From 3eb0928fc3e4b4702462164b399acddb7f0f09fd Mon Sep 17 00:00:00 2001 From: "Manoharan, Rajkumar" Date: Tue, 14 Feb 2017 12:27:16 -0800 Subject: mac80211: use DECLARE_EWMA for mesh_fail_avg As moving average is not considering fractional part, it will get stuck at the same level after certain state. For example, with current values, it can get stuck at 96. Fortunately the current threshold 95%, but if it were increased to 96 or more mesh paths would never be deactivated. Fix failure average movement by using EWMA helpers, which does take into account fractional parts. Signed-off-by: Rajkumar Manoharan [johannes: pick a larger EWMA factor for more precision with the limited range that we will feed into it, adjust to new API] Signed-off-by: Johannes Berg --- net/mac80211/mesh_hwmp.c | 21 +++++++++++++++------ net/mac80211/mesh_pathtbl.c | 3 +++ net/mac80211/sta_info.h | 5 ++++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index b747c9645e43..d07ee3ca07ee 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -307,10 +307,11 @@ void ieee80211s_update_metric(struct ieee80211_local *local, failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK); - /* moving average, scaled to 100 */ - sta->mesh->fail_avg = - ((80 * sta->mesh->fail_avg + 5) / 100 + 20 * failed); - if (sta->mesh->fail_avg > 95) + /* moving average, scaled to 100. + * feed failure as 100 and success as 0 + */ + ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, failed * 100); + if (ewma_mesh_fail_avg_read(&sta->mesh->fail_avg) > 95) mesh_plink_broken(sta); } @@ -325,6 +326,8 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, int rate, err; u32 tx_time, estimated_retx; u64 result; + unsigned long fail_avg = + ewma_mesh_fail_avg_read(&sta->mesh->fail_avg); /* Try to get rate based on HW/SW RC algorithm. * Rate is returned in units of Kbps, correct this @@ -336,7 +339,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, if (rate) { err = 0; } else { - if (sta->mesh->fail_avg >= 100) + if (fail_avg >= 100) return MAX_METRIC; sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate, &rinfo); @@ -344,7 +347,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, if (WARN_ON(!rate)) return MAX_METRIC; - err = (sta->mesh->fail_avg << ARITH_SHIFT) / 100; + err = (fail_avg << ARITH_SHIFT) / 100; } /* bitrate is in units of 100 Kbps, while we need rate in units of @@ -484,6 +487,9 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, ? mpath->exp_time : exp_time; mesh_path_activate(mpath); spin_unlock_bh(&mpath->state_lock); + ewma_mesh_fail_avg_init(&sta->mesh->fail_avg); + /* init it at a low value - 0 start is tricky */ + ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, 1); mesh_path_tx_pending(mpath); /* draft says preq_id should be saved to, but there does * not seem to be any use for it, skipping by now @@ -522,6 +528,9 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata, ? mpath->exp_time : exp_time; mesh_path_activate(mpath); spin_unlock_bh(&mpath->state_lock); + ewma_mesh_fail_avg_init(&sta->mesh->fail_avg); + /* init it at a low value - 0 start is tricky */ + ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, 1); mesh_path_tx_pending(mpath); } else spin_unlock_bh(&mpath->state_lock); diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index f0e6175a9821..98a3b1c0c338 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -829,6 +829,9 @@ void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop) mpath->flags = MESH_PATH_FIXED | MESH_PATH_SN_VALID; mesh_path_activate(mpath); spin_unlock_bh(&mpath->state_lock); + ewma_mesh_fail_avg_init(&next_hop->mesh->fail_avg); + /* init it at a low value - 0 start is tricky */ + ewma_mesh_fail_avg_add(&next_hop->mesh->fail_avg, 1); mesh_path_tx_pending(mpath); } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index e65cda34d2bc..cc413f52108e 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -324,6 +324,9 @@ struct ieee80211_fast_rx { struct rcu_head rcu_head; }; +/* we use only values in the range 0-100, so pick a large precision */ +DECLARE_EWMA(mesh_fail_avg, 20, 8) + /** * struct mesh_sta - mesh STA information * @plink_lock: serialize access to plink fields @@ -369,7 +372,7 @@ struct mesh_sta { enum nl80211_mesh_power_mode nonpeer_pm; /* moving percentage of failed MSDUs */ - unsigned int fail_avg; + struct ewma_mesh_fail_avg fail_avg; }; DECLARE_EWMA(signal, 10, 8) -- cgit From 4a4b8169501b18c3450ac735a7e277b24886a651 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Fri, 10 Feb 2017 10:02:31 +0100 Subject: cfg80211: Accept multiple RSSI thresholds for CQM Change the SET CQM command's RSSI threshold attribute to accept any number of thresholds as a sorted array. The API should be backwards compatible so that if one s32 threshold value is passed, the old mechanism is enabled. The netlink event generated is the same in both cases. cfg80211 handles an arbitrary number of RSSI thresholds but drivers have to provide a method (set_cqm_rssi_range_config) that configures a range set by a high and a low value. Drivers have to call back when the RSSI goes out of that range and there's no additional event for each time the range is reconfigured as there was with the current one-threshold API. This method doesn't have a hysteresis parameter because there's no benefit to the cfg80211 code from having the hysteresis be handled by hardware/driver in terms of the number of wakeups. At the same time it would likely be less consistent between drivers if offloaded or done in the drivers. Signed-off-by: Andrew Zaborowski Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 13 ++++ include/uapi/linux/nl80211.h | 9 ++- net/wireless/core.c | 9 +++ net/wireless/core.h | 9 +++ net/wireless/nl80211.c | 138 +++++++++++++++++++++++++++++++++++++++---- net/wireless/rdev-ops.h | 12 ++++ net/wireless/trace.h | 22 +++++++ 7 files changed, 198 insertions(+), 14 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ead1aa6d003e..ffc08687b31d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2712,6 +2712,11 @@ struct cfg80211_nan_func { * the current level is above/below the configured threshold; this may * need some care when the configuration is changed (without first being * disabled.) + * @set_cqm_rssi_range_config: Configure two RSSI thresholds in the + * connection quality monitor. An event is to be sent only when the + * signal level is found to be outside the two values. The driver should + * set %NL80211_EXT_FEATURE_CQM_RSSI_LIST if this method is implemented. + * If it is provided then there's no point providing @set_cqm_rssi_config. * @set_cqm_txe_config: Configure connection quality monitor TX error * thresholds. * @sched_scan_start: Tell the driver to start a scheduled scan. @@ -3001,6 +3006,10 @@ struct cfg80211_ops { struct net_device *dev, s32 rssi_thold, u32 rssi_hyst); + int (*set_cqm_rssi_range_config)(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_low, s32 rssi_high); + int (*set_cqm_txe_config)(struct wiphy *wiphy, struct net_device *dev, u32 rate, u32 pkts, u32 intvl); @@ -3871,6 +3880,7 @@ void wiphy_free(struct wiphy *wiphy); struct cfg80211_conn; struct cfg80211_internal_bss; struct cfg80211_cached_keys; +struct cfg80211_cqm_config; /** * struct wireless_dev - wireless device state @@ -3934,6 +3944,7 @@ struct cfg80211_cached_keys; * @event_list: (private) list for internal event processing * @event_lock: (private) lock for event list * @owner_nlportid: (private) owner socket port ID + * @cqm_config: (private) nl80211 RSSI monitor state */ struct wireless_dev { struct wiphy *wiphy; @@ -4002,6 +4013,8 @@ struct wireless_dev { bool prev_bssid_valid; } wext; #endif + + struct cfg80211_cqm_config *cqm_config; }; static inline u8 *wdev_address(struct wireless_dev *wdev) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 5ed257c4cd4e..9a499b15cfbc 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3942,7 +3942,10 @@ enum nl80211_ps_state { * @__NL80211_ATTR_CQM_INVALID: invalid * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies * the threshold for the RSSI level at which an event will be sent. Zero - * to disable. + * to disable. Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is + * set, multiple values can be supplied as a low-to-high sorted array of + * threshold values in dBm. Events will be sent when the RSSI value + * crosses any of the thresholds. * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies * the minimum amount the RSSI level must change after an event before a * new event may be issued (to reduce effects of RSSI oscillation). @@ -4753,6 +4756,9 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan * for reporting BSSs with better RSSI than the current connected BSS * (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI). + * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the + * %NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more + * RSSI threshold values to monitor rather than exactly one threshold. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -4771,6 +4777,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED, NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI, + NL80211_EXT_FEATURE_CQM_RSSI_LIST, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/core.c b/net/wireless/core.c index e55e05bc4805..04143df20f7f 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -954,6 +954,12 @@ void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) } EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); +void cfg80211_cqm_config_free(struct wireless_dev *wdev) +{ + kfree(wdev->cqm_config); + wdev->cqm_config = NULL; +} + void cfg80211_unregister_wdev(struct wireless_dev *wdev) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); @@ -980,6 +986,8 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev) WARN_ON_ONCE(1); break; } + + cfg80211_cqm_config_free(wdev); } EXPORT_SYMBOL(cfg80211_unregister_wdev); @@ -1234,6 +1242,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, kzfree(wdev->wext.keys); #endif flush_work(&wdev->disconnect_wk); + cfg80211_cqm_config_free(wdev); } /* * synchronise (so that we won't find this netdev diff --git a/net/wireless/core.h b/net/wireless/core.h index 58ca206982fe..efa690a7ef8d 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -272,6 +272,13 @@ struct cfg80211_iface_destroy { u32 nlportid; }; +struct cfg80211_cqm_config { + u32 rssi_hyst; + s32 last_rssi_event_value; + int n_rssi_thresholds; + s32 rssi_thresholds[0]; +}; + void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev); /* free object */ @@ -512,4 +519,6 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev, #define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; }) #endif +void cfg80211_cqm_config_free(struct wireless_dev *wdev); + #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d7f8be4e321a..d516527fcb8e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -9473,7 +9473,7 @@ static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) static const struct nla_policy nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { - [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_BINARY }, [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 }, @@ -9502,28 +9502,123 @@ static int nl80211_set_cqm_txe(struct genl_info *info, return rdev_set_cqm_txe_config(rdev, dev, rate, pkts, intvl); } +static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + s32 last, low, high; + u32 hyst; + int i, n; + int err; + + /* RSSI reporting disabled? */ + if (!wdev->cqm_config) + return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0); + + /* + * Obtain current RSSI value if possible, if not and no RSSI threshold + * event has been received yet, we should receive an event after a + * connection is established and enough beacons received to calculate + * the average. + */ + if (!wdev->cqm_config->last_rssi_event_value && wdev->current_bss && + rdev->ops->get_station) { + struct station_info sinfo; + u8 *mac_addr; + + mac_addr = wdev->current_bss->pub.bssid; + + err = rdev_get_station(rdev, dev, mac_addr, &sinfo); + if (err) + return err; + + if (sinfo.filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG)) + wdev->cqm_config->last_rssi_event_value = + (s8) sinfo.rx_beacon_signal_avg; + } + + last = wdev->cqm_config->last_rssi_event_value; + hyst = wdev->cqm_config->rssi_hyst; + n = wdev->cqm_config->n_rssi_thresholds; + + for (i = 0; i < n; i++) + if (last < wdev->cqm_config->rssi_thresholds[i]) + break; + + low = i > 0 ? + (wdev->cqm_config->rssi_thresholds[i - 1] - hyst) : S32_MIN; + high = i < n ? + (wdev->cqm_config->rssi_thresholds[i] + hyst - 1) : S32_MAX; + + return rdev_set_cqm_rssi_range_config(rdev, dev, low, high); +} + static int nl80211_set_cqm_rssi(struct genl_info *info, - s32 threshold, u32 hysteresis) + const s32 *thresholds, int n_thresholds, + u32 hysteresis) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; + int i, err; + s32 prev = S32_MIN; - if (threshold > 0) - return -EINVAL; - - /* disabling - hysteresis should also be zero then */ - if (threshold == 0) - hysteresis = 0; + /* Check all values negative and sorted */ + for (i = 0; i < n_thresholds; i++) { + if (thresholds[i] > 0 || thresholds[i] <= prev) + return -EINVAL; - if (!rdev->ops->set_cqm_rssi_config) - return -EOPNOTSUPP; + prev = thresholds[i]; + } if (wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; - return rdev_set_cqm_rssi_config(rdev, dev, threshold, hysteresis); + wdev_lock(wdev); + cfg80211_cqm_config_free(wdev); + wdev_unlock(wdev); + + if (n_thresholds <= 1 && rdev->ops->set_cqm_rssi_config) { + if (n_thresholds == 0 || thresholds[0] == 0) /* Disabling */ + return rdev_set_cqm_rssi_config(rdev, dev, 0, 0); + + return rdev_set_cqm_rssi_config(rdev, dev, + thresholds[0], hysteresis); + } + + if (!wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_CQM_RSSI_LIST)) + return -EOPNOTSUPP; + + if (n_thresholds == 1 && thresholds[0] == 0) /* Disabling */ + n_thresholds = 0; + + wdev_lock(wdev); + if (n_thresholds) { + struct cfg80211_cqm_config *cqm_config; + + cqm_config = kzalloc(sizeof(struct cfg80211_cqm_config) + + n_thresholds * sizeof(s32), GFP_KERNEL); + if (!cqm_config) { + err = -ENOMEM; + goto unlock; + } + + cqm_config->rssi_hyst = hysteresis; + cqm_config->n_rssi_thresholds = n_thresholds; + memcpy(cqm_config->rssi_thresholds, thresholds, + n_thresholds * sizeof(s32)); + + wdev->cqm_config = cqm_config; + } + + err = cfg80211_cqm_rssi_update(rdev, dev); + +unlock: + wdev_unlock(wdev); + + return err; } static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) @@ -9543,10 +9638,16 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && attrs[NL80211_ATTR_CQM_RSSI_HYST]) { - s32 threshold = nla_get_s32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); + const s32 *thresholds = + nla_data(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); + int len = nla_len(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); u32 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); - return nl80211_set_cqm_rssi(info, threshold, hysteresis); + if (len % 4) + return -EINVAL; + + return nl80211_set_cqm_rssi(info, thresholds, len / 4, + hysteresis); } if (attrs[NL80211_ATTR_CQM_TXE_RATE] && @@ -13983,6 +14084,8 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, s32 rssi_level, gfp_t gfp) { struct sk_buff *msg; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); trace_cfg80211_cqm_rssi_notify(dev, rssi_event, rssi_level); @@ -13990,6 +14093,15 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH)) return; + if (wdev->cqm_config) { + wdev->cqm_config->last_rssi_event_value = rssi_level; + + cfg80211_cqm_rssi_update(rdev, dev); + + if (rssi_level == 0) + rssi_level = wdev->cqm_config->last_rssi_event_value; + } + msg = cfg80211_prepare_cqm(dev, NULL, gfp); if (!msg) return; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 2f425075ada8..f2baf5921091 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -749,6 +749,18 @@ rdev_set_cqm_rssi_config(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_set_cqm_rssi_range_config(struct cfg80211_registered_device *rdev, + struct net_device *dev, s32 low, s32 high) +{ + int ret; + trace_rdev_set_cqm_rssi_range_config(&rdev->wiphy, dev, low, high); + ret = rdev->ops->set_cqm_rssi_range_config(&rdev->wiphy, dev, + low, high); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline int rdev_set_cqm_txe_config(struct cfg80211_registered_device *rdev, struct net_device *dev, u32 rate, u32 pkts, u32 intvl) diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 776e80cef9b4..fd55786f0462 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1322,6 +1322,28 @@ TRACE_EVENT(rdev_set_cqm_rssi_config, __entry->rssi_thold, __entry->rssi_hyst) ); +TRACE_EVENT(rdev_set_cqm_rssi_range_config, + TP_PROTO(struct wiphy *wiphy, + struct net_device *netdev, s32 low, s32 high), + TP_ARGS(wiphy, netdev, low, high), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(s32, rssi_low) + __field(s32, rssi_high) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->rssi_low = low; + __entry->rssi_high = high; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT + ", range: %d - %d ", + WIPHY_PR_ARG, NETDEV_PR_ARG, + __entry->rssi_low, __entry->rssi_high) +); + TRACE_EVENT(rdev_set_cqm_txe_config, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 rate, u32 pkts, u32 intvl), -- cgit From 2c3c5f8c0cfa8e88a4c34d7651b5712c558ab9b7 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Fri, 10 Feb 2017 04:50:22 +0100 Subject: mac80211: Add set_cqm_rssi_range_config Support .set_cqm_rssi_range_config if the beacons are available for processing in mac80211. There's no reason that this couldn't be offloaded by mac80211-based drivers but there's no driver method for that added in this patch. Signed-off-by: Andrew Zaborowski Signed-off-by: Johannes Berg --- include/net/mac80211.h | 6 ++++++ net/mac80211/cfg.c | 28 ++++++++++++++++++++++++++++ net/mac80211/mlme.c | 24 ++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index a3bab3c5ecfb..1a26a375feb8 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -501,6 +501,10 @@ struct ieee80211_mu_group_data { * implies disabled. As with the cfg80211 callback, a change here should * cause an event to be sent indicating where the current value is in * relation to the newly configured threshold. + * @cqm_rssi_low: Connection quality monitor RSSI lower threshold, a zero value + * implies disabled. This is an alternative mechanism to the single + * threshold event and can't be enabled simultaneously with it. + * @cqm_rssi_high: Connection quality monitor RSSI upper threshold. * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis * @arp_addr_list: List of IPv4 addresses for hardware ARP filtering. The * may filter ARP queries targeted for other addresses than listed here. @@ -553,6 +557,8 @@ struct ieee80211_bss_conf { u16 ht_operation_mode; s32 cqm_rssi_thold; u32 cqm_rssi_hyst; + s32 cqm_rssi_low; + s32 cqm_rssi_high; struct cfg80211_chan_def chandef; struct ieee80211_mu_group_data mu_group; __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN]; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ac879bb17870..9c7490cb2243 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2630,6 +2630,33 @@ static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy, bss_conf->cqm_rssi_thold = rssi_thold; bss_conf->cqm_rssi_hyst = rssi_hyst; + bss_conf->cqm_rssi_low = 0; + bss_conf->cqm_rssi_high = 0; + sdata->u.mgd.last_cqm_event_signal = 0; + + /* tell the driver upon association, unless already associated */ + if (sdata->u.mgd.associated && + sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI) + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM); + + return 0; +} + +static int ieee80211_set_cqm_rssi_range_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_low, s32 rssi_high) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_vif *vif = &sdata->vif; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + + if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER) + return -EOPNOTSUPP; + + bss_conf->cqm_rssi_low = rssi_low; + bss_conf->cqm_rssi_high = rssi_high; + bss_conf->cqm_rssi_thold = 0; + bss_conf->cqm_rssi_hyst = 0; sdata->u.mgd.last_cqm_event_signal = 0; /* tell the driver upon association, unless already associated */ @@ -3639,6 +3666,7 @@ const struct cfg80211_ops mac80211_config_ops = { .mgmt_tx = ieee80211_mgmt_tx, .mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait, .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config, + .set_cqm_rssi_range_config = ieee80211_set_cqm_rssi_range_config, .mgmt_frame_register = ieee80211_mgmt_frame_register, .set_antenna = ieee80211_set_antenna, .get_antenna = ieee80211_get_antenna, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6e90301154d5..23986934d7af 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3430,6 +3430,30 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } } + if (bss_conf->cqm_rssi_low && + ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { + int sig = -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal); + int last_event = ifmgd->last_cqm_event_signal; + int low = bss_conf->cqm_rssi_low; + int high = bss_conf->cqm_rssi_high; + + if (sig < low && + (last_event == 0 || last_event >= low)) { + ifmgd->last_cqm_event_signal = sig; + ieee80211_cqm_rssi_notify( + &sdata->vif, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + sig, GFP_KERNEL); + } else if (sig > high && + (last_event == 0 || last_event <= high)) { + ifmgd->last_cqm_event_signal = sig; + ieee80211_cqm_rssi_notify( + &sdata->vif, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + sig, GFP_KERNEL); + } + } + if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) { mlme_dbg_ratelimited(sdata, "cancelling AP probe due to a received beacon\n"); -- cgit From ae44b502669d0cd1f167cdb48994292aa20fd3dd Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Fri, 10 Feb 2017 04:50:23 +0100 Subject: wireless: Set NL80211_EXT_FEATURE_CQM_RSSI_LIST in multiple drivers Set the NL80211_EXT_FEATURE_CQM_RSSI_LIST wiphy extended feature wholesale in all mac80211-based drivers that do not set the IEEE80211_VIF_BEACON_FILTER flags on their interfaces. mac80211 will be processing supplied RSSI values in ieee80211_rx_mgmt_beacon and will detect when the thresholds set by ieee80211_set_cqm_rssi_range_config are crossed. Remaining (few) drivers need code to enable the firmware to monitor the thresholds. This is mostly only compile-tested. Signed-off-by: Andrew Zaborowski Signed-off-by: Johannes Berg --- drivers/net/wireless/admtek/adm8211.c | 2 ++ drivers/net/wireless/ath/ar5523/ar5523.c | 2 ++ drivers/net/wireless/ath/ath10k/mac.c | 2 ++ drivers/net/wireless/ath/ath5k/base.c | 2 ++ drivers/net/wireless/ath/ath9k/htc_drv_init.c | 2 ++ drivers/net/wireless/ath/ath9k/init.c | 2 ++ drivers/net/wireless/ath/carl9170/main.c | 2 ++ drivers/net/wireless/ath/wcn36xx/main.c | 3 +++ drivers/net/wireless/atmel/at76c50x-usb.c | 2 ++ drivers/net/wireless/broadcom/b43/main.c | 2 ++ drivers/net/wireless/broadcom/b43legacy/main.c | 2 ++ drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c | 2 ++ drivers/net/wireless/intel/iwlegacy/3945-mac.c | 2 ++ drivers/net/wireless/intel/iwlegacy/4965-mac.c | 2 ++ drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c | 2 ++ drivers/net/wireless/mac80211_hwsim.c | 2 ++ drivers/net/wireless/marvell/libertas_tf/main.c | 2 ++ drivers/net/wireless/marvell/mwl8k.c | 2 ++ drivers/net/wireless/mediatek/mt7601u/init.c | 2 ++ drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 3 +++ drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c | 2 ++ drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c | 2 ++ drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 2 ++ drivers/net/wireless/rsi/rsi_91x_mac80211.c | 2 ++ drivers/net/wireless/zydas/zd1211rw/zd_mac.c | 2 ++ 25 files changed, 52 insertions(+) diff --git a/drivers/net/wireless/admtek/adm8211.c b/drivers/net/wireless/admtek/adm8211.c index 098c814e22c8..ed626f568b58 100644 --- a/drivers/net/wireless/admtek/adm8211.c +++ b/drivers/net/wireless/admtek/adm8211.c @@ -1917,6 +1917,8 @@ static int adm8211_probe(struct pci_dev *pdev, dev->wiphy->bands[NL80211_BAND_2GHZ] = &priv->band; + wiphy_ext_feature_set(dev->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + err = ieee80211_register_hw(dev); if (err) { printk(KERN_ERR "%s (adm8211): Cannot register device\n", diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 7a60d2e652da..f2f4ccfdf8da 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -1689,6 +1689,8 @@ static int ar5523_probe(struct usb_interface *intf, if (error) goto out_cancel_rx_cmd; + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + usb_set_intfdata(intf, hw); error = ieee80211_register_hw(hw); diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 3029f257a19a..abc291de1d31 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -8248,6 +8248,8 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->cipher_suites = cipher_suites; ar->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = ieee80211_register_hw(ar->hw); if (ret) { ath10k_err(ar, "failed to register ieee80211: %d\n", ret); diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index d98fd421c7ec..92ece64fd455 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2564,6 +2564,8 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) hw->extra_tx_headroom = 2; + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + /* * Mark the device as detached to avoid processing * interrupts until setup is complete. diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index b65c1b661ade..defacc6c9c99 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -780,6 +780,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, } SET_IEEE80211_PERM_ADDR(hw, common->macaddr); + + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); } static int ath9k_init_firmware_version(struct ath9k_htc_priv *priv) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index fa4b3cc1ba22..fd9a61834c17 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -955,6 +955,8 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) ath9k_cmn_reload_chainmask(ah); SET_IEEE80211_PERM_ADDR(hw, common->macaddr); + + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); } int ath9k_init_device(u16 devid, struct ath_softc *sc, diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index ffb22a04beeb..988c8857d78c 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1874,6 +1874,8 @@ void *carl9170_alloc(size_t priv_size) for (i = 0; i < ARRAY_SIZE(ar->noise); i++) ar->noise[i] = -95; /* ATH_DEFAULT_NOISE_FLOOR */ + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + return ar; err_nomem: diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 7a0c2e7da7f6..cee4f655bf36 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -1112,6 +1112,9 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta); wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif); + wiphy_ext_feature_set(wcn->hw->wiphy, + NL80211_EXT_FEATURE_CQM_RSSI_LIST); + return ret; } diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c index 0e180677c7fc..09defbcedd5e 100644 --- a/drivers/net/wireless/atmel/at76c50x-usb.c +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -2377,6 +2377,8 @@ static int at76_init_new_device(struct at76_priv *priv, wiphy->hw_version = priv->board_type; + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = ieee80211_register_hw(priv->hw); if (ret) { printk(KERN_ERR "cannot register mac80211 hw (status %d)!\n", diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index 52f3541ecbcf..d23aac7503d3 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -5598,6 +5598,8 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev) hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + wl->hw_registred = false; hw->max_rates = 2; SET_IEEE80211_DEV(hw, dev->dev); diff --git a/drivers/net/wireless/broadcom/b43legacy/main.c b/drivers/net/wireless/broadcom/b43legacy/main.c index cdafebb9c936..f1e3dad57629 100644 --- a/drivers/net/wireless/broadcom/b43legacy/main.c +++ b/drivers/net/wireless/broadcom/b43legacy/main.c @@ -3850,6 +3850,8 @@ static int b43legacy_wireless_init(struct ssb_device *dev) else SET_IEEE80211_PERM_ADDR(hw, sprom->il0mac); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + /* Get and initialize struct b43legacy_wl */ wl = hw_to_b43legacy_wl(hw); memset(wl, 0, sizeof(*wl)); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c index 7c2a9a9bc372..ddfdfe177e24 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c @@ -1082,6 +1082,8 @@ static int ieee_hw_init(struct ieee80211_hw *hw) * hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; */ + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + hw->rate_control_algorithm = "minstrel_ht"; hw->sta_data_size = 0; diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c index e8e65115feba..38bf403bb1e1 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c @@ -3592,6 +3592,8 @@ il3945_setup_mac(struct il_priv *il) il_leds_init(il); + wiphy_ext_feature_set(il->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = ieee80211_register_hw(il->hw); if (ret) { IL_ERR("Failed to register hw (error %d)\n", ret); diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index 2781f5728d07..7eda525e3f4f 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -5799,6 +5799,8 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) il_leds_init(il); + wiphy_ext_feature_set(il->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = ieee80211_register_hw(il->hw); if (ret) { IL_ERR("Failed to register hw (error %d)\n", ret); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index 2a04d0cd71ae..e3cab60ddf0f 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -213,6 +213,8 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, iwl_leds_init(priv); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = ieee80211_register_hw(priv->hw); if (ret) { IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 50c219fb1a52..307a53a7da23 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2645,6 +2645,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, if (param->no_vif) ieee80211_hw_set(hw, NO_AUTO_VIF); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + err = ieee80211_register_hw(hw); if (err < 0) { printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n", diff --git a/drivers/net/wireless/marvell/libertas_tf/main.c b/drivers/net/wireless/marvell/libertas_tf/main.c index 54e426c1e405..d80333117989 100644 --- a/drivers/net/wireless/marvell/libertas_tf/main.c +++ b/drivers/net/wireless/marvell/libertas_tf/main.c @@ -641,6 +641,8 @@ struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev) BIT(NL80211_IFTYPE_ADHOC); skb_queue_head_init(&priv->bc_ps_buf); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + SET_IEEE80211_DEV(hw, dmdev); INIT_WORK(&priv->cmd_work, lbtf_cmd_work); diff --git a/drivers/net/wireless/marvell/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c index b1b400b59d86..c295a4c6e5cd 100644 --- a/drivers/net/wireless/marvell/mwl8k.c +++ b/drivers/net/wireless/marvell/mwl8k.c @@ -6144,6 +6144,8 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv) if (priv->sta_macids_supported || priv->device_info->fw_image_sta) hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + rc = ieee80211_register_hw(hw); if (rc) { wiphy_err(hw->wiphy, "Cannot register device\n"); diff --git a/drivers/net/wireless/mediatek/mt7601u/init.c b/drivers/net/wireless/mediatek/mt7601u/init.c index a6e901766226..d3b611aaf061 100644 --- a/drivers/net/wireless/mediatek/mt7601u/init.c +++ b/drivers/net/wireless/mediatek/mt7601u/init.c @@ -615,6 +615,8 @@ int mt7601u_register_device(struct mt7601u_dev *dev) wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = mt76_init_sband_2g(dev); if (ret) return ret; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index dd6678109b7e..57e6af9a44a1 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -1384,6 +1384,9 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + wiphy_ext_feature_set(rt2x00dev->hw->wiphy, + NL80211_EXT_FEATURE_CQM_RSSI_LIST); + /* * Initialize ieee80211 structure. */ diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c index e895a84481da..e387dec82d3d 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c @@ -1877,6 +1877,8 @@ static int rtl8180_probe(struct pci_dev *pdev, else ieee80211_hw_set(dev, SIGNAL_UNSPEC); + wiphy_ext_feature_set(dev->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + rtl8180_eeprom_read(priv); switch (priv->rf_type) { diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c index 231f84db9ab0..274ad6d34d68 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c @@ -1609,6 +1609,8 @@ static int rtl8187_probe(struct usb_interface *intf, dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) ; + wiphy_ext_feature_set(dev->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b) printk(KERN_INFO "rtl8187: inconsistency between id with OEM" " info!\n"); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index e544dd1d618c..9b4a9a00be64 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -6135,6 +6135,8 @@ static int rtl8xxxu_probe(struct usb_interface *interface, ieee80211_hw_set(hw, HAS_RATE_CONTROL); ieee80211_hw_set(hw, AMPDU_AGGREGATION); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + ret = ieee80211_register_hw(priv->hw); if (ret) { dev_err(&udev->dev, "%s: Failed to register: %i\n", diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index e3216473aecb..021e5ac5f107 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -1261,6 +1261,8 @@ int rsi_mac80211_attach(struct rsi_common *common) wiphy->reg_notifier = rsi_reg_notify; + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + status = ieee80211_register_hw(hw); if (status) return status; diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c index 3e37a045f702..fe6517a621b0 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c @@ -1408,6 +1408,8 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf) BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + hw->max_signal = 100; hw->queues = 1; hw->extra_tx_headroom = sizeof(struct zd_ctrlset); -- cgit From d4f29978675d7d07e7bb9da30ed05ecf588820a0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 13 Feb 2017 20:53:38 +0100 Subject: cfg80211: combine two nested ifs into a single condition Combine two instances of having two nested if statements into a single one with a combined condition to reduce the indentation. Signed-off-by: Johannes Berg --- net/wireless/core.c | 12 ++++++------ net/wireless/util.c | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index 04143df20f7f..76e664144c8e 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1216,12 +1216,12 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, */ if ((wdev->iftype == NL80211_IFTYPE_STATION || wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && - rdev->ops->set_power_mgmt) - if (rdev_set_power_mgmt(rdev, dev, wdev->ps, - wdev->ps_timeout)) { - /* assume this means it's off */ - wdev->ps = false; - } + rdev->ops->set_power_mgmt && + rdev_set_power_mgmt(rdev, dev, wdev->ps, + wdev->ps_timeout)) { + /* assume this means it's off */ + wdev->ps = false; + } break; case NETDEV_UNREGISTER: /* diff --git a/net/wireless/util.c b/net/wireless/util.c index 68e5f2ecee1a..daef500a7b64 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -914,11 +914,11 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev) netdev_err(dev, "failed to set key %d\n", i); continue; } - if (wdev->connect_keys->def == i) - if (rdev_set_default_key(rdev, dev, i, true, true)) { - netdev_err(dev, "failed to set defkey %d\n", i); - continue; - } + if (wdev->connect_keys->def == i && + rdev_set_default_key(rdev, dev, i, true, true)) { + netdev_err(dev, "failed to set defkey %d\n", i); + continue; + } } kzfree(wdev->connect_keys); -- cgit From f22775ede2eb58ed84b55e30768d041f607a2199 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Mon, 13 Feb 2017 14:37:41 +0200 Subject: ieee80211: add FT-PSK AKM suite selector Signed-off-by: Avraham Stern Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 0dd9498c694f..6ea381c98aae 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2347,6 +2347,7 @@ enum ieee80211_sa_query_action { /* AKM suite selectors */ #define WLAN_AKM_SUITE_8021X SUITE(0x000FAC, 1) #define WLAN_AKM_SUITE_PSK SUITE(0x000FAC, 2) +#define WLAN_AKM_SUITE_FT_PSK SUITE(0x000FAC, 4) #define WLAN_AKM_SUITE_8021X_SHA256 SUITE(0x000FAC, 5) #define WLAN_AKM_SUITE_PSK_SHA256 SUITE(0x000FAC, 6) #define WLAN_AKM_SUITE_TDLS SUITE(0x000FAC, 7) -- cgit From 68506e9af132a6b5735c1dd4b11240da0cf5eeae Mon Sep 17 00:00:00 2001 From: Arkadiusz Miskiewicz Date: Wed, 15 Feb 2017 14:21:27 +0100 Subject: mac80211: Print text for disassociation reason MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When disassociation happens only numeric reason is printed in ieee80211_rx_mgmt_disassoc(). Add text variant, too. Signed-off-by: Arkadiusz Miśkiewicz Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 23986934d7af..1568a74757bc 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2797,8 +2797,9 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); - sdata_info(sdata, "disassociated from %pM (Reason: %u)\n", - mgmt->sa, reason_code); + sdata_info(sdata, "disassociated from %pM (Reason: %u=%s)\n", + mgmt->sa, reason_code, + ieee80211_get_reason_code_string(reason_code)); ieee80211_set_disassoc(sdata, 0, 0, false, NULL); -- cgit From 2fb51c35815dc08638a7d9b1a497a9d7cb4109b8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Feb 2017 15:02:06 +0100 Subject: ieee80211: rename CCFS1/CCFS2 to CCFS0/CCFS1 This matches the spec, and otherwise things are really confusing with the next patch adding CCFS2. Signed-off-by: Johannes Berg --- drivers/net/wireless/marvell/mwifiex/tdls.c | 2 +- include/linux/ieee80211.h | 4 ++-- net/mac80211/spectmgmt.c | 4 ++-- net/mac80211/util.c | 22 +++++++++++----------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c index df9704de0715..5fc8319ed302 100644 --- a/drivers/net/wireless/marvell/mwifiex/tdls.c +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -349,7 +349,7 @@ static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, chan_bw = IEEE80211_VHT_CHANWIDTH_USE_HT; break; } - vht_oper->center_freq_seg1_idx = + vht_oper->center_freq_seg0_idx = mwifiex_get_center_freq_index(priv, BAND_AAC, bss_desc->channel, chan_bw); diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 6ea381c98aae..e167a262d3b0 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1525,14 +1525,14 @@ enum ieee80211_vht_chanwidth { * This structure is the "VHT operation element" as * described in 802.11ac D3.0 8.4.2.161 * @chan_width: Operating channel width + * @center_freq_seg0_idx: center freq segment 0 index * @center_freq_seg1_idx: center freq segment 1 index - * @center_freq_seg2_idx: center freq segment 2 index * @basic_mcs_set: VHT Basic MCS rate set */ struct ieee80211_vht_operation { u8 chan_width; + u8 center_freq_seg0_idx; u8 center_freq_seg1_idx; - u8 center_freq_seg2_idx; __le16 basic_mcs_set; } __packed; diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 97f4c9d6b54c..0782e486fe89 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -132,9 +132,9 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, struct ieee80211_vht_operation vht_oper = { .chan_width = wide_bw_chansw_ie->new_channel_width, - .center_freq_seg1_idx = + .center_freq_seg0_idx = wide_bw_chansw_ie->new_center_freq_seg0, - .center_freq_seg2_idx = + .center_freq_seg1_idx = wide_bw_chansw_ie->new_center_freq_seg1, /* .basic_mcs_set doesn't matter */ }; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ac59fbd280df..7a37ce78bb38 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2413,13 +2413,13 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, *pos++ = WLAN_EID_VHT_OPERATION; *pos++ = sizeof(struct ieee80211_vht_operation); vht_oper = (struct ieee80211_vht_operation *)pos; - vht_oper->center_freq_seg1_idx = ieee80211_frequency_to_channel( + vht_oper->center_freq_seg0_idx = ieee80211_frequency_to_channel( chandef->center_freq1); if (chandef->center_freq2) - vht_oper->center_freq_seg2_idx = + vht_oper->center_freq_seg1_idx = ieee80211_frequency_to_channel(chandef->center_freq2); else - vht_oper->center_freq_seg2_idx = 0x00; + vht_oper->center_freq_seg1_idx = 0x00; switch (chandef->width) { case NL80211_CHAN_WIDTH_160: @@ -2428,11 +2428,11 @@ u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, * workaround. */ vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; - vht_oper->center_freq_seg2_idx = vht_oper->center_freq_seg1_idx; + vht_oper->center_freq_seg1_idx = vht_oper->center_freq_seg0_idx; if (chandef->chan->center_freq < chandef->center_freq1) - vht_oper->center_freq_seg1_idx -= 8; + vht_oper->center_freq_seg0_idx -= 8; else - vht_oper->center_freq_seg1_idx += 8; + vht_oper->center_freq_seg0_idx += 8; break; case NL80211_CHAN_WIDTH_80P80: /* @@ -2491,9 +2491,9 @@ bool ieee80211_chandef_vht_oper(const struct ieee80211_vht_operation *oper, if (!oper) return false; - cf1 = ieee80211_channel_to_frequency(oper->center_freq_seg1_idx, + cf1 = ieee80211_channel_to_frequency(oper->center_freq_seg0_idx, chandef->chan->band); - cf2 = ieee80211_channel_to_frequency(oper->center_freq_seg2_idx, + cf2 = ieee80211_channel_to_frequency(oper->center_freq_seg1_idx, chandef->chan->band); switch (oper->chan_width) { @@ -2503,11 +2503,11 @@ bool ieee80211_chandef_vht_oper(const struct ieee80211_vht_operation *oper, new.width = NL80211_CHAN_WIDTH_80; new.center_freq1 = cf1; /* If needed, adjust based on the newer interop workaround. */ - if (oper->center_freq_seg2_idx) { + if (oper->center_freq_seg1_idx) { unsigned int diff; - diff = abs(oper->center_freq_seg2_idx - - oper->center_freq_seg1_idx); + diff = abs(oper->center_freq_seg1_idx - + oper->center_freq_seg0_idx); if (diff == 8) { new.width = NL80211_CHAN_WIDTH_160; new.center_freq1 = cf2; -- cgit From a858958b689211dcfe54cdd94c93160d2d659eba Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Feb 2017 15:02:07 +0100 Subject: mac80211: remove local pointer from rate_ctrl_ref This pointer really isn't needed, so remove it. Signed-off-by: Johannes Berg --- net/mac80211/rate.c | 10 +++++----- net/mac80211/rate.h | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 206698bc93f4..094c15645228 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -208,7 +208,6 @@ static struct rate_control_ref *rate_control_alloc(const char *name, ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL); if (!ref) return NULL; - ref->local = local; ref->ops = ieee80211_rate_control_ops_get(name); if (!ref->ops) goto free; @@ -229,13 +228,14 @@ free: return NULL; } -static void rate_control_free(struct rate_control_ref *ctrl_ref) +static void rate_control_free(struct ieee80211_local *local, + struct rate_control_ref *ctrl_ref) { ctrl_ref->ops->free(ctrl_ref->priv); #ifdef CONFIG_MAC80211_DEBUGFS - debugfs_remove_recursive(ctrl_ref->local->debugfs.rcdir); - ctrl_ref->local->debugfs.rcdir = NULL; + debugfs_remove_recursive(local->debugfs.rcdir); + local->debugfs.rcdir = NULL; #endif kfree(ctrl_ref); @@ -936,6 +936,6 @@ void rate_control_deinitialize(struct ieee80211_local *local) return; local->rate_ctrl = NULL; - rate_control_free(ref); + rate_control_free(local, ref); } diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index 8d3260785b94..d51a1cce4d4a 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -20,7 +20,6 @@ #include "driver-ops.h" struct rate_control_ref { - struct ieee80211_local *local; const struct rate_control_ops *ops; void *priv; }; -- cgit From 0c1eca4e2f96000077900e0108fff23994c73486 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Feb 2017 15:02:08 +0100 Subject: cfg80211: refactor cfg80211_calculate_bitrate() This function contains the HT calculations, which makes no sense - split that out into a separate function. As a side effect, this makes the 60G flag independent from HT_MCS so remove the MCS one from wil6210 (also deleting a duplicate assignment.) Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/wil6210/cfg80211.c | 3 +- net/wireless/util.c | 60 +++++++++++++++-------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 83155b5ddbfb..79d107018eac 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -178,9 +178,8 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, BIT(NL80211_STA_INFO_RX_DROP_MISC) | BIT(NL80211_STA_INFO_TX_FAILED); - sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; + sinfo->txrate.flags = RATE_INFO_FLAGS_60G; sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs); - sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; sinfo->rxrate.mcs = stats->last_mcs_rx; sinfo->rx_bytes = stats->rx_bytes; sinfo->rx_packets = stats->rx_packets; diff --git a/net/wireless/util.c b/net/wireless/util.c index daef500a7b64..737c9c2c9cc9 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1097,6 +1097,35 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, return err; } +static u32 cfg80211_calculate_bitrate_ht(struct rate_info *rate) +{ + int modulation, streams, bitrate; + + /* the formula below does only work for MCS values smaller than 32 */ + if (WARN_ON_ONCE(rate->mcs >= 32)) + return 0; + + modulation = rate->mcs & 7; + streams = (rate->mcs >> 3) + 1; + + bitrate = (rate->bw == RATE_INFO_BW_40) ? 13500000 : 6500000; + + if (modulation < 4) + bitrate *= (modulation + 1); + else if (modulation == 4) + bitrate *= (modulation + 2); + else + bitrate *= (modulation + 3); + + bitrate *= streams; + + if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) + bitrate = (bitrate / 9) * 10; + + /* do NOT round down here */ + return (bitrate + 50000) / 100000; +} + static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate) { static const u32 __mcs2bitrate[] = { @@ -1230,39 +1259,14 @@ static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate) u32 cfg80211_calculate_bitrate(struct rate_info *rate) { - int modulation, streams, bitrate; - - if (!(rate->flags & RATE_INFO_FLAGS_MCS) && - !(rate->flags & RATE_INFO_FLAGS_VHT_MCS)) - return rate->legacy; + if (rate->flags & RATE_INFO_FLAGS_MCS) + return cfg80211_calculate_bitrate_ht(rate); if (rate->flags & RATE_INFO_FLAGS_60G) return cfg80211_calculate_bitrate_60g(rate); if (rate->flags & RATE_INFO_FLAGS_VHT_MCS) return cfg80211_calculate_bitrate_vht(rate); - /* the formula below does only work for MCS values smaller than 32 */ - if (WARN_ON_ONCE(rate->mcs >= 32)) - return 0; - - modulation = rate->mcs & 7; - streams = (rate->mcs >> 3) + 1; - - bitrate = (rate->bw == RATE_INFO_BW_40) ? 13500000 : 6500000; - - if (modulation < 4) - bitrate *= (modulation + 1); - else if (modulation == 4) - bitrate *= (modulation + 2); - else - bitrate *= (modulation + 3); - - bitrate *= streams; - - if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) - bitrate = (bitrate / 9) * 10; - - /* do NOT round down here */ - return (bitrate + 50000) / 100000; + return rate->legacy; } EXPORT_SYMBOL(cfg80211_calculate_bitrate); -- cgit From 7f406cd16a0f0965c761ea02bc1f03154b06bbfb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Feb 2017 15:02:09 +0100 Subject: mac80211: encode rate type (legacy, HT, VHT) with fewer bits We don't really need three different bits for each, since the types are mutually exclusive. Use just two bits for it. Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 11 ++++++++--- net/mac80211/sta_info.h | 13 +++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 3323a2fb289b..81ec1f72518d 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1960,14 +1960,17 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate, rinfo->bw = (rate & STA_STATS_RATE_BW_MASK) >> STA_STATS_RATE_BW_SHIFT; - if (rate & STA_STATS_RATE_VHT) { + switch (rate & STA_STATS_RATE_TYPE_MASK) { + case STA_STATS_RATE_TYPE_VHT: rinfo->flags = RATE_INFO_FLAGS_VHT_MCS; rinfo->mcs = rate & 0xf; rinfo->nss = (rate & 0xf0) >> 4; - } else if (rate & STA_STATS_RATE_HT) { + break; + case STA_STATS_RATE_TYPE_HT: rinfo->flags = RATE_INFO_FLAGS_MCS; rinfo->mcs = rate & 0xff; - } else if (rate & STA_STATS_RATE_LEGACY) { + break; + case STA_STATS_RATE_TYPE_LEGACY: { struct ieee80211_supported_band *sband; u16 brate; unsigned int shift; @@ -1982,6 +1985,8 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate, else shift = 0; rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift); + break; + } } if (rate & STA_STATS_RATE_SGI) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index cc413f52108e..8949266d7bc3 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -728,9 +728,10 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); unsigned long ieee80211_sta_last_active(struct sta_info *sta); #define STA_STATS_RATE_INVALID 0 -#define STA_STATS_RATE_VHT 0x8000 -#define STA_STATS_RATE_HT 0x4000 -#define STA_STATS_RATE_LEGACY 0x2000 +#define STA_STATS_RATE_TYPE_MASK 0xC000 +#define STA_STATS_RATE_TYPE_LEGACY 0x4000 +#define STA_STATS_RATE_TYPE_HT 0x8000 +#define STA_STATS_RATE_TYPE_VHT 0xC000 #define STA_STATS_RATE_SGI 0x1000 #define STA_STATS_RATE_BW_SHIFT 9 #define STA_STATS_RATE_BW_MASK (0x7 << STA_STATS_RATE_BW_SHIFT) @@ -756,11 +757,11 @@ static inline u16 sta_stats_encode_rate(struct ieee80211_rx_status *s) r |= STA_STATS_RATE_SGI; if (s->flag & RX_FLAG_VHT) - r |= STA_STATS_RATE_VHT | (s->vht_nss << 4); + r |= STA_STATS_RATE_TYPE_VHT | (s->vht_nss << 4); else if (s->flag & RX_FLAG_HT) - r |= STA_STATS_RATE_HT; + r |= STA_STATS_RATE_TYPE_HT; else - r |= STA_STATS_RATE_LEGACY | (s->band << 4); + r |= STA_STATS_RATE_TYPE_LEGACY | (s->band << 4); return r; } -- cgit From 75b99bc300463e65f87c90425704c2688489f963 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 Feb 2017 15:02:10 +0100 Subject: ieee80211: define HT operation CCFS2 field The Channel Center Frequency Segment 2 field is used in 802.11-2016 for encoding the actual channel position of the 80+80/160 MHz channel, if the max NSS is restricted. This is used for backwards compatibility. Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index e167a262d3b0..22bf0676d928 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1411,6 +1411,8 @@ struct ieee80211_ht_operation { #define IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED 3 #define IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT 0x0004 #define IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT 0x0010 +#define IEEE80211_HT_OP_MODE_CCFS2_SHIFT 5 +#define IEEE80211_HT_OP_MODE_CCFS2_MASK 0x1fe0 /* for stbc_param */ #define IEEE80211_HT_STBC_PARAM_DUAL_BEACON 0x0040 -- cgit From fe56c9c17b09769691e8b91747b32aa2555bef35 Mon Sep 17 00:00:00 2001 From: "Manoharan, Rajkumar" Date: Wed, 15 Feb 2017 12:46:50 -0800 Subject: mac80211: fix mesh fail_avg check Mesh failure average never be more than 100. Only in case of fixed path, average will be more than threshold limit (95%). With recent EWMA changes it may go upto 99 as it is scaled to 100. It make sense to return maximum metric when average is greater than threshold limit. Signed-off-by: Rajkumar Manoharan Signed-off-by: Johannes Berg --- net/mac80211/mesh_hwmp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index d07ee3ca07ee..4005edd71fe8 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -16,6 +16,7 @@ #define TEST_FRAME_LEN 8192 #define MAX_METRIC 0xffffffff #define ARITH_SHIFT 8 +#define LINK_FAIL_THRESH 95 #define MAX_PREQ_QUEUE_LEN 64 @@ -311,7 +312,8 @@ void ieee80211s_update_metric(struct ieee80211_local *local, * feed failure as 100 and success as 0 */ ewma_mesh_fail_avg_add(&sta->mesh->fail_avg, failed * 100); - if (ewma_mesh_fail_avg_read(&sta->mesh->fail_avg) > 95) + if (ewma_mesh_fail_avg_read(&sta->mesh->fail_avg) > + LINK_FAIL_THRESH) mesh_plink_broken(sta); } @@ -339,7 +341,7 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local, if (rate) { err = 0; } else { - if (fail_avg >= 100) + if (fail_avg > LINK_FAIL_THRESH) return MAX_METRIC; sta_set_rate_info_tx(sta, &sta->tx_stats.last_rate, &rinfo); -- cgit From f7d3b4f5203832f57ece96300888323fd5a6c6b3 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Thu, 23 Feb 2017 13:02:10 +0100 Subject: mac80211_hwsim: Make sure NEW_RADIO contains final name ieee80211_alloc_hw_nm will validate the requested name (if any) before creating the new device and may use a name different from the one requested rather than fail. Make sure the HWSIM_CMD_NEW_RADIO event/response generated has the final name or userspace will receive the wrong name. Note that mac80211_hwsim_new_radio may now modify params. A check for duplicate radio name could be added separately. Signed-off-by: Andrew Zaborowski Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 307a53a7da23..7b9662ae0143 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2438,6 +2438,9 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, goto failed; } + /* ieee80211_alloc_hw_nm may have used a default name */ + param->hwname = wiphy_name(hw->wiphy); + if (info) net = genl_info_net(info); else -- cgit From 037651950d5800f236407e139572433a53efb798 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 2 Mar 2017 09:39:40 +0100 Subject: mac80211_hwsim: fix command documentation indentation Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h index 39f22467ca2a..3f5eda591dba 100644 --- a/drivers/net/wireless/mac80211_hwsim.h +++ b/drivers/net/wireless/mac80211_hwsim.h @@ -57,12 +57,12 @@ enum hwsim_tx_control_flags { * @HWSIM_CMD_REGISTER: request to register and received all broadcasted * frames by any mac80211_hwsim radio device. * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user - * space, uses: + * space, uses: * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER, * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE, * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE, %HWSIM_ATTR_FREQ (optional) * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to - * kernel, uses: + * kernel, uses: * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS, * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE * @HWSIM_CMD_NEW_RADIO: create a new radio with the given parameters, -- cgit From 85bbd80373a7cc9d06d3ef103b1a93d934a8da43 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 6 Mar 2017 11:56:06 +0200 Subject: mac80211_hwsim: Add channel 169 (5845 MHz) This channel is defined in the IEEE 802.11 standard and available in number of countries, so extend the mac80211_hwsim channel list to cover channel 169 to enable additional testing. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 7b9662ae0143..8b823c70ab69 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -350,6 +350,7 @@ static const struct ieee80211_channel hwsim_channels_5ghz[] = { CHAN5G(5785), /* Channel 157 */ CHAN5G(5805), /* Channel 161 */ CHAN5G(5825), /* Channel 165 */ + CHAN5G(5845), /* Channel 169 */ }; static const struct ieee80211_rate hwsim_rates[] = { -- cgit From f8f118ceaa562d5b49252ecbfd7fe1f704f4e076 Mon Sep 17 00:00:00 2001 From: Ondřej Lysoněk Date: Fri, 3 Mar 2017 13:45:35 +0100 Subject: mac80211: Use setup_timer instead of init_timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use setup_timer() and setup_deferrable_timer() to set the data and function timer fields. It makes the code cleaner and will allow for easier change of the timer struct internals. Signed-off-by: Ondřej Lysoněk Signed-off-by: Jiri Slaby Cc: Johannes Berg Cc: "David S. Miller" Cc: Cc: Signed-off-by: Johannes Berg --- net/mac80211/agg-rx.c | 12 ++++++------ net/mac80211/agg-tx.c | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 4456559cb056..1b7a4daf283c 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -357,14 +357,14 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, spin_lock_init(&tid_agg_rx->reorder_lock); /* rx timer */ - tid_agg_rx->session_timer.function = sta_rx_agg_session_timer_expired; - tid_agg_rx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid]; - init_timer_deferrable(&tid_agg_rx->session_timer); + setup_deferrable_timer(&tid_agg_rx->session_timer, + sta_rx_agg_session_timer_expired, + (unsigned long)&sta->timer_to_tid[tid]); /* rx reorder timer */ - tid_agg_rx->reorder_timer.function = sta_rx_agg_reorder_timer_expired; - tid_agg_rx->reorder_timer.data = (unsigned long)&sta->timer_to_tid[tid]; - init_timer(&tid_agg_rx->reorder_timer); + setup_timer(&tid_agg_rx->reorder_timer, + sta_rx_agg_reorder_timer_expired, + (unsigned long)&sta->timer_to_tid[tid]); /* prepare reordering buffer */ tid_agg_rx->reorder_buf = diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 45319cc01121..60e2a62f7bef 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -670,14 +670,14 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, tid_tx->timeout = timeout; /* response timer */ - tid_tx->addba_resp_timer.function = sta_addba_resp_timer_expired; - tid_tx->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid]; - init_timer(&tid_tx->addba_resp_timer); + setup_timer(&tid_tx->addba_resp_timer, + sta_addba_resp_timer_expired, + (unsigned long)&sta->timer_to_tid[tid]); /* tx timer */ - tid_tx->session_timer.function = sta_tx_agg_session_timer_expired; - tid_tx->session_timer.data = (unsigned long)&sta->timer_to_tid[tid]; - init_timer_deferrable(&tid_tx->session_timer); + setup_deferrable_timer(&tid_tx->session_timer, + sta_tx_agg_session_timer_expired, + (unsigned long)&sta->timer_to_tid[tid]); /* assign a dialog token */ sta->ampdu_mlme.dialog_token_allocator++; -- cgit From 7f813ce1bd103a28e2333bd97cc6782c8e2fc8a9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 6 Nov 2013 10:37:34 +0100 Subject: mac80211_hwsim: report survey data for scanned channels Currently, hwsim is reporting survey data (only a fake noise floor) for the current channel. This breaks when the multi-channel support is enabled since then there's no current channel. Make the dummy implementation closer to a real one and only report data while scanning, for all the scanned channels. At other times, no survey data might be available (in real hardware) due to power- save for example. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 73 ++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 8b823c70ab69..67fc91dfcecd 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -526,6 +526,11 @@ struct mac80211_hwsim_data { struct ieee80211_vif *hw_scan_vif; int scan_chan_idx; u8 scan_addr[ETH_ALEN]; + struct { + struct ieee80211_channel *channel; + unsigned long next_start, start, end; + } survey_data[ARRAY_SIZE(hwsim_channels_2ghz) + + ARRAY_SIZE(hwsim_channels_5ghz)]; struct ieee80211_channel *channel; u64 beacon_int /* beacon interval in us */; @@ -1577,6 +1582,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) [IEEE80211_SMPS_STATIC] = "static", [IEEE80211_SMPS_DYNAMIC] = "dynamic", }; + int idx; if (conf->chandef.chan) wiphy_debug(hw->wiphy, @@ -1599,9 +1605,33 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) data->idle = !!(conf->flags & IEEE80211_CONF_IDLE); - data->channel = conf->chandef.chan; + WARN_ON(conf->chandef.chan && data->use_chanctx); + + mutex_lock(&data->mutex); + if (data->scanning && conf->chandef.chan) { + for (idx = 0; idx < ARRAY_SIZE(data->survey_data); idx++) { + if (data->survey_data[idx].channel == data->channel) { + data->survey_data[idx].start = + data->survey_data[idx].next_start; + data->survey_data[idx].end = jiffies; + break; + } + } - WARN_ON(data->channel && data->use_chanctx); + data->channel = conf->chandef.chan; + + for (idx = 0; idx < ARRAY_SIZE(data->survey_data); idx++) { + if (data->survey_data[idx].channel && + data->survey_data[idx].channel != data->channel) + continue; + data->survey_data[idx].channel = data->channel; + data->survey_data[idx].next_start = jiffies; + break; + } + } else { + data->channel = conf->chandef.chan; + } + mutex_unlock(&data->mutex); data->power_level = conf->power_level; if (!data->started || !data->beacon_int) @@ -1788,28 +1818,39 @@ static int mac80211_hwsim_conf_tx( return 0; } -static int mac80211_hwsim_get_survey( - struct ieee80211_hw *hw, int idx, - struct survey_info *survey) +static int mac80211_hwsim_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) { - struct ieee80211_conf *conf = &hw->conf; + struct mac80211_hwsim_data *hwsim = hw->priv; wiphy_debug(hw->wiphy, "%s (idx=%d)\n", __func__, idx); - if (idx != 0) + if (idx < 0 || idx >= ARRAY_SIZE(hwsim->survey_data)) return -ENOENT; - /* Current channel */ - survey->channel = conf->chandef.chan; + mutex_lock(&hwsim->mutex); + survey->channel = hwsim->survey_data[idx].channel; + if (!survey->channel) { + mutex_unlock(&hwsim->mutex); + return -ENOENT; + } /* - * Magically conjured noise level --- this is only ok for simulated hardware. + * Magically conjured dummy values --- this is only ok for simulated hardware. * - * A real driver which cannot determine the real channel noise MUST NOT - * report any noise, especially not a magically conjured one :-) + * A real driver which cannot determine real values noise MUST NOT + * report any, especially not a magically conjured ones :-) */ - survey->filled = SURVEY_INFO_NOISE_DBM; + survey->filled = SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY; survey->noise = -92; + survey->time = + jiffies_to_msecs(hwsim->survey_data[idx].end - + hwsim->survey_data[idx].start); + /* report 12.5% of channel time is used */ + survey->time_busy = survey->time/8; + mutex_unlock(&hwsim->mutex); return 0; } @@ -1987,6 +2028,10 @@ static void hw_scan_work(struct work_struct *work) } ieee80211_queue_delayed_work(hwsim->hw, &hwsim->hw_scan, msecs_to_jiffies(dwell)); + hwsim->survey_data[hwsim->scan_chan_idx].channel = hwsim->tmp_chan; + hwsim->survey_data[hwsim->scan_chan_idx].start = jiffies; + hwsim->survey_data[hwsim->scan_chan_idx].end = + jiffies + msecs_to_jiffies(dwell); hwsim->scan_chan_idx++; mutex_unlock(&hwsim->mutex); } @@ -2012,6 +2057,7 @@ static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw, hw_req->req.mac_addr_mask); else memcpy(hwsim->scan_addr, vif->addr, ETH_ALEN); + memset(hwsim->survey_data, 0, sizeof(hwsim->survey_data)); mutex_unlock(&hwsim->mutex); wiphy_debug(hw->wiphy, "hwsim hw_scan request\n"); @@ -2058,6 +2104,7 @@ static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw, memcpy(hwsim->scan_addr, mac_addr, ETH_ALEN); hwsim->scanning = true; + memset(hwsim->survey_data, 0, sizeof(hwsim->survey_data)); out: mutex_unlock(&hwsim->mutex); -- cgit From b35a51c7dd25a823767969e3089542d7478777e9 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 27 Feb 2017 17:04:33 +0530 Subject: cfg80211: Make pre-CAC results valid only for ETSI domain DFS requirement for ETSI domain (section 4.7.1.4 in ETSI EN 301 893 V1.8.1) is the only one which explicitly states that once DFS channel is marked as available afer the CAC, this channel will remain in available state even moving to a different operating channel. But the same is not explicitly stated in FCC DFS requirement. Also, Pre-CAC requriements are not explicitly mentioned in FCC requirement. Current implementation in keeping DFS channel in available state is same as described in ETSI domain. For non-ETSI DFS domain, this patch gives a grace period of 2 seconds since the completion of successful CAC before moving the channel's DFS state to 'usable' from 'available' state. The same grace period is checked against the channel's dfs_state_entered timestamp while deciding if a DFS channel is available for operation. There is a new radar event, NL80211_RADAR_PRE_CAC_EXPIRED, reported when DFS channel is moved from available to usable state after the grace period. Also make sure the DFS channel state is reset to usable once the beaconing operation on that channel is brought down (like stop_ap, leave_ibss and leave_mesh) in non-ETSI domain. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 5 +++ net/wireless/ap.c | 5 +++ net/wireless/chan.c | 101 +++++++++++++++++++++++++++++++++++++++++++ net/wireless/core.h | 10 +++++ net/wireless/ibss.c | 1 + net/wireless/mesh.c | 1 + net/wireless/mlme.c | 40 +++++++++++++---- net/wireless/reg.c | 28 ++++++++++++ net/wireless/reg.h | 14 ++++++ 9 files changed, 196 insertions(+), 9 deletions(-) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 9a499b15cfbc..cd4dfef58fab 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4913,12 +4913,17 @@ enum nl80211_smps_mode { * change to the channel status. * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is * over, channel becomes usable. + * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this + * non-operating channel is expired and no longer valid. New CAC must + * be done on this channel before starting the operation. This is not + * applicable for ETSI dfs domain where pre-CAC is valid for ever. */ enum nl80211_radar_event { NL80211_RADAR_DETECTED, NL80211_RADAR_CAC_FINISHED, NL80211_RADAR_CAC_ABORTED, NL80211_RADAR_NOP_FINISHED, + NL80211_RADAR_PRE_CAC_EXPIRED, }; /** diff --git a/net/wireless/ap.c b/net/wireless/ap.c index bdad1f951561..25666d3009be 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -32,6 +32,11 @@ int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, rdev_set_qos_map(rdev, dev, NULL); if (notify) nl80211_send_ap_stopped(wdev); + + /* Should we apply the grace period during beaconing interface + * shutdown also? + */ + cfg80211_sched_dfs_chan_update(rdev); } return err; diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 5497d022fada..099f13c0c39e 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -456,6 +456,107 @@ bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy, return (r1 + r2 > 0); } +/* + * Checks if center frequency of chan falls with in the bandwidth + * range of chandef. + */ +bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef, + struct ieee80211_channel *chan) +{ + int width; + u32 cf_offset, freq; + + if (chandef->chan->center_freq == chan->center_freq) + return true; + + width = cfg80211_chandef_get_width(chandef); + if (width <= 20) + return false; + + cf_offset = width / 2 - 10; + + for (freq = chandef->center_freq1 - width / 2 + 10; + freq <= chandef->center_freq1 + width / 2 - 10; freq += 20) { + if (chan->center_freq == freq) + return true; + } + + if (!chandef->center_freq2) + return false; + + for (freq = chandef->center_freq2 - width / 2 + 10; + freq <= chandef->center_freq2 + width / 2 - 10; freq += 20) { + if (chan->center_freq == freq) + return true; + } + + return false; +} + +bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev) +{ + bool active = false; + + ASSERT_WDEV_LOCK(wdev); + + if (!wdev->chandef.chan) + return false; + + switch (wdev->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + active = wdev->beacon_interval != 0; + break; + case NL80211_IFTYPE_ADHOC: + active = wdev->ssid_len != 0; + break; + case NL80211_IFTYPE_MESH_POINT: + active = wdev->mesh_id_len != 0; + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_OCB: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_P2P_DEVICE: + /* Can NAN type be considered as beaconing interface? */ + case NL80211_IFTYPE_NAN: + break; + case NL80211_IFTYPE_UNSPECIFIED: + case NUM_NL80211_IFTYPES: + WARN_ON(1); + } + + return active; +} + +bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan) +{ + struct wireless_dev *wdev; + + ASSERT_RTNL(); + + if (!(chan->flags & IEEE80211_CHAN_RADAR)) + return false; + + list_for_each_entry(wdev, &wiphy->wdev_list, list) { + wdev_lock(wdev); + if (!cfg80211_beaconing_iface_active(wdev)) { + wdev_unlock(wdev); + continue; + } + + if (cfg80211_is_sub_chan(&wdev->chandef, chan)) { + wdev_unlock(wdev); + return true; + } + wdev_unlock(wdev); + } + + return false; +} static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy, u32 center_freq, diff --git a/net/wireless/core.h b/net/wireless/core.h index efa690a7ef8d..519a29ebde5b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -466,6 +466,16 @@ unsigned int cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef); +void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev); + +bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan); + +bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev); + +bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef, + struct ieee80211_channel *chan); + static inline unsigned int elapsed_jiffies_msecs(unsigned long start) { unsigned long end = jiffies; diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 364f900a3dc4..10bf040a0982 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -190,6 +190,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) if (!nowext) wdev->wext.ibss.ssid_len = 0; #endif + cfg80211_sched_dfs_chan_update(rdev); } void cfg80211_clear_ibss(struct net_device *dev, bool nowext) diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 2d8518a37eab..ec0b1c20ac99 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -262,6 +262,7 @@ int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, wdev->beacon_interval = 0; memset(&wdev->chandef, 0, sizeof(wdev->chandef)); rdev_set_qos_map(rdev, dev, NULL); + cfg80211_sched_dfs_chan_update(rdev); } return err; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 22b3d9990065..cd29366a5206 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -745,6 +745,12 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, } EXPORT_SYMBOL(cfg80211_rx_mgmt); +void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev) +{ + cancel_delayed_work(&rdev->dfs_update_channels_wk); + queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, 0); +} + void cfg80211_dfs_channels_update_work(struct work_struct *work) { struct delayed_work *delayed_work = to_delayed_work(work); @@ -755,6 +761,8 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) struct wiphy *wiphy; bool check_again = false; unsigned long timeout, next_time = 0; + unsigned long time_dfs_update; + enum nl80211_radar_event radar_event; int bandid, i; rdev = container_of(delayed_work, struct cfg80211_registered_device, @@ -770,11 +778,27 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) for (i = 0; i < sband->n_channels; i++) { c = &sband->channels[i]; - if (c->dfs_state != NL80211_DFS_UNAVAILABLE) + if (!(c->flags & IEEE80211_CHAN_RADAR)) + continue; + + if (c->dfs_state != NL80211_DFS_UNAVAILABLE && + c->dfs_state != NL80211_DFS_AVAILABLE) continue; - timeout = c->dfs_state_entered + msecs_to_jiffies( - IEEE80211_DFS_MIN_NOP_TIME_MS); + if (c->dfs_state == NL80211_DFS_UNAVAILABLE) { + time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS; + radar_event = NL80211_RADAR_NOP_FINISHED; + } else { + if (regulatory_pre_cac_allowed(wiphy) || + cfg80211_any_wiphy_oper_chan(wiphy, c)) + continue; + + time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS; + radar_event = NL80211_RADAR_PRE_CAC_EXPIRED; + } + + timeout = c->dfs_state_entered + + msecs_to_jiffies(time_dfs_update); if (time_after_eq(jiffies, timeout)) { c->dfs_state = NL80211_DFS_USABLE; @@ -784,8 +808,8 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) NL80211_CHAN_NO_HT); nl80211_radar_notify(rdev, &chandef, - NL80211_RADAR_NOP_FINISHED, - NULL, GFP_ATOMIC); + radar_event, NULL, + GFP_ATOMIC); continue; } @@ -810,7 +834,6 @@ void cfg80211_radar_event(struct wiphy *wiphy, gfp_t gfp) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - unsigned long timeout; trace_cfg80211_radar_event(wiphy, chandef); @@ -820,9 +843,7 @@ void cfg80211_radar_event(struct wiphy *wiphy, */ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE); - timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS); - queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, - timeout); + cfg80211_sched_dfs_chan_update(rdev); nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp); } @@ -851,6 +872,7 @@ void cfg80211_cac_event(struct net_device *netdev, msecs_to_jiffies(wdev->cac_time_ms); WARN_ON(!time_after_eq(jiffies, timeout)); cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); + cfg80211_sched_dfs_chan_update(rdev); break; case NL80211_RADAR_CAC_ABORTED: break; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 753efcd51fa3..e59b192459e8 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -3120,6 +3120,34 @@ bool regulatory_indoor_allowed(void) return reg_is_indoor; } +bool regulatory_pre_cac_allowed(struct wiphy *wiphy) +{ + const struct ieee80211_regdomain *regd = NULL; + const struct ieee80211_regdomain *wiphy_regd = NULL; + bool pre_cac_allowed = false; + + rcu_read_lock(); + + regd = rcu_dereference(cfg80211_regdomain); + wiphy_regd = rcu_dereference(wiphy->regd); + if (!wiphy_regd) { + if (regd->dfs_region == NL80211_DFS_ETSI) + pre_cac_allowed = true; + + rcu_read_unlock(); + + return pre_cac_allowed; + } + + if (regd->dfs_region == wiphy_regd->dfs_region && + wiphy_regd->dfs_region == NL80211_DFS_ETSI) + pre_cac_allowed = true; + + rcu_read_unlock(); + + return pre_cac_allowed; +} + int __init regulatory_init(void) { int err = 0; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index f6ced316b5a4..ff078f093989 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -143,4 +143,18 @@ int cfg80211_get_unii(int freq); */ bool regulatory_indoor_allowed(void); +/* + * Grace period to timeout pre-CAC results on the dfs channels. This timeout + * value is used for Non-ETSI domain. + * TODO: May be make this timeout available through regdb? + */ +#define REG_PRE_CAC_EXPIRY_GRACE_MS 2000 + +/** + * regulatory_pre_cac_allowed - if pre-CAC allowed in the current dfs domain + * @wiphy: wiphy for which pre-CAC capability is checked. + + * Pre-CAC is allowed only in ETSI domain. + */ +bool regulatory_pre_cac_allowed(struct wiphy *wiphy); #endif /* __NET_WIRELESS_REG_H */ -- cgit From 34373d12f3cbb74960a73431138ef619d857996f Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 27 Feb 2017 17:04:34 +0530 Subject: cfg80211: Disallow moving out of operating DFS channel in non-ETSI For non-ETSI regulatory domain, CAC result on DFS channel may not be valid once moving out of that channel (as done during remain-on-channel, scannning and off-channel tx). Running CAC on an operating DFS channel after every off-channel operation will only add complexity and disturb the current link. Better do not allow any off-channel switch from a DFS operating channel in non-ETSI domain. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d516527fcb8e..b15903b9c0ab 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6556,6 +6556,19 @@ static int nl80211_parse_random_mac(struct nlattr **attrs, return 0; } +static bool cfg80211_off_channel_oper_allowed(struct wireless_dev *wdev) +{ + ASSERT_WDEV_LOCK(wdev); + + if (!cfg80211_beaconing_iface_active(wdev)) + return true; + + if (!(wdev->chandef.chan->flags & IEEE80211_CHAN_RADAR)) + return true; + + return regulatory_pre_cac_allowed(wdev->wiphy); +} + static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -6681,6 +6694,25 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) request->n_channels = i; + wdev_lock(wdev); + if (!cfg80211_off_channel_oper_allowed(wdev)) { + struct ieee80211_channel *chan; + + if (request->n_channels != 1) { + wdev_unlock(wdev); + err = -EBUSY; + goto out_free; + } + + chan = request->channels[0]; + if (chan->center_freq != wdev->chandef.chan->center_freq) { + wdev_unlock(wdev); + err = -EBUSY; + goto out_free; + } + } + wdev_unlock(wdev); + i = 0; if (n_ssids) { nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { @@ -9103,6 +9135,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; struct cfg80211_chan_def chandef; + const struct cfg80211_chan_def *compat_chandef; struct sk_buff *msg; void *hdr; u64 cookie; @@ -9131,6 +9164,18 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, if (err) return err; + wdev_lock(wdev); + if (!cfg80211_off_channel_oper_allowed(wdev) && + !cfg80211_chandef_identical(&wdev->chandef, &chandef)) { + compat_chandef = cfg80211_chandef_compatible(&wdev->chandef, + &chandef); + if (compat_chandef != &chandef) { + wdev_unlock(wdev); + return -EBUSY; + } + } + wdev_unlock(wdev); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -9306,6 +9351,13 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) if (!chandef.chan && params.offchan) return -EINVAL; + wdev_lock(wdev); + if (params.offchan && !cfg80211_off_channel_oper_allowed(wdev)) { + wdev_unlock(wdev); + return -EBUSY; + } + wdev_unlock(wdev); + params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]); -- cgit From 8976672736d6089ae011fda3482e30e4380276f8 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Mon, 27 Feb 2017 17:04:35 +0530 Subject: cfg80211: Share Channel DFS state across wiphys of same DFS domain Sharing DFS channel state across multiple wiphys (radios) could be useful with multiple radios on the system. When one radio completes CAC and markes the channel available another radio can use this information and start beaconing without really doing CAC. Whenever there is a state change in dfs channel associated to a particular wiphy the the same state change is propagated to other wiphys having the same DFS reg domain configuration. Also when a new wiphy is created the dfs channel state of other existing wiphys of same DFS domain is copied. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Johannes Berg --- net/wireless/chan.c | 30 ++++++++++--- net/wireless/core.c | 37 ++++++++++++++++ net/wireless/core.h | 6 +++ net/wireless/mlme.c | 10 +++++ net/wireless/reg.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++ net/wireless/reg.h | 22 ++++++++++ 6 files changed, 218 insertions(+), 7 deletions(-) diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 099f13c0c39e..b8aa5a7d5c77 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -531,16 +531,11 @@ bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev) return active; } -bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, - struct ieee80211_channel *chan) +static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan) { struct wireless_dev *wdev; - ASSERT_RTNL(); - - if (!(chan->flags & IEEE80211_CHAN_RADAR)) - return false; - list_for_each_entry(wdev, &wiphy->wdev_list, list) { wdev_lock(wdev); if (!cfg80211_beaconing_iface_active(wdev)) { @@ -558,6 +553,27 @@ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, return false; } +bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, + struct ieee80211_channel *chan) +{ + struct cfg80211_registered_device *rdev; + + ASSERT_RTNL(); + + if (!(chan->flags & IEEE80211_CHAN_RADAR)) + return false; + + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (!reg_dfs_domain_same(wiphy, &rdev->wiphy)) + continue; + + if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan)) + return true; + } + + return false; +} + static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy, u32 center_freq, u32 bandwidth) diff --git a/net/wireless/core.c b/net/wireless/core.c index 76e664144c8e..b1a028d381ef 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -357,6 +357,38 @@ static void cfg80211_sched_scan_stop_wk(struct work_struct *work) rtnl_unlock(); } +static void cfg80211_propagate_radar_detect_wk(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(work, struct cfg80211_registered_device, + propagate_radar_detect_wk); + + rtnl_lock(); + + regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->radar_chandef, + NL80211_DFS_UNAVAILABLE, + NL80211_RADAR_DETECTED); + + rtnl_unlock(); +} + +static void cfg80211_propagate_cac_done_wk(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev; + + rdev = container_of(work, struct cfg80211_registered_device, + propagate_cac_done_wk); + + rtnl_lock(); + + regulatory_propagate_dfs_state(&rdev->wiphy, &rdev->cac_done_chandef, + NL80211_DFS_AVAILABLE, + NL80211_RADAR_CAC_FINISHED); + + rtnl_unlock(); +} + /* exported functions */ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, @@ -456,6 +488,9 @@ use_default_name: spin_lock_init(&rdev->destroy_list_lock); INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); INIT_WORK(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk); + INIT_WORK(&rdev->propagate_radar_detect_wk, + cfg80211_propagate_radar_detect_wk); + INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk); #ifdef CONFIG_CFG80211_DEFAULT_PS rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -915,6 +950,8 @@ void wiphy_unregister(struct wiphy *wiphy) flush_work(&rdev->destroy_work); flush_work(&rdev->sched_scan_stop_wk); flush_work(&rdev->mlme_unreg_wk); + flush_work(&rdev->propagate_radar_detect_wk); + flush_work(&rdev->propagate_cac_done_wk); #ifdef CONFIG_PM if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) diff --git a/net/wireless/core.h b/net/wireless/core.h index 519a29ebde5b..a2fe8fc93283 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -97,6 +97,12 @@ struct cfg80211_registered_device { struct work_struct sched_scan_stop_wk; + struct cfg80211_chan_def radar_chandef; + struct work_struct propagate_radar_detect_wk; + + struct cfg80211_chan_def cac_done_chandef; + struct work_struct propagate_cac_done_wk; + /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ struct wiphy wiphy __aligned(NETDEV_ALIGN); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index cd29366a5206..01ce4a69e44d 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -810,6 +810,10 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) nl80211_radar_notify(rdev, &chandef, radar_event, NULL, GFP_ATOMIC); + + regulatory_propagate_dfs_state(wiphy, &chandef, + c->dfs_state, + radar_event); continue; } @@ -846,6 +850,9 @@ void cfg80211_radar_event(struct wiphy *wiphy, cfg80211_sched_dfs_chan_update(rdev); nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp); + + memcpy(&rdev->radar_chandef, chandef, sizeof(struct cfg80211_chan_def)); + queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk); } EXPORT_SYMBOL(cfg80211_radar_event); @@ -872,6 +879,9 @@ void cfg80211_cac_event(struct net_device *netdev, msecs_to_jiffies(wdev->cac_time_ms); WARN_ON(!time_after_eq(jiffies, timeout)); cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); + memcpy(&rdev->cac_done_chandef, chandef, + sizeof(struct cfg80211_chan_def)); + queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk); cfg80211_sched_dfs_chan_update(rdev); break; case NL80211_RADAR_CAC_ABORTED: diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e59b192459e8..a38f315819cd 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2067,6 +2067,88 @@ reg_process_hint_country_ie(struct wiphy *wiphy, return REG_REQ_IGNORE; } +bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2) +{ + const struct ieee80211_regdomain *wiphy1_regd = NULL; + const struct ieee80211_regdomain *wiphy2_regd = NULL; + const struct ieee80211_regdomain *cfg80211_regd = NULL; + bool dfs_domain_same; + + rcu_read_lock(); + + cfg80211_regd = rcu_dereference(cfg80211_regdomain); + wiphy1_regd = rcu_dereference(wiphy1->regd); + if (!wiphy1_regd) + wiphy1_regd = cfg80211_regd; + + wiphy2_regd = rcu_dereference(wiphy2->regd); + if (!wiphy2_regd) + wiphy2_regd = cfg80211_regd; + + dfs_domain_same = wiphy1_regd->dfs_region == wiphy2_regd->dfs_region; + + rcu_read_unlock(); + + return dfs_domain_same; +} + +static void reg_copy_dfs_chan_state(struct ieee80211_channel *dst_chan, + struct ieee80211_channel *src_chan) +{ + if (!(dst_chan->flags & IEEE80211_CHAN_RADAR) || + !(src_chan->flags & IEEE80211_CHAN_RADAR)) + return; + + if (dst_chan->flags & IEEE80211_CHAN_DISABLED || + src_chan->flags & IEEE80211_CHAN_DISABLED) + return; + + if (src_chan->center_freq == dst_chan->center_freq && + dst_chan->dfs_state == NL80211_DFS_USABLE) { + dst_chan->dfs_state = src_chan->dfs_state; + dst_chan->dfs_state_entered = src_chan->dfs_state_entered; + } +} + +static void wiphy_share_dfs_chan_state(struct wiphy *dst_wiphy, + struct wiphy *src_wiphy) +{ + struct ieee80211_supported_band *src_sband, *dst_sband; + struct ieee80211_channel *src_chan, *dst_chan; + int i, j, band; + + if (!reg_dfs_domain_same(dst_wiphy, src_wiphy)) + return; + + for (band = 0; band < NUM_NL80211_BANDS; band++) { + dst_sband = dst_wiphy->bands[band]; + src_sband = src_wiphy->bands[band]; + if (!dst_sband || !src_sband) + continue; + + for (i = 0; i < dst_sband->n_channels; i++) { + dst_chan = &dst_sband->channels[i]; + for (j = 0; j < src_sband->n_channels; j++) { + src_chan = &src_sband->channels[j]; + reg_copy_dfs_chan_state(dst_chan, src_chan); + } + } + } +} + +static void wiphy_all_share_dfs_chan_state(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *rdev; + + ASSERT_RTNL(); + + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (wiphy == &rdev->wiphy) + continue; + wiphy_share_dfs_chan_state(wiphy, &rdev->wiphy); + } +} + /* This processes *all* regulatory hints */ static void reg_process_hint(struct regulatory_request *reg_request) { @@ -2110,6 +2192,7 @@ static void reg_process_hint(struct regulatory_request *reg_request) if (treatment == REG_REQ_ALREADY_SET && wiphy && wiphy->regulatory_flags & REGULATORY_STRICT_REG) { wiphy_update_regulatory(wiphy, reg_request->initiator); + wiphy_all_share_dfs_chan_state(wiphy); reg_check_channels(); } @@ -3061,6 +3144,7 @@ void wiphy_regulatory_register(struct wiphy *wiphy) lr = get_last_request(); wiphy_update_regulatory(wiphy, lr->initiator); + wiphy_all_share_dfs_chan_state(wiphy); } void wiphy_regulatory_deregister(struct wiphy *wiphy) @@ -3148,6 +3232,42 @@ bool regulatory_pre_cac_allowed(struct wiphy *wiphy) return pre_cac_allowed; } +void regulatory_propagate_dfs_state(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef, + enum nl80211_dfs_state dfs_state, + enum nl80211_radar_event event) +{ + struct cfg80211_registered_device *rdev; + + ASSERT_RTNL(); + + if (WARN_ON(!cfg80211_chandef_valid(chandef))) + return; + + if (WARN_ON(!(chandef->chan->flags & IEEE80211_CHAN_RADAR))) + return; + + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (wiphy == &rdev->wiphy) + continue; + + if (!reg_dfs_domain_same(wiphy, &rdev->wiphy)) + continue; + + if (!ieee80211_get_channel(&rdev->wiphy, + chandef->chan->center_freq)) + continue; + + cfg80211_set_dfs_state(&rdev->wiphy, chandef, dfs_state); + + if (event == NL80211_RADAR_DETECTED || + event == NL80211_RADAR_CAC_FINISHED) + cfg80211_sched_dfs_chan_update(rdev); + + nl80211_radar_notify(rdev, chandef, event, NULL, GFP_KERNEL); + } +} + int __init regulatory_init(void) { int err = 0; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index ff078f093989..ca7fedf2e7a1 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -157,4 +157,26 @@ bool regulatory_indoor_allowed(void); * Pre-CAC is allowed only in ETSI domain. */ bool regulatory_pre_cac_allowed(struct wiphy *wiphy); + +/** + * regulatory_propagate_dfs_state - Propagate DFS channel state to other wiphys + * @wiphy - wiphy on which radar is detected and the event will be propagated + * to other available wiphys having the same DFS domain + * @chandef - Channel definition of radar detected channel + * @dfs_state - DFS channel state to be set + * @event - Type of radar event which triggered this DFS state change + * + * This function should be called with rtnl lock held. + */ +void regulatory_propagate_dfs_state(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef, + enum nl80211_dfs_state dfs_state, + enum nl80211_radar_event event); + +/** + * reg_dfs_domain_same - Checks if both wiphy have same DFS domain configured + * @wiphy1 - wiphy it's dfs_region to be checked against that of wiphy2 + * @wiphy2 - wiphy it's dfs_region to be checked against that of wiphy1 + */ +bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2); #endif /* __NET_WIRELESS_REG_H */ -- cgit From 3c1fece8819ed25257461b71e7c75a1f33eaa61d Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Mon, 20 Feb 2017 17:52:27 +0100 Subject: netfilter: nft_exthdr: Allow checking TCP option presence, too Honor NFT_EXTHDR_F_PRESENT flag so we check if the TCP option is present. Signed-off-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_exthdr.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index c308920b194c..d212a85d2f33 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -98,14 +98,21 @@ static void nft_exthdr_tcp_eval(const struct nft_expr *expr, goto err; offset = i + priv->offset; - dest[priv->len / NFT_REG32_SIZE] = 0; - memcpy(dest, opt + offset, priv->len); + if (priv->flags & NFT_EXTHDR_F_PRESENT) { + *dest = 1; + } else { + dest[priv->len / NFT_REG32_SIZE] = 0; + memcpy(dest, opt + offset, priv->len); + } return; } err: - regs->verdict.code = NFT_BREAK; + if (priv->flags & NFT_EXTHDR_F_PRESENT) + *dest = 0; + else + regs->verdict.code = NFT_BREAK; } static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = { -- cgit From 511040eea2234d9add3f33ba0e6c2e17944fdfb6 Mon Sep 17 00:00:00 2001 From: Laura Garcia Liebana Date: Thu, 23 Feb 2017 12:10:15 +0100 Subject: netfilter: nft_hash: rename nft_hash to nft_jhash This patch renames the local nft_hash structure and functions to nft_jhash in order to prepare the nft_hash module code to add new hash functions. Signed-off-by: Laura Garcia Liebana Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_hash.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index eb2721af898d..ccb834ef049b 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -17,7 +17,7 @@ #include #include -struct nft_hash { +struct nft_jhash { enum nft_registers sreg:8; enum nft_registers dreg:8; u8 len; @@ -26,11 +26,11 @@ struct nft_hash { u32 offset; }; -static void nft_hash_eval(const struct nft_expr *expr, - struct nft_regs *regs, - const struct nft_pktinfo *pkt) +static void nft_jhash_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) { - struct nft_hash *priv = nft_expr_priv(expr); + struct nft_jhash *priv = nft_expr_priv(expr); const void *data = ®s->data[priv->sreg]; u32 h; @@ -47,11 +47,11 @@ static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = { [NFTA_HASH_OFFSET] = { .type = NLA_U32 }, }; -static int nft_hash_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) +static int nft_jhash_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) { - struct nft_hash *priv = nft_expr_priv(expr); + struct nft_jhash *priv = nft_expr_priv(expr); u32 len; int err; @@ -92,10 +92,10 @@ static int nft_hash_init(const struct nft_ctx *ctx, NFT_DATA_VALUE, sizeof(u32)); } -static int nft_hash_dump(struct sk_buff *skb, - const struct nft_expr *expr) +static int nft_jhash_dump(struct sk_buff *skb, + const struct nft_expr *expr) { - const struct nft_hash *priv = nft_expr_priv(expr); + const struct nft_jhash *priv = nft_expr_priv(expr); if (nft_dump_register(skb, NFTA_HASH_SREG, priv->sreg)) goto nla_put_failure; @@ -117,17 +117,17 @@ nla_put_failure: } static struct nft_expr_type nft_hash_type; -static const struct nft_expr_ops nft_hash_ops = { +static const struct nft_expr_ops nft_jhash_ops = { .type = &nft_hash_type, - .size = NFT_EXPR_SIZE(sizeof(struct nft_hash)), - .eval = nft_hash_eval, - .init = nft_hash_init, - .dump = nft_hash_dump, + .size = NFT_EXPR_SIZE(sizeof(struct nft_jhash)), + .eval = nft_jhash_eval, + .init = nft_jhash_init, + .dump = nft_jhash_dump, }; static struct nft_expr_type nft_hash_type __read_mostly = { .name = "hash", - .ops = &nft_hash_ops, + .ops = &nft_jhash_ops, .policy = nft_hash_policy, .maxattr = NFTA_HASH_MAX, .owner = THIS_MODULE, -- cgit From 3206caded81ad9bdb2e7ff4c0b94ec5913df8618 Mon Sep 17 00:00:00 2001 From: Laura Garcia Liebana Date: Thu, 2 Mar 2017 17:00:14 +0100 Subject: netfilter: nft_hash: support of symmetric hash This patch provides symmetric hash support according to source ip address and port, and destination ip address and port. For this purpose, the __skb_get_hash_symmetric() is used to identify the flow as it uses FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL flag by default. The new attribute NFTA_HASH_TYPE has been included to support different types of hashing functions. Currently supported NFT_HASH_JENKINS through jhash and NFT_HASH_SYM through symhash. The main difference between both types are: - jhash requires an expression with sreg, symhash doesn't. - symhash supports modulus and offset, but not seed. Examples: nft add rule ip nat prerouting ct mark set jhash ip saddr mod 2 nft add rule ip nat prerouting ct mark set symhash mod 2 By default, jenkins hash will be used if no hash type is provided for compatibility reasons. Signed-off-by: Laura Garcia Liebana Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 13 +++++ net/netfilter/nft_hash.c | 99 +++++++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 05215d30fe5c..4f7d75682c59 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -815,6 +815,17 @@ enum nft_rt_keys { NFT_RT_NEXTHOP6, }; +/** + * enum nft_hash_types - nf_tables hash expression types + * + * @NFT_HASH_JENKINS: Jenkins Hash + * @NFT_HASH_SYM: Symmetric Hash + */ +enum nft_hash_types { + NFT_HASH_JENKINS, + NFT_HASH_SYM, +}; + /** * enum nft_hash_attributes - nf_tables hash expression netlink attributes * @@ -824,6 +835,7 @@ enum nft_rt_keys { * @NFTA_HASH_MODULUS: modulus value (NLA_U32) * @NFTA_HASH_SEED: seed value (NLA_U32) * @NFTA_HASH_OFFSET: add this offset value to hash result (NLA_U32) + * @NFTA_HASH_TYPE: hash operation (NLA_U32: nft_hash_types) */ enum nft_hash_attributes { NFTA_HASH_UNSPEC, @@ -833,6 +845,7 @@ enum nft_hash_attributes { NFTA_HASH_MODULUS, NFTA_HASH_SEED, NFTA_HASH_OFFSET, + NFTA_HASH_TYPE, __NFTA_HASH_MAX, }; #define NFTA_HASH_MAX (__NFTA_HASH_MAX - 1) diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index ccb834ef049b..a6a4633725bb 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -38,6 +38,25 @@ static void nft_jhash_eval(const struct nft_expr *expr, regs->data[priv->dreg] = h + priv->offset; } +struct nft_symhash { + enum nft_registers dreg:8; + u32 modulus; + u32 offset; +}; + +static void nft_symhash_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + struct nft_symhash *priv = nft_expr_priv(expr); + struct sk_buff *skb = pkt->skb; + u32 h; + + h = reciprocal_scale(__skb_get_hash_symmetric(skb), priv->modulus); + + regs->data[priv->dreg] = h + priv->offset; +} + static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = { [NFTA_HASH_SREG] = { .type = NLA_U32 }, [NFTA_HASH_DREG] = { .type = NLA_U32 }, @@ -45,6 +64,7 @@ static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = { [NFTA_HASH_MODULUS] = { .type = NLA_U32 }, [NFTA_HASH_SEED] = { .type = NLA_U32 }, [NFTA_HASH_OFFSET] = { .type = NLA_U32 }, + [NFTA_HASH_TYPE] = { .type = NLA_U32 }, }; static int nft_jhash_init(const struct nft_ctx *ctx, @@ -92,6 +112,32 @@ static int nft_jhash_init(const struct nft_ctx *ctx, NFT_DATA_VALUE, sizeof(u32)); } +static int nft_symhash_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_symhash *priv = nft_expr_priv(expr); + + if (!tb[NFTA_HASH_DREG] || + !tb[NFTA_HASH_MODULUS]) + return -EINVAL; + + if (tb[NFTA_HASH_OFFSET]) + priv->offset = ntohl(nla_get_be32(tb[NFTA_HASH_OFFSET])); + + priv->dreg = nft_parse_register(tb[NFTA_HASH_DREG]); + + priv->modulus = ntohl(nla_get_be32(tb[NFTA_HASH_MODULUS])); + if (priv->modulus <= 1) + return -ERANGE; + + if (priv->offset + priv->modulus - 1 < priv->offset) + return -EOVERFLOW; + + return nft_validate_register_store(ctx, priv->dreg, NULL, + NFT_DATA_VALUE, sizeof(u32)); +} + static int nft_jhash_dump(struct sk_buff *skb, const struct nft_expr *expr) { @@ -110,6 +156,28 @@ static int nft_jhash_dump(struct sk_buff *skb, if (priv->offset != 0) if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset))) goto nla_put_failure; + if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_JENKINS))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +static int nft_symhash_dump(struct sk_buff *skb, + const struct nft_expr *expr) +{ + const struct nft_symhash *priv = nft_expr_priv(expr); + + if (nft_dump_register(skb, NFTA_HASH_DREG, priv->dreg)) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_HASH_MODULUS, htonl(priv->modulus))) + goto nla_put_failure; + if (priv->offset != 0) + if (nla_put_be32(skb, NFTA_HASH_OFFSET, htonl(priv->offset))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_HASH_TYPE, htonl(NFT_HASH_SYM))) + goto nla_put_failure; return 0; nla_put_failure: @@ -125,9 +193,38 @@ static const struct nft_expr_ops nft_jhash_ops = { .dump = nft_jhash_dump, }; +static const struct nft_expr_ops nft_symhash_ops = { + .type = &nft_hash_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_symhash)), + .eval = nft_symhash_eval, + .init = nft_symhash_init, + .dump = nft_symhash_dump, +}; + +static const struct nft_expr_ops * +nft_hash_select_ops(const struct nft_ctx *ctx, + const struct nlattr * const tb[]) +{ + u32 type; + + if (!tb[NFTA_HASH_TYPE]) + return &nft_jhash_ops; + + type = ntohl(nla_get_be32(tb[NFTA_HASH_TYPE])); + switch (type) { + case NFT_HASH_SYM: + return &nft_symhash_ops; + case NFT_HASH_JENKINS: + return &nft_jhash_ops; + default: + break; + } + return ERR_PTR(-EOPNOTSUPP); +} + static struct nft_expr_type nft_hash_type __read_mostly = { .name = "hash", - .ops = &nft_jhash_ops, + .select_ops = &nft_hash_select_ops, .policy = nft_hash_policy, .maxattr = NFTA_HASH_MAX, .owner = THIS_MODULE, -- cgit From 13fa745da251606fcdbf58acd6b4a551bea6ae99 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 28 Feb 2017 14:09:24 -0800 Subject: netfilter: Use pr_cont where appropriate Logging output was changed when simple printks without KERN_CONT are now emitted on a new line and KERN_CONT is required to continue lines so use pr_cont. Miscellanea: o realign arguments o use print_hex_dump instead of a local variant Signed-off-by: Joe Perches Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/ebt_log.c | 34 +++++++++++++++++----------------- net/ipv4/netfilter/nf_nat_snmp_basic.c | 15 ++------------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 98b9c8e8615e..707caea39743 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -62,10 +62,10 @@ print_ports(const struct sk_buff *skb, uint8_t protocol, int offset) pptr = skb_header_pointer(skb, offset, sizeof(_ports), &_ports); if (pptr == NULL) { - printk(" INCOMPLETE TCP/UDP header"); + pr_cont(" INCOMPLETE TCP/UDP header"); return; } - printk(" SPT=%u DPT=%u", ntohs(pptr->src), ntohs(pptr->dst)); + pr_cont(" SPT=%u DPT=%u", ntohs(pptr->src), ntohs(pptr->dst)); } } @@ -100,11 +100,11 @@ ebt_log_packet(struct net *net, u_int8_t pf, unsigned int hooknum, ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); if (ih == NULL) { - printk(" INCOMPLETE IP header"); + pr_cont(" INCOMPLETE IP header"); goto out; } - printk(" IP SRC=%pI4 IP DST=%pI4, IP tos=0x%02X, IP proto=%d", - &ih->saddr, &ih->daddr, ih->tos, ih->protocol); + pr_cont(" IP SRC=%pI4 IP DST=%pI4, IP tos=0x%02X, IP proto=%d", + &ih->saddr, &ih->daddr, ih->tos, ih->protocol); print_ports(skb, ih->protocol, ih->ihl*4); goto out; } @@ -120,11 +120,11 @@ ebt_log_packet(struct net *net, u_int8_t pf, unsigned int hooknum, ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); if (ih == NULL) { - printk(" INCOMPLETE IPv6 header"); + pr_cont(" INCOMPLETE IPv6 header"); goto out; } - printk(" IPv6 SRC=%pI6 IPv6 DST=%pI6, IPv6 priority=0x%01X, Next Header=%d", - &ih->saddr, &ih->daddr, ih->priority, ih->nexthdr); + pr_cont(" IPv6 SRC=%pI6 IPv6 DST=%pI6, IPv6 priority=0x%01X, Next Header=%d", + &ih->saddr, &ih->daddr, ih->priority, ih->nexthdr); nexthdr = ih->nexthdr; offset_ph = ipv6_skip_exthdr(skb, sizeof(_iph), &nexthdr, &frag_off); if (offset_ph == -1) @@ -142,12 +142,12 @@ ebt_log_packet(struct net *net, u_int8_t pf, unsigned int hooknum, ah = skb_header_pointer(skb, 0, sizeof(_arph), &_arph); if (ah == NULL) { - printk(" INCOMPLETE ARP header"); + pr_cont(" INCOMPLETE ARP header"); goto out; } - printk(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d", - ntohs(ah->ar_hrd), ntohs(ah->ar_pro), - ntohs(ah->ar_op)); + pr_cont(" ARP HTYPE=%d, PTYPE=0x%04x, OPCODE=%d", + ntohs(ah->ar_hrd), ntohs(ah->ar_pro), + ntohs(ah->ar_op)); /* If it's for Ethernet and the lengths are OK, * then log the ARP payload @@ -161,17 +161,17 @@ ebt_log_packet(struct net *net, u_int8_t pf, unsigned int hooknum, ap = skb_header_pointer(skb, sizeof(_arph), sizeof(_arpp), &_arpp); if (ap == NULL) { - printk(" INCOMPLETE ARP payload"); + pr_cont(" INCOMPLETE ARP payload"); goto out; } - printk(" ARP MAC SRC=%pM ARP IP SRC=%pI4 ARP MAC DST=%pM ARP IP DST=%pI4", - ap->mac_src, ap->ip_src, ap->mac_dst, ap->ip_dst); + pr_cont(" ARP MAC SRC=%pM ARP IP SRC=%pI4 ARP MAC DST=%pM ARP IP DST=%pI4", + ap->mac_src, ap->ip_src, + ap->mac_dst, ap->ip_dst); } } out: - printk("\n"); + pr_cont("\n"); spin_unlock_bh(&ebt_log_lock); - } static unsigned int diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c index c9b52c361da2..ef49989c93b1 100644 --- a/net/ipv4/netfilter/nf_nat_snmp_basic.c +++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c @@ -998,18 +998,6 @@ err_id_free: * *****************************************************************************/ -static void hex_dump(const unsigned char *buf, size_t len) -{ - size_t i; - - for (i = 0; i < len; i++) { - if (i && !(i % 16)) - printk("\n"); - printk("%02x ", *(buf + i)); - } - printk("\n"); -} - /* * Parse and mangle SNMP message according to mapping. * (And this is the fucking 'basic' method). @@ -1026,7 +1014,8 @@ static int snmp_parse_mangle(unsigned char *msg, struct snmp_object *obj; if (debug > 1) - hex_dump(msg, len); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE, 16, 1, + msg, len, 0); asn1_open(&ctx, msg, len); -- cgit From 74664cf286dc0d2bf6960293cb219e5f741ac92b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 28 Feb 2017 11:31:15 +0000 Subject: netfilter: arp_tables: remove redundant check on ret being non-zero ret is initialized to zero and if it is set to non-zero in the xt_entry_foreach loop then we exit via the out_free label. Hence the check for ret being non-zero is redundant and can be removed. Detected by CoverityScan, CID#1357132 ("Logically Dead Code") Signed-off-by: Colin Ian King Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/arp_tables.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 6241a81fd7f5..f17dab1dee6e 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -562,8 +562,6 @@ static int translate_table(struct xt_table_info *newinfo, void *entry0, XT_ERROR_TARGET) == 0) ++newinfo->stacksize; } - if (ret != 0) - goto out_free; ret = -EINVAL; if (i != repl->num_entries) -- cgit From c56e3956c17bb24d18470122c0513d963e332205 Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Sun, 5 Mar 2017 21:02:23 +0800 Subject: netfilter: nf_tables: validate the expr explicitly after init successfully When we want to validate the expr's dependency or hooks, we must do two things to accomplish it. First, write a X_validate callback function and point ->validate to it. Second, call X_validate in init routine. This is very common, such as fib, nat, reject expr and so on ... It is a little ugly, since we will call X_validate in the expr's init routine, it's better to do it in nf_tables_newexpr. So we can avoid to do this again and again. After doing this, the second step listed above is not useful anymore, remove them now. Patch was tested by nftables/tests/py/nft-test.py and nftables/tests/shell/run-tests.sh. Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/nft_reject_bridge.c | 6 +----- net/netfilter/nf_tables_api.c | 11 +++++++++++ net/netfilter/nft_compat.c | 8 -------- net/netfilter/nft_fib.c | 2 +- net/netfilter/nft_masq.c | 4 ---- net/netfilter/nft_meta.c | 4 ---- net/netfilter/nft_nat.c | 4 ---- net/netfilter/nft_redir.c | 4 ---- net/netfilter/nft_reject.c | 5 ----- net/netfilter/nft_reject_inet.c | 6 +----- 10 files changed, 14 insertions(+), 40 deletions(-) diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c index 206dc266ecd2..346ef6b00b8f 100644 --- a/net/bridge/netfilter/nft_reject_bridge.c +++ b/net/bridge/netfilter/nft_reject_bridge.c @@ -375,11 +375,7 @@ static int nft_reject_bridge_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_reject *priv = nft_expr_priv(expr); - int icmp_code, err; - - err = nft_reject_bridge_validate(ctx, expr, NULL); - if (err < 0) - return err; + int icmp_code; if (tb[NFTA_REJECT_TYPE] == NULL) return -EINVAL; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 5e0ccfd5bb37..fd8789eccc92 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1772,8 +1772,19 @@ static int nf_tables_newexpr(const struct nft_ctx *ctx, goto err1; } + if (ops->validate) { + const struct nft_data *data = NULL; + + err = ops->validate(ctx, expr, &data); + if (err < 0) + goto err2; + } + return 0; +err2: + if (ops->destroy) + ops->destroy(ctx, expr); err1: expr->ops = NULL; return err; diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index c21e7eb8dce0..fab6bf3f955e 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -230,10 +230,6 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr, union nft_entry e = {}; int ret; - ret = nft_compat_chain_validate_dependency(target->table, ctx->chain); - if (ret < 0) - goto err; - target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info); if (ctx->nla[NFTA_RULE_COMPAT]) { @@ -419,10 +415,6 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, union nft_entry e = {}; int ret; - ret = nft_compat_chain_validate_dependency(match->table, ctx->chain); - if (ret < 0) - goto err; - match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info); if (ctx->nla[NFTA_RULE_COMPAT]) { diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index 29a4906adc27..fd0b19303b0d 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -112,7 +112,7 @@ int nft_fib_init(const struct nft_ctx *ctx, const struct nft_expr *expr, if (err < 0) return err; - return nft_fib_validate(ctx, expr, NULL); + return 0; } EXPORT_SYMBOL_GPL(nft_fib_init); diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c index 11ce016cd479..6ac03d4266c9 100644 --- a/net/netfilter/nft_masq.c +++ b/net/netfilter/nft_masq.c @@ -46,10 +46,6 @@ int nft_masq_init(const struct nft_ctx *ctx, struct nft_masq *priv = nft_expr_priv(expr); int err; - err = nft_masq_validate(ctx, expr, NULL); - if (err) - return err; - if (tb[NFTA_MASQ_FLAGS]) { priv->flags = ntohl(nla_get_be32(tb[NFTA_MASQ_FLAGS])); if (priv->flags & ~NF_NAT_RANGE_MASK) diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index e1f5ca9b423b..d14417aaf5d4 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -370,10 +370,6 @@ int nft_meta_set_init(const struct nft_ctx *ctx, return -EOPNOTSUPP; } - err = nft_meta_set_validate(ctx, expr, NULL); - if (err < 0) - return err; - priv->sreg = nft_parse_register(tb[NFTA_META_SREG]); err = nft_validate_register_load(priv->sreg, len); if (err < 0) diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c index 19a7bf3236f9..26a74dfb3b7a 100644 --- a/net/netfilter/nft_nat.c +++ b/net/netfilter/nft_nat.c @@ -138,10 +138,6 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, return -EINVAL; } - err = nft_nat_validate(ctx, expr, NULL); - if (err < 0) - return err; - if (tb[NFTA_NAT_FAMILY] == NULL) return -EINVAL; diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c index 40dcd05146d5..1e66538bf0ff 100644 --- a/net/netfilter/nft_redir.c +++ b/net/netfilter/nft_redir.c @@ -47,10 +47,6 @@ int nft_redir_init(const struct nft_ctx *ctx, unsigned int plen; int err; - err = nft_redir_validate(ctx, expr, NULL); - if (err < 0) - return err; - plen = FIELD_SIZEOF(struct nf_nat_range, min_addr.all); if (tb[NFTA_REDIR_REG_PROTO_MIN]) { priv->sreg_proto_min = diff --git a/net/netfilter/nft_reject.c b/net/netfilter/nft_reject.c index c64de3f7379d..29f5bd2377b0 100644 --- a/net/netfilter/nft_reject.c +++ b/net/netfilter/nft_reject.c @@ -42,11 +42,6 @@ int nft_reject_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_reject *priv = nft_expr_priv(expr); - int err; - - err = nft_reject_validate(ctx, expr, NULL); - if (err < 0) - return err; if (tb[NFTA_REJECT_TYPE] == NULL) return -EINVAL; diff --git a/net/netfilter/nft_reject_inet.c b/net/netfilter/nft_reject_inet.c index 9e90a02cb104..5a7fb5ff867d 100644 --- a/net/netfilter/nft_reject_inet.c +++ b/net/netfilter/nft_reject_inet.c @@ -66,11 +66,7 @@ static int nft_reject_inet_init(const struct nft_ctx *ctx, const struct nlattr * const tb[]) { struct nft_reject *priv = nft_expr_priv(expr); - int icmp_code, err; - - err = nft_reject_validate(ctx, expr, NULL); - if (err < 0) - return err; + int icmp_code; if (tb[NFTA_REJECT_TYPE] == NULL) return -EINVAL; -- cgit From c7a72e3fdb5d77486ca3a0ac942c0a2e0d80d5bb Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 6 Mar 2017 17:46:20 +0100 Subject: netfilter: nf_tables: add nft_set_lookup() This new function consolidates set lookup via either name or ID by introducing a new nft_set_lookup() function. Replace existing spots where we can use this too. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 9 +++++---- net/netfilter/nf_tables_api.c | 31 ++++++++++++++++++++++++------- net/netfilter/nft_dynset.c | 14 ++++---------- net/netfilter/nft_lookup.c | 14 ++++---------- net/netfilter/nft_objref.c | 14 ++++---------- 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 2aa8a9d80fbe..f0d46726d06e 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -385,10 +385,11 @@ static inline struct nft_set *nft_set_container_of(const void *priv) return (void *)priv - offsetof(struct nft_set, data); } -struct nft_set *nf_tables_set_lookup(const struct nft_table *table, - const struct nlattr *nla, u8 genmask); -struct nft_set *nf_tables_set_lookup_byid(const struct net *net, - const struct nlattr *nla, u8 genmask); +struct nft_set *nft_set_lookup(const struct net *net, + const struct nft_table *table, + const struct nlattr *nla_set_name, + const struct nlattr *nla_set_id, + u8 genmask); static inline unsigned long nft_set_gc_interval(const struct nft_set *set) { diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index fd8789eccc92..4559f5d66bcc 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2534,8 +2534,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, struct net *net, return 0; } -struct nft_set *nf_tables_set_lookup(const struct nft_table *table, - const struct nlattr *nla, u8 genmask) +static struct nft_set *nf_tables_set_lookup(const struct nft_table *table, + const struct nlattr *nla, u8 genmask) { struct nft_set *set; @@ -2549,11 +2549,10 @@ struct nft_set *nf_tables_set_lookup(const struct nft_table *table, } return ERR_PTR(-ENOENT); } -EXPORT_SYMBOL_GPL(nf_tables_set_lookup); -struct nft_set *nf_tables_set_lookup_byid(const struct net *net, - const struct nlattr *nla, - u8 genmask) +static struct nft_set *nf_tables_set_lookup_byid(const struct net *net, + const struct nlattr *nla, + u8 genmask) { struct nft_trans *trans; u32 id = ntohl(nla_get_be32(nla)); @@ -2568,7 +2567,25 @@ struct nft_set *nf_tables_set_lookup_byid(const struct net *net, } return ERR_PTR(-ENOENT); } -EXPORT_SYMBOL_GPL(nf_tables_set_lookup_byid); + +struct nft_set *nft_set_lookup(const struct net *net, + const struct nft_table *table, + const struct nlattr *nla_set_name, + const struct nlattr *nla_set_id, + u8 genmask) +{ + struct nft_set *set; + + set = nf_tables_set_lookup(table, nla_set_name, genmask); + if (IS_ERR(set)) { + if (!nla_set_id) + return set; + + set = nf_tables_set_lookup_byid(net, nla_set_id, genmask); + } + return set; +} +EXPORT_SYMBOL_GPL(nft_set_lookup); static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set, const char *name) diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c index 049ad2d9ee66..3948da380259 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -133,16 +133,10 @@ static int nft_dynset_init(const struct nft_ctx *ctx, priv->invert = true; } - set = nf_tables_set_lookup(ctx->table, tb[NFTA_DYNSET_SET_NAME], - genmask); - if (IS_ERR(set)) { - if (tb[NFTA_DYNSET_SET_ID]) - set = nf_tables_set_lookup_byid(ctx->net, - tb[NFTA_DYNSET_SET_ID], - genmask); - if (IS_ERR(set)) - return PTR_ERR(set); - } + set = nft_set_lookup(ctx->net, ctx->table, tb[NFTA_DYNSET_SET_NAME], + tb[NFTA_DYNSET_SET_ID], genmask); + if (IS_ERR(set)) + return PTR_ERR(set); if (set->ops->update == NULL) return -EOPNOTSUPP; diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index e21aea7e5ec8..475570e89ede 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -71,16 +71,10 @@ static int nft_lookup_init(const struct nft_ctx *ctx, tb[NFTA_LOOKUP_SREG] == NULL) return -EINVAL; - set = nf_tables_set_lookup(ctx->table, tb[NFTA_LOOKUP_SET], genmask); - if (IS_ERR(set)) { - if (tb[NFTA_LOOKUP_SET_ID]) { - set = nf_tables_set_lookup_byid(ctx->net, - tb[NFTA_LOOKUP_SET_ID], - genmask); - } - if (IS_ERR(set)) - return PTR_ERR(set); - } + set = nft_set_lookup(ctx->net, ctx->table, tb[NFTA_LOOKUP_SET], + tb[NFTA_LOOKUP_SET_ID], genmask); + if (IS_ERR(set)) + return PTR_ERR(set); if (set->flags & NFT_SET_EVAL) return -EOPNOTSUPP; diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c index 1ae8c49ca4a1..1dd428fbaaa3 100644 --- a/net/netfilter/nft_objref.c +++ b/net/netfilter/nft_objref.c @@ -116,16 +116,10 @@ static int nft_objref_map_init(const struct nft_ctx *ctx, struct nft_set *set; int err; - set = nf_tables_set_lookup(ctx->table, tb[NFTA_OBJREF_SET_NAME], genmask); - if (IS_ERR(set)) { - if (tb[NFTA_OBJREF_SET_ID]) { - set = nf_tables_set_lookup_byid(ctx->net, - tb[NFTA_OBJREF_SET_ID], - genmask); - } - if (IS_ERR(set)) - return PTR_ERR(set); - } + set = nft_set_lookup(ctx->net, ctx->table, tb[NFTA_OBJREF_SET_NAME], + tb[NFTA_OBJREF_SET_ID], genmask); + if (IS_ERR(set)) + return PTR_ERR(set); if (!(set->flags & NFT_SET_OBJECT)) return -EINVAL; -- cgit From df6f37225d62316f8505f10ef1099ee40fa3978b Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Wed, 22 Feb 2017 08:50:27 +0100 Subject: net: realtek: 8139cp: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/8139cp.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index 672f6b696069..72233ab9474b 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -1406,27 +1406,29 @@ static int cp_get_sset_count (struct net_device *dev, int sset) } } -static int cp_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int cp_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct cp_private *cp = netdev_priv(dev); int rc; unsigned long flags; spin_lock_irqsave(&cp->lock, flags); - rc = mii_ethtool_gset(&cp->mii_if, cmd); + rc = mii_ethtool_get_link_ksettings(&cp->mii_if, cmd); spin_unlock_irqrestore(&cp->lock, flags); return rc; } -static int cp_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int cp_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct cp_private *cp = netdev_priv(dev); int rc; unsigned long flags; spin_lock_irqsave(&cp->lock, flags); - rc = mii_ethtool_sset(&cp->mii_if, cmd); + rc = mii_ethtool_set_link_ksettings(&cp->mii_if, cmd); spin_unlock_irqrestore(&cp->lock, flags); return rc; @@ -1578,8 +1580,6 @@ static const struct ethtool_ops cp_ethtool_ops = { .get_drvinfo = cp_get_drvinfo, .get_regs_len = cp_get_regs_len, .get_sset_count = cp_get_sset_count, - .get_settings = cp_get_settings, - .set_settings = cp_set_settings, .nway_reset = cp_nway_reset, .get_link = ethtool_op_get_link, .get_msglevel = cp_get_msglevel, @@ -1593,6 +1593,8 @@ static const struct ethtool_ops cp_ethtool_ops = { .get_eeprom = cp_get_eeprom, .set_eeprom = cp_set_eeprom, .get_ringparam = cp_get_ringparam, + .get_link_ksettings = cp_get_link_ksettings, + .set_link_ksettings = cp_set_link_ksettings, }; static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) -- cgit From 182ed075e3c706e5e272b0b0135c8d02df59572f Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 23 Feb 2017 00:14:08 +0100 Subject: net: realtek: 8139too: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/8139too.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c index 89631753e799..ca22f2898664 100644 --- a/drivers/net/ethernet/realtek/8139too.c +++ b/drivers/net/ethernet/realtek/8139too.c @@ -2384,21 +2384,23 @@ static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo * strlcpy(info->bus_info, pci_name(tp->pci_dev), sizeof(info->bus_info)); } -static int rtl8139_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int rtl8139_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct rtl8139_private *tp = netdev_priv(dev); spin_lock_irq(&tp->lock); - mii_ethtool_gset(&tp->mii, cmd); + mii_ethtool_get_link_ksettings(&tp->mii, cmd); spin_unlock_irq(&tp->lock); return 0; } -static int rtl8139_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int rtl8139_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct rtl8139_private *tp = netdev_priv(dev); int rc; spin_lock_irq(&tp->lock); - rc = mii_ethtool_sset(&tp->mii, cmd); + rc = mii_ethtool_set_link_ksettings(&tp->mii, cmd); spin_unlock_irq(&tp->lock); return rc; } @@ -2480,8 +2482,6 @@ static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data) static const struct ethtool_ops rtl8139_ethtool_ops = { .get_drvinfo = rtl8139_get_drvinfo, - .get_settings = rtl8139_get_settings, - .set_settings = rtl8139_set_settings, .get_regs_len = rtl8139_get_regs_len, .get_regs = rtl8139_get_regs, .nway_reset = rtl8139_nway_reset, @@ -2493,6 +2493,8 @@ static const struct ethtool_ops rtl8139_ethtool_ops = { .get_strings = rtl8139_get_strings, .get_sset_count = rtl8139_get_sset_count, .get_ethtool_stats = rtl8139_get_ethtool_stats, + .get_link_ksettings = rtl8139_get_link_ksettings, + .set_link_ksettings = rtl8139_set_link_ksettings, }; static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -- cgit From 6fa1ba61520576cf1346c4ff09a056f2950cb3bf Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 23 Feb 2017 22:34:43 +0100 Subject: net: realtek: r8169: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169.c | 41 ++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 81f18a833527..24b045b777b6 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -817,7 +817,8 @@ struct rtl8169_private { } csi_ops; int (*set_speed)(struct net_device *, u8 aneg, u16 sp, u8 dpx, u32 adv); - int (*get_settings)(struct net_device *, struct ethtool_cmd *); + int (*get_link_ksettings)(struct net_device *, + struct ethtool_link_ksettings *); void (*phy_reset_enable)(struct rtl8169_private *tp); void (*hw_start)(struct net_device *); unsigned int (*phy_reset_pending)(struct rtl8169_private *tp); @@ -2115,41 +2116,49 @@ static void rtl8169_rx_vlan_tag(struct RxDesc *desc, struct sk_buff *skb) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & 0xffff)); } -static int rtl8169_gset_tbi(struct net_device *dev, struct ethtool_cmd *cmd) +static int rtl8169_get_link_ksettings_tbi(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct rtl8169_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->mmio_addr; u32 status; + u32 supported, advertising; - cmd->supported = + supported = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_FIBRE; - cmd->port = PORT_FIBRE; - cmd->transceiver = XCVR_INTERNAL; + cmd->base.port = PORT_FIBRE; status = RTL_R32(TBICSR); - cmd->advertising = (status & TBINwEnable) ? ADVERTISED_Autoneg : 0; - cmd->autoneg = !!(status & TBINwEnable); + advertising = (status & TBINwEnable) ? ADVERTISED_Autoneg : 0; + cmd->base.autoneg = !!(status & TBINwEnable); - ethtool_cmd_speed_set(cmd, SPEED_1000); - cmd->duplex = DUPLEX_FULL; /* Always set */ + cmd->base.speed = SPEED_1000; + cmd->base.duplex = DUPLEX_FULL; /* Always set */ + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return 0; } -static int rtl8169_gset_xmii(struct net_device *dev, struct ethtool_cmd *cmd) +static int rtl8169_get_link_ksettings_xmii(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct rtl8169_private *tp = netdev_priv(dev); - return mii_ethtool_gset(&tp->mii, cmd); + return mii_ethtool_get_link_ksettings(&tp->mii, cmd); } -static int rtl8169_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int rtl8169_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct rtl8169_private *tp = netdev_priv(dev); int rc; rtl_lock_work(tp); - rc = tp->get_settings(dev, cmd); + rc = tp->get_link_ksettings(dev, cmd); rtl_unlock_work(tp); return rc; @@ -2356,7 +2365,6 @@ static const struct ethtool_ops rtl8169_ethtool_ops = { .get_drvinfo = rtl8169_get_drvinfo, .get_regs_len = rtl8169_get_regs_len, .get_link = ethtool_op_get_link, - .get_settings = rtl8169_get_settings, .set_settings = rtl8169_set_settings, .get_msglevel = rtl8169_get_msglevel, .set_msglevel = rtl8169_set_msglevel, @@ -2368,6 +2376,7 @@ static const struct ethtool_ops rtl8169_ethtool_ops = { .get_ethtool_stats = rtl8169_get_ethtool_stats, .get_ts_info = ethtool_op_get_ts_info, .nway_reset = rtl8169_nway_reset, + .get_link_ksettings = rtl8169_get_link_ksettings, }; static void rtl8169_get_mac_version(struct rtl8169_private *tp, @@ -8351,14 +8360,14 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rtl_tbi_enabled(tp)) { tp->set_speed = rtl8169_set_speed_tbi; - tp->get_settings = rtl8169_gset_tbi; + tp->get_link_ksettings = rtl8169_get_link_ksettings_tbi; tp->phy_reset_enable = rtl8169_tbi_reset_enable; tp->phy_reset_pending = rtl8169_tbi_reset_pending; tp->link_ok = rtl8169_tbi_link_ok; tp->do_ioctl = rtl_tbi_ioctl; } else { tp->set_speed = rtl8169_set_speed_xmii; - tp->get_settings = rtl8169_gset_xmii; + tp->get_link_ksettings = rtl8169_get_link_ksettings_xmii; tp->phy_reset_enable = rtl8169_xmii_reset_enable; tp->phy_reset_pending = rtl8169_xmii_reset_pending; tp->link_ok = rtl8169_xmii_link_ok; -- cgit From de48015054fa697f647826ecee94d8f34633809f Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 26 Feb 2017 19:00:29 +0100 Subject: net: rocker: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/rocker/rocker_main.c | 55 ++++++++++++++++++------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 0f63a44a955d..b712ec23075b 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -1115,7 +1115,7 @@ rocker_cmd_get_port_settings_ethtool_proc(const struct rocker_port *rocker_port, const struct rocker_desc_info *desc_info, void *priv) { - struct ethtool_cmd *ecmd = priv; + struct ethtool_link_ksettings *ecmd = priv; const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1]; const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1]; u32 speed; @@ -1137,13 +1137,14 @@ rocker_cmd_get_port_settings_ethtool_proc(const struct rocker_port *rocker_port, duplex = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX]); autoneg = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]); - ecmd->transceiver = XCVR_INTERNAL; - ecmd->supported = SUPPORTED_TP; - ecmd->phy_address = 0xff; - ecmd->port = PORT_TP; - ethtool_cmd_speed_set(ecmd, speed); - ecmd->duplex = duplex ? DUPLEX_FULL : DUPLEX_HALF; - ecmd->autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; + ethtool_link_ksettings_zero_link_mode(ecmd, supported); + ethtool_link_ksettings_add_link_mode(ecmd, supported, TP); + + ecmd->base.phy_address = 0xff; + ecmd->base.port = PORT_TP; + ecmd->base.speed = speed; + ecmd->base.duplex = duplex ? DUPLEX_FULL : DUPLEX_HALF; + ecmd->base.autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; return 0; } @@ -1250,7 +1251,7 @@ rocker_cmd_set_port_settings_ethtool_prep(const struct rocker_port *rocker_port, struct rocker_desc_info *desc_info, void *priv) { - struct ethtool_cmd *ecmd = priv; + struct ethtool_link_ksettings *ecmd = priv; struct rocker_tlv *cmd_info; if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, @@ -1263,13 +1264,13 @@ rocker_cmd_set_port_settings_ethtool_prep(const struct rocker_port *rocker_port, rocker_port->pport)) return -EMSGSIZE; if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_SPEED, - ethtool_cmd_speed(ecmd))) + ecmd->base.speed)) return -EMSGSIZE; if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX, - ecmd->duplex)) + ecmd->base.duplex)) return -EMSGSIZE; if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG, - ecmd->autoneg)) + ecmd->base.autoneg)) return -EMSGSIZE; rocker_tlv_nest_end(desc_info, cmd_info); return 0; @@ -1347,8 +1348,9 @@ rocker_cmd_set_port_learning_prep(const struct rocker_port *rocker_port, return 0; } -static int rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port, - struct ethtool_cmd *ecmd) +static int +rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port, + struct ethtool_link_ksettings *ecmd) { return rocker_cmd_exec(rocker_port, false, rocker_cmd_get_port_settings_prep, NULL, @@ -1373,12 +1375,17 @@ static int rocker_cmd_get_port_settings_mode(struct rocker_port *rocker_port, rocker_cmd_get_port_settings_mode_proc, p_mode); } -static int rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port, - struct ethtool_cmd *ecmd) +static int +rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port, + const struct ethtool_link_ksettings *ecmd) { + struct ethtool_link_ksettings copy_ecmd; + + memcpy(©_ecmd, ecmd, sizeof(copy_ecmd)); + return rocker_cmd_exec(rocker_port, false, rocker_cmd_set_port_settings_ethtool_prep, - ecmd, NULL, NULL); + ©_ecmd, NULL, NULL); } static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port, @@ -2237,16 +2244,18 @@ static int rocker_router_fib_event(struct notifier_block *nb, * ethtool interface ********************/ -static int rocker_port_get_settings(struct net_device *dev, - struct ethtool_cmd *ecmd) +static int +rocker_port_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *ecmd) { struct rocker_port *rocker_port = netdev_priv(dev); return rocker_cmd_get_port_settings_ethtool(rocker_port, ecmd); } -static int rocker_port_set_settings(struct net_device *dev, - struct ethtool_cmd *ecmd) +static int +rocker_port_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *ecmd) { struct rocker_port *rocker_port = netdev_priv(dev); @@ -2388,13 +2397,13 @@ static int rocker_port_get_sset_count(struct net_device *netdev, int sset) } static const struct ethtool_ops rocker_port_ethtool_ops = { - .get_settings = rocker_port_get_settings, - .set_settings = rocker_port_set_settings, .get_drvinfo = rocker_port_get_drvinfo, .get_link = ethtool_op_get_link, .get_strings = rocker_port_get_strings, .get_ethtool_stats = rocker_port_get_stats, .get_sset_count = rocker_port_get_sset_count, + .get_link_ksettings = rocker_port_get_link_ksettings, + .set_link_ksettings = rocker_port_set_link_ksettings, }; /***************** -- cgit From b61a26f8d495edea3795079c87b3e4ad8ee93e1c Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 26 Feb 2017 22:48:59 +0100 Subject: net: sgi: ioc3-eth: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/sgi/ioc3-eth.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/sgi/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c index 57e6cef81ebe..52ead5524de7 100644 --- a/drivers/net/ethernet/sgi/ioc3-eth.c +++ b/drivers/net/ethernet/sgi/ioc3-eth.c @@ -1558,25 +1558,27 @@ static void ioc3_get_drvinfo (struct net_device *dev, strlcpy(info->bus_info, pci_name(ip->pdev), sizeof(info->bus_info)); } -static int ioc3_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int ioc3_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct ioc3_private *ip = netdev_priv(dev); int rc; spin_lock_irq(&ip->ioc3_lock); - rc = mii_ethtool_gset(&ip->mii, cmd); + rc = mii_ethtool_get_link_ksettings(&ip->mii, cmd); spin_unlock_irq(&ip->ioc3_lock); return rc; } -static int ioc3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int ioc3_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct ioc3_private *ip = netdev_priv(dev); int rc; spin_lock_irq(&ip->ioc3_lock); - rc = mii_ethtool_sset(&ip->mii, cmd); + rc = mii_ethtool_set_link_ksettings(&ip->mii, cmd); spin_unlock_irq(&ip->ioc3_lock); return rc; @@ -1608,10 +1610,10 @@ static u32 ioc3_get_link(struct net_device *dev) static const struct ethtool_ops ioc3_ethtool_ops = { .get_drvinfo = ioc3_get_drvinfo, - .get_settings = ioc3_get_settings, - .set_settings = ioc3_set_settings, .nway_reset = ioc3_nway_reset, .get_link = ioc3_get_link, + .get_link_ksettings = ioc3_get_link_ksettings, + .set_link_ksettings = ioc3_set_link_ksettings, }; static int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -- cgit From a972c3062f982c63525f20cbce00a6f329406c0a Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Mon, 27 Feb 2017 22:50:25 +0100 Subject: net: silan: sc92031: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/silan/sc92031.c | 83 ++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/silan/sc92031.c b/drivers/net/ethernet/silan/sc92031.c index 6c2e2b311c16..751c81848f35 100644 --- a/drivers/net/ethernet/silan/sc92031.c +++ b/drivers/net/ethernet/silan/sc92031.c @@ -1122,14 +1122,16 @@ static void sc92031_poll_controller(struct net_device *dev) } #endif -static int sc92031_ethtool_get_settings(struct net_device *dev, - struct ethtool_cmd *cmd) +static int +sc92031_ethtool_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct sc92031_priv *priv = netdev_priv(dev); void __iomem *port_base = priv->port_base; u8 phy_address; u32 phy_ctrl; u16 output_status; + u32 supported, advertising; spin_lock_bh(&priv->lock); @@ -1142,68 +1144,77 @@ static int sc92031_ethtool_get_settings(struct net_device *dev, spin_unlock_bh(&priv->lock); - cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full + supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII; - cmd->advertising = ADVERTISED_TP | ADVERTISED_MII; + advertising = ADVERTISED_TP | ADVERTISED_MII; if ((phy_ctrl & (PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10)) == (PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10)) - cmd->advertising |= ADVERTISED_Autoneg; + advertising |= ADVERTISED_Autoneg; if ((phy_ctrl & PhyCtrlSpd10) == PhyCtrlSpd10) - cmd->advertising |= ADVERTISED_10baseT_Half; + advertising |= ADVERTISED_10baseT_Half; if ((phy_ctrl & (PhyCtrlSpd10 | PhyCtrlDux)) == (PhyCtrlSpd10 | PhyCtrlDux)) - cmd->advertising |= ADVERTISED_10baseT_Full; + advertising |= ADVERTISED_10baseT_Full; if ((phy_ctrl & PhyCtrlSpd100) == PhyCtrlSpd100) - cmd->advertising |= ADVERTISED_100baseT_Half; + advertising |= ADVERTISED_100baseT_Half; if ((phy_ctrl & (PhyCtrlSpd100 | PhyCtrlDux)) == (PhyCtrlSpd100 | PhyCtrlDux)) - cmd->advertising |= ADVERTISED_100baseT_Full; + advertising |= ADVERTISED_100baseT_Full; if (phy_ctrl & PhyCtrlAne) - cmd->advertising |= ADVERTISED_Autoneg; + advertising |= ADVERTISED_Autoneg; - ethtool_cmd_speed_set(cmd, - (output_status & 0x2) ? SPEED_100 : SPEED_10); - cmd->duplex = (output_status & 0x4) ? DUPLEX_FULL : DUPLEX_HALF; - cmd->port = PORT_MII; - cmd->phy_address = phy_address; - cmd->transceiver = XCVR_INTERNAL; - cmd->autoneg = (phy_ctrl & PhyCtrlAne) ? AUTONEG_ENABLE : AUTONEG_DISABLE; + cmd->base.speed = (output_status & 0x2) ? SPEED_100 : SPEED_10; + cmd->base.duplex = (output_status & 0x4) ? DUPLEX_FULL : DUPLEX_HALF; + cmd->base.port = PORT_MII; + cmd->base.phy_address = phy_address; + cmd->base.autoneg = (phy_ctrl & PhyCtrlAne) ? + AUTONEG_ENABLE : AUTONEG_DISABLE; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return 0; } -static int sc92031_ethtool_set_settings(struct net_device *dev, - struct ethtool_cmd *cmd) +static int +sc92031_ethtool_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct sc92031_priv *priv = netdev_priv(dev); void __iomem *port_base = priv->port_base; - u32 speed = ethtool_cmd_speed(cmd); + u32 speed = cmd->base.speed; u32 phy_ctrl; u32 old_phy_ctrl; + u32 advertising; + + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); if (!(speed == SPEED_10 || speed == SPEED_100)) return -EINVAL; - if (!(cmd->duplex == DUPLEX_HALF || cmd->duplex == DUPLEX_FULL)) - return -EINVAL; - if (!(cmd->port == PORT_MII)) + if (!(cmd->base.duplex == DUPLEX_HALF || + cmd->base.duplex == DUPLEX_FULL)) return -EINVAL; - if (!(cmd->phy_address == 0x1f)) + if (!(cmd->base.port == PORT_MII)) return -EINVAL; - if (!(cmd->transceiver == XCVR_INTERNAL)) + if (!(cmd->base.phy_address == 0x1f)) return -EINVAL; - if (!(cmd->autoneg == AUTONEG_DISABLE || cmd->autoneg == AUTONEG_ENABLE)) + if (!(cmd->base.autoneg == AUTONEG_DISABLE || + cmd->base.autoneg == AUTONEG_ENABLE)) return -EINVAL; - if (cmd->autoneg == AUTONEG_ENABLE) { - if (!(cmd->advertising & (ADVERTISED_Autoneg + if (cmd->base.autoneg == AUTONEG_ENABLE) { + if (!(advertising & (ADVERTISED_Autoneg | ADVERTISED_100baseT_Full | ADVERTISED_100baseT_Half | ADVERTISED_10baseT_Full @@ -1213,15 +1224,15 @@ static int sc92031_ethtool_set_settings(struct net_device *dev, phy_ctrl = PhyCtrlAne; // FIXME: I'm not sure what the original code was trying to do - if (cmd->advertising & ADVERTISED_Autoneg) + if (advertising & ADVERTISED_Autoneg) phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100 | PhyCtrlSpd10; - if (cmd->advertising & ADVERTISED_100baseT_Full) + if (advertising & ADVERTISED_100baseT_Full) phy_ctrl |= PhyCtrlDux | PhyCtrlSpd100; - if (cmd->advertising & ADVERTISED_100baseT_Half) + if (advertising & ADVERTISED_100baseT_Half) phy_ctrl |= PhyCtrlSpd100; - if (cmd->advertising & ADVERTISED_10baseT_Full) + if (advertising & ADVERTISED_10baseT_Full) phy_ctrl |= PhyCtrlSpd10 | PhyCtrlDux; - if (cmd->advertising & ADVERTISED_10baseT_Half) + if (advertising & ADVERTISED_10baseT_Half) phy_ctrl |= PhyCtrlSpd10; } else { // FIXME: Whole branch guessed @@ -1232,7 +1243,7 @@ static int sc92031_ethtool_set_settings(struct net_device *dev, else /* cmd->speed == SPEED_100 */ phy_ctrl |= PhyCtrlSpd100; - if (cmd->duplex == DUPLEX_FULL) + if (cmd->base.duplex == DUPLEX_FULL) phy_ctrl |= PhyCtrlDux; } @@ -1368,8 +1379,6 @@ static void sc92031_ethtool_get_ethtool_stats(struct net_device *dev, } static const struct ethtool_ops sc92031_ethtool_ops = { - .get_settings = sc92031_ethtool_get_settings, - .set_settings = sc92031_ethtool_set_settings, .get_wol = sc92031_ethtool_get_wol, .set_wol = sc92031_ethtool_set_wol, .nway_reset = sc92031_ethtool_nway_reset, @@ -1377,6 +1386,8 @@ static const struct ethtool_ops sc92031_ethtool_ops = { .get_strings = sc92031_ethtool_get_strings, .get_sset_count = sc92031_ethtool_get_sset_count, .get_ethtool_stats = sc92031_ethtool_get_ethtool_stats, + .get_link_ksettings = sc92031_ethtool_get_link_ksettings, + .set_link_ksettings = sc92031_ethtool_set_link_ksettings, }; -- cgit From fb70eb20fea66426aee0a21cfa35ab79d3d10e1c Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Mon, 27 Feb 2017 23:06:41 +0100 Subject: net: sis: sis190: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/sis/sis190.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/sis/sis190.c b/drivers/net/ethernet/sis/sis190.c index 210e35d079dd..02da106c6e04 100644 --- a/drivers/net/ethernet/sis/sis190.c +++ b/drivers/net/ethernet/sis/sis190.c @@ -1734,18 +1734,20 @@ static void sis190_set_speed_auto(struct net_device *dev) BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET); } -static int sis190_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int sis190_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct sis190_private *tp = netdev_priv(dev); - return mii_ethtool_gset(&tp->mii_if, cmd); + return mii_ethtool_get_link_ksettings(&tp->mii_if, cmd); } -static int sis190_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int sis190_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct sis190_private *tp = netdev_priv(dev); - return mii_ethtool_sset(&tp->mii_if, cmd); + return mii_ethtool_set_link_ksettings(&tp->mii_if, cmd); } static void sis190_get_drvinfo(struct net_device *dev, @@ -1797,8 +1799,6 @@ static void sis190_set_msglevel(struct net_device *dev, u32 value) } static const struct ethtool_ops sis190_ethtool_ops = { - .get_settings = sis190_get_settings, - .set_settings = sis190_set_settings, .get_drvinfo = sis190_get_drvinfo, .get_regs_len = sis190_get_regs_len, .get_regs = sis190_get_regs, @@ -1806,6 +1806,8 @@ static const struct ethtool_ops sis190_ethtool_ops = { .get_msglevel = sis190_get_msglevel, .set_msglevel = sis190_set_msglevel, .nway_reset = sis190_nway_reset, + .get_link_ksettings = sis190_get_link_ksettings, + .set_link_ksettings = sis190_set_link_ksettings, }; static int sis190_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -- cgit From 7d59e319d94f2cffaf5d9d3604bbd841baec933f Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Mon, 27 Feb 2017 23:17:37 +0100 Subject: net: sis: sis900: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/sis/sis900.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/sis/sis900.c b/drivers/net/ethernet/sis/sis900.c index 1b6f6171d078..40bd88362e3d 100644 --- a/drivers/net/ethernet/sis/sis900.c +++ b/drivers/net/ethernet/sis/sis900.c @@ -2035,23 +2035,23 @@ static u32 sis900_get_link(struct net_device *net_dev) return mii_link_ok(&sis_priv->mii_info); } -static int sis900_get_settings(struct net_device *net_dev, - struct ethtool_cmd *cmd) +static int sis900_get_link_ksettings(struct net_device *net_dev, + struct ethtool_link_ksettings *cmd) { struct sis900_private *sis_priv = netdev_priv(net_dev); spin_lock_irq(&sis_priv->lock); - mii_ethtool_gset(&sis_priv->mii_info, cmd); + mii_ethtool_get_link_ksettings(&sis_priv->mii_info, cmd); spin_unlock_irq(&sis_priv->lock); return 0; } -static int sis900_set_settings(struct net_device *net_dev, - struct ethtool_cmd *cmd) +static int sis900_set_link_ksettings(struct net_device *net_dev, + const struct ethtool_link_ksettings *cmd) { struct sis900_private *sis_priv = netdev_priv(net_dev); int rt; spin_lock_irq(&sis_priv->lock); - rt = mii_ethtool_sset(&sis_priv->mii_info, cmd); + rt = mii_ethtool_set_link_ksettings(&sis_priv->mii_info, cmd); spin_unlock_irq(&sis_priv->lock); return rt; } @@ -2129,11 +2129,11 @@ static const struct ethtool_ops sis900_ethtool_ops = { .get_msglevel = sis900_get_msglevel, .set_msglevel = sis900_set_msglevel, .get_link = sis900_get_link, - .get_settings = sis900_get_settings, - .set_settings = sis900_set_settings, .nway_reset = sis900_nway_reset, .get_wol = sis900_get_wol, - .set_wol = sis900_set_wol + .set_wol = sis900_set_wol, + .get_link_ksettings = sis900_get_link_ksettings, + .set_link_ksettings = sis900_set_link_ksettings, }; /** -- cgit From 60269ae1b01e640aac8fc879aaf1e3dc98beb83b Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Mon, 27 Feb 2017 23:43:14 +0100 Subject: net: smsc: epic100: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/smsc/epic100.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/smsc/epic100.c b/drivers/net/ethernet/smsc/epic100.c index 5f2737189c72..db6dcb06193d 100644 --- a/drivers/net/ethernet/smsc/epic100.c +++ b/drivers/net/ethernet/smsc/epic100.c @@ -1387,25 +1387,27 @@ static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo * strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); } -static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int netdev_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct epic_private *np = netdev_priv(dev); int rc; spin_lock_irq(&np->lock); - rc = mii_ethtool_gset(&np->mii, cmd); + rc = mii_ethtool_get_link_ksettings(&np->mii, cmd); spin_unlock_irq(&np->lock); return rc; } -static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int netdev_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct epic_private *np = netdev_priv(dev); int rc; spin_lock_irq(&np->lock); - rc = mii_ethtool_sset(&np->mii, cmd); + rc = mii_ethtool_set_link_ksettings(&np->mii, cmd); spin_unlock_irq(&np->lock); return rc; @@ -1460,14 +1462,14 @@ static void ethtool_complete(struct net_device *dev) static const struct ethtool_ops netdev_ethtool_ops = { .get_drvinfo = netdev_get_drvinfo, - .get_settings = netdev_get_settings, - .set_settings = netdev_set_settings, .nway_reset = netdev_nway_reset, .get_link = netdev_get_link, .get_msglevel = netdev_get_msglevel, .set_msglevel = netdev_set_msglevel, .begin = ethtool_begin, - .complete = ethtool_complete + .complete = ethtool_complete, + .get_link_ksettings = netdev_get_link_ksettings, + .set_link_ksettings = netdev_set_link_ksettings, }; static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -- cgit From 88da73a3a5da989cc986a4b1f6a40e3292fc93d1 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Fri, 3 Mar 2017 23:39:35 +0100 Subject: net: smsc: smc91c92_cs: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/smsc/smc91c92_cs.c | 98 +++++++++++++++++---------------- 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c index 97280daba27f..976aa876789a 100644 --- a/drivers/net/ethernet/smsc/smc91c92_cs.c +++ b/drivers/net/ethernet/smsc/smc91c92_cs.c @@ -1843,56 +1843,60 @@ static int smc_link_ok(struct net_device *dev) } } -static int smc_netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd) +static int smc_netdev_get_ecmd(struct net_device *dev, + struct ethtool_link_ksettings *ecmd) { - u16 tmp; - unsigned int ioaddr = dev->base_addr; + u16 tmp; + unsigned int ioaddr = dev->base_addr; + u32 supported; - ecmd->supported = (SUPPORTED_TP | SUPPORTED_AUI | - SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full); - - SMC_SELECT_BANK(1); - tmp = inw(ioaddr + CONFIG); - ecmd->port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP; - ecmd->transceiver = XCVR_INTERNAL; - ethtool_cmd_speed_set(ecmd, SPEED_10); - ecmd->phy_address = ioaddr + MGMT; + supported = (SUPPORTED_TP | SUPPORTED_AUI | + SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full); - SMC_SELECT_BANK(0); - tmp = inw(ioaddr + TCR); - ecmd->duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF; + SMC_SELECT_BANK(1); + tmp = inw(ioaddr + CONFIG); + ecmd->base.port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP; + ecmd->base.speed = SPEED_10; + ecmd->base.phy_address = ioaddr + MGMT; - return 0; + SMC_SELECT_BANK(0); + tmp = inw(ioaddr + TCR); + ecmd->base.duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF; + + ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.supported, + supported); + + return 0; } -static int smc_netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd) +static int smc_netdev_set_ecmd(struct net_device *dev, + const struct ethtool_link_ksettings *ecmd) { - u16 tmp; - unsigned int ioaddr = dev->base_addr; + u16 tmp; + unsigned int ioaddr = dev->base_addr; - if (ethtool_cmd_speed(ecmd) != SPEED_10) - return -EINVAL; - if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) - return -EINVAL; - if (ecmd->port != PORT_TP && ecmd->port != PORT_AUI) - return -EINVAL; - if (ecmd->transceiver != XCVR_INTERNAL) - return -EINVAL; + if (ecmd->base.speed != SPEED_10) + return -EINVAL; + if (ecmd->base.duplex != DUPLEX_HALF && + ecmd->base.duplex != DUPLEX_FULL) + return -EINVAL; + if (ecmd->base.port != PORT_TP && ecmd->base.port != PORT_AUI) + return -EINVAL; - if (ecmd->port == PORT_AUI) - smc_set_xcvr(dev, 1); - else - smc_set_xcvr(dev, 0); + if (ecmd->base.port == PORT_AUI) + smc_set_xcvr(dev, 1); + else + smc_set_xcvr(dev, 0); - SMC_SELECT_BANK(0); - tmp = inw(ioaddr + TCR); - if (ecmd->duplex == DUPLEX_FULL) - tmp |= TCR_FDUPLX; - else - tmp &= ~TCR_FDUPLX; - outw(tmp, ioaddr + TCR); - - return 0; + SMC_SELECT_BANK(0); + tmp = inw(ioaddr + TCR); + if (ecmd->base.duplex == DUPLEX_FULL) + tmp |= TCR_FDUPLX; + else + tmp &= ~TCR_FDUPLX; + outw(tmp, ioaddr + TCR); + + return 0; } static int check_if_running(struct net_device *dev) @@ -1908,7 +1912,8 @@ static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info strlcpy(info->version, DRV_VERSION, sizeof(info->version)); } -static int smc_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +static int smc_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *ecmd) { struct smc_private *smc = netdev_priv(dev); unsigned int ioaddr = dev->base_addr; @@ -1919,7 +1924,7 @@ static int smc_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) spin_lock_irqsave(&smc->lock, flags); SMC_SELECT_BANK(3); if (smc->cfg & CFG_MII_SELECT) - ret = mii_ethtool_gset(&smc->mii_if, ecmd); + ret = mii_ethtool_get_link_ksettings(&smc->mii_if, ecmd); else ret = smc_netdev_get_ecmd(dev, ecmd); SMC_SELECT_BANK(saved_bank); @@ -1927,7 +1932,8 @@ static int smc_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) return ret; } -static int smc_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +static int smc_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *ecmd) { struct smc_private *smc = netdev_priv(dev); unsigned int ioaddr = dev->base_addr; @@ -1938,7 +1944,7 @@ static int smc_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) spin_lock_irqsave(&smc->lock, flags); SMC_SELECT_BANK(3); if (smc->cfg & CFG_MII_SELECT) - ret = mii_ethtool_sset(&smc->mii_if, ecmd); + ret = mii_ethtool_set_link_ksettings(&smc->mii_if, ecmd); else ret = smc_netdev_set_ecmd(dev, ecmd); SMC_SELECT_BANK(saved_bank); @@ -1982,10 +1988,10 @@ static int smc_nway_reset(struct net_device *dev) static const struct ethtool_ops ethtool_ops = { .begin = check_if_running, .get_drvinfo = smc_get_drvinfo, - .get_settings = smc_get_settings, - .set_settings = smc_set_settings, .get_link = smc_get_link, .nway_reset = smc_nway_reset, + .get_link_ksettings = smc_get_link_ksettings, + .set_link_ksettings = smc_set_link_ksettings, }; static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) -- cgit From df789fe752065f2ce761ba434125e335b514899f Mon Sep 17 00:00:00 2001 From: David Forster Date: Thu, 23 Feb 2017 16:27:18 +0000 Subject: ipv6: Provide ipv6 version of "disable_policy" sysctl This provides equivalent functionality to the existing ipv4 "disable_policy" systcl. ie. Allows IPsec processing to be skipped on terminating packets on a per-interface basis. Signed-off-by: David Forster Signed-off-by: David S. Miller --- include/linux/ipv6.h | 1 + include/uapi/linux/ipv6.h | 1 + net/ipv6/addrconf.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+) diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 71be5b330d21..f0d79bd054ca 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -70,6 +70,7 @@ struct ipv6_devconf { #endif __u32 enhanced_dad; __u32 addr_gen_mode; + __s32 disable_policy; struct ctl_table_header *sysctl_header; }; diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 8ef9e75e004e..d8f6a1ac9af4 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -183,6 +183,7 @@ enum { DEVCONF_SEG6_REQUIRE_HMAC, DEVCONF_ENHANCED_DAD, DEVCONF_ADDR_GEN_MODE, + DEVCONF_DISABLE_POLICY, DEVCONF_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 363172527e43..8c69768a5c46 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -245,6 +245,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { #endif .enhanced_dad = 1, .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, + .disable_policy = 0, }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -297,6 +298,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { #endif .enhanced_dad = 1, .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, + .disable_policy = 0, }; /* Check if a valid qdisc is available */ @@ -944,6 +946,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, const struct in6_addr *peer_addr, int pfxlen, int scope, u32 flags, u32 valid_lft, u32 prefered_lft) { + struct net *net = dev_net(idev->dev); struct inet6_ifaddr *ifa = NULL; struct rt6_info *rt; unsigned int hash; @@ -990,6 +993,10 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, goto out; } + if (net->ipv6.devconf_all->disable_policy || + idev->cnf.disable_policy) + rt->dst.flags |= DST_NOPOLICY; + neigh_parms_data_state_setall(idev->nd_parms); ifa->addr = *addr; @@ -5003,6 +5010,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, #endif array[DEVCONF_ENHANCED_DAD] = cnf->enhanced_dad; array[DEVCONF_ADDR_GEN_MODE] = cnf->addr_gen_mode; + array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy; } static inline size_t inet6_ifla6_size(void) @@ -5827,6 +5835,105 @@ int addrconf_sysctl_ignore_routes_with_linkdown(struct ctl_table *ctl, return ret; } +static +void addrconf_set_nopolicy(struct rt6_info *rt, int action) +{ + if (rt) { + if (action) + rt->dst.flags |= DST_NOPOLICY; + else + rt->dst.flags &= ~DST_NOPOLICY; + } +} + +static +void addrconf_disable_policy_idev(struct inet6_dev *idev, int val) +{ + struct inet6_ifaddr *ifa; + + read_lock_bh(&idev->lock); + list_for_each_entry(ifa, &idev->addr_list, if_list) { + spin_lock(&ifa->lock); + if (ifa->rt) { + struct rt6_info *rt = ifa->rt; + struct fib6_table *table = rt->rt6i_table; + int cpu; + + read_lock(&table->tb6_lock); + addrconf_set_nopolicy(ifa->rt, val); + if (rt->rt6i_pcpu) { + for_each_possible_cpu(cpu) { + struct rt6_info **rtp; + + rtp = per_cpu_ptr(rt->rt6i_pcpu, cpu); + addrconf_set_nopolicy(*rtp, val); + } + } + read_unlock(&table->tb6_lock); + } + spin_unlock(&ifa->lock); + } + read_unlock_bh(&idev->lock); +} + +static +int addrconf_disable_policy(struct ctl_table *ctl, int *valp, int val) +{ + struct inet6_dev *idev; + struct net *net; + + if (!rtnl_trylock()) + return restart_syscall(); + + *valp = val; + + net = (struct net *)ctl->extra2; + if (valp == &net->ipv6.devconf_dflt->disable_policy) { + rtnl_unlock(); + return 0; + } + + if (valp == &net->ipv6.devconf_all->disable_policy) { + struct net_device *dev; + + for_each_netdev(net, dev) { + idev = __in6_dev_get(dev); + if (idev) + addrconf_disable_policy_idev(idev, val); + } + } else { + idev = (struct inet6_dev *)ctl->extra1; + addrconf_disable_policy_idev(idev, val); + } + + rtnl_unlock(); + return 0; +} + +static +int addrconf_sysctl_disable_policy(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int *valp = ctl->data; + int val = *valp; + loff_t pos = *ppos; + struct ctl_table lctl; + int ret; + + lctl = *ctl; + lctl.data = &val; + ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); + + if (write && (*valp != val)) + ret = addrconf_disable_policy(ctl, valp, val); + + if (ret) + *ppos = pos; + + return ret; +} + static int minus_one = -1; static const int one = 1; static const int two_five_five = 255; @@ -6184,6 +6291,13 @@ static const struct ctl_table addrconf_sysctl[] = { .mode = 0644, .proc_handler = addrconf_sysctl_addr_gen_mode, }, + { + .procname = "disable_policy", + .data = &ipv6_devconf.disable_policy, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = addrconf_sysctl_disable_policy, + }, { /* sentinel */ } -- cgit From 1a4691b204e75a853ec74947bed0dd6966558d29 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Fri, 24 Feb 2017 17:30:32 +0000 Subject: tools: hv: Add clean up function for Ubuntu config This patch adds a function to clean up duplicate config info on Ubuntu. Signed-off-by: Haiyang Zhang Signed-off-by: David S. Miller --- tools/hv/bondvf.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/hv/bondvf.sh b/tools/hv/bondvf.sh index 4aa5369ffa4e..d85968cb1bf2 100755 --- a/tools/hv/bondvf.sh +++ b/tools/hv/bondvf.sh @@ -101,9 +101,25 @@ function create_bond_cfg_redhat { echo BONDING_OPTS=\"mode=active-backup miimon=100 primary=$2\" >>$fn } +function del_eth_cfg_ubuntu { + local fn=$cfgdir/interfaces + local tmpfl=$(mktemp) + + local nic_start='^[ \t]*(auto|iface|mapping|allow-.*)[ \t]+'$1 + local nic_end='^[ \t]*(auto|iface|mapping|allow-.*|source)' + + awk "/$nic_end/{x=0} x{next} /$nic_start/{x=1;next} 1" $fn >$tmpfl + + cp $tmpfl $fn + + rm $tmpfl +} + function create_eth_cfg_ubuntu { local fn=$cfgdir/interfaces + del_eth_cfg_ubuntu $1 + echo $'\n'auto $1 >>$fn echo iface $1 inet manual >>$fn echo bond-master $2 >>$fn @@ -119,6 +135,8 @@ function create_eth_cfg_pri_ubuntu { function create_bond_cfg_ubuntu { local fn=$cfgdir/interfaces + del_eth_cfg_ubuntu $1 + echo $'\n'auto $1 >>$fn echo iface $1 inet dhcp >>$fn echo bond-mode active-backup >>$fn -- cgit From 50698d80f8bb1db989b7b9fa433f588fade5e382 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 27 Feb 2017 10:26:47 -0800 Subject: netvsc: don't overload variable in same function There are two variables named packet in the same function. One is the metadata descriptor from host (vmpacket_descriptor) and the other is the control block in the skb used to hold metadata from send. Change name to avoid possible confusion and bugs. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index d35ebd993b38..5dedbc36c326 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -600,9 +600,9 @@ static inline void netvsc_free_send_slot(struct netvsc_device *net_device, static void netvsc_send_tx_complete(struct netvsc_device *net_device, struct vmbus_channel *incoming_channel, struct hv_device *device, - struct vmpacket_descriptor *packet) + const struct vmpacket_descriptor *desc) { - struct sk_buff *skb = (struct sk_buff *)(unsigned long)packet->trans_id; + struct sk_buff *skb = (struct sk_buff *)(unsigned long)desc->trans_id; struct net_device *ndev = hv_get_drvdata(device); struct net_device_context *net_device_ctx = netdev_priv(ndev); struct vmbus_channel *channel = device->channel; -- cgit From f3dd3f4797652c311df9c074436d420f1ad3566e Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 27 Feb 2017 10:26:48 -0800 Subject: vmbus: introduce in-place packet iterator This is mostly just a refactoring of previous functions (get_pkt_next_raw, put_pkt_raw and commit_rd_index) to make it easier to use for other drivers and NAPI. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/hv/ring_buffer.c | 94 +++++++++++++++++++++++++++++++++++++++++++- drivers/net/hyperv/netvsc.c | 34 +++++----------- include/linux/hyperv.h | 96 ++++++++++++++------------------------------- 3 files changed, 133 insertions(+), 91 deletions(-) diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 87799e81af97..c3f1a9e33cef 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -32,6 +32,8 @@ #include "hyperv_vmbus.h" +#define VMBUS_PKT_TRAILER 8 + /* * When we write to the ring buffer, check if the host needs to * be signaled. Here is the details of this protocol: @@ -336,6 +338,12 @@ int hv_ringbuffer_write(struct vmbus_channel *channel, return 0; } +static inline void +init_cached_read_index(struct hv_ring_buffer_info *rbi) +{ + rbi->cached_read_index = rbi->ring_buffer->read_index; +} + int hv_ringbuffer_read(struct vmbus_channel *channel, void *buffer, u32 buflen, u32 *buffer_actual_len, u64 *requestid, bool raw) @@ -366,7 +374,8 @@ int hv_ringbuffer_read(struct vmbus_channel *channel, return ret; } - init_cached_read_index(channel); + init_cached_read_index(inring_info); + next_read_location = hv_get_next_read_location(inring_info); next_read_location = hv_copyfrom_ringbuffer(inring_info, &desc, sizeof(desc), @@ -410,3 +419,86 @@ int hv_ringbuffer_read(struct vmbus_channel *channel, return ret; } + +/* + * Determine number of bytes available in ring buffer after + * the current iterator (priv_read_index) location. + * + * This is similar to hv_get_bytes_to_read but with private + * read index instead. + */ +static u32 hv_pkt_iter_avail(const struct hv_ring_buffer_info *rbi) +{ + u32 priv_read_loc = rbi->priv_read_index; + u32 write_loc = READ_ONCE(rbi->ring_buffer->write_index); + + if (write_loc >= priv_read_loc) + return write_loc - priv_read_loc; + else + return (rbi->ring_datasize - priv_read_loc) + write_loc; +} + +/* + * Get first vmbus packet from ring buffer after read_index + * + * If ring buffer is empty, returns NULL and no other action needed. + */ +struct vmpacket_descriptor *hv_pkt_iter_first(struct vmbus_channel *channel) +{ + struct hv_ring_buffer_info *rbi = &channel->inbound; + + /* set state for later hv_signal_on_read() */ + init_cached_read_index(rbi); + + if (hv_pkt_iter_avail(rbi) < sizeof(struct vmpacket_descriptor)) + return NULL; + + return hv_get_ring_buffer(rbi) + rbi->priv_read_index; +} +EXPORT_SYMBOL_GPL(hv_pkt_iter_first); + +/* + * Get next vmbus packet from ring buffer. + * + * Advances the current location (priv_read_index) and checks for more + * data. If the end of the ring buffer is reached, then return NULL. + */ +struct vmpacket_descriptor * +__hv_pkt_iter_next(struct vmbus_channel *channel, + const struct vmpacket_descriptor *desc) +{ + struct hv_ring_buffer_info *rbi = &channel->inbound; + u32 packetlen = desc->len8 << 3; + u32 dsize = rbi->ring_datasize; + + /* bump offset to next potential packet */ + rbi->priv_read_index += packetlen + VMBUS_PKT_TRAILER; + if (rbi->priv_read_index >= dsize) + rbi->priv_read_index -= dsize; + + /* more data? */ + if (hv_pkt_iter_avail(rbi) < sizeof(struct vmpacket_descriptor)) + return NULL; + else + return hv_get_ring_buffer(rbi) + rbi->priv_read_index; +} +EXPORT_SYMBOL_GPL(__hv_pkt_iter_next); + +/* + * Update host ring buffer after iterating over packets. + */ +void hv_pkt_iter_close(struct vmbus_channel *channel) +{ + struct hv_ring_buffer_info *rbi = &channel->inbound; + + /* + * Make sure all reads are done before we update the read index since + * the writer may start writing to the read area once the read index + * is updated. + */ + virt_rmb(); + rbi->ring_buffer->read_index = rbi->priv_read_index; + + hv_signal_on_read(channel); +} +EXPORT_SYMBOL_GPL(hv_pkt_iter_close); diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 5dedbc36c326..3681fb59bdbe 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -647,14 +647,11 @@ static void netvsc_send_tx_complete(struct netvsc_device *net_device, static void netvsc_send_completion(struct netvsc_device *net_device, struct vmbus_channel *incoming_channel, struct hv_device *device, - struct vmpacket_descriptor *packet) + const struct vmpacket_descriptor *desc) { - struct nvsp_message *nvsp_packet; + struct nvsp_message *nvsp_packet = hv_pkt_data(desc); struct net_device *ndev = hv_get_drvdata(device); - nvsp_packet = (struct nvsp_message *)((unsigned long)packet + - (packet->offset8 << 3)); - switch (nvsp_packet->hdr.msg_type) { case NVSP_MSG_TYPE_INIT_COMPLETE: case NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE: @@ -668,7 +665,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device, case NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE: netvsc_send_tx_complete(net_device, incoming_channel, - device, packet); + device, desc); break; default: @@ -1071,9 +1068,11 @@ static void netvsc_receive(struct net_device *ndev, struct net_device_context *net_device_ctx, struct hv_device *device, struct vmbus_channel *channel, - struct vmtransfer_page_packet_header *vmxferpage_packet, + const struct vmpacket_descriptor *desc, struct nvsp_message *nvsp) { + const struct vmtransfer_page_packet_header *vmxferpage_packet + = container_of(desc, const struct vmtransfer_page_packet_header, d); char *recv_buf = net_device->recv_buf; u32 status = NVSP_STAT_SUCCESS; int i; @@ -1185,12 +1184,10 @@ static void netvsc_process_raw_pkt(struct hv_device *device, struct netvsc_device *net_device, struct net_device *ndev, u64 request_id, - struct vmpacket_descriptor *desc) + const struct vmpacket_descriptor *desc) { struct net_device_context *net_device_ctx = netdev_priv(ndev); - struct nvsp_message *nvmsg - = (struct nvsp_message *)((unsigned long)desc - + (desc->offset8 << 3)); + struct nvsp_message *nvmsg = hv_pkt_data(desc); switch (desc->type) { case VM_PKT_COMP: @@ -1199,9 +1196,7 @@ static void netvsc_process_raw_pkt(struct hv_device *device, case VM_PKT_DATA_USING_XFER_PAGES: netvsc_receive(ndev, net_device, net_device_ctx, - device, channel, - (struct vmtransfer_page_packet_header *)desc, - nvmsg); + device, channel, desc, nvmsg); break; case VM_PKT_DATA_INBAND: @@ -1223,7 +1218,6 @@ void netvsc_channel_cb(void *context) struct netvsc_device *net_device; struct vmpacket_descriptor *desc; struct net_device *ndev; - bool need_to_commit = false; if (channel->primary_channel != NULL) device = channel->primary_channel->device_obj; @@ -1239,20 +1233,12 @@ void netvsc_channel_cb(void *context) netvsc_channel_idle(net_device, q_idx)) return; - /* commit_rd_index() -> hv_signal_on_read() needs this. */ - init_cached_read_index(channel); - - while ((desc = get_next_pkt_raw(channel)) != NULL) { + foreach_vmbus_pkt(desc, channel) { netvsc_process_raw_pkt(device, channel, net_device, ndev, desc->trans_id, desc); - put_pkt_raw(channel, desc); - need_to_commit = true; } - if (need_to_commit) - commit_rd_index(channel); - netvsc_chk_recv_comp(net_device, channel, q_idx); } diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 62bbf3c1aa4a..36162485d663 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1504,14 +1504,6 @@ static inline void hv_signal_on_read(struct vmbus_channel *channel) return; } -static inline void -init_cached_read_index(struct vmbus_channel *channel) -{ - struct hv_ring_buffer_info *rbi = &channel->inbound; - - rbi->cached_read_index = rbi->ring_buffer->read_index; -} - /* * Mask off host interrupt callback notifications */ @@ -1545,76 +1537,48 @@ static inline u32 hv_end_read(struct hv_ring_buffer_info *rbi) /* * An API to support in-place processing of incoming VMBUS packets. */ -#define VMBUS_PKT_TRAILER 8 -static inline struct vmpacket_descriptor * -get_next_pkt_raw(struct vmbus_channel *channel) +/* Get data payload associated with descriptor */ +static inline void *hv_pkt_data(const struct vmpacket_descriptor *desc) { - struct hv_ring_buffer_info *ring_info = &channel->inbound; - u32 priv_read_loc = ring_info->priv_read_index; - void *ring_buffer = hv_get_ring_buffer(ring_info); - u32 dsize = ring_info->ring_datasize; - /* - * delta is the difference between what is available to read and - * what was already consumed in place. We commit read index after - * the whole batch is processed. - */ - u32 delta = priv_read_loc >= ring_info->ring_buffer->read_index ? - priv_read_loc - ring_info->ring_buffer->read_index : - (dsize - ring_info->ring_buffer->read_index) + priv_read_loc; - u32 bytes_avail_toread = (hv_get_bytes_to_read(ring_info) - delta); - - if (bytes_avail_toread < sizeof(struct vmpacket_descriptor)) - return NULL; - - return ring_buffer + priv_read_loc; + return (void *)((unsigned long)desc + (desc->offset8 << 3)); } -/* - * A helper function to step through packets "in-place" - * This API is to be called after each successful call - * get_next_pkt_raw(). - */ -static inline void put_pkt_raw(struct vmbus_channel *channel, - struct vmpacket_descriptor *desc) +/* Get data size associated with descriptor */ +static inline u32 hv_pkt_datalen(const struct vmpacket_descriptor *desc) { - struct hv_ring_buffer_info *ring_info = &channel->inbound; - u32 packetlen = desc->len8 << 3; - u32 dsize = ring_info->ring_datasize; - - /* - * Include the packet trailer. - */ - ring_info->priv_read_index += packetlen + VMBUS_PKT_TRAILER; - ring_info->priv_read_index %= dsize; + return (desc->len8 << 3) - (desc->offset8 << 3); } + +struct vmpacket_descriptor * +hv_pkt_iter_first(struct vmbus_channel *channel); + +struct vmpacket_descriptor * +__hv_pkt_iter_next(struct vmbus_channel *channel, + const struct vmpacket_descriptor *pkt); + +void hv_pkt_iter_close(struct vmbus_channel *channel); + /* - * This call commits the read index and potentially signals the host. - * Here is the pattern for using the "in-place" consumption APIs: - * - * init_cached_read_index(); - * - * while (get_next_pkt_raw() { - * process the packet "in-place"; - * put_pkt_raw(); - * } - * if (packets processed in place) - * commit_rd_index(); + * Get next packet descriptor from iterator + * If at end of list, return NULL and update host. */ -static inline void commit_rd_index(struct vmbus_channel *channel) +static inline struct vmpacket_descriptor * +hv_pkt_iter_next(struct vmbus_channel *channel, + const struct vmpacket_descriptor *pkt) { - struct hv_ring_buffer_info *ring_info = &channel->inbound; - /* - * Make sure all reads are done before we update the read index since - * the writer may start writing to the read area once the read index - * is updated. - */ - virt_rmb(); - ring_info->ring_buffer->read_index = ring_info->priv_read_index; + struct vmpacket_descriptor *nxt; + + nxt = __hv_pkt_iter_next(channel, pkt); + if (!nxt) + hv_pkt_iter_close(channel); - hv_signal_on_read(channel); + return nxt; } +#define foreach_vmbus_pkt(pkt, channel) \ + for (pkt = hv_pkt_iter_first(channel); pkt; \ + pkt = hv_pkt_iter_next(channel, pkt)) #endif /* _HYPERV_H */ -- cgit From 15a863bf7436124e799ba175a801e25f7b57191e Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 27 Feb 2017 10:26:49 -0800 Subject: netvsc: implement NAPI Use NAPI (softirq), to handle receive packets and send completions. Previously this was handled by tasklet. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 2 + drivers/net/hyperv/netvsc.c | 140 ++++++++++++++++++++++++++------------ drivers/net/hyperv/netvsc_drv.c | 5 -- drivers/net/hyperv/rndis_filter.c | 2 + 4 files changed, 102 insertions(+), 47 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index d3e73ac158ae..7433b164e513 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -196,6 +196,7 @@ int netvsc_recv_callback(struct net_device *net, const struct ndis_tcp_ip_checksum_info *csum_info, const struct ndis_pkt_8021q_info *vlan); void netvsc_channel_cb(void *context); +int netvsc_poll(struct napi_struct *napi, int budget); int rndis_filter_open(struct netvsc_device *nvdev); int rndis_filter_close(struct netvsc_device *nvdev); int rndis_filter_device_add(struct hv_device *dev, @@ -720,6 +721,7 @@ struct net_device_context { /* Per channel data */ struct netvsc_channel { struct vmbus_channel *channel; + struct napi_struct napi; struct multi_send_data msd; struct multi_recv_comp mrc; atomic_t queue_sends; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 3681fb59bdbe..b1328cef9d5a 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -556,6 +556,7 @@ void netvsc_device_remove(struct hv_device *device) struct net_device *ndev = hv_get_drvdata(device); struct net_device_context *net_device_ctx = netdev_priv(ndev); struct netvsc_device *net_device = net_device_ctx->nvdev; + int i; netvsc_disconnect_vsp(device); @@ -570,6 +571,9 @@ void netvsc_device_remove(struct hv_device *device) /* Now, we can close the channel safely */ vmbus_close(device->channel); + for (i = 0; i < VRSS_CHANNEL_MAX; i++) + napi_disable(&net_device->chan_table[0].napi); + /* Release all resources */ free_netvsc_device(net_device); } @@ -1063,7 +1067,7 @@ static inline struct recv_comp_data *get_recv_comp_slot( return rcd; } -static void netvsc_receive(struct net_device *ndev, +static int netvsc_receive(struct net_device *ndev, struct netvsc_device *net_device, struct net_device_context *net_device_ctx, struct hv_device *device, @@ -1073,20 +1077,19 @@ static void netvsc_receive(struct net_device *ndev, { const struct vmtransfer_page_packet_header *vmxferpage_packet = container_of(desc, const struct vmtransfer_page_packet_header, d); + u16 q_idx = channel->offermsg.offer.sub_channel_index; char *recv_buf = net_device->recv_buf; u32 status = NVSP_STAT_SUCCESS; int i; int count = 0; int ret; - struct recv_comp_data *rcd; - u16 q_idx = channel->offermsg.offer.sub_channel_index; /* Make sure this is a valid nvsp packet */ if (unlikely(nvsp->hdr.msg_type != NVSP_MSG1_TYPE_SEND_RNDIS_PKT)) { netif_err(net_device_ctx, rx_err, ndev, "Unknown nvsp packet type received %u\n", nvsp->hdr.msg_type); - return; + return 0; } if (unlikely(vmxferpage_packet->xfer_pageset_id != NETVSC_RECEIVE_BUFFER_ID)) { @@ -1094,7 +1097,7 @@ static void netvsc_receive(struct net_device *ndev, "Invalid xfer page set id - expecting %x got %x\n", NETVSC_RECEIVE_BUFFER_ID, vmxferpage_packet->xfer_pageset_id); - return; + return 0; } count = vmxferpage_packet->range_cnt; @@ -1110,26 +1113,26 @@ static void netvsc_receive(struct net_device *ndev, channel, data, buflen); } - if (!net_device->chan_table[q_idx].mrc.buf) { + if (net_device->chan_table[q_idx].mrc.buf) { + struct recv_comp_data *rcd; + + rcd = get_recv_comp_slot(net_device, channel, q_idx); + if (rcd) { + rcd->tid = vmxferpage_packet->d.trans_id; + rcd->status = status; + } else { + netdev_err(ndev, "Recv_comp full buf q:%hd, tid:%llx\n", + q_idx, vmxferpage_packet->d.trans_id); + } + } else { ret = netvsc_send_recv_completion(channel, vmxferpage_packet->d.trans_id, status); if (ret) netdev_err(ndev, "Recv_comp q:%hd, tid:%llx, err:%d\n", q_idx, vmxferpage_packet->d.trans_id, ret); - return; } - - rcd = get_recv_comp_slot(net_device, channel, q_idx); - - if (!rcd) { - netdev_err(ndev, "Recv_comp full buf q:%hd, tid:%llx\n", - q_idx, vmxferpage_packet->d.trans_id); - return; - } - - rcd->tid = vmxferpage_packet->d.trans_id; - rcd->status = status; + return count; } static void netvsc_send_table(struct hv_device *hdev, @@ -1179,12 +1182,12 @@ static inline void netvsc_receive_inband(struct hv_device *hdev, } } -static void netvsc_process_raw_pkt(struct hv_device *device, - struct vmbus_channel *channel, - struct netvsc_device *net_device, - struct net_device *ndev, - u64 request_id, - const struct vmpacket_descriptor *desc) +static int netvsc_process_raw_pkt(struct hv_device *device, + struct vmbus_channel *channel, + struct netvsc_device *net_device, + struct net_device *ndev, + u64 request_id, + const struct vmpacket_descriptor *desc) { struct net_device_context *net_device_ctx = netdev_priv(ndev); struct nvsp_message *nvmsg = hv_pkt_data(desc); @@ -1195,8 +1198,8 @@ static void netvsc_process_raw_pkt(struct hv_device *device, break; case VM_PKT_DATA_USING_XFER_PAGES: - netvsc_receive(ndev, net_device, net_device_ctx, - device, channel, desc, nvmsg); + return netvsc_receive(ndev, net_device, net_device_ctx, + device, channel, desc, nvmsg); break; case VM_PKT_DATA_INBAND: @@ -1208,22 +1211,64 @@ static void netvsc_process_raw_pkt(struct hv_device *device, desc->type, request_id); break; } + + return 0; +} + +static struct hv_device *netvsc_channel_to_device(struct vmbus_channel *channel) +{ + struct vmbus_channel *primary = channel->primary_channel; + + return primary ? primary->device_obj : channel->device_obj; +} + +int netvsc_poll(struct napi_struct *napi, int budget) +{ + struct netvsc_channel *nvchan + = container_of(napi, struct netvsc_channel, napi); + struct vmbus_channel *channel = nvchan->channel; + struct hv_device *device = netvsc_channel_to_device(channel); + u16 q_idx = channel->offermsg.offer.sub_channel_index; + struct net_device *ndev = hv_get_drvdata(device); + struct netvsc_device *net_device = net_device_to_netvsc_device(ndev); + const struct vmpacket_descriptor *desc; + int work_done = 0; + + desc = hv_pkt_iter_first(channel); + while (desc) { + int count; + + count = netvsc_process_raw_pkt(device, channel, net_device, + ndev, desc->trans_id, desc); + work_done += count; + desc = __hv_pkt_iter_next(channel, desc); + + /* If receive packet budget is exhausted, reschedule */ + if (work_done >= budget) { + work_done = budget; + break; + } + } + hv_pkt_iter_close(channel); + + /* If ring is empty and NAPI is not doing polling */ + if (work_done < budget && + napi_complete_done(napi, work_done) && + hv_end_read(&channel->inbound) != 0) + napi_reschedule(napi); + + netvsc_chk_recv_comp(net_device, channel, q_idx); + return work_done; } void netvsc_channel_cb(void *context) { struct vmbus_channel *channel = context; + struct hv_device *device = netvsc_channel_to_device(channel); u16 q_idx = channel->offermsg.offer.sub_channel_index; - struct hv_device *device; struct netvsc_device *net_device; - struct vmpacket_descriptor *desc; struct net_device *ndev; - if (channel->primary_channel != NULL) - device = channel->primary_channel->device_obj; - else - device = channel->device_obj; - ndev = hv_get_drvdata(device); if (unlikely(!ndev)) return; @@ -1233,13 +1278,9 @@ void netvsc_channel_cb(void *context) netvsc_channel_idle(net_device, q_idx)) return; - foreach_vmbus_pkt(desc, channel) { - netvsc_process_raw_pkt(device, channel, net_device, - ndev, desc->trans_id, desc); - - } - - netvsc_chk_recv_comp(net_device, channel, q_idx); + /* disable interupts from host */ + hv_begin_read(&channel->inbound); + napi_schedule(&net_device->chan_table[q_idx].napi); } /* @@ -1261,6 +1302,11 @@ int netvsc_device_add(struct hv_device *device, net_device->ring_size = ring_size; + /* Because the device uses NAPI, all the interrupt batching and + * control is done via Net softirq, not the channel handling + */ + set_channel_read_mode(device->channel, HV_CALL_ISR); + /* Open the channel */ ret = vmbus_open(device->channel, ring_size * PAGE_SIZE, ring_size * PAGE_SIZE, NULL, 0, @@ -1278,8 +1324,16 @@ int netvsc_device_add(struct hv_device *device, * chn_table with the default channel to use it before subchannels are * opened. */ - for (i = 0; i < VRSS_CHANNEL_MAX; i++) - net_device->chan_table[i].channel = device->channel; + for (i = 0; i < VRSS_CHANNEL_MAX; i++) { + struct netvsc_channel *nvchan = &net_device->chan_table[i]; + + nvchan->channel = device->channel; + netif_napi_add(ndev, &nvchan->napi, + netvsc_poll, NAPI_POLL_WEIGHT); + } + + /* Enable NAPI handler for init callbacks */ + napi_enable(&net_device->chan_table[0].napi); /* Writing nvdev pointer unlocks netvsc_send(), make sure chn_table is * populated. @@ -1299,6 +1353,8 @@ int netvsc_device_add(struct hv_device *device, return ret; close: + napi_disable(&net_device->chan_table[0].napi); + /* Now, we can close the channel safely */ vmbus_close(device->channel); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index bc05c895d958..65d738b783cb 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -690,11 +690,6 @@ int netvsc_recv_callback(struct net_device *net, ++rx_stats->multicast; u64_stats_update_end(&rx_stats->syncp); - /* - * Pass the skb back up. Network stack will deallocate the skb when it - * is done. - * TODO - use NAPI? - */ netif_receive_skb(skb); rcu_read_unlock(); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 19356f56b7b1..d7b6311e6c19 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1012,6 +1012,8 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) if (ret == 0) nvscdev->chan_table[chn_index].channel = new_sc; + napi_enable(&nvscdev->chan_table[chn_index].napi); + spin_lock_irqsave(&nvscdev->sc_lock, flags); nvscdev->num_sc_offered--; spin_unlock_irqrestore(&nvscdev->sc_lock, flags); -- cgit From 742fe54c7b03c83ce8067822a8739a4091c319ed Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 27 Feb 2017 10:26:50 -0800 Subject: netvsc: enable GRO Use GRO when receiving packets. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 65d738b783cb..1345f34b6baf 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -642,11 +642,11 @@ int netvsc_recv_callback(struct net_device *net, { struct net_device_context *net_device_ctx = netdev_priv(net); struct netvsc_device *net_device = net_device_ctx->nvdev; + u16 q_idx = channel->offermsg.offer.sub_channel_index; + struct netvsc_channel *nvchan = &net_device->chan_table[q_idx]; struct net_device *vf_netdev; struct sk_buff *skb; struct netvsc_stats *rx_stats; - u16 q_idx = channel->offermsg.offer.sub_channel_index; - if (net->reg_state != NETREG_REGISTERED) return NVSP_STAT_FAIL; @@ -679,7 +679,7 @@ int netvsc_recv_callback(struct net_device *net, * on the synthetic device because modifying the VF device * statistics will not work correctly. */ - rx_stats = &net_device->chan_table[q_idx].rx_stats; + rx_stats = &nvchan->rx_stats; u64_stats_update_begin(&rx_stats->syncp); rx_stats->packets++; rx_stats->bytes += len; @@ -690,7 +690,7 @@ int netvsc_recv_callback(struct net_device *net, ++rx_stats->multicast; u64_stats_update_end(&rx_stats->syncp); - netif_receive_skb(skb); + napi_gro_receive(&nvchan->napi, skb); rcu_read_unlock(); return 0; -- cgit From e91e7dd71dedbf4508513c8a66212248a853ecbe Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 27 Feb 2017 10:26:51 -0800 Subject: netvsc: replace netdev_alloc_skb_ip_align with napi_alloc_skb Gives potential performance gain. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 1345f34b6baf..617dd90803c9 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -589,13 +589,14 @@ void netvsc_linkstatus_callback(struct hv_device *device_obj, } static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net, + struct napi_struct *napi, const struct ndis_tcp_ip_checksum_info *csum_info, const struct ndis_pkt_8021q_info *vlan, void *data, u32 buflen) { struct sk_buff *skb; - skb = netdev_alloc_skb_ip_align(net, buflen); + skb = napi_alloc_skb(napi, buflen); if (!skb) return skb; @@ -664,7 +665,8 @@ int netvsc_recv_callback(struct net_device *net, net = vf_netdev; /* Allocate a skb - TODO direct I/O to pages? */ - skb = netvsc_alloc_recv_skb(net, csum_info, vlan, data, len); + skb = netvsc_alloc_recv_skb(net, &nvchan->napi, + csum_info, vlan, data, len); if (unlikely(!skb)) { ++net->stats.rx_dropped; rcu_read_unlock(); -- cgit From 452349c3235252aaccaf30cc75402827d43b72d4 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 28 Feb 2017 12:21:12 +0100 Subject: net: axienet: use eth_hw_addr_random() Use eth_hw_addr_random() to set a random MAC address in order to make sure ndev->addr_assign_type will be properly set to NET_ADDR_RANDOM. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index b96e96919e31..33c595f4691d 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -301,7 +301,7 @@ static void axienet_set_mac_address(struct net_device *ndev, if (address) memcpy(ndev->dev_addr, address, ETH_ALEN); if (!is_valid_ether_addr(ndev->dev_addr)) - eth_random_addr(ndev->dev_addr); + eth_hw_addr_random(ndev); /* Set up unicast MAC address filter set its mac address */ axienet_iow(lp, XAE_UAW0_OFFSET, -- cgit From 02083c3aeda7b5fab49920b7fd9277b2c9ec50a9 Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Tue, 28 Feb 2017 13:50:59 -0500 Subject: net: ethernet: bgmac: use #defines for MAX size The maximum frame size is really just the standard ethernet frame size and FCS. So use those existing defines to make the code a little more beautiful. Signed-off-by: Jon Mason Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bgmac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bgmac.h b/drivers/net/ethernet/broadcom/bgmac.h index 6d1c6ff1ed96..a75ed3538e5c 100644 --- a/drivers/net/ethernet/broadcom/bgmac.h +++ b/drivers/net/ethernet/broadcom/bgmac.h @@ -402,7 +402,7 @@ #define BGMAC_WEIGHT 64 -#define ETHER_MAX_LEN 1518 +#define ETHER_MAX_LEN (ETH_FRAME_LEN + ETH_FCS_LEN) /* Feature Flags */ #define BGMAC_FEAT_TX_MASK_SETUP BIT(0) -- cgit From cb1b0f90acfedd2164e524b327d9e60b097646cc Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Tue, 28 Feb 2017 13:51:00 -0500 Subject: net: ethernet: bgmac: unify code of the same family BCM471X and BCM535X are of the same family (from what I can derive from internal documents). Group them into the case statement together, which results in more code reuse. Also, use existing helper variables to make the code a little more readable too. Signed-off-by: Jon Mason Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bgmac-bcma.c | 64 +++++++++++++----------------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bgmac-bcma.c b/drivers/net/ethernet/broadcom/bgmac-bcma.c index d59cfcc4c4d5..cf15b7e2929c 100644 --- a/drivers/net/ethernet/broadcom/bgmac-bcma.c +++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c @@ -192,36 +192,50 @@ static int bgmac_probe(struct bcma_device *core) goto err1; } - bgmac->has_robosw = !!(core->bus->sprom.boardflags_lo & - BGMAC_BFL_ENETROBO); + bgmac->has_robosw = !!(sprom->boardflags_lo & BGMAC_BFL_ENETROBO); if (bgmac->has_robosw) dev_warn(bgmac->dev, "Support for Roboswitch not implemented\n"); - if (core->bus->sprom.boardflags_lo & BGMAC_BFL_ENETADM) + if (sprom->boardflags_lo & BGMAC_BFL_ENETADM) dev_warn(bgmac->dev, "Support for ADMtek ethernet switch not implemented\n"); /* Feature Flags */ - switch (core->bus->chipinfo.id) { + switch (ci->id) { + /* BCM 471X/535X family */ + case BCMA_CHIP_ID_BCM4716: + bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; + /* fallthrough */ + case BCMA_CHIP_ID_BCM47162: + bgmac->feature_flags |= BGMAC_FEAT_FLW_CTRL2; + bgmac->feature_flags |= BGMAC_FEAT_SET_RXQ_CLK; + break; case BCMA_CHIP_ID_BCM5357: + case BCMA_CHIP_ID_BCM53572: bgmac->feature_flags |= BGMAC_FEAT_SET_RXQ_CLK; bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; bgmac->feature_flags |= BGMAC_FEAT_FLW_CTRL1; bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_PHY; - if (core->bus->chipinfo.pkg == BCMA_PKG_ID_BCM47186) { - bgmac->feature_flags |= BGMAC_FEAT_IOST_ATTACHED; + if (ci->pkg == BCMA_PKG_ID_BCM47188 || + ci->pkg == BCMA_PKG_ID_BCM47186) { bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_RGMII; + bgmac->feature_flags |= BGMAC_FEAT_IOST_ATTACHED; } - if (core->bus->chipinfo.pkg == BCMA_PKG_ID_BCM5358) + if (ci->pkg == BCMA_PKG_ID_BCM5358) bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_EPHYRMII; break; - case BCMA_CHIP_ID_BCM53572: - bgmac->feature_flags |= BGMAC_FEAT_SET_RXQ_CLK; + case BCMA_CHIP_ID_BCM53573: bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; - bgmac->feature_flags |= BGMAC_FEAT_FLW_CTRL1; - bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_PHY; - if (core->bus->chipinfo.pkg == BCMA_PKG_ID_BCM47188) { - bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_RGMII; + bgmac->feature_flags |= BGMAC_FEAT_SET_RXQ_CLK; + if (ci->pkg == BCMA_PKG_ID_BCM47189) bgmac->feature_flags |= BGMAC_FEAT_IOST_ATTACHED; + if (core->core_unit == 0) { + bgmac->feature_flags |= BGMAC_FEAT_CC4_IF_SW_TYPE; + if (ci->pkg == BCMA_PKG_ID_BCM47189) + bgmac->feature_flags |= + BGMAC_FEAT_CC4_IF_SW_TYPE_RGMII; + } else if (core->core_unit == 1) { + bgmac->feature_flags |= BGMAC_FEAT_IRQ_ID_OOB_6; + bgmac->feature_flags |= BGMAC_FEAT_CC7_IF_TYPE_RGMII; } break; case BCMA_CHIP_ID_BCM4749: @@ -229,18 +243,11 @@ static int bgmac_probe(struct bcma_device *core) bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; bgmac->feature_flags |= BGMAC_FEAT_FLW_CTRL1; bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_PHY; - if (core->bus->chipinfo.pkg == 10) { + if (ci->pkg == 10) { bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_RGMII; bgmac->feature_flags |= BGMAC_FEAT_IOST_ATTACHED; } break; - case BCMA_CHIP_ID_BCM4716: - bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; - /* fallthrough */ - case BCMA_CHIP_ID_BCM47162: - bgmac->feature_flags |= BGMAC_FEAT_FLW_CTRL2; - bgmac->feature_flags |= BGMAC_FEAT_SET_RXQ_CLK; - break; /* bcm4707_family */ case BCMA_CHIP_ID_BCM4707: case BCMA_CHIP_ID_BCM47094: @@ -249,21 +256,6 @@ static int bgmac_probe(struct bcma_device *core) bgmac->feature_flags |= BGMAC_FEAT_NO_RESET; bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500; break; - case BCMA_CHIP_ID_BCM53573: - bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; - bgmac->feature_flags |= BGMAC_FEAT_SET_RXQ_CLK; - if (ci->pkg == BCMA_PKG_ID_BCM47189) - bgmac->feature_flags |= BGMAC_FEAT_IOST_ATTACHED; - if (core->core_unit == 0) { - bgmac->feature_flags |= BGMAC_FEAT_CC4_IF_SW_TYPE; - if (ci->pkg == BCMA_PKG_ID_BCM47189) - bgmac->feature_flags |= - BGMAC_FEAT_CC4_IF_SW_TYPE_RGMII; - } else if (core->core_unit == 1) { - bgmac->feature_flags |= BGMAC_FEAT_IRQ_ID_OOB_6; - bgmac->feature_flags |= BGMAC_FEAT_CC7_IF_TYPE_RGMII; - } - break; default: bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; bgmac->feature_flags |= BGMAC_FEAT_SET_RXQ_CLK; -- cgit From f3537b3494c06a98a8636c375b6d7a5060c5ac2d Mon Sep 17 00:00:00 2001 From: Joey Zhong Date: Tue, 28 Feb 2017 13:51:01 -0500 Subject: net: ethernet: bgmac: driver power manangement Implement suspend/resume callbacks in the bgmac driver. This makes sure that we de-initialize and re-initialize the hardware correctly before entering suspend and when resuming. Signed-off-by: Joey Zhong Signed-off-by: Jon Mason Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bgmac-platform.c | 34 +++++++++++++++++ drivers/net/ethernet/broadcom/bgmac.c | 51 ++++++++++++++++++++++++++ drivers/net/ethernet/broadcom/bgmac.h | 2 + 3 files changed, 87 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bgmac-platform.c b/drivers/net/ethernet/broadcom/bgmac-platform.c index da1b8b225eb9..73aca97a96bc 100644 --- a/drivers/net/ethernet/broadcom/bgmac-platform.c +++ b/drivers/net/ethernet/broadcom/bgmac-platform.c @@ -21,8 +21,12 @@ #include #include "bgmac.h" +#define NICPM_PADRING_CFG 0x00000004 #define NICPM_IOMUX_CTRL 0x00000008 +#define NICPM_PADRING_CFG_INIT_VAL 0x74000000 +#define NICPM_IOMUX_CTRL_INIT_VAL_AX 0x21880000 + #define NICPM_IOMUX_CTRL_INIT_VAL 0x3196e000 #define NICPM_IOMUX_CTRL_SPD_SHIFT 10 #define NICPM_IOMUX_CTRL_SPD_10M 0 @@ -113,6 +117,10 @@ static void bgmac_nicpm_speed_set(struct net_device *net_dev) if (!bgmac->plat.nicpm_base) return; + /* SET RGMII IO CONFIG */ + writel(NICPM_PADRING_CFG_INIT_VAL, + bgmac->plat.nicpm_base + NICPM_PADRING_CFG); + val = NICPM_IOMUX_CTRL_INIT_VAL; switch (bgmac->net_dev->phydev->speed) { default: @@ -244,6 +252,31 @@ static int bgmac_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int bgmac_suspend(struct device *dev) +{ + struct bgmac *bgmac = dev_get_drvdata(dev); + + return bgmac_enet_suspend(bgmac); +} + +static int bgmac_resume(struct device *dev) +{ + struct bgmac *bgmac = dev_get_drvdata(dev); + + return bgmac_enet_resume(bgmac); +} + +static const struct dev_pm_ops bgmac_pm_ops = { + .suspend = bgmac_suspend, + .resume = bgmac_resume +}; + +#define BGMAC_PM_OPS (&bgmac_pm_ops) +#else +#define BGMAC_PM_OPS NULL +#endif /* CONFIG_PM */ + static const struct of_device_id bgmac_of_enet_match[] = { {.compatible = "brcm,amac",}, {.compatible = "brcm,nsp-amac",}, @@ -257,6 +290,7 @@ static struct platform_driver bgmac_enet_driver = { .driver = { .name = "bgmac-enet", .of_match_table = bgmac_of_enet_match, + .pm = BGMAC_PM_OPS }, .probe = bgmac_probe, .remove = bgmac_remove, diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index fd66fca00e01..e1a24ee6ab8b 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -1480,6 +1480,7 @@ int bgmac_enet_probe(struct bgmac *bgmac) net_dev->irq = bgmac->irq; SET_NETDEV_DEV(net_dev, bgmac->dev); + dev_set_drvdata(bgmac->dev, bgmac); if (!is_valid_ether_addr(net_dev->dev_addr)) { dev_err(bgmac->dev, "Invalid MAC addr: %pM\n", @@ -1552,5 +1553,55 @@ void bgmac_enet_remove(struct bgmac *bgmac) } EXPORT_SYMBOL_GPL(bgmac_enet_remove); +int bgmac_enet_suspend(struct bgmac *bgmac) +{ + if (!netif_running(bgmac->net_dev)) + return 0; + + phy_stop(bgmac->net_dev->phydev); + + netif_stop_queue(bgmac->net_dev); + + napi_disable(&bgmac->napi); + + netif_tx_lock(bgmac->net_dev); + netif_device_detach(bgmac->net_dev); + netif_tx_unlock(bgmac->net_dev); + + bgmac_chip_intrs_off(bgmac); + bgmac_chip_reset(bgmac); + bgmac_dma_cleanup(bgmac); + + return 0; +} +EXPORT_SYMBOL_GPL(bgmac_enet_suspend); + +int bgmac_enet_resume(struct bgmac *bgmac) +{ + int rc; + + if (!netif_running(bgmac->net_dev)) + return 0; + + rc = bgmac_dma_init(bgmac); + if (rc) + return rc; + + bgmac_chip_init(bgmac); + + napi_enable(&bgmac->napi); + + netif_tx_lock(bgmac->net_dev); + netif_device_attach(bgmac->net_dev); + netif_tx_unlock(bgmac->net_dev); + + netif_start_queue(bgmac->net_dev); + + phy_start(bgmac->net_dev->phydev); + + return 0; +} +EXPORT_SYMBOL_GPL(bgmac_enet_resume); + MODULE_AUTHOR("Rafał Miłecki"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/broadcom/bgmac.h b/drivers/net/ethernet/broadcom/bgmac.h index a75ed3538e5c..c1818766c501 100644 --- a/drivers/net/ethernet/broadcom/bgmac.h +++ b/drivers/net/ethernet/broadcom/bgmac.h @@ -537,6 +537,8 @@ int bgmac_enet_probe(struct bgmac *bgmac); void bgmac_enet_remove(struct bgmac *bgmac); void bgmac_adjust_link(struct net_device *net_dev); int bgmac_phy_connect_direct(struct bgmac *bgmac); +int bgmac_enet_suspend(struct bgmac *bgmac); +int bgmac_enet_resume(struct bgmac *bgmac); struct mii_bus *bcma_mdio_mii_register(struct bgmac *bgmac); void bcma_mdio_mii_unregister(struct mii_bus *mii_bus); -- cgit From a6289d3fcc7349402e198ea8fb22d63ed4cb09dd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Mar 2017 22:59:04 +0100 Subject: mac80211: ignore VHT membership selector when parsing rates There isn't really much harm in not ignoring, since it doesn't represent a valid rate, but since we already ignore the HT one also ignore VHT. Also simplify the code a bit. Fix a typo in the related comment (pointed out by Arend) while at it. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1568a74757bc..4b4d29edec09 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2823,15 +2823,15 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband, *have_higher_than_11mbit = true; /* - * BSS_MEMBERSHIP_SELECTOR_HT_PHY is defined in 802.11n-2009 - * 7.3.2.2 as a magic value instead of a rate. Hence, skip it. + * Skip HT and VHT BSS membership selectors since they're not + * rates. * - * Note: Even through the membership selector and the basic + * Note: Even though the membership selector and the basic * rate flag share the same bit, they are not exactly * the same. */ - if (!!(supp_rates[i] & 0x80) && - (supp_rates[i] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY) + if (supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY) || + supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY)) continue; for (j = 0; j < sband->n_bitrates; j++) { -- cgit From a3f9d596b1ea9cf7db482879a8aeb91f54a87e75 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Mar 2017 23:04:09 +0100 Subject: iwlegacy: remove usage of txrc->max_rate_idx Just calculate it like mac80211 does today, so we can get rid of the calculation in mac80211 for everyone else. Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlegacy/3945-rs.c | 2 +- drivers/net/wireless/intel/iwlegacy/4965-rs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlegacy/3945-rs.c b/drivers/net/wireless/intel/iwlegacy/3945-rs.c index 03ad9b8b55f4..b2f35dfbc01b 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-rs.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-rs.c @@ -656,7 +656,7 @@ il3945_rs_get_rate(void *il_r, struct ieee80211_sta *sta, void *il_sta, rate_mask = sta->supp_rates[sband->band]; /* get user max rate if set */ - max_rate_idx = txrc->max_rate_idx; + max_rate_idx = fls(txrc->rate_idx_mask) - 1; if (sband->band == NL80211_BAND_5GHZ && max_rate_idx != -1) max_rate_idx += IL_FIRST_OFDM_RATE; if (max_rate_idx < 0 || max_rate_idx >= RATE_COUNT) diff --git a/drivers/net/wireless/intel/iwlegacy/4965-rs.c b/drivers/net/wireless/intel/iwlegacy/4965-rs.c index a867ae7f4095..c055f6da11c6 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-rs.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-rs.c @@ -2211,7 +2211,7 @@ il4965_rs_get_rate(void *il_r, struct ieee80211_sta *sta, void *il_sta, /* Get max rate if user set max rate */ if (lq_sta) { - lq_sta->max_rate_idx = txrc->max_rate_idx; + lq_sta->max_rate_idx = fls(txrc->rate_idx_mask) - 1; if (sband->band == NL80211_BAND_5GHZ && lq_sta->max_rate_idx != -1) lq_sta->max_rate_idx += IL_FIRST_OFDM_RATE; -- cgit From ce1d834f9d213c9f76f693762710f7ecfbb69503 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Mar 2017 23:04:09 +0100 Subject: iwlwifi: dvm: remove usage of txrc->max_rate_idx Just calculate it like mac80211 does today, so we can get rid of the calculation in mac80211 for everyone else. Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/dvm/rs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rs.c b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c index ff44ebc5829d..ddcd8c2d66cd 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rs.c @@ -2720,7 +2720,7 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, /* Get max rate if user set max rate */ if (lq_sta) { - lq_sta->max_rate_idx = txrc->max_rate_idx; + lq_sta->max_rate_idx = fls(txrc->rate_idx_mask) - 1; if ((sband->band == NL80211_BAND_5GHZ) && (lq_sta->max_rate_idx != -1)) lq_sta->max_rate_idx += IWL_FIRST_OFDM_RATE; -- cgit From b61fbda180b5c9f5f3ce7f2e63b0253c84ffdf09 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Mar 2017 22:59:59 +0100 Subject: mac80211: remove ieee80211_tx_rate_control.max_rate_idx As promised a little more than 7 years ago, remove it now since nothing uses it anymore. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 4 ---- net/mac80211/tx.c | 8 -------- 2 files changed, 12 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 1a26a375feb8..b1ac872dc88a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -5444,9 +5444,6 @@ void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif, * RTS threshold * @short_preamble: whether mac80211 will request short-preamble transmission * if the selected rate supports it - * @max_rate_idx: user-requested maximum (legacy) rate - * (deprecated; this will be removed once drivers get updated to use - * rate_idx_mask) * @rate_idx_mask: user-requested (legacy) rate mask * @rate_idx_mcs_mask: user-requested MCS rate mask (NULL if not in use) * @bss: whether this frame is sent out in AP or IBSS mode @@ -5458,7 +5455,6 @@ struct ieee80211_tx_rate_control { struct sk_buff *skb; struct ieee80211_tx_rate reported_rate; bool rts, short_preamble; - u8 max_rate_idx; u32 rate_idx_mask; u8 *rate_idx_mcs_mask; bool bss; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ba8d7db0a071..f27719eeeed7 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -682,10 +682,6 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) txrc.skb = tx->skb; txrc.reported_rate.idx = -1; txrc.rate_idx_mask = tx->sdata->rc_rateidx_mask[info->band]; - if (txrc.rate_idx_mask == (1 << sband->n_bitrates) - 1) - txrc.max_rate_idx = -1; - else - txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; if (tx->sdata->rc_has_mcs_mask[info->band]) txrc.rate_idx_mcs_mask = @@ -4249,10 +4245,6 @@ __ieee80211_beacon_get(struct ieee80211_hw *hw, txrc.skb = skb; txrc.reported_rate.idx = -1; txrc.rate_idx_mask = sdata->rc_rateidx_mask[band]; - if (txrc.rate_idx_mask == (1 << txrc.sband->n_bitrates) - 1) - txrc.max_rate_idx = -1; - else - txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; txrc.bss = true; rate_control_get_rate(sdata, NULL, &txrc); -- cgit From 57dacfedf856c475a10890c1f8ebcc1c63473cd1 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 28 Feb 2017 17:16:02 -0600 Subject: net: qcom/emac: optimize QDF2400 SGMII RX/TX impedence values Adjust the impedance values of the RX and TX lanes in the SGMII block so that they are closer to optimal values. Signed-off-by: Timur Tabi Signed-off-by: David S. Miller --- drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c index f62c215be779..7116be485e61 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c @@ -26,6 +26,7 @@ /* SGMII digital lane registers */ #define EMAC_SGMII_LN_DRVR_CTRL0 0x000C +#define EMAC_SGMII_LN_DRVR_CTRL1 0x0010 #define EMAC_SGMII_LN_DRVR_TAP_EN 0x0018 #define EMAC_SGMII_LN_TX_MARGINING 0x001C #define EMAC_SGMII_LN_TX_PRE 0x0020 @@ -48,6 +49,7 @@ #define EMAC_SGMII_LN_RX_EN_SIGNAL 0x02AC #define EMAC_SGMII_LN_RX_MISC_CNTRL0 0x02B8 #define EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV 0x02C8 +#define EMAC_SGMII_LN_RX_RESECODE_OFFSET 0x02CC /* SGMII digital lane register values */ #define UCDR_STEP_BY_TWO_MODE0 BIT(7) @@ -73,6 +75,8 @@ #define CML_GEAR_MODE(x) (((x) & 7) << 3) #define CML2CMOS_IBOOST_MODE(x) ((x) & 7) +#define RESCODE_OFFSET(x) ((x) & 0x1f) + #define MIXER_LOADB_MODE(x) (((x) & 0xf) << 2) #define MIXER_DATARATE_MODE(x) ((x) & 3) @@ -159,6 +163,8 @@ static const struct emac_reg_write sgmii_laned[] = { {EMAC_SGMII_LN_PARALLEL_RATE, PARALLEL_RATE_MODE0(1)}, {EMAC_SGMII_LN_TX_BAND_MODE, BAND_MODE0(1)}, {EMAC_SGMII_LN_RX_BAND, BAND_MODE0(2)}, + {EMAC_SGMII_LN_DRVR_CTRL1, RESCODE_OFFSET(7)}, + {EMAC_SGMII_LN_RX_RESECODE_OFFSET, RESCODE_OFFSET(9)}, {EMAC_SGMII_LN_LANE_MODE, LANE_MODE(26)}, {EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0, CDR_PD_SEL_MODE0(2) | EN_DLL_MODE0 | EN_IQ_DCC_MODE0 | EN_IQCAL_MODE0}, -- cgit From 9c28286b1b4b9bce6e35dd4c8a1265f03802a89a Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Sat, 4 Mar 2017 22:10:28 +0800 Subject: decnet: Use TCP nagle macro instead of literal number in decnet Use existing TCP nagle macro TCP_NAGLE_OFF and TCP_NAGLE_CORK instead of the literal number 1 and 2 in the current decnet codes. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- net/decnet/af_decnet.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index e6e79eda9763..0ec8cb4363e9 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -132,6 +132,7 @@ Version 0.0.6 2.1.110 07-aug-98 Eduardo Marcelo Serrat #include #include #include +#include #include #include #include @@ -1468,18 +1469,18 @@ static int __dn_setsockopt(struct socket *sock, int level,int optname, char __us case DSO_NODELAY: if (optlen != sizeof(int)) return -EINVAL; - if (scp->nonagle == 2) + if (scp->nonagle == TCP_NAGLE_CORK) return -EINVAL; - scp->nonagle = (u.val == 0) ? 0 : 1; + scp->nonagle = (u.val == 0) ? 0 : TCP_NAGLE_OFF; /* if (scp->nonagle == 1) { Push pending frames } */ break; case DSO_CORK: if (optlen != sizeof(int)) return -EINVAL; - if (scp->nonagle == 1) + if (scp->nonagle == TCP_NAGLE_OFF) return -EINVAL; - scp->nonagle = (u.val == 0) ? 0 : 2; + scp->nonagle = (u.val == 0) ? 0 : TCP_NAGLE_CORK; /* if (scp->nonagle == 0) { Push pending frames } */ break; @@ -1607,14 +1608,14 @@ static int __dn_getsockopt(struct socket *sock, int level,int optname, char __us case DSO_NODELAY: if (r_len > sizeof(int)) r_len = sizeof(int); - val = (scp->nonagle == 1); + val = (scp->nonagle == TCP_NAGLE_OFF); r_data = &val; break; case DSO_CORK: if (r_len > sizeof(int)) r_len = sizeof(int); - val = (scp->nonagle == 2); + val = (scp->nonagle == TCP_NAGLE_CORK); r_data = &val; break; -- cgit From e8e4f5280ddd0a7b43a795f90a0758e3c99df6a6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 Mar 2017 11:12:10 +0100 Subject: mac80211: reject/clear user rate mask if not usable If the user rate mask results in no (basic) rates being usable, clear it. Also, if we're already operating when it's set, reject it instead. Technically, selecting basic rates as the criterion is a bit too restrictive, but calculating the usable rates over all stations (e.g. in AP mode) is harder, and all stations must support the basic rates. Similarly, in client mode, the basic rates will be used anyway for control frames. This fixes the "no supported rates (...) in rate_mask ..." warning that occurs on TX when you've selected a rate mask that's not compatible with the connection (e.g. an AP that enables only the rates 36, 48, 54 and you've selected only 6, 9, 12.) Reported-by: Kirtika Ruchandani Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 18 +++++++++++++++++- net/mac80211/mlme.c | 2 ++ net/mac80211/rate.c | 27 +++++++++++++++++++++++++++ net/mac80211/rate.h | 2 ++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9c7490cb2243..8bc3d3669348 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3,7 +3,7 @@ * * Copyright 2006-2010 Johannes Berg * Copyright 2013-2015 Intel Mobile Communications GmbH - * Copyright (C) 2015-2016 Intel Deutschland GmbH + * Copyright (C) 2015-2017 Intel Deutschland GmbH * * This file is GPLv2 as found in COPYING. */ @@ -2042,6 +2042,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy, params->basic_rates_len, &sdata->vif.bss_conf.basic_rates); changed |= BSS_CHANGED_BASIC_RATES; + ieee80211_check_rate_mask(sdata); } if (params->ap_isolate >= 0) { @@ -2685,6 +2686,21 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, return ret; } + /* + * If active validate the setting and reject it if it doesn't leave + * at least one basic rate usable, since we really have to be able + * to send something, and if we're an AP we have to be able to do + * so at a basic rate so that all clients can receive it. + */ + if (rcu_access_pointer(sdata->vif.chanctx_conf) && + sdata->vif.bss_conf.chandef.chan) { + u32 basic_rates = sdata->vif.bss_conf.basic_rates; + enum nl80211_band band = sdata->vif.bss_conf.chandef.chan->band; + + if (!(mask->control[band].legacy & basic_rates)) + return -EINVAL; + } + for (i = 0; i < NUM_NL80211_BANDS; i++) { struct ieee80211_supported_band *sband = wiphy->bands[i]; int j; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 4b4d29edec09..24d69bcf71ad 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1908,6 +1908,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, sdata->u.mgd.associated = cbss; memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN); + ieee80211_check_rate_mask(sdata); + sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; if (sdata->vif.p2p || diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 094c15645228..3bddd9bbb76f 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -2,6 +2,7 @@ * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2006 Jiri Benc + * Copyright 2017 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 @@ -241,6 +242,32 @@ static void rate_control_free(struct ieee80211_local *local, kfree(ctrl_ref); } +void ieee80211_check_rate_mask(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + u32 user_mask, basic_rates = sdata->vif.bss_conf.basic_rates; + enum nl80211_band band; + + if (WARN_ON(!sdata->vif.bss_conf.chandef.chan)) + return; + + if (WARN_ON_ONCE(!basic_rates)) + return; + + band = sdata->vif.bss_conf.chandef.chan->band; + user_mask = sdata->rc_rateidx_mask[band]; + sband = local->hw.wiphy->bands[band]; + + if (user_mask & basic_rates) + return; + + sdata_dbg(sdata, + "no overlap between basic rates (0x%x) and user mask (0x%x on band %d) - clearing the latter", + basic_rates, user_mask, band); + sdata->rc_rateidx_mask[band] = (1 << sband->n_bitrates) - 1; +} + static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc) { struct sk_buff *skb = txrc->skb; diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index d51a1cce4d4a..f7825ef5f871 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -110,6 +110,8 @@ static inline void rate_control_remove_sta_debugfs(struct sta_info *sta) #endif } +void ieee80211_check_rate_mask(struct ieee80211_sub_if_data *sdata); + /* Get a reference to the rate control algorithm. If `name' is NULL, get the * first available algorithm. */ int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local, -- cgit From a4bb5b1b47cb50ac6feb662fade4608f8dd0248b Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Tue, 14 Feb 2017 23:03:46 +0100 Subject: bcma: gpio: set of_node regardless of the host type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DT allows describing many device types, not only platform ones. If e.g. bcma is hosted on PCI(e) and it has its of_node, let's pass it to the GPIO subsystem. This allows GPIO code to handle more hardware details not only for bcma on a SoC. Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo --- drivers/bcma/driver_gpio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c index 771a2a253440..7bde8d7a2816 100644 --- a/drivers/bcma/driver_gpio.c +++ b/drivers/bcma/driver_gpio.c @@ -185,8 +185,7 @@ int bcma_gpio_init(struct bcma_drv_cc *cc) chip->owner = THIS_MODULE; chip->parent = bcma_bus_get_host_dev(bus); #if IS_BUILTIN(CONFIG_OF) - if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) - chip->of_node = cc->core->dev.of_node; + chip->of_node = cc->core->dev.of_node; #endif switch (bus->chipinfo.id) { case BCMA_CHIP_ID_BCM4707: -- cgit From 96609f366c6f792421e1939c5c834abbe24eb88a Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 15 Feb 2017 10:25:04 +0100 Subject: rt2x00: rt2800lib: move rt2800_drv_data declaration into rt2800lib.h The rt2800_drv_data structure contains driver specific information. Move the declaration into the rt2800lib.h header which is a more logical place for it. Also fix the comment style to avoid checkpatch warning. The patch contains no functional changes, it is in preparation for the next patch. Signed-off-by: Gabor Juhos Signed-off-by: Daniel Golle Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800.h | 25 ------------------------- drivers/net/wireless/ralink/rt2x00/rt2800lib.h | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800.h b/drivers/net/wireless/ralink/rt2x00/rt2800.h index 256496bfbafb..0e7051d8132f 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h @@ -2987,29 +2987,4 @@ enum rt2800_eeprom_word { */ #define BCN_TBTT_OFFSET 64 -/* - * Hardware has 255 WCID table entries. First 32 entries are reserved for - * shared keys. Since parts of the pairwise key table might be shared with - * the beacon frame buffers 6 & 7 we could only use the first 222 entries. - */ -#define WCID_START 33 -#define WCID_END 222 -#define STA_IDS_SIZE (WCID_END - WCID_START + 2) - -/* - * RT2800 driver data structure - */ -struct rt2800_drv_data { - u8 calibration_bw20; - u8 calibration_bw40; - u8 bbp25; - u8 bbp26; - u8 txmixer_gain_24g; - u8 txmixer_gain_5g; - u8 max_psdu; - unsigned int tbtt_tick; - unsigned int ampdu_factor_cnt[4]; - DECLARE_BITMAP(sta_ids, STA_IDS_SIZE); -}; - #endif /* RT2800_H */ diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h index 0a8b4df665fe..8e1ae138c3f1 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h @@ -20,6 +20,29 @@ #ifndef RT2800LIB_H #define RT2800LIB_H +/* + * Hardware has 255 WCID table entries. First 32 entries are reserved for + * shared keys. Since parts of the pairwise key table might be shared with + * the beacon frame buffers 6 & 7 we could only use the first 222 entries. + */ +#define WCID_START 33 +#define WCID_END 222 +#define STA_IDS_SIZE (WCID_END - WCID_START + 2) + +/* RT2800 driver data structure */ +struct rt2800_drv_data { + u8 calibration_bw20; + u8 calibration_bw40; + u8 bbp25; + u8 bbp26; + u8 txmixer_gain_24g; + u8 txmixer_gain_5g; + u8 max_psdu; + unsigned int tbtt_tick; + unsigned int ampdu_factor_cnt[4]; + DECLARE_BITMAP(sta_ids, STA_IDS_SIZE); +}; + struct rt2800_ops { void (*register_read)(struct rt2x00_dev *rt2x00dev, const unsigned int offset, u32 *value); -- cgit From a13d985f26f6df07d5c5c0e190477628e236babc Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 15 Feb 2017 10:25:05 +0100 Subject: rt2800: identify station based on status WCID Add framework to identify sta based on tx status WCID. This is currently not used, will start be utilized in the future patch. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 5 +++++ drivers/net/wireless/ralink/rt2x00/rt2800lib.h | 1 + drivers/net/wireless/ralink/rt2x00/rt2x00queue.h | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 8223a1520316..46405cce35e0 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -855,11 +855,13 @@ EXPORT_SYMBOL_GPL(rt2800_process_rxwi); void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); struct txdone_entry_desc txdesc; u32 word; u16 mcs, real_mcs; int aggr, ampdu; + int wcid; /* * Obtain the status about this packet. @@ -872,6 +874,7 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi) real_mcs = rt2x00_get_field32(status, TX_STA_FIFO_MCS); aggr = rt2x00_get_field32(status, TX_STA_FIFO_TX_AGGRE); + wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID); /* * If a frame was meant to be sent as a single non-aggregated MPDU @@ -1468,6 +1471,7 @@ int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif, return 0; __set_bit(wcid - WCID_START, drv_data->sta_ids); + drv_data->wcid_to_sta[wcid - WCID_START] = sta; /* * Clean up WCID attributes and write STA address to the device. @@ -1498,6 +1502,7 @@ int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, struct ieee80211_sta *sta) * get renewed when the WCID is reused. */ rt2800_config_wcid(rt2x00dev, NULL, wcid); + drv_data->wcid_to_sta[wcid - WCID_START] = NULL; __clear_bit(wcid - WCID_START, drv_data->sta_ids); return 0; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h index 8e1ae138c3f1..6811d677a6e7 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h @@ -41,6 +41,7 @@ struct rt2800_drv_data { unsigned int tbtt_tick; unsigned int ampdu_factor_cnt[4]; DECLARE_BITMAP(sta_ids, STA_IDS_SIZE); + struct ieee80211_sta *wcid_to_sta[STA_IDS_SIZE]; }; struct rt2800_ops { diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h index 22d18818e850..9b297fce4692 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h @@ -102,7 +102,7 @@ enum skb_frame_desc_flags { * of the scope of the skb->data pointer. * @iv: IV/EIV data used during encryption/decryption. * @skb_dma: (PCI-only) the DMA address associated with the sk buffer. - * @entry: The entry to which this sk buffer belongs. + * @sta: The station where sk buffer was sent. */ struct skb_frame_desc { u8 flags; @@ -116,6 +116,7 @@ struct skb_frame_desc { __le32 iv[2]; dma_addr_t skb_dma; + struct ieee80211_sta *sta; }; /** -- cgit From 5edb05afebba8f488a30db29550e55c42eea6d6a Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 15 Feb 2017 10:25:06 +0100 Subject: rt2x00: separte filling tx status from rt2x00lib_txdone This makes rt2x00lib_txdone a bit simpler and will allow to reuse code in different variant of txdone which I'm preparing. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 141 +++++++++++++------------ 1 file changed, 76 insertions(+), 65 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index dd6678109b7e..b5d90fefc96b 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -313,73 +313,14 @@ static inline int rt2x00lib_txdone_bar_status(struct queue_entry *entry) return ret; } -void rt2x00lib_txdone(struct queue_entry *entry, - struct txdone_entry_desc *txdesc) +static void rt2x00lib_fill_tx_status(struct rt2x00_dev *rt2x00dev, + struct ieee80211_tx_info *tx_info, + struct skb_frame_desc *skbdesc, + struct txdone_entry_desc *txdesc, + bool success) { - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb); - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - unsigned int header_length, i; u8 rate_idx, rate_flags, retry_rates; - u8 skbdesc_flags = skbdesc->flags; - bool success; - - /* - * Unmap the skb. - */ - rt2x00queue_unmap_skb(entry); - - /* - * Remove the extra tx headroom from the skb. - */ - skb_pull(entry->skb, rt2x00dev->extra_tx_headroom); - - /* - * Signal that the TX descriptor is no longer in the skb. - */ - skbdesc->flags &= ~SKBDESC_DESC_IN_SKB; - - /* - * Determine the length of 802.11 header. - */ - header_length = ieee80211_get_hdrlen_from_skb(entry->skb); - - /* - * Remove L2 padding which was added during - */ - if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD)) - rt2x00queue_remove_l2pad(entry->skb, header_length); - - /* - * If the IV/EIV data was stripped from the frame before it was - * passed to the hardware, we should now reinsert it again because - * mac80211 will expect the same data to be present it the - * frame as it was passed to us. - */ - if (rt2x00_has_cap_hw_crypto(rt2x00dev)) - rt2x00crypto_tx_insert_iv(entry->skb, header_length); - - /* - * Send frame to debugfs immediately, after this call is completed - * we are going to overwrite the skb->cb array. - */ - rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry); - - /* - * Determine if the frame has been successfully transmitted and - * remove BARs from our check list while checking for their - * TX status. - */ - success = - rt2x00lib_txdone_bar_status(entry) || - test_bit(TXDONE_SUCCESS, &txdesc->flags) || - test_bit(TXDONE_UNKNOWN, &txdesc->flags); - - /* - * Update TX statistics. - */ - rt2x00dev->link.qual.tx_success += success; - rt2x00dev->link.qual.tx_failed += !success; + int i; rate_idx = skbdesc->tx_rate_idx; rate_flags = skbdesc->tx_rate_flags; @@ -448,6 +389,76 @@ void rt2x00lib_txdone(struct queue_entry *entry, else rt2x00dev->low_level_stats.dot11RTSFailureCount++; } +} + +void rt2x00lib_txdone(struct queue_entry *entry, + struct txdone_entry_desc *txdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb); + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + u8 skbdesc_flags = skbdesc->flags; + unsigned int header_length; + bool success; + + /* + * Unmap the skb. + */ + rt2x00queue_unmap_skb(entry); + + /* + * Remove the extra tx headroom from the skb. + */ + skb_pull(entry->skb, rt2x00dev->extra_tx_headroom); + + /* + * Signal that the TX descriptor is no longer in the skb. + */ + skbdesc->flags &= ~SKBDESC_DESC_IN_SKB; + + /* + * Determine the length of 802.11 header. + */ + header_length = ieee80211_get_hdrlen_from_skb(entry->skb); + + /* + * Remove L2 padding which was added during + */ + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD)) + rt2x00queue_remove_l2pad(entry->skb, header_length); + + /* + * If the IV/EIV data was stripped from the frame before it was + * passed to the hardware, we should now reinsert it again because + * mac80211 will expect the same data to be present it the + * frame as it was passed to us. + */ + if (rt2x00_has_cap_hw_crypto(rt2x00dev)) + rt2x00crypto_tx_insert_iv(entry->skb, header_length); + + /* + * Send frame to debugfs immediately, after this call is completed + * we are going to overwrite the skb->cb array. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry); + + /* + * Determine if the frame has been successfully transmitted and + * remove BARs from our check list while checking for their + * TX status. + */ + success = + rt2x00lib_txdone_bar_status(entry) || + test_bit(TXDONE_SUCCESS, &txdesc->flags) || + test_bit(TXDONE_UNKNOWN, &txdesc->flags); + + /* + * Update TX statistics. + */ + rt2x00dev->link.qual.tx_success += success; + rt2x00dev->link.qual.tx_failed += !success; + + rt2x00lib_fill_tx_status(rt2x00dev, tx_info, skbdesc, txdesc, success); /* * Only send the status report to mac80211 when it's a frame -- cgit From 56646adf9cd60b488ddc5633a2d9aa1f30efa5db Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 15 Feb 2017 10:25:07 +0100 Subject: rt2x00: separte clearing entry from rt2x00lib_txdone This makes rt2x00lib_txdone a bit simpler and will allow to reuse code in different variant of txdone which I'm preparing. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 51 +++++++++++++++----------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index b5d90fefc96b..03b368ac9cb6 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -391,6 +391,32 @@ static void rt2x00lib_fill_tx_status(struct rt2x00_dev *rt2x00dev, } } +static void rt2x00lib_clear_entry(struct rt2x00_dev *rt2x00dev, + struct queue_entry *entry) +{ + /* + * Make this entry available for reuse. + */ + entry->skb = NULL; + entry->flags = 0; + + rt2x00dev->ops->lib->clear_entry(entry); + + rt2x00queue_index_inc(entry, Q_INDEX_DONE); + + /* + * If the data queue was below the threshold before the txdone + * handler we must make sure the packet queue in the mac80211 stack + * is reenabled when the txdone handler has finished. This has to be + * serialized with rt2x00mac_tx(), otherwise we can wake up queue + * before it was stopped. + */ + spin_lock_bh(&entry->queue->tx_lock); + if (!rt2x00queue_threshold(entry->queue)) + rt2x00queue_unpause_queue(entry->queue); + spin_unlock_bh(&entry->queue->tx_lock); +} + void rt2x00lib_txdone(struct queue_entry *entry, struct txdone_entry_desc *txdesc) { @@ -471,30 +497,11 @@ void rt2x00lib_txdone(struct queue_entry *entry, ieee80211_tx_status(rt2x00dev->hw, entry->skb); else ieee80211_tx_status_ni(rt2x00dev->hw, entry->skb); - } else + } else { dev_kfree_skb_any(entry->skb); + } - /* - * Make this entry available for reuse. - */ - entry->skb = NULL; - entry->flags = 0; - - rt2x00dev->ops->lib->clear_entry(entry); - - rt2x00queue_index_inc(entry, Q_INDEX_DONE); - - /* - * If the data queue was below the threshold before the txdone - * handler we must make sure the packet queue in the mac80211 stack - * is reenabled when the txdone handler has finished. This has to be - * serialized with rt2x00mac_tx(), otherwise we can wake up queue - * before it was stopped. - */ - spin_lock_bh(&entry->queue->tx_lock); - if (!rt2x00queue_threshold(entry->queue)) - rt2x00queue_unpause_queue(entry->queue); - spin_unlock_bh(&entry->queue->tx_lock); + rt2x00lib_clear_entry(rt2x00dev, entry); } EXPORT_SYMBOL_GPL(rt2x00lib_txdone); -- cgit From a09305d052166cb489402a63a5d275e954e0b923 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 15 Feb 2017 10:25:08 +0100 Subject: rt2x00: add txdone nomatch function This txdone nomatch function will be used when we get status from the HW, but we could not match it with any sent skb. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2x00.h | 2 ++ drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 50 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h index 340787894c69..91ba10fdf732 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -1425,6 +1425,8 @@ void rt2x00lib_dmastart(struct queue_entry *entry); void rt2x00lib_dmadone(struct queue_entry *entry); void rt2x00lib_txdone(struct queue_entry *entry, struct txdone_entry_desc *txdesc); +void rt2x00lib_txdone_nomatch(struct queue_entry *entry, + struct txdone_entry_desc *txdesc); void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status); void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index 03b368ac9cb6..90fc259fb5bc 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -417,6 +417,56 @@ static void rt2x00lib_clear_entry(struct rt2x00_dev *rt2x00dev, spin_unlock_bh(&entry->queue->tx_lock); } +void rt2x00lib_txdone_nomatch(struct queue_entry *entry, + struct txdone_entry_desc *txdesc) +{ + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct ieee80211_tx_info txinfo = {}; + bool success; + + /* + * Unmap the skb. + */ + rt2x00queue_unmap_skb(entry); + + /* + * Signal that the TX descriptor is no longer in the skb. + */ + skbdesc->flags &= ~SKBDESC_DESC_IN_SKB; + + /* + * Send frame to debugfs immediately, after this call is completed + * we are going to overwrite the skb->cb array. + */ + rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TXDONE, entry); + + /* + * Determine if the frame has been successfully transmitted and + * remove BARs from our check list while checking for their + * TX status. + */ + success = + rt2x00lib_txdone_bar_status(entry) || + test_bit(TXDONE_SUCCESS, &txdesc->flags); + + if (!test_bit(TXDONE_UNKNOWN, &txdesc->flags)) { + /* + * Update TX statistics. + */ + rt2x00dev->link.qual.tx_success += success; + rt2x00dev->link.qual.tx_failed += !success; + + rt2x00lib_fill_tx_status(rt2x00dev, &txinfo, skbdesc, txdesc, + success); + ieee80211_tx_status_noskb(rt2x00dev->hw, skbdesc->sta, &txinfo); + } + + dev_kfree_skb_any(entry->skb); + rt2x00lib_clear_entry(rt2x00dev, entry); +} +EXPORT_SYMBOL_GPL(rt2x00lib_txdone_nomatch); + void rt2x00lib_txdone(struct queue_entry *entry, struct txdone_entry_desc *txdesc) { -- cgit From ec80ad70d778af7665992672896633ebd3b02ac8 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 15 Feb 2017 10:25:09 +0100 Subject: rt2x00: fixup fill_tx_status for nomatch case Add bits rt2x00lib_fill_tx_status() when filling status in nomatch case and hopefully do not break the function for existing cases. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 6 +++++- drivers/net/wireless/ralink/rt2x00/rt2x00queue.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index 90fc259fb5bc..e95d2aad3b3f 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -357,6 +357,9 @@ static void rt2x00lib_fill_tx_status(struct rt2x00_dev *rt2x00dev, if (i < (IEEE80211_TX_MAX_RATES - 1)) tx_info->status.rates[i].idx = -1; /* terminate */ + if (test_bit(TXDONE_NO_ACK_REQ, &txdesc->flags)) + tx_info->flags |= IEEE80211_TX_CTL_NO_ACK; + if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) { if (success) tx_info->flags |= IEEE80211_TX_STAT_ACK; @@ -375,7 +378,8 @@ static void rt2x00lib_fill_tx_status(struct rt2x00_dev *rt2x00dev, */ if (test_bit(TXDONE_AMPDU, &txdesc->flags) || tx_info->flags & IEEE80211_TX_CTL_AMPDU) { - tx_info->flags |= IEEE80211_TX_STAT_AMPDU; + tx_info->flags |= IEEE80211_TX_STAT_AMPDU | + IEEE80211_TX_CTL_AMPDU; tx_info->status.ampdu_len = 1; tx_info->status.ampdu_ack_len = success ? 1 : 0; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h index 9b297fce4692..c78fb8c8838a 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h @@ -215,6 +215,7 @@ enum txdone_entry_desc_flags { TXDONE_FAILURE, TXDONE_EXCESSIVE_RETRY, TXDONE_AMPDU, + TXDONE_NO_ACK_REQ, }; /** -- cgit From 293dff78ee058ec1e0b90e05a803c512b6a2097f Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 15 Feb 2017 10:25:10 +0100 Subject: rt2x00: use txdone_nomatch on rt2800usb If we do not match skb entry, provide tx status via nomatch procedure. Currently in that case we do rt2x00lib_txdone_noinfo(TXDONE_NOINFO), which actually assume that entry->skb was posted without retries and provide rate saved in skb desc as successful. Patch changed that to rate read from TX_STAT_FIFO, however still do not provide correct number of retries. On SoC/PCI devices we keep providing status via standard txdone procedure, no change in those devices, though we should thing about it. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 31 ++++++++++++++++++++----- drivers/net/wireless/ralink/rt2x00/rt2800lib.h | 3 ++- drivers/net/wireless/ralink/rt2x00/rt2800mmio.c | 2 +- drivers/net/wireless/ralink/rt2x00/rt2800usb.c | 18 ++++++-------- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 46405cce35e0..4a7bec708a13 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -852,7 +852,8 @@ void rt2800_process_rxwi(struct queue_entry *entry, } EXPORT_SYMBOL_GPL(rt2800_process_rxwi); -void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi) +void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi, + bool match) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; @@ -860,8 +861,7 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi) struct txdone_entry_desc txdesc; u32 word; u16 mcs, real_mcs; - int aggr, ampdu; - int wcid; + int aggr, ampdu, wcid, ack_req; /* * Obtain the status about this packet. @@ -875,6 +875,7 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi) real_mcs = rt2x00_get_field32(status, TX_STA_FIFO_MCS); aggr = rt2x00_get_field32(status, TX_STA_FIFO_TX_AGGRE); wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID); + ack_req = rt2x00_get_field32(status, TX_STA_FIFO_TX_ACK_REQUIRED); /* * If a frame was meant to be sent as a single non-aggregated MPDU @@ -891,8 +892,12 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi) * Hence, replace the requested rate with the real tx rate to not * confuse the rate control algortihm by providing clearly wrong * data. - */ - if (unlikely(aggr == 1 && ampdu == 0 && real_mcs != mcs)) { + * + * FIXME: if we do not find matching entry, we tell that frame was + * posted without any retries. We need to find a way to fix that + * and provide retry count. + */ + if (unlikely((aggr == 1 && ampdu == 0 && real_mcs != mcs)) || !match) { skbdesc->tx_rate_idx = real_mcs; mcs = real_mcs; } @@ -900,6 +905,9 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi) if (aggr == 1 || ampdu == 1) __set_bit(TXDONE_AMPDU, &txdesc.flags); + if (!ack_req) + __set_bit(TXDONE_NO_ACK_REQ, &txdesc.flags); + /* * Ralink has a retry mechanism using a global fallback * table. We setup this fallback table to try the immediate @@ -931,7 +939,18 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi) if (txdesc.retry) __set_bit(TXDONE_FALLBACK, &txdesc.flags); - rt2x00lib_txdone(entry, &txdesc); + if (!match) { + /* RCU assures non-null sta will not be freed by mac80211. */ + rcu_read_lock(); + if (likely(wcid >= WCID_START && wcid <= WCID_END)) + skbdesc->sta = drv_data->wcid_to_sta[wcid - WCID_START]; + else + skbdesc->sta = NULL; + rt2x00lib_txdone_nomatch(entry, &txdesc); + rcu_read_unlock(); + } else { + rt2x00lib_txdone(entry, &txdesc); + } } EXPORT_SYMBOL_GPL(rt2800_txdone_entry); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h index 6811d677a6e7..d9ef260d542a 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h @@ -191,7 +191,8 @@ void rt2800_write_tx_data(struct queue_entry *entry, struct txentry_desc *txdesc); void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *txdesc); -void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32* txwi); +void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi, + bool match); void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc); void rt2800_clear_beacon(struct queue_entry *entry); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c index de4790b41be7..3ab3b5323897 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c @@ -239,7 +239,7 @@ static bool rt2800mmio_txdone_release_entries(struct queue_entry *entry, { if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { rt2800_txdone_entry(entry, entry->status, - rt2800mmio_get_txwi(entry)); + rt2800mmio_get_txwi(entry), true); return false; } diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c index 205a7b8ac8a7..f11e3f532a84 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c @@ -501,8 +501,7 @@ static int rt2800usb_get_tx_data_len(struct queue_entry *entry) /* * TX control handlers */ -static enum txdone_entry_desc_flags -rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg) +static bool rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg) { __le32 *txwi; u32 word; @@ -515,7 +514,7 @@ rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg) * frame. */ if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) - return TXDONE_FAILURE; + return false; wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID); ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED); @@ -537,10 +536,10 @@ rt2800usb_txdone_entry_check(struct queue_entry *entry, u32 reg) rt2x00_dbg(entry->queue->rt2x00dev, "TX status report missed for queue %d entry %d\n", entry->queue->qid, entry->entry_idx); - return TXDONE_UNKNOWN; + return false; } - return TXDONE_SUCCESS; + return true; } static void rt2800usb_txdone(struct rt2x00_dev *rt2x00dev) @@ -549,7 +548,7 @@ static void rt2800usb_txdone(struct rt2x00_dev *rt2x00dev) struct queue_entry *entry; u32 reg; u8 qid; - enum txdone_entry_desc_flags done_status; + bool match; while (kfifo_get(&rt2x00dev->txstatus_fifo, ®)) { /* @@ -574,11 +573,8 @@ static void rt2800usb_txdone(struct rt2x00_dev *rt2x00dev) break; } - done_status = rt2800usb_txdone_entry_check(entry, reg); - if (likely(done_status == TXDONE_SUCCESS)) - rt2800_txdone_entry(entry, reg, rt2800usb_get_txwi(entry)); - else - rt2x00lib_txdone_noinfo(entry, done_status); + match = rt2800usb_txdone_entry_check(entry, reg); + rt2800_txdone_entry(entry, reg, rt2800usb_get_txwi(entry), match); } } -- cgit From 9d7a7a4d2b02bcd30fb5fe4270278212353cc332 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 15 Feb 2017 10:25:11 +0100 Subject: rt2800: status based rate flags for nomatch case We use skb_desc->tx_rate_flags from entry as rate[].flags even if skb does not match status. Patch corrects flags and also fixes mcs for legacy rates. rt2800_rate_from_status() is based on Felix's mt76 mt76x2_mac_process_tx_rate() function. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800.h | 2 ++ drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 35 +++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800.h b/drivers/net/wireless/ralink/rt2x00/rt2800.h index 0e7051d8132f..480b08601785 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h @@ -1760,6 +1760,8 @@ #define TX_STA_FIFO_WCID FIELD32(0x0000ff00) #define TX_STA_FIFO_SUCCESS_RATE FIELD32(0xffff0000) #define TX_STA_FIFO_MCS FIELD32(0x007f0000) +#define TX_STA_FIFO_BW FIELD32(0x00800000) +#define TX_STA_FIFO_SGI FIELD32(0x01000000) #define TX_STA_FIFO_PHYMODE FIELD32(0xc0000000) /* diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 4a7bec708a13..8d00c599e47a 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -852,6 +852,39 @@ void rt2800_process_rxwi(struct queue_entry *entry, } EXPORT_SYMBOL_GPL(rt2800_process_rxwi); +static void rt2800_rate_from_status(struct skb_frame_desc *skbdesc, + u32 status, enum nl80211_band band) +{ + u8 flags = 0; + u8 idx = rt2x00_get_field32(status, TX_STA_FIFO_MCS); + + switch (rt2x00_get_field32(status, TX_STA_FIFO_PHYMODE)) { + case RATE_MODE_HT_GREENFIELD: + flags |= IEEE80211_TX_RC_GREEN_FIELD; + /* fall through */ + case RATE_MODE_HT_MIX: + flags |= IEEE80211_TX_RC_MCS; + break; + case RATE_MODE_OFDM: + if (band == NL80211_BAND_2GHZ) + idx += 4; + break; + case RATE_MODE_CCK: + if (idx >= 8) + idx -= 8; + break; + } + + if (rt2x00_get_field32(status, TX_STA_FIFO_BW)) + flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + + if (rt2x00_get_field32(status, TX_STA_FIFO_SGI)) + flags |= IEEE80211_TX_RC_SHORT_GI; + + skbdesc->tx_rate_idx = idx; + skbdesc->tx_rate_flags = flags; +} + void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi, bool match) { @@ -898,7 +931,7 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi, * and provide retry count. */ if (unlikely((aggr == 1 && ampdu == 0 && real_mcs != mcs)) || !match) { - skbdesc->tx_rate_idx = real_mcs; + rt2800_rate_from_status(skbdesc, status, rt2x00dev->curr_band); mcs = real_mcs; } -- cgit From fb47ada8dc3c30c8e7b415da155742b49536c61e Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 15 Feb 2017 10:25:12 +0100 Subject: rt2800: use TXOP_BACKOFF for probe frames Even if we do not set AMPDU bit in TXWI, device still can aggregate frame and send it with rate not corresponding to requested. That mean we can do not sent probe frames with requested rate. To prevent that use TXOP_BACKOFF for probe frames. Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2x00queue.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c index e1660b92b20c..a2c1ca5c76d1 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.c @@ -372,15 +372,16 @@ static void rt2x00queue_create_tx_descriptor_ht(struct rt2x00_dev *rt2x00dev, /* * Determine IFS values - * - Use TXOP_BACKOFF for management frames except beacons + * - Use TXOP_BACKOFF for probe and management frames except beacons * - Use TXOP_SIFS for fragment bursts * - Use TXOP_HTTXOP for everything else * * Note: rt2800 devices won't use CTS protection (if used) * for frames not transmitted with TXOP_HTTXOP */ - if (ieee80211_is_mgmt(hdr->frame_control) && - !ieee80211_is_beacon(hdr->frame_control)) + if ((ieee80211_is_mgmt(hdr->frame_control) && + !ieee80211_is_beacon(hdr->frame_control)) || + (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) txdesc->u.ht.txop = TXOP_BACKOFF; else if (!(tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)) txdesc->u.ht.txop = TXOP_SIFS; -- cgit From dd35cc0896faff5ed9d22eac9ea4a1920e2eec0c Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 15 Feb 2017 10:25:13 +0100 Subject: rt2x00: fix rt2x00debug_dump_frame comment Reported-by: Jeroen Roovers Signed-off-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2x00.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h index 91ba10fdf732..ce340bfd71a0 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -1396,7 +1396,7 @@ void rt2x00queue_flush_queues(struct rt2x00_dev *rt2x00dev, bool drop); * rt2x00debug_dump_frame - Dump a frame to userspace through debugfs. * @rt2x00dev: Pointer to &struct rt2x00_dev. * @type: The type of frame that is being dumped. - * @skb: The skb containing the frame to be dumped. + * @entry: The queue entry containing the frame to be dumped. */ #ifdef CONFIG_RT2X00_LIB_DEBUGFS void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev, -- cgit From 3beb27028c446dce74a1690ea3ef2dfae7503e74 Mon Sep 17 00:00:00 2001 From: Nils Holland Date: Mon, 20 Feb 2017 00:59:28 +0100 Subject: rtl8187: Enable monitor mode to fix multicast reception The rtl8187 cards don't seem to receive multicast frames, which, among other things, makes them fail to receive RAs in IPv6 networks. The cause seems to be that the RTL818X_RX_CONF_MULTICAST flag doesn't have the desired effect. Fix the issue by setting RTL818X_RX_CONF_MONITOR instead, which puts the card into monitor mode and resolves the problem so that multicast frames are sucessfully passed to the kernel. The existence of the problem and the effectiveness of the solution has originally been confirmed on an 8187B based card with the USB id of 0bda:8197. Subsequent testing by Larry Finger on an 8187L based card, which follows the second (8187, i.e. "non-b") code path in the driver, has confirmed that the fix does not cause any noticeable regresssions there either. Signed-off-by: Nils Holland Acked-by: Larry Finger Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c index 231f84db9ab0..56a8686cd367 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c @@ -946,8 +946,7 @@ static int rtl8187_start(struct ieee80211_hw *dev) (7 << 13 /* RX FIFO threshold NONE */) | (7 << 10 /* MAX RX DMA */) | RTL818X_RX_CONF_RX_AUTORESETPHY | - RTL818X_RX_CONF_ONLYERLPKT | - RTL818X_RX_CONF_MULTICAST; + RTL818X_RX_CONF_ONLYERLPKT; priv->rx_conf = reg; rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); @@ -1319,12 +1318,11 @@ static void rtl8187_configure_filter(struct ieee80211_hw *dev, priv->rx_conf ^= RTL818X_RX_CONF_FCS; if (changed_flags & FIF_CONTROL) priv->rx_conf ^= RTL818X_RX_CONF_CTRL; - if (changed_flags & FIF_OTHER_BSS) - priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; - if (*total_flags & FIF_ALLMULTI || multicast > 0) - priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; + if (*total_flags & FIF_OTHER_BSS || + *total_flags & FIF_ALLMULTI || multicast > 0) + priv->rx_conf |= RTL818X_RX_CONF_MONITOR; else - priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; + priv->rx_conf &= ~RTL818X_RX_CONF_MONITOR; *total_flags = 0; @@ -1332,10 +1330,10 @@ static void rtl8187_configure_filter(struct ieee80211_hw *dev, *total_flags |= FIF_FCSFAIL; if (priv->rx_conf & RTL818X_RX_CONF_CTRL) *total_flags |= FIF_CONTROL; - if (priv->rx_conf & RTL818X_RX_CONF_MONITOR) + if (priv->rx_conf & RTL818X_RX_CONF_MONITOR) { *total_flags |= FIF_OTHER_BSS; - if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST) *total_flags |= FIF_ALLMULTI; + } rtl818x_iowrite32_async(priv, &priv->map->RX_CONF, priv->rx_conf); } -- cgit From 3e062eb21df80977a63e630d7da326c9b1e9ab51 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 22 Feb 2017 23:35:52 +0000 Subject: rtlwifi: fix spelling mistake: "conuntry" -> "country" trivial fix to spelling mistake in RT_TRACE message Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/regd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/regd.c b/drivers/net/wireless/realtek/rtlwifi/regd.c index 558c31bf5c80..1bf3eb25c1da 100644 --- a/drivers/net/wireless/realtek/rtlwifi/regd.c +++ b/drivers/net/wireless/realtek/rtlwifi/regd.c @@ -435,7 +435,7 @@ int rtl_regd_init(struct ieee80211_hw *hw, channel_plan_to_country_code(rtlpriv->efuse.channel_plan); RT_TRACE(rtlpriv, COMP_REGD, DBG_DMESG, - "rtl: EEPROM regdomain: 0x%0x conuntry code: %d\n", + "rtl: EEPROM regdomain: 0x%0x country code: %d\n", rtlpriv->efuse.channel_plan, rtlpriv->regd.country_code); if (rtlpriv->regd.country_code >= COUNTRY_CODE_MAX) { -- cgit From efc9b8e33b8b5ef890288758454ce62a1319c94a Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 7 Mar 2017 12:45:04 +0100 Subject: netfilter: bridge: remove unneeded rcu_read_lock as comment says, the function is always called with rcu read lock held. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/bridge/br_netfilter_hooks.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 95087e6e8258..52739e6c610e 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -1016,13 +1016,10 @@ int br_nf_hook_thresh(unsigned int hook, struct net *net, if (!elem) return okfn(net, sk, skb); - /* We may already have this, but read-locks nest anyway */ - rcu_read_lock(); nf_hook_state_init(&state, hook, NFPROTO_BRIDGE, indev, outdev, sk, net, okfn); ret = nf_hook_slow(skb, &state, elem); - rcu_read_unlock(); if (ret == 1) ret = okfn(net, sk, skb); -- cgit From c1183db8853d14df390748aa6baa69734db151aa Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Tue, 7 Mar 2017 22:02:50 +0900 Subject: netfilter: nf_reject: remove unused variable variable oiph is not used. Signed-off-by: Taehee Yoo Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/nf_reject_ipv4.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c index 146d86105183..7cd8d0d918f8 100644 --- a/net/ipv4/netfilter/nf_reject_ipv4.c +++ b/net/ipv4/netfilter/nf_reject_ipv4.c @@ -104,7 +104,6 @@ EXPORT_SYMBOL_GPL(nf_reject_ip_tcphdr_put); void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook) { struct sk_buff *nskb; - const struct iphdr *oiph; struct iphdr *niph; const struct tcphdr *oth; struct tcphdr _oth; @@ -116,8 +115,6 @@ void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook) if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST)) return; - oiph = ip_hdr(oldskb); - nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) + LL_MAX_HEADER, GFP_ATOMIC); if (!nskb) -- cgit From 9e0b516af24eb72502bb61d383c22cfbe0407c2c Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Tue, 28 Feb 2017 23:49:38 +0100 Subject: net: smsc: smc911x: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/smsc/smc911x.c | 51 ++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c index 4f19c6166182..36307d34f641 100644 --- a/drivers/net/ethernet/smsc/smc911x.c +++ b/drivers/net/ethernet/smsc/smc911x.c @@ -1446,40 +1446,40 @@ static int smc911x_close(struct net_device *dev) * Ethtool support */ static int -smc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd) +smc911x_ethtool_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct smc911x_local *lp = netdev_priv(dev); int ret, status; unsigned long flags; + u32 supported; DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - cmd->maxtxpkt = 1; - cmd->maxrxpkt = 1; if (lp->phy_type != 0) { spin_lock_irqsave(&lp->lock, flags); - ret = mii_ethtool_gset(&lp->mii, cmd); + ret = mii_ethtool_get_link_ksettings(&lp->mii, cmd); spin_unlock_irqrestore(&lp->lock, flags); } else { - cmd->supported = SUPPORTED_10baseT_Half | + supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_TP | SUPPORTED_AUI; if (lp->ctl_rspeed == 10) - ethtool_cmd_speed_set(cmd, SPEED_10); + cmd->base.speed = SPEED_10; else if (lp->ctl_rspeed == 100) - ethtool_cmd_speed_set(cmd, SPEED_100); - - cmd->autoneg = AUTONEG_DISABLE; - if (lp->mii.phy_id==1) - cmd->transceiver = XCVR_INTERNAL; - else - cmd->transceiver = XCVR_EXTERNAL; - cmd->port = 0; + cmd->base.speed = SPEED_100; + + cmd->base.autoneg = AUTONEG_DISABLE; + cmd->base.port = 0; SMC_GET_PHY_SPECIAL(lp, lp->mii.phy_id, status); - cmd->duplex = + cmd->base.duplex = (status & (PHY_SPECIAL_SPD_10FULL_ | PHY_SPECIAL_SPD_100FULL_)) ? DUPLEX_FULL : DUPLEX_HALF; + + ethtool_convert_legacy_u32_to_link_mode( + cmd->link_modes.supported, supported); + ret = 0; } @@ -1487,7 +1487,8 @@ smc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd) } static int -smc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd) +smc911x_ethtool_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct smc911x_local *lp = netdev_priv(dev); int ret; @@ -1495,16 +1496,18 @@ smc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd) if (lp->phy_type != 0) { spin_lock_irqsave(&lp->lock, flags); - ret = mii_ethtool_sset(&lp->mii, cmd); + ret = mii_ethtool_set_link_ksettings(&lp->mii, cmd); spin_unlock_irqrestore(&lp->lock, flags); } else { - if (cmd->autoneg != AUTONEG_DISABLE || - cmd->speed != SPEED_10 || - (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL) || - (cmd->port != PORT_TP && cmd->port != PORT_AUI)) + if (cmd->base.autoneg != AUTONEG_DISABLE || + cmd->base.speed != SPEED_10 || + (cmd->base.duplex != DUPLEX_HALF && + cmd->base.duplex != DUPLEX_FULL) || + (cmd->base.port != PORT_TP && + cmd->base.port != PORT_AUI)) return -EINVAL; - lp->ctl_rfduplx = cmd->duplex == DUPLEX_FULL; + lp->ctl_rfduplx = cmd->base.duplex == DUPLEX_FULL; ret = 0; } @@ -1686,8 +1689,6 @@ static int smc911x_ethtool_geteeprom_len(struct net_device *dev) } static const struct ethtool_ops smc911x_ethtool_ops = { - .get_settings = smc911x_ethtool_getsettings, - .set_settings = smc911x_ethtool_setsettings, .get_drvinfo = smc911x_ethtool_getdrvinfo, .get_msglevel = smc911x_ethtool_getmsglevel, .set_msglevel = smc911x_ethtool_setmsglevel, @@ -1698,6 +1699,8 @@ static const struct ethtool_ops smc911x_ethtool_ops = { .get_eeprom_len = smc911x_ethtool_geteeprom_len, .get_eeprom = smc911x_ethtool_geteeprom, .set_eeprom = smc911x_ethtool_seteeprom, + .get_link_ksettings = smc911x_ethtool_get_link_ksettings, + .set_link_ksettings = smc911x_ethtool_set_link_ksettings, }; /* -- cgit From c4df19a66617a4818769468975019c736a84e0c9 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 4 Mar 2017 12:42:39 +0100 Subject: net: smsc: smc91x: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Tested-by: Robert Jarzmik Signed-off-by: David S. Miller --- drivers/net/ethernet/smsc/smc91x.c | 47 ++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 65077c77082a..91e9bd7159ab 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -1535,32 +1535,33 @@ static int smc_close(struct net_device *dev) * Ethtool support */ static int -smc_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd) +smc_ethtool_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct smc_local *lp = netdev_priv(dev); int ret; - cmd->maxtxpkt = 1; - cmd->maxrxpkt = 1; - if (lp->phy_type != 0) { spin_lock_irq(&lp->lock); - ret = mii_ethtool_gset(&lp->mii, cmd); + ret = mii_ethtool_get_link_ksettings(&lp->mii, cmd); spin_unlock_irq(&lp->lock); } else { - cmd->supported = SUPPORTED_10baseT_Half | + u32 supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_TP | SUPPORTED_AUI; if (lp->ctl_rspeed == 10) - ethtool_cmd_speed_set(cmd, SPEED_10); + cmd->base.speed = SPEED_10; else if (lp->ctl_rspeed == 100) - ethtool_cmd_speed_set(cmd, SPEED_100); + cmd->base.speed = SPEED_100; + + cmd->base.autoneg = AUTONEG_DISABLE; + cmd->base.port = 0; + cmd->base.duplex = lp->tcr_cur_mode & TCR_SWFDUP ? + DUPLEX_FULL : DUPLEX_HALF; - cmd->autoneg = AUTONEG_DISABLE; - cmd->transceiver = XCVR_INTERNAL; - cmd->port = 0; - cmd->duplex = lp->tcr_cur_mode & TCR_SWFDUP ? DUPLEX_FULL : DUPLEX_HALF; + ethtool_convert_legacy_u32_to_link_mode( + cmd->link_modes.supported, supported); ret = 0; } @@ -1569,24 +1570,26 @@ smc_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd) } static int -smc_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd) +smc_ethtool_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct smc_local *lp = netdev_priv(dev); int ret; if (lp->phy_type != 0) { spin_lock_irq(&lp->lock); - ret = mii_ethtool_sset(&lp->mii, cmd); + ret = mii_ethtool_set_link_ksettings(&lp->mii, cmd); spin_unlock_irq(&lp->lock); } else { - if (cmd->autoneg != AUTONEG_DISABLE || - cmd->speed != SPEED_10 || - (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL) || - (cmd->port != PORT_TP && cmd->port != PORT_AUI)) + if (cmd->base.autoneg != AUTONEG_DISABLE || + cmd->base.speed != SPEED_10 || + (cmd->base.duplex != DUPLEX_HALF && + cmd->base.duplex != DUPLEX_FULL) || + (cmd->base.port != PORT_TP && cmd->base.port != PORT_AUI)) return -EINVAL; -// lp->port = cmd->port; - lp->ctl_rfduplx = cmd->duplex == DUPLEX_FULL; +// lp->port = cmd->base.port; + lp->ctl_rfduplx = cmd->base.duplex == DUPLEX_FULL; // if (netif_running(dev)) // smc_set_port(dev); @@ -1744,8 +1747,6 @@ static int smc_ethtool_seteeprom(struct net_device *dev, static const struct ethtool_ops smc_ethtool_ops = { - .get_settings = smc_ethtool_getsettings, - .set_settings = smc_ethtool_setsettings, .get_drvinfo = smc_ethtool_getdrvinfo, .get_msglevel = smc_ethtool_getmsglevel, @@ -1755,6 +1756,8 @@ static const struct ethtool_ops smc_ethtool_ops = { .get_eeprom_len = smc_ethtool_geteeprom_len, .get_eeprom = smc_ethtool_geteeprom, .set_eeprom = smc_ethtool_seteeprom, + .get_link_ksettings = smc_ethtool_get_link_ksettings, + .set_link_ksettings = smc_ethtool_set_link_ksettings, }; static const struct net_device_ops smc_netdev_ops = { -- cgit From 2c784b0087d8b03b97bfcdbad8b162539dc614c6 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 4 Mar 2017 16:16:12 +0100 Subject: net: sun: cassini: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/cassini.c | 98 ++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index 0e8e89f17dbb..382993c1561c 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -691,7 +691,8 @@ static void cas_mif_poll(struct cas *cp, const int enable) } /* Must be invoked under cp->lock */ -static void cas_begin_auto_negotiation(struct cas *cp, struct ethtool_cmd *ep) +static void cas_begin_auto_negotiation(struct cas *cp, + const struct ethtool_link_ksettings *ep) { u16 ctl; #if 1 @@ -704,16 +705,16 @@ static void cas_begin_auto_negotiation(struct cas *cp, struct ethtool_cmd *ep) if (!ep) goto start_aneg; lcntl = cp->link_cntl; - if (ep->autoneg == AUTONEG_ENABLE) + if (ep->base.autoneg == AUTONEG_ENABLE) { cp->link_cntl = BMCR_ANENABLE; - else { - u32 speed = ethtool_cmd_speed(ep); + } else { + u32 speed = ep->base.speed; cp->link_cntl = 0; if (speed == SPEED_100) cp->link_cntl |= BMCR_SPEED100; else if (speed == SPEED_1000) cp->link_cntl |= CAS_BMCR_SPEED1000; - if (ep->duplex == DUPLEX_FULL) + if (ep->base.duplex == DUPLEX_FULL) cp->link_cntl |= BMCR_FULLDPLX; } #if 1 @@ -4528,19 +4529,21 @@ static void cas_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info strlcpy(info->bus_info, pci_name(cp->pdev), sizeof(info->bus_info)); } -static int cas_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int cas_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct cas *cp = netdev_priv(dev); u16 bmcr; int full_duplex, speed, pause; unsigned long flags; enum link_state linkstate = link_up; + u32 supported, advertising; - cmd->advertising = 0; - cmd->supported = SUPPORTED_Autoneg; + advertising = 0; + supported = SUPPORTED_Autoneg; if (cp->cas_flags & CAS_FLAG_1000MB_CAP) { - cmd->supported |= SUPPORTED_1000baseT_Full; - cmd->advertising |= ADVERTISED_1000baseT_Full; + supported |= SUPPORTED_1000baseT_Full; + advertising |= ADVERTISED_1000baseT_Full; } /* Record PHY settings if HW is on. */ @@ -4548,17 +4551,15 @@ static int cas_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) bmcr = 0; linkstate = cp->lstate; if (CAS_PHY_MII(cp->phy_type)) { - cmd->port = PORT_MII; - cmd->transceiver = (cp->cas_flags & CAS_FLAG_SATURN) ? - XCVR_INTERNAL : XCVR_EXTERNAL; - cmd->phy_address = cp->phy_addr; - cmd->advertising |= ADVERTISED_TP | ADVERTISED_MII | + cmd->base.port = PORT_MII; + cmd->base.phy_address = cp->phy_addr; + advertising |= ADVERTISED_TP | ADVERTISED_MII | ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full; - cmd->supported |= + supported |= (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | @@ -4574,11 +4575,10 @@ static int cas_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) } } else { - cmd->port = PORT_FIBRE; - cmd->transceiver = XCVR_INTERNAL; - cmd->phy_address = 0; - cmd->supported |= SUPPORTED_FIBRE; - cmd->advertising |= ADVERTISED_FIBRE; + cmd->base.port = PORT_FIBRE; + cmd->base.phy_address = 0; + supported |= SUPPORTED_FIBRE; + advertising |= ADVERTISED_FIBRE; if (cp->hw_running) { /* pcs uses the same bits as mii */ @@ -4590,21 +4590,20 @@ static int cas_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) spin_unlock_irqrestore(&cp->lock, flags); if (bmcr & BMCR_ANENABLE) { - cmd->advertising |= ADVERTISED_Autoneg; - cmd->autoneg = AUTONEG_ENABLE; - ethtool_cmd_speed_set(cmd, ((speed == 10) ? + advertising |= ADVERTISED_Autoneg; + cmd->base.autoneg = AUTONEG_ENABLE; + cmd->base.speed = ((speed == 10) ? SPEED_10 : ((speed == 1000) ? - SPEED_1000 : SPEED_100))); - cmd->duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + SPEED_1000 : SPEED_100)); + cmd->base.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF; } else { - cmd->autoneg = AUTONEG_DISABLE; - ethtool_cmd_speed_set(cmd, ((bmcr & CAS_BMCR_SPEED1000) ? + cmd->base.autoneg = AUTONEG_DISABLE; + cmd->base.speed = ((bmcr & CAS_BMCR_SPEED1000) ? SPEED_1000 : ((bmcr & BMCR_SPEED100) ? - SPEED_100 : SPEED_10))); - cmd->duplex = - (bmcr & BMCR_FULLDPLX) ? + SPEED_100 : SPEED_10)); + cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; } if (linkstate != link_up) { @@ -4619,39 +4618,46 @@ static int cas_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) * settings that we configured. */ if (cp->link_cntl & BMCR_ANENABLE) { - ethtool_cmd_speed_set(cmd, 0); - cmd->duplex = 0xff; + cmd->base.speed = 0; + cmd->base.duplex = 0xff; } else { - ethtool_cmd_speed_set(cmd, SPEED_10); + cmd->base.speed = SPEED_10; if (cp->link_cntl & BMCR_SPEED100) { - ethtool_cmd_speed_set(cmd, SPEED_100); + cmd->base.speed = SPEED_100; } else if (cp->link_cntl & CAS_BMCR_SPEED1000) { - ethtool_cmd_speed_set(cmd, SPEED_1000); + cmd->base.speed = SPEED_1000; } - cmd->duplex = (cp->link_cntl & BMCR_FULLDPLX)? + cmd->base.duplex = (cp->link_cntl & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; } } + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); + return 0; } -static int cas_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int cas_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct cas *cp = netdev_priv(dev); unsigned long flags; - u32 speed = ethtool_cmd_speed(cmd); + u32 speed = cmd->base.speed; /* Verify the settings we care about. */ - if (cmd->autoneg != AUTONEG_ENABLE && - cmd->autoneg != AUTONEG_DISABLE) + if (cmd->base.autoneg != AUTONEG_ENABLE && + cmd->base.autoneg != AUTONEG_DISABLE) return -EINVAL; - if (cmd->autoneg == AUTONEG_DISABLE && + if (cmd->base.autoneg == AUTONEG_DISABLE && ((speed != SPEED_1000 && speed != SPEED_100 && speed != SPEED_10) || - (cmd->duplex != DUPLEX_HALF && - cmd->duplex != DUPLEX_FULL))) + (cmd->base.duplex != DUPLEX_HALF && + cmd->base.duplex != DUPLEX_FULL))) return -EINVAL; /* Apply settings and restart link process. */ @@ -4753,8 +4759,6 @@ static void cas_get_ethtool_stats(struct net_device *dev, static const struct ethtool_ops cas_ethtool_ops = { .get_drvinfo = cas_get_drvinfo, - .get_settings = cas_get_settings, - .set_settings = cas_set_settings, .nway_reset = cas_nway_reset, .get_link = cas_get_link, .get_msglevel = cas_get_msglevel, @@ -4764,6 +4768,8 @@ static const struct ethtool_ops cas_ethtool_ops = { .get_sset_count = cas_get_sset_count, .get_strings = cas_get_strings, .get_ethtool_stats = cas_get_ethtool_stats, + .get_link_ksettings = cas_get_link_ksettings, + .set_link_ksettings = cas_set_link_ksettings, }; static int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -- cgit From d972253146867b65ecff0bcc0e551a970b3efa1b Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 4 Mar 2017 17:50:06 +0100 Subject: net: sun: niu: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/niu.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index 57978056b336..2dcca249eb9c 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -6813,7 +6813,8 @@ static void niu_get_drvinfo(struct net_device *dev, sizeof(info->bus_info)); } -static int niu_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int niu_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct niu *np = netdev_priv(dev); struct niu_link_config *lp; @@ -6821,28 +6822,30 @@ static int niu_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) lp = &np->link_config; memset(cmd, 0, sizeof(*cmd)); - cmd->phy_address = np->phy_addr; - cmd->supported = lp->supported; - cmd->advertising = lp->active_advertising; - cmd->autoneg = lp->active_autoneg; - ethtool_cmd_speed_set(cmd, lp->active_speed); - cmd->duplex = lp->active_duplex; - cmd->port = (np->flags & NIU_FLAGS_FIBER) ? PORT_FIBRE : PORT_TP; - cmd->transceiver = (np->flags & NIU_FLAGS_XCVR_SERDES) ? - XCVR_EXTERNAL : XCVR_INTERNAL; + cmd->base.phy_address = np->phy_addr; + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + lp->supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + lp->active_advertising); + cmd->base.autoneg = lp->active_autoneg; + cmd->base.speed = lp->active_speed; + cmd->base.duplex = lp->active_duplex; + cmd->base.port = (np->flags & NIU_FLAGS_FIBER) ? PORT_FIBRE : PORT_TP; return 0; } -static int niu_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int niu_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct niu *np = netdev_priv(dev); struct niu_link_config *lp = &np->link_config; - lp->advertising = cmd->advertising; - lp->speed = ethtool_cmd_speed(cmd); - lp->duplex = cmd->duplex; - lp->autoneg = cmd->autoneg; + ethtool_convert_link_mode_to_legacy_u32(&lp->advertising, + cmd->link_modes.advertising); + lp->speed = cmd->base.speed; + lp->duplex = cmd->base.duplex; + lp->autoneg = cmd->base.autoneg; return niu_init_link(np); } @@ -7902,14 +7905,14 @@ static const struct ethtool_ops niu_ethtool_ops = { .nway_reset = niu_nway_reset, .get_eeprom_len = niu_get_eeprom_len, .get_eeprom = niu_get_eeprom, - .get_settings = niu_get_settings, - .set_settings = niu_set_settings, .get_strings = niu_get_strings, .get_sset_count = niu_get_sset_count, .get_ethtool_stats = niu_get_ethtool_stats, .set_phys_id = niu_set_phys_id, .get_rxnfc = niu_get_nfc, .set_rxnfc = niu_set_nfc, + .get_link_ksettings = niu_get_link_ksettings, + .set_link_ksettings = niu_set_link_ksettings, }; static int niu_ldg_assign_ldn(struct niu *np, struct niu_parent *parent, -- cgit From 92552fdda557d1a9d7a819a79d0e356d439e8cfc Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 5 Mar 2017 00:04:18 +0100 Subject: net: sun: sungem: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sungem.c | 98 +++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c index 5c5952e782cd..dbfca0466760 100644 --- a/drivers/net/ethernet/sun/sungem.c +++ b/drivers/net/ethernet/sun/sungem.c @@ -1250,12 +1250,17 @@ static void gem_stop_dma(struct gem *gp) // XXX dbl check what that function should do when called on PCS PHY -static void gem_begin_auto_negotiation(struct gem *gp, struct ethtool_cmd *ep) +static void gem_begin_auto_negotiation(struct gem *gp, + const struct ethtool_link_ksettings *ep) { u32 advertise, features; int autoneg; int speed; int duplex; + u32 advertising; + + ethtool_convert_link_mode_to_legacy_u32(&advertising, + ep->link_modes.advertising); if (gp->phy_type != phy_mii_mdio0 && gp->phy_type != phy_mii_mdio1) @@ -1278,13 +1283,13 @@ static void gem_begin_auto_negotiation(struct gem *gp, struct ethtool_cmd *ep) /* Setup link parameters */ if (!ep) goto start_aneg; - if (ep->autoneg == AUTONEG_ENABLE) { - advertise = ep->advertising; + if (ep->base.autoneg == AUTONEG_ENABLE) { + advertise = advertising; autoneg = 1; } else { autoneg = 0; - speed = ethtool_cmd_speed(ep); - duplex = ep->duplex; + speed = ep->base.speed; + duplex = ep->base.duplex; } start_aneg: @@ -2515,85 +2520,96 @@ static void gem_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info strlcpy(info->bus_info, pci_name(gp->pdev), sizeof(info->bus_info)); } -static int gem_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int gem_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct gem *gp = netdev_priv(dev); + u32 supported, advertising; if (gp->phy_type == phy_mii_mdio0 || gp->phy_type == phy_mii_mdio1) { if (gp->phy_mii.def) - cmd->supported = gp->phy_mii.def->features; + supported = gp->phy_mii.def->features; else - cmd->supported = (SUPPORTED_10baseT_Half | + supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full); /* XXX hardcoded stuff for now */ - cmd->port = PORT_MII; - cmd->transceiver = XCVR_EXTERNAL; - cmd->phy_address = 0; /* XXX fixed PHYAD */ + cmd->base.port = PORT_MII; + cmd->base.phy_address = 0; /* XXX fixed PHYAD */ /* Return current PHY settings */ - cmd->autoneg = gp->want_autoneg; - ethtool_cmd_speed_set(cmd, gp->phy_mii.speed); - cmd->duplex = gp->phy_mii.duplex; - cmd->advertising = gp->phy_mii.advertising; + cmd->base.autoneg = gp->want_autoneg; + cmd->base.speed = gp->phy_mii.speed; + cmd->base.duplex = gp->phy_mii.duplex; + advertising = gp->phy_mii.advertising; /* If we started with a forced mode, we don't have a default * advertise set, we need to return something sensible so * userland can re-enable autoneg properly. */ - if (cmd->advertising == 0) - cmd->advertising = cmd->supported; + if (advertising == 0) + advertising = supported; } else { // XXX PCS ? - cmd->supported = + supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg); - cmd->advertising = cmd->supported; - ethtool_cmd_speed_set(cmd, 0); - cmd->duplex = cmd->port = cmd->phy_address = - cmd->transceiver = cmd->autoneg = 0; + advertising = supported; + cmd->base.speed = 0; + cmd->base.duplex = 0; + cmd->base.port = 0; + cmd->base.phy_address = 0; + cmd->base.autoneg = 0; /* serdes means usually a Fibre connector, with most fixed */ if (gp->phy_type == phy_serdes) { - cmd->port = PORT_FIBRE; - cmd->supported = (SUPPORTED_1000baseT_Half | + cmd->base.port = PORT_FIBRE; + supported = (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Autoneg | SUPPORTED_Pause | SUPPORTED_Asym_Pause); - cmd->advertising = cmd->supported; - cmd->transceiver = XCVR_INTERNAL; + advertising = supported; if (gp->lstate == link_up) - ethtool_cmd_speed_set(cmd, SPEED_1000); - cmd->duplex = DUPLEX_FULL; - cmd->autoneg = 1; + cmd->base.speed = SPEED_1000; + cmd->base.duplex = DUPLEX_FULL; + cmd->base.autoneg = 1; } } - cmd->maxtxpkt = cmd->maxrxpkt = 0; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return 0; } -static int gem_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int gem_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct gem *gp = netdev_priv(dev); - u32 speed = ethtool_cmd_speed(cmd); + u32 speed = cmd->base.speed; + u32 advertising; + + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); /* Verify the settings we care about. */ - if (cmd->autoneg != AUTONEG_ENABLE && - cmd->autoneg != AUTONEG_DISABLE) + if (cmd->base.autoneg != AUTONEG_ENABLE && + cmd->base.autoneg != AUTONEG_DISABLE) return -EINVAL; - if (cmd->autoneg == AUTONEG_ENABLE && - cmd->advertising == 0) + if (cmd->base.autoneg == AUTONEG_ENABLE && + advertising == 0) return -EINVAL; - if (cmd->autoneg == AUTONEG_DISABLE && + if (cmd->base.autoneg == AUTONEG_DISABLE && ((speed != SPEED_1000 && speed != SPEED_100 && speed != SPEED_10) || - (cmd->duplex != DUPLEX_HALF && - cmd->duplex != DUPLEX_FULL))) + (cmd->base.duplex != DUPLEX_HALF && + cmd->base.duplex != DUPLEX_FULL))) return -EINVAL; /* Apply settings and restart link process. */ @@ -2666,13 +2682,13 @@ static int gem_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) static const struct ethtool_ops gem_ethtool_ops = { .get_drvinfo = gem_get_drvinfo, .get_link = ethtool_op_get_link, - .get_settings = gem_get_settings, - .set_settings = gem_set_settings, .nway_reset = gem_nway_reset, .get_msglevel = gem_get_msglevel, .set_msglevel = gem_set_msglevel, .get_wol = gem_get_wol, .set_wol = gem_set_wol, + .get_link_ksettings = gem_get_link_ksettings, + .set_link_ksettings = gem_set_link_ksettings, }; static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -- cgit From e016cc64423d2b6476e5c99d8a278f9093d53407 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 5 Mar 2017 22:25:39 +0100 Subject: net: sun: sunhme: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunhme.c | 62 +++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/drivers/net/ethernet/sun/sunhme.c b/drivers/net/ethernet/sun/sunhme.c index 72ff05cd3ed8..53ff66ef53ac 100644 --- a/drivers/net/ethernet/sun/sunhme.c +++ b/drivers/net/ethernet/sun/sunhme.c @@ -1294,9 +1294,10 @@ static void happy_meal_init_rings(struct happy_meal *hp) } /* hp->happy_lock must be held */ -static void happy_meal_begin_auto_negotiation(struct happy_meal *hp, - void __iomem *tregs, - struct ethtool_cmd *ep) +static void +happy_meal_begin_auto_negotiation(struct happy_meal *hp, + void __iomem *tregs, + const struct ethtool_link_ksettings *ep) { int timeout; @@ -1309,7 +1310,7 @@ static void happy_meal_begin_auto_negotiation(struct happy_meal *hp, /* XXX Check BMSR_ANEGCAPABLE, should not be necessary though. */ hp->sw_advertise = happy_meal_tcvr_read(hp, tregs, MII_ADVERTISE); - if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) { + if (!ep || ep->base.autoneg == AUTONEG_ENABLE) { /* Advertise everything we can support. */ if (hp->sw_bmsr & BMSR_10HALF) hp->sw_advertise |= (ADVERTISE_10HALF); @@ -1384,14 +1385,14 @@ force_link: /* Disable auto-negotiation in BMCR, enable the duplex and * speed setting, init the timer state machine, and fire it off. */ - if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) { + if (!ep || ep->base.autoneg == AUTONEG_ENABLE) { hp->sw_bmcr = BMCR_SPEED100; } else { - if (ethtool_cmd_speed(ep) == SPEED_100) + if (ep->base.speed == SPEED_100) hp->sw_bmcr = BMCR_SPEED100; else hp->sw_bmcr = 0; - if (ep->duplex == DUPLEX_FULL) + if (ep->base.duplex == DUPLEX_FULL) hp->sw_bmcr |= BMCR_FULLDPLX; } happy_meal_tcvr_write(hp, tregs, MII_BMCR, hp->sw_bmcr); @@ -2434,20 +2435,21 @@ static void happy_meal_set_multicast(struct net_device *dev) } /* Ethtool support... */ -static int hme_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int hme_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct happy_meal *hp = netdev_priv(dev); u32 speed; + u32 supported; - cmd->supported = + supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); /* XXX hardcoded stuff for now */ - cmd->port = PORT_TP; /* XXX no MII support */ - cmd->transceiver = XCVR_INTERNAL; /* XXX no external xcvr support */ - cmd->phy_address = 0; /* XXX fixed PHYAD */ + cmd->base.port = PORT_TP; /* XXX no MII support */ + cmd->base.phy_address = 0; /* XXX fixed PHYAD */ /* Record PHY settings. */ spin_lock_irq(&hp->happy_lock); @@ -2456,41 +2458,45 @@ static int hme_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) spin_unlock_irq(&hp->happy_lock); if (hp->sw_bmcr & BMCR_ANENABLE) { - cmd->autoneg = AUTONEG_ENABLE; + cmd->base.autoneg = AUTONEG_ENABLE; speed = ((hp->sw_lpa & (LPA_100HALF | LPA_100FULL)) ? SPEED_100 : SPEED_10); if (speed == SPEED_100) - cmd->duplex = + cmd->base.duplex = (hp->sw_lpa & (LPA_100FULL)) ? DUPLEX_FULL : DUPLEX_HALF; else - cmd->duplex = + cmd->base.duplex = (hp->sw_lpa & (LPA_10FULL)) ? DUPLEX_FULL : DUPLEX_HALF; } else { - cmd->autoneg = AUTONEG_DISABLE; + cmd->base.autoneg = AUTONEG_DISABLE; speed = (hp->sw_bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10; - cmd->duplex = + cmd->base.duplex = (hp->sw_bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; } - ethtool_cmd_speed_set(cmd, speed); + cmd->base.speed = speed; + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + return 0; } -static int hme_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int hme_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct happy_meal *hp = netdev_priv(dev); /* Verify the settings we care about. */ - if (cmd->autoneg != AUTONEG_ENABLE && - cmd->autoneg != AUTONEG_DISABLE) + if (cmd->base.autoneg != AUTONEG_ENABLE && + cmd->base.autoneg != AUTONEG_DISABLE) return -EINVAL; - if (cmd->autoneg == AUTONEG_DISABLE && - ((ethtool_cmd_speed(cmd) != SPEED_100 && - ethtool_cmd_speed(cmd) != SPEED_10) || - (cmd->duplex != DUPLEX_HALF && - cmd->duplex != DUPLEX_FULL))) + if (cmd->base.autoneg == AUTONEG_DISABLE && + ((cmd->base.speed != SPEED_100 && + cmd->base.speed != SPEED_10) || + (cmd->base.duplex != DUPLEX_HALF && + cmd->base.duplex != DUPLEX_FULL))) return -EINVAL; /* Ok, do it to it. */ @@ -2537,10 +2543,10 @@ static u32 hme_get_link(struct net_device *dev) } static const struct ethtool_ops hme_ethtool_ops = { - .get_settings = hme_get_settings, - .set_settings = hme_set_settings, .get_drvinfo = hme_get_drvinfo, .get_link = hme_get_link, + .get_link_ksettings = hme_get_link_ksettings, + .set_link_ksettings = hme_set_link_ksettings, }; static int hme_version_printed; -- cgit From 60f285129905c33203f66111363c5503ccbc5c41 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 5 Mar 2017 23:21:06 +0100 Subject: net: toshiba: ps3_genic_net: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Tested-by: Geoff Levand Signed-off-by: David S. Miller --- drivers/net/ethernet/toshiba/ps3_gelic_net.c | 51 ++++++++++++++++------------ 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.c b/drivers/net/ethernet/toshiba/ps3_gelic_net.c index 72013314bba8..fa6a06571187 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_net.c +++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c @@ -1206,61 +1206,68 @@ void gelic_net_get_drvinfo(struct net_device *netdev, strlcpy(info->version, DRV_VERSION, sizeof(info->version)); } -static int gelic_ether_get_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) +static int gelic_ether_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct gelic_card *card = netdev_card(netdev); + u32 supported, advertising; gelic_card_get_ether_port_status(card, 0); if (card->ether_port_status & GELIC_LV1_ETHER_FULL_DUPLEX) - cmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; else - cmd->duplex = DUPLEX_HALF; + cmd->base.duplex = DUPLEX_HALF; switch (card->ether_port_status & GELIC_LV1_ETHER_SPEED_MASK) { case GELIC_LV1_ETHER_SPEED_10: - ethtool_cmd_speed_set(cmd, SPEED_10); + cmd->base.speed = SPEED_10; break; case GELIC_LV1_ETHER_SPEED_100: - ethtool_cmd_speed_set(cmd, SPEED_100); + cmd->base.speed = SPEED_100; break; case GELIC_LV1_ETHER_SPEED_1000: - ethtool_cmd_speed_set(cmd, SPEED_1000); + cmd->base.speed = SPEED_1000; break; default: pr_info("%s: speed unknown\n", __func__); - ethtool_cmd_speed_set(cmd, SPEED_10); + cmd->base.speed = SPEED_10; break; } - cmd->supported = SUPPORTED_TP | SUPPORTED_Autoneg | + supported = SUPPORTED_TP | SUPPORTED_Autoneg | SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full; - cmd->advertising = cmd->supported; + advertising = supported; if (card->link_mode & GELIC_LV1_ETHER_AUTO_NEG) { - cmd->autoneg = AUTONEG_ENABLE; + cmd->base.autoneg = AUTONEG_ENABLE; } else { - cmd->autoneg = AUTONEG_DISABLE; - cmd->advertising &= ~ADVERTISED_Autoneg; + cmd->base.autoneg = AUTONEG_DISABLE; + advertising &= ~ADVERTISED_Autoneg; } - cmd->port = PORT_TP; + cmd->base.port = PORT_TP; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return 0; } -static int gelic_ether_set_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) +static int +gelic_ether_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct gelic_card *card = netdev_card(netdev); u64 mode; int ret; - if (cmd->autoneg == AUTONEG_ENABLE) { + if (cmd->base.autoneg == AUTONEG_ENABLE) { mode = GELIC_LV1_ETHER_AUTO_NEG; } else { - switch (cmd->speed) { + switch (cmd->base.speed) { case SPEED_10: mode = GELIC_LV1_ETHER_SPEED_10; break; @@ -1273,9 +1280,9 @@ static int gelic_ether_set_settings(struct net_device *netdev, default: return -EINVAL; } - if (cmd->duplex == DUPLEX_FULL) + if (cmd->base.duplex == DUPLEX_FULL) { mode |= GELIC_LV1_ETHER_FULL_DUPLEX; - else if (cmd->speed == SPEED_1000) { + } else if (cmd->base.speed == SPEED_1000) { pr_info("1000 half duplex is not supported.\n"); return -EINVAL; } @@ -1370,11 +1377,11 @@ done: static const struct ethtool_ops gelic_ether_ethtool_ops = { .get_drvinfo = gelic_net_get_drvinfo, - .get_settings = gelic_ether_get_settings, - .set_settings = gelic_ether_set_settings, .get_link = ethtool_op_get_link, .get_wol = gelic_net_get_wol, .set_wol = gelic_net_set_wol, + .get_link_ksettings = gelic_ether_get_link_ksettings, + .set_link_ksettings = gelic_ether_set_link_ksettings, }; /** -- cgit From fa383d663e3804ef76382c32524802b419b43770 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 5 Mar 2017 23:46:00 +0100 Subject: net: toshiba: spider_net: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/toshiba/spider_net_ethtool.c | 24 +++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/toshiba/spider_net_ethtool.c b/drivers/net/ethernet/toshiba/spider_net_ethtool.c index ffe519382e11..16bd036d0682 100644 --- a/drivers/net/ethernet/toshiba/spider_net_ethtool.c +++ b/drivers/net/ethernet/toshiba/spider_net_ethtool.c @@ -47,19 +47,23 @@ static struct { }; static int -spider_net_ethtool_get_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) +spider_net_ethtool_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct spider_net_card *card; card = netdev_priv(netdev); - cmd->supported = (SUPPORTED_1000baseT_Full | - SUPPORTED_FIBRE); - cmd->advertising = (ADVERTISED_1000baseT_Full | - ADVERTISED_FIBRE); - cmd->port = PORT_FIBRE; - ethtool_cmd_speed_set(cmd, card->phy.speed); - cmd->duplex = DUPLEX_FULL; + ethtool_link_ksettings_zero_link_mode(cmd, supported); + ethtool_link_ksettings_add_link_mode(cmd, supported, 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); + + ethtool_link_ksettings_zero_link_mode(cmd, advertising); + ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE); + + cmd->base.port = PORT_FIBRE; + cmd->base.speed = card->phy.speed; + cmd->base.duplex = DUPLEX_FULL; return 0; } @@ -166,7 +170,6 @@ static void spider_net_get_strings(struct net_device *netdev, u32 stringset, } const struct ethtool_ops spider_net_ethtool_ops = { - .get_settings = spider_net_ethtool_get_settings, .get_drvinfo = spider_net_ethtool_get_drvinfo, .get_wol = spider_net_ethtool_get_wol, .get_msglevel = spider_net_ethtool_get_msglevel, @@ -177,5 +180,6 @@ const struct ethtool_ops spider_net_ethtool_ops = { .get_strings = spider_net_get_strings, .get_sset_count = spider_net_get_sset_count, .get_ethtool_stats = spider_net_get_ethtool_stats, + .get_link_ksettings = spider_net_ethtool_get_link_ksettings, }; -- cgit From 3907e490d358aa82bf7446fc9d340f688c97c6de Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Thu, 12 Jan 2017 10:00:17 +0200 Subject: fsl/fman: parse result data is big endian Signed-off-by: Madalin Bucur --- drivers/net/ethernet/freescale/fman/fman.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/freescale/fman/fman.h b/drivers/net/ethernet/freescale/fman/fman.h index 57aae8d17d77..f53e1473dbcc 100644 --- a/drivers/net/ethernet/freescale/fman/fman.h +++ b/drivers/net/ethernet/freescale/fman/fman.h @@ -134,14 +134,14 @@ enum fman_exceptions { struct fman_prs_result { u8 lpid; /* Logical port id */ u8 shimr; /* Shim header result */ - u16 l2r; /* Layer 2 result */ - u16 l3r; /* Layer 3 result */ + __be16 l2r; /* Layer 2 result */ + __be16 l3r; /* Layer 3 result */ u8 l4r; /* Layer 4 result */ u8 cplan; /* Classification plan id */ - u16 nxthdr; /* Next Header */ - u16 cksum; /* Running-sum */ + __be16 nxthdr; /* Next Header */ + __be16 cksum; /* Running-sum */ /* Flags&fragment-offset field of the last IP-header */ - u16 flags_frag_off; + __be16 flags_frag_off; /* Routing type field of a IPV6 routing extension header */ u8 route_type; /* Routing Extension Header Present; last bit is IP valid */ -- cgit From 1df653cfea251386e8ecd8c9f983caacd965c78e Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Wed, 25 Jan 2017 13:41:28 +0200 Subject: fsl/fman: set HW parser as BMI next engine Enable the HW parser for all DPAA interfaces. Signed-off-by: Madalin Bucur --- drivers/net/ethernet/freescale/fman/fman.c | 21 ++++++++ drivers/net/ethernet/freescale/fman/fman_port.c | 72 +++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index f60845f0c6ca..d7559306226d 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -59,6 +59,7 @@ #define DMA_OFFSET 0x000C2000 #define FPM_OFFSET 0x000C3000 #define IMEM_OFFSET 0x000C4000 +#define HWP_OFFSET 0x000C7000 #define CGP_OFFSET 0x000DB000 /* Exceptions bit map */ @@ -218,6 +219,9 @@ #define QMI_GS_HALT_NOT_BUSY 0x00000002 +/* HWP defines */ +#define HWP_RPIMAC_PEN 0x00000001 + /* IRAM defines */ #define IRAM_IADD_AIE 0x80000000 #define IRAM_READY 0x80000000 @@ -475,6 +479,12 @@ struct fman_dma_regs { u32 res00e0[0x400 - 56]; }; +struct fman_hwp_regs { + u32 res0000[0x844 / 4]; /* 0x000..0x843 */ + u32 fmprrpimac; /* FM Parser Internal memory access control */ + u32 res[(0x1000 - 0x848) / 4]; /* 0x848..0xFFF */ +}; + /* Structure that holds current FMan state. * Used for saving run time information. */ @@ -606,6 +616,7 @@ struct fman { struct fman_bmi_regs __iomem *bmi_regs; struct fman_qmi_regs __iomem *qmi_regs; struct fman_dma_regs __iomem *dma_regs; + struct fman_hwp_regs __iomem *hwp_regs; fman_exceptions_cb *exception_cb; fman_bus_error_cb *bus_error_cb; /* Spinlock for FMan use */ @@ -999,6 +1010,12 @@ static void qmi_init(struct fman_qmi_regs __iomem *qmi_rg, iowrite32be(tmp_reg, &qmi_rg->fmqm_ien); } +static void hwp_init(struct fman_hwp_regs __iomem *hwp_rg) +{ + /* enable HW Parser */ + iowrite32be(HWP_RPIMAC_PEN, &hwp_rg->fmprrpimac); +} + static int enable(struct fman *fman, struct fman_cfg *cfg) { u32 cfg_reg = 0; @@ -1793,6 +1810,7 @@ static int fman_config(struct fman *fman) fman->bmi_regs = base_addr + BMI_OFFSET; fman->qmi_regs = base_addr + QMI_OFFSET; fman->dma_regs = base_addr + DMA_OFFSET; + fman->hwp_regs = base_addr + HWP_OFFSET; fman->base_addr = base_addr; spin_lock_init(&fman->spinlock); @@ -2062,6 +2080,9 @@ static int fman_init(struct fman *fman) /* Init QMI Registers */ qmi_init(fman->qmi_regs, fman->cfg); + /* Init HW Parser */ + hwp_init(fman->hwp_regs); + err = enable(fman, cfg); if (err != 0) return err; diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c index 9f3bb50a2365..f314348b3387 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.c +++ b/drivers/net/ethernet/freescale/fman/fman_port.c @@ -62,6 +62,7 @@ #define BMI_PORT_REGS_OFFSET 0 #define QMI_PORT_REGS_OFFSET 0x400 +#define HWP_PORT_REGS_OFFSET 0x800 /* Default values */ #define DFLT_PORT_BUFFER_PREFIX_CONTEXT_DATA_ALIGN \ @@ -182,7 +183,7 @@ #define NIA_ENG_BMI 0x00500000 #define NIA_ENG_QMI_ENQ 0x00540000 #define NIA_ENG_QMI_DEQ 0x00580000 - +#define NIA_ENG_HWP 0x00440000 #define NIA_BMI_AC_ENQ_FRAME 0x00000002 #define NIA_BMI_AC_TX_RELEASE 0x000002C0 #define NIA_BMI_AC_RELEASE 0x000000C0 @@ -317,6 +318,19 @@ struct fman_port_qmi_regs { u32 fmqm_pndcc; /* PortID n Dequeue Confirm Counter */ }; +#define HWP_HXS_COUNT 16 +#define HWP_HXS_PHE_REPORT 0x00000800 +#define HWP_HXS_PCAC_PSTAT 0x00000100 +#define HWP_HXS_PCAC_PSTOP 0x00000001 +struct fman_port_hwp_regs { + struct { + u32 ssa; /* Soft Sequence Attachment */ + u32 lcv; /* Line-up Enable Confirmation Mask */ + } pmda[HWP_HXS_COUNT]; /* Parse Memory Direct Access Registers */ + u32 reserved080[(0x3f8 - 0x080) / 4]; /* (0x080-0x3f7) */ + u32 fmpr_pcac; /* Configuration Access Control */ +}; + /* QMI dequeue prefetch modes */ enum fman_port_deq_prefetch { FMAN_PORT_DEQ_NO_PREFETCH, /* No prefetch mode */ @@ -436,6 +450,7 @@ struct fman_port { union fman_port_bmi_regs __iomem *bmi_regs; struct fman_port_qmi_regs __iomem *qmi_regs; + struct fman_port_hwp_regs __iomem *hwp_regs; struct fman_sp_buffer_offsets buffer_offsets; @@ -521,9 +536,12 @@ static int init_bmi_rx(struct fman_port *port) /* NIA */ tmp = (u32)cfg->rx_fd_bits << BMI_NEXT_ENG_FD_BITS_SHIFT; - tmp |= NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME; + tmp |= NIA_ENG_HWP; iowrite32be(tmp, ®s->fmbm_rfne); + /* Parser Next Engine NIA */ + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME, ®s->fmbm_rfpne); + /* Enqueue NIA */ iowrite32be(NIA_ENG_QMI_ENQ | NIA_ORDER_RESTOR, ®s->fmbm_rfene); @@ -665,6 +683,50 @@ static int init_qmi(struct fman_port *port) return 0; } +static void stop_port_hwp(struct fman_port *port) +{ + struct fman_port_hwp_regs __iomem *regs = port->hwp_regs; + int cnt = 100; + + iowrite32be(HWP_HXS_PCAC_PSTOP, ®s->fmpr_pcac); + + while (cnt-- > 0 && + (ioread32be(®s->fmpr_pcac) & HWP_HXS_PCAC_PSTAT)) + udelay(10); + if (!cnt) + pr_err("Timeout stopping HW Parser\n"); +} + +static void start_port_hwp(struct fman_port *port) +{ + struct fman_port_hwp_regs __iomem *regs = port->hwp_regs; + int cnt = 100; + + iowrite32be(0, ®s->fmpr_pcac); + + while (cnt-- > 0 && + !(ioread32be(®s->fmpr_pcac) & HWP_HXS_PCAC_PSTAT)) + udelay(10); + if (!cnt) + pr_err("Timeout starting HW Parser\n"); +} + +static void init_hwp(struct fman_port *port) +{ + struct fman_port_hwp_regs __iomem *regs = port->hwp_regs; + int i; + + stop_port_hwp(port); + + for (i = 0; i < HWP_HXS_COUNT; i++) { + /* enable HXS error reporting into FD[STATUS] PHE */ + iowrite32be(0x00000000, ®s->pmda[i].ssa); + iowrite32be(0xffffffff, ®s->pmda[i].lcv); + } + + start_port_hwp(port); +} + static int init(struct fman_port *port) { int err; @@ -673,6 +735,8 @@ static int init(struct fman_port *port) switch (port->port_type) { case FMAN_PORT_TYPE_RX: err = init_bmi_rx(port); + if (!err) + init_hwp(port); break; case FMAN_PORT_TYPE_TX: err = init_bmi_tx(port); @@ -686,7 +750,8 @@ static int init(struct fman_port *port) /* Init QMI registers */ err = init_qmi(port); - return err; + if (err) + return err; return 0; } @@ -1276,6 +1341,7 @@ int fman_port_config(struct fman_port *port, struct fman_port_params *params) /* set memory map pointers */ port->bmi_regs = base_addr + BMI_PORT_REGS_OFFSET; port->qmi_regs = base_addr + QMI_PORT_REGS_OFFSET; + port->hwp_regs = base_addr + HWP_PORT_REGS_OFFSET; port->max_frame_length = DFLT_PORT_MAX_FRAME_LENGTH; /* resource distribution. */ -- cgit From 226327b236c97c860fe644a409fc536da53d5a98 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Mon, 13 Feb 2017 17:10:46 +0200 Subject: fsl/fman: remove wrong free Reported-by: Dan Carpenter Signed-off-by: Madalin Bucur --- drivers/net/ethernet/freescale/fman/fman_port.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c index f314348b3387..57bf44fa16a1 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.c +++ b/drivers/net/ethernet/freescale/fman/fman_port.c @@ -1312,7 +1312,7 @@ int fman_port_config(struct fman_port *port, struct fman_port_params *params) /* Allocate the FM driver's parameters structure */ port->cfg = kzalloc(sizeof(*port->cfg), GFP_KERNEL); if (!port->cfg) - goto err_params; + return -EINVAL; /* Initialize FM port parameters which will be kept by the driver */ port->port_type = port->dts_params.type; @@ -1393,8 +1393,6 @@ int fman_port_config(struct fman_port *port, struct fman_port_params *params) err_port_cfg: kfree(port->cfg); -err_params: - kfree(port); return -EINVAL; } EXPORT_SYMBOL(fman_port_config); -- cgit From de8b1e41a251577bdd6eb44a7702473fb22ae497 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Tue, 14 Feb 2017 15:09:07 +0200 Subject: fsl/fman: enlarge FIFO to allow for the 5th port Signed-off-by: Madalin Bucur --- drivers/net/ethernet/freescale/fman/fman.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index d7559306226d..4aefe2438969 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -1212,7 +1212,7 @@ static int fill_soc_specific_params(struct fman_state_struct *state) state->max_num_of_open_dmas = 32; state->fm_port_num_of_cg = 256; state->num_of_rx_ports = 6; - state->total_fifo_size = 122 * 1024; + state->total_fifo_size = 136 * 1024; break; case 2: -- cgit From 4529da5b7feab8f9bef585b5a56c7e33bef66e19 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Thu, 12 Jan 2017 09:54:28 +0200 Subject: dpaa_eth: remove redundant initialization Signed-off-by: Madalin Bucur --- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index e2ca107f9d94..e19181f096bc 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -2093,7 +2093,7 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal, dma_addr_t addr = qm_fd_addr(fd); enum qm_fd_format fd_format; struct net_device *net_dev; - u32 fd_status = fd->status; + u32 fd_status; struct dpaa_bp *dpaa_bp; struct dpaa_priv *priv; unsigned int skb_len; -- cgit From 5accb28241e027c9d816b348a870ee3f27c499ff Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Thu, 26 Jan 2017 14:34:02 +0200 Subject: dpaa_eth: enable Rx checksum offload Use the FMan HW parser L4CV flag to offload Rx checksumming. Signed-off-by: Madalin Bucur --- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 29 ++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index e19181f096bc..a7a595c1c07d 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -137,6 +137,13 @@ MODULE_PARM_DESC(tx_timeout, "The Tx timeout in ms"); /* L4 Type field: TCP */ #define FM_L4_PARSE_RESULT_TCP 0x20 +/* FD status field indicating whether the FM Parser has attempted to validate + * the L4 csum of the frame. + * Note that having this bit set doesn't necessarily imply that the checksum + * is valid. One would have to check the parse results to find that out. + */ +#define FM_FD_STAT_L4CV 0x00000004 + #define DPAA_SGT_MAX_ENTRIES 16 /* maximum number of entries in SG Table */ #define DPAA_BUFF_RELEASE_MAX 8 /* maximum number of buffers released at once */ @@ -235,6 +242,7 @@ static int dpaa_netdev_init(struct net_device *net_dev, * For conformity, we'll still declare GSO explicitly. */ net_dev->features |= NETIF_F_GSO; + net_dev->features |= NETIF_F_RXCSUM; net_dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; /* we do not want shared skbs on TX */ @@ -1526,6 +1534,23 @@ static struct sk_buff *dpaa_cleanup_tx_fd(const struct dpaa_priv *priv, return skb; } +static u8 rx_csum_offload(const struct dpaa_priv *priv, const struct qm_fd *fd) +{ + /* The parser has run and performed L4 checksum validation. + * We know there were no parser errors (and implicitly no + * L4 csum error), otherwise we wouldn't be here. + */ + if ((priv->net_dev->features & NETIF_F_RXCSUM) && + (be32_to_cpu(fd->status) & FM_FD_STAT_L4CV)) + return CHECKSUM_UNNECESSARY; + + /* We're here because either the parser didn't run or the L4 checksum + * was not verified. This may include the case of a UDP frame with + * checksum zero or an L4 proto other than TCP/UDP + */ + return CHECKSUM_NONE; +} + /* Build a linear skb around the received buffer. * We are guaranteed there is enough room at the end of the data buffer to * accommodate the shared info area of the skb. @@ -1556,7 +1581,7 @@ static struct sk_buff *contig_fd_to_skb(const struct dpaa_priv *priv, skb_reserve(skb, fd_off); skb_put(skb, qm_fd_get_length(fd)); - skb->ip_summed = CHECKSUM_NONE; + skb->ip_summed = rx_csum_offload(priv, fd); return skb; @@ -1616,7 +1641,7 @@ static struct sk_buff *sg_fd_to_skb(const struct dpaa_priv *priv, if (WARN_ON(unlikely(!skb))) goto free_buffers; - skb->ip_summed = CHECKSUM_NONE; + skb->ip_summed = rx_csum_offload(priv, fd); /* Make sure forwarded skbs will have enough space * on Tx, if extra headers are added. -- cgit From 7f8a6a1b8fa4915548e2e426e9780e7c217c3e0e Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Mon, 13 Feb 2017 17:20:01 +0200 Subject: dpaa_eth: do not ignore port api return value Reported-by: Dan Carpenter Signed-off-by: Madalin Bucur --- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 65 +++++++++++++++++--------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index a7a595c1c07d..ae64cdb7a28c 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -1063,9 +1063,9 @@ static int dpaa_fq_free(struct device *dev, struct list_head *list) return err; } -static void dpaa_eth_init_tx_port(struct fman_port *port, struct dpaa_fq *errq, - struct dpaa_fq *defq, - struct dpaa_buffer_layout *buf_layout) +static int dpaa_eth_init_tx_port(struct fman_port *port, struct dpaa_fq *errq, + struct dpaa_fq *defq, + struct dpaa_buffer_layout *buf_layout) { struct fman_buffer_prefix_content buf_prefix_content; struct fman_port_params params; @@ -1084,23 +1084,29 @@ static void dpaa_eth_init_tx_port(struct fman_port *port, struct dpaa_fq *errq, params.specific_params.non_rx_params.dflt_fqid = defq->fqid; err = fman_port_config(port, ¶ms); - if (err) + if (err) { pr_err("%s: fman_port_config failed\n", __func__); + return err; + } err = fman_port_cfg_buf_prefix_content(port, &buf_prefix_content); - if (err) + if (err) { pr_err("%s: fman_port_cfg_buf_prefix_content failed\n", __func__); + return err; + } err = fman_port_init(port); if (err) pr_err("%s: fm_port_init failed\n", __func__); + + return err; } -static void dpaa_eth_init_rx_port(struct fman_port *port, struct dpaa_bp **bps, - size_t count, struct dpaa_fq *errq, - struct dpaa_fq *defq, - struct dpaa_buffer_layout *buf_layout) +static int dpaa_eth_init_rx_port(struct fman_port *port, struct dpaa_bp **bps, + size_t count, struct dpaa_fq *errq, + struct dpaa_fq *defq, + struct dpaa_buffer_layout *buf_layout) { struct fman_buffer_prefix_content buf_prefix_content; struct fman_port_rx_params *rx_p; @@ -1128,32 +1134,44 @@ static void dpaa_eth_init_rx_port(struct fman_port *port, struct dpaa_bp **bps, } err = fman_port_config(port, ¶ms); - if (err) + if (err) { pr_err("%s: fman_port_config failed\n", __func__); + return err; + } err = fman_port_cfg_buf_prefix_content(port, &buf_prefix_content); - if (err) + if (err) { pr_err("%s: fman_port_cfg_buf_prefix_content failed\n", __func__); + return err; + } err = fman_port_init(port); if (err) pr_err("%s: fm_port_init failed\n", __func__); + + return err; } -static void dpaa_eth_init_ports(struct mac_device *mac_dev, - struct dpaa_bp **bps, size_t count, - struct fm_port_fqs *port_fqs, - struct dpaa_buffer_layout *buf_layout, - struct device *dev) +static int dpaa_eth_init_ports(struct mac_device *mac_dev, + struct dpaa_bp **bps, size_t count, + struct fm_port_fqs *port_fqs, + struct dpaa_buffer_layout *buf_layout, + struct device *dev) { struct fman_port *rxport = mac_dev->port[RX]; struct fman_port *txport = mac_dev->port[TX]; + int err; - dpaa_eth_init_tx_port(txport, port_fqs->tx_errq, - port_fqs->tx_defq, &buf_layout[TX]); - dpaa_eth_init_rx_port(rxport, bps, count, port_fqs->rx_errq, - port_fqs->rx_defq, &buf_layout[RX]); + err = dpaa_eth_init_tx_port(txport, port_fqs->tx_errq, + port_fqs->tx_defq, &buf_layout[TX]); + if (err) + return err; + + err = dpaa_eth_init_rx_port(rxport, bps, count, port_fqs->rx_errq, + port_fqs->rx_defq, &buf_layout[RX]); + + return err; } static int dpaa_bman_release(const struct dpaa_bp *dpaa_bp, @@ -2649,8 +2667,10 @@ static int dpaa_eth_probe(struct platform_device *pdev) priv->rx_headroom = dpaa_get_headroom(&priv->buf_layout[RX]); /* All real interfaces need their ports initialized */ - dpaa_eth_init_ports(mac_dev, dpaa_bps, DPAA_BPS_NUM, &port_fqs, - &priv->buf_layout[0], dev); + err = dpaa_eth_init_ports(mac_dev, dpaa_bps, DPAA_BPS_NUM, &port_fqs, + &priv->buf_layout[0], dev); + if (err) + goto init_ports_failed; priv->percpu_priv = devm_alloc_percpu(dev, *priv->percpu_priv); if (!priv->percpu_priv) { @@ -2683,6 +2703,7 @@ netdev_init_failed: napi_add_failed: dpaa_napi_del(net_dev); alloc_percpu_failed: +init_ports_failed: dpaa_fq_free(dev, &priv->dpaa_fq_list); fq_alloc_failed: qman_delete_cgr_safe(&priv->ingress_cgr); -- cgit From c44efa1d75e4c0a720fd39d7095a0bd6b306576e Mon Sep 17 00:00:00 2001 From: Camelia Groza Date: Mon, 25 Jul 2016 16:38:21 +0300 Subject: dpaa_eth: add four prioritised Tx traffic classes Each traffic class corresponds to a WQ priority level. The number of Tx netdev queues and frame queues is increased to NR_CPUS queues for each traffic class. In addition, the priority of the Rx, Error and Conf queues is lowered but their order is maintained. By default, only one traffic class is enabled, only the low priority Tx queues are used and only the corresponding netdev queues are advertised. Signed-off-by: Camelia Groza Signed-off-by: Madalin Bucur --- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 43 +++++++++++++++++++++----- drivers/net/ethernet/freescale/dpaa/dpaa_eth.h | 8 ++++- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index ae64cdb7a28c..ac75d09c643e 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -565,16 +565,18 @@ static void dpaa_bps_free(struct dpaa_priv *priv) /* Use multiple WQs for FQ assignment: * - Tx Confirmation queues go to WQ1. - * - Rx Error and Tx Error queues go to WQ2 (giving them a better chance - * to be scheduled, in case there are many more FQs in WQ3). - * - Rx Default and Tx queues go to WQ3 (no differentiation between - * Rx and Tx traffic). + * - Rx Error and Tx Error queues go to WQ5 (giving them a better chance + * to be scheduled, in case there are many more FQs in WQ6). + * - Rx Default goes to WQ6. + * - Tx queues go to different WQs depending on their priority. Equal + * chunks of NR_CPUS queues go to WQ6 (lowest priority), WQ2, WQ1 and + * WQ0 (highest priority). * This ensures that Tx-confirmed buffers are timely released. In particular, * it avoids congestion on the Tx Confirm FQs, which can pile up PFDRs if they * are greatly outnumbered by other FQs in the system, while * dequeue scheduling is round-robin. */ -static inline void dpaa_assign_wq(struct dpaa_fq *fq) +static inline void dpaa_assign_wq(struct dpaa_fq *fq, int idx) { switch (fq->fq_type) { case FQ_TYPE_TX_CONFIRM: @@ -583,11 +585,33 @@ static inline void dpaa_assign_wq(struct dpaa_fq *fq) break; case FQ_TYPE_RX_ERROR: case FQ_TYPE_TX_ERROR: - fq->wq = 2; + fq->wq = 5; break; case FQ_TYPE_RX_DEFAULT: + fq->wq = 6; + break; case FQ_TYPE_TX: - fq->wq = 3; + switch (idx / DPAA_TC_TXQ_NUM) { + case 0: + /* Low priority (best effort) */ + fq->wq = 6; + break; + case 1: + /* Medium priority */ + fq->wq = 2; + break; + case 2: + /* High priority */ + fq->wq = 1; + break; + case 3: + /* Very high priority */ + fq->wq = 0; + break; + default: + WARN(1, "Too many TX FQs: more than %d!\n", + DPAA_ETH_TXQ_NUM); + } break; default: WARN(1, "Invalid FQ type %d for FQID %d!\n", @@ -615,7 +639,7 @@ static struct dpaa_fq *dpaa_fq_alloc(struct device *dev, } for (i = 0; i < count; i++) - dpaa_assign_wq(dpaa_fq + i); + dpaa_assign_wq(dpaa_fq + i, i); return dpaa_fq; } @@ -2683,6 +2707,9 @@ static int dpaa_eth_probe(struct platform_device *pdev) memset(percpu_priv, 0, sizeof(*percpu_priv)); } + priv->num_tc = 1; + netif_set_real_num_tx_queues(net_dev, priv->num_tc * DPAA_TC_TXQ_NUM); + /* Initialize NAPI */ err = dpaa_napi_add(net_dev); if (err < 0) diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h index 1f9aebf3f3c5..9941a7866ebe 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h @@ -39,7 +39,12 @@ #include "mac.h" #include "dpaa_eth_trace.h" -#define DPAA_ETH_TXQ_NUM NR_CPUS +/* Number of prioritised traffic classes */ +#define DPAA_TC_NUM 4 +/* Number of Tx queues per traffic class */ +#define DPAA_TC_TXQ_NUM NR_CPUS +/* Total number of Tx queues */ +#define DPAA_ETH_TXQ_NUM (DPAA_TC_NUM * DPAA_TC_TXQ_NUM) #define DPAA_BPS_NUM 3 /* number of bpools per interface */ @@ -152,6 +157,7 @@ struct dpaa_priv { u16 channel; struct list_head dpaa_fq_list; + u8 num_tc; u32 msg_enable; /* net_device message level */ struct { -- cgit From 2ea08f8261b1469c728204aaacfca1d046eb0bff Mon Sep 17 00:00:00 2001 From: Camelia Groza Date: Mon, 25 Jul 2016 16:54:56 +0300 Subject: dpaa_eth: enable multiple Tx traffic classes Implement the setup_tc ndo to configure prioritised Tx traffic classes. Priorities range from 0 (lowest) to 3 (highest). The driver assigns NR_CPUS queues to each traffic class. Signed-off-by: Camelia Groza Signed-off-by: Madalin Bucur --- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index ac75d09c643e..1b3ea38d014f 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -342,6 +342,41 @@ static void dpaa_get_stats64(struct net_device *net_dev, } } +static int dpaa_setup_tc(struct net_device *net_dev, u32 handle, __be16 proto, + struct tc_to_netdev *tc) +{ + struct dpaa_priv *priv = netdev_priv(net_dev); + int i; + + if (tc->type != TC_SETUP_MQPRIO) + return -EINVAL; + + if (tc->tc == priv->num_tc) + return 0; + + if (!tc->tc) { + netdev_reset_tc(net_dev); + goto out; + } + + if (tc->tc > DPAA_TC_NUM) { + netdev_err(net_dev, "Too many traffic classes: max %d supported.\n", + DPAA_TC_NUM); + return -EINVAL; + } + + netdev_set_num_tc(net_dev, tc->tc); + + for (i = 0; i < tc->tc; i++) + netdev_set_tc_queue(net_dev, i, DPAA_TC_TXQ_NUM, + i * DPAA_TC_TXQ_NUM); + +out: + priv->num_tc = tc->tc ? tc->tc : 1; + netif_set_real_num_tx_queues(net_dev, priv->num_tc * DPAA_TC_TXQ_NUM); + return 0; +} + static struct mac_device *dpaa_mac_dev_get(struct platform_device *pdev) { struct platform_device *of_dev; @@ -2417,6 +2452,7 @@ static const struct net_device_ops dpaa_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = dpaa_set_rx_mode, .ndo_do_ioctl = dpaa_ioctl, + .ndo_setup_tc = dpaa_setup_tc, }; static int dpaa_napi_add(struct net_device *net_dev) -- cgit From 7fe1e290bfca75061d77ad2a67b12c23d6d35e7b Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Tue, 14 Feb 2017 17:17:28 +0200 Subject: dpaa_eth: enable context-A stashing Signed-off-by: Madalin Bucur --- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 1b3ea38d014f..aa769cbc7425 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -1052,7 +1052,8 @@ static int dpaa_fq_init(struct dpaa_fq *dpaa_fq, bool td_enable) /* Initialization common to all ingress queues */ if (dpaa_fq->flags & QMAN_FQ_FLAG_NO_ENQUEUE) { initfq.we_mask |= cpu_to_be16(QM_INITFQ_WE_CONTEXTA); - initfq.fqd.fq_ctrl |= cpu_to_be16(QM_FQCTRL_HOLDACTIVE); + initfq.fqd.fq_ctrl |= cpu_to_be16(QM_FQCTRL_HOLDACTIVE | + QM_FQCTRL_CTXASTASHING); initfq.fqd.context_a.stashing.exclusive = QM_STASHING_EXCL_DATA | QM_STASHING_EXCL_CTX | QM_STASHING_EXCL_ANNOTATION; -- cgit From 9bf881ffc5c0e65343fb51eef10dd989b36d1c1f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 6 Mar 2017 16:39:51 +0100 Subject: flow_dissector: Move ARP dissection into a separate function Make the main flow_dissect function a bit smaller and move the ARP dissection into a separate function. Along with that, do the ARP header processing only in case the flow dissection user requires it. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 120 ++++++++++++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 53 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index c35aae13c8d2..d79fb8f8f033 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -113,6 +113,66 @@ __be32 __skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto, } EXPORT_SYMBOL(__skb_flow_get_ports); +enum flow_dissect_ret { + FLOW_DISSECT_RET_OUT_GOOD, + FLOW_DISSECT_RET_OUT_BAD, +}; + +static enum flow_dissect_ret +__skb_flow_dissect_arp(const struct sk_buff *skb, + struct flow_dissector *flow_dissector, + void *target_container, void *data, int nhoff, int hlen) +{ + struct flow_dissector_key_arp *key_arp; + struct { + unsigned char ar_sha[ETH_ALEN]; + unsigned char ar_sip[4]; + unsigned char ar_tha[ETH_ALEN]; + unsigned char ar_tip[4]; + } *arp_eth, _arp_eth; + const struct arphdr *arp; + struct arphdr *_arp; + + if (!dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_ARP)) + return FLOW_DISSECT_RET_OUT_GOOD; + + arp = __skb_header_pointer(skb, nhoff, sizeof(_arp), data, + hlen, &_arp); + if (!arp) + return FLOW_DISSECT_RET_OUT_BAD; + + if (arp->ar_hrd != htons(ARPHRD_ETHER) || + arp->ar_pro != htons(ETH_P_IP) || + arp->ar_hln != ETH_ALEN || + arp->ar_pln != 4 || + (arp->ar_op != htons(ARPOP_REPLY) && + arp->ar_op != htons(ARPOP_REQUEST))) + return FLOW_DISSECT_RET_OUT_BAD; + + arp_eth = __skb_header_pointer(skb, nhoff + sizeof(_arp), + sizeof(_arp_eth), data, + hlen, &_arp_eth); + if (!arp_eth) + return FLOW_DISSECT_RET_OUT_BAD; + + key_arp = skb_flow_dissector_target(flow_dissector, + FLOW_DISSECTOR_KEY_ARP, + target_container); + + memcpy(&key_arp->sip, arp_eth->ar_sip, sizeof(key_arp->sip)); + memcpy(&key_arp->tip, arp_eth->ar_tip, sizeof(key_arp->tip)); + + /* Only store the lower byte of the opcode; + * this covers ARPOP_REPLY and ARPOP_REQUEST. + */ + key_arp->op = ntohs(arp->ar_op) & 0xff; + + ether_addr_copy(key_arp->sha, arp_eth->ar_sha); + ether_addr_copy(key_arp->tha, arp_eth->ar_tha); + + return FLOW_DISSECT_RET_OUT_GOOD; +} + /** * __skb_flow_dissect - extract the flow_keys struct and return it * @skb: sk_buff to extract the flow from, can be NULL if the rest are specified @@ -138,7 +198,6 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_dissector_key_control *key_control; struct flow_dissector_key_basic *key_basic; struct flow_dissector_key_addrs *key_addrs; - struct flow_dissector_key_arp *key_arp; struct flow_dissector_key_ports *key_ports; struct flow_dissector_key_icmp *key_icmp; struct flow_dissector_key_tags *key_tags; @@ -382,60 +441,15 @@ mpls: goto out_good; case htons(ETH_P_ARP): - case htons(ETH_P_RARP): { - struct { - unsigned char ar_sha[ETH_ALEN]; - unsigned char ar_sip[4]; - unsigned char ar_tha[ETH_ALEN]; - unsigned char ar_tip[4]; - } *arp_eth, _arp_eth; - const struct arphdr *arp; - struct arphdr *_arp; - - arp = __skb_header_pointer(skb, nhoff, sizeof(_arp), data, - hlen, &_arp); - if (!arp) - goto out_bad; - - if (arp->ar_hrd != htons(ARPHRD_ETHER) || - arp->ar_pro != htons(ETH_P_IP) || - arp->ar_hln != ETH_ALEN || - arp->ar_pln != 4 || - (arp->ar_op != htons(ARPOP_REPLY) && - arp->ar_op != htons(ARPOP_REQUEST))) - goto out_bad; - - arp_eth = __skb_header_pointer(skb, nhoff + sizeof(_arp), - sizeof(_arp_eth), data, - hlen, - &_arp_eth); - if (!arp_eth) + case htons(ETH_P_RARP): + switch (__skb_flow_dissect_arp(skb, flow_dissector, + target_container, data, + nhoff, hlen)) { + case FLOW_DISSECT_RET_OUT_GOOD: + goto out_good; + case FLOW_DISSECT_RET_OUT_BAD: goto out_bad; - - if (dissector_uses_key(flow_dissector, - FLOW_DISSECTOR_KEY_ARP)) { - - key_arp = skb_flow_dissector_target(flow_dissector, - FLOW_DISSECTOR_KEY_ARP, - target_container); - - memcpy(&key_arp->sip, arp_eth->ar_sip, - sizeof(key_arp->sip)); - memcpy(&key_arp->tip, arp_eth->ar_tip, - sizeof(key_arp->tip)); - - /* Only store the lower byte of the opcode; - * this covers ARPOP_REPLY and ARPOP_REQUEST. - */ - key_arp->op = ntohs(arp->ar_op) & 0xff; - - ether_addr_copy(key_arp->sha, arp_eth->ar_sha); - ether_addr_copy(key_arp->tha, arp_eth->ar_tha); } - - goto out_good; - } - default: goto out_bad; } -- cgit From 4a5d6c8b14b81e3704607a354434321e390d228a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 6 Mar 2017 16:39:52 +0100 Subject: flow_dissector: Move MPLS dissection into a separate function Make the main flow_dissect function a bit smaller and move the MPLS dissection into a separate function. Along with that, do the MPLS header processing only in case the flow dissection user requires it. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 56 ++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index d79fb8f8f033..8d012987e3c3 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -118,6 +118,33 @@ enum flow_dissect_ret { FLOW_DISSECT_RET_OUT_BAD, }; +static enum flow_dissect_ret +__skb_flow_dissect_mpls(const struct sk_buff *skb, + struct flow_dissector *flow_dissector, + void *target_container, void *data, int nhoff, int hlen) +{ + struct flow_dissector_key_keyid *key_keyid; + struct mpls_label *hdr, _hdr[2]; + + if (!dissector_uses_key(flow_dissector, + FLOW_DISSECTOR_KEY_MPLS_ENTROPY)) + return FLOW_DISSECT_RET_OUT_GOOD; + + hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, + hlen, &_hdr); + if (!hdr) + return FLOW_DISSECT_RET_OUT_BAD; + + if ((ntohl(hdr[0].entry) & MPLS_LS_LABEL_MASK) >> + MPLS_LS_LABEL_SHIFT == MPLS_LABEL_ENTROPY) { + key_keyid = skb_flow_dissector_target(flow_dissector, + FLOW_DISSECTOR_KEY_MPLS_ENTROPY, + target_container); + key_keyid->keyid = hdr[1].entry & htonl(MPLS_LS_LABEL_MASK); + } + return FLOW_DISSECT_RET_OUT_GOOD; +} + static enum flow_dissect_ret __skb_flow_dissect_arp(const struct sk_buff *skb, struct flow_dissector *flow_dissector, @@ -408,31 +435,16 @@ ipv6: } case htons(ETH_P_MPLS_UC): - case htons(ETH_P_MPLS_MC): { - struct mpls_label *hdr, _hdr[2]; + case htons(ETH_P_MPLS_MC): mpls: - hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, - hlen, &_hdr); - if (!hdr) - goto out_bad; - - if ((ntohl(hdr[0].entry) & MPLS_LS_LABEL_MASK) >> - MPLS_LS_LABEL_SHIFT == MPLS_LABEL_ENTROPY) { - if (dissector_uses_key(flow_dissector, - FLOW_DISSECTOR_KEY_MPLS_ENTROPY)) { - key_keyid = skb_flow_dissector_target(flow_dissector, - FLOW_DISSECTOR_KEY_MPLS_ENTROPY, - target_container); - key_keyid->keyid = hdr[1].entry & - htonl(MPLS_LS_LABEL_MASK); - } - + switch (__skb_flow_dissect_mpls(skb, flow_dissector, + target_container, data, + nhoff, hlen)) { + case FLOW_DISSECT_RET_OUT_GOOD: goto out_good; + case FLOW_DISSECT_RET_OUT_BAD: + goto out_bad; } - - goto out_good; - } - case htons(ETH_P_FCOE): if ((hlen - nhoff) < FCOE_HEADER_LEN) goto out_bad; -- cgit From d5774b93f04252b81bd2c2cc84ec663c6aa798d1 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 6 Mar 2017 16:39:53 +0100 Subject: flow_dissector: Fix GRE header error path Now, when an unexpected element in the GRE header appears, we break so the l4 ports are processed. But since the ports are processed unconditionally, there will be certainly random values dissected. Fix this by just bailing out in such situations. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 8d012987e3c3..cefaf2368a3f 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -479,18 +479,18 @@ ip_proto_again: /* Only look inside GRE without routing */ if (hdr->flags & GRE_ROUTING) - break; + goto out_good; /* Only look inside GRE for version 0 and 1 */ gre_ver = ntohs(hdr->flags & GRE_VERSION); if (gre_ver > 1) - break; + goto out_good; proto = hdr->protocol; if (gre_ver) { /* Version1 must be PPTP, and check the flags */ if (!(proto == GRE_PROTO_PPP && (hdr->flags & GRE_KEY))) - break; + goto out_good; } offset += sizeof(struct gre_base_hdr); -- cgit From c5ef188e9318694a073ceacb26011f62d7ed9b3f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 6 Mar 2017 16:39:54 +0100 Subject: flow_dissector: rename "proto again" goto label Align with "ip_proto_again" label used in the same function and rename vague "again" to "proto_again". Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index cefaf2368a3f..912083576594 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -267,7 +267,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb, memcpy(key_eth_addrs, ð->h_dest, sizeof(*key_eth_addrs)); } -again: +proto_again: switch (proto) { case htons(ETH_P_IP): { const struct iphdr *iph; @@ -370,7 +370,7 @@ ipv6: proto = vlan->h_vlan_encapsulated_proto; nhoff += sizeof(*vlan); if (skip_vlan) - goto again; + goto proto_again; } skip_vlan = true; @@ -393,7 +393,7 @@ ipv6: } } - goto again; + goto proto_again; } case htons(ETH_P_PPP_SES): { struct { @@ -577,7 +577,7 @@ ip_proto_again: if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP) goto out_good; - goto again; + goto proto_again; } case NEXTHDR_HOP: case NEXTHDR_ROUTING: -- cgit From 7c92de8eaabfff42f6f57466c12f255cbd718f58 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 6 Mar 2017 16:39:55 +0100 Subject: flow_dissector: Move GRE dissection into a separate function Make the main flow_dissect function a bit smaller and move the GRE dissection into a separate function. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 244 +++++++++++++++++++++++++--------------------- 1 file changed, 134 insertions(+), 110 deletions(-) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 912083576594..5f3ae922fcd1 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -116,6 +116,7 @@ EXPORT_SYMBOL(__skb_flow_get_ports); enum flow_dissect_ret { FLOW_DISSECT_RET_OUT_GOOD, FLOW_DISSECT_RET_OUT_BAD, + FLOW_DISSECT_RET_OUT_PROTO_AGAIN, }; static enum flow_dissect_ret @@ -200,6 +201,128 @@ __skb_flow_dissect_arp(const struct sk_buff *skb, return FLOW_DISSECT_RET_OUT_GOOD; } +static enum flow_dissect_ret +__skb_flow_dissect_gre(const struct sk_buff *skb, + struct flow_dissector_key_control *key_control, + struct flow_dissector *flow_dissector, + void *target_container, void *data, + __be16 *p_proto, int *p_nhoff, int *p_hlen, + unsigned int flags) +{ + struct flow_dissector_key_keyid *key_keyid; + struct gre_base_hdr *hdr, _hdr; + int offset = 0; + u16 gre_ver; + + hdr = __skb_header_pointer(skb, *p_nhoff, sizeof(_hdr), + data, *p_hlen, &_hdr); + if (!hdr) + return FLOW_DISSECT_RET_OUT_BAD; + + /* Only look inside GRE without routing */ + if (hdr->flags & GRE_ROUTING) + return FLOW_DISSECT_RET_OUT_GOOD; + + /* Only look inside GRE for version 0 and 1 */ + gre_ver = ntohs(hdr->flags & GRE_VERSION); + if (gre_ver > 1) + return FLOW_DISSECT_RET_OUT_GOOD; + + *p_proto = hdr->protocol; + if (gre_ver) { + /* Version1 must be PPTP, and check the flags */ + if (!(*p_proto == GRE_PROTO_PPP && (hdr->flags & GRE_KEY))) + return FLOW_DISSECT_RET_OUT_GOOD; + } + + offset += sizeof(struct gre_base_hdr); + + if (hdr->flags & GRE_CSUM) + offset += sizeof(((struct gre_full_hdr *) 0)->csum) + + sizeof(((struct gre_full_hdr *) 0)->reserved1); + + if (hdr->flags & GRE_KEY) { + const __be32 *keyid; + __be32 _keyid; + + keyid = __skb_header_pointer(skb, *p_nhoff + offset, + sizeof(_keyid), + data, *p_hlen, &_keyid); + if (!keyid) + return FLOW_DISSECT_RET_OUT_BAD; + + if (dissector_uses_key(flow_dissector, + FLOW_DISSECTOR_KEY_GRE_KEYID)) { + key_keyid = skb_flow_dissector_target(flow_dissector, + FLOW_DISSECTOR_KEY_GRE_KEYID, + target_container); + if (gre_ver == 0) + key_keyid->keyid = *keyid; + else + key_keyid->keyid = *keyid & GRE_PPTP_KEY_MASK; + } + offset += sizeof(((struct gre_full_hdr *) 0)->key); + } + + if (hdr->flags & GRE_SEQ) + offset += sizeof(((struct pptp_gre_header *) 0)->seq); + + if (gre_ver == 0) { + if (*p_proto == htons(ETH_P_TEB)) { + const struct ethhdr *eth; + struct ethhdr _eth; + + eth = __skb_header_pointer(skb, *p_nhoff + offset, + sizeof(_eth), + data, *p_hlen, &_eth); + if (!eth) + return FLOW_DISSECT_RET_OUT_BAD; + *p_proto = eth->h_proto; + offset += sizeof(*eth); + + /* Cap headers that we access via pointers at the + * end of the Ethernet header as our maximum alignment + * at that point is only 2 bytes. + */ + if (NET_IP_ALIGN) + *p_hlen = *p_nhoff + offset; + } + } else { /* version 1, must be PPTP */ + u8 _ppp_hdr[PPP_HDRLEN]; + u8 *ppp_hdr; + + if (hdr->flags & GRE_ACK) + offset += sizeof(((struct pptp_gre_header *) 0)->ack); + + ppp_hdr = __skb_header_pointer(skb, *p_nhoff + offset, + sizeof(_ppp_hdr), + data, *p_hlen, _ppp_hdr); + if (!ppp_hdr) + return FLOW_DISSECT_RET_OUT_BAD; + + switch (PPP_PROTOCOL(ppp_hdr)) { + case PPP_IP: + *p_proto = htons(ETH_P_IP); + break; + case PPP_IPV6: + *p_proto = htons(ETH_P_IPV6); + break; + default: + /* Could probably catch some more like MPLS */ + break; + } + + offset += PPP_HDRLEN; + } + + *p_nhoff += offset; + key_control->flags |= FLOW_DIS_ENCAPSULATION; + if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP) + return FLOW_DISSECT_RET_OUT_GOOD; + + return FLOW_DISSECT_RET_OUT_PROTO_AGAIN; +} + /** * __skb_flow_dissect - extract the flow_keys struct and return it * @skb: sk_buff to extract the flow from, can be NULL if the rest are specified @@ -229,7 +352,6 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_dissector_key_icmp *key_icmp; struct flow_dissector_key_tags *key_tags; struct flow_dissector_key_vlan *key_vlan; - struct flow_dissector_key_keyid *key_keyid; bool skip_vlan = false; u8 ip_proto = 0; bool ret; @@ -443,6 +565,7 @@ mpls: case FLOW_DISSECT_RET_OUT_GOOD: goto out_good; case FLOW_DISSECT_RET_OUT_BAD: + default: goto out_bad; } case htons(ETH_P_FCOE): @@ -460,6 +583,7 @@ mpls: case FLOW_DISSECT_RET_OUT_GOOD: goto out_good; case FLOW_DISSECT_RET_OUT_BAD: + default: goto out_bad; } default: @@ -468,117 +592,17 @@ mpls: ip_proto_again: switch (ip_proto) { - case IPPROTO_GRE: { - struct gre_base_hdr *hdr, _hdr; - u16 gre_ver; - int offset = 0; - - hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, hlen, &_hdr); - if (!hdr) - goto out_bad; - - /* Only look inside GRE without routing */ - if (hdr->flags & GRE_ROUTING) - goto out_good; - - /* Only look inside GRE for version 0 and 1 */ - gre_ver = ntohs(hdr->flags & GRE_VERSION); - if (gre_ver > 1) + case IPPROTO_GRE: + switch (__skb_flow_dissect_gre(skb, key_control, flow_dissector, + target_container, data, + &proto, &nhoff, &hlen, flags)) { + case FLOW_DISSECT_RET_OUT_GOOD: goto out_good; - - proto = hdr->protocol; - if (gre_ver) { - /* Version1 must be PPTP, and check the flags */ - if (!(proto == GRE_PROTO_PPP && (hdr->flags & GRE_KEY))) - goto out_good; - } - - offset += sizeof(struct gre_base_hdr); - - if (hdr->flags & GRE_CSUM) - offset += sizeof(((struct gre_full_hdr *)0)->csum) + - sizeof(((struct gre_full_hdr *)0)->reserved1); - - if (hdr->flags & GRE_KEY) { - const __be32 *keyid; - __be32 _keyid; - - keyid = __skb_header_pointer(skb, nhoff + offset, sizeof(_keyid), - data, hlen, &_keyid); - if (!keyid) - goto out_bad; - - if (dissector_uses_key(flow_dissector, - FLOW_DISSECTOR_KEY_GRE_KEYID)) { - key_keyid = skb_flow_dissector_target(flow_dissector, - FLOW_DISSECTOR_KEY_GRE_KEYID, - target_container); - if (gre_ver == 0) - key_keyid->keyid = *keyid; - else - key_keyid->keyid = *keyid & GRE_PPTP_KEY_MASK; - } - offset += sizeof(((struct gre_full_hdr *)0)->key); - } - - if (hdr->flags & GRE_SEQ) - offset += sizeof(((struct pptp_gre_header *)0)->seq); - - if (gre_ver == 0) { - if (proto == htons(ETH_P_TEB)) { - const struct ethhdr *eth; - struct ethhdr _eth; - - eth = __skb_header_pointer(skb, nhoff + offset, - sizeof(_eth), - data, hlen, &_eth); - if (!eth) - goto out_bad; - proto = eth->h_proto; - offset += sizeof(*eth); - - /* Cap headers that we access via pointers at the - * end of the Ethernet header as our maximum alignment - * at that point is only 2 bytes. - */ - if (NET_IP_ALIGN) - hlen = (nhoff + offset); - } - } else { /* version 1, must be PPTP */ - u8 _ppp_hdr[PPP_HDRLEN]; - u8 *ppp_hdr; - - if (hdr->flags & GRE_ACK) - offset += sizeof(((struct pptp_gre_header *)0)->ack); - - ppp_hdr = __skb_header_pointer(skb, nhoff + offset, - sizeof(_ppp_hdr), - data, hlen, _ppp_hdr); - if (!ppp_hdr) - goto out_bad; - - switch (PPP_PROTOCOL(ppp_hdr)) { - case PPP_IP: - proto = htons(ETH_P_IP); - break; - case PPP_IPV6: - proto = htons(ETH_P_IPV6); - break; - default: - /* Could probably catch some more like MPLS */ - break; - } - - offset += PPP_HDRLEN; + case FLOW_DISSECT_RET_OUT_BAD: + goto out_bad; + case FLOW_DISSECT_RET_OUT_PROTO_AGAIN: + goto proto_again; } - - nhoff += offset; - key_control->flags |= FLOW_DIS_ENCAPSULATION; - if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP) - goto out_good; - - goto proto_again; - } case NEXTHDR_HOP: case NEXTHDR_ROUTING: case NEXTHDR_DEST: { -- cgit From 95964c6de787eb21468a29b94e9d25e1a24d6a37 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 6 Mar 2017 11:23:55 -0800 Subject: net: use proper lockdep annotation in __sk_dst_set() __sk_dst_set() must be called while we own the socket. We can get proper lockdep coverage using lockdep_sock_is_held() and rcu_dereference_protected() Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sock.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index 5e5997654db6..6db7693b9e61 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1780,11 +1780,8 @@ __sk_dst_set(struct sock *sk, struct dst_entry *dst) sk_tx_queue_clear(sk); sk->sk_dst_pending_confirm = 0; - /* - * This can be called while sk is owned by the caller only, - * with no state that can be checked in a rcu_dereference_check() cond - */ - old_dst = rcu_dereference_raw(sk->sk_dst_cache); + old_dst = rcu_dereference_protected(sk->sk_dst_cache, + lockdep_sock_is_held(sk)); rcu_assign_pointer(sk->sk_dst_cache, dst); dst_release(old_dst); } -- cgit From 1182e53639518698e3bc5a6cc9a09659623e7cac Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 6 Mar 2017 21:25:20 +0100 Subject: mlxsw: spectrum: Fix helper function and port variable names Commit dd82364c3ab9 ("mlxsw: Flip to the new dev walk API") did some small changes in mlxsw code, but it did not respect the naming conventions. So fix this now. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 16484f24b7db..ae18067198dd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3326,13 +3326,13 @@ bool mlxsw_sp_port_dev_check(const struct net_device *dev) return dev->netdev_ops == &mlxsw_sp_port_netdev_ops; } -static int mlxsw_lower_dev_walk(struct net_device *lower_dev, void *data) +static int mlxsw_sp_lower_dev_walk(struct net_device *lower_dev, void *data) { - struct mlxsw_sp_port **port = data; + struct mlxsw_sp_port **p_mlxsw_sp_port = data; int ret = 0; if (mlxsw_sp_port_dev_check(lower_dev)) { - *port = netdev_priv(lower_dev); + *p_mlxsw_sp_port = netdev_priv(lower_dev); ret = 1; } @@ -3341,15 +3341,15 @@ static int mlxsw_lower_dev_walk(struct net_device *lower_dev, void *data) static struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find(struct net_device *dev) { - struct mlxsw_sp_port *port; + struct mlxsw_sp_port *mlxsw_sp_port; if (mlxsw_sp_port_dev_check(dev)) return netdev_priv(dev); - port = NULL; - netdev_walk_all_lower_dev(dev, mlxsw_lower_dev_walk, &port); + mlxsw_sp_port = NULL; + netdev_walk_all_lower_dev(dev, mlxsw_sp_lower_dev_walk, &mlxsw_sp_port); - return port; + return mlxsw_sp_port; } static struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev) @@ -3362,15 +3362,16 @@ static struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev) static struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find_rcu(struct net_device *dev) { - struct mlxsw_sp_port *port; + struct mlxsw_sp_port *mlxsw_sp_port; if (mlxsw_sp_port_dev_check(dev)) return netdev_priv(dev); - port = NULL; - netdev_walk_all_lower_dev_rcu(dev, mlxsw_lower_dev_walk, &port); + mlxsw_sp_port = NULL; + netdev_walk_all_lower_dev_rcu(dev, mlxsw_sp_lower_dev_walk, + &mlxsw_sp_port); - return port; + return mlxsw_sp_port; } struct mlxsw_sp_port *mlxsw_sp_port_lower_dev_hold(struct net_device *dev) -- cgit From 61793af6ab8b4455cdfd7a41b45991f8e3900dc8 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Mon, 6 Mar 2017 21:25:21 +0100 Subject: mlxsw: pci: Remove unused bit The overrun ignore bit isn't supported by the device's firmware and was recently removed from the programmer's reference manual (PRM). Remove it from the driver as well. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/cmd.h | 12 ------------ drivers/net/ethernet/mellanox/mlxsw/pci.c | 2 -- 2 files changed, 14 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/cmd.h b/drivers/net/ethernet/mellanox/mlxsw/cmd.h index a1b48421648a..479511cf79bc 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/cmd.h +++ b/drivers/net/ethernet/mellanox/mlxsw/cmd.h @@ -1043,13 +1043,6 @@ MLXSW_ITEM32(cmd_mbox, sw2hw_cq, cv, 0x00, 28, 4); */ MLXSW_ITEM32(cmd_mbox, sw2hw_cq, c_eqn, 0x00, 24, 1); -/* cmd_mbox_sw2hw_cq_oi - * When set, overrun ignore is enabled. When set, updates of - * CQ consumer counter (poll for completion) or Request completion - * notifications (Arm CQ) DoorBells should not be rung on that CQ. - */ -MLXSW_ITEM32(cmd_mbox, sw2hw_cq, oi, 0x00, 12, 1); - /* cmd_mbox_sw2hw_cq_st * Event delivery state machine * 0x0 - FIRED @@ -1132,11 +1125,6 @@ static inline int mlxsw_cmd_sw2hw_eq(struct mlxsw_core *mlxsw_core, */ MLXSW_ITEM32(cmd_mbox, sw2hw_eq, int_msix, 0x00, 24, 1); -/* cmd_mbox_sw2hw_eq_oi - * When set, overrun ignore is enabled. - */ -MLXSW_ITEM32(cmd_mbox, sw2hw_eq, oi, 0x00, 12, 1); - /* cmd_mbox_sw2hw_eq_st * Event delivery state machine * 0x0 - FIRED diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index a223c85dfde0..ffeb746fe2f4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -580,7 +580,6 @@ static int mlxsw_pci_cq_init(struct mlxsw_pci *mlxsw_pci, char *mbox, mlxsw_cmd_mbox_sw2hw_cq_cv_set(mbox, 0); /* CQE ver 0 */ mlxsw_cmd_mbox_sw2hw_cq_c_eqn_set(mbox, MLXSW_PCI_EQ_COMP_NUM); - mlxsw_cmd_mbox_sw2hw_cq_oi_set(mbox, 0); mlxsw_cmd_mbox_sw2hw_cq_st_set(mbox, 0); mlxsw_cmd_mbox_sw2hw_cq_log_cq_size_set(mbox, ilog2(q->count)); for (i = 0; i < MLXSW_PCI_AQ_PAGES; i++) { @@ -755,7 +754,6 @@ static int mlxsw_pci_eq_init(struct mlxsw_pci *mlxsw_pci, char *mbox, } mlxsw_cmd_mbox_sw2hw_eq_int_msix_set(mbox, 1); /* MSI-X used */ - mlxsw_cmd_mbox_sw2hw_eq_oi_set(mbox, 0); mlxsw_cmd_mbox_sw2hw_eq_st_set(mbox, 1); /* armed */ mlxsw_cmd_mbox_sw2hw_eq_log_eq_size_set(mbox, ilog2(q->count)); for (i = 0; i < MLXSW_PCI_AQ_PAGES; i++) { -- cgit From 583a6629432ca95813a585a7117331ffe36fe939 Mon Sep 17 00:00:00 2001 From: Ryan Hsu Date: Wed, 8 Mar 2017 13:52:04 +0200 Subject: ath10k: improve the firmware download time for QCA6174 Len Brown reported the system resume time is taking more than 2 seconds in bug - https://bugzilla.kernel.org/show_bug.cgi?id=185621. The reason of the 2 seconds is due to the firmware download time. The chip is booted up in the default reference clock speed to handle the firmware download to chip memory and advanced to the support higher speed clock to run the firmware after all. The default reference clock in the hardware is slow so that the firmware download time is taking up to 2 seconds for a 600KB firmware file. [76796.349701] ath10k_pci : boot uploading firmware image len 688691 [76798.334612] ath10k_pci : htt tx max num pending tx 1056 The resolution here is to enable the higher speed clock if the hardware supported before the firmware download at BMI stage, so that the hardware can handle the firmare download in a more efficient way. This can help to improve the firmware download time from 2 seconds to around 500ms for the same 600KB firmware file. [322858.577919] ath10k_pci boot uploading firmware image len 688691 [322859.093094] ath10k_pci htt tx max num pending tx 1056 The steps to advance to the higher speed clock is very hardware specific, so adding the hardware ops for the hardware that can support this. Reported-by: Len Brown Tested-by: Paul Menzel Signed-off-by: Ryan Hsu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/bmi.c | 72 +++++++++ drivers/net/wireless/ath/ath10k/bmi.h | 2 + drivers/net/wireless/ath/ath10k/core.c | 4 +- drivers/net/wireless/ath/ath10k/hw.c | 265 +++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/hw.h | 69 +++++++++ 5 files changed, 411 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 2872d347ea78..abeee200310b 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -19,12 +19,21 @@ #include "hif.h" #include "debug.h" #include "htc.h" +#include "hw.h" void ath10k_bmi_start(struct ath10k *ar) { + int ret; + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi start\n"); ar->bmi.done_sent = false; + + /* Enable hardware clock to speed up firmware download */ + if (ar->hw_params.hw_ops->enable_pll_clk) { + ret = ar->hw_params.hw_ops->enable_pll_clk(ar); + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi enable pll ret %d\n", ret); + } } int ath10k_bmi_done(struct ath10k *ar) @@ -129,6 +138,69 @@ int ath10k_bmi_read_memory(struct ath10k *ar, return 0; } +int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val) +{ + struct bmi_cmd cmd; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.write_soc_reg); + int ret; + + ath10k_dbg(ar, ATH10K_DBG_BMI, + "bmi write soc register 0x%08x val 0x%08x\n", + address, reg_val); + + if (ar->bmi.done_sent) { + ath10k_warn(ar, "bmi write soc register command in progress\n"); + return -EBUSY; + } + + cmd.id = __cpu_to_le32(BMI_WRITE_SOC_REGISTER); + cmd.write_soc_reg.addr = __cpu_to_le32(address); + cmd.write_soc_reg.value = __cpu_to_le32(reg_val); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); + if (ret) { + ath10k_warn(ar, "Unable to write soc register to device: %d\n", + ret); + return ret; + } + + return 0; +} + +int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val) +{ + struct bmi_cmd cmd; + union bmi_resp resp; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_soc_reg); + u32 resplen = sizeof(resp.read_soc_reg); + int ret; + + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register 0x%08x\n", + address); + + if (ar->bmi.done_sent) { + ath10k_warn(ar, "bmi read soc register command in progress\n"); + return -EBUSY; + } + + cmd.id = __cpu_to_le32(BMI_READ_SOC_REGISTER); + cmd.read_soc_reg.addr = __cpu_to_le32(address); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); + if (ret) { + ath10k_warn(ar, "Unable to read soc register from device: %d\n", + ret); + return ret; + } + + *reg_val = __le32_to_cpu(resp.read_soc_reg.value); + + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register value 0x%08x\n", + *reg_val); + + return 0; +} + int ath10k_bmi_write_memory(struct ath10k *ar, u32 address, const void *buffer, u32 length) { diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index 7d3231acfb24..a65f26267fe3 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -232,4 +232,6 @@ int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address); int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length); int ath10k_bmi_fast_download(struct ath10k *ar, u32 address, const void *buffer, u32 length); +int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val); +int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val); #endif /* _BMI_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 0a8e29e9a0eb..9916c428d02c 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -166,7 +166,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA6174_BOARD_DATA_SZ, .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, - .hw_ops = &qca988x_ops, + .hw_ops = &qca6174_ops, + .hw_clk = qca6174_clk, + .target_cpu_freq = 176000000, .decap_align_bytes = 4, }, { diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 33fb26833cd0..85582bdd7524 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -19,6 +19,7 @@ #include "hw.h" #include "hif.h" #include "wmi-ops.h" +#include "bmi.h" const struct ath10k_hw_regs qca988x_regs = { .rtc_soc_base_address = 0x00004000, @@ -72,6 +73,9 @@ const struct ath10k_hw_regs qca6174_regs = { .pcie_intr_fw_mask = 0x00000400, .pcie_intr_ce_mask_all = 0x0007f800, .pcie_intr_clr_address = 0x00000014, + .cpu_pll_init_address = 0x00404020, + .cpu_speed_address = 0x00404024, + .core_clk_div_address = 0x00404028, }; const struct ath10k_hw_regs qca99x0_regs = { @@ -187,6 +191,73 @@ const struct ath10k_hw_values qca4019_values = { .ce_desc_meta_data_lsb = 4, }; +const struct ath10k_hw_clk_params qca6174_clk[ATH10K_HW_REFCLK_COUNT] = { + { + .refclk = 48000000, + .div = 0xe, + .rnfrac = 0x2aaa8, + .settle_time = 2400, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 19200000, + .div = 0x24, + .rnfrac = 0x2aaa8, + .settle_time = 960, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 24000000, + .div = 0x1d, + .rnfrac = 0x15551, + .settle_time = 1200, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 26000000, + .div = 0x1b, + .rnfrac = 0x4ec4, + .settle_time = 1300, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 37400000, + .div = 0x12, + .rnfrac = 0x34b49, + .settle_time = 1870, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 38400000, + .div = 0x12, + .rnfrac = 0x15551, + .settle_time = 1920, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 40000000, + .div = 0x12, + .rnfrac = 0x26665, + .settle_time = 2000, + .refdiv = 0, + .outdiv = 1, + }, + { + .refclk = 52000000, + .div = 0x1b, + .rnfrac = 0x4ec4, + .settle_time = 2600, + .refdiv = 0, + .outdiv = 1, + }, +}; + void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey, u32 cc, u32 rcc, u32 cc_prev, u32 rcc_prev) { @@ -361,6 +432,195 @@ unlock: mutex_unlock(&ar->conf_mutex); } +/** + * ath10k_hw_qca6174_enable_pll_clock() - enable the qca6174 hw pll clock + * @ar: the ath10k blob + * + * This function is very hardware specific, the clock initialization + * steps is very sensitive and could lead to unknown crash, so they + * should be done in sequence. + * + * *** Be aware if you planned to refactor them. *** + * + * Return: 0 if successfully enable the pll, otherwise EINVAL + */ +static int ath10k_hw_qca6174_enable_pll_clock(struct ath10k *ar) +{ + int ret, wait_limit; + u32 clk_div_addr, pll_init_addr, speed_addr; + u32 addr, reg_val, mem_val; + struct ath10k_hw_params *hw; + const struct ath10k_hw_clk_params *hw_clk; + + hw = &ar->hw_params; + + if (ar->regs->core_clk_div_address == 0 || + ar->regs->cpu_pll_init_address == 0 || + ar->regs->cpu_speed_address == 0) + return -EINVAL; + + clk_div_addr = ar->regs->core_clk_div_address; + pll_init_addr = ar->regs->cpu_pll_init_address; + speed_addr = ar->regs->cpu_speed_address; + + /* Read efuse register to find out the right hw clock configuration */ + addr = (RTC_SOC_BASE_ADDRESS | EFUSE_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + /* sanitize if the hw refclk index is out of the boundary */ + if (MS(reg_val, EFUSE_XTAL_SEL) > ATH10K_HW_REFCLK_COUNT) + return -EINVAL; + + hw_clk = &hw->hw_clk[MS(reg_val, EFUSE_XTAL_SEL)]; + + /* Set the rnfrac and outdiv params to bb_pll register */ + addr = (RTC_SOC_BASE_ADDRESS | BB_PLL_CONFIG_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val &= ~(BB_PLL_CONFIG_FRAC_MASK | BB_PLL_CONFIG_OUTDIV_MASK); + reg_val |= (SM(hw_clk->rnfrac, BB_PLL_CONFIG_FRAC) | + SM(hw_clk->outdiv, BB_PLL_CONFIG_OUTDIV)); + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* Set the correct settle time value to pll_settle register */ + addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_SETTLE_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val &= ~WLAN_PLL_SETTLE_TIME_MASK; + reg_val |= SM(hw_clk->settle_time, WLAN_PLL_SETTLE_TIME); + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* Set the clock_ctrl div to core_clk_ctrl register */ + addr = (RTC_SOC_BASE_ADDRESS | SOC_CORE_CLK_CTRL_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val &= ~SOC_CORE_CLK_CTRL_DIV_MASK; + reg_val |= SM(1, SOC_CORE_CLK_CTRL_DIV); + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* Set the clock_div register */ + mem_val = 1; + ret = ath10k_bmi_write_memory(ar, clk_div_addr, &mem_val, + sizeof(mem_val)); + if (ret) + return -EINVAL; + + /* Configure the pll_control register */ + addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_CONTROL_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val |= (SM(hw_clk->refdiv, WLAN_PLL_CONTROL_REFDIV) | + SM(hw_clk->div, WLAN_PLL_CONTROL_DIV) | + SM(1, WLAN_PLL_CONTROL_NOPWD)); + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* busy wait (max 1s) the rtc_sync status register indicate ready */ + wait_limit = 100000; + addr = (RTC_WMAC_BASE_ADDRESS | RTC_SYNC_STATUS_OFFSET); + do { + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + if (!MS(reg_val, RTC_SYNC_STATUS_PLL_CHANGING)) + break; + + wait_limit--; + udelay(10); + + } while (wait_limit > 0); + + if (MS(reg_val, RTC_SYNC_STATUS_PLL_CHANGING)) + return -EINVAL; + + /* Unset the pll_bypass in pll_control register */ + addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_CONTROL_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val &= ~WLAN_PLL_CONTROL_BYPASS_MASK; + reg_val |= SM(0, WLAN_PLL_CONTROL_BYPASS); + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* busy wait (max 1s) the rtc_sync status register indicate ready */ + wait_limit = 100000; + addr = (RTC_WMAC_BASE_ADDRESS | RTC_SYNC_STATUS_OFFSET); + do { + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + if (!MS(reg_val, RTC_SYNC_STATUS_PLL_CHANGING)) + break; + + wait_limit--; + udelay(10); + + } while (wait_limit > 0); + + if (MS(reg_val, RTC_SYNC_STATUS_PLL_CHANGING)) + return -EINVAL; + + /* Enable the hardware cpu clock register */ + addr = (RTC_SOC_BASE_ADDRESS | SOC_CPU_CLOCK_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val &= ~SOC_CPU_CLOCK_STANDARD_MASK; + reg_val |= SM(1, SOC_CPU_CLOCK_STANDARD); + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* unset the nopwd from pll_control register */ + addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_CONTROL_OFFSET); + ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); + if (ret) + return -EINVAL; + + reg_val &= ~WLAN_PLL_CONTROL_NOPWD_MASK; + ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); + if (ret) + return -EINVAL; + + /* enable the pll_init register */ + mem_val = 1; + ret = ath10k_bmi_write_memory(ar, pll_init_addr, &mem_val, + sizeof(mem_val)); + if (ret) + return -EINVAL; + + /* set the target clock frequency to speed register */ + ret = ath10k_bmi_write_memory(ar, speed_addr, &hw->target_cpu_freq, + sizeof(hw->target_cpu_freq)); + if (ret) + return -EINVAL; + + return 0; +} + const struct ath10k_hw_ops qca988x_ops = { .set_coverage_class = ath10k_hw_qca988x_set_coverage_class, }; @@ -374,3 +634,8 @@ static int ath10k_qca99x0_rx_desc_get_l3_pad_bytes(struct htt_rx_desc *rxd) const struct ath10k_hw_ops qca99x0_ops = { .rx_desc_get_l3_pad_bytes = ath10k_qca99x0_rx_desc_get_l3_pad_bytes, }; + +const struct ath10k_hw_ops qca6174_ops = { + .set_coverage_class = ath10k_hw_qca988x_set_coverage_class, + .enable_pll_clk = ath10k_hw_qca6174_enable_pll_clock, +}; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index f0fda0f2b3b4..d370b573e0f9 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -255,6 +255,9 @@ struct ath10k_hw_regs { u32 pcie_intr_fw_mask; u32 pcie_intr_ce_mask_all; u32 pcie_intr_clr_address; + u32 cpu_pll_init_address; + u32 cpu_speed_address; + u32 core_clk_div_address; }; extern const struct ath10k_hw_regs qca988x_regs; @@ -363,6 +366,30 @@ enum ath10k_hw_cc_wraparound_type { ATH10K_HW_CC_WRAP_SHIFTED_EACH = 2, }; +enum ath10k_hw_refclk_speed { + ATH10K_HW_REFCLK_UNKNOWN = -1, + ATH10K_HW_REFCLK_48_MHZ = 0, + ATH10K_HW_REFCLK_19_2_MHZ = 1, + ATH10K_HW_REFCLK_24_MHZ = 2, + ATH10K_HW_REFCLK_26_MHZ = 3, + ATH10K_HW_REFCLK_37_4_MHZ = 4, + ATH10K_HW_REFCLK_38_4_MHZ = 5, + ATH10K_HW_REFCLK_40_MHZ = 6, + ATH10K_HW_REFCLK_52_MHZ = 7, + + /* must be the last one */ + ATH10K_HW_REFCLK_COUNT, +}; + +struct ath10k_hw_clk_params { + u32 refclk; + u32 div; + u32 rnfrac; + u32 settle_time; + u32 refdiv; + u32 outdiv; +}; + struct ath10k_hw_params { u32 id; u16 dev_id; @@ -416,6 +443,10 @@ struct ath10k_hw_params { /* Number of bytes used for alignment in rx_hdr_status of rx desc. */ int decap_align_bytes; + + /* hw specific clock control parameters */ + const struct ath10k_hw_clk_params *hw_clk; + int target_cpu_freq; }; struct htt_rx_desc; @@ -424,10 +455,14 @@ struct htt_rx_desc; struct ath10k_hw_ops { int (*rx_desc_get_l3_pad_bytes)(struct htt_rx_desc *rxd); void (*set_coverage_class)(struct ath10k *ar, s16 value); + int (*enable_pll_clk)(struct ath10k *ar); }; extern const struct ath10k_hw_ops qca988x_ops; extern const struct ath10k_hw_ops qca99x0_ops; +extern const struct ath10k_hw_ops qca6174_ops; + +extern const struct ath10k_hw_clk_params qca6174_clk[]; static inline int ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, @@ -847,4 +882,38 @@ ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, #define WAVE1_PHYCLK_USEC_MASK 0x0000007F #define WAVE1_PHYCLK_USEC_LSB 0 +/* qca6174 PLL offset/mask */ +#define SOC_CORE_CLK_CTRL_OFFSET 0x00000114 +#define SOC_CORE_CLK_CTRL_DIV_LSB 0 +#define SOC_CORE_CLK_CTRL_DIV_MASK 0x00000007 + +#define EFUSE_OFFSET 0x0000032c +#define EFUSE_XTAL_SEL_LSB 8 +#define EFUSE_XTAL_SEL_MASK 0x00000700 + +#define BB_PLL_CONFIG_OFFSET 0x000002f4 +#define BB_PLL_CONFIG_FRAC_LSB 0 +#define BB_PLL_CONFIG_FRAC_MASK 0x0003ffff +#define BB_PLL_CONFIG_OUTDIV_LSB 18 +#define BB_PLL_CONFIG_OUTDIV_MASK 0x001c0000 + +#define WLAN_PLL_SETTLE_OFFSET 0x0018 +#define WLAN_PLL_SETTLE_TIME_LSB 0 +#define WLAN_PLL_SETTLE_TIME_MASK 0x000007ff + +#define WLAN_PLL_CONTROL_OFFSET 0x0014 +#define WLAN_PLL_CONTROL_DIV_LSB 0 +#define WLAN_PLL_CONTROL_DIV_MASK 0x000003ff +#define WLAN_PLL_CONTROL_REFDIV_LSB 10 +#define WLAN_PLL_CONTROL_REFDIV_MASK 0x00003c00 +#define WLAN_PLL_CONTROL_BYPASS_LSB 16 +#define WLAN_PLL_CONTROL_BYPASS_MASK 0x00010000 +#define WLAN_PLL_CONTROL_NOPWD_LSB 18 +#define WLAN_PLL_CONTROL_NOPWD_MASK 0x00040000 + +#define RTC_SYNC_STATUS_OFFSET 0x0244 +#define RTC_SYNC_STATUS_PLL_CHANGING_LSB 5 +#define RTC_SYNC_STATUS_PLL_CHANGING_MASK 0x00000020 +/* qca6174 PLL offset/mask end */ + #endif /* _HW_H_ */ -- cgit From 912b6e8850a51a09ec771aedf2b4428ac9b34e20 Mon Sep 17 00:00:00 2001 From: Ryan Hsu Date: Wed, 8 Mar 2017 13:52:05 +0200 Subject: ath10k: improve the firmware download time for QCA9377 QCA9377 is the family of QCA61x4 which shared the same procedure to enable the hardware clock that could improve the firmware download time. Signed-off-by: Ryan Hsu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 9916c428d02c..f450ebbb28d5 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -282,7 +282,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .board_size = QCA9377_BOARD_DATA_SZ, .board_ext_size = QCA9377_BOARD_EXT_DATA_SZ, }, - .hw_ops = &qca988x_ops, + .hw_ops = &qca6174_ops, + .hw_clk = qca6174_clk, + .target_cpu_freq = 176000000, .decap_align_bytes = 4, }, { -- cgit From 16ff1fb0e32f76a5d285a6f23b82d21aa52813c6 Mon Sep 17 00:00:00 2001 From: Dmitry Tunin Date: Wed, 8 Mar 2017 13:52:07 +0200 Subject: ath9k_htc: Add support of AirTies 1eda:2315 AR9271 device T: Bus=01 Lev=02 Prnt=02 Port=02 Cnt=01 Dev#= 7 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=ff(vend.) Sub=ff Prot=ff MxPS=64 #Cfgs= 1 P: Vendor=1eda ProdID=2315 Rev=01.08 S: Manufacturer=ATHEROS S: Product=USB2.0 WLAN S: SerialNumber=12345 C: #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 6 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) Signed-off-by: Dmitry Tunin Cc: stable@vger.kernel.org Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/hif_usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index de2d212f39ec..05dd056cab6e 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -37,6 +37,7 @@ static struct usb_device_id ath9k_hif_usb_ids[] = { { USB_DEVICE(0x0cf3, 0xb002) }, /* Ubiquiti WifiStation */ { USB_DEVICE(0x057c, 0x8403) }, /* AVM FRITZ!WLAN 11N v2 USB */ { USB_DEVICE(0x0471, 0x209e) }, /* Philips (or NXP) PTA01 */ + { USB_DEVICE(0x1eda, 0x2315) }, /* AirTies */ { USB_DEVICE(0x0cf3, 0x7015), .driver_info = AR9287_USB }, /* Atheros */ -- cgit From c73f8c00330f59ce9b1ace9ff698aca83390d358 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 8 Mar 2017 13:52:06 +0200 Subject: ath10k: fix a warning during channel switch with multiple vaps Doing a channel switch via hostapd_cli seems to update the new channel context for each VAP's appropriately as below in 'ath10k_mac_update_vif_chan', hence we can safely suppress the warning that shows up during this operation and dump the warning only if no vaps are available for channel switch hostapd_cli -i wlan0 chan_switch 5 5200 OK ath10k_pci : mac chanctx switch n_vifs 3 mode 1 ath10k_pci : mac chanctx switch vdev_id 2 freq 5180->5200 width 0->0 ath10k_pci : mac chanctx switch vdev_id 1 freq 5180->5200 width 0->0 ath10k_pci : mac chanctx switch vdev_id 0 freq 5180->5200 width 0->0 Call Trace: WARNING: backports-20161201-3.14.77-9ab3068/drivers/net/wireless/ath/ath10k/mac.c:7126 [] (warn_slowpath_null) from [] (ath10k_reconfig_complete+0xe4/0x25c [ath10k_core]) [] (ath10k_reconfig_complete [ath10k_core]) [] (ath10k_mac_vif_ap_csa_work+0x214/0x370 [ath10k_core]) [] (ath10k_mac_op_change_chanctx+0x108/0x128 [ath10k_core]) [] (ieee80211_recalc_chanctx_min_def+0x30c/0x430 [mac80211]) [] (ieee80211_recalc_smps_chanctx+0x2ec/0x840 [mac80211]) [] (ieee80211_vif_use_reserved_context+0x7c/0xf8 [mac80211]) [] (ieee80211_vif_use_reserved_context [mac80211]) [] (ieee80211_csa_finalize_work+0x5c/0x88 [mac80211]) Fixes: d7bf4b4aba05 ("ath10k: fix ar->rx_channel updating logic") Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 3029f257a19a..f0e46804ad82 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -7129,7 +7129,7 @@ ath10k_mac_update_rx_channel(struct ath10k *ar, lockdep_assert_held(&ar->data_lock); WARN_ON(ctx && vifs); - WARN_ON(vifs && n_vifs != 1); + WARN_ON(vifs && !n_vifs); /* FIXME: Sort of an optimization and a workaround. Peers and vifs are * on a linked list now. Doing a lookup peer -> vif -> chanctx for each -- cgit From 4aa2d31f5df8c47cc92ccacf49a2dff3db6c1d53 Mon Sep 17 00:00:00 2001 From: Christophe Jaillet Date: Wed, 8 Mar 2017 13:52:09 +0200 Subject: wcn36xx: Fix error handling Reorder 'out_free_dxe_pool' and 'out_free_dxe_ctl' error handling labels in order to match the way resources have been allocated. Signed-off-by: Christophe JAILLET Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 7a0c2e7da7f6..2b05154d05a4 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -337,10 +337,10 @@ out_smd_stop: wcn36xx_smd_stop(wcn); out_free_smd_buf: kfree(wcn->hal_buf); -out_free_dxe_pool: - wcn36xx_dxe_free_mem_pools(wcn); out_free_dxe_ctl: wcn36xx_dxe_free_ctl_blks(wcn); +out_free_dxe_pool: + wcn36xx_dxe_free_mem_pools(wcn); out_smd_close: wcn36xx_smd_close(wcn); out_err: -- cgit From f2de576dcfd094b02297c251223b0e2c9de96c33 Mon Sep 17 00:00:00 2001 From: Hamad Kadmany Date: Wed, 8 Mar 2017 13:52:10 +0200 Subject: wil6210: set dma mask to reflect device capability device supports 48bit addresses, reflect that by setting the dma mask accordingly. Signed-off-by: Hamad Kadmany Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/pcie_bus.c | 16 ++++++++++++++++ drivers/net/wireless/ath/wil6210/pmc.c | 17 ++++++++++++++++- drivers/net/wireless/ath/wil6210/txrx.c | 19 ++++++++++++++++++- drivers/net/wireless/ath/wil6210/wil6210.h | 1 + 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 874c787727fe..b38515fc7ce7 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -211,6 +211,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev_err(dev, "wil_if_alloc failed: %d\n", rc); return rc; } + wil->pdev = pdev; pci_set_drvdata(pdev, wil); /* rollback to if_free */ @@ -224,6 +225,21 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) } /* rollback to err_plat */ + /* device supports 48bit addresses */ + rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); + if (rc) { + dev_err(dev, "dma_set_mask_and_coherent(48) failed: %d\n", rc); + rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (rc) { + dev_err(dev, + "dma_set_mask_and_coherent(32) failed: %d\n", + rc); + goto err_plat; + } + } else { + wil->use_extended_dma_addr = 1; + } + rc = pci_enable_device(pdev); if (rc) { wil_err(wil, diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c index 3ff4f4ce9fef..b067fdf086d4 100644 --- a/drivers/net/wireless/ath/wil6210/pmc.c +++ b/drivers/net/wireless/ath/wil6210/pmc.c @@ -107,13 +107,28 @@ void wil_pmc_alloc(struct wil6210_priv *wil, /* Allocate pring buffer and descriptors. * vring->va should be aligned on its size rounded up to power of 2 - * This is granted by the dma_alloc_coherent + * This is granted by the dma_alloc_coherent. + * + * HW has limitation that all vrings addresses must share the same + * upper 16 msb bits part of 48 bits address. To workaround that, + * if we are using 48 bit addresses switch to 32 bit allocation + * before allocating vring memory. + * + * There's no check for the return value of dma_set_mask_and_coherent, + * since we assume if we were able to set the mask during + * initialization in this system it will not fail if we set it again */ + if (wil->use_extended_dma_addr) + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + pmc->pring_va = dma_alloc_coherent(dev, sizeof(struct vring_tx_desc) * num_descriptors, &pmc->pring_pa, GFP_KERNEL); + if (wil->use_extended_dma_addr) + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); + wil_dbg_misc(wil, "pmc_alloc: allocated pring %p => %pad. %zd x %d = total %zd bytes\n", pmc->pring_va, &pmc->pring_pa, diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 072182e527e6..67f50ae17cd3 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -123,15 +123,32 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) vring->va = NULL; return -ENOMEM; } + /* vring->va should be aligned on its size rounded up to power of 2 - * This is granted by the dma_alloc_coherent + * This is granted by the dma_alloc_coherent. + * + * HW has limitation that all vrings addresses must share the same + * upper 16 msb bits part of 48 bits address. To workaround that, + * if we are using 48 bit addresses switch to 32 bit allocation + * before allocating vring memory. + * + * There's no check for the return value of dma_set_mask_and_coherent, + * since we assume if we were able to set the mask during + * initialization in this system it will not fail if we set it again */ + if (wil->use_extended_dma_addr) + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL); if (!vring->va) { kfree(vring->ctx); vring->ctx = NULL; return -ENOMEM; } + + if (wil->use_extended_dma_addr) + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); + /* initially, all descriptors are SW owned * For Tx and Rx, ownership bit is at the same location, thus * we can use any diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 085a2dbfa21d..ea1e5b34e011 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -657,6 +657,7 @@ struct wil6210_priv { u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */ struct wil_sta_info sta[WIL6210_MAX_CID]; int bcast_vring; + bool use_extended_dma_addr; /* indicates whether we are using 48 bits */ /* scan */ struct cfg80211_scan_request *scan_request; -- cgit From 4a0e45a78328957b012f554fb2ffcd5c69fe418a Mon Sep 17 00:00:00 2001 From: Lior David Date: Wed, 8 Mar 2017 13:52:11 +0200 Subject: wil6210: do not start regular scan on stopped p2p device The driver should not allow starting any type of scan on a stopped P2P device. Current implementation only checked social scan. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 83155b5ddbfb..14aa6d36fd1e 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -390,22 +390,23 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, } mutex_unlock(&wil->p2p_wdev_mutex); - /* social scan on P2P_DEVICE is handled as p2p search */ - if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE && - wil_p2p_is_social_scan(request)) { + if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) { if (!wil->p2p.p2p_dev_started) { wil_err(wil, "P2P search requested on stopped P2P device\n"); rc = -EIO; goto out; } - wil->scan_request = request; - wil->radio_wdev = wdev; - rc = wil_p2p_search(wil, request); - if (rc) { - wil->radio_wdev = wil_to_wdev(wil); - wil->scan_request = NULL; + /* social scan on P2P_DEVICE is handled as p2p search */ + if (wil_p2p_is_social_scan(request)) { + wil->scan_request = request; + wil->radio_wdev = wdev; + rc = wil_p2p_search(wil, request); + if (rc) { + wil->radio_wdev = wil_to_wdev(wil); + wil->scan_request = NULL; + } + goto out; } - goto out; } (void)wil_p2p_stop_discovery(wil); -- cgit From 9953a782f9fac1becccb8f48f1a276c310f2ab5a Mon Sep 17 00:00:00 2001 From: Lior David Date: Wed, 8 Mar 2017 13:52:12 +0200 Subject: wil6210: bus_request platform operation refinement The driver uses the bus_request platform operation to request resources from the platform for a specific bandwidth. Currently the driver requests resources for the maximum theoretical bandwidth, when interface is brought up. Refine this process a bit: now the driver will request a small amount of resources when interface is up, and will only issue the maximum request when connected. This mechanism will be improved further in the future to make more refined requests based on actual bandwidth. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 4 ++++ drivers/net/wireless/ath/wil6210/main.c | 14 +++++++++----- drivers/net/wireless/ath/wil6210/wil6210.h | 5 +++-- drivers/net/wireless/ath/wil6210/wmi.c | 1 + 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 14aa6d36fd1e..2dc594d98862 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -680,6 +680,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); if (rc == 0) { netif_carrier_on(ndev); + wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS); /* Connect can take lots of time */ mod_timer(&wil->connect_timer, jiffies + msecs_to_jiffies(2000)); @@ -1199,6 +1200,7 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy, wil->pbss = pbss; netif_carrier_on(ndev); + wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS); rc = wmi_pcp_start(wil, bi, wmi_nettype, chan, hidden_ssid, is_go); if (rc) @@ -1214,6 +1216,7 @@ err_bcast: wmi_pcp_stop(wil); err_pcp_start: netif_carrier_off(ndev); + wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); out: mutex_unlock(&wil->mutex); return rc; @@ -1320,6 +1323,7 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy, wil_dbg_misc(wil, "stop_ap\n"); netif_carrier_off(ndev); + wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); wil_set_recovery_state(wil, fw_recovery_idle); mutex_lock(&wil->mutex); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index efb1f59aafd9..21b7faca2c9b 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -274,6 +274,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, wil_bcast_fini(wil); wil_update_net_queues_bh(wil, NULL, true); netif_carrier_off(ndev); + wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); if (test_bit(wil_status_fwconnected, wil->status)) { clear_bit(wil_status_fwconnected, wil->status); @@ -555,6 +556,12 @@ out_wmi_wq: return -EAGAIN; } +void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps) +{ + if (wil->platform_ops.bus_request) + wil->platform_ops.bus_request(wil->platform_handle, kbps); +} + /** * wil6210_disconnect - disconnect one connection * @wil: driver context @@ -1066,9 +1073,7 @@ int __wil_up(struct wil6210_priv *wil) napi_enable(&wil->napi_tx); set_bit(wil_status_napi_en, wil->status); - if (wil->platform_ops.bus_request) - wil->platform_ops.bus_request(wil->platform_handle, - WIL_MAX_BUS_REQUEST_KBPS); + wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); return 0; } @@ -1092,8 +1097,7 @@ int __wil_down(struct wil6210_priv *wil) set_bit(wil_status_resetting, wil->status); - if (wil->platform_ops.bus_request) - wil->platform_ops.bus_request(wil->platform_handle, 0); + wil6210_bus_request(wil, 0); wil_disable_irq(wil); if (test_and_clear_bit(wil_status_napi_en, wil->status)) { diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index ea1e5b34e011..12de74a38f8d 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -40,6 +40,7 @@ extern bool disable_ap_sme; #define WIL_FW_NAME_SPARROW_PLUS "wil6210_sparrow_plus.fw" /* code Sparrow D0 */ #define WIL_BOARD_FILE_NAME "wil6210.brd" /* board & radio parameters */ +#define WIL_DEFAULT_BUS_REQUEST_KBPS 128000 /* ~1Gbps */ #define WIL_MAX_BUS_REQUEST_KBPS 800000 /* ~6.1Gbps */ /** @@ -900,7 +901,7 @@ int wmi_pcp_stop(struct wil6210_priv *wil); int wmi_led_cfg(struct wil6210_priv *wil, bool enable); int wmi_abort_scan(struct wil6210_priv *wil); void wil_abort_scan(struct wil6210_priv *wil, bool sync); - +void wil6210_bus_request(struct wil6210_priv *wil, u32 kbps); void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, u16 reason_code, bool from_event); void wil_probe_client_flush(struct wil6210_priv *wil); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 1f22c19696b1..a7a4ac1a8eb7 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -565,6 +565,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { if (rc) { netif_carrier_off(ndev); + wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); wil_err(wil, "cfg80211_connect_result with failure\n"); cfg80211_connect_result(ndev, evt->bssid, NULL, 0, NULL, 0, -- cgit From 5eb443e9af87776f94e77a9b72e54ceb81cbabc1 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 8 Mar 2017 13:52:13 +0200 Subject: wil6210: use print_hex_dump_debug instead of print_hex_dump_bytes Some dynamic debug printouts in driver are using print_hex_dump_bytes. However, with dynamic debug disabled, print_hex_dump_bytes outputs to log unconditionally. Use print_hex_dump_debug instead to prevent log pollution when dynamic debug disabled. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 41 +++++++++++++++-------------- drivers/net/wireless/ath/wil6210/wil6210.h | 12 +++++++++ 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 2dc594d98862..581a4e2966fc 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -416,9 +416,9 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, for (i = 0; i < request->n_ssids; i++) { wil_dbg_misc(wil, "SSID[%d]", i); - print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, - request->ssids[i].ssid, - request->ssids[i].ssid_len); + wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1, + request->ssids[i].ssid, + request->ssids[i].ssid_len, true); } if (request->n_ssids) @@ -455,8 +455,8 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, } if (request->ie_len) - print_hex_dump_bytes("Scan IE ", DUMP_PREFIX_OFFSET, - request->ie, request->ie_len); + wil_hex_dump_misc("Scan IE ", DUMP_PREFIX_OFFSET, 16, 1, + request->ie, request->ie_len, true); else wil_dbg_misc(wil, "Scan has no IE's\n"); @@ -762,7 +762,8 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, */ wil_dbg_misc(wil, "mgmt_tx\n"); - print_hex_dump_bytes("mgmt tx frame ", DUMP_PREFIX_OFFSET, buf, len); + wil_hex_dump_misc("mgmt tx frame ", DUMP_PREFIX_OFFSET, 16, 1, buf, + len, true); cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL); if (!cmd) { @@ -1095,18 +1096,18 @@ static int _wil_cfg80211_merge_extra_ies(const u8 *ies1, u16 ies1_len, static void wil_print_bcon_data(struct cfg80211_beacon_data *b) { - print_hex_dump_bytes("head ", DUMP_PREFIX_OFFSET, - b->head, b->head_len); - print_hex_dump_bytes("tail ", DUMP_PREFIX_OFFSET, - b->tail, b->tail_len); - print_hex_dump_bytes("BCON IE ", DUMP_PREFIX_OFFSET, - b->beacon_ies, b->beacon_ies_len); - print_hex_dump_bytes("PROBE ", DUMP_PREFIX_OFFSET, - b->probe_resp, b->probe_resp_len); - print_hex_dump_bytes("PROBE IE ", DUMP_PREFIX_OFFSET, - b->proberesp_ies, b->proberesp_ies_len); - print_hex_dump_bytes("ASSOC IE ", DUMP_PREFIX_OFFSET, - b->assocresp_ies, b->assocresp_ies_len); + wil_hex_dump_misc("head ", DUMP_PREFIX_OFFSET, 16, 1, + b->head, b->head_len, true); + wil_hex_dump_misc("tail ", DUMP_PREFIX_OFFSET, 16, 1, + b->tail, b->tail_len, true); + wil_hex_dump_misc("BCON IE ", DUMP_PREFIX_OFFSET, 16, 1, + b->beacon_ies, b->beacon_ies_len, true); + wil_hex_dump_misc("PROBE ", DUMP_PREFIX_OFFSET, 16, 1, + b->probe_resp, b->probe_resp_len, true); + wil_hex_dump_misc("PROBE IE ", DUMP_PREFIX_OFFSET, 16, 1, + b->proberesp_ies, b->proberesp_ies_len, true); + wil_hex_dump_misc("ASSOC IE ", DUMP_PREFIX_OFFSET, 16, 1, + b->assocresp_ies, b->assocresp_ies_len, true); } /* internal functions for device reset and starting AP */ @@ -1302,8 +1303,8 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval, info->dtim_period); wil_dbg_misc(wil, "PBSS %d\n", info->pbss); - print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, - info->ssid, info->ssid_len); + wil_hex_dump_misc("SSID ", DUMP_PREFIX_OFFSET, 16, 1, + info->ssid, info->ssid_len, true); wil_print_bcon_data(bcon); wil_print_crypto(wil, crypto); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 12de74a38f8d..89e7eb7790fa 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -766,6 +766,12 @@ static inline void wil_c(struct wil6210_priv *wil, u32 reg, u32 val) print_hex_dump_debug("DBG[ WMI]" prefix_str,\ prefix_type, rowsize, \ groupsize, buf, len, ascii) + +#define wil_hex_dump_misc(prefix_str, prefix_type, rowsize, \ + groupsize, buf, len, ascii) \ + print_hex_dump_debug("DBG[MISC]" prefix_str,\ + prefix_type, rowsize, \ + groupsize, buf, len, ascii) #else /* defined(CONFIG_DYNAMIC_DEBUG) */ static inline void wil_hex_dump_txrx(const char *prefix_str, int prefix_type, int rowsize, @@ -778,6 +784,12 @@ void wil_hex_dump_wmi(const char *prefix_str, int prefix_type, int rowsize, int groupsize, const void *buf, size_t len, bool ascii) { } + +static inline +void wil_hex_dump_misc(const char *prefix_str, int prefix_type, int rowsize, + int groupsize, const void *buf, size_t len, bool ascii) +{ +} #endif /* defined(CONFIG_DYNAMIC_DEBUG) */ void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, -- cgit From 18618a9fba33f87a1cc6083a22a541972cd3f3b3 Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Wed, 8 Mar 2017 13:52:14 +0200 Subject: wil6210: missing reinit_completion in HALP voting After setting HALP ICR bit, we keep it set until HALP unvote. Masking HALP ICR should protect the driver from hitting the HALP ICR over and over again. However, in case there is another MISC ICR we will read the HALP ICR and issue a completion. This can lead to a case where HALP voting is completed immediately, as the completion is already set. Reinit the HALP completion before the actual vote will clear previous completions and protect from such cases. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 21b7faca2c9b..dddde94f0356 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -1158,6 +1158,7 @@ void wil_halp_vote(struct wil6210_priv *wil) wil->halp.ref_cnt); if (++wil->halp.ref_cnt == 1) { + reinit_completion(&wil->halp.comp); wil6210_set_halp(wil); rc = wait_for_completion_timeout(&wil->halp.comp, to_jiffies); if (!rc) { -- cgit From bcdd49b074d0ea48f54bc6d24a66efab8688b43a Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 8 Mar 2017 13:52:15 +0200 Subject: wil6210: store bss object and use cfg80211_connect_bss() In a fast disconnect/connect sequence, cfg80211_connect_result() can fail to find the bss object which the driver is connecting to. Detailed sequence of events: * Driver is connected in STA mode * Disconnect request arrives from user space. Driver disconnects and calls cfg80211_disconnected() which adds new event to the cfg80211_wq worker thread * Connect request arrives from user space. cfg80211_connect() stores ssid/ssid_len and calls rdev_connect() * __cfg80211_disconnected() runs in worker thread and zero wdev->ssid_len * Connect succeeds. Driver calls cfg80211_connect_result() which fails to find the bss because wdev->ssid_len is zero To overcome this, upon connect request, store the bss object in the driver and upon connect completion pass it to kernel using cfg80211_connect_bss(). Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 1 + drivers/net/wireless/ath/wil6210/main.c | 1 + drivers/net/wireless/ath/wil6210/wil6210.h | 1 + drivers/net/wireless/ath/wil6210/wmi.c | 14 +++++++++----- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 581a4e2966fc..39ee565ecf56 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -681,6 +681,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, if (rc == 0) { netif_carrier_on(ndev); wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS); + wil->bss = bss; /* Connect can take lots of time */ mod_timer(&wil->connect_timer, jiffies + msecs_to_jiffies(2000)); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index dddde94f0356..5d45faca5d51 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -284,6 +284,7 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); + wil->bss = NULL; } clear_bit(wil_status_fwconnecting, wil->status); break; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 89e7eb7790fa..09e2fcef4f07 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -613,6 +613,7 @@ struct wil6210_priv { u16 channel; /* relevant in AP mode */ int sinfo_gen; u32 ap_isolate; /* no intra-BSS communication */ + struct cfg80211_bss *bss; /* connected bss, relevant in STA mode */ /* interrupt moderation */ u32 tx_max_burst_duration; u32 tx_interframe_timeout; diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index a7a4ac1a8eb7..a2a895bee6d8 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -573,12 +573,16 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) GFP_KERNEL); goto out; } else { - cfg80211_connect_result(ndev, evt->bssid, - assoc_req_ie, assoc_req_ielen, - assoc_resp_ie, assoc_resp_ielen, - WLAN_STATUS_SUCCESS, - GFP_KERNEL); + struct wiphy *wiphy = wil_to_wiphy(wil); + + cfg80211_ref_bss(wiphy, wil->bss); + cfg80211_connect_bss(ndev, evt->bssid, wil->bss, + assoc_req_ie, assoc_req_ielen, + assoc_resp_ie, assoc_resp_ielen, + WLAN_STATUS_SUCCESS, GFP_KERNEL, + NL80211_TIMEOUT_UNSPECIFIED); } + wil->bss = NULL; } else if ((wdev->iftype == NL80211_IFTYPE_AP) || (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { if (rc) { -- cgit From f6b29b6585593b28c4fde142727bfef350372b83 Mon Sep 17 00:00:00 2001 From: Hamad Kadmany Date: Wed, 8 Mar 2017 13:52:16 +0200 Subject: wil6210: protect list of pending wmi events during flush When flush is done, pending events list is manipulated without taking the proper spinlock, which could lead to memory corruption if list is manipulated by wmi worker or by interrupt routine. Signed-off-by: Hamad Kadmany Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wmi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index a2a895bee6d8..02ccdaee6da9 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1738,14 +1738,19 @@ int wmi_new_sta(struct wil6210_priv *wil, const u8 *mac, u8 aid) void wmi_event_flush(struct wil6210_priv *wil) { + ulong flags; struct pending_wmi_event *evt, *t; wil_dbg_wmi(wil, "event_flush\n"); + spin_lock_irqsave(&wil->wmi_ev_lock, flags); + list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) { list_del(&evt->list); kfree(evt); } + + spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); } static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id, -- cgit From 628639b1c976a5f02792dc6595a4d199b7b70d85 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 8 Mar 2017 13:52:17 +0200 Subject: wil6210: use WMI_DISCONNECT_CMDID upon connect timeout Upon connect timeout driver invokes _wil6210_disconnect() which iterates over sta array and disconnects each connected sta. In practice, because the connection is still ongoing and because cid is not yet allocated, disconnect is not actually happening. This leaves FW in connecting state while driver is in disconnected state. To fix this, upon connect timeout, explicitly send WMI_DISCONNECT_CMDID to FW to make sure it gets disconnected. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 5d45faca5d51..79aeb2b80055 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -306,10 +306,34 @@ static void wil_disconnect_worker(struct work_struct *work) { struct wil6210_priv *wil = container_of(work, struct wil6210_priv, disconnect_worker); + struct net_device *ndev = wil_to_ndev(wil); + int rc; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_disconnect_event evt; + } __packed reply; - mutex_lock(&wil->mutex); - _wil6210_disconnect(wil, NULL, WLAN_REASON_UNSPECIFIED, false); - mutex_unlock(&wil->mutex); + if (test_bit(wil_status_fwconnected, wil->status)) + /* connect succeeded after all */ + return; + + if (!test_bit(wil_status_fwconnecting, wil->status)) + /* already disconnected */ + return; + + rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0, + WMI_DISCONNECT_EVENTID, &reply, sizeof(reply), + WIL6210_DISCONNECT_TO_MS); + if (rc) { + wil_err(wil, "disconnect error %d\n", rc); + return; + } + + wil_update_net_queues_bh(wil, NULL, true); + netif_carrier_off(ndev); + cfg80211_connect_result(ndev, NULL, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); + clear_bit(wil_status_fwconnecting, wil->status); } static void wil_connect_timer_fn(ulong x) -- cgit From 3b56c15fa3315942b4cb02e57ff842f2f9b14ef7 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 8 Mar 2017 13:52:18 +0200 Subject: wil6210: correctly report locally generated disconnect in STA mode Driver always invoke cfg80211_disconnected() with locally_generated as false. Fix this by reporting true whenever the disconnect is triggered from upper layers (cfg80211) or from within the driver itself (reset, deinit). Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 1 + drivers/net/wireless/ath/wil6210/main.c | 5 ++++- drivers/net/wireless/ath/wil6210/wil6210.h | 1 + drivers/net/wireless/ath/wil6210/wmi.c | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 39ee565ecf56..1981ec2e0186 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -710,6 +710,7 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy, return 0; } + wil->locally_generated_disc = true; rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0, WMI_DISCONNECT_EVENTID, NULL, 0, WIL6210_DISCONNECT_TO_MS); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 79aeb2b80055..3206aea00ccd 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -279,7 +279,10 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, if (test_bit(wil_status_fwconnected, wil->status)) { clear_bit(wil_status_fwconnected, wil->status); cfg80211_disconnected(ndev, reason_code, - NULL, 0, false, GFP_KERNEL); + NULL, 0, + wil->locally_generated_disc, + GFP_KERNEL); + wil->locally_generated_disc = false; } else if (test_bit(wil_status_fwconnecting, wil->status)) { cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 09e2fcef4f07..cb825ccedbe0 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -614,6 +614,7 @@ struct wil6210_priv { int sinfo_gen; u32 ap_isolate; /* no intra-BSS communication */ struct cfg80211_bss *bss; /* connected bss, relevant in STA mode */ + int locally_generated_disc; /* relevant in STA mode */ /* interrupt moderation */ u32 tx_max_burst_duration; u32 tx_interframe_timeout; diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 02ccdaee6da9..9255c47af15a 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1497,6 +1497,7 @@ int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, wil_dbg_wmi(wil, "disconnect_sta: (%pM, reason %d)\n", mac, reason); + wil->locally_generated_disc = true; if (del_sta) { ether_addr_copy(del_sta_cmd.dst_mac, mac); rc = wmi_call(wil, WMI_DEL_STA_CMDID, &del_sta_cmd, -- cgit From b8c31b5d6c232a36c821bdd556b60c033a1c1577 Mon Sep 17 00:00:00 2001 From: Lior David Date: Wed, 8 Mar 2017 13:52:19 +0200 Subject: wil6210: add oob_mode for AP certification Add a new value to the oob_mode module parameter for supporting AP certification. All enabled values of oob_mode (>0) are intended only for debugging and diagnostics. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 24 ++++++++++++++++++------ drivers/net/wireless/ath/wil6210/wil6210.h | 1 + 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 3206aea00ccd..c33cc4ad44c4 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -30,8 +30,8 @@ bool debug_fw; /* = false; */ module_param(debug_fw, bool, 0444); MODULE_PARM_DESC(debug_fw, " do not perform card reset. For FW debug"); -static bool oob_mode; -module_param(oob_mode, bool, 0444); +static u8 oob_mode; +module_param(oob_mode, byte, 0444); MODULE_PARM_DESC(oob_mode, " enable out of the box (OOB) mode in FW, for diagnostics and certification"); @@ -642,13 +642,25 @@ static inline void wil_release_cpu(struct wil6210_priv *wil) wil_w(wil, RGF_USER_USER_CPU_0, 1); } -static void wil_set_oob_mode(struct wil6210_priv *wil, bool enable) +static void wil_set_oob_mode(struct wil6210_priv *wil, u8 mode) { - wil_info(wil, "enable=%d\n", enable); - if (enable) + wil_info(wil, "oob_mode to %d\n", mode); + switch (mode) { + case 0: + wil_c(wil, RGF_USER_USAGE_6, BIT_USER_OOB_MODE | + BIT_USER_OOB_R2_MODE); + break; + case 1: + wil_c(wil, RGF_USER_USAGE_6, BIT_USER_OOB_R2_MODE); wil_s(wil, RGF_USER_USAGE_6, BIT_USER_OOB_MODE); - else + break; + case 2: wil_c(wil, RGF_USER_USAGE_6, BIT_USER_OOB_MODE); + wil_s(wil, RGF_USER_USAGE_6, BIT_USER_OOB_R2_MODE); + break; + default: + wil_err(wil, "invalid oob_mode: %d\n", mode); + } } static int wil_target_reset(struct wil6210_priv *wil) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index cb825ccedbe0..fee18916b713 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -140,6 +140,7 @@ struct RGF_ICR { #define RGF_USER_USAGE_1 (0x880004) #define RGF_USER_USAGE_6 (0x880018) #define BIT_USER_OOB_MODE BIT(31) + #define BIT_USER_OOB_R2_MODE BIT(30) #define RGF_USER_HW_MACHINE_STATE (0x8801dc) #define HW_MACHINE_BOOT_DONE (0x3fffffd) #define RGF_USER_USER_CPU_0 (0x8801e0) -- cgit From 69ba943151b2e40e201700cf5b3a94e433c6fd83 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Mar 2017 08:17:06 -0800 Subject: mlx4: dma_dir is a mlx4_en_priv attribute No need to duplicate it for all queues and frags. num_frags & log_rx_info become u8 to save space. u8 accesses are a bit faster than u16 anyway. Signed-off-by: Eric Dumazet Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 16 ++++++++-------- drivers/net/ethernet/mellanox/mlx4/en_tx.c | 2 +- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 867292880c07..6183128b2d3d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -72,7 +72,7 @@ static int mlx4_alloc_pages(struct mlx4_en_priv *priv, return -ENOMEM; } dma = dma_map_page(priv->ddev, page, 0, PAGE_SIZE << order, - frag_info->dma_dir); + priv->dma_dir); if (unlikely(dma_mapping_error(priv->ddev, dma))) { put_page(page); return -ENOMEM; @@ -128,7 +128,7 @@ out: if (page_alloc[i].page != ring_alloc[i].page) { dma_unmap_page(priv->ddev, page_alloc[i].dma, page_alloc[i].page_size, - priv->frag_info[i].dma_dir); + priv->dma_dir); page = page_alloc[i].page; /* Revert changes done by mlx4_alloc_pages */ page_ref_sub(page, page_alloc[i].page_size / @@ -149,7 +149,7 @@ static void mlx4_en_free_frag(struct mlx4_en_priv *priv, if (next_frag_end > frags[i].page_size) dma_unmap_page(priv->ddev, frags[i].dma, frags[i].page_size, - frag_info->dma_dir); + priv->dma_dir); if (frags[i].page) put_page(frags[i].page); @@ -181,7 +181,7 @@ out: page_alloc = &ring->page_alloc[i]; dma_unmap_page(priv->ddev, page_alloc->dma, page_alloc->page_size, - priv->frag_info[i].dma_dir); + priv->dma_dir); page = page_alloc->page; /* Revert changes done by mlx4_alloc_pages */ page_ref_sub(page, page_alloc->page_size / @@ -206,7 +206,7 @@ static void mlx4_en_destroy_allocator(struct mlx4_en_priv *priv, i, page_count(page_alloc->page)); dma_unmap_page(priv->ddev, page_alloc->dma, - page_alloc->page_size, frag_info->dma_dir); + page_alloc->page_size, priv->dma_dir); while (page_alloc->page_offset + frag_info->frag_stride < page_alloc->page_size) { put_page(page_alloc->page); @@ -570,7 +570,7 @@ void mlx4_en_deactivate_rx_ring(struct mlx4_en_priv *priv, struct mlx4_en_rx_alloc *frame = &ring->page_cache.buf[i]; dma_unmap_page(priv->ddev, frame->dma, frame->page_size, - priv->frag_info[0].dma_dir); + priv->dma_dir); put_page(frame->page); } ring->page_cache.index = 0; @@ -1202,7 +1202,7 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) * expense of more costly truesize accounting */ priv->frag_info[0].frag_stride = PAGE_SIZE; - priv->frag_info[0].dma_dir = PCI_DMA_BIDIRECTIONAL; + priv->dma_dir = PCI_DMA_BIDIRECTIONAL; priv->frag_info[0].rx_headroom = XDP_PACKET_HEADROOM; i = 1; } else { @@ -1217,11 +1217,11 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) priv->frag_info[i].frag_stride = ALIGN(priv->frag_info[i].frag_size, SMP_CACHE_BYTES); - priv->frag_info[i].dma_dir = PCI_DMA_FROMDEVICE; priv->frag_info[i].rx_headroom = 0; buf_size += priv->frag_info[i].frag_size; i++; } + priv->dma_dir = PCI_DMA_FROMDEVICE; } priv->num_frags = i; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 3ed42199d3f1..98bc67a7249b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -360,7 +360,7 @@ u32 mlx4_en_recycle_tx_desc(struct mlx4_en_priv *priv, if (!mlx4_en_rx_recycle(ring->recycle_ring, &frame)) { dma_unmap_page(priv->ddev, tx_info->map0_dma, - PAGE_SIZE, priv->frag_info[0].dma_dir); + PAGE_SIZE, priv->dma_dir); put_page(tx_info->page); } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 3629ce11a68b..a4c7d94d52c6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -474,7 +474,6 @@ struct mlx4_en_frag_info { u16 frag_size; u16 frag_prefix_size; u32 frag_stride; - enum dma_data_direction dma_dir; u16 order; u16 rx_headroom; }; @@ -584,8 +583,9 @@ struct mlx4_en_priv { u32 rx_ring_num; u32 rx_skb_size; struct mlx4_en_frag_info frag_info[MLX4_EN_MAX_RX_FRAGS]; - u16 num_frags; - u16 log_rx_info; + u8 num_frags; + u8 log_rx_info; + u8 dma_dir; struct mlx4_en_tx_ring **tx_ring[MLX4_EN_NUM_TX_TYPES]; struct mlx4_en_rx_ring *rx_ring[MAX_RX_RINGS]; -- cgit From 159ddfd2ca30a2361324111ef64fdedf43e7dc32 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Mar 2017 08:17:07 -0800 Subject: mlx4: remove order field from mlx4_en_frag_info This is really a port attribute, no need to duplicate it per RX queue and per frag. Signed-off-by: Eric Dumazet Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 6 +++--- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 6183128b2d3d..b78d6762e03f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -59,7 +59,7 @@ static int mlx4_alloc_pages(struct mlx4_en_priv *priv, struct page *page; dma_addr_t dma; - for (order = frag_info->order; ;) { + for (order = priv->rx_page_order; ;) { gfp_t gfp = _gfp; if (order) @@ -1195,7 +1195,7 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) * This only works when num_frags == 1. */ if (priv->tx_ring_num[TX_XDP]) { - priv->frag_info[0].order = 0; + priv->rx_page_order = 0; priv->frag_info[0].frag_size = eff_mtu; priv->frag_info[0].frag_prefix_size = 0; /* This will gain efficient xdp frame recycling at the @@ -1209,7 +1209,6 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) int buf_size = 0; while (buf_size < eff_mtu) { - priv->frag_info[i].order = MLX4_EN_ALLOC_PREFER_ORDER; priv->frag_info[i].frag_size = (eff_mtu > buf_size + frag_sizes[i]) ? frag_sizes[i] : eff_mtu - buf_size; @@ -1221,6 +1220,7 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) buf_size += priv->frag_info[i].frag_size; i++; } + priv->rx_page_order = MLX4_EN_ALLOC_PREFER_ORDER; priv->dma_dir = PCI_DMA_FROMDEVICE; } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index a4c7d94d52c6..9d2c53ddb15c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -474,7 +474,6 @@ struct mlx4_en_frag_info { u16 frag_size; u16 frag_prefix_size; u32 frag_stride; - u16 order; u16 rx_headroom; }; @@ -586,6 +585,7 @@ struct mlx4_en_priv { u8 num_frags; u8 log_rx_info; u8 dma_dir; + u8 rx_page_order; struct mlx4_en_tx_ring **tx_ring[MLX4_EN_NUM_TX_TYPES]; struct mlx4_en_rx_ring *rx_ring[MAX_RX_RINGS]; -- cgit From aaca121dd6cfedc9afb9d0ee23ded9d84e217e20 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Mar 2017 08:17:08 -0800 Subject: mlx4: get rid of frag_prefix_size Using per frag storage for frag_prefix_size is really silly. mlx4_en_complete_rx_desc() has all needed info already. Signed-off-by: Eric Dumazet Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 27 ++++++++++++--------------- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 3 +-- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index b78d6762e03f..118ea83cff08 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -588,15 +588,14 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv, int length) { struct skb_frag_struct *skb_frags_rx = skb_shinfo(skb)->frags; - struct mlx4_en_frag_info *frag_info; - int nr; + struct mlx4_en_frag_info *frag_info = priv->frag_info; + int nr, frag_size; dma_addr_t dma; /* Collect used fragments while replacing them in the HW descriptors */ - for (nr = 0; nr < priv->num_frags; nr++) { - frag_info = &priv->frag_info[nr]; - if (length <= frag_info->frag_prefix_size) - break; + for (nr = 0;;) { + frag_size = min_t(int, length, frag_info->frag_size); + if (unlikely(!frags[nr].page)) goto fail; @@ -606,15 +605,16 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv, __skb_fill_page_desc(skb, nr, frags[nr].page, frags[nr].page_offset, - frag_info->frag_size); + frag_size); skb->truesize += frag_info->frag_stride; frags[nr].page = NULL; + nr++; + length -= frag_size; + if (!length) + break; + frag_info++; } - /* Adjust size of last fragment to match actual length */ - if (nr > 0) - skb_frag_size_set(&skb_frags_rx[nr - 1], - length - priv->frag_info[nr - 1].frag_prefix_size); return nr; fail: @@ -1197,7 +1197,6 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) if (priv->tx_ring_num[TX_XDP]) { priv->rx_page_order = 0; priv->frag_info[0].frag_size = eff_mtu; - priv->frag_info[0].frag_prefix_size = 0; /* This will gain efficient xdp frame recycling at the * expense of more costly truesize accounting */ @@ -1212,7 +1211,6 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) priv->frag_info[i].frag_size = (eff_mtu > buf_size + frag_sizes[i]) ? frag_sizes[i] : eff_mtu - buf_size; - priv->frag_info[i].frag_prefix_size = buf_size; priv->frag_info[i].frag_stride = ALIGN(priv->frag_info[i].frag_size, SMP_CACHE_BYTES); @@ -1232,10 +1230,9 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) eff_mtu, priv->num_frags); for (i = 0; i < priv->num_frags; i++) { en_err(priv, - " frag:%d - size:%d prefix:%d stride:%d\n", + " frag:%d - size:%d stride:%d\n", i, priv->frag_info[i].frag_size, - priv->frag_info[i].frag_prefix_size, priv->frag_info[i].frag_stride); } } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 9d2c53ddb15c..849aa8af4dd7 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -472,9 +472,8 @@ struct mlx4_en_mc_list { struct mlx4_en_frag_info { u16 frag_size; - u16 frag_prefix_size; - u32 frag_stride; u16 rx_headroom; + u32 frag_stride; }; #ifdef CONFIG_MLX4_EN_DCB -- cgit From d85f6c14e967b0bddfa9712daa17d79e297d18b8 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Mar 2017 08:17:09 -0800 Subject: mlx4: rx_headroom is a per port attribute No need to duplicate it per RX queue / frags. Signed-off-by: Eric Dumazet Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 6 +++--- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 118ea83cff08..bb33032a280f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -115,7 +115,7 @@ static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv, for (i = 0; i < priv->num_frags; i++) { frags[i] = ring_alloc[i]; - frags[i].page_offset += priv->frag_info[i].rx_headroom; + frags[i].page_offset += priv->rx_headroom; rx_desc->data[i].addr = cpu_to_be64(frags[i].dma + frags[i].page_offset); ring_alloc[i] = page_alloc[i]; @@ -1202,7 +1202,7 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) */ priv->frag_info[0].frag_stride = PAGE_SIZE; priv->dma_dir = PCI_DMA_BIDIRECTIONAL; - priv->frag_info[0].rx_headroom = XDP_PACKET_HEADROOM; + priv->rx_headroom = XDP_PACKET_HEADROOM; i = 1; } else { int buf_size = 0; @@ -1214,12 +1214,12 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) priv->frag_info[i].frag_stride = ALIGN(priv->frag_info[i].frag_size, SMP_CACHE_BYTES); - priv->frag_info[i].rx_headroom = 0; buf_size += priv->frag_info[i].frag_size; i++; } priv->rx_page_order = MLX4_EN_ALLOC_PREFER_ORDER; priv->dma_dir = PCI_DMA_FROMDEVICE; + priv->rx_headroom = 0; } priv->num_frags = i; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 849aa8af4dd7..fc7b4da5d8c3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -472,7 +472,6 @@ struct mlx4_en_mc_list { struct mlx4_en_frag_info { u16 frag_size; - u16 rx_headroom; u32 frag_stride; }; @@ -585,6 +584,7 @@ struct mlx4_en_priv { u8 log_rx_info; u8 dma_dir; u8 rx_page_order; + u16 rx_headroom; struct mlx4_en_tx_ring **tx_ring[MLX4_EN_NUM_TX_TYPES]; struct mlx4_en_rx_ring *rx_ring[MAX_RX_RINGS]; -- cgit From acd7628de05c73118aab7ae4847a5b5b2c6999c9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Mar 2017 08:17:10 -0800 Subject: mlx4: reduce rx ring page_cache size We only need to store the page and dma address. Signed-off-by: Eric Dumazet Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 17 ++++++++++------- drivers/net/ethernet/mellanox/mlx4/en_tx.c | 2 -- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 6 +++++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index bb33032a280f..453313d404e3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -250,7 +250,10 @@ static int mlx4_en_prepare_rx_desc(struct mlx4_en_priv *priv, (index << priv->log_rx_info); if (ring->page_cache.index > 0) { - frags[0] = ring->page_cache.buf[--ring->page_cache.index]; + ring->page_cache.index--; + frags[0].page = ring->page_cache.buf[ring->page_cache.index].page; + frags[0].dma = ring->page_cache.buf[ring->page_cache.index].dma; + frags[0].page_offset = XDP_PACKET_HEADROOM; rx_desc->data[0].addr = cpu_to_be64(frags[0].dma + frags[0].page_offset); return 0; @@ -537,7 +540,9 @@ bool mlx4_en_rx_recycle(struct mlx4_en_rx_ring *ring, if (cache->index >= MLX4_EN_CACHE_SIZE) return false; - cache->buf[cache->index++] = *frame; + cache->buf[cache->index].page = frame->page; + cache->buf[cache->index].dma = frame->dma; + cache->index++; return true; } @@ -567,11 +572,9 @@ void mlx4_en_deactivate_rx_ring(struct mlx4_en_priv *priv, int i; for (i = 0; i < ring->page_cache.index; i++) { - struct mlx4_en_rx_alloc *frame = &ring->page_cache.buf[i]; - - dma_unmap_page(priv->ddev, frame->dma, frame->page_size, - priv->dma_dir); - put_page(frame->page); + dma_unmap_page(priv->ddev, ring->page_cache.buf[i].dma, + PAGE_SIZE, priv->dma_dir); + put_page(ring->page_cache.buf[i].page); } ring->page_cache.index = 0; mlx4_en_free_rx_buf(priv, ring); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 98bc67a7249b..e0c5ffb3e3a6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -354,8 +354,6 @@ u32 mlx4_en_recycle_tx_desc(struct mlx4_en_priv *priv, struct mlx4_en_rx_alloc frame = { .page = tx_info->page, .dma = tx_info->map0_dma, - .page_offset = XDP_PACKET_HEADROOM, - .page_size = PAGE_SIZE, }; if (!mlx4_en_rx_recycle(ring->recycle_ring, &frame)) { diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index fc7b4da5d8c3..c98940c6acd0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -268,9 +268,13 @@ struct mlx4_en_rx_alloc { }; #define MLX4_EN_CACHE_SIZE (2 * NAPI_POLL_WEIGHT) + struct mlx4_en_page_cache { u32 index; - struct mlx4_en_rx_alloc buf[MLX4_EN_CACHE_SIZE]; + struct { + struct page *page; + dma_addr_t dma; + } buf[MLX4_EN_CACHE_SIZE]; }; struct mlx4_en_priv; -- cgit From 60c7f5ae5416a8491216bcccf6b3b3d842d69fa4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Mar 2017 08:17:11 -0800 Subject: mlx4: removal of frag_sizes[] We will soon use order-0 pages, and frag truesize will more precisely match real sizes. In the new model, we prefer to use <= 2048 bytes fragments, so that we can use page-recycle technique on PAGE_SIZE=4096 arches. We will still pack as much frames as possible on arches with big pages, like PowerPC. Signed-off-by: Eric Dumazet Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 24 ++++++++++-------------- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 8 -------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 453313d404e3..0c61c1200f2a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -1181,13 +1181,6 @@ int mlx4_en_poll_rx_cq(struct napi_struct *napi, int budget) return done; } -static const int frag_sizes[] = { - FRAG_SZ0, - FRAG_SZ1, - FRAG_SZ2, - FRAG_SZ3 -}; - void mlx4_en_calc_rx_buf(struct net_device *dev) { struct mlx4_en_priv *priv = netdev_priv(dev); @@ -1211,13 +1204,16 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) int buf_size = 0; while (buf_size < eff_mtu) { - priv->frag_info[i].frag_size = - (eff_mtu > buf_size + frag_sizes[i]) ? - frag_sizes[i] : eff_mtu - buf_size; - priv->frag_info[i].frag_stride = - ALIGN(priv->frag_info[i].frag_size, - SMP_CACHE_BYTES); - buf_size += priv->frag_info[i].frag_size; + int frag_size = eff_mtu - buf_size; + + if (i < MLX4_EN_MAX_RX_FRAGS - 1) + frag_size = min(frag_size, 2048); + + priv->frag_info[i].frag_size = frag_size; + + priv->frag_info[i].frag_stride = ALIGN(frag_size, + SMP_CACHE_BYTES); + buf_size += frag_size; i++; } priv->rx_page_order = MLX4_EN_ALLOC_PREFER_ORDER; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index c98940c6acd0..608396dc6d95 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -105,14 +105,6 @@ #define MLX4_EN_ALLOC_PREFER_ORDER min_t(int, get_order(32768), \ PAGE_ALLOC_COSTLY_ORDER) -/* Receive fragment sizes; we use at most 3 fragments (for 9600 byte MTU - * and 4K allocations) */ -enum { - FRAG_SZ0 = 1536 - NET_IP_ALIGN, - FRAG_SZ1 = 4096, - FRAG_SZ2 = 4096, - FRAG_SZ3 = MLX4_EN_ALLOC_SIZE -}; #define MLX4_EN_MAX_RX_FRAGS 4 /* Maximum ring sizes */ -- cgit From b5a54d9a313645ec9607dc557b67d9325c28884c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Mar 2017 08:17:12 -0800 Subject: mlx4: use order-0 pages for RX Use of order-3 pages is problematic in some cases. This patch might add three kinds of regression : 1) a CPU performance regression, but we will add later page recycling and performance should be back. 2) TCP receiver could grow its receive window slightly slower, because skb->len/skb->truesize ratio will decrease. This is mostly ok, we prefer being conservative to not risk OOM, and eventually tune TCP better in the future. This is consistent with other drivers using 2048 per ethernet frame. 3) Because we allocate one page per RX slot, we consume more memory for the ring buffers. XDP already had this constraint anyway. Signed-off-by: Eric Dumazet Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 72 +++++++++++++--------------- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 5 -- 2 files changed, 33 insertions(+), 44 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 0c61c1200f2a..069ea09185fb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -53,38 +53,26 @@ static int mlx4_alloc_pages(struct mlx4_en_priv *priv, struct mlx4_en_rx_alloc *page_alloc, const struct mlx4_en_frag_info *frag_info, - gfp_t _gfp) + gfp_t gfp) { - int order; struct page *page; dma_addr_t dma; - for (order = priv->rx_page_order; ;) { - gfp_t gfp = _gfp; - - if (order) - gfp |= __GFP_COMP | __GFP_NOWARN | __GFP_NOMEMALLOC; - page = alloc_pages(gfp, order); - if (likely(page)) - break; - if (--order < 0 || - ((PAGE_SIZE << order) < frag_info->frag_size)) - return -ENOMEM; - } - dma = dma_map_page(priv->ddev, page, 0, PAGE_SIZE << order, - priv->dma_dir); + page = alloc_page(gfp); + if (unlikely(!page)) + return -ENOMEM; + dma = dma_map_page(priv->ddev, page, 0, PAGE_SIZE, priv->dma_dir); if (unlikely(dma_mapping_error(priv->ddev, dma))) { put_page(page); return -ENOMEM; } - page_alloc->page_size = PAGE_SIZE << order; page_alloc->page = page; page_alloc->dma = dma; page_alloc->page_offset = 0; /* Not doing get_page() for each frag is a big win * on asymetric workloads. Note we can not use atomic_set(). */ - page_ref_add(page, page_alloc->page_size / frag_info->frag_stride - 1); + page_ref_add(page, PAGE_SIZE / frag_info->frag_stride - 1); return 0; } @@ -105,7 +93,7 @@ static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv, page_alloc[i].page_offset += frag_info->frag_stride; if (page_alloc[i].page_offset + frag_info->frag_stride <= - ring_alloc[i].page_size) + PAGE_SIZE) continue; if (unlikely(mlx4_alloc_pages(priv, &page_alloc[i], @@ -127,11 +115,10 @@ out: while (i--) { if (page_alloc[i].page != ring_alloc[i].page) { dma_unmap_page(priv->ddev, page_alloc[i].dma, - page_alloc[i].page_size, - priv->dma_dir); + PAGE_SIZE, priv->dma_dir); page = page_alloc[i].page; /* Revert changes done by mlx4_alloc_pages */ - page_ref_sub(page, page_alloc[i].page_size / + page_ref_sub(page, PAGE_SIZE / priv->frag_info[i].frag_stride - 1); put_page(page); } @@ -147,8 +134,8 @@ static void mlx4_en_free_frag(struct mlx4_en_priv *priv, u32 next_frag_end = frags[i].page_offset + 2 * frag_info->frag_stride; - if (next_frag_end > frags[i].page_size) - dma_unmap_page(priv->ddev, frags[i].dma, frags[i].page_size, + if (next_frag_end > PAGE_SIZE) + dma_unmap_page(priv->ddev, frags[i].dma, PAGE_SIZE, priv->dma_dir); if (frags[i].page) @@ -168,9 +155,8 @@ static int mlx4_en_init_allocator(struct mlx4_en_priv *priv, frag_info, GFP_KERNEL | __GFP_COLD)) goto out; - en_dbg(DRV, priv, " frag %d allocator: - size:%d frags:%d\n", - i, ring->page_alloc[i].page_size, - page_ref_count(ring->page_alloc[i].page)); + en_dbg(DRV, priv, " frag %d allocator: - frags:%d\n", + i, page_ref_count(ring->page_alloc[i].page)); } return 0; @@ -180,11 +166,10 @@ out: page_alloc = &ring->page_alloc[i]; dma_unmap_page(priv->ddev, page_alloc->dma, - page_alloc->page_size, - priv->dma_dir); + PAGE_SIZE, priv->dma_dir); page = page_alloc->page; /* Revert changes done by mlx4_alloc_pages */ - page_ref_sub(page, page_alloc->page_size / + page_ref_sub(page, PAGE_SIZE / priv->frag_info[i].frag_stride - 1); put_page(page); page_alloc->page = NULL; @@ -206,9 +191,9 @@ static void mlx4_en_destroy_allocator(struct mlx4_en_priv *priv, i, page_count(page_alloc->page)); dma_unmap_page(priv->ddev, page_alloc->dma, - page_alloc->page_size, priv->dma_dir); + PAGE_SIZE, priv->dma_dir); while (page_alloc->page_offset + frag_info->frag_stride < - page_alloc->page_size) { + PAGE_SIZE) { put_page(page_alloc->page); page_alloc->page_offset += frag_info->frag_stride; } @@ -1191,7 +1176,6 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) * This only works when num_frags == 1. */ if (priv->tx_ring_num[TX_XDP]) { - priv->rx_page_order = 0; priv->frag_info[0].frag_size = eff_mtu; /* This will gain efficient xdp frame recycling at the * expense of more costly truesize accounting @@ -1201,22 +1185,32 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) priv->rx_headroom = XDP_PACKET_HEADROOM; i = 1; } else { - int buf_size = 0; + int frag_size_max = 2048, buf_size = 0; + + /* should not happen, right ? */ + if (eff_mtu > PAGE_SIZE + (MLX4_EN_MAX_RX_FRAGS - 1) * 2048) + frag_size_max = PAGE_SIZE; while (buf_size < eff_mtu) { - int frag_size = eff_mtu - buf_size; + int frag_stride, frag_size = eff_mtu - buf_size; + int pad, nb; if (i < MLX4_EN_MAX_RX_FRAGS - 1) - frag_size = min(frag_size, 2048); + frag_size = min(frag_size, frag_size_max); priv->frag_info[i].frag_size = frag_size; + frag_stride = ALIGN(frag_size, SMP_CACHE_BYTES); + /* We can only pack 2 1536-bytes frames in on 4K page + * Therefore, each frame would consume more bytes (truesize) + */ + nb = PAGE_SIZE / frag_stride; + pad = (PAGE_SIZE - nb * frag_stride) / nb; + pad &= ~(SMP_CACHE_BYTES - 1); + priv->frag_info[i].frag_stride = frag_stride + pad; - priv->frag_info[i].frag_stride = ALIGN(frag_size, - SMP_CACHE_BYTES); buf_size += frag_size; i++; } - priv->rx_page_order = MLX4_EN_ALLOC_PREFER_ORDER; priv->dma_dir = PCI_DMA_FROMDEVICE; priv->rx_headroom = 0; } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 608396dc6d95..6c80117006ed 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -102,9 +102,6 @@ /* Use the maximum between 16384 and a single page */ #define MLX4_EN_ALLOC_SIZE PAGE_ALIGN(16384) -#define MLX4_EN_ALLOC_PREFER_ORDER min_t(int, get_order(32768), \ - PAGE_ALLOC_COSTLY_ORDER) - #define MLX4_EN_MAX_RX_FRAGS 4 /* Maximum ring sizes */ @@ -256,7 +253,6 @@ struct mlx4_en_rx_alloc { struct page *page; dma_addr_t dma; u32 page_offset; - u32 page_size; }; #define MLX4_EN_CACHE_SIZE (2 * NAPI_POLL_WEIGHT) @@ -579,7 +575,6 @@ struct mlx4_en_priv { u8 num_frags; u8 log_rx_info; u8 dma_dir; - u8 rx_page_order; u16 rx_headroom; struct mlx4_en_tx_ring **tx_ring[MLX4_EN_NUM_TX_TYPES]; -- cgit From 34db548bfb9580f33d9a7faecafe4da61a4428a3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Mar 2017 08:17:13 -0800 Subject: mlx4: add page recycling in receive path Same technique than some Intel drivers, for arches where PAGE_SIZE = 4096 In most cases, pages are reused because they were consumed before we could loop around the RX ring. This brings back performance, and is even better, a single TCP flow reaches 30Gbit on my hosts. v2: added full memset() in mlx4_en_free_frag(), as Tariq found it was needed if we switch to large MTU, as priv->log_rx_info can dynamically be changed. Signed-off-by: Eric Dumazet Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 258 +++++++++------------------ drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 1 - 2 files changed, 82 insertions(+), 177 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 069ea09185fb..5edd0cf2999c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -50,10 +50,9 @@ #include "mlx4_en.h" -static int mlx4_alloc_pages(struct mlx4_en_priv *priv, - struct mlx4_en_rx_alloc *page_alloc, - const struct mlx4_en_frag_info *frag_info, - gfp_t gfp) +static int mlx4_alloc_page(struct mlx4_en_priv *priv, + struct mlx4_en_rx_alloc *frag, + gfp_t gfp) { struct page *page; dma_addr_t dma; @@ -63,145 +62,46 @@ static int mlx4_alloc_pages(struct mlx4_en_priv *priv, return -ENOMEM; dma = dma_map_page(priv->ddev, page, 0, PAGE_SIZE, priv->dma_dir); if (unlikely(dma_mapping_error(priv->ddev, dma))) { - put_page(page); + __free_page(page); return -ENOMEM; } - page_alloc->page = page; - page_alloc->dma = dma; - page_alloc->page_offset = 0; - /* Not doing get_page() for each frag is a big win - * on asymetric workloads. Note we can not use atomic_set(). - */ - page_ref_add(page, PAGE_SIZE / frag_info->frag_stride - 1); + frag->page = page; + frag->dma = dma; + frag->page_offset = priv->rx_headroom; return 0; } static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv, struct mlx4_en_rx_desc *rx_desc, struct mlx4_en_rx_alloc *frags, - struct mlx4_en_rx_alloc *ring_alloc, gfp_t gfp) { - struct mlx4_en_rx_alloc page_alloc[MLX4_EN_MAX_RX_FRAGS]; - const struct mlx4_en_frag_info *frag_info; - struct page *page; int i; - for (i = 0; i < priv->num_frags; i++) { - frag_info = &priv->frag_info[i]; - page_alloc[i] = ring_alloc[i]; - page_alloc[i].page_offset += frag_info->frag_stride; - - if (page_alloc[i].page_offset + frag_info->frag_stride <= - PAGE_SIZE) - continue; - - if (unlikely(mlx4_alloc_pages(priv, &page_alloc[i], - frag_info, gfp))) - goto out; - } - - for (i = 0; i < priv->num_frags; i++) { - frags[i] = ring_alloc[i]; - frags[i].page_offset += priv->rx_headroom; - rx_desc->data[i].addr = cpu_to_be64(frags[i].dma + - frags[i].page_offset); - ring_alloc[i] = page_alloc[i]; - } - - return 0; - -out: - while (i--) { - if (page_alloc[i].page != ring_alloc[i].page) { - dma_unmap_page(priv->ddev, page_alloc[i].dma, - PAGE_SIZE, priv->dma_dir); - page = page_alloc[i].page; - /* Revert changes done by mlx4_alloc_pages */ - page_ref_sub(page, PAGE_SIZE / - priv->frag_info[i].frag_stride - 1); - put_page(page); - } - } - return -ENOMEM; -} - -static void mlx4_en_free_frag(struct mlx4_en_priv *priv, - struct mlx4_en_rx_alloc *frags, - int i) -{ - const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i]; - u32 next_frag_end = frags[i].page_offset + 2 * frag_info->frag_stride; - - - if (next_frag_end > PAGE_SIZE) - dma_unmap_page(priv->ddev, frags[i].dma, PAGE_SIZE, - priv->dma_dir); - - if (frags[i].page) - put_page(frags[i].page); -} - -static int mlx4_en_init_allocator(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring) -{ - int i; - struct mlx4_en_rx_alloc *page_alloc; - - for (i = 0; i < priv->num_frags; i++) { - const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i]; - - if (mlx4_alloc_pages(priv, &ring->page_alloc[i], - frag_info, GFP_KERNEL | __GFP_COLD)) - goto out; - - en_dbg(DRV, priv, " frag %d allocator: - frags:%d\n", - i, page_ref_count(ring->page_alloc[i].page)); + for (i = 0; i < priv->num_frags; i++, frags++) { + if (!frags->page && mlx4_alloc_page(priv, frags, gfp)) + return -ENOMEM; + rx_desc->data[i].addr = cpu_to_be64(frags->dma + + frags->page_offset); } return 0; - -out: - while (i--) { - struct page *page; - - page_alloc = &ring->page_alloc[i]; - dma_unmap_page(priv->ddev, page_alloc->dma, - PAGE_SIZE, priv->dma_dir); - page = page_alloc->page; - /* Revert changes done by mlx4_alloc_pages */ - page_ref_sub(page, PAGE_SIZE / - priv->frag_info[i].frag_stride - 1); - put_page(page); - page_alloc->page = NULL; - } - return -ENOMEM; } -static void mlx4_en_destroy_allocator(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring) +static void mlx4_en_free_frag(const struct mlx4_en_priv *priv, + struct mlx4_en_rx_alloc *frag) { - struct mlx4_en_rx_alloc *page_alloc; - int i; - - for (i = 0; i < priv->num_frags; i++) { - const struct mlx4_en_frag_info *frag_info = &priv->frag_info[i]; - - page_alloc = &ring->page_alloc[i]; - en_dbg(DRV, priv, "Freeing allocator:%d count:%d\n", - i, page_count(page_alloc->page)); - - dma_unmap_page(priv->ddev, page_alloc->dma, + if (frag->page) { + dma_unmap_page(priv->ddev, frag->dma, PAGE_SIZE, priv->dma_dir); - while (page_alloc->page_offset + frag_info->frag_stride < - PAGE_SIZE) { - put_page(page_alloc->page); - page_alloc->page_offset += frag_info->frag_stride; - } - page_alloc->page = NULL; + __free_page(frag->page); } + /* We need to clear all fields, otherwise a change of priv->log_rx_info + * could lead to see garbage later in frag->page. + */ + memset(frag, 0, sizeof(*frag)); } -static void mlx4_en_init_rx_desc(struct mlx4_en_priv *priv, +static void mlx4_en_init_rx_desc(const struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring, int index) { struct mlx4_en_rx_desc *rx_desc = ring->buf + ring->stride * index; @@ -235,19 +135,22 @@ static int mlx4_en_prepare_rx_desc(struct mlx4_en_priv *priv, (index << priv->log_rx_info); if (ring->page_cache.index > 0) { - ring->page_cache.index--; - frags[0].page = ring->page_cache.buf[ring->page_cache.index].page; - frags[0].dma = ring->page_cache.buf[ring->page_cache.index].dma; - frags[0].page_offset = XDP_PACKET_HEADROOM; - rx_desc->data[0].addr = cpu_to_be64(frags[0].dma + - frags[0].page_offset); + /* XDP uses a single page per frame */ + if (!frags->page) { + ring->page_cache.index--; + frags->page = ring->page_cache.buf[ring->page_cache.index].page; + frags->dma = ring->page_cache.buf[ring->page_cache.index].dma; + } + frags->page_offset = XDP_PACKET_HEADROOM; + rx_desc->data[0].addr = cpu_to_be64(frags->dma + + XDP_PACKET_HEADROOM); return 0; } - return mlx4_en_alloc_frags(priv, rx_desc, frags, ring->page_alloc, gfp); + return mlx4_en_alloc_frags(priv, rx_desc, frags, gfp); } -static inline bool mlx4_en_is_ring_empty(struct mlx4_en_rx_ring *ring) +static bool mlx4_en_is_ring_empty(const struct mlx4_en_rx_ring *ring) { return ring->prod == ring->cons; } @@ -257,7 +160,8 @@ static inline void mlx4_en_update_rx_prod_db(struct mlx4_en_rx_ring *ring) *ring->wqres.db.db = cpu_to_be32(ring->prod & 0xffff); } -static void mlx4_en_free_rx_desc(struct mlx4_en_priv *priv, +/* slow path */ +static void mlx4_en_free_rx_desc(const struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring, int index) { @@ -267,7 +171,7 @@ static void mlx4_en_free_rx_desc(struct mlx4_en_priv *priv, frags = ring->rx_info + (index << priv->log_rx_info); for (nr = 0; nr < priv->num_frags; nr++) { en_dbg(DRV, priv, "Freeing fragment:%d\n", nr); - mlx4_en_free_frag(priv, frags, nr); + mlx4_en_free_frag(priv, frags + nr); } } @@ -323,12 +227,12 @@ static void mlx4_en_free_rx_buf(struct mlx4_en_priv *priv, ring->cons, ring->prod); /* Unmap and free Rx buffers */ - while (!mlx4_en_is_ring_empty(ring)) { - index = ring->cons & ring->size_mask; + for (index = 0; index < ring->size; index++) { en_dbg(DRV, priv, "Processing descriptor:%d\n", index); mlx4_en_free_rx_desc(priv, ring, index); - ++ring->cons; } + ring->cons = 0; + ring->prod = 0; } void mlx4_en_set_num_rx_rings(struct mlx4_en_dev *mdev) @@ -380,9 +284,9 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv, tmp = size * roundup_pow_of_two(MLX4_EN_MAX_RX_FRAGS * sizeof(struct mlx4_en_rx_alloc)); - ring->rx_info = vmalloc_node(tmp, node); + ring->rx_info = vzalloc_node(tmp, node); if (!ring->rx_info) { - ring->rx_info = vmalloc(tmp); + ring->rx_info = vzalloc(tmp); if (!ring->rx_info) { err = -ENOMEM; goto err_ring; @@ -452,16 +356,6 @@ int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv) /* Initialize all descriptors */ for (i = 0; i < ring->size; i++) mlx4_en_init_rx_desc(priv, ring, i); - - /* Initialize page allocators */ - err = mlx4_en_init_allocator(priv, ring); - if (err) { - en_err(priv, "Failed initializing ring allocator\n"); - if (ring->stride <= TXBB_SIZE) - ring->buf -= TXBB_SIZE; - ring_ind--; - goto err_allocator; - } } err = mlx4_en_fill_rx_buffers(priv); if (err) @@ -481,11 +375,9 @@ err_buffers: mlx4_en_free_rx_buf(priv, priv->rx_ring[ring_ind]); ring_ind = priv->rx_ring_num - 1; -err_allocator: while (ring_ind >= 0) { if (priv->rx_ring[ring_ind]->stride <= TXBB_SIZE) priv->rx_ring[ring_ind]->buf -= TXBB_SIZE; - mlx4_en_destroy_allocator(priv, priv->rx_ring[ring_ind]); ring_ind--; } return err; @@ -565,50 +457,68 @@ void mlx4_en_deactivate_rx_ring(struct mlx4_en_priv *priv, mlx4_en_free_rx_buf(priv, ring); if (ring->stride <= TXBB_SIZE) ring->buf -= TXBB_SIZE; - mlx4_en_destroy_allocator(priv, ring); } static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv, - struct mlx4_en_rx_desc *rx_desc, struct mlx4_en_rx_alloc *frags, struct sk_buff *skb, int length) { - struct skb_frag_struct *skb_frags_rx = skb_shinfo(skb)->frags; - struct mlx4_en_frag_info *frag_info = priv->frag_info; + const struct mlx4_en_frag_info *frag_info = priv->frag_info; + unsigned int truesize = 0; int nr, frag_size; + struct page *page; dma_addr_t dma; + bool release; /* Collect used fragments while replacing them in the HW descriptors */ - for (nr = 0;;) { + for (nr = 0;; frags++) { frag_size = min_t(int, length, frag_info->frag_size); - if (unlikely(!frags[nr].page)) + page = frags->page; + if (unlikely(!page)) goto fail; - dma = be64_to_cpu(rx_desc->data[nr].addr); - dma_sync_single_for_cpu(priv->ddev, dma, frag_info->frag_size, - DMA_FROM_DEVICE); + dma = frags->dma; + dma_sync_single_range_for_cpu(priv->ddev, dma, frags->page_offset, + frag_size, priv->dma_dir); - __skb_fill_page_desc(skb, nr, frags[nr].page, - frags[nr].page_offset, + __skb_fill_page_desc(skb, nr, page, frags->page_offset, frag_size); - skb->truesize += frag_info->frag_stride; - frags[nr].page = NULL; + truesize += frag_info->frag_stride; + if (frag_info->frag_stride == PAGE_SIZE / 2) { + frags->page_offset ^= PAGE_SIZE / 2; + release = page_count(page) != 1 || + page_is_pfmemalloc(page) || + page_to_nid(page) != numa_mem_id(); + } else { + u32 sz_align = ALIGN(frag_size, SMP_CACHE_BYTES); + + frags->page_offset += sz_align; + release = frags->page_offset + frag_info->frag_size > PAGE_SIZE; + } + if (release) { + dma_unmap_page(priv->ddev, dma, PAGE_SIZE, priv->dma_dir); + frags->page = NULL; + } else { + page_ref_inc(page); + } + nr++; length -= frag_size; if (!length) break; frag_info++; } + skb->truesize += truesize; return nr; fail: while (nr > 0) { nr--; - __skb_frag_unref(&skb_frags_rx[nr]); + __skb_frag_unref(skb_shinfo(skb)->frags + nr); } return 0; } @@ -639,7 +549,8 @@ static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv, if (length <= SMALL_PACKET_SIZE) { /* We are copying all relevant data to the skb - temporarily * sync buffers for the copy */ - dma = be64_to_cpu(rx_desc->data[0].addr); + + dma = frags[0].dma + frags[0].page_offset; dma_sync_single_for_cpu(priv->ddev, dma, length, DMA_FROM_DEVICE); skb_copy_to_linear_data(skb, va, length); @@ -648,7 +559,7 @@ static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv, unsigned int pull_len; /* Move relevant fragments to skb */ - used_frags = mlx4_en_complete_rx_desc(priv, rx_desc, frags, + used_frags = mlx4_en_complete_rx_desc(priv, frags, skb, length); if (unlikely(!used_frags)) { kfree_skb(skb); @@ -916,8 +827,10 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud case XDP_TX: if (likely(!mlx4_en_xmit_frame(ring, frags, dev, length, cq->ring, - &doorbell_pending))) - goto consumed; + &doorbell_pending))) { + frags[0].page = NULL; + goto next; + } trace_xdp_exception(dev, xdp_prog, act); goto xdp_drop_no_cnt; /* Drop on xmit failure */ default: @@ -927,8 +840,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud case XDP_DROP: ring->xdp_drop++; xdp_drop_no_cnt: - if (likely(mlx4_en_rx_recycle(ring, frags))) - goto consumed; goto next; } } @@ -974,9 +885,8 @@ xdp_drop_no_cnt: if (!gro_skb) goto next; - nr = mlx4_en_complete_rx_desc(priv, - rx_desc, frags, gro_skb, - length); + nr = mlx4_en_complete_rx_desc(priv, frags, gro_skb, + length); if (!nr) goto next; @@ -1084,10 +994,6 @@ xdp_drop_no_cnt: napi_gro_receive(&cq->napi, skb); next: - for (nr = 0; nr < priv->num_frags; nr++) - mlx4_en_free_frag(priv, frags, nr); - -consumed: ++cq->mcq.cons_index; index = (cq->mcq.cons_index) & ring->size_mask; cqe = mlx4_en_get_cqe(cq->buf, index, priv->cqe_size) + factor; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 6c80117006ed..5f6080266981 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -327,7 +327,6 @@ struct mlx4_en_rx_desc { struct mlx4_en_rx_ring { struct mlx4_hwq_resources wqres; - struct mlx4_en_rx_alloc page_alloc[MLX4_EN_MAX_RX_FRAGS]; u32 size ; /* number of Rx descs*/ u32 actual_size; u32 size_mask; -- cgit From 7d7bfc6a3f69d2debe104656fadd8d568fda0e5b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Mar 2017 08:17:14 -0800 Subject: mlx4: add rx_alloc_pages counter in ethtool -S This new counter tracks number of pages that we allocated for one port. lpaa24:~# ethtool -S eth0 | egrep 'rx_alloc_pages|rx_packets' rx_packets: 306755183 rx_alloc_pages: 932897 Signed-off-by: Eric Dumazet Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_port.c | 2 ++ drivers/net/ethernet/mellanox/mlx4/en_rx.c | 11 +++++++---- drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 1 + drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h | 2 +- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index c4d714fcc7da..ffbcb27c05e5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -117,7 +117,7 @@ static const char main_strings[][ETH_GSTRING_LEN] = { /* port statistics */ "tso_packets", "xmit_more", - "queue_stopped", "wake_queue", "tx_timeout", "rx_alloc_failed", + "queue_stopped", "wake_queue", "tx_timeout", "rx_alloc_pages", "rx_csum_good", "rx_csum_none", "rx_csum_complete", "tx_chksum_offload", /* pf statistics */ diff --git a/drivers/net/ethernet/mellanox/mlx4/en_port.c b/drivers/net/ethernet/mellanox/mlx4/en_port.c index 9166d90e7328..e0eb695318e6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_port.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_port.c @@ -213,6 +213,7 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) priv->port_stats.rx_chksum_good = 0; priv->port_stats.rx_chksum_none = 0; priv->port_stats.rx_chksum_complete = 0; + priv->port_stats.rx_alloc_pages = 0; priv->xdp_stats.rx_xdp_drop = 0; priv->xdp_stats.rx_xdp_tx = 0; priv->xdp_stats.rx_xdp_tx_full = 0; @@ -223,6 +224,7 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) priv->port_stats.rx_chksum_good += READ_ONCE(ring->csum_ok); priv->port_stats.rx_chksum_none += READ_ONCE(ring->csum_none); priv->port_stats.rx_chksum_complete += READ_ONCE(ring->csum_complete); + priv->port_stats.rx_alloc_pages += READ_ONCE(ring->rx_alloc_pages); priv->xdp_stats.rx_xdp_drop += READ_ONCE(ring->xdp_drop); priv->xdp_stats.rx_xdp_tx += READ_ONCE(ring->xdp_tx); priv->xdp_stats.rx_xdp_tx_full += READ_ONCE(ring->xdp_tx_full); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 5edd0cf2999c..d3a425fa46b3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -72,6 +72,7 @@ static int mlx4_alloc_page(struct mlx4_en_priv *priv, } static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv, + struct mlx4_en_rx_ring *ring, struct mlx4_en_rx_desc *rx_desc, struct mlx4_en_rx_alloc *frags, gfp_t gfp) @@ -79,8 +80,11 @@ static int mlx4_en_alloc_frags(struct mlx4_en_priv *priv, int i; for (i = 0; i < priv->num_frags; i++, frags++) { - if (!frags->page && mlx4_alloc_page(priv, frags, gfp)) - return -ENOMEM; + if (!frags->page) { + if (mlx4_alloc_page(priv, frags, gfp)) + return -ENOMEM; + ring->rx_alloc_pages++; + } rx_desc->data[i].addr = cpu_to_be64(frags->dma + frags->page_offset); } @@ -133,7 +137,6 @@ static int mlx4_en_prepare_rx_desc(struct mlx4_en_priv *priv, struct mlx4_en_rx_desc *rx_desc = ring->buf + (index * ring->stride); struct mlx4_en_rx_alloc *frags = ring->rx_info + (index << priv->log_rx_info); - if (ring->page_cache.index > 0) { /* XDP uses a single page per frame */ if (!frags->page) { @@ -147,7 +150,7 @@ static int mlx4_en_prepare_rx_desc(struct mlx4_en_priv *priv, return 0; } - return mlx4_en_alloc_frags(priv, rx_desc, frags, gfp); + return mlx4_en_alloc_frags(priv, ring, rx_desc, frags, gfp); } static bool mlx4_en_is_ring_empty(const struct mlx4_en_rx_ring *ring) diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 5f6080266981..39f401aa3047 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -346,6 +346,7 @@ struct mlx4_en_rx_ring { unsigned long csum_ok; unsigned long csum_none; unsigned long csum_complete; + unsigned long rx_alloc_pages; unsigned long xdp_drop; unsigned long xdp_tx; unsigned long xdp_tx_full; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h index 48641cb0367f..926f3c3f3665 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_stats.h @@ -37,7 +37,7 @@ struct mlx4_en_port_stats { unsigned long queue_stopped; unsigned long wake_queue; unsigned long tx_timeout; - unsigned long rx_alloc_failed; + unsigned long rx_alloc_pages; unsigned long rx_chksum_good; unsigned long rx_chksum_none; unsigned long rx_chksum_complete; -- cgit From 9e8c0395a7e87a8f0eb008297df25ffdf3e0e5b3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Mar 2017 08:17:15 -0800 Subject: mlx4: do not access rx_desc from mlx4_en_process_rx_cq() Instead of fetching dma address from rx_desc->data[0].addr, prefer using frags[0].dma + frags[0].page_offset to avoid a potential cache line miss. Signed-off-by: Eric Dumazet Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index d3a425fa46b3..b62fa265890e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -528,7 +528,6 @@ fail: static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv, - struct mlx4_en_rx_desc *rx_desc, struct mlx4_en_rx_alloc *frags, unsigned int length) { @@ -703,7 +702,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud struct mlx4_cqe *cqe; struct mlx4_en_rx_ring *ring = priv->rx_ring[cq->ring]; struct mlx4_en_rx_alloc *frags; - struct mlx4_en_rx_desc *rx_desc; struct bpf_prog *xdp_prog; int doorbell_pending; struct sk_buff *skb; @@ -738,7 +736,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud cq->mcq.cons_index & cq->size)) { frags = ring->rx_info + (index << priv->log_rx_info); - rx_desc = ring->buf + (index << ring->log_stride); /* * make sure we read the CQE after we read the ownership bit @@ -767,7 +764,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud /* Get pointer to first fragment since we haven't * skb yet and cast it to ethhdr struct */ - dma = be64_to_cpu(rx_desc->data[0].addr); + dma = frags[0].dma + frags[0].page_offset; dma_sync_single_for_cpu(priv->ddev, dma, sizeof(*ethh), DMA_FROM_DEVICE); ethh = (struct ethhdr *)(page_address(frags[0].page) + @@ -806,7 +803,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud void *orig_data; u32 act; - dma = be64_to_cpu(rx_desc->data[0].addr); + dma = frags[0].dma + frags[0].page_offset; dma_sync_single_for_cpu(priv->ddev, dma, priv->frag_info[0].frag_size, DMA_FROM_DEVICE); @@ -946,7 +943,7 @@ xdp_drop_no_cnt: } /* GRO not possible, complete processing here */ - skb = mlx4_en_rx_skb(priv, rx_desc, frags, length); + skb = mlx4_en_rx_skb(priv, frags, length); if (unlikely(!skb)) { ring->dropped++; goto next; -- cgit From 02e6fd3e5598bace683e05ee783f134722cb21b4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Mar 2017 08:17:16 -0800 Subject: mlx4: factorize page_address() calls We need to compute the frame virtual address at different points. Do it once. Following patch will use the new va address for validate_loopback() Signed-off-by: Eric Dumazet Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index b62fa265890e..b5aa3f986508 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -734,9 +734,10 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud /* Process all completed CQEs */ while (XNOR(cqe->owner_sr_opcode & MLX4_CQE_OWNER_MASK, cq->mcq.cons_index & cq->size)) { + void *va; frags = ring->rx_info + (index << priv->log_rx_info); - + va = page_address(frags[0].page) + frags[0].page_offset; /* * make sure we read the CQE after we read the ownership bit */ @@ -759,7 +760,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud * and not performing the selftest or flb disabled */ if (priv->flags & MLX4_EN_FLAG_RX_FILTER_NEEDED) { - struct ethhdr *ethh; + const struct ethhdr *ethh = va; dma_addr_t dma; /* Get pointer to first fragment since we haven't * skb yet and cast it to ethhdr struct @@ -767,8 +768,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud dma = frags[0].dma + frags[0].page_offset; dma_sync_single_for_cpu(priv->ddev, dma, sizeof(*ethh), DMA_FROM_DEVICE); - ethh = (struct ethhdr *)(page_address(frags[0].page) + - frags[0].page_offset); if (is_multicast_ether_addr(ethh->h_dest)) { struct mlx4_mac_entry *entry; @@ -808,8 +807,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud priv->frag_info[0].frag_size, DMA_FROM_DEVICE); - xdp.data_hard_start = page_address(frags[0].page); - xdp.data = xdp.data_hard_start + frags[0].page_offset; + xdp.data_hard_start = va - frags[0].page_offset; + xdp.data = va; xdp.data_end = xdp.data + length; orig_data = xdp.data; @@ -819,6 +818,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud length = xdp.data_end - xdp.data; frags[0].page_offset = xdp.data - xdp.data_hard_start; + va = xdp.data; } switch (act) { @@ -891,7 +891,6 @@ xdp_drop_no_cnt: goto next; if (ip_summed == CHECKSUM_COMPLETE) { - void *va = skb_frag_address(skb_shinfo(gro_skb)->frags); if (check_csum(cqe, gro_skb, va, dev->features)) { ip_summed = CHECKSUM_NONE; @@ -955,7 +954,7 @@ xdp_drop_no_cnt: } if (ip_summed == CHECKSUM_COMPLETE) { - if (check_csum(cqe, skb, skb->data, dev->features)) { + if (check_csum(cqe, skb, va, dev->features)) { ip_summed = CHECKSUM_NONE; ring->csum_complete--; ring->csum_none++; -- cgit From 6969cf0fdbe9e67ed14cdf7ec95977abcf4a14af Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Mar 2017 08:17:17 -0800 Subject: mlx4: make validate_loopback() more generic Testing a boolean in fast path is not worth duplicating the code allocating packets, when GRO is on or off. If this proves to be a problem, we might later use a jump label. Next patch will remove this duplicated code and ease code review. Signed-off-by: Eric Dumazet Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 23 ++++++++++------------- drivers/net/ethernet/mellanox/mlx4/en_selftest.c | 6 ------ 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index b5aa3f986508..33b28278c827 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -584,20 +584,17 @@ static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv, return skb; } -static void validate_loopback(struct mlx4_en_priv *priv, struct sk_buff *skb) +static void validate_loopback(struct mlx4_en_priv *priv, void *va) { + const unsigned char *data = va + ETH_HLEN; int i; - int offset = ETH_HLEN; - for (i = 0; i < MLX4_LOOPBACK_TEST_PAYLOAD; i++, offset++) { - if (*(skb->data + offset) != (unsigned char) (i & 0xff)) - goto out_loopback; + for (i = 0; i < MLX4_LOOPBACK_TEST_PAYLOAD; i++) { + if (data[i] != (unsigned char)i) + return; } /* Loopback found */ priv->loopback_ok = 1; - -out_loopback: - dev_kfree_skb_any(skb); } static bool mlx4_en_refill_rx_buffers(struct mlx4_en_priv *priv, @@ -785,6 +782,11 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud } } + if (unlikely(priv->validate_loopback)) { + validate_loopback(priv, va); + goto next; + } + /* * Packet is OK - process it. */ @@ -948,11 +950,6 @@ xdp_drop_no_cnt: goto next; } - if (unlikely(priv->validate_loopback)) { - validate_loopback(priv, skb); - goto next; - } - if (ip_summed == CHECKSUM_COMPLETE) { if (check_csum(cqe, skb, va, dev->features)) { ip_summed = CHECKSUM_NONE; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_selftest.c b/drivers/net/ethernet/mellanox/mlx4/en_selftest.c index 95290e1fc9fe..17112faafbcc 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_selftest.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_selftest.c @@ -81,14 +81,11 @@ static int mlx4_en_test_loopback(struct mlx4_en_priv *priv) { u32 loopback_ok = 0; int i; - bool gro_enabled; priv->loopback_ok = 0; priv->validate_loopback = 1; - gro_enabled = priv->dev->features & NETIF_F_GRO; mlx4_en_update_loopback_state(priv->dev, priv->dev->features); - priv->dev->features &= ~NETIF_F_GRO; /* xmit */ if (mlx4_en_test_loopback_xmit(priv)) { @@ -111,9 +108,6 @@ mlx4_en_test_loopback_exit: priv->validate_loopback = 0; - if (gro_enabled) - priv->dev->features |= NETIF_F_GRO; - mlx4_en_update_loopback_state(priv->dev, priv->dev->features); return !loopback_ok; } -- cgit From 68b8df464406e39925a54955294d5994df46bcf4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 8 Mar 2017 08:17:18 -0800 Subject: mlx4: remove duplicate code in mlx4_en_process_rx_cq() We should keep one way to build skbs, regardless of GRO being on or off. Note that I made sure to defer as much as possible the point we need to pull data from the frame, so that future prefetch() we might add are more effective. These skb attributes derive from the CQE or ring : ip_summed, csum hash vlan offload hwtstamps queue_mapping As a bonus, this patch removes mlx4 dependency on eth_get_headlen() which is very often broken enough to give us headaches. Signed-off-by: Eric Dumazet Acked-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 209 ++++++----------------------- 1 file changed, 41 insertions(+), 168 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index 33b28278c827..aa074e57ce06 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -526,64 +526,6 @@ fail: return 0; } - -static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv, - struct mlx4_en_rx_alloc *frags, - unsigned int length) -{ - struct sk_buff *skb; - void *va; - int used_frags; - dma_addr_t dma; - - skb = netdev_alloc_skb(priv->dev, SMALL_PACKET_SIZE + NET_IP_ALIGN); - if (unlikely(!skb)) { - en_dbg(RX_ERR, priv, "Failed allocating skb\n"); - return NULL; - } - skb_reserve(skb, NET_IP_ALIGN); - skb->len = length; - - /* Get pointer to first fragment so we could copy the headers into the - * (linear part of the) skb */ - va = page_address(frags[0].page) + frags[0].page_offset; - - if (length <= SMALL_PACKET_SIZE) { - /* We are copying all relevant data to the skb - temporarily - * sync buffers for the copy */ - - dma = frags[0].dma + frags[0].page_offset; - dma_sync_single_for_cpu(priv->ddev, dma, length, - DMA_FROM_DEVICE); - skb_copy_to_linear_data(skb, va, length); - skb->tail += length; - } else { - unsigned int pull_len; - - /* Move relevant fragments to skb */ - used_frags = mlx4_en_complete_rx_desc(priv, frags, - skb, length); - if (unlikely(!used_frags)) { - kfree_skb(skb); - return NULL; - } - skb_shinfo(skb)->nr_frags = used_frags; - - pull_len = eth_get_headlen(va, SMALL_PACKET_SIZE); - /* Copy headers into the skb linear buffer */ - memcpy(skb->data, va, pull_len); - skb->tail += pull_len; - - /* Skip headers in first fragment */ - skb_shinfo(skb)->frags[0].page_offset += pull_len; - - /* Adjust size of first fragment */ - skb_frag_size_sub(&skb_shinfo(skb)->frags[0], pull_len); - skb->data_len = length - pull_len; - } - return skb; -} - static void validate_loopback(struct mlx4_en_priv *priv, void *va) { const unsigned char *data = va + ETH_HLEN; @@ -792,8 +734,6 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud */ length = be32_to_cpu(cqe->byte_cnt); length -= ring->fcs_del; - l2_tunnel = (dev->hw_enc_features & NETIF_F_RXCSUM) && - (cqe->vlan_my_qpn & cpu_to_be32(MLX4_CQE_L2_TUNNEL)); /* A bpf program gets first chance to drop the packet. It may * read bytes but not past the end of the frag. @@ -849,122 +789,51 @@ xdp_drop_no_cnt: ring->bytes += length; ring->packets++; + skb = napi_get_frags(&cq->napi); + if (!skb) + goto next; + + if (unlikely(ring->hwtstamp_rx_filter == HWTSTAMP_FILTER_ALL)) { + timestamp = mlx4_en_get_cqe_ts(cqe); + mlx4_en_fill_hwtstamps(mdev, skb_hwtstamps(skb), + timestamp); + } + skb_record_rx_queue(skb, cq->ring); + if (likely(dev->features & NETIF_F_RXCSUM)) { if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_TCP | MLX4_CQE_STATUS_UDP)) { if ((cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPOK)) && cqe->checksum == cpu_to_be16(0xffff)) { ip_summed = CHECKSUM_UNNECESSARY; + l2_tunnel = (dev->hw_enc_features & NETIF_F_RXCSUM) && + (cqe->vlan_my_qpn & cpu_to_be32(MLX4_CQE_L2_TUNNEL)); + if (l2_tunnel) + skb->csum_level = 1; ring->csum_ok++; } else { - ip_summed = CHECKSUM_NONE; - ring->csum_none++; + goto csum_none; } } else { if (priv->flags & MLX4_EN_FLAG_RX_CSUM_NON_TCP_UDP && (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV4 | MLX4_CQE_STATUS_IPV6))) { - ip_summed = CHECKSUM_COMPLETE; - ring->csum_complete++; + if (check_csum(cqe, skb, va, dev->features)) { + goto csum_none; + } else { + ip_summed = CHECKSUM_COMPLETE; + ring->csum_complete++; + } } else { - ip_summed = CHECKSUM_NONE; - ring->csum_none++; + goto csum_none; } } } else { +csum_none: ip_summed = CHECKSUM_NONE; ring->csum_none++; } - - /* This packet is eligible for GRO if it is: - * - DIX Ethernet (type interpretation) - * - TCP/IP (v4) - * - without IP options - * - not an IP fragment - */ - if (dev->features & NETIF_F_GRO) { - struct sk_buff *gro_skb = napi_get_frags(&cq->napi); - if (!gro_skb) - goto next; - - nr = mlx4_en_complete_rx_desc(priv, frags, gro_skb, - length); - if (!nr) - goto next; - - if (ip_summed == CHECKSUM_COMPLETE) { - if (check_csum(cqe, gro_skb, va, - dev->features)) { - ip_summed = CHECKSUM_NONE; - ring->csum_none++; - ring->csum_complete--; - } - } - - skb_shinfo(gro_skb)->nr_frags = nr; - gro_skb->len = length; - gro_skb->data_len = length; - gro_skb->ip_summed = ip_summed; - - if (l2_tunnel && ip_summed == CHECKSUM_UNNECESSARY) - gro_skb->csum_level = 1; - - if ((cqe->vlan_my_qpn & - cpu_to_be32(MLX4_CQE_CVLAN_PRESENT_MASK)) && - (dev->features & NETIF_F_HW_VLAN_CTAG_RX)) { - u16 vid = be16_to_cpu(cqe->sl_vid); - - __vlan_hwaccel_put_tag(gro_skb, htons(ETH_P_8021Q), vid); - } else if ((be32_to_cpu(cqe->vlan_my_qpn) & - MLX4_CQE_SVLAN_PRESENT_MASK) && - (dev->features & NETIF_F_HW_VLAN_STAG_RX)) { - __vlan_hwaccel_put_tag(gro_skb, - htons(ETH_P_8021AD), - be16_to_cpu(cqe->sl_vid)); - } - - if (dev->features & NETIF_F_RXHASH) - skb_set_hash(gro_skb, - be32_to_cpu(cqe->immed_rss_invalid), - (ip_summed == CHECKSUM_UNNECESSARY) ? - PKT_HASH_TYPE_L4 : - PKT_HASH_TYPE_L3); - - skb_record_rx_queue(gro_skb, cq->ring); - - if (ring->hwtstamp_rx_filter == HWTSTAMP_FILTER_ALL) { - timestamp = mlx4_en_get_cqe_ts(cqe); - mlx4_en_fill_hwtstamps(mdev, - skb_hwtstamps(gro_skb), - timestamp); - } - - napi_gro_frags(&cq->napi); - goto next; - } - - /* GRO not possible, complete processing here */ - skb = mlx4_en_rx_skb(priv, frags, length); - if (unlikely(!skb)) { - ring->dropped++; - goto next; - } - - if (ip_summed == CHECKSUM_COMPLETE) { - if (check_csum(cqe, skb, va, dev->features)) { - ip_summed = CHECKSUM_NONE; - ring->csum_complete--; - ring->csum_none++; - } - } - skb->ip_summed = ip_summed; - skb->protocol = eth_type_trans(skb, dev); - skb_record_rx_queue(skb, cq->ring); - - if (l2_tunnel && ip_summed == CHECKSUM_UNNECESSARY) - skb->csum_level = 1; - if (dev->features & NETIF_F_RXHASH) skb_set_hash(skb, be32_to_cpu(cqe->immed_rss_invalid), @@ -972,32 +841,36 @@ xdp_drop_no_cnt: PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3); - if ((be32_to_cpu(cqe->vlan_my_qpn) & - MLX4_CQE_CVLAN_PRESENT_MASK) && + + if ((cqe->vlan_my_qpn & + cpu_to_be32(MLX4_CQE_CVLAN_PRESENT_MASK)) && (dev->features & NETIF_F_HW_VLAN_CTAG_RX)) - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), be16_to_cpu(cqe->sl_vid)); - else if ((be32_to_cpu(cqe->vlan_my_qpn) & - MLX4_CQE_SVLAN_PRESENT_MASK) && + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + be16_to_cpu(cqe->sl_vid)); + else if ((cqe->vlan_my_qpn & + cpu_to_be32(MLX4_CQE_SVLAN_PRESENT_MASK)) && (dev->features & NETIF_F_HW_VLAN_STAG_RX)) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021AD), be16_to_cpu(cqe->sl_vid)); - if (ring->hwtstamp_rx_filter == HWTSTAMP_FILTER_ALL) { - timestamp = mlx4_en_get_cqe_ts(cqe); - mlx4_en_fill_hwtstamps(mdev, skb_hwtstamps(skb), - timestamp); + nr = mlx4_en_complete_rx_desc(priv, frags, skb, length); + if (likely(nr)) { + skb_shinfo(skb)->nr_frags = nr; + skb->len = length; + skb->data_len = length; + napi_gro_frags(&cq->napi); + } else { + skb->vlan_tci = 0; + skb_clear_hash(skb); } - - napi_gro_receive(&cq->napi, skb); next: ++cq->mcq.cons_index; index = (cq->mcq.cons_index) & ring->size_mask; cqe = mlx4_en_get_cqe(cq->buf, index, priv->cqe_size) + factor; if (++polled == budget) - goto out; + break; } -out: rcu_read_unlock(); if (polled) { -- cgit From aee441193ee2609070418af5843bff55907c713c Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:52:59 +0100 Subject: dt-bindings: net: update Marvell PPv2 binding for PPv2.2 support The Marvell PPv2 Device Tree binding was so far only used to describe the PPv2.1 network controller, used in the Marvell Armada 375. A new version of this IP block, PPv2.2 is used in the Marvell Armada 7K/8K processor. This commit extends the existing binding so that it can also be used to describe PPv2.2 hardware. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- .../devicetree/bindings/net/marvell-pp2.txt | 62 ++++++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/Documentation/devicetree/bindings/net/marvell-pp2.txt b/Documentation/devicetree/bindings/net/marvell-pp2.txt index 4754364df4c6..6b4956beff8c 100644 --- a/Documentation/devicetree/bindings/net/marvell-pp2.txt +++ b/Documentation/devicetree/bindings/net/marvell-pp2.txt @@ -1,17 +1,28 @@ -* Marvell Armada 375 Ethernet Controller (PPv2) +* Marvell Armada 375 Ethernet Controller (PPv2.1) + Marvell Armada 7K/8K Ethernet Controller (PPv2.2) Required properties: -- compatible: should be "marvell,armada-375-pp2" +- compatible: should be one of: + "marvell,armada-375-pp2" + "marvell,armada-7k-pp2" - reg: addresses and length of the register sets for the device. - Must contain the following register sets: + For "marvell,armada-375-pp2", must contain the following register + sets: - common controller registers - LMS registers - In addition, at least one port register set is required. -- clocks: a pointer to the reference clocks for this device, consequently: - - main controller clock - - GOP clock -- clock-names: names of used clocks, must be "pp_clk" and "gop_clk". + - one register area per Ethernet port + For "marvell,armada-7k-pp2", must contain the following register + sets: + - packet processor registers + - networking interfaces registers + +- clocks: pointers to the reference clocks for this device, consequently: + - main controller clock (for both armada-375-pp2 and armada-7k-pp2) + - GOP clock (for both armada-375-pp2 and armada-7k-pp2) + - MG clock (only for armada-7k-pp2) +- clock-names: names of used clocks, must be "pp_clk", "gop_clk" and + "mg_clk" (the latter only for armada-7k-pp2). The ethernet ports are represented by subnodes. At least one port is required. @@ -19,8 +30,10 @@ required. Required properties (port): - interrupts: interrupt for the port -- port-id: should be '0' or '1' for ethernet ports, and '2' for the - loopback port +- port-id: ID of the port from the MAC point of view +- gop-port-id: only for marvell,armada-7k-pp2, ID of the port from the + GOP (Group Of Ports) point of view. This ID is used to index the + per-port registers in the second register area. - phy-mode: See ethernet.txt file in the same directory Optional properties (port): @@ -29,7 +42,7 @@ Optional properties (port): - phy: a phandle to a phy node defining the PHY address (as the reg property, a single integer). -Example: +Example for marvell,armada-375-pp2: ethernet@f0000 { compatible = "marvell,armada-375-pp2"; @@ -57,3 +70,30 @@ ethernet@f0000 { phy-mode = "gmii"; }; }; + +Example for marvell,armada-7k-pp2: + +cpm_ethernet: ethernet@0 { + compatible = "marvell,armada-7k-pp22"; + reg = <0x0 0x100000>, <0x129000 0xb000>; + clocks = <&cpm_syscon0 1 3>, <&cpm_syscon0 1 9>, <&cpm_syscon0 1 5>; + clock-names = "pp_clk", "gop_clk", "gp_clk"; + + eth0: eth0 { + interrupts = ; + port-id = <0>; + gop-port-id = <0>; + }; + + eth1: eth1 { + interrupts = ; + port-id = <1>; + gop-port-id = <2>; + }; + + eth2: eth2 { + interrupts = ; + port-id = <2>; + gop-port-id = <3>; + }; +}; -- cgit From 20396136fbe563feac927151966a5931c1bac7a9 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:00 +0100 Subject: net: mvpp2: use "dma" instead of "phys" where appropriate As indicated by Russell King, the mvpp2 driver currently uses a lot "phys" or "phys_addr" to store what really is a DMA address. This commit clarifies this by using "dma" or "dma_addr" where appropriate. This is especially important as we are going to introduce more changes where the distinction between physical address and DMA address will be key. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 148 +++++++++++++++++------------------ 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index d00421b9ffea..6a4d1266d1e4 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -746,7 +746,7 @@ struct mvpp2_tx_desc { u8 packet_offset; /* the offset from the buffer beginning */ u8 phys_txq; /* destination queue ID */ u16 data_size; /* data size of transmitted packet in bytes */ - u32 buf_phys_addr; /* physical addr of transmitted buffer */ + u32 buf_dma_addr; /* physical addr of transmitted buffer */ u32 buf_cookie; /* cookie for access to TX buffer in tx path */ u32 reserved1[3]; /* hw_cmd (for future use, BM, PON, PNC) */ u32 reserved2; /* reserved (for future use) */ @@ -756,7 +756,7 @@ struct mvpp2_rx_desc { u32 status; /* info about received packet */ u16 reserved1; /* parser_info (for future use, PnC) */ u16 data_size; /* size of received packet in bytes */ - u32 buf_phys_addr; /* physical address of the buffer */ + u32 buf_dma_addr; /* physical address of the buffer */ u32 buf_cookie; /* cookie for access to RX buffer in rx path */ u16 reserved2; /* gem_port_id (for future use, PON) */ u16 reserved3; /* csum_l4 (for future use, PnC) */ @@ -772,7 +772,7 @@ struct mvpp2_txq_pcpu_buf { struct sk_buff *skb; /* Physical address of transmitted buffer */ - dma_addr_t phys; + dma_addr_t dma; /* Size transmitted */ size_t size; @@ -825,7 +825,7 @@ struct mvpp2_tx_queue { struct mvpp2_tx_desc *descs; /* DMA address of the Tx DMA descriptors array */ - dma_addr_t descs_phys; + dma_addr_t descs_dma; /* Index of the last Tx DMA descriptor */ int last_desc; @@ -848,7 +848,7 @@ struct mvpp2_rx_queue { struct mvpp2_rx_desc *descs; /* DMA address of the RX DMA descriptors array */ - dma_addr_t descs_phys; + dma_addr_t descs_dma; /* Index of the last RX DMA descriptor */ int last_desc; @@ -922,15 +922,15 @@ struct mvpp2_bm_pool { /* BPPE virtual base address */ u32 *virt_addr; - /* BPPE physical base address */ - dma_addr_t phys_addr; + /* BPPE DMA base address */ + dma_addr_t dma_addr; /* Ports using BM pool */ u32 port_map; }; struct mvpp2_buff_hdr { - u32 next_buff_phys_addr; + u32 next_buff_dma_addr; u32 next_buff_virt_addr; u16 byte_count; u16 info; @@ -982,7 +982,7 @@ static void mvpp2_txq_inc_put(struct mvpp2_txq_pcpu *txq_pcpu, txq_pcpu->buffs + txq_pcpu->txq_put_index; tx_buf->skb = skb; tx_buf->size = tx_desc->data_size; - tx_buf->phys = tx_desc->buf_phys_addr + tx_desc->packet_offset; + tx_buf->dma = tx_desc->buf_dma_addr + tx_desc->packet_offset; txq_pcpu->txq_put_index++; if (txq_pcpu->txq_put_index == txq_pcpu->size) txq_pcpu->txq_put_index = 0; @@ -3383,7 +3383,7 @@ static int mvpp2_bm_pool_create(struct platform_device *pdev, size_bytes = sizeof(u32) * size; bm_pool->virt_addr = dma_alloc_coherent(&pdev->dev, size_bytes, - &bm_pool->phys_addr, + &bm_pool->dma_addr, GFP_KERNEL); if (!bm_pool->virt_addr) return -ENOMEM; @@ -3391,14 +3391,14 @@ static int mvpp2_bm_pool_create(struct platform_device *pdev, if (!IS_ALIGNED((unsigned long)bm_pool->virt_addr, MVPP2_BM_POOL_PTR_ALIGN)) { dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr, - bm_pool->phys_addr); + bm_pool->dma_addr); dev_err(&pdev->dev, "BM pool %d is not %d bytes aligned\n", bm_pool->id, MVPP2_BM_POOL_PTR_ALIGN); return -ENOMEM; } mvpp2_write(priv, MVPP2_BM_POOL_BASE_REG(bm_pool->id), - bm_pool->phys_addr); + bm_pool->dma_addr); mvpp2_write(priv, MVPP2_BM_POOL_SIZE_REG(bm_pool->id), size); val = mvpp2_read(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id)); @@ -3433,15 +3433,15 @@ static void mvpp2_bm_bufs_free(struct device *dev, struct mvpp2 *priv, int i; for (i = 0; i < bm_pool->buf_num; i++) { - dma_addr_t buf_phys_addr; + dma_addr_t buf_dma_addr; unsigned long vaddr; /* Get buffer virtual address (indirect access) */ - buf_phys_addr = mvpp2_read(priv, - MVPP2_BM_PHY_ALLOC_REG(bm_pool->id)); + buf_dma_addr = mvpp2_read(priv, + MVPP2_BM_PHY_ALLOC_REG(bm_pool->id)); vaddr = mvpp2_read(priv, MVPP2_BM_VIRT_ALLOC_REG); - dma_unmap_single(dev, buf_phys_addr, + dma_unmap_single(dev, buf_dma_addr, bm_pool->buf_size, DMA_FROM_DEVICE); if (!vaddr) @@ -3473,7 +3473,7 @@ static int mvpp2_bm_pool_destroy(struct platform_device *pdev, dma_free_coherent(&pdev->dev, sizeof(u32) * bm_pool->size, bm_pool->virt_addr, - bm_pool->phys_addr); + bm_pool->dma_addr); return 0; } @@ -3563,24 +3563,24 @@ static void mvpp2_rxq_short_pool_set(struct mvpp2_port *port, static void *mvpp2_buf_alloc(struct mvpp2_port *port, struct mvpp2_bm_pool *bm_pool, - dma_addr_t *buf_phys_addr, + dma_addr_t *buf_dma_addr, gfp_t gfp_mask) { - dma_addr_t phys_addr; + dma_addr_t dma_addr; void *data; data = mvpp2_frag_alloc(bm_pool); if (!data) return NULL; - phys_addr = dma_map_single(port->dev->dev.parent, data, - MVPP2_RX_BUF_SIZE(bm_pool->pkt_size), - DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(port->dev->dev.parent, phys_addr))) { + dma_addr = dma_map_single(port->dev->dev.parent, data, + MVPP2_RX_BUF_SIZE(bm_pool->pkt_size), + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(port->dev->dev.parent, dma_addr))) { mvpp2_frag_free(bm_pool, data); return NULL; } - *buf_phys_addr = phys_addr; + *buf_dma_addr = dma_addr; return data; } @@ -3604,16 +3604,16 @@ static inline int mvpp2_bm_cookie_pool_get(unsigned long cookie) /* Release buffer to BM */ static inline void mvpp2_bm_pool_put(struct mvpp2_port *port, int pool, - dma_addr_t buf_phys_addr, + dma_addr_t buf_dma_addr, unsigned long buf_virt_addr) { mvpp2_write(port->priv, MVPP2_BM_VIRT_RLS_REG, buf_virt_addr); - mvpp2_write(port->priv, MVPP2_BM_PHY_RLS_REG(pool), buf_phys_addr); + mvpp2_write(port->priv, MVPP2_BM_PHY_RLS_REG(pool), buf_dma_addr); } /* Release multicast buffer */ static void mvpp2_bm_pool_mc_put(struct mvpp2_port *port, int pool, - dma_addr_t buf_phys_addr, + dma_addr_t buf_dma_addr, unsigned long buf_virt_addr, int mc_id) { @@ -3623,18 +3623,18 @@ static void mvpp2_bm_pool_mc_put(struct mvpp2_port *port, int pool, mvpp2_write(port->priv, MVPP2_BM_MC_RLS_REG, val); mvpp2_bm_pool_put(port, pool, - buf_phys_addr | MVPP2_BM_PHY_RLS_MC_BUFF_MASK, + buf_dma_addr | MVPP2_BM_PHY_RLS_MC_BUFF_MASK, buf_virt_addr); } /* Refill BM pool */ static void mvpp2_pool_refill(struct mvpp2_port *port, u32 bm, - dma_addr_t phys_addr, + dma_addr_t dma_addr, unsigned long cookie) { int pool = mvpp2_bm_cookie_pool_get(bm); - mvpp2_bm_pool_put(port, pool, phys_addr, cookie); + mvpp2_bm_pool_put(port, pool, dma_addr, cookie); } /* Allocate buffers for the pool */ @@ -3642,7 +3642,7 @@ static int mvpp2_bm_bufs_add(struct mvpp2_port *port, struct mvpp2_bm_pool *bm_pool, int buf_num) { int i, buf_size, total_size; - dma_addr_t phys_addr; + dma_addr_t dma_addr; void *buf; buf_size = MVPP2_RX_BUF_SIZE(bm_pool->pkt_size); @@ -3657,11 +3657,11 @@ static int mvpp2_bm_bufs_add(struct mvpp2_port *port, } for (i = 0; i < buf_num; i++) { - buf = mvpp2_buf_alloc(port, bm_pool, &phys_addr, GFP_KERNEL); + buf = mvpp2_buf_alloc(port, bm_pool, &dma_addr, GFP_KERNEL); if (!buf) break; - mvpp2_bm_pool_put(port, bm_pool->id, phys_addr, + mvpp2_bm_pool_put(port, bm_pool->id, dma_addr, (unsigned long)buf); } @@ -4449,7 +4449,7 @@ static void mvpp2_txq_bufs_free(struct mvpp2_port *port, struct mvpp2_txq_pcpu_buf *tx_buf = txq_pcpu->buffs + txq_pcpu->txq_get_index; - dma_unmap_single(port->dev->dev.parent, tx_buf->phys, + dma_unmap_single(port->dev->dev.parent, tx_buf->dma, tx_buf->size, DMA_TO_DEVICE); if (tx_buf->skb) dev_kfree_skb_any(tx_buf->skb); @@ -4530,7 +4530,7 @@ static int mvpp2_aggr_txq_init(struct platform_device *pdev, /* Allocate memory for TX descriptors */ aggr_txq->descs = dma_alloc_coherent(&pdev->dev, desc_num * MVPP2_DESC_ALIGNED_SIZE, - &aggr_txq->descs_phys, GFP_KERNEL); + &aggr_txq->descs_dma, GFP_KERNEL); if (!aggr_txq->descs) return -ENOMEM; @@ -4543,7 +4543,7 @@ static int mvpp2_aggr_txq_init(struct platform_device *pdev, /* Set Tx descriptors queue starting address */ /* indirect access */ mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_ADDR_REG(cpu), - aggr_txq->descs_phys); + aggr_txq->descs_dma); mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu), desc_num); return 0; @@ -4559,7 +4559,7 @@ static int mvpp2_rxq_init(struct mvpp2_port *port, /* Allocate memory for RX descriptors */ rxq->descs = dma_alloc_coherent(port->dev->dev.parent, rxq->size * MVPP2_DESC_ALIGNED_SIZE, - &rxq->descs_phys, GFP_KERNEL); + &rxq->descs_dma, GFP_KERNEL); if (!rxq->descs) return -ENOMEM; @@ -4570,7 +4570,7 @@ static int mvpp2_rxq_init(struct mvpp2_port *port, /* Set Rx descriptors queue starting address - indirect access */ mvpp2_write(port->priv, MVPP2_RXQ_NUM_REG, rxq->id); - mvpp2_write(port->priv, MVPP2_RXQ_DESC_ADDR_REG, rxq->descs_phys); + mvpp2_write(port->priv, MVPP2_RXQ_DESC_ADDR_REG, rxq->descs_dma); mvpp2_write(port->priv, MVPP2_RXQ_DESC_SIZE_REG, rxq->size); mvpp2_write(port->priv, MVPP2_RXQ_INDEX_REG, 0); @@ -4601,7 +4601,7 @@ static void mvpp2_rxq_drop_pkts(struct mvpp2_port *port, struct mvpp2_rx_desc *rx_desc = mvpp2_rxq_next_desc_get(rxq); u32 bm = mvpp2_bm_cookie_build(rx_desc); - mvpp2_pool_refill(port, bm, rx_desc->buf_phys_addr, + mvpp2_pool_refill(port, bm, rx_desc->buf_dma_addr, rx_desc->buf_cookie); } mvpp2_rxq_status_update(port, rxq->id, rx_received, rx_received); @@ -4617,12 +4617,12 @@ static void mvpp2_rxq_deinit(struct mvpp2_port *port, dma_free_coherent(port->dev->dev.parent, rxq->size * MVPP2_DESC_ALIGNED_SIZE, rxq->descs, - rxq->descs_phys); + rxq->descs_dma); rxq->descs = NULL; rxq->last_desc = 0; rxq->next_desc_to_proc = 0; - rxq->descs_phys = 0; + rxq->descs_dma = 0; /* Clear Rx descriptors queue starting address and size; * free descriptor number @@ -4646,7 +4646,7 @@ static int mvpp2_txq_init(struct mvpp2_port *port, /* Allocate memory for Tx descriptors */ txq->descs = dma_alloc_coherent(port->dev->dev.parent, txq->size * MVPP2_DESC_ALIGNED_SIZE, - &txq->descs_phys, GFP_KERNEL); + &txq->descs_dma, GFP_KERNEL); if (!txq->descs) return -ENOMEM; @@ -4654,7 +4654,7 @@ static int mvpp2_txq_init(struct mvpp2_port *port, /* Set Tx descriptors queue starting address - indirect access */ mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); - mvpp2_write(port->priv, MVPP2_TXQ_DESC_ADDR_REG, txq->descs_phys); + mvpp2_write(port->priv, MVPP2_TXQ_DESC_ADDR_REG, txq->descs_dma); mvpp2_write(port->priv, MVPP2_TXQ_DESC_SIZE_REG, txq->size & MVPP2_TXQ_DESC_SIZE_MASK); mvpp2_write(port->priv, MVPP2_TXQ_INDEX_REG, 0); @@ -4716,7 +4716,7 @@ error: dma_free_coherent(port->dev->dev.parent, txq->size * MVPP2_DESC_ALIGNED_SIZE, - txq->descs, txq->descs_phys); + txq->descs, txq->descs_dma); return -ENOMEM; } @@ -4736,12 +4736,12 @@ static void mvpp2_txq_deinit(struct mvpp2_port *port, if (txq->descs) dma_free_coherent(port->dev->dev.parent, txq->size * MVPP2_DESC_ALIGNED_SIZE, - txq->descs, txq->descs_phys); + txq->descs, txq->descs_dma); txq->descs = NULL; txq->last_desc = 0; txq->next_desc_to_proc = 0; - txq->descs_phys = 0; + txq->descs_dma = 0; /* Set minimum bandwidth for disabled TXQs */ mvpp2_write(port->priv, MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(txq->id), 0); @@ -5031,15 +5031,15 @@ static void mvpp2_rx_csum(struct mvpp2_port *port, u32 status, static int mvpp2_rx_refill(struct mvpp2_port *port, struct mvpp2_bm_pool *bm_pool, u32 bm) { - dma_addr_t phys_addr; + dma_addr_t dma_addr; void *buf; /* No recycle or too many buffers are in use, so allocate a new skb */ - buf = mvpp2_buf_alloc(port, bm_pool, &phys_addr, GFP_ATOMIC); + buf = mvpp2_buf_alloc(port, bm_pool, &dma_addr, GFP_ATOMIC); if (!buf) return -ENOMEM; - mvpp2_pool_refill(port, bm, phys_addr, (unsigned long)buf); + mvpp2_pool_refill(port, bm, dma_addr, (unsigned long)buf); return 0; } @@ -5081,16 +5081,16 @@ static void mvpp2_buff_hdr_rx(struct mvpp2_port *port, struct mvpp2_buff_hdr *buff_hdr; struct sk_buff *skb; u32 rx_status = rx_desc->status; - dma_addr_t buff_phys_addr; + dma_addr_t buff_dma_addr; unsigned long buff_virt_addr; - dma_addr_t buff_phys_addr_next; + dma_addr_t buff_dma_addr_next; unsigned long buff_virt_addr_next; int mc_id; int pool_id; pool_id = (rx_status & MVPP2_RXD_BM_POOL_ID_MASK) >> MVPP2_RXD_BM_POOL_ID_OFFS; - buff_phys_addr = rx_desc->buf_phys_addr; + buff_dma_addr = rx_desc->buf_dma_addr; buff_virt_addr = rx_desc->buf_cookie; do { @@ -5099,14 +5099,14 @@ static void mvpp2_buff_hdr_rx(struct mvpp2_port *port, mc_id = MVPP2_B_HDR_INFO_MC_ID(buff_hdr->info); - buff_phys_addr_next = buff_hdr->next_buff_phys_addr; + buff_dma_addr_next = buff_hdr->next_buff_dma_addr; buff_virt_addr_next = buff_hdr->next_buff_virt_addr; /* Release buffer */ - mvpp2_bm_pool_mc_put(port, pool_id, buff_phys_addr, + mvpp2_bm_pool_mc_put(port, pool_id, buff_dma_addr, buff_virt_addr, mc_id); - buff_phys_addr = buff_phys_addr_next; + buff_dma_addr = buff_dma_addr_next; buff_virt_addr = buff_virt_addr_next; } while (!MVPP2_B_HDR_INFO_IS_LAST(buff_hdr->info)); @@ -5132,7 +5132,7 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, struct mvpp2_bm_pool *bm_pool; struct sk_buff *skb; unsigned int frag_size; - dma_addr_t phys_addr; + dma_addr_t dma_addr; u32 bm, rx_status; int pool, rx_bytes, err; void *data; @@ -5140,7 +5140,7 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, rx_done++; rx_status = rx_desc->status; rx_bytes = rx_desc->data_size - MVPP2_MH_SIZE; - phys_addr = rx_desc->buf_phys_addr; + dma_addr = rx_desc->buf_dma_addr; data = (void *)(uintptr_t)rx_desc->buf_cookie; bm = mvpp2_bm_cookie_build(rx_desc); @@ -5163,7 +5163,7 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, mvpp2_rx_error(port, rx_desc); /* Return the buffer to the pool */ - mvpp2_pool_refill(port, bm, rx_desc->buf_phys_addr, + mvpp2_pool_refill(port, bm, rx_desc->buf_dma_addr, rx_desc->buf_cookie); continue; } @@ -5185,7 +5185,7 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, goto err_drop_frame; } - dma_unmap_single(dev->dev.parent, phys_addr, + dma_unmap_single(dev->dev.parent, dma_addr, bm_pool->buf_size, DMA_FROM_DEVICE); rcvd_pkts++; @@ -5219,7 +5219,7 @@ static inline void tx_desc_unmap_put(struct device *dev, struct mvpp2_tx_queue *txq, struct mvpp2_tx_desc *desc) { - dma_unmap_single(dev, desc->buf_phys_addr, + dma_unmap_single(dev, desc->buf_dma_addr, desc->data_size, DMA_TO_DEVICE); mvpp2_txq_desc_put(txq); } @@ -5232,7 +5232,7 @@ static int mvpp2_tx_frag_process(struct mvpp2_port *port, struct sk_buff *skb, struct mvpp2_txq_pcpu *txq_pcpu = this_cpu_ptr(txq->pcpu); struct mvpp2_tx_desc *tx_desc; int i; - dma_addr_t buf_phys_addr; + dma_addr_t buf_dma_addr; for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; @@ -5242,16 +5242,16 @@ static int mvpp2_tx_frag_process(struct mvpp2_port *port, struct sk_buff *skb, tx_desc->phys_txq = txq->id; tx_desc->data_size = frag->size; - buf_phys_addr = dma_map_single(port->dev->dev.parent, addr, - tx_desc->data_size, - DMA_TO_DEVICE); - if (dma_mapping_error(port->dev->dev.parent, buf_phys_addr)) { + buf_dma_addr = dma_map_single(port->dev->dev.parent, addr, + tx_desc->data_size, + DMA_TO_DEVICE); + if (dma_mapping_error(port->dev->dev.parent, buf_dma_addr)) { mvpp2_txq_desc_put(txq); goto error; } - tx_desc->packet_offset = buf_phys_addr & MVPP2_TX_DESC_ALIGN; - tx_desc->buf_phys_addr = buf_phys_addr & (~MVPP2_TX_DESC_ALIGN); + tx_desc->packet_offset = buf_dma_addr & MVPP2_TX_DESC_ALIGN; + tx_desc->buf_dma_addr = buf_dma_addr & (~MVPP2_TX_DESC_ALIGN); if (i == (skb_shinfo(skb)->nr_frags - 1)) { /* Last descriptor */ @@ -5285,7 +5285,7 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) struct mvpp2_tx_queue *txq, *aggr_txq; struct mvpp2_txq_pcpu *txq_pcpu; struct mvpp2_tx_desc *tx_desc; - dma_addr_t buf_phys_addr; + dma_addr_t buf_dma_addr; int frags = 0; u16 txq_id; u32 tx_cmd; @@ -5310,15 +5310,15 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) tx_desc->phys_txq = txq->id; tx_desc->data_size = skb_headlen(skb); - buf_phys_addr = dma_map_single(dev->dev.parent, skb->data, - tx_desc->data_size, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(dev->dev.parent, buf_phys_addr))) { + buf_dma_addr = dma_map_single(dev->dev.parent, skb->data, + tx_desc->data_size, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev->dev.parent, buf_dma_addr))) { mvpp2_txq_desc_put(txq); frags = 0; goto out; } - tx_desc->packet_offset = buf_phys_addr & MVPP2_TX_DESC_ALIGN; - tx_desc->buf_phys_addr = buf_phys_addr & ~MVPP2_TX_DESC_ALIGN; + tx_desc->packet_offset = buf_dma_addr & MVPP2_TX_DESC_ALIGN; + tx_desc->buf_dma_addr = buf_dma_addr & ~MVPP2_TX_DESC_ALIGN; tx_cmd = mvpp2_skb_tx_csum(port, skb); @@ -6506,7 +6506,7 @@ static int mvpp2_remove(struct platform_device *pdev) dma_free_coherent(&pdev->dev, MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE, aggr_txq->descs, - aggr_txq->descs_phys); + aggr_txq->descs_dma); } clk_disable_unprepare(priv->pp_clk); -- cgit From aeb3d1109afad0f4ebb5c9e27bbb67b755c5f47d Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:01 +0100 Subject: net: mvpp2: remove support for buffer header The "buffer header" functionality is a functionality used by the hardware to split an incoming packets over multiple BM buffers if they are not large enough. However, the mvpp2 driver guarantees that a pool of BM buffers has buffers with a size large enough to store MTU-sized packets. Therefore, this functionality is completely unused, and the code can be removed, and we should never get a descriptor with bit MVPP2_RXD_BUF_HDR set. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 77 ------------------------------------ 1 file changed, 77 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 6a4d1266d1e4..fb8b5e9a92f0 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -215,9 +215,6 @@ #define MVPP2_BM_PHY_RLS_PRIO_EN_MASK BIT(1) #define MVPP2_BM_PHY_RLS_GRNTD_MASK BIT(2) #define MVPP2_BM_VIRT_RLS_REG 0x64c0 -#define MVPP2_BM_MC_RLS_REG 0x64c4 -#define MVPP2_BM_MC_ID_MASK 0xfff -#define MVPP2_BM_FORCE_RELEASE_MASK BIT(12) /* TX Scheduler registers */ #define MVPP2_TXP_SCHED_PORT_INDEX_REG 0x8000 @@ -929,22 +926,6 @@ struct mvpp2_bm_pool { u32 port_map; }; -struct mvpp2_buff_hdr { - u32 next_buff_dma_addr; - u32 next_buff_virt_addr; - u16 byte_count; - u16 info; - u8 reserved1; /* bm_qset (for future use, BM) */ -}; - -/* Buffer header info bits */ -#define MVPP2_B_HDR_INFO_MC_ID_MASK 0xfff -#define MVPP2_B_HDR_INFO_MC_ID(info) ((info) & MVPP2_B_HDR_INFO_MC_ID_MASK) -#define MVPP2_B_HDR_INFO_LAST_OFFS 12 -#define MVPP2_B_HDR_INFO_LAST_MASK BIT(12) -#define MVPP2_B_HDR_INFO_IS_LAST(info) \ - ((info & MVPP2_B_HDR_INFO_LAST_MASK) >> MVPP2_B_HDR_INFO_LAST_OFFS) - /* Static declaractions */ /* Number of RXQs used by single port */ @@ -3611,22 +3592,6 @@ static inline void mvpp2_bm_pool_put(struct mvpp2_port *port, int pool, mvpp2_write(port->priv, MVPP2_BM_PHY_RLS_REG(pool), buf_dma_addr); } -/* Release multicast buffer */ -static void mvpp2_bm_pool_mc_put(struct mvpp2_port *port, int pool, - dma_addr_t buf_dma_addr, - unsigned long buf_virt_addr, - int mc_id) -{ - u32 val = 0; - - val |= (mc_id & MVPP2_BM_MC_ID_MASK); - mvpp2_write(port->priv, MVPP2_BM_MC_RLS_REG, val); - - mvpp2_bm_pool_put(port, pool, - buf_dma_addr | MVPP2_BM_PHY_RLS_MC_BUFF_MASK, - buf_virt_addr); -} - /* Refill BM pool */ static void mvpp2_pool_refill(struct mvpp2_port *port, u32 bm, dma_addr_t dma_addr, @@ -5075,43 +5040,6 @@ static u32 mvpp2_skb_tx_csum(struct mvpp2_port *port, struct sk_buff *skb) return MVPP2_TXD_L4_CSUM_NOT | MVPP2_TXD_IP_CSUM_DISABLE; } -static void mvpp2_buff_hdr_rx(struct mvpp2_port *port, - struct mvpp2_rx_desc *rx_desc) -{ - struct mvpp2_buff_hdr *buff_hdr; - struct sk_buff *skb; - u32 rx_status = rx_desc->status; - dma_addr_t buff_dma_addr; - unsigned long buff_virt_addr; - dma_addr_t buff_dma_addr_next; - unsigned long buff_virt_addr_next; - int mc_id; - int pool_id; - - pool_id = (rx_status & MVPP2_RXD_BM_POOL_ID_MASK) >> - MVPP2_RXD_BM_POOL_ID_OFFS; - buff_dma_addr = rx_desc->buf_dma_addr; - buff_virt_addr = rx_desc->buf_cookie; - - do { - skb = (struct sk_buff *)buff_virt_addr; - buff_hdr = (struct mvpp2_buff_hdr *)skb->head; - - mc_id = MVPP2_B_HDR_INFO_MC_ID(buff_hdr->info); - - buff_dma_addr_next = buff_hdr->next_buff_dma_addr; - buff_virt_addr_next = buff_hdr->next_buff_virt_addr; - - /* Release buffer */ - mvpp2_bm_pool_mc_put(port, pool_id, buff_dma_addr, - buff_virt_addr, mc_id); - - buff_dma_addr = buff_dma_addr_next; - buff_virt_addr = buff_virt_addr_next; - - } while (!MVPP2_B_HDR_INFO_IS_LAST(buff_hdr->info)); -} - /* Main rx processing */ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, struct mvpp2_rx_queue *rxq) @@ -5146,11 +5074,6 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, bm = mvpp2_bm_cookie_build(rx_desc); pool = mvpp2_bm_cookie_pool_get(bm); bm_pool = &port->priv->bm_pools[pool]; - /* Check if buffer header is used */ - if (rx_status & MVPP2_RXD_BUF_HDR) { - mvpp2_buff_hdr_rx(port, rx_desc); - continue; - } /* In case of an error, release the requested buffer pointer * to the Buffer Manager. This request process is controlled -- cgit From df905f2e21b42a948b662d39de82039e02b88de3 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:02 +0100 Subject: net: mvpp2: remove unused register definition MVPP2_TXQ_THRESH_REG This register is no longer used since commit edc660fa09e2 ("net: mvpp2: replace TX coalescing interrupts with hrtimer"). Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index fb8b5e9a92f0..60cc0206a8f0 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -117,9 +117,6 @@ #define MVPP2_TXQ_DESC_SIZE_REG 0x2088 #define MVPP2_TXQ_DESC_SIZE_MASK 0x3ff0 #define MVPP2_AGGR_TXQ_UPDATE_REG 0x2090 -#define MVPP2_TXQ_THRESH_REG 0x2094 -#define MVPP2_TRANSMITTED_THRESH_OFFSET 16 -#define MVPP2_TRANSMITTED_THRESH_MASK 0x3fff0000 #define MVPP2_TXQ_INDEX_REG 0x2098 #define MVPP2_TXQ_PREF_BUF_REG 0x209c #define MVPP2_PREF_BUF_PTR(desc) ((desc) & 0xfff) -- cgit From 4d6c2a677fe5a903ed6483865cc14ff00a2054b5 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:03 +0100 Subject: net: mvpp2: remove mvpp2_txq_pend_desc_num_get() function The mvpp2_txq_pend_desc_num_get() function only selects a TX queue, and reads the number of pending descriptors. It is used in only one place, in mvpp2_txq_clean(), where the TX queue has already been selected by a write to MVPP2_TXQ_NUM_REG. Therefore, this function is useless, and the caller can simply read the value of the MVPP2_TXQ_PENDING_REG register instead. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 60cc0206a8f0..9ddda20b1603 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -4123,18 +4123,6 @@ static u32 mvpp2_bm_cookie_build(struct mvpp2_rx_desc *rx_desc) /* Tx descriptors helper methods */ -/* Get number of Tx descriptors waiting to be transmitted by HW */ -static int mvpp2_txq_pend_desc_num_get(struct mvpp2_port *port, - struct mvpp2_tx_queue *txq) -{ - u32 val; - - mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); - val = mvpp2_read(port->priv, MVPP2_TXQ_PENDING_REG); - - return val & MVPP2_TXQ_PENDING_MASK; -} - /* Get pointer to next Tx descriptor to be processed (send) by HW */ static struct mvpp2_tx_desc * mvpp2_txq_next_desc_get(struct mvpp2_tx_queue *txq) @@ -4740,7 +4728,8 @@ static void mvpp2_txq_clean(struct mvpp2_port *port, struct mvpp2_tx_queue *txq) mdelay(1); delay++; - pending = mvpp2_txq_pend_desc_num_get(port, txq); + pending = mvpp2_read(port->priv, MVPP2_TXQ_PENDING_REG) & + MVPP2_TXQ_PENDING_MASK; } while (pending); val &= ~MVPP2_TXQ_DRAIN_EN_MASK; -- cgit From 4e4a105f1fde326a5d1b0fbcbba8254c54a673e4 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:04 +0100 Subject: net: mvpp2: store physical address of buffer in rx_desc->buf_cookie The RX descriptors of the PPv2 hardware allow to store several information, amongst which: - the DMA address of the buffer in which the data has been received - a "cookie" field, left to the use of the driver, and not used by the hardware In the current implementation, the "cookie" field is used to store the virtual address of the buffer, so that in the receive completion path, we can easily get the virtual address of the buffer that corresponds to a completed RX descriptors. On PPv2.1, used on 32-bit platforms, those two fields are 32-bit wide, which is enough to store a DMA address in the first field, and a virtual address in the second field. On PPv2.2, used on 64-bit platforms, these two fields have been extended to 40 bits. While 40 bits is enough to store a DMA address (as long as the DMA mask is 40 bits or lower), it is not enough to store a virtual address. Therefore, the "cookie" field can no longer be used to store the virtual address of the buffer. However, as Russell King pointed out, the RX buffers are always allocated in the kernel linear mapping, and therefore using phys_to_virt() on the physical address of the RX buffer is possible and correct. Therefore, this commit changes the driver to use the "cookie" field to store the physical address instead of the virtual address. phys_to_virt() is used in the receive completion path to retrieve the virtual address from the physical address. It is obviously important to realize that the DMA address and physical address are two different things, which is why we store both in the RX descriptors. While those addresses may be identical in some situations, it remains two distinct concepts, and both addresses should be handled separately. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 40 +++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 9ddda20b1603..35dc07160ac6 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -3412,20 +3412,21 @@ static void mvpp2_bm_bufs_free(struct device *dev, struct mvpp2 *priv, for (i = 0; i < bm_pool->buf_num; i++) { dma_addr_t buf_dma_addr; - unsigned long vaddr; + phys_addr_t buf_phys_addr; + void *data; - /* Get buffer virtual address (indirect access) */ buf_dma_addr = mvpp2_read(priv, MVPP2_BM_PHY_ALLOC_REG(bm_pool->id)); - vaddr = mvpp2_read(priv, MVPP2_BM_VIRT_ALLOC_REG); + buf_phys_addr = mvpp2_read(priv, MVPP2_BM_VIRT_ALLOC_REG); dma_unmap_single(dev, buf_dma_addr, bm_pool->buf_size, DMA_FROM_DEVICE); - if (!vaddr) + data = (void *)phys_to_virt(buf_phys_addr); + if (!data) break; - mvpp2_frag_free(bm_pool, (void *)vaddr); + mvpp2_frag_free(bm_pool, data); } /* Update BM driver with number of buffers removed from pool */ @@ -3542,6 +3543,7 @@ static void mvpp2_rxq_short_pool_set(struct mvpp2_port *port, static void *mvpp2_buf_alloc(struct mvpp2_port *port, struct mvpp2_bm_pool *bm_pool, dma_addr_t *buf_dma_addr, + phys_addr_t *buf_phys_addr, gfp_t gfp_mask) { dma_addr_t dma_addr; @@ -3559,6 +3561,7 @@ static void *mvpp2_buf_alloc(struct mvpp2_port *port, return NULL; } *buf_dma_addr = dma_addr; + *buf_phys_addr = virt_to_phys(data); return data; } @@ -3583,20 +3586,25 @@ static inline int mvpp2_bm_cookie_pool_get(unsigned long cookie) /* Release buffer to BM */ static inline void mvpp2_bm_pool_put(struct mvpp2_port *port, int pool, dma_addr_t buf_dma_addr, - unsigned long buf_virt_addr) + phys_addr_t buf_phys_addr) { - mvpp2_write(port->priv, MVPP2_BM_VIRT_RLS_REG, buf_virt_addr); + /* MVPP2_BM_VIRT_RLS_REG is not interpreted by HW, and simply + * returned in the "cookie" field of the RX + * descriptor. Instead of storing the virtual address, we + * store the physical address + */ + mvpp2_write(port->priv, MVPP2_BM_VIRT_RLS_REG, buf_phys_addr); mvpp2_write(port->priv, MVPP2_BM_PHY_RLS_REG(pool), buf_dma_addr); } /* Refill BM pool */ static void mvpp2_pool_refill(struct mvpp2_port *port, u32 bm, dma_addr_t dma_addr, - unsigned long cookie) + phys_addr_t phys_addr) { int pool = mvpp2_bm_cookie_pool_get(bm); - mvpp2_bm_pool_put(port, pool, dma_addr, cookie); + mvpp2_bm_pool_put(port, pool, dma_addr, phys_addr); } /* Allocate buffers for the pool */ @@ -3605,6 +3613,7 @@ static int mvpp2_bm_bufs_add(struct mvpp2_port *port, { int i, buf_size, total_size; dma_addr_t dma_addr; + phys_addr_t phys_addr; void *buf; buf_size = MVPP2_RX_BUF_SIZE(bm_pool->pkt_size); @@ -3619,12 +3628,13 @@ static int mvpp2_bm_bufs_add(struct mvpp2_port *port, } for (i = 0; i < buf_num; i++) { - buf = mvpp2_buf_alloc(port, bm_pool, &dma_addr, GFP_KERNEL); + buf = mvpp2_buf_alloc(port, bm_pool, &dma_addr, + &phys_addr, GFP_KERNEL); if (!buf) break; mvpp2_bm_pool_put(port, bm_pool->id, dma_addr, - (unsigned long)buf); + phys_addr); } /* Update BM driver with number of buffers added to pool */ @@ -4983,14 +4993,16 @@ static int mvpp2_rx_refill(struct mvpp2_port *port, struct mvpp2_bm_pool *bm_pool, u32 bm) { dma_addr_t dma_addr; + phys_addr_t phys_addr; void *buf; /* No recycle or too many buffers are in use, so allocate a new skb */ - buf = mvpp2_buf_alloc(port, bm_pool, &dma_addr, GFP_ATOMIC); + buf = mvpp2_buf_alloc(port, bm_pool, &dma_addr, &phys_addr, + GFP_ATOMIC); if (!buf) return -ENOMEM; - mvpp2_pool_refill(port, bm, dma_addr, (unsigned long)buf); + mvpp2_pool_refill(port, bm, dma_addr, phys_addr); return 0; } @@ -5055,7 +5067,7 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, rx_status = rx_desc->status; rx_bytes = rx_desc->data_size - MVPP2_MH_SIZE; dma_addr = rx_desc->buf_dma_addr; - data = (void *)(uintptr_t)rx_desc->buf_cookie; + data = (void *)phys_to_virt(rx_desc->buf_cookie); bm = mvpp2_bm_cookie_build(rx_desc); pool = mvpp2_bm_cookie_pool_get(bm); -- cgit From ac3dd277826979f50214894eb2781df4b85ab2a8 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:05 +0100 Subject: net: mvpp2: add and use accessors for TX/RX descriptors The PPv2.2 IP has a different TX and RX descriptor layout compared to PPv2.1. In order to prepare for the introduction of PPv2.2 support in mvpp2, this commit adds accessors for the different fields of the TX and RX descriptors, and changes the code to use them. For now, the mvpp2_port argument passed to the accessors is not used, but it will be used in follow-up to update the descriptor according to the version of the IP being used. Apart from the mechanical changes to use the newly introduced accessors, a few other changes, needed to use the accessors, are made: - The mvpp2_txq_inc_put() function now takes a mvpp2_port as first argument, as it is needed to use the accessors. - Similarly, the mvpp2_bm_cookie_build() gains a mvpp2_port first argument, for the same reason. - In mvpp2_rx_error(), instead of accessing the RX descriptor in each case of the switch, we introduce a local variable to store the packet size. - In mvpp2_tx_frag_process() and mvpp2_tx() instead of accessing the packet size from the TX descriptor, we use the actual value available in the function, which is used to set the TX descriptor packet size a few lines before. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 194 ++++++++++++++++++++++++++--------- 1 file changed, 145 insertions(+), 49 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 35dc07160ac6..2ee066b5c28a 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -945,6 +945,83 @@ static u32 mvpp2_read(struct mvpp2 *priv, u32 offset) return readl(priv->base + offset); } +static dma_addr_t mvpp2_txdesc_dma_addr_get(struct mvpp2_port *port, + struct mvpp2_tx_desc *tx_desc) +{ + return tx_desc->buf_dma_addr; +} + +static void mvpp2_txdesc_dma_addr_set(struct mvpp2_port *port, + struct mvpp2_tx_desc *tx_desc, + dma_addr_t dma_addr) +{ + tx_desc->buf_dma_addr = dma_addr; +} + +static size_t mvpp2_txdesc_size_get(struct mvpp2_port *port, + struct mvpp2_tx_desc *tx_desc) +{ + return tx_desc->data_size; +} + +static void mvpp2_txdesc_size_set(struct mvpp2_port *port, + struct mvpp2_tx_desc *tx_desc, + size_t size) +{ + tx_desc->data_size = size; +} + +static void mvpp2_txdesc_txq_set(struct mvpp2_port *port, + struct mvpp2_tx_desc *tx_desc, + unsigned int txq) +{ + tx_desc->phys_txq = txq; +} + +static void mvpp2_txdesc_cmd_set(struct mvpp2_port *port, + struct mvpp2_tx_desc *tx_desc, + unsigned int command) +{ + tx_desc->command = command; +} + +static void mvpp2_txdesc_offset_set(struct mvpp2_port *port, + struct mvpp2_tx_desc *tx_desc, + unsigned int offset) +{ + tx_desc->packet_offset = offset; +} + +static unsigned int mvpp2_txdesc_offset_get(struct mvpp2_port *port, + struct mvpp2_tx_desc *tx_desc) +{ + return tx_desc->packet_offset; +} + +static dma_addr_t mvpp2_rxdesc_dma_addr_get(struct mvpp2_port *port, + struct mvpp2_rx_desc *rx_desc) +{ + return rx_desc->buf_dma_addr; +} + +static unsigned long mvpp2_rxdesc_cookie_get(struct mvpp2_port *port, + struct mvpp2_rx_desc *rx_desc) +{ + return rx_desc->buf_cookie; +} + +static size_t mvpp2_rxdesc_size_get(struct mvpp2_port *port, + struct mvpp2_rx_desc *rx_desc) +{ + return rx_desc->data_size; +} + +static u32 mvpp2_rxdesc_status_get(struct mvpp2_port *port, + struct mvpp2_rx_desc *rx_desc) +{ + return rx_desc->status; +} + static void mvpp2_txq_inc_get(struct mvpp2_txq_pcpu *txq_pcpu) { txq_pcpu->txq_get_index++; @@ -952,15 +1029,17 @@ static void mvpp2_txq_inc_get(struct mvpp2_txq_pcpu *txq_pcpu) txq_pcpu->txq_get_index = 0; } -static void mvpp2_txq_inc_put(struct mvpp2_txq_pcpu *txq_pcpu, +static void mvpp2_txq_inc_put(struct mvpp2_port *port, + struct mvpp2_txq_pcpu *txq_pcpu, struct sk_buff *skb, struct mvpp2_tx_desc *tx_desc) { struct mvpp2_txq_pcpu_buf *tx_buf = txq_pcpu->buffs + txq_pcpu->txq_put_index; tx_buf->skb = skb; - tx_buf->size = tx_desc->data_size; - tx_buf->dma = tx_desc->buf_dma_addr + tx_desc->packet_offset; + tx_buf->size = mvpp2_txdesc_size_get(port, tx_desc); + tx_buf->dma = mvpp2_txdesc_dma_addr_get(port, tx_desc) + + mvpp2_txdesc_offset_get(port, tx_desc); txq_pcpu->txq_put_index++; if (txq_pcpu->txq_put_index == txq_pcpu->size) txq_pcpu->txq_put_index = 0; @@ -4121,11 +4200,15 @@ static void mvpp2_rxq_offset_set(struct mvpp2_port *port, } /* Obtain BM cookie information from descriptor */ -static u32 mvpp2_bm_cookie_build(struct mvpp2_rx_desc *rx_desc) +static u32 mvpp2_bm_cookie_build(struct mvpp2_port *port, + struct mvpp2_rx_desc *rx_desc) { - int pool = (rx_desc->status & MVPP2_RXD_BM_POOL_ID_MASK) >> - MVPP2_RXD_BM_POOL_ID_OFFS; int cpu = smp_processor_id(); + int pool; + + pool = (mvpp2_rxdesc_status_get(port, rx_desc) & + MVPP2_RXD_BM_POOL_ID_MASK) >> + MVPP2_RXD_BM_POOL_ID_OFFS; return ((pool & 0xFF) << MVPP2_BM_COOKIE_POOL_OFFS) | ((cpu & 0xFF) << MVPP2_BM_COOKIE_CPU_OFFS); @@ -4559,10 +4642,11 @@ static void mvpp2_rxq_drop_pkts(struct mvpp2_port *port, for (i = 0; i < rx_received; i++) { struct mvpp2_rx_desc *rx_desc = mvpp2_rxq_next_desc_get(rxq); - u32 bm = mvpp2_bm_cookie_build(rx_desc); + u32 bm = mvpp2_bm_cookie_build(port, rx_desc); - mvpp2_pool_refill(port, bm, rx_desc->buf_dma_addr, - rx_desc->buf_cookie); + mvpp2_pool_refill(port, bm, + mvpp2_rxdesc_dma_addr_get(port, rx_desc), + mvpp2_rxdesc_cookie_get(port, rx_desc)); } mvpp2_rxq_status_update(port, rxq->id, rx_received, rx_received); } @@ -4952,20 +5036,21 @@ static enum hrtimer_restart mvpp2_hr_timer_cb(struct hrtimer *timer) static void mvpp2_rx_error(struct mvpp2_port *port, struct mvpp2_rx_desc *rx_desc) { - u32 status = rx_desc->status; + u32 status = mvpp2_rxdesc_status_get(port, rx_desc); + size_t sz = mvpp2_rxdesc_size_get(port, rx_desc); switch (status & MVPP2_RXD_ERR_CODE_MASK) { case MVPP2_RXD_ERR_CRC: - netdev_err(port->dev, "bad rx status %08x (crc error), size=%d\n", - status, rx_desc->data_size); + netdev_err(port->dev, "bad rx status %08x (crc error), size=%zu\n", + status, sz); break; case MVPP2_RXD_ERR_OVERRUN: - netdev_err(port->dev, "bad rx status %08x (overrun error), size=%d\n", - status, rx_desc->data_size); + netdev_err(port->dev, "bad rx status %08x (overrun error), size=%zu\n", + status, sz); break; case MVPP2_RXD_ERR_RESOURCE: - netdev_err(port->dev, "bad rx status %08x (resource error), size=%d\n", - status, rx_desc->data_size); + netdev_err(port->dev, "bad rx status %08x (resource error), size=%zu\n", + status, sz); break; } } @@ -5059,17 +5144,20 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, struct sk_buff *skb; unsigned int frag_size; dma_addr_t dma_addr; + phys_addr_t phys_addr; u32 bm, rx_status; int pool, rx_bytes, err; void *data; rx_done++; - rx_status = rx_desc->status; - rx_bytes = rx_desc->data_size - MVPP2_MH_SIZE; - dma_addr = rx_desc->buf_dma_addr; - data = (void *)phys_to_virt(rx_desc->buf_cookie); - - bm = mvpp2_bm_cookie_build(rx_desc); + rx_status = mvpp2_rxdesc_status_get(port, rx_desc); + rx_bytes = mvpp2_rxdesc_size_get(port, rx_desc); + rx_bytes -= MVPP2_MH_SIZE; + dma_addr = mvpp2_rxdesc_dma_addr_get(port, rx_desc); + phys_addr = mvpp2_rxdesc_cookie_get(port, rx_desc); + data = (void *)phys_to_virt(phys_addr); + + bm = mvpp2_bm_cookie_build(port, rx_desc); pool = mvpp2_bm_cookie_pool_get(bm); bm_pool = &port->priv->bm_pools[pool]; @@ -5083,9 +5171,7 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, dev->stats.rx_errors++; mvpp2_rx_error(port, rx_desc); /* Return the buffer to the pool */ - - mvpp2_pool_refill(port, bm, rx_desc->buf_dma_addr, - rx_desc->buf_cookie); + mvpp2_pool_refill(port, bm, dma_addr, phys_addr); continue; } @@ -5137,11 +5223,15 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, } static inline void -tx_desc_unmap_put(struct device *dev, struct mvpp2_tx_queue *txq, +tx_desc_unmap_put(struct mvpp2_port *port, struct mvpp2_tx_queue *txq, struct mvpp2_tx_desc *desc) { - dma_unmap_single(dev, desc->buf_dma_addr, - desc->data_size, DMA_TO_DEVICE); + dma_addr_t buf_dma_addr = + mvpp2_txdesc_dma_addr_get(port, desc); + size_t buf_sz = + mvpp2_txdesc_size_get(port, desc); + dma_unmap_single(port->dev->dev.parent, buf_dma_addr, + buf_sz, DMA_TO_DEVICE); mvpp2_txq_desc_put(txq); } @@ -5160,28 +5250,31 @@ static int mvpp2_tx_frag_process(struct mvpp2_port *port, struct sk_buff *skb, void *addr = page_address(frag->page.p) + frag->page_offset; tx_desc = mvpp2_txq_next_desc_get(aggr_txq); - tx_desc->phys_txq = txq->id; - tx_desc->data_size = frag->size; + mvpp2_txdesc_txq_set(port, tx_desc, txq->id); + mvpp2_txdesc_size_set(port, tx_desc, frag->size); buf_dma_addr = dma_map_single(port->dev->dev.parent, addr, - tx_desc->data_size, - DMA_TO_DEVICE); + frag->size, + DMA_TO_DEVICE); if (dma_mapping_error(port->dev->dev.parent, buf_dma_addr)) { mvpp2_txq_desc_put(txq); goto error; } - tx_desc->packet_offset = buf_dma_addr & MVPP2_TX_DESC_ALIGN; - tx_desc->buf_dma_addr = buf_dma_addr & (~MVPP2_TX_DESC_ALIGN); + mvpp2_txdesc_offset_set(port, tx_desc, + buf_dma_addr & MVPP2_TX_DESC_ALIGN); + mvpp2_txdesc_dma_addr_set(port, tx_desc, + buf_dma_addr & ~MVPP2_TX_DESC_ALIGN); if (i == (skb_shinfo(skb)->nr_frags - 1)) { /* Last descriptor */ - tx_desc->command = MVPP2_TXD_L_DESC; - mvpp2_txq_inc_put(txq_pcpu, skb, tx_desc); + mvpp2_txdesc_cmd_set(port, tx_desc, + MVPP2_TXD_L_DESC); + mvpp2_txq_inc_put(port, txq_pcpu, skb, tx_desc); } else { /* Descriptor in the middle: Not First, Not Last */ - tx_desc->command = 0; - mvpp2_txq_inc_put(txq_pcpu, NULL, tx_desc); + mvpp2_txdesc_cmd_set(port, tx_desc, 0); + mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc); } } @@ -5193,7 +5286,7 @@ error: */ for (i = i - 1; i >= 0; i--) { tx_desc = txq->descs + i; - tx_desc_unmap_put(port->dev->dev.parent, txq, tx_desc); + tx_desc_unmap_put(port, txq, tx_desc); } return -ENOMEM; @@ -5228,35 +5321,38 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) /* Get a descriptor for the first part of the packet */ tx_desc = mvpp2_txq_next_desc_get(aggr_txq); - tx_desc->phys_txq = txq->id; - tx_desc->data_size = skb_headlen(skb); + mvpp2_txdesc_txq_set(port, tx_desc, txq->id); + mvpp2_txdesc_size_set(port, tx_desc, skb_headlen(skb)); buf_dma_addr = dma_map_single(dev->dev.parent, skb->data, - tx_desc->data_size, DMA_TO_DEVICE); + skb_headlen(skb), DMA_TO_DEVICE); if (unlikely(dma_mapping_error(dev->dev.parent, buf_dma_addr))) { mvpp2_txq_desc_put(txq); frags = 0; goto out; } - tx_desc->packet_offset = buf_dma_addr & MVPP2_TX_DESC_ALIGN; - tx_desc->buf_dma_addr = buf_dma_addr & ~MVPP2_TX_DESC_ALIGN; + + mvpp2_txdesc_offset_set(port, tx_desc, + buf_dma_addr & MVPP2_TX_DESC_ALIGN); + mvpp2_txdesc_dma_addr_set(port, tx_desc, + buf_dma_addr & ~MVPP2_TX_DESC_ALIGN); tx_cmd = mvpp2_skb_tx_csum(port, skb); if (frags == 1) { /* First and Last descriptor */ tx_cmd |= MVPP2_TXD_F_DESC | MVPP2_TXD_L_DESC; - tx_desc->command = tx_cmd; - mvpp2_txq_inc_put(txq_pcpu, skb, tx_desc); + mvpp2_txdesc_cmd_set(port, tx_desc, tx_cmd); + mvpp2_txq_inc_put(port, txq_pcpu, skb, tx_desc); } else { /* First but not Last */ tx_cmd |= MVPP2_TXD_F_DESC | MVPP2_TXD_PADDING_DISABLE; - tx_desc->command = tx_cmd; - mvpp2_txq_inc_put(txq_pcpu, NULL, tx_desc); + mvpp2_txdesc_cmd_set(port, tx_desc, tx_cmd); + mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc); /* Continue with other skb fragments */ if (mvpp2_tx_frag_process(port, skb, aggr_txq, txq)) { - tx_desc_unmap_put(port->dev->dev.parent, txq, tx_desc); + tx_desc_unmap_put(port, txq, tx_desc); frags = 0; goto out; } -- cgit From faca9247980238f297b04f6701cb0f0f3f18b40c Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:06 +0100 Subject: net: mvpp2: add hw_version field in "struct mvpp2" In preparation to the introduction for the support of PPv2.2 in the mvpp2 driver, this commit adds a hw_version field to the struct mvpp2, and uses the .data field of the DT match table to fill it in. Having the MVPP21 and MVPP22 definitions available will allow to start adding the necessary conditional code to support PPv2.2. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 2ee066b5c28a..27d88388ec65 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -643,6 +644,9 @@ struct mvpp2 { /* Tclk value */ u32 tclk; + + /* HW version */ + enum { MVPP21, MVPP22 } hw_version; }; struct mvpp2_pcpu_stats { @@ -6429,6 +6433,9 @@ static int mvpp2_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + priv->hw_version = + (unsigned long)of_device_get_match_data(&pdev->dev); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->base)) @@ -6533,7 +6540,10 @@ static int mvpp2_remove(struct platform_device *pdev) } static const struct of_device_id mvpp2_match[] = { - { .compatible = "marvell,armada-375-pp2" }, + { + .compatible = "marvell,armada-375-pp2", + .data = (void *)MVPP21, + }, { } }; MODULE_DEVICE_TABLE(of, mvpp2_match); -- cgit From 054f6372dc9c0a02a1b6491ef182f1edafa0b189 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:07 +0100 Subject: net: mvpp2: introduce an intermediate union for the TX/RX descriptors Since the format of the HW descriptors is different between PPv2.1 and PPv2.2, this commit introduces an intermediate union, with for now only the PPv2.1 descriptors. The bulk of the driver code only manipulates opaque mvpp2_tx_desc and mvpp2_rx_desc pointers, and the descriptors can only be accessed and modified through the accessor functions. A follow-up commit will add the descriptor definitions for PPv2.2. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 45 +++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 27d88388ec65..e9710e86a072 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -739,7 +739,8 @@ struct mvpp2_port { #define MVPP2_RXD_L3_IP6 BIT(30) #define MVPP2_RXD_BUF_HDR BIT(31) -struct mvpp2_tx_desc { +/* HW TX descriptor for PPv2.1 */ +struct mvpp21_tx_desc { u32 command; /* Options used by HW for packet transmitting.*/ u8 packet_offset; /* the offset from the buffer beginning */ u8 phys_txq; /* destination queue ID */ @@ -750,7 +751,8 @@ struct mvpp2_tx_desc { u32 reserved2; /* reserved (for future use) */ }; -struct mvpp2_rx_desc { +/* HW RX descriptor for PPv2.1 */ +struct mvpp21_rx_desc { u32 status; /* info about received packet */ u16 reserved1; /* parser_info (for future use, PnC) */ u16 data_size; /* size of received packet in bytes */ @@ -765,6 +767,21 @@ struct mvpp2_rx_desc { u32 reserved8; }; +/* Opaque type used by the driver to manipulate the HW TX and RX + * descriptors + */ +struct mvpp2_tx_desc { + union { + struct mvpp21_tx_desc pp21; + }; +}; + +struct mvpp2_rx_desc { + union { + struct mvpp21_rx_desc pp21; + }; +}; + struct mvpp2_txq_pcpu_buf { /* Transmitted SKB */ struct sk_buff *skb; @@ -952,78 +969,78 @@ static u32 mvpp2_read(struct mvpp2 *priv, u32 offset) static dma_addr_t mvpp2_txdesc_dma_addr_get(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc) { - return tx_desc->buf_dma_addr; + return tx_desc->pp21.buf_dma_addr; } static void mvpp2_txdesc_dma_addr_set(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc, dma_addr_t dma_addr) { - tx_desc->buf_dma_addr = dma_addr; + tx_desc->pp21.buf_dma_addr = dma_addr; } static size_t mvpp2_txdesc_size_get(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc) { - return tx_desc->data_size; + return tx_desc->pp21.data_size; } static void mvpp2_txdesc_size_set(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc, size_t size) { - tx_desc->data_size = size; + tx_desc->pp21.data_size = size; } static void mvpp2_txdesc_txq_set(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc, unsigned int txq) { - tx_desc->phys_txq = txq; + tx_desc->pp21.phys_txq = txq; } static void mvpp2_txdesc_cmd_set(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc, unsigned int command) { - tx_desc->command = command; + tx_desc->pp21.command = command; } static void mvpp2_txdesc_offset_set(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc, unsigned int offset) { - tx_desc->packet_offset = offset; + tx_desc->pp21.packet_offset = offset; } static unsigned int mvpp2_txdesc_offset_get(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc) { - return tx_desc->packet_offset; + return tx_desc->pp21.packet_offset; } static dma_addr_t mvpp2_rxdesc_dma_addr_get(struct mvpp2_port *port, struct mvpp2_rx_desc *rx_desc) { - return rx_desc->buf_dma_addr; + return rx_desc->pp21.buf_dma_addr; } static unsigned long mvpp2_rxdesc_cookie_get(struct mvpp2_port *port, struct mvpp2_rx_desc *rx_desc) { - return rx_desc->buf_cookie; + return rx_desc->pp21.buf_cookie; } static size_t mvpp2_rxdesc_size_get(struct mvpp2_port *port, struct mvpp2_rx_desc *rx_desc) { - return rx_desc->data_size; + return rx_desc->pp21.data_size; } static u32 mvpp2_rxdesc_status_get(struct mvpp2_port *port, struct mvpp2_rx_desc *rx_desc) { - return rx_desc->status; + return rx_desc->pp21.status; } static void mvpp2_txq_inc_get(struct mvpp2_txq_pcpu *txq_pcpu) -- cgit From e7c5359f2eedf2ad3b1b586552d3b6dfa44c2a79 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:08 +0100 Subject: net: mvpp2: introduce PPv2.2 HW descriptors and adapt accessors This commit adds the definition of the PPv2.2 HW descriptors, adjusts the mvpp2_tx_desc and mvpp2_rx_desc structures accordingly, and adapts the accessors to work on both PPv2.1 and PPv2.2. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 88 +++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index e9710e86a072..746b5bea714a 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -767,18 +767,42 @@ struct mvpp21_rx_desc { u32 reserved8; }; +/* HW TX descriptor for PPv2.2 */ +struct mvpp22_tx_desc { + u32 command; + u8 packet_offset; + u8 phys_txq; + u16 data_size; + u64 reserved1; + u64 buf_dma_addr_ptp; + u64 buf_cookie_misc; +}; + +/* HW RX descriptor for PPv2.2 */ +struct mvpp22_rx_desc { + u32 status; + u16 reserved1; + u16 data_size; + u32 reserved2; + u32 reserved3; + u64 buf_dma_addr_key_hash; + u64 buf_cookie_misc; +}; + /* Opaque type used by the driver to manipulate the HW TX and RX * descriptors */ struct mvpp2_tx_desc { union { struct mvpp21_tx_desc pp21; + struct mvpp22_tx_desc pp22; }; }; struct mvpp2_rx_desc { union { struct mvpp21_rx_desc pp21; + struct mvpp22_rx_desc pp22; }; }; @@ -969,78 +993,118 @@ static u32 mvpp2_read(struct mvpp2 *priv, u32 offset) static dma_addr_t mvpp2_txdesc_dma_addr_get(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc) { - return tx_desc->pp21.buf_dma_addr; + if (port->priv->hw_version == MVPP21) + return tx_desc->pp21.buf_dma_addr; + else + return tx_desc->pp22.buf_dma_addr_ptp & GENMASK_ULL(40, 0); } static void mvpp2_txdesc_dma_addr_set(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc, dma_addr_t dma_addr) { - tx_desc->pp21.buf_dma_addr = dma_addr; + if (port->priv->hw_version == MVPP21) { + tx_desc->pp21.buf_dma_addr = dma_addr; + } else { + u64 val = (u64)dma_addr; + + tx_desc->pp22.buf_dma_addr_ptp &= ~GENMASK_ULL(40, 0); + tx_desc->pp22.buf_dma_addr_ptp |= val; + } } static size_t mvpp2_txdesc_size_get(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc) { - return tx_desc->pp21.data_size; + if (port->priv->hw_version == MVPP21) + return tx_desc->pp21.data_size; + else + return tx_desc->pp22.data_size; } static void mvpp2_txdesc_size_set(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc, size_t size) { - tx_desc->pp21.data_size = size; + if (port->priv->hw_version == MVPP21) + tx_desc->pp21.data_size = size; + else + tx_desc->pp22.data_size = size; } static void mvpp2_txdesc_txq_set(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc, unsigned int txq) { - tx_desc->pp21.phys_txq = txq; + if (port->priv->hw_version == MVPP21) + tx_desc->pp21.phys_txq = txq; + else + tx_desc->pp22.phys_txq = txq; } static void mvpp2_txdesc_cmd_set(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc, unsigned int command) { - tx_desc->pp21.command = command; + if (port->priv->hw_version == MVPP21) + tx_desc->pp21.command = command; + else + tx_desc->pp22.command = command; } static void mvpp2_txdesc_offset_set(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc, unsigned int offset) { - tx_desc->pp21.packet_offset = offset; + if (port->priv->hw_version == MVPP21) + tx_desc->pp21.packet_offset = offset; + else + tx_desc->pp22.packet_offset = offset; } static unsigned int mvpp2_txdesc_offset_get(struct mvpp2_port *port, struct mvpp2_tx_desc *tx_desc) { - return tx_desc->pp21.packet_offset; + if (port->priv->hw_version == MVPP21) + return tx_desc->pp21.packet_offset; + else + return tx_desc->pp22.packet_offset; } static dma_addr_t mvpp2_rxdesc_dma_addr_get(struct mvpp2_port *port, struct mvpp2_rx_desc *rx_desc) { - return rx_desc->pp21.buf_dma_addr; + if (port->priv->hw_version == MVPP21) + return rx_desc->pp21.buf_dma_addr; + else + return rx_desc->pp22.buf_dma_addr_key_hash & GENMASK_ULL(40, 0); } static unsigned long mvpp2_rxdesc_cookie_get(struct mvpp2_port *port, struct mvpp2_rx_desc *rx_desc) { - return rx_desc->pp21.buf_cookie; + if (port->priv->hw_version == MVPP21) + return rx_desc->pp21.buf_cookie; + else + return rx_desc->pp22.buf_cookie_misc & GENMASK_ULL(40, 0); } static size_t mvpp2_rxdesc_size_get(struct mvpp2_port *port, struct mvpp2_rx_desc *rx_desc) { - return rx_desc->pp21.data_size; + if (port->priv->hw_version == MVPP21) + return rx_desc->pp21.data_size; + else + return rx_desc->pp22.data_size; } static u32 mvpp2_rxdesc_status_get(struct mvpp2_port *port, struct mvpp2_rx_desc *rx_desc) { - return rx_desc->pp21.status; + if (port->priv->hw_version == MVPP21) + return rx_desc->pp21.status; + else + return rx_desc->pp22.status; } static void mvpp2_txq_inc_get(struct mvpp2_txq_pcpu *txq_pcpu) -- cgit From d01524d8abab00e3688c7435bcae4aeb461b51f7 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:09 +0100 Subject: net: mvpp2: adjust the allocation/free of BM pools for PPv2.2 This commit adjusts the allocation and freeing of BM pools to support PPv2.2. This involves: - Checking that the number of buffer pointers is a multiple of 16, as required by the hardware. - Adjusting the size of the DMA coherent area allocated for buffer pointers. Indeed, PPv2.2 needs space for 2 pointers of 64-bits per buffer, as opposed to 2 pointers of 32-bits per buffer in PPv2.1. The size in bytes is now stored in a new field of the mvpp2_bm_pool structure. - On PPv2.2, getting the DMA address and cookie (used for the physical address) of each buffer requires reading the MVPP22_BM_ADDR_HIGH_ALLOC to get the high order bits of those addresses. A new utility function mvpp2_bm_bufs_get_addrs() is introduced to handle this. - On PPv2.2, releasing a buffer requires writing the high order 32 bits of the DMA address and cookie to MVPP22_BM_PHY_VIRT_HIGH_RLS_REG. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 81 +++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 746b5bea714a..36aef5cd5446 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -208,11 +208,19 @@ #define MVPP2_BM_PHY_ALLOC_REG(pool) (0x6400 + ((pool) * 4)) #define MVPP2_BM_PHY_ALLOC_GRNTD_MASK BIT(0) #define MVPP2_BM_VIRT_ALLOC_REG 0x6440 +#define MVPP22_BM_ADDR_HIGH_ALLOC 0x6444 +#define MVPP22_BM_ADDR_HIGH_PHYS_MASK 0xff +#define MVPP22_BM_ADDR_HIGH_VIRT_MASK 0xff00 +#define MVPP22_BM_ADDR_HIGH_VIRT_SHIFT 8 #define MVPP2_BM_PHY_RLS_REG(pool) (0x6480 + ((pool) * 4)) #define MVPP2_BM_PHY_RLS_MC_BUFF_MASK BIT(0) #define MVPP2_BM_PHY_RLS_PRIO_EN_MASK BIT(1) #define MVPP2_BM_PHY_RLS_GRNTD_MASK BIT(2) #define MVPP2_BM_VIRT_RLS_REG 0x64c0 +#define MVPP22_BM_ADDR_HIGH_RLS_REG 0x64c4 +#define MVPP22_BM_ADDR_HIGH_PHYS_RLS_MASK 0xff +#define MVPP22_BM_ADDR_HIGH_VIRT_RLS_MASK 0xff00 +#define MVPP22_BM_ADDR_HIGH_VIRT_RLS_SHIFT 8 /* TX Scheduler registers */ #define MVPP2_TXP_SCHED_PORT_INDEX_REG 0x8000 @@ -951,6 +959,8 @@ struct mvpp2_bm_pool { /* Buffer Pointers Pool External (BPPE) size */ int size; + /* BPPE size in bytes */ + int size_bytes; /* Number of buffers for this pool */ int buf_num; /* Pool buffer size */ @@ -3520,11 +3530,23 @@ static int mvpp2_bm_pool_create(struct platform_device *pdev, struct mvpp2 *priv, struct mvpp2_bm_pool *bm_pool, int size) { - int size_bytes; u32 val; - size_bytes = sizeof(u32) * size; - bm_pool->virt_addr = dma_alloc_coherent(&pdev->dev, size_bytes, + /* Number of buffer pointers must be a multiple of 16, as per + * hardware constraints + */ + if (!IS_ALIGNED(size, 16)) + return -EINVAL; + + /* PPv2.1 needs 8 bytes per buffer pointer, PPv2.2 needs 16 + * bytes per buffer pointer + */ + if (priv->hw_version == MVPP21) + bm_pool->size_bytes = 2 * sizeof(u32) * size; + else + bm_pool->size_bytes = 2 * sizeof(u64) * size; + + bm_pool->virt_addr = dma_alloc_coherent(&pdev->dev, bm_pool->size_bytes, &bm_pool->dma_addr, GFP_KERNEL); if (!bm_pool->virt_addr) @@ -3532,15 +3554,15 @@ static int mvpp2_bm_pool_create(struct platform_device *pdev, if (!IS_ALIGNED((unsigned long)bm_pool->virt_addr, MVPP2_BM_POOL_PTR_ALIGN)) { - dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr, - bm_pool->dma_addr); + dma_free_coherent(&pdev->dev, bm_pool->size_bytes, + bm_pool->virt_addr, bm_pool->dma_addr); dev_err(&pdev->dev, "BM pool %d is not %d bytes aligned\n", bm_pool->id, MVPP2_BM_POOL_PTR_ALIGN); return -ENOMEM; } mvpp2_write(priv, MVPP2_BM_POOL_BASE_REG(bm_pool->id), - bm_pool->dma_addr); + lower_32_bits(bm_pool->dma_addr)); mvpp2_write(priv, MVPP2_BM_POOL_SIZE_REG(bm_pool->id), size); val = mvpp2_read(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id)); @@ -3568,6 +3590,31 @@ static void mvpp2_bm_pool_bufsize_set(struct mvpp2 *priv, mvpp2_write(priv, MVPP2_POOL_BUF_SIZE_REG(bm_pool->id), val); } +static void mvpp2_bm_bufs_get_addrs(struct device *dev, struct mvpp2 *priv, + struct mvpp2_bm_pool *bm_pool, + dma_addr_t *dma_addr, + phys_addr_t *phys_addr) +{ + *dma_addr = mvpp2_read(priv, MVPP2_BM_PHY_ALLOC_REG(bm_pool->id)); + *phys_addr = mvpp2_read(priv, MVPP2_BM_VIRT_ALLOC_REG); + + if (priv->hw_version == MVPP22) { + u32 val; + u32 dma_addr_highbits, phys_addr_highbits; + + val = mvpp2_read(priv, MVPP22_BM_ADDR_HIGH_ALLOC); + dma_addr_highbits = (val & MVPP22_BM_ADDR_HIGH_PHYS_MASK); + phys_addr_highbits = (val & MVPP22_BM_ADDR_HIGH_VIRT_MASK) >> + MVPP22_BM_ADDR_HIGH_VIRT_SHIFT; + + if (sizeof(dma_addr_t) == 8) + *dma_addr |= (u64)dma_addr_highbits << 32; + + if (sizeof(phys_addr_t) == 8) + *phys_addr |= (u64)phys_addr_highbits << 32; + } +} + /* Free all buffers from the pool */ static void mvpp2_bm_bufs_free(struct device *dev, struct mvpp2 *priv, struct mvpp2_bm_pool *bm_pool) @@ -3579,9 +3626,8 @@ static void mvpp2_bm_bufs_free(struct device *dev, struct mvpp2 *priv, phys_addr_t buf_phys_addr; void *data; - buf_dma_addr = mvpp2_read(priv, - MVPP2_BM_PHY_ALLOC_REG(bm_pool->id)); - buf_phys_addr = mvpp2_read(priv, MVPP2_BM_VIRT_ALLOC_REG); + mvpp2_bm_bufs_get_addrs(dev, priv, bm_pool, + &buf_dma_addr, &buf_phys_addr); dma_unmap_single(dev, buf_dma_addr, bm_pool->buf_size, DMA_FROM_DEVICE); @@ -3614,7 +3660,7 @@ static int mvpp2_bm_pool_destroy(struct platform_device *pdev, val |= MVPP2_BM_STOP_MASK; mvpp2_write(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id), val); - dma_free_coherent(&pdev->dev, sizeof(u32) * bm_pool->size, + dma_free_coherent(&pdev->dev, bm_pool->size_bytes, bm_pool->virt_addr, bm_pool->dma_addr); return 0; @@ -3752,6 +3798,21 @@ static inline void mvpp2_bm_pool_put(struct mvpp2_port *port, int pool, dma_addr_t buf_dma_addr, phys_addr_t buf_phys_addr) { + if (port->priv->hw_version == MVPP22) { + u32 val = 0; + + if (sizeof(dma_addr_t) == 8) + val |= upper_32_bits(buf_dma_addr) & + MVPP22_BM_ADDR_HIGH_PHYS_RLS_MASK; + + if (sizeof(phys_addr_t) == 8) + val |= (upper_32_bits(buf_phys_addr) + << MVPP22_BM_ADDR_HIGH_VIRT_RLS_SHIFT) & + MVPP22_BM_ADDR_HIGH_VIRT_RLS_MASK; + + mvpp2_write(port->priv, MVPP22_BM_ADDR_HIGH_RLS_REG, val); + } + /* MVPP2_BM_VIRT_RLS_REG is not interpreted by HW, and simply * returned in the "cookie" field of the RX * descriptor. Instead of storing the virtual address, we -- cgit From 5eac892ac937a9edb490a8ad8755db874c63983e Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:10 +0100 Subject: net: mvpp2: adapt the mvpp2_rxq_*_pool_set functions to PPv2.2 The MVPP2_RXQ_CONFIG_REG register has a slightly different layout between PPv2.1 and PPv2.2, so this commit adapts the functions modifying this register to accommodate for both the PPv2.1 and PPv2.2 cases. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 36aef5cd5446..91dca01b66a8 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -50,9 +50,11 @@ #define MVPP2_SNOOP_PKT_SIZE_MASK 0x1ff #define MVPP2_SNOOP_BUF_HDR_MASK BIT(9) #define MVPP2_RXQ_POOL_SHORT_OFFS 20 -#define MVPP2_RXQ_POOL_SHORT_MASK 0x700000 +#define MVPP21_RXQ_POOL_SHORT_MASK 0x700000 +#define MVPP22_RXQ_POOL_SHORT_MASK 0xf00000 #define MVPP2_RXQ_POOL_LONG_OFFS 24 -#define MVPP2_RXQ_POOL_LONG_MASK 0x7000000 +#define MVPP21_RXQ_POOL_LONG_MASK 0x7000000 +#define MVPP22_RXQ_POOL_LONG_MASK 0xf000000 #define MVPP2_RXQ_PACKET_OFFSET_OFFS 28 #define MVPP2_RXQ_PACKET_OFFSET_MASK 0x70000000 #define MVPP2_RXQ_DISABLE_MASK BIT(31) @@ -3718,17 +3720,20 @@ static int mvpp2_bm_init(struct platform_device *pdev, struct mvpp2 *priv) static void mvpp2_rxq_long_pool_set(struct mvpp2_port *port, int lrxq, int long_pool) { - u32 val; + u32 val, mask; int prxq; /* Get queue physical ID */ prxq = port->rxqs[lrxq]->id; - val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(prxq)); - val &= ~MVPP2_RXQ_POOL_LONG_MASK; - val |= ((long_pool << MVPP2_RXQ_POOL_LONG_OFFS) & - MVPP2_RXQ_POOL_LONG_MASK); + if (port->priv->hw_version == MVPP21) + mask = MVPP21_RXQ_POOL_LONG_MASK; + else + mask = MVPP22_RXQ_POOL_LONG_MASK; + val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(prxq)); + val &= ~mask; + val |= (long_pool << MVPP2_RXQ_POOL_LONG_OFFS) & mask; mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(prxq), val); } @@ -3736,17 +3741,20 @@ static void mvpp2_rxq_long_pool_set(struct mvpp2_port *port, static void mvpp2_rxq_short_pool_set(struct mvpp2_port *port, int lrxq, int short_pool) { - u32 val; + u32 val, mask; int prxq; /* Get queue physical ID */ prxq = port->rxqs[lrxq]->id; - val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(prxq)); - val &= ~MVPP2_RXQ_POOL_SHORT_MASK; - val |= ((short_pool << MVPP2_RXQ_POOL_SHORT_OFFS) & - MVPP2_RXQ_POOL_SHORT_MASK); + if (port->priv->hw_version == MVPP21) + mask = MVPP21_RXQ_POOL_SHORT_MASK; + else + mask = MVPP22_RXQ_POOL_SHORT_MASK; + val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(prxq)); + val &= ~mask; + val |= (short_pool << MVPP2_RXQ_POOL_SHORT_OFFS) & mask; mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(prxq), val); } -- cgit From 3d9017d9449bf8756f881970c970999c9148d306 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:11 +0100 Subject: net: mvpp2: adapt mvpp2_defaults_set() to PPv2.2 This commit modifies the mvpp2_defaults_set() function to not do the loopback and FIFO threshold initialization, which are not needed for PPv2.2. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 91dca01b66a8..da48d6066e7d 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -4157,16 +4157,18 @@ static void mvpp2_defaults_set(struct mvpp2_port *port) { int tx_port_num, val, queue, ptxq, lrxq; - /* Configure port to loopback if needed */ - if (port->flags & MVPP2_F_LOOPBACK) - mvpp2_port_loopback_set(port); - - /* Update TX FIFO MIN Threshold */ - val = readl(port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG); - val &= ~MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK; - /* Min. TX threshold must be less than minimal packet length */ - val |= MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(64 - 4 - 2); - writel(val, port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG); + if (port->priv->hw_version == MVPP21) { + /* Configure port to loopback if needed */ + if (port->flags & MVPP2_F_LOOPBACK) + mvpp2_port_loopback_set(port); + + /* Update TX FIFO MIN Threshold */ + val = readl(port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG); + val &= ~MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK; + /* Min. TX threshold must be less than minimal packet length */ + val |= MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(64 - 4 - 2); + writel(val, port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG); + } /* Disable Legacy WRR, Disable EJP, Release from reset */ tx_port_num = mvpp2_egress_port(port); -- cgit From b02f31fbf92d01ad47a7ea1e071301134be2d332 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:12 +0100 Subject: net: mvpp2: adjust mvpp2_{rxq, txq}_init for PPv2.2 In PPv2.2, the MVPP2_RXQ_DESC_ADDR_REG and MVPP2_TXQ_DESC_ADDR_REG registers have a slightly different layout, because they need to contain a 64-bit address for the RX and TX descriptor arrays. This commit adjusts those functions accordingly. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index da48d6066e7d..2eec380722c4 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -102,6 +102,7 @@ /* Descriptor Manager Top Registers */ #define MVPP2_RXQ_NUM_REG 0x2040 #define MVPP2_RXQ_DESC_ADDR_REG 0x2044 +#define MVPP22_DESC_ADDR_OFFS 8 #define MVPP2_RXQ_DESC_SIZE_REG 0x2048 #define MVPP2_RXQ_DESC_SIZE_MASK 0x3ff0 #define MVPP2_RXQ_STATUS_UPDATE_REG(rxq) (0x3000 + 4 * (rxq)) @@ -140,6 +141,7 @@ #define MVPP2_TXQ_RSVD_CLR_REG 0x20b8 #define MVPP2_TXQ_RSVD_CLR_OFFSET 16 #define MVPP2_AGGR_TXQ_DESC_ADDR_REG(cpu) (0x2100 + 4 * (cpu)) +#define MVPP22_AGGR_TXQ_DESC_ADDR_OFFS 8 #define MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu) (0x2140 + 4 * (cpu)) #define MVPP2_AGGR_TXQ_DESC_SIZE_MASK 0x3ff0 #define MVPP2_AGGR_TXQ_STATUS_REG(cpu) (0x2180 + 4 * (cpu)) @@ -4726,6 +4728,8 @@ static int mvpp2_aggr_txq_init(struct platform_device *pdev, int desc_num, int cpu, struct mvpp2 *priv) { + u32 txq_dma; + /* Allocate memory for TX descriptors */ aggr_txq->descs = dma_alloc_coherent(&pdev->dev, desc_num * MVPP2_DESC_ALIGNED_SIZE, @@ -4739,10 +4743,16 @@ static int mvpp2_aggr_txq_init(struct platform_device *pdev, aggr_txq->next_desc_to_proc = mvpp2_read(priv, MVPP2_AGGR_TXQ_INDEX_REG(cpu)); - /* Set Tx descriptors queue starting address */ - /* indirect access */ - mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_ADDR_REG(cpu), - aggr_txq->descs_dma); + /* Set Tx descriptors queue starting address indirect + * access + */ + if (priv->hw_version == MVPP21) + txq_dma = aggr_txq->descs_dma; + else + txq_dma = aggr_txq->descs_dma >> + MVPP22_AGGR_TXQ_DESC_ADDR_OFFS; + + mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_ADDR_REG(cpu), txq_dma); mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu), desc_num); return 0; @@ -4753,6 +4763,8 @@ static int mvpp2_rxq_init(struct mvpp2_port *port, struct mvpp2_rx_queue *rxq) { + u32 rxq_dma; + rxq->size = port->rx_ring_size; /* Allocate memory for RX descriptors */ @@ -4769,7 +4781,11 @@ static int mvpp2_rxq_init(struct mvpp2_port *port, /* Set Rx descriptors queue starting address - indirect access */ mvpp2_write(port->priv, MVPP2_RXQ_NUM_REG, rxq->id); - mvpp2_write(port->priv, MVPP2_RXQ_DESC_ADDR_REG, rxq->descs_dma); + if (port->priv->hw_version == MVPP21) + rxq_dma = rxq->descs_dma; + else + rxq_dma = rxq->descs_dma >> MVPP22_DESC_ADDR_OFFS; + mvpp2_write(port->priv, MVPP2_RXQ_DESC_ADDR_REG, rxq_dma); mvpp2_write(port->priv, MVPP2_RXQ_DESC_SIZE_REG, rxq->size); mvpp2_write(port->priv, MVPP2_RXQ_INDEX_REG, 0); -- cgit From a786841df72e37d574b6135b05ebcdb903034224 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:13 +0100 Subject: net: mvpp2: handle register mapping and access for PPv2.2 This commit adjusts the mvpp2 driver register mapping and access logic to support PPv2.2, to handle a number of differences. Due to how the registers are laid out in memory, the Device Tree binding for the "reg" property is different: - On PPv2.1, we had a first area for the packet processor registers (common to all ports), and then one area per port. - On PPv2.2, we have a first area for the packet processor registers (common to all ports), and a second area for numerous other registers, including a large number of per-port registers In addition, on PPv2.2, the area for the common registers is split into so-called "address spaces" of 64 KB each. They allow to access per-CPU registers, where each CPU has its own copy of some registers. A few other registers, which have a single copy, also need to be accessed from those per-CPU windows if they are related to a per-CPU register. For example: - Writing to MVPP2_TXQ_NUM_REG selects a TX queue. This register is a per-CPU register, it must be accessed from the current CPU register window. - Then a write to MVPP2_TXQ_PENDING_REG, MVPP2_TXQ_DESC_ADDR_REG (and a few others) will affect the TX queue that was selected by the write to MVPP2_TXQ_NUM_REG. It must be accessed from the same CPU window as the write to the TXQ_NUM_REG. Therefore, the ->base member of 'struct mvpp2' is replaced with a ->cpu_base[] array, each entry pointing to a mapping of the per-CPU area. Since PPv2.1 doesn't have this concept of per-CPU windows, all entries in ->cpu_base[] point to the same io-remapped area. The existing mvpp2_read() and mvpp2_write() accessors use cpu_base[0], they are used for registers for which the CPU window doesn't matter. mvpp2_percpu_read() and mvpp2_percpu_write() are new accessors added to access the registers for which the CPU window does matter, which is why they take a "cpu" as argument. The driver is then changed to use mvpp2_percpu_read() and mvpp2_percpu_write() where it matters. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 257 +++++++++++++++++++++++++---------- 1 file changed, 188 insertions(+), 69 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 2eec380722c4..2b4b4f082dce 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -295,6 +295,8 @@ #define MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(v) (((v) << 6) & \ MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK) +#define MVPP22_GMAC_BASE(port) (0x7000 + (port) * 0x1000 + 0xe00) + #define MVPP2_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff /* Descriptor ring Macros */ @@ -622,6 +624,11 @@ enum mvpp2_prs_l3_cast { */ #define MVPP2_BM_SHORT_PKT_SIZE MVPP2_RX_MAX_PKT_SIZE(512) +#define MVPP21_ADDR_SPACE_SZ 0 +#define MVPP22_ADDR_SPACE_SZ SZ_64K + +#define MVPP2_MAX_CPUS 4 + enum mvpp2_bm_type { MVPP2_BM_FREE, MVPP2_BM_SWF_LONG, @@ -633,8 +640,14 @@ enum mvpp2_bm_type { /* Shared Packet Processor resources */ struct mvpp2 { /* Shared registers' base addresses */ - void __iomem *base; void __iomem *lms_base; + void __iomem *iface_base; + + /* On PPv2.2, each CPU can access the base register through a + * separate address space, each 64 KB apart from each + * other. + */ + void __iomem *cpu_base[MVPP2_MAX_CPUS]; /* Common clocks */ struct clk *pp_clk; @@ -680,6 +693,11 @@ struct mvpp2_port_pcpu { struct mvpp2_port { u8 id; + /* Index of the port from the "group of ports" complex point + * of view + */ + int gop_id; + int irq; struct mvpp2 *priv; @@ -996,12 +1014,60 @@ static int txq_number = MVPP2_MAX_TXQ; static void mvpp2_write(struct mvpp2 *priv, u32 offset, u32 data) { - writel(data, priv->base + offset); + writel(data, priv->cpu_base[0] + offset); } static u32 mvpp2_read(struct mvpp2 *priv, u32 offset) { - return readl(priv->base + offset); + return readl(priv->cpu_base[0] + offset); +} + +/* These accessors should be used to access: + * + * - per-CPU registers, where each CPU has its own copy of the + * register. + * + * MVPP2_BM_VIRT_ALLOC_REG + * MVPP2_BM_ADDR_HIGH_ALLOC + * MVPP22_BM_ADDR_HIGH_RLS_REG + * MVPP2_BM_VIRT_RLS_REG + * MVPP2_ISR_RX_TX_CAUSE_REG + * MVPP2_ISR_RX_TX_MASK_REG + * MVPP2_TXQ_NUM_REG + * MVPP2_AGGR_TXQ_UPDATE_REG + * MVPP2_TXQ_RSVD_REQ_REG + * MVPP2_TXQ_RSVD_RSLT_REG + * MVPP2_TXQ_SENT_REG + * MVPP2_RXQ_NUM_REG + * + * - global registers that must be accessed through a specific CPU + * window, because they are related to an access to a per-CPU + * register + * + * MVPP2_BM_PHY_ALLOC_REG (related to MVPP2_BM_VIRT_ALLOC_REG) + * MVPP2_BM_PHY_RLS_REG (related to MVPP2_BM_VIRT_RLS_REG) + * MVPP2_RXQ_THRESH_REG (related to MVPP2_RXQ_NUM_REG) + * MVPP2_RXQ_DESC_ADDR_REG (related to MVPP2_RXQ_NUM_REG) + * MVPP2_RXQ_DESC_SIZE_REG (related to MVPP2_RXQ_NUM_REG) + * MVPP2_RXQ_INDEX_REG (related to MVPP2_RXQ_NUM_REG) + * MVPP2_TXQ_PENDING_REG (related to MVPP2_TXQ_NUM_REG) + * MVPP2_TXQ_DESC_ADDR_REG (related to MVPP2_TXQ_NUM_REG) + * MVPP2_TXQ_DESC_SIZE_REG (related to MVPP2_TXQ_NUM_REG) + * MVPP2_TXQ_INDEX_REG (related to MVPP2_TXQ_NUM_REG) + * MVPP2_TXQ_PENDING_REG (related to MVPP2_TXQ_NUM_REG) + * MVPP2_TXQ_PREF_BUF_REG (related to MVPP2_TXQ_NUM_REG) + * MVPP2_TXQ_PREF_BUF_REG (related to MVPP2_TXQ_NUM_REG) + */ +static void mvpp2_percpu_write(struct mvpp2 *priv, int cpu, + u32 offset, u32 data) +{ + writel(data, priv->cpu_base[cpu] + offset); +} + +static u32 mvpp2_percpu_read(struct mvpp2 *priv, int cpu, + u32 offset) +{ + return readl(priv->cpu_base[cpu] + offset); } static dma_addr_t mvpp2_txdesc_dma_addr_get(struct mvpp2_port *port, @@ -3599,14 +3665,17 @@ static void mvpp2_bm_bufs_get_addrs(struct device *dev, struct mvpp2 *priv, dma_addr_t *dma_addr, phys_addr_t *phys_addr) { - *dma_addr = mvpp2_read(priv, MVPP2_BM_PHY_ALLOC_REG(bm_pool->id)); - *phys_addr = mvpp2_read(priv, MVPP2_BM_VIRT_ALLOC_REG); + int cpu = smp_processor_id(); + + *dma_addr = mvpp2_percpu_read(priv, cpu, + MVPP2_BM_PHY_ALLOC_REG(bm_pool->id)); + *phys_addr = mvpp2_percpu_read(priv, cpu, MVPP2_BM_VIRT_ALLOC_REG); if (priv->hw_version == MVPP22) { u32 val; u32 dma_addr_highbits, phys_addr_highbits; - val = mvpp2_read(priv, MVPP22_BM_ADDR_HIGH_ALLOC); + val = mvpp2_percpu_read(priv, cpu, MVPP22_BM_ADDR_HIGH_ALLOC); dma_addr_highbits = (val & MVPP22_BM_ADDR_HIGH_PHYS_MASK); phys_addr_highbits = (val & MVPP22_BM_ADDR_HIGH_VIRT_MASK) >> MVPP22_BM_ADDR_HIGH_VIRT_SHIFT; @@ -3808,6 +3877,8 @@ static inline void mvpp2_bm_pool_put(struct mvpp2_port *port, int pool, dma_addr_t buf_dma_addr, phys_addr_t buf_phys_addr) { + int cpu = smp_processor_id(); + if (port->priv->hw_version == MVPP22) { u32 val = 0; @@ -3820,7 +3891,8 @@ static inline void mvpp2_bm_pool_put(struct mvpp2_port *port, int pool, << MVPP22_BM_ADDR_HIGH_VIRT_RLS_SHIFT) & MVPP22_BM_ADDR_HIGH_VIRT_RLS_MASK; - mvpp2_write(port->priv, MVPP22_BM_ADDR_HIGH_RLS_REG, val); + mvpp2_percpu_write(port->priv, cpu, + MVPP22_BM_ADDR_HIGH_RLS_REG, val); } /* MVPP2_BM_VIRT_RLS_REG is not interpreted by HW, and simply @@ -3828,8 +3900,10 @@ static inline void mvpp2_bm_pool_put(struct mvpp2_port *port, int pool, * descriptor. Instead of storing the virtual address, we * store the physical address */ - mvpp2_write(port->priv, MVPP2_BM_VIRT_RLS_REG, buf_phys_addr); - mvpp2_write(port->priv, MVPP2_BM_PHY_RLS_REG(pool), buf_dma_addr); + mvpp2_percpu_write(port->priv, cpu, + MVPP2_BM_VIRT_RLS_REG, buf_phys_addr); + mvpp2_percpu_write(port->priv, cpu, + MVPP2_BM_PHY_RLS_REG(pool), buf_dma_addr); } /* Refill BM pool */ @@ -4037,7 +4111,8 @@ static void mvpp2_interrupts_mask(void *arg) { struct mvpp2_port *port = arg; - mvpp2_write(port->priv, MVPP2_ISR_RX_TX_MASK_REG(port->id), 0); + mvpp2_percpu_write(port->priv, smp_processor_id(), + MVPP2_ISR_RX_TX_MASK_REG(port->id), 0); } /* Unmask the current CPU's Rx/Tx interrupts */ @@ -4045,9 +4120,10 @@ static void mvpp2_interrupts_unmask(void *arg) { struct mvpp2_port *port = arg; - mvpp2_write(port->priv, MVPP2_ISR_RX_TX_MASK_REG(port->id), - (MVPP2_CAUSE_MISC_SUM_MASK | - MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK)); + mvpp2_percpu_write(port->priv, smp_processor_id(), + MVPP2_ISR_RX_TX_MASK_REG(port->id), + (MVPP2_CAUSE_MISC_SUM_MASK | + MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK)); } /* Port configuration routines */ @@ -4388,7 +4464,8 @@ mvpp2_txq_next_desc_get(struct mvpp2_tx_queue *txq) static void mvpp2_aggr_txq_pend_desc_add(struct mvpp2_port *port, int pending) { /* aggregated access - relevant TXQ number is written in TX desc */ - mvpp2_write(port->priv, MVPP2_AGGR_TXQ_UPDATE_REG, pending); + mvpp2_percpu_write(port->priv, smp_processor_id(), + MVPP2_AGGR_TXQ_UPDATE_REG, pending); } @@ -4417,11 +4494,12 @@ static int mvpp2_txq_alloc_reserved_desc(struct mvpp2 *priv, struct mvpp2_tx_queue *txq, int num) { u32 val; + int cpu = smp_processor_id(); val = (txq->id << MVPP2_TXQ_RSVD_REQ_Q_OFFSET) | num; - mvpp2_write(priv, MVPP2_TXQ_RSVD_REQ_REG, val); + mvpp2_percpu_write(priv, cpu, MVPP2_TXQ_RSVD_REQ_REG, val); - val = mvpp2_read(priv, MVPP2_TXQ_RSVD_RSLT_REG); + val = mvpp2_percpu_read(priv, cpu, MVPP2_TXQ_RSVD_RSLT_REG); return val & MVPP2_TXQ_RSVD_RSLT_MASK; } @@ -4522,7 +4600,8 @@ static inline int mvpp2_txq_sent_desc_proc(struct mvpp2_port *port, u32 val; /* Reading status reg resets transmitted descriptor counter */ - val = mvpp2_read(port->priv, MVPP2_TXQ_SENT_REG(txq->id)); + val = mvpp2_percpu_read(port->priv, smp_processor_id(), + MVPP2_TXQ_SENT_REG(txq->id)); return (val & MVPP2_TRANSMITTED_COUNT_MASK) >> MVPP2_TRANSMITTED_COUNT_OFFSET; @@ -4536,7 +4615,8 @@ static void mvpp2_txq_sent_counter_clear(void *arg) for (queue = 0; queue < txq_number; queue++) { int id = port->txqs[queue]->id; - mvpp2_read(port->priv, MVPP2_TXQ_SENT_REG(id)); + mvpp2_percpu_read(port->priv, smp_processor_id(), + MVPP2_TXQ_SENT_REG(id)); } } @@ -4595,12 +4675,14 @@ static void mvpp2_txp_max_tx_size_set(struct mvpp2_port *port) static void mvpp2_rx_pkts_coal_set(struct mvpp2_port *port, struct mvpp2_rx_queue *rxq) { + int cpu = smp_processor_id(); + if (rxq->pkts_coal > MVPP2_OCCUPIED_THRESH_MASK) rxq->pkts_coal = MVPP2_OCCUPIED_THRESH_MASK; - mvpp2_write(port->priv, MVPP2_RXQ_NUM_REG, rxq->id); - mvpp2_write(port->priv, MVPP2_RXQ_THRESH_REG, - rxq->pkts_coal); + mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_NUM_REG, rxq->id); + mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_THRESH_REG, + rxq->pkts_coal); } static u32 mvpp2_usec_to_cycles(u32 usec, unsigned long clk_hz) @@ -4764,6 +4846,7 @@ static int mvpp2_rxq_init(struct mvpp2_port *port, { u32 rxq_dma; + int cpu; rxq->size = port->rx_ring_size; @@ -4780,14 +4863,15 @@ static int mvpp2_rxq_init(struct mvpp2_port *port, mvpp2_write(port->priv, MVPP2_RXQ_STATUS_REG(rxq->id), 0); /* Set Rx descriptors queue starting address - indirect access */ - mvpp2_write(port->priv, MVPP2_RXQ_NUM_REG, rxq->id); + cpu = smp_processor_id(); + mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_NUM_REG, rxq->id); if (port->priv->hw_version == MVPP21) rxq_dma = rxq->descs_dma; else rxq_dma = rxq->descs_dma >> MVPP22_DESC_ADDR_OFFS; - mvpp2_write(port->priv, MVPP2_RXQ_DESC_ADDR_REG, rxq_dma); - mvpp2_write(port->priv, MVPP2_RXQ_DESC_SIZE_REG, rxq->size); - mvpp2_write(port->priv, MVPP2_RXQ_INDEX_REG, 0); + mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_DESC_ADDR_REG, rxq_dma); + mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_DESC_SIZE_REG, rxq->size); + mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_INDEX_REG, 0); /* Set Offset */ mvpp2_rxq_offset_set(port, rxq->id, NET_SKB_PAD); @@ -4827,6 +4911,8 @@ static void mvpp2_rxq_drop_pkts(struct mvpp2_port *port, static void mvpp2_rxq_deinit(struct mvpp2_port *port, struct mvpp2_rx_queue *rxq) { + int cpu; + mvpp2_rxq_drop_pkts(port, rxq); if (rxq->descs) @@ -4844,9 +4930,10 @@ static void mvpp2_rxq_deinit(struct mvpp2_port *port, * free descriptor number */ mvpp2_write(port->priv, MVPP2_RXQ_STATUS_REG(rxq->id), 0); - mvpp2_write(port->priv, MVPP2_RXQ_NUM_REG, rxq->id); - mvpp2_write(port->priv, MVPP2_RXQ_DESC_ADDR_REG, 0); - mvpp2_write(port->priv, MVPP2_RXQ_DESC_SIZE_REG, 0); + cpu = smp_processor_id(); + mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_NUM_REG, rxq->id); + mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_DESC_ADDR_REG, 0); + mvpp2_percpu_write(port->priv, cpu, MVPP2_RXQ_DESC_SIZE_REG, 0); } /* Create and initialize a Tx queue */ @@ -4869,16 +4956,18 @@ static int mvpp2_txq_init(struct mvpp2_port *port, txq->last_desc = txq->size - 1; /* Set Tx descriptors queue starting address - indirect access */ - mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); - mvpp2_write(port->priv, MVPP2_TXQ_DESC_ADDR_REG, txq->descs_dma); - mvpp2_write(port->priv, MVPP2_TXQ_DESC_SIZE_REG, txq->size & - MVPP2_TXQ_DESC_SIZE_MASK); - mvpp2_write(port->priv, MVPP2_TXQ_INDEX_REG, 0); - mvpp2_write(port->priv, MVPP2_TXQ_RSVD_CLR_REG, - txq->id << MVPP2_TXQ_RSVD_CLR_OFFSET); - val = mvpp2_read(port->priv, MVPP2_TXQ_PENDING_REG); + cpu = smp_processor_id(); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_NUM_REG, txq->id); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_DESC_ADDR_REG, + txq->descs_dma); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_DESC_SIZE_REG, + txq->size & MVPP2_TXQ_DESC_SIZE_MASK); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_INDEX_REG, 0); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_RSVD_CLR_REG, + txq->id << MVPP2_TXQ_RSVD_CLR_OFFSET); + val = mvpp2_percpu_read(port->priv, cpu, MVPP2_TXQ_PENDING_REG); val &= ~MVPP2_TXQ_PENDING_MASK; - mvpp2_write(port->priv, MVPP2_TXQ_PENDING_REG, val); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_PENDING_REG, val); /* Calculate base address in prefetch buffer. We reserve 16 descriptors * for each existing TXQ. @@ -4889,9 +4978,9 @@ static int mvpp2_txq_init(struct mvpp2_port *port, desc = (port->id * MVPP2_MAX_TXQ * desc_per_txq) + (txq->log_id * desc_per_txq); - mvpp2_write(port->priv, MVPP2_TXQ_PREF_BUF_REG, - MVPP2_PREF_BUF_PTR(desc) | MVPP2_PREF_BUF_SIZE_16 | - MVPP2_PREF_BUF_THRESH(desc_per_txq/2)); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_PREF_BUF_REG, + MVPP2_PREF_BUF_PTR(desc) | MVPP2_PREF_BUF_SIZE_16 | + MVPP2_PREF_BUF_THRESH(desc_per_txq / 2)); /* WRR / EJP configuration - indirect access */ tx_port_num = mvpp2_egress_port(port); @@ -4963,9 +5052,10 @@ static void mvpp2_txq_deinit(struct mvpp2_port *port, mvpp2_write(port->priv, MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(txq->id), 0); /* Set Tx descriptors queue starting address and size */ - mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); - mvpp2_write(port->priv, MVPP2_TXQ_DESC_ADDR_REG, 0); - mvpp2_write(port->priv, MVPP2_TXQ_DESC_SIZE_REG, 0); + cpu = smp_processor_id(); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_NUM_REG, txq->id); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_DESC_ADDR_REG, 0); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_DESC_SIZE_REG, 0); } /* Cleanup Tx ports */ @@ -4975,10 +5065,11 @@ static void mvpp2_txq_clean(struct mvpp2_port *port, struct mvpp2_tx_queue *txq) int delay, pending, cpu; u32 val; - mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); - val = mvpp2_read(port->priv, MVPP2_TXQ_PREF_BUF_REG); + cpu = smp_processor_id(); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_NUM_REG, txq->id); + val = mvpp2_percpu_read(port->priv, cpu, MVPP2_TXQ_PREF_BUF_REG); val |= MVPP2_TXQ_DRAIN_EN_MASK; - mvpp2_write(port->priv, MVPP2_TXQ_PREF_BUF_REG, val); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_PREF_BUF_REG, val); /* The napi queue has been stopped so wait for all packets * to be transmitted. @@ -4994,12 +5085,13 @@ static void mvpp2_txq_clean(struct mvpp2_port *port, struct mvpp2_tx_queue *txq) mdelay(1); delay++; - pending = mvpp2_read(port->priv, MVPP2_TXQ_PENDING_REG) & - MVPP2_TXQ_PENDING_MASK; + pending = mvpp2_percpu_read(port->priv, cpu, + MVPP2_TXQ_PENDING_REG); + pending &= MVPP2_TXQ_PENDING_MASK; } while (pending); val &= ~MVPP2_TXQ_DRAIN_EN_MASK; - mvpp2_write(port->priv, MVPP2_TXQ_PREF_BUF_REG, val); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_PREF_BUF_REG, val); for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); @@ -5585,6 +5677,7 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) u32 cause_rx_tx, cause_rx, cause_misc; int rx_done = 0; struct mvpp2_port *port = netdev_priv(napi->dev); + int cpu = smp_processor_id(); /* Rx/Tx cause register * @@ -5596,8 +5689,8 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) * * Each CPU has its own Rx/Tx cause register */ - cause_rx_tx = mvpp2_read(port->priv, - MVPP2_ISR_RX_TX_CAUSE_REG(port->id)); + cause_rx_tx = mvpp2_percpu_read(port->priv, cpu, + MVPP2_ISR_RX_TX_CAUSE_REG(port->id)); cause_rx_tx &= ~MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK; @@ -5606,8 +5699,9 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) /* Clear the cause register */ mvpp2_write(port->priv, MVPP2_ISR_MISC_CAUSE_REG, 0); - mvpp2_write(port->priv, MVPP2_ISR_RX_TX_CAUSE_REG(port->id), - cause_rx_tx & ~MVPP2_CAUSE_MISC_SUM_MASK); + mvpp2_percpu_write(port->priv, cpu, + MVPP2_ISR_RX_TX_CAUSE_REG(port->id), + cause_rx_tx & ~MVPP2_CAUSE_MISC_SUM_MASK); } cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; @@ -6306,7 +6400,6 @@ static int mvpp2_port_probe(struct platform_device *pdev, u32 id; int features; int phy_mode; - int priv_common_regs_num = 2; int err, i, cpu; dev = alloc_etherdev_mqs(sizeof(struct mvpp2_port), txq_number, @@ -6356,12 +6449,22 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->phy_node = phy_node; port->phy_interface = phy_mode; - res = platform_get_resource(pdev, IORESOURCE_MEM, - priv_common_regs_num + id); - port->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(port->base)) { - err = PTR_ERR(port->base); - goto err_free_irq; + if (priv->hw_version == MVPP21) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 2 + id); + port->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(port->base)) { + err = PTR_ERR(port->base); + goto err_free_irq; + } + } else { + if (of_property_read_u32(port_node, "gop-port-id", + &port->gop_id)) { + err = -EINVAL; + dev_err(&pdev->dev, "missing gop-port-id value\n"); + goto err_free_irq; + } + + port->base = priv->iface_base + MVPP22_GMAC_BASE(port->gop_id); } /* Alloc per-cpu stats */ @@ -6594,7 +6697,8 @@ static int mvpp2_probe(struct platform_device *pdev) struct device_node *port_node; struct mvpp2 *priv; struct resource *res; - int port_count, first_rxq; + void __iomem *base; + int port_count, first_rxq, cpu; int err; priv = devm_kzalloc(&pdev->dev, sizeof(struct mvpp2), GFP_KERNEL); @@ -6605,14 +6709,29 @@ static int mvpp2_probe(struct platform_device *pdev) (unsigned long)of_device_get_match_data(&pdev->dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - priv->lms_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(priv->lms_base)) - return PTR_ERR(priv->lms_base); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + if (priv->hw_version == MVPP21) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + priv->lms_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->lms_base)) + return PTR_ERR(priv->lms_base); + } else { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + priv->iface_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->iface_base)) + return PTR_ERR(priv->iface_base); + } + + for_each_present_cpu(cpu) { + u32 addr_space_sz; + + addr_space_sz = (priv->hw_version == MVPP21 ? + MVPP21_ADDR_SPACE_SZ : MVPP22_ADDR_SPACE_SZ); + priv->cpu_base[cpu] = base + cpu * addr_space_sz; + } priv->pp_clk = devm_clk_get(&pdev->dev, "pp_clk"); if (IS_ERR(priv->pp_clk)) -- cgit From 2697582144dd813b0e071ac18e4e59b4f53192de Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:14 +0100 Subject: net: mvpp2: handle misc PPv2.1/PPv2.2 differences This commit handles a few miscellaneous differences between PPv2.1 and PPv2.2 in different areas, where code done for PPv2.1 doesn't apply for PPv2.2 or needs to be adjusted (getting the MAC address, disabling PHY polling, etc.). Thanks to Russell King for providing the initial implementation of mvpp22_port_mii_set(). Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 85 ++++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 2b4b4f082dce..bd7dc4b6eb2d 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -294,6 +294,22 @@ #define MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0 #define MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(v) (((v) << 6) & \ MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK) +#define MVPP22_GMAC_CTRL_4_REG 0x90 +#define MVPP22_CTRL4_EXT_PIN_GMII_SEL BIT(0) +#define MVPP22_CTRL4_DP_CLK_SEL BIT(5) +#define MVPP22_CTRL4_SYNC_BYPASS BIT(6) +#define MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE BIT(7) + +/* Per-port XGMAC registers. PPv2.2 only, only for GOP port 0, + * relative to port->base. + */ +#define MVPP22_XLG_CTRL3_REG 0x11c +#define MVPP22_XLG_CTRL3_MACMODESELECT_MASK (7 << 13) +#define MVPP22_XLG_CTRL3_MACMODESELECT_GMAC (0 << 13) + +/* SMI registers. PPv2.2 only, relative to priv->iface_base. */ +#define MVPP22_SMI_MISC_CFG_REG 0x1204 +#define MVPP22_SMI_POLLING_EN BIT(10) #define MVPP22_GMAC_BASE(port) (0x7000 + (port) * 0x1000 + 0xe00) @@ -4128,10 +4144,38 @@ static void mvpp2_interrupts_unmask(void *arg) /* Port configuration routines */ +static void mvpp22_port_mii_set(struct mvpp2_port *port) +{ + u32 val; + + return; + + /* Only GOP port 0 has an XLG MAC */ + if (port->gop_id == 0) { + val = readl(port->base + MVPP22_XLG_CTRL3_REG); + val &= ~MVPP22_XLG_CTRL3_MACMODESELECT_MASK; + val |= MVPP22_XLG_CTRL3_MACMODESELECT_GMAC; + writel(val, port->base + MVPP22_XLG_CTRL3_REG); + } + + val = readl(port->base + MVPP22_GMAC_CTRL_4_REG); + if (port->phy_interface == PHY_INTERFACE_MODE_RGMII) + val |= MVPP22_CTRL4_EXT_PIN_GMII_SEL; + else + val &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL; + val &= ~MVPP22_CTRL4_DP_CLK_SEL; + val |= MVPP22_CTRL4_SYNC_BYPASS; + val |= MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE; + writel(val, port->base + MVPP22_GMAC_CTRL_4_REG); +} + static void mvpp2_port_mii_set(struct mvpp2_port *port) { u32 val; + if (port->priv->hw_version == MVPP22) + mvpp22_port_mii_set(port); + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); switch (port->phy_interface) { @@ -5813,7 +5857,7 @@ static int mvpp2_check_ringparam_valid(struct net_device *dev, return 0; } -static void mvpp2_get_mac_address(struct mvpp2_port *port, unsigned char *addr) +static void mvpp21_get_mac_address(struct mvpp2_port *port, unsigned char *addr) { u32 mac_addr_l, mac_addr_m, mac_addr_h; @@ -6258,16 +6302,6 @@ static const struct ethtool_ops mvpp2_eth_tool_ops = { .set_link_ksettings = phy_ethtool_set_link_ksettings, }; -/* Driver initialization */ - -static void mvpp2_port_power_up(struct mvpp2_port *port) -{ - mvpp2_port_mii_set(port); - mvpp2_port_periodic_xon_disable(port); - mvpp2_port_fc_adv_enable(port); - mvpp2_port_reset(port); -} - /* Initialize port HW */ static int mvpp2_port_init(struct mvpp2_port *port) { @@ -6479,7 +6513,8 @@ static int mvpp2_port_probe(struct platform_device *pdev, mac_from = "device tree"; ether_addr_copy(dev->dev_addr, dt_mac_addr); } else { - mvpp2_get_mac_address(port, hw_mac_addr); + if (priv->hw_version == MVPP21) + mvpp21_get_mac_address(port, hw_mac_addr); if (is_valid_ether_addr(hw_mac_addr)) { mac_from = "hardware"; ether_addr_copy(dev->dev_addr, hw_mac_addr); @@ -6499,7 +6534,14 @@ static int mvpp2_port_probe(struct platform_device *pdev, dev_err(&pdev->dev, "failed to init port %d\n", id); goto err_free_stats; } - mvpp2_port_power_up(port); + + mvpp2_port_mii_set(port); + mvpp2_port_periodic_xon_disable(port); + + if (priv->hw_version == MVPP21) + mvpp2_port_fc_adv_enable(port); + + mvpp2_port_reset(port); port->pcpu = alloc_percpu(struct mvpp2_port_pcpu); if (!port->pcpu) { @@ -6642,9 +6684,15 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) mvpp2_conf_mbus_windows(dram_target_info, priv); /* Disable HW PHY polling */ - val = readl(priv->lms_base + MVPP2_PHY_AN_CFG0_REG); - val |= MVPP2_PHY_AN_STOP_SMI0_MASK; - writel(val, priv->lms_base + MVPP2_PHY_AN_CFG0_REG); + if (priv->hw_version == MVPP21) { + val = readl(priv->lms_base + MVPP2_PHY_AN_CFG0_REG); + val |= MVPP2_PHY_AN_STOP_SMI0_MASK; + writel(val, priv->lms_base + MVPP2_PHY_AN_CFG0_REG); + } else { + val = readl(priv->iface_base + MVPP22_SMI_MISC_CFG_REG); + val &= ~MVPP22_SMI_POLLING_EN; + writel(val, priv->iface_base + MVPP22_SMI_MISC_CFG_REG); + } /* Allocate and initialize aggregated TXQs */ priv->aggr_txqs = devm_kcalloc(&pdev->dev, num_present_cpus(), @@ -6669,8 +6717,9 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) for (i = 0; i < MVPP2_MAX_PORTS; i++) mvpp2_write(priv, MVPP2_ISR_RXQ_GROUP_REG(i), rxq_number); - writel(MVPP2_EXT_GLOBAL_CTRL_DEFAULT, - priv->lms_base + MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG); + if (priv->hw_version == MVPP21) + writel(MVPP2_EXT_GLOBAL_CTRL_DEFAULT, + priv->lms_base + MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG); /* Allow cache snoop when transmiting packets */ mvpp2_write(priv, MVPP2_TX_SNOOP_REG, 0x1); -- cgit From 6763ce3127ed6f346fe60975deea12e290dca719 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:15 +0100 Subject: net: mvpp2: add AXI bridge initialization for PPv2.2 The PPv2.2 unit is connected to an AXI bus on Armada 7K/8K, so this commit adds the necessary initialization of the AXI bridge. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 85 ++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index bd7dc4b6eb2d..0e103032d16c 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -154,6 +154,34 @@ #define MVPP2_WIN_REMAP(w) (0x4040 + ((w) << 2)) #define MVPP2_BASE_ADDR_ENABLE 0x4060 +/* AXI Bridge Registers */ +#define MVPP22_AXI_BM_WR_ATTR_REG 0x4100 +#define MVPP22_AXI_BM_RD_ATTR_REG 0x4104 +#define MVPP22_AXI_AGGRQ_DESCR_RD_ATTR_REG 0x4110 +#define MVPP22_AXI_TXQ_DESCR_WR_ATTR_REG 0x4114 +#define MVPP22_AXI_TXQ_DESCR_RD_ATTR_REG 0x4118 +#define MVPP22_AXI_RXQ_DESCR_WR_ATTR_REG 0x411c +#define MVPP22_AXI_RX_DATA_WR_ATTR_REG 0x4120 +#define MVPP22_AXI_TX_DATA_RD_ATTR_REG 0x4130 +#define MVPP22_AXI_RD_NORMAL_CODE_REG 0x4150 +#define MVPP22_AXI_RD_SNOOP_CODE_REG 0x4154 +#define MVPP22_AXI_WR_NORMAL_CODE_REG 0x4160 +#define MVPP22_AXI_WR_SNOOP_CODE_REG 0x4164 + +/* Values for AXI Bridge registers */ +#define MVPP22_AXI_ATTR_CACHE_OFFS 0 +#define MVPP22_AXI_ATTR_DOMAIN_OFFS 12 + +#define MVPP22_AXI_CODE_CACHE_OFFS 0 +#define MVPP22_AXI_CODE_DOMAIN_OFFS 4 + +#define MVPP22_AXI_CODE_CACHE_NON_CACHE 0x3 +#define MVPP22_AXI_CODE_CACHE_WR_CACHE 0x7 +#define MVPP22_AXI_CODE_CACHE_RD_CACHE 0xb + +#define MVPP22_AXI_CODE_DOMAIN_OUTER_DOM 2 +#define MVPP22_AXI_CODE_DOMAIN_SYSTEM 3 + /* Interrupt Cause and Mask registers */ #define MVPP2_ISR_RX_THRESHOLD_REG(rxq) (0x5200 + 4 * (rxq)) #define MVPP2_MAX_ISR_RX_THRESHOLD 0xfffff0 @@ -6664,6 +6692,60 @@ static void mvpp2_rx_fifo_init(struct mvpp2 *priv) mvpp2_write(priv, MVPP2_RX_FIFO_INIT_REG, 0x1); } +static void mvpp2_axi_init(struct mvpp2 *priv) +{ + u32 val, rdval, wrval; + + mvpp2_write(priv, MVPP22_BM_ADDR_HIGH_RLS_REG, 0x0); + + /* AXI Bridge Configuration */ + + rdval = MVPP22_AXI_CODE_CACHE_RD_CACHE + << MVPP22_AXI_ATTR_CACHE_OFFS; + rdval |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM + << MVPP22_AXI_ATTR_DOMAIN_OFFS; + + wrval = MVPP22_AXI_CODE_CACHE_WR_CACHE + << MVPP22_AXI_ATTR_CACHE_OFFS; + wrval |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM + << MVPP22_AXI_ATTR_DOMAIN_OFFS; + + /* BM */ + mvpp2_write(priv, MVPP22_AXI_BM_WR_ATTR_REG, wrval); + mvpp2_write(priv, MVPP22_AXI_BM_RD_ATTR_REG, rdval); + + /* Descriptors */ + mvpp2_write(priv, MVPP22_AXI_AGGRQ_DESCR_RD_ATTR_REG, rdval); + mvpp2_write(priv, MVPP22_AXI_TXQ_DESCR_WR_ATTR_REG, wrval); + mvpp2_write(priv, MVPP22_AXI_TXQ_DESCR_RD_ATTR_REG, rdval); + mvpp2_write(priv, MVPP22_AXI_RXQ_DESCR_WR_ATTR_REG, wrval); + + /* Buffer Data */ + mvpp2_write(priv, MVPP22_AXI_TX_DATA_RD_ATTR_REG, rdval); + mvpp2_write(priv, MVPP22_AXI_RX_DATA_WR_ATTR_REG, wrval); + + val = MVPP22_AXI_CODE_CACHE_NON_CACHE + << MVPP22_AXI_CODE_CACHE_OFFS; + val |= MVPP22_AXI_CODE_DOMAIN_SYSTEM + << MVPP22_AXI_CODE_DOMAIN_OFFS; + mvpp2_write(priv, MVPP22_AXI_RD_NORMAL_CODE_REG, val); + mvpp2_write(priv, MVPP22_AXI_WR_NORMAL_CODE_REG, val); + + val = MVPP22_AXI_CODE_CACHE_RD_CACHE + << MVPP22_AXI_CODE_CACHE_OFFS; + val |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM + << MVPP22_AXI_CODE_DOMAIN_OFFS; + + mvpp2_write(priv, MVPP22_AXI_RD_SNOOP_CODE_REG, val); + + val = MVPP22_AXI_CODE_CACHE_WR_CACHE + << MVPP22_AXI_CODE_CACHE_OFFS; + val |= MVPP22_AXI_CODE_DOMAIN_OUTER_DOM + << MVPP22_AXI_CODE_DOMAIN_OFFS; + + mvpp2_write(priv, MVPP22_AXI_WR_SNOOP_CODE_REG, val); +} + /* Initialize network controller common part HW */ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) { @@ -6683,6 +6765,9 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) if (dram_target_info) mvpp2_conf_mbus_windows(dram_target_info, priv); + if (priv->hw_version == MVPP22) + mvpp2_axi_init(priv); + /* Disable HW PHY polling */ if (priv->hw_version == MVPP21) { val = readl(priv->lms_base + MVPP2_PHY_AN_CFG0_REG); -- cgit From a73fef100275d5228cad5041b3e55af859f30d1e Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:16 +0100 Subject: net: mvpp2: rework RXQ interrupt group initialization for PPv2.2 This commit adjusts how the MVPP2_ISR_RXQ_GROUP_REG register is configured, since it changed between PPv2.1 and PPv2.2. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 46 ++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 0e103032d16c..21f47d280995 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -185,7 +185,21 @@ /* Interrupt Cause and Mask registers */ #define MVPP2_ISR_RX_THRESHOLD_REG(rxq) (0x5200 + 4 * (rxq)) #define MVPP2_MAX_ISR_RX_THRESHOLD 0xfffff0 -#define MVPP2_ISR_RXQ_GROUP_REG(rxq) (0x5400 + 4 * (rxq)) +#define MVPP21_ISR_RXQ_GROUP_REG(rxq) (0x5400 + 4 * (rxq)) + +#define MVPP22_ISR_RXQ_GROUP_INDEX_REG 0x5400 +#define MVPP22_ISR_RXQ_GROUP_INDEX_SUBGROUP_MASK 0xf +#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 +#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET 7 + +#define MVPP22_ISR_RXQ_GROUP_INDEX_SUBGROUP_MASK 0xf +#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 + +#define MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG 0x5404 +#define MVPP22_ISR_RXQ_SUB_GROUP_STARTQ_MASK 0x1f +#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_MASK 0xf00 +#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET 8 + #define MVPP2_ISR_ENABLE_REG(port) (0x5420 + 4 * (port)) #define MVPP2_ISR_ENABLE_INTERRUPT(mask) ((mask) & 0xffff) #define MVPP2_ISR_DISABLE_INTERRUPT(mask) (((mask) << 16) & 0xffff0000) @@ -6406,7 +6420,18 @@ static int mvpp2_port_init(struct mvpp2_port *port) } /* Configure Rx queue group interrupt for this port */ - mvpp2_write(priv, MVPP2_ISR_RXQ_GROUP_REG(port->id), rxq_number); + if (priv->hw_version == MVPP21) { + mvpp2_write(priv, MVPP21_ISR_RXQ_GROUP_REG(port->id), + rxq_number); + } else { + u32 val; + + val = (port->id << MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET); + mvpp2_write(priv, MVPP22_ISR_RXQ_GROUP_INDEX_REG, val); + + val = (rxq_number << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET); + mvpp2_write(priv, MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG, val); + } /* Create Rx descriptor rings */ for (queue = 0; queue < rxq_number; queue++) { @@ -6799,8 +6824,21 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) mvpp2_rx_fifo_init(priv); /* Reset Rx queue group interrupt configuration */ - for (i = 0; i < MVPP2_MAX_PORTS; i++) - mvpp2_write(priv, MVPP2_ISR_RXQ_GROUP_REG(i), rxq_number); + for (i = 0; i < MVPP2_MAX_PORTS; i++) { + if (priv->hw_version == MVPP21) { + mvpp2_write(priv, MVPP21_ISR_RXQ_GROUP_REG(i), + rxq_number); + continue; + } else { + u32 val; + + val = (i << MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET); + mvpp2_write(priv, MVPP22_ISR_RXQ_GROUP_INDEX_REG, val); + + val = (rxq_number << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET); + mvpp2_write(priv, MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG, val); + } + } if (priv->hw_version == MVPP21) writel(MVPP2_EXT_GLOBAL_CTRL_DEFAULT, -- cgit From 59b9a31ede18ef54dcac466052706bb47a394ab5 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:17 +0100 Subject: net: mvpp2: adapt rxq distribution to PPv2.2 In PPv2.1, we have a maximum of 8 RXQs per port, with a default of 4 RXQs per port, and we were assigning RXQs 0->3 to the first port, 4->7 to the second port, 8->11 to the third port, etc. In PPv2.2, we have a maximum of 32 RXQs per port, and we must allocate RXQs from the range of 32 RXQs available for each port. So port 0 must use RXQs in the range 0->31, port 1 in the range 32->63, etc. This commit adapts the mvpp2 to this difference between PPv2.1 and PPv2.2: - The constant definition MVPP2_MAX_RXQ is replaced by a new field 'max_port_rxqs' in 'struct mvpp2', which stores the maximum number of RXQs per port. This field is initialized during ->probe() depending on the IP version. - MVPP2_RXQ_TOTAL_NUM is removed, and instead we calculate the total number of RXQs by multiplying the number of ports by the maximum of RXQs per port. This was anyway used in only one place. - In mvpp2_port_probe(), the calculation of port->first_rxq is adjusted to cope with the different allocation strategy between PPv2.1 and PPv2.2. Due to this change, the 'next_first_rxq' argument of this function is no longer needed and is removed. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 21f47d280995..1bb1aa52618d 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -402,15 +402,9 @@ /* Maximum number of TXQs used by single port */ #define MVPP2_MAX_TXQ 8 -/* Maximum number of RXQs used by single port */ -#define MVPP2_MAX_RXQ 8 - /* Dfault number of RXQs in use */ #define MVPP2_DEFAULT_RXQ 4 -/* Total number of RXQs available to all ports */ -#define MVPP2_RXQ_TOTAL_NUM (MVPP2_MAX_PORTS * MVPP2_MAX_RXQ) - /* Max number of Rx descriptors */ #define MVPP2_MAX_RXD 128 @@ -730,6 +724,9 @@ struct mvpp2 { /* HW version */ enum { MVPP21, MVPP22 } hw_version; + + /* Maximum number of RXQs per port */ + unsigned int max_port_rxqs; }; struct mvpp2_pcpu_stats { @@ -6352,7 +6349,8 @@ static int mvpp2_port_init(struct mvpp2_port *port) struct mvpp2_txq_pcpu *txq_pcpu; int queue, cpu, err; - if (port->first_rxq + rxq_number > MVPP2_RXQ_TOTAL_NUM) + if (port->first_rxq + rxq_number > + MVPP2_MAX_PORTS * priv->max_port_rxqs) return -EINVAL; /* Disable port */ @@ -6473,8 +6471,7 @@ err_free_percpu: /* Ports initialization */ static int mvpp2_port_probe(struct platform_device *pdev, struct device_node *port_node, - struct mvpp2 *priv, - int *next_first_rxq) + struct mvpp2 *priv) { struct device_node *phy_node; struct mvpp2_port *port; @@ -6532,7 +6529,11 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->priv = priv; port->id = id; - port->first_rxq = *next_first_rxq; + if (priv->hw_version == MVPP21) + port->first_rxq = port->id * rxq_number; + else + port->first_rxq = port->id * priv->max_port_rxqs; + port->phy_node = phy_node; port->phy_interface = phy_mode; @@ -6632,8 +6633,6 @@ static int mvpp2_port_probe(struct platform_device *pdev, } netdev_info(dev, "Using %s mac address %pM\n", mac_from, dev->dev_addr); - /* Increment the first Rx queue number to be used by the next port */ - *next_first_rxq += rxq_number; priv->port_list[id] = port; return 0; @@ -6779,7 +6778,7 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) u32 val; /* Checks for hardware constraints */ - if (rxq_number % 4 || (rxq_number > MVPP2_MAX_RXQ) || + if (rxq_number % 4 || (rxq_number > priv->max_port_rxqs) || (txq_number > MVPP2_MAX_TXQ)) { dev_err(&pdev->dev, "invalid queue size parameter\n"); return -EINVAL; @@ -6870,7 +6869,7 @@ static int mvpp2_probe(struct platform_device *pdev) struct mvpp2 *priv; struct resource *res; void __iomem *base; - int port_count, first_rxq, cpu; + int port_count, cpu; int err; priv = devm_kzalloc(&pdev->dev, sizeof(struct mvpp2), GFP_KERNEL); @@ -6905,6 +6904,11 @@ static int mvpp2_probe(struct platform_device *pdev) priv->cpu_base[cpu] = base + cpu * addr_space_sz; } + if (priv->hw_version == MVPP21) + priv->max_port_rxqs = 8; + else + priv->max_port_rxqs = 32; + priv->pp_clk = devm_clk_get(&pdev->dev, "pp_clk"); if (IS_ERR(priv->pp_clk)) return PTR_ERR(priv->pp_clk); @@ -6947,9 +6951,8 @@ static int mvpp2_probe(struct platform_device *pdev) } /* Initialize ports */ - first_rxq = 0; for_each_available_child_of_node(dn, port_node) { - err = mvpp2_port_probe(pdev, port_node, priv, &first_rxq); + err = mvpp2_port_probe(pdev, port_node, priv); if (err < 0) goto err_gop_clk; } -- cgit From fceb55d41508c01ac05f1c9382d7efddcdf71409 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:18 +0100 Subject: net: mvpp2: add support for an additional clock needed for PPv2.2 The PPv2.2 variant of the network controller needs an additional clock, the "MG clock" in order for the IP block to operate properly. This commit adds support for this additional clock to the driver, reworking as needed the error handling path. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 1bb1aa52618d..0a3c47017809 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -704,6 +704,7 @@ struct mvpp2 { /* Common clocks */ struct clk *pp_clk; struct clk *gop_clk; + struct clk *mg_clk; /* List of pointers to port structures */ struct mvpp2_port **port_list; @@ -6925,6 +6926,18 @@ static int mvpp2_probe(struct platform_device *pdev) if (err < 0) goto err_pp_clk; + if (priv->hw_version == MVPP22) { + priv->mg_clk = devm_clk_get(&pdev->dev, "mg_clk"); + if (IS_ERR(priv->mg_clk)) { + err = PTR_ERR(priv->mg_clk); + goto err_gop_clk; + } + + err = clk_prepare_enable(priv->mg_clk); + if (err < 0) + goto err_gop_clk; + } + /* Get system's tclk rate */ priv->tclk = clk_get_rate(priv->pp_clk); @@ -6932,14 +6945,14 @@ static int mvpp2_probe(struct platform_device *pdev) err = mvpp2_init(pdev, priv); if (err < 0) { dev_err(&pdev->dev, "failed to initialize controller\n"); - goto err_gop_clk; + goto err_mg_clk; } port_count = of_get_available_child_count(dn); if (port_count == 0) { dev_err(&pdev->dev, "no ports enabled\n"); err = -ENODEV; - goto err_gop_clk; + goto err_mg_clk; } priv->port_list = devm_kcalloc(&pdev->dev, port_count, @@ -6947,19 +6960,22 @@ static int mvpp2_probe(struct platform_device *pdev) GFP_KERNEL); if (!priv->port_list) { err = -ENOMEM; - goto err_gop_clk; + goto err_mg_clk; } /* Initialize ports */ for_each_available_child_of_node(dn, port_node) { err = mvpp2_port_probe(pdev, port_node, priv); if (err < 0) - goto err_gop_clk; + goto err_mg_clk; } platform_set_drvdata(pdev, priv); return 0; +err_mg_clk: + if (priv->hw_version == MVPP22) + clk_disable_unprepare(priv->mg_clk); err_gop_clk: clk_disable_unprepare(priv->gop_clk); err_pp_clk: @@ -6995,6 +7011,7 @@ static int mvpp2_remove(struct platform_device *pdev) aggr_txq->descs_dma); } + clk_disable_unprepare(priv->mg_clk); clk_disable_unprepare(priv->pp_clk); clk_disable_unprepare(priv->gop_clk); -- cgit From 2067e0a13cfe0b1bdca7b91bc5e4f2740b07d478 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:19 +0100 Subject: net: mvpp2: set dma mask and coherent dma mask on PPv2.2 On PPv2.2, the streaming mappings can be anywhere in the first 40 bits of the physical address space. However, for the coherent mappings, we still need them to be in the first 32 bits of the address space, because all BM pools share a single register to store the high 32 bits of the BM pool address, which means all BM pools must be allocated in the same 4GB memory area. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 0a3c47017809..92c47f35bbdb 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -6941,6 +6941,20 @@ static int mvpp2_probe(struct platform_device *pdev) /* Get system's tclk rate */ priv->tclk = clk_get_rate(priv->pp_clk); + if (priv->hw_version == MVPP22) { + err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)); + if (err) + goto err_mg_clk; + /* Sadly, the BM pools all share the same register to + * store the high 32 bits of their address. So they + * must all have the same high 32 bits, which forces + * us to restrict coherent memory to DMA_BIT_MASK(32). + */ + err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (err) + goto err_mg_clk; + } + /* Initialize network controller */ err = mvpp2_init(pdev, priv); if (err < 0) { -- cgit From fc5e1550e5c365ecd94497fe6d2f230a5e8a2a22 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Tue, 7 Mar 2017 16:53:20 +0100 Subject: net: mvpp2: finally add the PPv2.2 compatible string Now that the mvpp2 driver has been modified to accommodate the support for PPv2.2, we can finally advertise this support by adding the appropriate compatible string. At the same time, we update the Kconfig description of the MVPP2 driver. Signed-off-by: Thomas Petazzoni Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/Kconfig | 4 ++-- drivers/net/ethernet/marvell/mvpp2.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index d2555e8b947e..da6fb825afea 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -82,13 +82,13 @@ config MVNETA_BM that all dependencies are met. config MVPP2 - tristate "Marvell Armada 375 network interface support" + tristate "Marvell Armada 375/7K/8K network interface support" depends on ARCH_MVEBU || COMPILE_TEST depends on HAS_DMA select MVMDIO ---help--- This driver supports the network interface units in the - Marvell ARMADA 375 SoC. + Marvell ARMADA 375, 7K and 8K SoCs. config PXA168_ETH tristate "Marvell pxa168 ethernet support" diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 92c47f35bbdb..af5bfa13d976 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -7037,6 +7037,10 @@ static const struct of_device_id mvpp2_match[] = { .compatible = "marvell,armada-375-pp2", .data = (void *)MVPP21, }, + { + .compatible = "marvell,armada-7k-pp22", + .data = (void *)MVPP22, + }, { } }; MODULE_DEVICE_TABLE(of, mvpp2_match); -- cgit From 038e893d78037b5876771f84722b459b3916bfaf Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 6 Mar 2017 12:56:02 -0800 Subject: tg3: Add the ability to conditionally build w/ HWMON Introduce a Kconfig option: CONFIG_TIGON3_HWMON which allows to build in/out support for thermal sensors reported by Tigon3 NICs. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/Kconfig | 8 +++++++- drivers/net/ethernet/broadcom/tg3.c | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index 940fb24bba21..96413808c726 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -109,7 +109,6 @@ config TIGON3 tristate "Broadcom Tigon3 support" depends on PCI select PHYLIB - select HWMON imply PTP_1588_CLOCK ---help--- This driver supports Broadcom Tigon3 based gigabit Ethernet cards. @@ -117,6 +116,13 @@ config TIGON3 To compile this driver as a module, choose M here: the module will be called tg3. This is recommended. +config TIGON3_HWMON + bool "Broadcom Tigon3 HWMON support" + default y + depends on TIGON3 && HWMON && !(TIGON3=y && HWMON=m) + ---help--- + Say Y if you want to expose the thermal sensor on Tigon3 devices. + config BNX2X tristate "Broadcom NetXtremeII 10Gb support" depends on PCI diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 30d1eb9ebec9..f395b951f5e7 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -825,6 +825,7 @@ static int tg3_ape_event_lock(struct tg3 *tp, u32 timeout_us) return timeout_us ? 0 : -EBUSY; } +#ifdef CONFIG_TIGON3_HWMON static int tg3_ape_wait_for_event(struct tg3 *tp, u32 timeout_us) { u32 i, apedata; @@ -904,6 +905,7 @@ static int tg3_ape_scratchpad_read(struct tg3 *tp, u32 *data, u32 base_off, return 0; } +#endif static int tg3_ape_send_event(struct tg3 *tp, u32 event) { @@ -10744,6 +10746,7 @@ static int tg3_init_hw(struct tg3 *tp, bool reset_phy) return tg3_reset_hw(tp, reset_phy); } +#ifdef CONFIG_TIGON3_HWMON static void tg3_sd_scan_scratchpad(struct tg3 *tp, struct tg3_ocir *ocir) { int i; @@ -10826,6 +10829,10 @@ static void tg3_hwmon_open(struct tg3 *tp) dev_err(&pdev->dev, "Cannot register hwmon device, aborting\n"); } } +#else +static inline void tg3_hwmon_close(struct tg3 *tp) { } +static inline void tg3_hwmon_open(struct tg3 *tp) { } +#endif /* CONFIG_TIGON3_HWMON */ #define TG3_STAT_ADD32(PSTAT, REG) \ -- cgit From e3c36e483b4806a6bf28db411f79ea9aad61ec78 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 7 Mar 2017 16:27:10 +0100 Subject: net: mediatek: Use eth_hw_addr_random() Use eth_hw_addr_random() to set a random dev_addr and update addr_assign_type instead of open-coding it. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 9e757684816d..bf6317eca2f6 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -1908,10 +1908,9 @@ static int __init mtk_init(struct net_device *dev) /* If the mac address is invalid, use random mac address */ if (!is_valid_ether_addr(dev->dev_addr)) { - random_ether_addr(dev->dev_addr); + eth_hw_addr_random(dev); dev_err(eth->dev, "generated random MAC address %pM\n", dev->dev_addr); - dev->addr_assign_type = NET_ADDR_RANDOM; } return mtk_phy_connect(dev); -- cgit From 64f48e593a54a8bee8bee4bf8391d5a4e9057d34 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Tue, 7 Mar 2017 15:27:36 +0000 Subject: net: stmicro: replace kzalloc with devm_kzalloc The axi variable was not being freed upon device removal. With devm_kzalloc it ensures that it is properly freed. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 433a84239a68..0ba1caf18619 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -108,7 +108,7 @@ static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev) if (!np) return NULL; - axi = kzalloc(sizeof(*axi), GFP_KERNEL); + axi = devm_kzalloc(&pdev->dev, sizeof(*axi), GFP_KERNEL); if (!axi) { of_node_put(np); return ERR_PTR(-ENOMEM); -- cgit From 35ae57eae9135f891640f3076fd0a3c990632af0 Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Tue, 7 Mar 2017 11:40:41 -0800 Subject: liquidio: add support for XPS Add support for XPS. Signed-off-by: Rick Farrington Signed-off-by: Felix Manlunas Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index be9c0e3f5ade..dffed432d58e 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -2553,6 +2553,15 @@ static inline int setup_io_queues(struct octeon_device *octeon_dev, __func__); return 1; } + + if (octeon_dev->ioq_vector) { + struct octeon_ioq_vector *ioq_vector; + + ioq_vector = &octeon_dev->ioq_vector[q]; + netif_set_xps_queue(netdev, + &ioq_vector->affinity_mask, + ioq_vector->iq_index); + } } return 0; -- cgit From 384fe7a4d732c3812542142ce5bb353973b34638 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Tue, 7 Mar 2017 17:08:40 -0800 Subject: drivers: net: xgene-v2: Add DMA descriptor This patch adds DMA descriptor setup and interrupt enable/disable functions. Signed-off-by: Iyappan Subramanian Signed-off-by: Keyur Chudgar Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene-v2/main.h | 74 +++++++++++++++++++ drivers/net/ethernet/apm/xgene-v2/ring.c | 81 +++++++++++++++++++++ drivers/net/ethernet/apm/xgene-v2/ring.h | 119 +++++++++++++++++++++++++++++++ 3 files changed, 274 insertions(+) create mode 100644 drivers/net/ethernet/apm/xgene-v2/main.h create mode 100644 drivers/net/ethernet/apm/xgene-v2/ring.c create mode 100644 drivers/net/ethernet/apm/xgene-v2/ring.h diff --git a/drivers/net/ethernet/apm/xgene-v2/main.h b/drivers/net/ethernet/apm/xgene-v2/main.h new file mode 100644 index 000000000000..a2f87126dce0 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/main.h @@ -0,0 +1,74 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian + * Keyur Chudgar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __XGENE_ENET_V2_MAIN_H__ +#define __XGENE_ENET_V2_MAIN_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mac.h" +#include "enet.h" +#include "ring.h" + +#define XGENE_ENET_V2_VERSION "v1.0" +#define XGENE_ENET_STD_MTU 1536 +#define XGENE_ENET_MIN_FRAME 60 +#define IRQ_ID_SIZE 16 + +struct xge_resource { + void __iomem *base_addr; + int phy_mode; + u32 irq; +}; + +struct xge_stats { + u64 tx_packets; + u64 tx_bytes; + u64 rx_packets; + u64 rx_bytes; +}; + +/* ethernet private data */ +struct xge_pdata { + struct xge_resource resources; + struct xge_desc_ring *tx_ring; + struct xge_desc_ring *rx_ring; + struct platform_device *pdev; + char irq_name[IRQ_ID_SIZE]; + struct net_device *ndev; + struct napi_struct napi; + struct xge_stats stats; + int phy_speed; + u8 nbufs; +}; + +#endif /* __XGENE_ENET_V2_MAIN_H__ */ diff --git a/drivers/net/ethernet/apm/xgene-v2/ring.c b/drivers/net/ethernet/apm/xgene-v2/ring.c new file mode 100644 index 000000000000..38810828f8f0 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/ring.c @@ -0,0 +1,81 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian + * Keyur Chudgar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +/* create circular linked list of descriptors */ +void xge_setup_desc(struct xge_desc_ring *ring) +{ + struct xge_raw_desc *raw_desc; + dma_addr_t dma_h, next_dma; + u16 offset; + int i; + + for (i = 0; i < XGENE_ENET_NUM_DESC; i++) { + raw_desc = &ring->raw_desc[i]; + + offset = (i + 1) & (XGENE_ENET_NUM_DESC - 1); + next_dma = ring->dma_addr + (offset * XGENE_ENET_DESC_SIZE); + + raw_desc->m0 = cpu_to_le64(SET_BITS(E, 1) | + SET_BITS(PKT_SIZE, SLOT_EMPTY)); + dma_h = upper_32_bits(next_dma); + raw_desc->m1 = cpu_to_le64(SET_BITS(NEXT_DESC_ADDRL, next_dma) | + SET_BITS(NEXT_DESC_ADDRH, dma_h)); + } +} + +void xge_update_tx_desc_addr(struct xge_pdata *pdata) +{ + struct xge_desc_ring *ring = pdata->tx_ring; + dma_addr_t dma_addr = ring->dma_addr; + + xge_wr_csr(pdata, DMATXDESCL, dma_addr); + xge_wr_csr(pdata, DMATXDESCH, upper_32_bits(dma_addr)); + + ring->head = 0; + ring->tail = 0; +} + +void xge_update_rx_desc_addr(struct xge_pdata *pdata) +{ + struct xge_desc_ring *ring = pdata->rx_ring; + dma_addr_t dma_addr = ring->dma_addr; + + xge_wr_csr(pdata, DMARXDESCL, dma_addr); + xge_wr_csr(pdata, DMARXDESCH, upper_32_bits(dma_addr)); + + ring->head = 0; + ring->tail = 0; +} + +void xge_intr_enable(struct xge_pdata *pdata) +{ + u32 data; + + data = RX_PKT_RCVD | TX_PKT_SENT; + xge_wr_csr(pdata, DMAINTRMASK, data); +} + +void xge_intr_disable(struct xge_pdata *pdata) +{ + xge_wr_csr(pdata, DMAINTRMASK, 0); +} diff --git a/drivers/net/ethernet/apm/xgene-v2/ring.h b/drivers/net/ethernet/apm/xgene-v2/ring.h new file mode 100644 index 000000000000..abc8c9a84954 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/ring.h @@ -0,0 +1,119 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian + * Keyur Chudgar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __XGENE_ENET_V2_RING_H__ +#define __XGENE_ENET_V2_RING_H__ + +#define XGENE_ENET_DESC_SIZE 64 +#define XGENE_ENET_NUM_DESC 256 +#define NUM_BUFS 8 +#define SLOT_EMPTY 0xfff + +#define DMATXCTRL 0xa180 +#define DMATXDESCL 0xa184 +#define DMATXDESCH 0xa1a0 +#define DMATXSTATUS 0xa188 +#define DMARXCTRL 0xa18c +#define DMARXDESCL 0xa190 +#define DMARXDESCH 0xa1a4 +#define DMARXSTATUS 0xa194 +#define DMAINTRMASK 0xa198 +#define DMAINTERRUPT 0xa19c + +#define D_POS 62 +#define D_LEN 2 +#define E_POS 63 +#define E_LEN 1 +#define PKT_ADDRL_POS 0 +#define PKT_ADDRL_LEN 32 +#define PKT_ADDRH_POS 32 +#define PKT_ADDRH_LEN 10 +#define PKT_SIZE_POS 32 +#define PKT_SIZE_LEN 12 +#define NEXT_DESC_ADDRL_POS 0 +#define NEXT_DESC_ADDRL_LEN 32 +#define NEXT_DESC_ADDRH_POS 48 +#define NEXT_DESC_ADDRH_LEN 10 + +#define TXPKTCOUNT_POS 16 +#define TXPKTCOUNT_LEN 8 +#define RXPKTCOUNT_POS 16 +#define RXPKTCOUNT_LEN 8 + +#define TX_PKT_SENT BIT(0) +#define TX_BUS_ERROR BIT(3) +#define RX_PKT_RCVD BIT(4) +#define RX_BUS_ERROR BIT(7) +#define RXSTATUS_RXPKTRCVD BIT(0) + +struct xge_raw_desc { + __le64 m0; + __le64 m1; + __le64 m2; + __le64 m3; + __le64 m4; + __le64 m5; + __le64 m6; + __le64 m7; +}; + +struct pkt_info { + struct sk_buff *skb; + dma_addr_t dma_addr; + void *pkt_buf; +}; + +/* software context of a descriptor ring */ +struct xge_desc_ring { + struct net_device *ndev; + dma_addr_t dma_addr; + u8 head; + u8 tail; + union { + void *desc_addr; + struct xge_raw_desc *raw_desc; + }; + struct pkt_info (*pkt_info); +}; + +static inline u64 xge_set_desc_bits(int pos, int len, u64 val) +{ + return (val & ((1ULL << len) - 1)) << pos; +} + +static inline u64 xge_get_desc_bits(int pos, int len, u64 src) +{ + return (src >> pos) & ((1ULL << len) - 1); +} + +#define SET_BITS(field, val) \ + xge_set_desc_bits(field ## _POS, field ## _LEN, val) + +#define GET_BITS(field, src) \ + xge_get_desc_bits(field ## _POS, field ## _LEN, src) + +void xge_setup_desc(struct xge_desc_ring *ring); +void xge_update_tx_desc_addr(struct xge_pdata *pdata); +void xge_update_rx_desc_addr(struct xge_pdata *pdata); +void xge_intr_enable(struct xge_pdata *pdata); +void xge_intr_disable(struct xge_pdata *pdata); + +#endif /* __XGENE_ENET_V2_RING_H__ */ -- cgit From 81ccd0cab29b682a0f5337fd2076a77aa8e4472e Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Tue, 7 Mar 2017 17:08:41 -0800 Subject: drivers: net: xgene-v2: Add mac configuration This patch adds functions to configure and control mac. This patch also adds helper functions to get/set registers. Signed-off-by: Iyappan Subramanian Signed-off-by: Keyur Chudgar Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene-v2/mac.c | 116 ++++++++++++++++++++++++++++++++ drivers/net/ethernet/apm/xgene-v2/mac.h | 110 ++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 drivers/net/ethernet/apm/xgene-v2/mac.c create mode 100644 drivers/net/ethernet/apm/xgene-v2/mac.h diff --git a/drivers/net/ethernet/apm/xgene-v2/mac.c b/drivers/net/ethernet/apm/xgene-v2/mac.c new file mode 100644 index 000000000000..9c3d32d3b7dd --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/mac.c @@ -0,0 +1,116 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian + * Keyur Chudgar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +void xge_mac_reset(struct xge_pdata *pdata) +{ + xge_wr_csr(pdata, MAC_CONFIG_1, SOFT_RESET); + xge_wr_csr(pdata, MAC_CONFIG_1, 0); +} + +static void xge_mac_set_speed(struct xge_pdata *pdata) +{ + u32 icm0, icm2, ecm0, mc2; + u32 intf_ctrl, rgmii; + + icm0 = xge_rd_csr(pdata, ICM_CONFIG0_REG_0); + icm2 = xge_rd_csr(pdata, ICM_CONFIG2_REG_0); + ecm0 = xge_rd_csr(pdata, ECM_CONFIG0_REG_0); + rgmii = xge_rd_csr(pdata, RGMII_REG_0); + mc2 = xge_rd_csr(pdata, MAC_CONFIG_2); + intf_ctrl = xge_rd_csr(pdata, INTERFACE_CONTROL); + icm2 |= CFG_WAITASYNCRD_EN; + + switch (pdata->phy_speed) { + case SPEED_10: + SET_REG_BITS(&mc2, INTF_MODE, 1); + SET_REG_BITS(&intf_ctrl, HD_MODE, 0); + SET_REG_BITS(&icm0, CFG_MACMODE, 0); + SET_REG_BITS(&icm2, CFG_WAITASYNCRD, 500); + SET_REG_BIT(&rgmii, CFG_SPEED_125, 0); + break; + case SPEED_100: + SET_REG_BITS(&mc2, INTF_MODE, 1); + SET_REG_BITS(&intf_ctrl, HD_MODE, 1); + SET_REG_BITS(&icm0, CFG_MACMODE, 1); + SET_REG_BITS(&icm2, CFG_WAITASYNCRD, 80); + SET_REG_BIT(&rgmii, CFG_SPEED_125, 0); + break; + default: + SET_REG_BITS(&mc2, INTF_MODE, 2); + SET_REG_BITS(&intf_ctrl, HD_MODE, 2); + SET_REG_BITS(&icm0, CFG_MACMODE, 2); + SET_REG_BITS(&icm2, CFG_WAITASYNCRD, 16); + SET_REG_BIT(&rgmii, CFG_SPEED_125, 1); + break; + } + + mc2 |= FULL_DUPLEX | CRC_EN | PAD_CRC; + SET_REG_BITS(&ecm0, CFG_WFIFOFULLTHR, 0x32); + + xge_wr_csr(pdata, MAC_CONFIG_2, mc2); + xge_wr_csr(pdata, INTERFACE_CONTROL, intf_ctrl); + xge_wr_csr(pdata, RGMII_REG_0, rgmii); + xge_wr_csr(pdata, ICM_CONFIG0_REG_0, icm0); + xge_wr_csr(pdata, ICM_CONFIG2_REG_0, icm2); + xge_wr_csr(pdata, ECM_CONFIG0_REG_0, ecm0); +} + +void xge_mac_set_station_addr(struct xge_pdata *pdata) +{ + u32 addr0, addr1; + u8 *dev_addr = pdata->ndev->dev_addr; + + addr0 = (dev_addr[3] << 24) | (dev_addr[2] << 16) | + (dev_addr[1] << 8) | dev_addr[0]; + addr1 = (dev_addr[5] << 24) | (dev_addr[4] << 16); + + xge_wr_csr(pdata, STATION_ADDR0, addr0); + xge_wr_csr(pdata, STATION_ADDR1, addr1); +} + +void xge_mac_init(struct xge_pdata *pdata) +{ + xge_mac_reset(pdata); + xge_mac_set_speed(pdata); + xge_mac_set_station_addr(pdata); +} + +void xge_mac_enable(struct xge_pdata *pdata) +{ + u32 data; + + data = xge_rd_csr(pdata, MAC_CONFIG_1); + data |= TX_EN | RX_EN; + xge_wr_csr(pdata, MAC_CONFIG_1, data); + + data = xge_rd_csr(pdata, MAC_CONFIG_1); +} + +void xge_mac_disable(struct xge_pdata *pdata) +{ + u32 data; + + data = xge_rd_csr(pdata, MAC_CONFIG_1); + data &= ~(TX_EN | RX_EN); + xge_wr_csr(pdata, MAC_CONFIG_1, data); +} diff --git a/drivers/net/ethernet/apm/xgene-v2/mac.h b/drivers/net/ethernet/apm/xgene-v2/mac.h new file mode 100644 index 000000000000..0fce6ae15ce0 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/mac.h @@ -0,0 +1,110 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian + * Keyur Chudgar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __XGENE_ENET_V2_MAC_H__ +#define __XGENE_ENET_V2_MAC_H__ + +/* Register offsets */ +#define MAC_CONFIG_1 0xa000 +#define MAC_CONFIG_2 0xa004 +#define MII_MGMT_CONFIG 0xa020 +#define MII_MGMT_COMMAND 0xa024 +#define MII_MGMT_ADDRESS 0xa028 +#define MII_MGMT_CONTROL 0xa02c +#define MII_MGMT_STATUS 0xa030 +#define MII_MGMT_INDICATORS 0xa034 +#define INTERFACE_CONTROL 0xa038 +#define STATION_ADDR0 0xa040 +#define STATION_ADDR1 0xa044 +#define RBYT 0xa09c +#define RPKT 0xa0a0 +#define RFCS 0xa0a4 + +#define RGMII_REG_0 0x27e0 +#define ICM_CONFIG0_REG_0 0x2c00 +#define ICM_CONFIG2_REG_0 0x2c08 +#define ECM_CONFIG0_REG_0 0x2d00 + +/* Register fields */ +#define SOFT_RESET BIT(31) +#define TX_EN BIT(0) +#define RX_EN BIT(2) +#define PAD_CRC BIT(2) +#define CRC_EN BIT(1) +#define FULL_DUPLEX BIT(0) + +#define INTF_MODE_POS 8 +#define INTF_MODE_LEN 2 +#define HD_MODE_POS 25 +#define HD_MODE_LEN 2 +#define CFG_MACMODE_POS 18 +#define CFG_MACMODE_LEN 2 +#define CFG_WAITASYNCRD_POS 0 +#define CFG_WAITASYNCRD_LEN 16 +#define CFG_SPEED_125_POS 24 +#define CFG_WFIFOFULLTHR_POS 0 +#define CFG_WFIFOFULLTHR_LEN 7 +#define MGMT_CLOCK_SEL_POS 0 +#define MGMT_CLOCK_SEL_LEN 3 +#define PHY_ADDR_POS 8 +#define PHY_ADDR_LEN 5 +#define REG_ADDR_POS 0 +#define REG_ADDR_LEN 5 +#define MII_MGMT_BUSY BIT(0) +#define MII_READ_CYCLE BIT(0) +#define CFG_WAITASYNCRD_EN BIT(16) + +static inline void xgene_set_reg_bits(u32 *var, int pos, int len, u32 val) +{ + u32 mask = GENMASK(pos + len, pos); + + *var &= ~mask; + *var |= ((val << pos) & mask); +} + +static inline u32 xgene_get_reg_bits(u32 var, int pos, int len) +{ + u32 mask = GENMASK(pos + len, pos); + + return (var & mask) >> pos; +} + +#define SET_REG_BITS(var, field, val) \ + xgene_set_reg_bits(var, field ## _POS, field ## _LEN, val) + +#define SET_REG_BIT(var, field, val) \ + xgene_set_reg_bits(var, field ## _POS, 1, val) + +#define GET_REG_BITS(var, field) \ + xgene_get_reg_bits(var, field ## _POS, field ## _LEN) + +#define GET_REG_BIT(var, field) ((var) & (field)) + +struct xge_pdata; + +void xge_mac_reset(struct xge_pdata *pdata); +void xge_mac_enable(struct xge_pdata *pdata); +void xge_mac_disable(struct xge_pdata *pdata); +void xge_mac_init(struct xge_pdata *pdata); +int xge_port_init(struct net_device *ndev); +void xge_mac_set_station_addr(struct xge_pdata *pdata); + +#endif /* __XGENE_ENET_V2_MAC_H__ */ -- cgit From 272d6dc1fcab6307c10bd411c614933bf10877e5 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Tue, 7 Mar 2017 17:08:42 -0800 Subject: drivers: net: xgene-v2: Add ethernet hardware configuration This patch adds functions to configure ethernet hardware. Signed-off-by: Iyappan Subramanian Signed-off-by: Keyur Chudgar Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene-v2/enet.c | 71 ++++++++++++++++++++++++++++++++ drivers/net/ethernet/apm/xgene-v2/enet.h | 43 +++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 drivers/net/ethernet/apm/xgene-v2/enet.c create mode 100644 drivers/net/ethernet/apm/xgene-v2/enet.h diff --git a/drivers/net/ethernet/apm/xgene-v2/enet.c b/drivers/net/ethernet/apm/xgene-v2/enet.c new file mode 100644 index 000000000000..b49edeeb6275 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/enet.c @@ -0,0 +1,71 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian + * Keyur Chudgar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +void xge_wr_csr(struct xge_pdata *pdata, u32 offset, u32 val) +{ + void __iomem *addr = pdata->resources.base_addr + offset; + + iowrite32(val, addr); +} + +u32 xge_rd_csr(struct xge_pdata *pdata, u32 offset) +{ + void __iomem *addr = pdata->resources.base_addr + offset; + + return ioread32(addr); +} + +int xge_port_reset(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + + xge_wr_csr(pdata, ENET_SRST, 0x3); + xge_wr_csr(pdata, ENET_SRST, 0x2); + xge_wr_csr(pdata, ENET_SRST, 0x0); + + xge_wr_csr(pdata, ENET_SHIM, DEVM_ARAUX_COH | DEVM_AWAUX_COH); + + return 0; +} + +static void xge_traffic_resume(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + + xge_wr_csr(pdata, CFG_FORCE_LINK_STATUS_EN, 1); + xge_wr_csr(pdata, FORCE_LINK_STATUS, 1); + + xge_wr_csr(pdata, CFG_LINK_AGGR_RESUME, 1); + xge_wr_csr(pdata, RX_DV_GATE_REG, 1); +} + +int xge_port_init(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + + pdata->phy_speed = SPEED_1000; + xge_mac_init(pdata); + xge_traffic_resume(ndev); + + return 0; +} diff --git a/drivers/net/ethernet/apm/xgene-v2/enet.h b/drivers/net/ethernet/apm/xgene-v2/enet.h new file mode 100644 index 000000000000..40371cfcfce4 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/enet.h @@ -0,0 +1,43 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian + * Keyur Chudgar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __XGENE_ENET_V2_ENET_H__ +#define __XGENE_ENET_V2_ENET_H__ + +#define ENET_CLKEN 0xc008 +#define ENET_SRST 0xc000 +#define ENET_SHIM 0xc010 +#define CFG_MEM_RAM_SHUTDOWN 0xd070 +#define BLOCK_MEM_RDY 0xd074 + +#define DEVM_ARAUX_COH BIT(19) +#define DEVM_AWAUX_COH BIT(3) + +#define CFG_FORCE_LINK_STATUS_EN 0x229c +#define FORCE_LINK_STATUS 0x22a0 +#define CFG_LINK_AGGR_RESUME 0x27c8 +#define RX_DV_GATE_REG 0x2dfc + +void xge_wr_csr(struct xge_pdata *pdata, u32 offset, u32 val); +u32 xge_rd_csr(struct xge_pdata *pdata, u32 offset); +int xge_port_reset(struct net_device *ndev); + +#endif /* __XGENE_ENET_V2_ENET__H__ */ -- cgit From 3b3f9a75d93186613bb5ee19484454928af469c3 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Tue, 7 Mar 2017 17:08:43 -0800 Subject: drivers: net: xgene-v2: Add base driver This patch adds, - probe, remove, shutdown - open, close and stats - create and delete ring - request and delete irq Signed-off-by: Iyappan Subramanian Signed-off-by: Keyur Chudgar Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene-v2/main.c | 510 +++++++++++++++++++++++++++++++ 1 file changed, 510 insertions(+) create mode 100644 drivers/net/ethernet/apm/xgene-v2/main.c diff --git a/drivers/net/ethernet/apm/xgene-v2/main.c b/drivers/net/ethernet/apm/xgene-v2/main.c new file mode 100644 index 000000000000..c96b4ccdcfbf --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/main.c @@ -0,0 +1,510 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian + * Keyur Chudgar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +static const struct acpi_device_id xge_acpi_match[]; + +static int xge_get_resources(struct xge_pdata *pdata) +{ + struct platform_device *pdev; + struct net_device *ndev; + struct device *dev; + struct resource *res; + int phy_mode, ret = 0; + + pdev = pdata->pdev; + dev = &pdev->dev; + ndev = pdata->ndev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Resource enet_csr not defined\n"); + return -ENODEV; + } + + pdata->resources.base_addr = devm_ioremap(dev, res->start, + resource_size(res)); + if (!pdata->resources.base_addr) { + dev_err(dev, "Unable to retrieve ENET Port CSR region\n"); + return -ENOMEM; + } + + if (!device_get_mac_address(dev, ndev->dev_addr, ETH_ALEN)) + eth_hw_addr_random(ndev); + + memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len); + + phy_mode = device_get_phy_mode(dev); + if (phy_mode < 0) { + dev_err(dev, "Unable to get phy-connection-type\n"); + return phy_mode; + } + pdata->resources.phy_mode = phy_mode; + + if (pdata->resources.phy_mode != PHY_INTERFACE_MODE_RGMII) { + dev_err(dev, "Incorrect phy-connection-type specified\n"); + return -ENODEV; + } + + ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(dev, "Unable to get ENET IRQ\n"); + ret = ret ? : -ENXIO; + return ret; + } + pdata->resources.irq = ret; + + return 0; +} + +static int xge_refill_buffers(struct net_device *ndev, u32 nbuf) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct xge_desc_ring *ring = pdata->rx_ring; + const u8 slots = XGENE_ENET_NUM_DESC - 1; + struct device *dev = &pdata->pdev->dev; + struct xge_raw_desc *raw_desc; + u64 addr_lo, addr_hi; + u8 tail = ring->tail; + struct sk_buff *skb; + dma_addr_t dma_addr; + u16 len; + int i; + + for (i = 0; i < nbuf; i++) { + raw_desc = &ring->raw_desc[tail]; + + len = XGENE_ENET_STD_MTU; + skb = netdev_alloc_skb(ndev, len); + if (unlikely(!skb)) + return -ENOMEM; + + dma_addr = dma_map_single(dev, skb->data, len, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, dma_addr)) { + netdev_err(ndev, "DMA mapping error\n"); + dev_kfree_skb_any(skb); + return -EINVAL; + } + + ring->pkt_info[tail].skb = skb; + ring->pkt_info[tail].dma_addr = dma_addr; + + addr_hi = GET_BITS(NEXT_DESC_ADDRH, le64_to_cpu(raw_desc->m1)); + addr_lo = GET_BITS(NEXT_DESC_ADDRL, le64_to_cpu(raw_desc->m1)); + raw_desc->m1 = cpu_to_le64(SET_BITS(NEXT_DESC_ADDRL, addr_lo) | + SET_BITS(NEXT_DESC_ADDRH, addr_hi) | + SET_BITS(PKT_ADDRH, + dma_addr >> PKT_ADDRL_LEN)); + + dma_wmb(); + raw_desc->m0 = cpu_to_le64(SET_BITS(PKT_ADDRL, dma_addr) | + SET_BITS(E, 1)); + tail = (tail + 1) & slots; + } + + ring->tail = tail; + + return 0; +} + +static int xge_init_hw(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + int ret; + + ret = xge_port_reset(ndev); + if (ret) + return ret; + + xge_port_init(ndev); + pdata->nbufs = NUM_BUFS; + + return 0; +} + +static irqreturn_t xge_irq(const int irq, void *data) +{ + struct xge_pdata *pdata = data; + + if (napi_schedule_prep(&pdata->napi)) { + xge_intr_disable(pdata); + __napi_schedule(&pdata->napi); + } + + return IRQ_HANDLED; +} + +static int xge_request_irq(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + int ret; + + snprintf(pdata->irq_name, IRQ_ID_SIZE, "%s", ndev->name); + + ret = devm_request_irq(dev, pdata->resources.irq, xge_irq, + 0, pdata->irq_name, pdata); + if (ret) + netdev_err(ndev, "Failed to request irq %s\n", pdata->irq_name); + + return ret; +} + +static void xge_free_irq(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + + devm_free_irq(dev, pdata->resources.irq, pdata); +} + +static void xge_delete_desc_ring(struct net_device *ndev, + struct xge_desc_ring *ring) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + u16 size; + + if (!ring) + return; + + size = XGENE_ENET_DESC_SIZE * XGENE_ENET_NUM_DESC; + if (ring->desc_addr) + dma_free_coherent(dev, size, ring->desc_addr, ring->dma_addr); + + kfree(ring->pkt_info); + kfree(ring); +} + +static void xge_free_buffers(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct xge_desc_ring *ring = pdata->rx_ring; + struct device *dev = &pdata->pdev->dev; + struct sk_buff *skb; + dma_addr_t dma_addr; + int i; + + for (i = 0; i < XGENE_ENET_NUM_DESC; i++) { + skb = ring->pkt_info[i].skb; + dma_addr = ring->pkt_info[i].dma_addr; + + if (!skb) + continue; + + dma_unmap_single(dev, dma_addr, XGENE_ENET_STD_MTU, + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + } +} + +static void xge_delete_desc_rings(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + + xge_delete_desc_ring(ndev, pdata->tx_ring); + + xge_free_buffers(ndev); + xge_delete_desc_ring(ndev, pdata->rx_ring); +} + +static struct xge_desc_ring *xge_create_desc_ring(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + struct xge_desc_ring *ring; + u16 size; + + ring = kzalloc(sizeof(struct xge_desc_ring), GFP_KERNEL); + if (!ring) + return NULL; + + ring->ndev = ndev; + + size = XGENE_ENET_DESC_SIZE * XGENE_ENET_NUM_DESC; + ring->desc_addr = dma_zalloc_coherent(dev, size, &ring->dma_addr, + GFP_KERNEL); + if (!ring->desc_addr) + goto err; + + ring->pkt_info = kcalloc(XGENE_ENET_NUM_DESC, sizeof(struct pkt_info), + GFP_KERNEL); + if (!ring->pkt_info) + goto err; + + xge_setup_desc(ring); + + return ring; + +err: + xge_delete_desc_ring(ndev, ring); + + return NULL; +} + +static int xge_create_desc_rings(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct xge_desc_ring *ring; + int ret; + + /* create tx ring */ + ring = xge_create_desc_ring(ndev); + if (!ring) + goto err; + + pdata->tx_ring = ring; + xge_update_tx_desc_addr(pdata); + + /* create rx ring */ + ring = xge_create_desc_ring(ndev); + if (!ring) + goto err; + + pdata->rx_ring = ring; + xge_update_rx_desc_addr(pdata); + + ret = xge_refill_buffers(ndev, XGENE_ENET_NUM_DESC); + if (ret) + goto err; + + return 0; +err: + xge_delete_desc_rings(ndev); + + return -ENOMEM; +} + +static int xge_open(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + int ret; + + ret = xge_create_desc_rings(ndev); + if (ret) + return ret; + + napi_enable(&pdata->napi); + ret = xge_request_irq(ndev); + if (ret) + return ret; + + xge_intr_enable(pdata); + xge_wr_csr(pdata, DMARXCTRL, 1); + xge_mac_enable(pdata); + netif_start_queue(ndev); + netif_carrier_on(ndev); + + return 0; +} + +static int xge_close(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + + netif_carrier_off(ndev); + netif_stop_queue(ndev); + xge_mac_disable(pdata); + + xge_intr_disable(pdata); + xge_free_irq(ndev); + napi_disable(&pdata->napi); + xge_delete_desc_rings(ndev); + + return 0; +} + +static int xge_set_mac_addr(struct net_device *ndev, void *addr) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + int ret; + + ret = eth_mac_addr(ndev, addr); + if (ret) + return ret; + + xge_mac_set_station_addr(pdata); + + return 0; +} + +static void xge_timeout(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + + rtnl_lock(); + + if (netif_running(ndev)) { + netif_carrier_off(ndev); + netif_stop_queue(ndev); + xge_intr_disable(pdata); + napi_disable(&pdata->napi); + + xge_wr_csr(pdata, DMATXCTRL, 0); + xge_txc_poll(ndev); + xge_free_pending_skb(ndev); + xge_wr_csr(pdata, DMATXSTATUS, ~0U); + + xge_setup_desc(pdata->tx_ring); + xge_update_tx_desc_addr(pdata); + xge_mac_init(pdata); + + napi_enable(&pdata->napi); + xge_intr_enable(pdata); + xge_mac_enable(pdata); + netif_start_queue(ndev); + netif_carrier_on(ndev); + } + + rtnl_unlock(); +} + +static void xge_get_stats64(struct net_device *ndev, + struct rtnl_link_stats64 *storage) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct xge_stats *stats = &pdata->stats; + + storage->tx_packets += stats->tx_packets; + storage->tx_bytes += stats->tx_bytes; + + storage->rx_packets += stats->rx_packets; + storage->rx_bytes += stats->rx_bytes; +} + +static const struct net_device_ops xgene_ndev_ops = { + .ndo_open = xge_open, + .ndo_stop = xge_close, + .ndo_set_mac_address = xge_set_mac_addr, + .ndo_tx_timeout = xge_timeout, + .ndo_get_stats64 = xge_get_stats64, +}; + +static int xge_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct net_device *ndev; + struct xge_pdata *pdata; + int ret; + + ndev = alloc_etherdev(sizeof(struct xge_pdata)); + if (!ndev) + return -ENOMEM; + + pdata = netdev_priv(ndev); + + pdata->pdev = pdev; + pdata->ndev = ndev; + SET_NETDEV_DEV(ndev, dev); + platform_set_drvdata(pdev, pdata); + ndev->netdev_ops = &xgene_ndev_ops; + + ndev->features |= NETIF_F_GSO | + NETIF_F_GRO; + + ret = xge_get_resources(pdata); + if (ret) + goto err; + + ndev->hw_features = ndev->features; + + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) { + netdev_err(ndev, "No usable DMA configuration\n"); + goto err; + } + + ret = xge_init_hw(ndev); + if (ret) + goto err; + + netif_napi_add(ndev, &pdata->napi, xge_napi, NAPI_POLL_WEIGHT); + + netif_carrier_off(ndev); + ret = register_netdev(ndev); + if (ret) { + netdev_err(ndev, "Failed to register netdev\n"); + goto err; + } + + return 0; + +err: + free_netdev(ndev); + + return ret; +} + +static int xge_remove(struct platform_device *pdev) +{ + struct xge_pdata *pdata; + struct net_device *ndev; + + pdata = platform_get_drvdata(pdev); + ndev = pdata->ndev; + + rtnl_lock(); + if (netif_running(ndev)) + dev_close(ndev); + rtnl_unlock(); + + unregister_netdev(ndev); + free_netdev(ndev); + + return 0; +} + +static void xge_shutdown(struct platform_device *pdev) +{ + struct xge_pdata *pdata; + + pdata = platform_get_drvdata(pdev); + if (!pdata) + return; + + if (!pdata->ndev) + return; + + xge_remove(pdev); +} + +static const struct acpi_device_id xge_acpi_match[] = { + { "APMC0D80" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, xge_acpi_match); + +static struct platform_driver xge_driver = { + .driver = { + .name = "xgene-enet-v2", + .acpi_match_table = ACPI_PTR(xge_acpi_match), + }, + .probe = xge_probe, + .remove = xge_remove, + .shutdown = xge_shutdown, +}; +module_platform_driver(xge_driver); + +MODULE_DESCRIPTION("APM X-Gene SoC Ethernet v2 driver"); +MODULE_AUTHOR("Iyappan Subramanian "); +MODULE_VERSION(XGENE_ENET_V2_VERSION); +MODULE_LICENSE("GPL"); -- cgit From b105bcdaaa0efac4512d108495f1cc19abe7b7a4 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Tue, 7 Mar 2017 17:08:44 -0800 Subject: drivers: net: xgene-v2: Add transmit and receive This patch adds, - Transmit - Transmit completion poll - Receive poll - NAPI handler and enables the driver. Signed-off-by: Iyappan Subramanian Signed-off-by: Keyur Chudgar Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/Kconfig | 1 + drivers/net/ethernet/apm/Makefile | 1 + drivers/net/ethernet/apm/xgene-v2/Kconfig | 11 ++ drivers/net/ethernet/apm/xgene-v2/Makefile | 6 + drivers/net/ethernet/apm/xgene-v2/main.c | 248 ++++++++++++++++++++++++++++- drivers/net/ethernet/apm/xgene-v2/main.h | 1 + 6 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/apm/xgene-v2/Kconfig create mode 100644 drivers/net/ethernet/apm/xgene-v2/Makefile diff --git a/drivers/net/ethernet/apm/Kconfig b/drivers/net/ethernet/apm/Kconfig index ec63d706d464..59efe5b145dd 100644 --- a/drivers/net/ethernet/apm/Kconfig +++ b/drivers/net/ethernet/apm/Kconfig @@ -1 +1,2 @@ source "drivers/net/ethernet/apm/xgene/Kconfig" +source "drivers/net/ethernet/apm/xgene-v2/Kconfig" diff --git a/drivers/net/ethernet/apm/Makefile b/drivers/net/ethernet/apm/Makefile index 65ce32ad1b2c..946b2a4c882d 100644 --- a/drivers/net/ethernet/apm/Makefile +++ b/drivers/net/ethernet/apm/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_NET_XGENE) += xgene/ +obj-$(CONFIG_NET_XGENE_V2) += xgene-v2/ diff --git a/drivers/net/ethernet/apm/xgene-v2/Kconfig b/drivers/net/ethernet/apm/xgene-v2/Kconfig new file mode 100644 index 000000000000..1205861b6318 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/Kconfig @@ -0,0 +1,11 @@ +config NET_XGENE_V2 + tristate "APM X-Gene SoC Ethernet-v2 Driver" + depends on HAS_DMA + depends on ARCH_XGENE || COMPILE_TEST + help + This is the Ethernet driver for the on-chip ethernet interface + which uses a linked list of DMA descriptor architecture (v2) for + APM X-Gene SoCs. + + To compile this driver as a module, choose M here. This module will + be called xgene-enet-v2. diff --git a/drivers/net/ethernet/apm/xgene-v2/Makefile b/drivers/net/ethernet/apm/xgene-v2/Makefile new file mode 100644 index 000000000000..735309c0b8b1 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for APM X-Gene Ethernet v2 driver +# + +xgene-enet-v2-objs := main.o mac.o enet.o ring.o +obj-$(CONFIG_NET_XGENE_V2) += xgene-enet-v2.o diff --git a/drivers/net/ethernet/apm/xgene-v2/main.c b/drivers/net/ethernet/apm/xgene-v2/main.c index c96b4ccdcfbf..b16ef43bc83c 100644 --- a/drivers/net/ethernet/apm/xgene-v2/main.c +++ b/drivers/net/ethernet/apm/xgene-v2/main.c @@ -113,7 +113,7 @@ static int xge_refill_buffers(struct net_device *ndev, u32 nbuf) raw_desc->m1 = cpu_to_le64(SET_BITS(NEXT_DESC_ADDRL, addr_lo) | SET_BITS(NEXT_DESC_ADDRH, addr_hi) | SET_BITS(PKT_ADDRH, - dma_addr >> PKT_ADDRL_LEN)); + upper_32_bits(dma_addr))); dma_wmb(); raw_desc->m0 = cpu_to_le64(SET_BITS(PKT_ADDRL, dma_addr) | @@ -177,6 +177,194 @@ static void xge_free_irq(struct net_device *ndev) devm_free_irq(dev, pdata->resources.irq, pdata); } +static bool is_tx_slot_available(struct xge_raw_desc *raw_desc) +{ + if (GET_BITS(E, le64_to_cpu(raw_desc->m0)) && + (GET_BITS(PKT_SIZE, le64_to_cpu(raw_desc->m0)) == SLOT_EMPTY)) + return true; + + return false; +} + +static netdev_tx_t xge_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + static dma_addr_t dma_addr; + struct xge_desc_ring *tx_ring; + struct xge_raw_desc *raw_desc; + u64 addr_lo, addr_hi; + void *pkt_buf; + u8 tail; + u16 len; + + tx_ring = pdata->tx_ring; + tail = tx_ring->tail; + len = skb_headlen(skb); + raw_desc = &tx_ring->raw_desc[tail]; + + if (!is_tx_slot_available(raw_desc)) { + netif_stop_queue(ndev); + return NETDEV_TX_BUSY; + } + + /* Packet buffers should be 64B aligned */ + pkt_buf = dma_zalloc_coherent(dev, XGENE_ENET_STD_MTU, &dma_addr, + GFP_ATOMIC); + if (unlikely(!pkt_buf)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + memcpy(pkt_buf, skb->data, len); + + addr_hi = GET_BITS(NEXT_DESC_ADDRH, le64_to_cpu(raw_desc->m1)); + addr_lo = GET_BITS(NEXT_DESC_ADDRL, le64_to_cpu(raw_desc->m1)); + raw_desc->m1 = cpu_to_le64(SET_BITS(NEXT_DESC_ADDRL, addr_lo) | + SET_BITS(NEXT_DESC_ADDRH, addr_hi) | + SET_BITS(PKT_ADDRH, + upper_32_bits(dma_addr))); + + tx_ring->pkt_info[tail].skb = skb; + tx_ring->pkt_info[tail].dma_addr = dma_addr; + tx_ring->pkt_info[tail].pkt_buf = pkt_buf; + + dma_wmb(); + + raw_desc->m0 = cpu_to_le64(SET_BITS(PKT_ADDRL, dma_addr) | + SET_BITS(PKT_SIZE, len) | + SET_BITS(E, 0)); + skb_tx_timestamp(skb); + xge_wr_csr(pdata, DMATXCTRL, 1); + + tx_ring->tail = (tail + 1) & (XGENE_ENET_NUM_DESC - 1); + + return NETDEV_TX_OK; +} + +static bool is_tx_hw_done(struct xge_raw_desc *raw_desc) +{ + if (GET_BITS(E, le64_to_cpu(raw_desc->m0)) && + !GET_BITS(PKT_SIZE, le64_to_cpu(raw_desc->m0))) + return true; + + return false; +} + +static void xge_txc_poll(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + struct xge_desc_ring *tx_ring; + struct xge_raw_desc *raw_desc; + dma_addr_t dma_addr; + struct sk_buff *skb; + void *pkt_buf; + u32 data; + u8 head; + + tx_ring = pdata->tx_ring; + head = tx_ring->head; + + data = xge_rd_csr(pdata, DMATXSTATUS); + if (!GET_BITS(TXPKTCOUNT, data)) + return; + + while (1) { + raw_desc = &tx_ring->raw_desc[head]; + + if (!is_tx_hw_done(raw_desc)) + break; + + dma_rmb(); + + skb = tx_ring->pkt_info[head].skb; + dma_addr = tx_ring->pkt_info[head].dma_addr; + pkt_buf = tx_ring->pkt_info[head].pkt_buf; + pdata->stats.tx_packets++; + pdata->stats.tx_bytes += skb->len; + dma_free_coherent(dev, XGENE_ENET_STD_MTU, pkt_buf, dma_addr); + dev_kfree_skb_any(skb); + + /* clear pktstart address and pktsize */ + raw_desc->m0 = cpu_to_le64(SET_BITS(E, 1) | + SET_BITS(PKT_SIZE, SLOT_EMPTY)); + xge_wr_csr(pdata, DMATXSTATUS, 1); + + head = (head + 1) & (XGENE_ENET_NUM_DESC - 1); + } + + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + + tx_ring->head = head; +} + +static int xge_rx_poll(struct net_device *ndev, unsigned int budget) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + struct xge_desc_ring *rx_ring; + struct xge_raw_desc *raw_desc; + struct sk_buff *skb; + dma_addr_t dma_addr; + int processed = 0; + u8 head, rx_error; + int i, ret; + u32 data; + u16 len; + + rx_ring = pdata->rx_ring; + head = rx_ring->head; + + data = xge_rd_csr(pdata, DMARXSTATUS); + if (!GET_BITS(RXPKTCOUNT, data)) + return 0; + + for (i = 0; i < budget; i++) { + raw_desc = &rx_ring->raw_desc[head]; + + if (GET_BITS(E, le64_to_cpu(raw_desc->m0))) + break; + + dma_rmb(); + + skb = rx_ring->pkt_info[head].skb; + rx_ring->pkt_info[head].skb = NULL; + dma_addr = rx_ring->pkt_info[head].dma_addr; + len = GET_BITS(PKT_SIZE, le64_to_cpu(raw_desc->m0)); + dma_unmap_single(dev, dma_addr, XGENE_ENET_STD_MTU, + DMA_FROM_DEVICE); + + rx_error = GET_BITS(D, le64_to_cpu(raw_desc->m2)); + if (unlikely(rx_error)) { + pdata->stats.rx_errors++; + dev_kfree_skb_any(skb); + goto out; + } + + skb_put(skb, len); + skb->protocol = eth_type_trans(skb, ndev); + + pdata->stats.rx_packets++; + pdata->stats.rx_bytes += len; + napi_gro_receive(&pdata->napi, skb); +out: + ret = xge_refill_buffers(ndev, 1); + xge_wr_csr(pdata, DMARXSTATUS, 1); + xge_wr_csr(pdata, DMARXCTRL, 1); + + if (ret) + break; + + head = (head + 1) & (XGENE_ENET_NUM_DESC - 1); + processed++; + } + + rx_ring->head = head; + + return processed; +} + static void xge_delete_desc_ring(struct net_device *ndev, struct xge_desc_ring *ring) { @@ -221,8 +409,10 @@ static void xge_delete_desc_rings(struct net_device *ndev) { struct xge_pdata *pdata = netdev_priv(ndev); + xge_txc_poll(ndev); xge_delete_desc_ring(ndev, pdata->tx_ring); + xge_rx_poll(ndev, 64); xge_free_buffers(ndev); xge_delete_desc_ring(ndev, pdata->rx_ring); } @@ -333,6 +523,25 @@ static int xge_close(struct net_device *ndev) return 0; } +static int xge_napi(struct napi_struct *napi, const int budget) +{ + struct net_device *ndev = napi->dev; + struct xge_pdata *pdata = netdev_priv(ndev); + int processed; + + pdata = netdev_priv(ndev); + + xge_txc_poll(ndev); + processed = xge_rx_poll(ndev, budget); + + if (processed < budget) { + napi_complete_done(napi, processed); + xge_intr_enable(pdata); + } + + return processed; +} + static int xge_set_mac_addr(struct net_device *ndev, void *addr) { struct xge_pdata *pdata = netdev_priv(ndev); @@ -347,6 +556,41 @@ static int xge_set_mac_addr(struct net_device *ndev, void *addr) return 0; } +static bool is_tx_pending(struct xge_raw_desc *raw_desc) +{ + if (!GET_BITS(E, le64_to_cpu(raw_desc->m0))) + return true; + + return false; +} + +static void xge_free_pending_skb(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + struct xge_desc_ring *tx_ring; + struct xge_raw_desc *raw_desc; + dma_addr_t dma_addr; + struct sk_buff *skb; + void *pkt_buf; + int i; + + tx_ring = pdata->tx_ring; + + for (i = 0; i < XGENE_ENET_NUM_DESC; i++) { + raw_desc = &tx_ring->raw_desc[i]; + + if (!is_tx_pending(raw_desc)) + continue; + + skb = tx_ring->pkt_info[i].skb; + dma_addr = tx_ring->pkt_info[i].dma_addr; + pkt_buf = tx_ring->pkt_info[i].pkt_buf; + dma_free_coherent(dev, XGENE_ENET_STD_MTU, pkt_buf, dma_addr); + dev_kfree_skb_any(skb); + } +} + static void xge_timeout(struct net_device *ndev) { struct xge_pdata *pdata = netdev_priv(ndev); @@ -389,11 +633,13 @@ static void xge_get_stats64(struct net_device *ndev, storage->rx_packets += stats->rx_packets; storage->rx_bytes += stats->rx_bytes; + storage->rx_errors += stats->rx_errors; } static const struct net_device_ops xgene_ndev_ops = { .ndo_open = xge_open, .ndo_stop = xge_close, + .ndo_start_xmit = xge_start_xmit, .ndo_set_mac_address = xge_set_mac_addr, .ndo_tx_timeout = xge_timeout, .ndo_get_stats64 = xge_get_stats64, diff --git a/drivers/net/ethernet/apm/xgene-v2/main.h b/drivers/net/ethernet/apm/xgene-v2/main.h index a2f87126dce0..ada7b0e82586 100644 --- a/drivers/net/ethernet/apm/xgene-v2/main.h +++ b/drivers/net/ethernet/apm/xgene-v2/main.h @@ -55,6 +55,7 @@ struct xge_stats { u64 tx_bytes; u64 rx_packets; u64 rx_bytes; + u64 rx_errors; }; /* ethernet private data */ -- cgit From 70dbd9b258d5a6a21d9a41ae798f5476dcdb086c Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Tue, 7 Mar 2017 17:08:45 -0800 Subject: MAINTAINERS: Add entry for APM X-Gene SoC Ethernet (v2) driver This patch adds a MAINTAINERS entry for the ethernet driver for the on-chip ethernet interface which uses a linked list of DMA descriptor architecture (v2) for APM X-Gene SoCs. Signed-off-by: Iyappan Subramanian Signed-off-by: Keyur Chudgar Signed-off-by: David S. Miller --- MAINTAINERS | 6 ++++++ drivers/net/ethernet/apm/xgene-v2/mac.c | 2 +- drivers/net/ethernet/apm/xgene-v2/main.c | 8 ++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index c265a5fe4848..e04d3a6725fc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -902,6 +902,12 @@ F: drivers/net/phy/mdio-xgene.c F: Documentation/devicetree/bindings/net/apm-xgene-enet.txt F: Documentation/devicetree/bindings/net/apm-xgene-mdio.txt +APPLIED MICRO (APM) X-GENE SOC ETHERNET (V2) DRIVER +M: Iyappan Subramanian +M: Keyur Chudgar +S: Supported +F: drivers/net/ethernet/apm/xgene-v2/ + APPLIED MICRO (APM) X-GENE SOC PMU M: Tai Nguyen S: Supported diff --git a/drivers/net/ethernet/apm/xgene-v2/mac.c b/drivers/net/ethernet/apm/xgene-v2/mac.c index 9c3d32d3b7dd..c3189de3df55 100644 --- a/drivers/net/ethernet/apm/xgene-v2/mac.c +++ b/drivers/net/ethernet/apm/xgene-v2/mac.c @@ -77,8 +77,8 @@ static void xge_mac_set_speed(struct xge_pdata *pdata) void xge_mac_set_station_addr(struct xge_pdata *pdata) { - u32 addr0, addr1; u8 *dev_addr = pdata->ndev->dev_addr; + u32 addr0, addr1; addr0 = (dev_addr[3] << 24) | (dev_addr[2] << 16) | (dev_addr[1] << 8) | dev_addr[0]; diff --git a/drivers/net/ethernet/apm/xgene-v2/main.c b/drivers/net/ethernet/apm/xgene-v2/main.c index b16ef43bc83c..ae76977d10b4 100644 --- a/drivers/net/ethernet/apm/xgene-v2/main.c +++ b/drivers/net/ethernet/apm/xgene-v2/main.c @@ -27,9 +27,9 @@ static int xge_get_resources(struct xge_pdata *pdata) { struct platform_device *pdev; struct net_device *ndev; - struct device *dev; - struct resource *res; int phy_mode, ret = 0; + struct resource *res; + struct device *dev; pdev = pdata->pdev; dev = &pdev->dev; @@ -190,9 +190,9 @@ static netdev_tx_t xge_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct xge_pdata *pdata = netdev_priv(ndev); struct device *dev = &pdata->pdev->dev; - static dma_addr_t dma_addr; struct xge_desc_ring *tx_ring; struct xge_raw_desc *raw_desc; + static dma_addr_t dma_addr; u64 addr_lo, addr_hi; void *pkt_buf; u8 tail; @@ -526,7 +526,7 @@ static int xge_close(struct net_device *ndev) static int xge_napi(struct napi_struct *napi, const int budget) { struct net_device *ndev = napi->dev; - struct xge_pdata *pdata = netdev_priv(ndev); + struct xge_pdata *pdata; int processed; pdata = netdev_priv(ndev); -- cgit From 65e0ace2c5cdd7aa898fea17d6e7bdc909394bf9 Mon Sep 17 00:00:00 2001 From: Jie Deng Date: Wed, 8 Mar 2017 14:06:18 +0800 Subject: net: dwc-xlgmac: Initial driver for DesignWare Enterprise Ethernet Synopsys provides a new DesignWare Core Enterprise Ethernet MAC IP (DWC-XLGMAC) for Ethernet designs. It is compliant with the IEEE 802.3-2012 specifications, including IEEE 802.3ba and consortium specifications. This patch provides the initial 25G/40G/50G/100G Ethernet driver for Synopsys XLGMAC IP Prototyping Kit. Signed-off-by: Jie Deng Signed-off-by: David S. Miller --- MAINTAINERS | 6 + drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/synopsys/Kconfig | 41 + drivers/net/ethernet/synopsys/Makefile | 9 + drivers/net/ethernet/synopsys/dwc-xlgmac-common.c | 736 +++++ drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c | 648 +++++ drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c | 3146 +++++++++++++++++++++ drivers/net/ethernet/synopsys/dwc-xlgmac-net.c | 1334 +++++++++ drivers/net/ethernet/synopsys/dwc-xlgmac-pci.c | 80 + drivers/net/ethernet/synopsys/dwc-xlgmac-reg.h | 746 +++++ drivers/net/ethernet/synopsys/dwc-xlgmac.h | 651 +++++ 12 files changed, 7399 insertions(+) create mode 100644 drivers/net/ethernet/synopsys/Kconfig create mode 100644 drivers/net/ethernet/synopsys/Makefile create mode 100644 drivers/net/ethernet/synopsys/dwc-xlgmac-common.c create mode 100644 drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c create mode 100644 drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c create mode 100644 drivers/net/ethernet/synopsys/dwc-xlgmac-net.c create mode 100644 drivers/net/ethernet/synopsys/dwc-xlgmac-pci.c create mode 100644 drivers/net/ethernet/synopsys/dwc-xlgmac-reg.h create mode 100644 drivers/net/ethernet/synopsys/dwc-xlgmac.h diff --git a/MAINTAINERS b/MAINTAINERS index e04d3a6725fc..a375d855f539 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11068,6 +11068,12 @@ F: include/linux/dma/dw.h F: include/linux/platform_data/dma-dw.h F: drivers/dma/dw/ +SYNOPSYS DESIGNWARE ENTERPRISE ETHERNET DRIVER +M: Jie Deng +L: netdev@vger.kernel.org +S: Supported +F: drivers/net/ethernet/synopsys/ + SYNOPSYS DESIGNWARE I2C DRIVER M: Jarkko Nikula R: Andy Shevchenko diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 8c08f9deef92..edae15ac0e98 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -180,5 +180,6 @@ source "drivers/net/ethernet/via/Kconfig" source "drivers/net/ethernet/wiznet/Kconfig" source "drivers/net/ethernet/xilinx/Kconfig" source "drivers/net/ethernet/xircom/Kconfig" +source "drivers/net/ethernet/synopsys/Kconfig" endif # ETHERNET diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 26dce5bf2c18..bf7f4502cabc 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -91,3 +91,4 @@ obj-$(CONFIG_NET_VENDOR_VIA) += via/ obj-$(CONFIG_NET_VENDOR_WIZNET) += wiznet/ obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/ obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/ +obj-$(CONFIG_NET_VENDOR_SYNOPSYS) += synopsys/ diff --git a/drivers/net/ethernet/synopsys/Kconfig b/drivers/net/ethernet/synopsys/Kconfig new file mode 100644 index 000000000000..a9503884e1c2 --- /dev/null +++ b/drivers/net/ethernet/synopsys/Kconfig @@ -0,0 +1,41 @@ +# +# Synopsys network device configuration +# + +config NET_VENDOR_SYNOPSYS + bool "Synopsys devices" + default y + ---help--- + If you have a network (Ethernet) device belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Synopsys devices. If you say Y, you will be asked + for your specific device in the following questions. + +if NET_VENDOR_SYNOPSYS + +config DWC_XLGMAC + tristate "Synopsys DWC Enterprise Ethernet (XLGMAC) driver support" + depends on HAS_IOMEM && HAS_DMA + select BITREVERSE + select CRC32 + ---help--- + This driver supports the Synopsys DesignWare Cores Enterprise + Ethernet (dwc-xlgmac). + +if DWC_XLGMAC + +config DWC_XLGMAC_PCI + tristate "XLGMAC PCI bus support" + depends on DWC_XLGMAC && PCI + ---help--- + This selects the pci bus support for the dwc-xlgmac driver. + This driver was tested on Synopsys XLGMAC IP Prototyping Kit. + + If you have a controller with this interface, say Y or M here. + If unsure, say N. + +endif # DWC_XLGMAC + +endif # NET_VENDOR_SYNOPSYS diff --git a/drivers/net/ethernet/synopsys/Makefile b/drivers/net/ethernet/synopsys/Makefile new file mode 100644 index 000000000000..c06e2eb3be90 --- /dev/null +++ b/drivers/net/ethernet/synopsys/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the Synopsys network device drivers. +# + +obj-$(CONFIG_DWC_XLGMAC) += dwc-xlgmac.o +dwc-xlgmac-objs := dwc-xlgmac-net.o dwc-xlgmac-desc.o \ + dwc-xlgmac-hw.o dwc-xlgmac-common.o + +dwc-xlgmac-$(CONFIG_DWC_XLGMAC_PCI) += dwc-xlgmac-pci.o diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c new file mode 100644 index 000000000000..726d78ac4907 --- /dev/null +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c @@ -0,0 +1,736 @@ +/* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver + * + * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This Synopsys DWC XLGMAC software driver and associated documentation + * (hereinafter the "Software") is an unsupported proprietary work of + * Synopsys, Inc. unless otherwise expressly agreed to in writing between + * Synopsys and you. The Software IS NOT an item of Licensed Software or a + * Licensed Product under any End User Software License Agreement or + * Agreement for Licensed Products with Synopsys or any supplement thereto. + * Synopsys is a registered trademark of Synopsys, Inc. Other names included + * in the SOFTWARE may be the trademarks of their respective owners. + */ + +#include +#include + +#include "dwc-xlgmac.h" +#include "dwc-xlgmac-reg.h" + +static int debug = -1; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "DWC ethernet debug level (0=none,...,16=all)"); +static const u32 default_msg_level = (NETIF_MSG_LINK | NETIF_MSG_IFDOWN | + NETIF_MSG_IFUP); + +static unsigned char dev_addr[6] = {0, 0x55, 0x7b, 0xb5, 0x7d, 0xf7}; + +static void xlgmac_read_mac_addr(struct xlgmac_pdata *pdata) +{ + struct net_device *netdev = pdata->netdev; + + /* Currently it uses a static mac address for test */ + memcpy(pdata->mac_addr, dev_addr, netdev->addr_len); +} + +static void xlgmac_default_config(struct xlgmac_pdata *pdata) +{ + pdata->tx_osp_mode = DMA_OSP_ENABLE; + pdata->tx_sf_mode = MTL_TSF_ENABLE; + pdata->rx_sf_mode = MTL_RSF_DISABLE; + pdata->pblx8 = DMA_PBL_X8_ENABLE; + pdata->tx_pbl = DMA_PBL_32; + pdata->rx_pbl = DMA_PBL_32; + pdata->tx_threshold = MTL_TX_THRESHOLD_128; + pdata->rx_threshold = MTL_RX_THRESHOLD_128; + pdata->tx_pause = 1; + pdata->rx_pause = 1; + pdata->phy_speed = SPEED_25000; + pdata->sysclk_rate = XLGMAC_SYSCLOCK; + + strlcpy(pdata->drv_name, XLGMAC_DRV_NAME, sizeof(pdata->drv_name)); + strlcpy(pdata->drv_ver, XLGMAC_DRV_VERSION, sizeof(pdata->drv_ver)); +} + +static void xlgmac_init_all_ops(struct xlgmac_pdata *pdata) +{ + xlgmac_init_desc_ops(&pdata->desc_ops); + xlgmac_init_hw_ops(&pdata->hw_ops); +} + +static int xlgmac_init(struct xlgmac_pdata *pdata) +{ + struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; + struct net_device *netdev = pdata->netdev; + unsigned int i; + int ret; + + /* Set default configuration data */ + xlgmac_default_config(pdata); + + /* Set irq, base_addr, MAC address, */ + netdev->irq = pdata->dev_irq; + netdev->base_addr = (unsigned long)pdata->mac_regs; + xlgmac_read_mac_addr(pdata); + memcpy(netdev->dev_addr, pdata->mac_addr, netdev->addr_len); + + /* Set all the function pointers */ + xlgmac_init_all_ops(pdata); + + /* Issue software reset to device */ + hw_ops->exit(pdata); + + /* Populate the hardware features */ + xlgmac_get_all_hw_features(pdata); + xlgmac_print_all_hw_features(pdata); + + /* TODO: Set the PHY mode to XLGMII */ + + /* Set the DMA mask */ + ret = dma_set_mask_and_coherent(pdata->dev, + DMA_BIT_MASK(pdata->hw_feat.dma_width)); + if (ret) { + dev_err(pdata->dev, "dma_set_mask_and_coherent failed\n"); + return ret; + } + + /* Channel and ring params initializtion + * pdata->channel_count; + * pdata->tx_ring_count; + * pdata->rx_ring_count; + * pdata->tx_desc_count; + * pdata->rx_desc_count; + */ + BUILD_BUG_ON_NOT_POWER_OF_2(XLGMAC_TX_DESC_CNT); + pdata->tx_desc_count = XLGMAC_TX_DESC_CNT; + if (pdata->tx_desc_count & (pdata->tx_desc_count - 1)) { + dev_err(pdata->dev, "tx descriptor count (%d) is not valid\n", + pdata->tx_desc_count); + ret = -EINVAL; + return ret; + } + BUILD_BUG_ON_NOT_POWER_OF_2(XLGMAC_RX_DESC_CNT); + pdata->rx_desc_count = XLGMAC_RX_DESC_CNT; + if (pdata->rx_desc_count & (pdata->rx_desc_count - 1)) { + dev_err(pdata->dev, "rx descriptor count (%d) is not valid\n", + pdata->rx_desc_count); + ret = -EINVAL; + return ret; + } + + pdata->tx_ring_count = min_t(unsigned int, num_online_cpus(), + pdata->hw_feat.tx_ch_cnt); + pdata->tx_ring_count = min_t(unsigned int, pdata->tx_ring_count, + pdata->hw_feat.tx_q_cnt); + pdata->tx_q_count = pdata->tx_ring_count; + ret = netif_set_real_num_tx_queues(netdev, pdata->tx_q_count); + if (ret) { + dev_err(pdata->dev, "error setting real tx queue count\n"); + return ret; + } + + pdata->rx_ring_count = min_t(unsigned int, + netif_get_num_default_rss_queues(), + pdata->hw_feat.rx_ch_cnt); + pdata->rx_ring_count = min_t(unsigned int, pdata->rx_ring_count, + pdata->hw_feat.rx_q_cnt); + pdata->rx_q_count = pdata->rx_ring_count; + ret = netif_set_real_num_rx_queues(netdev, pdata->rx_q_count); + if (ret) { + dev_err(pdata->dev, "error setting real rx queue count\n"); + return ret; + } + + pdata->channel_count = + max_t(unsigned int, pdata->tx_ring_count, pdata->rx_ring_count); + + /* Initialize RSS hash key and lookup table */ + netdev_rss_key_fill(pdata->rss_key, sizeof(pdata->rss_key)); + + for (i = 0; i < XLGMAC_RSS_MAX_TABLE_SIZE; i++) + pdata->rss_table[i] = XLGMAC_SET_REG_BITS( + pdata->rss_table[i], + MAC_RSSDR_DMCH_POS, + MAC_RSSDR_DMCH_LEN, + i % pdata->rx_ring_count); + + pdata->rss_options = XLGMAC_SET_REG_BITS( + pdata->rss_options, + MAC_RSSCR_IP2TE_POS, + MAC_RSSCR_IP2TE_LEN, 1); + pdata->rss_options = XLGMAC_SET_REG_BITS( + pdata->rss_options, + MAC_RSSCR_TCP4TE_POS, + MAC_RSSCR_TCP4TE_LEN, 1); + pdata->rss_options = XLGMAC_SET_REG_BITS( + pdata->rss_options, + MAC_RSSCR_UDP4TE_POS, + MAC_RSSCR_UDP4TE_LEN, 1); + + /* Set device operations */ + netdev->netdev_ops = xlgmac_get_netdev_ops(); + + /* Set device features */ + if (pdata->hw_feat.tso) { + netdev->hw_features = NETIF_F_TSO; + netdev->hw_features |= NETIF_F_TSO6; + netdev->hw_features |= NETIF_F_SG; + netdev->hw_features |= NETIF_F_IP_CSUM; + netdev->hw_features |= NETIF_F_IPV6_CSUM; + } else if (pdata->hw_feat.tx_coe) { + netdev->hw_features = NETIF_F_IP_CSUM; + netdev->hw_features |= NETIF_F_IPV6_CSUM; + } + + if (pdata->hw_feat.rx_coe) { + netdev->hw_features |= NETIF_F_RXCSUM; + netdev->hw_features |= NETIF_F_GRO; + } + + if (pdata->hw_feat.rss) + netdev->hw_features |= NETIF_F_RXHASH; + + netdev->vlan_features |= netdev->hw_features; + + netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; + if (pdata->hw_feat.sa_vlan_ins) + netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX; + if (pdata->hw_feat.vlhash) + netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; + + netdev->features |= netdev->hw_features; + pdata->netdev_features = netdev->features; + + netdev->priv_flags |= IFF_UNICAST_FLT; + + /* Use default watchdog timeout */ + netdev->watchdog_timeo = 0; + + /* Tx coalesce parameters initialization */ + pdata->tx_usecs = XLGMAC_INIT_DMA_TX_USECS; + pdata->tx_frames = XLGMAC_INIT_DMA_TX_FRAMES; + + /* Rx coalesce parameters initialization */ + pdata->rx_riwt = hw_ops->usec_to_riwt(pdata, XLGMAC_INIT_DMA_RX_USECS); + pdata->rx_usecs = XLGMAC_INIT_DMA_RX_USECS; + pdata->rx_frames = XLGMAC_INIT_DMA_RX_FRAMES; + + return 0; +} + +int xlgmac_drv_probe(struct device *dev, struct xlgmac_resources *res) +{ + struct xlgmac_pdata *pdata; + struct net_device *netdev; + int ret; + + netdev = alloc_etherdev_mq(sizeof(struct xlgmac_pdata), + XLGMAC_MAX_DMA_CHANNELS); + + if (!netdev) { + dev_err(dev, "alloc_etherdev failed\n"); + return -ENOMEM; + } + + SET_NETDEV_DEV(netdev, dev); + dev_set_drvdata(dev, netdev); + pdata = netdev_priv(netdev); + pdata->dev = dev; + pdata->netdev = netdev; + + pdata->dev_irq = res->irq; + pdata->mac_regs = res->addr; + + mutex_init(&pdata->rss_mutex); + pdata->msg_enable = netif_msg_init(debug, default_msg_level); + + ret = xlgmac_init(pdata); + if (ret) { + dev_err(dev, "xlgmac init failed\n"); + goto err_free_netdev; + } + + ret = register_netdev(netdev); + if (ret) { + dev_err(dev, "net device registration failed\n"); + goto err_free_netdev; + } + + return 0; + +err_free_netdev: + free_netdev(netdev); + + return ret; +} + +int xlgmac_drv_remove(struct device *dev) +{ + struct net_device *netdev = dev_get_drvdata(dev); + + unregister_netdev(netdev); + free_netdev(netdev); + + return 0; +} + +void xlgmac_dump_tx_desc(struct xlgmac_pdata *pdata, + struct xlgmac_ring *ring, + unsigned int idx, + unsigned int count, + unsigned int flag) +{ + struct xlgmac_desc_data *desc_data; + struct xlgmac_dma_desc *dma_desc; + + while (count--) { + desc_data = XLGMAC_GET_DESC_DATA(ring, idx); + dma_desc = desc_data->dma_desc; + + netdev_dbg(pdata->netdev, "TX: dma_desc=%p, dma_desc_addr=%pad\n", + desc_data->dma_desc, &desc_data->dma_desc_addr); + netdev_dbg(pdata->netdev, + "TX_NORMAL_DESC[%d %s] = %08x:%08x:%08x:%08x\n", idx, + (flag == 1) ? "QUEUED FOR TX" : "TX BY DEVICE", + le32_to_cpu(dma_desc->desc0), + le32_to_cpu(dma_desc->desc1), + le32_to_cpu(dma_desc->desc2), + le32_to_cpu(dma_desc->desc3)); + + idx++; + } +} + +void xlgmac_dump_rx_desc(struct xlgmac_pdata *pdata, + struct xlgmac_ring *ring, + unsigned int idx) +{ + struct xlgmac_desc_data *desc_data; + struct xlgmac_dma_desc *dma_desc; + + desc_data = XLGMAC_GET_DESC_DATA(ring, idx); + dma_desc = desc_data->dma_desc; + + netdev_dbg(pdata->netdev, "RX: dma_desc=%p, dma_desc_addr=%pad\n", + desc_data->dma_desc, &desc_data->dma_desc_addr); + netdev_dbg(pdata->netdev, + "RX_NORMAL_DESC[%d RX BY DEVICE] = %08x:%08x:%08x:%08x\n", + idx, + le32_to_cpu(dma_desc->desc0), + le32_to_cpu(dma_desc->desc1), + le32_to_cpu(dma_desc->desc2), + le32_to_cpu(dma_desc->desc3)); +} + +void xlgmac_print_pkt(struct net_device *netdev, + struct sk_buff *skb, bool tx_rx) +{ + struct ethhdr *eth = (struct ethhdr *)skb->data; + unsigned char *buf = skb->data; + unsigned char buffer[128]; + unsigned int i, j; + + netdev_dbg(netdev, "\n************** SKB dump ****************\n"); + + netdev_dbg(netdev, "%s packet of %d bytes\n", + (tx_rx ? "TX" : "RX"), skb->len); + + netdev_dbg(netdev, "Dst MAC addr: %pM\n", eth->h_dest); + netdev_dbg(netdev, "Src MAC addr: %pM\n", eth->h_source); + netdev_dbg(netdev, "Protocol: %#06hx\n", ntohs(eth->h_proto)); + + for (i = 0, j = 0; i < skb->len;) { + j += snprintf(buffer + j, sizeof(buffer) - j, "%02hhx", + buf[i++]); + + if ((i % 32) == 0) { + netdev_dbg(netdev, " %#06x: %s\n", i - 32, buffer); + j = 0; + } else if ((i % 16) == 0) { + buffer[j++] = ' '; + buffer[j++] = ' '; + } else if ((i % 4) == 0) { + buffer[j++] = ' '; + } + } + if (i % 32) + netdev_dbg(netdev, " %#06x: %s\n", i - (i % 32), buffer); + + netdev_dbg(netdev, "\n************** SKB dump ****************\n"); +} + +void xlgmac_get_all_hw_features(struct xlgmac_pdata *pdata) +{ + struct xlgmac_hw_features *hw_feat = &pdata->hw_feat; + unsigned int mac_hfr0, mac_hfr1, mac_hfr2; + + mac_hfr0 = readl(pdata->mac_regs + MAC_HWF0R); + mac_hfr1 = readl(pdata->mac_regs + MAC_HWF1R); + mac_hfr2 = readl(pdata->mac_regs + MAC_HWF2R); + + memset(hw_feat, 0, sizeof(*hw_feat)); + + hw_feat->version = readl(pdata->mac_regs + MAC_VR); + + /* Hardware feature register 0 */ + hw_feat->phyifsel = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_PHYIFSEL_POS, + MAC_HWF0R_PHYIFSEL_LEN); + hw_feat->vlhash = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_VLHASH_POS, + MAC_HWF0R_VLHASH_LEN); + hw_feat->sma = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_SMASEL_POS, + MAC_HWF0R_SMASEL_LEN); + hw_feat->rwk = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_RWKSEL_POS, + MAC_HWF0R_RWKSEL_LEN); + hw_feat->mgk = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_MGKSEL_POS, + MAC_HWF0R_MGKSEL_LEN); + hw_feat->mmc = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_MMCSEL_POS, + MAC_HWF0R_MMCSEL_LEN); + hw_feat->aoe = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_ARPOFFSEL_POS, + MAC_HWF0R_ARPOFFSEL_LEN); + hw_feat->ts = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_TSSEL_POS, + MAC_HWF0R_TSSEL_LEN); + hw_feat->eee = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_EEESEL_POS, + MAC_HWF0R_EEESEL_LEN); + hw_feat->tx_coe = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_TXCOESEL_POS, + MAC_HWF0R_TXCOESEL_LEN); + hw_feat->rx_coe = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_RXCOESEL_POS, + MAC_HWF0R_RXCOESEL_LEN); + hw_feat->addn_mac = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_ADDMACADRSEL_POS, + MAC_HWF0R_ADDMACADRSEL_LEN); + hw_feat->ts_src = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_TSSTSSEL_POS, + MAC_HWF0R_TSSTSSEL_LEN); + hw_feat->sa_vlan_ins = XLGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_SAVLANINS_POS, + MAC_HWF0R_SAVLANINS_LEN); + + /* Hardware feature register 1 */ + hw_feat->rx_fifo_size = XLGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_RXFIFOSIZE_POS, + MAC_HWF1R_RXFIFOSIZE_LEN); + hw_feat->tx_fifo_size = XLGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_TXFIFOSIZE_POS, + MAC_HWF1R_TXFIFOSIZE_LEN); + hw_feat->adv_ts_hi = XLGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_ADVTHWORD_POS, + MAC_HWF1R_ADVTHWORD_LEN); + hw_feat->dma_width = XLGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_ADDR64_POS, + MAC_HWF1R_ADDR64_LEN); + hw_feat->dcb = XLGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_DCBEN_POS, + MAC_HWF1R_DCBEN_LEN); + hw_feat->sph = XLGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_SPHEN_POS, + MAC_HWF1R_SPHEN_LEN); + hw_feat->tso = XLGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_TSOEN_POS, + MAC_HWF1R_TSOEN_LEN); + hw_feat->dma_debug = XLGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_DBGMEMA_POS, + MAC_HWF1R_DBGMEMA_LEN); + hw_feat->rss = XLGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_RSSEN_POS, + MAC_HWF1R_RSSEN_LEN); + hw_feat->tc_cnt = XLGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_NUMTC_POS, + MAC_HWF1R_NUMTC_LEN); + hw_feat->hash_table_size = XLGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_HASHTBLSZ_POS, + MAC_HWF1R_HASHTBLSZ_LEN); + hw_feat->l3l4_filter_num = XLGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_L3L4FNUM_POS, + MAC_HWF1R_L3L4FNUM_LEN); + + /* Hardware feature register 2 */ + hw_feat->rx_q_cnt = XLGMAC_GET_REG_BITS(mac_hfr2, + MAC_HWF2R_RXQCNT_POS, + MAC_HWF2R_RXQCNT_LEN); + hw_feat->tx_q_cnt = XLGMAC_GET_REG_BITS(mac_hfr2, + MAC_HWF2R_TXQCNT_POS, + MAC_HWF2R_TXQCNT_LEN); + hw_feat->rx_ch_cnt = XLGMAC_GET_REG_BITS(mac_hfr2, + MAC_HWF2R_RXCHCNT_POS, + MAC_HWF2R_RXCHCNT_LEN); + hw_feat->tx_ch_cnt = XLGMAC_GET_REG_BITS(mac_hfr2, + MAC_HWF2R_TXCHCNT_POS, + MAC_HWF2R_TXCHCNT_LEN); + hw_feat->pps_out_num = XLGMAC_GET_REG_BITS(mac_hfr2, + MAC_HWF2R_PPSOUTNUM_POS, + MAC_HWF2R_PPSOUTNUM_LEN); + hw_feat->aux_snap_num = XLGMAC_GET_REG_BITS(mac_hfr2, + MAC_HWF2R_AUXSNAPNUM_POS, + MAC_HWF2R_AUXSNAPNUM_LEN); + + /* Translate the Hash Table size into actual number */ + switch (hw_feat->hash_table_size) { + case 0: + break; + case 1: + hw_feat->hash_table_size = 64; + break; + case 2: + hw_feat->hash_table_size = 128; + break; + case 3: + hw_feat->hash_table_size = 256; + break; + } + + /* Translate the address width setting into actual number */ + switch (hw_feat->dma_width) { + case 0: + hw_feat->dma_width = 32; + break; + case 1: + hw_feat->dma_width = 40; + break; + case 2: + hw_feat->dma_width = 48; + break; + default: + hw_feat->dma_width = 32; + } + + /* The Queue, Channel and TC counts are zero based so increment them + * to get the actual number + */ + hw_feat->rx_q_cnt++; + hw_feat->tx_q_cnt++; + hw_feat->rx_ch_cnt++; + hw_feat->tx_ch_cnt++; + hw_feat->tc_cnt++; +} + +void xlgmac_print_all_hw_features(struct xlgmac_pdata *pdata) +{ + char *str = NULL; + + XLGMAC_PR("\n"); + XLGMAC_PR("=====================================================\n"); + XLGMAC_PR("\n"); + XLGMAC_PR("HW support following features\n"); + XLGMAC_PR("\n"); + /* HW Feature Register0 */ + XLGMAC_PR("VLAN Hash Filter Selected : %s\n", + pdata->hw_feat.vlhash ? "YES" : "NO"); + XLGMAC_PR("SMA (MDIO) Interface : %s\n", + pdata->hw_feat.sma ? "YES" : "NO"); + XLGMAC_PR("PMT Remote Wake-up Packet Enable : %s\n", + pdata->hw_feat.rwk ? "YES" : "NO"); + XLGMAC_PR("PMT Magic Packet Enable : %s\n", + pdata->hw_feat.mgk ? "YES" : "NO"); + XLGMAC_PR("RMON/MMC Module Enable : %s\n", + pdata->hw_feat.mmc ? "YES" : "NO"); + XLGMAC_PR("ARP Offload Enabled : %s\n", + pdata->hw_feat.aoe ? "YES" : "NO"); + XLGMAC_PR("IEEE 1588-2008 Timestamp Enabled : %s\n", + pdata->hw_feat.ts ? "YES" : "NO"); + XLGMAC_PR("Energy Efficient Ethernet Enabled : %s\n", + pdata->hw_feat.eee ? "YES" : "NO"); + XLGMAC_PR("Transmit Checksum Offload Enabled : %s\n", + pdata->hw_feat.tx_coe ? "YES" : "NO"); + XLGMAC_PR("Receive Checksum Offload Enabled : %s\n", + pdata->hw_feat.rx_coe ? "YES" : "NO"); + XLGMAC_PR("Additional MAC Addresses 1-31 Selected : %s\n", + pdata->hw_feat.addn_mac ? "YES" : "NO"); + + switch (pdata->hw_feat.ts_src) { + case 0: + str = "RESERVED"; + break; + case 1: + str = "INTERNAL"; + break; + case 2: + str = "EXTERNAL"; + break; + case 3: + str = "BOTH"; + break; + } + XLGMAC_PR("Timestamp System Time Source : %s\n", str); + + XLGMAC_PR("Source Address or VLAN Insertion Enable : %s\n", + pdata->hw_feat.sa_vlan_ins ? "YES" : "NO"); + + /* HW Feature Register1 */ + switch (pdata->hw_feat.rx_fifo_size) { + case 0: + str = "128 bytes"; + break; + case 1: + str = "256 bytes"; + break; + case 2: + str = "512 bytes"; + break; + case 3: + str = "1 KBytes"; + break; + case 4: + str = "2 KBytes"; + break; + case 5: + str = "4 KBytes"; + break; + case 6: + str = "8 KBytes"; + break; + case 7: + str = "16 KBytes"; + break; + case 8: + str = "32 kBytes"; + break; + case 9: + str = "64 KBytes"; + break; + case 10: + str = "128 KBytes"; + break; + case 11: + str = "256 KBytes"; + break; + default: + str = "RESERVED"; + } + XLGMAC_PR("MTL Receive FIFO Size : %s\n", str); + + switch (pdata->hw_feat.tx_fifo_size) { + case 0: + str = "128 bytes"; + break; + case 1: + str = "256 bytes"; + break; + case 2: + str = "512 bytes"; + break; + case 3: + str = "1 KBytes"; + break; + case 4: + str = "2 KBytes"; + break; + case 5: + str = "4 KBytes"; + break; + case 6: + str = "8 KBytes"; + break; + case 7: + str = "16 KBytes"; + break; + case 8: + str = "32 kBytes"; + break; + case 9: + str = "64 KBytes"; + break; + case 10: + str = "128 KBytes"; + break; + case 11: + str = "256 KBytes"; + break; + default: + str = "RESERVED"; + } + XLGMAC_PR("MTL Transmit FIFO Size : %s\n", str); + + XLGMAC_PR("IEEE 1588 High Word Register Enable : %s\n", + pdata->hw_feat.adv_ts_hi ? "YES" : "NO"); + XLGMAC_PR("Address width : %u\n", + pdata->hw_feat.dma_width); + XLGMAC_PR("DCB Feature Enable : %s\n", + pdata->hw_feat.dcb ? "YES" : "NO"); + XLGMAC_PR("Split Header Feature Enable : %s\n", + pdata->hw_feat.sph ? "YES" : "NO"); + XLGMAC_PR("TCP Segmentation Offload Enable : %s\n", + pdata->hw_feat.tso ? "YES" : "NO"); + XLGMAC_PR("DMA Debug Registers Enabled : %s\n", + pdata->hw_feat.dma_debug ? "YES" : "NO"); + XLGMAC_PR("RSS Feature Enabled : %s\n", + pdata->hw_feat.rss ? "YES" : "NO"); + XLGMAC_PR("Number of Traffic classes : %u\n", + (pdata->hw_feat.tc_cnt)); + XLGMAC_PR("Hash Table Size : %u\n", + pdata->hw_feat.hash_table_size); + XLGMAC_PR("Total number of L3 or L4 Filters : %u\n", + pdata->hw_feat.l3l4_filter_num); + + /* HW Feature Register2 */ + XLGMAC_PR("Number of MTL Receive Queues : %u\n", + pdata->hw_feat.rx_q_cnt); + XLGMAC_PR("Number of MTL Transmit Queues : %u\n", + pdata->hw_feat.tx_q_cnt); + XLGMAC_PR("Number of DMA Receive Channels : %u\n", + pdata->hw_feat.rx_ch_cnt); + XLGMAC_PR("Number of DMA Transmit Channels : %u\n", + pdata->hw_feat.tx_ch_cnt); + + switch (pdata->hw_feat.pps_out_num) { + case 0: + str = "No PPS output"; + break; + case 1: + str = "1 PPS output"; + break; + case 2: + str = "2 PPS output"; + break; + case 3: + str = "3 PPS output"; + break; + case 4: + str = "4 PPS output"; + break; + default: + str = "RESERVED"; + } + XLGMAC_PR("Number of PPS Outputs : %s\n", str); + + switch (pdata->hw_feat.aux_snap_num) { + case 0: + str = "No auxiliary input"; + break; + case 1: + str = "1 auxiliary input"; + break; + case 2: + str = "2 auxiliary input"; + break; + case 3: + str = "3 auxiliary input"; + break; + case 4: + str = "4 auxiliary input"; + break; + default: + str = "RESERVED"; + } + XLGMAC_PR("Number of Auxiliary Snapshot Inputs : %s", str); + + XLGMAC_PR("\n"); + XLGMAC_PR("=====================================================\n"); + XLGMAC_PR("\n"); +} diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c new file mode 100644 index 000000000000..55c796ed7d26 --- /dev/null +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c @@ -0,0 +1,648 @@ +/* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver + * + * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This Synopsys DWC XLGMAC software driver and associated documentation + * (hereinafter the "Software") is an unsupported proprietary work of + * Synopsys, Inc. unless otherwise expressly agreed to in writing between + * Synopsys and you. The Software IS NOT an item of Licensed Software or a + * Licensed Product under any End User Software License Agreement or + * Agreement for Licensed Products with Synopsys or any supplement thereto. + * Synopsys is a registered trademark of Synopsys, Inc. Other names included + * in the SOFTWARE may be the trademarks of their respective owners. + */ + +#include "dwc-xlgmac.h" +#include "dwc-xlgmac-reg.h" + +static void xlgmac_unmap_desc_data(struct xlgmac_pdata *pdata, + struct xlgmac_desc_data *desc_data) +{ + if (desc_data->skb_dma) { + if (desc_data->mapped_as_page) { + dma_unmap_page(pdata->dev, desc_data->skb_dma, + desc_data->skb_dma_len, DMA_TO_DEVICE); + } else { + dma_unmap_single(pdata->dev, desc_data->skb_dma, + desc_data->skb_dma_len, DMA_TO_DEVICE); + } + desc_data->skb_dma = 0; + desc_data->skb_dma_len = 0; + } + + if (desc_data->skb) { + dev_kfree_skb_any(desc_data->skb); + desc_data->skb = NULL; + } + + if (desc_data->rx.hdr.pa.pages) + put_page(desc_data->rx.hdr.pa.pages); + + if (desc_data->rx.hdr.pa_unmap.pages) { + dma_unmap_page(pdata->dev, desc_data->rx.hdr.pa_unmap.pages_dma, + desc_data->rx.hdr.pa_unmap.pages_len, + DMA_FROM_DEVICE); + put_page(desc_data->rx.hdr.pa_unmap.pages); + } + + if (desc_data->rx.buf.pa.pages) + put_page(desc_data->rx.buf.pa.pages); + + if (desc_data->rx.buf.pa_unmap.pages) { + dma_unmap_page(pdata->dev, desc_data->rx.buf.pa_unmap.pages_dma, + desc_data->rx.buf.pa_unmap.pages_len, + DMA_FROM_DEVICE); + put_page(desc_data->rx.buf.pa_unmap.pages); + } + + memset(&desc_data->tx, 0, sizeof(desc_data->tx)); + memset(&desc_data->rx, 0, sizeof(desc_data->rx)); + + desc_data->mapped_as_page = 0; + + if (desc_data->state_saved) { + desc_data->state_saved = 0; + desc_data->state.skb = NULL; + desc_data->state.len = 0; + desc_data->state.error = 0; + } +} + +static void xlgmac_free_ring(struct xlgmac_pdata *pdata, + struct xlgmac_ring *ring) +{ + struct xlgmac_desc_data *desc_data; + unsigned int i; + + if (!ring) + return; + + if (ring->desc_data_head) { + for (i = 0; i < ring->dma_desc_count; i++) { + desc_data = XLGMAC_GET_DESC_DATA(ring, i); + xlgmac_unmap_desc_data(pdata, desc_data); + } + + kfree(ring->desc_data_head); + ring->desc_data_head = NULL; + } + + if (ring->rx_hdr_pa.pages) { + dma_unmap_page(pdata->dev, ring->rx_hdr_pa.pages_dma, + ring->rx_hdr_pa.pages_len, DMA_FROM_DEVICE); + put_page(ring->rx_hdr_pa.pages); + + ring->rx_hdr_pa.pages = NULL; + ring->rx_hdr_pa.pages_len = 0; + ring->rx_hdr_pa.pages_offset = 0; + ring->rx_hdr_pa.pages_dma = 0; + } + + if (ring->rx_buf_pa.pages) { + dma_unmap_page(pdata->dev, ring->rx_buf_pa.pages_dma, + ring->rx_buf_pa.pages_len, DMA_FROM_DEVICE); + put_page(ring->rx_buf_pa.pages); + + ring->rx_buf_pa.pages = NULL; + ring->rx_buf_pa.pages_len = 0; + ring->rx_buf_pa.pages_offset = 0; + ring->rx_buf_pa.pages_dma = 0; + } + + if (ring->dma_desc_head) { + dma_free_coherent(pdata->dev, + (sizeof(struct xlgmac_dma_desc) * + ring->dma_desc_count), + ring->dma_desc_head, + ring->dma_desc_head_addr); + ring->dma_desc_head = NULL; + } +} + +static int xlgmac_init_ring(struct xlgmac_pdata *pdata, + struct xlgmac_ring *ring, + unsigned int dma_desc_count) +{ + if (!ring) + return 0; + + /* Descriptors */ + ring->dma_desc_count = dma_desc_count; + ring->dma_desc_head = dma_alloc_coherent(pdata->dev, + (sizeof(struct xlgmac_dma_desc) * + dma_desc_count), + &ring->dma_desc_head_addr, + GFP_KERNEL); + if (!ring->dma_desc_head) + return -ENOMEM; + + /* Array of descriptor data */ + ring->desc_data_head = kcalloc(dma_desc_count, + sizeof(struct xlgmac_desc_data), + GFP_KERNEL); + if (!ring->desc_data_head) + return -ENOMEM; + + netif_dbg(pdata, drv, pdata->netdev, + "dma_desc_head=%p, dma_desc_head_addr=%pad, desc_data_head=%p\n", + ring->dma_desc_head, + &ring->dma_desc_head_addr, + ring->desc_data_head); + + return 0; +} + +static void xlgmac_free_rings(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + + if (!pdata->channel_head) + return; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + xlgmac_free_ring(pdata, channel->tx_ring); + xlgmac_free_ring(pdata, channel->rx_ring); + } +} + +static int xlgmac_alloc_rings(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + int ret; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + netif_dbg(pdata, drv, pdata->netdev, "%s - Tx ring:\n", + channel->name); + + ret = xlgmac_init_ring(pdata, channel->tx_ring, + pdata->tx_desc_count); + + if (ret) { + netdev_alert(pdata->netdev, + "error initializing Tx ring"); + goto err_init_ring; + } + + netif_dbg(pdata, drv, pdata->netdev, "%s - Rx ring:\n", + channel->name); + + ret = xlgmac_init_ring(pdata, channel->rx_ring, + pdata->rx_desc_count); + if (ret) { + netdev_alert(pdata->netdev, + "error initializing Rx ring\n"); + goto err_init_ring; + } + } + + return 0; + +err_init_ring: + xlgmac_free_rings(pdata); + + return ret; +} + +static void xlgmac_free_channels(struct xlgmac_pdata *pdata) +{ + if (!pdata->channel_head) + return; + + kfree(pdata->channel_head->tx_ring); + pdata->channel_head->tx_ring = NULL; + + kfree(pdata->channel_head->rx_ring); + pdata->channel_head->rx_ring = NULL; + + kfree(pdata->channel_head); + + pdata->channel_head = NULL; + pdata->channel_count = 0; +} + +static int xlgmac_alloc_channels(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel_head, *channel; + struct xlgmac_ring *tx_ring, *rx_ring; + int ret = -ENOMEM; + unsigned int i; + + channel_head = kcalloc(pdata->channel_count, + sizeof(struct xlgmac_channel), GFP_KERNEL); + if (!channel_head) + return ret; + + netif_dbg(pdata, drv, pdata->netdev, + "channel_head=%p\n", channel_head); + + tx_ring = kcalloc(pdata->tx_ring_count, sizeof(struct xlgmac_ring), + GFP_KERNEL); + if (!tx_ring) + goto err_tx_ring; + + rx_ring = kcalloc(pdata->rx_ring_count, sizeof(struct xlgmac_ring), + GFP_KERNEL); + if (!rx_ring) + goto err_rx_ring; + + for (i = 0, channel = channel_head; i < pdata->channel_count; + i++, channel++) { + snprintf(channel->name, sizeof(channel->name), "channel-%u", i); + channel->pdata = pdata; + channel->queue_index = i; + channel->dma_regs = pdata->mac_regs + DMA_CH_BASE + + (DMA_CH_INC * i); + + if (pdata->per_channel_irq) { + /* Get the per DMA interrupt */ + ret = pdata->channel_irq[i]; + if (ret < 0) { + netdev_err(pdata->netdev, + "get_irq %u failed\n", + i + 1); + goto err_irq; + } + channel->dma_irq = ret; + } + + if (i < pdata->tx_ring_count) + channel->tx_ring = tx_ring++; + + if (i < pdata->rx_ring_count) + channel->rx_ring = rx_ring++; + + netif_dbg(pdata, drv, pdata->netdev, + "%s: dma_regs=%p, tx_ring=%p, rx_ring=%p\n", + channel->name, channel->dma_regs, + channel->tx_ring, channel->rx_ring); + } + + pdata->channel_head = channel_head; + + return 0; + +err_irq: + kfree(rx_ring); + +err_rx_ring: + kfree(tx_ring); + +err_tx_ring: + kfree(channel_head); + + return ret; +} + +static void xlgmac_free_channels_and_rings(struct xlgmac_pdata *pdata) +{ + xlgmac_free_rings(pdata); + + xlgmac_free_channels(pdata); +} + +static int xlgmac_alloc_channels_and_rings(struct xlgmac_pdata *pdata) +{ + int ret; + + ret = xlgmac_alloc_channels(pdata); + if (ret) + goto err_alloc; + + ret = xlgmac_alloc_rings(pdata); + if (ret) + goto err_alloc; + + return 0; + +err_alloc: + xlgmac_free_channels_and_rings(pdata); + + return ret; +} + +static int xlgmac_alloc_pages(struct xlgmac_pdata *pdata, + struct xlgmac_page_alloc *pa, + gfp_t gfp, int order) +{ + struct page *pages = NULL; + dma_addr_t pages_dma; + int ret; + + /* Try to obtain pages, decreasing order if necessary */ + gfp |= __GFP_COLD | __GFP_COMP | __GFP_NOWARN; + while (order >= 0) { + pages = alloc_pages(gfp, order); + if (pages) + break; + + order--; + } + if (!pages) + return -ENOMEM; + + /* Map the pages */ + pages_dma = dma_map_page(pdata->dev, pages, 0, + PAGE_SIZE << order, DMA_FROM_DEVICE); + ret = dma_mapping_error(pdata->dev, pages_dma); + if (ret) { + put_page(pages); + return ret; + } + + pa->pages = pages; + pa->pages_len = PAGE_SIZE << order; + pa->pages_offset = 0; + pa->pages_dma = pages_dma; + + return 0; +} + +static void xlgmac_set_buffer_data(struct xlgmac_buffer_data *bd, + struct xlgmac_page_alloc *pa, + unsigned int len) +{ + get_page(pa->pages); + bd->pa = *pa; + + bd->dma_base = pa->pages_dma; + bd->dma_off = pa->pages_offset; + bd->dma_len = len; + + pa->pages_offset += len; + if ((pa->pages_offset + len) > pa->pages_len) { + /* This data descriptor is responsible for unmapping page(s) */ + bd->pa_unmap = *pa; + + /* Get a new allocation next time */ + pa->pages = NULL; + pa->pages_len = 0; + pa->pages_offset = 0; + pa->pages_dma = 0; + } +} + +static int xlgmac_map_rx_buffer(struct xlgmac_pdata *pdata, + struct xlgmac_ring *ring, + struct xlgmac_desc_data *desc_data) +{ + int order, ret; + + if (!ring->rx_hdr_pa.pages) { + ret = xlgmac_alloc_pages(pdata, &ring->rx_hdr_pa, + GFP_ATOMIC, 0); + if (ret) + return ret; + } + + if (!ring->rx_buf_pa.pages) { + order = max_t(int, PAGE_ALLOC_COSTLY_ORDER - 1, 0); + ret = xlgmac_alloc_pages(pdata, &ring->rx_buf_pa, + GFP_ATOMIC, order); + if (ret) + return ret; + } + + /* Set up the header page info */ + xlgmac_set_buffer_data(&desc_data->rx.hdr, &ring->rx_hdr_pa, + XLGMAC_SKB_ALLOC_SIZE); + + /* Set up the buffer page info */ + xlgmac_set_buffer_data(&desc_data->rx.buf, &ring->rx_buf_pa, + pdata->rx_buf_size); + + return 0; +} + +static void xlgmac_tx_desc_init(struct xlgmac_pdata *pdata) +{ + struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; + struct xlgmac_desc_data *desc_data; + struct xlgmac_dma_desc *dma_desc; + struct xlgmac_channel *channel; + struct xlgmac_ring *ring; + dma_addr_t dma_desc_addr; + unsigned int i, j; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + ring = channel->tx_ring; + if (!ring) + break; + + dma_desc = ring->dma_desc_head; + dma_desc_addr = ring->dma_desc_head_addr; + + for (j = 0; j < ring->dma_desc_count; j++) { + desc_data = XLGMAC_GET_DESC_DATA(ring, j); + + desc_data->dma_desc = dma_desc; + desc_data->dma_desc_addr = dma_desc_addr; + + dma_desc++; + dma_desc_addr += sizeof(struct xlgmac_dma_desc); + } + + ring->cur = 0; + ring->dirty = 0; + memset(&ring->tx, 0, sizeof(ring->tx)); + + hw_ops->tx_desc_init(channel); + } +} + +static void xlgmac_rx_desc_init(struct xlgmac_pdata *pdata) +{ + struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; + struct xlgmac_desc_data *desc_data; + struct xlgmac_dma_desc *dma_desc; + struct xlgmac_channel *channel; + struct xlgmac_ring *ring; + dma_addr_t dma_desc_addr; + unsigned int i, j; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + ring = channel->rx_ring; + if (!ring) + break; + + dma_desc = ring->dma_desc_head; + dma_desc_addr = ring->dma_desc_head_addr; + + for (j = 0; j < ring->dma_desc_count; j++) { + desc_data = XLGMAC_GET_DESC_DATA(ring, j); + + desc_data->dma_desc = dma_desc; + desc_data->dma_desc_addr = dma_desc_addr; + + if (xlgmac_map_rx_buffer(pdata, ring, desc_data)) + break; + + dma_desc++; + dma_desc_addr += sizeof(struct xlgmac_dma_desc); + } + + ring->cur = 0; + ring->dirty = 0; + + hw_ops->rx_desc_init(channel); + } +} + +static int xlgmac_map_tx_skb(struct xlgmac_channel *channel, + struct sk_buff *skb) +{ + struct xlgmac_pdata *pdata = channel->pdata; + struct xlgmac_ring *ring = channel->tx_ring; + unsigned int start_index, cur_index; + struct xlgmac_desc_data *desc_data; + unsigned int offset, datalen, len; + struct xlgmac_pkt_info *pkt_info; + struct skb_frag_struct *frag; + unsigned int tso, vlan; + dma_addr_t skb_dma; + unsigned int i; + + offset = 0; + start_index = ring->cur; + cur_index = ring->cur; + + pkt_info = &ring->pkt_info; + pkt_info->desc_count = 0; + pkt_info->length = 0; + + tso = XLGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN); + vlan = XLGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, + TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN); + + /* Save space for a context descriptor if needed */ + if ((tso && (pkt_info->mss != ring->tx.cur_mss)) || + (vlan && (pkt_info->vlan_ctag != ring->tx.cur_vlan_ctag))) + cur_index++; + desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index); + + if (tso) { + /* Map the TSO header */ + skb_dma = dma_map_single(pdata->dev, skb->data, + pkt_info->header_len, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, skb_dma)) { + netdev_alert(pdata->netdev, "dma_map_single failed\n"); + goto err_out; + } + desc_data->skb_dma = skb_dma; + desc_data->skb_dma_len = pkt_info->header_len; + netif_dbg(pdata, tx_queued, pdata->netdev, + "skb header: index=%u, dma=%pad, len=%u\n", + cur_index, &skb_dma, pkt_info->header_len); + + offset = pkt_info->header_len; + + pkt_info->length += pkt_info->header_len; + + cur_index++; + desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index); + } + + /* Map the (remainder of the) packet */ + for (datalen = skb_headlen(skb) - offset; datalen; ) { + len = min_t(unsigned int, datalen, XLGMAC_TX_MAX_BUF_SIZE); + + skb_dma = dma_map_single(pdata->dev, skb->data + offset, len, + DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, skb_dma)) { + netdev_alert(pdata->netdev, "dma_map_single failed\n"); + goto err_out; + } + desc_data->skb_dma = skb_dma; + desc_data->skb_dma_len = len; + netif_dbg(pdata, tx_queued, pdata->netdev, + "skb data: index=%u, dma=%pad, len=%u\n", + cur_index, &skb_dma, len); + + datalen -= len; + offset += len; + + pkt_info->length += len; + + cur_index++; + desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index); + } + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + netif_dbg(pdata, tx_queued, pdata->netdev, + "mapping frag %u\n", i); + + frag = &skb_shinfo(skb)->frags[i]; + offset = 0; + + for (datalen = skb_frag_size(frag); datalen; ) { + len = min_t(unsigned int, datalen, + XLGMAC_TX_MAX_BUF_SIZE); + + skb_dma = skb_frag_dma_map(pdata->dev, frag, offset, + len, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, skb_dma)) { + netdev_alert(pdata->netdev, + "skb_frag_dma_map failed\n"); + goto err_out; + } + desc_data->skb_dma = skb_dma; + desc_data->skb_dma_len = len; + desc_data->mapped_as_page = 1; + netif_dbg(pdata, tx_queued, pdata->netdev, + "skb frag: index=%u, dma=%pad, len=%u\n", + cur_index, &skb_dma, len); + + datalen -= len; + offset += len; + + pkt_info->length += len; + + cur_index++; + desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index); + } + } + + /* Save the skb address in the last entry. We always have some data + * that has been mapped so desc_data is always advanced past the last + * piece of mapped data - use the entry pointed to by cur_index - 1. + */ + desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index - 1); + desc_data->skb = skb; + + /* Save the number of descriptor entries used */ + pkt_info->desc_count = cur_index - start_index; + + return pkt_info->desc_count; + +err_out: + while (start_index < cur_index) { + desc_data = XLGMAC_GET_DESC_DATA(ring, start_index++); + xlgmac_unmap_desc_data(pdata, desc_data); + } + + return 0; +} + +void xlgmac_init_desc_ops(struct xlgmac_desc_ops *desc_ops) +{ + desc_ops->alloc_channles_and_rings = xlgmac_alloc_channels_and_rings; + desc_ops->free_channels_and_rings = xlgmac_free_channels_and_rings; + desc_ops->map_tx_skb = xlgmac_map_tx_skb; + desc_ops->map_rx_buffer = xlgmac_map_rx_buffer; + desc_ops->unmap_desc_data = xlgmac_unmap_desc_data; + desc_ops->tx_desc_init = xlgmac_tx_desc_init; + desc_ops->rx_desc_init = xlgmac_rx_desc_init; +} diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c new file mode 100644 index 000000000000..5cf3e90d4834 --- /dev/null +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c @@ -0,0 +1,3146 @@ +/* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver + * + * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This Synopsys DWC XLGMAC software driver and associated documentation + * (hereinafter the "Software") is an unsupported proprietary work of + * Synopsys, Inc. unless otherwise expressly agreed to in writing between + * Synopsys and you. The Software IS NOT an item of Licensed Software or a + * Licensed Product under any End User Software License Agreement or + * Agreement for Licensed Products with Synopsys or any supplement thereto. + * Synopsys is a registered trademark of Synopsys, Inc. Other names included + * in the SOFTWARE may be the trademarks of their respective owners. + */ + +#include +#include +#include +#include +#include + +#include "dwc-xlgmac.h" +#include "dwc-xlgmac-reg.h" + +static int xlgmac_tx_complete(struct xlgmac_dma_desc *dma_desc) +{ + return !XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + TX_NORMAL_DESC3_OWN_POS, + TX_NORMAL_DESC3_OWN_LEN); +} + +static int xlgmac_disable_rx_csum(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = readl(pdata->mac_regs + MAC_RCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RCR_IPC_POS, + MAC_RCR_IPC_LEN, 0); + writel(regval, pdata->mac_regs + MAC_RCR); + + return 0; +} + +static int xlgmac_enable_rx_csum(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = readl(pdata->mac_regs + MAC_RCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RCR_IPC_POS, + MAC_RCR_IPC_LEN, 1); + writel(regval, pdata->mac_regs + MAC_RCR); + + return 0; +} + +static int xlgmac_set_mac_address(struct xlgmac_pdata *pdata, u8 *addr) +{ + unsigned int mac_addr_hi, mac_addr_lo; + + mac_addr_hi = (addr[5] << 8) | (addr[4] << 0); + mac_addr_lo = (addr[3] << 24) | (addr[2] << 16) | + (addr[1] << 8) | (addr[0] << 0); + + writel(mac_addr_hi, pdata->mac_regs + MAC_MACA0HR); + writel(mac_addr_lo, pdata->mac_regs + MAC_MACA0LR); + + return 0; +} + +static void xlgmac_set_mac_reg(struct xlgmac_pdata *pdata, + struct netdev_hw_addr *ha, + unsigned int *mac_reg) +{ + unsigned int mac_addr_hi, mac_addr_lo; + u8 *mac_addr; + + mac_addr_lo = 0; + mac_addr_hi = 0; + + if (ha) { + mac_addr = (u8 *)&mac_addr_lo; + mac_addr[0] = ha->addr[0]; + mac_addr[1] = ha->addr[1]; + mac_addr[2] = ha->addr[2]; + mac_addr[3] = ha->addr[3]; + mac_addr = (u8 *)&mac_addr_hi; + mac_addr[0] = ha->addr[4]; + mac_addr[1] = ha->addr[5]; + + netif_dbg(pdata, drv, pdata->netdev, + "adding mac address %pM at %#x\n", + ha->addr, *mac_reg); + + mac_addr_hi = XLGMAC_SET_REG_BITS(mac_addr_hi, + MAC_MACA1HR_AE_POS, + MAC_MACA1HR_AE_LEN, + 1); + } + + writel(mac_addr_hi, pdata->mac_regs + *mac_reg); + *mac_reg += MAC_MACA_INC; + writel(mac_addr_lo, pdata->mac_regs + *mac_reg); + *mac_reg += MAC_MACA_INC; +} + +static int xlgmac_enable_rx_vlan_stripping(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = readl(pdata->mac_regs + MAC_VLANTR); + /* Put the VLAN tag in the Rx descriptor */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_VLANTR_EVLRXS_POS, + MAC_VLANTR_EVLRXS_LEN, 1); + /* Don't check the VLAN type */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_VLANTR_DOVLTC_POS, + MAC_VLANTR_DOVLTC_LEN, 1); + /* Check only C-TAG (0x8100) packets */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_VLANTR_ERSVLM_POS, + MAC_VLANTR_ERSVLM_LEN, 0); + /* Don't consider an S-TAG (0x88A8) packet as a VLAN packet */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_VLANTR_ESVL_POS, + MAC_VLANTR_ESVL_LEN, 0); + /* Enable VLAN tag stripping */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_VLANTR_EVLS_POS, + MAC_VLANTR_EVLS_LEN, 0x3); + writel(regval, pdata->mac_regs + MAC_VLANTR); + + return 0; +} + +static int xlgmac_disable_rx_vlan_stripping(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = readl(pdata->mac_regs + MAC_VLANTR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_VLANTR_EVLS_POS, + MAC_VLANTR_EVLS_LEN, 0); + writel(regval, pdata->mac_regs + MAC_VLANTR); + + return 0; +} + +static int xlgmac_enable_rx_vlan_filtering(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = readl(pdata->mac_regs + MAC_PFR); + /* Enable VLAN filtering */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_PFR_VTFE_POS, + MAC_PFR_VTFE_LEN, 1); + writel(regval, pdata->mac_regs + MAC_PFR); + + regval = readl(pdata->mac_regs + MAC_VLANTR); + /* Enable VLAN Hash Table filtering */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_VLANTR_VTHM_POS, + MAC_VLANTR_VTHM_LEN, 1); + /* Disable VLAN tag inverse matching */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_VLANTR_VTIM_POS, + MAC_VLANTR_VTIM_LEN, 0); + /* Only filter on the lower 12-bits of the VLAN tag */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_VLANTR_ETV_POS, + MAC_VLANTR_ETV_LEN, 1); + /* In order for the VLAN Hash Table filtering to be effective, + * the VLAN tag identifier in the VLAN Tag Register must not + * be zero. Set the VLAN tag identifier to "1" to enable the + * VLAN Hash Table filtering. This implies that a VLAN tag of + * 1 will always pass filtering. + */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_VLANTR_VL_POS, + MAC_VLANTR_VL_LEN, 1); + writel(regval, pdata->mac_regs + MAC_VLANTR); + + return 0; +} + +static int xlgmac_disable_rx_vlan_filtering(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = readl(pdata->mac_regs + MAC_PFR); + /* Disable VLAN filtering */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_PFR_VTFE_POS, + MAC_PFR_VTFE_LEN, 0); + writel(regval, pdata->mac_regs + MAC_PFR); + + return 0; +} + +static u32 xlgmac_vid_crc32_le(__le16 vid_le) +{ + unsigned char *data = (unsigned char *)&vid_le; + unsigned char data_byte = 0; + u32 poly = 0xedb88320; + u32 crc = ~0; + u32 temp = 0; + int i, bits; + + bits = get_bitmask_order(VLAN_VID_MASK); + for (i = 0; i < bits; i++) { + if ((i % 8) == 0) + data_byte = data[i / 8]; + + temp = ((crc & 1) ^ data_byte) & 1; + crc >>= 1; + data_byte >>= 1; + + if (temp) + crc ^= poly; + } + + return crc; +} + +static int xlgmac_update_vlan_hash_table(struct xlgmac_pdata *pdata) +{ + u16 vlan_hash_table = 0; + __le16 vid_le; + u32 regval; + u32 crc; + u16 vid; + + /* Generate the VLAN Hash Table value */ + for_each_set_bit(vid, pdata->active_vlans, VLAN_N_VID) { + /* Get the CRC32 value of the VLAN ID */ + vid_le = cpu_to_le16(vid); + crc = bitrev32(~xlgmac_vid_crc32_le(vid_le)) >> 28; + + vlan_hash_table |= (1 << crc); + } + + regval = readl(pdata->mac_regs + MAC_VLANHTR); + /* Set the VLAN Hash Table filtering register */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_VLANHTR_VLHT_POS, + MAC_VLANHTR_VLHT_LEN, vlan_hash_table); + writel(regval, pdata->mac_regs + MAC_VLANHTR); + + return 0; +} + +static int xlgmac_set_promiscuous_mode(struct xlgmac_pdata *pdata, + unsigned int enable) +{ + unsigned int val = enable ? 1 : 0; + u32 regval; + + regval = XLGMAC_GET_REG_BITS(readl(pdata->mac_regs + MAC_PFR), + MAC_PFR_PR_POS, MAC_PFR_PR_LEN); + if (regval == val) + return 0; + + netif_dbg(pdata, drv, pdata->netdev, "%s promiscuous mode\n", + enable ? "entering" : "leaving"); + + regval = readl(pdata->mac_regs + MAC_PFR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_PFR_PR_POS, + MAC_PFR_PR_LEN, val); + writel(regval, pdata->mac_regs + MAC_PFR); + + /* Hardware will still perform VLAN filtering in promiscuous mode */ + if (enable) { + xlgmac_disable_rx_vlan_filtering(pdata); + } else { + if (pdata->netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER) + xlgmac_enable_rx_vlan_filtering(pdata); + } + + return 0; +} + +static int xlgmac_set_all_multicast_mode(struct xlgmac_pdata *pdata, + unsigned int enable) +{ + unsigned int val = enable ? 1 : 0; + u32 regval; + + regval = XLGMAC_GET_REG_BITS(readl(pdata->mac_regs + MAC_PFR), + MAC_PFR_PM_POS, MAC_PFR_PM_LEN); + if (regval == val) + return 0; + + netif_dbg(pdata, drv, pdata->netdev, "%s allmulti mode\n", + enable ? "entering" : "leaving"); + + regval = readl(pdata->mac_regs + MAC_PFR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_PFR_PM_POS, + MAC_PFR_PM_LEN, val); + writel(regval, pdata->mac_regs + MAC_PFR); + + return 0; +} + +static void xlgmac_set_mac_addn_addrs(struct xlgmac_pdata *pdata) +{ + struct net_device *netdev = pdata->netdev; + struct netdev_hw_addr *ha; + unsigned int addn_macs; + unsigned int mac_reg; + + mac_reg = MAC_MACA1HR; + addn_macs = pdata->hw_feat.addn_mac; + + if (netdev_uc_count(netdev) > addn_macs) { + xlgmac_set_promiscuous_mode(pdata, 1); + } else { + netdev_for_each_uc_addr(ha, netdev) { + xlgmac_set_mac_reg(pdata, ha, &mac_reg); + addn_macs--; + } + + if (netdev_mc_count(netdev) > addn_macs) { + xlgmac_set_all_multicast_mode(pdata, 1); + } else { + netdev_for_each_mc_addr(ha, netdev) { + xlgmac_set_mac_reg(pdata, ha, &mac_reg); + addn_macs--; + } + } + } + + /* Clear remaining additional MAC address entries */ + while (addn_macs--) + xlgmac_set_mac_reg(pdata, NULL, &mac_reg); +} + +static void xlgmac_set_mac_hash_table(struct xlgmac_pdata *pdata) +{ + unsigned int hash_table_shift, hash_table_count; + u32 hash_table[XLGMAC_MAC_HASH_TABLE_SIZE]; + struct net_device *netdev = pdata->netdev; + struct netdev_hw_addr *ha; + unsigned int hash_reg; + unsigned int i; + u32 crc; + + hash_table_shift = 26 - (pdata->hw_feat.hash_table_size >> 7); + hash_table_count = pdata->hw_feat.hash_table_size / 32; + memset(hash_table, 0, sizeof(hash_table)); + + /* Build the MAC Hash Table register values */ + netdev_for_each_uc_addr(ha, netdev) { + crc = bitrev32(~crc32_le(~0, ha->addr, ETH_ALEN)); + crc >>= hash_table_shift; + hash_table[crc >> 5] |= (1 << (crc & 0x1f)); + } + + netdev_for_each_mc_addr(ha, netdev) { + crc = bitrev32(~crc32_le(~0, ha->addr, ETH_ALEN)); + crc >>= hash_table_shift; + hash_table[crc >> 5] |= (1 << (crc & 0x1f)); + } + + /* Set the MAC Hash Table registers */ + hash_reg = MAC_HTR0; + for (i = 0; i < hash_table_count; i++) { + writel(hash_table[i], pdata->mac_regs + hash_reg); + hash_reg += MAC_HTR_INC; + } +} + +static int xlgmac_add_mac_addresses(struct xlgmac_pdata *pdata) +{ + if (pdata->hw_feat.hash_table_size) + xlgmac_set_mac_hash_table(pdata); + else + xlgmac_set_mac_addn_addrs(pdata); + + return 0; +} + +static void xlgmac_config_mac_address(struct xlgmac_pdata *pdata) +{ + u32 regval; + + xlgmac_set_mac_address(pdata, pdata->netdev->dev_addr); + + /* Filtering is done using perfect filtering and hash filtering */ + if (pdata->hw_feat.hash_table_size) { + regval = readl(pdata->mac_regs + MAC_PFR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_PFR_HPF_POS, + MAC_PFR_HPF_LEN, 1); + regval = XLGMAC_SET_REG_BITS(regval, MAC_PFR_HUC_POS, + MAC_PFR_HUC_LEN, 1); + regval = XLGMAC_SET_REG_BITS(regval, MAC_PFR_HMC_POS, + MAC_PFR_HMC_LEN, 1); + writel(regval, pdata->mac_regs + MAC_PFR); + } +} + +static void xlgmac_config_jumbo_enable(struct xlgmac_pdata *pdata) +{ + unsigned int val; + u32 regval; + + val = (pdata->netdev->mtu > XLGMAC_STD_PACKET_MTU) ? 1 : 0; + + regval = readl(pdata->mac_regs + MAC_RCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RCR_JE_POS, + MAC_RCR_JE_LEN, val); + writel(regval, pdata->mac_regs + MAC_RCR); +} + +static void xlgmac_config_checksum_offload(struct xlgmac_pdata *pdata) +{ + if (pdata->netdev->features & NETIF_F_RXCSUM) + xlgmac_enable_rx_csum(pdata); + else + xlgmac_disable_rx_csum(pdata); +} + +static void xlgmac_config_vlan_support(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = readl(pdata->mac_regs + MAC_VLANIR); + /* Indicate that VLAN Tx CTAGs come from context descriptors */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_VLANIR_CSVL_POS, + MAC_VLANIR_CSVL_LEN, 0); + regval = XLGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLTI_POS, + MAC_VLANIR_VLTI_LEN, 1); + writel(regval, pdata->mac_regs + MAC_VLANIR); + + /* Set the current VLAN Hash Table register value */ + xlgmac_update_vlan_hash_table(pdata); + + if (pdata->netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER) + xlgmac_enable_rx_vlan_filtering(pdata); + else + xlgmac_disable_rx_vlan_filtering(pdata); + + if (pdata->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) + xlgmac_enable_rx_vlan_stripping(pdata); + else + xlgmac_disable_rx_vlan_stripping(pdata); +} + +static int xlgmac_config_rx_mode(struct xlgmac_pdata *pdata) +{ + struct net_device *netdev = pdata->netdev; + unsigned int pr_mode, am_mode; + + pr_mode = ((netdev->flags & IFF_PROMISC) != 0); + am_mode = ((netdev->flags & IFF_ALLMULTI) != 0); + + xlgmac_set_promiscuous_mode(pdata, pr_mode); + xlgmac_set_all_multicast_mode(pdata, am_mode); + + xlgmac_add_mac_addresses(pdata); + + return 0; +} + +static void xlgmac_prepare_tx_stop(struct xlgmac_pdata *pdata, + struct xlgmac_channel *channel) +{ + unsigned int tx_dsr, tx_pos, tx_qidx; + unsigned long tx_timeout; + unsigned int tx_status; + + /* Calculate the status register to read and the position within */ + if (channel->queue_index < DMA_DSRX_FIRST_QUEUE) { + tx_dsr = DMA_DSR0; + tx_pos = (channel->queue_index * DMA_DSR_Q_LEN) + + DMA_DSR0_TPS_START; + } else { + tx_qidx = channel->queue_index - DMA_DSRX_FIRST_QUEUE; + + tx_dsr = DMA_DSR1 + ((tx_qidx / DMA_DSRX_QPR) * DMA_DSRX_INC); + tx_pos = ((tx_qidx % DMA_DSRX_QPR) * DMA_DSR_Q_LEN) + + DMA_DSRX_TPS_START; + } + + /* The Tx engine cannot be stopped if it is actively processing + * descriptors. Wait for the Tx engine to enter the stopped or + * suspended state. Don't wait forever though... + */ + tx_timeout = jiffies + (XLGMAC_DMA_STOP_TIMEOUT * HZ); + while (time_before(jiffies, tx_timeout)) { + tx_status = readl(pdata->mac_regs + tx_dsr); + tx_status = XLGMAC_GET_REG_BITS(tx_status, tx_pos, + DMA_DSR_TPS_LEN); + if ((tx_status == DMA_TPS_STOPPED) || + (tx_status == DMA_TPS_SUSPENDED)) + break; + + usleep_range(500, 1000); + } + + if (!time_before(jiffies, tx_timeout)) + netdev_info(pdata->netdev, + "timed out waiting for Tx DMA channel %u to stop\n", + channel->queue_index); +} + +static void xlgmac_enable_tx(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + u32 regval; + + /* Enable each Tx DMA channel */ + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + break; + + regval = readl(XLGMAC_DMA_REG(channel, DMA_CH_TCR)); + regval = XLGMAC_SET_REG_BITS(regval, DMA_CH_TCR_ST_POS, + DMA_CH_TCR_ST_LEN, 1); + writel(regval, XLGMAC_DMA_REG(channel, DMA_CH_TCR)); + } + + /* Enable each Tx queue */ + for (i = 0; i < pdata->tx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TXQEN_POS, + MTL_Q_TQOMR_TXQEN_LEN, + MTL_Q_ENABLED); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + } + + /* Enable MAC Tx */ + regval = readl(pdata->mac_regs + MAC_TCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_TCR_TE_POS, + MAC_TCR_TE_LEN, 1); + writel(regval, pdata->mac_regs + MAC_TCR); +} + +static void xlgmac_disable_tx(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + u32 regval; + + /* Prepare for Tx DMA channel stop */ + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + break; + + xlgmac_prepare_tx_stop(pdata, channel); + } + + /* Disable MAC Tx */ + regval = readl(pdata->mac_regs + MAC_TCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_TCR_TE_POS, + MAC_TCR_TE_LEN, 0); + writel(regval, pdata->mac_regs + MAC_TCR); + + /* Disable each Tx queue */ + for (i = 0; i < pdata->tx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TXQEN_POS, + MTL_Q_TQOMR_TXQEN_LEN, 0); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + } + + /* Disable each Tx DMA channel */ + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + break; + + regval = readl(XLGMAC_DMA_REG(channel, DMA_CH_TCR)); + regval = XLGMAC_SET_REG_BITS(regval, DMA_CH_TCR_ST_POS, + DMA_CH_TCR_ST_LEN, 0); + writel(regval, XLGMAC_DMA_REG(channel, DMA_CH_TCR)); + } +} + +static void xlgmac_prepare_rx_stop(struct xlgmac_pdata *pdata, + unsigned int queue) +{ + unsigned int rx_status, prxq, rxqsts; + unsigned long rx_timeout; + + /* The Rx engine cannot be stopped if it is actively processing + * packets. Wait for the Rx queue to empty the Rx fifo. Don't + * wait forever though... + */ + rx_timeout = jiffies + (XLGMAC_DMA_STOP_TIMEOUT * HZ); + while (time_before(jiffies, rx_timeout)) { + rx_status = readl(XLGMAC_MTL_REG(pdata, queue, MTL_Q_RQDR)); + prxq = XLGMAC_GET_REG_BITS(rx_status, MTL_Q_RQDR_PRXQ_POS, + MTL_Q_RQDR_PRXQ_LEN); + rxqsts = XLGMAC_GET_REG_BITS(rx_status, MTL_Q_RQDR_RXQSTS_POS, + MTL_Q_RQDR_RXQSTS_LEN); + if ((prxq == 0) && (rxqsts == 0)) + break; + + usleep_range(500, 1000); + } + + if (!time_before(jiffies, rx_timeout)) + netdev_info(pdata->netdev, + "timed out waiting for Rx queue %u to empty\n", + queue); +} + +static void xlgmac_enable_rx(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int regval, i; + + /* Enable each Rx DMA channel */ + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->rx_ring) + break; + + regval = readl(XLGMAC_DMA_REG(channel, DMA_CH_RCR)); + regval = XLGMAC_SET_REG_BITS(regval, DMA_CH_RCR_SR_POS, + DMA_CH_RCR_SR_LEN, 1); + writel(regval, XLGMAC_DMA_REG(channel, DMA_CH_RCR)); + } + + /* Enable each Rx queue */ + regval = 0; + for (i = 0; i < pdata->rx_q_count; i++) + regval |= (0x02 << (i << 1)); + writel(regval, pdata->mac_regs + MAC_RQC0R); + + /* Enable MAC Rx */ + regval = readl(pdata->mac_regs + MAC_RCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RCR_DCRCC_POS, + MAC_RCR_DCRCC_LEN, 1); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RCR_CST_POS, + MAC_RCR_CST_LEN, 1); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RCR_ACS_POS, + MAC_RCR_ACS_LEN, 1); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RCR_RE_POS, + MAC_RCR_RE_LEN, 1); + writel(regval, pdata->mac_regs + MAC_RCR); +} + +static void xlgmac_disable_rx(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + u32 regval; + + /* Disable MAC Rx */ + regval = readl(pdata->mac_regs + MAC_RCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RCR_DCRCC_POS, + MAC_RCR_DCRCC_LEN, 0); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RCR_CST_POS, + MAC_RCR_CST_LEN, 0); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RCR_ACS_POS, + MAC_RCR_ACS_LEN, 0); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RCR_RE_POS, + MAC_RCR_RE_LEN, 0); + writel(regval, pdata->mac_regs + MAC_RCR); + + /* Prepare for Rx DMA channel stop */ + for (i = 0; i < pdata->rx_q_count; i++) + xlgmac_prepare_rx_stop(pdata, i); + + /* Disable each Rx queue */ + writel(0, pdata->mac_regs + MAC_RQC0R); + + /* Disable each Rx DMA channel */ + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->rx_ring) + break; + + regval = readl(XLGMAC_DMA_REG(channel, DMA_CH_RCR)); + regval = XLGMAC_SET_REG_BITS(regval, DMA_CH_RCR_SR_POS, + DMA_CH_RCR_SR_LEN, 0); + writel(regval, XLGMAC_DMA_REG(channel, DMA_CH_RCR)); + } +} + +static void xlgmac_tx_start_xmit(struct xlgmac_channel *channel, + struct xlgmac_ring *ring) +{ + struct xlgmac_pdata *pdata = channel->pdata; + struct xlgmac_desc_data *desc_data; + + /* Make sure everything is written before the register write */ + wmb(); + + /* Issue a poll command to Tx DMA by writing address + * of next immediate free descriptor + */ + desc_data = XLGMAC_GET_DESC_DATA(ring, ring->cur); + writel(lower_32_bits(desc_data->dma_desc_addr), + XLGMAC_DMA_REG(channel, DMA_CH_TDTR_LO)); + + /* Start the Tx timer */ + if (pdata->tx_usecs && !channel->tx_timer_active) { + channel->tx_timer_active = 1; + mod_timer(&channel->tx_timer, + jiffies + usecs_to_jiffies(pdata->tx_usecs)); + } + + ring->tx.xmit_more = 0; +} + +static void xlgmac_dev_xmit(struct xlgmac_channel *channel) +{ + struct xlgmac_pdata *pdata = channel->pdata; + struct xlgmac_ring *ring = channel->tx_ring; + unsigned int tso_context, vlan_context; + struct xlgmac_desc_data *desc_data; + struct xlgmac_dma_desc *dma_desc; + struct xlgmac_pkt_info *pkt_info; + unsigned int csum, tso, vlan; + int start_index = ring->cur; + int cur_index = ring->cur; + unsigned int tx_set_ic; + int i; + + pkt_info = &ring->pkt_info; + csum = XLGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS, + TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN); + tso = XLGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN); + vlan = XLGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, + TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN); + + if (tso && (pkt_info->mss != ring->tx.cur_mss)) + tso_context = 1; + else + tso_context = 0; + + if (vlan && (pkt_info->vlan_ctag != ring->tx.cur_vlan_ctag)) + vlan_context = 1; + else + vlan_context = 0; + + /* Determine if an interrupt should be generated for this Tx: + * Interrupt: + * - Tx frame count exceeds the frame count setting + * - Addition of Tx frame count to the frame count since the + * last interrupt was set exceeds the frame count setting + * No interrupt: + * - No frame count setting specified (ethtool -C ethX tx-frames 0) + * - Addition of Tx frame count to the frame count since the + * last interrupt was set does not exceed the frame count setting + */ + ring->coalesce_count += pkt_info->tx_packets; + if (!pdata->tx_frames) + tx_set_ic = 0; + else if (pkt_info->tx_packets > pdata->tx_frames) + tx_set_ic = 1; + else if ((ring->coalesce_count % pdata->tx_frames) < + pkt_info->tx_packets) + tx_set_ic = 1; + else + tx_set_ic = 0; + + desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index); + dma_desc = desc_data->dma_desc; + + /* Create a context descriptor if this is a TSO pkt_info */ + if (tso_context || vlan_context) { + if (tso_context) { + netif_dbg(pdata, tx_queued, pdata->netdev, + "TSO context descriptor, mss=%u\n", + pkt_info->mss); + + /* Set the MSS size */ + dma_desc->desc2 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc2, + TX_CONTEXT_DESC2_MSS_POS, + TX_CONTEXT_DESC2_MSS_LEN, + pkt_info->mss); + + /* Mark it as a CONTEXT descriptor */ + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_CONTEXT_DESC3_CTXT_POS, + TX_CONTEXT_DESC3_CTXT_LEN, + 1); + + /* Indicate this descriptor contains the MSS */ + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_CONTEXT_DESC3_TCMSSV_POS, + TX_CONTEXT_DESC3_TCMSSV_LEN, + 1); + + ring->tx.cur_mss = pkt_info->mss; + } + + if (vlan_context) { + netif_dbg(pdata, tx_queued, pdata->netdev, + "VLAN context descriptor, ctag=%u\n", + pkt_info->vlan_ctag); + + /* Mark it as a CONTEXT descriptor */ + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_CONTEXT_DESC3_CTXT_POS, + TX_CONTEXT_DESC3_CTXT_LEN, + 1); + + /* Set the VLAN tag */ + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_CONTEXT_DESC3_VT_POS, + TX_CONTEXT_DESC3_VT_LEN, + pkt_info->vlan_ctag); + + /* Indicate this descriptor contains the VLAN tag */ + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_CONTEXT_DESC3_VLTV_POS, + TX_CONTEXT_DESC3_VLTV_LEN, + 1); + + ring->tx.cur_vlan_ctag = pkt_info->vlan_ctag; + } + + cur_index++; + desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index); + dma_desc = desc_data->dma_desc; + } + + /* Update buffer address (for TSO this is the header) */ + dma_desc->desc0 = cpu_to_le32(lower_32_bits(desc_data->skb_dma)); + dma_desc->desc1 = cpu_to_le32(upper_32_bits(desc_data->skb_dma)); + + /* Update the buffer length */ + dma_desc->desc2 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc2, + TX_NORMAL_DESC2_HL_B1L_POS, + TX_NORMAL_DESC2_HL_B1L_LEN, + desc_data->skb_dma_len); + + /* VLAN tag insertion check */ + if (vlan) + dma_desc->desc2 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc2, + TX_NORMAL_DESC2_VTIR_POS, + TX_NORMAL_DESC2_VTIR_LEN, + TX_NORMAL_DESC2_VLAN_INSERT); + + /* Timestamp enablement check */ + if (XLGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_PTP_POS, + TX_PACKET_ATTRIBUTES_PTP_LEN)) + dma_desc->desc2 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc2, + TX_NORMAL_DESC2_TTSE_POS, + TX_NORMAL_DESC2_TTSE_LEN, + 1); + + /* Mark it as First Descriptor */ + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_FD_POS, + TX_NORMAL_DESC3_FD_LEN, + 1); + + /* Mark it as a NORMAL descriptor */ + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_CTXT_POS, + TX_NORMAL_DESC3_CTXT_LEN, + 0); + + /* Set OWN bit if not the first descriptor */ + if (cur_index != start_index) + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_OWN_POS, + TX_NORMAL_DESC3_OWN_LEN, + 1); + + if (tso) { + /* Enable TSO */ + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_TSE_POS, + TX_NORMAL_DESC3_TSE_LEN, 1); + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_TCPPL_POS, + TX_NORMAL_DESC3_TCPPL_LEN, + pkt_info->tcp_payload_len); + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_TCPHDRLEN_POS, + TX_NORMAL_DESC3_TCPHDRLEN_LEN, + pkt_info->tcp_header_len / 4); + + pdata->stats.tx_tso_packets++; + } else { + /* Enable CRC and Pad Insertion */ + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_CPC_POS, + TX_NORMAL_DESC3_CPC_LEN, 0); + + /* Enable HW CSUM */ + if (csum) + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_CIC_POS, + TX_NORMAL_DESC3_CIC_LEN, + 0x3); + + /* Set the total length to be transmitted */ + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_FL_POS, + TX_NORMAL_DESC3_FL_LEN, + pkt_info->length); + } + + for (i = cur_index - start_index + 1; i < pkt_info->desc_count; i++) { + cur_index++; + desc_data = XLGMAC_GET_DESC_DATA(ring, cur_index); + dma_desc = desc_data->dma_desc; + + /* Update buffer address */ + dma_desc->desc0 = + cpu_to_le32(lower_32_bits(desc_data->skb_dma)); + dma_desc->desc1 = + cpu_to_le32(upper_32_bits(desc_data->skb_dma)); + + /* Update the buffer length */ + dma_desc->desc2 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc2, + TX_NORMAL_DESC2_HL_B1L_POS, + TX_NORMAL_DESC2_HL_B1L_LEN, + desc_data->skb_dma_len); + + /* Set OWN bit */ + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_OWN_POS, + TX_NORMAL_DESC3_OWN_LEN, 1); + + /* Mark it as NORMAL descriptor */ + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_CTXT_POS, + TX_NORMAL_DESC3_CTXT_LEN, 0); + + /* Enable HW CSUM */ + if (csum) + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_CIC_POS, + TX_NORMAL_DESC3_CIC_LEN, + 0x3); + } + + /* Set LAST bit for the last descriptor */ + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_LD_POS, + TX_NORMAL_DESC3_LD_LEN, 1); + + /* Set IC bit based on Tx coalescing settings */ + if (tx_set_ic) + dma_desc->desc2 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc2, + TX_NORMAL_DESC2_IC_POS, + TX_NORMAL_DESC2_IC_LEN, 1); + + /* Save the Tx info to report back during cleanup */ + desc_data->tx.packets = pkt_info->tx_packets; + desc_data->tx.bytes = pkt_info->tx_bytes; + + /* In case the Tx DMA engine is running, make sure everything + * is written to the descriptor(s) before setting the OWN bit + * for the first descriptor + */ + dma_wmb(); + + /* Set OWN bit for the first descriptor */ + desc_data = XLGMAC_GET_DESC_DATA(ring, start_index); + dma_desc = desc_data->dma_desc; + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + TX_NORMAL_DESC3_OWN_POS, + TX_NORMAL_DESC3_OWN_LEN, 1); + + if (netif_msg_tx_queued(pdata)) + xlgmac_dump_tx_desc(pdata, ring, start_index, + pkt_info->desc_count, 1); + + /* Make sure ownership is written to the descriptor */ + smp_wmb(); + + ring->cur = cur_index + 1; + if (!pkt_info->skb->xmit_more || + netif_xmit_stopped(netdev_get_tx_queue(pdata->netdev, + channel->queue_index))) + xlgmac_tx_start_xmit(channel, ring); + else + ring->tx.xmit_more = 1; + + XLGMAC_PR("%s: descriptors %u to %u written\n", + channel->name, start_index & (ring->dma_desc_count - 1), + (ring->cur - 1) & (ring->dma_desc_count - 1)); +} + +static void xlgmac_get_rx_tstamp(struct xlgmac_pkt_info *pkt_info, + struct xlgmac_dma_desc *dma_desc) +{ + u32 tsa, tsd; + u64 nsec; + + tsa = XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + RX_CONTEXT_DESC3_TSA_POS, + RX_CONTEXT_DESC3_TSA_LEN); + tsd = XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + RX_CONTEXT_DESC3_TSD_POS, + RX_CONTEXT_DESC3_TSD_LEN); + if (tsa && !tsd) { + nsec = le32_to_cpu(dma_desc->desc1); + nsec <<= 32; + nsec |= le32_to_cpu(dma_desc->desc0); + if (nsec != 0xffffffffffffffffULL) { + pkt_info->rx_tstamp = nsec; + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_RX_TSTAMP_POS, + RX_PACKET_ATTRIBUTES_RX_TSTAMP_LEN, + 1); + } + } +} + +static void xlgmac_tx_desc_reset(struct xlgmac_desc_data *desc_data) +{ + struct xlgmac_dma_desc *dma_desc = desc_data->dma_desc; + + /* Reset the Tx descriptor + * Set buffer 1 (lo) address to zero + * Set buffer 1 (hi) address to zero + * Reset all other control bits (IC, TTSE, B2L & B1L) + * Reset all other control bits (OWN, CTXT, FD, LD, CPC, CIC, etc) + */ + dma_desc->desc0 = 0; + dma_desc->desc1 = 0; + dma_desc->desc2 = 0; + dma_desc->desc3 = 0; + + /* Make sure ownership is written to the descriptor */ + dma_wmb(); +} + +static void xlgmac_tx_desc_init(struct xlgmac_channel *channel) +{ + struct xlgmac_ring *ring = channel->tx_ring; + struct xlgmac_desc_data *desc_data; + int start_index = ring->cur; + int i; + + /* Initialze all descriptors */ + for (i = 0; i < ring->dma_desc_count; i++) { + desc_data = XLGMAC_GET_DESC_DATA(ring, i); + + /* Initialize Tx descriptor */ + xlgmac_tx_desc_reset(desc_data); + } + + /* Update the total number of Tx descriptors */ + writel(ring->dma_desc_count - 1, XLGMAC_DMA_REG(channel, DMA_CH_TDRLR)); + + /* Update the starting address of descriptor ring */ + desc_data = XLGMAC_GET_DESC_DATA(ring, start_index); + writel(upper_32_bits(desc_data->dma_desc_addr), + XLGMAC_DMA_REG(channel, DMA_CH_TDLR_HI)); + writel(lower_32_bits(desc_data->dma_desc_addr), + XLGMAC_DMA_REG(channel, DMA_CH_TDLR_LO)); +} + +static void xlgmac_rx_desc_reset(struct xlgmac_pdata *pdata, + struct xlgmac_desc_data *desc_data, + unsigned int index) +{ + struct xlgmac_dma_desc *dma_desc = desc_data->dma_desc; + unsigned int rx_frames = pdata->rx_frames; + unsigned int rx_usecs = pdata->rx_usecs; + dma_addr_t hdr_dma, buf_dma; + unsigned int inte; + + if (!rx_usecs && !rx_frames) { + /* No coalescing, interrupt for every descriptor */ + inte = 1; + } else { + /* Set interrupt based on Rx frame coalescing setting */ + if (rx_frames && !((index + 1) % rx_frames)) + inte = 1; + else + inte = 0; + } + + /* Reset the Rx descriptor + * Set buffer 1 (lo) address to header dma address (lo) + * Set buffer 1 (hi) address to header dma address (hi) + * Set buffer 2 (lo) address to buffer dma address (lo) + * Set buffer 2 (hi) address to buffer dma address (hi) and + * set control bits OWN and INTE + */ + hdr_dma = desc_data->rx.hdr.dma_base + desc_data->rx.hdr.dma_off; + buf_dma = desc_data->rx.buf.dma_base + desc_data->rx.buf.dma_off; + dma_desc->desc0 = cpu_to_le32(lower_32_bits(hdr_dma)); + dma_desc->desc1 = cpu_to_le32(upper_32_bits(hdr_dma)); + dma_desc->desc2 = cpu_to_le32(lower_32_bits(buf_dma)); + dma_desc->desc3 = cpu_to_le32(upper_32_bits(buf_dma)); + + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + RX_NORMAL_DESC3_INTE_POS, + RX_NORMAL_DESC3_INTE_LEN, + inte); + + /* Since the Rx DMA engine is likely running, make sure everything + * is written to the descriptor(s) before setting the OWN bit + * for the descriptor + */ + dma_wmb(); + + dma_desc->desc3 = XLGMAC_SET_REG_BITS_LE( + dma_desc->desc3, + RX_NORMAL_DESC3_OWN_POS, + RX_NORMAL_DESC3_OWN_LEN, + 1); + + /* Make sure ownership is written to the descriptor */ + dma_wmb(); +} + +static void xlgmac_rx_desc_init(struct xlgmac_channel *channel) +{ + struct xlgmac_pdata *pdata = channel->pdata; + struct xlgmac_ring *ring = channel->rx_ring; + unsigned int start_index = ring->cur; + struct xlgmac_desc_data *desc_data; + unsigned int i; + + /* Initialize all descriptors */ + for (i = 0; i < ring->dma_desc_count; i++) { + desc_data = XLGMAC_GET_DESC_DATA(ring, i); + + /* Initialize Rx descriptor */ + xlgmac_rx_desc_reset(pdata, desc_data, i); + } + + /* Update the total number of Rx descriptors */ + writel(ring->dma_desc_count - 1, XLGMAC_DMA_REG(channel, DMA_CH_RDRLR)); + + /* Update the starting address of descriptor ring */ + desc_data = XLGMAC_GET_DESC_DATA(ring, start_index); + writel(upper_32_bits(desc_data->dma_desc_addr), + XLGMAC_DMA_REG(channel, DMA_CH_RDLR_HI)); + writel(lower_32_bits(desc_data->dma_desc_addr), + XLGMAC_DMA_REG(channel, DMA_CH_RDLR_LO)); + + /* Update the Rx Descriptor Tail Pointer */ + desc_data = XLGMAC_GET_DESC_DATA(ring, start_index + + ring->dma_desc_count - 1); + writel(lower_32_bits(desc_data->dma_desc_addr), + XLGMAC_DMA_REG(channel, DMA_CH_RDTR_LO)); +} + +static int xlgmac_is_context_desc(struct xlgmac_dma_desc *dma_desc) +{ + /* Rx and Tx share CTXT bit, so check TDES3.CTXT bit */ + return XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + TX_NORMAL_DESC3_CTXT_POS, + TX_NORMAL_DESC3_CTXT_LEN); +} + +static int xlgmac_is_last_desc(struct xlgmac_dma_desc *dma_desc) +{ + /* Rx and Tx share LD bit, so check TDES3.LD bit */ + return XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + TX_NORMAL_DESC3_LD_POS, + TX_NORMAL_DESC3_LD_LEN); +} + +static int xlgmac_disable_tx_flow_control(struct xlgmac_pdata *pdata) +{ + unsigned int max_q_count, q_count; + unsigned int reg, regval; + unsigned int i; + + /* Clear MTL flow control */ + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_EHFC_POS, + MTL_Q_RQOMR_EHFC_LEN, 0); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } + + /* Clear MAC flow control */ + max_q_count = XLGMAC_MAX_FLOW_CONTROL_QUEUES; + q_count = min_t(unsigned int, pdata->tx_q_count, max_q_count); + reg = MAC_Q0TFCR; + for (i = 0; i < q_count; i++) { + regval = readl(pdata->mac_regs + reg); + regval = XLGMAC_SET_REG_BITS(regval, + MAC_Q0TFCR_TFE_POS, + MAC_Q0TFCR_TFE_LEN, + 0); + writel(regval, pdata->mac_regs + reg); + + reg += MAC_QTFCR_INC; + } + + return 0; +} + +static int xlgmac_enable_tx_flow_control(struct xlgmac_pdata *pdata) +{ + unsigned int max_q_count, q_count; + unsigned int reg, regval; + unsigned int i; + + /* Set MTL flow control */ + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_EHFC_POS, + MTL_Q_RQOMR_EHFC_LEN, 1); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } + + /* Set MAC flow control */ + max_q_count = XLGMAC_MAX_FLOW_CONTROL_QUEUES; + q_count = min_t(unsigned int, pdata->tx_q_count, max_q_count); + reg = MAC_Q0TFCR; + for (i = 0; i < q_count; i++) { + regval = readl(pdata->mac_regs + reg); + + /* Enable transmit flow control */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_Q0TFCR_TFE_POS, + MAC_Q0TFCR_TFE_LEN, 1); + /* Set pause time */ + regval = XLGMAC_SET_REG_BITS(regval, MAC_Q0TFCR_PT_POS, + MAC_Q0TFCR_PT_LEN, 0xffff); + + writel(regval, pdata->mac_regs + reg); + + reg += MAC_QTFCR_INC; + } + + return 0; +} + +static int xlgmac_disable_rx_flow_control(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = readl(pdata->mac_regs + MAC_RFCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RFCR_RFE_POS, + MAC_RFCR_RFE_LEN, 0); + writel(regval, pdata->mac_regs + MAC_RFCR); + + return 0; +} + +static int xlgmac_enable_rx_flow_control(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = readl(pdata->mac_regs + MAC_RFCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RFCR_RFE_POS, + MAC_RFCR_RFE_LEN, 1); + writel(regval, pdata->mac_regs + MAC_RFCR); + + return 0; +} + +static int xlgmac_config_tx_flow_control(struct xlgmac_pdata *pdata) +{ + if (pdata->tx_pause) + xlgmac_enable_tx_flow_control(pdata); + else + xlgmac_disable_tx_flow_control(pdata); + + return 0; +} + +static int xlgmac_config_rx_flow_control(struct xlgmac_pdata *pdata) +{ + if (pdata->rx_pause) + xlgmac_enable_rx_flow_control(pdata); + else + xlgmac_disable_rx_flow_control(pdata); + + return 0; +} + +static int xlgmac_config_rx_coalesce(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->rx_ring) + break; + + regval = readl(XLGMAC_DMA_REG(channel, DMA_CH_RIWT)); + regval = XLGMAC_SET_REG_BITS(regval, DMA_CH_RIWT_RWT_POS, + DMA_CH_RIWT_RWT_LEN, + pdata->rx_riwt); + writel(regval, XLGMAC_DMA_REG(channel, DMA_CH_RIWT)); + } + + return 0; +} + +static void xlgmac_config_flow_control(struct xlgmac_pdata *pdata) +{ + xlgmac_config_tx_flow_control(pdata); + xlgmac_config_rx_flow_control(pdata); +} + +static void xlgmac_config_rx_fep_enable(struct xlgmac_pdata *pdata) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_FEP_POS, + MTL_Q_RQOMR_FEP_LEN, 1); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } +} + +static void xlgmac_config_rx_fup_enable(struct xlgmac_pdata *pdata) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_FUP_POS, + MTL_Q_RQOMR_FUP_LEN, 1); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } +} + +static int xlgmac_config_tx_coalesce(struct xlgmac_pdata *pdata) +{ + return 0; +} + +static void xlgmac_config_rx_buffer_size(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->rx_ring) + break; + + regval = readl(XLGMAC_DMA_REG(channel, DMA_CH_RCR)); + regval = XLGMAC_SET_REG_BITS(regval, DMA_CH_RCR_RBSZ_POS, + DMA_CH_RCR_RBSZ_LEN, + pdata->rx_buf_size); + writel(regval, XLGMAC_DMA_REG(channel, DMA_CH_RCR)); + } +} + +static void xlgmac_config_tso_mode(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + break; + + if (pdata->hw_feat.tso) { + regval = readl(XLGMAC_DMA_REG(channel, DMA_CH_TCR)); + regval = XLGMAC_SET_REG_BITS(regval, DMA_CH_TCR_TSE_POS, + DMA_CH_TCR_TSE_LEN, 1); + writel(regval, XLGMAC_DMA_REG(channel, DMA_CH_TCR)); + } + } +} + +static void xlgmac_config_sph_mode(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->rx_ring) + break; + + regval = readl(XLGMAC_DMA_REG(channel, DMA_CH_CR)); + regval = XLGMAC_SET_REG_BITS(regval, DMA_CH_CR_SPH_POS, + DMA_CH_CR_SPH_LEN, 1); + writel(regval, XLGMAC_DMA_REG(channel, DMA_CH_CR)); + } + + regval = readl(pdata->mac_regs + MAC_RCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RCR_HDSMS_POS, + MAC_RCR_HDSMS_LEN, + XLGMAC_SPH_HDSMS_SIZE); + writel(regval, pdata->mac_regs + MAC_RCR); +} + +static unsigned int xlgmac_usec_to_riwt(struct xlgmac_pdata *pdata, + unsigned int usec) +{ + unsigned long rate; + unsigned int ret; + + rate = pdata->sysclk_rate; + + /* Convert the input usec value to the watchdog timer value. Each + * watchdog timer value is equivalent to 256 clock cycles. + * Calculate the required value as: + * ( usec * ( system_clock_mhz / 10^6 ) / 256 + */ + ret = (usec * (rate / 1000000)) / 256; + + return ret; +} + +static unsigned int xlgmac_riwt_to_usec(struct xlgmac_pdata *pdata, + unsigned int riwt) +{ + unsigned long rate; + unsigned int ret; + + rate = pdata->sysclk_rate; + + /* Convert the input watchdog timer value to the usec value. Each + * watchdog timer value is equivalent to 256 clock cycles. + * Calculate the required value as: + * ( riwt * 256 ) / ( system_clock_mhz / 10^6 ) + */ + ret = (riwt * 256) / (rate / 1000000); + + return ret; +} + +static int xlgmac_config_rx_threshold(struct xlgmac_pdata *pdata, + unsigned int val) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_RTC_POS, + MTL_Q_RQOMR_RTC_LEN, val); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } + + return 0; +} + +static void xlgmac_config_mtl_mode(struct xlgmac_pdata *pdata) +{ + unsigned int i; + u32 regval; + + /* Set Tx to weighted round robin scheduling algorithm */ + regval = readl(pdata->mac_regs + MTL_OMR); + regval = XLGMAC_SET_REG_BITS(regval, MTL_OMR_ETSALG_POS, + MTL_OMR_ETSALG_LEN, MTL_ETSALG_WRR); + writel(regval, pdata->mac_regs + MTL_OMR); + + /* Set Tx traffic classes to use WRR algorithm with equal weights */ + for (i = 0; i < pdata->hw_feat.tc_cnt; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_TC_ETSCR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_TC_ETSCR_TSA_POS, + MTL_TC_ETSCR_TSA_LEN, MTL_TSA_ETS); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_TC_ETSCR)); + + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_TC_QWR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_TC_QWR_QW_POS, + MTL_TC_QWR_QW_LEN, 1); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_TC_QWR)); + } + + /* Set Rx to strict priority algorithm */ + regval = readl(pdata->mac_regs + MTL_OMR); + regval = XLGMAC_SET_REG_BITS(regval, MTL_OMR_RAA_POS, + MTL_OMR_RAA_LEN, MTL_RAA_SP); + writel(regval, pdata->mac_regs + MTL_OMR); +} + +static void xlgmac_config_queue_mapping(struct xlgmac_pdata *pdata) +{ + unsigned int ppq, ppq_extra, prio, prio_queues; + unsigned int qptc, qptc_extra, queue; + unsigned int reg, regval; + unsigned int mask; + unsigned int i, j; + + /* Map the MTL Tx Queues to Traffic Classes + * Note: Tx Queues >= Traffic Classes + */ + qptc = pdata->tx_q_count / pdata->hw_feat.tc_cnt; + qptc_extra = pdata->tx_q_count % pdata->hw_feat.tc_cnt; + + for (i = 0, queue = 0; i < pdata->hw_feat.tc_cnt; i++) { + for (j = 0; j < qptc; j++) { + netif_dbg(pdata, drv, pdata->netdev, + "TXq%u mapped to TC%u\n", queue, i); + regval = readl(XLGMAC_MTL_REG(pdata, queue, + MTL_Q_TQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, + MTL_Q_TQOMR_Q2TCMAP_POS, + MTL_Q_TQOMR_Q2TCMAP_LEN, + i); + writel(regval, XLGMAC_MTL_REG(pdata, queue, + MTL_Q_TQOMR)); + queue++; + } + + if (i < qptc_extra) { + netif_dbg(pdata, drv, pdata->netdev, + "TXq%u mapped to TC%u\n", queue, i); + regval = readl(XLGMAC_MTL_REG(pdata, queue, + MTL_Q_TQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, + MTL_Q_TQOMR_Q2TCMAP_POS, + MTL_Q_TQOMR_Q2TCMAP_LEN, + i); + writel(regval, XLGMAC_MTL_REG(pdata, queue, + MTL_Q_TQOMR)); + queue++; + } + } + + /* Map the 8 VLAN priority values to available MTL Rx queues */ + prio_queues = min_t(unsigned int, IEEE_8021QAZ_MAX_TCS, + pdata->rx_q_count); + ppq = IEEE_8021QAZ_MAX_TCS / prio_queues; + ppq_extra = IEEE_8021QAZ_MAX_TCS % prio_queues; + + reg = MAC_RQC2R; + regval = 0; + for (i = 0, prio = 0; i < prio_queues;) { + mask = 0; + for (j = 0; j < ppq; j++) { + netif_dbg(pdata, drv, pdata->netdev, + "PRIO%u mapped to RXq%u\n", prio, i); + mask |= (1 << prio); + prio++; + } + + if (i < ppq_extra) { + netif_dbg(pdata, drv, pdata->netdev, + "PRIO%u mapped to RXq%u\n", prio, i); + mask |= (1 << prio); + prio++; + } + + regval |= (mask << ((i++ % MAC_RQC2_Q_PER_REG) << 3)); + + if ((i % MAC_RQC2_Q_PER_REG) && (i != prio_queues)) + continue; + + writel(regval, pdata->mac_regs + reg); + reg += MAC_RQC2_INC; + regval = 0; + } + + /* Configure one to one, MTL Rx queue to DMA Rx channel mapping + * ie Q0 <--> CH0, Q1 <--> CH1 ... Q11 <--> CH11 + */ + reg = MTL_RQDCM0R; + regval = readl(pdata->mac_regs + reg); + regval |= (MTL_RQDCM0R_Q0MDMACH | MTL_RQDCM0R_Q1MDMACH | + MTL_RQDCM0R_Q2MDMACH | MTL_RQDCM0R_Q3MDMACH); + writel(regval, pdata->mac_regs + reg); + + reg += MTL_RQDCM_INC; + regval = readl(pdata->mac_regs + reg); + regval |= (MTL_RQDCM1R_Q4MDMACH | MTL_RQDCM1R_Q5MDMACH | + MTL_RQDCM1R_Q6MDMACH | MTL_RQDCM1R_Q7MDMACH); + writel(regval, pdata->mac_regs + reg); + + reg += MTL_RQDCM_INC; + regval = readl(pdata->mac_regs + reg); + regval |= (MTL_RQDCM2R_Q8MDMACH | MTL_RQDCM2R_Q9MDMACH | + MTL_RQDCM2R_Q10MDMACH | MTL_RQDCM2R_Q11MDMACH); + writel(regval, pdata->mac_regs + reg); +} + +static unsigned int xlgmac_calculate_per_queue_fifo( + unsigned int fifo_size, + unsigned int queue_count) +{ + unsigned int q_fifo_size; + unsigned int p_fifo; + + /* Calculate the configured fifo size */ + q_fifo_size = 1 << (fifo_size + 7); + + /* The configured value may not be the actual amount of fifo RAM */ + q_fifo_size = min_t(unsigned int, XLGMAC_MAX_FIFO, q_fifo_size); + + q_fifo_size = q_fifo_size / queue_count; + + /* Each increment in the queue fifo size represents 256 bytes of + * fifo, with 0 representing 256 bytes. Distribute the fifo equally + * between the queues. + */ + p_fifo = q_fifo_size / 256; + if (p_fifo) + p_fifo--; + + return p_fifo; +} + +static void xlgmac_config_tx_fifo_size(struct xlgmac_pdata *pdata) +{ + unsigned int fifo_size; + unsigned int i; + u32 regval; + + fifo_size = xlgmac_calculate_per_queue_fifo( + pdata->hw_feat.tx_fifo_size, + pdata->tx_q_count); + + for (i = 0; i < pdata->tx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TQS_POS, + MTL_Q_TQOMR_TQS_LEN, fifo_size); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + } + + netif_info(pdata, drv, pdata->netdev, + "%d Tx hardware queues, %d byte fifo per queue\n", + pdata->tx_q_count, ((fifo_size + 1) * 256)); +} + +static void xlgmac_config_rx_fifo_size(struct xlgmac_pdata *pdata) +{ + unsigned int fifo_size; + unsigned int i; + u32 regval; + + fifo_size = xlgmac_calculate_per_queue_fifo( + pdata->hw_feat.rx_fifo_size, + pdata->rx_q_count); + + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_RQS_POS, + MTL_Q_RQOMR_RQS_LEN, fifo_size); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } + + netif_info(pdata, drv, pdata->netdev, + "%d Rx hardware queues, %d byte fifo per queue\n", + pdata->rx_q_count, ((fifo_size + 1) * 256)); +} + +static void xlgmac_config_flow_control_threshold(struct xlgmac_pdata *pdata) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_RQFCR)); + /* Activate flow control when less than 4k left in fifo */ + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_RQFCR_RFA_POS, + MTL_Q_RQFCR_RFA_LEN, 2); + /* De-activate flow control when more than 6k left in fifo */ + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_RQFCR_RFD_POS, + MTL_Q_RQFCR_RFD_LEN, 4); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_RQFCR)); + } +} + +static int xlgmac_config_tx_threshold(struct xlgmac_pdata *pdata, + unsigned int val) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->tx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TTC_POS, + MTL_Q_TQOMR_TTC_LEN, val); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + } + + return 0; +} + +static int xlgmac_config_rsf_mode(struct xlgmac_pdata *pdata, + unsigned int val) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_RSF_POS, + MTL_Q_RQOMR_RSF_LEN, val); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } + + return 0; +} + +static int xlgmac_config_tsf_mode(struct xlgmac_pdata *pdata, + unsigned int val) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->tx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TSF_POS, + MTL_Q_TQOMR_TSF_LEN, val); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + } + + return 0; +} + +static int xlgmac_config_osp_mode(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + break; + + regval = readl(XLGMAC_DMA_REG(channel, DMA_CH_TCR)); + regval = XLGMAC_SET_REG_BITS(regval, DMA_CH_TCR_OSP_POS, + DMA_CH_TCR_OSP_LEN, + pdata->tx_osp_mode); + writel(regval, XLGMAC_DMA_REG(channel, DMA_CH_TCR)); + } + + return 0; +} + +static int xlgmac_config_pblx8(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + regval = readl(XLGMAC_DMA_REG(channel, DMA_CH_CR)); + regval = XLGMAC_SET_REG_BITS(regval, DMA_CH_CR_PBLX8_POS, + DMA_CH_CR_PBLX8_LEN, + pdata->pblx8); + writel(regval, XLGMAC_DMA_REG(channel, DMA_CH_CR)); + } + + return 0; +} + +static int xlgmac_get_tx_pbl_val(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = readl(XLGMAC_DMA_REG(pdata->channel_head, DMA_CH_TCR)); + regval = XLGMAC_GET_REG_BITS(regval, DMA_CH_TCR_PBL_POS, + DMA_CH_TCR_PBL_LEN); + return regval; +} + +static int xlgmac_config_tx_pbl_val(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + break; + + regval = readl(XLGMAC_DMA_REG(channel, DMA_CH_TCR)); + regval = XLGMAC_SET_REG_BITS(regval, DMA_CH_TCR_PBL_POS, + DMA_CH_TCR_PBL_LEN, + pdata->tx_pbl); + writel(regval, XLGMAC_DMA_REG(channel, DMA_CH_TCR)); + } + + return 0; +} + +static int xlgmac_get_rx_pbl_val(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = readl(XLGMAC_DMA_REG(pdata->channel_head, DMA_CH_RCR)); + regval = XLGMAC_GET_REG_BITS(regval, DMA_CH_RCR_PBL_POS, + DMA_CH_RCR_PBL_LEN); + return regval; +} + +static int xlgmac_config_rx_pbl_val(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->rx_ring) + break; + + regval = readl(XLGMAC_DMA_REG(channel, DMA_CH_RCR)); + regval = XLGMAC_SET_REG_BITS(regval, DMA_CH_RCR_PBL_POS, + DMA_CH_RCR_PBL_LEN, + pdata->rx_pbl); + writel(regval, XLGMAC_DMA_REG(channel, DMA_CH_RCR)); + } + + return 0; +} + +static u64 xlgmac_mmc_read(struct xlgmac_pdata *pdata, unsigned int reg_lo) +{ + bool read_hi; + u64 val; + + switch (reg_lo) { + /* These registers are always 64 bit */ + case MMC_TXOCTETCOUNT_GB_LO: + case MMC_TXOCTETCOUNT_G_LO: + case MMC_RXOCTETCOUNT_GB_LO: + case MMC_RXOCTETCOUNT_G_LO: + read_hi = true; + break; + + default: + read_hi = false; + } + + val = (u64)readl(pdata->mac_regs + reg_lo); + + if (read_hi) + val |= ((u64)readl(pdata->mac_regs + reg_lo + 4) << 32); + + return val; +} + +static void xlgmac_tx_mmc_int(struct xlgmac_pdata *pdata) +{ + unsigned int mmc_isr = readl(pdata->mac_regs + MMC_TISR); + struct xlgmac_stats *stats = &pdata->stats; + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TXOCTETCOUNT_GB_POS, + MMC_TISR_TXOCTETCOUNT_GB_LEN)) + stats->txoctetcount_gb += + xlgmac_mmc_read(pdata, MMC_TXOCTETCOUNT_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TXFRAMECOUNT_GB_POS, + MMC_TISR_TXFRAMECOUNT_GB_LEN)) + stats->txframecount_gb += + xlgmac_mmc_read(pdata, MMC_TXFRAMECOUNT_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TXBROADCASTFRAMES_G_POS, + MMC_TISR_TXBROADCASTFRAMES_G_LEN)) + stats->txbroadcastframes_g += + xlgmac_mmc_read(pdata, MMC_TXBROADCASTFRAMES_G_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TXMULTICASTFRAMES_G_POS, + MMC_TISR_TXMULTICASTFRAMES_G_LEN)) + stats->txmulticastframes_g += + xlgmac_mmc_read(pdata, MMC_TXMULTICASTFRAMES_G_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TX64OCTETS_GB_POS, + MMC_TISR_TX64OCTETS_GB_LEN)) + stats->tx64octets_gb += + xlgmac_mmc_read(pdata, MMC_TX64OCTETS_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TX65TO127OCTETS_GB_POS, + MMC_TISR_TX65TO127OCTETS_GB_LEN)) + stats->tx65to127octets_gb += + xlgmac_mmc_read(pdata, MMC_TX65TO127OCTETS_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TX128TO255OCTETS_GB_POS, + MMC_TISR_TX128TO255OCTETS_GB_LEN)) + stats->tx128to255octets_gb += + xlgmac_mmc_read(pdata, MMC_TX128TO255OCTETS_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TX256TO511OCTETS_GB_POS, + MMC_TISR_TX256TO511OCTETS_GB_LEN)) + stats->tx256to511octets_gb += + xlgmac_mmc_read(pdata, MMC_TX256TO511OCTETS_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TX512TO1023OCTETS_GB_POS, + MMC_TISR_TX512TO1023OCTETS_GB_LEN)) + stats->tx512to1023octets_gb += + xlgmac_mmc_read(pdata, MMC_TX512TO1023OCTETS_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TX1024TOMAXOCTETS_GB_POS, + MMC_TISR_TX1024TOMAXOCTETS_GB_LEN)) + stats->tx1024tomaxoctets_gb += + xlgmac_mmc_read(pdata, MMC_TX1024TOMAXOCTETS_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TXUNICASTFRAMES_GB_POS, + MMC_TISR_TXUNICASTFRAMES_GB_LEN)) + stats->txunicastframes_gb += + xlgmac_mmc_read(pdata, MMC_TXUNICASTFRAMES_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TXMULTICASTFRAMES_GB_POS, + MMC_TISR_TXMULTICASTFRAMES_GB_LEN)) + stats->txmulticastframes_gb += + xlgmac_mmc_read(pdata, MMC_TXMULTICASTFRAMES_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TXBROADCASTFRAMES_GB_POS, + MMC_TISR_TXBROADCASTFRAMES_GB_LEN)) + stats->txbroadcastframes_g += + xlgmac_mmc_read(pdata, MMC_TXBROADCASTFRAMES_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TXUNDERFLOWERROR_POS, + MMC_TISR_TXUNDERFLOWERROR_LEN)) + stats->txunderflowerror += + xlgmac_mmc_read(pdata, MMC_TXUNDERFLOWERROR_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TXOCTETCOUNT_G_POS, + MMC_TISR_TXOCTETCOUNT_G_LEN)) + stats->txoctetcount_g += + xlgmac_mmc_read(pdata, MMC_TXOCTETCOUNT_G_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TXFRAMECOUNT_G_POS, + MMC_TISR_TXFRAMECOUNT_G_LEN)) + stats->txframecount_g += + xlgmac_mmc_read(pdata, MMC_TXFRAMECOUNT_G_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TXPAUSEFRAMES_POS, + MMC_TISR_TXPAUSEFRAMES_LEN)) + stats->txpauseframes += + xlgmac_mmc_read(pdata, MMC_TXPAUSEFRAMES_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TXVLANFRAMES_G_POS, + MMC_TISR_TXVLANFRAMES_G_LEN)) + stats->txvlanframes_g += + xlgmac_mmc_read(pdata, MMC_TXVLANFRAMES_G_LO); +} + +static void xlgmac_rx_mmc_int(struct xlgmac_pdata *pdata) +{ + unsigned int mmc_isr = readl(pdata->mac_regs + MMC_RISR); + struct xlgmac_stats *stats = &pdata->stats; + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXFRAMECOUNT_GB_POS, + MMC_RISR_RXFRAMECOUNT_GB_LEN)) + stats->rxframecount_gb += + xlgmac_mmc_read(pdata, MMC_RXFRAMECOUNT_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXOCTETCOUNT_GB_POS, + MMC_RISR_RXOCTETCOUNT_GB_LEN)) + stats->rxoctetcount_gb += + xlgmac_mmc_read(pdata, MMC_RXOCTETCOUNT_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXOCTETCOUNT_G_POS, + MMC_RISR_RXOCTETCOUNT_G_LEN)) + stats->rxoctetcount_g += + xlgmac_mmc_read(pdata, MMC_RXOCTETCOUNT_G_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXBROADCASTFRAMES_G_POS, + MMC_RISR_RXBROADCASTFRAMES_G_LEN)) + stats->rxbroadcastframes_g += + xlgmac_mmc_read(pdata, MMC_RXBROADCASTFRAMES_G_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXMULTICASTFRAMES_G_POS, + MMC_RISR_RXMULTICASTFRAMES_G_LEN)) + stats->rxmulticastframes_g += + xlgmac_mmc_read(pdata, MMC_RXMULTICASTFRAMES_G_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXCRCERROR_POS, + MMC_RISR_RXCRCERROR_LEN)) + stats->rxcrcerror += + xlgmac_mmc_read(pdata, MMC_RXCRCERROR_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXRUNTERROR_POS, + MMC_RISR_RXRUNTERROR_LEN)) + stats->rxrunterror += + xlgmac_mmc_read(pdata, MMC_RXRUNTERROR); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXJABBERERROR_POS, + MMC_RISR_RXJABBERERROR_LEN)) + stats->rxjabbererror += + xlgmac_mmc_read(pdata, MMC_RXJABBERERROR); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXUNDERSIZE_G_POS, + MMC_RISR_RXUNDERSIZE_G_LEN)) + stats->rxundersize_g += + xlgmac_mmc_read(pdata, MMC_RXUNDERSIZE_G); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXOVERSIZE_G_POS, + MMC_RISR_RXOVERSIZE_G_LEN)) + stats->rxoversize_g += + xlgmac_mmc_read(pdata, MMC_RXOVERSIZE_G); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RX64OCTETS_GB_POS, + MMC_RISR_RX64OCTETS_GB_LEN)) + stats->rx64octets_gb += + xlgmac_mmc_read(pdata, MMC_RX64OCTETS_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RX65TO127OCTETS_GB_POS, + MMC_RISR_RX65TO127OCTETS_GB_LEN)) + stats->rx65to127octets_gb += + xlgmac_mmc_read(pdata, MMC_RX65TO127OCTETS_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RX128TO255OCTETS_GB_POS, + MMC_RISR_RX128TO255OCTETS_GB_LEN)) + stats->rx128to255octets_gb += + xlgmac_mmc_read(pdata, MMC_RX128TO255OCTETS_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RX256TO511OCTETS_GB_POS, + MMC_RISR_RX256TO511OCTETS_GB_LEN)) + stats->rx256to511octets_gb += + xlgmac_mmc_read(pdata, MMC_RX256TO511OCTETS_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RX512TO1023OCTETS_GB_POS, + MMC_RISR_RX512TO1023OCTETS_GB_LEN)) + stats->rx512to1023octets_gb += + xlgmac_mmc_read(pdata, MMC_RX512TO1023OCTETS_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RX1024TOMAXOCTETS_GB_POS, + MMC_RISR_RX1024TOMAXOCTETS_GB_LEN)) + stats->rx1024tomaxoctets_gb += + xlgmac_mmc_read(pdata, MMC_RX1024TOMAXOCTETS_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXUNICASTFRAMES_G_POS, + MMC_RISR_RXUNICASTFRAMES_G_LEN)) + stats->rxunicastframes_g += + xlgmac_mmc_read(pdata, MMC_RXUNICASTFRAMES_G_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXLENGTHERROR_POS, + MMC_RISR_RXLENGTHERROR_LEN)) + stats->rxlengtherror += + xlgmac_mmc_read(pdata, MMC_RXLENGTHERROR_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXOUTOFRANGETYPE_POS, + MMC_RISR_RXOUTOFRANGETYPE_LEN)) + stats->rxoutofrangetype += + xlgmac_mmc_read(pdata, MMC_RXOUTOFRANGETYPE_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXPAUSEFRAMES_POS, + MMC_RISR_RXPAUSEFRAMES_LEN)) + stats->rxpauseframes += + xlgmac_mmc_read(pdata, MMC_RXPAUSEFRAMES_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXFIFOOVERFLOW_POS, + MMC_RISR_RXFIFOOVERFLOW_LEN)) + stats->rxfifooverflow += + xlgmac_mmc_read(pdata, MMC_RXFIFOOVERFLOW_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXVLANFRAMES_GB_POS, + MMC_RISR_RXVLANFRAMES_GB_LEN)) + stats->rxvlanframes_gb += + xlgmac_mmc_read(pdata, MMC_RXVLANFRAMES_GB_LO); + + if (XLGMAC_GET_REG_BITS(mmc_isr, + MMC_RISR_RXWATCHDOGERROR_POS, + MMC_RISR_RXWATCHDOGERROR_LEN)) + stats->rxwatchdogerror += + xlgmac_mmc_read(pdata, MMC_RXWATCHDOGERROR); +} + +static void xlgmac_read_mmc_stats(struct xlgmac_pdata *pdata) +{ + struct xlgmac_stats *stats = &pdata->stats; + u32 regval; + + /* Freeze counters */ + regval = readl(pdata->mac_regs + MMC_CR); + regval = XLGMAC_SET_REG_BITS(regval, MMC_CR_MCF_POS, + MMC_CR_MCF_LEN, 1); + writel(regval, pdata->mac_regs + MMC_CR); + + stats->txoctetcount_gb += + xlgmac_mmc_read(pdata, MMC_TXOCTETCOUNT_GB_LO); + + stats->txframecount_gb += + xlgmac_mmc_read(pdata, MMC_TXFRAMECOUNT_GB_LO); + + stats->txbroadcastframes_g += + xlgmac_mmc_read(pdata, MMC_TXBROADCASTFRAMES_G_LO); + + stats->txmulticastframes_g += + xlgmac_mmc_read(pdata, MMC_TXMULTICASTFRAMES_G_LO); + + stats->tx64octets_gb += + xlgmac_mmc_read(pdata, MMC_TX64OCTETS_GB_LO); + + stats->tx65to127octets_gb += + xlgmac_mmc_read(pdata, MMC_TX65TO127OCTETS_GB_LO); + + stats->tx128to255octets_gb += + xlgmac_mmc_read(pdata, MMC_TX128TO255OCTETS_GB_LO); + + stats->tx256to511octets_gb += + xlgmac_mmc_read(pdata, MMC_TX256TO511OCTETS_GB_LO); + + stats->tx512to1023octets_gb += + xlgmac_mmc_read(pdata, MMC_TX512TO1023OCTETS_GB_LO); + + stats->tx1024tomaxoctets_gb += + xlgmac_mmc_read(pdata, MMC_TX1024TOMAXOCTETS_GB_LO); + + stats->txunicastframes_gb += + xlgmac_mmc_read(pdata, MMC_TXUNICASTFRAMES_GB_LO); + + stats->txmulticastframes_gb += + xlgmac_mmc_read(pdata, MMC_TXMULTICASTFRAMES_GB_LO); + + stats->txbroadcastframes_g += + xlgmac_mmc_read(pdata, MMC_TXBROADCASTFRAMES_GB_LO); + + stats->txunderflowerror += + xlgmac_mmc_read(pdata, MMC_TXUNDERFLOWERROR_LO); + + stats->txoctetcount_g += + xlgmac_mmc_read(pdata, MMC_TXOCTETCOUNT_G_LO); + + stats->txframecount_g += + xlgmac_mmc_read(pdata, MMC_TXFRAMECOUNT_G_LO); + + stats->txpauseframes += + xlgmac_mmc_read(pdata, MMC_TXPAUSEFRAMES_LO); + + stats->txvlanframes_g += + xlgmac_mmc_read(pdata, MMC_TXVLANFRAMES_G_LO); + + stats->rxframecount_gb += + xlgmac_mmc_read(pdata, MMC_RXFRAMECOUNT_GB_LO); + + stats->rxoctetcount_gb += + xlgmac_mmc_read(pdata, MMC_RXOCTETCOUNT_GB_LO); + + stats->rxoctetcount_g += + xlgmac_mmc_read(pdata, MMC_RXOCTETCOUNT_G_LO); + + stats->rxbroadcastframes_g += + xlgmac_mmc_read(pdata, MMC_RXBROADCASTFRAMES_G_LO); + + stats->rxmulticastframes_g += + xlgmac_mmc_read(pdata, MMC_RXMULTICASTFRAMES_G_LO); + + stats->rxcrcerror += + xlgmac_mmc_read(pdata, MMC_RXCRCERROR_LO); + + stats->rxrunterror += + xlgmac_mmc_read(pdata, MMC_RXRUNTERROR); + + stats->rxjabbererror += + xlgmac_mmc_read(pdata, MMC_RXJABBERERROR); + + stats->rxundersize_g += + xlgmac_mmc_read(pdata, MMC_RXUNDERSIZE_G); + + stats->rxoversize_g += + xlgmac_mmc_read(pdata, MMC_RXOVERSIZE_G); + + stats->rx64octets_gb += + xlgmac_mmc_read(pdata, MMC_RX64OCTETS_GB_LO); + + stats->rx65to127octets_gb += + xlgmac_mmc_read(pdata, MMC_RX65TO127OCTETS_GB_LO); + + stats->rx128to255octets_gb += + xlgmac_mmc_read(pdata, MMC_RX128TO255OCTETS_GB_LO); + + stats->rx256to511octets_gb += + xlgmac_mmc_read(pdata, MMC_RX256TO511OCTETS_GB_LO); + + stats->rx512to1023octets_gb += + xlgmac_mmc_read(pdata, MMC_RX512TO1023OCTETS_GB_LO); + + stats->rx1024tomaxoctets_gb += + xlgmac_mmc_read(pdata, MMC_RX1024TOMAXOCTETS_GB_LO); + + stats->rxunicastframes_g += + xlgmac_mmc_read(pdata, MMC_RXUNICASTFRAMES_G_LO); + + stats->rxlengtherror += + xlgmac_mmc_read(pdata, MMC_RXLENGTHERROR_LO); + + stats->rxoutofrangetype += + xlgmac_mmc_read(pdata, MMC_RXOUTOFRANGETYPE_LO); + + stats->rxpauseframes += + xlgmac_mmc_read(pdata, MMC_RXPAUSEFRAMES_LO); + + stats->rxfifooverflow += + xlgmac_mmc_read(pdata, MMC_RXFIFOOVERFLOW_LO); + + stats->rxvlanframes_gb += + xlgmac_mmc_read(pdata, MMC_RXVLANFRAMES_GB_LO); + + stats->rxwatchdogerror += + xlgmac_mmc_read(pdata, MMC_RXWATCHDOGERROR); + + /* Un-freeze counters */ + regval = readl(pdata->mac_regs + MMC_CR); + regval = XLGMAC_SET_REG_BITS(regval, MMC_CR_MCF_POS, + MMC_CR_MCF_LEN, 0); + writel(regval, pdata->mac_regs + MMC_CR); +} + +static void xlgmac_config_mmc(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = readl(pdata->mac_regs + MMC_CR); + /* Set counters to reset on read */ + regval = XLGMAC_SET_REG_BITS(regval, MMC_CR_ROR_POS, + MMC_CR_ROR_LEN, 1); + /* Reset the counters */ + regval = XLGMAC_SET_REG_BITS(regval, MMC_CR_CR_POS, + MMC_CR_CR_LEN, 1); + writel(regval, pdata->mac_regs + MMC_CR); +} + +static int xlgmac_write_rss_reg(struct xlgmac_pdata *pdata, unsigned int type, + unsigned int index, unsigned int val) +{ + unsigned int wait; + int ret = 0; + u32 regval; + + mutex_lock(&pdata->rss_mutex); + + regval = XLGMAC_GET_REG_BITS(readl(pdata->mac_regs + MAC_RSSAR), + MAC_RSSAR_OB_POS, MAC_RSSAR_OB_LEN); + if (regval) { + ret = -EBUSY; + goto unlock; + } + + writel(val, pdata->mac_regs + MAC_RSSDR); + + regval = readl(pdata->mac_regs + MAC_RSSAR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RSSAR_RSSIA_POS, + MAC_RSSAR_RSSIA_LEN, index); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RSSAR_ADDRT_POS, + MAC_RSSAR_ADDRT_LEN, type); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RSSAR_CT_POS, + MAC_RSSAR_CT_LEN, 0); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RSSAR_OB_POS, + MAC_RSSAR_OB_LEN, 1); + writel(regval, pdata->mac_regs + MAC_RSSAR); + + wait = 1000; + while (wait--) { + regval = XLGMAC_GET_REG_BITS(readl(pdata->mac_regs + MAC_RSSAR), + MAC_RSSAR_OB_POS, + MAC_RSSAR_OB_LEN); + if (!regval) + goto unlock; + + usleep_range(1000, 1500); + } + + ret = -EBUSY; + +unlock: + mutex_unlock(&pdata->rss_mutex); + + return ret; +} + +static int xlgmac_write_rss_hash_key(struct xlgmac_pdata *pdata) +{ + unsigned int key_regs = sizeof(pdata->rss_key) / sizeof(u32); + unsigned int *key = (unsigned int *)&pdata->rss_key; + int ret; + + while (key_regs--) { + ret = xlgmac_write_rss_reg(pdata, XLGMAC_RSS_HASH_KEY_TYPE, + key_regs, *key++); + if (ret) + return ret; + } + + return 0; +} + +static int xlgmac_write_rss_lookup_table(struct xlgmac_pdata *pdata) +{ + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(pdata->rss_table); i++) { + ret = xlgmac_write_rss_reg(pdata, + XLGMAC_RSS_LOOKUP_TABLE_TYPE, i, + pdata->rss_table[i]); + if (ret) + return ret; + } + + return 0; +} + +static int xlgmac_set_rss_hash_key(struct xlgmac_pdata *pdata, const u8 *key) +{ + memcpy(pdata->rss_key, key, sizeof(pdata->rss_key)); + + return xlgmac_write_rss_hash_key(pdata); +} + +static int xlgmac_set_rss_lookup_table(struct xlgmac_pdata *pdata, + const u32 *table) +{ + unsigned int i; + u32 tval; + + for (i = 0; i < ARRAY_SIZE(pdata->rss_table); i++) { + tval = table[i]; + pdata->rss_table[i] = XLGMAC_SET_REG_BITS( + pdata->rss_table[i], + MAC_RSSDR_DMCH_POS, + MAC_RSSDR_DMCH_LEN, + tval); + } + + return xlgmac_write_rss_lookup_table(pdata); +} + +static int xlgmac_enable_rss(struct xlgmac_pdata *pdata) +{ + u32 regval; + int ret; + + if (!pdata->hw_feat.rss) + return -EOPNOTSUPP; + + /* Program the hash key */ + ret = xlgmac_write_rss_hash_key(pdata); + if (ret) + return ret; + + /* Program the lookup table */ + ret = xlgmac_write_rss_lookup_table(pdata); + if (ret) + return ret; + + /* Set the RSS options */ + writel(pdata->rss_options, pdata->mac_regs + MAC_RSSCR); + + /* Enable RSS */ + regval = readl(pdata->mac_regs + MAC_RSSCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RSSCR_RSSE_POS, + MAC_RSSCR_RSSE_LEN, 1); + writel(regval, pdata->mac_regs + MAC_RSSCR); + + return 0; +} + +static int xlgmac_disable_rss(struct xlgmac_pdata *pdata) +{ + u32 regval; + + if (!pdata->hw_feat.rss) + return -EOPNOTSUPP; + + regval = readl(pdata->mac_regs + MAC_RSSCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_RSSCR_RSSE_POS, + MAC_RSSCR_RSSE_LEN, 0); + writel(regval, pdata->mac_regs + MAC_RSSCR); + + return 0; +} + +static void xlgmac_config_rss(struct xlgmac_pdata *pdata) +{ + int ret; + + if (!pdata->hw_feat.rss) + return; + + if (pdata->netdev->features & NETIF_F_RXHASH) + ret = xlgmac_enable_rss(pdata); + else + ret = xlgmac_disable_rss(pdata); + + if (ret) + netdev_err(pdata->netdev, + "error configuring RSS, RSS disabled\n"); +} + +static void xlgmac_enable_dma_interrupts(struct xlgmac_pdata *pdata) +{ + unsigned int dma_ch_isr, dma_ch_ier; + struct xlgmac_channel *channel; + unsigned int i; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + /* Clear all the interrupts which are set */ + dma_ch_isr = readl(XLGMAC_DMA_REG(channel, DMA_CH_SR)); + writel(dma_ch_isr, XLGMAC_DMA_REG(channel, DMA_CH_SR)); + + /* Clear all interrupt enable bits */ + dma_ch_ier = 0; + + /* Enable following interrupts + * NIE - Normal Interrupt Summary Enable + * AIE - Abnormal Interrupt Summary Enable + * FBEE - Fatal Bus Error Enable + */ + dma_ch_ier = XLGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_NIE_POS, + DMA_CH_IER_NIE_LEN, 1); + dma_ch_ier = XLGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_AIE_POS, + DMA_CH_IER_AIE_LEN, 1); + dma_ch_ier = XLGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_FBEE_POS, + DMA_CH_IER_FBEE_LEN, 1); + + if (channel->tx_ring) { + /* Enable the following Tx interrupts + * TIE - Transmit Interrupt Enable (unless using + * per channel interrupts) + */ + if (!pdata->per_channel_irq) + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, + DMA_CH_IER_TIE_POS, + DMA_CH_IER_TIE_LEN, + 1); + } + if (channel->rx_ring) { + /* Enable following Rx interrupts + * RBUE - Receive Buffer Unavailable Enable + * RIE - Receive Interrupt Enable (unless using + * per channel interrupts) + */ + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, + DMA_CH_IER_RBUE_POS, + DMA_CH_IER_RBUE_LEN, + 1); + if (!pdata->per_channel_irq) + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, + DMA_CH_IER_RIE_POS, + DMA_CH_IER_RIE_LEN, + 1); + } + + writel(dma_ch_isr, XLGMAC_DMA_REG(channel, DMA_CH_IER)); + } +} + +static void xlgmac_enable_mtl_interrupts(struct xlgmac_pdata *pdata) +{ + unsigned int q_count, i; + unsigned int mtl_q_isr; + + q_count = max(pdata->hw_feat.tx_q_cnt, pdata->hw_feat.rx_q_cnt); + for (i = 0; i < q_count; i++) { + /* Clear all the interrupts which are set */ + mtl_q_isr = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_ISR)); + writel(mtl_q_isr, XLGMAC_MTL_REG(pdata, i, MTL_Q_ISR)); + + /* No MTL interrupts to be enabled */ + writel(0, XLGMAC_MTL_REG(pdata, i, MTL_Q_IER)); + } +} + +static void xlgmac_enable_mac_interrupts(struct xlgmac_pdata *pdata) +{ + unsigned int mac_ier = 0; + u32 regval; + + /* Enable Timestamp interrupt */ + mac_ier = XLGMAC_SET_REG_BITS(mac_ier, MAC_IER_TSIE_POS, + MAC_IER_TSIE_LEN, 1); + + writel(mac_ier, pdata->mac_regs + MAC_IER); + + /* Enable all counter interrupts */ + regval = readl(pdata->mac_regs + MMC_RIER); + regval = XLGMAC_SET_REG_BITS(regval, MMC_RIER_ALL_INTERRUPTS_POS, + MMC_RIER_ALL_INTERRUPTS_LEN, 0xffffffff); + writel(regval, pdata->mac_regs + MMC_RIER); + regval = readl(pdata->mac_regs + MMC_TIER); + regval = XLGMAC_SET_REG_BITS(regval, MMC_TIER_ALL_INTERRUPTS_POS, + MMC_TIER_ALL_INTERRUPTS_LEN, 0xffffffff); + writel(regval, pdata->mac_regs + MMC_TIER); +} + +static int xlgmac_set_xlgmii_25000_speed(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = XLGMAC_GET_REG_BITS(readl(pdata->mac_regs + MAC_TCR), + MAC_TCR_SS_POS, MAC_TCR_SS_LEN); + if (regval == 0x1) + return 0; + + regval = readl(pdata->mac_regs + MAC_TCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_TCR_SS_POS, + MAC_TCR_SS_LEN, 0x1); + writel(regval, pdata->mac_regs + MAC_TCR); + + return 0; +} + +static int xlgmac_set_xlgmii_40000_speed(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = XLGMAC_GET_REG_BITS(readl(pdata->mac_regs + MAC_TCR), + MAC_TCR_SS_POS, MAC_TCR_SS_LEN); + if (regval == 0) + return 0; + + regval = readl(pdata->mac_regs + MAC_TCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_TCR_SS_POS, + MAC_TCR_SS_LEN, 0); + writel(regval, pdata->mac_regs + MAC_TCR); + + return 0; +} + +static int xlgmac_set_xlgmii_50000_speed(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = XLGMAC_GET_REG_BITS(readl(pdata->mac_regs + MAC_TCR), + MAC_TCR_SS_POS, MAC_TCR_SS_LEN); + if (regval == 0x2) + return 0; + + regval = readl(pdata->mac_regs + MAC_TCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_TCR_SS_POS, + MAC_TCR_SS_LEN, 0x2); + writel(regval, pdata->mac_regs + MAC_TCR); + + return 0; +} + +static int xlgmac_set_xlgmii_100000_speed(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = XLGMAC_GET_REG_BITS(readl(pdata->mac_regs + MAC_TCR), + MAC_TCR_SS_POS, MAC_TCR_SS_LEN); + if (regval == 0x3) + return 0; + + regval = readl(pdata->mac_regs + MAC_TCR); + regval = XLGMAC_SET_REG_BITS(regval, MAC_TCR_SS_POS, + MAC_TCR_SS_LEN, 0x3); + writel(regval, pdata->mac_regs + MAC_TCR); + + return 0; +} + +static void xlgmac_config_mac_speed(struct xlgmac_pdata *pdata) +{ + switch (pdata->phy_speed) { + case SPEED_100000: + xlgmac_set_xlgmii_100000_speed(pdata); + break; + + case SPEED_50000: + xlgmac_set_xlgmii_50000_speed(pdata); + break; + + case SPEED_40000: + xlgmac_set_xlgmii_40000_speed(pdata); + break; + + case SPEED_25000: + xlgmac_set_xlgmii_25000_speed(pdata); + break; + } +} + +static int xlgmac_dev_read(struct xlgmac_channel *channel) +{ + struct xlgmac_pdata *pdata = channel->pdata; + struct xlgmac_ring *ring = channel->rx_ring; + struct net_device *netdev = pdata->netdev; + struct xlgmac_desc_data *desc_data; + struct xlgmac_dma_desc *dma_desc; + struct xlgmac_pkt_info *pkt_info; + unsigned int err, etlt, l34t; + + desc_data = XLGMAC_GET_DESC_DATA(ring, ring->cur); + dma_desc = desc_data->dma_desc; + pkt_info = &ring->pkt_info; + + /* Check for data availability */ + if (XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_OWN_POS, + RX_NORMAL_DESC3_OWN_LEN)) + return 1; + + /* Make sure descriptor fields are read after reading the OWN bit */ + dma_rmb(); + + if (netif_msg_rx_status(pdata)) + xlgmac_dump_rx_desc(pdata, ring, ring->cur); + + if (XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_CTXT_POS, + RX_NORMAL_DESC3_CTXT_LEN)) { + /* Timestamp Context Descriptor */ + xlgmac_get_rx_tstamp(pkt_info, dma_desc); + + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_CONTEXT_POS, + RX_PACKET_ATTRIBUTES_CONTEXT_LEN, + 1); + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_POS, + RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_LEN, + 0); + return 0; + } + + /* Normal Descriptor, be sure Context Descriptor bit is off */ + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_CONTEXT_POS, + RX_PACKET_ATTRIBUTES_CONTEXT_LEN, + 0); + + /* Indicate if a Context Descriptor is next */ + if (XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_CDA_POS, + RX_NORMAL_DESC3_CDA_LEN)) + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_POS, + RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_LEN, + 1); + + /* Get the header length */ + if (XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_FD_POS, + RX_NORMAL_DESC3_FD_LEN)) { + desc_data->rx.hdr_len = XLGMAC_GET_REG_BITS_LE(dma_desc->desc2, + RX_NORMAL_DESC2_HL_POS, + RX_NORMAL_DESC2_HL_LEN); + if (desc_data->rx.hdr_len) + pdata->stats.rx_split_header_packets++; + } + + /* Get the RSS hash */ + if (XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_RSV_POS, + RX_NORMAL_DESC3_RSV_LEN)) { + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_RSS_HASH_POS, + RX_PACKET_ATTRIBUTES_RSS_HASH_LEN, + 1); + + pkt_info->rss_hash = le32_to_cpu(dma_desc->desc1); + + l34t = XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_L34T_POS, + RX_NORMAL_DESC3_L34T_LEN); + switch (l34t) { + case RX_DESC3_L34T_IPV4_TCP: + case RX_DESC3_L34T_IPV4_UDP: + case RX_DESC3_L34T_IPV6_TCP: + case RX_DESC3_L34T_IPV6_UDP: + pkt_info->rss_hash_type = PKT_HASH_TYPE_L4; + break; + default: + pkt_info->rss_hash_type = PKT_HASH_TYPE_L3; + } + } + + /* Get the pkt_info length */ + desc_data->rx.len = XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_PL_POS, + RX_NORMAL_DESC3_PL_LEN); + + if (!XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_LD_POS, + RX_NORMAL_DESC3_LD_LEN)) { + /* Not all the data has been transferred for this pkt_info */ + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_INCOMPLETE_POS, + RX_PACKET_ATTRIBUTES_INCOMPLETE_LEN, + 1); + return 0; + } + + /* This is the last of the data for this pkt_info */ + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_INCOMPLETE_POS, + RX_PACKET_ATTRIBUTES_INCOMPLETE_LEN, + 0); + + /* Set checksum done indicator as appropriate */ + if (netdev->features & NETIF_F_RXCSUM) + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_CSUM_DONE_POS, + RX_PACKET_ATTRIBUTES_CSUM_DONE_LEN, + 1); + + /* Check for errors (only valid in last descriptor) */ + err = XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_ES_POS, + RX_NORMAL_DESC3_ES_LEN); + etlt = XLGMAC_GET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_ETLT_POS, + RX_NORMAL_DESC3_ETLT_LEN); + netif_dbg(pdata, rx_status, netdev, "err=%u, etlt=%#x\n", err, etlt); + + if (!err || !etlt) { + /* No error if err is 0 or etlt is 0 */ + if ((etlt == 0x09) && + (netdev->features & NETIF_F_HW_VLAN_CTAG_RX)) { + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, + RX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN, + 1); + pkt_info->vlan_ctag = + XLGMAC_GET_REG_BITS_LE(dma_desc->desc0, + RX_NORMAL_DESC0_OVT_POS, + RX_NORMAL_DESC0_OVT_LEN); + netif_dbg(pdata, rx_status, netdev, "vlan-ctag=%#06x\n", + pkt_info->vlan_ctag); + } + } else { + if ((etlt == 0x05) || (etlt == 0x06)) + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_CSUM_DONE_POS, + RX_PACKET_ATTRIBUTES_CSUM_DONE_LEN, + 0); + else + pkt_info->errors = XLGMAC_SET_REG_BITS( + pkt_info->errors, + RX_PACKET_ERRORS_FRAME_POS, + RX_PACKET_ERRORS_FRAME_LEN, + 1); + } + + XLGMAC_PR("%s - descriptor=%u (cur=%d)\n", channel->name, + ring->cur & (ring->dma_desc_count - 1), ring->cur); + + return 0; +} + +static int xlgmac_enable_int(struct xlgmac_channel *channel, + enum xlgmac_int int_id) +{ + unsigned int dma_ch_ier; + + dma_ch_ier = readl(XLGMAC_DMA_REG(channel, DMA_CH_IER)); + + switch (int_id) { + case XLGMAC_INT_DMA_CH_SR_TI: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_TIE_POS, + DMA_CH_IER_TIE_LEN, 1); + break; + case XLGMAC_INT_DMA_CH_SR_TPS: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_TXSE_POS, + DMA_CH_IER_TXSE_LEN, 1); + break; + case XLGMAC_INT_DMA_CH_SR_TBU: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_TBUE_POS, + DMA_CH_IER_TBUE_LEN, 1); + break; + case XLGMAC_INT_DMA_CH_SR_RI: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_RIE_POS, + DMA_CH_IER_RIE_LEN, 1); + break; + case XLGMAC_INT_DMA_CH_SR_RBU: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_RBUE_POS, + DMA_CH_IER_RBUE_LEN, 1); + break; + case XLGMAC_INT_DMA_CH_SR_RPS: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_RSE_POS, + DMA_CH_IER_RSE_LEN, 1); + break; + case XLGMAC_INT_DMA_CH_SR_TI_RI: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_TIE_POS, + DMA_CH_IER_TIE_LEN, 1); + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_RIE_POS, + DMA_CH_IER_RIE_LEN, 1); + break; + case XLGMAC_INT_DMA_CH_SR_FBE: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_FBEE_POS, + DMA_CH_IER_FBEE_LEN, 1); + break; + case XLGMAC_INT_DMA_ALL: + dma_ch_ier |= channel->saved_ier; + break; + default: + return -1; + } + + writel(dma_ch_ier, XLGMAC_DMA_REG(channel, DMA_CH_IER)); + + return 0; +} + +static int xlgmac_disable_int(struct xlgmac_channel *channel, + enum xlgmac_int int_id) +{ + unsigned int dma_ch_ier; + + dma_ch_ier = readl(XLGMAC_DMA_REG(channel, DMA_CH_IER)); + + switch (int_id) { + case XLGMAC_INT_DMA_CH_SR_TI: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_TIE_POS, + DMA_CH_IER_TIE_LEN, 0); + break; + case XLGMAC_INT_DMA_CH_SR_TPS: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_TXSE_POS, + DMA_CH_IER_TXSE_LEN, 0); + break; + case XLGMAC_INT_DMA_CH_SR_TBU: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_TBUE_POS, + DMA_CH_IER_TBUE_LEN, 0); + break; + case XLGMAC_INT_DMA_CH_SR_RI: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_RIE_POS, + DMA_CH_IER_RIE_LEN, 0); + break; + case XLGMAC_INT_DMA_CH_SR_RBU: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_RBUE_POS, + DMA_CH_IER_RBUE_LEN, 0); + break; + case XLGMAC_INT_DMA_CH_SR_RPS: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_RSE_POS, + DMA_CH_IER_RSE_LEN, 0); + break; + case XLGMAC_INT_DMA_CH_SR_TI_RI: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_TIE_POS, + DMA_CH_IER_TIE_LEN, 0); + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_RIE_POS, + DMA_CH_IER_RIE_LEN, 0); + break; + case XLGMAC_INT_DMA_CH_SR_FBE: + dma_ch_ier = XLGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_FBEE_POS, + DMA_CH_IER_FBEE_LEN, 0); + break; + case XLGMAC_INT_DMA_ALL: + channel->saved_ier = dma_ch_ier & XLGMAC_DMA_INTERRUPT_MASK; + dma_ch_ier &= ~XLGMAC_DMA_INTERRUPT_MASK; + break; + default: + return -1; + } + + writel(dma_ch_ier, XLGMAC_DMA_REG(channel, DMA_CH_IER)); + + return 0; +} + +static int xlgmac_flush_tx_queues(struct xlgmac_pdata *pdata) +{ + unsigned int i, count; + u32 regval; + + for (i = 0; i < pdata->tx_q_count; i++) { + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = XLGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_FTQ_POS, + MTL_Q_TQOMR_FTQ_LEN, 1); + writel(regval, XLGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + } + + /* Poll Until Poll Condition */ + for (i = 0; i < pdata->tx_q_count; i++) { + count = 2000; + regval = readl(XLGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = XLGMAC_GET_REG_BITS(regval, MTL_Q_TQOMR_FTQ_POS, + MTL_Q_TQOMR_FTQ_LEN); + while (--count && regval) + usleep_range(500, 600); + + if (!count) + return -EBUSY; + } + + return 0; +} + +static void xlgmac_config_dma_bus(struct xlgmac_pdata *pdata) +{ + u32 regval; + + regval = readl(pdata->mac_regs + DMA_SBMR); + /* Set enhanced addressing mode */ + regval = XLGMAC_SET_REG_BITS(regval, DMA_SBMR_EAME_POS, + DMA_SBMR_EAME_LEN, 1); + /* Set the System Bus mode */ + regval = XLGMAC_SET_REG_BITS(regval, DMA_SBMR_UNDEF_POS, + DMA_SBMR_UNDEF_LEN, 1); + regval = XLGMAC_SET_REG_BITS(regval, DMA_SBMR_BLEN_256_POS, + DMA_SBMR_BLEN_256_LEN, 1); + writel(regval, pdata->mac_regs + DMA_SBMR); +} + +static int xlgmac_hw_init(struct xlgmac_pdata *pdata) +{ + struct xlgmac_desc_ops *desc_ops = &pdata->desc_ops; + int ret; + + /* Flush Tx queues */ + ret = xlgmac_flush_tx_queues(pdata); + if (ret) + return ret; + + /* Initialize DMA related features */ + xlgmac_config_dma_bus(pdata); + xlgmac_config_osp_mode(pdata); + xlgmac_config_pblx8(pdata); + xlgmac_config_tx_pbl_val(pdata); + xlgmac_config_rx_pbl_val(pdata); + xlgmac_config_rx_coalesce(pdata); + xlgmac_config_tx_coalesce(pdata); + xlgmac_config_rx_buffer_size(pdata); + xlgmac_config_tso_mode(pdata); + xlgmac_config_sph_mode(pdata); + xlgmac_config_rss(pdata); + desc_ops->tx_desc_init(pdata); + desc_ops->rx_desc_init(pdata); + xlgmac_enable_dma_interrupts(pdata); + + /* Initialize MTL related features */ + xlgmac_config_mtl_mode(pdata); + xlgmac_config_queue_mapping(pdata); + xlgmac_config_tsf_mode(pdata, pdata->tx_sf_mode); + xlgmac_config_rsf_mode(pdata, pdata->rx_sf_mode); + xlgmac_config_tx_threshold(pdata, pdata->tx_threshold); + xlgmac_config_rx_threshold(pdata, pdata->rx_threshold); + xlgmac_config_tx_fifo_size(pdata); + xlgmac_config_rx_fifo_size(pdata); + xlgmac_config_flow_control_threshold(pdata); + xlgmac_config_rx_fep_enable(pdata); + xlgmac_config_rx_fup_enable(pdata); + xlgmac_enable_mtl_interrupts(pdata); + + /* Initialize MAC related features */ + xlgmac_config_mac_address(pdata); + xlgmac_config_rx_mode(pdata); + xlgmac_config_jumbo_enable(pdata); + xlgmac_config_flow_control(pdata); + xlgmac_config_mac_speed(pdata); + xlgmac_config_checksum_offload(pdata); + xlgmac_config_vlan_support(pdata); + xlgmac_config_mmc(pdata); + xlgmac_enable_mac_interrupts(pdata); + + return 0; +} + +static int xlgmac_hw_exit(struct xlgmac_pdata *pdata) +{ + unsigned int count = 2000; + u32 regval; + + /* Issue a software reset */ + regval = readl(pdata->mac_regs + DMA_MR); + regval = XLGMAC_SET_REG_BITS(regval, DMA_MR_SWR_POS, + DMA_MR_SWR_LEN, 1); + writel(regval, pdata->mac_regs + DMA_MR); + usleep_range(10, 15); + + /* Poll Until Poll Condition */ + while (--count && + XLGMAC_GET_REG_BITS(readl(pdata->mac_regs + DMA_MR), + DMA_MR_SWR_POS, DMA_MR_SWR_LEN)) + usleep_range(500, 600); + + if (!count) + return -EBUSY; + + return 0; +} + +void xlgmac_init_hw_ops(struct xlgmac_hw_ops *hw_ops) +{ + hw_ops->init = xlgmac_hw_init; + hw_ops->exit = xlgmac_hw_exit; + + hw_ops->tx_complete = xlgmac_tx_complete; + + hw_ops->enable_tx = xlgmac_enable_tx; + hw_ops->disable_tx = xlgmac_disable_tx; + hw_ops->enable_rx = xlgmac_enable_rx; + hw_ops->disable_rx = xlgmac_disable_rx; + + hw_ops->dev_xmit = xlgmac_dev_xmit; + hw_ops->dev_read = xlgmac_dev_read; + hw_ops->enable_int = xlgmac_enable_int; + hw_ops->disable_int = xlgmac_disable_int; + + hw_ops->set_mac_address = xlgmac_set_mac_address; + hw_ops->config_rx_mode = xlgmac_config_rx_mode; + hw_ops->enable_rx_csum = xlgmac_enable_rx_csum; + hw_ops->disable_rx_csum = xlgmac_disable_rx_csum; + + /* For MII speed configuration */ + hw_ops->set_xlgmii_25000_speed = xlgmac_set_xlgmii_25000_speed; + hw_ops->set_xlgmii_40000_speed = xlgmac_set_xlgmii_40000_speed; + hw_ops->set_xlgmii_50000_speed = xlgmac_set_xlgmii_50000_speed; + hw_ops->set_xlgmii_100000_speed = xlgmac_set_xlgmii_100000_speed; + + /* For descriptor related operation */ + hw_ops->tx_desc_init = xlgmac_tx_desc_init; + hw_ops->rx_desc_init = xlgmac_rx_desc_init; + hw_ops->tx_desc_reset = xlgmac_tx_desc_reset; + hw_ops->rx_desc_reset = xlgmac_rx_desc_reset; + hw_ops->is_last_desc = xlgmac_is_last_desc; + hw_ops->is_context_desc = xlgmac_is_context_desc; + hw_ops->tx_start_xmit = xlgmac_tx_start_xmit; + + /* For Flow Control */ + hw_ops->config_tx_flow_control = xlgmac_config_tx_flow_control; + hw_ops->config_rx_flow_control = xlgmac_config_rx_flow_control; + + /* For Vlan related config */ + hw_ops->enable_rx_vlan_stripping = xlgmac_enable_rx_vlan_stripping; + hw_ops->disable_rx_vlan_stripping = xlgmac_disable_rx_vlan_stripping; + hw_ops->enable_rx_vlan_filtering = xlgmac_enable_rx_vlan_filtering; + hw_ops->disable_rx_vlan_filtering = xlgmac_disable_rx_vlan_filtering; + hw_ops->update_vlan_hash_table = xlgmac_update_vlan_hash_table; + + /* For RX coalescing */ + hw_ops->config_rx_coalesce = xlgmac_config_rx_coalesce; + hw_ops->config_tx_coalesce = xlgmac_config_tx_coalesce; + hw_ops->usec_to_riwt = xlgmac_usec_to_riwt; + hw_ops->riwt_to_usec = xlgmac_riwt_to_usec; + + /* For RX and TX threshold config */ + hw_ops->config_rx_threshold = xlgmac_config_rx_threshold; + hw_ops->config_tx_threshold = xlgmac_config_tx_threshold; + + /* For RX and TX Store and Forward Mode config */ + hw_ops->config_rsf_mode = xlgmac_config_rsf_mode; + hw_ops->config_tsf_mode = xlgmac_config_tsf_mode; + + /* For TX DMA Operating on Second Frame config */ + hw_ops->config_osp_mode = xlgmac_config_osp_mode; + + /* For RX and TX PBL config */ + hw_ops->config_rx_pbl_val = xlgmac_config_rx_pbl_val; + hw_ops->get_rx_pbl_val = xlgmac_get_rx_pbl_val; + hw_ops->config_tx_pbl_val = xlgmac_config_tx_pbl_val; + hw_ops->get_tx_pbl_val = xlgmac_get_tx_pbl_val; + hw_ops->config_pblx8 = xlgmac_config_pblx8; + + /* For MMC statistics support */ + hw_ops->tx_mmc_int = xlgmac_tx_mmc_int; + hw_ops->rx_mmc_int = xlgmac_rx_mmc_int; + hw_ops->read_mmc_stats = xlgmac_read_mmc_stats; + + /* For Receive Side Scaling */ + hw_ops->enable_rss = xlgmac_enable_rss; + hw_ops->disable_rss = xlgmac_disable_rss; + hw_ops->set_rss_hash_key = xlgmac_set_rss_hash_key; + hw_ops->set_rss_lookup_table = xlgmac_set_rss_lookup_table; +} diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c new file mode 100644 index 000000000000..5e8428be3d66 --- /dev/null +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c @@ -0,0 +1,1334 @@ +/* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver + * + * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This Synopsys DWC XLGMAC software driver and associated documentation + * (hereinafter the "Software") is an unsupported proprietary work of + * Synopsys, Inc. unless otherwise expressly agreed to in writing between + * Synopsys and you. The Software IS NOT an item of Licensed Software or a + * Licensed Product under any End User Software License Agreement or + * Agreement for Licensed Products with Synopsys or any supplement thereto. + * Synopsys is a registered trademark of Synopsys, Inc. Other names included + * in the SOFTWARE may be the trademarks of their respective owners. + */ + +#include +#include + +#include "dwc-xlgmac.h" +#include "dwc-xlgmac-reg.h" + +static int xlgmac_one_poll(struct napi_struct *, int); +static int xlgmac_all_poll(struct napi_struct *, int); + +static inline unsigned int xlgmac_tx_avail_desc(struct xlgmac_ring *ring) +{ + return (ring->dma_desc_count - (ring->cur - ring->dirty)); +} + +static inline unsigned int xlgmac_rx_dirty_desc(struct xlgmac_ring *ring) +{ + return (ring->cur - ring->dirty); +} + +static int xlgmac_maybe_stop_tx_queue( + struct xlgmac_channel *channel, + struct xlgmac_ring *ring, + unsigned int count) +{ + struct xlgmac_pdata *pdata = channel->pdata; + + if (count > xlgmac_tx_avail_desc(ring)) { + netif_info(pdata, drv, pdata->netdev, + "Tx queue stopped, not enough descriptors available\n"); + netif_stop_subqueue(pdata->netdev, channel->queue_index); + ring->tx.queue_stopped = 1; + + /* If we haven't notified the hardware because of xmit_more + * support, tell it now + */ + if (ring->tx.xmit_more) + pdata->hw_ops.tx_start_xmit(channel, ring); + + return NETDEV_TX_BUSY; + } + + return 0; +} + +static void xlgmac_prep_vlan(struct sk_buff *skb, + struct xlgmac_pkt_info *pkt_info) +{ + if (skb_vlan_tag_present(skb)) + pkt_info->vlan_ctag = skb_vlan_tag_get(skb); +} + +static int xlgmac_prep_tso(struct sk_buff *skb, + struct xlgmac_pkt_info *pkt_info) +{ + int ret; + + if (!XLGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN)) + return 0; + + ret = skb_cow_head(skb, 0); + if (ret) + return ret; + + pkt_info->header_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + pkt_info->tcp_header_len = tcp_hdrlen(skb); + pkt_info->tcp_payload_len = skb->len - pkt_info->header_len; + pkt_info->mss = skb_shinfo(skb)->gso_size; + + XLGMAC_PR("header_len=%u\n", pkt_info->header_len); + XLGMAC_PR("tcp_header_len=%u, tcp_payload_len=%u\n", + pkt_info->tcp_header_len, pkt_info->tcp_payload_len); + XLGMAC_PR("mss=%u\n", pkt_info->mss); + + /* Update the number of packets that will ultimately be transmitted + * along with the extra bytes for each extra packet + */ + pkt_info->tx_packets = skb_shinfo(skb)->gso_segs; + pkt_info->tx_bytes += (pkt_info->tx_packets - 1) * pkt_info->header_len; + + return 0; +} + +static int xlgmac_is_tso(struct sk_buff *skb) +{ + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + if (!skb_is_gso(skb)) + return 0; + + return 1; +} + +static void xlgmac_prep_tx_pkt(struct xlgmac_pdata *pdata, + struct xlgmac_ring *ring, + struct sk_buff *skb, + struct xlgmac_pkt_info *pkt_info) +{ + struct skb_frag_struct *frag; + unsigned int context_desc; + unsigned int len; + unsigned int i; + + pkt_info->skb = skb; + + context_desc = 0; + pkt_info->desc_count = 0; + + pkt_info->tx_packets = 1; + pkt_info->tx_bytes = skb->len; + + if (xlgmac_is_tso(skb)) { + /* TSO requires an extra descriptor if mss is different */ + if (skb_shinfo(skb)->gso_size != ring->tx.cur_mss) { + context_desc = 1; + pkt_info->desc_count++; + } + + /* TSO requires an extra descriptor for TSO header */ + pkt_info->desc_count++; + + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN, + 1); + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS, + TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN, + 1); + } else if (skb->ip_summed == CHECKSUM_PARTIAL) + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS, + TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN, + 1); + + if (skb_vlan_tag_present(skb)) { + /* VLAN requires an extra descriptor if tag is different */ + if (skb_vlan_tag_get(skb) != ring->tx.cur_vlan_ctag) + /* We can share with the TSO context descriptor */ + if (!context_desc) { + context_desc = 1; + pkt_info->desc_count++; + } + + pkt_info->attributes = XLGMAC_SET_REG_BITS( + pkt_info->attributes, + TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, + TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN, + 1); + } + + for (len = skb_headlen(skb); len;) { + pkt_info->desc_count++; + len -= min_t(unsigned int, len, XLGMAC_TX_MAX_BUF_SIZE); + } + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + frag = &skb_shinfo(skb)->frags[i]; + for (len = skb_frag_size(frag); len; ) { + pkt_info->desc_count++; + len -= min_t(unsigned int, len, XLGMAC_TX_MAX_BUF_SIZE); + } + } +} + +static int xlgmac_calc_rx_buf_size(struct net_device *netdev, unsigned int mtu) +{ + unsigned int rx_buf_size; + + if (mtu > XLGMAC_JUMBO_PACKET_MTU) { + netdev_alert(netdev, "MTU exceeds maximum supported value\n"); + return -EINVAL; + } + + rx_buf_size = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + rx_buf_size = clamp_val(rx_buf_size, XLGMAC_RX_MIN_BUF_SIZE, PAGE_SIZE); + + rx_buf_size = (rx_buf_size + XLGMAC_RX_BUF_ALIGN - 1) & + ~(XLGMAC_RX_BUF_ALIGN - 1); + + return rx_buf_size; +} + +static void xlgmac_enable_rx_tx_ints(struct xlgmac_pdata *pdata) +{ + struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; + struct xlgmac_channel *channel; + enum xlgmac_int int_id; + unsigned int i; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (channel->tx_ring && channel->rx_ring) + int_id = XLGMAC_INT_DMA_CH_SR_TI_RI; + else if (channel->tx_ring) + int_id = XLGMAC_INT_DMA_CH_SR_TI; + else if (channel->rx_ring) + int_id = XLGMAC_INT_DMA_CH_SR_RI; + else + continue; + + hw_ops->enable_int(channel, int_id); + } +} + +static void xlgmac_disable_rx_tx_ints(struct xlgmac_pdata *pdata) +{ + struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; + struct xlgmac_channel *channel; + enum xlgmac_int int_id; + unsigned int i; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (channel->tx_ring && channel->rx_ring) + int_id = XLGMAC_INT_DMA_CH_SR_TI_RI; + else if (channel->tx_ring) + int_id = XLGMAC_INT_DMA_CH_SR_TI; + else if (channel->rx_ring) + int_id = XLGMAC_INT_DMA_CH_SR_RI; + else + continue; + + hw_ops->disable_int(channel, int_id); + } +} + +static irqreturn_t xlgmac_isr(int irq, void *data) +{ + unsigned int dma_isr, dma_ch_isr, mac_isr; + struct xlgmac_pdata *pdata = data; + struct xlgmac_channel *channel; + struct xlgmac_hw_ops *hw_ops; + unsigned int i, ti, ri; + + hw_ops = &pdata->hw_ops; + + /* The DMA interrupt status register also reports MAC and MTL + * interrupts. So for polling mode, we just need to check for + * this register to be non-zero + */ + dma_isr = readl(pdata->mac_regs + DMA_ISR); + if (!dma_isr) + return IRQ_HANDLED; + + netif_dbg(pdata, intr, pdata->netdev, "DMA_ISR=%#010x\n", dma_isr); + + for (i = 0; i < pdata->channel_count; i++) { + if (!(dma_isr & (1 << i))) + continue; + + channel = pdata->channel_head + i; + + dma_ch_isr = readl(XLGMAC_DMA_REG(channel, DMA_CH_SR)); + netif_dbg(pdata, intr, pdata->netdev, "DMA_CH%u_ISR=%#010x\n", + i, dma_ch_isr); + + /* The TI or RI interrupt bits may still be set even if using + * per channel DMA interrupts. Check to be sure those are not + * enabled before using the private data napi structure. + */ + ti = XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TI_POS, + DMA_CH_SR_TI_LEN); + ri = XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RI_POS, + DMA_CH_SR_RI_LEN); + if (!pdata->per_channel_irq && (ti || ri)) { + if (napi_schedule_prep(&pdata->napi)) { + /* Disable Tx and Rx interrupts */ + xlgmac_disable_rx_tx_ints(pdata); + + /* Turn on polling */ + __napi_schedule_irqoff(&pdata->napi); + } + } + + if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RBU_POS, + DMA_CH_SR_RBU_LEN)) + pdata->stats.rx_buffer_unavailable++; + + /* Restart the device on a Fatal Bus Error */ + if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_FBE_POS, + DMA_CH_SR_FBE_LEN)) + schedule_work(&pdata->restart_work); + + /* Clear all interrupt signals */ + writel(dma_ch_isr, XLGMAC_DMA_REG(channel, DMA_CH_SR)); + } + + if (XLGMAC_GET_REG_BITS(dma_isr, DMA_ISR_MACIS_POS, + DMA_ISR_MACIS_LEN)) { + mac_isr = readl(pdata->mac_regs + MAC_ISR); + + if (XLGMAC_GET_REG_BITS(mac_isr, MAC_ISR_MMCTXIS_POS, + MAC_ISR_MMCTXIS_LEN)) + hw_ops->tx_mmc_int(pdata); + + if (XLGMAC_GET_REG_BITS(mac_isr, MAC_ISR_MMCRXIS_POS, + MAC_ISR_MMCRXIS_LEN)) + hw_ops->rx_mmc_int(pdata); + } + + return IRQ_HANDLED; +} + +static irqreturn_t xlgmac_dma_isr(int irq, void *data) +{ + struct xlgmac_channel *channel = data; + + /* Per channel DMA interrupts are enabled, so we use the per + * channel napi structure and not the private data napi structure + */ + if (napi_schedule_prep(&channel->napi)) { + /* Disable Tx and Rx interrupts */ + disable_irq_nosync(channel->dma_irq); + + /* Turn on polling */ + __napi_schedule_irqoff(&channel->napi); + } + + return IRQ_HANDLED; +} + +static void xlgmac_tx_timer(unsigned long data) +{ + struct xlgmac_channel *channel = (struct xlgmac_channel *)data; + struct xlgmac_pdata *pdata = channel->pdata; + struct napi_struct *napi; + + napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi; + + if (napi_schedule_prep(napi)) { + /* Disable Tx and Rx interrupts */ + if (pdata->per_channel_irq) + disable_irq_nosync(channel->dma_irq); + else + xlgmac_disable_rx_tx_ints(pdata); + + /* Turn on polling */ + __napi_schedule(napi); + } + + channel->tx_timer_active = 0; +} + +static void xlgmac_init_timers(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + break; + + setup_timer(&channel->tx_timer, xlgmac_tx_timer, + (unsigned long)channel); + } +} + +static void xlgmac_stop_timers(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + break; + + del_timer_sync(&channel->tx_timer); + } +} + +static void xlgmac_napi_enable(struct xlgmac_pdata *pdata, unsigned int add) +{ + struct xlgmac_channel *channel; + unsigned int i; + + if (pdata->per_channel_irq) { + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (add) + netif_napi_add(pdata->netdev, &channel->napi, + xlgmac_one_poll, + NAPI_POLL_WEIGHT); + + napi_enable(&channel->napi); + } + } else { + if (add) + netif_napi_add(pdata->netdev, &pdata->napi, + xlgmac_all_poll, NAPI_POLL_WEIGHT); + + napi_enable(&pdata->napi); + } +} + +static void xlgmac_napi_disable(struct xlgmac_pdata *pdata, unsigned int del) +{ + struct xlgmac_channel *channel; + unsigned int i; + + if (pdata->per_channel_irq) { + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + napi_disable(&channel->napi); + + if (del) + netif_napi_del(&channel->napi); + } + } else { + napi_disable(&pdata->napi); + + if (del) + netif_napi_del(&pdata->napi); + } +} + +static int xlgmac_request_irqs(struct xlgmac_pdata *pdata) +{ + struct net_device *netdev = pdata->netdev; + struct xlgmac_channel *channel; + unsigned int i; + int ret; + + ret = devm_request_irq(pdata->dev, pdata->dev_irq, xlgmac_isr, + IRQF_SHARED, netdev->name, pdata); + if (ret) { + netdev_alert(netdev, "error requesting irq %d\n", + pdata->dev_irq); + return ret; + } + + if (!pdata->per_channel_irq) + return 0; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + snprintf(channel->dma_irq_name, + sizeof(channel->dma_irq_name) - 1, + "%s-TxRx-%u", netdev_name(netdev), + channel->queue_index); + + ret = devm_request_irq(pdata->dev, channel->dma_irq, + xlgmac_dma_isr, 0, + channel->dma_irq_name, channel); + if (ret) { + netdev_alert(netdev, "error requesting irq %d\n", + channel->dma_irq); + goto err_irq; + } + } + + return 0; + +err_irq: + /* Using an unsigned int, 'i' will go to UINT_MAX and exit */ + for (i--, channel--; i < pdata->channel_count; i--, channel--) + devm_free_irq(pdata->dev, channel->dma_irq, channel); + + devm_free_irq(pdata->dev, pdata->dev_irq, pdata); + + return ret; +} + +static void xlgmac_free_irqs(struct xlgmac_pdata *pdata) +{ + struct xlgmac_channel *channel; + unsigned int i; + + devm_free_irq(pdata->dev, pdata->dev_irq, pdata); + + if (!pdata->per_channel_irq) + return; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) + devm_free_irq(pdata->dev, channel->dma_irq, channel); +} + +static void xlgmac_free_tx_data(struct xlgmac_pdata *pdata) +{ + struct xlgmac_desc_ops *desc_ops = &pdata->desc_ops; + struct xlgmac_desc_data *desc_data; + struct xlgmac_channel *channel; + struct xlgmac_ring *ring; + unsigned int i, j; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + ring = channel->tx_ring; + if (!ring) + break; + + for (j = 0; j < ring->dma_desc_count; j++) { + desc_data = XLGMAC_GET_DESC_DATA(ring, j); + desc_ops->unmap_desc_data(pdata, desc_data); + } + } +} + +static void xlgmac_free_rx_data(struct xlgmac_pdata *pdata) +{ + struct xlgmac_desc_ops *desc_ops = &pdata->desc_ops; + struct xlgmac_desc_data *desc_data; + struct xlgmac_channel *channel; + struct xlgmac_ring *ring; + unsigned int i, j; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + ring = channel->rx_ring; + if (!ring) + break; + + for (j = 0; j < ring->dma_desc_count; j++) { + desc_data = XLGMAC_GET_DESC_DATA(ring, j); + desc_ops->unmap_desc_data(pdata, desc_data); + } + } +} + +static int xlgmac_start(struct xlgmac_pdata *pdata) +{ + struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; + struct net_device *netdev = pdata->netdev; + int ret; + + hw_ops->init(pdata); + xlgmac_napi_enable(pdata, 1); + + ret = xlgmac_request_irqs(pdata); + if (ret) + goto err_napi; + + hw_ops->enable_tx(pdata); + hw_ops->enable_rx(pdata); + netif_tx_start_all_queues(netdev); + + return 0; + +err_napi: + xlgmac_napi_disable(pdata, 1); + hw_ops->exit(pdata); + + return ret; +} + +static void xlgmac_stop(struct xlgmac_pdata *pdata) +{ + struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; + struct net_device *netdev = pdata->netdev; + struct xlgmac_channel *channel; + struct netdev_queue *txq; + unsigned int i; + + netif_tx_stop_all_queues(netdev); + xlgmac_stop_timers(pdata); + hw_ops->disable_tx(pdata); + hw_ops->disable_rx(pdata); + xlgmac_free_irqs(pdata); + xlgmac_napi_disable(pdata, 1); + hw_ops->exit(pdata); + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + continue; + + txq = netdev_get_tx_queue(netdev, channel->queue_index); + netdev_tx_reset_queue(txq); + } +} + +static void xlgmac_restart_dev(struct xlgmac_pdata *pdata) +{ + /* If not running, "restart" will happen on open */ + if (!netif_running(pdata->netdev)) + return; + + xlgmac_stop(pdata); + + xlgmac_free_tx_data(pdata); + xlgmac_free_rx_data(pdata); + + xlgmac_start(pdata); +} + +static void xlgmac_restart(struct work_struct *work) +{ + struct xlgmac_pdata *pdata = container_of(work, + struct xlgmac_pdata, + restart_work); + + rtnl_lock(); + + xlgmac_restart_dev(pdata); + + rtnl_unlock(); +} + +static int xlgmac_open(struct net_device *netdev) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + struct xlgmac_desc_ops *desc_ops; + int ret; + + desc_ops = &pdata->desc_ops; + + /* TODO: Initialize the phy */ + + /* Calculate the Rx buffer size before allocating rings */ + ret = xlgmac_calc_rx_buf_size(netdev, netdev->mtu); + if (ret < 0) + return ret; + pdata->rx_buf_size = ret; + + /* Allocate the channels and rings */ + ret = desc_ops->alloc_channles_and_rings(pdata); + if (ret) + return ret; + + INIT_WORK(&pdata->restart_work, xlgmac_restart); + xlgmac_init_timers(pdata); + + ret = xlgmac_start(pdata); + if (ret) + goto err_channels_and_rings; + + return 0; + +err_channels_and_rings: + desc_ops->free_channels_and_rings(pdata); + + return ret; +} + +static int xlgmac_close(struct net_device *netdev) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + struct xlgmac_desc_ops *desc_ops; + + desc_ops = &pdata->desc_ops; + + /* Stop the device */ + xlgmac_stop(pdata); + + /* Free the channels and rings */ + desc_ops->free_channels_and_rings(pdata); + + return 0; +} + +static void xlgmac_tx_timeout(struct net_device *netdev) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + + netdev_warn(netdev, "tx timeout, device restarting\n"); + schedule_work(&pdata->restart_work); +} + +static int xlgmac_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + struct xlgmac_pkt_info *tx_pkt_info; + struct xlgmac_desc_ops *desc_ops; + struct xlgmac_channel *channel; + struct xlgmac_hw_ops *hw_ops; + struct netdev_queue *txq; + struct xlgmac_ring *ring; + int ret; + + desc_ops = &pdata->desc_ops; + hw_ops = &pdata->hw_ops; + + XLGMAC_PR("skb->len = %d\n", skb->len); + + channel = pdata->channel_head + skb->queue_mapping; + txq = netdev_get_tx_queue(netdev, channel->queue_index); + ring = channel->tx_ring; + tx_pkt_info = &ring->pkt_info; + + if (skb->len == 0) { + netif_err(pdata, tx_err, netdev, + "empty skb received from stack\n"); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + /* Prepare preliminary packet info for TX */ + memset(tx_pkt_info, 0, sizeof(*tx_pkt_info)); + xlgmac_prep_tx_pkt(pdata, ring, skb, tx_pkt_info); + + /* Check that there are enough descriptors available */ + ret = xlgmac_maybe_stop_tx_queue(channel, ring, + tx_pkt_info->desc_count); + if (ret) + return ret; + + ret = xlgmac_prep_tso(skb, tx_pkt_info); + if (ret) { + netif_err(pdata, tx_err, netdev, + "error processing TSO packet\n"); + dev_kfree_skb_any(skb); + return ret; + } + xlgmac_prep_vlan(skb, tx_pkt_info); + + if (!desc_ops->map_tx_skb(channel, skb)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + /* Report on the actual number of bytes (to be) sent */ + netdev_tx_sent_queue(txq, tx_pkt_info->tx_bytes); + + /* Configure required descriptor fields for transmission */ + hw_ops->dev_xmit(channel); + + if (netif_msg_pktdata(pdata)) + xlgmac_print_pkt(netdev, skb, true); + + /* Stop the queue in advance if there may not be enough descriptors */ + xlgmac_maybe_stop_tx_queue(channel, ring, XLGMAC_TX_MAX_DESC_NR); + + return NETDEV_TX_OK; +} + +static void xlgmac_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *s) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + struct xlgmac_stats *pstats = &pdata->stats; + + pdata->hw_ops.read_mmc_stats(pdata); + + s->rx_packets = pstats->rxframecount_gb; + s->rx_bytes = pstats->rxoctetcount_gb; + s->rx_errors = pstats->rxframecount_gb - + pstats->rxbroadcastframes_g - + pstats->rxmulticastframes_g - + pstats->rxunicastframes_g; + s->multicast = pstats->rxmulticastframes_g; + s->rx_length_errors = pstats->rxlengtherror; + s->rx_crc_errors = pstats->rxcrcerror; + s->rx_fifo_errors = pstats->rxfifooverflow; + + s->tx_packets = pstats->txframecount_gb; + s->tx_bytes = pstats->txoctetcount_gb; + s->tx_errors = pstats->txframecount_gb - pstats->txframecount_g; + s->tx_dropped = netdev->stats.tx_dropped; +} + +static int xlgmac_set_mac_address(struct net_device *netdev, void *addr) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; + struct sockaddr *saddr = addr; + + if (!is_valid_ether_addr(saddr->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(netdev->dev_addr, saddr->sa_data, netdev->addr_len); + + hw_ops->set_mac_address(pdata, netdev->dev_addr); + + return 0; +} + +static int xlgmac_ioctl(struct net_device *netdev, + struct ifreq *ifreq, int cmd) +{ + if (!netif_running(netdev)) + return -ENODEV; + + return 0; +} + +static int xlgmac_change_mtu(struct net_device *netdev, int mtu) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + int ret; + + ret = xlgmac_calc_rx_buf_size(netdev, mtu); + if (ret < 0) + return ret; + + pdata->rx_buf_size = ret; + netdev->mtu = mtu; + + xlgmac_restart_dev(pdata); + + return 0; +} + +static int xlgmac_vlan_rx_add_vid(struct net_device *netdev, + __be16 proto, + u16 vid) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; + + set_bit(vid, pdata->active_vlans); + hw_ops->update_vlan_hash_table(pdata); + + return 0; +} + +static int xlgmac_vlan_rx_kill_vid(struct net_device *netdev, + __be16 proto, + u16 vid) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; + + clear_bit(vid, pdata->active_vlans); + hw_ops->update_vlan_hash_table(pdata); + + return 0; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void xlgmac_poll_controller(struct net_device *netdev) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + struct xlgmac_channel *channel; + unsigned int i; + + if (pdata->per_channel_irq) { + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) + xlgmac_dma_isr(channel->dma_irq, channel); + } else { + disable_irq(pdata->dev_irq); + xlgmac_isr(pdata->dev_irq, pdata); + enable_irq(pdata->dev_irq); + } +} +#endif /* CONFIG_NET_POLL_CONTROLLER */ + +static int xlgmac_set_features(struct net_device *netdev, + netdev_features_t features) +{ + netdev_features_t rxhash, rxcsum, rxvlan, rxvlan_filter; + struct xlgmac_pdata *pdata = netdev_priv(netdev); + struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; + int ret = 0; + + rxhash = pdata->netdev_features & NETIF_F_RXHASH; + rxcsum = pdata->netdev_features & NETIF_F_RXCSUM; + rxvlan = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_RX; + rxvlan_filter = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_FILTER; + + if ((features & NETIF_F_RXHASH) && !rxhash) + ret = hw_ops->enable_rss(pdata); + else if (!(features & NETIF_F_RXHASH) && rxhash) + ret = hw_ops->disable_rss(pdata); + if (ret) + return ret; + + if ((features & NETIF_F_RXCSUM) && !rxcsum) + hw_ops->enable_rx_csum(pdata); + else if (!(features & NETIF_F_RXCSUM) && rxcsum) + hw_ops->disable_rx_csum(pdata); + + if ((features & NETIF_F_HW_VLAN_CTAG_RX) && !rxvlan) + hw_ops->enable_rx_vlan_stripping(pdata); + else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) && rxvlan) + hw_ops->disable_rx_vlan_stripping(pdata); + + if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) && !rxvlan_filter) + hw_ops->enable_rx_vlan_filtering(pdata); + else if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER) && rxvlan_filter) + hw_ops->disable_rx_vlan_filtering(pdata); + + pdata->netdev_features = features; + + return 0; +} + +static void xlgmac_set_rx_mode(struct net_device *netdev) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; + + hw_ops->config_rx_mode(pdata); +} + +static const struct net_device_ops xlgmac_netdev_ops = { + .ndo_open = xlgmac_open, + .ndo_stop = xlgmac_close, + .ndo_start_xmit = xlgmac_xmit, + .ndo_tx_timeout = xlgmac_tx_timeout, + .ndo_get_stats64 = xlgmac_get_stats64, + .ndo_change_mtu = xlgmac_change_mtu, + .ndo_set_mac_address = xlgmac_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = xlgmac_ioctl, + .ndo_vlan_rx_add_vid = xlgmac_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = xlgmac_vlan_rx_kill_vid, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = xlgmac_poll_controller, +#endif + .ndo_set_features = xlgmac_set_features, + .ndo_set_rx_mode = xlgmac_set_rx_mode, +}; + +const struct net_device_ops *xlgmac_get_netdev_ops(void) +{ + return &xlgmac_netdev_ops; +} + +static void xlgmac_rx_refresh(struct xlgmac_channel *channel) +{ + struct xlgmac_pdata *pdata = channel->pdata; + struct xlgmac_ring *ring = channel->rx_ring; + struct xlgmac_desc_data *desc_data; + struct xlgmac_desc_ops *desc_ops; + struct xlgmac_hw_ops *hw_ops; + + desc_ops = &pdata->desc_ops; + hw_ops = &pdata->hw_ops; + + while (ring->dirty != ring->cur) { + desc_data = XLGMAC_GET_DESC_DATA(ring, ring->dirty); + + /* Reset desc_data values */ + desc_ops->unmap_desc_data(pdata, desc_data); + + if (desc_ops->map_rx_buffer(pdata, ring, desc_data)) + break; + + hw_ops->rx_desc_reset(pdata, desc_data, ring->dirty); + + ring->dirty++; + } + + /* Make sure everything is written before the register write */ + wmb(); + + /* Update the Rx Tail Pointer Register with address of + * the last cleaned entry + */ + desc_data = XLGMAC_GET_DESC_DATA(ring, ring->dirty - 1); + writel(lower_32_bits(desc_data->dma_desc_addr), + XLGMAC_DMA_REG(channel, DMA_CH_RDTR_LO)); +} + +static struct sk_buff *xlgmac_create_skb(struct xlgmac_pdata *pdata, + struct napi_struct *napi, + struct xlgmac_desc_data *desc_data, + unsigned int len) +{ + unsigned int copy_len; + struct sk_buff *skb; + u8 *packet; + + skb = napi_alloc_skb(napi, desc_data->rx.hdr.dma_len); + if (!skb) + return NULL; + + /* Start with the header buffer which may contain just the header + * or the header plus data + */ + dma_sync_single_range_for_cpu(pdata->dev, desc_data->rx.hdr.dma_base, + desc_data->rx.hdr.dma_off, + desc_data->rx.hdr.dma_len, + DMA_FROM_DEVICE); + + packet = page_address(desc_data->rx.hdr.pa.pages) + + desc_data->rx.hdr.pa.pages_offset; + copy_len = (desc_data->rx.hdr_len) ? desc_data->rx.hdr_len : len; + copy_len = min(desc_data->rx.hdr.dma_len, copy_len); + skb_copy_to_linear_data(skb, packet, copy_len); + skb_put(skb, copy_len); + + len -= copy_len; + if (len) { + /* Add the remaining data as a frag */ + dma_sync_single_range_for_cpu(pdata->dev, + desc_data->rx.buf.dma_base, + desc_data->rx.buf.dma_off, + desc_data->rx.buf.dma_len, + DMA_FROM_DEVICE); + + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + desc_data->rx.buf.pa.pages, + desc_data->rx.buf.pa.pages_offset, + len, desc_data->rx.buf.dma_len); + desc_data->rx.buf.pa.pages = NULL; + } + + return skb; +} + +static int xlgmac_tx_poll(struct xlgmac_channel *channel) +{ + struct xlgmac_pdata *pdata = channel->pdata; + struct xlgmac_ring *ring = channel->tx_ring; + struct net_device *netdev = pdata->netdev; + unsigned int tx_packets = 0, tx_bytes = 0; + struct xlgmac_desc_data *desc_data; + struct xlgmac_dma_desc *dma_desc; + struct xlgmac_desc_ops *desc_ops; + struct xlgmac_hw_ops *hw_ops; + struct netdev_queue *txq; + int processed = 0; + unsigned int cur; + + desc_ops = &pdata->desc_ops; + hw_ops = &pdata->hw_ops; + + /* Nothing to do if there isn't a Tx ring for this channel */ + if (!ring) + return 0; + + cur = ring->cur; + + /* Be sure we get ring->cur before accessing descriptor data */ + smp_rmb(); + + txq = netdev_get_tx_queue(netdev, channel->queue_index); + + while ((processed < XLGMAC_TX_DESC_MAX_PROC) && + (ring->dirty != cur)) { + desc_data = XLGMAC_GET_DESC_DATA(ring, ring->dirty); + dma_desc = desc_data->dma_desc; + + if (!hw_ops->tx_complete(dma_desc)) + break; + + /* Make sure descriptor fields are read after reading + * the OWN bit + */ + dma_rmb(); + + if (netif_msg_tx_done(pdata)) + xlgmac_dump_tx_desc(pdata, ring, ring->dirty, 1, 0); + + if (hw_ops->is_last_desc(dma_desc)) { + tx_packets += desc_data->tx.packets; + tx_bytes += desc_data->tx.bytes; + } + + /* Free the SKB and reset the descriptor for re-use */ + desc_ops->unmap_desc_data(pdata, desc_data); + hw_ops->tx_desc_reset(desc_data); + + processed++; + ring->dirty++; + } + + if (!processed) + return 0; + + netdev_tx_completed_queue(txq, tx_packets, tx_bytes); + + if ((ring->tx.queue_stopped == 1) && + (xlgmac_tx_avail_desc(ring) > XLGMAC_TX_DESC_MIN_FREE)) { + ring->tx.queue_stopped = 0; + netif_tx_wake_queue(txq); + } + + XLGMAC_PR("processed=%d\n", processed); + + return processed; +} + +static int xlgmac_rx_poll(struct xlgmac_channel *channel, int budget) +{ + struct xlgmac_pdata *pdata = channel->pdata; + struct xlgmac_ring *ring = channel->rx_ring; + struct net_device *netdev = pdata->netdev; + unsigned int len, dma_desc_len, max_len; + unsigned int context_next, context; + struct xlgmac_desc_data *desc_data; + struct xlgmac_pkt_info *pkt_info; + unsigned int incomplete, error; + struct xlgmac_hw_ops *hw_ops; + unsigned int received = 0; + struct napi_struct *napi; + struct sk_buff *skb; + int packet_count = 0; + + hw_ops = &pdata->hw_ops; + + /* Nothing to do if there isn't a Rx ring for this channel */ + if (!ring) + return 0; + + incomplete = 0; + context_next = 0; + + napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi; + + desc_data = XLGMAC_GET_DESC_DATA(ring, ring->cur); + pkt_info = &ring->pkt_info; + while (packet_count < budget) { + /* First time in loop see if we need to restore state */ + if (!received && desc_data->state_saved) { + skb = desc_data->state.skb; + error = desc_data->state.error; + len = desc_data->state.len; + } else { + memset(pkt_info, 0, sizeof(*pkt_info)); + skb = NULL; + error = 0; + len = 0; + } + +read_again: + desc_data = XLGMAC_GET_DESC_DATA(ring, ring->cur); + + if (xlgmac_rx_dirty_desc(ring) > XLGMAC_RX_DESC_MAX_DIRTY) + xlgmac_rx_refresh(channel); + + if (hw_ops->dev_read(channel)) + break; + + received++; + ring->cur++; + + incomplete = XLGMAC_GET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_INCOMPLETE_POS, + RX_PACKET_ATTRIBUTES_INCOMPLETE_LEN); + context_next = XLGMAC_GET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_POS, + RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_LEN); + context = XLGMAC_GET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_CONTEXT_POS, + RX_PACKET_ATTRIBUTES_CONTEXT_LEN); + + /* Earlier error, just drain the remaining data */ + if ((incomplete || context_next) && error) + goto read_again; + + if (error || pkt_info->errors) { + if (pkt_info->errors) + netif_err(pdata, rx_err, netdev, + "error in received packet\n"); + dev_kfree_skb(skb); + goto next_packet; + } + + if (!context) { + /* Length is cumulative, get this descriptor's length */ + dma_desc_len = desc_data->rx.len - len; + len += dma_desc_len; + + if (dma_desc_len && !skb) { + skb = xlgmac_create_skb(pdata, napi, desc_data, + dma_desc_len); + if (!skb) + error = 1; + } else if (dma_desc_len) { + dma_sync_single_range_for_cpu( + pdata->dev, + desc_data->rx.buf.dma_base, + desc_data->rx.buf.dma_off, + desc_data->rx.buf.dma_len, + DMA_FROM_DEVICE); + + skb_add_rx_frag( + skb, skb_shinfo(skb)->nr_frags, + desc_data->rx.buf.pa.pages, + desc_data->rx.buf.pa.pages_offset, + dma_desc_len, + desc_data->rx.buf.dma_len); + desc_data->rx.buf.pa.pages = NULL; + } + } + + if (incomplete || context_next) + goto read_again; + + if (!skb) + goto next_packet; + + /* Be sure we don't exceed the configured MTU */ + max_len = netdev->mtu + ETH_HLEN; + if (!(netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && + (skb->protocol == htons(ETH_P_8021Q))) + max_len += VLAN_HLEN; + + if (skb->len > max_len) { + netif_err(pdata, rx_err, netdev, + "packet length exceeds configured MTU\n"); + dev_kfree_skb(skb); + goto next_packet; + } + + if (netif_msg_pktdata(pdata)) + xlgmac_print_pkt(netdev, skb, false); + + skb_checksum_none_assert(skb); + if (XLGMAC_GET_REG_BITS(pkt_info->attributes, + RX_PACKET_ATTRIBUTES_CSUM_DONE_POS, + RX_PACKET_ATTRIBUTES_CSUM_DONE_LEN)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (XLGMAC_GET_REG_BITS(pkt_info->attributes, + RX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, + RX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN)) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + pkt_info->vlan_ctag); + + if (XLGMAC_GET_REG_BITS(pkt_info->attributes, + RX_PACKET_ATTRIBUTES_RSS_HASH_POS, + RX_PACKET_ATTRIBUTES_RSS_HASH_LEN)) + skb_set_hash(skb, pkt_info->rss_hash, + pkt_info->rss_hash_type); + + skb->dev = netdev; + skb->protocol = eth_type_trans(skb, netdev); + skb_record_rx_queue(skb, channel->queue_index); + + napi_gro_receive(napi, skb); + +next_packet: + packet_count++; + } + + /* Check if we need to save state before leaving */ + if (received && (incomplete || context_next)) { + desc_data = XLGMAC_GET_DESC_DATA(ring, ring->cur); + desc_data->state_saved = 1; + desc_data->state.skb = skb; + desc_data->state.len = len; + desc_data->state.error = error; + } + + XLGMAC_PR("packet_count = %d\n", packet_count); + + return packet_count; +} + +static int xlgmac_one_poll(struct napi_struct *napi, int budget) +{ + struct xlgmac_channel *channel = container_of(napi, + struct xlgmac_channel, + napi); + int processed = 0; + + XLGMAC_PR("budget=%d\n", budget); + + /* Cleanup Tx ring first */ + xlgmac_tx_poll(channel); + + /* Process Rx ring next */ + processed = xlgmac_rx_poll(channel, budget); + + /* If we processed everything, we are done */ + if (processed < budget) { + /* Turn off polling */ + napi_complete_done(napi, processed); + + /* Enable Tx and Rx interrupts */ + enable_irq(channel->dma_irq); + } + + XLGMAC_PR("received = %d\n", processed); + + return processed; +} + +static int xlgmac_all_poll(struct napi_struct *napi, int budget) +{ + struct xlgmac_pdata *pdata = container_of(napi, + struct xlgmac_pdata, + napi); + struct xlgmac_channel *channel; + int processed, last_processed; + int ring_budget; + unsigned int i; + + XLGMAC_PR("budget=%d\n", budget); + + processed = 0; + ring_budget = budget / pdata->rx_ring_count; + do { + last_processed = processed; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + /* Cleanup Tx ring first */ + xlgmac_tx_poll(channel); + + /* Process Rx ring next */ + if (ring_budget > (budget - processed)) + ring_budget = budget - processed; + processed += xlgmac_rx_poll(channel, ring_budget); + } + } while ((processed < budget) && (processed != last_processed)); + + /* If we processed everything, we are done */ + if (processed < budget) { + /* Turn off polling */ + napi_complete_done(napi, processed); + + /* Enable Tx and Rx interrupts */ + xlgmac_enable_rx_tx_ints(pdata); + } + + XLGMAC_PR("received = %d\n", processed); + + return processed; +} diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-pci.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-pci.c new file mode 100644 index 000000000000..504e80de7bba --- /dev/null +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-pci.c @@ -0,0 +1,80 @@ +/* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver + * + * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This Synopsys DWC XLGMAC software driver and associated documentation + * (hereinafter the "Software") is an unsupported proprietary work of + * Synopsys, Inc. unless otherwise expressly agreed to in writing between + * Synopsys and you. The Software IS NOT an item of Licensed Software or a + * Licensed Product under any End User Software License Agreement or + * Agreement for Licensed Products with Synopsys or any supplement thereto. + * Synopsys is a registered trademark of Synopsys, Inc. Other names included + * in the SOFTWARE may be the trademarks of their respective owners. + */ + +#include +#include +#include + +#include "dwc-xlgmac.h" +#include "dwc-xlgmac-reg.h" + +static int xlgmac_probe(struct pci_dev *pcidev, const struct pci_device_id *id) +{ + struct device *dev = &pcidev->dev; + struct xlgmac_resources res; + int i, ret; + + ret = pcim_enable_device(pcidev); + if (ret) { + dev_err(dev, "ERROR: failed to enable device\n"); + return ret; + } + + for (i = 0; i <= PCI_STD_RESOURCE_END; i++) { + if (pci_resource_len(pcidev, i) == 0) + continue; + ret = pcim_iomap_regions(pcidev, BIT(i), XLGMAC_DRV_NAME); + if (ret) + return ret; + break; + } + + pci_set_master(pcidev); + + memset(&res, 0, sizeof(res)); + res.irq = pcidev->irq; + res.addr = pcim_iomap_table(pcidev)[i]; + + return xlgmac_drv_probe(&pcidev->dev, &res); +} + +static void xlgmac_remove(struct pci_dev *pcidev) +{ + xlgmac_drv_remove(&pcidev->dev); +} + +static const struct pci_device_id xlgmac_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, 0x7302) }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, xlgmac_pci_tbl); + +static struct pci_driver xlgmac_pci_driver = { + .name = XLGMAC_DRV_NAME, + .id_table = xlgmac_pci_tbl, + .probe = xlgmac_probe, + .remove = xlgmac_remove, +}; + +module_pci_driver(xlgmac_pci_driver); + +MODULE_DESCRIPTION(XLGMAC_DRV_DESC); +MODULE_VERSION(XLGMAC_DRV_VERSION); +MODULE_AUTHOR("Jie Deng "); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-reg.h b/drivers/net/ethernet/synopsys/dwc-xlgmac-reg.h new file mode 100644 index 000000000000..782448128a89 --- /dev/null +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-reg.h @@ -0,0 +1,746 @@ +/* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver + * + * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This Synopsys DWC XLGMAC software driver and associated documentation + * (hereinafter the "Software") is an unsupported proprietary work of + * Synopsys, Inc. unless otherwise expressly agreed to in writing between + * Synopsys and you. The Software IS NOT an item of Licensed Software or a + * Licensed Product under any End User Software License Agreement or + * Agreement for Licensed Products with Synopsys or any supplement thereto. + * Synopsys is a registered trademark of Synopsys, Inc. Other names included + * in the SOFTWARE may be the trademarks of their respective owners. + */ + +#ifndef __DWC_XLGMAC_REG_H__ +#define __DWC_XLGMAC_REG_H__ + +/* MAC register offsets */ +#define MAC_TCR 0x0000 +#define MAC_RCR 0x0004 +#define MAC_PFR 0x0008 +#define MAC_HTR0 0x0010 +#define MAC_VLANTR 0x0050 +#define MAC_VLANHTR 0x0058 +#define MAC_VLANIR 0x0060 +#define MAC_Q0TFCR 0x0070 +#define MAC_RFCR 0x0090 +#define MAC_RQC0R 0x00a0 +#define MAC_RQC1R 0x00a4 +#define MAC_RQC2R 0x00a8 +#define MAC_RQC3R 0x00ac +#define MAC_ISR 0x00b0 +#define MAC_IER 0x00b4 +#define MAC_VR 0x0110 +#define MAC_HWF0R 0x011c +#define MAC_HWF1R 0x0120 +#define MAC_HWF2R 0x0124 +#define MAC_MACA0HR 0x0300 +#define MAC_MACA0LR 0x0304 +#define MAC_MACA1HR 0x0308 +#define MAC_MACA1LR 0x030c +#define MAC_RSSCR 0x0c80 +#define MAC_RSSAR 0x0c88 +#define MAC_RSSDR 0x0c8c + +#define MAC_QTFCR_INC 4 +#define MAC_MACA_INC 4 +#define MAC_HTR_INC 4 +#define MAC_RQC2_INC 4 +#define MAC_RQC2_Q_PER_REG 4 + +/* MAC register entry bit positions and sizes */ +#define MAC_HWF0R_ADDMACADRSEL_POS 18 +#define MAC_HWF0R_ADDMACADRSEL_LEN 5 +#define MAC_HWF0R_ARPOFFSEL_POS 9 +#define MAC_HWF0R_ARPOFFSEL_LEN 1 +#define MAC_HWF0R_EEESEL_POS 13 +#define MAC_HWF0R_EEESEL_LEN 1 +#define MAC_HWF0R_PHYIFSEL_POS 1 +#define MAC_HWF0R_PHYIFSEL_LEN 2 +#define MAC_HWF0R_MGKSEL_POS 7 +#define MAC_HWF0R_MGKSEL_LEN 1 +#define MAC_HWF0R_MMCSEL_POS 8 +#define MAC_HWF0R_MMCSEL_LEN 1 +#define MAC_HWF0R_RWKSEL_POS 6 +#define MAC_HWF0R_RWKSEL_LEN 1 +#define MAC_HWF0R_RXCOESEL_POS 16 +#define MAC_HWF0R_RXCOESEL_LEN 1 +#define MAC_HWF0R_SAVLANINS_POS 27 +#define MAC_HWF0R_SAVLANINS_LEN 1 +#define MAC_HWF0R_SMASEL_POS 5 +#define MAC_HWF0R_SMASEL_LEN 1 +#define MAC_HWF0R_TSSEL_POS 12 +#define MAC_HWF0R_TSSEL_LEN 1 +#define MAC_HWF0R_TSSTSSEL_POS 25 +#define MAC_HWF0R_TSSTSSEL_LEN 2 +#define MAC_HWF0R_TXCOESEL_POS 14 +#define MAC_HWF0R_TXCOESEL_LEN 1 +#define MAC_HWF0R_VLHASH_POS 4 +#define MAC_HWF0R_VLHASH_LEN 1 +#define MAC_HWF1R_ADDR64_POS 14 +#define MAC_HWF1R_ADDR64_LEN 2 +#define MAC_HWF1R_ADVTHWORD_POS 13 +#define MAC_HWF1R_ADVTHWORD_LEN 1 +#define MAC_HWF1R_DBGMEMA_POS 19 +#define MAC_HWF1R_DBGMEMA_LEN 1 +#define MAC_HWF1R_DCBEN_POS 16 +#define MAC_HWF1R_DCBEN_LEN 1 +#define MAC_HWF1R_HASHTBLSZ_POS 24 +#define MAC_HWF1R_HASHTBLSZ_LEN 3 +#define MAC_HWF1R_L3L4FNUM_POS 27 +#define MAC_HWF1R_L3L4FNUM_LEN 4 +#define MAC_HWF1R_NUMTC_POS 21 +#define MAC_HWF1R_NUMTC_LEN 3 +#define MAC_HWF1R_RSSEN_POS 20 +#define MAC_HWF1R_RSSEN_LEN 1 +#define MAC_HWF1R_RXFIFOSIZE_POS 0 +#define MAC_HWF1R_RXFIFOSIZE_LEN 5 +#define MAC_HWF1R_SPHEN_POS 17 +#define MAC_HWF1R_SPHEN_LEN 1 +#define MAC_HWF1R_TSOEN_POS 18 +#define MAC_HWF1R_TSOEN_LEN 1 +#define MAC_HWF1R_TXFIFOSIZE_POS 6 +#define MAC_HWF1R_TXFIFOSIZE_LEN 5 +#define MAC_HWF2R_AUXSNAPNUM_POS 28 +#define MAC_HWF2R_AUXSNAPNUM_LEN 3 +#define MAC_HWF2R_PPSOUTNUM_POS 24 +#define MAC_HWF2R_PPSOUTNUM_LEN 3 +#define MAC_HWF2R_RXCHCNT_POS 12 +#define MAC_HWF2R_RXCHCNT_LEN 4 +#define MAC_HWF2R_RXQCNT_POS 0 +#define MAC_HWF2R_RXQCNT_LEN 4 +#define MAC_HWF2R_TXCHCNT_POS 18 +#define MAC_HWF2R_TXCHCNT_LEN 4 +#define MAC_HWF2R_TXQCNT_POS 6 +#define MAC_HWF2R_TXQCNT_LEN 4 +#define MAC_IER_TSIE_POS 12 +#define MAC_IER_TSIE_LEN 1 +#define MAC_ISR_MMCRXIS_POS 9 +#define MAC_ISR_MMCRXIS_LEN 1 +#define MAC_ISR_MMCTXIS_POS 10 +#define MAC_ISR_MMCTXIS_LEN 1 +#define MAC_ISR_PMTIS_POS 4 +#define MAC_ISR_PMTIS_LEN 1 +#define MAC_ISR_TSIS_POS 12 +#define MAC_ISR_TSIS_LEN 1 +#define MAC_MACA1HR_AE_POS 31 +#define MAC_MACA1HR_AE_LEN 1 +#define MAC_PFR_HMC_POS 2 +#define MAC_PFR_HMC_LEN 1 +#define MAC_PFR_HPF_POS 10 +#define MAC_PFR_HPF_LEN 1 +#define MAC_PFR_HUC_POS 1 +#define MAC_PFR_HUC_LEN 1 +#define MAC_PFR_PM_POS 4 +#define MAC_PFR_PM_LEN 1 +#define MAC_PFR_PR_POS 0 +#define MAC_PFR_PR_LEN 1 +#define MAC_PFR_VTFE_POS 16 +#define MAC_PFR_VTFE_LEN 1 +#define MAC_Q0TFCR_PT_POS 16 +#define MAC_Q0TFCR_PT_LEN 16 +#define MAC_Q0TFCR_TFE_POS 1 +#define MAC_Q0TFCR_TFE_LEN 1 +#define MAC_RCR_ACS_POS 1 +#define MAC_RCR_ACS_LEN 1 +#define MAC_RCR_CST_POS 2 +#define MAC_RCR_CST_LEN 1 +#define MAC_RCR_DCRCC_POS 3 +#define MAC_RCR_DCRCC_LEN 1 +#define MAC_RCR_HDSMS_POS 12 +#define MAC_RCR_HDSMS_LEN 3 +#define MAC_RCR_IPC_POS 9 +#define MAC_RCR_IPC_LEN 1 +#define MAC_RCR_JE_POS 8 +#define MAC_RCR_JE_LEN 1 +#define MAC_RCR_LM_POS 10 +#define MAC_RCR_LM_LEN 1 +#define MAC_RCR_RE_POS 0 +#define MAC_RCR_RE_LEN 1 +#define MAC_RFCR_PFCE_POS 8 +#define MAC_RFCR_PFCE_LEN 1 +#define MAC_RFCR_RFE_POS 0 +#define MAC_RFCR_RFE_LEN 1 +#define MAC_RFCR_UP_POS 1 +#define MAC_RFCR_UP_LEN 1 +#define MAC_RQC0R_RXQ0EN_POS 0 +#define MAC_RQC0R_RXQ0EN_LEN 2 +#define MAC_RSSAR_ADDRT_POS 2 +#define MAC_RSSAR_ADDRT_LEN 1 +#define MAC_RSSAR_CT_POS 1 +#define MAC_RSSAR_CT_LEN 1 +#define MAC_RSSAR_OB_POS 0 +#define MAC_RSSAR_OB_LEN 1 +#define MAC_RSSAR_RSSIA_POS 8 +#define MAC_RSSAR_RSSIA_LEN 8 +#define MAC_RSSCR_IP2TE_POS 1 +#define MAC_RSSCR_IP2TE_LEN 1 +#define MAC_RSSCR_RSSE_POS 0 +#define MAC_RSSCR_RSSE_LEN 1 +#define MAC_RSSCR_TCP4TE_POS 2 +#define MAC_RSSCR_TCP4TE_LEN 1 +#define MAC_RSSCR_UDP4TE_POS 3 +#define MAC_RSSCR_UDP4TE_LEN 1 +#define MAC_RSSDR_DMCH_POS 0 +#define MAC_RSSDR_DMCH_LEN 4 +#define MAC_TCR_SS_POS 28 +#define MAC_TCR_SS_LEN 3 +#define MAC_TCR_TE_POS 0 +#define MAC_TCR_TE_LEN 1 +#define MAC_VLANHTR_VLHT_POS 0 +#define MAC_VLANHTR_VLHT_LEN 16 +#define MAC_VLANIR_VLTI_POS 20 +#define MAC_VLANIR_VLTI_LEN 1 +#define MAC_VLANIR_CSVL_POS 19 +#define MAC_VLANIR_CSVL_LEN 1 +#define MAC_VLANTR_DOVLTC_POS 20 +#define MAC_VLANTR_DOVLTC_LEN 1 +#define MAC_VLANTR_ERSVLM_POS 19 +#define MAC_VLANTR_ERSVLM_LEN 1 +#define MAC_VLANTR_ESVL_POS 18 +#define MAC_VLANTR_ESVL_LEN 1 +#define MAC_VLANTR_ETV_POS 16 +#define MAC_VLANTR_ETV_LEN 1 +#define MAC_VLANTR_EVLS_POS 21 +#define MAC_VLANTR_EVLS_LEN 2 +#define MAC_VLANTR_EVLRXS_POS 24 +#define MAC_VLANTR_EVLRXS_LEN 1 +#define MAC_VLANTR_VL_POS 0 +#define MAC_VLANTR_VL_LEN 16 +#define MAC_VLANTR_VTHM_POS 25 +#define MAC_VLANTR_VTHM_LEN 1 +#define MAC_VLANTR_VTIM_POS 17 +#define MAC_VLANTR_VTIM_LEN 1 +#define MAC_VR_DEVID_POS 8 +#define MAC_VR_DEVID_LEN 8 +#define MAC_VR_SNPSVER_POS 0 +#define MAC_VR_SNPSVER_LEN 8 +#define MAC_VR_USERVER_POS 16 +#define MAC_VR_USERVER_LEN 8 + +/* MMC register offsets */ +#define MMC_CR 0x0800 +#define MMC_RISR 0x0804 +#define MMC_TISR 0x0808 +#define MMC_RIER 0x080c +#define MMC_TIER 0x0810 +#define MMC_TXOCTETCOUNT_GB_LO 0x0814 +#define MMC_TXFRAMECOUNT_GB_LO 0x081c +#define MMC_TXBROADCASTFRAMES_G_LO 0x0824 +#define MMC_TXMULTICASTFRAMES_G_LO 0x082c +#define MMC_TX64OCTETS_GB_LO 0x0834 +#define MMC_TX65TO127OCTETS_GB_LO 0x083c +#define MMC_TX128TO255OCTETS_GB_LO 0x0844 +#define MMC_TX256TO511OCTETS_GB_LO 0x084c +#define MMC_TX512TO1023OCTETS_GB_LO 0x0854 +#define MMC_TX1024TOMAXOCTETS_GB_LO 0x085c +#define MMC_TXUNICASTFRAMES_GB_LO 0x0864 +#define MMC_TXMULTICASTFRAMES_GB_LO 0x086c +#define MMC_TXBROADCASTFRAMES_GB_LO 0x0874 +#define MMC_TXUNDERFLOWERROR_LO 0x087c +#define MMC_TXOCTETCOUNT_G_LO 0x0884 +#define MMC_TXFRAMECOUNT_G_LO 0x088c +#define MMC_TXPAUSEFRAMES_LO 0x0894 +#define MMC_TXVLANFRAMES_G_LO 0x089c +#define MMC_RXFRAMECOUNT_GB_LO 0x0900 +#define MMC_RXOCTETCOUNT_GB_LO 0x0908 +#define MMC_RXOCTETCOUNT_G_LO 0x0910 +#define MMC_RXBROADCASTFRAMES_G_LO 0x0918 +#define MMC_RXMULTICASTFRAMES_G_LO 0x0920 +#define MMC_RXCRCERROR_LO 0x0928 +#define MMC_RXRUNTERROR 0x0930 +#define MMC_RXJABBERERROR 0x0934 +#define MMC_RXUNDERSIZE_G 0x0938 +#define MMC_RXOVERSIZE_G 0x093c +#define MMC_RX64OCTETS_GB_LO 0x0940 +#define MMC_RX65TO127OCTETS_GB_LO 0x0948 +#define MMC_RX128TO255OCTETS_GB_LO 0x0950 +#define MMC_RX256TO511OCTETS_GB_LO 0x0958 +#define MMC_RX512TO1023OCTETS_GB_LO 0x0960 +#define MMC_RX1024TOMAXOCTETS_GB_LO 0x0968 +#define MMC_RXUNICASTFRAMES_G_LO 0x0970 +#define MMC_RXLENGTHERROR_LO 0x0978 +#define MMC_RXOUTOFRANGETYPE_LO 0x0980 +#define MMC_RXPAUSEFRAMES_LO 0x0988 +#define MMC_RXFIFOOVERFLOW_LO 0x0990 +#define MMC_RXVLANFRAMES_GB_LO 0x0998 +#define MMC_RXWATCHDOGERROR 0x09a0 + +/* MMC register entry bit positions and sizes */ +#define MMC_CR_CR_POS 0 +#define MMC_CR_CR_LEN 1 +#define MMC_CR_CSR_POS 1 +#define MMC_CR_CSR_LEN 1 +#define MMC_CR_ROR_POS 2 +#define MMC_CR_ROR_LEN 1 +#define MMC_CR_MCF_POS 3 +#define MMC_CR_MCF_LEN 1 +#define MMC_CR_MCT_POS 4 +#define MMC_CR_MCT_LEN 2 +#define MMC_RIER_ALL_INTERRUPTS_POS 0 +#define MMC_RIER_ALL_INTERRUPTS_LEN 23 +#define MMC_RISR_RXFRAMECOUNT_GB_POS 0 +#define MMC_RISR_RXFRAMECOUNT_GB_LEN 1 +#define MMC_RISR_RXOCTETCOUNT_GB_POS 1 +#define MMC_RISR_RXOCTETCOUNT_GB_LEN 1 +#define MMC_RISR_RXOCTETCOUNT_G_POS 2 +#define MMC_RISR_RXOCTETCOUNT_G_LEN 1 +#define MMC_RISR_RXBROADCASTFRAMES_G_POS 3 +#define MMC_RISR_RXBROADCASTFRAMES_G_LEN 1 +#define MMC_RISR_RXMULTICASTFRAMES_G_POS 4 +#define MMC_RISR_RXMULTICASTFRAMES_G_LEN 1 +#define MMC_RISR_RXCRCERROR_POS 5 +#define MMC_RISR_RXCRCERROR_LEN 1 +#define MMC_RISR_RXRUNTERROR_POS 6 +#define MMC_RISR_RXRUNTERROR_LEN 1 +#define MMC_RISR_RXJABBERERROR_POS 7 +#define MMC_RISR_RXJABBERERROR_LEN 1 +#define MMC_RISR_RXUNDERSIZE_G_POS 8 +#define MMC_RISR_RXUNDERSIZE_G_LEN 1 +#define MMC_RISR_RXOVERSIZE_G_POS 9 +#define MMC_RISR_RXOVERSIZE_G_LEN 1 +#define MMC_RISR_RX64OCTETS_GB_POS 10 +#define MMC_RISR_RX64OCTETS_GB_LEN 1 +#define MMC_RISR_RX65TO127OCTETS_GB_POS 11 +#define MMC_RISR_RX65TO127OCTETS_GB_LEN 1 +#define MMC_RISR_RX128TO255OCTETS_GB_POS 12 +#define MMC_RISR_RX128TO255OCTETS_GB_LEN 1 +#define MMC_RISR_RX256TO511OCTETS_GB_POS 13 +#define MMC_RISR_RX256TO511OCTETS_GB_LEN 1 +#define MMC_RISR_RX512TO1023OCTETS_GB_POS 14 +#define MMC_RISR_RX512TO1023OCTETS_GB_LEN 1 +#define MMC_RISR_RX1024TOMAXOCTETS_GB_POS 15 +#define MMC_RISR_RX1024TOMAXOCTETS_GB_LEN 1 +#define MMC_RISR_RXUNICASTFRAMES_G_POS 16 +#define MMC_RISR_RXUNICASTFRAMES_G_LEN 1 +#define MMC_RISR_RXLENGTHERROR_POS 17 +#define MMC_RISR_RXLENGTHERROR_LEN 1 +#define MMC_RISR_RXOUTOFRANGETYPE_POS 18 +#define MMC_RISR_RXOUTOFRANGETYPE_LEN 1 +#define MMC_RISR_RXPAUSEFRAMES_POS 19 +#define MMC_RISR_RXPAUSEFRAMES_LEN 1 +#define MMC_RISR_RXFIFOOVERFLOW_POS 20 +#define MMC_RISR_RXFIFOOVERFLOW_LEN 1 +#define MMC_RISR_RXVLANFRAMES_GB_POS 21 +#define MMC_RISR_RXVLANFRAMES_GB_LEN 1 +#define MMC_RISR_RXWATCHDOGERROR_POS 22 +#define MMC_RISR_RXWATCHDOGERROR_LEN 1 +#define MMC_TIER_ALL_INTERRUPTS_POS 0 +#define MMC_TIER_ALL_INTERRUPTS_LEN 18 +#define MMC_TISR_TXOCTETCOUNT_GB_POS 0 +#define MMC_TISR_TXOCTETCOUNT_GB_LEN 1 +#define MMC_TISR_TXFRAMECOUNT_GB_POS 1 +#define MMC_TISR_TXFRAMECOUNT_GB_LEN 1 +#define MMC_TISR_TXBROADCASTFRAMES_G_POS 2 +#define MMC_TISR_TXBROADCASTFRAMES_G_LEN 1 +#define MMC_TISR_TXMULTICASTFRAMES_G_POS 3 +#define MMC_TISR_TXMULTICASTFRAMES_G_LEN 1 +#define MMC_TISR_TX64OCTETS_GB_POS 4 +#define MMC_TISR_TX64OCTETS_GB_LEN 1 +#define MMC_TISR_TX65TO127OCTETS_GB_POS 5 +#define MMC_TISR_TX65TO127OCTETS_GB_LEN 1 +#define MMC_TISR_TX128TO255OCTETS_GB_POS 6 +#define MMC_TISR_TX128TO255OCTETS_GB_LEN 1 +#define MMC_TISR_TX256TO511OCTETS_GB_POS 7 +#define MMC_TISR_TX256TO511OCTETS_GB_LEN 1 +#define MMC_TISR_TX512TO1023OCTETS_GB_POS 8 +#define MMC_TISR_TX512TO1023OCTETS_GB_LEN 1 +#define MMC_TISR_TX1024TOMAXOCTETS_GB_POS 9 +#define MMC_TISR_TX1024TOMAXOCTETS_GB_LEN 1 +#define MMC_TISR_TXUNICASTFRAMES_GB_POS 10 +#define MMC_TISR_TXUNICASTFRAMES_GB_LEN 1 +#define MMC_TISR_TXMULTICASTFRAMES_GB_POS 11 +#define MMC_TISR_TXMULTICASTFRAMES_GB_LEN 1 +#define MMC_TISR_TXBROADCASTFRAMES_GB_POS 12 +#define MMC_TISR_TXBROADCASTFRAMES_GB_LEN 1 +#define MMC_TISR_TXUNDERFLOWERROR_POS 13 +#define MMC_TISR_TXUNDERFLOWERROR_LEN 1 +#define MMC_TISR_TXOCTETCOUNT_G_POS 14 +#define MMC_TISR_TXOCTETCOUNT_G_LEN 1 +#define MMC_TISR_TXFRAMECOUNT_G_POS 15 +#define MMC_TISR_TXFRAMECOUNT_G_LEN 1 +#define MMC_TISR_TXPAUSEFRAMES_POS 16 +#define MMC_TISR_TXPAUSEFRAMES_LEN 1 +#define MMC_TISR_TXVLANFRAMES_G_POS 17 +#define MMC_TISR_TXVLANFRAMES_G_LEN 1 + +/* MTL register offsets */ +#define MTL_OMR 0x1000 +#define MTL_FDDR 0x1010 +#define MTL_RQDCM0R 0x1030 + +#define MTL_RQDCM_INC 4 +#define MTL_RQDCM_Q_PER_REG 4 + +/* MTL register entry bit positions and sizes */ +#define MTL_OMR_ETSALG_POS 5 +#define MTL_OMR_ETSALG_LEN 2 +#define MTL_OMR_RAA_POS 2 +#define MTL_OMR_RAA_LEN 1 + +/* MTL queue register offsets + * Multiple queues can be active. The first queue has registers + * that begin at 0x1100. Each subsequent queue has registers that + * are accessed using an offset of 0x80 from the previous queue. + */ +#define MTL_Q_BASE 0x1100 +#define MTL_Q_INC 0x80 + +#define MTL_Q_TQOMR 0x00 +#define MTL_Q_RQOMR 0x40 +#define MTL_Q_RQDR 0x48 +#define MTL_Q_RQFCR 0x50 +#define MTL_Q_IER 0x70 +#define MTL_Q_ISR 0x74 + +/* MTL queue register entry bit positions and sizes */ +#define MTL_Q_RQDR_PRXQ_POS 16 +#define MTL_Q_RQDR_PRXQ_LEN 14 +#define MTL_Q_RQDR_RXQSTS_POS 4 +#define MTL_Q_RQDR_RXQSTS_LEN 2 +#define MTL_Q_RQFCR_RFA_POS 1 +#define MTL_Q_RQFCR_RFA_LEN 6 +#define MTL_Q_RQFCR_RFD_POS 17 +#define MTL_Q_RQFCR_RFD_LEN 6 +#define MTL_Q_RQOMR_EHFC_POS 7 +#define MTL_Q_RQOMR_EHFC_LEN 1 +#define MTL_Q_RQOMR_RQS_POS 16 +#define MTL_Q_RQOMR_RQS_LEN 9 +#define MTL_Q_RQOMR_RSF_POS 5 +#define MTL_Q_RQOMR_RSF_LEN 1 +#define MTL_Q_RQOMR_FEP_POS 4 +#define MTL_Q_RQOMR_FEP_LEN 1 +#define MTL_Q_RQOMR_FUP_POS 3 +#define MTL_Q_RQOMR_FUP_LEN 1 +#define MTL_Q_RQOMR_RTC_POS 0 +#define MTL_Q_RQOMR_RTC_LEN 2 +#define MTL_Q_TQOMR_FTQ_POS 0 +#define MTL_Q_TQOMR_FTQ_LEN 1 +#define MTL_Q_TQOMR_Q2TCMAP_POS 8 +#define MTL_Q_TQOMR_Q2TCMAP_LEN 3 +#define MTL_Q_TQOMR_TQS_POS 16 +#define MTL_Q_TQOMR_TQS_LEN 10 +#define MTL_Q_TQOMR_TSF_POS 1 +#define MTL_Q_TQOMR_TSF_LEN 1 +#define MTL_Q_TQOMR_TTC_POS 4 +#define MTL_Q_TQOMR_TTC_LEN 3 +#define MTL_Q_TQOMR_TXQEN_POS 2 +#define MTL_Q_TQOMR_TXQEN_LEN 2 + +/* MTL queue register value */ +#define MTL_RSF_DISABLE 0x00 +#define MTL_RSF_ENABLE 0x01 +#define MTL_TSF_DISABLE 0x00 +#define MTL_TSF_ENABLE 0x01 + +#define MTL_RX_THRESHOLD_64 0x00 +#define MTL_RX_THRESHOLD_96 0x02 +#define MTL_RX_THRESHOLD_128 0x03 +#define MTL_TX_THRESHOLD_64 0x00 +#define MTL_TX_THRESHOLD_96 0x02 +#define MTL_TX_THRESHOLD_128 0x03 +#define MTL_TX_THRESHOLD_192 0x04 +#define MTL_TX_THRESHOLD_256 0x05 +#define MTL_TX_THRESHOLD_384 0x06 +#define MTL_TX_THRESHOLD_512 0x07 + +#define MTL_ETSALG_WRR 0x00 +#define MTL_ETSALG_WFQ 0x01 +#define MTL_ETSALG_DWRR 0x02 +#define MTL_RAA_SP 0x00 +#define MTL_RAA_WSP 0x01 + +#define MTL_Q_DISABLED 0x00 +#define MTL_Q_ENABLED 0x02 + +#define MTL_RQDCM0R_Q0MDMACH 0x0 +#define MTL_RQDCM0R_Q1MDMACH 0x00000100 +#define MTL_RQDCM0R_Q2MDMACH 0x00020000 +#define MTL_RQDCM0R_Q3MDMACH 0x03000000 +#define MTL_RQDCM1R_Q4MDMACH 0x00000004 +#define MTL_RQDCM1R_Q5MDMACH 0x00000500 +#define MTL_RQDCM1R_Q6MDMACH 0x00060000 +#define MTL_RQDCM1R_Q7MDMACH 0x07000000 +#define MTL_RQDCM2R_Q8MDMACH 0x00000008 +#define MTL_RQDCM2R_Q9MDMACH 0x00000900 +#define MTL_RQDCM2R_Q10MDMACH 0x000A0000 +#define MTL_RQDCM2R_Q11MDMACH 0x0B000000 + +/* MTL traffic class register offsets + * Multiple traffic classes can be active. The first class has registers + * that begin at 0x1100. Each subsequent queue has registers that + * are accessed using an offset of 0x80 from the previous queue. + */ +#define MTL_TC_BASE MTL_Q_BASE +#define MTL_TC_INC MTL_Q_INC + +#define MTL_TC_ETSCR 0x10 +#define MTL_TC_ETSSR 0x14 +#define MTL_TC_QWR 0x18 + +/* MTL traffic class register entry bit positions and sizes */ +#define MTL_TC_ETSCR_TSA_POS 0 +#define MTL_TC_ETSCR_TSA_LEN 2 +#define MTL_TC_QWR_QW_POS 0 +#define MTL_TC_QWR_QW_LEN 21 + +/* MTL traffic class register value */ +#define MTL_TSA_SP 0x00 +#define MTL_TSA_ETS 0x02 + +/* DMA register offsets */ +#define DMA_MR 0x3000 +#define DMA_SBMR 0x3004 +#define DMA_ISR 0x3008 +#define DMA_DSR0 0x3020 +#define DMA_DSR1 0x3024 + +/* DMA register entry bit positions and sizes */ +#define DMA_ISR_MACIS_POS 17 +#define DMA_ISR_MACIS_LEN 1 +#define DMA_ISR_MTLIS_POS 16 +#define DMA_ISR_MTLIS_LEN 1 +#define DMA_MR_SWR_POS 0 +#define DMA_MR_SWR_LEN 1 +#define DMA_SBMR_EAME_POS 11 +#define DMA_SBMR_EAME_LEN 1 +#define DMA_SBMR_BLEN_64_POS 5 +#define DMA_SBMR_BLEN_64_LEN 1 +#define DMA_SBMR_BLEN_128_POS 6 +#define DMA_SBMR_BLEN_128_LEN 1 +#define DMA_SBMR_BLEN_256_POS 7 +#define DMA_SBMR_BLEN_256_LEN 1 +#define DMA_SBMR_UNDEF_POS 0 +#define DMA_SBMR_UNDEF_LEN 1 + +/* DMA register values */ +#define DMA_DSR_RPS_LEN 4 +#define DMA_DSR_TPS_LEN 4 +#define DMA_DSR_Q_LEN (DMA_DSR_RPS_LEN + DMA_DSR_TPS_LEN) +#define DMA_DSR0_TPS_START 12 +#define DMA_DSRX_FIRST_QUEUE 3 +#define DMA_DSRX_INC 4 +#define DMA_DSRX_QPR 4 +#define DMA_DSRX_TPS_START 4 +#define DMA_TPS_STOPPED 0x00 +#define DMA_TPS_SUSPENDED 0x06 + +/* DMA channel register offsets + * Multiple channels can be active. The first channel has registers + * that begin at 0x3100. Each subsequent channel has registers that + * are accessed using an offset of 0x80 from the previous channel. + */ +#define DMA_CH_BASE 0x3100 +#define DMA_CH_INC 0x80 + +#define DMA_CH_CR 0x00 +#define DMA_CH_TCR 0x04 +#define DMA_CH_RCR 0x08 +#define DMA_CH_TDLR_HI 0x10 +#define DMA_CH_TDLR_LO 0x14 +#define DMA_CH_RDLR_HI 0x18 +#define DMA_CH_RDLR_LO 0x1c +#define DMA_CH_TDTR_LO 0x24 +#define DMA_CH_RDTR_LO 0x2c +#define DMA_CH_TDRLR 0x30 +#define DMA_CH_RDRLR 0x34 +#define DMA_CH_IER 0x38 +#define DMA_CH_RIWT 0x3c +#define DMA_CH_SR 0x60 + +/* DMA channel register entry bit positions and sizes */ +#define DMA_CH_CR_PBLX8_POS 16 +#define DMA_CH_CR_PBLX8_LEN 1 +#define DMA_CH_CR_SPH_POS 24 +#define DMA_CH_CR_SPH_LEN 1 +#define DMA_CH_IER_AIE_POS 15 +#define DMA_CH_IER_AIE_LEN 1 +#define DMA_CH_IER_FBEE_POS 12 +#define DMA_CH_IER_FBEE_LEN 1 +#define DMA_CH_IER_NIE_POS 16 +#define DMA_CH_IER_NIE_LEN 1 +#define DMA_CH_IER_RBUE_POS 7 +#define DMA_CH_IER_RBUE_LEN 1 +#define DMA_CH_IER_RIE_POS 6 +#define DMA_CH_IER_RIE_LEN 1 +#define DMA_CH_IER_RSE_POS 8 +#define DMA_CH_IER_RSE_LEN 1 +#define DMA_CH_IER_TBUE_POS 2 +#define DMA_CH_IER_TBUE_LEN 1 +#define DMA_CH_IER_TIE_POS 0 +#define DMA_CH_IER_TIE_LEN 1 +#define DMA_CH_IER_TXSE_POS 1 +#define DMA_CH_IER_TXSE_LEN 1 +#define DMA_CH_RCR_PBL_POS 16 +#define DMA_CH_RCR_PBL_LEN 6 +#define DMA_CH_RCR_RBSZ_POS 1 +#define DMA_CH_RCR_RBSZ_LEN 14 +#define DMA_CH_RCR_SR_POS 0 +#define DMA_CH_RCR_SR_LEN 1 +#define DMA_CH_RIWT_RWT_POS 0 +#define DMA_CH_RIWT_RWT_LEN 8 +#define DMA_CH_SR_FBE_POS 12 +#define DMA_CH_SR_FBE_LEN 1 +#define DMA_CH_SR_RBU_POS 7 +#define DMA_CH_SR_RBU_LEN 1 +#define DMA_CH_SR_RI_POS 6 +#define DMA_CH_SR_RI_LEN 1 +#define DMA_CH_SR_RPS_POS 8 +#define DMA_CH_SR_RPS_LEN 1 +#define DMA_CH_SR_TBU_POS 2 +#define DMA_CH_SR_TBU_LEN 1 +#define DMA_CH_SR_TI_POS 0 +#define DMA_CH_SR_TI_LEN 1 +#define DMA_CH_SR_TPS_POS 1 +#define DMA_CH_SR_TPS_LEN 1 +#define DMA_CH_TCR_OSP_POS 4 +#define DMA_CH_TCR_OSP_LEN 1 +#define DMA_CH_TCR_PBL_POS 16 +#define DMA_CH_TCR_PBL_LEN 6 +#define DMA_CH_TCR_ST_POS 0 +#define DMA_CH_TCR_ST_LEN 1 +#define DMA_CH_TCR_TSE_POS 12 +#define DMA_CH_TCR_TSE_LEN 1 + +/* DMA channel register values */ +#define DMA_OSP_DISABLE 0x00 +#define DMA_OSP_ENABLE 0x01 +#define DMA_PBL_1 1 +#define DMA_PBL_2 2 +#define DMA_PBL_4 4 +#define DMA_PBL_8 8 +#define DMA_PBL_16 16 +#define DMA_PBL_32 32 +#define DMA_PBL_64 64 +#define DMA_PBL_128 128 +#define DMA_PBL_256 256 +#define DMA_PBL_X8_DISABLE 0x00 +#define DMA_PBL_X8_ENABLE 0x01 + +/* Descriptor/Packet entry bit positions and sizes */ +#define RX_PACKET_ERRORS_CRC_POS 2 +#define RX_PACKET_ERRORS_CRC_LEN 1 +#define RX_PACKET_ERRORS_FRAME_POS 3 +#define RX_PACKET_ERRORS_FRAME_LEN 1 +#define RX_PACKET_ERRORS_LENGTH_POS 0 +#define RX_PACKET_ERRORS_LENGTH_LEN 1 +#define RX_PACKET_ERRORS_OVERRUN_POS 1 +#define RX_PACKET_ERRORS_OVERRUN_LEN 1 + +#define RX_PACKET_ATTRIBUTES_CSUM_DONE_POS 0 +#define RX_PACKET_ATTRIBUTES_CSUM_DONE_LEN 1 +#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_POS 1 +#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN 1 +#define RX_PACKET_ATTRIBUTES_INCOMPLETE_POS 2 +#define RX_PACKET_ATTRIBUTES_INCOMPLETE_LEN 1 +#define RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_POS 3 +#define RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_LEN 1 +#define RX_PACKET_ATTRIBUTES_CONTEXT_POS 4 +#define RX_PACKET_ATTRIBUTES_CONTEXT_LEN 1 +#define RX_PACKET_ATTRIBUTES_RX_TSTAMP_POS 5 +#define RX_PACKET_ATTRIBUTES_RX_TSTAMP_LEN 1 +#define RX_PACKET_ATTRIBUTES_RSS_HASH_POS 6 +#define RX_PACKET_ATTRIBUTES_RSS_HASH_LEN 1 + +#define RX_NORMAL_DESC0_OVT_POS 0 +#define RX_NORMAL_DESC0_OVT_LEN 16 +#define RX_NORMAL_DESC2_HL_POS 0 +#define RX_NORMAL_DESC2_HL_LEN 10 +#define RX_NORMAL_DESC3_CDA_POS 27 +#define RX_NORMAL_DESC3_CDA_LEN 1 +#define RX_NORMAL_DESC3_CTXT_POS 30 +#define RX_NORMAL_DESC3_CTXT_LEN 1 +#define RX_NORMAL_DESC3_ES_POS 15 +#define RX_NORMAL_DESC3_ES_LEN 1 +#define RX_NORMAL_DESC3_ETLT_POS 16 +#define RX_NORMAL_DESC3_ETLT_LEN 4 +#define RX_NORMAL_DESC3_FD_POS 29 +#define RX_NORMAL_DESC3_FD_LEN 1 +#define RX_NORMAL_DESC3_INTE_POS 30 +#define RX_NORMAL_DESC3_INTE_LEN 1 +#define RX_NORMAL_DESC3_L34T_POS 20 +#define RX_NORMAL_DESC3_L34T_LEN 4 +#define RX_NORMAL_DESC3_LD_POS 28 +#define RX_NORMAL_DESC3_LD_LEN 1 +#define RX_NORMAL_DESC3_OWN_POS 31 +#define RX_NORMAL_DESC3_OWN_LEN 1 +#define RX_NORMAL_DESC3_PL_POS 0 +#define RX_NORMAL_DESC3_PL_LEN 14 +#define RX_NORMAL_DESC3_RSV_POS 26 +#define RX_NORMAL_DESC3_RSV_LEN 1 + +#define RX_DESC3_L34T_IPV4_TCP 1 +#define RX_DESC3_L34T_IPV4_UDP 2 +#define RX_DESC3_L34T_IPV4_ICMP 3 +#define RX_DESC3_L34T_IPV6_TCP 9 +#define RX_DESC3_L34T_IPV6_UDP 10 +#define RX_DESC3_L34T_IPV6_ICMP 11 + +#define RX_CONTEXT_DESC3_TSA_POS 4 +#define RX_CONTEXT_DESC3_TSA_LEN 1 +#define RX_CONTEXT_DESC3_TSD_POS 6 +#define RX_CONTEXT_DESC3_TSD_LEN 1 + +#define TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS 0 +#define TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN 1 +#define TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS 1 +#define TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN 1 +#define TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS 2 +#define TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN 1 +#define TX_PACKET_ATTRIBUTES_PTP_POS 3 +#define TX_PACKET_ATTRIBUTES_PTP_LEN 1 + +#define TX_CONTEXT_DESC2_MSS_POS 0 +#define TX_CONTEXT_DESC2_MSS_LEN 15 +#define TX_CONTEXT_DESC3_CTXT_POS 30 +#define TX_CONTEXT_DESC3_CTXT_LEN 1 +#define TX_CONTEXT_DESC3_TCMSSV_POS 26 +#define TX_CONTEXT_DESC3_TCMSSV_LEN 1 +#define TX_CONTEXT_DESC3_VLTV_POS 16 +#define TX_CONTEXT_DESC3_VLTV_LEN 1 +#define TX_CONTEXT_DESC3_VT_POS 0 +#define TX_CONTEXT_DESC3_VT_LEN 16 + +#define TX_NORMAL_DESC2_HL_B1L_POS 0 +#define TX_NORMAL_DESC2_HL_B1L_LEN 14 +#define TX_NORMAL_DESC2_IC_POS 31 +#define TX_NORMAL_DESC2_IC_LEN 1 +#define TX_NORMAL_DESC2_TTSE_POS 30 +#define TX_NORMAL_DESC2_TTSE_LEN 1 +#define TX_NORMAL_DESC2_VTIR_POS 14 +#define TX_NORMAL_DESC2_VTIR_LEN 2 +#define TX_NORMAL_DESC3_CIC_POS 16 +#define TX_NORMAL_DESC3_CIC_LEN 2 +#define TX_NORMAL_DESC3_CPC_POS 26 +#define TX_NORMAL_DESC3_CPC_LEN 2 +#define TX_NORMAL_DESC3_CTXT_POS 30 +#define TX_NORMAL_DESC3_CTXT_LEN 1 +#define TX_NORMAL_DESC3_FD_POS 29 +#define TX_NORMAL_DESC3_FD_LEN 1 +#define TX_NORMAL_DESC3_FL_POS 0 +#define TX_NORMAL_DESC3_FL_LEN 15 +#define TX_NORMAL_DESC3_LD_POS 28 +#define TX_NORMAL_DESC3_LD_LEN 1 +#define TX_NORMAL_DESC3_OWN_POS 31 +#define TX_NORMAL_DESC3_OWN_LEN 1 +#define TX_NORMAL_DESC3_TCPHDRLEN_POS 19 +#define TX_NORMAL_DESC3_TCPHDRLEN_LEN 4 +#define TX_NORMAL_DESC3_TCPPL_POS 0 +#define TX_NORMAL_DESC3_TCPPL_LEN 18 +#define TX_NORMAL_DESC3_TSE_POS 18 +#define TX_NORMAL_DESC3_TSE_LEN 1 + +#define TX_NORMAL_DESC2_VLAN_INSERT 0x2 + +#define XLGMAC_MTL_REG(pdata, n, reg) \ + ((pdata)->mac_regs + MTL_Q_BASE + ((n) * MTL_Q_INC) + (reg)) + +#define XLGMAC_DMA_REG(channel, reg) ((channel)->dma_regs + (reg)) + +#endif /* __DWC_XLGMAC_REG_H__ */ diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac.h b/drivers/net/ethernet/synopsys/dwc-xlgmac.h new file mode 100644 index 000000000000..7a4dc643b2b9 --- /dev/null +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac.h @@ -0,0 +1,651 @@ +/* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver + * + * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This Synopsys DWC XLGMAC software driver and associated documentation + * (hereinafter the "Software") is an unsupported proprietary work of + * Synopsys, Inc. unless otherwise expressly agreed to in writing between + * Synopsys and you. The Software IS NOT an item of Licensed Software or a + * Licensed Product under any End User Software License Agreement or + * Agreement for Licensed Products with Synopsys or any supplement thereto. + * Synopsys is a registered trademark of Synopsys, Inc. Other names included + * in the SOFTWARE may be the trademarks of their respective owners. + */ + +#ifndef __DWC_XLGMAC_H__ +#define __DWC_XLGMAC_H__ + +#include +#include +#include +#include +#include +#include +#include + +#define XLGMAC_DRV_NAME "dwc-xlgmac" +#define XLGMAC_DRV_VERSION "1.0.0" +#define XLGMAC_DRV_DESC "Synopsys DWC XLGMAC Driver" + +/* Descriptor related parameters */ +#define XLGMAC_TX_DESC_CNT 1024 +#define XLGMAC_TX_DESC_MIN_FREE (XLGMAC_TX_DESC_CNT >> 3) +#define XLGMAC_TX_DESC_MAX_PROC (XLGMAC_TX_DESC_CNT >> 1) +#define XLGMAC_RX_DESC_CNT 1024 +#define XLGMAC_RX_DESC_MAX_DIRTY (XLGMAC_RX_DESC_CNT >> 3) + +/* Descriptors required for maximum contiguous TSO/GSO packet */ +#define XLGMAC_TX_MAX_SPLIT ((GSO_MAX_SIZE / XLGMAC_TX_MAX_BUF_SIZE) + 1) + +/* Maximum possible descriptors needed for a SKB */ +#define XLGMAC_TX_MAX_DESC_NR (MAX_SKB_FRAGS + XLGMAC_TX_MAX_SPLIT + 2) + +#define XLGMAC_TX_MAX_BUF_SIZE (0x3fff & ~(64 - 1)) +#define XLGMAC_RX_MIN_BUF_SIZE (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN) +#define XLGMAC_RX_BUF_ALIGN 64 + +/* Maximum Size for Splitting the Header Data + * Keep in sync with SKB_ALLOC_SIZE + * 3'b000: 64 bytes, 3'b001: 128 bytes + * 3'b010: 256 bytes, 3'b011: 512 bytes + * 3'b100: 1023 bytes , 3'b101'3'b111: Reserved + */ +#define XLGMAC_SPH_HDSMS_SIZE 3 +#define XLGMAC_SKB_ALLOC_SIZE 512 + +#define XLGMAC_MAX_FIFO 81920 + +#define XLGMAC_MAX_DMA_CHANNELS 16 +#define XLGMAC_DMA_STOP_TIMEOUT 5 +#define XLGMAC_DMA_INTERRUPT_MASK 0x31c7 + +/* Default coalescing parameters */ +#define XLGMAC_INIT_DMA_TX_USECS 1000 +#define XLGMAC_INIT_DMA_TX_FRAMES 25 +#define XLGMAC_INIT_DMA_RX_USECS 30 +#define XLGMAC_INIT_DMA_RX_FRAMES 25 + +/* Flow control queue count */ +#define XLGMAC_MAX_FLOW_CONTROL_QUEUES 8 + +/* System clock is 125 MHz */ +#define XLGMAC_SYSCLOCK 125000000 + +/* Maximum MAC address hash table size (256 bits = 8 bytes) */ +#define XLGMAC_MAC_HASH_TABLE_SIZE 8 + +/* Receive Side Scaling */ +#define XLGMAC_RSS_HASH_KEY_SIZE 40 +#define XLGMAC_RSS_MAX_TABLE_SIZE 256 +#define XLGMAC_RSS_LOOKUP_TABLE_TYPE 0 +#define XLGMAC_RSS_HASH_KEY_TYPE 1 + +#define XLGMAC_STD_PACKET_MTU 1500 +#define XLGMAC_JUMBO_PACKET_MTU 9000 + +/* Helper macro for descriptor handling + * Always use XLGMAC_GET_DESC_DATA to access the descriptor data + */ +#define XLGMAC_GET_DESC_DATA(ring, idx) ({ \ + typeof(ring) _ring = (ring); \ + ((_ring)->desc_data_head + \ + ((idx) & ((_ring)->dma_desc_count - 1))); \ +}) + +#define XLGMAC_GET_REG_BITS(var, pos, len) ({ \ + typeof(pos) _pos = (pos); \ + typeof(len) _len = (len); \ + ((var) & GENMASK(_pos + _len - 1, _pos)) >> (_pos); \ +}) + +#define XLGMAC_GET_REG_BITS_LE(var, pos, len) ({ \ + typeof(pos) _pos = (pos); \ + typeof(len) _len = (len); \ + typeof(var) _var = le32_to_cpu((var)); \ + ((_var) & GENMASK(_pos + _len - 1, _pos)) >> (_pos); \ +}) + +#define XLGMAC_SET_REG_BITS(var, pos, len, val) ({ \ + typeof(var) _var = (var); \ + typeof(pos) _pos = (pos); \ + typeof(len) _len = (len); \ + typeof(val) _val = (val); \ + _val = (_val << _pos) & GENMASK(_pos + _len - 1, _pos); \ + _var = (_var & ~GENMASK(_pos + _len - 1, _pos)) | _val; \ +}) + +#define XLGMAC_SET_REG_BITS_LE(var, pos, len, val) ({ \ + typeof(var) _var = (var); \ + typeof(pos) _pos = (pos); \ + typeof(len) _len = (len); \ + typeof(val) _val = (val); \ + _val = (_val << _pos) & GENMASK(_pos + _len - 1, _pos); \ + _var = (_var & ~GENMASK(_pos + _len - 1, _pos)) | _val; \ + cpu_to_le32(_var); \ +}) + +struct xlgmac_pdata; + +enum xlgmac_int { + XLGMAC_INT_DMA_CH_SR_TI, + XLGMAC_INT_DMA_CH_SR_TPS, + XLGMAC_INT_DMA_CH_SR_TBU, + XLGMAC_INT_DMA_CH_SR_RI, + XLGMAC_INT_DMA_CH_SR_RBU, + XLGMAC_INT_DMA_CH_SR_RPS, + XLGMAC_INT_DMA_CH_SR_TI_RI, + XLGMAC_INT_DMA_CH_SR_FBE, + XLGMAC_INT_DMA_ALL, +}; + +struct xlgmac_stats { + /* MMC TX counters */ + u64 txoctetcount_gb; + u64 txframecount_gb; + u64 txbroadcastframes_g; + u64 txmulticastframes_g; + u64 tx64octets_gb; + u64 tx65to127octets_gb; + u64 tx128to255octets_gb; + u64 tx256to511octets_gb; + u64 tx512to1023octets_gb; + u64 tx1024tomaxoctets_gb; + u64 txunicastframes_gb; + u64 txmulticastframes_gb; + u64 txbroadcastframes_gb; + u64 txunderflowerror; + u64 txoctetcount_g; + u64 txframecount_g; + u64 txpauseframes; + u64 txvlanframes_g; + + /* MMC RX counters */ + u64 rxframecount_gb; + u64 rxoctetcount_gb; + u64 rxoctetcount_g; + u64 rxbroadcastframes_g; + u64 rxmulticastframes_g; + u64 rxcrcerror; + u64 rxrunterror; + u64 rxjabbererror; + u64 rxundersize_g; + u64 rxoversize_g; + u64 rx64octets_gb; + u64 rx65to127octets_gb; + u64 rx128to255octets_gb; + u64 rx256to511octets_gb; + u64 rx512to1023octets_gb; + u64 rx1024tomaxoctets_gb; + u64 rxunicastframes_g; + u64 rxlengtherror; + u64 rxoutofrangetype; + u64 rxpauseframes; + u64 rxfifooverflow; + u64 rxvlanframes_gb; + u64 rxwatchdogerror; + + /* Extra counters */ + u64 tx_tso_packets; + u64 rx_split_header_packets; + u64 rx_buffer_unavailable; +}; + +struct xlgmac_ring_buf { + struct sk_buff *skb; + dma_addr_t skb_dma; + unsigned int skb_len; +}; + +/* Common Tx and Rx DMA hardware descriptor */ +struct xlgmac_dma_desc { + __le32 desc0; + __le32 desc1; + __le32 desc2; + __le32 desc3; +}; + +/* Page allocation related values */ +struct xlgmac_page_alloc { + struct page *pages; + unsigned int pages_len; + unsigned int pages_offset; + + dma_addr_t pages_dma; +}; + +/* Ring entry buffer data */ +struct xlgmac_buffer_data { + struct xlgmac_page_alloc pa; + struct xlgmac_page_alloc pa_unmap; + + dma_addr_t dma_base; + unsigned long dma_off; + unsigned int dma_len; +}; + +/* Tx-related desc data */ +struct xlgmac_tx_desc_data { + unsigned int packets; /* BQL packet count */ + unsigned int bytes; /* BQL byte count */ +}; + +/* Rx-related desc data */ +struct xlgmac_rx_desc_data { + struct xlgmac_buffer_data hdr; /* Header locations */ + struct xlgmac_buffer_data buf; /* Payload locations */ + + unsigned short hdr_len; /* Length of received header */ + unsigned short len; /* Length of received packet */ +}; + +struct xlgmac_pkt_info { + struct sk_buff *skb; + + unsigned int attributes; + + unsigned int errors; + + /* descriptors needed for this packet */ + unsigned int desc_count; + unsigned int length; + + unsigned int tx_packets; + unsigned int tx_bytes; + + unsigned int header_len; + unsigned int tcp_header_len; + unsigned int tcp_payload_len; + unsigned short mss; + + unsigned short vlan_ctag; + + u64 rx_tstamp; + + u32 rss_hash; + enum pkt_hash_types rss_hash_type; +}; + +struct xlgmac_desc_data { + /* dma_desc: Virtual address of descriptor + * dma_desc_addr: DMA address of descriptor + */ + struct xlgmac_dma_desc *dma_desc; + dma_addr_t dma_desc_addr; + + /* skb: Virtual address of SKB + * skb_dma: DMA address of SKB data + * skb_dma_len: Length of SKB DMA area + */ + struct sk_buff *skb; + dma_addr_t skb_dma; + unsigned int skb_dma_len; + + /* Tx/Rx -related data */ + struct xlgmac_tx_desc_data tx; + struct xlgmac_rx_desc_data rx; + + unsigned int mapped_as_page; + + /* Incomplete receive save location. If the budget is exhausted + * or the last descriptor (last normal descriptor or a following + * context descriptor) has not been DMA'd yet the current state + * of the receive processing needs to be saved. + */ + unsigned int state_saved; + struct { + struct sk_buff *skb; + unsigned int len; + unsigned int error; + } state; +}; + +struct xlgmac_ring { + /* Per packet related information */ + struct xlgmac_pkt_info pkt_info; + + /* Virtual/DMA addresses of DMA descriptor list and the total count */ + struct xlgmac_dma_desc *dma_desc_head; + dma_addr_t dma_desc_head_addr; + unsigned int dma_desc_count; + + /* Array of descriptor data corresponding the DMA descriptor + * (always use the XLGMAC_GET_DESC_DATA macro to access this data) + */ + struct xlgmac_desc_data *desc_data_head; + + /* Page allocation for RX buffers */ + struct xlgmac_page_alloc rx_hdr_pa; + struct xlgmac_page_alloc rx_buf_pa; + + /* Ring index values + * cur - Tx: index of descriptor to be used for current transfer + * Rx: index of descriptor to check for packet availability + * dirty - Tx: index of descriptor to check for transfer complete + * Rx: index of descriptor to check for buffer reallocation + */ + unsigned int cur; + unsigned int dirty; + + /* Coalesce frame count used for interrupt bit setting */ + unsigned int coalesce_count; + + union { + struct { + unsigned int xmit_more; + unsigned int queue_stopped; + unsigned short cur_mss; + unsigned short cur_vlan_ctag; + } tx; + }; +} ____cacheline_aligned; + +struct xlgmac_channel { + char name[16]; + + /* Address of private data area for device */ + struct xlgmac_pdata *pdata; + + /* Queue index and base address of queue's DMA registers */ + unsigned int queue_index; + void __iomem *dma_regs; + + /* Per channel interrupt irq number */ + int dma_irq; + char dma_irq_name[IFNAMSIZ + 32]; + + /* Netdev related settings */ + struct napi_struct napi; + + unsigned int saved_ier; + + unsigned int tx_timer_active; + struct timer_list tx_timer; + + struct xlgmac_ring *tx_ring; + struct xlgmac_ring *rx_ring; +} ____cacheline_aligned; + +struct xlgmac_desc_ops { + int (*alloc_channles_and_rings)(struct xlgmac_pdata *pdata); + void (*free_channels_and_rings)(struct xlgmac_pdata *pdata); + int (*map_tx_skb)(struct xlgmac_channel *channel, + struct sk_buff *skb); + int (*map_rx_buffer)(struct xlgmac_pdata *pdata, + struct xlgmac_ring *ring, + struct xlgmac_desc_data *desc_data); + void (*unmap_desc_data)(struct xlgmac_pdata *pdata, + struct xlgmac_desc_data *desc_data); + void (*tx_desc_init)(struct xlgmac_pdata *pdata); + void (*rx_desc_init)(struct xlgmac_pdata *pdata); +}; + +struct xlgmac_hw_ops { + int (*init)(struct xlgmac_pdata *pdata); + int (*exit)(struct xlgmac_pdata *pdata); + + int (*tx_complete)(struct xlgmac_dma_desc *dma_desc); + + void (*enable_tx)(struct xlgmac_pdata *pdata); + void (*disable_tx)(struct xlgmac_pdata *pdata); + void (*enable_rx)(struct xlgmac_pdata *pdata); + void (*disable_rx)(struct xlgmac_pdata *pdata); + + int (*enable_int)(struct xlgmac_channel *channel, + enum xlgmac_int int_id); + int (*disable_int)(struct xlgmac_channel *channel, + enum xlgmac_int int_id); + void (*dev_xmit)(struct xlgmac_channel *channel); + int (*dev_read)(struct xlgmac_channel *channel); + + int (*set_mac_address)(struct xlgmac_pdata *pdata, u8 *addr); + int (*config_rx_mode)(struct xlgmac_pdata *pdata); + int (*enable_rx_csum)(struct xlgmac_pdata *pdata); + int (*disable_rx_csum)(struct xlgmac_pdata *pdata); + + /* For MII speed configuration */ + int (*set_xlgmii_25000_speed)(struct xlgmac_pdata *pdata); + int (*set_xlgmii_40000_speed)(struct xlgmac_pdata *pdata); + int (*set_xlgmii_50000_speed)(struct xlgmac_pdata *pdata); + int (*set_xlgmii_100000_speed)(struct xlgmac_pdata *pdata); + + /* For descriptor related operation */ + void (*tx_desc_init)(struct xlgmac_channel *channel); + void (*rx_desc_init)(struct xlgmac_channel *channel); + void (*tx_desc_reset)(struct xlgmac_desc_data *desc_data); + void (*rx_desc_reset)(struct xlgmac_pdata *pdata, + struct xlgmac_desc_data *desc_data, + unsigned int index); + int (*is_last_desc)(struct xlgmac_dma_desc *dma_desc); + int (*is_context_desc)(struct xlgmac_dma_desc *dma_desc); + void (*tx_start_xmit)(struct xlgmac_channel *channel, + struct xlgmac_ring *ring); + + /* For Flow Control */ + int (*config_tx_flow_control)(struct xlgmac_pdata *pdata); + int (*config_rx_flow_control)(struct xlgmac_pdata *pdata); + + /* For Vlan related config */ + int (*enable_rx_vlan_stripping)(struct xlgmac_pdata *pdata); + int (*disable_rx_vlan_stripping)(struct xlgmac_pdata *pdata); + int (*enable_rx_vlan_filtering)(struct xlgmac_pdata *pdata); + int (*disable_rx_vlan_filtering)(struct xlgmac_pdata *pdata); + int (*update_vlan_hash_table)(struct xlgmac_pdata *pdata); + + /* For RX coalescing */ + int (*config_rx_coalesce)(struct xlgmac_pdata *pdata); + int (*config_tx_coalesce)(struct xlgmac_pdata *pdata); + unsigned int (*usec_to_riwt)(struct xlgmac_pdata *pdata, + unsigned int usec); + unsigned int (*riwt_to_usec)(struct xlgmac_pdata *pdata, + unsigned int riwt); + + /* For RX and TX threshold config */ + int (*config_rx_threshold)(struct xlgmac_pdata *pdata, + unsigned int val); + int (*config_tx_threshold)(struct xlgmac_pdata *pdata, + unsigned int val); + + /* For RX and TX Store and Forward Mode config */ + int (*config_rsf_mode)(struct xlgmac_pdata *pdata, + unsigned int val); + int (*config_tsf_mode)(struct xlgmac_pdata *pdata, + unsigned int val); + + /* For TX DMA Operate on Second Frame config */ + int (*config_osp_mode)(struct xlgmac_pdata *pdata); + + /* For RX and TX PBL config */ + int (*config_rx_pbl_val)(struct xlgmac_pdata *pdata); + int (*get_rx_pbl_val)(struct xlgmac_pdata *pdata); + int (*config_tx_pbl_val)(struct xlgmac_pdata *pdata); + int (*get_tx_pbl_val)(struct xlgmac_pdata *pdata); + int (*config_pblx8)(struct xlgmac_pdata *pdata); + + /* For MMC statistics */ + void (*rx_mmc_int)(struct xlgmac_pdata *pdata); + void (*tx_mmc_int)(struct xlgmac_pdata *pdata); + void (*read_mmc_stats)(struct xlgmac_pdata *pdata); + + /* For Receive Side Scaling */ + int (*enable_rss)(struct xlgmac_pdata *pdata); + int (*disable_rss)(struct xlgmac_pdata *pdata); + int (*set_rss_hash_key)(struct xlgmac_pdata *pdata, + const u8 *key); + int (*set_rss_lookup_table)(struct xlgmac_pdata *pdata, + const u32 *table); +}; + +/* This structure contains flags that indicate what hardware features + * or configurations are present in the device. + */ +struct xlgmac_hw_features { + /* HW Version */ + unsigned int version; + + /* HW Feature Register0 */ + unsigned int phyifsel; /* PHY interface support */ + unsigned int vlhash; /* VLAN Hash Filter */ + unsigned int sma; /* SMA(MDIO) Interface */ + unsigned int rwk; /* PMT remote wake-up packet */ + unsigned int mgk; /* PMT magic packet */ + unsigned int mmc; /* RMON module */ + unsigned int aoe; /* ARP Offload */ + unsigned int ts; /* IEEE 1588-2008 Advanced Timestamp */ + unsigned int eee; /* Energy Efficient Ethernet */ + unsigned int tx_coe; /* Tx Checksum Offload */ + unsigned int rx_coe; /* Rx Checksum Offload */ + unsigned int addn_mac; /* Additional MAC Addresses */ + unsigned int ts_src; /* Timestamp Source */ + unsigned int sa_vlan_ins; /* Source Address or VLAN Insertion */ + + /* HW Feature Register1 */ + unsigned int rx_fifo_size; /* MTL Receive FIFO Size */ + unsigned int tx_fifo_size; /* MTL Transmit FIFO Size */ + unsigned int adv_ts_hi; /* Advance Timestamping High Word */ + unsigned int dma_width; /* DMA width */ + unsigned int dcb; /* DCB Feature */ + unsigned int sph; /* Split Header Feature */ + unsigned int tso; /* TCP Segmentation Offload */ + unsigned int dma_debug; /* DMA Debug Registers */ + unsigned int rss; /* Receive Side Scaling */ + unsigned int tc_cnt; /* Number of Traffic Classes */ + unsigned int hash_table_size; /* Hash Table Size */ + unsigned int l3l4_filter_num; /* Number of L3-L4 Filters */ + + /* HW Feature Register2 */ + unsigned int rx_q_cnt; /* Number of MTL Receive Queues */ + unsigned int tx_q_cnt; /* Number of MTL Transmit Queues */ + unsigned int rx_ch_cnt; /* Number of DMA Receive Channels */ + unsigned int tx_ch_cnt; /* Number of DMA Transmit Channels */ + unsigned int pps_out_num; /* Number of PPS outputs */ + unsigned int aux_snap_num; /* Number of Aux snapshot inputs */ +}; + +struct xlgmac_resources { + void __iomem *addr; + int irq; +}; + +struct xlgmac_pdata { + struct net_device *netdev; + struct device *dev; + + struct xlgmac_hw_ops hw_ops; + struct xlgmac_desc_ops desc_ops; + + /* Device statistics */ + struct xlgmac_stats stats; + + u32 msg_enable; + + /* MAC registers base */ + void __iomem *mac_regs; + + /* Hardware features of the device */ + struct xlgmac_hw_features hw_feat; + + struct work_struct restart_work; + + /* Rings for Tx/Rx on a DMA channel */ + struct xlgmac_channel *channel_head; + unsigned int channel_count; + unsigned int tx_ring_count; + unsigned int rx_ring_count; + unsigned int tx_desc_count; + unsigned int rx_desc_count; + unsigned int tx_q_count; + unsigned int rx_q_count; + + /* Tx/Rx common settings */ + unsigned int pblx8; + + /* Tx settings */ + unsigned int tx_sf_mode; + unsigned int tx_threshold; + unsigned int tx_pbl; + unsigned int tx_osp_mode; + + /* Rx settings */ + unsigned int rx_sf_mode; + unsigned int rx_threshold; + unsigned int rx_pbl; + + /* Tx coalescing settings */ + unsigned int tx_usecs; + unsigned int tx_frames; + + /* Rx coalescing settings */ + unsigned int rx_riwt; + unsigned int rx_usecs; + unsigned int rx_frames; + + /* Current Rx buffer size */ + unsigned int rx_buf_size; + + /* Flow control settings */ + unsigned int tx_pause; + unsigned int rx_pause; + + /* Device interrupt number */ + int dev_irq; + unsigned int per_channel_irq; + int channel_irq[XLGMAC_MAX_DMA_CHANNELS]; + + /* Netdev related settings */ + unsigned char mac_addr[ETH_ALEN]; + netdev_features_t netdev_features; + struct napi_struct napi; + + /* Filtering support */ + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; + + /* Device clocks */ + unsigned long sysclk_rate; + + /* RSS addressing mutex */ + struct mutex rss_mutex; + + /* Receive Side Scaling settings */ + u8 rss_key[XLGMAC_RSS_HASH_KEY_SIZE]; + u32 rss_table[XLGMAC_RSS_MAX_TABLE_SIZE]; + u32 rss_options; + + int phy_speed; + + char drv_name[32]; + char drv_ver[32]; +}; + +void xlgmac_init_desc_ops(struct xlgmac_desc_ops *desc_ops); +void xlgmac_init_hw_ops(struct xlgmac_hw_ops *hw_ops); +const struct net_device_ops *xlgmac_get_netdev_ops(void); +void xlgmac_dump_tx_desc(struct xlgmac_pdata *pdata, + struct xlgmac_ring *ring, + unsigned int idx, + unsigned int count, + unsigned int flag); +void xlgmac_dump_rx_desc(struct xlgmac_pdata *pdata, + struct xlgmac_ring *ring, + unsigned int idx); +void xlgmac_print_pkt(struct net_device *netdev, + struct sk_buff *skb, bool tx_rx); +void xlgmac_get_all_hw_features(struct xlgmac_pdata *pdata); +void xlgmac_print_all_hw_features(struct xlgmac_pdata *pdata); +int xlgmac_drv_probe(struct device *dev, + struct xlgmac_resources *res); +int xlgmac_drv_remove(struct device *dev); + +/* For debug prints */ +#ifdef XLGMAC_DEBUG +#define XLGMAC_PR(fmt, args...) \ + pr_alert("[%s,%d]:" fmt, __func__, __LINE__, ## args) +#else +#define XLGMAC_PR(x...) do { } while (0) +#endif + +#endif /* __DWC_XLGMAC_H__ */ -- cgit From 581319c58600b54612c417aff32ae9bbd79f4cdb Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 9 Mar 2017 13:54:08 +0100 Subject: net/socket: use per af lockdep classes for sk queues Currently the sock queue's spin locks get their lockdep classes by the default init_spin_lock() initializer: all socket families get - usually, see below - a single class for rx, another specific class for tx, etc. This can lead to false positive lockdep splat, as reported by Andrey. Moreover there are two separate initialization points for the sock queues, one in sk_clone_lock() and one in sock_init_data(), so that e.g. the rx queue lock can get one of two possible, different classes, depending on the socket being cloned or not. This change tries to address the above, setting explicitly a per address family lockdep class for each queue's spinlock. Also, move the duplicated initialization code to a single location. v1 -> v2: - renamed the init helper rfc -> v1: - no changes, tested with several different workload Suggested-by: Cong Wang Signed-off-by: Paolo Abeni Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/sock.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 18 deletions(-) diff --git a/net/core/sock.c b/net/core/sock.c index f6fd79f33097..768aedf238f5 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -258,12 +258,66 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = { "clock-AF_NFC" , "clock-AF_VSOCK" , "clock-AF_KCM" , "clock-AF_QIPCRTR", "clock-AF_SMC" , "clock-AF_MAX" }; +static const char *const af_family_rlock_key_strings[AF_MAX+1] = { + "rlock-AF_UNSPEC", "rlock-AF_UNIX" , "rlock-AF_INET" , + "rlock-AF_AX25" , "rlock-AF_IPX" , "rlock-AF_APPLETALK", + "rlock-AF_NETROM", "rlock-AF_BRIDGE" , "rlock-AF_ATMPVC" , + "rlock-AF_X25" , "rlock-AF_INET6" , "rlock-AF_ROSE" , + "rlock-AF_DECnet", "rlock-AF_NETBEUI" , "rlock-AF_SECURITY" , + "rlock-AF_KEY" , "rlock-AF_NETLINK" , "rlock-AF_PACKET" , + "rlock-AF_ASH" , "rlock-AF_ECONET" , "rlock-AF_ATMSVC" , + "rlock-AF_RDS" , "rlock-AF_SNA" , "rlock-AF_IRDA" , + "rlock-AF_PPPOX" , "rlock-AF_WANPIPE" , "rlock-AF_LLC" , + "rlock-27" , "rlock-28" , "rlock-AF_CAN" , + "rlock-AF_TIPC" , "rlock-AF_BLUETOOTH", "rlock-AF_IUCV" , + "rlock-AF_RXRPC" , "rlock-AF_ISDN" , "rlock-AF_PHONET" , + "rlock-AF_IEEE802154", "rlock-AF_CAIF" , "rlock-AF_ALG" , + "rlock-AF_NFC" , "rlock-AF_VSOCK" , "rlock-AF_KCM" , + "rlock-AF_QIPCRTR", "rlock-AF_SMC" , "rlock-AF_MAX" +}; +static const char *const af_family_wlock_key_strings[AF_MAX+1] = { + "wlock-AF_UNSPEC", "wlock-AF_UNIX" , "wlock-AF_INET" , + "wlock-AF_AX25" , "wlock-AF_IPX" , "wlock-AF_APPLETALK", + "wlock-AF_NETROM", "wlock-AF_BRIDGE" , "wlock-AF_ATMPVC" , + "wlock-AF_X25" , "wlock-AF_INET6" , "wlock-AF_ROSE" , + "wlock-AF_DECnet", "wlock-AF_NETBEUI" , "wlock-AF_SECURITY" , + "wlock-AF_KEY" , "wlock-AF_NETLINK" , "wlock-AF_PACKET" , + "wlock-AF_ASH" , "wlock-AF_ECONET" , "wlock-AF_ATMSVC" , + "wlock-AF_RDS" , "wlock-AF_SNA" , "wlock-AF_IRDA" , + "wlock-AF_PPPOX" , "wlock-AF_WANPIPE" , "wlock-AF_LLC" , + "wlock-27" , "wlock-28" , "wlock-AF_CAN" , + "wlock-AF_TIPC" , "wlock-AF_BLUETOOTH", "wlock-AF_IUCV" , + "wlock-AF_RXRPC" , "wlock-AF_ISDN" , "wlock-AF_PHONET" , + "wlock-AF_IEEE802154", "wlock-AF_CAIF" , "wlock-AF_ALG" , + "wlock-AF_NFC" , "wlock-AF_VSOCK" , "wlock-AF_KCM" , + "wlock-AF_QIPCRTR", "wlock-AF_SMC" , "wlock-AF_MAX" +}; +static const char *const af_family_elock_key_strings[AF_MAX+1] = { + "elock-AF_UNSPEC", "elock-AF_UNIX" , "elock-AF_INET" , + "elock-AF_AX25" , "elock-AF_IPX" , "elock-AF_APPLETALK", + "elock-AF_NETROM", "elock-AF_BRIDGE" , "elock-AF_ATMPVC" , + "elock-AF_X25" , "elock-AF_INET6" , "elock-AF_ROSE" , + "elock-AF_DECnet", "elock-AF_NETBEUI" , "elock-AF_SECURITY" , + "elock-AF_KEY" , "elock-AF_NETLINK" , "elock-AF_PACKET" , + "elock-AF_ASH" , "elock-AF_ECONET" , "elock-AF_ATMSVC" , + "elock-AF_RDS" , "elock-AF_SNA" , "elock-AF_IRDA" , + "elock-AF_PPPOX" , "elock-AF_WANPIPE" , "elock-AF_LLC" , + "elock-27" , "elock-28" , "elock-AF_CAN" , + "elock-AF_TIPC" , "elock-AF_BLUETOOTH", "elock-AF_IUCV" , + "elock-AF_RXRPC" , "elock-AF_ISDN" , "elock-AF_PHONET" , + "elock-AF_IEEE802154", "elock-AF_CAIF" , "elock-AF_ALG" , + "elock-AF_NFC" , "elock-AF_VSOCK" , "elock-AF_KCM" , + "elock-AF_QIPCRTR", "elock-AF_SMC" , "elock-AF_MAX" +}; /* - * sk_callback_lock locking rules are per-address-family, + * sk_callback_lock and sk queues locking rules are per-address-family, * so split the lock classes by using a per-AF key: */ static struct lock_class_key af_callback_keys[AF_MAX]; +static struct lock_class_key af_rlock_keys[AF_MAX]; +static struct lock_class_key af_wlock_keys[AF_MAX]; +static struct lock_class_key af_elock_keys[AF_MAX]; /* Take into consideration the size of the struct sk_buff overhead in the * determination of these values, since that is non-constant across @@ -1478,6 +1532,27 @@ void sk_free(struct sock *sk) } EXPORT_SYMBOL(sk_free); +static void sk_init_common(struct sock *sk) +{ + skb_queue_head_init(&sk->sk_receive_queue); + skb_queue_head_init(&sk->sk_write_queue); + skb_queue_head_init(&sk->sk_error_queue); + + rwlock_init(&sk->sk_callback_lock); + lockdep_set_class_and_name(&sk->sk_receive_queue.lock, + af_rlock_keys + sk->sk_family, + af_family_rlock_key_strings[sk->sk_family]); + lockdep_set_class_and_name(&sk->sk_write_queue.lock, + af_wlock_keys + sk->sk_family, + af_family_wlock_key_strings[sk->sk_family]); + lockdep_set_class_and_name(&sk->sk_error_queue.lock, + af_elock_keys + sk->sk_family, + af_family_elock_key_strings[sk->sk_family]); + lockdep_set_class_and_name(&sk->sk_callback_lock, + af_callback_keys + sk->sk_family, + af_family_clock_key_strings[sk->sk_family]); +} + /** * sk_clone_lock - clone a socket, and lock its clone * @sk: the socket to clone @@ -1511,13 +1586,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) */ atomic_set(&newsk->sk_wmem_alloc, 1); atomic_set(&newsk->sk_omem_alloc, 0); - skb_queue_head_init(&newsk->sk_receive_queue); - skb_queue_head_init(&newsk->sk_write_queue); - - rwlock_init(&newsk->sk_callback_lock); - lockdep_set_class_and_name(&newsk->sk_callback_lock, - af_callback_keys + newsk->sk_family, - af_family_clock_key_strings[newsk->sk_family]); + sk_init_common(newsk); newsk->sk_dst_cache = NULL; newsk->sk_dst_pending_confirm = 0; @@ -1528,7 +1597,6 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) newsk->sk_userlocks = sk->sk_userlocks & ~SOCK_BINDPORT_LOCK; sock_reset_flag(newsk, SOCK_DONE); - skb_queue_head_init(&newsk->sk_error_queue); filter = rcu_dereference_protected(newsk->sk_filter, 1); if (filter != NULL) @@ -2454,10 +2522,7 @@ EXPORT_SYMBOL(sk_stop_timer); void sock_init_data(struct socket *sock, struct sock *sk) { - skb_queue_head_init(&sk->sk_receive_queue); - skb_queue_head_init(&sk->sk_write_queue); - skb_queue_head_init(&sk->sk_error_queue); - + sk_init_common(sk); sk->sk_send_head = NULL; init_timer(&sk->sk_timer); @@ -2480,11 +2545,6 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_uid = make_kuid(sock_net(sk)->user_ns, 0); } - rwlock_init(&sk->sk_callback_lock); - lockdep_set_class_and_name(&sk->sk_callback_lock, - af_callback_keys + sk->sk_family, - af_family_clock_key_strings[sk->sk_family]); - sk->sk_state_change = sock_def_wakeup; sk->sk_data_ready = sock_def_readable; sk->sk_write_space = sock_def_write_space; -- cgit From abb521e36b9286c262971974ebaeda2d67dadd86 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 8 Mar 2017 08:57:00 -0800 Subject: ethtool: add CRC32 as an RSS hash function CRC32 engines are usually easily available in hardware and generate OK spread for RSS hash. Add CRC32 RSS hash function to ethtool API. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/linux/ethtool.h | 2 ++ net/core/ethtool.c | 1 + 2 files changed, 3 insertions(+) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 9ded8c6d8176..83cc9863444b 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -60,6 +60,7 @@ enum ethtool_phys_id_state { enum { ETH_RSS_HASH_TOP_BIT, /* Configurable RSS hash function - Toeplitz */ ETH_RSS_HASH_XOR_BIT, /* Configurable RSS hash function - Xor */ + ETH_RSS_HASH_CRC32_BIT, /* Configurable RSS hash function - Crc32 */ /* * Add your fresh new hash function bits above and remember to update @@ -73,6 +74,7 @@ enum { #define ETH_RSS_HASH_TOP __ETH_RSS_HASH(TOP) #define ETH_RSS_HASH_XOR __ETH_RSS_HASH(XOR) +#define ETH_RSS_HASH_CRC32 __ETH_RSS_HASH(CRC32) #define ETH_RSS_HASH_UNKNOWN 0 #define ETH_RSS_HASH_NO_CHANGE 0 diff --git a/net/core/ethtool.c b/net/core/ethtool.c index aecb2c7241b6..905a88ad28e0 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -109,6 +109,7 @@ static const char rss_hash_func_strings[ETH_RSS_HASH_FUNCS_COUNT][ETH_GSTRING_LEN] = { [ETH_RSS_HASH_TOP_BIT] = "toeplitz", [ETH_RSS_HASH_XOR_BIT] = "xor", + [ETH_RSS_HASH_CRC32_BIT] = "crc32", }; static const char -- cgit From 9ff304bfaf58c119ef8ba3e20326edeed9983aef Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 8 Mar 2017 08:57:01 -0800 Subject: nfp: add support for reporting CRC32 hash function Some firmware images may reuse CRC32 hardware to compute RXHASH. Make sure we report the correct hash function. Note that we don't support changing functions at runtime. That would also require a few more additions to the way the key is set because different functions have different key sizes. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 3 ++ .../net/ethernet/netronome/nfp/nfp_net_common.c | 46 ++++++++++++++++++++-- drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h | 14 ++++++- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 23 +++++++---- 4 files changed, 75 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index e614a376b595..9843e953bbed 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -446,6 +446,7 @@ struct nfp_stat_pair { * @fw_ver: Firmware version * @cap: Capabilities advertised by the Firmware * @max_mtu: Maximum support MTU advertised by the Firmware + * @rss_hfunc: RSS selected hash function * @rss_cfg: RSS configuration * @rss_key: RSS secret key * @rss_itbl: RSS indirection table @@ -518,6 +519,7 @@ struct nfp_net { u32 cap; u32 max_mtu; + u8 rss_hfunc; u32 rss_cfg; u8 rss_key[NFP_NET_CFG_RSS_KEY_SZ]; u8 rss_itbl[NFP_NET_CFG_RSS_ITBL_SZ]; @@ -776,6 +778,7 @@ void nfp_net_netdev_clean(struct net_device *netdev); void nfp_net_set_ethtool_ops(struct net_device *netdev); void nfp_net_info(struct nfp_net *nn); int nfp_net_reconfig(struct nfp_net *nn, u32 update); +unsigned int nfp_net_rss_key_sz(struct nfp_net *nn); void nfp_net_rss_write_itbl(struct nfp_net *nn); void nfp_net_rss_write_key(struct nfp_net *nn); void nfp_net_coalesce_write_cfg(struct nfp_net *nn); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 9179a99563af..e72468d65c28 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -41,6 +41,7 @@ * Chris Telfer */ +#include #include #include #include @@ -2045,7 +2046,7 @@ void nfp_net_rss_write_key(struct nfp_net *nn) { int i; - for (i = 0; i < NFP_NET_CFG_RSS_KEY_SZ; i += 4) + for (i = 0; i < nfp_net_rss_key_sz(nn); i += 4) nn_writel(nn, NFP_NET_CFG_RSS_KEY + i, get_unaligned_le32(nn->rss_key + i)); } @@ -3111,20 +3112,59 @@ void nfp_net_netdev_free(struct nfp_net *nn) free_netdev(nn->netdev); } +/** + * nfp_net_rss_key_sz() - Get current size of the RSS key + * @nn: NFP Net device instance + * + * Return: size of the RSS key for currently selected hash function. + */ +unsigned int nfp_net_rss_key_sz(struct nfp_net *nn) +{ + switch (nn->rss_hfunc) { + case ETH_RSS_HASH_TOP: + return NFP_NET_CFG_RSS_KEY_SZ; + case ETH_RSS_HASH_XOR: + return 0; + case ETH_RSS_HASH_CRC32: + return 4; + } + + nn_warn(nn, "Unknown hash function: %u\n", nn->rss_hfunc); + return 0; +} + /** * nfp_net_rss_init() - Set the initial RSS parameters * @nn: NFP Net device to reconfigure */ static void nfp_net_rss_init(struct nfp_net *nn) { - netdev_rss_key_fill(nn->rss_key, NFP_NET_CFG_RSS_KEY_SZ); + unsigned long func_bit, rss_cap_hfunc; + u32 reg; + + /* Read the RSS function capability and select first supported func */ + reg = nn_readl(nn, NFP_NET_CFG_RSS_CAP); + rss_cap_hfunc = FIELD_GET(NFP_NET_CFG_RSS_CAP_HFUNC, reg); + if (!rss_cap_hfunc) + rss_cap_hfunc = FIELD_GET(NFP_NET_CFG_RSS_CAP_HFUNC, + NFP_NET_CFG_RSS_TOEPLITZ); + + func_bit = find_first_bit(&rss_cap_hfunc, NFP_NET_CFG_RSS_HFUNCS); + if (func_bit == NFP_NET_CFG_RSS_HFUNCS) { + dev_warn(&nn->pdev->dev, + "Bad RSS config, defaulting to Toeplitz hash\n"); + func_bit = ETH_RSS_HASH_TOP_BIT; + } + nn->rss_hfunc = 1 << func_bit; + + netdev_rss_key_fill(nn->rss_key, nfp_net_rss_key_sz(nn)); nfp_net_rss_init_itbl(nn); /* Enable IPv4/IPv6 TCP by default */ nn->rss_cfg = NFP_NET_CFG_RSS_IPV4_TCP | NFP_NET_CFG_RSS_IPV6_TCP | - NFP_NET_CFG_RSS_TOEPLITZ | + FIELD_PREP(NFP_NET_CFG_RSS_HFUNC, nn->rss_hfunc) | NFP_NET_CFG_RSS_MASK; } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index 385ba355c965..71d86171b4ee 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Netronome Systems, Inc. + * Copyright (C) 2015-2017 Netronome Systems, Inc. * * This software is dual licensed under the GNU General License Version 2, * June 1991 as shown in the file COPYING in the top-level directory of this @@ -191,6 +191,14 @@ #define NFP_NET_CFG_RX_OFFSET 0x0050 #define NFP_NET_CFG_RX_OFFSET_DYNAMIC 0 /* Prepend mode */ +/** + * RSS capabilities + * @NFP_NET_CFG_RSS_CAP_HFUNC: supported hash functions (same bits as + * @NFP_NET_CFG_RSS_HFUNC) + */ +#define NFP_NET_CFG_RSS_CAP 0x0054 +#define NFP_NET_CFG_RSS_CAP_HFUNC 0xff000000 + /** * VXLAN/UDP encap configuration * @NFP_NET_CFG_VXLAN_PORT: Base address of table of tunnels' UDP dst ports @@ -249,7 +257,11 @@ #define NFP_NET_CFG_RSS_IPV4_UDP (1 << 11) /* RSS for IPv4/UDP */ #define NFP_NET_CFG_RSS_IPV6_TCP (1 << 12) /* RSS for IPv6/TCP */ #define NFP_NET_CFG_RSS_IPV6_UDP (1 << 13) /* RSS for IPv6/UDP */ +#define NFP_NET_CFG_RSS_HFUNC 0xff000000 #define NFP_NET_CFG_RSS_TOEPLITZ (1 << 24) /* Use Toeplitz hash */ +#define NFP_NET_CFG_RSS_XOR (1 << 25) /* Use XOR as hash */ +#define NFP_NET_CFG_RSS_CRC32 (1 << 26) /* Use CRC32 as hash */ +#define NFP_NET_CFG_RSS_HFUNCS 3 #define NFP_NET_CFG_RSS_KEY (NFP_NET_CFG_RSS_BASE + 0x4) #define NFP_NET_CFG_RSS_KEY_SZ 0x28 #define NFP_NET_CFG_RSS_ITBL (NFP_NET_CFG_RSS_BASE + 0x4 + \ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 2649f7523c81..a1bca2dca0a5 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -40,6 +40,7 @@ * Brad Petrus */ +#include #include #include #include @@ -454,7 +455,7 @@ static int nfp_net_set_rss_hash_opt(struct nfp_net *nn, return -EINVAL; } - new_rss_cfg |= NFP_NET_CFG_RSS_TOEPLITZ; + new_rss_cfg |= FIELD_PREP(NFP_NET_CFG_RSS_HFUNC, nn->rss_hfunc); new_rss_cfg |= NFP_NET_CFG_RSS_MASK; if (new_rss_cfg == nn->rss_cfg) @@ -496,7 +497,12 @@ static u32 nfp_net_get_rxfh_indir_size(struct net_device *netdev) static u32 nfp_net_get_rxfh_key_size(struct net_device *netdev) { - return NFP_NET_CFG_RSS_KEY_SZ; + struct nfp_net *nn = netdev_priv(netdev); + + if (!(nn->cap & NFP_NET_CFG_CTRL_RSS)) + return -EOPNOTSUPP; + + return nfp_net_rss_key_sz(nn); } static int nfp_net_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, @@ -512,9 +518,12 @@ static int nfp_net_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, for (i = 0; i < ARRAY_SIZE(nn->rss_itbl); i++) indir[i] = nn->rss_itbl[i]; if (key) - memcpy(key, nn->rss_key, NFP_NET_CFG_RSS_KEY_SZ); - if (hfunc) - *hfunc = ETH_RSS_HASH_TOP; + memcpy(key, nn->rss_key, nfp_net_rss_key_sz(nn)); + if (hfunc) { + *hfunc = nn->rss_hfunc; + if (*hfunc >= 1 << ETH_RSS_HASH_FUNCS_COUNT) + *hfunc = ETH_RSS_HASH_UNKNOWN; + } return 0; } @@ -527,14 +536,14 @@ static int nfp_net_set_rxfh(struct net_device *netdev, int i; if (!(nn->cap & NFP_NET_CFG_CTRL_RSS) || - !(hfunc == ETH_RSS_HASH_NO_CHANGE || hfunc == ETH_RSS_HASH_TOP)) + !(hfunc == ETH_RSS_HASH_NO_CHANGE || hfunc == nn->rss_hfunc)) return -EOPNOTSUPP; if (!key && !indir) return 0; if (key) { - memcpy(nn->rss_key, key, NFP_NET_CFG_RSS_KEY_SZ); + memcpy(nn->rss_key, key, nfp_net_rss_key_sz(nn)); nfp_net_rss_write_key(nn); } if (indir) { -- cgit From 47465aed3220c3b95646bd53d3a6fd11cbf1ebfe Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 8 Mar 2017 08:57:02 -0800 Subject: nfp: implement .ndo_get_phys_port_name() NSP reports to us port labels. First id is the id of the physical port, the other one tells us which logical interface is it within a split port. Instead of printing them as string keep them in integer format. Compute which interfaces are part of port split. On netdev side use port labels and split information to provide a .ndo_get_phys_port_name() implementation. We follow the name format of mlxsw which is also suggested in "Port Netdev Naming" section of Documentation/networking/switchdev.txt. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 4 +++ .../net/ethernet/netronome/nfp/nfp_net_common.c | 22 +++++++++++++++ drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 2 ++ .../ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c | 31 +++++++++++++++++++--- .../ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h | 15 +++++++++-- 5 files changed, 69 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 9843e953bbed..50413eea9540 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -112,6 +112,7 @@ /* Forward declarations */ struct nfp_cpp; +struct nfp_eth_table_port; struct nfp_net; struct nfp_net_r_vector; @@ -496,6 +497,7 @@ struct nfp_stat_pair { * @ethtool_dump_flag: Ethtool dump flag * @port_list: Entry on device port list * @cpp: CPP device handle if available + * @eth_port: Translated ETH Table port entry */ struct nfp_net { struct pci_dev *pdev; @@ -587,6 +589,8 @@ struct nfp_net { struct list_head port_list; struct nfp_cpp *cpp; + + struct nfp_eth_table_port *eth_port; }; struct nfp_net_ring_set { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index e72468d65c28..5c34f79053f7 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -67,6 +67,7 @@ #include #include +#include "nfpcore/nfp_nsp_eth.h" #include "nfp_net_ctrl.h" #include "nfp_net.h" @@ -2831,6 +2832,26 @@ nfp_net_features_check(struct sk_buff *skb, struct net_device *dev, return features; } +static int +nfp_net_get_phys_port_name(struct net_device *netdev, char *name, size_t len) +{ + struct nfp_net *nn = netdev_priv(netdev); + int err; + + if (!nn->eth_port) + return -EOPNOTSUPP; + + if (!nn->eth_port->is_split) + err = snprintf(name, len, "p%d", nn->eth_port->label_port); + else + err = snprintf(name, len, "p%ds%d", nn->eth_port->label_port, + nn->eth_port->label_subport); + if (err >= len) + return -EINVAL; + + return 0; +} + /** * nfp_net_set_vxlan_port() - set vxlan port in SW and reconfigure HW * @nn: NFP Net device to reconfigure @@ -3009,6 +3030,7 @@ static const struct net_device_ops nfp_net_netdev_ops = { .ndo_set_mac_address = eth_mac_addr, .ndo_set_features = nfp_net_set_features, .ndo_features_check = nfp_net_features_check, + .ndo_get_phys_port_name = nfp_net_get_phys_port_name, .ndo_udp_tunnel_add = nfp_net_add_vxlan_port, .ndo_udp_tunnel_del = nfp_net_del_vxlan_port, .ndo_xdp = nfp_net_xdp, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 3afcdc11480c..f04d0b8e84ad 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -178,6 +178,8 @@ nfp_net_get_mac_addr(struct nfp_net *nn, struct nfp_pf *pf, unsigned int id) if (pf->eth_tbl->ports[i].eth_index == id) { const u8 *mac_addr = pf->eth_tbl->ports[i].mac_addr; + nn->eth_port = &pf->eth_tbl->ports[i]; + ether_addr_copy(nn->netdev->dev_addr, mac_addr); ether_addr_copy(nn->netdev->perm_addr, mac_addr); return; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 1ece1f8ae4b3..10a0c8392d2b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -134,9 +134,32 @@ nfp_eth_port_translate(const struct eth_table_entry *src, unsigned int index, nfp_eth_copy_mac_reverse(dst->mac_addr, src->mac_addr); - snprintf(dst->label, sizeof(dst->label) - 1, "%llu.%llu", - FIELD_GET(NSP_ETH_PORT_PHYLABEL, port), - FIELD_GET(NSP_ETH_PORT_LABEL, port)); + dst->label_port = FIELD_GET(NSP_ETH_PORT_PHYLABEL, port); + dst->label_subport = FIELD_GET(NSP_ETH_PORT_LABEL, port); +} + +static void +nfp_eth_mark_split_ports(struct nfp_cpp *cpp, struct nfp_eth_table *table) +{ + unsigned int i, j; + + for (i = 0; i < table->count; i++) + for (j = 0; j < table->count; j++) { + if (i == j) + continue; + if (table->ports[i].label_port != + table->ports[j].label_port) + continue; + if (table->ports[i].label_subport == + table->ports[j].label_subport) + nfp_warn(cpp, + "Port %d subport %d is a duplicate\n", + table->ports[i].label_port, + table->ports[i].label_subport); + + table->ports[i].is_split = true; + break; + } } /** @@ -203,6 +226,8 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) nfp_eth_port_translate(&entries[i], i, &table->ports[j++]); + nfp_eth_mark_split_ports(cpp, table); + kfree(entries); return table; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h index edf703d319c8..325e841ca90a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h @@ -49,10 +49,13 @@ * @lanes: number of channels * @speed: interface speed (in Mbps) * @mac_addr: interface MAC address - * @label: interface id string + * @label_port: port id + * @label_subport: id of interface within port (for split ports) * @enabled: is enabled? * @tx_enabled: is TX enabled? * @rx_enabled: is RX enabled? + * + * @is_split: is interface part of a split port */ struct nfp_eth_table { unsigned int count; @@ -65,14 +68,22 @@ struct nfp_eth_table { unsigned int speed; u8 mac_addr[ETH_ALEN]; - char label[8]; + + u8 label_port; + u8 label_subport; bool enabled; bool tx_enabled; bool rx_enabled; + + /* Computed fields */ + bool is_split; } ports[0]; }; +struct nfp_cpp; +struct nfp_nsp; + struct nfp_eth_table *nfp_eth_read_ports(struct nfp_cpp *cpp); struct nfp_eth_table * __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp); -- cgit From 43860c1211650ef4f2e03413cacb3420fdb69cde Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 8 Mar 2017 08:57:03 -0800 Subject: nfp: move more ring debug info to debugfs We already print most of ring configuration including descriptors in debugfs, add the few missing pieces and remove debug prints. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 9 --------- drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c | 13 +++++++++---- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 5c34f79053f7..0be47188fd36 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1776,11 +1776,6 @@ nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring, u32 cnt, bool is_xdp) netif_set_xps_queue(nn->netdev, &r_vec->affinity_mask, tx_ring->idx); - nn_dbg(nn, "TxQ%02d: QCidx=%02d cnt=%d dma=%#llx host=%p %s\n", - tx_ring->idx, tx_ring->qcidx, - tx_ring->cnt, (unsigned long long)tx_ring->dma, tx_ring->txds, - is_xdp ? "XDP" : ""); - return 0; err_alloc: @@ -1900,10 +1895,6 @@ nfp_net_rx_ring_alloc(struct nfp_net_rx_ring *rx_ring, unsigned int fl_bufsz, if (!rx_ring->rxbufs) goto err_alloc; - nn_dbg(nn, "RxQ%02d: FlQCidx=%02d RxQCidx=%02d cnt=%d dma=%#llx host=%p\n", - rx_ring->idx, rx_ring->fl_qcidx, rx_ring->rx_qcidx, - rx_ring->cnt, (unsigned long long)rx_ring->dma, rx_ring->rxds); - return 0; err_alloc: diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c index 6e9372a18375..edfa59e51fdd 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c @@ -64,8 +64,10 @@ static int nfp_net_debugfs_rx_q_read(struct seq_file *file, void *data) rx_rd_p = nfp_qcp_rd_ptr_read(rx_ring->qcp_rx); rx_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_rx); - seq_printf(file, "RX[%02d]: H_RD=%d H_WR=%d FL_RD=%d FL_WR=%d RX_RD=%d RX_WR=%d\n", - rx_ring->idx, rx_ring->rd_p, rx_ring->wr_p, + seq_printf(file, "RX[%02d,%02d,%02d]: cnt=%d dma=%pad host=%p H_RD=%d H_WR=%d FL_RD=%d FL_WR=%d RX_RD=%d RX_WR=%d\n", + rx_ring->idx, rx_ring->fl_qcidx, rx_ring->rx_qcidx, + rx_ring->cnt, &rx_ring->dma, rx_ring->rxds, + rx_ring->rd_p, rx_ring->wr_p, fl_rd_p, fl_wr_p, rx_rd_p, rx_wr_p); for (i = 0; i < rxd_cnt; i++) { @@ -151,8 +153,11 @@ static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data) d_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q); d_wr_p = nfp_qcp_wr_ptr_read(tx_ring->qcp_q); - seq_printf(file, "TX[%02d]: H_RD=%d H_WR=%d D_RD=%d D_WR=%d\n", - tx_ring->idx, tx_ring->rd_p, tx_ring->wr_p, d_rd_p, d_wr_p); + seq_printf(file, "TX[%02d,%02d%s]: cnt=%d dma=%pad host=%p H_RD=%d H_WR=%d D_RD=%d D_WR=%d\n", + tx_ring->idx, tx_ring->qcidx, + tx_ring == r_vec->tx_ring ? "" : "xdp", + tx_ring->cnt, &tx_ring->dma, tx_ring->txds, + tx_ring->rd_p, tx_ring->wr_p, d_rd_p, d_wr_p); for (i = 0; i < txd_cnt; i++) { txd = &tx_ring->txds[i]; -- cgit From bef6b1b7a6ffaa9afc8776c5e09e4ea11ac1727e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 8 Mar 2017 08:57:04 -0800 Subject: nfp: reorder variables in nfp_net_tx() Reorder variables longest to shortest to comply with netdev coding style. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 0be47188fd36..8b5fa6fe8807 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -739,10 +739,10 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) { struct nfp_net *nn = netdev_priv(netdev); const struct skb_frag_struct *frag; - struct nfp_net_r_vector *r_vec; struct nfp_net_tx_desc *txd, txdg; - struct nfp_net_tx_buf *txbuf; struct nfp_net_tx_ring *tx_ring; + struct nfp_net_r_vector *r_vec; + struct nfp_net_tx_buf *txbuf; struct netdev_queue *nd_q; dma_addr_t dma_addr; unsigned int fsize; -- cgit From fa43d2a895e61defdd32f6ed825740bd2f08106c Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 8 Mar 2017 08:57:05 -0800 Subject: nfp: store device pointer for the fastpath We really only need the device pointer on the fast path, stash it at the beginning of the adapter structure and move pci_dev pointer down. This saves up a few lines of code. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 6 ++- .../net/ethernet/netronome/nfp/nfp_net_common.c | 51 ++++++++++------------ drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 5 +-- .../net/ethernet/netronome/nfp/nfp_net_offload.c | 7 ++- 4 files changed, 31 insertions(+), 38 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 50413eea9540..4ad27570b6c4 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -435,7 +435,7 @@ struct nfp_stat_pair { /** * struct nfp_net - NFP network device structure - * @pdev: Backpointer to PCI device + * @dev: Backpointer to struct device * @netdev: Backpointer to net_device structure * @is_vf: Is the driver attached to a VF? * @bpf_offload_skip_sw: Offloaded BPF program will not be rerun by cls_bpf @@ -496,11 +496,12 @@ struct nfp_stat_pair { * @debugfs_dir: Device directory in debugfs * @ethtool_dump_flag: Ethtool dump flag * @port_list: Entry on device port list + * @pdev: Backpointer to PCI device * @cpp: CPP device handle if available * @eth_port: Translated ETH Table port entry */ struct nfp_net { - struct pci_dev *pdev; + struct device *dev; struct net_device *netdev; unsigned is_vf:1; @@ -588,6 +589,7 @@ struct nfp_net { struct list_head port_list; + struct pci_dev *pdev; struct nfp_cpp *cpp; struct nfp_eth_table_port *eth_port; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 8b5fa6fe8807..397d9e82ed0e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -89,7 +89,7 @@ static dma_addr_t nfp_net_dma_map_rx(struct nfp_net *nn, void *frag, unsigned int bufsz, int direction) { - return dma_map_single(&nn->pdev->dev, frag + NFP_NET_RX_BUF_HEADROOM, + return dma_map_single(nn->dev, frag + NFP_NET_RX_BUF_HEADROOM, bufsz - NFP_NET_RX_BUF_NON_DATA, direction); } @@ -97,7 +97,7 @@ static void nfp_net_dma_unmap_rx(struct nfp_net *nn, dma_addr_t dma_addr, unsigned int bufsz, int direction) { - dma_unmap_single(&nn->pdev->dev, dma_addr, + dma_unmap_single(nn->dev, dma_addr, bufsz - NFP_NET_RX_BUF_NON_DATA, direction); } @@ -768,9 +768,9 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) } /* Start with the head skbuf */ - dma_addr = dma_map_single(&nn->pdev->dev, skb->data, skb_headlen(skb), + dma_addr = dma_map_single(nn->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - if (dma_mapping_error(&nn->pdev->dev, dma_addr)) + if (dma_mapping_error(nn->dev, dma_addr)) goto err_free; wr_idx = tx_ring->wr_p & (tx_ring->cnt - 1); @@ -812,9 +812,9 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) frag = &skb_shinfo(skb)->frags[f]; fsize = skb_frag_size(frag); - dma_addr = skb_frag_dma_map(&nn->pdev->dev, frag, 0, + dma_addr = skb_frag_dma_map(nn->dev, frag, 0, fsize, DMA_TO_DEVICE); - if (dma_mapping_error(&nn->pdev->dev, dma_addr)) + if (dma_mapping_error(nn->dev, dma_addr)) goto err_unmap; wr_idx = (wr_idx + 1) & (tx_ring->cnt - 1); @@ -853,8 +853,7 @@ err_unmap: --f; while (f >= 0) { frag = &skb_shinfo(skb)->frags[f]; - dma_unmap_page(&nn->pdev->dev, - tx_ring->txbufs[wr_idx].dma_addr, + dma_unmap_page(nn->dev, tx_ring->txbufs[wr_idx].dma_addr, skb_frag_size(frag), DMA_TO_DEVICE); tx_ring->txbufs[wr_idx].skb = NULL; tx_ring->txbufs[wr_idx].dma_addr = 0; @@ -863,7 +862,7 @@ err_unmap: if (wr_idx < 0) wr_idx += tx_ring->cnt; } - dma_unmap_single(&nn->pdev->dev, tx_ring->txbufs[wr_idx].dma_addr, + dma_unmap_single(nn->dev, tx_ring->txbufs[wr_idx].dma_addr, skb_headlen(skb), DMA_TO_DEVICE); tx_ring->txbufs[wr_idx].skb = NULL; tx_ring->txbufs[wr_idx].dma_addr = 0; @@ -920,8 +919,7 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) if (fidx == -1) { /* unmap head */ - dma_unmap_single(&nn->pdev->dev, - tx_ring->txbufs[idx].dma_addr, + dma_unmap_single(nn->dev, tx_ring->txbufs[idx].dma_addr, skb_headlen(skb), DMA_TO_DEVICE); done_pkts += tx_ring->txbufs[idx].pkt_cnt; @@ -929,8 +927,7 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) } else { /* unmap fragment */ frag = &skb_shinfo(skb)->frags[fidx]; - dma_unmap_page(&nn->pdev->dev, - tx_ring->txbufs[idx].dma_addr, + dma_unmap_page(nn->dev, tx_ring->txbufs[idx].dma_addr, skb_frag_size(frag), DMA_TO_DEVICE); } @@ -1027,7 +1024,6 @@ nfp_net_tx_ring_reset(struct nfp_net *nn, struct nfp_net_tx_ring *tx_ring) { struct nfp_net_r_vector *r_vec = tx_ring->r_vec; const struct skb_frag_struct *frag; - struct pci_dev *pdev = nn->pdev; struct netdev_queue *nd_q; while (tx_ring->rd_p != tx_ring->wr_p) { @@ -1047,13 +1043,13 @@ nfp_net_tx_ring_reset(struct nfp_net *nn, struct nfp_net_tx_ring *tx_ring) if (tx_buf->fidx == -1) { /* unmap head */ - dma_unmap_single(&pdev->dev, tx_buf->dma_addr, + dma_unmap_single(nn->dev, tx_buf->dma_addr, skb_headlen(skb), DMA_TO_DEVICE); } else { /* unmap fragment */ frag = &skb_shinfo(skb)->frags[tx_buf->fidx]; - dma_unmap_page(&pdev->dev, tx_buf->dma_addr, + dma_unmap_page(nn->dev, tx_buf->dma_addr, skb_frag_size(frag), DMA_TO_DEVICE); } @@ -1157,7 +1153,7 @@ nfp_net_rx_alloc_one(struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr, direction = xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; *dma_addr = nfp_net_dma_map_rx(nn, frag, fl_bufsz, direction); - if (dma_mapping_error(&nn->pdev->dev, *dma_addr)) { + if (dma_mapping_error(nn->dev, *dma_addr)) { nfp_net_free_frag(frag, xdp); nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n"); return NULL; @@ -1181,7 +1177,7 @@ nfp_net_napi_alloc_one(struct nfp_net *nn, int direction, dma_addr_t *dma_addr) } *dma_addr = nfp_net_dma_map_rx(nn, frag, nn->fl_bufsz, direction); - if (dma_mapping_error(&nn->pdev->dev, *dma_addr)) { + if (dma_mapping_error(nn->dev, *dma_addr)) { nfp_net_free_frag(frag, nn->xdp_prog); nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n"); return NULL; @@ -1499,7 +1495,7 @@ nfp_net_tx_xdp_buf(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring, txbuf->pkt_cnt = 1; txbuf->real_len = pkt_len; - dma_sync_single_for_device(&nn->pdev->dev, rxbuf->dma_addr + pkt_off, + dma_sync_single_for_device(nn->dev, rxbuf->dma_addr + pkt_off, pkt_len, DMA_BIDIRECTIONAL); /* Build TX descriptor */ @@ -1611,7 +1607,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) nn->bpf_offload_xdp)) { int act; - dma_sync_single_for_cpu(&nn->pdev->dev, + dma_sync_single_for_cpu(nn->dev, rxbuf->dma_addr + pkt_off, pkt_len, DMA_BIDIRECTIONAL); act = nfp_net_run_xdp(xdp_prog, rxbuf->frag + data_off, @@ -1728,12 +1724,11 @@ static void nfp_net_tx_ring_free(struct nfp_net_tx_ring *tx_ring) { struct nfp_net_r_vector *r_vec = tx_ring->r_vec; struct nfp_net *nn = r_vec->nfp_net; - struct pci_dev *pdev = nn->pdev; kfree(tx_ring->txbufs); if (tx_ring->txds) - dma_free_coherent(&pdev->dev, tx_ring->size, + dma_free_coherent(nn->dev, tx_ring->size, tx_ring->txds, tx_ring->dma); tx_ring->cnt = 0; @@ -1756,13 +1751,12 @@ nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring, u32 cnt, bool is_xdp) { struct nfp_net_r_vector *r_vec = tx_ring->r_vec; struct nfp_net *nn = r_vec->nfp_net; - struct pci_dev *pdev = nn->pdev; int sz; tx_ring->cnt = cnt; tx_ring->size = sizeof(*tx_ring->txds) * tx_ring->cnt; - tx_ring->txds = dma_zalloc_coherent(&pdev->dev, tx_ring->size, + tx_ring->txds = dma_zalloc_coherent(nn->dev, tx_ring->size, &tx_ring->dma, GFP_KERNEL); if (!tx_ring->txds) goto err_alloc; @@ -1849,12 +1843,11 @@ static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring) { struct nfp_net_r_vector *r_vec = rx_ring->r_vec; struct nfp_net *nn = r_vec->nfp_net; - struct pci_dev *pdev = nn->pdev; kfree(rx_ring->rxbufs); if (rx_ring->rxds) - dma_free_coherent(&pdev->dev, rx_ring->size, + dma_free_coherent(nn->dev, rx_ring->size, rx_ring->rxds, rx_ring->dma); rx_ring->cnt = 0; @@ -1878,14 +1871,13 @@ nfp_net_rx_ring_alloc(struct nfp_net_rx_ring *rx_ring, unsigned int fl_bufsz, { struct nfp_net_r_vector *r_vec = rx_ring->r_vec; struct nfp_net *nn = r_vec->nfp_net; - struct pci_dev *pdev = nn->pdev; int sz; rx_ring->cnt = cnt; rx_ring->bufsz = fl_bufsz; rx_ring->size = sizeof(*rx_ring->rxds) * rx_ring->cnt; - rx_ring->rxds = dma_zalloc_coherent(&pdev->dev, rx_ring->size, + rx_ring->rxds = dma_zalloc_coherent(nn->dev, rx_ring->size, &rx_ring->dma, GFP_KERNEL); if (!rx_ring->rxds) goto err_alloc; @@ -3089,6 +3081,7 @@ struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev, nn = netdev_priv(netdev); nn->netdev = netdev; + nn->dev = &pdev->dev; nn->pdev = pdev; nn->max_tx_rings = max_tx_rings; @@ -3164,7 +3157,7 @@ static void nfp_net_rss_init(struct nfp_net *nn) func_bit = find_first_bit(&rss_cap_hfunc, NFP_NET_CFG_RSS_HFUNCS); if (func_bit == NFP_NET_CFG_RSS_HFUNCS) { - dev_warn(&nn->pdev->dev, + dev_warn(nn->dev, "Bad RSS config, defaulting to Toeplitz hash\n"); func_bit = ETH_RSS_HASH_TOP_BIT; } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index f04d0b8e84ad..8a9b3f3b95a8 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -141,8 +141,7 @@ nfp_net_get_mac_addr_hwinfo(struct nfp_net *nn, struct nfp_cpp *cpp, mac_str = nfp_hwinfo_lookup(cpp, name); if (!mac_str) { - dev_warn(&nn->pdev->dev, - "Can't lookup MAC address. Generate\n"); + dev_warn(nn->dev, "Can't lookup MAC address. Generate\n"); eth_hw_addr_random(nn->netdev); return; } @@ -150,7 +149,7 @@ nfp_net_get_mac_addr_hwinfo(struct nfp_net *nn, struct nfp_cpp *cpp, if (sscanf(mac_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &mac_addr[0], &mac_addr[1], &mac_addr[2], &mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6) { - dev_warn(&nn->pdev->dev, + dev_warn(nn->dev, "Can't parse MAC address (%s). Generate.\n", mac_str); eth_hw_addr_random(nn->netdev); return; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c index 18a851eb3508..f6ed1aa9d94b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c @@ -168,8 +168,7 @@ nfp_net_bpf_offload_prepare(struct nfp_net *nn, start_off = nn_readw(nn, NFP_NET_CFG_BPF_START); done_off = nn_readw(nn, NFP_NET_CFG_BPF_DONE); - *code = dma_zalloc_coherent(&nn->pdev->dev, code_sz, dma_addr, - GFP_KERNEL); + *code = dma_zalloc_coherent(nn->dev, code_sz, dma_addr, GFP_KERNEL); if (!*code) return -ENOMEM; @@ -181,7 +180,7 @@ nfp_net_bpf_offload_prepare(struct nfp_net *nn, return 0; out: - dma_free_coherent(&nn->pdev->dev, code_sz, *code, *dma_addr); + dma_free_coherent(nn->dev, code_sz, *code, *dma_addr); return ret; } @@ -214,7 +213,7 @@ nfp_net_bpf_load_and_start(struct nfp_net *nn, u32 tc_flags, if (err) nn_err(nn, "FW command error while enabling BPF: %d\n", err); - dma_free_coherent(&nn->pdev->dev, code_sz, code, dma_addr); + dma_free_coherent(nn->dev, code_sz, code, dma_addr); nfp_net_bpf_stats_reset(nn); mod_timer(&nn->rx_filter_stats_timer, jiffies + NFP_NET_STAT_POLL_IVL); -- cgit From 7de5f115e1fd8a6df7aa89078cb70700e725173d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 8 Mar 2017 08:57:06 -0800 Subject: nfp: avoid rearming the interrupts when in busy poll Make use of return code from napi_complete_done() to avoid rearming interrupts when busy polling is on. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 397d9e82ed0e..119775d4097e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1705,10 +1705,9 @@ static int nfp_net_poll(struct napi_struct *napi, int budget) nfp_net_xdp_complete(r_vec->xdp_ring); } - if (pkts_polled < budget) { - napi_complete_done(napi, pkts_polled); - nfp_net_irq_unmask(r_vec->nfp_net, r_vec->irq_entry); - } + if (pkts_polled < budget) + if (napi_complete_done(napi, pkts_polled)) + nfp_net_irq_unmask(r_vec->nfp_net, r_vec->irq_entry); return pkts_polled; } -- cgit From b9dcf88a47ebab2743a0c627a95126c4cb3e9883 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 8 Mar 2017 08:57:07 -0800 Subject: nfp: add metadata format bit We only need FW version in the first cache line of adapter struct because we need to know the metadata format. To save space add a metadata format bit. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 2 ++ drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 4ad27570b6c4..34f8c439f42f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -440,6 +440,7 @@ struct nfp_stat_pair { * @is_vf: Is the driver attached to a VF? * @bpf_offload_skip_sw: Offloaded BPF program will not be rerun by cls_bpf * @bpf_offload_xdp: Offloaded BPF program is XDP + * @chained_metadata_format: Firemware will use new metadata format * @ctrl: Local copy of the control register/word. * @fl_bufsz: Currently configured size of the freelist buffers * @rx_offset: Offset in the RX buffers where packet data starts @@ -507,6 +508,7 @@ struct nfp_net { unsigned is_vf:1; unsigned bpf_offload_skip_sw:1; unsigned bpf_offload_xdp:1; + unsigned chained_metadata_format:1; u32 ctrl; u32 fl_bufsz; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 119775d4097e..2d964d030dbe 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1652,7 +1652,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) skb_reserve(skb, data_off); skb_put(skb, pkt_len); - if (nn->fw_ver.major <= 3) { + if (!nn->chained_metadata_format) { nfp_net_set_hash_desc(nn->netdev, skb, rxd); } else if (meta_len) { void *end; @@ -3196,6 +3196,8 @@ int nfp_net_netdev_init(struct net_device *netdev) struct nfp_net *nn = netdev_priv(netdev); int err; + nn->chained_metadata_format = nn->fw_ver.major > 3; + /* Get some of the read-only fields from the BAR */ nn->cap = nn_readl(nn, NFP_NET_CFG_CAP); nn->max_mtu = nn_readl(nn, NFP_NET_CFG_MAX_MTU); -- cgit From 5692dbb56e6012c0755614ee64fe4c221f357e7a Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 8 Mar 2017 08:57:08 -0800 Subject: nfp: prevent theoretical buffer overrun in nfp_eth_read_ports Prevent theoretical buffer overrun by returning an error if the number of entries returned by the firmware does not match those present. Also use a common handling error path. Found by inspection. Signed-off-by: Simon Horman Tested-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c | 34 +++++++++++++--------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 10a0c8392d2b..38bd80077e33 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -191,8 +191,7 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) { struct eth_table_entry *entries; struct nfp_eth_table *table; - unsigned int cnt; - int i, j, ret; + int i, j, ret, cnt = 0; entries = kzalloc(NSP_ETH_TABLE_SIZE, GFP_KERNEL); if (!entries) @@ -201,24 +200,27 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) ret = nfp_nsp_read_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); if (ret < 0) { nfp_err(cpp, "reading port table failed %d\n", ret); - kfree(entries); - return NULL; + goto err; } - /* Some versions of flash will give us 0 instead of port count */ - cnt = ret; - if (!cnt) { - for (i = 0; i < NSP_ETH_MAX_COUNT; i++) - if (entries[i].port & NSP_ETH_PORT_LANES_MASK) - cnt++; + for (i = 0; i < NSP_ETH_MAX_COUNT; i++) + if (entries[i].port & NSP_ETH_PORT_LANES_MASK) + cnt++; + + /* Some versions of flash will give us 0 instead of port count. + * For those that give a port count, verify it against the value + * calculated above. + */ + if (ret && ret != cnt) { + nfp_err(cpp, "table entry count reported (%d) does not match entries present (%d)\n", + ret, cnt); + goto err; } table = kzalloc(sizeof(*table) + sizeof(struct nfp_eth_table_port) * cnt, GFP_KERNEL); - if (!table) { - kfree(entries); - return NULL; - } + if (!table) + goto err; table->count = cnt; for (i = 0, j = 0; i < NSP_ETH_MAX_COUNT; i++) @@ -231,6 +233,10 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) kfree(entries); return table; + +err: + kfree(entries); + return NULL; } /** -- cgit From d5e73f7be850323ae3adbbe84ed37a38b0c31476 Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Wed, 8 Mar 2017 10:55:51 -0800 Subject: bonding: restructure arp-monitor In preparation to move the work-queue initialization to port creation from current port_open phase. Work-queue initialization does not make sense every time we do 'ifup/ifdown'. So moving to port creation phase. Arp monitoring work depends on the bonding mode and that is not tied to the port creation and can change anytime during the life after port creation. So this restructuring allows us to move the initialization at creation without losing the ability to arm the correct work for the mode user has selected. Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 8a4ba8b88e52..619f0c65f18a 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2575,10 +2575,8 @@ static bool bond_time_in_interval(struct bonding *bond, unsigned long last_act, * arp is transmitted to generate traffic. see activebackup_arp_monitor for * arp monitoring in active backup mode. */ -static void bond_loadbalance_arp_mon(struct work_struct *work) +static void bond_loadbalance_arp_mon(struct bonding *bond) { - struct bonding *bond = container_of(work, struct bonding, - arp_work.work); struct slave *slave, *oldcurrent; struct list_head *iter; int do_failover = 0, slave_state_changed = 0; @@ -2916,10 +2914,8 @@ check_state: return should_notify_rtnl; } -static void bond_activebackup_arp_mon(struct work_struct *work) +static void bond_activebackup_arp_mon(struct bonding *bond) { - struct bonding *bond = container_of(work, struct bonding, - arp_work.work); bool should_notify_peers = false; bool should_notify_rtnl = false; int delta_in_ticks; @@ -2972,6 +2968,17 @@ re_arm: } } +static void bond_arp_monitor(struct work_struct *work) +{ + struct bonding *bond = container_of(work, struct bonding, + arp_work.work); + + if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) + bond_activebackup_arp_mon(bond); + else + bond_loadbalance_arp_mon(bond); +} + /*-------------------------- netdev event handling --------------------------*/ /* Change device name */ @@ -3228,10 +3235,7 @@ static void bond_work_init_all(struct bonding *bond) bond_resend_igmp_join_requests_delayed); INIT_DELAYED_WORK(&bond->alb_work, bond_alb_monitor); INIT_DELAYED_WORK(&bond->mii_work, bond_mii_monitor); - if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) - INIT_DELAYED_WORK(&bond->arp_work, bond_activebackup_arp_mon); - else - INIT_DELAYED_WORK(&bond->arp_work, bond_loadbalance_arp_mon); + INIT_DELAYED_WORK(&bond->arp_work, bond_arp_monitor); INIT_DELAYED_WORK(&bond->ad_work, bond_3ad_state_machine_handler); INIT_DELAYED_WORK(&bond->slave_arr_work, bond_slave_arr_handler); } -- cgit From 4493b81bea24269df898339dee638d7c5cb2b2df Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Wed, 8 Mar 2017 10:55:54 -0800 Subject: bonding: initialize work-queues during creation of bond Initializing work-queues every time ifup operation performed is unnecessary and can be performed only once when the port is created. Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 619f0c65f18a..1329110ed85f 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3270,8 +3270,6 @@ static int bond_open(struct net_device *bond_dev) } } - bond_work_init_all(bond); - if (bond_is_lb(bond)) { /* bond_alb_initialize must be called before the timer * is started. @@ -4691,6 +4689,8 @@ int bond_create(struct net *net, const char *name) netif_carrier_off(bond_dev); + bond_work_init_all(bond); + rtnl_unlock(); if (res < 0) bond_destructor(bond_dev); -- cgit From 8b426dc54cf4056984bab7dfa48c92ee79a46434 Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Wed, 8 Mar 2017 10:55:56 -0800 Subject: bonding: remove hardcoded value Eliminate hard-coded value and use the default that is set. Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 1329110ed85f..0f9f5ceae80e 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4254,6 +4254,7 @@ static int bond_check_params(struct bond_params *params) int arp_all_targets_value; u16 ad_actor_sys_prio = 0; u16 ad_user_port_key = 0; + int tlb_dynamic_lb = 0; /* Convert string parameters. */ if (mode) { @@ -4566,6 +4567,17 @@ static int bond_check_params(struct bond_params *params) } ad_user_port_key = valptr->value; + if (bond_mode == BOND_MODE_TLB) { + bond_opt_initstr(&newval, "default"); + valptr = bond_opt_parse(bond_opt_get(BOND_OPT_TLB_DYNAMIC_LB), + &newval); + if (!valptr) { + pr_err("Error: No tlb_dynamic_lb default value"); + return -EINVAL; + } + tlb_dynamic_lb = valptr->value; + } + if (lp_interval == 0) { pr_warn("Warning: ip_interval must be between 1 and %d, so it was reset to %d\n", INT_MAX, BOND_ALB_DEFAULT_LP_INTERVAL); @@ -4593,7 +4605,7 @@ static int bond_check_params(struct bond_params *params) params->min_links = min_links; params->lp_interval = lp_interval; params->packets_per_slave = packets_per_slave; - params->tlb_dynamic_lb = 1; /* Default value */ + params->tlb_dynamic_lb = tlb_dynamic_lb; params->ad_actor_sys_prio = ad_actor_sys_prio; eth_zero_addr(params->ad_actor_system); params->ad_user_port_key = ad_user_port_key; -- cgit From ec891c8b8da2c3862a5f76ba72f6d140c418d812 Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Wed, 8 Mar 2017 10:55:59 -0800 Subject: bonding: remove "port-moved" state that was never implemented LACP state-machine defines "port-moved" state when the same ActorSystemID and Port are seen in a LACPDU received on different port. The state is never set since it's not implemented. However the state-machine attempts to clear that state occasionally. LACP state machine is already complicated and since this state is not implemented, removing it's checks makes the state-machine little simpler. Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- drivers/net/bonding/bond_3ad.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index edc70ffad660..431926bba9f4 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -1052,8 +1052,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) port->sm_rx_state = AD_RX_INITIALIZE; port->sm_vars |= AD_PORT_CHURNED; /* check if port is not enabled */ - } else if (!(port->sm_vars & AD_PORT_BEGIN) - && !port->is_enabled && !(port->sm_vars & AD_PORT_MOVED)) + } else if (!(port->sm_vars & AD_PORT_BEGIN) && !port->is_enabled) port->sm_rx_state = AD_RX_PORT_DISABLED; /* check if new lacpdu arrived */ else if (lacpdu && ((port->sm_rx_state == AD_RX_EXPIRED) || @@ -1081,11 +1080,8 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) /* if no lacpdu arrived and no timer is on */ switch (port->sm_rx_state) { case AD_RX_PORT_DISABLED: - if (port->sm_vars & AD_PORT_MOVED) - port->sm_rx_state = AD_RX_INITIALIZE; - else if (port->is_enabled - && (port->sm_vars - & AD_PORT_LACP_ENABLED)) + if (port->is_enabled && + (port->sm_vars & AD_PORT_LACP_ENABLED)) port->sm_rx_state = AD_RX_EXPIRED; else if (port->is_enabled && ((port->sm_vars @@ -1115,7 +1111,6 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) port->sm_vars &= ~AD_PORT_SELECTED; __record_default(port); port->actor_oper_port_state &= ~AD_STATE_EXPIRED; - port->sm_vars &= ~AD_PORT_MOVED; port->sm_rx_state = AD_RX_PORT_DISABLED; /* Fall Through */ -- cgit From dc9c4d0fe023b508f1b41dbe2a3f3133f81b4d29 Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Wed, 8 Mar 2017 10:56:02 -0800 Subject: bonding: reduce scope of some global variables Many of the bond param variables are declared global while it's not really necessary for these variables to be global. So moving them to the location these are used. Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 0f9f5ceae80e..ba934020dfaa 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -201,12 +201,6 @@ atomic_t netpoll_block_tx = ATOMIC_INIT(0); unsigned int bond_net_id __read_mostly; -static __be32 arp_target[BOND_MAX_ARP_TARGETS]; -static int arp_ip_count; -static int bond_mode = BOND_MODE_ROUNDROBIN; -static int xmit_hashtype = BOND_XMIT_POLICY_LAYER2; -static int lacp_fast; - /*-------------------------- Forward declarations ---------------------------*/ static int bond_init(struct net_device *bond_dev); @@ -4254,6 +4248,11 @@ static int bond_check_params(struct bond_params *params) int arp_all_targets_value; u16 ad_actor_sys_prio = 0; u16 ad_user_port_key = 0; + __be32 arp_target[BOND_MAX_ARP_TARGETS]; + int arp_ip_count; + int bond_mode = BOND_MODE_ROUNDROBIN; + int xmit_hashtype = BOND_XMIT_POLICY_LAYER2; + int lacp_fast = 0; int tlb_dynamic_lb = 0; /* Convert string parameters. */ -- cgit From 9efd3831d5ae3babb45a37ae7d6b18642a0745de Mon Sep 17 00:00:00 2001 From: Sergey Shcherbakov Date: Thu, 9 Mar 2017 02:58:14 +0200 Subject: net: ks8851: Added support for half-duplex SPI In original driver was implemented support for half- and full-duplex modes, but it was not enabled. Instead of it ks8851_rx_1msg method always returns "true" that means "full-duplex" mode. This patch replaces hard-coded functionality with flexible solution that supports both SPI modes. Signed-off-by: Sergey Shcherbakov Signed-off-by: David S. Miller --- drivers/net/ethernet/micrel/ks8851.c | 41 ++++++++++-------------------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/drivers/net/ethernet/micrel/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c index 279ee4612981..20358f87de57 100644 --- a/drivers/net/ethernet/micrel/ks8851.c +++ b/drivers/net/ethernet/micrel/ks8851.c @@ -211,25 +211,6 @@ static void ks8851_wrreg8(struct ks8851_net *ks, unsigned reg, unsigned val) netdev_err(ks->netdev, "spi_sync() failed\n"); } -/** - * ks8851_rx_1msg - select whether to use one or two messages for spi read - * @ks: The device structure - * - * Return whether to generate a single message with a tx and rx buffer - * supplied to spi_sync(), or alternatively send the tx and rx buffers - * as separate messages. - * - * Depending on the hardware in use, a single message may be more efficient - * on interrupts or work done by the driver. - * - * This currently always returns true until we add some per-device data passed - * from the platform code to specify which mode is better. - */ -static inline bool ks8851_rx_1msg(struct ks8851_net *ks) -{ - return true; -} - /** * ks8851_rdreg - issue read register command and return the data * @ks: The device state @@ -251,14 +232,7 @@ static void ks8851_rdreg(struct ks8851_net *ks, unsigned op, txb[0] = cpu_to_le16(op | KS_SPIOP_RD); - if (ks8851_rx_1msg(ks)) { - msg = &ks->spi_msg1; - xfer = &ks->spi_xfer1; - - xfer->tx_buf = txb; - xfer->rx_buf = trx; - xfer->len = rxl + 2; - } else { + if (ks->spidev->master->flags & SPI_MASTER_HALF_DUPLEX) { msg = &ks->spi_msg2; xfer = ks->spi_xfer2; @@ -270,15 +244,22 @@ static void ks8851_rdreg(struct ks8851_net *ks, unsigned op, xfer->tx_buf = NULL; xfer->rx_buf = trx; xfer->len = rxl; + } else { + msg = &ks->spi_msg1; + xfer = &ks->spi_xfer1; + + xfer->tx_buf = txb; + xfer->rx_buf = trx; + xfer->len = rxl + 2; } ret = spi_sync(ks->spidev, msg); if (ret < 0) netdev_err(ks->netdev, "read: spi_sync() failed\n"); - else if (ks8851_rx_1msg(ks)) - memcpy(rxb, trx + 2, rxl); - else + else if (ks->spidev->master->flags & SPI_MASTER_HALF_DUPLEX) memcpy(rxb, trx, rxl); + else + memcpy(rxb, trx + 2, rxl); } /** -- cgit From a30aad50c26cac63026e5dfcc2e055ae63fe6ef7 Mon Sep 17 00:00:00 2001 From: Alexey Kodanev Date: Thu, 9 Mar 2017 13:53:55 +0300 Subject: tcp: rename *_sequence_number() to *_seq_and_tsoff() The functions that are returning tcp sequence number also setup TS offset value, so rename them to better describe their purpose. No functional changes in this patch. Suggested-by: Eric Dumazet Signed-off-by: Alexey Kodanev Signed-off-by: David S. Miller --- include/net/secure_seq.h | 6 +++--- include/net/tcp.h | 2 +- net/core/secure_seq.c | 13 ++++++------- net/ipv4/tcp_input.c | 4 ++-- net/ipv4/tcp_ipv4.c | 22 +++++++++++----------- net/ipv6/tcp_ipv6.c | 22 +++++++++++----------- 6 files changed, 34 insertions(+), 35 deletions(-) diff --git a/include/net/secure_seq.h b/include/net/secure_seq.h index 0caee631a836..fe236b3429f0 100644 --- a/include/net/secure_seq.h +++ b/include/net/secure_seq.h @@ -6,10 +6,10 @@ u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport); u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, __be16 dport); -u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, +u32 secure_tcp_seq_and_tsoff(__be32 saddr, __be32 daddr, + __be16 sport, __be16 dport, u32 *tsoff); +u32 secure_tcpv6_seq_and_tsoff(const __be32 *saddr, const __be32 *daddr, __be16 sport, __be16 dport, u32 *tsoff); -u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, - __be16 sport, __be16 dport, u32 *tsoff); u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport); u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr, diff --git a/include/net/tcp.h b/include/net/tcp.h index 6ec4ea652f3f..bede8f7fa742 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1816,7 +1816,7 @@ struct tcp_request_sock_ops { struct dst_entry *(*route_req)(const struct sock *sk, struct flowi *fl, const struct request_sock *req, bool *strict); - __u32 (*init_seq)(const struct sk_buff *skb, u32 *tsoff); + __u32 (*init_seq_tsoff)(const struct sk_buff *skb, u32 *tsoff); int (*send_synack)(const struct sock *sk, struct dst_entry *dst, struct flowi *fl, struct request_sock *req, struct tcp_fastopen_cookie *foc, diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c index 758f140b6bed..fb87e78a2cc7 100644 --- a/net/core/secure_seq.c +++ b/net/core/secure_seq.c @@ -45,8 +45,8 @@ static u32 seq_scale(u32 seq) #endif #if IS_ENABLED(CONFIG_IPV6) -u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, - __be16 sport, __be16 dport, u32 *tsoff) +u32 secure_tcpv6_seq_and_tsoff(const __be32 *saddr, const __be32 *daddr, + __be16 sport, __be16 dport, u32 *tsoff) { const struct { struct in6_addr saddr; @@ -66,7 +66,7 @@ u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, *tsoff = sysctl_tcp_timestamps == 1 ? (hash >> 32) : 0; return seq_scale(hash); } -EXPORT_SYMBOL(secure_tcpv6_sequence_number); +EXPORT_SYMBOL(secure_tcpv6_seq_and_tsoff); u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, __be16 dport) @@ -89,14 +89,13 @@ EXPORT_SYMBOL(secure_ipv6_port_ephemeral); #ifdef CONFIG_INET -/* secure_tcp_sequence_number(a, b, 0, d) == secure_ipv4_port_ephemeral(a, b, d), +/* secure_tcp_seq_and_tsoff(a, b, 0, d) == secure_ipv4_port_ephemeral(a, b, d), * but fortunately, `sport' cannot be 0 in any circumstances. If this changes, * it would be easy enough to have the former function use siphash_4u32, passing * the arguments as separate u32. */ - -u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, - __be16 sport, __be16 dport, u32 *tsoff) +u32 secure_tcp_seq_and_tsoff(__be32 saddr, __be32 daddr, + __be16 sport, __be16 dport, u32 *tsoff) { u64 hash; net_secret_init(); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 39c393cc0fd3..96b67a8b18c3 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6324,7 +6324,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, goto drop_and_free; if (isn && tmp_opt.tstamp_ok) - af_ops->init_seq(skb, &tcp_rsk(req)->ts_off); + af_ops->init_seq_tsoff(skb, &tcp_rsk(req)->ts_off); if (!want_cookie && !isn) { /* VJ's idea. We save last timestamp seen @@ -6366,7 +6366,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, goto drop_and_release; } - isn = af_ops->init_seq(skb, &tcp_rsk(req)->ts_off); + isn = af_ops->init_seq_tsoff(skb, &tcp_rsk(req)->ts_off); } if (!dst) { dst = af_ops->route_req(sk, &fl, req, NULL); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 9a89b8deafae..7b332ed66488 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -94,12 +94,12 @@ static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key, struct inet_hashinfo tcp_hashinfo; EXPORT_SYMBOL(tcp_hashinfo); -static u32 tcp_v4_init_sequence(const struct sk_buff *skb, u32 *tsoff) +static u32 tcp_v4_init_seq_and_tsoff(const struct sk_buff *skb, u32 *tsoff) { - return secure_tcp_sequence_number(ip_hdr(skb)->daddr, - ip_hdr(skb)->saddr, - tcp_hdr(skb)->dest, - tcp_hdr(skb)->source, tsoff); + return secure_tcp_seq_and_tsoff(ip_hdr(skb)->daddr, + ip_hdr(skb)->saddr, + tcp_hdr(skb)->dest, + tcp_hdr(skb)->source, tsoff); } int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp) @@ -236,11 +236,11 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) rt = NULL; if (likely(!tp->repair)) { - seq = secure_tcp_sequence_number(inet->inet_saddr, - inet->inet_daddr, - inet->inet_sport, - usin->sin_port, - &tp->tsoffset); + seq = secure_tcp_seq_and_tsoff(inet->inet_saddr, + inet->inet_daddr, + inet->inet_sport, + usin->sin_port, + &tp->tsoffset); if (!tp->write_seq) tp->write_seq = seq; } @@ -1249,7 +1249,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = { .cookie_init_seq = cookie_v4_init_sequence, #endif .route_req = tcp_v4_route_req, - .init_seq = tcp_v4_init_sequence, + .init_seq_tsoff = tcp_v4_init_seq_and_tsoff, .send_synack = tcp_v4_send_synack, }; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 60a5295a7de6..56f742fff967 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -101,12 +101,12 @@ static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) } } -static u32 tcp_v6_init_sequence(const struct sk_buff *skb, u32 *tsoff) +static u32 tcp_v6_init_seq_and_tsoff(const struct sk_buff *skb, u32 *tsoff) { - return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32, - ipv6_hdr(skb)->saddr.s6_addr32, - tcp_hdr(skb)->dest, - tcp_hdr(skb)->source, tsoff); + return secure_tcpv6_seq_and_tsoff(ipv6_hdr(skb)->daddr.s6_addr32, + ipv6_hdr(skb)->saddr.s6_addr32, + tcp_hdr(skb)->dest, + tcp_hdr(skb)->source, tsoff); } static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, @@ -287,11 +287,11 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, sk_set_txhash(sk); if (likely(!tp->repair)) { - seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32, - sk->sk_v6_daddr.s6_addr32, - inet->inet_sport, - inet->inet_dport, - &tp->tsoffset); + seq = secure_tcpv6_seq_and_tsoff(np->saddr.s6_addr32, + sk->sk_v6_daddr.s6_addr32, + inet->inet_sport, + inet->inet_dport, + &tp->tsoffset); if (!tp->write_seq) tp->write_seq = seq; } @@ -755,7 +755,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { .cookie_init_seq = cookie_v6_init_sequence, #endif .route_req = tcp_v6_route_req, - .init_seq = tcp_v6_init_sequence, + .init_seq_tsoff = tcp_v6_init_seq_and_tsoff, .send_synack = tcp_v6_send_synack, }; -- cgit From a150201a70da3bcbe76c85c255c6ab2d342c3278 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Thu, 9 Mar 2017 09:25:19 +0100 Subject: mlxsw: spectrum: Add support for vlan modify TC action Add VLAN action offloading. Invoke it from Spectrum flower handler for "vlan modify" actions. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../mellanox/mlxsw/core_acl_flex_actions.c | 83 ++++++++++++++++++++++ .../mellanox/mlxsw/core_acl_flex_actions.h | 2 + drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 3 + drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 29 ++++++++ .../net/ethernet/mellanox/mlxsw/spectrum_flower.c | 10 +++ include/net/tc_act/tc_vlan.h | 5 ++ 6 files changed, 132 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c index 5f337715a4da..fe3c6ea16a99 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c @@ -567,6 +567,89 @@ static char *mlxsw_afa_block_append_action(struct mlxsw_afa_block *block, return oneact + MLXSW_AFA_PAYLOAD_OFFSET; } +/* VLAN Action + * ----------- + * VLAN action is used for manipulating VLANs. It can be used to implement QinQ, + * VLAN translation, change of PCP bits of the VLAN tag, push, pop as swap VLANs + * and more. + */ + +#define MLXSW_AFA_VLAN_CODE 0x02 +#define MLXSW_AFA_VLAN_SIZE 1 + +enum mlxsw_afa_vlan_vlan_tag_cmd { + MLXSW_AFA_VLAN_VLAN_TAG_CMD_NOP, + MLXSW_AFA_VLAN_VLAN_TAG_CMD_PUSH_TAG, + MLXSW_AFA_VLAN_VLAN_TAG_CMD_POP_TAG, +}; + +enum mlxsw_afa_vlan_cmd { + MLXSW_AFA_VLAN_CMD_NOP, + MLXSW_AFA_VLAN_CMD_SET_OUTER, + MLXSW_AFA_VLAN_CMD_SET_INNER, + MLXSW_AFA_VLAN_CMD_COPY_OUTER_TO_INNER, + MLXSW_AFA_VLAN_CMD_COPY_INNER_TO_OUTER, + MLXSW_AFA_VLAN_CMD_SWAP, +}; + +/* afa_vlan_vlan_tag_cmd + * Tag command: push, pop, nop VLAN header. + */ +MLXSW_ITEM32(afa, vlan, vlan_tag_cmd, 0x00, 29, 3); + +/* afa_vlan_vid_cmd */ +MLXSW_ITEM32(afa, vlan, vid_cmd, 0x04, 29, 3); + +/* afa_vlan_vid */ +MLXSW_ITEM32(afa, vlan, vid, 0x04, 0, 12); + +/* afa_vlan_ethertype_cmd */ +MLXSW_ITEM32(afa, vlan, ethertype_cmd, 0x08, 29, 3); + +/* afa_vlan_ethertype + * Index to EtherTypes in Switch VLAN EtherType Register (SVER). + */ +MLXSW_ITEM32(afa, vlan, ethertype, 0x08, 24, 3); + +/* afa_vlan_pcp_cmd */ +MLXSW_ITEM32(afa, vlan, pcp_cmd, 0x08, 13, 3); + +/* afa_vlan_pcp */ +MLXSW_ITEM32(afa, vlan, pcp, 0x08, 8, 3); + +static inline void +mlxsw_afa_vlan_pack(char *payload, + enum mlxsw_afa_vlan_vlan_tag_cmd vlan_tag_cmd, + enum mlxsw_afa_vlan_cmd vid_cmd, u16 vid, + enum mlxsw_afa_vlan_cmd pcp_cmd, u8 pcp, + enum mlxsw_afa_vlan_cmd ethertype_cmd, u8 ethertype) +{ + mlxsw_afa_vlan_vlan_tag_cmd_set(payload, vlan_tag_cmd); + mlxsw_afa_vlan_vid_cmd_set(payload, vid_cmd); + mlxsw_afa_vlan_vid_set(payload, vid); + mlxsw_afa_vlan_pcp_cmd_set(payload, pcp_cmd); + mlxsw_afa_vlan_pcp_set(payload, pcp); + mlxsw_afa_vlan_ethertype_cmd_set(payload, ethertype_cmd); + mlxsw_afa_vlan_ethertype_set(payload, ethertype); +} + +int mlxsw_afa_block_append_vlan_modify(struct mlxsw_afa_block *block, + u16 vid, u8 pcp, u8 et) +{ + char *act = mlxsw_afa_block_append_action(block, + MLXSW_AFA_VLAN_CODE, + MLXSW_AFA_VLAN_SIZE); + + if (!act) + return -ENOBUFS; + mlxsw_afa_vlan_pack(act, MLXSW_AFA_VLAN_VLAN_TAG_CMD_NOP, + MLXSW_AFA_VLAN_CMD_SET_OUTER, vid, + MLXSW_AFA_VLAN_CMD_SET_OUTER, pcp, + MLXSW_AFA_VLAN_CMD_SET_OUTER, et); + return 0; +} +EXPORT_SYMBOL(mlxsw_afa_block_append_vlan_modify); + /* Trap / Discard Action * --------------------- * The Trap / Discard action enables trapping / mirroring packets to the CPU diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h index 43f78dcfe394..6e103ac41d99 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h @@ -62,5 +62,7 @@ void mlxsw_afa_block_jump(struct mlxsw_afa_block *block, u16 group_id); int mlxsw_afa_block_append_drop(struct mlxsw_afa_block *block); int mlxsw_afa_block_append_fwd(struct mlxsw_afa_block *block, u8 local_port, bool in_port); +int mlxsw_afa_block_append_vlan_modify(struct mlxsw_afa_block *block, + u16 vid, u8 pcp, u8 et); #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 13ec85e7c392..ac445d810eb3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -679,6 +679,9 @@ int mlxsw_sp_acl_rulei_act_drop(struct mlxsw_sp_acl_rule_info *rulei); int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_rule_info *rulei, struct net_device *out_dev); +int mlxsw_sp_acl_rulei_act_vlan(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + u32 action, u16 vid, u16 proto, u8 prio); struct mlxsw_sp_acl_rule; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index 8a18b3aa70dc..3c5ea7e41db0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "reg.h" #include "core.h" @@ -335,6 +336,34 @@ int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp, local_port, in_port); } +int mlxsw_sp_acl_rulei_act_vlan(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + u32 action, u16 vid, u16 proto, u8 prio) +{ + u8 ethertype; + + if (action == TCA_VLAN_ACT_MODIFY) { + switch (proto) { + case ETH_P_8021Q: + ethertype = 0; + break; + case ETH_P_8021AD: + ethertype = 1; + break; + default: + dev_err(mlxsw_sp->bus_info->dev, "Unsupported VLAN protocol %#04x\n", + proto); + return -EINVAL; + } + + return mlxsw_afa_block_append_vlan_modify(rulei->act_block, + vid, prio, ethertype); + } else { + dev_err(mlxsw_sp->bus_info->dev, "Unsupported VLAN action\n"); + return -EINVAL; + } +} + struct mlxsw_sp_acl_rule * mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 22ab42925377..d8984074f358 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "spectrum.h" #include "core_acl_flex_keys.h" @@ -73,6 +74,15 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, out_dev); if (err) return err; + } else if (is_tcf_vlan(a)) { + u16 proto = be16_to_cpu(tcf_vlan_push_proto(a)); + u32 action = tcf_vlan_action(a); + u8 prio = tcf_vlan_push_prio(a); + u16 vid = tcf_vlan_push_vid(a); + + return mlxsw_sp_acl_rulei_act_vlan(mlxsw_sp, rulei, + action, vid, + proto, prio); } else { dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n"); return -EOPNOTSUPP; diff --git a/include/net/tc_act/tc_vlan.h b/include/net/tc_act/tc_vlan.h index 48cca321ee6c..9690c047b6cf 100644 --- a/include/net/tc_act/tc_vlan.h +++ b/include/net/tc_act/tc_vlan.h @@ -49,4 +49,9 @@ static inline __be16 tcf_vlan_push_proto(const struct tc_action *a) return to_vlan(a)->tcfv_push_proto; } +static inline u8 tcf_vlan_push_prio(const struct tc_action *a) +{ + return to_vlan(a)->tcfv_push_prio; +} + #endif /* __NET_TC_VLAN_H */ -- cgit From 9caab08a7685c874e7f20ef16f8191daf85609ae Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Thu, 9 Mar 2017 09:25:20 +0100 Subject: mlxsw: spectrum: Add support for flower matches on VLAN ID, PCP Introduce MLXSW_AFK_ELEMENT_VID, PCP and declare them in afk_element infos that contain them. Use the elements when VLAD ID or priority are used in the flow. Also add MLXSW_AFK_ELEMENT_VID, PCP to mlxsw_sp_acl_tcam_pattern_ipv4. Both items are included in mlxsw_sp_afk_element_info_l2_dmac, resp. _smac, and both MLXSW_AFK_ELEMENT_SMAC and _DMAC are already in the pattern. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/core_acl_flex_keys.h | 6 +++++- .../mellanox/mlxsw/spectrum_acl_flex_keys.h | 6 ++++++ .../ethernet/mellanox/mlxsw/spectrum_acl_tcam.c | 2 ++ .../net/ethernet/mellanox/mlxsw/spectrum_flower.c | 24 +++++++++++++++++++++- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h index e4fcba7c2af2..c75e9141e3ec 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h @@ -54,6 +54,8 @@ enum mlxsw_afk_element { MLXSW_AFK_ELEMENT_DST_IP6_LO, MLXSW_AFK_ELEMENT_DST_L4_PORT, MLXSW_AFK_ELEMENT_SRC_L4_PORT, + MLXSW_AFK_ELEMENT_VID, + MLXSW_AFK_ELEMENT_PCP, MLXSW_AFK_ELEMENT_MAX, }; @@ -88,7 +90,7 @@ struct mlxsw_afk_element_info { MLXSW_AFK_ELEMENT_INFO(MLXSW_AFK_ELEMENT_TYPE_BUF, \ _element, _offset, 0, _size) -/* For the purpose of the driver, define a internal storage scratchpad +/* For the purpose of the driver, define an internal storage scratchpad * that will be used to store key/mask values. For each defined element type * define an internal storage geometry. */ @@ -98,6 +100,8 @@ static const struct mlxsw_afk_element_info mlxsw_afk_element_infos[] = { MLXSW_AFK_ELEMENT_INFO_BUF(SMAC, 0x0A, 6), MLXSW_AFK_ELEMENT_INFO_U32(ETHERTYPE, 0x00, 0, 16), MLXSW_AFK_ELEMENT_INFO_U32(IP_PROTO, 0x10, 0, 8), + MLXSW_AFK_ELEMENT_INFO_U32(VID, 0x10, 8, 12), + MLXSW_AFK_ELEMENT_INFO_U32(PCP, 0x10, 20, 3), MLXSW_AFK_ELEMENT_INFO_U32(SRC_IP4, 0x18, 0, 32), MLXSW_AFK_ELEMENT_INFO_U32(DST_IP4, 0x1C, 0, 32), MLXSW_AFK_ELEMENT_INFO_BUF(SRC_IP6_HI, 0x18, 8), diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h index 82b81cf7f4a7..af7b7bad48df 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h @@ -39,11 +39,15 @@ static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_l2_dmac[] = { MLXSW_AFK_ELEMENT_INST_BUF(DMAC, 0x00, 6), + MLXSW_AFK_ELEMENT_INST_U32(PCP, 0x08, 13, 3), + MLXSW_AFK_ELEMENT_INST_U32(VID, 0x08, 0, 12), MLXSW_AFK_ELEMENT_INST_U32(SRC_SYS_PORT, 0x0C, 0, 16), }; static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_l2_smac[] = { MLXSW_AFK_ELEMENT_INST_BUF(SMAC, 0x00, 6), + MLXSW_AFK_ELEMENT_INST_U32(PCP, 0x08, 13, 3), + MLXSW_AFK_ELEMENT_INST_U32(VID, 0x08, 0, 12), MLXSW_AFK_ELEMENT_INST_U32(SRC_SYS_PORT, 0x0C, 0, 16), }; @@ -65,6 +69,8 @@ static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_ipv4_dip[] = { }; static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_ipv4_ex[] = { + MLXSW_AFK_ELEMENT_INST_U32(VID, 0x00, 0, 12), + MLXSW_AFK_ELEMENT_INST_U32(PCP, 0x08, 29, 3), MLXSW_AFK_ELEMENT_INST_U32(SRC_L4_PORT, 0x08, 0, 16), MLXSW_AFK_ELEMENT_INST_U32(DST_L4_PORT, 0x0C, 0, 16), }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c index 7382832215fa..6858439a1319 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c @@ -950,6 +950,8 @@ static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv4[] = { MLXSW_AFK_ELEMENT_DST_IP4, MLXSW_AFK_ELEMENT_DST_L4_PORT, MLXSW_AFK_ELEMENT_SRC_L4_PORT, + MLXSW_AFK_ELEMENT_VID, + MLXSW_AFK_ELEMENT_PCP, }; static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv6[] = { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index d8984074f358..f2ed0b3d5718 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -183,7 +183,8 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) | BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | - BIT(FLOW_DISSECTOR_KEY_PORTS))) { + BIT(FLOW_DISSECTOR_KEY_PORTS) | + BIT(FLOW_DISSECTOR_KEY_VLAN))) { dev_err(mlxsw_sp->bus_info->dev, "Unsupported key\n"); return -EOPNOTSUPP; } @@ -244,6 +245,27 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, sizeof(key->src)); } + if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) { + struct flow_dissector_key_vlan *key = + skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_VLAN, + f->key); + struct flow_dissector_key_vlan *mask = + skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_VLAN, + f->mask); + if (mask->vlan_id != 0) + mlxsw_sp_acl_rulei_keymask_u32(rulei, + MLXSW_AFK_ELEMENT_VID, + key->vlan_id, + mask->vlan_id); + if (mask->vlan_priority != 0) + mlxsw_sp_acl_rulei_keymask_u32(rulei, + MLXSW_AFK_ELEMENT_PCP, + key->vlan_priority, + mask->vlan_priority); + } + if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) mlxsw_sp_flower_parse_ipv4(rulei, f); -- cgit From b414970e210d39d81cc70b46f7d9aa8e07b9297f Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 10 Mar 2017 08:53:34 +0100 Subject: mlxsw: spectrum: Sanitize bridge's upper devices We're going to allow bridges stacked on top of port netdevs to be enslaved to a VRF, but for now, only VLAN uppers of the VLAN-aware bridge are supported. Sanitize any other bridge upper. This is consistent with the way we sanitize port netdevs' uppers. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 31 ++++++++++++++++---------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index ae18067198dd..c27c8f83f7ac 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4565,33 +4565,40 @@ static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev, struct netdev_notifier_changeupper_info *info; struct net_device *upper_dev; struct mlxsw_sp *mlxsw_sp; - int err; + int err = 0; mlxsw_sp = mlxsw_sp_lower_get(br_dev); if (!mlxsw_sp) return 0; - if (br_dev != mlxsw_sp->master_bridge.dev) - return 0; info = ptr; switch (event) { - case NETDEV_CHANGEUPPER: + case NETDEV_PRECHANGEUPPER: upper_dev = info->upper_dev; if (!is_vlan_dev(upper_dev)) - break; - if (info->linking) { - err = mlxsw_sp_master_bridge_vlan_link(mlxsw_sp, - upper_dev); - if (err) - return err; + return -EINVAL; + if (is_vlan_dev(upper_dev) && + br_dev != mlxsw_sp->master_bridge.dev) + return -EINVAL; + break; + case NETDEV_CHANGEUPPER: + upper_dev = info->upper_dev; + if (is_vlan_dev(upper_dev)) { + if (info->linking) + err = mlxsw_sp_master_bridge_vlan_link(mlxsw_sp, + upper_dev); + else + mlxsw_sp_master_bridge_vlan_unlink(mlxsw_sp, + upper_dev); } else { - mlxsw_sp_master_bridge_vlan_unlink(mlxsw_sp, upper_dev); + err = -EINVAL; + WARN_ON(1); } break; } - return 0; + return err; } static u16 mlxsw_sp_avail_vfid_get(const struct mlxsw_sp *mlxsw_sp) -- cgit From 1f88061ee409f5ec3d3451fc3235965aa3789af1 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 10 Mar 2017 08:53:35 +0100 Subject: mlxsw: spectrum: Don't assume upper device's type When an upper device is configured on top of a vPort we make sure it's a bridge master during PRECHANGEUPPER and fail otherwise. Therefore, when CHANGEUPPER is later received we don't bother checking the upper's type. Make the code more extendable in preparation for VRF uppers, by checking the upper's type. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index c27c8f83f7ac..b8238ed9ae73 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4818,6 +4818,8 @@ static int mlxsw_sp_netdevice_vport_event(struct net_device *dev, int err = 0; mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid); + if (!mlxsw_sp_vport) + return 0; switch (event) { case NETDEV_PRECHANGEUPPER: @@ -4835,16 +4837,17 @@ static int mlxsw_sp_netdevice_vport_event(struct net_device *dev, break; case NETDEV_CHANGEUPPER: upper_dev = info->upper_dev; - if (info->linking) { - if (WARN_ON(!mlxsw_sp_vport)) - return -EINVAL; - err = mlxsw_sp_vport_bridge_join(mlxsw_sp_vport, - upper_dev); + if (netif_is_bridge_master(upper_dev)) { + if (info->linking) + err = mlxsw_sp_vport_bridge_join(mlxsw_sp_vport, + upper_dev); + else + mlxsw_sp_vport_bridge_leave(mlxsw_sp_vport); } else { - if (!mlxsw_sp_vport) - return 0; - mlxsw_sp_vport_bridge_leave(mlxsw_sp_vport); + err = -EINVAL; + WARN_ON(1); } + break; } return err; -- cgit From 186962ebb7ecd74d305cf167e99bf1885b89ea8c Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 10 Mar 2017 08:53:36 +0100 Subject: mlxsw: spectrum: Associate PVID vPort with appropriate netdev When a VLAN device is configured on top of a LAG device (f.e., bond0.10), a vPort is created on top of each of the LAG's slaves and its 'dev' pointer is set to the VLAN device. This is in contrast to the implicit PVID vPort (representing 'bond0'), whose 'dev' pointer keeps pointing to the port netdev itself (f.e., 'sw1p1'). Make both cases consistent by setting their 'dev' pointer to the actual netdev they represent. Either the LAG device itself (in the case of the PVID vPort) or the VLAN device on top of it. This will later allow us to more easily understand for which netdev we should create the router interface (RIF) upon enslavement to a VRF master. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index b8238ed9ae73..fd6c40ead96a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4221,7 +4221,7 @@ static int mlxsw_sp_port_lag_index_get(struct mlxsw_sp *mlxsw_sp, static void mlxsw_sp_port_pvid_vport_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, - u16 lag_id) + struct net_device *lag_dev, u16 lag_id) { struct mlxsw_sp_port *mlxsw_sp_vport; struct mlxsw_sp_fid *f; @@ -4239,6 +4239,7 @@ mlxsw_sp_port_pvid_vport_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_vport->lag_id = lag_id; mlxsw_sp_vport->lagged = 1; + mlxsw_sp_vport->dev = lag_dev; } static void @@ -4255,6 +4256,7 @@ mlxsw_sp_port_pvid_vport_lag_leave(struct mlxsw_sp_port *mlxsw_sp_port) if (f) f->leave(mlxsw_sp_vport); + mlxsw_sp_vport->dev = mlxsw_sp_port->dev; mlxsw_sp_vport->lagged = 0; } @@ -4294,7 +4296,7 @@ static int mlxsw_sp_port_lag_join(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_port->lagged = 1; lag->ref_count++; - mlxsw_sp_port_pvid_vport_lag_join(mlxsw_sp_port, lag_id); + mlxsw_sp_port_pvid_vport_lag_join(mlxsw_sp_port, lag_dev, lag_id); return 0; -- cgit From f4a761d2030289229e5e7837a8afd35e48af464c Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 10 Mar 2017 08:53:37 +0100 Subject: mlxsw: spectrum: Destroy RIFs based on last removed address We only use the RIF reference count to determine when the last IP address was removed, but instead we can just test 'in_dev->ifa_list'. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 7 +++---- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index fd6c40ead96a..cb0e77ad4c0a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3392,16 +3392,16 @@ void mlxsw_sp_port_dev_put(struct mlxsw_sp_port *mlxsw_sp_port) } static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *r, + const struct in_device *in_dev, unsigned long event) { switch (event) { case NETDEV_UP: if (!r) return true; - r->ref_count++; return false; case NETDEV_DOWN: - if (r && --r->ref_count == 0) + if (r && !in_dev->ifa_list) return true; /* It is possible we already removed the RIF ourselves * if it was assigned to a netdev that is now a bridge @@ -3484,7 +3484,6 @@ mlxsw_sp_rif_alloc(u16 rif, struct net_device *l3_dev, struct mlxsw_sp_fid *f) INIT_LIST_HEAD(&r->neigh_list); ether_addr_copy(r->addr, l3_dev->dev_addr); r->mtu = l3_dev->mtu; - r->ref_count = 1; r->dev = l3_dev; r->rif = rif; r->f = f; @@ -3858,7 +3857,7 @@ static int mlxsw_sp_inetaddr_event(struct notifier_block *unused, goto out; r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); - if (!mlxsw_sp_rif_should_config(r, event)) + if (!mlxsw_sp_rif_should_config(r, ifa->ifa_dev, event)) goto out; if (mlxsw_sp_port_dev_check(dev)) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index ac445d810eb3..7805430e5d4f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -111,7 +111,6 @@ struct mlxsw_sp_rif { struct list_head nexthop_list; struct list_head neigh_list; struct net_device *dev; - unsigned int ref_count; struct mlxsw_sp_fid *f; unsigned char addr[ETH_ALEN]; int mtu; -- cgit From 97989ee0f5e855f4f5ec473b292c17f39cbaac58 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 10 Mar 2017 08:53:38 +0100 Subject: mlxsw: spectrum_router: Allow more route types to be programmed Allow 'unreachable', 'blackhole' and 'prohibit' route types to be programmed into the device by sending any packet hitting them to the CPU. This is needed so that users will be able to program a default route into the VRF's table, thereby preventing lookup from leaking to other tables. Audit the code paths to make sure we don't rely on the presence of a nexthop netdev, as it doesn't exist for above mentioned route types. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 31 ++++++++++++++++------ 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index bd8de6b9be71..2fc4a9751469 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1514,6 +1514,9 @@ static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp, if (err) return err; + if (!dev) + return 0; + in_dev = __in_dev_get_rtnl(dev); if (in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && fib_nh->nh_flags & RTNH_F_LINKDOWN) @@ -1877,17 +1880,29 @@ mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp, { struct fib_info *fi = fen_info->fi; - if (fen_info->type == RTN_LOCAL || fen_info->type == RTN_BROADCAST) { + switch (fen_info->type) { + case RTN_BROADCAST: /* fall through */ + case RTN_LOCAL: fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; return 0; - } - if (fen_info->type != RTN_UNICAST) - return -EINVAL; - if (fi->fib_nh->nh_scope != RT_SCOPE_LINK) + case RTN_UNREACHABLE: /* fall through */ + case RTN_BLACKHOLE: /* fall through */ + case RTN_PROHIBIT: + /* Packets hitting these routes need to be trapped, but + * can do so with a lower priority than packets directed + * at the host, so use action type local instead of trap. + */ fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; - else - fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE; - return 0; + return 0; + case RTN_UNICAST: + if (fi->fib_nh->nh_scope != RT_SCOPE_LINK) + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; + else + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE; + return 0; + default: + return -EINVAL; + } } static struct mlxsw_sp_fib_entry * -- cgit From 4724ba561a1ae0fcad2f2f2d3a5b1d4fd0840a9d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 10 Mar 2017 08:53:39 +0100 Subject: mlxsw: spectrum_router: Place RIF related code with router code The inetaddr notification block is currently implemented in the main driver file, but this isn't really appropriate, as it mainly creates and destroys router interfaces (RIFs) which belong with the rest of the router code. This will become even more apparent later on when we'll need to bind these RIFs to virtual routers according to the VRF's table. Structure the driver better and prevent unnecessary function exports by moving the RIF related code with the rest of the router code. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 541 +--------------- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 45 +- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 700 +++++++++++++++++++-- 3 files changed, 644 insertions(+), 642 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index cb0e77ad4c0a..2104ee47e965 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3352,7 +3352,7 @@ static struct mlxsw_sp_port *mlxsw_sp_port_dev_lower_find(struct net_device *dev return mlxsw_sp_port; } -static struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev) +struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev) { struct mlxsw_sp_port *mlxsw_sp_port; @@ -3391,545 +3391,6 @@ void mlxsw_sp_port_dev_put(struct mlxsw_sp_port *mlxsw_sp_port) dev_put(mlxsw_sp_port->dev); } -static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *r, - const struct in_device *in_dev, - unsigned long event) -{ - switch (event) { - case NETDEV_UP: - if (!r) - return true; - return false; - case NETDEV_DOWN: - if (r && !in_dev->ifa_list) - return true; - /* It is possible we already removed the RIF ourselves - * if it was assigned to a netdev that is now a bridge - * or LAG slave. - */ - return false; - } - - return false; -} - -static int mlxsw_sp_avail_rif_get(struct mlxsw_sp *mlxsw_sp) -{ - int i; - - for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) - if (!mlxsw_sp->rifs[i]) - return i; - - return MLXSW_SP_INVALID_RIF; -} - -static void mlxsw_sp_vport_rif_sp_attr_get(struct mlxsw_sp_port *mlxsw_sp_vport, - bool *p_lagged, u16 *p_system_port) -{ - u8 local_port = mlxsw_sp_vport->local_port; - - *p_lagged = mlxsw_sp_vport->lagged; - *p_system_port = *p_lagged ? mlxsw_sp_vport->lag_id : local_port; -} - -static int mlxsw_sp_vport_rif_sp_op(struct mlxsw_sp_port *mlxsw_sp_vport, - struct net_device *l3_dev, u16 rif, - bool create) -{ - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; - bool lagged = mlxsw_sp_vport->lagged; - char ritr_pl[MLXSW_REG_RITR_LEN]; - u16 system_port; - - mlxsw_reg_ritr_pack(ritr_pl, create, MLXSW_REG_RITR_SP_IF, rif, - l3_dev->mtu, l3_dev->dev_addr); - - mlxsw_sp_vport_rif_sp_attr_get(mlxsw_sp_vport, &lagged, &system_port); - mlxsw_reg_ritr_sp_if_pack(ritr_pl, lagged, system_port, - mlxsw_sp_vport_vid_get(mlxsw_sp_vport)); - - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); -} - -static void mlxsw_sp_vport_rif_sp_leave(struct mlxsw_sp_port *mlxsw_sp_vport); - -static struct mlxsw_sp_fid * -mlxsw_sp_rfid_alloc(u16 fid, struct net_device *l3_dev) -{ - struct mlxsw_sp_fid *f; - - f = kzalloc(sizeof(*f), GFP_KERNEL); - if (!f) - return NULL; - - f->leave = mlxsw_sp_vport_rif_sp_leave; - f->ref_count = 0; - f->dev = l3_dev; - f->fid = fid; - - return f; -} - -static struct mlxsw_sp_rif * -mlxsw_sp_rif_alloc(u16 rif, struct net_device *l3_dev, struct mlxsw_sp_fid *f) -{ - struct mlxsw_sp_rif *r; - - r = kzalloc(sizeof(*r), GFP_KERNEL); - if (!r) - return NULL; - - INIT_LIST_HEAD(&r->nexthop_list); - INIT_LIST_HEAD(&r->neigh_list); - ether_addr_copy(r->addr, l3_dev->dev_addr); - r->mtu = l3_dev->mtu; - r->dev = l3_dev; - r->rif = rif; - r->f = f; - - return r; -} - -static struct mlxsw_sp_rif * -mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport, - struct net_device *l3_dev) -{ - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; - struct mlxsw_sp_fid *f; - struct mlxsw_sp_rif *r; - u16 fid, rif; - int err; - - rif = mlxsw_sp_avail_rif_get(mlxsw_sp); - if (rif == MLXSW_SP_INVALID_RIF) - return ERR_PTR(-ERANGE); - - err = mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, true); - if (err) - return ERR_PTR(err); - - fid = mlxsw_sp_rif_sp_to_fid(rif); - err = mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, true); - if (err) - goto err_rif_fdb_op; - - f = mlxsw_sp_rfid_alloc(fid, l3_dev); - if (!f) { - err = -ENOMEM; - goto err_rfid_alloc; - } - - r = mlxsw_sp_rif_alloc(rif, l3_dev, f); - if (!r) { - err = -ENOMEM; - goto err_rif_alloc; - } - - f->r = r; - mlxsw_sp->rifs[rif] = r; - - return r; - -err_rif_alloc: - kfree(f); -err_rfid_alloc: - mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false); -err_rif_fdb_op: - mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, false); - return ERR_PTR(err); -} - -static void mlxsw_sp_vport_rif_sp_destroy(struct mlxsw_sp_port *mlxsw_sp_vport, - struct mlxsw_sp_rif *r) -{ - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; - struct net_device *l3_dev = r->dev; - struct mlxsw_sp_fid *f = r->f; - u16 fid = f->fid; - u16 rif = r->rif; - - mlxsw_sp_router_rif_gone_sync(mlxsw_sp, r); - - mlxsw_sp->rifs[rif] = NULL; - f->r = NULL; - - kfree(r); - - kfree(f); - - mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false); - - mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, false); -} - -static int mlxsw_sp_vport_rif_sp_join(struct mlxsw_sp_port *mlxsw_sp_vport, - struct net_device *l3_dev) -{ - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; - struct mlxsw_sp_rif *r; - - r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev); - if (!r) { - r = mlxsw_sp_vport_rif_sp_create(mlxsw_sp_vport, l3_dev); - if (IS_ERR(r)) - return PTR_ERR(r); - } - - mlxsw_sp_vport_fid_set(mlxsw_sp_vport, r->f); - r->f->ref_count++; - - netdev_dbg(mlxsw_sp_vport->dev, "Joined FID=%d\n", r->f->fid); - - return 0; -} - -static void mlxsw_sp_vport_rif_sp_leave(struct mlxsw_sp_port *mlxsw_sp_vport) -{ - struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport); - - netdev_dbg(mlxsw_sp_vport->dev, "Left FID=%d\n", f->fid); - - mlxsw_sp_vport_fid_set(mlxsw_sp_vport, NULL); - if (--f->ref_count == 0) - mlxsw_sp_vport_rif_sp_destroy(mlxsw_sp_vport, f->r); -} - -static int mlxsw_sp_inetaddr_vport_event(struct net_device *l3_dev, - struct net_device *port_dev, - unsigned long event, u16 vid) -{ - struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev); - struct mlxsw_sp_port *mlxsw_sp_vport; - - mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid); - if (WARN_ON(!mlxsw_sp_vport)) - return -EINVAL; - - switch (event) { - case NETDEV_UP: - return mlxsw_sp_vport_rif_sp_join(mlxsw_sp_vport, l3_dev); - case NETDEV_DOWN: - mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport); - break; - } - - return 0; -} - -static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev, - unsigned long event) -{ - if (netif_is_bridge_port(port_dev) || netif_is_lag_port(port_dev)) - return 0; - - return mlxsw_sp_inetaddr_vport_event(port_dev, port_dev, event, 1); -} - -static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev, - struct net_device *lag_dev, - unsigned long event, u16 vid) -{ - struct net_device *port_dev; - struct list_head *iter; - int err; - - netdev_for_each_lower_dev(lag_dev, port_dev, iter) { - if (mlxsw_sp_port_dev_check(port_dev)) { - err = mlxsw_sp_inetaddr_vport_event(l3_dev, port_dev, - event, vid); - if (err) - return err; - } - } - - return 0; -} - -static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev, - unsigned long event) -{ - if (netif_is_bridge_port(lag_dev)) - return 0; - - return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1); -} - -static struct mlxsw_sp_fid *mlxsw_sp_bridge_fid_get(struct mlxsw_sp *mlxsw_sp, - struct net_device *l3_dev) -{ - u16 fid; - - if (is_vlan_dev(l3_dev)) - fid = vlan_dev_vlan_id(l3_dev); - else if (mlxsw_sp->master_bridge.dev == l3_dev) - fid = 1; - else - return mlxsw_sp_vfid_find(mlxsw_sp, l3_dev); - - return mlxsw_sp_fid_find(mlxsw_sp, fid); -} - -static enum mlxsw_flood_table_type mlxsw_sp_flood_table_type_get(u16 fid) -{ - return mlxsw_sp_fid_is_vfid(fid) ? MLXSW_REG_SFGC_TABLE_TYPE_FID : - MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST; -} - -static u16 mlxsw_sp_flood_table_index_get(u16 fid) -{ - return mlxsw_sp_fid_is_vfid(fid) ? mlxsw_sp_fid_to_vfid(fid) : fid; -} - -static int mlxsw_sp_router_port_flood_set(struct mlxsw_sp *mlxsw_sp, u16 fid, - bool set) -{ - enum mlxsw_flood_table_type table_type; - char *sftr_pl; - u16 index; - int err; - - sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); - if (!sftr_pl) - return -ENOMEM; - - table_type = mlxsw_sp_flood_table_type_get(fid); - index = mlxsw_sp_flood_table_index_get(fid); - mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BC, index, table_type, - 1, MLXSW_PORT_ROUTER_PORT, set); - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); - - kfree(sftr_pl); - return err; -} - -static enum mlxsw_reg_ritr_if_type mlxsw_sp_rif_type_get(u16 fid) -{ - if (mlxsw_sp_fid_is_vfid(fid)) - return MLXSW_REG_RITR_FID_IF; - else - return MLXSW_REG_RITR_VLAN_IF; -} - -static int mlxsw_sp_rif_bridge_op(struct mlxsw_sp *mlxsw_sp, - struct net_device *l3_dev, - u16 fid, u16 rif, - bool create) -{ - enum mlxsw_reg_ritr_if_type rif_type; - char ritr_pl[MLXSW_REG_RITR_LEN]; - - rif_type = mlxsw_sp_rif_type_get(fid); - mlxsw_reg_ritr_pack(ritr_pl, create, rif_type, rif, l3_dev->mtu, - l3_dev->dev_addr); - mlxsw_reg_ritr_fid_set(ritr_pl, rif_type, fid); - - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); -} - -static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp, - struct net_device *l3_dev, - struct mlxsw_sp_fid *f) -{ - struct mlxsw_sp_rif *r; - u16 rif; - int err; - - rif = mlxsw_sp_avail_rif_get(mlxsw_sp); - if (rif == MLXSW_SP_INVALID_RIF) - return -ERANGE; - - err = mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, true); - if (err) - return err; - - err = mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, true); - if (err) - goto err_rif_bridge_op; - - err = mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, true); - if (err) - goto err_rif_fdb_op; - - r = mlxsw_sp_rif_alloc(rif, l3_dev, f); - if (!r) { - err = -ENOMEM; - goto err_rif_alloc; - } - - f->r = r; - mlxsw_sp->rifs[rif] = r; - - netdev_dbg(l3_dev, "RIF=%d created\n", rif); - - return 0; - -err_rif_alloc: - mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false); -err_rif_fdb_op: - mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, false); -err_rif_bridge_op: - mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, false); - return err; -} - -void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *r) -{ - struct net_device *l3_dev = r->dev; - struct mlxsw_sp_fid *f = r->f; - u16 rif = r->rif; - - mlxsw_sp_router_rif_gone_sync(mlxsw_sp, r); - - mlxsw_sp->rifs[rif] = NULL; - f->r = NULL; - - kfree(r); - - mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false); - - mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, false); - - mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, false); - - netdev_dbg(l3_dev, "RIF=%d destroyed\n", rif); -} - -static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev, - struct net_device *br_dev, - unsigned long event) -{ - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev); - struct mlxsw_sp_fid *f; - - /* FID can either be an actual FID if the L3 device is the - * VLAN-aware bridge or a VLAN device on top. Otherwise, the - * L3 device is a VLAN-unaware bridge and we get a vFID. - */ - f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev); - if (WARN_ON(!f)) - return -EINVAL; - - switch (event) { - case NETDEV_UP: - return mlxsw_sp_rif_bridge_create(mlxsw_sp, l3_dev, f); - case NETDEV_DOWN: - mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); - break; - } - - return 0; -} - -static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev, - unsigned long event) -{ - struct net_device *real_dev = vlan_dev_real_dev(vlan_dev); - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(vlan_dev); - u16 vid = vlan_dev_vlan_id(vlan_dev); - - if (mlxsw_sp_port_dev_check(real_dev)) - return mlxsw_sp_inetaddr_vport_event(vlan_dev, real_dev, event, - vid); - else if (netif_is_lag_master(real_dev)) - return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event, - vid); - else if (netif_is_bridge_master(real_dev) && - mlxsw_sp->master_bridge.dev == real_dev) - return mlxsw_sp_inetaddr_bridge_event(vlan_dev, real_dev, - event); - - return 0; -} - -static int mlxsw_sp_inetaddr_event(struct notifier_block *unused, - unsigned long event, void *ptr) -{ - struct in_ifaddr *ifa = (struct in_ifaddr *) ptr; - struct net_device *dev = ifa->ifa_dev->dev; - struct mlxsw_sp *mlxsw_sp; - struct mlxsw_sp_rif *r; - int err = 0; - - mlxsw_sp = mlxsw_sp_lower_get(dev); - if (!mlxsw_sp) - goto out; - - r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); - if (!mlxsw_sp_rif_should_config(r, ifa->ifa_dev, event)) - goto out; - - if (mlxsw_sp_port_dev_check(dev)) - err = mlxsw_sp_inetaddr_port_event(dev, event); - else if (netif_is_lag_master(dev)) - err = mlxsw_sp_inetaddr_lag_event(dev, event); - else if (netif_is_bridge_master(dev)) - err = mlxsw_sp_inetaddr_bridge_event(dev, dev, event); - else if (is_vlan_dev(dev)) - err = mlxsw_sp_inetaddr_vlan_event(dev, event); - -out: - return notifier_from_errno(err); -} - -static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif, - const char *mac, int mtu) -{ - char ritr_pl[MLXSW_REG_RITR_LEN]; - int err; - - mlxsw_reg_ritr_rif_pack(ritr_pl, rif); - err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); - if (err) - return err; - - mlxsw_reg_ritr_mtu_set(ritr_pl, mtu); - mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac); - mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE); - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); -} - -static int mlxsw_sp_netdevice_router_port_event(struct net_device *dev) -{ - struct mlxsw_sp *mlxsw_sp; - struct mlxsw_sp_rif *r; - int err; - - mlxsw_sp = mlxsw_sp_lower_get(dev); - if (!mlxsw_sp) - return 0; - - r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); - if (!r) - return 0; - - err = mlxsw_sp_rif_fdb_op(mlxsw_sp, r->addr, r->f->fid, false); - if (err) - return err; - - err = mlxsw_sp_rif_edit(mlxsw_sp, r->rif, dev->dev_addr, dev->mtu); - if (err) - goto err_rif_edit; - - err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, r->f->fid, true); - if (err) - goto err_rif_fdb_op; - - ether_addr_copy(r->addr, dev->dev_addr); - r->mtu = dev->mtu; - - netdev_dbg(dev, "Updated RIF=%d\n", r->rif); - - return 0; - -err_rif_fdb_op: - mlxsw_sp_rif_edit(mlxsw_sp, r->rif, r->addr, r->mtu); -err_rif_edit: - mlxsw_sp_rif_fdb_op(mlxsw_sp, r->addr, r->f->fid, true); - return err; -} - static bool mlxsw_sp_lag_port_fid_member(struct mlxsw_sp_port *lag_port, u16 fid) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 7805430e5d4f..5bc60421a860 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -58,7 +58,6 @@ #define MLXSW_SP_VFID_MAX 1024 /* Bridged VLAN interfaces */ #define MLXSW_SP_RFID_BASE 15360 -#define MLXSW_SP_INVALID_RIF 0xffff #define MLXSW_SP_MID_MAX 7000 @@ -92,6 +91,7 @@ static inline u16 mlxsw_sp_pfc_delay_get(int mtu, u16 delay) } struct mlxsw_sp_port; +struct mlxsw_sp_rif; struct mlxsw_sp_upper { struct net_device *dev; @@ -107,16 +107,6 @@ struct mlxsw_sp_fid { u16 fid; }; -struct mlxsw_sp_rif { - struct list_head nexthop_list; - struct list_head neigh_list; - struct net_device *dev; - struct mlxsw_sp_fid *f; - unsigned char addr[ETH_ALEN]; - int mtu; - u16 rif; -}; - struct mlxsw_sp_mid { struct list_head list; unsigned char addr[ETH_ALEN]; @@ -140,16 +130,6 @@ static inline bool mlxsw_sp_fid_is_vfid(u16 fid) return fid >= MLXSW_SP_VFID_BASE && fid < MLXSW_SP_RFID_BASE; } -static inline bool mlxsw_sp_fid_is_rfid(u16 fid) -{ - return fid >= MLXSW_SP_RFID_BASE; -} - -static inline u16 mlxsw_sp_rif_sp_to_fid(u16 rif) -{ - return MLXSW_SP_RFID_BASE + rif; -} - struct mlxsw_sp_sb_pr { enum mlxsw_reg_sbpr_mode mode; u32 size; @@ -385,6 +365,7 @@ struct mlxsw_sp_port { }; bool mlxsw_sp_port_dev_check(const struct net_device *dev); +struct mlxsw_sp *mlxsw_sp_lower_get(struct net_device *dev); struct mlxsw_sp_port *mlxsw_sp_port_lower_dev_hold(struct net_device *dev); void mlxsw_sp_port_dev_put(struct mlxsw_sp_port *mlxsw_sp_port); @@ -496,19 +477,6 @@ mlxsw_sp_vfid_find(const struct mlxsw_sp *mlxsw_sp, return NULL; } -static inline struct mlxsw_sp_rif * -mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp, - const struct net_device *dev) -{ - int i; - - for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) - if (mlxsw_sp->rifs[i] && mlxsw_sp->rifs[i]->dev == dev) - return mlxsw_sp->rifs[i]; - - return NULL; -} - enum mlxsw_sp_flood_table { MLXSW_SP_FLOOD_TABLE_UC, MLXSW_SP_FLOOD_TABLE_BC, @@ -569,8 +537,6 @@ int mlxsw_sp_rif_fdb_op(struct mlxsw_sp *mlxsw_sp, const char *mac, u16 fid, bool adding); struct mlxsw_sp_fid *mlxsw_sp_fid_create(struct mlxsw_sp *mlxsw_sp, u16 fid); void mlxsw_sp_fid_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *f); -void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *r); int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port, enum mlxsw_reg_qeec_hr hr, u8 index, u8 next_index, bool dwrr, u8 dwrr_weight); @@ -607,8 +573,11 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp); void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp); int mlxsw_sp_router_netevent_event(struct notifier_block *unused, unsigned long event, void *ptr); -void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *r); +int mlxsw_sp_netdevice_router_port_event(struct net_device *dev); +int mlxsw_sp_inetaddr_event(struct notifier_block *unused, + unsigned long event, void *ptr); +void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_rif *r); int mlxsw_sp_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, unsigned int entry_count); void mlxsw_sp_kvdl_free(struct mlxsw_sp *mlxsw_sp, int entry_index); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 2fc4a9751469..1839ba05f4dd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -50,6 +50,20 @@ #include "core.h" #include "reg.h" +struct mlxsw_sp_rif { + struct list_head nexthop_list; + struct list_head neigh_list; + struct net_device *dev; + struct mlxsw_sp_fid *f; + unsigned char addr[ETH_ALEN]; + int mtu; + u16 rif; +}; + +static struct mlxsw_sp_rif * +mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp, + const struct net_device *dev); + #define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \ for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT) @@ -2452,70 +2466,6 @@ static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp) dev_warn(mlxsw_sp->bus_info->dev, "Failed to set abort trap.\n"); } -static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif) -{ - char ritr_pl[MLXSW_REG_RITR_LEN]; - int err; - - mlxsw_reg_ritr_rif_pack(ritr_pl, rif); - err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); - if (WARN_ON_ONCE(err)) - return err; - - mlxsw_reg_ritr_enable_set(ritr_pl, false); - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); -} - -void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *r) -{ - mlxsw_sp_router_rif_disable(mlxsw_sp, r->rif); - mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, r); - mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, r); -} - -static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) -{ - char rgcr_pl[MLXSW_REG_RGCR_LEN]; - u64 max_rifs; - int err; - - if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS)) - return -EIO; - - max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); - mlxsw_sp->rifs = kcalloc(max_rifs, sizeof(struct mlxsw_sp_rif *), - GFP_KERNEL); - if (!mlxsw_sp->rifs) - return -ENOMEM; - - mlxsw_reg_rgcr_pack(rgcr_pl, true); - mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs); - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl); - if (err) - goto err_rgcr_fail; - - return 0; - -err_rgcr_fail: - kfree(mlxsw_sp->rifs); - return err; -} - -static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp) -{ - char rgcr_pl[MLXSW_REG_RGCR_LEN]; - int i; - - mlxsw_reg_rgcr_pack(rgcr_pl, false); - mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl); - - for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) - WARN_ON_ONCE(mlxsw_sp->rifs[i]); - - kfree(mlxsw_sp->rifs); -} - struct mlxsw_sp_fib_event_work { struct work_struct work; union { @@ -2609,6 +2559,586 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb, return NOTIFY_DONE; } +static struct mlxsw_sp_rif * +mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp, + const struct net_device *dev) +{ + int i; + + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) + if (mlxsw_sp->rifs[i] && mlxsw_sp->rifs[i]->dev == dev) + return mlxsw_sp->rifs[i]; + + return NULL; +} + +static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif) +{ + char ritr_pl[MLXSW_REG_RITR_LEN]; + int err; + + mlxsw_reg_ritr_rif_pack(ritr_pl, rif); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); + if (WARN_ON_ONCE(err)) + return err; + + mlxsw_reg_ritr_enable_set(ritr_pl, false); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); +} + +static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_rif *r) +{ + mlxsw_sp_router_rif_disable(mlxsw_sp, r->rif); + mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, r); + mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, r); +} + +static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *r, + const struct in_device *in_dev, + unsigned long event) +{ + switch (event) { + case NETDEV_UP: + if (!r) + return true; + return false; + case NETDEV_DOWN: + if (r && !in_dev->ifa_list) + return true; + /* It is possible we already removed the RIF ourselves + * if it was assigned to a netdev that is now a bridge + * or LAG slave. + */ + return false; + } + + return false; +} + +#define MLXSW_SP_INVALID_RIF 0xffff +static int mlxsw_sp_avail_rif_get(struct mlxsw_sp *mlxsw_sp) +{ + int i; + + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) + if (!mlxsw_sp->rifs[i]) + return i; + + return MLXSW_SP_INVALID_RIF; +} + +static void mlxsw_sp_vport_rif_sp_attr_get(struct mlxsw_sp_port *mlxsw_sp_vport, + bool *p_lagged, u16 *p_system_port) +{ + u8 local_port = mlxsw_sp_vport->local_port; + + *p_lagged = mlxsw_sp_vport->lagged; + *p_system_port = *p_lagged ? mlxsw_sp_vport->lag_id : local_port; +} + +static int mlxsw_sp_vport_rif_sp_op(struct mlxsw_sp_port *mlxsw_sp_vport, + struct net_device *l3_dev, u16 rif, + bool create) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; + bool lagged = mlxsw_sp_vport->lagged; + char ritr_pl[MLXSW_REG_RITR_LEN]; + u16 system_port; + + mlxsw_reg_ritr_pack(ritr_pl, create, MLXSW_REG_RITR_SP_IF, rif, + l3_dev->mtu, l3_dev->dev_addr); + + mlxsw_sp_vport_rif_sp_attr_get(mlxsw_sp_vport, &lagged, &system_port); + mlxsw_reg_ritr_sp_if_pack(ritr_pl, lagged, system_port, + mlxsw_sp_vport_vid_get(mlxsw_sp_vport)); + + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); +} + +static void mlxsw_sp_vport_rif_sp_leave(struct mlxsw_sp_port *mlxsw_sp_vport); + +static u16 mlxsw_sp_rif_sp_to_fid(u16 rif) +{ + return MLXSW_SP_RFID_BASE + rif; +} + +static struct mlxsw_sp_fid * +mlxsw_sp_rfid_alloc(u16 fid, struct net_device *l3_dev) +{ + struct mlxsw_sp_fid *f; + + f = kzalloc(sizeof(*f), GFP_KERNEL); + if (!f) + return NULL; + + f->leave = mlxsw_sp_vport_rif_sp_leave; + f->ref_count = 0; + f->dev = l3_dev; + f->fid = fid; + + return f; +} + +static struct mlxsw_sp_rif * +mlxsw_sp_rif_alloc(u16 rif, struct net_device *l3_dev, struct mlxsw_sp_fid *f) +{ + struct mlxsw_sp_rif *r; + + r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) + return NULL; + + INIT_LIST_HEAD(&r->nexthop_list); + INIT_LIST_HEAD(&r->neigh_list); + ether_addr_copy(r->addr, l3_dev->dev_addr); + r->mtu = l3_dev->mtu; + r->dev = l3_dev; + r->rif = rif; + r->f = f; + + return r; +} + +static struct mlxsw_sp_rif * +mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport, + struct net_device *l3_dev) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; + struct mlxsw_sp_fid *f; + struct mlxsw_sp_rif *r; + u16 fid, rif; + int err; + + rif = mlxsw_sp_avail_rif_get(mlxsw_sp); + if (rif == MLXSW_SP_INVALID_RIF) + return ERR_PTR(-ERANGE); + + err = mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, true); + if (err) + return ERR_PTR(err); + + fid = mlxsw_sp_rif_sp_to_fid(rif); + err = mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, true); + if (err) + goto err_rif_fdb_op; + + f = mlxsw_sp_rfid_alloc(fid, l3_dev); + if (!f) { + err = -ENOMEM; + goto err_rfid_alloc; + } + + r = mlxsw_sp_rif_alloc(rif, l3_dev, f); + if (!r) { + err = -ENOMEM; + goto err_rif_alloc; + } + + f->r = r; + mlxsw_sp->rifs[rif] = r; + + return r; + +err_rif_alloc: + kfree(f); +err_rfid_alloc: + mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false); +err_rif_fdb_op: + mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, false); + return ERR_PTR(err); +} + +static void mlxsw_sp_vport_rif_sp_destroy(struct mlxsw_sp_port *mlxsw_sp_vport, + struct mlxsw_sp_rif *r) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; + struct net_device *l3_dev = r->dev; + struct mlxsw_sp_fid *f = r->f; + u16 fid = f->fid; + u16 rif = r->rif; + + mlxsw_sp_router_rif_gone_sync(mlxsw_sp, r); + + mlxsw_sp->rifs[rif] = NULL; + f->r = NULL; + + kfree(r); + + kfree(f); + + mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false); + + mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, false); +} + +static int mlxsw_sp_vport_rif_sp_join(struct mlxsw_sp_port *mlxsw_sp_vport, + struct net_device *l3_dev) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; + struct mlxsw_sp_rif *r; + + r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev); + if (!r) { + r = mlxsw_sp_vport_rif_sp_create(mlxsw_sp_vport, l3_dev); + if (IS_ERR(r)) + return PTR_ERR(r); + } + + mlxsw_sp_vport_fid_set(mlxsw_sp_vport, r->f); + r->f->ref_count++; + + netdev_dbg(mlxsw_sp_vport->dev, "Joined FID=%d\n", r->f->fid); + + return 0; +} + +static void mlxsw_sp_vport_rif_sp_leave(struct mlxsw_sp_port *mlxsw_sp_vport) +{ + struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport); + + netdev_dbg(mlxsw_sp_vport->dev, "Left FID=%d\n", f->fid); + + mlxsw_sp_vport_fid_set(mlxsw_sp_vport, NULL); + if (--f->ref_count == 0) + mlxsw_sp_vport_rif_sp_destroy(mlxsw_sp_vport, f->r); +} + +static int mlxsw_sp_inetaddr_vport_event(struct net_device *l3_dev, + struct net_device *port_dev, + unsigned long event, u16 vid) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(port_dev); + struct mlxsw_sp_port *mlxsw_sp_vport; + + mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid); + if (WARN_ON(!mlxsw_sp_vport)) + return -EINVAL; + + switch (event) { + case NETDEV_UP: + return mlxsw_sp_vport_rif_sp_join(mlxsw_sp_vport, l3_dev); + case NETDEV_DOWN: + mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport); + break; + } + + return 0; +} + +static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev, + unsigned long event) +{ + if (netif_is_bridge_port(port_dev) || netif_is_lag_port(port_dev)) + return 0; + + return mlxsw_sp_inetaddr_vport_event(port_dev, port_dev, event, 1); +} + +static int __mlxsw_sp_inetaddr_lag_event(struct net_device *l3_dev, + struct net_device *lag_dev, + unsigned long event, u16 vid) +{ + struct net_device *port_dev; + struct list_head *iter; + int err; + + netdev_for_each_lower_dev(lag_dev, port_dev, iter) { + if (mlxsw_sp_port_dev_check(port_dev)) { + err = mlxsw_sp_inetaddr_vport_event(l3_dev, port_dev, + event, vid); + if (err) + return err; + } + } + + return 0; +} + +static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev, + unsigned long event) +{ + if (netif_is_bridge_port(lag_dev)) + return 0; + + return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1); +} + +static struct mlxsw_sp_fid *mlxsw_sp_bridge_fid_get(struct mlxsw_sp *mlxsw_sp, + struct net_device *l3_dev) +{ + u16 fid; + + if (is_vlan_dev(l3_dev)) + fid = vlan_dev_vlan_id(l3_dev); + else if (mlxsw_sp->master_bridge.dev == l3_dev) + fid = 1; + else + return mlxsw_sp_vfid_find(mlxsw_sp, l3_dev); + + return mlxsw_sp_fid_find(mlxsw_sp, fid); +} + +static enum mlxsw_flood_table_type mlxsw_sp_flood_table_type_get(u16 fid) +{ + return mlxsw_sp_fid_is_vfid(fid) ? MLXSW_REG_SFGC_TABLE_TYPE_FID : + MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST; +} + +static u16 mlxsw_sp_flood_table_index_get(u16 fid) +{ + return mlxsw_sp_fid_is_vfid(fid) ? mlxsw_sp_fid_to_vfid(fid) : fid; +} + +static int mlxsw_sp_router_port_flood_set(struct mlxsw_sp *mlxsw_sp, u16 fid, + bool set) +{ + enum mlxsw_flood_table_type table_type; + char *sftr_pl; + u16 index; + int err; + + sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); + if (!sftr_pl) + return -ENOMEM; + + table_type = mlxsw_sp_flood_table_type_get(fid); + index = mlxsw_sp_flood_table_index_get(fid); + mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BC, index, table_type, + 1, MLXSW_PORT_ROUTER_PORT, set); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); + + kfree(sftr_pl); + return err; +} + +static enum mlxsw_reg_ritr_if_type mlxsw_sp_rif_type_get(u16 fid) +{ + if (mlxsw_sp_fid_is_vfid(fid)) + return MLXSW_REG_RITR_FID_IF; + else + return MLXSW_REG_RITR_VLAN_IF; +} + +static int mlxsw_sp_rif_bridge_op(struct mlxsw_sp *mlxsw_sp, + struct net_device *l3_dev, + u16 fid, u16 rif, + bool create) +{ + enum mlxsw_reg_ritr_if_type rif_type; + char ritr_pl[MLXSW_REG_RITR_LEN]; + + rif_type = mlxsw_sp_rif_type_get(fid); + mlxsw_reg_ritr_pack(ritr_pl, create, rif_type, rif, l3_dev->mtu, + l3_dev->dev_addr); + mlxsw_reg_ritr_fid_set(ritr_pl, rif_type, fid); + + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); +} + +static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp, + struct net_device *l3_dev, + struct mlxsw_sp_fid *f) +{ + struct mlxsw_sp_rif *r; + u16 rif; + int err; + + rif = mlxsw_sp_avail_rif_get(mlxsw_sp); + if (rif == MLXSW_SP_INVALID_RIF) + return -ERANGE; + + err = mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, true); + if (err) + return err; + + err = mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, true); + if (err) + goto err_rif_bridge_op; + + err = mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, true); + if (err) + goto err_rif_fdb_op; + + r = mlxsw_sp_rif_alloc(rif, l3_dev, f); + if (!r) { + err = -ENOMEM; + goto err_rif_alloc; + } + + f->r = r; + mlxsw_sp->rifs[rif] = r; + + netdev_dbg(l3_dev, "RIF=%d created\n", rif); + + return 0; + +err_rif_alloc: + mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false); +err_rif_fdb_op: + mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, false); +err_rif_bridge_op: + mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, false); + return err; +} + +void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_rif *r) +{ + struct net_device *l3_dev = r->dev; + struct mlxsw_sp_fid *f = r->f; + u16 rif = r->rif; + + mlxsw_sp_router_rif_gone_sync(mlxsw_sp, r); + + mlxsw_sp->rifs[rif] = NULL; + f->r = NULL; + + kfree(r); + + mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false); + + mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, false); + + mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, false); + + netdev_dbg(l3_dev, "RIF=%d destroyed\n", rif); +} + +static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev, + struct net_device *br_dev, + unsigned long event) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev); + struct mlxsw_sp_fid *f; + + /* FID can either be an actual FID if the L3 device is the + * VLAN-aware bridge or a VLAN device on top. Otherwise, the + * L3 device is a VLAN-unaware bridge and we get a vFID. + */ + f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev); + if (WARN_ON(!f)) + return -EINVAL; + + switch (event) { + case NETDEV_UP: + return mlxsw_sp_rif_bridge_create(mlxsw_sp, l3_dev, f); + case NETDEV_DOWN: + mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); + break; + } + + return 0; +} + +static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev, + unsigned long event) +{ + struct net_device *real_dev = vlan_dev_real_dev(vlan_dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(vlan_dev); + u16 vid = vlan_dev_vlan_id(vlan_dev); + + if (mlxsw_sp_port_dev_check(real_dev)) + return mlxsw_sp_inetaddr_vport_event(vlan_dev, real_dev, event, + vid); + else if (netif_is_lag_master(real_dev)) + return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event, + vid); + else if (netif_is_bridge_master(real_dev) && + mlxsw_sp->master_bridge.dev == real_dev) + return mlxsw_sp_inetaddr_bridge_event(vlan_dev, real_dev, + event); + + return 0; +} + +int mlxsw_sp_inetaddr_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct in_ifaddr *ifa = (struct in_ifaddr *) ptr; + struct net_device *dev = ifa->ifa_dev->dev; + struct mlxsw_sp *mlxsw_sp; + struct mlxsw_sp_rif *r; + int err = 0; + + mlxsw_sp = mlxsw_sp_lower_get(dev); + if (!mlxsw_sp) + goto out; + + r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); + if (!mlxsw_sp_rif_should_config(r, ifa->ifa_dev, event)) + goto out; + + if (mlxsw_sp_port_dev_check(dev)) + err = mlxsw_sp_inetaddr_port_event(dev, event); + else if (netif_is_lag_master(dev)) + err = mlxsw_sp_inetaddr_lag_event(dev, event); + else if (netif_is_bridge_master(dev)) + err = mlxsw_sp_inetaddr_bridge_event(dev, dev, event); + else if (is_vlan_dev(dev)) + err = mlxsw_sp_inetaddr_vlan_event(dev, event); + +out: + return notifier_from_errno(err); +} + +static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif, + const char *mac, int mtu) +{ + char ritr_pl[MLXSW_REG_RITR_LEN]; + int err; + + mlxsw_reg_ritr_rif_pack(ritr_pl, rif); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); + if (err) + return err; + + mlxsw_reg_ritr_mtu_set(ritr_pl, mtu); + mlxsw_reg_ritr_if_mac_memcpy_to(ritr_pl, mac); + mlxsw_reg_ritr_op_set(ritr_pl, MLXSW_REG_RITR_RIF_CREATE); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); +} + +int mlxsw_sp_netdevice_router_port_event(struct net_device *dev) +{ + struct mlxsw_sp *mlxsw_sp; + struct mlxsw_sp_rif *r; + int err; + + mlxsw_sp = mlxsw_sp_lower_get(dev); + if (!mlxsw_sp) + return 0; + + r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); + if (!r) + return 0; + + err = mlxsw_sp_rif_fdb_op(mlxsw_sp, r->addr, r->f->fid, false); + if (err) + return err; + + err = mlxsw_sp_rif_edit(mlxsw_sp, r->rif, dev->dev_addr, dev->mtu); + if (err) + goto err_rif_edit; + + err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, r->f->fid, true); + if (err) + goto err_rif_fdb_op; + + ether_addr_copy(r->addr, dev->dev_addr); + r->mtu = dev->mtu; + + netdev_dbg(dev, "Updated RIF=%d\n", r->rif); + + return 0; + +err_rif_fdb_op: + mlxsw_sp_rif_edit(mlxsw_sp, r->rif, r->addr, r->mtu); +err_rif_edit: + mlxsw_sp_rif_fdb_op(mlxsw_sp, r->addr, r->f->fid, true); + return err; +} + static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb) { struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb); @@ -2621,6 +3151,48 @@ static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb) mlxsw_sp_router_fib_flush(mlxsw_sp); } +static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) +{ + char rgcr_pl[MLXSW_REG_RGCR_LEN]; + u64 max_rifs; + int err; + + if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_RIFS)) + return -EIO; + + max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); + mlxsw_sp->rifs = kcalloc(max_rifs, sizeof(struct mlxsw_sp_rif *), + GFP_KERNEL); + if (!mlxsw_sp->rifs) + return -ENOMEM; + + mlxsw_reg_rgcr_pack(rgcr_pl, true); + mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl); + if (err) + goto err_rgcr_fail; + + return 0; + +err_rgcr_fail: + kfree(mlxsw_sp->rifs); + return err; +} + +static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp) +{ + char rgcr_pl[MLXSW_REG_RGCR_LEN]; + int i; + + mlxsw_reg_rgcr_pack(rgcr_pl, false); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl); + + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) + WARN_ON_ONCE(mlxsw_sp->rifs[i]); + + kfree(mlxsw_sp->rifs); +} + int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) { int err; -- cgit From 382dbb40145076f3a023992117ddaaae1ced5af8 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 10 Mar 2017 08:53:40 +0100 Subject: mlxsw: spectrum_router: Simplify LPM tree allocation When looking for a new LPM tree we should always consider all the unused trees. It doesn't matter if the new tree is required due to changes in currently used prefixes inside an existing routing table or because a route was inserted into an empty table. Both cases are functionally identical and therefore should be treated the same. When looking for a new LPM tree, consider all unused trees and don't reserve trees for specific cases. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 1839ba05f4dd..c11a5baca539 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -196,19 +196,15 @@ static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib) } static struct mlxsw_sp_lpm_tree * -mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp, bool one_reserved) +mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp) { static struct mlxsw_sp_lpm_tree *lpm_tree; int i; for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) { lpm_tree = &mlxsw_sp->router.lpm_trees[i]; - if (lpm_tree->ref_count == 0) { - if (one_reserved) - one_reserved = false; - else - return lpm_tree; - } + if (lpm_tree->ref_count == 0) + return lpm_tree; } return NULL; } @@ -262,12 +258,12 @@ mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp, static struct mlxsw_sp_lpm_tree * mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_prefix_usage *prefix_usage, - enum mlxsw_sp_l3proto proto, bool one_reserved) + enum mlxsw_sp_l3proto proto) { struct mlxsw_sp_lpm_tree *lpm_tree; int err; - lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp, one_reserved); + lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp); if (!lpm_tree) return ERR_PTR(-EBUSY); lpm_tree->proto = proto; @@ -297,7 +293,7 @@ static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp, static struct mlxsw_sp_lpm_tree * mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_prefix_usage *prefix_usage, - enum mlxsw_sp_l3proto proto, bool one_reserved) + enum mlxsw_sp_l3proto proto) { struct mlxsw_sp_lpm_tree *lpm_tree; int i; @@ -311,7 +307,7 @@ mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp, goto inc_ref_count; } lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, - proto, one_reserved); + proto); if (IS_ERR(lpm_tree)) return lpm_tree; @@ -421,7 +417,7 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_prefix_usage_zero(&req_prefix_usage); mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len); lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, - proto, true); + proto); if (IS_ERR(lpm_tree)) { err = PTR_ERR(lpm_tree); goto err_tree_get; @@ -463,7 +459,7 @@ mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr, return 0; new_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage, - vr->proto, false); + vr->proto); if (IS_ERR(new_tree)) { /* We failed to get a tree according to the required * prefix usage. However, the current tree might be still good -- cgit From 76610ebbde1883527cf5afa8708a5b0599439a28 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 10 Mar 2017 08:53:41 +0100 Subject: mlxsw: spectrum_router: Refactor virtual router handling A virtual router (VR) is an entity within the device to which routing tables and interfaces can be bound to. It can be used to implement VRFs. In the initial implementation we associated the VR with a specific protocol (e.g., IPv4) and an LPM tree. However, this isn't really accurate, as the same VR can be used for both IPv4 and IPv6 traffic, by binding a different LPM tree to a {VR, Proto} pair. This patch aims to restructure the VR code according to the above logic, so that VRs are more accurately represented by the driver's data structures. The main motivation behind this change is to prepare the driver for VRF offload. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 5 +- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 326 +++++++++++---------- 2 files changed, 180 insertions(+), 151 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 5bc60421a860..5a7ffa1cadf9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -186,11 +186,8 @@ struct mlxsw_sp_fib; struct mlxsw_sp_vr { u16 id; /* virtual router ID */ - bool used; - enum mlxsw_sp_l3proto proto; u32 tb_id; /* kernel fib table id */ - struct mlxsw_sp_lpm_tree *lpm_tree; - struct mlxsw_sp_fib *fib; + struct mlxsw_sp_fib *fib4; }; enum mlxsw_sp_span_type { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index c11a5baca539..0a79fc7afd63 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -102,12 +102,6 @@ mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1, memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1)); } -static void -mlxsw_sp_prefix_usage_zero(struct mlxsw_sp_prefix_usage *prefix_usage) -{ - memset(prefix_usage, 0, sizeof(*prefix_usage)); -} - static void mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage, unsigned char prefix_len) @@ -139,7 +133,7 @@ struct mlxsw_sp_fib_node { struct list_head entry_list; struct list_head list; struct rhash_head ht_node; - struct mlxsw_sp_vr *vr; + struct mlxsw_sp_fib *fib; struct mlxsw_sp_fib_key key; }; @@ -163,13 +157,17 @@ struct mlxsw_sp_fib_entry { struct mlxsw_sp_fib { struct rhashtable ht; struct list_head node_list; + struct mlxsw_sp_vr *vr; + struct mlxsw_sp_lpm_tree *lpm_tree; unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT]; struct mlxsw_sp_prefix_usage prefix_usage; + enum mlxsw_sp_l3proto proto; }; static const struct rhashtable_params mlxsw_sp_fib_ht_params; -static struct mlxsw_sp_fib *mlxsw_sp_fib_create(void) +static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr, + enum mlxsw_sp_l3proto proto) { struct mlxsw_sp_fib *fib; int err; @@ -181,6 +179,8 @@ static struct mlxsw_sp_fib *mlxsw_sp_fib_create(void) if (err) goto err_rhashtable_init; INIT_LIST_HEAD(&fib->node_list); + fib->proto = proto; + fib->vr = vr; return fib; err_rhashtable_init: @@ -191,6 +191,7 @@ err_rhashtable_init: static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib) { WARN_ON(!list_empty(&fib->node_list)); + WARN_ON(fib->lpm_tree); rhashtable_destroy(&fib->ht); kfree(fib); } @@ -335,6 +336,11 @@ static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp) } } +static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr) +{ + return !!vr->fib4; +} + static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp) { struct mlxsw_sp_vr *vr; @@ -342,31 +348,31 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp) for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { vr = &mlxsw_sp->router.vrs[i]; - if (!vr->used) + if (!mlxsw_sp_vr_is_used(vr)) return vr; } return NULL; } static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_vr *vr) + const struct mlxsw_sp_fib *fib) { char raltb_pl[MLXSW_REG_RALTB_LEN]; - mlxsw_reg_raltb_pack(raltb_pl, vr->id, - (enum mlxsw_reg_ralxx_protocol) vr->proto, - vr->lpm_tree->id); + mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id, + (enum mlxsw_reg_ralxx_protocol) fib->proto, + fib->lpm_tree->id); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl); } static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_vr *vr) + const struct mlxsw_sp_fib *fib) { char raltb_pl[MLXSW_REG_RALTB_LEN]; /* Bind to tree 0 which is default */ - mlxsw_reg_raltb_pack(raltb_pl, vr->id, - (enum mlxsw_reg_ralxx_protocol) vr->proto, 0); + mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id, + (enum mlxsw_reg_ralxx_protocol) fib->proto, 0); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl); } @@ -379,8 +385,7 @@ static u32 mlxsw_sp_fix_tb_id(u32 tb_id) } static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp, - u32 tb_id, - enum mlxsw_sp_l3proto proto) + u32 tb_id) { struct mlxsw_sp_vr *vr; int i; @@ -389,69 +394,50 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp, for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { vr = &mlxsw_sp->router.vrs[i]; - if (vr->used && vr->proto == proto && vr->tb_id == tb_id) + if (mlxsw_sp_vr_is_used(vr) && vr->tb_id == tb_id) return vr; } return NULL; } +static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr, + enum mlxsw_sp_l3proto proto) +{ + switch (proto) { + case MLXSW_SP_L3_PROTO_IPV4: + return vr->fib4; + case MLXSW_SP_L3_PROTO_IPV6: + BUG_ON(1); + } + return NULL; +} + static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp, - unsigned char prefix_len, - u32 tb_id, - enum mlxsw_sp_l3proto proto) + u32 tb_id) { - struct mlxsw_sp_prefix_usage req_prefix_usage; - struct mlxsw_sp_lpm_tree *lpm_tree; struct mlxsw_sp_vr *vr; - int err; vr = mlxsw_sp_vr_find_unused(mlxsw_sp); if (!vr) return ERR_PTR(-EBUSY); - vr->fib = mlxsw_sp_fib_create(); - if (IS_ERR(vr->fib)) - return ERR_CAST(vr->fib); - - vr->proto = proto; + vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4); + if (IS_ERR(vr->fib4)) + return ERR_CAST(vr->fib4); vr->tb_id = tb_id; - mlxsw_sp_prefix_usage_zero(&req_prefix_usage); - mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len); - lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, - proto); - if (IS_ERR(lpm_tree)) { - err = PTR_ERR(lpm_tree); - goto err_tree_get; - } - vr->lpm_tree = lpm_tree; - err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr); - if (err) - goto err_tree_bind; - - vr->used = true; return vr; - -err_tree_bind: - mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree); -err_tree_get: - mlxsw_sp_fib_destroy(vr->fib); - - return ERR_PTR(err); } -static void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_vr *vr) +static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr) { - mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, vr); - mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree); - mlxsw_sp_fib_destroy(vr->fib); - vr->used = false; + mlxsw_sp_fib_destroy(vr->fib4); + vr->fib4 = NULL; } static int -mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr, +mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib *fib, struct mlxsw_sp_prefix_usage *req_prefix_usage) { - struct mlxsw_sp_lpm_tree *lpm_tree = vr->lpm_tree; + struct mlxsw_sp_lpm_tree *lpm_tree = fib->lpm_tree; struct mlxsw_sp_lpm_tree *new_tree; int err; @@ -459,7 +445,7 @@ mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr, return 0; new_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage, - vr->proto); + fib->proto); if (IS_ERR(new_tree)) { /* We failed to get a tree according to the required * prefix usage. However, the current tree might be still good @@ -473,8 +459,8 @@ mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr, } /* Prevent packet loss by overwriting existing binding */ - vr->lpm_tree = new_tree; - err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr); + fib->lpm_tree = new_tree; + err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib); if (err) goto err_tree_bind; mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); @@ -482,53 +468,26 @@ mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr, return 0; err_tree_bind: - vr->lpm_tree = lpm_tree; + fib->lpm_tree = lpm_tree; mlxsw_sp_lpm_tree_put(mlxsw_sp, new_tree); return err; } -static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, - unsigned char prefix_len, - u32 tb_id, - enum mlxsw_sp_l3proto proto) +static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id) { struct mlxsw_sp_vr *vr; - int err; tb_id = mlxsw_sp_fix_tb_id(tb_id); - vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id, proto); - if (!vr) { - vr = mlxsw_sp_vr_create(mlxsw_sp, prefix_len, tb_id, proto); - if (IS_ERR(vr)) - return vr; - } else { - struct mlxsw_sp_prefix_usage req_prefix_usage; - - mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, - &vr->fib->prefix_usage); - mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len); - /* Need to replace LPM tree in case new prefix is required. */ - err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr, - &req_prefix_usage); - if (err) - return ERR_PTR(err); - } + vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id); + if (!vr) + vr = mlxsw_sp_vr_create(mlxsw_sp, tb_id); return vr; } -static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr) +static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr) { - /* Destroy virtual router entity in case the associated FIB is empty - * and allow it to be used for other tables in future. Otherwise, - * check if some prefix usage did not disappear and change tree if - * that is the case. Note that in case new, smaller tree cannot be - * allocated, the original one will be kept being used. - */ - if (mlxsw_sp_prefix_usage_none(&vr->fib->prefix_usage)) - mlxsw_sp_vr_destroy(mlxsw_sp, vr); - else - mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr, - &vr->fib->prefix_usage); + if (list_empty(&vr->fib4->node_list)) + mlxsw_sp_vr_destroy(vr); } static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp) @@ -1181,7 +1140,7 @@ mlxsw_sp_nexthop_lookup(struct mlxsw_sp *mlxsw_sp, } static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_vr *vr, + const struct mlxsw_sp_fib *fib, u32 adj_index, u16 ecmp_size, u32 new_adj_index, u16 new_ecmp_size) @@ -1189,8 +1148,8 @@ static int mlxsw_sp_adj_index_mass_update_vr(struct mlxsw_sp *mlxsw_sp, char raleu_pl[MLXSW_REG_RALEU_LEN]; mlxsw_reg_raleu_pack(raleu_pl, - (enum mlxsw_reg_ralxx_protocol) vr->proto, vr->id, - adj_index, ecmp_size, new_adj_index, + (enum mlxsw_reg_ralxx_protocol) fib->proto, + fib->vr->id, adj_index, ecmp_size, new_adj_index, new_ecmp_size); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raleu), raleu_pl); } @@ -1200,14 +1159,14 @@ static int mlxsw_sp_adj_index_mass_update(struct mlxsw_sp *mlxsw_sp, u32 old_adj_index, u16 old_ecmp_size) { struct mlxsw_sp_fib_entry *fib_entry; - struct mlxsw_sp_vr *vr = NULL; + struct mlxsw_sp_fib *fib = NULL; int err; list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) { - if (vr == fib_entry->fib_node->vr) + if (fib == fib_entry->fib_node->fib) continue; - vr = fib_entry->fib_node->vr; - err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, vr, + fib = fib_entry->fib_node->fib; + err = mlxsw_sp_adj_index_mass_update_vr(mlxsw_sp, fib, old_adj_index, old_ecmp_size, nh_grp->adj_index, @@ -1712,7 +1671,7 @@ static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) { fib_entry->offloaded = true; - switch (fib_entry->fib_node->vr->proto) { + switch (fib_entry->fib_node->fib->proto) { case MLXSW_SP_L3_PROTO_IPV4: fib_info_offload_inc(fib_entry->nh_group->key.fi); break; @@ -1724,7 +1683,7 @@ static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) static void mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) { - switch (fib_entry->fib_node->vr->proto) { + switch (fib_entry->fib_node->fib->proto) { case MLXSW_SP_L3_PROTO_IPV4: fib_info_offload_dec(fib_entry->nh_group->key.fi); break; @@ -1764,8 +1723,8 @@ static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp, enum mlxsw_reg_ralue_op op) { char ralue_pl[MLXSW_REG_RALUE_LEN]; + struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib; u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr; - struct mlxsw_sp_vr *vr = fib_entry->fib_node->vr; enum mlxsw_reg_ralue_trap_action trap_action; u16 trap_id = 0; u32 adjacency_index = 0; @@ -1785,8 +1744,8 @@ static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp, } mlxsw_reg_ralue_pack4(ralue_pl, - (enum mlxsw_reg_ralxx_protocol) vr->proto, op, - vr->id, fib_entry->fib_node->key.prefix_len, + (enum mlxsw_reg_ralxx_protocol) fib->proto, op, + fib->vr->id, fib_entry->fib_node->key.prefix_len, *p_dip); mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id, adjacency_index, ecmp_size); @@ -1798,10 +1757,10 @@ static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp, enum mlxsw_reg_ralue_op op) { struct mlxsw_sp_rif *r = fib_entry->nh_group->nh_rif; + struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib; enum mlxsw_reg_ralue_trap_action trap_action; char ralue_pl[MLXSW_REG_RALUE_LEN]; u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr; - struct mlxsw_sp_vr *vr = fib_entry->fib_node->vr; u16 trap_id = 0; u16 rif = 0; @@ -1814,8 +1773,8 @@ static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp, } mlxsw_reg_ralue_pack4(ralue_pl, - (enum mlxsw_reg_ralxx_protocol) vr->proto, op, - vr->id, fib_entry->fib_node->key.prefix_len, + (enum mlxsw_reg_ralxx_protocol) fib->proto, op, + fib->vr->id, fib_entry->fib_node->key.prefix_len, *p_dip); mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id, rif); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); @@ -1825,13 +1784,13 @@ static int mlxsw_sp_fib_entry_op4_trap(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry *fib_entry, enum mlxsw_reg_ralue_op op) { + struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib; char ralue_pl[MLXSW_REG_RALUE_LEN]; u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr; - struct mlxsw_sp_vr *vr = fib_entry->fib_node->vr; mlxsw_reg_ralue_pack4(ralue_pl, - (enum mlxsw_reg_ralxx_protocol) vr->proto, op, - vr->id, fib_entry->fib_node->key.prefix_len, + (enum mlxsw_reg_ralxx_protocol) fib->proto, op, + fib->vr->id, fib_entry->fib_node->key.prefix_len, *p_dip); mlxsw_reg_ralue_act_ip2me_pack(ralue_pl); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); @@ -1858,7 +1817,7 @@ static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp, { int err = -EINVAL; - switch (fib_entry->fib_node->vr->proto) { + switch (fib_entry->fib_node->fib->proto) { case MLXSW_SP_L3_PROTO_IPV4: err = mlxsw_sp_fib_entry_op4(mlxsw_sp, fib_entry, op); break; @@ -2021,7 +1980,7 @@ mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr, } static struct mlxsw_sp_fib_node * -mlxsw_sp_fib_node_create(struct mlxsw_sp_vr *vr, const void *addr, +mlxsw_sp_fib_node_create(struct mlxsw_sp_fib *fib, const void *addr, size_t addr_len, unsigned char prefix_len) { struct mlxsw_sp_fib_node *fib_node; @@ -2031,18 +1990,15 @@ mlxsw_sp_fib_node_create(struct mlxsw_sp_vr *vr, const void *addr, return NULL; INIT_LIST_HEAD(&fib_node->entry_list); - list_add(&fib_node->list, &vr->fib->node_list); + list_add(&fib_node->list, &fib->node_list); memcpy(fib_node->key.addr, addr, addr_len); fib_node->key.prefix_len = prefix_len; - mlxsw_sp_fib_node_insert(vr->fib, fib_node); - fib_node->vr = vr; return fib_node; } static void mlxsw_sp_fib_node_destroy(struct mlxsw_sp_fib_node *fib_node) { - mlxsw_sp_fib_node_remove(fib_node->vr->fib, fib_node); list_del(&fib_node->list); WARN_ON(!list_empty(&fib_node->entry_list)); kfree(fib_node); @@ -2059,7 +2015,7 @@ mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node, static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node) { unsigned char prefix_len = fib_node->key.prefix_len; - struct mlxsw_sp_fib *fib = fib_node->vr->fib; + struct mlxsw_sp_fib *fib = fib_node->fib; if (fib->prefix_ref_count[prefix_len]++ == 0) mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len); @@ -2068,32 +2024,98 @@ static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node) static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node) { unsigned char prefix_len = fib_node->key.prefix_len; - struct mlxsw_sp_fib *fib = fib_node->vr->fib; + struct mlxsw_sp_fib *fib = fib_node->fib; if (--fib->prefix_ref_count[prefix_len] == 0) mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len); } +static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_node *fib_node, + struct mlxsw_sp_fib *fib) +{ + struct mlxsw_sp_prefix_usage req_prefix_usage; + struct mlxsw_sp_lpm_tree *lpm_tree; + int err; + + err = mlxsw_sp_fib_node_insert(fib, fib_node); + if (err) + return err; + fib_node->fib = fib; + + mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &fib->prefix_usage); + mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len); + + if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) { + err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib, + &req_prefix_usage); + if (err) + goto err_tree_check; + } else { + lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, + fib->proto); + if (IS_ERR(lpm_tree)) + return PTR_ERR(lpm_tree); + fib->lpm_tree = lpm_tree; + err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib); + if (err) + goto err_tree_bind; + } + + mlxsw_sp_fib_node_prefix_inc(fib_node); + + return 0; + +err_tree_bind: + fib->lpm_tree = NULL; + mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); +err_tree_check: + fib_node->fib = NULL; + mlxsw_sp_fib_node_remove(fib, fib_node); + return err; +} + +static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_node *fib_node) +{ + struct mlxsw_sp_lpm_tree *lpm_tree = fib_node->fib->lpm_tree; + struct mlxsw_sp_fib *fib = fib_node->fib; + + mlxsw_sp_fib_node_prefix_dec(fib_node); + + if (mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) { + mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib); + fib->lpm_tree = NULL; + mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); + } else { + mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib, &fib->prefix_usage); + } + + fib_node->fib = NULL; + mlxsw_sp_fib_node_remove(fib, fib_node); +} + static struct mlxsw_sp_fib_node * mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp, const struct fib_entry_notifier_info *fen_info) { struct mlxsw_sp_fib_node *fib_node; + struct mlxsw_sp_fib *fib; struct mlxsw_sp_vr *vr; int err; - vr = mlxsw_sp_vr_get(mlxsw_sp, fen_info->dst_len, fen_info->tb_id, - MLXSW_SP_L3_PROTO_IPV4); + vr = mlxsw_sp_vr_get(mlxsw_sp, fen_info->tb_id); if (IS_ERR(vr)) return ERR_CAST(vr); + fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4); - fib_node = mlxsw_sp_fib_node_lookup(vr->fib, &fen_info->dst, + fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst, sizeof(fen_info->dst), fen_info->dst_len); if (fib_node) return fib_node; - fib_node = mlxsw_sp_fib_node_create(vr, &fen_info->dst, + fib_node = mlxsw_sp_fib_node_create(fib, &fen_info->dst, sizeof(fen_info->dst), fen_info->dst_len); if (!fib_node) { @@ -2101,22 +2123,29 @@ mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp, goto err_fib_node_create; } + err = mlxsw_sp_fib_node_init(mlxsw_sp, fib_node, fib); + if (err) + goto err_fib_node_init; + return fib_node; +err_fib_node_init: + mlxsw_sp_fib_node_destroy(fib_node); err_fib_node_create: - mlxsw_sp_vr_put(mlxsw_sp, vr); + mlxsw_sp_vr_put(vr); return ERR_PTR(err); } static void mlxsw_sp_fib4_node_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node) { - struct mlxsw_sp_vr *vr = fib_node->vr; + struct mlxsw_sp_vr *vr = fib_node->fib->vr; if (!list_empty(&fib_node->entry_list)) return; + mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node); mlxsw_sp_fib_node_destroy(fib_node); - mlxsw_sp_vr_put(mlxsw_sp, vr); + mlxsw_sp_vr_put(vr); } static struct mlxsw_sp_fib_entry * @@ -2261,8 +2290,6 @@ static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp, if (err) goto err_fib4_node_entry_add; - mlxsw_sp_fib_node_prefix_inc(fib_node); - return 0; err_fib4_node_entry_add: @@ -2276,7 +2303,6 @@ mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; - mlxsw_sp_fib_node_prefix_dec(fib_node); mlxsw_sp_fib4_node_entry_del(mlxsw_sp, fib_node, fib_entry); mlxsw_sp_fib4_node_list_remove(fib_entry); } @@ -2415,7 +2441,7 @@ static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp, static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node) { - switch (fib_node->vr->proto) { + switch (fib_node->fib->proto) { case MLXSW_SP_L3_PROTO_IPV4: mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node); break; @@ -2425,26 +2451,32 @@ static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp, } } -static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp) +static void mlxsw_sp_vr_fib_flush(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_vr *vr, + enum mlxsw_sp_l3proto proto) { + struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto); struct mlxsw_sp_fib_node *fib_node, *tmp; - struct mlxsw_sp_vr *vr; + + list_for_each_entry_safe(fib_node, tmp, &fib->node_list, list) { + bool do_break = &tmp->list == &fib->node_list; + + mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node); + if (do_break) + break; + } +} + +static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp) +{ int i; for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { - vr = &mlxsw_sp->router.vrs[i]; + struct mlxsw_sp_vr *vr = &mlxsw_sp->router.vrs[i]; - if (!vr->used) + if (!mlxsw_sp_vr_is_used(vr)) continue; - - list_for_each_entry_safe(fib_node, tmp, &vr->fib->node_list, - list) { - bool do_break = &tmp->list == &vr->fib->node_list; - - mlxsw_sp_fib_node_flush(mlxsw_sp, fib_node); - if (do_break) - break; - } + mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4); } } -- cgit From 6913229eea27e63df086f55372e52021727fd0a7 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 10 Mar 2017 08:53:42 +0100 Subject: mlxsw: spectrum_router: Explicitly Associate RIFs with VRs Up until now we implicitly associated all the router interfaces (RIFs) with the first virtual router (VR). This must be changed in order to enable VRF offload. Otherwise, a packet received via a VRF slave would do a FIB lookup in the same table used by other VRFs. Instead, bind the RIF to a VR according to the table where FIB lookup should be performed for packets received via the RIF. Currently, we only care about the MAIN and LOCAL tables (which we squash together). Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 4 +- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 1 + .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 63 ++++++++++++++++------ 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 0899e2d310e2..eb94a5a4625d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4141,7 +4141,8 @@ static inline void mlxsw_reg_ritr_sp_if_pack(char *payload, bool lag, static inline void mlxsw_reg_ritr_pack(char *payload, bool enable, enum mlxsw_reg_ritr_if_type type, - u16 rif, u16 mtu, const char *mac) + u16 rif, u16 vr_id, u16 mtu, + const char *mac) { bool op = enable ? MLXSW_REG_RITR_RIF_CREATE : MLXSW_REG_RITR_RIF_DEL; @@ -4153,6 +4154,7 @@ static inline void mlxsw_reg_ritr_pack(char *payload, bool enable, mlxsw_reg_ritr_rif_set(payload, rif); mlxsw_reg_ritr_ipv4_fe_set(payload, 1); mlxsw_reg_ritr_lb_en_set(payload, 1); + mlxsw_reg_ritr_virtual_router_set(payload, vr_id); mlxsw_reg_ritr_mtu_set(payload, mtu); mlxsw_reg_ritr_if_mac_memcpy_to(payload, mac); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 5a7ffa1cadf9..3bc1b0998654 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -187,6 +187,7 @@ struct mlxsw_sp_fib; struct mlxsw_sp_vr { u16 id; /* virtual router ID */ u32 tb_id; /* kernel fib table id */ + unsigned int rif_count; struct mlxsw_sp_fib *fib4; }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 0a79fc7afd63..22a4d689ea13 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -58,6 +58,7 @@ struct mlxsw_sp_rif { unsigned char addr[ETH_ALEN]; int mtu; u16 rif; + u16 vr_id; }; static struct mlxsw_sp_rif * @@ -486,7 +487,7 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id) static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr) { - if (list_empty(&vr->fib4->node_list)) + if (!vr->rif_count && list_empty(&vr->fib4->node_list)) mlxsw_sp_vr_destroy(vr); } @@ -2666,15 +2667,15 @@ static void mlxsw_sp_vport_rif_sp_attr_get(struct mlxsw_sp_port *mlxsw_sp_vport, } static int mlxsw_sp_vport_rif_sp_op(struct mlxsw_sp_port *mlxsw_sp_vport, - struct net_device *l3_dev, u16 rif, - bool create) + u16 vr_id, struct net_device *l3_dev, + u16 rif, bool create) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; bool lagged = mlxsw_sp_vport->lagged; char ritr_pl[MLXSW_REG_RITR_LEN]; u16 system_port; - mlxsw_reg_ritr_pack(ritr_pl, create, MLXSW_REG_RITR_SP_IF, rif, + mlxsw_reg_ritr_pack(ritr_pl, create, MLXSW_REG_RITR_SP_IF, rif, vr_id, l3_dev->mtu, l3_dev->dev_addr); mlxsw_sp_vport_rif_sp_attr_get(mlxsw_sp_vport, &lagged, &system_port); @@ -2709,7 +2710,8 @@ mlxsw_sp_rfid_alloc(u16 fid, struct net_device *l3_dev) } static struct mlxsw_sp_rif * -mlxsw_sp_rif_alloc(u16 rif, struct net_device *l3_dev, struct mlxsw_sp_fid *f) +mlxsw_sp_rif_alloc(u16 rif, u16 vr_id, struct net_device *l3_dev, + struct mlxsw_sp_fid *f) { struct mlxsw_sp_rif *r; @@ -2721,6 +2723,7 @@ mlxsw_sp_rif_alloc(u16 rif, struct net_device *l3_dev, struct mlxsw_sp_fid *f) INIT_LIST_HEAD(&r->neigh_list); ether_addr_copy(r->addr, l3_dev->dev_addr); r->mtu = l3_dev->mtu; + r->vr_id = vr_id; r->dev = l3_dev; r->rif = rif; r->f = f; @@ -2733,6 +2736,7 @@ mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport, struct net_device *l3_dev) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; + struct mlxsw_sp_vr *vr; struct mlxsw_sp_fid *f; struct mlxsw_sp_rif *r; u16 fid, rif; @@ -2742,9 +2746,14 @@ mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport, if (rif == MLXSW_SP_INVALID_RIF) return ERR_PTR(-ERANGE); - err = mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, true); + vr = mlxsw_sp_vr_get(mlxsw_sp, RT_TABLE_MAIN); + if (IS_ERR(vr)) + return ERR_CAST(vr); + + err = mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, vr->id, l3_dev, rif, + true); if (err) - return ERR_PTR(err); + goto err_vport_rif_sp_op; fid = mlxsw_sp_rif_sp_to_fid(rif); err = mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, true); @@ -2757,7 +2766,7 @@ mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport, goto err_rfid_alloc; } - r = mlxsw_sp_rif_alloc(rif, l3_dev, f); + r = mlxsw_sp_rif_alloc(rif, vr->id, l3_dev, f); if (!r) { err = -ENOMEM; goto err_rif_alloc; @@ -2765,6 +2774,7 @@ mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport, f->r = r; mlxsw_sp->rifs[rif] = r; + vr->rif_count++; return r; @@ -2773,7 +2783,9 @@ err_rif_alloc: err_rfid_alloc: mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false); err_rif_fdb_op: - mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, false); + mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, vr->id, l3_dev, rif, false); +err_vport_rif_sp_op: + mlxsw_sp_vr_put(vr); return ERR_PTR(err); } @@ -2781,6 +2793,7 @@ static void mlxsw_sp_vport_rif_sp_destroy(struct mlxsw_sp_port *mlxsw_sp_vport, struct mlxsw_sp_rif *r) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; + struct mlxsw_sp_vr *vr = &mlxsw_sp->router.vrs[r->vr_id]; struct net_device *l3_dev = r->dev; struct mlxsw_sp_fid *f = r->f; u16 fid = f->fid; @@ -2788,6 +2801,7 @@ static void mlxsw_sp_vport_rif_sp_destroy(struct mlxsw_sp_port *mlxsw_sp_vport, mlxsw_sp_router_rif_gone_sync(mlxsw_sp, r); + vr->rif_count--; mlxsw_sp->rifs[rif] = NULL; f->r = NULL; @@ -2797,7 +2811,9 @@ static void mlxsw_sp_vport_rif_sp_destroy(struct mlxsw_sp_port *mlxsw_sp_vport, mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false); - mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, l3_dev, rif, false); + mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, vr->id, l3_dev, rif, false); + + mlxsw_sp_vr_put(vr); } static int mlxsw_sp_vport_rif_sp_join(struct mlxsw_sp_port *mlxsw_sp_vport, @@ -2948,7 +2964,7 @@ static enum mlxsw_reg_ritr_if_type mlxsw_sp_rif_type_get(u16 fid) return MLXSW_REG_RITR_VLAN_IF; } -static int mlxsw_sp_rif_bridge_op(struct mlxsw_sp *mlxsw_sp, +static int mlxsw_sp_rif_bridge_op(struct mlxsw_sp *mlxsw_sp, u16 vr_id, struct net_device *l3_dev, u16 fid, u16 rif, bool create) @@ -2957,7 +2973,7 @@ static int mlxsw_sp_rif_bridge_op(struct mlxsw_sp *mlxsw_sp, char ritr_pl[MLXSW_REG_RITR_LEN]; rif_type = mlxsw_sp_rif_type_get(fid); - mlxsw_reg_ritr_pack(ritr_pl, create, rif_type, rif, l3_dev->mtu, + mlxsw_reg_ritr_pack(ritr_pl, create, rif_type, rif, vr_id, l3_dev->mtu, l3_dev->dev_addr); mlxsw_reg_ritr_fid_set(ritr_pl, rif_type, fid); @@ -2968,6 +2984,7 @@ static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp, struct net_device *l3_dev, struct mlxsw_sp_fid *f) { + struct mlxsw_sp_vr *vr; struct mlxsw_sp_rif *r; u16 rif; int err; @@ -2976,11 +2993,16 @@ static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp, if (rif == MLXSW_SP_INVALID_RIF) return -ERANGE; + vr = mlxsw_sp_vr_get(mlxsw_sp, RT_TABLE_MAIN); + if (IS_ERR(vr)) + return PTR_ERR(vr); + err = mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, true); if (err) - return err; + goto err_port_flood_set; - err = mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, true); + err = mlxsw_sp_rif_bridge_op(mlxsw_sp, vr->id, l3_dev, f->fid, rif, + true); if (err) goto err_rif_bridge_op; @@ -2988,7 +3010,7 @@ static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp, if (err) goto err_rif_fdb_op; - r = mlxsw_sp_rif_alloc(rif, l3_dev, f); + r = mlxsw_sp_rif_alloc(rif, vr->id, l3_dev, f); if (!r) { err = -ENOMEM; goto err_rif_alloc; @@ -2996,6 +3018,7 @@ static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp, f->r = r; mlxsw_sp->rifs[rif] = r; + vr->rif_count++; netdev_dbg(l3_dev, "RIF=%d created\n", rif); @@ -3004,21 +3027,25 @@ static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp, err_rif_alloc: mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false); err_rif_fdb_op: - mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, false); + mlxsw_sp_rif_bridge_op(mlxsw_sp, vr->id, l3_dev, f->fid, rif, false); err_rif_bridge_op: mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, false); +err_port_flood_set: + mlxsw_sp_vr_put(vr); return err; } void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_rif *r) { + struct mlxsw_sp_vr *vr = &mlxsw_sp->router.vrs[r->vr_id]; struct net_device *l3_dev = r->dev; struct mlxsw_sp_fid *f = r->f; u16 rif = r->rif; mlxsw_sp_router_rif_gone_sync(mlxsw_sp, r); + vr->rif_count--; mlxsw_sp->rifs[rif] = NULL; f->r = NULL; @@ -3026,10 +3053,12 @@ void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false); - mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, false); + mlxsw_sp_rif_bridge_op(mlxsw_sp, vr->id, l3_dev, f->fid, rif, false); mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, false); + mlxsw_sp_vr_put(vr); + netdev_dbg(l3_dev, "RIF=%d destroyed\n", rif); } -- cgit From b5d90e6d6b19477c49ad699c173bbcfa3e4725bd Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 10 Mar 2017 08:53:43 +0100 Subject: mlxsw: spectrum_router: Make abort mechanism VR-aware When the abort mechanism is invoked it binds the first virtual router (VR) to an LPM tree and inserts a default route to direct packets to the CPU. With VRFs, we can have router interfaces (RIFs) bound to multiple VRs, so we need to make sure packets are trapped from all VRs and not just the first one. Upon abort invocation, bind all active VRs to the same LPM tree and insert a default route in each. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 39 +++++++++++++++------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 22a4d689ea13..80345a1ddf17 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2392,9 +2392,7 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) { char ralta_pl[MLXSW_REG_RALTA_LEN]; char ralst_pl[MLXSW_REG_RALST_LEN]; - char raltb_pl[MLXSW_REG_RALTB_LEN]; - char ralue_pl[MLXSW_REG_RALUE_LEN]; - int err; + int i, err; mlxsw_reg_ralta_pack(ralta_pl, true, MLXSW_REG_RALXX_PROTOCOL_IPV4, MLXSW_SP_LPM_TREE_MIN); @@ -2407,16 +2405,33 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) if (err) return err; - mlxsw_reg_raltb_pack(raltb_pl, 0, MLXSW_REG_RALXX_PROTOCOL_IPV4, - MLXSW_SP_LPM_TREE_MIN); - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl); - if (err) - return err; + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { + struct mlxsw_sp_vr *vr = &mlxsw_sp->router.vrs[i]; + char raltb_pl[MLXSW_REG_RALTB_LEN]; + char ralue_pl[MLXSW_REG_RALUE_LEN]; - mlxsw_reg_ralue_pack4(ralue_pl, MLXSW_SP_L3_PROTO_IPV4, - MLXSW_REG_RALUE_OP_WRITE_WRITE, 0, 0, 0); - mlxsw_reg_ralue_act_ip2me_pack(ralue_pl); - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); + if (!mlxsw_sp_vr_is_used(vr)) + continue; + + mlxsw_reg_raltb_pack(raltb_pl, vr->id, + MLXSW_REG_RALXX_PROTOCOL_IPV4, + MLXSW_SP_LPM_TREE_MIN); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), + raltb_pl); + if (err) + return err; + + mlxsw_reg_ralue_pack4(ralue_pl, MLXSW_SP_L3_PROTO_IPV4, + MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0, + 0); + mlxsw_reg_ralue_act_ip2me_pack(ralue_pl); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), + ralue_pl); + if (err) + return err; + } + + return 0; } static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp, -- cgit From c0243892cbb0e48873d6132f673c830602808245 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 10 Mar 2017 08:56:18 +0100 Subject: ipv4: fib: Move FIB notification code to a separate file Most of the code concerned with the FIB notification chain currently resides in fib_trie.c, but this isn't really appropriate, as the FIB notification chain is also used for FIB rules. Therefore, it makes sense to move the common FIB notification code to a separate file and have it export the relevant functions, which can be invoked by its different users (e.g., fib_trie.c, fib_rules.c). Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Acked-by: David Ahern Signed-off-by: David S. Miller --- include/net/ip_fib.h | 15 ++++++++ net/ipv4/Makefile | 2 +- net/ipv4/fib_notifier.c | 86 +++++++++++++++++++++++++++++++++++++++++++ net/ipv4/fib_rules.c | 9 +++++ net/ipv4/fib_trie.c | 97 +------------------------------------------------ 5 files changed, 113 insertions(+), 96 deletions(-) create mode 100644 net/ipv4/fib_notifier.c diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 368bb4024b78..3ad87063a475 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -232,9 +232,24 @@ enum fib_event_type { int register_fib_notifier(struct notifier_block *nb, void (*cb)(struct notifier_block *nb)); int unregister_fib_notifier(struct notifier_block *nb); +int call_fib_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct fib_notifier_info *info); int call_fib_notifiers(struct net *net, enum fib_event_type event_type, struct fib_notifier_info *info); +void fib_notify(struct net *net, struct notifier_block *nb, + enum fib_event_type event_type); +#ifdef CONFIG_IP_MULTIPLE_TABLES +void fib_rules_notify(struct net *net, struct notifier_block *nb, + enum fib_event_type event_type); +#else +static inline void fib_rules_notify(struct net *net, struct notifier_block *nb, + enum fib_event_type event_type) +{ +} +#endif + struct fib_table { struct hlist_node tb_hlist; u32 tb_id; diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index c6d4238ff94a..f83de23a30e7 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -11,7 +11,7 @@ obj-y := route.o inetpeer.o protocol.o \ tcp_rate.o tcp_recovery.o \ tcp_offload.o datagram.o raw.o udp.o udplite.o \ udp_offload.o arp.o icmp.o devinet.o af_inet.o igmp.o \ - fib_frontend.o fib_semantics.o fib_trie.o \ + fib_frontend.o fib_semantics.o fib_trie.o fib_notifier.o \ inet_fragment.o ping.o ip_tunnel_core.o gre_offload.o obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o diff --git a/net/ipv4/fib_notifier.c b/net/ipv4/fib_notifier.c new file mode 100644 index 000000000000..91f8f181fa03 --- /dev/null +++ b/net/ipv4/fib_notifier.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include +#include + +static ATOMIC_NOTIFIER_HEAD(fib_chain); + +int call_fib_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct fib_notifier_info *info) +{ + info->net = net; + return nb->notifier_call(nb, event_type, info); +} + +int call_fib_notifiers(struct net *net, enum fib_event_type event_type, + struct fib_notifier_info *info) +{ + net->ipv4.fib_seq++; + info->net = net; + return atomic_notifier_call_chain(&fib_chain, event_type, info); +} + +static unsigned int fib_seq_sum(void) +{ + unsigned int fib_seq = 0; + struct net *net; + + rtnl_lock(); + for_each_net(net) + fib_seq += net->ipv4.fib_seq; + rtnl_unlock(); + + return fib_seq; +} + +static bool fib_dump_is_consistent(struct notifier_block *nb, + void (*cb)(struct notifier_block *nb), + unsigned int fib_seq) +{ + atomic_notifier_chain_register(&fib_chain, nb); + if (fib_seq == fib_seq_sum()) + return true; + atomic_notifier_chain_unregister(&fib_chain, nb); + if (cb) + cb(nb); + return false; +} + +#define FIB_DUMP_MAX_RETRIES 5 +int register_fib_notifier(struct notifier_block *nb, + void (*cb)(struct notifier_block *nb)) +{ + int retries = 0; + + do { + unsigned int fib_seq = fib_seq_sum(); + struct net *net; + + /* Mutex semantics guarantee that every change done to + * FIB tries before we read the change sequence counter + * is now visible to us. + */ + rcu_read_lock(); + for_each_net_rcu(net) { + fib_rules_notify(net, nb, FIB_EVENT_RULE_ADD); + fib_notify(net, nb, FIB_EVENT_ENTRY_ADD); + } + rcu_read_unlock(); + + if (fib_dump_is_consistent(nb, cb, fib_seq)) + return 0; + } while (++retries < FIB_DUMP_MAX_RETRIES); + + return -EBUSY; +} +EXPORT_SYMBOL(register_fib_notifier); + +int unregister_fib_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&fib_chain, nb); +} +EXPORT_SYMBOL(unregister_fib_notifier); diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 2e50062f642d..bbd57f099183 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -172,6 +172,15 @@ static int call_fib_rule_notifiers(struct net *net, return call_fib_notifiers(net, event_type, &info); } +void fib_rules_notify(struct net *net, struct notifier_block *nb, + enum fib_event_type event_type) +{ + struct fib_notifier_info info; + + if (net->ipv4.fib_has_custom_rules) + call_fib_notifier(nb, net, event_type, &info); +} + static const struct nla_policy fib4_rule_policy[FRA_MAX+1] = { FRA_GENERIC_POLICY, [FRA_FLOW] = { .type = NLA_U32 }, diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 2f0d8233950f..5639e8a42f1b 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -84,43 +84,6 @@ #include #include "fib_lookup.h" -static unsigned int fib_seq_sum(void) -{ - unsigned int fib_seq = 0; - struct net *net; - - rtnl_lock(); - for_each_net(net) - fib_seq += net->ipv4.fib_seq; - rtnl_unlock(); - - return fib_seq; -} - -static ATOMIC_NOTIFIER_HEAD(fib_chain); - -static int call_fib_notifier(struct notifier_block *nb, struct net *net, - enum fib_event_type event_type, - struct fib_notifier_info *info) -{ - info->net = net; - return nb->notifier_call(nb, event_type, info); -} - -static void fib_rules_notify(struct net *net, struct notifier_block *nb, - enum fib_event_type event_type) -{ -#ifdef CONFIG_IP_MULTIPLE_TABLES - struct fib_notifier_info info; - - if (net->ipv4.fib_has_custom_rules) - call_fib_notifier(nb, net, event_type, &info); -#endif -} - -static void fib_notify(struct net *net, struct notifier_block *nb, - enum fib_event_type event_type); - static int call_fib_entry_notifier(struct notifier_block *nb, struct net *net, enum fib_event_type event_type, u32 dst, int dst_len, struct fib_info *fi, @@ -137,62 +100,6 @@ static int call_fib_entry_notifier(struct notifier_block *nb, struct net *net, return call_fib_notifier(nb, net, event_type, &info.info); } -static bool fib_dump_is_consistent(struct notifier_block *nb, - void (*cb)(struct notifier_block *nb), - unsigned int fib_seq) -{ - atomic_notifier_chain_register(&fib_chain, nb); - if (fib_seq == fib_seq_sum()) - return true; - atomic_notifier_chain_unregister(&fib_chain, nb); - if (cb) - cb(nb); - return false; -} - -#define FIB_DUMP_MAX_RETRIES 5 -int register_fib_notifier(struct notifier_block *nb, - void (*cb)(struct notifier_block *nb)) -{ - int retries = 0; - - do { - unsigned int fib_seq = fib_seq_sum(); - struct net *net; - - /* Mutex semantics guarantee that every change done to - * FIB tries before we read the change sequence counter - * is now visible to us. - */ - rcu_read_lock(); - for_each_net_rcu(net) { - fib_rules_notify(net, nb, FIB_EVENT_RULE_ADD); - fib_notify(net, nb, FIB_EVENT_ENTRY_ADD); - } - rcu_read_unlock(); - - if (fib_dump_is_consistent(nb, cb, fib_seq)) - return 0; - } while (++retries < FIB_DUMP_MAX_RETRIES); - - return -EBUSY; -} -EXPORT_SYMBOL(register_fib_notifier); - -int unregister_fib_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&fib_chain, nb); -} -EXPORT_SYMBOL(unregister_fib_notifier); - -int call_fib_notifiers(struct net *net, enum fib_event_type event_type, - struct fib_notifier_info *info) -{ - net->ipv4.fib_seq++; - info->net = net; - return atomic_notifier_call_chain(&fib_chain, event_type, info); -} - static int call_fib_entry_notifiers(struct net *net, enum fib_event_type event_type, u32 dst, int dst_len, struct fib_info *fi, @@ -2036,8 +1943,8 @@ static void fib_table_notify(struct net *net, struct fib_table *tb, } } -static void fib_notify(struct net *net, struct notifier_block *nb, - enum fib_event_type event_type) +void fib_notify(struct net *net, struct notifier_block *nb, + enum fib_event_type event_type) { unsigned int h; -- cgit From d05f7a7dd470f71dc45c2928dbf76afe2b1c2f07 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 10 Mar 2017 08:56:19 +0100 Subject: ipv4: fib: Remove redundant argument We always pass the same event type to fib_notify() and fib_rules_notify(), so we can safely drop this argument. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Acked-by: David Ahern Signed-off-by: David S. Miller --- include/net/ip_fib.h | 9 +++------ net/ipv4/fib_notifier.c | 4 ++-- net/ipv4/fib_rules.c | 5 ++--- net/ipv4/fib_trie.c | 15 ++++++--------- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 3ad87063a475..d9cee9659978 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -238,14 +238,11 @@ int call_fib_notifier(struct notifier_block *nb, struct net *net, int call_fib_notifiers(struct net *net, enum fib_event_type event_type, struct fib_notifier_info *info); -void fib_notify(struct net *net, struct notifier_block *nb, - enum fib_event_type event_type); +void fib_notify(struct net *net, struct notifier_block *nb); #ifdef CONFIG_IP_MULTIPLE_TABLES -void fib_rules_notify(struct net *net, struct notifier_block *nb, - enum fib_event_type event_type); +void fib_rules_notify(struct net *net, struct notifier_block *nb); #else -static inline void fib_rules_notify(struct net *net, struct notifier_block *nb, - enum fib_event_type event_type) +static inline void fib_rules_notify(struct net *net, struct notifier_block *nb) { } #endif diff --git a/net/ipv4/fib_notifier.c b/net/ipv4/fib_notifier.c index 91f8f181fa03..e0714d975947 100644 --- a/net/ipv4/fib_notifier.c +++ b/net/ipv4/fib_notifier.c @@ -66,8 +66,8 @@ int register_fib_notifier(struct notifier_block *nb, */ rcu_read_lock(); for_each_net_rcu(net) { - fib_rules_notify(net, nb, FIB_EVENT_RULE_ADD); - fib_notify(net, nb, FIB_EVENT_ENTRY_ADD); + fib_rules_notify(net, nb); + fib_notify(net, nb); } rcu_read_unlock(); diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index bbd57f099183..289210903d58 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -172,13 +172,12 @@ static int call_fib_rule_notifiers(struct net *net, return call_fib_notifiers(net, event_type, &info); } -void fib_rules_notify(struct net *net, struct notifier_block *nb, - enum fib_event_type event_type) +void fib_rules_notify(struct net *net, struct notifier_block *nb) { struct fib_notifier_info info; if (net->ipv4.fib_has_custom_rules) - call_fib_notifier(nb, net, event_type, &info); + call_fib_notifier(nb, net, FIB_EVENT_RULE_ADD, &info); } static const struct nla_policy fib4_rule_policy[FRA_MAX+1] = { diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 5639e8a42f1b..1201409ba1dc 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1902,8 +1902,7 @@ int fib_table_flush(struct net *net, struct fib_table *tb) } static void fib_leaf_notify(struct net *net, struct key_vector *l, - struct fib_table *tb, struct notifier_block *nb, - enum fib_event_type event_type) + struct fib_table *tb, struct notifier_block *nb) { struct fib_alias *fa; @@ -1919,22 +1918,21 @@ static void fib_leaf_notify(struct net *net, struct key_vector *l, if (tb->tb_id != fa->tb_id) continue; - call_fib_entry_notifier(nb, net, event_type, l->key, + call_fib_entry_notifier(nb, net, FIB_EVENT_ENTRY_ADD, l->key, KEYLENGTH - fa->fa_slen, fi, fa->fa_tos, fa->fa_type, fa->tb_id); } } static void fib_table_notify(struct net *net, struct fib_table *tb, - struct notifier_block *nb, - enum fib_event_type event_type) + struct notifier_block *nb) { struct trie *t = (struct trie *)tb->tb_data; struct key_vector *l, *tp = t->kv; t_key key = 0; while ((l = leaf_walk_rcu(&tp, key)) != NULL) { - fib_leaf_notify(net, l, tb, nb, event_type); + fib_leaf_notify(net, l, tb, nb); key = l->key + 1; /* stop in case of wrap around */ @@ -1943,8 +1941,7 @@ static void fib_table_notify(struct net *net, struct fib_table *tb, } } -void fib_notify(struct net *net, struct notifier_block *nb, - enum fib_event_type event_type) +void fib_notify(struct net *net, struct notifier_block *nb) { unsigned int h; @@ -1953,7 +1950,7 @@ void fib_notify(struct net *net, struct notifier_block *nb, struct fib_table *tb; hlist_for_each_entry_rcu(tb, head, tb_hlist) - fib_table_notify(net, tb, nb, event_type); + fib_table_notify(net, tb, nb); } } -- cgit From 99bdb400f7a5c998e106aeef3954e94b1d3d4ca5 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Mon, 6 Mar 2017 23:26:09 +0100 Subject: net: tundra: tsi108: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/tundra/tsi108_eth.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/tundra/tsi108_eth.c b/drivers/net/ethernet/tundra/tsi108_eth.c index c5583991da4a..5ac6eaa9e785 100644 --- a/drivers/net/ethernet/tundra/tsi108_eth.c +++ b/drivers/net/ethernet/tundra/tsi108_eth.c @@ -1499,27 +1499,29 @@ static void tsi108_init_mac(struct net_device *dev) TSI_WRITE(TSI108_EC_INTMASK, ~0); } -static int tsi108_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int tsi108_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct tsi108_prv_data *data = netdev_priv(dev); unsigned long flags; int rc; spin_lock_irqsave(&data->txlock, flags); - rc = mii_ethtool_gset(&data->mii_if, cmd); + rc = mii_ethtool_get_link_ksettings(&data->mii_if, cmd); spin_unlock_irqrestore(&data->txlock, flags); return rc; } -static int tsi108_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int tsi108_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct tsi108_prv_data *data = netdev_priv(dev); unsigned long flags; int rc; spin_lock_irqsave(&data->txlock, flags); - rc = mii_ethtool_sset(&data->mii_if, cmd); + rc = mii_ethtool_set_link_ksettings(&data->mii_if, cmd); spin_unlock_irqrestore(&data->txlock, flags); return rc; @@ -1535,8 +1537,8 @@ static int tsi108_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static const struct ethtool_ops tsi108_ethtool_ops = { .get_link = ethtool_op_get_link, - .get_settings = tsi108_get_settings, - .set_settings = tsi108_set_settings, + .get_link_ksettings = tsi108_get_link_ksettings, + .set_link_ksettings = tsi108_set_link_ksettings, }; static const struct net_device_ops tsi108_netdev_ops = { -- cgit From 8704f21c8449d4ee0ea22a16014b6af6e757ec81 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Tue, 7 Mar 2017 23:32:25 +0100 Subject: net: intel: ixgbe: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 168 ++++++++++++----------- 1 file changed, 91 insertions(+), 77 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 90fa5bf23d1b..0da0752fedef 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -186,60 +186,62 @@ static u32 ixgbe_get_supported_10gtypes(struct ixgbe_hw *hw) } } -static int ixgbe_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int ixgbe_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; ixgbe_link_speed supported_link; bool autoneg = false; + u32 supported, advertising; + + ethtool_convert_link_mode_to_legacy_u32(&supported, + cmd->link_modes.supported); hw->mac.ops.get_link_capabilities(hw, &supported_link, &autoneg); /* set the supported link speeds */ if (supported_link & IXGBE_LINK_SPEED_10GB_FULL) - ecmd->supported |= ixgbe_get_supported_10gtypes(hw); + supported |= ixgbe_get_supported_10gtypes(hw); if (supported_link & IXGBE_LINK_SPEED_1GB_FULL) - ecmd->supported |= (ixgbe_isbackplane(hw->phy.media_type)) ? + supported |= (ixgbe_isbackplane(hw->phy.media_type)) ? SUPPORTED_1000baseKX_Full : SUPPORTED_1000baseT_Full; if (supported_link & IXGBE_LINK_SPEED_100_FULL) - ecmd->supported |= SUPPORTED_100baseT_Full; + supported |= SUPPORTED_100baseT_Full; if (supported_link & IXGBE_LINK_SPEED_10_FULL) - ecmd->supported |= SUPPORTED_10baseT_Full; + supported |= SUPPORTED_10baseT_Full; /* default advertised speed if phy.autoneg_advertised isn't set */ - ecmd->advertising = ecmd->supported; + advertising = supported; /* set the advertised speeds */ if (hw->phy.autoneg_advertised) { - ecmd->advertising = 0; + advertising = 0; if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10_FULL) - ecmd->advertising |= ADVERTISED_10baseT_Full; + advertising |= ADVERTISED_10baseT_Full; if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL) - ecmd->advertising |= ADVERTISED_100baseT_Full; + advertising |= ADVERTISED_100baseT_Full; if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL) - ecmd->advertising |= ecmd->supported & ADVRTSD_MSK_10G; + advertising |= supported & ADVRTSD_MSK_10G; if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) { - if (ecmd->supported & SUPPORTED_1000baseKX_Full) - ecmd->advertising |= ADVERTISED_1000baseKX_Full; + if (supported & SUPPORTED_1000baseKX_Full) + advertising |= ADVERTISED_1000baseKX_Full; else - ecmd->advertising |= ADVERTISED_1000baseT_Full; + advertising |= ADVERTISED_1000baseT_Full; } } else { if (hw->phy.multispeed_fiber && !autoneg) { if (supported_link & IXGBE_LINK_SPEED_10GB_FULL) - ecmd->advertising = ADVERTISED_10000baseT_Full; + advertising = ADVERTISED_10000baseT_Full; } } if (autoneg) { - ecmd->supported |= SUPPORTED_Autoneg; - ecmd->advertising |= ADVERTISED_Autoneg; - ecmd->autoneg = AUTONEG_ENABLE; + supported |= SUPPORTED_Autoneg; + advertising |= ADVERTISED_Autoneg; + cmd->base.autoneg = AUTONEG_ENABLE; } else - ecmd->autoneg = AUTONEG_DISABLE; - - ecmd->transceiver = XCVR_EXTERNAL; + cmd->base.autoneg = AUTONEG_DISABLE; /* Determine the remaining settings based on the PHY type. */ switch (adapter->hw.phy.type) { @@ -248,14 +250,14 @@ static int ixgbe_get_settings(struct net_device *netdev, case ixgbe_phy_x550em_ext_t: case ixgbe_phy_fw: case ixgbe_phy_cu_unknown: - ecmd->supported |= SUPPORTED_TP; - ecmd->advertising |= ADVERTISED_TP; - ecmd->port = PORT_TP; + supported |= SUPPORTED_TP; + advertising |= ADVERTISED_TP; + cmd->base.port = PORT_TP; break; case ixgbe_phy_qt: - ecmd->supported |= SUPPORTED_FIBRE; - ecmd->advertising |= ADVERTISED_FIBRE; - ecmd->port = PORT_FIBRE; + supported |= SUPPORTED_FIBRE; + advertising |= ADVERTISED_FIBRE; + cmd->base.port = PORT_FIBRE; break; case ixgbe_phy_nl: case ixgbe_phy_sfp_passive_tyco: @@ -273,9 +275,9 @@ static int ixgbe_get_settings(struct net_device *netdev, case ixgbe_sfp_type_da_cu: case ixgbe_sfp_type_da_cu_core0: case ixgbe_sfp_type_da_cu_core1: - ecmd->supported |= SUPPORTED_FIBRE; - ecmd->advertising |= ADVERTISED_FIBRE; - ecmd->port = PORT_DA; + supported |= SUPPORTED_FIBRE; + advertising |= ADVERTISED_FIBRE; + cmd->base.port = PORT_DA; break; case ixgbe_sfp_type_sr: case ixgbe_sfp_type_lr: @@ -285,102 +287,113 @@ static int ixgbe_get_settings(struct net_device *netdev, case ixgbe_sfp_type_1g_sx_core1: case ixgbe_sfp_type_1g_lx_core0: case ixgbe_sfp_type_1g_lx_core1: - ecmd->supported |= SUPPORTED_FIBRE; - ecmd->advertising |= ADVERTISED_FIBRE; - ecmd->port = PORT_FIBRE; + supported |= SUPPORTED_FIBRE; + advertising |= ADVERTISED_FIBRE; + cmd->base.port = PORT_FIBRE; break; case ixgbe_sfp_type_not_present: - ecmd->supported |= SUPPORTED_FIBRE; - ecmd->advertising |= ADVERTISED_FIBRE; - ecmd->port = PORT_NONE; + supported |= SUPPORTED_FIBRE; + advertising |= ADVERTISED_FIBRE; + cmd->base.port = PORT_NONE; break; case ixgbe_sfp_type_1g_cu_core0: case ixgbe_sfp_type_1g_cu_core1: - ecmd->supported |= SUPPORTED_TP; - ecmd->advertising |= ADVERTISED_TP; - ecmd->port = PORT_TP; + supported |= SUPPORTED_TP; + advertising |= ADVERTISED_TP; + cmd->base.port = PORT_TP; break; case ixgbe_sfp_type_unknown: default: - ecmd->supported |= SUPPORTED_FIBRE; - ecmd->advertising |= ADVERTISED_FIBRE; - ecmd->port = PORT_OTHER; + supported |= SUPPORTED_FIBRE; + advertising |= ADVERTISED_FIBRE; + cmd->base.port = PORT_OTHER; break; } break; case ixgbe_phy_xaui: - ecmd->supported |= SUPPORTED_FIBRE; - ecmd->advertising |= ADVERTISED_FIBRE; - ecmd->port = PORT_NONE; + supported |= SUPPORTED_FIBRE; + advertising |= ADVERTISED_FIBRE; + cmd->base.port = PORT_NONE; break; case ixgbe_phy_unknown: case ixgbe_phy_generic: case ixgbe_phy_sfp_unsupported: default: - ecmd->supported |= SUPPORTED_FIBRE; - ecmd->advertising |= ADVERTISED_FIBRE; - ecmd->port = PORT_OTHER; + supported |= SUPPORTED_FIBRE; + advertising |= ADVERTISED_FIBRE; + cmd->base.port = PORT_OTHER; break; } /* Indicate pause support */ - ecmd->supported |= SUPPORTED_Pause; + supported |= SUPPORTED_Pause; switch (hw->fc.requested_mode) { case ixgbe_fc_full: - ecmd->advertising |= ADVERTISED_Pause; + advertising |= ADVERTISED_Pause; break; case ixgbe_fc_rx_pause: - ecmd->advertising |= ADVERTISED_Pause | + advertising |= ADVERTISED_Pause | ADVERTISED_Asym_Pause; break; case ixgbe_fc_tx_pause: - ecmd->advertising |= ADVERTISED_Asym_Pause; + advertising |= ADVERTISED_Asym_Pause; break; default: - ecmd->advertising &= ~(ADVERTISED_Pause | + advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); } if (netif_carrier_ok(netdev)) { switch (adapter->link_speed) { case IXGBE_LINK_SPEED_10GB_FULL: - ethtool_cmd_speed_set(ecmd, SPEED_10000); + cmd->base.speed = SPEED_10000; break; case IXGBE_LINK_SPEED_5GB_FULL: - ethtool_cmd_speed_set(ecmd, SPEED_5000); + cmd->base.speed = SPEED_5000; break; case IXGBE_LINK_SPEED_2_5GB_FULL: - ethtool_cmd_speed_set(ecmd, SPEED_2500); + cmd->base.speed = SPEED_2500; break; case IXGBE_LINK_SPEED_1GB_FULL: - ethtool_cmd_speed_set(ecmd, SPEED_1000); + cmd->base.speed = SPEED_1000; break; case IXGBE_LINK_SPEED_100_FULL: - ethtool_cmd_speed_set(ecmd, SPEED_100); + cmd->base.speed = SPEED_100; break; case IXGBE_LINK_SPEED_10_FULL: - ethtool_cmd_speed_set(ecmd, SPEED_10); + cmd->base.speed = SPEED_10; break; default: break; } - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; } else { - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; } + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); + return 0; } -static int ixgbe_set_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int ixgbe_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; u32 advertised, old; s32 err = 0; + u32 supported, advertising; + + ethtool_convert_link_mode_to_legacy_u32(&supported, + cmd->link_modes.supported); + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); if ((hw->phy.media_type == ixgbe_media_type_copper) || (hw->phy.multispeed_fiber)) { @@ -388,12 +401,12 @@ static int ixgbe_set_settings(struct net_device *netdev, * this function does not support duplex forcing, but can * limit the advertising of the adapter to the specified speed */ - if (ecmd->advertising & ~ecmd->supported) + if (advertising & ~supported) return -EINVAL; /* only allow one speed at a time if no autoneg */ - if (!ecmd->autoneg && hw->phy.multispeed_fiber) { - if (ecmd->advertising == + if (!cmd->base.autoneg && hw->phy.multispeed_fiber) { + if (advertising == (ADVERTISED_10000baseT_Full | ADVERTISED_1000baseT_Full)) return -EINVAL; @@ -401,16 +414,16 @@ static int ixgbe_set_settings(struct net_device *netdev, old = hw->phy.autoneg_advertised; advertised = 0; - if (ecmd->advertising & ADVERTISED_10000baseT_Full) + if (advertising & ADVERTISED_10000baseT_Full) advertised |= IXGBE_LINK_SPEED_10GB_FULL; - if (ecmd->advertising & ADVERTISED_1000baseT_Full) + if (advertising & ADVERTISED_1000baseT_Full) advertised |= IXGBE_LINK_SPEED_1GB_FULL; - if (ecmd->advertising & ADVERTISED_100baseT_Full) + if (advertising & ADVERTISED_100baseT_Full) advertised |= IXGBE_LINK_SPEED_100_FULL; - if (ecmd->advertising & ADVERTISED_10baseT_Full) + if (advertising & ADVERTISED_10baseT_Full) advertised |= IXGBE_LINK_SPEED_10_FULL; if (old == advertised) @@ -428,10 +441,11 @@ static int ixgbe_set_settings(struct net_device *netdev, clear_bit(__IXGBE_IN_SFP_INIT, &adapter->state); } else { /* in this case we currently only support 10Gb/FULL */ - u32 speed = ethtool_cmd_speed(ecmd); - if ((ecmd->autoneg == AUTONEG_ENABLE) || - (ecmd->advertising != ADVERTISED_10000baseT_Full) || - (speed + ecmd->duplex != SPEED_10000 + DUPLEX_FULL)) + u32 speed = cmd->base.speed; + + if ((cmd->base.autoneg == AUTONEG_ENABLE) || + (advertising != ADVERTISED_10000baseT_Full) || + (speed + cmd->base.duplex != SPEED_10000 + DUPLEX_FULL)) return -EINVAL; } @@ -3402,8 +3416,6 @@ static int ixgbe_set_priv_flags(struct net_device *netdev, u32 priv_flags) } static const struct ethtool_ops ixgbe_ethtool_ops = { - .get_settings = ixgbe_get_settings, - .set_settings = ixgbe_set_settings, .get_drvinfo = ixgbe_get_drvinfo, .get_regs_len = ixgbe_get_regs_len, .get_regs = ixgbe_get_regs, @@ -3442,6 +3454,8 @@ static const struct ethtool_ops ixgbe_ethtool_ops = { .get_ts_info = ixgbe_get_ts_info, .get_module_info = ixgbe_get_module_info, .get_module_eeprom = ixgbe_get_module_eeprom, + .get_link_ksettings = ixgbe_get_link_ksettings, + .set_link_ksettings = ixgbe_set_link_ksettings, }; void ixgbe_set_ethtool_ops(struct net_device *netdev) -- cgit From f918b9861f222bae04747fe0ec804d1825337f8e Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Tue, 7 Mar 2017 23:46:16 +0100 Subject: net: via: via-rhine: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/via/via-rhine.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c index c068c58428f7..4cf41f779d0e 100644 --- a/drivers/net/ethernet/via/via-rhine.c +++ b/drivers/net/ethernet/via/via-rhine.c @@ -2303,25 +2303,27 @@ static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *i strlcpy(info->bus_info, dev_name(hwdev), sizeof(info->bus_info)); } -static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int netdev_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct rhine_private *rp = netdev_priv(dev); int rc; mutex_lock(&rp->task_lock); - rc = mii_ethtool_gset(&rp->mii_if, cmd); + rc = mii_ethtool_get_link_ksettings(&rp->mii_if, cmd); mutex_unlock(&rp->task_lock); return rc; } -static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int netdev_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct rhine_private *rp = netdev_priv(dev); int rc; mutex_lock(&rp->task_lock); - rc = mii_ethtool_sset(&rp->mii_if, cmd); + rc = mii_ethtool_set_link_ksettings(&rp->mii_if, cmd); rhine_set_carrier(&rp->mii_if); mutex_unlock(&rp->task_lock); @@ -2391,14 +2393,14 @@ static int rhine_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) static const struct ethtool_ops netdev_ethtool_ops = { .get_drvinfo = netdev_get_drvinfo, - .get_settings = netdev_get_settings, - .set_settings = netdev_set_settings, .nway_reset = netdev_nway_reset, .get_link = netdev_get_link, .get_msglevel = netdev_get_msglevel, .set_msglevel = netdev_set_msglevel, .get_wol = rhine_get_wol, .set_wol = rhine_set_wol, + .get_link_ksettings = netdev_get_link_ksettings, + .set_link_ksettings = netdev_set_link_ksettings, }; static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -- cgit From 92f4a13efd8b2ab263e589e8bb814ef49f4fc6f9 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Wed, 8 Mar 2017 22:20:20 +0100 Subject: net: via: via-velocity: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/via/via-velocity.c | 62 +++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c index d088788b27a7..ef9538ee53d0 100644 --- a/drivers/net/ethernet/via/via-velocity.c +++ b/drivers/net/ethernet/via/via-velocity.c @@ -3291,15 +3291,17 @@ static void velocity_ethtool_down(struct net_device *dev) velocity_set_power_state(vptr, PCI_D3hot); } -static int velocity_get_settings(struct net_device *dev, - struct ethtool_cmd *cmd) +static int velocity_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct velocity_info *vptr = netdev_priv(dev); struct mac_regs __iomem *regs = vptr->mac_regs; u32 status; + u32 supported, advertising; + status = check_connection_type(vptr->mac_regs); - cmd->supported = SUPPORTED_TP | + supported = SUPPORTED_TP | SUPPORTED_Autoneg | SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | @@ -3308,9 +3310,9 @@ static int velocity_get_settings(struct net_device *dev, SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full; - cmd->advertising = ADVERTISED_TP | ADVERTISED_Autoneg; + advertising = ADVERTISED_TP | ADVERTISED_Autoneg; if (vptr->options.spd_dpx == SPD_DPX_AUTO) { - cmd->advertising |= + advertising |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | @@ -3320,19 +3322,19 @@ static int velocity_get_settings(struct net_device *dev, } else { switch (vptr->options.spd_dpx) { case SPD_DPX_1000_FULL: - cmd->advertising |= ADVERTISED_1000baseT_Full; + advertising |= ADVERTISED_1000baseT_Full; break; case SPD_DPX_100_HALF: - cmd->advertising |= ADVERTISED_100baseT_Half; + advertising |= ADVERTISED_100baseT_Half; break; case SPD_DPX_100_FULL: - cmd->advertising |= ADVERTISED_100baseT_Full; + advertising |= ADVERTISED_100baseT_Full; break; case SPD_DPX_10_HALF: - cmd->advertising |= ADVERTISED_10baseT_Half; + advertising |= ADVERTISED_10baseT_Half; break; case SPD_DPX_10_FULL: - cmd->advertising |= ADVERTISED_10baseT_Full; + advertising |= ADVERTISED_10baseT_Full; break; default: break; @@ -3340,30 +3342,35 @@ static int velocity_get_settings(struct net_device *dev, } if (status & VELOCITY_SPEED_1000) - ethtool_cmd_speed_set(cmd, SPEED_1000); + cmd->base.speed = SPEED_1000; else if (status & VELOCITY_SPEED_100) - ethtool_cmd_speed_set(cmd, SPEED_100); + cmd->base.speed = SPEED_100; else - ethtool_cmd_speed_set(cmd, SPEED_10); + cmd->base.speed = SPEED_10; - cmd->autoneg = (status & VELOCITY_AUTONEG_ENABLE) ? AUTONEG_ENABLE : AUTONEG_DISABLE; - cmd->port = PORT_TP; - cmd->transceiver = XCVR_INTERNAL; - cmd->phy_address = readb(®s->MIIADR) & 0x1F; + cmd->base.autoneg = (status & VELOCITY_AUTONEG_ENABLE) ? + AUTONEG_ENABLE : AUTONEG_DISABLE; + cmd->base.port = PORT_TP; + cmd->base.phy_address = readb(®s->MIIADR) & 0x1F; if (status & VELOCITY_DUPLEX_FULL) - cmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; else - cmd->duplex = DUPLEX_HALF; + cmd->base.duplex = DUPLEX_HALF; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return 0; } -static int velocity_set_settings(struct net_device *dev, - struct ethtool_cmd *cmd) +static int velocity_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct velocity_info *vptr = netdev_priv(dev); - u32 speed = ethtool_cmd_speed(cmd); + u32 speed = cmd->base.speed; u32 curr_status; u32 new_status = 0; int ret = 0; @@ -3371,11 +3378,12 @@ static int velocity_set_settings(struct net_device *dev, curr_status = check_connection_type(vptr->mac_regs); curr_status &= (~VELOCITY_LINK_FAIL); - new_status |= ((cmd->autoneg) ? VELOCITY_AUTONEG_ENABLE : 0); + new_status |= ((cmd->base.autoneg) ? VELOCITY_AUTONEG_ENABLE : 0); new_status |= ((speed == SPEED_1000) ? VELOCITY_SPEED_1000 : 0); new_status |= ((speed == SPEED_100) ? VELOCITY_SPEED_100 : 0); new_status |= ((speed == SPEED_10) ? VELOCITY_SPEED_10 : 0); - new_status |= ((cmd->duplex == DUPLEX_FULL) ? VELOCITY_DUPLEX_FULL : 0); + new_status |= ((cmd->base.duplex == DUPLEX_FULL) ? + VELOCITY_DUPLEX_FULL : 0); if ((new_status & VELOCITY_AUTONEG_ENABLE) && (new_status != (curr_status | VELOCITY_AUTONEG_ENABLE))) { @@ -3644,8 +3652,6 @@ static void velocity_get_ethtool_stats(struct net_device *dev, } static const struct ethtool_ops velocity_ethtool_ops = { - .get_settings = velocity_get_settings, - .set_settings = velocity_set_settings, .get_drvinfo = velocity_get_drvinfo, .get_wol = velocity_ethtool_get_wol, .set_wol = velocity_ethtool_set_wol, @@ -3658,7 +3664,9 @@ static const struct ethtool_ops velocity_ethtool_ops = { .get_coalesce = velocity_get_coalesce, .set_coalesce = velocity_set_coalesce, .begin = velocity_ethtool_up, - .complete = velocity_ethtool_down + .complete = velocity_ethtool_down, + .get_link_ksettings = velocity_get_link_ksettings, + .set_link_ksettings = velocity_set_link_ksettings, }; #if defined(CONFIG_PM) && defined(CONFIG_INET) -- cgit From 031e51445a065d94639faf1581d0377438a33b96 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Wed, 8 Mar 2017 23:16:11 +0100 Subject: net: fjes: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/fjes/fjes_ethtool.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/net/fjes/fjes_ethtool.c b/drivers/net/fjes/fjes_ethtool.c index 6575f880f1be..7d101714c2ef 100644 --- a/drivers/net/fjes/fjes_ethtool.c +++ b/drivers/net/fjes/fjes_ethtool.c @@ -175,16 +175,15 @@ static void fjes_get_drvinfo(struct net_device *netdev, "platform:%s", plat_dev->name); } -static int fjes_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int fjes_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *ecmd) { - ecmd->supported = 0; - ecmd->advertising = 0; - ecmd->duplex = DUPLEX_FULL; - ecmd->autoneg = AUTONEG_DISABLE; - ecmd->transceiver = XCVR_DUMMY1; - ecmd->port = PORT_NONE; - ethtool_cmd_speed_set(ecmd, 20000); /* 20Gb/s */ + ethtool_link_ksettings_zero_link_mode(ecmd, supported); + ethtool_link_ksettings_zero_link_mode(ecmd, advertising); + ecmd->base.duplex = DUPLEX_FULL; + ecmd->base.autoneg = AUTONEG_DISABLE; + ecmd->base.port = PORT_NONE; + ecmd->base.speed = 20000; /* 20Gb/s */ return 0; } @@ -296,7 +295,6 @@ static int fjes_get_dump_data(struct net_device *netdev, } static const struct ethtool_ops fjes_ethtool_ops = { - .get_settings = fjes_get_settings, .get_drvinfo = fjes_get_drvinfo, .get_ethtool_stats = fjes_get_ethtool_stats, .get_strings = fjes_get_strings, @@ -306,6 +304,7 @@ static const struct ethtool_ops fjes_ethtool_ops = { .set_dump = fjes_set_dump, .get_dump_flag = fjes_get_dump_flag, .get_dump_data = fjes_get_dump_data, + .get_link_ksettings = fjes_get_link_ksettings, }; void fjes_set_ethtool_ops(struct net_device *netdev) -- cgit From 5e8456fdd23c23bbfa06e0d647c90758102a4410 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Wed, 8 Mar 2017 23:41:04 +0100 Subject: net: hyperv: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Tested-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 617dd90803c9..b12808ab3432 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -789,18 +789,19 @@ static int netvsc_set_channels(struct net_device *net, return ret; } -static bool netvsc_validate_ethtool_ss_cmd(const struct ethtool_cmd *cmd) +static bool +netvsc_validate_ethtool_ss_cmd(const struct ethtool_link_ksettings *cmd) { - struct ethtool_cmd diff1 = *cmd; - struct ethtool_cmd diff2 = {}; + struct ethtool_link_ksettings diff1 = *cmd; + struct ethtool_link_ksettings diff2 = {}; - ethtool_cmd_speed_set(&diff1, 0); - diff1.duplex = 0; + diff1.base.speed = 0; + diff1.base.duplex = 0; /* advertising and cmd are usually set */ - diff1.advertising = 0; - diff1.cmd = 0; + ethtool_link_ksettings_zero_link_mode(&diff1, advertising); + diff1.base.cmd = 0; /* We set port to PORT_OTHER */ - diff2.port = PORT_OTHER; + diff2.base.port = PORT_OTHER; return !memcmp(&diff1, &diff2, sizeof(diff1)); } @@ -813,30 +814,32 @@ static void netvsc_init_settings(struct net_device *dev) ndc->duplex = DUPLEX_UNKNOWN; } -static int netvsc_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int netvsc_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct net_device_context *ndc = netdev_priv(dev); - ethtool_cmd_speed_set(cmd, ndc->speed); - cmd->duplex = ndc->duplex; - cmd->port = PORT_OTHER; + cmd->base.speed = ndc->speed; + cmd->base.duplex = ndc->duplex; + cmd->base.port = PORT_OTHER; return 0; } -static int netvsc_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int netvsc_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct net_device_context *ndc = netdev_priv(dev); u32 speed; - speed = ethtool_cmd_speed(cmd); + speed = cmd->base.speed; if (!ethtool_validate_speed(speed) || - !ethtool_validate_duplex(cmd->duplex) || + !ethtool_validate_duplex(cmd->base.duplex) || !netvsc_validate_ethtool_ss_cmd(cmd)) return -EINVAL; ndc->speed = speed; - ndc->duplex = cmd->duplex; + ndc->duplex = cmd->base.duplex; return 0; } @@ -1170,13 +1173,13 @@ static const struct ethtool_ops ethtool_ops = { .get_channels = netvsc_get_channels, .set_channels = netvsc_set_channels, .get_ts_info = ethtool_op_get_ts_info, - .get_settings = netvsc_get_settings, - .set_settings = netvsc_set_settings, .get_rxnfc = netvsc_get_rxnfc, .get_rxfh_key_size = netvsc_get_rxfh_key_size, .get_rxfh_indir_size = netvsc_rss_indir_size, .get_rxfh = netvsc_get_rxfh, .set_rxfh = netvsc_set_rxfh, + .get_link_ksettings = netvsc_get_link_ksettings, + .set_link_ksettings = netvsc_set_link_ksettings, }; static const struct net_device_ops device_ops = { -- cgit From 49b499718fa1b0d639663cfd03085b9bfd23cdc8 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 8 Mar 2017 16:03:32 +0100 Subject: net: sched: make default fifo qdiscs appear in the dump The original reason [1] for having hidden qdiscs (potential scalability issues in qdisc_match_from_root() with single linked list in case of large amount of qdiscs) has been invalidated by 59cc1f61f0 ("net: sched: convert qdisc linked list to hashtable"). This allows us for bringing more clarity and determinism into the dump by making default pfifo qdiscs visible. We're not turning this on by default though, at it was deemed [2] too intrusive / unnecessary change of default behavior towards userspace. Instead, TCA_DUMP_INVISIBLE netlink attribute is introduced, which allows applications to request complete qdisc hierarchy dump, including the ones that have always been implicit/invisible. Singleton noop_qdisc stays invisible, as teaching the whole infrastructure about singletons would require quite some surgery with very little gain (seeing no qdisc or seeing noop qdisc in the dump is probably setting the same user expectation). [1] http://lkml.kernel.org/r/1460732328.10638.74.camel@edumazet-glaptop3.roam.corp.google.com [2] http://lkml.kernel.org/r/20161021.105935.1907696543877061916.davem@davemloft.net Signed-off-by: Jiri Kosina Signed-off-by: David S. Miller --- include/net/pkt_sched.h | 2 +- include/net/sch_generic.h | 1 + include/uapi/linux/rtnetlink.h | 1 + net/sched/sch_api.c | 42 ++++++++++++++++++++++++++++++------------ net/sched/sch_cbq.c | 5 +++++ net/sched/sch_drr.c | 2 ++ net/sched/sch_dsmark.c | 2 ++ net/sched/sch_generic.c | 2 +- net/sched/sch_hfsc.c | 4 ++++ net/sched/sch_htb.c | 2 ++ net/sched/sch_mq.c | 2 +- net/sched/sch_mqprio.c | 2 +- net/sched/sch_multiq.c | 2 ++ net/sched/sch_prio.c | 5 ++++- net/sched/sch_qfq.c | 2 ++ net/sched/sch_red.c | 2 ++ net/sched/sch_sfb.c | 2 ++ net/sched/sch_tbf.c | 2 ++ 18 files changed, 65 insertions(+), 17 deletions(-) diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index f1b76b8e6d2d..bec46f63f10c 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -92,7 +92,7 @@ int unregister_qdisc(struct Qdisc_ops *qops); void qdisc_get_default(char *id, size_t len); int qdisc_set_default(const char *id); -void qdisc_hash_add(struct Qdisc *q); +void qdisc_hash_add(struct Qdisc *q, bool invisible); void qdisc_hash_del(struct Qdisc *q); struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle); struct Qdisc *qdisc_lookup_class(struct net_device *dev, u32 handle); diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index aeec4086afb2..65d502610314 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -66,6 +66,7 @@ struct Qdisc { #define TCQ_F_NOPARENT 0x40 /* root of its hierarchy : * qdisc_tree_decrease_qlen() should stop. */ +#define TCQ_F_INVISIBLE 0x80 /* invisible by default in dump */ u32 limit; const struct Qdisc_ops *ops; struct qdisc_size_table __rcu *stab; diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 6546917d605a..75fcf5eff093 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -545,6 +545,7 @@ enum { TCA_STATS2, TCA_STAB, TCA_PAD, + TCA_DUMP_INVISIBLE, __TCA_MAX }; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index bcf49cd22786..62567bfe52c7 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -274,7 +274,7 @@ static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle) return NULL; } -void qdisc_hash_add(struct Qdisc *q) +void qdisc_hash_add(struct Qdisc *q, bool invisible) { if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) { struct Qdisc *root = qdisc_dev(q)->qdisc; @@ -282,6 +282,8 @@ void qdisc_hash_add(struct Qdisc *q) WARN_ON_ONCE(root == &noop_qdisc); ASSERT_RTNL(); hash_add_rcu(qdisc_dev(q)->qdisc_hash, &q->hash, q->handle); + if (invisible) + q->flags |= TCQ_F_INVISIBLE; } } EXPORT_SYMBOL(qdisc_hash_add); @@ -1003,7 +1005,7 @@ static struct Qdisc *qdisc_create(struct net_device *dev, goto err_out4; } - qdisc_hash_add(sch); + qdisc_hash_add(sch, false); return sch; } @@ -1401,9 +1403,14 @@ nla_put_failure: return -1; } -static bool tc_qdisc_dump_ignore(struct Qdisc *q) +static bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible) { - return (q->flags & TCQ_F_BUILTIN) ? true : false; + if (q->flags & TCQ_F_BUILTIN) + return true; + if ((q->flags & TCQ_F_INVISIBLE) && !dump_invisible) + return true; + + return false; } static int qdisc_notify(struct net *net, struct sk_buff *oskb, @@ -1417,12 +1424,12 @@ static int qdisc_notify(struct net *net, struct sk_buff *oskb, if (!skb) return -ENOBUFS; - if (old && !tc_qdisc_dump_ignore(old)) { + if (old && !tc_qdisc_dump_ignore(old, false)) { if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq, 0, RTM_DELQDISC) < 0) goto err_out; } - if (new && !tc_qdisc_dump_ignore(new)) { + if (new && !tc_qdisc_dump_ignore(new, false)) { if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq, old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0) goto err_out; @@ -1439,7 +1446,8 @@ err_out: static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, struct netlink_callback *cb, - int *q_idx_p, int s_q_idx, bool recur) + int *q_idx_p, int s_q_idx, bool recur, + bool dump_invisible) { int ret = 0, q_idx = *q_idx_p; struct Qdisc *q; @@ -1452,7 +1460,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, if (q_idx < s_q_idx) { q_idx++; } else { - if (!tc_qdisc_dump_ignore(q) && + if (!tc_qdisc_dump_ignore(q, dump_invisible) && tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) @@ -1474,7 +1482,7 @@ static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, q_idx++; continue; } - if (!tc_qdisc_dump_ignore(q) && + if (!tc_qdisc_dump_ignore(q, dump_invisible) && tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) @@ -1496,12 +1504,21 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) int idx, q_idx; int s_idx, s_q_idx; struct net_device *dev; + const struct nlmsghdr *nlh = cb->nlh; + struct tcmsg *tcm = nlmsg_data(nlh); + struct nlattr *tca[TCA_MAX + 1]; + int err; s_idx = cb->args[0]; s_q_idx = q_idx = cb->args[1]; idx = 0; ASSERT_RTNL(); + + err = nlmsg_parse(nlh, sizeof(*tcm), tca, TCA_MAX, NULL); + if (err < 0) + return err; + for_each_netdev(net, dev) { struct netdev_queue *dev_queue; @@ -1512,13 +1529,14 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) q_idx = 0; if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx, - true) < 0) + true, tca[TCA_DUMP_INVISIBLE]) < 0) goto done; dev_queue = dev_ingress_queue(dev); if (dev_queue && tc_dump_qdisc_root(dev_queue->qdisc_sleeping, skb, cb, - &q_idx, s_q_idx, false) < 0) + &q_idx, s_q_idx, false, + tca[TCA_DUMP_INVISIBLE]) < 0) goto done; cont: @@ -1762,7 +1780,7 @@ static int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb, { struct qdisc_dump_args arg; - if (tc_qdisc_dump_ignore(q) || + if (tc_qdisc_dump_ignore(q, false) || *t_p < s_t || !q->ops->cl_ops || (tcm->tcm_parent && TC_H_MAJ(tcm->tcm_parent) != q->handle)) { diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index d6ca18dc04c3..cf93e5ff3d63 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1161,6 +1161,8 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt) sch->handle); if (!q->link.q) q->link.q = &noop_qdisc; + else + qdisc_hash_add(q->link.q, true); q->link.priority = TC_CBQ_MAXPRIO - 1; q->link.priority2 = TC_CBQ_MAXPRIO - 1; @@ -1600,6 +1602,9 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t cl->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid); if (!cl->q) cl->q = &noop_qdisc; + else + qdisc_hash_add(cl->q, true); + cl->common.classid = classid; cl->tparent = parent; cl->qdisc = sch; diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index bb4cbdf75004..9fe67e257dfa 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -117,6 +117,8 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid, &pfifo_qdisc_ops, classid); if (cl->qdisc == NULL) cl->qdisc = &noop_qdisc; + else + qdisc_hash_add(cl->qdisc, true); if (tca[TCA_RATE]) { err = gen_replace_estimator(&cl->bstats, NULL, &cl->rate_est, diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index 802ac7c2e5e8..1b98cb2160ff 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -368,6 +368,8 @@ static int dsmark_init(struct Qdisc *sch, struct nlattr *opt) p->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, sch->handle); if (p->q == NULL) p->q = &noop_qdisc; + else + qdisc_hash_add(p->q, true); pr_debug("%s: qdisc %p\n", __func__, p->q); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index b052b27a984e..3e64d23e098c 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -795,7 +795,7 @@ static void attach_default_qdiscs(struct net_device *dev) } #ifdef CONFIG_NET_SCHED if (dev->qdisc) - qdisc_hash_add(dev->qdisc); + qdisc_hash_add(dev->qdisc, false); #endif } diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 3ffaa6fb0990..0198c6cdda49 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1066,6 +1066,8 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid, &pfifo_qdisc_ops, classid); if (cl->qdisc == NULL) cl->qdisc = &noop_qdisc; + else + qdisc_hash_add(cl->qdisc, true); INIT_LIST_HEAD(&cl->children); cl->vt_tree = RB_ROOT; cl->cf_tree = RB_ROOT; @@ -1425,6 +1427,8 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt) sch->handle); if (q->root.qdisc == NULL) q->root.qdisc = &noop_qdisc; + else + qdisc_hash_add(q->root.qdisc, true); INIT_LIST_HEAD(&q->root.children); q->root.vt_tree = RB_ROOT; q->root.cf_tree = RB_ROOT; diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 4cd5fb134bc9..95867033542e 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1460,6 +1460,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, qdisc_class_hash_insert(&q->clhash, &cl->common); if (parent) parent->children++; + if (cl->un.leaf.q != &noop_qdisc) + qdisc_hash_add(cl->un.leaf.q, true); } else { if (tca[TCA_RATE]) { err = gen_replace_estimator(&cl->bstats, NULL, diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c index 20b7f1646f69..cadfdd4f1e52 100644 --- a/net/sched/sch_mq.c +++ b/net/sched/sch_mq.c @@ -84,7 +84,7 @@ static void mq_attach(struct Qdisc *sch) qdisc_destroy(old); #ifdef CONFIG_NET_SCHED if (ntx < dev->real_num_tx_queues) - qdisc_hash_add(qdisc); + qdisc_hash_add(qdisc, false); #endif } diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 922683418e53..b851e209da4d 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -175,7 +175,7 @@ static void mqprio_attach(struct Qdisc *sch) if (old) qdisc_destroy(old); if (ntx < dev->real_num_tx_queues) - qdisc_hash_add(qdisc); + qdisc_hash_add(qdisc, false); } kfree(priv->qdiscs); priv->qdiscs = NULL; diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index e7839a0d0eaa..43a3a10b3c81 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -217,6 +217,8 @@ static int multiq_tune(struct Qdisc *sch, struct nlattr *opt) sch_tree_lock(sch); old = q->queues[i]; q->queues[i] = child; + if (child != &noop_qdisc) + qdisc_hash_add(child, true); if (old != &noop_qdisc) { qdisc_tree_reduce_backlog(old, diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index d4d7db267b6e..92c2e6d448d7 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -192,8 +192,11 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt) qdisc_destroy(child); } - for (i = oldbands; i < q->bands; i++) + for (i = oldbands; i < q->bands; i++) { q->queues[i] = queues[i]; + if (q->queues[i] != &noop_qdisc) + qdisc_hash_add(q->queues[i], true); + } sch_tree_unlock(sch); return 0; diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index f9e712ce2d15..6c85f3e9239b 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -494,6 +494,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, goto destroy_class; } + if (cl->qdisc != &noop_qdisc) + qdisc_hash_add(cl->qdisc, true); sch_tree_lock(sch); qdisc_class_hash_insert(&q->clhash, &cl->common); sch_tree_unlock(sch); diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index 249b2a18acbd..799ea6dd69b2 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -191,6 +191,8 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt) return PTR_ERR(child); } + if (child != &noop_qdisc) + qdisc_hash_add(child, true); sch_tree_lock(sch); q->flags = ctl->flags; q->limit = ctl->limit; diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index fe6963d21519..ae862f172c94 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -513,6 +513,8 @@ static int sfb_change(struct Qdisc *sch, struct nlattr *opt) if (IS_ERR(child)) return PTR_ERR(child); + if (child != &noop_qdisc) + qdisc_hash_add(child, true); sch_tree_lock(sch); qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen, diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 303355c449ab..40c29a801391 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -396,6 +396,8 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt) q->qdisc->qstats.backlog); qdisc_destroy(q->qdisc); q->qdisc = child; + if (child != &noop_qdisc); + qdisc_hash_add(child, true); } q->limit = qopt->limit; if (tb[TCA_TBF_PBURST]) -- cgit From 5425077d73e0c8e7e9267ca8397cc0e2293c1fb9 Mon Sep 17 00:00:00 2001 From: "subashab@codeaurora.org" Date: Wed, 8 Mar 2017 16:36:49 -0700 Subject: net: ipv6: Add early demux handler for UDP unicast While running a single stream UDPv6 test, we observed that amount of CPU spent in NET_RX softirq was much greater than UDPv4 for an equivalent receive rate. The test here was run on an ARM64 based Android system. On further analysis with perf, we found that UDPv6 was spending significant time in the statistics netfilter targets which did socket lookup per packet. These statistics rules perform a lookup when there is no socket associated with the skb. Since there are multiple instances of these rules based on UID, there will be equal number of lookups per skb. By introducing early demux for UDPv6, we avoid the redundant lookups. This also helped to improve the performance (800Mbps -> 870Mbps) on a CPU limited system in a single stream UDPv6 receive test with 1450 byte sized datagrams using iperf. v1->v2: Use IPv6 cookie to validate dst instead of 0 as suggested by Eric Signed-off-by: Subash Abhinov Kasiviswanathan Signed-off-by: David S. Miller --- net/ipv6/udp.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 4e4c401e3bc6..08a188ffe070 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -864,6 +864,64 @@ discard: return 0; } +static struct sock *__udp6_lib_demux_lookup(struct net *net, + __be16 loc_port, const struct in6_addr *loc_addr, + __be16 rmt_port, const struct in6_addr *rmt_addr, + int dif) +{ + struct sock *sk; + + rcu_read_lock(); + sk = __udp6_lib_lookup(net, rmt_addr, rmt_port, loc_addr, loc_port, + dif, &udp_table, NULL); + if (sk && !atomic_inc_not_zero(&sk->sk_refcnt)) + sk = NULL; + rcu_read_unlock(); + + return sk; +} + +static void udp_v6_early_demux(struct sk_buff *skb) +{ + struct net *net = dev_net(skb->dev); + const struct udphdr *uh; + struct sock *sk; + struct dst_entry *dst; + int dif = skb->dev->ifindex; + + if (!pskb_may_pull(skb, skb_transport_offset(skb) + + sizeof(struct udphdr))) + return; + + uh = udp_hdr(skb); + + if (skb->pkt_type == PACKET_HOST) + sk = __udp6_lib_demux_lookup(net, uh->dest, + &ipv6_hdr(skb)->daddr, + uh->source, &ipv6_hdr(skb)->saddr, + dif); + else + return; + + if (!sk) + return; + + skb->sk = sk; + skb->destructor = sock_efree; + dst = READ_ONCE(sk->sk_rx_dst); + + if (dst) + dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie); + if (dst) { + if (dst->flags & DST_NOCACHE) { + if (likely(atomic_inc_not_zero(&dst->__refcnt))) + skb_dst_set(skb, dst); + } else { + skb_dst_set_noref(skb, dst); + } + } +} + static __inline__ int udpv6_rcv(struct sk_buff *skb) { return __udp6_lib_rcv(skb, &udp_table, IPPROTO_UDP); @@ -1379,6 +1437,7 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname, #endif static const struct inet6_protocol udpv6_protocol = { + .early_demux = udp_v6_early_demux, .handler = udpv6_rcv, .err_handler = udpv6_err, .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, -- cgit From 0d6dd35784e76bc3e052ffa13c6839d5c9bcb449 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Thu, 9 Mar 2017 15:04:14 -0800 Subject: netvsc: need napi scheduled during removal Since rndis_halt_device waits until all outstanding sends and receives are completed. Netvsc device needs to still schedule NAPI to see those completions. Fixes: 2506b1dc4bbe ("netvsc: implement NAPI") Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index b1328cef9d5a..0e0c757c1681 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -91,15 +91,6 @@ static void free_netvsc_device(struct netvsc_device *nvdev) } -static inline bool netvsc_channel_idle(const struct netvsc_device *net_device, - u16 q_idx) -{ - const struct netvsc_channel *nvchan = &net_device->chan_table[q_idx]; - - return atomic_read(&net_device->num_outstanding_recvs) == 0 && - atomic_read(&nvchan->queue_sends) == 0; -} - static struct netvsc_device *get_outbound_net_device(struct hv_device *device) { struct netvsc_device *net_device = hv_device_to_netvsc_device(device); @@ -1273,13 +1264,10 @@ void netvsc_channel_cb(void *context) if (unlikely(!ndev)) return; - net_device = net_device_to_netvsc_device(ndev); - if (unlikely(net_device->destroy) && - netvsc_channel_idle(net_device, q_idx)) - return; - /* disable interupts from host */ hv_begin_read(&channel->inbound); + + net_device = net_device_to_netvsc_device(ndev); napi_schedule(&net_device->chan_table[q_idx].napi); } -- cgit From 79cd874c96b38694fb2b4ee3850f6fbbc3015439 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Thu, 9 Mar 2017 15:04:15 -0800 Subject: netvsc: fix hang on netvsc module removal The code in netvsc_device_remove was incorrectly calling napi_disable repeatedly on the same element. This would cause attempts to remove netvsc module to hang. Fixes: 2506b1dc4bbe ("netvsc: implement NAPI") Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 0e0c757c1681..8f9aeec2ce0f 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -562,8 +562,8 @@ void netvsc_device_remove(struct hv_device *device) /* Now, we can close the channel safely */ vmbus_close(device->channel); - for (i = 0; i < VRSS_CHANNEL_MAX; i++) - napi_disable(&net_device->chan_table[0].napi); + for (i = 0; i < net_device->num_chn; i++) + napi_disable(&net_device->chan_table[i].napi); /* Release all resources */ free_netvsc_device(net_device); -- cgit From b3ca9af0fb65098dd2afecca6831c361a585f37f Mon Sep 17 00:00:00 2001 From: VSR Burru Date: Thu, 9 Mar 2017 17:03:24 -0800 Subject: liquidio: optimize DMA in NUMA systems Optimize DMA in NUMA systems by allocating memory from NUMA node that NIC is plugged in to; DMA will no longer cross NUMA nodes. If NIC IRQs are pinned to a local CPU, that CPU's access to the DMA'd data is also optimized. Signed-off-by: VSR Burru Signed-off-by: Felix Manlunas Signed-off-by: Raghu Vatsavayi Signed-off-by: Satanand Burla Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 2 +- drivers/net/ethernet/cavium/liquidio/octeon_device.c | 4 ++-- drivers/net/ethernet/cavium/liquidio/octeon_droq.c | 10 ++-------- drivers/net/ethernet/cavium/liquidio/octeon_iq.h | 2 +- drivers/net/ethernet/cavium/liquidio/request_manager.c | 13 +++---------- 5 files changed, 9 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index dffed432d58e..acfd848d5344 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -782,7 +782,7 @@ static int setup_glists(struct octeon_device *oct, struct lio *lio, int num_iqs) } for (i = 0; i < num_iqs; i++) { - int numa_node = cpu_to_node(i % num_online_cpus()); + int numa_node = dev_to_node(&oct->pci_dev->dev); spin_lock_init(&lio->glist_lock[i]); diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 9675ffbf25e6..e21b477d0159 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -793,7 +793,7 @@ int octeon_setup_instr_queues(struct octeon_device *oct) u32 num_descs = 0; u32 iq_no = 0; union oct_txpciq txpciq; - int numa_node = cpu_to_node(iq_no % num_online_cpus()); + int numa_node = dev_to_node(&oct->pci_dev->dev); if (OCTEON_CN6XXX(oct)) num_descs = @@ -837,7 +837,7 @@ int octeon_setup_output_queues(struct octeon_device *oct) u32 num_descs = 0; u32 desc_size = 0; u32 oq_no = 0; - int numa_node = cpu_to_node(oq_no % num_online_cpus()); + int numa_node = dev_to_node(&oct->pci_dev->dev); if (OCTEON_CN6XXX(oct)) { num_descs = diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c index 0be87d119a97..a91835da1acc 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c @@ -234,8 +234,7 @@ int octeon_init_droq(struct octeon_device *oct, struct octeon_droq *droq; u32 desc_ring_size = 0, c_num_descs = 0, c_buf_size = 0; u32 c_pkts_per_intr = 0, c_refill_threshold = 0; - int orig_node = dev_to_node(&oct->pci_dev->dev); - int numa_node = cpu_to_node(q_no % num_online_cpus()); + int numa_node = dev_to_node(&oct->pci_dev->dev); dev_dbg(&oct->pci_dev->dev, "%s[%d]\n", __func__, q_no); @@ -275,13 +274,8 @@ int octeon_init_droq(struct octeon_device *oct, droq->buffer_size = c_buf_size; desc_ring_size = droq->max_count * OCT_DROQ_DESC_SIZE; - set_dev_node(&oct->pci_dev->dev, numa_node); droq->desc_ring = lio_dma_alloc(oct, desc_ring_size, (dma_addr_t *)&droq->desc_ring_dma); - set_dev_node(&oct->pci_dev->dev, orig_node); - if (!droq->desc_ring) - droq->desc_ring = lio_dma_alloc(oct, desc_ring_size, - (dma_addr_t *)&droq->desc_ring_dma); if (!droq->desc_ring) { dev_err(&oct->pci_dev->dev, @@ -983,7 +977,7 @@ int octeon_create_droq(struct octeon_device *oct, u32 desc_size, void *app_ctx) { struct octeon_droq *droq; - int numa_node = cpu_to_node(q_no % num_online_cpus()); + int numa_node = dev_to_node(&oct->pci_dev->dev); if (oct->droq[q_no]) { dev_dbg(&oct->pci_dev->dev, "Droq already in use. Cannot create droq %d again\n", diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_iq.h b/drivers/net/ethernet/cavium/liquidio/octeon_iq.h index 4608a5af35a3..5063a12613e5 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_iq.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_iq.h @@ -152,7 +152,7 @@ struct octeon_instr_queue { struct oct_iq_stats stats; /** DMA mapped base address of the input descriptor ring. */ - u64 base_addr_dma; + dma_addr_t base_addr_dma; /** Application context */ void *app_ctx; diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c index 707bc15adec6..261f448f9de2 100644 --- a/drivers/net/ethernet/cavium/liquidio/request_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c @@ -62,8 +62,7 @@ int octeon_init_instr_queue(struct octeon_device *oct, u32 iq_no = (u32)txpciq.s.q_no; u32 q_size; struct cavium_wq *db_wq; - int orig_node = dev_to_node(&oct->pci_dev->dev); - int numa_node = cpu_to_node(iq_no % num_online_cpus()); + int numa_node = dev_to_node(&oct->pci_dev->dev); if (OCTEON_CN6XXX(oct)) conf = &(CFG_GET_IQ_CFG(CHIP_CONF(oct, cn6xxx))); @@ -91,13 +90,7 @@ int octeon_init_instr_queue(struct octeon_device *oct, iq->oct_dev = oct; - set_dev_node(&oct->pci_dev->dev, numa_node); - iq->base_addr = lio_dma_alloc(oct, q_size, - (dma_addr_t *)&iq->base_addr_dma); - set_dev_node(&oct->pci_dev->dev, orig_node); - if (!iq->base_addr) - iq->base_addr = lio_dma_alloc(oct, q_size, - (dma_addr_t *)&iq->base_addr_dma); + iq->base_addr = lio_dma_alloc(oct, q_size, &iq->base_addr_dma); if (!iq->base_addr) { dev_err(&oct->pci_dev->dev, "Cannot allocate memory for instr queue %d\n", iq_no); @@ -211,7 +204,7 @@ int octeon_setup_iq(struct octeon_device *oct, void *app_ctx) { u32 iq_no = (u32)txpciq.s.q_no; - int numa_node = cpu_to_node(iq_no % num_online_cpus()); + int numa_node = dev_to_node(&oct->pci_dev->dev); if (oct->instr_queue[iq_no]) { dev_dbg(&oct->pci_dev->dev, "IQ is in use. Cannot create the IQ: %d again\n", -- cgit From c95129d127c6d3d9fca189c6f94c539a7f086b1a Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 10 Mar 2017 12:11:06 +0800 Subject: sctp: add support for generating assoc reset event notification This patch is to add Association Reset Event described in rfc6525 section 6.1.2. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/ulpevent.h | 4 ++++ include/uapi/linux/sctp.h | 15 +++++++++++++++ net/sctp/ulpevent.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h index 324b5965fc4d..2ab7ed44de52 100644 --- a/include/net/sctp/ulpevent.h +++ b/include/net/sctp/ulpevent.h @@ -132,6 +132,10 @@ struct sctp_ulpevent *sctp_ulpevent_make_stream_reset_event( const struct sctp_association *asoc, __u16 flags, __u16 stream_num, __u16 *stream_list, gfp_t gfp); +struct sctp_ulpevent *sctp_ulpevent_make_assoc_reset_event( + const struct sctp_association *asoc, __u16 flags, + __u32 local_tsn, __u32 remote_tsn, gfp_t gfp); + void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event, struct msghdr *); void sctp_ulpevent_read_rcvinfo(const struct sctp_ulpevent *event, diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index d3ae381fcf33..77358297c2f9 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -502,6 +502,17 @@ struct sctp_stream_reset_event { __u16 strreset_stream_list[]; }; +#define SCTP_ASSOC_RESET_DENIED 0x0004 +#define SCTP_ASSOC_RESET_FAILED 0x0008 +struct sctp_assoc_reset_event { + __u16 assocreset_type; + __u16 assocreset_flags; + __u32 assocreset_length; + sctp_assoc_t assocreset_assoc_id; + __u32 assocreset_local_tsn; + __u32 assocreset_remote_tsn; +}; + /* * Described in Section 7.3 * Ancillary Data and Notification Interest Options @@ -518,6 +529,7 @@ struct sctp_event_subscribe { __u8 sctp_authentication_event; __u8 sctp_sender_dry_event; __u8 sctp_stream_reset_event; + __u8 sctp_assoc_reset_event; }; /* @@ -543,6 +555,7 @@ union sctp_notification { struct sctp_authkey_event sn_authkey_event; struct sctp_sender_dry_event sn_sender_dry_event; struct sctp_stream_reset_event sn_strreset_event; + struct sctp_assoc_reset_event sn_assocreset_event; }; /* Section 5.3.1 @@ -572,6 +585,8 @@ enum sctp_sn_type { #define SCTP_SENDER_DRY_EVENT SCTP_SENDER_DRY_EVENT SCTP_STREAM_RESET_EVENT, #define SCTP_STREAM_RESET_EVENT SCTP_STREAM_RESET_EVENT + SCTP_ASSOC_RESET_EVENT, +#define SCTP_ASSOC_RESET_EVENT SCTP_ASSOC_RESET_EVENT }; /* Notification error codes used to fill up the error fields in some diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index c8881bc542a0..420d7f35256a 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -883,6 +883,34 @@ struct sctp_ulpevent *sctp_ulpevent_make_stream_reset_event( return event; } +struct sctp_ulpevent *sctp_ulpevent_make_assoc_reset_event( + const struct sctp_association *asoc, __u16 flags, __u32 local_tsn, + __u32 remote_tsn, gfp_t gfp) +{ + struct sctp_assoc_reset_event *areset; + struct sctp_ulpevent *event; + struct sk_buff *skb; + + event = sctp_ulpevent_new(sizeof(struct sctp_assoc_reset_event), + MSG_NOTIFICATION, gfp); + if (!event) + return NULL; + + skb = sctp_event2skb(event); + areset = (struct sctp_assoc_reset_event *) + skb_put(skb, sizeof(struct sctp_assoc_reset_event)); + + areset->assocreset_type = SCTP_ASSOC_RESET_EVENT; + areset->assocreset_flags = flags; + areset->assocreset_length = sizeof(struct sctp_assoc_reset_event); + sctp_ulpevent_set_owner(event, asoc); + areset->assocreset_assoc_id = sctp_assoc2id(asoc); + areset->assocreset_local_tsn = local_tsn; + areset->assocreset_remote_tsn = remote_tsn; + + return event; +} + /* Return the notification type, assuming this is a notification * event. */ -- cgit From 692787cef6515188f25a5b6cdf008cfe10acf89f Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 10 Mar 2017 12:11:07 +0800 Subject: sctp: implement receiver-side procedures for the SSN/TSN Reset Request Parameter This patch is to implement Receiver-Side Procedures for the SSN/TSN Reset Request Parameter described in rfc6525 section 6.2.4. The process is kind of complicate, it's wonth having some comments from section 6.2.4 in the codes. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/sm.h | 4 +++ net/sctp/sm_statefuns.c | 3 ++ net/sctp/stream.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index b6f682ec184a..2629d6670a27 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -293,6 +293,10 @@ struct sctp_chunk *sctp_process_strreset_inreq( struct sctp_association *asoc, union sctp_params param, struct sctp_ulpevent **evp); +struct sctp_chunk *sctp_process_strreset_tsnreq( + struct sctp_association *asoc, + union sctp_params param, + struct sctp_ulpevent **evp); /* Prototypes for statetable processing. */ diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index e03bb1aab4d0..6982064957a7 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -3872,6 +3872,9 @@ sctp_disposition_t sctp_sf_do_reconf(struct net *net, else if (param.p->type == SCTP_PARAM_RESET_IN_REQUEST) reply = sctp_process_strreset_inreq( (struct sctp_association *)asoc, param, &ev); + else if (param.p->type == SCTP_PARAM_RESET_TSN_REQUEST) + reply = sctp_process_strreset_tsnreq( + (struct sctp_association *)asoc, param, &ev); /* More handles for other types will be added here, by now it * just ignores other types. */ diff --git a/net/sctp/stream.c b/net/sctp/stream.c index 1c6cc04fa3a4..7e993b0c1412 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -477,3 +477,82 @@ out: return chunk; } + +struct sctp_chunk *sctp_process_strreset_tsnreq( + struct sctp_association *asoc, + union sctp_params param, + struct sctp_ulpevent **evp) +{ + __u32 init_tsn = 0, next_tsn = 0, max_tsn_seen; + struct sctp_strreset_tsnreq *tsnreq = param.v; + struct sctp_stream *stream = asoc->stream; + __u32 result = SCTP_STRRESET_DENIED; + __u32 request_seq; + __u16 i; + + request_seq = ntohl(tsnreq->request_seq); + if (request_seq > asoc->strreset_inseq) { + result = SCTP_STRRESET_ERR_BAD_SEQNO; + goto out; + } else if (request_seq == asoc->strreset_inseq) { + asoc->strreset_inseq++; + } + + if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) + goto out; + + if (asoc->strreset_outstanding) { + result = SCTP_STRRESET_ERR_IN_PROGRESS; + goto out; + } + + /* G3: The same processing as though a SACK chunk with no gap report + * and a cumulative TSN ACK of the Sender's Next TSN minus 1 were + * received MUST be performed. + */ + max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map); + sctp_ulpq_reasm_flushtsn(&asoc->ulpq, max_tsn_seen); + sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); + + /* G1: Compute an appropriate value for the Receiver's Next TSN -- the + * TSN that the peer should use to send the next DATA chunk. The + * value SHOULD be the smallest TSN not acknowledged by the + * receiver of the request plus 2^31. + */ + init_tsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + (1 << 31); + sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_INITIAL, + init_tsn, GFP_ATOMIC); + + /* G4: The same processing as though a FWD-TSN chunk (as defined in + * [RFC3758]) with all streams affected and a new cumulative TSN + * ACK of the Receiver's Next TSN minus 1 were received MUST be + * performed. + */ + sctp_outq_free(&asoc->outqueue); + + /* G2: Compute an appropriate value for the local endpoint's next TSN, + * i.e., the next TSN assigned by the receiver of the SSN/TSN reset + * chunk. The value SHOULD be the highest TSN sent by the receiver + * of the request plus 1. + */ + next_tsn = asoc->next_tsn; + asoc->ctsn_ack_point = next_tsn - 1; + asoc->adv_peer_ack_point = asoc->ctsn_ack_point; + + /* G5: The next expected and outgoing SSNs MUST be reset to 0 for all + * incoming and outgoing streams. + */ + for (i = 0; i < stream->outcnt; i++) + stream->out[i].ssn = 0; + for (i = 0; i < stream->incnt; i++) + stream->in[i].ssn = 0; + + result = SCTP_STRRESET_PERFORMED; + + *evp = sctp_ulpevent_make_assoc_reset_event(asoc, 0, init_tsn, + next_tsn, GFP_ATOMIC); + +out: + return sctp_make_strreset_tsnresp(asoc, result, request_seq, + next_tsn, init_tsn); +} -- cgit From b444153fb5a647448c2080ad28656ad183cae4fc Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 10 Mar 2017 12:11:08 +0800 Subject: sctp: add support for generating add stream change event notification This patch is to add Stream Change Event described in rfc6525 section 6.1.3. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/ulpevent.h | 4 ++++ include/uapi/linux/sctp.h | 15 +++++++++++++++ net/sctp/ulpevent.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h index 2ab7ed44de52..1060494ac230 100644 --- a/include/net/sctp/ulpevent.h +++ b/include/net/sctp/ulpevent.h @@ -136,6 +136,10 @@ struct sctp_ulpevent *sctp_ulpevent_make_assoc_reset_event( const struct sctp_association *asoc, __u16 flags, __u32 local_tsn, __u32 remote_tsn, gfp_t gfp); +struct sctp_ulpevent *sctp_ulpevent_make_stream_change_event( + const struct sctp_association *asoc, __u16 flags, + __u32 strchange_instrms, __u32 strchange_outstrms, gfp_t gfp); + void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event, struct msghdr *); void sctp_ulpevent_read_rcvinfo(const struct sctp_ulpevent *event, diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 77358297c2f9..fd652e6501b4 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -513,6 +513,17 @@ struct sctp_assoc_reset_event { __u32 assocreset_remote_tsn; }; +#define SCTP_ASSOC_CHANGE_DENIED 0x0004 +#define SCTP_ASSOC_CHANGE_FAILED 0x0008 +struct sctp_stream_change_event { + __u16 strchange_type; + __u16 strchange_flags; + __u32 strchange_length; + sctp_assoc_t strchange_assoc_id; + __u16 strchange_instrms; + __u16 strchange_outstrms; +}; + /* * Described in Section 7.3 * Ancillary Data and Notification Interest Options @@ -530,6 +541,7 @@ struct sctp_event_subscribe { __u8 sctp_sender_dry_event; __u8 sctp_stream_reset_event; __u8 sctp_assoc_reset_event; + __u8 sctp_stream_change_event; }; /* @@ -556,6 +568,7 @@ union sctp_notification { struct sctp_sender_dry_event sn_sender_dry_event; struct sctp_stream_reset_event sn_strreset_event; struct sctp_assoc_reset_event sn_assocreset_event; + struct sctp_stream_change_event sn_strchange_event; }; /* Section 5.3.1 @@ -587,6 +600,8 @@ enum sctp_sn_type { #define SCTP_STREAM_RESET_EVENT SCTP_STREAM_RESET_EVENT SCTP_ASSOC_RESET_EVENT, #define SCTP_ASSOC_RESET_EVENT SCTP_ASSOC_RESET_EVENT + SCTP_STREAM_CHANGE_EVENT, +#define SCTP_STREAM_CHANGE_EVENT SCTP_STREAM_CHANGE_EVENT }; /* Notification error codes used to fill up the error fields in some diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 420d7f35256a..ec2b3e013c2f 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -911,6 +911,34 @@ struct sctp_ulpevent *sctp_ulpevent_make_assoc_reset_event( return event; } +struct sctp_ulpevent *sctp_ulpevent_make_stream_change_event( + const struct sctp_association *asoc, __u16 flags, + __u32 strchange_instrms, __u32 strchange_outstrms, gfp_t gfp) +{ + struct sctp_stream_change_event *schange; + struct sctp_ulpevent *event; + struct sk_buff *skb; + + event = sctp_ulpevent_new(sizeof(struct sctp_stream_change_event), + MSG_NOTIFICATION, gfp); + if (!event) + return NULL; + + skb = sctp_event2skb(event); + schange = (struct sctp_stream_change_event *) + skb_put(skb, sizeof(struct sctp_stream_change_event)); + + schange->strchange_type = SCTP_STREAM_CHANGE_EVENT; + schange->strchange_flags = flags; + schange->strchange_length = sizeof(struct sctp_stream_change_event); + sctp_ulpevent_set_owner(event, asoc); + schange->strchange_assoc_id = sctp_assoc2id(asoc); + schange->strchange_instrms = strchange_instrms; + schange->strchange_outstrms = strchange_outstrms; + + return event; +} + /* Return the notification type, assuming this is a notification * event. */ -- cgit From 50a41591f11022dcf76c23238dce1a1a62470c46 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 10 Mar 2017 12:11:09 +0800 Subject: sctp: implement receiver-side procedures for the Add Outgoing Streams Request Parameter This patch is to add Receiver-Side Procedures for the Add Outgoing Streams Request Parameter described in section 5.2.5. It is also to improve sctp_chunk_lookup_strreset_param, so that it can be used for processing addstrm_out request. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/sm.h | 4 +++ net/sctp/sm_statefuns.c | 3 ++ net/sctp/stream.c | 89 +++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 86 insertions(+), 10 deletions(-) diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 2629d6670a27..93ae50bbfa99 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -297,6 +297,10 @@ struct sctp_chunk *sctp_process_strreset_tsnreq( struct sctp_association *asoc, union sctp_params param, struct sctp_ulpevent **evp); +struct sctp_chunk *sctp_process_strreset_addstrm_out( + struct sctp_association *asoc, + union sctp_params param, + struct sctp_ulpevent **evp); /* Prototypes for statetable processing. */ diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 6982064957a7..881122b8c370 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -3875,6 +3875,9 @@ sctp_disposition_t sctp_sf_do_reconf(struct net *net, else if (param.p->type == SCTP_PARAM_RESET_TSN_REQUEST) reply = sctp_process_strreset_tsnreq( (struct sctp_association *)asoc, param, &ev); + else if (param.p->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) + reply = sctp_process_strreset_addstrm_out( + (struct sctp_association *)asoc, param, &ev); /* More handles for other types will be added here, by now it * just ignores other types. */ diff --git a/net/sctp/stream.c b/net/sctp/stream.c index 7e993b0c1412..df2794db05d3 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -303,13 +303,14 @@ out: } static sctp_paramhdr_t *sctp_chunk_lookup_strreset_param( - struct sctp_association *asoc, __u32 resp_seq) + struct sctp_association *asoc, __u32 resp_seq, + __be16 type) { struct sctp_chunk *chunk = asoc->strreset_chunk; struct sctp_reconf_chunk *hdr; union sctp_params param; - if (ntohl(resp_seq) != asoc->strreset_outseq || !chunk) + if (!chunk) return NULL; hdr = (struct sctp_reconf_chunk *)chunk->chunk_hdr; @@ -320,7 +321,8 @@ static sctp_paramhdr_t *sctp_chunk_lookup_strreset_param( */ struct sctp_strreset_tsnreq *req = param.v; - if (req->request_seq == resp_seq) + if ((!resp_seq || req->request_seq == resp_seq) && + (!type || type == req->param_hdr.type)) return param.v; } @@ -361,13 +363,9 @@ struct sctp_chunk *sctp_process_strreset_outreq( goto out; if (asoc->strreset_chunk) { - sctp_paramhdr_t *param_hdr; - struct sctp_transport *t; - - param_hdr = sctp_chunk_lookup_strreset_param( - asoc, outreq->response_seq); - if (!param_hdr || param_hdr->type != - SCTP_PARAM_RESET_IN_REQUEST) { + if (!sctp_chunk_lookup_strreset_param( + asoc, outreq->response_seq, + SCTP_PARAM_RESET_IN_REQUEST)) { /* same process with outstanding isn't 0 */ result = SCTP_STRRESET_ERR_IN_PROGRESS; goto out; @@ -377,6 +375,8 @@ struct sctp_chunk *sctp_process_strreset_outreq( asoc->strreset_outseq++; if (!asoc->strreset_outstanding) { + struct sctp_transport *t; + t = asoc->strreset_chunk->transport; if (del_timer(&t->reconf_timer)) sctp_transport_put(t); @@ -556,3 +556,72 @@ out: return sctp_make_strreset_tsnresp(asoc, result, request_seq, next_tsn, init_tsn); } + +struct sctp_chunk *sctp_process_strreset_addstrm_out( + struct sctp_association *asoc, + union sctp_params param, + struct sctp_ulpevent **evp) +{ + struct sctp_strreset_addstrm *addstrm = param.v; + struct sctp_stream *stream = asoc->stream; + __u32 result = SCTP_STRRESET_DENIED; + struct sctp_stream_in *streamin; + __u32 request_seq, incnt; + __u16 in; + + request_seq = ntohl(addstrm->request_seq); + if (request_seq > asoc->strreset_inseq) { + result = SCTP_STRRESET_ERR_BAD_SEQNO; + goto out; + } else if (request_seq == asoc->strreset_inseq) { + asoc->strreset_inseq++; + } + + if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) + goto out; + + if (asoc->strreset_chunk) { + if (!sctp_chunk_lookup_strreset_param( + asoc, 0, SCTP_PARAM_RESET_ADD_IN_STREAMS)) { + /* same process with outstanding isn't 0 */ + result = SCTP_STRRESET_ERR_IN_PROGRESS; + goto out; + } + + asoc->strreset_outstanding--; + asoc->strreset_outseq++; + + if (!asoc->strreset_outstanding) { + struct sctp_transport *t; + + t = asoc->strreset_chunk->transport; + if (del_timer(&t->reconf_timer)) + sctp_transport_put(t); + + sctp_chunk_put(asoc->strreset_chunk); + asoc->strreset_chunk = NULL; + } + } + + in = ntohs(addstrm->number_of_streams); + incnt = stream->incnt + in; + if (!in || incnt > SCTP_MAX_STREAM) + goto out; + + streamin = krealloc(stream->in, incnt * sizeof(*streamin), + GFP_ATOMIC); + if (!streamin) + goto out; + + memset(streamin + stream->incnt, 0, in * sizeof(*streamin)); + stream->in = streamin; + stream->incnt = incnt; + + result = SCTP_STRRESET_PERFORMED; + + *evp = sctp_ulpevent_make_stream_change_event(asoc, + 0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC); + +out: + return sctp_make_strreset_resp(asoc, result, request_seq); +} -- cgit From c5c4ebb3ab87fd87e44a47ec8289238e6f6084c1 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 10 Mar 2017 12:11:10 +0800 Subject: sctp: implement receiver-side procedures for the Add Incoming Streams Request Parameter This patch is to implement Receiver-Side Procedures for the Add Incoming Streams Request Parameter described in rfc6525 section 5.2.6. It is also to fix that it shouldn't have add streams when sending addstrm in request, as the process in peer will handle it by sending a addstrm out request back. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/sm.h | 4 +++ net/sctp/sm_statefuns.c | 3 ++ net/sctp/stream.c | 74 +++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 69 insertions(+), 12 deletions(-) diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 93ae50bbfa99..e0d982588642 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -301,6 +301,10 @@ struct sctp_chunk *sctp_process_strreset_addstrm_out( struct sctp_association *asoc, union sctp_params param, struct sctp_ulpevent **evp); +struct sctp_chunk *sctp_process_strreset_addstrm_in( + struct sctp_association *asoc, + union sctp_params param, + struct sctp_ulpevent **evp); /* Prototypes for statetable processing. */ diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 881122b8c370..a8687a78ed41 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -3878,6 +3878,9 @@ sctp_disposition_t sctp_sf_do_reconf(struct net *net, else if (param.p->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) reply = sctp_process_strreset_addstrm_out( (struct sctp_association *)asoc, param, &ev); + else if (param.p->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) + reply = sctp_process_strreset_addstrm_in( + (struct sctp_association *)asoc, param, &ev); /* More handles for other types will be added here, by now it * just ignores other types. */ diff --git a/net/sctp/stream.c b/net/sctp/stream.c index df2794db05d3..1c925db2e914 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -267,18 +267,6 @@ int sctp_send_add_streams(struct sctp_association *asoc, stream->out = streamout; } - if (in) { - struct sctp_stream_in *streamin; - - streamin = krealloc(stream->in, incnt * sizeof(*streamin), - GFP_KERNEL); - if (!streamin) - goto out; - - memset(streamin + stream->incnt, 0, in * sizeof(*streamin)); - stream->in = streamin; - } - chunk = sctp_make_strreset_addstrm(asoc, out, in); if (!chunk) goto out; @@ -625,3 +613,65 @@ struct sctp_chunk *sctp_process_strreset_addstrm_out( out: return sctp_make_strreset_resp(asoc, result, request_seq); } + +struct sctp_chunk *sctp_process_strreset_addstrm_in( + struct sctp_association *asoc, + union sctp_params param, + struct sctp_ulpevent **evp) +{ + struct sctp_strreset_addstrm *addstrm = param.v; + struct sctp_stream *stream = asoc->stream; + __u32 result = SCTP_STRRESET_DENIED; + struct sctp_stream_out *streamout; + struct sctp_chunk *chunk = NULL; + __u32 request_seq, outcnt; + __u16 out; + + request_seq = ntohl(addstrm->request_seq); + if (request_seq > asoc->strreset_inseq) { + result = SCTP_STRRESET_ERR_BAD_SEQNO; + goto out; + } else if (request_seq == asoc->strreset_inseq) { + asoc->strreset_inseq++; + } + + if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) + goto out; + + if (asoc->strreset_outstanding) { + result = SCTP_STRRESET_ERR_IN_PROGRESS; + goto out; + } + + out = ntohs(addstrm->number_of_streams); + outcnt = stream->outcnt + out; + if (!out || outcnt > SCTP_MAX_STREAM) + goto out; + + streamout = krealloc(stream->out, outcnt * sizeof(*streamout), + GFP_ATOMIC); + if (!streamout) + goto out; + + memset(streamout + stream->outcnt, 0, out * sizeof(*streamout)); + stream->out = streamout; + + chunk = sctp_make_strreset_addstrm(asoc, out, 0); + if (!chunk) + goto out; + + asoc->strreset_chunk = chunk; + asoc->strreset_outstanding = 1; + sctp_chunk_hold(asoc->strreset_chunk); + + stream->outcnt = outcnt; + + *evp = sctp_ulpevent_make_stream_change_event(asoc, + 0, 0, ntohs(addstrm->number_of_streams), GFP_ATOMIC); + +out: + if (!chunk) + chunk = sctp_make_strreset_resp(asoc, result, request_seq); + + return chunk; +} -- cgit From 11ae76e67a179f25ae8f772d62a7ddf717b1cdf3 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 10 Mar 2017 12:11:11 +0800 Subject: sctp: implement receiver-side procedures for the Reconf Response Parameter This patch is to implement Receiver-Side Procedures for the Re-configuration Response Parameter in rfc6525 section 5.2.7. sctp_process_strreset_resp would process the response for any kind of reconf request, and the stream reconf is applied only when the response result is success. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/sm.h | 4 ++ net/sctp/sm_statefuns.c | 6 +- net/sctp/stream.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 3 deletions(-) diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index e0d982588642..47113f2c4b0a 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -305,6 +305,10 @@ struct sctp_chunk *sctp_process_strreset_addstrm_in( struct sctp_association *asoc, union sctp_params param, struct sctp_ulpevent **evp); +struct sctp_chunk *sctp_process_strreset_resp( + struct sctp_association *asoc, + union sctp_params param, + struct sctp_ulpevent **evp); /* Prototypes for statetable processing. */ diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index a8687a78ed41..ab1374fa5ab0 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -3881,9 +3881,9 @@ sctp_disposition_t sctp_sf_do_reconf(struct net *net, else if (param.p->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) reply = sctp_process_strreset_addstrm_in( (struct sctp_association *)asoc, param, &ev); - /* More handles for other types will be added here, by now it - * just ignores other types. - */ + else if (param.p->type == SCTP_PARAM_RESET_RESPONSE) + reply = sctp_process_strreset_resp( + (struct sctp_association *)asoc, param, &ev); if (ev) sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, diff --git a/net/sctp/stream.c b/net/sctp/stream.c index 1c925db2e914..961d0a1e99d1 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -675,3 +675,157 @@ out: return chunk; } + +struct sctp_chunk *sctp_process_strreset_resp( + struct sctp_association *asoc, + union sctp_params param, + struct sctp_ulpevent **evp) +{ + struct sctp_strreset_resp *resp = param.v; + struct sctp_stream *stream = asoc->stream; + struct sctp_transport *t; + __u16 i, nums, flags = 0; + sctp_paramhdr_t *req; + __u32 result; + + req = sctp_chunk_lookup_strreset_param(asoc, resp->response_seq, 0); + if (!req) + return NULL; + + result = ntohl(resp->result); + if (result != SCTP_STRRESET_PERFORMED) { + /* if in progress, do nothing but retransmit */ + if (result == SCTP_STRRESET_IN_PROGRESS) + return NULL; + else if (result == SCTP_STRRESET_DENIED) + flags = SCTP_STREAM_RESET_DENIED; + else + flags = SCTP_STREAM_RESET_FAILED; + } + + if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) { + struct sctp_strreset_outreq *outreq; + __u16 *str_p = NULL; + + outreq = (struct sctp_strreset_outreq *)req; + nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 2; + + if (result == SCTP_STRRESET_PERFORMED) { + if (nums) { + str_p = outreq->list_of_streams; + for (i = 0; i < nums; i++) + stream->out[ntohs(str_p[i])].ssn = 0; + } else { + for (i = 0; i < stream->outcnt; i++) + stream->out[i].ssn = 0; + } + + flags = SCTP_STREAM_RESET_OUTGOING_SSN; + } + + for (i = 0; i < stream->outcnt; i++) + stream->out[i].state = SCTP_STREAM_OPEN; + + *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, + nums, str_p, GFP_ATOMIC); + } else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) { + struct sctp_strreset_inreq *inreq; + __u16 *str_p = NULL; + + /* if the result is performed, it's impossible for inreq */ + if (result == SCTP_STRRESET_PERFORMED) + return NULL; + + inreq = (struct sctp_strreset_inreq *)req; + nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 2; + + str_p = inreq->list_of_streams; + *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, + nums, str_p, GFP_ATOMIC); + } else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) { + struct sctp_strreset_resptsn *resptsn; + __u32 stsn, rtsn; + + /* check for resptsn, as sctp_verify_reconf didn't do it*/ + if (ntohs(param.p->length) != sizeof(*resptsn)) + return NULL; + + resptsn = (struct sctp_strreset_resptsn *)resp; + stsn = ntohl(resptsn->senders_next_tsn); + rtsn = ntohl(resptsn->receivers_next_tsn); + + if (result == SCTP_STRRESET_PERFORMED) { + __u32 mtsn = sctp_tsnmap_get_max_tsn_seen( + &asoc->peer.tsn_map); + + sctp_ulpq_reasm_flushtsn(&asoc->ulpq, mtsn); + sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); + + sctp_tsnmap_init(&asoc->peer.tsn_map, + SCTP_TSN_MAP_INITIAL, + stsn, GFP_ATOMIC); + + sctp_outq_free(&asoc->outqueue); + + asoc->next_tsn = rtsn; + asoc->ctsn_ack_point = asoc->next_tsn - 1; + asoc->adv_peer_ack_point = asoc->ctsn_ack_point; + + for (i = 0; i < stream->outcnt; i++) + stream->out[i].ssn = 0; + for (i = 0; i < stream->incnt; i++) + stream->in[i].ssn = 0; + } + + for (i = 0; i < stream->outcnt; i++) + stream->out[i].state = SCTP_STREAM_OPEN; + + *evp = sctp_ulpevent_make_assoc_reset_event(asoc, flags, + stsn, rtsn, GFP_ATOMIC); + } else if (req->type == SCTP_PARAM_RESET_ADD_OUT_STREAMS) { + struct sctp_strreset_addstrm *addstrm; + __u16 number; + + addstrm = (struct sctp_strreset_addstrm *)req; + nums = ntohs(addstrm->number_of_streams); + number = stream->outcnt - nums; + + if (result == SCTP_STRRESET_PERFORMED) + for (i = number; i < stream->outcnt; i++) + stream->out[i].state = SCTP_STREAM_OPEN; + else + stream->outcnt = number; + + *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, + 0, nums, GFP_ATOMIC); + } else if (req->type == SCTP_PARAM_RESET_ADD_IN_STREAMS) { + struct sctp_strreset_addstrm *addstrm; + + /* if the result is performed, it's impossible for addstrm in + * request. + */ + if (result == SCTP_STRRESET_PERFORMED) + return NULL; + + addstrm = (struct sctp_strreset_addstrm *)req; + nums = ntohs(addstrm->number_of_streams); + + *evp = sctp_ulpevent_make_stream_change_event(asoc, flags, + nums, 0, GFP_ATOMIC); + } + + asoc->strreset_outstanding--; + asoc->strreset_outseq++; + + /* remove everything for this reconf request */ + if (!asoc->strreset_outstanding) { + t = asoc->strreset_chunk->transport; + if (del_timer(&t->reconf_timer)) + sctp_transport_put(t); + + sctp_chunk_put(asoc->strreset_chunk); + asoc->strreset_chunk = NULL; + } + + return NULL; +} -- cgit From c0d8bab6ae518cedfb5246e99ece43fe51d79b56 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 10 Mar 2017 12:11:12 +0800 Subject: sctp: add get and set sockopt for reconf_enable This patchset is to add SCTP_RECONFIG_SUPPORTED sockopt, it would set and get asoc reconf_enable value when asoc_id is set, or it would set and get ep reconf_enalbe value if asoc_id is 0. It is also to add sysctl interface for users to set the default value for reconf_enable. After this patch, stream reconf will work. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/uapi/linux/sctp.h | 1 + net/sctp/socket.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++ net/sctp/sysctl.c | 7 ++++ 3 files changed, 89 insertions(+) diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index fd652e6501b4..7212870ef5d7 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -115,6 +115,7 @@ typedef __s32 sctp_assoc_t; #define SCTP_PR_SUPPORTED 113 #define SCTP_DEFAULT_PRINFO 114 #define SCTP_PR_ASSOC_STATUS 115 +#define SCTP_RECONFIG_SUPPORTED 117 #define SCTP_ENABLE_STREAM_RESET 118 #define SCTP_RESET_STREAMS 119 #define SCTP_RESET_ASSOC 120 diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 6f0a9be50f50..24e28cfb542b 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -3758,6 +3758,39 @@ out: return retval; } +static int sctp_setsockopt_reconfig_supported(struct sock *sk, + char __user *optval, + unsigned int optlen) +{ + struct sctp_assoc_value params; + struct sctp_association *asoc; + int retval = -EINVAL; + + if (optlen != sizeof(params)) + goto out; + + if (copy_from_user(¶ms, optval, optlen)) { + retval = -EFAULT; + goto out; + } + + asoc = sctp_id2assoc(sk, params.assoc_id); + if (asoc) { + asoc->reconf_enable = !!params.assoc_value; + } else if (!params.assoc_id) { + struct sctp_sock *sp = sctp_sk(sk); + + sp->ep->reconf_enable = !!params.assoc_value; + } else { + goto out; + } + + retval = 0; + +out: + return retval; +} + static int sctp_setsockopt_enable_strreset(struct sock *sk, char __user *optval, unsigned int optlen) @@ -4038,6 +4071,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, case SCTP_DEFAULT_PRINFO: retval = sctp_setsockopt_default_prinfo(sk, optval, optlen); break; + case SCTP_RECONFIG_SUPPORTED: + retval = sctp_setsockopt_reconfig_supported(sk, optval, optlen); + break; case SCTP_ENABLE_STREAM_RESET: retval = sctp_setsockopt_enable_strreset(sk, optval, optlen); break; @@ -6540,6 +6576,47 @@ out: return retval; } +static int sctp_getsockopt_reconfig_supported(struct sock *sk, int len, + char __user *optval, + int __user *optlen) +{ + struct sctp_assoc_value params; + struct sctp_association *asoc; + int retval = -EFAULT; + + if (len < sizeof(params)) { + retval = -EINVAL; + goto out; + } + + len = sizeof(params); + if (copy_from_user(¶ms, optval, len)) + goto out; + + asoc = sctp_id2assoc(sk, params.assoc_id); + if (asoc) { + params.assoc_value = asoc->reconf_enable; + } else if (!params.assoc_id) { + struct sctp_sock *sp = sctp_sk(sk); + + params.assoc_value = sp->ep->reconf_enable; + } else { + retval = -EINVAL; + goto out; + } + + if (put_user(len, optlen)) + goto out; + + if (copy_to_user(optval, ¶ms, len)) + goto out; + + retval = 0; + +out: + return retval; +} + static int sctp_getsockopt_enable_strreset(struct sock *sk, int len, char __user *optval, int __user *optlen) @@ -6748,6 +6825,10 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, retval = sctp_getsockopt_pr_assocstatus(sk, len, optval, optlen); break; + case SCTP_RECONFIG_SUPPORTED: + retval = sctp_getsockopt_reconfig_supported(sk, len, optval, + optlen); + break; case SCTP_ENABLE_STREAM_RESET: retval = sctp_getsockopt_enable_strreset(sk, len, optval, optlen); diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index daf8554fd42a..0e732f68c2bf 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -274,6 +274,13 @@ static struct ctl_table sctp_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "reconf_enable", + .data = &init_net.sctp.reconf_enable, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { .procname = "auth_enable", .data = &init_net.sctp.auth_enable, -- cgit From 8b57fd1ec13e22b3e722891ff7a6e6ccaf286779 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Fri, 10 Mar 2017 12:38:47 +0800 Subject: net: Eliminate duplicated codes by creating one new function in_dev_select_addr There are two duplicated loops codes which used to select right address in current codes. Now eliminate these codes by creating one new function in_dev_select_addr. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- net/ipv4/devinet.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index cebedd545e5e..927f1d4b8c80 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1192,6 +1192,18 @@ out: return done; } +static __be32 in_dev_select_addr(const struct in_device *in_dev, + int scope) +{ + for_primary_ifa(in_dev) { + if (ifa->ifa_scope != RT_SCOPE_LINK && + ifa->ifa_scope <= scope) + return ifa->ifa_local; + } endfor_ifa(in_dev); + + return 0; +} + __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) { __be32 addr = 0; @@ -1228,13 +1240,9 @@ no_in_dev: if (master_idx && (dev = dev_get_by_index_rcu(net, master_idx)) && (in_dev = __in_dev_get_rcu(dev))) { - for_primary_ifa(in_dev) { - if (ifa->ifa_scope != RT_SCOPE_LINK && - ifa->ifa_scope <= scope) { - addr = ifa->ifa_local; - goto out_unlock; - } - } endfor_ifa(in_dev); + addr = in_dev_select_addr(in_dev, scope); + if (addr) + goto out_unlock; } /* Not loopback addresses on loopback should be preferred @@ -1249,13 +1257,9 @@ no_in_dev: if (!in_dev) continue; - for_primary_ifa(in_dev) { - if (ifa->ifa_scope != RT_SCOPE_LINK && - ifa->ifa_scope <= scope) { - addr = ifa->ifa_local; - goto out_unlock; - } - } endfor_ifa(in_dev); + addr = in_dev_select_addr(in_dev, scope); + if (addr) + goto out_unlock; } out_unlock: rcu_read_unlock(); -- cgit From 8f48ba71ede12231d5d63fdd34bd8ce7908a3377 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Fri, 10 Mar 2017 16:30:24 +0100 Subject: vxlan: use appropriate family on L3 miss When sending a L3 miss, the family is set to AF_INET even for IPv6. This causes userland (eg "ip monitor") to be confused. Ensure we send the appropriate family in this case. For L2 miss, keep using AF_INET. Signed-off-by: Vincent Bernat Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index e375560cc74e..168257aa8ace 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -276,9 +276,9 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, send_eth = send_ip = true; if (type == RTM_GETNEIGH) { - ndm->ndm_family = AF_INET; send_ip = !vxlan_addr_any(&rdst->remote_ip); send_eth = !is_zero_ether_addr(fdb->eth_addr); + ndm->ndm_family = send_ip ? rdst->remote_ip.sa.sa_family : AF_INET; } else ndm->ndm_family = AF_BRIDGE; ndm->ndm_state = fdb->state; -- cgit From 9fbb9dd8eee459e810560a7d459103d865c48beb Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Mar 2017 17:34:53 +0100 Subject: net: stmmac: Rename clk_ptp_ref clock to ptp_ref There aren't currently any users of the "clk_ptp_ref", but there are other references to "ptp_ref", so I'm leaning towards considering that a typo. Fix it. Cc: Mark Rutland Cc: Rob Herring Cc: devicetree@vger.kernel.org Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 6 +++--- drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index d3bfc2b30fb5..11b27dfd1627 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -28,9 +28,9 @@ Optional properties: clocks may be specified in derived bindings. - clock-names: One name for each entry in the clocks property, the first one should be "stmmaceth" and the second one should be "pclk". -- clk_ptp_ref: this is the PTP reference clock; in case of the PTP is - available this clock is used for programming the Timestamp Addend Register. - If not passed then the system clock will be used and this is fine on some +- ptp_ref: this is the PTP reference clock; in case of the PTP is available + this clock is used for programming the Timestamp Addend Register. If not + passed then the system clock will be used and this is fine on some platforms. - tx-fifo-depth: See ethernet.txt file in the same directory - rx-fifo-depth: See ethernet.txt file in the same directory diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 0ba1caf18619..f2d94eafeb0a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -359,7 +359,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) clk_prepare_enable(plat->pclk); /* Fall-back to main clock in case of no PTP ref is passed */ - plat->clk_ptp_ref = devm_clk_get(&pdev->dev, "clk_ptp_ref"); + plat->clk_ptp_ref = devm_clk_get(&pdev->dev, "ptp_ref"); if (IS_ERR(plat->clk_ptp_ref)) { plat->clk_ptp_rate = clk_get_rate(plat->stmmac_clk); plat->clk_ptp_ref = NULL; -- cgit From 6c1e5abe0780cecafdc24b64aefc84ce6924dda9 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Mar 2017 17:34:54 +0100 Subject: net: stmmac: Stop PHY and remove TX timer on error If an error occurs while opening the device, make sure that both the TX timer and the PHY are properly cleaned up. Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 4498a3861aa3..7c38c9baf238 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1821,7 +1821,7 @@ static int stmmac_open(struct net_device *dev) netdev_err(priv->dev, "%s: ERROR: allocating the IRQ %d (error: %d)\n", __func__, dev->irq, ret); - goto init_error; + goto irq_error; } /* Request the Wake IRQ in case of another line is used for WoL */ @@ -1858,7 +1858,11 @@ lpiirq_error: free_irq(priv->wol_irq, dev); wolirq_error: free_irq(dev->irq, dev); +irq_error: + if (dev->phydev) + phy_stop(dev->phydev); + del_timer_sync(&priv->txtimer); init_error: free_dma_desc_resources(priv); dma_desc_error: -- cgit From c66f6c3775bed342693feec2925276fd13bb1355 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Mar 2017 17:34:55 +0100 Subject: net: stmmac: Disable PTP reference clock on error If an error occurs while opening the device, make sure to disable the PTP reference clock. Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 7c38c9baf238..6060410d2b9e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1754,6 +1754,13 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) return 0; } +static void stmmac_hw_teardown(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + clk_disable_unprepare(priv->plat->clk_ptp_ref); +} + /** * stmmac_open - open entry point of the driver * @dev : pointer to the device structure. @@ -1863,6 +1870,7 @@ irq_error: phy_stop(dev->phydev); del_timer_sync(&priv->txtimer); + stmmac_hw_teardown(dev); init_error: free_dma_desc_resources(priv); dma_desc_error: -- cgit From 0ad2be79f254965f277fabdd7c64512527208f64 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Mar 2017 17:34:56 +0100 Subject: net: stmmac: Balance PTP reference clock enable/disable clk_prepare_enable() and clk_disable_unprepare() for this clock aren't properly balanced, which can trigger a WARN_ON() in the common clock framework. Reviewed-By: Joao Pinto Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++++ drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 6060410d2b9e..ce6d7e791f3f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1711,6 +1711,10 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) stmmac_mmc_setup(priv); if (init_ptp) { + ret = clk_prepare_enable(priv->plat->clk_ptp_ref); + if (ret < 0) + netdev_warn(priv->dev, "failed to enable PTP reference clock: %d\n", ret); + ret = stmmac_init_ptp(priv); if (ret == -EOPNOTSUPP) netdev_warn(priv->dev, "PTP not supported by HW\n"); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index f2d94eafeb0a..fe49c3105755 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -365,7 +365,6 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) plat->clk_ptp_ref = NULL; dev_warn(&pdev->dev, "PTP uses main clock\n"); } else { - clk_prepare_enable(plat->clk_ptp_ref); plat->clk_ptp_rate = clk_get_rate(plat->clk_ptp_ref); dev_dbg(&pdev->dev, "PTP rate %d\n", plat->clk_ptp_rate); } -- cgit From 937071c171ff3656e196c965c5179ba3f9f6a2fc Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Mar 2017 17:34:57 +0100 Subject: net: stmmac: Check for DMA mapping errors When DMA mapping an SKB fragment, the mapping must be checked for errors, otherwise the DMA debug code will complain upon unmap. Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index ce6d7e791f3f..eba9088e1f61 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2079,6 +2079,8 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) des = skb_frag_dma_map(priv->device, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); + if (dma_mapping_error(priv->device, des)) + goto dma_map_err; stmmac_tso_allocator(priv, des, skb_frag_size(frag), (i == nfrags - 1)); -- cgit From 11fbf811c8e8a11529214a494eab25e21ca86739 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Mar 2017 17:34:58 +0100 Subject: net: stmmac: Parse FIFO sizes from feature registers New version of this core encode the FIFO sizes in one of the feature registers. Use these sizes as default, but still allow device tree to override them for backwards compatibility. Reviewed-by: Mikko Perttunen Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 3 +++ drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 2 ++ drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 5 +++++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 +++ 4 files changed, 13 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 04d9245b7149..3ca36744007b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -324,6 +324,9 @@ struct dma_features { unsigned int number_tx_queues; /* Alternate (enhanced) DESC mode */ unsigned int enh_desc; + /* TX and RX FIFO sizes */ + unsigned int tx_fifo_size; + unsigned int rx_fifo_size; }; /* GMAC TX FIFO is 8K, Rx FIFO is 16K */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index db45134fddf0..83f5e953e291 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -148,6 +148,8 @@ enum power_event { /* MAC HW features1 bitmap */ #define GMAC_HW_FEAT_AVSEL BIT(20) #define GMAC_HW_TSOEN BIT(18) +#define GMAC_HW_TXFIFOSIZE GENMASK(10, 6) +#define GMAC_HW_RXFIFOSIZE GENMASK(4, 0) /* MAC HW features2 bitmap */ #define GMAC_HW_FEAT_TXCHCNT GENMASK(21, 18) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index f97b0d5d9987..55270933bae1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -294,6 +294,11 @@ static void dwmac4_get_hw_feature(void __iomem *ioaddr, hw_cap = readl(ioaddr + GMAC_HW_FEATURE1); dma_cap->av = (hw_cap & GMAC_HW_FEAT_AVSEL) >> 20; dma_cap->tsoen = (hw_cap & GMAC_HW_TSOEN) >> 18; + /* RX and TX FIFO sizes are encoded as log2(n / 128). Undo that by + * shifting and store the sizes in bytes. + */ + dma_cap->tx_fifo_size = 128 << ((hw_cap & GMAC_HW_TXFIFOSIZE) >> 6); + dma_cap->rx_fifo_size = 128 << ((hw_cap & GMAC_HW_RXFIFOSIZE) >> 0); /* MAC HW feature2 */ hw_cap = readl(ioaddr + GMAC_HW_FEATURE2); /* TX and RX number of channels */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index eba9088e1f61..78f6ec2d165b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1281,6 +1281,9 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv) { int rxfifosz = priv->plat->rx_fifo_size; + if (rxfifosz == 0) + rxfifosz = priv->dma_cap.rx_fifo_size; + if (priv->plat->force_thresh_dma_mode) priv->hw->dma->dma_mode(priv->ioaddr, tc, tc, rxfifosz); else if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe) { -- cgit From 356b7557857e43c52561ac722168ea173f5f6ddc Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Mar 2017 17:34:59 +0100 Subject: net: stmmac: Program RX queue size and flow control Program the receive queue size based on the RX FIFO size and enable hardware flow control for large FIFOs. Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 12 ++++++ drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 52 +++++++++++++++++++++++- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 83f5e953e291..3b1828b4d294 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -182,6 +182,7 @@ enum power_event { #define MTL_OP_MODE_TSF BIT(1) #define MTL_OP_MODE_TQS_MASK GENMASK(24, 16) +#define MTL_OP_MODE_TQS_SHIFT 16 #define MTL_OP_MODE_TTC_MASK 0x70 #define MTL_OP_MODE_TTC_SHIFT 4 @@ -195,6 +196,17 @@ enum power_event { #define MTL_OP_MODE_TTC_384 (6 << MTL_OP_MODE_TTC_SHIFT) #define MTL_OP_MODE_TTC_512 (7 << MTL_OP_MODE_TTC_SHIFT) +#define MTL_OP_MODE_RQS_MASK GENMASK(29, 20) +#define MTL_OP_MODE_RQS_SHIFT 20 + +#define MTL_OP_MODE_RFD_MASK GENMASK(19, 14) +#define MTL_OP_MODE_RFD_SHIFT 14 + +#define MTL_OP_MODE_RFA_MASK GENMASK(13, 8) +#define MTL_OP_MODE_RFA_SHIFT 8 + +#define MTL_OP_MODE_EHFC BIT(7) + #define MTL_OP_MODE_RTC_MASK 0x18 #define MTL_OP_MODE_RTC_SHIFT 3 diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index 55270933bae1..6ac6b2600a7c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -183,8 +183,9 @@ static void dwmac4_rx_watchdog(void __iomem *ioaddr, u32 riwt) } static void dwmac4_dma_chan_op_mode(void __iomem *ioaddr, int txmode, - int rxmode, u32 channel) + int rxmode, u32 channel, int rxfifosz) { + unsigned int rqs = rxfifosz / 256 - 1; u32 mtl_tx_op, mtl_rx_op, mtl_rx_int; /* Following code only done for channel 0, other channels not yet @@ -250,6 +251,53 @@ static void dwmac4_dma_chan_op_mode(void __iomem *ioaddr, int txmode, mtl_rx_op |= MTL_OP_MODE_RTC_128; } + mtl_rx_op &= ~MTL_OP_MODE_RQS_MASK; + mtl_rx_op |= rqs << MTL_OP_MODE_RQS_SHIFT; + + /* enable flow control only if each channel gets 4 KiB or more FIFO */ + if (rxfifosz >= 4096) { + unsigned int rfd, rfa; + + mtl_rx_op |= MTL_OP_MODE_EHFC; + + /* Set Threshold for Activating Flow Control to min 2 frames, + * i.e. 1500 * 2 = 3000 bytes. + * + * Set Threshold for Deactivating Flow Control to min 1 frame, + * i.e. 1500 bytes. + */ + switch (rxfifosz) { + case 4096: + /* This violates the above formula because of FIFO size + * limit therefore overflow may occur in spite of this. + */ + rfd = 0x03; /* Full-2.5K */ + rfa = 0x01; /* Full-1.5K */ + break; + + case 8192: + rfd = 0x06; /* Full-4K */ + rfa = 0x0a; /* Full-6K */ + break; + + case 16384: + rfd = 0x06; /* Full-4K */ + rfa = 0x12; /* Full-10K */ + break; + + default: + rfd = 0x06; /* Full-4K */ + rfa = 0x1e; /* Full-16K */ + break; + } + + mtl_rx_op &= ~MTL_OP_MODE_RFD_MASK; + mtl_rx_op |= rfd << MTL_OP_MODE_RFD_SHIFT; + + mtl_rx_op &= ~MTL_OP_MODE_RFA_MASK; + mtl_rx_op |= rfa << MTL_OP_MODE_RFA_SHIFT; + } + writel(mtl_rx_op, ioaddr + MTL_CHAN_RX_OP_MODE(channel)); /* Enable MTL RX overflow */ @@ -262,7 +310,7 @@ static void dwmac4_dma_operation_mode(void __iomem *ioaddr, int txmode, int rxmode, int rxfifosz) { /* Only Channel 0 is actually configured and used */ - dwmac4_dma_chan_op_mode(ioaddr, txmode, rxmode, 0); + dwmac4_dma_chan_op_mode(ioaddr, txmode, rxmode, 0, rxfifosz); } static void dwmac4_get_hw_feature(void __iomem *ioaddr, -- cgit From cee45b2edafaa1063bd3a43ba3577490a26fed4c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Mar 2017 17:35:00 +0100 Subject: net: stmmac: dwc-qos: Split out ->probe() and ->remove() Split out the binding specific parts of ->probe() and ->remove() to enable the driver to support variants of the binding. This is useful in order to keep backwards-compatibility while making it easy for a sub- driver to deal only with the updated bindings rather than having to add compatibility quirks all over the place. Reviewed-by: Mikko Perttunen Reviewed-By: Joao Pinto Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- .../ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c | 124 ++++++++++++++++----- 1 file changed, 98 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c index 1a3fa3d9f855..319232021bb7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -106,13 +107,80 @@ static int dwc_eth_dwmac_config_dt(struct platform_device *pdev, return 0; } +static void *dwc_qos_probe(struct platform_device *pdev, + struct plat_stmmacenet_data *plat_dat, + struct stmmac_resources *stmmac_res) +{ + int err; + + plat_dat->stmmac_clk = devm_clk_get(&pdev->dev, "apb_pclk"); + if (IS_ERR(plat_dat->stmmac_clk)) { + dev_err(&pdev->dev, "apb_pclk clock not found.\n"); + return ERR_CAST(plat_dat->stmmac_clk); + } + + err = clk_prepare_enable(plat_dat->stmmac_clk); + if (err < 0) { + dev_err(&pdev->dev, "failed to enable apb_pclk clock: %d\n", + err); + return ERR_PTR(err); + } + + plat_dat->pclk = devm_clk_get(&pdev->dev, "phy_ref_clk"); + if (IS_ERR(plat_dat->pclk)) { + dev_err(&pdev->dev, "phy_ref_clk clock not found.\n"); + err = PTR_ERR(plat_dat->pclk); + goto disable; + } + + err = clk_prepare_enable(plat_dat->pclk); + if (err < 0) { + dev_err(&pdev->dev, "failed to enable phy_ref clock: %d\n", + err); + goto disable; + } + + return NULL; + +disable: + clk_disable_unprepare(plat_dat->stmmac_clk); + return ERR_PTR(err); +} + +static int dwc_qos_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(ndev); + + clk_disable_unprepare(priv->plat->pclk); + clk_disable_unprepare(priv->plat->stmmac_clk); + + return 0; +} + +struct dwc_eth_dwmac_data { + void *(*probe)(struct platform_device *pdev, + struct plat_stmmacenet_data *data, + struct stmmac_resources *res); + int (*remove)(struct platform_device *pdev); +}; + +static const struct dwc_eth_dwmac_data dwc_qos_data = { + .probe = dwc_qos_probe, + .remove = dwc_qos_remove, +}; + static int dwc_eth_dwmac_probe(struct platform_device *pdev) { + const struct dwc_eth_dwmac_data *data; struct plat_stmmacenet_data *plat_dat; struct stmmac_resources stmmac_res; struct resource *res; + void *priv; int ret; + data = of_device_get_match_data(&pdev->dev); + memset(&stmmac_res, 0, sizeof(struct stmmac_resources)); /** @@ -138,39 +206,26 @@ static int dwc_eth_dwmac_probe(struct platform_device *pdev) if (IS_ERR(plat_dat)) return PTR_ERR(plat_dat); - plat_dat->stmmac_clk = devm_clk_get(&pdev->dev, "apb_pclk"); - if (IS_ERR(plat_dat->stmmac_clk)) { - dev_err(&pdev->dev, "apb_pclk clock not found.\n"); - ret = PTR_ERR(plat_dat->stmmac_clk); - plat_dat->stmmac_clk = NULL; - goto err_remove_config_dt; + priv = data->probe(pdev, plat_dat, &stmmac_res); + if (IS_ERR(priv)) { + ret = PTR_ERR(priv); + dev_err(&pdev->dev, "failed to probe subdriver: %d\n", ret); + goto remove_config; } - clk_prepare_enable(plat_dat->stmmac_clk); - - plat_dat->pclk = devm_clk_get(&pdev->dev, "phy_ref_clk"); - if (IS_ERR(plat_dat->pclk)) { - dev_err(&pdev->dev, "phy_ref_clk clock not found.\n"); - ret = PTR_ERR(plat_dat->pclk); - plat_dat->pclk = NULL; - goto err_out_clk_dis_phy; - } - clk_prepare_enable(plat_dat->pclk); ret = dwc_eth_dwmac_config_dt(pdev, plat_dat); if (ret) - goto err_out_clk_dis_aper; + goto remove; ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); if (ret) - goto err_out_clk_dis_aper; + goto remove; - return 0; + return ret; -err_out_clk_dis_aper: - clk_disable_unprepare(plat_dat->pclk); -err_out_clk_dis_phy: - clk_disable_unprepare(plat_dat->stmmac_clk); -err_remove_config_dt: +remove: + data->remove(pdev); +remove_config: stmmac_remove_config_dt(pdev, plat_dat); return ret; @@ -178,11 +233,28 @@ err_remove_config_dt: static int dwc_eth_dwmac_remove(struct platform_device *pdev) { - return stmmac_pltfr_remove(pdev); + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(ndev); + const struct dwc_eth_dwmac_data *data; + int err; + + data = of_device_get_match_data(&pdev->dev); + + err = stmmac_dvr_remove(&pdev->dev); + if (err < 0) + dev_err(&pdev->dev, "failed to remove platform: %d\n", err); + + err = data->remove(pdev); + if (err < 0) + dev_err(&pdev->dev, "failed to remove subdriver: %d\n", err); + + stmmac_remove_config_dt(pdev, priv->plat); + + return err; } static const struct of_device_id dwc_eth_dwmac_match[] = { - { .compatible = "snps,dwc-qos-ethernet-4.10", }, + { .compatible = "snps,dwc-qos-ethernet-4.10", .data = &dwc_qos_data }, { } }; MODULE_DEVICE_TABLE(of, dwc_eth_dwmac_match); -- cgit From e6ea2d16fc615e82bbd9c022c6a4302f22f418bf Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 10 Mar 2017 17:35:01 +0100 Subject: net: stmmac: dwc-qos: Add Tegra186 support The NVIDIA Tegra186 SoC contains an instance of the Synopsys DWC ethernet QOS IP core. The binding that it uses is slightly different from existing ones because of the integration (clocks, resets, ...). Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- .../ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c | 247 +++++++++++++++++++++ drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 1 + 2 files changed, 248 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c index 319232021bb7..dd6a2f9791cc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c @@ -14,17 +14,34 @@ #include #include #include +#include #include #include +#include #include #include #include #include #include #include +#include #include #include "stmmac_platform.h" +#include "dwmac4.h" + +struct tegra_eqos { + struct device *dev; + void __iomem *regs; + + struct reset_control *rst; + struct clk *clk_master; + struct clk *clk_slave; + struct clk *clk_tx; + struct clk *clk_rx; + + struct gpio_desc *reset; +}; static int dwc_eth_dwmac_config_dt(struct platform_device *pdev, struct plat_stmmacenet_data *plat_dat) @@ -158,6 +175,230 @@ static int dwc_qos_remove(struct platform_device *pdev) return 0; } +#define SDMEMCOMPPADCTRL 0x8800 +#define SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD BIT(31) + +#define AUTO_CAL_CONFIG 0x8804 +#define AUTO_CAL_CONFIG_START BIT(31) +#define AUTO_CAL_CONFIG_ENABLE BIT(29) + +#define AUTO_CAL_STATUS 0x880c +#define AUTO_CAL_STATUS_ACTIVE BIT(31) + +static void tegra_eqos_fix_speed(void *priv, unsigned int speed) +{ + struct tegra_eqos *eqos = priv; + unsigned long rate = 125000000; + bool needs_calibration = false; + u32 value; + int err; + + switch (speed) { + case SPEED_1000: + needs_calibration = true; + rate = 125000000; + break; + + case SPEED_100: + needs_calibration = true; + rate = 25000000; + break; + + case SPEED_10: + rate = 2500000; + break; + + default: + dev_err(eqos->dev, "invalid speed %u\n", speed); + break; + } + + if (needs_calibration) { + /* calibrate */ + value = readl(eqos->regs + SDMEMCOMPPADCTRL); + value |= SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD; + writel(value, eqos->regs + SDMEMCOMPPADCTRL); + + udelay(1); + + value = readl(eqos->regs + AUTO_CAL_CONFIG); + value |= AUTO_CAL_CONFIG_START | AUTO_CAL_CONFIG_ENABLE; + writel(value, eqos->regs + AUTO_CAL_CONFIG); + + err = readl_poll_timeout_atomic(eqos->regs + AUTO_CAL_STATUS, + value, + value & AUTO_CAL_STATUS_ACTIVE, + 1, 10); + if (err < 0) { + dev_err(eqos->dev, "calibration did not start\n"); + goto failed; + } + + err = readl_poll_timeout_atomic(eqos->regs + AUTO_CAL_STATUS, + value, + (value & AUTO_CAL_STATUS_ACTIVE) == 0, + 20, 200); + if (err < 0) { + dev_err(eqos->dev, "calibration didn't finish\n"); + goto failed; + } + + failed: + value = readl(eqos->regs + SDMEMCOMPPADCTRL); + value &= ~SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD; + writel(value, eqos->regs + SDMEMCOMPPADCTRL); + } else { + value = readl(eqos->regs + AUTO_CAL_CONFIG); + value &= ~AUTO_CAL_CONFIG_ENABLE; + writel(value, eqos->regs + AUTO_CAL_CONFIG); + } + + err = clk_set_rate(eqos->clk_tx, rate); + if (err < 0) + dev_err(eqos->dev, "failed to set TX rate: %d\n", err); +} + +static int tegra_eqos_init(struct platform_device *pdev, void *priv) +{ + struct tegra_eqos *eqos = priv; + unsigned long rate; + u32 value; + + rate = clk_get_rate(eqos->clk_slave); + + value = (rate / 1000000) - 1; + writel(value, eqos->regs + GMAC_1US_TIC_COUNTER); + + return 0; +} + +static void *tegra_eqos_probe(struct platform_device *pdev, + struct plat_stmmacenet_data *data, + struct stmmac_resources *res) +{ + struct tegra_eqos *eqos; + int err; + + eqos = devm_kzalloc(&pdev->dev, sizeof(*eqos), GFP_KERNEL); + if (!eqos) { + err = -ENOMEM; + goto error; + } + + eqos->dev = &pdev->dev; + eqos->regs = res->addr; + + eqos->clk_master = devm_clk_get(&pdev->dev, "master_bus"); + if (IS_ERR(eqos->clk_master)) { + err = PTR_ERR(eqos->clk_master); + goto error; + } + + err = clk_prepare_enable(eqos->clk_master); + if (err < 0) + goto error; + + eqos->clk_slave = devm_clk_get(&pdev->dev, "slave_bus"); + if (IS_ERR(eqos->clk_slave)) { + err = PTR_ERR(eqos->clk_slave); + goto disable_master; + } + + data->stmmac_clk = eqos->clk_slave; + + err = clk_prepare_enable(eqos->clk_slave); + if (err < 0) + goto disable_master; + + eqos->clk_rx = devm_clk_get(&pdev->dev, "rx"); + if (IS_ERR(eqos->clk_rx)) { + err = PTR_ERR(eqos->clk_rx); + goto disable_slave; + } + + err = clk_prepare_enable(eqos->clk_rx); + if (err < 0) + goto disable_slave; + + eqos->clk_tx = devm_clk_get(&pdev->dev, "tx"); + if (IS_ERR(eqos->clk_tx)) { + err = PTR_ERR(eqos->clk_tx); + goto disable_rx; + } + + err = clk_prepare_enable(eqos->clk_tx); + if (err < 0) + goto disable_rx; + + eqos->reset = devm_gpiod_get(&pdev->dev, "phy-reset", GPIOD_OUT_HIGH); + if (IS_ERR(eqos->reset)) { + err = PTR_ERR(eqos->reset); + goto disable_tx; + } + + usleep_range(2000, 4000); + gpiod_set_value(eqos->reset, 0); + + eqos->rst = devm_reset_control_get(&pdev->dev, "eqos"); + if (IS_ERR(eqos->rst)) { + err = PTR_ERR(eqos->rst); + goto reset_phy; + } + + err = reset_control_assert(eqos->rst); + if (err < 0) + goto reset_phy; + + usleep_range(2000, 4000); + + err = reset_control_deassert(eqos->rst); + if (err < 0) + goto reset_phy; + + usleep_range(2000, 4000); + + data->fix_mac_speed = tegra_eqos_fix_speed; + data->init = tegra_eqos_init; + data->bsp_priv = eqos; + + err = tegra_eqos_init(pdev, eqos); + if (err < 0) + goto reset; + +out: + return eqos; + +reset: + reset_control_assert(eqos->rst); +reset_phy: + gpiod_set_value(eqos->reset, 1); +disable_tx: + clk_disable_unprepare(eqos->clk_tx); +disable_rx: + clk_disable_unprepare(eqos->clk_rx); +disable_slave: + clk_disable_unprepare(eqos->clk_slave); +disable_master: + clk_disable_unprepare(eqos->clk_master); +error: + eqos = ERR_PTR(err); + goto out; +} + +static int tegra_eqos_remove(struct platform_device *pdev) +{ + struct tegra_eqos *eqos = get_stmmac_bsp_priv(&pdev->dev); + + reset_control_assert(eqos->rst); + gpiod_set_value(eqos->reset, 1); + clk_disable_unprepare(eqos->clk_tx); + clk_disable_unprepare(eqos->clk_rx); + clk_disable_unprepare(eqos->clk_slave); + clk_disable_unprepare(eqos->clk_master); + + return 0; +} + struct dwc_eth_dwmac_data { void *(*probe)(struct platform_device *pdev, struct plat_stmmacenet_data *data, @@ -170,6 +411,11 @@ static const struct dwc_eth_dwmac_data dwc_qos_data = { .remove = dwc_qos_remove, }; +static const struct dwc_eth_dwmac_data tegra_eqos_data = { + .probe = tegra_eqos_probe, + .remove = tegra_eqos_remove, +}; + static int dwc_eth_dwmac_probe(struct platform_device *pdev) { const struct dwc_eth_dwmac_data *data; @@ -255,6 +501,7 @@ static int dwc_eth_dwmac_remove(struct platform_device *pdev) static const struct of_device_id dwc_eth_dwmac_match[] = { { .compatible = "snps,dwc-qos-ethernet-4.10", .data = &dwc_qos_data }, + { .compatible = "nvidia,tegra186-eqos", .data = &tegra_eqos_data }, { } }; MODULE_DEVICE_TABLE(of, dwc_eth_dwmac_match); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 3b1828b4d294..018fc2d447c4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -25,6 +25,7 @@ #define GMAC_RXQ_CTRL0 0x000000a0 #define GMAC_INT_STATUS 0x000000b0 #define GMAC_INT_EN 0x000000b4 +#define GMAC_1US_TIC_COUNTER 0x000000dc #define GMAC_PCS_BASE 0x000000e0 #define GMAC_PHYIF_CONTROL_STATUS 0x000000f8 #define GMAC_PMT 0x000000c0 -- cgit From d976a525c371276cebd2517349d1d3568a0e48b5 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Fri, 10 Mar 2017 18:24:51 +0000 Subject: net: stmmac: multiple queues dt configuration This patch adds the multiple queues configuration in the Device Tree. It was also created a set of structures to keep the RX and TX queues configurations to be used in the driver. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 40 ++++++++++ .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 91 ++++++++++++++++++++++ include/linux/stmmac.h | 30 +++++++ 3 files changed, 161 insertions(+) diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index 11b27dfd1627..04a258e2d4e0 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -72,6 +72,27 @@ Optional properties: - snps,mb: mixed-burst - snps,rb: rebuild INCRx Burst - mdio: with compatible = "snps,dwmac-mdio", create and register mdio bus. +- Multiple RX Queues parameters: below the list of all the parameters to + configure the multiple RX queues: + - snps,rx-queues-to-use: number of RX queues to be used in the driver + - Choose one of these RX scheduling algorithms: + - snps,rx-sched-sp: Strict priority + - snps,rx-sched-wsp: Weighted Strict priority + - For each RX queue + - Choose one of these modes: + - snps,dcb-algorithm: Queue to be enabled as DCB + - snps,avb-algorithm: Queue to be enabled as AVB + - snps,map-to-dma-channel: Channel to map +- Multiple TX Queues parameters: below the list of all the parameters to + configure the multiple TX queues: + - snps,tx-queues-to-use: number of TX queues to be used in the driver + - Choose one of these TX scheduling algorithms: + - snps,tx-sched-wrr: Weighted Round Robin + - snps,tx-sched-wfq: Weighted Fair Queuing + - snps,tx-sched-dwrr: Deficit Weighted Round Robin + - snps,tx-sched-sp: Strict priority + - For each TX queue + - snps,weight: TX queue weight (if using a weighted algorithm) Examples: @@ -81,6 +102,23 @@ Examples: snps,blen = <256 128 64 32 0 0 0>; }; + mtl_rx_setup: rx-queues-config { + snps,rx-queues-to-use = <1>; + snps,rx-sched-sp; + queue0 { + snps,dcb-algorithm; + snps,map-to-dma-channel = <0x0>; + }; + }; + + mtl_tx_setup: tx-queues-config { + snps,tx-queues-to-use = <1>; + snps,tx-sched-wrr; + queue0 { + snps,weight = <0x10>; + }; + }; + gmac0: ethernet@e0800000 { compatible = "st,spear600-gmac"; reg = <0xe0800000 0x8000>; @@ -104,4 +142,6 @@ Examples: phy1: ethernet-phy@0 { }; }; + snps,mtl-rx-config = <&mtl_rx_setup>; + snps,mtl-tx-config = <&mtl_tx_setup>; }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index fe49c3105755..0b76e3de502d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -131,6 +131,95 @@ static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev) return axi; } +/** + * stmmac_mtl_setup - parse DT parameters for multiple queues configuration + * @pdev: platform device + */ +static void stmmac_mtl_setup(struct platform_device *pdev, + struct plat_stmmacenet_data *plat) +{ + struct device_node *q_node; + struct device_node *rx_node; + struct device_node *tx_node; + u8 queue = 0; + + rx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-rx-config", 0); + if (!rx_node) + return; + + tx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-tx-config", 0); + if (!tx_node) { + of_node_put(rx_node); + return; + } + + /* Processing RX queues common config */ + if (of_property_read_u8(rx_node, "snps,rx-queues-to-use", + &plat->rx_queues_to_use)) + plat->rx_queues_to_use = 1; + + if (of_property_read_bool(rx_node, "snps,rx-sched-sp")) + plat->rx_sched_algorithm = MTL_RX_ALGORITHM_SP; + else if (of_property_read_bool(rx_node, "snps,rx-sched-wsp")) + plat->rx_sched_algorithm = MTL_RX_ALGORITHM_WSP; + else + plat->rx_sched_algorithm = MTL_RX_ALGORITHM_SP; + + /* Processing individual RX queue config */ + for_each_child_of_node(rx_node, q_node) { + if (queue >= plat->rx_queues_to_use) + break; + + if (of_property_read_bool(q_node, "snps,dcb-algorithm")) + plat->rx_queues_cfg[queue].mode_to_use = MTL_RX_DCB; + else if (of_property_read_bool(q_node, "snps,avb-algorithm")) + plat->rx_queues_cfg[queue].mode_to_use = MTL_RX_AVB; + else + plat->rx_queues_cfg[queue].mode_to_use = MTL_RX_DCB; + + if (of_property_read_u8(q_node, "snps,map-to-dma-channel", + &plat->rx_queues_cfg[queue].chan)) + plat->rx_queues_cfg[queue].chan = queue; + /* TODO: Dynamic mapping to be included in the future */ + + queue++; + } + + /* Processing TX queues common config */ + if (of_property_read_u8(tx_node, "snps,tx-queues-to-use", + &plat->tx_queues_to_use)) + plat->tx_queues_to_use = 1; + + if (of_property_read_bool(tx_node, "snps,tx-sched-wrr")) + plat->tx_sched_algorithm = MTL_TX_ALGORITHM_WRR; + else if (of_property_read_bool(tx_node, "snps,tx-sched-wfq")) + plat->tx_sched_algorithm = MTL_TX_ALGORITHM_WFQ; + else if (of_property_read_bool(tx_node, "snps,tx-sched-dwrr")) + plat->tx_sched_algorithm = MTL_TX_ALGORITHM_DWRR; + else if (of_property_read_bool(tx_node, "snps,tx-sched-sp")) + plat->tx_sched_algorithm = MTL_TX_ALGORITHM_SP; + else + plat->tx_sched_algorithm = MTL_TX_ALGORITHM_SP; + + queue = 0; + + /* Processing individual TX queue config */ + for_each_child_of_node(tx_node, q_node) { + if (queue >= plat->tx_queues_to_use) + break; + + if (of_property_read_u8(q_node, "snps,weight", + &plat->tx_queues_cfg[queue].weight)) + plat->tx_queues_cfg[queue].weight = 0x10 + queue; + + queue++; + } + + of_node_put(rx_node); + of_node_put(tx_node); + of_node_put(q_node); +} + /** * stmmac_dt_phy - parse device-tree driver parameters to allocate PHY resources * @plat: driver data platform structure @@ -340,6 +429,8 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) plat->axi = stmmac_axi_setup(pdev); + stmmac_mtl_setup(pdev, plat); + /* clock setup */ plat->stmmac_clk = devm_clk_get(&pdev->dev, STMMAC_RESOURCE_NAME); diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index fc273e9d5f67..266ff2af91e5 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -28,6 +28,9 @@ #include +#define MTL_MAX_RX_QUEUES 8 +#define MTL_MAX_TX_QUEUES 8 + #define STMMAC_RX_COE_NONE 0 #define STMMAC_RX_COE_TYPE1 1 #define STMMAC_RX_COE_TYPE2 2 @@ -44,6 +47,18 @@ #define STMMAC_CSR_150_250M 0x4 /* MDC = clk_scr_i/102 */ #define STMMAC_CSR_250_300M 0x5 /* MDC = clk_scr_i/122 */ +/* MTL algorithms identifiers */ +#define MTL_TX_ALGORITHM_WRR 0x0 +#define MTL_TX_ALGORITHM_WFQ 0x1 +#define MTL_TX_ALGORITHM_DWRR 0x2 +#define MTL_TX_ALGORITHM_SP 0x3 +#define MTL_RX_ALGORITHM_SP 0x4 +#define MTL_RX_ALGORITHM_WSP 0x5 + +/* RX Queue Mode */ +#define MTL_RX_DCB 0x0 +#define MTL_RX_AVB 0x1 + /* The MDC clock could be set higher than the IEEE 802.3 * specified frequency limit 0f 2.5 MHz, by programming a clock divider * of value different than the above defined values. The resultant MDIO @@ -109,6 +124,15 @@ struct stmmac_axi { bool axi_rb; }; +struct stmmac_rxq_cfg { + u8 mode_to_use; + u8 chan; +}; + +struct stmmac_txq_cfg { + u8 weight; +}; + struct plat_stmmacenet_data { int bus_id; int phy_addr; @@ -133,6 +157,12 @@ struct plat_stmmacenet_data { int unicast_filter_entries; int tx_fifo_size; int rx_fifo_size; + u8 rx_queues_to_use; + u8 tx_queues_to_use; + u8 rx_sched_algorithm; + u8 tx_sched_algorithm; + struct stmmac_rxq_cfg rx_queues_cfg[MTL_MAX_RX_QUEUES]; + struct stmmac_txq_cfg tx_queues_cfg[MTL_MAX_TX_QUEUES]; void (*fix_mac_speed)(void *priv, unsigned int speed); int (*init)(struct platform_device *pdev, void *priv); void (*exit)(struct platform_device *pdev, void *priv); -- cgit From d0a9c9f9c6d0b1f0773e0aba5ab519b8ddc87a7a Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Fri, 10 Mar 2017 18:24:52 +0000 Subject: net: stmmac: configure mtl rx and tx algorithms This patch adds the RX and TX scheduling algorithms programming. It introduces the multiple queues configuration function (stmmac_mtl_configuration) in stmmac_main. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 4 ++ drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 10 +++++ drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 48 +++++++++++++++++++++++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 31 +++++++++++++-- 4 files changed, 90 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 3ca36744007b..31d3324df6d4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -458,6 +458,10 @@ struct stmmac_ops { int (*rx_ipc)(struct mac_device_info *hw); /* Enable RX Queues */ void (*rx_queue_enable)(struct mac_device_info *hw, u32 queue); + /* Program RX Algorithms */ + void (*prog_mtl_rx_algorithms)(struct mac_device_info *hw, u32 rx_alg); + /* Program TX Algorithms */ + void (*prog_mtl_tx_algorithms)(struct mac_device_info *hw, u32 tx_alg); /* Dump MAC registers */ void (*dump_regs)(struct mac_device_info *hw, u32 *reg_space); /* Handle extra events on specific interrupts hw dependent */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 018fc2d447c4..bc994fe3e6bf 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -164,6 +164,16 @@ enum power_event { #define GMAC_HI_REG_AE BIT(31) /* MTL registers */ +#define MTL_OPERATION_MODE 0x00000c00 +#define MTL_OPERATION_SCHALG_MASK GENMASK(6, 5) +#define MTL_OPERATION_SCHALG_WRR (0x0 << 5) +#define MTL_OPERATION_SCHALG_WFQ (0x1 << 5) +#define MTL_OPERATION_SCHALG_DWRR (0x2 << 5) +#define MTL_OPERATION_SCHALG_SP (0x3 << 5) +#define MTL_OPERATION_RAA BIT(2) +#define MTL_OPERATION_RAA_SP (0x0 << 2) +#define MTL_OPERATION_RAA_WSP (0x1 << 2) + #define MTL_INT_STATUS 0x00000c20 #define MTL_INT_Q0 BIT(0) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 1e79e6529c4a..f96675567192 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -70,6 +70,52 @@ static void dwmac4_rx_queue_enable(struct mac_device_info *hw, u32 queue) writel(value, ioaddr + GMAC_RXQ_CTRL0); } +static void dwmac4_prog_mtl_rx_algorithms(struct mac_device_info *hw, + u32 rx_alg) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value = readl(ioaddr + MTL_OPERATION_MODE); + + value &= ~MTL_OPERATION_RAA; + switch (rx_alg) { + case MTL_RX_ALGORITHM_SP: + value |= MTL_OPERATION_RAA_SP; + break; + case MTL_RX_ALGORITHM_WSP: + value |= MTL_OPERATION_RAA_WSP; + break; + default: + break; + } + + writel(value, ioaddr + MTL_OPERATION_MODE); +} + +static void dwmac4_prog_mtl_tx_algorithms(struct mac_device_info *hw, + u32 tx_alg) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value = readl(ioaddr + MTL_OPERATION_MODE); + + value &= ~MTL_OPERATION_SCHALG_MASK; + switch (tx_alg) { + case MTL_TX_ALGORITHM_WRR: + value |= MTL_OPERATION_SCHALG_WRR; + break; + case MTL_TX_ALGORITHM_WFQ: + value |= MTL_OPERATION_SCHALG_WFQ; + break; + case MTL_TX_ALGORITHM_DWRR: + value |= MTL_OPERATION_SCHALG_DWRR; + break; + case MTL_TX_ALGORITHM_SP: + value |= MTL_OPERATION_SCHALG_SP; + break; + default: + break; + } +} + static void dwmac4_dump_regs(struct mac_device_info *hw, u32 *reg_space) { void __iomem *ioaddr = hw->pcsr; @@ -457,6 +503,8 @@ static const struct stmmac_ops dwmac4_ops = { .core_init = dwmac4_core_init, .rx_ipc = dwmac4_rx_ipc_enable, .rx_queue_enable = dwmac4_rx_queue_enable, + .prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms, + .prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms, .dump_regs = dwmac4_dump_regs, .host_irq_status = dwmac4_irq_status, .flow_ctrl = dwmac4_flow_ctrl, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 78f6ec2d165b..064fa0e6b536 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1647,6 +1647,31 @@ static void stmmac_init_tx_coalesce(struct stmmac_priv *priv) add_timer(&priv->txtimer); } +/** + * stmmac_mtl_configuration - Configure MTL + * @priv: driver private structure + * Description: It is used for configurring MTL + */ +static void stmmac_mtl_configuration(struct stmmac_priv *priv) +{ + u32 rx_queues_count = priv->plat->rx_queues_to_use; + u32 tx_queues_count = priv->plat->tx_queues_to_use; + + /* Configure MTL RX algorithms */ + if (rx_queues_count > 1 && priv->hw->mac->prog_mtl_rx_algorithms) + priv->hw->mac->prog_mtl_rx_algorithms(priv->hw, + priv->plat->rx_sched_algorithm); + + /* Configure MTL TX algorithms */ + if (tx_queues_count > 1 && priv->hw->mac->prog_mtl_tx_algorithms) + priv->hw->mac->prog_mtl_tx_algorithms(priv->hw, + priv->plat->tx_sched_algorithm); + + /* Enable MAC RX Queues */ + if (rx_queues_count > 1 && priv->hw->mac->rx_queue_enable) + stmmac_mac_enable_rx_queues(priv); +} + /** * stmmac_hw_setup - setup mac in a usable state. * @dev : pointer to the device structure. @@ -1691,9 +1716,9 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) /* Initialize the MAC Core */ priv->hw->mac->core_init(priv->hw, dev->mtu); - /* Initialize MAC RX Queues */ - if (priv->hw->mac->rx_queue_enable) - stmmac_mac_enable_rx_queues(priv); + /* Initialize MTL*/ + if (priv->synopsys_id >= DWMAC_CORE_4_00) + stmmac_mtl_configuration(priv); ret = priv->hw->mac->rx_ipc(priv->hw); if (!ret) { -- cgit From 6a3a7193b2811537c64eb513e39473050a72f1a4 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Fri, 10 Mar 2017 18:24:53 +0000 Subject: net: stmmac: configure tx queue weight This patch adds TX queues weight programming. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 3 +++ drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 7 +++++++ drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 12 ++++++++++++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 20 ++++++++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 31d3324df6d4..4eeaa5c905f2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -462,6 +462,9 @@ struct stmmac_ops { void (*prog_mtl_rx_algorithms)(struct mac_device_info *hw, u32 rx_alg); /* Program TX Algorithms */ void (*prog_mtl_tx_algorithms)(struct mac_device_info *hw, u32 tx_alg); + /* Set MTL TX queues weight */ + void (*set_mtl_tx_queue_weight)(struct mac_device_info *hw, + u32 weight, u32 queue); /* Dump MAC registers */ void (*dump_regs)(struct mac_device_info *hw, u32 *reg_space); /* Handle extra events on specific interrupts hw dependent */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index bc994fe3e6bf..89824fe484f6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -226,6 +226,13 @@ enum power_event { #define MTL_OP_MODE_RTC_96 (2 << MTL_OP_MODE_RTC_SHIFT) #define MTL_OP_MODE_RTC_128 (3 << MTL_OP_MODE_RTC_SHIFT) +/* MTL Queue Quantum Weight */ +#define MTL_TXQ_WEIGHT_BASE_ADDR 0x00000d18 +#define MTL_TXQ_WEIGHT_BASE_OFFSET 0x40 +#define MTL_TXQX_WEIGHT_BASE_ADDR(x) (MTL_TXQ_WEIGHT_BASE_ADDR + \ + ((x) * MTL_TXQ_WEIGHT_BASE_OFFSET)) +#define MTL_TXQ_WEIGHT_ISCQW_MASK GENMASK(20, 0) + /* MTL debug */ #define MTL_DEBUG_TXSTSFSTS BIT(5) #define MTL_DEBUG_TXFSTS BIT(4) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index f96675567192..fda6cfa7aba4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -116,6 +116,17 @@ static void dwmac4_prog_mtl_tx_algorithms(struct mac_device_info *hw, } } +static void dwmac4_set_mtl_tx_queue_weight(struct mac_device_info *hw, + u32 weight, u32 queue) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value = readl(ioaddr + MTL_TXQX_WEIGHT_BASE_ADDR(queue)); + + value &= ~MTL_TXQ_WEIGHT_ISCQW_MASK; + value |= weight & MTL_TXQ_WEIGHT_ISCQW_MASK; + writel(value, ioaddr + MTL_TXQX_WEIGHT_BASE_ADDR(queue)); +} + static void dwmac4_dump_regs(struct mac_device_info *hw, u32 *reg_space) { void __iomem *ioaddr = hw->pcsr; @@ -505,6 +516,7 @@ static const struct stmmac_ops dwmac4_ops = { .rx_queue_enable = dwmac4_rx_queue_enable, .prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms, .prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms, + .set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight, .dump_regs = dwmac4_dump_regs, .host_irq_status = dwmac4_irq_status, .flow_ctrl = dwmac4_flow_ctrl, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 064fa0e6b536..7b05cd3fce1c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1647,6 +1647,23 @@ static void stmmac_init_tx_coalesce(struct stmmac_priv *priv) add_timer(&priv->txtimer); } +/** + * stmmac_set_tx_queue_weight - Set TX queue weight + * @priv: driver private structure + * Description: It is used for setting TX queues weight + */ +static void stmmac_set_tx_queue_weight(struct stmmac_priv *priv) +{ + u32 tx_queues_count = priv->plat->tx_queues_to_use; + u32 weight; + u32 queue; + + for (queue = 0; queue < tx_queues_count; queue++) { + weight = priv->plat->tx_queues_cfg[queue].weight; + priv->hw->mac->set_mtl_tx_queue_weight(priv->hw, weight, queue); + } +} + /** * stmmac_mtl_configuration - Configure MTL * @priv: driver private structure @@ -1657,6 +1674,9 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv) u32 rx_queues_count = priv->plat->rx_queues_to_use; u32 tx_queues_count = priv->plat->tx_queues_to_use; + if (tx_queues_count > 1 && priv->hw->mac->set_mtl_tx_queue_weight) + stmmac_set_tx_queue_weight(priv); + /* Configure MTL RX algorithms */ if (rx_queues_count > 1 && priv->hw->mac->prog_mtl_rx_algorithms) priv->hw->mac->prog_mtl_rx_algorithms(priv->hw, -- cgit From 4f6046f5872c7db6a152b923cee21c37a04d56b8 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Fri, 10 Mar 2017 18:24:54 +0000 Subject: net: stmmac: mtl rx queue enabled as dcb or avb This patch introduces the enabling of RX queues as DCB or as AVB based on configuration. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 2 +- drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 8 ++++++-- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 19 +++++++------------ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 4eeaa5c905f2..f61611c8e8b0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -457,7 +457,7 @@ struct stmmac_ops { /* Enable and verify that the IPC module is supported */ int (*rx_ipc)(struct mac_device_info *hw); /* Enable RX Queues */ - void (*rx_queue_enable)(struct mac_device_info *hw, u32 queue); + void (*rx_queue_enable)(struct mac_device_info *hw, u8 mode, u32 queue); /* Program RX Algorithms */ void (*prog_mtl_rx_algorithms)(struct mac_device_info *hw, u32 rx_alg); /* Program TX Algorithms */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index fda6cfa7aba4..21a696eda9ce 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -59,13 +59,17 @@ static void dwmac4_core_init(struct mac_device_info *hw, int mtu) writel(value, ioaddr + GMAC_INT_EN); } -static void dwmac4_rx_queue_enable(struct mac_device_info *hw, u32 queue) +static void dwmac4_rx_queue_enable(struct mac_device_info *hw, + u8 mode, u32 queue) { void __iomem *ioaddr = hw->pcsr; u32 value = readl(ioaddr + GMAC_RXQ_CTRL0); value &= GMAC_RX_QUEUE_CLEAR(queue); - value |= GMAC_RX_AV_QUEUE_ENABLE(queue); + if (mode == MTL_RX_AVB) + value |= GMAC_RX_AV_QUEUE_ENABLE(queue); + else if (mode == MTL_RX_DCB) + value |= GMAC_RX_DCB_QUEUE_ENABLE(queue); writel(value, ioaddr + GMAC_RXQ_CTRL0); } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 7b05cd3fce1c..43036dabb9f9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1256,19 +1256,14 @@ static void free_dma_desc_resources(struct stmmac_priv *priv) */ static void stmmac_mac_enable_rx_queues(struct stmmac_priv *priv) { - int rx_count = priv->dma_cap.number_rx_queues; - int queue = 0; - - /* If GMAC does not have multiple queues, then this is not necessary*/ - if (rx_count == 1) - return; + u32 rx_queues_count = priv->plat->rx_queues_to_use; + int queue; + u8 mode; - /** - * If the core is synthesized with multiple rx queues / multiple - * dma channels, then rx queues will be disabled by default. - * For now only rx queue 0 is enabled. - */ - priv->hw->mac->rx_queue_enable(priv->hw, queue); + for (queue = 0; queue < rx_queues_count; queue++) { + mode = priv->plat->rx_queues_cfg[queue].mode_to_use; + priv->hw->mac->rx_queue_enable(priv->hw, mode, queue); + } } /** -- cgit From d43042f4da3e1c2e4ccac3b1d9153cb0798533a4 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Fri, 10 Mar 2017 18:24:55 +0000 Subject: net: stmmac: mapping mtl rx to dma channel This patch adds the functionality of RX queue to dma channel mapping based on configuration. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 2 ++ drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 7 +++++++ drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 25 +++++++++++++++++++++++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 21 +++++++++++++++++++ 4 files changed, 55 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index f61611c8e8b0..7d23e3dc7797 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -465,6 +465,8 @@ struct stmmac_ops { /* Set MTL TX queues weight */ void (*set_mtl_tx_queue_weight)(struct mac_device_info *hw, u32 weight, u32 queue); + /* RX MTL queue to RX dma mapping */ + void (*map_mtl_to_dma)(struct mac_device_info *hw, u32 queue, u32 chan); /* Dump MAC registers */ void (*dump_regs)(struct mac_device_info *hw, u32 *reg_space); /* Handle extra events on specific interrupts hw dependent */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 89824fe484f6..773f25b8e403 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -177,6 +177,13 @@ enum power_event { #define MTL_INT_STATUS 0x00000c20 #define MTL_INT_Q0 BIT(0) +#define MTL_RXQ_DMA_MAP0 0x00000c30 /* queue 0 to 3 */ +#define MTL_RXQ_DMA_MAP1 0x00000c34 /* queue 4 to 7 */ +#define MTL_RXQ_DMA_Q04MDMACH_MASK GENMASK(3, 0) +#define MTL_RXQ_DMA_Q04MDMACH(x) ((x) << 0) +#define MTL_RXQ_DMA_QXMDMACH_MASK(x) GENMASK(11 + (8 * ((x) - 1)), 8 * (x)) +#define MTL_RXQ_DMA_QXMDMACH(chan, q) ((chan) << (8 * (q))) + #define MTL_CHAN_BASE_ADDR 0x00000d00 #define MTL_CHAN_BASE_OFFSET 0x40 #define MTL_CHANX_BASE_ADDR(x) (MTL_CHAN_BASE_ADDR + \ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 21a696eda9ce..e9b153f75538 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -131,6 +131,30 @@ static void dwmac4_set_mtl_tx_queue_weight(struct mac_device_info *hw, writel(value, ioaddr + MTL_TXQX_WEIGHT_BASE_ADDR(queue)); } +static void dwmac4_map_mtl_dma(struct mac_device_info *hw, u32 queue, u32 chan) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value; + + if (queue < 4) + value = readl(ioaddr + MTL_RXQ_DMA_MAP0); + else + value = readl(ioaddr + MTL_RXQ_DMA_MAP1); + + if (queue == 0 || queue == 4) { + value &= ~MTL_RXQ_DMA_Q04MDMACH_MASK; + value |= MTL_RXQ_DMA_Q04MDMACH(chan); + } else { + value &= ~MTL_RXQ_DMA_QXMDMACH_MASK(queue); + value |= MTL_RXQ_DMA_QXMDMACH(chan, queue); + } + + if (queue < 4) + writel(value, ioaddr + MTL_RXQ_DMA_MAP0); + else + writel(value, ioaddr + MTL_RXQ_DMA_MAP1); +} + static void dwmac4_dump_regs(struct mac_device_info *hw, u32 *reg_space) { void __iomem *ioaddr = hw->pcsr; @@ -521,6 +545,7 @@ static const struct stmmac_ops dwmac4_ops = { .prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms, .prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms, .set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight, + .map_mtl_to_dma = dwmac4_map_mtl_dma, .dump_regs = dwmac4_dump_regs, .host_irq_status = dwmac4_irq_status, .flow_ctrl = dwmac4_flow_ctrl, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 43036dabb9f9..e1d5fe7f54a9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1659,6 +1659,23 @@ static void stmmac_set_tx_queue_weight(struct stmmac_priv *priv) } } +/** + * stmmac_rx_queue_dma_chan_map - Map RX queue to RX dma channel + * @priv: driver private structure + * Description: It is used for mapping RX queues to RX dma channels + */ +static void stmmac_rx_queue_dma_chan_map(struct stmmac_priv *priv) +{ + u32 rx_queues_count = priv->plat->rx_queues_to_use; + u32 queue; + u32 chan; + + for (queue = 0; queue < rx_queues_count; queue++) { + chan = priv->plat->rx_queues_cfg[queue].chan; + priv->hw->mac->map_mtl_to_dma(priv->hw, queue, chan); + } +} + /** * stmmac_mtl_configuration - Configure MTL * @priv: driver private structure @@ -1682,6 +1699,10 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv) priv->hw->mac->prog_mtl_tx_algorithms(priv->hw, priv->plat->tx_sched_algorithm); + /* Map RX MTL to DMA channels */ + if (rx_queues_count > 1 && priv->hw->mac->map_mtl_to_dma) + stmmac_rx_queue_dma_chan_map(priv); + /* Enable MAC RX Queues */ if (rx_queues_count > 1 && priv->hw->mac->rx_queue_enable) stmmac_mac_enable_rx_queues(priv); -- cgit From 29feff39000bb57fe6c25f5ec1435489196c8838 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Fri, 10 Mar 2017 18:24:56 +0000 Subject: net: stmmac: flow_ctrl functions adapted to mtl This patch adapts flow_ctrl function to prepare it for multiple queues. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 2 +- drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c | 3 ++- drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c | 3 ++- drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 20 +++++++++++++------- drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 3 ++- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 17 ++++++++++++++--- 6 files changed, 34 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 7d23e3dc7797..7a175e9893d1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -476,7 +476,7 @@ struct stmmac_ops { void (*set_filter)(struct mac_device_info *hw, struct net_device *dev); /* Flow control setting */ void (*flow_ctrl)(struct mac_device_info *hw, unsigned int duplex, - unsigned int fc, unsigned int pause_time); + unsigned int fc, unsigned int pause_time, u32 tx_cnt); /* Set power management mode (e.g. magic frame) */ void (*pmt)(struct mac_device_info *hw, unsigned long mode); /* Set/Get Unicast MAC addresses */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index 19b9b3087099..3a95ad9357e3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -216,7 +216,8 @@ static void dwmac1000_set_filter(struct mac_device_info *hw, static void dwmac1000_flow_ctrl(struct mac_device_info *hw, unsigned int duplex, - unsigned int fc, unsigned int pause_time) + unsigned int fc, unsigned int pause_time, + u32 tx_cnt) { void __iomem *ioaddr = hw->pcsr; /* Set flow such that DZPQ in Mac Register 6 is 0, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c index e370ccec6176..524135e6dd89 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c @@ -131,7 +131,8 @@ static void dwmac100_set_filter(struct mac_device_info *hw, } static void dwmac100_flow_ctrl(struct mac_device_info *hw, unsigned int duplex, - unsigned int fc, unsigned int pause_time) + unsigned int fc, unsigned int pause_time, + u32 tx_cnt) { void __iomem *ioaddr = hw->pcsr; unsigned int flow = MAC_FLOW_CTRL_ENABLE; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index e9b153f75538..3069defb7eb1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -336,11 +336,12 @@ static void dwmac4_set_filter(struct mac_device_info *hw, } static void dwmac4_flow_ctrl(struct mac_device_info *hw, unsigned int duplex, - unsigned int fc, unsigned int pause_time) + unsigned int fc, unsigned int pause_time, + u32 tx_cnt) { void __iomem *ioaddr = hw->pcsr; - u32 channel = STMMAC_CHAN0; /* FIXME */ unsigned int flow = 0; + u32 queue = 0; pr_debug("GMAC Flow-Control:\n"); if (fc & FLOW_RX) { @@ -350,13 +351,18 @@ static void dwmac4_flow_ctrl(struct mac_device_info *hw, unsigned int duplex, } if (fc & FLOW_TX) { pr_debug("\tTransmit Flow-Control ON\n"); - flow |= GMAC_TX_FLOW_CTRL_TFE; - writel(flow, ioaddr + GMAC_QX_TX_FLOW_CTRL(channel)); - if (duplex) { + if (duplex) pr_debug("\tduplex mode: PAUSE %d\n", pause_time); - flow |= (pause_time << GMAC_TX_FLOW_CTRL_PT_SHIFT); - writel(flow, ioaddr + GMAC_QX_TX_FLOW_CTRL(channel)); + + for (queue = 0; queue < tx_cnt; queue++) { + flow |= GMAC_TX_FLOW_CTRL_TFE; + + if (duplex) + flow |= + (pause_time << GMAC_TX_FLOW_CTRL_PT_SHIFT); + + writel(flow, ioaddr + GMAC_QX_TX_FLOW_CTRL(queue)); } } } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 85d64114e159..4a5dc896044f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -481,6 +481,7 @@ stmmac_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct stmmac_priv *priv = netdev_priv(netdev); + u32 tx_cnt = priv->plat->tx_queues_to_use; struct phy_device *phy = netdev->phydev; int new_pause = FLOW_OFF; @@ -511,7 +512,7 @@ stmmac_set_pauseparam(struct net_device *netdev, } priv->hw->mac->flow_ctrl(priv->hw, phy->duplex, priv->flow_ctrl, - priv->pause); + priv->pause, tx_cnt); return 0; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index e1d5fe7f54a9..f453fc5a2eda 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -672,6 +672,19 @@ static void stmmac_release_ptp(struct stmmac_priv *priv) stmmac_ptp_unregister(priv); } +/** + * stmmac_mac_flow_ctrl - Configure flow control in all queues + * @priv: driver private structure + * Description: It is used for configuring the flow control in all queues + */ +static void stmmac_mac_flow_ctrl(struct stmmac_priv *priv, u32 duplex) +{ + u32 tx_cnt = priv->plat->tx_queues_to_use; + + priv->hw->mac->flow_ctrl(priv->hw, duplex, priv->flow_ctrl, + priv->pause, tx_cnt); +} + /** * stmmac_adjust_link - adjusts the link parameters * @dev: net device structure @@ -687,7 +700,6 @@ static void stmmac_adjust_link(struct net_device *dev) struct phy_device *phydev = dev->phydev; unsigned long flags; int new_state = 0; - unsigned int fc = priv->flow_ctrl, pause_time = priv->pause; if (!phydev) return; @@ -709,8 +721,7 @@ static void stmmac_adjust_link(struct net_device *dev) } /* Flow Control operation */ if (phydev->pause) - priv->hw->mac->flow_ctrl(priv->hw, phydev->duplex, - fc, pause_time); + stmmac_mac_flow_ctrl(priv, phydev->duplex); if (phydev->speed != priv->speed) { new_state = 1; -- cgit From 8f71a88d3619db0abbca3bd18ba15a479a5abf1b Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Fri, 10 Mar 2017 18:24:57 +0000 Subject: net: stmmac: prepare irq_status for mtl This patch prepares mac irq status treatment for multiple queues. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 2 ++ drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 2 +- drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 40 ++++++++++++++--------- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 5 +++ 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 7a175e9893d1..23c9e2a1bb6f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -472,6 +472,8 @@ struct stmmac_ops { /* Handle extra events on specific interrupts hw dependent */ int (*host_irq_status)(struct mac_device_info *hw, struct stmmac_extra_stats *x); + /* Handle MTL interrupts */ + int (*host_mtl_irq_status)(struct mac_device_info *hw, u32 chan); /* Multicast filter setting */ void (*set_filter)(struct mac_device_info *hw, struct net_device *dev); /* Flow control setting */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 773f25b8e403..1ddf75c0eab5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -175,7 +175,7 @@ enum power_event { #define MTL_OPERATION_RAA_WSP (0x1 << 2) #define MTL_INT_STATUS 0x00000c20 -#define MTL_INT_Q0 BIT(0) +#define MTL_INT_QX(x) BIT(x) #define MTL_RXQ_DMA_MAP0 0x00000c30 /* queue 0 to 3 */ #define MTL_RXQ_DMA_MAP1 0x00000c34 /* queue 4 to 7 */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 3069defb7eb1..f0f2dce79d09 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -416,11 +416,34 @@ static void dwmac4_phystatus(void __iomem *ioaddr, struct stmmac_extra_stats *x) } } +static int dwmac4_irq_mtl_status(struct mac_device_info *hw, u32 chan) +{ + void __iomem *ioaddr = hw->pcsr; + u32 mtl_int_qx_status; + int ret = 0; + + mtl_int_qx_status = readl(ioaddr + MTL_INT_STATUS); + + /* Check MTL Interrupt */ + if (mtl_int_qx_status & MTL_INT_QX(chan)) { + /* read Queue x Interrupt status */ + u32 status = readl(ioaddr + MTL_CHAN_INT_CTRL(chan)); + + if (status & MTL_RX_OVERFLOW_INT) { + /* clear Interrupt */ + writel(status | MTL_RX_OVERFLOW_INT, + ioaddr + MTL_CHAN_INT_CTRL(chan)); + ret = CORE_IRQ_MTL_RX_OVERFLOW; + } + } + + return ret; +} + static int dwmac4_irq_status(struct mac_device_info *hw, struct stmmac_extra_stats *x) { void __iomem *ioaddr = hw->pcsr; - u32 mtl_int_qx_status; u32 intr_status; int ret = 0; @@ -439,20 +462,6 @@ static int dwmac4_irq_status(struct mac_device_info *hw, x->irq_receive_pmt_irq_n++; } - mtl_int_qx_status = readl(ioaddr + MTL_INT_STATUS); - /* Check MTL Interrupt: Currently only one queue is used: Q0. */ - if (mtl_int_qx_status & MTL_INT_Q0) { - /* read Queue 0 Interrupt status */ - u32 status = readl(ioaddr + MTL_CHAN_INT_CTRL(STMMAC_CHAN0)); - - if (status & MTL_RX_OVERFLOW_INT) { - /* clear Interrupt */ - writel(status | MTL_RX_OVERFLOW_INT, - ioaddr + MTL_CHAN_INT_CTRL(STMMAC_CHAN0)); - ret = CORE_IRQ_MTL_RX_OVERFLOW; - } - } - dwmac_pcs_isr(ioaddr, GMAC_PCS_BASE, intr_status, x); if (intr_status & PCS_RGSMIIIS_IRQ) dwmac4_phystatus(ioaddr, x); @@ -554,6 +563,7 @@ static const struct stmmac_ops dwmac4_ops = { .map_mtl_to_dma = dwmac4_map_mtl_dma, .dump_regs = dwmac4_dump_regs, .host_irq_status = dwmac4_irq_status, + .host_mtl_irq_status = dwmac4_irq_mtl_status, .flow_ctrl = dwmac4_flow_ctrl, .pmt = dwmac4_pmt, .set_umac_addr = dwmac4_set_umac_addr, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index f453fc5a2eda..2449487be534 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2901,6 +2901,11 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) if ((priv->plat->has_gmac) || (priv->plat->has_gmac4)) { int status = priv->hw->mac->host_irq_status(priv->hw, &priv->xstats); + + if (priv->synopsys_id >= DWMAC_CORE_4_00) + status |= priv->hw->mac->host_mtl_irq_status(priv->hw, + STMMAC_CHAN0); + if (unlikely(status)) { /* For LPI we need to save the tx status */ if (status & CORE_IRQ_TX_PATH_IN_LPI_MODE) -- cgit From ad5a87d7e3c5fcea987728027a3fae2c2f067173 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Fri, 10 Mar 2017 18:24:58 +0000 Subject: net: stmmac: mac debug prepared for multiple queues This patch prepares mac debug dump for multiple queues. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 3 +- .../net/ethernet/stmicro/stmmac/dwmac1000_core.c | 3 +- drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 107 +++++++++++---------- .../net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 5 +- 4 files changed, 64 insertions(+), 54 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 23c9e2a1bb6f..e7fb98569d2d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -491,7 +491,8 @@ struct stmmac_ops { void (*reset_eee_mode)(struct mac_device_info *hw); void (*set_eee_timer)(struct mac_device_info *hw, int ls, int tw); void (*set_eee_pls)(struct mac_device_info *hw, int link); - void (*debug)(void __iomem *ioaddr, struct stmmac_extra_stats *x); + void (*debug)(void __iomem *ioaddr, struct stmmac_extra_stats *x, + u32 rx_queues, u32 tx_queues); /* PCS calls */ void (*pcs_ctrl_ane)(void __iomem *ioaddr, bool ane, bool srgmi_ral, bool loopback); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index 3a95ad9357e3..7f78f7746a5b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -413,7 +413,8 @@ static void dwmac1000_get_adv_lp(void __iomem *ioaddr, struct rgmii_adv *adv) dwmac_get_adv_lp(ioaddr, GMAC_PCS_BASE, adv); } -static void dwmac1000_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x) +static void dwmac1000_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x, + u32 rx_queues, u32 tx_queues) { u32 value = readl(ioaddr + GMAC_DEBUG); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index f0f2dce79d09..670cfee415fd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -469,64 +469,69 @@ static int dwmac4_irq_status(struct mac_device_info *hw, return ret; } -static void dwmac4_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x) +static void dwmac4_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x, + u32 rx_queues, u32 tx_queues) { u32 value; - - /* Currently only channel 0 is supported */ - value = readl(ioaddr + MTL_CHAN_TX_DEBUG(STMMAC_CHAN0)); - - if (value & MTL_DEBUG_TXSTSFSTS) - x->mtl_tx_status_fifo_full++; - if (value & MTL_DEBUG_TXFSTS) - x->mtl_tx_fifo_not_empty++; - if (value & MTL_DEBUG_TWCSTS) - x->mmtl_fifo_ctrl++; - if (value & MTL_DEBUG_TRCSTS_MASK) { - u32 trcsts = (value & MTL_DEBUG_TRCSTS_MASK) - >> MTL_DEBUG_TRCSTS_SHIFT; - if (trcsts == MTL_DEBUG_TRCSTS_WRITE) - x->mtl_tx_fifo_read_ctrl_write++; - else if (trcsts == MTL_DEBUG_TRCSTS_TXW) - x->mtl_tx_fifo_read_ctrl_wait++; - else if (trcsts == MTL_DEBUG_TRCSTS_READ) - x->mtl_tx_fifo_read_ctrl_read++; - else - x->mtl_tx_fifo_read_ctrl_idle++; + u32 queue; + + for (queue = 0; queue < tx_queues; queue++) { + value = readl(ioaddr + MTL_CHAN_TX_DEBUG(queue)); + + if (value & MTL_DEBUG_TXSTSFSTS) + x->mtl_tx_status_fifo_full++; + if (value & MTL_DEBUG_TXFSTS) + x->mtl_tx_fifo_not_empty++; + if (value & MTL_DEBUG_TWCSTS) + x->mmtl_fifo_ctrl++; + if (value & MTL_DEBUG_TRCSTS_MASK) { + u32 trcsts = (value & MTL_DEBUG_TRCSTS_MASK) + >> MTL_DEBUG_TRCSTS_SHIFT; + if (trcsts == MTL_DEBUG_TRCSTS_WRITE) + x->mtl_tx_fifo_read_ctrl_write++; + else if (trcsts == MTL_DEBUG_TRCSTS_TXW) + x->mtl_tx_fifo_read_ctrl_wait++; + else if (trcsts == MTL_DEBUG_TRCSTS_READ) + x->mtl_tx_fifo_read_ctrl_read++; + else + x->mtl_tx_fifo_read_ctrl_idle++; + } + if (value & MTL_DEBUG_TXPAUSED) + x->mac_tx_in_pause++; } - if (value & MTL_DEBUG_TXPAUSED) - x->mac_tx_in_pause++; - value = readl(ioaddr + MTL_CHAN_RX_DEBUG(STMMAC_CHAN0)); + for (queue = 0; queue < rx_queues; queue++) { + value = readl(ioaddr + MTL_CHAN_RX_DEBUG(queue)); - if (value & MTL_DEBUG_RXFSTS_MASK) { - u32 rxfsts = (value & MTL_DEBUG_RXFSTS_MASK) - >> MTL_DEBUG_RRCSTS_SHIFT; + if (value & MTL_DEBUG_RXFSTS_MASK) { + u32 rxfsts = (value & MTL_DEBUG_RXFSTS_MASK) + >> MTL_DEBUG_RRCSTS_SHIFT; - if (rxfsts == MTL_DEBUG_RXFSTS_FULL) - x->mtl_rx_fifo_fill_level_full++; - else if (rxfsts == MTL_DEBUG_RXFSTS_AT) - x->mtl_rx_fifo_fill_above_thresh++; - else if (rxfsts == MTL_DEBUG_RXFSTS_BT) - x->mtl_rx_fifo_fill_below_thresh++; - else - x->mtl_rx_fifo_fill_level_empty++; - } - if (value & MTL_DEBUG_RRCSTS_MASK) { - u32 rrcsts = (value & MTL_DEBUG_RRCSTS_MASK) >> - MTL_DEBUG_RRCSTS_SHIFT; - - if (rrcsts == MTL_DEBUG_RRCSTS_FLUSH) - x->mtl_rx_fifo_read_ctrl_flush++; - else if (rrcsts == MTL_DEBUG_RRCSTS_RSTAT) - x->mtl_rx_fifo_read_ctrl_read_data++; - else if (rrcsts == MTL_DEBUG_RRCSTS_RDATA) - x->mtl_rx_fifo_read_ctrl_status++; - else - x->mtl_rx_fifo_read_ctrl_idle++; + if (rxfsts == MTL_DEBUG_RXFSTS_FULL) + x->mtl_rx_fifo_fill_level_full++; + else if (rxfsts == MTL_DEBUG_RXFSTS_AT) + x->mtl_rx_fifo_fill_above_thresh++; + else if (rxfsts == MTL_DEBUG_RXFSTS_BT) + x->mtl_rx_fifo_fill_below_thresh++; + else + x->mtl_rx_fifo_fill_level_empty++; + } + if (value & MTL_DEBUG_RRCSTS_MASK) { + u32 rrcsts = (value & MTL_DEBUG_RRCSTS_MASK) >> + MTL_DEBUG_RRCSTS_SHIFT; + + if (rrcsts == MTL_DEBUG_RRCSTS_FLUSH) + x->mtl_rx_fifo_read_ctrl_flush++; + else if (rrcsts == MTL_DEBUG_RRCSTS_RSTAT) + x->mtl_rx_fifo_read_ctrl_read_data++; + else if (rrcsts == MTL_DEBUG_RRCSTS_RDATA) + x->mtl_rx_fifo_read_ctrl_status++; + else + x->mtl_rx_fifo_read_ctrl_idle++; + } + if (value & MTL_DEBUG_RWCSTS) + x->mtl_rx_fifo_ctrl_active++; } - if (value & MTL_DEBUG_RWCSTS) - x->mtl_rx_fifo_ctrl_active++; /* GMAC debug */ value = readl(ioaddr + GMAC_DEBUG); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 4a5dc896044f..61b9369a041e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -520,6 +520,8 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 *data) { struct stmmac_priv *priv = netdev_priv(dev); + u32 rx_queues_count = priv->plat->rx_queues_to_use; + u32 tx_queues_count = priv->plat->tx_queues_to_use; int i, j = 0; /* Update the DMA HW counters for dwmac10/100 */ @@ -550,7 +552,8 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, if ((priv->hw->mac->debug) && (priv->synopsys_id >= DWMAC_CORE_3_50)) priv->hw->mac->debug(priv->ioaddr, - (void *)&priv->xstats); + (void *)&priv->xstats, + rx_queues_count, tx_queues_count); } for (i = 0; i < STMMAC_STATS_LEN; i++) { char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset; -- cgit From 19d9187317979cf0c25f67017d2676149abc46b2 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Fri, 10 Mar 2017 18:24:59 +0000 Subject: net: stmmac: configuration of CBS in case of a TX AVB queue This patch adds the configuration of the AVB Credit-Based Shaper. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 22 +++++++++-- drivers/net/ethernet/stmicro/stmmac/common.h | 4 ++ drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 33 ++++++++++++++++ drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 46 +++++++++++++++++++++- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 29 ++++++++++++++ .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 29 ++++++++++++-- include/linux/stmmac.h | 12 ++++-- 7 files changed, 164 insertions(+), 11 deletions(-) diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index 04a258e2d4e0..a7b0e41cb264 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -92,8 +92,15 @@ Optional properties: - snps,tx-sched-dwrr: Deficit Weighted Round Robin - snps,tx-sched-sp: Strict priority - For each TX queue - - snps,weight: TX queue weight (if using a weighted algorithm) - + - snps,weight: TX queue weight (if using a DCB weight algorithm) + - Choose one of these modes: + - snps,dcb-algorithm: TX queue will be working in DCB + - snps,avb-algorithm: TX queue will be working in AVB + - Configure Credit Base Shaper (if AVB Mode selected): + - snps,send_slope: enable Low Power Interface + - snps,idle_slope: unlock on WoL + - snps,high_credit: max write outstanding req. limit + - snps,low_credit: max read outstanding req. limit Examples: stmmac_axi_setup: stmmac-axi-config { @@ -112,10 +119,19 @@ Examples: }; mtl_tx_setup: tx-queues-config { - snps,tx-queues-to-use = <1>; + snps,tx-queues-to-use = <2>; snps,tx-sched-wrr; queue0 { snps,weight = <0x10>; + snps,dcb-algorithm; + }; + + queue1 { + snps,avb-algorithm; + snps,send_slope = <0x1000>; + snps,idle_slope = <0x1000>; + snps,high_credit = <0x3E800>; + snps,low_credit = <0xFFC18000>; }; }; diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index e7fb98569d2d..9f0d26da6813 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -467,6 +467,10 @@ struct stmmac_ops { u32 weight, u32 queue); /* RX MTL queue to RX dma mapping */ void (*map_mtl_to_dma)(struct mac_device_info *hw, u32 queue, u32 chan); + /* Configure AV Algorithm */ + void (*config_cbs)(struct mac_device_info *hw, u32 send_slope, + u32 idle_slope, u32 high_credit, u32 low_credit, + u32 queue); /* Dump MAC registers */ void (*dump_regs)(struct mac_device_info *hw, u32 *reg_space); /* Handle extra events on specific interrupts hw dependent */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 1ddf75c0eab5..54bcdb4d10db 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -233,6 +233,15 @@ enum power_event { #define MTL_OP_MODE_RTC_96 (2 << MTL_OP_MODE_RTC_SHIFT) #define MTL_OP_MODE_RTC_128 (3 << MTL_OP_MODE_RTC_SHIFT) +/* MTL ETS Control register */ +#define MTL_ETS_CTRL_BASE_ADDR 0x00000d10 +#define MTL_ETS_CTRL_BASE_OFFSET 0x40 +#define MTL_ETSX_CTRL_BASE_ADDR(x) (MTL_ETS_CTRL_BASE_ADDR + \ + ((x) * MTL_ETS_CTRL_BASE_OFFSET)) + +#define MTL_ETS_CTRL_CC BIT(3) +#define MTL_ETS_CTRL_AVALG BIT(2) + /* MTL Queue Quantum Weight */ #define MTL_TXQ_WEIGHT_BASE_ADDR 0x00000d18 #define MTL_TXQ_WEIGHT_BASE_OFFSET 0x40 @@ -240,6 +249,30 @@ enum power_event { ((x) * MTL_TXQ_WEIGHT_BASE_OFFSET)) #define MTL_TXQ_WEIGHT_ISCQW_MASK GENMASK(20, 0) +/* MTL sendSlopeCredit register */ +#define MTL_SEND_SLP_CRED_BASE_ADDR 0x00000d1c +#define MTL_SEND_SLP_CRED_OFFSET 0x40 +#define MTL_SEND_SLP_CREDX_BASE_ADDR(x) (MTL_SEND_SLP_CRED_BASE_ADDR + \ + ((x) * MTL_SEND_SLP_CRED_OFFSET)) + +#define MTL_SEND_SLP_CRED_SSC_MASK GENMASK(13, 0) + +/* MTL hiCredit register */ +#define MTL_HIGH_CRED_BASE_ADDR 0x00000d20 +#define MTL_HIGH_CRED_OFFSET 0x40 +#define MTL_HIGH_CREDX_BASE_ADDR(x) (MTL_HIGH_CRED_BASE_ADDR + \ + ((x) * MTL_HIGH_CRED_OFFSET)) + +#define MTL_HIGH_CRED_HC_MASK GENMASK(28, 0) + +/* MTL loCredit register */ +#define MTL_LOW_CRED_BASE_ADDR 0x00000d24 +#define MTL_LOW_CRED_OFFSET 0x40 +#define MTL_LOW_CREDX_BASE_ADDR(x) (MTL_LOW_CRED_BASE_ADDR + \ + ((x) * MTL_LOW_CRED_OFFSET)) + +#define MTL_HIGH_CRED_LC_MASK GENMASK(28, 0) + /* MTL debug */ #define MTL_DEBUG_TXSTSFSTS BIT(5) #define MTL_DEBUG_TXFSTS BIT(4) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 670cfee415fd..10599dbc232f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -66,9 +66,9 @@ static void dwmac4_rx_queue_enable(struct mac_device_info *hw, u32 value = readl(ioaddr + GMAC_RXQ_CTRL0); value &= GMAC_RX_QUEUE_CLEAR(queue); - if (mode == MTL_RX_AVB) + if (mode == MTL_QUEUE_AVB) value |= GMAC_RX_AV_QUEUE_ENABLE(queue); - else if (mode == MTL_RX_DCB) + else if (mode == MTL_QUEUE_DCB) value |= GMAC_RX_DCB_QUEUE_ENABLE(queue); writel(value, ioaddr + GMAC_RXQ_CTRL0); @@ -155,6 +155,47 @@ static void dwmac4_map_mtl_dma(struct mac_device_info *hw, u32 queue, u32 chan) writel(value, ioaddr + MTL_RXQ_DMA_MAP1); } +static void dwmac4_config_cbs(struct mac_device_info *hw, + u32 send_slope, u32 idle_slope, + u32 high_credit, u32 low_credit, u32 queue) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value; + + pr_debug("Queue %d configured as AVB. Parameters:\n", queue); + pr_debug("\tsend_slope: 0x%08x\n", send_slope); + pr_debug("\tidle_slope: 0x%08x\n", idle_slope); + pr_debug("\thigh_credit: 0x%08x\n", high_credit); + pr_debug("\tlow_credit: 0x%08x\n", low_credit); + + /* enable AV algorithm */ + value = readl(ioaddr + MTL_ETSX_CTRL_BASE_ADDR(queue)); + value |= MTL_ETS_CTRL_AVALG; + value |= MTL_ETS_CTRL_CC; + writel(value, ioaddr + MTL_ETSX_CTRL_BASE_ADDR(queue)); + + /* configure send slope */ + value = readl(ioaddr + MTL_SEND_SLP_CREDX_BASE_ADDR(queue)); + value &= ~MTL_SEND_SLP_CRED_SSC_MASK; + value |= send_slope & MTL_SEND_SLP_CRED_SSC_MASK; + writel(value, ioaddr + MTL_SEND_SLP_CREDX_BASE_ADDR(queue)); + + /* configure idle slope (same register as tx weight) */ + dwmac4_set_mtl_tx_queue_weight(hw, idle_slope, queue); + + /* configure high credit */ + value = readl(ioaddr + MTL_HIGH_CREDX_BASE_ADDR(queue)); + value &= ~MTL_HIGH_CRED_HC_MASK; + value |= high_credit & MTL_HIGH_CRED_HC_MASK; + writel(value, ioaddr + MTL_HIGH_CREDX_BASE_ADDR(queue)); + + /* configure high credit */ + value = readl(ioaddr + MTL_LOW_CREDX_BASE_ADDR(queue)); + value &= ~MTL_HIGH_CRED_LC_MASK; + value |= low_credit & MTL_HIGH_CRED_LC_MASK; + writel(value, ioaddr + MTL_LOW_CREDX_BASE_ADDR(queue)); +} + static void dwmac4_dump_regs(struct mac_device_info *hw, u32 *reg_space) { void __iomem *ioaddr = hw->pcsr; @@ -566,6 +607,7 @@ static const struct stmmac_ops dwmac4_ops = { .prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms, .set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight, .map_mtl_to_dma = dwmac4_map_mtl_dma, + .config_cbs = dwmac4_config_cbs, .dump_regs = dwmac4_dump_regs, .host_irq_status = dwmac4_irq_status, .host_mtl_irq_status = dwmac4_irq_mtl_status, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 2449487be534..915636ff2fc1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1670,6 +1670,31 @@ static void stmmac_set_tx_queue_weight(struct stmmac_priv *priv) } } +/** + * stmmac_configure_cbs - Configure CBS in TX queue + * @priv: driver private structure + * Description: It is used for configuring CBS in AVB TX queues + */ +static void stmmac_configure_cbs(struct stmmac_priv *priv) +{ + u32 tx_queues_count = priv->plat->tx_queues_to_use; + u32 mode_to_use; + u32 queue; + + for (queue = 0; queue < tx_queues_count; queue++) { + mode_to_use = priv->plat->tx_queues_cfg[queue].mode_to_use; + if (mode_to_use == MTL_QUEUE_DCB) + continue; + + priv->hw->mac->config_cbs(priv->hw, + priv->plat->tx_queues_cfg[queue].send_slope, + priv->plat->tx_queues_cfg[queue].idle_slope, + priv->plat->tx_queues_cfg[queue].high_credit, + priv->plat->tx_queues_cfg[queue].low_credit, + queue); + } +} + /** * stmmac_rx_queue_dma_chan_map - Map RX queue to RX dma channel * @priv: driver private structure @@ -1710,6 +1735,10 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv) priv->hw->mac->prog_mtl_tx_algorithms(priv->hw, priv->plat->tx_sched_algorithm); + /* Configure CBS in AVB TX queues */ + if (tx_queues_count > 1 && priv->hw->mac->config_cbs) + stmmac_configure_cbs(priv); + /* Map RX MTL to DMA channels */ if (rx_queues_count > 1 && priv->hw->mac->map_mtl_to_dma) stmmac_rx_queue_dma_chan_map(priv); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 0b76e3de502d..37f550ae76a5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -171,11 +171,11 @@ static void stmmac_mtl_setup(struct platform_device *pdev, break; if (of_property_read_bool(q_node, "snps,dcb-algorithm")) - plat->rx_queues_cfg[queue].mode_to_use = MTL_RX_DCB; + plat->rx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB; else if (of_property_read_bool(q_node, "snps,avb-algorithm")) - plat->rx_queues_cfg[queue].mode_to_use = MTL_RX_AVB; + plat->rx_queues_cfg[queue].mode_to_use = MTL_QUEUE_AVB; else - plat->rx_queues_cfg[queue].mode_to_use = MTL_RX_DCB; + plat->rx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB; if (of_property_read_u8(q_node, "snps,map-to-dma-channel", &plat->rx_queues_cfg[queue].chan)) @@ -212,6 +212,29 @@ static void stmmac_mtl_setup(struct platform_device *pdev, &plat->tx_queues_cfg[queue].weight)) plat->tx_queues_cfg[queue].weight = 0x10 + queue; + if (of_property_read_bool(q_node, "snps,dcb-algorithm")) { + plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB; + } else if (of_property_read_bool(q_node, + "snps,avb-algorithm")) { + plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_AVB; + + /* Credit Base Shaper parameters used by AVB */ + if (of_property_read_u32(q_node, "snps,send_slope", + &plat->tx_queues_cfg[queue].send_slope)) + plat->tx_queues_cfg[queue].send_slope = 0x0; + if (of_property_read_u32(q_node, "snps,idle_slope", + &plat->tx_queues_cfg[queue].idle_slope)) + plat->tx_queues_cfg[queue].idle_slope = 0x0; + if (of_property_read_u32(q_node, "snps,high_credit", + &plat->tx_queues_cfg[queue].high_credit)) + plat->tx_queues_cfg[queue].high_credit = 0x0; + if (of_property_read_u32(q_node, "snps,low_credit", + &plat->tx_queues_cfg[queue].low_credit)) + plat->tx_queues_cfg[queue].low_credit = 0x0; + } else { + plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB; + } + queue++; } diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 266ff2af91e5..be47b859e954 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -55,9 +55,9 @@ #define MTL_RX_ALGORITHM_SP 0x4 #define MTL_RX_ALGORITHM_WSP 0x5 -/* RX Queue Mode */ -#define MTL_RX_DCB 0x0 -#define MTL_RX_AVB 0x1 +/* RX/TX Queue Mode */ +#define MTL_QUEUE_DCB 0x0 +#define MTL_QUEUE_AVB 0x1 /* The MDC clock could be set higher than the IEEE 802.3 * specified frequency limit 0f 2.5 MHz, by programming a clock divider @@ -131,6 +131,12 @@ struct stmmac_rxq_cfg { struct stmmac_txq_cfg { u8 weight; + u8 mode_to_use; + /* Credit Base Shaper parameters */ + u32 send_slope; + u32 idle_slope; + u32 high_credit; + u32 low_credit; }; struct plat_stmmacenet_data { -- cgit From 79c12a752cea61d41fd2f95600eaaaaafb99fe9e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 10 Mar 2017 10:38:27 -0800 Subject: nfp: separate data path information from the reset of adapter structure Move all data path information into a separate structure. This way we will be able to allocate new data path with all new rings etc. and swap it in easily. No functional changes. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 105 ++-- .../net/ethernet/netronome/nfp/nfp_net_common.c | 602 +++++++++++---------- .../net/ethernet/netronome/nfp/nfp_net_debugfs.c | 4 +- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 64 +-- drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 30 +- .../net/ethernet/netronome/nfp/nfp_net_offload.c | 30 +- .../net/ethernet/netronome/nfp/nfp_netvf_main.c | 15 +- 7 files changed, 436 insertions(+), 414 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 34f8c439f42f..7d2c38604372 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -50,14 +50,14 @@ #include "nfp_net_ctrl.h" -#define nn_err(nn, fmt, args...) netdev_err((nn)->netdev, fmt, ## args) -#define nn_warn(nn, fmt, args...) netdev_warn((nn)->netdev, fmt, ## args) -#define nn_info(nn, fmt, args...) netdev_info((nn)->netdev, fmt, ## args) -#define nn_dbg(nn, fmt, args...) netdev_dbg((nn)->netdev, fmt, ## args) -#define nn_warn_ratelimit(nn, fmt, args...) \ +#define nn_err(nn, fmt, args...) netdev_err((nn)->dp.netdev, fmt, ## args) +#define nn_warn(nn, fmt, args...) netdev_warn((nn)->dp.netdev, fmt, ## args) +#define nn_info(nn, fmt, args...) netdev_info((nn)->dp.netdev, fmt, ## args) +#define nn_dbg(nn, fmt, args...) netdev_dbg((nn)->dp.netdev, fmt, ## args) +#define nn_dp_warn(dp, fmt, args...) \ do { \ if (unlikely(net_ratelimit())) \ - netdev_warn((nn)->netdev, fmt, ## args); \ + netdev_warn((dp)->netdev, fmt, ## args); \ } while (0) /* Max time to wait for NFP to respond on updates (in seconds) */ @@ -434,18 +434,62 @@ struct nfp_stat_pair { }; /** - * struct nfp_net - NFP network device structure + * struct nfp_net_dp - NFP network device datapath data structure * @dev: Backpointer to struct device - * @netdev: Backpointer to net_device structure - * @is_vf: Is the driver attached to a VF? + * @netdev: Backpointer to net_device structure + * @is_vf: Is the driver attached to a VF? * @bpf_offload_skip_sw: Offloaded BPF program will not be rerun by cls_bpf * @bpf_offload_xdp: Offloaded BPF program is XDP * @chained_metadata_format: Firemware will use new metadata format - * @ctrl: Local copy of the control register/word. - * @fl_bufsz: Currently configured size of the freelist buffers + * @ctrl: Local copy of the control register/word. + * @fl_bufsz: Currently configured size of the freelist buffers * @rx_offset: Offset in the RX buffers where packet data starts * @xdp_prog: Installed XDP program - * @fw_ver: Firmware version + * @tx_rings: Array of pre-allocated TX ring structures + * @rx_rings: Array of pre-allocated RX ring structures + * + * @txd_cnt: Size of the TX ring in number of descriptors + * @rxd_cnt: Size of the RX ring in number of descriptors + * @num_r_vecs: Number of used ring vectors + * @num_tx_rings: Currently configured number of TX rings + * @num_stack_tx_rings: Number of TX rings used by the stack (not XDP) + * @num_rx_rings: Currently configured number of RX rings + */ +struct nfp_net_dp { + struct device *dev; + struct net_device *netdev; + + unsigned is_vf:1; + unsigned bpf_offload_skip_sw:1; + unsigned bpf_offload_xdp:1; + unsigned chained_metadata_format:1; + + u32 ctrl; + u32 fl_bufsz; + + u32 rx_offset; + + struct bpf_prog *xdp_prog; + + struct nfp_net_tx_ring *tx_rings; + struct nfp_net_rx_ring *rx_rings; + + /* Cold data follows */ + + unsigned int txd_cnt; + unsigned int rxd_cnt; + + unsigned int num_r_vecs; + + unsigned int num_tx_rings; + unsigned int num_stack_tx_rings; + unsigned int num_rx_rings; +}; + +/** + * struct nfp_net - NFP network device structure + * @dp: Datapath structure + * @fw_ver: Firmware version * @cap: Capabilities advertised by the Firmware * @max_mtu: Maximum support MTU advertised by the Firmware * @rss_hfunc: RSS selected hash function @@ -457,17 +501,9 @@ struct nfp_stat_pair { * @rx_filter_change: Jiffies when statistics last changed * @rx_filter_stats_timer: Timer for polling filter offload statistics * @rx_filter_lock: Lock protecting timer state changes (teardown) + * @max_r_vecs: Number of allocated interrupt vectors for RX/TX * @max_tx_rings: Maximum number of TX rings supported by the Firmware * @max_rx_rings: Maximum number of RX rings supported by the Firmware - * @num_tx_rings: Currently configured number of TX rings - * @num_stack_tx_rings: Number of TX rings used by the stack (not XDP) - * @num_rx_rings: Currently configured number of RX rings - * @txd_cnt: Size of the TX ring in number of descriptors - * @rxd_cnt: Size of the RX ring in number of descriptors - * @tx_rings: Array of pre-allocated TX ring structures - * @rx_rings: Array of pre-allocated RX ring structures - * @max_r_vecs: Number of allocated interrupt vectors for RX/TX - * @num_r_vecs: Number of used ring vectors * @r_vecs: Pre-allocated array of ring vectors * @irq_entries: Pre-allocated array of MSI-X entries * @lsc_handler: Handler for Link State Change interrupt @@ -502,25 +538,10 @@ struct nfp_stat_pair { * @eth_port: Translated ETH Table port entry */ struct nfp_net { - struct device *dev; - struct net_device *netdev; - - unsigned is_vf:1; - unsigned bpf_offload_skip_sw:1; - unsigned bpf_offload_xdp:1; - unsigned chained_metadata_format:1; - - u32 ctrl; - u32 fl_bufsz; - - u32 rx_offset; - - struct bpf_prog *xdp_prog; - - struct nfp_net_tx_ring *tx_rings; - struct nfp_net_rx_ring *rx_rings; + struct nfp_net_dp dp; struct nfp_net_fw_version fw_ver; + u32 cap; u32 max_mtu; @@ -537,18 +558,10 @@ struct nfp_net { unsigned int max_tx_rings; unsigned int max_rx_rings; - unsigned int num_tx_rings; - unsigned int num_stack_tx_rings; - unsigned int num_rx_rings; - int stride_tx; int stride_rx; - int txd_cnt; - int rxd_cnt; - unsigned int max_r_vecs; - unsigned int num_r_vecs; struct nfp_net_r_vector r_vecs[NFP_NET_MAX_R_VECS]; struct msix_entry irq_entries[NFP_NET_MAX_IRQS]; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 2d964d030dbe..951d511643f1 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -86,18 +86,18 @@ void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver, } static dma_addr_t -nfp_net_dma_map_rx(struct nfp_net *nn, void *frag, unsigned int bufsz, +nfp_net_dma_map_rx(struct nfp_net_dp *dp, void *frag, unsigned int bufsz, int direction) { - return dma_map_single(nn->dev, frag + NFP_NET_RX_BUF_HEADROOM, + return dma_map_single(dp->dev, frag + NFP_NET_RX_BUF_HEADROOM, bufsz - NFP_NET_RX_BUF_NON_DATA, direction); } static void -nfp_net_dma_unmap_rx(struct nfp_net *nn, dma_addr_t dma_addr, +nfp_net_dma_unmap_rx(struct nfp_net_dp *dp, dma_addr_t dma_addr, unsigned int bufsz, int direction) { - dma_unmap_single(nn->dev, dma_addr, + dma_unmap_single(dp->dev, dma_addr, bufsz - NFP_NET_RX_BUF_NON_DATA, direction); } @@ -329,19 +329,22 @@ void nfp_net_irqs_assign(struct nfp_net *nn, struct msix_entry *irq_entries, unsigned int n) { + struct nfp_net_dp *dp = &nn->dp; + nn->max_r_vecs = n - NFP_NET_NON_Q_VECTORS; - nn->num_r_vecs = nn->max_r_vecs; + dp->num_r_vecs = nn->max_r_vecs; memcpy(nn->irq_entries, irq_entries, sizeof(*irq_entries) * n); - if (nn->num_rx_rings > nn->num_r_vecs || - nn->num_tx_rings > nn->num_r_vecs) + if (dp->num_rx_rings > dp->num_r_vecs || + dp->num_tx_rings > dp->num_r_vecs) nn_warn(nn, "More rings (%d,%d) than vectors (%d).\n", - nn->num_rx_rings, nn->num_tx_rings, nn->num_r_vecs); + dp->num_rx_rings, dp->num_tx_rings, + dp->num_r_vecs); - nn->num_rx_rings = min(nn->num_r_vecs, nn->num_rx_rings); - nn->num_tx_rings = min(nn->num_r_vecs, nn->num_tx_rings); - nn->num_stack_tx_rings = nn->num_tx_rings; + dp->num_rx_rings = min(dp->num_r_vecs, dp->num_rx_rings); + dp->num_tx_rings = min(dp->num_r_vecs, dp->num_tx_rings); + dp->num_stack_tx_rings = dp->num_tx_rings; } /** @@ -396,11 +399,11 @@ static void nfp_net_read_link_status(struct nfp_net *nn) nn->link_up = link_up; if (nn->link_up) { - netif_carrier_on(nn->netdev); - netdev_info(nn->netdev, "NIC Link is Up\n"); + netif_carrier_on(nn->dp.netdev); + netdev_info(nn->dp.netdev, "NIC Link is Up\n"); } else { - netif_carrier_off(nn->netdev); - netdev_info(nn->netdev, "NIC Link is Down\n"); + netif_carrier_off(nn->dp.netdev); + netdev_info(nn->dp.netdev, "NIC Link is Down\n"); } out: spin_unlock_irqrestore(&nn->link_status_lock, flags); @@ -532,7 +535,7 @@ nfp_net_aux_irq_request(struct nfp_net *nn, u32 ctrl_offset, entry = &nn->irq_entries[vector_idx]; - snprintf(name, name_sz, format, netdev_name(nn->netdev)); + snprintf(name, name_sz, format, netdev_name(nn->dp.netdev)); err = request_irq(entry->vector, handler, 0, name, nn); if (err) { nn_err(nn, "Failed to request IRQ %d (err=%d).\n", @@ -619,7 +622,6 @@ static void nfp_net_tx_ring_stop(struct netdev_queue *nd_q, /** * nfp_net_tx_tso() - Set up Tx descriptor for LSO - * @nn: NFP Net device * @r_vec: per-ring structure * @txbuf: Pointer to driver soft TX descriptor * @txd: Pointer to HW TX descriptor @@ -628,7 +630,7 @@ static void nfp_net_tx_ring_stop(struct netdev_queue *nd_q, * Set up Tx descriptor for LSO, do nothing for non-LSO skbs. * Return error on packet header greater than maximum supported LSO header size. */ -static void nfp_net_tx_tso(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, +static void nfp_net_tx_tso(struct nfp_net_r_vector *r_vec, struct nfp_net_tx_buf *txbuf, struct nfp_net_tx_desc *txd, struct sk_buff *skb) { @@ -659,7 +661,7 @@ static void nfp_net_tx_tso(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, /** * nfp_net_tx_csum() - Set TX CSUM offload flags in TX descriptor - * @nn: NFP Net device + * @dp: NFP Net data path struct * @r_vec: per-ring structure * @txbuf: Pointer to driver soft TX descriptor * @txd: Pointer to TX descriptor @@ -668,7 +670,8 @@ static void nfp_net_tx_tso(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, * This function sets the TX checksum flags in the TX descriptor based * on the configuration and the protocol of the packet to be transmitted. */ -static void nfp_net_tx_csum(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, +static void nfp_net_tx_csum(struct nfp_net_dp *dp, + struct nfp_net_r_vector *r_vec, struct nfp_net_tx_buf *txbuf, struct nfp_net_tx_desc *txd, struct sk_buff *skb) { @@ -676,7 +679,7 @@ static void nfp_net_tx_csum(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, struct iphdr *iph; u8 l4_hdr; - if (!(nn->ctrl & NFP_NET_CFG_CTRL_TXCSUM)) + if (!(dp->ctrl & NFP_NET_CFG_CTRL_TXCSUM)) return; if (skb->ip_summed != CHECKSUM_PARTIAL) @@ -695,8 +698,7 @@ static void nfp_net_tx_csum(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, } else if (ipv6h->version == 6) { l4_hdr = ipv6h->nexthdr; } else { - nn_warn_ratelimit(nn, "partial checksum but ipv=%x!\n", - iph->version); + nn_dp_warn(dp, "partial checksum but ipv=%x!\n", iph->version); return; } @@ -708,8 +710,7 @@ static void nfp_net_tx_csum(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, txd->flags |= PCIE_DESC_TX_UDP_CSUM; break; default: - nn_warn_ratelimit(nn, "partial checksum but l4 proto=%x!\n", - l4_hdr); + nn_dp_warn(dp, "partial checksum but l4 proto=%x!\n", l4_hdr); return; } @@ -744,22 +745,24 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) struct nfp_net_r_vector *r_vec; struct nfp_net_tx_buf *txbuf; struct netdev_queue *nd_q; + struct nfp_net_dp *dp; dma_addr_t dma_addr; unsigned int fsize; int f, nr_frags; int wr_idx; u16 qidx; + dp = &nn->dp; qidx = skb_get_queue_mapping(skb); - tx_ring = &nn->tx_rings[qidx]; + tx_ring = &dp->tx_rings[qidx]; r_vec = tx_ring->r_vec; - nd_q = netdev_get_tx_queue(nn->netdev, qidx); + nd_q = netdev_get_tx_queue(dp->netdev, qidx); nr_frags = skb_shinfo(skb)->nr_frags; if (unlikely(nfp_net_tx_full(tx_ring, nr_frags + 1))) { - nn_warn_ratelimit(nn, "TX ring %d busy. wrp=%u rdp=%u\n", - qidx, tx_ring->wr_p, tx_ring->rd_p); + nn_dp_warn(dp, "TX ring %d busy. wrp=%u rdp=%u\n", + qidx, tx_ring->wr_p, tx_ring->rd_p); netif_tx_stop_queue(nd_q); u64_stats_update_begin(&r_vec->tx_sync); r_vec->tx_busy++; @@ -768,9 +771,9 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) } /* Start with the head skbuf */ - dma_addr = dma_map_single(nn->dev, skb->data, skb_headlen(skb), + dma_addr = dma_map_single(dp->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - if (dma_mapping_error(nn->dev, dma_addr)) + if (dma_mapping_error(dp->dev, dma_addr)) goto err_free; wr_idx = tx_ring->wr_p & (tx_ring->cnt - 1); @@ -794,11 +797,11 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) txd->mss = 0; txd->l4_offset = 0; - nfp_net_tx_tso(nn, r_vec, txbuf, txd, skb); + nfp_net_tx_tso(r_vec, txbuf, txd, skb); - nfp_net_tx_csum(nn, r_vec, txbuf, txd, skb); + nfp_net_tx_csum(dp, r_vec, txbuf, txd, skb); - if (skb_vlan_tag_present(skb) && nn->ctrl & NFP_NET_CFG_CTRL_TXVLAN) { + if (skb_vlan_tag_present(skb) && dp->ctrl & NFP_NET_CFG_CTRL_TXVLAN) { txd->flags |= PCIE_DESC_TX_VLAN; txd->vlan = cpu_to_le16(skb_vlan_tag_get(skb)); } @@ -812,9 +815,9 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) frag = &skb_shinfo(skb)->frags[f]; fsize = skb_frag_size(frag); - dma_addr = skb_frag_dma_map(nn->dev, frag, 0, + dma_addr = skb_frag_dma_map(dp->dev, frag, 0, fsize, DMA_TO_DEVICE); - if (dma_mapping_error(nn->dev, dma_addr)) + if (dma_mapping_error(dp->dev, dma_addr)) goto err_unmap; wr_idx = (wr_idx + 1) & (tx_ring->cnt - 1); @@ -853,7 +856,7 @@ err_unmap: --f; while (f >= 0) { frag = &skb_shinfo(skb)->frags[f]; - dma_unmap_page(nn->dev, tx_ring->txbufs[wr_idx].dma_addr, + dma_unmap_page(dp->dev, tx_ring->txbufs[wr_idx].dma_addr, skb_frag_size(frag), DMA_TO_DEVICE); tx_ring->txbufs[wr_idx].skb = NULL; tx_ring->txbufs[wr_idx].dma_addr = 0; @@ -862,13 +865,13 @@ err_unmap: if (wr_idx < 0) wr_idx += tx_ring->cnt; } - dma_unmap_single(nn->dev, tx_ring->txbufs[wr_idx].dma_addr, + dma_unmap_single(dp->dev, tx_ring->txbufs[wr_idx].dma_addr, skb_headlen(skb), DMA_TO_DEVICE); tx_ring->txbufs[wr_idx].skb = NULL; tx_ring->txbufs[wr_idx].dma_addr = 0; tx_ring->txbufs[wr_idx].fidx = -2; err_free: - nn_warn_ratelimit(nn, "Failed to map DMA TX buffer\n"); + nn_dp_warn(dp, "Failed to map DMA TX buffer\n"); u64_stats_update_begin(&r_vec->tx_sync); r_vec->tx_errors++; u64_stats_update_end(&r_vec->tx_sync); @@ -885,7 +888,7 @@ err_free: static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) { struct nfp_net_r_vector *r_vec = tx_ring->r_vec; - struct nfp_net *nn = r_vec->nfp_net; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; const struct skb_frag_struct *frag; struct netdev_queue *nd_q; u32 done_pkts = 0, done_bytes = 0; @@ -919,7 +922,7 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) if (fidx == -1) { /* unmap head */ - dma_unmap_single(nn->dev, tx_ring->txbufs[idx].dma_addr, + dma_unmap_single(dp->dev, tx_ring->txbufs[idx].dma_addr, skb_headlen(skb), DMA_TO_DEVICE); done_pkts += tx_ring->txbufs[idx].pkt_cnt; @@ -927,7 +930,7 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) } else { /* unmap fragment */ frag = &skb_shinfo(skb)->frags[fidx]; - dma_unmap_page(nn->dev, tx_ring->txbufs[idx].dma_addr, + dma_unmap_page(dp->dev, tx_ring->txbufs[idx].dma_addr, skb_frag_size(frag), DMA_TO_DEVICE); } @@ -947,7 +950,7 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) r_vec->tx_pkts += done_pkts; u64_stats_update_end(&r_vec->tx_sync); - nd_q = netdev_get_tx_queue(nn->netdev, tx_ring->idx); + nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx); netdev_tx_completed_queue(nd_q, done_pkts, done_bytes); if (nfp_net_tx_ring_should_wake(tx_ring)) { /* Make sure TX thread will see updated tx_ring->rd_p */ @@ -965,7 +968,7 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) static void nfp_net_xdp_complete(struct nfp_net_tx_ring *tx_ring) { struct nfp_net_r_vector *r_vec = tx_ring->r_vec; - struct nfp_net *nn = r_vec->nfp_net; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; u32 done_pkts = 0, done_bytes = 0; int idx, todo; u32 qcp_rd_p; @@ -988,8 +991,8 @@ static void nfp_net_xdp_complete(struct nfp_net_tx_ring *tx_ring) if (!tx_ring->txbufs[idx].frag) continue; - nfp_net_dma_unmap_rx(nn, tx_ring->txbufs[idx].dma_addr, - nn->fl_bufsz, DMA_BIDIRECTIONAL); + nfp_net_dma_unmap_rx(dp, tx_ring->txbufs[idx].dma_addr, + dp->fl_bufsz, DMA_BIDIRECTIONAL); __free_page(virt_to_page(tx_ring->txbufs[idx].frag)); done_pkts++; @@ -1014,13 +1017,13 @@ static void nfp_net_xdp_complete(struct nfp_net_tx_ring *tx_ring) /** * nfp_net_tx_ring_reset() - Free any untransmitted buffers and reset pointers - * @nn: NFP Net device + * @dp: NFP Net data path struct * @tx_ring: TX ring structure * * Assumes that the device is stopped */ static void -nfp_net_tx_ring_reset(struct nfp_net *nn, struct nfp_net_tx_ring *tx_ring) +nfp_net_tx_ring_reset(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) { struct nfp_net_r_vector *r_vec = tx_ring->r_vec; const struct skb_frag_struct *frag; @@ -1034,8 +1037,8 @@ nfp_net_tx_ring_reset(struct nfp_net *nn, struct nfp_net_tx_ring *tx_ring) tx_buf = &tx_ring->txbufs[idx]; if (tx_ring == r_vec->xdp_ring) { - nfp_net_dma_unmap_rx(nn, tx_buf->dma_addr, - nn->fl_bufsz, DMA_BIDIRECTIONAL); + nfp_net_dma_unmap_rx(dp, tx_buf->dma_addr, + dp->fl_bufsz, DMA_BIDIRECTIONAL); __free_page(virt_to_page(tx_ring->txbufs[idx].frag)); } else { struct sk_buff *skb = tx_ring->txbufs[idx].skb; @@ -1043,13 +1046,13 @@ nfp_net_tx_ring_reset(struct nfp_net *nn, struct nfp_net_tx_ring *tx_ring) if (tx_buf->fidx == -1) { /* unmap head */ - dma_unmap_single(nn->dev, tx_buf->dma_addr, + dma_unmap_single(dp->dev, tx_buf->dma_addr, skb_headlen(skb), DMA_TO_DEVICE); } else { /* unmap fragment */ frag = &skb_shinfo(skb)->frags[tx_buf->fidx]; - dma_unmap_page(nn->dev, tx_buf->dma_addr, + dma_unmap_page(dp->dev, tx_buf->dma_addr, skb_frag_size(frag), DMA_TO_DEVICE); } @@ -1076,7 +1079,7 @@ nfp_net_tx_ring_reset(struct nfp_net *nn, struct nfp_net_tx_ring *tx_ring) if (tx_ring == r_vec->xdp_ring) return; - nd_q = netdev_get_tx_queue(nn->netdev, tx_ring->idx); + nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx); netdev_tx_reset_queue(nd_q); } @@ -1085,7 +1088,7 @@ static void nfp_net_tx_timeout(struct net_device *netdev) struct nfp_net *nn = netdev_priv(netdev); int i; - for (i = 0; i < nn->netdev->real_num_tx_queues; i++) { + for (i = 0; i < nn->dp.netdev->real_num_tx_queues; i++) { if (!netif_tx_queue_stopped(netdev_get_tx_queue(netdev, i))) continue; nn_warn(nn, "TX timeout on ring: %d\n", i); @@ -1096,15 +1099,15 @@ static void nfp_net_tx_timeout(struct net_device *netdev) /* Receive processing */ static unsigned int -nfp_net_calc_fl_bufsz(struct nfp_net *nn, unsigned int mtu) +nfp_net_calc_fl_bufsz(struct nfp_net_dp *dp, unsigned int mtu) { unsigned int fl_bufsz; fl_bufsz = NFP_NET_RX_BUF_HEADROOM; - if (nn->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) + if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) fl_bufsz += NFP_NET_MAX_PREPEND; else - fl_bufsz += nn->rx_offset; + fl_bufsz += dp->rx_offset; fl_bufsz += ETH_HLEN + VLAN_HLEN * 2 + mtu; fl_bufsz = SKB_DATA_ALIGN(fl_bufsz); @@ -1137,7 +1140,7 @@ static void * nfp_net_rx_alloc_one(struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr, unsigned int fl_bufsz, bool xdp) { - struct nfp_net *nn = rx_ring->r_vec->nfp_net; + struct nfp_net_dp *dp = &rx_ring->r_vec->nfp_net->dp; int direction; void *frag; @@ -1146,16 +1149,16 @@ nfp_net_rx_alloc_one(struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr, else frag = page_address(alloc_page(GFP_KERNEL | __GFP_COLD)); if (!frag) { - nn_warn_ratelimit(nn, "Failed to alloc receive page frag\n"); + nn_dp_warn(dp, "Failed to alloc receive page frag\n"); return NULL; } direction = xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; - *dma_addr = nfp_net_dma_map_rx(nn, frag, fl_bufsz, direction); - if (dma_mapping_error(nn->dev, *dma_addr)) { + *dma_addr = nfp_net_dma_map_rx(dp, frag, fl_bufsz, direction); + if (dma_mapping_error(dp->dev, *dma_addr)) { nfp_net_free_frag(frag, xdp); - nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n"); + nn_dp_warn(dp, "Failed to map DMA RX buffer\n"); return NULL; } @@ -1163,23 +1166,24 @@ nfp_net_rx_alloc_one(struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr, } static void * -nfp_net_napi_alloc_one(struct nfp_net *nn, int direction, dma_addr_t *dma_addr) +nfp_net_napi_alloc_one(struct nfp_net_dp *dp, int direction, + dma_addr_t *dma_addr) { void *frag; - if (!nn->xdp_prog) - frag = napi_alloc_frag(nn->fl_bufsz); + if (!dp->xdp_prog) + frag = napi_alloc_frag(dp->fl_bufsz); else frag = page_address(alloc_page(GFP_ATOMIC | __GFP_COLD)); if (!frag) { - nn_warn_ratelimit(nn, "Failed to alloc receive page frag\n"); + nn_dp_warn(dp, "Failed to alloc receive page frag\n"); return NULL; } - *dma_addr = nfp_net_dma_map_rx(nn, frag, nn->fl_bufsz, direction); - if (dma_mapping_error(nn->dev, *dma_addr)) { - nfp_net_free_frag(frag, nn->xdp_prog); - nn_warn_ratelimit(nn, "Failed to map DMA RX buffer\n"); + *dma_addr = nfp_net_dma_map_rx(dp, frag, dp->fl_bufsz, direction); + if (dma_mapping_error(dp->dev, *dma_addr)) { + nfp_net_free_frag(frag, dp->xdp_prog); + nn_dp_warn(dp, "Failed to map DMA RX buffer\n"); return NULL; } @@ -1247,7 +1251,7 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring) /** * nfp_net_rx_ring_bufs_free() - Free any buffers currently on the RX ring - * @nn: NFP Net device + * @dp: NFP Net data path struct * @rx_ring: RX ring to remove buffers from * @xdp: Whether XDP is enabled * @@ -1256,8 +1260,8 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring) * to restore required ring geometry. */ static void -nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring, - bool xdp) +nfp_net_rx_ring_bufs_free(struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring, bool xdp) { int direction = xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; unsigned int i; @@ -1270,7 +1274,7 @@ nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring, if (!rx_ring->rxbufs[i].frag) continue; - nfp_net_dma_unmap_rx(nn, rx_ring->rxbufs[i].dma_addr, + nfp_net_dma_unmap_rx(dp, rx_ring->rxbufs[i].dma_addr, rx_ring->bufsz, direction); nfp_net_free_frag(rx_ring->rxbufs[i].frag, xdp); rx_ring->rxbufs[i].dma_addr = 0; @@ -1280,13 +1284,13 @@ nfp_net_rx_ring_bufs_free(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring, /** * nfp_net_rx_ring_bufs_alloc() - Fill RX ring with buffers (don't give to FW) - * @nn: NFP Net device + * @dp: NFP Net data path struct * @rx_ring: RX ring to remove buffers from * @xdp: Whether XDP is enabled */ static int -nfp_net_rx_ring_bufs_alloc(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring, - bool xdp) +nfp_net_rx_ring_bufs_alloc(struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring, bool xdp) { struct nfp_net_rx_buf *rxbufs; unsigned int i; @@ -1298,7 +1302,7 @@ nfp_net_rx_ring_bufs_alloc(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring, nfp_net_rx_alloc_one(rx_ring, &rxbufs[i].dma_addr, rx_ring->bufsz, xdp); if (!rxbufs[i].frag) { - nfp_net_rx_ring_bufs_free(nn, rx_ring, xdp); + nfp_net_rx_ring_bufs_free(dp, rx_ring, xdp); return -ENOMEM; } } @@ -1335,17 +1339,18 @@ static int nfp_net_rx_csum_has_errors(u16 flags) /** * nfp_net_rx_csum() - set SKB checksum field based on RX descriptor flags - * @nn: NFP Net device + * @dp: NFP Net data path struct * @r_vec: per-ring structure * @rxd: Pointer to RX descriptor * @skb: Pointer to SKB */ -static void nfp_net_rx_csum(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, +static void nfp_net_rx_csum(struct nfp_net_dp *dp, + struct nfp_net_r_vector *r_vec, struct nfp_net_rx_desc *rxd, struct sk_buff *skb) { skb_checksum_none_assert(skb); - if (!(nn->netdev->features & NETIF_F_RXCSUM)) + if (!(dp->netdev->features & NETIF_F_RXCSUM)) return; if (nfp_net_rx_csum_has_errors(le16_to_cpu(rxd->rxd.flags))) { @@ -1462,7 +1467,7 @@ nfp_net_rx_drop(struct nfp_net_r_vector *r_vec, struct nfp_net_rx_ring *rx_ring, } static bool -nfp_net_tx_xdp_buf(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring, +nfp_net_tx_xdp_buf(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, struct nfp_net_tx_ring *tx_ring, struct nfp_net_rx_buf *rxbuf, unsigned int pkt_off, unsigned int pkt_len) @@ -1478,7 +1483,7 @@ nfp_net_tx_xdp_buf(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring, return false; } - new_frag = nfp_net_napi_alloc_one(nn, DMA_BIDIRECTIONAL, &new_dma_addr); + new_frag = nfp_net_napi_alloc_one(dp, DMA_BIDIRECTIONAL, &new_dma_addr); if (unlikely(!new_frag)) { nfp_net_rx_drop(rx_ring->r_vec, rx_ring, rxbuf, NULL); return false; @@ -1495,7 +1500,7 @@ nfp_net_tx_xdp_buf(struct nfp_net *nn, struct nfp_net_rx_ring *rx_ring, txbuf->pkt_cnt = 1; txbuf->real_len = pkt_len; - dma_sync_single_for_device(nn->dev, rxbuf->dma_addr + pkt_off, + dma_sync_single_for_device(dp->dev, rxbuf->dma_addr + pkt_off, pkt_len, DMA_BIDIRECTIONAL); /* Build TX descriptor */ @@ -1538,7 +1543,7 @@ static int nfp_net_run_xdp(struct bpf_prog *prog, void *data, unsigned int len) static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) { struct nfp_net_r_vector *r_vec = rx_ring->r_vec; - struct nfp_net *nn = r_vec->nfp_net; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; struct nfp_net_tx_ring *tx_ring; struct bpf_prog *xdp_prog; unsigned int true_bufsz; @@ -1548,9 +1553,9 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) int idx; rcu_read_lock(); - xdp_prog = READ_ONCE(nn->xdp_prog); + xdp_prog = READ_ONCE(dp->xdp_prog); rx_dma_map_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; - true_bufsz = xdp_prog ? PAGE_SIZE : nn->fl_bufsz; + true_bufsz = xdp_prog ? PAGE_SIZE : dp->fl_bufsz; tx_ring = r_vec->xdp_ring; while (pkts_polled < budget) { @@ -1591,10 +1596,10 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) data_len = le16_to_cpu(rxd->rxd.data_len); pkt_len = data_len - meta_len; - if (nn->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) + if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) pkt_off = meta_len; else - pkt_off = nn->rx_offset; + pkt_off = dp->rx_offset; data_off = NFP_NET_RX_BUF_HEADROOM + pkt_off; /* Stats update */ @@ -1604,10 +1609,10 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) u64_stats_update_end(&r_vec->rx_sync); if (xdp_prog && !(rxd->rxd.flags & PCIE_DESC_RX_BPF && - nn->bpf_offload_xdp)) { + dp->bpf_offload_xdp)) { int act; - dma_sync_single_for_cpu(nn->dev, + dma_sync_single_for_cpu(dp->dev, rxbuf->dma_addr + pkt_off, pkt_len, DMA_BIDIRECTIONAL); act = nfp_net_run_xdp(xdp_prog, rxbuf->frag + data_off, @@ -1616,15 +1621,17 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) case XDP_PASS: break; case XDP_TX: - if (unlikely(!nfp_net_tx_xdp_buf(nn, rx_ring, + if (unlikely(!nfp_net_tx_xdp_buf(dp, rx_ring, tx_ring, rxbuf, - pkt_off, pkt_len))) - trace_xdp_exception(nn->netdev, xdp_prog, act); + pkt_off, + pkt_len))) + trace_xdp_exception(dp->netdev, + xdp_prog, act); continue; default: bpf_warn_invalid_xdp_action(act); case XDP_ABORTED: - trace_xdp_exception(nn->netdev, xdp_prog, act); + trace_xdp_exception(dp->netdev, xdp_prog, act); case XDP_DROP: nfp_net_rx_give_one(rx_ring, rxbuf->frag, rxbuf->dma_addr); @@ -1637,14 +1644,14 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) nfp_net_rx_drop(r_vec, rx_ring, rxbuf, NULL); continue; } - new_frag = nfp_net_napi_alloc_one(nn, rx_dma_map_dir, + new_frag = nfp_net_napi_alloc_one(dp, rx_dma_map_dir, &new_dma_addr); if (unlikely(!new_frag)) { nfp_net_rx_drop(r_vec, rx_ring, rxbuf, skb); continue; } - nfp_net_dma_unmap_rx(nn, rxbuf->dma_addr, nn->fl_bufsz, + nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr, dp->fl_bufsz, rx_dma_map_dir); nfp_net_rx_give_one(rx_ring, new_frag, new_dma_addr); @@ -1652,23 +1659,23 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) skb_reserve(skb, data_off); skb_put(skb, pkt_len); - if (!nn->chained_metadata_format) { - nfp_net_set_hash_desc(nn->netdev, skb, rxd); + if (!dp->chained_metadata_format) { + nfp_net_set_hash_desc(dp->netdev, skb, rxd); } else if (meta_len) { void *end; - end = nfp_net_parse_meta(nn->netdev, skb, meta_len); + end = nfp_net_parse_meta(dp->netdev, skb, meta_len); if (unlikely(end != skb->data)) { - nn_warn_ratelimit(nn, "invalid RX packet metadata\n"); + nn_dp_warn(dp, "invalid RX packet metadata\n"); nfp_net_rx_drop(r_vec, rx_ring, NULL, skb); continue; } } skb_record_rx_queue(skb, rx_ring->idx); - skb->protocol = eth_type_trans(skb, nn->netdev); + skb->protocol = eth_type_trans(skb, dp->netdev); - nfp_net_rx_csum(nn, r_vec, rxd, skb); + nfp_net_rx_csum(dp, r_vec, rxd, skb); if (rxd->rxd.flags & PCIE_DESC_RX_VLAN) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), @@ -1722,12 +1729,12 @@ static int nfp_net_poll(struct napi_struct *napi, int budget) static void nfp_net_tx_ring_free(struct nfp_net_tx_ring *tx_ring) { struct nfp_net_r_vector *r_vec = tx_ring->r_vec; - struct nfp_net *nn = r_vec->nfp_net; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; kfree(tx_ring->txbufs); if (tx_ring->txds) - dma_free_coherent(nn->dev, tx_ring->size, + dma_free_coherent(dp->dev, tx_ring->size, tx_ring->txds, tx_ring->dma); tx_ring->cnt = 0; @@ -1749,13 +1756,13 @@ static int nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring, u32 cnt, bool is_xdp) { struct nfp_net_r_vector *r_vec = tx_ring->r_vec; - struct nfp_net *nn = r_vec->nfp_net; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; int sz; tx_ring->cnt = cnt; tx_ring->size = sizeof(*tx_ring->txds) * tx_ring->cnt; - tx_ring->txds = dma_zalloc_coherent(nn->dev, tx_ring->size, + tx_ring->txds = dma_zalloc_coherent(dp->dev, tx_ring->size, &tx_ring->dma, GFP_KERNEL); if (!tx_ring->txds) goto err_alloc; @@ -1766,7 +1773,7 @@ nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring, u32 cnt, bool is_xdp) goto err_alloc; if (!is_xdp) - netif_set_xps_queue(nn->netdev, &r_vec->affinity_mask, + netif_set_xps_queue(dp->netdev, &r_vec->affinity_mask, tx_ring->idx); return 0; @@ -1813,17 +1820,16 @@ nfp_net_tx_ring_set_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) { struct nfp_net_ring_set new = *s; - s->dcnt = nn->txd_cnt; - s->rings = nn->tx_rings; - s->n_rings = nn->num_tx_rings; + s->dcnt = nn->dp.txd_cnt; + s->rings = nn->dp.tx_rings; + s->n_rings = nn->dp.num_tx_rings; - nn->txd_cnt = new.dcnt; - nn->tx_rings = new.rings; - nn->num_tx_rings = new.n_rings; + nn->dp.txd_cnt = new.dcnt; + nn->dp.tx_rings = new.rings; + nn->dp.num_tx_rings = new.n_rings; } -static void -nfp_net_tx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s) +static void nfp_net_tx_ring_set_free(struct nfp_net_ring_set *s) { struct nfp_net_tx_ring *rings = s->rings; unsigned int r; @@ -1841,12 +1847,12 @@ nfp_net_tx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s) static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring) { struct nfp_net_r_vector *r_vec = rx_ring->r_vec; - struct nfp_net *nn = r_vec->nfp_net; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; kfree(rx_ring->rxbufs); if (rx_ring->rxds) - dma_free_coherent(nn->dev, rx_ring->size, + dma_free_coherent(dp->dev, rx_ring->size, rx_ring->rxds, rx_ring->dma); rx_ring->cnt = 0; @@ -1869,14 +1875,14 @@ nfp_net_rx_ring_alloc(struct nfp_net_rx_ring *rx_ring, unsigned int fl_bufsz, u32 cnt) { struct nfp_net_r_vector *r_vec = rx_ring->r_vec; - struct nfp_net *nn = r_vec->nfp_net; + struct nfp_net_dp *dp = &r_vec->nfp_net->dp; int sz; rx_ring->cnt = cnt; rx_ring->bufsz = fl_bufsz; rx_ring->size = sizeof(*rx_ring->rxds) * rx_ring->cnt; - rx_ring->rxds = dma_zalloc_coherent(nn->dev, rx_ring->size, + rx_ring->rxds = dma_zalloc_coherent(dp->dev, rx_ring->size, &rx_ring->dma, GFP_KERNEL); if (!rx_ring->rxds) goto err_alloc; @@ -1897,7 +1903,7 @@ static struct nfp_net_rx_ring * nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s, bool xdp) { - unsigned int fl_bufsz = nfp_net_calc_fl_bufsz(nn, s->mtu); + unsigned int fl_bufsz = nfp_net_calc_fl_bufsz(&nn->dp, s->mtu); struct nfp_net_rx_ring *rings; unsigned int r; @@ -1911,7 +1917,7 @@ nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s, if (nfp_net_rx_ring_alloc(&rings[r], fl_bufsz, s->dcnt)) goto err_free_prev; - if (nfp_net_rx_ring_bufs_alloc(nn, &rings[r], xdp)) + if (nfp_net_rx_ring_bufs_alloc(&nn->dp, &rings[r], xdp)) goto err_free_ring; } @@ -1919,7 +1925,7 @@ nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s, err_free_prev: while (r--) { - nfp_net_rx_ring_bufs_free(nn, &rings[r], xdp); + nfp_net_rx_ring_bufs_free(&nn->dp, &rings[r], xdp); err_free_ring: nfp_net_rx_ring_free(&rings[r]); } @@ -1932,27 +1938,27 @@ nfp_net_rx_ring_set_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) { struct nfp_net_ring_set new = *s; - s->mtu = nn->netdev->mtu; - s->dcnt = nn->rxd_cnt; - s->rings = nn->rx_rings; - s->n_rings = nn->num_rx_rings; + s->mtu = nn->dp.netdev->mtu; + s->dcnt = nn->dp.rxd_cnt; + s->rings = nn->dp.rx_rings; + s->n_rings = nn->dp.num_rx_rings; - nn->netdev->mtu = new.mtu; - nn->fl_bufsz = nfp_net_calc_fl_bufsz(nn, new.mtu); - nn->rxd_cnt = new.dcnt; - nn->rx_rings = new.rings; - nn->num_rx_rings = new.n_rings; + nn->dp.netdev->mtu = new.mtu; + nn->dp.fl_bufsz = nfp_net_calc_fl_bufsz(&nn->dp, new.mtu); + nn->dp.rxd_cnt = new.dcnt; + nn->dp.rx_rings = new.rings; + nn->dp.num_rx_rings = new.n_rings; } static void -nfp_net_rx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s, +nfp_net_rx_ring_set_free(struct nfp_net_dp *dp, struct nfp_net_ring_set *s, bool xdp) { struct nfp_net_rx_ring *rings = s->rings; unsigned int r; for (r = 0; r < s->n_rings; r++) { - nfp_net_rx_ring_bufs_free(nn, &rings[r], xdp); + nfp_net_rx_ring_bufs_free(dp, &rings[r], xdp); nfp_net_rx_ring_free(&rings[r]); } @@ -1960,15 +1966,15 @@ nfp_net_rx_ring_set_free(struct nfp_net *nn, struct nfp_net_ring_set *s, } static void -nfp_net_vector_assign_rings(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, - int idx) +nfp_net_vector_assign_rings(struct nfp_net_dp *dp, + struct nfp_net_r_vector *r_vec, int idx) { - r_vec->rx_ring = idx < nn->num_rx_rings ? &nn->rx_rings[idx] : NULL; + r_vec->rx_ring = idx < dp->num_rx_rings ? &dp->rx_rings[idx] : NULL; r_vec->tx_ring = - idx < nn->num_stack_tx_rings ? &nn->tx_rings[idx] : NULL; + idx < dp->num_stack_tx_rings ? &dp->tx_rings[idx] : NULL; - r_vec->xdp_ring = idx < nn->num_tx_rings - nn->num_stack_tx_rings ? - &nn->tx_rings[nn->num_stack_tx_rings + idx] : NULL; + r_vec->xdp_ring = idx < dp->num_tx_rings - dp->num_stack_tx_rings ? + &dp->tx_rings[dp->num_stack_tx_rings + idx] : NULL; } static int @@ -1978,11 +1984,11 @@ nfp_net_prepare_vector(struct nfp_net *nn, struct nfp_net_r_vector *r_vec, int err; /* Setup NAPI */ - netif_napi_add(nn->netdev, &r_vec->napi, + netif_napi_add(nn->dp.netdev, &r_vec->napi, nfp_net_poll, NAPI_POLL_WEIGHT); snprintf(r_vec->name, sizeof(r_vec->name), - "%s-rxtx-%d", nn->netdev->name, idx); + "%s-rxtx-%d", nn->dp.netdev->name, idx); err = request_irq(r_vec->irq_vector, r_vec->handler, 0, r_vec->name, r_vec); if (err) { @@ -2053,13 +2059,13 @@ void nfp_net_coalesce_write_cfg(struct nfp_net *nn) /* copy RX interrupt coalesce parameters */ value = (nn->rx_coalesce_max_frames << 16) | (factor * nn->rx_coalesce_usecs); - for (i = 0; i < nn->num_rx_rings; i++) + for (i = 0; i < nn->dp.num_rx_rings; i++) nn_writel(nn, NFP_NET_CFG_RXR_IRQ_MOD(i), value); /* copy TX interrupt coalesce parameters */ value = (nn->tx_coalesce_max_frames << 16) | (factor * nn->tx_coalesce_usecs); - for (i = 0; i < nn->num_tx_rings; i++) + for (i = 0; i < nn->dp.num_tx_rings; i++) nn_writel(nn, NFP_NET_CFG_TXR_IRQ_MOD(i), value); } @@ -2074,9 +2080,9 @@ void nfp_net_coalesce_write_cfg(struct nfp_net *nn) static void nfp_net_write_mac_addr(struct nfp_net *nn) { nn_writel(nn, NFP_NET_CFG_MACADDR + 0, - get_unaligned_be32(nn->netdev->dev_addr)); + get_unaligned_be32(nn->dp.netdev->dev_addr)); nn_writew(nn, NFP_NET_CFG_MACADDR + 6, - get_unaligned_be16(nn->netdev->dev_addr + 4)); + get_unaligned_be16(nn->dp.netdev->dev_addr + 4)); } static void nfp_net_vec_clear_ring_data(struct nfp_net *nn, unsigned int idx) @@ -2100,7 +2106,7 @@ static void nfp_net_clear_config_and_disable(struct nfp_net *nn) unsigned int r; int err; - new_ctrl = nn->ctrl; + new_ctrl = nn->dp.ctrl; new_ctrl &= ~NFP_NET_CFG_CTRL_ENABLE; update = NFP_NET_CFG_UPDATE_GEN; update |= NFP_NET_CFG_UPDATE_MSIX; @@ -2117,14 +2123,14 @@ static void nfp_net_clear_config_and_disable(struct nfp_net *nn) if (err) nn_err(nn, "Could not disable device: %d\n", err); - for (r = 0; r < nn->num_rx_rings; r++) - nfp_net_rx_ring_reset(&nn->rx_rings[r]); - for (r = 0; r < nn->num_tx_rings; r++) - nfp_net_tx_ring_reset(nn, &nn->tx_rings[r]); - for (r = 0; r < nn->num_r_vecs; r++) + for (r = 0; r < nn->dp.num_rx_rings; r++) + nfp_net_rx_ring_reset(&nn->dp.rx_rings[r]); + for (r = 0; r < nn->dp.num_tx_rings; r++) + nfp_net_tx_ring_reset(&nn->dp, &nn->dp.tx_rings[r]); + for (r = 0; r < nn->dp.num_r_vecs; r++) nfp_net_vec_clear_ring_data(nn, r); - nn->ctrl = new_ctrl; + nn->dp.ctrl = new_ctrl; } static void @@ -2152,7 +2158,7 @@ static int __nfp_net_set_config_and_enable(struct nfp_net *nn) unsigned int r; int err; - new_ctrl = nn->ctrl; + new_ctrl = nn->dp.ctrl; if (nn->cap & NFP_NET_CFG_CTRL_RSS) { nfp_net_rss_write_key(nn); @@ -2168,22 +2174,22 @@ static int __nfp_net_set_config_and_enable(struct nfp_net *nn) update |= NFP_NET_CFG_UPDATE_IRQMOD; } - for (r = 0; r < nn->num_tx_rings; r++) - nfp_net_tx_ring_hw_cfg_write(nn, &nn->tx_rings[r], r); - for (r = 0; r < nn->num_rx_rings; r++) - nfp_net_rx_ring_hw_cfg_write(nn, &nn->rx_rings[r], r); + for (r = 0; r < nn->dp.num_tx_rings; r++) + nfp_net_tx_ring_hw_cfg_write(nn, &nn->dp.tx_rings[r], r); + for (r = 0; r < nn->dp.num_rx_rings; r++) + nfp_net_rx_ring_hw_cfg_write(nn, &nn->dp.rx_rings[r], r); - nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, nn->num_tx_rings == 64 ? - 0xffffffffffffffffULL : ((u64)1 << nn->num_tx_rings) - 1); + nn_writeq(nn, NFP_NET_CFG_TXRS_ENABLE, nn->dp.num_tx_rings == 64 ? + 0xffffffffffffffffULL : ((u64)1 << nn->dp.num_tx_rings) - 1); - nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, nn->num_rx_rings == 64 ? - 0xffffffffffffffffULL : ((u64)1 << nn->num_rx_rings) - 1); + nn_writeq(nn, NFP_NET_CFG_RXRS_ENABLE, nn->dp.num_rx_rings == 64 ? + 0xffffffffffffffffULL : ((u64)1 << nn->dp.num_rx_rings) - 1); nfp_net_write_mac_addr(nn); - nn_writel(nn, NFP_NET_CFG_MTU, nn->netdev->mtu); + nn_writel(nn, NFP_NET_CFG_MTU, nn->dp.netdev->mtu); nn_writel(nn, NFP_NET_CFG_FLBUFSZ, - nn->fl_bufsz - NFP_NET_RX_BUF_NON_DATA); + nn->dp.fl_bufsz - NFP_NET_RX_BUF_NON_DATA); /* Enable device */ new_ctrl |= NFP_NET_CFG_CTRL_ENABLE; @@ -2196,18 +2202,18 @@ static int __nfp_net_set_config_and_enable(struct nfp_net *nn) nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl); err = nfp_net_reconfig(nn, update); - nn->ctrl = new_ctrl; + nn->dp.ctrl = new_ctrl; - for (r = 0; r < nn->num_rx_rings; r++) - nfp_net_rx_ring_fill_freelist(&nn->rx_rings[r]); + for (r = 0; r < nn->dp.num_rx_rings; r++) + nfp_net_rx_ring_fill_freelist(&nn->dp.rx_rings[r]); /* Since reconfiguration requests while NFP is down are ignored we * have to wipe the entire VXLAN configuration and reinitialize it. */ - if (nn->ctrl & NFP_NET_CFG_CTRL_VXLAN) { + if (nn->dp.ctrl & NFP_NET_CFG_CTRL_VXLAN) { memset(&nn->vxlan_ports, 0, sizeof(nn->vxlan_ports)); memset(&nn->vxlan_usecnt, 0, sizeof(nn->vxlan_usecnt)); - udp_tunnel_get_rx_info(nn->netdev); + udp_tunnel_get_rx_info(nn->dp.netdev); } return err; @@ -2236,12 +2242,12 @@ static void nfp_net_open_stack(struct nfp_net *nn) { unsigned int r; - for (r = 0; r < nn->num_r_vecs; r++) { + for (r = 0; r < nn->dp.num_r_vecs; r++) { napi_enable(&nn->r_vecs[r].napi); enable_irq(nn->r_vecs[r].irq_vector); } - netif_tx_wake_all_queues(nn->netdev); + netif_tx_wake_all_queues(nn->dp.netdev); enable_irq(nn->irq_entries[NFP_NET_IRQ_LSC_IDX].vector); nfp_net_read_link_status(nn); @@ -2251,18 +2257,18 @@ static int nfp_net_netdev_open(struct net_device *netdev) { struct nfp_net *nn = netdev_priv(netdev); struct nfp_net_ring_set rx = { - .n_rings = nn->num_rx_rings, - .mtu = nn->netdev->mtu, - .dcnt = nn->rxd_cnt, + .n_rings = nn->dp.num_rx_rings, + .mtu = nn->dp.netdev->mtu, + .dcnt = nn->dp.rxd_cnt, }; struct nfp_net_ring_set tx = { - .n_rings = nn->num_tx_rings, - .dcnt = nn->txd_cnt, + .n_rings = nn->dp.num_tx_rings, + .dcnt = nn->dp.txd_cnt, }; int err, r; - if (nn->ctrl & NFP_NET_CFG_CTRL_ENABLE) { - nn_err(nn, "Dev is already enabled: 0x%08x\n", nn->ctrl); + if (nn->dp.ctrl & NFP_NET_CFG_CTRL_ENABLE) { + nn_err(nn, "Dev is already enabled: 0x%08x\n", nn->dp.ctrl); return -EBUSY; } @@ -2283,33 +2289,33 @@ static int nfp_net_netdev_open(struct net_device *netdev) goto err_free_exn; disable_irq(nn->irq_entries[NFP_NET_IRQ_LSC_IDX].vector); - for (r = 0; r < nn->num_r_vecs; r++) { + for (r = 0; r < nn->dp.num_r_vecs; r++) { err = nfp_net_prepare_vector(nn, &nn->r_vecs[r], r); if (err) goto err_cleanup_vec_p; } - nn->rx_rings = nfp_net_rx_ring_set_prepare(nn, &rx, nn->xdp_prog); - if (!nn->rx_rings) { + nn->dp.rx_rings = nfp_net_rx_ring_set_prepare(nn, &rx, nn->dp.xdp_prog); + if (!nn->dp.rx_rings) { err = -ENOMEM; goto err_cleanup_vec; } - nn->tx_rings = nfp_net_tx_ring_set_prepare(nn, &tx, - nn->num_stack_tx_rings); - if (!nn->tx_rings) { + nn->dp.tx_rings = nfp_net_tx_ring_set_prepare(nn, &tx, + nn->dp.num_stack_tx_rings); + if (!nn->dp.tx_rings) { err = -ENOMEM; goto err_free_rx_rings; } for (r = 0; r < nn->max_r_vecs; r++) - nfp_net_vector_assign_rings(nn, &nn->r_vecs[r], r); + nfp_net_vector_assign_rings(&nn->dp, &nn->r_vecs[r], r); - err = netif_set_real_num_tx_queues(netdev, nn->num_stack_tx_rings); + err = netif_set_real_num_tx_queues(netdev, nn->dp.num_stack_tx_rings); if (err) goto err_free_rings; - err = netif_set_real_num_rx_queues(netdev, nn->num_rx_rings); + err = netif_set_real_num_rx_queues(netdev, nn->dp.num_rx_rings); if (err) goto err_free_rings; @@ -2335,11 +2341,11 @@ static int nfp_net_netdev_open(struct net_device *netdev) return 0; err_free_rings: - nfp_net_tx_ring_set_free(nn, &tx); + nfp_net_tx_ring_set_free(&tx); err_free_rx_rings: - nfp_net_rx_ring_set_free(nn, &rx, nn->xdp_prog); + nfp_net_rx_ring_set_free(&nn->dp, &rx, nn->dp.xdp_prog); err_cleanup_vec: - r = nn->num_r_vecs; + r = nn->dp.num_r_vecs; err_cleanup_vec_p: while (r--) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); @@ -2358,15 +2364,15 @@ static void nfp_net_close_stack(struct nfp_net *nn) unsigned int r; disable_irq(nn->irq_entries[NFP_NET_IRQ_LSC_IDX].vector); - netif_carrier_off(nn->netdev); + netif_carrier_off(nn->dp.netdev); nn->link_up = false; - for (r = 0; r < nn->num_r_vecs; r++) { + for (r = 0; r < nn->dp.num_r_vecs; r++) { disable_irq(nn->r_vecs[r].irq_vector); napi_disable(&nn->r_vecs[r].napi); } - netif_tx_disable(nn->netdev); + netif_tx_disable(nn->dp.netdev); } /** @@ -2377,17 +2383,18 @@ static void nfp_net_close_free_all(struct nfp_net *nn) { unsigned int r; - for (r = 0; r < nn->num_rx_rings; r++) { - nfp_net_rx_ring_bufs_free(nn, &nn->rx_rings[r], nn->xdp_prog); - nfp_net_rx_ring_free(&nn->rx_rings[r]); + for (r = 0; r < nn->dp.num_rx_rings; r++) { + nfp_net_rx_ring_bufs_free(&nn->dp, &nn->dp.rx_rings[r], + nn->dp.xdp_prog); + nfp_net_rx_ring_free(&nn->dp.rx_rings[r]); } - for (r = 0; r < nn->num_tx_rings; r++) - nfp_net_tx_ring_free(&nn->tx_rings[r]); - for (r = 0; r < nn->num_r_vecs; r++) + for (r = 0; r < nn->dp.num_tx_rings; r++) + nfp_net_tx_ring_free(&nn->dp.tx_rings[r]); + for (r = 0; r < nn->dp.num_r_vecs; r++) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); - kfree(nn->rx_rings); - kfree(nn->tx_rings); + kfree(nn->dp.rx_rings); + kfree(nn->dp.tx_rings); nfp_net_aux_irq_free(nn, NFP_NET_CFG_LSC, NFP_NET_IRQ_LSC_IDX); nfp_net_aux_irq_free(nn, NFP_NET_CFG_EXN, NFP_NET_IRQ_EXN_IDX); @@ -2401,8 +2408,8 @@ static int nfp_net_netdev_close(struct net_device *netdev) { struct nfp_net *nn = netdev_priv(netdev); - if (!(nn->ctrl & NFP_NET_CFG_CTRL_ENABLE)) { - nn_err(nn, "Dev is not up: 0x%08x\n", nn->ctrl); + if (!(nn->dp.ctrl & NFP_NET_CFG_CTRL_ENABLE)) { + nn_err(nn, "Dev is not up: 0x%08x\n", nn->dp.ctrl); return 0; } @@ -2427,7 +2434,7 @@ static void nfp_net_set_rx_mode(struct net_device *netdev) struct nfp_net *nn = netdev_priv(netdev); u32 new_ctrl; - new_ctrl = nn->ctrl; + new_ctrl = nn->dp.ctrl; if (netdev->flags & IFF_PROMISC) { if (nn->cap & NFP_NET_CFG_CTRL_PROMISC) @@ -2438,13 +2445,13 @@ static void nfp_net_set_rx_mode(struct net_device *netdev) new_ctrl &= ~NFP_NET_CFG_CTRL_PROMISC; } - if (new_ctrl == nn->ctrl) + if (new_ctrl == nn->dp.ctrl) return; nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl); nfp_net_reconfig_post(nn, NFP_NET_CFG_UPDATE_GEN); - nn->ctrl = new_ctrl; + nn->dp.ctrl = new_ctrl; } static void nfp_net_rss_init_itbl(struct nfp_net *nn) @@ -2453,7 +2460,7 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn) for (i = 0; i < sizeof(nn->rss_itbl); i++) nn->rss_itbl[i] = - ethtool_rxfh_indir_default(i, nn->num_rx_rings); + ethtool_rxfh_indir_default(i, nn->dp.num_rx_rings); } static int @@ -2471,24 +2478,23 @@ nfp_net_ring_swap_enable(struct nfp_net *nn, unsigned int *num_vecs, if (tx) nfp_net_tx_ring_set_swap(nn, tx); - swap(*num_vecs, nn->num_r_vecs); - swap(*stack_tx_rings, nn->num_stack_tx_rings); - *xdp_prog = xchg(&nn->xdp_prog, *xdp_prog); + swap(*num_vecs, nn->dp.num_r_vecs); + swap(*stack_tx_rings, nn->dp.num_stack_tx_rings); + *xdp_prog = xchg(&nn->dp.xdp_prog, *xdp_prog); for (r = 0; r < nn->max_r_vecs; r++) - nfp_net_vector_assign_rings(nn, &nn->r_vecs[r], r); + nfp_net_vector_assign_rings(&nn->dp, &nn->r_vecs[r], r); - if (!netif_is_rxfh_configured(nn->netdev)) + if (!netif_is_rxfh_configured(nn->dp.netdev)) nfp_net_rss_init_itbl(nn); - err = netif_set_real_num_rx_queues(nn->netdev, - nn->num_rx_rings); + err = netif_set_real_num_rx_queues(nn->dp.netdev, nn->dp.num_rx_rings); if (err) return err; - if (nn->netdev->real_num_tx_queues != nn->num_stack_tx_rings) { - err = netif_set_real_num_tx_queues(nn->netdev, - nn->num_stack_tx_rings); + if (nn->dp.netdev->real_num_tx_queues != nn->dp.num_stack_tx_rings) { + err = netif_set_real_num_tx_queues(nn->dp.netdev, + nn->dp.num_stack_tx_rings); if (err) return err; } @@ -2503,7 +2509,7 @@ nfp_net_check_config(struct nfp_net *nn, struct bpf_prog *xdp_prog, /* XDP-enabled tests */ if (!xdp_prog) return 0; - if (rx && nfp_net_calc_fl_bufsz(nn, rx->mtu) > PAGE_SIZE) { + if (rx && nfp_net_calc_fl_bufsz(&nn->dp, rx->mtu) > PAGE_SIZE) { nn_warn(nn, "MTU too large w/ XDP enabled\n"); return -EINVAL; } @@ -2521,17 +2527,17 @@ nfp_net_ring_reconfig_down(struct nfp_net *nn, struct bpf_prog **xdp_prog, struct nfp_net_ring_set *tx, unsigned int stack_tx_rings, unsigned int num_vecs) { - nn->netdev->mtu = rx ? rx->mtu : nn->netdev->mtu; - nn->fl_bufsz = nfp_net_calc_fl_bufsz(nn, nn->netdev->mtu); - nn->rxd_cnt = rx ? rx->dcnt : nn->rxd_cnt; - nn->txd_cnt = tx ? tx->dcnt : nn->txd_cnt; - nn->num_rx_rings = rx ? rx->n_rings : nn->num_rx_rings; - nn->num_tx_rings = tx ? tx->n_rings : nn->num_tx_rings; - nn->num_stack_tx_rings = stack_tx_rings; - nn->num_r_vecs = num_vecs; - *xdp_prog = xchg(&nn->xdp_prog, *xdp_prog); - - if (!netif_is_rxfh_configured(nn->netdev)) + nn->dp.netdev->mtu = rx ? rx->mtu : nn->dp.netdev->mtu; + nn->dp.fl_bufsz = nfp_net_calc_fl_bufsz(&nn->dp, nn->dp.netdev->mtu); + nn->dp.rxd_cnt = rx ? rx->dcnt : nn->dp.rxd_cnt; + nn->dp.txd_cnt = tx ? tx->dcnt : nn->dp.txd_cnt; + nn->dp.num_rx_rings = rx ? rx->n_rings : nn->dp.num_rx_rings; + nn->dp.num_tx_rings = tx ? tx->n_rings : nn->dp.num_tx_rings; + nn->dp.num_stack_tx_rings = stack_tx_rings; + nn->dp.num_r_vecs = num_vecs; + *xdp_prog = xchg(&nn->dp.xdp_prog, *xdp_prog); + + if (!netif_is_rxfh_configured(nn->dp.netdev)) nfp_net_rss_init_itbl(nn); } @@ -2542,24 +2548,24 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog, unsigned int stack_tx_rings, num_vecs, r; int err; - stack_tx_rings = tx ? tx->n_rings : nn->num_tx_rings; + stack_tx_rings = tx ? tx->n_rings : nn->dp.num_tx_rings; if (*xdp_prog) - stack_tx_rings -= rx ? rx->n_rings : nn->num_rx_rings; + stack_tx_rings -= rx ? rx->n_rings : nn->dp.num_rx_rings; - num_vecs = max(rx ? rx->n_rings : nn->num_rx_rings, stack_tx_rings); + num_vecs = max(rx ? rx->n_rings : nn->dp.num_rx_rings, stack_tx_rings); err = nfp_net_check_config(nn, *xdp_prog, rx, tx); if (err) return err; - if (!netif_running(nn->netdev)) { + if (!netif_running(nn->dp.netdev)) { nfp_net_ring_reconfig_down(nn, xdp_prog, rx, tx, stack_tx_rings, num_vecs); return 0; } /* Prepare new rings */ - for (r = nn->num_r_vecs; r < num_vecs; r++) { + for (r = nn->dp.num_r_vecs; r < num_vecs; r++) { err = nfp_net_prepare_vector(nn, &nn->r_vecs[r], r); if (err) { num_vecs = r; @@ -2597,13 +2603,13 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog, nn_err(nn, "Can't restore ring config - FW communication failed (%d,%d)\n", err, err2); } - for (r = num_vecs - 1; r >= nn->num_r_vecs; r--) + for (r = num_vecs - 1; r >= nn->dp.num_r_vecs; r--) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); if (rx) - nfp_net_rx_ring_set_free(nn, rx, *xdp_prog); + nfp_net_rx_ring_set_free(&nn->dp, rx, *xdp_prog); if (tx) - nfp_net_tx_ring_set_free(nn, tx); + nfp_net_tx_ring_set_free(tx); nfp_net_open_stack(nn); @@ -2611,9 +2617,9 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog, err_free_rx: if (rx) - nfp_net_rx_ring_set_free(nn, rx, *xdp_prog); + nfp_net_rx_ring_set_free(&nn->dp, rx, *xdp_prog); err_cleanup_vecs: - for (r = num_vecs - 1; r >= nn->num_r_vecs; r--) + for (r = num_vecs - 1; r >= nn->dp.num_r_vecs; r--) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); return err; } @@ -2622,12 +2628,12 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) { struct nfp_net *nn = netdev_priv(netdev); struct nfp_net_ring_set rx = { - .n_rings = nn->num_rx_rings, + .n_rings = nn->dp.num_rx_rings, .mtu = new_mtu, - .dcnt = nn->rxd_cnt, + .dcnt = nn->dp.rxd_cnt, }; - return nfp_net_ring_reconfig(nn, &nn->xdp_prog, &rx, NULL); + return nfp_net_ring_reconfig(nn, &nn->dp.xdp_prog, &rx, NULL); } static void nfp_net_stat64(struct net_device *netdev, @@ -2636,7 +2642,7 @@ static void nfp_net_stat64(struct net_device *netdev, struct nfp_net *nn = netdev_priv(netdev); int r; - for (r = 0; r < nn->num_r_vecs; r++) { + for (r = 0; r < nn->dp.num_r_vecs; r++) { struct nfp_net_r_vector *r_vec = &nn->r_vecs[r]; u64 data[3]; unsigned int start; @@ -2683,7 +2689,7 @@ nfp_net_setup_tc(struct net_device *netdev, u32 handle, __be16 proto, return -ENOTSUPP; if (tc->type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn)) { - if (!nn->bpf_offload_xdp) + if (!nn->dp.bpf_offload_xdp) return nfp_net_bpf_offload(nn, tc->cls_bpf); else return -EBUSY; @@ -2702,7 +2708,7 @@ static int nfp_net_set_features(struct net_device *netdev, /* Assume this is not called with features we have not advertised */ - new_ctrl = nn->ctrl; + new_ctrl = nn->dp.ctrl; if (changed & NETIF_F_RXCSUM) { if (features & NETIF_F_RXCSUM) @@ -2746,7 +2752,7 @@ static int nfp_net_set_features(struct net_device *netdev, new_ctrl &= ~NFP_NET_CFG_CTRL_GATHER; } - if (changed & NETIF_F_HW_TC && nn->ctrl & NFP_NET_CFG_CTRL_BPF) { + if (changed & NETIF_F_HW_TC && nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF) { nn_err(nn, "Cannot disable HW TC offload while in use\n"); return -EBUSY; } @@ -2754,16 +2760,16 @@ static int nfp_net_set_features(struct net_device *netdev, nn_dbg(nn, "Feature change 0x%llx -> 0x%llx (changed=0x%llx)\n", netdev->features, features, changed); - if (new_ctrl == nn->ctrl) + if (new_ctrl == nn->dp.ctrl) return 0; - nn_dbg(nn, "NIC ctrl: 0x%x -> 0x%x\n", nn->ctrl, new_ctrl); + nn_dbg(nn, "NIC ctrl: 0x%x -> 0x%x\n", nn->dp.ctrl, new_ctrl); nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl); err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN); if (err) return err; - nn->ctrl = new_ctrl; + nn->dp.ctrl = new_ctrl; return 0; } @@ -2846,7 +2852,7 @@ static void nfp_net_set_vxlan_port(struct nfp_net *nn, int idx, __be16 port) nn->vxlan_ports[idx] = port; - if (!(nn->ctrl & NFP_NET_CFG_CTRL_VXLAN)) + if (!(nn->dp.ctrl & NFP_NET_CFG_CTRL_VXLAN)) return; BUILD_BUG_ON(NFP_NET_N_VXLAN_PORTS & 1); @@ -2925,8 +2931,8 @@ static int nfp_net_xdp_offload(struct nfp_net *nn, struct bpf_prog *prog) if (!nfp_net_ebpf_capable(nn)) return -EINVAL; - if (nn->ctrl & NFP_NET_CFG_CTRL_BPF) { - if (!nn->bpf_offload_xdp) + if (nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF) { + if (!nn->dp.bpf_offload_xdp) return prog ? -EBUSY : 0; cmd.command = prog ? TC_CLSBPF_REPLACE : TC_CLSBPF_DESTROY; } else { @@ -2939,20 +2945,20 @@ static int nfp_net_xdp_offload(struct nfp_net *nn, struct bpf_prog *prog) /* Stop offload if replace not possible */ if (ret && cmd.command == TC_CLSBPF_REPLACE) nfp_net_xdp_offload(nn, NULL); - nn->bpf_offload_xdp = prog && !ret; + nn->dp.bpf_offload_xdp = prog && !ret; return ret; } static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) { struct nfp_net_ring_set rx = { - .n_rings = nn->num_rx_rings, - .mtu = nn->netdev->mtu, - .dcnt = nn->rxd_cnt, + .n_rings = nn->dp.num_rx_rings, + .mtu = nn->dp.netdev->mtu, + .dcnt = nn->dp.rxd_cnt, }; struct nfp_net_ring_set tx = { - .n_rings = nn->num_tx_rings, - .dcnt = nn->txd_cnt, + .n_rings = nn->dp.num_tx_rings, + .dcnt = nn->dp.txd_cnt, }; int err; @@ -2960,16 +2966,16 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) nn_err(nn, "Does not support bpf_xdp_adjust_head()\n"); return -EOPNOTSUPP; } - if (!prog && !nn->xdp_prog) + if (!prog && !nn->dp.xdp_prog) return 0; - if (prog && nn->xdp_prog) { - prog = xchg(&nn->xdp_prog, prog); + if (prog && nn->dp.xdp_prog) { + prog = xchg(&nn->dp.xdp_prog, prog); bpf_prog_put(prog); - nfp_net_xdp_offload(nn, nn->xdp_prog); + nfp_net_xdp_offload(nn, nn->dp.xdp_prog); return 0; } - tx.n_rings += prog ? nn->num_rx_rings : -nn->num_rx_rings; + tx.n_rings += prog ? nn->dp.num_rx_rings : -nn->dp.num_rx_rings; /* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */ err = nfp_net_ring_reconfig(nn, &prog, &rx, &tx); @@ -2980,7 +2986,7 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) if (prog) bpf_prog_put(prog); - nfp_net_xdp_offload(nn, nn->xdp_prog); + nfp_net_xdp_offload(nn, nn->dp.xdp_prog); return 0; } @@ -2993,7 +2999,7 @@ static int nfp_net_xdp(struct net_device *netdev, struct netdev_xdp *xdp) case XDP_SETUP_PROG: return nfp_net_xdp_setup(nn, xdp->prog); case XDP_QUERY_PROG: - xdp->prog_attached = !!nn->xdp_prog; + xdp->prog_attached = !!nn->dp.xdp_prog; return 0; default: return -EINVAL; @@ -3025,9 +3031,9 @@ static const struct net_device_ops nfp_net_netdev_ops = { void nfp_net_info(struct nfp_net *nn) { nn_info(nn, "Netronome NFP-6xxx %sNetdev: TxQs=%d/%d RxQs=%d/%d\n", - nn->is_vf ? "VF " : "", - nn->num_tx_rings, nn->max_tx_rings, - nn->num_rx_rings, nn->max_rx_rings); + nn->dp.is_vf ? "VF " : "", + nn->dp.num_tx_rings, nn->max_tx_rings, + nn->dp.num_rx_rings, nn->max_rx_rings); nn_info(nn, "VER: %d.%d.%d.%d, Maximum supported MTU: %d\n", nn->fw_ver.resv, nn->fw_ver.class, nn->fw_ver.major, nn->fw_ver.minor, @@ -3079,22 +3085,24 @@ struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev, SET_NETDEV_DEV(netdev, &pdev->dev); nn = netdev_priv(netdev); - nn->netdev = netdev; - nn->dev = &pdev->dev; + nn->dp.netdev = netdev; + nn->dp.dev = &pdev->dev; nn->pdev = pdev; nn->max_tx_rings = max_tx_rings; nn->max_rx_rings = max_rx_rings; - nn->num_tx_rings = min_t(unsigned int, max_tx_rings, num_online_cpus()); - nn->num_rx_rings = min_t(unsigned int, max_rx_rings, + nn->dp.num_tx_rings = min_t(unsigned int, + max_tx_rings, num_online_cpus()); + nn->dp.num_rx_rings = min_t(unsigned int, max_rx_rings, netif_get_num_default_rss_queues()); - nn->num_r_vecs = max(nn->num_tx_rings, nn->num_rx_rings); - nn->num_r_vecs = min_t(unsigned int, nn->num_r_vecs, num_online_cpus()); + nn->dp.num_r_vecs = max(nn->dp.num_tx_rings, nn->dp.num_rx_rings); + nn->dp.num_r_vecs = min_t(unsigned int, + nn->dp.num_r_vecs, num_online_cpus()); - nn->txd_cnt = NFP_NET_TX_DESCS_DEFAULT; - nn->rxd_cnt = NFP_NET_RX_DESCS_DEFAULT; + nn->dp.txd_cnt = NFP_NET_TX_DESCS_DEFAULT; + nn->dp.rxd_cnt = NFP_NET_RX_DESCS_DEFAULT; spin_lock_init(&nn->reconfig_lock); spin_lock_init(&nn->rx_filter_lock); @@ -3114,7 +3122,7 @@ struct nfp_net *nfp_net_netdev_alloc(struct pci_dev *pdev, */ void nfp_net_netdev_free(struct nfp_net *nn) { - free_netdev(nn->netdev); + free_netdev(nn->dp.netdev); } /** @@ -3156,7 +3164,7 @@ static void nfp_net_rss_init(struct nfp_net *nn) func_bit = find_first_bit(&rss_cap_hfunc, NFP_NET_CFG_RSS_HFUNCS); if (func_bit == NFP_NET_CFG_RSS_HFUNCS) { - dev_warn(nn->dev, + dev_warn(nn->dp.dev, "Bad RSS config, defaulting to Toeplitz hash\n"); func_bit = ETH_RSS_HASH_TOP_BIT; } @@ -3196,7 +3204,7 @@ int nfp_net_netdev_init(struct net_device *netdev) struct nfp_net *nn = netdev_priv(netdev); int err; - nn->chained_metadata_format = nn->fw_ver.major > 3; + nn->dp.chained_metadata_format = nn->fw_ver.major > 3; /* Get some of the read-only fields from the BAR */ nn->cap = nn_readl(nn, NFP_NET_CFG_CAP); @@ -3206,16 +3214,16 @@ int nfp_net_netdev_init(struct net_device *netdev) /* Determine RX packet/metadata boundary offset */ if (nn->fw_ver.major >= 2) - nn->rx_offset = nn_readl(nn, NFP_NET_CFG_RX_OFFSET); + nn->dp.rx_offset = nn_readl(nn, NFP_NET_CFG_RX_OFFSET); else - nn->rx_offset = NFP_NET_RX_OFFSET; + nn->dp.rx_offset = NFP_NET_RX_OFFSET; /* Set default MTU and Freelist buffer size */ if (nn->max_mtu < NFP_NET_DEFAULT_MTU) netdev->mtu = nn->max_mtu; else netdev->mtu = NFP_NET_DEFAULT_MTU; - nn->fl_bufsz = nfp_net_calc_fl_bufsz(nn, netdev->mtu); + nn->dp.fl_bufsz = nfp_net_calc_fl_bufsz(&nn->dp, netdev->mtu); /* Advertise/enable offloads based on capabilities * @@ -3226,31 +3234,31 @@ int nfp_net_netdev_init(struct net_device *netdev) netdev->hw_features = NETIF_F_HIGHDMA; if (nn->cap & NFP_NET_CFG_CTRL_RXCSUM) { netdev->hw_features |= NETIF_F_RXCSUM; - nn->ctrl |= NFP_NET_CFG_CTRL_RXCSUM; + nn->dp.ctrl |= NFP_NET_CFG_CTRL_RXCSUM; } if (nn->cap & NFP_NET_CFG_CTRL_TXCSUM) { netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; - nn->ctrl |= NFP_NET_CFG_CTRL_TXCSUM; + nn->dp.ctrl |= NFP_NET_CFG_CTRL_TXCSUM; } if (nn->cap & NFP_NET_CFG_CTRL_GATHER) { netdev->hw_features |= NETIF_F_SG; - nn->ctrl |= NFP_NET_CFG_CTRL_GATHER; + nn->dp.ctrl |= NFP_NET_CFG_CTRL_GATHER; } if ((nn->cap & NFP_NET_CFG_CTRL_LSO) && nn->fw_ver.major > 2) { netdev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6; - nn->ctrl |= NFP_NET_CFG_CTRL_LSO; + nn->dp.ctrl |= NFP_NET_CFG_CTRL_LSO; } if (nn->cap & NFP_NET_CFG_CTRL_RSS) { netdev->hw_features |= NETIF_F_RXHASH; nfp_net_rss_init(nn); - nn->ctrl |= NFP_NET_CFG_CTRL_RSS; + nn->dp.ctrl |= NFP_NET_CFG_CTRL_RSS; } if (nn->cap & NFP_NET_CFG_CTRL_VXLAN && nn->cap & NFP_NET_CFG_CTRL_NVGRE) { if (nn->cap & NFP_NET_CFG_CTRL_LSO) netdev->hw_features |= NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL; - nn->ctrl |= NFP_NET_CFG_CTRL_VXLAN | NFP_NET_CFG_CTRL_NVGRE; + nn->dp.ctrl |= NFP_NET_CFG_CTRL_VXLAN | NFP_NET_CFG_CTRL_NVGRE; netdev->hw_enc_features = netdev->hw_features; } @@ -3259,11 +3267,11 @@ int nfp_net_netdev_init(struct net_device *netdev) if (nn->cap & NFP_NET_CFG_CTRL_RXVLAN) { netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; - nn->ctrl |= NFP_NET_CFG_CTRL_RXVLAN; + nn->dp.ctrl |= NFP_NET_CFG_CTRL_RXVLAN; } if (nn->cap & NFP_NET_CFG_CTRL_TXVLAN) { netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX; - nn->ctrl |= NFP_NET_CFG_CTRL_TXVLAN; + nn->dp.ctrl |= NFP_NET_CFG_CTRL_TXVLAN; } netdev->features = netdev->hw_features; @@ -3276,14 +3284,14 @@ int nfp_net_netdev_init(struct net_device *netdev) /* Allow L2 Broadcast and Multicast through by default, if supported */ if (nn->cap & NFP_NET_CFG_CTRL_L2BC) - nn->ctrl |= NFP_NET_CFG_CTRL_L2BC; + nn->dp.ctrl |= NFP_NET_CFG_CTRL_L2BC; if (nn->cap & NFP_NET_CFG_CTRL_L2MC) - nn->ctrl |= NFP_NET_CFG_CTRL_L2MC; + nn->dp.ctrl |= NFP_NET_CFG_CTRL_L2MC; /* Allow IRQ moderation, if supported */ if (nn->cap & NFP_NET_CFG_CTRL_IRQMOD) { nfp_net_irqmod_init(nn); - nn->ctrl |= NFP_NET_CFG_CTRL_IRQMOD; + nn->dp.ctrl |= NFP_NET_CFG_CTRL_IRQMOD; } /* Stash the re-configuration queue away. First odd queue in TX Bar */ @@ -3322,9 +3330,9 @@ void nfp_net_netdev_clean(struct net_device *netdev) { struct nfp_net *nn = netdev_priv(netdev); - if (nn->xdp_prog) - bpf_prog_put(nn->xdp_prog); - if (nn->bpf_offload_xdp) + if (nn->dp.xdp_prog) + bpf_prog_put(nn->dp.xdp_prog); + if (nn->dp.bpf_offload_xdp) nfp_net_xdp_offload(nn, NULL); - unregister_netdev(nn->netdev); + unregister_netdev(nn->dp.netdev); } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c index edfa59e51fdd..74125584260b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c @@ -54,7 +54,7 @@ static int nfp_net_debugfs_rx_q_read(struct seq_file *file, void *data) goto out; nn = r_vec->nfp_net; rx_ring = r_vec->rx_ring; - if (!netif_running(nn->netdev)) + if (!netif_running(nn->dp.netdev)) goto out; rxd_cnt = rx_ring->cnt; @@ -145,7 +145,7 @@ static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data) if (!r_vec->nfp_net || !tx_ring) goto out; nn = r_vec->nfp_net; - if (!netif_running(nn->netdev)) + if (!netif_running(nn->dp.netdev)) goto out; txd_cnt = tx_ring->cnt; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index a1bca2dca0a5..4620c1bba96e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -127,9 +127,9 @@ static const struct _nfp_net_et_stats nfp_net_et_stats[] = { }; #define NN_ET_GLOBAL_STATS_LEN ARRAY_SIZE(nfp_net_et_stats) -#define NN_ET_RVEC_STATS_LEN (nn->num_r_vecs * 3) +#define NN_ET_RVEC_STATS_LEN (nn->dp.num_r_vecs * 3) #define NN_ET_RVEC_GATHER_STATS 7 -#define NN_ET_QUEUE_STATS_LEN ((nn->num_tx_rings + nn->num_rx_rings) * 2) +#define NN_ET_QUEUE_STATS_LEN ((nn->dp.num_tx_rings + nn->dp.num_rx_rings) * 2) #define NN_ET_STATS_LEN (NN_ET_GLOBAL_STATS_LEN + NN_ET_RVEC_GATHER_STATS + \ NN_ET_RVEC_STATS_LEN + NN_ET_QUEUE_STATS_LEN) @@ -180,29 +180,29 @@ static void nfp_net_get_ringparam(struct net_device *netdev, ring->rx_max_pending = NFP_NET_MAX_RX_DESCS; ring->tx_max_pending = NFP_NET_MAX_TX_DESCS; - ring->rx_pending = nn->rxd_cnt; - ring->tx_pending = nn->txd_cnt; + ring->rx_pending = nn->dp.rxd_cnt; + ring->tx_pending = nn->dp.txd_cnt; } static int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt) { struct nfp_net_ring_set *reconfig_rx = NULL, *reconfig_tx = NULL; struct nfp_net_ring_set rx = { - .n_rings = nn->num_rx_rings, - .mtu = nn->netdev->mtu, + .n_rings = nn->dp.num_rx_rings, + .mtu = nn->dp.netdev->mtu, .dcnt = rxd_cnt, }; struct nfp_net_ring_set tx = { - .n_rings = nn->num_tx_rings, + .n_rings = nn->dp.num_tx_rings, .dcnt = txd_cnt, }; - if (nn->rxd_cnt != rxd_cnt) + if (nn->dp.rxd_cnt != rxd_cnt) reconfig_rx = ℞ - if (nn->txd_cnt != txd_cnt) + if (nn->dp.txd_cnt != txd_cnt) reconfig_tx = &tx; - return nfp_net_ring_reconfig(nn, &nn->xdp_prog, + return nfp_net_ring_reconfig(nn, &nn->dp.xdp_prog, reconfig_rx, reconfig_tx); } @@ -224,11 +224,11 @@ static int nfp_net_set_ringparam(struct net_device *netdev, txd_cnt < NFP_NET_MIN_TX_DESCS || txd_cnt > NFP_NET_MAX_TX_DESCS) return -EINVAL; - if (nn->rxd_cnt == rxd_cnt && nn->txd_cnt == txd_cnt) + if (nn->dp.rxd_cnt == rxd_cnt && nn->dp.txd_cnt == txd_cnt) return 0; nn_dbg(nn, "Change ring size: RxQ %u->%u, TxQ %u->%u\n", - nn->rxd_cnt, rxd_cnt, nn->txd_cnt, txd_cnt); + nn->dp.rxd_cnt, rxd_cnt, nn->dp.txd_cnt, txd_cnt); return nfp_net_set_ring_size(nn, rxd_cnt, txd_cnt); } @@ -246,7 +246,7 @@ static void nfp_net_get_strings(struct net_device *netdev, memcpy(p, nfp_net_et_stats[i].name, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } - for (i = 0; i < nn->num_r_vecs; i++) { + for (i = 0; i < nn->dp.num_r_vecs; i++) { sprintf(p, "rvec_%u_rx_pkts", i); p += ETH_GSTRING_LEN; sprintf(p, "rvec_%u_tx_pkts", i); @@ -268,13 +268,13 @@ static void nfp_net_get_strings(struct net_device *netdev, p += ETH_GSTRING_LEN; strncpy(p, "tx_lso", ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; - for (i = 0; i < nn->num_tx_rings; i++) { + for (i = 0; i < nn->dp.num_tx_rings; i++) { sprintf(p, "txq_%u_pkts", i); p += ETH_GSTRING_LEN; sprintf(p, "txq_%u_bytes", i); p += ETH_GSTRING_LEN; } - for (i = 0; i < nn->num_rx_rings; i++) { + for (i = 0; i < nn->dp.num_rx_rings; i++) { sprintf(p, "rxq_%u_pkts", i); p += ETH_GSTRING_LEN; sprintf(p, "rxq_%u_bytes", i); @@ -312,7 +312,7 @@ static void nfp_net_get_stats(struct net_device *netdev, break; } } - for (j = 0; j < nn->num_r_vecs; j++) { + for (j = 0; j < nn->dp.num_r_vecs; j++) { unsigned int start; do { @@ -338,13 +338,13 @@ static void nfp_net_get_stats(struct net_device *netdev, } for (j = 0; j < NN_ET_RVEC_GATHER_STATS; j++) data[i++] = gathered_stats[j]; - for (j = 0; j < nn->num_tx_rings; j++) { + for (j = 0; j < nn->dp.num_tx_rings; j++) { io_p = nn->ctrl_bar + NFP_NET_CFG_TXR_STATS(j); data[i++] = readq(io_p); io_p = nn->ctrl_bar + NFP_NET_CFG_TXR_STATS(j) + 8; data[i++] = readq(io_p); } - for (j = 0; j < nn->num_rx_rings; j++) { + for (j = 0; j < nn->dp.num_rx_rings; j++) { io_p = nn->ctrl_bar + NFP_NET_CFG_RXR_STATS(j); data[i++] = readq(io_p); io_p = nn->ctrl_bar + NFP_NET_CFG_RXR_STATS(j) + 8; @@ -411,7 +411,7 @@ static int nfp_net_get_rxnfc(struct net_device *netdev, switch (cmd->cmd) { case ETHTOOL_GRXRINGS: - cmd->data = nn->num_rx_rings; + cmd->data = nn->dp.num_rx_rings; return 0; case ETHTOOL_GRXFH: return nfp_net_get_rss_hash_opts(nn, cmd); @@ -745,16 +745,16 @@ static void nfp_net_get_channels(struct net_device *netdev, struct nfp_net *nn = netdev_priv(netdev); unsigned int num_tx_rings; - num_tx_rings = nn->num_tx_rings; - if (nn->xdp_prog) - num_tx_rings -= nn->num_rx_rings; + num_tx_rings = nn->dp.num_tx_rings; + if (nn->dp.xdp_prog) + num_tx_rings -= nn->dp.num_rx_rings; channel->max_rx = min(nn->max_rx_rings, nn->max_r_vecs); channel->max_tx = min(nn->max_tx_rings, nn->max_r_vecs); channel->max_combined = min(channel->max_rx, channel->max_tx); channel->max_other = NFP_NET_NON_Q_VECTORS; - channel->combined_count = min(nn->num_rx_rings, num_tx_rings); - channel->rx_count = nn->num_rx_rings - channel->combined_count; + channel->combined_count = min(nn->dp.num_rx_rings, num_tx_rings); + channel->rx_count = nn->dp.num_rx_rings - channel->combined_count; channel->tx_count = num_tx_rings - channel->combined_count; channel->other_count = NFP_NET_NON_Q_VECTORS; } @@ -765,25 +765,25 @@ static int nfp_net_set_num_rings(struct nfp_net *nn, unsigned int total_rx, struct nfp_net_ring_set *reconfig_rx = NULL, *reconfig_tx = NULL; struct nfp_net_ring_set rx = { .n_rings = total_rx, - .mtu = nn->netdev->mtu, - .dcnt = nn->rxd_cnt, + .mtu = nn->dp.netdev->mtu, + .dcnt = nn->dp.rxd_cnt, }; struct nfp_net_ring_set tx = { .n_rings = total_tx, - .dcnt = nn->txd_cnt, + .dcnt = nn->dp.txd_cnt, }; - if (nn->num_rx_rings != total_rx) + if (nn->dp.num_rx_rings != total_rx) reconfig_rx = ℞ - if (nn->num_stack_tx_rings != total_tx || - (nn->xdp_prog && reconfig_rx)) + if (nn->dp.num_stack_tx_rings != total_tx || + (nn->dp.xdp_prog && reconfig_rx)) reconfig_tx = &tx; /* nfp_net_check_config() will catch tx.n_rings > nn->max_tx_rings */ - if (nn->xdp_prog) + if (nn->dp.xdp_prog) tx.n_rings += total_rx; - return nfp_net_ring_reconfig(nn, &nn->xdp_prog, + return nfp_net_ring_reconfig(nn, &nn->dp.xdp_prog, reconfig_rx, reconfig_tx); } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 8a9b3f3b95a8..3935d19a273d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -130,7 +130,7 @@ err_area: } static void -nfp_net_get_mac_addr_hwinfo(struct nfp_net *nn, struct nfp_cpp *cpp, +nfp_net_get_mac_addr_hwinfo(struct nfp_net_dp *dp, struct nfp_cpp *cpp, unsigned int id) { u8 mac_addr[ETH_ALEN]; @@ -141,22 +141,22 @@ nfp_net_get_mac_addr_hwinfo(struct nfp_net *nn, struct nfp_cpp *cpp, mac_str = nfp_hwinfo_lookup(cpp, name); if (!mac_str) { - dev_warn(nn->dev, "Can't lookup MAC address. Generate\n"); - eth_hw_addr_random(nn->netdev); + dev_warn(dp->dev, "Can't lookup MAC address. Generate\n"); + eth_hw_addr_random(dp->netdev); return; } if (sscanf(mac_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &mac_addr[0], &mac_addr[1], &mac_addr[2], &mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6) { - dev_warn(nn->dev, + dev_warn(dp->dev, "Can't parse MAC address (%s). Generate.\n", mac_str); - eth_hw_addr_random(nn->netdev); + eth_hw_addr_random(dp->netdev); return; } - ether_addr_copy(nn->netdev->dev_addr, mac_addr); - ether_addr_copy(nn->netdev->perm_addr, mac_addr); + ether_addr_copy(dp->netdev->dev_addr, mac_addr); + ether_addr_copy(dp->netdev->perm_addr, mac_addr); } /** @@ -179,12 +179,12 @@ nfp_net_get_mac_addr(struct nfp_net *nn, struct nfp_pf *pf, unsigned int id) nn->eth_port = &pf->eth_tbl->ports[i]; - ether_addr_copy(nn->netdev->dev_addr, mac_addr); - ether_addr_copy(nn->netdev->perm_addr, mac_addr); + ether_addr_copy(nn->dp.netdev->dev_addr, mac_addr); + ether_addr_copy(nn->dp.netdev->perm_addr, mac_addr); return; } - nfp_net_get_mac_addr_hwinfo(nn, pf->cpp, id); + nfp_net_get_mac_addr_hwinfo(&nn->dp, pf->cpp, id); } static unsigned int nfp_net_pf_get_num_ports(struct nfp_pf *pf) @@ -309,7 +309,7 @@ nfp_net_pf_alloc_port_netdev(struct nfp_pf *pf, void __iomem *ctrl_bar, nn->ctrl_bar = ctrl_bar; nn->tx_bar = tx_bar; nn->rx_bar = rx_bar; - nn->is_vf = 0; + nn->dp.is_vf = 0; nn->stride_rx = stride; nn->stride_tx = stride; @@ -331,7 +331,7 @@ nfp_net_pf_init_port_netdev(struct nfp_pf *pf, struct nfp_net *nn, */ nn->me_freq_mhz = 1200; - err = nfp_net_netdev_init(nn->netdev); + err = nfp_net_netdev_init(nn->dp.netdev); if (err) return err; @@ -400,7 +400,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf, /* Get MSI-X vectors */ wanted_irqs = 0; list_for_each_entry(nn, &pf->ports, port_list) - wanted_irqs += NFP_NET_NON_Q_VECTORS + nn->num_r_vecs; + wanted_irqs += NFP_NET_NON_Q_VECTORS + nn->dp.num_r_vecs; pf->irq_entries = kcalloc(wanted_irqs, sizeof(*pf->irq_entries), GFP_KERNEL); if (!pf->irq_entries) { @@ -445,7 +445,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf, err_prev_deinit: list_for_each_entry_continue_reverse(nn, &pf->ports, port_list) { nfp_net_debugfs_dir_clean(&nn->debugfs_dir); - nfp_net_netdev_clean(nn->netdev); + nfp_net_netdev_clean(nn->dp.netdev); } nfp_net_irqs_disable(pf->pdev); err_vec_free: @@ -571,7 +571,7 @@ void nfp_net_pci_remove(struct nfp_pf *pf) list_for_each_entry(nn, &pf->ports, port_list) { nfp_net_debugfs_dir_clean(&nn->debugfs_dir); - nfp_net_netdev_clean(nn->netdev); + nfp_net_netdev_clean(nn->dp.netdev); } nfp_net_pf_free_netdevs(pf); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c index f6ed1aa9d94b..b5b6f69d1e0f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c @@ -58,7 +58,7 @@ void nfp_net_filter_stats_timer(unsigned long data) spin_lock_bh(&nn->rx_filter_lock); - if (nn->ctrl & NFP_NET_CFG_CTRL_BPF) + if (nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF) mod_timer(&nn->rx_filter_stats_timer, jiffies + NFP_NET_STAT_POLL_IVL); @@ -132,7 +132,7 @@ nfp_net_bpf_get_act(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf) return NN_ACT_TC_DROP; if (is_tcf_mirred_egress_redirect(a) && - tcf_mirred_ifindex(a) == nn->netdev->ifindex) + tcf_mirred_ifindex(a) == nn->dp.netdev->ifindex) return NN_ACT_TC_REDIR; } @@ -160,7 +160,7 @@ nfp_net_bpf_offload_prepare(struct nfp_net *nn, act = ret; max_mtu = nn_readb(nn, NFP_NET_CFG_BPF_INL_MTU) * 64 - 32; - if (max_mtu < nn->netdev->mtu) { + if (max_mtu < nn->dp.netdev->mtu) { nn_info(nn, "BPF offload not supported with MTU larger than HW packet split boundary\n"); return -ENOTSUPP; } @@ -168,7 +168,7 @@ nfp_net_bpf_offload_prepare(struct nfp_net *nn, start_off = nn_readw(nn, NFP_NET_CFG_BPF_START); done_off = nn_readw(nn, NFP_NET_CFG_BPF_DONE); - *code = dma_zalloc_coherent(nn->dev, code_sz, dma_addr, GFP_KERNEL); + *code = dma_zalloc_coherent(nn->dp.dev, code_sz, dma_addr, GFP_KERNEL); if (!*code) return -ENOMEM; @@ -180,7 +180,7 @@ nfp_net_bpf_offload_prepare(struct nfp_net *nn, return 0; out: - dma_free_coherent(nn->dev, code_sz, *code, *dma_addr); + dma_free_coherent(nn->dp.dev, code_sz, *code, *dma_addr); return ret; } @@ -193,7 +193,7 @@ nfp_net_bpf_load_and_start(struct nfp_net *nn, u32 tc_flags, u64 bpf_addr = dma_addr; int err; - nn->bpf_offload_skip_sw = !!(tc_flags & TCA_CLS_FLAGS_SKIP_SW); + nn->dp.bpf_offload_skip_sw = !!(tc_flags & TCA_CLS_FLAGS_SKIP_SW); if (dense_mode) bpf_addr |= NFP_NET_CFG_BPF_CFG_8CTX; @@ -207,13 +207,13 @@ nfp_net_bpf_load_and_start(struct nfp_net *nn, u32 tc_flags, nn_err(nn, "FW command error while loading BPF: %d\n", err); /* Enable passing packets through BPF function */ - nn->ctrl |= NFP_NET_CFG_CTRL_BPF; - nn_writel(nn, NFP_NET_CFG_CTRL, nn->ctrl); + nn->dp.ctrl |= NFP_NET_CFG_CTRL_BPF; + nn_writel(nn, NFP_NET_CFG_CTRL, nn->dp.ctrl); err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN); if (err) nn_err(nn, "FW command error while enabling BPF: %d\n", err); - dma_free_coherent(nn->dev, code_sz, code, dma_addr); + dma_free_coherent(nn->dp.dev, code_sz, code, dma_addr); nfp_net_bpf_stats_reset(nn); mod_timer(&nn->rx_filter_stats_timer, jiffies + NFP_NET_STAT_POLL_IVL); @@ -221,16 +221,16 @@ nfp_net_bpf_load_and_start(struct nfp_net *nn, u32 tc_flags, static int nfp_net_bpf_stop(struct nfp_net *nn) { - if (!(nn->ctrl & NFP_NET_CFG_CTRL_BPF)) + if (!(nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF)) return 0; spin_lock_bh(&nn->rx_filter_lock); - nn->ctrl &= ~NFP_NET_CFG_CTRL_BPF; + nn->dp.ctrl &= ~NFP_NET_CFG_CTRL_BPF; spin_unlock_bh(&nn->rx_filter_lock); - nn_writel(nn, NFP_NET_CFG_CTRL, nn->ctrl); + nn_writel(nn, NFP_NET_CFG_CTRL, nn->dp.ctrl); del_timer_sync(&nn->rx_filter_stats_timer); - nn->bpf_offload_skip_sw = 0; + nn->dp.bpf_offload_skip_sw = 0; return nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_GEN); } @@ -254,7 +254,7 @@ int nfp_net_bpf_offload(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf) * frames which didn't have BPF applied in the hardware should * be fine if software fallback is available, though. */ - if (nn->bpf_offload_skip_sw) + if (nn->dp.bpf_offload_skip_sw) return -EBUSY; err = nfp_net_bpf_offload_prepare(nn, cls_bpf, &res, &code, @@ -269,7 +269,7 @@ int nfp_net_bpf_offload(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf) return 0; case TC_CLSBPF_ADD: - if (nn->ctrl & NFP_NET_CFG_CTRL_BPF) + if (nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF) return -EBUSY; err = nfp_net_bpf_offload_prepare(nn, cls_bpf, &res, &code, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c index 39407f7cc586..bc0bdbc1c8fd 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c @@ -84,12 +84,12 @@ static void nfp_netvf_get_mac_addr(struct nfp_net *nn) put_unaligned_be16(nn_readw(nn, NFP_NET_CFG_MACADDR + 6), &mac_addr[4]); if (!is_valid_ether_addr(mac_addr)) { - eth_hw_addr_random(nn->netdev); + eth_hw_addr_random(nn->dp.netdev); return; } - ether_addr_copy(nn->netdev->dev_addr, mac_addr); - ether_addr_copy(nn->netdev->perm_addr, mac_addr); + ether_addr_copy(nn->dp.netdev->dev_addr, mac_addr); + ether_addr_copy(nn->dp.netdev->perm_addr, mac_addr); } static int nfp_netvf_pci_probe(struct pci_dev *pdev, @@ -211,7 +211,7 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, nn->fw_ver = fw_ver; nn->ctrl_bar = ctrl_bar; - nn->is_vf = 1; + nn->dp.is_vf = 1; nn->stride_tx = stride; nn->stride_rx = stride; @@ -268,7 +268,8 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, num_irqs = nfp_net_irqs_alloc(pdev, vf->irq_entries, NFP_NET_MIN_PORT_IRQS, - NFP_NET_NON_Q_VECTORS + nn->num_r_vecs); + NFP_NET_NON_Q_VECTORS + + nn->dp.num_r_vecs); if (!num_irqs) { nn_warn(nn, "Unable to allocate MSI-X Vectors. Exiting\n"); err = -EIO; @@ -282,7 +283,7 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, */ nn->me_freq_mhz = 1200; - err = nfp_net_netdev_init(nn->netdev); + err = nfp_net_netdev_init(nn->dp.netdev); if (err) goto err_irqs_disable; @@ -327,7 +328,7 @@ static void nfp_netvf_pci_remove(struct pci_dev *pdev) nfp_net_debugfs_dir_clean(&nn->debugfs_dir); nfp_net_debugfs_dir_clean(&vf->ddir); - nfp_net_netdev_clean(nn->netdev); + nfp_net_netdev_clean(nn->dp.netdev); nfp_net_irqs_disable(pdev); -- cgit From d2b84397601f370ca11a5a37a40787d31af778b7 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 10 Mar 2017 10:38:28 -0800 Subject: nfp: move control BAR pointer into data path structure Control BAR pointer is used to unmask interrupts so it should be in the first cacheline of adapter structure. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 21 +++++++++++---------- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 14 +++++++------- drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 2 +- drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c | 4 ++-- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 7d2c38604372..90a44fad6bd5 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -447,6 +447,7 @@ struct nfp_stat_pair { * @xdp_prog: Installed XDP program * @tx_rings: Array of pre-allocated TX ring structures * @rx_rings: Array of pre-allocated RX ring structures + * @ctrl_bar: Pointer to mapped control BAR * * @txd_cnt: Size of the TX ring in number of descriptors * @rxd_cnt: Size of the RX ring in number of descriptors @@ -474,6 +475,8 @@ struct nfp_net_dp { struct nfp_net_tx_ring *tx_rings; struct nfp_net_rx_ring *rx_rings; + u8 __iomem *ctrl_bar; + /* Cold data follows */ unsigned int txd_cnt; @@ -527,7 +530,6 @@ struct nfp_net_dp { * @vxlan_ports: VXLAN ports for RX inner csum offload communicated to HW * @vxlan_usecnt: IPv4/IPv6 VXLAN port use counts * @qcp_cfg: Pointer to QCP queue used for configuration notification - * @ctrl_bar: Pointer to mapped control BAR * @tx_bar: Pointer to mapped TX queues * @rx_bar: Pointer to mapped FL/RX queues * @debugfs_dir: Device directory in debugfs @@ -595,7 +597,6 @@ struct nfp_net { u8 __iomem *qcp_cfg; - u8 __iomem *ctrl_bar; u8 __iomem *tx_bar; u8 __iomem *rx_bar; @@ -622,42 +623,42 @@ struct nfp_net_ring_set { */ static inline u16 nn_readb(struct nfp_net *nn, int off) { - return readb(nn->ctrl_bar + off); + return readb(nn->dp.ctrl_bar + off); } static inline void nn_writeb(struct nfp_net *nn, int off, u8 val) { - writeb(val, nn->ctrl_bar + off); + writeb(val, nn->dp.ctrl_bar + off); } static inline u16 nn_readw(struct nfp_net *nn, int off) { - return readw(nn->ctrl_bar + off); + return readw(nn->dp.ctrl_bar + off); } static inline void nn_writew(struct nfp_net *nn, int off, u16 val) { - writew(val, nn->ctrl_bar + off); + writew(val, nn->dp.ctrl_bar + off); } static inline u32 nn_readl(struct nfp_net *nn, int off) { - return readl(nn->ctrl_bar + off); + return readl(nn->dp.ctrl_bar + off); } static inline void nn_writel(struct nfp_net *nn, int off, u32 val) { - writel(val, nn->ctrl_bar + off); + writel(val, nn->dp.ctrl_bar + off); } static inline u64 nn_readq(struct nfp_net *nn, int off) { - return readq(nn->ctrl_bar + off); + return readq(nn->dp.ctrl_bar + off); } static inline void nn_writeq(struct nfp_net *nn, int off, u64 val) { - writeq(val, nn->ctrl_bar + off); + writeq(val, nn->dp.ctrl_bar + off); } /* Flush posted PCI writes by reading something without side effects */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 4620c1bba96e..969c30589f23 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -307,7 +307,7 @@ static void nfp_net_get_stats(struct net_device *netdev, break; case NFP_NET_DEV_ET_STATS: - io_p = nn->ctrl_bar + nfp_net_et_stats[i].off; + io_p = nn->dp.ctrl_bar + nfp_net_et_stats[i].off; data[i] = readq(io_p); break; } @@ -339,15 +339,15 @@ static void nfp_net_get_stats(struct net_device *netdev, for (j = 0; j < NN_ET_RVEC_GATHER_STATS; j++) data[i++] = gathered_stats[j]; for (j = 0; j < nn->dp.num_tx_rings; j++) { - io_p = nn->ctrl_bar + NFP_NET_CFG_TXR_STATS(j); + io_p = nn->dp.ctrl_bar + NFP_NET_CFG_TXR_STATS(j); data[i++] = readq(io_p); - io_p = nn->ctrl_bar + NFP_NET_CFG_TXR_STATS(j) + 8; + io_p = nn->dp.ctrl_bar + NFP_NET_CFG_TXR_STATS(j) + 8; data[i++] = readq(io_p); } for (j = 0; j < nn->dp.num_rx_rings; j++) { - io_p = nn->ctrl_bar + NFP_NET_CFG_RXR_STATS(j); + io_p = nn->dp.ctrl_bar + NFP_NET_CFG_RXR_STATS(j); data[i++] = readq(io_p); - io_p = nn->ctrl_bar + NFP_NET_CFG_RXR_STATS(j) + 8; + io_p = nn->dp.ctrl_bar + NFP_NET_CFG_RXR_STATS(j) + 8; data[i++] = readq(io_p); } } @@ -461,7 +461,7 @@ static int nfp_net_set_rss_hash_opt(struct nfp_net *nn, if (new_rss_cfg == nn->rss_cfg) return 0; - writel(new_rss_cfg, nn->ctrl_bar + NFP_NET_CFG_RSS_CTRL); + writel(new_rss_cfg, nn->dp.ctrl_bar + NFP_NET_CFG_RSS_CTRL); err = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_RSS); if (err) return err; @@ -573,7 +573,7 @@ static void nfp_net_get_regs(struct net_device *netdev, regs->version = nn_readl(nn, NFP_NET_CFG_VERSION); for (i = 0; i < NFP_NET_CFG_BAR_SZ / sizeof(u32); i++) - regs_buf[i] = readl(nn->ctrl_bar + (i * sizeof(u32))); + regs_buf[i] = readl(nn->dp.ctrl_bar + (i * sizeof(u32))); } static int nfp_net_get_coalesce(struct net_device *netdev, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 3935d19a273d..2025cb7c6d90 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -306,7 +306,7 @@ nfp_net_pf_alloc_port_netdev(struct nfp_pf *pf, void __iomem *ctrl_bar, nn->cpp = pf->cpp; nn->fw_ver = *fw_ver; - nn->ctrl_bar = ctrl_bar; + nn->dp.ctrl_bar = ctrl_bar; nn->tx_bar = tx_bar; nn->rx_bar = rx_bar; nn->dp.is_vf = 0; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c index bc0bdbc1c8fd..86e61be6f35c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c @@ -210,7 +210,7 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev, vf->nn = nn; nn->fw_ver = fw_ver; - nn->ctrl_bar = ctrl_bar; + nn->dp.ctrl_bar = ctrl_bar; nn->dp.is_vf = 1; nn->stride_tx = stride; nn->stride_rx = stride; @@ -338,7 +338,7 @@ static void nfp_netvf_pci_remove(struct pci_dev *pdev) } else { iounmap(vf->q_bar); } - iounmap(nn->ctrl_bar); + iounmap(nn->dp.ctrl_bar); nfp_net_netdev_free(nn); -- cgit From 783496b0ddc2bd4ad561864138596ebb336a7100 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 10 Mar 2017 10:38:29 -0800 Subject: nfp: pass new data path to ring reconfig Make callers of nfp_net_ring_reconfig() pass newly allocated data path structure. We will gradually make use of that structure instead of passing parameters around to all the allocation functions. This commit adds allocation and propagation of new data path struct, no parameters are converted, yet. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 5 +- .../net/ethernet/netronome/nfp/nfp_net_common.c | 108 ++++++++++++++------- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 14 ++- 3 files changed, 91 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 90a44fad6bd5..74f6d485351f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -812,8 +812,11 @@ void nfp_net_irqs_disable(struct pci_dev *pdev); void nfp_net_irqs_assign(struct nfp_net *nn, struct msix_entry *irq_entries, unsigned int n); + +struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn); int -nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog, +nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new, + struct bpf_prog **xdp_prog, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx); #ifdef CONFIG_NFP_DEBUG diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 951d511643f1..7afefb44b642 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1127,6 +1127,7 @@ nfp_net_free_frag(void *frag, bool xdp) /** * nfp_net_rx_alloc_one() - Allocate and map page frag for RX + * @dp: NFP Net data path struct * @rx_ring: RX ring structure of the skb * @dma_addr: Pointer to storage for DMA address (output param) * @fl_bufsz: size of freelist buffers @@ -1137,10 +1138,10 @@ nfp_net_free_frag(void *frag, bool xdp) * Return: allocated page frag or NULL on failure. */ static void * -nfp_net_rx_alloc_one(struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr, +nfp_net_rx_alloc_one(struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr, unsigned int fl_bufsz, bool xdp) { - struct nfp_net_dp *dp = &rx_ring->r_vec->nfp_net->dp; int direction; void *frag; @@ -1299,7 +1300,7 @@ nfp_net_rx_ring_bufs_alloc(struct nfp_net_dp *dp, for (i = 0; i < rx_ring->cnt - 1; i++) { rxbufs[i].frag = - nfp_net_rx_alloc_one(rx_ring, &rxbufs[i].dma_addr, + nfp_net_rx_alloc_one(dp, rx_ring, &rxbufs[i].dma_addr, rx_ring->bufsz, xdp); if (!rxbufs[i].frag) { nfp_net_rx_ring_bufs_free(dp, rx_ring, xdp); @@ -1784,7 +1785,8 @@ err_alloc: } static struct nfp_net_tx_ring * -nfp_net_tx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s, +nfp_net_tx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_dp *dp, + struct nfp_net_ring_set *s, unsigned int num_stack_tx_rings) { struct nfp_net_tx_ring *rings; @@ -1900,10 +1902,10 @@ err_alloc: } static struct nfp_net_rx_ring * -nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s, - bool xdp) +nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_dp *dp, + struct nfp_net_ring_set *s, bool xdp) { - unsigned int fl_bufsz = nfp_net_calc_fl_bufsz(&nn->dp, s->mtu); + unsigned int fl_bufsz = nfp_net_calc_fl_bufsz(dp, s->mtu); struct nfp_net_rx_ring *rings; unsigned int r; @@ -1917,7 +1919,7 @@ nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s, if (nfp_net_rx_ring_alloc(&rings[r], fl_bufsz, s->dcnt)) goto err_free_prev; - if (nfp_net_rx_ring_bufs_alloc(&nn->dp, &rings[r], xdp)) + if (nfp_net_rx_ring_bufs_alloc(dp, &rings[r], xdp)) goto err_free_ring; } @@ -1925,7 +1927,7 @@ nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_ring_set *s, err_free_prev: while (r--) { - nfp_net_rx_ring_bufs_free(&nn->dp, &rings[r], xdp); + nfp_net_rx_ring_bufs_free(dp, &rings[r], xdp); err_free_ring: nfp_net_rx_ring_free(&rings[r]); } @@ -2295,14 +2297,15 @@ static int nfp_net_netdev_open(struct net_device *netdev) goto err_cleanup_vec_p; } - nn->dp.rx_rings = nfp_net_rx_ring_set_prepare(nn, &rx, nn->dp.xdp_prog); + nn->dp.rx_rings = nfp_net_rx_ring_set_prepare(nn, &nn->dp, &rx, + nn->dp.xdp_prog); if (!nn->dp.rx_rings) { err = -ENOMEM; goto err_cleanup_vec; } - nn->dp.tx_rings = nfp_net_tx_ring_set_prepare(nn, &tx, - nn->dp.num_stack_tx_rings); + nn->dp.tx_rings = nfp_net_tx_ring_set_prepare(nn, &nn->dp, &tx, + nn->dp.num_stack_tx_rings); if (!nn->dp.tx_rings) { err = -ENOMEM; goto err_free_rx_rings; @@ -2464,7 +2467,8 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn) } static int -nfp_net_ring_swap_enable(struct nfp_net *nn, unsigned int *num_vecs, +nfp_net_ring_swap_enable(struct nfp_net *nn, struct nfp_net_dp *dp, + unsigned int *num_vecs, unsigned int *stack_tx_rings, struct bpf_prog **xdp_prog, struct nfp_net_ring_set *rx, @@ -2502,14 +2506,35 @@ nfp_net_ring_swap_enable(struct nfp_net *nn, unsigned int *num_vecs, return __nfp_net_set_config_and_enable(nn); } +struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn) +{ + struct nfp_net_dp *new; + + new = kmalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return NULL; + + *new = nn->dp; + + /* Clear things which need to be recomputed */ + new->fl_bufsz = 0; + new->tx_rings = NULL; + new->rx_rings = NULL; + new->num_r_vecs = 0; + new->num_stack_tx_rings = 0; + + return new; +} + static int -nfp_net_check_config(struct nfp_net *nn, struct bpf_prog *xdp_prog, +nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp, + struct bpf_prog *xdp_prog, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) { /* XDP-enabled tests */ if (!xdp_prog) return 0; - if (rx && nfp_net_calc_fl_bufsz(&nn->dp, rx->mtu) > PAGE_SIZE) { + if (rx && nfp_net_calc_fl_bufsz(dp, rx->mtu) > PAGE_SIZE) { nn_warn(nn, "MTU too large w/ XDP enabled\n"); return -EINVAL; } @@ -2522,7 +2547,8 @@ nfp_net_check_config(struct nfp_net *nn, struct bpf_prog *xdp_prog, } static void -nfp_net_ring_reconfig_down(struct nfp_net *nn, struct bpf_prog **xdp_prog, +nfp_net_ring_reconfig_down(struct nfp_net *nn, struct nfp_net_dp *dp, + struct bpf_prog **xdp_prog, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx, unsigned int stack_tx_rings, unsigned int num_vecs) @@ -2542,26 +2568,28 @@ nfp_net_ring_reconfig_down(struct nfp_net *nn, struct bpf_prog **xdp_prog, } int -nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog, +nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, + struct bpf_prog **xdp_prog, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) { unsigned int stack_tx_rings, num_vecs, r; int err; - stack_tx_rings = tx ? tx->n_rings : nn->dp.num_tx_rings; + stack_tx_rings = tx ? tx->n_rings : dp->num_tx_rings; if (*xdp_prog) - stack_tx_rings -= rx ? rx->n_rings : nn->dp.num_rx_rings; + stack_tx_rings -= rx ? rx->n_rings : dp->num_rx_rings; - num_vecs = max(rx ? rx->n_rings : nn->dp.num_rx_rings, stack_tx_rings); + num_vecs = max(rx ? rx->n_rings : dp->num_rx_rings, stack_tx_rings); - err = nfp_net_check_config(nn, *xdp_prog, rx, tx); + err = nfp_net_check_config(nn, dp, *xdp_prog, rx, tx); if (err) - return err; + goto exit_free_dp; - if (!netif_running(nn->dp.netdev)) { - nfp_net_ring_reconfig_down(nn, xdp_prog, rx, tx, + if (!netif_running(dp->netdev)) { + nfp_net_ring_reconfig_down(nn, dp, xdp_prog, rx, tx, stack_tx_rings, num_vecs); - return 0; + err = 0; + goto exit_free_dp; } /* Prepare new rings */ @@ -2573,13 +2601,13 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog, } } if (rx) { - if (!nfp_net_rx_ring_set_prepare(nn, rx, *xdp_prog)) { + if (!nfp_net_rx_ring_set_prepare(nn, dp, rx, *xdp_prog)) { err = -ENOMEM; goto err_cleanup_vecs; } } if (tx) { - if (!nfp_net_tx_ring_set_prepare(nn, tx, stack_tx_rings)) { + if (!nfp_net_tx_ring_set_prepare(nn, dp, tx, stack_tx_rings)) { err = -ENOMEM; goto err_free_rx; } @@ -2589,7 +2617,7 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog, nfp_net_close_stack(nn); nfp_net_clear_config_and_disable(nn); - err = nfp_net_ring_swap_enable(nn, &num_vecs, &stack_tx_rings, + err = nfp_net_ring_swap_enable(nn, dp, &num_vecs, &stack_tx_rings, xdp_prog, rx, tx); if (err) { int err2; @@ -2597,7 +2625,8 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog, nfp_net_clear_config_and_disable(nn); /* Try with old configuration and old rings */ - err2 = nfp_net_ring_swap_enable(nn, &num_vecs, &stack_tx_rings, + err2 = nfp_net_ring_swap_enable(nn, dp, &num_vecs, + &stack_tx_rings, xdp_prog, rx, tx); if (err2) nn_err(nn, "Can't restore ring config - FW communication failed (%d,%d)\n", @@ -2607,20 +2636,23 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct bpf_prog **xdp_prog, nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); if (rx) - nfp_net_rx_ring_set_free(&nn->dp, rx, *xdp_prog); + nfp_net_rx_ring_set_free(dp, rx, *xdp_prog); if (tx) nfp_net_tx_ring_set_free(tx); nfp_net_open_stack(nn); +exit_free_dp: + kfree(dp); return err; err_free_rx: if (rx) - nfp_net_rx_ring_set_free(&nn->dp, rx, *xdp_prog); + nfp_net_rx_ring_set_free(dp, rx, *xdp_prog); err_cleanup_vecs: for (r = num_vecs - 1; r >= nn->dp.num_r_vecs; r--) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); + kfree(dp); return err; } @@ -2632,8 +2664,13 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) .mtu = new_mtu, .dcnt = nn->dp.rxd_cnt, }; + struct nfp_net_dp *dp; + + dp = nfp_net_clone_dp(nn); + if (!dp) + return -ENOMEM; - return nfp_net_ring_reconfig(nn, &nn->dp.xdp_prog, &rx, NULL); + return nfp_net_ring_reconfig(nn, dp, &nn->dp.xdp_prog, &rx, NULL); } static void nfp_net_stat64(struct net_device *netdev, @@ -2960,6 +2997,7 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) .n_rings = nn->dp.num_tx_rings, .dcnt = nn->dp.txd_cnt, }; + struct nfp_net_dp *dp; int err; if (prog && prog->xdp_adjust_head) { @@ -2975,10 +3013,14 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) return 0; } + dp = nfp_net_clone_dp(nn); + if (!dp) + return -ENOMEM; + tx.n_rings += prog ? nn->dp.num_rx_rings : -nn->dp.num_rx_rings; /* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */ - err = nfp_net_ring_reconfig(nn, &prog, &rx, &tx); + err = nfp_net_ring_reconfig(nn, dp, &prog, &rx, &tx); if (err) return err; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 969c30589f23..326ccd74a4bf 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -196,13 +196,18 @@ static int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt) .n_rings = nn->dp.num_tx_rings, .dcnt = txd_cnt, }; + struct nfp_net_dp *dp; if (nn->dp.rxd_cnt != rxd_cnt) reconfig_rx = ℞ if (nn->dp.txd_cnt != txd_cnt) reconfig_tx = &tx; - return nfp_net_ring_reconfig(nn, &nn->dp.xdp_prog, + dp = nfp_net_clone_dp(nn); + if (!dp) + return -ENOMEM; + + return nfp_net_ring_reconfig(nn, dp, &nn->dp.xdp_prog, reconfig_rx, reconfig_tx); } @@ -772,6 +777,7 @@ static int nfp_net_set_num_rings(struct nfp_net *nn, unsigned int total_rx, .n_rings = total_tx, .dcnt = nn->dp.txd_cnt, }; + struct nfp_net_dp *dp; if (nn->dp.num_rx_rings != total_rx) reconfig_rx = ℞ @@ -783,7 +789,11 @@ static int nfp_net_set_num_rings(struct nfp_net *nn, unsigned int total_rx, if (nn->dp.xdp_prog) tx.n_rings += total_rx; - return nfp_net_ring_reconfig(nn, &nn->dp.xdp_prog, + dp = nfp_net_clone_dp(nn); + if (!dp) + return -ENOMEM; + + return nfp_net_ring_reconfig(nn, dp, &nn->dp.xdp_prog, reconfig_rx, reconfig_tx); } -- cgit From 512e94dc3229a824190e310c899a3f6d08216477 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 10 Mar 2017 10:38:30 -0800 Subject: nfp: use dp to carry number of stack tx rings and vectors Instead of passing variables around use dp to store number of tx rings for the stack and number of IRQ vectors. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_common.c | 62 +++++++++++----------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 7afefb44b642..52f0e9dfd15a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1786,8 +1786,7 @@ err_alloc: static struct nfp_net_tx_ring * nfp_net_tx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_dp *dp, - struct nfp_net_ring_set *s, - unsigned int num_stack_tx_rings) + struct nfp_net_ring_set *s) { struct nfp_net_tx_ring *rings; unsigned int r; @@ -1799,8 +1798,8 @@ nfp_net_tx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_dp *dp, for (r = 0; r < s->n_rings; r++) { int bias = 0; - if (r >= num_stack_tx_rings) - bias = num_stack_tx_rings; + if (r >= dp->num_stack_tx_rings) + bias = dp->num_stack_tx_rings; nfp_net_tx_ring_init(&rings[r], &nn->r_vecs[r - bias], r); @@ -2304,8 +2303,7 @@ static int nfp_net_netdev_open(struct net_device *netdev) goto err_cleanup_vec; } - nn->dp.tx_rings = nfp_net_tx_ring_set_prepare(nn, &nn->dp, &tx, - nn->dp.num_stack_tx_rings); + nn->dp.tx_rings = nfp_net_tx_ring_set_prepare(nn, &nn->dp, &tx); if (!nn->dp.tx_rings) { err = -ENOMEM; goto err_free_rx_rings; @@ -2466,10 +2464,16 @@ static void nfp_net_rss_init_itbl(struct nfp_net *nn) ethtool_rxfh_indir_default(i, nn->dp.num_rx_rings); } +static void nfp_net_dp_swap(struct nfp_net *nn, struct nfp_net_dp *dp) +{ + struct nfp_net_dp new_dp = *dp; + + *dp = nn->dp; + nn->dp = new_dp; +} + static int nfp_net_ring_swap_enable(struct nfp_net *nn, struct nfp_net_dp *dp, - unsigned int *num_vecs, - unsigned int *stack_tx_rings, struct bpf_prog **xdp_prog, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) @@ -2482,8 +2486,8 @@ nfp_net_ring_swap_enable(struct nfp_net *nn, struct nfp_net_dp *dp, if (tx) nfp_net_tx_ring_set_swap(nn, tx); - swap(*num_vecs, nn->dp.num_r_vecs); - swap(*stack_tx_rings, nn->dp.num_stack_tx_rings); + swap(dp->num_r_vecs, nn->dp.num_r_vecs); + swap(dp->num_stack_tx_rings, nn->dp.num_stack_tx_rings); *xdp_prog = xchg(&nn->dp.xdp_prog, *xdp_prog); for (r = 0; r < nn->max_r_vecs; r++) @@ -2550,17 +2554,16 @@ static void nfp_net_ring_reconfig_down(struct nfp_net *nn, struct nfp_net_dp *dp, struct bpf_prog **xdp_prog, struct nfp_net_ring_set *rx, - struct nfp_net_ring_set *tx, - unsigned int stack_tx_rings, unsigned int num_vecs) + struct nfp_net_ring_set *tx) { + nfp_net_dp_swap(nn, dp); + nn->dp.netdev->mtu = rx ? rx->mtu : nn->dp.netdev->mtu; nn->dp.fl_bufsz = nfp_net_calc_fl_bufsz(&nn->dp, nn->dp.netdev->mtu); nn->dp.rxd_cnt = rx ? rx->dcnt : nn->dp.rxd_cnt; nn->dp.txd_cnt = tx ? tx->dcnt : nn->dp.txd_cnt; nn->dp.num_rx_rings = rx ? rx->n_rings : nn->dp.num_rx_rings; nn->dp.num_tx_rings = tx ? tx->n_rings : nn->dp.num_tx_rings; - nn->dp.num_stack_tx_rings = stack_tx_rings; - nn->dp.num_r_vecs = num_vecs; *xdp_prog = xchg(&nn->dp.xdp_prog, *xdp_prog); if (!netif_is_rxfh_configured(nn->dp.netdev)) @@ -2572,31 +2575,31 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, struct bpf_prog **xdp_prog, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) { - unsigned int stack_tx_rings, num_vecs, r; - int err; + int r, err; - stack_tx_rings = tx ? tx->n_rings : dp->num_tx_rings; + dp->num_stack_tx_rings = tx ? tx->n_rings : dp->num_tx_rings; if (*xdp_prog) - stack_tx_rings -= rx ? rx->n_rings : dp->num_rx_rings; + dp->num_stack_tx_rings -= rx ? rx->n_rings : dp->num_rx_rings; - num_vecs = max(rx ? rx->n_rings : dp->num_rx_rings, stack_tx_rings); + dp->num_r_vecs = max(rx ? rx->n_rings : dp->num_rx_rings, + dp->num_stack_tx_rings); err = nfp_net_check_config(nn, dp, *xdp_prog, rx, tx); if (err) goto exit_free_dp; if (!netif_running(dp->netdev)) { - nfp_net_ring_reconfig_down(nn, dp, xdp_prog, rx, tx, - stack_tx_rings, num_vecs); + nfp_net_ring_reconfig_down(nn, dp, xdp_prog, rx, tx); + err = 0; goto exit_free_dp; } /* Prepare new rings */ - for (r = nn->dp.num_r_vecs; r < num_vecs; r++) { + for (r = nn->dp.num_r_vecs; r < dp->num_r_vecs; r++) { err = nfp_net_prepare_vector(nn, &nn->r_vecs[r], r); if (err) { - num_vecs = r; + dp->num_r_vecs = r; goto err_cleanup_vecs; } } @@ -2607,7 +2610,7 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, } } if (tx) { - if (!nfp_net_tx_ring_set_prepare(nn, dp, tx, stack_tx_rings)) { + if (!nfp_net_tx_ring_set_prepare(nn, dp, tx)) { err = -ENOMEM; goto err_free_rx; } @@ -2617,22 +2620,19 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, nfp_net_close_stack(nn); nfp_net_clear_config_and_disable(nn); - err = nfp_net_ring_swap_enable(nn, dp, &num_vecs, &stack_tx_rings, - xdp_prog, rx, tx); + err = nfp_net_ring_swap_enable(nn, dp, xdp_prog, rx, tx); if (err) { int err2; nfp_net_clear_config_and_disable(nn); /* Try with old configuration and old rings */ - err2 = nfp_net_ring_swap_enable(nn, dp, &num_vecs, - &stack_tx_rings, - xdp_prog, rx, tx); + err2 = nfp_net_ring_swap_enable(nn, dp, xdp_prog, rx, tx); if (err2) nn_err(nn, "Can't restore ring config - FW communication failed (%d,%d)\n", err, err2); } - for (r = num_vecs - 1; r >= nn->dp.num_r_vecs; r--) + for (r = dp->num_r_vecs - 1; r >= nn->dp.num_r_vecs; r--) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); if (rx) @@ -2650,7 +2650,7 @@ err_free_rx: if (rx) nfp_net_rx_ring_set_free(dp, rx, *xdp_prog); err_cleanup_vecs: - for (r = num_vecs - 1; r >= nn->dp.num_r_vecs; r--) + for (r = dp->num_r_vecs - 1; r >= nn->dp.num_r_vecs; r--) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); kfree(dp); return err; -- cgit From 2195c2637f90e97ef8cd8f8e0fe11c1829e7f1f6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 10 Mar 2017 10:38:31 -0800 Subject: nfp: use dp to carry fl_bufsz at reconfig time Use fl_bufsz member of data path struct to carry desired size of free list entries. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 3 -- .../net/ethernet/netronome/nfp/nfp_net_common.c | 56 +++++++++++----------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 74f6d485351f..ab5865b955dd 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -316,8 +316,6 @@ struct nfp_net_rx_buf { * @rxds: Virtual address of FL/RX ring in host memory * @dma: DMA address of the FL/RX ring * @size: Size, in bytes, of the FL/RX ring (needed to free) - * @bufsz: Buffer allocation size for convenience of management routines - * (NOTE: this is in second cache line, do not use on fast path!) */ struct nfp_net_rx_ring { struct nfp_net_r_vector *r_vec; @@ -339,7 +337,6 @@ struct nfp_net_rx_ring { dma_addr_t dma; unsigned int size; - unsigned int bufsz; } ____cacheline_aligned; /** diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 52f0e9dfd15a..92d4c2991a85 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -86,19 +86,19 @@ void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver, } static dma_addr_t -nfp_net_dma_map_rx(struct nfp_net_dp *dp, void *frag, unsigned int bufsz, - int direction) +nfp_net_dma_map_rx(struct nfp_net_dp *dp, void *frag, int direction) { return dma_map_single(dp->dev, frag + NFP_NET_RX_BUF_HEADROOM, - bufsz - NFP_NET_RX_BUF_NON_DATA, direction); + dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, + direction); } static void nfp_net_dma_unmap_rx(struct nfp_net_dp *dp, dma_addr_t dma_addr, - unsigned int bufsz, int direction) + int direction) { dma_unmap_single(dp->dev, dma_addr, - bufsz - NFP_NET_RX_BUF_NON_DATA, direction); + dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, direction); } /* Firmware reconfig @@ -992,7 +992,7 @@ static void nfp_net_xdp_complete(struct nfp_net_tx_ring *tx_ring) continue; nfp_net_dma_unmap_rx(dp, tx_ring->txbufs[idx].dma_addr, - dp->fl_bufsz, DMA_BIDIRECTIONAL); + DMA_BIDIRECTIONAL); __free_page(virt_to_page(tx_ring->txbufs[idx].frag)); done_pkts++; @@ -1038,7 +1038,7 @@ nfp_net_tx_ring_reset(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) if (tx_ring == r_vec->xdp_ring) { nfp_net_dma_unmap_rx(dp, tx_buf->dma_addr, - dp->fl_bufsz, DMA_BIDIRECTIONAL); + DMA_BIDIRECTIONAL); __free_page(virt_to_page(tx_ring->txbufs[idx].frag)); } else { struct sk_buff *skb = tx_ring->txbufs[idx].skb; @@ -1130,7 +1130,6 @@ nfp_net_free_frag(void *frag, bool xdp) * @dp: NFP Net data path struct * @rx_ring: RX ring structure of the skb * @dma_addr: Pointer to storage for DMA address (output param) - * @fl_bufsz: size of freelist buffers * @xdp: Whether XDP is enabled * * This function will allcate a new page frag, map it for DMA. @@ -1140,13 +1139,13 @@ nfp_net_free_frag(void *frag, bool xdp) static void * nfp_net_rx_alloc_one(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr, - unsigned int fl_bufsz, bool xdp) + bool xdp) { int direction; void *frag; if (!xdp) - frag = netdev_alloc_frag(fl_bufsz); + frag = netdev_alloc_frag(dp->fl_bufsz); else frag = page_address(alloc_page(GFP_KERNEL | __GFP_COLD)); if (!frag) { @@ -1156,7 +1155,7 @@ nfp_net_rx_alloc_one(struct nfp_net_dp *dp, direction = xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; - *dma_addr = nfp_net_dma_map_rx(dp, frag, fl_bufsz, direction); + *dma_addr = nfp_net_dma_map_rx(dp, frag, direction); if (dma_mapping_error(dp->dev, *dma_addr)) { nfp_net_free_frag(frag, xdp); nn_dp_warn(dp, "Failed to map DMA RX buffer\n"); @@ -1181,7 +1180,7 @@ nfp_net_napi_alloc_one(struct nfp_net_dp *dp, int direction, return NULL; } - *dma_addr = nfp_net_dma_map_rx(dp, frag, dp->fl_bufsz, direction); + *dma_addr = nfp_net_dma_map_rx(dp, frag, direction); if (dma_mapping_error(dp->dev, *dma_addr)) { nfp_net_free_frag(frag, dp->xdp_prog); nn_dp_warn(dp, "Failed to map DMA RX buffer\n"); @@ -1276,7 +1275,7 @@ nfp_net_rx_ring_bufs_free(struct nfp_net_dp *dp, continue; nfp_net_dma_unmap_rx(dp, rx_ring->rxbufs[i].dma_addr, - rx_ring->bufsz, direction); + direction); nfp_net_free_frag(rx_ring->rxbufs[i].frag, xdp); rx_ring->rxbufs[i].dma_addr = 0; rx_ring->rxbufs[i].frag = NULL; @@ -1301,7 +1300,7 @@ nfp_net_rx_ring_bufs_alloc(struct nfp_net_dp *dp, for (i = 0; i < rx_ring->cnt - 1; i++) { rxbufs[i].frag = nfp_net_rx_alloc_one(dp, rx_ring, &rxbufs[i].dma_addr, - rx_ring->bufsz, xdp); + xdp); if (!rxbufs[i].frag) { nfp_net_rx_ring_bufs_free(dp, rx_ring, xdp); return -ENOMEM; @@ -1652,8 +1651,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) continue; } - nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr, dp->fl_bufsz, - rx_dma_map_dir); + nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr, rx_dma_map_dir); nfp_net_rx_give_one(rx_ring, new_frag, new_dma_addr); @@ -1865,23 +1863,19 @@ static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring) /** * nfp_net_rx_ring_alloc() - Allocate resource for a RX ring + * @dp: NFP Net data path struct * @rx_ring: RX ring to allocate - * @fl_bufsz: Size of buffers to allocate * @cnt: Ring buffer count * * Return: 0 on success, negative errno otherwise. */ static int -nfp_net_rx_ring_alloc(struct nfp_net_rx_ring *rx_ring, unsigned int fl_bufsz, +nfp_net_rx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, u32 cnt) { - struct nfp_net_r_vector *r_vec = rx_ring->r_vec; - struct nfp_net_dp *dp = &r_vec->nfp_net->dp; int sz; rx_ring->cnt = cnt; - rx_ring->bufsz = fl_bufsz; - rx_ring->size = sizeof(*rx_ring->rxds) * rx_ring->cnt; rx_ring->rxds = dma_zalloc_coherent(dp->dev, rx_ring->size, &rx_ring->dma, GFP_KERNEL); @@ -1904,7 +1898,6 @@ static struct nfp_net_rx_ring * nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_dp *dp, struct nfp_net_ring_set *s, bool xdp) { - unsigned int fl_bufsz = nfp_net_calc_fl_bufsz(dp, s->mtu); struct nfp_net_rx_ring *rings; unsigned int r; @@ -1915,7 +1908,7 @@ nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_dp *dp, for (r = 0; r < s->n_rings; r++) { nfp_net_rx_ring_init(&rings[r], &nn->r_vecs[r], r); - if (nfp_net_rx_ring_alloc(&rings[r], fl_bufsz, s->dcnt)) + if (nfp_net_rx_ring_alloc(dp, &rings[r], s->dcnt)) goto err_free_prev; if (nfp_net_rx_ring_bufs_alloc(dp, &rings[r], xdp)) @@ -1935,17 +1928,20 @@ err_free_ring: } static void -nfp_net_rx_ring_set_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) +nfp_net_rx_ring_set_swap(struct nfp_net *nn, struct nfp_net_dp *dp, + struct nfp_net_ring_set *s) { struct nfp_net_ring_set new = *s; + struct nfp_net_dp new_dp = *dp; + dp->fl_bufsz = nn->dp.fl_bufsz; s->mtu = nn->dp.netdev->mtu; s->dcnt = nn->dp.rxd_cnt; s->rings = nn->dp.rx_rings; s->n_rings = nn->dp.num_rx_rings; nn->dp.netdev->mtu = new.mtu; - nn->dp.fl_bufsz = nfp_net_calc_fl_bufsz(&nn->dp, new.mtu); + nn->dp.fl_bufsz = new_dp.fl_bufsz; nn->dp.rxd_cnt = new.dcnt; nn->dp.rx_rings = new.rings; nn->dp.num_rx_rings = new.n_rings; @@ -2482,7 +2478,7 @@ nfp_net_ring_swap_enable(struct nfp_net *nn, struct nfp_net_dp *dp, int err; if (rx) - nfp_net_rx_ring_set_swap(nn, rx); + nfp_net_rx_ring_set_swap(nn, dp, rx); if (tx) nfp_net_tx_ring_set_swap(nn, tx); @@ -2538,7 +2534,7 @@ nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp, /* XDP-enabled tests */ if (!xdp_prog) return 0; - if (rx && nfp_net_calc_fl_bufsz(dp, rx->mtu) > PAGE_SIZE) { + if (dp->fl_bufsz > PAGE_SIZE) { nn_warn(nn, "MTU too large w/ XDP enabled\n"); return -EINVAL; } @@ -2559,7 +2555,6 @@ nfp_net_ring_reconfig_down(struct nfp_net *nn, struct nfp_net_dp *dp, nfp_net_dp_swap(nn, dp); nn->dp.netdev->mtu = rx ? rx->mtu : nn->dp.netdev->mtu; - nn->dp.fl_bufsz = nfp_net_calc_fl_bufsz(&nn->dp, nn->dp.netdev->mtu); nn->dp.rxd_cnt = rx ? rx->dcnt : nn->dp.rxd_cnt; nn->dp.txd_cnt = tx ? tx->dcnt : nn->dp.txd_cnt; nn->dp.num_rx_rings = rx ? rx->n_rings : nn->dp.num_rx_rings; @@ -2577,6 +2572,9 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, { int r, err; + dp->fl_bufsz = nfp_net_calc_fl_bufsz(dp, + rx ? rx->mtu : nn->dp.netdev->mtu); + dp->num_stack_tx_rings = tx ? tx->n_rings : dp->num_tx_rings; if (*xdp_prog) dp->num_stack_tx_rings -= rx ? rx->n_rings : dp->num_rx_rings; -- cgit From 76e1e1a89351832ea5d9f7b57677e7420ba6bc92 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 10 Mar 2017 10:38:32 -0800 Subject: nfp: use dp to carry mtu at reconfig time Move the mtu member from ring set to data path struct. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 4 +++- .../net/ethernet/netronome/nfp/nfp_net_common.c | 23 +++++++++++----------- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 2 -- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index ab5865b955dd..84774c281b61 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -452,6 +452,7 @@ struct nfp_stat_pair { * @num_tx_rings: Currently configured number of TX rings * @num_stack_tx_rings: Number of TX rings used by the stack (not XDP) * @num_rx_rings: Currently configured number of RX rings + * @mtu: Device MTU */ struct nfp_net_dp { struct device *dev; @@ -484,6 +485,8 @@ struct nfp_net_dp { unsigned int num_tx_rings; unsigned int num_stack_tx_rings; unsigned int num_rx_rings; + + unsigned int mtu; }; /** @@ -610,7 +613,6 @@ struct nfp_net { struct nfp_net_ring_set { unsigned int n_rings; - unsigned int mtu; unsigned int dcnt; void *rings; }; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 92d4c2991a85..862e86cb5688 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1099,7 +1099,7 @@ static void nfp_net_tx_timeout(struct net_device *netdev) /* Receive processing */ static unsigned int -nfp_net_calc_fl_bufsz(struct nfp_net_dp *dp, unsigned int mtu) +nfp_net_calc_fl_bufsz(struct nfp_net_dp *dp) { unsigned int fl_bufsz; @@ -1108,7 +1108,7 @@ nfp_net_calc_fl_bufsz(struct nfp_net_dp *dp, unsigned int mtu) fl_bufsz += NFP_NET_MAX_PREPEND; else fl_bufsz += dp->rx_offset; - fl_bufsz += ETH_HLEN + VLAN_HLEN * 2 + mtu; + fl_bufsz += ETH_HLEN + VLAN_HLEN * 2 + dp->mtu; fl_bufsz = SKB_DATA_ALIGN(fl_bufsz); fl_bufsz += SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); @@ -1935,12 +1935,13 @@ nfp_net_rx_ring_set_swap(struct nfp_net *nn, struct nfp_net_dp *dp, struct nfp_net_dp new_dp = *dp; dp->fl_bufsz = nn->dp.fl_bufsz; - s->mtu = nn->dp.netdev->mtu; + dp->mtu = nn->dp.netdev->mtu; s->dcnt = nn->dp.rxd_cnt; s->rings = nn->dp.rx_rings; s->n_rings = nn->dp.num_rx_rings; - nn->dp.netdev->mtu = new.mtu; + nn->dp.mtu = new_dp.mtu; + nn->dp.netdev->mtu = new_dp.mtu; nn->dp.fl_bufsz = new_dp.fl_bufsz; nn->dp.rxd_cnt = new.dcnt; nn->dp.rx_rings = new.rings; @@ -2255,7 +2256,6 @@ static int nfp_net_netdev_open(struct net_device *netdev) struct nfp_net *nn = netdev_priv(netdev); struct nfp_net_ring_set rx = { .n_rings = nn->dp.num_rx_rings, - .mtu = nn->dp.netdev->mtu, .dcnt = nn->dp.rxd_cnt, }; struct nfp_net_ring_set tx = { @@ -2466,6 +2466,8 @@ static void nfp_net_dp_swap(struct nfp_net *nn, struct nfp_net_dp *dp) *dp = nn->dp; nn->dp = new_dp; + + nn->dp.netdev->mtu = new_dp.mtu; } static int @@ -2554,7 +2556,6 @@ nfp_net_ring_reconfig_down(struct nfp_net *nn, struct nfp_net_dp *dp, { nfp_net_dp_swap(nn, dp); - nn->dp.netdev->mtu = rx ? rx->mtu : nn->dp.netdev->mtu; nn->dp.rxd_cnt = rx ? rx->dcnt : nn->dp.rxd_cnt; nn->dp.txd_cnt = tx ? tx->dcnt : nn->dp.txd_cnt; nn->dp.num_rx_rings = rx ? rx->n_rings : nn->dp.num_rx_rings; @@ -2572,8 +2573,7 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, { int r, err; - dp->fl_bufsz = nfp_net_calc_fl_bufsz(dp, - rx ? rx->mtu : nn->dp.netdev->mtu); + dp->fl_bufsz = nfp_net_calc_fl_bufsz(dp); dp->num_stack_tx_rings = tx ? tx->n_rings : dp->num_tx_rings; if (*xdp_prog) @@ -2659,7 +2659,6 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) struct nfp_net *nn = netdev_priv(netdev); struct nfp_net_ring_set rx = { .n_rings = nn->dp.num_rx_rings, - .mtu = new_mtu, .dcnt = nn->dp.rxd_cnt, }; struct nfp_net_dp *dp; @@ -2668,6 +2667,8 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) if (!dp) return -ENOMEM; + dp->mtu = new_mtu; + return nfp_net_ring_reconfig(nn, dp, &nn->dp.xdp_prog, &rx, NULL); } @@ -2988,7 +2989,6 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) { struct nfp_net_ring_set rx = { .n_rings = nn->dp.num_rx_rings, - .mtu = nn->dp.netdev->mtu, .dcnt = nn->dp.rxd_cnt, }; struct nfp_net_ring_set tx = { @@ -3263,7 +3263,8 @@ int nfp_net_netdev_init(struct net_device *netdev) netdev->mtu = nn->max_mtu; else netdev->mtu = NFP_NET_DEFAULT_MTU; - nn->dp.fl_bufsz = nfp_net_calc_fl_bufsz(&nn->dp, netdev->mtu); + nn->dp.mtu = netdev->mtu; + nn->dp.fl_bufsz = nfp_net_calc_fl_bufsz(&nn->dp); /* Advertise/enable offloads based on capabilities * diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 326ccd74a4bf..eccb01f3659f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -189,7 +189,6 @@ static int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt) struct nfp_net_ring_set *reconfig_rx = NULL, *reconfig_tx = NULL; struct nfp_net_ring_set rx = { .n_rings = nn->dp.num_rx_rings, - .mtu = nn->dp.netdev->mtu, .dcnt = rxd_cnt, }; struct nfp_net_ring_set tx = { @@ -770,7 +769,6 @@ static int nfp_net_set_num_rings(struct nfp_net *nn, unsigned int total_rx, struct nfp_net_ring_set *reconfig_rx = NULL, *reconfig_tx = NULL; struct nfp_net_ring_set rx = { .n_rings = total_rx, - .mtu = nn->dp.netdev->mtu, .dcnt = nn->dp.rxd_cnt, }; struct nfp_net_ring_set tx = { -- cgit From 9dc6b116e21b9ff258334c09d78ce26caa5ade45 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 10 Mar 2017 10:38:33 -0800 Subject: nfp: use dp to carry xdp_prog at reconfig time Use xdp_prog member of data path struct to carry the xdp_prog to alloc/free free functions. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 1 - .../net/ethernet/netronome/nfp/nfp_net_common.c | 82 +++++++++------------- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 6 +- 3 files changed, 37 insertions(+), 52 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 84774c281b61..19dacc3f1269 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -815,7 +815,6 @@ nfp_net_irqs_assign(struct nfp_net *nn, struct msix_entry *irq_entries, struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn); int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new, - struct bpf_prog **xdp_prog, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx); #ifdef CONFIG_NFP_DEBUG diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 862e86cb5688..6ab824a48d1d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1130,21 +1130,19 @@ nfp_net_free_frag(void *frag, bool xdp) * @dp: NFP Net data path struct * @rx_ring: RX ring structure of the skb * @dma_addr: Pointer to storage for DMA address (output param) - * @xdp: Whether XDP is enabled * * This function will allcate a new page frag, map it for DMA. * * Return: allocated page frag or NULL on failure. */ static void * -nfp_net_rx_alloc_one(struct nfp_net_dp *dp, - struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr, - bool xdp) +nfp_net_rx_alloc_one(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, + dma_addr_t *dma_addr) { int direction; void *frag; - if (!xdp) + if (!dp->xdp_prog) frag = netdev_alloc_frag(dp->fl_bufsz); else frag = page_address(alloc_page(GFP_KERNEL | __GFP_COLD)); @@ -1153,11 +1151,11 @@ nfp_net_rx_alloc_one(struct nfp_net_dp *dp, return NULL; } - direction = xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; + direction = dp->xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; *dma_addr = nfp_net_dma_map_rx(dp, frag, direction); if (dma_mapping_error(dp->dev, *dma_addr)) { - nfp_net_free_frag(frag, xdp); + nfp_net_free_frag(frag, dp->xdp_prog); nn_dp_warn(dp, "Failed to map DMA RX buffer\n"); return NULL; } @@ -1253,7 +1251,6 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring) * nfp_net_rx_ring_bufs_free() - Free any buffers currently on the RX ring * @dp: NFP Net data path struct * @rx_ring: RX ring to remove buffers from - * @xdp: Whether XDP is enabled * * Assumes that the device is stopped and buffers are in [0, ring->cnt - 1) * entries. After device is disabled nfp_net_rx_ring_reset() must be called @@ -1261,9 +1258,9 @@ static void nfp_net_rx_ring_reset(struct nfp_net_rx_ring *rx_ring) */ static void nfp_net_rx_ring_bufs_free(struct nfp_net_dp *dp, - struct nfp_net_rx_ring *rx_ring, bool xdp) + struct nfp_net_rx_ring *rx_ring) { - int direction = xdp ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; + int direction = dp->xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; unsigned int i; for (i = 0; i < rx_ring->cnt - 1; i++) { @@ -1276,7 +1273,7 @@ nfp_net_rx_ring_bufs_free(struct nfp_net_dp *dp, nfp_net_dma_unmap_rx(dp, rx_ring->rxbufs[i].dma_addr, direction); - nfp_net_free_frag(rx_ring->rxbufs[i].frag, xdp); + nfp_net_free_frag(rx_ring->rxbufs[i].frag, dp->xdp_prog); rx_ring->rxbufs[i].dma_addr = 0; rx_ring->rxbufs[i].frag = NULL; } @@ -1286,11 +1283,10 @@ nfp_net_rx_ring_bufs_free(struct nfp_net_dp *dp, * nfp_net_rx_ring_bufs_alloc() - Fill RX ring with buffers (don't give to FW) * @dp: NFP Net data path struct * @rx_ring: RX ring to remove buffers from - * @xdp: Whether XDP is enabled */ static int nfp_net_rx_ring_bufs_alloc(struct nfp_net_dp *dp, - struct nfp_net_rx_ring *rx_ring, bool xdp) + struct nfp_net_rx_ring *rx_ring) { struct nfp_net_rx_buf *rxbufs; unsigned int i; @@ -1299,10 +1295,9 @@ nfp_net_rx_ring_bufs_alloc(struct nfp_net_dp *dp, for (i = 0; i < rx_ring->cnt - 1; i++) { rxbufs[i].frag = - nfp_net_rx_alloc_one(dp, rx_ring, &rxbufs[i].dma_addr, - xdp); + nfp_net_rx_alloc_one(dp, rx_ring, &rxbufs[i].dma_addr); if (!rxbufs[i].frag) { - nfp_net_rx_ring_bufs_free(dp, rx_ring, xdp); + nfp_net_rx_ring_bufs_free(dp, rx_ring); return -ENOMEM; } } @@ -1896,7 +1891,7 @@ err_alloc: static struct nfp_net_rx_ring * nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_dp *dp, - struct nfp_net_ring_set *s, bool xdp) + struct nfp_net_ring_set *s) { struct nfp_net_rx_ring *rings; unsigned int r; @@ -1911,7 +1906,7 @@ nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_dp *dp, if (nfp_net_rx_ring_alloc(dp, &rings[r], s->dcnt)) goto err_free_prev; - if (nfp_net_rx_ring_bufs_alloc(dp, &rings[r], xdp)) + if (nfp_net_rx_ring_bufs_alloc(dp, &rings[r])) goto err_free_ring; } @@ -1919,7 +1914,7 @@ nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_dp *dp, err_free_prev: while (r--) { - nfp_net_rx_ring_bufs_free(dp, &rings[r], xdp); + nfp_net_rx_ring_bufs_free(dp, &rings[r]); err_free_ring: nfp_net_rx_ring_free(&rings[r]); } @@ -1949,14 +1944,13 @@ nfp_net_rx_ring_set_swap(struct nfp_net *nn, struct nfp_net_dp *dp, } static void -nfp_net_rx_ring_set_free(struct nfp_net_dp *dp, struct nfp_net_ring_set *s, - bool xdp) +nfp_net_rx_ring_set_free(struct nfp_net_dp *dp, struct nfp_net_ring_set *s) { struct nfp_net_rx_ring *rings = s->rings; unsigned int r; for (r = 0; r < s->n_rings; r++) { - nfp_net_rx_ring_bufs_free(dp, &rings[r], xdp); + nfp_net_rx_ring_bufs_free(dp, &rings[r]); nfp_net_rx_ring_free(&rings[r]); } @@ -2292,8 +2286,7 @@ static int nfp_net_netdev_open(struct net_device *netdev) goto err_cleanup_vec_p; } - nn->dp.rx_rings = nfp_net_rx_ring_set_prepare(nn, &nn->dp, &rx, - nn->dp.xdp_prog); + nn->dp.rx_rings = nfp_net_rx_ring_set_prepare(nn, &nn->dp, &rx); if (!nn->dp.rx_rings) { err = -ENOMEM; goto err_cleanup_vec; @@ -2340,7 +2333,7 @@ static int nfp_net_netdev_open(struct net_device *netdev) err_free_rings: nfp_net_tx_ring_set_free(&tx); err_free_rx_rings: - nfp_net_rx_ring_set_free(&nn->dp, &rx, nn->dp.xdp_prog); + nfp_net_rx_ring_set_free(&nn->dp, &rx); err_cleanup_vec: r = nn->dp.num_r_vecs; err_cleanup_vec_p: @@ -2381,8 +2374,7 @@ static void nfp_net_close_free_all(struct nfp_net *nn) unsigned int r; for (r = 0; r < nn->dp.num_rx_rings; r++) { - nfp_net_rx_ring_bufs_free(&nn->dp, &nn->dp.rx_rings[r], - nn->dp.xdp_prog); + nfp_net_rx_ring_bufs_free(&nn->dp, &nn->dp.rx_rings[r]); nfp_net_rx_ring_free(&nn->dp.rx_rings[r]); } for (r = 0; r < nn->dp.num_tx_rings; r++) @@ -2472,7 +2464,6 @@ static void nfp_net_dp_swap(struct nfp_net *nn, struct nfp_net_dp *dp) static int nfp_net_ring_swap_enable(struct nfp_net *nn, struct nfp_net_dp *dp, - struct bpf_prog **xdp_prog, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) { @@ -2486,7 +2477,7 @@ nfp_net_ring_swap_enable(struct nfp_net *nn, struct nfp_net_dp *dp, swap(dp->num_r_vecs, nn->dp.num_r_vecs); swap(dp->num_stack_tx_rings, nn->dp.num_stack_tx_rings); - *xdp_prog = xchg(&nn->dp.xdp_prog, *xdp_prog); + dp->xdp_prog = xchg(&nn->dp.xdp_prog, dp->xdp_prog); for (r = 0; r < nn->max_r_vecs; r++) nfp_net_vector_assign_rings(&nn->dp, &nn->r_vecs[r], r); @@ -2530,11 +2521,10 @@ struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn) static int nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp, - struct bpf_prog *xdp_prog, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) { /* XDP-enabled tests */ - if (!xdp_prog) + if (!dp->xdp_prog) return 0; if (dp->fl_bufsz > PAGE_SIZE) { nn_warn(nn, "MTU too large w/ XDP enabled\n"); @@ -2550,7 +2540,6 @@ nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp, static void nfp_net_ring_reconfig_down(struct nfp_net *nn, struct nfp_net_dp *dp, - struct bpf_prog **xdp_prog, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) { @@ -2560,7 +2549,6 @@ nfp_net_ring_reconfig_down(struct nfp_net *nn, struct nfp_net_dp *dp, nn->dp.txd_cnt = tx ? tx->dcnt : nn->dp.txd_cnt; nn->dp.num_rx_rings = rx ? rx->n_rings : nn->dp.num_rx_rings; nn->dp.num_tx_rings = tx ? tx->n_rings : nn->dp.num_tx_rings; - *xdp_prog = xchg(&nn->dp.xdp_prog, *xdp_prog); if (!netif_is_rxfh_configured(nn->dp.netdev)) nfp_net_rss_init_itbl(nn); @@ -2568,7 +2556,6 @@ nfp_net_ring_reconfig_down(struct nfp_net *nn, struct nfp_net_dp *dp, int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, - struct bpf_prog **xdp_prog, struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) { int r, err; @@ -2576,18 +2563,18 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, dp->fl_bufsz = nfp_net_calc_fl_bufsz(dp); dp->num_stack_tx_rings = tx ? tx->n_rings : dp->num_tx_rings; - if (*xdp_prog) + if (dp->xdp_prog) dp->num_stack_tx_rings -= rx ? rx->n_rings : dp->num_rx_rings; dp->num_r_vecs = max(rx ? rx->n_rings : dp->num_rx_rings, dp->num_stack_tx_rings); - err = nfp_net_check_config(nn, dp, *xdp_prog, rx, tx); + err = nfp_net_check_config(nn, dp, rx, tx); if (err) goto exit_free_dp; if (!netif_running(dp->netdev)) { - nfp_net_ring_reconfig_down(nn, dp, xdp_prog, rx, tx); + nfp_net_ring_reconfig_down(nn, dp, rx, tx); err = 0; goto exit_free_dp; @@ -2602,7 +2589,7 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, } } if (rx) { - if (!nfp_net_rx_ring_set_prepare(nn, dp, rx, *xdp_prog)) { + if (!nfp_net_rx_ring_set_prepare(nn, dp, rx)) { err = -ENOMEM; goto err_cleanup_vecs; } @@ -2618,14 +2605,14 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, nfp_net_close_stack(nn); nfp_net_clear_config_and_disable(nn); - err = nfp_net_ring_swap_enable(nn, dp, xdp_prog, rx, tx); + err = nfp_net_ring_swap_enable(nn, dp, rx, tx); if (err) { int err2; nfp_net_clear_config_and_disable(nn); /* Try with old configuration and old rings */ - err2 = nfp_net_ring_swap_enable(nn, dp, xdp_prog, rx, tx); + err2 = nfp_net_ring_swap_enable(nn, dp, rx, tx); if (err2) nn_err(nn, "Can't restore ring config - FW communication failed (%d,%d)\n", err, err2); @@ -2634,7 +2621,7 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); if (rx) - nfp_net_rx_ring_set_free(dp, rx, *xdp_prog); + nfp_net_rx_ring_set_free(dp, rx); if (tx) nfp_net_tx_ring_set_free(tx); @@ -2646,7 +2633,7 @@ exit_free_dp: err_free_rx: if (rx) - nfp_net_rx_ring_set_free(dp, rx, *xdp_prog); + nfp_net_rx_ring_set_free(dp, rx); err_cleanup_vecs: for (r = dp->num_r_vecs - 1; r >= nn->dp.num_r_vecs; r--) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); @@ -2669,7 +2656,7 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) dp->mtu = new_mtu; - return nfp_net_ring_reconfig(nn, dp, &nn->dp.xdp_prog, &rx, NULL); + return nfp_net_ring_reconfig(nn, dp, &rx, NULL); } static void nfp_net_stat64(struct net_device *netdev, @@ -2987,6 +2974,7 @@ static int nfp_net_xdp_offload(struct nfp_net *nn, struct bpf_prog *prog) static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) { + struct bpf_prog *old_prog = nn->dp.xdp_prog; struct nfp_net_ring_set rx = { .n_rings = nn->dp.num_rx_rings, .dcnt = nn->dp.rxd_cnt, @@ -3015,16 +3003,16 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) if (!dp) return -ENOMEM; + dp->xdp_prog = prog; tx.n_rings += prog ? nn->dp.num_rx_rings : -nn->dp.num_rx_rings; /* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */ - err = nfp_net_ring_reconfig(nn, dp, &prog, &rx, &tx); + err = nfp_net_ring_reconfig(nn, dp, &rx, &tx); if (err) return err; - /* @prog got swapped and is now the old one */ - if (prog) - bpf_prog_put(prog); + if (old_prog) + bpf_prog_put(old_prog); nfp_net_xdp_offload(nn, nn->dp.xdp_prog); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index eccb01f3659f..63c1d9ab2335 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -206,8 +206,7 @@ static int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt) if (!dp) return -ENOMEM; - return nfp_net_ring_reconfig(nn, dp, &nn->dp.xdp_prog, - reconfig_rx, reconfig_tx); + return nfp_net_ring_reconfig(nn, dp, reconfig_rx, reconfig_tx); } static int nfp_net_set_ringparam(struct net_device *netdev, @@ -791,8 +790,7 @@ static int nfp_net_set_num_rings(struct nfp_net *nn, unsigned int total_rx, if (!dp) return -ENOMEM; - return nfp_net_ring_reconfig(nn, dp, &nn->dp.xdp_prog, - reconfig_rx, reconfig_tx); + return nfp_net_ring_reconfig(nn, dp, reconfig_rx, reconfig_tx); } static int nfp_net_set_channels(struct net_device *netdev, -- cgit From 892a7f700b361882105739632ccbd6ceb848c21d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 10 Mar 2017 10:38:34 -0800 Subject: nfp: switch to using data path structures for reconfiguration Instead of passing around sets of rings and their parameters just store all information in the data path structure. We will no longer user xchg() on XDP programs when we swap programs while the traffic is guaranteed not to be flowing. This allows us to simply assign the entire data path structures instead of copying field by field. The optimization to reallocate only the rings on the side (RX/TX) which has been changed is also removed since it seems like it's not worth the code complexity. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 10 +- .../net/ethernet/netronome/nfp/nfp_net_common.c | 260 ++++++--------------- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 46 +--- 3 files changed, 89 insertions(+), 227 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 19dacc3f1269..5a92f6e41dae 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -611,12 +611,6 @@ struct nfp_net { struct nfp_eth_table_port *eth_port; }; -struct nfp_net_ring_set { - unsigned int n_rings; - unsigned int dcnt; - void *rings; -}; - /* Functions to read/write from/to a BAR * Performs any endian conversion necessary. */ @@ -813,9 +807,7 @@ nfp_net_irqs_assign(struct nfp_net *nn, struct msix_entry *irq_entries, unsigned int n); struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn); -int -nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new, - struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx); +int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new); #ifdef CONFIG_NFP_DEBUG void nfp_net_debugfs_create(void); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 6ab824a48d1d..a9359da64f80 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1740,20 +1740,20 @@ static void nfp_net_tx_ring_free(struct nfp_net_tx_ring *tx_ring) /** * nfp_net_tx_ring_alloc() - Allocate resource for a TX ring + * @dp: NFP Net data path struct * @tx_ring: TX Ring structure to allocate - * @cnt: Ring buffer count * @is_xdp: True if ring will be used for XDP * * Return: 0 on success, negative errno otherwise. */ static int -nfp_net_tx_ring_alloc(struct nfp_net_tx_ring *tx_ring, u32 cnt, bool is_xdp) +nfp_net_tx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring, + bool is_xdp) { struct nfp_net_r_vector *r_vec = tx_ring->r_vec; - struct nfp_net_dp *dp = &r_vec->nfp_net->dp; int sz; - tx_ring->cnt = cnt; + tx_ring->cnt = dp->txd_cnt; tx_ring->size = sizeof(*tx_ring->txds) * tx_ring->cnt; tx_ring->txds = dma_zalloc_coherent(dp->dev, tx_ring->size, @@ -1777,61 +1777,45 @@ err_alloc: return -ENOMEM; } -static struct nfp_net_tx_ring * -nfp_net_tx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_dp *dp, - struct nfp_net_ring_set *s) +static int nfp_net_tx_rings_prepare(struct nfp_net *nn, struct nfp_net_dp *dp) { - struct nfp_net_tx_ring *rings; unsigned int r; - rings = kcalloc(s->n_rings, sizeof(*rings), GFP_KERNEL); - if (!rings) - return NULL; + dp->tx_rings = kcalloc(dp->num_tx_rings, sizeof(*dp->tx_rings), + GFP_KERNEL); + if (!dp->tx_rings) + return -ENOMEM; - for (r = 0; r < s->n_rings; r++) { + for (r = 0; r < dp->num_tx_rings; r++) { int bias = 0; if (r >= dp->num_stack_tx_rings) bias = dp->num_stack_tx_rings; - nfp_net_tx_ring_init(&rings[r], &nn->r_vecs[r - bias], r); + nfp_net_tx_ring_init(&dp->tx_rings[r], &nn->r_vecs[r - bias], + r); - if (nfp_net_tx_ring_alloc(&rings[r], s->dcnt, bias)) + if (nfp_net_tx_ring_alloc(dp, &dp->tx_rings[r], bias)) goto err_free_prev; } - return s->rings = rings; + return 0; err_free_prev: while (r--) - nfp_net_tx_ring_free(&rings[r]); - kfree(rings); - return NULL; -} - -static void -nfp_net_tx_ring_set_swap(struct nfp_net *nn, struct nfp_net_ring_set *s) -{ - struct nfp_net_ring_set new = *s; - - s->dcnt = nn->dp.txd_cnt; - s->rings = nn->dp.tx_rings; - s->n_rings = nn->dp.num_tx_rings; - - nn->dp.txd_cnt = new.dcnt; - nn->dp.tx_rings = new.rings; - nn->dp.num_tx_rings = new.n_rings; + nfp_net_tx_ring_free(&dp->tx_rings[r]); + kfree(dp->tx_rings); + return -ENOMEM; } -static void nfp_net_tx_ring_set_free(struct nfp_net_ring_set *s) +static void nfp_net_tx_rings_free(struct nfp_net_dp *dp) { - struct nfp_net_tx_ring *rings = s->rings; unsigned int r; - for (r = 0; r < s->n_rings; r++) - nfp_net_tx_ring_free(&rings[r]); + for (r = 0; r < dp->num_tx_rings; r++) + nfp_net_tx_ring_free(&dp->tx_rings[r]); - kfree(rings); + kfree(dp->tx_rings); } /** @@ -1860,17 +1844,15 @@ static void nfp_net_rx_ring_free(struct nfp_net_rx_ring *rx_ring) * nfp_net_rx_ring_alloc() - Allocate resource for a RX ring * @dp: NFP Net data path struct * @rx_ring: RX ring to allocate - * @cnt: Ring buffer count * * Return: 0 on success, negative errno otherwise. */ static int -nfp_net_rx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, - u32 cnt) +nfp_net_rx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring) { int sz; - rx_ring->cnt = cnt; + rx_ring->cnt = dp->rxd_cnt; rx_ring->size = sizeof(*rx_ring->rxds) * rx_ring->cnt; rx_ring->rxds = dma_zalloc_coherent(dp->dev, rx_ring->size, &rx_ring->dma, GFP_KERNEL); @@ -1889,72 +1871,47 @@ err_alloc: return -ENOMEM; } -static struct nfp_net_rx_ring * -nfp_net_rx_ring_set_prepare(struct nfp_net *nn, struct nfp_net_dp *dp, - struct nfp_net_ring_set *s) +static int nfp_net_rx_rings_prepare(struct nfp_net *nn, struct nfp_net_dp *dp) { - struct nfp_net_rx_ring *rings; unsigned int r; - rings = kcalloc(s->n_rings, sizeof(*rings), GFP_KERNEL); - if (!rings) - return NULL; + dp->rx_rings = kcalloc(dp->num_rx_rings, sizeof(*dp->rx_rings), + GFP_KERNEL); + if (!dp->rx_rings) + return -ENOMEM; - for (r = 0; r < s->n_rings; r++) { - nfp_net_rx_ring_init(&rings[r], &nn->r_vecs[r], r); + for (r = 0; r < dp->num_rx_rings; r++) { + nfp_net_rx_ring_init(&dp->rx_rings[r], &nn->r_vecs[r], r); - if (nfp_net_rx_ring_alloc(dp, &rings[r], s->dcnt)) + if (nfp_net_rx_ring_alloc(dp, &dp->rx_rings[r])) goto err_free_prev; - if (nfp_net_rx_ring_bufs_alloc(dp, &rings[r])) + if (nfp_net_rx_ring_bufs_alloc(dp, &dp->rx_rings[r])) goto err_free_ring; } - return s->rings = rings; + return 0; err_free_prev: while (r--) { - nfp_net_rx_ring_bufs_free(dp, &rings[r]); + nfp_net_rx_ring_bufs_free(dp, &dp->rx_rings[r]); err_free_ring: - nfp_net_rx_ring_free(&rings[r]); + nfp_net_rx_ring_free(&dp->rx_rings[r]); } - kfree(rings); - return NULL; -} - -static void -nfp_net_rx_ring_set_swap(struct nfp_net *nn, struct nfp_net_dp *dp, - struct nfp_net_ring_set *s) -{ - struct nfp_net_ring_set new = *s; - struct nfp_net_dp new_dp = *dp; - - dp->fl_bufsz = nn->dp.fl_bufsz; - dp->mtu = nn->dp.netdev->mtu; - s->dcnt = nn->dp.rxd_cnt; - s->rings = nn->dp.rx_rings; - s->n_rings = nn->dp.num_rx_rings; - - nn->dp.mtu = new_dp.mtu; - nn->dp.netdev->mtu = new_dp.mtu; - nn->dp.fl_bufsz = new_dp.fl_bufsz; - nn->dp.rxd_cnt = new.dcnt; - nn->dp.rx_rings = new.rings; - nn->dp.num_rx_rings = new.n_rings; + kfree(dp->rx_rings); + return -ENOMEM; } -static void -nfp_net_rx_ring_set_free(struct nfp_net_dp *dp, struct nfp_net_ring_set *s) +static void nfp_net_rx_rings_free(struct nfp_net_dp *dp) { - struct nfp_net_rx_ring *rings = s->rings; unsigned int r; - for (r = 0; r < s->n_rings; r++) { - nfp_net_rx_ring_bufs_free(dp, &rings[r]); - nfp_net_rx_ring_free(&rings[r]); + for (r = 0; r < dp->num_rx_rings; r++) { + nfp_net_rx_ring_bufs_free(dp, &dp->rx_rings[r]); + nfp_net_rx_ring_free(&dp->rx_rings[r]); } - kfree(rings); + kfree(dp->rx_rings); } static void @@ -2248,14 +2205,6 @@ static void nfp_net_open_stack(struct nfp_net *nn) static int nfp_net_netdev_open(struct net_device *netdev) { struct nfp_net *nn = netdev_priv(netdev); - struct nfp_net_ring_set rx = { - .n_rings = nn->dp.num_rx_rings, - .dcnt = nn->dp.rxd_cnt, - }; - struct nfp_net_ring_set tx = { - .n_rings = nn->dp.num_tx_rings, - .dcnt = nn->dp.txd_cnt, - }; int err, r; if (nn->dp.ctrl & NFP_NET_CFG_CTRL_ENABLE) { @@ -2286,17 +2235,13 @@ static int nfp_net_netdev_open(struct net_device *netdev) goto err_cleanup_vec_p; } - nn->dp.rx_rings = nfp_net_rx_ring_set_prepare(nn, &nn->dp, &rx); - if (!nn->dp.rx_rings) { - err = -ENOMEM; + err = nfp_net_rx_rings_prepare(nn, &nn->dp); + if (err) goto err_cleanup_vec; - } - nn->dp.tx_rings = nfp_net_tx_ring_set_prepare(nn, &nn->dp, &tx); - if (!nn->dp.tx_rings) { - err = -ENOMEM; + err = nfp_net_tx_rings_prepare(nn, &nn->dp); + if (err) goto err_free_rx_rings; - } for (r = 0; r < nn->max_r_vecs; r++) nfp_net_vector_assign_rings(&nn->dp, &nn->r_vecs[r], r); @@ -2331,9 +2276,9 @@ static int nfp_net_netdev_open(struct net_device *netdev) return 0; err_free_rings: - nfp_net_tx_ring_set_free(&tx); + nfp_net_tx_rings_free(&nn->dp); err_free_rx_rings: - nfp_net_rx_ring_set_free(&nn->dp, &rx); + nfp_net_rx_rings_free(&nn->dp); err_cleanup_vec: r = nn->dp.num_r_vecs; err_cleanup_vec_p: @@ -2460,31 +2405,21 @@ static void nfp_net_dp_swap(struct nfp_net *nn, struct nfp_net_dp *dp) nn->dp = new_dp; nn->dp.netdev->mtu = new_dp.mtu; + + if (!netif_is_rxfh_configured(nn->dp.netdev)) + nfp_net_rss_init_itbl(nn); } -static int -nfp_net_ring_swap_enable(struct nfp_net *nn, struct nfp_net_dp *dp, - struct nfp_net_ring_set *rx, - struct nfp_net_ring_set *tx) +static int nfp_net_dp_swap_enable(struct nfp_net *nn, struct nfp_net_dp *dp) { unsigned int r; int err; - if (rx) - nfp_net_rx_ring_set_swap(nn, dp, rx); - if (tx) - nfp_net_tx_ring_set_swap(nn, tx); - - swap(dp->num_r_vecs, nn->dp.num_r_vecs); - swap(dp->num_stack_tx_rings, nn->dp.num_stack_tx_rings); - dp->xdp_prog = xchg(&nn->dp.xdp_prog, dp->xdp_prog); + nfp_net_dp_swap(nn, dp); for (r = 0; r < nn->max_r_vecs; r++) nfp_net_vector_assign_rings(&nn->dp, &nn->r_vecs[r], r); - if (!netif_is_rxfh_configured(nn->dp.netdev)) - nfp_net_rss_init_itbl(nn); - err = netif_set_real_num_rx_queues(nn->dp.netdev, nn->dp.num_rx_rings); if (err) return err; @@ -2519,9 +2454,7 @@ struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn) return new; } -static int -nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp, - struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) +static int nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp) { /* XDP-enabled tests */ if (!dp->xdp_prog) @@ -2530,7 +2463,7 @@ nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp, nn_warn(nn, "MTU too large w/ XDP enabled\n"); return -EINVAL; } - if (tx && tx->n_rings > nn->max_tx_rings) { + if (dp->num_tx_rings > nn->max_tx_rings) { nn_warn(nn, "Insufficient number of TX rings w/ XDP enabled\n"); return -EINVAL; } @@ -2538,44 +2471,24 @@ nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp, return 0; } -static void -nfp_net_ring_reconfig_down(struct nfp_net *nn, struct nfp_net_dp *dp, - struct nfp_net_ring_set *rx, - struct nfp_net_ring_set *tx) -{ - nfp_net_dp_swap(nn, dp); - - nn->dp.rxd_cnt = rx ? rx->dcnt : nn->dp.rxd_cnt; - nn->dp.txd_cnt = tx ? tx->dcnt : nn->dp.txd_cnt; - nn->dp.num_rx_rings = rx ? rx->n_rings : nn->dp.num_rx_rings; - nn->dp.num_tx_rings = tx ? tx->n_rings : nn->dp.num_tx_rings; - - if (!netif_is_rxfh_configured(nn->dp.netdev)) - nfp_net_rss_init_itbl(nn); -} - -int -nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, - struct nfp_net_ring_set *rx, struct nfp_net_ring_set *tx) +int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp) { int r, err; dp->fl_bufsz = nfp_net_calc_fl_bufsz(dp); - dp->num_stack_tx_rings = tx ? tx->n_rings : dp->num_tx_rings; + dp->num_stack_tx_rings = dp->num_tx_rings; if (dp->xdp_prog) - dp->num_stack_tx_rings -= rx ? rx->n_rings : dp->num_rx_rings; + dp->num_stack_tx_rings -= dp->num_rx_rings; - dp->num_r_vecs = max(rx ? rx->n_rings : dp->num_rx_rings, - dp->num_stack_tx_rings); + dp->num_r_vecs = max(dp->num_rx_rings, dp->num_stack_tx_rings); - err = nfp_net_check_config(nn, dp, rx, tx); + err = nfp_net_check_config(nn, dp); if (err) goto exit_free_dp; if (!netif_running(dp->netdev)) { - nfp_net_ring_reconfig_down(nn, dp, rx, tx); - + nfp_net_dp_swap(nn, dp); err = 0; goto exit_free_dp; } @@ -2588,31 +2501,27 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, goto err_cleanup_vecs; } } - if (rx) { - if (!nfp_net_rx_ring_set_prepare(nn, dp, rx)) { - err = -ENOMEM; - goto err_cleanup_vecs; - } - } - if (tx) { - if (!nfp_net_tx_ring_set_prepare(nn, dp, tx)) { - err = -ENOMEM; - goto err_free_rx; - } - } + + err = nfp_net_rx_rings_prepare(nn, dp); + if (err) + goto err_cleanup_vecs; + + err = nfp_net_tx_rings_prepare(nn, dp); + if (err) + goto err_free_rx; /* Stop device, swap in new rings, try to start the firmware */ nfp_net_close_stack(nn); nfp_net_clear_config_and_disable(nn); - err = nfp_net_ring_swap_enable(nn, dp, rx, tx); + err = nfp_net_dp_swap_enable(nn, dp); if (err) { int err2; nfp_net_clear_config_and_disable(nn); /* Try with old configuration and old rings */ - err2 = nfp_net_ring_swap_enable(nn, dp, rx, tx); + err2 = nfp_net_dp_swap_enable(nn, dp); if (err2) nn_err(nn, "Can't restore ring config - FW communication failed (%d,%d)\n", err, err2); @@ -2620,10 +2529,8 @@ nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, for (r = dp->num_r_vecs - 1; r >= nn->dp.num_r_vecs; r--) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); - if (rx) - nfp_net_rx_ring_set_free(dp, rx); - if (tx) - nfp_net_tx_ring_set_free(tx); + nfp_net_rx_rings_free(dp); + nfp_net_tx_rings_free(dp); nfp_net_open_stack(nn); exit_free_dp: @@ -2632,8 +2539,7 @@ exit_free_dp: return err; err_free_rx: - if (rx) - nfp_net_rx_ring_set_free(dp, rx); + nfp_net_rx_rings_free(dp); err_cleanup_vecs: for (r = dp->num_r_vecs - 1; r >= nn->dp.num_r_vecs; r--) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); @@ -2644,10 +2550,6 @@ err_cleanup_vecs: static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) { struct nfp_net *nn = netdev_priv(netdev); - struct nfp_net_ring_set rx = { - .n_rings = nn->dp.num_rx_rings, - .dcnt = nn->dp.rxd_cnt, - }; struct nfp_net_dp *dp; dp = nfp_net_clone_dp(nn); @@ -2656,7 +2558,7 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) dp->mtu = new_mtu; - return nfp_net_ring_reconfig(nn, dp, &rx, NULL); + return nfp_net_ring_reconfig(nn, dp); } static void nfp_net_stat64(struct net_device *netdev, @@ -2975,14 +2877,6 @@ static int nfp_net_xdp_offload(struct nfp_net *nn, struct bpf_prog *prog) static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) { struct bpf_prog *old_prog = nn->dp.xdp_prog; - struct nfp_net_ring_set rx = { - .n_rings = nn->dp.num_rx_rings, - .dcnt = nn->dp.rxd_cnt, - }; - struct nfp_net_ring_set tx = { - .n_rings = nn->dp.num_tx_rings, - .dcnt = nn->dp.txd_cnt, - }; struct nfp_net_dp *dp; int err; @@ -3004,10 +2898,10 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) return -ENOMEM; dp->xdp_prog = prog; - tx.n_rings += prog ? nn->dp.num_rx_rings : -nn->dp.num_rx_rings; + dp->num_tx_rings += prog ? nn->dp.num_rx_rings : -nn->dp.num_rx_rings; /* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */ - err = nfp_net_ring_reconfig(nn, dp, &rx, &tx); + err = nfp_net_ring_reconfig(nn, dp); if (err) return err; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 63c1d9ab2335..ed22a813e579 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -186,27 +186,16 @@ static void nfp_net_get_ringparam(struct net_device *netdev, static int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt) { - struct nfp_net_ring_set *reconfig_rx = NULL, *reconfig_tx = NULL; - struct nfp_net_ring_set rx = { - .n_rings = nn->dp.num_rx_rings, - .dcnt = rxd_cnt, - }; - struct nfp_net_ring_set tx = { - .n_rings = nn->dp.num_tx_rings, - .dcnt = txd_cnt, - }; struct nfp_net_dp *dp; - if (nn->dp.rxd_cnt != rxd_cnt) - reconfig_rx = ℞ - if (nn->dp.txd_cnt != txd_cnt) - reconfig_tx = &tx; - dp = nfp_net_clone_dp(nn); if (!dp) return -ENOMEM; - return nfp_net_ring_reconfig(nn, dp, reconfig_rx, reconfig_tx); + dp->rxd_cnt = rxd_cnt; + dp->txd_cnt = txd_cnt; + + return nfp_net_ring_reconfig(nn, dp); } static int nfp_net_set_ringparam(struct net_device *netdev, @@ -765,32 +754,19 @@ static void nfp_net_get_channels(struct net_device *netdev, static int nfp_net_set_num_rings(struct nfp_net *nn, unsigned int total_rx, unsigned int total_tx) { - struct nfp_net_ring_set *reconfig_rx = NULL, *reconfig_tx = NULL; - struct nfp_net_ring_set rx = { - .n_rings = total_rx, - .dcnt = nn->dp.rxd_cnt, - }; - struct nfp_net_ring_set tx = { - .n_rings = total_tx, - .dcnt = nn->dp.txd_cnt, - }; struct nfp_net_dp *dp; - if (nn->dp.num_rx_rings != total_rx) - reconfig_rx = ℞ - if (nn->dp.num_stack_tx_rings != total_tx || - (nn->dp.xdp_prog && reconfig_rx)) - reconfig_tx = &tx; - - /* nfp_net_check_config() will catch tx.n_rings > nn->max_tx_rings */ - if (nn->dp.xdp_prog) - tx.n_rings += total_rx; - dp = nfp_net_clone_dp(nn); if (!dp) return -ENOMEM; - return nfp_net_ring_reconfig(nn, dp, reconfig_rx, reconfig_tx); + dp->num_rx_rings = total_rx; + dp->num_tx_rings = total_tx; + /* nfp_net_check_config() will catch num_tx_rings > nn->max_tx_rings */ + if (dp->xdp_prog) + dp->num_tx_rings += total_rx; + + return nfp_net_ring_reconfig(nn, dp); } static int nfp_net_set_channels(struct net_device *netdev, -- cgit From c487e6b199eab3af14e54406d97cc3c149e591e1 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 10 Mar 2017 10:38:35 -0800 Subject: nfp: store dma direction in data path structure Instead of testing if xdp_prog is present store the dma direction in data path structure. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 11 ++++-- .../net/ethernet/netronome/nfp/nfp_net_common.c | 45 ++++++++-------------- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 5a92f6e41dae..db92463da440 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -438,6 +438,7 @@ struct nfp_stat_pair { * @bpf_offload_skip_sw: Offloaded BPF program will not be rerun by cls_bpf * @bpf_offload_xdp: Offloaded BPF program is XDP * @chained_metadata_format: Firemware will use new metadata format + * @rx_dma_dir: Mapping direction for RX buffers * @ctrl: Local copy of the control register/word. * @fl_bufsz: Currently configured size of the freelist buffers * @rx_offset: Offset in the RX buffers where packet data starts @@ -458,10 +459,12 @@ struct nfp_net_dp { struct device *dev; struct net_device *netdev; - unsigned is_vf:1; - unsigned bpf_offload_skip_sw:1; - unsigned bpf_offload_xdp:1; - unsigned chained_metadata_format:1; + u8 is_vf:1; + u8 bpf_offload_skip_sw:1; + u8 bpf_offload_xdp:1; + u8 chained_metadata_format:1; + + u8 rx_dma_dir; u32 ctrl; u32 fl_bufsz; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index a9359da64f80..ab03f2f301cd 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -85,20 +85,18 @@ void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver, put_unaligned_le32(reg, fw_ver); } -static dma_addr_t -nfp_net_dma_map_rx(struct nfp_net_dp *dp, void *frag, int direction) +static dma_addr_t nfp_net_dma_map_rx(struct nfp_net_dp *dp, void *frag) { return dma_map_single(dp->dev, frag + NFP_NET_RX_BUF_HEADROOM, dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, - direction); + dp->rx_dma_dir); } -static void -nfp_net_dma_unmap_rx(struct nfp_net_dp *dp, dma_addr_t dma_addr, - int direction) +static void nfp_net_dma_unmap_rx(struct nfp_net_dp *dp, dma_addr_t dma_addr) { dma_unmap_single(dp->dev, dma_addr, - dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, direction); + dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, + dp->rx_dma_dir); } /* Firmware reconfig @@ -991,8 +989,7 @@ static void nfp_net_xdp_complete(struct nfp_net_tx_ring *tx_ring) if (!tx_ring->txbufs[idx].frag) continue; - nfp_net_dma_unmap_rx(dp, tx_ring->txbufs[idx].dma_addr, - DMA_BIDIRECTIONAL); + nfp_net_dma_unmap_rx(dp, tx_ring->txbufs[idx].dma_addr); __free_page(virt_to_page(tx_ring->txbufs[idx].frag)); done_pkts++; @@ -1037,8 +1034,7 @@ nfp_net_tx_ring_reset(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) tx_buf = &tx_ring->txbufs[idx]; if (tx_ring == r_vec->xdp_ring) { - nfp_net_dma_unmap_rx(dp, tx_buf->dma_addr, - DMA_BIDIRECTIONAL); + nfp_net_dma_unmap_rx(dp, tx_buf->dma_addr); __free_page(virt_to_page(tx_ring->txbufs[idx].frag)); } else { struct sk_buff *skb = tx_ring->txbufs[idx].skb; @@ -1139,7 +1135,6 @@ static void * nfp_net_rx_alloc_one(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, dma_addr_t *dma_addr) { - int direction; void *frag; if (!dp->xdp_prog) @@ -1151,9 +1146,7 @@ nfp_net_rx_alloc_one(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, return NULL; } - direction = dp->xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; - - *dma_addr = nfp_net_dma_map_rx(dp, frag, direction); + *dma_addr = nfp_net_dma_map_rx(dp, frag); if (dma_mapping_error(dp->dev, *dma_addr)) { nfp_net_free_frag(frag, dp->xdp_prog); nn_dp_warn(dp, "Failed to map DMA RX buffer\n"); @@ -1163,9 +1156,7 @@ nfp_net_rx_alloc_one(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, return frag; } -static void * -nfp_net_napi_alloc_one(struct nfp_net_dp *dp, int direction, - dma_addr_t *dma_addr) +static void *nfp_net_napi_alloc_one(struct nfp_net_dp *dp, dma_addr_t *dma_addr) { void *frag; @@ -1178,7 +1169,7 @@ nfp_net_napi_alloc_one(struct nfp_net_dp *dp, int direction, return NULL; } - *dma_addr = nfp_net_dma_map_rx(dp, frag, direction); + *dma_addr = nfp_net_dma_map_rx(dp, frag); if (dma_mapping_error(dp->dev, *dma_addr)) { nfp_net_free_frag(frag, dp->xdp_prog); nn_dp_warn(dp, "Failed to map DMA RX buffer\n"); @@ -1260,7 +1251,6 @@ static void nfp_net_rx_ring_bufs_free(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring) { - int direction = dp->xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; unsigned int i; for (i = 0; i < rx_ring->cnt - 1; i++) { @@ -1271,8 +1261,7 @@ nfp_net_rx_ring_bufs_free(struct nfp_net_dp *dp, if (!rx_ring->rxbufs[i].frag) continue; - nfp_net_dma_unmap_rx(dp, rx_ring->rxbufs[i].dma_addr, - direction); + nfp_net_dma_unmap_rx(dp, rx_ring->rxbufs[i].dma_addr); nfp_net_free_frag(rx_ring->rxbufs[i].frag, dp->xdp_prog); rx_ring->rxbufs[i].dma_addr = 0; rx_ring->rxbufs[i].frag = NULL; @@ -1478,7 +1467,7 @@ nfp_net_tx_xdp_buf(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, return false; } - new_frag = nfp_net_napi_alloc_one(dp, DMA_BIDIRECTIONAL, &new_dma_addr); + new_frag = nfp_net_napi_alloc_one(dp, &new_dma_addr); if (unlikely(!new_frag)) { nfp_net_rx_drop(rx_ring->r_vec, rx_ring, rxbuf, NULL); return false; @@ -1544,12 +1533,10 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) unsigned int true_bufsz; struct sk_buff *skb; int pkts_polled = 0; - int rx_dma_map_dir; int idx; rcu_read_lock(); xdp_prog = READ_ONCE(dp->xdp_prog); - rx_dma_map_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; true_bufsz = xdp_prog ? PAGE_SIZE : dp->fl_bufsz; tx_ring = r_vec->xdp_ring; @@ -1639,14 +1626,13 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) nfp_net_rx_drop(r_vec, rx_ring, rxbuf, NULL); continue; } - new_frag = nfp_net_napi_alloc_one(dp, rx_dma_map_dir, - &new_dma_addr); + new_frag = nfp_net_napi_alloc_one(dp, &new_dma_addr); if (unlikely(!new_frag)) { nfp_net_rx_drop(r_vec, rx_ring, rxbuf, skb); continue; } - nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr, rx_dma_map_dir); + nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr); nfp_net_rx_give_one(rx_ring, new_frag, new_dma_addr); @@ -2899,6 +2885,7 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) dp->xdp_prog = prog; dp->num_tx_rings += prog ? nn->dp.num_rx_rings : -nn->dp.num_rx_rings; + dp->rx_dma_dir = prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; /* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */ err = nfp_net_ring_reconfig(nn, dp); @@ -3128,6 +3115,8 @@ int nfp_net_netdev_init(struct net_device *netdev) nn->dp.chained_metadata_format = nn->fw_ver.major > 3; + nn->dp.rx_dma_dir = DMA_FROM_DEVICE; + /* Get some of the read-only fields from the BAR */ nn->cap = nn_readl(nn, NFP_NET_CFG_CAP); nn->max_mtu = nn_readl(nn, NFP_NET_CFG_MAX_MTU); -- cgit From 97717aca618a85d9e52327b4509a3b95c2f8f121 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 10 Mar 2017 10:38:36 -0800 Subject: nfp: validate rx offset from the BAR and size down it's field NFP_NET_CFG_RX_OFFSET is 32bit wide, make sure what we read from there is reasonable for packet headroom. This allows us to store the rx_offset in a 8bit variable. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 6 +++--- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 14 +++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index db92463da440..5f0547c6efb8 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -439,9 +439,9 @@ struct nfp_stat_pair { * @bpf_offload_xdp: Offloaded BPF program is XDP * @chained_metadata_format: Firemware will use new metadata format * @rx_dma_dir: Mapping direction for RX buffers + * @rx_offset: Offset in the RX buffers where packet data starts * @ctrl: Local copy of the control register/word. * @fl_bufsz: Currently configured size of the freelist buffers - * @rx_offset: Offset in the RX buffers where packet data starts * @xdp_prog: Installed XDP program * @tx_rings: Array of pre-allocated TX ring structures * @rx_rings: Array of pre-allocated RX ring structures @@ -466,11 +466,11 @@ struct nfp_net_dp { u8 rx_dma_dir; + u8 rx_offset; + u32 ctrl; u32 fl_bufsz; - u32 rx_offset; - struct bpf_prog *xdp_prog; struct nfp_net_tx_ring *tx_rings; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index ab03f2f301cd..513f55dd746b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -3124,10 +3124,18 @@ int nfp_net_netdev_init(struct net_device *netdev) nfp_net_write_mac_addr(nn); /* Determine RX packet/metadata boundary offset */ - if (nn->fw_ver.major >= 2) - nn->dp.rx_offset = nn_readl(nn, NFP_NET_CFG_RX_OFFSET); - else + if (nn->fw_ver.major >= 2) { + u32 reg; + + reg = nn_readl(nn, NFP_NET_CFG_RX_OFFSET); + if (reg > NFP_NET_MAX_PREPEND) { + nn_err(nn, "Invalid rx offset: %d\n", reg); + return -EINVAL; + } + nn->dp.rx_offset = reg; + } else { nn->dp.rx_offset = NFP_NET_RX_OFFSET; + } /* Set default MTU and Freelist buffer size */ if (nn->max_mtu < NFP_NET_DEFAULT_MTU) -- cgit From 1abae31953996283e1956af0b8ffa72c2b682e77 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 10 Mar 2017 10:38:37 -0800 Subject: nfp: reorganize pkt_off variable Rename pkt_off variable to dma_off, it should hold data offset counting from beginning of DMA mapping. Compute the value only in XDP context. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 513f55dd746b..0e4fa6802733 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1453,7 +1453,7 @@ nfp_net_rx_drop(struct nfp_net_r_vector *r_vec, struct nfp_net_rx_ring *rx_ring, static bool nfp_net_tx_xdp_buf(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, struct nfp_net_tx_ring *tx_ring, - struct nfp_net_rx_buf *rxbuf, unsigned int pkt_off, + struct nfp_net_rx_buf *rxbuf, unsigned int dma_off, unsigned int pkt_len) { struct nfp_net_tx_buf *txbuf; @@ -1484,14 +1484,14 @@ nfp_net_tx_xdp_buf(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, txbuf->pkt_cnt = 1; txbuf->real_len = pkt_len; - dma_sync_single_for_device(dp->dev, rxbuf->dma_addr + pkt_off, + dma_sync_single_for_device(dp->dev, rxbuf->dma_addr + dma_off, pkt_len, DMA_BIDIRECTIONAL); /* Build TX descriptor */ txd = &tx_ring->txds[wr_idx]; txd->offset_eop = PCIE_DESC_TX_EOP; txd->dma_len = cpu_to_le16(pkt_len); - nfp_desc_set_dma_addr(txd, rxbuf->dma_addr + pkt_off); + nfp_desc_set_dma_addr(txd, rxbuf->dma_addr + dma_off); txd->data_len = cpu_to_le16(pkt_len); txd->flags = 0; @@ -1541,7 +1541,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) tx_ring = r_vec->xdp_ring; while (pkts_polled < budget) { - unsigned int meta_len, data_len, data_off, pkt_len, pkt_off; + unsigned int meta_len, data_len, data_off, pkt_len; struct nfp_net_rx_buf *rxbuf; struct nfp_net_rx_desc *rxd; dma_addr_t new_dma_addr; @@ -1579,10 +1579,9 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) pkt_len = data_len - meta_len; if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) - pkt_off = meta_len; + data_off = NFP_NET_RX_BUF_HEADROOM + meta_len; else - pkt_off = dp->rx_offset; - data_off = NFP_NET_RX_BUF_HEADROOM + pkt_off; + data_off = NFP_NET_RX_BUF_HEADROOM + dp->rx_offset; /* Stats update */ u64_stats_update_begin(&r_vec->rx_sync); @@ -1592,10 +1591,12 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) if (xdp_prog && !(rxd->rxd.flags & PCIE_DESC_RX_BPF && dp->bpf_offload_xdp)) { + unsigned int dma_off; int act; + dma_off = data_off - NFP_NET_RX_BUF_HEADROOM; dma_sync_single_for_cpu(dp->dev, - rxbuf->dma_addr + pkt_off, + rxbuf->dma_addr + dma_off, pkt_len, DMA_BIDIRECTIONAL); act = nfp_net_run_xdp(xdp_prog, rxbuf->frag + data_off, pkt_len); @@ -1605,7 +1606,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) case XDP_TX: if (unlikely(!nfp_net_tx_xdp_buf(dp, rx_ring, tx_ring, rxbuf, - pkt_off, + dma_off, pkt_len))) trace_xdp_exception(dp->netdev, xdp_prog, act); -- cgit From b92fb77f27af90d3ee785eedc326f80cb6190597 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 10 Mar 2017 10:38:38 -0800 Subject: nfp: prepare metadata handling for xdp_adjust_head() XDP may require us to move metadata to make room for pushing headers. Track meta data location with a pointer and pass it explicitly to functions. While at it validate that meta_len from the descriptor is not bogus. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_common.c | 28 +++++++++++++++------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 0e4fa6802733..fe7c3f6d820d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1385,24 +1385,21 @@ static void nfp_net_set_hash(struct net_device *netdev, struct sk_buff *skb, static void nfp_net_set_hash_desc(struct net_device *netdev, struct sk_buff *skb, - struct nfp_net_rx_desc *rxd) + void *data, struct nfp_net_rx_desc *rxd) { - struct nfp_net_rx_hash *rx_hash; + struct nfp_net_rx_hash *rx_hash = data; if (!(rxd->rxd.flags & PCIE_DESC_RX_RSS)) return; - rx_hash = (struct nfp_net_rx_hash *)(skb->data - sizeof(*rx_hash)); - nfp_net_set_hash(netdev, skb, get_unaligned_be32(&rx_hash->hash_type), &rx_hash->hash); } static void * nfp_net_parse_meta(struct net_device *netdev, struct sk_buff *skb, - int meta_len) + void *data, int meta_len) { - u8 *data = skb->data - meta_len; u32 meta_info; meta_info = get_unaligned_be32(data); @@ -1546,6 +1543,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) struct nfp_net_rx_desc *rxd; dma_addr_t new_dma_addr; void *new_frag; + u8 *meta; idx = rx_ring->rd_p & (rx_ring->cnt - 1); @@ -1589,6 +1587,17 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) r_vec->rx_bytes += pkt_len; u64_stats_update_end(&r_vec->rx_sync); + /* Pointer to start of metadata */ + meta = rxbuf->frag + data_off - meta_len; + + if (unlikely(meta_len > NFP_NET_MAX_PREPEND || + (dp->rx_offset && meta_len > dp->rx_offset))) { + nn_dp_warn(dp, "oversized RX packet metadata %u\n", + meta_len); + nfp_net_rx_drop(r_vec, rx_ring, rxbuf, NULL); + continue; + } + if (xdp_prog && !(rxd->rxd.flags & PCIE_DESC_RX_BPF && dp->bpf_offload_xdp)) { unsigned int dma_off; @@ -1641,12 +1650,13 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) skb_put(skb, pkt_len); if (!dp->chained_metadata_format) { - nfp_net_set_hash_desc(dp->netdev, skb, rxd); + nfp_net_set_hash_desc(dp->netdev, skb, meta, rxd); } else if (meta_len) { void *end; - end = nfp_net_parse_meta(dp->netdev, skb, meta_len); - if (unlikely(end != skb->data)) { + end = nfp_net_parse_meta(dp->netdev, skb, meta, + meta_len); + if (unlikely(end != meta + meta_len)) { nn_dp_warn(dp, "invalid RX packet metadata\n"); nfp_net_rx_drop(r_vec, rx_ring, NULL, skb); continue; -- cgit From 6fe0c3b43804c9803a4a31c9d2ed8c2014bc0dc3 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 10 Mar 2017 10:38:39 -0800 Subject: nfp: add support for xdp_adjust_head() Support prepending data from XDP. We are already always allocating some headroom because FW may prepend metadata to packets. xdp_adjust_head() can be supported by making sure that headroom is big enough for XDP. In case FW had prepended metadata to the packet, however, we have to move it out of the way before we call XDP. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 2 + .../net/ethernet/netronome/nfp/nfp_net_common.c | 98 +++++++++++++++------- 2 files changed, 70 insertions(+), 30 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 5f0547c6efb8..4d45f4573b57 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -439,6 +439,7 @@ struct nfp_stat_pair { * @bpf_offload_xdp: Offloaded BPF program is XDP * @chained_metadata_format: Firemware will use new metadata format * @rx_dma_dir: Mapping direction for RX buffers + * @rx_dma_off: Offset at which DMA packets (for XDP headroom) * @rx_offset: Offset in the RX buffers where packet data starts * @ctrl: Local copy of the control register/word. * @fl_bufsz: Currently configured size of the freelist buffers @@ -465,6 +466,7 @@ struct nfp_net_dp { u8 chained_metadata_format:1; u8 rx_dma_dir; + u8 rx_dma_off; u8 rx_offset; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index fe7c3f6d820d..f134f1808b9a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1100,6 +1100,7 @@ nfp_net_calc_fl_bufsz(struct nfp_net_dp *dp) unsigned int fl_bufsz; fl_bufsz = NFP_NET_RX_BUF_HEADROOM; + fl_bufsz += dp->rx_dma_off; if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) fl_bufsz += NFP_NET_MAX_PREPEND; else @@ -1181,11 +1182,13 @@ static void *nfp_net_napi_alloc_one(struct nfp_net_dp *dp, dma_addr_t *dma_addr) /** * nfp_net_rx_give_one() - Put mapped skb on the software and hardware rings + * @dp: NFP Net data path struct * @rx_ring: RX ring structure * @frag: page fragment buffer * @dma_addr: DMA address of skb mapping */ -static void nfp_net_rx_give_one(struct nfp_net_rx_ring *rx_ring, +static void nfp_net_rx_give_one(const struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring, void *frag, dma_addr_t dma_addr) { unsigned int wr_idx; @@ -1199,7 +1202,8 @@ static void nfp_net_rx_give_one(struct nfp_net_rx_ring *rx_ring, /* Fill freelist descriptor */ rx_ring->rxds[wr_idx].fld.reserved = 0; rx_ring->rxds[wr_idx].fld.meta_len_dd = 0; - nfp_desc_set_dma_addr(&rx_ring->rxds[wr_idx].fld, dma_addr); + nfp_desc_set_dma_addr(&rx_ring->rxds[wr_idx].fld, + dma_addr + dp->rx_dma_off); rx_ring->wr_p++; rx_ring->wr_ptr_add++; @@ -1296,14 +1300,17 @@ nfp_net_rx_ring_bufs_alloc(struct nfp_net_dp *dp, /** * nfp_net_rx_ring_fill_freelist() - Give buffers from the ring to FW + * @dp: NFP Net data path struct * @rx_ring: RX ring to fill */ -static void nfp_net_rx_ring_fill_freelist(struct nfp_net_rx_ring *rx_ring) +static void +nfp_net_rx_ring_fill_freelist(struct nfp_net_dp *dp, + struct nfp_net_rx_ring *rx_ring) { unsigned int i; for (i = 0; i < rx_ring->cnt - 1; i++) - nfp_net_rx_give_one(rx_ring, rx_ring->rxbufs[i].frag, + nfp_net_rx_give_one(dp, rx_ring, rx_ring->rxbufs[i].frag, rx_ring->rxbufs[i].dma_addr); } @@ -1429,8 +1436,9 @@ nfp_net_parse_meta(struct net_device *netdev, struct sk_buff *skb, } static void -nfp_net_rx_drop(struct nfp_net_r_vector *r_vec, struct nfp_net_rx_ring *rx_ring, - struct nfp_net_rx_buf *rxbuf, struct sk_buff *skb) +nfp_net_rx_drop(const struct nfp_net_dp *dp, struct nfp_net_r_vector *r_vec, + struct nfp_net_rx_ring *rx_ring, struct nfp_net_rx_buf *rxbuf, + struct sk_buff *skb) { u64_stats_update_begin(&r_vec->rx_sync); r_vec->rx_drops++; @@ -1442,7 +1450,7 @@ nfp_net_rx_drop(struct nfp_net_r_vector *r_vec, struct nfp_net_rx_ring *rx_ring, if (skb && rxbuf && skb->head == rxbuf->frag) page_ref_inc(virt_to_head_page(rxbuf->frag)); if (rxbuf) - nfp_net_rx_give_one(rx_ring, rxbuf->frag, rxbuf->dma_addr); + nfp_net_rx_give_one(dp, rx_ring, rxbuf->frag, rxbuf->dma_addr); if (skb) dev_kfree_skb_any(skb); } @@ -1460,16 +1468,16 @@ nfp_net_tx_xdp_buf(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, int wr_idx; if (unlikely(nfp_net_tx_full(tx_ring, 1))) { - nfp_net_rx_drop(rx_ring->r_vec, rx_ring, rxbuf, NULL); + nfp_net_rx_drop(dp, rx_ring->r_vec, rx_ring, rxbuf, NULL); return false; } new_frag = nfp_net_napi_alloc_one(dp, &new_dma_addr); if (unlikely(!new_frag)) { - nfp_net_rx_drop(rx_ring->r_vec, rx_ring, rxbuf, NULL); + nfp_net_rx_drop(dp, rx_ring->r_vec, rx_ring, rxbuf, NULL); return false; } - nfp_net_rx_give_one(rx_ring, new_frag, new_dma_addr); + nfp_net_rx_give_one(dp, rx_ring, new_frag, new_dma_addr); wr_idx = tx_ring->wr_p & (tx_ring->cnt - 1); @@ -1500,14 +1508,24 @@ nfp_net_tx_xdp_buf(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, return true; } -static int nfp_net_run_xdp(struct bpf_prog *prog, void *data, unsigned int len) +static int nfp_net_run_xdp(struct bpf_prog *prog, void *data, void *hard_start, + unsigned int *off, unsigned int *len) { struct xdp_buff xdp; + void *orig_data; + int ret; + + xdp.data_hard_start = hard_start; + xdp.data = data + *off; + xdp.data_end = data + *off + *len; - xdp.data = data; - xdp.data_end = data + len; + orig_data = xdp.data; + ret = bpf_prog_run_xdp(prog, &xdp); - return bpf_prog_run_xdp(prog, &xdp); + *len -= xdp.data - orig_data; + *off += xdp.data - orig_data; + + return ret; } /** @@ -1539,6 +1557,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) while (pkts_polled < budget) { unsigned int meta_len, data_len, data_off, pkt_len; + u8 meta_prepend[NFP_NET_MAX_PREPEND]; struct nfp_net_rx_buf *rxbuf; struct nfp_net_rx_desc *rxd; dma_addr_t new_dma_addr; @@ -1580,6 +1599,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) data_off = NFP_NET_RX_BUF_HEADROOM + meta_len; else data_off = NFP_NET_RX_BUF_HEADROOM + dp->rx_offset; + data_off += dp->rx_dma_off; /* Stats update */ u64_stats_update_begin(&r_vec->rx_sync); @@ -1594,25 +1614,35 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) (dp->rx_offset && meta_len > dp->rx_offset))) { nn_dp_warn(dp, "oversized RX packet metadata %u\n", meta_len); - nfp_net_rx_drop(r_vec, rx_ring, rxbuf, NULL); + nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); continue; } if (xdp_prog && !(rxd->rxd.flags & PCIE_DESC_RX_BPF && dp->bpf_offload_xdp)) { unsigned int dma_off; + void *hard_start; int act; + hard_start = rxbuf->frag + NFP_NET_RX_BUF_HEADROOM; dma_off = data_off - NFP_NET_RX_BUF_HEADROOM; - dma_sync_single_for_cpu(dp->dev, - rxbuf->dma_addr + dma_off, - pkt_len, DMA_BIDIRECTIONAL); - act = nfp_net_run_xdp(xdp_prog, rxbuf->frag + data_off, - pkt_len); + dma_sync_single_for_cpu(dp->dev, rxbuf->dma_addr, + dma_off + pkt_len, + DMA_BIDIRECTIONAL); + + /* Move prepend out of the way */ + if (xdp_prog->xdp_adjust_head) { + memcpy(meta_prepend, meta, meta_len); + meta = meta_prepend; + } + + act = nfp_net_run_xdp(xdp_prog, rxbuf->frag, hard_start, + &data_off, &pkt_len); switch (act) { case XDP_PASS: break; case XDP_TX: + dma_off = data_off - NFP_NET_RX_BUF_HEADROOM; if (unlikely(!nfp_net_tx_xdp_buf(dp, rx_ring, tx_ring, rxbuf, dma_off, @@ -1625,7 +1655,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) case XDP_ABORTED: trace_xdp_exception(dp->netdev, xdp_prog, act); case XDP_DROP: - nfp_net_rx_give_one(rx_ring, rxbuf->frag, + nfp_net_rx_give_one(dp, rx_ring, rxbuf->frag, rxbuf->dma_addr); continue; } @@ -1633,18 +1663,18 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) skb = build_skb(rxbuf->frag, true_bufsz); if (unlikely(!skb)) { - nfp_net_rx_drop(r_vec, rx_ring, rxbuf, NULL); + nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, NULL); continue; } new_frag = nfp_net_napi_alloc_one(dp, &new_dma_addr); if (unlikely(!new_frag)) { - nfp_net_rx_drop(r_vec, rx_ring, rxbuf, skb); + nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, skb); continue; } nfp_net_dma_unmap_rx(dp, rxbuf->dma_addr); - nfp_net_rx_give_one(rx_ring, new_frag, new_dma_addr); + nfp_net_rx_give_one(dp, rx_ring, new_frag, new_dma_addr); skb_reserve(skb, data_off); skb_put(skb, pkt_len); @@ -1658,7 +1688,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) meta_len); if (unlikely(end != meta + meta_len)) { nn_dp_warn(dp, "invalid RX packet metadata\n"); - nfp_net_rx_drop(r_vec, rx_ring, NULL, skb); + nfp_net_rx_drop(dp, r_vec, rx_ring, NULL, skb); continue; } } @@ -2151,7 +2181,7 @@ static int __nfp_net_set_config_and_enable(struct nfp_net *nn) nn->dp.ctrl = new_ctrl; for (r = 0; r < nn->dp.num_rx_rings; r++) - nfp_net_rx_ring_fill_freelist(&nn->dp.rx_rings[r]); + nfp_net_rx_ring_fill_freelist(&nn->dp, &nn->dp.rx_rings[r]); /* Since reconfiguration requests while NFP is down are ignored we * have to wipe the entire VXLAN configuration and reinitialize it. @@ -2877,10 +2907,6 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) struct nfp_net_dp *dp; int err; - if (prog && prog->xdp_adjust_head) { - nn_err(nn, "Does not support bpf_xdp_adjust_head()\n"); - return -EOPNOTSUPP; - } if (!prog && !nn->dp.xdp_prog) return 0; if (prog && nn->dp.xdp_prog) { @@ -2897,6 +2923,11 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) dp->xdp_prog = prog; dp->num_tx_rings += prog ? nn->dp.num_rx_rings : -nn->dp.num_rx_rings; dp->rx_dma_dir = prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; + if (prog) + dp->rx_dma_off = XDP_PACKET_HEADROOM - + (nn->dp.rx_offset ?: NFP_NET_MAX_PREPEND); + else + dp->rx_dma_off = 0; /* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */ err = nfp_net_ring_reconfig(nn, dp); @@ -3124,6 +3155,13 @@ int nfp_net_netdev_init(struct net_device *netdev) struct nfp_net *nn = netdev_priv(netdev); int err; + /* XDP calls for 256 byte packet headroom which wouldn't fit in a u8. + * We, however, reuse the metadata prepend space for XDP buffers which + * is at least 1 byte long and as long as XDP headroom doesn't increase + * above 256 the *extra* XDP headroom will fit on 8 bits. + */ + BUILD_BUG_ON(XDP_PACKET_HEADROOM > 256); + nn->dp.chained_metadata_format = nn->fw_ver.major > 3; nn->dp.rx_dma_dir = DMA_FROM_DEVICE; -- cgit From 0329b7daff75eddcf1df2eecf2cf97a91c33d0ee Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sat, 11 Mar 2017 08:46:56 +0800 Subject: ambassador: use setup_timer Use setup_timer() instead of init_timer() to simplify the code. Signed-off-by: Geliang Tang Signed-off-by: David S. Miller --- drivers/atm/ambassador.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c index 4a610795b585..906705e5f776 100644 --- a/drivers/atm/ambassador.c +++ b/drivers/atm/ambassador.c @@ -2267,9 +2267,8 @@ static int amb_probe(struct pci_dev *pci_dev, dev->atm_dev->ci_range.vpi_bits = NUM_VPI_BITS; dev->atm_dev->ci_range.vci_bits = NUM_VCI_BITS; - init_timer(&dev->housekeeping); - dev->housekeeping.function = do_housekeeping; - dev->housekeeping.data = (unsigned long) dev; + setup_timer(&dev->housekeeping, do_housekeeping, + (unsigned long)dev); mod_timer(&dev->housekeeping, jiffies); // enable host interrupts -- cgit From 27303fcf5734d84c8d018b79d1db154ceaf88df8 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sat, 11 Mar 2017 08:46:59 +0800 Subject: drop_monitor: use setup_timer Use setup_timer() instead of init_timer() to simplify the code. Signed-off-by: Geliang Tang Signed-off-by: David S. Miller --- net/core/drop_monitor.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index fb55327dcfea..70ccda233bd1 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -412,9 +412,8 @@ static int __init init_net_drop_monitor(void) for_each_possible_cpu(cpu) { data = &per_cpu(dm_cpu_data, cpu); INIT_WORK(&data->dm_alert_work, send_dm_alert); - init_timer(&data->send_timer); - data->send_timer.data = (unsigned long)data; - data->send_timer.function = sched_send_work; + setup_timer(&data->send_timer, sched_send_work, + (unsigned long)data); spin_lock_init(&data->lock); reset_per_cpu_data(data); } -- cgit From ff7b0d27208bd730aa71bd91087f0ff80ca60eda Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sat, 11 Mar 2017 09:42:51 +0100 Subject: mlxsw: spectrum: Add support for counter allocator Add implementation for counter allocator. The ASIC has special memory pool for various counting purposes. Counter memory is distributed between equal size banks. The static sub-pool configuration should specify the following parameters for each sub-pool: - Number of required banks. - Maximum entry size. Each module can add dedicated sub-pool or use existing one. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/Makefile | 3 +- drivers/net/ethernet/mellanox/mlxsw/resources.h | 2 + drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 10 ++ drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 2 + drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c | 177 +++++++++++++++++++++ drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h | 54 +++++++ 6 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index 6b6c30deee83..95fcacf9c8be 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -15,7 +15,8 @@ obj-$(CONFIG_MLXSW_SPECTRUM) += mlxsw_spectrum.o mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \ spectrum_switchdev.o spectrum_router.o \ spectrum_kvdl.o spectrum_acl_tcam.o \ - spectrum_acl.o spectrum_flower.o + spectrum_acl.o spectrum_flower.o \ + spectrum_cnt.o mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB) += spectrum_dcb.o obj-$(CONFIG_MLXSW_MINIMAL) += mlxsw_minimal.o mlxsw_minimal-objs := minimal.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h index bce8c2e00630..15b808693f00 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/resources.h +++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h @@ -43,6 +43,7 @@ enum mlxsw_res_id { MLXSW_RES_ID_KVD_SINGLE_MIN_SIZE, MLXSW_RES_ID_KVD_DOUBLE_MIN_SIZE, MLXSW_RES_ID_MAX_TRAP_GROUPS, + MLXSW_RES_ID_COUNTER_POOL_SIZE, MLXSW_RES_ID_MAX_SPAN, MLXSW_RES_ID_MAX_SYSTEM_PORT, MLXSW_RES_ID_MAX_LAG, @@ -75,6 +76,7 @@ static u16 mlxsw_res_ids[] = { [MLXSW_RES_ID_KVD_SINGLE_MIN_SIZE] = 0x1002, [MLXSW_RES_ID_KVD_DOUBLE_MIN_SIZE] = 0x1003, [MLXSW_RES_ID_MAX_TRAP_GROUPS] = 0x2201, + [MLXSW_RES_ID_COUNTER_POOL_SIZE] = 0x2410, [MLXSW_RES_ID_MAX_SPAN] = 0x2420, [MLXSW_RES_ID_MAX_SYSTEM_PORT] = 0x2502, [MLXSW_RES_ID_MAX_LAG] = 0x2520, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 2104ee47e965..b8237f98d470 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -66,6 +66,7 @@ #include "port.h" #include "trap.h" #include "txheader.h" +#include "spectrum_cnt.h" static const char mlxsw_sp_driver_name[] = "mlxsw_spectrum"; static const char mlxsw_sp_driver_version[] = "1.0"; @@ -3224,6 +3225,12 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, goto err_acl_init; } + err = mlxsw_sp_counter_pool_init(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to init counter pool\n"); + goto err_counter_pool_init; + } + err = mlxsw_sp_ports_create(mlxsw_sp); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n"); @@ -3233,6 +3240,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, return 0; err_ports_create: + mlxsw_sp_counter_pool_fini(mlxsw_sp); +err_counter_pool_init: mlxsw_sp_acl_fini(mlxsw_sp); err_acl_init: mlxsw_sp_span_fini(mlxsw_sp); @@ -3255,6 +3264,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core) struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); mlxsw_sp_ports_remove(mlxsw_sp); + mlxsw_sp_counter_pool_fini(mlxsw_sp); mlxsw_sp_acl_fini(mlxsw_sp); mlxsw_sp_span_fini(mlxsw_sp); mlxsw_sp_router_fini(mlxsw_sp); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 3bc1b0998654..dd59a9bf9085 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -246,6 +246,7 @@ struct mlxsw_sp_router { }; struct mlxsw_sp_acl; +struct mlxsw_sp_counter_pool; struct mlxsw_sp { struct { @@ -281,6 +282,7 @@ struct mlxsw_sp { DECLARE_BITMAP(usage, MLXSW_SP_KVD_LINEAR_SIZE); } kvdl; + struct mlxsw_sp_counter_pool *counter_pool; struct { struct mlxsw_sp_span_entry *entries; int entries_count; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c new file mode 100644 index 000000000000..0cae75c415d4 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c @@ -0,0 +1,177 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. + * Copyright (c) 2017 Arkadi Sharshevsky + * + * 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_cnt.h" + +#define MLXSW_SP_COUNTER_POOL_BANK_SIZE 4096 + +struct mlxsw_sp_counter_sub_pool { + unsigned int base_index; + unsigned int size; + unsigned int entry_size; + unsigned int bank_count; +}; + +struct mlxsw_sp_counter_pool { + unsigned int pool_size; + unsigned long *usage; /* Usage bitmap */ + struct mlxsw_sp_counter_sub_pool *sub_pools; +}; + +static struct mlxsw_sp_counter_sub_pool mlxsw_sp_counter_sub_pools[] = {}; + +static int mlxsw_sp_counter_pool_validate(struct mlxsw_sp *mlxsw_sp) +{ + unsigned int total_bank_config = 0; + unsigned int pool_size; + int i; + + pool_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, COUNTER_POOL_SIZE); + /* Check config is valid, no bank over subscription */ + for (i = 0; i < ARRAY_SIZE(mlxsw_sp_counter_sub_pools); i++) + total_bank_config += mlxsw_sp_counter_sub_pools[i].bank_count; + if (total_bank_config > pool_size / MLXSW_SP_COUNTER_POOL_BANK_SIZE + 1) + return -EINVAL; + return 0; +} + +int mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp) +{ + struct mlxsw_sp_counter_sub_pool *sub_pool; + struct mlxsw_sp_counter_pool *pool; + unsigned int base_index; + unsigned int map_size; + int i; + int err; + + if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, COUNTER_POOL_SIZE)) + return -EIO; + + err = mlxsw_sp_counter_pool_validate(mlxsw_sp); + if (err) + return err; + + pool = kzalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) + return -ENOMEM; + + pool->pool_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, COUNTER_POOL_SIZE); + map_size = BITS_TO_LONGS(pool->pool_size) * sizeof(unsigned long); + + pool->usage = kzalloc(map_size, GFP_KERNEL); + if (!pool->usage) { + err = -ENOMEM; + goto err_usage_alloc; + } + + pool->sub_pools = mlxsw_sp_counter_sub_pools; + /* Allocation is based on bank count which should be + * specified for each sub pool statically. + */ + base_index = 0; + for (i = 0; i < ARRAY_SIZE(mlxsw_sp_counter_sub_pools); i++) { + sub_pool = &pool->sub_pools[i]; + sub_pool->size = sub_pool->bank_count * + MLXSW_SP_COUNTER_POOL_BANK_SIZE; + sub_pool->base_index = base_index; + base_index += sub_pool->size; + /* The last bank can't be fully used */ + if (sub_pool->base_index + sub_pool->size > pool->pool_size) + sub_pool->size = pool->pool_size - sub_pool->base_index; + } + + mlxsw_sp->counter_pool = pool; + return 0; + +err_usage_alloc: + kfree(pool); + return err; +} + +void mlxsw_sp_counter_pool_fini(struct mlxsw_sp *mlxsw_sp) +{ + struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; + + WARN_ON(find_first_bit(pool->usage, pool->pool_size) != + pool->pool_size); + kfree(pool->usage); + kfree(pool); +} + +int mlxsw_sp_counter_alloc(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_counter_sub_pool_id sub_pool_id, + unsigned int *p_counter_index) +{ + struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; + struct mlxsw_sp_counter_sub_pool *sub_pool; + unsigned int entry_index; + unsigned int stop_index; + int i; + + sub_pool = &mlxsw_sp_counter_sub_pools[sub_pool_id]; + stop_index = sub_pool->base_index + sub_pool->size; + entry_index = sub_pool->base_index; + + entry_index = find_next_zero_bit(pool->usage, stop_index, entry_index); + if (entry_index == stop_index) + return -ENOBUFS; + /* The sub-pools can contain non-integer number of entries + * so we must check for overflow + */ + if (entry_index + sub_pool->entry_size > stop_index) + return -ENOBUFS; + for (i = 0; i < sub_pool->entry_size; i++) + __set_bit(entry_index + i, pool->usage); + + *p_counter_index = entry_index; + return 0; +} + +void mlxsw_sp_counter_free(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_counter_sub_pool_id sub_pool_id, + unsigned int counter_index) +{ + struct mlxsw_sp_counter_pool *pool = mlxsw_sp->counter_pool; + struct mlxsw_sp_counter_sub_pool *sub_pool; + int i; + + if (WARN_ON(counter_index >= pool->pool_size)) + return; + sub_pool = &mlxsw_sp_counter_sub_pools[sub_pool_id]; + for (i = 0; i < sub_pool->entry_size; i++) + __clear_bit(counter_index + i, pool->usage); +} diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h new file mode 100644 index 000000000000..5bf42d923edf --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h @@ -0,0 +1,54 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. + * Copyright (c) 2017 Arkadi Sharshevsky + * + * 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_CNT_H +#define _MLXSW_SPECTRUM_CNT_H + +#include "spectrum.h" + +enum mlxsw_sp_counter_sub_pool_id { + /* Placeholder before first pool registered */ + MLXSW_SP_COUNTER_SUB_POOL_PLACEHOLDER, +}; + +int mlxsw_sp_counter_alloc(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_counter_sub_pool_id sub_pool_id, + unsigned int *p_counter_index); +void mlxsw_sp_counter_free(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_counter_sub_pool_id sub_pool_id, + unsigned int counter_index); +int mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp); +void mlxsw_sp_counter_pool_fini(struct mlxsw_sp *mlxsw_sp); + +#endif -- cgit From 5766532abcee1e06897f3e1865c763f5d6eac495 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sat, 11 Mar 2017 09:42:52 +0100 Subject: mlxsw: reg: Add Monitoring General Purpose Counter Set register The MGPC register retrieves generic flow counter value. It will be used to query ACL counters. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 65 +++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index eb94a5a4625d..393743c08650 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5506,6 +5506,70 @@ static inline void mlxsw_reg_mpsc_pack(char *payload, u8 local_port, bool e, mlxsw_reg_mpsc_rate_set(payload, rate); } +/* MGPC - Monitoring General Purpose Counter Set Register + * The MGPC register retrieves and sets the General Purpose Counter Set. + */ +#define MLXSW_REG_MGPC_ID 0x9081 +#define MLXSW_REG_MGPC_LEN 0x18 + +MLXSW_REG_DEFINE(mgpc, MLXSW_REG_MGPC_ID, MLXSW_REG_MGPC_LEN); + +enum mlxsw_reg_mgpc_counter_set_type { + /* No count */ + MLXSW_REG_MGPC_COUNTER_SET_TYPE_NO_COUT = 0x00, + /* Count packets and bytes */ + MLXSW_REG_MGPC_COUNTER_SET_TYPE_PACKETS_BYTES = 0x03, + /* Count only packets */ + MLXSW_REG_MGPC_COUNTER_SET_TYPE_PACKETS = 0x05, +}; + +/* reg_mgpc_counter_set_type + * Counter set type. + * Access: OP + */ +MLXSW_ITEM32(reg, mgpc, counter_set_type, 0x00, 24, 8); + +/* reg_mgpc_counter_index + * Counter index. + * Access: Index + */ +MLXSW_ITEM32(reg, mgpc, counter_index, 0x00, 0, 24); + +enum mlxsw_reg_mgpc_opcode { + /* Nop */ + MLXSW_REG_MGPC_OPCODE_NOP = 0x00, + /* Clear counters */ + MLXSW_REG_MGPC_OPCODE_CLEAR = 0x08, +}; + +/* reg_mgpc_opcode + * Opcode. + * Access: OP + */ +MLXSW_ITEM32(reg, mgpc, opcode, 0x04, 28, 4); + +/* reg_mgpc_byte_counter + * Byte counter value. + * Access: RW + */ +MLXSW_ITEM64(reg, mgpc, byte_counter, 0x08, 0, 64); + +/* reg_mgpc_packet_counter + * Packet counter value. + * Access: RW + */ +MLXSW_ITEM64(reg, mgpc, packet_counter, 0x10, 0, 64); + +static inline void mlxsw_reg_mgpc_pack(char *payload, u32 counter_index, + enum mlxsw_reg_mgpc_opcode opcode, + enum mlxsw_reg_mgpc_counter_set_type set_type) +{ + MLXSW_REG_ZERO(mgpc, payload); + mlxsw_reg_mgpc_counter_index_set(payload, counter_index); + mlxsw_reg_mgpc_counter_set_type_set(payload, set_type); + mlxsw_reg_mgpc_opcode_set(payload, opcode); +} + /* SBPR - Shared Buffer Pools Register * ----------------------------------- * The SBPR configures and retrieves the shared buffer pools and configuration. @@ -5979,6 +6043,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(mpar), MLXSW_REG(mlcr), MLXSW_REG(mpsc), + MLXSW_REG(mgpc), MLXSW_REG(sbpr), MLXSW_REG(sbcm), MLXSW_REG(sbpm), -- cgit From 1abcbcc292ee6c66e659b2a7a5e84e5d151e9334 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sat, 11 Mar 2017 09:42:53 +0100 Subject: mlxsw: spectrum: Add support for generic flow counter allocation Add support for allocating generic flow counter. Generic flow counter can count packets or packets and bytes and can be assigned to different hardware processes. First use will be for counting packets and bytes of ACL rules, and will be introduced in the following patches. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/resources.h | 2 + drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 54 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 7 +++ drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c | 23 ++++++++- drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h | 3 +- 5 files changed, 86 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h index 15b808693f00..905a8e269f87 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/resources.h +++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h @@ -45,6 +45,7 @@ enum mlxsw_res_id { MLXSW_RES_ID_MAX_TRAP_GROUPS, MLXSW_RES_ID_COUNTER_POOL_SIZE, MLXSW_RES_ID_MAX_SPAN, + MLXSW_RES_ID_COUNTER_SIZE_PACKETS_BYTES, MLXSW_RES_ID_MAX_SYSTEM_PORT, MLXSW_RES_ID_MAX_LAG, MLXSW_RES_ID_MAX_LAG_MEMBERS, @@ -78,6 +79,7 @@ static u16 mlxsw_res_ids[] = { [MLXSW_RES_ID_MAX_TRAP_GROUPS] = 0x2201, [MLXSW_RES_ID_COUNTER_POOL_SIZE] = 0x2410, [MLXSW_RES_ID_MAX_SPAN] = 0x2420, + [MLXSW_RES_ID_COUNTER_SIZE_PACKETS_BYTES] = 0x2443, [MLXSW_RES_ID_MAX_SYSTEM_PORT] = 0x2502, [MLXSW_RES_ID_MAX_LAG] = 0x2520, [MLXSW_RES_ID_MAX_LAG_MEMBERS] = 0x2521, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index b8237f98d470..af430484ed2f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -139,6 +139,60 @@ MLXSW_ITEM32(tx, hdr, fid, 0x08, 0, 16); */ MLXSW_ITEM32(tx, hdr, type, 0x0C, 0, 4); +int mlxsw_sp_flow_counter_get(struct mlxsw_sp *mlxsw_sp, + unsigned int counter_index, u64 *packets, + u64 *bytes) +{ + char mgpc_pl[MLXSW_REG_MGPC_LEN]; + int err; + + mlxsw_reg_mgpc_pack(mgpc_pl, counter_index, MLXSW_REG_MGPC_OPCODE_NOP, + MLXSW_REG_MGPC_COUNTER_SET_TYPE_PACKETS_BYTES); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mgpc), mgpc_pl); + if (err) + return err; + *packets = mlxsw_reg_mgpc_packet_counter_get(mgpc_pl); + *bytes = mlxsw_reg_mgpc_byte_counter_get(mgpc_pl); + return 0; +} + +static int mlxsw_sp_flow_counter_clear(struct mlxsw_sp *mlxsw_sp, + unsigned int counter_index) +{ + char mgpc_pl[MLXSW_REG_MGPC_LEN]; + + mlxsw_reg_mgpc_pack(mgpc_pl, counter_index, MLXSW_REG_MGPC_OPCODE_CLEAR, + MLXSW_REG_MGPC_COUNTER_SET_TYPE_PACKETS_BYTES); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mgpc), mgpc_pl); +} + +int mlxsw_sp_flow_counter_alloc(struct mlxsw_sp *mlxsw_sp, + unsigned int *p_counter_index) +{ + int err; + + err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_FLOW, + p_counter_index); + if (err) + return err; + err = mlxsw_sp_flow_counter_clear(mlxsw_sp, *p_counter_index); + if (err) + goto err_counter_clear; + return 0; + +err_counter_clear: + mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_FLOW, + *p_counter_index); + return err; +} + +void mlxsw_sp_flow_counter_free(struct mlxsw_sp *mlxsw_sp, + unsigned int counter_index) +{ + mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_FLOW, + counter_index); +} + static void mlxsw_sp_txhdr_construct(struct sk_buff *skb, const struct mlxsw_tx_info *tx_info) { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index dd59a9bf9085..b0500b878fb4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -679,5 +679,12 @@ int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, __be16 protocol, struct tc_cls_flower_offload *f); void mlxsw_sp_flower_destroy(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, struct tc_cls_flower_offload *f); +int mlxsw_sp_flow_counter_get(struct mlxsw_sp *mlxsw_sp, + unsigned int counter_index, u64 *packets, + u64 *bytes); +int mlxsw_sp_flow_counter_alloc(struct mlxsw_sp *mlxsw_sp, + unsigned int *p_counter_index); +void mlxsw_sp_flow_counter_free(struct mlxsw_sp *mlxsw_sp, + unsigned int counter_index); #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c index 0cae75c415d4..1631e01908c0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c @@ -52,7 +52,11 @@ struct mlxsw_sp_counter_pool { struct mlxsw_sp_counter_sub_pool *sub_pools; }; -static struct mlxsw_sp_counter_sub_pool mlxsw_sp_counter_sub_pools[] = {}; +static struct mlxsw_sp_counter_sub_pool mlxsw_sp_counter_sub_pools[] = { + [MLXSW_SP_COUNTER_SUB_POOL_FLOW] = { + .bank_count = 6, + }, +}; static int mlxsw_sp_counter_pool_validate(struct mlxsw_sp *mlxsw_sp) { @@ -69,6 +73,19 @@ static int mlxsw_sp_counter_pool_validate(struct mlxsw_sp *mlxsw_sp) return 0; } +static int mlxsw_sp_counter_sub_pools_prepare(struct mlxsw_sp *mlxsw_sp) +{ + struct mlxsw_sp_counter_sub_pool *sub_pool; + + /* Prepare generic flow pool*/ + sub_pool = &mlxsw_sp_counter_sub_pools[MLXSW_SP_COUNTER_SUB_POOL_FLOW]; + if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, COUNTER_SIZE_PACKETS_BYTES)) + return -EIO; + sub_pool->entry_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, + COUNTER_SIZE_PACKETS_BYTES); + return 0; +} + int mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp) { struct mlxsw_sp_counter_sub_pool *sub_pool; @@ -85,6 +102,10 @@ int mlxsw_sp_counter_pool_init(struct mlxsw_sp *mlxsw_sp) if (err) return err; + err = mlxsw_sp_counter_sub_pools_prepare(mlxsw_sp); + if (err) + return err; + pool = kzalloc(sizeof(*pool), GFP_KERNEL); if (!pool) return -ENOMEM; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h index 5bf42d923edf..031bc4abbe2d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h @@ -38,8 +38,7 @@ #include "spectrum.h" enum mlxsw_sp_counter_sub_pool_id { - /* Placeholder before first pool registered */ - MLXSW_SP_COUNTER_SUB_POOL_PLACEHOLDER, + MLXSW_SP_COUNTER_SUB_POOL_FLOW, }; int mlxsw_sp_counter_alloc(struct mlxsw_sp *mlxsw_sp, -- cgit From 7fd056c2ef50088b891f2044af52bbb28699f4e6 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sat, 11 Mar 2017 09:42:54 +0100 Subject: mlxsw: spectrum_acl_tcam: Add support for retrieving TCAM entry activity Add support for retrieving TCAM entry activity. In order to support ACL rule activity corresponding TCAM entry should be queried. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 2 ++ .../ethernet/mellanox/mlxsw/spectrum_acl_tcam.c | 42 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index b0500b878fb4..26718b167352 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -607,6 +607,8 @@ struct mlxsw_sp_acl_profile_ops { void *ruleset_priv, void *rule_priv, struct mlxsw_sp_acl_rule_info *rulei); void (*rule_del)(struct mlxsw_sp *mlxsw_sp, void *rule_priv); + int (*rule_activity_get)(struct mlxsw_sp *mlxsw_sp, void *rule_priv, + bool *activity); }; struct mlxsw_sp_acl_ops { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c index 6858439a1319..3a24289979d9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c @@ -561,6 +561,24 @@ mlxsw_sp_acl_tcam_region_entry_remove(struct mlxsw_sp *mlxsw_sp, mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptce2), ptce2_pl); } +static int +mlxsw_sp_acl_tcam_region_entry_activity_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_tcam_region *region, + unsigned int offset, + bool *activity) +{ + char ptce2_pl[MLXSW_REG_PTCE2_LEN]; + int err; + + mlxsw_reg_ptce2_pack(ptce2_pl, true, MLXSW_REG_PTCE2_OP_QUERY_CLEAR_ON_READ, + region->tcam_region_info, offset); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptce2), ptce2_pl); + if (err) + return err; + *activity = mlxsw_reg_ptce2_a_get(ptce2_pl); + return 0; +} + #define MLXSW_SP_ACL_TCAM_CATCHALL_PRIO (~0U) static int @@ -940,6 +958,19 @@ static void mlxsw_sp_acl_tcam_entry_del(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_acl_tcam_chunk_put(mlxsw_sp, chunk); } +static int +mlxsw_sp_acl_tcam_entry_activity_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_tcam_entry *entry, + bool *activity) +{ + struct mlxsw_sp_acl_tcam_chunk *chunk = entry->chunk; + struct mlxsw_sp_acl_tcam_region *region = chunk->region; + + return mlxsw_sp_acl_tcam_region_entry_activity_get(mlxsw_sp, region, + entry->parman_item.index, + activity); +} + static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv4[] = { MLXSW_AFK_ELEMENT_SRC_SYS_PORT, MLXSW_AFK_ELEMENT_DMAC, @@ -1048,6 +1079,16 @@ mlxsw_sp_acl_tcam_flower_rule_del(struct mlxsw_sp *mlxsw_sp, void *rule_priv) mlxsw_sp_acl_tcam_entry_del(mlxsw_sp, &rule->entry); } +static int +mlxsw_sp_acl_tcam_flower_rule_activity_get(struct mlxsw_sp *mlxsw_sp, + void *rule_priv, bool *activity) +{ + struct mlxsw_sp_acl_tcam_flower_rule *rule = rule_priv; + + return mlxsw_sp_acl_tcam_entry_activity_get(mlxsw_sp, &rule->entry, + activity); +} + static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_flower_ops = { .ruleset_priv_size = sizeof(struct mlxsw_sp_acl_tcam_flower_ruleset), .ruleset_add = mlxsw_sp_acl_tcam_flower_ruleset_add, @@ -1057,6 +1098,7 @@ static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_flower_ops = { .rule_priv_size = sizeof(struct mlxsw_sp_acl_tcam_flower_rule), .rule_add = mlxsw_sp_acl_tcam_flower_rule_add, .rule_del = mlxsw_sp_acl_tcam_flower_rule_del, + .rule_activity_get = mlxsw_sp_acl_tcam_flower_rule_activity_get, }; static const struct mlxsw_sp_acl_profile_ops * -- cgit From 096e914f69dcb07ee6f82f8507325e9668979395 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sat, 11 Mar 2017 09:42:55 +0100 Subject: mlxsw: spectrum: Add support for direct rule access Currently the ACL rules can be accessed only by hashing. In order to dump the activity the rules are also placed in a list. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index 3c5ea7e41db0..1888092471ea 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -54,6 +54,7 @@ struct mlxsw_sp_acl { struct mlxsw_afa *afa; const struct mlxsw_sp_acl_ops *ops; struct rhashtable ruleset_ht; + struct list_head rules; unsigned long priv[0]; /* priv has to be always the last item */ }; @@ -80,6 +81,7 @@ struct mlxsw_sp_acl_ruleset { struct mlxsw_sp_acl_rule { struct rhash_head ht_node; /* Member of rule HT */ + struct list_head list; unsigned long cookie; /* HT key */ struct mlxsw_sp_acl_ruleset *ruleset; struct mlxsw_sp_acl_rule_info *rulei; @@ -422,6 +424,7 @@ int mlxsw_sp_acl_rule_add(struct mlxsw_sp *mlxsw_sp, if (err) goto err_rhashtable_insert; + list_add_tail(&rule->list, &mlxsw_sp->acl->rules); return 0; err_rhashtable_insert: @@ -435,6 +438,7 @@ void mlxsw_sp_acl_rule_del(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset; const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops; + list_del(&rule->list); rhashtable_remove_fast(&ruleset->rule_ht, &rule->ht_node, mlxsw_sp_acl_rule_ht_params); ops->rule_del(mlxsw_sp, rule->priv); @@ -570,6 +574,7 @@ int mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp) if (err) goto err_rhashtable_init; + INIT_LIST_HEAD(&acl->rules); err = acl_ops->init(mlxsw_sp, acl->priv); if (err) goto err_acl_ops_init; @@ -594,6 +599,7 @@ void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp) const struct mlxsw_sp_acl_ops *acl_ops = acl->ops; acl_ops->fini(mlxsw_sp, acl->priv); + WARN_ON(!list_empty(&acl->rules)); rhashtable_destroy(&acl->ruleset_ht); mlxsw_afa_destroy(acl->afa); mlxsw_afk_destroy(acl->afk); -- cgit From 446a154187b9d9a35f97f9d981501d6b55d8d6ed Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sat, 11 Mar 2017 09:42:56 +0100 Subject: mlxsw: spectrum: Add periodic ACL rule activity update Introduce periodic task for dumping the activity status for the ACL rule TCAM entries. This is done in order to emulate last use statistics. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 74 +++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index 1888092471ea..d2f6cbb6a0c6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -50,11 +50,17 @@ #include "spectrum_acl_flex_keys.h" struct mlxsw_sp_acl { + struct mlxsw_sp *mlxsw_sp; struct mlxsw_afk *afk; struct mlxsw_afa *afa; const struct mlxsw_sp_acl_ops *ops; struct rhashtable ruleset_ht; struct list_head rules; + struct { + struct delayed_work dw; + unsigned long interval; /* ms */ +#define MLXSW_SP_ACL_RULE_ACTIVITY_UPDATE_PERIOD_MS 1000 + } rule_activity_update; unsigned long priv[0]; /* priv has to be always the last item */ }; @@ -85,6 +91,7 @@ struct mlxsw_sp_acl_rule { unsigned long cookie; /* HT key */ struct mlxsw_sp_acl_ruleset *ruleset; struct mlxsw_sp_acl_rule_info *rulei; + u64 last_used; unsigned long priv[0]; /* priv has to be always the last item */ }; @@ -459,6 +466,64 @@ mlxsw_sp_acl_rule_rulei(struct mlxsw_sp_acl_rule *rule) return rule->rulei; } +static int mlxsw_sp_acl_rule_activity_update(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule *rule) +{ + struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset; + const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops; + bool active; + int err; + + err = ops->rule_activity_get(mlxsw_sp, rule->priv, &active); + if (err) + return err; + if (active) + rule->last_used = jiffies; + return 0; +} + +static int mlxsw_sp_acl_rules_activity_update(struct mlxsw_sp_acl *acl) +{ + struct mlxsw_sp_acl_rule *rule; + int err; + + /* Protect internal structures from changes */ + rtnl_lock(); + list_for_each_entry(rule, &acl->rules, list) { + err = mlxsw_sp_acl_rule_activity_update(acl->mlxsw_sp, + rule); + if (err) + goto err_rule_update; + } + rtnl_unlock(); + return 0; + +err_rule_update: + rtnl_unlock(); + return err; +} + +static void mlxsw_sp_acl_rule_activity_work_schedule(struct mlxsw_sp_acl *acl) +{ + unsigned long interval = acl->rule_activity_update.interval; + + mlxsw_core_schedule_dw(&acl->rule_activity_update.dw, + msecs_to_jiffies(interval)); +} + +static void mlxsw_sp_acl_rul_activity_update_work(struct work_struct *work) +{ + struct mlxsw_sp_acl *acl = container_of(work, struct mlxsw_sp_acl, + rule_activity_update.dw.work); + int err; + + err = mlxsw_sp_acl_rules_activity_update(acl); + if (err) + dev_err(acl->mlxsw_sp->bus_info->dev, "Could not update acl activity"); + + mlxsw_sp_acl_rule_activity_work_schedule(acl); +} + #define MLXSW_SP_KDVL_ACT_EXT_SIZE 1 static int mlxsw_sp_act_kvdl_set_add(void *priv, u32 *p_kvdl_index, @@ -551,7 +616,7 @@ int mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp) if (!acl) return -ENOMEM; mlxsw_sp->acl = acl; - + acl->mlxsw_sp = mlxsw_sp; acl->afk = mlxsw_afk_create(MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_FLEX_KEYS), mlxsw_sp_afk_blocks, @@ -580,6 +645,12 @@ int mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp) goto err_acl_ops_init; acl->ops = acl_ops; + + /* Create the delayed work for the rule activity_update */ + INIT_DELAYED_WORK(&acl->rule_activity_update.dw, + mlxsw_sp_acl_rul_activity_update_work); + acl->rule_activity_update.interval = MLXSW_SP_ACL_RULE_ACTIVITY_UPDATE_PERIOD_MS; + mlxsw_core_schedule_dw(&acl->rule_activity_update.dw, 0); return 0; err_acl_ops_init: @@ -598,6 +669,7 @@ void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp) struct mlxsw_sp_acl *acl = mlxsw_sp->acl; const struct mlxsw_sp_acl_ops *acl_ops = acl->ops; + cancel_delayed_work_sync(&mlxsw_sp->acl->rule_activity_update.dw); acl_ops->fini(mlxsw_sp, acl->priv); WARN_ON(!list_empty(&acl->rules)); rhashtable_destroy(&acl->ruleset_ht); -- cgit From 938ab60860e81f123460b56b8463d6b5d6c51ac7 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sat, 11 Mar 2017 09:42:57 +0100 Subject: mlxsw: spectrum: Add support for Policing and Counting action block Add support for Policing and Counting action block. This action block will be used to bind counter to TCAM entries. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../mellanox/mlxsw/core_acl_flex_actions.c | 51 ++++++++++++++++++++++ .../mellanox/mlxsw/core_acl_flex_actions.h | 2 + 2 files changed, 53 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c index fe3c6ea16a99..a984c361926c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c @@ -760,3 +760,54 @@ err_append_action: return err; } EXPORT_SYMBOL(mlxsw_afa_block_append_fwd); + +/* Policing and Counting Action + * ---------------------------- + * Policing and Counting action is used for binding policer and counter + * to ACL rules. + */ + +#define MLXSW_AFA_POLCNT_CODE 0x08 +#define MLXSW_AFA_POLCNT_SIZE 1 + +enum mlxsw_afa_polcnt_counter_set_type { + /* No count */ + MLXSW_AFA_POLCNT_COUNTER_SET_TYPE_NO_COUNT = 0x00, + /* Count packets and bytes */ + MLXSW_AFA_POLCNT_COUNTER_SET_TYPE_PACKETS_BYTES = 0x03, + /* Count only packets */ + MLXSW_AFA_POLCNT_COUNTER_SET_TYPE_PACKETS = 0x05, +}; + +/* afa_polcnt_counter_set_type + * Counter set type for flow counters. + */ +MLXSW_ITEM32(afa, polcnt, counter_set_type, 0x04, 24, 8); + +/* afa_polcnt_counter_index + * Counter index for flow counters. + */ +MLXSW_ITEM32(afa, polcnt, counter_index, 0x04, 0, 24); + +static inline void +mlxsw_afa_polcnt_pack(char *payload, + enum mlxsw_afa_polcnt_counter_set_type set_type, + u32 counter_index) +{ + mlxsw_afa_polcnt_counter_set_type_set(payload, set_type); + mlxsw_afa_polcnt_counter_index_set(payload, counter_index); +} + +int mlxsw_afa_block_append_counter(struct mlxsw_afa_block *block, + u32 counter_index) +{ + char *act = mlxsw_afa_block_append_action(block, + MLXSW_AFA_POLCNT_CODE, + MLXSW_AFA_POLCNT_SIZE); + if (!act) + return -ENOBUFS; + mlxsw_afa_polcnt_pack(act, MLXSW_AFA_POLCNT_COUNTER_SET_TYPE_PACKETS_BYTES, + counter_index); + return 0; +} +EXPORT_SYMBOL(mlxsw_afa_block_append_counter); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h index 6e103ac41d99..a03362c1ef32 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h @@ -64,5 +64,7 @@ int mlxsw_afa_block_append_fwd(struct mlxsw_afa_block *block, u8 local_port, bool in_port); int mlxsw_afa_block_append_vlan_modify(struct mlxsw_afa_block *block, u16 vid, u8 pcp, u8 et); +int mlxsw_afa_block_append_counter(struct mlxsw_afa_block *block, + u32 counter_index); #endif -- cgit From 4817072950ad38229991ab0019fa28fece39285b Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sat, 11 Mar 2017 09:42:58 +0100 Subject: mlxsw: spectrum: Add support for counters on TCAM entries Add support for packets and byte statistics on TCAM entries. The counters are allocated from the generic flow counters pool. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 7 +++++ drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 35 ++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 26718b167352..b4f32a6d0352 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -588,6 +588,8 @@ struct mlxsw_sp_acl_rule_info { unsigned int priority; struct mlxsw_afk_element_values values; struct mlxsw_afa_block *act_block; + unsigned int counter_index; + bool counter_valid; }; enum mlxsw_sp_acl_profile { @@ -652,6 +654,8 @@ int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp, int mlxsw_sp_acl_rulei_act_vlan(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_rule_info *rulei, u32 action, u16 vid, u16 proto, u8 prio); +int mlxsw_sp_acl_rulei_act_count(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei); struct mlxsw_sp_acl_rule; @@ -671,6 +675,9 @@ mlxsw_sp_acl_rule_lookup(struct mlxsw_sp *mlxsw_sp, unsigned long cookie); struct mlxsw_sp_acl_rule_info * mlxsw_sp_acl_rule_rulei(struct mlxsw_sp_acl_rule *rule); +int mlxsw_sp_acl_rule_get_stats(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule *rule, + u64 *packets, u64 *bytes, u64 *last_use); int mlxsw_sp_acl_init(struct mlxsw_sp *mlxsw_sp); void mlxsw_sp_acl_fini(struct mlxsw_sp *mlxsw_sp); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index d2f6cbb6a0c6..87d011171da9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -247,6 +247,27 @@ void mlxsw_sp_acl_ruleset_put(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset); } +static int +mlxsw_sp_acl_rulei_counter_alloc(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei) +{ + int err; + + err = mlxsw_sp_flow_counter_alloc(mlxsw_sp, &rulei->counter_index); + if (err) + return err; + rulei->counter_valid = true; + return 0; +} + +static void +mlxsw_sp_acl_rulei_counter_free(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei) +{ + rulei->counter_valid = false; + mlxsw_sp_flow_counter_free(mlxsw_sp, rulei->counter_index); +} + struct mlxsw_sp_acl_rule_info * mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl) { @@ -373,6 +394,13 @@ int mlxsw_sp_acl_rulei_act_vlan(struct mlxsw_sp *mlxsw_sp, } } +int mlxsw_sp_acl_rulei_act_count(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei) +{ + return mlxsw_afa_block_append_counter(rulei->act_block, + rulei->counter_index); +} + struct mlxsw_sp_acl_rule * mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset, @@ -396,8 +424,14 @@ mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp, err = PTR_ERR(rule->rulei); goto err_rulei_create; } + + err = mlxsw_sp_acl_rulei_counter_alloc(mlxsw_sp, rule->rulei); + if (err) + goto err_counter_alloc; return rule; +err_counter_alloc: + mlxsw_sp_acl_rulei_destroy(rule->rulei); err_rulei_create: kfree(rule); err_alloc: @@ -410,6 +444,7 @@ void mlxsw_sp_acl_rule_destroy(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_acl_ruleset *ruleset = rule->ruleset; + mlxsw_sp_acl_rulei_counter_free(mlxsw_sp, rule->rulei); mlxsw_sp_acl_rulei_destroy(rule->rulei); kfree(rule); mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset); -- cgit From 7c1b8eb175b69add8eac105409d2a59723574675 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sat, 11 Mar 2017 09:42:59 +0100 Subject: mlxsw: spectrum: Add support for TC flower offload statistics Add support for TC flower offload statistics including number of packets, bytes and last use timestamp. Currently the statistics are gathered on a per-rule basis. Signed-off-by: Arkadi Sharshvesky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 3 ++ drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 2 + drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 28 +++++++++++++ .../net/ethernet/mellanox/mlxsw/spectrum_flower.c | 49 ++++++++++++++++++++++ 4 files changed, 82 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index af430484ed2f..475499b6c989 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1434,6 +1434,9 @@ static int mlxsw_sp_setup_tc(struct net_device *dev, u32 handle, mlxsw_sp_flower_destroy(mlxsw_sp_port, ingress, tc->cls_flower); return 0; + case TC_CLSFLOWER_STATS: + return mlxsw_sp_flower_stats(mlxsw_sp_port, ingress, + tc->cls_flower); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index b4f32a6d0352..5502232b06cf 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -688,6 +688,8 @@ int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, __be16 protocol, struct tc_cls_flower_offload *f); void mlxsw_sp_flower_destroy(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, struct tc_cls_flower_offload *f); +int mlxsw_sp_flower_stats(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, + struct tc_cls_flower_offload *f); int mlxsw_sp_flow_counter_get(struct mlxsw_sp *mlxsw_sp, unsigned int counter_index, u64 *packets, u64 *bytes); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index 87d011171da9..4d6920d45026 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -92,6 +92,8 @@ struct mlxsw_sp_acl_rule { struct mlxsw_sp_acl_ruleset *ruleset; struct mlxsw_sp_acl_rule_info *rulei; u64 last_used; + u64 last_packets; + u64 last_bytes; unsigned long priv[0]; /* priv has to be always the last item */ }; @@ -559,6 +561,32 @@ static void mlxsw_sp_acl_rul_activity_update_work(struct work_struct *work) mlxsw_sp_acl_rule_activity_work_schedule(acl); } +int mlxsw_sp_acl_rule_get_stats(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule *rule, + u64 *packets, u64 *bytes, u64 *last_use) + +{ + struct mlxsw_sp_acl_rule_info *rulei; + u64 current_packets; + u64 current_bytes; + int err; + + rulei = mlxsw_sp_acl_rule_rulei(rule); + err = mlxsw_sp_flow_counter_get(mlxsw_sp, rulei->counter_index, + ¤t_packets, ¤t_bytes); + if (err) + return err; + + *packets = current_packets - rule->last_packets; + *bytes = current_bytes - rule->last_bytes; + *last_use = rule->last_used; + + rule->last_bytes = current_bytes; + rule->last_packets = current_packets; + + return 0; +} + #define MLXSW_SP_KDVL_ACT_EXT_SIZE 1 static int mlxsw_sp_act_kvdl_set_add(void *priv, u32 *p_kvdl_index, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index f2ed0b3d5718..28bc989371c6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -56,6 +56,11 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, if (tc_no_actions(exts)) return 0; + /* Count action is inserted first */ + err = mlxsw_sp_acl_rulei_act_count(mlxsw_sp, rulei); + if (err) + return err; + tcf_exts_to_list(exts, &actions); list_for_each_entry(a, &actions, list) { if (is_tcf_gact_shot(a)) { @@ -346,3 +351,47 @@ void mlxsw_sp_flower_destroy(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); } + +int mlxsw_sp_flower_stats(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, + struct tc_cls_flower_offload *f) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + struct mlxsw_sp_acl_ruleset *ruleset; + struct mlxsw_sp_acl_rule *rule; + struct tc_action *a; + LIST_HEAD(actions); + u64 packets; + u64 lastuse; + u64 bytes; + int err; + + ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, mlxsw_sp_port->dev, + ingress, + MLXSW_SP_ACL_PROFILE_FLOWER); + if (WARN_ON(IS_ERR(ruleset))) + return -EINVAL; + + rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie); + if (!rule) + return -EINVAL; + + err = mlxsw_sp_acl_rule_get_stats(mlxsw_sp, rule, &bytes, &packets, + &lastuse); + if (err) + goto err_rule_get_stats; + + preempt_disable(); + + tcf_exts_to_list(f->exts, &actions); + list_for_each_entry(a, &actions, list) + tcf_action_stats_update(a, bytes, packets, lastuse); + + preempt_enable(); + + mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); + return 0; + +err_rule_get_stats: + mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); + return err; +} -- cgit From 4d294af2cd932e0c1b2534137b143920c17dffd6 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:12:47 -0500 Subject: net: dsa: mv88e6xxx: add port mask helper Add a mv88e6xxx_port_mask() helper to get the bitmask of ports in a switch chip, that will be used in several features. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 5 +++++ drivers/net/dsa/mv88e6xxx/port.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 6033f2f6260a..166b513ff751 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -944,6 +944,11 @@ static inline unsigned int mv88e6xxx_num_ports(struct mv88e6xxx_chip *chip) return chip->info->num_ports; } +static inline u16 mv88e6xxx_port_mask(struct mv88e6xxx_chip *chip) +{ + return GENMASK(mv88e6xxx_num_ports(chip) - 1, 0); +} + int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val); int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val); int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 8875784c4718..9bb0f2134cba 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -539,7 +539,7 @@ int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map) { - const u16 mask = GENMASK(mv88e6xxx_num_ports(chip) - 1, 0); + const u16 mask = mv88e6xxx_port_mask(chip); u16 reg; int err; -- cgit From 720c6343ef67de926251b0c1b3bb0b76d2cff946 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:12:48 -0500 Subject: net: dsa: mv88e6xxx: move ATU ageing time setter Move the ATU ageing time setter code in a new global1_atu.c file, which will be extended in future patches to contains all consequent Global (1) ATU support code. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/Makefile | 1 + drivers/net/dsa/mv88e6xxx/chip.c | 31 ++---------------------- drivers/net/dsa/mv88e6xxx/global1.h | 3 +++ drivers/net/dsa/mv88e6xxx/global1_atu.c | 43 +++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 29 deletions(-) create mode 100644 drivers/net/dsa/mv88e6xxx/global1_atu.c diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile index c36be318de1a..31d37a90cec7 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o mv88e6xxx-objs := chip.o mv88e6xxx-objs += global1.o +mv88e6xxx-objs += global1_atu.o mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o mv88e6xxx-objs += port.o diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 03dc886ed3d6..b228b8491afc 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2697,33 +2697,6 @@ static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr) return 0; } -static int mv88e6xxx_g1_set_age_time(struct mv88e6xxx_chip *chip, - unsigned int msecs) -{ - const unsigned int coeff = chip->info->age_time_coeff; - const unsigned int min = 0x01 * coeff; - const unsigned int max = 0xff * coeff; - u8 age_time; - u16 val; - int err; - - if (msecs < min || msecs > max) - return -ERANGE; - - /* Round to nearest multiple of coeff */ - age_time = (msecs + coeff / 2) / coeff; - - err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val); - if (err) - return err; - - /* AgeTime is 11:4 bits */ - val &= ~0xff0; - val |= age_time << 4; - - return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val); -} - static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds, unsigned int ageing_time) { @@ -2731,7 +2704,7 @@ static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds, int err; mutex_lock(&chip->reg_lock); - err = mv88e6xxx_g1_set_age_time(chip, ageing_time); + err = mv88e6xxx_g1_atu_set_age_time(chip, ageing_time); mutex_unlock(&chip->reg_lock); return err; @@ -2783,7 +2756,7 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip) if (err) return err; - err = mv88e6xxx_g1_set_age_time(chip, 300000); + err = mv88e6xxx_g1_atu_set_age_time(chip, 300000); if (err) return err; diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 1aec7382c02d..b8d0fb519bab 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -38,4 +38,7 @@ int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); +int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip, + unsigned int msecs); + #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c new file mode 100644 index 000000000000..4d0ada9efc6d --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -0,0 +1,43 @@ +/* + * Marvell 88E6xxx Address Translation Unit (ATU) support + * + * Copyright (c) 2008 Marvell Semiconductor + * Copyright (c) 2017 Savoir-faire Linux, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "mv88e6xxx.h" +#include "global1.h" + +/* Offset 0x0A: ATU Control Register */ + +int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip, + unsigned int msecs) +{ + const unsigned int coeff = chip->info->age_time_coeff; + const unsigned int min = 0x01 * coeff; + const unsigned int max = 0xff * coeff; + u8 age_time; + u16 val; + int err; + + if (msecs < min || msecs > max) + return -ERANGE; + + /* Round to nearest multiple of coeff */ + age_time = (msecs + coeff / 2) / coeff; + + err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val); + if (err) + return err; + + /* AgeTime is 11:4 bits */ + val &= ~0xff0; + val |= age_time << 4; + + return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val); +} -- cgit From a2ac29d2cccdaa647b67355786d961196140491b Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:12:49 -0500 Subject: net: dsa: mv88e6xxx: add ATU setup helper Move the configuration of the default ageing time in a new mv88e6xxx_atu_setup function. That function will be extended later to contain all ATU related configuration bits. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index b228b8491afc..0ad8200f3321 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1306,6 +1306,11 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, netdev_err(ds->ports[port].netdev, "failed to update state\n"); } +static int mv88e6xxx_atu_setup(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g1_atu_set_age_time(chip, 300000); +} + static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) { struct mv88e6xxx_chip *chip = ds->priv; @@ -2756,10 +2761,6 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip) if (err) return err; - err = mv88e6xxx_g1_atu_set_age_time(chip, 300000); - if (err) - return err; - /* Clear all ATU entries */ err = _mv88e6xxx_atu_flush(chip, 0, true); if (err) @@ -2845,6 +2846,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) goto unlock; } + err = mv88e6xxx_atu_setup(chip); + if (err) + goto unlock; + /* Some generations have the configuration of sending reserved * management frames to the CPU in global2, others in * global1. Hence it does not fit the two setup functions -- cgit From ea698f4f70937f02961f2041db82d4baf6c986d6 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:12:50 -0500 Subject: net: dsa: mv88e6xxx: setup message ports All interconnectable Marvell switch chips have an ATU Learn2All feature which allows newly learnt addresses to be spanned on ports marked as "Message Port". This commit configures the DSA ports as Message Port. Note that this has no effect until the Learn2All feature is enabled. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 12 ++++++++---- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 1 + drivers/net/dsa/mv88e6xxx/port.c | 18 ++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 2 ++ 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 0ad8200f3321..5c1b5825fe6c 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2504,6 +2504,13 @@ static int mv88e6xxx_setup_port_normal(struct mv88e6xxx_chip *chip, int port) return chip->info->ops->port_set_egress_unknowns(chip, port, false); } +static int mv88e6xxx_setup_message_port(struct mv88e6xxx_chip *chip, int port) +{ + bool message = dsa_is_dsa_port(chip->ds, port); + + return mv88e6xxx_port_set_message_port(chip, port, message); +} + static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) { struct dsa_switch *ds = chip->ds; @@ -2658,10 +2665,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return err; } - /* Port Control 1: disable trunking, disable sending - * learning messages to this port. - */ - err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, 0x0000); + err = mv88e6xxx_setup_message_port(chip, port); if (err) return err; diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 166b513ff751..45205519b9fe 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -144,6 +144,7 @@ #define PORT_CONTROL_STATE_LEARNING 0x02 #define PORT_CONTROL_STATE_FORWARDING 0x03 #define PORT_CONTROL_1 0x05 +#define PORT_CONTROL_1_MESSAGE_PORT BIT(15) #define PORT_CONTROL_1_FID_11_4_MASK (0xff << 0) #define PORT_BASE_VLAN 0x06 #define PORT_BASE_VLAN_FID_3_0_MASK (0xf << 12) diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 9bb0f2134cba..40fe48188056 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -535,6 +535,24 @@ int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, /* Offset 0x05: Port Control 1 */ +int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port, + bool message_port) +{ + u16 val; + int err; + + err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &val); + if (err) + return err; + + if (message_port) + val |= PORT_CONTROL_1_MESSAGE_PORT; + else + val &= ~PORT_CONTROL_1_MESSAGE_PORT; + + return mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, val); +} + /* Offset 0x06: Port Based VLAN Map */ int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map) diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index c83cbb3f4491..4328d6f29d4d 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -64,6 +64,8 @@ int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, bool on); int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, u16 etype); +int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port, + bool message_port); int mv88e6165_port_jumbo_config(struct mv88e6xxx_chip *chip, int port); int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port); int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port); -- cgit From c3a7d4ade5a5ac5bec63382bda35a92629447efb Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:12:51 -0500 Subject: net: dsa: mv88e6xxx: enable ATU Learn2All The ATU Learn2All feature allows newly learnt addresses to be spanned on ports marked as "Message Port", currently all DSA ports. This commit enables this feature which is necessary and quite convenient for multi-chip switch fabrics. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 15 ++++++--------- drivers/net/dsa/mv88e6xxx/global1.h | 1 + drivers/net/dsa/mv88e6xxx/global1_atu.c | 17 +++++++++++++++++ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 5c1b5825fe6c..1ee0c05f624e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1308,6 +1308,12 @@ static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, static int mv88e6xxx_atu_setup(struct mv88e6xxx_chip *chip) { + int err; + + err = mv88e6xxx_g1_atu_set_learn2all(chip, true); + if (err) + return err; + return mv88e6xxx_g1_atu_set_age_time(chip, 300000); } @@ -2756,15 +2762,6 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip) if (err < 0) return err; - /* Set the default address aging time to 5 minutes, and - * enable address learn messages to be sent to all message - * ports. - */ - err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, - GLOBAL_ATU_CONTROL_LEARN2ALL); - if (err) - return err; - /* Clear all ATU entries */ err = _mv88e6xxx_atu_flush(chip, 0, true); if (err) diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index b8d0fb519bab..18322d05225a 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -38,6 +38,7 @@ int mv88e6095_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_set_cpu_port(struct mv88e6xxx_chip *chip, int port); int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); +int mv88e6xxx_g1_atu_set_learn2all(struct mv88e6xxx_chip *chip, bool learn2all); int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip, unsigned int msecs); diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index 4d0ada9efc6d..843a21e05f7b 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -15,6 +15,23 @@ /* Offset 0x0A: ATU Control Register */ +int mv88e6xxx_g1_atu_set_learn2all(struct mv88e6xxx_chip *chip, bool learn2all) +{ + u16 val; + int err; + + err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val); + if (err) + return err; + + if (learn2all) + val |= GLOBAL_ATU_CONTROL_LEARN2ALL; + else + val &= ~GLOBAL_ATU_CONTROL_LEARN2ALL; + + return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val); +} + int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip, unsigned int msecs) { -- cgit From 9c13c0267683733f549bb883220ab042028e1c7d Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:12:52 -0500 Subject: net: dsa: mv88e6xxx: rework ATU Load/Purge All Marvell switch chips have an ATU accessed using the same Global (1) register layout. Only the handling of the FID differs as more bits were necessary to support more and more databases. Add and use a fresh documented implementation of the ATU Load/Purge. The static mv88e6xxx_g1_atu_{fid_write,op_wait,op,data_write,mac_write} functions won't need to be exposed in the end so for the moment keep their counterparts _mv88e6xxx_atu_{wait,cmd,data_write,mac_write} as is, since they are still used by other ATU operations. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 22 +------ drivers/net/dsa/mv88e6xxx/global1.h | 2 + drivers/net/dsa/mv88e6xxx/global1_atu.c | 108 ++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 21 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 1ee0c05f624e..95dbf581b54a 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2045,26 +2045,6 @@ static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_chip *chip, return 0; } -static int _mv88e6xxx_atu_load(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_atu_entry *entry) -{ - int ret; - - ret = _mv88e6xxx_atu_wait(chip); - if (ret < 0) - return ret; - - ret = _mv88e6xxx_atu_mac_write(chip, entry->mac); - if (ret < 0) - return ret; - - ret = _mv88e6xxx_atu_data_write(chip, entry); - if (ret < 0) - return ret; - - return _mv88e6xxx_atu_cmd(chip, entry->fid, GLOBAL_ATU_OP_LOAD_DB); -} - static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, struct mv88e6xxx_atu_entry *entry); @@ -2132,7 +2112,7 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, entry.state = state; } - return _mv88e6xxx_atu_load(chip, &entry); + return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry); } static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 18322d05225a..2c03f2e04639 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -41,5 +41,7 @@ int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_atu_set_learn2all(struct mv88e6xxx_chip *chip, bool learn2all); int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip, unsigned int msecs); +int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid, + struct mv88e6xxx_atu_entry *entry); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index 843a21e05f7b..09190559178b 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -13,6 +13,13 @@ #include "mv88e6xxx.h" #include "global1.h" +/* Offset 0x01: ATU FID Register */ + +static int mv88e6xxx_g1_atu_fid_write(struct mv88e6xxx_chip *chip, u16 fid) +{ + return mv88e6xxx_g1_write(chip, GLOBAL_ATU_FID, fid & 0xfff); +} + /* Offset 0x0A: ATU Control Register */ int mv88e6xxx_g1_atu_set_learn2all(struct mv88e6xxx_chip *chip, bool learn2all) @@ -58,3 +65,104 @@ int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip, return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val); } + +/* Offset 0x0B: ATU Operation Register */ + +static int mv88e6xxx_g1_atu_op_wait(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g1_wait(chip, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY); +} + +static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op) +{ + u16 val; + int err; + + /* FID bits are dispatched all around gradually as more are supported */ + if (mv88e6xxx_num_databases(chip) > 256) { + err = mv88e6xxx_g1_atu_fid_write(chip, fid); + if (err) + return err; + } else { + if (mv88e6xxx_num_databases(chip) > 16) { + /* ATU DBNum[7:4] are located in ATU Control 15:12 */ + err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val); + if (err) + return err; + + val = (val & 0x0fff) | ((fid << 8) & 0xf000); + err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val); + if (err) + return err; + } + + /* ATU DBNum[3:0] are located in ATU Operation 3:0 */ + op |= fid & 0xf; + } + + err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_OP, op); + if (err) + return err; + + return mv88e6xxx_g1_atu_op_wait(chip); +} + +/* Offset 0x0C: ATU Data Register */ + +static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_atu_entry *entry) +{ + u16 data = entry->state & 0xf; + + if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) { + if (entry->trunk) + data |= GLOBAL_ATU_DATA_TRUNK; + + data |= (entry->portv_trunkid & mv88e6xxx_port_mask(chip)) << 4; + } + + return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data); +} + +/* Offset 0x0D: ATU MAC Address Register Bytes 0 & 1 + * Offset 0x0E: ATU MAC Address Register Bytes 2 & 3 + * Offset 0x0F: ATU MAC Address Register Bytes 4 & 5 + */ + +static int mv88e6xxx_g1_atu_mac_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_atu_entry *entry) +{ + u16 val; + int i, err; + + for (i = 0; i < 3; i++) { + val = (entry->mac[i * 2] << 8) | entry->mac[i * 2 + 1]; + err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i, val); + if (err) + return err; + } + + return 0; +} + +/* Address Translation Unit operations */ + +int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid, + struct mv88e6xxx_atu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_atu_op_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g1_atu_mac_write(chip, entry); + if (err) + return err; + + err = mv88e6xxx_g1_atu_data_write(chip, entry); + if (err) + return err; + + return mv88e6xxx_g1_atu_op(chip, fid, GLOBAL_ATU_OP_LOAD_DB); +} -- cgit From dabc1a968ae17bcd99c216f509be101dfdeac664 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:12:53 -0500 Subject: net: dsa: mv88e6xxx: rework ATU GetNext Add and use a fresh documented implementation of the ATU GetNext. Since it is not necessary to write the MAC address to iterate from, only do it once directly in the ATU GetNext operation, if the provided ATU entry structure is not valid. This makes the user code simpler. Also, there is no need to loop when getting a single ATU entry. So remove the mv88e6xxx_atu_get helper and add a simpler snippet in mv88e6xxx_port_db_load_purge to lookup a given MAC address. The _mv88e6xxx_atu_mac_{read,write} are not used anymore thus remove them. _mv88e6xxx_atu_data_{read,write} are still used so keep them. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 140 ++++---------------------------- drivers/net/dsa/mv88e6xxx/global1.h | 2 + drivers/net/dsa/mv88e6xxx/global1_atu.c | 66 +++++++++++++++ 3 files changed, 84 insertions(+), 124 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 95dbf581b54a..2d1ec0d79fb6 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2012,76 +2012,6 @@ unlock: return err; } -static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_chip *chip, - const unsigned char *addr) -{ - int i, err; - - for (i = 0; i < 3; i++) { - err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i, - (addr[i * 2] << 8) | addr[i * 2 + 1]); - if (err) - return err; - } - - return 0; -} - -static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_chip *chip, - unsigned char *addr) -{ - u16 val; - int i, err; - - for (i = 0; i < 3; i++) { - err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_MAC_01 + i, &val); - if (err) - return err; - - addr[i * 2] = val >> 8; - addr[i * 2 + 1] = val & 0xff; - } - - return 0; -} - -static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, - struct mv88e6xxx_atu_entry *entry); - -static int mv88e6xxx_atu_get(struct mv88e6xxx_chip *chip, int fid, - const u8 *addr, struct mv88e6xxx_atu_entry *entry) -{ - struct mv88e6xxx_atu_entry next; - int err; - - memcpy(next.mac, addr, ETH_ALEN); - eth_addr_dec(next.mac); - - err = _mv88e6xxx_atu_mac_write(chip, next.mac); - if (err) - return err; - - do { - err = _mv88e6xxx_atu_getnext(chip, fid, &next); - if (err) - return err; - - if (next.state == GLOBAL_ATU_DATA_STATE_UNUSED) - break; - - if (ether_addr_equal(next.mac, addr)) { - *entry = next; - return 0; - } - } while (ether_addr_greater(addr, next.mac)); - - memset(entry, 0, sizeof(*entry)); - entry->fid = fid; - ether_addr_copy(entry->mac, addr); - - return 0; -} - static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, const unsigned char *addr, u16 vid, u8 state) @@ -2098,10 +2028,21 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, if (err) return err; - err = mv88e6xxx_atu_get(chip, vlan.fid, addr, &entry); + entry.state = GLOBAL_ATU_DATA_STATE_UNUSED; + ether_addr_copy(entry.mac, addr); + eth_addr_dec(entry.mac); + + err = mv88e6xxx_g1_atu_getnext(chip, vlan.fid, &entry); if (err) return err; + /* Initialize a fresh ATU entry if it isn't found */ + if (entry.state == GLOBAL_ATU_DATA_STATE_UNUSED || + !ether_addr_equal(entry.mac, addr)) { + memset(&entry, 0, sizeof(entry)); + ether_addr_copy(entry.mac, addr); + } + /* Purge the ATU entry only if no port is using it anymore */ if (state == GLOBAL_ATU_DATA_STATE_UNUSED) { entry.portv_trunkid &= ~BIT(port); @@ -2152,68 +2093,19 @@ static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, return err; } -static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, - struct mv88e6xxx_atu_entry *entry) -{ - struct mv88e6xxx_atu_entry next = { 0 }; - u16 val; - int err; - - next.fid = fid; - - err = _mv88e6xxx_atu_wait(chip); - if (err) - return err; - - err = _mv88e6xxx_atu_cmd(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB); - if (err) - return err; - - err = _mv88e6xxx_atu_mac_read(chip, next.mac); - if (err) - return err; - - err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_DATA, &val); - if (err) - return err; - - next.state = val & GLOBAL_ATU_DATA_STATE_MASK; - if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) { - unsigned int mask, shift; - - if (val & GLOBAL_ATU_DATA_TRUNK) { - next.trunk = true; - mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK; - shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT; - } else { - next.trunk = false; - mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK; - shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT; - } - - next.portv_trunkid = (val & mask) >> shift; - } - - *entry = next; - return 0; -} - static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, u16 fid, u16 vid, int port, struct switchdev_obj *obj, int (*cb)(struct switchdev_obj *obj)) { - struct mv88e6xxx_atu_entry addr = { - .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, - }; + struct mv88e6xxx_atu_entry addr; int err; - err = _mv88e6xxx_atu_mac_write(chip, addr.mac); - if (err) - return err; + addr.state = GLOBAL_ATU_DATA_STATE_UNUSED; + eth_broadcast_addr(addr.mac); do { - err = _mv88e6xxx_atu_getnext(chip, fid, &addr); + err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr); if (err) return err; diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 2c03f2e04639..8dd8ecc9f064 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -41,6 +41,8 @@ int mv88e6390_g1_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_atu_set_learn2all(struct mv88e6xxx_chip *chip, bool learn2all); int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip, unsigned int msecs); +int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, + struct mv88e6xxx_atu_entry *entry); int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid, struct mv88e6xxx_atu_entry *entry); diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index 09190559178b..94e940522849 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -109,6 +109,27 @@ static int mv88e6xxx_g1_atu_op(struct mv88e6xxx_chip *chip, u16 fid, u16 op) /* Offset 0x0C: ATU Data Register */ +static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_atu_entry *entry) +{ + u16 val; + int err; + + err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_DATA, &val); + if (err) + return err; + + entry->state = val & 0xf; + if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) { + if (val & GLOBAL_ATU_DATA_TRUNK) + entry->trunk = true; + + entry->portv_trunkid = (val >> 4) & mv88e6xxx_port_mask(chip); + } + + return 0; +} + static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip, struct mv88e6xxx_atu_entry *entry) { @@ -129,6 +150,24 @@ static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip, * Offset 0x0F: ATU MAC Address Register Bytes 4 & 5 */ +static int mv88e6xxx_g1_atu_mac_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_atu_entry *entry) +{ + u16 val; + int i, err; + + for (i = 0; i < 3; i++) { + err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_MAC_01 + i, &val); + if (err) + return err; + + entry->mac[i * 2] = val >> 8; + entry->mac[i * 2 + 1] = val & 0xff; + } + + return 0; +} + static int mv88e6xxx_g1_atu_mac_write(struct mv88e6xxx_chip *chip, struct mv88e6xxx_atu_entry *entry) { @@ -147,6 +186,33 @@ static int mv88e6xxx_g1_atu_mac_write(struct mv88e6xxx_chip *chip, /* Address Translation Unit operations */ +int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, + struct mv88e6xxx_atu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_atu_op_wait(chip); + if (err) + return err; + + /* Write the MAC address to iterate from only once */ + if (entry->state == GLOBAL_ATU_DATA_STATE_UNUSED) { + err = mv88e6xxx_g1_atu_mac_write(chip, entry); + if (err) + return err; + } + + err = mv88e6xxx_g1_atu_op(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB); + if (err) + return err; + + err = mv88e6xxx_g1_atu_data_read(chip, entry); + if (err) + return err; + + return mv88e6xxx_g1_atu_mac_read(chip, entry); +} + int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid, struct mv88e6xxx_atu_entry *entry) { -- cgit From daefc94321ee146df2a750a4575bfbde98bad2d9 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:12:54 -0500 Subject: net: dsa: mv88e6xxx: rework ATU Flush Add a fresh documented implementation of the ATU Flush/Move operation. Use it to replace the current ATU Flush operation. _mv88e6xxx_atu_flush_move is still used by the Move operation so keep it until the Move operation is refactored in a next commit. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 22 +++++--------------- drivers/net/dsa/mv88e6xxx/global1.h | 1 + drivers/net/dsa/mv88e6xxx/global1_atu.c | 37 +++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 2d1ec0d79fb6..8b8c3cb167c1 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1210,17 +1210,6 @@ static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_chip *chip, return _mv88e6xxx_atu_cmd(chip, entry->fid, op); } -static int _mv88e6xxx_atu_flush(struct mv88e6xxx_chip *chip, - u16 fid, bool static_too) -{ - struct mv88e6xxx_atu_entry entry = { - .fid = fid, - .state = 0, /* EntryState bits must be 0 */ - }; - - return _mv88e6xxx_atu_flush_move(chip, &entry, static_too); -} - static int _mv88e6xxx_atu_move(struct mv88e6xxx_chip *chip, u16 fid, int from_port, int to_port, bool static_too) { @@ -1310,6 +1299,10 @@ static int mv88e6xxx_atu_setup(struct mv88e6xxx_chip *chip) { int err; + err = mv88e6xxx_g1_atu_flush(chip, 0, true); + if (err) + return err; + err = mv88e6xxx_g1_atu_set_learn2all(chip, true); if (err) return err; @@ -1714,7 +1707,7 @@ static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid) return -ENOSPC; /* Clear the database */ - return _mv88e6xxx_atu_flush(chip, *fid, true); + return mv88e6xxx_g1_atu_flush(chip, *fid, true); } static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid, @@ -2634,11 +2627,6 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip) if (err < 0) return err; - /* Clear all ATU entries */ - err = _mv88e6xxx_atu_flush(chip, 0, true); - if (err) - return err; - /* Configure the IP ToS mapping registers. */ err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_0, 0x0000); if (err) diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 8dd8ecc9f064..187acfc8ed37 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -45,5 +45,6 @@ int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, struct mv88e6xxx_atu_entry *entry); int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid, struct mv88e6xxx_atu_entry *entry); +int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index 94e940522849..7ec9b22feaee 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -232,3 +232,40 @@ int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid, return mv88e6xxx_g1_atu_op(chip, fid, GLOBAL_ATU_OP_LOAD_DB); } + +static int mv88e6xxx_g1_atu_flushmove(struct mv88e6xxx_chip *chip, u16 fid, + struct mv88e6xxx_atu_entry *entry, + bool all) +{ + u16 op; + int err; + + err = mv88e6xxx_g1_atu_op_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g1_atu_data_write(chip, entry); + if (err) + return err; + + /* Flush/Move all or non-static entries from all or a given database */ + if (all && fid) + op = GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB; + else if (fid) + op = GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB; + else if (all) + op = GLOBAL_ATU_OP_FLUSH_MOVE_ALL; + else + op = GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC; + + return mv88e6xxx_g1_atu_op(chip, fid, op); +} + +int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all) +{ + struct mv88e6xxx_atu_entry entry = { + .state = 0, /* Null EntryState means Flush */ + }; + + return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all); +} -- cgit From e606ca36bbf2e243171d42186573df254b33a123 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:12:55 -0500 Subject: net: dsa: mv88e6xxx: rework ATU Remove Add a fresh documented implementation of the ATU Move operation, and use it to replace the current ATU Remove operation. Note that not all Marvell switch chip support the ATU Move operation. For those supporting it, the number of bits used to mask the destination port may vary. 6352 and such use 4-bit, while 6390 use 5-bit. Thus add a new atu_move_port_mask member in the info structure to describe the presence and variant of ATU Move operation. Note that the ATU Move operation is not documented in the 6185 datasheet but the chip does support the operation. All remaining _mv88e6xxx_atu_* functions are now unused as well as the MV88E6XXX_FLAG_G1_ATU_FID flag, thus remove them. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 140 +++++++------------------------- drivers/net/dsa/mv88e6xxx/global1.h | 2 + drivers/net/dsa/mv88e6xxx/global1_atu.c | 29 +++++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 18 ++-- 4 files changed, 67 insertions(+), 122 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 8b8c3cb167c1..a11e5354f14f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1066,11 +1066,6 @@ static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, mutex_unlock(&chip->reg_lock); } -static int _mv88e6xxx_atu_wait(struct mv88e6xxx_chip *chip) -{ - return mv88e6xxx_g1_wait(chip, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY); -} - static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) { @@ -1130,111 +1125,6 @@ out: return err; } -static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_chip *chip, u16 fid, u16 cmd) -{ - u16 val; - int err; - - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_ATU_FID)) { - err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_FID, fid); - if (err) - return err; - } else if (mv88e6xxx_num_databases(chip) == 256) { - /* ATU DBNum[7:4] are located in ATU Control 15:12 */ - err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val); - if (err) - return err; - - err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, - (val & 0xfff) | ((fid << 8) & 0xf000)); - if (err) - return err; - - /* ATU DBNum[3:0] are located in ATU Operation 3:0 */ - cmd |= fid & 0xf; - } - - err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_OP, cmd); - if (err) - return err; - - return _mv88e6xxx_atu_wait(chip); -} - -static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_atu_entry *entry) -{ - u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK; - - if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) { - unsigned int mask, shift; - - if (entry->trunk) { - data |= GLOBAL_ATU_DATA_TRUNK; - mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK; - shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT; - } else { - mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK; - shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT; - } - - data |= (entry->portv_trunkid << shift) & mask; - } - - return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data); -} - -static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_atu_entry *entry, - bool static_too) -{ - int op; - int err; - - err = _mv88e6xxx_atu_wait(chip); - if (err) - return err; - - err = _mv88e6xxx_atu_data_write(chip, entry); - if (err) - return err; - - if (entry->fid) { - op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB : - GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB; - } else { - op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL : - GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC; - } - - return _mv88e6xxx_atu_cmd(chip, entry->fid, op); -} - -static int _mv88e6xxx_atu_move(struct mv88e6xxx_chip *chip, u16 fid, - int from_port, int to_port, bool static_too) -{ - struct mv88e6xxx_atu_entry entry = { - .trunk = false, - .fid = fid, - }; - - /* EntryState bits must be 0xF */ - entry.state = GLOBAL_ATU_DATA_STATE_MASK; - - /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */ - entry.portv_trunkid = (to_port & 0x0f) << 4; - entry.portv_trunkid |= from_port & 0x0f; - - return _mv88e6xxx_atu_flush_move(chip, &entry, static_too); -} - -static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, - int port, bool static_too) -{ - /* Destination port 0xF means remove the entries */ - return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too); -} - static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port) { struct dsa_switch *ds = chip->ds; @@ -1316,7 +1206,7 @@ static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) int err; mutex_lock(&chip->reg_lock); - err = _mv88e6xxx_atu_remove(chip, 0, port, false); + err = mv88e6xxx_g1_atu_remove(chip, 0, port, false); mutex_unlock(&chip->reg_lock); if (err) @@ -1968,7 +1858,7 @@ static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip, if (err) return err; - return _mv88e6xxx_atu_remove(chip, vlan.fid, port, false); + return mv88e6xxx_g1_atu_remove(chip, vlan.fid, port, false); } static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, @@ -3688,6 +3578,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6097, .ops = &mv88e6085_ops, @@ -3703,6 +3594,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6095, .ops = &mv88e6095_ops, @@ -3718,6 +3610,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6097, .ops = &mv88e6097_ops, @@ -3733,6 +3626,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6123_ops, @@ -3748,6 +3642,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6185, .ops = &mv88e6131_ops, @@ -3763,6 +3658,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6161_ops, @@ -3778,6 +3674,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6165_ops, @@ -3793,6 +3690,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6171_ops, @@ -3808,6 +3706,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6172_ops, @@ -3823,6 +3722,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6175_ops, @@ -3838,6 +3738,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6176_ops, @@ -3853,6 +3754,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6185, .ops = &mv88e6185_ops, @@ -3869,6 +3771,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .tag_protocol = DSA_TAG_PROTO_DSA, .age_time_coeff = 3750, .g1_irqs = 9, + .atu_move_port_mask = 0x1f, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6190_ops, }, @@ -3883,6 +3786,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .g1_irqs = 9, + .atu_move_port_mask = 0x1f, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6190x_ops, @@ -3898,6 +3802,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .g1_irqs = 9, + .atu_move_port_mask = 0x1f, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6391_ops, @@ -3913,6 +3818,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6240_ops, @@ -3928,6 +3834,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .g1_irqs = 9, + .atu_move_port_mask = 0x1f, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6290_ops, @@ -3943,6 +3850,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6320, .ops = &mv88e6320_ops, @@ -3958,6 +3866,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 8, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6320, .ops = &mv88e6321_ops, @@ -3972,6 +3881,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 3750, + .atu_move_port_mask = 0x1f, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6341, .ops = &mv88e6141_ops, @@ -3986,6 +3896,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 3750, + .atu_move_port_mask = 0x1f, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6341, .ops = &mv88e6341_ops, @@ -4001,6 +3912,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6350_ops, @@ -4016,6 +3928,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6351_ops, @@ -4031,6 +3944,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 15000, .g1_irqs = 9, + .atu_move_port_mask = 0xf, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6352_ops, @@ -4045,6 +3959,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .g1_irqs = 9, + .atu_move_port_mask = 0x1f, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6390_ops, @@ -4059,6 +3974,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .g1_irqs = 9, + .atu_move_port_mask = 0x1f, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6390x_ops, diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 187acfc8ed37..eece7418e67d 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -46,5 +46,7 @@ int mv88e6xxx_g1_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid, int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid, struct mv88e6xxx_atu_entry *entry); int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all); +int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port, + bool all); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index 7ec9b22feaee..d753e3eb1359 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -269,3 +269,32 @@ int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all) return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all); } + +static int mv88e6xxx_g1_atu_move(struct mv88e6xxx_chip *chip, u16 fid, + int from_port, int to_port, bool all) +{ + struct mv88e6xxx_atu_entry entry = { 0 }; + unsigned long mask; + int shift; + + if (!chip->info->atu_move_port_mask) + return -EOPNOTSUPP; + + mask = chip->info->atu_move_port_mask; + shift = bitmap_weight(&mask, 16); + + entry.state = 0xf, /* Full EntryState means Move */ + entry.portv_trunkid = from_port & mask; + entry.portv_trunkid |= (to_port & mask) << shift; + + return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all); +} + +int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port, + bool all) +{ + int from_port = port; + int to_port = chip->info->atu_move_port_mask; + + return mv88e6xxx_g1_atu_move(chip, fid, from_port, to_port, all); +} diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 45205519b9fe..1fca6fae2f83 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -552,7 +552,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAG_SERDES BIT_ULL(MV88E6XXX_CAP_SERDES) -#define MV88E6XXX_FLAG_G1_ATU_FID BIT_ULL(MV88E6XXX_CAP_G1_ATU_FID) #define MV88E6XXX_FLAG_G1_VTU_FID BIT_ULL(MV88E6XXX_CAP_G1_VTU_FID) #define MV88E6XXX_FLAG_GLOBAL2 BIT_ULL(MV88E6XXX_CAP_GLOBAL2) @@ -595,8 +594,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6097 \ - (MV88E6XXX_FLAG_G1_ATU_FID | \ - MV88E6XXX_FLAG_G1_VTU_FID | \ + (MV88E6XXX_FLAG_G1_VTU_FID | \ MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ @@ -609,8 +607,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAGS_PVT) #define MV88E6XXX_FLAGS_FAMILY_6165 \ - (MV88E6XXX_FLAG_G1_ATU_FID | \ - MV88E6XXX_FLAG_G1_VTU_FID | \ + (MV88E6XXX_FLAG_G1_VTU_FID | \ MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ @@ -642,7 +639,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6341 \ (MV88E6XXX_FLAG_EEE | \ - MV88E6XXX_FLAG_G1_ATU_FID | \ MV88E6XXX_FLAG_G1_VTU_FID | \ MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ @@ -655,8 +651,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAGS_SERDES) #define MV88E6XXX_FLAGS_FAMILY_6351 \ - (MV88E6XXX_FLAG_G1_ATU_FID | \ - MV88E6XXX_FLAG_G1_VTU_FID | \ + (MV88E6XXX_FLAG_G1_VTU_FID | \ MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ @@ -670,7 +665,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6352 \ (MV88E6XXX_FLAG_EEE | \ - MV88E6XXX_FLAG_G1_ATU_FID | \ MV88E6XXX_FLAG_G1_VTU_FID | \ MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ @@ -708,11 +702,15 @@ struct mv88e6xxx_info { unsigned int g1_irqs; enum dsa_tag_protocol tag_protocol; unsigned long long flags; + + /* Mask for FromPort and ToPort value of PortVec used in ATU Move + * operation. 0 means that the ATU Move operation is not supported. + */ + u8 atu_move_port_mask; const struct mv88e6xxx_ops *ops; }; struct mv88e6xxx_atu_entry { - u16 fid; u8 state; bool trunk; u16 portv_trunkid; -- cgit From d7f435f9671ed207231c3711d175a0178356514d Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:12:56 -0500 Subject: net: dsa: mv88e6xxx: rename new FID helper Rename the _mv88e6xxx_fid_new helper to mv88e6xxx_atu_new to get rid of the old underscore prefix naming convention and be consistent with the rest of the chip-wide ATU API. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index a11e5354f14f..df31c2c57e09 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1556,7 +1556,7 @@ loadpurge: return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); } -static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid) +static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) { DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); struct mv88e6xxx_vtu_entry vlan; @@ -1610,7 +1610,7 @@ static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid, }; int i, err; - err = _mv88e6xxx_fid_new(chip, &vlan.fid); + err = mv88e6xxx_atu_new(chip, &vlan.fid); if (err) return err; -- cgit From 01bd96c8831e54a3ba2794c647a269d130872750 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:12:57 -0500 Subject: net: dsa: mv88e6xxx: rename the port vector member Not all Marvell switch chips support port trunking, which is embedded in the port vector data for ATU operations. Rename the portv_trunkid member of the mv88e6xxx_atu_entry structure to portvec to be more concise and consistent with the different chips. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 8 ++++---- drivers/net/dsa/mv88e6xxx/global1_atu.c | 8 ++++---- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index df31c2c57e09..e9533d4919bc 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1928,11 +1928,11 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, /* Purge the ATU entry only if no port is using it anymore */ if (state == GLOBAL_ATU_DATA_STATE_UNUSED) { - entry.portv_trunkid &= ~BIT(port); - if (!entry.portv_trunkid) + entry.portvec &= ~BIT(port); + if (!entry.portvec) entry.state = GLOBAL_ATU_DATA_STATE_UNUSED; } else { - entry.portv_trunkid |= BIT(port); + entry.portvec |= BIT(port); entry.state = state; } @@ -1995,7 +1995,7 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED) break; - if (addr.trunk || (addr.portv_trunkid & BIT(port)) == 0) + if (addr.trunk || (addr.portvec & BIT(port)) == 0) continue; if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) { diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index d753e3eb1359..120b7f41a735 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -124,7 +124,7 @@ static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip, if (val & GLOBAL_ATU_DATA_TRUNK) entry->trunk = true; - entry->portv_trunkid = (val >> 4) & mv88e6xxx_port_mask(chip); + entry->portvec = (val >> 4) & mv88e6xxx_port_mask(chip); } return 0; @@ -139,7 +139,7 @@ static int mv88e6xxx_g1_atu_data_write(struct mv88e6xxx_chip *chip, if (entry->trunk) data |= GLOBAL_ATU_DATA_TRUNK; - data |= (entry->portv_trunkid & mv88e6xxx_port_mask(chip)) << 4; + data |= (entry->portvec & mv88e6xxx_port_mask(chip)) << 4; } return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data); @@ -284,8 +284,8 @@ static int mv88e6xxx_g1_atu_move(struct mv88e6xxx_chip *chip, u16 fid, shift = bitmap_weight(&mask, 16); entry.state = 0xf, /* Full EntryState means Move */ - entry.portv_trunkid = from_port & mask; - entry.portv_trunkid |= (to_port & mask) << shift; + entry.portvec = from_port & mask; + entry.portvec |= (to_port & mask) << shift; return mv88e6xxx_g1_atu_flushmove(chip, fid, &entry, all); } diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 1fca6fae2f83..69acb4c40fdc 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -713,7 +713,7 @@ struct mv88e6xxx_info { struct mv88e6xxx_atu_entry { u8 state; bool trunk; - u16 portv_trunkid; + u16 portvec; u8 mac[ETH_ALEN]; }; -- cgit From 6cd456f38207b56ba18c444d50fe49a7fe8b2a0a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:12:58 -0500 Subject: net: dsa: add dsa_is_normal_port helper Introduce a dsa_is_normal_port helper to check if a given port is a normal user port as opposed to a CPU port or DSA link. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- include/net/dsa.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/net/dsa.h b/include/net/dsa.h index 4e13e695f025..bf0e42c2a6f7 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -248,6 +248,11 @@ static inline bool dsa_is_dsa_port(struct dsa_switch *ds, int p) return !!((ds->dsa_port_mask) & (1 << p)); } +static inline bool dsa_is_normal_port(struct dsa_switch *ds, int p) +{ + return !dsa_is_cpu_port(ds, p) && !dsa_is_dsa_port(ds, p); +} + static inline bool dsa_is_port_initialized(struct dsa_switch *ds, int p) { return ds->enabled_port_mask & (1 << p) && ds->ports[p].netdev; -- cgit From 4314557c6505a14043717de01036dc0e3a357010 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:12:59 -0500 Subject: net: dsa: mv88e6xxx: rework port mode setup A switch port mode is defined by the association of its egress mode, its frame mode and if supported or required, the ether type value. Pack all this in a mv88e6xxx_set_port_mode function and provide helpers for the Normal Network mode, the DSA mode, and the Ether Type DSA mode, as well as an helper to setup a port's mode depending on its nature. Define PORT_ETH_TYPE_DEFAULT for the 0x9100 reset value of port E Type. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 106 +++++++++++++++++++--------------- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 1 + 2 files changed, 60 insertions(+), 47 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index e9533d4919bc..5f5023215ce1 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2199,69 +2199,82 @@ static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip) return err; } -static int mv88e6xxx_setup_port_dsa(struct mv88e6xxx_chip *chip, int port, - int upstream_port) +static int mv88e6xxx_set_port_mode(struct mv88e6xxx_chip *chip, int port, + enum mv88e6xxx_frame_mode frame, u16 egress, + u16 etype) { int err; - err = chip->info->ops->port_set_frame_mode( - chip, port, MV88E6XXX_FRAME_MODE_DSA); + if (!chip->info->ops->port_set_frame_mode) + return -EOPNOTSUPP; + + err = mv88e6xxx_port_set_egress_mode(chip, port, egress); if (err) return err; - return chip->info->ops->port_set_egress_unknowns( - chip, port, port == upstream_port); + err = chip->info->ops->port_set_frame_mode(chip, port, frame); + if (err) + return err; + + if (chip->info->ops->port_set_ether_type) + return chip->info->ops->port_set_ether_type(chip, port, etype); + + return 0; } -static int mv88e6xxx_setup_port_cpu(struct mv88e6xxx_chip *chip, int port) +static int mv88e6xxx_set_port_mode_normal(struct mv88e6xxx_chip *chip, int port) { - int err; + return mv88e6xxx_set_port_mode(chip, port, MV88E6XXX_FRAME_MODE_NORMAL, + PORT_CONTROL_EGRESS_UNMODIFIED, + PORT_ETH_TYPE_DEFAULT); +} - switch (chip->info->tag_protocol) { - case DSA_TAG_PROTO_EDSA: - err = chip->info->ops->port_set_frame_mode( - chip, port, MV88E6XXX_FRAME_MODE_ETHERTYPE); - if (err) - return err; +static int mv88e6xxx_set_port_mode_dsa(struct mv88e6xxx_chip *chip, int port) +{ + return mv88e6xxx_set_port_mode(chip, port, MV88E6XXX_FRAME_MODE_DSA, + PORT_CONTROL_EGRESS_UNMODIFIED, + PORT_ETH_TYPE_DEFAULT); +} - err = mv88e6xxx_port_set_egress_mode( - chip, port, PORT_CONTROL_EGRESS_ADD_TAG); - if (err) - return err; +static int mv88e6xxx_set_port_mode_edsa(struct mv88e6xxx_chip *chip, int port) +{ + return mv88e6xxx_set_port_mode(chip, port, + MV88E6XXX_FRAME_MODE_ETHERTYPE, + PORT_CONTROL_EGRESS_ADD_TAG, ETH_P_EDSA); +} - if (chip->info->ops->port_set_ether_type) - err = chip->info->ops->port_set_ether_type( - chip, port, ETH_P_EDSA); - break; +static int mv88e6xxx_setup_port_mode(struct mv88e6xxx_chip *chip, int port) +{ + if (dsa_is_dsa_port(chip->ds, port)) + return mv88e6xxx_set_port_mode_dsa(chip, port); - case DSA_TAG_PROTO_DSA: - err = chip->info->ops->port_set_frame_mode( - chip, port, MV88E6XXX_FRAME_MODE_DSA); - if (err) - return err; + if (dsa_is_normal_port(chip->ds, port)) + return mv88e6xxx_set_port_mode_normal(chip, port); - err = mv88e6xxx_port_set_egress_mode( - chip, port, PORT_CONTROL_EGRESS_UNMODIFIED); - break; - default: - err = -EINVAL; - } + /* Setup CPU port mode depending on its supported tag format */ + if (chip->info->tag_protocol == DSA_TAG_PROTO_DSA) + return mv88e6xxx_set_port_mode_dsa(chip, port); - if (err) - return err; + if (chip->info->tag_protocol == DSA_TAG_PROTO_EDSA) + return mv88e6xxx_set_port_mode_edsa(chip, port); - return chip->info->ops->port_set_egress_unknowns(chip, port, true); + return -EINVAL; } -static int mv88e6xxx_setup_port_normal(struct mv88e6xxx_chip *chip, int port) +static int mv88e6xxx_setup_port_dsa(struct mv88e6xxx_chip *chip, int port, + int upstream_port) { - int err; + return chip->info->ops->port_set_egress_unknowns( + chip, port, port == upstream_port); +} - err = chip->info->ops->port_set_frame_mode( - chip, port, MV88E6XXX_FRAME_MODE_NORMAL); - if (err) - return err; +static int mv88e6xxx_setup_port_cpu(struct mv88e6xxx_chip *chip, int port) +{ + return chip->info->ops->port_set_egress_unknowns(chip, port, true); +} +static int mv88e6xxx_setup_port_normal(struct mv88e6xxx_chip *chip, int port) +{ return chip->info->ops->port_set_egress_unknowns(chip, port, false); } @@ -2325,6 +2338,10 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) if (err) return err; + err = mv88e6xxx_setup_port_mode(chip, port); + if (err) + return err; + /* If this port is connected to a SerDes, make sure the SerDes is not * powered down. */ @@ -3554,11 +3571,6 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { static int mv88e6xxx_verify_madatory_ops(struct mv88e6xxx_chip *chip, const struct mv88e6xxx_ops *ops) { - if (!ops->port_set_frame_mode) { - dev_err(chip->dev, "Missing port_set_frame_mode"); - return -EINVAL; - } - if (!ops->port_set_egress_unknowns) { dev_err(chip->dev, "Missing port_set_egress_mode"); return -EINVAL; diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 69acb4c40fdc..185be5434980 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -182,6 +182,7 @@ #define PORT_ATU_CONTROL 0x0c #define PORT_PRI_OVERRIDE 0x0d #define PORT_ETH_TYPE 0x0f +#define PORT_ETH_TYPE_DEFAULT 0x9100 #define PORT_IN_DISCARD_LO 0x10 #define PORT_IN_DISCARD_HI 0x11 #define PORT_IN_FILTERED 0x12 -- cgit From 601aeed371a36e6675f3c7f7f6cf8bc181f1f276 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:13:00 -0500 Subject: net: dsa: mv88e6xxx: fix port egress flooding mode The Marvell switch ports can be configured to allow or prevent egress of frames with an unknown unicast or multicast destination address. Some switch chips such as 88E6095 and 88E6185 have two disjoint bits in Port Control Register (0x04) bit 2 "Forward Unknown" (for unicast) and Port Control 2 Register (0x08) bit 6 "Default Forward" (for multicast). Other chips such as 88E6085, 88E6123, 88E6352, and 88E6390 have a 2-bit value in Port Control Register (0x04) bits 3:2 "EgressFloods". The current code does not fully implement the disjoint bits variant and assigns incorrect ones to some chip models. Fix that with two implementation references (6185 and 6352 that I currently have) of a port_set_egress_floods operation (as named in datasheets). Old chips such as 88E6060 don't have egress flooding mode, so don't error out if the operation is not provided. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 103 +++++++++++++--------------------- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 15 +++-- drivers/net/dsa/mv88e6xxx/port.c | 44 ++++++++++----- drivers/net/dsa/mv88e6xxx/port.h | 10 ++-- 4 files changed, 80 insertions(+), 92 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 5f5023215ce1..0844df1b25e9 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2261,28 +2261,23 @@ static int mv88e6xxx_setup_port_mode(struct mv88e6xxx_chip *chip, int port) return -EINVAL; } -static int mv88e6xxx_setup_port_dsa(struct mv88e6xxx_chip *chip, int port, - int upstream_port) +static int mv88e6xxx_setup_message_port(struct mv88e6xxx_chip *chip, int port) { - return chip->info->ops->port_set_egress_unknowns( - chip, port, port == upstream_port); -} + bool message = dsa_is_dsa_port(chip->ds, port); -static int mv88e6xxx_setup_port_cpu(struct mv88e6xxx_chip *chip, int port) -{ - return chip->info->ops->port_set_egress_unknowns(chip, port, true); + return mv88e6xxx_port_set_message_port(chip, port, message); } -static int mv88e6xxx_setup_port_normal(struct mv88e6xxx_chip *chip, int port) +static int mv88e6xxx_setup_egress_floods(struct mv88e6xxx_chip *chip, int port) { - return chip->info->ops->port_set_egress_unknowns(chip, port, false); -} + bool flood = port == dsa_upstream_port(chip->ds); -static int mv88e6xxx_setup_message_port(struct mv88e6xxx_chip *chip, int port) -{ - bool message = dsa_is_dsa_port(chip->ds, port); + /* Upstream ports flood frames with unknown unicast or multicast DA */ + if (chip->info->ops->port_set_egress_floods) + return chip->info->ops->port_set_egress_floods(chip, port, + flood, flood); - return mv88e6xxx_port_set_message_port(chip, port, message); + return 0; } static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) @@ -2327,18 +2322,11 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) if (err) return err; - if (dsa_is_cpu_port(ds, port)) { - err = mv88e6xxx_setup_port_cpu(chip, port); - } else if (dsa_is_dsa_port(ds, port)) { - err = mv88e6xxx_setup_port_dsa(chip, port, - dsa_upstream_port(ds)); - } else { - err = mv88e6xxx_setup_port_normal(chip, port); - } + err = mv88e6xxx_setup_port_mode(chip, port); if (err) return err; - err = mv88e6xxx_setup_port_mode(chip, port); + err = mv88e6xxx_setup_egress_floods(chip, port); if (err) return err; @@ -2847,7 +2835,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, @@ -2873,7 +2861,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .port_set_frame_mode = mv88e6085_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6095_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6185_port_set_egress_floods, .port_set_upstream_port = mv88e6095_port_set_upstream_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2895,7 +2883,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, @@ -2920,7 +2908,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .port_set_frame_mode = mv88e6085_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6085_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -2942,7 +2930,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6095_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6185_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_set_upstream_port = mv88e6095_port_set_upstream_port, .port_jumbo_config = mv88e6165_port_jumbo_config, @@ -2971,7 +2959,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3017,7 +3005,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3046,7 +3034,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_set_speed = mv88e6352_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3073,7 +3061,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3102,7 +3090,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_set_speed = mv88e6352_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3127,7 +3115,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .port_set_frame_mode = mv88e6085_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6095_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6185_port_set_egress_floods, .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, .port_set_upstream_port = mv88e6095_port_set_upstream_port, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, @@ -3156,7 +3144,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_set_speed = mv88e6390_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, .stats_snapshot = mv88e6390_g1_stats_snapshot, @@ -3184,7 +3172,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_set_speed = mv88e6390x_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, .stats_snapshot = mv88e6390_g1_stats_snapshot, @@ -3212,7 +3200,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_set_speed = mv88e6390_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, .stats_snapshot = mv88e6390_g1_stats_snapshot, @@ -3240,7 +3228,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_set_speed = mv88e6352_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3269,7 +3257,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_set_speed = mv88e6390_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, .port_set_cmode = mv88e6390x_port_set_cmode, @@ -3297,7 +3285,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3324,7 +3312,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3349,7 +3337,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3376,7 +3364,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3405,7 +3393,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_set_speed = mv88e6352_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3434,7 +3422,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .port_set_speed = mv88e6390_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3463,7 +3451,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .port_set_speed = mv88e6390_port_set_speed, .port_tag_remap = mv88e6095_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3492,7 +3480,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_set_speed = mv88e6390_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3523,7 +3511,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_speed = mv88e6390x_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, @@ -3553,7 +3541,7 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .port_set_speed = mv88e6390_port_set_speed, .port_tag_remap = mv88e6390_port_tag_remap, .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_unknowns = mv88e6351_port_set_egress_unknowns, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, .stats_snapshot = mv88e6390_g1_stats_snapshot, @@ -3568,17 +3556,6 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .reset = mv88e6352_g1_reset, }; -static int mv88e6xxx_verify_madatory_ops(struct mv88e6xxx_chip *chip, - const struct mv88e6xxx_ops *ops) -{ - if (!ops->port_set_egress_unknowns) { - dev_err(chip->dev, "Missing port_set_egress_mode"); - return -EINVAL; - } - - return 0; -} - static const struct mv88e6xxx_info mv88e6xxx_table[] = { [MV88E6085] = { .prod_num = PORT_SWITCH_ID_PROD_NUM_6085, @@ -4269,10 +4246,6 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) chip->info = compat_info; - err = mv88e6xxx_verify_madatory_ops(chip, chip->info->ops); - if (err) - return err; - err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr); if (err) return err; diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 185be5434980..bde7c775d559 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -132,12 +132,12 @@ #define PORT_CONTROL_TAG_IF_BOTH BIT(6) #define PORT_CONTROL_USE_IP BIT(5) #define PORT_CONTROL_USE_TAG BIT(4) -#define PORT_CONTROL_FORWARD_UNKNOWN_MC BIT(3) #define PORT_CONTROL_FORWARD_UNKNOWN BIT(2) -#define PORT_CONTROL_NOT_EGRESS_UNKNOWN_DA (0x0 << 2) -#define PORT_CONTROL_NOT_EGRESS_UNKNOWN_MULTICAST_DA (0x1 << 2) -#define PORT_CONTROL_NOT_EGRESS_UNKNOWN_UNITCAST_DA (0x2 << 2) -#define PORT_CONTROL_EGRESS_ALL_UNKNOWN_DA (0x3 << 2) +#define PORT_CONTROL_EGRESS_FLOODS_MASK (0x3 << 2) +#define PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_DA (0x0 << 2) +#define PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_MC_DA (0x1 << 2) +#define PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_UC_DA (0x2 << 2) +#define PORT_CONTROL_EGRESS_FLOODS_ALL_UNKNOWN_DA (0x3 << 2) #define PORT_CONTROL_STATE_MASK 0x03 #define PORT_CONTROL_STATE_DISABLED 0x00 #define PORT_CONTROL_STATE_BLOCKING 0x01 @@ -167,7 +167,6 @@ #define PORT_CONTROL_2_DISCARD_UNTAGGED BIT(8) #define PORT_CONTROL_2_MAP_DA BIT(7) #define PORT_CONTROL_2_DEFAULT_FORWARD BIT(6) -#define PORT_CONTROL_2_FORWARD_UNKNOWN BIT(6) #define PORT_CONTROL_2_EGRESS_MONITOR BIT(5) #define PORT_CONTROL_2_INGRESS_MONITOR BIT(4) #define PORT_CONTROL_2_UPSTREAM_MASK 0x0f @@ -864,8 +863,8 @@ struct mv88e6xxx_ops { int (*port_set_frame_mode)(struct mv88e6xxx_chip *chip, int port, enum mv88e6xxx_frame_mode mode); - int (*port_set_egress_unknowns)(struct mv88e6xxx_chip *chip, int port, - bool on); + int (*port_set_egress_floods)(struct mv88e6xxx_chip *chip, int port, + bool unicast, bool multicast); int (*port_set_ether_type)(struct mv88e6xxx_chip *chip, int port, u16 etype); int (*port_jumbo_config)(struct mv88e6xxx_chip *chip, int port); diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 40fe48188056..c918292217ff 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -497,8 +497,8 @@ int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); } -int mv88e6085_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, - bool on) +static int mv88e6185_port_set_forward_unknown(struct mv88e6xxx_chip *chip, + int port, bool unicast) { int err; u16 reg; @@ -507,7 +507,7 @@ int mv88e6085_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, if (err) return err; - if (on) + if (unicast) reg |= PORT_CONTROL_FORWARD_UNKNOWN; else reg &= ~PORT_CONTROL_FORWARD_UNKNOWN; @@ -515,8 +515,8 @@ int mv88e6085_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); } -int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, - bool on) +int mv88e6352_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, + bool unicast, bool multicast) { int err; u16 reg; @@ -525,10 +525,16 @@ int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, if (err) return err; - if (on) - reg |= PORT_CONTROL_EGRESS_ALL_UNKNOWN_DA; + reg &= ~PORT_CONTROL_EGRESS_FLOODS_MASK; + + if (unicast && multicast) + reg |= PORT_CONTROL_EGRESS_FLOODS_ALL_UNKNOWN_DA; + else if (unicast) + reg |= PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_MC_DA; + else if (multicast) + reg |= PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_UC_DA; else - reg &= ~PORT_CONTROL_EGRESS_ALL_UNKNOWN_DA; + reg |= PORT_CONTROL_EGRESS_FLOODS_NO_UNKNOWN_DA; return mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg); } @@ -690,8 +696,8 @@ static const char * const mv88e6xxx_port_8021q_mode_names[] = { [PORT_CONTROL_2_8021Q_SECURE] = "Secure", }; -int mv88e6095_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, - bool on) +static int mv88e6185_port_set_default_forward(struct mv88e6xxx_chip *chip, + int port, bool multicast) { int err; u16 reg; @@ -700,14 +706,26 @@ int mv88e6095_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, if (err) return err; - if (on) - reg |= PORT_CONTROL_2_FORWARD_UNKNOWN; + if (multicast) + reg |= PORT_CONTROL_2_DEFAULT_FORWARD; else - reg &= ~PORT_CONTROL_2_FORWARD_UNKNOWN; + reg &= ~PORT_CONTROL_2_DEFAULT_FORWARD; return mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg); } +int mv88e6185_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, + bool unicast, bool multicast) +{ + int err; + + err = mv88e6185_port_set_forward_unknown(chip, port, unicast); + if (err) + return err; + + return mv88e6185_port_set_default_forward(chip, port, multicast); +} + int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, int upstream_port) { diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 4328d6f29d4d..35d28d05c009 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -56,12 +56,10 @@ int mv88e6085_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, enum mv88e6xxx_frame_mode mode); int mv88e6351_port_set_frame_mode(struct mv88e6xxx_chip *chip, int port, enum mv88e6xxx_frame_mode mode); -int mv88e6085_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, - bool on); -int mv88e6095_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, - bool on); -int mv88e6351_port_set_egress_unknowns(struct mv88e6xxx_chip *chip, int port, - bool on); +int mv88e6185_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, + bool unicast, bool multicast); +int mv88e6352_port_set_egress_floods(struct mv88e6xxx_chip *chip, int port, + bool unicast, bool multicast); int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, u16 etype); int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port, -- cgit From c8c94891527a9e624a40a2e40c5934698443fd9c Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:13:01 -0500 Subject: net: dsa: mv88e6xxx: add port ATU learn limit op Add a new operation to disable the limiting of learnt MAC addresses. Setting such limit is not likely to be used soon, so provide a port_disable_learn_limit operation directly. This can be changed later for port_set_learn_limit when we'll need it. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 36 +++++++++++++++++++++++++++++------ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 1 + drivers/net/dsa/mv88e6xxx/port.c | 7 +++++++ drivers/net/dsa/mv88e6xxx/port.h | 3 +++ 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 0844df1b25e9..06fe86309e27 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2401,15 +2401,15 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return err; } + if (chip->info->ops->port_disable_learn_limit) { + err = chip->info->ops->port_disable_learn_limit(chip, port); + if (err) + return err; + } + if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || mv88e6xxx_6320_family(chip) || mv88e6xxx_6341_family(chip)) { - /* Port ATU control: disable limiting the number of - * address database entries that this port is allowed - * to use. - */ - err = mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL, - 0x0000); /* Priority Override: disable DA, SA and VTU priority * override. */ @@ -2839,6 +2839,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -2888,6 +2889,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -2909,6 +2911,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_set_frame_mode = mv88e6085_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -2964,6 +2967,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -2983,6 +2987,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .port_set_link = mv88e6xxx_port_set_link, .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3010,6 +3015,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3039,6 +3045,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3066,6 +3073,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3095,6 +3103,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3147,6 +3156,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3175,6 +3185,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3203,6 +3214,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3233,6 +3245,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3261,6 +3274,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, .port_set_cmode = mv88e6390x_port_set_cmode, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3290,6 +3304,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3317,6 +3332,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3342,6 +3358,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3369,6 +3386,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3398,6 +3416,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3427,6 +3446,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3456,6 +3476,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3486,6 +3507,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6390_port_pause_config, .port_set_cmode = mv88e6390x_port_set_cmode, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3516,6 +3538,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_jumbo_config = mv88e6165_port_jumbo_config, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6390_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3544,6 +3567,7 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index bde7c775d559..a2a0006d8c98 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -871,6 +871,7 @@ struct mv88e6xxx_ops { int (*port_egress_rate_limiting)(struct mv88e6xxx_chip *chip, int port); int (*port_pause_config)(struct mv88e6xxx_chip *chip, int port); + int (*port_disable_learn_limit)(struct mv88e6xxx_chip *chip, int port); /* CMODE control what PHY mode the MAC will use, eg. SGMII, RGMII, etc. * Some chips allow this to be configured on specific ports. diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index c918292217ff..8152fc96fa67 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -805,6 +805,13 @@ int mv88e6097_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port) return mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL, 0x0001); } +/* Offset 0x0C: Port ATU Control */ + +int mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port) +{ + return mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL, 0); +} + /* Offset 0x0f: Port Ether type */ int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 35d28d05c009..e9eccbf277b8 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -75,4 +75,7 @@ int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port); int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, int upstream_port); + +int mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port); + #endif /* _MV88E6XXX_PORT_H */ -- cgit From 9dbfb4e1ca45c069bd1156adedfbc95e0c24be28 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:13:02 -0500 Subject: net: dsa: mv88e6xxx: add port priority override op Add a new operation to disable the DA, SA and VTU priority override. Setting such limit is not likely to be used soon, so provide a port_disable_pri_override operation directly. This can be changed later for port_set_pri_override when we'll need it. Also remove the now obsolete mv88e6xxx_6320_family helper. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 39 +++++++++++++++++++++++------------ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 1 + drivers/net/dsa/mv88e6xxx/port.c | 7 +++++++ drivers/net/dsa/mv88e6xxx/port.h | 1 + 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 06fe86309e27..3354f99df378 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -687,11 +687,6 @@ static bool mv88e6xxx_6165_family(struct mv88e6xxx_chip *chip) return chip->info->family == MV88E6XXX_FAMILY_6165; } -static bool mv88e6xxx_6320_family(struct mv88e6xxx_chip *chip) -{ - return chip->info->family == MV88E6XXX_FAMILY_6320; -} - static bool mv88e6xxx_6341_family(struct mv88e6xxx_chip *chip) { return chip->info->family == MV88E6XXX_FAMILY_6341; @@ -2407,14 +2402,8 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) return err; } - if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) || - mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) || - mv88e6xxx_6320_family(chip) || mv88e6xxx_6341_family(chip)) { - /* Priority Override: disable DA, SA and VTU priority - * override. - */ - err = mv88e6xxx_port_write(chip, port, PORT_PRI_OVERRIDE, - 0x0000); + if (chip->info->ops->port_disable_pri_override) { + err = chip->info->ops->port_disable_pri_override(chip, port); if (err) return err; } @@ -2840,6 +2829,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -2890,6 +2880,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -2912,6 +2903,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .port_set_frame_mode = mv88e6085_port_set_frame_mode, .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -2968,6 +2960,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -2988,6 +2981,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .port_set_duplex = mv88e6xxx_port_set_duplex, .port_set_speed = mv88e6185_port_set_speed, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3016,6 +3010,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3046,6 +3041,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3074,6 +3070,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3104,6 +3101,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3157,6 +3155,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3186,6 +3185,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3215,6 +3215,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3246,6 +3247,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3275,6 +3277,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_pause_config = mv88e6390_port_pause_config, .port_set_cmode = mv88e6390x_port_set_cmode, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3305,6 +3308,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3333,6 +3337,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3359,6 +3364,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3387,6 +3393,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3417,6 +3424,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_get_sset_count = mv88e6095_stats_get_sset_count, .stats_get_strings = mv88e6095_stats_get_strings, @@ -3447,6 +3455,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3477,6 +3486,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6097_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_get_sset_count = mv88e6320_stats_get_sset_count, .stats_get_strings = mv88e6320_stats_get_strings, @@ -3508,6 +3518,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_pause_config = mv88e6390_port_pause_config, .port_set_cmode = mv88e6390x_port_set_cmode, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3539,6 +3550,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_config = mv88e6390_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3568,6 +3580,7 @@ static const struct mv88e6xxx_ops mv88e6391_ops = { .port_set_ether_type = mv88e6351_port_set_ether_type, .port_pause_config = mv88e6390_port_pause_config, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index a2a0006d8c98..75be2c339a49 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -872,6 +872,7 @@ struct mv88e6xxx_ops { int (*port_egress_rate_limiting)(struct mv88e6xxx_chip *chip, int port); int (*port_pause_config)(struct mv88e6xxx_chip *chip, int port); int (*port_disable_learn_limit)(struct mv88e6xxx_chip *chip, int port); + int (*port_disable_pri_override)(struct mv88e6xxx_chip *chip, int port); /* CMODE control what PHY mode the MAC will use, eg. SGMII, RGMII, etc. * Some chips allow this to be configured on specific ports. diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 8152fc96fa67..d4868bb50ed5 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -812,6 +812,13 @@ int mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port) return mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL, 0); } +/* Offset 0x0D: (Priority) Override Register */ + +int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port) +{ + return mv88e6xxx_port_write(chip, port, PORT_PRI_OVERRIDE, 0); +} + /* Offset 0x0f: Port Ether type */ int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port, diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index e9eccbf277b8..c2425ddab287 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -77,5 +77,6 @@ int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, int upstream_port); int mv88e6xxx_port_disable_learn_limit(struct mv88e6xxx_chip *chip, int port); +int mv88e6xxx_port_disable_pri_override(struct mv88e6xxx_chip *chip, int port); #endif /* _MV88E6XXX_PORT_H */ -- cgit From 0c68f666d4cc8835ed888ffdd58f76d4d8e2da51 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 11 Mar 2017 16:13:03 -0500 Subject: etherdevice: remove unused eth_addr_greater eth_addr_greater() was introduced for the mv88e6xxx driver, but is not used anymore. There is no other user, thus remove this function. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- include/linux/etherdevice.h | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index c62b709b1ce0..2d9f80848d4b 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -446,21 +446,6 @@ static inline void eth_addr_dec(u8 *addr) u64_to_ether_addr(u, addr); } -/** - * ether_addr_greater - Compare two Ethernet addresses - * @addr1: Pointer to a six-byte array containing the Ethernet address - * @addr2: Pointer other six-byte array containing the Ethernet address - * - * Compare two Ethernet addresses, returns true addr1 is greater than addr2 - */ -static inline bool ether_addr_greater(const u8 *addr1, const u8 *addr2) -{ - u64 u1 = ether_addr_to_u64(addr1); - u64 u2 = ether_addr_to_u64(addr2); - - return u1 > u2; -} - /** * is_etherdev_addr - Tell if given Ethernet address belongs to the device. * @dev: Pointer to a device structure -- cgit From 1bcf165ac6556dc55a596d524b8187d1ba7a8c7d Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Sun, 12 Mar 2017 05:02:54 -0400 Subject: r8169: replace init_timer with setup_timer Replace init_timer with setup_timer to simplify the source code. Signed-off-by: Zhu Yanjun Signed-off-by: David S. Miller --- drivers/net/ethernet/realtek/r8169.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 24b045b777b6..0a8f2817ea60 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -8453,9 +8453,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) tp->opts1_mask = (tp->mac_version != RTL_GIGA_MAC_VER_01) ? ~(RxBOVF | RxFOVF) : ~0; - init_timer(&tp->timer); - tp->timer.data = (unsigned long) dev; - tp->timer.function = rtl8169_phy_timer; + setup_timer(&tp->timer, rtl8169_phy_timer, (unsigned long)dev); tp->rtl_fw = RTL_FIRMWARE_UNKNOWN; -- cgit From e33cc31630c3ca1de47c2470ec8d41dee0683b11 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 13 Mar 2017 00:00:03 -0700 Subject: sch_tbf: Remove bogus semicolon in if() conditional. Fixes: 49b499718fa1 ("net: sched: make default fifo qdiscs appear in the dump") Reported-by: kbuild test robot Signed-off-by: David S. Miller --- net/sched/sch_tbf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 40c29a801391..9850126129a3 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -396,7 +396,7 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt) q->qdisc->qstats.backlog); qdisc_destroy(q->qdisc); q->qdisc = child; - if (child != &noop_qdisc); + if (child != &noop_qdisc) qdisc_hash_add(child, true); } q->limit = qopt->limit; -- cgit From 84fba05511c8e0e1a6e6c8ec2c3c85616c39a8c1 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 8 Mar 2017 16:48:43 +0100 Subject: netfilter: provide nft_ctx in object init function this is needed by the upcoming ct helper object type -- we'd like to be able use the table family (ip, ip6, inet) to figure out which helper has to be requested. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 3 ++- net/netfilter/nf_tables_api.c | 7 ++++--- net/netfilter/nft_counter.c | 3 ++- net/netfilter/nft_quota.c | 3 ++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index f0d46726d06e..49436849d7d7 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -1017,7 +1017,8 @@ struct nft_object_type { unsigned int maxattr; struct module *owner; const struct nla_policy *policy; - int (*init)(const struct nlattr * const tb[], + int (*init)(const struct nft_ctx *ctx, + const struct nlattr *const tb[], struct nft_object *obj); void (*destroy)(struct nft_object *obj); int (*dump)(struct sk_buff *skb, diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 4559f5d66bcc..12cc5218de96 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4095,7 +4095,8 @@ static const struct nla_policy nft_obj_policy[NFTA_OBJ_MAX + 1] = { [NFTA_OBJ_DATA] = { .type = NLA_NESTED }, }; -static struct nft_object *nft_obj_init(const struct nft_object_type *type, +static struct nft_object *nft_obj_init(const struct nft_ctx *ctx, + const struct nft_object_type *type, const struct nlattr *attr) { struct nlattr *tb[type->maxattr + 1]; @@ -4115,7 +4116,7 @@ static struct nft_object *nft_obj_init(const struct nft_object_type *type, if (obj == NULL) goto err1; - err = type->init((const struct nlattr * const *)tb, obj); + err = type->init(ctx, (const struct nlattr * const *)tb, obj); if (err < 0) goto err2; @@ -4223,7 +4224,7 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk, if (IS_ERR(type)) return PTR_ERR(type); - obj = nft_obj_init(type, nla[NFTA_OBJ_DATA]); + obj = nft_obj_init(&ctx, type, nla[NFTA_OBJ_DATA]); if (IS_ERR(obj)) { err = PTR_ERR(obj); goto err1; diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c index 7f8422213341..67a710ebde09 100644 --- a/net/netfilter/nft_counter.c +++ b/net/netfilter/nft_counter.c @@ -82,7 +82,8 @@ static int nft_counter_do_init(const struct nlattr * const tb[], return 0; } -static int nft_counter_obj_init(const struct nlattr * const tb[], +static int nft_counter_obj_init(const struct nft_ctx *ctx, + const struct nlattr * const tb[], struct nft_object *obj) { struct nft_counter_percpu_priv *priv = nft_obj_data(obj); diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c index 2d6fe3559912..25e33159be57 100644 --- a/net/netfilter/nft_quota.c +++ b/net/netfilter/nft_quota.c @@ -99,7 +99,8 @@ static int nft_quota_do_init(const struct nlattr * const tb[], return 0; } -static int nft_quota_obj_init(const struct nlattr * const tb[], +static int nft_quota_obj_init(const struct nft_ctx *ctx, + const struct nlattr * const tb[], struct nft_object *obj) { struct nft_quota *priv = nft_obj_data(obj); -- cgit From 1a64edf54f55d7956cf5a0d95898bc1f84f9b818 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 8 Mar 2017 16:48:44 +0100 Subject: netfilter: nft_ct: add helper set support this allows to assign connection tracking helpers to connections via nft objref infrastructure. The idea is to first specifiy a helper object: table ip filter { ct helper some-name { type "ftp" protocol tcp l3proto ip } } and then assign it via nft add ... ct helper set "some-name" helper assignment works for new conntracks only as we cannot expand the conntrack extension area once it has been committed to the main conntrack table. ipv4 and ipv6 protocols are tracked stored separately so we can also handle families that observe both ipv4 and ipv6 traffic. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 12 ++- net/netfilter/nft_ct.c | 171 +++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 4f7d75682c59..34c8d08b687a 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1259,10 +1259,20 @@ enum nft_fib_flags { NFTA_FIB_F_OIF = 1 << 4, /* restrict to oif */ }; +enum nft_ct_helper_attributes { + NFTA_CT_HELPER_UNSPEC, + NFTA_CT_HELPER_NAME, + NFTA_CT_HELPER_L3PROTO, + NFTA_CT_HELPER_L4PROTO, + __NFTA_CT_HELPER_MAX, +}; +#define NFTA_CT_HELPER_MAX (__NFTA_CT_HELPER_MAX - 1) + #define NFT_OBJECT_UNSPEC 0 #define NFT_OBJECT_COUNTER 1 #define NFT_OBJECT_QUOTA 2 -#define __NFT_OBJECT_MAX 3 +#define NFT_OBJECT_CT_HELPER 3 +#define __NFT_OBJECT_MAX 4 #define NFT_OBJECT_MAX (__NFT_OBJECT_MAX - 1) /** diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index bf548a7a71ec..4144ae845bdd 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -32,6 +32,12 @@ struct nft_ct { }; }; +struct nft_ct_helper_obj { + struct nf_conntrack_helper *helper4; + struct nf_conntrack_helper *helper6; + u8 l4proto; +}; + #ifdef CONFIG_NF_CONNTRACK_ZONES static DEFINE_PER_CPU(struct nf_conn *, nft_ct_pcpu_template); static unsigned int nft_ct_pcpu_template_refcnt __read_mostly; @@ -730,6 +736,162 @@ static struct nft_expr_type nft_notrack_type __read_mostly = { .owner = THIS_MODULE, }; +static int nft_ct_helper_obj_init(const struct nft_ctx *ctx, + const struct nlattr * const tb[], + struct nft_object *obj) +{ + struct nft_ct_helper_obj *priv = nft_obj_data(obj); + struct nf_conntrack_helper *help4, *help6; + char name[NF_CT_HELPER_NAME_LEN]; + int family = ctx->afi->family; + + if (!tb[NFTA_CT_HELPER_NAME] || !tb[NFTA_CT_HELPER_L4PROTO]) + return -EINVAL; + + priv->l4proto = nla_get_u8(tb[NFTA_CT_HELPER_L4PROTO]); + if (!priv->l4proto) + return -ENOENT; + + nla_strlcpy(name, tb[NFTA_CT_HELPER_NAME], sizeof(name)); + + if (tb[NFTA_CT_HELPER_L3PROTO]) + family = ntohs(nla_get_be16(tb[NFTA_CT_HELPER_L3PROTO])); + + help4 = NULL; + help6 = NULL; + + switch (family) { + case NFPROTO_IPV4: + if (ctx->afi->family == NFPROTO_IPV6) + return -EINVAL; + + help4 = nf_conntrack_helper_try_module_get(name, family, + priv->l4proto); + break; + case NFPROTO_IPV6: + if (ctx->afi->family == NFPROTO_IPV4) + return -EINVAL; + + help6 = nf_conntrack_helper_try_module_get(name, family, + priv->l4proto); + break; + case NFPROTO_NETDEV: /* fallthrough */ + case NFPROTO_BRIDGE: /* same */ + case NFPROTO_INET: + help4 = nf_conntrack_helper_try_module_get(name, NFPROTO_IPV4, + priv->l4proto); + help6 = nf_conntrack_helper_try_module_get(name, NFPROTO_IPV6, + priv->l4proto); + break; + default: + return -EAFNOSUPPORT; + } + + /* && is intentional; only error if INET found neither ipv4 or ipv6 */ + if (!help4 && !help6) + return -ENOENT; + + priv->helper4 = help4; + priv->helper6 = help6; + + return 0; +} + +static void nft_ct_helper_obj_destroy(struct nft_object *obj) +{ + struct nft_ct_helper_obj *priv = nft_obj_data(obj); + + if (priv->helper4) + module_put(priv->helper4->me); + if (priv->helper6) + module_put(priv->helper6->me); +} + +static void nft_ct_helper_obj_eval(struct nft_object *obj, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + const struct nft_ct_helper_obj *priv = nft_obj_data(obj); + struct nf_conn *ct = (struct nf_conn *)skb_nfct(pkt->skb); + struct nf_conntrack_helper *to_assign = NULL; + struct nf_conn_help *help; + + if (!ct || + nf_ct_is_confirmed(ct) || + nf_ct_is_template(ct) || + priv->l4proto != nf_ct_protonum(ct)) + return; + + switch (nf_ct_l3num(ct)) { + case NFPROTO_IPV4: + to_assign = priv->helper4; + break; + case NFPROTO_IPV6: + to_assign = priv->helper6; + break; + default: + WARN_ON_ONCE(1); + return; + } + + if (!to_assign) + return; + + if (test_bit(IPS_HELPER_BIT, &ct->status)) + return; + + help = nf_ct_helper_ext_add(ct, to_assign, GFP_ATOMIC); + if (help) { + rcu_assign_pointer(help->helper, to_assign); + set_bit(IPS_HELPER_BIT, &ct->status); + } +} + +static int nft_ct_helper_obj_dump(struct sk_buff *skb, + struct nft_object *obj, bool reset) +{ + const struct nft_ct_helper_obj *priv = nft_obj_data(obj); + const struct nf_conntrack_helper *helper = priv->helper4; + u16 family; + + if (nla_put_string(skb, NFTA_CT_HELPER_NAME, helper->name)) + return -1; + + if (nla_put_u8(skb, NFTA_CT_HELPER_L4PROTO, priv->l4proto)) + return -1; + + if (priv->helper4 && priv->helper6) + family = NFPROTO_INET; + else if (priv->helper6) + family = NFPROTO_IPV6; + else + family = NFPROTO_IPV4; + + if (nla_put_be16(skb, NFTA_CT_HELPER_L3PROTO, htons(family))) + return -1; + + return 0; +} + +static const struct nla_policy nft_ct_helper_policy[NFTA_CT_HELPER_MAX + 1] = { + [NFTA_CT_HELPER_NAME] = { .type = NLA_STRING, + .len = NF_CT_HELPER_NAME_LEN - 1 }, + [NFTA_CT_HELPER_L3PROTO] = { .type = NLA_U16 }, + [NFTA_CT_HELPER_L4PROTO] = { .type = NLA_U8 }, +}; + +static struct nft_object_type nft_ct_helper_obj __read_mostly = { + .type = NFT_OBJECT_CT_HELPER, + .size = sizeof(struct nft_ct_helper_obj), + .maxattr = NFTA_CT_HELPER_MAX, + .policy = nft_ct_helper_policy, + .eval = nft_ct_helper_obj_eval, + .init = nft_ct_helper_obj_init, + .destroy = nft_ct_helper_obj_destroy, + .dump = nft_ct_helper_obj_dump, + .owner = THIS_MODULE, +}; + static int __init nft_ct_module_init(void) { int err; @@ -744,7 +906,14 @@ static int __init nft_ct_module_init(void) if (err < 0) goto err1; + err = nft_register_obj(&nft_ct_helper_obj); + if (err < 0) + goto err2; + return 0; + +err2: + nft_unregister_expr(&nft_notrack_type); err1: nft_unregister_expr(&nft_ct_type); return err; @@ -752,6 +921,7 @@ err1: static void __exit nft_ct_module_exit(void) { + nft_unregister_obj(&nft_ct_helper_obj); nft_unregister_expr(&nft_notrack_type); nft_unregister_expr(&nft_ct_type); } @@ -763,3 +933,4 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Patrick McHardy "); MODULE_ALIAS_NFT_EXPR("ct"); MODULE_ALIAS_NFT_EXPR("notrack"); +MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_CT_HELPER); -- cgit From 055c4b34b94f696d9bd9aad53a11378a0fc409c9 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Fri, 10 Mar 2017 18:08:02 +0100 Subject: netfilter: nft_fib: Support existence check Instead of the actual interface index or name, set destination register to just 1 or 0 depending on whether the lookup succeeded or not if NFTA_FIB_F_PRESENT was set in userspace. Signed-off-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nft_fib.h | 2 +- include/uapi/linux/netfilter/nf_tables.h | 1 + net/ipv4/netfilter/nft_fib_ipv4.c | 4 ++-- net/ipv6/netfilter/nft_fib_ipv6.c | 2 +- net/netfilter/nft_fib.c | 14 +++++++++----- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/include/net/netfilter/nft_fib.h b/include/net/netfilter/nft_fib.h index 5ceb2205e4e3..381af9469e6a 100644 --- a/include/net/netfilter/nft_fib.h +++ b/include/net/netfilter/nft_fib.h @@ -32,6 +32,6 @@ void nft_fib6_eval_type(const struct nft_expr *expr, struct nft_regs *regs, void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt); -void nft_fib_store_result(void *reg, enum nft_fib_result r, +void nft_fib_store_result(void *reg, const struct nft_fib *priv, const struct nft_pktinfo *pkt, int index); #endif diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 34c8d08b687a..8f3842690d17 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1257,6 +1257,7 @@ enum nft_fib_flags { NFTA_FIB_F_MARK = 1 << 2, /* use skb->mark */ NFTA_FIB_F_IIF = 1 << 3, /* restrict to iif */ NFTA_FIB_F_OIF = 1 << 4, /* restrict to oif */ + NFTA_FIB_F_PRESENT = 1 << 5, /* check existence only */ }; enum nft_ct_helper_attributes { diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c index 2981291910dd..f4e4462cb5bb 100644 --- a/net/ipv4/netfilter/nft_fib_ipv4.c +++ b/net/ipv4/netfilter/nft_fib_ipv4.c @@ -90,7 +90,7 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, if (nft_hook(pkt) == NF_INET_PRE_ROUTING && nft_fib_is_loopback(pkt->skb, nft_in(pkt))) { - nft_fib_store_result(dest, priv->result, pkt, + nft_fib_store_result(dest, priv, pkt, nft_in(pkt)->ifindex); return; } @@ -99,7 +99,7 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, if (ipv4_is_zeronet(iph->saddr)) { if (ipv4_is_lbcast(iph->daddr) || ipv4_is_local_multicast(iph->daddr)) { - nft_fib_store_result(dest, priv->result, pkt, + nft_fib_store_result(dest, priv, pkt, get_ifindex(pkt->skb->dev)); return; } diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c index 765facf03d45..e8d88d82636b 100644 --- a/net/ipv6/netfilter/nft_fib_ipv6.c +++ b/net/ipv6/netfilter/nft_fib_ipv6.c @@ -159,7 +159,7 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs, if (nft_hook(pkt) == NF_INET_PRE_ROUTING && nft_fib_is_loopback(pkt->skb, nft_in(pkt))) { - nft_fib_store_result(dest, priv->result, pkt, + nft_fib_store_result(dest, priv, pkt, nft_in(pkt)->ifindex); return; } diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index fd0b19303b0d..21df8cccea65 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -24,7 +24,8 @@ const struct nla_policy nft_fib_policy[NFTA_FIB_MAX + 1] = { EXPORT_SYMBOL(nft_fib_policy); #define NFTA_FIB_F_ALL (NFTA_FIB_F_SADDR | NFTA_FIB_F_DADDR | \ - NFTA_FIB_F_MARK | NFTA_FIB_F_IIF | NFTA_FIB_F_OIF) + NFTA_FIB_F_MARK | NFTA_FIB_F_IIF | NFTA_FIB_F_OIF | \ + NFTA_FIB_F_PRESENT) int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nft_data **data) @@ -133,19 +134,22 @@ int nft_fib_dump(struct sk_buff *skb, const struct nft_expr *expr) } EXPORT_SYMBOL_GPL(nft_fib_dump); -void nft_fib_store_result(void *reg, enum nft_fib_result r, +void nft_fib_store_result(void *reg, const struct nft_fib *priv, const struct nft_pktinfo *pkt, int index) { struct net_device *dev; u32 *dreg = reg; - switch (r) { + switch (priv->result) { case NFT_FIB_RESULT_OIF: - *dreg = index; + *dreg = (priv->flags & NFTA_FIB_F_PRESENT) ? !!index : index; break; case NFT_FIB_RESULT_OIFNAME: dev = dev_get_by_index_rcu(nft_net(pkt), index); - strncpy(reg, dev ? dev->name : "", IFNAMSIZ); + if (priv->flags & NFTA_FIB_F_PRESENT) + *dreg = !!dev; + else + strncpy(reg, dev ? dev->name : "", IFNAMSIZ); break; default: WARN_ON_ONCE(1); -- cgit From fc09e4a75a12552b9374a5b0c5f5815aecf8c37a Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 9 Mar 2017 12:57:15 +0100 Subject: netfilter: nf_conntrack: reduce resolve_normal_ct args also mark init_conntrack noinline, in most cases resolve_normal_ct will find an existing conntrack entry. text data bss dec hex filename 16735 5707 176 22618 585a net/netfilter/nf_conntrack_core.o 16687 5707 176 22570 582a net/netfilter/nf_conntrack_core.o Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_core.c | 57 ++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 071b97fcbefb..b0f2e8e65084 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1129,7 +1129,7 @@ EXPORT_SYMBOL_GPL(nf_conntrack_free); /* Allocate a new conntrack: we return -ENOMEM if classification failed due to stress. Otherwise it really is unclassifiable. */ -static struct nf_conntrack_tuple_hash * +static noinline struct nf_conntrack_tuple_hash * init_conntrack(struct net *net, struct nf_conn *tmpl, const struct nf_conntrack_tuple *tuple, struct nf_conntrack_l3proto *l3proto, @@ -1237,21 +1237,20 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, return &ct->tuplehash[IP_CT_DIR_ORIGINAL]; } -/* On success, returns conntrack ptr, sets skb->_nfct | ctinfo */ -static inline struct nf_conn * +/* On success, returns 0, sets skb->_nfct | ctinfo */ +static int resolve_normal_ct(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, unsigned int dataoff, u_int16_t l3num, u_int8_t protonum, struct nf_conntrack_l3proto *l3proto, - struct nf_conntrack_l4proto *l4proto, - int *set_reply, - enum ip_conntrack_info *ctinfo) + struct nf_conntrack_l4proto *l4proto) { const struct nf_conntrack_zone *zone; struct nf_conntrack_tuple tuple; struct nf_conntrack_tuple_hash *h; + enum ip_conntrack_info ctinfo; struct nf_conntrack_zone tmp; struct nf_conn *ct; u32 hash; @@ -1260,7 +1259,7 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl, dataoff, l3num, protonum, net, &tuple, l3proto, l4proto)) { pr_debug("Can't get tuple\n"); - return NULL; + return 0; } /* look for tuple match */ @@ -1271,33 +1270,30 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl, h = init_conntrack(net, tmpl, &tuple, l3proto, l4proto, skb, dataoff, hash); if (!h) - return NULL; + return 0; if (IS_ERR(h)) - return (void *)h; + return PTR_ERR(h); } ct = nf_ct_tuplehash_to_ctrack(h); /* It exists; we have (non-exclusive) reference. */ if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) { - *ctinfo = IP_CT_ESTABLISHED_REPLY; - /* Please set reply bit if this packet OK */ - *set_reply = 1; + ctinfo = IP_CT_ESTABLISHED_REPLY; } else { /* Once we've had two way comms, always ESTABLISHED. */ if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { pr_debug("normal packet for %p\n", ct); - *ctinfo = IP_CT_ESTABLISHED; + ctinfo = IP_CT_ESTABLISHED; } else if (test_bit(IPS_EXPECTED_BIT, &ct->status)) { pr_debug("related packet for %p\n", ct); - *ctinfo = IP_CT_RELATED; + ctinfo = IP_CT_RELATED; } else { pr_debug("new packet for %p\n", ct); - *ctinfo = IP_CT_NEW; + ctinfo = IP_CT_NEW; } - *set_reply = 0; } - nf_ct_set(skb, ct, *ctinfo); - return ct; + nf_ct_set(skb, ct, ctinfo); + return 0; } unsigned int @@ -1311,7 +1307,6 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, unsigned int *timeouts; unsigned int dataoff; u_int8_t protonum; - int set_reply = 0; int ret; tmpl = nf_ct_get(skb, &ctinfo); @@ -1354,23 +1349,22 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, goto out; } repeat: - ct = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum, - l3proto, l4proto, &set_reply, &ctinfo); - if (!ct) { - /* Not valid part of a connection */ - NF_CT_STAT_INC_ATOMIC(net, invalid); - ret = NF_ACCEPT; - goto out; - } - - if (IS_ERR(ct)) { + ret = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum, + l3proto, l4proto); + if (ret < 0) { /* Too stressed to deal. */ NF_CT_STAT_INC_ATOMIC(net, drop); ret = NF_DROP; goto out; } - NF_CT_ASSERT(skb_nfct(skb)); + ct = nf_ct_get(skb, &ctinfo); + if (!ct) { + /* Not valid part of a connection */ + NF_CT_STAT_INC_ATOMIC(net, invalid); + ret = NF_ACCEPT; + goto out; + } /* Decide what timeout policy we want to apply to this flow. */ timeouts = nf_ct_timeout_lookup(net, ct, l4proto); @@ -1395,7 +1389,8 @@ repeat: goto out; } - if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status)) + if (ctinfo == IP_CT_ESTABLISHED_REPLY && + !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status)) nf_conntrack_event_cache(IPCT_REPLY, ct); out: if (tmpl) -- cgit From 2cb4bbd75bdf9d423b9f6c629f81eb66ee312fac Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Sat, 11 Mar 2017 14:08:09 +0800 Subject: netfilter: limit: use per-rule spinlock to improve the scalability The limit token is independent between each rules, so there's no need to use a global spinlock. Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_limit.c | 10 +++++----- net/netfilter/xt_limit.c | 11 ++++++----- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c index c6baf412236d..18dd57a52651 100644 --- a/net/netfilter/nft_limit.c +++ b/net/netfilter/nft_limit.c @@ -17,9 +17,8 @@ #include #include -static DEFINE_SPINLOCK(limit_lock); - struct nft_limit { + spinlock_t lock; u64 last; u64 tokens; u64 tokens_max; @@ -34,7 +33,7 @@ static inline bool nft_limit_eval(struct nft_limit *limit, u64 cost) u64 now, tokens; s64 delta; - spin_lock_bh(&limit_lock); + spin_lock_bh(&limit->lock); now = ktime_get_ns(); tokens = limit->tokens + now - limit->last; if (tokens > limit->tokens_max) @@ -44,11 +43,11 @@ static inline bool nft_limit_eval(struct nft_limit *limit, u64 cost) delta = tokens - cost; if (delta >= 0) { limit->tokens = delta; - spin_unlock_bh(&limit_lock); + spin_unlock_bh(&limit->lock); return limit->invert; } limit->tokens = tokens; - spin_unlock_bh(&limit_lock); + spin_unlock_bh(&limit->lock); return !limit->invert; } @@ -86,6 +85,7 @@ static int nft_limit_init(struct nft_limit *limit, limit->invert = true; } limit->last = ktime_get_ns(); + spin_lock_init(&limit->lock); return 0; } diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c index dab962df1787..d27b5f1ea619 100644 --- a/net/netfilter/xt_limit.c +++ b/net/netfilter/xt_limit.c @@ -18,6 +18,7 @@ #include struct xt_limit_priv { + spinlock_t lock; unsigned long prev; uint32_t credit; }; @@ -32,8 +33,6 @@ MODULE_ALIAS("ip6t_limit"); * see net/sched/sch_tbf.c in the linux source tree */ -static DEFINE_SPINLOCK(limit_lock); - /* Rusty: This is my (non-mathematically-inclined) understanding of this algorithm. The `average rate' in jiffies becomes your initial amount of credit `credit' and the most credit you can ever have @@ -72,7 +71,7 @@ limit_mt(const struct sk_buff *skb, struct xt_action_param *par) struct xt_limit_priv *priv = r->master; unsigned long now = jiffies; - spin_lock_bh(&limit_lock); + spin_lock_bh(&priv->lock); priv->credit += (now - xchg(&priv->prev, now)) * CREDITS_PER_JIFFY; if (priv->credit > r->credit_cap) priv->credit = r->credit_cap; @@ -80,11 +79,11 @@ limit_mt(const struct sk_buff *skb, struct xt_action_param *par) if (priv->credit >= r->cost) { /* We're not limited. */ priv->credit -= r->cost; - spin_unlock_bh(&limit_lock); + spin_unlock_bh(&priv->lock); return true; } - spin_unlock_bh(&limit_lock); + spin_unlock_bh(&priv->lock); return false; } @@ -126,6 +125,8 @@ static int limit_mt_check(const struct xt_mtchk_param *par) r->credit_cap = priv->credit; /* Credits full. */ r->cost = user2credits(r->avg); } + spin_lock_init(&priv->lock); + return 0; } -- cgit From 03e5fd0e9bcc1f34b7a542786b34b8f771e7c260 Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Sun, 12 Mar 2017 19:38:47 +0800 Subject: netfilter: nft_set_rbtree: use per-set rwlock to improve the scalability Karel Rericha reported that in his test case, ICMP packets going through boxes had normally about 5ms latency. But when running nft, actually listing the sets with interval flags, latency would go up to 30-100ms. This was observed when router throughput is from 600Mbps to 2Gbps. This is because we use a single global spinlock to protect the whole rbtree sets, so "dumping sets" will race with the "key lookup" inevitably. But actually they are all _readers_, so it's ok to convert the spinlock to rwlock to avoid competition between them. Also use per-set rwlock since each set is independent. Reported-by: Karel Rericha Tested-by: Karel Rericha Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_set_rbtree.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index 78dfbf9588b3..e97e2fb53f0a 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -18,9 +18,8 @@ #include #include -static DEFINE_SPINLOCK(nft_rbtree_lock); - struct nft_rbtree { + rwlock_t lock; struct rb_root root; }; @@ -44,14 +43,14 @@ static bool nft_rbtree_equal(const struct nft_set *set, const void *this, static bool nft_rbtree_lookup(const struct net *net, const struct nft_set *set, const u32 *key, const struct nft_set_ext **ext) { - const struct nft_rbtree *priv = nft_set_priv(set); + struct nft_rbtree *priv = nft_set_priv(set); const struct nft_rbtree_elem *rbe, *interval = NULL; u8 genmask = nft_genmask_cur(net); const struct rb_node *parent; const void *this; int d; - spin_lock_bh(&nft_rbtree_lock); + read_lock_bh(&priv->lock); parent = priv->root.rb_node; while (parent != NULL) { rbe = rb_entry(parent, struct nft_rbtree_elem, node); @@ -75,7 +74,7 @@ static bool nft_rbtree_lookup(const struct net *net, const struct nft_set *set, } if (nft_rbtree_interval_end(rbe)) goto out; - spin_unlock_bh(&nft_rbtree_lock); + read_unlock_bh(&priv->lock); *ext = &rbe->ext; return true; @@ -85,12 +84,12 @@ static bool nft_rbtree_lookup(const struct net *net, const struct nft_set *set, if (set->flags & NFT_SET_INTERVAL && interval != NULL && nft_set_elem_active(&interval->ext, genmask) && !nft_rbtree_interval_end(interval)) { - spin_unlock_bh(&nft_rbtree_lock); + read_unlock_bh(&priv->lock); *ext = &interval->ext; return true; } out: - spin_unlock_bh(&nft_rbtree_lock); + read_unlock_bh(&priv->lock); return false; } @@ -140,12 +139,13 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set, const struct nft_set_elem *elem, struct nft_set_ext **ext) { + struct nft_rbtree *priv = nft_set_priv(set); struct nft_rbtree_elem *rbe = elem->priv; int err; - spin_lock_bh(&nft_rbtree_lock); + write_lock_bh(&priv->lock); err = __nft_rbtree_insert(net, set, rbe, ext); - spin_unlock_bh(&nft_rbtree_lock); + write_unlock_bh(&priv->lock); return err; } @@ -157,9 +157,9 @@ static void nft_rbtree_remove(const struct net *net, struct nft_rbtree *priv = nft_set_priv(set); struct nft_rbtree_elem *rbe = elem->priv; - spin_lock_bh(&nft_rbtree_lock); + write_lock_bh(&priv->lock); rb_erase(&rbe->node, &priv->root); - spin_unlock_bh(&nft_rbtree_lock); + write_unlock_bh(&priv->lock); } static void nft_rbtree_activate(const struct net *net, @@ -224,12 +224,12 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx, struct nft_set *set, struct nft_set_iter *iter) { - const struct nft_rbtree *priv = nft_set_priv(set); + struct nft_rbtree *priv = nft_set_priv(set); struct nft_rbtree_elem *rbe; struct nft_set_elem elem; struct rb_node *node; - spin_lock_bh(&nft_rbtree_lock); + read_lock_bh(&priv->lock); for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) { rbe = rb_entry(node, struct nft_rbtree_elem, node); @@ -242,13 +242,13 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx, iter->err = iter->fn(ctx, set, iter, &elem); if (iter->err < 0) { - spin_unlock_bh(&nft_rbtree_lock); + read_unlock_bh(&priv->lock); return; } cont: iter->count++; } - spin_unlock_bh(&nft_rbtree_lock); + read_unlock_bh(&priv->lock); } static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[]) @@ -262,6 +262,7 @@ static int nft_rbtree_init(const struct nft_set *set, { struct nft_rbtree *priv = nft_set_priv(set); + rwlock_init(&priv->lock); priv->root = RB_ROOT; return 0; } -- cgit From 26d6851fd24ed5d88580d66b4c8384947d5ca29b Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Mon, 13 Mar 2017 10:07:07 +0000 Subject: net: stmmac: set default number of rx and tx queues in stmmac_pci This patch configures default number of RX and TX queues when using the pci glue driver. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c index 5c9e462276b9..cea472a7c335 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c @@ -88,6 +88,10 @@ static void stmmac_default_data(struct plat_stmmacenet_data *plat) /* Set the maxmtu to a default of JUMBO_LEN */ plat->maxmtu = JUMBO_LEN; + + /* Set default number of RX and TX queues to use */ + plat->tx_queues_to_use = 1; + plat->rx_queues_to_use = 1; } static int quark_default_data(struct plat_stmmacenet_data *plat, -- cgit From 68e5cfaffaca73bda1263c503f9b186a196dd97b Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Mon, 13 Mar 2017 10:36:29 +0000 Subject: net: stmmac: added default rx queue size in stmmac_dma_interrupt This patch adds the rx queue default size when dma interrupts are treated, since dma op mode can be also set there. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 915636ff2fc1..ec363e19cd79 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1455,6 +1455,9 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv) int status; int rxfifosz = priv->plat->rx_fifo_size; + if (rxfifosz == 0) + rxfifosz = priv->dma_cap.rx_fifo_size; + status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats); if (likely((status & handle_rx)) || (status & handle_tx)) { if (likely(napi_schedule_prep(&priv->napi))) { -- cgit From 17886c4705853fe402d06b2820ae155d38fb345e Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 9 Mar 2017 17:42:56 +0100 Subject: gtp: switch from struct socket to struct sock for the GTP sockets After enabling the UDP encapsulation, only the sk member is used. Holding the socket would prevent user space from closing the socket, but holding a reference to the sk member does not have the same effect. This change will make it simpler to later detach the sockets from the netdevice. Signed-off-by: Andreas Schultz Signed-off-by: David S. Miller --- drivers/net/gtp.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 89698741682f..e1b5af377fbc 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -66,8 +66,8 @@ struct pdp_ctx { struct gtp_dev { struct list_head list; - struct socket *sock0; - struct socket *sock1u; + struct sock *sk0; + struct sock *sk1u; struct net_device *dev; @@ -261,17 +261,19 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, static void gtp_encap_disable(struct gtp_dev *gtp) { - if (gtp->sock0 && gtp->sock0->sk) { - udp_sk(gtp->sock0->sk)->encap_type = 0; - rcu_assign_sk_user_data(gtp->sock0->sk, NULL); + if (gtp->sk0) { + udp_sk(gtp->sk0)->encap_type = 0; + rcu_assign_sk_user_data(gtp->sk0, NULL); + sock_put(gtp->sk0); } - if (gtp->sock1u && gtp->sock1u->sk) { - udp_sk(gtp->sock1u->sk)->encap_type = 0; - rcu_assign_sk_user_data(gtp->sock1u->sk, NULL); + if (gtp->sk1u) { + udp_sk(gtp->sk1u)->encap_type = 0; + rcu_assign_sk_user_data(gtp->sk1u, NULL); + sock_put(gtp->sk1u); } - gtp->sock0 = NULL; - gtp->sock1u = NULL; + gtp->sk0 = NULL; + gtp->sk1u = NULL; } static void gtp_encap_destroy(struct sock *sk) @@ -484,14 +486,14 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, switch (pctx->gtp_version) { case GTP_V0: - if (gtp->sock0) - sk = gtp->sock0->sk; + if (gtp->sk0) + sk = gtp->sk0; else sk = NULL; break; case GTP_V1: - if (gtp->sock1u) - sk = gtp->sock1u->sk; + if (gtp->sk1u) + sk = gtp->sk1u; else sk = NULL; break; @@ -504,7 +506,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, return -ENOENT; } - rt = ip4_route_output_gtp(sock_net(sk), &fl4, gtp->sock0->sk, + rt = ip4_route_output_gtp(sock_net(sk), &fl4, gtp->sk0, pctx->sgsn_addr_ip4.s_addr); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to SSGN %pI4\n", @@ -839,18 +841,20 @@ static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp, netdev_dbg(dev, "enable gtp on %p, %p\n", sock0, sock1u); - gtp->sock0 = sock0; - gtp->sock1u = sock1u; + sock_hold(sock0->sk); + gtp->sk0 = sock0->sk; + sock_hold(sock1u->sk); + gtp->sk1u = sock1u->sk; tuncfg.sk_user_data = gtp; tuncfg.encap_rcv = gtp_encap_recv; tuncfg.encap_destroy = gtp_encap_destroy; tuncfg.encap_type = UDP_ENCAP_GTP0; - setup_udp_tunnel_sock(sock_net(gtp->sock0->sk), gtp->sock0, &tuncfg); + setup_udp_tunnel_sock(sock_net(gtp->sk0), sock0, &tuncfg); tuncfg.encap_type = UDP_ENCAP_GTP1U; - setup_udp_tunnel_sock(sock_net(gtp->sock1u->sk), gtp->sock1u, &tuncfg); + setup_udp_tunnel_sock(sock_net(gtp->sk1u), sock1u, &tuncfg); err = 0; err2: -- cgit From 1e3a3abd8b28cfda9d0d0167e50e0fe11bc372a9 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 9 Mar 2017 17:42:57 +0100 Subject: gtp: make GTP sockets in gtp_newlink optional Having both GTPv0-U and GTPv1-U is not always desirable. Fallback from GTPv1-U to GTPv0-U was depreciated from 3GPP Rel-8 onwards. Post Rel-8 implementation are discuraged from listening on the v0 port (see 3GPP TS 29.281, Sect. 1). A future change will completely decouple the sockets from the network device. Till then, at least one of the sockets needs to be specified (either v0 or v1), the other is optional. Signed-off-by: Andreas Schultz Signed-off-by: David S. Miller --- drivers/net/gtp.c | 142 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 75 insertions(+), 67 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index e1b5af377fbc..9aa2b355f2be 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -259,30 +259,30 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); } -static void gtp_encap_disable(struct gtp_dev *gtp) +static void gtp_encap_destroy(struct sock *sk) { - if (gtp->sk0) { - udp_sk(gtp->sk0)->encap_type = 0; - rcu_assign_sk_user_data(gtp->sk0, NULL); - sock_put(gtp->sk0); - } - if (gtp->sk1u) { - udp_sk(gtp->sk1u)->encap_type = 0; - rcu_assign_sk_user_data(gtp->sk1u, NULL); - sock_put(gtp->sk1u); - } + struct gtp_dev *gtp; - gtp->sk0 = NULL; - gtp->sk1u = NULL; + gtp = rcu_dereference_sk_user_data(sk); + if (gtp) { + udp_sk(sk)->encap_type = 0; + rcu_assign_sk_user_data(sk, NULL); + sock_put(sk); + } } -static void gtp_encap_destroy(struct sock *sk) +static void gtp_encap_disable_sock(struct sock *sk) { - struct gtp_dev *gtp; + if (!sk) + return; - gtp = rcu_dereference_sk_user_data(sk); - if (gtp) - gtp_encap_disable(gtp); + gtp_encap_destroy(sk); +} + +static void gtp_encap_disable(struct gtp_dev *gtp) +{ + gtp_encap_disable_sock(gtp->sk0); + gtp_encap_disable_sock(gtp->sk1u); } /* UDP encapsulation receive handler. See net/ipv4/udp.c. @@ -642,27 +642,23 @@ static void gtp_link_setup(struct net_device *dev) static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize); static void gtp_hashtable_free(struct gtp_dev *gtp); -static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp, - int fd_gtp0, int fd_gtp1); +static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]); static int gtp_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { - int hashsize, err, fd0, fd1; struct gtp_dev *gtp; struct gtp_net *gn; + int hashsize, err; - if (!data[IFLA_GTP_FD0] || !data[IFLA_GTP_FD1]) + if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1]) return -EINVAL; gtp = netdev_priv(dev); - fd0 = nla_get_u32(data[IFLA_GTP_FD0]); - fd1 = nla_get_u32(data[IFLA_GTP_FD1]); - - err = gtp_encap_enable(dev, gtp, fd0, fd1); + err = gtp_encap_enable(gtp, data); if (err < 0) - goto out_err; + return err; if (!data[IFLA_GTP_PDP_HASHSIZE]) hashsize = 1024; @@ -690,7 +686,6 @@ out_hashtable: gtp_hashtable_free(gtp); out_encap: gtp_encap_disable(gtp); -out_err: return err; } @@ -805,63 +800,76 @@ static void gtp_hashtable_free(struct gtp_dev *gtp) kfree(gtp->tid_hash); } -static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp, - int fd_gtp0, int fd_gtp1) +static struct sock *gtp_encap_enable_socket(int fd, int type, + struct gtp_dev *gtp) { struct udp_tunnel_sock_cfg tuncfg = {NULL}; - struct socket *sock0, *sock1u; + struct socket *sock; + struct sock *sk; int err; - netdev_dbg(dev, "enable gtp on %d, %d\n", fd_gtp0, fd_gtp1); + pr_debug("enable gtp on %d, %d\n", fd, type); - sock0 = sockfd_lookup(fd_gtp0, &err); - if (sock0 == NULL) { - netdev_dbg(dev, "socket fd=%d not found (gtp0)\n", fd_gtp0); - return -ENOENT; + sock = sockfd_lookup(fd, &err); + if (!sock) { + pr_debug("gtp socket fd=%d not found\n", fd); + return NULL; } - if (sock0->sk->sk_protocol != IPPROTO_UDP) { - netdev_dbg(dev, "socket fd=%d not UDP\n", fd_gtp0); - err = -EINVAL; - goto err1; + if (sock->sk->sk_protocol != IPPROTO_UDP) { + pr_debug("socket fd=%d not UDP\n", fd); + sk = ERR_PTR(-EINVAL); + goto out_sock; } - sock1u = sockfd_lookup(fd_gtp1, &err); - if (sock1u == NULL) { - netdev_dbg(dev, "socket fd=%d not found (gtp1u)\n", fd_gtp1); - err = -ENOENT; - goto err1; - } - - if (sock1u->sk->sk_protocol != IPPROTO_UDP) { - netdev_dbg(dev, "socket fd=%d not UDP\n", fd_gtp1); - err = -EINVAL; - goto err2; + if (rcu_dereference_sk_user_data(sock->sk)) { + sk = ERR_PTR(-EBUSY); + goto out_sock; } - netdev_dbg(dev, "enable gtp on %p, %p\n", sock0, sock1u); - - sock_hold(sock0->sk); - gtp->sk0 = sock0->sk; - sock_hold(sock1u->sk); - gtp->sk1u = sock1u->sk; + sk = sock->sk; + sock_hold(sk); tuncfg.sk_user_data = gtp; + tuncfg.encap_type = type; tuncfg.encap_rcv = gtp_encap_recv; tuncfg.encap_destroy = gtp_encap_destroy; - tuncfg.encap_type = UDP_ENCAP_GTP0; - setup_udp_tunnel_sock(sock_net(gtp->sk0), sock0, &tuncfg); + setup_udp_tunnel_sock(sock_net(sock->sk), sock, &tuncfg); - tuncfg.encap_type = UDP_ENCAP_GTP1U; - setup_udp_tunnel_sock(sock_net(gtp->sk1u), sock1u, &tuncfg); +out_sock: + sockfd_put(sock); + return sk; +} - err = 0; -err2: - sockfd_put(sock1u); -err1: - sockfd_put(sock0); - return err; +static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) +{ + struct sock *sk1u = NULL; + struct sock *sk0 = NULL; + + if (data[IFLA_GTP_FD0]) { + u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]); + + sk0 = gtp_encap_enable_socket(fd0, UDP_ENCAP_GTP0, gtp); + if (IS_ERR(sk0)) + return PTR_ERR(sk0); + } + + if (data[IFLA_GTP_FD1]) { + u32 fd1 = nla_get_u32(data[IFLA_GTP_FD1]); + + sk1u = gtp_encap_enable_socket(fd1, UDP_ENCAP_GTP1U, gtp); + if (IS_ERR(sk1u)) { + if (sk0) + gtp_encap_disable_sock(sk0); + return PTR_ERR(sk1u); + } + } + + gtp->sk0 = sk0; + gtp->sk1u = sk1u; + + return 0; } static struct net_device *gtp_find_dev(struct net *net, int ifindex) -- cgit From 3fb94617ca1b2f60119ae14643a83a66079fe4c9 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 9 Mar 2017 17:42:58 +0100 Subject: gtp: merge gtp_get_net and gtp_genl_find_dev Both function are always used together with the final goal to get the gtp_dev. This simplifies the code by merging them together. The netdevice lookup is changed to use the regular dev_get_by_index. The gtp netdevice list is now only used to find the PDP contexts for imcomming packets. It can be completely eliminated Once the TEID hash is moved into the GTP socket. Signed-off-by: Andreas Schultz Signed-off-by: David S. Miller --- drivers/net/gtp.c | 140 +++++++++++++++++++++++++----------------------------- 1 file changed, 66 insertions(+), 74 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 9aa2b355f2be..74018ebcaf3a 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -744,21 +744,6 @@ static struct rtnl_link_ops gtp_link_ops __read_mostly = { .fill_info = gtp_fill_info, }; -static struct net *gtp_genl_get_net(struct net *src_net, struct nlattr *tb[]) -{ - struct net *net; - - /* Examine the link attributes and figure out which network namespace - * we are talking about. - */ - if (tb[GTPA_NET_NS_FD]) - net = get_net_ns_by_fd(nla_get_u32(tb[GTPA_NET_NS_FD])); - else - net = get_net(src_net); - - return net; -} - static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize) { int i; @@ -872,16 +857,30 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) return 0; } -static struct net_device *gtp_find_dev(struct net *net, int ifindex) +static struct gtp_dev *gtp_find_dev(struct net *src_net, struct nlattr *nla[]) { - struct gtp_net *gn = net_generic(net, gtp_net_id); - struct gtp_dev *gtp; + struct gtp_dev *gtp = NULL; + struct net_device *dev; + struct net *net; - list_for_each_entry_rcu(gtp, &gn->gtp_dev_list, list) { - if (ifindex == gtp->dev->ifindex) - return gtp->dev; - } - return NULL; + /* Examine the link attributes and figure out which network namespace + * we are talking about. + */ + if (nla[GTPA_NET_NS_FD]) + net = get_net_ns_by_fd(nla_get_u32(nla[GTPA_NET_NS_FD])); + else + net = get_net(src_net); + + if (IS_ERR(net)) + return NULL; + + /* Check if there's an existing gtpX device to configure */ + dev = dev_get_by_index_rcu(net, nla_get_u32(nla[GTPA_LINK])); + if (dev->netdev_ops == >p_netdev_ops) + gtp = netdev_priv(dev); + + put_net(net); + return gtp; } static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) @@ -911,9 +910,9 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) } } -static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info) +static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) { - struct gtp_dev *gtp = netdev_priv(dev); + struct net_device *dev = gtp->dev; u32 hash_ms, hash_tid = 0; struct pdp_ctx *pctx; bool found = false; @@ -990,8 +989,8 @@ static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info) static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) { - struct net_device *dev; - struct net *net; + struct gtp_dev *gtp; + int err; if (!info->attrs[GTPA_VERSION] || !info->attrs[GTPA_LINK] || @@ -1015,77 +1014,79 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); - if (IS_ERR(net)) - return PTR_ERR(net); + rcu_read_lock(); - /* Check if there's an existing gtpX device to configure */ - dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK])); - if (dev == NULL) { - put_net(net); - return -ENODEV; + gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); + if (!gtp) { + err = -ENODEV; + goto out_unlock; } - put_net(net); - return ipv4_pdp_add(dev, info); + err = ipv4_pdp_add(gtp, info); + +out_unlock: + rcu_read_unlock(); + return err; } static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) { - struct net_device *dev; struct pdp_ctx *pctx; struct gtp_dev *gtp; - struct net *net; + int err = 0; if (!info->attrs[GTPA_VERSION] || !info->attrs[GTPA_LINK]) return -EINVAL; - net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); - if (IS_ERR(net)) - return PTR_ERR(net); + rcu_read_lock(); - /* Check if there's an existing gtpX device to configure */ - dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK])); - if (dev == NULL) { - put_net(net); - return -ENODEV; + gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); + if (!gtp) { + err = -ENODEV; + goto out_unlock; } - put_net(net); - - gtp = netdev_priv(dev); switch (nla_get_u32(info->attrs[GTPA_VERSION])) { case GTP_V0: - if (!info->attrs[GTPA_TID]) - return -EINVAL; + if (!info->attrs[GTPA_TID]) { + err = -EINVAL; + goto out_unlock; + } pctx = gtp0_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_TID])); break; case GTP_V1: - if (!info->attrs[GTPA_I_TEI]) - return -EINVAL; + if (!info->attrs[GTPA_I_TEI]) { + err = -EINVAL; + goto out_unlock; + } pctx = gtp1_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_I_TEI])); break; default: - return -EINVAL; + err = -EINVAL; + goto out_unlock; } - if (pctx == NULL) - return -ENOENT; + if (!pctx) { + err = -ENOENT; + goto out_unlock; + } if (pctx->gtp_version == GTP_V0) - netdev_dbg(dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", + netdev_dbg(gtp->dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", pctx->u.v0.tid, pctx); else if (pctx->gtp_version == GTP_V1) - netdev_dbg(dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", + netdev_dbg(gtp->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); hlist_del_rcu(&pctx->hlist_tid); hlist_del_rcu(&pctx->hlist_addr); kfree_rcu(pctx, rcu_head); - return 0; +out_unlock: + rcu_read_unlock(); + return err; } static struct genl_family gtp_genl_family; @@ -1129,11 +1130,9 @@ nla_put_failure: static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info) { struct pdp_ctx *pctx = NULL; - struct net_device *dev; struct sk_buff *skb2; struct gtp_dev *gtp; u32 gtp_version; - struct net *net; int err; if (!info->attrs[GTPA_VERSION] || @@ -1149,21 +1148,14 @@ static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); - if (IS_ERR(net)) - return PTR_ERR(net); + rcu_read_lock(); - /* Check if there's an existing gtpX device to configure */ - dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK])); - if (dev == NULL) { - put_net(net); - return -ENODEV; + gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); + if (!gtp) { + err = -ENODEV; + goto err_unlock; } - put_net(net); - - gtp = netdev_priv(dev); - rcu_read_lock(); if (gtp_version == GTP_V0 && info->attrs[GTPA_TID]) { u64 tid = nla_get_u64(info->attrs[GTPA_TID]); -- cgit From 5b171f9cfe5e628c3537be159fa36fff1f5cec8f Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 9 Mar 2017 17:42:59 +0100 Subject: gtp: consolidate gtp socket rx path Add network device to gtp context in preparation for splitting the TEID from the network device. Use this to rework the socker rx path. Move the common RX part of v0 and v1 into a helper. Also move the final rx part into that helper as well. Signed-off-by: Andreas Schultz Signed-off-by: David S. Miller --- drivers/net/gtp.c | 80 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 74018ebcaf3a..0c6707a0d9d3 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -58,6 +58,8 @@ struct pdp_ctx { struct in_addr ms_addr_ip4; struct in_addr sgsn_addr_ip4; + struct net_device *dev; + atomic_t tx_seq; struct rcu_head rcu_head; }; @@ -175,6 +177,40 @@ static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx, return false; } +static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen, + bool xnet) +{ + struct pcpu_sw_netstats *stats; + + if (!gtp_check_src_ms(skb, pctx, hdrlen)) { + netdev_dbg(pctx->dev, "No PDP ctx for this MS\n"); + return 1; + } + + /* Get rid of the GTP + UDP headers. */ + if (iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet)) + return -1; + + netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n"); + + /* Now that the UDP and the GTP header have been removed, set up the + * new network header. This is required by the upper layer to + * calculate the transport header. + */ + skb_reset_network_header(skb); + + skb->dev = pctx->dev; + + stats = this_cpu_ptr(pctx->dev->tstats); + u64_stats_update_begin(&stats->syncp); + stats->rx_packets++; + stats->rx_bytes += skb->len; + u64_stats_update_end(&stats->syncp); + + netif_rx(skb); + return 0; +} + /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, bool xnet) @@ -201,13 +237,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return 1; } - if (!gtp_check_src_ms(skb, pctx, hdrlen)) { - netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); - return 1; - } - - /* Get rid of the GTP + UDP headers. */ - return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); + return gtp_rx(pctx, skb, hdrlen, xnet); } static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, @@ -250,13 +280,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return 1; } - if (!gtp_check_src_ms(skb, pctx, hdrlen)) { - netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); - return 1; - } - - /* Get rid of the GTP + UDP headers. */ - return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); + return gtp_rx(pctx, skb, hdrlen, xnet); } static void gtp_encap_destroy(struct sock *sk) @@ -290,10 +314,9 @@ static void gtp_encap_disable(struct gtp_dev *gtp) */ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) { - struct pcpu_sw_netstats *stats; struct gtp_dev *gtp; + int ret = 0; bool xnet; - int ret; gtp = rcu_dereference_sk_user_data(sk); if (!gtp) @@ -319,33 +342,17 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) switch (ret) { case 1: netdev_dbg(gtp->dev, "pass up to the process\n"); - return 1; + break; case 0: - netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n"); break; case -1: netdev_dbg(gtp->dev, "GTP packet has been dropped\n"); kfree_skb(skb); - return 0; + ret = 0; + break; } - /* Now that the UDP and the GTP header have been removed, set up the - * new network header. This is required by the upper layer to - * calculate the transport header. - */ - skb_reset_network_header(skb); - - skb->dev = gtp->dev; - - stats = this_cpu_ptr(gtp->dev->tstats); - u64_stats_update_begin(&stats->syncp); - stats->rx_packets++; - stats->rx_bytes += skb->len; - u64_stats_update_end(&stats->syncp); - - netif_rx(skb); - - return 0; + return ret; } static int gtp_dev_init(struct net_device *dev) @@ -951,6 +958,7 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) if (pctx == NULL) return -ENOMEM; + pctx->dev = gtp->dev; ipv4_pdp_fill(pctx, info); atomic_set(&pctx->tx_seq, 0); -- cgit From d9e2dd122637034a0697bf268eed9233701b9dca Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 9 Mar 2017 17:43:00 +0100 Subject: gtp: unify genl_find_pdp and prepare for per socket lookup This unifies duplicate code into a helper. It also prepares the groundwork to add a lookup version that uses the socket to find attached pdp contexts. Signed-off-by: Andreas Schultz Signed-off-by: David S. Miller --- drivers/net/gtp.c | 121 ++++++++++++++++++++++-------------------------------- 1 file changed, 50 insertions(+), 71 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 0c6707a0d9d3..bf4b352e06c4 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -1037,55 +1037,67 @@ out_unlock: return err; } -static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) +static struct pdp_ctx *gtp_find_pdp_by_link(struct net *net, + struct nlattr *nla[]) { - struct pdp_ctx *pctx; struct gtp_dev *gtp; - int err = 0; - if (!info->attrs[GTPA_VERSION] || - !info->attrs[GTPA_LINK]) - return -EINVAL; + gtp = gtp_find_dev(net, nla); + if (!gtp) + return ERR_PTR(-ENODEV); - rcu_read_lock(); + if (nla[GTPA_MS_ADDRESS]) { + __be32 ip = nla_get_be32(nla[GTPA_MS_ADDRESS]); - gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); - if (!gtp) { - err = -ENODEV; - goto out_unlock; + return ipv4_pdp_find(gtp, ip); + } else if (nla[GTPA_VERSION]) { + u32 gtp_version = nla_get_u32(nla[GTPA_VERSION]); + + if (gtp_version == GTP_V0 && nla[GTPA_TID]) + return gtp0_pdp_find(gtp, nla_get_u64(nla[GTPA_TID])); + else if (gtp_version == GTP_V1 && nla[GTPA_I_TEI]) + return gtp1_pdp_find(gtp, nla_get_u32(nla[GTPA_I_TEI])); } - switch (nla_get_u32(info->attrs[GTPA_VERSION])) { - case GTP_V0: - if (!info->attrs[GTPA_TID]) { - err = -EINVAL; - goto out_unlock; - } - pctx = gtp0_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_TID])); - break; - case GTP_V1: - if (!info->attrs[GTPA_I_TEI]) { - err = -EINVAL; - goto out_unlock; - } - pctx = gtp1_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_I_TEI])); - break; + return ERR_PTR(-EINVAL); +} - default: - err = -EINVAL; - goto out_unlock; - } +static struct pdp_ctx *gtp_find_pdp(struct net *net, struct nlattr *nla[]) +{ + struct pdp_ctx *pctx; - if (!pctx) { - err = -ENOENT; + if (nla[GTPA_LINK]) + pctx = gtp_find_pdp_by_link(net, nla); + else + pctx = ERR_PTR(-EINVAL); + + if (!pctx) + pctx = ERR_PTR(-ENOENT); + + return pctx; +} + +static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) +{ + struct pdp_ctx *pctx; + int err = 0; + + if (!info->attrs[GTPA_VERSION]) + return -EINVAL; + + rcu_read_lock(); + + pctx = gtp_find_pdp(sock_net(skb->sk), info->attrs); + if (IS_ERR(pctx)) { + err = PTR_ERR(pctx); goto out_unlock; } if (pctx->gtp_version == GTP_V0) - netdev_dbg(gtp->dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", + netdev_dbg(pctx->dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", pctx->u.v0.tid, pctx); else if (pctx->gtp_version == GTP_V1) - netdev_dbg(gtp->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", + netdev_dbg(pctx->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); hlist_del_rcu(&pctx->hlist_tid); @@ -1139,49 +1151,16 @@ static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info) { struct pdp_ctx *pctx = NULL; struct sk_buff *skb2; - struct gtp_dev *gtp; - u32 gtp_version; int err; - if (!info->attrs[GTPA_VERSION] || - !info->attrs[GTPA_LINK]) - return -EINVAL; - - gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]); - switch (gtp_version) { - case GTP_V0: - case GTP_V1: - break; - default: + if (!info->attrs[GTPA_VERSION]) return -EINVAL; - } rcu_read_lock(); - gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); - if (!gtp) { - err = -ENODEV; - goto err_unlock; - } - - if (gtp_version == GTP_V0 && - info->attrs[GTPA_TID]) { - u64 tid = nla_get_u64(info->attrs[GTPA_TID]); - - pctx = gtp0_pdp_find(gtp, tid); - } else if (gtp_version == GTP_V1 && - info->attrs[GTPA_I_TEI]) { - u32 tid = nla_get_u32(info->attrs[GTPA_I_TEI]); - - pctx = gtp1_pdp_find(gtp, tid); - } else if (info->attrs[GTPA_MS_ADDRESS]) { - __be32 ip = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); - - pctx = ipv4_pdp_find(gtp, ip); - } - - if (pctx == NULL) { - err = -ENOENT; + pctx = gtp_find_pdp(sock_net(skb->sk), info->attrs); + if (IS_ERR(pctx)) { + err = PTR_ERR(pctx); goto err_unlock; } -- cgit From 6b5e2e7401de30903ad05d3ef18e3a72d835ce24 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 9 Mar 2017 17:43:01 +0100 Subject: gtp: consolidate pdp context destruction into helper Consolidate duplicate code into helper. Signed-off-by: Andreas Schultz Signed-off-by: David S. Miller --- drivers/net/gtp.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index bf4b352e06c4..d58f46fd766a 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -86,6 +86,8 @@ struct gtp_net { static u32 gtp_h_initval; +static void pdp_context_delete(struct pdp_ctx *pctx); + static inline u32 gtp0_hashfn(u64 tid) { u32 *tid32 = (u32 *) &tid; @@ -780,13 +782,10 @@ static void gtp_hashtable_free(struct gtp_dev *gtp) struct pdp_ctx *pctx; int i; - for (i = 0; i < gtp->hash_size; i++) { - hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) { - hlist_del_rcu(&pctx->hlist_tid); - hlist_del_rcu(&pctx->hlist_addr); - kfree_rcu(pctx, rcu_head); - } - } + for (i = 0; i < gtp->hash_size; i++) + hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) + pdp_context_delete(pctx); + synchronize_rcu(); kfree(gtp->addr_hash); kfree(gtp->tid_hash); @@ -995,6 +994,13 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) return 0; } +static void pdp_context_delete(struct pdp_ctx *pctx) +{ + hlist_del_rcu(&pctx->hlist_tid); + hlist_del_rcu(&pctx->hlist_addr); + kfree_rcu(pctx, rcu_head); +} + static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) { struct gtp_dev *gtp; @@ -1100,9 +1106,7 @@ static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) netdev_dbg(pctx->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); - hlist_del_rcu(&pctx->hlist_tid); - hlist_del_rcu(&pctx->hlist_addr); - kfree_rcu(pctx, rcu_head); + pdp_context_delete(pctx); out_unlock: rcu_read_unlock(); -- cgit From 101cfbc155d082e8567f697f86f89853c790be5e Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Thu, 9 Mar 2017 17:43:02 +0100 Subject: gtp: add socket to pdp context Having the socket present in context simplifies the sending logic. It also fixes the invalid assumption that we have to use the same sending socket for all client IP's on a specific gtp interface. Signed-off-by: Andreas Schultz Signed-off-by: David S. Miller --- drivers/net/gtp.c | 94 +++++++++++++++++++++++++++---------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index d58f46fd766a..3e1854f34420 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -58,6 +58,7 @@ struct pdp_ctx { struct in_addr ms_addr_ip4; struct in_addr sgsn_addr_ip4; + struct sock *sk; struct net_device *dev; atomic_t tx_seq; @@ -179,8 +180,7 @@ static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx, return false; } -static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen, - bool xnet) +static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen) { struct pcpu_sw_netstats *stats; @@ -190,7 +190,8 @@ static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen } /* Get rid of the GTP + UDP headers. */ - if (iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet)) + if (iptunnel_pull_header(skb, hdrlen, skb->protocol, + !net_eq(sock_net(pctx->sk), dev_net(pctx->dev)))) return -1; netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n"); @@ -214,8 +215,7 @@ static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen } /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */ -static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, - bool xnet) +static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) { unsigned int hdrlen = sizeof(struct udphdr) + sizeof(struct gtp0_header); @@ -239,11 +239,10 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return 1; } - return gtp_rx(pctx, skb, hdrlen, xnet); + return gtp_rx(pctx, skb, hdrlen); } -static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, - bool xnet) +static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) { unsigned int hdrlen = sizeof(struct udphdr) + sizeof(struct gtp1_header); @@ -282,7 +281,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return 1; } - return gtp_rx(pctx, skb, hdrlen, xnet); + return gtp_rx(pctx, skb, hdrlen); } static void gtp_encap_destroy(struct sock *sk) @@ -318,7 +317,6 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) { struct gtp_dev *gtp; int ret = 0; - bool xnet; gtp = rcu_dereference_sk_user_data(sk); if (!gtp) @@ -326,16 +324,14 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk); - xnet = !net_eq(sock_net(sk), dev_net(gtp->dev)); - switch (udp_sk(sk)->encap_type) { case UDP_ENCAP_GTP0: netdev_dbg(gtp->dev, "received GTP0 packet\n"); - ret = gtp0_udp_encap_recv(gtp, skb, xnet); + ret = gtp0_udp_encap_recv(gtp, skb); break; case UDP_ENCAP_GTP1U: netdev_dbg(gtp->dev, "received GTP1U packet\n"); - ret = gtp1u_udp_encap_recv(gtp, skb, xnet); + ret = gtp1u_udp_encap_recv(gtp, skb); break; default: ret = -1; /* Shouldn't happen. */ @@ -378,8 +374,9 @@ static void gtp_dev_uninit(struct net_device *dev) free_percpu(dev->tstats); } -static struct rtable *ip4_route_output_gtp(struct net *net, struct flowi4 *fl4, - const struct sock *sk, __be32 daddr) +static struct rtable *ip4_route_output_gtp(struct flowi4 *fl4, + const struct sock *sk, + __be32 daddr) { memset(fl4, 0, sizeof(*fl4)); fl4->flowi4_oif = sk->sk_bound_dev_if; @@ -388,7 +385,7 @@ static struct rtable *ip4_route_output_gtp(struct net *net, struct flowi4 *fl4, fl4->flowi4_tos = RT_CONN_FLAGS(sk); fl4->flowi4_proto = sk->sk_protocol; - return ip_route_output_key(net, fl4); + return ip_route_output_key(sock_net(sk), fl4); } static inline void gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) @@ -477,7 +474,6 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, struct rtable *rt; struct flowi4 fl4; struct iphdr *iph; - struct sock *sk; __be16 df; int mtu; @@ -493,30 +489,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, } netdev_dbg(dev, "found PDP context %p\n", pctx); - switch (pctx->gtp_version) { - case GTP_V0: - if (gtp->sk0) - sk = gtp->sk0; - else - sk = NULL; - break; - case GTP_V1: - if (gtp->sk1u) - sk = gtp->sk1u; - else - sk = NULL; - break; - default: - return -ENOENT; - } - - if (!sk) { - netdev_dbg(dev, "no userspace socket is available, skip\n"); - return -ENOENT; - } - - rt = ip4_route_output_gtp(sock_net(sk), &fl4, gtp->sk0, - pctx->sgsn_addr_ip4.s_addr); + rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->sgsn_addr_ip4.s_addr); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to SSGN %pI4\n", &pctx->sgsn_addr_ip4.s_addr); @@ -561,7 +534,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, goto err_rt; } - gtp_set_pktinfo_ipv4(pktinfo, sk, iph, pctx, rt, &fl4, dev); + gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, iph, pctx, rt, &fl4, dev); gtp_push_header(skb, pktinfo); return 0; @@ -916,7 +889,8 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) } } -static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) +static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk, + struct genl_info *info) { struct net_device *dev = gtp->dev; u32 hash_ms, hash_tid = 0; @@ -957,6 +931,8 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) if (pctx == NULL) return -ENOMEM; + sock_hold(sk); + pctx->sk = sk; pctx->dev = gtp->dev; ipv4_pdp_fill(pctx, info); atomic_set(&pctx->tx_seq, 0); @@ -994,16 +970,26 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) return 0; } +static void pdp_context_free(struct rcu_head *head) +{ + struct pdp_ctx *pctx = container_of(head, struct pdp_ctx, rcu_head); + + sock_put(pctx->sk); + kfree(pctx); +} + static void pdp_context_delete(struct pdp_ctx *pctx) { hlist_del_rcu(&pctx->hlist_tid); hlist_del_rcu(&pctx->hlist_addr); - kfree_rcu(pctx, rcu_head); + call_rcu(&pctx->rcu_head, pdp_context_free); } static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) { + unsigned int version; struct gtp_dev *gtp; + struct sock *sk; int err; if (!info->attrs[GTPA_VERSION] || @@ -1012,7 +998,9 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) !info->attrs[GTPA_MS_ADDRESS]) return -EINVAL; - switch (nla_get_u32(info->attrs[GTPA_VERSION])) { + version = nla_get_u32(info->attrs[GTPA_VERSION]); + + switch (version) { case GTP_V0: if (!info->attrs[GTPA_TID] || !info->attrs[GTPA_FLOW]) @@ -1036,7 +1024,19 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) goto out_unlock; } - err = ipv4_pdp_add(gtp, info); + if (version == GTP_V0) + sk = gtp->sk0; + else if (version == GTP_V1) + sk = gtp->sk1u; + else + sk = NULL; + + if (!sk) { + err = -ENODEV; + goto out_unlock; + } + + err = ipv4_pdp_add(gtp, sk, info); out_unlock: rcu_read_unlock(); -- cgit From a062d19e941071bac9f67b61a4bd5f851fd4ede9 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 9 Mar 2017 23:10:13 +0100 Subject: net: net_netdev: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ntb_netdev.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c index 36877ba65516..4daf3d0926a8 100644 --- a/drivers/net/ntb_netdev.c +++ b/drivers/net/ntb_netdev.c @@ -372,18 +372,19 @@ static void ntb_get_drvinfo(struct net_device *ndev, strlcpy(info->bus_info, pci_name(dev->pdev), sizeof(info->bus_info)); } -static int ntb_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int ntb_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { - cmd->supported = SUPPORTED_Backplane; - cmd->advertising = ADVERTISED_Backplane; - ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN); - cmd->duplex = DUPLEX_FULL; - cmd->port = PORT_OTHER; - cmd->phy_address = 0; - cmd->transceiver = XCVR_DUMMY1; - cmd->autoneg = AUTONEG_ENABLE; - cmd->maxtxpkt = 0; - cmd->maxrxpkt = 0; + ethtool_link_ksettings_zero_link_mode(cmd, supported); + ethtool_link_ksettings_add_link_mode(cmd, supported, Backplane); + ethtool_link_ksettings_zero_link_mode(cmd, advertising); + ethtool_link_ksettings_add_link_mode(cmd, advertising, Backplane); + + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_FULL; + cmd->base.port = PORT_OTHER; + cmd->base.phy_address = 0; + cmd->base.autoneg = AUTONEG_ENABLE; return 0; } @@ -391,7 +392,7 @@ static int ntb_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) static const struct ethtool_ops ntb_ethtool_ops = { .get_drvinfo = ntb_get_drvinfo, .get_link = ethtool_op_get_link, - .get_settings = ntb_get_settings, + .get_link_ksettings = ntb_get_link_ksettings, }; static const struct ntb_queue_handlers ntb_netdev_handlers = { -- cgit From 29ccc49d69615818743b2d31b16d698b6eea8aad Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 11 Mar 2017 22:03:50 +0100 Subject: net: tun: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/tun.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index dc1b1dd9157c..c418f0a9d2d1 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2430,18 +2430,16 @@ static struct miscdevice tun_miscdev = { /* ethtool interface */ -static int tun_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - cmd->supported = 0; - cmd->advertising = 0; - ethtool_cmd_speed_set(cmd, SPEED_10); - cmd->duplex = DUPLEX_FULL; - cmd->port = PORT_TP; - cmd->phy_address = 0; - cmd->transceiver = XCVR_INTERNAL; - cmd->autoneg = AUTONEG_DISABLE; - cmd->maxtxpkt = 0; - cmd->maxrxpkt = 0; +static int tun_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) +{ + ethtool_link_ksettings_zero_link_mode(cmd, supported); + ethtool_link_ksettings_zero_link_mode(cmd, advertising); + cmd->base.speed = SPEED_10; + cmd->base.duplex = DUPLEX_FULL; + cmd->base.port = PORT_TP; + cmd->base.phy_address = 0; + cmd->base.autoneg = AUTONEG_DISABLE; return 0; } @@ -2504,7 +2502,6 @@ static int tun_set_coalesce(struct net_device *dev, } static const struct ethtool_ops tun_ethtool_ops = { - .get_settings = tun_get_settings, .get_drvinfo = tun_get_drvinfo, .get_msglevel = tun_get_msglevel, .set_msglevel = tun_set_msglevel, @@ -2512,6 +2509,7 @@ static const struct ethtool_ops tun_ethtool_ops = { .get_ts_info = ethtool_op_get_ts_info, .get_coalesce = tun_get_coalesce, .set_coalesce = tun_set_coalesce, + .get_link_ksettings = tun_get_link_ksettings, }; static int tun_queue_resize(struct tun_struct *tun) -- cgit From 3c9b803b87b5ecb4f15b24886f7f97116cc4e1d8 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 12 Mar 2017 18:02:36 +0100 Subject: net: usb: asix88179_178a: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/usb/ax88179_178a.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index a3a7db0702d8..4a0ae7ce83f6 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -620,16 +620,18 @@ ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, return 0; } -static int ax88179_get_settings(struct net_device *net, struct ethtool_cmd *cmd) +static int ax88179_get_link_ksettings(struct net_device *net, + struct ethtool_link_ksettings *cmd) { struct usbnet *dev = netdev_priv(net); - return mii_ethtool_gset(&dev->mii, cmd); + return mii_ethtool_get_link_ksettings(&dev->mii, cmd); } -static int ax88179_set_settings(struct net_device *net, struct ethtool_cmd *cmd) +static int ax88179_set_link_ksettings(struct net_device *net, + const struct ethtool_link_ksettings *cmd) { struct usbnet *dev = netdev_priv(net); - return mii_ethtool_sset(&dev->mii, cmd); + return mii_ethtool_set_link_ksettings(&dev->mii, cmd); } static int @@ -826,11 +828,11 @@ static const struct ethtool_ops ax88179_ethtool_ops = { .set_wol = ax88179_set_wol, .get_eeprom_len = ax88179_get_eeprom_len, .get_eeprom = ax88179_get_eeprom, - .get_settings = ax88179_get_settings, - .set_settings = ax88179_set_settings, .get_eee = ax88179_get_eee, .set_eee = ax88179_set_eee, .nway_reset = usbnet_nway_reset, + .get_link_ksettings = ax88179_get_link_ksettings, + .set_link_ksettings = ax88179_set_link_ksettings, }; static void ax88179_set_multicast(struct net_device *net) -- cgit From de1e98c69bc22870342a099c2fc1257b09b15b7a Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 12 Mar 2017 22:08:26 +0100 Subject: net: usb: catc: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/usb/catc.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c index 0acc9b640419..fce92f0e5abd 100644 --- a/drivers/net/usb/catc.c +++ b/drivers/net/usb/catc.c @@ -688,29 +688,34 @@ static void catc_get_drvinfo(struct net_device *dev, usb_make_path(catc->usbdev, info->bus_info, sizeof(info->bus_info)); } -static int catc_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int catc_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct catc *catc = netdev_priv(dev); if (!catc->is_f5u011) return -EOPNOTSUPP; - cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_TP; - cmd->advertising = ADVERTISED_10baseT_Half | ADVERTISED_TP; - ethtool_cmd_speed_set(cmd, SPEED_10); - cmd->duplex = DUPLEX_HALF; - cmd->port = PORT_TP; - cmd->phy_address = 0; - cmd->transceiver = XCVR_INTERNAL; - cmd->autoneg = AUTONEG_DISABLE; - cmd->maxtxpkt = 1; - cmd->maxrxpkt = 1; + ethtool_link_ksettings_zero_link_mode(cmd, supported); + ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Half); + ethtool_link_ksettings_add_link_mode(cmd, supported, TP); + + ethtool_link_ksettings_zero_link_mode(cmd, advertising); + ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Half); + ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); + + cmd->base.speed = SPEED_10; + cmd->base.duplex = DUPLEX_HALF; + cmd->base.port = PORT_TP; + cmd->base.phy_address = 0; + cmd->base.autoneg = AUTONEG_DISABLE; + return 0; } static const struct ethtool_ops ops = { .get_drvinfo = catc_get_drvinfo, - .get_settings = catc_get_settings, - .get_link = ethtool_op_get_link + .get_link = ethtool_op_get_link, + .get_link_ksettings = catc_get_link_ksettings, }; /* -- cgit From 06144dcfd3b473a0b21c6c836ceb0a9cc4aab1c3 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 12 Mar 2017 22:41:58 +0100 Subject: net: usb: r8152: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Acked-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 986243c932cc..227e1fdd9228 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -3800,7 +3800,8 @@ static void rtl8152_get_drvinfo(struct net_device *netdev, } static -int rtl8152_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd) +int rtl8152_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct r8152 *tp = netdev_priv(netdev); int ret; @@ -3814,7 +3815,7 @@ int rtl8152_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd) mutex_lock(&tp->control); - ret = mii_ethtool_gset(&tp->mii, cmd); + ret = mii_ethtool_get_link_ksettings(&tp->mii, cmd); mutex_unlock(&tp->control); @@ -3824,7 +3825,8 @@ out: return ret; } -static int rtl8152_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int rtl8152_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct r8152 *tp = netdev_priv(dev); int ret; @@ -3835,11 +3837,12 @@ static int rtl8152_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) mutex_lock(&tp->control); - ret = rtl8152_set_speed(tp, cmd->autoneg, cmd->speed, cmd->duplex); + ret = rtl8152_set_speed(tp, cmd->base.autoneg, cmd->base.speed, + cmd->base.duplex); if (!ret) { - tp->autoneg = cmd->autoneg; - tp->speed = cmd->speed; - tp->duplex = cmd->duplex; + tp->autoneg = cmd->base.autoneg; + tp->speed = cmd->base.speed; + tp->duplex = cmd->base.duplex; } mutex_unlock(&tp->control); @@ -4117,8 +4120,6 @@ static int rtl8152_set_coalesce(struct net_device *netdev, static const struct ethtool_ops ops = { .get_drvinfo = rtl8152_get_drvinfo, - .get_settings = rtl8152_get_settings, - .set_settings = rtl8152_set_settings, .get_link = ethtool_op_get_link, .nway_reset = rtl8152_nway_reset, .get_msglevel = rtl8152_get_msglevel, @@ -4132,6 +4133,8 @@ static const struct ethtool_ops ops = { .set_coalesce = rtl8152_set_coalesce, .get_eee = rtl_ethtool_get_eee, .set_eee = rtl_ethtool_set_eee, + .get_link_ksettings = rtl8152_get_link_ksettings, + .set_link_ksettings = rtl8152_set_link_ksettings, }; static int rtl8152_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -- cgit From b66239b6824f387361bf18747a7e93760ac3c0fb Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 12 Mar 2017 23:16:25 +0100 Subject: net: usb: rtl8150: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/usb/rtl8150.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index c81c79110cef..daaa88a66f40 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -791,47 +791,52 @@ static void rtl8150_get_drvinfo(struct net_device *netdev, struct ethtool_drvinf usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info)); } -static int rtl8150_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +static int rtl8150_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *ecmd) { rtl8150_t *dev = netdev_priv(netdev); short lpa, bmcr; + u32 supported; - ecmd->supported = (SUPPORTED_10baseT_Half | + supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII); - ecmd->port = PORT_TP; - ecmd->transceiver = XCVR_INTERNAL; - ecmd->phy_address = dev->phy; + ecmd->base.port = PORT_TP; + ecmd->base.phy_address = dev->phy; get_registers(dev, BMCR, 2, &bmcr); get_registers(dev, ANLP, 2, &lpa); if (bmcr & BMCR_ANENABLE) { u32 speed = ((lpa & (LPA_100HALF | LPA_100FULL)) ? SPEED_100 : SPEED_10); - ethtool_cmd_speed_set(ecmd, speed); - ecmd->autoneg = AUTONEG_ENABLE; + ecmd->base.speed = speed; + ecmd->base.autoneg = AUTONEG_ENABLE; if (speed == SPEED_100) - ecmd->duplex = (lpa & LPA_100FULL) ? + ecmd->base.duplex = (lpa & LPA_100FULL) ? DUPLEX_FULL : DUPLEX_HALF; else - ecmd->duplex = (lpa & LPA_10FULL) ? + ecmd->base.duplex = (lpa & LPA_10FULL) ? DUPLEX_FULL : DUPLEX_HALF; } else { - ecmd->autoneg = AUTONEG_DISABLE; - ethtool_cmd_speed_set(ecmd, ((bmcr & BMCR_SPEED100) ? - SPEED_100 : SPEED_10)); - ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? + ecmd->base.autoneg = AUTONEG_DISABLE; + ecmd->base.speed = ((bmcr & BMCR_SPEED100) ? + SPEED_100 : SPEED_10); + ecmd->base.duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; } + + ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.supported, + supported); + return 0; } static const struct ethtool_ops ops = { .get_drvinfo = rtl8150_get_drvinfo, - .get_settings = rtl8150_get_settings, - .get_link = ethtool_op_get_link + .get_link = ethtool_op_get_link, + .get_link_ksettings = rtl8150_get_link_ksettings, }; static int rtl8150_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -- cgit From 5b441ac8784c1e7f3c619f14da4c3f52e87348d5 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Fri, 10 Mar 2017 20:43:24 +0000 Subject: mpls: allow TTL propagation to IP packets to be configured Provide the ability to control on a per-route basis whether the TTL value from an MPLS packet is propagated to an IPv4/IPv6 packet when the last label is popped as per the theoretical model in RFC 3443 through a new route attribute, RTA_TTL_PROPAGATE which can be 0 to mean disable propagation and 1 to mean enable propagation. In order to provide the ability to change the behaviour for packets arriving with IPv4/IPv6 Explicit Null labels and to provide an easy way for a user to change the behaviour for all existing routes without having to reprogram them, a global knob is provided. This is done through the addition of a new per-namespace sysctl, "net.mpls.ip_ttl_propagate", which defaults to enabled. If the per-route attribute is set (either enabled or disabled) then it overrides the global configuration. Signed-off-by: Robert Shearman Acked-by: David Ahern Tested-by: David Ahern Signed-off-by: David S. Miller --- Documentation/networking/mpls-sysctl.txt | 11 ++++ include/net/netns/mpls.h | 2 + include/uapi/linux/rtnetlink.h | 1 + net/mpls/af_mpls.c | 87 +++++++++++++++++++++++++++++--- net/mpls/internal.h | 7 +++ 5 files changed, 100 insertions(+), 8 deletions(-) diff --git a/Documentation/networking/mpls-sysctl.txt b/Documentation/networking/mpls-sysctl.txt index 15d8d16934fd..9badd1d6685f 100644 --- a/Documentation/networking/mpls-sysctl.txt +++ b/Documentation/networking/mpls-sysctl.txt @@ -19,6 +19,17 @@ platform_labels - INTEGER Possible values: 0 - 1048575 Default: 0 +ip_ttl_propagate - BOOL + Control whether TTL is propagated from the IPv4/IPv6 header to + the MPLS header on imposing labels and propagated from the + MPLS header to the IPv4/IPv6 header on popping the last label. + + If disabled, the MPLS transport network will appear as a + single hop to transit traffic. + + 0 - disabled / RFC 3443 [Short] Pipe Model + 1 - enabled / RFC 3443 Uniform Model (default) + conf//input - BOOL Control whether packets can be input on this interface. diff --git a/include/net/netns/mpls.h b/include/net/netns/mpls.h index d29203651c01..08652eedabb2 100644 --- a/include/net/netns/mpls.h +++ b/include/net/netns/mpls.h @@ -9,8 +9,10 @@ struct mpls_route; struct ctl_table_header; struct netns_mpls { + int ip_ttl_propagate; size_t platform_labels; struct mpls_route __rcu * __rcu *platform_label; + struct ctl_table_header *ctl; }; diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 75fcf5eff093..3dd72aee4d32 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -319,6 +319,7 @@ enum rtattr_type_t { RTA_EXPIRES, RTA_PAD, RTA_UID, + RTA_TTL_PROPAGATE, __RTA_MAX }; diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 3818686182b2..0e1046f21af8 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -32,6 +32,7 @@ #define MPLS_NEIGH_TABLE_UNSPEC (NEIGH_LINK_TABLE + 1) static int zero = 0; +static int one = 1; static int label_limit = (1 << 20) - 1; static void rtmsg_lfib(int event, u32 label, struct mpls_route *rt, @@ -220,8 +221,8 @@ out: return &rt->rt_nh[nh_index]; } -static bool mpls_egress(struct mpls_route *rt, struct sk_buff *skb, - struct mpls_entry_decoded dec) +static bool mpls_egress(struct net *net, struct mpls_route *rt, + struct sk_buff *skb, struct mpls_entry_decoded dec) { enum mpls_payload_type payload_type; bool success = false; @@ -246,22 +247,46 @@ static bool mpls_egress(struct mpls_route *rt, struct sk_buff *skb, switch (payload_type) { case MPT_IPV4: { struct iphdr *hdr4 = ip_hdr(skb); + u8 new_ttl; skb->protocol = htons(ETH_P_IP); + + /* If propagating TTL, take the decremented TTL from + * the incoming MPLS header, otherwise decrement the + * TTL, but only if not 0 to avoid underflow. + */ + if (rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED || + (rt->rt_ttl_propagate == MPLS_TTL_PROP_DEFAULT && + net->mpls.ip_ttl_propagate)) + new_ttl = dec.ttl; + else + new_ttl = hdr4->ttl ? hdr4->ttl - 1 : 0; + csum_replace2(&hdr4->check, htons(hdr4->ttl << 8), - htons(dec.ttl << 8)); - hdr4->ttl = dec.ttl; + htons(new_ttl << 8)); + hdr4->ttl = new_ttl; success = true; break; } case MPT_IPV6: { struct ipv6hdr *hdr6 = ipv6_hdr(skb); skb->protocol = htons(ETH_P_IPV6); - hdr6->hop_limit = dec.ttl; + + /* If propagating TTL, take the decremented TTL from + * the incoming MPLS header, otherwise decrement the + * hop limit, but only if not 0 to avoid underflow. + */ + if (rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED || + (rt->rt_ttl_propagate == MPLS_TTL_PROP_DEFAULT && + net->mpls.ip_ttl_propagate)) + hdr6->hop_limit = dec.ttl; + else if (hdr6->hop_limit) + hdr6->hop_limit = hdr6->hop_limit - 1; success = true; break; } case MPT_UNSPEC: + /* Should have decided which protocol it is by now */ break; } @@ -361,7 +386,7 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev, if (unlikely(!new_header_size && dec.bos)) { /* Penultimate hop popping */ - if (!mpls_egress(rt, skb, dec)) + if (!mpls_egress(dev_net(out_dev), rt, skb, dec)) goto err; } else { bool bos; @@ -412,6 +437,7 @@ static struct packet_type mpls_packet_type __read_mostly = { static const struct nla_policy rtm_mpls_policy[RTA_MAX+1] = { [RTA_DST] = { .type = NLA_U32 }, [RTA_OIF] = { .type = NLA_U32 }, + [RTA_TTL_PROPAGATE] = { .type = NLA_U8 }, }; struct mpls_route_config { @@ -421,6 +447,7 @@ struct mpls_route_config { u8 rc_via_alen; u8 rc_via[MAX_VIA_ALEN]; u32 rc_label; + u8 rc_ttl_propagate; u8 rc_output_labels; u32 rc_output_label[MAX_NEW_LABELS]; u32 rc_nlflags; @@ -856,6 +883,7 @@ static int mpls_route_add(struct mpls_route_config *cfg) rt->rt_protocol = cfg->rc_protocol; rt->rt_payload_type = cfg->rc_payload_type; + rt->rt_ttl_propagate = cfg->rc_ttl_propagate; if (cfg->rc_mp) err = mpls_nh_build_multi(cfg, rt); @@ -1576,6 +1604,7 @@ static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, cfg->rc_label = LABEL_NOT_SPECIFIED; cfg->rc_protocol = rtm->rtm_protocol; cfg->rc_via_table = MPLS_NEIGH_TABLE_UNSPEC; + cfg->rc_ttl_propagate = MPLS_TTL_PROP_DEFAULT; cfg->rc_nlflags = nlh->nlmsg_flags; cfg->rc_nlinfo.portid = NETLINK_CB(skb).portid; cfg->rc_nlinfo.nlh = nlh; @@ -1622,6 +1651,17 @@ static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, cfg->rc_mp_len = nla_len(nla); break; } + case RTA_TTL_PROPAGATE: + { + u8 ttl_propagate = nla_get_u8(nla); + + if (ttl_propagate > 1) + goto errout; + cfg->rc_ttl_propagate = ttl_propagate ? + MPLS_TTL_PROP_ENABLED : + MPLS_TTL_PROP_DISABLED; + break; + } default: /* Unsupported attribute */ goto errout; @@ -1682,6 +1722,15 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, if (nla_put_labels(skb, RTA_DST, 1, &label)) goto nla_put_failure; + + if (rt->rt_ttl_propagate != MPLS_TTL_PROP_DEFAULT) { + bool ttl_propagate = + rt->rt_ttl_propagate == MPLS_TTL_PROP_ENABLED; + + if (nla_put_u8(skb, RTA_TTL_PROPAGATE, + ttl_propagate)) + goto nla_put_failure; + } if (rt->rt_nhn == 1) { const struct mpls_nh *nh = rt->rt_nh; @@ -1792,7 +1841,8 @@ static inline size_t lfib_nlmsg_size(struct mpls_route *rt) { size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg)) - + nla_total_size(4); /* RTA_DST */ + + nla_total_size(4) /* RTA_DST */ + + nla_total_size(1); /* RTA_TTL_PROPAGATE */ if (rt->rt_nhn == 1) { struct mpls_nh *nh = rt->rt_nh; @@ -1876,6 +1926,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo); rt0->rt_protocol = RTPROT_KERNEL; rt0->rt_payload_type = MPT_IPV4; + rt0->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT; rt0->rt_nh->nh_via_table = NEIGH_LINK_TABLE; rt0->rt_nh->nh_via_alen = lo->addr_len; memcpy(__mpls_nh_via(rt0, rt0->rt_nh), lo->dev_addr, @@ -1889,6 +1940,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo); rt2->rt_protocol = RTPROT_KERNEL; rt2->rt_payload_type = MPT_IPV6; + rt0->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT; rt2->rt_nh->nh_via_table = NEIGH_LINK_TABLE; rt2->rt_nh->nh_via_alen = lo->addr_len; memcpy(__mpls_nh_via(rt2, rt2->rt_nh), lo->dev_addr, @@ -1970,6 +2022,9 @@ static int mpls_platform_labels(struct ctl_table *table, int write, return ret; } +#define MPLS_NS_SYSCTL_OFFSET(field) \ + (&((struct net *)0)->field) + static const struct ctl_table mpls_table[] = { { .procname = "platform_labels", @@ -1978,21 +2033,37 @@ static const struct ctl_table mpls_table[] = { .mode = 0644, .proc_handler = mpls_platform_labels, }, + { + .procname = "ip_ttl_propagate", + .data = MPLS_NS_SYSCTL_OFFSET(mpls.ip_ttl_propagate), + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, { } }; static int mpls_net_init(struct net *net) { struct ctl_table *table; + int i; net->mpls.platform_labels = 0; net->mpls.platform_label = NULL; + net->mpls.ip_ttl_propagate = 1; table = kmemdup(mpls_table, sizeof(mpls_table), GFP_KERNEL); if (table == NULL) return -ENOMEM; - table[0].data = net; + /* Table data contains only offsets relative to the base of + * the mdev at this point, so make them absolute. + */ + for (i = 0; i < ARRAY_SIZE(mpls_table) - 1; i++) + table[i].data = (char *)net + (uintptr_t)table[i].data; + net->mpls.ctl = register_net_sysctl(net, "net/mpls", table); if (net->mpls.ctl == NULL) { kfree(table); diff --git a/net/mpls/internal.h b/net/mpls/internal.h index 76360d8b9579..62928d8fabd1 100644 --- a/net/mpls/internal.h +++ b/net/mpls/internal.h @@ -90,6 +90,12 @@ struct mpls_nh { /* next hop label forwarding entry */ u8 nh_via_table; }; +enum mpls_ttl_propagation { + MPLS_TTL_PROP_DEFAULT, + MPLS_TTL_PROP_ENABLED, + MPLS_TTL_PROP_DISABLED, +}; + /* The route, nexthops and vias are stored together in the same memory * block: * @@ -116,6 +122,7 @@ struct mpls_route { /* next hop label forwarding entry */ u8 rt_protocol; u8 rt_payload_type; u8 rt_max_alen; + u8 rt_ttl_propagate; unsigned int rt_nhn; unsigned int rt_nhn_alive; struct mpls_nh rt_nh[0]; -- cgit From a59166e470868d92f0813977817e99e699398af5 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Fri, 10 Mar 2017 20:43:25 +0000 Subject: mpls: allow TTL propagation from IP packets to be configured Allow TTL propagation from IP packets to MPLS packets to be configured. Add a new optional LWT attribute, MPLS_IPTUNNEL_TTL, which allows the TTL to be set in the resulting MPLS packet, with the value of 0 having the semantics of enabling propagation of the TTL from the IP header (i.e. non-zero values disable propagation). Also allow the configuration to be overridden globally by reusing the same sysctl to control whether the TTL is propagated from IP packets into the MPLS header. If the per-LWT attribute is set then it overrides the global configuration. If the TTL isn't propagated then a default TTL value is used which can be configured via a new sysctl, "net.mpls.default_ttl". This is kept separate from the configuration of whether IP TTL propagation is enabled as it can be used in the future when non-IP payloads are supported (i.e. where there is no payload TTL that can be propagated). Signed-off-by: Robert Shearman Acked-by: David Ahern Tested-by: David Ahern Signed-off-by: David S. Miller --- Documentation/networking/mpls-sysctl.txt | 8 ++++ include/net/mpls_iptunnel.h | 2 + include/net/netns/mpls.h | 1 + include/uapi/linux/mpls_iptunnel.h | 2 + net/mpls/af_mpls.c | 11 +++++ net/mpls/mpls_iptunnel.c | 73 ++++++++++++++++++++++++++------ 6 files changed, 84 insertions(+), 13 deletions(-) diff --git a/Documentation/networking/mpls-sysctl.txt b/Documentation/networking/mpls-sysctl.txt index 9badd1d6685f..2f24a1912a48 100644 --- a/Documentation/networking/mpls-sysctl.txt +++ b/Documentation/networking/mpls-sysctl.txt @@ -30,6 +30,14 @@ ip_ttl_propagate - BOOL 0 - disabled / RFC 3443 [Short] Pipe Model 1 - enabled / RFC 3443 Uniform Model (default) +default_ttl - BOOL + Default TTL value to use for MPLS packets where it cannot be + propagated from an IP header, either because one isn't present + or ip_ttl_propagate has been disabled. + + Possible values: 1 - 255 + Default: 255 + conf//input - BOOL Control whether packets can be input on this interface. diff --git a/include/net/mpls_iptunnel.h b/include/net/mpls_iptunnel.h index 179253f9dcfd..a18af6a16eb5 100644 --- a/include/net/mpls_iptunnel.h +++ b/include/net/mpls_iptunnel.h @@ -19,6 +19,8 @@ struct mpls_iptunnel_encap { u32 label[MAX_NEW_LABELS]; u8 labels; + u8 ttl_propagate; + u8 default_ttl; }; static inline struct mpls_iptunnel_encap *mpls_lwtunnel_encap(struct lwtunnel_state *lwtstate) diff --git a/include/net/netns/mpls.h b/include/net/netns/mpls.h index 08652eedabb2..6608b3693385 100644 --- a/include/net/netns/mpls.h +++ b/include/net/netns/mpls.h @@ -10,6 +10,7 @@ struct ctl_table_header; struct netns_mpls { int ip_ttl_propagate; + int default_ttl; size_t platform_labels; struct mpls_route __rcu * __rcu *platform_label; diff --git a/include/uapi/linux/mpls_iptunnel.h b/include/uapi/linux/mpls_iptunnel.h index d80a0498f77e..f5e45095b0bb 100644 --- a/include/uapi/linux/mpls_iptunnel.h +++ b/include/uapi/linux/mpls_iptunnel.h @@ -16,11 +16,13 @@ /* MPLS tunnel attributes * [RTA_ENCAP] = { * [MPLS_IPTUNNEL_DST] + * [MPLS_IPTUNNEL_TTL] * } */ enum { MPLS_IPTUNNEL_UNSPEC, MPLS_IPTUNNEL_DST, + MPLS_IPTUNNEL_TTL, __MPLS_IPTUNNEL_MAX, }; #define MPLS_IPTUNNEL_MAX (__MPLS_IPTUNNEL_MAX - 1) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 0e1046f21af8..0c5d111abe36 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -34,6 +34,7 @@ static int zero = 0; static int one = 1; static int label_limit = (1 << 20) - 1; +static int ttl_max = 255; static void rtmsg_lfib(int event, u32 label, struct mpls_route *rt, struct nlmsghdr *nlh, struct net *net, u32 portid, @@ -2042,6 +2043,15 @@ static const struct ctl_table mpls_table[] = { .extra1 = &zero, .extra2 = &one, }, + { + .procname = "default_ttl", + .data = MPLS_NS_SYSCTL_OFFSET(mpls.default_ttl), + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &one, + .extra2 = &ttl_max, + }, { } }; @@ -2053,6 +2063,7 @@ static int mpls_net_init(struct net *net) net->mpls.platform_labels = 0; net->mpls.platform_label = NULL; net->mpls.ip_ttl_propagate = 1; + net->mpls.default_ttl = 255; table = kmemdup(mpls_table, sizeof(mpls_table), GFP_KERNEL); if (table == NULL) diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c index e4e4424f9eb1..22f71fce0bfb 100644 --- a/net/mpls/mpls_iptunnel.c +++ b/net/mpls/mpls_iptunnel.c @@ -29,6 +29,7 @@ static const struct nla_policy mpls_iptunnel_policy[MPLS_IPTUNNEL_MAX + 1] = { [MPLS_IPTUNNEL_DST] = { .type = NLA_U32 }, + [MPLS_IPTUNNEL_TTL] = { .type = NLA_U8 }, }; static unsigned int mpls_encap_size(struct mpls_iptunnel_encap *en) @@ -49,6 +50,7 @@ static int mpls_xmit(struct sk_buff *skb) struct rtable *rt = NULL; struct rt6_info *rt6 = NULL; struct mpls_dev *out_mdev; + struct net *net; int err = 0; bool bos; int i; @@ -56,17 +58,7 @@ static int mpls_xmit(struct sk_buff *skb) /* Find the output device */ out_dev = dst->dev; - - /* Obtain the ttl */ - if (dst->ops->family == AF_INET) { - ttl = ip_hdr(skb)->ttl; - rt = (struct rtable *)dst; - } else if (dst->ops->family == AF_INET6) { - ttl = ipv6_hdr(skb)->hop_limit; - rt6 = (struct rt6_info *)dst; - } else { - goto drop; - } + net = dev_net(out_dev); skb_orphan(skb); @@ -78,6 +70,38 @@ static int mpls_xmit(struct sk_buff *skb) tun_encap_info = mpls_lwtunnel_encap(dst->lwtstate); + /* Obtain the ttl using the following set of rules. + * + * LWT ttl propagation setting: + * - disabled => use default TTL value from LWT + * - enabled => use TTL value from IPv4/IPv6 header + * - default => + * Global ttl propagation setting: + * - disabled => use default TTL value from global setting + * - enabled => use TTL value from IPv4/IPv6 header + */ + if (dst->ops->family == AF_INET) { + if (tun_encap_info->ttl_propagate == MPLS_TTL_PROP_DISABLED) + ttl = tun_encap_info->default_ttl; + else if (tun_encap_info->ttl_propagate == MPLS_TTL_PROP_DEFAULT && + !net->mpls.ip_ttl_propagate) + ttl = net->mpls.default_ttl; + else + ttl = ip_hdr(skb)->ttl; + rt = (struct rtable *)dst; + } else if (dst->ops->family == AF_INET6) { + if (tun_encap_info->ttl_propagate == MPLS_TTL_PROP_DISABLED) + ttl = tun_encap_info->default_ttl; + else if (tun_encap_info->ttl_propagate == MPLS_TTL_PROP_DEFAULT && + !net->mpls.ip_ttl_propagate) + ttl = net->mpls.default_ttl; + else + ttl = ipv6_hdr(skb)->hop_limit; + rt6 = (struct rt6_info *)dst; + } else { + goto drop; + } + /* Verify the destination can hold the packet */ new_header_size = mpls_encap_size(tun_encap_info); mtu = mpls_dev_mtu(out_dev); @@ -160,6 +184,17 @@ static int mpls_build_state(struct nlattr *nla, &tun_encap_info->labels, tun_encap_info->label); if (ret) goto errout; + + tun_encap_info->ttl_propagate = MPLS_TTL_PROP_DEFAULT; + + if (tb[MPLS_IPTUNNEL_TTL]) { + tun_encap_info->default_ttl = nla_get_u8(tb[MPLS_IPTUNNEL_TTL]); + /* TTL 0 implies propagate from IP header */ + tun_encap_info->ttl_propagate = tun_encap_info->default_ttl ? + MPLS_TTL_PROP_DISABLED : + MPLS_TTL_PROP_ENABLED; + } + newts->type = LWTUNNEL_ENCAP_MPLS; newts->flags |= LWTUNNEL_STATE_XMIT_REDIRECT; newts->headroom = mpls_encap_size(tun_encap_info); @@ -186,6 +221,10 @@ static int mpls_fill_encap_info(struct sk_buff *skb, tun_encap_info->label)) goto nla_put_failure; + if (tun_encap_info->ttl_propagate != MPLS_TTL_PROP_DEFAULT && + nla_put_u8(skb, MPLS_IPTUNNEL_TTL, tun_encap_info->default_ttl)) + goto nla_put_failure; + return 0; nla_put_failure: @@ -195,10 +234,16 @@ nla_put_failure: static int mpls_encap_nlsize(struct lwtunnel_state *lwtstate) { struct mpls_iptunnel_encap *tun_encap_info; + int nlsize; tun_encap_info = mpls_lwtunnel_encap(lwtstate); - return nla_total_size(tun_encap_info->labels * 4); + nlsize = nla_total_size(tun_encap_info->labels * 4); + + if (tun_encap_info->ttl_propagate != MPLS_TTL_PROP_DEFAULT) + nlsize += nla_total_size(1); + + return nlsize; } static int mpls_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) @@ -207,7 +252,9 @@ static int mpls_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) struct mpls_iptunnel_encap *b_hdr = mpls_lwtunnel_encap(b); int l; - if (a_hdr->labels != b_hdr->labels) + if (a_hdr->labels != b_hdr->labels || + a_hdr->ttl_propagate != b_hdr->ttl_propagate || + a_hdr->default_ttl != b_hdr->default_ttl) return 1; for (l = 0; l < MAX_NEW_LABELS; l++) -- cgit From be086e7c53f1fac51eed14523b28f2214b548dd2 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Sat, 11 Mar 2017 18:39:18 +0200 Subject: qed*: Utilize Firmware 8.15.3.0 This patch advances the qed* drivers into using the newer firmware - This solves several firmware bugs, mostly related [but not limited to] various init/deinit issues in various offloaded protocols. It also introduces a major 4-Cached SGE change in firmware, which can be seen in the storage drivers' changes. In addition, this firmware is required for supporting the new QL41xxx series of adapters; While this patch doesn't add the actual support, the firmware contains the necessary initialization & firmware logic to operate such adapters [actual support would be added later on]. Changes from Previous versions: ------------------------------- - V2 - fix kbuild-test robot warnings Signed-off-by: Tomer Tayar Signed-off-by: Ram Amrani Signed-off-by: Manish Rangankar Signed-off-by: Chad Dupuis Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/infiniband/hw/qedr/main.c | 5 +- drivers/infiniband/hw/qedr/qedr.h | 3 +- drivers/infiniband/hw/qedr/qedr_cm.c | 3 - drivers/infiniband/hw/qedr/qedr_hsi.h | 56 - drivers/infiniband/hw/qedr/verbs.c | 3 +- drivers/net/ethernet/qlogic/qed/qed.h | 2 +- drivers/net/ethernet/qlogic/qed/qed_cxt.c | 5 +- drivers/net/ethernet/qlogic/qed/qed_debug.c | 1564 ++++++++++++-------- drivers/net/ethernet/qlogic/qed/qed_hsi.h | 764 +++++++++- .../net/ethernet/qlogic/qed/qed_init_fw_funcs.c | 56 +- drivers/net/ethernet/qlogic/qed/qed_init_ops.c | 2 +- drivers/net/ethernet/qlogic/qed/qed_ll2.c | 48 +- drivers/net/ethernet/qlogic/qed/qed_reg_addr.h | 30 + drivers/net/ethernet/qlogic/qed/qed_roce.c | 220 ++- drivers/net/ethernet/qlogic/qed/qed_roce.h | 11 +- drivers/net/ethernet/qlogic/qed/qed_spq.c | 13 +- drivers/net/ethernet/qlogic/qede/qede.h | 2 +- drivers/scsi/qedf/Makefile | 2 +- drivers/scsi/qedf/drv_fcoe_fw_funcs.c | 190 +++ drivers/scsi/qedf/drv_fcoe_fw_funcs.h | 93 ++ drivers/scsi/qedf/drv_scsi_fw_funcs.c | 44 + drivers/scsi/qedf/drv_scsi_fw_funcs.h | 85 ++ drivers/scsi/qedf/qedf.h | 23 +- drivers/scsi/qedf/qedf_els.c | 25 +- drivers/scsi/qedf/qedf_io.c | 670 +++------ drivers/scsi/qedi/Makefile | 2 +- drivers/scsi/qedi/qedi_fw.c | 1068 ++++++------- drivers/scsi/qedi/qedi_fw_api.c | 781 ++++++++++ drivers/scsi/qedi/qedi_fw_iscsi.h | 117 ++ drivers/scsi/qedi/qedi_fw_scsi.h | 55 + drivers/scsi/qedi/qedi_iscsi.c | 12 +- drivers/scsi/qedi/qedi_iscsi.h | 2 +- drivers/scsi/qedi/qedi_version.h | 4 +- include/linux/qed/common_hsi.h | 30 +- include/linux/qed/eth_common.h | 3 + include/linux/qed/fcoe_common.h | 180 +-- include/linux/qed/iscsi_common.h | 241 ++- include/linux/qed/roce_common.h | 17 + include/linux/qed/storage_common.h | 30 +- include/linux/qed/tcp_common.h | 1 + 40 files changed, 4234 insertions(+), 2228 deletions(-) delete mode 100644 drivers/infiniband/hw/qedr/qedr_hsi.h create mode 100644 drivers/scsi/qedf/drv_fcoe_fw_funcs.c create mode 100644 drivers/scsi/qedf/drv_fcoe_fw_funcs.h create mode 100644 drivers/scsi/qedf/drv_scsi_fw_funcs.c create mode 100644 drivers/scsi/qedf/drv_scsi_fw_funcs.h create mode 100644 drivers/scsi/qedi/qedi_fw_api.c create mode 100644 drivers/scsi/qedi/qedi_fw_iscsi.h create mode 100644 drivers/scsi/qedi/qedi_fw_scsi.h diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c index b9b47e5cc8b3..ced0461d6e9f 100644 --- a/drivers/infiniband/hw/qedr/main.c +++ b/drivers/infiniband/hw/qedr/main.c @@ -587,9 +587,8 @@ void qedr_affiliated_event(void *context, u8 e_code, void *fw_handle) #define EVENT_TYPE_CQ 1 #define EVENT_TYPE_QP 2 struct qedr_dev *dev = (struct qedr_dev *)context; - union event_ring_data *data = fw_handle; - u64 roce_handle64 = ((u64)data->roce_handle.hi << 32) + - data->roce_handle.lo; + struct regpair *async_handle = (struct regpair *)fw_handle; + u64 roce_handle64 = ((u64) async_handle->hi << 32) + async_handle->lo; u8 event_type = EVENT_TYPE_NOT_DEFINED; struct ib_event event; struct ib_cq *ibcq; diff --git a/drivers/infiniband/hw/qedr/qedr.h b/drivers/infiniband/hw/qedr/qedr.h index bb32e4792ec9..5cb9195513bd 100644 --- a/drivers/infiniband/hw/qedr/qedr.h +++ b/drivers/infiniband/hw/qedr/qedr.h @@ -38,7 +38,8 @@ #include #include #include -#include "qedr_hsi.h" +#include +#include "qedr_hsi_rdma.h" #define QEDR_MODULE_VERSION "8.10.10.0" #define QEDR_NODE_DESC "QLogic 579xx RoCE HCA" diff --git a/drivers/infiniband/hw/qedr/qedr_cm.c b/drivers/infiniband/hw/qedr/qedr_cm.c index 699632893dd9..a6280ce3e2a5 100644 --- a/drivers/infiniband/hw/qedr/qedr_cm.c +++ b/drivers/infiniband/hw/qedr/qedr_cm.c @@ -43,14 +43,11 @@ #include #include -#include "qedr_hsi.h" #include #include #include "qedr.h" -#include "qedr_hsi.h" #include "verbs.h" #include -#include "qedr_hsi.h" #include "qedr_cm.h" void qedr_inc_sw_gsi_cons(struct qedr_qp_hwq_info *info) diff --git a/drivers/infiniband/hw/qedr/qedr_hsi.h b/drivers/infiniband/hw/qedr/qedr_hsi.h deleted file mode 100644 index 66d27521373f..000000000000 --- a/drivers/infiniband/hw/qedr/qedr_hsi.h +++ /dev/null @@ -1,56 +0,0 @@ -/* QLogic qedr NIC Driver - * Copyright (c) 2015-2016 QLogic Corporation - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and /or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef __QED_HSI_ROCE__ -#define __QED_HSI_ROCE__ - -#include -#include -#include "qedr_hsi_rdma.h" - -/* Affiliated asynchronous events / errors enumeration */ -enum roce_async_events_type { - ROCE_ASYNC_EVENT_NONE = 0, - ROCE_ASYNC_EVENT_COMM_EST = 1, - ROCE_ASYNC_EVENT_SQ_DRAINED, - ROCE_ASYNC_EVENT_SRQ_LIMIT, - ROCE_ASYNC_EVENT_LAST_WQE_REACHED, - ROCE_ASYNC_EVENT_CQ_ERR, - ROCE_ASYNC_EVENT_LOCAL_INVALID_REQUEST_ERR, - ROCE_ASYNC_EVENT_LOCAL_CATASTROPHIC_ERR, - ROCE_ASYNC_EVENT_LOCAL_ACCESS_ERR, - ROCE_ASYNC_EVENT_QP_CATASTROPHIC_ERR, - ROCE_ASYNC_EVENT_CQ_OVERFLOW_ERR, - ROCE_ASYNC_EVENT_SRQ_EMPTY, - MAX_ROCE_ASYNC_EVENTS_TYPE -}; - -#endif /* __QED_HSI_ROCE__ */ diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index 6b3bb32803bd..2091902848e6 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -43,7 +43,8 @@ #include #include -#include "qedr_hsi.h" +#include +#include "qedr_hsi_rdma.h" #include #include "qedr.h" #include "verbs.h" diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 00c17fa6545b..be99092d7209 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -51,7 +51,7 @@ #include "qed_hsi.h" extern const struct qed_common_ops qed_common_ops_pass; -#define DRV_MODULE_VERSION "8.10.10.20" +#define DRV_MODULE_VERSION "8.10.10.21" #define MAX_HWFNS_PER_DEVICE (4) #define NAME_SIZE 16 diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c index d42d03df751a..89d210a54335 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c @@ -71,8 +71,7 @@ #define TM_ALIGN BIT(TM_SHIFT) #define TM_ELEM_SIZE 4 -/* For RoCE we configure to 64K to cover for RoCE max tasks 256K purpose. */ -#define ILT_DEFAULT_HW_P_SIZE (IS_ENABLED(CONFIG_QED_RDMA) ? 4 : 3) +#define ILT_DEFAULT_HW_P_SIZE 4 #define ILT_PAGE_IN_BYTES(hw_p_size) (1U << ((hw_p_size) + 12)) #define ILT_CFG_REG(cli, reg) PSWRQ2_REG_ ## cli ## _ ## reg ## _RT_OFFSET @@ -1126,7 +1125,7 @@ int qed_cxt_mngr_alloc(struct qed_hwfn *p_hwfn) clients[ILT_CLI_TSDM].first.reg = ILT_CFG_REG(TSDM, FIRST_ILT); clients[ILT_CLI_TSDM].last.reg = ILT_CFG_REG(TSDM, LAST_ILT); clients[ILT_CLI_TSDM].p_size.reg = ILT_CFG_REG(TSDM, P_SIZE); - /* default ILT page size for all clients is 32K */ + /* default ILT page size for all clients is 64K */ for (i = 0; i < ILT_CLI_MAX; i++) p_mngr->clients[i].p_size.val = ILT_DEFAULT_HW_P_SIZE; diff --git a/drivers/net/ethernet/qlogic/qed/qed_debug.c b/drivers/net/ethernet/qlogic/qed/qed_debug.c index 68f19ca57f96..5e81e8a7a109 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_debug.c +++ b/drivers/net/ethernet/qlogic/qed/qed_debug.c @@ -17,7 +17,6 @@ /* Chip IDs enum */ enum chip_ids { - CHIP_RESERVED, CHIP_BB_B0, CHIP_K2, MAX_CHIP_IDS @@ -40,6 +39,7 @@ enum mem_groups { MEM_GROUP_BTB_RAM, MEM_GROUP_RDIF_CTX, MEM_GROUP_TDIF_CTX, + MEM_GROUP_CFC_MEM, MEM_GROUP_CONN_CFC_MEM, MEM_GROUP_TASK_CFC_MEM, MEM_GROUP_CAU_PI, @@ -72,6 +72,7 @@ static const char * const s_mem_group_names[] = { "BTB_RAM", "RDIF_CTX", "TDIF_CTX", + "CFC_MEM", "CONN_CFC_MEM", "TASK_CFC_MEM", "CAU_PI", @@ -185,13 +186,16 @@ struct dbg_array { u32 size_in_dwords; }; +struct chip_platform_defs { + u8 num_ports; + u8 num_pfs; + u8 num_vfs; +}; + /* Chip constant definitions */ struct chip_defs { const char *name; - struct { - u8 num_ports; - u8 num_pfs; - } per_platform[MAX_PLATFORM_IDS]; + struct chip_platform_defs per_platform[MAX_PLATFORM_IDS]; }; /* Platform constant definitions */ @@ -405,22 +409,23 @@ struct phy_defs { /***************************** Constant Arrays *******************************/ /* Debug arrays */ -static struct dbg_array s_dbg_arrays[MAX_BIN_DBG_BUFFER_TYPE] = { {NULL} }; +static struct dbg_array s_dbg_arrays[MAX_BIN_DBG_BUFFER_TYPE] = { {0} }; /* Chip constant definitions array */ static struct chip_defs s_chip_defs[MAX_CHIP_IDS] = { - { "reserved", { {0, 0}, {0, 0}, {0, 0}, {0, 0} } }, { "bb_b0", - { {MAX_NUM_PORTS_BB, MAX_NUM_PFS_BB}, {0, 0}, {0, 0}, {0, 0} } }, - { "k2", { {MAX_NUM_PORTS_K2, MAX_NUM_PFS_K2}, {0, 0}, {0, 0}, {0, 0} } } + { {MAX_NUM_PORTS_BB, MAX_NUM_PFS_BB, MAX_NUM_VFS_BB}, {0, 0, 0}, + {0, 0, 0}, {0, 0, 0} } }, + { "k2", + { {MAX_NUM_PORTS_K2, MAX_NUM_PFS_K2, MAX_NUM_VFS_K2}, {0, 0, 0}, + {0, 0, 0}, {0, 0, 0} } } }; /* Storm constant definitions array */ static struct storm_defs s_storm_defs[] = { /* Tstorm */ {'T', BLOCK_TSEM, - {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT, - DBG_BUS_CLIENT_RBCT}, true, + {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT}, true, TSEM_REG_FAST_MEMORY, TSEM_REG_DBG_FRAME_MODE, TSEM_REG_SLOW_DBG_ACTIVE, TSEM_REG_SLOW_DBG_MODE, TSEM_REG_DBG_MODE1_CFG, @@ -432,8 +437,7 @@ static struct storm_defs s_storm_defs[] = { 4, TCM_REG_SM_TASK_CTX}, /* Mstorm */ {'M', BLOCK_MSEM, - {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT, - DBG_BUS_CLIENT_RBCM}, false, + {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, false, MSEM_REG_FAST_MEMORY, MSEM_REG_DBG_FRAME_MODE, MSEM_REG_SLOW_DBG_ACTIVE, MSEM_REG_SLOW_DBG_MODE, MSEM_REG_DBG_MODE1_CFG, @@ -445,8 +449,7 @@ static struct storm_defs s_storm_defs[] = { 7, MCM_REG_SM_TASK_CTX}, /* Ustorm */ {'U', BLOCK_USEM, - {DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU, - DBG_BUS_CLIENT_RBCU}, false, + {DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU}, false, USEM_REG_FAST_MEMORY, USEM_REG_DBG_FRAME_MODE, USEM_REG_SLOW_DBG_ACTIVE, USEM_REG_SLOW_DBG_MODE, USEM_REG_DBG_MODE1_CFG, @@ -458,8 +461,7 @@ static struct storm_defs s_storm_defs[] = { 3, UCM_REG_SM_TASK_CTX}, /* Xstorm */ {'X', BLOCK_XSEM, - {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX, - DBG_BUS_CLIENT_RBCX}, false, + {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX}, false, XSEM_REG_FAST_MEMORY, XSEM_REG_DBG_FRAME_MODE, XSEM_REG_SLOW_DBG_ACTIVE, XSEM_REG_SLOW_DBG_MODE, XSEM_REG_DBG_MODE1_CFG, @@ -471,8 +473,7 @@ static struct storm_defs s_storm_defs[] = { 0, 0}, /* Ystorm */ {'Y', BLOCK_YSEM, - {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX, - DBG_BUS_CLIENT_RBCY}, false, + {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCY}, false, YSEM_REG_FAST_MEMORY, YSEM_REG_DBG_FRAME_MODE, YSEM_REG_SLOW_DBG_ACTIVE, YSEM_REG_SLOW_DBG_MODE, YSEM_REG_DBG_MODE1_CFG, @@ -484,8 +485,7 @@ static struct storm_defs s_storm_defs[] = { 12, YCM_REG_SM_TASK_CTX}, /* Pstorm */ {'P', BLOCK_PSEM, - {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS, - DBG_BUS_CLIENT_RBCS}, true, + {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS}, true, PSEM_REG_FAST_MEMORY, PSEM_REG_DBG_FRAME_MODE, PSEM_REG_SLOW_DBG_ACTIVE, PSEM_REG_SLOW_DBG_MODE, PSEM_REG_DBG_MODE1_CFG, @@ -499,8 +499,9 @@ static struct storm_defs s_storm_defs[] = { /* Block definitions array */ static struct block_defs block_grc_defs = { - "grc", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCN, DBG_BUS_CLIENT_RBCN, DBG_BUS_CLIENT_RBCN}, + "grc", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCN, DBG_BUS_CLIENT_RBCN}, GRC_REG_DBG_SELECT, GRC_REG_DBG_DWORD_ENABLE, GRC_REG_DBG_SHIFT, GRC_REG_DBG_FORCE_VALID, GRC_REG_DBG_FORCE_FRAME, @@ -508,29 +509,30 @@ static struct block_defs block_grc_defs = { }; static struct block_defs block_miscs_defs = { - "miscs", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + "miscs", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, 0, 0, 0, 0, 0, false, false, MAX_DBG_RESET_REGS, 0 }; static struct block_defs block_misc_defs = { - "misc", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + "misc", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, 0, 0, 0, 0, 0, false, false, MAX_DBG_RESET_REGS, 0 }; static struct block_defs block_dbu_defs = { - "dbu", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + "dbu", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, 0, 0, 0, 0, 0, false, false, MAX_DBG_RESET_REGS, 0 }; static struct block_defs block_pglue_b_defs = { - "pglue_b", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCH, DBG_BUS_CLIENT_RBCH, DBG_BUS_CLIENT_RBCH}, + "pglue_b", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCH, DBG_BUS_CLIENT_RBCH}, PGLUE_B_REG_DBG_SELECT, PGLUE_B_REG_DBG_DWORD_ENABLE, PGLUE_B_REG_DBG_SHIFT, PGLUE_B_REG_DBG_FORCE_VALID, PGLUE_B_REG_DBG_FORCE_FRAME, @@ -538,8 +540,9 @@ static struct block_defs block_pglue_b_defs = { }; static struct block_defs block_cnig_defs = { - "cnig", {false, false, true}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCW}, + "cnig", + {false, true}, false, 0, + {MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCW}, CNIG_REG_DBG_SELECT_K2, CNIG_REG_DBG_DWORD_ENABLE_K2, CNIG_REG_DBG_SHIFT_K2, CNIG_REG_DBG_FORCE_VALID_K2, CNIG_REG_DBG_FORCE_FRAME_K2, @@ -547,15 +550,16 @@ static struct block_defs block_cnig_defs = { }; static struct block_defs block_cpmu_defs = { - "cpmu", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + "cpmu", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, 0, 0, 0, 0, 0, true, false, DBG_RESET_REG_MISCS_PL_HV, 8 }; static struct block_defs block_ncsi_defs = { - "ncsi", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCZ, DBG_BUS_CLIENT_RBCZ, DBG_BUS_CLIENT_RBCZ}, + "ncsi", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCZ, DBG_BUS_CLIENT_RBCZ}, NCSI_REG_DBG_SELECT, NCSI_REG_DBG_DWORD_ENABLE, NCSI_REG_DBG_SHIFT, NCSI_REG_DBG_FORCE_VALID, NCSI_REG_DBG_FORCE_FRAME, @@ -563,15 +567,16 @@ static struct block_defs block_ncsi_defs = { }; static struct block_defs block_opte_defs = { - "opte", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + "opte", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, 0, 0, 0, 0, 0, true, false, DBG_RESET_REG_MISCS_PL_HV, 4 }; static struct block_defs block_bmb_defs = { - "bmb", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCZ, DBG_BUS_CLIENT_RBCZ, DBG_BUS_CLIENT_RBCB}, + "bmb", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCZ, DBG_BUS_CLIENT_RBCB}, BMB_REG_DBG_SELECT, BMB_REG_DBG_DWORD_ENABLE, BMB_REG_DBG_SHIFT, BMB_REG_DBG_FORCE_VALID, BMB_REG_DBG_FORCE_FRAME, @@ -579,8 +584,9 @@ static struct block_defs block_bmb_defs = { }; static struct block_defs block_pcie_defs = { - "pcie", {false, false, true}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCH}, + "pcie", + {false, true}, false, 0, + {MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCH}, PCIE_REG_DBG_COMMON_SELECT, PCIE_REG_DBG_COMMON_DWORD_ENABLE, PCIE_REG_DBG_COMMON_SHIFT, PCIE_REG_DBG_COMMON_FORCE_VALID, PCIE_REG_DBG_COMMON_FORCE_FRAME, @@ -588,15 +594,16 @@ static struct block_defs block_pcie_defs = { }; static struct block_defs block_mcp_defs = { - "mcp", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + "mcp", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, 0, 0, 0, 0, 0, false, false, MAX_DBG_RESET_REGS, 0 }; static struct block_defs block_mcp2_defs = { - "mcp2", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCZ, DBG_BUS_CLIENT_RBCZ, DBG_BUS_CLIENT_RBCZ}, + "mcp2", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCZ, DBG_BUS_CLIENT_RBCZ}, MCP2_REG_DBG_SELECT, MCP2_REG_DBG_DWORD_ENABLE, MCP2_REG_DBG_SHIFT, MCP2_REG_DBG_FORCE_VALID, MCP2_REG_DBG_FORCE_FRAME, @@ -604,8 +611,9 @@ static struct block_defs block_mcp2_defs = { }; static struct block_defs block_pswhst_defs = { - "pswhst", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, + "pswhst", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, PSWHST_REG_DBG_SELECT, PSWHST_REG_DBG_DWORD_ENABLE, PSWHST_REG_DBG_SHIFT, PSWHST_REG_DBG_FORCE_VALID, PSWHST_REG_DBG_FORCE_FRAME, @@ -613,8 +621,9 @@ static struct block_defs block_pswhst_defs = { }; static struct block_defs block_pswhst2_defs = { - "pswhst2", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, + "pswhst2", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, PSWHST2_REG_DBG_SELECT, PSWHST2_REG_DBG_DWORD_ENABLE, PSWHST2_REG_DBG_SHIFT, PSWHST2_REG_DBG_FORCE_VALID, PSWHST2_REG_DBG_FORCE_FRAME, @@ -622,8 +631,9 @@ static struct block_defs block_pswhst2_defs = { }; static struct block_defs block_pswrd_defs = { - "pswrd", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, + "pswrd", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, PSWRD_REG_DBG_SELECT, PSWRD_REG_DBG_DWORD_ENABLE, PSWRD_REG_DBG_SHIFT, PSWRD_REG_DBG_FORCE_VALID, PSWRD_REG_DBG_FORCE_FRAME, @@ -631,8 +641,9 @@ static struct block_defs block_pswrd_defs = { }; static struct block_defs block_pswrd2_defs = { - "pswrd2", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, + "pswrd2", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, PSWRD2_REG_DBG_SELECT, PSWRD2_REG_DBG_DWORD_ENABLE, PSWRD2_REG_DBG_SHIFT, PSWRD2_REG_DBG_FORCE_VALID, PSWRD2_REG_DBG_FORCE_FRAME, @@ -640,8 +651,9 @@ static struct block_defs block_pswrd2_defs = { }; static struct block_defs block_pswwr_defs = { - "pswwr", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, + "pswwr", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, PSWWR_REG_DBG_SELECT, PSWWR_REG_DBG_DWORD_ENABLE, PSWWR_REG_DBG_SHIFT, PSWWR_REG_DBG_FORCE_VALID, PSWWR_REG_DBG_FORCE_FRAME, @@ -649,15 +661,16 @@ static struct block_defs block_pswwr_defs = { }; static struct block_defs block_pswwr2_defs = { - "pswwr2", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + "pswwr2", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, 0, 0, 0, 0, 0, true, false, DBG_RESET_REG_MISC_PL_HV, 3 }; static struct block_defs block_pswrq_defs = { - "pswrq", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, + "pswrq", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, PSWRQ_REG_DBG_SELECT, PSWRQ_REG_DBG_DWORD_ENABLE, PSWRQ_REG_DBG_SHIFT, PSWRQ_REG_DBG_FORCE_VALID, PSWRQ_REG_DBG_FORCE_FRAME, @@ -665,8 +678,9 @@ static struct block_defs block_pswrq_defs = { }; static struct block_defs block_pswrq2_defs = { - "pswrq2", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, + "pswrq2", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, PSWRQ2_REG_DBG_SELECT, PSWRQ2_REG_DBG_DWORD_ENABLE, PSWRQ2_REG_DBG_SHIFT, PSWRQ2_REG_DBG_FORCE_VALID, PSWRQ2_REG_DBG_FORCE_FRAME, @@ -674,8 +688,9 @@ static struct block_defs block_pswrq2_defs = { }; static struct block_defs block_pglcs_defs = { - "pglcs", {false, false, true}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCH}, + "pglcs", + {false, true}, false, 0, + {MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCH}, PGLCS_REG_DBG_SELECT, PGLCS_REG_DBG_DWORD_ENABLE, PGLCS_REG_DBG_SHIFT, PGLCS_REG_DBG_FORCE_VALID, PGLCS_REG_DBG_FORCE_FRAME, @@ -683,8 +698,9 @@ static struct block_defs block_pglcs_defs = { }; static struct block_defs block_ptu_defs = { - "ptu", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, + "ptu", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, PTU_REG_DBG_SELECT, PTU_REG_DBG_DWORD_ENABLE, PTU_REG_DBG_SHIFT, PTU_REG_DBG_FORCE_VALID, PTU_REG_DBG_FORCE_FRAME, @@ -692,8 +708,9 @@ static struct block_defs block_ptu_defs = { }; static struct block_defs block_dmae_defs = { - "dmae", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, + "dmae", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, DMAE_REG_DBG_SELECT, DMAE_REG_DBG_DWORD_ENABLE, DMAE_REG_DBG_SHIFT, DMAE_REG_DBG_FORCE_VALID, DMAE_REG_DBG_FORCE_FRAME, @@ -701,8 +718,9 @@ static struct block_defs block_dmae_defs = { }; static struct block_defs block_tcm_defs = { - "tcm", {true, true, true}, true, DBG_TSTORM_ID, - {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT}, + "tcm", + {true, true}, true, DBG_TSTORM_ID, + {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT}, TCM_REG_DBG_SELECT, TCM_REG_DBG_DWORD_ENABLE, TCM_REG_DBG_SHIFT, TCM_REG_DBG_FORCE_VALID, TCM_REG_DBG_FORCE_FRAME, @@ -710,8 +728,9 @@ static struct block_defs block_tcm_defs = { }; static struct block_defs block_mcm_defs = { - "mcm", {true, true, true}, true, DBG_MSTORM_ID, - {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, + "mcm", + {true, true}, true, DBG_MSTORM_ID, + {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, MCM_REG_DBG_SELECT, MCM_REG_DBG_DWORD_ENABLE, MCM_REG_DBG_SHIFT, MCM_REG_DBG_FORCE_VALID, MCM_REG_DBG_FORCE_FRAME, @@ -719,8 +738,9 @@ static struct block_defs block_mcm_defs = { }; static struct block_defs block_ucm_defs = { - "ucm", {true, true, true}, true, DBG_USTORM_ID, - {DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU}, + "ucm", + {true, true}, true, DBG_USTORM_ID, + {DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU}, UCM_REG_DBG_SELECT, UCM_REG_DBG_DWORD_ENABLE, UCM_REG_DBG_SHIFT, UCM_REG_DBG_FORCE_VALID, UCM_REG_DBG_FORCE_FRAME, @@ -728,8 +748,9 @@ static struct block_defs block_ucm_defs = { }; static struct block_defs block_xcm_defs = { - "xcm", {true, true, true}, true, DBG_XSTORM_ID, - {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX}, + "xcm", + {true, true}, true, DBG_XSTORM_ID, + {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX}, XCM_REG_DBG_SELECT, XCM_REG_DBG_DWORD_ENABLE, XCM_REG_DBG_SHIFT, XCM_REG_DBG_FORCE_VALID, XCM_REG_DBG_FORCE_FRAME, @@ -737,8 +758,9 @@ static struct block_defs block_xcm_defs = { }; static struct block_defs block_ycm_defs = { - "ycm", {true, true, true}, true, DBG_YSTORM_ID, - {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCY}, + "ycm", + {true, true}, true, DBG_YSTORM_ID, + {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCY}, YCM_REG_DBG_SELECT, YCM_REG_DBG_DWORD_ENABLE, YCM_REG_DBG_SHIFT, YCM_REG_DBG_FORCE_VALID, YCM_REG_DBG_FORCE_FRAME, @@ -746,8 +768,9 @@ static struct block_defs block_ycm_defs = { }; static struct block_defs block_pcm_defs = { - "pcm", {true, true, true}, true, DBG_PSTORM_ID, - {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS}, + "pcm", + {true, true}, true, DBG_PSTORM_ID, + {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS}, PCM_REG_DBG_SELECT, PCM_REG_DBG_DWORD_ENABLE, PCM_REG_DBG_SHIFT, PCM_REG_DBG_FORCE_VALID, PCM_REG_DBG_FORCE_FRAME, @@ -755,8 +778,9 @@ static struct block_defs block_pcm_defs = { }; static struct block_defs block_qm_defs = { - "qm", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCQ}, + "qm", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCQ}, QM_REG_DBG_SELECT, QM_REG_DBG_DWORD_ENABLE, QM_REG_DBG_SHIFT, QM_REG_DBG_FORCE_VALID, QM_REG_DBG_FORCE_FRAME, @@ -764,8 +788,9 @@ static struct block_defs block_qm_defs = { }; static struct block_defs block_tm_defs = { - "tm", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS}, + "tm", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS}, TM_REG_DBG_SELECT, TM_REG_DBG_DWORD_ENABLE, TM_REG_DBG_SHIFT, TM_REG_DBG_FORCE_VALID, TM_REG_DBG_FORCE_FRAME, @@ -773,8 +798,9 @@ static struct block_defs block_tm_defs = { }; static struct block_defs block_dorq_defs = { - "dorq", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCY}, + "dorq", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCY}, DORQ_REG_DBG_SELECT, DORQ_REG_DBG_DWORD_ENABLE, DORQ_REG_DBG_SHIFT, DORQ_REG_DBG_FORCE_VALID, DORQ_REG_DBG_FORCE_FRAME, @@ -782,8 +808,9 @@ static struct block_defs block_dorq_defs = { }; static struct block_defs block_brb_defs = { - "brb", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCR, DBG_BUS_CLIENT_RBCR, DBG_BUS_CLIENT_RBCR}, + "brb", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCR, DBG_BUS_CLIENT_RBCR}, BRB_REG_DBG_SELECT, BRB_REG_DBG_DWORD_ENABLE, BRB_REG_DBG_SHIFT, BRB_REG_DBG_FORCE_VALID, BRB_REG_DBG_FORCE_FRAME, @@ -791,8 +818,9 @@ static struct block_defs block_brb_defs = { }; static struct block_defs block_src_defs = { - "src", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCF, DBG_BUS_CLIENT_RBCF, DBG_BUS_CLIENT_RBCF}, + "src", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCF, DBG_BUS_CLIENT_RBCF}, SRC_REG_DBG_SELECT, SRC_REG_DBG_DWORD_ENABLE, SRC_REG_DBG_SHIFT, SRC_REG_DBG_FORCE_VALID, SRC_REG_DBG_FORCE_FRAME, @@ -800,8 +828,9 @@ static struct block_defs block_src_defs = { }; static struct block_defs block_prs_defs = { - "prs", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCR, DBG_BUS_CLIENT_RBCR, DBG_BUS_CLIENT_RBCR}, + "prs", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCR, DBG_BUS_CLIENT_RBCR}, PRS_REG_DBG_SELECT, PRS_REG_DBG_DWORD_ENABLE, PRS_REG_DBG_SHIFT, PRS_REG_DBG_FORCE_VALID, PRS_REG_DBG_FORCE_FRAME, @@ -809,8 +838,9 @@ static struct block_defs block_prs_defs = { }; static struct block_defs block_tsdm_defs = { - "tsdm", {true, true, true}, true, DBG_TSTORM_ID, - {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT}, + "tsdm", + {true, true}, true, DBG_TSTORM_ID, + {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT}, TSDM_REG_DBG_SELECT, TSDM_REG_DBG_DWORD_ENABLE, TSDM_REG_DBG_SHIFT, TSDM_REG_DBG_FORCE_VALID, TSDM_REG_DBG_FORCE_FRAME, @@ -818,8 +848,9 @@ static struct block_defs block_tsdm_defs = { }; static struct block_defs block_msdm_defs = { - "msdm", {true, true, true}, true, DBG_MSTORM_ID, - {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, + "msdm", + {true, true}, true, DBG_MSTORM_ID, + {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, MSDM_REG_DBG_SELECT, MSDM_REG_DBG_DWORD_ENABLE, MSDM_REG_DBG_SHIFT, MSDM_REG_DBG_FORCE_VALID, MSDM_REG_DBG_FORCE_FRAME, @@ -827,8 +858,9 @@ static struct block_defs block_msdm_defs = { }; static struct block_defs block_usdm_defs = { - "usdm", {true, true, true}, true, DBG_USTORM_ID, - {DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU}, + "usdm", + {true, true}, true, DBG_USTORM_ID, + {DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU}, USDM_REG_DBG_SELECT, USDM_REG_DBG_DWORD_ENABLE, USDM_REG_DBG_SHIFT, USDM_REG_DBG_FORCE_VALID, USDM_REG_DBG_FORCE_FRAME, @@ -836,8 +868,9 @@ static struct block_defs block_usdm_defs = { }; static struct block_defs block_xsdm_defs = { - "xsdm", {true, true, true}, true, DBG_XSTORM_ID, - {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX}, + "xsdm", + {true, true}, true, DBG_XSTORM_ID, + {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX}, XSDM_REG_DBG_SELECT, XSDM_REG_DBG_DWORD_ENABLE, XSDM_REG_DBG_SHIFT, XSDM_REG_DBG_FORCE_VALID, XSDM_REG_DBG_FORCE_FRAME, @@ -845,8 +878,9 @@ static struct block_defs block_xsdm_defs = { }; static struct block_defs block_ysdm_defs = { - "ysdm", {true, true, true}, true, DBG_YSTORM_ID, - {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCY}, + "ysdm", + {true, true}, true, DBG_YSTORM_ID, + {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCY}, YSDM_REG_DBG_SELECT, YSDM_REG_DBG_DWORD_ENABLE, YSDM_REG_DBG_SHIFT, YSDM_REG_DBG_FORCE_VALID, YSDM_REG_DBG_FORCE_FRAME, @@ -854,8 +888,9 @@ static struct block_defs block_ysdm_defs = { }; static struct block_defs block_psdm_defs = { - "psdm", {true, true, true}, true, DBG_PSTORM_ID, - {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS}, + "psdm", + {true, true}, true, DBG_PSTORM_ID, + {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS}, PSDM_REG_DBG_SELECT, PSDM_REG_DBG_DWORD_ENABLE, PSDM_REG_DBG_SHIFT, PSDM_REG_DBG_FORCE_VALID, PSDM_REG_DBG_FORCE_FRAME, @@ -863,8 +898,9 @@ static struct block_defs block_psdm_defs = { }; static struct block_defs block_tsem_defs = { - "tsem", {true, true, true}, true, DBG_TSTORM_ID, - {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT}, + "tsem", + {true, true}, true, DBG_TSTORM_ID, + {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT}, TSEM_REG_DBG_SELECT, TSEM_REG_DBG_DWORD_ENABLE, TSEM_REG_DBG_SHIFT, TSEM_REG_DBG_FORCE_VALID, TSEM_REG_DBG_FORCE_FRAME, @@ -872,8 +908,9 @@ static struct block_defs block_tsem_defs = { }; static struct block_defs block_msem_defs = { - "msem", {true, true, true}, true, DBG_MSTORM_ID, - {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, + "msem", + {true, true}, true, DBG_MSTORM_ID, + {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, MSEM_REG_DBG_SELECT, MSEM_REG_DBG_DWORD_ENABLE, MSEM_REG_DBG_SHIFT, MSEM_REG_DBG_FORCE_VALID, MSEM_REG_DBG_FORCE_FRAME, @@ -881,8 +918,9 @@ static struct block_defs block_msem_defs = { }; static struct block_defs block_usem_defs = { - "usem", {true, true, true}, true, DBG_USTORM_ID, - {DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU}, + "usem", + {true, true}, true, DBG_USTORM_ID, + {DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU}, USEM_REG_DBG_SELECT, USEM_REG_DBG_DWORD_ENABLE, USEM_REG_DBG_SHIFT, USEM_REG_DBG_FORCE_VALID, USEM_REG_DBG_FORCE_FRAME, @@ -890,8 +928,9 @@ static struct block_defs block_usem_defs = { }; static struct block_defs block_xsem_defs = { - "xsem", {true, true, true}, true, DBG_XSTORM_ID, - {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX}, + "xsem", + {true, true}, true, DBG_XSTORM_ID, + {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX}, XSEM_REG_DBG_SELECT, XSEM_REG_DBG_DWORD_ENABLE, XSEM_REG_DBG_SHIFT, XSEM_REG_DBG_FORCE_VALID, XSEM_REG_DBG_FORCE_FRAME, @@ -899,8 +938,9 @@ static struct block_defs block_xsem_defs = { }; static struct block_defs block_ysem_defs = { - "ysem", {true, true, true}, true, DBG_YSTORM_ID, - {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCY}, + "ysem", + {true, true}, true, DBG_YSTORM_ID, + {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCY}, YSEM_REG_DBG_SELECT, YSEM_REG_DBG_DWORD_ENABLE, YSEM_REG_DBG_SHIFT, YSEM_REG_DBG_FORCE_VALID, YSEM_REG_DBG_FORCE_FRAME, @@ -908,8 +948,9 @@ static struct block_defs block_ysem_defs = { }; static struct block_defs block_psem_defs = { - "psem", {true, true, true}, true, DBG_PSTORM_ID, - {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS}, + "psem", + {true, true}, true, DBG_PSTORM_ID, + {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS}, PSEM_REG_DBG_SELECT, PSEM_REG_DBG_DWORD_ENABLE, PSEM_REG_DBG_SHIFT, PSEM_REG_DBG_FORCE_VALID, PSEM_REG_DBG_FORCE_FRAME, @@ -917,8 +958,9 @@ static struct block_defs block_psem_defs = { }; static struct block_defs block_rss_defs = { - "rss", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT}, + "rss", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT}, RSS_REG_DBG_SELECT, RSS_REG_DBG_DWORD_ENABLE, RSS_REG_DBG_SHIFT, RSS_REG_DBG_FORCE_VALID, RSS_REG_DBG_FORCE_FRAME, @@ -926,8 +968,9 @@ static struct block_defs block_rss_defs = { }; static struct block_defs block_tmld_defs = { - "tmld", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, + "tmld", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, TMLD_REG_DBG_SELECT, TMLD_REG_DBG_DWORD_ENABLE, TMLD_REG_DBG_SHIFT, TMLD_REG_DBG_FORCE_VALID, TMLD_REG_DBG_FORCE_FRAME, @@ -935,8 +978,9 @@ static struct block_defs block_tmld_defs = { }; static struct block_defs block_muld_defs = { - "muld", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU}, + "muld", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU}, MULD_REG_DBG_SELECT, MULD_REG_DBG_DWORD_ENABLE, MULD_REG_DBG_SHIFT, MULD_REG_DBG_FORCE_VALID, MULD_REG_DBG_FORCE_FRAME, @@ -944,8 +988,9 @@ static struct block_defs block_muld_defs = { }; static struct block_defs block_yuld_defs = { - "yuld", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU}, + "yuld", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCU, DBG_BUS_CLIENT_RBCU}, YULD_REG_DBG_SELECT, YULD_REG_DBG_DWORD_ENABLE, YULD_REG_DBG_SHIFT, YULD_REG_DBG_FORCE_VALID, YULD_REG_DBG_FORCE_FRAME, @@ -953,8 +998,9 @@ static struct block_defs block_yuld_defs = { }; static struct block_defs block_xyld_defs = { - "xyld", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX}, + "xyld", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCX, DBG_BUS_CLIENT_RBCX}, XYLD_REG_DBG_SELECT, XYLD_REG_DBG_DWORD_ENABLE, XYLD_REG_DBG_SHIFT, XYLD_REG_DBG_FORCE_VALID, XYLD_REG_DBG_FORCE_FRAME, @@ -962,8 +1008,9 @@ static struct block_defs block_xyld_defs = { }; static struct block_defs block_prm_defs = { - "prm", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, + "prm", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, PRM_REG_DBG_SELECT, PRM_REG_DBG_DWORD_ENABLE, PRM_REG_DBG_SHIFT, PRM_REG_DBG_FORCE_VALID, PRM_REG_DBG_FORCE_FRAME, @@ -971,8 +1018,9 @@ static struct block_defs block_prm_defs = { }; static struct block_defs block_pbf_pb1_defs = { - "pbf_pb1", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCV}, + "pbf_pb1", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCV}, PBF_PB1_REG_DBG_SELECT, PBF_PB1_REG_DBG_DWORD_ENABLE, PBF_PB1_REG_DBG_SHIFT, PBF_PB1_REG_DBG_FORCE_VALID, PBF_PB1_REG_DBG_FORCE_FRAME, @@ -981,8 +1029,9 @@ static struct block_defs block_pbf_pb1_defs = { }; static struct block_defs block_pbf_pb2_defs = { - "pbf_pb2", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCV}, + "pbf_pb2", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCV}, PBF_PB2_REG_DBG_SELECT, PBF_PB2_REG_DBG_DWORD_ENABLE, PBF_PB2_REG_DBG_SHIFT, PBF_PB2_REG_DBG_FORCE_VALID, PBF_PB2_REG_DBG_FORCE_FRAME, @@ -991,8 +1040,9 @@ static struct block_defs block_pbf_pb2_defs = { }; static struct block_defs block_rpb_defs = { - "rpb", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, + "rpb", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, RPB_REG_DBG_SELECT, RPB_REG_DBG_DWORD_ENABLE, RPB_REG_DBG_SHIFT, RPB_REG_DBG_FORCE_VALID, RPB_REG_DBG_FORCE_FRAME, @@ -1000,8 +1050,9 @@ static struct block_defs block_rpb_defs = { }; static struct block_defs block_btb_defs = { - "btb", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCR, DBG_BUS_CLIENT_RBCR, DBG_BUS_CLIENT_RBCV}, + "btb", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCR, DBG_BUS_CLIENT_RBCV}, BTB_REG_DBG_SELECT, BTB_REG_DBG_DWORD_ENABLE, BTB_REG_DBG_SHIFT, BTB_REG_DBG_FORCE_VALID, BTB_REG_DBG_FORCE_FRAME, @@ -1009,8 +1060,9 @@ static struct block_defs block_btb_defs = { }; static struct block_defs block_pbf_defs = { - "pbf", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCV}, + "pbf", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCV}, PBF_REG_DBG_SELECT, PBF_REG_DBG_DWORD_ENABLE, PBF_REG_DBG_SHIFT, PBF_REG_DBG_FORCE_VALID, PBF_REG_DBG_FORCE_FRAME, @@ -1018,8 +1070,9 @@ static struct block_defs block_pbf_defs = { }; static struct block_defs block_rdif_defs = { - "rdif", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, + "rdif", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCT, DBG_BUS_CLIENT_RBCM}, RDIF_REG_DBG_SELECT, RDIF_REG_DBG_DWORD_ENABLE, RDIF_REG_DBG_SHIFT, RDIF_REG_DBG_FORCE_VALID, RDIF_REG_DBG_FORCE_FRAME, @@ -1027,8 +1080,9 @@ static struct block_defs block_rdif_defs = { }; static struct block_defs block_tdif_defs = { - "tdif", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS}, + "tdif", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCS, DBG_BUS_CLIENT_RBCS}, TDIF_REG_DBG_SELECT, TDIF_REG_DBG_DWORD_ENABLE, TDIF_REG_DBG_SHIFT, TDIF_REG_DBG_FORCE_VALID, TDIF_REG_DBG_FORCE_FRAME, @@ -1036,8 +1090,9 @@ static struct block_defs block_tdif_defs = { }; static struct block_defs block_cdu_defs = { - "cdu", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCF, DBG_BUS_CLIENT_RBCF, DBG_BUS_CLIENT_RBCF}, + "cdu", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCF, DBG_BUS_CLIENT_RBCF}, CDU_REG_DBG_SELECT, CDU_REG_DBG_DWORD_ENABLE, CDU_REG_DBG_SHIFT, CDU_REG_DBG_FORCE_VALID, CDU_REG_DBG_FORCE_FRAME, @@ -1045,8 +1100,9 @@ static struct block_defs block_cdu_defs = { }; static struct block_defs block_ccfc_defs = { - "ccfc", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCF, DBG_BUS_CLIENT_RBCF, DBG_BUS_CLIENT_RBCF}, + "ccfc", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCF, DBG_BUS_CLIENT_RBCF}, CCFC_REG_DBG_SELECT, CCFC_REG_DBG_DWORD_ENABLE, CCFC_REG_DBG_SHIFT, CCFC_REG_DBG_FORCE_VALID, CCFC_REG_DBG_FORCE_FRAME, @@ -1054,8 +1110,9 @@ static struct block_defs block_ccfc_defs = { }; static struct block_defs block_tcfc_defs = { - "tcfc", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCF, DBG_BUS_CLIENT_RBCF, DBG_BUS_CLIENT_RBCF}, + "tcfc", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCF, DBG_BUS_CLIENT_RBCF}, TCFC_REG_DBG_SELECT, TCFC_REG_DBG_DWORD_ENABLE, TCFC_REG_DBG_SHIFT, TCFC_REG_DBG_FORCE_VALID, TCFC_REG_DBG_FORCE_FRAME, @@ -1063,8 +1120,9 @@ static struct block_defs block_tcfc_defs = { }; static struct block_defs block_igu_defs = { - "igu", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, + "igu", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, IGU_REG_DBG_SELECT, IGU_REG_DBG_DWORD_ENABLE, IGU_REG_DBG_SHIFT, IGU_REG_DBG_FORCE_VALID, IGU_REG_DBG_FORCE_FRAME, @@ -1072,8 +1130,9 @@ static struct block_defs block_igu_defs = { }; static struct block_defs block_cau_defs = { - "cau", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, + "cau", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCP, DBG_BUS_CLIENT_RBCP}, CAU_REG_DBG_SELECT, CAU_REG_DBG_DWORD_ENABLE, CAU_REG_DBG_SHIFT, CAU_REG_DBG_FORCE_VALID, CAU_REG_DBG_FORCE_FRAME, @@ -1081,8 +1140,9 @@ static struct block_defs block_cau_defs = { }; static struct block_defs block_umac_defs = { - "umac", {false, false, true}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCZ}, + "umac", + {false, true}, false, 0, + {MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCZ}, UMAC_REG_DBG_SELECT, UMAC_REG_DBG_DWORD_ENABLE, UMAC_REG_DBG_SHIFT, UMAC_REG_DBG_FORCE_VALID, UMAC_REG_DBG_FORCE_FRAME, @@ -1090,22 +1150,23 @@ static struct block_defs block_umac_defs = { }; static struct block_defs block_xmac_defs = { - "xmac", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + "xmac", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, 0, 0, 0, 0, 0, false, false, MAX_DBG_RESET_REGS, 0 }; static struct block_defs block_dbg_defs = { - "dbg", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + "dbg", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, 0, 0, 0, 0, 0, true, true, DBG_RESET_REG_MISC_PL_PDA_VAUX, 3 }; static struct block_defs block_nig_defs = { - "nig", {true, true, true}, false, 0, - {DBG_BUS_CLIENT_RBCN, DBG_BUS_CLIENT_RBCN, DBG_BUS_CLIENT_RBCN}, + "nig", + {true, true}, false, 0, + {DBG_BUS_CLIENT_RBCN, DBG_BUS_CLIENT_RBCN}, NIG_REG_DBG_SELECT, NIG_REG_DBG_DWORD_ENABLE, NIG_REG_DBG_SHIFT, NIG_REG_DBG_FORCE_VALID, NIG_REG_DBG_FORCE_FRAME, @@ -1113,8 +1174,9 @@ static struct block_defs block_nig_defs = { }; static struct block_defs block_wol_defs = { - "wol", {false, false, true}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCZ}, + "wol", + {false, true}, false, 0, + {MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCZ}, WOL_REG_DBG_SELECT, WOL_REG_DBG_DWORD_ENABLE, WOL_REG_DBG_SHIFT, WOL_REG_DBG_FORCE_VALID, WOL_REG_DBG_FORCE_FRAME, @@ -1122,8 +1184,9 @@ static struct block_defs block_wol_defs = { }; static struct block_defs block_bmbn_defs = { - "bmbn", {false, false, true}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCB}, + "bmbn", + {false, true}, false, 0, + {MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCB}, BMBN_REG_DBG_SELECT, BMBN_REG_DBG_DWORD_ENABLE, BMBN_REG_DBG_SHIFT, BMBN_REG_DBG_FORCE_VALID, BMBN_REG_DBG_FORCE_FRAME, @@ -1131,15 +1194,16 @@ static struct block_defs block_bmbn_defs = { }; static struct block_defs block_ipc_defs = { - "ipc", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + "ipc", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, 0, 0, 0, 0, 0, true, false, DBG_RESET_REG_MISCS_PL_UA, 8 }; static struct block_defs block_nwm_defs = { - "nwm", {false, false, true}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCW}, + "nwm", + {false, true}, false, 0, + {MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCW}, NWM_REG_DBG_SELECT, NWM_REG_DBG_DWORD_ENABLE, NWM_REG_DBG_SHIFT, NWM_REG_DBG_FORCE_VALID, NWM_REG_DBG_FORCE_FRAME, @@ -1147,22 +1211,29 @@ static struct block_defs block_nwm_defs = { }; static struct block_defs block_nws_defs = { - "nws", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, - 0, 0, 0, 0, 0, + "nws", + {false, true}, false, 0, + {MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCW}, + NWS_REG_DBG_SELECT, NWS_REG_DBG_DWORD_ENABLE, + NWS_REG_DBG_SHIFT, NWS_REG_DBG_FORCE_VALID, + NWS_REG_DBG_FORCE_FRAME, true, false, DBG_RESET_REG_MISCS_PL_HV, 12 }; static struct block_defs block_ms_defs = { - "ms", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, - 0, 0, 0, 0, 0, + "ms", + {false, true}, false, 0, + {MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCZ}, + MS_REG_DBG_SELECT, MS_REG_DBG_DWORD_ENABLE, + MS_REG_DBG_SHIFT, MS_REG_DBG_FORCE_VALID, + MS_REG_DBG_FORCE_FRAME, true, false, DBG_RESET_REG_MISCS_PL_HV, 13 }; static struct block_defs block_phy_pcie_defs = { - "phy_pcie", {false, false, true}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCH}, + "phy_pcie", + {false, true}, false, 0, + {MAX_DBG_BUS_CLIENTS, DBG_BUS_CLIENT_RBCH}, PCIE_REG_DBG_COMMON_SELECT, PCIE_REG_DBG_COMMON_DWORD_ENABLE, PCIE_REG_DBG_COMMON_SHIFT, PCIE_REG_DBG_COMMON_FORCE_VALID, PCIE_REG_DBG_COMMON_FORCE_FRAME, @@ -1170,22 +1241,57 @@ static struct block_defs block_phy_pcie_defs = { }; static struct block_defs block_led_defs = { - "led", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + "led", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + 0, 0, 0, 0, 0, + true, false, DBG_RESET_REG_MISCS_PL_HV, 14 +}; + +static struct block_defs block_avs_wrap_defs = { + "avs_wrap", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + 0, 0, 0, 0, 0, + true, false, DBG_RESET_REG_MISCS_PL_UA, 11 +}; + +static struct block_defs block_rgfs_defs = { + "rgfs", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, 0, 0, 0, 0, 0, - true, true, DBG_RESET_REG_MISCS_PL_HV, 14 + false, false, MAX_DBG_RESET_REGS, 0 +}; + +static struct block_defs block_tgfs_defs = { + "tgfs", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + 0, 0, 0, 0, 0, + false, false, MAX_DBG_RESET_REGS, 0 +}; + +static struct block_defs block_ptld_defs = { + "ptld", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + 0, 0, 0, 0, 0, + false, false, MAX_DBG_RESET_REGS, 0 +}; + +static struct block_defs block_ypld_defs = { + "ypld", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + 0, 0, 0, 0, 0, + false, false, MAX_DBG_RESET_REGS, 0 }; static struct block_defs block_misc_aeu_defs = { - "misc_aeu", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + "misc_aeu", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, 0, 0, 0, 0, 0, false, false, MAX_DBG_RESET_REGS, 0 }; static struct block_defs block_bar0_map_defs = { - "bar0_map", {false, false, false}, false, 0, - {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, + "bar0_map", {false, false}, false, 0, + {MAX_DBG_BUS_CLIENTS, MAX_DBG_BUS_CLIENTS}, 0, 0, 0, 0, 0, false, false, MAX_DBG_RESET_REGS, 0 }; @@ -1269,6 +1375,11 @@ static struct block_defs *s_block_defs[MAX_BLOCK_ID] = { &block_ms_defs, &block_phy_pcie_defs, &block_led_defs, + &block_avs_wrap_defs, + &block_rgfs_defs, + &block_tgfs_defs, + &block_ptld_defs, + &block_ypld_defs, &block_misc_aeu_defs, &block_bar0_map_defs, }; @@ -1281,65 +1392,67 @@ static struct platform_defs s_platform_defs[] = { }; static struct grc_param_defs s_grc_param_defs[] = { - {{1, 1, 1}, 0, 1, false, 1, 1}, /* DBG_GRC_PARAM_DUMP_TSTORM */ - {{1, 1, 1}, 0, 1, false, 1, 1}, /* DBG_GRC_PARAM_DUMP_MSTORM */ - {{1, 1, 1}, 0, 1, false, 1, 1}, /* DBG_GRC_PARAM_DUMP_USTORM */ - {{1, 1, 1}, 0, 1, false, 1, 1}, /* DBG_GRC_PARAM_DUMP_XSTORM */ - {{1, 1, 1}, 0, 1, false, 1, 1}, /* DBG_GRC_PARAM_DUMP_YSTORM */ - {{1, 1, 1}, 0, 1, false, 1, 1}, /* DBG_GRC_PARAM_DUMP_PSTORM */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_REGS */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_RAM */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_PBUF */ - {{0, 0, 0}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_IOR */ - {{0, 0, 0}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_VFC */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_CM_CTX */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_ILT */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_RSS */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_CAU */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_QM */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_MCP */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_RESERVED */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_CFC */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_IGU */ - {{0, 0, 0}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_BRB */ - {{0, 0, 0}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_BTB */ - {{0, 0, 0}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_BMB */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_NIG */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_MULD */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_PRS */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_DMAE */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_TM */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_SDM */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_DIF */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_STATIC */ - {{0, 0, 0}, 0, 1, false, 0, 0}, /* DBG_GRC_PARAM_UNSTALL */ - {{MAX_LCIDS, MAX_LCIDS, MAX_LCIDS}, 1, MAX_LCIDS, false, MAX_LCIDS, + {{1, 1}, 0, 1, false, 1, 1}, /* DBG_GRC_PARAM_DUMP_TSTORM */ + {{1, 1}, 0, 1, false, 1, 1}, /* DBG_GRC_PARAM_DUMP_MSTORM */ + {{1, 1}, 0, 1, false, 1, 1}, /* DBG_GRC_PARAM_DUMP_USTORM */ + {{1, 1}, 0, 1, false, 1, 1}, /* DBG_GRC_PARAM_DUMP_XSTORM */ + {{1, 1}, 0, 1, false, 1, 1}, /* DBG_GRC_PARAM_DUMP_YSTORM */ + {{1, 1}, 0, 1, false, 1, 1}, /* DBG_GRC_PARAM_DUMP_PSTORM */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_REGS */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_RAM */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_PBUF */ + {{0, 0}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_IOR */ + {{0, 0}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_VFC */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_CM_CTX */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_ILT */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_RSS */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_CAU */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_QM */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_MCP */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_RESERVED */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_CFC */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_IGU */ + {{0, 0}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_BRB */ + {{0, 0}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_BTB */ + {{0, 0}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_BMB */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_NIG */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_MULD */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_PRS */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_DMAE */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_TM */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_SDM */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_DIF */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_STATIC */ + {{0, 0}, 0, 1, false, 0, 0}, /* DBG_GRC_PARAM_UNSTALL */ + {{MAX_LCIDS, MAX_LCIDS}, 1, MAX_LCIDS, false, MAX_LCIDS, MAX_LCIDS}, /* DBG_GRC_PARAM_NUM_LCIDS */ - {{MAX_LTIDS, MAX_LTIDS, MAX_LTIDS}, 1, MAX_LTIDS, false, MAX_LTIDS, + {{MAX_LTIDS, MAX_LTIDS}, 1, MAX_LTIDS, false, MAX_LTIDS, MAX_LTIDS}, /* DBG_GRC_PARAM_NUM_LTIDS */ - {{0, 0, 0}, 0, 1, true, 0, 0}, /* DBG_GRC_PARAM_EXCLUDE_ALL */ - {{0, 0, 0}, 0, 1, true, 0, 0}, /* DBG_GRC_PARAM_CRASH */ - {{0, 0, 0}, 0, 1, false, 1, 0}, /* DBG_GRC_PARAM_PARITY_SAFE */ - {{1, 1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_CM */ - {{1, 1, 1}, 0, 1, false, 0, 1} /* DBG_GRC_PARAM_DUMP_PHY */ + {{0, 0}, 0, 1, true, 0, 0}, /* DBG_GRC_PARAM_EXCLUDE_ALL */ + {{0, 0}, 0, 1, true, 0, 0}, /* DBG_GRC_PARAM_CRASH */ + {{0, 0}, 0, 1, false, 1, 0}, /* DBG_GRC_PARAM_PARITY_SAFE */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_CM */ + {{1, 1}, 0, 1, false, 0, 1}, /* DBG_GRC_PARAM_DUMP_PHY */ + {{0, 0}, 0, 1, false, 0, 0}, /* DBG_GRC_PARAM_NO_MCP */ + {{0, 0}, 0, 1, false, 0, 0} /* DBG_GRC_PARAM_NO_FW_VER */ }; static struct rss_mem_defs s_rss_mem_defs[] = { { "rss_mem_cid", "rss_cid", 0, - {256, 256, 320}, - {32, 32, 32} }, + {256, 320}, + {32, 32} }, { "rss_mem_key_msb", "rss_key", 1024, - {128, 128, 208}, - {256, 256, 256} }, + {128, 208}, + {256, 256} }, { "rss_mem_key_lsb", "rss_key", 2048, - {128, 128, 208}, - {64, 64, 64} }, + {128, 208}, + {64, 64} }, { "rss_mem_info", "rss_info", 3072, - {128, 128, 208}, - {16, 16, 16} }, + {128, 208}, + {16, 16} }, { "rss_mem_ind", "rss_ind", 4096, - {(128 * 128), (128 * 128), (128 * 208)}, - {16, 16, 16} } + {(128 * 128), (128 * 208)}, + {16, 16} } }; static struct vfc_ram_defs s_vfc_ram_defs[] = { @@ -1352,32 +1465,32 @@ static struct vfc_ram_defs s_vfc_ram_defs[] = { static struct big_ram_defs s_big_ram_defs[] = { { "BRB", MEM_GROUP_BRB_MEM, MEM_GROUP_BRB_RAM, DBG_GRC_PARAM_DUMP_BRB, BRB_REG_BIG_RAM_ADDRESS, BRB_REG_BIG_RAM_DATA, - {4800, 4800, 5632} }, + {4800, 5632} }, { "BTB", MEM_GROUP_BTB_MEM, MEM_GROUP_BTB_RAM, DBG_GRC_PARAM_DUMP_BTB, BTB_REG_BIG_RAM_ADDRESS, BTB_REG_BIG_RAM_DATA, - {2880, 2880, 3680} }, + {2880, 3680} }, { "BMB", MEM_GROUP_BMB_MEM, MEM_GROUP_BMB_RAM, DBG_GRC_PARAM_DUMP_BMB, BMB_REG_BIG_RAM_ADDRESS, BMB_REG_BIG_RAM_DATA, - {1152, 1152, 1152} } + {1152, 1152} } }; static struct reset_reg_defs s_reset_regs_defs[] = { { MISCS_REG_RESET_PL_UA, 0x0, - {true, true, true} }, /* DBG_RESET_REG_MISCS_PL_UA */ + {true, true} }, /* DBG_RESET_REG_MISCS_PL_UA */ { MISCS_REG_RESET_PL_HV, 0x0, - {true, true, true} }, /* DBG_RESET_REG_MISCS_PL_HV */ + {true, true} }, /* DBG_RESET_REG_MISCS_PL_HV */ { MISCS_REG_RESET_PL_HV_2, 0x0, - {false, false, true} }, /* DBG_RESET_REG_MISCS_PL_HV_2 */ + {false, true} }, /* DBG_RESET_REG_MISCS_PL_HV_2 */ { MISC_REG_RESET_PL_UA, 0x0, - {true, true, true} }, /* DBG_RESET_REG_MISC_PL_UA */ + {true, true} }, /* DBG_RESET_REG_MISC_PL_UA */ { MISC_REG_RESET_PL_HV, 0x0, - {true, true, true} }, /* DBG_RESET_REG_MISC_PL_HV */ + {true, true} }, /* DBG_RESET_REG_MISC_PL_HV */ { MISC_REG_RESET_PL_PDA_VMAIN_1, 0x4404040, - {true, true, true} }, /* DBG_RESET_REG_MISC_PL_PDA_VMAIN_1 */ + {true, true} }, /* DBG_RESET_REG_MISC_PL_PDA_VMAIN_1 */ { MISC_REG_RESET_PL_PDA_VMAIN_2, 0x7c00007, - {true, true, true} }, /* DBG_RESET_REG_MISC_PL_PDA_VMAIN_2 */ + {true, true} }, /* DBG_RESET_REG_MISC_PL_PDA_VMAIN_2 */ { MISC_REG_RESET_PL_PDA_VAUX, 0x2, - {true, true, true} }, /* DBG_RESET_REG_MISC_PL_PDA_VAUX */ + {true, true} }, /* DBG_RESET_REG_MISC_PL_PDA_VAUX */ }; static struct phy_defs s_phy_defs[] = { @@ -1410,6 +1523,26 @@ static u32 qed_read_unaligned_dword(u8 *buf) return dword; } +/* Returns the value of the specified GRC param */ +static u32 qed_grc_get_param(struct qed_hwfn *p_hwfn, + enum dbg_grc_params grc_param) +{ + struct dbg_tools_data *dev_data = &p_hwfn->dbg_info; + + return dev_data->grc.param_val[grc_param]; +} + +/* Initializes the GRC parameters */ +static void qed_dbg_grc_init_params(struct qed_hwfn *p_hwfn) +{ + struct dbg_tools_data *dev_data = &p_hwfn->dbg_info; + + if (!dev_data->grc.params_initialized) { + qed_dbg_grc_set_params_default(p_hwfn); + dev_data->grc.params_initialized = 1; + } +} + /* Initializes debug data for the specified device */ static enum dbg_status qed_dbg_dev_init(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) @@ -1431,6 +1564,10 @@ static enum dbg_status qed_dbg_dev_init(struct qed_hwfn *p_hwfn, dev_data->platform_id = PLATFORM_ASIC; dev_data->mode_enable[MODE_ASIC] = 1; + + /* Initializes the GRC parameters */ + qed_dbg_grc_init_params(p_hwfn); + dev_data->initialized = true; return DBG_STATUS_OK; } @@ -1561,7 +1698,7 @@ static u32 qed_dump_fw_ver_param(struct qed_hwfn *p_hwfn, int printed_chars; u32 offset = 0; - if (dump) { + if (dump && !qed_grc_get_param(p_hwfn, DBG_GRC_PARAM_NO_FW_VER)) { /* Read FW image/version from PRAM in a non-reset SEMI */ bool found = false; u8 storm_id; @@ -1622,7 +1759,7 @@ static u32 qed_dump_mfw_ver_param(struct qed_hwfn *p_hwfn, { char mfw_ver_str[16] = EMPTY_FW_VERSION_STR; - if (dump) { + if (dump && !qed_grc_get_param(p_hwfn, DBG_GRC_PARAM_NO_FW_VER)) { u32 global_section_offsize, global_section_addr, mfw_ver; u32 public_data_addr, global_section_offsize_addr; int printed_chars; @@ -1683,15 +1820,13 @@ static u32 qed_dump_common_global_params(struct qed_hwfn *p_hwfn, bool dump, u8 num_specific_global_params) { + u8 num_params = NUM_COMMON_GLOBAL_PARAMS + num_specific_global_params; struct dbg_tools_data *dev_data = &p_hwfn->dbg_info; u32 offset = 0; /* Find platform string and dump global params section header */ offset += qed_dump_section_hdr(dump_buf + offset, - dump, - "global_params", - NUM_COMMON_GLOBAL_PARAMS + - num_specific_global_params); + dump, "global_params", num_params); /* Store params */ offset += qed_dump_fw_ver_param(p_hwfn, p_ptt, dump_buf + offset, dump); @@ -1815,37 +1950,6 @@ static bool qed_is_mode_match(struct qed_hwfn *p_hwfn, u16 *modes_buf_offset) } } -/* Returns the value of the specified GRC param */ -static u32 qed_grc_get_param(struct qed_hwfn *p_hwfn, - enum dbg_grc_params grc_param) -{ - struct dbg_tools_data *dev_data = &p_hwfn->dbg_info; - - return dev_data->grc.param_val[grc_param]; -} - -/* Clear all GRC params */ -static void qed_dbg_grc_clear_params(struct qed_hwfn *p_hwfn) -{ - struct dbg_tools_data *dev_data = &p_hwfn->dbg_info; - u32 i; - - for (i = 0; i < MAX_DBG_GRC_PARAMS; i++) - dev_data->grc.param_set_by_user[i] = 0; -} - -/* Assign default GRC param values */ -static void qed_dbg_grc_set_params_default(struct qed_hwfn *p_hwfn) -{ - struct dbg_tools_data *dev_data = &p_hwfn->dbg_info; - u32 i; - - for (i = 0; i < MAX_DBG_GRC_PARAMS; i++) - if (!dev_data->grc.param_set_by_user[i]) - dev_data->grc.param_val[i] = - s_grc_param_defs[i].default_val[dev_data->chip_id]; -} - /* Returns true if the specified entity (indicated by GRC param) should be * included in the dump, false otherwise. */ @@ -1971,7 +2075,7 @@ static void qed_grc_unreset_blocks(struct qed_hwfn *p_hwfn, } } -/* Returns the attention name offsets of the specified block */ +/* Returns the attention block data of the specified block */ static const struct dbg_attn_block_type_data * qed_get_block_attn_data(enum block_id block_id, enum dbg_attn_type attn_type) { @@ -2040,7 +2144,7 @@ static void qed_grc_clear_all_prty(struct qed_hwfn *p_hwfn, * The following parameters are dumped: * - 'count' = num_dumped_entries * - 'split' = split_type - * - 'id'i = split_id (dumped only if split_id >= 0) + * - 'id' = split_id (dumped only if split_id >= 0) * - 'param_name' = param_val (user param, dumped only if param_name != NULL and * param_val != NULL) */ @@ -2069,21 +2173,81 @@ static u32 qed_grc_dump_regs_hdr(u32 *dump_buf, return offset; } -/* Dumps GRC register/memory. Returns the dumped size in dwords. */ +/* Dumps the GRC registers in the specified address range. + * Returns the dumped size in dwords. + */ +static u32 qed_grc_dump_addr_range(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u32 *dump_buf, + bool dump, u32 addr, u32 len) +{ + u32 byte_addr = DWORDS_TO_BYTES(addr), offset = 0, i; + + if (dump) + for (i = 0; i < len; i++, byte_addr += BYTES_IN_DWORD, offset++) + *(dump_buf + offset) = qed_rd(p_hwfn, p_ptt, byte_addr); + else + offset += len; + return offset; +} + +/* Dumps GRC registers sequence header. Returns the dumped size in dwords. */ +static u32 qed_grc_dump_reg_entry_hdr(u32 *dump_buf, bool dump, u32 addr, + u32 len) +{ + if (dump) + *dump_buf = addr | (len << REG_DUMP_LEN_SHIFT); + return 1; +} + +/* Dumps GRC registers sequence. Returns the dumped size in dwords. */ static u32 qed_grc_dump_reg_entry(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u32 *dump_buf, bool dump, u32 addr, u32 len) { - u32 offset = 0, i; + u32 offset = 0; + + offset += qed_grc_dump_reg_entry_hdr(dump_buf, dump, addr, len); + offset += qed_grc_dump_addr_range(p_hwfn, + p_ptt, + dump_buf + offset, dump, addr, len); + return offset; +} + +/* Dumps GRC registers sequence with skip cycle. + * Returns the dumped size in dwords. + */ +static u32 qed_grc_dump_reg_entry_skip(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u32 *dump_buf, + bool dump, u32 addr, u32 total_len, + u32 read_len, u32 skip_len) +{ + u32 offset = 0, reg_offset = 0; + offset += qed_grc_dump_reg_entry_hdr(dump_buf, dump, addr, total_len); if (dump) { - *(dump_buf + offset++) = addr | (len << REG_DUMP_LEN_SHIFT); - for (i = 0; i < len; i++, addr++, offset++) - *(dump_buf + offset) = qed_rd(p_hwfn, - p_ptt, - DWORDS_TO_BYTES(addr)); + while (reg_offset < total_len) { + u32 curr_len = min_t(u32, + read_len, + total_len - reg_offset); + offset += qed_grc_dump_addr_range(p_hwfn, + p_ptt, + dump_buf + offset, + dump, addr, curr_len); + reg_offset += curr_len; + addr += curr_len; + if (reg_offset < total_len) { + curr_len = min_t(u32, + skip_len, + total_len - skip_len); + memset(dump_buf + offset, 0, + DWORDS_TO_BYTES(curr_len)); + offset += curr_len; + reg_offset += curr_len; + addr += curr_len; + } + } } else { - offset += len + 1; + offset += total_len; } return offset; @@ -2124,14 +2288,17 @@ static u32 qed_grc_dump_regs_entries(struct qed_hwfn *p_hwfn, const struct dbg_dump_reg *reg = (const struct dbg_dump_reg *) &input_regs_arr.ptr[input_offset]; + u32 addr, len; + addr = GET_FIELD(reg->data, + DBG_DUMP_REG_ADDRESS); + len = GET_FIELD(reg->data, DBG_DUMP_REG_LENGTH); offset += - qed_grc_dump_reg_entry(p_hwfn, p_ptt, - dump_buf + offset, dump, - GET_FIELD(reg->data, - DBG_DUMP_REG_ADDRESS), - GET_FIELD(reg->data, - DBG_DUMP_REG_LENGTH)); + qed_grc_dump_reg_entry(p_hwfn, p_ptt, + dump_buf + offset, + dump, + addr, + len); (*num_dumped_reg_entries)++; } } else { @@ -2194,8 +2361,14 @@ static u32 qed_grc_dump_registers(struct qed_hwfn *p_hwfn, const char *param_name, const char *param_val) { struct dbg_tools_data *dev_data = &p_hwfn->dbg_info; + struct chip_platform_defs *p_platform_defs; u32 offset = 0, input_offset = 0; - u8 port_id, pf_id; + struct chip_defs *p_chip_defs; + u8 port_id, pf_id, vf_id; + u16 fid; + + p_chip_defs = &s_chip_defs[dev_data->chip_id]; + p_platform_defs = &p_chip_defs->per_platform[dev_data->platform_id]; if (dump) DP_VERBOSE(p_hwfn, QED_MSG_DEBUG, "Dumping registers...\n"); @@ -2214,7 +2387,6 @@ static u32 qed_grc_dump_registers(struct qed_hwfn *p_hwfn, switch (split_type_id) { case SPLIT_TYPE_NONE: - case SPLIT_TYPE_VF: offset += qed_grc_dump_split_data(p_hwfn, p_ptt, curr_input_regs_arr, @@ -2227,10 +2399,7 @@ static u32 qed_grc_dump_registers(struct qed_hwfn *p_hwfn, param_val); break; case SPLIT_TYPE_PORT: - for (port_id = 0; - port_id < - s_chip_defs[dev_data->chip_id]. - per_platform[dev_data->platform_id].num_ports; + for (port_id = 0; port_id < p_platform_defs->num_ports; port_id++) { if (dump) qed_port_pretend(p_hwfn, p_ptt, @@ -2247,20 +2416,48 @@ static u32 qed_grc_dump_registers(struct qed_hwfn *p_hwfn, break; case SPLIT_TYPE_PF: case SPLIT_TYPE_PORT_PF: - for (pf_id = 0; - pf_id < - s_chip_defs[dev_data->chip_id]. - per_platform[dev_data->platform_id].num_pfs; + for (pf_id = 0; pf_id < p_platform_defs->num_pfs; pf_id++) { - if (dump) - qed_fid_pretend(p_hwfn, p_ptt, pf_id); - offset += qed_grc_dump_split_data(p_hwfn, - p_ptt, - curr_input_regs_arr, - dump_buf + offset, - dump, block_enable, - "pf", pf_id, param_name, - param_val); + u8 pfid_shift = + PXP_PRETEND_CONCRETE_FID_PFID_SHIFT; + + if (dump) { + fid = pf_id << pfid_shift; + qed_fid_pretend(p_hwfn, p_ptt, fid); + } + + offset += + qed_grc_dump_split_data(p_hwfn, p_ptt, + curr_input_regs_arr, + dump_buf + offset, + dump, block_enable, + "pf", pf_id, + param_name, + param_val); + } + break; + case SPLIT_TYPE_VF: + for (vf_id = 0; vf_id < p_platform_defs->num_vfs; + vf_id++) { + u8 vfvalid_shift = + PXP_PRETEND_CONCRETE_FID_VFVALID_SHIFT; + u8 vfid_shift = + PXP_PRETEND_CONCRETE_FID_VFID_SHIFT; + + if (dump) { + fid = BIT(vfvalid_shift) | + (vf_id << vfid_shift); + qed_fid_pretend(p_hwfn, p_ptt, fid); + } + + offset += + qed_grc_dump_split_data(p_hwfn, p_ptt, + curr_input_regs_arr, + dump_buf + offset, + dump, block_enable, + "vf", vf_id, + param_name, + param_val); } break; default: @@ -2271,8 +2468,11 @@ static u32 qed_grc_dump_registers(struct qed_hwfn *p_hwfn, } /* Pretend to original PF */ - if (dump) - qed_fid_pretend(p_hwfn, p_ptt, p_hwfn->rel_pf_id); + if (dump) { + fid = p_hwfn->rel_pf_id << PXP_PRETEND_CONCRETE_FID_PFID_SHIFT; + qed_fid_pretend(p_hwfn, p_ptt, fid); + } + return offset; } @@ -2291,13 +2491,14 @@ static u32 qed_grc_dump_reset_regs(struct qed_hwfn *p_hwfn, /* Write reset registers */ for (i = 0; i < MAX_DBG_RESET_REGS; i++) { if (s_reset_regs_defs[i].exists[dev_data->chip_id]) { + u32 addr = BYTES_TO_DWORDS(s_reset_regs_defs[i].addr); + offset += qed_grc_dump_reg_entry(p_hwfn, p_ptt, dump_buf + offset, dump, - BYTES_TO_DWORDS - (s_reset_regs_defs - [i].addr), 1); + addr, + 1); num_regs++; } } @@ -2339,6 +2540,7 @@ static u32 qed_grc_dump_modified_regs(struct qed_hwfn *p_hwfn, &attn_reg_arr[reg_idx]; u16 modes_buf_offset; bool eval_mode; + u32 addr; /* Check mode */ eval_mode = GET_FIELD(reg_data->mode.data, @@ -2349,19 +2551,23 @@ static u32 qed_grc_dump_modified_regs(struct qed_hwfn *p_hwfn, if (!eval_mode || qed_is_mode_match(p_hwfn, &modes_buf_offset)) { /* Mode match - read and dump registers */ - offset += qed_grc_dump_reg_entry(p_hwfn, - p_ptt, - dump_buf + offset, - dump, - reg_data->mask_address, - 1); - offset += qed_grc_dump_reg_entry(p_hwfn, - p_ptt, - dump_buf + offset, - dump, - GET_FIELD(reg_data->data, - DBG_ATTN_REG_STS_ADDRESS), - 1); + addr = reg_data->mask_address; + offset += + qed_grc_dump_reg_entry(p_hwfn, + p_ptt, + dump_buf + offset, + dump, + addr, + 1); + addr = GET_FIELD(reg_data->data, + DBG_ATTN_REG_STS_ADDRESS); + offset += + qed_grc_dump_reg_entry(p_hwfn, + p_ptt, + dump_buf + offset, + dump, + addr, + 1); num_reg_entries += 2; } } @@ -2369,18 +2575,21 @@ static u32 qed_grc_dump_modified_regs(struct qed_hwfn *p_hwfn, /* Write storm stall status registers */ for (storm_id = 0; storm_id < MAX_DBG_STORMS; storm_id++) { + u32 addr; + if (dev_data->block_in_reset[s_storm_defs[storm_id].block_id] && dump) continue; + addr = + BYTES_TO_DWORDS(s_storm_defs[storm_id].sem_fast_mem_addr + + SEM_FAST_REG_STALLED); offset += qed_grc_dump_reg_entry(p_hwfn, - p_ptt, - dump_buf + offset, - dump, - BYTES_TO_DWORDS(s_storm_defs[storm_id]. - sem_fast_mem_addr + - SEM_FAST_REG_STALLED), - 1); + p_ptt, + dump_buf + offset, + dump, + addr, + 1); num_reg_entries++; } @@ -2392,11 +2601,47 @@ static u32 qed_grc_dump_modified_regs(struct qed_hwfn *p_hwfn, return offset; } +/* Dumps registers that can't be represented in the debug arrays */ +static u32 qed_grc_dump_special_regs(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 *dump_buf, bool dump) +{ + u32 offset = 0, addr; + + offset += qed_grc_dump_regs_hdr(dump_buf, + dump, 2, "eng", -1, NULL, NULL); + + /* Dump R/TDIF_REG_DEBUG_ERROR_INFO_SIZE (every 8'th register should be + * skipped). + */ + addr = BYTES_TO_DWORDS(RDIF_REG_DEBUG_ERROR_INFO); + offset += qed_grc_dump_reg_entry_skip(p_hwfn, + p_ptt, + dump_buf + offset, + dump, + addr, + RDIF_REG_DEBUG_ERROR_INFO_SIZE, + 7, + 1); + addr = BYTES_TO_DWORDS(TDIF_REG_DEBUG_ERROR_INFO); + offset += + qed_grc_dump_reg_entry_skip(p_hwfn, + p_ptt, + dump_buf + offset, + dump, + addr, + TDIF_REG_DEBUG_ERROR_INFO_SIZE, + 7, + 1); + + return offset; +} + /* Dumps a GRC memory header (section and params). * The following parameters are dumped: * name - name is dumped only if it's not NULL. - * addr - byte_addr is dumped only if name is NULL. - * len - dword_len is always dumped. + * addr - addr is dumped only if name is NULL. + * len - len is always dumped. * width - bit_width is dumped if it's not zero. * packed - packed=1 is dumped if it's not false. * mem_group - mem_group is always dumped. @@ -2408,8 +2653,8 @@ static u32 qed_grc_dump_mem_hdr(struct qed_hwfn *p_hwfn, u32 *dump_buf, bool dump, const char *name, - u32 byte_addr, - u32 dword_len, + u32 addr, + u32 len, u32 bit_width, bool packed, const char *mem_group, @@ -2419,7 +2664,7 @@ static u32 qed_grc_dump_mem_hdr(struct qed_hwfn *p_hwfn, u32 offset = 0; char buf[64]; - if (!dword_len) + if (!len) DP_NOTICE(p_hwfn, "Unexpected GRC Dump error: dumped memory size must be non-zero\n"); if (bit_width) @@ -2446,20 +2691,21 @@ static u32 qed_grc_dump_mem_hdr(struct qed_hwfn *p_hwfn, DP_VERBOSE(p_hwfn, QED_MSG_DEBUG, "Dumping %d registers from %s...\n", - dword_len, buf); + len, buf); } else { /* Dump address */ offset += qed_dump_num_param(dump_buf + offset, - dump, "addr", byte_addr); - if (dump && dword_len > 64) + dump, "addr", + DWORDS_TO_BYTES(addr)); + if (dump && len > 64) DP_VERBOSE(p_hwfn, QED_MSG_DEBUG, "Dumping %d registers from address 0x%x...\n", - dword_len, byte_addr); + len, (u32)DWORDS_TO_BYTES(addr)); } /* Dump len */ - offset += qed_dump_num_param(dump_buf + offset, dump, "len", dword_len); + offset += qed_dump_num_param(dump_buf + offset, dump, "len", len); /* Dump bit width */ if (bit_width) @@ -2492,8 +2738,8 @@ static u32 qed_grc_dump_mem(struct qed_hwfn *p_hwfn, u32 *dump_buf, bool dump, const char *name, - u32 byte_addr, - u32 dword_len, + u32 addr, + u32 len, u32 bit_width, bool packed, const char *mem_group, @@ -2505,21 +2751,14 @@ static u32 qed_grc_dump_mem(struct qed_hwfn *p_hwfn, dump_buf + offset, dump, name, - byte_addr, - dword_len, + addr, + len, bit_width, packed, mem_group, is_storm, storm_letter); - if (dump) { - u32 i; - - for (i = 0; i < dword_len; - i++, byte_addr += BYTES_IN_DWORD, offset++) - *(dump_buf + offset) = qed_rd(p_hwfn, p_ptt, byte_addr); - } else { - offset += dword_len; - } - + offset += qed_grc_dump_addr_range(p_hwfn, + p_ptt, + dump_buf + offset, dump, addr, len); return offset; } @@ -2575,25 +2814,41 @@ static u32 qed_grc_dump_mem_entries(struct qed_hwfn *p_hwfn, if (qed_grc_is_mem_included(p_hwfn, (enum block_id)cond_hdr->block_id, mem_group_id)) { - u32 mem_byte_addr = - DWORDS_TO_BYTES(GET_FIELD(mem->dword0, - DBG_DUMP_MEM_ADDRESS)); + u32 mem_addr = GET_FIELD(mem->dword0, + DBG_DUMP_MEM_ADDRESS); u32 mem_len = GET_FIELD(mem->dword1, DBG_DUMP_MEM_LENGTH); + enum dbg_grc_params grc_param; char storm_letter = 'a'; bool is_storm = false; /* Update memory length for CCFC/TCFC memories * according to number of LCIDs/LTIDs. */ - if (mem_group_id == MEM_GROUP_CONN_CFC_MEM) + if (mem_group_id == MEM_GROUP_CONN_CFC_MEM) { + if (mem_len % MAX_LCIDS != 0) { + DP_NOTICE(p_hwfn, + "Invalid CCFC connection memory size\n"); + return 0; + } + + grc_param = DBG_GRC_PARAM_NUM_LCIDS; mem_len = qed_grc_get_param(p_hwfn, - DBG_GRC_PARAM_NUM_LCIDS) - * (mem_len / MAX_LCIDS); - else if (mem_group_id == MEM_GROUP_TASK_CFC_MEM) + grc_param) * + (mem_len / MAX_LCIDS); + } else if (mem_group_id == + MEM_GROUP_TASK_CFC_MEM) { + if (mem_len % MAX_LTIDS != 0) { + DP_NOTICE(p_hwfn, + "Invalid TCFC task memory size\n"); + return 0; + } + + grc_param = DBG_GRC_PARAM_NUM_LTIDS; mem_len = qed_grc_get_param(p_hwfn, - DBG_GRC_PARAM_NUM_LTIDS) - * (mem_len / MAX_LTIDS); + grc_param) * + (mem_len / MAX_LTIDS); + } /* If memory is associated with Storm, update * Storm details. @@ -2610,7 +2865,7 @@ static u32 qed_grc_dump_mem_entries(struct qed_hwfn *p_hwfn, /* Dump memory */ offset += qed_grc_dump_mem(p_hwfn, p_ptt, dump_buf + offset, dump, NULL, - mem_byte_addr, mem_len, 0, + mem_addr, mem_len, 0, false, s_mem_group_names[mem_group_id], is_storm, storm_letter); @@ -2799,29 +3054,31 @@ static u32 qed_grc_dump_iors(struct qed_hwfn *p_hwfn, u32 offset = 0; for (storm_id = 0; storm_id < MAX_DBG_STORMS; storm_id++) { - if (qed_grc_is_storm_included(p_hwfn, - (enum dbg_storms)storm_id)) { - for (set_id = 0; set_id < NUM_IOR_SETS; set_id++) { - u32 addr = - s_storm_defs[storm_id].sem_fast_mem_addr + - SEM_FAST_REG_STORM_REG_FILE + - DWORDS_TO_BYTES(IOR_SET_OFFSET(set_id)); + struct storm_defs *storm = &s_storm_defs[storm_id]; - buf[strlen(buf) - 1] = '0' + set_id; - offset += qed_grc_dump_mem(p_hwfn, - p_ptt, - dump_buf + offset, - dump, - buf, - addr, - IORS_PER_SET, - 32, - false, - "ior", - true, - s_storm_defs - [storm_id].letter); - } + if (!qed_grc_is_storm_included(p_hwfn, + (enum dbg_storms)storm_id)) + continue; + + for (set_id = 0; set_id < NUM_IOR_SETS; set_id++) { + u32 dwords, addr; + + dwords = storm->sem_fast_mem_addr + + SEM_FAST_REG_STORM_REG_FILE; + addr = BYTES_TO_DWORDS(dwords) + IOR_SET_OFFSET(set_id); + buf[strlen(buf) - 1] = '0' + set_id; + offset += qed_grc_dump_mem(p_hwfn, + p_ptt, + dump_buf + offset, + dump, + buf, + addr, + IORS_PER_SET, + 32, + false, + "ior", + true, + storm->letter); } } @@ -2990,34 +3247,39 @@ static u32 qed_grc_dump_rss(struct qed_hwfn *p_hwfn, struct rss_mem_defs *rss_defs = &s_rss_mem_defs[rss_mem_id]; u32 num_entries = rss_defs->num_entries[dev_data->chip_id]; u32 entry_width = rss_defs->entry_width[dev_data->chip_id]; - u32 total_size = (num_entries * entry_width) / 32; + u32 total_dwords = (num_entries * entry_width) / 32; + u32 size = RSS_REG_RSS_RAM_DATA_SIZE; bool packed = (entry_width == 16); - u32 addr = rss_defs->addr; - u32 i, j; + u32 rss_addr = rss_defs->addr; + u32 i, addr; offset += qed_grc_dump_mem_hdr(p_hwfn, dump_buf + offset, dump, rss_defs->mem_name, - addr, - total_size, + 0, + total_dwords, entry_width, packed, rss_defs->type_name, false, 0); if (!dump) { - offset += total_size; + offset += total_dwords; continue; } /* Dump RSS data */ - for (i = 0; i < BYTES_TO_DWORDS(total_size); i++, addr++) { - qed_wr(p_hwfn, p_ptt, RSS_REG_RSS_RAM_ADDR, addr); - for (j = 0; j < BYTES_IN_DWORD; j++, offset++) - *(dump_buf + offset) = - qed_rd(p_hwfn, p_ptt, - RSS_REG_RSS_RAM_DATA + - DWORDS_TO_BYTES(j)); + for (i = 0; i < total_dwords; + i += RSS_REG_RSS_RAM_DATA_SIZE, rss_addr++) { + addr = BYTES_TO_DWORDS(RSS_REG_RSS_RAM_DATA); + qed_wr(p_hwfn, p_ptt, RSS_REG_RSS_RAM_ADDR, rss_addr); + offset += qed_grc_dump_addr_range(p_hwfn, + p_ptt, + dump_buf + + offset, + dump, + addr, + size); } } @@ -3030,19 +3292,19 @@ static u32 qed_grc_dump_big_ram(struct qed_hwfn *p_hwfn, u32 *dump_buf, bool dump, u8 big_ram_id) { struct dbg_tools_data *dev_data = &p_hwfn->dbg_info; + u32 total_blocks, ram_size, offset = 0, i; char mem_name[12] = "???_BIG_RAM"; char type_name[8] = "???_RAM"; - u32 ram_size, total_blocks; - u32 offset = 0, i, j; + struct big_ram_defs *big_ram; - total_blocks = - s_big_ram_defs[big_ram_id].num_of_blocks[dev_data->chip_id]; + big_ram = &s_big_ram_defs[big_ram_id]; + total_blocks = big_ram->num_of_blocks[dev_data->chip_id]; ram_size = total_blocks * BIG_RAM_BLOCK_SIZE_DWORDS; - strncpy(type_name, s_big_ram_defs[big_ram_id].instance_name, - strlen(s_big_ram_defs[big_ram_id].instance_name)); - strncpy(mem_name, s_big_ram_defs[big_ram_id].instance_name, - strlen(s_big_ram_defs[big_ram_id].instance_name)); + strncpy(type_name, big_ram->instance_name, + strlen(big_ram->instance_name)); + strncpy(mem_name, big_ram->instance_name, + strlen(big_ram->instance_name)); /* Dump memory header */ offset += qed_grc_dump_mem_hdr(p_hwfn, @@ -3059,13 +3321,17 @@ static u32 qed_grc_dump_big_ram(struct qed_hwfn *p_hwfn, /* Read and dump Big RAM data */ for (i = 0; i < total_blocks / 2; i++) { - qed_wr(p_hwfn, p_ptt, s_big_ram_defs[big_ram_id].addr_reg_addr, - i); - for (j = 0; j < 2 * BIG_RAM_BLOCK_SIZE_DWORDS; j++, offset++) - *(dump_buf + offset) = qed_rd(p_hwfn, p_ptt, - s_big_ram_defs[big_ram_id]. - data_reg_addr + - DWORDS_TO_BYTES(j)); + u32 addr, len; + + qed_wr(p_hwfn, p_ptt, big_ram->addr_reg_addr, i); + addr = BYTES_TO_DWORDS(big_ram->data_reg_addr); + len = 2 * BIG_RAM_BLOCK_SIZE_DWORDS; + offset += qed_grc_dump_addr_range(p_hwfn, + p_ptt, + dump_buf + offset, + dump, + addr, + len); } return offset; @@ -3075,11 +3341,11 @@ static u32 qed_grc_dump_mcp(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u32 *dump_buf, bool dump) { bool block_enable[MAX_BLOCK_ID] = { 0 }; + u32 offset = 0, addr; bool halted = false; - u32 offset = 0; /* Halt MCP */ - if (dump) { + if (dump && !qed_grc_get_param(p_hwfn, DBG_GRC_PARAM_NO_MCP)) { halted = !qed_mcp_halt(p_hwfn, p_ptt); if (!halted) DP_NOTICE(p_hwfn, "MCP halt failed!\n"); @@ -3091,7 +3357,7 @@ static u32 qed_grc_dump_mcp(struct qed_hwfn *p_hwfn, dump_buf + offset, dump, NULL, - MCP_REG_SCRATCH, + BYTES_TO_DWORDS(MCP_REG_SCRATCH), MCP_REG_SCRATCH_SIZE, 0, false, "MCP", false, 0); @@ -3101,7 +3367,7 @@ static u32 qed_grc_dump_mcp(struct qed_hwfn *p_hwfn, dump_buf + offset, dump, NULL, - MCP_REG_CPU_REG_FILE, + BYTES_TO_DWORDS(MCP_REG_CPU_REG_FILE), MCP_REG_CPU_REG_FILE_SIZE, 0, false, "MCP", false, 0); @@ -3115,12 +3381,13 @@ static u32 qed_grc_dump_mcp(struct qed_hwfn *p_hwfn, /* Dump required non-MCP registers */ offset += qed_grc_dump_regs_hdr(dump_buf + offset, dump, 1, "eng", -1, "block", "MCP"); + addr = BYTES_TO_DWORDS(MISC_REG_SHARED_MEM_ADDR); offset += qed_grc_dump_reg_entry(p_hwfn, p_ptt, dump_buf + offset, dump, - BYTES_TO_DWORDS - (MISC_REG_SHARED_MEM_ADDR), 1); + addr, + 1); /* Release MCP */ if (halted && qed_mcp_resume(p_hwfn, p_ptt)) @@ -3212,7 +3479,7 @@ static u32 qed_grc_dump_static_debug(struct qed_hwfn *p_hwfn, { u32 block_dwords = NUM_DBG_BUS_LINES * STATIC_DEBUG_LINE_DWORDS; struct dbg_tools_data *dev_data = &p_hwfn->dbg_info; - u32 offset = 0, block_id, line_id, addr, i; + u32 offset = 0, block_id, line_id; struct block_defs *p_block_defs; if (dump) { @@ -3255,6 +3522,8 @@ static u32 qed_grc_dump_static_debug(struct qed_hwfn *p_hwfn, if (dump && !dev_data->block_in_reset[block_id]) { u8 dbg_client_id = p_block_defs->dbg_client_id[dev_data->chip_id]; + u32 addr = BYTES_TO_DWORDS(DBG_REG_CALENDAR_OUT_DATA); + u32 len = STATIC_DEBUG_LINE_DWORDS; /* Enable block's client */ qed_bus_enable_clients(p_hwfn, p_ptt, @@ -3270,11 +3539,13 @@ static u32 qed_grc_dump_static_debug(struct qed_hwfn *p_hwfn, 0xf, 0, 0, 0); /* Read debug line info */ - for (i = 0, addr = DBG_REG_CALENDAR_OUT_DATA; - i < STATIC_DEBUG_LINE_DWORDS; - i++, offset++, addr += BYTES_IN_DWORD) - dump_buf[offset] = qed_rd(p_hwfn, p_ptt, - addr); + offset += + qed_grc_dump_addr_range(p_hwfn, + p_ptt, + dump_buf + offset, + dump, + addr, + len); } /* Disable block's client and debug output */ @@ -3311,14 +3582,8 @@ static enum dbg_status qed_grc_dump(struct qed_hwfn *p_hwfn, u8 i, port_mode = 0; u32 offset = 0; - /* Check if emulation platform */ *num_dumped_dwords = 0; - /* Fill GRC parameters that were not set by the user with their default - * value. - */ - qed_dbg_grc_set_params_default(p_hwfn); - /* Find port mode */ if (dump) { switch (qed_rd(p_hwfn, p_ptt, MISC_REG_PORT_MODE)) { @@ -3370,15 +3635,14 @@ static enum dbg_status qed_grc_dump(struct qed_hwfn *p_hwfn, } /* Disable all parities using MFW command */ - if (dump) { + if (dump && !qed_grc_get_param(p_hwfn, DBG_GRC_PARAM_NO_MCP)) { parities_masked = !qed_mcp_mask_parities(p_hwfn, p_ptt, 1); if (!parities_masked) { + DP_NOTICE(p_hwfn, + "Failed to mask parities using MFW\n"); if (qed_grc_get_param (p_hwfn, DBG_GRC_PARAM_PARITY_SAFE)) return DBG_STATUS_MCP_COULD_NOT_MASK_PRTY; - else - DP_NOTICE(p_hwfn, - "Failed to mask parities using MFW\n"); } } @@ -3409,6 +3673,11 @@ static enum dbg_status qed_grc_dump(struct qed_hwfn *p_hwfn, offset, dump, block_enable, NULL, NULL); + + /* Dump special registers */ + offset += qed_grc_dump_special_regs(p_hwfn, + p_ptt, + dump_buf + offset, dump); } /* Dump memories */ @@ -3583,9 +3852,9 @@ static u32 qed_idle_chk_dump_failure(struct qed_hwfn *p_hwfn, } if (mode_match) { - u32 grc_addr = - DWORDS_TO_BYTES(GET_FIELD(reg->data, - DBG_IDLE_CHK_INFO_REG_ADDRESS)); + u32 addr = + GET_FIELD(reg->data, + DBG_IDLE_CHK_INFO_REG_ADDRESS); /* Write register header */ struct dbg_idle_chk_result_reg_hdr *reg_hdr = @@ -3597,16 +3866,19 @@ static u32 qed_idle_chk_dump_failure(struct qed_hwfn *p_hwfn, memset(reg_hdr, 0, sizeof(*reg_hdr)); reg_hdr->size = reg->size; SET_FIELD(reg_hdr->data, - DBG_IDLE_CHK_RESULT_REG_HDR_REG_ID, - rule->num_cond_regs + reg_id); + DBG_IDLE_CHK_RESULT_REG_HDR_REG_ID, + rule->num_cond_regs + reg_id); /* Write register values */ - for (i = 0; i < reg->size; - i++, offset++, grc_addr += 4) - dump_buf[offset] = - qed_rd(p_hwfn, p_ptt, grc_addr); - } + offset += + qed_grc_dump_addr_range(p_hwfn, + p_ptt, + dump_buf + offset, + dump, + addr, + reg->size); } + } } return offset; @@ -3621,7 +3893,7 @@ qed_idle_chk_dump_rule_entries(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, { struct dbg_tools_data *dev_data = &p_hwfn->dbg_info; u32 cond_reg_values[IDLE_CHK_MAX_ENTRIES_SIZE]; - u32 i, j, offset = 0; + u32 i, offset = 0; u16 entry_id; u8 reg_id; @@ -3664,73 +3936,83 @@ qed_idle_chk_dump_rule_entries(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, if (!check_rule && dump) continue; + if (!dump) { + u32 entry_dump_size = + qed_idle_chk_dump_failure(p_hwfn, + p_ptt, + dump_buf + offset, + false, + rule->rule_id, + rule, + 0, + NULL); + + offset += num_reg_entries * entry_dump_size; + (*num_failing_rules) += num_reg_entries; + continue; + } + /* Go over all register entries (number of entries is the same * for all condition registers). */ for (entry_id = 0; entry_id < num_reg_entries; entry_id++) { /* Read current entry of all condition registers */ - if (dump) { - u32 next_reg_offset = 0; - - for (reg_id = 0; - reg_id < rule->num_cond_regs; - reg_id++) { - const struct dbg_idle_chk_cond_reg - *reg = &cond_regs[reg_id]; - - /* Find GRC address (if it's a memory, - * the address of the specific entry is - * calculated). - */ - u32 grc_addr = - DWORDS_TO_BYTES( - GET_FIELD(reg->data, - DBG_IDLE_CHK_COND_REG_ADDRESS)); - - if (reg->num_entries > 1 || - reg->start_entry > 0) { - u32 padded_entry_size = - reg->entry_size > 1 ? - roundup_pow_of_two - (reg->entry_size) : 1; - - grc_addr += - DWORDS_TO_BYTES( - (reg->start_entry + - entry_id) - * padded_entry_size); - } + u32 next_reg_offset = 0; - /* Read registers */ - if (next_reg_offset + reg->entry_size >= - IDLE_CHK_MAX_ENTRIES_SIZE) { - DP_NOTICE(p_hwfn, - "idle check registers entry is too large\n"); - return 0; - } + for (reg_id = 0; reg_id < rule->num_cond_regs; + reg_id++) { + const struct dbg_idle_chk_cond_reg *reg = + &cond_regs[reg_id]; - for (j = 0; j < reg->entry_size; - j++, next_reg_offset++, - grc_addr += 4) - cond_reg_values[next_reg_offset] = - qed_rd(p_hwfn, p_ptt, grc_addr); + /* Find GRC address (if it's a memory,the + * address of the specific entry is calculated). + */ + u32 addr = + GET_FIELD(reg->data, + DBG_IDLE_CHK_COND_REG_ADDRESS); + + if (reg->num_entries > 1 || + reg->start_entry > 0) { + u32 padded_entry_size = + reg->entry_size > 1 ? + roundup_pow_of_two(reg->entry_size) : + 1; + + addr += (reg->start_entry + entry_id) * + padded_entry_size; } + + /* Read registers */ + if (next_reg_offset + reg->entry_size >= + IDLE_CHK_MAX_ENTRIES_SIZE) { + DP_NOTICE(p_hwfn, + "idle check registers entry is too large\n"); + return 0; + } + + next_reg_offset += + qed_grc_dump_addr_range(p_hwfn, + p_ptt, + cond_reg_values + + next_reg_offset, + dump, addr, + reg->entry_size); } /* Call rule's condition function - a return value of * true indicates failure. */ if ((*cond_arr[rule->cond_id])(cond_reg_values, - imm_values) || !dump) { + imm_values)) { offset += - qed_idle_chk_dump_failure(p_hwfn, - p_ptt, - dump_buf + offset, - dump, - rule->rule_id, - rule, - entry_id, - cond_reg_values); + qed_idle_chk_dump_failure(p_hwfn, + p_ptt, + dump_buf + offset, + dump, + rule->rule_id, + rule, + entry_id, + cond_reg_values); (*num_failing_rules)++; break; } @@ -3818,13 +4100,18 @@ static enum dbg_status qed_find_nvram_image(struct qed_hwfn *p_hwfn, struct mcp_file_att file_att; /* Call NVRAM get file command */ - if (qed_mcp_nvm_rd_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_NVM_GET_FILE_ATT, - image_type, &ret_mcp_resp, &ret_mcp_param, - &ret_txn_size, (u32 *)&file_att) != 0) - return DBG_STATUS_NVRAM_GET_IMAGE_FAILED; + int nvm_result = qed_mcp_nvm_rd_cmd(p_hwfn, + p_ptt, + DRV_MSG_CODE_NVM_GET_FILE_ATT, + image_type, + &ret_mcp_resp, + &ret_mcp_param, + &ret_txn_size, + (u32 *)&file_att); /* Check response */ - if ((ret_mcp_resp & FW_MSG_CODE_MASK) != FW_MSG_CODE_NVM_OK) + if (nvm_result || + (ret_mcp_resp & FW_MSG_CODE_MASK) != FW_MSG_CODE_NVM_OK) return DBG_STATUS_NVRAM_GET_IMAGE_FAILED; /* Update return values */ @@ -3944,7 +4231,6 @@ static enum dbg_status qed_mcp_trace_get_meta_info(struct qed_hwfn *p_hwfn, u32 running_mfw_addr = MCP_REG_SCRATCH + SECTION_OFFSET(spad_trace_offsize) + QED_SECTION_SIZE(spad_trace_offsize) + trace_data_size_bytes; - enum dbg_status status; u32 nvram_image_type; *running_bundle_id = qed_rd(p_hwfn, p_ptt, running_mfw_addr); @@ -3955,30 +4241,12 @@ static enum dbg_status qed_mcp_trace_get_meta_info(struct qed_hwfn *p_hwfn, nvram_image_type = (*running_bundle_id == DIR_ID_1) ? NVM_TYPE_MFW_TRACE1 : NVM_TYPE_MFW_TRACE2; - status = qed_find_nvram_image(p_hwfn, - p_ptt, - nvram_image_type, - trace_meta_offset_bytes, - trace_meta_size_bytes); - - return status; -} - -/* Reads the MCP Trace data from the specified GRC address into the specified - * buffer. - */ -static void qed_mcp_trace_read_data(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - u32 grc_addr, u32 size_in_dwords, u32 *buf) -{ - u32 i; - DP_VERBOSE(p_hwfn, - QED_MSG_DEBUG, - "mcp_trace_read_data: reading trace data of size %d dwords from GRC address 0x%x\n", - size_in_dwords, grc_addr); - for (i = 0; i < size_in_dwords; i++, grc_addr += BYTES_IN_DWORD) - buf[i] = qed_rd(p_hwfn, p_ptt, grc_addr); + return qed_find_nvram_image(p_hwfn, + p_ptt, + nvram_image_type, + trace_meta_offset_bytes, + trace_meta_size_bytes); } /* Reads the MCP Trace meta data (from NVRAM or buffer) into the specified @@ -4034,11 +4302,14 @@ static enum dbg_status qed_mcp_trace_dump(struct qed_hwfn *p_hwfn, bool dump, u32 *num_dumped_dwords) { u32 trace_data_grc_addr, trace_data_size_bytes, trace_data_size_dwords; - u32 trace_meta_size_dwords, running_bundle_id, offset = 0; - u32 trace_meta_offset_bytes, trace_meta_size_bytes; + u32 trace_meta_size_dwords = 0, running_bundle_id, offset = 0; + u32 trace_meta_offset_bytes = 0, trace_meta_size_bytes = 0; enum dbg_status status; + bool mcp_access; int halted = 0; + mcp_access = !qed_grc_get_param(p_hwfn, DBG_GRC_PARAM_NO_MCP); + *num_dumped_dwords = 0; /* Get trace data info */ @@ -4060,7 +4331,7 @@ static enum dbg_status qed_mcp_trace_dump(struct qed_hwfn *p_hwfn, * consistent if halt fails, MCP trace is taken anyway, with a small * risk that it may be corrupt. */ - if (dump) { + if (dump && mcp_access) { halted = !qed_mcp_halt(p_hwfn, p_ptt); if (!halted) DP_NOTICE(p_hwfn, "MCP halt failed!\n"); @@ -4078,13 +4349,12 @@ static enum dbg_status qed_mcp_trace_dump(struct qed_hwfn *p_hwfn, dump, "size", trace_data_size_dwords); /* Read trace data from scratchpad into dump buffer */ - if (dump) - qed_mcp_trace_read_data(p_hwfn, - p_ptt, - trace_data_grc_addr, - trace_data_size_dwords, - dump_buf + offset); - offset += trace_data_size_dwords; + offset += qed_grc_dump_addr_range(p_hwfn, + p_ptt, + dump_buf + offset, + dump, + BYTES_TO_DWORDS(trace_data_grc_addr), + trace_data_size_dwords); /* Resume MCP (only if halt succeeded) */ if (halted && qed_mcp_resume(p_hwfn, p_ptt) != 0) @@ -4095,38 +4365,38 @@ static enum dbg_status qed_mcp_trace_dump(struct qed_hwfn *p_hwfn, dump, "mcp_trace_meta", 1); /* Read trace meta info */ - status = qed_mcp_trace_get_meta_info(p_hwfn, - p_ptt, - trace_data_size_bytes, - &running_bundle_id, - &trace_meta_offset_bytes, - &trace_meta_size_bytes); - if (status != DBG_STATUS_OK) - return status; + if (mcp_access) { + status = qed_mcp_trace_get_meta_info(p_hwfn, + p_ptt, + trace_data_size_bytes, + &running_bundle_id, + &trace_meta_offset_bytes, + &trace_meta_size_bytes); + if (status == DBG_STATUS_OK) + trace_meta_size_dwords = + BYTES_TO_DWORDS(trace_meta_size_bytes); + } - /* Dump trace meta size param (trace_meta_size_bytes is always - * dword-aligned). - */ - trace_meta_size_dwords = BYTES_TO_DWORDS(trace_meta_size_bytes); - offset += qed_dump_num_param(dump_buf + offset, dump, "size", - trace_meta_size_dwords); + /* Dump trace meta size param */ + offset += qed_dump_num_param(dump_buf + offset, + dump, "size", trace_meta_size_dwords); /* Read trace meta image into dump buffer */ - if (dump) { + if (dump && trace_meta_size_dwords) status = qed_mcp_trace_read_meta(p_hwfn, - p_ptt, - trace_meta_offset_bytes, - trace_meta_size_bytes, - dump_buf + offset); - if (status != DBG_STATUS_OK) - return status; - } - - offset += trace_meta_size_dwords; + p_ptt, + trace_meta_offset_bytes, + trace_meta_size_bytes, + dump_buf + offset); + if (status == DBG_STATUS_OK) + offset += trace_meta_size_dwords; *num_dumped_dwords = offset; - return DBG_STATUS_OK; + /* If no mcp access, indicate that the dump doesn't contain the meta + * data from NVRAM. + */ + return mcp_access ? status : DBG_STATUS_NVRAM_GET_IMAGE_FAILED; } /* Dump GRC FIFO */ @@ -4311,9 +4581,10 @@ static u32 qed_fw_asserts_dump(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u32 *dump_buf, bool dump) { struct dbg_tools_data *dev_data = &p_hwfn->dbg_info; + struct fw_asserts_ram_section *asserts; char storm_letter_str[2] = "?"; struct fw_info fw_info; - u32 offset = 0, i; + u32 offset = 0; u8 storm_id; /* Dump global params */ @@ -4323,8 +4594,8 @@ static u32 qed_fw_asserts_dump(struct qed_hwfn *p_hwfn, offset += qed_dump_str_param(dump_buf + offset, dump, "dump-type", "fw-asserts"); for (storm_id = 0; storm_id < MAX_DBG_STORMS; storm_id++) { - u32 fw_asserts_section_addr, next_list_idx_addr, next_list_idx, - last_list_idx, element_addr; + u32 fw_asserts_section_addr, next_list_idx_addr, next_list_idx; + u32 last_list_idx, addr; if (dev_data->block_in_reset[s_storm_defs[storm_id].block_id]) continue; @@ -4332,6 +4603,8 @@ static u32 qed_fw_asserts_dump(struct qed_hwfn *p_hwfn, /* Read FW info for the current Storm */ qed_read_fw_info(p_hwfn, p_ptt, storm_id, &fw_info); + asserts = &fw_info.fw_asserts_section; + /* Dump FW Asserts section header and params */ storm_letter_str[0] = s_storm_defs[storm_id].letter; offset += qed_dump_section_hdr(dump_buf + offset, dump, @@ -4339,12 +4612,10 @@ static u32 qed_fw_asserts_dump(struct qed_hwfn *p_hwfn, offset += qed_dump_str_param(dump_buf + offset, dump, "storm", storm_letter_str); offset += qed_dump_num_param(dump_buf + offset, dump, "size", - fw_info.fw_asserts_section. - list_element_dword_size); + asserts->list_element_dword_size); if (!dump) { - offset += fw_info.fw_asserts_section. - list_element_dword_size; + offset += asserts->list_element_dword_size; continue; } @@ -4352,28 +4623,22 @@ static u32 qed_fw_asserts_dump(struct qed_hwfn *p_hwfn, fw_asserts_section_addr = s_storm_defs[storm_id].sem_fast_mem_addr + SEM_FAST_REG_INT_RAM + - RAM_LINES_TO_BYTES(fw_info.fw_asserts_section. - section_ram_line_offset); + RAM_LINES_TO_BYTES(asserts->section_ram_line_offset); next_list_idx_addr = fw_asserts_section_addr + - DWORDS_TO_BYTES(fw_info.fw_asserts_section. - list_next_index_dword_offset); + DWORDS_TO_BYTES(asserts->list_next_index_dword_offset); next_list_idx = qed_rd(p_hwfn, p_ptt, next_list_idx_addr); last_list_idx = (next_list_idx > 0 ? next_list_idx - : fw_info.fw_asserts_section.list_num_elements) - - 1; - element_addr = - fw_asserts_section_addr + - DWORDS_TO_BYTES(fw_info.fw_asserts_section. - list_dword_offset) + - last_list_idx * - DWORDS_TO_BYTES(fw_info.fw_asserts_section. - list_element_dword_size); - for (i = 0; - i < fw_info.fw_asserts_section.list_element_dword_size; - i++, offset++, element_addr += BYTES_IN_DWORD) - dump_buf[offset] = qed_rd(p_hwfn, p_ptt, element_addr); + : asserts->list_num_elements) - 1; + addr = BYTES_TO_DWORDS(fw_asserts_section_addr) + + asserts->list_dword_offset + + last_list_idx * asserts->list_element_dword_size; + offset += + qed_grc_dump_addr_range(p_hwfn, p_ptt, + dump_buf + offset, + dump, addr, + asserts->list_element_dword_size); } /* Dump last section */ @@ -4386,13 +4651,10 @@ static u32 qed_fw_asserts_dump(struct qed_hwfn *p_hwfn, enum dbg_status qed_dbg_set_bin_ptr(const u8 * const bin_ptr) { /* Convert binary data to debug arrays */ - u32 num_of_buffers = *(u32 *)bin_ptr; - struct bin_buffer_hdr *buf_array; + struct bin_buffer_hdr *buf_array = (struct bin_buffer_hdr *)bin_ptr; u8 buf_id; - buf_array = (struct bin_buffer_hdr *)((u32 *)bin_ptr + 1); - - for (buf_id = 0; buf_id < num_of_buffers; buf_id++) { + for (buf_id = 0; buf_id < MAX_BIN_DBG_BUFFER_TYPE; buf_id++) { s_dbg_arrays[buf_id].ptr = (u32 *)(bin_ptr + buf_array[buf_id].offset); s_dbg_arrays[buf_id].size_in_dwords = @@ -4402,6 +4664,17 @@ enum dbg_status qed_dbg_set_bin_ptr(const u8 * const bin_ptr) return DBG_STATUS_OK; } +/* Assign default GRC param values */ +void qed_dbg_grc_set_params_default(struct qed_hwfn *p_hwfn) +{ + struct dbg_tools_data *dev_data = &p_hwfn->dbg_info; + u32 i; + + for (i = 0; i < MAX_DBG_GRC_PARAMS; i++) + dev_data->grc.param_val[i] = + s_grc_param_defs[i].default_val[dev_data->chip_id]; +} + enum dbg_status qed_dbg_grc_get_dump_buf_size(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u32 *buf_size) @@ -4441,8 +4714,9 @@ enum dbg_status qed_dbg_grc_dump(struct qed_hwfn *p_hwfn, /* GRC Dump */ status = qed_grc_dump(p_hwfn, p_ptt, dump_buf, true, num_dumped_dwords); - /* Clear all GRC params */ - qed_dbg_grc_clear_params(p_hwfn); + /* Revert GRC params to their default */ + qed_dbg_grc_set_params_default(p_hwfn); + return status; } @@ -4495,6 +4769,10 @@ enum dbg_status qed_dbg_idle_chk_dump(struct qed_hwfn *p_hwfn, /* Idle Check Dump */ *num_dumped_dwords = qed_idle_chk_dump(p_hwfn, p_ptt, dump_buf, true); + + /* Revert GRC params to their default */ + qed_dbg_grc_set_params_default(p_hwfn); + return DBG_STATUS_OK; } @@ -4519,11 +4797,15 @@ enum dbg_status qed_dbg_mcp_trace_dump(struct qed_hwfn *p_hwfn, u32 needed_buf_size_in_dwords; enum dbg_status status; - status = qed_dbg_mcp_trace_get_dump_buf_size(p_hwfn, p_ptt, + /* validate buffer size */ + status = + qed_dbg_mcp_trace_get_dump_buf_size(p_hwfn, p_ptt, &needed_buf_size_in_dwords); - if (status != DBG_STATUS_OK) + if (status != DBG_STATUS_OK && + status != DBG_STATUS_NVRAM_GET_IMAGE_FAILED) return status; + if (buf_size_in_dwords < needed_buf_size_in_dwords) return DBG_STATUS_DUMP_BUF_TOO_SMALL; @@ -4531,8 +4813,13 @@ enum dbg_status qed_dbg_mcp_trace_dump(struct qed_hwfn *p_hwfn, qed_update_blocks_reset_state(p_hwfn, p_ptt); /* Perform dump */ - return qed_mcp_trace_dump(p_hwfn, - p_ptt, dump_buf, true, num_dumped_dwords); + status = qed_mcp_trace_dump(p_hwfn, + p_ptt, dump_buf, true, num_dumped_dwords); + + /* Revert GRC params to their default */ + qed_dbg_grc_set_params_default(p_hwfn); + + return status; } enum dbg_status qed_dbg_reg_fifo_get_dump_buf_size(struct qed_hwfn *p_hwfn, @@ -4567,8 +4854,14 @@ enum dbg_status qed_dbg_reg_fifo_dump(struct qed_hwfn *p_hwfn, /* Update reset state */ qed_update_blocks_reset_state(p_hwfn, p_ptt); - return qed_reg_fifo_dump(p_hwfn, - p_ptt, dump_buf, true, num_dumped_dwords); + + status = qed_reg_fifo_dump(p_hwfn, + p_ptt, dump_buf, true, num_dumped_dwords); + + /* Revert GRC params to their default */ + qed_dbg_grc_set_params_default(p_hwfn); + + return status; } enum dbg_status qed_dbg_igu_fifo_get_dump_buf_size(struct qed_hwfn *p_hwfn, @@ -4603,8 +4896,13 @@ enum dbg_status qed_dbg_igu_fifo_dump(struct qed_hwfn *p_hwfn, /* Update reset state */ qed_update_blocks_reset_state(p_hwfn, p_ptt); - return qed_igu_fifo_dump(p_hwfn, - p_ptt, dump_buf, true, num_dumped_dwords); + + status = qed_igu_fifo_dump(p_hwfn, + p_ptt, dump_buf, true, num_dumped_dwords); + /* Revert GRC params to their default */ + qed_dbg_grc_set_params_default(p_hwfn); + + return status; } enum dbg_status @@ -4641,9 +4939,16 @@ enum dbg_status qed_dbg_protection_override_dump(struct qed_hwfn *p_hwfn, /* Update reset state */ qed_update_blocks_reset_state(p_hwfn, p_ptt); - return qed_protection_override_dump(p_hwfn, - p_ptt, - dump_buf, true, num_dumped_dwords); + + status = qed_protection_override_dump(p_hwfn, + p_ptt, + dump_buf, + true, num_dumped_dwords); + + /* Revert GRC params to their default */ + qed_dbg_grc_set_params_default(p_hwfn); + + return status; } enum dbg_status qed_dbg_fw_asserts_get_dump_buf_size(struct qed_hwfn *p_hwfn, @@ -5045,13 +5350,10 @@ static char s_temp_buf[MAX_MSG_LEN]; enum dbg_status qed_dbg_user_set_bin_ptr(const u8 * const bin_ptr) { /* Convert binary data to debug arrays */ - u32 num_of_buffers = *(u32 *)bin_ptr; - struct bin_buffer_hdr *buf_array; + struct bin_buffer_hdr *buf_array = (struct bin_buffer_hdr *)bin_ptr; u8 buf_id; - buf_array = (struct bin_buffer_hdr *)((u32 *)bin_ptr + 1); - - for (buf_id = 0; buf_id < num_of_buffers; buf_id++) { + for (buf_id = 0; buf_id < MAX_BIN_DBG_BUFFER_TYPE; buf_id++) { s_dbg_arrays[buf_id].ptr = (u32 *)(bin_ptr + buf_array[buf_id].offset); s_dbg_arrays[buf_id].size_in_dwords = @@ -5874,16 +6176,16 @@ static enum dbg_status qed_parse_reg_fifo_dump(struct qed_hwfn *p_hwfn, results_offset += sprintf(qed_get_buf_ptr(results_buf, results_offset), - "raw: 0x%016llx, address: 0x%07llx, access: %-5s, pf: %2lld, vf: %s, port: %lld, privilege: %-3s, protection: %-12s, master: %-4s, errors: ", + "raw: 0x%016llx, address: 0x%07x, access: %-5s, pf: %2d, vf: %s, port: %d, privilege: %-3s, protection: %-12s, master: %-4s, errors: ", elements[i].data, - GET_FIELD(elements[i].data, + (u32)GET_FIELD(elements[i].data, REG_FIFO_ELEMENT_ADDRESS) * REG_FIFO_ELEMENT_ADDR_FACTOR, s_access_strs[GET_FIELD(elements[i].data, REG_FIFO_ELEMENT_ACCESS)], - GET_FIELD(elements[i].data, - REG_FIFO_ELEMENT_PF), vf_str, - GET_FIELD(elements[i].data, + (u32)GET_FIELD(elements[i].data, + REG_FIFO_ELEMENT_PF), vf_str, + (u32)GET_FIELD(elements[i].data, REG_FIFO_ELEMENT_PORT), s_privilege_strs[GET_FIELD(elements[i]. data, @@ -6189,13 +6491,13 @@ qed_parse_protection_override_dump(struct qed_hwfn *p_hwfn, results_offset += sprintf(qed_get_buf_ptr(results_buf, results_offset), - "window %2d, address: 0x%07x, size: %7lld regs, read: %lld, write: %lld, read protection: %-12s, write protection: %-12s\n", + "window %2d, address: 0x%07x, size: %7d regs, read: %d, write: %d, read protection: %-12s, write protection: %-12s\n", i, address, - GET_FIELD(elements[i].data, + (u32)GET_FIELD(elements[i].data, PROTECTION_OVERRIDE_ELEMENT_WINDOW_SIZE), - GET_FIELD(elements[i].data, + (u32)GET_FIELD(elements[i].data, PROTECTION_OVERRIDE_ELEMENT_READ), - GET_FIELD(elements[i].data, + (u32)GET_FIELD(elements[i].data, PROTECTION_OVERRIDE_ELEMENT_WRITE), s_protection_strs[GET_FIELD(elements[i].data, PROTECTION_OVERRIDE_ELEMENT_READ_PROTECTION)], @@ -6508,7 +6810,7 @@ static enum dbg_status qed_dbg_dump(struct qed_hwfn *p_hwfn, */ rc = qed_features_lookup[feature_idx].get_size(p_hwfn, p_ptt, &buf_size_dwords); - if (rc != DBG_STATUS_OK) + if (rc != DBG_STATUS_OK && rc != DBG_STATUS_NVRAM_GET_IMAGE_FAILED) return rc; feature->buf_size = buf_size_dwords * sizeof(u32); feature->dump_buf = vmalloc(feature->buf_size); diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index 37c2bfb663bb..88ca78a297a0 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -574,6 +574,7 @@ enum core_event_opcode { CORE_EVENT_TX_QUEUE_STOP, CORE_EVENT_RX_QUEUE_START, CORE_EVENT_RX_QUEUE_STOP, + CORE_EVENT_RX_QUEUE_FLUSH, MAX_CORE_EVENT_OPCODE }; @@ -625,6 +626,7 @@ enum core_ramrod_cmd_id { CORE_RAMROD_TX_QUEUE_START, CORE_RAMROD_RX_QUEUE_STOP, CORE_RAMROD_TX_QUEUE_STOP, + CORE_RAMROD_RX_QUEUE_FLUSH, MAX_CORE_RAMROD_CMD_ID }; @@ -698,7 +700,8 @@ struct core_rx_slow_path_cqe { u8 type; u8 ramrod_cmd_id; __le16 echo; - __le32 reserved1[7]; + struct core_rx_cqe_opaque_data opaque_data; + __le32 reserved1[5]; }; union core_rx_cqe_union { @@ -735,45 +738,46 @@ struct core_rx_stop_ramrod_data { __le16 reserved2[2]; }; -struct core_tx_bd_flags { - u8 as_bitfield; -#define CORE_TX_BD_FLAGS_FORCE_VLAN_MODE_MASK 0x1 -#define CORE_TX_BD_FLAGS_FORCE_VLAN_MODE_SHIFT 0 -#define CORE_TX_BD_FLAGS_VLAN_INSERTION_MASK 0x1 -#define CORE_TX_BD_FLAGS_VLAN_INSERTION_SHIFT 1 -#define CORE_TX_BD_FLAGS_START_BD_MASK 0x1 -#define CORE_TX_BD_FLAGS_START_BD_SHIFT 2 -#define CORE_TX_BD_FLAGS_IP_CSUM_MASK 0x1 -#define CORE_TX_BD_FLAGS_IP_CSUM_SHIFT 3 -#define CORE_TX_BD_FLAGS_L4_CSUM_MASK 0x1 -#define CORE_TX_BD_FLAGS_L4_CSUM_SHIFT 4 -#define CORE_TX_BD_FLAGS_IPV6_EXT_MASK 0x1 -#define CORE_TX_BD_FLAGS_IPV6_EXT_SHIFT 5 -#define CORE_TX_BD_FLAGS_L4_PROTOCOL_MASK 0x1 -#define CORE_TX_BD_FLAGS_L4_PROTOCOL_SHIFT 6 -#define CORE_TX_BD_FLAGS_L4_PSEUDO_CSUM_MODE_MASK 0x1 -#define CORE_TX_BD_FLAGS_L4_PSEUDO_CSUM_MODE_SHIFT 7 +struct core_tx_bd_data { + __le16 as_bitfield; +#define CORE_TX_BD_DATA_FORCE_VLAN_MODE_MASK 0x1 +#define CORE_TX_BD_DATA_FORCE_VLAN_MODE_SHIFT 0 +#define CORE_TX_BD_DATA_VLAN_INSERTION_MASK 0x1 +#define CORE_TX_BD_DATA_VLAN_INSERTION_SHIFT 1 +#define CORE_TX_BD_DATA_START_BD_MASK 0x1 +#define CORE_TX_BD_DATA_START_BD_SHIFT 2 +#define CORE_TX_BD_DATA_IP_CSUM_MASK 0x1 +#define CORE_TX_BD_DATA_IP_CSUM_SHIFT 3 +#define CORE_TX_BD_DATA_L4_CSUM_MASK 0x1 +#define CORE_TX_BD_DATA_L4_CSUM_SHIFT 4 +#define CORE_TX_BD_DATA_IPV6_EXT_MASK 0x1 +#define CORE_TX_BD_DATA_IPV6_EXT_SHIFT 5 +#define CORE_TX_BD_DATA_L4_PROTOCOL_MASK 0x1 +#define CORE_TX_BD_DATA_L4_PROTOCOL_SHIFT 6 +#define CORE_TX_BD_DATA_L4_PSEUDO_CSUM_MODE_MASK 0x1 +#define CORE_TX_BD_DATA_L4_PSEUDO_CSUM_MODE_SHIFT 7 +#define CORE_TX_BD_DATA_NBDS_MASK 0xF +#define CORE_TX_BD_DATA_NBDS_SHIFT 8 +#define CORE_TX_BD_DATA_ROCE_FLAV_MASK 0x1 +#define CORE_TX_BD_DATA_ROCE_FLAV_SHIFT 12 +#define CORE_TX_BD_DATA_IP_LEN_MASK 0x1 +#define CORE_TX_BD_DATA_IP_LEN_SHIFT 13 +#define CORE_TX_BD_DATA_RESERVED0_MASK 0x3 +#define CORE_TX_BD_DATA_RESERVED0_SHIFT 14 }; struct core_tx_bd { struct regpair addr; __le16 nbytes; __le16 nw_vlan_or_lb_echo; - u8 bitfield0; -#define CORE_TX_BD_NBDS_MASK 0xF -#define CORE_TX_BD_NBDS_SHIFT 0 -#define CORE_TX_BD_ROCE_FLAV_MASK 0x1 -#define CORE_TX_BD_ROCE_FLAV_SHIFT 4 -#define CORE_TX_BD_RESERVED0_MASK 0x7 -#define CORE_TX_BD_RESERVED0_SHIFT 5 - struct core_tx_bd_flags bd_flags; + struct core_tx_bd_data bd_data; __le16 bitfield1; #define CORE_TX_BD_L4_HDR_OFFSET_W_MASK 0x3FFF #define CORE_TX_BD_L4_HDR_OFFSET_W_SHIFT 0 #define CORE_TX_BD_TX_DST_MASK 0x1 #define CORE_TX_BD_TX_DST_SHIFT 14 -#define CORE_TX_BD_RESERVED1_MASK 0x1 -#define CORE_TX_BD_RESERVED1_SHIFT 15 +#define CORE_TX_BD_RESERVED_MASK 0x1 +#define CORE_TX_BD_RESERVED_SHIFT 15 }; enum core_tx_dest { @@ -800,6 +804,14 @@ struct core_tx_stop_ramrod_data { __le32 reserved0[2]; }; +enum dcb_dhcp_update_flag { + DONT_UPDATE_DCB_DHCP, + UPDATE_DCB, + UPDATE_DSCP, + UPDATE_DCB_DSCP, + MAX_DCB_DHCP_UPDATE_FLAG +}; + struct eth_mstorm_per_pf_stat { struct regpair gre_discard_pkts; struct regpair vxlan_discard_pkts; @@ -893,6 +905,12 @@ union event_ring_element { struct event_ring_next_addr next_addr; }; +enum fw_flow_ctrl_mode { + flow_ctrl_pause, + flow_ctrl_pfc, + MAX_FW_FLOW_CTRL_MODE +}; + /* Major and Minor hsi Versions */ struct hsi_fp_ver_struct { u8 minor_ver_arr[2]; @@ -921,6 +939,7 @@ enum malicious_vf_error_id { ETH_EDPM_OUT_OF_SYNC, ETH_TUNN_IPV6_EXT_NBD_ERR, ETH_CONTROL_PACKET_VIOLATION, + ETH_ANTI_SPOOFING_ERR, MAX_MALICIOUS_VF_ERROR_ID }; @@ -1106,8 +1125,9 @@ struct tstorm_per_port_stat { struct regpair ll2_mac_filter_discard; struct regpair ll2_conn_disabled_discard; struct regpair iscsi_irregular_pkt; - struct regpair reserved; + struct regpair fcoe_irregular_pkt; struct regpair roce_irregular_pkt; + struct regpair reserved; struct regpair eth_irregular_pkt; struct regpair reserved1; struct regpair preroce_irregular_pkt; @@ -1648,6 +1668,11 @@ enum block_addr { GRCBASE_MS = 0x6a0000, GRCBASE_PHY_PCIE = 0x620000, GRCBASE_LED = 0x6b8000, + GRCBASE_AVS_WRAP = 0x6b0000, + GRCBASE_RGFS = 0x19d0000, + GRCBASE_TGFS = 0x19e0000, + GRCBASE_PTLD = 0x19f0000, + GRCBASE_YPLD = 0x1a10000, GRCBASE_MISC_AEU = 0x8000, GRCBASE_BAR0_MAP = 0x1c00000, MAX_BLOCK_ADDR @@ -1732,6 +1757,11 @@ enum block_id { BLOCK_MS, BLOCK_PHY_PCIE, BLOCK_LED, + BLOCK_AVS_WRAP, + BLOCK_RGFS, + BLOCK_TGFS, + BLOCK_PTLD, + BLOCK_YPLD, BLOCK_MISC_AEU, BLOCK_BAR0_MAP, MAX_BLOCK_ID @@ -1783,9 +1813,9 @@ struct dbg_attn_reg_result { __le32 data; #define DBG_ATTN_REG_RESULT_STS_ADDRESS_MASK 0xFFFFFF #define DBG_ATTN_REG_RESULT_STS_ADDRESS_SHIFT 0 -#define DBG_ATTN_REG_RESULT_NUM_ATTN_IDX_MASK 0xFF -#define DBG_ATTN_REG_RESULT_NUM_ATTN_IDX_SHIFT 24 - __le16 attn_idx_offset; +#define DBG_ATTN_REG_RESULT_NUM_REG_ATTN_MASK 0xFF +#define DBG_ATTN_REG_RESULT_NUM_REG_ATTN_SHIFT 24 + __le16 block_attn_offset; __le16 reserved; __le32 sts_val; __le32 mask_val; @@ -1815,12 +1845,12 @@ struct dbg_mode_hdr { /* Attention register */ struct dbg_attn_reg { struct dbg_mode_hdr mode; - __le16 attn_idx_offset; + __le16 block_attn_offset; __le32 data; #define DBG_ATTN_REG_STS_ADDRESS_MASK 0xFFFFFF #define DBG_ATTN_REG_STS_ADDRESS_SHIFT 0 -#define DBG_ATTN_REG_NUM_ATTN_IDX_MASK 0xFF -#define DBG_ATTN_REG_NUM_ATTN_IDX_SHIFT 24 +#define DBG_ATTN_REG_NUM_REG_ATTN_MASK 0xFF +#define DBG_ATTN_REG_NUM_REG_ATTN_SHIFT 24 __le32 sts_clr_address; __le32 mask_address; }; @@ -2001,6 +2031,20 @@ enum dbg_bus_clients { MAX_DBG_BUS_CLIENTS }; +enum dbg_bus_constraint_ops { + DBG_BUS_CONSTRAINT_OP_EQ, + DBG_BUS_CONSTRAINT_OP_NE, + DBG_BUS_CONSTRAINT_OP_LT, + DBG_BUS_CONSTRAINT_OP_LTC, + DBG_BUS_CONSTRAINT_OP_LE, + DBG_BUS_CONSTRAINT_OP_LEC, + DBG_BUS_CONSTRAINT_OP_GT, + DBG_BUS_CONSTRAINT_OP_GTC, + DBG_BUS_CONSTRAINT_OP_GE, + DBG_BUS_CONSTRAINT_OP_GEC, + MAX_DBG_BUS_CONSTRAINT_OPS +}; + /* Debug Bus memory address */ struct dbg_bus_mem_addr { __le32 lo; @@ -2092,10 +2136,18 @@ struct dbg_bus_data { * DBG_BUS_TARGET_ID_PCI. */ __le16 reserved; - struct dbg_bus_block_data blocks[80];/* Debug Bus data for each block */ + struct dbg_bus_block_data blocks[88];/* Debug Bus data for each block */ struct dbg_bus_storm_data storms[6]; /* Debug Bus data for each block */ }; +enum dbg_bus_filter_types { + DBG_BUS_FILTER_TYPE_OFF, + DBG_BUS_FILTER_TYPE_PRE, + DBG_BUS_FILTER_TYPE_POST, + DBG_BUS_FILTER_TYPE_ON, + MAX_DBG_BUS_FILTER_TYPES +}; + /* Debug bus frame modes */ enum dbg_bus_frame_modes { DBG_BUS_FRAME_MODE_0HW_4ST = 0, /* 0 HW dwords, 4 Storm dwords */ @@ -2104,6 +2156,40 @@ enum dbg_bus_frame_modes { MAX_DBG_BUS_FRAME_MODES }; +enum dbg_bus_input_types { + DBG_BUS_INPUT_TYPE_STORM, + DBG_BUS_INPUT_TYPE_BLOCK, + MAX_DBG_BUS_INPUT_TYPES +}; + +enum dbg_bus_other_engine_modes { + DBG_BUS_OTHER_ENGINE_MODE_NONE, + DBG_BUS_OTHER_ENGINE_MODE_DOUBLE_BW_TX, + DBG_BUS_OTHER_ENGINE_MODE_DOUBLE_BW_RX, + DBG_BUS_OTHER_ENGINE_MODE_CROSS_ENGINE_TX, + DBG_BUS_OTHER_ENGINE_MODE_CROSS_ENGINE_RX, + MAX_DBG_BUS_OTHER_ENGINE_MODES +}; + +enum dbg_bus_post_trigger_types { + DBG_BUS_POST_TRIGGER_RECORD, + DBG_BUS_POST_TRIGGER_DROP, + MAX_DBG_BUS_POST_TRIGGER_TYPES +}; + +enum dbg_bus_pre_trigger_types { + DBG_BUS_PRE_TRIGGER_START_FROM_ZERO, + DBG_BUS_PRE_TRIGGER_NUM_CHUNKS, + DBG_BUS_PRE_TRIGGER_DROP, + MAX_DBG_BUS_PRE_TRIGGER_TYPES +}; + +enum dbg_bus_semi_frame_modes { + DBG_BUS_SEMI_FRAME_MODE_0SLOW_4FAST = 0, + DBG_BUS_SEMI_FRAME_MODE_4SLOW_0FAST = 3, + MAX_DBG_BUS_SEMI_FRAME_MODES +}; + /* Debug bus states */ enum dbg_bus_states { DBG_BUS_STATE_IDLE, /* debug bus idle state (not recording) */ @@ -2115,6 +2201,19 @@ enum dbg_bus_states { MAX_DBG_BUS_STATES }; +enum dbg_bus_storm_modes { + DBG_BUS_STORM_MODE_PRINTF, + DBG_BUS_STORM_MODE_PRAM_ADDR, + DBG_BUS_STORM_MODE_DRA_RW, + DBG_BUS_STORM_MODE_DRA_W, + DBG_BUS_STORM_MODE_LD_ST_ADDR, + DBG_BUS_STORM_MODE_DRA_FSM, + DBG_BUS_STORM_MODE_RH, + DBG_BUS_STORM_MODE_FOC, + DBG_BUS_STORM_MODE_EXT_STORE, + MAX_DBG_BUS_STORM_MODES +}; + /* Debug bus target IDs */ enum dbg_bus_targets { /* records debug bus to DBG block internal buffer */ @@ -2128,13 +2227,10 @@ enum dbg_bus_targets { /* GRC Dump data */ struct dbg_grc_data { - __le32 param_val[40]; /* Value of each GRC parameter. Array size must - * match the enum dbg_grc_params. - */ - u8 param_set_by_user[40]; /* Indicates for each GRC parameter if it was - * set by the user (0/1). Array size must - * match the enum dbg_grc_params. - */ + u8 params_initialized; + u8 reserved1; + __le16 reserved2; + __le32 param_val[48]; }; /* Debug GRC params */ @@ -2181,6 +2277,8 @@ enum dbg_grc_params { DBG_GRC_PARAM_PARITY_SAFE, DBG_GRC_PARAM_DUMP_CM, /* dump CM memories (0/1) */ DBG_GRC_PARAM_DUMP_PHY, /* dump PHY memories (0/1) */ + DBG_GRC_PARAM_NO_MCP, + DBG_GRC_PARAM_NO_FW_VER, MAX_DBG_GRC_PARAMS }; @@ -2280,7 +2378,7 @@ struct dbg_tools_data { struct dbg_bus_data bus; /* Debug Bus data */ struct idle_chk_data idle_chk; /* Idle Check data */ u8 mode_enable[40]; /* Indicates if a mode is enabled (0/1) */ - u8 block_in_reset[80]; /* Indicates if a block is in reset state (0/1). + u8 block_in_reset[88]; /* Indicates if a block is in reset state (0/1). */ u8 chip_id; /* Chip ID (from enum chip_ids) */ u8 platform_id; /* Platform ID (from enum platform_ids) */ @@ -2418,7 +2516,6 @@ enum init_modes { MODE_PORTS_PER_ENG_2, MODE_PORTS_PER_ENG_4, MODE_100G, - MODE_40G, MODE_RESERVED6, MAX_INIT_MODES }; @@ -2685,6 +2782,13 @@ struct iro { * @param bin_ptr - a pointer to the binary data with debug arrays. */ enum dbg_status qed_dbg_set_bin_ptr(const u8 * const bin_ptr); +/** + * @brief qed_dbg_grc_set_params_default - Reverts all GRC parameters to their + * default value. + * + * @param p_hwfn - HW device data + */ +void qed_dbg_grc_set_params_default(struct qed_hwfn *p_hwfn); /** * @brief qed_dbg_grc_get_dump_buf_size - Returns the required buffer size for * GRC Dump. @@ -3418,7 +3522,7 @@ void qed_set_geneve_enable(struct qed_hwfn *p_hwfn, #define MSTORM_TPA_TIMEOUT_US_SIZE (IRO[21].size) #define MSTORM_ETH_PF_STAT_OFFSET(pf_id) \ (IRO[22].base + ((pf_id) * IRO[22].m1)) -#define MSTORM_ETH_PF_STAT_SIZE (IRO[21].size) +#define MSTORM_ETH_PF_STAT_SIZE (IRO[22].size) #define USTORM_QUEUE_STAT_OFFSET(stat_counter_id) \ (IRO[23].base + ((stat_counter_id) * IRO[23].m1)) #define USTORM_QUEUE_STAT_SIZE (IRO[23].size) @@ -3482,7 +3586,7 @@ void qed_set_geneve_enable(struct qed_hwfn *p_hwfn, static const struct iro iro_arr[47] = { {0x0, 0x0, 0x0, 0x0, 0x8}, - {0x4cb0, 0x78, 0x0, 0x0, 0x78}, + {0x4cb0, 0x80, 0x0, 0x0, 0x80}, {0x6318, 0x20, 0x0, 0x0, 0x20}, {0xb00, 0x8, 0x0, 0x0, 0x4}, {0xa80, 0x8, 0x0, 0x0, 0x4}, @@ -3521,13 +3625,13 @@ static const struct iro iro_arr[47] = { {0xd888, 0x38, 0x0, 0x0, 0x24}, {0x12c38, 0x10, 0x0, 0x0, 0x8}, {0x11aa0, 0x38, 0x0, 0x0, 0x18}, - {0xa8c0, 0x30, 0x0, 0x0, 0x10}, - {0x86f8, 0x28, 0x0, 0x0, 0x18}, + {0xa8c0, 0x38, 0x0, 0x0, 0x10}, + {0x86f8, 0x30, 0x0, 0x0, 0x18}, {0x101f8, 0x10, 0x0, 0x0, 0x10}, {0xdd08, 0x48, 0x0, 0x0, 0x38}, {0x10660, 0x20, 0x0, 0x0, 0x20}, {0x2b80, 0x80, 0x0, 0x0, 0x10}, - {0x5000, 0x10, 0x0, 0x0, 0x10}, + {0x5020, 0x10, 0x0, 0x0, 0x10}, }; /* Runtime array offsets */ @@ -4595,6 +4699,12 @@ enum eth_ipv4_frag_type { MAX_ETH_IPV4_FRAG_TYPE }; +enum eth_ip_type { + ETH_IPV4, + ETH_IPV6, + MAX_ETH_IP_TYPE +}; + enum eth_ramrod_cmd_id { ETH_RAMROD_UNUSED, ETH_RAMROD_VPORT_START, @@ -4944,7 +5054,10 @@ struct vport_update_ramrod_data_cmn { u8 update_mtu_flg; __le16 mtu; - u8 reserved[2]; + u8 update_ctl_frame_checks_en_flg; + u8 ctl_frame_mac_check_en; + u8 ctl_frame_ethtype_check_en; + u8 reserved[15]; }; struct vport_update_ramrod_mcast { @@ -4962,6 +5075,492 @@ struct vport_update_ramrod_data { struct eth_vport_rss_config rss_config; }; +struct mstorm_eth_conn_ag_ctx { + u8 byte0; + u8 byte1; + u8 flags0; +#define MSTORM_ETH_CONN_AG_CTX_EXIST_IN_QM0_MASK 0x1 +#define MSTORM_ETH_CONN_AG_CTX_EXIST_IN_QM0_SHIFT 0 +#define MSTORM_ETH_CONN_AG_CTX_BIT1_MASK 0x1 +#define MSTORM_ETH_CONN_AG_CTX_BIT1_SHIFT 1 +#define MSTORM_ETH_CONN_AG_CTX_CF0_MASK 0x3 +#define MSTORM_ETH_CONN_AG_CTX_CF0_SHIFT 2 +#define MSTORM_ETH_CONN_AG_CTX_CF1_MASK 0x3 +#define MSTORM_ETH_CONN_AG_CTX_CF1_SHIFT 4 +#define MSTORM_ETH_CONN_AG_CTX_CF2_MASK 0x3 +#define MSTORM_ETH_CONN_AG_CTX_CF2_SHIFT 6 + u8 flags1; +#define MSTORM_ETH_CONN_AG_CTX_CF0EN_MASK 0x1 +#define MSTORM_ETH_CONN_AG_CTX_CF0EN_SHIFT 0 +#define MSTORM_ETH_CONN_AG_CTX_CF1EN_MASK 0x1 +#define MSTORM_ETH_CONN_AG_CTX_CF1EN_SHIFT 1 +#define MSTORM_ETH_CONN_AG_CTX_CF2EN_MASK 0x1 +#define MSTORM_ETH_CONN_AG_CTX_CF2EN_SHIFT 2 +#define MSTORM_ETH_CONN_AG_CTX_RULE0EN_MASK 0x1 +#define MSTORM_ETH_CONN_AG_CTX_RULE0EN_SHIFT 3 +#define MSTORM_ETH_CONN_AG_CTX_RULE1EN_MASK 0x1 +#define MSTORM_ETH_CONN_AG_CTX_RULE1EN_SHIFT 4 +#define MSTORM_ETH_CONN_AG_CTX_RULE2EN_MASK 0x1 +#define MSTORM_ETH_CONN_AG_CTX_RULE2EN_SHIFT 5 +#define MSTORM_ETH_CONN_AG_CTX_RULE3EN_MASK 0x1 +#define MSTORM_ETH_CONN_AG_CTX_RULE3EN_SHIFT 6 +#define MSTORM_ETH_CONN_AG_CTX_RULE4EN_MASK 0x1 +#define MSTORM_ETH_CONN_AG_CTX_RULE4EN_SHIFT 7 + __le16 word0; + __le16 word1; + __le32 reg0; + __le32 reg1; +}; + +struct xstorm_eth_conn_agctxdq_ext_ldpart { + u8 reserved0; + u8 eth_state; + u8 flags0; +#define XSTORMETHCONNAGCTXDQEXTLDPART_EXIST_IN_QM0_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_EXIST_IN_QM0_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED1_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED1_SHIFT 1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED2_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED2_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_EXIST_IN_QM3_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_EXIST_IN_QM3_SHIFT 3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED3_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED3_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED4_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED4_SHIFT 5 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED5_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED5_SHIFT 6 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED6_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED6_SHIFT 7 + u8 flags1; +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED7_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED7_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED8_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED8_SHIFT 1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED9_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED9_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_BIT11_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_BIT11_SHIFT 3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_BIT12_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_BIT12_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_BIT13_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_BIT13_SHIFT 5 +#define XSTORMETHCONNAGCTXDQEXTLDPART_TX_RULE_ACTIVE_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_TX_RULE_ACTIVE_SHIFT 6 +#define XSTORMETHCONNAGCTXDQEXTLDPART_DQ_CF_ACTIVE_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_DQ_CF_ACTIVE_SHIFT 7 + u8 flags2; +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF0_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF0_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF1_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF1_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF2_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF2_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF3_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF3_SHIFT 6 + u8 flags3; +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF4_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF4_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF5_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF5_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF6_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF6_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF7_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF7_SHIFT 6 + u8 flags4; +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF8_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF8_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF9_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF9_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF10_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF10_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF11_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF11_SHIFT 6 + u8 flags5; +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF12_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF12_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF13_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF13_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF14_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF14_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF15_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF15_SHIFT 6 + u8 flags6; +#define XSTORMETHCONNAGCTXDQEXTLDPART_GO_TO_BD_CONS_CF_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_GO_TO_BD_CONS_CF_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_MULTI_UNICAST_CF_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_MULTI_UNICAST_CF_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_DQ_CF_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_DQ_CF_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_TERMINATE_CF_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_TERMINATE_CF_SHIFT 6 + u8 flags7; +#define XSTORMETHCONNAGCTXDQEXTLDPART_FLUSH_Q0_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_FLUSH_Q0_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED10_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED10_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_SLOW_PATH_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_SLOW_PATH_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF0EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF0EN_SHIFT 6 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF1EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF1EN_SHIFT 7 + u8 flags8; +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF2EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF2EN_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF3EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF3EN_SHIFT 1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF4EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF4EN_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF5EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF5EN_SHIFT 3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF6EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF6EN_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF7EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF7EN_SHIFT 5 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF8EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF8EN_SHIFT 6 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF9EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF9EN_SHIFT 7 + u8 flags9; +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF10EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF10EN_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF11EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF11EN_SHIFT 1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF12EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF12EN_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF13EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF13EN_SHIFT 3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF14EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF14EN_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF15EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_CF15EN_SHIFT 5 +#define XSTORMETHCONNAGCTXDQEXTLDPART_GO_TO_BD_CONS_CF_EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_GO_TO_BD_CONS_CF_EN_SHIFT 6 +#define XSTORMETHCONNAGCTXDQEXTLDPART_MULTI_UNICAST_CF_EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_MULTI_UNICAST_CF_EN_SHIFT 7 + u8 flags10; +#define XSTORMETHCONNAGCTXDQEXTLDPART_DQ_CF_EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_DQ_CF_EN_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_TERMINATE_CF_EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_TERMINATE_CF_EN_SHIFT 1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_FLUSH_Q0_EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_FLUSH_Q0_EN_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED11_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED11_SHIFT 3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_SLOW_PATH_EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_SLOW_PATH_EN_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_TPH_ENABLE_EN_RESERVED_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_TPH_ENABLE_EN_RESERVED_SHIFT 5 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED12_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED12_SHIFT 6 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED13_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED13_SHIFT 7 + u8 flags11; +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED14_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED14_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED15_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RESERVED15_SHIFT 1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_TX_DEC_RULE_EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_TX_DEC_RULE_EN_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE5EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE5EN_SHIFT 3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE6EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE6EN_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE7EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE7EN_SHIFT 5 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED1_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED1_SHIFT 6 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE9EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE9EN_SHIFT 7 + u8 flags12; +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE10EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE10EN_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE11EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE11EN_SHIFT 1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED2_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED2_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED3_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED3_SHIFT 3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE14EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE14EN_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE15EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE15EN_SHIFT 5 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE16EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE16EN_SHIFT 6 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE17EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE17EN_SHIFT 7 + u8 flags13; +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE18EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE18EN_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE19EN_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_RULE19EN_SHIFT 1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED4_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED4_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED5_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED5_SHIFT 3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED6_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED6_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED7_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED7_SHIFT 5 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED8_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED8_SHIFT 6 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED9_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_A0_RESERVED9_SHIFT 7 + u8 flags14; +#define XSTORMETHCONNAGCTXDQEXTLDPART_EDPM_USE_EXT_HDR_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_EDPM_USE_EXT_HDR_SHIFT 0 +#define XSTORMETHCONNAGCTXDQEXTLDPART_EDPM_SEND_RAW_L3L4_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_EDPM_SEND_RAW_L3L4_SHIFT 1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_EDPM_INBAND_PROP_HDR_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_EDPM_INBAND_PROP_HDR_SHIFT 2 +#define XSTORMETHCONNAGCTXDQEXTLDPART_EDPM_SEND_EXT_TUNNEL_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_EDPM_SEND_EXT_TUNNEL_SHIFT 3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_L2_EDPM_ENABLE_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_L2_EDPM_ENABLE_SHIFT 4 +#define XSTORMETHCONNAGCTXDQEXTLDPART_ROCE_EDPM_ENABLE_MASK 0x1 +#define XSTORMETHCONNAGCTXDQEXTLDPART_ROCE_EDPM_ENABLE_SHIFT 5 +#define XSTORMETHCONNAGCTXDQEXTLDPART_TPH_ENABLE_MASK 0x3 +#define XSTORMETHCONNAGCTXDQEXTLDPART_TPH_ENABLE_SHIFT 6 + u8 edpm_event_id; + __le16 physical_q0; + __le16 quota; + __le16 edpm_num_bds; + __le16 tx_bd_cons; + __le16 tx_bd_prod; + __le16 tx_class; + __le16 conn_dpi; + u8 byte3; + u8 byte4; + u8 byte5; + u8 byte6; + __le32 reg0; + __le32 reg1; + __le32 reg2; + __le32 reg3; + __le32 reg4; +}; + +struct xstorm_eth_hw_conn_ag_ctx { + u8 reserved0; + u8 eth_state; + u8 flags0; +#define XSTORM_ETH_HW_CONN_AG_CTX_EXIST_IN_QM0_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_EXIST_IN_QM0_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED1_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED1_SHIFT 1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED2_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED2_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_EXIST_IN_QM3_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_EXIST_IN_QM3_SHIFT 3 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED3_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED3_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED4_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED4_SHIFT 5 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED5_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED5_SHIFT 6 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED6_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED6_SHIFT 7 + u8 flags1; +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED7_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED7_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED8_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED8_SHIFT 1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED9_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED9_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_BIT11_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_BIT11_SHIFT 3 +#define XSTORM_ETH_HW_CONN_AG_CTX_BIT12_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_BIT12_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_BIT13_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_BIT13_SHIFT 5 +#define XSTORM_ETH_HW_CONN_AG_CTX_TX_RULE_ACTIVE_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_TX_RULE_ACTIVE_SHIFT 6 +#define XSTORM_ETH_HW_CONN_AG_CTX_DQ_CF_ACTIVE_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_DQ_CF_ACTIVE_SHIFT 7 + u8 flags2; +#define XSTORM_ETH_HW_CONN_AG_CTX_CF0_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF0_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF1_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF1_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF2_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF2_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF3_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF3_SHIFT 6 + u8 flags3; +#define XSTORM_ETH_HW_CONN_AG_CTX_CF4_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF4_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF5_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF5_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF6_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF6_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF7_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF7_SHIFT 6 + u8 flags4; +#define XSTORM_ETH_HW_CONN_AG_CTX_CF8_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF8_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF9_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF9_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF10_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF10_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF11_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF11_SHIFT 6 + u8 flags5; +#define XSTORM_ETH_HW_CONN_AG_CTX_CF12_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF12_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF13_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF13_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF14_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF14_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF15_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF15_SHIFT 6 + u8 flags6; +#define XSTORM_ETH_HW_CONN_AG_CTX_GO_TO_BD_CONS_CF_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_GO_TO_BD_CONS_CF_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_MULTI_UNICAST_CF_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_MULTI_UNICAST_CF_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_DQ_CF_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_DQ_CF_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_TERMINATE_CF_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_TERMINATE_CF_SHIFT 6 + u8 flags7; +#define XSTORM_ETH_HW_CONN_AG_CTX_FLUSH_Q0_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_FLUSH_Q0_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED10_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED10_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_SLOW_PATH_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_SLOW_PATH_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF0EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF0EN_SHIFT 6 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF1EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF1EN_SHIFT 7 + u8 flags8; +#define XSTORM_ETH_HW_CONN_AG_CTX_CF2EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF2EN_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF3EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF3EN_SHIFT 1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF4EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF4EN_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF5EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF5EN_SHIFT 3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF6EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF6EN_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF7EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF7EN_SHIFT 5 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF8EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF8EN_SHIFT 6 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF9EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF9EN_SHIFT 7 + u8 flags9; +#define XSTORM_ETH_HW_CONN_AG_CTX_CF10EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF10EN_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF11EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF11EN_SHIFT 1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF12EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF12EN_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF13EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF13EN_SHIFT 3 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF14EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF14EN_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF15EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_CF15EN_SHIFT 5 +#define XSTORM_ETH_HW_CONN_AG_CTX_GO_TO_BD_CONS_CF_EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_GO_TO_BD_CONS_CF_EN_SHIFT 6 +#define XSTORM_ETH_HW_CONN_AG_CTX_MULTI_UNICAST_CF_EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_MULTI_UNICAST_CF_EN_SHIFT 7 + u8 flags10; +#define XSTORM_ETH_HW_CONN_AG_CTX_DQ_CF_EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_DQ_CF_EN_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_TERMINATE_CF_EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_TERMINATE_CF_EN_SHIFT 1 +#define XSTORM_ETH_HW_CONN_AG_CTX_FLUSH_Q0_EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_FLUSH_Q0_EN_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED11_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED11_SHIFT 3 +#define XSTORM_ETH_HW_CONN_AG_CTX_SLOW_PATH_EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_SLOW_PATH_EN_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_TPH_ENABLE_EN_RESERVED_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_TPH_ENABLE_EN_RESERVED_SHIFT 5 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED12_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED12_SHIFT 6 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED13_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED13_SHIFT 7 + u8 flags11; +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED14_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED14_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED15_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RESERVED15_SHIFT 1 +#define XSTORM_ETH_HW_CONN_AG_CTX_TX_DEC_RULE_EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_TX_DEC_RULE_EN_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE5EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE5EN_SHIFT 3 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE6EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE6EN_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE7EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE7EN_SHIFT 5 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED1_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED1_SHIFT 6 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE9EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE9EN_SHIFT 7 + u8 flags12; +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE10EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE10EN_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE11EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE11EN_SHIFT 1 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED2_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED2_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED3_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED3_SHIFT 3 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE14EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE14EN_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE15EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE15EN_SHIFT 5 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE16EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE16EN_SHIFT 6 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE17EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE17EN_SHIFT 7 + u8 flags13; +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE18EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE18EN_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE19EN_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_RULE19EN_SHIFT 1 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED4_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED4_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED5_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED5_SHIFT 3 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED6_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED6_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED7_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED7_SHIFT 5 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED8_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED8_SHIFT 6 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED9_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_A0_RESERVED9_SHIFT 7 + u8 flags14; +#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_USE_EXT_HDR_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_USE_EXT_HDR_SHIFT 0 +#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_SEND_RAW_L3L4_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_SEND_RAW_L3L4_SHIFT 1 +#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_INBAND_PROP_HDR_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_INBAND_PROP_HDR_SHIFT 2 +#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_SEND_EXT_TUNNEL_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_EDPM_SEND_EXT_TUNNEL_SHIFT 3 +#define XSTORM_ETH_HW_CONN_AG_CTX_L2_EDPM_ENABLE_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_L2_EDPM_ENABLE_SHIFT 4 +#define XSTORM_ETH_HW_CONN_AG_CTX_ROCE_EDPM_ENABLE_MASK 0x1 +#define XSTORM_ETH_HW_CONN_AG_CTX_ROCE_EDPM_ENABLE_SHIFT 5 +#define XSTORM_ETH_HW_CONN_AG_CTX_TPH_ENABLE_MASK 0x3 +#define XSTORM_ETH_HW_CONN_AG_CTX_TPH_ENABLE_SHIFT 6 + u8 edpm_event_id; + __le16 physical_q0; + __le16 quota; + __le16 edpm_num_bds; + __le16 tx_bd_cons; + __le16 tx_bd_prod; + __le16 tx_class; + __le16 conn_dpi; +}; + struct mstorm_rdma_task_st_ctx { struct regpair temp[4]; }; @@ -6165,7 +6764,7 @@ struct ystorm_roce_conn_st_ctx { }; struct xstorm_roce_conn_st_ctx { - struct regpair temp[22]; + struct regpair temp[24]; }; struct tstorm_roce_conn_st_ctx { @@ -6220,7 +6819,7 @@ struct roce_create_qp_req_ramrod_data { __le16 mtu; __le16 pd; __le16 sq_num_pages; - __le16 reseved2; + __le16 low_latency_phy_queue; struct regpair sq_pbl_addr; struct regpair orq_pbl_addr; __le16 local_mac_addr[3]; @@ -6234,7 +6833,7 @@ struct roce_create_qp_req_ramrod_data { u8 stats_counter_id; u8 reserved3[7]; __le32 cq_cid; - __le16 physical_queue0; + __le16 regular_latency_phy_queue; __le16 dpi; }; @@ -6282,15 +6881,16 @@ struct roce_create_qp_resp_ramrod_data { __le32 dst_gid[4]; struct regpair qp_handle_for_cqe; struct regpair qp_handle_for_async; - __le32 reserved2[2]; + __le16 low_latency_phy_queue; + u8 reserved2[6]; __le32 cq_cid; - __le16 physical_queue0; + __le16 regular_latency_phy_queue; __le16 dpi; }; struct roce_destroy_qp_req_output_params { __le32 num_bound_mw; - __le32 reserved; + __le32 cq_prod; }; struct roce_destroy_qp_req_ramrod_data { @@ -6299,7 +6899,7 @@ struct roce_destroy_qp_req_ramrod_data { struct roce_destroy_qp_resp_output_params { __le32 num_invalidated_mw; - __le32 reserved; + __le32 cq_prod; }; struct roce_destroy_qp_resp_ramrod_data { @@ -7426,6 +8026,7 @@ struct ystorm_fcoe_conn_st_ctx { u8 fcp_rsp_size; __le16 mss; struct regpair reserved; + __le16 min_frame_size; u8 protection_info_flags; #define YSTORM_FCOE_CONN_ST_CTX_SUPPORT_PROTECTION_MASK 0x1 #define YSTORM_FCOE_CONN_ST_CTX_SUPPORT_PROTECTION_SHIFT 0 @@ -7444,7 +8045,6 @@ struct ystorm_fcoe_conn_st_ctx { #define YSTORM_FCOE_CONN_ST_CTX_RSRV_MASK 0x3F #define YSTORM_FCOE_CONN_ST_CTX_RSRV_SHIFT 2 u8 fcp_xfer_size; - u8 reserved3[2]; }; struct fcoe_vlan_fields { @@ -8273,10 +8873,10 @@ struct xstorm_iscsi_conn_ag_ctx { #define XSTORM_ISCSI_CONN_AG_CTX_DQ_FLUSH_MASK 0x3 #define XSTORM_ISCSI_CONN_AG_CTX_DQ_FLUSH_SHIFT 6 u8 flags7; -#define XSTORM_ISCSI_CONN_AG_CTX_FLUSH_Q0_MASK 0x3 -#define XSTORM_ISCSI_CONN_AG_CTX_FLUSH_Q0_SHIFT 0 -#define XSTORM_ISCSI_CONN_AG_CTX_FLUSH_Q1_MASK 0x3 -#define XSTORM_ISCSI_CONN_AG_CTX_FLUSH_Q1_SHIFT 2 +#define XSTORM_ISCSI_CONN_AG_CTX_MST_XCM_Q0_FLUSH_CF_MASK 0x3 +#define XSTORM_ISCSI_CONN_AG_CTX_MST_XCM_Q0_FLUSH_CF_SHIFT 0 +#define XSTORM_ISCSI_CONN_AG_CTX_UST_XCM_Q1_FLUSH_CF_MASK 0x3 +#define XSTORM_ISCSI_CONN_AG_CTX_UST_XCM_Q1_FLUSH_CF_SHIFT 2 #define XSTORM_ISCSI_CONN_AG_CTX_SLOW_PATH_MASK 0x3 #define XSTORM_ISCSI_CONN_AG_CTX_SLOW_PATH_SHIFT 4 #define XSTORM_ISCSI_CONN_AG_CTX_CF0EN_MASK 0x1 @@ -8322,10 +8922,10 @@ struct xstorm_iscsi_conn_ag_ctx { #define XSTORM_ISCSI_CONN_AG_CTX_CF18EN_SHIFT 0 #define XSTORM_ISCSI_CONN_AG_CTX_DQ_FLUSH_EN_MASK 0x1 #define XSTORM_ISCSI_CONN_AG_CTX_DQ_FLUSH_EN_SHIFT 1 -#define XSTORM_ISCSI_CONN_AG_CTX_FLUSH_Q0_EN_MASK 0x1 -#define XSTORM_ISCSI_CONN_AG_CTX_FLUSH_Q0_EN_SHIFT 2 -#define XSTORM_ISCSI_CONN_AG_CTX_FLUSH_Q1_EN_MASK 0x1 -#define XSTORM_ISCSI_CONN_AG_CTX_FLUSH_Q1_EN_SHIFT 3 +#define XSTORM_ISCSI_CONN_AG_CTX_MST_XCM_Q0_FLUSH_CF_EN_MASK 0x1 +#define XSTORM_ISCSI_CONN_AG_CTX_MST_XCM_Q0_FLUSH_CF_EN_SHIFT 2 +#define XSTORM_ISCSI_CONN_AG_CTX_UST_XCM_Q1_FLUSH_CF_EN_MASK 0x1 +#define XSTORM_ISCSI_CONN_AG_CTX_UST_XCM_Q1_FLUSH_CF_EN_SHIFT 3 #define XSTORM_ISCSI_CONN_AG_CTX_SLOW_PATH_EN_MASK 0x1 #define XSTORM_ISCSI_CONN_AG_CTX_SLOW_PATH_EN_SHIFT 4 #define XSTORM_ISCSI_CONN_AG_CTX_PROC_ONLY_CLEANUP_EN_MASK 0x1 @@ -8335,8 +8935,8 @@ struct xstorm_iscsi_conn_ag_ctx { #define XSTORM_ISCSI_CONN_AG_CTX_MORE_TO_SEND_DEC_RULE_EN_MASK 0x1 #define XSTORM_ISCSI_CONN_AG_CTX_MORE_TO_SEND_DEC_RULE_EN_SHIFT 7 u8 flags11; -#define XSTORM_ISCSI_CONN_AG_CTX_RULE2EN_MASK 0x1 -#define XSTORM_ISCSI_CONN_AG_CTX_RULE2EN_SHIFT 0 +#define XSTORM_ISCSI_CONN_AG_CTX_TX_BLOCKED_EN_MASK 0x1 +#define XSTORM_ISCSI_CONN_AG_CTX_TX_BLOCKED_EN_SHIFT 0 #define XSTORM_ISCSI_CONN_AG_CTX_RULE3EN_MASK 0x1 #define XSTORM_ISCSI_CONN_AG_CTX_RULE3EN_SHIFT 1 #define XSTORM_ISCSI_CONN_AG_CTX_RESERVED3_MASK 0x1 @@ -8440,7 +9040,7 @@ struct xstorm_iscsi_conn_ag_ctx { __le32 reg10; __le32 reg11; __le32 exp_stat_sn; - __le32 reg13; + __le32 ongoing_fast_rxmit_seq; __le32 reg14; __le32 reg15; __le32 reg16; @@ -8466,10 +9066,10 @@ struct tstorm_iscsi_conn_ag_ctx { #define TSTORM_ISCSI_CONN_AG_CTX_CF0_MASK 0x3 #define TSTORM_ISCSI_CONN_AG_CTX_CF0_SHIFT 6 u8 flags1; -#define TSTORM_ISCSI_CONN_AG_CTX_CF1_MASK 0x3 -#define TSTORM_ISCSI_CONN_AG_CTX_CF1_SHIFT 0 -#define TSTORM_ISCSI_CONN_AG_CTX_CF2_MASK 0x3 -#define TSTORM_ISCSI_CONN_AG_CTX_CF2_SHIFT 2 +#define TSTORM_ISCSI_CONN_AG_CTX_P2T_FLUSH_CF_MASK 0x3 +#define TSTORM_ISCSI_CONN_AG_CTX_P2T_FLUSH_CF_SHIFT 0 +#define TSTORM_ISCSI_CONN_AG_CTX_M2T_FLUSH_CF_MASK 0x3 +#define TSTORM_ISCSI_CONN_AG_CTX_M2T_FLUSH_CF_SHIFT 2 #define TSTORM_ISCSI_CONN_AG_CTX_TIMER_STOP_ALL_MASK 0x3 #define TSTORM_ISCSI_CONN_AG_CTX_TIMER_STOP_ALL_SHIFT 4 #define TSTORM_ISCSI_CONN_AG_CTX_CF4_MASK 0x3 @@ -8490,10 +9090,10 @@ struct tstorm_iscsi_conn_ag_ctx { #define TSTORM_ISCSI_CONN_AG_CTX_CF10_SHIFT 2 #define TSTORM_ISCSI_CONN_AG_CTX_CF0EN_MASK 0x1 #define TSTORM_ISCSI_CONN_AG_CTX_CF0EN_SHIFT 4 -#define TSTORM_ISCSI_CONN_AG_CTX_CF1EN_MASK 0x1 -#define TSTORM_ISCSI_CONN_AG_CTX_CF1EN_SHIFT 5 -#define TSTORM_ISCSI_CONN_AG_CTX_CF2EN_MASK 0x1 -#define TSTORM_ISCSI_CONN_AG_CTX_CF2EN_SHIFT 6 +#define TSTORM_ISCSI_CONN_AG_CTX_P2T_FLUSH_CF_EN_MASK 0x1 +#define TSTORM_ISCSI_CONN_AG_CTX_P2T_FLUSH_CF_EN_SHIFT 5 +#define TSTORM_ISCSI_CONN_AG_CTX_M2T_FLUSH_CF_EN_MASK 0x1 +#define TSTORM_ISCSI_CONN_AG_CTX_M2T_FLUSH_CF_EN_SHIFT 6 #define TSTORM_ISCSI_CONN_AG_CTX_TIMER_STOP_ALL_EN_MASK 0x1 #define TSTORM_ISCSI_CONN_AG_CTX_TIMER_STOP_ALL_EN_SHIFT 7 u8 flags4; @@ -8539,7 +9139,7 @@ struct tstorm_iscsi_conn_ag_ctx { __le32 reg6; __le32 reg7; __le32 reg8; - u8 byte2; + u8 cid_offload_cnt; u8 byte3; __le16 word0; }; @@ -9067,6 +9667,10 @@ struct dcb_dscp_map { struct public_global { u32 max_path; u32 max_ports; +#define MODE_1P 1 +#define MODE_2P 2 +#define MODE_3P 3 +#define MODE_4P 4 u32 debug_mb_offset; u32 phymod_dbg_mb_offset; struct couple_mode_teaming cmt; diff --git a/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c b/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c index d891a6852695..2a50e2b7568f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c +++ b/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c @@ -215,13 +215,6 @@ static void qed_cmdq_lines_voq_rt_init(struct qed_hwfn *p_hwfn, { u32 qm_line_crd; - /* In A0 - Limit the size of pbf queue so that only 511 commands with - * the minimum size of 4 (FCoE minimum size) - */ - bool is_bb_a0 = QED_IS_BB_A0(p_hwfn->cdev); - - if (is_bb_a0) - cmdq_lines = min_t(u32, cmdq_lines, 1022); qm_line_crd = QM_VOQ_LINE_CRD(cmdq_lines); OVERWRITE_RT_REG(p_hwfn, PBF_CMDQ_LINES_RT_OFFSET(voq), (u32)cmdq_lines); @@ -343,13 +336,11 @@ static void qed_tx_pq_map_rt_init( u16 first_pq_group = p_params->start_pq / QM_PF_QUEUE_GROUP_SIZE; u16 last_pq_group = (p_params->start_pq + num_pqs - 1) / QM_PF_QUEUE_GROUP_SIZE; - bool is_bb_a0 = QED_IS_BB_A0(p_hwfn->cdev); u16 i, pq_id, pq_group; /* a bit per Tx PQ indicating if the PQ is associated with a VF */ u32 tx_pq_vf_mask[MAX_QM_TX_QUEUES / QM_PF_QUEUE_GROUP_SIZE] = { 0 }; - u32 tx_pq_vf_mask_width = is_bb_a0 ? 32 : QM_PF_QUEUE_GROUP_SIZE; - u32 num_tx_pq_vf_masks = MAX_QM_TX_QUEUES / tx_pq_vf_mask_width; + u32 num_tx_pq_vf_masks = MAX_QM_TX_QUEUES / QM_PF_QUEUE_GROUP_SIZE; u32 pq_mem_4kb = QM_PQ_MEM_4KB(p_params->num_pf_cids); u32 vport_pq_mem_4kb = QM_PQ_MEM_4KB(p_params->num_vf_cids); u32 mem_addr_4kb = base_mem_addr_4kb; @@ -371,6 +362,10 @@ static void qed_tx_pq_map_rt_init( bool is_vf_pq = (i >= p_params->num_pf_pqs); struct qm_rf_pq_map tx_pq_map; + bool rl_valid = p_params->pq_params[i].rl_valid && + (p_params->pq_params[i].vport_id < + MAX_QM_GLOBAL_RLS); + /* update first Tx PQ of VPORT/TC */ u8 vport_id_in_pf = p_params->pq_params[i].vport_id - p_params->start_vport; @@ -389,14 +384,18 @@ static void qed_tx_pq_map_rt_init( (p_params->pf_id << QM_WFQ_VP_PQ_PF_SHIFT)); } + + if (p_params->pq_params[i].rl_valid && !rl_valid) + DP_NOTICE(p_hwfn, + "Invalid VPORT ID for rate limiter configuration"); /* fill PQ map entry */ memset(&tx_pq_map, 0, sizeof(tx_pq_map)); SET_FIELD(tx_pq_map.reg, QM_RF_PQ_MAP_PQ_VALID, 1); - SET_FIELD(tx_pq_map.reg, QM_RF_PQ_MAP_RL_VALID, - p_params->pq_params[i].rl_valid ? 1 : 0); + SET_FIELD(tx_pq_map.reg, + QM_RF_PQ_MAP_RL_VALID, rl_valid ? 1 : 0); SET_FIELD(tx_pq_map.reg, QM_RF_PQ_MAP_VP_PQ_ID, first_tx_pq_id); SET_FIELD(tx_pq_map.reg, QM_RF_PQ_MAP_RL_ID, - p_params->pq_params[i].rl_valid ? + rl_valid ? p_params->pq_params[i].vport_id : 0); SET_FIELD(tx_pq_map.reg, QM_RF_PQ_MAP_VOQ, voq); SET_FIELD(tx_pq_map.reg, QM_RF_PQ_MAP_WRR_WEIGHT_GROUP, @@ -413,8 +412,9 @@ static void qed_tx_pq_map_rt_init( /* if PQ is associated with a VF, add indication * to PQ VF mask */ - tx_pq_vf_mask[pq_id / tx_pq_vf_mask_width] |= - (1 << (pq_id % tx_pq_vf_mask_width)); + tx_pq_vf_mask[pq_id / + QM_PF_QUEUE_GROUP_SIZE] |= + BIT((pq_id % QM_PF_QUEUE_GROUP_SIZE)); mem_addr_4kb += vport_pq_mem_4kb; } else { mem_addr_4kb += pq_mem_4kb; @@ -480,8 +480,8 @@ static int qed_pf_wfq_rt_init(struct qed_hwfn *p_hwfn, if (p_params->pf_id < MAX_NUM_PFS_BB) crd_reg_offset = QM_REG_WFQPFCRD_RT_OFFSET; else - crd_reg_offset = QM_REG_WFQPFCRD_MSB_RT_OFFSET + - (p_params->pf_id % MAX_NUM_PFS_BB); + crd_reg_offset = QM_REG_WFQPFCRD_MSB_RT_OFFSET; + crd_reg_offset += p_params->pf_id % MAX_NUM_PFS_BB; inc_val = QM_WFQ_INC_VAL(p_params->pf_wfq); if (!inc_val || inc_val > QM_WFQ_MAX_INC_VAL) { @@ -498,11 +498,11 @@ static int qed_pf_wfq_rt_init(struct qed_hwfn *p_hwfn, QM_WFQ_CRD_REG_SIGN_BIT); } - STORE_RT_REG(p_hwfn, QM_REG_WFQPFWEIGHT_RT_OFFSET + p_params->pf_id, - inc_val); STORE_RT_REG(p_hwfn, QM_REG_WFQPFUPPERBOUND_RT_OFFSET + p_params->pf_id, QM_WFQ_UPPER_BOUND | QM_WFQ_CRD_REG_SIGN_BIT); + STORE_RT_REG(p_hwfn, QM_REG_WFQPFWEIGHT_RT_OFFSET + p_params->pf_id, + inc_val); return 0; } @@ -576,6 +576,12 @@ static int qed_vport_rl_rt_init(struct qed_hwfn *p_hwfn, { u8 i, vport_id; + if (start_vport + num_vports >= MAX_QM_GLOBAL_RLS) { + DP_NOTICE(p_hwfn, + "Invalid VPORT ID for rate limiter configuration"); + return -1; + } + /* go over all PF VPORTs */ for (i = 0, vport_id = start_vport; i < num_vports; i++, vport_id++) { u32 inc_val = QM_RL_INC_VAL(vport_params[i].vport_rl); @@ -785,6 +791,12 @@ int qed_init_vport_rl(struct qed_hwfn *p_hwfn, { u32 inc_val = QM_RL_INC_VAL(vport_rl); + if (vport_id >= MAX_QM_GLOBAL_RLS) { + DP_NOTICE(p_hwfn, + "Invalid VPORT ID for rate limiter configuration"); + return -1; + } + if (inc_val > QM_RL_MAX_INC_VAL) { DP_NOTICE(p_hwfn, "Invalid VPORT rate-limit configuration"); return -1; @@ -940,12 +952,6 @@ void qed_set_geneve_enable(struct qed_hwfn *p_hwfn, eth_geneve_enable ? 1 : 0); qed_wr(p_hwfn, p_ptt, NIG_REG_NGE_IP_ENABLE, ip_geneve_enable ? 1 : 0); - /* comp ver */ - reg_val = (ip_geneve_enable || eth_geneve_enable) ? 1 : 0; - qed_wr(p_hwfn, p_ptt, NIG_REG_NGE_COMP_VER, reg_val); - qed_wr(p_hwfn, p_ptt, PBF_REG_NGE_COMP_VER, reg_val); - qed_wr(p_hwfn, p_ptt, PRS_REG_NGE_COMP_VER, reg_val); - /* EDPM with geneve tunnel not supported in BB_B0 */ if (QED_IS_BB_B0(p_hwfn->cdev)) return; diff --git a/drivers/net/ethernet/qlogic/qed/qed_init_ops.c b/drivers/net/ethernet/qlogic/qed/qed_init_ops.c index 243b64e0d4dc..4a2e7be5bf72 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_init_ops.c +++ b/drivers/net/ethernet/qlogic/qed/qed_init_ops.c @@ -554,7 +554,7 @@ int qed_init_fw_data(struct qed_dev *cdev, const u8 *data) } /* First Dword contains metadata and should be skipped */ - buf_hdr = (struct bin_buffer_hdr *)(data + sizeof(u32)); + buf_hdr = (struct bin_buffer_hdr *)data; offset = buf_hdr[BIN_BUF_INIT_FW_VER_INFO].offset; fw->fw_ver_info = (struct fw_ver_info *)(data + offset); diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c index 9a0b9af10a57..161d90376dae 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c @@ -594,7 +594,7 @@ static u8 qed_ll2_convert_rx_parse_to_tx_flags(u16 parse_flags) u8 bd_flags = 0; if (GET_FIELD(parse_flags, PARSING_AND_ERR_FLAGS_TAG8021QEXIST)) - SET_FIELD(bd_flags, CORE_TX_BD_FLAGS_VLAN_INSERTION, 1); + SET_FIELD(bd_flags, CORE_TX_BD_DATA_VLAN_INSERTION, 1); return bd_flags; } @@ -755,8 +755,8 @@ qed_ooo_submit_tx_buffers(struct qed_hwfn *p_hwfn, p_buffer->placement_offset; parse_flags = p_buffer->parse_flags; bd_flags = qed_ll2_convert_rx_parse_to_tx_flags(parse_flags); - SET_FIELD(bd_flags, CORE_TX_BD_FLAGS_FORCE_VLAN_MODE, 1); - SET_FIELD(bd_flags, CORE_TX_BD_FLAGS_L4_PROTOCOL, 1); + SET_FIELD(bd_flags, CORE_TX_BD_DATA_FORCE_VLAN_MODE, 1); + SET_FIELD(bd_flags, CORE_TX_BD_DATA_L4_PROTOCOL, 1); rc = qed_ll2_prepare_tx_packet(p_hwfn, p_ll2_conn->my_id, 1, p_buffer->vlan, bd_flags, @@ -1588,33 +1588,34 @@ static void qed_ll2_prepare_tx_packet_set(struct qed_hwfn *p_hwfn, p_tx->cur_send_frag_num++; } -static void qed_ll2_prepare_tx_packet_set_bd(struct qed_hwfn *p_hwfn, - struct qed_ll2_info *p_ll2, - struct qed_ll2_tx_packet *p_curp, - u8 num_of_bds, - enum core_tx_dest tx_dest, - u16 vlan, - u8 bd_flags, - u16 l4_hdr_offset_w, - enum core_roce_flavor_type type, - dma_addr_t first_frag, - u16 first_frag_len) +static void +qed_ll2_prepare_tx_packet_set_bd(struct qed_hwfn *p_hwfn, + struct qed_ll2_info *p_ll2, + struct qed_ll2_tx_packet *p_curp, + u8 num_of_bds, + enum core_tx_dest tx_dest, + u16 vlan, + u8 bd_flags, + u16 l4_hdr_offset_w, + enum core_roce_flavor_type roce_flavor, + dma_addr_t first_frag, + u16 first_frag_len) { struct qed_chain *p_tx_chain = &p_ll2->tx_queue.txq_chain; u16 prod_idx = qed_chain_get_prod_idx(p_tx_chain); struct core_tx_bd *start_bd = NULL; - u16 frag_idx; + u16 bd_data = 0, frag_idx; start_bd = (struct core_tx_bd *)qed_chain_produce(p_tx_chain); start_bd->nw_vlan_or_lb_echo = cpu_to_le16(vlan); SET_FIELD(start_bd->bitfield1, CORE_TX_BD_L4_HDR_OFFSET_W, cpu_to_le16(l4_hdr_offset_w)); SET_FIELD(start_bd->bitfield1, CORE_TX_BD_TX_DST, tx_dest); - start_bd->bd_flags.as_bitfield = bd_flags; - start_bd->bd_flags.as_bitfield |= CORE_TX_BD_FLAGS_START_BD_MASK << - CORE_TX_BD_FLAGS_START_BD_SHIFT; - SET_FIELD(start_bd->bitfield0, CORE_TX_BD_NBDS, num_of_bds); - SET_FIELD(start_bd->bitfield0, CORE_TX_BD_ROCE_FLAV, type); + bd_data |= bd_flags; + SET_FIELD(bd_data, CORE_TX_BD_DATA_START_BD, 0x1); + SET_FIELD(bd_data, CORE_TX_BD_DATA_NBDS, num_of_bds); + SET_FIELD(bd_data, CORE_TX_BD_DATA_ROCE_FLAV, roce_flavor); + start_bd->bd_data.as_bitfield = cpu_to_le16(bd_data); DMA_REGPAIR_LE(start_bd->addr, first_frag); start_bd->nbytes = cpu_to_le16(first_frag_len); @@ -1639,9 +1640,8 @@ static void qed_ll2_prepare_tx_packet_set_bd(struct qed_hwfn *p_hwfn, struct core_tx_bd **p_bd = &p_curp->bds_set[frag_idx].txq_bd; *p_bd = (struct core_tx_bd *)qed_chain_produce(p_tx_chain); - (*p_bd)->bd_flags.as_bitfield = 0; + (*p_bd)->bd_data.as_bitfield = 0; (*p_bd)->bitfield1 = 0; - (*p_bd)->bitfield0 = 0; p_curp->bds_set[frag_idx].tx_frag = 0; p_curp->bds_set[frag_idx].frag_len = 0; } @@ -2238,11 +2238,11 @@ static int qed_ll2_start_xmit(struct qed_dev *cdev, struct sk_buff *skb) /* Request HW to calculate IP csum */ if (!((vlan_get_protocol(skb) == htons(ETH_P_IPV6)) && ipv6_hdr(skb)->nexthdr == NEXTHDR_IPV6)) - flags |= BIT(CORE_TX_BD_FLAGS_IP_CSUM_SHIFT); + flags |= BIT(CORE_TX_BD_DATA_IP_CSUM_SHIFT); if (skb_vlan_tag_present(skb)) { vlan = skb_vlan_tag_get(skb); - flags |= BIT(CORE_TX_BD_FLAGS_VLAN_INSERTION_SHIFT); + flags |= BIT(CORE_TX_BD_DATA_VLAN_INSERTION_SHIFT); } rc = qed_ll2_prepare_tx_packet(QED_LEADING_HWFN(cdev), diff --git a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h index d59d9df60cd2..36ae361884e0 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h +++ b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h @@ -356,6 +356,10 @@ 0x238804UL #define RDIF_REG_STOP_ON_ERROR \ 0x300040UL +#define RDIF_REG_DEBUG_ERROR_INFO \ + 0x300400UL +#define RDIF_REG_DEBUG_ERROR_INFO_SIZE \ + 64 #define SRC_REG_SOFT_RST \ 0x23874cUL #define TCFC_REG_ACTIVITY_COUNTER \ @@ -370,6 +374,10 @@ 0x1700004UL #define TDIF_REG_STOP_ON_ERROR \ 0x310040UL +#define TDIF_REG_DEBUG_ERROR_INFO \ + 0x310400UL +#define TDIF_REG_DEBUG_ERROR_INFO_SIZE \ + 64 #define UCM_REG_INIT \ 0x1280000UL #define UMAC_REG_IPG_HD_BKP_CNTL_BB_B0 \ @@ -1236,6 +1244,26 @@ 0x1901534UL #define USEM_REG_DBG_FORCE_FRAME \ 0x1901538UL +#define NWS_REG_DBG_SELECT \ + 0x700128UL +#define NWS_REG_DBG_DWORD_ENABLE \ + 0x70012cUL +#define NWS_REG_DBG_SHIFT \ + 0x700130UL +#define NWS_REG_DBG_FORCE_VALID \ + 0x700134UL +#define NWS_REG_DBG_FORCE_FRAME \ + 0x700138UL +#define MS_REG_DBG_SELECT \ + 0x6a0228UL +#define MS_REG_DBG_DWORD_ENABLE \ + 0x6a022cUL +#define MS_REG_DBG_SHIFT \ + 0x6a0230UL +#define MS_REG_DBG_FORCE_VALID \ + 0x6a0234UL +#define MS_REG_DBG_FORCE_FRAME \ + 0x6a0238UL #define PCIE_REG_DBG_COMMON_SELECT \ 0x054398UL #define PCIE_REG_DBG_COMMON_DWORD_ENABLE \ @@ -1448,6 +1476,8 @@ 0x000b48UL #define RSS_REG_RSS_RAM_DATA \ 0x238c20UL +#define RSS_REG_RSS_RAM_DATA_SIZE \ + 4 #define MISC_REG_BLOCK_256B_EN \ 0x008c14UL #define NWS_REG_NWS_CMU \ diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c index d9ff6b28591c..4bef5c59627c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_roce.c +++ b/drivers/net/ethernet/qlogic/qed/qed_roce.c @@ -66,13 +66,27 @@ #include "qed_roce.h" #include "qed_ll2.h" -void qed_async_roce_event(struct qed_hwfn *p_hwfn, - struct event_ring_entry *p_eqe) +static void qed_roce_free_real_icid(struct qed_hwfn *p_hwfn, u16 icid); + +void qed_roce_async_event(struct qed_hwfn *p_hwfn, + u8 fw_event_code, union rdma_eqe_data *rdma_data) { - struct qed_rdma_info *p_rdma_info = p_hwfn->p_rdma_info; + if (fw_event_code == ROCE_ASYNC_EVENT_DESTROY_QP_DONE) { + u16 icid = + (u16)le32_to_cpu(rdma_data->rdma_destroy_qp_data.cid); + + /* icid release in this async event can occur only if the icid + * was offloaded to the FW. In case it wasn't offloaded this is + * handled in qed_roce_sp_destroy_qp. + */ + qed_roce_free_real_icid(p_hwfn, icid); + } else { + struct qed_rdma_events *events = &p_hwfn->p_rdma_info->events; - p_rdma_info->events.affiliated_event(p_rdma_info->events.context, - p_eqe->opcode, &p_eqe->data); + events->affiliated_event(p_hwfn->p_rdma_info->events.context, + fw_event_code, + &rdma_data->async_handle); + } } static int qed_rdma_bmap_alloc(struct qed_hwfn *p_hwfn, @@ -113,6 +127,15 @@ static int qed_rdma_bmap_alloc_id(struct qed_hwfn *p_hwfn, return 0; } +static void qed_bmap_set_id(struct qed_hwfn *p_hwfn, + struct qed_bmap *bmap, u32 id_num) +{ + if (id_num >= bmap->max_count) + return; + + __set_bit(id_num, bmap->bitmap); +} + static void qed_bmap_release_id(struct qed_hwfn *p_hwfn, struct qed_bmap *bmap, u32 id_num) { @@ -129,6 +152,15 @@ static void qed_bmap_release_id(struct qed_hwfn *p_hwfn, } } +static int qed_bmap_test_id(struct qed_hwfn *p_hwfn, + struct qed_bmap *bmap, u32 id_num) +{ + if (id_num >= bmap->max_count) + return -1; + + return test_bit(id_num, bmap->bitmap); +} + static u32 qed_rdma_get_sb_id(void *p_hwfn, u32 rel_sb_id) { /* First sb id for RoCE is after all the l2 sb */ @@ -170,7 +202,8 @@ static int qed_rdma_alloc(struct qed_hwfn *p_hwfn, /* Queue zone lines are shared between RoCE and L2 in such a way that * they can be used by each without obstructing the other. */ - p_rdma_info->queue_zone_base = (u16)FEAT_NUM(p_hwfn, QED_L2_QUEUE); + p_rdma_info->queue_zone_base = (u16)RESC_START(p_hwfn, QED_L2_QUEUE); + p_rdma_info->max_queue_zones = (u16)RESC_NUM(p_hwfn, QED_L2_QUEUE); /* Allocate a struct with device params and fill it */ p_rdma_info->dev = kzalloc(sizeof(*p_rdma_info->dev), GFP_KERNEL); @@ -248,9 +281,18 @@ static int qed_rdma_alloc(struct qed_hwfn *p_hwfn, goto free_tid_map; } + /* Allocate bitmap for cids used for responders/requesters. */ + rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->real_cid_map, num_cons); + if (rc) { + DP_VERBOSE(p_hwfn, QED_MSG_RDMA, + "Failed to allocate real cid bitmap, rc = %d\n", rc); + goto free_cid_map; + } DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocation successful\n"); return 0; +free_cid_map: + kfree(p_rdma_info->cid_map.bitmap); free_tid_map: kfree(p_rdma_info->tid_map.bitmap); free_toggle_map: @@ -273,7 +315,22 @@ free_rdma_info: static void qed_rdma_resc_free(struct qed_hwfn *p_hwfn) { + struct qed_bmap *rcid_map = &p_hwfn->p_rdma_info->real_cid_map; struct qed_rdma_info *p_rdma_info = p_hwfn->p_rdma_info; + int wait_count = 0; + + /* when destroying a_RoCE QP the control is returned to the user after + * the synchronous part. The asynchronous part may take a little longer. + * We delay for a short while if an async destroy QP is still expected. + * Beyond the added delay we clear the bitmap anyway. + */ + while (bitmap_weight(rcid_map->bitmap, rcid_map->max_count)) { + msleep(100); + if (wait_count++ > 20) { + DP_NOTICE(p_hwfn, "cid bitmap wait timed out\n"); + break; + } + } kfree(p_rdma_info->cid_map.bitmap); kfree(p_rdma_info->tid_map.bitmap); @@ -724,6 +781,14 @@ static void qed_rdma_cnq_prod_update(void *rdma_cxt, u8 qz_offset, u16 prod) u32 addr; p_hwfn = (struct qed_hwfn *)rdma_cxt; + + if (qz_offset > p_hwfn->p_rdma_info->max_queue_zones) { + DP_NOTICE(p_hwfn, + "queue zone offset %d is too large (max is %d)\n", + qz_offset, p_hwfn->p_rdma_info->max_queue_zones); + return; + } + qz_num = p_hwfn->p_rdma_info->queue_zone_base + qz_offset; addr = GTT_BAR0_MAP_REG_USDM_RAM + USTORM_COMMON_QUEUE_CONS_OFFSET(qz_num); @@ -1080,6 +1145,14 @@ static enum roce_flavor qed_roce_mode_to_flavor(enum roce_mode roce_mode) return flavor; } +void qed_roce_free_cid_pair(struct qed_hwfn *p_hwfn, u16 cid) +{ + spin_lock_bh(&p_hwfn->p_rdma_info->lock); + qed_bmap_release_id(p_hwfn, &p_hwfn->p_rdma_info->cid_map, cid); + qed_bmap_release_id(p_hwfn, &p_hwfn->p_rdma_info->cid_map, cid + 1); + spin_unlock_bh(&p_hwfn->p_rdma_info->lock); +} + static int qed_roce_alloc_cid(struct qed_hwfn *p_hwfn, u16 *cid) { struct qed_rdma_info *p_rdma_info = p_hwfn->p_rdma_info; @@ -1139,6 +1212,13 @@ err: return rc; } +static void qed_roce_set_real_cid(struct qed_hwfn *p_hwfn, u32 cid) +{ + spin_lock_bh(&p_hwfn->p_rdma_info->lock); + qed_bmap_set_id(p_hwfn, &p_hwfn->p_rdma_info->real_cid_map, cid); + spin_unlock_bh(&p_hwfn->p_rdma_info->lock); +} + static int qed_roce_sp_create_responder(struct qed_hwfn *p_hwfn, struct qed_rdma_qp *qp) { @@ -1147,7 +1227,8 @@ static int qed_roce_sp_create_responder(struct qed_hwfn *p_hwfn, union qed_qm_pq_params qm_params; enum roce_flavor roce_flavor; struct qed_spq_entry *p_ent; - u16 physical_queue0 = 0; + u16 regular_latency_queue; + enum protocol_type proto; int rc; DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", qp->icid); @@ -1229,15 +1310,19 @@ static int qed_roce_sp_create_responder(struct qed_hwfn *p_hwfn, p_ramrod->qp_handle_for_async.lo = cpu_to_le32(qp->qp_handle_async.lo); p_ramrod->qp_handle_for_cqe.hi = cpu_to_le32(qp->qp_handle.hi); p_ramrod->qp_handle_for_cqe.lo = cpu_to_le32(qp->qp_handle.lo); - p_ramrod->stats_counter_id = p_hwfn->rel_pf_id; p_ramrod->cq_cid = cpu_to_le32((p_hwfn->hw_info.opaque_fid << 16) | qp->rq_cq_id); memset(&qm_params, 0, sizeof(qm_params)); qm_params.roce.qpid = qp->icid >> 1; - physical_queue0 = qed_get_qm_pq(p_hwfn, PROTOCOLID_ROCE, &qm_params); + regular_latency_queue = qed_get_qm_pq(p_hwfn, PROTOCOLID_ROCE, + &qm_params); + + p_ramrod->regular_latency_phy_queue = + cpu_to_le16(regular_latency_queue); + p_ramrod->low_latency_phy_queue = + cpu_to_le16(regular_latency_queue); - p_ramrod->physical_queue0 = cpu_to_le16(physical_queue0); p_ramrod->dpi = cpu_to_le16(qp->dpi); qed_rdma_set_fw_mac(p_ramrod->remote_mac_addr, qp->remote_mac_addr); @@ -1253,13 +1338,19 @@ static int qed_roce_sp_create_responder(struct qed_hwfn *p_hwfn, rc = qed_spq_post(p_hwfn, p_ent, NULL); - DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "rc = %d physical_queue0 = 0x%x\n", - rc, physical_queue0); + DP_VERBOSE(p_hwfn, QED_MSG_RDMA, + "rc = %d regular physical queue = 0x%x\n", rc, + regular_latency_queue); if (rc) goto err; qp->resp_offloaded = true; + qp->cq_prod = 0; + + proto = p_hwfn->p_rdma_info->proto; + qed_roce_set_real_cid(p_hwfn, qp->icid - + qed_cxt_get_proto_cid_start(p_hwfn, proto)); return rc; @@ -1280,7 +1371,8 @@ static int qed_roce_sp_create_requester(struct qed_hwfn *p_hwfn, union qed_qm_pq_params qm_params; enum roce_flavor roce_flavor; struct qed_spq_entry *p_ent; - u16 physical_queue0 = 0; + u16 regular_latency_queue; + enum protocol_type proto; int rc; DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", qp->icid); @@ -1351,15 +1443,19 @@ static int qed_roce_sp_create_requester(struct qed_hwfn *p_hwfn, p_ramrod->qp_handle_for_async.lo = cpu_to_le32(qp->qp_handle_async.lo); p_ramrod->qp_handle_for_cqe.hi = cpu_to_le32(qp->qp_handle.hi); p_ramrod->qp_handle_for_cqe.lo = cpu_to_le32(qp->qp_handle.lo); - p_ramrod->stats_counter_id = p_hwfn->rel_pf_id; - p_ramrod->cq_cid = cpu_to_le32((p_hwfn->hw_info.opaque_fid << 16) | - qp->sq_cq_id); + p_ramrod->cq_cid = + cpu_to_le32((p_hwfn->hw_info.opaque_fid << 16) | qp->sq_cq_id); memset(&qm_params, 0, sizeof(qm_params)); qm_params.roce.qpid = qp->icid >> 1; - physical_queue0 = qed_get_qm_pq(p_hwfn, PROTOCOLID_ROCE, &qm_params); + regular_latency_queue = qed_get_qm_pq(p_hwfn, PROTOCOLID_ROCE, + &qm_params); + + p_ramrod->regular_latency_phy_queue = + cpu_to_le16(regular_latency_queue); + p_ramrod->low_latency_phy_queue = + cpu_to_le16(regular_latency_queue); - p_ramrod->physical_queue0 = cpu_to_le16(physical_queue0); p_ramrod->dpi = cpu_to_le16(qp->dpi); qed_rdma_set_fw_mac(p_ramrod->remote_mac_addr, qp->remote_mac_addr); @@ -1378,6 +1474,10 @@ static int qed_roce_sp_create_requester(struct qed_hwfn *p_hwfn, goto err; qp->req_offloaded = true; + proto = p_hwfn->p_rdma_info->proto; + qed_roce_set_real_cid(p_hwfn, + qp->icid + 1 - + qed_cxt_get_proto_cid_start(p_hwfn, proto)); return rc; @@ -1577,7 +1677,8 @@ static int qed_roce_sp_modify_requester(struct qed_hwfn *p_hwfn, static int qed_roce_sp_destroy_qp_responder(struct qed_hwfn *p_hwfn, struct qed_rdma_qp *qp, - u32 *num_invalidated_mw) + u32 *num_invalidated_mw, + u32 *cq_prod) { struct roce_destroy_qp_resp_output_params *p_ramrod_res; struct roce_destroy_qp_resp_ramrod_data *p_ramrod; @@ -1588,8 +1689,22 @@ static int qed_roce_sp_destroy_qp_responder(struct qed_hwfn *p_hwfn, DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "icid = %08x\n", qp->icid); - if (!qp->resp_offloaded) + *num_invalidated_mw = 0; + *cq_prod = qp->cq_prod; + + if (!qp->resp_offloaded) { + /* If a responder was never offload, we need to free the cids + * allocated in create_qp as a FW async event will never arrive + */ + u32 cid; + + cid = qp->icid - + qed_cxt_get_proto_cid_start(p_hwfn, + p_hwfn->p_rdma_info->proto); + qed_roce_free_cid_pair(p_hwfn, (u16)cid); + return 0; + } /* Get SPQ entry */ memset(&init_data, 0, sizeof(init_data)); @@ -1624,6 +1739,8 @@ static int qed_roce_sp_destroy_qp_responder(struct qed_hwfn *p_hwfn, goto err; *num_invalidated_mw = le32_to_cpu(p_ramrod_res->num_invalidated_mw); + *cq_prod = le32_to_cpu(p_ramrod_res->cq_prod); + qp->cq_prod = *cq_prod; /* Free IRQ - only if ramrod succeeded, in case FW is still using it */ dma_free_coherent(&p_hwfn->cdev->pdev->dev, @@ -1827,10 +1944,8 @@ static int qed_roce_query_qp(struct qed_hwfn *p_hwfn, out_params->draining = false; - if (rq_err_state) + if (rq_err_state || sq_err_state) qp->cur_state = QED_ROCE_QP_STATE_ERR; - else if (sq_err_state) - qp->cur_state = QED_ROCE_QP_STATE_SQE; else if (sq_draining) out_params->draining = true; out_params->state = qp->cur_state; @@ -1849,10 +1964,9 @@ err_resp: static int qed_roce_destroy_qp(struct qed_hwfn *p_hwfn, struct qed_rdma_qp *qp) { - struct qed_rdma_info *p_rdma_info = p_hwfn->p_rdma_info; u32 num_invalidated_mw = 0; u32 num_bound_mw = 0; - u32 start_cid; + u32 cq_prod; int rc; /* Destroys the specified QP */ @@ -1866,7 +1980,8 @@ static int qed_roce_destroy_qp(struct qed_hwfn *p_hwfn, struct qed_rdma_qp *qp) if (qp->cur_state != QED_ROCE_QP_STATE_RESET) { rc = qed_roce_sp_destroy_qp_responder(p_hwfn, qp, - &num_invalidated_mw); + &num_invalidated_mw, + &cq_prod); if (rc) return rc; @@ -1881,21 +1996,6 @@ static int qed_roce_destroy_qp(struct qed_hwfn *p_hwfn, struct qed_rdma_qp *qp) "number of invalidate memory windows is different from bounded ones\n"); return -EINVAL; } - - spin_lock_bh(&p_rdma_info->lock); - - start_cid = qed_cxt_get_proto_cid_start(p_hwfn, - p_rdma_info->proto); - - /* Release responder's icid */ - qed_bmap_release_id(p_hwfn, &p_rdma_info->cid_map, - qp->icid - start_cid); - - /* Release requester's icid */ - qed_bmap_release_id(p_hwfn, &p_rdma_info->cid_map, - qp->icid + 1 - start_cid); - - spin_unlock_bh(&p_rdma_info->lock); } return 0; @@ -2110,12 +2210,19 @@ static int qed_roce_modify_qp(struct qed_hwfn *p_hwfn, return rc; } else if (qp->cur_state == QED_ROCE_QP_STATE_RESET) { /* Any state -> RESET */ + u32 cq_prod; + + /* Send destroy responder ramrod */ + rc = qed_roce_sp_destroy_qp_responder(p_hwfn, + qp, + &num_invalidated_mw, + &cq_prod); - rc = qed_roce_sp_destroy_qp_responder(p_hwfn, qp, - &num_invalidated_mw); if (rc) return rc; + qp->cq_prod = cq_prod; + rc = qed_roce_sp_destroy_qp_requester(p_hwfn, qp, &num_bound_mw); @@ -2454,6 +2561,31 @@ static int qed_rdma_deregister_tid(void *rdma_cxt, u32 itid) return rc; } +static void qed_roce_free_real_icid(struct qed_hwfn *p_hwfn, u16 icid) +{ + struct qed_rdma_info *p_rdma_info = p_hwfn->p_rdma_info; + u32 start_cid, cid, xcid; + + /* an even icid belongs to a responder while an odd icid belongs to a + * requester. The 'cid' received as an input can be either. We calculate + * the "partner" icid and call it xcid. Only if both are free then the + * "cid" map can be cleared. + */ + start_cid = qed_cxt_get_proto_cid_start(p_hwfn, p_rdma_info->proto); + cid = icid - start_cid; + xcid = cid ^ 1; + + spin_lock_bh(&p_rdma_info->lock); + + qed_bmap_release_id(p_hwfn, &p_rdma_info->real_cid_map, cid); + if (qed_bmap_test_id(p_hwfn, &p_rdma_info->real_cid_map, xcid) == 0) { + qed_bmap_release_id(p_hwfn, &p_rdma_info->cid_map, cid); + qed_bmap_release_id(p_hwfn, &p_rdma_info->cid_map, xcid); + } + + spin_unlock_bh(&p_hwfn->p_rdma_info->lock); +} + static void *qed_rdma_get_rdma_ctx(struct qed_dev *cdev) { return QED_LEADING_HWFN(cdev); @@ -2773,7 +2905,7 @@ static int qed_roce_ll2_tx(struct qed_dev *cdev, : QED_LL2_RROCE; if (pkt->roce_mode == ROCE_V2_IPV4) - flags |= BIT(CORE_TX_BD_FLAGS_IP_CSUM_SHIFT); + flags |= BIT(CORE_TX_BD_DATA_IP_CSUM_SHIFT); /* Tx header */ rc = qed_ll2_prepare_tx_packet(QED_LEADING_HWFN(cdev), roce_ll2->handle, diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.h b/drivers/net/ethernet/qlogic/qed/qed_roce.h index 36cf4b2ab7fa..3ccc08a7c995 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_roce.h +++ b/drivers/net/ethernet/qlogic/qed/qed_roce.h @@ -82,6 +82,7 @@ struct qed_rdma_info { struct qed_bmap qp_map; struct qed_bmap srq_map; struct qed_bmap cid_map; + struct qed_bmap real_cid_map; struct qed_bmap dpi_map; struct qed_bmap toggle_bits; struct qed_rdma_events events; @@ -92,6 +93,7 @@ struct qed_rdma_info { u32 num_qps; u32 num_mrs; u16 queue_zone_base; + u16 max_queue_zones; enum protocol_type proto; }; @@ -153,6 +155,7 @@ struct qed_rdma_qp { dma_addr_t irq_phys_addr; u8 irq_num_pages; bool resp_offloaded; + u32 cq_prod; u8 remote_mac_addr[6]; u8 local_mac_addr[6]; @@ -163,8 +166,8 @@ struct qed_rdma_qp { #if IS_ENABLED(CONFIG_QED_RDMA) void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); -void qed_async_roce_event(struct qed_hwfn *p_hwfn, - struct event_ring_entry *p_eqe); +void qed_roce_async_event(struct qed_hwfn *p_hwfn, + u8 fw_event_code, union rdma_eqe_data *rdma_data); void qed_ll2b_complete_tx_gsi_packet(struct qed_hwfn *p_hwfn, u8 connection_handle, void *cookie, @@ -187,7 +190,9 @@ void qed_ll2b_complete_rx_gsi_packet(struct qed_hwfn *p_hwfn, u16 src_mac_addr_lo, bool b_last_packet); #else static inline void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) {} -static inline void qed_async_roce_event(struct qed_hwfn *p_hwfn, struct event_ring_entry *p_eqe) {} +static inline void qed_roce_async_event(struct qed_hwfn *p_hwfn, + u8 fw_event_code, + union rdma_eqe_data *rdma_data) {} static inline void qed_ll2b_complete_tx_gsi_packet(struct qed_hwfn *p_hwfn, u8 connection_handle, void *cookie, diff --git a/drivers/net/ethernet/qlogic/qed/qed_spq.c b/drivers/net/ethernet/qlogic/qed/qed_spq.c index 645328a9f0cf..54fbe3789cf3 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_spq.c +++ b/drivers/net/ethernet/qlogic/qed/qed_spq.c @@ -296,9 +296,12 @@ qed_async_event_completion(struct qed_hwfn *p_hwfn, struct event_ring_entry *p_eqe) { switch (p_eqe->protocol_id) { +#if IS_ENABLED(CONFIG_QED_RDMA) case PROTOCOLID_ROCE: - qed_async_roce_event(p_hwfn, p_eqe); + qed_roce_async_event(p_hwfn, p_eqe->opcode, + &p_eqe->data.rdma_data); return 0; +#endif case PROTOCOLID_COMMON: return qed_sriov_eqe_event(p_hwfn, p_eqe->opcode, @@ -306,14 +309,6 @@ qed_async_event_completion(struct qed_hwfn *p_hwfn, case PROTOCOLID_ISCSI: if (!IS_ENABLED(CONFIG_QED_ISCSI)) return -EINVAL; - if (p_eqe->opcode == ISCSI_EVENT_TYPE_ASYN_DELETE_OOO_ISLES) { - u32 cid = le32_to_cpu(p_eqe->data.iscsi_info.cid); - - qed_ooo_release_connection_isles(p_hwfn, - p_hwfn->p_ooo_info, - cid); - return 0; - } if (p_hwfn->p_iscsi_info->event_cb) { struct qed_iscsi_info *p_iscsi = p_hwfn->p_iscsi_info; diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index f2aaef2cfb86..8d02fb6c19d7 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -50,7 +50,7 @@ #define QEDE_MAJOR_VERSION 8 #define QEDE_MINOR_VERSION 10 #define QEDE_REVISION_VERSION 10 -#define QEDE_ENGINEERING_VERSION 20 +#define QEDE_ENGINEERING_VERSION 21 #define DRV_MODULE_VERSION __stringify(QEDE_MAJOR_VERSION) "." \ __stringify(QEDE_MINOR_VERSION) "." \ __stringify(QEDE_REVISION_VERSION) "." \ diff --git a/drivers/scsi/qedf/Makefile b/drivers/scsi/qedf/Makefile index 64e9f507ce32..414f2a772a5f 100644 --- a/drivers/scsi/qedf/Makefile +++ b/drivers/scsi/qedf/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_QEDF) := qedf.o qedf-y = qedf_dbg.o qedf_main.o qedf_io.o qedf_fip.o \ - qedf_attr.o qedf_els.o + qedf_attr.o qedf_els.o drv_scsi_fw_funcs.o drv_fcoe_fw_funcs.o qedf-$(CONFIG_DEBUG_FS) += qedf_debugfs.o diff --git a/drivers/scsi/qedf/drv_fcoe_fw_funcs.c b/drivers/scsi/qedf/drv_fcoe_fw_funcs.c new file mode 100644 index 000000000000..bb812db48da6 --- /dev/null +++ b/drivers/scsi/qedf/drv_fcoe_fw_funcs.c @@ -0,0 +1,190 @@ +/* QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#include "drv_fcoe_fw_funcs.h" +#include "drv_scsi_fw_funcs.h" + +#define FCOE_RX_ID ((u32)0x0000FFFF) + +static inline void init_common_sqe(struct fcoe_task_params *task_params, + enum fcoe_sqe_request_type request_type) +{ + memset(task_params->sqe, 0, sizeof(*(task_params->sqe))); + SET_FIELD(task_params->sqe->flags, FCOE_WQE_REQ_TYPE, + request_type); + task_params->sqe->task_id = task_params->itid; +} + +int init_initiator_rw_fcoe_task(struct fcoe_task_params *task_params, + struct scsi_sgl_task_params *sgl_task_params, + struct regpair sense_data_buffer_phys_addr, + u32 task_retry_id, + u8 fcp_cmd_payload[32]) +{ + struct fcoe_task_context *ctx = task_params->context; + struct ystorm_fcoe_task_st_ctx *y_st_ctx; + struct tstorm_fcoe_task_st_ctx *t_st_ctx; + struct ustorm_fcoe_task_ag_ctx *u_ag_ctx; + struct mstorm_fcoe_task_st_ctx *m_st_ctx; + u32 io_size, val; + bool slow_sgl; + + memset(ctx, 0, sizeof(*(ctx))); + slow_sgl = scsi_is_slow_sgl(sgl_task_params->num_sges, + sgl_task_params->small_mid_sge); + io_size = (task_params->task_type == FCOE_TASK_TYPE_WRITE_INITIATOR ? + task_params->tx_io_size : task_params->rx_io_size); + + /* Ystorm ctx */ + y_st_ctx = &ctx->ystorm_st_context; + y_st_ctx->data_2_trns_rem = cpu_to_le32(io_size); + y_st_ctx->task_rety_identifier = cpu_to_le32(task_retry_id); + y_st_ctx->task_type = task_params->task_type; + memcpy(&y_st_ctx->tx_info_union.fcp_cmd_payload, + fcp_cmd_payload, sizeof(struct fcoe_fcp_cmd_payload)); + + /* Tstorm ctx */ + t_st_ctx = &ctx->tstorm_st_context; + t_st_ctx->read_only.dev_type = (task_params->is_tape_device == 1 ? + FCOE_TASK_DEV_TYPE_TAPE : + FCOE_TASK_DEV_TYPE_DISK); + t_st_ctx->read_only.cid = cpu_to_le32(task_params->conn_cid); + val = cpu_to_le32(task_params->cq_rss_number); + t_st_ctx->read_only.glbl_q_num = val; + t_st_ctx->read_only.fcp_cmd_trns_size = cpu_to_le32(io_size); + t_st_ctx->read_only.task_type = task_params->task_type; + SET_FIELD(t_st_ctx->read_write.flags, + FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_EXP_FIRST_FRAME, 1); + t_st_ctx->read_write.rx_id = cpu_to_le32(FCOE_RX_ID); + + /* Ustorm ctx */ + u_ag_ctx = &ctx->ustorm_ag_context; + u_ag_ctx->global_cq_num = cpu_to_le32(task_params->cq_rss_number); + + /* Mstorm buffer for sense/rsp data placement */ + m_st_ctx = &ctx->mstorm_st_context; + val = cpu_to_le32(sense_data_buffer_phys_addr.hi); + m_st_ctx->rsp_buf_addr.hi = val; + val = cpu_to_le32(sense_data_buffer_phys_addr.lo); + m_st_ctx->rsp_buf_addr.lo = val; + + if (task_params->task_type == FCOE_TASK_TYPE_WRITE_INITIATOR) { + /* Ystorm ctx */ + y_st_ctx->expect_first_xfer = 1; + + /* Set the amount of super SGEs. Can be up to 4. */ + SET_FIELD(y_st_ctx->sgl_mode, + YSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE, + (slow_sgl ? SCSI_TX_SLOW_SGL : SCSI_FAST_SGL)); + init_scsi_sgl_context(&y_st_ctx->sgl_params, + &y_st_ctx->data_desc, + sgl_task_params); + + /* Mstorm ctx */ + SET_FIELD(m_st_ctx->flags, + MSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE, + (slow_sgl ? SCSI_TX_SLOW_SGL : SCSI_FAST_SGL)); + } else { + /* Tstorm ctx */ + SET_FIELD(t_st_ctx->read_write.flags, + FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_RX_SGL_MODE, + (slow_sgl ? SCSI_TX_SLOW_SGL : SCSI_FAST_SGL)); + + /* Mstorm ctx */ + m_st_ctx->data_2_trns_rem = cpu_to_le32(io_size); + init_scsi_sgl_context(&m_st_ctx->sgl_params, + &m_st_ctx->data_desc, + sgl_task_params); + } + + init_common_sqe(task_params, SEND_FCOE_CMD); + return 0; +} + +int init_initiator_midpath_unsolicited_fcoe_task( + struct fcoe_task_params *task_params, + struct fcoe_tx_mid_path_params *mid_path_fc_header, + struct scsi_sgl_task_params *tx_sgl_task_params, + struct scsi_sgl_task_params *rx_sgl_task_params, + u8 fw_to_place_fc_header) +{ + struct fcoe_task_context *ctx = task_params->context; + struct ystorm_fcoe_task_st_ctx *y_st_ctx; + struct tstorm_fcoe_task_st_ctx *t_st_ctx; + struct ustorm_fcoe_task_ag_ctx *u_ag_ctx; + struct mstorm_fcoe_task_st_ctx *m_st_ctx; + u32 val; + + memset(ctx, 0, sizeof(*(ctx))); + + /* Init Ystorm */ + y_st_ctx = &ctx->ystorm_st_context; + init_scsi_sgl_context(&y_st_ctx->sgl_params, + &y_st_ctx->data_desc, + tx_sgl_task_params); + SET_FIELD(y_st_ctx->sgl_mode, + YSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE, SCSI_FAST_SGL); + y_st_ctx->data_2_trns_rem = cpu_to_le32(task_params->tx_io_size); + y_st_ctx->task_type = task_params->task_type; + memcpy(&y_st_ctx->tx_info_union.tx_params.mid_path, + mid_path_fc_header, sizeof(struct fcoe_tx_mid_path_params)); + + /* Init Mstorm */ + m_st_ctx = &ctx->mstorm_st_context; + init_scsi_sgl_context(&m_st_ctx->sgl_params, + &m_st_ctx->data_desc, + rx_sgl_task_params); + SET_FIELD(m_st_ctx->flags, + MSTORM_FCOE_TASK_ST_CTX_MP_INCLUDE_FC_HEADER, + fw_to_place_fc_header); + m_st_ctx->data_2_trns_rem = cpu_to_le32(task_params->rx_io_size); + + /* Init Tstorm */ + t_st_ctx = &ctx->tstorm_st_context; + t_st_ctx->read_only.cid = cpu_to_le32(task_params->conn_cid); + val = cpu_to_le32(task_params->cq_rss_number); + t_st_ctx->read_only.glbl_q_num = val; + t_st_ctx->read_only.task_type = task_params->task_type; + SET_FIELD(t_st_ctx->read_write.flags, + FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_EXP_FIRST_FRAME, 1); + t_st_ctx->read_write.rx_id = cpu_to_le32(FCOE_RX_ID); + + /* Init Ustorm */ + u_ag_ctx = &ctx->ustorm_ag_context; + u_ag_ctx->global_cq_num = cpu_to_le32(task_params->cq_rss_number); + + /* Init SQE */ + init_common_sqe(task_params, SEND_FCOE_MIDPATH); + task_params->sqe->additional_info_union.burst_length = + tx_sgl_task_params->total_buffer_size; + SET_FIELD(task_params->sqe->flags, + FCOE_WQE_NUM_SGES, tx_sgl_task_params->num_sges); + SET_FIELD(task_params->sqe->flags, FCOE_WQE_SGL_MODE, + SCSI_FAST_SGL); + + return 0; +} + +int init_initiator_abort_fcoe_task(struct fcoe_task_params *task_params) +{ + init_common_sqe(task_params, SEND_FCOE_ABTS_REQUEST); + return 0; +} + +int init_initiator_cleanup_fcoe_task(struct fcoe_task_params *task_params) +{ + init_common_sqe(task_params, FCOE_EXCHANGE_CLEANUP); + return 0; +} + +int init_initiator_sequence_recovery_fcoe_task( + struct fcoe_task_params *task_params, u32 off) +{ + init_common_sqe(task_params, FCOE_SEQUENCE_RECOVERY); + task_params->sqe->additional_info_union.seq_rec_updated_offset = off; + return 0; +} diff --git a/drivers/scsi/qedf/drv_fcoe_fw_funcs.h b/drivers/scsi/qedf/drv_fcoe_fw_funcs.h new file mode 100644 index 000000000000..617529b058f4 --- /dev/null +++ b/drivers/scsi/qedf/drv_fcoe_fw_funcs.h @@ -0,0 +1,93 @@ +/* QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#ifndef _FCOE_FW_FUNCS_H +#define _FCOE_FW_FUNCS_H +#include "drv_scsi_fw_funcs.h" +#include "qedf_hsi.h" +#include + +struct fcoe_task_params { + /* Output parameter [set/filled by the HSI function] */ + struct fcoe_task_context *context; + + /* Output parameter [set/filled by the HSI function] */ + struct fcoe_wqe *sqe; + enum fcoe_task_type task_type; + u32 tx_io_size; /* in bytes */ + u32 rx_io_size; /* in bytes */ + u32 conn_cid; + u16 itid; + u8 cq_rss_number; + + /* Whether it's Tape device or not (0=Disk, 1=Tape) */ + u8 is_tape_device; +}; + +/** + * @brief init_initiator_rw_fcoe_task - Initializes FCoE task context for + * read/write task types and init fcoe_sqe + * + * @param task_params - Pointer to task parameters struct + * @param sgl_task_params - Pointer to SGL task params + * @param sense_data_buffer_phys_addr - Pointer to sense data buffer + * @param task_retry_id - retry identification - Used only for Tape device + * @param fcp_cmnd_payload - FCP CMD Payload + */ +int init_initiator_rw_fcoe_task(struct fcoe_task_params *task_params, + struct scsi_sgl_task_params *sgl_task_params, + struct regpair sense_data_buffer_phys_addr, + u32 task_retry_id, + u8 fcp_cmd_payload[32]); + +/** + * @brief init_initiator_midpath_fcoe_task - Initializes FCoE task context for + * midpath/unsolicited task types and init fcoe_sqe + * + * @param task_params - Pointer to task parameters struct + * @param mid_path_fc_header - FC header + * @param tx_sgl_task_params - Pointer to Tx SGL task params + * @param rx_sgl_task_params - Pointer to Rx SGL task params + * @param fw_to_place_fc_header - Indication if the FW will place the FC header + * in addition to the data arrives. + */ +int init_initiator_midpath_unsolicited_fcoe_task( + struct fcoe_task_params *task_params, + struct fcoe_tx_mid_path_params *mid_path_fc_header, + struct scsi_sgl_task_params *tx_sgl_task_params, + struct scsi_sgl_task_params *rx_sgl_task_params, + u8 fw_to_place_fc_header); + +/** + * @brief init_initiator_abort_fcoe_task - Initializes FCoE task context for + * abort task types and init fcoe_sqe + * + * @param task_params - Pointer to task parameters struct + */ +int init_initiator_abort_fcoe_task(struct fcoe_task_params *task_params); + +/** + * @brief init_initiator_cleanup_fcoe_task - Initializes FCoE task context for + * cleanup task types and init fcoe_sqe + * + * + * @param task_params - Pointer to task parameters struct + */ +int init_initiator_cleanup_fcoe_task(struct fcoe_task_params *task_params); + +/** + * @brief init_initiator_cleanup_fcoe_task - Initializes FCoE task context for + * sequence recovery task types and init fcoe_sqe + * + * + * @param task_params - Pointer to task parameters struct + * @param desired_offset - The desired offest the task will be re-sent from + */ +int init_initiator_sequence_recovery_fcoe_task( + struct fcoe_task_params *task_params, + u32 desired_offset); +#endif diff --git a/drivers/scsi/qedf/drv_scsi_fw_funcs.c b/drivers/scsi/qedf/drv_scsi_fw_funcs.c new file mode 100644 index 000000000000..11e0cc082ec0 --- /dev/null +++ b/drivers/scsi/qedf/drv_scsi_fw_funcs.c @@ -0,0 +1,44 @@ +/* QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#include "drv_scsi_fw_funcs.h" + +#define SCSI_NUM_SGES_IN_CACHE 0x4 + +bool scsi_is_slow_sgl(u16 num_sges, bool small_mid_sge) +{ + return (num_sges > SCSI_NUM_SGES_SLOW_SGL_THR && small_mid_sge); +} + +void init_scsi_sgl_context(struct scsi_sgl_params *ctx_sgl_params, + struct scsi_cached_sges *ctx_data_desc, + struct scsi_sgl_task_params *sgl_task_params) +{ + /* no need to check for sgl_task_params->sgl validity */ + u8 num_sges_to_init = sgl_task_params->num_sges > + SCSI_NUM_SGES_IN_CACHE ? SCSI_NUM_SGES_IN_CACHE : + sgl_task_params->num_sges; + u8 sge_index; + u32 val; + + val = cpu_to_le32(sgl_task_params->sgl_phys_addr.lo); + ctx_sgl_params->sgl_addr.lo = val; + val = cpu_to_le32(sgl_task_params->sgl_phys_addr.hi); + ctx_sgl_params->sgl_addr.hi = val; + val = cpu_to_le32(sgl_task_params->total_buffer_size); + ctx_sgl_params->sgl_total_length = val; + ctx_sgl_params->sgl_num_sges = cpu_to_le16(sgl_task_params->num_sges); + + for (sge_index = 0; sge_index < num_sges_to_init; sge_index++) { + val = cpu_to_le32(sgl_task_params->sgl[sge_index].sge_addr.lo); + ctx_data_desc->sge[sge_index].sge_addr.lo = val; + val = cpu_to_le32(sgl_task_params->sgl[sge_index].sge_addr.hi); + ctx_data_desc->sge[sge_index].sge_addr.hi = val; + val = cpu_to_le32(sgl_task_params->sgl[sge_index].sge_len); + ctx_data_desc->sge[sge_index].sge_len = val; + } +} diff --git a/drivers/scsi/qedf/drv_scsi_fw_funcs.h b/drivers/scsi/qedf/drv_scsi_fw_funcs.h new file mode 100644 index 000000000000..9cb45410bc45 --- /dev/null +++ b/drivers/scsi/qedf/drv_scsi_fw_funcs.h @@ -0,0 +1,85 @@ +/* QLogic FCoE Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ +#ifndef _SCSI_FW_FUNCS_H +#define _SCSI_FW_FUNCS_H +#include +#include +#include + +struct scsi_sgl_task_params { + struct scsi_sge *sgl; + struct regpair sgl_phys_addr; + u32 total_buffer_size; + u16 num_sges; + + /* true if SGL contains a small (< 4KB) SGE in middle(not 1st or last) + * -> relevant for tx only + */ + bool small_mid_sge; +}; + +struct scsi_dif_task_params { + u32 initial_ref_tag; + bool initial_ref_tag_is_valid; + u16 application_tag; + u16 application_tag_mask; + u16 dif_block_size_log; + bool dif_on_network; + bool dif_on_host; + u8 host_guard_type; + u8 protection_type; + u8 ref_tag_mask; + bool crc_seed; + + /* Enable Connection error upon DIF error (segments with DIF errors are + * dropped) + */ + bool tx_dif_conn_err_en; + bool ignore_app_tag; + bool keep_ref_tag_const; + bool validate_guard; + bool validate_app_tag; + bool validate_ref_tag; + bool forward_guard; + bool forward_app_tag; + bool forward_ref_tag; + bool forward_app_tag_with_mask; + bool forward_ref_tag_with_mask; +}; + +struct scsi_initiator_cmd_params { + /* for cdb_size > default CDB size (extended CDB > 16 bytes) -> + * pointer to the CDB buffer SGE + */ + struct scsi_sge extended_cdb_sge; + + /* Physical address of sense data buffer for sense data - 256B buffer */ + struct regpair sense_data_buffer_phys_addr; +}; + +/** + * @brief scsi_is_slow_sgl - checks for slow SGL + * + * @param num_sges - number of sges in SGL + * @param small_mid_sge - True is the SGL contains an SGE which is smaller than + * 4KB and its not the 1st or last SGE in the SGL + */ +bool scsi_is_slow_sgl(u16 num_sges, bool small_mid_sge); + +/** + * @brief init_scsi_sgl_context - initializes SGL task context + * + * @param sgl_params - SGL context parameters to initialize (output parameter) + * @param data_desc - context struct containing SGEs array to set (output + * parameter) + * @param sgl_task_params - SGL parameters (input) + */ +void init_scsi_sgl_context(struct scsi_sgl_params *sgl_params, + struct scsi_cached_sges *ctx_data_desc, + struct scsi_sgl_task_params *sgl_task_params); +#endif diff --git a/drivers/scsi/qedf/qedf.h b/drivers/scsi/qedf/qedf.h index 96346a1b1515..40aeb6bb96a2 100644 --- a/drivers/scsi/qedf/qedf.h +++ b/drivers/scsi/qedf/qedf.h @@ -26,6 +26,7 @@ #include #include "qedf_version.h" #include "qedf_dbg.h" +#include "drv_fcoe_fw_funcs.h" /* Helpers to extract upper and lower 32-bits of pointer */ #define U64_HI(val) ((u32)(((u64)(val)) >> 32)) @@ -59,19 +60,17 @@ #define UPSTREAM_KEEP 1 struct qedf_mp_req { - uint8_t tm_flags; - uint32_t req_len; void *req_buf; dma_addr_t req_buf_dma; - struct fcoe_sge *mp_req_bd; + struct scsi_sge *mp_req_bd; dma_addr_t mp_req_bd_dma; struct fc_frame_header req_fc_hdr; uint32_t resp_len; void *resp_buf; dma_addr_t resp_buf_dma; - struct fcoe_sge *mp_resp_bd; + struct scsi_sge *mp_resp_bd; dma_addr_t mp_resp_bd_dma; struct fc_frame_header resp_fc_hdr; }; @@ -119,6 +118,7 @@ struct qedf_ioreq { #define QEDF_CMD_IN_CLEANUP 0x2 #define QEDF_CMD_SRR_SENT 0x3 u8 io_req_flags; + uint8_t tm_flags; struct qedf_rport *fcport; unsigned long flags; enum qedf_ioreq_event event; @@ -130,6 +130,8 @@ struct qedf_ioreq { struct completion tm_done; struct completion abts_done; struct fcoe_task_context *task; + struct fcoe_task_params *task_params; + struct scsi_sgl_task_params *sgl_task_params; int idx; /* * Need to allocate enough room for both sense data and FCP response data @@ -199,8 +201,8 @@ struct qedf_rport { dma_addr_t sq_pbl_dma; u32 sq_pbl_size; u32 sid; -#define QEDF_RPORT_TYPE_DISK 1 -#define QEDF_RPORT_TYPE_TAPE 2 +#define QEDF_RPORT_TYPE_DISK 0 +#define QEDF_RPORT_TYPE_TAPE 1 uint dev_type; /* Disk or tape */ struct list_head peers; }; @@ -391,7 +393,7 @@ struct qedf_ctx { struct io_bdt { struct qedf_ioreq *io_req; - struct fcoe_sge *bd_tbl; + struct scsi_sge *bd_tbl; dma_addr_t bd_tbl_dma; u16 bd_valid; }; @@ -400,7 +402,7 @@ struct qedf_cmd_mgr { struct qedf_ctx *qedf; u16 idx; struct io_bdt **io_bdt_pool; -#define FCOE_PARAMS_NUM_TASKS 4096 +#define FCOE_PARAMS_NUM_TASKS 2048 struct qedf_ioreq cmds[FCOE_PARAMS_NUM_TASKS]; spinlock_t lock; atomic_t free_list_cnt; @@ -465,9 +467,8 @@ extern void qedf_cmd_timer_set(struct qedf_ctx *qedf, struct qedf_ioreq *io_req, unsigned int timer_msec); extern int qedf_init_mp_req(struct qedf_ioreq *io_req); extern void qedf_init_mp_task(struct qedf_ioreq *io_req, - struct fcoe_task_context *task_ctx); -extern void qedf_add_to_sq(struct qedf_rport *fcport, u16 xid, - u32 ptu_invalidate, enum fcoe_task_type req_type, u32 offset); + struct fcoe_task_context *task_ctx, struct fcoe_wqe *wqe); +extern u16 qedf_get_sqe_idx(struct qedf_rport *fcport); extern void qedf_ring_doorbell(struct qedf_rport *fcport); extern void qedf_process_els_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, struct qedf_ioreq *els_req); diff --git a/drivers/scsi/qedf/qedf_els.c b/drivers/scsi/qedf/qedf_els.c index 59f3e5c73a13..c505d41f6dc8 100644 --- a/drivers/scsi/qedf/qedf_els.c +++ b/drivers/scsi/qedf/qedf_els.c @@ -25,6 +25,9 @@ static int qedf_initiate_els(struct qedf_rport *fcport, unsigned int op, uint16_t xid; uint32_t start_time = jiffies / HZ; uint32_t current_time; + struct fcoe_wqe *sqe; + unsigned long flags; + u16 sqe_idx; QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending ELS\n"); @@ -113,20 +116,25 @@ retry_els: /* Obtain exchange id */ xid = els_req->xid; + spin_lock_irqsave(&fcport->rport_lock, flags); + + sqe_idx = qedf_get_sqe_idx(fcport); + sqe = &fcport->sq[sqe_idx]; + memset(sqe, 0, sizeof(struct fcoe_wqe)); + /* Initialize task context for this IO request */ task = qedf_get_task_mem(&qedf->tasks, xid); - qedf_init_mp_task(els_req, task); + qedf_init_mp_task(els_req, task, sqe); /* Put timer on original I/O request */ if (timer_msec) qedf_cmd_timer_set(qedf, els_req, timer_msec); - qedf_add_to_sq(fcport, xid, 0, FCOE_TASK_TYPE_MIDPATH, 0); - /* Ring doorbell */ QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Ringing doorbell for ELS " "req\n"); qedf_ring_doorbell(fcport); + spin_unlock_irqrestore(&fcport->rport_lock, flags); els_err: return rc; } @@ -604,6 +612,8 @@ static void qedf_initiate_seq_cleanup(struct qedf_ioreq *orig_io_req, struct qedf_rport *fcport; unsigned long flags; struct qedf_els_cb_arg *cb_arg; + struct fcoe_wqe *sqe; + u16 sqe_idx; fcport = orig_io_req->fcport; @@ -631,8 +641,13 @@ static void qedf_initiate_seq_cleanup(struct qedf_ioreq *orig_io_req, spin_lock_irqsave(&fcport->rport_lock, flags); - qedf_add_to_sq(fcport, orig_io_req->xid, 0, - FCOE_TASK_TYPE_SEQUENCE_CLEANUP, offset); + sqe_idx = qedf_get_sqe_idx(fcport); + sqe = &fcport->sq[sqe_idx]; + memset(sqe, 0, sizeof(struct fcoe_wqe)); + orig_io_req->task_params->sqe = sqe; + + init_initiator_sequence_recovery_fcoe_task(orig_io_req->task_params, + offset); qedf_ring_doorbell(fcport); spin_unlock_irqrestore(&fcport->rport_lock, flags); diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c index ee0dcf9d3aba..af2294635ab2 100644 --- a/drivers/scsi/qedf/qedf_io.c +++ b/drivers/scsi/qedf/qedf_io.c @@ -96,7 +96,7 @@ void qedf_cmd_mgr_free(struct qedf_cmd_mgr *cmgr) if (!cmgr->io_bdt_pool) goto free_cmd_pool; - bd_tbl_sz = QEDF_MAX_BDS_PER_CMD * sizeof(struct fcoe_sge); + bd_tbl_sz = QEDF_MAX_BDS_PER_CMD * sizeof(struct scsi_sge); for (i = 0; i < num_ios; i++) { bdt_info = cmgr->io_bdt_pool[i]; if (bdt_info->bd_tbl) { @@ -119,6 +119,8 @@ free_cmd_pool: for (i = 0; i < num_ios; i++) { io_req = &cmgr->cmds[i]; + kfree(io_req->sgl_task_params); + kfree(io_req->task_params); /* Make sure we free per command sense buffer */ if (io_req->sense_buffer) dma_free_coherent(&qedf->pdev->dev, @@ -178,7 +180,7 @@ struct qedf_cmd_mgr *qedf_cmd_mgr_alloc(struct qedf_ctx *qedf) spin_lock_init(&cmgr->lock); /* - * Initialize list of qedf_ioreq. + * Initialize I/O request fields. */ xid = QEDF_MIN_XID; @@ -196,6 +198,29 @@ struct qedf_cmd_mgr *qedf_cmd_mgr_alloc(struct qedf_ctx *qedf) GFP_KERNEL); if (!io_req->sense_buffer) goto mem_err; + + /* Allocate task parameters to pass to f/w init funcions */ + io_req->task_params = kzalloc(sizeof(*io_req->task_params), + GFP_KERNEL); + if (!io_req->task_params) { + QEDF_ERR(&(qedf->dbg_ctx), + "Failed to allocate task_params for xid=0x%x\n", + i); + goto mem_err; + } + + /* + * Allocate scatter/gather list info to pass to f/w init + * functions. + */ + io_req->sgl_task_params = kzalloc( + sizeof(struct scsi_sgl_task_params), GFP_KERNEL); + if (!io_req->sgl_task_params) { + QEDF_ERR(&(qedf->dbg_ctx), + "Failed to allocate sgl_task_params for xid=0x%x\n", + i); + goto mem_err; + } } /* Allocate pool of io_bdts - one for each qedf_ioreq */ @@ -211,8 +236,8 @@ struct qedf_cmd_mgr *qedf_cmd_mgr_alloc(struct qedf_ctx *qedf) cmgr->io_bdt_pool[i] = kmalloc(sizeof(struct io_bdt), GFP_KERNEL); if (!cmgr->io_bdt_pool[i]) { - QEDF_WARN(&(qedf->dbg_ctx), "Failed to alloc " - "io_bdt_pool[%d].\n", i); + QEDF_WARN(&(qedf->dbg_ctx), + "Failed to alloc io_bdt_pool[%d].\n", i); goto mem_err; } } @@ -220,11 +245,11 @@ struct qedf_cmd_mgr *qedf_cmd_mgr_alloc(struct qedf_ctx *qedf) for (i = 0; i < num_ios; i++) { bdt_info = cmgr->io_bdt_pool[i]; bdt_info->bd_tbl = dma_alloc_coherent(&qedf->pdev->dev, - QEDF_MAX_BDS_PER_CMD * sizeof(struct fcoe_sge), + QEDF_MAX_BDS_PER_CMD * sizeof(struct scsi_sge), &bdt_info->bd_tbl_dma, GFP_KERNEL); if (!bdt_info->bd_tbl) { - QEDF_WARN(&(qedf->dbg_ctx), "Failed to alloc " - "bdt_tbl[%d].\n", i); + QEDF_WARN(&(qedf->dbg_ctx), + "Failed to alloc bdt_tbl[%d].\n", i); goto mem_err; } } @@ -318,6 +343,7 @@ struct qedf_ioreq *qedf_alloc_cmd(struct qedf_rport *fcport, u8 cmd_type) } bd_tbl->io_req = io_req; io_req->cmd_type = cmd_type; + io_req->tm_flags = 0; /* Reset sequence offset data */ io_req->rx_buf_off = 0; @@ -336,10 +362,9 @@ static void qedf_free_mp_resc(struct qedf_ioreq *io_req) { struct qedf_mp_req *mp_req = &(io_req->mp_req); struct qedf_ctx *qedf = io_req->fcport->qedf; - uint64_t sz = sizeof(struct fcoe_sge); + uint64_t sz = sizeof(struct scsi_sge); /* clear tm flags */ - mp_req->tm_flags = 0; if (mp_req->mp_req_bd) { dma_free_coherent(&qedf->pdev->dev, sz, mp_req->mp_req_bd, mp_req->mp_req_bd_dma); @@ -387,7 +412,7 @@ void qedf_release_cmd(struct kref *ref) static int qedf_split_bd(struct qedf_ioreq *io_req, u64 addr, int sg_len, int bd_index) { - struct fcoe_sge *bd = io_req->bd_tbl->bd_tbl; + struct scsi_sge *bd = io_req->bd_tbl->bd_tbl; int frag_size, sg_frags; sg_frags = 0; @@ -398,7 +423,7 @@ static int qedf_split_bd(struct qedf_ioreq *io_req, u64 addr, int sg_len, frag_size = sg_len; bd[bd_index + sg_frags].sge_addr.lo = U64_LO(addr); bd[bd_index + sg_frags].sge_addr.hi = U64_HI(addr); - bd[bd_index + sg_frags].size = (uint16_t)frag_size; + bd[bd_index + sg_frags].sge_len = (uint16_t)frag_size; addr += (u64)frag_size; sg_frags++; @@ -413,7 +438,7 @@ static int qedf_map_sg(struct qedf_ioreq *io_req) struct Scsi_Host *host = sc->device->host; struct fc_lport *lport = shost_priv(host); struct qedf_ctx *qedf = lport_priv(lport); - struct fcoe_sge *bd = io_req->bd_tbl->bd_tbl; + struct scsi_sge *bd = io_req->bd_tbl->bd_tbl; struct scatterlist *sg; int byte_count = 0; int sg_count = 0; @@ -439,7 +464,7 @@ static int qedf_map_sg(struct qedf_ioreq *io_req) bd[bd_count].sge_addr.lo = (addr & 0xffffffff); bd[bd_count].sge_addr.hi = (addr >> 32); - bd[bd_count].size = (u16)sg_len; + bd[bd_count].sge_len = (u16)sg_len; return ++bd_count; } @@ -480,7 +505,7 @@ static int qedf_map_sg(struct qedf_ioreq *io_req) sg_frags = 1; bd[bd_count].sge_addr.lo = U64_LO(addr); bd[bd_count].sge_addr.hi = U64_HI(addr); - bd[bd_count].size = (uint16_t)sg_len; + bd[bd_count].sge_len = (uint16_t)sg_len; } bd_count += sg_frags; @@ -498,7 +523,7 @@ static int qedf_map_sg(struct qedf_ioreq *io_req) static int qedf_build_bd_list_from_sg(struct qedf_ioreq *io_req) { struct scsi_cmnd *sc = io_req->sc_cmd; - struct fcoe_sge *bd = io_req->bd_tbl->bd_tbl; + struct scsi_sge *bd = io_req->bd_tbl->bd_tbl; int bd_count; if (scsi_sg_count(sc)) { @@ -508,7 +533,7 @@ static int qedf_build_bd_list_from_sg(struct qedf_ioreq *io_req) } else { bd_count = 0; bd[0].sge_addr.lo = bd[0].sge_addr.hi = 0; - bd[0].size = 0; + bd[0].sge_len = 0; } io_req->bd_tbl->bd_valid = bd_count; @@ -529,430 +554,223 @@ static void qedf_build_fcp_cmnd(struct qedf_ioreq *io_req, /* 4 bytes: flag info */ fcp_cmnd->fc_pri_ta = 0; - fcp_cmnd->fc_tm_flags = io_req->mp_req.tm_flags; + fcp_cmnd->fc_tm_flags = io_req->tm_flags; fcp_cmnd->fc_flags = io_req->io_req_flags; fcp_cmnd->fc_cmdref = 0; /* Populate data direction */ - if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) - fcp_cmnd->fc_flags |= FCP_CFL_WRDATA; - else if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) + if (io_req->cmd_type == QEDF_TASK_MGMT_CMD) { fcp_cmnd->fc_flags |= FCP_CFL_RDDATA; + } else { + if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) + fcp_cmnd->fc_flags |= FCP_CFL_WRDATA; + else if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) + fcp_cmnd->fc_flags |= FCP_CFL_RDDATA; + } fcp_cmnd->fc_pri_ta = FCP_PTA_SIMPLE; /* 16 bytes: CDB information */ - memcpy(fcp_cmnd->fc_cdb, sc_cmd->cmnd, sc_cmd->cmd_len); + if (io_req->cmd_type != QEDF_TASK_MGMT_CMD) + memcpy(fcp_cmnd->fc_cdb, sc_cmd->cmnd, sc_cmd->cmd_len); /* 4 bytes: FCP data length */ fcp_cmnd->fc_dl = htonl(io_req->data_xfer_len); - } static void qedf_init_task(struct qedf_rport *fcport, struct fc_lport *lport, - struct qedf_ioreq *io_req, u32 *ptu_invalidate, - struct fcoe_task_context *task_ctx) + struct qedf_ioreq *io_req, struct fcoe_task_context *task_ctx, + struct fcoe_wqe *sqe) { enum fcoe_task_type task_type; struct scsi_cmnd *sc_cmd = io_req->sc_cmd; struct io_bdt *bd_tbl = io_req->bd_tbl; - union fcoe_data_desc_ctx *data_desc; - u32 *fcp_cmnd; + u8 fcp_cmnd[32]; u32 tmp_fcp_cmnd[8]; - int cnt, i; - int bd_count; + int bd_count = 0; struct qedf_ctx *qedf = fcport->qedf; uint16_t cq_idx = smp_processor_id() % qedf->num_queues; - u8 tmp_sgl_mode = 0; - u8 mst_sgl_mode = 0; + struct regpair sense_data_buffer_phys_addr; + u32 tx_io_size = 0; + u32 rx_io_size = 0; + int i, cnt; - memset(task_ctx, 0, sizeof(struct fcoe_task_context)); + /* Note init_initiator_rw_fcoe_task memsets the task context */ io_req->task = task_ctx; + memset(task_ctx, 0, sizeof(struct fcoe_task_context)); + memset(io_req->task_params, 0, sizeof(struct fcoe_task_params)); + memset(io_req->sgl_task_params, 0, sizeof(struct scsi_sgl_task_params)); - if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) - task_type = FCOE_TASK_TYPE_WRITE_INITIATOR; - else + /* Set task type bassed on DMA directio of command */ + if (io_req->cmd_type == QEDF_TASK_MGMT_CMD) { task_type = FCOE_TASK_TYPE_READ_INITIATOR; - - /* Y Storm context */ - task_ctx->ystorm_st_context.expect_first_xfer = 1; - task_ctx->ystorm_st_context.data_2_trns_rem = io_req->data_xfer_len; - /* Check if this is required */ - task_ctx->ystorm_st_context.ox_id = io_req->xid; - task_ctx->ystorm_st_context.task_rety_identifier = - io_req->task_retry_identifier; - - /* T Storm ag context */ - SET_FIELD(task_ctx->tstorm_ag_context.flags0, - TSTORM_FCOE_TASK_AG_CTX_CONNECTION_TYPE, PROTOCOLID_FCOE); - task_ctx->tstorm_ag_context.icid = (u16)fcport->fw_cid; - - /* T Storm st context */ - SET_FIELD(task_ctx->tstorm_st_context.read_write.flags, - FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_EXP_FIRST_FRAME, - 1); - task_ctx->tstorm_st_context.read_write.rx_id = 0xffff; - - task_ctx->tstorm_st_context.read_only.dev_type = - FCOE_TASK_DEV_TYPE_DISK; - task_ctx->tstorm_st_context.read_only.conf_supported = 0; - task_ctx->tstorm_st_context.read_only.cid = fcport->fw_cid; - - /* Completion queue for response. */ - task_ctx->tstorm_st_context.read_only.glbl_q_num = cq_idx; - task_ctx->tstorm_st_context.read_only.fcp_cmd_trns_size = - io_req->data_xfer_len; - task_ctx->tstorm_st_context.read_write.e_d_tov_exp_timeout_val = - lport->e_d_tov; - - task_ctx->ustorm_ag_context.global_cq_num = cq_idx; - io_req->fp_idx = cq_idx; - - bd_count = bd_tbl->bd_valid; - if (task_type == FCOE_TASK_TYPE_WRITE_INITIATOR) { - /* Setup WRITE task */ - struct fcoe_sge *fcoe_bd_tbl = bd_tbl->bd_tbl; - - task_ctx->ystorm_st_context.task_type = - FCOE_TASK_TYPE_WRITE_INITIATOR; - data_desc = &task_ctx->ystorm_st_context.data_desc; - - if (io_req->use_slowpath) { - SET_FIELD(task_ctx->ystorm_st_context.sgl_mode, - YSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE, - FCOE_SLOW_SGL); - data_desc->slow.base_sgl_addr.lo = - U64_LO(bd_tbl->bd_tbl_dma); - data_desc->slow.base_sgl_addr.hi = - U64_HI(bd_tbl->bd_tbl_dma); - data_desc->slow.remainder_num_sges = bd_count; - data_desc->slow.curr_sge_off = 0; - data_desc->slow.curr_sgl_index = 0; - qedf->slow_sge_ios++; - io_req->sge_type = QEDF_IOREQ_SLOW_SGE; - } else { - SET_FIELD(task_ctx->ystorm_st_context.sgl_mode, - YSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE, - (bd_count <= 4) ? (enum fcoe_sgl_mode)bd_count : - FCOE_MUL_FAST_SGES); - - if (bd_count == 1) { - data_desc->single_sge.sge_addr.lo = - fcoe_bd_tbl->sge_addr.lo; - data_desc->single_sge.sge_addr.hi = - fcoe_bd_tbl->sge_addr.hi; - data_desc->single_sge.size = - fcoe_bd_tbl->size; - data_desc->single_sge.is_valid_sge = 0; - qedf->single_sge_ios++; - io_req->sge_type = QEDF_IOREQ_SINGLE_SGE; - } else { - data_desc->fast.sgl_start_addr.lo = - U64_LO(bd_tbl->bd_tbl_dma); - data_desc->fast.sgl_start_addr.hi = - U64_HI(bd_tbl->bd_tbl_dma); - data_desc->fast.sgl_byte_offset = - data_desc->fast.sgl_start_addr.lo & - (QEDF_PAGE_SIZE - 1); - if (data_desc->fast.sgl_byte_offset > 0) - QEDF_ERR(&(qedf->dbg_ctx), - "byte_offset=%u for xid=0x%x.\n", - io_req->xid, - data_desc->fast.sgl_byte_offset); - data_desc->fast.task_reuse_cnt = - io_req->reuse_count; - io_req->reuse_count++; - if (io_req->reuse_count == QEDF_MAX_REUSE) { - *ptu_invalidate = 1; - io_req->reuse_count = 0; - } - qedf->fast_sge_ios++; - io_req->sge_type = QEDF_IOREQ_FAST_SGE; - } - } - - /* T Storm context */ - task_ctx->tstorm_st_context.read_only.task_type = - FCOE_TASK_TYPE_WRITE_INITIATOR; - - /* M Storm context */ - tmp_sgl_mode = GET_FIELD(task_ctx->ystorm_st_context.sgl_mode, - YSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE); - SET_FIELD(task_ctx->mstorm_st_context.non_fp.tx_rx_sgl_mode, - FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_TX_SGL_MODE, - tmp_sgl_mode); - } else { - /* Setup READ task */ - - /* M Storm context */ - struct fcoe_sge *fcoe_bd_tbl = bd_tbl->bd_tbl; - - data_desc = &task_ctx->mstorm_st_context.fp.data_desc; - task_ctx->mstorm_st_context.fp.data_2_trns_rem = - io_req->data_xfer_len; - - if (io_req->use_slowpath) { - SET_FIELD( - task_ctx->mstorm_st_context.non_fp.tx_rx_sgl_mode, - FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_RX_SGL_MODE, - FCOE_SLOW_SGL); - data_desc->slow.base_sgl_addr.lo = - U64_LO(bd_tbl->bd_tbl_dma); - data_desc->slow.base_sgl_addr.hi = - U64_HI(bd_tbl->bd_tbl_dma); - data_desc->slow.remainder_num_sges = - bd_count; - data_desc->slow.curr_sge_off = 0; - data_desc->slow.curr_sgl_index = 0; - qedf->slow_sge_ios++; - io_req->sge_type = QEDF_IOREQ_SLOW_SGE; + if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) { + task_type = FCOE_TASK_TYPE_WRITE_INITIATOR; + tx_io_size = io_req->data_xfer_len; } else { - SET_FIELD( - task_ctx->mstorm_st_context.non_fp.tx_rx_sgl_mode, - FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_RX_SGL_MODE, - (bd_count <= 4) ? (enum fcoe_sgl_mode)bd_count : - FCOE_MUL_FAST_SGES); - - if (bd_count == 1) { - data_desc->single_sge.sge_addr.lo = - fcoe_bd_tbl->sge_addr.lo; - data_desc->single_sge.sge_addr.hi = - fcoe_bd_tbl->sge_addr.hi; - data_desc->single_sge.size = - fcoe_bd_tbl->size; - data_desc->single_sge.is_valid_sge = 0; - qedf->single_sge_ios++; - io_req->sge_type = QEDF_IOREQ_SINGLE_SGE; - } else { - data_desc->fast.sgl_start_addr.lo = - U64_LO(bd_tbl->bd_tbl_dma); - data_desc->fast.sgl_start_addr.hi = - U64_HI(bd_tbl->bd_tbl_dma); - data_desc->fast.sgl_byte_offset = 0; - data_desc->fast.task_reuse_cnt = - io_req->reuse_count; - io_req->reuse_count++; - if (io_req->reuse_count == QEDF_MAX_REUSE) { - *ptu_invalidate = 1; - io_req->reuse_count = 0; - } - qedf->fast_sge_ios++; - io_req->sge_type = QEDF_IOREQ_FAST_SGE; - } + task_type = FCOE_TASK_TYPE_READ_INITIATOR; + rx_io_size = io_req->data_xfer_len; } - - /* Y Storm context */ - task_ctx->ystorm_st_context.expect_first_xfer = 0; - task_ctx->ystorm_st_context.task_type = - FCOE_TASK_TYPE_READ_INITIATOR; - - /* T Storm context */ - task_ctx->tstorm_st_context.read_only.task_type = - FCOE_TASK_TYPE_READ_INITIATOR; - mst_sgl_mode = GET_FIELD( - task_ctx->mstorm_st_context.non_fp.tx_rx_sgl_mode, - FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_RX_SGL_MODE); - SET_FIELD(task_ctx->tstorm_st_context.read_write.flags, - FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_RX_SGL_MODE, - mst_sgl_mode); } + /* Setup the fields for fcoe_task_params */ + io_req->task_params->context = task_ctx; + io_req->task_params->sqe = sqe; + io_req->task_params->task_type = task_type; + io_req->task_params->tx_io_size = tx_io_size; + io_req->task_params->rx_io_size = rx_io_size; + io_req->task_params->conn_cid = fcport->fw_cid; + io_req->task_params->itid = io_req->xid; + io_req->task_params->cq_rss_number = cq_idx; + io_req->task_params->is_tape_device = fcport->dev_type; + + /* Fill in information for scatter/gather list */ + if (io_req->cmd_type != QEDF_TASK_MGMT_CMD) { + bd_count = bd_tbl->bd_valid; + io_req->sgl_task_params->sgl = bd_tbl->bd_tbl; + io_req->sgl_task_params->sgl_phys_addr.lo = + U64_LO(bd_tbl->bd_tbl_dma); + io_req->sgl_task_params->sgl_phys_addr.hi = + U64_HI(bd_tbl->bd_tbl_dma); + io_req->sgl_task_params->num_sges = bd_count; + io_req->sgl_task_params->total_buffer_size = + scsi_bufflen(io_req->sc_cmd); + io_req->sgl_task_params->small_mid_sge = + io_req->use_slowpath; + } + + /* Fill in physical address of sense buffer */ + sense_data_buffer_phys_addr.lo = U64_LO(io_req->sense_buffer_dma); + sense_data_buffer_phys_addr.hi = U64_HI(io_req->sense_buffer_dma); + /* fill FCP_CMND IU */ - fcp_cmnd = (u32 *)task_ctx->ystorm_st_context.tx_info_union.fcp_cmd_payload.opaque; - qedf_build_fcp_cmnd(io_req, (struct fcp_cmnd *)&tmp_fcp_cmnd); + qedf_build_fcp_cmnd(io_req, (struct fcp_cmnd *)tmp_fcp_cmnd); /* Swap fcp_cmnd since FC is big endian */ cnt = sizeof(struct fcp_cmnd) / sizeof(u32); - for (i = 0; i < cnt; i++) { - *fcp_cmnd = cpu_to_be32(tmp_fcp_cmnd[i]); - fcp_cmnd++; + tmp_fcp_cmnd[i] = cpu_to_be32(tmp_fcp_cmnd[i]); + } + memcpy(fcp_cmnd, tmp_fcp_cmnd, sizeof(struct fcp_cmnd)); + + init_initiator_rw_fcoe_task(io_req->task_params, + io_req->sgl_task_params, + sense_data_buffer_phys_addr, + io_req->task_retry_identifier, fcp_cmnd); + + /* Increment SGL type counters */ + if (bd_count == 1) { + qedf->single_sge_ios++; + io_req->sge_type = QEDF_IOREQ_SINGLE_SGE; + } else if (io_req->use_slowpath) { + qedf->slow_sge_ios++; + io_req->sge_type = QEDF_IOREQ_SLOW_SGE; + } else { + qedf->fast_sge_ios++; + io_req->sge_type = QEDF_IOREQ_FAST_SGE; } - - /* M Storm context - Sense buffer */ - task_ctx->mstorm_st_context.non_fp.rsp_buf_addr.lo = - U64_LO(io_req->sense_buffer_dma); - task_ctx->mstorm_st_context.non_fp.rsp_buf_addr.hi = - U64_HI(io_req->sense_buffer_dma); } void qedf_init_mp_task(struct qedf_ioreq *io_req, - struct fcoe_task_context *task_ctx) + struct fcoe_task_context *task_ctx, struct fcoe_wqe *sqe) { struct qedf_mp_req *mp_req = &(io_req->mp_req); struct qedf_rport *fcport = io_req->fcport; struct qedf_ctx *qedf = io_req->fcport->qedf; struct fc_frame_header *fc_hdr; - enum fcoe_task_type task_type = 0; - union fcoe_data_desc_ctx *data_desc; + struct fcoe_tx_mid_path_params task_fc_hdr; + struct scsi_sgl_task_params tx_sgl_task_params; + struct scsi_sgl_task_params rx_sgl_task_params; - QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Initializing MP task " - "for cmd_type = %d\n", io_req->cmd_type); + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "Initializing MP task for cmd_type=%d\n", + io_req->cmd_type); qedf->control_requests++; - /* Obtain task_type */ - if ((io_req->cmd_type == QEDF_TASK_MGMT_CMD) || - (io_req->cmd_type == QEDF_ELS)) { - task_type = FCOE_TASK_TYPE_MIDPATH; - } else if (io_req->cmd_type == QEDF_ABTS) { - task_type = FCOE_TASK_TYPE_ABTS; - } - + memset(&tx_sgl_task_params, 0, sizeof(struct scsi_sgl_task_params)); + memset(&rx_sgl_task_params, 0, sizeof(struct scsi_sgl_task_params)); memset(task_ctx, 0, sizeof(struct fcoe_task_context)); + memset(&task_fc_hdr, 0, sizeof(struct fcoe_tx_mid_path_params)); /* Setup the task from io_req for easy reference */ io_req->task = task_ctx; - QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "task type = %d\n", - task_type); - - /* YSTORM only */ - { - /* Initialize YSTORM task context */ - struct fcoe_tx_mid_path_params *task_fc_hdr = - &task_ctx->ystorm_st_context.tx_info_union.tx_params.mid_path; - memset(task_fc_hdr, 0, sizeof(struct fcoe_tx_mid_path_params)); - task_ctx->ystorm_st_context.task_rety_identifier = - io_req->task_retry_identifier; - - /* Init SGL parameters */ - if ((task_type == FCOE_TASK_TYPE_MIDPATH) || - (task_type == FCOE_TASK_TYPE_UNSOLICITED)) { - data_desc = &task_ctx->ystorm_st_context.data_desc; - data_desc->slow.base_sgl_addr.lo = - U64_LO(mp_req->mp_req_bd_dma); - data_desc->slow.base_sgl_addr.hi = - U64_HI(mp_req->mp_req_bd_dma); - data_desc->slow.remainder_num_sges = 1; - data_desc->slow.curr_sge_off = 0; - data_desc->slow.curr_sgl_index = 0; - } - - fc_hdr = &(mp_req->req_fc_hdr); - if (task_type == FCOE_TASK_TYPE_MIDPATH) { - fc_hdr->fh_ox_id = io_req->xid; - fc_hdr->fh_rx_id = htons(0xffff); - } else if (task_type == FCOE_TASK_TYPE_UNSOLICITED) { - fc_hdr->fh_rx_id = io_req->xid; - } + /* Setup the fields for fcoe_task_params */ + io_req->task_params->context = task_ctx; + io_req->task_params->sqe = sqe; + io_req->task_params->task_type = FCOE_TASK_TYPE_MIDPATH; + io_req->task_params->tx_io_size = io_req->data_xfer_len; + /* rx_io_size tells the f/w how large a response buffer we have */ + io_req->task_params->rx_io_size = PAGE_SIZE; + io_req->task_params->conn_cid = fcport->fw_cid; + io_req->task_params->itid = io_req->xid; + /* Return middle path commands on CQ 0 */ + io_req->task_params->cq_rss_number = 0; + io_req->task_params->is_tape_device = fcport->dev_type; + + fc_hdr = &(mp_req->req_fc_hdr); + /* Set OX_ID and RX_ID based on driver task id */ + fc_hdr->fh_ox_id = io_req->xid; + fc_hdr->fh_rx_id = htons(0xffff); + + /* Set up FC header information */ + task_fc_hdr.parameter = fc_hdr->fh_parm_offset; + task_fc_hdr.r_ctl = fc_hdr->fh_r_ctl; + task_fc_hdr.type = fc_hdr->fh_type; + task_fc_hdr.cs_ctl = fc_hdr->fh_cs_ctl; + task_fc_hdr.df_ctl = fc_hdr->fh_df_ctl; + task_fc_hdr.rx_id = fc_hdr->fh_rx_id; + task_fc_hdr.ox_id = fc_hdr->fh_ox_id; + + /* Set up s/g list parameters for request buffer */ + tx_sgl_task_params.sgl = mp_req->mp_req_bd; + tx_sgl_task_params.sgl_phys_addr.lo = U64_LO(mp_req->mp_req_bd_dma); + tx_sgl_task_params.sgl_phys_addr.hi = U64_HI(mp_req->mp_req_bd_dma); + tx_sgl_task_params.num_sges = 1; + /* Set PAGE_SIZE for now since sg element is that size ??? */ + tx_sgl_task_params.total_buffer_size = io_req->data_xfer_len; + tx_sgl_task_params.small_mid_sge = 0; + + /* Set up s/g list parameters for request buffer */ + rx_sgl_task_params.sgl = mp_req->mp_resp_bd; + rx_sgl_task_params.sgl_phys_addr.lo = U64_LO(mp_req->mp_resp_bd_dma); + rx_sgl_task_params.sgl_phys_addr.hi = U64_HI(mp_req->mp_resp_bd_dma); + rx_sgl_task_params.num_sges = 1; + /* Set PAGE_SIZE for now since sg element is that size ??? */ + rx_sgl_task_params.total_buffer_size = PAGE_SIZE; + rx_sgl_task_params.small_mid_sge = 0; - /* Fill FC Header into middle path buffer */ - task_fc_hdr->parameter = fc_hdr->fh_parm_offset; - task_fc_hdr->r_ctl = fc_hdr->fh_r_ctl; - task_fc_hdr->type = fc_hdr->fh_type; - task_fc_hdr->cs_ctl = fc_hdr->fh_cs_ctl; - task_fc_hdr->df_ctl = fc_hdr->fh_df_ctl; - task_fc_hdr->rx_id = fc_hdr->fh_rx_id; - task_fc_hdr->ox_id = fc_hdr->fh_ox_id; - - task_ctx->ystorm_st_context.data_2_trns_rem = - io_req->data_xfer_len; - task_ctx->ystorm_st_context.task_type = task_type; - } - - /* TSTORM ONLY */ - { - task_ctx->tstorm_ag_context.icid = (u16)fcport->fw_cid; - task_ctx->tstorm_st_context.read_only.cid = fcport->fw_cid; - /* Always send middle-path repsonses on CQ #0 */ - task_ctx->tstorm_st_context.read_only.glbl_q_num = 0; - io_req->fp_idx = 0; - SET_FIELD(task_ctx->tstorm_ag_context.flags0, - TSTORM_FCOE_TASK_AG_CTX_CONNECTION_TYPE, - PROTOCOLID_FCOE); - task_ctx->tstorm_st_context.read_only.task_type = task_type; - SET_FIELD(task_ctx->tstorm_st_context.read_write.flags, - FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_EXP_FIRST_FRAME, - 1); - task_ctx->tstorm_st_context.read_write.rx_id = 0xffff; - } - - /* MSTORM only */ - { - if (task_type == FCOE_TASK_TYPE_MIDPATH) { - /* Initialize task context */ - data_desc = &task_ctx->mstorm_st_context.fp.data_desc; - - /* Set cache sges address and length */ - data_desc->slow.base_sgl_addr.lo = - U64_LO(mp_req->mp_resp_bd_dma); - data_desc->slow.base_sgl_addr.hi = - U64_HI(mp_req->mp_resp_bd_dma); - data_desc->slow.remainder_num_sges = 1; - data_desc->slow.curr_sge_off = 0; - data_desc->slow.curr_sgl_index = 0; - /* - * Also need to fil in non-fastpath response address - * for middle path commands. - */ - task_ctx->mstorm_st_context.non_fp.rsp_buf_addr.lo = - U64_LO(mp_req->mp_resp_bd_dma); - task_ctx->mstorm_st_context.non_fp.rsp_buf_addr.hi = - U64_HI(mp_req->mp_resp_bd_dma); - } - } - - /* USTORM ONLY */ - { - task_ctx->ustorm_ag_context.global_cq_num = 0; - } + /* + * Last arg is 0 as previous code did not set that we wanted the + * fc header information. + */ + init_initiator_midpath_unsolicited_fcoe_task(io_req->task_params, + &task_fc_hdr, + &tx_sgl_task_params, + &rx_sgl_task_params, 0); - /* I/O stats. Middle path commands always use slow SGEs */ - qedf->slow_sge_ios++; - io_req->sge_type = QEDF_IOREQ_SLOW_SGE; + /* Midpath requests always consume 1 SGE */ + qedf->single_sge_ios++; } -void qedf_add_to_sq(struct qedf_rport *fcport, u16 xid, u32 ptu_invalidate, - enum fcoe_task_type req_type, u32 offset) +/* Presumed that fcport->rport_lock is held */ +u16 qedf_get_sqe_idx(struct qedf_rport *fcport) { - struct fcoe_wqe *sqe; uint16_t total_sqe = (fcport->sq_mem_size)/(sizeof(struct fcoe_wqe)); + u16 rval; - sqe = &fcport->sq[fcport->sq_prod_idx]; + rval = fcport->sq_prod_idx; + /* Adjust ring index */ fcport->sq_prod_idx++; fcport->fw_sq_prod_idx++; if (fcport->sq_prod_idx == total_sqe) fcport->sq_prod_idx = 0; - switch (req_type) { - case FCOE_TASK_TYPE_WRITE_INITIATOR: - case FCOE_TASK_TYPE_READ_INITIATOR: - SET_FIELD(sqe->flags, FCOE_WQE_REQ_TYPE, SEND_FCOE_CMD); - if (ptu_invalidate) - SET_FIELD(sqe->flags, FCOE_WQE_INVALIDATE_PTU, 1); - break; - case FCOE_TASK_TYPE_MIDPATH: - SET_FIELD(sqe->flags, FCOE_WQE_REQ_TYPE, SEND_FCOE_MIDPATH); - break; - case FCOE_TASK_TYPE_ABTS: - SET_FIELD(sqe->flags, FCOE_WQE_REQ_TYPE, - SEND_FCOE_ABTS_REQUEST); - break; - case FCOE_TASK_TYPE_EXCHANGE_CLEANUP: - SET_FIELD(sqe->flags, FCOE_WQE_REQ_TYPE, - FCOE_EXCHANGE_CLEANUP); - break; - case FCOE_TASK_TYPE_SEQUENCE_CLEANUP: - SET_FIELD(sqe->flags, FCOE_WQE_REQ_TYPE, - FCOE_SEQUENCE_RECOVERY); - /* NOTE: offset param only used for sequence recovery */ - sqe->additional_info_union.seq_rec_updated_offset = offset; - break; - case FCOE_TASK_TYPE_UNSOLICITED: - break; - default: - break; - } - - sqe->task_id = xid; - - /* Make sure SQ data is coherent */ - wmb(); - + return rval; } void qedf_ring_doorbell(struct qedf_rport *fcport) @@ -1029,7 +847,8 @@ int qedf_post_io_req(struct qedf_rport *fcport, struct qedf_ioreq *io_req) struct fcoe_task_context *task_ctx; u16 xid; enum fcoe_task_type req_type = 0; - u32 ptu_invalidate = 0; + struct fcoe_wqe *sqe; + u16 sqe_idx; /* Initialize rest of io_req fileds */ io_req->data_xfer_len = scsi_bufflen(sc_cmd); @@ -1061,6 +880,16 @@ int qedf_post_io_req(struct qedf_rport *fcport, struct qedf_ioreq *io_req) return -EAGAIN; } + if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { + QEDF_ERR(&(qedf->dbg_ctx), "Session not offloaded yet.\n"); + kref_put(&io_req->refcount, qedf_release_cmd); + } + + /* Obtain free SQE */ + sqe_idx = qedf_get_sqe_idx(fcport); + sqe = &fcport->sq[sqe_idx]; + memset(sqe, 0, sizeof(struct fcoe_wqe)); + /* Get the task context */ task_ctx = qedf_get_task_mem(&qedf->tasks, xid); if (!task_ctx) { @@ -1070,15 +899,7 @@ int qedf_post_io_req(struct qedf_rport *fcport, struct qedf_ioreq *io_req) return -EINVAL; } - qedf_init_task(fcport, lport, io_req, &ptu_invalidate, task_ctx); - - if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { - QEDF_ERR(&(qedf->dbg_ctx), "Session not offloaded yet.\n"); - kref_put(&io_req->refcount, qedf_release_cmd); - } - - /* Obtain free SQ entry */ - qedf_add_to_sq(fcport, xid, ptu_invalidate, req_type, 0); + qedf_init_task(fcport, lport, io_req, task_ctx, sqe); /* Ring doorbell */ qedf_ring_doorbell(fcport); @@ -1661,6 +1482,8 @@ int qedf_initiate_abts(struct qedf_ioreq *io_req, bool return_scsi_cmd_on_abts) u32 r_a_tov = 0; int rc = 0; unsigned long flags; + struct fcoe_wqe *sqe; + u16 sqe_idx; r_a_tov = rdata->r_a_tov; lport = qedf->lport; @@ -1712,10 +1535,12 @@ int qedf_initiate_abts(struct qedf_ioreq *io_req, bool return_scsi_cmd_on_abts) spin_lock_irqsave(&fcport->rport_lock, flags); - /* Add ABTS to send queue */ - qedf_add_to_sq(fcport, xid, 0, FCOE_TASK_TYPE_ABTS, 0); + sqe_idx = qedf_get_sqe_idx(fcport); + sqe = &fcport->sq[sqe_idx]; + memset(sqe, 0, sizeof(struct fcoe_wqe)); + io_req->task_params->sqe = sqe; - /* Ring doorbell */ + init_initiator_abort_fcoe_task(io_req->task_params); qedf_ring_doorbell(fcport); spin_unlock_irqrestore(&fcport->rport_lock, flags); @@ -1784,8 +1609,8 @@ void qedf_process_abts_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, int qedf_init_mp_req(struct qedf_ioreq *io_req) { struct qedf_mp_req *mp_req; - struct fcoe_sge *mp_req_bd; - struct fcoe_sge *mp_resp_bd; + struct scsi_sge *mp_req_bd; + struct scsi_sge *mp_resp_bd; struct qedf_ctx *qedf = io_req->fcport->qedf; dma_addr_t addr; uint64_t sz; @@ -1819,7 +1644,7 @@ int qedf_init_mp_req(struct qedf_ioreq *io_req) } /* Allocate and map mp_req_bd and mp_resp_bd */ - sz = sizeof(struct fcoe_sge); + sz = sizeof(struct scsi_sge); mp_req->mp_req_bd = dma_alloc_coherent(&qedf->pdev->dev, sz, &mp_req->mp_req_bd_dma, GFP_KERNEL); if (!mp_req->mp_req_bd) { @@ -1841,7 +1666,7 @@ int qedf_init_mp_req(struct qedf_ioreq *io_req) mp_req_bd = mp_req->mp_req_bd; mp_req_bd->sge_addr.lo = U64_LO(addr); mp_req_bd->sge_addr.hi = U64_HI(addr); - mp_req_bd->size = QEDF_PAGE_SIZE; + mp_req_bd->sge_len = QEDF_PAGE_SIZE; /* * MP buffer is either a task mgmt command or an ELS. @@ -1852,7 +1677,7 @@ int qedf_init_mp_req(struct qedf_ioreq *io_req) addr = mp_req->resp_buf_dma; mp_resp_bd->sge_addr.lo = U64_LO(addr); mp_resp_bd->sge_addr.hi = U64_HI(addr); - mp_resp_bd->size = QEDF_PAGE_SIZE; + mp_resp_bd->sge_len = QEDF_PAGE_SIZE; return 0; } @@ -1895,6 +1720,8 @@ int qedf_initiate_cleanup(struct qedf_ioreq *io_req, int tmo = 0; int rc = SUCCESS; unsigned long flags; + struct fcoe_wqe *sqe; + u16 sqe_idx; fcport = io_req->fcport; if (!fcport) { @@ -1940,12 +1767,16 @@ int qedf_initiate_cleanup(struct qedf_ioreq *io_req, init_completion(&io_req->tm_done); - /* Obtain free SQ entry */ spin_lock_irqsave(&fcport->rport_lock, flags); - qedf_add_to_sq(fcport, xid, 0, FCOE_TASK_TYPE_EXCHANGE_CLEANUP, 0); - /* Ring doorbell */ + sqe_idx = qedf_get_sqe_idx(fcport); + sqe = &fcport->sq[sqe_idx]; + memset(sqe, 0, sizeof(struct fcoe_wqe)); + io_req->task_params->sqe = sqe; + + init_initiator_cleanup_fcoe_task(io_req->task_params); qedf_ring_doorbell(fcport); + spin_unlock_irqrestore(&fcport->rport_lock, flags); tmo = wait_for_completion_timeout(&io_req->tm_done, @@ -1991,16 +1822,15 @@ static int qedf_execute_tmf(struct qedf_rport *fcport, struct scsi_cmnd *sc_cmd, uint8_t tm_flags) { struct qedf_ioreq *io_req; - struct qedf_mp_req *tm_req; struct fcoe_task_context *task; - struct fc_frame_header *fc_hdr; - struct fcp_cmnd *fcp_cmnd; struct qedf_ctx *qedf = fcport->qedf; + struct fc_lport *lport = qedf->lport; int rc = 0; uint16_t xid; - uint32_t sid, did; int tmo = 0; unsigned long flags; + struct fcoe_wqe *sqe; + u16 sqe_idx; if (!sc_cmd) { QEDF_ERR(&(qedf->dbg_ctx), "invalid arg\n"); @@ -2031,36 +1861,14 @@ static int qedf_execute_tmf(struct qedf_rport *fcport, struct scsi_cmnd *sc_cmd, /* Set the return CPU to be the same as the request one */ io_req->cpu = smp_processor_id(); - tm_req = (struct qedf_mp_req *)&(io_req->mp_req); - - rc = qedf_init_mp_req(io_req); - if (rc == FAILED) { - QEDF_ERR(&(qedf->dbg_ctx), "Task mgmt MP request init " - "failed\n"); - kref_put(&io_req->refcount, qedf_release_cmd); - goto reset_tmf_err; - } - /* Set TM flags */ - io_req->io_req_flags = 0; - tm_req->tm_flags = tm_flags; + io_req->io_req_flags = QEDF_READ; + io_req->data_xfer_len = 0; + io_req->tm_flags = tm_flags; /* Default is to return a SCSI command when an error occurs */ io_req->return_scsi_cmd_on_abts = true; - /* Fill FCP_CMND */ - qedf_build_fcp_cmnd(io_req, (struct fcp_cmnd *)tm_req->req_buf); - fcp_cmnd = (struct fcp_cmnd *)tm_req->req_buf; - memset(fcp_cmnd->fc_cdb, 0, FCP_CMND_LEN); - fcp_cmnd->fc_dl = 0; - - /* Fill FC header */ - fc_hdr = &(tm_req->req_fc_hdr); - sid = fcport->sid; - did = fcport->rdata->ids.port_id; - __fc_fill_fc_hdr(fc_hdr, FC_RCTL_DD_UNSOL_CMD, sid, did, - FC_TYPE_FCP, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | - FC_FC_SEQ_INIT, 0); /* Obtain exchange id */ xid = io_req->xid; @@ -2069,16 +1877,18 @@ static int qedf_execute_tmf(struct qedf_rport *fcport, struct scsi_cmnd *sc_cmd, /* Initialize task context for this IO request */ task = qedf_get_task_mem(&qedf->tasks, xid); - qedf_init_mp_task(io_req, task); init_completion(&io_req->tm_done); - /* Obtain free SQ entry */ spin_lock_irqsave(&fcport->rport_lock, flags); - qedf_add_to_sq(fcport, xid, 0, FCOE_TASK_TYPE_MIDPATH, 0); - /* Ring doorbell */ + sqe_idx = qedf_get_sqe_idx(fcport); + sqe = &fcport->sq[sqe_idx]; + memset(sqe, 0, sizeof(struct fcoe_wqe)); + + qedf_init_task(fcport, lport, io_req, task, sqe); qedf_ring_doorbell(fcport); + spin_unlock_irqrestore(&fcport->rport_lock, flags); tmo = wait_for_completion_timeout(&io_req->tm_done, @@ -2162,14 +1972,6 @@ void qedf_process_tmf_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, struct qedf_ioreq *io_req) { struct fcoe_cqe_rsp_info *fcp_rsp; - struct fcoe_cqe_midpath_info *mp_info; - - - /* Get TMF response length from CQE */ - mp_info = &cqe->cqe_info.midpath_info; - io_req->mp_req.resp_len = mp_info->data_placement_size; - QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_SCSI_TM, - "Response len is %d.\n", io_req->mp_req.resp_len); fcp_rsp = &cqe->cqe_info.rsp_info; qedf_parse_fcp_rsp(io_req, fcp_rsp); diff --git a/drivers/scsi/qedi/Makefile b/drivers/scsi/qedi/Makefile index 2b3e16b24299..90a6925577cc 100644 --- a/drivers/scsi/qedi/Makefile +++ b/drivers/scsi/qedi/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_QEDI) := qedi.o qedi-y := qedi_main.o qedi_iscsi.o qedi_fw.o qedi_sysfs.o \ - qedi_dbg.o + qedi_dbg.o qedi_fw_api.o qedi-$(CONFIG_DEBUG_FS) += qedi_debugfs.o diff --git a/drivers/scsi/qedi/qedi_fw.c b/drivers/scsi/qedi/qedi_fw.c index c9f0ef4e11b3..eca40b0513a3 100644 --- a/drivers/scsi/qedi/qedi_fw.c +++ b/drivers/scsi/qedi/qedi_fw.c @@ -14,6 +14,8 @@ #include "qedi.h" #include "qedi_iscsi.h" #include "qedi_gbl.h" +#include "qedi_fw_iscsi.h" +#include "qedi_fw_scsi.h" static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, struct iscsi_task *mtask); @@ -53,8 +55,8 @@ static void qedi_process_logout_resp(struct qedi_ctx *qedi, resp_hdr->exp_cmdsn = cpu_to_be32(cqe_logout_response->exp_cmd_sn); resp_hdr->max_cmdsn = cpu_to_be32(cqe_logout_response->max_cmd_sn); - resp_hdr->t2wait = cpu_to_be32(cqe_logout_response->time2wait); - resp_hdr->t2retain = cpu_to_be32(cqe_logout_response->time2retain); + resp_hdr->t2wait = cpu_to_be32(cqe_logout_response->time_2_wait); + resp_hdr->t2retain = cpu_to_be32(cqe_logout_response->time_2_retain); QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_TID, "Freeing tid=0x%x for cid=0x%x\n", @@ -975,81 +977,6 @@ exit_fp_process: return; } -static void qedi_add_to_sq(struct qedi_conn *qedi_conn, struct iscsi_task *task, - u16 tid, uint16_t ptu_invalidate, int is_cleanup) -{ - struct iscsi_wqe *wqe; - struct iscsi_wqe_field *cont_field; - struct qedi_endpoint *ep; - struct scsi_cmnd *sc = task->sc; - struct iscsi_login_req *login_hdr; - struct qedi_cmd *cmd = task->dd_data; - - login_hdr = (struct iscsi_login_req *)task->hdr; - ep = qedi_conn->ep; - wqe = &ep->sq[ep->sq_prod_idx]; - - memset(wqe, 0, sizeof(*wqe)); - - ep->sq_prod_idx++; - ep->fw_sq_prod_idx++; - if (ep->sq_prod_idx == QEDI_SQ_SIZE) - ep->sq_prod_idx = 0; - - if (is_cleanup) { - SET_FIELD(wqe->flags, ISCSI_WQE_WQE_TYPE, - ISCSI_WQE_TYPE_TASK_CLEANUP); - wqe->task_id = tid; - return; - } - - if (ptu_invalidate) { - SET_FIELD(wqe->flags, ISCSI_WQE_PTU_INVALIDATE, - ISCSI_WQE_SET_PTU_INVALIDATE); - } - - cont_field = &wqe->cont_prevtid_union.cont_field; - - switch (task->hdr->opcode & ISCSI_OPCODE_MASK) { - case ISCSI_OP_LOGIN: - case ISCSI_OP_TEXT: - SET_FIELD(wqe->flags, ISCSI_WQE_WQE_TYPE, - ISCSI_WQE_TYPE_MIDDLE_PATH); - SET_FIELD(wqe->flags, ISCSI_WQE_NUM_FAST_SGES, - 1); - cont_field->contlen_cdbsize_field = ntoh24(login_hdr->dlength); - break; - case ISCSI_OP_LOGOUT: - case ISCSI_OP_NOOP_OUT: - case ISCSI_OP_SCSI_TMFUNC: - SET_FIELD(wqe->flags, ISCSI_WQE_WQE_TYPE, - ISCSI_WQE_TYPE_NORMAL); - break; - default: - if (!sc) - break; - - SET_FIELD(wqe->flags, ISCSI_WQE_WQE_TYPE, - ISCSI_WQE_TYPE_NORMAL); - cont_field->contlen_cdbsize_field = - (sc->sc_data_direction == DMA_TO_DEVICE) ? - scsi_bufflen(sc) : 0; - if (cmd->use_slowpath) - SET_FIELD(wqe->flags, ISCSI_WQE_NUM_FAST_SGES, 0); - else - SET_FIELD(wqe->flags, ISCSI_WQE_NUM_FAST_SGES, - (sc->sc_data_direction == - DMA_TO_DEVICE) ? - min((u16)QEDI_FAST_SGE_COUNT, - (u16)cmd->io_tbl.sge_valid) : 0); - break; - } - - wqe->task_id = tid; - /* Make sure SQ data is coherent */ - wmb(); -} - static void qedi_ring_doorbell(struct qedi_conn *qedi_conn) { struct iscsi_db_data dbell = { 0 }; @@ -1076,96 +1003,116 @@ static void qedi_ring_doorbell(struct qedi_conn *qedi_conn) qedi_conn->iscsi_conn_id); } +static u16 qedi_get_wqe_idx(struct qedi_conn *qedi_conn) +{ + struct qedi_endpoint *ep; + u16 rval; + + ep = qedi_conn->ep; + rval = ep->sq_prod_idx; + + /* Increament SQ index */ + ep->sq_prod_idx++; + ep->fw_sq_prod_idx++; + if (ep->sq_prod_idx == QEDI_SQ_SIZE) + ep->sq_prod_idx = 0; + + return rval; +} + int qedi_send_iscsi_login(struct qedi_conn *qedi_conn, struct iscsi_task *task) { - struct qedi_ctx *qedi = qedi_conn->qedi; + struct iscsi_login_req_hdr login_req_pdu_header; + struct scsi_sgl_task_params tx_sgl_task_params; + struct scsi_sgl_task_params rx_sgl_task_params; + struct iscsi_task_params task_params; struct iscsi_task_context *fw_task_ctx; + struct qedi_ctx *qedi = qedi_conn->qedi; struct iscsi_login_req *login_hdr; - struct iscsi_login_req_hdr *fw_login_req = NULL; - struct iscsi_cached_sge_ctx *cached_sge = NULL; - struct iscsi_sge *single_sge = NULL; - struct iscsi_sge *req_sge = NULL; - struct iscsi_sge *resp_sge = NULL; + struct scsi_sge *req_sge = NULL; + struct scsi_sge *resp_sge = NULL; struct qedi_cmd *qedi_cmd; - s16 ptu_invalidate = 0; + struct qedi_endpoint *ep; s16 tid = 0; + u16 sq_idx = 0; + int rval = 0; - req_sge = (struct iscsi_sge *)qedi_conn->gen_pdu.req_bd_tbl; - resp_sge = (struct iscsi_sge *)qedi_conn->gen_pdu.resp_bd_tbl; + req_sge = (struct scsi_sge *)qedi_conn->gen_pdu.req_bd_tbl; + resp_sge = (struct scsi_sge *)qedi_conn->gen_pdu.resp_bd_tbl; qedi_cmd = (struct qedi_cmd *)task->dd_data; + ep = qedi_conn->ep; login_hdr = (struct iscsi_login_req *)task->hdr; tid = qedi_get_task_idx(qedi); if (tid == -1) return -ENOMEM; - fw_task_ctx = qedi_get_task_mem(&qedi->tasks, tid); + fw_task_ctx = + (struct iscsi_task_context *)qedi_get_task_mem(&qedi->tasks, tid); memset(fw_task_ctx, 0, sizeof(struct iscsi_task_context)); qedi_cmd->task_id = tid; - /* Ystorm context */ - fw_login_req = &fw_task_ctx->ystorm_st_context.pdu_hdr.login_req; - fw_login_req->opcode = login_hdr->opcode; - fw_login_req->version_min = login_hdr->min_version; - fw_login_req->version_max = login_hdr->max_version; - fw_login_req->flags_attr = login_hdr->flags; - fw_login_req->isid_tabc = *((u16 *)login_hdr->isid + 2); - fw_login_req->isid_d = *((u32 *)login_hdr->isid); - fw_login_req->tsih = login_hdr->tsih; - qedi_update_itt_map(qedi, tid, task->itt, qedi_cmd); - fw_login_req->itt = qedi_set_itt(tid, get_itt(task->itt)); - fw_login_req->cid = qedi_conn->iscsi_conn_id; - fw_login_req->cmd_sn = be32_to_cpu(login_hdr->cmdsn); - fw_login_req->exp_stat_sn = be32_to_cpu(login_hdr->exp_statsn); - fw_login_req->exp_stat_sn = 0; - - if (qedi->tid_reuse_count[tid] == QEDI_MAX_TASK_NUM) { - ptu_invalidate = 1; - qedi->tid_reuse_count[tid] = 0; - } + memset(&task_params, 0, sizeof(task_params)); + memset(&login_req_pdu_header, 0, sizeof(login_req_pdu_header)); + memset(&tx_sgl_task_params, 0, sizeof(tx_sgl_task_params)); + memset(&rx_sgl_task_params, 0, sizeof(rx_sgl_task_params)); + /* Update header info */ + login_req_pdu_header.opcode = login_hdr->opcode; + login_req_pdu_header.version_min = login_hdr->min_version; + login_req_pdu_header.version_max = login_hdr->max_version; + login_req_pdu_header.flags_attr = login_hdr->flags; + login_req_pdu_header.isid_tabc = swab32p((u32 *)login_hdr->isid); + login_req_pdu_header.isid_d = swab16p((u16 *)&login_hdr->isid[4]); + + login_req_pdu_header.tsih = login_hdr->tsih; + login_req_pdu_header.hdr_second_dword = ntoh24(login_hdr->dlength); - fw_task_ctx->ystorm_st_context.state.reuse_count = - qedi->tid_reuse_count[tid]; - fw_task_ctx->mstorm_st_context.reuse_count = - qedi->tid_reuse_count[tid]++; - cached_sge = - &fw_task_ctx->ystorm_st_context.state.sgl_ctx_union.cached_sge; - cached_sge->sge.sge_len = req_sge->sge_len; - cached_sge->sge.sge_addr.lo = (u32)(qedi_conn->gen_pdu.req_dma_addr); - cached_sge->sge.sge_addr.hi = - (u32)((u64)qedi_conn->gen_pdu.req_dma_addr >> 32); - - /* Mstorm context */ - single_sge = &fw_task_ctx->mstorm_st_context.sgl_union.single_sge; - fw_task_ctx->mstorm_st_context.task_type = 0x2; - fw_task_ctx->mstorm_ag_context.task_cid = (u16)qedi_conn->iscsi_conn_id; - single_sge->sge_addr.lo = resp_sge->sge_addr.lo; - single_sge->sge_addr.hi = resp_sge->sge_addr.hi; - single_sge->sge_len = resp_sge->sge_len; - - SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags, - ISCSI_MFLAGS_SINGLE_SGE, 1); - SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags, - ISCSI_MFLAGS_SLOW_IO, 0); - fw_task_ctx->mstorm_st_context.sgl_size = 1; - fw_task_ctx->mstorm_st_context.rem_task_size = resp_sge->sge_len; - - /* Ustorm context */ - fw_task_ctx->ustorm_st_context.rem_rcv_len = resp_sge->sge_len; - fw_task_ctx->ustorm_st_context.exp_data_transfer_len = - ntoh24(login_hdr->dlength); - fw_task_ctx->ustorm_st_context.exp_data_sn = 0; - fw_task_ctx->ustorm_st_context.cq_rss_number = 0; - fw_task_ctx->ustorm_st_context.task_type = 0x2; - fw_task_ctx->ustorm_ag_context.icid = (u16)qedi_conn->iscsi_conn_id; - fw_task_ctx->ustorm_ag_context.exp_data_acked = - ntoh24(login_hdr->dlength); - SET_FIELD(fw_task_ctx->ustorm_ag_context.flags1, - USTORM_ISCSI_TASK_AG_CTX_R2T2RECV, 1); - SET_FIELD(fw_task_ctx->ustorm_st_context.flags, - USTORM_ISCSI_TASK_ST_CTX_LOCAL_COMP, 0); + qedi_update_itt_map(qedi, tid, task->itt, qedi_cmd); + login_req_pdu_header.itt = qedi_set_itt(tid, get_itt(task->itt)); + login_req_pdu_header.cid = qedi_conn->iscsi_conn_id; + login_req_pdu_header.cmd_sn = be32_to_cpu(login_hdr->cmdsn); + login_req_pdu_header.exp_stat_sn = be32_to_cpu(login_hdr->exp_statsn); + login_req_pdu_header.exp_stat_sn = 0; + + /* Fill tx AHS and rx buffer */ + tx_sgl_task_params.sgl = + (struct scsi_sge *)qedi_conn->gen_pdu.req_bd_tbl; + tx_sgl_task_params.sgl_phys_addr.lo = + (u32)(qedi_conn->gen_pdu.req_dma_addr); + tx_sgl_task_params.sgl_phys_addr.hi = + (u32)((u64)qedi_conn->gen_pdu.req_dma_addr >> 32); + tx_sgl_task_params.total_buffer_size = ntoh24(login_hdr->dlength); + tx_sgl_task_params.num_sges = 1; + + rx_sgl_task_params.sgl = + (struct scsi_sge *)qedi_conn->gen_pdu.resp_bd_tbl; + rx_sgl_task_params.sgl_phys_addr.lo = + (u32)(qedi_conn->gen_pdu.resp_dma_addr); + rx_sgl_task_params.sgl_phys_addr.hi = + (u32)((u64)qedi_conn->gen_pdu.resp_dma_addr >> 32); + rx_sgl_task_params.total_buffer_size = resp_sge->sge_len; + rx_sgl_task_params.num_sges = 1; + + /* Fill fw input params */ + task_params.context = fw_task_ctx; + task_params.conn_icid = (u16)qedi_conn->iscsi_conn_id; + task_params.itid = tid; + task_params.cq_rss_number = 0; + task_params.tx_io_size = ntoh24(login_hdr->dlength); + task_params.rx_io_size = resp_sge->sge_len; + + sq_idx = qedi_get_wqe_idx(qedi_conn); + task_params.sqe = &ep->sq[sq_idx]; + + memset(task_params.sqe, 0, sizeof(struct iscsi_wqe)); + rval = init_initiator_login_request_task(&task_params, + &login_req_pdu_header, + &tx_sgl_task_params, + &rx_sgl_task_params); + if (rval) + return -1; spin_lock(&qedi_conn->list_lock); list_add_tail(&qedi_cmd->io_cmd, &qedi_conn->active_cmd_list); @@ -1173,7 +1120,6 @@ int qedi_send_iscsi_login(struct qedi_conn *qedi_conn, qedi_conn->active_cmd_count++; spin_unlock(&qedi_conn->list_lock); - qedi_add_to_sq(qedi_conn, task, tid, ptu_invalidate, false); qedi_ring_doorbell(qedi_conn); return 0; } @@ -1181,65 +1127,64 @@ int qedi_send_iscsi_login(struct qedi_conn *qedi_conn, int qedi_send_iscsi_logout(struct qedi_conn *qedi_conn, struct iscsi_task *task) { - struct qedi_ctx *qedi = qedi_conn->qedi; - struct iscsi_logout_req_hdr *fw_logout_req = NULL; - struct iscsi_task_context *fw_task_ctx = NULL; + struct iscsi_logout_req_hdr logout_pdu_header; + struct scsi_sgl_task_params tx_sgl_task_params; + struct scsi_sgl_task_params rx_sgl_task_params; + struct iscsi_task_params task_params; + struct iscsi_task_context *fw_task_ctx; struct iscsi_logout *logout_hdr = NULL; - struct qedi_cmd *qedi_cmd = NULL; - s16 tid = 0; - s16 ptu_invalidate = 0; + struct qedi_ctx *qedi = qedi_conn->qedi; + struct qedi_cmd *qedi_cmd; + struct qedi_endpoint *ep; + s16 tid = 0; + u16 sq_idx = 0; + int rval = 0; qedi_cmd = (struct qedi_cmd *)task->dd_data; logout_hdr = (struct iscsi_logout *)task->hdr; + ep = qedi_conn->ep; tid = qedi_get_task_idx(qedi); if (tid == -1) return -ENOMEM; - fw_task_ctx = qedi_get_task_mem(&qedi->tasks, tid); - + fw_task_ctx = + (struct iscsi_task_context *)qedi_get_task_mem(&qedi->tasks, tid); memset(fw_task_ctx, 0, sizeof(struct iscsi_task_context)); + qedi_cmd->task_id = tid; - /* Ystorm context */ - fw_logout_req = &fw_task_ctx->ystorm_st_context.pdu_hdr.logout_req; - fw_logout_req->opcode = ISCSI_OPCODE_LOGOUT_REQUEST; - fw_logout_req->reason_code = 0x80 | logout_hdr->flags; - qedi_update_itt_map(qedi, tid, task->itt, qedi_cmd); - fw_logout_req->itt = qedi_set_itt(tid, get_itt(task->itt)); - fw_logout_req->exp_stat_sn = be32_to_cpu(logout_hdr->exp_statsn); - fw_logout_req->cmd_sn = be32_to_cpu(logout_hdr->cmdsn); + memset(&task_params, 0, sizeof(task_params)); + memset(&logout_pdu_header, 0, sizeof(logout_pdu_header)); + memset(&tx_sgl_task_params, 0, sizeof(tx_sgl_task_params)); + memset(&rx_sgl_task_params, 0, sizeof(rx_sgl_task_params)); - if (qedi->tid_reuse_count[tid] == QEDI_MAX_TASK_NUM) { - ptu_invalidate = 1; - qedi->tid_reuse_count[tid] = 0; - } - fw_task_ctx->ystorm_st_context.state.reuse_count = - qedi->tid_reuse_count[tid]; - fw_task_ctx->mstorm_st_context.reuse_count = - qedi->tid_reuse_count[tid]++; - fw_logout_req->cid = qedi_conn->iscsi_conn_id; - fw_task_ctx->ystorm_st_context.state.buffer_offset[0] = 0; - - /* Mstorm context */ - fw_task_ctx->mstorm_st_context.task_type = ISCSI_TASK_TYPE_MIDPATH; - fw_task_ctx->mstorm_ag_context.task_cid = (u16)qedi_conn->iscsi_conn_id; - - /* Ustorm context */ - fw_task_ctx->ustorm_st_context.rem_rcv_len = 0; - fw_task_ctx->ustorm_st_context.exp_data_transfer_len = 0; - fw_task_ctx->ustorm_st_context.exp_data_sn = 0; - fw_task_ctx->ustorm_st_context.task_type = ISCSI_TASK_TYPE_MIDPATH; - fw_task_ctx->ustorm_st_context.cq_rss_number = 0; - - SET_FIELD(fw_task_ctx->ustorm_st_context.flags, - USTORM_ISCSI_TASK_ST_CTX_LOCAL_COMP, 0); - SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map, - ISCSI_REG1_NUM_FAST_SGES, 0); - - fw_task_ctx->ustorm_ag_context.icid = (u16)qedi_conn->iscsi_conn_id; - SET_FIELD(fw_task_ctx->ustorm_ag_context.flags1, - USTORM_ISCSI_TASK_AG_CTX_R2T2RECV, 1); + /* Update header info */ + logout_pdu_header.opcode = logout_hdr->opcode; + logout_pdu_header.reason_code = 0x80 | logout_hdr->flags; + qedi_update_itt_map(qedi, tid, task->itt, qedi_cmd); + logout_pdu_header.itt = qedi_set_itt(tid, get_itt(task->itt)); + logout_pdu_header.exp_stat_sn = be32_to_cpu(logout_hdr->exp_statsn); + logout_pdu_header.cmd_sn = be32_to_cpu(logout_hdr->cmdsn); + logout_pdu_header.cid = qedi_conn->iscsi_conn_id; + + /* Fill fw input params */ + task_params.context = fw_task_ctx; + task_params.conn_icid = (u16)qedi_conn->iscsi_conn_id; + task_params.itid = tid; + task_params.cq_rss_number = 0; + task_params.tx_io_size = 0; + task_params.rx_io_size = 0; + + sq_idx = qedi_get_wqe_idx(qedi_conn); + task_params.sqe = &ep->sq[sq_idx]; + memset(task_params.sqe, 0, sizeof(struct iscsi_wqe)); + + rval = init_initiator_logout_request_task(&task_params, + &logout_pdu_header, + NULL, NULL); + if (rval) + return -1; spin_lock(&qedi_conn->list_lock); list_add_tail(&qedi_cmd->io_cmd, &qedi_conn->active_cmd_list); @@ -1247,9 +1192,7 @@ int qedi_send_iscsi_logout(struct qedi_conn *qedi_conn, qedi_conn->active_cmd_count++; spin_unlock(&qedi_conn->list_lock); - qedi_add_to_sq(qedi_conn, task, tid, ptu_invalidate, false); qedi_ring_doorbell(qedi_conn); - return 0; } @@ -1533,47 +1476,46 @@ ldel_exit: static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, struct iscsi_task *mtask) { - struct iscsi_conn *conn = qedi_conn->cls_conn->dd_data; + struct iscsi_tmf_request_hdr tmf_pdu_header; + struct iscsi_task_params task_params; struct qedi_ctx *qedi = qedi_conn->qedi; struct iscsi_task_context *fw_task_ctx; - struct iscsi_tmf_request_hdr *fw_tmf_request; - struct iscsi_sge *single_sge; - struct qedi_cmd *qedi_cmd; - struct qedi_cmd *cmd; + struct iscsi_conn *conn = qedi_conn->cls_conn->dd_data; struct iscsi_task *ctask; struct iscsi_tm *tmf_hdr; - struct iscsi_sge *req_sge; - struct iscsi_sge *resp_sge; - u32 lun[2]; - s16 tid = 0, ptu_invalidate = 0; + struct qedi_cmd *qedi_cmd; + struct qedi_cmd *cmd; + struct qedi_endpoint *ep; + u32 scsi_lun[2]; + s16 tid = 0; + u16 sq_idx = 0; + int rval = 0; - req_sge = (struct iscsi_sge *)qedi_conn->gen_pdu.req_bd_tbl; - resp_sge = (struct iscsi_sge *)qedi_conn->gen_pdu.resp_bd_tbl; - qedi_cmd = (struct qedi_cmd *)mtask->dd_data; tmf_hdr = (struct iscsi_tm *)mtask->hdr; + qedi_cmd = (struct qedi_cmd *)mtask->dd_data; + ep = qedi_conn->ep; - tid = qedi_cmd->task_id; - qedi_update_itt_map(qedi, tid, mtask->itt, qedi_cmd); + tid = qedi_get_task_idx(qedi); + if (tid == -1) + return -ENOMEM; - fw_task_ctx = qedi_get_task_mem(&qedi->tasks, tid); + fw_task_ctx = + (struct iscsi_task_context *)qedi_get_task_mem(&qedi->tasks, tid); memset(fw_task_ctx, 0, sizeof(struct iscsi_task_context)); - fw_tmf_request = &fw_task_ctx->ystorm_st_context.pdu_hdr.tmf_request; - fw_tmf_request->itt = qedi_set_itt(tid, get_itt(mtask->itt)); - fw_tmf_request->cmd_sn = be32_to_cpu(tmf_hdr->cmdsn); + qedi_cmd->task_id = tid; - memcpy(lun, &tmf_hdr->lun, sizeof(struct scsi_lun)); - fw_tmf_request->lun.lo = be32_to_cpu(lun[0]); - fw_tmf_request->lun.hi = be32_to_cpu(lun[1]); + memset(&task_params, 0, sizeof(task_params)); + memset(&tmf_pdu_header, 0, sizeof(tmf_pdu_header)); - if (qedi->tid_reuse_count[tid] == QEDI_MAX_TASK_NUM) { - ptu_invalidate = 1; - qedi->tid_reuse_count[tid] = 0; - } - fw_task_ctx->ystorm_st_context.state.reuse_count = - qedi->tid_reuse_count[tid]; - fw_task_ctx->mstorm_st_context.reuse_count = - qedi->tid_reuse_count[tid]++; + /* Update header info */ + qedi_update_itt_map(qedi, tid, mtask->itt, qedi_cmd); + tmf_pdu_header.itt = qedi_set_itt(tid, get_itt(mtask->itt)); + tmf_pdu_header.cmd_sn = be32_to_cpu(tmf_hdr->cmdsn); + + memcpy(scsi_lun, &tmf_hdr->lun, sizeof(struct scsi_lun)); + tmf_pdu_header.lun.lo = be32_to_cpu(scsi_lun[0]); + tmf_pdu_header.lun.hi = be32_to_cpu(scsi_lun[1]); if ((tmf_hdr->flags & ISCSI_FLAG_TM_FUNC_MASK) == ISCSI_TM_FUNC_ABORT_TASK) { @@ -1584,53 +1526,34 @@ static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, return 0; } cmd = (struct qedi_cmd *)ctask->dd_data; - fw_tmf_request->rtt = + tmf_pdu_header.rtt = qedi_set_itt(cmd->task_id, get_itt(tmf_hdr->rtt)); } else { - fw_tmf_request->rtt = ISCSI_RESERVED_TAG; + tmf_pdu_header.rtt = ISCSI_RESERVED_TAG; } - fw_tmf_request->opcode = tmf_hdr->opcode; - fw_tmf_request->function = tmf_hdr->flags; - fw_tmf_request->hdr_second_dword = ntoh24(tmf_hdr->dlength); - fw_tmf_request->ref_cmd_sn = be32_to_cpu(tmf_hdr->refcmdsn); - - single_sge = &fw_task_ctx->mstorm_st_context.sgl_union.single_sge; - fw_task_ctx->mstorm_st_context.task_type = ISCSI_TASK_TYPE_MIDPATH; - fw_task_ctx->mstorm_ag_context.task_cid = (u16)qedi_conn->iscsi_conn_id; - single_sge->sge_addr.lo = resp_sge->sge_addr.lo; - single_sge->sge_addr.hi = resp_sge->sge_addr.hi; - single_sge->sge_len = resp_sge->sge_len; - - SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags, - ISCSI_MFLAGS_SINGLE_SGE, 1); - SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags, - ISCSI_MFLAGS_SLOW_IO, 0); - fw_task_ctx->mstorm_st_context.sgl_size = 1; - fw_task_ctx->mstorm_st_context.rem_task_size = resp_sge->sge_len; - - /* Ustorm context */ - fw_task_ctx->ustorm_st_context.rem_rcv_len = 0; - fw_task_ctx->ustorm_st_context.exp_data_transfer_len = 0; - fw_task_ctx->ustorm_st_context.exp_data_sn = 0; - fw_task_ctx->ustorm_st_context.task_type = ISCSI_TASK_TYPE_MIDPATH; - fw_task_ctx->ustorm_st_context.cq_rss_number = 0; - - SET_FIELD(fw_task_ctx->ustorm_st_context.flags, - USTORM_ISCSI_TASK_ST_CTX_LOCAL_COMP, 0); - SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map, - ISCSI_REG1_NUM_FAST_SGES, 0); - - fw_task_ctx->ustorm_ag_context.icid = (u16)qedi_conn->iscsi_conn_id; - SET_FIELD(fw_task_ctx->ustorm_ag_context.flags1, - USTORM_ISCSI_TASK_AG_CTX_R2T2RECV, 1); - fw_task_ctx->ustorm_st_context.lun.lo = be32_to_cpu(lun[0]); - fw_task_ctx->ustorm_st_context.lun.hi = be32_to_cpu(lun[1]); + tmf_pdu_header.opcode = tmf_hdr->opcode; + tmf_pdu_header.function = tmf_hdr->flags; + tmf_pdu_header.hdr_second_dword = ntoh24(tmf_hdr->dlength); + tmf_pdu_header.ref_cmd_sn = be32_to_cpu(tmf_hdr->refcmdsn); - QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_SCSI_TM, - "Add TMF to SQ, tmf tid=0x%x, itt=0x%x, cid=0x%x\n", - tid, mtask->itt, qedi_conn->iscsi_conn_id); + /* Fill fw input params */ + task_params.context = fw_task_ctx; + task_params.conn_icid = (u16)qedi_conn->iscsi_conn_id; + task_params.itid = tid; + task_params.cq_rss_number = 0; + task_params.tx_io_size = 0; + task_params.rx_io_size = 0; + + sq_idx = qedi_get_wqe_idx(qedi_conn); + task_params.sqe = &ep->sq[sq_idx]; + + memset(task_params.sqe, 0, sizeof(struct iscsi_wqe)); + rval = init_initiator_tmf_request_task(&task_params, + &tmf_pdu_header); + if (rval) + return -1; spin_lock(&qedi_conn->list_lock); list_add_tail(&qedi_cmd->io_cmd, &qedi_conn->active_cmd_list); @@ -1638,7 +1561,6 @@ static int qedi_send_iscsi_tmf(struct qedi_conn *qedi_conn, qedi_conn->active_cmd_count++; spin_unlock(&qedi_conn->list_lock); - qedi_add_to_sq(qedi_conn, mtask, tid, ptu_invalidate, false); qedi_ring_doorbell(qedi_conn); return 0; } @@ -1689,101 +1611,98 @@ int qedi_iscsi_abort_work(struct qedi_conn *qedi_conn, int qedi_send_iscsi_text(struct qedi_conn *qedi_conn, struct iscsi_task *task) { - struct qedi_ctx *qedi = qedi_conn->qedi; + struct iscsi_text_request_hdr text_request_pdu_header; + struct scsi_sgl_task_params tx_sgl_task_params; + struct scsi_sgl_task_params rx_sgl_task_params; + struct iscsi_task_params task_params; struct iscsi_task_context *fw_task_ctx; - struct iscsi_text_request_hdr *fw_text_request; - struct iscsi_cached_sge_ctx *cached_sge; - struct iscsi_sge *single_sge; - struct qedi_cmd *qedi_cmd; - /* For 6.5 hdr iscsi_hdr */ + struct qedi_ctx *qedi = qedi_conn->qedi; struct iscsi_text *text_hdr; - struct iscsi_sge *req_sge; - struct iscsi_sge *resp_sge; - s16 ptu_invalidate = 0; + struct scsi_sge *req_sge = NULL; + struct scsi_sge *resp_sge = NULL; + struct qedi_cmd *qedi_cmd; + struct qedi_endpoint *ep; s16 tid = 0; + u16 sq_idx = 0; + int rval = 0; - req_sge = (struct iscsi_sge *)qedi_conn->gen_pdu.req_bd_tbl; - resp_sge = (struct iscsi_sge *)qedi_conn->gen_pdu.resp_bd_tbl; + req_sge = (struct scsi_sge *)qedi_conn->gen_pdu.req_bd_tbl; + resp_sge = (struct scsi_sge *)qedi_conn->gen_pdu.resp_bd_tbl; qedi_cmd = (struct qedi_cmd *)task->dd_data; text_hdr = (struct iscsi_text *)task->hdr; + ep = qedi_conn->ep; tid = qedi_get_task_idx(qedi); if (tid == -1) return -ENOMEM; - fw_task_ctx = qedi_get_task_mem(&qedi->tasks, tid); + fw_task_ctx = + (struct iscsi_task_context *)qedi_get_task_mem(&qedi->tasks, tid); memset(fw_task_ctx, 0, sizeof(struct iscsi_task_context)); qedi_cmd->task_id = tid; - /* Ystorm context */ - fw_text_request = - &fw_task_ctx->ystorm_st_context.pdu_hdr.text_request; - fw_text_request->opcode = text_hdr->opcode; - fw_text_request->flags_attr = text_hdr->flags; + memset(&task_params, 0, sizeof(task_params)); + memset(&text_request_pdu_header, 0, sizeof(text_request_pdu_header)); + memset(&tx_sgl_task_params, 0, sizeof(tx_sgl_task_params)); + memset(&rx_sgl_task_params, 0, sizeof(rx_sgl_task_params)); + + /* Update header info */ + text_request_pdu_header.opcode = text_hdr->opcode; + text_request_pdu_header.flags_attr = text_hdr->flags; qedi_update_itt_map(qedi, tid, task->itt, qedi_cmd); - fw_text_request->itt = qedi_set_itt(tid, get_itt(task->itt)); - fw_text_request->ttt = text_hdr->ttt; - fw_text_request->cmd_sn = be32_to_cpu(text_hdr->cmdsn); - fw_text_request->exp_stat_sn = be32_to_cpu(text_hdr->exp_statsn); - fw_text_request->hdr_second_dword = ntoh24(text_hdr->dlength); - - if (qedi->tid_reuse_count[tid] == QEDI_MAX_TASK_NUM) { - ptu_invalidate = 1; - qedi->tid_reuse_count[tid] = 0; - } - fw_task_ctx->ystorm_st_context.state.reuse_count = - qedi->tid_reuse_count[tid]; - fw_task_ctx->mstorm_st_context.reuse_count = - qedi->tid_reuse_count[tid]++; - - cached_sge = - &fw_task_ctx->ystorm_st_context.state.sgl_ctx_union.cached_sge; - cached_sge->sge.sge_len = req_sge->sge_len; - cached_sge->sge.sge_addr.lo = (u32)(qedi_conn->gen_pdu.req_dma_addr); - cached_sge->sge.sge_addr.hi = + text_request_pdu_header.itt = qedi_set_itt(tid, get_itt(task->itt)); + text_request_pdu_header.ttt = text_hdr->ttt; + text_request_pdu_header.cmd_sn = be32_to_cpu(text_hdr->cmdsn); + text_request_pdu_header.exp_stat_sn = be32_to_cpu(text_hdr->exp_statsn); + text_request_pdu_header.hdr_second_dword = ntoh24(text_hdr->dlength); + + /* Fill tx AHS and rx buffer */ + tx_sgl_task_params.sgl = + (struct scsi_sge *)qedi_conn->gen_pdu.req_bd_tbl; + tx_sgl_task_params.sgl_phys_addr.lo = + (u32)(qedi_conn->gen_pdu.req_dma_addr); + tx_sgl_task_params.sgl_phys_addr.hi = (u32)((u64)qedi_conn->gen_pdu.req_dma_addr >> 32); + tx_sgl_task_params.total_buffer_size = req_sge->sge_len; + tx_sgl_task_params.num_sges = 1; + + rx_sgl_task_params.sgl = + (struct scsi_sge *)qedi_conn->gen_pdu.resp_bd_tbl; + rx_sgl_task_params.sgl_phys_addr.lo = + (u32)(qedi_conn->gen_pdu.resp_dma_addr); + rx_sgl_task_params.sgl_phys_addr.hi = + (u32)((u64)qedi_conn->gen_pdu.resp_dma_addr >> 32); + rx_sgl_task_params.total_buffer_size = resp_sge->sge_len; + rx_sgl_task_params.num_sges = 1; + + /* Fill fw input params */ + task_params.context = fw_task_ctx; + task_params.conn_icid = (u16)qedi_conn->iscsi_conn_id; + task_params.itid = tid; + task_params.cq_rss_number = 0; + task_params.tx_io_size = ntoh24(text_hdr->dlength); + task_params.rx_io_size = resp_sge->sge_len; + + sq_idx = qedi_get_wqe_idx(qedi_conn); + task_params.sqe = &ep->sq[sq_idx]; + + memset(task_params.sqe, 0, sizeof(struct iscsi_wqe)); + rval = init_initiator_text_request_task(&task_params, + &text_request_pdu_header, + &tx_sgl_task_params, + &rx_sgl_task_params); + if (rval) + return -1; - /* Mstorm context */ - single_sge = &fw_task_ctx->mstorm_st_context.sgl_union.single_sge; - fw_task_ctx->mstorm_st_context.task_type = 0x2; - fw_task_ctx->mstorm_ag_context.task_cid = (u16)qedi_conn->iscsi_conn_id; - single_sge->sge_addr.lo = resp_sge->sge_addr.lo; - single_sge->sge_addr.hi = resp_sge->sge_addr.hi; - single_sge->sge_len = resp_sge->sge_len; - - SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags, - ISCSI_MFLAGS_SINGLE_SGE, 1); - SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags, - ISCSI_MFLAGS_SLOW_IO, 0); - fw_task_ctx->mstorm_st_context.sgl_size = 1; - fw_task_ctx->mstorm_st_context.rem_task_size = resp_sge->sge_len; - - /* Ustorm context */ - fw_task_ctx->ustorm_ag_context.exp_data_acked = - ntoh24(text_hdr->dlength); - fw_task_ctx->ustorm_st_context.rem_rcv_len = resp_sge->sge_len; - fw_task_ctx->ustorm_st_context.exp_data_transfer_len = - ntoh24(text_hdr->dlength); - fw_task_ctx->ustorm_st_context.exp_data_sn = - be32_to_cpu(text_hdr->exp_statsn); - fw_task_ctx->ustorm_st_context.cq_rss_number = 0; - fw_task_ctx->ustorm_st_context.task_type = 0x2; - fw_task_ctx->ustorm_ag_context.icid = (u16)qedi_conn->iscsi_conn_id; - SET_FIELD(fw_task_ctx->ustorm_ag_context.flags1, - USTORM_ISCSI_TASK_AG_CTX_R2T2RECV, 1); - - /* Add command in active command list */ spin_lock(&qedi_conn->list_lock); list_add_tail(&qedi_cmd->io_cmd, &qedi_conn->active_cmd_list); qedi_cmd->io_cmd_in_list = true; qedi_conn->active_cmd_count++; spin_unlock(&qedi_conn->list_lock); - qedi_add_to_sq(qedi_conn, task, tid, ptu_invalidate, false); qedi_ring_doorbell(qedi_conn); - return 0; } @@ -1791,58 +1710,62 @@ int qedi_send_iscsi_nopout(struct qedi_conn *qedi_conn, struct iscsi_task *task, char *datap, int data_len, int unsol) { + struct iscsi_nop_out_hdr nop_out_pdu_header; + struct scsi_sgl_task_params tx_sgl_task_params; + struct scsi_sgl_task_params rx_sgl_task_params; + struct iscsi_task_params task_params; struct qedi_ctx *qedi = qedi_conn->qedi; struct iscsi_task_context *fw_task_ctx; - struct iscsi_nop_out_hdr *fw_nop_out; - struct qedi_cmd *qedi_cmd; - /* For 6.5 hdr iscsi_hdr */ struct iscsi_nopout *nopout_hdr; - struct iscsi_cached_sge_ctx *cached_sge; - struct iscsi_sge *single_sge; - struct iscsi_sge *req_sge; - struct iscsi_sge *resp_sge; - u32 lun[2]; - s16 ptu_invalidate = 0; + struct scsi_sge *req_sge = NULL; + struct scsi_sge *resp_sge = NULL; + struct qedi_cmd *qedi_cmd; + struct qedi_endpoint *ep; + u32 scsi_lun[2]; s16 tid = 0; + u16 sq_idx = 0; + int rval = 0; - req_sge = (struct iscsi_sge *)qedi_conn->gen_pdu.req_bd_tbl; - resp_sge = (struct iscsi_sge *)qedi_conn->gen_pdu.resp_bd_tbl; + req_sge = (struct scsi_sge *)qedi_conn->gen_pdu.req_bd_tbl; + resp_sge = (struct scsi_sge *)qedi_conn->gen_pdu.resp_bd_tbl; qedi_cmd = (struct qedi_cmd *)task->dd_data; nopout_hdr = (struct iscsi_nopout *)task->hdr; + ep = qedi_conn->ep; tid = qedi_get_task_idx(qedi); - if (tid == -1) { - QEDI_WARN(&qedi->dbg_ctx, "Invalid tid\n"); + if (tid == -1) return -ENOMEM; - } - - fw_task_ctx = qedi_get_task_mem(&qedi->tasks, tid); + fw_task_ctx = + (struct iscsi_task_context *)qedi_get_task_mem(&qedi->tasks, tid); memset(fw_task_ctx, 0, sizeof(struct iscsi_task_context)); + qedi_cmd->task_id = tid; - /* Ystorm context */ - fw_nop_out = &fw_task_ctx->ystorm_st_context.pdu_hdr.nop_out; - SET_FIELD(fw_nop_out->flags_attr, ISCSI_NOP_OUT_HDR_CONST1, 1); - SET_FIELD(fw_nop_out->flags_attr, ISCSI_NOP_OUT_HDR_RSRV, 0); + memset(&task_params, 0, sizeof(task_params)); + memset(&nop_out_pdu_header, 0, sizeof(nop_out_pdu_header)); + memset(&tx_sgl_task_params, 0, sizeof(tx_sgl_task_params)); + memset(&rx_sgl_task_params, 0, sizeof(rx_sgl_task_params)); + + /* Update header info */ + nop_out_pdu_header.opcode = nopout_hdr->opcode; + SET_FIELD(nop_out_pdu_header.flags_attr, ISCSI_NOP_OUT_HDR_CONST1, 1); + SET_FIELD(nop_out_pdu_header.flags_attr, ISCSI_NOP_OUT_HDR_RSRV, 0); - memcpy(lun, &nopout_hdr->lun, sizeof(struct scsi_lun)); - fw_nop_out->lun.lo = be32_to_cpu(lun[0]); - fw_nop_out->lun.hi = be32_to_cpu(lun[1]); + memcpy(scsi_lun, &nopout_hdr->lun, sizeof(struct scsi_lun)); + nop_out_pdu_header.lun.lo = be32_to_cpu(scsi_lun[0]); + nop_out_pdu_header.lun.hi = be32_to_cpu(scsi_lun[1]); + nop_out_pdu_header.cmd_sn = be32_to_cpu(nopout_hdr->cmdsn); + nop_out_pdu_header.exp_stat_sn = be32_to_cpu(nopout_hdr->exp_statsn); qedi_update_itt_map(qedi, tid, task->itt, qedi_cmd); if (nopout_hdr->ttt != ISCSI_TTT_ALL_ONES) { - fw_nop_out->itt = be32_to_cpu(nopout_hdr->itt); - fw_nop_out->ttt = be32_to_cpu(nopout_hdr->ttt); - fw_task_ctx->ystorm_st_context.state.buffer_offset[0] = 0; - fw_task_ctx->ystorm_st_context.state.local_comp = 1; - SET_FIELD(fw_task_ctx->ustorm_st_context.flags, - USTORM_ISCSI_TASK_ST_CTX_LOCAL_COMP, 1); + nop_out_pdu_header.itt = be32_to_cpu(nopout_hdr->itt); + nop_out_pdu_header.ttt = be32_to_cpu(nopout_hdr->ttt); } else { - fw_nop_out->itt = qedi_set_itt(tid, get_itt(task->itt)); - fw_nop_out->ttt = ISCSI_TTT_ALL_ONES; - fw_task_ctx->ystorm_st_context.state.buffer_offset[0] = 0; + nop_out_pdu_header.itt = qedi_set_itt(tid, get_itt(task->itt)); + nop_out_pdu_header.ttt = ISCSI_TTT_ALL_ONES; spin_lock(&qedi_conn->list_lock); list_add_tail(&qedi_cmd->io_cmd, &qedi_conn->active_cmd_list); @@ -1851,53 +1774,46 @@ int qedi_send_iscsi_nopout(struct qedi_conn *qedi_conn, spin_unlock(&qedi_conn->list_lock); } - fw_nop_out->opcode = ISCSI_OPCODE_NOP_OUT; - fw_nop_out->cmd_sn = be32_to_cpu(nopout_hdr->cmdsn); - fw_nop_out->exp_stat_sn = be32_to_cpu(nopout_hdr->exp_statsn); - - cached_sge = - &fw_task_ctx->ystorm_st_context.state.sgl_ctx_union.cached_sge; - cached_sge->sge.sge_len = req_sge->sge_len; - cached_sge->sge.sge_addr.lo = (u32)(qedi_conn->gen_pdu.req_dma_addr); - cached_sge->sge.sge_addr.hi = - (u32)((u64)qedi_conn->gen_pdu.req_dma_addr >> 32); - - /* Mstorm context */ - fw_task_ctx->mstorm_st_context.task_type = ISCSI_TASK_TYPE_MIDPATH; - fw_task_ctx->mstorm_ag_context.task_cid = (u16)qedi_conn->iscsi_conn_id; - - single_sge = &fw_task_ctx->mstorm_st_context.sgl_union.single_sge; - single_sge->sge_addr.lo = resp_sge->sge_addr.lo; - single_sge->sge_addr.hi = resp_sge->sge_addr.hi; - single_sge->sge_len = resp_sge->sge_len; - fw_task_ctx->mstorm_st_context.rem_task_size = resp_sge->sge_len; - - if (qedi->tid_reuse_count[tid] == QEDI_MAX_TASK_NUM) { - ptu_invalidate = 1; - qedi->tid_reuse_count[tid] = 0; - } - fw_task_ctx->ystorm_st_context.state.reuse_count = - qedi->tid_reuse_count[tid]; - fw_task_ctx->mstorm_st_context.reuse_count = - qedi->tid_reuse_count[tid]++; - /* Ustorm context */ - fw_task_ctx->ustorm_st_context.rem_rcv_len = resp_sge->sge_len; - fw_task_ctx->ustorm_st_context.exp_data_transfer_len = data_len; - fw_task_ctx->ustorm_st_context.exp_data_sn = 0; - fw_task_ctx->ustorm_st_context.task_type = ISCSI_TASK_TYPE_MIDPATH; - fw_task_ctx->ustorm_st_context.cq_rss_number = 0; - - SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map, - ISCSI_REG1_NUM_FAST_SGES, 0); - - fw_task_ctx->ustorm_ag_context.icid = (u16)qedi_conn->iscsi_conn_id; - SET_FIELD(fw_task_ctx->ustorm_ag_context.flags1, - USTORM_ISCSI_TASK_AG_CTX_R2T2RECV, 1); - - fw_task_ctx->ustorm_st_context.lun.lo = be32_to_cpu(lun[0]); - fw_task_ctx->ustorm_st_context.lun.hi = be32_to_cpu(lun[1]); - - qedi_add_to_sq(qedi_conn, task, tid, ptu_invalidate, false); + /* Fill tx AHS and rx buffer */ + if (data_len) { + tx_sgl_task_params.sgl = + (struct scsi_sge *)qedi_conn->gen_pdu.req_bd_tbl; + tx_sgl_task_params.sgl_phys_addr.lo = + (u32)(qedi_conn->gen_pdu.req_dma_addr); + tx_sgl_task_params.sgl_phys_addr.hi = + (u32)((u64)qedi_conn->gen_pdu.req_dma_addr >> 32); + tx_sgl_task_params.total_buffer_size = data_len; + tx_sgl_task_params.num_sges = 1; + + rx_sgl_task_params.sgl = + (struct scsi_sge *)qedi_conn->gen_pdu.resp_bd_tbl; + rx_sgl_task_params.sgl_phys_addr.lo = + (u32)(qedi_conn->gen_pdu.resp_dma_addr); + rx_sgl_task_params.sgl_phys_addr.hi = + (u32)((u64)qedi_conn->gen_pdu.resp_dma_addr >> 32); + rx_sgl_task_params.total_buffer_size = resp_sge->sge_len; + rx_sgl_task_params.num_sges = 1; + } + + /* Fill fw input params */ + task_params.context = fw_task_ctx; + task_params.conn_icid = (u16)qedi_conn->iscsi_conn_id; + task_params.itid = tid; + task_params.cq_rss_number = 0; + task_params.tx_io_size = data_len; + task_params.rx_io_size = resp_sge->sge_len; + + sq_idx = qedi_get_wqe_idx(qedi_conn); + task_params.sqe = &ep->sq[sq_idx]; + + memset(task_params.sqe, 0, sizeof(struct iscsi_wqe)); + rval = init_initiator_nop_out_task(&task_params, + &nop_out_pdu_header, + &tx_sgl_task_params, + &rx_sgl_task_params); + if (rval) + return -1; + qedi_ring_doorbell(qedi_conn); return 0; } @@ -1905,7 +1821,7 @@ int qedi_send_iscsi_nopout(struct qedi_conn *qedi_conn, static int qedi_split_bd(struct qedi_cmd *cmd, u64 addr, int sg_len, int bd_index) { - struct iscsi_sge *bd = cmd->io_tbl.sge_tbl; + struct scsi_sge *bd = cmd->io_tbl.sge_tbl; int frag_size, sg_frags; sg_frags = 0; @@ -1938,7 +1854,7 @@ static int qedi_split_bd(struct qedi_cmd *cmd, u64 addr, int sg_len, static int qedi_map_scsi_sg(struct qedi_ctx *qedi, struct qedi_cmd *cmd) { struct scsi_cmnd *sc = cmd->scsi_cmd; - struct iscsi_sge *bd = cmd->io_tbl.sge_tbl; + struct scsi_sge *bd = cmd->io_tbl.sge_tbl; struct scatterlist *sg; int byte_count = 0; int bd_count = 0; @@ -2040,7 +1956,7 @@ static void qedi_iscsi_map_sg_list(struct qedi_cmd *cmd) if (bd_count == 0) return; } else { - struct iscsi_sge *bd = cmd->io_tbl.sge_tbl; + struct scsi_sge *bd = cmd->io_tbl.sge_tbl; bd[0].sge_addr.lo = 0; bd[0].sge_addr.hi = 0; @@ -2136,244 +2052,182 @@ int qedi_iscsi_send_ioreq(struct iscsi_task *task) struct qedi_conn *qedi_conn = conn->dd_data; struct qedi_cmd *cmd = task->dd_data; struct scsi_cmnd *sc = task->sc; + struct iscsi_cmd_hdr cmd_pdu_header; + struct scsi_sgl_task_params tx_sgl_task_params; + struct scsi_sgl_task_params rx_sgl_task_params; + struct scsi_sgl_task_params *prx_sgl = NULL; + struct scsi_sgl_task_params *ptx_sgl = NULL; + struct iscsi_task_params task_params; + struct iscsi_conn_params conn_params; + struct scsi_initiator_cmd_params cmd_params; struct iscsi_task_context *fw_task_ctx; - struct iscsi_cached_sge_ctx *cached_sge; - struct iscsi_phys_sgl_ctx *phys_sgl; - struct iscsi_virt_sgl_ctx *virt_sgl; - struct ystorm_iscsi_task_st_ctx *yst_cxt; - struct mstorm_iscsi_task_st_ctx *mst_cxt; - struct iscsi_sgl *sgl_struct; - struct iscsi_sge *single_sge; + struct iscsi_cls_conn *cls_conn; struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)task->hdr; - struct iscsi_sge *bd = cmd->io_tbl.sge_tbl; - enum iscsi_task_type task_type; - struct iscsi_cmd_hdr *fw_cmd; - u32 lun[2]; - u32 exp_data; - u16 cq_idx = smp_processor_id() % qedi->num_queues; - s16 ptu_invalidate = 0; + enum iscsi_task_type task_type = MAX_ISCSI_TASK_TYPE; + struct qedi_endpoint *ep; + u32 scsi_lun[2]; s16 tid = 0; - u8 num_fast_sgs; + u16 sq_idx = 0; + u16 cq_idx; + int rval = 0; - tid = qedi_get_task_idx(qedi); - if (tid == -1) - return -ENOMEM; + ep = qedi_conn->ep; + cls_conn = qedi_conn->cls_conn; + conn = cls_conn->dd_data; qedi_iscsi_map_sg_list(cmd); + int_to_scsilun(sc->device->lun, (struct scsi_lun *)scsi_lun); - int_to_scsilun(sc->device->lun, (struct scsi_lun *)lun); - fw_task_ctx = qedi_get_task_mem(&qedi->tasks, tid); + tid = qedi_get_task_idx(qedi); + if (tid == -1) + return -ENOMEM; + fw_task_ctx = + (struct iscsi_task_context *)qedi_get_task_mem(&qedi->tasks, tid); memset(fw_task_ctx, 0, sizeof(struct iscsi_task_context)); - cmd->task_id = tid; - /* Ystorm context */ - fw_cmd = &fw_task_ctx->ystorm_st_context.pdu_hdr.cmd; - SET_FIELD(fw_cmd->flags_attr, ISCSI_CMD_HDR_ATTR, ISCSI_ATTR_SIMPLE); + cmd->task_id = tid; + memset(&task_params, 0, sizeof(task_params)); + memset(&cmd_pdu_header, 0, sizeof(cmd_pdu_header)); + memset(&tx_sgl_task_params, 0, sizeof(tx_sgl_task_params)); + memset(&rx_sgl_task_params, 0, sizeof(rx_sgl_task_params)); + memset(&conn_params, 0, sizeof(conn_params)); + memset(&cmd_params, 0, sizeof(cmd_params)); + + cq_idx = smp_processor_id() % qedi->num_queues; + /* Update header info */ + SET_FIELD(cmd_pdu_header.flags_attr, ISCSI_CMD_HDR_ATTR, + ISCSI_ATTR_SIMPLE); if (sc->sc_data_direction == DMA_TO_DEVICE) { - if (conn->session->initial_r2t_en) { - exp_data = min((conn->session->imm_data_en * - conn->max_xmit_dlength), - conn->session->first_burst); - exp_data = min(exp_data, scsi_bufflen(sc)); - fw_task_ctx->ustorm_ag_context.exp_data_acked = - cpu_to_le32(exp_data); - } else { - fw_task_ctx->ustorm_ag_context.exp_data_acked = - min(conn->session->first_burst, scsi_bufflen(sc)); - } - - SET_FIELD(fw_cmd->flags_attr, ISCSI_CMD_HDR_WRITE, 1); + SET_FIELD(cmd_pdu_header.flags_attr, + ISCSI_CMD_HDR_WRITE, 1); task_type = ISCSI_TASK_TYPE_INITIATOR_WRITE; } else { - if (scsi_bufflen(sc)) - SET_FIELD(fw_cmd->flags_attr, ISCSI_CMD_HDR_READ, 1); + SET_FIELD(cmd_pdu_header.flags_attr, + ISCSI_CMD_HDR_READ, 1); task_type = ISCSI_TASK_TYPE_INITIATOR_READ; } - fw_cmd->lun.lo = be32_to_cpu(lun[0]); - fw_cmd->lun.hi = be32_to_cpu(lun[1]); + cmd_pdu_header.lun.lo = be32_to_cpu(scsi_lun[0]); + cmd_pdu_header.lun.hi = be32_to_cpu(scsi_lun[1]); qedi_update_itt_map(qedi, tid, task->itt, cmd); - fw_cmd->itt = qedi_set_itt(tid, get_itt(task->itt)); - fw_cmd->expected_transfer_length = scsi_bufflen(sc); - fw_cmd->cmd_sn = be32_to_cpu(hdr->cmdsn); - fw_cmd->opcode = hdr->opcode; - qedi_cpy_scsi_cdb(sc, (u32 *)fw_cmd->cdb); - - /* Mstorm context */ - fw_task_ctx->mstorm_st_context.sense_db.lo = (u32)cmd->sense_buffer_dma; - fw_task_ctx->mstorm_st_context.sense_db.hi = - (u32)((u64)cmd->sense_buffer_dma >> 32); - fw_task_ctx->mstorm_ag_context.task_cid = qedi_conn->iscsi_conn_id; - fw_task_ctx->mstorm_st_context.task_type = task_type; - - if (qedi->tid_reuse_count[tid] == QEDI_MAX_TASK_NUM) { - ptu_invalidate = 1; - qedi->tid_reuse_count[tid] = 0; - } - fw_task_ctx->ystorm_st_context.state.reuse_count = - qedi->tid_reuse_count[tid]; - fw_task_ctx->mstorm_st_context.reuse_count = - qedi->tid_reuse_count[tid]++; - - /* Ustorm context */ - fw_task_ctx->ustorm_st_context.rem_rcv_len = scsi_bufflen(sc); - fw_task_ctx->ustorm_st_context.exp_data_transfer_len = scsi_bufflen(sc); - fw_task_ctx->ustorm_st_context.exp_data_sn = - be32_to_cpu(hdr->exp_statsn); - fw_task_ctx->ustorm_st_context.task_type = task_type; - fw_task_ctx->ustorm_st_context.cq_rss_number = cq_idx; - fw_task_ctx->ustorm_ag_context.icid = (u16)qedi_conn->iscsi_conn_id; - - SET_FIELD(fw_task_ctx->ustorm_ag_context.flags1, - USTORM_ISCSI_TASK_AG_CTX_R2T2RECV, 1); - SET_FIELD(fw_task_ctx->ustorm_st_context.flags, - USTORM_ISCSI_TASK_ST_CTX_LOCAL_COMP, 0); - - num_fast_sgs = (cmd->io_tbl.sge_valid ? - min((u16)QEDI_FAST_SGE_COUNT, - (u16)cmd->io_tbl.sge_valid) : 0); - SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map, - ISCSI_REG1_NUM_FAST_SGES, num_fast_sgs); - - fw_task_ctx->ustorm_st_context.lun.lo = be32_to_cpu(lun[0]); - fw_task_ctx->ustorm_st_context.lun.hi = be32_to_cpu(lun[1]); - - QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_IO, "Total sge count [%d]\n", - cmd->io_tbl.sge_valid); - - yst_cxt = &fw_task_ctx->ystorm_st_context; - mst_cxt = &fw_task_ctx->mstorm_st_context; - /* Tx path */ + cmd_pdu_header.itt = qedi_set_itt(tid, get_itt(task->itt)); + cmd_pdu_header.expected_transfer_length = cpu_to_be32(hdr->data_length); + cmd_pdu_header.hdr_second_dword = ntoh24(hdr->dlength); + cmd_pdu_header.cmd_sn = be32_to_cpu(hdr->cmdsn); + cmd_pdu_header.opcode = hdr->opcode; + qedi_cpy_scsi_cdb(sc, (u32 *)cmd_pdu_header.cdb); + + /* Fill tx AHS and rx buffer */ if (task_type == ISCSI_TASK_TYPE_INITIATOR_WRITE) { - /* not considering superIO or FastIO */ - if (cmd->io_tbl.sge_valid == 1) { - cached_sge = &yst_cxt->state.sgl_ctx_union.cached_sge; - cached_sge->sge.sge_addr.lo = bd[0].sge_addr.lo; - cached_sge->sge.sge_addr.hi = bd[0].sge_addr.hi; - cached_sge->sge.sge_len = bd[0].sge_len; - qedi->cached_sgls++; - } else if ((cmd->io_tbl.sge_valid != 1) && cmd->use_slowpath) { - SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags, - ISCSI_MFLAGS_SLOW_IO, 1); - SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map, - ISCSI_REG1_NUM_FAST_SGES, 0); - phys_sgl = &yst_cxt->state.sgl_ctx_union.phys_sgl; - phys_sgl->sgl_base.lo = (u32)(cmd->io_tbl.sge_tbl_dma); - phys_sgl->sgl_base.hi = - (u32)((u64)cmd->io_tbl.sge_tbl_dma >> 32); - phys_sgl->sgl_size = cmd->io_tbl.sge_valid; - qedi->slow_sgls++; - } else if ((cmd->io_tbl.sge_valid != 1) && !cmd->use_slowpath) { - SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags, - ISCSI_MFLAGS_SLOW_IO, 0); - SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map, - ISCSI_REG1_NUM_FAST_SGES, - min((u16)QEDI_FAST_SGE_COUNT, - (u16)cmd->io_tbl.sge_valid)); - virt_sgl = &yst_cxt->state.sgl_ctx_union.virt_sgl; - virt_sgl->sgl_base.lo = (u32)(cmd->io_tbl.sge_tbl_dma); - virt_sgl->sgl_base.hi = + tx_sgl_task_params.sgl = cmd->io_tbl.sge_tbl; + tx_sgl_task_params.sgl_phys_addr.lo = + (u32)(cmd->io_tbl.sge_tbl_dma); + tx_sgl_task_params.sgl_phys_addr.hi = (u32)((u64)cmd->io_tbl.sge_tbl_dma >> 32); - virt_sgl->sgl_initial_offset = - (u32)bd[0].sge_addr.lo & (QEDI_PAGE_SIZE - 1); - qedi->fast_sgls++; - } - fw_task_ctx->mstorm_st_context.sgl_size = cmd->io_tbl.sge_valid; - fw_task_ctx->mstorm_st_context.rem_task_size = scsi_bufflen(sc); - } else { - /* Rx path */ - if (cmd->io_tbl.sge_valid == 1) { - SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags, - ISCSI_MFLAGS_SLOW_IO, 0); - SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags, - ISCSI_MFLAGS_SINGLE_SGE, 1); - single_sge = &mst_cxt->sgl_union.single_sge; - single_sge->sge_addr.lo = bd[0].sge_addr.lo; - single_sge->sge_addr.hi = bd[0].sge_addr.hi; - single_sge->sge_len = bd[0].sge_len; - qedi->cached_sgls++; - } else if ((cmd->io_tbl.sge_valid != 1) && cmd->use_slowpath) { - sgl_struct = &mst_cxt->sgl_union.sgl_struct; - sgl_struct->sgl_addr.lo = - (u32)(cmd->io_tbl.sge_tbl_dma); - sgl_struct->sgl_addr.hi = - (u32)((u64)cmd->io_tbl.sge_tbl_dma >> 32); - SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags, - ISCSI_MFLAGS_SLOW_IO, 1); - SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map, - ISCSI_REG1_NUM_FAST_SGES, 0); - sgl_struct->updated_sge_size = 0; - sgl_struct->updated_sge_offset = 0; - qedi->slow_sgls++; - } else if ((cmd->io_tbl.sge_valid != 1) && !cmd->use_slowpath) { - sgl_struct = &mst_cxt->sgl_union.sgl_struct; - sgl_struct->sgl_addr.lo = - (u32)(cmd->io_tbl.sge_tbl_dma); - sgl_struct->sgl_addr.hi = - (u32)((u64)cmd->io_tbl.sge_tbl_dma >> 32); - sgl_struct->byte_offset = - (u32)bd[0].sge_addr.lo & (QEDI_PAGE_SIZE - 1); - SET_FIELD(fw_task_ctx->mstorm_st_context.flags.mflags, - ISCSI_MFLAGS_SLOW_IO, 0); - SET_FIELD(fw_task_ctx->ustorm_st_context.reg1.reg1_map, - ISCSI_REG1_NUM_FAST_SGES, 0); - sgl_struct->updated_sge_size = 0; - sgl_struct->updated_sge_offset = 0; - qedi->fast_sgls++; - } - fw_task_ctx->mstorm_st_context.sgl_size = cmd->io_tbl.sge_valid; - fw_task_ctx->mstorm_st_context.rem_task_size = scsi_bufflen(sc); - } - - if (cmd->io_tbl.sge_valid == 1) - /* Singel-SGL */ - qedi->use_cached_sge = true; - else { + tx_sgl_task_params.total_buffer_size = scsi_bufflen(sc); + tx_sgl_task_params.num_sges = cmd->io_tbl.sge_valid; if (cmd->use_slowpath) - qedi->use_slow_sge = true; - else - qedi->use_fast_sge = true; - } + tx_sgl_task_params.small_mid_sge = true; + } else if (task_type == ISCSI_TASK_TYPE_INITIATOR_READ) { + rx_sgl_task_params.sgl = cmd->io_tbl.sge_tbl; + rx_sgl_task_params.sgl_phys_addr.lo = + (u32)(cmd->io_tbl.sge_tbl_dma); + rx_sgl_task_params.sgl_phys_addr.hi = + (u32)((u64)cmd->io_tbl.sge_tbl_dma >> 32); + rx_sgl_task_params.total_buffer_size = scsi_bufflen(sc); + rx_sgl_task_params.num_sges = cmd->io_tbl.sge_valid; + } + + /* Add conn param */ + conn_params.first_burst_length = conn->session->first_burst; + conn_params.max_send_pdu_length = conn->max_xmit_dlength; + conn_params.max_burst_length = conn->session->max_burst; + if (conn->session->initial_r2t_en) + conn_params.initial_r2t = true; + if (conn->session->imm_data_en) + conn_params.immediate_data = true; + + /* Add cmd params */ + cmd_params.sense_data_buffer_phys_addr.lo = (u32)cmd->sense_buffer_dma; + cmd_params.sense_data_buffer_phys_addr.hi = + (u32)((u64)cmd->sense_buffer_dma >> 32); + /* Fill fw input params */ + task_params.context = fw_task_ctx; + task_params.conn_icid = (u16)qedi_conn->iscsi_conn_id; + task_params.itid = tid; + task_params.cq_rss_number = cq_idx; + if (task_type == ISCSI_TASK_TYPE_INITIATOR_WRITE) + task_params.tx_io_size = scsi_bufflen(sc); + else if (task_type == ISCSI_TASK_TYPE_INITIATOR_READ) + task_params.rx_io_size = scsi_bufflen(sc); + + sq_idx = qedi_get_wqe_idx(qedi_conn); + task_params.sqe = &ep->sq[sq_idx]; + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_IO, - "%s: %s-SGL: num_sges=0x%x first-sge-lo=0x%x first-sge-hi=0x%x", + "%s: %s-SGL: sg_len=0x%x num_sges=0x%x first-sge-lo=0x%x first-sge-hi=0x%x\n", (task_type == ISCSI_TASK_TYPE_INITIATOR_WRITE) ? "Write " : "Read ", (cmd->io_tbl.sge_valid == 1) ? "Single" : (cmd->use_slowpath ? "SLOW" : "FAST"), - (u16)cmd->io_tbl.sge_valid, (u32)(cmd->io_tbl.sge_tbl_dma), + (u16)cmd->io_tbl.sge_valid, scsi_bufflen(sc), + (u32)(cmd->io_tbl.sge_tbl_dma), (u32)((u64)cmd->io_tbl.sge_tbl_dma >> 32)); - /* Add command in active command list */ + memset(task_params.sqe, 0, sizeof(struct iscsi_wqe)); + + if (task_params.tx_io_size != 0) + ptx_sgl = &tx_sgl_task_params; + if (task_params.rx_io_size != 0) + prx_sgl = &rx_sgl_task_params; + + rval = init_initiator_rw_iscsi_task(&task_params, &conn_params, + &cmd_params, &cmd_pdu_header, + ptx_sgl, prx_sgl, + NULL); + if (rval) + return -1; + spin_lock(&qedi_conn->list_lock); list_add_tail(&cmd->io_cmd, &qedi_conn->active_cmd_list); cmd->io_cmd_in_list = true; qedi_conn->active_cmd_count++; spin_unlock(&qedi_conn->list_lock); - qedi_add_to_sq(qedi_conn, task, tid, ptu_invalidate, false); qedi_ring_doorbell(qedi_conn); - if (qedi_io_tracing) - qedi_trace_io(qedi, task, tid, QEDI_IO_TRACE_REQ); - return 0; } int qedi_iscsi_cleanup_task(struct iscsi_task *task, bool mark_cmd_node_deleted) { + struct iscsi_task_params task_params; + struct qedi_endpoint *ep; struct iscsi_conn *conn = task->conn; struct qedi_conn *qedi_conn = conn->dd_data; struct qedi_cmd *cmd = task->dd_data; - s16 ptu_invalidate = 0; + u16 sq_idx = 0; + int rval = 0; QEDI_INFO(&qedi_conn->qedi->dbg_ctx, QEDI_LOG_SCSI_TM, "issue cleanup tid=0x%x itt=0x%x task_state=%d cmd_state=0%x cid=0x%x\n", cmd->task_id, get_itt(task->itt), task->state, cmd->state, qedi_conn->iscsi_conn_id); - qedi_add_to_sq(qedi_conn, task, cmd->task_id, ptu_invalidate, true); - qedi_ring_doorbell(qedi_conn); + memset(&task_params, 0, sizeof(task_params)); + ep = qedi_conn->ep; + + sq_idx = qedi_get_wqe_idx(qedi_conn); + + task_params.sqe = &ep->sq[sq_idx]; + memset(task_params.sqe, 0, sizeof(struct iscsi_wqe)); + task_params.itid = cmd->task_id; + rval = init_cleanup_task(&task_params); + if (rval) + return rval; + + qedi_ring_doorbell(qedi_conn); return 0; } diff --git a/drivers/scsi/qedi/qedi_fw_api.c b/drivers/scsi/qedi/qedi_fw_api.c new file mode 100644 index 000000000000..fd354d4e03eb --- /dev/null +++ b/drivers/scsi/qedi/qedi_fw_api.c @@ -0,0 +1,781 @@ +/* QLogic iSCSI Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#include +#include +#include "qedi_hsi.h" +#include + +#include "qedi_fw_iscsi.h" +#include "qedi_fw_scsi.h" + +#define SCSI_NUM_SGES_IN_CACHE 0x4 + +static bool scsi_is_slow_sgl(u16 num_sges, bool small_mid_sge) +{ + return (num_sges > SCSI_NUM_SGES_SLOW_SGL_THR && small_mid_sge); +} + +static +void init_scsi_sgl_context(struct scsi_sgl_params *ctx_sgl_params, + struct scsi_cached_sges *ctx_data_desc, + struct scsi_sgl_task_params *sgl_task_params) +{ + u8 sge_index; + u8 num_sges; + u32 val; + + num_sges = (sgl_task_params->num_sges > SCSI_NUM_SGES_IN_CACHE) ? + SCSI_NUM_SGES_IN_CACHE : sgl_task_params->num_sges; + + /* sgl params */ + val = cpu_to_le32(sgl_task_params->sgl_phys_addr.lo); + ctx_sgl_params->sgl_addr.lo = val; + val = cpu_to_le32(sgl_task_params->sgl_phys_addr.hi); + ctx_sgl_params->sgl_addr.hi = val; + val = cpu_to_le32(sgl_task_params->total_buffer_size); + ctx_sgl_params->sgl_total_length = val; + ctx_sgl_params->sgl_num_sges = cpu_to_le16(sgl_task_params->num_sges); + + for (sge_index = 0; sge_index < num_sges; sge_index++) { + val = cpu_to_le32(sgl_task_params->sgl[sge_index].sge_addr.lo); + ctx_data_desc->sge[sge_index].sge_addr.lo = val; + val = cpu_to_le32(sgl_task_params->sgl[sge_index].sge_addr.hi); + ctx_data_desc->sge[sge_index].sge_addr.hi = val; + val = cpu_to_le32(sgl_task_params->sgl[sge_index].sge_len); + ctx_data_desc->sge[sge_index].sge_len = val; + } +} + +static u32 calc_rw_task_size(struct iscsi_task_params *task_params, + enum iscsi_task_type task_type, + struct scsi_sgl_task_params *sgl_task_params, + struct scsi_dif_task_params *dif_task_params) +{ + u32 io_size; + + if (task_type == ISCSI_TASK_TYPE_INITIATOR_WRITE || + task_type == ISCSI_TASK_TYPE_TARGET_READ) + io_size = task_params->tx_io_size; + else + io_size = task_params->rx_io_size; + + if (!io_size) + return 0; + + if (!dif_task_params) + return io_size; + + return !dif_task_params->dif_on_network ? + io_size : sgl_task_params->total_buffer_size; +} + +static void +init_dif_context_flags(struct iscsi_dif_flags *ctx_dif_flags, + struct scsi_dif_task_params *dif_task_params) +{ + if (!dif_task_params) + return; + + SET_FIELD(ctx_dif_flags->flags, ISCSI_DIF_FLAGS_PROT_INTERVAL_SIZE_LOG, + dif_task_params->dif_block_size_log); + SET_FIELD(ctx_dif_flags->flags, ISCSI_DIF_FLAGS_DIF_TO_PEER, + dif_task_params->dif_on_network ? 1 : 0); + SET_FIELD(ctx_dif_flags->flags, ISCSI_DIF_FLAGS_HOST_INTERFACE, + dif_task_params->dif_on_host ? 1 : 0); +} + +static void init_sqe(struct iscsi_task_params *task_params, + struct scsi_sgl_task_params *sgl_task_params, + struct scsi_dif_task_params *dif_task_params, + struct iscsi_common_hdr *pdu_header, + struct scsi_initiator_cmd_params *cmd_params, + enum iscsi_task_type task_type, + bool is_cleanup) +{ + if (!task_params->sqe) + return; + + memset(task_params->sqe, 0, sizeof(*task_params->sqe)); + task_params->sqe->task_id = cpu_to_le16(task_params->itid); + if (is_cleanup) { + SET_FIELD(task_params->sqe->flags, ISCSI_WQE_WQE_TYPE, + ISCSI_WQE_TYPE_TASK_CLEANUP); + return; + } + + switch (task_type) { + case ISCSI_TASK_TYPE_INITIATOR_WRITE: + { + u32 buf_size = 0; + u32 num_sges = 0; + + init_dif_context_flags(&task_params->sqe->prot_flags, + dif_task_params); + + SET_FIELD(task_params->sqe->flags, ISCSI_WQE_WQE_TYPE, + ISCSI_WQE_TYPE_NORMAL); + + if (task_params->tx_io_size) { + buf_size = calc_rw_task_size(task_params, task_type, + sgl_task_params, + dif_task_params); + + if (scsi_is_slow_sgl(sgl_task_params->num_sges, + sgl_task_params->small_mid_sge)) + num_sges = ISCSI_WQE_NUM_SGES_SLOWIO; + else + num_sges = min(sgl_task_params->num_sges, + (u16)SCSI_NUM_SGES_SLOW_SGL_THR); + } + + SET_FIELD(task_params->sqe->flags, ISCSI_WQE_NUM_SGES, num_sges); + SET_FIELD(task_params->sqe->contlen_cdbsize, ISCSI_WQE_CONT_LEN, + buf_size); + + if (GET_FIELD(pdu_header->hdr_second_dword, + ISCSI_CMD_HDR_TOTAL_AHS_LEN)) + SET_FIELD(task_params->sqe->contlen_cdbsize, ISCSI_WQE_CDB_SIZE, + cmd_params->extended_cdb_sge.sge_len); + } + break; + case ISCSI_TASK_TYPE_INITIATOR_READ: + SET_FIELD(task_params->sqe->flags, ISCSI_WQE_WQE_TYPE, + ISCSI_WQE_TYPE_NORMAL); + + if (GET_FIELD(pdu_header->hdr_second_dword, + ISCSI_CMD_HDR_TOTAL_AHS_LEN)) + SET_FIELD(task_params->sqe->contlen_cdbsize, + ISCSI_WQE_CDB_SIZE, + cmd_params->extended_cdb_sge.sge_len); + break; + case ISCSI_TASK_TYPE_LOGIN_RESPONSE: + case ISCSI_TASK_TYPE_MIDPATH: + { + bool advance_statsn = true; + + if (task_type == ISCSI_TASK_TYPE_LOGIN_RESPONSE) + SET_FIELD(task_params->sqe->flags, ISCSI_WQE_WQE_TYPE, + ISCSI_WQE_TYPE_LOGIN); + else + SET_FIELD(task_params->sqe->flags, ISCSI_WQE_WQE_TYPE, + ISCSI_WQE_TYPE_MIDDLE_PATH); + + if (task_type == ISCSI_TASK_TYPE_MIDPATH) { + u8 opcode = GET_FIELD(pdu_header->hdr_first_byte, + ISCSI_COMMON_HDR_OPCODE); + + if (opcode != ISCSI_OPCODE_TEXT_RESPONSE && + (opcode != ISCSI_OPCODE_NOP_IN || + pdu_header->itt == ISCSI_TTT_ALL_ONES)) + advance_statsn = false; + } + + SET_FIELD(task_params->sqe->flags, ISCSI_WQE_RESPONSE, + advance_statsn ? 1 : 0); + + if (task_params->tx_io_size) { + SET_FIELD(task_params->sqe->contlen_cdbsize, + ISCSI_WQE_CONT_LEN, task_params->tx_io_size); + + if (scsi_is_slow_sgl(sgl_task_params->num_sges, + sgl_task_params->small_mid_sge)) + SET_FIELD(task_params->sqe->flags, ISCSI_WQE_NUM_SGES, + ISCSI_WQE_NUM_SGES_SLOWIO); + else + SET_FIELD(task_params->sqe->flags, ISCSI_WQE_NUM_SGES, + min(sgl_task_params->num_sges, + (u16)SCSI_NUM_SGES_SLOW_SGL_THR)); + } + } + break; + default: + break; + } +} + +static void init_default_iscsi_task(struct iscsi_task_params *task_params, + struct data_hdr *pdu_header, + enum iscsi_task_type task_type) +{ + struct iscsi_task_context *context; + u16 index; + u32 val; + + context = task_params->context; + memset(context, 0, sizeof(*context)); + + for (index = 0; index < + ARRAY_SIZE(context->ystorm_st_context.pdu_hdr.data.data); + index++) { + val = cpu_to_le32(pdu_header->data[index]); + context->ystorm_st_context.pdu_hdr.data.data[index] = val; + } + + context->mstorm_st_context.task_type = task_type; + context->mstorm_ag_context.task_cid = + cpu_to_le16(task_params->conn_icid); + + SET_FIELD(context->ustorm_ag_context.flags1, + USTORM_ISCSI_TASK_AG_CTX_R2T2RECV, 1); + + context->ustorm_st_context.task_type = task_type; + context->ustorm_st_context.cq_rss_number = task_params->cq_rss_number; + context->ustorm_ag_context.icid = cpu_to_le16(task_params->conn_icid); +} + +static +void init_initiator_rw_cdb_ystorm_context(struct ystorm_iscsi_task_st_ctx *ystc, + struct scsi_initiator_cmd_params *cmd) +{ + union iscsi_task_hdr *ctx_pdu_hdr = &ystc->pdu_hdr; + u32 val; + + if (!cmd->extended_cdb_sge.sge_len) + return; + + SET_FIELD(ctx_pdu_hdr->ext_cdb_cmd.hdr_second_dword, + ISCSI_EXT_CDB_CMD_HDR_CDB_SIZE, + cmd->extended_cdb_sge.sge_len); + val = cpu_to_le32(cmd->extended_cdb_sge.sge_addr.lo); + ctx_pdu_hdr->ext_cdb_cmd.cdb_sge.sge_addr.lo = val; + val = cpu_to_le32(cmd->extended_cdb_sge.sge_addr.hi); + ctx_pdu_hdr->ext_cdb_cmd.cdb_sge.sge_addr.hi = val; + val = cpu_to_le32(cmd->extended_cdb_sge.sge_len); + ctx_pdu_hdr->ext_cdb_cmd.cdb_sge.sge_len = val; +} + +static +void init_ustorm_task_contexts(struct ustorm_iscsi_task_st_ctx *ustorm_st_cxt, + struct ustorm_iscsi_task_ag_ctx *ustorm_ag_cxt, + u32 remaining_recv_len, + u32 expected_data_transfer_len, + u8 num_sges, bool tx_dif_conn_err_en) +{ + u32 val; + + ustorm_st_cxt->rem_rcv_len = cpu_to_le32(remaining_recv_len); + ustorm_ag_cxt->exp_data_acked = cpu_to_le32(expected_data_transfer_len); + val = cpu_to_le32(expected_data_transfer_len); + ustorm_st_cxt->exp_data_transfer_len = val; + SET_FIELD(ustorm_st_cxt->reg1.reg1_map, ISCSI_REG1_NUM_SGES, num_sges); + SET_FIELD(ustorm_ag_cxt->flags2, + USTORM_ISCSI_TASK_AG_CTX_DIF_ERROR_CF_EN, + tx_dif_conn_err_en ? 1 : 0); +} + +static +void set_rw_exp_data_acked_and_cont_len(struct iscsi_task_context *context, + struct iscsi_conn_params *conn_params, + enum iscsi_task_type task_type, + u32 task_size, + u32 exp_data_transfer_len, + u8 total_ahs_length) +{ + u32 max_unsolicited_data = 0, val; + + if (total_ahs_length && + (task_type == ISCSI_TASK_TYPE_INITIATOR_WRITE || + task_type == ISCSI_TASK_TYPE_INITIATOR_READ)) + SET_FIELD(context->ustorm_st_context.flags2, + USTORM_ISCSI_TASK_ST_CTX_AHS_EXIST, 1); + + switch (task_type) { + case ISCSI_TASK_TYPE_INITIATOR_WRITE: + if (!conn_params->initial_r2t) + max_unsolicited_data = conn_params->first_burst_length; + else if (conn_params->immediate_data) + max_unsolicited_data = + min(conn_params->first_burst_length, + conn_params->max_send_pdu_length); + + context->ustorm_ag_context.exp_data_acked = + cpu_to_le32(total_ahs_length == 0 ? + min(exp_data_transfer_len, + max_unsolicited_data) : + ((u32)(total_ahs_length + + ISCSI_AHS_CNTL_SIZE))); + break; + case ISCSI_TASK_TYPE_TARGET_READ: + val = cpu_to_le32(exp_data_transfer_len); + context->ustorm_ag_context.exp_data_acked = val; + break; + case ISCSI_TASK_TYPE_INITIATOR_READ: + context->ustorm_ag_context.exp_data_acked = + cpu_to_le32((total_ahs_length == 0 ? 0 : + total_ahs_length + + ISCSI_AHS_CNTL_SIZE)); + break; + case ISCSI_TASK_TYPE_TARGET_WRITE: + val = cpu_to_le32(task_size); + context->ustorm_ag_context.exp_cont_len = val; + break; + default: + break; + } +} + +static +void init_rtdif_task_context(struct rdif_task_context *rdif_context, + struct tdif_task_context *tdif_context, + struct scsi_dif_task_params *dif_task_params, + enum iscsi_task_type task_type) +{ + u32 val; + + if (!dif_task_params->dif_on_network || !dif_task_params->dif_on_host) + return; + + if (task_type == ISCSI_TASK_TYPE_TARGET_WRITE || + task_type == ISCSI_TASK_TYPE_INITIATOR_READ) { + rdif_context->app_tag_value = + cpu_to_le16(dif_task_params->application_tag); + rdif_context->partial_crc_value = cpu_to_le16(0xffff); + val = cpu_to_le32(dif_task_params->initial_ref_tag); + rdif_context->initial_ref_tag = val; + rdif_context->app_tag_mask = + cpu_to_le16(dif_task_params->application_tag_mask); + SET_FIELD(rdif_context->flags0, RDIF_TASK_CONTEXT_CRC_SEED, + dif_task_params->crc_seed ? 1 : 0); + SET_FIELD(rdif_context->flags0, RDIF_TASK_CONTEXT_HOSTGUARDTYPE, + dif_task_params->host_guard_type); + SET_FIELD(rdif_context->flags0, + RDIF_TASK_CONTEXT_PROTECTIONTYPE, + dif_task_params->protection_type); + SET_FIELD(rdif_context->flags0, + RDIF_TASK_CONTEXT_INITIALREFTAGVALID, 1); + SET_FIELD(rdif_context->flags0, + RDIF_TASK_CONTEXT_KEEPREFTAGCONST, + dif_task_params->keep_ref_tag_const ? 1 : 0); + SET_FIELD(rdif_context->flags1, + RDIF_TASK_CONTEXT_VALIDATEAPPTAG, + (dif_task_params->validate_app_tag && + dif_task_params->dif_on_network) ? 1 : 0); + SET_FIELD(rdif_context->flags1, + RDIF_TASK_CONTEXT_VALIDATEGUARD, + (dif_task_params->validate_guard && + dif_task_params->dif_on_network) ? 1 : 0); + SET_FIELD(rdif_context->flags1, + RDIF_TASK_CONTEXT_VALIDATEREFTAG, + (dif_task_params->validate_ref_tag && + dif_task_params->dif_on_network) ? 1 : 0); + SET_FIELD(rdif_context->flags1, + RDIF_TASK_CONTEXT_HOSTINTERFACE, + dif_task_params->dif_on_host ? 1 : 0); + SET_FIELD(rdif_context->flags1, + RDIF_TASK_CONTEXT_NETWORKINTERFACE, + dif_task_params->dif_on_network ? 1 : 0); + SET_FIELD(rdif_context->flags1, + RDIF_TASK_CONTEXT_FORWARDGUARD, + dif_task_params->forward_guard ? 1 : 0); + SET_FIELD(rdif_context->flags1, + RDIF_TASK_CONTEXT_FORWARDAPPTAG, + dif_task_params->forward_app_tag ? 1 : 0); + SET_FIELD(rdif_context->flags1, + RDIF_TASK_CONTEXT_FORWARDREFTAG, + dif_task_params->forward_ref_tag ? 1 : 0); + SET_FIELD(rdif_context->flags1, + RDIF_TASK_CONTEXT_FORWARDAPPTAGWITHMASK, + dif_task_params->forward_app_tag_with_mask ? 1 : 0); + SET_FIELD(rdif_context->flags1, + RDIF_TASK_CONTEXT_FORWARDREFTAGWITHMASK, + dif_task_params->forward_ref_tag_with_mask ? 1 : 0); + SET_FIELD(rdif_context->flags1, + RDIF_TASK_CONTEXT_INTERVALSIZE, + dif_task_params->dif_block_size_log - 9); + SET_FIELD(rdif_context->state, + RDIF_TASK_CONTEXT_REFTAGMASK, + dif_task_params->ref_tag_mask); + SET_FIELD(rdif_context->state, RDIF_TASK_CONTEXT_IGNOREAPPTAG, + dif_task_params->ignore_app_tag); + } + + if (task_type == ISCSI_TASK_TYPE_TARGET_READ || + task_type == ISCSI_TASK_TYPE_INITIATOR_WRITE) { + tdif_context->app_tag_value = + cpu_to_le16(dif_task_params->application_tag); + tdif_context->partial_crc_valueB = + cpu_to_le16(dif_task_params->crc_seed ? 0xffff : 0x0000); + tdif_context->partial_crc_value_a = + cpu_to_le16(dif_task_params->crc_seed ? 0xffff : 0x0000); + SET_FIELD(tdif_context->flags0, TDIF_TASK_CONTEXT_CRC_SEED, + dif_task_params->crc_seed ? 1 : 0); + + SET_FIELD(tdif_context->flags0, + TDIF_TASK_CONTEXT_SETERRORWITHEOP, + dif_task_params->tx_dif_conn_err_en ? 1 : 0); + SET_FIELD(tdif_context->flags1, TDIF_TASK_CONTEXT_FORWARDGUARD, + dif_task_params->forward_guard ? 1 : 0); + SET_FIELD(tdif_context->flags1, TDIF_TASK_CONTEXT_FORWARDAPPTAG, + dif_task_params->forward_app_tag ? 1 : 0); + SET_FIELD(tdif_context->flags1, TDIF_TASK_CONTEXT_FORWARDREFTAG, + dif_task_params->forward_ref_tag ? 1 : 0); + SET_FIELD(tdif_context->flags1, TDIF_TASK_CONTEXT_INTERVALSIZE, + dif_task_params->dif_block_size_log - 9); + SET_FIELD(tdif_context->flags1, TDIF_TASK_CONTEXT_HOSTINTERFACE, + dif_task_params->dif_on_host ? 1 : 0); + SET_FIELD(tdif_context->flags1, + TDIF_TASK_CONTEXT_NETWORKINTERFACE, + dif_task_params->dif_on_network ? 1 : 0); + val = cpu_to_le32(dif_task_params->initial_ref_tag); + tdif_context->initial_ref_tag = val; + tdif_context->app_tag_mask = + cpu_to_le16(dif_task_params->application_tag_mask); + SET_FIELD(tdif_context->flags0, + TDIF_TASK_CONTEXT_HOSTGUARDTYPE, + dif_task_params->host_guard_type); + SET_FIELD(tdif_context->flags0, + TDIF_TASK_CONTEXT_PROTECTIONTYPE, + dif_task_params->protection_type); + SET_FIELD(tdif_context->flags0, + TDIF_TASK_CONTEXT_INITIALREFTAGVALID, + dif_task_params->initial_ref_tag_is_valid ? 1 : 0); + SET_FIELD(tdif_context->flags0, + TDIF_TASK_CONTEXT_KEEPREFTAGCONST, + dif_task_params->keep_ref_tag_const ? 1 : 0); + SET_FIELD(tdif_context->flags1, TDIF_TASK_CONTEXT_VALIDATEGUARD, + (dif_task_params->validate_guard && + dif_task_params->dif_on_host) ? 1 : 0); + SET_FIELD(tdif_context->flags1, + TDIF_TASK_CONTEXT_VALIDATEAPPTAG, + (dif_task_params->validate_app_tag && + dif_task_params->dif_on_host) ? 1 : 0); + SET_FIELD(tdif_context->flags1, + TDIF_TASK_CONTEXT_VALIDATEREFTAG, + (dif_task_params->validate_ref_tag && + dif_task_params->dif_on_host) ? 1 : 0); + SET_FIELD(tdif_context->flags1, + TDIF_TASK_CONTEXT_FORWARDAPPTAGWITHMASK, + dif_task_params->forward_app_tag_with_mask ? 1 : 0); + SET_FIELD(tdif_context->flags1, + TDIF_TASK_CONTEXT_FORWARDREFTAGWITHMASK, + dif_task_params->forward_ref_tag_with_mask ? 1 : 0); + SET_FIELD(tdif_context->flags1, + TDIF_TASK_CONTEXT_REFTAGMASK, + dif_task_params->ref_tag_mask); + SET_FIELD(tdif_context->flags0, + TDIF_TASK_CONTEXT_IGNOREAPPTAG, + dif_task_params->ignore_app_tag ? 1 : 0); + } +} + +static void set_local_completion_context(struct iscsi_task_context *context) +{ + SET_FIELD(context->ystorm_st_context.state.flags, + YSTORM_ISCSI_TASK_STATE_LOCAL_COMP, 1); + SET_FIELD(context->ustorm_st_context.flags, + USTORM_ISCSI_TASK_ST_CTX_LOCAL_COMP, 1); +} + +static int init_rw_iscsi_task(struct iscsi_task_params *task_params, + enum iscsi_task_type task_type, + struct iscsi_conn_params *conn_params, + struct iscsi_common_hdr *pdu_header, + struct scsi_sgl_task_params *sgl_task_params, + struct scsi_initiator_cmd_params *cmd_params, + struct scsi_dif_task_params *dif_task_params) +{ + u32 exp_data_transfer_len = conn_params->max_burst_length; + struct iscsi_task_context *cxt; + bool slow_io = false; + u32 task_size, val; + u8 num_sges = 0; + + task_size = calc_rw_task_size(task_params, task_type, sgl_task_params, + dif_task_params); + + init_default_iscsi_task(task_params, (struct data_hdr *)pdu_header, + task_type); + + cxt = task_params->context; + + val = cpu_to_le32(task_size); + cxt->ystorm_st_context.pdu_hdr.cmd.expected_transfer_length = val; + init_initiator_rw_cdb_ystorm_context(&cxt->ystorm_st_context, + cmd_params); + val = cpu_to_le32(cmd_params->sense_data_buffer_phys_addr.lo); + cxt->mstorm_st_context.sense_db.lo = val; + + val = cpu_to_le32(cmd_params->sense_data_buffer_phys_addr.hi); + cxt->mstorm_st_context.sense_db.hi = val; + + if (task_params->tx_io_size) { + init_dif_context_flags(&cxt->ystorm_st_context.state.dif_flags, + dif_task_params); + init_scsi_sgl_context(&cxt->ystorm_st_context.state.sgl_params, + &cxt->ystorm_st_context.state.data_desc, + sgl_task_params); + + slow_io = scsi_is_slow_sgl(sgl_task_params->num_sges, + sgl_task_params->small_mid_sge); + + num_sges = !slow_io ? min_t(u16, sgl_task_params->num_sges, + (u16)SCSI_NUM_SGES_SLOW_SGL_THR) : + ISCSI_WQE_NUM_SGES_SLOWIO; + + if (slow_io) { + SET_FIELD(cxt->ystorm_st_context.state.flags, + YSTORM_ISCSI_TASK_STATE_SLOW_IO, 1); + } + } else if (task_params->rx_io_size) { + init_dif_context_flags(&cxt->mstorm_st_context.dif_flags, + dif_task_params); + init_scsi_sgl_context(&cxt->mstorm_st_context.sgl_params, + &cxt->mstorm_st_context.data_desc, + sgl_task_params); + num_sges = !scsi_is_slow_sgl(sgl_task_params->num_sges, + sgl_task_params->small_mid_sge) ? + min_t(u16, sgl_task_params->num_sges, + (u16)SCSI_NUM_SGES_SLOW_SGL_THR) : + ISCSI_WQE_NUM_SGES_SLOWIO; + cxt->mstorm_st_context.rem_task_size = cpu_to_le32(task_size); + } + + if (exp_data_transfer_len > task_size || + task_type != ISCSI_TASK_TYPE_TARGET_WRITE) + exp_data_transfer_len = task_size; + + init_ustorm_task_contexts(&task_params->context->ustorm_st_context, + &task_params->context->ustorm_ag_context, + task_size, exp_data_transfer_len, num_sges, + dif_task_params ? + dif_task_params->tx_dif_conn_err_en : false); + + set_rw_exp_data_acked_and_cont_len(task_params->context, conn_params, + task_type, task_size, + exp_data_transfer_len, + GET_FIELD(pdu_header->hdr_second_dword, + ISCSI_CMD_HDR_TOTAL_AHS_LEN)); + + if (dif_task_params) + init_rtdif_task_context(&task_params->context->rdif_context, + &task_params->context->tdif_context, + dif_task_params, task_type); + + init_sqe(task_params, sgl_task_params, dif_task_params, pdu_header, + cmd_params, task_type, false); + + return 0; +} + +int init_initiator_rw_iscsi_task(struct iscsi_task_params *task_params, + struct iscsi_conn_params *conn_params, + struct scsi_initiator_cmd_params *cmd_params, + struct iscsi_cmd_hdr *cmd_header, + struct scsi_sgl_task_params *tx_sgl_params, + struct scsi_sgl_task_params *rx_sgl_params, + struct scsi_dif_task_params *dif_task_params) +{ + if (GET_FIELD(cmd_header->flags_attr, ISCSI_CMD_HDR_WRITE)) + return init_rw_iscsi_task(task_params, + ISCSI_TASK_TYPE_INITIATOR_WRITE, + conn_params, + (struct iscsi_common_hdr *)cmd_header, + tx_sgl_params, cmd_params, + dif_task_params); + else if (GET_FIELD(cmd_header->flags_attr, ISCSI_CMD_HDR_READ)) + return init_rw_iscsi_task(task_params, + ISCSI_TASK_TYPE_INITIATOR_READ, + conn_params, + (struct iscsi_common_hdr *)cmd_header, + rx_sgl_params, cmd_params, + dif_task_params); + else + return -1; +} + +int init_initiator_login_request_task(struct iscsi_task_params *task_params, + struct iscsi_login_req_hdr *login_header, + struct scsi_sgl_task_params *tx_params, + struct scsi_sgl_task_params *rx_params) +{ + struct iscsi_task_context *cxt; + + cxt = task_params->context; + + init_default_iscsi_task(task_params, + (struct data_hdr *)login_header, + ISCSI_TASK_TYPE_MIDPATH); + + init_ustorm_task_contexts(&cxt->ustorm_st_context, + &cxt->ustorm_ag_context, + task_params->rx_io_size ? + rx_params->total_buffer_size : 0, + task_params->tx_io_size ? + tx_params->total_buffer_size : 0, 0, + 0); + + if (task_params->tx_io_size) + init_scsi_sgl_context(&cxt->ystorm_st_context.state.sgl_params, + &cxt->ystorm_st_context.state.data_desc, + tx_params); + + if (task_params->rx_io_size) + init_scsi_sgl_context(&cxt->mstorm_st_context.sgl_params, + &cxt->mstorm_st_context.data_desc, + rx_params); + + cxt->mstorm_st_context.rem_task_size = + cpu_to_le32(task_params->rx_io_size ? + rx_params->total_buffer_size : 0); + + init_sqe(task_params, tx_params, NULL, + (struct iscsi_common_hdr *)login_header, NULL, + ISCSI_TASK_TYPE_MIDPATH, false); + + return 0; +} + +int init_initiator_nop_out_task(struct iscsi_task_params *task_params, + struct iscsi_nop_out_hdr *nop_out_pdu_header, + struct scsi_sgl_task_params *tx_sgl_task_params, + struct scsi_sgl_task_params *rx_sgl_task_params) +{ + struct iscsi_task_context *cxt; + + cxt = task_params->context; + + init_default_iscsi_task(task_params, + (struct data_hdr *)nop_out_pdu_header, + ISCSI_TASK_TYPE_MIDPATH); + + if (nop_out_pdu_header->itt == ISCSI_ITT_ALL_ONES) + set_local_completion_context(task_params->context); + + if (task_params->tx_io_size) + init_scsi_sgl_context(&cxt->ystorm_st_context.state.sgl_params, + &cxt->ystorm_st_context.state.data_desc, + tx_sgl_task_params); + + if (task_params->rx_io_size) + init_scsi_sgl_context(&cxt->mstorm_st_context.sgl_params, + &cxt->mstorm_st_context.data_desc, + rx_sgl_task_params); + + init_ustorm_task_contexts(&cxt->ustorm_st_context, + &cxt->ustorm_ag_context, + task_params->rx_io_size ? + rx_sgl_task_params->total_buffer_size : 0, + task_params->tx_io_size ? + tx_sgl_task_params->total_buffer_size : 0, + 0, 0); + + cxt->mstorm_st_context.rem_task_size = + cpu_to_le32(task_params->rx_io_size ? + rx_sgl_task_params->total_buffer_size : + 0); + + init_sqe(task_params, tx_sgl_task_params, NULL, + (struct iscsi_common_hdr *)nop_out_pdu_header, NULL, + ISCSI_TASK_TYPE_MIDPATH, false); + + return 0; +} + +int init_initiator_logout_request_task(struct iscsi_task_params *task_params, + struct iscsi_logout_req_hdr *logout_hdr, + struct scsi_sgl_task_params *tx_params, + struct scsi_sgl_task_params *rx_params) +{ + struct iscsi_task_context *cxt; + + cxt = task_params->context; + + init_default_iscsi_task(task_params, + (struct data_hdr *)logout_hdr, + ISCSI_TASK_TYPE_MIDPATH); + + if (task_params->tx_io_size) + init_scsi_sgl_context(&cxt->ystorm_st_context.state.sgl_params, + &cxt->ystorm_st_context.state.data_desc, + tx_params); + + if (task_params->rx_io_size) + init_scsi_sgl_context(&cxt->mstorm_st_context.sgl_params, + &cxt->mstorm_st_context.data_desc, + rx_params); + + init_ustorm_task_contexts(&cxt->ustorm_st_context, + &cxt->ustorm_ag_context, + task_params->rx_io_size ? + rx_params->total_buffer_size : 0, + task_params->tx_io_size ? + tx_params->total_buffer_size : 0, + 0, 0); + + cxt->mstorm_st_context.rem_task_size = + cpu_to_le32(task_params->rx_io_size ? + rx_params->total_buffer_size : 0); + + init_sqe(task_params, tx_params, NULL, + (struct iscsi_common_hdr *)logout_hdr, NULL, + ISCSI_TASK_TYPE_MIDPATH, false); + + return 0; +} + +int init_initiator_tmf_request_task(struct iscsi_task_params *task_params, + struct iscsi_tmf_request_hdr *tmf_header) +{ + init_default_iscsi_task(task_params, (struct data_hdr *)tmf_header, + ISCSI_TASK_TYPE_MIDPATH); + + init_sqe(task_params, NULL, NULL, + (struct iscsi_common_hdr *)tmf_header, NULL, + ISCSI_TASK_TYPE_MIDPATH, false); + + return 0; +} + +int init_initiator_text_request_task(struct iscsi_task_params *task_params, + struct iscsi_text_request_hdr *text_header, + struct scsi_sgl_task_params *tx_params, + struct scsi_sgl_task_params *rx_params) +{ + struct iscsi_task_context *cxt; + + cxt = task_params->context; + + init_default_iscsi_task(task_params, + (struct data_hdr *)text_header, + ISCSI_TASK_TYPE_MIDPATH); + + if (task_params->tx_io_size) + init_scsi_sgl_context(&cxt->ystorm_st_context.state.sgl_params, + &cxt->ystorm_st_context.state.data_desc, + tx_params); + + if (task_params->rx_io_size) + init_scsi_sgl_context(&cxt->mstorm_st_context.sgl_params, + &cxt->mstorm_st_context.data_desc, + rx_params); + + cxt->mstorm_st_context.rem_task_size = + cpu_to_le32(task_params->rx_io_size ? + rx_params->total_buffer_size : 0); + + init_ustorm_task_contexts(&cxt->ustorm_st_context, + &cxt->ustorm_ag_context, + task_params->rx_io_size ? + rx_params->total_buffer_size : 0, + task_params->tx_io_size ? + tx_params->total_buffer_size : 0, 0, 0); + + init_sqe(task_params, tx_params, NULL, + (struct iscsi_common_hdr *)text_header, NULL, + ISCSI_TASK_TYPE_MIDPATH, false); + + return 0; +} + +int init_cleanup_task(struct iscsi_task_params *task_params) +{ + init_sqe(task_params, NULL, NULL, NULL, NULL, ISCSI_TASK_TYPE_MIDPATH, + true); + return 0; +} diff --git a/drivers/scsi/qedi/qedi_fw_iscsi.h b/drivers/scsi/qedi/qedi_fw_iscsi.h new file mode 100644 index 000000000000..b6f24f91849d --- /dev/null +++ b/drivers/scsi/qedi/qedi_fw_iscsi.h @@ -0,0 +1,117 @@ +/* + * QLogic iSCSI Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#ifndef _QEDI_FW_ISCSI_H_ +#define _QEDI_FW_ISCSI_H_ + +#include "qedi_fw_scsi.h" + +struct iscsi_task_params { + struct iscsi_task_context *context; + struct iscsi_wqe *sqe; + u32 tx_io_size; + u32 rx_io_size; + u16 conn_icid; + u16 itid; + u8 cq_rss_number; +}; + +struct iscsi_conn_params { + u32 first_burst_length; + u32 max_send_pdu_length; + u32 max_burst_length; + bool initial_r2t; + bool immediate_data; +}; + +/* @brief init_initiator_read_iscsi_task - initializes iSCSI Initiator Read + * task context. + * + * @param task_params - Pointer to task parameters struct + * @param conn_params - Connection Parameters + * @param cmd_params - command specific parameters + * @param cmd_pdu_header - PDU Header Parameters + * @param sgl_task_params - Pointer to SGL task params + * @param dif_task_params - Pointer to DIF parameters struct + */ +int init_initiator_rw_iscsi_task(struct iscsi_task_params *task_params, + struct iscsi_conn_params *conn_params, + struct scsi_initiator_cmd_params *cmd_params, + struct iscsi_cmd_hdr *cmd_pdu_header, + struct scsi_sgl_task_params *tx_sgl_params, + struct scsi_sgl_task_params *rx_sgl_params, + struct scsi_dif_task_params *dif_task_params); + +/* @brief init_initiator_login_request_task - initializes iSCSI Initiator Login + * Request task context. + * + * @param task_params - Pointer to task parameters struct + * @param login_req_pdu_header - PDU Header Parameters + * @param tx_sgl_task_params - Pointer to SGL task params + * @param rx_sgl_task_params - Pointer to SGL task params + */ +int init_initiator_login_request_task(struct iscsi_task_params *task_params, + struct iscsi_login_req_hdr *login_header, + struct scsi_sgl_task_params *tx_params, + struct scsi_sgl_task_params *rx_params); + +/* @brief init_initiator_nop_out_task - initializes iSCSI Initiator NOP Out + * task context. + * + * @param task_params - Pointer to task parameters struct + * @param nop_out_pdu_header - PDU Header Parameters + * @param tx_sgl_task_params - Pointer to SGL task params + * @param rx_sgl_task_params - Pointer to SGL task params + */ +int init_initiator_nop_out_task(struct iscsi_task_params *task_params, + struct iscsi_nop_out_hdr *nop_out_pdu_header, + struct scsi_sgl_task_params *tx_sgl_params, + struct scsi_sgl_task_params *rx_sgl_params); + +/* @brief init_initiator_logout_request_task - initializes iSCSI Initiator + * Logout Request task context. + * + * @param task_params - Pointer to task parameters struct + * @param logout_pdu_header - PDU Header Parameters + * @param tx_sgl_task_params - Pointer to SGL task params + * @param rx_sgl_task_params - Pointer to SGL task params + */ +int init_initiator_logout_request_task(struct iscsi_task_params *task_params, + struct iscsi_logout_req_hdr *logout_hdr, + struct scsi_sgl_task_params *tx_params, + struct scsi_sgl_task_params *rx_params); + +/* @brief init_initiator_tmf_request_task - initializes iSCSI Initiator TMF + * task context. + * + * @param task_params - Pointer to task parameters struct + * @param tmf_pdu_header - PDU Header Parameters + */ +int init_initiator_tmf_request_task(struct iscsi_task_params *task_params, + struct iscsi_tmf_request_hdr *tmf_header); + +/* @brief init_initiator_text_request_task - initializes iSCSI Initiator Text + * Request task context. + * + * @param task_params - Pointer to task parameters struct + * @param text_request_pdu_header - PDU Header Parameters + * @param tx_sgl_task_params - Pointer to Tx SGL task params + * @param rx_sgl_task_params - Pointer to Rx SGL task params + */ +int init_initiator_text_request_task(struct iscsi_task_params *task_params, + struct iscsi_text_request_hdr *text_header, + struct scsi_sgl_task_params *tx_params, + struct scsi_sgl_task_params *rx_params); + +/* @brief init_cleanup_task - initializes Clean task (SQE) + * + * @param task_params - Pointer to task parameters struct + */ +int init_cleanup_task(struct iscsi_task_params *task_params); +#endif diff --git a/drivers/scsi/qedi/qedi_fw_scsi.h b/drivers/scsi/qedi/qedi_fw_scsi.h new file mode 100644 index 000000000000..cdaf918f1019 --- /dev/null +++ b/drivers/scsi/qedi/qedi_fw_scsi.h @@ -0,0 +1,55 @@ +/* + * QLogic iSCSI Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available under the terms of the GNU General Public License + * (GPL) Version 2, available from the file COPYING in the main directory of + * this source tree. + */ + +#ifndef _QEDI_FW_SCSI_H_ +#define _QEDI_FW_SCSI_H_ + +#include +#include +#include "qedi_hsi.h" +#include + +struct scsi_sgl_task_params { + struct scsi_sge *sgl; + struct regpair sgl_phys_addr; + u32 total_buffer_size; + u16 num_sges; + bool small_mid_sge; +}; + +struct scsi_dif_task_params { + u32 initial_ref_tag; + bool initial_ref_tag_is_valid; + u16 application_tag; + u16 application_tag_mask; + u16 dif_block_size_log; + bool dif_on_network; + bool dif_on_host; + u8 host_guard_type; + u8 protection_type; + u8 ref_tag_mask; + bool crc_seed; + bool tx_dif_conn_err_en; + bool ignore_app_tag; + bool keep_ref_tag_const; + bool validate_guard; + bool validate_app_tag; + bool validate_ref_tag; + bool forward_guard; + bool forward_app_tag; + bool forward_ref_tag; + bool forward_app_tag_with_mask; + bool forward_ref_tag_with_mask; +}; + +struct scsi_initiator_cmd_params { + struct scsi_sge extended_cdb_sge; + struct regpair sense_data_buffer_phys_addr; +}; +#endif diff --git a/drivers/scsi/qedi/qedi_iscsi.c b/drivers/scsi/qedi/qedi_iscsi.c index b9f79d36142d..d5eff68507e5 100644 --- a/drivers/scsi/qedi/qedi_iscsi.c +++ b/drivers/scsi/qedi/qedi_iscsi.c @@ -175,7 +175,7 @@ static void qedi_destroy_cmd_pool(struct qedi_ctx *qedi, if (cmd->io_tbl.sge_tbl) dma_free_coherent(&qedi->pdev->dev, QEDI_ISCSI_MAX_BDS_PER_CMD * - sizeof(struct iscsi_sge), + sizeof(struct scsi_sge), cmd->io_tbl.sge_tbl, cmd->io_tbl.sge_tbl_dma); @@ -191,7 +191,7 @@ static int qedi_alloc_sget(struct qedi_ctx *qedi, struct iscsi_session *session, struct qedi_cmd *cmd) { struct qedi_io_bdt *io = &cmd->io_tbl; - struct iscsi_sge *sge; + struct scsi_sge *sge; io->sge_tbl = dma_alloc_coherent(&qedi->pdev->dev, QEDI_ISCSI_MAX_BDS_PER_CMD * @@ -708,22 +708,20 @@ static void qedi_conn_get_stats(struct iscsi_cls_conn *cls_conn, static void qedi_iscsi_prep_generic_pdu_bd(struct qedi_conn *qedi_conn) { - struct iscsi_sge *bd_tbl; + struct scsi_sge *bd_tbl; - bd_tbl = (struct iscsi_sge *)qedi_conn->gen_pdu.req_bd_tbl; + bd_tbl = (struct scsi_sge *)qedi_conn->gen_pdu.req_bd_tbl; bd_tbl->sge_addr.hi = (u32)((u64)qedi_conn->gen_pdu.req_dma_addr >> 32); bd_tbl->sge_addr.lo = (u32)qedi_conn->gen_pdu.req_dma_addr; bd_tbl->sge_len = qedi_conn->gen_pdu.req_wr_ptr - qedi_conn->gen_pdu.req_buf; - bd_tbl->reserved0 = 0; - bd_tbl = (struct iscsi_sge *)qedi_conn->gen_pdu.resp_bd_tbl; + bd_tbl = (struct scsi_sge *)qedi_conn->gen_pdu.resp_bd_tbl; bd_tbl->sge_addr.hi = (u32)((u64)qedi_conn->gen_pdu.resp_dma_addr >> 32); bd_tbl->sge_addr.lo = (u32)qedi_conn->gen_pdu.resp_dma_addr; bd_tbl->sge_len = ISCSI_DEF_MAX_RECV_SEG_LEN; - bd_tbl->reserved0 = 0; } static int qedi_iscsi_send_generic_request(struct iscsi_task *task) diff --git a/drivers/scsi/qedi/qedi_iscsi.h b/drivers/scsi/qedi/qedi_iscsi.h index d3c06bbddb4e..3247287cb0e7 100644 --- a/drivers/scsi/qedi/qedi_iscsi.h +++ b/drivers/scsi/qedi/qedi_iscsi.h @@ -102,7 +102,7 @@ struct qedi_endpoint { #define QEDI_SQ_WQES_MIN 16 struct qedi_io_bdt { - struct iscsi_sge *sge_tbl; + struct scsi_sge *sge_tbl; dma_addr_t sge_tbl_dma; u16 sge_valid; }; diff --git a/drivers/scsi/qedi/qedi_version.h b/drivers/scsi/qedi/qedi_version.h index 9543a1b139d4..d61e3ac22e67 100644 --- a/drivers/scsi/qedi/qedi_version.h +++ b/drivers/scsi/qedi/qedi_version.h @@ -7,8 +7,8 @@ * this source tree. */ -#define QEDI_MODULE_VERSION "8.10.3.0" +#define QEDI_MODULE_VERSION "8.10.4.0" #define QEDI_DRIVER_MAJOR_VER 8 #define QEDI_DRIVER_MINOR_VER 10 -#define QEDI_DRIVER_REV_VER 3 +#define QEDI_DRIVER_REV_VER 4 #define QEDI_DRIVER_ENG_VER 0 diff --git a/include/linux/qed/common_hsi.h b/include/linux/qed/common_hsi.h index 52966b9bfde3..fbab6e0514f0 100644 --- a/include/linux/qed/common_hsi.h +++ b/include/linux/qed/common_hsi.h @@ -100,8 +100,8 @@ #define MAX_NUM_LL2_TX_STATS_COUNTERS 32 #define FW_MAJOR_VERSION 8 -#define FW_MINOR_VERSION 10 -#define FW_REVISION_VERSION 10 +#define FW_MINOR_VERSION 15 +#define FW_REVISION_VERSION 3 #define FW_ENGINEERING_VERSION 0 /***********************/ @@ -187,6 +187,9 @@ /* DEMS */ #define DQ_DEMS_LEGACY 0 +#define DQ_DEMS_TOE_MORE_TO_SEND 3 +#define DQ_DEMS_TOE_LOCAL_ADV_WND 4 +#define DQ_DEMS_ROCE_CQ_CONS 7 /* XCM agg val selection */ #define DQ_XCM_AGG_VAL_SEL_WORD2 0 @@ -214,6 +217,9 @@ #define DQ_XCM_ISCSI_MORE_TO_SEND_SEQ_CMD DQ_XCM_AGG_VAL_SEL_REG3 #define DQ_XCM_ISCSI_EXP_STAT_SN_CMD DQ_XCM_AGG_VAL_SEL_REG6 #define DQ_XCM_ROCE_SQ_PROD_CMD DQ_XCM_AGG_VAL_SEL_WORD4 +#define DQ_XCM_TOE_TX_BD_PROD_CMD DQ_XCM_AGG_VAL_SEL_WORD4 +#define DQ_XCM_TOE_MORE_TO_SEND_SEQ_CMD DQ_XCM_AGG_VAL_SEL_REG3 +#define DQ_XCM_TOE_LOCAL_ADV_WND_SEQ_CMD DQ_XCM_AGG_VAL_SEL_REG4 /* UCM agg val selection (HW) */ #define DQ_UCM_AGG_VAL_SEL_WORD0 0 @@ -269,6 +275,8 @@ #define DQ_XCM_ISCSI_DQ_FLUSH_CMD BIT(DQ_XCM_AGG_FLG_SHIFT_CF19) #define DQ_XCM_ISCSI_SLOW_PATH_CMD BIT(DQ_XCM_AGG_FLG_SHIFT_CF22) #define DQ_XCM_ISCSI_PROC_ONLY_CLEANUP_CMD BIT(DQ_XCM_AGG_FLG_SHIFT_CF23) +#define DQ_XCM_TOE_DQ_FLUSH_CMD BIT(DQ_XCM_AGG_FLG_SHIFT_CF19) +#define DQ_XCM_TOE_SLOW_PATH_CMD BIT(DQ_XCM_AGG_FLG_SHIFT_CF22) /* UCM agg counter flag selection (HW) */ #define DQ_UCM_AGG_FLG_SHIFT_CF0 0 @@ -285,6 +293,9 @@ #define DQ_UCM_ETH_PMD_RX_ARM_CMD BIT(DQ_UCM_AGG_FLG_SHIFT_CF5) #define DQ_UCM_ROCE_CQ_ARM_SE_CF_CMD BIT(DQ_UCM_AGG_FLG_SHIFT_CF4) #define DQ_UCM_ROCE_CQ_ARM_CF_CMD BIT(DQ_UCM_AGG_FLG_SHIFT_CF5) +#define DQ_UCM_TOE_TIMER_STOP_ALL_CMD BIT(DQ_UCM_AGG_FLG_SHIFT_CF3) +#define DQ_UCM_TOE_SLOW_PATH_CF_CMD BIT(DQ_UCM_AGG_FLG_SHIFT_CF4) +#define DQ_UCM_TOE_DQ_CF_CMD BIT(DQ_UCM_AGG_FLG_SHIFT_CF5) /* TCM agg counter flag selection (HW) */ #define DQ_TCM_AGG_FLG_SHIFT_CF0 0 @@ -301,6 +312,9 @@ #define DQ_TCM_FCOE_TIMER_STOP_ALL_CMD BIT(DQ_TCM_AGG_FLG_SHIFT_CF3) #define DQ_TCM_ISCSI_FLUSH_Q0_CMD BIT(DQ_TCM_AGG_FLG_SHIFT_CF1) #define DQ_TCM_ISCSI_TIMER_STOP_ALL_CMD BIT(DQ_TCM_AGG_FLG_SHIFT_CF3) +#define DQ_TCM_TOE_FLUSH_Q0_CMD BIT(DQ_TCM_AGG_FLG_SHIFT_CF1) +#define DQ_TCM_TOE_TIMER_STOP_ALL_CMD BIT(DQ_TCM_AGG_FLG_SHIFT_CF3) +#define DQ_TCM_IWARP_POST_RQ_CF_CMD BIT(DQ_TCM_AGG_FLG_SHIFT_CF1) /* PWM address mapping */ #define DQ_PWM_OFFSET_DPM_BASE 0x0 @@ -689,6 +703,16 @@ struct iscsi_eqe_data { #define ISCSI_EQE_DATA_RESERVED0_SHIFT 7 }; +struct rdma_eqe_destroy_qp { + __le32 cid; + u8 reserved[4]; +}; + +union rdma_eqe_data { + struct regpair async_handle; + struct rdma_eqe_destroy_qp rdma_destroy_qp_data; +}; + struct malicious_vf_eqe_data { u8 vf_id; u8 err_id; @@ -705,9 +729,9 @@ union event_ring_data { u8 bytes[8]; struct vf_pf_channel_eqe_data vf_pf_channel; struct iscsi_eqe_data iscsi_info; + union rdma_eqe_data rdma_data; struct malicious_vf_eqe_data malicious_vf; struct initial_cleanup_eqe_data vf_init_cleanup; - struct regpair roce_handle; }; /* Event Ring Entry */ diff --git a/include/linux/qed/eth_common.h b/include/linux/qed/eth_common.h index 4b402fb0eaad..34d93eb5bfba 100644 --- a/include/linux/qed/eth_common.h +++ b/include/linux/qed/eth_common.h @@ -49,6 +49,9 @@ #define ETH_RX_CQE_PAGE_SIZE_BYTES 4096 #define ETH_RX_NUM_NEXT_PAGE_BDS 2 +#define ETH_MAX_TUNN_LSO_INNER_IPV4_OFFSET 253 +#define ETH_MAX_TUNN_LSO_INNER_IPV6_OFFSET 251 + #define ETH_TX_MIN_BDS_PER_NON_LSO_PKT 1 #define ETH_TX_MAX_BDS_PER_NON_LSO_PACKET 18 #define ETH_TX_MAX_BDS_PER_LSO_PACKET 255 diff --git a/include/linux/qed/fcoe_common.h b/include/linux/qed/fcoe_common.h index 2e417a45c5f7..947a635d04bb 100644 --- a/include/linux/qed/fcoe_common.h +++ b/include/linux/qed/fcoe_common.h @@ -109,13 +109,6 @@ struct fcoe_conn_terminate_ramrod_data { struct regpair terminate_params_addr; }; -struct fcoe_fast_sgl_ctx { - struct regpair sgl_start_addr; - __le32 sgl_byte_offset; - __le16 task_reuse_cnt; - __le16 init_offset_in_first_sge; -}; - struct fcoe_slow_sgl_ctx { struct regpair base_sgl_addr; __le16 curr_sge_off; @@ -124,23 +117,16 @@ struct fcoe_slow_sgl_ctx { __le16 reserved; }; -struct fcoe_sge { - struct regpair sge_addr; - __le16 size; - __le16 reserved0; - u8 reserved1[3]; - u8 is_valid_sge; -}; - -union fcoe_data_desc_ctx { - struct fcoe_fast_sgl_ctx fast; - struct fcoe_slow_sgl_ctx slow; - struct fcoe_sge single_sge; -}; - union fcoe_dix_desc_ctx { struct fcoe_slow_sgl_ctx dix_sgl; - struct fcoe_sge cached_dix_sge; + struct scsi_sge cached_dix_sge; +}; + +struct fcoe_fast_sgl_ctx { + struct regpair sgl_start_addr; + __le32 sgl_byte_offset; + __le16 task_reuse_cnt; + __le16 init_offset_in_first_sge; }; struct fcoe_fcp_cmd_payload { @@ -172,57 +158,6 @@ enum fcoe_mode_type { MAX_FCOE_MODE_TYPE }; -struct fcoe_mstorm_fcoe_task_st_ctx_fp { - __le16 flags; -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_FP_RSRV0_MASK 0x7FFF -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_FP_RSRV0_SHIFT 0 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_FP_MP_INCLUDE_FC_HEADER_MASK 0x1 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_FP_MP_INCLUDE_FC_HEADER_SHIFT 15 - __le16 difDataResidue; - __le16 parent_id; - __le16 single_sge_saved_offset; - __le32 data_2_trns_rem; - __le32 offset_in_io; - union fcoe_dix_desc_ctx dix_desc; - union fcoe_data_desc_ctx data_desc; -}; - -struct fcoe_mstorm_fcoe_task_st_ctx_non_fp { - __le16 flags; -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_HOST_INTERFACE_MASK 0x3 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_HOST_INTERFACE_SHIFT 0 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_DIF_TO_PEER_MASK 0x1 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_DIF_TO_PEER_SHIFT 2 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_VALIDATE_DIX_APP_TAG_MASK 0x1 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_VALIDATE_DIX_APP_TAG_SHIFT 3 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_INTERVAL_SIZE_LOG_MASK 0xF -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_INTERVAL_SIZE_LOG_SHIFT 4 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_DIX_BLOCK_SIZE_MASK 0x3 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_DIX_BLOCK_SIZE_SHIFT 8 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_RESERVED_MASK 0x1 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_RESERVED_SHIFT 10 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_HAS_FIRST_PACKET_ARRIVED_MASK 0x1 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_HAS_FIRST_PACKET_ARRIVED_SHIFT 11 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_VALIDATE_DIX_REF_TAG_MASK 0x1 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_VALIDATE_DIX_REF_TAG_SHIFT 12 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_DIX_CACHED_SGE_FLG_MASK 0x1 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_DIX_CACHED_SGE_FLG_SHIFT 13 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_OFFSET_IN_IO_VALID_MASK 0x1 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_OFFSET_IN_IO_VALID_SHIFT 14 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_DIF_SUPPORTED_MASK 0x1 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_DIF_SUPPORTED_SHIFT 15 - u8 tx_rx_sgl_mode; -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_TX_SGL_MODE_MASK 0x7 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_TX_SGL_MODE_SHIFT 0 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_RX_SGL_MODE_MASK 0x7 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_RX_SGL_MODE_SHIFT 3 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_RSRV1_MASK 0x3 -#define FCOE_MSTORM_FCOE_TASK_ST_CTX_NON_FP_RSRV1_SHIFT 6 - u8 rsrv2; - __le32 num_prm_zero_read; - struct regpair rsp_buf_addr; -}; - struct fcoe_rx_stat { struct regpair fcoe_rx_byte_cnt; struct regpair fcoe_rx_data_pkt_cnt; @@ -236,16 +171,6 @@ struct fcoe_rx_stat { __le32 rsrv; }; -enum fcoe_sgl_mode { - FCOE_SLOW_SGL, - FCOE_SINGLE_FAST_SGE, - FCOE_2_FAST_SGE, - FCOE_3_FAST_SGE, - FCOE_4_FAST_SGE, - FCOE_MUL_FAST_SGES, - MAX_FCOE_SGL_MODE -}; - struct fcoe_stat_ramrod_data { struct regpair stat_params_addr; }; @@ -328,22 +253,24 @@ union fcoe_tx_info_union_ctx { struct ystorm_fcoe_task_st_ctx { u8 task_type; u8 sgl_mode; -#define YSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE_MASK 0x7 +#define YSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE_MASK 0x1 #define YSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE_SHIFT 0 -#define YSTORM_FCOE_TASK_ST_CTX_RSRV_MASK 0x1F -#define YSTORM_FCOE_TASK_ST_CTX_RSRV_SHIFT 3 +#define YSTORM_FCOE_TASK_ST_CTX_RSRV_MASK 0x7F +#define YSTORM_FCOE_TASK_ST_CTX_RSRV_SHIFT 1 u8 cached_dix_sge; u8 expect_first_xfer; __le32 num_pbf_zero_write; union protection_info_union_ctx protection_info_union; __le32 data_2_trns_rem; + struct scsi_sgl_params sgl_params; + u8 reserved1[12]; union fcoe_tx_info_union_ctx tx_info_union; union fcoe_dix_desc_ctx dix_desc; - union fcoe_data_desc_ctx data_desc; + struct scsi_cached_sges data_desc; __le16 ox_id; __le16 rx_id; __le32 task_rety_identifier; - __le32 reserved1[2]; + u8 reserved2[8]; }; struct ystorm_fcoe_task_ag_ctx { @@ -484,22 +411,22 @@ struct tstorm_fcoe_task_ag_ctx { struct fcoe_tstorm_fcoe_task_st_ctx_read_write { union fcoe_cleanup_addr_exp_ro_union cleanup_addr_exp_ro_union; __le16 flags; -#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_RX_SGL_MODE_MASK 0x7 +#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_RX_SGL_MODE_MASK 0x1 #define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_RX_SGL_MODE_SHIFT 0 #define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_EXP_FIRST_FRAME_MASK 0x1 -#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_EXP_FIRST_FRAME_SHIFT 3 +#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_EXP_FIRST_FRAME_SHIFT 1 #define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_SEQ_ACTIVE_MASK 0x1 -#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_SEQ_ACTIVE_SHIFT 4 +#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_SEQ_ACTIVE_SHIFT 2 #define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_SEQ_TIMEOUT_MASK 0x1 -#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_SEQ_TIMEOUT_SHIFT 5 +#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_SEQ_TIMEOUT_SHIFT 3 #define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_SINGLE_PKT_IN_EX_MASK 0x1 -#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_SINGLE_PKT_IN_EX_SHIFT 6 +#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_SINGLE_PKT_IN_EX_SHIFT 4 #define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_OOO_RX_SEQ_STAT_MASK 0x1 -#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_OOO_RX_SEQ_STAT_SHIFT 7 +#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_OOO_RX_SEQ_STAT_SHIFT 5 #define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_CQ_ADD_ADV_MASK 0x3 -#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_CQ_ADD_ADV_SHIFT 8 -#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_RSRV1_MASK 0x3F -#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_RSRV1_SHIFT 10 +#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_CQ_ADD_ADV_SHIFT 6 +#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_RSRV1_MASK 0xFF +#define FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_RSRV1_SHIFT 8 __le16 seq_cnt; u8 seq_id; u8 ooo_rx_seq_id; @@ -582,8 +509,34 @@ struct mstorm_fcoe_task_ag_ctx { }; struct mstorm_fcoe_task_st_ctx { - struct fcoe_mstorm_fcoe_task_st_ctx_non_fp non_fp; - struct fcoe_mstorm_fcoe_task_st_ctx_fp fp; + struct regpair rsp_buf_addr; + __le32 rsrv[2]; + struct scsi_sgl_params sgl_params; + __le32 data_2_trns_rem; + __le32 data_buffer_offset; + __le16 parent_id; + __le16 flags; +#define MSTORM_FCOE_TASK_ST_CTX_INTERVAL_SIZE_LOG_MASK 0xF +#define MSTORM_FCOE_TASK_ST_CTX_INTERVAL_SIZE_LOG_SHIFT 0 +#define MSTORM_FCOE_TASK_ST_CTX_HOST_INTERFACE_MASK 0x3 +#define MSTORM_FCOE_TASK_ST_CTX_HOST_INTERFACE_SHIFT 4 +#define MSTORM_FCOE_TASK_ST_CTX_DIF_TO_PEER_MASK 0x1 +#define MSTORM_FCOE_TASK_ST_CTX_DIF_TO_PEER_SHIFT 6 +#define MSTORM_FCOE_TASK_ST_CTX_MP_INCLUDE_FC_HEADER_MASK 0x1 +#define MSTORM_FCOE_TASK_ST_CTX_MP_INCLUDE_FC_HEADER_SHIFT 7 +#define MSTORM_FCOE_TASK_ST_CTX_DIX_BLOCK_SIZE_MASK 0x3 +#define MSTORM_FCOE_TASK_ST_CTX_DIX_BLOCK_SIZE_SHIFT 8 +#define MSTORM_FCOE_TASK_ST_CTX_VALIDATE_DIX_REF_TAG_MASK 0x1 +#define MSTORM_FCOE_TASK_ST_CTX_VALIDATE_DIX_REF_TAG_SHIFT 10 +#define MSTORM_FCOE_TASK_ST_CTX_DIX_CACHED_SGE_FLG_MASK 0x1 +#define MSTORM_FCOE_TASK_ST_CTX_DIX_CACHED_SGE_FLG_SHIFT 11 +#define MSTORM_FCOE_TASK_ST_CTX_DIF_SUPPORTED_MASK 0x1 +#define MSTORM_FCOE_TASK_ST_CTX_DIF_SUPPORTED_SHIFT 12 +#define MSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE_MASK 0x1 +#define MSTORM_FCOE_TASK_ST_CTX_TX_SGL_MODE_SHIFT 13 +#define MSTORM_FCOE_TASK_ST_CTX_RESERVED_MASK 0x3 +#define MSTORM_FCOE_TASK_ST_CTX_RESERVED_SHIFT 14 + struct scsi_cached_sges data_desc; }; struct ustorm_fcoe_task_ag_ctx { @@ -646,6 +599,7 @@ struct ustorm_fcoe_task_ag_ctx { struct fcoe_task_context { struct ystorm_fcoe_task_st_ctx ystorm_st_context; + struct regpair ystorm_st_padding[2]; struct tdif_task_context tdif_context; struct ystorm_fcoe_task_ag_ctx ystorm_ag_context; struct tstorm_fcoe_task_ag_ctx tstorm_ag_context; @@ -668,20 +622,20 @@ struct fcoe_tx_stat { struct fcoe_wqe { __le16 task_id; __le16 flags; -#define FCOE_WQE_REQ_TYPE_MASK 0xF -#define FCOE_WQE_REQ_TYPE_SHIFT 0 -#define FCOE_WQE_SGL_MODE_MASK 0x7 -#define FCOE_WQE_SGL_MODE_SHIFT 4 -#define FCOE_WQE_CONTINUATION_MASK 0x1 -#define FCOE_WQE_CONTINUATION_SHIFT 7 -#define FCOE_WQE_INVALIDATE_PTU_MASK 0x1 -#define FCOE_WQE_INVALIDATE_PTU_SHIFT 8 -#define FCOE_WQE_SUPER_IO_MASK 0x1 -#define FCOE_WQE_SUPER_IO_SHIFT 9 -#define FCOE_WQE_SEND_AUTO_RSP_MASK 0x1 -#define FCOE_WQE_SEND_AUTO_RSP_SHIFT 10 -#define FCOE_WQE_RESERVED0_MASK 0x1F -#define FCOE_WQE_RESERVED0_SHIFT 11 +#define FCOE_WQE_REQ_TYPE_MASK 0xF +#define FCOE_WQE_REQ_TYPE_SHIFT 0 +#define FCOE_WQE_SGL_MODE_MASK 0x1 +#define FCOE_WQE_SGL_MODE_SHIFT 4 +#define FCOE_WQE_CONTINUATION_MASK 0x1 +#define FCOE_WQE_CONTINUATION_SHIFT 5 +#define FCOE_WQE_SEND_AUTO_RSP_MASK 0x1 +#define FCOE_WQE_SEND_AUTO_RSP_SHIFT 6 +#define FCOE_WQE_RESERVED_MASK 0x1 +#define FCOE_WQE_RESERVED_SHIFT 7 +#define FCOE_WQE_NUM_SGES_MASK 0xF +#define FCOE_WQE_NUM_SGES_SHIFT 8 +#define FCOE_WQE_RESERVED1_MASK 0xF +#define FCOE_WQE_RESERVED1_SHIFT 12 union fcoe_additional_info_union additional_info_union; }; diff --git a/include/linux/qed/iscsi_common.h b/include/linux/qed/iscsi_common.h index 4c5747babcf6..69949f8e354b 100644 --- a/include/linux/qed/iscsi_common.h +++ b/include/linux/qed/iscsi_common.h @@ -39,17 +39,9 @@ /* iSCSI HSI constants */ #define ISCSI_DEFAULT_MTU (1500) -/* Current iSCSI HSI version number composed of two fields (16 bit) */ -#define ISCSI_HSI_MAJOR_VERSION (0) -#define ISCSI_HSI_MINOR_VERSION (0) - /* KWQ (kernel work queue) layer codes */ #define ISCSI_SLOW_PATH_LAYER_CODE (6) -/* CQE completion status */ -#define ISCSI_EQE_COMPLETION_SUCCESS (0x0) -#define ISCSI_EQE_RST_CONN_RCVD (0x1) - /* iSCSI parameter defaults */ #define ISCSI_DEFAULT_HEADER_DIGEST (0) #define ISCSI_DEFAULT_DATA_DIGEST (0) @@ -68,6 +60,10 @@ #define ISCSI_MIN_VAL_MAX_OUTSTANDING_R2T (1) #define ISCSI_MAX_VAL_MAX_OUTSTANDING_R2T (0xff) +#define ISCSI_AHS_CNTL_SIZE 4 + +#define ISCSI_WQE_NUM_SGES_SLOWIO (0xf) + /* iSCSI reserved params */ #define ISCSI_ITT_ALL_ONES (0xffffffff) #define ISCSI_TTT_ALL_ONES (0xffffffff) @@ -173,19 +169,6 @@ struct iscsi_async_msg_hdr { __le32 reserved7; }; -struct iscsi_sge { - struct regpair sge_addr; - __le16 sge_len; - __le16 reserved0; - __le32 reserved1; -}; - -struct iscsi_cached_sge_ctx { - struct iscsi_sge sge; - struct regpair reserved; - __le32 dsgl_curr_offset[2]; -}; - struct iscsi_cmd_hdr { __le16 reserved1; u8 flags_attr; @@ -229,8 +212,13 @@ struct iscsi_common_hdr { #define ISCSI_COMMON_HDR_DATA_SEG_LEN_SHIFT 0 #define ISCSI_COMMON_HDR_TOTAL_AHS_LEN_MASK 0xFF #define ISCSI_COMMON_HDR_TOTAL_AHS_LEN_SHIFT 24 - __le32 lun_reserved[4]; - __le32 data[6]; + struct regpair lun_reserved; + __le32 itt; + __le32 ttt; + __le32 cmdstat_sn; + __le32 exp_statcmd_sn; + __le32 max_cmd_sn; + __le32 data[3]; }; struct iscsi_conn_offload_params { @@ -246,8 +234,10 @@ struct iscsi_conn_offload_params { #define ISCSI_CONN_OFFLOAD_PARAMS_TCP_ON_CHIP_1B_SHIFT 0 #define ISCSI_CONN_OFFLOAD_PARAMS_TARGET_MODE_MASK 0x1 #define ISCSI_CONN_OFFLOAD_PARAMS_TARGET_MODE_SHIFT 1 -#define ISCSI_CONN_OFFLOAD_PARAMS_RESERVED1_MASK 0x3F -#define ISCSI_CONN_OFFLOAD_PARAMS_RESERVED1_SHIFT 2 +#define ISCSI_CONN_OFFLOAD_PARAMS_RESTRICTED_MODE_MASK 0x1 +#define ISCSI_CONN_OFFLOAD_PARAMS_RESTRICTED_MODE_SHIFT 2 +#define ISCSI_CONN_OFFLOAD_PARAMS_RESERVED1_MASK 0x1F +#define ISCSI_CONN_OFFLOAD_PARAMS_RESERVED1_SHIFT 3 u8 pbl_page_size_log; u8 pbe_page_size_log; u8 default_cq; @@ -278,8 +268,12 @@ struct iscsi_conn_update_ramrod_params { #define ISCSI_CONN_UPDATE_RAMROD_PARAMS_INITIAL_R2T_SHIFT 2 #define ISCSI_CONN_UPDATE_RAMROD_PARAMS_IMMEDIATE_DATA_MASK 0x1 #define ISCSI_CONN_UPDATE_RAMROD_PARAMS_IMMEDIATE_DATA_SHIFT 3 -#define ISCSI_CONN_UPDATE_RAMROD_PARAMS_RESERVED1_MASK 0xF -#define ISCSI_CONN_UPDATE_RAMROD_PARAMS_RESERVED1_SHIFT 4 +#define ISCSI_CONN_UPDATE_RAMROD_PARAMS_DIF_BLOCK_SIZE_MASK 0x1 +#define ISCSI_CONN_UPDATE_RAMROD_PARAMS_DIF_BLOCK_SIZE_SHIFT 4 +#define ISCSI_CONN_UPDATE_RAMROD_PARAMS_DIF_ON_HOST_EN_MASK 0x1 +#define ISCSI_CONN_UPDATE_RAMROD_PARAMS_DIF_ON_HOST_EN_SHIFT 5 +#define ISCSI_CONN_UPDATE_RAMROD_PARAMS_RESERVED1_MASK 0x3 +#define ISCSI_CONN_UPDATE_RAMROD_PARAMS_RESERVED1_SHIFT 6 u8 reserved0[3]; __le32 max_seq_size; __le32 max_send_pdu_length; @@ -312,7 +306,7 @@ struct iscsi_ext_cdb_cmd_hdr { __le32 expected_transfer_length; __le32 cmd_sn; __le32 exp_stat_sn; - struct iscsi_sge cdb_sge; + struct scsi_sge cdb_sge; }; struct iscsi_login_req_hdr { @@ -519,8 +513,8 @@ struct iscsi_logout_response_hdr { __le32 exp_cmd_sn; __le32 max_cmd_sn; __le32 reserved4; - __le16 time2retain; - __le16 time2wait; + __le16 time_2_retain; + __le16 time_2_wait; __le32 reserved5[1]; }; @@ -602,7 +596,7 @@ struct iscsi_tmf_response_hdr { #define ISCSI_TMF_RESPONSE_HDR_TOTAL_AHS_LEN_SHIFT 24 struct regpair reserved0; __le32 itt; - __le32 rtt; + __le32 reserved1; __le32 stat_sn; __le32 exp_cmd_sn; __le32 max_cmd_sn; @@ -641,7 +635,7 @@ struct iscsi_reject_hdr { #define ISCSI_REJECT_HDR_TOTAL_AHS_LEN_MASK 0xFF #define ISCSI_REJECT_HDR_TOTAL_AHS_LEN_SHIFT 24 struct regpair reserved0; - __le32 reserved1; + __le32 all_ones; __le32 reserved2; __le32 stat_sn; __le32 exp_cmd_sn; @@ -688,7 +682,9 @@ struct iscsi_cqe_solicited { __le16 itid; u8 task_type; u8 fw_dbg_field; - __le32 reserved1[2]; + u8 caused_conn_err; + u8 reserved0[3]; + __le32 reserved1[1]; union iscsi_task_hdr iscsi_hdr; }; @@ -727,35 +723,6 @@ enum iscsi_cqe_unsolicited_type { MAX_ISCSI_CQE_UNSOLICITED_TYPE }; -struct iscsi_virt_sgl_ctx { - struct regpair sgl_base; - struct regpair dsgl_base; - __le32 sgl_initial_offset; - __le32 dsgl_initial_offset; - __le32 dsgl_curr_offset[2]; -}; - -struct iscsi_sgl_var_params { - u8 sgl_ptr; - u8 dsgl_ptr; - __le16 sge_offset; - __le16 dsge_offset; -}; - -struct iscsi_phys_sgl_ctx { - struct regpair sgl_base; - struct regpair dsgl_base; - u8 sgl_size; - u8 dsgl_size; - __le16 reserved; - struct iscsi_sgl_var_params var_params[2]; -}; - -union iscsi_data_desc_ctx { - struct iscsi_virt_sgl_ctx virt_sgl; - struct iscsi_phys_sgl_ctx phys_sgl; - struct iscsi_cached_sge_ctx cached_sge; -}; struct iscsi_debug_modes { u8 flags; @@ -771,8 +738,10 @@ struct iscsi_debug_modes { #define ISCSI_DEBUG_MODES_ASSERT_IF_RECV_REJECT_OR_ASYNC_SHIFT 4 #define ISCSI_DEBUG_MODES_ASSERT_IF_RECV_NOP_MASK 0x1 #define ISCSI_DEBUG_MODES_ASSERT_IF_RECV_NOP_SHIFT 5 -#define ISCSI_DEBUG_MODES_RESERVED0_MASK 0x3 -#define ISCSI_DEBUG_MODES_RESERVED0_SHIFT 6 +#define ISCSI_DEBUG_MODES_ASSERT_IF_DATA_DIGEST_ERROR_MASK 0x1 +#define ISCSI_DEBUG_MODES_ASSERT_IF_DATA_DIGEST_ERROR_SHIFT 6 +#define ISCSI_DEBUG_MODES_ASSERT_IF_DIF_ERROR_MASK 0x1 +#define ISCSI_DEBUG_MODES_ASSERT_IF_DIF_ERROR_SHIFT 7 }; struct iscsi_dif_flags { @@ -806,7 +775,6 @@ enum iscsi_eqe_opcode { ISCSI_EVENT_TYPE_ASYN_FIN_WAIT2, ISCSI_EVENT_TYPE_ISCSI_CONN_ERROR, ISCSI_EVENT_TYPE_TCP_CONN_ERROR, - ISCSI_EVENT_TYPE_ASYN_DELETE_OOO_ISLES, MAX_ISCSI_EQE_OPCODE }; @@ -856,31 +824,11 @@ enum iscsi_error_types { ISCSI_CONN_ERROR_PROTOCOL_ERR_DIF_TX, ISCSI_CONN_ERROR_SENSE_DATA_LENGTH, ISCSI_CONN_ERROR_DATA_PLACEMENT_ERROR, + ISCSI_CONN_ERROR_INVALID_ITT, ISCSI_ERROR_UNKNOWN, MAX_ISCSI_ERROR_TYPES }; -struct iscsi_mflags { - u8 mflags; -#define ISCSI_MFLAGS_SLOW_IO_MASK 0x1 -#define ISCSI_MFLAGS_SLOW_IO_SHIFT 0 -#define ISCSI_MFLAGS_SINGLE_SGE_MASK 0x1 -#define ISCSI_MFLAGS_SINGLE_SGE_SHIFT 1 -#define ISCSI_MFLAGS_RESERVED_MASK 0x3F -#define ISCSI_MFLAGS_RESERVED_SHIFT 2 -}; - -struct iscsi_sgl { - struct regpair sgl_addr; - __le16 updated_sge_size; - __le16 updated_sge_offset; - __le32 byte_offset; -}; - -union iscsi_mstorm_sgl { - struct iscsi_sgl sgl_struct; - struct iscsi_sge single_sge; -}; enum iscsi_ramrod_cmd_id { ISCSI_RAMROD_CMD_ID_UNUSED = 0, @@ -896,10 +844,10 @@ enum iscsi_ramrod_cmd_id { struct iscsi_reg1 { __le32 reg1_map; -#define ISCSI_REG1_NUM_FAST_SGES_MASK 0x7 -#define ISCSI_REG1_NUM_FAST_SGES_SHIFT 0 -#define ISCSI_REG1_RESERVED1_MASK 0x1FFFFFFF -#define ISCSI_REG1_RESERVED1_SHIFT 3 +#define ISCSI_REG1_NUM_SGES_MASK 0xF +#define ISCSI_REG1_NUM_SGES_SHIFT 0 +#define ISCSI_REG1_RESERVED1_MASK 0xFFFFFFF +#define ISCSI_REG1_RESERVED1_SHIFT 4 }; union iscsi_seq_num { @@ -967,22 +915,33 @@ struct iscsi_spe_func_init { }; struct ystorm_iscsi_task_state { - union iscsi_data_desc_ctx sgl_ctx_union; - __le32 buffer_offset[2]; - __le16 bytes_nxt_dif; - __le16 rxmit_bytes_nxt_dif; - union iscsi_seq_num seq_num_union; - u8 dif_bytes_leftover; - u8 rxmit_dif_bytes_leftover; - __le16 reuse_count; - struct iscsi_dif_flags dif_flags; - u8 local_comp; + struct scsi_cached_sges data_desc; + struct scsi_sgl_params sgl_params; __le32 exp_r2t_sn; - __le32 sgl_offset[2]; + __le32 buffer_offset; + union iscsi_seq_num seq_num; + struct iscsi_dif_flags dif_flags; + u8 flags; +#define YSTORM_ISCSI_TASK_STATE_LOCAL_COMP_MASK 0x1 +#define YSTORM_ISCSI_TASK_STATE_LOCAL_COMP_SHIFT 0 +#define YSTORM_ISCSI_TASK_STATE_SLOW_IO_MASK 0x1 +#define YSTORM_ISCSI_TASK_STATE_SLOW_IO_SHIFT 1 +#define YSTORM_ISCSI_TASK_STATE_RESERVED0_MASK 0x3F +#define YSTORM_ISCSI_TASK_STATE_RESERVED0_SHIFT 2 +}; + +struct ystorm_iscsi_task_rxmit_opt { + __le32 fast_rxmit_sge_offset; + __le32 scan_start_buffer_offset; + __le32 fast_rxmit_buffer_offset; + u8 scan_start_sgl_index; + u8 fast_rxmit_sgl_index; + __le16 reserved; }; struct ystorm_iscsi_task_st_ctx { struct ystorm_iscsi_task_state state; + struct ystorm_iscsi_task_rxmit_opt rxmit_opt; union iscsi_task_hdr pdu_hdr; }; @@ -1152,25 +1111,16 @@ struct ustorm_iscsi_task_ag_ctx { }; struct mstorm_iscsi_task_st_ctx { - union iscsi_mstorm_sgl sgl_union; - struct iscsi_dif_flags dif_flags; - struct iscsi_mflags flags; - u8 sgl_size; - u8 host_sge_index; - __le16 dix_cur_sge_offset; - __le16 dix_cur_sge_size; - __le32 data_offset_rtid; - u8 dif_offset; - u8 dix_sgl_size; - u8 dix_sge_index; + struct scsi_cached_sges data_desc; + struct scsi_sgl_params sgl_params; + __le32 rem_task_size; + __le32 data_buffer_offset; u8 task_type; + struct iscsi_dif_flags dif_flags; + u8 reserved0[2]; struct regpair sense_db; - struct regpair dix_sgl_cur_sge; - __le32 rem_task_size; - __le16 reuse_count; - __le16 dif_data_residue; - u8 reserved0[4]; - __le32 reserved1[1]; + __le32 expected_itt; + __le32 reserved1; }; struct ustorm_iscsi_task_st_ctx { @@ -1184,7 +1134,7 @@ struct ustorm_iscsi_task_st_ctx { #define USTORM_ISCSI_TASK_ST_CTX_AHS_EXIST_SHIFT 0 #define USTORM_ISCSI_TASK_ST_CTX_RESERVED1_MASK 0x7F #define USTORM_ISCSI_TASK_ST_CTX_RESERVED1_SHIFT 1 - u8 reserved2; + struct iscsi_dif_flags dif_flags; __le16 reserved3; __le32 reserved4; __le32 reserved5; @@ -1207,10 +1157,10 @@ struct ustorm_iscsi_task_st_ctx { #define USTORM_ISCSI_TASK_ST_CTX_LOCAL_COMP_SHIFT 2 #define USTORM_ISCSI_TASK_ST_CTX_Q0_R2TQE_WRITE_MASK 0x1 #define USTORM_ISCSI_TASK_ST_CTX_Q0_R2TQE_WRITE_SHIFT 3 -#define USTORM_ISCSI_TASK_ST_CTX_TOTALDATAACKED_DONE_MASK 0x1 -#define USTORM_ISCSI_TASK_ST_CTX_TOTALDATAACKED_DONE_SHIFT 4 -#define USTORM_ISCSI_TASK_ST_CTX_HQSCANNED_DONE_MASK 0x1 -#define USTORM_ISCSI_TASK_ST_CTX_HQSCANNED_DONE_SHIFT 5 +#define USTORM_ISCSI_TASK_ST_CTX_TOTAL_DATA_ACKED_DONE_MASK 0x1 +#define USTORM_ISCSI_TASK_ST_CTX_TOTAL_DATA_ACKED_DONE_SHIFT 4 +#define USTORM_ISCSI_TASK_ST_CTX_HQ_SCANNED_DONE_MASK 0x1 +#define USTORM_ISCSI_TASK_ST_CTX_HQ_SCANNED_DONE_SHIFT 5 #define USTORM_ISCSI_TASK_ST_CTX_R2T2RECV_DONE_MASK 0x1 #define USTORM_ISCSI_TASK_ST_CTX_R2T2RECV_DONE_SHIFT 6 #define USTORM_ISCSI_TASK_ST_CTX_RESERVED0_MASK 0x1 @@ -1220,7 +1170,6 @@ struct ustorm_iscsi_task_st_ctx { struct iscsi_task_context { struct ystorm_iscsi_task_st_ctx ystorm_st_context; - struct regpair ystorm_st_padding[2]; struct ystorm_iscsi_task_ag_ctx ystorm_ag_context; struct regpair ystorm_ag_padding[2]; struct tdif_task_context tdif_context; @@ -1272,32 +1221,22 @@ struct iscsi_uhqe { #define ISCSI_UHQE_TASK_ID_LO_SHIFT 24 }; -struct iscsi_wqe_field { - __le32 contlen_cdbsize_field; -#define ISCSI_WQE_FIELD_CONT_LEN_MASK 0xFFFFFF -#define ISCSI_WQE_FIELD_CONT_LEN_SHIFT 0 -#define ISCSI_WQE_FIELD_CDB_SIZE_MASK 0xFF -#define ISCSI_WQE_FIELD_CDB_SIZE_SHIFT 24 -}; - -union iscsi_wqe_field_union { - struct iscsi_wqe_field cont_field; - __le32 prev_tid; -}; struct iscsi_wqe { __le16 task_id; u8 flags; #define ISCSI_WQE_WQE_TYPE_MASK 0x7 #define ISCSI_WQE_WQE_TYPE_SHIFT 0 -#define ISCSI_WQE_NUM_FAST_SGES_MASK 0x7 -#define ISCSI_WQE_NUM_FAST_SGES_SHIFT 3 -#define ISCSI_WQE_PTU_INVALIDATE_MASK 0x1 -#define ISCSI_WQE_PTU_INVALIDATE_SHIFT 6 +#define ISCSI_WQE_NUM_SGES_MASK 0xF +#define ISCSI_WQE_NUM_SGES_SHIFT 3 #define ISCSI_WQE_RESPONSE_MASK 0x1 #define ISCSI_WQE_RESPONSE_SHIFT 7 struct iscsi_dif_flags prot_flags; - union iscsi_wqe_field_union cont_prevtid_union; + __le32 contlen_cdbsize; +#define ISCSI_WQE_CONT_LEN_MASK 0xFFFFFF +#define ISCSI_WQE_CONT_LEN_SHIFT 0 +#define ISCSI_WQE_CDB_SIZE_MASK 0xFF +#define ISCSI_WQE_CDB_SIZE_SHIFT 24 }; enum iscsi_wqe_type { @@ -1318,17 +1257,15 @@ struct iscsi_xhqe { u8 total_ahs_length; u8 opcode; u8 flags; -#define ISCSI_XHQE_NUM_FAST_SGES_MASK 0x7 -#define ISCSI_XHQE_NUM_FAST_SGES_SHIFT 0 -#define ISCSI_XHQE_FINAL_MASK 0x1 -#define ISCSI_XHQE_FINAL_SHIFT 3 -#define ISCSI_XHQE_SUPER_IO_MASK 0x1 -#define ISCSI_XHQE_SUPER_IO_SHIFT 4 -#define ISCSI_XHQE_STATUS_BIT_MASK 0x1 -#define ISCSI_XHQE_STATUS_BIT_SHIFT 5 -#define ISCSI_XHQE_RESERVED_MASK 0x3 -#define ISCSI_XHQE_RESERVED_SHIFT 6 - union iscsi_seq_num seq_num_union; +#define ISCSI_XHQE_FINAL_MASK 0x1 +#define ISCSI_XHQE_FINAL_SHIFT 0 +#define ISCSI_XHQE_STATUS_BIT_MASK 0x1 +#define ISCSI_XHQE_STATUS_BIT_SHIFT 1 +#define ISCSI_XHQE_NUM_SGES_MASK 0xF +#define ISCSI_XHQE_NUM_SGES_SHIFT 2 +#define ISCSI_XHQE_RESERVED0_MASK 0x3 +#define ISCSI_XHQE_RESERVED0_SHIFT 6 + union iscsi_seq_num seq_num; __le16 reserved1; }; diff --git a/include/linux/qed/roce_common.h b/include/linux/qed/roce_common.h index bad02df213df..866f063026de 100644 --- a/include/linux/qed/roce_common.h +++ b/include/linux/qed/roce_common.h @@ -38,4 +38,21 @@ #define ROCE_MAX_QPS (32 * 1024) +enum roce_async_events_type { + ROCE_ASYNC_EVENT_NONE = 0, + ROCE_ASYNC_EVENT_COMM_EST = 1, + ROCE_ASYNC_EVENT_SQ_DRAINED, + ROCE_ASYNC_EVENT_SRQ_LIMIT, + ROCE_ASYNC_EVENT_LAST_WQE_REACHED, + ROCE_ASYNC_EVENT_CQ_ERR, + ROCE_ASYNC_EVENT_LOCAL_INVALID_REQUEST_ERR, + ROCE_ASYNC_EVENT_LOCAL_CATASTROPHIC_ERR, + ROCE_ASYNC_EVENT_LOCAL_ACCESS_ERR, + ROCE_ASYNC_EVENT_QP_CATASTROPHIC_ERR, + ROCE_ASYNC_EVENT_CQ_OVERFLOW_ERR, + ROCE_ASYNC_EVENT_SRQ_EMPTY, + ROCE_ASYNC_EVENT_DESTROY_QP_DONE, + MAX_ROCE_ASYNC_EVENTS_TYPE +}; + #endif /* __ROCE_COMMON__ */ diff --git a/include/linux/qed/storage_common.h b/include/linux/qed/storage_common.h index 03f3e37ab059..08df82a096b6 100644 --- a/include/linux/qed/storage_common.h +++ b/include/linux/qed/storage_common.h @@ -40,6 +40,8 @@ #define BDQ_ID_IMM_DATA (1) #define BDQ_NUM_IDS (2) +#define SCSI_NUM_SGES_SLOW_SGL_THR 8 + #define BDQ_MAX_EXTERNAL_RING_SIZE (1 << 15) struct scsi_bd { @@ -52,6 +54,16 @@ struct scsi_bdq_ram_drv_data { __le16 reserved0[3]; }; +struct scsi_sge { + struct regpair sge_addr; + __le32 sge_len; + __le32 reserved; +}; + +struct scsi_cached_sges { + struct scsi_sge sge[4]; +}; + struct scsi_drv_cmdq { __le16 cmdq_cons; __le16 reserved0; @@ -99,11 +111,19 @@ struct scsi_ram_per_bdq_resource_drv_data { struct scsi_bdq_ram_drv_data drv_data_per_bdq_id[BDQ_NUM_IDS]; }; -struct scsi_sge { - struct regpair sge_addr; - __le16 sge_len; - __le16 reserved0; - __le32 reserved1; +enum scsi_sgl_mode { + SCSI_TX_SLOW_SGL, + SCSI_FAST_SGL, + MAX_SCSI_SGL_MODE +}; + +struct scsi_sgl_params { + struct regpair sgl_addr; + __le32 sgl_total_length; + __le32 sge_offset; + __le16 sgl_num_sges; + u8 sgl_index; + u8 reserved; }; struct scsi_terminate_extra_params { diff --git a/include/linux/qed/tcp_common.h b/include/linux/qed/tcp_common.h index 46fe7856f1b2..a5e843268f0e 100644 --- a/include/linux/qed/tcp_common.h +++ b/include/linux/qed/tcp_common.h @@ -173,6 +173,7 @@ enum tcp_seg_placement_event { TCP_EVENT_ADD_ISLE_RIGHT, TCP_EVENT_ADD_ISLE_LEFT, TCP_EVENT_JOIN, + TCP_EVENT_DELETE_ISLES, TCP_EVENT_NOP, MAX_TCP_SEG_PLACEMENT_EVENT }; -- cgit From c55fa3cccbc2c672e7f118be8f7484e53a8e9e77 Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Sat, 11 Mar 2017 19:41:36 -0500 Subject: atm: remove an unnecessary loop Andrey reported this kernel warning: WARNING: CPU: 0 PID: 4114 at kernel/sched/core.c:7737 __might_sleep+0x149/0x1a0 do not call blocking ops when !TASK_RUNNING; state=1 set at [] prepare_to_wait+0x182/0x530 The deeply nested alloc_skb is a problem. Diagnosis: nesting is wrong. It makes zero sense. Fix it and the implicit task state change problem automagically goes away. alloc_skb() does not need to be in the "while" loop. alloc_skb() does not need to be in the {prepare_to_wait/add_wait_queue ... finish_wait/remove_wait_queue} block. I claim that: - alloc_tx() should only perform the "wait_for_decent_tx_drain" part - alloc_skb() ought to be done directly in vcc_sendmsg - alloc_skb() failure can be handled gracefully in vcc_sendmsg - alloc_skb() may use a (m->msg_flags & MSG_DONTWAIT) dependent GFP_{KERNEL / ATOMIC} flag Reported-by: Andrey Konovalov Reviewed-and-Tested-by: Chas Williams <3chas3@gmail.com> Signed-off-by: Chas Williams <3chas3@gmail.com> Signed-off-by: David S. Miller --- net/atm/common.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/net/atm/common.c b/net/atm/common.c index 9613381f5db0..f06422f4108d 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -62,21 +62,16 @@ static void vcc_remove_socket(struct sock *sk) write_unlock_irq(&vcc_sklist_lock); } -static struct sk_buff *alloc_tx(struct atm_vcc *vcc, unsigned int size) +static bool vcc_tx_ready(struct atm_vcc *vcc, unsigned int size) { - struct sk_buff *skb; struct sock *sk = sk_atm(vcc); if (sk_wmem_alloc_get(sk) && !atm_may_send(vcc, size)) { pr_debug("Sorry: wmem_alloc = %d, size = %d, sndbuf = %d\n", sk_wmem_alloc_get(sk), size, sk->sk_sndbuf); - return NULL; + return false; } - while (!(skb = alloc_skb(size, GFP_KERNEL))) - schedule(); - pr_debug("%d += %d\n", sk_wmem_alloc_get(sk), skb->truesize); - atomic_add(skb->truesize, &sk->sk_wmem_alloc); - return skb; + return true; } static void vcc_sock_destruct(struct sock *sk) @@ -606,7 +601,7 @@ int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size) eff = (size+3) & ~3; /* align to word boundary */ prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); error = 0; - while (!(skb = alloc_tx(vcc, eff))) { + while (!vcc_tx_ready(vcc, eff)) { if (m->msg_flags & MSG_DONTWAIT) { error = -EAGAIN; break; @@ -628,6 +623,15 @@ int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size) finish_wait(sk_sleep(sk), &wait); if (error) goto out; + + skb = alloc_skb(eff, GFP_KERNEL); + if (!skb) { + error = -ENOMEM; + goto out; + } + pr_debug("%d += %d\n", sk_wmem_alloc_get(sk), skb->truesize); + atomic_add(skb->truesize, &sk->sk_wmem_alloc); + skb->dev = NULL; /* for paths shared with net_device interfaces */ ATM_SKB(skb)->atm_options = vcc->atm_options; if (!copy_from_iter_full(skb_put(skb, size), size, &m->msg_iter)) { -- cgit From cda792c3f9b77acc3f42e4947d30f3a46e4da183 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Mon, 13 Mar 2017 17:41:31 -0700 Subject: net: phy: bcm-phylib: replace obsolete EEE macro references The macros MDIO_AN_EEE_ADV_100TX and MDIO_AN_EEE_ADV_1000T are now considered obsolete and are replaced in the kernel with the generic macros MDIO_EEE_100TX and MDIO_EEE_1000T respectively. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/bcm-phy-lib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c index ab9ad689617c..9656dbeb5de5 100644 --- a/drivers/net/phy/bcm-phy-lib.c +++ b/drivers/net/phy/bcm-phy-lib.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Broadcom Corporation + * Copyright (C) 2015-2017 Broadcom * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -221,9 +221,9 @@ int bcm_phy_set_eee(struct phy_device *phydev, bool enable) return val; if (enable) - val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); + val |= (MDIO_EEE_100TX | MDIO_EEE_1000T); else - val &= ~(MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); + val &= ~(MDIO_EEE_100TX | MDIO_EEE_1000T); phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, MDIO_MMD_AN, (u32)val); -- cgit From 83ee102a6998f808ac4c626e8f72344f0a355527 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Mon, 13 Mar 2017 17:41:32 -0700 Subject: net: phy: bcm7xxx: add support for 28nm EPHY This commit adds support for the internal fast ethernet 10/100 PHY found in the BCM7260, BCM7268, and BCM7271 devices. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/bcm7xxx.c | 215 +++++++++++++++++++++++++++++++++++++++++++++- include/linux/brcmphy.h | 3 + 2 files changed, 216 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index d1c2614dad3a..caa9f6e17f34 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -1,7 +1,7 @@ /* * Broadcom BCM7xxx internal transceivers support. * - * Copyright (C) 2014, Broadcom Corporation + * Copyright (C) 2014-2017 Broadcom * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,7 +19,7 @@ /* Broadcom BCM7xxx internal PHY registers */ -/* 40nm only register definitions */ +/* EPHY only register definitions */ #define MII_BCM7XXX_100TX_AUX_CTL 0x10 #define MII_BCM7XXX_100TX_FALSE_CAR 0x13 #define MII_BCM7XXX_100TX_DISC 0x14 @@ -27,6 +27,19 @@ #define MII_BCM7XXX_64CLK_MDIO BIT(12) #define MII_BCM7XXX_TEST 0x1f #define MII_BCM7XXX_SHD_MODE_2 BIT(2) +#define MII_BCM7XXX_SHD_2_ADDR_CTRL 0xe +#define MII_BCM7XXX_SHD_2_CTRL_STAT 0xf +#define MII_BCM7XXX_SHD_2_BIAS_TRIM 0x1a +#define MII_BCM7XXX_SHD_3_AN_EEE_ADV 0x3 +#define MII_BCM7XXX_SHD_3_PCS_CTRL_2 0x6 +#define MII_BCM7XXX_PCS_CTRL_2_DEF 0x4400 +#define MII_BCM7XXX_SHD_3_AN_STAT 0xb +#define MII_BCM7XXX_AN_NULL_MSG_EN BIT(0) +#define MII_BCM7XXX_AN_EEE_EN BIT(1) +#define MII_BCM7XXX_SHD_3_EEE_THRESH 0xe +#define MII_BCM7XXX_EEE_THRESH_DEF 0x50 +#define MII_BCM7XXX_SHD_3_TL4 0x23 +#define MII_BCM7XXX_TL4_RST_MSK (BIT(2) | BIT(1)) /* 28nm only register definitions */ #define MISC_ADDR(base, channel) base, channel @@ -286,6 +299,181 @@ static int phy_set_clr_bits(struct phy_device *dev, int location, return v; } +static int bcm7xxx_28nm_ephy_01_afe_config_init(struct phy_device *phydev) +{ + int ret; + + /* set shadow mode 2 */ + ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, + MII_BCM7XXX_SHD_MODE_2, 0); + if (ret < 0) + return ret; + + /* Set current trim values INT_trim = -1, Ext_trim =0 */ + ret = phy_write(phydev, MII_BCM7XXX_SHD_2_BIAS_TRIM, 0x3BE0); + if (ret < 0) + goto reset_shadow_mode; + + /* Cal reset */ + ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, + MII_BCM7XXX_SHD_3_TL4); + if (ret < 0) + goto reset_shadow_mode; + ret = phy_set_clr_bits(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, + MII_BCM7XXX_TL4_RST_MSK, 0); + if (ret < 0) + goto reset_shadow_mode; + + /* Cal reset disable */ + ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, + MII_BCM7XXX_SHD_3_TL4); + if (ret < 0) + goto reset_shadow_mode; + ret = phy_set_clr_bits(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, + 0, MII_BCM7XXX_TL4_RST_MSK); + if (ret < 0) + goto reset_shadow_mode; + +reset_shadow_mode: + /* reset shadow mode 2 */ + ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, + MII_BCM7XXX_SHD_MODE_2); + if (ret < 0) + return ret; + + return 0; +} + +/* The 28nm EPHY does not support Clause 45 (MMD) used by bcm-phy-lib */ +static int bcm7xxx_28nm_ephy_apd_enable(struct phy_device *phydev) +{ + int ret; + + /* set shadow mode 1 */ + ret = phy_set_clr_bits(phydev, MII_BRCM_FET_BRCMTEST, + MII_BRCM_FET_BT_SRE, 0); + if (ret < 0) + return ret; + + /* Enable auto-power down */ + ret = phy_set_clr_bits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2, + MII_BRCM_FET_SHDW_AS2_APDE, 0); + if (ret < 0) + return ret; + + /* reset shadow mode 1 */ + ret = phy_set_clr_bits(phydev, MII_BRCM_FET_BRCMTEST, 0, + MII_BRCM_FET_BT_SRE); + if (ret < 0) + return ret; + + return 0; +} + +static int bcm7xxx_28nm_ephy_eee_enable(struct phy_device *phydev) +{ + int ret; + + /* set shadow mode 2 */ + ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, + MII_BCM7XXX_SHD_MODE_2, 0); + if (ret < 0) + return ret; + + /* Advertise supported modes */ + ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, + MII_BCM7XXX_SHD_3_AN_EEE_ADV); + if (ret < 0) + goto reset_shadow_mode; + ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, + MDIO_EEE_100TX); + if (ret < 0) + goto reset_shadow_mode; + + /* Restore Defaults */ + ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, + MII_BCM7XXX_SHD_3_PCS_CTRL_2); + if (ret < 0) + goto reset_shadow_mode; + ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, + MII_BCM7XXX_PCS_CTRL_2_DEF); + if (ret < 0) + goto reset_shadow_mode; + + ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, + MII_BCM7XXX_SHD_3_EEE_THRESH); + if (ret < 0) + goto reset_shadow_mode; + ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, + MII_BCM7XXX_EEE_THRESH_DEF); + if (ret < 0) + goto reset_shadow_mode; + + /* Enable EEE autonegotiation */ + ret = phy_write(phydev, MII_BCM7XXX_SHD_2_ADDR_CTRL, + MII_BCM7XXX_SHD_3_AN_STAT); + if (ret < 0) + goto reset_shadow_mode; + ret = phy_write(phydev, MII_BCM7XXX_SHD_2_CTRL_STAT, + (MII_BCM7XXX_AN_NULL_MSG_EN | MII_BCM7XXX_AN_EEE_EN)); + if (ret < 0) + goto reset_shadow_mode; + +reset_shadow_mode: + /* reset shadow mode 2 */ + ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, + MII_BCM7XXX_SHD_MODE_2); + if (ret < 0) + return ret; + + /* Restart autoneg */ + phy_write(phydev, MII_BMCR, + (BMCR_SPEED100 | BMCR_ANENABLE | BMCR_ANRESTART)); + + return 0; +} + +static int bcm7xxx_28nm_ephy_config_init(struct phy_device *phydev) +{ + u8 rev = phydev->phy_id & ~phydev->drv->phy_id_mask; + int ret = 0; + + pr_info_once("%s: %s PHY revision: 0x%02x\n", + phydev_name(phydev), phydev->drv->name, rev); + + /* Dummy read to a register to workaround a possible issue upon reset + * where the internal inverter may not allow the first MDIO transaction + * to pass the MDIO management controller and make us return 0xffff for + * such reads. + */ + phy_read(phydev, MII_BMSR); + + /* Apply AFE software work-around if necessary */ + if (rev == 0x01) { + ret = bcm7xxx_28nm_ephy_01_afe_config_init(phydev); + if (ret) + return ret; + } + + ret = bcm7xxx_28nm_ephy_eee_enable(phydev); + if (ret) + return ret; + + return bcm7xxx_28nm_ephy_apd_enable(phydev); +} + +static int bcm7xxx_28nm_ephy_resume(struct phy_device *phydev) +{ + int ret; + + /* Re-apply workarounds coming out suspend/resume */ + ret = bcm7xxx_28nm_ephy_config_init(phydev); + if (ret) + return ret; + + return genphy_config_aneg(phydev); +} + static int bcm7xxx_config_init(struct phy_device *phydev) { int ret; @@ -434,6 +622,23 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev) .probe = bcm7xxx_28nm_probe, \ } +#define BCM7XXX_28NM_EPHY(_oui, _name) \ +{ \ + .phy_id = (_oui), \ + .phy_id_mask = 0xfffffff0, \ + .name = _name, \ + .features = PHY_BASIC_FEATURES, \ + .flags = PHY_IS_INTERNAL, \ + .config_init = bcm7xxx_28nm_ephy_config_init, \ + .config_aneg = genphy_config_aneg, \ + .read_status = genphy_read_status, \ + .resume = bcm7xxx_28nm_ephy_resume, \ + .get_sset_count = bcm_phy_get_sset_count, \ + .get_strings = bcm_phy_get_strings, \ + .get_stats = bcm7xxx_28nm_get_phy_stats, \ + .probe = bcm7xxx_28nm_probe, \ +} + #define BCM7XXX_40NM_EPHY(_oui, _name) \ { \ .phy_id = (_oui), \ @@ -450,6 +655,9 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev) static struct phy_driver bcm7xxx_driver[] = { BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"), + BCM7XXX_28NM_EPHY(PHY_ID_BCM7260, "Broadcom BCM7260"), + BCM7XXX_28NM_EPHY(PHY_ID_BCM7268, "Broadcom BCM7268"), + BCM7XXX_28NM_EPHY(PHY_ID_BCM7271, "Broadcom BCM7271"), BCM7XXX_28NM_GPHY(PHY_ID_BCM7278, "Broadcom BCM7278"), BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"), BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"), @@ -466,6 +674,9 @@ static struct phy_driver bcm7xxx_driver[] = { static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = { { PHY_ID_BCM7250, 0xfffffff0, }, + { PHY_ID_BCM7260, 0xfffffff0, }, + { PHY_ID_BCM7268, 0xfffffff0, }, + { PHY_ID_BCM7271, 0xfffffff0, }, { PHY_ID_BCM7278, 0xfffffff0, }, { PHY_ID_BCM7364, 0xfffffff0, }, { PHY_ID_BCM7366, 0xfffffff0, }, diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index 55e517130311..abcda9b458ab 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -25,6 +25,9 @@ #define PHY_ID_BCM57780 0x03625d90 #define PHY_ID_BCM7250 0xae025280 +#define PHY_ID_BCM7260 0xae025190 +#define PHY_ID_BCM7268 0xae025090 +#define PHY_ID_BCM7271 0xae0253b0 #define PHY_ID_BCM7278 0xae0251a0 #define PHY_ID_BCM7364 0xae025260 #define PHY_ID_BCM7366 0x600d8490 -- cgit From c298ede2fe21e034ab6ba4f15340fadc03ae9663 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Mon, 13 Mar 2017 17:41:33 -0700 Subject: net: bcmgenet: simplify circular pointer arithmetic A 2's complement subtraction will always do a borrow, so masking off the sign bits is the same as conditionally adding (mask+1). Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index f92896835d2a..2c008b09c4e3 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1,7 +1,7 @@ /* * Broadcom GENET (Gigabit Ethernet) controller driver * - * Copyright (c) 2014 Broadcom Corporation + * Copyright (c) 2014-2017 Broadcom * * 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 @@ -1175,13 +1175,9 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev, unsigned int txbds_processed = 0; /* Compute how many buffers are transmitted since last xmit call */ - c_index = bcmgenet_tdma_ring_readl(priv, ring->index, TDMA_CONS_INDEX); - c_index &= DMA_C_INDEX_MASK; - - if (likely(c_index >= ring->c_index)) - txbds_ready = c_index - ring->c_index; - else - txbds_ready = (DMA_C_INDEX_MASK + 1) - ring->c_index + c_index; + c_index = bcmgenet_tdma_ring_readl(priv, ring->index, TDMA_CONS_INDEX) + & DMA_C_INDEX_MASK; + txbds_ready = (c_index - ring->c_index) & DMA_C_INDEX_MASK; netif_dbg(priv, tx_done, dev, "%s ring=%d old_c_index=%u c_index=%u txbds_ready=%u\n", @@ -1611,12 +1607,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, } p_index &= DMA_P_INDEX_MASK; - - if (likely(p_index >= ring->c_index)) - rxpkttoprocess = p_index - ring->c_index; - else - rxpkttoprocess = (DMA_C_INDEX_MASK + 1) - ring->c_index + - p_index; + rxpkttoprocess = (p_index - ring->c_index) & DMA_C_INDEX_MASK; netif_dbg(priv, rx_status, dev, "RDMA: rxpkttoprocess=%d\n", rxpkttoprocess); -- cgit From 556c2cf4eff18bb7f41056796d2d37b48239278b Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Mon, 13 Mar 2017 17:41:34 -0700 Subject: net: bcmgenet: remove meaningless lines An assortment of non-functional lines are removed to reduce confusion and some typos in comments are corrected. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 2c008b09c4e3..22c92f5a9829 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -605,7 +605,7 @@ static int bcmgenet_set_coalesce(struct net_device *dev, /* GENET TDMA hardware does not support a configurable timeout, but will * always generate an interrupt either after MBDONE packets have been - * transmitted, or when the ring is emtpy. + * transmitted, or when the ring is empty. */ if (ec->tx_coalesce_usecs || ec->tx_coalesce_usecs_high || ec->tx_coalesce_usecs_irq || ec->tx_coalesce_usecs_low) @@ -1834,10 +1834,8 @@ static void bcmgenet_intr_disable(struct bcmgenet_priv *priv) /* Mask all interrupts.*/ bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_MASK_SET); bcmgenet_intrl2_0_writel(priv, 0xFFFFFFFF, INTRL2_CPU_CLEAR); - bcmgenet_intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); bcmgenet_intrl2_1_writel(priv, 0xFFFFFFFF, INTRL2_CPU_MASK_SET); bcmgenet_intrl2_1_writel(priv, 0xFFFFFFFF, INTRL2_CPU_CLEAR); - bcmgenet_intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); } static void bcmgenet_link_intr_enable(struct bcmgenet_priv *priv) @@ -1926,7 +1924,6 @@ static int init_umac(struct bcmgenet_priv *priv) bcmgenet_intrl2_0_writel(priv, int0_enable, INTRL2_CPU_MASK_CLEAR); bcmgenet_intrl2_1_writel(priv, int1_enable, INTRL2_CPU_MASK_CLEAR); - /* Enable rx/tx engine.*/ dev_dbg(kdev, "done init umac\n"); return 0; @@ -2836,7 +2833,7 @@ static int bcmgenet_close(struct net_device *dev) if (ret) return ret; - /* Disable MAC transmit. TX DMA disabled have to done before this */ + /* Disable MAC transmit. TX DMA disabled must be done before this */ umac_enable_set(priv, CMD_TX_EN, false); /* tx reclaim */ @@ -3115,22 +3112,18 @@ static void bcmgenet_set_hw_params(struct bcmgenet_priv *priv) bcmgenet_dma_regs = bcmgenet_dma_regs_v3plus; genet_dma_ring_regs = genet_dma_ring_regs_v4; priv->dma_rx_chk_bit = DMA_RX_CHK_V3PLUS; - priv->version = GENET_V4; } else if (GENET_IS_V3(priv)) { bcmgenet_dma_regs = bcmgenet_dma_regs_v3plus; genet_dma_ring_regs = genet_dma_ring_regs_v123; priv->dma_rx_chk_bit = DMA_RX_CHK_V3PLUS; - priv->version = GENET_V3; } else if (GENET_IS_V2(priv)) { bcmgenet_dma_regs = bcmgenet_dma_regs_v2; genet_dma_ring_regs = genet_dma_ring_regs_v123; priv->dma_rx_chk_bit = DMA_RX_CHK_V12; - priv->version = GENET_V2; } else if (GENET_IS_V1(priv)) { bcmgenet_dma_regs = bcmgenet_dma_regs_v1; genet_dma_ring_regs = genet_dma_ring_regs_v123; priv->dma_rx_chk_bit = DMA_RX_CHK_V12; - priv->version = GENET_V1; } /* enum genet_version starts at 1 */ @@ -3397,7 +3390,7 @@ static int bcmgenet_suspend(struct device *d) if (ret) return ret; - /* Disable MAC transmit. TX DMA disabled have to done before this */ + /* Disable MAC transmit. TX DMA disabled must be done before this */ umac_enable_set(priv, CMD_TX_EN, false); /* tx reclaim */ -- cgit From 6689da155bdcd17abfe4d3a8b1e245d9ed4b5f2c Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Mon, 13 Mar 2017 17:41:35 -0700 Subject: net: bcmgenet: manage dma interrupts in napi code This commit moves DMA interrupt enabling out of init_umac() and adds the masking of these interrupts to the napi enable and disable code. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 39 +++++++++++++++----------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 22c92f5a9829..9be884021679 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1862,8 +1862,6 @@ static int init_umac(struct bcmgenet_priv *priv) int ret; u32 reg; u32 int0_enable = 0; - u32 int1_enable = 0; - int i; dev_dbg(&priv->pdev->dev, "bcmgenet: init_umac\n"); @@ -1890,12 +1888,6 @@ static int init_umac(struct bcmgenet_priv *priv) bcmgenet_intr_disable(priv); - /* Enable Rx default queue 16 interrupts */ - int0_enable |= UMAC_IRQ_RXDMA_DONE; - - /* Enable Tx default queue 16 interrupts */ - int0_enable |= UMAC_IRQ_TXDMA_DONE; - /* Configure backpressure vectors for MoCA */ if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) { reg = bcmgenet_bp_mc_get(priv); @@ -1913,16 +1905,7 @@ static int init_umac(struct bcmgenet_priv *priv) if (priv->hw_params->flags & GENET_HAS_MDIO_INTR) int0_enable |= (UMAC_IRQ_MDIO_DONE | UMAC_IRQ_MDIO_ERROR); - /* Enable Rx priority queue interrupts */ - for (i = 0; i < priv->hw_params->rx_queues; ++i) - int1_enable |= (1 << (UMAC_IRQ1_RX_INTR_SHIFT + i)); - - /* Enable Tx priority queue interrupts */ - for (i = 0; i < priv->hw_params->tx_queues; ++i) - int1_enable |= (1 << i); - bcmgenet_intrl2_0_writel(priv, int0_enable, INTRL2_CPU_MASK_CLEAR); - bcmgenet_intrl2_1_writel(priv, int1_enable, INTRL2_CPU_MASK_CLEAR); dev_dbg(kdev, "done init umac\n"); @@ -2055,22 +2038,33 @@ static void bcmgenet_init_tx_napi(struct bcmgenet_priv *priv) static void bcmgenet_enable_tx_napi(struct bcmgenet_priv *priv) { unsigned int i; + u32 int0_enable = UMAC_IRQ_TXDMA_DONE; + u32 int1_enable = 0; struct bcmgenet_tx_ring *ring; for (i = 0; i < priv->hw_params->tx_queues; ++i) { ring = &priv->tx_rings[i]; napi_enable(&ring->napi); + int1_enable |= (1 << i); } ring = &priv->tx_rings[DESC_INDEX]; napi_enable(&ring->napi); + + bcmgenet_intrl2_0_writel(priv, int0_enable, INTRL2_CPU_MASK_CLEAR); + bcmgenet_intrl2_1_writel(priv, int1_enable, INTRL2_CPU_MASK_CLEAR); } static void bcmgenet_disable_tx_napi(struct bcmgenet_priv *priv) { unsigned int i; + u32 int0_disable = UMAC_IRQ_TXDMA_DONE; + u32 int1_disable = 0xffff; struct bcmgenet_tx_ring *ring; + bcmgenet_intrl2_0_writel(priv, int0_disable, INTRL2_CPU_MASK_SET); + bcmgenet_intrl2_1_writel(priv, int1_disable, INTRL2_CPU_MASK_SET); + for (i = 0; i < priv->hw_params->tx_queues; ++i) { ring = &priv->tx_rings[i]; napi_disable(&ring->napi); @@ -2183,22 +2177,33 @@ static void bcmgenet_init_rx_napi(struct bcmgenet_priv *priv) static void bcmgenet_enable_rx_napi(struct bcmgenet_priv *priv) { unsigned int i; + u32 int0_enable = UMAC_IRQ_RXDMA_DONE; + u32 int1_enable = 0; struct bcmgenet_rx_ring *ring; for (i = 0; i < priv->hw_params->rx_queues; ++i) { ring = &priv->rx_rings[i]; napi_enable(&ring->napi); + int1_enable |= (1 << (UMAC_IRQ1_RX_INTR_SHIFT + i)); } ring = &priv->rx_rings[DESC_INDEX]; napi_enable(&ring->napi); + + bcmgenet_intrl2_0_writel(priv, int0_enable, INTRL2_CPU_MASK_CLEAR); + bcmgenet_intrl2_1_writel(priv, int1_enable, INTRL2_CPU_MASK_CLEAR); } static void bcmgenet_disable_rx_napi(struct bcmgenet_priv *priv) { unsigned int i; + u32 int0_disable = UMAC_IRQ_RXDMA_DONE; + u32 int1_disable = 0xffff << UMAC_IRQ1_RX_INTR_SHIFT; struct bcmgenet_rx_ring *ring; + bcmgenet_intrl2_0_writel(priv, int0_disable, INTRL2_CPU_MASK_SET); + bcmgenet_intrl2_1_writel(priv, int1_disable, INTRL2_CPU_MASK_SET); + for (i = 0; i < priv->hw_params->rx_queues; ++i) { ring = &priv->rx_rings[i]; napi_disable(&ring->napi); -- cgit From b1ec494deea8322e88063c8a6bee903255a0590d Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Mon, 13 Mar 2017 17:41:36 -0700 Subject: net: bcmgenet: remove handling of wol interrupts from isr0 The bcmgenet_wol_isr() handler performs the necessary processing for waking from a GENET event. There is no necessary functionality behind servicing the UMAC_IRQ_MPD_R event in the handling of isr0. Therefore the code that unmasks and masks this interrupt and that gets invoked in response to it is removed by this commit. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 10 +--------- drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c | 15 +-------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 9be884021679..661ca1b39c89 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -2455,13 +2455,6 @@ static void bcmgenet_irq_task(struct work_struct *work) netif_dbg(priv, intr, priv->dev, "%s\n", __func__); - if (priv->irq0_stat & UMAC_IRQ_MPD_R) { - priv->irq0_stat &= ~UMAC_IRQ_MPD_R; - netif_dbg(priv, wol, priv->dev, - "magic packet detected, waking up\n"); - bcmgenet_power_up(priv, GENET_POWER_WOL_MAGIC); - } - /* Link UP/DOWN event */ if (priv->irq0_stat & UMAC_IRQ_LINK_EVENT) { phy_mac_interrupt(priv->phydev, @@ -2558,8 +2551,7 @@ static irqreturn_t bcmgenet_isr0(int irq, void *dev_id) UMAC_IRQ_PHY_DET_F | UMAC_IRQ_LINK_EVENT | UMAC_IRQ_HFB_SM | - UMAC_IRQ_HFB_MM | - UMAC_IRQ_MPD_R)) { + UMAC_IRQ_HFB_MM)) { /* all other interested interrupts handled in bottom half */ schedule_work(&priv->bcmgenet_irq_work); } diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c index b97122926d3a..2fbd027f0148 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet_wol.c @@ -1,7 +1,7 @@ /* * Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support * - * Copyright (c) 2014 Broadcom Corporation + * Copyright (c) 2014-2017 Broadcom * * 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 @@ -127,7 +127,6 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv, enum bcmgenet_power_mode mode) { struct net_device *dev = priv->dev; - u32 cpu_mask_clear; int retries = 0; u32 reg; @@ -173,18 +172,12 @@ int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv, bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); } - /* Enable the MPD interrupt */ - cpu_mask_clear = UMAC_IRQ_MPD_R; - - bcmgenet_intrl2_0_writel(priv, cpu_mask_clear, INTRL2_CPU_MASK_CLEAR); - return 0; } void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv, enum bcmgenet_power_mode mode) { - u32 cpu_mask_set; u32 reg; if (mode != GENET_POWER_WOL_MAGIC) { @@ -201,10 +194,4 @@ void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv, reg &= ~CMD_CRC_FWD; bcmgenet_umac_writel(priv, reg, UMAC_CMD); priv->crc_fwd_en = 0; - - /* Stop monitoring magic packet IRQ */ - cpu_mask_set = UMAC_IRQ_MPD_R; - - /* Stop monitoring magic packet IRQ */ - bcmgenet_intrl2_0_writel(priv, cpu_mask_set, INTRL2_CPU_MASK_SET); } -- cgit From d5810ca3252a5bf61e5b2fc789da456e0f65ba8c Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Mon, 13 Mar 2017 17:41:37 -0700 Subject: net: bcmgenet: clear status to reduce spurious interrupts Since the DMA interrupt status is latched and the DMA servicing can be polled, it is a good idea to clear the latched status of a DMA interrupt before performing the service that would be invoked by the interrupt. This prevents old status from causing spurious interrupts when the interrupt is unmasked at a later time. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 661ca1b39c89..1f94ba1773dd 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1174,6 +1174,14 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev, unsigned int txbds_ready; unsigned int txbds_processed = 0; + /* Clear status before servicing to reduce spurious interrupts */ + if (ring->index == DESC_INDEX) + bcmgenet_intrl2_0_writel(priv, UMAC_IRQ_TXDMA_DONE, + INTRL2_CPU_CLEAR); + else + bcmgenet_intrl2_1_writel(priv, (1 << ring->index), + INTRL2_CPU_CLEAR); + /* Compute how many buffers are transmitted since last xmit call */ c_index = bcmgenet_tdma_ring_readl(priv, ring->index, TDMA_CONS_INDEX) & DMA_C_INDEX_MASK; @@ -1584,10 +1592,21 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, unsigned long dma_flag; int len; unsigned int rxpktprocessed = 0, rxpkttoprocess; - unsigned int p_index; + unsigned int p_index, mask; unsigned int discards; unsigned int chksum_ok = 0; + /* Clear status before servicing to reduce spurious interrupts */ + if (ring->index == DESC_INDEX) { + bcmgenet_intrl2_0_writel(priv, UMAC_IRQ_RXDMA_DONE, + INTRL2_CPU_CLEAR); + } else { + mask = 1 << (UMAC_IRQ1_RX_INTR_SHIFT + ring->index); + bcmgenet_intrl2_1_writel(priv, + mask, + INTRL2_CPU_CLEAR); + } + p_index = bcmgenet_rdma_ring_readl(priv, ring->index, RDMA_PROD_INDEX); discards = (p_index >> DMA_P_INDEX_DISCARD_CNT_SHIFT) & -- cgit From c4d453d2b14d4c844fd6b492fbd16ce0615a59d8 Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Mon, 13 Mar 2017 17:41:38 -0700 Subject: net: bcmgenet: correct return value of __bcmgenet_tx_reclaim The reclaim function should return the number of buffer descriptors reclaimed, not just the number corresponding to skb packets. Also, remove the unnecessary computation when updating the consumer index. While this is not a functional problem it could degrade performance of napi in a fragmented transmit stream. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 1f94ba1773dd..d90d366b286f 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1218,7 +1218,7 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev, } ring->free_bds += txbds_processed; - ring->c_index = (ring->c_index + txbds_processed) & DMA_C_INDEX_MASK; + ring->c_index = c_index; dev->stats.tx_packets += pkts_compl; dev->stats.tx_bytes += bytes_compl; @@ -1231,7 +1231,7 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev, netif_tx_wake_queue(txq); } - return pkts_compl; + return txbds_processed; } static unsigned int bcmgenet_tx_reclaim(struct net_device *dev, -- cgit From 54fecff3461143bfc4c4f0431e9145e8748977ce Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Mon, 13 Mar 2017 17:41:39 -0700 Subject: net: bcmgenet: return EOPNOTSUPP for unknown ioctl commands This commit changes the ioctl handling behavior to return the EOPNOTSUPP error code instead of the EINVAL error code when an unknown ioctl command value is detected. It also removes some redundant parsing of the ioctl command value and allows the SIOCSHWTSTAMP value to be handled. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index d90d366b286f..3b49c14128e2 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1062,27 +1062,14 @@ static void bcmgenet_power_up(struct bcmgenet_priv *priv, static int bcmgenet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct bcmgenet_priv *priv = netdev_priv(dev); - int val = 0; if (!netif_running(dev)) return -EINVAL; - switch (cmd) { - case SIOCGMIIPHY: - case SIOCGMIIREG: - case SIOCSMIIREG: - if (!priv->phydev) - val = -ENODEV; - else - val = phy_mii_ioctl(priv->phydev, rq, cmd); - break; - - default: - val = -EINVAL; - break; - } + if (!priv->phydev) + return -ENODEV; - return val; + return phy_mii_ioctl(priv->phydev, rq, cmd); } static struct enet_cb *bcmgenet_get_txcb(struct bcmgenet_priv *priv, -- cgit From 75835695ad6362c94b918ef28799ed6b593f947c Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Mon, 13 Mar 2017 17:41:40 -0700 Subject: dt-bindings: net: document bcmgenet WoL interrupt A third interrupt cell can be provided to optionally specify the interrupt used for handling Wake on LAN events. Typically the wake up handling uses a separate interrupt controller, so the interrupts-extended property is used to accommodate this. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/brcm,bcmgenet.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/net/brcm,bcmgenet.txt b/Documentation/devicetree/bindings/net/brcm,bcmgenet.txt index 10587bdadbbe..01a70463cbc5 100644 --- a/Documentation/devicetree/bindings/net/brcm,bcmgenet.txt +++ b/Documentation/devicetree/bindings/net/brcm,bcmgenet.txt @@ -4,9 +4,12 @@ Required properties: - compatible: should contain one of "brcm,genet-v1", "brcm,genet-v2", "brcm,genet-v3", "brcm,genet-v4". - reg: address and length of the register set for the device -- interrupts: must be two cells, the first cell is the general purpose - interrupt line, while the second cell is the interrupt for the ring - RX and TX queues operating in ring mode +- interrupts and/or interrupts-extended: must be two cells, the first cell + is the general purpose interrupt line, while the second cell is the + interrupt for the ring RX and TX queues operating in ring mode. An + optional third interrupt cell for Wake-on-LAN can be specified. + See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt + for information on the property specifics. - phy-mode: see ethernet.txt file in the same directory - #address-cells: should be 1 - #size-cells: should be 1 -- cgit From 0ce5aa1d6c97425ae0fd4082066049edcaffe01b Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Mon, 13 Mar 2017 17:41:41 -0700 Subject: dt-bindings: net: update bcmgenet binding for GENETv5 The device tree documentation must be updated to reflect the new compatible strings "brcm,genet-v5" and "brcm,genet-mdio-v5" used by the GENETv5 driver. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/brcm,bcmgenet.txt | 10 +++++----- Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/net/brcm,bcmgenet.txt b/Documentation/devicetree/bindings/net/brcm,bcmgenet.txt index 01a70463cbc5..26c77d985faf 100644 --- a/Documentation/devicetree/bindings/net/brcm,bcmgenet.txt +++ b/Documentation/devicetree/bindings/net/brcm,bcmgenet.txt @@ -2,7 +2,7 @@ Required properties: - compatible: should contain one of "brcm,genet-v1", "brcm,genet-v2", - "brcm,genet-v3", "brcm,genet-v4". + "brcm,genet-v3", "brcm,genet-v4", "brcm,genet-v5". - reg: address and length of the register set for the device - interrupts and/or interrupts-extended: must be two cells, the first cell is the general purpose interrupt line, while the second cell is the @@ -32,15 +32,15 @@ Optional properties: Required child nodes: -- mdio bus node: this node should always be present regarless of the PHY +- mdio bus node: this node should always be present regardless of the PHY configuration of the GENET instance MDIO bus node required properties: - compatible: should contain one of "brcm,genet-mdio-v1", "brcm,genet-mdio-v2" - "brcm,genet-mdio-v3", "brcm,genet-mdio-v4", the version has to match the - parent node compatible property (e.g: brcm,genet-v4 pairs with - brcm,genet-mdio-v4) + "brcm,genet-mdio-v3", "brcm,genet-mdio-v4", "brcm,genet-mdio-v5", the version + has to match the parent node compatible property (e.g: brcm,genet-v4 pairs + with brcm,genet-mdio-v4) - reg: address and length relative to the parent node base register address - #address-cells: address cell for MDIO bus addressing, should be 1 - #size-cells: size of the cells for MDIO bus addressing, should be 0 diff --git a/Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt b/Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt index ab0bb4247d14..4648948f7c3b 100644 --- a/Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt +++ b/Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt @@ -2,8 +2,9 @@ Required properties: - compatible: should one from "brcm,genet-mdio-v1", "brcm,genet-mdio-v2", - "brcm,genet-mdio-v3", "brcm,genet-mdio-v4" or "brcm,unimac-mdio" -- reg: address and length of the regsiter set for the device, first one is the + "brcm,genet-mdio-v3", "brcm,genet-mdio-v4", "brcm,genet-mdio-v5" or + "brcm,unimac-mdio" +- reg: address and length of the register set for the device, first one is the base register, and the second one is optional and for indirect accesses to larger than 16-bits MDIO transactions - reg-names: name(s) of the register must be "mdio" and optional "mdio_indir_rw" -- cgit From 421380856d9c7466a28a1410cce2055acaf84ffd Mon Sep 17 00:00:00 2001 From: Doug Berger Date: Mon, 13 Mar 2017 17:41:42 -0700 Subject: net: bcmgenet: add support for the GENETv5 hardware This commit adds support for the GENETv5 implementation. The GENETv5 reports a major version of 6 instead of 5 so compensate for this when verifying the configuration of the driver. Also the EPHY revision is now contained in the MDIO registers of the PHY so the EPHY revision of 0 in GENET_VER_FMT is expected for GENETv5. Signed-off-by: Doug Berger Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 91 ++++++++++++++++++++------ drivers/net/ethernet/broadcom/genet/bcmgenet.h | 12 +++- drivers/net/ethernet/broadcom/genet/bcmmii.c | 62 ++++++++++-------- drivers/net/phy/mdio-bcm-unimac.c | 3 +- 4 files changed, 118 insertions(+), 50 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 3b49c14128e2..d848ac58189c 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1011,8 +1011,17 @@ static int bcmgenet_power_down(struct bcmgenet_priv *priv, /* Power down LED */ if (priv->hw_params->flags & GENET_HAS_EXT) { reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT); - reg |= (EXT_PWR_DOWN_PHY | - EXT_PWR_DOWN_DLL | EXT_PWR_DOWN_BIAS); + if (GENET_IS_V5(priv)) + reg |= EXT_PWR_DOWN_PHY_EN | + EXT_PWR_DOWN_PHY_RD | + EXT_PWR_DOWN_PHY_SD | + EXT_PWR_DOWN_PHY_RX | + EXT_PWR_DOWN_PHY_TX | + EXT_IDDQ_GLBL_PWR; + else + reg |= EXT_PWR_DOWN_PHY; + + reg |= (EXT_PWR_DOWN_DLL | EXT_PWR_DOWN_BIAS); bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); bcmgenet_phy_power_set(priv->dev, false); @@ -1037,12 +1046,34 @@ static void bcmgenet_power_up(struct bcmgenet_priv *priv, switch (mode) { case GENET_POWER_PASSIVE: - reg &= ~(EXT_PWR_DOWN_DLL | EXT_PWR_DOWN_PHY | - EXT_PWR_DOWN_BIAS); - /* fallthrough */ + reg &= ~(EXT_PWR_DOWN_DLL | EXT_PWR_DOWN_BIAS); + if (GENET_IS_V5(priv)) { + reg &= ~(EXT_PWR_DOWN_PHY_EN | + EXT_PWR_DOWN_PHY_RD | + EXT_PWR_DOWN_PHY_SD | + EXT_PWR_DOWN_PHY_RX | + EXT_PWR_DOWN_PHY_TX | + EXT_IDDQ_GLBL_PWR); + reg |= EXT_PHY_RESET; + bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); + mdelay(1); + + reg &= ~EXT_PHY_RESET; + } else { + reg &= ~EXT_PWR_DOWN_PHY; + reg |= EXT_PWR_DN_EN_LD; + } + bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); + bcmgenet_phy_power_set(priv->dev, true); + bcmgenet_mii_reset(priv->dev); + break; + case GENET_POWER_CABLE_SENSE: /* enable APD */ - reg |= EXT_PWR_DN_EN_LD; + if (!GENET_IS_V5(priv)) { + reg |= EXT_PWR_DN_EN_LD; + bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); + } break; case GENET_POWER_WOL_MAGIC: bcmgenet_wol_power_up_cfg(priv, mode); @@ -1050,12 +1081,6 @@ static void bcmgenet_power_up(struct bcmgenet_priv *priv, default: break; } - - bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); - if (mode == GENET_POWER_PASSIVE) { - bcmgenet_phy_power_set(priv->dev, true); - bcmgenet_mii_reset(priv->dev); - } } /* ioctl handle special commands that are not present in ethtool. */ @@ -3101,6 +3126,25 @@ static struct bcmgenet_hw_params bcmgenet_hw_params[] = { .flags = GENET_HAS_40BITS | GENET_HAS_EXT | GENET_HAS_MDIO_INTR | GENET_HAS_MOCA_LINK_DET, }, + [GENET_V5] = { + .tx_queues = 4, + .tx_bds_per_q = 32, + .rx_queues = 0, + .rx_bds_per_q = 0, + .bp_in_en_shift = 17, + .bp_in_mask = 0x1ffff, + .hfb_filter_cnt = 48, + .hfb_filter_size = 128, + .qtag_mask = 0x3F, + .tbuf_offset = 0x0600, + .hfb_offset = 0x8000, + .hfb_reg_offset = 0xfc00, + .rdma_offset = 0x2000, + .tdma_offset = 0x4000, + .words_per_bd = 3, + .flags = GENET_HAS_40BITS | GENET_HAS_EXT | + GENET_HAS_MDIO_INTR | GENET_HAS_MOCA_LINK_DET, + }, }; /* Infer hardware parameters from the detected GENET version */ @@ -3111,7 +3155,7 @@ static void bcmgenet_set_hw_params(struct bcmgenet_priv *priv) u8 major; u16 gphy_rev; - if (GENET_IS_V4(priv)) { + if (GENET_IS_V5(priv) || GENET_IS_V4(priv)) { bcmgenet_dma_regs = bcmgenet_dma_regs_v3plus; genet_dma_ring_regs = genet_dma_ring_regs_v4; priv->dma_rx_chk_bit = DMA_RX_CHK_V3PLUS; @@ -3136,7 +3180,9 @@ static void bcmgenet_set_hw_params(struct bcmgenet_priv *priv) /* Read GENET HW version */ reg = bcmgenet_sys_readl(priv, SYS_REV_CTRL); major = (reg >> 24 & 0x0f); - if (major == 5) + if (major == 6) + major = 5; + else if (major == 5) major = 4; else if (major == 0) major = 1; @@ -3164,16 +3210,22 @@ static void bcmgenet_set_hw_params(struct bcmgenet_priv *priv) */ gphy_rev = reg & 0xffff; + if (GENET_IS_V5(priv)) { + /* The EPHY revision should come from the MDIO registers of + * the PHY not from GENET. + */ + if (gphy_rev != 0) { + pr_warn("GENET is reporting EPHY revision: 0x%04x\n", + gphy_rev); + } /* This is the good old scheme, just GPHY major, no minor nor patch */ - if ((gphy_rev & 0xf0) != 0) + } else if ((gphy_rev & 0xf0) != 0) { priv->gphy_rev = gphy_rev << 8; - /* This is the new scheme, GPHY major rolls over with 0x10 = rev G0 */ - else if ((gphy_rev & 0xff00) != 0) + } else if ((gphy_rev & 0xff00) != 0) { priv->gphy_rev = gphy_rev; - /* This is reserved so should require special treatment */ - else if (gphy_rev == 0 || gphy_rev == 0x01ff) { + } else if (gphy_rev == 0 || gphy_rev == 0x01ff) { pr_warn("Invalid GPHY revision detected: 0x%04x\n", gphy_rev); return; } @@ -3206,6 +3258,7 @@ static const struct of_device_id bcmgenet_match[] = { { .compatible = "brcm,genet-v2", .data = (void *)GENET_V2 }, { .compatible = "brcm,genet-v3", .data = (void *)GENET_V3 }, { .compatible = "brcm,genet-v4", .data = (void *)GENET_V4 }, + { .compatible = "brcm,genet-v5", .data = (void *)GENET_V5 }, { }, }; MODULE_DEVICE_TABLE(of, bcmgenet_match); diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 1e2dc34d331a..1001d9131ba8 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Broadcom Corporation + * Copyright (c) 2014-2017 Broadcom * * 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 @@ -351,8 +351,14 @@ struct bcmgenet_mib_counters { #define EXT_PWR_DN_EN_LD (1 << 3) #define EXT_ENERGY_DET (1 << 4) #define EXT_IDDQ_FROM_PHY (1 << 5) +#define EXT_IDDQ_GLBL_PWR (1 << 7) #define EXT_PHY_RESET (1 << 8) #define EXT_ENERGY_DET_MASK (1 << 12) +#define EXT_PWR_DOWN_PHY_TX (1 << 16) +#define EXT_PWR_DOWN_PHY_RX (1 << 17) +#define EXT_PWR_DOWN_PHY_SD (1 << 18) +#define EXT_PWR_DOWN_PHY_RD (1 << 19) +#define EXT_PWR_DOWN_PHY_EN (1 << 20) #define EXT_RGMII_OOB_CTRL 0x0C #define RGMII_LINK (1 << 4) @@ -495,13 +501,15 @@ enum bcmgenet_version { GENET_V1 = 1, GENET_V2, GENET_V3, - GENET_V4 + GENET_V4, + GENET_V5 }; #define GENET_IS_V1(p) ((p)->version == GENET_V1) #define GENET_IS_V2(p) ((p)->version == GENET_V2) #define GENET_IS_V3(p) ((p)->version == GENET_V3) #define GENET_IS_V4(p) ((p)->version == GENET_V4) +#define GENET_IS_V5(p) ((p)->version == GENET_V5) /* Hardware flags */ #define GENET_HAS_40BITS (1 << 0) diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index e87607621e62..8df47c90cfc5 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -1,7 +1,7 @@ /* * Broadcom GENET MDIO routines * - * Copyright (c) 2014 Broadcom Corporation + * Copyright (c) 2014-2017 Broadcom * * 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 @@ -195,29 +195,31 @@ void bcmgenet_phy_power_set(struct net_device *dev, bool enable) u32 reg = 0; /* EXT_GPHY_CTRL is only valid for GENETv4 and onward */ - if (!GENET_IS_V4(priv)) - return; - - reg = bcmgenet_ext_readl(priv, EXT_GPHY_CTRL); - if (enable) { - reg &= ~EXT_CK25_DIS; - bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL); - mdelay(1); - - reg &= ~(EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN); - reg |= EXT_GPHY_RESET; + if (GENET_IS_V4(priv)) { + reg = bcmgenet_ext_readl(priv, EXT_GPHY_CTRL); + if (enable) { + reg &= ~EXT_CK25_DIS; + bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL); + mdelay(1); + + reg &= ~(EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN); + reg |= EXT_GPHY_RESET; + bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL); + mdelay(1); + + reg &= ~EXT_GPHY_RESET; + } else { + reg |= EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN | + EXT_GPHY_RESET; + bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL); + mdelay(1); + reg |= EXT_CK25_DIS; + } bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL); - mdelay(1); - - reg &= ~EXT_GPHY_RESET; + udelay(60); } else { - reg |= EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN | EXT_GPHY_RESET; - bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL); mdelay(1); - reg |= EXT_CK25_DIS; } - bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL); - udelay(60); } static void bcmgenet_internal_phy_setup(struct net_device *dev) @@ -227,10 +229,12 @@ static void bcmgenet_internal_phy_setup(struct net_device *dev) /* Power up PHY */ bcmgenet_phy_power_set(dev, true); - /* enable APD */ - reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT); - reg |= EXT_PWR_DN_EN_LD; - bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); + if (!GENET_IS_V5(priv)) { + /* enable APD */ + reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT); + reg |= EXT_PWR_DN_EN_LD; + bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); + } bcmgenet_mii_reset(dev); } @@ -238,10 +242,12 @@ static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv) { u32 reg; - /* Speed settings are set in bcmgenet_mii_setup() */ - reg = bcmgenet_sys_readl(priv, SYS_PORT_CTRL); - reg |= LED_ACT_SOURCE_MAC; - bcmgenet_sys_writel(priv, reg, SYS_PORT_CTRL); + if (!GENET_IS_V5(priv)) { + /* Speed settings are set in bcmgenet_mii_setup() */ + reg = bcmgenet_sys_readl(priv, SYS_PORT_CTRL); + reg |= LED_ACT_SOURCE_MAC; + bcmgenet_sys_writel(priv, reg, SYS_PORT_CTRL); + } if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET) fixed_phy_set_link_update(priv->phydev, diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c index 8c73b2e771dd..34395230ce70 100644 --- a/drivers/net/phy/mdio-bcm-unimac.c +++ b/drivers/net/phy/mdio-bcm-unimac.c @@ -1,7 +1,7 @@ /* * Broadcom UniMAC MDIO bus controller driver * - * Copyright (C) 2014, Broadcom Corporation + * Copyright (C) 2014-2017 Broadcom * * 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 @@ -228,6 +228,7 @@ static int unimac_mdio_remove(struct platform_device *pdev) } static const struct of_device_id unimac_mdio_ids[] = { + { .compatible = "brcm,genet-mdio-v5", }, { .compatible = "brcm,genet-mdio-v4", }, { .compatible = "brcm,genet-mdio-v3", }, { .compatible = "brcm,genet-mdio-v2", }, -- cgit From b418c5276a41c821c25410542df83d283bbc0b2a Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Mon, 13 Mar 2017 01:43:45 -0400 Subject: rds: ib: drop unnecessary rdma_reject When rdma_accept fails, rdma_reject is called in it. As such, it is not necessary to execute rdma_reject again. Cc: Joe Jin Cc: Junxiao Bi Acked-by: Santosh Shilimkar Signed-off-by: Zhu Yanjun Signed-off-by: David S. Miller --- net/rds/ib_cm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index ce3775abc6e7..11d535b16125 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -677,9 +677,8 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, event->param.conn.initiator_depth); /* rdma_accept() calls rdma_reject() internally if it fails */ - err = rdma_accept(cm_id, &conn_param); - if (err) - rds_ib_conn_error(conn, "rdma_accept failed (%d)\n", err); + if (rdma_accept(cm_id, &conn_param)) + rds_ib_conn_error(conn, "rdma_accept failed\n"); out: if (conn) -- cgit From ea69c883644a5c574ab518ae86ca3ce58e101072 Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Mon, 13 Mar 2017 01:43:46 -0400 Subject: rds: ib: remove redundant ib_dealloc_fmr The function ib_dealloc_fmr will never be called. As such, it should be removed. Cc: Joe Jin Cc: Junxiao Bi Reviewed-by: Yuval Shaia Reviewed-by: Johannes Thumshirn Acked-by: Santosh Shilimkar Signed-off-by: Zhu Yanjun Signed-off-by: David S. Miller --- net/rds/ib_fmr.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/rds/ib_fmr.c b/net/rds/ib_fmr.c index 4fe8f4fec4ee..249ae1c783cb 100644 --- a/net/rds/ib_fmr.c +++ b/net/rds/ib_fmr.c @@ -78,12 +78,9 @@ struct rds_ib_mr *rds_ib_alloc_fmr(struct rds_ib_device *rds_ibdev, int npages) return ibmr; out_no_cigar: - if (ibmr) { - if (fmr->fmr) - ib_dealloc_fmr(fmr->fmr); - kfree(ibmr); - } + kfree(ibmr); atomic_dec(&pool->item_count); + return ERR_PTR(err); } -- cgit From edd08f96db4f06beb09acdd71e00b07a1f6c13ca Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Mon, 13 Mar 2017 01:43:47 -0400 Subject: rds: ib: add the static type to the function The function rds_ib_map_fmr is used only in the ib_fmr.c file. As such, the static type is added to limit it in this file. Cc: Joe Jin Cc: Junxiao Bi Acked-by: Santosh Shilimkar Signed-off-by: Zhu Yanjun Signed-off-by: David S. Miller --- net/rds/ib_fmr.c | 5 +++-- net/rds/ib_mr.h | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/net/rds/ib_fmr.c b/net/rds/ib_fmr.c index 249ae1c783cb..c936b0d47693 100644 --- a/net/rds/ib_fmr.c +++ b/net/rds/ib_fmr.c @@ -84,8 +84,9 @@ out_no_cigar: return ERR_PTR(err); } -int rds_ib_map_fmr(struct rds_ib_device *rds_ibdev, struct rds_ib_mr *ibmr, - struct scatterlist *sg, unsigned int nents) +static int rds_ib_map_fmr(struct rds_ib_device *rds_ibdev, + struct rds_ib_mr *ibmr, struct scatterlist *sg, + unsigned int nents) { struct ib_device *dev = rds_ibdev->dev; struct rds_ib_fmr *fmr = &ibmr->u.fmr; diff --git a/net/rds/ib_mr.h b/net/rds/ib_mr.h index 5d6e98a79a5e..0ea4ab017a8c 100644 --- a/net/rds/ib_mr.h +++ b/net/rds/ib_mr.h @@ -125,8 +125,6 @@ void rds_ib_mr_exit(void); void __rds_ib_teardown_mr(struct rds_ib_mr *); void rds_ib_teardown_mr(struct rds_ib_mr *); struct rds_ib_mr *rds_ib_alloc_fmr(struct rds_ib_device *, int); -int rds_ib_map_fmr(struct rds_ib_device *, struct rds_ib_mr *, - struct scatterlist *, unsigned int); struct rds_ib_mr *rds_ib_reuse_mr(struct rds_ib_mr_pool *); int rds_ib_flush_mr_pool(struct rds_ib_mr_pool *, int, struct rds_ib_mr **); struct rds_ib_mr *rds_ib_reg_fmr(struct rds_ib_device *, struct scatterlist *, -- cgit From 569f41d187ac2c9a9809b9f4e27109fc558053eb Mon Sep 17 00:00:00 2001 From: Zhu Yanjun Date: Mon, 13 Mar 2017 01:43:48 -0400 Subject: rds: ib: unmap the scatter/gather list when error When some errors occur, the scatter/gather list mapped to DMA addresses should be handled. Cc: Joe Jin Cc: Junxiao Bi Acked-by: Santosh Shilimkar Signed-off-by: Zhu Yanjun Signed-off-by: David S. Miller --- net/rds/ib_fmr.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/net/rds/ib_fmr.c b/net/rds/ib_fmr.c index c936b0d47693..86ef907067bb 100644 --- a/net/rds/ib_fmr.c +++ b/net/rds/ib_fmr.c @@ -112,29 +112,39 @@ static int rds_ib_map_fmr(struct rds_ib_device *rds_ibdev, u64 dma_addr = ib_sg_dma_address(dev, &scat[i]); if (dma_addr & ~PAGE_MASK) { - if (i > 0) + if (i > 0) { + ib_dma_unmap_sg(dev, sg, nents, + DMA_BIDIRECTIONAL); return -EINVAL; - else + } else { ++page_cnt; + } } if ((dma_addr + dma_len) & ~PAGE_MASK) { - if (i < sg_dma_len - 1) + if (i < sg_dma_len - 1) { + ib_dma_unmap_sg(dev, sg, nents, + DMA_BIDIRECTIONAL); return -EINVAL; - else + } else { ++page_cnt; + } } len += dma_len; } page_cnt += len >> PAGE_SHIFT; - if (page_cnt > ibmr->pool->fmr_attr.max_pages) + if (page_cnt > ibmr->pool->fmr_attr.max_pages) { + ib_dma_unmap_sg(dev, sg, nents, DMA_BIDIRECTIONAL); return -EINVAL; + } dma_pages = kmalloc_node(sizeof(u64) * page_cnt, GFP_ATOMIC, rdsibdev_to_node(rds_ibdev)); - if (!dma_pages) + if (!dma_pages) { + ib_dma_unmap_sg(dev, sg, nents, DMA_BIDIRECTIONAL); return -ENOMEM; + } page_cnt = 0; for (i = 0; i < sg_dma_len; ++i) { @@ -147,8 +157,10 @@ static int rds_ib_map_fmr(struct rds_ib_device *rds_ibdev, } ret = ib_map_phys_fmr(fmr->fmr, dma_pages, page_cnt, io_addr); - if (ret) + if (ret) { + ib_dma_unmap_sg(dev, sg, nents, DMA_BIDIRECTIONAL); goto out; + } /* Success - we successfully remapped the MR, so we can * safely tear down the old mapping. -- cgit From 942c56ad07d111714899ec4bfb32212a65f40ac5 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Mon, 13 Mar 2017 23:04:48 -0700 Subject: lwtunnel: remove unused but set variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit silences the below warning: net/core/lwtunnel.c: In function ‘lwtunnel_valid_encap_type_attr’: net/core/lwtunnel.c:165:17: warning: variable ‘nla’ set but not used [-Wunused-but-set-variable] Fixes: 9ed59592e3e3 ("lwtunnel: fix autoload of lwt modules") Signed-off-by: Roopa Prabhu Signed-off-by: David S. Miller --- net/core/lwtunnel.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index 6df9f8fabf0c..b5888190223c 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -162,7 +162,6 @@ int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining) struct rtnexthop *rtnh = (struct rtnexthop *)attr; struct nlattr *nla_entype; struct nlattr *attrs; - struct nlattr *nla; u16 encap_type; int attrlen; @@ -170,7 +169,6 @@ int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining) attrlen = rtnh_attrlen(rtnh); if (attrlen > 0) { attrs = rtnh_attrs(rtnh); - nla = nla_find(attrs, attrlen, RTA_ENCAP); nla_entype = nla_find(attrs, attrlen, RTA_ENCAP_TYPE); if (nla_entype) { -- cgit From 9c79ddaa0f962d1f26537a670b0652ff509a6fe0 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 14 Mar 2017 16:23:54 +0200 Subject: qed*: Add support for QL41xxx adapters This adds the necessary infrastructure changes for initializing and working with the new series of QL41xxx adapaters. It also adds 2 new PCI device-IDs to qede: - 0x8070 for QL41xxx PFs - 0x8090 for VFs spawning from QL41xxx PFs Signed-off-by: Tomer Tayar Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 29 +++- drivers/net/ethernet/qlogic/qed/qed_debug.c | 2 +- drivers/net/ethernet/qlogic/qed/qed_dev.c | 186 ++++++++++++++++---- drivers/net/ethernet/qlogic/qed/qed_hsi.h | 61 +++++-- drivers/net/ethernet/qlogic/qed/qed_l2.c | 184 ++++++++++++-------- drivers/net/ethernet/qlogic/qed/qed_main.c | 7 +- drivers/net/ethernet/qlogic/qed/qed_mcp.h | 9 +- drivers/net/ethernet/qlogic/qed/qed_ptp.c | 12 +- drivers/net/ethernet/qlogic/qed/qed_reg_addr.h | 17 +- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 30 +++- drivers/net/ethernet/qlogic/qede/qede.h | 43 +++-- drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 85 +++++---- drivers/net/ethernet/qlogic/qede/qede_main.c | 222 +++++++++++++----------- include/linux/qed/qed_if.h | 48 +++-- include/linux/qed/rdma_common.h | 3 +- 15 files changed, 630 insertions(+), 308 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index be99092d7209..ca30a27df035 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -219,7 +219,9 @@ enum QED_PORT_MODE { QED_PORT_MODE_DE_4X20G, QED_PORT_MODE_DE_1X40G, QED_PORT_MODE_DE_2X25G, - QED_PORT_MODE_DE_1X25G + QED_PORT_MODE_DE_1X25G, + QED_PORT_MODE_DE_4X25G, + QED_PORT_MODE_DE_2X10G, }; enum qed_dev_cap { @@ -364,7 +366,8 @@ struct qed_hwfn { #define IS_LEAD_HWFN(edev) (!((edev)->my_id)) u8 rel_pf_id; /* Relative to engine*/ u8 abs_pf_id; -#define QED_PATH_ID(_p_hwfn) ((_p_hwfn)->abs_pf_id & 1) +#define QED_PATH_ID(_p_hwfn) \ + (QED_IS_K2((_p_hwfn)->cdev) ? 0 : ((_p_hwfn)->abs_pf_id & 1)) u8 port_id; bool b_active; @@ -523,9 +526,7 @@ struct qed_dev { u8 dp_level; char name[NAME_SIZE]; - u8 type; -#define QED_DEV_TYPE_BB (0 << 0) -#define QED_DEV_TYPE_AH BIT(0) + enum qed_dev_type type; /* Translate type/revision combo into the proper conditions */ #define QED_IS_BB(dev) ((dev)->type == QED_DEV_TYPE_BB) #define QED_IS_BB_A0(dev) (QED_IS_BB(dev) && \ @@ -540,6 +541,9 @@ struct qed_dev { u16 vendor_id; u16 device_id; +#define QED_DEV_ID_MASK 0xff00 +#define QED_DEV_ID_MASK_BB 0x1600 +#define QED_DEV_ID_MASK_AH 0x8000 u16 chip_num; #define CHIP_NUM_MASK 0xffff @@ -654,10 +658,16 @@ struct qed_dev { u32 rdma_max_srq_sge; }; -#define NUM_OF_VFS(dev) MAX_NUM_VFS_BB -#define NUM_OF_L2_QUEUES(dev) MAX_NUM_L2_QUEUES_BB -#define NUM_OF_SBS(dev) MAX_SB_PER_PATH_BB -#define NUM_OF_ENG_PFS(dev) MAX_NUM_PFS_BB +#define NUM_OF_VFS(dev) (QED_IS_BB(dev) ? MAX_NUM_VFS_BB \ + : MAX_NUM_VFS_K2) +#define NUM_OF_L2_QUEUES(dev) (QED_IS_BB(dev) ? MAX_NUM_L2_QUEUES_BB \ + : MAX_NUM_L2_QUEUES_K2) +#define NUM_OF_PORTS(dev) (QED_IS_BB(dev) ? MAX_NUM_PORTS_BB \ + : MAX_NUM_PORTS_K2) +#define NUM_OF_SBS(dev) (QED_IS_BB(dev) ? MAX_SB_PER_PATH_BB \ + : MAX_SB_PER_PATH_K2) +#define NUM_OF_ENG_PFS(dev) (QED_IS_BB(dev) ? MAX_NUM_PFS_BB \ + : MAX_NUM_PFS_K2) /** * @brief qed_concrete_to_sw_fid - get the sw function id from @@ -694,6 +704,7 @@ void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, void qed_clean_wfq_db(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); #define QED_LEADING_HWFN(dev) (&dev->hwfns[0]) +int qed_device_num_engines(struct qed_dev *cdev); /* Other Linux specific common definitions */ #define DP_NAME(cdev) ((cdev)->name) diff --git a/drivers/net/ethernet/qlogic/qed/qed_debug.c b/drivers/net/ethernet/qlogic/qed/qed_debug.c index 5e81e8a7a109..483241b4b05d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_debug.c +++ b/drivers/net/ethernet/qlogic/qed/qed_debug.c @@ -1557,7 +1557,7 @@ static enum dbg_status qed_dbg_dev_init(struct qed_hwfn *p_hwfn, dev_data->mode_enable[MODE_K2] = 1; } else if (QED_IS_BB_B0(p_hwfn->cdev)) { dev_data->chip_id = CHIP_BB_B0; - dev_data->mode_enable[MODE_BB_B0] = 1; + dev_data->mode_enable[MODE_BB] = 1; } else { return DBG_STATUS_UNKNOWN_CHIP; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index e2a081ceaf52..bd4f43ffb5a1 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -674,11 +674,19 @@ int qed_final_cleanup(struct qed_hwfn *p_hwfn, return rc; } -static void qed_calc_hw_mode(struct qed_hwfn *p_hwfn) +static int qed_calc_hw_mode(struct qed_hwfn *p_hwfn) { int hw_mode = 0; - hw_mode = (1 << MODE_BB_B0); + if (QED_IS_BB_B0(p_hwfn->cdev)) { + hw_mode |= 1 << MODE_BB; + } else if (QED_IS_AH(p_hwfn->cdev)) { + hw_mode |= 1 << MODE_K2; + } else { + DP_NOTICE(p_hwfn, "Unknown chip type %#x\n", + p_hwfn->cdev->type); + return -EINVAL; + } switch (p_hwfn->cdev->num_ports_in_engines) { case 1: @@ -693,7 +701,7 @@ static void qed_calc_hw_mode(struct qed_hwfn *p_hwfn) default: DP_NOTICE(p_hwfn, "num_ports_in_engine = %d not supported\n", p_hwfn->cdev->num_ports_in_engines); - return; + return -EINVAL; } switch (p_hwfn->cdev->mf_mode) { @@ -719,6 +727,8 @@ static void qed_calc_hw_mode(struct qed_hwfn *p_hwfn) DP_VERBOSE(p_hwfn, (NETIF_MSG_PROBE | NETIF_MSG_IFUP), "Configuring function for hw_mode: 0x%08x\n", p_hwfn->hw_info.hw_mode); + + return 0; } /* Init run time data for all PFs on an engine. */ @@ -754,10 +764,10 @@ static int qed_hw_init_common(struct qed_hwfn *p_hwfn, struct qed_qm_info *qm_info = &p_hwfn->qm_info; struct qed_qm_common_rt_init_params params; struct qed_dev *cdev = p_hwfn->cdev; + u8 vf_id, max_num_vfs; u16 num_pfs, pf_id; u32 concrete_fid; int rc = 0; - u8 vf_id; qed_init_cau_rt_data(cdev); @@ -814,7 +824,8 @@ static int qed_hw_init_common(struct qed_hwfn *p_hwfn, qed_fid_pretend(p_hwfn, p_ptt, p_hwfn->rel_pf_id); } - for (vf_id = 0; vf_id < MAX_NUM_VFS_BB; vf_id++) { + max_num_vfs = QED_IS_AH(cdev) ? MAX_NUM_VFS_K2 : MAX_NUM_VFS_BB; + for (vf_id = 0; vf_id < max_num_vfs; vf_id++) { concrete_fid = qed_vfid_to_concrete(p_hwfn, vf_id); qed_fid_pretend(p_hwfn, p_ptt, (u16) concrete_fid); qed_wr(p_hwfn, p_ptt, CCFC_REG_STRONG_ENABLE_VF, 0x1); @@ -1135,7 +1146,9 @@ int qed_hw_init(struct qed_dev *cdev, /* Enable DMAE in PXP */ rc = qed_change_pci_hwfn(p_hwfn, p_hwfn->p_main_ptt, true); - qed_calc_hw_mode(p_hwfn); + rc = qed_calc_hw_mode(p_hwfn); + if (rc) + return rc; rc = qed_mcp_load_req(p_hwfn, p_hwfn->p_main_ptt, &load_code); if (rc) { @@ -1485,10 +1498,25 @@ static void qed_hw_hwfn_free(struct qed_hwfn *p_hwfn) static void qed_hw_hwfn_prepare(struct qed_hwfn *p_hwfn) { /* clear indirect access */ - qed_wr(p_hwfn, p_hwfn->p_main_ptt, PGLUE_B_REG_PGL_ADDR_88_F0, 0); - qed_wr(p_hwfn, p_hwfn->p_main_ptt, PGLUE_B_REG_PGL_ADDR_8C_F0, 0); - qed_wr(p_hwfn, p_hwfn->p_main_ptt, PGLUE_B_REG_PGL_ADDR_90_F0, 0); - qed_wr(p_hwfn, p_hwfn->p_main_ptt, PGLUE_B_REG_PGL_ADDR_94_F0, 0); + if (QED_IS_AH(p_hwfn->cdev)) { + qed_wr(p_hwfn, p_hwfn->p_main_ptt, + PGLUE_B_REG_PGL_ADDR_E8_F0_K2, 0); + qed_wr(p_hwfn, p_hwfn->p_main_ptt, + PGLUE_B_REG_PGL_ADDR_EC_F0_K2, 0); + qed_wr(p_hwfn, p_hwfn->p_main_ptt, + PGLUE_B_REG_PGL_ADDR_F0_F0_K2, 0); + qed_wr(p_hwfn, p_hwfn->p_main_ptt, + PGLUE_B_REG_PGL_ADDR_F4_F0_K2, 0); + } else { + qed_wr(p_hwfn, p_hwfn->p_main_ptt, + PGLUE_B_REG_PGL_ADDR_88_F0_BB, 0); + qed_wr(p_hwfn, p_hwfn->p_main_ptt, + PGLUE_B_REG_PGL_ADDR_8C_F0_BB, 0); + qed_wr(p_hwfn, p_hwfn->p_main_ptt, + PGLUE_B_REG_PGL_ADDR_90_F0_BB, 0); + qed_wr(p_hwfn, p_hwfn->p_main_ptt, + PGLUE_B_REG_PGL_ADDR_94_F0_BB, 0); + } /* Clean Previous errors if such exist */ qed_wr(p_hwfn, p_hwfn->p_main_ptt, @@ -1610,6 +1638,7 @@ static u32 qed_hw_get_dflt_resc_num(struct qed_hwfn *p_hwfn, enum qed_resources res_id) { u8 num_funcs = p_hwfn->num_funcs_on_engine; + bool b_ah = QED_IS_AH(p_hwfn->cdev); struct qed_sb_cnt_info sb_cnt_info; u32 dflt_resc_num = 0; @@ -1620,17 +1649,22 @@ static u32 qed_hw_get_dflt_resc_num(struct qed_hwfn *p_hwfn, dflt_resc_num = sb_cnt_info.sb_cnt; break; case QED_L2_QUEUE: - dflt_resc_num = MAX_NUM_L2_QUEUES_BB / num_funcs; + dflt_resc_num = (b_ah ? MAX_NUM_L2_QUEUES_K2 + : MAX_NUM_L2_QUEUES_BB) / num_funcs; break; case QED_VPORT: dflt_resc_num = MAX_NUM_VPORTS_BB / num_funcs; + dflt_resc_num = (b_ah ? MAX_NUM_VPORTS_K2 + : MAX_NUM_VPORTS_BB) / num_funcs; break; case QED_RSS_ENG: - dflt_resc_num = ETH_RSS_ENGINE_NUM_BB / num_funcs; + dflt_resc_num = (b_ah ? ETH_RSS_ENGINE_NUM_K2 + : ETH_RSS_ENGINE_NUM_BB) / num_funcs; break; case QED_PQ: /* The granularity of the PQs is 8 */ - dflt_resc_num = MAX_QM_TX_QUEUES_BB / num_funcs; + dflt_resc_num = (b_ah ? MAX_QM_TX_QUEUES_K2 + : MAX_QM_TX_QUEUES_BB) / num_funcs; dflt_resc_num &= ~0x7; break; case QED_RL: @@ -1642,7 +1676,8 @@ static u32 qed_hw_get_dflt_resc_num(struct qed_hwfn *p_hwfn, dflt_resc_num = ETH_NUM_MAC_FILTERS / num_funcs; break; case QED_ILT: - dflt_resc_num = PXP_NUM_ILT_RECORDS_BB / num_funcs; + dflt_resc_num = (b_ah ? PXP_NUM_ILT_RECORDS_K2 + : PXP_NUM_ILT_RECORDS_BB) / num_funcs; break; case QED_LL2_QUEUE: dflt_resc_num = MAX_NUM_LL2_RX_QUEUES / num_funcs; @@ -1653,7 +1688,10 @@ static u32 qed_hw_get_dflt_resc_num(struct qed_hwfn *p_hwfn, dflt_resc_num = NUM_OF_CMDQS_CQS / num_funcs; break; case QED_RDMA_STATS_QUEUE: - dflt_resc_num = RDMA_NUM_STATISTIC_COUNTERS_BB / num_funcs; + dflt_resc_num = (b_ah ? RDMA_NUM_STATISTIC_COUNTERS_K2 + : RDMA_NUM_STATISTIC_COUNTERS_BB) / + num_funcs; + break; default: break; @@ -1780,6 +1818,7 @@ out: static int qed_hw_get_resc(struct qed_hwfn *p_hwfn) { + bool b_ah = QED_IS_AH(p_hwfn->cdev); u8 res_id; int rc; @@ -1790,7 +1829,8 @@ static int qed_hw_get_resc(struct qed_hwfn *p_hwfn) } /* Sanity for ILT */ - if ((RESC_END(p_hwfn, QED_ILT) > PXP_NUM_ILT_RECORDS_BB)) { + if ((b_ah && (RESC_END(p_hwfn, QED_ILT) > PXP_NUM_ILT_RECORDS_K2)) || + (!b_ah && (RESC_END(p_hwfn, QED_ILT) > PXP_NUM_ILT_RECORDS_BB))) { DP_NOTICE(p_hwfn, "Can't assign ILT pages [%08x,...,%08x]\n", RESC_START(p_hwfn, QED_ILT), RESC_END(p_hwfn, QED_ILT) - 1); @@ -1860,9 +1900,15 @@ static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) case NVM_CFG1_GLOB_NETWORK_PORT_MODE_2X25G: p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_2X25G; break; + case NVM_CFG1_GLOB_NETWORK_PORT_MODE_2X10G: + p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_2X10G; + break; case NVM_CFG1_GLOB_NETWORK_PORT_MODE_1X25G: p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_1X25G; break; + case NVM_CFG1_GLOB_NETWORK_PORT_MODE_4X25G: + p_hwfn->hw_info.port_mode = QED_PORT_MODE_DE_4X25G; + break; default: DP_NOTICE(p_hwfn, "Unknown port mode in 0x%08x\n", core_cfg); break; @@ -1976,8 +2022,9 @@ static void qed_get_num_funcs(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { u8 num_funcs, enabled_func_idx = p_hwfn->rel_pf_id; u32 reg_function_hide, tmp, eng_mask, low_pfs_mask; + struct qed_dev *cdev = p_hwfn->cdev; - num_funcs = MAX_NUM_PFS_BB; + num_funcs = QED_IS_AH(cdev) ? MAX_NUM_PFS_K2 : MAX_NUM_PFS_BB; /* Bit 0 of MISCS_REG_FUNCTION_HIDE indicates whether the bypass values * in the other bits are selected. @@ -1990,12 +2037,17 @@ static void qed_get_num_funcs(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) reg_function_hide = qed_rd(p_hwfn, p_ptt, MISCS_REG_FUNCTION_HIDE); if (reg_function_hide & 0x1) { - if (QED_PATH_ID(p_hwfn) && p_hwfn->cdev->num_hwfns == 1) { - num_funcs = 0; - eng_mask = 0xaaaa; + if (QED_IS_BB(cdev)) { + if (QED_PATH_ID(p_hwfn) && cdev->num_hwfns == 1) { + num_funcs = 0; + eng_mask = 0xaaaa; + } else { + num_funcs = 1; + eng_mask = 0x5554; + } } else { num_funcs = 1; - eng_mask = 0x5554; + eng_mask = 0xfffe; } /* Get the number of the enabled functions on the engine */ @@ -2027,24 +2079,12 @@ static void qed_get_num_funcs(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) p_hwfn->enabled_func_idx, p_hwfn->num_funcs_on_engine); } -static int -qed_get_hw_info(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - enum qed_pci_personality personality) +static void qed_hw_info_port_num_bb(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt) { u32 port_mode; - int rc; - /* Since all information is common, only first hwfns should do this */ - if (IS_LEAD_HWFN(p_hwfn)) { - rc = qed_iov_hw_info(p_hwfn); - if (rc) - return rc; - } - - /* Read the port mode */ - port_mode = qed_rd(p_hwfn, p_ptt, - CNIG_REG_NW_PORT_MODE_BB_B0); + port_mode = qed_rd(p_hwfn, p_ptt, CNIG_REG_NW_PORT_MODE_BB_B0); if (port_mode < 3) { p_hwfn->cdev->num_ports_in_engines = 1; @@ -2057,6 +2097,54 @@ qed_get_hw_info(struct qed_hwfn *p_hwfn, /* Default num_ports_in_engines to something */ p_hwfn->cdev->num_ports_in_engines = 1; } +} + +static void qed_hw_info_port_num_ah(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt) +{ + u32 port; + int i; + + p_hwfn->cdev->num_ports_in_engines = 0; + + for (i = 0; i < MAX_NUM_PORTS_K2; i++) { + port = qed_rd(p_hwfn, p_ptt, + CNIG_REG_NIG_PORT0_CONF_K2 + (i * 4)); + if (port & 1) + p_hwfn->cdev->num_ports_in_engines++; + } + + if (!p_hwfn->cdev->num_ports_in_engines) { + DP_NOTICE(p_hwfn, "All NIG ports are inactive\n"); + + /* Default num_ports_in_engine to something */ + p_hwfn->cdev->num_ports_in_engines = 1; + } +} + +static void qed_hw_info_port_num(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + if (QED_IS_BB(p_hwfn->cdev)) + qed_hw_info_port_num_bb(p_hwfn, p_ptt); + else + qed_hw_info_port_num_ah(p_hwfn, p_ptt); +} + +static int +qed_get_hw_info(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_pci_personality personality) +{ + int rc; + + /* Since all information is common, only first hwfns should do this */ + if (IS_LEAD_HWFN(p_hwfn)) { + rc = qed_iov_hw_info(p_hwfn); + if (rc) + return rc; + } + + qed_hw_info_port_num(p_hwfn, p_ptt); qed_hw_get_nvm_info(p_hwfn, p_ptt); @@ -2096,19 +2184,33 @@ qed_get_hw_info(struct qed_hwfn *p_hwfn, static int qed_get_dev_info(struct qed_dev *cdev) { struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); + u16 device_id_mask; u32 tmp; /* Read Vendor Id / Device Id */ pci_read_config_word(cdev->pdev, PCI_VENDOR_ID, &cdev->vendor_id); pci_read_config_word(cdev->pdev, PCI_DEVICE_ID, &cdev->device_id); + /* Determine type */ + device_id_mask = cdev->device_id & QED_DEV_ID_MASK; + switch (device_id_mask) { + case QED_DEV_ID_MASK_BB: + cdev->type = QED_DEV_TYPE_BB; + break; + case QED_DEV_ID_MASK_AH: + cdev->type = QED_DEV_TYPE_AH; + break; + default: + DP_NOTICE(p_hwfn, "Unknown device id 0x%x\n", cdev->device_id); + return -EBUSY; + } + cdev->chip_num = (u16)qed_rd(p_hwfn, p_hwfn->p_main_ptt, MISCS_REG_CHIP_NUM); cdev->chip_rev = (u16)qed_rd(p_hwfn, p_hwfn->p_main_ptt, MISCS_REG_CHIP_REV); MASK_FIELD(CHIP_REV, cdev->chip_rev); - cdev->type = QED_DEV_TYPE_BB; /* Learn number of HW-functions */ tmp = qed_rd(p_hwfn, p_hwfn->p_main_ptt, MISCS_REG_CMT_ENABLED_FOR_PAIR); @@ -2128,7 +2230,10 @@ static int qed_get_dev_info(struct qed_dev *cdev) MASK_FIELD(CHIP_METAL, cdev->chip_metal); DP_INFO(cdev->hwfns, - "Chip details - Num: %04x Rev: %04x Bond id: %04x Metal: %04x\n", + "Chip details - %s %c%d, Num: %04x Rev: %04x Bond id: %04x Metal: %04x\n", + QED_IS_BB(cdev) ? "BB" : "AH", + 'A' + cdev->chip_rev, + (int)cdev->chip_metal, cdev->chip_num, cdev->chip_rev, cdev->chip_bond_id, cdev->chip_metal); @@ -3364,3 +3469,8 @@ void qed_clean_wfq_db(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) memset(p_hwfn->qm_info.wfq_data, 0, sizeof(*p_hwfn->qm_info.wfq_data) * p_hwfn->qm_info.num_vports); } + +int qed_device_num_engines(struct qed_dev *cdev) +{ + return QED_IS_BB(cdev) ? 2 : 1; +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index 88ca78a297a0..e9acdc96ba84 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -2502,7 +2502,7 @@ struct fw_info_location { enum init_modes { MODE_RESERVED, - MODE_BB_B0, + MODE_BB, MODE_K2, MODE_ASIC, MODE_RESERVED2, @@ -9431,11 +9431,24 @@ struct eth_stats { u64 r511; u64 r1023; u64 r1518; - u64 r1522; - u64 r2047; - u64 r4095; - u64 r9216; - u64 r16383; + + union { + struct { + u64 r1522; + u64 r2047; + u64 r4095; + u64 r9216; + u64 r16383; + } bb0; + struct { + u64 unused1; + u64 r1519_to_max; + u64 unused2; + u64 unused3; + u64 unused4; + } ah0; + } u0; + u64 rfcs; u64 rxcf; u64 rxpf; @@ -9452,14 +9465,36 @@ struct eth_stats { u64 t511; u64 t1023; u64 t1518; - u64 t2047; - u64 t4095; - u64 t9216; - u64 t16383; + + union { + struct { + u64 t2047; + u64 t4095; + u64 t9216; + u64 t16383; + } bb1; + struct { + u64 t1519_to_max; + u64 unused6; + u64 unused7; + u64 unused8; + } ah1; + } u1; + u64 txpf; u64 txpp; - u64 tlpiec; - u64 tncl; + + union { + struct { + u64 tlpiec; + u64 tncl; + } bb2; + struct { + u64 unused9; + u64 unused10; + } ah2; + } u2; + u64 rbyte; u64 rxuca; u64 rxmca; @@ -10263,6 +10298,8 @@ struct nvm_cfg1_glob { #define NVM_CFG1_GLOB_NETWORK_PORT_MODE_2X25G 0xC #define NVM_CFG1_GLOB_NETWORK_PORT_MODE_1X25G 0xD #define NVM_CFG1_GLOB_NETWORK_PORT_MODE_4X25G 0xE +#define NVM_CFG1_GLOB_NETWORK_PORT_MODE_2X10G 0xF + u32 e_lane_cfg1; u32 e_lane_cfg2; u32 f_lane_cfg1; diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index df932be5a4e5..4385ccbb5efb 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -1470,13 +1470,20 @@ static void __qed_get_vport_pstats(struct qed_hwfn *p_hwfn, memset(&pstats, 0, sizeof(pstats)); qed_memcpy_from(p_hwfn, p_ptt, &pstats, pstats_addr, pstats_len); - p_stats->tx_ucast_bytes += HILO_64_REGPAIR(pstats.sent_ucast_bytes); - p_stats->tx_mcast_bytes += HILO_64_REGPAIR(pstats.sent_mcast_bytes); - p_stats->tx_bcast_bytes += HILO_64_REGPAIR(pstats.sent_bcast_bytes); - p_stats->tx_ucast_pkts += HILO_64_REGPAIR(pstats.sent_ucast_pkts); - p_stats->tx_mcast_pkts += HILO_64_REGPAIR(pstats.sent_mcast_pkts); - p_stats->tx_bcast_pkts += HILO_64_REGPAIR(pstats.sent_bcast_pkts); - p_stats->tx_err_drop_pkts += HILO_64_REGPAIR(pstats.error_drop_pkts); + p_stats->common.tx_ucast_bytes += + HILO_64_REGPAIR(pstats.sent_ucast_bytes); + p_stats->common.tx_mcast_bytes += + HILO_64_REGPAIR(pstats.sent_mcast_bytes); + p_stats->common.tx_bcast_bytes += + HILO_64_REGPAIR(pstats.sent_bcast_bytes); + p_stats->common.tx_ucast_pkts += + HILO_64_REGPAIR(pstats.sent_ucast_pkts); + p_stats->common.tx_mcast_pkts += + HILO_64_REGPAIR(pstats.sent_mcast_pkts); + p_stats->common.tx_bcast_pkts += + HILO_64_REGPAIR(pstats.sent_bcast_pkts); + p_stats->common.tx_err_drop_pkts += + HILO_64_REGPAIR(pstats.error_drop_pkts); } static void __qed_get_vport_tstats(struct qed_hwfn *p_hwfn, @@ -1502,10 +1509,10 @@ static void __qed_get_vport_tstats(struct qed_hwfn *p_hwfn, memset(&tstats, 0, sizeof(tstats)); qed_memcpy_from(p_hwfn, p_ptt, &tstats, tstats_addr, tstats_len); - p_stats->mftag_filter_discards += - HILO_64_REGPAIR(tstats.mftag_filter_discard); - p_stats->mac_filter_discards += - HILO_64_REGPAIR(tstats.eth_mac_filter_discard); + p_stats->common.mftag_filter_discards += + HILO_64_REGPAIR(tstats.mftag_filter_discard); + p_stats->common.mac_filter_discards += + HILO_64_REGPAIR(tstats.eth_mac_filter_discard); } static void __qed_get_vport_ustats_addrlen(struct qed_hwfn *p_hwfn, @@ -1539,12 +1546,15 @@ static void __qed_get_vport_ustats(struct qed_hwfn *p_hwfn, memset(&ustats, 0, sizeof(ustats)); qed_memcpy_from(p_hwfn, p_ptt, &ustats, ustats_addr, ustats_len); - p_stats->rx_ucast_bytes += HILO_64_REGPAIR(ustats.rcv_ucast_bytes); - p_stats->rx_mcast_bytes += HILO_64_REGPAIR(ustats.rcv_mcast_bytes); - p_stats->rx_bcast_bytes += HILO_64_REGPAIR(ustats.rcv_bcast_bytes); - p_stats->rx_ucast_pkts += HILO_64_REGPAIR(ustats.rcv_ucast_pkts); - p_stats->rx_mcast_pkts += HILO_64_REGPAIR(ustats.rcv_mcast_pkts); - p_stats->rx_bcast_pkts += HILO_64_REGPAIR(ustats.rcv_bcast_pkts); + p_stats->common.rx_ucast_bytes += + HILO_64_REGPAIR(ustats.rcv_ucast_bytes); + p_stats->common.rx_mcast_bytes += + HILO_64_REGPAIR(ustats.rcv_mcast_bytes); + p_stats->common.rx_bcast_bytes += + HILO_64_REGPAIR(ustats.rcv_bcast_bytes); + p_stats->common.rx_ucast_pkts += HILO_64_REGPAIR(ustats.rcv_ucast_pkts); + p_stats->common.rx_mcast_pkts += HILO_64_REGPAIR(ustats.rcv_mcast_pkts); + p_stats->common.rx_bcast_pkts += HILO_64_REGPAIR(ustats.rcv_bcast_pkts); } static void __qed_get_vport_mstats_addrlen(struct qed_hwfn *p_hwfn, @@ -1578,23 +1588,26 @@ static void __qed_get_vport_mstats(struct qed_hwfn *p_hwfn, memset(&mstats, 0, sizeof(mstats)); qed_memcpy_from(p_hwfn, p_ptt, &mstats, mstats_addr, mstats_len); - p_stats->no_buff_discards += HILO_64_REGPAIR(mstats.no_buff_discard); - p_stats->packet_too_big_discard += - HILO_64_REGPAIR(mstats.packet_too_big_discard); - p_stats->ttl0_discard += HILO_64_REGPAIR(mstats.ttl0_discard); - p_stats->tpa_coalesced_pkts += - HILO_64_REGPAIR(mstats.tpa_coalesced_pkts); - p_stats->tpa_coalesced_events += - HILO_64_REGPAIR(mstats.tpa_coalesced_events); - p_stats->tpa_aborts_num += HILO_64_REGPAIR(mstats.tpa_aborts_num); - p_stats->tpa_coalesced_bytes += - HILO_64_REGPAIR(mstats.tpa_coalesced_bytes); + p_stats->common.no_buff_discards += + HILO_64_REGPAIR(mstats.no_buff_discard); + p_stats->common.packet_too_big_discard += + HILO_64_REGPAIR(mstats.packet_too_big_discard); + p_stats->common.ttl0_discard += HILO_64_REGPAIR(mstats.ttl0_discard); + p_stats->common.tpa_coalesced_pkts += + HILO_64_REGPAIR(mstats.tpa_coalesced_pkts); + p_stats->common.tpa_coalesced_events += + HILO_64_REGPAIR(mstats.tpa_coalesced_events); + p_stats->common.tpa_aborts_num += + HILO_64_REGPAIR(mstats.tpa_aborts_num); + p_stats->common.tpa_coalesced_bytes += + HILO_64_REGPAIR(mstats.tpa_coalesced_bytes); } static void __qed_get_vport_port_stats(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, struct qed_eth_stats *p_stats) { + struct qed_eth_stats_common *p_common = &p_stats->common; struct port_stats port_stats; int j; @@ -1605,54 +1618,75 @@ static void __qed_get_vport_port_stats(struct qed_hwfn *p_hwfn, offsetof(struct public_port, stats), sizeof(port_stats)); - p_stats->rx_64_byte_packets += port_stats.eth.r64; - p_stats->rx_65_to_127_byte_packets += port_stats.eth.r127; - p_stats->rx_128_to_255_byte_packets += port_stats.eth.r255; - p_stats->rx_256_to_511_byte_packets += port_stats.eth.r511; - p_stats->rx_512_to_1023_byte_packets += port_stats.eth.r1023; - p_stats->rx_1024_to_1518_byte_packets += port_stats.eth.r1518; - p_stats->rx_1519_to_1522_byte_packets += port_stats.eth.r1522; - p_stats->rx_1519_to_2047_byte_packets += port_stats.eth.r2047; - p_stats->rx_2048_to_4095_byte_packets += port_stats.eth.r4095; - p_stats->rx_4096_to_9216_byte_packets += port_stats.eth.r9216; - p_stats->rx_9217_to_16383_byte_packets += port_stats.eth.r16383; - p_stats->rx_crc_errors += port_stats.eth.rfcs; - p_stats->rx_mac_crtl_frames += port_stats.eth.rxcf; - p_stats->rx_pause_frames += port_stats.eth.rxpf; - p_stats->rx_pfc_frames += port_stats.eth.rxpp; - p_stats->rx_align_errors += port_stats.eth.raln; - p_stats->rx_carrier_errors += port_stats.eth.rfcr; - p_stats->rx_oversize_packets += port_stats.eth.rovr; - p_stats->rx_jabbers += port_stats.eth.rjbr; - p_stats->rx_undersize_packets += port_stats.eth.rund; - p_stats->rx_fragments += port_stats.eth.rfrg; - p_stats->tx_64_byte_packets += port_stats.eth.t64; - p_stats->tx_65_to_127_byte_packets += port_stats.eth.t127; - p_stats->tx_128_to_255_byte_packets += port_stats.eth.t255; - p_stats->tx_256_to_511_byte_packets += port_stats.eth.t511; - p_stats->tx_512_to_1023_byte_packets += port_stats.eth.t1023; - p_stats->tx_1024_to_1518_byte_packets += port_stats.eth.t1518; - p_stats->tx_1519_to_2047_byte_packets += port_stats.eth.t2047; - p_stats->tx_2048_to_4095_byte_packets += port_stats.eth.t4095; - p_stats->tx_4096_to_9216_byte_packets += port_stats.eth.t9216; - p_stats->tx_9217_to_16383_byte_packets += port_stats.eth.t16383; - p_stats->tx_pause_frames += port_stats.eth.txpf; - p_stats->tx_pfc_frames += port_stats.eth.txpp; - p_stats->tx_lpi_entry_count += port_stats.eth.tlpiec; - p_stats->tx_total_collisions += port_stats.eth.tncl; - p_stats->rx_mac_bytes += port_stats.eth.rbyte; - p_stats->rx_mac_uc_packets += port_stats.eth.rxuca; - p_stats->rx_mac_mc_packets += port_stats.eth.rxmca; - p_stats->rx_mac_bc_packets += port_stats.eth.rxbca; - p_stats->rx_mac_frames_ok += port_stats.eth.rxpok; - p_stats->tx_mac_bytes += port_stats.eth.tbyte; - p_stats->tx_mac_uc_packets += port_stats.eth.txuca; - p_stats->tx_mac_mc_packets += port_stats.eth.txmca; - p_stats->tx_mac_bc_packets += port_stats.eth.txbca; - p_stats->tx_mac_ctrl_frames += port_stats.eth.txcf; + p_common->rx_64_byte_packets += port_stats.eth.r64; + p_common->rx_65_to_127_byte_packets += port_stats.eth.r127; + p_common->rx_128_to_255_byte_packets += port_stats.eth.r255; + p_common->rx_256_to_511_byte_packets += port_stats.eth.r511; + p_common->rx_512_to_1023_byte_packets += port_stats.eth.r1023; + p_common->rx_1024_to_1518_byte_packets += port_stats.eth.r1518; + p_common->rx_crc_errors += port_stats.eth.rfcs; + p_common->rx_mac_crtl_frames += port_stats.eth.rxcf; + p_common->rx_pause_frames += port_stats.eth.rxpf; + p_common->rx_pfc_frames += port_stats.eth.rxpp; + p_common->rx_align_errors += port_stats.eth.raln; + p_common->rx_carrier_errors += port_stats.eth.rfcr; + p_common->rx_oversize_packets += port_stats.eth.rovr; + p_common->rx_jabbers += port_stats.eth.rjbr; + p_common->rx_undersize_packets += port_stats.eth.rund; + p_common->rx_fragments += port_stats.eth.rfrg; + p_common->tx_64_byte_packets += port_stats.eth.t64; + p_common->tx_65_to_127_byte_packets += port_stats.eth.t127; + p_common->tx_128_to_255_byte_packets += port_stats.eth.t255; + p_common->tx_256_to_511_byte_packets += port_stats.eth.t511; + p_common->tx_512_to_1023_byte_packets += port_stats.eth.t1023; + p_common->tx_1024_to_1518_byte_packets += port_stats.eth.t1518; + p_common->tx_pause_frames += port_stats.eth.txpf; + p_common->tx_pfc_frames += port_stats.eth.txpp; + p_common->rx_mac_bytes += port_stats.eth.rbyte; + p_common->rx_mac_uc_packets += port_stats.eth.rxuca; + p_common->rx_mac_mc_packets += port_stats.eth.rxmca; + p_common->rx_mac_bc_packets += port_stats.eth.rxbca; + p_common->rx_mac_frames_ok += port_stats.eth.rxpok; + p_common->tx_mac_bytes += port_stats.eth.tbyte; + p_common->tx_mac_uc_packets += port_stats.eth.txuca; + p_common->tx_mac_mc_packets += port_stats.eth.txmca; + p_common->tx_mac_bc_packets += port_stats.eth.txbca; + p_common->tx_mac_ctrl_frames += port_stats.eth.txcf; for (j = 0; j < 8; j++) { - p_stats->brb_truncates += port_stats.brb.brb_truncate[j]; - p_stats->brb_discards += port_stats.brb.brb_discard[j]; + p_common->brb_truncates += port_stats.brb.brb_truncate[j]; + p_common->brb_discards += port_stats.brb.brb_discard[j]; + } + + if (QED_IS_BB(p_hwfn->cdev)) { + struct qed_eth_stats_bb *p_bb = &p_stats->bb; + + p_bb->rx_1519_to_1522_byte_packets += + port_stats.eth.u0.bb0.r1522; + p_bb->rx_1519_to_2047_byte_packets += + port_stats.eth.u0.bb0.r2047; + p_bb->rx_2048_to_4095_byte_packets += + port_stats.eth.u0.bb0.r4095; + p_bb->rx_4096_to_9216_byte_packets += + port_stats.eth.u0.bb0.r9216; + p_bb->rx_9217_to_16383_byte_packets += + port_stats.eth.u0.bb0.r16383; + p_bb->tx_1519_to_2047_byte_packets += + port_stats.eth.u1.bb1.t2047; + p_bb->tx_2048_to_4095_byte_packets += + port_stats.eth.u1.bb1.t4095; + p_bb->tx_4096_to_9216_byte_packets += + port_stats.eth.u1.bb1.t9216; + p_bb->tx_9217_to_16383_byte_packets += + port_stats.eth.u1.bb1.t16383; + p_bb->tx_lpi_entry_count += port_stats.eth.u2.bb2.tlpiec; + p_bb->tx_total_collisions += port_stats.eth.u2.bb2.tncl; + } else { + struct qed_eth_stats_ah *p_ah = &p_stats->ah; + + p_ah->rx_1519_to_max_byte_packets += + port_stats.eth.u0.ah0.r1519_to_max; + p_ah->tx_1519_to_max_byte_packets = + port_stats.eth.u1.ah1.t1519_to_max; } } diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index eef30a598b40..766c6f39ea63 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -238,6 +238,7 @@ int qed_fill_dev_info(struct qed_dev *cdev, dev_info->rdma_supported = (cdev->hwfns[0].hw_info.personality == QED_PCI_ETH_ROCE); dev_info->is_mf_default = IS_MF_DEFAULT(&cdev->hwfns[0]); + dev_info->dev_type = cdev->type; ether_addr_copy(dev_info->hw_mac, cdev->hwfns[0].hw_info.hw_mac_addr); if (IS_PF(cdev)) { @@ -1653,8 +1654,10 @@ void qed_get_protocol_stats(struct qed_dev *cdev, switch (type) { case QED_MCP_LAN_STATS: qed_get_vport_stats(cdev, ð_stats); - stats->lan_stats.ucast_rx_pkts = eth_stats.rx_ucast_pkts; - stats->lan_stats.ucast_tx_pkts = eth_stats.tx_ucast_pkts; + stats->lan_stats.ucast_rx_pkts = + eth_stats.common.rx_ucast_pkts; + stats->lan_stats.ucast_tx_pkts = + eth_stats.common.tx_ucast_pkts; stats->lan_stats.fcs_err = -1; break; case QED_MCP_FCOE_STATS: diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index 368e88de146c..bdbfd6d4485e 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -479,11 +479,10 @@ int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn, rel_pfid) #define MCP_PF_ID(p_hwfn) MCP_PF_ID_BY_REL(p_hwfn, (p_hwfn)->rel_pf_id) -/* TODO - this is only correct as long as only BB is supported, and - * no port-swapping is implemented; Afterwards we'll need to fix it. - */ -#define MFW_PORT(_p_hwfn) ((_p_hwfn)->abs_pf_id % \ - ((_p_hwfn)->cdev->num_ports_in_engines * 2)) +#define MFW_PORT(_p_hwfn) ((_p_hwfn)->abs_pf_id % \ + ((_p_hwfn)->cdev->num_ports_in_engines * \ + qed_device_num_engines((_p_hwfn)->cdev))) + struct qed_mcp_info { /* Spinlock used for protecting the access to the MFW mailbox */ spinlock_t lock; diff --git a/drivers/net/ethernet/qlogic/qed/qed_ptp.c b/drivers/net/ethernet/qlogic/qed/qed_ptp.c index d27aa85da23c..80c9c0b172dd 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ptp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ptp.c @@ -262,12 +262,20 @@ static int qed_ptp_hw_enable(struct qed_dev *cdev) qed_wr(p_hwfn, p_ptt, NIG_REG_TS_OUTPUT_ENABLE_PDA, 0x1); /* Pause free running counter */ - qed_wr(p_hwfn, p_ptt, NIG_REG_TIMESYNC_GEN_REG_BB, 2); + if (QED_IS_BB_B0(p_hwfn->cdev)) + qed_wr(p_hwfn, p_ptt, NIG_REG_TIMESYNC_GEN_REG_BB, 2); + if (QED_IS_AH(p_hwfn->cdev)) + qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREECNT_UPDATE_K2, 2); qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREE_CNT_VALUE_LSB, 0); qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREE_CNT_VALUE_MSB, 0); /* Resume free running counter */ - qed_wr(p_hwfn, p_ptt, NIG_REG_TIMESYNC_GEN_REG_BB, 4); + if (QED_IS_BB_B0(p_hwfn->cdev)) + qed_wr(p_hwfn, p_ptt, NIG_REG_TIMESYNC_GEN_REG_BB, 4); + if (QED_IS_AH(p_hwfn->cdev)) { + qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_FREECNT_UPDATE_K2, 4); + qed_wr(p_hwfn, p_ptt, NIG_REG_PTP_LATCH_OSTS_PKT_TIME, 1); + } /* Disable drift register */ qed_wr(p_hwfn, p_ptt, NIG_REG_TSGEN_DRIFT_CNTR_CONF, 0x0); diff --git a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h index 36ae361884e0..6d4ac7e2ee83 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h +++ b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h @@ -160,13 +160,13 @@ 0x2e0704UL #define CCFC_REG_STRONG_ENABLE_PF \ 0x2e0708UL -#define PGLUE_B_REG_PGL_ADDR_88_F0 \ +#define PGLUE_B_REG_PGL_ADDR_88_F0_BB \ 0x2aa404UL -#define PGLUE_B_REG_PGL_ADDR_8C_F0 \ +#define PGLUE_B_REG_PGL_ADDR_8C_F0_BB \ 0x2aa408UL -#define PGLUE_B_REG_PGL_ADDR_90_F0 \ +#define PGLUE_B_REG_PGL_ADDR_90_F0_BB \ 0x2aa40cUL -#define PGLUE_B_REG_PGL_ADDR_94_F0 \ +#define PGLUE_B_REG_PGL_ADDR_94_F0_BB \ 0x2aa410UL #define PGLUE_B_REG_WAS_ERROR_PF_31_0_CLR \ 0x2aa138UL @@ -1550,4 +1550,13 @@ #define NIG_REG_TIMESYNC_GEN_REG_BB 0x500d00UL #define NIG_REG_TSGEN_FREE_CNT_VALUE_LSB 0x5088a8UL #define NIG_REG_TSGEN_FREE_CNT_VALUE_MSB 0x5088acUL +#define NIG_REG_PTP_LATCH_OSTS_PKT_TIME 0x509040UL + +#define PGLUE_B_REG_PGL_ADDR_E8_F0_K2 0x2aaf98UL +#define PGLUE_B_REG_PGL_ADDR_EC_F0_K2 0x2aaf9cUL +#define PGLUE_B_REG_PGL_ADDR_F0_F0_K2 0x2aafa0UL +#define PGLUE_B_REG_PGL_ADDR_F4_F0_K2 0x2aafa4UL +#define NIG_REG_TSGEN_FREECNT_UPDATE_K2 0x509008UL +#define CNIG_REG_NIG_PORT0_CONF_K2 0x218200UL + #endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 253c2bbe1e4e..16f503c9b0af 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -557,14 +557,30 @@ int qed_iov_hw_info(struct qed_hwfn *p_hwfn) return 0; } - /* Calculate the first VF index - this is a bit tricky; Basically, - * VFs start at offset 16 relative to PF0, and 2nd engine VFs begin - * after the first engine's VFs. + /* First VF index based on offset is tricky: + * - If ARI is supported [likely], offset - (16 - pf_id) would + * provide the number for eng0. 2nd engine Vfs would begin + * after the first engine's VFs. + * - If !ARI, VFs would start on next device. + * so offset - (256 - pf_id) would provide the number. + * Utilize the fact that (256 - pf_id) is achieved only by later + * to diffrentiate between the two. */ - cdev->p_iov_info->first_vf_in_pf = p_hwfn->cdev->p_iov_info->offset + - p_hwfn->abs_pf_id - 16; - if (QED_PATH_ID(p_hwfn)) - cdev->p_iov_info->first_vf_in_pf -= MAX_NUM_VFS_BB; + + if (p_hwfn->cdev->p_iov_info->offset < (256 - p_hwfn->abs_pf_id)) { + u32 first = p_hwfn->cdev->p_iov_info->offset + + p_hwfn->abs_pf_id - 16; + + cdev->p_iov_info->first_vf_in_pf = first; + + if (QED_PATH_ID(p_hwfn)) + cdev->p_iov_info->first_vf_in_pf -= MAX_NUM_VFS_BB; + } else { + u32 first = p_hwfn->cdev->p_iov_info->offset + + p_hwfn->abs_pf_id - 256; + + cdev->p_iov_info->first_vf_in_pf = first; + } DP_VERBOSE(p_hwfn, QED_MSG_IOV, "First VF in hwfn 0x%08x\n", diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index 8d02fb6c19d7..e73a4a5165ee 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -58,7 +58,7 @@ #define DRV_MODULE_SYM qede -struct qede_stats { +struct qede_stats_common { u64 no_buff_discards; u64 packet_too_big_discard; u64 ttl0_discard; @@ -90,11 +90,6 @@ struct qede_stats { u64 rx_256_to_511_byte_packets; u64 rx_512_to_1023_byte_packets; u64 rx_1024_to_1518_byte_packets; - u64 rx_1519_to_1522_byte_packets; - u64 rx_1519_to_2047_byte_packets; - u64 rx_2048_to_4095_byte_packets; - u64 rx_4096_to_9216_byte_packets; - u64 rx_9217_to_16383_byte_packets; u64 rx_crc_errors; u64 rx_mac_crtl_frames; u64 rx_pause_frames; @@ -111,17 +106,39 @@ struct qede_stats { u64 tx_256_to_511_byte_packets; u64 tx_512_to_1023_byte_packets; u64 tx_1024_to_1518_byte_packets; + u64 tx_pause_frames; + u64 tx_pfc_frames; + u64 brb_truncates; + u64 brb_discards; + u64 tx_mac_ctrl_frames; +}; + +struct qede_stats_bb { + u64 rx_1519_to_1522_byte_packets; + u64 rx_1519_to_2047_byte_packets; + u64 rx_2048_to_4095_byte_packets; + u64 rx_4096_to_9216_byte_packets; + u64 rx_9217_to_16383_byte_packets; u64 tx_1519_to_2047_byte_packets; u64 tx_2048_to_4095_byte_packets; u64 tx_4096_to_9216_byte_packets; u64 tx_9217_to_16383_byte_packets; - u64 tx_pause_frames; - u64 tx_pfc_frames; u64 tx_lpi_entry_count; u64 tx_total_collisions; - u64 brb_truncates; - u64 brb_discards; - u64 tx_mac_ctrl_frames; +}; + +struct qede_stats_ah { + u64 rx_1519_to_max_byte_packets; + u64 tx_1519_to_max_byte_packets; +}; + +struct qede_stats { + struct qede_stats_common common; + + union { + struct qede_stats_bb bb; + struct qede_stats_ah ah; + }; }; struct qede_vlan { @@ -158,6 +175,10 @@ struct qede_dev { struct qed_dev_eth_info dev_info; #define QEDE_MAX_RSS_CNT(edev) ((edev)->dev_info.num_queues) #define QEDE_MAX_TSS_CNT(edev) ((edev)->dev_info.num_queues) +#define QEDE_IS_BB(edev) \ + ((edev)->dev_info.common.dev_type == QED_DEV_TYPE_BB) +#define QEDE_IS_AH(edev) \ + ((edev)->dev_info.common.dev_type == QED_DEV_TYPE_AH) struct qede_fastpath *fp_array; u8 req_num_tx; diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 897953133245..4dcfe9614731 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -75,16 +75,33 @@ static const struct { QEDE_TQSTAT(stopped_cnt), }; -#define QEDE_STAT_OFFSET(stat_name) (offsetof(struct qede_stats, stat_name)) -#define QEDE_STAT_STRING(stat_name) (#stat_name) -#define _QEDE_STAT(stat_name, pf_only) \ - {QEDE_STAT_OFFSET(stat_name), QEDE_STAT_STRING(stat_name), pf_only} -#define QEDE_PF_STAT(stat_name) _QEDE_STAT(stat_name, true) -#define QEDE_STAT(stat_name) _QEDE_STAT(stat_name, false) +#define QEDE_STAT_OFFSET(stat_name, type, base) \ + (offsetof(type, stat_name) + (base)) +#define QEDE_STAT_STRING(stat_name) (#stat_name) +#define _QEDE_STAT(stat_name, type, base, attr) \ + {QEDE_STAT_OFFSET(stat_name, type, base), \ + QEDE_STAT_STRING(stat_name), \ + attr} +#define QEDE_STAT(stat_name) \ + _QEDE_STAT(stat_name, struct qede_stats_common, 0, 0x0) +#define QEDE_PF_STAT(stat_name) \ + _QEDE_STAT(stat_name, struct qede_stats_common, 0, \ + BIT(QEDE_STAT_PF_ONLY)) +#define QEDE_PF_BB_STAT(stat_name) \ + _QEDE_STAT(stat_name, struct qede_stats_bb, \ + offsetof(struct qede_stats, bb), \ + BIT(QEDE_STAT_PF_ONLY) | BIT(QEDE_STAT_BB_ONLY)) +#define QEDE_PF_AH_STAT(stat_name) \ + _QEDE_STAT(stat_name, struct qede_stats_ah, \ + offsetof(struct qede_stats, ah), \ + BIT(QEDE_STAT_PF_ONLY) | BIT(QEDE_STAT_AH_ONLY)) static const struct { u64 offset; char string[ETH_GSTRING_LEN]; - bool pf_only; + unsigned long attr; +#define QEDE_STAT_PF_ONLY 0 +#define QEDE_STAT_BB_ONLY 1 +#define QEDE_STAT_AH_ONLY 2 } qede_stats_arr[] = { QEDE_STAT(rx_ucast_bytes), QEDE_STAT(rx_mcast_bytes), @@ -106,22 +123,23 @@ static const struct { QEDE_PF_STAT(rx_256_to_511_byte_packets), QEDE_PF_STAT(rx_512_to_1023_byte_packets), QEDE_PF_STAT(rx_1024_to_1518_byte_packets), - QEDE_PF_STAT(rx_1519_to_1522_byte_packets), - QEDE_PF_STAT(rx_1519_to_2047_byte_packets), - QEDE_PF_STAT(rx_2048_to_4095_byte_packets), - QEDE_PF_STAT(rx_4096_to_9216_byte_packets), - QEDE_PF_STAT(rx_9217_to_16383_byte_packets), + QEDE_PF_BB_STAT(rx_1519_to_1522_byte_packets), + QEDE_PF_BB_STAT(rx_1519_to_2047_byte_packets), + QEDE_PF_BB_STAT(rx_2048_to_4095_byte_packets), + QEDE_PF_BB_STAT(rx_4096_to_9216_byte_packets), + QEDE_PF_BB_STAT(rx_9217_to_16383_byte_packets), + QEDE_PF_AH_STAT(rx_1519_to_max_byte_packets), QEDE_PF_STAT(tx_64_byte_packets), QEDE_PF_STAT(tx_65_to_127_byte_packets), QEDE_PF_STAT(tx_128_to_255_byte_packets), QEDE_PF_STAT(tx_256_to_511_byte_packets), QEDE_PF_STAT(tx_512_to_1023_byte_packets), QEDE_PF_STAT(tx_1024_to_1518_byte_packets), - QEDE_PF_STAT(tx_1519_to_2047_byte_packets), - QEDE_PF_STAT(tx_2048_to_4095_byte_packets), - QEDE_PF_STAT(tx_4096_to_9216_byte_packets), - QEDE_PF_STAT(tx_9217_to_16383_byte_packets), - + QEDE_PF_BB_STAT(tx_1519_to_2047_byte_packets), + QEDE_PF_BB_STAT(tx_2048_to_4095_byte_packets), + QEDE_PF_BB_STAT(tx_4096_to_9216_byte_packets), + QEDE_PF_BB_STAT(tx_9217_to_16383_byte_packets), + QEDE_PF_AH_STAT(tx_1519_to_max_byte_packets), QEDE_PF_STAT(rx_mac_crtl_frames), QEDE_PF_STAT(tx_mac_ctrl_frames), QEDE_PF_STAT(rx_pause_frames), @@ -136,8 +154,8 @@ static const struct { QEDE_PF_STAT(rx_jabbers), QEDE_PF_STAT(rx_undersize_packets), QEDE_PF_STAT(rx_fragments), - QEDE_PF_STAT(tx_lpi_entry_count), - QEDE_PF_STAT(tx_total_collisions), + QEDE_PF_BB_STAT(tx_lpi_entry_count), + QEDE_PF_BB_STAT(tx_total_collisions), QEDE_PF_STAT(brb_truncates), QEDE_PF_STAT(brb_discards), QEDE_STAT(no_buff_discards), @@ -155,6 +173,12 @@ static const struct { }; #define QEDE_NUM_STATS ARRAY_SIZE(qede_stats_arr) +#define QEDE_STAT_IS_PF_ONLY(i) \ + test_bit(QEDE_STAT_PF_ONLY, &qede_stats_arr[i].attr) +#define QEDE_STAT_IS_BB_ONLY(i) \ + test_bit(QEDE_STAT_BB_ONLY, &qede_stats_arr[i].attr) +#define QEDE_STAT_IS_AH_ONLY(i) \ + test_bit(QEDE_STAT_AH_ONLY, &qede_stats_arr[i].attr) enum { QEDE_PRI_FLAG_CMT, @@ -213,6 +237,13 @@ static void qede_get_strings_stats_rxq(struct qede_dev *edev, } } +static bool qede_is_irrelevant_stat(struct qede_dev *edev, int stat_index) +{ + return (IS_VF(edev) && QEDE_STAT_IS_PF_ONLY(stat_index)) || + (QEDE_IS_BB(edev) && QEDE_STAT_IS_AH_ONLY(stat_index)) || + (QEDE_IS_AH(edev) && QEDE_STAT_IS_BB_ONLY(stat_index)); +} + static void qede_get_strings_stats(struct qede_dev *edev, u8 *buf) { struct qede_fastpath *fp; @@ -234,7 +265,7 @@ static void qede_get_strings_stats(struct qede_dev *edev, u8 *buf) /* Account for non-queue statistics */ for (i = 0; i < QEDE_NUM_STATS; i++) { - if (IS_VF(edev) && qede_stats_arr[i].pf_only) + if (qede_is_irrelevant_stat(edev, i)) continue; strcpy(buf, qede_stats_arr[i].string); buf += ETH_GSTRING_LEN; @@ -309,7 +340,7 @@ static void qede_get_ethtool_stats(struct net_device *dev, } for (i = 0; i < QEDE_NUM_STATS; i++) { - if (IS_VF(edev) && qede_stats_arr[i].pf_only) + if (qede_is_irrelevant_stat(edev, i)) continue; *buf = *((u64 *)(((void *)&edev->stats) + qede_stats_arr[i].offset)); @@ -323,17 +354,13 @@ static void qede_get_ethtool_stats(struct net_device *dev, static int qede_get_sset_count(struct net_device *dev, int stringset) { struct qede_dev *edev = netdev_priv(dev); - int num_stats = QEDE_NUM_STATS; + int num_stats = QEDE_NUM_STATS, i; switch (stringset) { case ETH_SS_STATS: - if (IS_VF(edev)) { - int i; - - for (i = 0; i < QEDE_NUM_STATS; i++) - if (qede_stats_arr[i].pf_only) - num_stats--; - } + for (i = 0; i < QEDE_NUM_STATS; i++) + if (qede_is_irrelevant_stat(edev, i)) + num_stats--; /* Account for the Regular Tx statistics */ num_stats += QEDE_TSS_COUNT(edev) * QEDE_NUM_TQSTATS; diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 3a78c3f25157..abd99109e532 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -84,6 +84,8 @@ static const struct qed_eth_ops *qed_ops; #define CHIP_NUM_57980S_50 0x1654 #define CHIP_NUM_57980S_25 0x1656 #define CHIP_NUM_57980S_IOV 0x1664 +#define CHIP_NUM_AH 0x8070 +#define CHIP_NUM_AH_IOV 0x8090 #ifndef PCI_DEVICE_ID_NX2_57980E #define PCI_DEVICE_ID_57980S_40 CHIP_NUM_57980S_40 @@ -93,6 +95,9 @@ static const struct qed_eth_ops *qed_ops; #define PCI_DEVICE_ID_57980S_50 CHIP_NUM_57980S_50 #define PCI_DEVICE_ID_57980S_25 CHIP_NUM_57980S_25 #define PCI_DEVICE_ID_57980S_IOV CHIP_NUM_57980S_IOV +#define PCI_DEVICE_ID_AH CHIP_NUM_AH +#define PCI_DEVICE_ID_AH_IOV CHIP_NUM_AH_IOV + #endif enum qede_pci_private { @@ -109,6 +114,10 @@ static const struct pci_device_id qede_pci_tbl[] = { {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_25), QEDE_PRIVATE_PF}, #ifdef CONFIG_QED_SRIOV {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_IOV), QEDE_PRIVATE_VF}, +#endif + {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_AH), QEDE_PRIVATE_PF}, +#ifdef CONFIG_QED_SRIOV + {PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_AH_IOV), QEDE_PRIVATE_VF}, #endif { 0 } }; @@ -314,122 +323,135 @@ static int qede_close(struct net_device *ndev); void qede_fill_by_demand_stats(struct qede_dev *edev) { + struct qede_stats_common *p_common = &edev->stats.common; struct qed_eth_stats stats; edev->ops->get_vport_stats(edev->cdev, &stats); - edev->stats.no_buff_discards = stats.no_buff_discards; - edev->stats.packet_too_big_discard = stats.packet_too_big_discard; - edev->stats.ttl0_discard = stats.ttl0_discard; - edev->stats.rx_ucast_bytes = stats.rx_ucast_bytes; - edev->stats.rx_mcast_bytes = stats.rx_mcast_bytes; - edev->stats.rx_bcast_bytes = stats.rx_bcast_bytes; - edev->stats.rx_ucast_pkts = stats.rx_ucast_pkts; - edev->stats.rx_mcast_pkts = stats.rx_mcast_pkts; - edev->stats.rx_bcast_pkts = stats.rx_bcast_pkts; - edev->stats.mftag_filter_discards = stats.mftag_filter_discards; - edev->stats.mac_filter_discards = stats.mac_filter_discards; - - edev->stats.tx_ucast_bytes = stats.tx_ucast_bytes; - edev->stats.tx_mcast_bytes = stats.tx_mcast_bytes; - edev->stats.tx_bcast_bytes = stats.tx_bcast_bytes; - edev->stats.tx_ucast_pkts = stats.tx_ucast_pkts; - edev->stats.tx_mcast_pkts = stats.tx_mcast_pkts; - edev->stats.tx_bcast_pkts = stats.tx_bcast_pkts; - edev->stats.tx_err_drop_pkts = stats.tx_err_drop_pkts; - edev->stats.coalesced_pkts = stats.tpa_coalesced_pkts; - edev->stats.coalesced_events = stats.tpa_coalesced_events; - edev->stats.coalesced_aborts_num = stats.tpa_aborts_num; - edev->stats.non_coalesced_pkts = stats.tpa_not_coalesced_pkts; - edev->stats.coalesced_bytes = stats.tpa_coalesced_bytes; - - edev->stats.rx_64_byte_packets = stats.rx_64_byte_packets; - edev->stats.rx_65_to_127_byte_packets = stats.rx_65_to_127_byte_packets; - edev->stats.rx_128_to_255_byte_packets = - stats.rx_128_to_255_byte_packets; - edev->stats.rx_256_to_511_byte_packets = - stats.rx_256_to_511_byte_packets; - edev->stats.rx_512_to_1023_byte_packets = - stats.rx_512_to_1023_byte_packets; - edev->stats.rx_1024_to_1518_byte_packets = - stats.rx_1024_to_1518_byte_packets; - edev->stats.rx_1519_to_1522_byte_packets = - stats.rx_1519_to_1522_byte_packets; - edev->stats.rx_1519_to_2047_byte_packets = - stats.rx_1519_to_2047_byte_packets; - edev->stats.rx_2048_to_4095_byte_packets = - stats.rx_2048_to_4095_byte_packets; - edev->stats.rx_4096_to_9216_byte_packets = - stats.rx_4096_to_9216_byte_packets; - edev->stats.rx_9217_to_16383_byte_packets = - stats.rx_9217_to_16383_byte_packets; - edev->stats.rx_crc_errors = stats.rx_crc_errors; - edev->stats.rx_mac_crtl_frames = stats.rx_mac_crtl_frames; - edev->stats.rx_pause_frames = stats.rx_pause_frames; - edev->stats.rx_pfc_frames = stats.rx_pfc_frames; - edev->stats.rx_align_errors = stats.rx_align_errors; - edev->stats.rx_carrier_errors = stats.rx_carrier_errors; - edev->stats.rx_oversize_packets = stats.rx_oversize_packets; - edev->stats.rx_jabbers = stats.rx_jabbers; - edev->stats.rx_undersize_packets = stats.rx_undersize_packets; - edev->stats.rx_fragments = stats.rx_fragments; - edev->stats.tx_64_byte_packets = stats.tx_64_byte_packets; - edev->stats.tx_65_to_127_byte_packets = stats.tx_65_to_127_byte_packets; - edev->stats.tx_128_to_255_byte_packets = - stats.tx_128_to_255_byte_packets; - edev->stats.tx_256_to_511_byte_packets = - stats.tx_256_to_511_byte_packets; - edev->stats.tx_512_to_1023_byte_packets = - stats.tx_512_to_1023_byte_packets; - edev->stats.tx_1024_to_1518_byte_packets = - stats.tx_1024_to_1518_byte_packets; - edev->stats.tx_1519_to_2047_byte_packets = - stats.tx_1519_to_2047_byte_packets; - edev->stats.tx_2048_to_4095_byte_packets = - stats.tx_2048_to_4095_byte_packets; - edev->stats.tx_4096_to_9216_byte_packets = - stats.tx_4096_to_9216_byte_packets; - edev->stats.tx_9217_to_16383_byte_packets = - stats.tx_9217_to_16383_byte_packets; - edev->stats.tx_pause_frames = stats.tx_pause_frames; - edev->stats.tx_pfc_frames = stats.tx_pfc_frames; - edev->stats.tx_lpi_entry_count = stats.tx_lpi_entry_count; - edev->stats.tx_total_collisions = stats.tx_total_collisions; - edev->stats.brb_truncates = stats.brb_truncates; - edev->stats.brb_discards = stats.brb_discards; - edev->stats.tx_mac_ctrl_frames = stats.tx_mac_ctrl_frames; + + p_common->no_buff_discards = stats.common.no_buff_discards; + p_common->packet_too_big_discard = stats.common.packet_too_big_discard; + p_common->ttl0_discard = stats.common.ttl0_discard; + p_common->rx_ucast_bytes = stats.common.rx_ucast_bytes; + p_common->rx_mcast_bytes = stats.common.rx_mcast_bytes; + p_common->rx_bcast_bytes = stats.common.rx_bcast_bytes; + p_common->rx_ucast_pkts = stats.common.rx_ucast_pkts; + p_common->rx_mcast_pkts = stats.common.rx_mcast_pkts; + p_common->rx_bcast_pkts = stats.common.rx_bcast_pkts; + p_common->mftag_filter_discards = stats.common.mftag_filter_discards; + p_common->mac_filter_discards = stats.common.mac_filter_discards; + + p_common->tx_ucast_bytes = stats.common.tx_ucast_bytes; + p_common->tx_mcast_bytes = stats.common.tx_mcast_bytes; + p_common->tx_bcast_bytes = stats.common.tx_bcast_bytes; + p_common->tx_ucast_pkts = stats.common.tx_ucast_pkts; + p_common->tx_mcast_pkts = stats.common.tx_mcast_pkts; + p_common->tx_bcast_pkts = stats.common.tx_bcast_pkts; + p_common->tx_err_drop_pkts = stats.common.tx_err_drop_pkts; + p_common->coalesced_pkts = stats.common.tpa_coalesced_pkts; + p_common->coalesced_events = stats.common.tpa_coalesced_events; + p_common->coalesced_aborts_num = stats.common.tpa_aborts_num; + p_common->non_coalesced_pkts = stats.common.tpa_not_coalesced_pkts; + p_common->coalesced_bytes = stats.common.tpa_coalesced_bytes; + + p_common->rx_64_byte_packets = stats.common.rx_64_byte_packets; + p_common->rx_65_to_127_byte_packets = + stats.common.rx_65_to_127_byte_packets; + p_common->rx_128_to_255_byte_packets = + stats.common.rx_128_to_255_byte_packets; + p_common->rx_256_to_511_byte_packets = + stats.common.rx_256_to_511_byte_packets; + p_common->rx_512_to_1023_byte_packets = + stats.common.rx_512_to_1023_byte_packets; + p_common->rx_1024_to_1518_byte_packets = + stats.common.rx_1024_to_1518_byte_packets; + p_common->rx_crc_errors = stats.common.rx_crc_errors; + p_common->rx_mac_crtl_frames = stats.common.rx_mac_crtl_frames; + p_common->rx_pause_frames = stats.common.rx_pause_frames; + p_common->rx_pfc_frames = stats.common.rx_pfc_frames; + p_common->rx_align_errors = stats.common.rx_align_errors; + p_common->rx_carrier_errors = stats.common.rx_carrier_errors; + p_common->rx_oversize_packets = stats.common.rx_oversize_packets; + p_common->rx_jabbers = stats.common.rx_jabbers; + p_common->rx_undersize_packets = stats.common.rx_undersize_packets; + p_common->rx_fragments = stats.common.rx_fragments; + p_common->tx_64_byte_packets = stats.common.tx_64_byte_packets; + p_common->tx_65_to_127_byte_packets = + stats.common.tx_65_to_127_byte_packets; + p_common->tx_128_to_255_byte_packets = + stats.common.tx_128_to_255_byte_packets; + p_common->tx_256_to_511_byte_packets = + stats.common.tx_256_to_511_byte_packets; + p_common->tx_512_to_1023_byte_packets = + stats.common.tx_512_to_1023_byte_packets; + p_common->tx_1024_to_1518_byte_packets = + stats.common.tx_1024_to_1518_byte_packets; + p_common->tx_pause_frames = stats.common.tx_pause_frames; + p_common->tx_pfc_frames = stats.common.tx_pfc_frames; + p_common->brb_truncates = stats.common.brb_truncates; + p_common->brb_discards = stats.common.brb_discards; + p_common->tx_mac_ctrl_frames = stats.common.tx_mac_ctrl_frames; + + if (QEDE_IS_BB(edev)) { + struct qede_stats_bb *p_bb = &edev->stats.bb; + + p_bb->rx_1519_to_1522_byte_packets = + stats.bb.rx_1519_to_1522_byte_packets; + p_bb->rx_1519_to_2047_byte_packets = + stats.bb.rx_1519_to_2047_byte_packets; + p_bb->rx_2048_to_4095_byte_packets = + stats.bb.rx_2048_to_4095_byte_packets; + p_bb->rx_4096_to_9216_byte_packets = + stats.bb.rx_4096_to_9216_byte_packets; + p_bb->rx_9217_to_16383_byte_packets = + stats.bb.rx_9217_to_16383_byte_packets; + p_bb->tx_1519_to_2047_byte_packets = + stats.bb.tx_1519_to_2047_byte_packets; + p_bb->tx_2048_to_4095_byte_packets = + stats.bb.tx_2048_to_4095_byte_packets; + p_bb->tx_4096_to_9216_byte_packets = + stats.bb.tx_4096_to_9216_byte_packets; + p_bb->tx_9217_to_16383_byte_packets = + stats.bb.tx_9217_to_16383_byte_packets; + p_bb->tx_lpi_entry_count = stats.bb.tx_lpi_entry_count; + p_bb->tx_total_collisions = stats.bb.tx_total_collisions; + } else { + struct qede_stats_ah *p_ah = &edev->stats.ah; + + p_ah->rx_1519_to_max_byte_packets = + stats.ah.rx_1519_to_max_byte_packets; + p_ah->tx_1519_to_max_byte_packets = + stats.ah.tx_1519_to_max_byte_packets; + } } static void qede_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { struct qede_dev *edev = netdev_priv(dev); + struct qede_stats_common *p_common; qede_fill_by_demand_stats(edev); + p_common = &edev->stats.common; - stats->rx_packets = edev->stats.rx_ucast_pkts + - edev->stats.rx_mcast_pkts + - edev->stats.rx_bcast_pkts; - stats->tx_packets = edev->stats.tx_ucast_pkts + - edev->stats.tx_mcast_pkts + - edev->stats.tx_bcast_pkts; - - stats->rx_bytes = edev->stats.rx_ucast_bytes + - edev->stats.rx_mcast_bytes + - edev->stats.rx_bcast_bytes; + stats->rx_packets = p_common->rx_ucast_pkts + p_common->rx_mcast_pkts + + p_common->rx_bcast_pkts; + stats->tx_packets = p_common->tx_ucast_pkts + p_common->tx_mcast_pkts + + p_common->tx_bcast_pkts; - stats->tx_bytes = edev->stats.tx_ucast_bytes + - edev->stats.tx_mcast_bytes + - edev->stats.tx_bcast_bytes; + stats->rx_bytes = p_common->rx_ucast_bytes + p_common->rx_mcast_bytes + + p_common->rx_bcast_bytes; + stats->tx_bytes = p_common->tx_ucast_bytes + p_common->tx_mcast_bytes + + p_common->tx_bcast_bytes; - stats->tx_errors = edev->stats.tx_err_drop_pkts; - stats->multicast = edev->stats.rx_mcast_pkts + - edev->stats.rx_bcast_pkts; + stats->tx_errors = p_common->tx_err_drop_pkts; + stats->multicast = p_common->rx_mcast_pkts + p_common->rx_bcast_pkts; - stats->rx_fifo_errors = edev->stats.no_buff_discards; + stats->rx_fifo_errors = p_common->no_buff_discards; - stats->collisions = edev->stats.tx_total_collisions; - stats->rx_crc_errors = edev->stats.rx_crc_errors; - stats->rx_frame_errors = edev->stats.rx_align_errors; + if (QEDE_IS_BB(edev)) + stats->collisions = edev->stats.bb.tx_total_collisions; + stats->rx_crc_errors = p_common->rx_crc_errors; + stats->rx_frame_errors = p_common->rx_align_errors; } #ifdef CONFIG_QED_SRIOV diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index fde56c436f71..8e0065c52857 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -300,6 +300,11 @@ struct qed_sb_info { struct qed_dev *cdev; }; +enum qed_dev_type { + QED_DEV_TYPE_BB, + QED_DEV_TYPE_AH, +}; + struct qed_dev_info { unsigned long pci_mem_start; unsigned long pci_mem_end; @@ -325,6 +330,8 @@ struct qed_dev_info { u16 mtu; bool wol_support; + + enum qed_dev_type dev_type; }; enum qed_sb_type { @@ -752,7 +759,7 @@ enum qed_mf_mode { QED_MF_NPAR, }; -struct qed_eth_stats { +struct qed_eth_stats_common { u64 no_buff_discards; u64 packet_too_big_discard; u64 ttl0_discard; @@ -784,11 +791,6 @@ struct qed_eth_stats { u64 rx_256_to_511_byte_packets; u64 rx_512_to_1023_byte_packets; u64 rx_1024_to_1518_byte_packets; - u64 rx_1519_to_1522_byte_packets; - u64 rx_1519_to_2047_byte_packets; - u64 rx_2048_to_4095_byte_packets; - u64 rx_4096_to_9216_byte_packets; - u64 rx_9217_to_16383_byte_packets; u64 rx_crc_errors; u64 rx_mac_crtl_frames; u64 rx_pause_frames; @@ -805,14 +807,8 @@ struct qed_eth_stats { u64 tx_256_to_511_byte_packets; u64 tx_512_to_1023_byte_packets; u64 tx_1024_to_1518_byte_packets; - u64 tx_1519_to_2047_byte_packets; - u64 tx_2048_to_4095_byte_packets; - u64 tx_4096_to_9216_byte_packets; - u64 tx_9217_to_16383_byte_packets; u64 tx_pause_frames; u64 tx_pfc_frames; - u64 tx_lpi_entry_count; - u64 tx_total_collisions; u64 brb_truncates; u64 brb_discards; u64 rx_mac_bytes; @@ -827,6 +823,34 @@ struct qed_eth_stats { u64 tx_mac_ctrl_frames; }; +struct qed_eth_stats_bb { + u64 rx_1519_to_1522_byte_packets; + u64 rx_1519_to_2047_byte_packets; + u64 rx_2048_to_4095_byte_packets; + u64 rx_4096_to_9216_byte_packets; + u64 rx_9217_to_16383_byte_packets; + u64 tx_1519_to_2047_byte_packets; + u64 tx_2048_to_4095_byte_packets; + u64 tx_4096_to_9216_byte_packets; + u64 tx_9217_to_16383_byte_packets; + u64 tx_lpi_entry_count; + u64 tx_total_collisions; +}; + +struct qed_eth_stats_ah { + u64 rx_1519_to_max_byte_packets; + u64 tx_1519_to_max_byte_packets; +}; + +struct qed_eth_stats { + struct qed_eth_stats_common common; + + union { + struct qed_eth_stats_bb bb; + struct qed_eth_stats_ah ah; + }; +}; + #define QED_SB_IDX 0x0002 #define RX_PI 0 diff --git a/include/linux/qed/rdma_common.h b/include/linux/qed/rdma_common.h index f773aa5e746f..72c770f9f666 100644 --- a/include/linux/qed/rdma_common.h +++ b/include/linux/qed/rdma_common.h @@ -52,7 +52,8 @@ #define RDMA_MAX_PDS (64 * 1024) #define RDMA_NUM_STATISTIC_COUNTERS MAX_NUM_VPORTS -#define RDMA_NUM_STATISTIC_COUNTERS_BB MAX_NUM_VPORTS_BB +#define RDMA_NUM_STATISTIC_COUNTERS_K2 MAX_NUM_VPORTS_K2 +#define RDMA_NUM_STATISTIC_COUNTERS_BB MAX_NUM_VPORTS_BB #define RDMA_TASK_TYPE (PROTOCOLID_ROCE) -- cgit From 96a39aed25e6559b160786117df124084feb9080 Mon Sep 17 00:00:00 2001 From: Aaron Salter Date: Fri, 2 Dec 2016 12:33:02 -0800 Subject: i40e: Acquire NVM lock before reads on all devices Acquire NVM lock before reads on all devices. Previously, locks were only used for X722 and later. Fixes an issue where simultaneous X710 NVM accesses were interfering with each other. Change-ID: If570bb7acf958cef58725ec2a2011cead6f80638 Signed-off-by: Aaron Salter Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_nvm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 38ee18f11124..800bd55d0159 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -292,14 +292,14 @@ i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, { enum i40e_status_code ret_code = 0; - if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) { - ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); - if (!ret_code) { + ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); + if (!ret_code) { + if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) { ret_code = i40e_read_nvm_word_aq(hw, offset, data); - i40e_release_nvm(hw); + } else { + ret_code = i40e_read_nvm_word_srctl(hw, offset, data); } - } else { - ret_code = i40e_read_nvm_word_srctl(hw, offset, data); + i40e_release_nvm(hw); } return ret_code; } -- cgit From d60be2ca9c42e127702a8fd556d696b101f3c09b Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Tue, 24 Jan 2017 10:23:58 -0800 Subject: i40e: fix up recent proxy and wol bits for X722_SUPPORT Some opcodes added & reordered to be in numerical order with the rest of the opcodes. This patch adds admin queue structs to support Wake on LAN feature for X722. Signed-off-by: Shannon Nelson Signed-off-by: Carolyn Wyborny Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h | 65 +++++++++++++++++++++- .../net/ethernet/intel/i40evf/i40e_adminq_cmd.h | 65 +++++++++++++++++++++- 2 files changed, 128 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 451f48b7540a..251074c677c4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -132,6 +132,10 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_list_func_capabilities = 0x000A, i40e_aqc_opc_list_dev_capabilities = 0x000B, + /* Proxy commands */ + i40e_aqc_opc_set_proxy_config = 0x0104, + i40e_aqc_opc_set_ns_proxy_table_entry = 0x0105, + /* LAA */ i40e_aqc_opc_mac_address_read = 0x0107, i40e_aqc_opc_mac_address_write = 0x0108, @@ -139,6 +143,10 @@ enum i40e_admin_queue_opc { /* PXE */ i40e_aqc_opc_clear_pxe_mode = 0x0110, + /* WoL commands */ + i40e_aqc_opc_set_wol_filter = 0x0120, + i40e_aqc_opc_get_wake_reason = 0x0121, + /* internal switch commands */ i40e_aqc_opc_get_switch_config = 0x0200, i40e_aqc_opc_add_statistics = 0x0201, @@ -177,6 +185,7 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_remove_control_packet_filter = 0x025B, i40e_aqc_opc_add_cloud_filters = 0x025C, i40e_aqc_opc_remove_cloud_filters = 0x025D, + i40e_aqc_opc_clear_wol_switch_filters = 0x025E, i40e_aqc_opc_add_mirror_rule = 0x0260, i40e_aqc_opc_delete_mirror_rule = 0x0261, @@ -563,6 +572,56 @@ struct i40e_aqc_clear_pxe { I40E_CHECK_CMD_LENGTH(i40e_aqc_clear_pxe); +/* Set WoL Filter (0x0120) */ + +struct i40e_aqc_set_wol_filter { + __le16 filter_index; +#define I40E_AQC_MAX_NUM_WOL_FILTERS 8 +#define I40E_AQC_SET_WOL_FILTER_TYPE_MAGIC_SHIFT 15 +#define I40E_AQC_SET_WOL_FILTER_TYPE_MAGIC_MASK (0x1 << \ + I40E_AQC_SET_WOL_FILTER_TYPE_MAGIC_SHIFT) + +#define I40E_AQC_SET_WOL_FILTER_INDEX_SHIFT 0 +#define I40E_AQC_SET_WOL_FILTER_INDEX_MASK (0x7 << \ + I40E_AQC_SET_WOL_FILTER_INDEX_SHIFT) + __le16 cmd_flags; +#define I40E_AQC_SET_WOL_FILTER 0x8000 +#define I40E_AQC_SET_WOL_FILTER_NO_TCO_WOL 0x4000 +#define I40E_AQC_SET_WOL_FILTER_ACTION_CLEAR 0 +#define I40E_AQC_SET_WOL_FILTER_ACTION_SET 1 + __le16 valid_flags; +#define I40E_AQC_SET_WOL_FILTER_ACTION_VALID 0x8000 +#define I40E_AQC_SET_WOL_FILTER_NO_TCO_ACTION_VALID 0x4000 + u8 reserved[2]; + __le32 address_high; + __le32 address_low; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_set_wol_filter); + +struct i40e_aqc_set_wol_filter_data { + u8 filter[128]; + u8 mask[16]; +}; + +I40E_CHECK_STRUCT_LEN(0x90, i40e_aqc_set_wol_filter_data); + +/* Get Wake Reason (0x0121) */ + +struct i40e_aqc_get_wake_reason_completion { + u8 reserved_1[2]; + __le16 wake_reason; +#define I40E_AQC_GET_WAKE_UP_REASON_WOL_REASON_MATCHED_INDEX_SHIFT 0 +#define I40E_AQC_GET_WAKE_UP_REASON_WOL_REASON_MATCHED_INDEX_MASK (0xFF << \ + I40E_AQC_GET_WAKE_UP_REASON_WOL_REASON_MATCHED_INDEX_SHIFT) +#define I40E_AQC_GET_WAKE_UP_REASON_WOL_REASON_RESERVED_SHIFT 8 +#define I40E_AQC_GET_WAKE_UP_REASON_WOL_REASON_RESERVED_MASK (0xFF << \ + I40E_AQC_GET_WAKE_UP_REASON_WOL_REASON_RESERVED_SHIFT) + u8 reserved_2[12]; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_get_wake_reason_completion); + /* Switch configuration commands (0x02xx) */ /* Used by many indirect commands that only pass an seid and a buffer in the @@ -645,6 +704,8 @@ struct i40e_aqc_set_port_parameters { #define I40E_AQ_SET_P_PARAMS_PAD_SHORT_PACKETS 2 /* must set! */ #define I40E_AQ_SET_P_PARAMS_DOUBLE_VLAN_ENA 4 __le16 bad_frame_vsi; +#define I40E_AQ_SET_P_PARAMS_BFRAME_SEID_SHIFT 0x0 +#define I40E_AQ_SET_P_PARAMS_BFRAME_SEID_MASK 0x3FF __le16 default_seid; /* reserved for command */ u8 reserved[10]; }; @@ -696,6 +757,7 @@ I40E_CHECK_STRUCT_LEN(0x10, i40e_aqc_switch_resource_alloc_element_resp); /* Set Switch Configuration (direct 0x0205) */ struct i40e_aqc_set_switch_config { __le16 flags; +/* flags used for both fields below */ #define I40E_AQ_SET_SWITCH_CFG_PROMISC 0x0001 #define I40E_AQ_SET_SWITCH_CFG_L2_FILTER 0x0002 __le16 valid_flags; @@ -1844,11 +1906,12 @@ struct i40e_aqc_get_link_status { #define I40E_AQ_CONFIG_FEC_RS_ENA 0x02 #define I40E_AQ_CONFIG_CRC_ENA 0x04 #define I40E_AQ_CONFIG_PACING_MASK 0x78 - u8 external_power_ability; + u8 power_desc; #define I40E_AQ_LINK_POWER_CLASS_1 0x00 #define I40E_AQ_LINK_POWER_CLASS_2 0x01 #define I40E_AQ_LINK_POWER_CLASS_3 0x02 #define I40E_AQ_LINK_POWER_CLASS_4 0x03 +#define I40E_AQ_PWR_CLASS_MASK 0x03 u8 reserved[4]; }; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h index eeb9864bc5b1..c28cb8f27243 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h @@ -132,6 +132,10 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_list_func_capabilities = 0x000A, i40e_aqc_opc_list_dev_capabilities = 0x000B, + /* Proxy commands */ + i40e_aqc_opc_set_proxy_config = 0x0104, + i40e_aqc_opc_set_ns_proxy_table_entry = 0x0105, + /* LAA */ i40e_aqc_opc_mac_address_read = 0x0107, i40e_aqc_opc_mac_address_write = 0x0108, @@ -139,6 +143,10 @@ enum i40e_admin_queue_opc { /* PXE */ i40e_aqc_opc_clear_pxe_mode = 0x0110, + /* WoL commands */ + i40e_aqc_opc_set_wol_filter = 0x0120, + i40e_aqc_opc_get_wake_reason = 0x0121, + /* internal switch commands */ i40e_aqc_opc_get_switch_config = 0x0200, i40e_aqc_opc_add_statistics = 0x0201, @@ -177,6 +185,7 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_remove_control_packet_filter = 0x025B, i40e_aqc_opc_add_cloud_filters = 0x025C, i40e_aqc_opc_remove_cloud_filters = 0x025D, + i40e_aqc_opc_clear_wol_switch_filters = 0x025E, i40e_aqc_opc_add_mirror_rule = 0x0260, i40e_aqc_opc_delete_mirror_rule = 0x0261, @@ -558,6 +567,56 @@ struct i40e_aqc_clear_pxe { I40E_CHECK_CMD_LENGTH(i40e_aqc_clear_pxe); +/* Set WoL Filter (0x0120) */ + +struct i40e_aqc_set_wol_filter { + __le16 filter_index; +#define I40E_AQC_MAX_NUM_WOL_FILTERS 8 +#define I40E_AQC_SET_WOL_FILTER_TYPE_MAGIC_SHIFT 15 +#define I40E_AQC_SET_WOL_FILTER_TYPE_MAGIC_MASK (0x1 << \ + I40E_AQC_SET_WOL_FILTER_TYPE_MAGIC_SHIFT) + +#define I40E_AQC_SET_WOL_FILTER_INDEX_SHIFT 0 +#define I40E_AQC_SET_WOL_FILTER_INDEX_MASK (0x7 << \ + I40E_AQC_SET_WOL_FILTER_INDEX_SHIFT) + __le16 cmd_flags; +#define I40E_AQC_SET_WOL_FILTER 0x8000 +#define I40E_AQC_SET_WOL_FILTER_NO_TCO_WOL 0x4000 +#define I40E_AQC_SET_WOL_FILTER_ACTION_CLEAR 0 +#define I40E_AQC_SET_WOL_FILTER_ACTION_SET 1 + __le16 valid_flags; +#define I40E_AQC_SET_WOL_FILTER_ACTION_VALID 0x8000 +#define I40E_AQC_SET_WOL_FILTER_NO_TCO_ACTION_VALID 0x4000 + u8 reserved[2]; + __le32 address_high; + __le32 address_low; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_set_wol_filter); + +struct i40e_aqc_set_wol_filter_data { + u8 filter[128]; + u8 mask[16]; +}; + +I40E_CHECK_STRUCT_LEN(0x90, i40e_aqc_set_wol_filter_data); + +/* Get Wake Reason (0x0121) */ + +struct i40e_aqc_get_wake_reason_completion { + u8 reserved_1[2]; + __le16 wake_reason; +#define I40E_AQC_GET_WAKE_UP_REASON_WOL_REASON_MATCHED_INDEX_SHIFT 0 +#define I40E_AQC_GET_WAKE_UP_REASON_WOL_REASON_MATCHED_INDEX_MASK (0xFF << \ + I40E_AQC_GET_WAKE_UP_REASON_WOL_REASON_MATCHED_INDEX_SHIFT) +#define I40E_AQC_GET_WAKE_UP_REASON_WOL_REASON_RESERVED_SHIFT 8 +#define I40E_AQC_GET_WAKE_UP_REASON_WOL_REASON_RESERVED_MASK (0xFF << \ + I40E_AQC_GET_WAKE_UP_REASON_WOL_REASON_RESERVED_SHIFT) + u8 reserved_2[12]; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_get_wake_reason_completion); + /* Switch configuration commands (0x02xx) */ /* Used by many indirect commands that only pass an seid and a buffer in the @@ -640,6 +699,8 @@ struct i40e_aqc_set_port_parameters { #define I40E_AQ_SET_P_PARAMS_PAD_SHORT_PACKETS 2 /* must set! */ #define I40E_AQ_SET_P_PARAMS_DOUBLE_VLAN_ENA 4 __le16 bad_frame_vsi; +#define I40E_AQ_SET_P_PARAMS_BFRAME_SEID_SHIFT 0x0 +#define I40E_AQ_SET_P_PARAMS_BFRAME_SEID_MASK 0x3FF __le16 default_seid; /* reserved for command */ u8 reserved[10]; }; @@ -691,6 +752,7 @@ I40E_CHECK_STRUCT_LEN(0x10, i40e_aqc_switch_resource_alloc_element_resp); /* Set Switch Configuration (direct 0x0205) */ struct i40e_aqc_set_switch_config { __le16 flags; +/* flags used for both fields below */ #define I40E_AQ_SET_SWITCH_CFG_PROMISC 0x0001 #define I40E_AQ_SET_SWITCH_CFG_L2_FILTER 0x0002 __le16 valid_flags; @@ -1839,11 +1901,12 @@ struct i40e_aqc_get_link_status { #define I40E_AQ_CONFIG_FEC_RS_ENA 0x02 #define I40E_AQ_CONFIG_CRC_ENA 0x04 #define I40E_AQ_CONFIG_PACING_MASK 0x78 - u8 external_power_ability; + u8 power_desc; #define I40E_AQ_LINK_POWER_CLASS_1 0x00 #define I40E_AQ_LINK_POWER_CLASS_2 0x01 #define I40E_AQ_LINK_POWER_CLASS_3 0x02 #define I40E_AQ_LINK_POWER_CLASS_4 0x03 +#define I40E_AQ_PWR_CLASS_MASK 0x03 u8 reserved[4]; }; -- cgit From ed0e894de7c1339be55ca0dcc11783d923ac5248 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Tue, 24 Jan 2017 10:23:59 -0800 Subject: i40evf: add client interface In preparation for upcoming RDMA-capable hardware, add a client interface to the VF driver. This is a slightly-simplified version of the PF client interface, with the names changed to protect the innocent. Due to the nature of the VF<->PF interactions, the client interface sometimes needs to call back into itself to pass messages. Because of this, we can't use the coarse-grained locking like the PF's client interface uses. Instead, we handle all client interactions in a separate thread so the watchdog can still run and process virtual channel messages. Signed-off-by: Mitch Williams Signed-off-by: Jesse Brandeburg Signed-off-by: Anjali Singhai Jain Signed-off-by: Avinash Dayanand Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/Makefile | 2 +- drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h | 33 ++ drivers/net/ethernet/intel/i40evf/i40evf.h | 29 +- drivers/net/ethernet/intel/i40evf/i40evf_client.c | 563 +++++++++++++++++++++ drivers/net/ethernet/intel/i40evf/i40evf_client.h | 166 ++++++ drivers/net/ethernet/intel/i40evf/i40evf_main.c | 83 ++- .../net/ethernet/intel/i40evf/i40evf_virtchnl.c | 13 +- 7 files changed, 879 insertions(+), 10 deletions(-) create mode 100644 drivers/net/ethernet/intel/i40evf/i40evf_client.c create mode 100644 drivers/net/ethernet/intel/i40evf/i40evf_client.h diff --git a/drivers/net/ethernet/intel/i40evf/Makefile b/drivers/net/ethernet/intel/i40evf/Makefile index 3a423836a565..827c7a6ed0ba 100644 --- a/drivers/net/ethernet/intel/i40evf/Makefile +++ b/drivers/net/ethernet/intel/i40evf/Makefile @@ -32,5 +32,5 @@ obj-$(CONFIG_I40EVF) += i40evf.o i40evf-objs := i40evf_main.o i40evf_ethtool.o i40evf_virtchnl.o \ - i40e_txrx.o i40e_common.o i40e_adminq.o + i40e_txrx.o i40e_common.o i40e_adminq.o i40evf_client.o diff --git a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h index d38a2b2aea2b..f431fbc4a3e7 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h @@ -81,7 +81,9 @@ enum i40e_virtchnl_ops { I40E_VIRTCHNL_OP_GET_STATS = 15, I40E_VIRTCHNL_OP_FCOE = 16, I40E_VIRTCHNL_OP_EVENT = 17, /* must ALWAYS be 17 */ + I40E_VIRTCHNL_OP_IWARP = 20, I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP = 21, + I40E_VIRTCHNL_OP_RELEASE_IWARP_IRQ_MAP = 22, I40E_VIRTCHNL_OP_CONFIG_RSS_KEY = 23, I40E_VIRTCHNL_OP_CONFIG_RSS_LUT = 24, I40E_VIRTCHNL_OP_GET_RSS_HENA_CAPS = 25, @@ -393,6 +395,37 @@ struct i40e_virtchnl_pf_event { int severity; }; +/* I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP + * VF uses this message to request PF to map IWARP vectors to IWARP queues. + * The request for this originates from the VF IWARP driver through + * a client interface between VF LAN and VF IWARP driver. + * A vector could have an AEQ and CEQ attached to it although + * there is a single AEQ per VF IWARP instance in which case + * most vectors will have an INVALID_IDX for aeq and valid idx for ceq. + * There will never be a case where there will be multiple CEQs attached + * to a single vector. + * PF configures interrupt mapping and returns status. + */ + +/* HW does not define a type value for AEQ; only for RX/TX and CEQ. + * In order for us to keep the interface simple, SW will define a + * unique type value for AEQ. + */ +#define I40E_QUEUE_TYPE_PE_AEQ 0x80 +#define I40E_QUEUE_INVALID_IDX 0xFFFF + +struct i40e_virtchnl_iwarp_qv_info { + u32 v_idx; /* msix_vector */ + u16 ceq_idx; + u16 aeq_idx; + u8 itr_idx; +}; + +struct i40e_virtchnl_iwarp_qvlist_info { + u32 num_vectors; + struct i40e_virtchnl_iwarp_qv_info qv_info[1]; +}; + /* VF reset states - these are written into the RSTAT register: * I40E_VFGEN_RSTAT1 on the PF * I40E_VFGEN_RSTAT on the VF diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 00c42d803276..b2b48511f457 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -60,6 +60,7 @@ struct i40e_vsi { int base_vector; u16 work_limit; u16 qs_handle; + void *priv; /* client driver data reference. */ }; /* How many Rx Buffers do we bundle into one write to the hardware ? */ @@ -169,6 +170,7 @@ enum i40evf_state_t { enum i40evf_critical_section_t { __I40EVF_IN_CRITICAL_TASK, /* cannot be interrupted */ + __I40EVF_IN_CLIENT_TASK, }; /* make common code happy */ #define __I40E_DOWN __I40EVF_DOWN @@ -178,6 +180,7 @@ struct i40evf_adapter { struct timer_list watchdog_timer; struct work_struct reset_task; struct work_struct adminq_task; + struct delayed_work client_task; struct delayed_work init_task; struct i40e_q_vector *q_vectors; struct list_head vlan_filter_list; @@ -195,7 +198,10 @@ struct i40evf_adapter { u64 hw_csum_rx_error; u32 rx_desc_count; int num_msix_vectors; + int num_iwarp_msix; + int iwarp_base_vector; u32 client_pending; + struct i40e_client_instance *cinst; struct msix_entry *msix_entries; u32 flags; @@ -211,8 +217,11 @@ struct i40evf_adapter { #define I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE BIT(12) #define I40EVF_FLAG_ADDR_SET_BY_PF BIT(13) #define I40EVF_FLAG_SERVICE_CLIENT_REQUESTED BIT(14) -#define I40EVF_FLAG_PROMISC_ON BIT(15) -#define I40EVF_FLAG_ALLMULTI_ON BIT(16) +#define I40EVF_FLAG_CLIENT_NEEDS_OPEN BIT(15) +#define I40EVF_FLAG_CLIENT_NEEDS_CLOSE BIT(16) +#define I40EVF_FLAG_CLIENT_NEEDS_L2_PARAMS BIT(17) +#define I40EVF_FLAG_PROMISC_ON BIT(18) +#define I40EVF_FLAG_ALLMULTI_ON BIT(19) /* duplicates for common code */ #define I40E_FLAG_FDIR_ATR_ENABLED 0 #define I40E_FLAG_DCB_ENABLED 0 @@ -258,10 +267,11 @@ struct i40evf_adapter { bool link_up; enum i40e_aq_link_speed link_speed; enum i40e_virtchnl_ops current_op; -#define CLIENT_ENABLED(_a) ((_a)->vf_res ? \ +#define CLIENT_ALLOWED(_a) ((_a)->vf_res ? \ (_a)->vf_res->vf_offload_flags & \ I40E_VIRTCHNL_VF_OFFLOAD_IWARP : \ 0) +#define CLIENT_ENABLED(_a) ((_a)->cinst) /* RSS by the PF should be preferred over RSS via other methods. */ #define RSS_PF(_a) ((_a)->vf_res->vf_offload_flags & \ I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF) @@ -292,6 +302,12 @@ struct i40evf_adapter { /* Ethtool Private Flags */ +/* lan device */ +struct i40e_device { + struct list_head list; + struct i40evf_adapter *vf; +}; + /* needed by i40evf_ethtool.c */ extern char i40evf_driver_name[]; extern const char i40evf_driver_version[]; @@ -337,4 +353,11 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, enum i40e_virtchnl_ops v_opcode, i40e_status v_retval, u8 *msg, u16 msglen); int i40evf_config_rss(struct i40evf_adapter *adapter); +int i40evf_lan_add_device(struct i40evf_adapter *adapter); +int i40evf_lan_del_device(struct i40evf_adapter *adapter); +void i40evf_client_subtask(struct i40evf_adapter *adapter); +void i40evf_notify_client_message(struct i40e_vsi *vsi, u8 *msg, u16 len); +void i40evf_notify_client_l2_params(struct i40e_vsi *vsi); +void i40evf_notify_client_open(struct i40e_vsi *vsi); +void i40evf_notify_client_close(struct i40e_vsi *vsi, bool reset); #endif /* _I40EVF_H_ */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_client.c b/drivers/net/ethernet/intel/i40evf/i40evf_client.c new file mode 100644 index 000000000000..5b43e5b6e2eb --- /dev/null +++ b/drivers/net/ethernet/intel/i40evf/i40evf_client.c @@ -0,0 +1,563 @@ +#include +#include + +#include "i40evf.h" +#include "i40e_prototype.h" +#include "i40evf_client.h" + +static +const char i40evf_client_interface_version_str[] = I40EVF_CLIENT_VERSION_STR; +static struct i40e_client *vf_registered_client; +static LIST_HEAD(i40evf_devices); +static DEFINE_MUTEX(i40evf_device_mutex); + +static u32 i40evf_client_virtchnl_send(struct i40e_info *ldev, + struct i40e_client *client, + u8 *msg, u16 len); + +static int i40evf_client_setup_qvlist(struct i40e_info *ldev, + struct i40e_client *client, + struct i40e_qvlist_info *qvlist_info); + +static struct i40e_ops i40evf_lan_ops = { + .virtchnl_send = i40evf_client_virtchnl_send, + .setup_qvlist = i40evf_client_setup_qvlist, +}; + +/** + * i40evf_notify_client_message - call the client message receive callback + * @vsi: the VSI associated with this client + * @msg: message buffer + * @len: length of message + * + * If there is a client to this VSI, call the client + **/ +void i40evf_notify_client_message(struct i40e_vsi *vsi, u8 *msg, u16 len) +{ + struct i40evf_adapter *adapter = vsi->back; + struct i40e_client_instance *cinst = adapter->cinst; + + if (!vsi) + return; + + if (!cinst || !cinst->client || !cinst->client->ops || + !cinst->client->ops->virtchnl_receive) { + dev_dbg(&vsi->back->pdev->dev, + "Cannot locate client instance virtchnl_receive function\n"); + return; + } + cinst->client->ops->virtchnl_receive(&cinst->lan_info, cinst->client, + msg, len); +} + +/** + * i40evf_notify_client_l2_params - call the client notify callback + * @vsi: the VSI with l2 param changes + * + * If there is a client to this VSI, call the client + **/ +void i40evf_notify_client_l2_params(struct i40e_vsi *vsi) +{ + struct i40evf_adapter *adapter = vsi->back; + struct i40e_client_instance *cinst = adapter->cinst; + struct i40e_params params; + + if (!vsi) + return; + memset(¶ms, 0, sizeof(params)); + params.mtu = vsi->netdev->mtu; + params.link_up = vsi->back->link_up; + params.qos.prio_qos[0].qs_handle = vsi->qs_handle; + + if (!cinst || !cinst->client || !cinst->client->ops || + !cinst->client->ops->l2_param_change) { + dev_dbg(&vsi->back->pdev->dev, + "Cannot locate client instance l2_param_change function\n"); + return; + } + cinst->client->ops->l2_param_change(&cinst->lan_info, cinst->client, + ¶ms); +} + +/** + * i40evf_notify_client_open - call the client open callback + * @vsi: the VSI with netdev opened + * + * If there is a client to this netdev, call the client with open + **/ +void i40evf_notify_client_open(struct i40e_vsi *vsi) +{ + struct i40evf_adapter *adapter = vsi->back; + struct i40e_client_instance *cinst = adapter->cinst; + int ret; + + if (!cinst || !cinst->client || !cinst->client->ops || + !cinst->client->ops->open) { + dev_dbg(&vsi->back->pdev->dev, + "Cannot locate client instance open function\n"); + return; + } + if (!(test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cinst->state))) { + ret = cinst->client->ops->open(&cinst->lan_info, cinst->client); + if (!ret) + set_bit(__I40E_CLIENT_INSTANCE_OPENED, &cinst->state); + } +} + +/** + * i40evf_client_release_qvlist - send a message to the PF to release iwarp qv map + * @ldev: pointer to L2 context. + * + * Return 0 on success or < 0 on error + **/ +static int i40evf_client_release_qvlist(struct i40e_info *ldev) +{ + struct i40evf_adapter *adapter = ldev->vf; + i40e_status err; + + if (adapter->aq_required) + return -EAGAIN; + + err = i40e_aq_send_msg_to_pf(&adapter->hw, + I40E_VIRTCHNL_OP_RELEASE_IWARP_IRQ_MAP, + I40E_SUCCESS, NULL, 0, NULL); + + if (err) + dev_err(&adapter->pdev->dev, + "Unable to send iWarp vector release message to PF, error %d, aq status %d\n", + err, adapter->hw.aq.asq_last_status); + + return err; +} + +/** + * i40evf_notify_client_close - call the client close callback + * @vsi: the VSI with netdev closed + * @reset: true when close called due to reset pending + * + * If there is a client to this netdev, call the client with close + **/ +void i40evf_notify_client_close(struct i40e_vsi *vsi, bool reset) +{ + struct i40evf_adapter *adapter = vsi->back; + struct i40e_client_instance *cinst = adapter->cinst; + + if (!cinst || !cinst->client || !cinst->client->ops || + !cinst->client->ops->close) { + dev_dbg(&vsi->back->pdev->dev, + "Cannot locate client instance close function\n"); + return; + } + cinst->client->ops->close(&cinst->lan_info, cinst->client, reset); + i40evf_client_release_qvlist(&cinst->lan_info); + clear_bit(__I40E_CLIENT_INSTANCE_OPENED, &cinst->state); +} + +/** + * i40evf_client_add_instance - add a client instance to the instance list + * @adapter: pointer to the board struct + * @client: pointer to a client struct in the client list. + * + * Returns cinst ptr on success, NULL on failure + **/ +static struct i40e_client_instance * +i40evf_client_add_instance(struct i40evf_adapter *adapter) +{ + struct i40e_client_instance *cinst = NULL; + struct netdev_hw_addr *mac = NULL; + struct i40e_vsi *vsi = &adapter->vsi; + int i; + + if (!vf_registered_client) + goto out; + + if (adapter->cinst) { + cinst = adapter->cinst; + goto out; + } + + cinst = kzalloc(sizeof(*cinst), GFP_KERNEL); + if (!cinst) + goto out; + + cinst->lan_info.vf = (void *)adapter; + cinst->lan_info.netdev = vsi->netdev; + cinst->lan_info.pcidev = adapter->pdev; + cinst->lan_info.fid = 0; + cinst->lan_info.ftype = I40E_CLIENT_FTYPE_VF; + cinst->lan_info.hw_addr = adapter->hw.hw_addr; + cinst->lan_info.ops = &i40evf_lan_ops; + cinst->lan_info.version.major = I40EVF_CLIENT_VERSION_MAJOR; + cinst->lan_info.version.minor = I40EVF_CLIENT_VERSION_MINOR; + cinst->lan_info.version.build = I40EVF_CLIENT_VERSION_BUILD; + set_bit(__I40E_CLIENT_INSTANCE_NONE, &cinst->state); + + cinst->lan_info.msix_count = adapter->num_iwarp_msix; + cinst->lan_info.msix_entries = + &adapter->msix_entries[adapter->iwarp_base_vector]; + + for (i = 0; i < I40E_MAX_USER_PRIORITY; i++) { + cinst->lan_info.params.qos.prio_qos[i].tc = 0; + cinst->lan_info.params.qos.prio_qos[i].qs_handle = + vsi->qs_handle; + } + + mac = list_first_entry(&cinst->lan_info.netdev->dev_addrs.list, + struct netdev_hw_addr, list); + if (mac) + ether_addr_copy(cinst->lan_info.lanmac, mac->addr); + else + dev_err(&adapter->pdev->dev, "MAC address list is empty!\n"); + + cinst->client = vf_registered_client; + adapter->cinst = cinst; +out: + return cinst; +} + +/** + * i40evf_client_del_instance - removes a client instance from the list + * @adapter: pointer to the board struct + * @client: pointer to the client struct + * + **/ +static +void i40evf_client_del_instance(struct i40evf_adapter *adapter) +{ + kfree(adapter->cinst); + adapter->cinst = NULL; +} + +/** + * i40evf_client_subtask - client maintenance work + * @adapter: board private structure + **/ +void i40evf_client_subtask(struct i40evf_adapter *adapter) +{ + struct i40e_client *client = vf_registered_client; + struct i40e_client_instance *cinst; + int ret = 0; + + if (adapter->state < __I40EVF_DOWN) + return; + + /* first check client is registered */ + if (!client) + return; + + /* Add the client instance to the instance list */ + cinst = i40evf_client_add_instance(adapter); + if (!cinst) + return; + + dev_info(&adapter->pdev->dev, "Added instance of Client %s\n", + client->name); + + if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cinst->state)) { + /* Send an Open request to the client */ + + if (client->ops && client->ops->open) + ret = client->ops->open(&cinst->lan_info, client); + if (!ret) + set_bit(__I40E_CLIENT_INSTANCE_OPENED, + &cinst->state); + else + /* remove client instance */ + i40evf_client_del_instance(adapter); + } +} + +/** + * i40evf_lan_add_device - add a lan device struct to the list of lan devices + * @adapter: pointer to the board struct + * + * Returns 0 on success or none 0 on error + **/ +int i40evf_lan_add_device(struct i40evf_adapter *adapter) +{ + struct i40e_device *ldev; + int ret = 0; + + mutex_lock(&i40evf_device_mutex); + list_for_each_entry(ldev, &i40evf_devices, list) { + if (ldev->vf == adapter) { + ret = -EEXIST; + goto out; + } + } + ldev = kzalloc(sizeof(*ldev), GFP_KERNEL); + if (!ldev) { + ret = -ENOMEM; + goto out; + } + ldev->vf = adapter; + INIT_LIST_HEAD(&ldev->list); + list_add(&ldev->list, &i40evf_devices); + dev_info(&adapter->pdev->dev, "Added LAN device bus=0x%02x dev=0x%02x func=0x%02x\n", + adapter->hw.bus.bus_id, adapter->hw.bus.device, + adapter->hw.bus.func); + + /* Since in some cases register may have happened before a device gets + * added, we can schedule a subtask to go initiate the clients. + */ + adapter->flags |= I40EVF_FLAG_SERVICE_CLIENT_REQUESTED; + +out: + mutex_unlock(&i40evf_device_mutex); + return ret; +} + +/** + * i40evf_lan_del_device - removes a lan device from the device list + * @adapter: pointer to the board struct + * + * Returns 0 on success or non-0 on error + **/ +int i40evf_lan_del_device(struct i40evf_adapter *adapter) +{ + struct i40e_device *ldev, *tmp; + int ret = -ENODEV; + + mutex_lock(&i40evf_device_mutex); + list_for_each_entry_safe(ldev, tmp, &i40evf_devices, list) { + if (ldev->vf == adapter) { + dev_info(&adapter->pdev->dev, + "Deleted LAN device bus=0x%02x dev=0x%02x func=0x%02x\n", + adapter->hw.bus.bus_id, adapter->hw.bus.device, + adapter->hw.bus.func); + list_del(&ldev->list); + kfree(ldev); + ret = 0; + break; + } + } + + mutex_unlock(&i40evf_device_mutex); + return ret; +} + +/** + * i40evf_client_release - release client specific resources + * @client: pointer to the registered client + * + **/ +static void i40evf_client_release(struct i40e_client *client) +{ + struct i40e_client_instance *cinst; + struct i40e_device *ldev; + struct i40evf_adapter *adapter; + + mutex_lock(&i40evf_device_mutex); + list_for_each_entry(ldev, &i40evf_devices, list) { + adapter = ldev->vf; + cinst = adapter->cinst; + if (!cinst) + continue; + if (test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cinst->state)) { + if (client->ops && client->ops->close) + client->ops->close(&cinst->lan_info, client, + false); + i40evf_client_release_qvlist(&cinst->lan_info); + clear_bit(__I40E_CLIENT_INSTANCE_OPENED, &cinst->state); + + dev_warn(&adapter->pdev->dev, + "Client %s instance closed\n", client->name); + } + /* delete the client instance */ + i40evf_client_del_instance(adapter); + dev_info(&adapter->pdev->dev, "Deleted client instance of Client %s\n", + client->name); + } + mutex_unlock(&i40evf_device_mutex); +} + +/** + * i40evf_client_prepare - prepare client specific resources + * @client: pointer to the registered client + * + **/ +static void i40evf_client_prepare(struct i40e_client *client) +{ + struct i40e_device *ldev; + struct i40evf_adapter *adapter; + + mutex_lock(&i40evf_device_mutex); + list_for_each_entry(ldev, &i40evf_devices, list) { + adapter = ldev->vf; + /* Signal the watchdog to service the client */ + adapter->flags |= I40EVF_FLAG_SERVICE_CLIENT_REQUESTED; + } + mutex_unlock(&i40evf_device_mutex); +} + +/** + * i40evf_client_virtchnl_send - send a message to the PF instance + * @ldev: pointer to L2 context. + * @client: Client pointer. + * @msg: pointer to message buffer + * @len: message length + * + * Return 0 on success or < 0 on error + **/ +static u32 i40evf_client_virtchnl_send(struct i40e_info *ldev, + struct i40e_client *client, + u8 *msg, u16 len) +{ + struct i40evf_adapter *adapter = ldev->vf; + i40e_status err; + + if (adapter->aq_required) + return -EAGAIN; + + err = i40e_aq_send_msg_to_pf(&adapter->hw, I40E_VIRTCHNL_OP_IWARP, + I40E_SUCCESS, msg, len, NULL); + if (err) + dev_err(&adapter->pdev->dev, "Unable to send iWarp message to PF, error %d, aq status %d\n", + err, adapter->hw.aq.asq_last_status); + + return err; +} + +/** + * i40evf_client_setup_qvlist - send a message to the PF to setup iwarp qv map + * @ldev: pointer to L2 context. + * @client: Client pointer. + * @qv_info: queue and vector list + * + * Return 0 on success or < 0 on error + **/ +static int i40evf_client_setup_qvlist(struct i40e_info *ldev, + struct i40e_client *client, + struct i40e_qvlist_info *qvlist_info) +{ + struct i40e_virtchnl_iwarp_qvlist_info *v_qvlist_info; + struct i40evf_adapter *adapter = ldev->vf; + struct i40e_qv_info *qv_info; + i40e_status err; + u32 v_idx, i; + u32 msg_size; + + if (adapter->aq_required) + return -EAGAIN; + + /* A quick check on whether the vectors belong to the client */ + for (i = 0; i < qvlist_info->num_vectors; i++) { + qv_info = &qvlist_info->qv_info[i]; + if (!qv_info) + continue; + v_idx = qv_info->v_idx; + if ((v_idx >= + (adapter->iwarp_base_vector + adapter->num_iwarp_msix)) || + (v_idx < adapter->iwarp_base_vector)) + return -EINVAL; + } + + v_qvlist_info = (struct i40e_virtchnl_iwarp_qvlist_info *)qvlist_info; + msg_size = sizeof(struct i40e_virtchnl_iwarp_qvlist_info) + + (sizeof(struct i40e_virtchnl_iwarp_qv_info) * + (v_qvlist_info->num_vectors - 1)); + + adapter->client_pending |= BIT(I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP); + err = i40e_aq_send_msg_to_pf(&adapter->hw, + I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP, + I40E_SUCCESS, (u8 *)v_qvlist_info, msg_size, NULL); + + if (err) { + dev_err(&adapter->pdev->dev, + "Unable to send iWarp vector config message to PF, error %d, aq status %d\n", + err, adapter->hw.aq.asq_last_status); + goto out; + } + + err = -EBUSY; + for (i = 0; i < 5; i++) { + msleep(100); + if (!(adapter->client_pending & + BIT(I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP))) { + err = 0; + break; + } + } +out: + return err; +} + +/** + * i40evf_register_client - Register a i40e client driver with the L2 driver + * @client: pointer to the i40e_client struct + * + * Returns 0 on success or non-0 on error + **/ +int i40evf_register_client(struct i40e_client *client) +{ + int ret = 0; + + if (!client) { + ret = -EIO; + goto out; + } + + if (strlen(client->name) == 0) { + pr_info("i40evf: Failed to register client with no name\n"); + ret = -EIO; + goto out; + } + + if (vf_registered_client) { + pr_info("i40evf: Client %s has already been registered!\n", + client->name); + ret = -EEXIST; + goto out; + } + + if ((client->version.major != I40EVF_CLIENT_VERSION_MAJOR) || + (client->version.minor != I40EVF_CLIENT_VERSION_MINOR)) { + pr_info("i40evf: Failed to register client %s due to mismatched client interface version\n", + client->name); + pr_info("Client is using version: %02d.%02d.%02d while LAN driver supports %s\n", + client->version.major, client->version.minor, + client->version.build, + i40evf_client_interface_version_str); + ret = -EIO; + goto out; + } + + vf_registered_client = client; + + i40evf_client_prepare(client); + + pr_info("i40evf: Registered client %s with return code %d\n", + client->name, ret); +out: + return ret; +} +EXPORT_SYMBOL(i40evf_register_client); + +/** + * i40evf_unregister_client - Unregister a i40e client driver with the L2 driver + * @client: pointer to the i40e_client struct + * + * Returns 0 on success or non-0 on error + **/ +int i40evf_unregister_client(struct i40e_client *client) +{ + int ret = 0; + + /* When a unregister request comes through we would have to send + * a close for each of the client instances that were opened. + * client_release function is called to handle this. + */ + i40evf_client_release(client); + + if (vf_registered_client != client) { + pr_info("i40evf: Client %s has not been registered\n", + client->name); + ret = -ENODEV; + goto out; + } + vf_registered_client = NULL; + pr_info("i40evf: Unregistered client %s\n", client->name); +out: + return ret; +} +EXPORT_SYMBOL(i40evf_unregister_client); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_client.h b/drivers/net/ethernet/intel/i40evf/i40evf_client.h new file mode 100644 index 000000000000..7d283c7506a5 --- /dev/null +++ b/drivers/net/ethernet/intel/i40evf/i40evf_client.h @@ -0,0 +1,166 @@ +#ifndef _I40E_CLIENT_H_ +#define _I40E_CLIENT_H_ + +#define I40EVF_CLIENT_STR_LENGTH 10 + +/* Client interface version should be updated anytime there is a change in the + * existing APIs or data structures. + */ +#define I40EVF_CLIENT_VERSION_MAJOR 0 +#define I40EVF_CLIENT_VERSION_MINOR 01 +#define I40EVF_CLIENT_VERSION_BUILD 00 +#define I40EVF_CLIENT_VERSION_STR \ + __stringify(I40EVF_CLIENT_VERSION_MAJOR) "." \ + __stringify(I40EVF_CLIENT_VERSION_MINOR) "." \ + __stringify(I40EVF_CLIENT_VERSION_BUILD) + +struct i40e_client_version { + u8 major; + u8 minor; + u8 build; + u8 rsvd; +}; + +enum i40e_client_state { + __I40E_CLIENT_NULL, + __I40E_CLIENT_REGISTERED +}; + +enum i40e_client_instance_state { + __I40E_CLIENT_INSTANCE_NONE, + __I40E_CLIENT_INSTANCE_OPENED, +}; + +struct i40e_ops; +struct i40e_client; + +/* HW does not define a type value for AEQ; only for RX/TX and CEQ. + * In order for us to keep the interface simple, SW will define a + * unique type value for AEQ. + */ +#define I40E_QUEUE_TYPE_PE_AEQ 0x80 +#define I40E_QUEUE_INVALID_IDX 0xFFFF + +struct i40e_qv_info { + u32 v_idx; /* msix_vector */ + u16 ceq_idx; + u16 aeq_idx; + u8 itr_idx; +}; + +struct i40e_qvlist_info { + u32 num_vectors; + struct i40e_qv_info qv_info[1]; +}; + +#define I40E_CLIENT_MSIX_ALL 0xFFFFFFFF + +/* set of LAN parameters useful for clients managed by LAN */ + +/* Struct to hold per priority info */ +struct i40e_prio_qos_params { + u16 qs_handle; /* qs handle for prio */ + u8 tc; /* TC mapped to prio */ + u8 reserved; +}; + +#define I40E_CLIENT_MAX_USER_PRIORITY 8 +/* Struct to hold Client QoS */ +struct i40e_qos_params { + struct i40e_prio_qos_params prio_qos[I40E_CLIENT_MAX_USER_PRIORITY]; +}; + +struct i40e_params { + struct i40e_qos_params qos; + u16 mtu; + u16 link_up; /* boolean */ +}; + +/* Structure to hold LAN device info for a client device */ +struct i40e_info { + struct i40e_client_version version; + u8 lanmac[6]; + struct net_device *netdev; + struct pci_dev *pcidev; + u8 __iomem *hw_addr; + u8 fid; /* function id, PF id or VF id */ +#define I40E_CLIENT_FTYPE_PF 0 +#define I40E_CLIENT_FTYPE_VF 1 + u8 ftype; /* function type, PF or VF */ + void *vf; /* cast to i40evf_adapter */ + + /* All L2 params that could change during the life span of the device + * and needs to be communicated to the client when they change + */ + struct i40e_params params; + struct i40e_ops *ops; + + u16 msix_count; /* number of msix vectors*/ + /* Array down below will be dynamically allocated based on msix_count */ + struct msix_entry *msix_entries; + u16 itr_index; /* Which ITR index the PE driver is suppose to use */ +}; + +struct i40e_ops { + /* setup_q_vector_list enables queues with a particular vector */ + int (*setup_qvlist)(struct i40e_info *ldev, struct i40e_client *client, + struct i40e_qvlist_info *qv_info); + + u32 (*virtchnl_send)(struct i40e_info *ldev, struct i40e_client *client, + u8 *msg, u16 len); + + /* If the PE Engine is unresponsive, RDMA driver can request a reset.*/ + void (*request_reset)(struct i40e_info *ldev, + struct i40e_client *client); +}; + +struct i40e_client_ops { + /* Should be called from register_client() or whenever the driver is + * ready to create a specific client instance. + */ + int (*open)(struct i40e_info *ldev, struct i40e_client *client); + + /* Should be closed when netdev is unavailable or when unregister + * call comes in. If the close happens due to a reset, set the reset + * bit to true. + */ + void (*close)(struct i40e_info *ldev, struct i40e_client *client, + bool reset); + + /* called when some l2 managed parameters changes - mss */ + void (*l2_param_change)(struct i40e_info *ldev, + struct i40e_client *client, + struct i40e_params *params); + + /* called when a message is received from the PF */ + int (*virtchnl_receive)(struct i40e_info *ldev, + struct i40e_client *client, + u8 *msg, u16 len); +}; + +/* Client device */ +struct i40e_client_instance { + struct list_head list; + struct i40e_info lan_info; + struct i40e_client *client; + unsigned long state; +}; + +struct i40e_client { + struct list_head list; /* list of registered clients */ + char name[I40EVF_CLIENT_STR_LENGTH]; + struct i40e_client_version version; + unsigned long state; /* client state */ + atomic_t ref_cnt; /* Count of all the client devices of this kind */ + u32 flags; +#define I40E_CLIENT_FLAGS_LAUNCH_ON_PROBE BIT(0) +#define I40E_TX_FLAGS_NOTIFY_OTHER_EVENTS BIT(2) + u8 type; +#define I40E_CLIENT_IWARP 0 + struct i40e_client_ops *ops; /* client ops provided by the client */ +}; + +/* used by clients */ +int i40evf_register_client(struct i40e_client *client); +int i40evf_unregister_client(struct i40e_client *client); +#endif /* _I40E_CLIENT_H_ */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index f35dcaac5bb7..9492b20da557 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -26,6 +26,7 @@ #include "i40evf.h" #include "i40e_prototype.h" +#include "i40evf_client.h" static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter); static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter); static int i40evf_close(struct net_device *netdev); @@ -1058,6 +1059,8 @@ static void i40evf_up_complete(struct i40evf_adapter *adapter) i40evf_napi_enable_all(adapter); adapter->aq_required |= I40EVF_FLAG_AQ_ENABLE_QUEUES; + if (CLIENT_ENABLED(adapter)) + adapter->flags |= I40EVF_FLAG_CLIENT_NEEDS_OPEN; mod_timer_pending(&adapter->watchdog_timer, jiffies + 1); } @@ -1685,6 +1688,7 @@ static void i40evf_watchdog_task(struct work_struct *work) i40evf_set_promiscuous(adapter, 0); goto watchdog_done; } + schedule_delayed_work(&adapter->client_task, msecs_to_jiffies(5)); if (adapter->state == __I40EVF_RUNNING) i40evf_request_stats(adapter); @@ -1773,10 +1777,17 @@ static void i40evf_reset_task(struct work_struct *work) u32 reg_val; int i = 0, err; - while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, + while (test_and_set_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section)) usleep_range(500, 1000); - + if (CLIENT_ENABLED(adapter)) { + adapter->flags &= ~(I40EVF_FLAG_CLIENT_NEEDS_OPEN | + I40EVF_FLAG_CLIENT_NEEDS_CLOSE | + I40EVF_FLAG_CLIENT_NEEDS_L2_PARAMS | + I40EVF_FLAG_SERVICE_CLIENT_REQUESTED); + cancel_delayed_work_sync(&adapter->client_task); + i40evf_notify_client_close(&adapter->vsi, true); + } i40evf_misc_irq_disable(adapter); if (adapter->flags & I40EVF_FLAG_RESET_NEEDED) { adapter->flags &= ~I40EVF_FLAG_RESET_NEEDED; @@ -1819,6 +1830,7 @@ static void i40evf_reset_task(struct work_struct *work) dev_err(&adapter->pdev->dev, "Reset never finished (%x)\n", reg_val); i40evf_disable_vf(adapter); + clear_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section); return; /* Do not attempt to reinit. It's dead, Jim. */ } @@ -1861,9 +1873,8 @@ continue_reset: } adapter->aq_required |= I40EVF_FLAG_AQ_ADD_MAC_FILTER; adapter->aq_required |= I40EVF_FLAG_AQ_ADD_VLAN_FILTER; - /* Open RDMA Client again */ - adapter->aq_required |= I40EVF_FLAG_SERVICE_CLIENT_REQUESTED; clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); + clear_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section); i40evf_misc_irq_enable(adapter); mod_timer(&adapter->watchdog_timer, jiffies + 2); @@ -1979,6 +1990,48 @@ out: i40evf_misc_irq_enable(adapter); } +/** + * i40evf_client_task - worker thread to perform client work + * @work: pointer to work_struct containing our data + * + * This task handles client interactions. Because client calls can be + * reentrant, we can't handle them in the watchdog. + **/ +static void i40evf_client_task(struct work_struct *work) +{ + struct i40evf_adapter *adapter = + container_of(work, struct i40evf_adapter, client_task.work); + + /* If we can't get the client bit, just give up. We'll be rescheduled + * later. + */ + + if (test_and_set_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section)) + return; + + if (adapter->flags & I40EVF_FLAG_SERVICE_CLIENT_REQUESTED) { + i40evf_client_subtask(adapter); + adapter->flags &= ~I40EVF_FLAG_SERVICE_CLIENT_REQUESTED; + goto out; + } + if (adapter->flags & I40EVF_FLAG_CLIENT_NEEDS_CLOSE) { + i40evf_notify_client_close(&adapter->vsi, false); + adapter->flags &= ~I40EVF_FLAG_CLIENT_NEEDS_CLOSE; + goto out; + } + if (adapter->flags & I40EVF_FLAG_CLIENT_NEEDS_OPEN) { + i40evf_notify_client_open(&adapter->vsi); + adapter->flags &= ~I40EVF_FLAG_CLIENT_NEEDS_OPEN; + goto out; + } + if (adapter->flags & I40EVF_FLAG_CLIENT_NEEDS_L2_PARAMS) { + i40evf_notify_client_l2_params(&adapter->vsi); + adapter->flags &= ~I40EVF_FLAG_CLIENT_NEEDS_L2_PARAMS; + } +out: + clear_bit(__I40EVF_IN_CLIENT_TASK, &adapter->crit_section); +} + /** * i40evf_free_all_tx_resources - Free Tx Resources for All Queues * @adapter: board private structure @@ -2148,6 +2201,8 @@ static int i40evf_close(struct net_device *netdev) set_bit(__I40E_DOWN, &adapter->vsi.state); + if (CLIENT_ENABLED(adapter)) + adapter->flags |= I40EVF_FLAG_CLIENT_NEEDS_CLOSE; i40evf_down(adapter); adapter->state = __I40EVF_DOWN_PENDING; @@ -2188,6 +2243,10 @@ static int i40evf_change_mtu(struct net_device *netdev, int new_mtu) struct i40evf_adapter *adapter = netdev_priv(netdev); netdev->mtu = new_mtu; + if (CLIENT_ENABLED(adapter)) { + i40evf_notify_client_l2_params(&adapter->vsi); + adapter->flags |= I40EVF_FLAG_SERVICE_CLIENT_REQUESTED; + } adapter->flags |= I40EVF_FLAG_RESET_NEEDED; schedule_work(&adapter->reset_task); @@ -2581,6 +2640,12 @@ static void i40evf_init_task(struct work_struct *work) adapter->netdev_registered = true; netif_tx_stop_all_queues(netdev); + if (CLIENT_ALLOWED(adapter)) { + err = i40evf_lan_add_device(adapter); + if (err) + dev_info(&pdev->dev, "Failed to add VF to client API service list: %d\n", + err); + } dev_info(&pdev->dev, "MAC address: %pM\n", adapter->hw.mac.addr); if (netdev->features & NETIF_F_GRO) @@ -2745,6 +2810,7 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) INIT_WORK(&adapter->reset_task, i40evf_reset_task); INIT_WORK(&adapter->adminq_task, i40evf_adminq_task); INIT_WORK(&adapter->watchdog_task, i40evf_watchdog_task); + INIT_DELAYED_WORK(&adapter->client_task, i40evf_client_task); INIT_DELAYED_WORK(&adapter->init_task, i40evf_init_task); schedule_delayed_work(&adapter->init_task, msecs_to_jiffies(5 * (pdev->devfn & 0x07))); @@ -2857,14 +2923,21 @@ static void i40evf_remove(struct pci_dev *pdev) struct i40evf_adapter *adapter = netdev_priv(netdev); struct i40evf_mac_filter *f, *ftmp; struct i40e_hw *hw = &adapter->hw; + int err; cancel_delayed_work_sync(&adapter->init_task); cancel_work_sync(&adapter->reset_task); - + cancel_delayed_work_sync(&adapter->client_task); if (adapter->netdev_registered) { unregister_netdev(netdev); adapter->netdev_registered = false; } + if (CLIENT_ALLOWED(adapter)) { + err = i40evf_lan_del_device(adapter); + if (err) + dev_warn(&pdev->dev, "Failed to delete client device: %d\n", + err); + } /* Shut down all the garbage mashers on the detention level */ adapter->state = __I40EVF_REMOVE; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index bee58af390e1..a2a7354426a3 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -26,6 +26,7 @@ #include "i40evf.h" #include "i40e_prototype.h" +#include "i40evf_client.h" /* busy wait delay in msec */ #define I40EVF_BUSY_WAIT_DELAY 10 @@ -999,6 +1000,16 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, if (v_opcode != adapter->current_op) return; break; + case I40E_VIRTCHNL_OP_IWARP: + /* Gobble zero-length replies from the PF. They indicate that + * a previous message was received OK, and the client doesn't + * care about that. + */ + if (msglen && CLIENT_ENABLED(adapter)) + i40evf_notify_client_message(&adapter->vsi, + msg, msglen); + break; + case I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP: adapter->client_pending &= ~(BIT(I40E_VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP)); @@ -1014,7 +1025,7 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, } break; default: - if (v_opcode != adapter->current_op) + if (adapter->current_op && (v_opcode != adapter->current_op)) dev_warn(&adapter->pdev->dev, "Expected response %d from PF, received %d\n", adapter->current_op, v_opcode); break; -- cgit From 0ef2d5afb12d379f4dd5df696219a01b88bb778a Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Tue, 24 Jan 2017 10:24:00 -0800 Subject: i40e: KISS the client interface (KISS is Keep It Simple, Stupid. Or is it?) The client interface vastly overengineered for what it needs to do. It was originally designed to support multiple clients on multiple netdevs, possibly even with multiple drivers. None of this happened, and now we know that there will only ever be one client for i40e (i40iw) and one for i40evf (i40iwvf). So, time for some KISS. Since i40e and i40evf are a Dynasty, we'll simplify this one to match the VF interface. First, be a Destroyer and remove all of the lists and locks required to support multiple clients. Keep one static around to keep track of one client, and track the client instances for each netdev in the driver's pf (or adapter) struct. Now it's Almost Human. Since we already know the client type is iWarp, get rid of any checks for this. Same for VSI type - it's always going to be the same type, so it's just a Parasite. While we're at it, fix up some comments. This makes the function headers actually match the functions. These changes reduce code complexity, simplify maintenance, squash some lurking timing bugs, and allow us to Rock and Roll All Nite. Change-ID: I1ea79948ad73b8685272451440a34507f9a9012e Signed-off-by: Mitch Williams Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 8 +- drivers/net/ethernet/intel/i40e/i40e_client.c | 457 +++++++-------------- drivers/net/ethernet/intel/i40e/i40e_client.h | 8 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 32 +- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 2 +- 5 files changed, 179 insertions(+), 328 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 82d8040fa418..9b2bb8d971cc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -348,8 +348,10 @@ struct i40e_pf { #define I40E_FLAG_TRUE_PROMISC_SUPPORT BIT_ULL(51) #define I40E_FLAG_HAVE_CRT_RETIMER BIT_ULL(52) #define I40E_FLAG_PTP_L4_CAPABLE BIT_ULL(53) -#define I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE BIT_ULL(54) +#define I40E_FLAG_CLIENT_RESET BIT_ULL(54) #define I40E_FLAG_TEMP_LINK_POLLING BIT_ULL(55) +#define I40E_FLAG_CLIENT_L2_CHANGE BIT_ULL(56) +#define I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE BIT_ULL(57) /* tracks features that get auto disabled by errors */ u64 auto_disable_flags; @@ -358,6 +360,7 @@ struct i40e_pf { struct i40e_fcoe fcoe; #endif /* I40E_FCOE */ + struct i40e_client_instance *cinst; bool stat_offsets_loaded; struct i40e_hw_port_stats stats; struct i40e_hw_port_stats stats_offsets; @@ -813,8 +816,7 @@ void i40e_notify_client_of_l2_param_changes(struct i40e_vsi *vsi); void i40e_notify_client_of_netdev_close(struct i40e_vsi *vsi, bool reset); void i40e_notify_client_of_vf_enable(struct i40e_pf *pf, u32 num_vfs); void i40e_notify_client_of_vf_reset(struct i40e_pf *pf, u32 vf_id); -int i40e_vf_client_capable(struct i40e_pf *pf, u32 vf_id, - enum i40e_client_type type); +int i40e_vf_client_capable(struct i40e_pf *pf, u32 vf_id); /** * i40e_irq_dynamic_enable - Enable default interrupt generation settings * @vsi: pointer to a vsi diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.c b/drivers/net/ethernet/intel/i40e/i40e_client.c index d570219efd9f..a9f0d22a7cf4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.c +++ b/drivers/net/ethernet/intel/i40e/i40e_client.c @@ -32,16 +32,10 @@ #include "i40e_client.h" static const char i40e_client_interface_version_str[] = I40E_CLIENT_VERSION_STR; - +static struct i40e_client *registered_client; static LIST_HEAD(i40e_devices); static DEFINE_MUTEX(i40e_device_mutex); -static LIST_HEAD(i40e_clients); -static DEFINE_MUTEX(i40e_client_mutex); - -static LIST_HEAD(i40e_client_instances); -static DEFINE_MUTEX(i40e_client_instance_mutex); - static int i40e_client_virtchnl_send(struct i40e_info *ldev, struct i40e_client *client, u32 vf_id, u8 *msg, u16 len); @@ -66,28 +60,6 @@ static struct i40e_ops i40e_lan_ops = { .update_vsi_ctxt = i40e_client_update_vsi_ctxt, }; -/** - * i40e_client_type_to_vsi_type - convert client type to vsi type - * @client_type: the i40e_client type - * - * returns the related vsi type value - **/ -static -enum i40e_vsi_type i40e_client_type_to_vsi_type(enum i40e_client_type type) -{ - switch (type) { - case I40E_CLIENT_IWARP: - return I40E_VSI_IWARP; - - case I40E_CLIENT_VMDQ2: - return I40E_VSI_VMDQ2; - - default: - pr_err("i40e: Client type unknown\n"); - return I40E_VSI_TYPE_UNKNOWN; - } -} - /** * i40e_client_get_params - Get the params that can change at runtime * @vsi: the VSI with the message @@ -134,31 +106,22 @@ int i40e_client_get_params(struct i40e_vsi *vsi, struct i40e_params *params) void i40e_notify_client_of_vf_msg(struct i40e_vsi *vsi, u32 vf_id, u8 *msg, u16 len) { - struct i40e_client_instance *cdev; + struct i40e_pf *pf = vsi->back; + struct i40e_client_instance *cdev = pf->cinst; - if (!vsi) + if (!cdev || !cdev->client) + return; + if (!cdev->client->ops || !cdev->client->ops->virtchnl_receive) { + dev_dbg(&pf->pdev->dev, + "Cannot locate client instance virtual channel receive routine\n"); + return; + } + if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { + dev_dbg(&pf->pdev->dev, "Client is not open, abort virtchnl_receive\n"); return; - mutex_lock(&i40e_client_instance_mutex); - list_for_each_entry(cdev, &i40e_client_instances, list) { - if (cdev->lan_info.pf == vsi->back) { - if (!cdev->client || - !cdev->client->ops || - !cdev->client->ops->virtchnl_receive) { - dev_dbg(&vsi->back->pdev->dev, - "Cannot locate client instance virtual channel receive routine\n"); - continue; - } - if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, - &cdev->state)) { - dev_dbg(&vsi->back->pdev->dev, "Client is not open, abort virtchnl_receive\n"); - continue; - } - cdev->client->ops->virtchnl_receive(&cdev->lan_info, - cdev->client, - vf_id, msg, len); - } } - mutex_unlock(&i40e_client_instance_mutex); + cdev->client->ops->virtchnl_receive(&cdev->lan_info, cdev->client, + vf_id, msg, len); } /** @@ -169,39 +132,28 @@ i40e_notify_client_of_vf_msg(struct i40e_vsi *vsi, u32 vf_id, u8 *msg, u16 len) **/ void i40e_notify_client_of_l2_param_changes(struct i40e_vsi *vsi) { - struct i40e_client_instance *cdev; + struct i40e_pf *pf = vsi->back; + struct i40e_client_instance *cdev = pf->cinst; struct i40e_params params; - if (!vsi) + if (!cdev || !cdev->client) + return; + if (!cdev->client->ops || !cdev->client->ops->l2_param_change) { + dev_dbg(&vsi->back->pdev->dev, + "Cannot locate client instance l2_param_change routine\n"); + return; + } + if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { + dev_dbg(&vsi->back->pdev->dev, "Client is not open, abort l2 param change\n"); return; - mutex_lock(&i40e_client_instance_mutex); - list_for_each_entry(cdev, &i40e_client_instances, list) { - if (cdev->lan_info.pf == vsi->back) { - if (!cdev->client || - !cdev->client->ops || - !cdev->client->ops->l2_param_change) { - dev_dbg(&vsi->back->pdev->dev, - "Cannot locate client instance l2_param_change routine\n"); - continue; - } - memset(¶ms, 0, sizeof(params)); - i40e_client_get_params(vsi, ¶ms); - if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, - &cdev->state)) { - dev_dbg(&vsi->back->pdev->dev, "Client is not open, abort l2 param change\n"); - continue; - } - cdev->lan_info.params = params; - cdev->client->ops->l2_param_change(&cdev->lan_info, - cdev->client, - ¶ms); - } } - mutex_unlock(&i40e_client_instance_mutex); + memcpy(&cdev->lan_info.params, ¶ms, sizeof(struct i40e_params)); + cdev->client->ops->l2_param_change(&cdev->lan_info, cdev->client, + ¶ms); } /** - * i40e_client_release_qvlist + * i40e_client_release_qvlist - release MSI-X vector mapping for client * @ldev: pointer to L2 context. * **/ @@ -237,26 +189,19 @@ static void i40e_client_release_qvlist(struct i40e_info *ldev) **/ void i40e_notify_client_of_netdev_close(struct i40e_vsi *vsi, bool reset) { - struct i40e_client_instance *cdev; + struct i40e_pf *pf = vsi->back; + struct i40e_client_instance *cdev = pf->cinst; - if (!vsi) + if (!cdev || !cdev->client) + return; + if (!cdev->client->ops || !cdev->client->ops->close) { + dev_dbg(&vsi->back->pdev->dev, + "Cannot locate client instance close routine\n"); return; - mutex_lock(&i40e_client_instance_mutex); - list_for_each_entry(cdev, &i40e_client_instances, list) { - if (cdev->lan_info.netdev == vsi->netdev) { - if (!cdev->client || - !cdev->client->ops || !cdev->client->ops->close) { - dev_dbg(&vsi->back->pdev->dev, - "Cannot locate client instance close routine\n"); - continue; - } - cdev->client->ops->close(&cdev->lan_info, cdev->client, - reset); - clear_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); - i40e_client_release_qvlist(&cdev->lan_info); - } } - mutex_unlock(&i40e_client_instance_mutex); + cdev->client->ops->close(&cdev->lan_info, cdev->client, reset); + clear_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); + i40e_client_release_qvlist(&cdev->lan_info); } /** @@ -268,30 +213,20 @@ void i40e_notify_client_of_netdev_close(struct i40e_vsi *vsi, bool reset) **/ void i40e_notify_client_of_vf_reset(struct i40e_pf *pf, u32 vf_id) { - struct i40e_client_instance *cdev; + struct i40e_client_instance *cdev = pf->cinst; - if (!pf) + if (!cdev || !cdev->client) + return; + if (!cdev->client->ops || !cdev->client->ops->vf_reset) { + dev_dbg(&pf->pdev->dev, + "Cannot locate client instance VF reset routine\n"); + return; + } + if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { + dev_dbg(&pf->pdev->dev, "Client is not open, abort vf-reset\n"); return; - mutex_lock(&i40e_client_instance_mutex); - list_for_each_entry(cdev, &i40e_client_instances, list) { - if (cdev->lan_info.pf == pf) { - if (!cdev->client || - !cdev->client->ops || - !cdev->client->ops->vf_reset) { - dev_dbg(&pf->pdev->dev, - "Cannot locate client instance VF reset routine\n"); - continue; - } - if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, - &cdev->state)) { - dev_dbg(&pf->pdev->dev, "Client is not open, abort vf-reset\n"); - continue; - } - cdev->client->ops->vf_reset(&cdev->lan_info, - cdev->client, vf_id); - } } - mutex_unlock(&i40e_client_instance_mutex); + cdev->client->ops->vf_reset(&cdev->lan_info, cdev->client, vf_id); } /** @@ -303,30 +238,21 @@ void i40e_notify_client_of_vf_reset(struct i40e_pf *pf, u32 vf_id) **/ void i40e_notify_client_of_vf_enable(struct i40e_pf *pf, u32 num_vfs) { - struct i40e_client_instance *cdev; + struct i40e_client_instance *cdev = pf->cinst; - if (!pf) + if (!cdev || !cdev->client) + return; + if (!cdev->client->ops || !cdev->client->ops->vf_enable) { + dev_dbg(&pf->pdev->dev, + "Cannot locate client instance VF enable routine\n"); + return; + } + if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, + &cdev->state)) { + dev_dbg(&pf->pdev->dev, "Client is not open, abort vf-enable\n"); return; - mutex_lock(&i40e_client_instance_mutex); - list_for_each_entry(cdev, &i40e_client_instances, list) { - if (cdev->lan_info.pf == pf) { - if (!cdev->client || - !cdev->client->ops || - !cdev->client->ops->vf_enable) { - dev_dbg(&pf->pdev->dev, - "Cannot locate client instance VF enable routine\n"); - continue; - } - if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, - &cdev->state)) { - dev_dbg(&pf->pdev->dev, "Client is not open, abort vf-enable\n"); - continue; - } - cdev->client->ops->vf_enable(&cdev->lan_info, - cdev->client, num_vfs); - } } - mutex_unlock(&i40e_client_instance_mutex); + cdev->client->ops->vf_enable(&cdev->lan_info, cdev->client, num_vfs); } /** @@ -337,37 +263,25 @@ void i40e_notify_client_of_vf_enable(struct i40e_pf *pf, u32 num_vfs) * If there is a client of the specified type attached to this PF, call * its vf_capable routine **/ -int i40e_vf_client_capable(struct i40e_pf *pf, u32 vf_id, - enum i40e_client_type type) +int i40e_vf_client_capable(struct i40e_pf *pf, u32 vf_id) { - struct i40e_client_instance *cdev; + struct i40e_client_instance *cdev = pf->cinst; int capable = false; - if (!pf) - return false; - mutex_lock(&i40e_client_instance_mutex); - list_for_each_entry(cdev, &i40e_client_instances, list) { - if (cdev->lan_info.pf == pf) { - if (!cdev->client || - !cdev->client->ops || - !cdev->client->ops->vf_capable || - !(cdev->client->type == type)) { - dev_dbg(&pf->pdev->dev, - "Cannot locate client instance VF capability routine\n"); - continue; - } - if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, - &cdev->state)) { - dev_dbg(&pf->pdev->dev, "Client is not open, abort vf-capable\n"); - continue; - } - capable = cdev->client->ops->vf_capable(&cdev->lan_info, - cdev->client, - vf_id); - break; - } + if (!cdev || !cdev->client) + goto out; + if (!cdev->client->ops || !cdev->client->ops->vf_capable) { + dev_info(&pf->pdev->dev, + "Cannot locate client instance VF capability routine\n"); + goto out; } - mutex_unlock(&i40e_client_instance_mutex); + if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) + goto out; + + capable = cdev->client->ops->vf_capable(&cdev->lan_info, + cdev->client, + vf_id); +out: return capable; } @@ -377,27 +291,19 @@ int i40e_vf_client_capable(struct i40e_pf *pf, u32 vf_id, * @client: pointer to a client struct in the client list. * @existing: if there was already an existing instance * - * Returns cdev ptr on success or if already exists, NULL on failure **/ -static -struct i40e_client_instance *i40e_client_add_instance(struct i40e_pf *pf, - struct i40e_client *client, - bool *existing) +static void i40e_client_add_instance(struct i40e_pf *pf) { - struct i40e_client_instance *cdev; + struct i40e_client_instance *cdev = NULL; struct netdev_hw_addr *mac = NULL; struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; - mutex_lock(&i40e_client_instance_mutex); - list_for_each_entry(cdev, &i40e_client_instances, list) { - if ((cdev->lan_info.pf == pf) && (cdev->client == client)) { - *existing = true; - goto out; - } - } + if (!registered_client || pf->cinst) + return; + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); if (!cdev) - goto out; + return; cdev->lan_info.pf = (void *)pf; cdev->lan_info.netdev = vsi->netdev; @@ -417,7 +323,7 @@ struct i40e_client_instance *i40e_client_add_instance(struct i40e_pf *pf, if (i40e_client_get_params(vsi, &cdev->lan_info.params)) { kfree(cdev); cdev = NULL; - goto out; + return; } cdev->lan_info.msix_count = pf->num_iwarp_msix; @@ -430,41 +336,20 @@ struct i40e_client_instance *i40e_client_add_instance(struct i40e_pf *pf, else dev_err(&pf->pdev->dev, "MAC address list is empty!\n"); - cdev->client = client; - INIT_LIST_HEAD(&cdev->list); - list_add(&cdev->list, &i40e_client_instances); -out: - mutex_unlock(&i40e_client_instance_mutex); - return cdev; + cdev->client = registered_client; + pf->cinst = cdev; } /** * i40e_client_del_instance - removes a client instance from the list * @pf: pointer to the board struct * - * Returns 0 on success or non-0 on error **/ static -int i40e_client_del_instance(struct i40e_pf *pf, struct i40e_client *client) +void i40e_client_del_instance(struct i40e_pf *pf) { - struct i40e_client_instance *cdev, *tmp; - int ret = -ENODEV; - - mutex_lock(&i40e_client_instance_mutex); - list_for_each_entry_safe(cdev, tmp, &i40e_client_instances, list) { - if ((cdev->lan_info.pf != pf) || (cdev->client != client)) - continue; - - dev_info(&pf->pdev->dev, "Deleted instance of Client %s, of dev %d bus=0x%02x func=0x%02x)\n", - client->name, pf->hw.pf_id, - pf->hw.bus.device, pf->hw.bus.func); - list_del(&cdev->list); - kfree(cdev); - ret = 0; - break; - } - mutex_unlock(&i40e_client_instance_mutex); - return ret; + kfree(pf->cinst); + pf->cinst = NULL; } /** @@ -473,67 +358,50 @@ int i40e_client_del_instance(struct i40e_pf *pf, struct i40e_client *client) **/ void i40e_client_subtask(struct i40e_pf *pf) { + struct i40e_client *client = registered_client; struct i40e_client_instance *cdev; - struct i40e_client *client; - bool existing = false; + struct i40e_vsi *vsi = pf->vsi[pf->lan_vsi]; int ret = 0; if (!(pf->flags & I40E_FLAG_SERVICE_CLIENT_REQUESTED)) return; pf->flags &= ~I40E_FLAG_SERVICE_CLIENT_REQUESTED; + cdev = pf->cinst; /* If we're down or resetting, just bail */ if (test_bit(__I40E_DOWN, &pf->state) || test_bit(__I40E_CONFIG_BUSY, &pf->state)) return; - /* Check client state and instantiate client if client registered */ - mutex_lock(&i40e_client_mutex); - list_for_each_entry(client, &i40e_clients, list) { - /* first check client is registered */ - if (!test_bit(__I40E_CLIENT_REGISTERED, &client->state)) - continue; - - /* Do we also need the LAN VSI to be up, to create instance */ - if (!(client->flags & I40E_CLIENT_FLAGS_LAUNCH_ON_PROBE)) { - /* check if L2 VSI is up, if not we are not ready */ - if (test_bit(__I40E_DOWN, &pf->vsi[pf->lan_vsi]->state)) - continue; - } else { - dev_warn(&pf->pdev->dev, "This client %s is being instantiated at probe\n", - client->name); - } - - /* Add the client instance to the instance list */ - cdev = i40e_client_add_instance(pf, client, &existing); - if (!cdev) - continue; - - if (!existing) { - dev_info(&pf->pdev->dev, "Added instance of Client %s to PF%d bus=0x%02x dev=0x%02x func=0x%02x\n", - client->name, pf->hw.pf_id, - pf->hw.bus.bus_id, pf->hw.bus.device, - pf->hw.bus.func); - } + if (!client || !cdev) + return; - mutex_lock(&i40e_client_instance_mutex); - if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, - &cdev->state)) { - /* Send an Open request to the client */ - if (client->ops && client->ops->open) - ret = client->ops->open(&cdev->lan_info, - client); - if (!ret) { - set_bit(__I40E_CLIENT_INSTANCE_OPENED, - &cdev->state); - } else { - /* remove client instance */ - i40e_client_del_instance(pf, client); + /* Here we handle client opens. If the client is down, but + * the netdev is up, then open the client. + */ + if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { + if (!test_bit(__I40E_DOWN, &vsi->state) && + client->ops && client->ops->open) { + set_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); + ret = client->ops->open(&cdev->lan_info, client); + if (ret) { + /* Remove failed client instance */ + clear_bit(__I40E_CLIENT_INSTANCE_OPENED, + &cdev->state); + i40e_client_del_instance(pf); } } - mutex_unlock(&i40e_client_instance_mutex); + } else { + /* Likewise for client close. If the client is up, but the netdev + * is down, then close the client. + */ + if (test_bit(__I40E_DOWN, &vsi->state) && + client->ops && client->ops->close) { + clear_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); + client->ops->close(&cdev->lan_info, client, false); + i40e_client_release_qvlist(&cdev->lan_info); + } } - mutex_unlock(&i40e_client_mutex); } /** @@ -601,7 +469,6 @@ int i40e_lan_del_device(struct i40e_pf *pf) break; } } - mutex_unlock(&i40e_device_mutex); return ret; } @@ -610,22 +477,24 @@ int i40e_lan_del_device(struct i40e_pf *pf) * i40e_client_release - release client specific resources * @client: pointer to the registered client * - * Return 0 on success or < 0 on error **/ -static int i40e_client_release(struct i40e_client *client) +static void i40e_client_release(struct i40e_client *client) { - struct i40e_client_instance *cdev, *tmp; + struct i40e_client_instance *cdev; + struct i40e_device *ldev; struct i40e_pf *pf; - int ret = 0; - LIST_HEAD(cdevs_tmp); - - mutex_lock(&i40e_client_instance_mutex); - list_for_each_entry_safe(cdev, tmp, &i40e_client_instances, list) { - if (strncmp(cdev->client->name, client->name, - I40E_CLIENT_STR_LENGTH)) + mutex_lock(&i40e_device_mutex); + list_for_each_entry(ldev, &i40e_devices, list) { + pf = ldev->pf; + cdev = pf->cinst; + if (!cdev) continue; - pf = (struct i40e_pf *)cdev->lan_info.pf; + + while (test_and_set_bit(__I40E_SERVICE_SCHED, + &pf->state)) + usleep_range(500, 1000); + if (test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { if (client->ops && client->ops->close) client->ops->close(&cdev->lan_info, client, @@ -637,18 +506,13 @@ static int i40e_client_release(struct i40e_client *client) "Client %s instance for PF id %d closed\n", client->name, pf->hw.pf_id); } - /* delete the client instance from the list */ - list_move(&cdev->list, &cdevs_tmp); + /* delete the client instance */ + i40e_client_del_instance(pf); dev_info(&pf->pdev->dev, "Deleted client instance of Client %s\n", client->name); + clear_bit(__I40E_SERVICE_SCHED, &pf->state); } - mutex_unlock(&i40e_client_instance_mutex); - - /* free the client device and release its vsi */ - list_for_each_entry_safe(cdev, tmp, &cdevs_tmp, list) { - kfree(cdev); - } - return ret; + mutex_unlock(&i40e_device_mutex); } /** @@ -664,6 +528,7 @@ static void i40e_client_prepare(struct i40e_client *client) mutex_lock(&i40e_device_mutex); list_for_each_entry(ldev, &i40e_devices, list) { pf = ldev->pf; + i40e_client_add_instance(pf); /* Start the client subtask */ pf->flags |= I40E_FLAG_SERVICE_CLIENT_REQUESTED; i40e_service_event_schedule(pf); @@ -792,8 +657,8 @@ static void i40e_client_request_reset(struct i40e_info *ldev, break; default: dev_warn(&pf->pdev->dev, - "Client %s instance for PF id %d request an unsupported reset: %d.\n", - client->name, pf->hw.pf_id, reset_level); + "Client for PF id %d requested an unsupported reset: %d.\n", + pf->hw.pf_id, reset_level); break; } @@ -852,8 +717,8 @@ static int i40e_client_update_vsi_ctxt(struct i40e_info *ldev, } else { update = false; dev_warn(&pf->pdev->dev, - "Client %s instance for PF id %d request an unsupported Config: %x.\n", - client->name, pf->hw.pf_id, flag); + "Client for PF id %d request an unsupported Config: %x.\n", + pf->hw.pf_id, flag); } if (update) { @@ -878,7 +743,6 @@ static int i40e_client_update_vsi_ctxt(struct i40e_info *ldev, int i40e_register_client(struct i40e_client *client) { int ret = 0; - enum i40e_vsi_type vsi_type; if (!client) { ret = -EIO; @@ -891,11 +755,9 @@ int i40e_register_client(struct i40e_client *client) goto out; } - mutex_lock(&i40e_client_mutex); - if (i40e_client_is_registered(client)) { + if (registered_client) { pr_info("i40e: Client %s has already been registered!\n", client->name); - mutex_unlock(&i40e_client_mutex); ret = -EEXIST; goto out; } @@ -908,22 +770,11 @@ int i40e_register_client(struct i40e_client *client) client->version.major, client->version.minor, client->version.build, i40e_client_interface_version_str); - mutex_unlock(&i40e_client_mutex); ret = -EIO; goto out; } - vsi_type = i40e_client_type_to_vsi_type(client->type); - if (vsi_type == I40E_VSI_TYPE_UNKNOWN) { - pr_info("i40e: Failed to register client %s due to unknown client type %d\n", - client->name, client->type); - mutex_unlock(&i40e_client_mutex); - ret = -EIO; - goto out; - } - list_add(&client->list, &i40e_clients); - set_bit(__I40E_CLIENT_REGISTERED, &client->state); - mutex_unlock(&i40e_client_mutex); + registered_client = client; i40e_client_prepare(client); @@ -943,29 +794,21 @@ int i40e_unregister_client(struct i40e_client *client) { int ret = 0; - /* When a unregister request comes through we would have to send - * a close for each of the client instances that were opened. - * client_release function is called to handle this. - */ - mutex_lock(&i40e_client_mutex); - if (!client || i40e_client_release(client)) { - ret = -EIO; - goto out; - } - - /* TODO: check if device is in reset, or if that matters? */ - if (!i40e_client_is_registered(client)) { + if (registered_client != client) { pr_info("i40e: Client %s has not been registered\n", client->name); ret = -ENODEV; goto out; } - clear_bit(__I40E_CLIENT_REGISTERED, &client->state); - list_del(&client->list); - pr_info("i40e: Unregistered client %s with return code %d\n", - client->name, ret); + registered_client = NULL; + /* When a unregister request comes through we would have to send + * a close for each of the client instances that were opened. + * client_release function is called to handle this. + */ + i40e_client_release(client); + + pr_info("i40e: Unregistered client %s\n", client->name); out: - mutex_unlock(&i40e_client_mutex); return ret; } EXPORT_SYMBOL(i40e_unregister_client); diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.h b/drivers/net/ethernet/intel/i40e/i40e_client.h index 528bd79b05fe..15b21a5315b5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.h +++ b/drivers/net/ethernet/intel/i40e/i40e_client.h @@ -57,11 +57,6 @@ enum i40e_client_instance_state { __I40E_CLIENT_INSTANCE_OPENED, }; -enum i40e_client_type { - I40E_CLIENT_IWARP, - I40E_CLIENT_VMDQ2 -}; - struct i40e_ops; struct i40e_client; @@ -214,7 +209,8 @@ struct i40e_client { u32 flags; #define I40E_CLIENT_FLAGS_LAUNCH_ON_PROBE BIT(0) #define I40E_TX_FLAGS_NOTIFY_OTHER_EVENTS BIT(2) - enum i40e_client_type type; + u8 type; +#define I40E_CLIENT_IWARP 0 const struct i40e_client_ops *ops; /* client ops provided by the client */ }; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index e8a8351c8ea9..3d7f179af6be 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2487,13 +2487,15 @@ static int i40e_change_mtu(struct net_device *netdev, int new_mtu) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; netdev_info(netdev, "changing MTU from %d to %d\n", netdev->mtu, new_mtu); netdev->mtu = new_mtu; if (netif_running(netdev)) i40e_vsi_reinit_locked(vsi); - i40e_notify_client_of_l2_param_changes(vsi); + pf->flags |= (I40E_FLAG_SERVICE_CLIENT_REQUESTED | + I40E_FLAG_CLIENT_L2_CHANGE); return 0; } @@ -4463,17 +4465,16 @@ static void i40e_napi_disable_all(struct i40e_vsi *vsi) **/ static void i40e_vsi_close(struct i40e_vsi *vsi) { - bool reset = false; - + struct i40e_pf *pf = vsi->back; if (!test_and_set_bit(__I40E_DOWN, &vsi->state)) i40e_down(vsi); i40e_vsi_free_irq(vsi); i40e_vsi_free_tx_resources(vsi); i40e_vsi_free_rx_resources(vsi); vsi->current_netdev_flags = 0; - if (test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state)) - reset = true; - i40e_notify_client_of_netdev_close(vsi, reset); + pf->flags |= I40E_FLAG_SERVICE_CLIENT_REQUESTED; + if (test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state)) + pf->flags |= I40E_FLAG_CLIENT_RESET; } /** @@ -5542,8 +5543,6 @@ void i40e_down(struct i40e_vsi *vsi) i40e_clean_rx_ring(vsi->rx_rings[i]); } - i40e_notify_client_of_netdev_close(vsi, false); - } /** @@ -6021,8 +6020,8 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf, i40e_service_event_schedule(pf); } else { i40e_pf_unquiesce_all_vsi(pf); - /* Notify the client for the DCB changes */ - i40e_notify_client_of_l2_param_changes(pf->vsi[pf->lan_vsi]); + pf->flags |= (I40E_FLAG_SERVICE_CLIENT_REQUESTED | + I40E_FLAG_CLIENT_L2_CHANGE); } exit: @@ -7411,7 +7410,18 @@ static void i40e_service_task(struct work_struct *work) i40e_vc_process_vflr_event(pf); i40e_watchdog_subtask(pf); i40e_fdir_reinit_subtask(pf); - i40e_client_subtask(pf); + if (pf->flags & I40E_FLAG_CLIENT_RESET) { + /* Client subtask will reopen next time through. */ + i40e_notify_client_of_netdev_close(pf->vsi[pf->lan_vsi], true); + pf->flags &= ~I40E_FLAG_CLIENT_RESET; + } else { + i40e_client_subtask(pf); + if (pf->flags & I40E_FLAG_CLIENT_L2_CHANGE) { + i40e_notify_client_of_l2_param_changes( + pf->vsi[pf->lan_vsi]); + pf->flags &= ~I40E_FLAG_CLIENT_L2_CHANGE; + } + } i40e_sync_filters_subtask(pf); i40e_sync_udp_filters_subtask(pf); i40e_clean_adminq_subtask(pf); diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 78460c52b7c4..25ee5af2d136 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1359,7 +1359,7 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) if (!vsi->info.pvid) vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_VLAN; - if (i40e_vf_client_capable(pf, vf->vf_id, I40E_CLIENT_IWARP) && + if (i40e_vf_client_capable(pf, vf->vf_id) && (vf->driver_caps & I40E_VIRTCHNL_VF_OFFLOAD_IWARP)) { vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_IWARP; set_bit(I40E_VF_STAT_IWARPENA, &vf->vf_states); -- cgit From c0cf70a6fc373570f86c6dc4799d775363b9946e Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 24 Jan 2017 10:24:01 -0800 Subject: i40e: don't add more vectors to num_lan_msix than number of CPUs This is a solution to avoid adding too many queues to num_lan_msix. A recent refactor of queue pairs accidentally added all remaining vectors to the num_lan_msix which can have adverse performance issues, due to enabling more queues than the number of CPU cores. This patch removes the old calculation, and replaces it with a simple algorithm. 1) add queue pairs up to num_online_cpus(), but capped at half of total vectors 2) then add alternative features such as flow directory and similar 3) finally, add the remaining vectors back to queue pairs, but capped such that the total number of queue pairs does not exceed num_online_cpus(). Change-ID: I668abf67d5011a1248866daba8885f4ff00cb8d9 Signed-off-by: Jacob Keller Signed-off-by: Harshitha Ramamurthy Signed-off-by: Carolyn Wyborny Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 30 ++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 3d7f179af6be..cb678ed7a2ad 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7819,6 +7819,7 @@ static int i40e_reserve_msix_vectors(struct i40e_pf *pf, int vectors) static int i40e_init_msix(struct i40e_pf *pf) { struct i40e_hw *hw = &pf->hw; + int cpus, extra_vectors; int vectors_left; int v_budget, i; int v_actual; @@ -7854,10 +7855,16 @@ static int i40e_init_msix(struct i40e_pf *pf) vectors_left--; } - /* reserve vectors for the main PF traffic queues */ - pf->num_lan_msix = min_t(int, num_online_cpus(), vectors_left); + /* reserve some vectors for the main PF traffic queues. Initially we + * only reserve at most 50% of the available vectors, in the case that + * the number of online CPUs is large. This ensures that we can enable + * extra features as well. Once we've enabled the other features, we + * will use any remaining vectors to reach as close as we can to the + * number of online CPUs. + */ + cpus = num_online_cpus(); + pf->num_lan_msix = min_t(int, cpus, vectors_left / 2); vectors_left -= pf->num_lan_msix; - v_budget += pf->num_lan_msix; /* reserve one vector for sideband flow director */ if (pf->flags & I40E_FLAG_FD_SB_ENABLED) { @@ -7920,6 +7927,23 @@ static int i40e_init_msix(struct i40e_pf *pf) } } + /* On systems with a large number of SMP cores, we previously limited + * the number of vectors for num_lan_msix to be at most 50% of the + * available vectors, to allow for other features. Now, we add back + * the remaining vectors. However, we ensure that the total + * num_lan_msix will not exceed num_online_cpus(). To do this, we + * calculate the number of vectors we can add without going over the + * cap of CPUs. For systems with a small number of CPUs this will be + * zero. + */ + extra_vectors = min_t(int, cpus - pf->num_lan_msix, vectors_left); + pf->num_lan_msix += extra_vectors; + vectors_left -= extra_vectors; + + WARN(vectors_left < 0, + "Calculation of remaining vectors underflowed. This is an accounting bug when determining total MSI-X vectors.\n"); + + v_budget += pf->num_lan_msix; pf->msix_entries = kcalloc(v_budget, sizeof(struct msix_entry), GFP_KERNEL); if (!pf->msix_entries) -- cgit From c271dd6c391b535226cf1a81aaad9f33cb5899d3 Mon Sep 17 00:00:00 2001 From: Lihong Yang Date: Mon, 30 Jan 2017 12:29:32 -0800 Subject: i40e: fix ethtool to get EEPROM data from X722 interface Currently ethtool -e will error out with a X722 interface as its EEPROM has a scope limit at offset 0x5B9FFF. This patch fixes the issue by setting the EEPROM length to the scope limit to avoid NVM read failure beyond that. Change-ID: I0b7d4dd6c7f2a57cace438af5dffa0f44c229372 Signed-off-by: Lihong Yang Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index a22e26200bcc..3aefc9e20439 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1165,6 +1165,11 @@ static int i40e_get_eeprom_len(struct net_device *netdev) struct i40e_hw *hw = &np->vsi->back->hw; u32 val; +#define X722_EEPROM_SCOPE_LIMIT 0x5B9FFF + if (hw->mac.type == I40E_MAC_X722) { + val = X722_EEPROM_SCOPE_LIMIT + 1; + return val; + } val = (rd32(hw, I40E_GLPCI_LBARCTRL) & I40E_GLPCI_LBARCTRL_FL_SIZE_MASK) >> I40E_GLPCI_LBARCTRL_FL_SIZE_SHIFT; -- cgit From 26f77e53cfee2b1b066071353be03988897e23c3 Mon Sep 17 00:00:00 2001 From: Lihong Yang Date: Mon, 30 Jan 2017 12:29:33 -0800 Subject: i40e: fix RSS queues only operating on PF0 This patch fixes the issue that RSS offloading only works on PF0 by using the direct register writing of the hash keys for the VFs instead of using the admin queue command to do so. Change-ID: Ia02cda7dbaa23def342e8786097a2c03db6f580b Signed-off-by: Lihong Yang Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 11 +++-------- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 6 ++---- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index cb678ed7a2ad..e577ff8a9c76 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -8394,13 +8394,10 @@ static int i40e_config_rss_reg(struct i40e_vsi *vsi, const u8 *seed, if (vsi->type == I40E_VSI_MAIN) { for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++) - i40e_write_rx_ctl(hw, I40E_PFQF_HKEY(i), - seed_dw[i]); + wr32(hw, I40E_PFQF_HKEY(i), seed_dw[i]); } else if (vsi->type == I40E_VSI_SRIOV) { for (i = 0; i <= I40E_VFQF_HKEY1_MAX_INDEX; i++) - i40e_write_rx_ctl(hw, - I40E_VFQF_HKEY1(i, vf_id), - seed_dw[i]); + wr32(hw, I40E_VFQF_HKEY1(i, vf_id), seed_dw[i]); } else { dev_err(&pf->pdev->dev, "Cannot set RSS seed - invalid VSI type\n"); } @@ -8418,9 +8415,7 @@ static int i40e_config_rss_reg(struct i40e_vsi *vsi, const u8 *seed, if (lut_size != I40E_VF_HLUT_ARRAY_SIZE) return -EINVAL; for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) - i40e_write_rx_ctl(hw, - I40E_VFQF_HLUT1(i, vf_id), - lut_dw[i]); + wr32(hw, I40E_VFQF_HLUT1(i, vf_id), lut_dw[i]); } else { dev_err(&pf->pdev->dev, "Cannot set RSS LUT - invalid VSI type\n"); } diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 25ee5af2d136..115a7286ab8f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -702,10 +702,8 @@ static int i40e_alloc_vsi_res(struct i40e_vf *vf, enum i40e_vsi_type type) dev_info(&pf->pdev->dev, "Could not allocate VF broadcast filter\n"); spin_unlock_bh(&vsi->mac_filter_hash_lock); - i40e_write_rx_ctl(&pf->hw, I40E_VFQF_HENA1(0, vf->vf_id), - (u32)hena); - i40e_write_rx_ctl(&pf->hw, I40E_VFQF_HENA1(1, vf->vf_id), - (u32)(hena >> 32)); + wr32(&pf->hw, I40E_VFQF_HENA1(0, vf->vf_id), (u32)hena); + wr32(&pf->hw, I40E_VFQF_HENA1(1, vf->vf_id), (u32)(hena >> 32)); } /* program mac filter */ -- cgit From 3954b39102677c879d7663621fb9a7e0da349274 Mon Sep 17 00:00:00 2001 From: Filip Sadowski Date: Mon, 30 Jan 2017 12:29:34 -0800 Subject: i40e: Clarify steps in MAC/VLAN filters initialization routine This patch clarifies the reason for removal of automatically firmware-generated filter and explicit addition of filter which accepts frames with any VLAN id. Change-ID: Iabf180b6d61c4d8a36d3bcf8457c377a6f2aca0e Signed-off-by: Filip Sadowski Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index e577ff8a9c76..414685c683d7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -9461,10 +9461,10 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) if (vsi->type == I40E_VSI_MAIN) { SET_NETDEV_DEV(netdev, &pf->pdev->dev); ether_addr_copy(mac_addr, hw->mac.perm_addr); - /* The following steps are necessary to prevent reception - * of tagged packets - some older NVM configurations load a - * default a MAC-VLAN filter that accepts any tagged packet - * which must be replaced by a normal filter. + /* The following steps are necessary to properly keep track of + * MAC-VLAN filters loaded into firmware - first we remove + * filter that is automatically generated by firmware and then + * add new filter both to the driver hash table and firmware. */ i40e_rm_default_mac_filter(vsi, mac_addr); spin_lock_bh(&vsi->mac_filter_hash_lock); -- cgit From 59605bc09630c2b577858c371edf89c099b5f925 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 30 Jan 2017 12:29:35 -0800 Subject: i40e/i40evf: Add support for mapping pages with DMA attributes This patch adds support for DMA_ATTR_SKIP_CPU_SYNC and DMA_ATTR_WEAK_ORDERING. By enabling both of these for the Rx path we are able to see performance improvements on architectures that implement either one due to the fact that page mapping and unmapping only has to sync what is actually being used instead of the entire buffer. In addition by enabling the weak ordering attribute enables a performance improvement for architectures that can associate a memory ordering with a DMA buffer such as Sparc. Change-ID: If176824e8231c5b24b8a5d55b339a6026738fc75 Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 31 ++++++++++++++++++++++----- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 3 +++ drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 31 ++++++++++++++++++++++----- drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 3 +++ 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 97d46058d71d..86e4991a03a7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1010,7 +1010,6 @@ err: **/ void i40e_clean_rx_ring(struct i40e_ring *rx_ring) { - struct device *dev = rx_ring->dev; unsigned long bi_size; u16 i; @@ -1030,7 +1029,20 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring) if (!rx_bi->page) continue; - dma_unmap_page(dev, rx_bi->dma, PAGE_SIZE, DMA_FROM_DEVICE); + /* Invalidate cache lines that may have been written to by + * device so that we avoid corrupting memory. + */ + dma_sync_single_range_for_cpu(rx_ring->dev, + rx_bi->dma, + rx_bi->page_offset, + I40E_RXBUFFER_2048, + DMA_FROM_DEVICE); + + /* free resources associated with mapping */ + dma_unmap_page_attrs(rx_ring->dev, rx_bi->dma, + PAGE_SIZE, + DMA_FROM_DEVICE, + I40E_RX_DMA_ATTR); __free_pages(rx_bi->page, 0); rx_bi->page = NULL; @@ -1159,7 +1171,10 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, } /* map page for use */ - dma = dma_map_page(rx_ring->dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE); + dma = dma_map_page_attrs(rx_ring->dev, page, 0, + PAGE_SIZE, + DMA_FROM_DEVICE, + I40E_RX_DMA_ATTR); /* if mapping failed free memory back to system since * there isn't much point in holding memory we can't use @@ -1219,6 +1234,12 @@ bool i40e_alloc_rx_buffers(struct i40e_ring *rx_ring, u16 cleaned_count) if (!i40e_alloc_mapped_page(rx_ring, bi)) goto no_buffers; + /* sync the buffer for use by the device */ + dma_sync_single_range_for_device(rx_ring->dev, bi->dma, + bi->page_offset, + I40E_RXBUFFER_2048, + DMA_FROM_DEVICE); + /* Refresh the desc even if buffer_addrs didn't change * because each write-back erases this info. */ @@ -1685,8 +1706,8 @@ struct sk_buff *i40e_fetch_rx_buffer(struct i40e_ring *rx_ring, rx_ring->rx_stats.page_reuse_count++; } else { /* we are not reusing the buffer so unmap it */ - dma_unmap_page(rx_ring->dev, rx_buffer->dma, PAGE_SIZE, - DMA_FROM_DEVICE); + dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, PAGE_SIZE, + DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); } /* clear contents of buffer_info */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index f80979025c01..49c7b2089d8e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -133,6 +133,9 @@ enum i40e_dyn_idx_t { #define I40E_RX_HDR_SIZE I40E_RXBUFFER_256 #define i40e_rx_desc i40e_32byte_rx_desc +#define I40E_RX_DMA_ATTR \ + (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) + /** * i40e_test_staterr - tests bits in Rx descriptor status and error fields * @rx_desc: pointer to receive descriptor (in le64 format) diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index c91fcf43ccbc..d7790c08e523 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -493,7 +493,6 @@ err: **/ void i40evf_clean_rx_ring(struct i40e_ring *rx_ring) { - struct device *dev = rx_ring->dev; unsigned long bi_size; u16 i; @@ -513,7 +512,20 @@ void i40evf_clean_rx_ring(struct i40e_ring *rx_ring) if (!rx_bi->page) continue; - dma_unmap_page(dev, rx_bi->dma, PAGE_SIZE, DMA_FROM_DEVICE); + /* Invalidate cache lines that may have been written to by + * device so that we avoid corrupting memory. + */ + dma_sync_single_range_for_cpu(rx_ring->dev, + rx_bi->dma, + rx_bi->page_offset, + I40E_RXBUFFER_2048, + DMA_FROM_DEVICE); + + /* free resources associated with mapping */ + dma_unmap_page_attrs(rx_ring->dev, rx_bi->dma, + PAGE_SIZE, + DMA_FROM_DEVICE, + I40E_RX_DMA_ATTR); __free_pages(rx_bi->page, 0); rx_bi->page = NULL; @@ -642,7 +654,10 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, } /* map page for use */ - dma = dma_map_page(rx_ring->dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE); + dma = dma_map_page_attrs(rx_ring->dev, page, 0, + PAGE_SIZE, + DMA_FROM_DEVICE, + I40E_RX_DMA_ATTR); /* if mapping failed free memory back to system since * there isn't much point in holding memory we can't use @@ -702,6 +717,12 @@ bool i40evf_alloc_rx_buffers(struct i40e_ring *rx_ring, u16 cleaned_count) if (!i40e_alloc_mapped_page(rx_ring, bi)) goto no_buffers; + /* sync the buffer for use by the device */ + dma_sync_single_range_for_device(rx_ring->dev, bi->dma, + bi->page_offset, + I40E_RXBUFFER_2048, + DMA_FROM_DEVICE); + /* Refresh the desc even if buffer_addrs didn't change * because each write-back erases this info. */ @@ -1158,8 +1179,8 @@ struct sk_buff *i40evf_fetch_rx_buffer(struct i40e_ring *rx_ring, rx_ring->rx_stats.page_reuse_count++; } else { /* we are not reusing the buffer so unmap it */ - dma_unmap_page(rx_ring->dev, rx_buffer->dma, PAGE_SIZE, - DMA_FROM_DEVICE); + dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, PAGE_SIZE, + DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); } /* clear contents of buffer_info */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 8274ba68bd32..013512124e6a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -120,6 +120,9 @@ enum i40e_dyn_idx_t { #define I40E_RX_HDR_SIZE I40E_RXBUFFER_256 #define i40e_rx_desc i40e_32byte_rx_desc +#define I40E_RX_DMA_ATTR \ + (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) + /** * i40e_test_staterr - tests bits in Rx descriptor status and error fields * @rx_desc: pointer to receive descriptor (in le64 format) -- cgit From 4dbc56613962bfa597c345d8eff839870293c0a5 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Mon, 30 Jan 2017 12:29:36 -0800 Subject: i40e: Allow untrusted VFs to have more filters Our original filter limit of 8 was based on behavior that we saw from Linux VMs. Now we're running Other Operating Systems under KVM and we see that they commonly use more MAC filters. Since it seems weird to require people to enable trusted VFs just to boot their OS, bump the number of filters allowed by default. Change-ID: I76b2dcb2ad6017e39231ad3096c3fb6f065eef5e Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 115a7286ab8f..cfe8b78dac0e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1851,7 +1851,7 @@ error_param: } /* If the VF is not trusted restrict the number of MAC/VLAN it can program */ -#define I40E_VC_MAX_MAC_ADDR_PER_VF 8 +#define I40E_VC_MAX_MAC_ADDR_PER_VF 12 #define I40E_VC_MAX_VLAN_PER_VF 8 /** -- cgit From 15990832cd3e7e8904f8dacdabfa33adb9a836d6 Mon Sep 17 00:00:00 2001 From: Bimmy Pujari Date: Mon, 30 Jan 2017 12:29:37 -0800 Subject: i40e/i40evf: Change version from 1.6.27 to 2.1.7 Signed-off-by: Bimmy Pujari Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 6 +++--- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 414685c683d7..4d305fb1f188 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -39,9 +39,9 @@ static const char i40e_driver_string[] = #define DRV_KERN "-k" -#define DRV_VERSION_MAJOR 1 -#define DRV_VERSION_MINOR 6 -#define DRV_VERSION_BUILD 27 +#define DRV_VERSION_MAJOR 2 +#define DRV_VERSION_MINOR 1 +#define DRV_VERSION_BUILD 7 #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 9492b20da557..6d666bde9df5 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -37,9 +37,9 @@ static const char i40evf_driver_string[] = #define DRV_KERN "-k" -#define DRV_VERSION_MAJOR 1 -#define DRV_VERSION_MINOR 6 -#define DRV_VERSION_BUILD 27 +#define DRV_VERSION_MAJOR 2 +#define DRV_VERSION_MINOR 1 +#define DRV_VERSION_BUILD 7 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) \ -- cgit From b77ac975930741f0b6612b18e55d7a72c81ea2ac Mon Sep 17 00:00:00 2001 From: Harshitha Ramamurthy Date: Fri, 3 Feb 2017 10:57:42 -0800 Subject: i40e: rename auto_disable_flags to hw_disabled_flags A previous commit introduced a field that tracks the features that are disabled due to HW resource limitations as opposed to the featured disabled by the user. This patch changes the name of the field to make it more readable since it might get confusing when looking at code containing both the flags field and the auto_disable_features field together. Change-ID: Idcc9888659698f6fe3ccff17c8c3f09b5026f708 Signed-off-by: Harshitha Ramamurthy Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 8 ++++++-- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 10 ++++----- drivers/net/ethernet/intel/i40e/i40e_main.c | 28 +++++++++++++------------- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 20 +++++++++--------- 4 files changed, 35 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 9b2bb8d971cc..a5cf5d11d0e7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -353,8 +353,12 @@ struct i40e_pf { #define I40E_FLAG_CLIENT_L2_CHANGE BIT_ULL(56) #define I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE BIT_ULL(57) - /* tracks features that get auto disabled by errors */ - u64 auto_disable_flags; + /* Tracks features that are disabled due to hw limitations. + * If a bit is set here, it means that the corresponding + * bit in the 'flags' field is cleared i.e that feature + * is disabled + */ + u64 hw_disabled_flags; #ifdef I40E_FCOE struct i40e_fcoe fcoe; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 3aefc9e20439..a933c6c2aff8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2717,7 +2717,7 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED)) return -EOPNOTSUPP; - if (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED) + if (pf->hw_disabled_flags & I40E_FLAG_FD_SB_ENABLED) return -ENOSPC; if (test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state) || @@ -3059,7 +3059,7 @@ static u32 i40e_get_priv_flags(struct net_device *dev) I40E_PRIV_FLAGS_FD_ATR : 0; ret_flags |= pf->flags & I40E_FLAG_VEB_STATS_ENABLED ? I40E_PRIV_FLAGS_VEB_STATS : 0; - ret_flags |= pf->auto_disable_flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE ? + ret_flags |= pf->hw_disabled_flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE ? 0 : I40E_PRIV_FLAGS_HW_ATR_EVICT; if (pf->hw.pf_id == 0) { ret_flags |= pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT ? @@ -3099,7 +3099,7 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) pf->flags |= I40E_FLAG_FD_ATR_ENABLED; } else { pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED; - pf->auto_disable_flags |= I40E_FLAG_FD_ATR_ENABLED; + pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; /* flush current ATR settings */ set_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); @@ -3144,9 +3144,9 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) if ((flags & I40E_PRIV_FLAGS_HW_ATR_EVICT) && (pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE)) - pf->auto_disable_flags &= ~I40E_FLAG_HW_ATR_EVICT_CAPABLE; + pf->hw_disabled_flags &= ~I40E_FLAG_HW_ATR_EVICT_CAPABLE; else - pf->auto_disable_flags |= I40E_FLAG_HW_ATR_EVICT_CAPABLE; + pf->hw_disabled_flags |= I40E_FLAG_HW_ATR_EVICT_CAPABLE; /* if needed, issue reset to cause things to take effect */ if (reset_required) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 4d305fb1f188..113b32911f1b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1101,13 +1101,13 @@ static void i40e_update_pf_stats(struct i40e_pf *pf) &osd->rx_lpi_count, &nsd->rx_lpi_count); if (pf->flags & I40E_FLAG_FD_SB_ENABLED && - !(pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED)) + !(pf->hw_disabled_flags & I40E_FLAG_FD_SB_ENABLED)) nsd->fd_sb_status = true; else nsd->fd_sb_status = false; if (pf->flags & I40E_FLAG_FD_ATR_ENABLED && - !(pf->auto_disable_flags & I40E_FLAG_FD_ATR_ENABLED)) + !(pf->hw_disabled_flags & I40E_FLAG_FD_ATR_ENABLED)) nsd->fd_atr_status = true; else nsd->fd_atr_status = false; @@ -5467,7 +5467,7 @@ static int i40e_up_complete(struct i40e_vsi *vsi) /* reset fd counters */ pf->fd_add_err = pf->fd_atr_cnt = 0; if (pf->fd_tcp_rule > 0) { - pf->auto_disable_flags |= I40E_FLAG_FD_ATR_ENABLED; + pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; if (I40E_DEBUG_FD & pf->hw.debug_mask) dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 exist\n"); pf->fd_tcp_rule = 0; @@ -6143,8 +6143,8 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf) (pf->fd_add_err == 0) || (i40e_get_current_atr_cnt(pf) < pf->fd_atr_cnt)) { if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) && - (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED)) { - pf->auto_disable_flags &= ~I40E_FLAG_FD_SB_ENABLED; + (pf->hw_disabled_flags & I40E_FLAG_FD_SB_ENABLED)) { + pf->hw_disabled_flags &= ~I40E_FLAG_FD_SB_ENABLED; if (I40E_DEBUG_FD & pf->hw.debug_mask) dev_info(&pf->pdev->dev, "FD Sideband/ntuple is being enabled since we have space in the table now\n"); } @@ -6155,9 +6155,9 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf) */ if (fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM * 2)) { if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && - (pf->auto_disable_flags & I40E_FLAG_FD_ATR_ENABLED) && + (pf->hw_disabled_flags & I40E_FLAG_FD_ATR_ENABLED) && (pf->fd_tcp_rule == 0)) { - pf->auto_disable_flags &= ~I40E_FLAG_FD_ATR_ENABLED; + pf->hw_disabled_flags &= ~I40E_FLAG_FD_ATR_ENABLED; if (I40E_DEBUG_FD & pf->hw.debug_mask) dev_info(&pf->pdev->dev, "ATR is being enabled since we have space in the table and there are no conflicting ntuple rules\n"); } @@ -6209,7 +6209,7 @@ static void i40e_fdir_flush_and_replay(struct i40e_pf *pf) } pf->fd_flush_timestamp = jiffies; - pf->auto_disable_flags |= I40E_FLAG_FD_ATR_ENABLED; + pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; /* flush all filters */ wr32(&pf->hw, I40E_PFQF_CTL_1, I40E_PFQF_CTL_1_CLEARFDTABLE_MASK); @@ -6229,7 +6229,7 @@ static void i40e_fdir_flush_and_replay(struct i40e_pf *pf) /* replay sideband filters */ i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]); if (!disable_atr) - pf->auto_disable_flags &= ~I40E_FLAG_FD_ATR_ENABLED; + pf->hw_disabled_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"); @@ -8872,9 +8872,9 @@ static int i40e_sw_init(struct i40e_pf *pf) (pf->hw.aq.api_min_ver > 4))) { /* Supported in FW API version higher than 1.4 */ pf->flags |= I40E_FLAG_GENEVE_OFFLOAD_CAPABLE; - pf->auto_disable_flags = I40E_FLAG_HW_ATR_EVICT_CAPABLE; + pf->hw_disabled_flags = I40E_FLAG_HW_ATR_EVICT_CAPABLE; } else { - pf->auto_disable_flags = I40E_FLAG_HW_ATR_EVICT_CAPABLE; + pf->hw_disabled_flags = I40E_FLAG_HW_ATR_EVICT_CAPABLE; } pf->eeprom_version = 0xDEAD; @@ -8935,14 +8935,14 @@ bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features) i40e_fdir_filter_exit(pf); } pf->flags &= ~I40E_FLAG_FD_SB_ENABLED; - pf->auto_disable_flags &= ~I40E_FLAG_FD_SB_ENABLED; + pf->hw_disabled_flags &= ~I40E_FLAG_FD_SB_ENABLED; /* reset fd counters */ pf->fd_add_err = pf->fd_atr_cnt = pf->fd_tcp_rule = 0; pf->fdir_pf_active_filters = 0; /* if ATR was auto disabled it can be re-enabled. */ if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && - (pf->auto_disable_flags & I40E_FLAG_FD_ATR_ENABLED)) { - pf->auto_disable_flags &= ~I40E_FLAG_FD_ATR_ENABLED; + (pf->hw_disabled_flags & I40E_FLAG_FD_ATR_ENABLED)) { + pf->hw_disabled_flags &= ~I40E_FLAG_FD_ATR_ENABLED; if (I40E_DEBUG_FD & pf->hw.debug_mask) dev_info(&pf->pdev->dev, "ATR re-enabled.\n"); } diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 86e4991a03a7..6eb5dc4168f3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -291,7 +291,7 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && I40E_DEBUG_FD & pf->hw.debug_mask) dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n"); - pf->auto_disable_flags |= I40E_FLAG_FD_ATR_ENABLED; + pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; } else { pf->fd_tcp_rule = (pf->fd_tcp_rule > 0) ? (pf->fd_tcp_rule - 1) : 0; @@ -299,7 +299,7 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && I40E_DEBUG_FD & pf->hw.debug_mask) dev_info(&pf->pdev->dev, "ATR re-enabled due to no sideband TCP/IPv4 rules\n"); - pf->auto_disable_flags &= ~I40E_FLAG_FD_ATR_ENABLED; + pf->hw_disabled_flags &= ~I40E_FLAG_FD_ATR_ENABLED; } } @@ -484,8 +484,8 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, pf->fd_atr_cnt = i40e_get_current_atr_cnt(pf); if ((rx_desc->wb.qword0.hi_dword.fd_id == 0) && - (pf->auto_disable_flags & I40E_FLAG_FD_SB_ENABLED)) { - pf->auto_disable_flags |= I40E_FLAG_FD_ATR_ENABLED; + (pf->hw_disabled_flags & I40E_FLAG_FD_SB_ENABLED)) { + pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; set_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); } @@ -498,11 +498,11 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, */ if (fcnt_prog >= (fcnt_avail - I40E_FDIR_BUFFER_FULL_MARGIN)) { if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) && - !(pf->auto_disable_flags & + !(pf->hw_disabled_flags & I40E_FLAG_FD_SB_ENABLED)) { if (I40E_DEBUG_FD & pf->hw.debug_mask) dev_warn(&pdev->dev, "FD filter space full, new ntuple rules will not be added\n"); - pf->auto_disable_flags |= + pf->hw_disabled_flags |= I40E_FLAG_FD_SB_ENABLED; } } @@ -2100,7 +2100,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, if (!(pf->flags & I40E_FLAG_FD_ATR_ENABLED)) return; - if ((pf->auto_disable_flags & I40E_FLAG_FD_ATR_ENABLED)) + if ((pf->hw_disabled_flags & I40E_FLAG_FD_ATR_ENABLED)) return; /* if sampling is disabled do nothing */ @@ -2134,10 +2134,10 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, th = (struct tcphdr *)(hdr.network + hlen); /* Due to lack of space, no more new filters can be programmed */ - if (th->syn && (pf->auto_disable_flags & I40E_FLAG_FD_ATR_ENABLED)) + if (th->syn && (pf->hw_disabled_flags & I40E_FLAG_FD_ATR_ENABLED)) return; if ((pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) && - (!(pf->auto_disable_flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE))) { + (!(pf->hw_disabled_flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE))) { /* HW ATR eviction will take care of removing filters on FIN * and RST packets. */ @@ -2200,7 +2200,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, I40E_TXD_FLTR_QW1_CNTINDEX_MASK; if ((pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) && - (!(pf->auto_disable_flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE))) + (!(pf->hw_disabled_flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE))) dtype_cmd |= I40E_TXD_FLTR_QW1_ATR_MASK; fdir_desc->qindex_flex_ptype_vsi = cpu_to_le32(flex_ptype); -- cgit From 6deee2221e110f6574988120dba6cab7e7313f44 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Wed, 15 Mar 2017 11:04:45 +0000 Subject: net: stmmac: prepare dma op mode config for multiple queues This patch prepares DMA Operation Mode configuration for multiple queues. The work consisted on breaking the DMA operation Mode configuration function into RX and TX scope and adapting its mechanism in stmmac_main. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 3 + drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 118 +++++++++++----------- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 82 +++++++++++---- 3 files changed, 124 insertions(+), 79 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 9f0d26da6813..13bd3d4e381f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -424,6 +424,9 @@ struct stmmac_dma_ops { * An invalid value enables the store-and-forward mode */ void (*dma_mode)(void __iomem *ioaddr, int txmode, int rxmode, int rxfifosz); + void (*dma_rx_mode)(void __iomem *ioaddr, int mode, u32 channel, + int fifosz); + void (*dma_tx_mode)(void __iomem *ioaddr, int mode, u32 channel); /* To track extra statistic (if supported) */ void (*dma_diagnostic_fr) (void *data, struct stmmac_extra_stats *x, void __iomem *ioaddr); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index 6ac6b2600a7c..6285e8ad340d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -182,70 +182,26 @@ static void dwmac4_rx_watchdog(void __iomem *ioaddr, u32 riwt) writel(riwt, ioaddr + DMA_CHAN_RX_WATCHDOG(i)); } -static void dwmac4_dma_chan_op_mode(void __iomem *ioaddr, int txmode, - int rxmode, u32 channel, int rxfifosz) +static void dwmac4_dma_rx_chan_op_mode(void __iomem *ioaddr, int mode, + u32 channel, int fifosz) { - unsigned int rqs = rxfifosz / 256 - 1; - u32 mtl_tx_op, mtl_rx_op, mtl_rx_int; - - /* Following code only done for channel 0, other channels not yet - * supported. - */ - mtl_tx_op = readl(ioaddr + MTL_CHAN_TX_OP_MODE(channel)); - - if (txmode == SF_DMA_MODE) { - pr_debug("GMAC: enable TX store and forward mode\n"); - /* Transmit COE type 2 cannot be done in cut-through mode. */ - mtl_tx_op |= MTL_OP_MODE_TSF; - } else { - pr_debug("GMAC: disabling TX SF (threshold %d)\n", txmode); - mtl_tx_op &= ~MTL_OP_MODE_TSF; - mtl_tx_op &= MTL_OP_MODE_TTC_MASK; - /* Set the transmit threshold */ - if (txmode <= 32) - mtl_tx_op |= MTL_OP_MODE_TTC_32; - else if (txmode <= 64) - mtl_tx_op |= MTL_OP_MODE_TTC_64; - else if (txmode <= 96) - mtl_tx_op |= MTL_OP_MODE_TTC_96; - else if (txmode <= 128) - mtl_tx_op |= MTL_OP_MODE_TTC_128; - else if (txmode <= 192) - mtl_tx_op |= MTL_OP_MODE_TTC_192; - else if (txmode <= 256) - mtl_tx_op |= MTL_OP_MODE_TTC_256; - else if (txmode <= 384) - mtl_tx_op |= MTL_OP_MODE_TTC_384; - else - mtl_tx_op |= MTL_OP_MODE_TTC_512; - } - /* For an IP with DWC_EQOS_NUM_TXQ == 1, the fields TXQEN and TQS are RO - * with reset values: TXQEN on, TQS == DWC_EQOS_TXFIFO_SIZE. - * For an IP with DWC_EQOS_NUM_TXQ > 1, the fields TXQEN and TQS are R/W - * with reset values: TXQEN off, TQS 256 bytes. - * - * Write the bits in both cases, since it will have no effect when RO. - * For DWC_EQOS_NUM_TXQ > 1, the top bits in MTL_OP_MODE_TQS_MASK might - * be RO, however, writing the whole TQS field will result in a value - * equal to DWC_EQOS_TXFIFO_SIZE, just like for DWC_EQOS_NUM_TXQ == 1. - */ - mtl_tx_op |= MTL_OP_MODE_TXQEN | MTL_OP_MODE_TQS_MASK; - writel(mtl_tx_op, ioaddr + MTL_CHAN_TX_OP_MODE(channel)); + unsigned int rqs = fifosz / 256 - 1; + u32 mtl_rx_op, mtl_rx_int; mtl_rx_op = readl(ioaddr + MTL_CHAN_RX_OP_MODE(channel)); - if (rxmode == SF_DMA_MODE) { + if (mode == SF_DMA_MODE) { pr_debug("GMAC: enable RX store and forward mode\n"); mtl_rx_op |= MTL_OP_MODE_RSF; } else { - pr_debug("GMAC: disable RX SF mode (threshold %d)\n", rxmode); + pr_debug("GMAC: disable RX SF mode (threshold %d)\n", mode); mtl_rx_op &= ~MTL_OP_MODE_RSF; mtl_rx_op &= MTL_OP_MODE_RTC_MASK; - if (rxmode <= 32) + if (mode <= 32) mtl_rx_op |= MTL_OP_MODE_RTC_32; - else if (rxmode <= 64) + else if (mode <= 64) mtl_rx_op |= MTL_OP_MODE_RTC_64; - else if (rxmode <= 96) + else if (mode <= 96) mtl_rx_op |= MTL_OP_MODE_RTC_96; else mtl_rx_op |= MTL_OP_MODE_RTC_128; @@ -255,7 +211,7 @@ static void dwmac4_dma_chan_op_mode(void __iomem *ioaddr, int txmode, mtl_rx_op |= rqs << MTL_OP_MODE_RQS_SHIFT; /* enable flow control only if each channel gets 4 KiB or more FIFO */ - if (rxfifosz >= 4096) { + if (fifosz >= 4096) { unsigned int rfd, rfa; mtl_rx_op |= MTL_OP_MODE_EHFC; @@ -266,7 +222,7 @@ static void dwmac4_dma_chan_op_mode(void __iomem *ioaddr, int txmode, * Set Threshold for Deactivating Flow Control to min 1 frame, * i.e. 1500 bytes. */ - switch (rxfifosz) { + switch (fifosz) { case 4096: /* This violates the above formula because of FIFO size * limit therefore overflow may occur in spite of this. @@ -306,11 +262,49 @@ static void dwmac4_dma_chan_op_mode(void __iomem *ioaddr, int txmode, ioaddr + MTL_CHAN_INT_CTRL(channel)); } -static void dwmac4_dma_operation_mode(void __iomem *ioaddr, int txmode, - int rxmode, int rxfifosz) +static void dwmac4_dma_tx_chan_op_mode(void __iomem *ioaddr, int mode, + u32 channel) { - /* Only Channel 0 is actually configured and used */ - dwmac4_dma_chan_op_mode(ioaddr, txmode, rxmode, 0, rxfifosz); + u32 mtl_tx_op = readl(ioaddr + MTL_CHAN_TX_OP_MODE(channel)); + + if (mode == SF_DMA_MODE) { + pr_debug("GMAC: enable TX store and forward mode\n"); + /* Transmit COE type 2 cannot be done in cut-through mode. */ + mtl_tx_op |= MTL_OP_MODE_TSF; + } else { + pr_debug("GMAC: disabling TX SF (threshold %d)\n", mode); + mtl_tx_op &= ~MTL_OP_MODE_TSF; + mtl_tx_op &= MTL_OP_MODE_TTC_MASK; + /* Set the transmit threshold */ + if (mode <= 32) + mtl_tx_op |= MTL_OP_MODE_TTC_32; + else if (mode <= 64) + mtl_tx_op |= MTL_OP_MODE_TTC_64; + else if (mode <= 96) + mtl_tx_op |= MTL_OP_MODE_TTC_96; + else if (mode <= 128) + mtl_tx_op |= MTL_OP_MODE_TTC_128; + else if (mode <= 192) + mtl_tx_op |= MTL_OP_MODE_TTC_192; + else if (mode <= 256) + mtl_tx_op |= MTL_OP_MODE_TTC_256; + else if (mode <= 384) + mtl_tx_op |= MTL_OP_MODE_TTC_384; + else + mtl_tx_op |= MTL_OP_MODE_TTC_512; + } + /* For an IP with DWC_EQOS_NUM_TXQ == 1, the fields TXQEN and TQS are RO + * with reset values: TXQEN on, TQS == DWC_EQOS_TXFIFO_SIZE. + * For an IP with DWC_EQOS_NUM_TXQ > 1, the fields TXQEN and TQS are R/W + * with reset values: TXQEN off, TQS 256 bytes. + * + * Write the bits in both cases, since it will have no effect when RO. + * For DWC_EQOS_NUM_TXQ > 1, the top bits in MTL_OP_MODE_TQS_MASK might + * be RO, however, writing the whole TQS field will result in a value + * equal to DWC_EQOS_TXFIFO_SIZE, just like for DWC_EQOS_NUM_TXQ == 1. + */ + mtl_tx_op |= MTL_OP_MODE_TXQEN | MTL_OP_MODE_TQS_MASK; + writel(mtl_tx_op, ioaddr + MTL_CHAN_TX_OP_MODE(channel)); } static void dwmac4_get_hw_feature(void __iomem *ioaddr, @@ -387,7 +381,8 @@ const struct stmmac_dma_ops dwmac4_dma_ops = { .init = dwmac4_dma_init, .axi = dwmac4_dma_axi, .dump_regs = dwmac4_dump_dma_regs, - .dma_mode = dwmac4_dma_operation_mode, + .dma_rx_mode = dwmac4_dma_rx_chan_op_mode, + .dma_tx_mode = dwmac4_dma_tx_chan_op_mode, .enable_dma_irq = dwmac4_enable_dma_irq, .disable_dma_irq = dwmac4_disable_dma_irq, .start_tx = dwmac4_dma_start_tx, @@ -409,7 +404,8 @@ const struct stmmac_dma_ops dwmac410_dma_ops = { .init = dwmac4_dma_init, .axi = dwmac4_dma_axi, .dump_regs = dwmac4_dump_dma_regs, - .dma_mode = dwmac4_dma_operation_mode, + .dma_rx_mode = dwmac4_dma_rx_chan_op_mode, + .dma_tx_mode = dwmac4_dma_tx_chan_op_mode, .enable_dma_irq = dwmac410_enable_dma_irq, .disable_dma_irq = dwmac4_disable_dma_irq, .start_tx = dwmac4_dma_start_tx, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index ec363e19cd79..c4e4a5328951 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1285,14 +1285,20 @@ static void stmmac_mac_enable_rx_queues(struct stmmac_priv *priv) */ static void stmmac_dma_operation_mode(struct stmmac_priv *priv) { + u32 rx_channels_count = priv->plat->rx_queues_to_use; + u32 tx_channels_count = priv->plat->tx_queues_to_use; int rxfifosz = priv->plat->rx_fifo_size; + u32 txmode = 0; + u32 rxmode = 0; + u32 chan = 0; if (rxfifosz == 0) rxfifosz = priv->dma_cap.rx_fifo_size; - if (priv->plat->force_thresh_dma_mode) - priv->hw->dma->dma_mode(priv->ioaddr, tc, tc, rxfifosz); - else if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe) { + if (priv->plat->force_thresh_dma_mode) { + txmode = tc; + rxmode = tc; + } else if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe) { /* * In case of GMAC, SF mode can be enabled * to perform the TX COE in HW. This depends on: @@ -1300,12 +1306,26 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv) * 2) There is no bugged Jumbo frame support * that needs to not insert csum in the TDES. */ - priv->hw->dma->dma_mode(priv->ioaddr, SF_DMA_MODE, SF_DMA_MODE, - rxfifosz); + txmode = SF_DMA_MODE; + rxmode = SF_DMA_MODE; priv->xstats.threshold = SF_DMA_MODE; - } else - priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE, + } else { + txmode = tc; + rxmode = SF_DMA_MODE; + } + + /* configure all channels */ + if (priv->synopsys_id >= DWMAC_CORE_4_00) { + for (chan = 0; chan < rx_channels_count; chan++) + priv->hw->dma->dma_rx_mode(priv->ioaddr, rxmode, chan, + rxfifosz); + + for (chan = 0; chan < tx_channels_count; chan++) + priv->hw->dma->dma_tx_mode(priv->ioaddr, txmode, chan); + } else { + priv->hw->dma->dma_mode(priv->ioaddr, txmode, rxmode, rxfifosz); + } } /** @@ -1443,6 +1463,34 @@ static void stmmac_tx_err(struct stmmac_priv *priv) netif_wake_queue(priv->dev); } +/** + * stmmac_set_dma_operation_mode - Set DMA operation mode by channel + * @priv: driver private structure + * @txmode: TX operating mode + * @rxmode: RX operating mode + * @chan: channel index + * Description: it is used for configuring of the DMA operation mode in + * runtime in order to program the tx/rx DMA thresholds or Store-And-Forward + * mode. + */ +static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode, + u32 rxmode, u32 chan) +{ + int rxfifosz = priv->plat->rx_fifo_size; + + if (rxfifosz == 0) + rxfifosz = priv->dma_cap.rx_fifo_size; + + if (priv->synopsys_id >= DWMAC_CORE_4_00) { + priv->hw->dma->dma_rx_mode(priv->ioaddr, rxmode, chan, + rxfifosz); + priv->hw->dma->dma_tx_mode(priv->ioaddr, txmode, chan); + } else { + priv->hw->dma->dma_mode(priv->ioaddr, txmode, rxmode, + rxfifosz); + } +} + /** * stmmac_dma_interrupt - DMA ISR * @priv: driver private structure @@ -1452,11 +1500,8 @@ static void stmmac_tx_err(struct stmmac_priv *priv) */ static void stmmac_dma_interrupt(struct stmmac_priv *priv) { + u32 chan = STMMAC_CHAN0; int status; - int rxfifosz = priv->plat->rx_fifo_size; - - if (rxfifosz == 0) - rxfifosz = priv->dma_cap.rx_fifo_size; status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats); if (likely((status & handle_rx)) || (status & handle_tx)) { @@ -1471,11 +1516,12 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv) (tc <= 256)) { tc += 64; if (priv->plat->force_thresh_dma_mode) - priv->hw->dma->dma_mode(priv->ioaddr, tc, tc, - rxfifosz); + stmmac_set_dma_operation_mode(priv->ioaddr, + tc, tc, chan); else - priv->hw->dma->dma_mode(priv->ioaddr, tc, - SF_DMA_MODE, rxfifosz); + stmmac_set_dma_operation_mode(priv->ioaddr, tc, + SF_DMA_MODE, chan); + priv->xstats.threshold = tc; } } else if (unlikely(status == tx_hard_error)) @@ -1749,6 +1795,9 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv) /* Enable MAC RX Queues */ if (rx_queues_count > 1 && priv->hw->mac->rx_queue_enable) stmmac_mac_enable_rx_queues(priv); + + /* Set the HW DMA mode and the COE */ + stmmac_dma_operation_mode(priv); } /** @@ -1812,9 +1861,6 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) else stmmac_set_mac(priv->ioaddr, true); - /* Set the HW DMA mode and the COE */ - stmmac_dma_operation_mode(priv); - stmmac_mmc_setup(priv); if (init_ptp) { -- cgit From 4f513ecd2f60d9ebd2ac0fa4cd0b5d0612d70233 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Wed, 15 Mar 2017 11:04:46 +0000 Subject: net: stmmac: enable/disable dma irq prepared for multiple queues This patch prepares the DMA IRQ enable/disable process for multiple queues. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 4 ++-- drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h | 6 +++--- drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c | 12 ++++++------ drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h | 4 ++-- drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c | 4 ++-- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 13 +++++++------ 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 13bd3d4e381f..0351b5408372 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -431,8 +431,8 @@ struct stmmac_dma_ops { void (*dma_diagnostic_fr) (void *data, struct stmmac_extra_stats *x, void __iomem *ioaddr); void (*enable_dma_transmission) (void __iomem *ioaddr); - void (*enable_dma_irq) (void __iomem *ioaddr); - void (*disable_dma_irq) (void __iomem *ioaddr); + void (*enable_dma_irq)(void __iomem *ioaddr, u32 chan); + void (*disable_dma_irq)(void __iomem *ioaddr, u32 chan); void (*start_tx) (void __iomem *ioaddr); void (*stop_tx) (void __iomem *ioaddr); void (*start_rx) (void __iomem *ioaddr); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h index 1b06df749e2b..393a657cd9ff 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h @@ -185,9 +185,9 @@ int dwmac4_dma_reset(void __iomem *ioaddr); void dwmac4_enable_dma_transmission(void __iomem *ioaddr, u32 tail_ptr); -void dwmac4_enable_dma_irq(void __iomem *ioaddr); -void dwmac410_enable_dma_irq(void __iomem *ioaddr); -void dwmac4_disable_dma_irq(void __iomem *ioaddr); +void dwmac4_enable_dma_irq(void __iomem *ioaddr, u32 chan); +void dwmac410_enable_dma_irq(void __iomem *ioaddr, u32 chan); +void dwmac4_disable_dma_irq(void __iomem *ioaddr, u32 chan); void dwmac4_dma_start_tx(void __iomem *ioaddr); void dwmac4_dma_stop_tx(void __iomem *ioaddr); void dwmac4_dma_start_rx(void __iomem *ioaddr); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c index c7326d5b2f43..c9327911e29a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c @@ -104,21 +104,21 @@ void dwmac4_set_rx_ring_len(void __iomem *ioaddr, u32 len) writel(len, ioaddr + DMA_CHAN_RX_RING_LEN(STMMAC_CHAN0)); } -void dwmac4_enable_dma_irq(void __iomem *ioaddr) +void dwmac4_enable_dma_irq(void __iomem *ioaddr, u32 chan) { writel(DMA_CHAN_INTR_DEFAULT_MASK, ioaddr + - DMA_CHAN_INTR_ENA(STMMAC_CHAN0)); + DMA_CHAN_INTR_ENA(chan)); } -void dwmac410_enable_dma_irq(void __iomem *ioaddr) +void dwmac410_enable_dma_irq(void __iomem *ioaddr, u32 chan) { writel(DMA_CHAN_INTR_DEFAULT_MASK_4_10, - ioaddr + DMA_CHAN_INTR_ENA(STMMAC_CHAN0)); + ioaddr + DMA_CHAN_INTR_ENA(chan)); } -void dwmac4_disable_dma_irq(void __iomem *ioaddr) +void dwmac4_disable_dma_irq(void __iomem *ioaddr, u32 chan) { - writel(0, ioaddr + DMA_CHAN_INTR_ENA(STMMAC_CHAN0)); + writel(0, ioaddr + DMA_CHAN_INTR_ENA(chan)); } int dwmac4_dma_interrupt(void __iomem *ioaddr, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h index 56e485f79077..dec0816565f3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h @@ -137,8 +137,8 @@ #define DMA_CONTROL_FTF 0x00100000 /* Flush transmit FIFO */ void dwmac_enable_dma_transmission(void __iomem *ioaddr); -void dwmac_enable_dma_irq(void __iomem *ioaddr); -void dwmac_disable_dma_irq(void __iomem *ioaddr); +void dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan); +void dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan); void dwmac_dma_start_tx(void __iomem *ioaddr); void dwmac_dma_stop_tx(void __iomem *ioaddr); void dwmac_dma_start_rx(void __iomem *ioaddr); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c index e60bfca2a763..285cfc9b3361 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c @@ -47,12 +47,12 @@ void dwmac_enable_dma_transmission(void __iomem *ioaddr) writel(1, ioaddr + DMA_XMT_POLL_DEMAND); } -void dwmac_enable_dma_irq(void __iomem *ioaddr) +void dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan) { writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA); } -void dwmac_disable_dma_irq(void __iomem *ioaddr) +void dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan) { writel(0, ioaddr + DMA_INTR_ENA); } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index c4e4a5328951..18cf58c16dc3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1422,14 +1422,14 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) netif_tx_unlock(priv->dev); } -static inline void stmmac_enable_dma_irq(struct stmmac_priv *priv) +static inline void stmmac_enable_dma_irq(struct stmmac_priv *priv, u32 chan) { - priv->hw->dma->enable_dma_irq(priv->ioaddr); + priv->hw->dma->enable_dma_irq(priv->ioaddr, chan); } -static inline void stmmac_disable_dma_irq(struct stmmac_priv *priv) +static inline void stmmac_disable_dma_irq(struct stmmac_priv *priv, u32 chan) { - priv->hw->dma->disable_dma_irq(priv->ioaddr); + priv->hw->dma->disable_dma_irq(priv->ioaddr, chan); } /** @@ -1506,7 +1506,7 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv) status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats); if (likely((status & handle_rx)) || (status & handle_tx)) { if (likely(napi_schedule_prep(&priv->napi))) { - stmmac_disable_dma_irq(priv); + stmmac_disable_dma_irq(priv, chan); __napi_schedule(&priv->napi); } } @@ -2832,6 +2832,7 @@ static int stmmac_poll(struct napi_struct *napi, int budget) { struct stmmac_priv *priv = container_of(napi, struct stmmac_priv, napi); int work_done = 0; + u32 chan = STMMAC_CHAN0; priv->xstats.napi_poll++; stmmac_tx_clean(priv); @@ -2839,7 +2840,7 @@ static int stmmac_poll(struct napi_struct *napi, int budget) work_done = stmmac_rx(priv, budget); if (work_done < budget) { napi_complete_done(napi, work_done); - stmmac_enable_dma_irq(priv); + stmmac_enable_dma_irq(priv, chan); } return work_done; } -- cgit From ae4f0d46830814e11ec91b8c76ebf3ae3f8140d7 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Wed, 15 Mar 2017 11:04:47 +0000 Subject: net: stmmac: rx/tx dma start/stop prepared for multiple queues This patch prepares the RX/TX DMA stop/start process for multiple queues. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 8 +- drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h | 8 +- drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c | 24 ++--- drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h | 8 +- drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c | 8 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 108 +++++++++++++++++++--- 6 files changed, 125 insertions(+), 39 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 0351b5408372..042b4828efaa 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -433,10 +433,10 @@ struct stmmac_dma_ops { void (*enable_dma_transmission) (void __iomem *ioaddr); void (*enable_dma_irq)(void __iomem *ioaddr, u32 chan); void (*disable_dma_irq)(void __iomem *ioaddr, u32 chan); - void (*start_tx) (void __iomem *ioaddr); - void (*stop_tx) (void __iomem *ioaddr); - void (*start_rx) (void __iomem *ioaddr); - void (*stop_rx) (void __iomem *ioaddr); + void (*start_tx)(void __iomem *ioaddr, u32 chan); + void (*stop_tx)(void __iomem *ioaddr, u32 chan); + void (*start_rx)(void __iomem *ioaddr, u32 chan); + void (*stop_rx)(void __iomem *ioaddr, u32 chan); int (*dma_interrupt) (void __iomem *ioaddr, struct stmmac_extra_stats *x); /* If supported then get the optional core features */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h index 393a657cd9ff..2c19042b47df 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h @@ -188,10 +188,10 @@ void dwmac4_enable_dma_transmission(void __iomem *ioaddr, u32 tail_ptr); void dwmac4_enable_dma_irq(void __iomem *ioaddr, u32 chan); void dwmac410_enable_dma_irq(void __iomem *ioaddr, u32 chan); void dwmac4_disable_dma_irq(void __iomem *ioaddr, u32 chan); -void dwmac4_dma_start_tx(void __iomem *ioaddr); -void dwmac4_dma_stop_tx(void __iomem *ioaddr); -void dwmac4_dma_start_rx(void __iomem *ioaddr); -void dwmac4_dma_stop_rx(void __iomem *ioaddr); +void dwmac4_dma_start_tx(void __iomem *ioaddr, u32 chan); +void dwmac4_dma_stop_tx(void __iomem *ioaddr, u32 chan); +void dwmac4_dma_start_rx(void __iomem *ioaddr, u32 chan); +void dwmac4_dma_stop_rx(void __iomem *ioaddr, u32 chan); int dwmac4_dma_interrupt(void __iomem *ioaddr, struct stmmac_extra_stats *x); void dwmac4_set_rx_ring_len(void __iomem *ioaddr, u32 len); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c index c9327911e29a..3512d18f626b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c @@ -45,49 +45,49 @@ void dwmac4_set_tx_tail_ptr(void __iomem *ioaddr, u32 tail_ptr, u32 chan) writel(tail_ptr, ioaddr + DMA_CHAN_TX_END_ADDR(0)); } -void dwmac4_dma_start_tx(void __iomem *ioaddr) +void dwmac4_dma_start_tx(void __iomem *ioaddr, u32 chan) { - u32 value = readl(ioaddr + DMA_CHAN_TX_CONTROL(STMMAC_CHAN0)); + u32 value = readl(ioaddr + DMA_CHAN_TX_CONTROL(chan)); value |= DMA_CONTROL_ST; - writel(value, ioaddr + DMA_CHAN_TX_CONTROL(STMMAC_CHAN0)); + writel(value, ioaddr + DMA_CHAN_TX_CONTROL(chan)); value = readl(ioaddr + GMAC_CONFIG); value |= GMAC_CONFIG_TE; writel(value, ioaddr + GMAC_CONFIG); } -void dwmac4_dma_stop_tx(void __iomem *ioaddr) +void dwmac4_dma_stop_tx(void __iomem *ioaddr, u32 chan) { - u32 value = readl(ioaddr + DMA_CHAN_TX_CONTROL(STMMAC_CHAN0)); + u32 value = readl(ioaddr + DMA_CHAN_TX_CONTROL(chan)); value &= ~DMA_CONTROL_ST; - writel(value, ioaddr + DMA_CHAN_TX_CONTROL(STMMAC_CHAN0)); + writel(value, ioaddr + DMA_CHAN_TX_CONTROL(chan)); value = readl(ioaddr + GMAC_CONFIG); value &= ~GMAC_CONFIG_TE; writel(value, ioaddr + GMAC_CONFIG); } -void dwmac4_dma_start_rx(void __iomem *ioaddr) +void dwmac4_dma_start_rx(void __iomem *ioaddr, u32 chan) { - u32 value = readl(ioaddr + DMA_CHAN_RX_CONTROL(STMMAC_CHAN0)); + u32 value = readl(ioaddr + DMA_CHAN_RX_CONTROL(chan)); value |= DMA_CONTROL_SR; - writel(value, ioaddr + DMA_CHAN_RX_CONTROL(STMMAC_CHAN0)); + writel(value, ioaddr + DMA_CHAN_RX_CONTROL(chan)); value = readl(ioaddr + GMAC_CONFIG); value |= GMAC_CONFIG_RE; writel(value, ioaddr + GMAC_CONFIG); } -void dwmac4_dma_stop_rx(void __iomem *ioaddr) +void dwmac4_dma_stop_rx(void __iomem *ioaddr, u32 chan) { - u32 value = readl(ioaddr + DMA_CHAN_RX_CONTROL(STMMAC_CHAN0)); + u32 value = readl(ioaddr + DMA_CHAN_RX_CONTROL(chan)); value &= ~DMA_CONTROL_SR; - writel(value, ioaddr + DMA_CHAN_RX_CONTROL(STMMAC_CHAN0)); + writel(value, ioaddr + DMA_CHAN_RX_CONTROL(chan)); value = readl(ioaddr + GMAC_CONFIG); value &= ~GMAC_CONFIG_RE; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h index dec0816565f3..6c6cc7137ee0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h @@ -139,10 +139,10 @@ void dwmac_enable_dma_transmission(void __iomem *ioaddr); void dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan); void dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan); -void dwmac_dma_start_tx(void __iomem *ioaddr); -void dwmac_dma_stop_tx(void __iomem *ioaddr); -void dwmac_dma_start_rx(void __iomem *ioaddr); -void dwmac_dma_stop_rx(void __iomem *ioaddr); +void dwmac_dma_start_tx(void __iomem *ioaddr, u32 chan); +void dwmac_dma_stop_tx(void __iomem *ioaddr, u32 chan); +void dwmac_dma_start_rx(void __iomem *ioaddr, u32 chan); +void dwmac_dma_stop_rx(void __iomem *ioaddr, u32 chan); int dwmac_dma_interrupt(void __iomem *ioaddr, struct stmmac_extra_stats *x); int dwmac_dma_reset(void __iomem *ioaddr); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c index 285cfc9b3361..7be60c3a24e8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c @@ -57,28 +57,28 @@ void dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan) writel(0, ioaddr + DMA_INTR_ENA); } -void dwmac_dma_start_tx(void __iomem *ioaddr) +void dwmac_dma_start_tx(void __iomem *ioaddr, u32 chan) { u32 value = readl(ioaddr + DMA_CONTROL); value |= DMA_CONTROL_ST; writel(value, ioaddr + DMA_CONTROL); } -void dwmac_dma_stop_tx(void __iomem *ioaddr) +void dwmac_dma_stop_tx(void __iomem *ioaddr, u32 chan) { u32 value = readl(ioaddr + DMA_CONTROL); value &= ~DMA_CONTROL_ST; writel(value, ioaddr + DMA_CONTROL); } -void dwmac_dma_start_rx(void __iomem *ioaddr) +void dwmac_dma_start_rx(void __iomem *ioaddr, u32 chan) { u32 value = readl(ioaddr + DMA_CONTROL); value |= DMA_CONTROL_SR; writel(value, ioaddr + DMA_CONTROL); } -void dwmac_dma_stop_rx(void __iomem *ioaddr) +void dwmac_dma_stop_rx(void __iomem *ioaddr, u32 chan) { u32 value = readl(ioaddr + DMA_CONTROL); value &= ~DMA_CONTROL_SR; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 18cf58c16dc3..a537276e1055 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1277,6 +1277,96 @@ static void stmmac_mac_enable_rx_queues(struct stmmac_priv *priv) } } +/** + * stmmac_start_rx_dma - start RX DMA channel + * @priv: driver private structure + * @chan: RX channel index + * Description: + * This starts a RX DMA channel + */ +static void stmmac_start_rx_dma(struct stmmac_priv *priv, u32 chan) +{ + netdev_dbg(priv->dev, "DMA RX processes started in channel %d\n", chan); + priv->hw->dma->start_rx(priv->ioaddr, chan); +} + +/** + * stmmac_start_tx_dma - start TX DMA channel + * @priv: driver private structure + * @chan: TX channel index + * Description: + * This starts a TX DMA channel + */ +static void stmmac_start_tx_dma(struct stmmac_priv *priv, u32 chan) +{ + netdev_dbg(priv->dev, "DMA TX processes started in channel %d\n", chan); + priv->hw->dma->start_tx(priv->ioaddr, chan); +} + +/** + * stmmac_stop_rx_dma - stop RX DMA channel + * @priv: driver private structure + * @chan: RX channel index + * Description: + * This stops a RX DMA channel + */ +static void stmmac_stop_rx_dma(struct stmmac_priv *priv, u32 chan) +{ + netdev_dbg(priv->dev, "DMA RX processes stopped in channel %d\n", chan); + priv->hw->dma->stop_rx(priv->ioaddr, chan); +} + +/** + * stmmac_stop_tx_dma - stop TX DMA channel + * @priv: driver private structure + * @chan: TX channel index + * Description: + * This stops a TX DMA channel + */ +static void stmmac_stop_tx_dma(struct stmmac_priv *priv, u32 chan) +{ + netdev_dbg(priv->dev, "DMA TX processes stopped in channel %d\n", chan); + priv->hw->dma->stop_tx(priv->ioaddr, chan); +} + +/** + * stmmac_start_all_dma - start all RX and TX DMA channels + * @priv: driver private structure + * Description: + * This starts all the RX and TX DMA channels + */ +static void stmmac_start_all_dma(struct stmmac_priv *priv) +{ + u32 rx_channels_count = priv->plat->rx_queues_to_use; + u32 tx_channels_count = priv->plat->tx_queues_to_use; + u32 chan = 0; + + for (chan = 0; chan < rx_channels_count; chan++) + stmmac_start_rx_dma(priv, chan); + + for (chan = 0; chan < tx_channels_count; chan++) + stmmac_start_tx_dma(priv, chan); +} + +/** + * stmmac_stop_all_dma - stop all RX and TX DMA channels + * @priv: driver private structure + * Description: + * This stops the RX and TX DMA channels + */ +static void stmmac_stop_all_dma(struct stmmac_priv *priv) +{ + u32 rx_channels_count = priv->plat->rx_queues_to_use; + u32 tx_channels_count = priv->plat->tx_queues_to_use; + u32 chan = 0; + + for (chan = 0; chan < rx_channels_count; chan++) + stmmac_stop_rx_dma(priv, chan); + + for (chan = 0; chan < tx_channels_count; chan++) + stmmac_stop_tx_dma(priv, chan); +} + /** * stmmac_dma_operation_mode - HW DMA operation mode * @priv: driver private structure @@ -1440,10 +1530,11 @@ static inline void stmmac_disable_dma_irq(struct stmmac_priv *priv, u32 chan) */ static void stmmac_tx_err(struct stmmac_priv *priv) { + u32 chan = STMMAC_CHAN0; int i; netif_stop_queue(priv->dev); - priv->hw->dma->stop_tx(priv->ioaddr); + stmmac_stop_tx_dma(priv, chan); dma_free_tx_skbufs(priv); for (i = 0; i < DMA_TX_SIZE; i++) if (priv->extend_desc) @@ -1457,7 +1548,7 @@ static void stmmac_tx_err(struct stmmac_priv *priv) priv->dirty_tx = 0; priv->cur_tx = 0; netdev_reset_queue(priv->dev); - priv->hw->dma->start_tx(priv->ioaddr); + stmmac_start_tx_dma(priv, chan); priv->dev->stats.tx_errors++; netif_wake_queue(priv->dev); @@ -1882,9 +1973,7 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) __func__); #endif /* Start the ball rolling... */ - netdev_dbg(priv->dev, "DMA RX/TX processes started...\n"); - priv->hw->dma->start_tx(priv->ioaddr); - priv->hw->dma->start_rx(priv->ioaddr); + stmmac_start_all_dma(priv); priv->tx_lpi_timer = STMMAC_DEFAULT_TWT_LS; @@ -2070,8 +2159,7 @@ static int stmmac_release(struct net_device *dev) free_irq(priv->lpi_irq, dev); /* Stop TX/RX DMA and clear the descriptors */ - priv->hw->dma->stop_tx(priv->ioaddr); - priv->hw->dma->stop_rx(priv->ioaddr); + stmmac_stop_all_dma(priv); /* Release and free the Rx/Tx resources */ free_dma_desc_resources(priv); @@ -3546,8 +3634,7 @@ int stmmac_dvr_remove(struct device *dev) netdev_info(priv->dev, "%s: removing driver", __func__); - priv->hw->dma->stop_rx(priv->ioaddr); - priv->hw->dma->stop_tx(priv->ioaddr); + stmmac_stop_all_dma(priv); stmmac_set_mac(priv->ioaddr, false); netif_carrier_off(ndev); @@ -3593,8 +3680,7 @@ int stmmac_suspend(struct device *dev) napi_disable(&priv->napi); /* Stop TX/RX DMA */ - priv->hw->dma->stop_tx(priv->ioaddr); - priv->hw->dma->stop_rx(priv->ioaddr); + stmmac_stop_all_dma(priv); /* Enable Power down mode by programming the PMT regs */ if (device_may_wakeup(priv->device)) { -- cgit From 4e593262290c3488ca7ab8b41a5c70c284e12b57 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Wed, 15 Mar 2017 11:04:48 +0000 Subject: net: stmmac: prepare stmmac_tx_err for multiple queues This patch prepares stmmac_err for multiple queues. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index a537276e1055..b166c05e2a5d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1525,12 +1525,12 @@ static inline void stmmac_disable_dma_irq(struct stmmac_priv *priv, u32 chan) /** * stmmac_tx_err - to manage the tx error * @priv: driver private structure + * @chan: channel index * Description: it cleans the descriptors and restarts the transmission * in case of transmission errors. */ -static void stmmac_tx_err(struct stmmac_priv *priv) +static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan) { - u32 chan = STMMAC_CHAN0; int i; netif_stop_queue(priv->dev); @@ -1616,7 +1616,7 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv) priv->xstats.threshold = tc; } } else if (unlikely(status == tx_hard_error)) - stmmac_tx_err(priv); + stmmac_tx_err(priv, chan); } /** @@ -2944,9 +2944,10 @@ static int stmmac_poll(struct napi_struct *napi, int budget) static void stmmac_tx_timeout(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); + u32 chan = STMMAC_CHAN0; /* Clear Tx resources and restart transmitting again */ - stmmac_tx_err(priv); + stmmac_tx_err(priv, chan); } /** -- cgit From d62a107a4f154c9aa826dbe03ac2322a389b37b5 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Wed, 15 Mar 2017 11:04:49 +0000 Subject: net: stmmac: prepare dma interrupt treatment for multiple queues This patch prepares DMA interrupts treatment for multiple queues. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 2 +- drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h | 2 +- drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c | 8 ++-- drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h | 3 +- drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c | 2 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 51 +++++++++++++---------- 6 files changed, 39 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 042b4828efaa..6dfb7f31898e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -438,7 +438,7 @@ struct stmmac_dma_ops { void (*start_rx)(void __iomem *ioaddr, u32 chan); void (*stop_rx)(void __iomem *ioaddr, u32 chan); int (*dma_interrupt) (void __iomem *ioaddr, - struct stmmac_extra_stats *x); + struct stmmac_extra_stats *x, u32 chan); /* If supported then get the optional core features */ void (*get_hw_feature)(void __iomem *ioaddr, struct dma_features *dma_cap); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h index 2c19042b47df..946dc14fe77f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h @@ -193,7 +193,7 @@ void dwmac4_dma_stop_tx(void __iomem *ioaddr, u32 chan); void dwmac4_dma_start_rx(void __iomem *ioaddr, u32 chan); void dwmac4_dma_stop_rx(void __iomem *ioaddr, u32 chan); int dwmac4_dma_interrupt(void __iomem *ioaddr, - struct stmmac_extra_stats *x); + struct stmmac_extra_stats *x, u32 chan); void dwmac4_set_rx_ring_len(void __iomem *ioaddr, u32 len); void dwmac4_set_tx_ring_len(void __iomem *ioaddr, u32 len); void dwmac4_set_rx_tail_ptr(void __iomem *ioaddr, u32 tail_ptr, u32 chan); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c index 3512d18f626b..fcd8ec8a240b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c @@ -122,11 +122,11 @@ void dwmac4_disable_dma_irq(void __iomem *ioaddr, u32 chan) } int dwmac4_dma_interrupt(void __iomem *ioaddr, - struct stmmac_extra_stats *x) + struct stmmac_extra_stats *x, u32 chan) { int ret = 0; - u32 intr_status = readl(ioaddr + DMA_CHAN_STATUS(0)); + u32 intr_status = readl(ioaddr + DMA_CHAN_STATUS(chan)); /* ABNORMAL interrupts */ if (unlikely(intr_status & DMA_CHAN_STATUS_AIS)) { @@ -153,7 +153,7 @@ int dwmac4_dma_interrupt(void __iomem *ioaddr, if (likely(intr_status & DMA_CHAN_STATUS_RI)) { u32 value; - value = readl(ioaddr + DMA_CHAN_INTR_ENA(STMMAC_CHAN0)); + value = readl(ioaddr + DMA_CHAN_INTR_ENA(chan)); /* to schedule NAPI on real RIE event. */ if (likely(value & DMA_CHAN_INTR_ENA_RIE)) { x->rx_normal_irq_n++; @@ -172,7 +172,7 @@ int dwmac4_dma_interrupt(void __iomem *ioaddr, * status [21-0] expect reserved bits [5-3] */ writel((intr_status & 0x3fffc7), - ioaddr + DMA_CHAN_STATUS(STMMAC_CHAN0)); + ioaddr + DMA_CHAN_STATUS(chan)); return ret; } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h index 6c6cc7137ee0..9091df86723a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h @@ -143,7 +143,8 @@ void dwmac_dma_start_tx(void __iomem *ioaddr, u32 chan); void dwmac_dma_stop_tx(void __iomem *ioaddr, u32 chan); void dwmac_dma_start_rx(void __iomem *ioaddr, u32 chan); void dwmac_dma_stop_rx(void __iomem *ioaddr, u32 chan); -int dwmac_dma_interrupt(void __iomem *ioaddr, struct stmmac_extra_stats *x); +int dwmac_dma_interrupt(void __iomem *ioaddr, struct stmmac_extra_stats *x, + u32 chan); int dwmac_dma_reset(void __iomem *ioaddr); #endif /* __DWMAC_DMA_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c index 7be60c3a24e8..38f94305aab5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c @@ -156,7 +156,7 @@ static void show_rx_process_state(unsigned int status) #endif int dwmac_dma_interrupt(void __iomem *ioaddr, - struct stmmac_extra_stats *x) + struct stmmac_extra_stats *x, u32 chan) { int ret = 0; /* read the status register (CSR5) */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b166c05e2a5d..79a792a0c12e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1591,32 +1591,41 @@ static void stmmac_set_dma_operation_mode(struct stmmac_priv *priv, u32 txmode, */ static void stmmac_dma_interrupt(struct stmmac_priv *priv) { - u32 chan = STMMAC_CHAN0; + u32 tx_channel_count = priv->plat->tx_queues_to_use; int status; + u32 chan; - status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats); - if (likely((status & handle_rx)) || (status & handle_tx)) { - if (likely(napi_schedule_prep(&priv->napi))) { - stmmac_disable_dma_irq(priv, chan); - __napi_schedule(&priv->napi); + for (chan = 0; chan < tx_channel_count; chan++) { + status = priv->hw->dma->dma_interrupt(priv->ioaddr, + &priv->xstats, chan); + if (likely((status & handle_rx)) || (status & handle_tx)) { + if (likely(napi_schedule_prep(&priv->napi))) { + stmmac_disable_dma_irq(priv, chan); + __napi_schedule(&priv->napi); + } } - } - if (unlikely(status & tx_hard_error_bump_tc)) { - /* Try to bump up the dma threshold on this failure */ - if (unlikely(priv->xstats.threshold != SF_DMA_MODE) && - (tc <= 256)) { - tc += 64; - if (priv->plat->force_thresh_dma_mode) - stmmac_set_dma_operation_mode(priv->ioaddr, - tc, tc, chan); - else - stmmac_set_dma_operation_mode(priv->ioaddr, tc, - SF_DMA_MODE, chan); - priv->xstats.threshold = tc; + if (unlikely(status & tx_hard_error_bump_tc)) { + /* Try to bump up the dma threshold on this failure */ + if (unlikely(priv->xstats.threshold != SF_DMA_MODE) && + (tc <= 256)) { + tc += 64; + if (priv->plat->force_thresh_dma_mode) + stmmac_set_dma_operation_mode(priv, + tc, + tc, + chan); + else + stmmac_set_dma_operation_mode(priv, + tc, + SF_DMA_MODE, + chan); + priv->xstats.threshold = tc; + } + } else if (unlikely(status == tx_hard_error)) { + stmmac_tx_err(priv, chan); } - } else if (unlikely(status == tx_hard_error)) - stmmac_tx_err(priv, chan); + } } /** -- cgit From 3c55d4d08bc9e94901a26eb57c9aa0b8c76a18c0 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Wed, 15 Mar 2017 11:04:50 +0000 Subject: net: stmmac: rx watchdog config prepared for multiple queues This patch adds rx watchdog configuration for all queues. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 2 +- drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 3 ++- drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 8 ++++---- drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 3 ++- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 ++- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 6dfb7f31898e..5fa23b13d3cd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -443,7 +443,7 @@ struct stmmac_dma_ops { void (*get_hw_feature)(void __iomem *ioaddr, struct dma_features *dma_cap); /* Program the HW RX Watchdog */ - void (*rx_watchdog) (void __iomem *ioaddr, u32 riwt); + void (*rx_watchdog)(void __iomem *ioaddr, u32 riwt, u32 number_chan); void (*set_tx_ring_len)(void __iomem *ioaddr, u32 len); void (*set_rx_ring_len)(void __iomem *ioaddr, u32 len); void (*set_rx_tail_ptr)(void __iomem *ioaddr, u32 tail_ptr, u32 chan); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index d3654a447046..471a9aa6ac94 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -247,7 +247,8 @@ static void dwmac1000_get_hw_feature(void __iomem *ioaddr, dma_cap->enh_desc = (hw_cap & DMA_HW_FEAT_ENHDESSEL) >> 24; } -static void dwmac1000_rx_watchdog(void __iomem *ioaddr, u32 riwt) +static void dwmac1000_rx_watchdog(void __iomem *ioaddr, u32 riwt, + u32 number_chan) { writel(riwt, ioaddr + DMA_RX_WATCHDOG); } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index 6285e8ad340d..74177f9d640b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -174,12 +174,12 @@ static void dwmac4_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space) _dwmac4_dump_dma_regs(ioaddr, i, reg_space); } -static void dwmac4_rx_watchdog(void __iomem *ioaddr, u32 riwt) +static void dwmac4_rx_watchdog(void __iomem *ioaddr, u32 riwt, u32 number_chan) { - int i; + u32 chan; - for (i = 0; i < DMA_CHANNEL_NB_MAX; i++) - writel(riwt, ioaddr + DMA_CHAN_RX_WATCHDOG(i)); + for (chan = 0; chan < number_chan; chan++) + writel(riwt, ioaddr + DMA_CHAN_RX_WATCHDOG(chan)); } static void dwmac4_dma_rx_chan_op_mode(void __iomem *ioaddr, int mode, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 61b9369a041e..16808e48ca1c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -730,6 +730,7 @@ static int stmmac_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) { struct stmmac_priv *priv = netdev_priv(dev); + u32 rx_cnt = priv->plat->rx_queues_to_use; unsigned int rx_riwt; /* Check not supported parameters */ @@ -768,7 +769,7 @@ static int stmmac_set_coalesce(struct net_device *dev, priv->tx_coal_frames = ec->tx_max_coalesced_frames; priv->tx_coal_timer = ec->tx_coalesce_usecs; priv->rx_riwt = rx_riwt; - priv->hw->dma->rx_watchdog(priv->ioaddr, priv->rx_riwt); + priv->hw->dma->rx_watchdog(priv->ioaddr, priv->rx_riwt, rx_cnt); return 0; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 79a792a0c12e..00d0f5ea7682 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1915,6 +1915,7 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv) static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) { struct stmmac_priv *priv = netdev_priv(dev); + u32 rx_cnt = priv->plat->rx_queues_to_use; int ret; /* DMA initialization and SW reset */ @@ -1988,7 +1989,7 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) if ((priv->use_riwt) && (priv->hw->dma->rx_watchdog)) { priv->rx_riwt = MAX_DMA_RIWT; - priv->hw->dma->rx_watchdog(priv->ioaddr, MAX_DMA_RIWT); + priv->hw->dma->rx_watchdog(priv->ioaddr, MAX_DMA_RIWT, rx_cnt); } if (priv->hw->pcs && priv->hw->mac->pcs_ctrl_ane) -- cgit From 4854ab9966e10ca002e4cc7a12e34c1444357d3d Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Wed, 15 Mar 2017 11:04:51 +0000 Subject: net: stmmac: rx and tx ring length prepared for multiple queues This patch prepares tx and rx ring length configuration for multiple queues. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 4 +-- drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h | 4 +-- drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c | 8 +++--- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 32 +++++++++++++++++------ 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 5fa23b13d3cd..bef1fc6a6756 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -444,8 +444,8 @@ struct stmmac_dma_ops { struct dma_features *dma_cap); /* Program the HW RX Watchdog */ void (*rx_watchdog)(void __iomem *ioaddr, u32 riwt, u32 number_chan); - void (*set_tx_ring_len)(void __iomem *ioaddr, u32 len); - void (*set_rx_ring_len)(void __iomem *ioaddr, u32 len); + void (*set_tx_ring_len)(void __iomem *ioaddr, u32 len, u32 chan); + void (*set_rx_ring_len)(void __iomem *ioaddr, u32 len, u32 chan); void (*set_rx_tail_ptr)(void __iomem *ioaddr, u32 tail_ptr, u32 chan); void (*set_tx_tail_ptr)(void __iomem *ioaddr, u32 tail_ptr, u32 chan); void (*enable_tso)(void __iomem *ioaddr, bool en, u32 chan); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h index 946dc14fe77f..8474bf961dd0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h @@ -194,8 +194,8 @@ void dwmac4_dma_start_rx(void __iomem *ioaddr, u32 chan); void dwmac4_dma_stop_rx(void __iomem *ioaddr, u32 chan); int dwmac4_dma_interrupt(void __iomem *ioaddr, struct stmmac_extra_stats *x, u32 chan); -void dwmac4_set_rx_ring_len(void __iomem *ioaddr, u32 len); -void dwmac4_set_tx_ring_len(void __iomem *ioaddr, u32 len); +void dwmac4_set_rx_ring_len(void __iomem *ioaddr, u32 len, u32 chan); +void dwmac4_set_tx_ring_len(void __iomem *ioaddr, u32 len, u32 chan); void dwmac4_set_rx_tail_ptr(void __iomem *ioaddr, u32 tail_ptr, u32 chan); void dwmac4_set_tx_tail_ptr(void __iomem *ioaddr, u32 tail_ptr, u32 chan); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c index fcd8ec8a240b..da54c0b0bdf5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c @@ -94,14 +94,14 @@ void dwmac4_dma_stop_rx(void __iomem *ioaddr, u32 chan) writel(value, ioaddr + GMAC_CONFIG); } -void dwmac4_set_tx_ring_len(void __iomem *ioaddr, u32 len) +void dwmac4_set_tx_ring_len(void __iomem *ioaddr, u32 len, u32 chan) { - writel(len, ioaddr + DMA_CHAN_TX_RING_LEN(STMMAC_CHAN0)); + writel(len, ioaddr + DMA_CHAN_TX_RING_LEN(chan)); } -void dwmac4_set_rx_ring_len(void __iomem *ioaddr, u32 len) +void dwmac4_set_rx_ring_len(void __iomem *ioaddr, u32 len, u32 chan) { - writel(len, ioaddr + DMA_CHAN_RX_RING_LEN(STMMAC_CHAN0)); + writel(len, ioaddr + DMA_CHAN_RX_RING_LEN(chan)); } void dwmac4_enable_dma_irq(void __iomem *ioaddr, u32 chan) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 00d0f5ea7682..26695d9a3283 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1802,6 +1802,27 @@ static void stmmac_init_tx_coalesce(struct stmmac_priv *priv) add_timer(&priv->txtimer); } +static void stmmac_set_rings_length(struct stmmac_priv *priv) +{ + u32 rx_channels_count = priv->plat->rx_queues_to_use; + u32 tx_channels_count = priv->plat->tx_queues_to_use; + u32 chan; + + /* set TX ring length */ + if (priv->hw->dma->set_tx_ring_len) { + for (chan = 0; chan < tx_channels_count; chan++) + priv->hw->dma->set_tx_ring_len(priv->ioaddr, + (DMA_TX_SIZE - 1), chan); + } + + /* set RX ring length */ + if (priv->hw->dma->set_rx_ring_len) { + for (chan = 0; chan < rx_channels_count; chan++) + priv->hw->dma->set_rx_ring_len(priv->ioaddr, + (DMA_RX_SIZE - 1), chan); + } +} + /** * stmmac_set_tx_queue_weight - Set TX queue weight * @priv: driver private structure @@ -1995,14 +2016,9 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) if (priv->hw->pcs && priv->hw->mac->pcs_ctrl_ane) priv->hw->mac->pcs_ctrl_ane(priv->hw, 1, priv->hw->ps, 0); - /* set TX ring length */ - if (priv->hw->dma->set_tx_ring_len) - priv->hw->dma->set_tx_ring_len(priv->ioaddr, - (DMA_TX_SIZE - 1)); - /* set RX ring length */ - if (priv->hw->dma->set_rx_ring_len) - priv->hw->dma->set_rx_ring_len(priv->ioaddr, - (DMA_RX_SIZE - 1)); + /* set TX and RX rings length */ + stmmac_set_rings_length(priv); + /* Enable TSO */ if (priv->tso) priv->hw->dma->enable_tso(priv->ioaddr, 1, STMMAC_CHAN0); -- cgit From 89cc57c55c1a7627b5dc912184da7e68d0d193bd Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Wed, 15 Mar 2017 11:04:52 +0000 Subject: net: stmmac: prepare rx/tx set tail function for multiple queues This patch prepares RX and TX set tail functions for multiple queues. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c index da54c0b0bdf5..49f5687879df 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c @@ -37,12 +37,12 @@ int dwmac4_dma_reset(void __iomem *ioaddr) void dwmac4_set_rx_tail_ptr(void __iomem *ioaddr, u32 tail_ptr, u32 chan) { - writel(tail_ptr, ioaddr + DMA_CHAN_RX_END_ADDR(0)); + writel(tail_ptr, ioaddr + DMA_CHAN_RX_END_ADDR(chan)); } void dwmac4_set_tx_tail_ptr(void __iomem *ioaddr, u32 tail_ptr, u32 chan) { - writel(tail_ptr, ioaddr + DMA_CHAN_TX_END_ADDR(0)); + writel(tail_ptr, ioaddr + DMA_CHAN_TX_END_ADDR(chan)); } void dwmac4_dma_start_tx(void __iomem *ioaddr, u32 chan) -- cgit From 47f2a9ce527addf8a2c384efc47f0c812dc1a162 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Wed, 15 Mar 2017 11:04:53 +0000 Subject: net: stmmac: dma channel init prepared for multiple queues This patch prepares the DMA initialization process for multiple queues. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 8 +++ drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c | 66 ++++++++++++++--------- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 51 ++++++++++++++---- 3 files changed, 88 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index bef1fc6a6756..badc4414d67b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -416,6 +416,14 @@ struct stmmac_dma_ops { int (*reset)(void __iomem *ioaddr); void (*init)(void __iomem *ioaddr, struct stmmac_dma_cfg *dma_cfg, u32 dma_tx, u32 dma_rx, int atds); + void (*init_chan)(void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, u32 chan); + void (*init_rx_chan)(void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, + u32 dma_rx_phy, u32 chan); + void (*init_tx_chan)(void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, + u32 dma_tx_phy, u32 chan); /* Configure the AXI Bus Mode Register */ void (*axi)(void __iomem *ioaddr, struct stmmac_axi *axi); /* Dump DMA registers */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c index 74177f9d640b..eec8463057fd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c @@ -71,36 +71,48 @@ static void dwmac4_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi) writel(value, ioaddr + DMA_SYS_BUS_MODE); } -static void dwmac4_dma_init_channel(void __iomem *ioaddr, - struct stmmac_dma_cfg *dma_cfg, - u32 dma_tx_phy, u32 dma_rx_phy, - u32 channel) +void dwmac4_dma_init_rx_chan(void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, + u32 dma_rx_phy, u32 chan) { u32 value; - int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl; - int rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl; + u32 rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl; - /* set PBL for each channels. Currently we affect same configuration - * on each channel - */ - value = readl(ioaddr + DMA_CHAN_CONTROL(channel)); - if (dma_cfg->pblx8) - value = value | DMA_BUS_MODE_PBL; - writel(value, ioaddr + DMA_CHAN_CONTROL(channel)); + value = readl(ioaddr + DMA_CHAN_RX_CONTROL(chan)); + value = value | (rxpbl << DMA_BUS_MODE_RPBL_SHIFT); + writel(value, ioaddr + DMA_CHAN_RX_CONTROL(chan)); + + writel(dma_rx_phy, ioaddr + DMA_CHAN_RX_BASE_ADDR(chan)); +} - value = readl(ioaddr + DMA_CHAN_TX_CONTROL(channel)); +void dwmac4_dma_init_tx_chan(void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, + u32 dma_tx_phy, u32 chan) +{ + u32 value; + u32 txpbl = dma_cfg->txpbl ?: dma_cfg->pbl; + + value = readl(ioaddr + DMA_CHAN_TX_CONTROL(chan)); value = value | (txpbl << DMA_BUS_MODE_PBL_SHIFT); - writel(value, ioaddr + DMA_CHAN_TX_CONTROL(channel)); + writel(value, ioaddr + DMA_CHAN_TX_CONTROL(chan)); - value = readl(ioaddr + DMA_CHAN_RX_CONTROL(channel)); - value = value | (rxpbl << DMA_BUS_MODE_RPBL_SHIFT); - writel(value, ioaddr + DMA_CHAN_RX_CONTROL(channel)); + writel(dma_tx_phy, ioaddr + DMA_CHAN_TX_BASE_ADDR(chan)); +} - /* Mask interrupts by writing to CSR7 */ - writel(DMA_CHAN_INTR_DEFAULT_MASK, ioaddr + DMA_CHAN_INTR_ENA(channel)); +void dwmac4_dma_init_channel(void __iomem *ioaddr, + struct stmmac_dma_cfg *dma_cfg, u32 chan) +{ + u32 value; + + /* common channel control register config */ + value = readl(ioaddr + DMA_CHAN_CONTROL(chan)); + if (dma_cfg->pblx8) + value = value | DMA_BUS_MODE_PBL; + writel(value, ioaddr + DMA_CHAN_CONTROL(chan)); - writel(dma_tx_phy, ioaddr + DMA_CHAN_TX_BASE_ADDR(channel)); - writel(dma_rx_phy, ioaddr + DMA_CHAN_RX_BASE_ADDR(channel)); + /* Mask interrupts by writing to CSR7 */ + writel(DMA_CHAN_INTR_DEFAULT_MASK, + ioaddr + DMA_CHAN_INTR_ENA(chan)); } static void dwmac4_dma_init(void __iomem *ioaddr, @@ -108,7 +120,6 @@ static void dwmac4_dma_init(void __iomem *ioaddr, u32 dma_tx, u32 dma_rx, int atds) { u32 value = readl(ioaddr + DMA_SYS_BUS_MODE); - int i; /* Set the Fixed burst mode */ if (dma_cfg->fixed_burst) @@ -122,9 +133,6 @@ static void dwmac4_dma_init(void __iomem *ioaddr, value |= DMA_SYS_BUS_AAL; writel(value, ioaddr + DMA_SYS_BUS_MODE); - - for (i = 0; i < DMA_CHANNEL_NB_MAX; i++) - dwmac4_dma_init_channel(ioaddr, dma_cfg, dma_tx, dma_rx, i); } static void _dwmac4_dump_dma_regs(void __iomem *ioaddr, u32 channel, @@ -379,6 +387,9 @@ static void dwmac4_enable_tso(void __iomem *ioaddr, bool en, u32 chan) const struct stmmac_dma_ops dwmac4_dma_ops = { .reset = dwmac4_dma_reset, .init = dwmac4_dma_init, + .init_chan = dwmac4_dma_init_channel, + .init_rx_chan = dwmac4_dma_init_rx_chan, + .init_tx_chan = dwmac4_dma_init_tx_chan, .axi = dwmac4_dma_axi, .dump_regs = dwmac4_dump_dma_regs, .dma_rx_mode = dwmac4_dma_rx_chan_op_mode, @@ -402,6 +413,9 @@ const struct stmmac_dma_ops dwmac4_dma_ops = { const struct stmmac_dma_ops dwmac410_dma_ops = { .reset = dwmac4_dma_reset, .init = dwmac4_dma_init, + .init_chan = dwmac4_dma_init_channel, + .init_rx_chan = dwmac4_dma_init_rx_chan, + .init_tx_chan = dwmac4_dma_init_tx_chan, .axi = dwmac4_dma_axi, .dump_regs = dwmac4_dump_dma_regs, .dma_rx_mode = dwmac4_dma_rx_chan_op_mode, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 26695d9a3283..2868391de0fb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1732,6 +1732,11 @@ static void stmmac_check_ether_addr(struct stmmac_priv *priv) */ static int stmmac_init_dma_engine(struct stmmac_priv *priv) { + u32 rx_channels_count = priv->plat->rx_queues_to_use; + u32 tx_channels_count = priv->plat->tx_queues_to_use; + u32 dummy_dma_rx_phy = 0; + u32 dummy_dma_tx_phy = 0; + u32 chan = 0; int atds = 0; int ret = 0; @@ -1749,19 +1754,43 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) return ret; } - priv->hw->dma->init(priv->ioaddr, priv->plat->dma_cfg, - priv->dma_tx_phy, priv->dma_rx_phy, atds); - if (priv->synopsys_id >= DWMAC_CORE_4_00) { - priv->rx_tail_addr = priv->dma_rx_phy + - (DMA_RX_SIZE * sizeof(struct dma_desc)); - priv->hw->dma->set_rx_tail_ptr(priv->ioaddr, priv->rx_tail_addr, - STMMAC_CHAN0); + /* DMA Configuration */ + priv->hw->dma->init(priv->ioaddr, priv->plat->dma_cfg, + dummy_dma_tx_phy, dummy_dma_rx_phy, atds); + + /* DMA RX Channel Configuration */ + for (chan = 0; chan < rx_channels_count; chan++) { + priv->hw->dma->init_rx_chan(priv->ioaddr, + priv->plat->dma_cfg, + priv->dma_rx_phy, chan); + + priv->rx_tail_addr = priv->dma_rx_phy + + (DMA_RX_SIZE * sizeof(struct dma_desc)); + priv->hw->dma->set_rx_tail_ptr(priv->ioaddr, + priv->rx_tail_addr, + chan); + } - priv->tx_tail_addr = priv->dma_tx_phy + - (DMA_TX_SIZE * sizeof(struct dma_desc)); - priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr, - STMMAC_CHAN0); + /* DMA TX Channel Configuration */ + for (chan = 0; chan < tx_channels_count; chan++) { + priv->hw->dma->init_chan(priv->ioaddr, + priv->plat->dma_cfg, + chan); + + priv->hw->dma->init_tx_chan(priv->ioaddr, + priv->plat->dma_cfg, + priv->dma_tx_phy, chan); + + priv->tx_tail_addr = priv->dma_tx_phy + + (DMA_TX_SIZE * sizeof(struct dma_desc)); + priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, + priv->tx_tail_addr, + chan); + } + } else { + priv->hw->dma->init(priv->ioaddr, priv->plat->dma_cfg, + priv->dma_tx_phy, priv->dma_rx_phy, atds); } if (priv->plat->axi && priv->hw->dma->axi) -- cgit From 146617b88b86c3d1f1309604d5d4cb38d5b24f1a Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Wed, 15 Mar 2017 11:04:54 +0000 Subject: net: stmmac: tso init prepared for multiple queues This patch configures TSO for all available tx queues. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 2868391de0fb..c802286380dc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1966,6 +1966,8 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) { struct stmmac_priv *priv = netdev_priv(dev); u32 rx_cnt = priv->plat->rx_queues_to_use; + u32 tx_cnt = priv->plat->tx_queues_to_use; + u32 chan; int ret; /* DMA initialization and SW reset */ @@ -2049,8 +2051,10 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) stmmac_set_rings_length(priv); /* Enable TSO */ - if (priv->tso) - priv->hw->dma->enable_tso(priv->ioaddr, 1, STMMAC_CHAN0); + if (priv->tso) { + for (chan = 0; chan < tx_cnt; chan++) + priv->hw->dma->enable_tso(priv->ioaddr, 1, chan); + } return 0; } -- cgit From 7bac4e1ec3ca2342929a39638d615c6b672c27a0 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Wed, 15 Mar 2017 11:04:55 +0000 Subject: net: stmmac: stmmac interrupt treatment prepared for multiple queues This patch prepares the main ISR for multiple queues. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 28 ++++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index c802286380dc..d3a21519e4c0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3115,6 +3115,12 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) { struct net_device *dev = (struct net_device *)dev_id; struct stmmac_priv *priv = netdev_priv(dev); + u32 rx_cnt = priv->plat->rx_queues_to_use; + u32 tx_cnt = priv->plat->tx_queues_to_use; + u32 queues_count; + u32 queue; + + queues_count = (rx_cnt > tx_cnt) ? rx_cnt : tx_cnt; if (priv->irq_wake) pm_wakeup_event(priv->device, 0); @@ -3129,20 +3135,26 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) int status = priv->hw->mac->host_irq_status(priv->hw, &priv->xstats); - if (priv->synopsys_id >= DWMAC_CORE_4_00) - status |= priv->hw->mac->host_mtl_irq_status(priv->hw, - STMMAC_CHAN0); - if (unlikely(status)) { /* For LPI we need to save the tx status */ if (status & CORE_IRQ_TX_PATH_IN_LPI_MODE) priv->tx_path_in_lpi_mode = true; if (status & CORE_IRQ_TX_PATH_EXIT_LPI_MODE) priv->tx_path_in_lpi_mode = false; - if (status & CORE_IRQ_MTL_RX_OVERFLOW && priv->hw->dma->set_rx_tail_ptr) - priv->hw->dma->set_rx_tail_ptr(priv->ioaddr, - priv->rx_tail_addr, - STMMAC_CHAN0); + } + + if (priv->synopsys_id >= DWMAC_CORE_4_00) { + for (queue = 0; queue < queues_count; queue++) { + status |= + priv->hw->mac->host_mtl_irq_status(priv->hw, + queue); + + if (status & CORE_IRQ_MTL_RX_OVERFLOW && + priv->hw->dma->set_rx_tail_ptr) + priv->hw->dma->set_rx_tail_ptr(priv->ioaddr, + priv->rx_tail_addr, + queue); + } } /* PCS link status */ -- cgit From 5b7696499c68a6b84127018d063649597e31627a Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 15 Mar 2017 15:31:58 +0000 Subject: netxen_nic: remove redundant check if retries is zero At the end of the timeout loop, retries will always be zero so the check for zero is redundant so remove it. Also replace printk with pr_err as recommended by checkpatch. Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c index 7b43a3b4abdc..3dd973475125 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c @@ -1375,13 +1375,8 @@ netxen_receive_peg_ready(struct netxen_adapter *adapter) } while (--retries); - if (!retries) { - printk(KERN_ERR "Receive Peg initialization not " - "complete, state: 0x%x.\n", val); - return -EIO; - } - - return 0; + pr_err("Receive Peg initialization not complete, state: 0x%x.\n", val); + return -EIO; } int netxen_init_firmware(struct netxen_adapter *adapter) -- cgit From 2026fecf516bc04df20cb50874957cd8c364fb4e Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 15 Mar 2017 10:39:18 -0700 Subject: mqprio: Change handling of hw u8 to allow for multiple hardware offload modes This patch is meant to allow for support of multiple hardware offload type for a single device. There is currently no bounds checking for the hw member of the mqprio_qopt structure. This results in us being able to pass values from 1 to 255 with all being treated the same. On retreiving the value it is returned as 1 for anything 1 or greater being set. With this change we are currently adding limited bounds checking by defining an enum and using those values to limit the reported hardware offloads. Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- include/uapi/linux/pkt_sched.h | 8 ++++++++ net/sched/sch_mqprio.c | 26 ++++++++++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index df7451d35131..099bf5528fed 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -617,6 +617,14 @@ struct tc_drr_stats { #define TC_QOPT_BITMASK 15 #define TC_QOPT_MAX_QUEUE 16 +enum { + TC_MQPRIO_HW_OFFLOAD_NONE, /* no offload requested */ + TC_MQPRIO_HW_OFFLOAD_TCS, /* offload TCs, no queue counts */ + __TC_MQPRIO_HW_OFFLOAD_MAX +}; + +#define TC_MQPRIO_HW_OFFLOAD_MAX (__TC_MQPRIO_HW_OFFLOAD_MAX - 1) + struct tc_mqprio_qopt { __u8 num_tc; __u8 prio_tc_map[TC_QOPT_BITMASK + 1]; diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index b851e209da4d..5f55bf149d9f 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -21,7 +21,7 @@ struct mqprio_sched { struct Qdisc **qdiscs; - int hw_owned; + int hw_offload; }; static void mqprio_destroy(struct Qdisc *sch) @@ -39,7 +39,7 @@ static void mqprio_destroy(struct Qdisc *sch) kfree(priv->qdiscs); } - if (priv->hw_owned && dev->netdev_ops->ndo_setup_tc) + if (priv->hw_offload && dev->netdev_ops->ndo_setup_tc) dev->netdev_ops->ndo_setup_tc(dev, sch->handle, 0, &tc); else netdev_set_num_tc(dev, 0); @@ -59,15 +59,20 @@ static int mqprio_parse_opt(struct net_device *dev, struct tc_mqprio_qopt *qopt) return -EINVAL; } - /* net_device does not support requested operation */ - if (qopt->hw && !dev->netdev_ops->ndo_setup_tc) - return -EINVAL; + /* Limit qopt->hw to maximum supported offload value. Drivers have + * the option of overriding this later if they don't support the a + * given offload type. + */ + if (qopt->hw > TC_MQPRIO_HW_OFFLOAD_MAX) + qopt->hw = TC_MQPRIO_HW_OFFLOAD_MAX; - /* if hw owned qcount and qoffset are taken from LLD so - * no reason to verify them here + /* If hardware offload is requested we will leave it to the device + * to either populate the queue counts itself or to validate the + * provided queue counts. If ndo_setup_tc is not present then + * hardware doesn't support offload and we should return an error. */ if (qopt->hw) - return 0; + return dev->netdev_ops->ndo_setup_tc ? 0 : -EINVAL; for (i = 0; i < qopt->num_tc; i++) { unsigned int last = qopt->offset[i] + qopt->count[i]; @@ -142,10 +147,11 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) struct tc_to_netdev tc = {.type = TC_SETUP_MQPRIO, { .tc = qopt->num_tc }}; - priv->hw_owned = 1; err = dev->netdev_ops->ndo_setup_tc(dev, sch->handle, 0, &tc); if (err) return err; + + priv->hw_offload = qopt->hw; } else { netdev_set_num_tc(dev, qopt->num_tc); for (i = 0; i < qopt->num_tc; i++) @@ -243,7 +249,7 @@ static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb) opt.num_tc = netdev_get_num_tc(dev); memcpy(opt.prio_tc_map, dev->prio_tc_map, sizeof(opt.prio_tc_map)); - opt.hw = priv->hw_owned; + opt.hw = priv->hw_offload; for (i = 0; i < netdev_get_num_tc(dev); i++) { opt.count[i] = dev->tc_to_txq[i].count; -- cgit From 56f36acd215cf7c28372b2fdb4f33f6900e97e05 Mon Sep 17 00:00:00 2001 From: Amritha Nambiar Date: Wed, 15 Mar 2017 10:39:25 -0700 Subject: mqprio: Modify mqprio to pass user parameters via ndo_setup_tc. The configurable priority to traffic class mapping and the user specified queue ranges are used to configure the traffic class, overriding the hardware defaults when the 'hw' option is set to 0. However, when the 'hw' option is non-zero, the hardware QOS defaults are used. This patch makes it so that we can pass the data the user provided to ndo_setup_tc. This allows us to pull in the queue configuration if the user requested it as well as any additional hardware offload type requested by using a value other than 1 for the hw value. Finally it also provides a means for the device driver to return the level supported for the offload type via the qopt->hw value. Previously we were just always assuming the value to be 1, in the future values beyond just 1 may be supported. Signed-off-by: Amritha Nambiar Signed-off-by: Alexander Duyck Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 3 ++- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 5 ++++- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 4 +++- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 16 ++++++++++------ drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 4 +++- drivers/net/ethernet/intel/i40e/i40e_main.c | 7 +++++-- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 +++- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 4 +++- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 +++- drivers/net/ethernet/sfc/falcon/tx.c | 4 +++- drivers/net/ethernet/sfc/tx.c | 4 +++- drivers/net/ethernet/ti/netcp_core.c | 12 ++++++++---- include/linux/netdevice.h | 2 +- net/sched/sch_mqprio.c | 17 +++++++++++------ 14 files changed, 62 insertions(+), 28 deletions(-) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index ffea9859f5a7..7ec2c9717cf1 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -1854,7 +1854,8 @@ static int xgbe_setup_tc(struct net_device *netdev, u32 handle, __be16 proto, if (tc_to_netdev->type != TC_SETUP_MQPRIO) return -EINVAL; - tc = tc_to_netdev->tc; + tc_to_netdev->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + tc = tc_to_netdev->mqprio->num_tc; if (tc > pdata->hw_feat.tc_cnt) return -EINVAL; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 9e8c06130c09..ad3e0631877e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4277,7 +4277,10 @@ int __bnx2x_setup_tc(struct net_device *dev, u32 handle, __be16 proto, { if (tc->type != TC_SETUP_MQPRIO) return -EINVAL; - return bnx2x_setup_tc(dev, tc->tc); + + tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + + return bnx2x_setup_tc(dev, tc->mqprio->num_tc); } /* called with rtnl_lock */ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 32de4589d16a..174ec8f84637 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -6905,7 +6905,9 @@ static int bnxt_setup_tc(struct net_device *dev, u32 handle, __be16 proto, if (ntc->type != TC_SETUP_MQPRIO) return -EINVAL; - return bnxt_setup_mq_tc(dev, ntc->tc); + ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + + return bnxt_setup_mq_tc(dev, ntc->mqprio->num_tc); } #ifdef CONFIG_RFS_ACCEL diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index aa769cbc7425..d4bb8bf86a45 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -346,33 +346,37 @@ static int dpaa_setup_tc(struct net_device *net_dev, u32 handle, __be16 proto, struct tc_to_netdev *tc) { struct dpaa_priv *priv = netdev_priv(net_dev); + u8 num_tc; int i; if (tc->type != TC_SETUP_MQPRIO) return -EINVAL; - if (tc->tc == priv->num_tc) + tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + num_tc = tc->mqprio->num_tc; + + if (num_tc == priv->num_tc) return 0; - if (!tc->tc) { + if (!num_tc) { netdev_reset_tc(net_dev); goto out; } - if (tc->tc > DPAA_TC_NUM) { + if (num_tc > DPAA_TC_NUM) { netdev_err(net_dev, "Too many traffic classes: max %d supported.\n", DPAA_TC_NUM); return -EINVAL; } - netdev_set_num_tc(net_dev, tc->tc); + netdev_set_num_tc(net_dev, num_tc); - for (i = 0; i < tc->tc; i++) + for (i = 0; i < num_tc; i++) netdev_set_tc_queue(net_dev, i, DPAA_TC_TXQ_NUM, i * DPAA_TC_TXQ_NUM); out: - priv->num_tc = tc->tc ? tc->tc : 1; + priv->num_tc = num_tc ? : 1; netif_set_real_num_tx_queues(net_dev, priv->num_tc * DPAA_TC_TXQ_NUM); return 0; } diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 01db688cf539..72481670478c 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1226,7 +1226,9 @@ static int __fm10k_setup_tc(struct net_device *dev, u32 handle, __be16 proto, if (tc->type != TC_SETUP_MQPRIO) return -EINVAL; - return fm10k_setup_tc(dev, tc->tc); + tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + + return fm10k_setup_tc(dev, tc->mqprio->num_tc); } static void fm10k_assign_l2_accel(struct fm10k_intfc *interface, diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 113b32911f1b..9df0d86812e7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5611,9 +5611,12 @@ static int __i40e_setup_tc(struct net_device *netdev, u32 handle, __be16 proto, struct tc_to_netdev *tc) #endif { - if (handle != TC_H_ROOT || tc->type != TC_SETUP_MQPRIO) + if (tc->type != TC_SETUP_MQPRIO) return -EINVAL; - return i40e_setup_tc(netdev, tc->tc); + + tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + + return i40e_setup_tc(netdev, tc->mqprio->num_tc); } /** diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index a7a430a7be2c..d45477db0227 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -8948,7 +8948,9 @@ static int __ixgbe_setup_tc(struct net_device *dev, u32 handle, __be16 proto, if (tc->type != TC_SETUP_MQPRIO) return -EINVAL; - return ixgbe_setup_tc(dev, tc->tc); + tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + + return ixgbe_setup_tc(dev, tc->mqprio->num_tc); } #ifdef CONFIG_PCI_IOV diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 61420473fe5f..94fab20ef146 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -92,7 +92,9 @@ static int __mlx4_en_setup_tc(struct net_device *dev, u32 handle, __be16 proto, if (tc->type != TC_SETUP_MQPRIO) return -EINVAL; - return mlx4_en_setup_tc(dev, tc->tc); + tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + + return mlx4_en_setup_tc(dev, tc->mqprio->num_tc); } #ifdef CONFIG_RFS_ACCEL diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 8ef64c4db2c2..f96a73ea8e0b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2737,7 +2737,9 @@ mqprio: if (tc->type != TC_SETUP_MQPRIO) return -EINVAL; - return mlx5e_setup_tc(dev, tc->tc); + tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + + return mlx5e_setup_tc(dev, tc->mqprio->num_tc); } static void diff --git a/drivers/net/ethernet/sfc/falcon/tx.c b/drivers/net/ethernet/sfc/falcon/tx.c index 104fb15a73f2..f6daf09b8627 100644 --- a/drivers/net/ethernet/sfc/falcon/tx.c +++ b/drivers/net/ethernet/sfc/falcon/tx.c @@ -437,11 +437,13 @@ int ef4_setup_tc(struct net_device *net_dev, u32 handle, __be16 proto, if (ntc->type != TC_SETUP_MQPRIO) return -EINVAL; - num_tc = ntc->tc; + num_tc = ntc->mqprio->num_tc; if (ef4_nic_rev(efx) < EF4_REV_FALCON_B0 || num_tc > EF4_MAX_TX_TC) return -EINVAL; + ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + if (num_tc == net_dev->num_tc) return 0; diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index ff88d60aa6d5..3bdf87f31087 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -665,11 +665,13 @@ int efx_setup_tc(struct net_device *net_dev, u32 handle, __be16 proto, if (ntc->type != TC_SETUP_MQPRIO) return -EINVAL; - num_tc = ntc->tc; + num_tc = ntc->mqprio->num_tc; if (num_tc > EFX_MAX_TX_TC) return -EINVAL; + ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + if (num_tc == net_dev->num_tc) return 0; diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 7c7ae0890e90..9027c9c509b5 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1882,6 +1882,7 @@ static u16 netcp_select_queue(struct net_device *dev, struct sk_buff *skb, static int netcp_setup_tc(struct net_device *dev, u32 handle, __be16 proto, struct tc_to_netdev *tc) { + u8 num_tc; int i; /* setup tc must be called under rtnl lock */ @@ -1890,15 +1891,18 @@ static int netcp_setup_tc(struct net_device *dev, u32 handle, __be16 proto, if (tc->type != TC_SETUP_MQPRIO) return -EINVAL; + tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + num_tc = tc->mqprio->num_tc; + /* Sanity-check the number of traffic classes requested */ if ((dev->real_num_tx_queues <= 1) || - (dev->real_num_tx_queues < tc->tc)) + (dev->real_num_tx_queues < num_tc)) return -EINVAL; /* Configure traffic class to queue mappings */ - if (tc->tc) { - netdev_set_num_tc(dev, tc->tc); - for (i = 0; i < tc->tc; i++) + if (num_tc) { + netdev_set_num_tc(dev, num_tc); + for (i = 0; i < num_tc; i++) netdev_set_tc_queue(dev, i, 1, i); } else { netdev_reset_tc(dev); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 97456b2539e4..b7365b587818 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -786,11 +786,11 @@ struct tc_cls_u32_offload; struct tc_to_netdev { unsigned int type; union { - u8 tc; struct tc_cls_u32_offload *cls_u32; struct tc_cls_flower_offload *cls_flower; struct tc_cls_matchall_offload *cls_mall; struct tc_cls_bpf_offload *cls_bpf; + struct tc_mqprio_qopt *mqprio; }; bool egress_dev; }; diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 5f55bf149d9f..0a4cf27ea54b 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -28,7 +28,6 @@ static void mqprio_destroy(struct Qdisc *sch) { struct net_device *dev = qdisc_dev(sch); struct mqprio_sched *priv = qdisc_priv(sch); - struct tc_to_netdev tc = {.type = TC_SETUP_MQPRIO}; unsigned int ntx; if (priv->qdiscs) { @@ -39,10 +38,15 @@ static void mqprio_destroy(struct Qdisc *sch) kfree(priv->qdiscs); } - if (priv->hw_offload && dev->netdev_ops->ndo_setup_tc) + if (priv->hw_offload && dev->netdev_ops->ndo_setup_tc) { + struct tc_mqprio_qopt offload = { 0 }; + struct tc_to_netdev tc = { .type = TC_SETUP_MQPRIO, + { .mqprio = &offload } }; + dev->netdev_ops->ndo_setup_tc(dev, sch->handle, 0, &tc); - else + } else { netdev_set_num_tc(dev, 0); + } } static int mqprio_parse_opt(struct net_device *dev, struct tc_mqprio_qopt *qopt) @@ -144,14 +148,15 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) * supplied and verified mapping */ if (qopt->hw) { - struct tc_to_netdev tc = {.type = TC_SETUP_MQPRIO, - { .tc = qopt->num_tc }}; + struct tc_mqprio_qopt offload = *qopt; + struct tc_to_netdev tc = { .type = TC_SETUP_MQPRIO, + { .mqprio = &offload } }; err = dev->netdev_ops->ndo_setup_tc(dev, sch->handle, 0, &tc); if (err) return err; - priv->hw_offload = qopt->hw; + priv->hw_offload = offload.hw; } else { netdev_set_num_tc(dev, qopt->num_tc); for (i = 0; i < qopt->num_tc; i++) -- cgit From e893de1ba1d39e78750ffadf7191aef8133c8fe8 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Wed, 15 Mar 2017 15:53:48 -0400 Subject: net: dsa: dsa_fastest_ageing_time return unsigned The ageing time is defined as unsigned int, so make dsa_fastest_ageing_time return an unsigned int instead of int. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/slave.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index c34872e1febc..cec47e843570 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -419,8 +419,8 @@ static int dsa_slave_vlan_filtering(struct net_device *dev, return 0; } -static int dsa_fastest_ageing_time(struct dsa_switch *ds, - unsigned int ageing_time) +static unsigned int dsa_fastest_ageing_time(struct dsa_switch *ds, + unsigned int ageing_time) { int i; -- cgit From 0f3da6afeef1f63c7254965253f7e6535dc7910c Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Wed, 15 Mar 2017 15:53:49 -0400 Subject: net: dsa: check out-of-range ageing time value If a DSA switch driver cannot program an ageing time value due to it being out-of-range, switchdev will raise a stack trace before failing. To fix this, add ageing_time_min and ageing_time_max members to the dsa_switch in order for the switch drivers to optionally specify their supported ageing time limits. The DSA core will now check for provided ageing time limits and return -ERANGE from the switchdev prepare phase if the value is out-of-range. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 4 ++++ net/dsa/slave.c | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index bf0e42c2a6f7..e42897fd7a96 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -233,6 +233,10 @@ struct dsa_switch { u32 phys_mii_mask; struct mii_bus *slave_mii_bus; + /* Ageing Time limits in msecs */ + unsigned int ageing_time_min; + unsigned int ageing_time_max; + /* Dynamically allocated ports, keep last */ size_t num_ports; struct dsa_port ports[]; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index cec47e843570..78128acfbf63 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -443,9 +443,13 @@ static int dsa_slave_ageing_time(struct net_device *dev, unsigned long ageing_jiffies = clock_t_to_jiffies(attr->u.ageing_time); unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies); - /* bridge skips -EOPNOTSUPP, so skip the prepare phase */ - if (switchdev_trans_ph_prepare(trans)) + if (switchdev_trans_ph_prepare(trans)) { + if (ds->ageing_time_min && ageing_time < ds->ageing_time_min) + return -ERANGE; + if (ds->ageing_time_max && ageing_time > ds->ageing_time_max) + return -ERANGE; return 0; + } /* Keep the fastest ageing time in case of multiple bridges */ p->dp->ageing_time = ageing_time; -- cgit From 9ff74f249db8c80f13568460e26d168f5c6d55cd Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Wed, 15 Mar 2017 15:53:50 -0400 Subject: net: dsa: mv88e6xxx: specify ageing time limits Now that DSA has ageing time limits, specify them when registering a switch so that out-of-range values are handled correctly by the core. Signed-off-by: Vivien Didelot Reported-by: Jason Cobham Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 3354f99df378..2bca297d9296 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -4253,6 +4253,8 @@ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) ds->priv = chip; ds->ops = &mv88e6xxx_switch_ops; + ds->ageing_time_min = chip->info->age_time_coeff; + ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX; dev_set_drvdata(dev, ds); -- cgit From 4b72436dc3dd2457056b22d6f147777368c869fa Mon Sep 17 00:00:00 2001 From: Quan Nguyen Date: Wed, 15 Mar 2017 13:27:15 -0700 Subject: drivers: net: phy: xgene: Fix mdio write This patches fixes a typo in the argument to xgene_enet_wr_mdio_csr(). Signed-off-by: Quan Nguyen Signed-off-by: Iyappan Subramanian Signed-off-by: David S. Miller --- drivers/net/phy/mdio-xgene.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/mdio-xgene.c b/drivers/net/phy/mdio-xgene.c index f095051beb54..3e2ac07b6e37 100644 --- a/drivers/net/phy/mdio-xgene.c +++ b/drivers/net/phy/mdio-xgene.c @@ -229,7 +229,7 @@ static int xgene_xfi_mdio_write(struct mii_bus *bus, int phy_id, val = SET_VAL(HSTPHYADX, phy_id) | SET_VAL(HSTREGADX, reg) | SET_VAL(HSTMIIMWRDAT, data); - xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, data); + xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, val); val = HSTLDCMD | SET_VAL(HSTMIIMCMD, MIIM_CMD_LEGACY_WRITE); xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, val); -- cgit From e026e700d940a1ea3d3bc84d92ac668b1f015462 Mon Sep 17 00:00:00 2001 From: Quan Nguyen Date: Wed, 15 Mar 2017 13:27:16 -0700 Subject: drivers: net: xgene: Fix hardware checksum setting This patch fixes the hardware checksum settings by properly program the classifier. Otherwise, packet may be received with checksum error on X-Gene1 SoC. Signed-off-by: Quan Nguyen Signed-off-by: Iyappan Subramanian Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 1 + drivers/net/ethernet/apm/xgene/xgene_enet_hw.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index 06e681697c17..c72a17e98f13 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -623,6 +623,7 @@ static void xgene_enet_cle_bypass(struct xgene_enet_pdata *pdata, xgene_enet_rd_csr(pdata, CLE_BYPASS_REG0_0_ADDR, &cb); cb |= CFG_CLE_BYPASS_EN0; CFG_CLE_IP_PROTOCOL0_SET(&cb, 3); + CFG_CLE_IP_HDR_LEN_SET(&cb, 0); xgene_enet_wr_csr(pdata, CLE_BYPASS_REG0_0_ADDR, cb); xgene_enet_rd_csr(pdata, CLE_BYPASS_REG1_0_ADDR, &cb); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h index 5f83037bb96b..b6cd625fad84 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h @@ -163,6 +163,7 @@ enum xgene_enet_rm { #define CFG_RXCLK_MUXSEL0_SET(dst, val) xgene_set_bits(dst, val, 26, 3) #define CFG_CLE_IP_PROTOCOL0_SET(dst, val) xgene_set_bits(dst, val, 16, 2) +#define CFG_CLE_IP_HDR_LEN_SET(dst, val) xgene_set_bits(dst, val, 8, 5) #define CFG_CLE_DSTQID0_SET(dst, val) xgene_set_bits(dst, val, 0, 12) #define CFG_CLE_FPSEL0_SET(dst, val) xgene_set_bits(dst, val, 16, 4) #define CFG_CLE_NXTFPSEL0_SET(dst, val) xgene_set_bits(dst, val, 20, 4) -- cgit From 11623fce0f9afef30c45e3f2120b063de3809a8f Mon Sep 17 00:00:00 2001 From: Quan Nguyen Date: Wed, 15 Mar 2017 13:27:17 -0700 Subject: drivers: net: xgene: Fix wrong logical operation This patch fixes the wrong logical OR operation by changing it to bit-wise OR operation. Fixes: 3bb502f83080 ("drivers: net: xgene: fix statistics counters race condition") Signed-off-by: Iyappan Subramanian Signed-off-by: Quan Nguyen Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index b3568c453b14..ec43278d4b93 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -677,9 +677,9 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, buf_pool->rx_skb[skb_index] = NULL; /* checking for error */ - status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) || + status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) | GET_VAL(LERR, le64_to_cpu(raw_desc->m0)); - if (unlikely(status > 2)) { + if (unlikely(status)) { dev_kfree_skb_any(skb); xgene_enet_free_pagepool(page_pool, raw_desc, exp_desc); xgene_enet_parse_error(rx_ring, netdev_priv(rx_ring->ndev), -- cgit From 0a0400c3094b5d5cedd479ddbf1329de74c09c4b Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Wed, 15 Mar 2017 13:27:18 -0700 Subject: drivers: net: xgene: Fix Rx checksum validation logic This patch fixes Rx checksum validation logic and adds NETIF_F_RXCSUM flag. Signed-off-by: Iyappan Subramanian Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 27 +++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index ec43278d4b93..e881365160e4 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -601,14 +601,24 @@ static netdev_tx_t xgene_enet_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } -static void xgene_enet_skip_csum(struct sk_buff *skb) +static void xgene_enet_rx_csum(struct sk_buff *skb) { + struct net_device *ndev = skb->dev; struct iphdr *iph = ip_hdr(skb); - if (!ip_is_fragment(iph) || - (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP)) { - skb->ip_summed = CHECKSUM_UNNECESSARY; - } + if (!(ndev->features & NETIF_F_RXCSUM)) + return; + + if (skb->protocol != htons(ETH_P_IP)) + return; + + if (ip_is_fragment(iph)) + return; + + if (iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_UDP) + return; + + skb->ip_summed = CHECKSUM_UNNECESSARY; } static void xgene_enet_free_pagepool(struct xgene_enet_desc_ring *buf_pool, @@ -729,10 +739,7 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, skip_jumbo: skb_checksum_none_assert(skb); skb->protocol = eth_type_trans(skb, ndev); - if (likely((ndev->features & NETIF_F_IP_CSUM) && - skb->protocol == htons(ETH_P_IP))) { - xgene_enet_skip_csum(skb); - } + xgene_enet_rx_csum(skb); rx_ring->rx_packets++; rx_ring->rx_bytes += datalen; @@ -2039,7 +2046,7 @@ static int xgene_enet_probe(struct platform_device *pdev) xgene_enet_setup_ops(pdata); if (pdata->phy_mode == PHY_INTERFACE_MODE_XGMII) { - ndev->features |= NETIF_F_TSO; + ndev->features |= NETIF_F_TSO | NETIF_F_RXCSUM; spin_lock_init(&pdata->mss_lock); } ndev->hw_features = ndev->features; -- cgit From 7eac928c19d7cb2dee9339a321c3f1abbb81358d Mon Sep 17 00:00:00 2001 From: Quan Nguyen Date: Wed, 15 Mar 2017 13:27:19 -0700 Subject: drivers: net: xgene: Add workaround for errata 10GE_1 This patch implements workaround for errata 10GE_1: 10Gb Ethernet port FIFO threshold default values are incorrect. Signed-off-by: Quan Nguyen Signed-off-by: Toan Le Signed-off-by: Iyappan Subramanian Tested-by: Fushen Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c | 7 +++++++ drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c index ece19e6d68e3..423240c97d39 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c @@ -341,8 +341,15 @@ static void xgene_xgmac_init(struct xgene_enet_pdata *pdata) xgene_enet_rd_csr(pdata, XG_RSIF_CONFIG_REG_ADDR, &data); data |= CFG_RSIF_FPBUFF_TIMEOUT_EN; + /* Errata 10GE_1 - FIFO threshold default value incorrect */ + RSIF_CLE_BUFF_THRESH_SET(&data, XG_RSIF_CLE_BUFF_THRESH); xgene_enet_wr_csr(pdata, XG_RSIF_CONFIG_REG_ADDR, data); + /* Errata 10GE_1 - FIFO threshold default value incorrect */ + xgene_enet_rd_csr(pdata, XG_RSIF_CONFIG1_REG_ADDR, &data); + RSIF_PLC_CLE_BUFF_THRESH_SET(&data, XG_RSIF_PLC_CLE_BUFF_THRESH); + xgene_enet_wr_csr(pdata, XG_RSIF_CONFIG1_REG_ADDR, data); + xgene_enet_rd_csr(pdata, XG_ENET_SPARE_CFG_REG_ADDR, &data); data |= BIT(12); xgene_enet_wr_csr(pdata, XG_ENET_SPARE_CFG_REG_ADDR, data); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h index 03b847ad8937..e644a429ebf4 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.h @@ -65,6 +65,11 @@ #define XG_DEF_PAUSE_THRES 0x390 #define XG_DEF_PAUSE_OFF_THRES 0x2c0 #define XG_RSIF_CONFIG_REG_ADDR 0x00a0 +#define XG_RSIF_CLE_BUFF_THRESH 0x3 +#define RSIF_CLE_BUFF_THRESH_SET(dst, val) xgene_set_bits(dst, val, 0, 3) +#define XG_RSIF_CONFIG1_REG_ADDR 0x00b8 +#define XG_RSIF_PLC_CLE_BUFF_THRESH 0x1 +#define RSIF_PLC_CLE_BUFF_THRESH_SET(dst, val) xgene_set_bits(dst, val, 0, 2) #define XCLE_BYPASS_REG0_ADDR 0x0160 #define XCLE_BYPASS_REG1_ADDR 0x0164 #define XG_CFG_BYPASS_ADDR 0x0204 -- cgit From 4902a92270fb2abec57a215d237a74f5ca16c9c7 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Wed, 15 Mar 2017 13:27:20 -0700 Subject: drivers: net: xgene: Add workaround for errata 10GE_8/ENET_11 This patch implements workaround for errata 10GE_8 and ENET_11: "HW reports length error for valid 64 byte frames with len <46 bytes" by recovering them from error. Signed-off-by: Iyappan Subramanian Signed-off-by: Quan Nguyen Signed-off-by: Toan Le Tested-by: Fushen Chen Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 2 +- drivers/net/ethernet/apm/xgene/xgene_enet_hw.h | 1 + drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 43 +++++++++++++++--------- drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 1 + 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index c72a17e98f13..2a835e07adfb 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -494,7 +494,7 @@ static void xgene_gmac_set_speed(struct xgene_enet_pdata *pdata) break; } - mc2 |= FULL_DUPLEX2 | PAD_CRC; + mc2 |= FULL_DUPLEX2 | PAD_CRC | LENGTH_CHK; xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_2_ADDR, mc2); xgene_enet_wr_mcx_mac(pdata, INTERFACE_CONTROL_ADDR, intf_ctl); xgene_enet_wr_csr(pdata, RGMII_REG_0_ADDR, rgmii); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h index b6cd625fad84..d250bfe94d24 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h @@ -216,6 +216,7 @@ enum xgene_enet_rm { #define ENET_GHD_MODE BIT(26) #define FULL_DUPLEX2 BIT(0) #define PAD_CRC BIT(2) +#define LENGTH_CHK BIT(4) #define SCAN_AUTO_INCR BIT(5) #define TBYT_ADDR 0x38 #define TPKT_ADDR 0x39 diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index e881365160e4..5f37ed3506d5 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -658,12 +658,24 @@ static void xgene_enet_free_pagepool(struct xgene_enet_desc_ring *buf_pool, buf_pool->head = head; } +/* Errata 10GE_8 and ENET_11 - allow packet with length <=64B */ +static bool xgene_enet_errata_10GE_8(struct sk_buff *skb, u32 len, u8 status) +{ + if (status == INGRESS_PKT_LEN && len == ETHER_MIN_PACKET) { + if (ntohs(eth_hdr(skb)->h_proto) < 46) + return true; + } + + return false; +} + static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, struct xgene_enet_raw_desc *raw_desc, struct xgene_enet_raw_desc *exp_desc) { struct xgene_enet_desc_ring *buf_pool, *page_pool; u32 datalen, frag_size, skb_index; + struct xgene_enet_pdata *pdata; struct net_device *ndev; dma_addr_t dma_addr; struct sk_buff *skb; @@ -676,6 +688,7 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, bool nv; ndev = rx_ring->ndev; + pdata = netdev_priv(ndev); dev = ndev_to_dev(rx_ring->ndev); buf_pool = rx_ring->buf_pool; page_pool = rx_ring->page_pool; @@ -686,30 +699,29 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, skb = buf_pool->rx_skb[skb_index]; buf_pool->rx_skb[skb_index] = NULL; + datalen = xgene_enet_get_data_len(le64_to_cpu(raw_desc->m1)); + skb_put(skb, datalen); + prefetch(skb->data - NET_IP_ALIGN); + skb->protocol = eth_type_trans(skb, ndev); + /* checking for error */ status = (GET_VAL(ELERR, le64_to_cpu(raw_desc->m0)) << LERR_LEN) | GET_VAL(LERR, le64_to_cpu(raw_desc->m0)); if (unlikely(status)) { - dev_kfree_skb_any(skb); - xgene_enet_free_pagepool(page_pool, raw_desc, exp_desc); - xgene_enet_parse_error(rx_ring, netdev_priv(rx_ring->ndev), - status); - ret = -EIO; - goto out; + if (!xgene_enet_errata_10GE_8(skb, datalen, status)) { + dev_kfree_skb_any(skb); + xgene_enet_free_pagepool(page_pool, raw_desc, exp_desc); + xgene_enet_parse_error(rx_ring, pdata, status); + goto out; + } } - /* strip off CRC as HW isn't doing this */ - datalen = xgene_enet_get_data_len(le64_to_cpu(raw_desc->m1)); - nv = GET_VAL(NV, le64_to_cpu(raw_desc->m0)); - if (!nv) + if (!nv) { + /* strip off CRC as HW isn't doing this */ datalen -= 4; - - skb_put(skb, datalen); - prefetch(skb->data - NET_IP_ALIGN); - - if (!nv) goto skip_jumbo; + } slots = page_pool->slots - 1; head = page_pool->head; @@ -738,7 +750,6 @@ static int xgene_enet_rx_frame(struct xgene_enet_desc_ring *rx_ring, skip_jumbo: skb_checksum_none_assert(skb); - skb->protocol = eth_type_trans(skb, ndev); xgene_enet_rx_csum(skb); rx_ring->rx_packets++; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h index 52571741da9f..0d4be2425ebc 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -41,6 +41,7 @@ #include "../../../phy/mdio-xgene.h" #define XGENE_DRV_VERSION "v1.0" +#define ETHER_MIN_PACKET 64 #define XGENE_ENET_STD_MTU 1536 #define XGENE_ENET_MAX_MTU 9600 #define SKB_BUFFER_SIZE (XGENE_ENET_STD_MTU - NET_IP_ALIGN) -- cgit From b3fd38d27320c2bf2dfe5de495ffa57f366d0bce Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Wed, 15 Mar 2017 13:27:21 -0700 Subject: MAINTAINERS: Update X-Gene SoC ethernet maintainer Signed-off-by: Iyappan Subramanian Signed-off-by: Keyur Chudgar Signed-off-by: Quan Nguyen Signed-off-by: David S. Miller --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index cefda30ed704..632e76223cc5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -896,6 +896,7 @@ F: arch/arm64/boot/dts/apm/ APPLIED MICRO (APM) X-GENE SOC ETHERNET DRIVER M: Iyappan Subramanian M: Keyur Chudgar +M: Quan Nguyen S: Supported F: drivers/net/ethernet/apm/xgene/ F: drivers/net/phy/mdio-xgene.c -- cgit From ca07baab0b1e627ae1d4a55d190fb1c9d32a3445 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 22 Feb 2017 21:03:11 +0530 Subject: ath10k: disallow DFS simulation if DFS channel is not enabled If DFS is not enabled in hostapd (ieee80211h=0) DFS channels shall not be available for use even though the hardware may have the capability to support DFS. With this configuration (DFS disabled in hostapd) trying to bring up ath10k device in DFS channel for AP mode fails and trying to simulate DFS in ath10k debugfs results in a warning in cfg80211 complaining invalid channel and this should be avoided in the driver itself rather than false propogating RADAR detection to mac80211/cfg80211. Fix this by checking for the first vif 'is_started' state(should work for client mode as well) as all the vifs shall be configured for the same channel sys/kernel/debug/ieee80211/phy1/ath10k# echo 1 > dfs_simulate_radar WARNING: at net/wireless/chan.c:265 cfg80211_radar_event+0x24/0x60 Workqueue: phy0 ieee80211_dfs_radar_detected_work [mac80211] [] (warn_slowpath_null) from [] (cfg80211_radar_event+0x24/0x60 [cfg80211]) [] (cfg80211_radar_event [cfg80211]) from [] (ieee80211_dfs_radar_detected_work+0x94/0xa0 [mac80211]) [] (ieee80211_dfs_radar_detected_work [mac80211]) from [] (process_one_work+0x20c/0x32c) WARNING: at net/wireless/nl80211.c:2488 nl80211_get_mpath+0x13c/0x4cc Workqueue: phy0 ieee80211_dfs_radar_detected_work [mac80211] [] (warn_slowpath_null) from [] (cfg80211_radar_event+0x24/0x60 [cfg80211]) [] (cfg80211_radar_event [cfg80211]) from [] (ieee80211_dfs_radar_detected_work+0x94/0xa0 [mac80211]) [] (ieee80211_dfs_radar_detected_work [mac80211]) from [] (process_one_work+0x20c/0x32c) Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index fb0ade3adb07..ac9090b752fc 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1997,6 +1997,15 @@ static ssize_t ath10k_write_simulate_radar(struct file *file, size_t count, loff_t *ppos) { struct ath10k *ar = file->private_data; + struct ath10k_vif *arvif; + + /* Just check for for the first vif alone, as all the vifs will be + * sharing the same channel and if the channel is disabled, all the + * vifs will share the same 'is_started' state. + */ + arvif = list_first_entry(&ar->arvifs, typeof(*arvif), list); + if (!arvif->is_started) + return -EINVAL; ieee80211_radar_detected(ar->hw); -- cgit From 523f6701dbabbe63b35d16bb676d35212b22e204 Mon Sep 17 00:00:00 2001 From: Tamizh chelvam Date: Thu, 23 Feb 2017 18:48:22 +0530 Subject: ath10k: update available channel list for 5G radio If a 5 GHz radio is calibrated for operation in both the low band (channels 36 to 64) and high band(channels 100 to 169), hardware allows operations in all the listed channels. However, if the chip has been calibrated only for the low/high band and a high/low band channel is configured, due to lack of calibration there will be potentially invalid signal on those non calibrated channels. To avoid this problem this patch sets IEEE80211_CHAN_DISABLED flag for those non calibrated channels by using low_5ghz_chan and high_5ghz_chan values which we get from target through wmi service ready event. Driver initialized flags are getting re initialized in handle_channel in cfg80211. So calling the function to disable the non supported channel from reg_notifier(). Signed-off-by: Tamizh chelvam Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 2 ++ drivers/net/wireless/ath/ath10k/mac.c | 19 +++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.c | 6 ++++++ drivers/net/wireless/ath/ath10k/wmi.h | 2 ++ 4 files changed, 29 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 88d14be7fcce..d4b9a0ec1bdc 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -775,6 +775,8 @@ struct ath10k { u32 num_rf_chains; u32 max_spatial_stream; /* protected by conf_mutex */ + u32 low_5ghz_chan; + u32 high_5ghz_chan; bool ani_enabled; bool p2p; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index f0e46804ad82..167bde5cbbad 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3126,6 +3126,21 @@ static void ath10k_regd_update(struct ath10k *ar) ath10k_warn(ar, "failed to set pdev regdomain: %d\n", ret); } +void ath10k_mac_update_channel_list(struct ath10k *ar, + struct ieee80211_supported_band *band) +{ + int i; + + if (ar->low_5ghz_chan && ar->high_5ghz_chan) { + for (i = 0; i < band->n_channels; i++) { + if (band->channels[i].center_freq < ar->low_5ghz_chan || + band->channels[i].center_freq > ar->high_5ghz_chan) + band->channels[i].flags |= + IEEE80211_CHAN_DISABLED; + } + } +} + static void ath10k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { @@ -3149,6 +3164,10 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, if (ar->state == ATH10K_STATE_ON) ath10k_regd_update(ar); mutex_unlock(&ar->conf_mutex); + + if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) + ath10k_mac_update_channel_list(ar, + ar->hw->wiphy->bands[NL80211_BAND_5GHZ]); } /***************/ diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 2f1743e60fa1..b31c4f71b413 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -4593,6 +4593,8 @@ ath10k_wmi_main_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb, arg->phy_capab = ev->phy_capability; arg->num_rf_chains = ev->num_rf_chains; arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd; + arg->low_5ghz_chan = ev->hal_reg_capabilities.low_5ghz_chan; + arg->high_5ghz_chan = ev->hal_reg_capabilities.high_5ghz_chan; arg->num_mem_reqs = ev->num_mem_reqs; arg->service_map = ev->wmi_service_bitmap; arg->service_map_len = sizeof(ev->wmi_service_bitmap); @@ -4629,6 +4631,8 @@ ath10k_wmi_10x_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb, arg->phy_capab = ev->phy_capability; arg->num_rf_chains = ev->num_rf_chains; arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd; + arg->low_5ghz_chan = ev->hal_reg_capabilities.low_5ghz_chan; + arg->high_5ghz_chan = ev->hal_reg_capabilities.high_5ghz_chan; arg->num_mem_reqs = ev->num_mem_reqs; arg->service_map = ev->wmi_service_bitmap; arg->service_map_len = sizeof(ev->wmi_service_bitmap); @@ -4682,6 +4686,8 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) ar->phy_capability = __le32_to_cpu(arg.phy_capab); ar->num_rf_chains = __le32_to_cpu(arg.num_rf_chains); ar->hw_eeprom_rd = __le32_to_cpu(arg.eeprom_rd); + ar->low_5ghz_chan = __le32_to_cpu(arg.low_5ghz_chan); + ar->high_5ghz_chan = __le32_to_cpu(arg.high_5ghz_chan); ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ", arg.service_map, arg.service_map_len); diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 386aa51435f1..78350d4c7b79 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -6335,6 +6335,8 @@ struct wmi_svc_rdy_ev_arg { __le32 num_rf_chains; __le32 eeprom_rd; __le32 num_mem_reqs; + __le32 low_5ghz_chan; + __le32 high_5ghz_chan; const __le32 *service_map; size_t service_map_len; const struct wlan_host_mem_req *mem_reqs[WMI_MAX_MEM_REQS]; -- cgit From 45c3d550b0684343b9d4b25aeec9b49c6effc1f9 Mon Sep 17 00:00:00 2001 From: Zefir Kurtisi Date: Mon, 27 Feb 2017 15:49:36 +0100 Subject: ath9k: don't trigger spectral scan when not enabled Doing so enables the FFT generation without prior configuration, leading to an IRQ storm caused by invalid (or at least unwanted) PHY errors. Signed-off-by: Zefir Kurtisi Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/common-spectral.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index 0ffa23a61568..5e77fe1f5b0d 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -742,6 +742,9 @@ void ath9k_cmn_spectral_scan_trigger(struct ath_common *common, return; } + if (!spec_priv->spec_config.enabled) + return; + ath_ps_ops(common)->wakeup(common); rxfilter = ath9k_hw_getrxfilter(ah); ath9k_hw_setrxfilter(ah, rxfilter | -- cgit From 51f60b8b6979da361c97602d6b6df3c3f72c7c4c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 28 Feb 2017 11:14:39 +0000 Subject: ath10k: remove redundant error check The check on ret for an error is redundant because it is already been checked for non-zero earlier on and ret is never non-zero at this point. Fix this by removing the redundant check and error message. Detected by CoverityScan, CID#1357170 ("Logically Dead Code") Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 6094372307aa..52896c20ca4e 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -970,12 +970,6 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, } remaining_bytes -= nbytes; - - if (ret) { - ath10k_warn(ar, "failed to read diag value at 0x%x: %d\n", - address, ret); - break; - } memcpy(data, data_buf, nbytes); address += nbytes; -- cgit From a28f6f27a88f047f03f04b9246ca260ebc91455e Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 8 Mar 2017 18:03:32 +0530 Subject: ath10k: fix fetching channel during potential radar detection Fetch target operating channel during potential radar detection when the interface is just brought up, but no channel is assigned from userspace. In this scenario rx_channel may not be having a valid pointer hence fetch the target operating channel to avoid warnings as below which can be triggered by the commands with DFS testing over longer run comamnds: iw wlan1 set type mesh ifconfig wlan1 up (valid tgt_oper_chan only) iw wlan1 cac trigger freq 5260 HT20 (valid rx_channel, tgt_oper_chan) iw wlan1 cac trigger freq 5280 HT20 iw wlan1 cac trigger freq 5300 HT20 Once the CAC expires, current channel context will be removed and we are only left with the fallback option of using 'target operating channel' Firmware and driver log: ath: phy1: DFS: radar found on freq=5300: id=1, pri=1125, count=5, count_false=4 ath: phy1: DFS: radar found on freq=5260: id=5, pri=3151, count=6, count_false=11 ath: phy1: DFS: radar found on freq=5280: id=1, pri=1351, count=6, count_false=4 ath: phy1: DFS: radar found on freq=5300: id=1, pri=1125, count=5, count_false=4 ath10k_pci 0001:01:00.0: failed to derive channel for radar pulse, treating as radar ath10k_pci 0001:01:00.0: failed to derive channel for radar pulse, treating as radar Call trace: WARNING: CPU: 1 PID: 2145 at backports-20161201-3.14.77-9ab3068/net/wireless/chan.c:265 cfg80211_set_dfs_state+0x3c/0x88 [cfg80211]() Workqueue: phy1 ieee80211_dfs_radar_detected_work [mac80211] [] (warn_slowpath_null) from [] (cfg80211_set_dfs_state+0x3c/0x88 [cfg80211]) [] (cfg80211_set_dfs_state [cfg80211]) from [] (cfg80211_radar_event+0xc4/0x140 [cfg80211]) [] (cfg80211_radar_event [cfg80211]) from [] (ieee80211_dfs_radar_detected_work+0xa8/0xb4 [mac80211]) [] (ieee80211_dfs_radar_detected_work [mac80211]) from [] (process_one_work+0x298/0x4a4) Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index b31c4f71b413..4e60caec7ab4 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3643,6 +3643,11 @@ static void ath10k_dfs_radar_report(struct ath10k *ar, spin_lock_bh(&ar->data_lock); ch = ar->rx_channel; + + /* fetch target operating channel during channel change */ + if (!ch) + ch = ar->tgt_oper_chan; + spin_unlock_bh(&ar->data_lock); if (!ch) { -- cgit From 5c19dfbe964f4bbb38c1868b851adf4855fc93ff Mon Sep 17 00:00:00 2001 From: Ondřej Lysoněk Date: Thu, 9 Mar 2017 10:34:36 +0100 Subject: mac80211: Use setup_timer instead of init_timer for mesh path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use setup_timer() and setup_deferrable_timer() to set the data and function timer fields. It makes the code cleaner and will allow for easier change of the timer struct internals. Signed-off-by: Ondřej Lysoněk Cc: Jiri Slaby Cc: Johannes Berg Cc: "David S. Miller" Cc: Cc: Signed-off-by: Johannes Berg --- net/mac80211/mesh_pathtbl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 98a3b1c0c338..97269caafecd 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -397,11 +397,10 @@ struct mesh_path *mesh_path_new(struct ieee80211_sub_if_data *sdata, new_mpath->sdata = sdata; new_mpath->flags = 0; skb_queue_head_init(&new_mpath->frame_queue); - new_mpath->timer.data = (unsigned long) new_mpath; - new_mpath->timer.function = mesh_path_timer; new_mpath->exp_time = jiffies; spin_lock_init(&new_mpath->state_lock); - init_timer(&new_mpath->timer); + setup_timer(&new_mpath->timer, mesh_path_timer, + (unsigned long) new_mpath); return new_mpath; } -- cgit From 335d534938d327d5ba266564b7e6beaf5d456dd2 Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Thu, 16 Mar 2017 10:57:17 +0900 Subject: nl80211: Use signed function for a signed variable The rssi_threshold is defined as s32. Signed-off-by: Masashi Honma Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b15903b9c0ab..bd5959fd29c5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5714,7 +5714,7 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, cur_params.dot11MeshGateAnnouncementProtocol) || nla_put_u8(msg, NL80211_MESHCONF_FORWARDING, cur_params.dot11MeshForwarding) || - nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD, + nla_put_s32(msg, NL80211_MESHCONF_RSSI_THRESHOLD, cur_params.rssi_threshold) || nla_put_u32(msg, NL80211_MESHCONF_HT_OPMODE, cur_params.ht_opmode) || -- cgit From 864e91ca981ea1d18f05a8f5063520d103373266 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 9 Dec 2016 21:09:59 -0800 Subject: ipvs: remove an annoying printk in netns init At most it is used for debugging purpose, but I don't think it is even useful for debugging, just remove it. Signed-off-by: Cong Wang Signed-off-by: Simon Horman --- net/netfilter/ipvs/ip_vs_core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index db40050f8785..9aaa49025cdc 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -2231,8 +2231,6 @@ static int __net_init __ip_vs_init(struct net *net) if (ip_vs_sync_net_init(ipvs) < 0) goto sync_fail; - printk(KERN_INFO "IPVS: Creating netns size=%zu id=%d\n", - sizeof(struct netns_ipvs), ipvs->gen); return 0; /* * Error handling -- cgit From a2f346d82bcab1927a199f475dc299c00413ec39 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Mon, 20 Feb 2017 16:31:35 +0800 Subject: ipvs: fix sync_threshold description and add sync_refresh_period, sync_retries Fix sync_threshold description which should have two values. Also add sync_refresh_period and sync_retries based on commit 749c42b620a9 ("ipvs: reduce sync rate with time thresholds"). Signed-off-by: Hangbin Liu Signed-off-by: Simon Horman --- Documentation/networking/ipvs-sysctl.txt | 40 +++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/Documentation/networking/ipvs-sysctl.txt b/Documentation/networking/ipvs-sysctl.txt index e6b1c025fdd8..7acaaa65451e 100644 --- a/Documentation/networking/ipvs-sysctl.txt +++ b/Documentation/networking/ipvs-sysctl.txt @@ -185,15 +185,37 @@ secure_tcp - INTEGER The value definition is the same as that of drop_entry and drop_packet. -sync_threshold - INTEGER - default 3 - - It sets synchronization threshold, which is the minimum number - of incoming packets that a connection needs to receive before - the connection will be synchronized. A connection will be - synchronized, every time the number of its incoming packets - modulus 50 equals the threshold. The range of the threshold is - from 0 to 49. +sync_threshold - vector of 2 INTEGERs: sync_threshold, sync_period + default 3 50 + + It sets synchronization threshold, which is the minimum number + of incoming packets that a connection needs to receive before + the connection will be synchronized. A connection will be + synchronized, every time the number of its incoming packets + modulus sync_period equals the threshold. The range of the + threshold is from 0 to sync_period. + + When sync_period and sync_refresh_period are 0, send sync only + for state changes or only once when pkts matches sync_threshold + +sync_refresh_period - UNSIGNED INTEGER + default 0 + + In seconds, difference in reported connection timer that triggers + new sync message. It can be used to avoid sync messages for the + specified period (or half of the connection timeout if it is lower) + if connection state is not changed since last sync. + + This is useful for normal connections with high traffic to reduce + sync rate. Additionally, retry sync_retries times with period of + sync_refresh_period/8. + +sync_retries - INTEGER + default 0 + + Defines sync retries with period of sync_refresh_period/8. Useful + to protect against loss of sync messages. The range of the + sync_retries is from 0 to 3. snat_reroute - BOOLEAN 0 - disabled -- cgit From 237e5722bc615c3440d8476a694448074380fa01 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Mon, 20 Feb 2017 16:31:36 +0800 Subject: ipvs: Document sysctl sync_qlen_max and sync_sock_size Document sysctl sync_qlen_max and sync_sock_size based on commit 1c003b1580e2 ("ipvs: wakeup master thread"). Signed-off-by: Hangbin Liu Signed-off-by: Simon Horman --- Documentation/networking/ipvs-sysctl.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/networking/ipvs-sysctl.txt b/Documentation/networking/ipvs-sysctl.txt index 7acaaa65451e..159d70b6dff3 100644 --- a/Documentation/networking/ipvs-sysctl.txt +++ b/Documentation/networking/ipvs-sysctl.txt @@ -217,6 +217,20 @@ sync_retries - INTEGER to protect against loss of sync messages. The range of the sync_retries is from 0 to 3. +sync_qlen_max - UNSIGNED LONG + + Hard limit for queued sync messages that are not sent yet. It + defaults to 1/32 of the memory pages but actually represents + number of messages. It will protect us from allocating large + parts of memory when the sending rate is lower than the queuing + rate. + +sync_sock_size - INTEGER + default 0 + + Configuration of SNDBUF (master) or RCVBUF (slave) socket limit. + Default value is 0 (preserve system defaults). + snat_reroute - BOOLEAN 0 - disabled not 0 - enabled (default) -- cgit From 24b444155b1e237accb2571ac656cf40c941a2a7 Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Mon, 20 Feb 2017 16:31:37 +0800 Subject: ipvs: Document sysctl sync_ports Document sysctl sync_ports based on commit f73181c8288f ("ipvs: add support for sync threads"). Signed-off-by: Hangbin Liu Signed-off-by: Simon Horman --- Documentation/networking/ipvs-sysctl.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/networking/ipvs-sysctl.txt b/Documentation/networking/ipvs-sysctl.txt index 159d70b6dff3..a6feecd467cd 100644 --- a/Documentation/networking/ipvs-sysctl.txt +++ b/Documentation/networking/ipvs-sysctl.txt @@ -231,6 +231,14 @@ sync_sock_size - INTEGER Configuration of SNDBUF (master) or RCVBUF (slave) socket limit. Default value is 0 (preserve system defaults). +sync_ports - INTEGER + default 1 + + The number of threads that master and backup servers can use for + sync traffic. Every thread will use single UDP port, thread 0 will + use the default port 8848 while last thread will use port + 8848+sync_ports-1. + snat_reroute - BOOLEAN 0 - disabled not 0 - enabled (default) -- cgit From 3c679cba588a46ba81a264673e192bbd3c92455b Mon Sep 17 00:00:00 2001 From: Hangbin Liu Date: Mon, 20 Feb 2017 16:31:38 +0800 Subject: ipvs: Document sysctl pmtu_disc Document sysctl pmtu_disc based on commit 3654e61137db ("ipvs: add pmtu_disc option to disable IP DF for TUN packets"). Signed-off-by: Hangbin Liu Signed-off-by: Simon Horman --- Documentation/networking/ipvs-sysctl.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/networking/ipvs-sysctl.txt b/Documentation/networking/ipvs-sysctl.txt index a6feecd467cd..056898685d40 100644 --- a/Documentation/networking/ipvs-sysctl.txt +++ b/Documentation/networking/ipvs-sysctl.txt @@ -175,6 +175,14 @@ nat_icmp_send - BOOLEAN for VS/NAT when the load balancer receives packets from real servers but the connection entries don't exist. +pmtu_disc - BOOLEAN + 0 - disabled + not 0 - enabled (default) + + By default, reject with FRAG_NEEDED all DF packets that exceed + the PMTU, irrespective of the forwarding method. For TUN method + the flag can be disabled to fragment such packets. + secure_tcp - INTEGER 0 - disabled (default) -- cgit From ce594e9824ab6ed41ac86f672203f1832b87de79 Mon Sep 17 00:00:00 2001 From: hayeswang Date: Thu, 16 Mar 2017 14:32:22 +0800 Subject: r8152: simply the arguments Replace &tp->napi with napi and tp->netdev with netdev. Signed-off-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 227e1fdd9228..4b85e95ab754 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1761,6 +1761,7 @@ static int rx_bottom(struct r8152 *tp, int budget) unsigned long flags; struct list_head *cursor, *next, rx_queue; int ret = 0, work_done = 0; + struct napi_struct *napi = &tp->napi; if (!skb_queue_empty(&tp->rx_queue)) { while (work_done < budget) { @@ -1773,7 +1774,7 @@ static int rx_bottom(struct r8152 *tp, int budget) break; pkt_len = skb->len; - napi_gro_receive(&tp->napi, skb); + napi_gro_receive(napi, skb); work_done++; stats->rx_packets++; stats->rx_bytes += pkt_len; @@ -1823,7 +1824,7 @@ static int rx_bottom(struct r8152 *tp, int budget) pkt_len -= CRC_SIZE; rx_data += sizeof(struct rx_desc); - skb = napi_alloc_skb(&tp->napi, pkt_len); + skb = napi_alloc_skb(napi, pkt_len); if (!skb) { stats->rx_dropped++; goto find_next_rx; @@ -1835,7 +1836,7 @@ static int rx_bottom(struct r8152 *tp, int budget) skb->protocol = eth_type_trans(skb, netdev); rtl_rx_vlan_tag(rx_desc, skb); if (work_done < budget) { - napi_gro_receive(&tp->napi, skb); + napi_gro_receive(napi, skb); work_done++; stats->rx_packets++; stats->rx_bytes += pkt_len; @@ -3150,6 +3151,7 @@ static bool rtl8153_in_nway(struct r8152 *tp) static void set_carrier(struct r8152 *tp) { struct net_device *netdev = tp->netdev; + struct napi_struct *napi = &tp->napi; u8 speed; speed = rtl8152_get_speed(tp); @@ -3159,7 +3161,7 @@ static void set_carrier(struct r8152 *tp) tp->rtl_ops.enable(tp); set_bit(RTL8152_SET_RX_MODE, &tp->flags); netif_stop_queue(netdev); - napi_disable(&tp->napi); + napi_disable(napi); netif_carrier_on(netdev); rtl_start_rx(tp); napi_enable(&tp->napi); @@ -3169,9 +3171,9 @@ static void set_carrier(struct r8152 *tp) } else { if (netif_carrier_ok(netdev)) { netif_carrier_off(netdev); - napi_disable(&tp->napi); + napi_disable(napi); tp->rtl_ops.disable(tp); - napi_enable(&tp->napi); + napi_enable(napi); netif_info(tp, link, netdev, "carrier off\n"); } } @@ -3633,11 +3635,13 @@ static int rtl8152_runtime_suspend(struct r8152 *tp) tp->rtl_ops.autosuspend_en(tp, true); if (netif_carrier_ok(netdev)) { - napi_disable(&tp->napi); + struct napi_struct *napi = &tp->napi; + + napi_disable(napi); rtl_stop_rx(tp); rxdy_gated_en(tp, false); ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, rcr); - napi_enable(&tp->napi); + napi_enable(napi); } } @@ -3653,12 +3657,14 @@ static int rtl8152_system_suspend(struct r8152 *tp) netif_device_detach(netdev); if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) { + struct napi_struct *napi = &tp->napi; + clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); - napi_disable(&tp->napi); + napi_disable(napi); cancel_delayed_work_sync(&tp->schedule); tp->rtl_ops.down(tp); - napi_enable(&tp->napi); + napi_enable(napi); } return ret; @@ -3684,35 +3690,38 @@ static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message) static int rtl8152_resume(struct usb_interface *intf) { struct r8152 *tp = usb_get_intfdata(intf); + struct net_device *netdev = tp->netdev; mutex_lock(&tp->control); if (!test_bit(SELECTIVE_SUSPEND, &tp->flags)) { tp->rtl_ops.init(tp); queue_delayed_work(system_long_wq, &tp->hw_phy_work, 0); - netif_device_attach(tp->netdev); + netif_device_attach(netdev); } - if (netif_running(tp->netdev) && tp->netdev->flags & IFF_UP) { + if (netif_running(netdev) && netdev->flags & IFF_UP) { if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { + struct napi_struct *napi = &tp->napi; + tp->rtl_ops.autosuspend_en(tp, false); - napi_disable(&tp->napi); + napi_disable(napi); set_bit(WORK_ENABLE, &tp->flags); - if (netif_carrier_ok(tp->netdev)) + if (netif_carrier_ok(netdev)) rtl_start_rx(tp); - napi_enable(&tp->napi); + napi_enable(napi); clear_bit(SELECTIVE_SUSPEND, &tp->flags); smp_mb__after_atomic(); if (!list_empty(&tp->rx_done)) napi_schedule(&tp->napi); } else { tp->rtl_ops.up(tp); - netif_carrier_off(tp->netdev); + netif_carrier_off(netdev); set_bit(WORK_ENABLE, &tp->flags); } usb_submit_urb(tp->intr_urb, GFP_KERNEL); } else if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { - if (tp->netdev->flags & IFF_UP) + if (netdev->flags & IFF_UP) tp->rtl_ops.autosuspend_en(tp, false); clear_bit(SELECTIVE_SUSPEND, &tp->flags); } -- cgit From 3c71006d15fd3a99071a2b20d01de3edabc85767 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 16 Mar 2017 09:08:12 +0100 Subject: ipv4: fib_rules: Check if rule is a default rule Currently, when non-default (custom) FIB rules are used, devices capable of layer 3 offloading flush their tables and let the kernel do the forwarding instead. When these devices' drivers are loaded they register to the FIB notification chain, which lets them know about the existence of any custom FIB rules. This is done by sending a RULE_ADD notification based on the value of 'net->ipv4.fib_has_custom_rules'. This approach is problematic when VRF offload is taken into account, as upon the creation of the first VRF netdev, a l3mdev rule is programmed to direct skbs to the VRF's table. Instead of merely reading the above value and sending a single RULE_ADD notification, we should iterate over all the FIB rules and send a detailed notification for each, thereby allowing offloading drivers to sanitize the rules they don't support and potentially flush their tables. While l3mdev rules are uniquely marked, the default rules are not. Therefore, when they are being notified they might invoke offloading drivers to unnecessarily flush their tables. Solve this by adding an helper to check if a FIB rule is a default rule. Namely, its selector should match all packets and its action should point to the local, main or default tables. As noted by David Ahern, uniquely marking the default rules is insufficient. When using VRFs, it's common to avoid false hits by moving the rule for the local table to just before the main table: Default configuration: $ ip rule show 0: from all lookup local 32766: from all lookup main 32767: from all lookup default Common configuration with VRFs: $ ip rule show 1000: from all lookup [l3mdev-table] 32765: from all lookup local 32766: from all lookup main 32767: from all lookup default Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Acked-by: David Ahern Signed-off-by: David S. Miller --- include/net/fib_rules.h | 1 + include/net/ip_fib.h | 7 +++++++ net/core/fib_rules.c | 14 ++++++++++++++ net/ipv4/fib_rules.c | 21 +++++++++++++++++++++ 4 files changed, 43 insertions(+) diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 8dbfdf728cd8..1243b9c7694e 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -141,6 +141,7 @@ int fib_rules_lookup(struct fib_rules_ops *, struct flowi *, int flags, struct fib_lookup_arg *); int fib_default_rule_add(struct fib_rules_ops *, u32 pref, u32 table, u32 flags); +bool fib_rule_matchall(const struct fib_rule *rule); int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh); int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh); diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index d9cee9659978..da6fa7b15558 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -311,6 +311,11 @@ static inline int fib_lookup(struct net *net, const struct flowi4 *flp, return err; } +static inline bool fib4_rule_default(const struct fib_rule *rule) +{ + return true; +} + #else /* CONFIG_IP_MULTIPLE_TABLES */ int __net_init fib4_rules_init(struct net *net); void __net_exit fib4_rules_exit(struct net *net); @@ -355,6 +360,8 @@ out: return err; } +bool fib4_rule_default(const struct fib_rule *rule); + #endif /* CONFIG_IP_MULTIPLE_TABLES */ /* Exported by fib_frontend.c */ diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index b6791d94841d..816e3ccb0ec9 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -23,6 +23,20 @@ static const struct fib_kuid_range fib_kuid_range_unset = { KUIDT_INIT(~0), }; +bool fib_rule_matchall(const struct fib_rule *rule) +{ + if (rule->iifindex || rule->oifindex || rule->mark || rule->tun_id || + rule->flags) + return false; + if (rule->suppress_ifgroup != -1 || rule->suppress_prefixlen != -1) + return false; + if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) || + !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end)) + return false; + return true; +} +EXPORT_SYMBOL_GPL(fib_rule_matchall); + int fib_default_rule_add(struct fib_rules_ops *ops, u32 pref, u32 table, u32 flags) { diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 289210903d58..d531bc94b15e 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -47,6 +47,27 @@ struct fib4_rule { #endif }; +static bool fib4_rule_matchall(const struct fib_rule *rule) +{ + struct fib4_rule *r = container_of(rule, struct fib4_rule, common); + + if (r->dst_len || r->src_len || r->tos) + return false; + return fib_rule_matchall(rule); +} + +bool fib4_rule_default(const struct fib_rule *rule) +{ + if (!fib4_rule_matchall(rule) || rule->action != FR_ACT_TO_TBL || + rule->l3mdev) + return false; + if (rule->table != RT_TABLE_LOCAL && rule->table != RT_TABLE_MAIN && + rule->table != RT_TABLE_DEFAULT) + return false; + return true; +} +EXPORT_SYMBOL_GPL(fib4_rule_default); + int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res, unsigned int flags) { -- cgit From 6a003a5ff29499a94373110202631743663569c6 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 16 Mar 2017 09:08:13 +0100 Subject: ipv4: fib_rules: Add notifier info to FIB rules notifications Whenever a FIB rule is added or removed, a notification is sent in the FIB notification chain. However, listeners don't have a way to tell which rule was added or removed. This is problematic as we would like to give listeners the ability to decide which action to execute based on the notified rule. Specifically, offloading drivers should be able to determine if they support the reflection of the notified FIB rule and flush their LPM tables in case they don't. Do that by adding a notifier info to these notifications and embed the common FIB rule struct in it. Signed-off-by: Ido Schimmel Acked-by: David Ahern Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/ip_fib.h | 5 +++++ net/ipv4/fib_rules.c | 13 ++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index da6fa7b15558..272e62e139e0 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -213,6 +213,11 @@ struct fib_entry_notifier_info { u32 tb_id; }; +struct fib_rule_notifier_info { + struct fib_notifier_info info; /* must be first */ + struct fib_rule *rule; +}; + struct fib_nh_notifier_info { struct fib_notifier_info info; /* must be first */ struct fib_nh *fib_nh; diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index d531bc94b15e..310d24a2e097 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -186,11 +186,14 @@ static struct fib_table *fib_empty_table(struct net *net) } static int call_fib_rule_notifiers(struct net *net, - enum fib_event_type event_type) + enum fib_event_type event_type, + struct fib_rule *rule) { - struct fib_notifier_info info; + struct fib_rule_notifier_info info = { + .rule = rule, + }; - return call_fib_notifiers(net, event_type, &info); + return call_fib_notifiers(net, event_type, &info.info); } void fib_rules_notify(struct net *net, struct notifier_block *nb) @@ -257,7 +260,7 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, rule4->tos = frh->tos; net->ipv4.fib_has_custom_rules = true; - call_fib_rule_notifiers(net, FIB_EVENT_RULE_ADD); + call_fib_rule_notifiers(net, FIB_EVENT_RULE_ADD, rule); err = 0; errout: @@ -279,7 +282,7 @@ static int fib4_rule_delete(struct fib_rule *rule) net->ipv4.fib_num_tclassid_users--; #endif net->ipv4.fib_has_custom_rules = true; - call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL); + call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule); errout: return err; } -- cgit From 5d7bfd141924a5ece21eb612ad3c56612f041c1e Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 16 Mar 2017 09:08:14 +0100 Subject: ipv4: fib_rules: Dump FIB rules when registering FIB notifier In commit c3852ef7f2f8 ("ipv4: fib: Replay events when registering FIB notifier") we dumped the FIB tables and replayed the events to the passed notification block. However, we merely sent a RULE_ADD notification in case custom rules were in use. As explained in previous patches, this approach won't work anymore. Instead, we should notify the caller about all the FIB rules and let it act accordingly. Upon registration to the FIB notification chain, replay a RULE_ADD notification for each programmed FIB rule, custom or not. The integrity of the dump is ensured by the mechanism introduced in the above mentioned commit. Prevent regressions by making sure current listeners correctly sanitize the notified rules. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Acked-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 13 ++++++++++++- drivers/net/ethernet/rocker/rocker_main.c | 17 +++++++++++++++-- net/ipv4/fib_rules.c | 19 ++++++++++++++++--- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 80345a1ddf17..9ab41c47c263 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -45,6 +45,7 @@ #include #include #include +#include #include "spectrum.h" #include "core.h" @@ -2514,6 +2515,7 @@ struct mlxsw_sp_fib_event_work { struct work_struct work; union { struct fib_entry_notifier_info fen_info; + struct fib_rule_notifier_info fr_info; struct fib_nh_notifier_info fnh_info; }; struct mlxsw_sp *mlxsw_sp; @@ -2525,6 +2527,7 @@ static void mlxsw_sp_router_fib_event_work(struct work_struct *work) struct mlxsw_sp_fib_event_work *fib_work = container_of(work, struct mlxsw_sp_fib_event_work, work); struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp; + struct fib_rule *rule; bool replace, append; int err; @@ -2548,7 +2551,10 @@ static void mlxsw_sp_router_fib_event_work(struct work_struct *work) break; case FIB_EVENT_RULE_ADD: /* fall through */ case FIB_EVENT_RULE_DEL: - mlxsw_sp_router_fib4_abort(mlxsw_sp); + rule = fib_work->fr_info.rule; + if (!fib4_rule_default(rule)) + mlxsw_sp_router_fib4_abort(mlxsw_sp); + fib_rule_put(rule); break; case FIB_EVENT_NH_ADD: /* fall through */ case FIB_EVENT_NH_DEL: @@ -2591,6 +2597,11 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb, */ fib_info_hold(fib_work->fen_info.fi); break; + case FIB_EVENT_RULE_ADD: /* fall through */ + case FIB_EVENT_RULE_DEL: + memcpy(&fib_work->fr_info, ptr, sizeof(fib_work->fr_info)); + fib_rule_get(fib_work->fr_info.rule); + break; case FIB_EVENT_NH_ADD: /* fall through */ case FIB_EVENT_NH_DEL: memcpy(&fib_work->fnh_info, ptr, sizeof(fib_work->fnh_info)); diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index b712ec23075b..bab13613b138 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -2175,7 +2176,10 @@ static const struct switchdev_ops rocker_port_switchdev_ops = { struct rocker_fib_event_work { struct work_struct work; - struct fib_entry_notifier_info fen_info; + union { + struct fib_entry_notifier_info fen_info; + struct fib_rule_notifier_info fr_info; + }; struct rocker *rocker; unsigned long event; }; @@ -2185,6 +2189,7 @@ static void rocker_router_fib_event_work(struct work_struct *work) struct rocker_fib_event_work *fib_work = container_of(work, struct rocker_fib_event_work, work); struct rocker *rocker = fib_work->rocker; + struct fib_rule *rule; int err; /* Protect internal structures from changes */ @@ -2202,7 +2207,10 @@ static void rocker_router_fib_event_work(struct work_struct *work) break; case FIB_EVENT_RULE_ADD: /* fall through */ case FIB_EVENT_RULE_DEL: - rocker_world_fib4_abort(rocker); + rule = fib_work->fr_info.rule; + if (!fib4_rule_default(rule)) + rocker_world_fib4_abort(rocker); + fib_rule_put(rule); break; } rtnl_unlock(); @@ -2233,6 +2241,11 @@ static int rocker_router_fib_event(struct notifier_block *nb, */ fib_info_hold(fib_work->fen_info.fi); break; + case FIB_EVENT_RULE_ADD: /* fall through */ + case FIB_EVENT_RULE_DEL: + memcpy(&fib_work->fr_info, ptr, sizeof(fib_work->fr_info)); + fib_rule_get(fib_work->fr_info.rule); + break; } queue_work(rocker->rocker_owq, &fib_work->work); diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 310d24a2e097..778ecf977eb2 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -185,6 +185,17 @@ static struct fib_table *fib_empty_table(struct net *net) return NULL; } +static int call_fib_rule_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct fib_rule *rule) +{ + struct fib_rule_notifier_info info = { + .rule = rule, + }; + + return call_fib_notifier(nb, net, event_type, &info.info); +} + static int call_fib_rule_notifiers(struct net *net, enum fib_event_type event_type, struct fib_rule *rule) @@ -196,12 +207,14 @@ static int call_fib_rule_notifiers(struct net *net, return call_fib_notifiers(net, event_type, &info.info); } +/* Called with rcu_read_lock() */ void fib_rules_notify(struct net *net, struct notifier_block *nb) { - struct fib_notifier_info info; + struct fib_rules_ops *ops = net->ipv4.rules_ops; + struct fib_rule *rule; - if (net->ipv4.fib_has_custom_rules) - call_fib_notifier(nb, net, FIB_EVENT_RULE_ADD, &info); + list_for_each_entry_rcu(rule, &ops->rules_list, list) + call_fib_rule_notifier(nb, net, FIB_EVENT_RULE_ADD, rule); } static const struct nla_policy fib4_rule_policy[FRA_MAX+1] = { -- cgit From fdeea7be88b12742bfd50d9e19a06c0d2e702400 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 16 Mar 2017 09:08:15 +0100 Subject: net: vrf: Set slave's private flag before linking Allow listeners of the subsequent CHANGEUPPER notification to retrieve the VRF's table ID by calling l3mdev_fib_table() with the slave netdev. Without this change, the netdev won't be considered an L3 slave and the function would return 0. This is consistent with other master device such as bridge and bond that set the slave's private flag before linking. It also makes do_vrf_{add,del}_slave() symmetric. Signed-off-by: Ido Schimmel Acked-by: David Ahern Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/vrf.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index fea687f35b5a..7f28021d9d93 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -747,14 +747,18 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) { int ret; + port_dev->priv_flags |= IFF_L3MDEV_SLAVE; ret = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL); if (ret < 0) - return ret; + goto err; - port_dev->priv_flags |= IFF_L3MDEV_SLAVE; cycle_netdev(port_dev); return 0; + +err: + port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE; + return ret; } static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev) -- cgit From 57837885e3c74e42596a8d8c3e6831be1c0a7974 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 16 Mar 2017 09:08:16 +0100 Subject: mlxsw: spectrum_router: Associate RIFs with correct VR When a router interface (RIF) is created due to a netdev being enslaved to a VRF master, then it should be associated with the appropriate virtual router (VR) and not the default one. If netdev is a VRF slave, lookup the VR based on the VRF's table ID. Otherwise default to the MAIN table. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 9ab41c47c263..5aad0aef1ed3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -46,6 +46,7 @@ #include #include #include +#include #include "spectrum.h" #include "core.h" @@ -2762,6 +2763,7 @@ mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport, struct net_device *l3_dev) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; + u32 tb_id = l3mdev_fib_table(l3_dev); struct mlxsw_sp_vr *vr; struct mlxsw_sp_fid *f; struct mlxsw_sp_rif *r; @@ -2772,7 +2774,7 @@ mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport, if (rif == MLXSW_SP_INVALID_RIF) return ERR_PTR(-ERANGE); - vr = mlxsw_sp_vr_get(mlxsw_sp, RT_TABLE_MAIN); + vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN); if (IS_ERR(vr)) return ERR_CAST(vr); @@ -3010,6 +3012,7 @@ static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp, struct net_device *l3_dev, struct mlxsw_sp_fid *f) { + u32 tb_id = l3mdev_fib_table(l3_dev); struct mlxsw_sp_vr *vr; struct mlxsw_sp_rif *r; u16 rif; @@ -3019,7 +3022,7 @@ static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp, if (rif == MLXSW_SP_INVALID_RIF) return -ERANGE; - vr = mlxsw_sp_vr_get(mlxsw_sp, RT_TABLE_MAIN); + vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN); if (IS_ERR(vr)) return PTR_ERR(vr); -- cgit From 9db032bb1e8eed0721884c01a6c87f55f3d400fa Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 16 Mar 2017 09:08:17 +0100 Subject: mlxsw: spectrum_router: Don't destroy RIF if L3 slave We usually destroy the netdev's router interface (RIF) when the last IP address is removed from it. However, we shouldn't do that if it's enslaved to an L3 master device. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 5aad0aef1ed3..91ec62a2db48 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -2660,7 +2661,7 @@ static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *r, return true; return false; case NETDEV_DOWN: - if (r && !in_dev->ifa_list) + if (r && !in_dev->ifa_list && !netif_is_l3_slave(r->dev)) return true; /* It is possible we already removed the RIF ourselves * if it was assigned to a netdev that is now a bridge -- cgit From 7179eb5acd59d64cb8bef2d8788af8e9647f6986 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 16 Mar 2017 09:08:18 +0100 Subject: mlxsw: spectrum_router: Add support for VRFs Allow port netdevs, LAG and VLAN devices stacked on top of these to be enslaved to a VRF master device. Upon enslavement, create a router interface (RIF) for the enslaved netdev and associate it with a virtual router (VR) based on the VRF's table ID. If a RIF already exists for the netdev (f.e., due to the existence of an IP address), then it's deleted and a new one is created with the appropriate VR binding. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 19 ++++++++-- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 4 +++ .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 41 ++++++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 475499b6c989..659df3225a24 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3951,7 +3951,8 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, upper_dev = info->upper_dev; if (!is_vlan_dev(upper_dev) && !netif_is_lag_master(upper_dev) && - !netif_is_bridge_master(upper_dev)) + !netif_is_bridge_master(upper_dev) && + !netif_is_l3_master(upper_dev)) return -EINVAL; if (!info->linking) break; @@ -3991,6 +3992,11 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, else mlxsw_sp_port_lag_leave(mlxsw_sp_port, upper_dev); + } else if (netif_is_l3_master(upper_dev)) { + if (info->linking) + err = mlxsw_sp_port_vrf_join(mlxsw_sp_port); + else + mlxsw_sp_port_vrf_leave(mlxsw_sp_port); } else { err = -EINVAL; WARN_ON(1); @@ -4353,14 +4359,16 @@ static int mlxsw_sp_netdevice_vport_event(struct net_device *dev, switch (event) { case NETDEV_PRECHANGEUPPER: upper_dev = info->upper_dev; - if (!netif_is_bridge_master(upper_dev)) + if (!netif_is_bridge_master(upper_dev) && + !netif_is_l3_master(upper_dev)) return -EINVAL; if (!info->linking) break; /* We can't have multiple VLAN interfaces configured on * the same port and being members in the same bridge. */ - if (!mlxsw_sp_port_master_bridge_check(mlxsw_sp_port, + if (netif_is_bridge_master(upper_dev) && + !mlxsw_sp_port_master_bridge_check(mlxsw_sp_port, upper_dev)) return -EINVAL; break; @@ -4372,6 +4380,11 @@ static int mlxsw_sp_netdevice_vport_event(struct net_device *dev, upper_dev); else mlxsw_sp_vport_bridge_leave(mlxsw_sp_vport); + } else if (netif_is_l3_master(upper_dev)) { + if (info->linking) + err = mlxsw_sp_vport_vrf_join(mlxsw_sp_vport); + else + mlxsw_sp_vport_vrf_leave(mlxsw_sp_vport); } else { err = -EINVAL; WARN_ON(1); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 5502232b06cf..60004d97e631 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -578,6 +578,10 @@ int mlxsw_sp_inetaddr_event(struct notifier_block *unused, unsigned long event, void *ptr); void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_rif *r); +int mlxsw_sp_vport_vrf_join(struct mlxsw_sp_port *mlxsw_sp_vport); +void mlxsw_sp_vport_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_vport); +int mlxsw_sp_port_vrf_join(struct mlxsw_sp_port *mlxsw_sp_port); +void mlxsw_sp_port_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_port); int mlxsw_sp_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, unsigned int entry_count); void mlxsw_sp_kvdl_free(struct mlxsw_sp *mlxsw_sp, int entry_index); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 91ec62a2db48..e26268805fcc 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3226,6 +3226,47 @@ err_rif_edit: return err; } +int mlxsw_sp_vport_vrf_join(struct mlxsw_sp_port *mlxsw_sp_vport) +{ + struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport); + struct net_device *dev = mlxsw_sp_vport->dev; + + /* In case vPort already has a RIF, then we need to drop it. + * A new one will be created using the VRF's VR. + */ + if (f && f->r) + mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport); + + return mlxsw_sp_vport_rif_sp_join(mlxsw_sp_vport, dev); +} + +void mlxsw_sp_vport_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_vport) +{ + mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport); +} + +int mlxsw_sp_port_vrf_join(struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct mlxsw_sp_port *mlxsw_sp_vport; + + mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, 1); + if (WARN_ON(!mlxsw_sp_vport)) + return -EINVAL; + + return mlxsw_sp_vport_vrf_join(mlxsw_sp_vport); +} + +void mlxsw_sp_port_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_port) +{ + struct mlxsw_sp_port *mlxsw_sp_vport; + + mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, 1); + if (WARN_ON(!mlxsw_sp_vport)) + return; + + mlxsw_sp_vport_vrf_leave(mlxsw_sp_vport); +} + static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb) { struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb); -- cgit From 3d70e458be3af878216f8c97fb4c0926cc003e31 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 16 Mar 2017 09:08:19 +0100 Subject: mlxsw: spectrum_router: Add support for VRFs on top of bridges In a similar fashion to the previous patch, allow bridges and VLAN devices on top of bridges to be enslaved to a VRF master device. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 52 +++++++++++++++++++++- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 4 ++ .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 26 +++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 659df3225a24..28019f8b70f7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4111,7 +4111,7 @@ static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev, switch (event) { case NETDEV_PRECHANGEUPPER: upper_dev = info->upper_dev; - if (!is_vlan_dev(upper_dev)) + if (!is_vlan_dev(upper_dev) && !netif_is_l3_master(upper_dev)) return -EINVAL; if (is_vlan_dev(upper_dev) && br_dev != mlxsw_sp->master_bridge.dev) @@ -4126,6 +4126,12 @@ static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev, else mlxsw_sp_master_bridge_vlan_unlink(mlxsw_sp, upper_dev); + } else if (netif_is_l3_master(upper_dev)) { + if (info->linking) + err = mlxsw_sp_bridge_vrf_join(mlxsw_sp, + br_dev); + else + mlxsw_sp_bridge_vrf_leave(mlxsw_sp, br_dev); } else { err = -EINVAL; WARN_ON(1); @@ -4415,6 +4421,47 @@ static int mlxsw_sp_netdevice_lag_vport_event(struct net_device *lag_dev, return 0; } +static int mlxsw_sp_netdevice_bridge_vlan_event(struct net_device *vlan_dev, + unsigned long event, void *ptr) +{ + struct netdev_notifier_changeupper_info *info; + struct mlxsw_sp *mlxsw_sp; + int err = 0; + + mlxsw_sp = mlxsw_sp_lower_get(vlan_dev); + if (!mlxsw_sp) + return 0; + + info = ptr; + + switch (event) { + case NETDEV_PRECHANGEUPPER: + /* VLAN devices are only allowed on top of the + * VLAN-aware bridge. + */ + if (WARN_ON(vlan_dev_real_dev(vlan_dev) != + mlxsw_sp->master_bridge.dev)) + return -EINVAL; + if (!netif_is_l3_master(info->upper_dev)) + return -EINVAL; + break; + case NETDEV_CHANGEUPPER: + if (netif_is_l3_master(info->upper_dev)) { + if (info->linking) + err = mlxsw_sp_bridge_vrf_join(mlxsw_sp, + vlan_dev); + else + mlxsw_sp_bridge_vrf_leave(mlxsw_sp, vlan_dev); + } else { + err = -EINVAL; + WARN_ON(1); + } + break; + } + + return err; +} + static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev, unsigned long event, void *ptr) { @@ -4427,6 +4474,9 @@ static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev, else if (netif_is_lag_master(real_dev)) return mlxsw_sp_netdevice_lag_vport_event(real_dev, event, ptr, vid); + else if (netif_is_bridge_master(real_dev)) + return mlxsw_sp_netdevice_bridge_vlan_event(vlan_dev, event, + ptr); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 60004d97e631..0e223f6983c5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -582,6 +582,10 @@ int mlxsw_sp_vport_vrf_join(struct mlxsw_sp_port *mlxsw_sp_vport); void mlxsw_sp_vport_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_vport); int mlxsw_sp_port_vrf_join(struct mlxsw_sp_port *mlxsw_sp_port); void mlxsw_sp_port_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_port); +int mlxsw_sp_bridge_vrf_join(struct mlxsw_sp *mlxsw_sp, + struct net_device *l3_dev); +void mlxsw_sp_bridge_vrf_leave(struct mlxsw_sp *mlxsw_sp, + struct net_device *l3_dev); int mlxsw_sp_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, unsigned int entry_count); void mlxsw_sp_kvdl_free(struct mlxsw_sp *mlxsw_sp, int entry_index); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index e26268805fcc..c89f4b4d70b1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3267,6 +3267,32 @@ void mlxsw_sp_port_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_port) mlxsw_sp_vport_vrf_leave(mlxsw_sp_vport); } +int mlxsw_sp_bridge_vrf_join(struct mlxsw_sp *mlxsw_sp, + struct net_device *l3_dev) +{ + struct mlxsw_sp_fid *f; + + f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev); + if (WARN_ON(!f)) + return -EINVAL; + + if (f->r) + mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); + + return mlxsw_sp_rif_bridge_create(mlxsw_sp, l3_dev, f); +} + +void mlxsw_sp_bridge_vrf_leave(struct mlxsw_sp *mlxsw_sp, + struct net_device *l3_dev) +{ + struct mlxsw_sp_fid *f; + + f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev); + if (WARN_ON(!f)) + return; + mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); +} + static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb) { struct mlxsw_sp *mlxsw_sp = container_of(nb, struct mlxsw_sp, fib_nb); -- cgit From c7f6e6658b771c64a07b69852ac4362a8e781ab6 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 16 Mar 2017 09:08:20 +0100 Subject: mlxsw: spectrum_router: Don't abort on l3mdev rules Now that port netdevs can be enslaved to a VRF master we need to make sure the device's routing tables won't be flushed upon the insertion of a l3mdev rule. Note that we assume the notified l3mdev rule is a simple rule as used by the VRF master. We don't check for the presence of other selectors such as 'iif' and 'oif'. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index c89f4b4d70b1..488bc1fd7868 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2554,7 +2554,7 @@ static void mlxsw_sp_router_fib_event_work(struct work_struct *work) case FIB_EVENT_RULE_ADD: /* fall through */ case FIB_EVENT_RULE_DEL: rule = fib_work->fr_info.rule; - if (!fib4_rule_default(rule)) + if (!fib4_rule_default(rule) && !rule->l3mdev) mlxsw_sp_router_fib4_abort(mlxsw_sp); fib_rule_put(rule); break; -- cgit From 9768b45ceb0bc7bdee61837afad331dd6bf7977f Mon Sep 17 00:00:00 2001 From: Jane Li Date: Thu, 16 Mar 2017 16:22:28 +0800 Subject: net: mvneta: support suspend and resume Add basic support for handling suspend and resume. Signed-off-by: Jane Li Reviewed-by: Jisheng Zhang Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 61 ++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 61dd4462411c..aebbc5399a06 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -431,6 +431,7 @@ struct mvneta_port { /* Flags for special SoC configurations */ bool neta_armada3700; u16 rx_offset_correction; + const struct mbus_dram_target_info *dram_target_info; }; /* The mvneta_tx_desc and mvneta_rx_desc structures describe the @@ -4118,7 +4119,6 @@ static int mvneta_port_power_up(struct mvneta_port *pp, int phy_mode) /* Device initialization routine */ static int mvneta_probe(struct platform_device *pdev) { - const struct mbus_dram_target_info *dram_target_info; struct resource *res; struct device_node *dn = pdev->dev.of_node; struct device_node *phy_node; @@ -4267,13 +4267,13 @@ static int mvneta_probe(struct platform_device *pdev) pp->tx_csum_limit = tx_csum_limit; - dram_target_info = mv_mbus_dram_info(); + pp->dram_target_info = mv_mbus_dram_info(); /* Armada3700 requires setting default configuration of Mbus * windows, however without using filled mbus_dram_target_info * structure. */ - if (dram_target_info || pp->neta_armada3700) - mvneta_conf_mbus_windows(pp, dram_target_info); + if (pp->dram_target_info || pp->neta_armada3700) + mvneta_conf_mbus_windows(pp, pp->dram_target_info); pp->tx_ring_size = MVNETA_MAX_TXD; pp->rx_ring_size = MVNETA_MAX_RXD; @@ -4405,6 +4405,58 @@ static int mvneta_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int mvneta_suspend(struct device *device) +{ + struct net_device *dev = dev_get_drvdata(device); + struct mvneta_port *pp = netdev_priv(dev); + + if (netif_running(dev)) + mvneta_stop(dev); + netif_device_detach(dev); + clk_disable_unprepare(pp->clk_bus); + clk_disable_unprepare(pp->clk); + return 0; +} + +static int mvneta_resume(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct net_device *dev = dev_get_drvdata(device); + struct mvneta_port *pp = netdev_priv(dev); + int err; + + clk_prepare_enable(pp->clk); + if (!IS_ERR(pp->clk_bus)) + clk_prepare_enable(pp->clk_bus); + if (pp->dram_target_info || pp->neta_armada3700) + mvneta_conf_mbus_windows(pp, pp->dram_target_info); + if (pp->bm_priv) { + err = mvneta_bm_port_init(pdev, pp); + if (err < 0) { + dev_info(&pdev->dev, "use SW buffer management\n"); + pp->bm_priv = NULL; + } + } + mvneta_defaults_set(pp); + err = mvneta_port_power_up(pp, pp->phy_interface); + if (err < 0) { + dev_err(device, "can't power up port\n"); + return err; + } + + if (pp->use_inband_status) + mvneta_fixed_link_update(pp, dev->phydev); + + netif_device_attach(dev); + if (netif_running(dev)) + mvneta_open(dev); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mvneta_pm_ops, mvneta_suspend, mvneta_resume); + static const struct of_device_id mvneta_match[] = { { .compatible = "marvell,armada-370-neta" }, { .compatible = "marvell,armada-xp-neta" }, @@ -4419,6 +4471,7 @@ static struct platform_driver mvneta_driver = { .driver = { .name = MVNETA_DRIVER_NAME, .of_match_table = mvneta_match, + .pm = &mvneta_pm_ops, }, }; -- cgit From b79df0fc60c5b459ac7ba60c03467d672745954b Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Thu, 16 Mar 2017 10:18:02 +0100 Subject: isdn: hardware: mISDN: Remove reference to CONFIG_8xx CONFIG_8xx is deprecated and should soon be removed in favor of CONFIG_PPC_8xx. Anyway, hfc_multi_8xx.h only uses 8xx I/O ports which are linked to the CPM1 communication processor included in the 8xx rather than the 8xx itself. This patch therefore makes it dependent on CONFIG_CPM1 instead, like several other drivers. Signed-off-by: Christophe Leroy Signed-off-by: David S. Miller --- drivers/isdn/hardware/mISDN/Kconfig | 6 +++--- drivers/isdn/hardware/mISDN/hfc_multi_8xx.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig index 09df54fc1fef..fda912b0833f 100644 --- a/drivers/isdn/hardware/mISDN/Kconfig +++ b/drivers/isdn/hardware/mISDN/Kconfig @@ -13,7 +13,7 @@ config MISDN_HFCPCI config MISDN_HFCMULTI tristate "Support for HFC multiport cards (HFC-4S/8S/E1)" - depends on PCI || 8xx + depends on PCI || CPM1 depends on MISDN help Enable support for cards with Cologne Chip AG's HFC multiport @@ -27,8 +27,8 @@ config MISDN_HFCMULTI_8xx bool "Support for XHFC embedded board in HFC multiport driver" depends on MISDN depends on MISDN_HFCMULTI - depends on 8xx - default 8xx + depends on CPM1 + default CPM1 help Enable support for the XHFC embedded solution from Speech Design. diff --git a/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h b/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h index 0eafe9f04fca..8a254747768e 100644 --- a/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h +++ b/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h @@ -6,7 +6,7 @@ * */ -#include +#include /* Change this to the value used by your board */ #ifndef IMAP_ADDR -- cgit From 01ac2994c0d566f2e816ef0c88ab8e4f8b1b3ed1 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Thu, 16 Mar 2017 10:18:04 +0100 Subject: net: ethernet: fs_enet: Remove useless includes CONFIG_8xx is being deprecated. Since the includes dependent on CONFIG_8xx are useless, just drop them. Signed-off-by: Christophe Leroy Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fs_enet/mac-fec.c | 6 ------ drivers/net/ethernet/freescale/fs_enet/mac-scc.c | 6 ------ 2 files changed, 12 deletions(-) diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c index db9c0bcf54cd..1fc27c97e3b2 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c @@ -38,12 +38,6 @@ #include #include -#ifdef CONFIG_8xx -#include -#include -#include -#endif - #include "fs_enet.h" #include "fec.h" diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-scc.c b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c index 96d44cf44fe0..64300ac13e02 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-scc.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-scc.c @@ -37,12 +37,6 @@ #include #include -#ifdef CONFIG_8xx -#include -#include -#include -#endif - #include "fs_enet.h" /*************************************************/ -- cgit From 2f771399a3a2c371c140ff33544a583c6fbc5fd9 Mon Sep 17 00:00:00 2001 From: Steve Lin Date: Thu, 16 Mar 2017 11:48:58 -0400 Subject: net: ethernet: bgmac: Allow MAC address to be specified in DTB Allows the BCMA version of the bgmac driver to obtain MAC address from the device tree. If no MAC address is specified there, then the previous behavior (obtaining MAC address from SPROM) is used. Signed-off-by: Steve Lin Reviewed-by: Florian Fainelli Acked-by: Jon Mason Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bgmac-bcma.c | 39 ++++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bgmac-bcma.c b/drivers/net/ethernet/broadcom/bgmac-bcma.c index cf15b7e2929c..6322594ab260 100644 --- a/drivers/net/ethernet/broadcom/bgmac-bcma.c +++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "bgmac.h" static inline bool bgmac_is_bcm4707_family(struct bcma_device *core) @@ -114,7 +115,7 @@ static int bgmac_probe(struct bcma_device *core) struct ssb_sprom *sprom = &core->bus->sprom; struct mii_bus *mii_bus; struct bgmac *bgmac; - u8 *mac; + const u8 *mac = NULL; int err; bgmac = bgmac_alloc(&core->dev); @@ -127,21 +128,27 @@ static int bgmac_probe(struct bcma_device *core) bcma_set_drvdata(core, bgmac); - switch (core->core_unit) { - case 0: - mac = sprom->et0mac; - break; - case 1: - mac = sprom->et1mac; - break; - case 2: - mac = sprom->et2mac; - break; - default: - dev_err(bgmac->dev, "Unsupported core_unit %d\n", - core->core_unit); - err = -ENOTSUPP; - goto err; + if (bgmac->dev->of_node) + mac = of_get_mac_address(bgmac->dev->of_node); + + /* If no MAC address assigned via device tree, check SPROM */ + if (!mac) { + switch (core->core_unit) { + case 0: + mac = sprom->et0mac; + break; + case 1: + mac = sprom->et1mac; + break; + case 2: + mac = sprom->et2mac; + break; + default: + dev_err(bgmac->dev, "Unsupported core_unit %d\n", + core->core_unit); + err = -ENOTSUPP; + goto err; + } } ether_addr_copy(bgmac->net_dev->dev_addr, mac); -- cgit From 4dba87b07394ebf9fa4693d90f4074bda977449b Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Thu, 16 Mar 2017 12:53:41 +0200 Subject: net/sched: act_ife: Staticfy find_decode_metaid() As it's used only on that file. Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- net/sched/act_ife.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c index 71e7ff22f7c9..c75ea5c9102c 100644 --- a/net/sched/act_ife.c +++ b/net/sched/act_ife.c @@ -603,8 +603,8 @@ nla_put_failure: return -1; } -int find_decode_metaid(struct sk_buff *skb, struct tcf_ife_info *ife, - u16 metaid, u16 mlen, void *mdata) +static int find_decode_metaid(struct sk_buff *skb, struct tcf_ife_info *ife, + u16 metaid, u16 mlen, void *mdata) { struct tcf_meta_info *e; -- cgit From a5e6a3b0222fb19eaae424226533eadb0be20e93 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Thu, 16 Mar 2017 12:53:42 +0200 Subject: net/sched: fq_codel: Avoid set-but-unused variable The code introduced by commit 2ccccf5fb43f ("net_sched: update hierarchical backlog too") only sets prev_backlog in fq_codel_dequeue() but not using that anywhere, remove that setting. Cc: Cong Wang Signed-off-by: Or Gerlitz Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/sched/sch_fq_codel.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index 9f3a884d1590..097bbe9857a5 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -288,7 +288,6 @@ static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) struct fq_codel_flow *flow; struct list_head *head; u32 prev_drop_count, prev_ecn_mark; - unsigned int prev_backlog; begin: head = &q->new_flows; @@ -307,7 +306,6 @@ begin: prev_drop_count = q->cstats.drop_count; prev_ecn_mark = q->cstats.ecn_mark; - prev_backlog = sch->qstats.backlog; skb = codel_dequeue(sch, &sch->qstats.backlog, &q->cparams, &flow->cvars, &q->cstats, qdisc_pkt_len, -- cgit From b96a89818805d90513ac5516e17ecd9d8c19313a Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Tue, 28 Feb 2017 17:25:12 +0100 Subject: batman-adv: Start new development cycle Signed-off-by: Simon Wunderlich --- net/batman-adv/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 57a8103dbce7..f87b17c613a6 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -24,7 +24,7 @@ #define BATADV_DRIVER_DEVICE "batman-adv" #ifndef BATADV_SOURCE_VERSION -#define BATADV_SOURCE_VERSION "2017.0" +#define BATADV_SOURCE_VERSION "2017.1" #endif /* B.A.T.M.A.N. parameters */ -- cgit From c1bacea053fdaa20cbb37287eb33198ddef255bd Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 22 Feb 2017 17:16:39 +0100 Subject: batman-adv: Reduce preprocessor checks in multicast.c It is not necessary to disable these code sections in case other kernel features are disabled. Instead the IS_ENABLED tests can be added directly in the code and the compiler can remove the unnecessary code parts during its optimization run. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/multicast.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index 952ba81a565b..d327670641ac 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c @@ -494,9 +494,8 @@ static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv) if (!bridged) goto update; -#if !IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING) - pr_warn_once("No bridge IGMP snooping compiled - multicast optimizations disabled\n"); -#endif + if (!IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)) + pr_warn_once("No bridge IGMP snooping compiled - multicast optimizations disabled\n"); querier4.exists = br_multicast_has_querier_anywhere(dev, ETH_P_IP); querier4.shadowing = br_multicast_has_querier_adjacent(dev, ETH_P_IP); @@ -671,7 +670,6 @@ static int batadv_mcast_forw_mode_check_ipv4(struct batadv_priv *bat_priv, return 0; } -#if IS_ENABLED(CONFIG_IPV6) /** * batadv_mcast_is_report_ipv6 - check for MLD reports * @skb: the ethernet frame destined for the mesh @@ -736,7 +734,6 @@ static int batadv_mcast_forw_mode_check_ipv6(struct batadv_priv *bat_priv, return 0; } -#endif /** * batadv_mcast_forw_mode_check - check for optimized forwarding potential @@ -765,11 +762,12 @@ static int batadv_mcast_forw_mode_check(struct batadv_priv *bat_priv, case ETH_P_IP: return batadv_mcast_forw_mode_check_ipv4(bat_priv, skb, is_unsnoopable); -#if IS_ENABLED(CONFIG_IPV6) case ETH_P_IPV6: + if (!IS_ENABLED(CONFIG_IPV6)) + return -EINVAL; + return batadv_mcast_forw_mode_check_ipv6(bat_priv, skb, is_unsnoopable); -#endif default: return -EINVAL; } -- cgit From 1fda4c0ac00353beceb33b524888c3b71432b6d6 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 22 Feb 2017 17:16:40 +0100 Subject: batman-adv: Fix unbalanced braces around else statement Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/translation-table.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 6077a87d46f0..a75f0a640bcd 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -2253,12 +2253,13 @@ batadv_tt_global_del_roaming(struct batadv_priv *bat_priv, /* its the last one, mark for roaming. */ tt_global_entry->common.flags |= BATADV_TT_CLIENT_ROAM; tt_global_entry->roam_at = jiffies; - } else + } else { /* there is another entry, we can simply delete this * one and can still use the other one. */ batadv_tt_global_del_orig_node(bat_priv, tt_global_entry, orig_node, message); + } } /** @@ -2314,10 +2315,11 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv, /* local entry exists, case 2: client roamed to us. */ batadv_tt_global_del_orig_list(tt_global_entry); batadv_tt_global_free(bat_priv, tt_global_entry, message); - } else + } else { /* no local entry exists, case 1: check for roaming */ batadv_tt_global_del_roaming(bat_priv, tt_global_entry, orig_node, message); + } out: if (tt_global_entry) -- cgit From a09c94d07becd85b5be57074fd4553e197ce97a4 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 22 Feb 2017 17:16:41 +0100 Subject: batman-adv: Fix possible side-effects in _batadv_dbg An argument of a macro should not be evaluated multiple times. Otherwise embedded operations in these arguments will be executed multiple times. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/log.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/log.h b/net/batman-adv/log.h index 7a2b9f4da078..65ce97efa6b5 100644 --- a/net/batman-adv/log.h +++ b/net/batman-adv/log.h @@ -73,9 +73,10 @@ __printf(2, 3); /* possibly ratelimited debug output */ #define _batadv_dbg(type, bat_priv, ratelimited, fmt, arg...) \ do { \ - if (atomic_read(&(bat_priv)->log_level) & (type) && \ + struct batadv_priv *__batpriv = (bat_priv); \ + if (atomic_read(&__batpriv->log_level) & (type) && \ (!(ratelimited) || net_ratelimit())) \ - batadv_debug_log(bat_priv, fmt, ## arg); \ + batadv_debug_log(__batpriv, fmt, ## arg); \ } \ while (0) #else /* !CONFIG_BATMAN_ADV_DEBUG */ -- cgit From f7a2bd6544657f125d2c8a8b4f231a8cd225ee8f Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 22 Feb 2017 17:16:42 +0100 Subject: batman-adv: Convert BATADV_PRINT_VID macro to function The BATADV_PRINT_VID is not free of of possible side-effects. This can be avoided when the the macro is converted to a simple inline function. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bridge_loop_avoidance.c | 38 +++++++++++++++++----------------- net/batman-adv/distributed-arp-table.c | 6 +++--- net/batman-adv/main.h | 16 ++++++++++++-- net/batman-adv/translation-table.c | 36 ++++++++++++++++---------------- 4 files changed, 54 insertions(+), 42 deletions(-) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index ba8420d8a992..7332d284b0fc 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -395,7 +395,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, u8 *mac, ether_addr_copy(ethhdr->h_source, mac); batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_send_claim(): CLAIM %pM on vid %d\n", mac, - BATADV_PRINT_VID(vid)); + batadv_print_vid(vid)); break; case BATADV_CLAIM_TYPE_UNCLAIM: /* unclaim frame @@ -404,7 +404,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, u8 *mac, ether_addr_copy(hw_src, mac); batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_send_claim(): UNCLAIM %pM on vid %d\n", mac, - BATADV_PRINT_VID(vid)); + batadv_print_vid(vid)); break; case BATADV_CLAIM_TYPE_ANNOUNCE: /* announcement frame @@ -413,7 +413,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, u8 *mac, ether_addr_copy(hw_src, mac); batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_send_claim(): ANNOUNCE of %pM on vid %d\n", - ethhdr->h_source, BATADV_PRINT_VID(vid)); + ethhdr->h_source, batadv_print_vid(vid)); break; case BATADV_CLAIM_TYPE_REQUEST: /* request frame @@ -425,14 +425,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, u8 *mac, batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_send_claim(): REQUEST of %pM to %pM on vid %d\n", ethhdr->h_source, ethhdr->h_dest, - BATADV_PRINT_VID(vid)); + batadv_print_vid(vid)); break; case BATADV_CLAIM_TYPE_LOOPDETECT: ether_addr_copy(ethhdr->h_source, mac); batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_send_claim(): LOOPDETECT of %pM to %pM on vid %d\n", ethhdr->h_source, ethhdr->h_dest, - BATADV_PRINT_VID(vid)); + batadv_print_vid(vid)); break; } @@ -475,9 +475,9 @@ static void batadv_bla_loopdetect_report(struct work_struct *work) batadv_info(bat_priv->soft_iface, "Possible loop on VLAN %d detected which can't be handled by BLA - please check your network setup!\n", - BATADV_PRINT_VID(backbone_gw->vid)); + batadv_print_vid(backbone_gw->vid)); snprintf(vid_str, sizeof(vid_str), "%d", - BATADV_PRINT_VID(backbone_gw->vid)); + batadv_print_vid(backbone_gw->vid)); vid_str[sizeof(vid_str) - 1] = 0; batadv_throw_uevent(bat_priv, BATADV_UEV_BLA, BATADV_UEV_LOOPDETECT, @@ -510,7 +510,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, u8 *orig, batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_get_backbone_gw(): not found (%pM, %d), creating new entry\n", - orig, BATADV_PRINT_VID(vid)); + orig, batadv_print_vid(vid)); entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) @@ -719,7 +719,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_add_claim(): adding new entry %pM, vid %d to hash ...\n", - mac, BATADV_PRINT_VID(vid)); + mac, batadv_print_vid(vid)); kref_get(&claim->refcount); hash_added = batadv_hash_add(bat_priv->bla.claim_hash, @@ -740,7 +740,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_add_claim(): changing ownership for %pM, vid %d\n", - mac, BATADV_PRINT_VID(vid)); + mac, batadv_print_vid(vid)); remove_crc = true; } @@ -809,7 +809,7 @@ static void batadv_bla_del_claim(struct batadv_priv *bat_priv, return; batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_del_claim(): %pM, vid %d\n", - mac, BATADV_PRINT_VID(vid)); + mac, batadv_print_vid(vid)); batadv_hash_remove(bat_priv->bla.claim_hash, batadv_compare_claim, batadv_choose_claim, claim); @@ -849,7 +849,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, batadv_dbg(BATADV_DBG_BLA, bat_priv, "handle_announce(): ANNOUNCE vid %d (sent by %pM)... CRC = %#.4x\n", - BATADV_PRINT_VID(vid), backbone_gw->orig, crc); + batadv_print_vid(vid), backbone_gw->orig, crc); spin_lock_bh(&backbone_gw->crc_lock); backbone_crc = backbone_gw->crc; @@ -859,7 +859,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv, "handle_announce(): CRC FAILED for %pM/%d (my = %#.4x, sent = %#.4x)\n", backbone_gw->orig, - BATADV_PRINT_VID(backbone_gw->vid), + batadv_print_vid(backbone_gw->vid), backbone_crc, crc); batadv_bla_send_request(backbone_gw); @@ -904,7 +904,7 @@ static bool batadv_handle_request(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_BLA, bat_priv, "handle_request(): REQUEST vid %d (sent by %pM)...\n", - BATADV_PRINT_VID(vid), ethhdr->h_source); + batadv_print_vid(vid), ethhdr->h_source); batadv_bla_answer_request(bat_priv, primary_if, vid); return true; @@ -941,7 +941,7 @@ static bool batadv_handle_unclaim(struct batadv_priv *bat_priv, /* this must be an UNCLAIM frame */ batadv_dbg(BATADV_DBG_BLA, bat_priv, "handle_unclaim(): UNCLAIM %pM on vid %d (sent by %pM)...\n", - claim_addr, BATADV_PRINT_VID(vid), backbone_gw->orig); + claim_addr, batadv_print_vid(vid), backbone_gw->orig); batadv_bla_del_claim(bat_priv, claim_addr, vid); batadv_backbone_gw_put(backbone_gw); @@ -1161,7 +1161,7 @@ static bool batadv_bla_process_claim(struct batadv_priv *bat_priv, if (ret == 1) batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_process_claim(): received a claim frame from another group. From: %pM on vid %d ...(hw_src %pM, hw_dst %pM)\n", - ethhdr->h_source, BATADV_PRINT_VID(vid), hw_src, + ethhdr->h_source, batadv_print_vid(vid), hw_src, hw_dst); if (ret < 2) @@ -1197,7 +1197,7 @@ static bool batadv_bla_process_claim(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_process_claim(): ERROR - this looks like a claim frame, but is useless. eth src %pM on vid %d ...(hw_src %pM, hw_dst %pM)\n", - ethhdr->h_source, BATADV_PRINT_VID(vid), hw_src, hw_dst); + ethhdr->h_source, batadv_print_vid(vid), hw_src, hw_dst); return true; } @@ -2042,7 +2042,7 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset) backbone_crc = backbone_gw->crc; spin_unlock_bh(&backbone_gw->crc_lock); seq_printf(seq, " * %pM on %5d by %pM [%c] (%#.4x)\n", - claim->addr, BATADV_PRINT_VID(claim->vid), + claim->addr, batadv_print_vid(claim->vid), backbone_gw->orig, (is_own ? 'x' : ' '), backbone_crc); @@ -2274,7 +2274,7 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset) seq_printf(seq, " * %pM on %5d %4i.%03is (%#.4x)\n", backbone_gw->orig, - BATADV_PRINT_VID(backbone_gw->vid), secs, + batadv_print_vid(backbone_gw->vid), secs, msecs, backbone_crc); } rcu_read_unlock(); diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 1bfd1dbc2feb..4f643fdb5bc6 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -330,7 +330,7 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip, batadv_dbg(BATADV_DBG_DAT, bat_priv, "Entry updated: %pI4 %pM (vid: %d)\n", &dat_entry->ip, dat_entry->mac_addr, - BATADV_PRINT_VID(vid)); + batadv_print_vid(vid)); goto out; } @@ -356,7 +356,7 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip, } batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM (vid: %d)\n", - &dat_entry->ip, dat_entry->mac_addr, BATADV_PRINT_VID(vid)); + &dat_entry->ip, dat_entry->mac_addr, batadv_print_vid(vid)); out: if (dat_entry) @@ -835,7 +835,7 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset) seq_printf(seq, " * %15pI4 %14pM %4i %6i:%02i\n", &dat_entry->ip, dat_entry->mac_addr, - BATADV_PRINT_VID(dat_entry->vid), + batadv_print_vid(dat_entry->vid), last_seen_mins, last_seen_secs); } rcu_read_unlock(); diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index f87b17c613a6..810f7d026f54 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -193,6 +193,7 @@ enum batadv_uev_type { #include #include +#include "packet.h" #include "types.h" struct net_device; @@ -200,8 +201,19 @@ struct packet_type; struct seq_file; struct sk_buff; -#define BATADV_PRINT_VID(vid) (((vid) & BATADV_VLAN_HAS_TAG) ? \ - (int)((vid) & VLAN_VID_MASK) : -1) +/** + * batadv_print_vid - return printable version of vid information + * @vid: the VLAN identifier + * + * Return: -1 when no VLAN is used, VLAN id otherwise + */ +static inline int batadv_print_vid(unsigned short vid) +{ + if (vid & BATADV_VLAN_HAS_TAG) + return (int)(vid & VLAN_VID_MASK); + else + return -1; +} extern struct list_head batadv_hardif_list; diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index a75f0a640bcd..e75b4937b497 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -617,7 +617,7 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_TT, bat_priv, "Deleting global tt entry %pM (vid: %d): %s\n", tt_global->common.addr, - BATADV_PRINT_VID(tt_global->common.vid), message); + batadv_print_vid(tt_global->common.vid), message); batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt, batadv_choose_tt, &tt_global->common); @@ -671,7 +671,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr, if (tt_local->common.flags & BATADV_TT_CLIENT_PENDING) { batadv_dbg(BATADV_DBG_TT, bat_priv, "Re-adding pending client %pM (vid: %d)\n", - addr, BATADV_PRINT_VID(vid)); + addr, batadv_print_vid(vid)); /* whatever the reason why the PENDING flag was set, * this is a client which was enqueued to be removed in * this orig_interval. Since it popped up again, the @@ -684,7 +684,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr, if (tt_local->common.flags & BATADV_TT_CLIENT_ROAM) { batadv_dbg(BATADV_DBG_TT, bat_priv, "Roaming client %pM (vid: %d) came back to its original location\n", - addr, BATADV_PRINT_VID(vid)); + addr, batadv_print_vid(vid)); /* the ROAM flag is set because this client roamed away * and the node got a roaming_advertisement message. Now * that the client popped up again at its original @@ -716,7 +716,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr, if (!vlan) { net_ratelimited_function(batadv_info, soft_iface, "adding TT local entry %pM to non-existent VLAN %d\n", - addr, BATADV_PRINT_VID(vid)); + addr, batadv_print_vid(vid)); kmem_cache_free(batadv_tl_cache, tt_local); tt_local = NULL; goto out; @@ -724,7 +724,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr, batadv_dbg(BATADV_DBG_TT, bat_priv, "Creating new local tt entry: %pM (vid: %d, ttvn: %d)\n", - addr, BATADV_PRINT_VID(vid), + addr, batadv_print_vid(vid), (u8)atomic_read(&bat_priv->tt.vn)); ether_addr_copy(tt_local->common.addr, addr); @@ -1097,7 +1097,7 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) seq_printf(seq, " * %pM %4i [%c%c%c%c%c%c] %3u.%03u (%#.8x)\n", tt_common_entry->addr, - BATADV_PRINT_VID(tt_common_entry->vid), + batadv_print_vid(tt_common_entry->vid), ((tt_common_entry->flags & BATADV_TT_CLIENT_ROAM) ? 'R' : '.'), no_purge ? 'P' : '.', @@ -1296,7 +1296,7 @@ batadv_tt_local_set_pending(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_TT, bat_priv, "Local tt entry (%pM, vid: %d) pending to be removed: %s\n", tt_local_entry->common.addr, - BATADV_PRINT_VID(tt_local_entry->common.vid), message); + batadv_print_vid(tt_local_entry->common.vid), message); } /** @@ -1727,7 +1727,7 @@ add_orig_entry: batadv_dbg(BATADV_DBG_TT, bat_priv, "Creating new global tt entry: %pM (vid: %d, via %pM)\n", - common->addr, BATADV_PRINT_VID(common->vid), + common->addr, batadv_print_vid(common->vid), orig_node->orig); ret = true; @@ -1835,7 +1835,7 @@ batadv_tt_global_print_entry(struct batadv_priv *bat_priv, if (!vlan) { seq_printf(seq, " * Cannot retrieve VLAN %d for originator %pM\n", - BATADV_PRINT_VID(tt_common_entry->vid), + batadv_print_vid(tt_common_entry->vid), best_entry->orig_node->orig); goto print_list; } @@ -1844,7 +1844,7 @@ batadv_tt_global_print_entry(struct batadv_priv *bat_priv, seq_printf(seq, " %c %pM %4i (%3u) via %pM (%3u) (%#.8x) [%c%c%c%c]\n", '*', tt_global_entry->common.addr, - BATADV_PRINT_VID(tt_global_entry->common.vid), + batadv_print_vid(tt_global_entry->common.vid), best_entry->ttvn, best_entry->orig_node->orig, last_ttvn, vlan->tt.crc, ((flags & BATADV_TT_CLIENT_ROAM) ? 'R' : '.'), @@ -1867,7 +1867,7 @@ print_list: if (!vlan) { seq_printf(seq, " + Cannot retrieve VLAN %d for originator %pM\n", - BATADV_PRINT_VID(tt_common_entry->vid), + batadv_print_vid(tt_common_entry->vid), orig_entry->orig_node->orig); continue; } @@ -1876,7 +1876,7 @@ print_list: seq_printf(seq, " %c %pM %4d (%3u) via %pM (%3u) (%#.8x) [%c%c%c%c]\n", '+', tt_global_entry->common.addr, - BATADV_PRINT_VID(tt_global_entry->common.vid), + batadv_print_vid(tt_global_entry->common.vid), orig_entry->ttvn, orig_entry->orig_node->orig, last_ttvn, vlan->tt.crc, ((flags & BATADV_TT_CLIENT_ROAM) ? 'R' : '.'), @@ -2213,7 +2213,7 @@ batadv_tt_global_del_orig_node(struct batadv_priv *bat_priv, "Deleting %pM from global tt entry %pM (vid: %d): %s\n", orig_node->orig, tt_global_entry->common.addr, - BATADV_PRINT_VID(vid), message); + batadv_print_vid(vid), message); _batadv_tt_global_del_orig_entry(tt_global_entry, orig_entry); } @@ -2377,7 +2377,7 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_TT, bat_priv, "Deleting global tt entry %pM (vid: %d): %s\n", tt_global->common.addr, - BATADV_PRINT_VID(vid), message); + batadv_print_vid(vid), message); hlist_del_rcu(&tt_common_entry->hash_entry); batadv_tt_global_entry_put(tt_global); } @@ -2437,7 +2437,7 @@ static void batadv_tt_global_purge(struct batadv_priv *bat_priv) batadv_dbg(BATADV_DBG_TT, bat_priv, "Deleting global tt entry %pM (vid: %d): %s\n", tt_global->common.addr, - BATADV_PRINT_VID(tt_global->common.vid), + batadv_print_vid(tt_global->common.vid), msg); hlist_del_rcu(&tt_common->hash_entry); @@ -3652,7 +3652,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, u8 *client, batadv_dbg(BATADV_DBG_TT, bat_priv, "Sending ROAMING_ADV to %pM (client %pM, vid: %d)\n", - orig_node->orig, client, BATADV_PRINT_VID(vid)); + orig_node->orig, client, batadv_print_vid(vid)); batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_TX); @@ -3775,7 +3775,7 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) batadv_dbg(BATADV_DBG_TT, bat_priv, "Deleting local tt entry (%pM, vid: %d): pending\n", tt_common->addr, - BATADV_PRINT_VID(tt_common->vid)); + batadv_print_vid(tt_common->vid)); batadv_tt_local_size_dec(bat_priv, tt_common->vid); hlist_del_rcu(&tt_common->hash_entry); @@ -4019,7 +4019,7 @@ bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_TT, bat_priv, "Added temporary global client (addr: %pM, vid: %d, orig: %pM)\n", - addr, BATADV_PRINT_VID(vid), orig_node->orig); + addr, batadv_print_vid(vid), orig_node->orig); ret = true; out: return ret; -- cgit From 5080f39e8c72e01cf37e8359023e7018e2a4901e Mon Sep 17 00:00:00 2001 From: Nik Unger Date: Mon, 13 Mar 2017 10:16:58 -0700 Subject: netem: apply correct delay when rate throttling I recently reported on the netem list that iperf network benchmarks show unexpected results when a bandwidth throttling rate has been configured for netem. Specifically: 1) The measured link bandwidth *increases* when a higher delay is added 2) The measured link bandwidth appears higher than the specified limit 3) The measured link bandwidth for the same very slow settings varies significantly across machines The issue can be reproduced by using tc to configure netem with a 512kbit rate and various (none, 1us, 50ms, 100ms, 200ms) delays on a veth pair between network namespaces, and then using iperf (or any other network benchmarking tool) to test throughput. Complete detailed instructions are in the original email chain here: https://lists.linuxfoundation.org/pipermail/netem/2017-February/001672.html There appear to be two underlying bugs causing these effects: - The first issue causes long delays when the rate is slow and no delay is configured (e.g., "rate 512kbit"). This is because SKBs are not orphaned when no delay is configured, so orphaning does not occur until *after* the rate-induced delay has been applied. For this reason, adding a tiny delay (e.g., "rate 512kbit delay 1us") dramatically increases the measured bandwidth. - The second issue is that rate-induced delays are not correctly applied, allowing SKB delays to occur in parallel. The indended approach is to compute the delay for an SKB and to add this delay to the end of the current queue. However, the code does not detect existing SKBs in the queue due to improperly testing sch->q.qlen, which is nonzero even when packets exist only in the rbtree. Consequently, new SKBs do not wait for the current queue to empty. When packet delays vary significantly (e.g., if packet sizes are different), then this also causes unintended reordering. I modified the code to expect a delay (and orphan the SKB) when a rate is configured. I also added some defensive tests that correctly find the latest scheduled delivery time, even if it is (unexpectedly) for a packet in sch->q. I have tested these changes on the latest kernel (4.11.0-rc1+) and the iperf / ping test results are as expected. Signed-off-by: Nik Unger Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/sched/sch_netem.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index c8bb62a1e744..94b4928ad413 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -462,7 +462,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, /* If a delay is expected, orphan the skb. (orphaning usually takes * place at TX completion time, so _before_ the link transit delay) */ - if (q->latency || q->jitter) + if (q->latency || q->jitter || q->rate) skb_orphan_partial(skb); /* @@ -530,21 +530,31 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, now = psched_get_time(); if (q->rate) { - struct sk_buff *last; + struct netem_skb_cb *last = NULL; + + if (sch->q.tail) + last = netem_skb_cb(sch->q.tail); + if (q->t_root.rb_node) { + struct sk_buff *t_skb; + struct netem_skb_cb *t_last; + + t_skb = netem_rb_to_skb(rb_last(&q->t_root)); + t_last = netem_skb_cb(t_skb); + if (!last || + t_last->time_to_send > last->time_to_send) { + last = t_last; + } + } - if (sch->q.qlen) - last = sch->q.tail; - else - last = netem_rb_to_skb(rb_last(&q->t_root)); if (last) { /* * Last packet in queue is reference point (now), * calculate this time bonus and subtract * from delay. */ - delay -= netem_skb_cb(last)->time_to_send - now; + delay -= last->time_to_send - now; delay = max_t(psched_tdiff_t, 0, delay); - now = netem_skb_cb(last)->time_to_send; + now = last->time_to_send; } delay += packet_len_2_sched_time(qdisc_pkt_len(skb), q); -- cgit From b229487b36a418c755eab6485aae74582d52d4a4 Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Mon, 13 Mar 2017 12:07:32 -0700 Subject: liquidio: remove/replace invalid code Remove invalid call to dma_sync_single_for_cpu() because previous DMA allocation was coherent--not streaming. Remove code that references fields in struct list_head; replace it with calls to list_empty() and list_first_entry(). Also, add comment to clarify complicated if statement. Signed-off-by: Rick Farrington Signed-off-by: Felix Manlunas Signed-off-by: Derek Chickles Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/response_manager.c | 26 +++++++++------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/response_manager.c b/drivers/net/ethernet/cavium/liquidio/response_manager.c index 2fbaae96b505..1d987d7cd7bd 100644 --- a/drivers/net/ethernet/cavium/liquidio/response_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/response_manager.c @@ -69,41 +69,35 @@ int lio_process_ordered_list(struct octeon_device *octeon_dev, int resp_to_process = MAX_ORD_REQS_TO_PROCESS; u32 status; u64 status64; - struct octeon_instr_rdp *rdp; - u64 rptr; ordered_sc_list = &octeon_dev->response_list[OCTEON_ORDERED_SC_LIST]; do { spin_lock_bh(&ordered_sc_list->lock); - if (ordered_sc_list->head.next == &ordered_sc_list->head) { + if (list_empty(&ordered_sc_list->head)) { spin_unlock_bh(&ordered_sc_list->lock); return 1; } - sc = (struct octeon_soft_command *)ordered_sc_list-> - head.next; - if (OCTEON_CN23XX_PF(octeon_dev) || - OCTEON_CN23XX_VF(octeon_dev)) { - rdp = (struct octeon_instr_rdp *)&sc->cmd.cmd3.rdp; - rptr = sc->cmd.cmd3.rptr; - } else { - rdp = (struct octeon_instr_rdp *)&sc->cmd.cmd2.rdp; - rptr = sc->cmd.cmd2.rptr; - } + sc = list_first_entry(&ordered_sc_list->head, + struct octeon_soft_command, node); status = OCTEON_REQUEST_PENDING; /* check if octeon has finished DMA'ing a response * to where rptr is pointing to */ - dma_sync_single_for_cpu(&octeon_dev->pci_dev->dev, - rptr, rdp->rlen, - DMA_FROM_DEVICE); status64 = *sc->status_word; if (status64 != COMPLETION_WORD_INIT) { + /* This logic ensures that all 64b have been written. + * 1. check byte 0 for non-FF + * 2. if non-FF, then swap result from BE to host order + * 3. check byte 7 (swapped to 0) for non-FF + * 4. if non-FF, use the low 32-bit status code + * 5. if either byte 0 or byte 7 is FF, don't use status + */ if ((status64 & 0xff) != 0xff) { octeon_swap_8B_data(&status64, 1); if (((status64 & 0xff) != 0xff)) { -- cgit From 0c88a76148432097aa9859bcd08ad07c05507725 Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Mon, 13 Mar 2017 12:58:04 -0700 Subject: liquidio: use meaningful names for IRQs All IRQs owned by the PF and VF drivers share the same nondescript name "octeon"; this makes it difficult to setup interrupt affinity. Change the IRQ names to reflect their specific purpose: LiquidIO--- Examples: LiquidIO0-pf0-rxtx-3 LiquidIO1-vf1-rxtx-0 LiquidIO0-pf0-aux We cannot use netdev->name for naming the IRQs because: 1. Early during init, the PF and VF drivers require interrupts to send/receive control data from the NIC firmware; so the PF and VF must request IRQs long before the netdev struct is registered. 2. The IRQ name can only be specified at the time it is requested. It cannot be changed after that. Signed-off-by: Rick Farrington Signed-off-by: Felix Manlunas Signed-off-by: Satanand Burla Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 72 ++++++++++++++++++---- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 37 +++++++++-- .../net/ethernet/cavium/liquidio/liquidio_common.h | 5 ++ .../net/ethernet/cavium/liquidio/octeon_device.h | 3 + 4 files changed, 101 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index ca529a78bca7..761061b96948 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -1084,16 +1084,35 @@ static int octeon_setup_interrupt(struct octeon_device *oct) int i; int num_ioq_vectors; int num_alloc_ioq_vectors; + char *queue_irq_names = NULL; + char *aux_irq_name = NULL; if (OCTEON_CN23XX_PF(oct) && oct->msix_on) { oct->num_msix_irqs = oct->sriov_info.num_pf_rings; /* one non ioq interrupt for handling sli_mac_pf_int_sum */ oct->num_msix_irqs += 1; + /* allocate storage for the names assigned to each irq */ + oct->irq_name_storage = + kcalloc((MAX_IOQ_INTERRUPTS_PER_PF + 1), INTRNAMSIZ, + GFP_KERNEL); + if (!oct->irq_name_storage) { + dev_err(&oct->pci_dev->dev, "Irq name storage alloc failed...\n"); + return -ENOMEM; + } + + queue_irq_names = oct->irq_name_storage; + aux_irq_name = &queue_irq_names + [IRQ_NAME_OFF(MAX_IOQ_INTERRUPTS_PER_PF)]; + oct->msix_entries = kcalloc( oct->num_msix_irqs, sizeof(struct msix_entry), GFP_KERNEL); - if (!oct->msix_entries) - return 1; + if (!oct->msix_entries) { + dev_err(&oct->pci_dev->dev, "Memory Alloc failed...\n"); + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return -ENOMEM; + } msix_entries = (struct msix_entry *)oct->msix_entries; /*Assumption is that pf msix vectors start from pf srn to pf to @@ -1111,7 +1130,9 @@ static int octeon_setup_interrupt(struct octeon_device *oct) dev_err(&oct->pci_dev->dev, "unable to Allocate MSI-X interrupts\n"); kfree(oct->msix_entries); oct->msix_entries = NULL; - return 1; + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return num_alloc_ioq_vectors; } dev_dbg(&oct->pci_dev->dev, "OCTEON: Enough MSI-X interrupts are allocated...\n"); @@ -1119,9 +1140,12 @@ static int octeon_setup_interrupt(struct octeon_device *oct) /** For PF, there is one non-ioq interrupt handler */ num_ioq_vectors -= 1; + + snprintf(aux_irq_name, INTRNAMSIZ, + "LiquidIO%u-pf%u-aux", oct->octeon_id, oct->pf_num); irqret = request_irq(msix_entries[num_ioq_vectors].vector, - liquidio_legacy_intr_handler, 0, "octeon", - oct); + liquidio_legacy_intr_handler, 0, + aux_irq_name, oct); if (irqret) { dev_err(&oct->pci_dev->dev, "OCTEON: Request_irq failed for MSIX interrupt Error: %d\n", @@ -1129,13 +1153,20 @@ static int octeon_setup_interrupt(struct octeon_device *oct) pci_disable_msix(oct->pci_dev); kfree(oct->msix_entries); oct->msix_entries = NULL; - return 1; + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return irqret; } for (i = 0; i < num_ioq_vectors; i++) { + snprintf(&queue_irq_names[IRQ_NAME_OFF(i)], INTRNAMSIZ, + "LiquidIO%u-pf%u-rxtx-%u", + oct->octeon_id, oct->pf_num, i); + irqret = request_irq(msix_entries[i].vector, liquidio_msix_intr_handler, 0, - "octeon", &oct->ioq_vector[i]); + &queue_irq_names[IRQ_NAME_OFF(i)], + &oct->ioq_vector[i]); if (irqret) { dev_err(&oct->pci_dev->dev, "OCTEON: Request_irq failed for MSIX interrupt Error: %d\n", @@ -1155,7 +1186,9 @@ static int octeon_setup_interrupt(struct octeon_device *oct) pci_disable_msix(oct->pci_dev); kfree(oct->msix_entries); oct->msix_entries = NULL; - return 1; + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return irqret; } oct->ioq_vector[i].vector = msix_entries[i].vector; /* assign the cpu mask for this msix interrupt vector */ @@ -1173,15 +1206,29 @@ static int octeon_setup_interrupt(struct octeon_device *oct) else oct->flags |= LIO_FLAG_MSI_ENABLED; + /* allocate storage for the names assigned to the irq */ + oct->irq_name_storage = kcalloc(1, INTRNAMSIZ, GFP_KERNEL); + if (!oct->irq_name_storage) + return -ENOMEM; + + queue_irq_names = oct->irq_name_storage; + + snprintf(&queue_irq_names[IRQ_NAME_OFF(0)], INTRNAMSIZ, + "LiquidIO%u-pf%u-rxtx-%u", + oct->octeon_id, oct->pf_num, 0); + irqret = request_irq(oct->pci_dev->irq, - liquidio_legacy_intr_handler, IRQF_SHARED, - "octeon", oct); + liquidio_legacy_intr_handler, + IRQF_SHARED, + &queue_irq_names[IRQ_NAME_OFF(0)], oct); if (irqret) { if (oct->flags & LIO_FLAG_MSI_ENABLED) pci_disable_msi(oct->pci_dev); dev_err(&oct->pci_dev->dev, "Request IRQ failed with code: %d\n", irqret); - return 1; + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return irqret; } } return 0; @@ -1449,6 +1496,9 @@ static void octeon_destroy_resources(struct octeon_device *oct) pci_disable_msi(oct->pci_dev); } + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + /* fallthrough */ case OCT_DEV_MSIX_ALLOC_VECTOR_DONE: if (OCTEON_CN23XX_PF(oct)) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 7b83be4ce1fe..5ec5c24cee5d 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -780,6 +780,7 @@ liquidio_msix_intr_handler(int irq __attribute__((unused)), void *dev) static int octeon_setup_interrupt(struct octeon_device *oct) { struct msix_entry *msix_entries; + char *queue_irq_names = NULL; int num_alloc_ioq_vectors; int num_ioq_vectors; int irqret; @@ -788,10 +789,25 @@ static int octeon_setup_interrupt(struct octeon_device *oct) if (oct->msix_on) { oct->num_msix_irqs = oct->sriov_info.rings_per_vf; + /* allocate storage for the names assigned to each irq */ + oct->irq_name_storage = + kcalloc(MAX_IOQ_INTERRUPTS_PER_VF, INTRNAMSIZ, + GFP_KERNEL); + if (!oct->irq_name_storage) { + dev_err(&oct->pci_dev->dev, "Irq name storage alloc failed...\n"); + return -ENOMEM; + } + + queue_irq_names = oct->irq_name_storage; + oct->msix_entries = kcalloc( oct->num_msix_irqs, sizeof(struct msix_entry), GFP_KERNEL); - if (!oct->msix_entries) - return 1; + if (!oct->msix_entries) { + dev_err(&oct->pci_dev->dev, "Memory Alloc failed...\n"); + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return -ENOMEM; + } msix_entries = (struct msix_entry *)oct->msix_entries; @@ -805,16 +821,23 @@ static int octeon_setup_interrupt(struct octeon_device *oct) dev_err(&oct->pci_dev->dev, "unable to Allocate MSI-X interrupts\n"); kfree(oct->msix_entries); oct->msix_entries = NULL; - return 1; + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return num_alloc_ioq_vectors; } dev_dbg(&oct->pci_dev->dev, "OCTEON: Enough MSI-X interrupts are allocated...\n"); num_ioq_vectors = oct->num_msix_irqs; for (i = 0; i < num_ioq_vectors; i++) { + snprintf(&queue_irq_names[IRQ_NAME_OFF(i)], INTRNAMSIZ, + "LiquidIO%u-vf%u-rxtx-%u", + oct->octeon_id, oct->vf_num, i); + irqret = request_irq(msix_entries[i].vector, liquidio_msix_intr_handler, 0, - "octeon", &oct->ioq_vector[i]); + &queue_irq_names[IRQ_NAME_OFF(i)], + &oct->ioq_vector[i]); if (irqret) { dev_err(&oct->pci_dev->dev, "OCTEON: Request_irq failed for MSIX interrupt Error: %d\n", @@ -830,7 +853,9 @@ static int octeon_setup_interrupt(struct octeon_device *oct) pci_disable_msix(oct->pci_dev); kfree(oct->msix_entries); oct->msix_entries = NULL; - return 1; + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return irqret; } oct->ioq_vector[i].vector = msix_entries[i].vector; /* assign the cpu mask for this msix interrupt vector */ @@ -975,6 +1000,8 @@ static void octeon_destroy_resources(struct octeon_device *oct) pci_disable_msix(oct->pci_dev); kfree(oct->msix_entries); oct->msix_entries = NULL; + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; } /* Soft reset the octeon device before exiting */ if (oct->pci_dev->reset_fn) diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index 294c6f3c6b48..4a07c0ac9fab 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -100,6 +100,11 @@ enum octeon_tag_type { #define BYTES_PER_DHLEN_UNIT 8 #define MAX_REG_CNT 2000000U +#define INTRNAMSIZ 32 +#define IRQ_NAME_OFF(i) ((i) * INTRNAMSIZ) +#define MAX_IOQ_INTERRUPTS_PER_PF (64 * 2) +#define MAX_IOQ_INTERRUPTS_PER_VF (8 * 2) + static inline u32 incr_index(u32 index, u32 count, u32 max) { diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index c301a3852482..8c5d33e53cfa 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -517,6 +517,9 @@ struct octeon_device { void *msix_entries; + /* when requesting IRQs, the names are stored here */ + void *irq_name_storage; + struct octeon_sriov_info sriov_info; struct octeon_pf_vf_hs_word pfvf_hsword; -- cgit From be7164cd579edab53edd184382b0bcd35f3bcc0a Mon Sep 17 00:00:00 2001 From: chun Long Date: Tue, 14 Mar 2017 15:26:24 +0800 Subject: tcp_westwood: fix tcp_westwood_info() style mistakes replace comma to semi colons in tcp_westwood_info(). Acked-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/ipv4/tcp_westwood.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c index fed66dc0e0f5..9775453b8d17 100644 --- a/net/ipv4/tcp_westwood.c +++ b/net/ipv4/tcp_westwood.c @@ -265,8 +265,8 @@ static size_t tcp_westwood_info(struct sock *sk, u32 ext, int *attr, if (ext & (1 << (INET_DIAG_VEGASINFO - 1))) { info->vegas.tcpv_enabled = 1; info->vegas.tcpv_rttcnt = 0; - info->vegas.tcpv_rtt = jiffies_to_usecs(ca->rtt), - info->vegas.tcpv_minrtt = jiffies_to_usecs(ca->rtt_min), + info->vegas.tcpv_rtt = jiffies_to_usecs(ca->rtt); + info->vegas.tcpv_minrtt = jiffies_to_usecs(ca->rtt_min); *attr = INET_DIAG_VEGASINFO; return sizeof(struct tcpvegas_info); -- cgit From 19ddde1eeca1ee81f4add5e04da66055e09281ac Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Tue, 14 Mar 2017 11:48:32 -0400 Subject: bonding: add 802.3ad support for 25G speeds Cut-n-paste enablement of 802.3ad bonding on 25G NICs, which currently report 0 as their bandwidth. CC: Jay Vosburgh CC: Veaceslav Falico CC: Andy Gospodarek CC: netdev@vger.kernel.org Signed-off-by: Jarod Wilson Acked-by: Andy Gospodarek Signed-off-by: David S. Miller --- drivers/net/bonding/bond_3ad.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 431926bba9f4..508713b4e533 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -92,6 +92,7 @@ enum ad_link_speed_type { AD_LINK_SPEED_2500MBPS, AD_LINK_SPEED_10000MBPS, AD_LINK_SPEED_20000MBPS, + AD_LINK_SPEED_25000MBPS, AD_LINK_SPEED_40000MBPS, AD_LINK_SPEED_56000MBPS, AD_LINK_SPEED_100000MBPS, @@ -260,6 +261,7 @@ static inline int __check_agg_selection_timer(struct port *port) * %AD_LINK_SPEED_2500MBPS, * %AD_LINK_SPEED_10000MBPS * %AD_LINK_SPEED_20000MBPS + * %AD_LINK_SPEED_25000MBPS * %AD_LINK_SPEED_40000MBPS * %AD_LINK_SPEED_56000MBPS * %AD_LINK_SPEED_100000MBPS @@ -302,6 +304,10 @@ static u16 __get_link_speed(struct port *port) speed = AD_LINK_SPEED_20000MBPS; break; + case SPEED_25000: + speed = AD_LINK_SPEED_25000MBPS; + break; + case SPEED_40000: speed = AD_LINK_SPEED_40000MBPS; break; @@ -707,6 +713,9 @@ static u32 __get_agg_bandwidth(struct aggregator *aggregator) case AD_LINK_SPEED_20000MBPS: bandwidth = nports * 20000; break; + case AD_LINK_SPEED_25000MBPS: + bandwidth = nports * 25000; + break; case AD_LINK_SPEED_40000MBPS: bandwidth = nports * 40000; break; -- cgit From 867fa150f8f7ee6e9e5a9ab768e2d0dc675a968b Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Tue, 14 Mar 2017 10:24:39 -0700 Subject: ldmvsw: better use of link up and down on ldom vswitch When an ldom VM is bound, the network vswitch infrastructure is set up for it, but was being forced 'UP' by the userland switch configuration script. When 'UP' but not actually connected to a running VM, the ipv6 neighbor probes fail (not a horrible thing) and start cluttering up the kernel logs. Funny thing: these are debug messages that never actually show up, but we do see the net_ratelimited messages that say N callbacks were suppressed. This patch defers the netif_carrier_on() until an actual link has been established with the VM, as indicated by receiving an LDC_EVENT_UP from the underlying LDC protocol. Similarly, we take the link down when we see the LDC_EVENT_RESET. Now when we see the ndo_open(), we reset the link to get things talking again. Orabug: 25525312 Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/ldmvsw.c | 27 +++++++++++++++++++++++---- drivers/net/ethernet/sun/sunvnet_common.c | 20 +++++++++++++++++--- drivers/net/ethernet/sun/sunvnet_common.h | 1 + 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/sun/ldmvsw.c b/drivers/net/ethernet/sun/ldmvsw.c index 89952deae47f..5a90fed06260 100644 --- a/drivers/net/ethernet/sun/ldmvsw.c +++ b/drivers/net/ethernet/sun/ldmvsw.c @@ -1,6 +1,6 @@ /* ldmvsw.c: Sun4v LDOM Virtual Switch Driver. * - * Copyright (C) 2016 Oracle. All rights reserved. + * Copyright (C) 2016-2017 Oracle. All rights reserved. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -41,8 +41,8 @@ static u8 vsw_port_hwaddr[ETH_ALEN] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; #define DRV_MODULE_NAME "ldmvsw" -#define DRV_MODULE_VERSION "1.1" -#define DRV_MODULE_RELDATE "February 3, 2017" +#define DRV_MODULE_VERSION "1.2" +#define DRV_MODULE_RELDATE "March 4, 2017" static char version[] = DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")"; @@ -123,6 +123,20 @@ static void vsw_set_rx_mode(struct net_device *dev) return sunvnet_set_rx_mode_common(dev, port->vp); } +int ldmvsw_open(struct net_device *dev) +{ + struct vnet_port *port = netdev_priv(dev); + struct vio_driver_state *vio = &port->vio; + + /* reset the channel */ + vio_link_state_change(vio, LDC_EVENT_RESET); + vnet_port_reset(port); + vio_port_up(vio); + + return 0; +} +EXPORT_SYMBOL_GPL(ldmvsw_open); + #ifdef CONFIG_NET_POLL_CONTROLLER static void vsw_poll_controller(struct net_device *dev) { @@ -133,7 +147,7 @@ static void vsw_poll_controller(struct net_device *dev) #endif static const struct net_device_ops vsw_ops = { - .ndo_open = sunvnet_open_common, + .ndo_open = ldmvsw_open, .ndo_stop = sunvnet_close_common, .ndo_set_rx_mode = vsw_set_rx_mode, .ndo_set_mac_address = sunvnet_set_mac_addr_common, @@ -365,6 +379,11 @@ static int vsw_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) napi_enable(&port->napi); vio_port_up(&port->vio); + /* assure no carrier until we receive an LDC_EVENT_UP, + * even if the vsw config script tries to force us up + */ + netif_carrier_off(dev); + netdev_info(dev, "LDOM vsw-port %pM\n", dev->dev_addr); pr_info("%s: PORT ( remote-mac %pM%s )\n", dev->name, diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index fa2d11ca9b81..fbd0f1e5210e 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -1,7 +1,7 @@ /* sunvnet.c: Sun LDOM Virtual Network Driver. * * Copyright (C) 2007, 2008 David S. Miller - * Copyright (C) 2016 Oracle. All rights reserved. + * Copyright (C) 2016-2017 Oracle. All rights reserved. */ #include @@ -43,7 +43,6 @@ MODULE_LICENSE("GPL"); MODULE_VERSION("1.1"); static int __vnet_tx_trigger(struct vnet_port *port, u32 start); -static void vnet_port_reset(struct vnet_port *port); static inline u32 vnet_tx_dring_avail(struct vio_dring_state *dr) { @@ -747,6 +746,13 @@ static int vnet_event_napi(struct vnet_port *port, int budget) /* RESET takes precedent over any other event */ if (port->rx_event & LDC_EVENT_RESET) { + /* a link went down */ + + if (port->vsw == 1) { + netif_tx_stop_all_queues(dev); + netif_carrier_off(dev); + } + vio_link_state_change(vio, LDC_EVENT_RESET); vnet_port_reset(port); vio_port_up(vio); @@ -766,6 +772,13 @@ static int vnet_event_napi(struct vnet_port *port, int budget) } if (port->rx_event & LDC_EVENT_UP) { + /* a link came up */ + + if (port->vsw == 1) { + netif_carrier_on(port->dev); + netif_tx_start_all_queues(port->dev); + } + vio_link_state_change(vio, LDC_EVENT_UP); port->rx_event = 0; return 0; @@ -1631,7 +1644,7 @@ void sunvnet_port_free_tx_bufs_common(struct vnet_port *port) } EXPORT_SYMBOL_GPL(sunvnet_port_free_tx_bufs_common); -static void vnet_port_reset(struct vnet_port *port) +void vnet_port_reset(struct vnet_port *port) { del_timer(&port->clean_timer); sunvnet_port_free_tx_bufs_common(port); @@ -1639,6 +1652,7 @@ static void vnet_port_reset(struct vnet_port *port) port->tso = (port->vsw == 0); /* no tso in vsw, misbehaves in bridge */ port->tsolen = 0; } +EXPORT_SYMBOL_GPL(vnet_port_reset); static int vnet_port_alloc_tx_ring(struct vnet_port *port) { diff --git a/drivers/net/ethernet/sun/sunvnet_common.h b/drivers/net/ethernet/sun/sunvnet_common.h index ce5c824128a3..b21ef4704bd1 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.h +++ b/drivers/net/ethernet/sun/sunvnet_common.h @@ -139,6 +139,7 @@ int sunvnet_handle_attr_common(struct vio_driver_state *vio, void *arg); void sunvnet_handshake_complete_common(struct vio_driver_state *vio); int sunvnet_poll_common(struct napi_struct *napi, int budget); void sunvnet_port_free_tx_bufs_common(struct vnet_port *port); +void vnet_port_reset(struct vnet_port *port); bool sunvnet_port_is_up_common(struct vnet_port *vnet); void sunvnet_port_add_txq_common(struct vnet_port *port); void sunvnet_port_rm_txq_common(struct vnet_port *port); -- cgit From 0f512c84544b9a8f8de53b6f4bc0c372c45d8693 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Tue, 14 Mar 2017 10:24:40 -0700 Subject: sunvnet: add stats to track ldom to ldom packets and bytes In this driver, there is a "port" created for the connection to each of the other ldoms; a netdev queue is mapped to each port, and they are collected under a single netdev. The generic netdev statistics show us all the traffic in and out of our network device, but don't show individual queue/port stats. This patch breaks out the traffic counts for the individual ports and gives us a little view into the state of those connections. Orabug: 25190537 Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunvnet.c | 116 +++++++++++++++++++++++++++++- drivers/net/ethernet/sun/sunvnet_common.c | 6 ++ drivers/net/ethernet/sun/sunvnet_common.h | 15 ++++ 3 files changed, 136 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index 4cc2571f71c6..0b95105f7060 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -1,7 +1,7 @@ /* sunvnet.c: Sun LDOM Virtual Network Driver. * * Copyright (C) 2007, 2008 David S. Miller - * Copyright (C) 2016 Oracle. All rights reserved. + * Copyright (C) 2016-2017 Oracle. All rights reserved. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -77,11 +77,125 @@ static void vnet_set_msglevel(struct net_device *dev, u32 value) vp->msg_enable = value; } +static const struct { + const char string[ETH_GSTRING_LEN]; +} ethtool_stats_keys[] = { + { "rx_packets" }, + { "tx_packets" }, + { "rx_bytes" }, + { "tx_bytes" }, + { "rx_errors" }, + { "tx_errors" }, + { "rx_dropped" }, + { "tx_dropped" }, + { "multicast" }, + { "rx_length_errors" }, + { "rx_frame_errors" }, + { "rx_missed_errors" }, + { "tx_carrier_errors" }, + { "nports" }, +}; + +static int vnet_get_sset_count(struct net_device *dev, int sset) +{ + struct vnet *vp = (struct vnet *)netdev_priv(dev); + + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(ethtool_stats_keys) + + (NUM_VNET_PORT_STATS * vp->nports); + default: + return -EOPNOTSUPP; + } +} + +static void vnet_get_strings(struct net_device *dev, u32 stringset, u8 *buf) +{ + struct vnet *vp = (struct vnet *)netdev_priv(dev); + struct vnet_port *port; + char *p = (char *)buf; + + switch (stringset) { + case ETH_SS_STATS: + memcpy(buf, ðtool_stats_keys, sizeof(ethtool_stats_keys)); + p += sizeof(ethtool_stats_keys); + + rcu_read_lock(); + list_for_each_entry_rcu(port, &vp->port_list, list) { + snprintf(p, ETH_GSTRING_LEN, "p%u.%s-%pM", + port->q_index, port->switch_port ? "s" : "q", + port->raddr); + p += ETH_GSTRING_LEN; + snprintf(p, ETH_GSTRING_LEN, "p%u.rx_packets", + port->q_index); + p += ETH_GSTRING_LEN; + snprintf(p, ETH_GSTRING_LEN, "p%u.tx_packets", + port->q_index); + p += ETH_GSTRING_LEN; + snprintf(p, ETH_GSTRING_LEN, "p%u.rx_bytes", + port->q_index); + p += ETH_GSTRING_LEN; + snprintf(p, ETH_GSTRING_LEN, "p%u.tx_bytes", + port->q_index); + p += ETH_GSTRING_LEN; + snprintf(p, ETH_GSTRING_LEN, "p%u.event_up", + port->q_index); + p += ETH_GSTRING_LEN; + snprintf(p, ETH_GSTRING_LEN, "p%u.event_reset", + port->q_index); + p += ETH_GSTRING_LEN; + } + rcu_read_unlock(); + break; + default: + WARN_ON(1); + break; + } +} + +static void vnet_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *estats, u64 *data) +{ + struct vnet *vp = (struct vnet *)netdev_priv(dev); + struct vnet_port *port; + int i = 0; + + data[i++] = dev->stats.rx_packets; + data[i++] = dev->stats.tx_packets; + data[i++] = dev->stats.rx_bytes; + data[i++] = dev->stats.tx_bytes; + data[i++] = dev->stats.rx_errors; + data[i++] = dev->stats.tx_errors; + data[i++] = dev->stats.rx_dropped; + data[i++] = dev->stats.tx_dropped; + data[i++] = dev->stats.multicast; + data[i++] = dev->stats.rx_length_errors; + data[i++] = dev->stats.rx_frame_errors; + data[i++] = dev->stats.rx_missed_errors; + data[i++] = dev->stats.tx_carrier_errors; + data[i++] = vp->nports; + + rcu_read_lock(); + list_for_each_entry_rcu(port, &vp->port_list, list) { + data[i++] = port->q_index; + data[i++] = port->stats.rx_packets; + data[i++] = port->stats.tx_packets; + data[i++] = port->stats.rx_bytes; + data[i++] = port->stats.tx_bytes; + data[i++] = port->stats.event_up; + data[i++] = port->stats.event_reset; + } + rcu_read_unlock(); +} + static const struct ethtool_ops vnet_ethtool_ops = { .get_drvinfo = vnet_get_drvinfo, .get_msglevel = vnet_get_msglevel, .set_msglevel = vnet_set_msglevel, .get_link = ethtool_op_get_link, + .get_sset_count = vnet_get_sset_count, + .get_strings = vnet_get_strings, + .get_ethtool_stats = vnet_get_ethtool_stats, }; static LIST_HEAD(vnet_list); diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index fbd0f1e5210e..7e95808f8227 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -411,6 +411,8 @@ static int vnet_rx_one(struct vnet_port *port, struct vio_net_desc *desc) dev->stats.rx_packets++; dev->stats.rx_bytes += len; + port->stats.rx_packets++; + port->stats.rx_bytes += len; napi_gro_receive(&port->napi, skb); return 0; @@ -768,6 +770,7 @@ static int vnet_event_napi(struct vnet_port *port, int budget) maybe_tx_wakeup(port); port->rx_event = 0; + port->stats.event_reset++; return 0; } @@ -781,6 +784,7 @@ static int vnet_event_napi(struct vnet_port *port, int budget) vio_link_state_change(vio, LDC_EVENT_UP); port->rx_event = 0; + port->stats.event_up++; return 0; } @@ -1430,6 +1434,8 @@ ldc_start_done: dev->stats.tx_packets++; dev->stats.tx_bytes += port->tx_bufs[txi].skb->len; + port->stats.tx_packets++; + port->stats.tx_bytes += port->tx_bufs[txi].skb->len; dr->prod = (dr->prod + 1) & (VNET_TX_RING_SIZE - 1); if (unlikely(vnet_tx_dring_avail(dr) < 1)) { diff --git a/drivers/net/ethernet/sun/sunvnet_common.h b/drivers/net/ethernet/sun/sunvnet_common.h index b21ef4704bd1..c0fac03cb87a 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.h +++ b/drivers/net/ethernet/sun/sunvnet_common.h @@ -35,6 +35,19 @@ struct vnet_tx_entry { struct vnet; +struct vnet_port_stats { + /* keep them all the same size */ + u32 rx_bytes; + u32 tx_bytes; + u32 rx_packets; + u32 tx_packets; + u32 event_up; + u32 event_reset; + u32 q_placeholder; +}; + +#define NUM_VNET_PORT_STATS (sizeof(struct vnet_port_stats) / sizeof(u32)) + /* Structure to describe a vnet-port or vsw-port in the MD. * If the vsw bit is set, this structure represents a vswitch * port, and the net_device can be found from ->dev. If the @@ -44,6 +57,8 @@ struct vnet; struct vnet_port { struct vio_driver_state vio; + struct vnet_port_stats stats; + struct hlist_node hash; u8 raddr[ETH_ALEN]; unsigned switch_port:1; -- cgit From e1f1e5f711265ee9d881afd12ff252b2d01e1174 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Tue, 14 Mar 2017 10:24:41 -0700 Subject: sunvnet: track port queues correctly Track our used and unused queue indexies correctly. Otherwise, as ports dropped out and returned, they all eventually ended up with the same queue index. Orabug: 25190537 Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunvnet_common.c | 24 ++++++++++++++++++++---- drivers/net/ethernet/sun/sunvnet_common.h | 11 ++--------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index 7e95808f8227..f8d1cc7e4977 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -1728,11 +1728,25 @@ EXPORT_SYMBOL_GPL(sunvnet_poll_controller_common); void sunvnet_port_add_txq_common(struct vnet_port *port) { struct vnet *vp = port->vp; - int n; + int smallest = 0; + int i; + + /* find the first least-used q + * When there are more ldoms than q's, we start to + * double up on ports per queue. + */ + for (i = 0; i < VNET_MAX_TXQS; i++) { + if (vp->q_used[i] == 0) { + smallest = i; + break; + } + if (vp->q_used[i] < vp->q_used[smallest]) + smallest = i; + } - n = vp->nports++; - n = n & (VNET_MAX_TXQS - 1); - port->q_index = n; + vp->nports++; + vp->q_used[smallest]++; + port->q_index = smallest; netif_tx_wake_queue(netdev_get_tx_queue(VNET_PORT_TO_NET_DEVICE(port), port->q_index)); } @@ -1743,5 +1757,7 @@ void sunvnet_port_rm_txq_common(struct vnet_port *port) port->vp->nports--; netif_tx_stop_queue(netdev_get_tx_queue(VNET_PORT_TO_NET_DEVICE(port), port->q_index)); + port->vp->q_used[port->q_index]--; + port->q_index = 0; } EXPORT_SYMBOL_GPL(sunvnet_port_rm_txq_common); diff --git a/drivers/net/ethernet/sun/sunvnet_common.h b/drivers/net/ethernet/sun/sunvnet_common.h index c0fac03cb87a..b20d6fa7ef25 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.h +++ b/drivers/net/ethernet/sun/sunvnet_common.h @@ -112,22 +112,15 @@ struct vnet_mcast_entry { }; struct vnet { - /* Protects port_list and port_hash. */ - spinlock_t lock; - + spinlock_t lock; /* Protects port_list and port_hash. */ struct net_device *dev; - u32 msg_enable; - + u8 q_used[VNET_MAX_TXQS]; struct list_head port_list; - struct hlist_head port_hash[VNET_PORT_HASH_SIZE]; - struct vnet_mcast_entry *mcast_list; - struct list_head list; u64 local_mac; - int nports; }; -- cgit From b12a96f5cd04583f45a1b6554b8f3786b26db913 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Tue, 14 Mar 2017 10:24:42 -0700 Subject: sunvnet: count multicast packets Make sure multicast packets get counted in the device. Orabug: 25190537 Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunvnet_common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index f8d1cc7e4977..bf7e0fbe5a9d 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -409,6 +409,8 @@ static int vnet_rx_one(struct vnet_port *port, struct vio_net_desc *desc) skb->ip_summed = port->switch_port ? CHECKSUM_NONE : CHECKSUM_PARTIAL; + if (unlikely(is_multicast_ether_addr(eth_hdr(skb)->h_dest))) + dev->stats.multicast++; dev->stats.rx_packets++; dev->stats.rx_bytes += len; port->stats.rx_packets++; -- cgit From 9c5a3a1f9388100d4b03e85faf0cce8264985302 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Tue, 14 Mar 2017 10:24:43 -0700 Subject: sunvnet: xoff not needed when removing port link The sunvnet netdev is connected to the controlling ldom's vswitch for network bridging. However, for higher performance between ldoms, there also is a channel between each client ldom. These connections are represented in the sunvnet driver by a queue for each ldom. The driver uses select_queue to tell the stack which queue to use by tracking the mac addresses on the other end of each port. When a connected ldom shuts down, the driver receives an LDC_EVENT_RESET and the port is removed from the driver, thus a queue with no ldom on the other end will never be selected for Tx. The driver was trying to reinforce the "don't use this queue" notion with netif_tx_stop_queue() and netif_tx_wake_queue(), which really should only be used to signal a Tx queue is full (aka XOFF). This misuse of queue state resulted in NETDEV WATCHDOG messages and lots of unnecessary calls into the driver's tx_timeout handler. Simply removing these takes care of the problem. Orabug: 25190537 Signed-off-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunvnet_common.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index bf7e0fbe5a9d..9e86833249d4 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -1749,16 +1749,12 @@ void sunvnet_port_add_txq_common(struct vnet_port *port) vp->nports++; vp->q_used[smallest]++; port->q_index = smallest; - netif_tx_wake_queue(netdev_get_tx_queue(VNET_PORT_TO_NET_DEVICE(port), - port->q_index)); } EXPORT_SYMBOL_GPL(sunvnet_port_add_txq_common); void sunvnet_port_rm_txq_common(struct vnet_port *port) { port->vp->nports--; - netif_tx_stop_queue(netdev_get_tx_queue(VNET_PORT_TO_NET_DEVICE(port), - port->q_index)); port->vp->q_used[port->q_index]--; port->q_index = 0; } -- cgit From d82bae12dc38d79a2b77473f5eb0612a3d69c55b Mon Sep 17 00:00:00 2001 From: Soheil Hassas Yeganeh Date: Wed, 15 Mar 2017 16:30:45 -0400 Subject: tcp: remove per-destination timestamp cache Commit 8a5bd45f6616 (tcp: randomize tcp timestamp offsets for each connection) randomizes TCP timestamps per connection. After this commit, there is no guarantee that the timestamps received from the same destination are monotonically increasing. As a result, the per-destination timestamp cache in TCP metrics (i.e., tcpm_ts in struct tcp_metrics_block) is broken and cannot be relied upon. Remove the per-destination timestamp cache and all related code paths. Note that this cache was already broken for caching timestamps of multiple machines behind a NAT sharing the same address. Signed-off-by: Soheil Hassas Yeganeh Signed-off-by: Eric Dumazet Signed-off-by: Neal Cardwell Signed-off-by: Yuchung Cheng Cc: Lutz Vieweg Cc: Florian Westphal Signed-off-by: David S. Miller --- include/net/tcp.h | 6 +- net/ipv4/tcp_input.c | 6 +- net/ipv4/tcp_ipv4.c | 4 -- net/ipv4/tcp_metrics.c | 147 ++--------------------------------------------- net/ipv4/tcp_minisocks.c | 22 ++----- net/ipv6/tcp_ipv6.c | 5 -- 6 files changed, 11 insertions(+), 179 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index bede8f7fa742..c81f3b958d44 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -406,11 +406,7 @@ void tcp_clear_retrans(struct tcp_sock *tp); void tcp_update_metrics(struct sock *sk); void tcp_init_metrics(struct sock *sk); void tcp_metrics_init(void); -bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, - bool paws_check, bool timestamps); -bool tcp_remember_stamp(struct sock *sk); -bool tcp_tw_remember_stamp(struct inet_timewait_sock *tw); -void tcp_fetch_timewait_stamp(struct sock *sk, struct dst_entry *dst); +bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst); void tcp_disable_fack(struct tcp_sock *tp); void tcp_close(struct sock *sk, long timeout); void tcp_init_sock(struct sock *sk); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 96b67a8b18c3..aafec0676d3e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6342,8 +6342,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, dst = af_ops->route_req(sk, &fl, req, &strict); if (dst && strict && - !tcp_peer_is_proven(req, dst, true, - tmp_opt.saw_tstamp)) { + !tcp_peer_is_proven(req, dst)) { NET_INC_STATS(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED); goto drop_and_release; } @@ -6352,8 +6351,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, else if (!net->ipv4.sysctl_tcp_syncookies && (net->ipv4.sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < (net->ipv4.sysctl_max_syn_backlog >> 2)) && - !tcp_peer_is_proven(req, dst, false, - tmp_opt.saw_tstamp)) { + !tcp_peer_is_proven(req, dst)) { /* Without syncookies last quarter of * backlog is filled with destinations, * proven to be alive. diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 08d870e45658..d8b401fff9fe 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -198,10 +198,6 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) tp->write_seq = 0; } - if (tcp_death_row->sysctl_tw_recycle && - !tp->rx_opt.ts_recent_stamp && fl4->daddr == daddr) - tcp_fetch_timewait_stamp(sk, &rt->dst); - inet->inet_dport = usin->sin_port; sk_daddr_set(sk, daddr); diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 0f46e5fe31ad..9d0d4f39e42b 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -45,8 +45,6 @@ struct tcp_metrics_block { struct inetpeer_addr tcpm_saddr; struct inetpeer_addr tcpm_daddr; unsigned long tcpm_stamp; - u32 tcpm_ts; - u32 tcpm_ts_stamp; u32 tcpm_lock; u32 tcpm_vals[TCP_METRIC_MAX_KERNEL + 1]; struct tcp_fastopen_metrics tcpm_fastopen; @@ -123,8 +121,6 @@ static void tcpm_suck_dst(struct tcp_metrics_block *tm, tm->tcpm_vals[TCP_METRIC_SSTHRESH] = dst_metric_raw(dst, RTAX_SSTHRESH); tm->tcpm_vals[TCP_METRIC_CWND] = dst_metric_raw(dst, RTAX_CWND); tm->tcpm_vals[TCP_METRIC_REORDERING] = dst_metric_raw(dst, RTAX_REORDERING); - tm->tcpm_ts = 0; - tm->tcpm_ts_stamp = 0; if (fastopen_clear) { tm->tcpm_fastopen.mss = 0; tm->tcpm_fastopen.syn_loss = 0; @@ -273,48 +269,6 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, return tm; } -static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock *tw) -{ - struct tcp_metrics_block *tm; - struct inetpeer_addr saddr, daddr; - unsigned int hash; - struct net *net; - - if (tw->tw_family == AF_INET) { - inetpeer_set_addr_v4(&saddr, tw->tw_rcv_saddr); - inetpeer_set_addr_v4(&daddr, tw->tw_daddr); - hash = ipv4_addr_hash(tw->tw_daddr); - } -#if IS_ENABLED(CONFIG_IPV6) - else if (tw->tw_family == AF_INET6) { - if (ipv6_addr_v4mapped(&tw->tw_v6_daddr)) { - inetpeer_set_addr_v4(&saddr, tw->tw_rcv_saddr); - inetpeer_set_addr_v4(&daddr, tw->tw_daddr); - hash = ipv4_addr_hash(tw->tw_daddr); - } else { - inetpeer_set_addr_v6(&saddr, &tw->tw_v6_rcv_saddr); - inetpeer_set_addr_v6(&daddr, &tw->tw_v6_daddr); - hash = ipv6_addr_hash(&tw->tw_v6_daddr); - } - } -#endif - else - return NULL; - - net = twsk_net(tw); - hash ^= net_hash_mix(net); - hash = hash_32(hash, tcp_metrics_hash_log); - - for (tm = rcu_dereference(tcp_metrics_hash[hash].chain); tm; - tm = rcu_dereference(tm->tcpm_next)) { - if (addr_same(&tm->tcpm_saddr, &saddr) && - addr_same(&tm->tcpm_daddr, &daddr) && - net_eq(tm_net(tm), net)) - break; - } - return tm; -} - static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, struct dst_entry *dst, bool create) @@ -573,8 +527,7 @@ reset: tp->snd_cwnd_stamp = tcp_time_stamp; } -bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, - bool paws_check, bool timestamps) +bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst) { struct tcp_metrics_block *tm; bool ret; @@ -584,94 +537,10 @@ bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, rcu_read_lock(); tm = __tcp_get_metrics_req(req, dst); - if (paws_check) { - if (tm && - (u32)get_seconds() - tm->tcpm_ts_stamp < TCP_PAWS_MSL && - ((s32)(tm->tcpm_ts - req->ts_recent) > TCP_PAWS_WINDOW || - !timestamps)) - ret = false; - else - ret = true; - } else { - if (tm && tcp_metric_get(tm, TCP_METRIC_RTT) && tm->tcpm_ts_stamp) - ret = true; - else - ret = false; - } - rcu_read_unlock(); - - return ret; -} - -void tcp_fetch_timewait_stamp(struct sock *sk, struct dst_entry *dst) -{ - struct tcp_metrics_block *tm; - - rcu_read_lock(); - tm = tcp_get_metrics(sk, dst, true); - if (tm) { - struct tcp_sock *tp = tcp_sk(sk); - - if ((u32)get_seconds() - tm->tcpm_ts_stamp <= TCP_PAWS_MSL) { - tp->rx_opt.ts_recent_stamp = tm->tcpm_ts_stamp; - tp->rx_opt.ts_recent = tm->tcpm_ts; - } - } - rcu_read_unlock(); -} -EXPORT_SYMBOL_GPL(tcp_fetch_timewait_stamp); - -/* VJ's idea. Save last timestamp seen from this destination and hold - * it at least for normal timewait interval to use for duplicate - * segment detection in subsequent connections, before they enter - * synchronized state. - */ -bool tcp_remember_stamp(struct sock *sk) -{ - struct dst_entry *dst = __sk_dst_get(sk); - bool ret = false; - - if (dst) { - struct tcp_metrics_block *tm; - - rcu_read_lock(); - tm = tcp_get_metrics(sk, dst, true); - if (tm) { - struct tcp_sock *tp = tcp_sk(sk); - - if ((s32)(tm->tcpm_ts - tp->rx_opt.ts_recent) <= 0 || - ((u32)get_seconds() - tm->tcpm_ts_stamp > TCP_PAWS_MSL && - tm->tcpm_ts_stamp <= (u32)tp->rx_opt.ts_recent_stamp)) { - tm->tcpm_ts_stamp = (u32)tp->rx_opt.ts_recent_stamp; - tm->tcpm_ts = tp->rx_opt.ts_recent; - } - ret = true; - } - rcu_read_unlock(); - } - return ret; -} - -bool tcp_tw_remember_stamp(struct inet_timewait_sock *tw) -{ - struct tcp_metrics_block *tm; - bool ret = false; - - rcu_read_lock(); - tm = __tcp_get_metrics_tw(tw); - if (tm) { - const struct tcp_timewait_sock *tcptw; - struct sock *sk = (struct sock *) tw; - - tcptw = tcp_twsk(sk); - if ((s32)(tm->tcpm_ts - tcptw->tw_ts_recent) <= 0 || - ((u32)get_seconds() - tm->tcpm_ts_stamp > TCP_PAWS_MSL && - tm->tcpm_ts_stamp <= (u32)tcptw->tw_ts_recent_stamp)) { - tm->tcpm_ts_stamp = (u32)tcptw->tw_ts_recent_stamp; - tm->tcpm_ts = tcptw->tw_ts_recent; - } + if (tm && tcp_metric_get(tm, TCP_METRIC_RTT)) ret = true; - } + else + ret = false; rcu_read_unlock(); return ret; @@ -791,14 +660,6 @@ static int tcp_metrics_fill_info(struct sk_buff *msg, jiffies - tm->tcpm_stamp, TCP_METRICS_ATTR_PAD) < 0) goto nla_put_failure; - if (tm->tcpm_ts_stamp) { - if (nla_put_s32(msg, TCP_METRICS_ATTR_TW_TS_STAMP, - (s32) (get_seconds() - tm->tcpm_ts_stamp)) < 0) - goto nla_put_failure; - if (nla_put_u32(msg, TCP_METRICS_ATTR_TW_TSVAL, - tm->tcpm_ts) < 0) - goto nla_put_failure; - } { int n = 0; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 7e16243cdb58..692f974e5abe 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -94,7 +94,6 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, struct tcp_options_received tmp_opt; struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw); bool paws_reject = false; - struct inet_timewait_death_row *tcp_death_row = &sock_net((struct sock*)tw)->ipv4.tcp_death_row; tmp_opt.saw_tstamp = 0; if (th->doff > (sizeof(*th) >> 2) && tcptw->tw_ts_recent_stamp) { @@ -149,12 +148,7 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, tcptw->tw_ts_recent = tmp_opt.rcv_tsval; } - if (tcp_death_row->sysctl_tw_recycle && - tcptw->tw_ts_recent_stamp && - tcp_tw_remember_stamp(tw)) - inet_twsk_reschedule(tw, tw->tw_timeout); - else - inet_twsk_reschedule(tw, TCP_TIMEWAIT_LEN); + inet_twsk_reschedule(tw, TCP_TIMEWAIT_LEN); return TCP_TW_ACK; } @@ -259,12 +253,8 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) const struct inet_connection_sock *icsk = inet_csk(sk); const struct tcp_sock *tp = tcp_sk(sk); struct inet_timewait_sock *tw; - bool recycle_ok = false; struct inet_timewait_death_row *tcp_death_row = &sock_net(sk)->ipv4.tcp_death_row; - if (tcp_death_row->sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp) - recycle_ok = tcp_remember_stamp(sk); - tw = inet_twsk_alloc(sk, tcp_death_row, state); if (tw) { @@ -317,13 +307,9 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) if (timeo < rto) timeo = rto; - if (recycle_ok) { - tw->tw_timeout = rto; - } else { - tw->tw_timeout = TCP_TIMEWAIT_LEN; - if (state == TCP_TIME_WAIT) - timeo = TCP_TIMEWAIT_LEN; - } + tw->tw_timeout = TCP_TIMEWAIT_LEN; + if (state == TCP_TIME_WAIT) + timeo = TCP_TIMEWAIT_LEN; inet_twsk_schedule(tw, timeo); /* Linkage updates. */ diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index c73a431fd06f..853cb43e3e3c 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -265,11 +265,6 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, sk->sk_gso_type = SKB_GSO_TCPV6; ip6_dst_store(sk, dst, NULL, NULL); - if (tcp_death_row->sysctl_tw_recycle && - !tp->rx_opt.ts_recent_stamp && - ipv6_addr_equal(&fl6.daddr, &sk->sk_v6_daddr)) - tcp_fetch_timewait_stamp(sk, dst); - icsk->icsk_ext_hdr_len = 0; if (opt) icsk->icsk_ext_hdr_len = opt->opt_flen + -- cgit From 4396e46187ca5070219b81773c4e65088dac50cc Mon Sep 17 00:00:00 2001 From: Soheil Hassas Yeganeh Date: Wed, 15 Mar 2017 16:30:46 -0400 Subject: tcp: remove tcp_tw_recycle The tcp_tw_recycle was already broken for connections behind NAT, since the per-destination timestamp is not monotonically increasing for multiple machines behind a single destination address. After the randomization of TCP timestamp offsets in commit 8a5bd45f6616 (tcp: randomize tcp timestamp offsets for each connection), the tcp_tw_recycle is broken for all types of connections for the same reason: the timestamps received from a single machine is not monotonically increasing, anymore. Remove tcp_tw_recycle, since it is not functional. Also, remove the PAWSPassive SNMP counter since it is only used for tcp_tw_recycle, and simplify tcp_v4_route_req and tcp_v6_route_req since the strict argument is only set when tcp_tw_recycle is enabled. Signed-off-by: Soheil Hassas Yeganeh Signed-off-by: Eric Dumazet Signed-off-by: Neal Cardwell Signed-off-by: Yuchung Cheng Cc: Lutz Vieweg Cc: Florian Westphal Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 5 ----- include/net/netns/ipv4.h | 1 - include/net/tcp.h | 3 +-- include/uapi/linux/snmp.h | 1 - net/ipv4/proc.c | 1 - net/ipv4/sysctl_net_ipv4.c | 7 ------- net/ipv4/tcp_input.c | 30 +++++------------------------- net/ipv4/tcp_ipv4.c | 15 ++------------- net/ipv6/tcp_ipv6.c | 5 +---- 9 files changed, 9 insertions(+), 59 deletions(-) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index ab0230461377..ed3d0791eb27 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -640,11 +640,6 @@ tcp_tso_win_divisor - INTEGER building larger TSO frames. Default: 3 -tcp_tw_recycle - BOOLEAN - Enable fast recycling TIME-WAIT sockets. Default value is 0. - It should not be changed without advice/request of technical - experts. - tcp_tw_reuse - BOOLEAN Allow to reuse TIME-WAIT sockets for new connections when it is safe from protocol viewpoint. Default value is 0. diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 622d2da27135..2e9d649ba169 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -33,7 +33,6 @@ struct inet_timewait_death_row { atomic_t tw_count; struct inet_hashinfo *hashinfo ____cacheline_aligned_in_smp; - int sysctl_tw_recycle; int sysctl_max_tw_buckets; }; diff --git a/include/net/tcp.h b/include/net/tcp.h index c81f3b958d44..e614ad4d613e 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1810,8 +1810,7 @@ struct tcp_request_sock_ops { __u16 *mss); #endif struct dst_entry *(*route_req)(const struct sock *sk, struct flowi *fl, - const struct request_sock *req, - bool *strict); + const struct request_sock *req); __u32 (*init_seq_tsoff)(const struct sk_buff *skb, u32 *tsoff); int (*send_synack)(const struct sock *sk, struct dst_entry *dst, struct flowi *fl, struct request_sock *req, diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index 3b2bed7ca9a4..cec0e171d20c 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -177,7 +177,6 @@ enum LINUX_MIB_TIMEWAITED, /* TimeWaited */ LINUX_MIB_TIMEWAITRECYCLED, /* TimeWaitRecycled */ LINUX_MIB_TIMEWAITKILLED, /* TimeWaitKilled */ - LINUX_MIB_PAWSPASSIVEREJECTED, /* PAWSPassiveRejected */ LINUX_MIB_PAWSACTIVEREJECTED, /* PAWSActiveRejected */ LINUX_MIB_PAWSESTABREJECTED, /* PAWSEstabRejected */ LINUX_MIB_DELAYEDACKS, /* DelayedACKs */ diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 69cf49e8356d..4ccbf464d1ac 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -199,7 +199,6 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TW", LINUX_MIB_TIMEWAITED), SNMP_MIB_ITEM("TWRecycled", LINUX_MIB_TIMEWAITRECYCLED), SNMP_MIB_ITEM("TWKilled", LINUX_MIB_TIMEWAITKILLED), - SNMP_MIB_ITEM("PAWSPassive", LINUX_MIB_PAWSPASSIVEREJECTED), SNMP_MIB_ITEM("PAWSActive", LINUX_MIB_PAWSACTIVEREJECTED), SNMP_MIB_ITEM("PAWSEstab", LINUX_MIB_PAWSESTABREJECTED), SNMP_MIB_ITEM("DelayedACKs", LINUX_MIB_DELAYEDACKS), diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index d6880a6149ee..11aaef0939b2 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -980,13 +980,6 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_tw_recycle", - .data = &init_net.ipv4.tcp_death_row.sysctl_tw_recycle, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_max_syn_backlog", .data = &init_net.ipv4.sysctl_max_syn_backlog, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index aafec0676d3e..bb09c7095988 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6327,31 +6327,11 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, af_ops->init_seq_tsoff(skb, &tcp_rsk(req)->ts_off); if (!want_cookie && !isn) { - /* VJ's idea. We save last timestamp seen - * from the destination in peer table, when entering - * state TIME-WAIT, and check against it before - * accepting new connection request. - * - * If "isn" is not zero, this request hit alive - * timewait bucket, so that all the necessary checks - * are made in the function processing timewait state. - */ - if (net->ipv4.tcp_death_row.sysctl_tw_recycle) { - bool strict; - - dst = af_ops->route_req(sk, &fl, req, &strict); - - if (dst && strict && - !tcp_peer_is_proven(req, dst)) { - NET_INC_STATS(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED); - goto drop_and_release; - } - } /* Kill the following clause, if you dislike this way. */ - else if (!net->ipv4.sysctl_tcp_syncookies && - (net->ipv4.sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < - (net->ipv4.sysctl_max_syn_backlog >> 2)) && - !tcp_peer_is_proven(req, dst)) { + if (!net->ipv4.sysctl_tcp_syncookies && + (net->ipv4.sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < + (net->ipv4.sysctl_max_syn_backlog >> 2)) && + !tcp_peer_is_proven(req, dst)) { /* Without syncookies last quarter of * backlog is filled with destinations, * proven to be alive. @@ -6367,7 +6347,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, isn = af_ops->init_seq_tsoff(skb, &tcp_rsk(req)->ts_off); } if (!dst) { - dst = af_ops->route_req(sk, &fl, req, NULL); + dst = af_ops->route_req(sk, &fl, req); if (!dst) goto drop_and_free; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d8b401fff9fe..7482b5d11861 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1213,19 +1213,9 @@ static void tcp_v4_init_req(struct request_sock *req, static struct dst_entry *tcp_v4_route_req(const struct sock *sk, struct flowi *fl, - const struct request_sock *req, - bool *strict) + const struct request_sock *req) { - struct dst_entry *dst = inet_csk_route_req(sk, &fl->u.ip4, req); - - if (strict) { - if (fl->u.ip4.daddr == inet_rsk(req)->ir_rmt_addr) - *strict = true; - else - *strict = false; - } - - return dst; + return inet_csk_route_req(sk, &fl->u.ip4, req); } struct request_sock_ops tcp_request_sock_ops __read_mostly = { @@ -2462,7 +2452,6 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_tw_reuse = 0; cnt = tcp_hashinfo.ehash_mask + 1; - net->ipv4.tcp_death_row.sysctl_tw_recycle = 0; net->ipv4.tcp_death_row.sysctl_max_tw_buckets = (cnt + 1) / 2; net->ipv4.tcp_death_row.hashinfo = &tcp_hashinfo; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 853cb43e3e3c..0f08d718a002 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -722,11 +722,8 @@ static void tcp_v6_init_req(struct request_sock *req, static struct dst_entry *tcp_v6_route_req(const struct sock *sk, struct flowi *fl, - const struct request_sock *req, - bool *strict) + const struct request_sock *req) { - if (strict) - *strict = true; return inet6_csk_route_req(sk, &fl->u.ip6, req, IPPROTO_TCP); } -- cgit From e245c5c6a5656e4d61aa7bb08e9694fd6e5b2b9d Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 15 Mar 2017 18:26:39 -0700 Subject: bpf: move fixup_bpf_calls() function no functional change. move fixup_bpf_calls() to verifier.c it's being refactored in the next patch Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- kernel/bpf/syscall.c | 56 -------------------------------------------------- kernel/bpf/verifier.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 56 deletions(-) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 7af0dcc5d755..48c914b983bd 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -586,59 +586,6 @@ void bpf_register_prog_type(struct bpf_prog_type_list *tl) list_add(&tl->list_node, &bpf_prog_types); } -/* fixup insn->imm field of bpf_call instructions: - * if (insn->imm == BPF_FUNC_map_lookup_elem) - * insn->imm = bpf_map_lookup_elem - __bpf_call_base; - * else if (insn->imm == BPF_FUNC_map_update_elem) - * insn->imm = bpf_map_update_elem - __bpf_call_base; - * else ... - * - * this function is called after eBPF program passed verification - */ -static void fixup_bpf_calls(struct bpf_prog *prog) -{ - const struct bpf_func_proto *fn; - int i; - - for (i = 0; i < prog->len; i++) { - struct bpf_insn *insn = &prog->insnsi[i]; - - if (insn->code == (BPF_JMP | BPF_CALL)) { - /* we reach here when program has bpf_call instructions - * and it passed bpf_check(), means that - * ops->get_func_proto must have been supplied, check it - */ - BUG_ON(!prog->aux->ops->get_func_proto); - - 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_xdp_adjust_head) - prog->xdp_adjust_head = 1; - if (insn->imm == BPF_FUNC_tail_call) { - /* mark bpf_tail_call as different opcode - * to avoid conditional branch in - * interpeter for every normal call - * and to prevent accidental JITing by - * JIT compiler that doesn't support - * bpf_tail_call yet - */ - insn->imm = 0; - insn->code |= BPF_X; - continue; - } - - fn = prog->aux->ops->get_func_proto(insn->imm); - /* all functions that have prototype and verifier allowed - * programs to call them, must be real in-kernel functions - */ - BUG_ON(!fn->func); - insn->imm = fn->func - __bpf_call_base; - } - } -} - /* drop refcnt on maps used by eBPF program and free auxilary data */ static void free_used_maps(struct bpf_prog_aux *aux) { @@ -892,9 +839,6 @@ static int bpf_prog_load(union bpf_attr *attr) if (err < 0) goto free_used_maps; - /* fixup BPF_CALL->imm field */ - fixup_bpf_calls(prog); - /* eBPF program is ready to be JITed */ prog = bpf_prog_select_runtime(prog, &err); if (err < 0) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 796b68d00119..e41da6c57053 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3233,6 +3233,60 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) return 0; } +/* fixup insn->imm field of bpf_call instructions: + * if (insn->imm == BPF_FUNC_map_lookup_elem) + * insn->imm = bpf_map_lookup_elem - __bpf_call_base; + * else if (insn->imm == BPF_FUNC_map_update_elem) + * insn->imm = bpf_map_update_elem - __bpf_call_base; + * else ... + * + * this function is called after eBPF program passed verification + */ +static void fixup_bpf_calls(struct bpf_prog *prog) +{ + const struct bpf_func_proto *fn; + int i; + + for (i = 0; i < prog->len; i++) { + struct bpf_insn *insn = &prog->insnsi[i]; + + if (insn->code == (BPF_JMP | BPF_CALL)) { + /* we reach here when program has bpf_call instructions + * and it passed bpf_check(), means that + * ops->get_func_proto must have been supplied, check it + */ + BUG_ON(!prog->aux->ops->get_func_proto); + + 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_xdp_adjust_head) + prog->xdp_adjust_head = 1; + if (insn->imm == BPF_FUNC_tail_call) { + /* mark bpf_tail_call as different opcode + * to avoid conditional branch in + * interpeter for every normal call + * and to prevent accidental JITing by + * JIT compiler that doesn't support + * bpf_tail_call yet + */ + insn->imm = 0; + insn->code |= BPF_X; + continue; + } + + fn = prog->aux->ops->get_func_proto(insn->imm); + /* all functions that have prototype and verifier allowed + * programs to call them, must be real in-kernel functions + */ + BUG_ON(!fn->func); + insn->imm = fn->func - __bpf_call_base; + } + } +} + + static void free_states(struct bpf_verifier_env *env) { struct bpf_verifier_state_list *sl, *sln; @@ -3328,6 +3382,9 @@ skip_full_check: /* program is valid, convert *(u32*)(ctx + off) accesses */ ret = convert_ctx_accesses(env); + if (ret == 0) + fixup_bpf_calls(env->prog); + if (log_level && log_len >= log_size - 1) { BUG_ON(log_len >= log_size); /* verifier log exceeded user supplied buffer */ -- cgit From 79741b3bdec01a8628368fbcfccc7d189ed606cb Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 15 Mar 2017 18:26:40 -0700 Subject: bpf: refactor fixup_bpf_calls() reduce indent and make it iterate over instructions similar to convert_ctx_accesses(). Also convert hard BUG_ON into soft verifier error. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- kernel/bpf/verifier.c | 76 ++++++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index e41da6c57053..5dfa9b8111da 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3233,59 +3233,53 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) return 0; } -/* fixup insn->imm field of bpf_call instructions: - * if (insn->imm == BPF_FUNC_map_lookup_elem) - * insn->imm = bpf_map_lookup_elem - __bpf_call_base; - * else if (insn->imm == BPF_FUNC_map_update_elem) - * insn->imm = bpf_map_update_elem - __bpf_call_base; - * else ... +/* fixup insn->imm field of bpf_call instructions * * this function is called after eBPF program passed verification */ -static void fixup_bpf_calls(struct bpf_prog *prog) +static int fixup_bpf_calls(struct bpf_verifier_env *env) { + struct bpf_prog *prog = env->prog; + struct bpf_insn *insn = prog->insnsi; const struct bpf_func_proto *fn; + const int insn_cnt = prog->len; int i; - for (i = 0; i < prog->len; i++) { - struct bpf_insn *insn = &prog->insnsi[i]; + for (i = 0; i < insn_cnt; i++, insn++) { + if (insn->code != (BPF_JMP | BPF_CALL)) + continue; - if (insn->code == (BPF_JMP | BPF_CALL)) { - /* we reach here when program has bpf_call instructions - * and it passed bpf_check(), means that - * ops->get_func_proto must have been supplied, check it + 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_xdp_adjust_head) + prog->xdp_adjust_head = 1; + if (insn->imm == BPF_FUNC_tail_call) { + /* mark bpf_tail_call as different opcode to avoid + * conditional branch in the interpeter for every normal + * call and to prevent accidental JITing by JIT compiler + * that doesn't support bpf_tail_call yet */ - BUG_ON(!prog->aux->ops->get_func_proto); - - 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_xdp_adjust_head) - prog->xdp_adjust_head = 1; - if (insn->imm == BPF_FUNC_tail_call) { - /* mark bpf_tail_call as different opcode - * to avoid conditional branch in - * interpeter for every normal call - * and to prevent accidental JITing by - * JIT compiler that doesn't support - * bpf_tail_call yet - */ - insn->imm = 0; - insn->code |= BPF_X; - continue; - } + insn->imm = 0; + insn->code |= BPF_X; + continue; + } - fn = prog->aux->ops->get_func_proto(insn->imm); - /* all functions that have prototype and verifier allowed - * programs to call them, must be real in-kernel functions - */ - BUG_ON(!fn->func); - insn->imm = fn->func - __bpf_call_base; + fn = prog->aux->ops->get_func_proto(insn->imm); + /* all functions that have prototype and verifier allowed + * programs to call them, must be real in-kernel functions + */ + if (!fn->func) { + verbose("kernel subsystem misconfigured func %s#%d\n", + func_id_name(insn->imm), insn->imm); + return -EFAULT; } + insn->imm = fn->func - __bpf_call_base; } -} + return 0; +} static void free_states(struct bpf_verifier_env *env) { @@ -3383,7 +3377,7 @@ skip_full_check: ret = convert_ctx_accesses(env); if (ret == 0) - fixup_bpf_calls(env->prog); + ret = fixup_bpf_calls(env); if (log_level && log_len >= log_size - 1) { BUG_ON(log_len >= log_size); -- cgit From 8041902dae5299c1f194ba42d14383f734631009 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 15 Mar 2017 18:26:41 -0700 Subject: bpf: adjust insn_aux_data when patching insns convert_ctx_accesses() replaces single bpf instruction with a set of instructions. Adjust corresponding insn_aux_data while patching. It's needed to make sure subsequent 'for(all insn)' loops have matching insn and insn_aux_data. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- kernel/bpf/verifier.c | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 5dfa9b8111da..2990fda1c6a5 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3162,6 +3162,41 @@ static void convert_pseudo_ld_imm64(struct bpf_verifier_env *env) insn->src_reg = 0; } +/* single env->prog->insni[off] instruction was replaced with the range + * insni[off, off + cnt). Adjust corresponding insn_aux_data by copying + * [0, off) and [off, end) to new locations, so the patched range stays zero + */ +static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len, + u32 off, u32 cnt) +{ + struct bpf_insn_aux_data *new_data, *old_data = env->insn_aux_data; + + if (cnt == 1) + return 0; + new_data = vzalloc(sizeof(struct bpf_insn_aux_data) * prog_len); + if (!new_data) + return -ENOMEM; + memcpy(new_data, old_data, sizeof(struct bpf_insn_aux_data) * off); + memcpy(new_data + off + cnt - 1, old_data + off, + sizeof(struct bpf_insn_aux_data) * (prog_len - off - cnt + 1)); + env->insn_aux_data = new_data; + vfree(old_data); + return 0; +} + +static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off, + const struct bpf_insn *patch, u32 len) +{ + struct bpf_prog *new_prog; + + new_prog = bpf_patch_insn_single(env->prog, off, patch, len); + if (!new_prog) + return NULL; + if (adjust_insn_aux_data(env, new_prog->len, off, len)) + return NULL; + return new_prog; +} + /* convert load instructions that access fields of 'struct __sk_buff' * into sequence of instructions that access fields of 'struct sk_buff' */ @@ -3181,10 +3216,10 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) verbose("bpf verifier is misconfigured\n"); return -EINVAL; } else if (cnt) { - new_prog = bpf_patch_insn_single(env->prog, 0, - insn_buf, cnt); + new_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt); if (!new_prog) return -ENOMEM; + env->prog = new_prog; delta += cnt - 1; } @@ -3209,7 +3244,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) else continue; - if (env->insn_aux_data[i].ptr_type != PTR_TO_CTX) + if (env->insn_aux_data[i + delta].ptr_type != PTR_TO_CTX) continue; cnt = ops->convert_ctx_access(type, insn, insn_buf, env->prog); @@ -3218,8 +3253,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) return -EINVAL; } - new_prog = bpf_patch_insn_single(env->prog, i + delta, insn_buf, - cnt); + new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt); if (!new_prog) return -ENOMEM; -- cgit From 81ed18ab3098b6519274545e80a29caacb77d160 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 15 Mar 2017 18:26:42 -0700 Subject: bpf: add helper inlining infra and optimize map_array lookup Optimize bpf_call -> bpf_map_lookup_elem() -> array_map_lookup_elem() into a sequence of bpf instructions. When JIT is on the sequence of bpf instructions is the sequence of native cpu instructions with significantly faster performance than indirect call and two function's prologue/epilogue. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf.h | 1 + include/linux/bpf_verifier.h | 5 ++++- include/linux/filter.h | 10 ++++++++++ kernel/bpf/arraymap.c | 29 +++++++++++++++++++++++++++++ kernel/bpf/verifier.c | 36 +++++++++++++++++++++++++++++++++--- 5 files changed, 77 insertions(+), 4 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 909fc033173a..da8c64ca8dc9 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -35,6 +35,7 @@ struct bpf_map_ops { void *(*map_fd_get_ptr)(struct bpf_map *map, struct file *map_file, int fd); void (*map_fd_put_ptr)(void *ptr); + u32 (*map_gen_lookup)(struct bpf_map *map, struct bpf_insn *insn_buf); }; struct bpf_map { diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index a13b031dc6b8..5efb4db44e1e 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -66,7 +66,10 @@ struct bpf_verifier_state_list { }; struct bpf_insn_aux_data { - enum bpf_reg_type ptr_type; /* pointer type for load/store insns */ + union { + enum bpf_reg_type ptr_type; /* pointer type for load/store insns */ + struct bpf_map *map_ptr; /* pointer for call insn into lookup_elem */ + }; }; #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */ diff --git a/include/linux/filter.h b/include/linux/filter.h index fbf7b39e8103..dffa072b7b79 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -693,6 +693,11 @@ static inline bool bpf_jit_is_ebpf(void) # endif } +static inline bool ebpf_jit_enabled(void) +{ + return bpf_jit_enable && bpf_jit_is_ebpf(); +} + static inline bool bpf_prog_ebpf_jited(const struct bpf_prog *fp) { return fp->jited && bpf_jit_is_ebpf(); @@ -753,6 +758,11 @@ void bpf_prog_kallsyms_del(struct bpf_prog *fp); #else /* CONFIG_BPF_JIT */ +static inline bool ebpf_jit_enabled(void) +{ + return false; +} + static inline bool bpf_prog_ebpf_jited(const struct bpf_prog *fp) { return false; diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 6b6f41f0b211..bcf9955fac95 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -1,4 +1,5 @@ /* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com + * Copyright (c) 2016,2017 Facebook * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -113,6 +114,33 @@ static void *array_map_lookup_elem(struct bpf_map *map, void *key) return array->value + array->elem_size * index; } +/* emit BPF instructions equivalent to C code of array_map_lookup_elem() */ +static u32 array_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf) +{ + struct bpf_array *array = container_of(map, struct bpf_array, map); + struct bpf_insn *insn = insn_buf; + u32 elem_size = array->elem_size; + const int ret = BPF_REG_0; + const int map_ptr = BPF_REG_1; + const int index = BPF_REG_2; + + *insn++ = BPF_ALU64_IMM(BPF_ADD, map_ptr, offsetof(struct bpf_array, value)); + *insn++ = BPF_LDX_MEM(BPF_W, ret, index, 0); + *insn++ = BPF_JMP_IMM(BPF_JGE, ret, array->map.max_entries, + elem_size == 1 ? 2 : 3); + if (elem_size == 1) { + /* nop */ + } else if (is_power_of_2(elem_size)) { + *insn++ = BPF_ALU64_IMM(BPF_LSH, ret, ilog2(elem_size)); + } else { + *insn++ = BPF_ALU64_IMM(BPF_MUL, ret, elem_size); + } + *insn++ = BPF_ALU64_REG(BPF_ADD, ret, map_ptr); + *insn++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1); + *insn++ = BPF_MOV64_IMM(ret, 0); + return insn - insn_buf; +} + /* Called from eBPF program */ static void *percpu_array_map_lookup_elem(struct bpf_map *map, void *key) { @@ -267,6 +295,7 @@ static const struct bpf_map_ops array_ops = { .map_lookup_elem = array_map_lookup_elem, .map_update_elem = array_map_update_elem, .map_delete_elem = array_map_delete_elem, + .map_gen_lookup = array_map_gen_lookup, }; static struct bpf_map_type_list array_type __ro_after_init = { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 2990fda1c6a5..90bf46787603 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1273,7 +1273,7 @@ static void clear_all_pkt_pointers(struct bpf_verifier_env *env) } } -static int check_call(struct bpf_verifier_env *env, int func_id) +static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) { struct bpf_verifier_state *state = &env->cur_state; const struct bpf_func_proto *fn = NULL; @@ -1369,6 +1369,7 @@ static int check_call(struct bpf_verifier_env *env, int func_id) } regs[BPF_REG_0].map_ptr = meta.map_ptr; regs[BPF_REG_0].id = ++env->id_gen; + env->insn_aux_data[insn_idx].map_ptr = meta.map_ptr; } else { verbose("unknown return type %d of func %s#%d\n", fn->ret_type, func_id_name(func_id), func_id); @@ -2940,7 +2941,7 @@ static int do_check(struct bpf_verifier_env *env) return -EINVAL; } - err = check_call(env, insn->imm); + err = check_call(env, insn->imm, insn_idx); if (err) return err; @@ -3268,6 +3269,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) } /* fixup insn->imm field of bpf_call instructions + * and inline eligible helpers as explicit sequence of BPF instructions * * this function is called after eBPF program passed verification */ @@ -3277,7 +3279,10 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env) struct bpf_insn *insn = prog->insnsi; const struct bpf_func_proto *fn; const int insn_cnt = prog->len; - int i; + struct bpf_insn insn_buf[16]; + struct bpf_prog *new_prog; + struct bpf_map *map_ptr; + int i, cnt, delta = 0; for (i = 0; i < insn_cnt; i++, insn++) { if (insn->code != (BPF_JMP | BPF_CALL)) @@ -3300,6 +3305,31 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env) continue; } + if (ebpf_jit_enabled() && insn->imm == BPF_FUNC_map_lookup_elem) { + map_ptr = env->insn_aux_data[i + delta].map_ptr; + if (!map_ptr->ops->map_gen_lookup) + goto patch_call_imm; + + cnt = map_ptr->ops->map_gen_lookup(map_ptr, insn_buf); + if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) { + verbose("bpf verifier is misconfigured\n"); + return -EINVAL; + } + + new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, + cnt); + if (!new_prog) + return -ENOMEM; + + delta += cnt - 1; + + /* keep walking new program and skip insns we just inserted */ + env->prog = prog = new_prog; + insn = new_prog->insnsi + i + delta; + continue; + } + +patch_call_imm: fn = prog->aux->ops->get_func_proto(insn->imm); /* all functions that have prototype and verifier allowed * programs to call them, must be real in-kernel functions -- cgit From 9015d2f5953590e8273392b44c2b0f864350b427 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 15 Mar 2017 18:26:43 -0700 Subject: bpf: inline htab_map_lookup_elem() Optimize: bpf_call bpf_map_lookup_elem map->ops->map_lookup_elem htab_map_lookup_elem __htab_map_lookup_elem into: bpf_call __htab_map_lookup_elem to improve performance of JITed programs. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- kernel/bpf/hashtab.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index afe5bab376c9..000153acb6d5 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -419,7 +419,11 @@ again: return NULL; } -/* Called from syscall or from eBPF program */ +/* Called from syscall or from eBPF program directly, so + * arguments have to match bpf_map_lookup_elem() exactly. + * The return value is adjusted by BPF instructions + * in htab_map_gen_lookup(). + */ static void *__htab_map_lookup_elem(struct bpf_map *map, void *key) { struct bpf_htab *htab = container_of(map, struct bpf_htab, map); @@ -451,6 +455,30 @@ static void *htab_map_lookup_elem(struct bpf_map *map, void *key) return NULL; } +/* inline bpf_map_lookup_elem() call. + * Instead of: + * bpf_prog + * bpf_map_lookup_elem + * map->ops->map_lookup_elem + * htab_map_lookup_elem + * __htab_map_lookup_elem + * do: + * bpf_prog + * __htab_map_lookup_elem + */ +static u32 htab_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf) +{ + struct bpf_insn *insn = insn_buf; + const int ret = BPF_REG_0; + + *insn++ = BPF_EMIT_CALL((u64 (*)(u64, u64, u64, u64, u64))__htab_map_lookup_elem); + *insn++ = BPF_JMP_IMM(BPF_JEQ, ret, 0, 1); + *insn++ = BPF_ALU64_IMM(BPF_ADD, ret, + offsetof(struct htab_elem, key) + + round_up(map->key_size, 8)); + return insn - insn_buf; +} + static void *htab_lru_map_lookup_elem(struct bpf_map *map, void *key) { struct htab_elem *l = __htab_map_lookup_elem(map, key); @@ -1062,6 +1090,7 @@ static const struct bpf_map_ops htab_ops = { .map_lookup_elem = htab_map_lookup_elem, .map_update_elem = htab_map_update_elem, .map_delete_elem = htab_map_delete_elem, + .map_gen_lookup = htab_map_gen_lookup, }; static struct bpf_map_type_list htab_type __ro_after_init = { -- cgit From 95ff141e52f84f476fcde50560f42d4f118539c0 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Wed, 15 Mar 2017 18:26:44 -0700 Subject: samples/bpf: add map_lookup microbenchmark $ map_perf_test 128 speed of HASH bpf_map_lookup_elem() in lookups per second w/o JIT w/JIT before 46M 58M after 42M 74M perf report before: 54.23% map_perf_test [kernel.kallsyms] [k] __htab_map_lookup_elem 14.24% map_perf_test [kernel.kallsyms] [k] lookup_elem_raw 8.84% map_perf_test [kernel.kallsyms] [k] htab_map_lookup_elem 5.93% map_perf_test [kernel.kallsyms] [k] bpf_map_lookup_elem 2.30% map_perf_test [kernel.kallsyms] [k] bpf_prog_da4fc6a3f41761a2 1.49% map_perf_test [kernel.kallsyms] [k] kprobe_ftrace_handler after: 60.03% map_perf_test [kernel.kallsyms] [k] __htab_map_lookup_elem 18.07% map_perf_test [kernel.kallsyms] [k] lookup_elem_raw 2.91% map_perf_test [kernel.kallsyms] [k] bpf_prog_da4fc6a3f41761a2 1.94% map_perf_test [kernel.kallsyms] [k] _einittext 1.90% map_perf_test [kernel.kallsyms] [k] __audit_syscall_exit 1.72% map_perf_test [kernel.kallsyms] [k] kprobe_ftrace_handler Notice that bpf_map_lookup_elem() and htab_map_lookup_elem() are trivial functions, yet they take sizeable amount of cpu time. htab_map_gen_lookup() removes bpf_map_lookup_elem() and converts htab_map_lookup_elem() into three BPF insns which causing cpu time for bpf_prog_da4fc6a3f41761a2() slightly increase. $ map_perf_test 256 speed of ARRAY bpf_map_lookup_elem() in lookups per second w/o JIT w/JIT before 97M 174M after 64M 280M before: 37.33% map_perf_test [kernel.kallsyms] [k] array_map_lookup_elem 13.95% map_perf_test [kernel.kallsyms] [k] bpf_map_lookup_elem 6.54% map_perf_test [kernel.kallsyms] [k] bpf_prog_da4fc6a3f41761a2 4.57% map_perf_test [kernel.kallsyms] [k] kprobe_ftrace_handler after: 32.86% map_perf_test [kernel.kallsyms] [k] bpf_prog_da4fc6a3f41761a2 6.54% map_perf_test [kernel.kallsyms] [k] kprobe_ftrace_handler array_map_gen_lookup() removes calls to array_map_lookup_elem() and bpf_map_lookup_elem() and replaces them with 7 bpf insns. The performance without JIT is slower, since executing extra insns in the interpreter is slower than running native C code, but with JIT the performance gains are obvious, since native C->x86 code is replaced with fewer bpf->x86 instructions. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- samples/bpf/map_perf_test_kern.c | 33 +++++++++++++++++++++++++++++++++ samples/bpf/map_perf_test_user.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/samples/bpf/map_perf_test_kern.c b/samples/bpf/map_perf_test_kern.c index a91872a97742..9da2a3441b0a 100644 --- a/samples/bpf/map_perf_test_kern.c +++ b/samples/bpf/map_perf_test_kern.c @@ -65,6 +65,13 @@ struct bpf_map_def SEC("maps") lpm_trie_map_alloc = { .map_flags = BPF_F_NO_PREALLOC, }; +struct bpf_map_def SEC("maps") array_map = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(u32), + .value_size = sizeof(long), + .max_entries = MAX_ENTRIES, +}; + SEC("kprobe/sys_getuid") int stress_hmap(struct pt_regs *ctx) { @@ -165,5 +172,31 @@ int stress_lpm_trie_map_alloc(struct pt_regs *ctx) return 0; } +SEC("kprobe/sys_getpgid") +int stress_hash_map_lookup(struct pt_regs *ctx) +{ + u32 key = 1, i; + long *value; + +#pragma clang loop unroll(full) + for (i = 0; i < 64; ++i) + value = bpf_map_lookup_elem(&hash_map, &key); + + return 0; +} + +SEC("kprobe/sys_getpgrp") +int stress_array_map_lookup(struct pt_regs *ctx) +{ + u32 key = 1, i; + long *value; + +#pragma clang loop unroll(full) + for (i = 0; i < 64; ++i) + value = bpf_map_lookup_elem(&array_map, &key); + + return 0; +} + char _license[] SEC("license") = "GPL"; u32 _version SEC("version") = LINUX_VERSION_CODE; diff --git a/samples/bpf/map_perf_test_user.c b/samples/bpf/map_perf_test_user.c index 680260a91f50..e29ff318a793 100644 --- a/samples/bpf/map_perf_test_user.c +++ b/samples/bpf/map_perf_test_user.c @@ -38,6 +38,8 @@ static __u64 time_get_ns(void) #define LRU_HASH_PREALLOC (1 << 4) #define PERCPU_LRU_HASH_PREALLOC (1 << 5) #define LPM_KMALLOC (1 << 6) +#define HASH_LOOKUP (1 << 7) +#define ARRAY_LOOKUP (1 << 8) static int test_flags = ~0; @@ -125,6 +127,30 @@ static void test_lpm_kmalloc(int cpu) cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); } +static void test_hash_lookup(int cpu) +{ + __u64 start_time; + int i; + + start_time = time_get_ns(); + for (i = 0; i < MAX_CNT; i++) + syscall(__NR_getpgid, 0); + printf("%d:hash_lookup %lld lookups per sec\n", + cpu, MAX_CNT * 1000000000ll * 64 / (time_get_ns() - start_time)); +} + +static void test_array_lookup(int cpu) +{ + __u64 start_time; + int i; + + start_time = time_get_ns(); + for (i = 0; i < MAX_CNT; i++) + syscall(__NR_getpgrp, 0); + printf("%d:array_lookup %lld lookups per sec\n", + cpu, MAX_CNT * 1000000000ll * 64 / (time_get_ns() - start_time)); +} + static void loop(int cpu) { cpu_set_t cpuset; @@ -153,6 +179,12 @@ static void loop(int cpu) if (test_flags & LPM_KMALLOC) test_lpm_kmalloc(cpu); + + if (test_flags & HASH_LOOKUP) + test_hash_lookup(cpu); + + if (test_flags & ARRAY_LOOKUP) + test_array_lookup(cpu); } static void run_perf_test(int tasks) -- cgit From 6de38af611ca81a970965c06231cd2d5f30b2566 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Thu, 16 Mar 2017 16:12:37 -0700 Subject: netvsc: avoid race with callback Change the argument to channel callback from the channel pointer to the internal data structure containing per-channel info. This avoids any possible races when callback happens during initialization and makes IRQ code simpler. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 18 +++++------------- drivers/net/hyperv/rndis_filter.c | 15 ++++++++++----- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 0e71164849dd..0a2e9bd98d2c 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -1250,21 +1250,12 @@ int netvsc_poll(struct napi_struct *napi, int budget) void netvsc_channel_cb(void *context) { - struct vmbus_channel *channel = context; - struct hv_device *device = netvsc_channel_to_device(channel); - u16 q_idx = channel->offermsg.offer.sub_channel_index; - struct netvsc_device *net_device; - struct net_device *ndev; - - ndev = hv_get_drvdata(device); - if (unlikely(!ndev)) - return; + struct netvsc_channel *nvchan = context; /* disable interupts from host */ - hv_begin_read(&channel->inbound); + hv_begin_read(&nvchan->channel->inbound); - net_device = net_device_to_netvsc_device(ndev); - napi_schedule(&net_device->chan_table[q_idx].napi); + napi_schedule(&nvchan->napi); } /* @@ -1294,7 +1285,8 @@ int netvsc_device_add(struct hv_device *device, /* Open the channel */ ret = vmbus_open(device->channel, ring_size * PAGE_SIZE, ring_size * PAGE_SIZE, NULL, 0, - netvsc_channel_cb, device->channel); + netvsc_channel_cb, + net_device->chan_table); if (ret != 0) { netdev_err(ndev, "unable to open channel: %d\n", ret); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index d7b6311e6c19..382b9a62e3c4 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -996,23 +996,28 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) hv_get_drvdata(new_sc->primary_channel->device_obj); struct netvsc_device *nvscdev = net_device_to_netvsc_device(ndev); u16 chn_index = new_sc->offermsg.offer.sub_channel_index; - int ret; + struct netvsc_channel *nvchan; unsigned long flags; + int ret; if (chn_index >= nvscdev->num_chn) return; - nvscdev->chan_table[chn_index].mrc.buf + nvchan = nvscdev->chan_table + chn_index; + nvchan->mrc.buf = vzalloc(NETVSC_RECVSLOT_MAX * sizeof(struct recv_comp_data)); + if (!nvchan->mrc.buf) + return; + ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE, nvscdev->ring_size * PAGE_SIZE, NULL, 0, - netvsc_channel_cb, new_sc); + netvsc_channel_cb, nvchan); if (ret == 0) - nvscdev->chan_table[chn_index].channel = new_sc; + nvchan->channel = new_sc; - napi_enable(&nvscdev->chan_table[chn_index].napi); + napi_enable(&nvchan->napi); spin_lock_irqsave(&nvscdev->sc_lock, flags); nvscdev->num_sc_offered--; -- cgit From 262b7f142a50badde4032e925902a0d0ad89eb7f Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Thu, 16 Mar 2017 16:12:38 -0700 Subject: netvsc: add comments about callback's and NAPI Add some short description of how callback's and NAPI interoperate. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 0a2e9bd98d2c..989b7cd99380 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -1209,6 +1209,10 @@ static struct hv_device *netvsc_channel_to_device(struct vmbus_channel *channel) return primary ? primary->device_obj : channel->device_obj; } +/* Network processing softirq + * Process data in incoming ring buffer from host + * Stops when ring is empty or budget is met or exceeded. + */ int netvsc_poll(struct napi_struct *napi, int budget) { struct netvsc_channel *nvchan @@ -1238,7 +1242,11 @@ int netvsc_poll(struct napi_struct *napi, int budget) } hv_pkt_iter_close(channel); - /* If ring is empty and NAPI is not doing polling */ + /* If budget was not exhausted and + * not doing busy poll + * then re-enable host interrupts + * and reschedule if ring is not empty. + */ if (work_done < budget && napi_complete_done(napi, work_done) && hv_end_read(&channel->inbound) != 0) @@ -1248,6 +1256,9 @@ int netvsc_poll(struct napi_struct *napi, int budget) return work_done; } +/* Call back when data is available in host ring buffer. + * Processing is deferred until network softirq (NAPI) + */ void netvsc_channel_cb(void *context) { struct netvsc_channel *nvchan = context; -- cgit From 76f5ed881c8de0264283c5b934deb33bf87209a8 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Thu, 16 Mar 2017 16:12:39 -0700 Subject: netvsc: remove unused #define Not used anywhere. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index b09c4fca1805..6b5f75217694 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -1427,9 +1427,6 @@ struct rndis_message { ((void *) rndis_msg) -#define __struct_bcount(x) - - #define RNDIS_HEADER_SIZE (sizeof(struct rndis_message) - \ sizeof(union rndis_message_container)) -- cgit From fe723dff0fa4181ddb8116e72bc67d00d4239cb6 Mon Sep 17 00:00:00 2001 From: Manish Awasthi Date: Thu, 16 Mar 2017 16:16:17 -0700 Subject: liquidio: fix wrong information about link modes reported to ethtool Information reported to ethtool about link modes is wrong for 25G NIC. Fix it by checking for presence of 25G NIC, checking the link speed reported by NIC firmware, and then assigning proper values to the ethtool_link_ksettings struct. Signed-off-by: Manish Awasthi Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index 50384cede8be..6eef3b999130 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -213,17 +213,23 @@ static int lio_get_link_ksettings(struct net_device *netdev, struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; struct oct_link_info *linfo; - u32 supported, advertising; + u32 supported = 0, advertising = 0; linfo = &lio->linfo; if (linfo->link.s.if_mode == INTERFACE_MODE_XAUI || linfo->link.s.if_mode == INTERFACE_MODE_RXAUI || + linfo->link.s.if_mode == INTERFACE_MODE_XLAUI || linfo->link.s.if_mode == INTERFACE_MODE_XFI) { ecmd->base.port = PORT_FIBRE; - supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE | - SUPPORTED_Pause); - advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_Pause); + + if (linfo->link.s.speed == SPEED_10000) { + supported = SUPPORTED_10000baseT_Full; + advertising = ADVERTISED_10000baseT_Full; + } + + supported |= SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising |= ADVERTISED_Pause; ethtool_convert_legacy_u32_to_link_mode( ecmd->link_modes.supported, supported); ethtool_convert_legacy_u32_to_link_mode( -- cgit From b6ecfd469e82886308b44bf715d48fbe7b19ad4c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 17 Mar 2017 10:24:15 +0100 Subject: cfg80211: preserve wdev ID across netns changes When a wdev changes network namespace, its wdev ID will get reassigned since NETDEV_REGISTER is called again, in the new network namespace. Avoid that by checking if it was already assigned before, and document why we do that. Reported-and-tested-by: Arend Van Spriel Signed-off-by: Johannes Berg --- net/wireless/core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index b1a028d381ef..b0d6761f0cdd 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1159,7 +1159,15 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, INIT_LIST_HEAD(&wdev->mgmt_registrations); spin_lock_init(&wdev->mgmt_registrations_lock); - wdev->identifier = ++rdev->wdev_id; + /* + * We get here also when the interface changes network namespaces, + * as it's registered into the new one, but we don't want it to + * change ID in that case. Checking if the ID is already assigned + * works, because 0 isn't considered a valid ID and the memory is + * 0-initialized. + */ + if (!wdev->identifier) + wdev->identifier = ++rdev->wdev_id; list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list); rdev->devlist_generation++; /* can only change netns with wiphy */ -- cgit From b54ab92b84b6161f91b1ad9160199422b3699009 Mon Sep 17 00:00:00 2001 From: "Reshetova, Elena" Date: Thu, 16 Mar 2017 10:03:34 +0200 Subject: netfilter: refcounter conversions refcount_t type and corresponding API (see include/linux/refcount.h) should be used instead of atomic_t when the variable is used as a reference counter. This allows to avoid accidental refcounter overflows that might lead to use-after-free situations. Signed-off-by: Elena Reshetova Signed-off-by: Hans Liljestrand Signed-off-by: Kees Cook Signed-off-by: David Windsor Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 16 +++++++++------- include/net/netfilter/nf_conntrack_expect.h | 4 +++- include/net/netfilter/nf_conntrack_timeout.h | 3 ++- net/ipv4/netfilter/ipt_CLUSTERIP.c | 19 ++++++++++--------- net/netfilter/ipvs/ip_vs_conn.c | 24 ++++++++++++------------ net/netfilter/ipvs/ip_vs_core.c | 4 ++-- net/netfilter/ipvs/ip_vs_ctl.c | 12 ++++++------ net/netfilter/ipvs/ip_vs_lblc.c | 2 +- net/netfilter/ipvs/ip_vs_lblcr.c | 6 +++--- net/netfilter/ipvs/ip_vs_nq.c | 2 +- net/netfilter/ipvs/ip_vs_proto_sctp.c | 2 +- net/netfilter/ipvs/ip_vs_proto_tcp.c | 2 +- net/netfilter/ipvs/ip_vs_rr.c | 2 +- net/netfilter/ipvs/ip_vs_sed.c | 2 +- net/netfilter/ipvs/ip_vs_wlc.c | 2 +- net/netfilter/ipvs/ip_vs_wrr.c | 2 +- net/netfilter/nf_conntrack_expect.c | 10 +++++----- net/netfilter/nf_conntrack_netlink.c | 4 ++-- net/netfilter/nfnetlink_acct.c | 16 +++++++++------- net/netfilter/nfnetlink_cttimeout.c | 12 ++++++------ net/netfilter/nfnetlink_log.c | 14 ++++++++------ 21 files changed, 85 insertions(+), 75 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 7bdfa7d78363..8a4a57b887fb 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -12,6 +12,8 @@ #include /* for struct list_head */ #include /* for struct rwlock_t */ #include /* for struct atomic_t */ +#include /* for struct refcount_t */ + #include #include #include @@ -525,7 +527,7 @@ struct ip_vs_conn { struct netns_ipvs *ipvs; /* counter and timer */ - atomic_t refcnt; /* reference count */ + refcount_t refcnt; /* reference count */ struct timer_list timer; /* Expiration timer */ volatile unsigned long timeout; /* timeout */ @@ -667,7 +669,7 @@ struct ip_vs_dest { atomic_t conn_flags; /* flags to copy to conn */ atomic_t weight; /* server weight */ - atomic_t refcnt; /* reference counter */ + refcount_t refcnt; /* reference counter */ struct ip_vs_stats stats; /* statistics */ unsigned long idle_start; /* start time, jiffies */ @@ -1211,14 +1213,14 @@ struct ip_vs_conn * ip_vs_conn_out_get_proto(struct netns_ipvs *ipvs, int af, */ static inline bool __ip_vs_conn_get(struct ip_vs_conn *cp) { - return atomic_inc_not_zero(&cp->refcnt); + return refcount_inc_not_zero(&cp->refcnt); } /* put back the conn without restarting its timer */ static inline void __ip_vs_conn_put(struct ip_vs_conn *cp) { smp_mb__before_atomic(); - atomic_dec(&cp->refcnt); + refcount_dec(&cp->refcnt); } void ip_vs_conn_put(struct ip_vs_conn *cp); void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport); @@ -1410,18 +1412,18 @@ void ip_vs_try_bind_dest(struct ip_vs_conn *cp); static inline void ip_vs_dest_hold(struct ip_vs_dest *dest) { - atomic_inc(&dest->refcnt); + refcount_inc(&dest->refcnt); } static inline void ip_vs_dest_put(struct ip_vs_dest *dest) { smp_mb__before_atomic(); - atomic_dec(&dest->refcnt); + refcount_dec(&dest->refcnt); } static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest) { - if (atomic_dec_and_test(&dest->refcnt)) + if (refcount_dec_and_test(&dest->refcnt)) kfree(dest); } diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index 5ed33ea4718e..65cc2cb005d9 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -5,6 +5,8 @@ #ifndef _NF_CONNTRACK_EXPECT_H #define _NF_CONNTRACK_EXPECT_H +#include + #include #include @@ -37,7 +39,7 @@ struct nf_conntrack_expect { struct timer_list timeout; /* Usage count. */ - atomic_t use; + refcount_t use; /* Flags */ unsigned int flags; diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h index 5cc5e9e6171a..d40b89355fdd 100644 --- a/include/net/netfilter/nf_conntrack_timeout.h +++ b/include/net/netfilter/nf_conntrack_timeout.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -12,7 +13,7 @@ struct ctnl_timeout { struct list_head head; struct rcu_head rcu_head; - atomic_t refcnt; + refcount_t refcnt; char name[CTNL_TIMEOUT_NAME_MAX]; __u16 l3num; struct nf_conntrack_l4proto *l4proto; diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 52f26459efc3..fcbdc0c49b0e 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -40,8 +41,8 @@ MODULE_DESCRIPTION("Xtables: CLUSTERIP target"); struct clusterip_config { struct list_head list; /* list of all configs */ - atomic_t refcount; /* reference count */ - atomic_t entries; /* number of entries/rules + refcount_t refcount; /* reference count */ + refcount_t entries; /* number of entries/rules * referencing us */ __be32 clusterip; /* the IP address */ @@ -77,7 +78,7 @@ struct clusterip_net { static inline void clusterip_config_get(struct clusterip_config *c) { - atomic_inc(&c->refcount); + refcount_inc(&c->refcount); } @@ -89,7 +90,7 @@ static void clusterip_config_rcu_free(struct rcu_head *head) static inline void clusterip_config_put(struct clusterip_config *c) { - if (atomic_dec_and_test(&c->refcount)) + if (refcount_dec_and_test(&c->refcount)) call_rcu_bh(&c->rcu, clusterip_config_rcu_free); } @@ -103,7 +104,7 @@ clusterip_config_entry_put(struct clusterip_config *c) struct clusterip_net *cn = net_generic(net, clusterip_net_id); local_bh_disable(); - if (atomic_dec_and_lock(&c->entries, &cn->lock)) { + if (refcount_dec_and_lock(&c->entries, &cn->lock)) { list_del_rcu(&c->list); spin_unlock(&cn->lock); local_bh_enable(); @@ -149,10 +150,10 @@ clusterip_config_find_get(struct net *net, __be32 clusterip, int entry) c = NULL; else #endif - if (unlikely(!atomic_inc_not_zero(&c->refcount))) + if (unlikely(!refcount_inc_not_zero(&c->refcount))) c = NULL; else if (entry) - atomic_inc(&c->entries); + refcount_inc(&c->entries); } rcu_read_unlock_bh(); @@ -188,8 +189,8 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip, clusterip_config_init_nodelist(c, i); c->hash_mode = i->hash_mode; c->hash_initval = i->hash_initval; - atomic_set(&c->refcount, 1); - atomic_set(&c->entries, 1); + refcount_set(&c->refcount, 1); + refcount_set(&c->entries, 1); spin_lock_bh(&cn->lock); if (__clusterip_config_find(net, ip)) { diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index e6a2753dff9e..3d2ac71a83ec 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -181,7 +181,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) if (!(cp->flags & IP_VS_CONN_F_HASHED)) { cp->flags |= IP_VS_CONN_F_HASHED; - atomic_inc(&cp->refcnt); + refcount_inc(&cp->refcnt); hlist_add_head_rcu(&cp->c_list, &ip_vs_conn_tab[hash]); ret = 1; } else { @@ -215,7 +215,7 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp) if (cp->flags & IP_VS_CONN_F_HASHED) { hlist_del_rcu(&cp->c_list); cp->flags &= ~IP_VS_CONN_F_HASHED; - atomic_dec(&cp->refcnt); + refcount_dec(&cp->refcnt); ret = 1; } else ret = 0; @@ -242,13 +242,13 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp) if (cp->flags & IP_VS_CONN_F_HASHED) { ret = false; /* Decrease refcnt and unlink conn only if we are last user */ - if (atomic_cmpxchg(&cp->refcnt, 1, 0) == 1) { + if (refcount_dec_if_one(&cp->refcnt)) { hlist_del_rcu(&cp->c_list); cp->flags &= ~IP_VS_CONN_F_HASHED; ret = true; } } else - ret = atomic_read(&cp->refcnt) ? false : true; + ret = refcount_read(&cp->refcnt) ? false : true; spin_unlock(&cp->lock); ct_write_unlock_bh(hash); @@ -475,7 +475,7 @@ static void __ip_vs_conn_put_timer(struct ip_vs_conn *cp) void ip_vs_conn_put(struct ip_vs_conn *cp) { if ((cp->flags & IP_VS_CONN_F_ONE_PACKET) && - (atomic_read(&cp->refcnt) == 1) && + (refcount_read(&cp->refcnt) == 1) && !timer_pending(&cp->timer)) /* expire connection immediately */ __ip_vs_conn_put_notimer(cp); @@ -617,8 +617,8 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest) IP_VS_DBG_ADDR(cp->af, &cp->vaddr), ntohs(cp->vport), IP_VS_DBG_ADDR(cp->daf, &cp->daddr), ntohs(cp->dport), ip_vs_fwd_tag(cp), cp->state, - cp->flags, atomic_read(&cp->refcnt), - atomic_read(&dest->refcnt)); + cp->flags, refcount_read(&cp->refcnt), + refcount_read(&dest->refcnt)); /* Update the connection counters */ if (!(flags & IP_VS_CONN_F_TEMPLATE)) { @@ -714,8 +714,8 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp) IP_VS_DBG_ADDR(cp->af, &cp->vaddr), ntohs(cp->vport), IP_VS_DBG_ADDR(cp->daf, &cp->daddr), ntohs(cp->dport), ip_vs_fwd_tag(cp), cp->state, - cp->flags, atomic_read(&cp->refcnt), - atomic_read(&dest->refcnt)); + cp->flags, refcount_read(&cp->refcnt), + refcount_read(&dest->refcnt)); /* Update the connection counters */ if (!(cp->flags & IP_VS_CONN_F_TEMPLATE)) { @@ -863,10 +863,10 @@ static void ip_vs_conn_expire(unsigned long data) expire_later: IP_VS_DBG(7, "delayed: conn->refcnt=%d conn->n_control=%d\n", - atomic_read(&cp->refcnt), + refcount_read(&cp->refcnt), atomic_read(&cp->n_control)); - atomic_inc(&cp->refcnt); + refcount_inc(&cp->refcnt); cp->timeout = 60*HZ; if (ipvs->sync_state & IP_VS_STATE_MASTER) @@ -941,7 +941,7 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, int dest_af, * it in the table, so that other thread run ip_vs_random_dropentry * but cannot drop this entry. */ - atomic_set(&cp->refcnt, 1); + refcount_set(&cp->refcnt, 1); cp->control = NULL; atomic_set(&cp->n_control, 0); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 9aaa49025cdc..b4a746d0e39b 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -542,7 +542,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, IP_VS_DBG_ADDR(cp->af, &cp->caddr), ntohs(cp->cport), IP_VS_DBG_ADDR(cp->af, &cp->vaddr), ntohs(cp->vport), IP_VS_DBG_ADDR(cp->daf, &cp->daddr), ntohs(cp->dport), - cp->flags, atomic_read(&cp->refcnt)); + cp->flags, refcount_read(&cp->refcnt)); ip_vs_conn_stats(cp, svc); return cp; @@ -1193,7 +1193,7 @@ struct ip_vs_conn *ip_vs_new_conn_out(struct ip_vs_service *svc, IP_VS_DBG_ADDR(cp->af, &cp->caddr), ntohs(cp->cport), IP_VS_DBG_ADDR(cp->af, &cp->vaddr), ntohs(cp->vport), IP_VS_DBG_ADDR(cp->af, &cp->daddr), ntohs(cp->dport), - cp->flags, atomic_read(&cp->refcnt)); + cp->flags, refcount_read(&cp->refcnt)); LeaveFunction(12); return cp; } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 5aeb0dde6ccc..541aa7694775 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -699,7 +699,7 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, int dest_af, dest->vfwmark, IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port), - atomic_read(&dest->refcnt)); + refcount_read(&dest->refcnt)); if (dest->af == dest_af && ip_vs_addr_equal(dest_af, &dest->addr, daddr) && dest->port == dport && @@ -934,7 +934,7 @@ ip_vs_new_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest, atomic_set(&dest->activeconns, 0); atomic_set(&dest->inactconns, 0); atomic_set(&dest->persistconns, 0); - atomic_set(&dest->refcnt, 1); + refcount_set(&dest->refcnt, 1); INIT_HLIST_NODE(&dest->d_list); spin_lock_init(&dest->dst_lock); @@ -998,7 +998,7 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) IP_VS_DBG_BUF(3, "Get destination %s:%u from trash, " "dest->refcnt=%d, service %u/%s:%u\n", IP_VS_DBG_ADDR(udest->af, &daddr), ntohs(dport), - atomic_read(&dest->refcnt), + refcount_read(&dest->refcnt), dest->vfwmark, IP_VS_DBG_ADDR(svc->af, &dest->vaddr), ntohs(dest->vport)); @@ -1074,7 +1074,7 @@ static void __ip_vs_del_dest(struct netns_ipvs *ipvs, struct ip_vs_dest *dest, spin_lock_bh(&ipvs->dest_trash_lock); IP_VS_DBG_BUF(3, "Moving dest %s:%u into trash, dest->refcnt=%d\n", IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port), - atomic_read(&dest->refcnt)); + refcount_read(&dest->refcnt)); if (list_empty(&ipvs->dest_trash) && !cleanup) mod_timer(&ipvs->dest_trash_timer, jiffies + (IP_VS_DEST_TRASH_PERIOD >> 1)); @@ -1157,7 +1157,7 @@ static void ip_vs_dest_trash_expire(unsigned long data) spin_lock(&ipvs->dest_trash_lock); list_for_each_entry_safe(dest, next, &ipvs->dest_trash, t_list) { - if (atomic_read(&dest->refcnt) > 1) + if (refcount_read(&dest->refcnt) > 1) continue; if (dest->idle_start) { if (time_before(now, dest->idle_start + @@ -1545,7 +1545,7 @@ ip_vs_forget_dev(struct ip_vs_dest *dest, struct net_device *dev) dev->name, IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port), - atomic_read(&dest->refcnt)); + refcount_read(&dest->refcnt)); __ip_vs_dst_cache_reset(dest); } spin_unlock_bh(&dest->dst_lock); diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 5824927cf8e0..b6aa4a970c6e 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -448,7 +448,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc) IP_VS_DBG_ADDR(least->af, &least->addr), ntohs(least->port), atomic_read(&least->activeconns), - atomic_read(&least->refcnt), + refcount_read(&least->refcnt), atomic_read(&least->weight), loh); return least; diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index 703f11877bee..c13ff575f9f7 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -204,7 +204,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) IP_VS_DBG_ADDR(least->af, &least->addr), ntohs(least->port), atomic_read(&least->activeconns), - atomic_read(&least->refcnt), + refcount_read(&least->refcnt), atomic_read(&least->weight), loh); return least; } @@ -249,7 +249,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set) __func__, IP_VS_DBG_ADDR(most->af, &most->addr), ntohs(most->port), atomic_read(&most->activeconns), - atomic_read(&most->refcnt), + refcount_read(&most->refcnt), atomic_read(&most->weight), moh); return most; } @@ -612,7 +612,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc) IP_VS_DBG_ADDR(least->af, &least->addr), ntohs(least->port), atomic_read(&least->activeconns), - atomic_read(&least->refcnt), + refcount_read(&least->refcnt), atomic_read(&least->weight), loh); return least; diff --git a/net/netfilter/ipvs/ip_vs_nq.c b/net/netfilter/ipvs/ip_vs_nq.c index a8b63401e773..7d9d4ac596ca 100644 --- a/net/netfilter/ipvs/ip_vs_nq.c +++ b/net/netfilter/ipvs/ip_vs_nq.c @@ -110,7 +110,7 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, IP_VS_DBG_ADDR(least->af, &least->addr), ntohs(least->port), atomic_read(&least->activeconns), - atomic_read(&least->refcnt), + refcount_read(&least->refcnt), atomic_read(&least->weight), loh); return least; diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index d952d67f904d..56f8e4b204ff 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -447,7 +447,7 @@ set_sctp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, ntohs(cp->cport), sctp_state_name(cp->state), sctp_state_name(next_state), - atomic_read(&cp->refcnt)); + refcount_read(&cp->refcnt)); if (dest) { if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && (next_state != IP_VS_SCTP_S_ESTABLISHED)) { diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 5117bcb7d2f0..12dc8d5bc37d 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -557,7 +557,7 @@ set_tcp_state(struct ip_vs_proto_data *pd, struct ip_vs_conn *cp, ntohs(cp->cport), tcp_state_name(cp->state), tcp_state_name(new_state), - atomic_read(&cp->refcnt)); + refcount_read(&cp->refcnt)); if (dest) { if (!(cp->flags & IP_VS_CONN_F_INACTIVE) && diff --git a/net/netfilter/ipvs/ip_vs_rr.c b/net/netfilter/ipvs/ip_vs_rr.c index 58bacfc461ee..ee0530d14c5f 100644 --- a/net/netfilter/ipvs/ip_vs_rr.c +++ b/net/netfilter/ipvs/ip_vs_rr.c @@ -97,7 +97,7 @@ stop: "activeconns %d refcnt %d weight %d\n", IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port), atomic_read(&dest->activeconns), - atomic_read(&dest->refcnt), atomic_read(&dest->weight)); + refcount_read(&dest->refcnt), atomic_read(&dest->weight)); return dest; } diff --git a/net/netfilter/ipvs/ip_vs_sed.c b/net/netfilter/ipvs/ip_vs_sed.c index f8e2d00f528b..ab23cf203437 100644 --- a/net/netfilter/ipvs/ip_vs_sed.c +++ b/net/netfilter/ipvs/ip_vs_sed.c @@ -111,7 +111,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, IP_VS_DBG_ADDR(least->af, &least->addr), ntohs(least->port), atomic_read(&least->activeconns), - atomic_read(&least->refcnt), + refcount_read(&least->refcnt), atomic_read(&least->weight), loh); return least; diff --git a/net/netfilter/ipvs/ip_vs_wlc.c b/net/netfilter/ipvs/ip_vs_wlc.c index 6b366fd90554..6add39e0ec20 100644 --- a/net/netfilter/ipvs/ip_vs_wlc.c +++ b/net/netfilter/ipvs/ip_vs_wlc.c @@ -83,7 +83,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, IP_VS_DBG_ADDR(least->af, &least->addr), ntohs(least->port), atomic_read(&least->activeconns), - atomic_read(&least->refcnt), + refcount_read(&least->refcnt), atomic_read(&least->weight), loh); return least; diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c index 17e6d4406ca7..62258dd457ac 100644 --- a/net/netfilter/ipvs/ip_vs_wrr.c +++ b/net/netfilter/ipvs/ip_vs_wrr.c @@ -218,7 +218,7 @@ found: "activeconns %d refcnt %d weight %d\n", IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port), atomic_read(&dest->activeconns), - atomic_read(&dest->refcnt), + refcount_read(&dest->refcnt), atomic_read(&dest->weight)); mark->cl = dest; diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 4b2e1fb28bb4..cb29e598605f 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -133,7 +133,7 @@ nf_ct_expect_find_get(struct net *net, rcu_read_lock(); i = __nf_ct_expect_find(net, zone, tuple); - if (i && !atomic_inc_not_zero(&i->use)) + if (i && !refcount_inc_not_zero(&i->use)) i = NULL; rcu_read_unlock(); @@ -186,7 +186,7 @@ nf_ct_find_expectation(struct net *net, return NULL; if (exp->flags & NF_CT_EXPECT_PERMANENT) { - atomic_inc(&exp->use); + refcount_inc(&exp->use); return exp; } else if (del_timer(&exp->timeout)) { nf_ct_unlink_expect(exp); @@ -275,7 +275,7 @@ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me) return NULL; new->master = me; - atomic_set(&new->use, 1); + refcount_set(&new->use, 1); return new; } EXPORT_SYMBOL_GPL(nf_ct_expect_alloc); @@ -348,7 +348,7 @@ static void nf_ct_expect_free_rcu(struct rcu_head *head) void nf_ct_expect_put(struct nf_conntrack_expect *exp) { - if (atomic_dec_and_test(&exp->use)) + if (refcount_dec_and_test(&exp->use)) call_rcu(&exp->rcu, nf_ct_expect_free_rcu); } EXPORT_SYMBOL_GPL(nf_ct_expect_put); @@ -361,7 +361,7 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) unsigned int h = nf_ct_expect_dst_hash(net, &exp->tuple); /* two references : one for hash insert, one for the timer */ - atomic_add(2, &exp->use); + refcount_add(2, &exp->use); hlist_add_head(&exp->lnode, &master_help->expectations); master_help->expecting[exp->class]++; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 6806b5e73567..d49cc1e03c5b 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2693,7 +2693,7 @@ restart: cb->nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW, exp) < 0) { - if (!atomic_inc_not_zero(&exp->use)) + if (!refcount_inc_not_zero(&exp->use)) continue; cb->args[1] = (unsigned long)exp; goto out; @@ -2739,7 +2739,7 @@ restart: cb->nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW, exp) < 0) { - if (!atomic_inc_not_zero(&exp->use)) + if (!refcount_inc_not_zero(&exp->use)) continue; cb->args[1] = (unsigned long)exp; goto out; diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c index d44d89b56127..f44cbd35357f 100644 --- a/net/netfilter/nfnetlink_acct.c +++ b/net/netfilter/nfnetlink_acct.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,7 @@ struct nf_acct { atomic64_t bytes; unsigned long flags; struct list_head head; - atomic_t refcnt; + refcount_t refcnt; char name[NFACCT_NAME_MAX]; struct rcu_head rcu_head; char data[0]; @@ -123,7 +124,7 @@ static int nfnl_acct_new(struct net *net, struct sock *nfnl, atomic64_set(&nfacct->pkts, be64_to_cpu(nla_get_be64(tb[NFACCT_PKTS]))); } - atomic_set(&nfacct->refcnt, 1); + refcount_set(&nfacct->refcnt, 1); list_add_tail_rcu(&nfacct->head, &net->nfnl_acct_list); return 0; } @@ -166,7 +167,7 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, NFACCT_PAD) || nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes), NFACCT_PAD) || - nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt)))) + nla_put_be32(skb, NFACCT_USE, htonl(refcount_read(&acct->refcnt)))) goto nla_put_failure; if (acct->flags & NFACCT_F_QUOTA) { u64 *quota = (u64 *)acct->data; @@ -325,11 +326,12 @@ static int nfnl_acct_get(struct net *net, struct sock *nfnl, static int nfnl_acct_try_del(struct nf_acct *cur) { int ret = 0; + unsigned int refcount; /* We want to avoid races with nfnl_acct_put. So only when the current * refcnt is 1, we decrease it to 0. */ - if (atomic_cmpxchg(&cur->refcnt, 1, 0) == 1) { + if (refcount_dec_if_one(&cur->refcnt)) { /* We are protected by nfnl mutex. */ list_del_rcu(&cur->head); kfree_rcu(cur, rcu_head); @@ -413,7 +415,7 @@ struct nf_acct *nfnl_acct_find_get(struct net *net, const char *acct_name) if (!try_module_get(THIS_MODULE)) goto err; - if (!atomic_inc_not_zero(&cur->refcnt)) { + if (!refcount_inc_not_zero(&cur->refcnt)) { module_put(THIS_MODULE); goto err; } @@ -429,7 +431,7 @@ EXPORT_SYMBOL_GPL(nfnl_acct_find_get); void nfnl_acct_put(struct nf_acct *acct) { - if (atomic_dec_and_test(&acct->refcnt)) + if (refcount_dec_and_test(&acct->refcnt)) kfree_rcu(acct, rcu_head); module_put(THIS_MODULE); @@ -502,7 +504,7 @@ static void __net_exit nfnl_acct_net_exit(struct net *net) list_for_each_entry_safe(cur, tmp, &net->nfnl_acct_list, head) { list_del_rcu(&cur->head); - if (atomic_dec_and_test(&cur->refcnt)) + if (refcount_dec_and_test(&cur->refcnt)) kfree_rcu(cur, rcu_head); } } diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index 139e0867e56e..baa75f3ab7e7 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -138,7 +138,7 @@ static int cttimeout_new_timeout(struct net *net, struct sock *ctnl, strcpy(timeout->name, nla_data(cda[CTA_TIMEOUT_NAME])); timeout->l3num = l3num; timeout->l4proto = l4proto; - atomic_set(&timeout->refcnt, 1); + refcount_set(&timeout->refcnt, 1); list_add_tail_rcu(&timeout->head, &net->nfct_timeout_list); return 0; @@ -172,7 +172,7 @@ ctnl_timeout_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, nla_put_be16(skb, CTA_TIMEOUT_L3PROTO, htons(timeout->l3num)) || nla_put_u8(skb, CTA_TIMEOUT_L4PROTO, timeout->l4proto->l4proto) || nla_put_be32(skb, CTA_TIMEOUT_USE, - htonl(atomic_read(&timeout->refcnt)))) + htonl(refcount_read(&timeout->refcnt)))) goto nla_put_failure; if (likely(l4proto->ctnl_timeout.obj_to_nlattr)) { @@ -339,7 +339,7 @@ static int ctnl_timeout_try_del(struct net *net, struct ctnl_timeout *timeout) /* We want to avoid races with ctnl_timeout_put. So only when the * current refcnt is 1, we decrease it to 0. */ - if (atomic_cmpxchg(&timeout->refcnt, 1, 0) == 1) { + if (refcount_dec_if_one(&timeout->refcnt)) { /* We are protected by nfnl mutex. */ list_del_rcu(&timeout->head); nf_ct_l4proto_put(timeout->l4proto); @@ -536,7 +536,7 @@ ctnl_timeout_find_get(struct net *net, const char *name) if (!try_module_get(THIS_MODULE)) goto err; - if (!atomic_inc_not_zero(&timeout->refcnt)) { + if (!refcount_inc_not_zero(&timeout->refcnt)) { module_put(THIS_MODULE); goto err; } @@ -550,7 +550,7 @@ err: static void ctnl_timeout_put(struct ctnl_timeout *timeout) { - if (atomic_dec_and_test(&timeout->refcnt)) + if (refcount_dec_and_test(&timeout->refcnt)) kfree_rcu(timeout, rcu_head); module_put(THIS_MODULE); @@ -601,7 +601,7 @@ static void __net_exit cttimeout_net_exit(struct net *net) list_del_rcu(&cur->head); nf_ct_l4proto_put(cur->l4proto); - if (atomic_dec_and_test(&cur->refcnt)) + if (refcount_dec_and_test(&cur->refcnt)) kfree_rcu(cur, rcu_head); } } diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 08247bf7d7b8..ecd857b75ffe 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -40,6 +40,8 @@ #include #include +#include + #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) #include "../bridge/br_private.h" @@ -57,7 +59,7 @@ struct nfulnl_instance { struct hlist_node hlist; /* global list of instances */ spinlock_t lock; - atomic_t use; /* use count */ + refcount_t use; /* use count */ unsigned int qlen; /* number of nlmsgs in skb */ struct sk_buff *skb; /* pre-allocatd skb */ @@ -115,7 +117,7 @@ __instance_lookup(struct nfnl_log_net *log, u_int16_t group_num) static inline void instance_get(struct nfulnl_instance *inst) { - atomic_inc(&inst->use); + refcount_inc(&inst->use); } static struct nfulnl_instance * @@ -125,7 +127,7 @@ instance_lookup_get(struct nfnl_log_net *log, u_int16_t group_num) rcu_read_lock_bh(); inst = __instance_lookup(log, group_num); - if (inst && !atomic_inc_not_zero(&inst->use)) + if (inst && !refcount_inc_not_zero(&inst->use)) inst = NULL; rcu_read_unlock_bh(); @@ -145,7 +147,7 @@ static void nfulnl_instance_free_rcu(struct rcu_head *head) static void instance_put(struct nfulnl_instance *inst) { - if (inst && atomic_dec_and_test(&inst->use)) + if (inst && refcount_dec_and_test(&inst->use)) call_rcu_bh(&inst->rcu, nfulnl_instance_free_rcu); } @@ -180,7 +182,7 @@ instance_create(struct net *net, u_int16_t group_num, INIT_HLIST_NODE(&inst->hlist); spin_lock_init(&inst->lock); /* needs to be two, since we _put() after creation */ - atomic_set(&inst->use, 2); + refcount_set(&inst->use, 2); setup_timer(&inst->timer, nfulnl_timer, (unsigned long)inst); @@ -1031,7 +1033,7 @@ static int seq_show(struct seq_file *s, void *v) inst->group_num, inst->peer_portid, inst->qlen, inst->copy_mode, inst->copy_range, - inst->flushtimeout, atomic_read(&inst->use)); + inst->flushtimeout, refcount_read(&inst->use)); return 0; } -- cgit From 4e09991af206539add91e9d793ec1de61ec72e66 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 8 Mar 2017 12:43:59 +0100 Subject: batman-adv: Use __func__ to add function names to messages The name of the function might change in which these messages are printed. It is therefore better to let the compiler handle the insertion of the correct function name. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/tp_meter.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index c94ebdecdc3d..556f9a865ddf 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -873,8 +873,8 @@ static int batadv_tp_send(void *arg) /* something went wrong during the preparation/transmission */ if (unlikely(err && err != BATADV_TP_REASON_CANT_SEND)) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, - "Meter: batadv_tp_send() cannot send packets (%d)\n", - err); + "Meter: %s() cannot send packets (%d)\n", + __func__, err); /* ensure nobody else tries to stop the thread now */ if (atomic_dec_and_test(&tp_vars->sending)) tp_vars->reason = err; @@ -979,7 +979,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, if (!tp_vars) { spin_unlock_bh(&bat_priv->tp_list_lock); batadv_dbg(BATADV_DBG_TP_METER, bat_priv, - "Meter: batadv_tp_start cannot allocate list elements\n"); + "Meter: %s cannot allocate list elements\n", + __func__); batadv_tp_batctl_error_notify(BATADV_TP_REASON_MEMORY_ERROR, dst, bat_priv, session_cookie); return; -- cgit From 13b0ea0f5934ac30bf75c21554d3061b02abbad4 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 17 Mar 2017 12:08:59 +0100 Subject: batman-adv: Omit unnecessary memset of netdev private data The memory for netdev_priv is allocated using kzalloc in alloc_netdev (or alloc_netdev_mq respectively) so there is no need to set it to 0 again. Signed-off-by: Tobias Klauser Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/soft-interface.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 5d099b2e6cfc..8226495c6664 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -972,8 +972,6 @@ static void batadv_softif_free(struct net_device *dev) */ static void batadv_softif_init_early(struct net_device *dev) { - struct batadv_priv *priv = netdev_priv(dev); - ether_setup(dev); dev->netdev_ops = &batadv_netdev_ops; @@ -990,8 +988,6 @@ static void batadv_softif_init_early(struct net_device *dev) eth_hw_addr_random(dev); dev->ethtool_ops = &batadv_ethtool_ops; - - memset(priv, 0, sizeof(*priv)); } struct net_device *batadv_softif_create(struct net *net, const char *name) -- cgit From 7bd175928280c3e5d741cb9948cffaa61b7cc7c6 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 6 Feb 2017 18:25:26 -0800 Subject: igb: Add support for DMA_ATTR_WEAK_ORDERING Since we are already using DMA attributes in igb for Rx there is no reason why we can't also apply DMA_ATTR_WEAK_ORDERING which is needed on some platforms to improve performance. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 3 +++ drivers/net/ethernet/intel/igb/igb_main.c | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index acbc3abe2ddd..87c9fe9d6f18 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -148,6 +148,9 @@ struct vf_data_storage { /* How many Rx Buffers do we bundle into one write to the hardware ? */ #define IGB_RX_BUFFER_WRITE 16 /* Must be power of 2 */ +#define IGB_RX_DMA_ATTR \ + (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) + #define AUTO_ALL_MODES 0 #define IGB_EEPROM_APME 0x0400 diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index be456bae8169..cf7ee9cdac6f 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3963,7 +3963,7 @@ static void igb_clean_rx_ring(struct igb_ring *rx_ring) buffer_info->dma, PAGE_SIZE, DMA_FROM_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC); + IGB_RX_DMA_ATTR); __page_frag_cache_drain(buffer_info->page, buffer_info->pagecnt_bias); @@ -6990,7 +6990,7 @@ static struct sk_buff *igb_fetch_rx_buffer(struct igb_ring *rx_ring, */ dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, PAGE_SIZE, DMA_FROM_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC); + IGB_RX_DMA_ATTR); __page_frag_cache_drain(page, rx_buffer->pagecnt_bias); } @@ -7250,7 +7250,7 @@ static bool igb_alloc_mapped_page(struct igb_ring *rx_ring, /* map page for use */ dma = dma_map_page_attrs(rx_ring->dev, page, 0, PAGE_SIZE, - DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + DMA_FROM_DEVICE, IGB_RX_DMA_ATTR); /* if mapping failed free memory back to system since * there isn't much point in holding memory we can't use -- cgit From 7ec0116c9131a8cd58dc456ae2bd5bc9976460d1 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 6 Feb 2017 18:25:41 -0800 Subject: igb: Use length to determine if descriptor is done This change makes it so that we use the length of the packet instead of the DD status bit to determine if a new descriptor is ready to be processed. The obvious advantage is that it cuts down on reads as we don't really even need the DD bit if going from a 0 to a non-zero value on size is enough to inform us that the packet has been completed. In addition I have updated the code so that we only reset the Rx descriptor length for descriptor zero when resetting a ring instead of having to do a memset with 0 over the entire ring. By doing this we can save some time on initialization. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_ethtool.c | 2 +- drivers/net/ethernet/intel/igb/igb_main.c | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 737b664d004c..3f5f7744c90f 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -1811,7 +1811,7 @@ static int igb_clean_test_rings(struct igb_ring *rx_ring, tx_ntc = tx_ring->next_to_clean; rx_desc = IGB_RX_DESC(rx_ring, rx_ntc); - while (igb_test_staterr(rx_desc, E1000_RXD_STAT_DD)) { + while (rx_desc->wb.upper.length) { /* check Rx buffer */ rx_buffer_info = &rx_ring->rx_buffer_info[rx_ntc]; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index cf7ee9cdac6f..1d76d3a90a17 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3720,6 +3720,7 @@ void igb_configure_rx_ring(struct igb_adapter *adapter, struct igb_ring *ring) { struct e1000_hw *hw = &adapter->hw; + union e1000_adv_rx_desc *rx_desc; u64 rdba = ring->dma; int reg_idx = ring->reg_idx; u32 srrctl = 0, rxdctl = 0; @@ -3758,6 +3759,10 @@ void igb_configure_rx_ring(struct igb_adapter *adapter, rxdctl |= IGB_RX_HTHRESH << 8; rxdctl |= IGB_RX_WTHRESH << 16; + /* initialize Rx descriptor 0 */ + rx_desc = IGB_RX_DESC(ring, 0); + rx_desc->wb.upper.length = 0; + /* enable receive descriptor fetching */ rxdctl |= E1000_RXDCTL_QUEUE_ENABLE; wr32(E1000_RXDCTL(reg_idx), rxdctl); @@ -3973,9 +3978,6 @@ static void igb_clean_rx_ring(struct igb_ring *rx_ring) size = sizeof(struct igb_rx_buffer) * rx_ring->count; memset(rx_ring->rx_buffer_info, 0, size); - /* Zero out the descriptor ring */ - memset(rx_ring->desc, 0, rx_ring->size); - rx_ring->next_to_alloc = 0; rx_ring->next_to_clean = 0; rx_ring->next_to_use = 0; @@ -7172,7 +7174,7 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) rx_desc = IGB_RX_DESC(rx_ring, rx_ring->next_to_clean); - if (!rx_desc->wb.upper.status_error) + if (!rx_desc->wb.upper.length) break; /* This memory barrier is needed to keep us from reading @@ -7312,8 +7314,8 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) i -= rx_ring->count; } - /* clear the status bits for the next_to_use descriptor */ - rx_desc->wb.upper.status_error = 0; + /* clear the length for the next_to_use descriptor */ + rx_desc->wb.upper.length = 0; cleaned_count--; } while (cleaned_count); -- cgit From d2bead576e67c34fe9ea174bb245254d0fe237b5 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 6 Feb 2017 18:25:50 -0800 Subject: igb: Clear Rx buffer_info in configure instead of clean This change makes it so that instead of going through the entire ring on Rx cleanup we only go through the region that was designated to be cleaned up and stop when we reach the region where new allocations should start. In addition we can avoid having to perform a memset on the Rx buffer_info structures until we are about to start using the ring again. By deferring this we can avoid dirtying the cache any more than we have to which can help to improve the time needed to bring the interface down and then back up again in a reset or suspend/resume cycle. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 1d76d3a90a17..680f0d3d9b72 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3435,7 +3435,7 @@ int igb_setup_rx_resources(struct igb_ring *rx_ring) size = sizeof(struct igb_rx_buffer) * rx_ring->count; - rx_ring->rx_buffer_info = vzalloc(size); + rx_ring->rx_buffer_info = vmalloc(size); if (!rx_ring->rx_buffer_info) goto err; @@ -3759,6 +3759,10 @@ void igb_configure_rx_ring(struct igb_adapter *adapter, rxdctl |= IGB_RX_HTHRESH << 8; rxdctl |= IGB_RX_WTHRESH << 16; + /* initialize rx_buffer_info */ + memset(ring->rx_buffer_info, 0, + sizeof(struct igb_rx_buffer) * ring->count); + /* initialize Rx descriptor 0 */ rx_desc = IGB_RX_DESC(ring, 0); rx_desc->wb.upper.length = 0; @@ -3937,23 +3941,16 @@ static void igb_free_all_rx_resources(struct igb_adapter *adapter) **/ static void igb_clean_rx_ring(struct igb_ring *rx_ring) { - unsigned long size; - u16 i; + u16 i = rx_ring->next_to_clean; if (rx_ring->skb) dev_kfree_skb(rx_ring->skb); rx_ring->skb = NULL; - if (!rx_ring->rx_buffer_info) - return; - /* Free all the Rx ring sk_buffs */ - for (i = 0; i < rx_ring->count; i++) { + while (i != rx_ring->next_to_alloc) { struct igb_rx_buffer *buffer_info = &rx_ring->rx_buffer_info[i]; - if (!buffer_info->page) - continue; - /* Invalidate cache lines that may have been written to by * device so that we avoid corrupting memory. */ @@ -3972,12 +3969,11 @@ static void igb_clean_rx_ring(struct igb_ring *rx_ring) __page_frag_cache_drain(buffer_info->page, buffer_info->pagecnt_bias); - buffer_info->page = NULL; + i++; + if (i == rx_ring->count) + i = 0; } - size = sizeof(struct igb_rx_buffer) * rx_ring->count; - memset(rx_ring->rx_buffer_info, 0, size); - rx_ring->next_to_alloc = 0; rx_ring->next_to_clean = 0; rx_ring->next_to_use = 0; -- cgit From 7cc6fd4c60f267e17b0baef1580d7a6258c0a6f0 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 6 Feb 2017 18:26:02 -0800 Subject: igb: Don't bother clearing Tx buffer_info in igb_clean_tx_ring In the case of the Tx rings we need to only clear the Tx buffer_info when we are resetting the rings. Ideally we do this when we configure the ring to bring it back up instead of when we are taking it down in order to avoid dirtying pages we don't need to. In addition we don't need to clear the Tx descriptor ring since we will fully repopulate it when we begin transmitting frames and next_to_watch can be cleared to prevent the ring from being cleaned beyond that point instead of needing to touch anything in the Tx descriptor ring. Finally with these changes we can avoid having to reset the skb member of the Tx buffer_info structure in the cleanup path since the skb will always be associated with the first buffer which has next_to_watch set. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 1 - drivers/net/ethernet/intel/igb/igb_ethtool.c | 11 ++- drivers/net/ethernet/intel/igb/igb_main.c | 120 ++++++++++++++++----------- 3 files changed, 83 insertions(+), 49 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 87c9fe9d6f18..a638254f4e06 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -594,7 +594,6 @@ void igb_configure_rx_ring(struct igb_adapter *, struct igb_ring *); void igb_setup_tctl(struct igb_adapter *); void igb_setup_rctl(struct igb_adapter *); netdev_tx_t igb_xmit_frame_ring(struct sk_buff *, struct igb_ring *); -void igb_unmap_and_free_tx_resource(struct igb_ring *, struct igb_tx_buffer *); void igb_alloc_rx_buffers(struct igb_ring *, u16); void igb_update_stats(struct igb_adapter *, struct rtnl_link_stats64 *); bool igb_has_link(struct igb_adapter *adapter); diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 3f5f7744c90f..612cf13b7a3a 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -1833,7 +1833,16 @@ static int igb_clean_test_rings(struct igb_ring *rx_ring, /* unmap buffer on Tx side */ tx_buffer_info = &tx_ring->tx_buffer_info[tx_ntc]; - igb_unmap_and_free_tx_resource(tx_ring, tx_buffer_info); + + /* Free all the Tx ring sk_buffs */ + dev_kfree_skb_any(tx_buffer_info->skb); + + /* unmap skb header data */ + dma_unmap_single(tx_ring->dev, + dma_unmap_addr(tx_buffer_info, dma), + dma_unmap_len(tx_buffer_info, len), + DMA_TO_DEVICE); + dma_unmap_len_set(tx_buffer_info, len, 0); /* increment Rx/Tx next to clean counters */ rx_ntc++; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 680f0d3d9b72..e541cccaae1b 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3293,7 +3293,7 @@ int igb_setup_tx_resources(struct igb_ring *tx_ring) size = sizeof(struct igb_tx_buffer) * tx_ring->count; - tx_ring->tx_buffer_info = vzalloc(size); + tx_ring->tx_buffer_info = vmalloc(size); if (!tx_ring->tx_buffer_info) goto err; @@ -3404,6 +3404,10 @@ void igb_configure_tx_ring(struct igb_adapter *adapter, txdctl |= IGB_TX_HTHRESH << 8; txdctl |= IGB_TX_WTHRESH << 16; + /* reinitialize tx_buffer_info */ + memset(ring->tx_buffer_info, 0, + sizeof(struct igb_tx_buffer) * ring->count); + txdctl |= E1000_TXDCTL_QUEUE_ENABLE; wr32(E1000_TXDCTL(reg_idx), txdctl); } @@ -3831,55 +3835,63 @@ static void igb_free_all_tx_resources(struct igb_adapter *adapter) igb_free_tx_resources(adapter->tx_ring[i]); } -void igb_unmap_and_free_tx_resource(struct igb_ring *ring, - struct igb_tx_buffer *tx_buffer) -{ - if (tx_buffer->skb) { - dev_kfree_skb_any(tx_buffer->skb); - if (dma_unmap_len(tx_buffer, len)) - dma_unmap_single(ring->dev, - dma_unmap_addr(tx_buffer, dma), - dma_unmap_len(tx_buffer, len), - DMA_TO_DEVICE); - } else if (dma_unmap_len(tx_buffer, len)) { - dma_unmap_page(ring->dev, - dma_unmap_addr(tx_buffer, dma), - dma_unmap_len(tx_buffer, len), - DMA_TO_DEVICE); - } - tx_buffer->next_to_watch = NULL; - tx_buffer->skb = NULL; - dma_unmap_len_set(tx_buffer, len, 0); - /* buffer_info must be completely set up in the transmit path */ -} - /** * igb_clean_tx_ring - Free Tx Buffers * @tx_ring: ring to be cleaned **/ static void igb_clean_tx_ring(struct igb_ring *tx_ring) { - struct igb_tx_buffer *buffer_info; - unsigned long size; - u16 i; + u16 i = tx_ring->next_to_clean; + struct igb_tx_buffer *tx_buffer = &tx_ring->tx_buffer_info[i]; - if (!tx_ring->tx_buffer_info) - return; - /* Free all the Tx ring sk_buffs */ + while (i != tx_ring->next_to_use) { + union e1000_adv_tx_desc *eop_desc, *tx_desc; - for (i = 0; i < tx_ring->count; i++) { - buffer_info = &tx_ring->tx_buffer_info[i]; - igb_unmap_and_free_tx_resource(tx_ring, buffer_info); - } + /* Free all the Tx ring sk_buffs */ + dev_kfree_skb_any(tx_buffer->skb); - netdev_tx_reset_queue(txring_txq(tx_ring)); + /* unmap skb header data */ + dma_unmap_single(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); - size = sizeof(struct igb_tx_buffer) * tx_ring->count; - memset(tx_ring->tx_buffer_info, 0, size); + /* check for eop_desc to determine the end of the packet */ + eop_desc = tx_buffer->next_to_watch; + tx_desc = IGB_TX_DESC(tx_ring, i); - /* Zero out the descriptor ring */ - memset(tx_ring->desc, 0, tx_ring->size); + /* unmap remaining buffers */ + while (tx_desc != eop_desc) { + tx_buffer++; + tx_desc++; + i++; + if (unlikely(i == tx_ring->count)) { + i = 0; + tx_buffer = tx_ring->tx_buffer_info; + tx_desc = IGB_TX_DESC(tx_ring, 0); + } + /* unmap any remaining paged data */ + if (dma_unmap_len(tx_buffer, len)) + dma_unmap_page(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + } + + /* move us one more past the eop_desc for start of next pkt */ + tx_buffer++; + i++; + if (unlikely(i == tx_ring->count)) { + i = 0; + tx_buffer = tx_ring->tx_buffer_info; + } + } + + /* reset BQL for queue */ + netdev_tx_reset_queue(txring_txq(tx_ring)); + + /* reset next_to_use and next_to_clean */ tx_ring->next_to_use = 0; tx_ring->next_to_clean = 0; } @@ -5254,18 +5266,32 @@ static void igb_tx_map(struct igb_ring *tx_ring, dma_error: dev_err(tx_ring->dev, "TX DMA map failed\n"); + tx_buffer = &tx_ring->tx_buffer_info[i]; /* clear dma mappings for failed tx_buffer_info map */ - for (;;) { + while (tx_buffer != first) { + if (dma_unmap_len(tx_buffer, len)) + dma_unmap_page(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + dma_unmap_len_set(tx_buffer, len, 0); + + if (i--) + i += tx_ring->count; tx_buffer = &tx_ring->tx_buffer_info[i]; - igb_unmap_and_free_tx_resource(tx_ring, tx_buffer); - if (tx_buffer == first) - break; - if (i == 0) - i = tx_ring->count; - i--; } + if (dma_unmap_len(tx_buffer, len)) + dma_unmap_single(tx_ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); + dma_unmap_len_set(tx_buffer, len, 0); + + dev_kfree_skb_any(tx_buffer->skb); + tx_buffer->skb = NULL; + tx_ring->next_to_use = i; } @@ -5337,7 +5363,8 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb, return NETDEV_TX_OK; out_drop: - igb_unmap_and_free_tx_resource(tx_ring, first); + dev_kfree_skb_any(first->skb); + first->skb = NULL; return NETDEV_TX_OK; } @@ -6684,7 +6711,6 @@ static bool igb_clean_tx_irq(struct igb_q_vector *q_vector, int napi_budget) DMA_TO_DEVICE); /* clear tx_buffer data */ - tx_buffer->skb = NULL; dma_unmap_len_set(tx_buffer, len, 0); /* clear last DMA location and unmap remaining buffers */ -- cgit From cfbc871c2174f352542053d25659920d6841ed41 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 6 Feb 2017 18:26:15 -0800 Subject: igb: Limit maximum frame Rx based on MTU In order to support the use of build_skb going forward it will be necessary to place a maximum limit on the amount of data we can receive when jumbo frames is not enabled. In order to do this I am adding a new upper limit for receive based on the size of a 2K buffer minus padding. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 10 +++++++++- drivers/net/ethernet/intel/igb/igb_main.c | 21 +++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index a638254f4e06..a74928cc0e58 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -143,8 +143,17 @@ struct vf_data_storage { #define IGB_RXBUFFER_256 256 #define IGB_RXBUFFER_2048 2048 #define IGB_RX_HDR_LEN IGB_RXBUFFER_256 +#define IGB_TS_HDR_LEN 16 #define IGB_RX_BUFSZ IGB_RXBUFFER_2048 +#define IGB_SKB_PAD (NET_SKB_PAD + NET_IP_ALIGN) +#if (PAGE_SIZE < 8192) +#define IGB_MAX_FRAME_BUILD_SKB \ + (SKB_WITH_OVERHEAD(IGB_RXBUFFER_2048) - IGB_SKB_PAD - IGB_TS_HDR_LEN) +#else +#define IGB_MAX_FRAME_BUILD_SKB (IGB_RXBUFFER_2048 - IGB_TS_HDR_LEN) +#endif + /* How many Rx Buffers do we bundle into one write to the hardware ? */ #define IGB_RX_BUFFER_WRITE 16 /* Must be power of 2 */ @@ -561,7 +570,6 @@ struct igb_adapter { #define IGB_DMCTLX_DCFLUSH_DIS 0x80000000 /* Disable DMA Coal Flush */ #define IGB_82576_TSYNC_SHIFT 19 -#define IGB_TS_HDR_LEN 16 enum e1000_state_t { __IGB_TESTING, __IGB_RESETTING, diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index e541cccaae1b..1bf31224dfac 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -4250,7 +4250,7 @@ static void igb_set_rx_mode(struct net_device *netdev) struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; unsigned int vfn = adapter->vfs_allocated_count; - u32 rctl = 0, vmolr = 0; + u32 rctl = 0, vmolr = 0, rlpml = MAX_JUMBO_FRAME_SIZE; int count; /* Check for Promiscuous and All Multicast modes */ @@ -4308,6 +4308,14 @@ static void igb_set_rx_mode(struct net_device *netdev) E1000_RCTL_VFE); wr32(E1000_RCTL, rctl); +#if (PAGE_SIZE < 8192) + if (!adapter->vfs_allocated_count) { + if (adapter->max_frame_size <= IGB_MAX_FRAME_BUILD_SKB) + rlpml = IGB_MAX_FRAME_BUILD_SKB; + } +#endif + wr32(E1000_RLPML, rlpml); + /* In order to support SR-IOV and eventually VMDq it is necessary to set * the VMOLR to enable the appropriate modes. Without this workaround * we will have issues with VLAN tag stripping not being done for frames @@ -4322,12 +4330,17 @@ static void igb_set_rx_mode(struct net_device *netdev) vmolr |= rd32(E1000_VMOLR(vfn)) & ~(E1000_VMOLR_ROPE | E1000_VMOLR_MPME | E1000_VMOLR_ROMPE); - /* enable Rx jumbo frames, no need for restriction */ + /* enable Rx jumbo frames, restrict as needed to support build_skb */ vmolr &= ~E1000_VMOLR_RLPML_MASK; - vmolr |= MAX_JUMBO_FRAME_SIZE | E1000_VMOLR_LPE; +#if (PAGE_SIZE < 8192) + if (adapter->max_frame_size <= IGB_MAX_FRAME_BUILD_SKB) + vmolr |= IGB_MAX_FRAME_BUILD_SKB; + else +#endif + vmolr |= MAX_JUMBO_FRAME_SIZE; + vmolr |= E1000_VMOLR_LPE; wr32(E1000_VMOLR(vfn), vmolr); - wr32(E1000_RLPML, MAX_JUMBO_FRAME_SIZE); igb_restore_vf_multicasts(adapter); } -- cgit From cb0ef1d1dc8739e307f70493b7eb1184685dbbde Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 6 Feb 2017 18:26:26 -0800 Subject: igb: Only sync size of expected frame in ethtool testing We only need to sync the size of the frame that is read to test. We don't need to sync the entire Rx buffer. This way the testing is more consistent with how we handle things in the receive path. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_ethtool.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 612cf13b7a3a..d5966feb7b96 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -1818,7 +1818,7 @@ static int igb_clean_test_rings(struct igb_ring *rx_ring, /* sync Rx buffer for CPU read */ dma_sync_single_for_cpu(rx_ring->dev, rx_buffer_info->dma, - IGB_RX_BUFSZ, + size, DMA_FROM_DEVICE); /* verify contents of skb */ @@ -1828,7 +1828,7 @@ static int igb_clean_test_rings(struct igb_ring *rx_ring, /* sync Rx buffer for device write */ dma_sync_single_for_device(rx_ring->dev, rx_buffer_info->dma, - IGB_RX_BUFSZ, + size, DMA_FROM_DEVICE); /* unmap buffer on Tx side */ -- cgit From 3456fd53421e7f499395542d6c2e76e0b46ab4d3 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 6 Feb 2017 18:26:40 -0800 Subject: igb: Use page_address offset from page instead of masking virtual address Update the handling of page addresses so that we always refer to them using a void pointer, and try to use the consistent name of va indicating we are working with a virtual address. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 2 +- drivers/net/ethernet/intel/igb/igb_main.c | 11 +++++------ drivers/net/ethernet/intel/igb/igb_ptp.c | 3 +-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index a74928cc0e58..6a88a08c021c 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -614,7 +614,7 @@ void igb_ptp_reset(struct igb_adapter *adapter); void igb_ptp_suspend(struct igb_adapter *adapter); void igb_ptp_rx_hang(struct igb_adapter *adapter); void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector, struct sk_buff *skb); -void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, unsigned char *va, +void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, void *va, struct sk_buff *skb); int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr); int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr); diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 1bf31224dfac..44c590c5d5b7 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -6927,7 +6927,7 @@ static bool igb_add_rx_frag(struct igb_ring *rx_ring, struct sk_buff *skb) { struct page *page = rx_buffer->page; - unsigned char *va = page_address(page) + rx_buffer->page_offset; + void *va = page_address(page) + rx_buffer->page_offset; #if (PAGE_SIZE < 8192) unsigned int truesize = IGB_RX_BUFSZ; #else @@ -6969,7 +6969,7 @@ static bool igb_add_rx_frag(struct igb_ring *rx_ring, add_tail_frag: skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, - (unsigned long)va & ~PAGE_MASK, size, truesize); + va - page_address(page), size, truesize); return igb_can_reuse_rx_page(rx_buffer, page, truesize); } @@ -6994,13 +6994,12 @@ static struct sk_buff *igb_fetch_rx_buffer(struct igb_ring *rx_ring, DMA_FROM_DEVICE); if (likely(!skb)) { - void *page_addr = page_address(page) + - rx_buffer->page_offset; + void *va = page_address(page) + rx_buffer->page_offset; /* prefetch first cache line of first page */ - prefetch(page_addr); + prefetch(va); #if L1_CACHE_BYTES < 128 - prefetch(page_addr + L1_CACHE_BYTES); + prefetch(va + L1_CACHE_BYTES); #endif /* allocate a skb to store the frags */ diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index c4477552ce9e..7a3fd4d74592 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -764,8 +764,7 @@ static void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter) * incoming frame. The value is stored in little endian format starting on * byte 8. **/ -void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, - unsigned char *va, +void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, void *va, struct sk_buff *skb) { __le64 *regval = (__le64 *)va; -- cgit From e08912985b296b33b18a563cc126e3e2f018c2e1 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 6 Feb 2017 18:26:52 -0800 Subject: igb: Add support for ethtool private flag to allow use of legacy Rx Since there are potential drawbacks to the new Rx allocation approach I thought it best to add a "chicken bit" so that we can turn the feature off if in the event that a problem is found. It also provides a means of validating the legacy Rx path in the event that we are forced to fall back. At some point in the future when we are convinced we don't need it anymore we might be able to drop the legacy-rx flag. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 1 + drivers/net/ethernet/intel/igb/igb_ethtool.c | 48 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 6a88a08c021c..bffdfe65a0b6 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -557,6 +557,7 @@ struct igb_adapter { #define IGB_FLAG_HAS_MSIX BIT(13) #define IGB_FLAG_EEE BIT(14) #define IGB_FLAG_VLAN_PROMISC BIT(15) +#define IGB_FLAG_RX_LEGACY BIT(16) /* Media Auto Sense */ #define IGB_MAS_ENABLE_0 0X0001 diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index d5966feb7b96..797b9daba224 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -144,6 +144,13 @@ static const char igb_gstrings_test[][ETH_GSTRING_LEN] = { }; #define IGB_TEST_LEN (sizeof(igb_gstrings_test) / ETH_GSTRING_LEN) +static const char igb_priv_flags_strings[][ETH_GSTRING_LEN] = { +#define IGB_PRIV_FLAGS_LEGACY_RX BIT(0) + "legacy-rx", +}; + +#define IGB_PRIV_FLAGS_STR_LEN ARRAY_SIZE(igb_priv_flags_strings) + static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct igb_adapter *adapter = netdev_priv(netdev); @@ -852,6 +859,8 @@ 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_priv_flags = IGB_PRIV_FLAGS_STR_LEN; } static void igb_get_ringparam(struct net_device *netdev, @@ -2280,6 +2289,8 @@ static int igb_get_sset_count(struct net_device *netdev, int sset) return IGB_STATS_LEN; case ETH_SS_TEST: return IGB_TEST_LEN; + case ETH_SS_PRIV_FLAGS: + return IGB_PRIV_FLAGS_STR_LEN; default: return -ENOTSUPP; } @@ -2385,6 +2396,10 @@ static void igb_get_strings(struct net_device *netdev, u32 stringset, u8 *data) } /* BUG_ON(p - data != IGB_STATS_LEN * ETH_GSTRING_LEN); */ break; + case ETH_SS_PRIV_FLAGS: + memcpy(data, igb_priv_flags_strings, + IGB_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN); + break; } } @@ -3397,6 +3412,37 @@ static int igb_set_channels(struct net_device *netdev, return 0; } +static u32 igb_get_priv_flags(struct net_device *netdev) +{ + struct igb_adapter *adapter = netdev_priv(netdev); + u32 priv_flags = 0; + + if (adapter->flags & IGB_FLAG_RX_LEGACY) + priv_flags |= IGB_PRIV_FLAGS_LEGACY_RX; + + return priv_flags; +} + +static int igb_set_priv_flags(struct net_device *netdev, u32 priv_flags) +{ + struct igb_adapter *adapter = netdev_priv(netdev); + unsigned int flags = adapter->flags; + + flags &= ~IGB_FLAG_RX_LEGACY; + if (priv_flags & IGB_PRIV_FLAGS_LEGACY_RX) + flags |= IGB_FLAG_RX_LEGACY; + + if (flags != adapter->flags) { + adapter->flags = flags; + + /* reset interface to repopulate queues */ + if (netif_running(netdev)) + igb_reinit_locked(adapter); + } + + return 0; +} + static const struct ethtool_ops igb_ethtool_ops = { .get_settings = igb_get_settings, .set_settings = igb_set_settings, @@ -3435,6 +3481,8 @@ static const struct ethtool_ops igb_ethtool_ops = { .set_rxfh = igb_set_rxfh, .get_channels = igb_get_channels, .set_channels = igb_set_channels, + .get_priv_flags = igb_get_priv_flags, + .set_priv_flags = igb_set_priv_flags, .begin = igb_ethtool_begin, .complete = igb_ethtool_complete, }; -- cgit From 8649aaef4044681257ed38cf8706aea88430f2c4 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 6 Feb 2017 18:27:03 -0800 Subject: igb: Add support for using order 1 pages to receive large frames This patch adds support for using 3K buffers in order 1 pages the same way we were using 2K buffers in 4K pages. We are reserving 1K of room for now to have space available for future headroom and tailroom when we enable build_skb support. One side effect of this patch is that we can end up using a larger buffer if jumbo frames is enabled. The impact shouldn't be too great, but it could hurt small packet performance for UDP workloads if jumbo frames is enabled as the truesize of frames will be larger. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 30 ++++++++++++++- drivers/net/ethernet/intel/igb/igb_main.c | 64 +++++++++++++++++++++++-------- 2 files changed, 76 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index bffdfe65a0b6..eb91c87e0c1d 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -142,9 +142,9 @@ struct vf_data_storage { /* Supported Rx Buffer Sizes */ #define IGB_RXBUFFER_256 256 #define IGB_RXBUFFER_2048 2048 +#define IGB_RXBUFFER_3072 3072 #define IGB_RX_HDR_LEN IGB_RXBUFFER_256 #define IGB_TS_HDR_LEN 16 -#define IGB_RX_BUFSZ IGB_RXBUFFER_2048 #define IGB_SKB_PAD (NET_SKB_PAD + NET_IP_ALIGN) #if (PAGE_SIZE < 8192) @@ -313,12 +313,40 @@ struct igb_q_vector { }; enum e1000_ring_flags_t { + IGB_RING_FLAG_RX_3K_BUFFER, IGB_RING_FLAG_RX_SCTP_CSUM, IGB_RING_FLAG_RX_LB_VLAN_BSWAP, IGB_RING_FLAG_TX_CTX_IDX, IGB_RING_FLAG_TX_DETECT_HANG }; +#define ring_uses_large_buffer(ring) \ + test_bit(IGB_RING_FLAG_RX_3K_BUFFER, &(ring)->flags) +#define set_ring_uses_large_buffer(ring) \ + set_bit(IGB_RING_FLAG_RX_3K_BUFFER, &(ring)->flags) +#define clear_ring_uses_large_buffer(ring) \ + clear_bit(IGB_RING_FLAG_RX_3K_BUFFER, &(ring)->flags) + +static inline unsigned int igb_rx_bufsz(struct igb_ring *ring) +{ +#if (PAGE_SIZE < 8192) + if (ring_uses_large_buffer(ring)) + return IGB_RXBUFFER_3072; +#endif + return IGB_RXBUFFER_2048; +} + +static inline unsigned int igb_rx_pg_order(struct igb_ring *ring) +{ +#if (PAGE_SIZE < 8192) + if (ring_uses_large_buffer(ring)) + return 1; +#endif + return 0; +} + +#define igb_rx_pg_size(_ring) (PAGE_SIZE << igb_rx_pg_order(_ring)) + #define IGB_TXD_DCMD (E1000_ADVTXD_DCMD_EOP | E1000_ADVTXD_DCMD_RS) #define IGB_RX_DESC(R, i) \ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 44c590c5d5b7..24c20d401240 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -554,7 +554,7 @@ rx_ring_summary: 16, 1, page_address(buffer_info->page) + buffer_info->page_offset, - IGB_RX_BUFSZ, true); + igb_rx_bufsz(rx_ring), true); } } } @@ -3746,7 +3746,10 @@ void igb_configure_rx_ring(struct igb_adapter *adapter, /* set descriptor configuration */ srrctl = IGB_RX_HDR_LEN << E1000_SRRCTL_BSIZEHDRSIZE_SHIFT; - srrctl |= IGB_RX_BUFSZ >> E1000_SRRCTL_BSIZEPKT_SHIFT; + if (ring_uses_large_buffer(ring)) + srrctl |= IGB_RXBUFFER_3072 >> E1000_SRRCTL_BSIZEPKT_SHIFT; + else + srrctl |= IGB_RXBUFFER_2048 >> E1000_SRRCTL_BSIZEPKT_SHIFT; srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF; if (hw->mac.type >= e1000_82580) srrctl |= E1000_SRRCTL_TIMESTAMP; @@ -3776,6 +3779,23 @@ void igb_configure_rx_ring(struct igb_adapter *adapter, wr32(E1000_RXDCTL(reg_idx), rxdctl); } +static void igb_set_rx_buffer_len(struct igb_adapter *adapter, + struct igb_ring *rx_ring) +{ + /* set build_skb and buffer size flags */ + clear_ring_uses_large_buffer(rx_ring); + + if (adapter->flags & IGB_FLAG_RX_LEGACY) + return; + +#if (PAGE_SIZE < 8192) + if (adapter->max_frame_size <= IGB_MAX_FRAME_BUILD_SKB) + return; + + set_ring_uses_large_buffer(rx_ring); +#endif +} + /** * igb_configure_rx - Configure receive Unit after Reset * @adapter: board private structure @@ -3793,8 +3813,12 @@ static void igb_configure_rx(struct igb_adapter *adapter) /* Setup the HW Rx Head and Tail Descriptor Pointers and * the Base and Length of the Rx Descriptor Ring */ - for (i = 0; i < adapter->num_rx_queues; i++) - igb_configure_rx_ring(adapter, adapter->rx_ring[i]); + for (i = 0; i < adapter->num_rx_queues; i++) { + struct igb_ring *rx_ring = adapter->rx_ring[i]; + + igb_set_rx_buffer_len(adapter, rx_ring); + igb_configure_rx_ring(adapter, rx_ring); + } } /** @@ -3969,13 +3993,13 @@ static void igb_clean_rx_ring(struct igb_ring *rx_ring) dma_sync_single_range_for_cpu(rx_ring->dev, buffer_info->dma, buffer_info->page_offset, - IGB_RX_BUFSZ, + igb_rx_bufsz(rx_ring), DMA_FROM_DEVICE); /* free resources associated with mapping */ dma_unmap_page_attrs(rx_ring->dev, buffer_info->dma, - PAGE_SIZE, + igb_rx_pg_size(rx_ring), DMA_FROM_DEVICE, IGB_RX_DMA_ATTR); __page_frag_cache_drain(buffer_info->page, @@ -6870,7 +6894,7 @@ static inline bool igb_page_is_reserved(struct page *page) static bool igb_can_reuse_rx_page(struct igb_rx_buffer *rx_buffer, struct page *page, - unsigned int truesize) + const unsigned int truesize) { unsigned int pagecnt_bias = rx_buffer->pagecnt_bias--; @@ -6884,12 +6908,14 @@ static bool igb_can_reuse_rx_page(struct igb_rx_buffer *rx_buffer, return false; /* flip page offset to other buffer */ - rx_buffer->page_offset ^= IGB_RX_BUFSZ; + rx_buffer->page_offset ^= truesize; #else /* move offset up to the next cache line */ rx_buffer->page_offset += truesize; +#define IGB_LAST_OFFSET \ + (SKB_WITH_OVERHEAD(PAGE_SIZE) - IGB_RXBUFFER_2048) - if (rx_buffer->page_offset > (PAGE_SIZE - IGB_RX_BUFSZ)) + if (rx_buffer->page_offset > IGB_LAST_OFFSET) return false; #endif @@ -6929,7 +6955,7 @@ static bool igb_add_rx_frag(struct igb_ring *rx_ring, struct page *page = rx_buffer->page; void *va = page_address(page) + rx_buffer->page_offset; #if (PAGE_SIZE < 8192) - unsigned int truesize = IGB_RX_BUFSZ; + unsigned int truesize = igb_rx_pg_size(rx_ring) / 2; #else unsigned int truesize = SKB_DATA_ALIGN(size); #endif @@ -7025,7 +7051,7 @@ static struct sk_buff *igb_fetch_rx_buffer(struct igb_ring *rx_ring, * any references we are holding to it */ dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, - PAGE_SIZE, DMA_FROM_DEVICE, + igb_rx_pg_size(rx_ring), DMA_FROM_DEVICE, IGB_RX_DMA_ATTR); __page_frag_cache_drain(page, rx_buffer->pagecnt_bias); } @@ -7278,21 +7304,23 @@ static bool igb_alloc_mapped_page(struct igb_ring *rx_ring, return true; /* alloc new page for storage */ - page = dev_alloc_page(); + page = dev_alloc_pages(igb_rx_pg_order(rx_ring)); if (unlikely(!page)) { rx_ring->rx_stats.alloc_failed++; return false; } /* map page for use */ - dma = dma_map_page_attrs(rx_ring->dev, page, 0, PAGE_SIZE, - DMA_FROM_DEVICE, IGB_RX_DMA_ATTR); + dma = dma_map_page_attrs(rx_ring->dev, page, 0, + igb_rx_pg_size(rx_ring), + DMA_FROM_DEVICE, + IGB_RX_DMA_ATTR); /* if mapping failed free memory back to system since * there isn't much point in holding memory we can't use */ if (dma_mapping_error(rx_ring->dev, dma)) { - __free_page(page); + __free_pages(page, igb_rx_pg_order(rx_ring)); rx_ring->rx_stats.alloc_failed++; return false; @@ -7315,6 +7343,7 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) union e1000_adv_rx_desc *rx_desc; struct igb_rx_buffer *bi; u16 i = rx_ring->next_to_use; + u16 bufsz; /* nothing to do */ if (!cleaned_count) @@ -7324,14 +7353,15 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) bi = &rx_ring->rx_buffer_info[i]; i -= rx_ring->count; + bufsz = igb_rx_bufsz(rx_ring); + do { if (!igb_alloc_mapped_page(rx_ring, bi)) break; /* sync the buffer for use by the device */ dma_sync_single_range_for_device(rx_ring->dev, bi->dma, - bi->page_offset, - IGB_RX_BUFSZ, + bi->page_offset, bufsz, DMA_FROM_DEVICE); /* Refresh the desc even if buffer_addrs didn't change -- cgit From e3cdf68d4a861d91ef62ed615483e673f07fccfe Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 6 Feb 2017 18:27:14 -0800 Subject: igb: Add support for padding packet With the size of the frame limited we can now write to an offset within the buffer instead of having to write at the very start of the buffer. The advantage to this is that it allows us to leave padding room for things like supporting XDP in the future. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 11 +++++++++++ drivers/net/ethernet/intel/igb/igb_main.c | 14 ++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index eb91c87e0c1d..dc6e2980718f 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -314,6 +314,7 @@ struct igb_q_vector { enum e1000_ring_flags_t { IGB_RING_FLAG_RX_3K_BUFFER, + IGB_RING_FLAG_RX_BUILD_SKB_ENABLED, IGB_RING_FLAG_RX_SCTP_CSUM, IGB_RING_FLAG_RX_LB_VLAN_BSWAP, IGB_RING_FLAG_TX_CTX_IDX, @@ -327,11 +328,21 @@ enum e1000_ring_flags_t { #define clear_ring_uses_large_buffer(ring) \ clear_bit(IGB_RING_FLAG_RX_3K_BUFFER, &(ring)->flags) +#define ring_uses_build_skb(ring) \ + test_bit(IGB_RING_FLAG_RX_BUILD_SKB_ENABLED, &(ring)->flags) +#define set_ring_build_skb_enabled(ring) \ + set_bit(IGB_RING_FLAG_RX_BUILD_SKB_ENABLED, &(ring)->flags) +#define clear_ring_build_skb_enabled(ring) \ + clear_bit(IGB_RING_FLAG_RX_BUILD_SKB_ENABLED, &(ring)->flags) + static inline unsigned int igb_rx_bufsz(struct igb_ring *ring) { #if (PAGE_SIZE < 8192) if (ring_uses_large_buffer(ring)) return IGB_RXBUFFER_3072; + + if (ring_uses_build_skb(ring)) + return IGB_MAX_FRAME_BUILD_SKB + IGB_TS_HDR_LEN; #endif return IGB_RXBUFFER_2048; } diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 24c20d401240..3ef66577872b 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3783,11 +3783,14 @@ static void igb_set_rx_buffer_len(struct igb_adapter *adapter, struct igb_ring *rx_ring) { /* set build_skb and buffer size flags */ + clear_ring_build_skb_enabled(rx_ring); clear_ring_uses_large_buffer(rx_ring); if (adapter->flags & IGB_FLAG_RX_LEGACY) return; + set_ring_build_skb_enabled(rx_ring); + #if (PAGE_SIZE < 8192) if (adapter->max_frame_size <= IGB_MAX_FRAME_BUILD_SKB) return; @@ -6957,7 +6960,9 @@ static bool igb_add_rx_frag(struct igb_ring *rx_ring, #if (PAGE_SIZE < 8192) unsigned int truesize = igb_rx_pg_size(rx_ring) / 2; #else - unsigned int truesize = SKB_DATA_ALIGN(size); + unsigned int truesize = ring_uses_build_skb(rx_ring) ? + SKB_DATA_ALIGN(IGB_SKB_PAD + size) : + SKB_DATA_ALIGN(size); #endif unsigned int pull_len; @@ -7293,6 +7298,11 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) return total_packets; } +static inline unsigned int igb_rx_offset(struct igb_ring *rx_ring) +{ + return ring_uses_build_skb(rx_ring) ? IGB_SKB_PAD : 0; +} + static bool igb_alloc_mapped_page(struct igb_ring *rx_ring, struct igb_rx_buffer *bi) { @@ -7328,7 +7338,7 @@ static bool igb_alloc_mapped_page(struct igb_ring *rx_ring, bi->dma = dma; bi->page = page; - bi->page_offset = 0; + bi->page_offset = igb_rx_offset(rx_ring); bi->pagecnt_bias = 1; return true; -- cgit From e014272672b964471608a2624e4cdf1d5e7c22ea Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 6 Feb 2017 18:27:26 -0800 Subject: igb: Break out Rx buffer page management At this point we have 2 to 3 paths that can be taken depending on what Rx modes are enabled. In order to better support that and improve the maintainability I am breaking out the common bits from those paths and making them into their own functions. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 235 +++++++++++++++--------------- 1 file changed, 121 insertions(+), 114 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 3ef66577872b..dfae641647d3 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -6886,8 +6886,14 @@ static void igb_reuse_rx_page(struct igb_ring *rx_ring, nta++; rx_ring->next_to_alloc = (nta < rx_ring->count) ? nta : 0; - /* transfer page from old buffer to new buffer */ - *new_buff = *old_buff; + /* Transfer page from old buffer to new buffer. + * Move each member individually to avoid possible store + * forwarding stalls. + */ + new_buff->dma = old_buff->dma; + new_buff->page = old_buff->page; + new_buff->page_offset = old_buff->page_offset; + new_buff->pagecnt_bias = old_buff->pagecnt_bias; } static inline bool igb_page_is_reserved(struct page *page) @@ -6895,11 +6901,10 @@ static inline bool igb_page_is_reserved(struct page *page) return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page); } -static bool igb_can_reuse_rx_page(struct igb_rx_buffer *rx_buffer, - struct page *page, - const unsigned int truesize) +static bool igb_can_reuse_rx_page(struct igb_rx_buffer *rx_buffer) { - unsigned int pagecnt_bias = rx_buffer->pagecnt_bias--; + unsigned int pagecnt_bias = rx_buffer->pagecnt_bias; + struct page *page = rx_buffer->page; /* avoid re-using remote pages */ if (unlikely(igb_page_is_reserved(page))) @@ -6907,14 +6912,9 @@ static bool igb_can_reuse_rx_page(struct igb_rx_buffer *rx_buffer, #if (PAGE_SIZE < 8192) /* if we are only owner of page we can reuse it */ - if (unlikely(page_ref_count(page) != pagecnt_bias)) + if (unlikely((page_ref_count(page) - pagecnt_bias) > 1)) return false; - - /* flip page offset to other buffer */ - rx_buffer->page_offset ^= truesize; #else - /* move offset up to the next cache line */ - rx_buffer->page_offset += truesize; #define IGB_LAST_OFFSET \ (SKB_WITH_OVERHEAD(PAGE_SIZE) - IGB_RXBUFFER_2048) @@ -6926,7 +6926,7 @@ static bool igb_can_reuse_rx_page(struct igb_rx_buffer *rx_buffer, * the pagecnt_bias and page count so that we fully restock the * number of references the driver holds. */ - if (unlikely(pagecnt_bias == 1)) { + if (unlikely(!pagecnt_bias)) { page_ref_add(page, USHRT_MAX); rx_buffer->pagecnt_bias = USHRT_MAX; } @@ -6938,25 +6938,16 @@ static bool igb_can_reuse_rx_page(struct igb_rx_buffer *rx_buffer, * igb_add_rx_frag - Add contents of Rx buffer to sk_buff * @rx_ring: rx descriptor ring to transact packets on * @rx_buffer: buffer containing page to add - * @rx_desc: descriptor containing length of buffer written by hardware * @skb: sk_buff to place the data into + * @size: size of buffer to be added * * This function will add the data contained in rx_buffer->page to the skb. - * This is done either through a direct copy if the data in the buffer is - * less than the skb header size, otherwise it will just attach the page as - * a frag to the skb. - * - * The function will then update the page offset if necessary and return - * true if the buffer can be reused by the adapter. **/ -static bool igb_add_rx_frag(struct igb_ring *rx_ring, +static void igb_add_rx_frag(struct igb_ring *rx_ring, struct igb_rx_buffer *rx_buffer, - unsigned int size, - union e1000_adv_rx_desc *rx_desc, - struct sk_buff *skb) + struct sk_buff *skb, + unsigned int size) { - struct page *page = rx_buffer->page; - void *va = page_address(page) + rx_buffer->page_offset; #if (PAGE_SIZE < 8192) unsigned int truesize = igb_rx_pg_size(rx_ring) / 2; #else @@ -6964,10 +6955,39 @@ static bool igb_add_rx_frag(struct igb_ring *rx_ring, SKB_DATA_ALIGN(IGB_SKB_PAD + size) : SKB_DATA_ALIGN(size); #endif - unsigned int pull_len; + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, + rx_buffer->page_offset, size, truesize); +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif +} + +static struct sk_buff *igb_construct_skb(struct igb_ring *rx_ring, + struct igb_rx_buffer *rx_buffer, + union e1000_adv_rx_desc *rx_desc, + unsigned int size) +{ + void *va = page_address(rx_buffer->page) + rx_buffer->page_offset; +#if (PAGE_SIZE < 8192) + unsigned int truesize = igb_rx_pg_size(rx_ring) / 2; +#else + unsigned int truesize = SKB_DATA_ALIGN(size); +#endif + unsigned int headlen; + struct sk_buff *skb; - if (unlikely(skb_is_nonlinear(skb))) - goto add_tail_frag; + /* prefetch first cache line of first page */ + prefetch(va); +#if L1_CACHE_BYTES < 128 + prefetch(va + L1_CACHE_BYTES); +#endif + + /* allocate a skb to store the frags */ + skb = napi_alloc_skb(&rx_ring->q_vector->napi, IGB_RX_HDR_LEN); + if (unlikely(!skb)) + return NULL; if (unlikely(igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP))) { igb_ptp_rx_pktstamp(rx_ring->q_vector, va, skb); @@ -6975,95 +6995,29 @@ static bool igb_add_rx_frag(struct igb_ring *rx_ring, size -= IGB_TS_HDR_LEN; } - if (likely(size <= IGB_RX_HDR_LEN)) { - memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long))); - - /* page is not reserved, we can reuse buffer as-is */ - if (likely(!igb_page_is_reserved(page))) - return true; - - /* this page cannot be reused so discard it */ - return false; - } - - /* we need the header to contain the greater of either ETH_HLEN or - * 60 bytes if the skb->len is less than 60 for skb_pad. - */ - pull_len = eth_get_headlen(va, IGB_RX_HDR_LEN); + /* Determine available headroom for copy */ + headlen = size; + if (headlen > IGB_RX_HDR_LEN) + headlen = eth_get_headlen(va, IGB_RX_HDR_LEN); /* align pull length to size of long to optimize memcpy performance */ - memcpy(__skb_put(skb, pull_len), va, ALIGN(pull_len, sizeof(long))); + memcpy(__skb_put(skb, headlen), va, ALIGN(headlen, sizeof(long))); /* update all of the pointers */ - va += pull_len; - size -= pull_len; - -add_tail_frag: - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, - va - page_address(page), size, truesize); - - return igb_can_reuse_rx_page(rx_buffer, page, truesize); -} - -static struct sk_buff *igb_fetch_rx_buffer(struct igb_ring *rx_ring, - union e1000_adv_rx_desc *rx_desc, - struct sk_buff *skb) -{ - unsigned int size = le16_to_cpu(rx_desc->wb.upper.length); - struct igb_rx_buffer *rx_buffer; - struct page *page; - - rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean]; - page = rx_buffer->page; - prefetchw(page); - - /* we are reusing so sync this buffer for CPU use */ - dma_sync_single_range_for_cpu(rx_ring->dev, - rx_buffer->dma, - rx_buffer->page_offset, - size, - DMA_FROM_DEVICE); - - if (likely(!skb)) { - void *va = page_address(page) + rx_buffer->page_offset; - - /* prefetch first cache line of first page */ - prefetch(va); -#if L1_CACHE_BYTES < 128 - prefetch(va + L1_CACHE_BYTES); + size -= headlen; + if (size) { + skb_add_rx_frag(skb, 0, rx_buffer->page, + (va + headlen) - page_address(rx_buffer->page), + size, truesize); +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; #endif - - /* allocate a skb to store the frags */ - skb = napi_alloc_skb(&rx_ring->q_vector->napi, IGB_RX_HDR_LEN); - if (unlikely(!skb)) { - rx_ring->rx_stats.alloc_failed++; - return NULL; - } - - /* we will be copying header into skb->data in - * pskb_may_pull so it is in our interest to prefetch - * it now to avoid a possible cache miss - */ - prefetchw(skb->data); - } - - /* pull page into skb */ - if (igb_add_rx_frag(rx_ring, rx_buffer, size, rx_desc, skb)) { - /* hand second half of page back to the ring */ - igb_reuse_rx_page(rx_ring, rx_buffer); } else { - /* We are not reusing the buffer so unmap it and free - * any references we are holding to it - */ - dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, - igb_rx_pg_size(rx_ring), DMA_FROM_DEVICE, - IGB_RX_DMA_ATTR); - __page_frag_cache_drain(page, rx_buffer->pagecnt_bias); + rx_buffer->pagecnt_bias++; } - /* clear contents of rx_buffer */ - rx_buffer->page = NULL; - return skb; } @@ -7221,6 +7175,47 @@ static void igb_process_skb_fields(struct igb_ring *rx_ring, skb->protocol = eth_type_trans(skb, rx_ring->netdev); } +static struct igb_rx_buffer *igb_get_rx_buffer(struct igb_ring *rx_ring, + const unsigned int size) +{ + struct igb_rx_buffer *rx_buffer; + + rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean]; + prefetchw(rx_buffer->page); + + /* we are reusing so sync this buffer for CPU use */ + dma_sync_single_range_for_cpu(rx_ring->dev, + rx_buffer->dma, + rx_buffer->page_offset, + size, + DMA_FROM_DEVICE); + + rx_buffer->pagecnt_bias--; + + return rx_buffer; +} + +static void igb_put_rx_buffer(struct igb_ring *rx_ring, + struct igb_rx_buffer *rx_buffer) +{ + if (igb_can_reuse_rx_page(rx_buffer)) { + /* hand second half of page back to the ring */ + igb_reuse_rx_page(rx_ring, rx_buffer); + } else { + /* We are not reusing the buffer so unmap it and free + * any references we are holding to it + */ + dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, + igb_rx_pg_size(rx_ring), DMA_FROM_DEVICE, + IGB_RX_DMA_ATTR); + __page_frag_cache_drain(rx_buffer->page, + rx_buffer->pagecnt_bias); + } + + /* clear contents of rx_buffer */ + rx_buffer->page = NULL; +} + static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) { struct igb_ring *rx_ring = q_vector->rx.ring; @@ -7230,6 +7225,8 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) while (likely(total_packets < budget)) { union e1000_adv_rx_desc *rx_desc; + struct igb_rx_buffer *rx_buffer; + unsigned int size; /* return some buffers to hardware, one at a time is too slow */ if (cleaned_count >= IGB_RX_BUFFER_WRITE) { @@ -7238,8 +7235,8 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) } rx_desc = IGB_RX_DESC(rx_ring, rx_ring->next_to_clean); - - if (!rx_desc->wb.upper.length) + size = le16_to_cpu(rx_desc->wb.upper.length); + if (!size) break; /* This memory barrier is needed to keep us from reading @@ -7248,13 +7245,23 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) */ dma_rmb(); + rx_buffer = igb_get_rx_buffer(rx_ring, size); + /* retrieve a buffer from the ring */ - skb = igb_fetch_rx_buffer(rx_ring, rx_desc, skb); + if (skb) + igb_add_rx_frag(rx_ring, rx_buffer, skb, size); + else + skb = igb_construct_skb(rx_ring, rx_buffer, + rx_desc, size); /* exit if we failed to retrieve a buffer */ - if (!skb) + if (!skb) { + rx_ring->rx_stats.alloc_failed++; + rx_buffer->pagecnt_bias++; break; + } + igb_put_rx_buffer(rx_ring, rx_buffer); cleaned_count++; /* fetch next buffer in frame if non-eop */ -- cgit From b1bb2eb0a0deb03e9847d8e29eb1e75ce141e4d9 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 6 Feb 2017 18:27:36 -0800 Subject: igb: Re-add support for build_skb in igb This reverts commit f9d40f6a9921 ("igb: Revert support for build_skb in igb") and adds a few changes to update it to work with the latest version of igb. We are now able to revert the removal of this due to the fact that with the recent changes to the page count and the use of DMA_ATTR_SKIP_CPU_SYNC we can make the pages writable so we should not be invalidating the additional data added when we call build_skb. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 47 +++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index dfae641647d3..79f39a785dca 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -7021,6 +7021,51 @@ static struct sk_buff *igb_construct_skb(struct igb_ring *rx_ring, return skb; } +static struct sk_buff *igb_build_skb(struct igb_ring *rx_ring, + struct igb_rx_buffer *rx_buffer, + union e1000_adv_rx_desc *rx_desc, + unsigned int size) +{ + void *va = page_address(rx_buffer->page) + rx_buffer->page_offset; +#if (PAGE_SIZE < 8192) + unsigned int truesize = igb_rx_pg_size(rx_ring) / 2; +#else + unsigned int truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + SKB_DATA_ALIGN(IGB_SKB_PAD + size); +#endif + struct sk_buff *skb; + + /* prefetch first cache line of first page */ + prefetch(va); +#if L1_CACHE_BYTES < 128 + prefetch(va + L1_CACHE_BYTES); +#endif + + /* build an skb to around the page buffer */ + skb = build_skb(va - IGB_SKB_PAD, truesize); + if (unlikely(!skb)) + return NULL; + + /* update pointers within the skb to store the data */ + skb_reserve(skb, IGB_SKB_PAD); + __skb_put(skb, size); + + /* pull timestamp out of packet data */ + if (igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP)) { + igb_ptp_rx_pktstamp(rx_ring->q_vector, skb->data, skb); + __skb_pull(skb, IGB_TS_HDR_LEN); + } + + /* update buffer offset */ +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif + + return skb; +} + static inline void igb_rx_checksum(struct igb_ring *ring, union e1000_adv_rx_desc *rx_desc, struct sk_buff *skb) @@ -7250,6 +7295,8 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) /* retrieve a buffer from the ring */ if (skb) igb_add_rx_frag(rx_ring, rx_buffer, skb, size); + else if (ring_uses_build_skb(rx_ring)) + skb = igb_build_skb(rx_ring, rx_buffer, rx_desc, size); else skb = igb_construct_skb(rx_ring, rx_buffer, rx_desc, size); -- cgit From 3a1eb6d10c9350fa7adce850a752693460ac62d6 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 15 Feb 2017 09:15:59 -0800 Subject: igb/ixgbe: Fix typo in igb_build_skb and/or ixgbe_build_skb code comment There was a typo that I had left in the code comments for the igb and ixgbe functions that enabled build_skb support. Signed-off-by: Alexander Duyck Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 79f39a785dca..26a821fcd220 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -7041,7 +7041,7 @@ static struct sk_buff *igb_build_skb(struct igb_ring *rx_ring, prefetch(va + L1_CACHE_BYTES); #endif - /* build an skb to around the page buffer */ + /* build an skb around the page buffer */ skb = build_skb(va - IGB_SKB_PAD, truesize); if (unlikely(!skb)) return NULL; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index d45477db0227..852a2e7e25ed 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2122,7 +2122,7 @@ static struct sk_buff *ixgbe_build_skb(struct ixgbe_ring *rx_ring, prefetch(va + L1_CACHE_BYTES); #endif - /* build an skb to around the page buffer */ + /* build an skb around the page buffer */ skb = build_skb(va - IXGBE_SKB_PAD, truesize); if (unlikely(!skb)) return NULL; -- cgit From a7f909405bf9bd3a737a39d3a433a4eae4d7eb21 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 4 Feb 2017 22:05:06 +0100 Subject: i40e: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 264 ++++++++++++++----------- 1 file changed, 153 insertions(+), 111 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index a933c6c2aff8..ceb57ad59e8f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -387,7 +387,7 @@ static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, u32 *supported, * **/ static void i40e_get_settings_link_up(struct i40e_hw *hw, - struct ethtool_cmd *ecmd, + struct ethtool_link_ksettings *cmd, struct net_device *netdev, struct i40e_pf *pf) { @@ -395,90 +395,96 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, u32 link_speed = hw_link_info->link_speed; u32 e_advertising = 0x0; u32 e_supported = 0x0; + u32 supported, advertising; + + ethtool_convert_link_mode_to_legacy_u32(&supported, + cmd->link_modes.supported); + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); /* Initialize supported and advertised settings based on phy settings */ switch (hw_link_info->phy_type) { case I40E_PHY_TYPE_40GBASE_CR4: case I40E_PHY_TYPE_40GBASE_CR4_CU: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_40000baseCR4_Full; - ecmd->advertising = ADVERTISED_Autoneg | - ADVERTISED_40000baseCR4_Full; + supported = SUPPORTED_Autoneg | + SUPPORTED_40000baseCR4_Full; + advertising = ADVERTISED_Autoneg | + ADVERTISED_40000baseCR4_Full; break; case I40E_PHY_TYPE_XLAUI: case I40E_PHY_TYPE_XLPPI: case I40E_PHY_TYPE_40GBASE_AOC: - ecmd->supported = SUPPORTED_40000baseCR4_Full; + supported = SUPPORTED_40000baseCR4_Full; break; case I40E_PHY_TYPE_40GBASE_SR4: - ecmd->supported = SUPPORTED_40000baseSR4_Full; + supported = SUPPORTED_40000baseSR4_Full; break; case I40E_PHY_TYPE_40GBASE_LR4: - ecmd->supported = SUPPORTED_40000baseLR4_Full; + supported = SUPPORTED_40000baseLR4_Full; break; case I40E_PHY_TYPE_10GBASE_SR: case I40E_PHY_TYPE_10GBASE_LR: case I40E_PHY_TYPE_1000BASE_SX: case I40E_PHY_TYPE_1000BASE_LX: - ecmd->supported = SUPPORTED_10000baseT_Full; + 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; + supported |= SUPPORTED_1000baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - ecmd->advertising |= ADVERTISED_1000baseT_Full; + advertising |= ADVERTISED_1000baseT_Full; } if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - ecmd->advertising |= ADVERTISED_10000baseT_Full; + advertising |= ADVERTISED_10000baseT_Full; 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; - ecmd->advertising = ADVERTISED_Autoneg; + supported = SUPPORTED_Autoneg | + SUPPORTED_10000baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_100baseT_Full; + advertising = ADVERTISED_Autoneg; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - ecmd->advertising |= ADVERTISED_10000baseT_Full; + advertising |= ADVERTISED_10000baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - ecmd->advertising |= ADVERTISED_1000baseT_Full; + advertising |= ADVERTISED_1000baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) - ecmd->advertising |= ADVERTISED_100baseT_Full; + advertising |= ADVERTISED_100baseT_Full; break; case I40E_PHY_TYPE_1000BASE_T_OPTICAL: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_1000baseT_Full; - ecmd->advertising = ADVERTISED_Autoneg | - ADVERTISED_1000baseT_Full; + supported = SUPPORTED_Autoneg | + SUPPORTED_1000baseT_Full; + advertising = ADVERTISED_Autoneg | + ADVERTISED_1000baseT_Full; break; case I40E_PHY_TYPE_10GBASE_CR1_CU: case I40E_PHY_TYPE_10GBASE_CR1: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_10000baseT_Full; - ecmd->advertising = ADVERTISED_Autoneg | - ADVERTISED_10000baseT_Full; + supported = SUPPORTED_Autoneg | + SUPPORTED_10000baseT_Full; + advertising = ADVERTISED_Autoneg | + ADVERTISED_10000baseT_Full; break; case I40E_PHY_TYPE_XAUI: case I40E_PHY_TYPE_XFI: case I40E_PHY_TYPE_SFI: case I40E_PHY_TYPE_10GBASE_SFPP_CU: case I40E_PHY_TYPE_10GBASE_AOC: - ecmd->supported = SUPPORTED_10000baseT_Full; - ecmd->advertising = SUPPORTED_10000baseT_Full; + supported = SUPPORTED_10000baseT_Full; + advertising = SUPPORTED_10000baseT_Full; break; case I40E_PHY_TYPE_SGMII: - ecmd->supported = SUPPORTED_Autoneg | - SUPPORTED_1000baseT_Full; + supported = SUPPORTED_Autoneg | + SUPPORTED_1000baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - ecmd->advertising |= ADVERTISED_1000baseT_Full; + advertising |= ADVERTISED_1000baseT_Full; if (pf->flags & I40E_FLAG_100M_SGMII_CAPABLE) { - ecmd->supported |= SUPPORTED_100baseT_Full; + supported |= SUPPORTED_100baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) - ecmd->advertising |= ADVERTISED_100baseT_Full; + advertising |= ADVERTISED_100baseT_Full; } break; case I40E_PHY_TYPE_40GBASE_KR4: @@ -486,25 +492,25 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, case I40E_PHY_TYPE_10GBASE_KR: case I40E_PHY_TYPE_10GBASE_KX4: case I40E_PHY_TYPE_1000BASE_KX: - ecmd->supported |= SUPPORTED_40000baseKR4_Full | - SUPPORTED_20000baseKR2_Full | - SUPPORTED_10000baseKR_Full | - SUPPORTED_10000baseKX4_Full | - SUPPORTED_1000baseKX_Full | - SUPPORTED_Autoneg; - ecmd->advertising |= ADVERTISED_40000baseKR4_Full | - ADVERTISED_20000baseKR2_Full | - ADVERTISED_10000baseKR_Full | - ADVERTISED_10000baseKX4_Full | - ADVERTISED_1000baseKX_Full | - ADVERTISED_Autoneg; + supported |= SUPPORTED_40000baseKR4_Full | + SUPPORTED_20000baseKR2_Full | + SUPPORTED_10000baseKR_Full | + SUPPORTED_10000baseKX4_Full | + SUPPORTED_1000baseKX_Full | + SUPPORTED_Autoneg; + advertising |= ADVERTISED_40000baseKR4_Full | + ADVERTISED_20000baseKR2_Full | + ADVERTISED_10000baseKR_Full | + ADVERTISED_10000baseKX4_Full | + ADVERTISED_1000baseKX_Full | + ADVERTISED_Autoneg; break; case I40E_PHY_TYPE_25GBASE_KR: case I40E_PHY_TYPE_25GBASE_CR: case I40E_PHY_TYPE_25GBASE_SR: case I40E_PHY_TYPE_25GBASE_LR: - ecmd->supported = SUPPORTED_Autoneg; - ecmd->advertising = ADVERTISED_Autoneg; + supported = SUPPORTED_Autoneg; + advertising = ADVERTISED_Autoneg; /* TODO: add speeds when ethtool is ready to support*/ break; default: @@ -520,38 +526,43 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, i40e_phy_type_to_ethtool(pf, &e_supported, &e_advertising); - ecmd->supported = ecmd->supported & e_supported; - ecmd->advertising = ecmd->advertising & e_advertising; + supported = supported & e_supported; + advertising = advertising & e_advertising; /* Set speed and duplex */ switch (link_speed) { case I40E_LINK_SPEED_40GB: - ethtool_cmd_speed_set(ecmd, SPEED_40000); + cmd->base.speed = SPEED_40000; break; case I40E_LINK_SPEED_25GB: #ifdef SPEED_25000 - ethtool_cmd_speed_set(ecmd, SPEED_25000); + cmd->base.speed = SPEED_25000; #else netdev_info(netdev, "Speed is 25G, display not supported by this version of ethtool.\n"); #endif break; case I40E_LINK_SPEED_20GB: - ethtool_cmd_speed_set(ecmd, SPEED_20000); + cmd->base.speed = SPEED_20000; break; case I40E_LINK_SPEED_10GB: - ethtool_cmd_speed_set(ecmd, SPEED_10000); + cmd->base.speed = SPEED_10000; break; case I40E_LINK_SPEED_1GB: - ethtool_cmd_speed_set(ecmd, SPEED_1000); + cmd->base.speed = SPEED_1000; break; case I40E_LINK_SPEED_100MB: - ethtool_cmd_speed_set(ecmd, SPEED_100); + cmd->base.speed = SPEED_100; break; default: break; } - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); } /** @@ -562,18 +573,24 @@ 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_link_ksettings *cmd, struct i40e_pf *pf) { + u32 supported, advertising; + /* link is down and the driver needs to fall back on * supported phy types to figure out what info to display */ - i40e_phy_type_to_ethtool(pf, &ecmd->supported, - &ecmd->advertising); + i40e_phy_type_to_ethtool(pf, &supported, &advertising); + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); /* With no link speed and duplex are unknown */ - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; } /** @@ -583,74 +600,85 @@ static void i40e_get_settings_link_down(struct i40e_hw *hw, * * Reports speed/duplex settings based on media_type **/ -static int i40e_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int i40e_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; struct i40e_hw *hw = &pf->hw; struct i40e_link_status *hw_link_info = &hw->phy.link_info; bool link_up = hw_link_info->link_info & I40E_AQ_LINK_UP; + u32 advertising; if (link_up) - i40e_get_settings_link_up(hw, ecmd, netdev, pf); + i40e_get_settings_link_up(hw, cmd, netdev, pf); else - i40e_get_settings_link_down(hw, ecmd, pf); + i40e_get_settings_link_down(hw, cmd, pf); /* Now set the settings that don't rely on link being up/down */ /* Set autoneg settings */ - ecmd->autoneg = ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ? + cmd->base.autoneg = ((hw_link_info->an_info & I40E_AQ_AN_COMPLETED) ? AUTONEG_ENABLE : AUTONEG_DISABLE); switch (hw->phy.media_type) { case I40E_MEDIA_TYPE_BACKPLANE: - ecmd->supported |= SUPPORTED_Autoneg | - SUPPORTED_Backplane; - ecmd->advertising |= ADVERTISED_Autoneg | - ADVERTISED_Backplane; - ecmd->port = PORT_NONE; + ethtool_link_ksettings_add_link_mode(cmd, supported, + Autoneg); + ethtool_link_ksettings_add_link_mode(cmd, supported, + Backplane); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Autoneg); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Backplane); + cmd->base.port = PORT_NONE; break; case I40E_MEDIA_TYPE_BASET: - ecmd->supported |= SUPPORTED_TP; - ecmd->advertising |= ADVERTISED_TP; - ecmd->port = PORT_TP; + ethtool_link_ksettings_add_link_mode(cmd, supported, TP); + ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); + cmd->base.port = PORT_TP; break; case I40E_MEDIA_TYPE_DA: case I40E_MEDIA_TYPE_CX4: - ecmd->supported |= SUPPORTED_FIBRE; - ecmd->advertising |= ADVERTISED_FIBRE; - ecmd->port = PORT_DA; + ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); + ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE); + cmd->base.port = PORT_DA; break; case I40E_MEDIA_TYPE_FIBER: - ecmd->supported |= SUPPORTED_FIBRE; - ecmd->port = PORT_FIBRE; + ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); + cmd->base.port = PORT_FIBRE; break; case I40E_MEDIA_TYPE_UNKNOWN: default: - ecmd->port = PORT_OTHER; + cmd->base.port = PORT_OTHER; break; } - /* Set transceiver */ - ecmd->transceiver = XCVR_EXTERNAL; - /* Set flow control settings */ - ecmd->supported |= SUPPORTED_Pause; + ethtool_link_ksettings_add_link_mode(cmd, supported, Pause); switch (hw->fc.requested_mode) { case I40E_FC_FULL: - ecmd->advertising |= ADVERTISED_Pause; + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Pause); break; case I40E_FC_TX_PAUSE: - ecmd->advertising |= ADVERTISED_Asym_Pause; + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Asym_Pause); break; case I40E_FC_RX_PAUSE: - ecmd->advertising |= (ADVERTISED_Pause | - ADVERTISED_Asym_Pause); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Pause); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Asym_Pause); break; default: - ecmd->advertising &= ~(ADVERTISED_Pause | - ADVERTISED_Asym_Pause); + ethtool_convert_link_mode_to_legacy_u32( + &advertising, cmd->link_modes.advertising); + + advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); + + ethtool_convert_legacy_u32_to_link_mode( + cmd->link_modes.advertising, advertising); break; } @@ -664,8 +692,8 @@ static int i40e_get_settings(struct net_device *netdev, * * Set speed/duplex per media_types advertised/forced **/ -static int i40e_set_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int i40e_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_aq_get_phy_abilities_resp abilities; @@ -673,12 +701,14 @@ static int i40e_set_settings(struct net_device *netdev, struct i40e_pf *pf = np->vsi->back; struct i40e_vsi *vsi = np->vsi; struct i40e_hw *hw = &pf->hw; - struct ethtool_cmd safe_ecmd; + struct ethtool_link_ksettings safe_cmd; + struct ethtool_link_ksettings copy_cmd; i40e_status status = 0; bool change = false; int err = 0; - u8 autoneg; + u32 autoneg; u32 advertise; + u32 tmp; /* Changing port settings is not supported if this isn't the * port's controlling PF @@ -706,23 +736,31 @@ static int i40e_set_settings(struct net_device *netdev, return -EOPNOTSUPP; } + /* copy the cmd to copy_cmd to avoid modifying the origin */ + memcpy(©_cmd, cmd, sizeof(struct ethtool_link_ksettings)); + /* get our own copy of the bits to check against */ - memset(&safe_ecmd, 0, sizeof(struct ethtool_cmd)); - i40e_get_settings(netdev, &safe_ecmd); + memset(&safe_cmd, 0, sizeof(struct ethtool_link_ksettings)); + i40e_get_link_ksettings(netdev, &safe_cmd); - /* save autoneg and speed out of ecmd */ - autoneg = ecmd->autoneg; - advertise = ecmd->advertising; + /* save autoneg and speed out of cmd */ + autoneg = cmd->base.autoneg; + ethtool_convert_link_mode_to_legacy_u32(&advertise, + cmd->link_modes.advertising); /* set autoneg and speed back to what they currently are */ - ecmd->autoneg = safe_ecmd.autoneg; - ecmd->advertising = safe_ecmd.advertising; + copy_cmd.base.autoneg = safe_cmd.base.autoneg; + ethtool_convert_link_mode_to_legacy_u32( + &tmp, safe_cmd.link_modes.advertising); + ethtool_convert_legacy_u32_to_link_mode( + copy_cmd.link_modes.advertising, tmp); + + copy_cmd.base.cmd = safe_cmd.base.cmd; - ecmd->cmd = safe_ecmd.cmd; - /* If ecmd and safe_ecmd are not the same now, then they are + /* If copy_cmd and safe_cmd are not the same now, then they are * trying to set something that we do not support */ - if (memcmp(ecmd, &safe_ecmd, sizeof(struct ethtool_cmd))) + if (memcmp(©_cmd, &safe_cmd, sizeof(struct ethtool_link_ksettings))) return -EOPNOTSUPP; while (test_bit(__I40E_CONFIG_BUSY, &vsi->state)) @@ -745,7 +783,8 @@ static int i40e_set_settings(struct net_device *netdev, /* If autoneg was not already enabled */ if (!(hw->phy.link_info.an_info & I40E_AQ_AN_COMPLETED)) { /* If autoneg is not supported, return error */ - if (!(safe_ecmd.supported & SUPPORTED_Autoneg)) { + if (!ethtool_link_ksettings_test_link_mode( + &safe_cmd, supported, Autoneg)) { netdev_info(netdev, "Autoneg not supported on this phy\n"); return -EINVAL; } @@ -760,7 +799,8 @@ static int i40e_set_settings(struct net_device *netdev, /* If autoneg is supported 10GBASE_T is the only PHY * that can disable it, so otherwise return error */ - if (safe_ecmd.supported & SUPPORTED_Autoneg && + if (ethtool_link_ksettings_test_link_mode( + &safe_cmd, supported, Autoneg) && hw->phy.link_info.phy_type != I40E_PHY_TYPE_10GBASE_T) { netdev_info(netdev, "Autoneg cannot be disabled on this phy\n"); @@ -773,7 +813,9 @@ static int i40e_set_settings(struct net_device *netdev, } } - if (advertise & ~safe_ecmd.supported) + ethtool_convert_link_mode_to_legacy_u32(&tmp, + safe_cmd.link_modes.supported); + if (advertise & ~tmp) return -EINVAL; if (advertise & ADVERTISED_100baseT_Full) @@ -3156,8 +3198,6 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) } static const struct ethtool_ops i40e_ethtool_ops = { - .get_settings = i40e_get_settings, - .set_settings = i40e_set_settings, .get_drvinfo = i40e_get_drvinfo, .get_regs_len = i40e_get_regs_len, .get_regs = i40e_get_regs, @@ -3194,6 +3234,8 @@ static const struct ethtool_ops i40e_ethtool_ops = { .set_priv_flags = i40e_set_priv_flags, .get_per_queue_coalesce = i40e_get_per_queue_coalesce, .set_per_queue_coalesce = i40e_set_per_queue_coalesce, + .get_link_ksettings = i40e_get_link_ksettings, + .set_link_ksettings = i40e_set_link_ksettings, }; void i40e_set_ethtool_ops(struct net_device *netdev) -- cgit From 4485a841be171dbd8d3f0701b00f59d389e94ce6 Mon Sep 17 00:00:00 2001 From: "Reshetova, Elena" Date: Mon, 20 Mar 2017 11:43:28 +0200 Subject: netfilter: fix the warning on unused refcount variable net/netfilter/nfnetlink_acct.c: In function 'nfnl_acct_try_del': net/netfilter/nfnetlink_acct.c:329:15: warning: unused variable 'refcount' [-Wunused-variable] unsigned int refcount; ^ Fixes: b54ab92b84b6 ("netfilter: refcounter conversions") Signed-off-by: Elena Reshetova Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nfnetlink_acct.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c index f44cbd35357f..c86da174a5fc 100644 --- a/net/netfilter/nfnetlink_acct.c +++ b/net/netfilter/nfnetlink_acct.c @@ -326,7 +326,6 @@ static int nfnl_acct_get(struct net *net, struct sock *nfnl, static int nfnl_acct_try_del(struct nf_acct *cur) { int ret = 0; - unsigned int refcount; /* We want to avoid races with nfnl_acct_put. So only when the current * refcnt is 1, we decrease it to 0. -- cgit From 46bc92bedbd3cc9594bf572897e6a38346067176 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Thu, 16 Mar 2017 11:11:02 +0200 Subject: ath10k: fix warnings from an earlier commit I failed to notice that commit 523f6701dbab ("ath10k: update available channel list for 5G radio") added two new warnings: drivers/net/wireless/ath/ath10k/mac.c:3129:6: warning: symbol 'ath10k_mac_update_channel_list' was not declared. Should it be static? drivers/net/wireless/ath/ath10k/mac.c:3170: Alignment should match open parenthesis Fix those. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 167bde5cbbad..968b1d421225 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3126,8 +3126,8 @@ static void ath10k_regd_update(struct ath10k *ar) ath10k_warn(ar, "failed to set pdev regdomain: %d\n", ret); } -void ath10k_mac_update_channel_list(struct ath10k *ar, - struct ieee80211_supported_band *band) +static void ath10k_mac_update_channel_list(struct ath10k *ar, + struct ieee80211_supported_band *band) { int i; @@ -3167,7 +3167,7 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) ath10k_mac_update_channel_list(ar, - ar->hw->wiphy->bands[NL80211_BAND_5GHZ]); + ar->hw->wiphy->bands[NL80211_BAND_5GHZ]); } /***************/ -- cgit From 0a7d88e432e7fc968dee1ddbd214cf1bb966b6f9 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Tue, 14 Mar 2017 17:03:24 +0530 Subject: ath10k: fix typo in wmi header file Fix typo (spelling mistake) in wmi.h Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 78350d4c7b79..cf385feb5707 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -3182,7 +3182,7 @@ struct wmi_10_4_phyerr_event { struct phyerr_radar_report { __le32 reg0; /* RADAR_REPORT_REG0_* */ - __le32 reg1; /* REDAR_REPORT_REG1_* */ + __le32 reg1; /* RADAR_REPORT_REG1_* */ } __packed; #define RADAR_REPORT_REG0_PULSE_IS_CHIRP_MASK 0x80000000 -- cgit From eed4721fb6172b3051a8341f9089b728681a9454 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 15 Mar 2017 16:36:41 +0000 Subject: ath10k: remove redundant check of len with buf_len The check of len > buf_len is redundant as len is initialized to 0 and buf_len to 4096, so this comparison is always false. Remove it. Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index ac9090b752fc..00b424d99126 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -249,9 +249,6 @@ static ssize_t ath10k_read_wmi_services(struct file *file, mutex_lock(&ar->conf_mutex); - if (len > buf_len) - len = buf_len; - spin_lock_bh(&ar->data_lock); for (i = 0; i < WMI_SERVICE_MAX; i++) { enabled = test_bit(i, ar->wmi.svc_map); -- cgit From b51040fc375c194e20164be489668d4d28001f53 Mon Sep 17 00:00:00 2001 From: Rostyslav Khudolii Date: Thu, 16 Mar 2017 11:22:17 +0200 Subject: ath6kl: Remove old 802.11a-only channels Channels 34/38/42/46 can only be used for compatibility with old devices sold in Japan. Modern products, such as AR6003/AR6004 don't support these channels. Keeping them in the upstream is error prone and requires full network stack support. A custom patch should be used in case such compatibility is required. Without this one, a user is able to start an AP using wpa_supplicant, for example, on one of these channels (34/38/42/46), without getting any warning/error from the cfg80211 or ath6kl - which is correct (since these channels match regdom rules). However, the AR6003 and its firmware (we're using v3.4.0.225) will fail and return "WMI_CMDERROR_EVENTID" with "INVALID_PARAM" error code. Signed-off-by: Rostyslav Khudolii Cc: Attila Sukosd Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 363b30a549c2..aae65ce9a2b1 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -102,10 +102,8 @@ static struct ieee80211_channel ath6kl_2ghz_channels[] = { }; static struct ieee80211_channel ath6kl_5ghz_a_channels[] = { - CHAN5G(34, 0), CHAN5G(36, 0), - CHAN5G(38, 0), CHAN5G(40, 0), - CHAN5G(42, 0), CHAN5G(44, 0), - CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(36, 0), CHAN5G(40, 0), + CHAN5G(44, 0), CHAN5G(48, 0), CHAN5G(52, 0), CHAN5G(56, 0), CHAN5G(60, 0), CHAN5G(64, 0), CHAN5G(100, 0), CHAN5G(104, 0), -- cgit From 5c5105666de5ac1eef6c3b0710b88d5536b7f6dd Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 23 Feb 2017 17:14:45 +0100 Subject: atmel: remove time_t usage last_qual never really holds a time. It only holds jiffies. Make it the same type as jiffies. Signed-off-by: Alexandre Belloni Signed-off-by: Kalle Valo --- drivers/net/wireless/atmel/atmel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/atmel/atmel.c b/drivers/net/wireless/atmel/atmel.c index e12f62356fd1..27b110dc8cc6 100644 --- a/drivers/net/wireless/atmel/atmel.c +++ b/drivers/net/wireless/atmel/atmel.c @@ -513,7 +513,7 @@ struct atmel_private { } station_state; int operating_mode, power_mode; - time_t last_qual; + unsigned long last_qual; int beacons_this_sec; int channel; int reg_domain, config_reg_domain; -- cgit From ef7e0714742541dcce75b646b64f7a376b7d4368 Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Fri, 24 Feb 2017 14:24:31 +0800 Subject: mwifiex: wake system up when receives a wake irq Currrently we are disabling this wake irq after receiving it. If this happens before we finish suspend and the pm event check is disabled, the system will continue suspending, and this irq would not work again. We may need to abort system suspend to avoid that. Signed-off-by: Jeffy Chen Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 5ebca1d0cfc7..30f49944661f 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -17,6 +17,8 @@ * this warranty disclaimer. */ +#include + #include "main.h" #include "wmm.h" #include "cfg80211.h" @@ -1509,6 +1511,7 @@ static irqreturn_t mwifiex_irq_wakeup_handler(int irq, void *priv) /* Notify PM core we are wakeup source */ pm_wakeup_event(adapter->dev, 0); + pm_system_wakeup(); return IRQ_HANDLED; } -- cgit From 520334156213b3cf1becc238f881bd5333172b4d Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 7 Mar 2017 12:47:46 -0800 Subject: mwifiex: pcie: clean up error prints in mwifiex_pcie_reset_notify() We shouldn't be printing a kernel pointer as a decimal integer. But we really shouldn't be printing this case at all; we should never get here with NULL drvdata. We've eliminated this unnecessary conditional in several other places, so kill it here too. Similarly, there's no need to check for '!pdev'; we are guaranteed to have a real device here. And finally, use dev_err() instead of pr_err(). This yields (for failed PCIe resets): [ 68.286586] mwifiex_pcie 0000:01:00.0: mwifiex_pcie_reset_notify: adapter structure is not valid instead of: [ 82.932658] mwifiex_pcie: mwifiex_pcie_reset_notify: Card or adapter structure is not valid (-270880688088) Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index a0d918094889..5438483fcefe 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -350,22 +350,15 @@ MODULE_DEVICE_TABLE(pci, mwifiex_ids); static void mwifiex_pcie_reset_notify(struct pci_dev *pdev, bool prepare) { - struct mwifiex_adapter *adapter; - struct pcie_service_card *card; - - if (!pdev) { - pr_err("%s: PCIe device is not specified\n", __func__); - return; - } + struct pcie_service_card *card = pci_get_drvdata(pdev); + struct mwifiex_adapter *adapter = card->adapter; - card = (struct pcie_service_card *)pci_get_drvdata(pdev); - if (!card || !card->adapter) { - pr_err("%s: Card or adapter structure is not valid (%ld)\n", - __func__, (long)card); + if (!adapter) { + dev_err(&pdev->dev, "%s: adapter structure is not valid\n", + __func__); return; } - adapter = card->adapter; mwifiex_dbg(adapter, INFO, "%s: vendor=0x%4.04x device=0x%4.04x rev=%d %s\n", __func__, pdev->vendor, pdev->device, -- cgit From 5c0b87987432ad9d2c7068dc739d9f21bb6ff72e Mon Sep 17 00:00:00 2001 From: Karthik Ananthapadmanabha Date: Thu, 9 Mar 2017 14:06:15 +0530 Subject: mwifiex: add qualifier to firmware structures Adding qualifier "__packed" indicates that no padding should be performed on the qualified object for alignment. This patch adds qualifier __packed to the required firmware structures in mwifiex driver. Signed-off-by: Karthik Ananthapadmanabha Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/fw.h | 36 +++++++++++++++--------------- drivers/net/wireless/marvell/mwifiex/usb.h | 4 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index cb6a1a81d44e..0b683742e30c 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -31,17 +31,17 @@ struct rfc_1042_hdr { u8 llc_ctrl; u8 snap_oui[3]; __be16 snap_type; -}; +} __packed; struct rx_packet_hdr { struct ethhdr eth803_hdr; struct rfc_1042_hdr rfc1042_hdr; -}; +} __packed; struct tx_packet_hdr { struct ethhdr eth803_hdr; struct rfc_1042_hdr rfc1042_hdr; -}; +} __packed; #define B_SUPPORTED_RATES 5 #define G_SUPPORTED_RATES 9 @@ -707,7 +707,7 @@ struct uap_txpd { u8 reserved1[2]; u8 tx_token_id; u8 reserved[2]; -}; +} __packed; struct uap_rxpd { u8 bss_type; @@ -723,7 +723,7 @@ struct uap_rxpd { u8 ht_info; u8 reserved[3]; u8 flags; -}; +} __packed; struct mwifiex_fw_chan_stats { u8 chan_num; @@ -987,7 +987,7 @@ struct mwifiex_ps_param { __le16 adhoc_wake_period; __le16 mode; __le16 delay_to_ps; -}; +} __packed; #define HS_DEF_WAKE_INTERVAL 100 #define HS_DEF_INACTIVITY_TIMEOUT 50 @@ -996,7 +996,7 @@ struct mwifiex_ps_param_in_hs { struct mwifiex_ie_types_header header; __le32 hs_wake_int; __le32 hs_inact_timeout; -}; +} __packed; #define BITMAP_AUTO_DS 0x01 #define BITMAP_STA_PS 0x10 @@ -1062,7 +1062,7 @@ struct host_cmd_ds_802_11_rssi_info { __le16 nbcn; __le16 reserved[9]; long long reserved_1; -}; +} __packed; struct host_cmd_ds_802_11_rssi_info_rsp { __le16 action; @@ -1077,12 +1077,12 @@ struct host_cmd_ds_802_11_rssi_info_rsp { __le16 bcn_rssi_avg; __le16 bcn_nf_avg; long long tsf_bcn; -}; +} __packed; struct host_cmd_ds_802_11_mac_address { __le16 action; u8 mac_addr[ETH_ALEN]; -}; +} __packed; struct host_cmd_ds_mac_control { __le32 action; @@ -1230,7 +1230,7 @@ struct host_cmd_ds_802_11_get_log { __le32 wep_icv_err_cnt[4]; __le32 bcn_rcv_cnt; __le32 bcn_miss_cnt; -}; +} __packed; /* Enumeration for rate format */ enum _mwifiex_rate_format { @@ -1368,12 +1368,12 @@ struct host_cmd_ds_rf_ant_mimo { __le16 tx_ant_mode; __le16 action_rx; __le16 rx_ant_mode; -}; +} __packed; struct host_cmd_ds_rf_ant_siso { __le16 action; __le16 ant_mode; -}; +} __packed; struct host_cmd_ds_tdls_oper { __le16 tdls_action; @@ -1383,13 +1383,13 @@ struct host_cmd_ds_tdls_oper { struct mwifiex_tdls_config { __le16 enable; -}; +} __packed; struct mwifiex_tdls_config_cs_params { u8 unit_time; u8 thr_otherlink; u8 thr_directlink; -}; +} __packed; struct mwifiex_tdls_init_cs_params { u8 peer_mac[ETH_ALEN]; @@ -1404,7 +1404,7 @@ struct mwifiex_tdls_init_cs_params { struct mwifiex_tdls_stop_cs_params { u8 peer_mac[ETH_ALEN]; -}; +} __packed; struct host_cmd_ds_tdls_config { __le16 tdls_action; @@ -1709,7 +1709,7 @@ struct mwifiex_ie_types_local_pwr_constraint { struct mwifiex_ie_types_wmm_param_set { struct mwifiex_ie_types_header header; u8 wmm_ie[1]; -}; +} __packed; struct mwifiex_ie_types_mgmt_frame { struct mwifiex_ie_types_header header; @@ -1834,7 +1834,7 @@ struct host_cmd_ds_mem_access { __le16 reserved; __le32 addr; __le32 value; -}; +} __packed; struct mwifiex_ie_types_qos_info { struct mwifiex_ie_types_header header; diff --git a/drivers/net/wireless/marvell/mwifiex/usb.h b/drivers/net/wireless/marvell/mwifiex/usb.h index e5f204ea018b..16017aeb8cfe 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.h +++ b/drivers/net/wireless/marvell/mwifiex/usb.h @@ -102,12 +102,12 @@ struct fw_header { struct fw_sync_header { __le32 cmd; __le32 seq_num; -}; +} __packed; struct fw_data { struct fw_header fw_hdr; __le32 seq_num; u8 data[1]; -}; +} __packed; #endif /*_MWIFIEX_USB_H */ -- cgit From 5653c6462f56e13cb943cd572fdae9252157a523 Mon Sep 17 00:00:00 2001 From: Daniel Mentz Date: Thu, 9 Mar 2017 14:06:16 +0530 Subject: mwifiex: Use accessors routines for unaligned values Synopsys' ARCompact architecture does not support loading from or storing values to unaligned memory locations. We saw a series of misaligned access exceptions on ARC. To work around this issue, we bulk replaced le16_to_cpu and le32_to_cpu with get_unaligned_le16 and get_unaligned_le32, respectively. We also added le16_unaligned_add_cpu which is similar to le16_add_cpu but works with unaligned values. Signed-off-by: Daniel Mentz Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/11h.c | 3 ++- drivers/net/wireless/marvell/mwifiex/ie.c | 15 +++++++------ drivers/net/wireless/marvell/mwifiex/main.h | 2 +- drivers/net/wireless/marvell/mwifiex/scan.c | 26 +++++++++++++--------- drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 30 +++++++++++++++----------- drivers/net/wireless/marvell/mwifiex/util.h | 5 +++++ 6 files changed, 49 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/11h.c b/drivers/net/wireless/marvell/mwifiex/11h.c index 43dccd5b0291..366eb4991a7d 100644 --- a/drivers/net/wireless/marvell/mwifiex/11h.c +++ b/drivers/net/wireless/marvell/mwifiex/11h.c @@ -153,7 +153,8 @@ int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv, cmd->command = cpu_to_le16(HostCmd_CMD_CHAN_REPORT_REQUEST); cmd->size = cpu_to_le16(S_DS_GEN); - le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_chan_rpt_req)); + le16_unaligned_add_cpu(&cmd->size, + sizeof(struct host_cmd_ds_chan_rpt_req)); cr_req->chan_desc.start_freq = cpu_to_le16(MWIFIEX_A_BAND_START_FREQ); cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value; diff --git a/drivers/net/wireless/marvell/mwifiex/ie.c b/drivers/net/wireless/marvell/mwifiex/ie.c index c488c3068abc..922e3d69fd84 100644 --- a/drivers/net/wireless/marvell/mwifiex/ie.c +++ b/drivers/net/wireless/marvell/mwifiex/ie.c @@ -131,9 +131,10 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv, sizeof(struct mwifiex_ie)); } - le16_add_cpu(&ie_list->len, - le16_to_cpu(priv->mgmt_ie[index].ie_length) + - MWIFIEX_IE_HDR_SIZE); + le16_unaligned_add_cpu(&ie_list->len, + le16_to_cpu( + priv->mgmt_ie[index].ie_length) + + MWIFIEX_IE_HDR_SIZE); input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE; } @@ -172,21 +173,21 @@ mwifiex_update_uap_custom_ie(struct mwifiex_private *priv, le16_to_cpu(beacon_ie->ie_length); memcpy(pos, beacon_ie, len); pos += len; - le16_add_cpu(&ap_custom_ie->len, len); + le16_unaligned_add_cpu(&ap_custom_ie->len, len); } if (pr_ie) { len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + le16_to_cpu(pr_ie->ie_length); memcpy(pos, pr_ie, len); pos += len; - le16_add_cpu(&ap_custom_ie->len, len); + le16_unaligned_add_cpu(&ap_custom_ie->len, len); } if (ar_ie) { len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + le16_to_cpu(ar_ie->ie_length); memcpy(pos, ar_ie, len); pos += len; - le16_add_cpu(&ap_custom_ie->len, len); + le16_unaligned_add_cpu(&ap_custom_ie->len, len); } ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie); @@ -242,7 +243,7 @@ static int mwifiex_update_vs_ie(const u8 *ies, int ies_len, vs_ie = (struct ieee_types_header *)vendor_ie; memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length), vs_ie, vs_ie->len + 2); - le16_add_cpu(&ie->ie_length, vs_ie->len + 2); + le16_unaligned_add_cpu(&ie->ie_length, vs_ie->len + 2); ie->mgmt_subtype_mask = cpu_to_le16(mask); ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK); } diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 5c8297207f33..f1cb8753dc02 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1359,7 +1359,7 @@ mwifiex_netdev_get_priv(struct net_device *dev) */ static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb) { - return (le32_to_cpu(*(__le32 *)skb->data) == PKT_TYPE_MGMT); + return (get_unaligned_le32(skb->data) == PKT_TYPE_MGMT); } /* This function retrieves channel closed for operation by Channel diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index 181691684a08..8cd12347ee3b 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -691,8 +691,9 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, /* Increment the TLV header length by the size appended */ - le16_add_cpu(&chan_tlv_out->header.len, - sizeof(chan_tlv_out->chan_scan_param)); + le16_unaligned_add_cpu(&chan_tlv_out->header.len, + sizeof( + chan_tlv_out->chan_scan_param)); /* * The tlv buffer length is set to the number of bytes @@ -859,6 +860,7 @@ mwifiex_config_scan(struct mwifiex_private *priv, *scan_current_only = false; if (user_scan_in) { + u8 tmpaddr[ETH_ALEN]; /* Default the ssid_filter flag to TRUE, set false under certain wildcard conditions and qualified by the existence @@ -883,8 +885,10 @@ mwifiex_config_scan(struct mwifiex_private *priv, user_scan_in->specific_bssid, sizeof(scan_cfg_out->specific_bssid)); + memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN); + if (adapter->ext_scan && - !is_zero_ether_addr(scan_cfg_out->specific_bssid)) { + !is_zero_ether_addr(tmpaddr)) { bssid_tlv = (struct mwifiex_ie_types_bssid_list *)tlv_pos; bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID); @@ -947,8 +951,9 @@ mwifiex_config_scan(struct mwifiex_private *priv, * truncate scan results. That is not an issue with an SSID * or BSSID filter applied to the scan results in the firmware. */ + memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN); if ((i && ssid_filter) || - !is_zero_ether_addr(scan_cfg_out->specific_bssid)) + !is_zero_ether_addr(tmpaddr)) *filtered_scan = true; if (user_scan_in->scan_chan_gap) { @@ -1742,7 +1747,7 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, if (*bytes_left >= sizeof(beacon_size)) { /* Extract & convert beacon size from command buffer */ - beacon_size = le16_to_cpu(*(__le16 *)(*bss_info)); + beacon_size = get_unaligned_le16((*bss_info)); *bytes_left -= sizeof(beacon_size); *bss_info += sizeof(beacon_size); } @@ -2369,8 +2374,9 @@ int mwifiex_cmd_802_11_bg_scan_config(struct mwifiex_private *priv, temp_chan = chan_list_tlv->chan_scan_param + chan_idx; /* Increment the TLV header length by size appended */ - le16_add_cpu(&chan_list_tlv->header.len, - sizeof(chan_list_tlv->chan_scan_param)); + le16_unaligned_add_cpu(&chan_list_tlv->header.len, + sizeof( + chan_list_tlv->chan_scan_param)); temp_chan->chan_number = bgscan_cfg_in->chan_list[chan_idx].chan_number; @@ -2407,8 +2413,8 @@ int mwifiex_cmd_802_11_bg_scan_config(struct mwifiex_private *priv, mwifiex_bgscan_create_channel_list(priv, bgscan_cfg_in, chan_list_tlv-> chan_scan_param); - le16_add_cpu(&chan_list_tlv->header.len, - chan_num * + le16_unaligned_add_cpu(&chan_list_tlv->header.len, + chan_num * sizeof(chan_list_tlv->chan_scan_param[0])); } @@ -2432,7 +2438,7 @@ int mwifiex_cmd_802_11_bg_scan_config(struct mwifiex_private *priv, /* Append vendor specific IE TLV */ mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_BGSCAN, &tlv_pos); - le16_add_cpu(&cmd->size, tlv_pos - bgscan_config->tlv); + le16_unaligned_add_cpu(&cmd->size, tlv_pos - bgscan_config->tlv); return 0; } diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index 2f1f4d190b28..cf9121e5f87a 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -126,12 +126,12 @@ static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv, if (cmd_action == HostCmd_ACT_GEN_GET) { snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_GET); snmp_mib->buf_size = cpu_to_le16(MAX_SNMP_BUF_SIZE); - le16_add_cpu(&cmd->size, MAX_SNMP_BUF_SIZE); + le16_unaligned_add_cpu(&cmd->size, MAX_SNMP_BUF_SIZE); } else if (cmd_action == HostCmd_ACT_GEN_SET) { snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET); snmp_mib->buf_size = cpu_to_le16(sizeof(u16)); *((__le16 *) (snmp_mib->value)) = cpu_to_le16(*ul_temp); - le16_add_cpu(&cmd->size, sizeof(u16)); + le16_unaligned_add_cpu(&cmd->size, sizeof(u16)); } mwifiex_dbg(priv->adapter, CMD, @@ -1357,8 +1357,9 @@ mwifiex_cmd_802_11_subsc_evt(struct mwifiex_private *priv, subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq); pos += sizeof(struct mwifiex_ie_types_rssi_threshold); - le16_add_cpu(&cmd->size, - sizeof(struct mwifiex_ie_types_rssi_threshold)); + le16_unaligned_add_cpu(&cmd->size, + sizeof( + struct mwifiex_ie_types_rssi_threshold)); } if (event_bitmap & BITMASK_BCN_RSSI_HIGH) { @@ -1378,8 +1379,9 @@ mwifiex_cmd_802_11_subsc_evt(struct mwifiex_private *priv, subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq); pos += sizeof(struct mwifiex_ie_types_rssi_threshold); - le16_add_cpu(&cmd->size, - sizeof(struct mwifiex_ie_types_rssi_threshold)); + le16_unaligned_add_cpu(&cmd->size, + sizeof( + struct mwifiex_ie_types_rssi_threshold)); } return 0; @@ -1683,14 +1685,15 @@ mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv, sizeof(u8) + sizeof(u8)); /* Add the rule length to the command size*/ - le16_add_cpu(&cmd->size, le16_to_cpu(rule->header.len) + - sizeof(struct mwifiex_ie_types_header)); + le16_unaligned_add_cpu(&cmd->size, + le16_to_cpu(rule->header.len) + + sizeof(struct mwifiex_ie_types_header)); rule = (void *)((u8 *)rule->params + length); } /* Add sizeof action, num_of_rules to total command length */ - le16_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16)); + le16_unaligned_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16)); return 0; } @@ -1708,7 +1711,7 @@ mwifiex_cmd_tdls_config(struct mwifiex_private *priv, cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_CONFIG); cmd->size = cpu_to_le16(S_DS_GEN); tdls_config->tdls_action = cpu_to_le16(cmd_action); - le16_add_cpu(&cmd->size, sizeof(tdls_config->tdls_action)); + le16_unaligned_add_cpu(&cmd->size, sizeof(tdls_config->tdls_action)); switch (cmd_action) { case ACT_TDLS_CS_ENABLE_CONFIG: @@ -1735,7 +1738,7 @@ mwifiex_cmd_tdls_config(struct mwifiex_private *priv, return -ENOTSUPP; } - le16_add_cpu(&cmd->size, len); + le16_unaligned_add_cpu(&cmd->size, len); return 0; } @@ -1759,7 +1762,8 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_OPER); cmd->size = cpu_to_le16(S_DS_GEN); - le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_tdls_oper)); + le16_unaligned_add_cpu(&cmd->size, + sizeof(struct host_cmd_ds_tdls_oper)); tdls_oper->reason = 0; memcpy(tdls_oper->peer_mac, oper->peer_mac, ETH_ALEN); @@ -1861,7 +1865,7 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, return -ENOTSUPP; } - le16_add_cpu(&cmd->size, config_len); + le16_unaligned_add_cpu(&cmd->size, config_len); return 0; } diff --git a/drivers/net/wireless/marvell/mwifiex/util.h b/drivers/net/wireless/marvell/mwifiex/util.h index b541d66c01eb..c386992abcdb 100644 --- a/drivers/net/wireless/marvell/mwifiex/util.h +++ b/drivers/net/wireless/marvell/mwifiex/util.h @@ -93,4 +93,9 @@ static inline dma_addr_t MWIFIEX_SKB_DMA_ADDR(struct sk_buff *skb) int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, struct mwifiex_debug_info *info); +static inline void le16_unaligned_add_cpu(__le16 *var, u16 val) +{ + put_unaligned_le16(get_unaligned_le16(var) + val, var); +} + #endif /* !_MWIFIEX_UTIL_H_ */ -- cgit From 92c70a958b0b6b7e729a47a7c85762636859eee9 Mon Sep 17 00:00:00 2001 From: Devidas Puranik Date: Thu, 9 Mar 2017 14:06:17 +0530 Subject: mwifiex: fix for unaligned reads Using the accessor function e.g. get_unaligned_le32 instead of le32_to_cpu to avoid the unaligned access. This is for the architectures that don't handle the unaligned memory access Signed-off-by: Devidas Puranik Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cmdevt.c | 4 +-- drivers/net/wireless/marvell/mwifiex/pcie.c | 37 ++++++++++------------ drivers/net/wireless/marvell/mwifiex/sdio.c | 23 ++++++++------ drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 19 +++++------ drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c | 4 +-- drivers/net/wireless/marvell/mwifiex/sta_event.c | 8 ++--- drivers/net/wireless/marvell/mwifiex/tdls.c | 10 +++--- drivers/net/wireless/marvell/mwifiex/uap_event.c | 2 +- drivers/net/wireless/marvell/mwifiex/util.c | 6 ++-- 9 files changed, 56 insertions(+), 57 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c index 25a7475702f7..0c3b217247b1 100644 --- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c +++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c @@ -242,7 +242,7 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, mwifiex_dbg(adapter, CMD, "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", cmd_code, - le16_to_cpu(*(__le16 *)((u8 *)host_cmd + S_DS_GEN)), + get_unaligned_le16((u8 *)host_cmd + S_DS_GEN), cmd_size, le16_to_cpu(host_cmd->seq_num)); mwifiex_dbg_dump(adapter, CMD_D, "cmd buffer:", host_cmd, cmd_size); @@ -286,7 +286,7 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM; adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code; adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] = - le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)); + get_unaligned_le16((u8 *)host_cmd + S_DS_GEN); /* Clear BSS_NO_BITS from HostCmd */ cmd_code &= HostCmd_CMD_ID_MASK; diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 5438483fcefe..23e09117bf4a 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -119,7 +119,7 @@ static int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter, */ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) { - u32 *cookie_addr; + u32 cookie_value; struct pcie_service_card *card = adapter->card; const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; @@ -127,11 +127,11 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) return true; if (card->sleep_cookie_vbase) { - cookie_addr = (u32 *)card->sleep_cookie_vbase; + cookie_value = get_unaligned_le32(card->sleep_cookie_vbase); mwifiex_dbg(adapter, INFO, "info: ACCESS_HW: sleep cookie=0x%x\n", - *cookie_addr); - if (*cookie_addr == FW_AWAKE_COOKIE) + cookie_value); + if (cookie_value == FW_AWAKE_COOKIE) return true; } @@ -440,7 +440,7 @@ static void mwifiex_delay_for_sleep_cookie(struct mwifiex_adapter *adapter, sizeof(sleep_cookie), PCI_DMA_FROMDEVICE); buffer = cmdrsp->data; - sleep_cookie = READ_ONCE(*(u32 *)buffer); + sleep_cookie = get_unaligned_le32(buffer); if (sleep_cookie == MWIFIEX_DEF_SLEEP_COOKIE) { mwifiex_dbg(adapter, INFO, @@ -1042,6 +1042,7 @@ static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) static int mwifiex_pcie_alloc_sleep_cookie_buf(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; + u32 tmp; card->sleep_cookie_vbase = pci_alloc_consistent(card->dev, sizeof(u32), &card->sleep_cookie_pbase); @@ -1051,11 +1052,12 @@ static int mwifiex_pcie_alloc_sleep_cookie_buf(struct mwifiex_adapter *adapter) return -ENOMEM; } /* Init val of Sleep Cookie */ - *(u32 *)card->sleep_cookie_vbase = FW_AWAKE_COOKIE; + tmp = FW_AWAKE_COOKIE; + put_unaligned(tmp, card->sleep_cookie_vbase); mwifiex_dbg(adapter, INFO, "alloc_scook: sleep cookie=0x%x\n", - *((u32 *)card->sleep_cookie_vbase)); + get_unaligned(card->sleep_cookie_vbase)); return 0; } @@ -1216,7 +1218,6 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, dma_addr_t buf_pa; struct mwifiex_pcie_buf_desc *desc = NULL; struct mwifiex_pfu_buf_desc *desc2 = NULL; - __le16 *tmp; if (!(skb->data && skb->len)) { mwifiex_dbg(adapter, ERROR, @@ -1237,10 +1238,8 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, adapter->data_sent = true; payload = skb->data; - tmp = (__le16 *)&payload[0]; - *tmp = cpu_to_le16((u16)skb->len); - tmp = (__le16 *)&payload[2]; - *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA); + put_unaligned_le16((u16)skb->len, payload + 0); + put_unaligned_le16(MWIFIEX_TYPE_DATA, payload + 2); if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE)) @@ -1369,7 +1368,6 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) (card->rxbd_rdptr & reg->rx_rollover_ind))) { struct sk_buff *skb_data; u16 rx_len; - __le16 pkt_len; rd_index = card->rxbd_rdptr & reg->rx_mask; skb_data = card->rx_buf_list[rd_index]; @@ -1386,8 +1384,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) /* Get data length from interface header - * first 2 bytes for len, next 2 bytes is for type */ - pkt_len = *((__le16 *)skb_data->data); - rx_len = le16_to_cpu(pkt_len); + rx_len = get_unaligned_le16(skb_data->data); if (WARN_ON(rx_len <= INTF_HEADER_LEN || rx_len > MWIFIEX_RX_DATA_BUF_SIZE)) { mwifiex_dbg(adapter, ERROR, @@ -1594,8 +1591,8 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) adapter->cmd_sent = true; - *(__le16 *)&payload[0] = cpu_to_le16((u16)skb->len); - *(__le16 *)&payload[2] = cpu_to_le16(MWIFIEX_TYPE_CMD); + put_unaligned_le16((u16)skb->len, &payload[0]); + put_unaligned_le16(MWIFIEX_TYPE_CMD, &payload[2]); if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE)) return -1; @@ -1687,7 +1684,6 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) struct sk_buff *skb = card->cmdrsp_buf; int count = 0; u16 rx_len; - __le16 pkt_len; mwifiex_dbg(adapter, CMD, "info: Rx CMD Response\n"); @@ -1707,8 +1703,7 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) card->cmd_buf = NULL; } - pkt_len = *((__le16 *)skb->data); - rx_len = le16_to_cpu(pkt_len); + rx_len = get_unaligned_le16(skb->data); skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len); skb_trim(skb, rx_len); @@ -1849,7 +1844,7 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter) desc = card->evtbd_ring[rdptr]; memset(desc, 0, sizeof(*desc)); - event = *(u32 *) &skb_cmd->data[INTF_HEADER_LEN]; + event = get_unaligned_le32(&skb_cmd->data[INTF_HEADER_LEN]); adapter->event_cause = event; /* The first 4bytes will be the event transfer header len is 2 bytes followed by type which is 2 bytes */ diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index a4b356d267f9..9f7cabf0236b 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -943,7 +943,7 @@ static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter, return -1; } - nb = le16_to_cpu(*(__le16 *) (buffer)); + nb = get_unaligned_le16((buffer)); if (nb > npayload) { mwifiex_dbg(adapter, ERROR, "%s: invalid packet, nb=%d npayload=%d\n", @@ -951,7 +951,7 @@ static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter, return -1; } - *type = le16_to_cpu(*(__le16 *) (buffer + 2)); + *type = get_unaligned_le16((buffer + 2)); return ret; } @@ -1139,7 +1139,8 @@ static void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter, __func__, blk_num, blk_size, total_pkt_len); break; } - pkt_len = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET)); + pkt_len = get_unaligned_le16((data + + SDIO_HEADER_OFFSET)); if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) { mwifiex_dbg(adapter, ERROR, "%s: error in pkt_len,\t" @@ -1172,10 +1173,11 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb, u32 upld_typ) { u8 *cmd_buf; - __le16 *curr_ptr = (__le16 *)skb->data; - u16 pkt_len = le16_to_cpu(*curr_ptr); + u16 pkt_len; struct mwifiex_rxinfo *rx_info; + pkt_len = get_unaligned_le16(skb->data); + if (upld_typ != MWIFIEX_TYPE_AGGR_DATA) { skb_trim(skb, pkt_len); skb_pull(skb, INTF_HEADER_LEN); @@ -1235,7 +1237,7 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, case MWIFIEX_TYPE_EVENT: mwifiex_dbg(adapter, EVENT, "info: --- Rx: Event ---\n"); - adapter->event_cause = le32_to_cpu(*(__le32 *) skb->data); + adapter->event_cause = get_unaligned_le32(skb->data); if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE)) memcpy(adapter->event_body, @@ -1392,8 +1394,8 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, u32 *len_arr = card->mpa_rx.len_arr; /* get curr PKT len & type */ - pkt_len = le16_to_cpu(*(__le16 *) &curr_ptr[0]); - pkt_type = le16_to_cpu(*(__le16 *) &curr_ptr[2]); + pkt_len = get_unaligned_le16(&curr_ptr[0]); + pkt_type = get_unaligned_le16(&curr_ptr[2]); /* copy pkt to deaggr buf */ skb_deaggr = mwifiex_alloc_dma_align_buf(len_arr[pind], @@ -1874,8 +1876,9 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, /* Allocate buffer and copy payload */ blk_size = MWIFIEX_SDIO_BLOCK_SIZE; buf_block_len = (pkt_len + blk_size - 1) / blk_size; - *(__le16 *)&payload[0] = cpu_to_le16((u16)pkt_len); - *(__le16 *)&payload[2] = cpu_to_le16(type); + put_unaligned_le16((u16)pkt_len, payload + 0); + put_unaligned_le16((u32)type, payload + 2); + /* * This is SDIO specific header diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index cf9121e5f87a..f6056ab28efe 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -130,7 +130,7 @@ static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv, } else if (cmd_action == HostCmd_ACT_GEN_SET) { snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET); snmp_mib->buf_size = cpu_to_le16(sizeof(u16)); - *((__le16 *) (snmp_mib->value)) = cpu_to_le16(*ul_temp); + put_unaligned_le16(*ul_temp, snmp_mib->value); le16_unaligned_add_cpu(&cmd->size, sizeof(u16)); } @@ -138,7 +138,7 @@ static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv, "cmd: SNMP_CMD: Action=0x%x, OID=0x%x,\t" "OIDSize=0x%x, Value=0x%x\n", cmd_action, cmd_oid, le16_to_cpu(snmp_mib->buf_size), - le16_to_cpu(*(__le16 *)snmp_mib->value)); + get_unaligned_le16(snmp_mib->value)); return 0; } @@ -1400,7 +1400,7 @@ mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv, filter = &mef_entry->filter[i]; if (!filter->filt_type) break; - *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->repeat); + put_unaligned_le32((u32)filter->repeat, stack_ptr); stack_ptr += 4; *stack_ptr = TYPE_DNUM; stack_ptr += 1; @@ -1412,8 +1412,7 @@ mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv, stack_ptr += 1; *stack_ptr = TYPE_BYTESEQ; stack_ptr += 1; - - *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->offset); + put_unaligned_le32((u32)filter->offset, stack_ptr); stack_ptr += 4; *stack_ptr = TYPE_DNUM; stack_ptr += 1; @@ -1787,7 +1786,7 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, return -ENODATA; } - *(__le16 *)pos = cpu_to_le16(params->capability); + put_unaligned_le16(params->capability, pos); config_len += sizeof(params->capability); qos_info = params->uapsd_queues | (params->max_sp << 5); @@ -2036,7 +2035,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, case HostCmd_CMD_VERSION_EXT: cmd_ptr->command = cpu_to_le16(cmd_no); cmd_ptr->params.verext.version_str_sel = - (u8) (*((u32 *) data_buf)); + (u8)(get_unaligned((u32 *)data_buf)); memcpy(&cmd_ptr->params, data_buf, sizeof(struct host_cmd_ds_version_ext)); cmd_ptr->size = @@ -2047,7 +2046,8 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, case HostCmd_CMD_MGMT_FRAME_REG: cmd_ptr->command = cpu_to_le16(cmd_no); cmd_ptr->params.reg_mask.action = cpu_to_le16(cmd_action); - cmd_ptr->params.reg_mask.mask = cpu_to_le32(*(u32 *)data_buf); + cmd_ptr->params.reg_mask.mask = cpu_to_le32( + get_unaligned((u32 *)data_buf)); cmd_ptr->size = cpu_to_le16(sizeof(struct host_cmd_ds_mgmt_frame_reg) + S_DS_GEN); @@ -2067,7 +2067,8 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, case HostCmd_CMD_P2P_MODE_CFG: cmd_ptr->command = cpu_to_le16(cmd_no); cmd_ptr->params.mode_cfg.action = cpu_to_le16(cmd_action); - cmd_ptr->params.mode_cfg.mode = cpu_to_le16(*(u16 *)data_buf); + cmd_ptr->params.mode_cfg.mode = cpu_to_le16( + get_unaligned((u16 *)data_buf)); cmd_ptr->size = cpu_to_le16(sizeof(struct host_cmd_ds_p2p_mode_cfg) + S_DS_GEN); diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c index 8548027abf71..ab75da3e0c2b 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c @@ -183,7 +183,7 @@ static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv, "query_type = %#x, buf size = %#x\n", oid, query_type, le16_to_cpu(smib->buf_size)); if (query_type == HostCmd_ACT_GEN_GET) { - ul_temp = le16_to_cpu(*((__le16 *) (smib->value))); + ul_temp = get_unaligned_le16(smib->value); if (data_buf) *data_buf = ul_temp; switch (oid) { @@ -741,7 +741,7 @@ mwifiex_ret_p2p_mode_cfg(struct mwifiex_private *priv, struct host_cmd_ds_p2p_mode_cfg *mode_cfg = &resp->params.mode_cfg; if (data_buf) - *((u16 *)data_buf) = le16_to_cpu(mode_cfg->mode); + put_unaligned_le16(le16_to_cpu(mode_cfg->mode), data_buf); return 0; } diff --git a/drivers/net/wireless/marvell/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c index d63d163eb1ec..b5b7664507eb 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_event.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c @@ -670,7 +670,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->dbg.num_event_deauth++; if (priv->media_connected) { reason_code = - le16_to_cpu(*(__le16 *)adapter->event_body); + get_unaligned_le16(adapter->event_body); mwifiex_reset_connect_state(priv, reason_code, true); } break; @@ -685,7 +685,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->dbg.num_event_disassoc++; if (priv->media_connected) { reason_code = - le16_to_cpu(*(__le16 *)adapter->event_body); + get_unaligned_le16(adapter->event_body); mwifiex_reset_connect_state(priv, reason_code, true); } break; @@ -695,7 +695,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->dbg.num_event_link_lost++; if (priv->media_connected) { reason_code = - le16_to_cpu(*(__le16 *)adapter->event_body); + get_unaligned_le16(adapter->event_body); mwifiex_reset_connect_state(priv, reason_code, true); } break; @@ -923,7 +923,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->event_body); break; case EVENT_AMSDU_AGGR_CTRL: - ctrl = le16_to_cpu(*(__le16 *)adapter->event_body); + ctrl = get_unaligned_le16(adapter->event_body); mwifiex_dbg(adapter, EVENT, "event: AMSDU_AGGR_CTRL %d\n", ctrl); diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c index df9704de0715..e228c03f98f6 100644 --- a/drivers/net/wireless/marvell/mwifiex/tdls.c +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -857,7 +857,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, struct mwifiex_sta_node *sta_ptr; u8 *peer, *pos, *end; u8 i, action, basic; - __le16 cap = 0; + u16 cap = 0; int ie_len = 0; if (len < (sizeof(struct ethhdr) + 3)) @@ -879,7 +879,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, pos = buf + sizeof(struct ethhdr) + 4; /* payload 1+ category 1 + action 1 + dialog 1 */ - cap = cpu_to_le16(*(u16 *)pos); + cap = get_unaligned_le16(pos); ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN; pos += 2; break; @@ -889,7 +889,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, return; /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/ pos = buf + sizeof(struct ethhdr) + 6; - cap = cpu_to_le16(*(u16 *)pos); + cap = get_unaligned_le16(pos); ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN; pos += 2; break; @@ -909,7 +909,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, if (!sta_ptr) return; - sta_ptr->tdls_cap.capab = cap; + sta_ptr->tdls_cap.capab = cpu_to_le16(cap); for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) { if (pos + 2 + pos[1] > end) @@ -969,7 +969,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, case WLAN_EID_AID: if (priv->adapter->is_hw_11ac_capable) sta_ptr->tdls_cap.aid = - le16_to_cpu(*(__le16 *)(pos + 2)); + get_unaligned_le16((pos + 2)); default: break; } diff --git a/drivers/net/wireless/marvell/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c index d24eca34ac11..e10b2a52e78f 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_event.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c @@ -202,7 +202,7 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) "AP EVENT: event id: %#x\n", eventcause); break; case EVENT_AMSDU_AGGR_CTRL: - ctrl = le16_to_cpu(*(__le16 *)adapter->event_body); + ctrl = get_unaligned_le16(adapter->event_body); mwifiex_dbg(adapter, EVENT, "event: AMSDU_AGGR_CTRL %d\n", ctrl); diff --git a/drivers/net/wireless/marvell/mwifiex/util.c b/drivers/net/wireless/marvell/mwifiex/util.c index b1ab8da121dd..0cd68ffc2c74 100644 --- a/drivers/net/wireless/marvell/mwifiex/util.c +++ b/drivers/net/wireless/marvell/mwifiex/util.c @@ -274,13 +274,13 @@ int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, val = *((u8 *)addr); break; case 2: - val = *((u16 *)addr); + val = get_unaligned((u16 *)addr); break; case 4: - val = *((u32 *)addr); + val = get_unaligned((u32 *)addr); break; case 8: - val = *((long long *)addr); + val = get_unaligned((long long *)addr); break; default: val = -1; -- cgit From 0a5cc49750b89717ad02abf132aa119743c0e5d4 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 16 Mar 2017 16:16:11 +0530 Subject: mwifiex: send fewer channels to scan while connected Application triggers periodic background scans when device is connected. We will scan less number of channels per scan command so that data traffic won't get affected. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/scan.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index 8cd12347ee3b..ce6936d0c5c0 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -994,10 +994,15 @@ mwifiex_config_scan(struct mwifiex_private *priv, * If a specific BSSID or SSID is used, the number of channels in the * scan command will be increased to the absolute maximum. */ - if (*filtered_scan) + if (*filtered_scan) { *max_chan_per_scan = MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; - else - *max_chan_per_scan = MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD; + } else { + if (!priv->media_connected) + *max_chan_per_scan = MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD; + else + *max_chan_per_scan = + MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD / 2; + } if (adapter->ext_scan) { bss_mode = (struct mwifiex_ie_types_bss_mode *)tlv_pos; -- cgit From 5caa7f384629eb2b02f2289ffea1277a401adfcb Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 16 Mar 2017 14:36:07 -0700 Subject: mwifiex: fix kernel crash after shutdown command timeout We observed a SHUTDOWN command timeout during reboot stress test due to a corner case firmware bug. It can lead to either a use-after-free + OOPS (on either the adapter structure, or the 'card' structure) or an abort (where, e.g., the PCI device is "disabled" before we're done dumping the FW). We can avoid this by canceling/flushing the FW dump work: (a) after we've terminated all other work queues (e.g., for processing commands which could time out) (b) after we've disabled all interrupts (which could also queue more work for us) (c) after we've unregistered the netdev and wiphy structures (and implicitly, and debugfs entries which could manually trigger FW dumps) (d) before we've actually disabled the device (e.g., pci_device_disable()) Altogether, this means no card->work will be scheduled if we sync at a point that satisfies the above. This can be done at the beginning of the .cleanup_if() callback. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 4 ++-- drivers/net/wireless/marvell/mwifiex/sdio.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 23e09117bf4a..e381deff37f3 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -294,8 +294,6 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) if (!adapter || !adapter->priv_num) return; - cancel_work_sync(&card->work); - reg = card->pcie.reg; if (reg) ret = mwifiex_read_reg(adapter, reg->fw_status, &fw_status); @@ -2854,6 +2852,8 @@ static void mwifiex_cleanup_pcie(struct mwifiex_adapter *adapter) int ret; u32 fw_status; + cancel_work_sync(&card->work); + ret = mwifiex_read_reg(adapter, reg->fw_status, &fw_status); if (fw_status == FIRMWARE_READY_PCIE) { mwifiex_dbg(adapter, INFO, diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 9f7cabf0236b..58d3da09cfbd 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -387,8 +387,6 @@ mwifiex_sdio_remove(struct sdio_func *func) if (!adapter || !adapter->priv_num) return; - cancel_work_sync(&card->work); - mwifiex_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num); ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); @@ -2158,6 +2156,8 @@ static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; + cancel_work_sync(&card->work); + kfree(card->mp_regs); kfree(card->mpa_rx.skb_arr); kfree(card->mpa_rx.len_arr); -- cgit From 84d26fda52e2eb1ee75dd6a2df9d8e595994b8f8 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 23 Feb 2017 11:19:54 -0600 Subject: rtlwifi: Update 8821ae new phy parameters and its parser. There are new PHY table values for the RTL8821AE. The changes require new parsing code. Signed-off-by: Ping-Ke Shih Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/rtl8821ae/phy.c | 377 +++++++++------------ .../net/wireless/realtek/rtlwifi/rtl8821ae/table.c | 55 +-- .../net/wireless/realtek/rtlwifi/rtl8821ae/table.h | 13 +- drivers/net/wireless/realtek/rtlwifi/wifi.h | 18 + 4 files changed, 214 insertions(+), 249 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c index 8da874cbec1a..04c9c31c9663 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c @@ -660,6 +660,88 @@ void rtl8821ae_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band) return; } +static bool _rtl8821ae_check_positive(struct ieee80211_hw *hw, + const u32 condition1, + const u32 condition2) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + u32 cut_ver = ((rtlhal->version & CHIP_VER_RTL_MASK) + >> CHIP_VER_RTL_SHIFT); + u32 intf = (rtlhal->interface == INTF_USB ? BIT(1) : BIT(0)); + + u8 board_type = ((rtlhal->board_type & BIT(4)) >> 4) << 0 | /* _GLNA */ + ((rtlhal->board_type & BIT(3)) >> 3) << 1 | /* _GPA */ + ((rtlhal->board_type & BIT(7)) >> 7) << 2 | /* _ALNA */ + ((rtlhal->board_type & BIT(6)) >> 6) << 3 | /* _APA */ + ((rtlhal->board_type & BIT(2)) >> 2) << 4; /* _BT */ + + u32 cond1 = condition1, cond2 = condition2; + u32 driver1 = cut_ver << 24 | /* CUT ver */ + 0 << 20 | /* interface 2/2 */ + 0x04 << 16 | /* platform */ + rtlhal->package_type << 12 | + intf << 8 | /* interface 1/2 */ + board_type; + + u32 driver2 = rtlhal->type_glna << 0 | + rtlhal->type_gpa << 8 | + rtlhal->type_alna << 16 | + rtlhal->type_apa << 24; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "===> [8812A] CheckPositive (cond1, cond2) = (0x%X 0x%X)\n", + cond1, cond2); + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "===> [8812A] CheckPositive (driver1, driver2) = (0x%X 0x%X)\n", + driver1, driver2); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + " (Platform, Interface) = (0x%X, 0x%X)\n", 0x04, intf); + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + " (Board, Package) = (0x%X, 0x%X)\n", + rtlhal->board_type, rtlhal->package_type); + + /*============== Value Defined Check ===============*/ + /*QFN Type [15:12] and Cut Version [27:24] need to do value check*/ + + if (((cond1 & 0x0000F000) != 0) && ((cond1 & 0x0000F000) != + (driver1 & 0x0000F000))) + return false; + if (((cond1 & 0x0F000000) != 0) && ((cond1 & 0x0F000000) != + (driver1 & 0x0F000000))) + return false; + + /*=============== Bit Defined Check ================*/ + /* We don't care [31:28] */ + + cond1 &= 0x00FF0FFF; + driver1 &= 0x00FF0FFF; + + if ((cond1 & driver1) == cond1) { + u32 mask = 0; + + if ((cond1 & 0x0F) == 0) /* BoardType is DONTCARE*/ + return true; + + if ((cond1 & BIT(0)) != 0) /*GLNA*/ + mask |= 0x000000FF; + if ((cond1 & BIT(1)) != 0) /*GPA*/ + mask |= 0x0000FF00; + if ((cond1 & BIT(2)) != 0) /*ALNA*/ + mask |= 0x00FF0000; + if ((cond1 & BIT(3)) != 0) /*APA*/ + mask |= 0xFF000000; + + /* BoardType of each RF path is matched*/ + if ((cond2 & mask) == (driver2 & mask)) + return true; + else + return false; + } else + return false; +} + static bool _rtl8821ae_check_condition(struct ieee80211_hw *hw, const u32 condition) { @@ -1695,17 +1777,68 @@ static bool _rtl8821ae_phy_bb8821a_config_parafile(struct ieee80211_hw *hw) return true; } +static bool +__rtl8821ae_phy_config_with_headerfile(struct ieee80211_hw *hw, + u32 *array_table, u16 arraylen, + void (*set_reg)(struct ieee80211_hw *hw, + u32 regaddr, u32 data)) +{ + #define COND_ELSE 2 + #define COND_ENDIF 3 + + int i = 0; + u8 cond; + bool matched = true, skipped = false; + + while ((i + 1) < arraylen) { + u32 v1 = array_table[i]; + u32 v2 = array_table[i + 1]; + + if (v1 & (BIT(31) | BIT(30))) {/*positive & negative condition*/ + if (v1 & BIT(31)) {/* positive condition*/ + cond = (u8)((v1 & (BIT(29) | BIT(28))) >> 28); + if (cond == COND_ENDIF) {/*end*/ + matched = true; + skipped = false; + } else if (cond == COND_ELSE) /*else*/ + matched = skipped ? false : true; + else {/*if , else if*/ + if (skipped) { + matched = false; + } else { + if (_rtl8821ae_check_positive( + hw, v1, v2)) { + matched = true; + skipped = true; + } else { + matched = false; + skipped = false; + } + } + } + } else if (v1 & BIT(30)) { /*negative condition*/ + /*do nothing*/ + } + } else { + if (matched) + set_reg(hw, v1, v2); + } + i = i + 2; + } + + return true; +} + static bool _rtl8821ae_phy_config_mac_with_headerfile(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); - u32 i, v1, v2; u32 arraylength; u32 *ptrarray; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Read MAC_REG_Array\n"); if (rtlhal->hw_type == HARDWARE_TYPE_RTL8821AE) { - arraylength = RTL8821AEMAC_1T_ARRAYLEN; + arraylength = RTL8821AE_MAC_1T_ARRAYLEN; ptrarray = RTL8821AE_MAC_REG_ARRAY; } else { arraylength = RTL8812AEMAC_1T_ARRAYLEN; @@ -1713,37 +1846,9 @@ static bool _rtl8821ae_phy_config_mac_with_headerfile(struct ieee80211_hw *hw) } RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Img: MAC_REG_ARRAY LEN %d\n", arraylength); - for (i = 0; i < arraylength; i += 2) { - v1 = ptrarray[i]; - v2 = (u8)ptrarray[i + 1]; - if (v1 < 0xCDCDCDCD) { - rtl_write_byte(rtlpriv, v1, (u8)v2); - continue; - } else { - if (!_rtl8821ae_check_condition(hw, v1)) { - /*Discard the following (offset, data) pairs*/ - READ_NEXT_PAIR(ptrarray, v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && i < arraylength - 2) { - READ_NEXT_PAIR(ptrarray, v1, v2, i); - } - i -= 2; /* prevent from for-loop += 2*/ - } else {/*Configure matched pairs and skip to end of if-else.*/ - READ_NEXT_PAIR(ptrarray, v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && i < arraylength - 2) { - rtl_write_byte(rtlpriv, v1, v2); - READ_NEXT_PAIR(ptrarray, v1, v2, i); - } - while (v2 != 0xDEAD && i < arraylength - 2) - READ_NEXT_PAIR(ptrarray, v1, v2, i); - } - } - } - return true; + return __rtl8821ae_phy_config_with_headerfile(hw, + ptrarray, arraylength, rtl_write_byte_with_val32); } static bool _rtl8821ae_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, @@ -1751,111 +1856,33 @@ static bool _rtl8821ae_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); - int i; u32 *array_table; u16 arraylen; - u32 v1 = 0, v2 = 0; if (configtype == BASEBAND_CONFIG_PHY_REG) { if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { arraylen = RTL8812AEPHY_REG_1TARRAYLEN; array_table = RTL8812AE_PHY_REG_ARRAY; } else { - arraylen = RTL8821AEPHY_REG_1TARRAYLEN; + arraylen = RTL8821AE_PHY_REG_1TARRAYLEN; array_table = RTL8821AE_PHY_REG_ARRAY; } - for (i = 0; i < arraylen; i += 2) { - v1 = array_table[i]; - v2 = array_table[i + 1]; - if (v1 < 0xCDCDCDCD) { - _rtl8821ae_config_bb_reg(hw, v1, v2); - continue; - } else {/*This line is the start line of branch.*/ - if (!_rtl8821ae_check_condition(hw, v1)) { - /*Discard the following (offset, data) pairs*/ - READ_NEXT_PAIR(array_table, v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && - i < arraylen - 2) { - READ_NEXT_PAIR(array_table, v1, - v2, i); - } - - i -= 2; /* prevent from for-loop += 2*/ - } else {/*Configure matched pairs and skip to end of if-else.*/ - READ_NEXT_PAIR(array_table, v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && - i < arraylen - 2) { - _rtl8821ae_config_bb_reg(hw, v1, - v2); - READ_NEXT_PAIR(array_table, v1, - v2, i); - } - - while (v2 != 0xDEAD && - i < arraylen - 2) { - READ_NEXT_PAIR(array_table, v1, - v2, i); - } - } - } - } + return __rtl8821ae_phy_config_with_headerfile(hw, + array_table, arraylen, + _rtl8821ae_config_bb_reg); } else if (configtype == BASEBAND_CONFIG_AGC_TAB) { if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { arraylen = RTL8812AEAGCTAB_1TARRAYLEN; array_table = RTL8812AE_AGC_TAB_ARRAY; } else { - arraylen = RTL8821AEAGCTAB_1TARRAYLEN; + arraylen = RTL8821AE_AGC_TAB_1TARRAYLEN; array_table = RTL8821AE_AGC_TAB_ARRAY; } - for (i = 0; i < arraylen; i = i + 2) { - v1 = array_table[i]; - v2 = array_table[i+1]; - if (v1 < 0xCDCDCDCD) { - rtl_set_bbreg(hw, v1, MASKDWORD, v2); - udelay(1); - continue; - } else {/*This line is the start line of branch.*/ - if (!_rtl8821ae_check_condition(hw, v1)) { - /*Discard the following (offset, data) pairs*/ - READ_NEXT_PAIR(array_table, v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && - i < arraylen - 2) { - READ_NEXT_PAIR(array_table, v1, - v2, i); - } - i -= 2; /* prevent from for-loop += 2*/ - } else {/*Configure matched pairs and skip to end of if-else.*/ - READ_NEXT_PAIR(array_table, v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && - i < arraylen - 2) { - rtl_set_bbreg(hw, v1, MASKDWORD, - v2); - udelay(1); - READ_NEXT_PAIR(array_table, v1, - v2, i); - } - - while (v2 != 0xDEAD && - i < arraylen - 2) { - READ_NEXT_PAIR(array_table, v1, - v2, i); - } - } - RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, - "The agctab_array_table[0] is %x Rtl818EEPHY_REGArray[1] is %x\n", - array_table[i], array_table[i + 1]); - } - } + return __rtl8821ae_phy_config_with_headerfile(hw, + array_table, arraylen, + rtl_set_bbreg_with_dwmask); } return true; } @@ -1916,7 +1943,7 @@ static bool _rtl8821ae_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, arraylen = RTL8812AEPHY_REG_ARRAY_PGLEN; array = RTL8812AE_PHY_REG_ARRAY_PG; } else { - arraylen = RTL8821AEPHY_REG_ARRAY_PGLEN; + arraylen = RTL8821AE_PHY_REG_ARRAY_PGLEN; array = RTL8821AE_PHY_REG_ARRAY_PG; } @@ -1980,12 +2007,10 @@ static bool _rtl8821ae_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, bool rtl8812ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath) { - int i; bool rtstatus = true; u32 *radioa_array_table_a, *radioa_array_table_b; u16 radioa_arraylen_a, radioa_arraylen_b; struct rtl_priv *rtlpriv = rtl_priv(hw); - u32 v1 = 0, v2 = 0; radioa_arraylen_a = RTL8812AE_RADIOA_1TARRAYLEN; radioa_array_table_a = RTL8812AE_RADIOA_ARRAY; @@ -1997,69 +2022,14 @@ bool rtl8812ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, rtstatus = true; switch (rfpath) { case RF90_PATH_A: - for (i = 0; i < radioa_arraylen_a; i = i + 2) { - v1 = radioa_array_table_a[i]; - v2 = radioa_array_table_a[i+1]; - if (v1 < 0xcdcdcdcd) { - _rtl8821ae_config_rf_radio_a(hw, v1, v2); - continue; - } else{/*This line is the start line of branch.*/ - if (!_rtl8821ae_check_condition(hw, v1)) { - /*Discard the following (offset, data) pairs*/ - READ_NEXT_PAIR(radioa_array_table_a, v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && i < radioa_arraylen_a-2) - READ_NEXT_PAIR(radioa_array_table_a, v1, v2, i); - - i -= 2; /* prevent from for-loop += 2*/ - } else {/*Configure matched pairs and skip to end of if-else.*/ - READ_NEXT_PAIR(radioa_array_table_a, v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && i < radioa_arraylen_a - 2) { - _rtl8821ae_config_rf_radio_a(hw, v1, v2); - READ_NEXT_PAIR(radioa_array_table_a, v1, v2, i); - } - - while (v2 != 0xDEAD && i < radioa_arraylen_a-2) - READ_NEXT_PAIR(radioa_array_table_a, v1, v2, i); - - } - } - } + return __rtl8821ae_phy_config_with_headerfile(hw, + radioa_array_table_a, radioa_arraylen_a, + _rtl8821ae_config_rf_radio_a); break; case RF90_PATH_B: - for (i = 0; i < radioa_arraylen_b; i = i + 2) { - v1 = radioa_array_table_b[i]; - v2 = radioa_array_table_b[i+1]; - if (v1 < 0xcdcdcdcd) { - _rtl8821ae_config_rf_radio_b(hw, v1, v2); - continue; - } else{/*This line is the start line of branch.*/ - if (!_rtl8821ae_check_condition(hw, v1)) { - /*Discard the following (offset, data) pairs*/ - READ_NEXT_PAIR(radioa_array_table_b, v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && i < radioa_arraylen_b-2) - READ_NEXT_PAIR(radioa_array_table_b, v1, v2, i); - - i -= 2; /* prevent from for-loop += 2*/ - } else {/*Configure matched pairs and skip to end of if-else.*/ - READ_NEXT_PAIR(radioa_array_table_b, v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && i < radioa_arraylen_b-2) { - _rtl8821ae_config_rf_radio_b(hw, v1, v2); - READ_NEXT_PAIR(radioa_array_table_b, v1, v2, i); - } - - while (v2 != 0xDEAD && i < radioa_arraylen_b-2) - READ_NEXT_PAIR(radioa_array_table_b, v1, v2, i); - } - } - } + return __rtl8821ae_phy_config_with_headerfile(hw, + radioa_array_table_b, radioa_arraylen_b, + _rtl8821ae_config_rf_radio_b); break; case RF90_PATH_C: case RF90_PATH_D: @@ -2072,21 +2042,10 @@ bool rtl8812ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, bool rtl8821ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath) { - #define READ_NEXT_RF_PAIR(v1, v2, i) \ - do { \ - i += 2; \ - v1 = radioa_array_table[i]; \ - v2 = radioa_array_table[i+1]; \ - } \ - while (0) - - int i; bool rtstatus = true; u32 *radioa_array_table; u16 radioa_arraylen; struct rtl_priv *rtlpriv = rtl_priv(hw); - /* struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); */ - u32 v1 = 0, v2 = 0; radioa_arraylen = RTL8821AE_RADIOA_1TARRAYLEN; radioa_array_table = RTL8821AE_RADIOA_ARRAY; @@ -2096,35 +2055,9 @@ bool rtl8821ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, rtstatus = true; switch (rfpath) { case RF90_PATH_A: - for (i = 0; i < radioa_arraylen; i = i + 2) { - v1 = radioa_array_table[i]; - v2 = radioa_array_table[i+1]; - if (v1 < 0xcdcdcdcd) - _rtl8821ae_config_rf_radio_a(hw, v1, v2); - else{/*This line is the start line of branch.*/ - if (!_rtl8821ae_check_condition(hw, v1)) { - /*Discard the following (offset, data) pairs*/ - READ_NEXT_RF_PAIR(v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && i < radioa_arraylen - 2) - READ_NEXT_RF_PAIR(v1, v2, i); - - i -= 2; /* prevent from for-loop += 2*/ - } else {/*Configure matched pairs and skip to end of if-else.*/ - READ_NEXT_RF_PAIR(v1, v2, i); - while (v2 != 0xDEAD && - v2 != 0xCDEF && - v2 != 0xCDCD && i < radioa_arraylen - 2) { - _rtl8821ae_config_rf_radio_a(hw, v1, v2); - READ_NEXT_RF_PAIR(v1, v2, i); - } - - while (v2 != 0xDEAD && i < radioa_arraylen - 2) - READ_NEXT_RF_PAIR(v1, v2, i); - } - } - } + return __rtl8821ae_phy_config_with_headerfile(hw, + radioa_array_table, radioa_arraylen, + _rtl8821ae_config_rf_radio_a); break; case RF90_PATH_B: diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.c index 62a0fb76f080..64e69e534662 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.c @@ -449,6 +449,9 @@ u32 RTL8821AE_PHY_REG_ARRAY[] = { 0xCB8, 0x00508240, }; +u32 RTL8821AE_PHY_REG_1TARRAYLEN = + sizeof(RTL8821AE_PHY_REG_ARRAY) / sizeof(u32); + u32 RTL8812AE_PHY_REG_ARRAY_PG[] = { 0, 0, 0, 0x00000c20, 0xffffffff, 0x34363840, 0, 0, 0, 0x00000c24, 0xffffffff, 0x42424444, @@ -516,6 +519,9 @@ u32 RTL8821AE_PHY_REG_ARRAY_PG[] = { 1, 0, 0, 0x00000c44, 0x0000ffff, 0x00002022 }; +u32 RTL8821AE_PHY_REG_ARRAY_PGLEN = + sizeof(RTL8821AE_PHY_REG_ARRAY_PG) / sizeof(u32); + u32 RTL8812AE_RADIOA_ARRAY[] = { 0x000, 0x00010000, 0x018, 0x0001712A, @@ -2285,16 +2291,16 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { 0x0EF, 0x00000000, 0x0EF, 0x00000100, 0x034, 0x0000ADF3, - 0x034, 0x00009DEF, - 0x034, 0x00008DEC, - 0x034, 0x00007DE9, - 0x034, 0x00006CED, - 0x034, 0x00005CE9, - 0x034, 0x000044E9, - 0x034, 0x000034E6, - 0x034, 0x0000246A, - 0x034, 0x00001467, - 0x034, 0x00000068, + 0x034, 0x00009DF0, + 0x034, 0x00008D70, + 0x034, 0x00007D6D, + 0x034, 0x00006CEE, + 0x034, 0x00005CCC, + 0x034, 0x000044EC, + 0x034, 0x000034AC, + 0x034, 0x0000246D, + 0x034, 0x0000106F, + 0x034, 0x0000006C, 0x0EF, 0x00000000, 0x0ED, 0x00000010, 0x044, 0x0000ADF2, @@ -2365,8 +2371,11 @@ u32 RTL8821AE_RADIOA_ARRAY[] = { 0x0FE, 0x00000000, 0x0FE, 0x00000000, 0x018, 0x0001712A, + }; +u32 RTL8821AE_RADIOA_1TARRAYLEN = sizeof(RTL8821AE_RADIOA_ARRAY) / sizeof(u32); + u32 RTL8812AE_MAC_REG_ARRAY[] = { 0x010, 0x0000000C, 0xFF0F0180, 0xABCD, @@ -2578,6 +2587,8 @@ u32 RTL8821AE_MAC_REG_ARRAY[] = { 0x718, 0x00000040, }; +u32 RTL8821AE_MAC_1T_ARRAYLEN = sizeof(RTL8821AE_MAC_REG_ARRAY) / sizeof(u32); + u32 RTL8812AE_AGC_TAB_ARRAY[] = { 0xFF0F07D8, 0xABCD, 0x81C, 0xFC000001, @@ -3430,9 +3441,11 @@ u32 RTL8821AE_AGC_TAB_ARRAY[] = { 0x81C, 0x017E0101, 0xC50, 0x00000022, 0xC50, 0x00000020, - }; +u32 RTL8821AE_AGC_TAB_1TARRAYLEN = + sizeof(RTL8821AE_AGC_TAB_ARRAY) / sizeof(u32); + /****************************************************************************** * TXPWR_LMT.TXT ******************************************************************************/ @@ -4284,9 +4297,9 @@ u8 *RTL8821AE_TXPWR_LMT[] = { "FCC", "5G", "20M", "OFDM", "1T", "100", "32", "ETSI", "5G", "20M", "OFDM", "1T", "100", "30", "MKK", "5G", "20M", "OFDM", "1T", "100", "30", - "FCC", "5G", "20M", "OFDM", "1T", "114", "32", - "ETSI", "5G", "20M", "OFDM", "1T", "114", "30", - "MKK", "5G", "20M", "OFDM", "1T", "114", "30", + "FCC", "5G", "20M", "OFDM", "1T", "104", "32", + "ETSI", "5G", "20M", "OFDM", "1T", "104", "30", + "MKK", "5G", "20M", "OFDM", "1T", "104", "30", "FCC", "5G", "20M", "OFDM", "1T", "108", "32", "ETSI", "5G", "20M", "OFDM", "1T", "108", "30", "MKK", "5G", "20M", "OFDM", "1T", "108", "30", @@ -4356,9 +4369,9 @@ u8 *RTL8821AE_TXPWR_LMT[] = { "FCC", "5G", "20M", "HT", "1T", "100", "32", "ETSI", "5G", "20M", "HT", "1T", "100", "30", "MKK", "5G", "20M", "HT", "1T", "100", "30", - "FCC", "5G", "20M", "HT", "1T", "114", "32", - "ETSI", "5G", "20M", "HT", "1T", "114", "30", - "MKK", "5G", "20M", "HT", "1T", "114", "30", + "FCC", "5G", "20M", "HT", "1T", "104", "32", + "ETSI", "5G", "20M", "HT", "1T", "104", "30", + "MKK", "5G", "20M", "HT", "1T", "104", "30", "FCC", "5G", "20M", "HT", "1T", "108", "32", "ETSI", "5G", "20M", "HT", "1T", "108", "30", "MKK", "5G", "20M", "HT", "1T", "108", "30", @@ -4428,9 +4441,9 @@ u8 *RTL8821AE_TXPWR_LMT[] = { "FCC", "5G", "20M", "HT", "2T", "100", "28", "ETSI", "5G", "20M", "HT", "2T", "100", "30", "MKK", "5G", "20M", "HT", "2T", "100", "30", - "FCC", "5G", "20M", "HT", "2T", "114", "28", - "ETSI", "5G", "20M", "HT", "2T", "114", "30", - "MKK", "5G", "20M", "HT", "2T", "114", "30", + "FCC", "5G", "20M", "HT", "2T", "104", "28", + "ETSI", "5G", "20M", "HT", "2T", "104", "30", + "MKK", "5G", "20M", "HT", "2T", "104", "30", "FCC", "5G", "20M", "HT", "2T", "108", "30", "ETSI", "5G", "20M", "HT", "2T", "108", "30", "MKK", "5G", "20M", "HT", "2T", "108", "30", @@ -4570,3 +4583,5 @@ u8 *RTL8821AE_TXPWR_LMT[] = { "ETSI", "5G", "80M", "VHT", "2T", "155", "30", "MKK", "5G", "80M", "VHT", "2T", "155", "63" }; + +u32 RTL8821AE_TXPWR_LMT_ARRAY_LEN = sizeof(RTL8821AE_TXPWR_LMT) / sizeof(u8 *); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.h index 24bcff6bc507..192057af75dc 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.h @@ -29,32 +29,31 @@ #define __RTL8821AE_TABLE__H_ #include -#define RTL8821AEPHY_REG_1TARRAYLEN 344 +extern u32 RTL8821AE_PHY_REG_1TARRAYLEN; extern u32 RTL8821AE_PHY_REG_ARRAY[]; #define RTL8812AEPHY_REG_1TARRAYLEN 490 extern u32 RTL8812AE_PHY_REG_ARRAY[]; -#define RTL8821AEPHY_REG_ARRAY_PGLEN 90 +extern u32 RTL8821AE_PHY_REG_ARRAY_PGLEN; extern u32 RTL8821AE_PHY_REG_ARRAY_PG[]; #define RTL8812AEPHY_REG_ARRAY_PGLEN 276 extern u32 RTL8812AE_PHY_REG_ARRAY_PG[]; /* #define RTL8723BE_RADIOA_1TARRAYLEN 206 */ -/* extern u8 *RTL8821AE_TXPWR_LMT_ARRAY[]; */ #define RTL8812AE_RADIOA_1TARRAYLEN 1264 extern u32 RTL8812AE_RADIOA_ARRAY[]; #define RTL8812AE_RADIOB_1TARRAYLEN 1240 extern u32 RTL8812AE_RADIOB_ARRAY[]; -#define RTL8821AE_RADIOA_1TARRAYLEN 1176 +extern u32 RTL8821AE_RADIOA_1TARRAYLEN; extern u32 RTL8821AE_RADIOA_ARRAY[]; -#define RTL8821AEMAC_1T_ARRAYLEN 194 +extern u32 RTL8821AE_MAC_1T_ARRAYLEN; extern u32 RTL8821AE_MAC_REG_ARRAY[]; #define RTL8812AEMAC_1T_ARRAYLEN 214 extern u32 RTL8812AE_MAC_REG_ARRAY[]; -#define RTL8821AEAGCTAB_1TARRAYLEN 382 +extern u32 RTL8821AE_AGC_TAB_1TARRAYLEN; extern u32 RTL8821AE_AGC_TAB_ARRAY[]; #define RTL8812AEAGCTAB_1TARRAYLEN 1312 extern u32 RTL8812AE_AGC_TAB_ARRAY[]; #define RTL8812AE_TXPWR_LMT_ARRAY_LEN 3948 extern u8 *RTL8812AE_TXPWR_LMT[]; -#define RTL8821AE_TXPWR_LMT_ARRAY_LEN 3948 +extern u32 RTL8821AE_TXPWR_LMT_ARRAY_LEN; extern u8 *RTL8821AE_TXPWR_LMT[]; #endif diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h index 65ef42b37651..c0d2601bc55f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/wifi.h +++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h @@ -1529,6 +1529,10 @@ struct rtl_hal { u8 external_lna_2g; u8 external_pa_5g; u8 external_lna_5g; + u8 type_glna; + u8 type_gpa; + u8 type_alna; + u8 type_apa; u8 rfe_type; /*firmware */ @@ -2933,6 +2937,14 @@ static inline void rtl_write_byte(struct rtl_priv *rtlpriv, u32 addr, u8 val8) rtlpriv->io.read8_sync(rtlpriv, addr); } +static inline void rtl_write_byte_with_val32(struct ieee80211_hw *hw, + u32 addr, u32 val8) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtl_write_byte(rtlpriv, addr, (u8)val8); +} + static inline void rtl_write_word(struct rtl_priv *rtlpriv, u32 addr, u16 val16) { rtlpriv->io.write16_async(rtlpriv, addr, val16); @@ -2966,6 +2978,12 @@ static inline void rtl_set_bbreg(struct ieee80211_hw *hw, u32 regaddr, rtlpriv->cfg->ops->set_bbreg(hw, regaddr, bitmask, data); } +static inline void rtl_set_bbreg_with_dwmask(struct ieee80211_hw *hw, + u32 regaddr, u32 data) +{ + rtl_set_bbreg(hw, regaddr, 0xffffffff, data); +} + static inline u32 rtl_get_rfreg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask) -- cgit From e6042859302eb43ff788df909626090d23ab416e Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 23 Feb 2017 11:19:55 -0600 Subject: rtlwifi: Update 8812ae new phy parameters and its parser. Update PHY tables for the RTL8812AE. A new parser is also needed. Signed-off-by: Ping-Ke Shih Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/rtl8821ae/phy.c | 8 +- .../net/wireless/realtek/rtlwifi/rtl8821ae/table.c | 1805 ++++++-------------- .../net/wireless/realtek/rtlwifi/rtl8821ae/table.h | 15 +- 3 files changed, 531 insertions(+), 1297 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c index 04c9c31c9663..94a5e587a1cd 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c @@ -1841,7 +1841,7 @@ static bool _rtl8821ae_phy_config_mac_with_headerfile(struct ieee80211_hw *hw) arraylength = RTL8821AE_MAC_1T_ARRAYLEN; ptrarray = RTL8821AE_MAC_REG_ARRAY; } else { - arraylength = RTL8812AEMAC_1T_ARRAYLEN; + arraylength = RTL8812AE_MAC_1T_ARRAYLEN; ptrarray = RTL8812AE_MAC_REG_ARRAY; } RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, @@ -1861,7 +1861,7 @@ static bool _rtl8821ae_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, if (configtype == BASEBAND_CONFIG_PHY_REG) { if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { - arraylen = RTL8812AEPHY_REG_1TARRAYLEN; + arraylen = RTL8812AE_PHY_REG_1TARRAYLEN; array_table = RTL8812AE_PHY_REG_ARRAY; } else { arraylen = RTL8821AE_PHY_REG_1TARRAYLEN; @@ -1873,7 +1873,7 @@ static bool _rtl8821ae_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, _rtl8821ae_config_bb_reg); } else if (configtype == BASEBAND_CONFIG_AGC_TAB) { if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { - arraylen = RTL8812AEAGCTAB_1TARRAYLEN; + arraylen = RTL8812AE_AGC_TAB_1TARRAYLEN; array_table = RTL8812AE_AGC_TAB_ARRAY; } else { arraylen = RTL8821AE_AGC_TAB_1TARRAYLEN; @@ -1940,7 +1940,7 @@ static bool _rtl8821ae_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, u32 v1, v2, v3, v4, v5, v6; if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { - arraylen = RTL8812AEPHY_REG_ARRAY_PGLEN; + arraylen = RTL8812AE_PHY_REG_ARRAY_PGLEN; array = RTL8812AE_PHY_REG_ARRAY_PG; } else { arraylen = RTL8821AE_PHY_REG_ARRAY_PGLEN; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.c index 64e69e534662..408c4611e5de 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.c @@ -38,7 +38,7 @@ u32 RTL8812AE_PHY_REG_ARRAY[] = { 0x824, 0x00030FE0, 0x828, 0x00000000, 0x82C, 0x002083DD, - 0x830, 0x2AAA6C86, + 0x830, 0x2EAAEEB8, 0x834, 0x0037A706, 0x838, 0x06C89B44, 0x83C, 0x0000095B, @@ -68,7 +68,7 @@ u32 RTL8812AE_PHY_REG_ARRAY[] = { 0x8BC, 0x4CA520A3, 0x8C0, 0x27F00020, 0x8C4, 0x00000000, - 0x8C8, 0x00013169, + 0x8C8, 0x00012D69, 0x8CC, 0x08248492, 0x8D0, 0x0000B800, 0x8DC, 0x00000000, @@ -76,13 +76,7 @@ u32 RTL8812AE_PHY_REG_ARRAY[] = { 0x8D8, 0x290B5612, 0x8F8, 0x400002C0, 0x8FC, 0x00000000, - 0xFF0F07D8, 0xABCD, 0x900, 0x00000701, - 0xFF0F07D0, 0xCDEF, - 0x900, 0x00000701, - 0xCDCDCDCD, 0xCDCD, - 0x900, 0x00000700, - 0xFF0F07D8, 0xDEAD, 0x90C, 0x00000000, 0x910, 0x0000FC00, 0x914, 0x00000404, @@ -120,7 +114,7 @@ u32 RTL8812AE_PHY_REG_ARRAY[] = { 0x9D4, 0x00000000, 0x9D8, 0x00000000, 0x9DC, 0x00000000, - 0x9E4, 0x00000002, + 0x9E4, 0x00000003, 0x9E8, 0x000002D5, 0xA00, 0x00D047C8, 0xA04, 0x01FF000C, @@ -189,7 +183,21 @@ u32 RTL8812AE_PHY_REG_ARRAY[] = { 0xC5C, 0x00000058, 0xC60, 0x34344443, 0xC64, 0x07003333, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0xC68, 0x59791979, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, + 0xC68, 0x59791979, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, 0xC68, 0x59791979, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0xC68, 0x59791979, + 0x90000001, 0x00000000, 0x40000000, 0x00000000, + 0xC68, 0x59791979, + 0x90000001, 0x00000005, 0x40000000, 0x00000000, + 0xC68, 0x59791979, + 0xA0000000, 0x00000000, + 0xC68, 0x59799979, + 0xB0000000, 0x00000000, 0xC6C, 0x59795979, 0xC70, 0x19795979, 0xC74, 0x19795979, @@ -203,19 +211,7 @@ u32 RTL8812AE_PHY_REG_ARRAY[] = { 0xCA0, 0x00000029, 0xCA4, 0x08040201, 0xCA8, 0x80402010, - 0xFF0F0740, 0xABCD, - 0xCB0, 0x77547717, - 0xFF0F01C0, 0xCDEF, - 0xCB0, 0x77547717, - 0xFF0F02C0, 0xCDEF, - 0xCB0, 0x77547717, - 0xFF0F07D8, 0xCDEF, - 0xCB0, 0x54547710, - 0xFF0F07D0, 0xCDEF, - 0xCB0, 0x54547710, - 0xCDCDCDCD, 0xCDCD, 0xCB0, 0x77547777, - 0xFF0F0740, 0xDEAD, 0xCB4, 0x00000077, 0xCB8, 0x00508242, 0xE00, 0x00000007, @@ -257,23 +253,14 @@ u32 RTL8812AE_PHY_REG_ARRAY[] = { 0xEA0, 0x00000029, 0xEA4, 0x08040201, 0xEA8, 0x80402010, - 0xFF0F0740, 0xABCD, - 0xEB0, 0x77547717, - 0xFF0F01C0, 0xCDEF, - 0xEB0, 0x77547717, - 0xFF0F02C0, 0xCDEF, - 0xEB0, 0x77547717, - 0xFF0F07D8, 0xCDEF, - 0xEB0, 0x54547710, - 0xFF0F07D0, 0xCDEF, - 0xEB0, 0x54547710, - 0xCDCDCDCD, 0xCDCD, 0xEB0, 0x77547777, - 0xFF0F0740, 0xDEAD, 0xEB4, 0x00000077, 0xEB8, 0x00508242, }; +u32 RTL8812AE_PHY_REG_1TARRAYLEN = + sizeof(RTL8812AE_PHY_REG_ARRAY) / sizeof(u32); + u32 RTL8821AE_PHY_REG_ARRAY[] = { 0x800, 0x0020D090, 0x804, 0x080112E0, @@ -501,6 +488,9 @@ u32 RTL8812AE_PHY_REG_ARRAY_PG[] = { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628 }; +u32 RTL8812AE_PHY_REG_ARRAY_PGLEN = + sizeof(RTL8812AE_PHY_REG_ARRAY_PG) / sizeof(u32); + u32 RTL8821AE_PHY_REG_ARRAY_PG[] = { 0, 0, 0, 0x00000c20, 0xffffffff, 0x32343638, 0, 0, 0, 0x00000c24, 0xffffffff, 0x36363838, @@ -529,26 +519,25 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x066, 0x00040000, 0x01E, 0x00080000, 0x089, 0x00000080, - 0xFF0F0740, 0xABCD, - 0x086, 0x00014B38, - 0xFF0F02C0, 0xCDEF, - 0x086, 0x00014B38, - 0xFF0F01C0, 0xCDEF, - 0x086, 0x00014B38, - 0xFF0F07D8, 0xCDEF, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, 0x086, 0x00014B3A, - 0xFF0F07D0, 0xCDEF, + 0x90000001, 0x00000005, 0x40000000, 0x00000000, 0x086, 0x00014B3A, - 0xCDCDCDCD, 0xCDCD, + 0xA0000000, 0x00000000, 0x086, 0x00014B38, - 0xFF0F0740, 0xDEAD, + 0xB0000000, 0x00000000, + 0x80000004, 0x00000000, 0x40000000, 0x00000000, + 0x08B, 0x00080180, + 0xA0000000, 0x00000000, + 0x08B, 0x00087180, + 0xB0000000, 0x00000000, 0x0B1, 0x0001FC1A, 0x0B3, 0x000F0810, 0x0B4, 0x0001A78D, 0x0BA, 0x00086180, 0x018, 0x00000006, 0x0EF, 0x00002000, - 0xFF0F07D8, 0xABCD, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, 0x03B, 0x0003F218, 0x03B, 0x00030A58, 0x03B, 0x0002FA58, @@ -556,7 +545,7 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x03B, 0x0001FA50, 0x03B, 0x00010248, 0x03B, 0x00008240, - 0xFF0F07D0, 0xCDEF, + 0x90000001, 0x00000005, 0x40000000, 0x00000000, 0x03B, 0x0003F218, 0x03B, 0x00030A58, 0x03B, 0x0002FA58, @@ -564,7 +553,7 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x03B, 0x0001FA50, 0x03B, 0x00010248, 0x03B, 0x00008240, - 0xCDCDCDCD, 0xCDCD, + 0xA0000000, 0x00000000, 0x03B, 0x00038A58, 0x03B, 0x00037A58, 0x03B, 0x0002A590, @@ -572,9 +561,9 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x03B, 0x00018248, 0x03B, 0x00010240, 0x03B, 0x00008240, - 0xFF0F07D8, 0xDEAD, + 0xB0000000, 0x00000000, 0x0EF, 0x00000100, - 0xFF0F07D8, 0xABCD, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, 0x034, 0x0000A4EE, 0x034, 0x00009076, 0x034, 0x00008073, @@ -586,7 +575,7 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x034, 0x00002028, 0x034, 0x00001025, 0x034, 0x00000022, - 0xCDCDCDCD, 0xCDCD, + 0xA0000000, 0x00000000, 0x034, 0x0000ADF4, 0x034, 0x00009DF1, 0x034, 0x00008DEE, @@ -598,7 +587,7 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x034, 0x000024E7, 0x034, 0x0000146B, 0x034, 0x0000006D, - 0xFF0F07D8, 0xDEAD, + 0xB0000000, 0x00000000, 0x0EF, 0x00000000, 0x0EF, 0x000020A2, 0x0DF, 0x00000080, @@ -652,7 +641,7 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x03B, 0x0006B064, 0x03C, 0x00004000, 0x03A, 0x000000D8, - 0x03B, 0x00023070, + 0x03B, 0x00063070, 0x03C, 0x00004000, 0x03A, 0x00000468, 0x03B, 0x0005B870, @@ -691,43 +680,7 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x03B, 0x00082080, 0x03C, 0x00010000, 0x0EF, 0x00001100, - 0xFF0F0740, 0xABCD, - 0x034, 0x0004A0B2, - 0x034, 0x000490AF, - 0x034, 0x00048070, - 0x034, 0x0004706D, - 0x034, 0x00046050, - 0x034, 0x0004504D, - 0x034, 0x0004404A, - 0x034, 0x00043047, - 0x034, 0x0004200A, - 0x034, 0x00041007, - 0x034, 0x00040004, - 0xFF0F02C0, 0xCDEF, - 0x034, 0x0004A0B2, - 0x034, 0x000490AF, - 0x034, 0x00048070, - 0x034, 0x0004706D, - 0x034, 0x00046050, - 0x034, 0x0004504D, - 0x034, 0x0004404A, - 0x034, 0x00043047, - 0x034, 0x0004200A, - 0x034, 0x00041007, - 0x034, 0x00040004, - 0xFF0F01C0, 0xCDEF, - 0x034, 0x0004A0B2, - 0x034, 0x000490AF, - 0x034, 0x00048070, - 0x034, 0x0004706D, - 0x034, 0x00046050, - 0x034, 0x0004504D, - 0x034, 0x0004404A, - 0x034, 0x00043047, - 0x034, 0x0004200A, - 0x034, 0x00041007, - 0x034, 0x00040004, - 0xFF0F07D8, 0xCDEF, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x034, 0x0004A0B2, 0x034, 0x000490AF, 0x034, 0x00048070, @@ -739,80 +692,32 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x034, 0x0004200A, 0x034, 0x00041007, 0x034, 0x00040004, - 0xFF0F07D0, 0xCDEF, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, 0x034, 0x0004A0B2, 0x034, 0x000490AF, 0x034, 0x00048070, 0x034, 0x0004706D, - 0x034, 0x00046050, - 0x034, 0x0004504D, - 0x034, 0x0004404A, - 0x034, 0x00043047, - 0x034, 0x0004200A, - 0x034, 0x00041007, - 0x034, 0x00040004, - 0xCDCDCDCD, 0xCDCD, + 0x034, 0x0004604D, + 0x034, 0x0004504A, + 0x034, 0x00044047, + 0x034, 0x00043044, + 0x034, 0x00042007, + 0x034, 0x00041004, + 0x034, 0x00040001, + 0xA0000000, 0x00000000, 0x034, 0x0004ADF5, 0x034, 0x00049DF2, 0x034, 0x00048DEF, 0x034, 0x00047DEC, 0x034, 0x00046DE9, - 0x034, 0x00045DC9, - 0x034, 0x00044CE8, - 0x034, 0x000438CA, - 0x034, 0x00042889, - 0x034, 0x0004184A, - 0x034, 0x0004044A, - 0xFF0F0740, 0xDEAD, - 0xFF0F0740, 0xABCD, - 0x034, 0x0002A0B2, - 0x034, 0x000290AF, - 0x034, 0x00028070, - 0x034, 0x0002706D, - 0x034, 0x00026050, - 0x034, 0x0002504D, - 0x034, 0x0002404A, - 0x034, 0x00023047, - 0x034, 0x0002200A, - 0x034, 0x00021007, - 0x034, 0x00020004, - 0xFF0F02C0, 0xCDEF, - 0x034, 0x0002A0B2, - 0x034, 0x000290AF, - 0x034, 0x00028070, - 0x034, 0x0002706D, - 0x034, 0x00026050, - 0x034, 0x0002504D, - 0x034, 0x0002404A, - 0x034, 0x00023047, - 0x034, 0x0002200A, - 0x034, 0x00021007, - 0x034, 0x00020004, - 0xFF0F01C0, 0xCDEF, - 0x034, 0x0002A0B2, - 0x034, 0x000290AF, - 0x034, 0x00028070, - 0x034, 0x0002706D, - 0x034, 0x00026050, - 0x034, 0x0002504D, - 0x034, 0x0002404A, - 0x034, 0x00023047, - 0x034, 0x0002200A, - 0x034, 0x00021007, - 0x034, 0x00020004, - 0xFF0F07D8, 0xCDEF, - 0x034, 0x0002A0B2, - 0x034, 0x000290AF, - 0x034, 0x00028070, - 0x034, 0x0002706D, - 0x034, 0x00026050, - 0x034, 0x0002504D, - 0x034, 0x0002404A, - 0x034, 0x00023047, - 0x034, 0x0002200A, - 0x034, 0x00021007, - 0x034, 0x00020004, - 0xFF0F07D0, 0xCDEF, + 0x034, 0x00045DE6, + 0x034, 0x00044DE3, + 0x034, 0x000438C8, + 0x034, 0x000428C5, + 0x034, 0x000418C2, + 0x034, 0x000408C0, + 0xB0000000, 0x00000000, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x034, 0x0002A0B2, 0x034, 0x000290AF, 0x034, 0x00028070, @@ -824,56 +729,32 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x034, 0x0002200A, 0x034, 0x00021007, 0x034, 0x00020004, - 0xCDCDCDCD, 0xCDCD, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, + 0x034, 0x0002A0B4, + 0x034, 0x000290B1, + 0x034, 0x00028072, + 0x034, 0x0002706F, + 0x034, 0x0002604F, + 0x034, 0x0002504C, + 0x034, 0x00024049, + 0x034, 0x00023046, + 0x034, 0x00022009, + 0x034, 0x00021006, + 0x034, 0x00020003, + 0xA0000000, 0x00000000, 0x034, 0x0002ADF5, 0x034, 0x00029DF2, 0x034, 0x00028DEF, 0x034, 0x00027DEC, 0x034, 0x00026DE9, - 0x034, 0x00025DC9, - 0x034, 0x00024CE8, - 0x034, 0x000238CA, - 0x034, 0x00022889, - 0x034, 0x0002184A, - 0x034, 0x0002044A, - 0xFF0F0740, 0xDEAD, - 0xFF0F0740, 0xABCD, - 0x034, 0x0000A0B2, - 0x034, 0x000090AF, - 0x034, 0x00008070, - 0x034, 0x0000706D, - 0x034, 0x00006050, - 0x034, 0x0000504D, - 0x034, 0x0000404A, - 0x034, 0x00003047, - 0x034, 0x0000200A, - 0x034, 0x00001007, - 0x034, 0x00000004, - 0xFF0F02C0, 0xCDEF, - 0x034, 0x0000A0B2, - 0x034, 0x000090AF, - 0x034, 0x00008070, - 0x034, 0x0000706D, - 0x034, 0x00006050, - 0x034, 0x0000504D, - 0x034, 0x0000404A, - 0x034, 0x00003047, - 0x034, 0x0000200A, - 0x034, 0x00001007, - 0x034, 0x00000004, - 0xFF0F01C0, 0xCDEF, - 0x034, 0x0000A0B2, - 0x034, 0x000090AF, - 0x034, 0x00008070, - 0x034, 0x0000706D, - 0x034, 0x00006050, - 0x034, 0x0000504D, - 0x034, 0x0000404A, - 0x034, 0x00003047, - 0x034, 0x0000200A, - 0x034, 0x00001007, - 0x034, 0x00000004, - 0xFF0F07D8, 0xCDEF, + 0x034, 0x00025DE6, + 0x034, 0x00024DE3, + 0x034, 0x000238C8, + 0x034, 0x000228C5, + 0x034, 0x000218C2, + 0x034, 0x000208C0, + 0xB0000000, 0x00000000, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x034, 0x0000A0B2, 0x034, 0x000090AF, 0x034, 0x00008070, @@ -885,69 +766,33 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x034, 0x0000200A, 0x034, 0x00001007, 0x034, 0x00000004, - 0xFF0F07D0, 0xCDEF, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, 0x034, 0x0000A0B2, 0x034, 0x000090AF, 0x034, 0x00008070, 0x034, 0x0000706D, - 0x034, 0x00006050, - 0x034, 0x0000504D, - 0x034, 0x0000404A, - 0x034, 0x00003047, - 0x034, 0x0000200A, - 0x034, 0x00001007, - 0x034, 0x00000004, - 0xCDCDCDCD, 0xCDCD, + 0x034, 0x0000604D, + 0x034, 0x0000504A, + 0x034, 0x00004047, + 0x034, 0x00003044, + 0x034, 0x00002007, + 0x034, 0x00001004, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, 0x034, 0x0000AFF7, 0x034, 0x00009DF7, 0x034, 0x00008DF4, 0x034, 0x00007DF1, 0x034, 0x00006DEE, - 0x034, 0x00005DCD, - 0x034, 0x00004CEB, + 0x034, 0x00005DEB, + 0x034, 0x00004DE8, 0x034, 0x000038CC, - 0x034, 0x0000288B, - 0x034, 0x0000184C, - 0x034, 0x0000044C, - 0xFF0F0740, 0xDEAD, + 0x034, 0x000028C9, + 0x034, 0x000018C6, + 0x034, 0x000008C3, + 0xB0000000, 0x00000000, 0x0EF, 0x00000000, - 0xFF0F0740, 0xABCD, - 0x018, 0x0001712A, - 0x0EF, 0x00000040, - 0x035, 0x000001D4, - 0x035, 0x000081D4, - 0x035, 0x000101D4, - 0x035, 0x000201B4, - 0x035, 0x000281B4, - 0x035, 0x000301B4, - 0x035, 0x000401B4, - 0x035, 0x000481B4, - 0x035, 0x000501B4, - 0xFF0F02C0, 0xCDEF, - 0x018, 0x0001712A, - 0x0EF, 0x00000040, - 0x035, 0x000001D4, - 0x035, 0x000081D4, - 0x035, 0x000101D4, - 0x035, 0x000201B4, - 0x035, 0x000281B4, - 0x035, 0x000301B4, - 0x035, 0x000401B4, - 0x035, 0x000481B4, - 0x035, 0x000501B4, - 0xFF0F01C0, 0xCDEF, - 0x018, 0x0001712A, - 0x0EF, 0x00000040, - 0x035, 0x000001D4, - 0x035, 0x000081D4, - 0x035, 0x000101D4, - 0x035, 0x000201B4, - 0x035, 0x000281B4, - 0x035, 0x000301B4, - 0x035, 0x000401B4, - 0x035, 0x000481B4, - 0x035, 0x000501B4, - 0xFF0F07D8, 0xCDEF, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000040, 0x035, 0x000001D4, @@ -959,7 +804,7 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x035, 0x000401B4, 0x035, 0x000481B4, 0x035, 0x000501B4, - 0xFF0F07D0, 0xCDEF, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000040, 0x035, 0x000001D4, @@ -971,7 +816,7 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x035, 0x000401B4, 0x035, 0x000481B4, 0x035, 0x000501B4, - 0xCDCDCDCD, 0xCDCD, + 0xA0000000, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000040, 0x035, 0x00000188, @@ -983,54 +828,9 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x035, 0x000401D8, 0x035, 0x000481D8, 0x035, 0x000501D8, - 0xFF0F0740, 0xDEAD, + 0xB0000000, 0x00000000, 0x0EF, 0x00000000, - 0xFF0F0740, 0xABCD, - 0x018, 0x0001712A, - 0x0EF, 0x00000010, - 0x036, 0x00004BFB, - 0x036, 0x0000CBFB, - 0x036, 0x00014BFB, - 0x036, 0x0001CBFB, - 0x036, 0x00024F4B, - 0x036, 0x0002CF4B, - 0x036, 0x00034F4B, - 0x036, 0x0003CF4B, - 0x036, 0x00044F4B, - 0x036, 0x0004CF4B, - 0x036, 0x00054F4B, - 0x036, 0x0005CF4B, - 0xFF0F02C0, 0xCDEF, - 0x018, 0x0001712A, - 0x0EF, 0x00000010, - 0x036, 0x00004BFB, - 0x036, 0x0000CBFB, - 0x036, 0x00014BFB, - 0x036, 0x0001CBFB, - 0x036, 0x00024F4B, - 0x036, 0x0002CF4B, - 0x036, 0x00034F4B, - 0x036, 0x0003CF4B, - 0x036, 0x00044F4B, - 0x036, 0x0004CF4B, - 0x036, 0x00054F4B, - 0x036, 0x0005CF4B, - 0xFF0F01C0, 0xCDEF, - 0x018, 0x0001712A, - 0x0EF, 0x00000010, - 0x036, 0x00004BFB, - 0x036, 0x0000CBFB, - 0x036, 0x00014BFB, - 0x036, 0x0001CBFB, - 0x036, 0x00024F4B, - 0x036, 0x0002CF4B, - 0x036, 0x00034F4B, - 0x036, 0x0003CF4B, - 0x036, 0x00044F4B, - 0x036, 0x0004CF4B, - 0x036, 0x00054F4B, - 0x036, 0x0005CF4B, - 0xFF0F07D8, 0xCDEF, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000010, 0x036, 0x00004BFB, @@ -1045,7 +845,7 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x036, 0x0004CF4B, 0x036, 0x00054F4B, 0x036, 0x0005CF4B, - 0xFF0F07D0, 0xCDEF, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000010, 0x036, 0x00004BFB, @@ -1060,91 +860,61 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x036, 0x0004CF4B, 0x036, 0x00054F4B, 0x036, 0x0005CF4B, - 0xCDCDCDCD, 0xCDCD, + 0xA0000000, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000010, 0x036, 0x00084EB4, 0x036, 0x0008CC35, 0x036, 0x00094C35, 0x036, 0x0009CC35, - 0x036, 0x000A4935, + 0x036, 0x000A4C35, 0x036, 0x000ACC35, 0x036, 0x000B4C35, 0x036, 0x000BCC35, - 0x036, 0x000C4EB4, - 0x036, 0x000CCEB5, - 0x036, 0x000D4EB5, - 0x036, 0x000DCEB5, - 0xFF0F0740, 0xDEAD, + 0x036, 0x000C4C34, + 0x036, 0x000CCC35, + 0x036, 0x000D4C35, + 0x036, 0x000DCC35, + 0xB0000000, 0x00000000, 0x0EF, 0x00000000, 0x0EF, 0x00000008, - 0xFF0F0740, 0xABCD, - 0x03C, 0x000002CC, - 0x03C, 0x00000522, - 0x03C, 0x00000902, - 0xFF0F02C0, 0xCDEF, - 0x03C, 0x000002CC, - 0x03C, 0x00000522, - 0x03C, 0x00000902, - 0xFF0F01C0, 0xCDEF, - 0x03C, 0x000002CC, - 0x03C, 0x00000522, - 0x03C, 0x00000902, - 0xFF0F07D8, 0xCDEF, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x03C, 0x000002CC, 0x03C, 0x00000522, 0x03C, 0x00000902, - 0xFF0F07D0, 0xCDEF, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, 0x03C, 0x000002CC, 0x03C, 0x00000522, 0x03C, 0x00000902, - 0xCDCDCDCD, 0xCDCD, + 0xA0000000, 0x00000000, 0x03C, 0x000002A8, 0x03C, 0x000005A2, 0x03C, 0x00000880, - 0xFF0F0740, 0xDEAD, + 0xB0000000, 0x00000000, 0x0EF, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000002, 0x0DF, 0x00000080, - 0x01F, 0x00040064, - 0xFF0F0740, 0xABCD, - 0x061, 0x000FDD43, - 0x062, 0x00038F4B, - 0x063, 0x00032117, - 0x064, 0x000194AC, - 0x065, 0x000931D1, - 0xFF0F02C0, 0xCDEF, + 0x01F, 0x00000064, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x061, 0x000FDD43, 0x062, 0x00038F4B, 0x063, 0x00032117, 0x064, 0x000194AC, 0x065, 0x000931D1, - 0xFF0F01C0, 0xCDEF, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, 0x061, 0x000FDD43, 0x062, 0x00038F4B, 0x063, 0x00032117, 0x064, 0x000194AC, - 0x065, 0x000931D1, - 0xFF0F07D8, 0xCDEF, - 0x061, 0x000FDD43, - 0x062, 0x00038F4B, - 0x063, 0x00032117, - 0x064, 0x000194AC, - 0x065, 0x000931D1, - 0xFF0F07D0, 0xCDEF, - 0x061, 0x000FDD43, - 0x062, 0x00038F4B, - 0x063, 0x00032117, - 0x064, 0x000194AC, - 0x065, 0x000931D1, - 0xCDCDCDCD, 0xCDCD, + 0x065, 0x000931D2, + 0xA0000000, 0x00000000, 0x061, 0x000E5D53, 0x062, 0x00038FCD, - 0x063, 0x000314EB, + 0x063, 0x000114EB, 0x064, 0x000196AC, 0x065, 0x000911D7, - 0xFF0F0740, 0xDEAD, + 0xB0000000, 0x00000000, 0x008, 0x00008400, 0x01C, 0x000739D2, 0x0B4, 0x0001E78D, @@ -1155,29 +925,29 @@ u32 RTL8812AE_RADIOA_ARRAY[] = { 0x0FE, 0x00000000, 0x0B4, 0x0001A78D, 0x018, 0x0001712A, - }; +u32 RTL8812AE_RADIOA_1TARRAYLEN = sizeof(RTL8812AE_RADIOA_ARRAY) / sizeof(u32); + u32 RTL8812AE_RADIOB_ARRAY[] = { 0x056, 0x00051CF2, 0x066, 0x00040000, 0x089, 0x00000080, - 0xFF0F0740, 0xABCD, - 0x086, 0x00014B38, - 0xFF0F01C0, 0xCDEF, - 0x086, 0x00014B38, - 0xFF0F02C0, 0xCDEF, - 0x086, 0x00014B38, - 0xFF0F07D8, 0xCDEF, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, 0x086, 0x00014B3A, - 0xFF0F07D0, 0xCDEF, + 0x90000001, 0x00000005, 0x40000000, 0x00000000, 0x086, 0x00014B3A, - 0xCDCDCDCD, 0xCDCD, + 0xA0000000, 0x00000000, 0x086, 0x00014B38, - 0xFF0F0740, 0xDEAD, + 0xB0000000, 0x00000000, + 0x80000004, 0x00000000, 0x40000000, 0x00000000, + 0x08B, 0x00080180, + 0xA0000000, 0x00000000, + 0x08B, 0x00087180, + 0xB0000000, 0x00000000, 0x018, 0x00000006, 0x0EF, 0x00002000, - 0xFF0F07D8, 0xABCD, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, 0x03B, 0x0003F218, 0x03B, 0x00030A58, 0x03B, 0x0002FA58, @@ -1185,7 +955,7 @@ u32 RTL8812AE_RADIOB_ARRAY[] = { 0x03B, 0x0001FA50, 0x03B, 0x00010248, 0x03B, 0x00008240, - 0xFF0F07D0, 0xCDEF, + 0x90000001, 0x00000005, 0x40000000, 0x00000000, 0x03B, 0x0003F218, 0x03B, 0x00030A58, 0x03B, 0x0002FA58, @@ -1193,7 +963,7 @@ u32 RTL8812AE_RADIOB_ARRAY[] = { 0x03B, 0x0001FA50, 0x03B, 0x00010248, 0x03B, 0x00008240, - 0xCDCDCDCD, 0xCDCD, + 0xA0000000, 0x00000000, 0x03B, 0x00038A58, 0x03B, 0x00037A58, 0x03B, 0x0002A590, @@ -1201,9 +971,9 @@ u32 RTL8812AE_RADIOB_ARRAY[] = { 0x03B, 0x00018248, 0x03B, 0x00010240, 0x03B, 0x00008240, - 0xFF0F07D8, 0xDEAD, + 0xB0000000, 0x00000000, 0x0EF, 0x00000100, - 0xFF0F07D8, 0xABCD, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, 0x034, 0x0000A4EE, 0x034, 0x00009076, 0x034, 0x00008073, @@ -1215,7 +985,7 @@ u32 RTL8812AE_RADIOB_ARRAY[] = { 0x034, 0x00002028, 0x034, 0x00001025, 0x034, 0x00000022, - 0xCDCDCDCD, 0xCDCD, + 0xA0000000, 0x00000000, 0x034, 0x0000ADF4, 0x034, 0x00009DF1, 0x034, 0x00008DEE, @@ -1227,7 +997,7 @@ u32 RTL8812AE_RADIOB_ARRAY[] = { 0x034, 0x000024E7, 0x034, 0x0000146B, 0x034, 0x0000006D, - 0xFF0F07D8, 0xDEAD, + 0xB0000000, 0x00000000, 0x0EF, 0x00000000, 0x0EF, 0x000020A2, 0x0DF, 0x00000080, @@ -1320,55 +1090,7 @@ u32 RTL8812AE_RADIOB_ARRAY[] = { 0x03B, 0x00082080, 0x03C, 0x00010000, 0x0EF, 0x00001100, - 0xFF0F0740, 0xABCD, - 0x034, 0x0004A0B2, - 0x034, 0x000490AF, - 0x034, 0x00048070, - 0x034, 0x0004706D, - 0x034, 0x00046050, - 0x034, 0x0004504D, - 0x034, 0x0004404A, - 0x034, 0x00043047, - 0x034, 0x0004200A, - 0x034, 0x00041007, - 0x034, 0x00040004, - 0xFF0F01C0, 0xCDEF, - 0x034, 0x0004A0B2, - 0x034, 0x000490AF, - 0x034, 0x00048070, - 0x034, 0x0004706D, - 0x034, 0x00046050, - 0x034, 0x0004504D, - 0x034, 0x0004404A, - 0x034, 0x00043047, - 0x034, 0x0004200A, - 0x034, 0x00041007, - 0x034, 0x00040004, - 0xFF0F02C0, 0xCDEF, - 0x034, 0x0004A0B2, - 0x034, 0x000490AF, - 0x034, 0x00048070, - 0x034, 0x0004706D, - 0x034, 0x00046050, - 0x034, 0x0004504D, - 0x034, 0x0004404A, - 0x034, 0x00043047, - 0x034, 0x0004200A, - 0x034, 0x00041007, - 0x034, 0x00040004, - 0xFF0F07D8, 0xCDEF, - 0x034, 0x0004A0B2, - 0x034, 0x000490AF, - 0x034, 0x00048070, - 0x034, 0x0004706D, - 0x034, 0x00046050, - 0x034, 0x0004504D, - 0x034, 0x0004404A, - 0x034, 0x00043047, - 0x034, 0x0004200A, - 0x034, 0x00041007, - 0x034, 0x00040004, - 0xFF0F07D0, 0xCDEF, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x034, 0x0004A0B2, 0x034, 0x000490AF, 0x034, 0x00048070, @@ -1380,68 +1102,32 @@ u32 RTL8812AE_RADIOB_ARRAY[] = { 0x034, 0x0004200A, 0x034, 0x00041007, 0x034, 0x00040004, - 0xCDCDCDCD, 0xCDCD, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, + 0x034, 0x0004A0B1, + 0x034, 0x000490AE, + 0x034, 0x0004806F, + 0x034, 0x0004706C, + 0x034, 0x0004604C, + 0x034, 0x00045049, + 0x034, 0x00044046, + 0x034, 0x00043043, + 0x034, 0x00042006, + 0x034, 0x00041003, + 0x034, 0x00040000, + 0xA0000000, 0x00000000, 0x034, 0x0004ADF5, 0x034, 0x00049DF2, 0x034, 0x00048DEF, 0x034, 0x00047DEC, 0x034, 0x00046DE9, - 0x034, 0x00045DC9, - 0x034, 0x00044CE8, - 0x034, 0x000438CA, - 0x034, 0x00042889, - 0x034, 0x0004184A, - 0x034, 0x0004044A, - 0xFF0F0740, 0xDEAD, - 0xFF0F0740, 0xABCD, - 0x034, 0x0002A0B2, - 0x034, 0x000290AF, - 0x034, 0x00028070, - 0x034, 0x0002706D, - 0x034, 0x00026050, - 0x034, 0x0002504D, - 0x034, 0x0002404A, - 0x034, 0x00023047, - 0x034, 0x0002200A, - 0x034, 0x00021007, - 0x034, 0x00020004, - 0xFF0F01C0, 0xCDEF, - 0x034, 0x0002A0B2, - 0x034, 0x000290AF, - 0x034, 0x00028070, - 0x034, 0x0002706D, - 0x034, 0x00026050, - 0x034, 0x0002504D, - 0x034, 0x0002404A, - 0x034, 0x00023047, - 0x034, 0x0002200A, - 0x034, 0x00021007, - 0x034, 0x00020004, - 0xFF0F02C0, 0xCDEF, - 0x034, 0x0002A0B2, - 0x034, 0x000290AF, - 0x034, 0x00028070, - 0x034, 0x0002706D, - 0x034, 0x00026050, - 0x034, 0x0002504D, - 0x034, 0x0002404A, - 0x034, 0x00023047, - 0x034, 0x0002200A, - 0x034, 0x00021007, - 0x034, 0x00020004, - 0xFF0F07D8, 0xCDEF, - 0x034, 0x0002A0B2, - 0x034, 0x000290AF, - 0x034, 0x00028070, - 0x034, 0x0002706D, - 0x034, 0x00026050, - 0x034, 0x0002504D, - 0x034, 0x0002404A, - 0x034, 0x00023047, - 0x034, 0x0002200A, - 0x034, 0x00021007, - 0x034, 0x00020004, - 0xFF0F07D0, 0xCDEF, + 0x034, 0x00045DE6, + 0x034, 0x00044DE3, + 0x034, 0x000438C8, + 0x034, 0x000428C5, + 0x034, 0x000418C2, + 0x034, 0x000408C0, + 0xB0000000, 0x00000000, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x034, 0x0002A0B2, 0x034, 0x000290AF, 0x034, 0x00028070, @@ -1453,32 +1139,32 @@ u32 RTL8812AE_RADIOB_ARRAY[] = { 0x034, 0x0002200A, 0x034, 0x00021007, 0x034, 0x00020004, - 0xCDCDCDCD, 0xCDCD, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, + 0x034, 0x0002A0B3, + 0x034, 0x000290B0, + 0x034, 0x00028071, + 0x034, 0x0002706E, + 0x034, 0x0002604E, + 0x034, 0x0002504B, + 0x034, 0x00024048, + 0x034, 0x00023045, + 0x034, 0x00022008, + 0x034, 0x00021005, + 0x034, 0x00020002, + 0xA0000000, 0x00000000, 0x034, 0x0002ADF5, 0x034, 0x00029DF2, 0x034, 0x00028DEF, 0x034, 0x00027DEC, 0x034, 0x00026DE9, - 0x034, 0x00025DC9, - 0x034, 0x00024CE8, - 0x034, 0x000238CA, - 0x034, 0x00022889, - 0x034, 0x0002184A, - 0x034, 0x0002044A, - 0xFF0F0740, 0xDEAD, - 0xFF0F0740, 0xABCD, - 0x034, 0x0000A0B2, - 0x034, 0x000090AF, - 0x034, 0x00008070, - 0x034, 0x0000706D, - 0x034, 0x00006050, - 0x034, 0x0000504D, - 0x034, 0x0000404A, - 0x034, 0x00003047, - 0x034, 0x0000200A, - 0x034, 0x00001007, - 0x034, 0x00000004, - 0xFF0F01C0, 0xCDEF, + 0x034, 0x00025DE6, + 0x034, 0x00024DE3, + 0x034, 0x000238C8, + 0x034, 0x000228C5, + 0x034, 0x000218C2, + 0x034, 0x000208C0, + 0xB0000000, 0x00000000, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x034, 0x0000A0B2, 0x034, 0x000090AF, 0x034, 0x00008070, @@ -1490,96 +1176,33 @@ u32 RTL8812AE_RADIOB_ARRAY[] = { 0x034, 0x0000200A, 0x034, 0x00001007, 0x034, 0x00000004, - 0xFF0F02C0, 0xCDEF, - 0x034, 0x0000A0B2, - 0x034, 0x000090AF, - 0x034, 0x00008070, - 0x034, 0x0000706D, - 0x034, 0x00006050, - 0x034, 0x0000504D, - 0x034, 0x0000404A, - 0x034, 0x00003047, - 0x034, 0x0000200A, - 0x034, 0x00001007, - 0x034, 0x00000004, - 0xFF0F07D8, 0xCDEF, - 0x034, 0x0000A0B2, - 0x034, 0x000090AF, - 0x034, 0x00008070, - 0x034, 0x0000706D, - 0x034, 0x00006050, - 0x034, 0x0000504D, - 0x034, 0x0000404A, - 0x034, 0x00003047, - 0x034, 0x0000200A, - 0x034, 0x00001007, - 0x034, 0x00000004, - 0xFF0F07D0, 0xCDEF, - 0x034, 0x0000A0B2, - 0x034, 0x000090AF, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0B3, + 0x034, 0x000090B0, 0x034, 0x00008070, 0x034, 0x0000706D, - 0x034, 0x00006050, - 0x034, 0x0000504D, - 0x034, 0x0000404A, - 0x034, 0x00003047, - 0x034, 0x0000200A, - 0x034, 0x00001007, - 0x034, 0x00000004, - 0xCDCDCDCD, 0xCDCD, + 0x034, 0x0000604D, + 0x034, 0x0000504A, + 0x034, 0x00004047, + 0x034, 0x00003044, + 0x034, 0x00002007, + 0x034, 0x00001004, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, 0x034, 0x0000AFF7, 0x034, 0x00009DF7, 0x034, 0x00008DF4, 0x034, 0x00007DF1, 0x034, 0x00006DEE, - 0x034, 0x00005DCD, - 0x034, 0x00004CEB, + 0x034, 0x00005DEB, + 0x034, 0x00004DE8, 0x034, 0x000038CC, - 0x034, 0x0000288B, - 0x034, 0x0000184C, - 0x034, 0x0000044C, - 0xFF0F0740, 0xDEAD, - 0x0EF, 0x00000000, - 0xFF0F0740, 0xABCD, - 0x018, 0x0001712A, - 0x0EF, 0x00000040, - 0x035, 0x000001C5, - 0x035, 0x000081C5, - 0x035, 0x000101C5, - 0x035, 0x00020174, - 0x035, 0x00028174, - 0x035, 0x00030174, - 0x035, 0x00040185, - 0x035, 0x00048185, - 0x035, 0x00050185, - 0x0EF, 0x00000000, - 0xFF0F01C0, 0xCDEF, - 0x018, 0x0001712A, - 0x0EF, 0x00000040, - 0x035, 0x000001C5, - 0x035, 0x000081C5, - 0x035, 0x000101C5, - 0x035, 0x00020174, - 0x035, 0x00028174, - 0x035, 0x00030174, - 0x035, 0x00040185, - 0x035, 0x00048185, - 0x035, 0x00050185, - 0x0EF, 0x00000000, - 0xFF0F02C0, 0xCDEF, - 0x018, 0x0001712A, - 0x0EF, 0x00000040, - 0x035, 0x000001C5, - 0x035, 0x000081C5, - 0x035, 0x000101C5, - 0x035, 0x00020174, - 0x035, 0x00028174, - 0x035, 0x00030174, - 0x035, 0x00040185, - 0x035, 0x00048185, - 0x035, 0x00050185, + 0x034, 0x000028C9, + 0x034, 0x000018C6, + 0x034, 0x000008C3, + 0xB0000000, 0x00000000, 0x0EF, 0x00000000, - 0xFF0F07D8, 0xCDEF, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000040, 0x035, 0x000001C5, @@ -1592,7 +1215,7 @@ u32 RTL8812AE_RADIOB_ARRAY[] = { 0x035, 0x00048185, 0x035, 0x00050185, 0x0EF, 0x00000000, - 0xFF0F07D0, 0xCDEF, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000040, 0x035, 0x000001C5, @@ -1605,66 +1228,21 @@ u32 RTL8812AE_RADIOB_ARRAY[] = { 0x035, 0x00048185, 0x035, 0x00050185, 0x0EF, 0x00000000, - 0xCDCDCDCD, 0xCDCD, + 0xA0000000, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000040, - 0x035, 0x00000186, - 0x035, 0x00008186, - 0x035, 0x00010185, - 0x035, 0x000201D5, - 0x035, 0x000281D5, - 0x035, 0x000301D5, - 0x035, 0x000401D5, - 0x035, 0x000481D5, - 0x035, 0x000501D5, + 0x035, 0x00000188, + 0x035, 0x00008147, + 0x035, 0x00010147, + 0x035, 0x000201D7, + 0x035, 0x000281D7, + 0x035, 0x000301D7, + 0x035, 0x000401D8, + 0x035, 0x000481D8, + 0x035, 0x000501D8, 0x0EF, 0x00000000, - 0xFF0F0740, 0xDEAD, - 0xFF0F0740, 0xABCD, - 0x018, 0x0001712A, - 0x0EF, 0x00000010, - 0x036, 0x00005B8B, - 0x036, 0x0000DB8B, - 0x036, 0x00015B8B, - 0x036, 0x0001DB8B, - 0x036, 0x000262DB, - 0x036, 0x0002E2DB, - 0x036, 0x000362DB, - 0x036, 0x0003E2DB, - 0x036, 0x0004553B, - 0x036, 0x0004D53B, - 0x036, 0x0005553B, - 0x036, 0x0005D53B, - 0xFF0F01C0, 0xCDEF, - 0x018, 0x0001712A, - 0x0EF, 0x00000010, - 0x036, 0x00005B8B, - 0x036, 0x0000DB8B, - 0x036, 0x00015B8B, - 0x036, 0x0001DB8B, - 0x036, 0x000262DB, - 0x036, 0x0002E2DB, - 0x036, 0x000362DB, - 0x036, 0x0003E2DB, - 0x036, 0x0004553B, - 0x036, 0x0004D53B, - 0x036, 0x0005553B, - 0x036, 0x0005D53B, - 0xFF0F02C0, 0xCDEF, - 0x018, 0x0001712A, - 0x0EF, 0x00000010, - 0x036, 0x00005B8B, - 0x036, 0x0000DB8B, - 0x036, 0x00015B8B, - 0x036, 0x0001DB8B, - 0x036, 0x000262DB, - 0x036, 0x0002E2DB, - 0x036, 0x000362DB, - 0x036, 0x0003E2DB, - 0x036, 0x0004553B, - 0x036, 0x0004D53B, - 0x036, 0x0005553B, - 0x036, 0x0005D53B, - 0xFF0F07D8, 0xCDEF, + 0xB0000000, 0x00000000, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000010, 0x036, 0x00005B8B, @@ -1679,7 +1257,7 @@ u32 RTL8812AE_RADIOB_ARRAY[] = { 0x036, 0x0004D53B, 0x036, 0x0005553B, 0x036, 0x0005D53B, - 0xFF0F07D0, 0xCDEF, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000010, 0x036, 0x00005B8B, @@ -1694,94 +1272,71 @@ u32 RTL8812AE_RADIOB_ARRAY[] = { 0x036, 0x0004D53B, 0x036, 0x0005553B, 0x036, 0x0005D53B, - 0xCDCDCDCD, 0xCDCD, + 0xA0000000, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000010, 0x036, 0x00084EB4, - 0x036, 0x0008C9B4, - 0x036, 0x000949B4, - 0x036, 0x0009C9B4, - 0x036, 0x000A4935, - 0x036, 0x000AC935, - 0x036, 0x000B4935, - 0x036, 0x000BC935, - 0x036, 0x000C4EB4, - 0x036, 0x000CCEB4, - 0x036, 0x000D4EB4, - 0x036, 0x000DCEB4, - 0xFF0F0740, 0xDEAD, - 0x0EF, 0x00000000, - 0x0EF, 0x00000008, - 0xFF0F0740, 0xABCD, - 0x03C, 0x000002DC, - 0x03C, 0x00000524, - 0x03C, 0x00000902, - 0xFF0F01C0, 0xCDEF, - 0x03C, 0x000002DC, - 0x03C, 0x00000524, - 0x03C, 0x00000902, - 0xFF0F02C0, 0xCDEF, - 0x03C, 0x000002DC, - 0x03C, 0x00000524, - 0x03C, 0x00000902, - 0xFF0F07D8, 0xCDEF, + 0x036, 0x0008CC35, + 0x036, 0x00094C35, + 0x036, 0x0009CC35, + 0x036, 0x000A4C35, + 0x036, 0x000ACC35, + 0x036, 0x000B4C35, + 0x036, 0x000BCC35, + 0x036, 0x000C4C34, + 0x036, 0x000CCC35, + 0x036, 0x000D4C35, + 0x036, 0x000DCC35, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x00000008, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x03C, 0x000002DC, 0x03C, 0x00000524, 0x03C, 0x00000902, - 0xFF0F07D0, 0xCDEF, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, 0x03C, 0x000002DC, 0x03C, 0x00000524, 0x03C, 0x00000902, - 0xCDCDCDCD, 0xCDCD, - 0x03C, 0x000002AA, + 0xA0000000, 0x00000000, + 0x03C, 0x000002A8, 0x03C, 0x000005A2, 0x03C, 0x00000880, - 0xFF0F0740, 0xDEAD, + 0xB0000000, 0x00000000, 0x0EF, 0x00000000, 0x018, 0x0001712A, 0x0EF, 0x00000002, 0x0DF, 0x00000080, - 0xFF0F0740, 0xABCD, - 0x061, 0x000EAC43, - 0x062, 0x00038F47, - 0x063, 0x00031157, - 0x064, 0x0001C4AC, - 0x065, 0x000931D1, - 0xFF0F01C0, 0xCDEF, - 0x061, 0x000EAC43, - 0x062, 0x00038F47, - 0x063, 0x00031157, - 0x064, 0x0001C4AC, - 0x065, 0x000931D1, - 0xFF0F02C0, 0xCDEF, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, 0x061, 0x000EAC43, 0x062, 0x00038F47, 0x063, 0x00031157, 0x064, 0x0001C4AC, 0x065, 0x000931D1, - 0xFF0F07D8, 0xCDEF, + 0x90000008, 0x05000000, 0x40000000, 0x00000000, 0x061, 0x000EAC43, 0x062, 0x00038F47, 0x063, 0x00031157, 0x064, 0x0001C4AC, - 0x065, 0x000931D1, - 0xFF0F07D0, 0xCDEF, + 0x065, 0x000931D2, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, 0x061, 0x000EAC43, 0x062, 0x00038F47, 0x063, 0x00031157, 0x064, 0x0001C4AC, 0x065, 0x000931D1, - 0xCDCDCDCD, 0xCDCD, + 0xA0000000, 0x00000000, 0x061, 0x000E5D53, 0x062, 0x00038FCD, - 0x063, 0x000314EB, + 0x063, 0x000114EB, 0x064, 0x000196AC, - 0x065, 0x000931D7, - 0xFF0F0740, 0xDEAD, + 0x065, 0x000911D7, + 0xB0000000, 0x00000000, 0x008, 0x00008400, - }; +u32 RTL8812AE_RADIOB_1TARRAYLEN = sizeof(RTL8812AE_RADIOB_ARRAY) / sizeof(u32); + u32 RTL8821AE_RADIOA_ARRAY[] = { 0x018, 0x0001712A, 0x056, 0x00051CF2, @@ -2378,14 +1933,14 @@ u32 RTL8821AE_RADIOA_1TARRAYLEN = sizeof(RTL8821AE_RADIOA_ARRAY) / sizeof(u32); u32 RTL8812AE_MAC_REG_ARRAY[] = { 0x010, 0x0000000C, - 0xFF0F0180, 0xABCD, + 0x80000200, 0x00000000, 0x40000000, 0x00000000, + 0x011, 0x00000066, + 0xA0000000, 0x00000000, + 0x011, 0x0000005A, + 0xB0000000, 0x00000000, 0x025, 0x0000000F, - 0xFF0F01C0, 0xCDEF, - 0x025, 0x0000000F, - 0xCDCDCDCD, 0xCDCD, - 0x025, 0x0000006F, - 0xFF0F0180, 0xDEAD, 0x072, 0x00000000, + 0x420, 0x00000080, 0x428, 0x0000000A, 0x429, 0x00000010, 0x430, 0x00000000, @@ -2452,7 +2007,7 @@ u32 RTL8812AE_MAC_REG_ARRAY[] = { 0x559, 0x00000002, 0x55C, 0x00000050, 0x55D, 0x000000FF, - 0x604, 0x00000001, + 0x604, 0x00000009, 0x605, 0x00000030, 0x607, 0x00000003, 0x608, 0x0000000E, @@ -2484,9 +2039,10 @@ u32 RTL8812AE_MAC_REG_ARRAY[] = { 0x70A, 0x00000065, 0x70B, 0x00000087, 0x718, 0x00000040, - }; +u32 RTL8812AE_MAC_1T_ARRAYLEN = sizeof(RTL8812AE_MAC_REG_ARRAY) / sizeof(u32); + u32 RTL8821AE_MAC_REG_ARRAY[] = { 0x428, 0x0000000A, 0x429, 0x00000010, @@ -2533,585 +2089,260 @@ u32 RTL8821AE_MAC_REG_ARRAY[] = { 0x501, 0x000000A2, 0x502, 0x0000002F, 0x503, 0x00000000, - 0x504, 0x00000028, - 0x505, 0x000000A3, - 0x506, 0x0000005E, - 0x507, 0x00000000, - 0x508, 0x0000002B, - 0x509, 0x000000A4, - 0x50A, 0x0000005E, - 0x50B, 0x00000000, - 0x50C, 0x0000004F, - 0x50D, 0x000000A4, - 0x50E, 0x00000000, - 0x50F, 0x00000000, - 0x512, 0x0000001C, - 0x514, 0x0000000A, - 0x516, 0x0000000A, - 0x525, 0x0000004F, - 0x550, 0x00000010, - 0x551, 0x00000010, - 0x559, 0x00000002, - 0x55C, 0x00000050, - 0x55D, 0x000000FF, - 0x605, 0x00000030, - 0x607, 0x00000007, - 0x608, 0x0000000E, - 0x609, 0x0000002A, - 0x620, 0x000000FF, - 0x621, 0x000000FF, - 0x622, 0x000000FF, - 0x623, 0x000000FF, - 0x624, 0x000000FF, - 0x625, 0x000000FF, - 0x626, 0x000000FF, - 0x627, 0x000000FF, - 0x638, 0x00000050, - 0x63C, 0x0000000A, - 0x63D, 0x0000000A, - 0x63E, 0x0000000E, - 0x63F, 0x0000000E, - 0x640, 0x00000040, - 0x642, 0x00000040, - 0x643, 0x00000000, - 0x652, 0x000000C8, - 0x66E, 0x00000005, - 0x700, 0x00000021, - 0x701, 0x00000043, - 0x702, 0x00000065, - 0x703, 0x00000087, - 0x708, 0x00000021, - 0x709, 0x00000043, - 0x70A, 0x00000065, - 0x70B, 0x00000087, - 0x718, 0x00000040, -}; - -u32 RTL8821AE_MAC_1T_ARRAYLEN = sizeof(RTL8821AE_MAC_REG_ARRAY) / sizeof(u32); - -u32 RTL8812AE_AGC_TAB_ARRAY[] = { - 0xFF0F07D8, 0xABCD, - 0x81C, 0xFC000001, - 0x81C, 0xFB020001, - 0x81C, 0xFA040001, - 0x81C, 0xF9060001, - 0x81C, 0xF8080001, - 0x81C, 0xF70A0001, - 0x81C, 0xF60C0001, - 0x81C, 0xF50E0001, - 0x81C, 0xF4100001, - 0x81C, 0xF3120001, - 0x81C, 0xF2140001, - 0x81C, 0xF1160001, - 0x81C, 0xF0180001, - 0x81C, 0xEF1A0001, - 0x81C, 0xEE1C0001, - 0x81C, 0xED1E0001, - 0x81C, 0xEC200001, - 0x81C, 0xEB220001, - 0x81C, 0xEA240001, - 0x81C, 0xCD260001, - 0x81C, 0xCC280001, - 0x81C, 0xCB2A0001, - 0x81C, 0xCA2C0001, - 0x81C, 0xC92E0001, - 0x81C, 0xC8300001, - 0x81C, 0xA6320001, - 0x81C, 0xA5340001, - 0x81C, 0xA4360001, - 0x81C, 0xA3380001, - 0x81C, 0xA23A0001, - 0x81C, 0x883C0001, - 0x81C, 0x873E0001, - 0x81C, 0x86400001, - 0x81C, 0x85420001, - 0x81C, 0x84440001, - 0x81C, 0x83460001, - 0x81C, 0x82480001, - 0x81C, 0x814A0001, - 0x81C, 0x484C0001, - 0x81C, 0x474E0001, - 0x81C, 0x46500001, - 0x81C, 0x45520001, - 0x81C, 0x44540001, - 0x81C, 0x43560001, - 0x81C, 0x42580001, - 0x81C, 0x415A0001, - 0x81C, 0x255C0001, - 0x81C, 0x245E0001, - 0x81C, 0x23600001, - 0x81C, 0x22620001, - 0x81C, 0x21640001, - 0x81C, 0x21660001, - 0x81C, 0x21680001, - 0x81C, 0x216A0001, - 0x81C, 0x216C0001, - 0x81C, 0x216E0001, - 0x81C, 0x21700001, - 0x81C, 0x21720001, - 0x81C, 0x21740001, - 0x81C, 0x21760001, - 0x81C, 0x21780001, - 0x81C, 0x217A0001, - 0x81C, 0x217C0001, - 0x81C, 0x217E0001, - 0xFF0F07D0, 0xCDEF, - 0x81C, 0xF9000001, - 0x81C, 0xF8020001, - 0x81C, 0xF7040001, - 0x81C, 0xF6060001, - 0x81C, 0xF5080001, - 0x81C, 0xF40A0001, - 0x81C, 0xF30C0001, - 0x81C, 0xF20E0001, - 0x81C, 0xF1100001, - 0x81C, 0xF0120001, - 0x81C, 0xEF140001, - 0x81C, 0xEE160001, - 0x81C, 0xED180001, - 0x81C, 0xEC1A0001, - 0x81C, 0xEB1C0001, - 0x81C, 0xEA1E0001, - 0x81C, 0xCD200001, - 0x81C, 0xCC220001, - 0x81C, 0xCB240001, - 0x81C, 0xCA260001, - 0x81C, 0xC9280001, - 0x81C, 0xC82A0001, - 0x81C, 0xC72C0001, - 0x81C, 0xC62E0001, - 0x81C, 0xA5300001, - 0x81C, 0xA4320001, - 0x81C, 0xA3340001, - 0x81C, 0xA2360001, - 0x81C, 0x88380001, - 0x81C, 0x873A0001, - 0x81C, 0x863C0001, - 0x81C, 0x853E0001, - 0x81C, 0x84400001, - 0x81C, 0x83420001, - 0x81C, 0x82440001, - 0x81C, 0x81460001, - 0x81C, 0x48480001, - 0x81C, 0x474A0001, - 0x81C, 0x464C0001, - 0x81C, 0x454E0001, - 0x81C, 0x44500001, - 0x81C, 0x43520001, - 0x81C, 0x42540001, - 0x81C, 0x41560001, - 0x81C, 0x25580001, - 0x81C, 0x245A0001, - 0x81C, 0x235C0001, - 0x81C, 0x225E0001, - 0x81C, 0x21600001, - 0x81C, 0x21620001, - 0x81C, 0x21640001, - 0x81C, 0x21660001, - 0x81C, 0x21680001, - 0x81C, 0x216A0001, - 0x81C, 0x236C0001, - 0x81C, 0x226E0001, - 0x81C, 0x21700001, - 0x81C, 0x21720001, - 0x81C, 0x21740001, - 0x81C, 0x21760001, - 0x81C, 0x21780001, - 0x81C, 0x217A0001, - 0x81C, 0x217C0001, - 0x81C, 0x217E0001, - 0xCDCDCDCD, 0xCDCD, - 0x81C, 0xFF000001, - 0x81C, 0xFF020001, - 0x81C, 0xFF040001, - 0x81C, 0xFF060001, - 0x81C, 0xFF080001, - 0x81C, 0xFE0A0001, - 0x81C, 0xFD0C0001, - 0x81C, 0xFC0E0001, - 0x81C, 0xFB100001, - 0x81C, 0xFA120001, - 0x81C, 0xF9140001, - 0x81C, 0xF8160001, - 0x81C, 0xF7180001, - 0x81C, 0xF61A0001, - 0x81C, 0xF51C0001, - 0x81C, 0xF41E0001, - 0x81C, 0xF3200001, - 0x81C, 0xF2220001, - 0x81C, 0xF1240001, - 0x81C, 0xF0260001, - 0x81C, 0xEF280001, - 0x81C, 0xEE2A0001, - 0x81C, 0xED2C0001, - 0x81C, 0xEC2E0001, - 0x81C, 0xEB300001, - 0x81C, 0xEA320001, - 0x81C, 0xE9340001, - 0x81C, 0xE8360001, - 0x81C, 0xE7380001, - 0x81C, 0xE63A0001, - 0x81C, 0xE53C0001, - 0x81C, 0xC73E0001, - 0x81C, 0xC6400001, - 0x81C, 0xC5420001, - 0x81C, 0xC4440001, - 0x81C, 0xC3460001, - 0x81C, 0xC2480001, - 0x81C, 0xC14A0001, - 0x81C, 0xA74C0001, - 0x81C, 0xA64E0001, - 0x81C, 0xA5500001, - 0x81C, 0xA4520001, - 0x81C, 0xA3540001, - 0x81C, 0xA2560001, - 0x81C, 0xA1580001, - 0x81C, 0x675A0001, - 0x81C, 0x665C0001, - 0x81C, 0x655E0001, - 0x81C, 0x64600001, - 0x81C, 0x63620001, - 0x81C, 0x48640001, - 0x81C, 0x47660001, - 0x81C, 0x46680001, - 0x81C, 0x456A0001, - 0x81C, 0x446C0001, - 0x81C, 0x436E0001, - 0x81C, 0x42700001, - 0x81C, 0x41720001, - 0x81C, 0x41740001, - 0x81C, 0x41760001, - 0x81C, 0x41780001, - 0x81C, 0x417A0001, - 0x81C, 0x417C0001, - 0x81C, 0x417E0001, - 0xFF0F07D8, 0xDEAD, - 0xFF0F0180, 0xABCD, - 0x81C, 0xFC800001, - 0x81C, 0xFB820001, - 0x81C, 0xFA840001, - 0x81C, 0xF9860001, - 0x81C, 0xF8880001, - 0x81C, 0xF78A0001, - 0x81C, 0xF68C0001, - 0x81C, 0xF58E0001, - 0x81C, 0xF4900001, - 0x81C, 0xF3920001, - 0x81C, 0xF2940001, - 0x81C, 0xF1960001, - 0x81C, 0xF0980001, - 0x81C, 0xEF9A0001, - 0x81C, 0xEE9C0001, - 0x81C, 0xED9E0001, - 0x81C, 0xECA00001, - 0x81C, 0xEBA20001, - 0x81C, 0xEAA40001, - 0x81C, 0xE9A60001, - 0x81C, 0xE8A80001, - 0x81C, 0xE7AA0001, - 0x81C, 0xE6AC0001, - 0x81C, 0xE5AE0001, - 0x81C, 0xE4B00001, - 0x81C, 0xE3B20001, - 0x81C, 0xA8B40001, - 0x81C, 0xA7B60001, - 0x81C, 0xA6B80001, - 0x81C, 0xA5BA0001, - 0x81C, 0xA4BC0001, - 0x81C, 0xA3BE0001, - 0x81C, 0xA2C00001, - 0x81C, 0xA1C20001, - 0x81C, 0x68C40001, - 0x81C, 0x67C60001, - 0x81C, 0x66C80001, - 0x81C, 0x65CA0001, - 0x81C, 0x64CC0001, - 0x81C, 0x47CE0001, - 0x81C, 0x46D00001, - 0x81C, 0x45D20001, - 0x81C, 0x44D40001, - 0x81C, 0x43D60001, - 0x81C, 0x42D80001, - 0x81C, 0x08DA0001, - 0x81C, 0x07DC0001, - 0x81C, 0x06DE0001, - 0x81C, 0x05E00001, - 0x81C, 0x04E20001, - 0x81C, 0x03E40001, - 0x81C, 0x02E60001, - 0x81C, 0x01E80001, - 0x81C, 0x01EA0001, - 0x81C, 0x01EC0001, - 0x81C, 0x01EE0001, - 0x81C, 0x01F00001, - 0x81C, 0x01F20001, - 0x81C, 0x01F40001, - 0x81C, 0x01F60001, - 0x81C, 0x01F80001, - 0x81C, 0x01FA0001, - 0x81C, 0x01FC0001, - 0x81C, 0x01FE0001, - 0xFF0F0280, 0xCDEF, - 0x81C, 0xFC800001, - 0x81C, 0xFB820001, - 0x81C, 0xFA840001, - 0x81C, 0xF9860001, - 0x81C, 0xF8880001, - 0x81C, 0xF78A0001, - 0x81C, 0xF68C0001, - 0x81C, 0xF58E0001, - 0x81C, 0xF4900001, - 0x81C, 0xF3920001, - 0x81C, 0xF2940001, - 0x81C, 0xF1960001, - 0x81C, 0xF0980001, - 0x81C, 0xEF9A0001, - 0x81C, 0xEE9C0001, - 0x81C, 0xED9E0001, - 0x81C, 0xECA00001, - 0x81C, 0xEBA20001, - 0x81C, 0xEAA40001, - 0x81C, 0xE9A60001, - 0x81C, 0xE8A80001, - 0x81C, 0xE7AA0001, - 0x81C, 0xE6AC0001, - 0x81C, 0xE5AE0001, - 0x81C, 0xE4B00001, - 0x81C, 0xE3B20001, - 0x81C, 0xA8B40001, - 0x81C, 0xA7B60001, - 0x81C, 0xA6B80001, - 0x81C, 0xA5BA0001, - 0x81C, 0xA4BC0001, - 0x81C, 0xA3BE0001, - 0x81C, 0xA2C00001, - 0x81C, 0xA1C20001, - 0x81C, 0x68C40001, - 0x81C, 0x67C60001, - 0x81C, 0x66C80001, - 0x81C, 0x65CA0001, - 0x81C, 0x64CC0001, - 0x81C, 0x47CE0001, - 0x81C, 0x46D00001, - 0x81C, 0x45D20001, - 0x81C, 0x44D40001, - 0x81C, 0x43D60001, - 0x81C, 0x42D80001, - 0x81C, 0x08DA0001, - 0x81C, 0x07DC0001, - 0x81C, 0x06DE0001, - 0x81C, 0x05E00001, - 0x81C, 0x04E20001, - 0x81C, 0x03E40001, - 0x81C, 0x02E60001, - 0x81C, 0x01E80001, - 0x81C, 0x01EA0001, - 0x81C, 0x01EC0001, - 0x81C, 0x01EE0001, - 0x81C, 0x01F00001, - 0x81C, 0x01F20001, - 0x81C, 0x01F40001, - 0x81C, 0x01F60001, - 0x81C, 0x01F80001, - 0x81C, 0x01FA0001, - 0x81C, 0x01FC0001, - 0x81C, 0x01FE0001, - 0xFF0F01C0, 0xCDEF, - 0x81C, 0xFC800001, - 0x81C, 0xFB820001, - 0x81C, 0xFA840001, - 0x81C, 0xF9860001, - 0x81C, 0xF8880001, - 0x81C, 0xF78A0001, - 0x81C, 0xF68C0001, - 0x81C, 0xF58E0001, - 0x81C, 0xF4900001, - 0x81C, 0xF3920001, - 0x81C, 0xF2940001, - 0x81C, 0xF1960001, - 0x81C, 0xF0980001, - 0x81C, 0xEF9A0001, - 0x81C, 0xEE9C0001, - 0x81C, 0xED9E0001, - 0x81C, 0xECA00001, - 0x81C, 0xEBA20001, - 0x81C, 0xEAA40001, - 0x81C, 0xE9A60001, - 0x81C, 0xE8A80001, - 0x81C, 0xE7AA0001, - 0x81C, 0xE6AC0001, - 0x81C, 0xE5AE0001, - 0x81C, 0xE4B00001, - 0x81C, 0xE3B20001, - 0x81C, 0xA8B40001, - 0x81C, 0xA7B60001, - 0x81C, 0xA6B80001, - 0x81C, 0xA5BA0001, - 0x81C, 0xA4BC0001, - 0x81C, 0xA3BE0001, - 0x81C, 0xA2C00001, - 0x81C, 0xA1C20001, - 0x81C, 0x68C40001, - 0x81C, 0x67C60001, - 0x81C, 0x66C80001, - 0x81C, 0x65CA0001, - 0x81C, 0x64CC0001, - 0x81C, 0x47CE0001, - 0x81C, 0x46D00001, - 0x81C, 0x45D20001, - 0x81C, 0x44D40001, - 0x81C, 0x43D60001, - 0x81C, 0x42D80001, - 0x81C, 0x08DA0001, - 0x81C, 0x07DC0001, - 0x81C, 0x06DE0001, - 0x81C, 0x05E00001, - 0x81C, 0x04E20001, - 0x81C, 0x03E40001, - 0x81C, 0x02E60001, - 0x81C, 0x01E80001, - 0x81C, 0x01EA0001, - 0x81C, 0x01EC0001, - 0x81C, 0x01EE0001, - 0x81C, 0x01F00001, - 0x81C, 0x01F20001, - 0x81C, 0x01F40001, - 0x81C, 0x01F60001, - 0x81C, 0x01F80001, - 0x81C, 0x01FA0001, - 0x81C, 0x01FC0001, - 0x81C, 0x01FE0001, - 0xFF0F02C0, 0xCDEF, - 0x81C, 0xFC800001, - 0x81C, 0xFB820001, - 0x81C, 0xFA840001, - 0x81C, 0xF9860001, - 0x81C, 0xF8880001, - 0x81C, 0xF78A0001, - 0x81C, 0xF68C0001, - 0x81C, 0xF58E0001, - 0x81C, 0xF4900001, - 0x81C, 0xF3920001, - 0x81C, 0xF2940001, - 0x81C, 0xF1960001, - 0x81C, 0xF0980001, - 0x81C, 0xEF9A0001, - 0x81C, 0xEE9C0001, - 0x81C, 0xED9E0001, - 0x81C, 0xECA00001, - 0x81C, 0xEBA20001, - 0x81C, 0xEAA40001, - 0x81C, 0xE9A60001, - 0x81C, 0xE8A80001, - 0x81C, 0xE7AA0001, - 0x81C, 0xE6AC0001, - 0x81C, 0xE5AE0001, - 0x81C, 0xE4B00001, - 0x81C, 0xE3B20001, - 0x81C, 0xA8B40001, - 0x81C, 0xA7B60001, - 0x81C, 0xA6B80001, - 0x81C, 0xA5BA0001, - 0x81C, 0xA4BC0001, - 0x81C, 0xA3BE0001, - 0x81C, 0xA2C00001, - 0x81C, 0xA1C20001, - 0x81C, 0x68C40001, - 0x81C, 0x67C60001, - 0x81C, 0x66C80001, - 0x81C, 0x65CA0001, - 0x81C, 0x64CC0001, - 0x81C, 0x47CE0001, - 0x81C, 0x46D00001, - 0x81C, 0x45D20001, - 0x81C, 0x44D40001, - 0x81C, 0x43D60001, - 0x81C, 0x42D80001, - 0x81C, 0x08DA0001, - 0x81C, 0x07DC0001, - 0x81C, 0x06DE0001, - 0x81C, 0x05E00001, - 0x81C, 0x04E20001, - 0x81C, 0x03E40001, - 0x81C, 0x02E60001, - 0x81C, 0x01E80001, - 0x81C, 0x01EA0001, - 0x81C, 0x01EC0001, - 0x81C, 0x01EE0001, - 0x81C, 0x01F00001, - 0x81C, 0x01F20001, - 0x81C, 0x01F40001, - 0x81C, 0x01F60001, - 0x81C, 0x01F80001, - 0x81C, 0x01FA0001, - 0x81C, 0x01FC0001, - 0x81C, 0x01FE0001, - 0xFF0F07D8, 0xCDEF, - 0x81C, 0xFC800001, - 0x81C, 0xFB820001, - 0x81C, 0xFA840001, - 0x81C, 0xF9860001, - 0x81C, 0xF8880001, - 0x81C, 0xF78A0001, - 0x81C, 0xF68C0001, - 0x81C, 0xF58E0001, - 0x81C, 0xF4900001, - 0x81C, 0xF3920001, - 0x81C, 0xF2940001, - 0x81C, 0xF1960001, - 0x81C, 0xF0980001, - 0x81C, 0xEF9A0001, - 0x81C, 0xEE9C0001, - 0x81C, 0xED9E0001, - 0x81C, 0xECA00001, - 0x81C, 0xEBA20001, - 0x81C, 0xEAA40001, - 0x81C, 0xE9A60001, - 0x81C, 0xE8A80001, - 0x81C, 0xE7AA0001, - 0x81C, 0xE6AC0001, - 0x81C, 0xE5AE0001, - 0x81C, 0xE4B00001, - 0x81C, 0xE3B20001, - 0x81C, 0xA8B40001, - 0x81C, 0xA7B60001, - 0x81C, 0xA6B80001, - 0x81C, 0xA5BA0001, - 0x81C, 0xA4BC0001, - 0x81C, 0xA3BE0001, - 0x81C, 0xA2C00001, - 0x81C, 0xA1C20001, - 0x81C, 0x68C40001, - 0x81C, 0x67C60001, - 0x81C, 0x66C80001, - 0x81C, 0x65CA0001, - 0x81C, 0x64CC0001, - 0x81C, 0x47CE0001, - 0x81C, 0x46D00001, - 0x81C, 0x45D20001, - 0x81C, 0x44D40001, - 0x81C, 0x43D60001, - 0x81C, 0x42D80001, - 0x81C, 0x08DA0001, - 0x81C, 0x07DC0001, - 0x81C, 0x06DE0001, - 0x81C, 0x05E00001, - 0x81C, 0x04E20001, - 0x81C, 0x03E40001, - 0x81C, 0x02E60001, - 0x81C, 0x01E80001, - 0x81C, 0x01EA0001, - 0x81C, 0x01EC0001, - 0x81C, 0x01EE0001, - 0x81C, 0x01F00001, - 0x81C, 0x01F20001, - 0x81C, 0x01F40001, - 0x81C, 0x01F60001, - 0x81C, 0x01F80001, - 0x81C, 0x01FA0001, - 0x81C, 0x01FC0001, - 0x81C, 0x01FE0001, - 0xFF0F07D0, 0xCDEF, + 0x504, 0x00000028, + 0x505, 0x000000A3, + 0x506, 0x0000005E, + 0x507, 0x00000000, + 0x508, 0x0000002B, + 0x509, 0x000000A4, + 0x50A, 0x0000005E, + 0x50B, 0x00000000, + 0x50C, 0x0000004F, + 0x50D, 0x000000A4, + 0x50E, 0x00000000, + 0x50F, 0x00000000, + 0x512, 0x0000001C, + 0x514, 0x0000000A, + 0x516, 0x0000000A, + 0x525, 0x0000004F, + 0x550, 0x00000010, + 0x551, 0x00000010, + 0x559, 0x00000002, + 0x55C, 0x00000050, + 0x55D, 0x000000FF, + 0x605, 0x00000030, + 0x607, 0x00000007, + 0x608, 0x0000000E, + 0x609, 0x0000002A, + 0x620, 0x000000FF, + 0x621, 0x000000FF, + 0x622, 0x000000FF, + 0x623, 0x000000FF, + 0x624, 0x000000FF, + 0x625, 0x000000FF, + 0x626, 0x000000FF, + 0x627, 0x000000FF, + 0x638, 0x00000050, + 0x63C, 0x0000000A, + 0x63D, 0x0000000A, + 0x63E, 0x0000000E, + 0x63F, 0x0000000E, + 0x640, 0x00000040, + 0x642, 0x00000040, + 0x643, 0x00000000, + 0x652, 0x000000C8, + 0x66E, 0x00000005, + 0x700, 0x00000021, + 0x701, 0x00000043, + 0x702, 0x00000065, + 0x703, 0x00000087, + 0x708, 0x00000021, + 0x709, 0x00000043, + 0x70A, 0x00000065, + 0x70B, 0x00000087, + 0x718, 0x00000040, +}; + +u32 RTL8821AE_MAC_1T_ARRAYLEN = sizeof(RTL8821AE_MAC_REG_ARRAY) / sizeof(u32); + +u32 RTL8812AE_AGC_TAB_ARRAY[] = { + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFC000001, + 0x81C, 0xFB020001, + 0x81C, 0xFA040001, + 0x81C, 0xF9060001, + 0x81C, 0xF8080001, + 0x81C, 0xF70A0001, + 0x81C, 0xF60C0001, + 0x81C, 0xF50E0001, + 0x81C, 0xF4100001, + 0x81C, 0xF3120001, + 0x81C, 0xF2140001, + 0x81C, 0xF1160001, + 0x81C, 0xF0180001, + 0x81C, 0xEF1A0001, + 0x81C, 0xEE1C0001, + 0x81C, 0xED1E0001, + 0x81C, 0xEC200001, + 0x81C, 0xEB220001, + 0x81C, 0xEA240001, + 0x81C, 0xCD260001, + 0x81C, 0xCC280001, + 0x81C, 0xCB2A0001, + 0x81C, 0xCA2C0001, + 0x81C, 0xC92E0001, + 0x81C, 0xC8300001, + 0x81C, 0xA6320001, + 0x81C, 0xA5340001, + 0x81C, 0xA4360001, + 0x81C, 0xA3380001, + 0x81C, 0xA23A0001, + 0x81C, 0x883C0001, + 0x81C, 0x873E0001, + 0x81C, 0x86400001, + 0x81C, 0x85420001, + 0x81C, 0x84440001, + 0x81C, 0x83460001, + 0x81C, 0x82480001, + 0x81C, 0x814A0001, + 0x81C, 0x484C0001, + 0x81C, 0x474E0001, + 0x81C, 0x46500001, + 0x81C, 0x45520001, + 0x81C, 0x44540001, + 0x81C, 0x43560001, + 0x81C, 0x42580001, + 0x81C, 0x415A0001, + 0x81C, 0x255C0001, + 0x81C, 0x245E0001, + 0x81C, 0x23600001, + 0x81C, 0x22620001, + 0x81C, 0x21640001, + 0x81C, 0x21660001, + 0x81C, 0x21680001, + 0x81C, 0x216A0001, + 0x81C, 0x216C0001, + 0x81C, 0x216E0001, + 0x81C, 0x21700001, + 0x81C, 0x21720001, + 0x81C, 0x21740001, + 0x81C, 0x21760001, + 0x81C, 0x21780001, + 0x81C, 0x217A0001, + 0x81C, 0x217C0001, + 0x81C, 0x217E0001, + 0x90000001, 0x00000005, 0x40000000, 0x00000000, + 0x81C, 0xF9000001, + 0x81C, 0xF8020001, + 0x81C, 0xF7040001, + 0x81C, 0xF6060001, + 0x81C, 0xF5080001, + 0x81C, 0xF40A0001, + 0x81C, 0xF30C0001, + 0x81C, 0xF20E0001, + 0x81C, 0xF1100001, + 0x81C, 0xF0120001, + 0x81C, 0xEF140001, + 0x81C, 0xEE160001, + 0x81C, 0xED180001, + 0x81C, 0xEC1A0001, + 0x81C, 0xEB1C0001, + 0x81C, 0xEA1E0001, + 0x81C, 0xCD200001, + 0x81C, 0xCC220001, + 0x81C, 0xCB240001, + 0x81C, 0xCA260001, + 0x81C, 0xC9280001, + 0x81C, 0xC82A0001, + 0x81C, 0xC72C0001, + 0x81C, 0xC62E0001, + 0x81C, 0xA5300001, + 0x81C, 0xA4320001, + 0x81C, 0xA3340001, + 0x81C, 0xA2360001, + 0x81C, 0x88380001, + 0x81C, 0x873A0001, + 0x81C, 0x863C0001, + 0x81C, 0x853E0001, + 0x81C, 0x84400001, + 0x81C, 0x83420001, + 0x81C, 0x82440001, + 0x81C, 0x81460001, + 0x81C, 0x48480001, + 0x81C, 0x474A0001, + 0x81C, 0x464C0001, + 0x81C, 0x454E0001, + 0x81C, 0x44500001, + 0x81C, 0x43520001, + 0x81C, 0x42540001, + 0x81C, 0x41560001, + 0x81C, 0x25580001, + 0x81C, 0x245A0001, + 0x81C, 0x235C0001, + 0x81C, 0x225E0001, + 0x81C, 0x21600001, + 0x81C, 0x21620001, + 0x81C, 0x21640001, + 0x81C, 0x21660001, + 0x81C, 0x21680001, + 0x81C, 0x216A0001, + 0x81C, 0x236C0001, + 0x81C, 0x226E0001, + 0x81C, 0x21700001, + 0x81C, 0x21720001, + 0x81C, 0x21740001, + 0x81C, 0x21760001, + 0x81C, 0x21780001, + 0x81C, 0x217A0001, + 0x81C, 0x217C0001, + 0x81C, 0x217E0001, + 0xA0000000, 0x00000000, + 0x81C, 0xFF000001, + 0x81C, 0xFF020001, + 0x81C, 0xFF040001, + 0x81C, 0xFF060001, + 0x81C, 0xFF080001, + 0x81C, 0xFE0A0001, + 0x81C, 0xFD0C0001, + 0x81C, 0xFC0E0001, + 0x81C, 0xFB100001, + 0x81C, 0xFA120001, + 0x81C, 0xF9140001, + 0x81C, 0xF8160001, + 0x81C, 0xF7180001, + 0x81C, 0xF61A0001, + 0x81C, 0xF51C0001, + 0x81C, 0xF41E0001, + 0x81C, 0xF3200001, + 0x81C, 0xF2220001, + 0x81C, 0xF1240001, + 0x81C, 0xF0260001, + 0x81C, 0xEF280001, + 0x81C, 0xEE2A0001, + 0x81C, 0xED2C0001, + 0x81C, 0xEC2E0001, + 0x81C, 0xEB300001, + 0x81C, 0xEA320001, + 0x81C, 0xE9340001, + 0x81C, 0xE8360001, + 0x81C, 0xE7380001, + 0x81C, 0xE63A0001, + 0x81C, 0xE53C0001, + 0x81C, 0xC73E0001, + 0x81C, 0xC6400001, + 0x81C, 0xC5420001, + 0x81C, 0xC4440001, + 0x81C, 0xC3460001, + 0x81C, 0xC2480001, + 0x81C, 0xC14A0001, + 0x81C, 0xA74C0001, + 0x81C, 0xA64E0001, + 0x81C, 0xA5500001, + 0x81C, 0xA4520001, + 0x81C, 0xA3540001, + 0x81C, 0xA2560001, + 0x81C, 0xA1580001, + 0x81C, 0x675A0001, + 0x81C, 0x665C0001, + 0x81C, 0x655E0001, + 0x81C, 0x64600001, + 0x81C, 0x63620001, + 0x81C, 0x48640001, + 0x81C, 0x47660001, + 0x81C, 0x46680001, + 0x81C, 0x456A0001, + 0x81C, 0x446C0001, + 0x81C, 0x436E0001, + 0x81C, 0x42700001, + 0x81C, 0x41720001, + 0x81C, 0x41740001, + 0x81C, 0x41760001, + 0x81C, 0x41780001, + 0x81C, 0x417A0001, + 0x81C, 0x417C0001, + 0x81C, 0x417E0001, + 0xB0000000, 0x00000000, + 0x80000004, 0x00000000, 0x40000000, 0x00000000, 0x81C, 0xFC800001, 0x81C, 0xFB820001, 0x81C, 0xFA840001, @@ -3176,7 +2407,7 @@ u32 RTL8812AE_AGC_TAB_ARRAY[] = { 0x81C, 0x01FA0001, 0x81C, 0x01FC0001, 0x81C, 0x01FE0001, - 0xCDCDCDCD, 0xCDCD, + 0xA0000000, 0x00000000, 0x81C, 0xFF800001, 0x81C, 0xFF820001, 0x81C, 0xFF840001, @@ -3241,14 +2472,16 @@ u32 RTL8812AE_AGC_TAB_ARRAY[] = { 0x81C, 0x01FA0001, 0x81C, 0x01FC0001, 0x81C, 0x01FE0001, - 0xFF0F0180, 0xDEAD, + 0xB0000000, 0x00000000, 0xC50, 0x00000022, 0xC50, 0x00000020, 0xE50, 0x00000022, 0xE50, 0x00000020, - }; +u32 RTL8812AE_AGC_TAB_1TARRAYLEN = + sizeof(RTL8812AE_AGC_TAB_ARRAY) / sizeof(u32); + u32 RTL8821AE_AGC_TAB_ARRAY[] = { 0x81C, 0xBF000001, 0x81C, 0xBF020001, @@ -3730,9 +2963,9 @@ u8 *RTL8812AE_TXPWR_LMT[] = { "FCC", "5G", "20M", "OFDM", "1T", "100", "30", "ETSI", "5G", "20M", "OFDM", "1T", "100", "32", "MKK", "5G", "20M", "OFDM", "1T", "100", "32", - "FCC", "5G", "20M", "OFDM", "1T", "114", "30", - "ETSI", "5G", "20M", "OFDM", "1T", "114", "32", - "MKK", "5G", "20M", "OFDM", "1T", "114", "32", + "FCC", "5G", "20M", "OFDM", "1T", "104", "30", + "ETSI", "5G", "20M", "OFDM", "1T", "104", "32", + "MKK", "5G", "20M", "OFDM", "1T", "104", "32", "FCC", "5G", "20M", "OFDM", "1T", "108", "32", "ETSI", "5G", "20M", "OFDM", "1T", "108", "32", "MKK", "5G", "20M", "OFDM", "1T", "108", "32", @@ -3802,9 +3035,9 @@ u8 *RTL8812AE_TXPWR_LMT[] = { "FCC", "5G", "20M", "HT", "1T", "100", "30", "ETSI", "5G", "20M", "HT", "1T", "100", "32", "MKK", "5G", "20M", "HT", "1T", "100", "32", - "FCC", "5G", "20M", "HT", "1T", "114", "30", - "ETSI", "5G", "20M", "HT", "1T", "114", "32", - "MKK", "5G", "20M", "HT", "1T", "114", "32", + "FCC", "5G", "20M", "HT", "1T", "104", "30", + "ETSI", "5G", "20M", "HT", "1T", "104", "32", + "MKK", "5G", "20M", "HT", "1T", "104", "32", "FCC", "5G", "20M", "HT", "1T", "108", "32", "ETSI", "5G", "20M", "HT", "1T", "108", "32", "MKK", "5G", "20M", "HT", "1T", "108", "32", @@ -3874,9 +3107,9 @@ u8 *RTL8812AE_TXPWR_LMT[] = { "FCC", "5G", "20M", "HT", "2T", "100", "28", "ETSI", "5G", "20M", "HT", "2T", "100", "30", "MKK", "5G", "20M", "HT", "2T", "100", "30", - "FCC", "5G", "20M", "HT", "2T", "114", "28", - "ETSI", "5G", "20M", "HT", "2T", "114", "30", - "MKK", "5G", "20M", "HT", "2T", "114", "30", + "FCC", "5G", "20M", "HT", "2T", "104", "28", + "ETSI", "5G", "20M", "HT", "2T", "104", "30", + "MKK", "5G", "20M", "HT", "2T", "104", "30", "FCC", "5G", "20M", "HT", "2T", "108", "30", "ETSI", "5G", "20M", "HT", "2T", "108", "30", "MKK", "5G", "20M", "HT", "2T", "108", "30", @@ -4017,6 +3250,8 @@ u8 *RTL8812AE_TXPWR_LMT[] = { "MKK", "5G", "80M", "VHT", "2T", "155", "63" }; +u32 RTL8812AE_TXPWR_LMT_ARRAY_LEN = sizeof(RTL8812AE_TXPWR_LMT) / sizeof(u8 *); + u8 *RTL8821AE_TXPWR_LMT[] = { "FCC", "2.4G", "20M", "CCK", "1T", "01", "32", "ETSI", "2.4G", "20M", "CCK", "1T", "01", "32", diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.h index 192057af75dc..36c2388b60bc 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/table.h @@ -31,28 +31,27 @@ #include extern u32 RTL8821AE_PHY_REG_1TARRAYLEN; extern u32 RTL8821AE_PHY_REG_ARRAY[]; -#define RTL8812AEPHY_REG_1TARRAYLEN 490 +extern u32 RTL8812AE_PHY_REG_1TARRAYLEN; extern u32 RTL8812AE_PHY_REG_ARRAY[]; extern u32 RTL8821AE_PHY_REG_ARRAY_PGLEN; extern u32 RTL8821AE_PHY_REG_ARRAY_PG[]; -#define RTL8812AEPHY_REG_ARRAY_PGLEN 276 +extern u32 RTL8812AE_PHY_REG_ARRAY_PGLEN; extern u32 RTL8812AE_PHY_REG_ARRAY_PG[]; -/* #define RTL8723BE_RADIOA_1TARRAYLEN 206 */ -#define RTL8812AE_RADIOA_1TARRAYLEN 1264 +extern u32 RTL8812AE_RADIOA_1TARRAYLEN; extern u32 RTL8812AE_RADIOA_ARRAY[]; -#define RTL8812AE_RADIOB_1TARRAYLEN 1240 +extern u32 RTL8812AE_RADIOB_1TARRAYLEN; extern u32 RTL8812AE_RADIOB_ARRAY[]; extern u32 RTL8821AE_RADIOA_1TARRAYLEN; extern u32 RTL8821AE_RADIOA_ARRAY[]; extern u32 RTL8821AE_MAC_1T_ARRAYLEN; extern u32 RTL8821AE_MAC_REG_ARRAY[]; -#define RTL8812AEMAC_1T_ARRAYLEN 214 +extern u32 RTL8812AE_MAC_1T_ARRAYLEN; extern u32 RTL8812AE_MAC_REG_ARRAY[]; extern u32 RTL8821AE_AGC_TAB_1TARRAYLEN; extern u32 RTL8821AE_AGC_TAB_ARRAY[]; -#define RTL8812AEAGCTAB_1TARRAYLEN 1312 +extern u32 RTL8812AE_AGC_TAB_1TARRAYLEN; extern u32 RTL8812AE_AGC_TAB_ARRAY[]; -#define RTL8812AE_TXPWR_LMT_ARRAY_LEN 3948 +extern u32 RTL8812AE_TXPWR_LMT_ARRAY_LEN; extern u8 *RTL8812AE_TXPWR_LMT[]; extern u32 RTL8821AE_TXPWR_LMT_ARRAY_LEN; extern u8 *RTL8821AE_TXPWR_LMT[]; -- cgit From f70e4df2b384d21e36a7c30a591639592692e0ec Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 23 Feb 2017 11:19:56 -0600 Subject: rtlwifi: Add code to read new versions of firmware Changes in the drivers for RTL8723BE and RTL8821AE require corresponding changes in the firmware. This new firmware has been accepted into the Linux firmware repo. To handle the case where the kernel has been updated before the firmware, the new versions have been given new names. The code will attempt to read the new name, and fall back to the old one if the new one is not available. Signed-off-by: Larry Finger Cc: Ping-Ke Shih Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c | 15 ++++++++++++--- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c | 15 ++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c index 92dbfa8f297f..8c0ac96b5430 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c @@ -91,7 +91,7 @@ int rtl8723be_init_sw_vars(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); - char *fw_name = "rtlwifi/rtl8723befw.bin"; + char *fw_name = "rtlwifi/rtl8723befw_36.bin"; rtl8723be_bt_reg_init(hw); rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer(); @@ -187,8 +187,16 @@ int rtl8723be_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->io.dev, GFP_KERNEL, hw, rtl_fw_cb); if (err) { - pr_err("Failed to request firmware!\n"); - return 1; + /* Failed to get firmware. Check if old version available */ + fw_name = "rtlwifi/rtl8723befw.bin"; + pr_info("Using firmware %s\n", fw_name); + err = request_firmware_nowait(THIS_MODULE, 1, fw_name, + rtlpriv->io.dev, GFP_KERNEL, hw, + rtl_fw_cb); + if (err) { + pr_err("Failed to request firmware!\n"); + return 1; + } } return 0; } @@ -384,6 +392,7 @@ MODULE_AUTHOR("Realtek WlanFAE "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek 8723BE 802.11n PCI wireless"); MODULE_FIRMWARE("rtlwifi/rtl8723befw.bin"); +MODULE_FIRMWARE("rtlwifi/rtl8723befw_36.bin"); module_param_named(swenc, rtl8723be_mod_params.sw_crypto, bool, 0444); module_param_named(debug_level, rtl8723be_mod_params.debug_level, int, 0644); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c index 77cf3b2cd3f1..abaf34cb1433 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/sw.c @@ -203,7 +203,7 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) fw_name = "rtlwifi/rtl8812aefw.bin"; wowlan_fw_name = "rtlwifi/rtl8812aefw_wowlan.bin"; } else { - fw_name = "rtlwifi/rtl8821aefw.bin"; + fw_name = "rtlwifi/rtl8821aefw_29.bin"; wowlan_fw_name = "rtlwifi/rtl8821aefw_wowlan.bin"; } @@ -214,8 +214,16 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->io.dev, GFP_KERNEL, hw, rtl_fw_cb); if (err) { - pr_err("Failed to request normal firmware!\n"); - return 1; + /* Failed to get firmware. Check if old version available */ + fw_name = "rtlwifi/rtl8821aefw.bin"; + pr_info("Using firmware %s\n", fw_name); + err = request_firmware_nowait(THIS_MODULE, 1, fw_name, + rtlpriv->io.dev, GFP_KERNEL, hw, + rtl_fw_cb); + if (err) { + pr_err("Failed to request normal firmware!\n"); + return 1; + } } /*load wowlan firmware*/ pr_info("Using firmware %s\n", wowlan_fw_name); @@ -428,6 +436,7 @@ MODULE_AUTHOR("Realtek WlanFAE "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek 8821ae 802.11ac PCI wireless"); MODULE_FIRMWARE("rtlwifi/rtl8821aefw.bin"); +MODULE_FIRMWARE("rtlwifi/rtl8821aefw_29.bin"); module_param_named(swenc, rtl8821ae_mod_params.sw_crypto, bool, 0444); module_param_named(debug_level, rtl8821ae_mod_params.debug_level, int, 0644); -- cgit From 74a7dfbcc64986504dbf9f5f7c7cca521db4d230 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Thu, 23 Feb 2017 11:19:57 -0600 Subject: rtlwifi: Add QoS-NULL and BT-QoS-NULL to reserved page. Two additional preset responses are added to the reserved page on the wifi device. Signed-off-by: Ping-Ke Shih Signed-off-by: Larry Finger Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/rtl8192ee/fw.c | 64 +++++++- .../net/wireless/realtek/rtlwifi/rtl8192ee/fw.h | 4 + .../net/wireless/realtek/rtlwifi/rtl8192ee/hw.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8723be/fw.c | 69 ++++++++- .../net/wireless/realtek/rtlwifi/rtl8723be/fw.h | 4 + .../net/wireless/realtek/rtlwifi/rtl8821ae/fw.c | 165 ++++++++++++++++++--- .../net/wireless/realtek/rtlwifi/rtl8821ae/fw.h | 2 + .../net/wireless/realtek/rtlwifi/rtl8821ae/hw.c | 8 +- 8 files changed, 287 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.c index 9fec345a42a0..1f42ce5f8f27 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.c @@ -468,8 +468,10 @@ void rtl92ee_set_fw_media_status_rpt_cmd(struct ieee80211_hw *hw, u8 mstatus) #define PSPOLL_PG 2 #define NULL_PG 3 #define PROBERSP_PG 4 /* ->5 */ +#define QOS_NULL_PG 6 +#define BT_QOS_NULL_PG 7 -#define TOTAL_RESERVED_PKT_LEN 768 +#define TOTAL_RESERVED_PKT_LEN 1024 static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = { /* page 0 beacon */ @@ -570,6 +572,42 @@ static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1A, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* page 6 qos null data */ + 0xC8, 0x01, 0x00, 0x00, 0x84, 0xC9, 0xB2, 0xA7, + 0xB3, 0x6E, 0x00, 0xE0, 0x4C, 0x02, 0x51, 0x02, + 0x84, 0xC9, 0xB2, 0xA7, 0xB3, 0x6E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1A, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* page 7 BT-qos null data */ + 0xC8, 0x01, 0x00, 0x00, 0x84, 0xC9, 0xB2, 0xA7, + 0xB3, 0x6E, 0x00, 0xE0, 0x4C, 0x02, 0x51, 0x02, + 0x84, 0xC9, 0xB2, 0xA7, 0xB3, 0x6E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -595,6 +633,8 @@ void rtl92ee_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished) u8 *p_pspoll; u8 *nullfunc; u8 *p_probersp; + u8 *qosnull; + u8 *btqosnull; /*--------------------------------------------------------- * (1) beacon *--------------------------------------------------------- @@ -636,6 +676,28 @@ void rtl92ee_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished) SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(u1rsvdpageloc, PROBERSP_PG); + /*--------------------------------------------------------- + * (5) QoS null data + *---------------------------------------------------------- + */ + qosnull = &reserved_page_packet[QOS_NULL_PG * 128]; + SET_80211_HDR_ADDRESS1(qosnull, mac->bssid); + SET_80211_HDR_ADDRESS2(qosnull, mac->mac_addr); + SET_80211_HDR_ADDRESS3(qosnull, mac->bssid); + + SET_H2CCMD_RSVDPAGE_LOC_QOS_NULL_DATA(u1rsvdpageloc, QOS_NULL_PG); + + /*--------------------------------------------------------- + * (6) BT QoS null data + *---------------------------------------------------------- + */ + btqosnull = &reserved_page_packet[BT_QOS_NULL_PG * 128]; + SET_80211_HDR_ADDRESS1(btqosnull, mac->bssid); + SET_80211_HDR_ADDRESS2(btqosnull, mac->mac_addr); + SET_80211_HDR_ADDRESS3(btqosnull, mac->bssid); + + SET_H2CCMD_RSVDPAGE_LOC_BT_QOS_NULL_DATA(u1rsvdpageloc, BT_QOS_NULL_PG); + totalpacketlen = TOTAL_RESERVED_PKT_LEN; RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD , diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.h index 72da3f92f02c..af8271967a88 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/fw.h @@ -165,6 +165,10 @@ enum rtl8192e_c2h_evt { SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) #define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) +#define SET_H2CCMD_RSVDPAGE_LOC_QOS_NULL_DATA(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 3, 0, 8, __val) +#define SET_H2CCMD_RSVDPAGE_LOC_BT_QOS_NULL_DATA(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 4, 0, 8, __val) /* _MEDIA_STATUS_RPT_PARM_CMD1 */ #define SET_H2CCMD_MSRRPT_PARM_OPMODE(__cmd, __val) \ diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c index 56ca7f5351ea..6f5098a18655 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/hw.c @@ -699,9 +699,9 @@ static bool _rtl92ee_llt_table_init(struct ieee80211_hw *hw) u8 txpktbuf_bndy; u8 u8tmp, testcnt = 0; - txpktbuf_bndy = 0xFA; + txpktbuf_bndy = 0xF7; - rtl_write_dword(rtlpriv, REG_RQPN, 0x80E90808); + rtl_write_dword(rtlpriv, REG_RQPN, 0x80E60808); rtl_write_byte(rtlpriv, REG_TRXFF_BNDY, txpktbuf_bndy); rtl_write_word(rtlpriv, REG_TRXFF_BNDY + 2, 0x3d00 - 1); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.c index c7ee9ba5e26e..4fc839b1d601 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.c @@ -284,8 +284,10 @@ void rtl8723be_set_fw_media_status_rpt_cmd(struct ieee80211_hw *hw, u8 mstatus) #define PSPOLL_PG 2 #define NULL_PG 3 #define PROBERSP_PG 4 /* ->5 */ +#define QOS_NULL_PG 6 +#define BT_QOS_NULL_PG 7 -#define TOTAL_RESERVED_PKT_LEN 768 +#define TOTAL_RESERVED_PKT_LEN 1024 /* can be up to 1280 (tx_bndy=245) */ static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = { /* page 0 beacon */ @@ -390,11 +392,48 @@ static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1A, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* page 6 qos null data */ + 0xC8, 0x01, 0x00, 0x00, 0x84, 0xC9, 0xB2, 0xA7, + 0xB3, 0x6E, 0x00, 0xE0, 0x4C, 0x02, 0x51, 0x02, + 0x84, 0xC9, 0xB2, 0xA7, 0xB3, 0x6E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1A, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* page 7 BT-qos null data */ + 0xC8, 0x01, 0x00, 0x00, 0x84, 0xC9, 0xB2, 0xA7, + 0xB3, 0x6E, 0x00, 0xE0, 0x4C, 0x02, 0x51, 0x02, + 0x84, 0xC9, 0xB2, 0xA7, 0xB3, 0x6E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; void rtl8723be_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, @@ -413,6 +452,8 @@ void rtl8723be_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, u8 *p_pspoll; u8 *nullfunc; u8 *p_probersp; + u8 *qosnull; + u8 *btqosnull; /*--------------------------------------------------------- * (1) beacon *--------------------------------------------------------- @@ -454,6 +495,28 @@ void rtl8723be_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(u1rsvdpageloc, PROBERSP_PG); + /*--------------------------------------------------------- + * (5) QoS Null + *--------------------------------------------------------- + */ + qosnull = &reserved_page_packet[QOS_NULL_PG * 128]; + SET_80211_HDR_ADDRESS1(qosnull, mac->bssid); + SET_80211_HDR_ADDRESS2(qosnull, mac->mac_addr); + SET_80211_HDR_ADDRESS3(qosnull, mac->bssid); + + SET_H2CCMD_RSVDPAGE_LOC_QOS_NULL_DATA(u1rsvdpageloc, QOS_NULL_PG); + + /*--------------------------------------------------------- + * (5) QoS Null + *--------------------------------------------------------- + */ + btqosnull = &reserved_page_packet[BT_QOS_NULL_PG * 128]; + SET_80211_HDR_ADDRESS1(btqosnull, mac->bssid); + SET_80211_HDR_ADDRESS2(btqosnull, mac->mac_addr); + SET_80211_HDR_ADDRESS3(btqosnull, mac->bssid); + + SET_H2CCMD_RSVDPAGE_LOC_BT_QOS_NULL_DATA(u1rsvdpageloc, BT_QOS_NULL_PG); + totalpacketlen = TOTAL_RESERVED_PKT_LEN; RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD, @@ -461,7 +524,7 @@ void rtl8723be_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, &reserved_page_packet[0], totalpacketlen); RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, "rtl8723be_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL\n", - u1rsvdpageloc, 3); + u1rsvdpageloc, sizeof(u1rsvdpageloc)); skb = dev_alloc_skb(totalpacketlen); memcpy((u8 *)skb_put(skb, totalpacketlen), @@ -476,7 +539,7 @@ void rtl8723be_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Set RSVD page location to Fw.\n"); RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, "H2C_RSVDPAGE:\n", - u1rsvdpageloc, 3); + u1rsvdpageloc, sizeof(u1rsvdpageloc)); rtl8723be_fill_h2c_cmd(hw, H2C_8723B_RSVDPAGE, sizeof(u1rsvdpageloc), u1rsvdpageloc); } else diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.h index c652fa1339a7..2482b3bc2bfa 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/fw.h @@ -139,6 +139,10 @@ enum rtl8723b_c2h_evt { SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) #define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) +#define SET_H2CCMD_RSVDPAGE_LOC_QOS_NULL_DATA(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 3, 0, 8, __val) +#define SET_H2CCMD_RSVDPAGE_LOC_BT_QOS_NULL_DATA(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 4, 0, 8, __val) void rtl8723be_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c index a504dfae4ed3..73350103b736 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.c @@ -678,12 +678,13 @@ void rtl8821ae_set_fw_global_info_cmd(struct ieee80211_hw *hw) #define PSPOLL_PG 1 #define NULL_PG 2 #define QOSNULL_PG 3 -#define ARPRESP_PG 4 -#define REMOTE_PG 5 -#define GTKEXT_PG 6 +#define BT_QOSNULL_PG 4 +#define ARPRESP_PG 5 +#define REMOTE_PG 6 +#define GTKEXT_PG 7 -#define TOTAL_RESERVED_PKT_LEN_8812 3584 -#define TOTAL_RESERVED_PKT_LEN_8821 1792 +#define TOTAL_RESERVED_PKT_LEN_8812 4096 +#define TOTAL_RESERVED_PKT_LEN_8821 2048 static u8 reserved_page_packet_8821[TOTAL_RESERVED_PKT_LEN_8821] = { /* page 0: beacon */ @@ -813,13 +814,46 @@ static u8 reserved_page_packet_8821[TOTAL_RESERVED_PKT_LEN_8821] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1A, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* page 4: BT qos null data */ + 0xC8, 0x01, 0x00, 0x00, 0x84, 0xC9, 0xB2, 0xA7, + 0xB3, 0x6E, 0x00, 0xE0, 0x4C, 0x02, 0x51, 0x02, + 0x84, 0xC9, 0xB2, 0xA7, 0xB3, 0x6E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* page 4~6 is for wowlan */ - /* page 4: ARP resp */ + /* page 5~7 is for wowlan */ + /* page 5: ARP resp */ 0x08, 0x01, 0x00, 0x00, 0x84, 0xC9, 0xB2, 0xA7, 0xB3, 0x6E, 0x00, 0xE0, 0x4C, 0x02, 0x51, 0x02, 0x84, 0xC9, 0xB2, 0xA7, 0xB3, 0x6E, 0x00, 0x00, @@ -852,7 +886,7 @@ static u8 reserved_page_packet_8821[TOTAL_RESERVED_PKT_LEN_8821] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* page 5: H2C_REMOTE_WAKE_CTRL_INFO */ + /* page 6: H2C_REMOTE_WAKE_CTRL_INFO */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -885,7 +919,7 @@ static u8 reserved_page_packet_8821[TOTAL_RESERVED_PKT_LEN_8821] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* page 6: Rsvd GTK extend memory (zero memory) */ + /* page 7: Rsvd GTK extend memory (zero memory) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1176,13 +1210,78 @@ static u8 reserved_page_packet_8812[TOTAL_RESERVED_PKT_LEN_8812] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1A, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* page 4: BT Qos null data */ + 0xC8, 0x01, 0x00, 0x00, 0x84, 0xC9, 0xB2, 0xA7, + 0xB3, 0x6E, 0x00, 0xE0, 0x4C, 0x02, 0x51, 0x02, + 0x84, 0xC9, 0xB2, 0xA7, 0xB3, 0x6E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* page 4~6 is for wowlan */ - /* page 4: ARP resp */ + /* page 5~7 is for wowlan */ + /* page 5: ARP resp */ 0x08, 0x01, 0x00, 0x00, 0x84, 0xC9, 0xB2, 0xA7, 0xB3, 0x6E, 0x00, 0xE0, 0x4C, 0x02, 0x51, 0x02, 0x84, 0xC9, 0xB2, 0xA7, 0xB3, 0x6E, 0x00, 0x00, @@ -1247,7 +1346,7 @@ static u8 reserved_page_packet_8812[TOTAL_RESERVED_PKT_LEN_8812] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* page 5: H2C_REMOTE_WAKE_CTRL_INFO */ + /* page 6: H2C_REMOTE_WAKE_CTRL_INFO */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1312,7 +1411,7 @@ static u8 reserved_page_packet_8812[TOTAL_RESERVED_PKT_LEN_8812] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* page 6: Rsvd GTK extend memory (zero memory) */ + /* page 7: Rsvd GTK extend memory (zero memory) */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1394,6 +1493,7 @@ void rtl8812ae_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, u8 *p_pspoll; u8 *nullfunc; u8 *qosnull; + u8 *btqosnull; u8 *arpresp; /*--------------------------------------------------------- @@ -1441,12 +1541,23 @@ void rtl8812ae_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, SET_H2CCMD_RSVDPAGE_LOC_QOS_NULL_DATA(u1RsvdPageLoc, QOSNULL_PG); + /*--------------------------------------------------------- + * (5) BT Qos null data + *---------------------------------------------------------- + */ + btqosnull = &reserved_page_packet_8812[BT_QOSNULL_PG * 512]; + SET_80211_HDR_ADDRESS1(btqosnull, mac->bssid); + SET_80211_HDR_ADDRESS2(btqosnull, mac->mac_addr); + SET_80211_HDR_ADDRESS3(btqosnull, mac->bssid); + + SET_H2CCMD_RSVDPAGE_LOC_BT_QOS_NULL_DATA(u1RsvdPageLoc, BT_QOSNULL_PG); + if (!dl_whole_packets) { - totalpacketlen = 512 * (QOSNULL_PG + 1) - 40; + totalpacketlen = 512 * (BT_QOSNULL_PG + 1) - 40; goto out; } /*--------------------------------------------------------- - * (5) ARP Resp + * (6) ARP Resp *---------------------------------------------------------- */ arpresp = &reserved_page_packet_8812[ARPRESP_PG * 512]; @@ -1457,14 +1568,14 @@ void rtl8812ae_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, SET_8821AE_H2CCMD_AOAC_RSVDPAGE_LOC_ARP_RSP(u1RsvdPageLoc2, ARPRESP_PG); /*--------------------------------------------------------- - * (6) Remote Wake Ctrl + * (7) Remote Wake Ctrl *---------------------------------------------------------- */ SET_8821AE_H2CCMD_AOAC_RSVDPAGE_LOC_REMOTE_WAKE_CTRL_INFO(u1RsvdPageLoc2, REMOTE_PG); /*--------------------------------------------------------- - * (7) GTK Ext Memory + * (8) GTK Ext Memory *---------------------------------------------------------- */ SET_8821AE_H2CCMD_AOAC_RSVDPAGE_LOC_GTK_EXT_MEM(u1RsvdPageLoc2, GTKEXT_PG); @@ -1518,6 +1629,7 @@ void rtl8821ae_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, u8 *p_pspoll; u8 *nullfunc; u8 *qosnull; + u8 *btqosnull; u8 *arpresp; /*--------------------------------------------------------- @@ -1565,12 +1677,23 @@ void rtl8821ae_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, SET_H2CCMD_RSVDPAGE_LOC_QOS_NULL_DATA(u1RsvdPageLoc, QOSNULL_PG); + /*--------------------------------------------------------- + * (5) Qos null data + *---------------------------------------------------------- + */ + btqosnull = &reserved_page_packet_8821[BT_QOSNULL_PG * 256]; + SET_80211_HDR_ADDRESS1(btqosnull, mac->bssid); + SET_80211_HDR_ADDRESS2(btqosnull, mac->mac_addr); + SET_80211_HDR_ADDRESS3(btqosnull, mac->bssid); + + SET_H2CCMD_RSVDPAGE_LOC_BT_QOS_NULL_DATA(u1RsvdPageLoc, BT_QOSNULL_PG); + if (!dl_whole_packets) { - totalpacketlen = 256 * (QOSNULL_PG + 1) - 40; + totalpacketlen = 256 * (BT_QOSNULL_PG + 1) - 40; goto out; } /*--------------------------------------------------------- - * (5) ARP Resp + * (6) ARP Resp *---------------------------------------------------------- */ arpresp = &reserved_page_packet_8821[ARPRESP_PG * 256]; @@ -1581,14 +1704,14 @@ void rtl8821ae_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, SET_8821AE_H2CCMD_AOAC_RSVDPAGE_LOC_ARP_RSP(u1RsvdPageLoc2, ARPRESP_PG); /*--------------------------------------------------------- - * (6) Remote Wake Ctrl + * (7) Remote Wake Ctrl *---------------------------------------------------------- */ SET_8821AE_H2CCMD_AOAC_RSVDPAGE_LOC_REMOTE_WAKE_CTRL_INFO(u1RsvdPageLoc2, REMOTE_PG); /*--------------------------------------------------------- - * (7) GTK Ext Memory + * (8) GTK Ext Memory *---------------------------------------------------------- */ SET_8821AE_H2CCMD_AOAC_RSVDPAGE_LOC_GTK_EXT_MEM(u1RsvdPageLoc2, GTKEXT_PG); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.h index 90a98ed879f7..98d871afd92a 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/fw.h @@ -229,6 +229,8 @@ enum rtl8821a_h2c_cmd { SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) #define SET_H2CCMD_RSVDPAGE_LOC_QOS_NULL_DATA(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+3, 0, 8, __val) +#define SET_H2CCMD_RSVDPAGE_LOC_BT_QOS_NULL_DATA(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd) + 4, 0, 8, __val) /* _MEDIA_STATUS_RPT_PARM_CMD1 */ #define SET_H2CCMD_MSRRPT_PARM_OPMODE(__cmd, __value) \ diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c index 363d2f28da1f..3571ce4bd276 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c @@ -842,12 +842,8 @@ static bool _rtl8821ae_llt_table_init(struct ieee80211_hw *hw) bool status; maxpage = 255; - txpktbuf_bndy = 0xF8; - rqpn = 0x80e70808; - if (rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8812AE) { - txpktbuf_bndy = 0xFA; - rqpn = 0x80e90808; - } + txpktbuf_bndy = 0xF7; + rqpn = 0x80e60808; rtl_write_byte(rtlpriv, REG_TRXFF_BNDY, txpktbuf_bndy); rtl_write_word(rtlpriv, REG_TRXFF_BNDY + 2, MAX_RX_DMA_BUFFER_SIZE - 1); -- cgit From 48d1977655341f1e004beb81eda8e93316c55963 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Fri, 3 Mar 2017 11:33:30 +0100 Subject: bcma: drop unneeded check for CONFIG_OF_IRQ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have the same check in bcma_of_get_irq which really calls symbols available with CONFIG_OF_IRQ only. It appears this duplicated check was accidentally added in commit c58d900cc96a ("bcma: fix building without OF_IRQ"). The rest of code in bcma_of_fill_device should work fine without CONFIG_OF_IRQ. Signed-off-by: Rafał Miłecki Cc: Arnd Bergmann Acked-by: Arnd Bergmann Signed-off-by: Kalle Valo --- drivers/bcma/main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 12da68ec48ba..8957137fc368 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -201,9 +201,6 @@ static void bcma_of_fill_device(struct device *parent, { struct device_node *node; - if (!IS_ENABLED(CONFIG_OF_IRQ)) - return; - node = bcma_of_find_child_device(parent, core); if (node) core->dev.of_node = node; -- cgit From 5e48a4cd2ee916a98fbc36c944b21a7c935cf4f4 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Fri, 3 Mar 2017 11:34:14 +0100 Subject: bcma: use helper function to set core dev's parent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A tiny code deduplication thanks to the bcma_bus_get_host_dev. Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo --- drivers/bcma/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 8957137fc368..e6f3810d594d 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -239,17 +239,16 @@ void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core) core->dev.release = bcma_release_core_dev; core->dev.bus = &bcma_bus_type; dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index); + core->dev.parent = bcma_bus_get_host_dev(bus); switch (bus->hosttype) { case BCMA_HOSTTYPE_PCI: - core->dev.parent = &bus->host_pci->dev; core->dma_dev = &bus->host_pci->dev; core->irq = bus->host_pci->irq; break; case BCMA_HOSTTYPE_SOC: if (IS_ENABLED(CONFIG_OF) && bus->host_pdev) { core->dma_dev = &bus->host_pdev->dev; - core->dev.parent = &bus->host_pdev->dev; if (core->dev.parent) bcma_of_fill_device(core->dev.parent, core); } else { -- cgit From f825f6ed20e37a20ad5feaf607acfb84bfdf9d99 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Fri, 3 Mar 2017 11:34:15 +0100 Subject: bcma: fill core OF info independently of bus type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCI devices can be described in DT as well so we should always execute relevant code. This will make bcma e.g. set of_node for cores described in DT. Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo --- drivers/bcma/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index e6f3810d594d..e6986c7608f1 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -240,6 +240,8 @@ void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core) core->dev.bus = &bcma_bus_type; dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index); core->dev.parent = bcma_bus_get_host_dev(bus); + if (core->dev.parent) + bcma_of_fill_device(core->dev.parent, core); switch (bus->hosttype) { case BCMA_HOSTTYPE_PCI: @@ -249,8 +251,6 @@ void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core) case BCMA_HOSTTYPE_SOC: if (IS_ENABLED(CONFIG_OF) && bus->host_pdev) { core->dma_dev = &bus->host_pdev->dev; - if (core->dev.parent) - bcma_of_fill_device(core->dev.parent, core); } else { core->dev.dma_mask = &core->dev.coherent_dma_mask; core->dma_dev = &core->dev; -- cgit From f1ac3aa212af6dd0a36dc07a63f95f91be6f4935 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Fri, 24 Feb 2017 17:32:46 +0100 Subject: brcmfmac: always print error when PSM's watchdog fires MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far we were attaching BRCMF_E_PSM_WATCHDOG event listener in brcmf_debug_attach which gets compiled only with CONFIG_BRCMDBG. This event means something went wrong and firmware / hardware usually can't be expected to work (reliably). Such a problem is significant for user experience so I believe we should print an error unconditionally (even with debugging disabled). What can be indeed optional is dumping bus memory as this is clearly part of debugging process. In the future we may also try to extend this listener by trying to recover from the error or at least signal it to the cfg80211. Signed-off-by: Rafał Miłecki Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/core.c | 22 ++++++++++++++++++ .../wireless/broadcom/brcm80211/brcmfmac/debug.c | 26 +++------------------- .../wireless/broadcom/brcm80211/brcmfmac/debug.h | 9 ++++++++ 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 60da86a8d95b..2f2f3a5ad86a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -738,6 +738,24 @@ void brcmf_remove_interface(struct brcmf_if *ifp, bool rtnl_locked) brcmf_del_if(ifp->drvr, ifp->bsscfgidx, rtnl_locked); } +static int brcmf_psm_watchdog_notify(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data) +{ + int err; + + brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx); + + brcmf_err("PSM's watchdog has fired!\n"); + + err = brcmf_debug_create_memdump(ifp->drvr->bus_if, data, + evtmsg->datalen); + if (err) + brcmf_err("Failed to get memory dump, %d\n", err); + + return err; +} + #ifdef CONFIG_INET #define ARPOL_MAX_ENTRIES 8 static int brcmf_inetaddr_changed(struct notifier_block *nb, @@ -917,6 +935,10 @@ int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings) goto fail; } + /* Attach to events important for core code */ + brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG, + brcmf_psm_watchdog_notify); + /* attach firmware event handler */ brcmf_fweh_attach(drvr); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c index f4644cf371c7..1447a8352383 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c @@ -27,8 +27,8 @@ static struct dentry *root_folder; -static int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, - size_t len) +int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, + size_t len) { void *dump; size_t ramsize; @@ -54,24 +54,6 @@ static int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, return 0; } -static int brcmf_debug_psm_watchdog_notify(struct brcmf_if *ifp, - const struct brcmf_event_msg *evtmsg, - void *data) -{ - int err; - - brcmf_dbg(TRACE, "enter: bsscfgidx=%d\n", ifp->bsscfgidx); - - brcmf_err("PSM's watchdog has fired!\n"); - - err = brcmf_debug_create_memdump(ifp->drvr->bus_if, data, - evtmsg->datalen); - if (err) - brcmf_err("Failed to get memory dump, %d\n", err); - - return err; -} - void brcmf_debugfs_init(void) { root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL); @@ -99,9 +81,7 @@ int brcmf_debug_attach(struct brcmf_pub *drvr) if (IS_ERR(drvr->dbgfs_dir)) return PTR_ERR(drvr->dbgfs_dir); - - return brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG, - brcmf_debug_psm_watchdog_notify); + return 0; } void brcmf_debug_detach(struct brcmf_pub *drvr) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h index 066126123e96..389166abb520 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h @@ -99,6 +99,7 @@ do { \ extern int brcmf_msg_level; +struct brcmf_bus; struct brcmf_pub; #ifdef DEBUG void brcmf_debugfs_init(void); @@ -108,6 +109,8 @@ void brcmf_debug_detach(struct brcmf_pub *drvr); struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr); int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, int (*read_fn)(struct seq_file *seq, void *data)); +int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, + size_t len); #else static inline void brcmf_debugfs_init(void) { @@ -128,6 +131,12 @@ int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, { return 0; } +static inline +int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, + size_t len) +{ + return 0; +} #endif #endif /* BRCMFMAC_DEBUG_H */ -- cgit From d79fe4cb70d8deab7b8dc1de547ed4b915574414 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 8 Mar 2017 14:50:15 +0100 Subject: brcmfmac: Do not print the firmware version as an error Using pr_err for things which are not errors is a bad idea. E.g. it will cause the plymouth bootsplash screen to drop back to the text console so that the user can see the error, which is not what we normally want to happen. Instead add a new brcmf_info macro and use that. Signed-off-by: Hans de Goede Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index 33b133f7e63a..7a2b49587b4d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -161,7 +161,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) strsep(&ptr, "\n"); /* Print fw version info */ - brcmf_err("Firmware version = %s\n", buf); + brcmf_info("Firmware version = %s\n", buf); /* locate firmware version number for ethtool */ ptr = strrchr(buf, ' ') + 1; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h index 389166abb520..fe264a5798f1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h @@ -59,6 +59,10 @@ void __brcmf_err(const char *func, const char *fmt, ...); } while (0) #if defined(DEBUG) || defined(CONFIG_BRCM_TRACING) + +/* For debug/tracing purposes treat info messages as errors */ +#define brcmf_info brcmf_err + __printf(3, 4) void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...); #define brcmf_dbg(level, fmt, ...) \ @@ -77,6 +81,11 @@ do { \ #else /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ +#define brcmf_info(fmt, ...) \ + do { \ + pr_info("%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + #define brcmf_dbg(level, fmt, ...) no_printk(fmt, ##__VA_ARGS__) #define BRCMF_DATA_ON() 0 -- cgit From 26e537884a8ef451f5c60f6949b1615069931ffa Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 8 Mar 2017 14:50:16 +0100 Subject: brcmfmac: Do not complain about country code "00" The country code gets set to "00" by default at boot, ignore this rather then logging an error about it. Signed-off-by: Hans de Goede Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 944b83cfc519..7765ad09d793 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -6736,6 +6736,10 @@ static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, s32 err; int i; + /* The country code gets set to "00" by default at boot, ignore */ + if (req->alpha2[0] == '0' && req->alpha2[1] == '0') + return; + /* ignore non-ISO3166 country codes */ for (i = 0; i < sizeof(req->alpha2); i++) if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { -- cgit From b9472a2e3e452c414634b3ccb1ef6c4098878686 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 8 Mar 2017 14:50:17 +0100 Subject: brcmfmac: Handle status == BRCMF_E_STATUS_ABORT in cfg80211_escan_handler If a scan gets aborted BRCMF_SCAN_STATUS_BUSY gets cleared in cfg->scan_status and when we receive an abort event from the firmware the BRCMF_SCAN_STATUS_BUSY check in the cfg80211_escan_handler will trigger resulting in multiple errors getting logged. Check for a status of BRCMF_E_STATUS_ABORT and in this case simply cleanly exit the cfg80211_escan_handler. This also avoids a BRCMF_E_STATUS_ABORT event arriving after a new scan has been started causing the new scan to complete prematurely without any data. Signed-off-by: Hans de Goede Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 7765ad09d793..4e1ca69f6aa1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3097,6 +3097,9 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, status = e->status; + if (status == BRCMF_E_STATUS_ABORT) + goto exit; + if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { brcmf_err("scan not ready, bsscfgidx=%d\n", ifp->bsscfgidx); return -EPERM; -- cgit From 20ec4f57498f8770c7a1a3e2a316fa752a424178 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Fri, 10 Mar 2017 21:17:02 +0000 Subject: brcmfmac: move brcmf_txflowblock to bcdc layer brcmf_txflowblock is invoked by sdio and usb bus module which are using bcdc protocol. This patch makes it a bcdc API instead of a core module function. Reviewed-by: Arend Van Spriel Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c | 10 ++++++++++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h | 1 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 2 -- drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 10 ---------- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 5 +++-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 5 +++-- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index 384b1873e7e3..4c7dafa162ac 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -345,6 +345,16 @@ brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset, return brcmf_bus_txdata(drvr->bus_if, pktbuf); } +void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + brcmf_dbg(TRACE, "Enter\n"); + + brcmf_fws_bus_blocked(drvr, state); +} + static void brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, enum proto_addr_mode addr_mode) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h index 6003179c0ceb..b5282cb08199 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h @@ -19,6 +19,7 @@ #ifdef CONFIG_BRCMFMAC_PROTO_BCDC int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr); void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr); +void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state); #else static inline int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { return 0; } static inline void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) {} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 76693df34742..eeb6643eb1da 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -229,8 +229,6 @@ int brcmf_attach(struct device *dev, struct brcmf_mp_device *settings); void brcmf_detach(struct device *dev); /* Indication from bus module that dongle should be reset */ void brcmf_dev_reset(struct device *dev); -/* Indication from bus module to change flow-control state */ -void brcmf_txflowblock(struct device *dev, bool state); /* Notify the bus has transferred the tx packet to firmware */ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 2f2f3a5ad86a..59831dc446d6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -283,16 +283,6 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp, spin_unlock_irqrestore(&ifp->netif_stop_lock, flags); } -void brcmf_txflowblock(struct device *dev, bool state) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - - brcmf_dbg(TRACE, "Enter\n"); - - brcmf_fws_bus_blocked(drvr, state); -} - void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) { if (skb->pkt_type == PACKET_MULTICAST) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 65689469c5a1..2a38dd3eade3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -44,6 +44,7 @@ #include "firmware.h" #include "core.h" #include "common.h" +#include "bcdc.h" #define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500) #define CTL_DONE_TIMEOUT msecs_to_jiffies(2500) @@ -2328,7 +2329,7 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) if ((bus->sdiodev->state == BRCMF_SDIOD_DATA) && bus->txoff && (pktq_len(&bus->txq) < TXLOW)) { bus->txoff = false; - brcmf_txflowblock(bus->sdiodev->dev, false); + brcmf_proto_bcdc_txflowblock(bus->sdiodev->dev, false); } return cnt; @@ -2753,7 +2754,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) if (pktq_len(&bus->txq) >= TXHI) { bus->txoff = true; - brcmf_txflowblock(dev, true); + brcmf_proto_bcdc_txflowblock(dev, true); } spin_unlock_bh(&bus->txq_lock); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index d93ebbdc7737..c45c7c1c7864 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -29,6 +29,7 @@ #include "usb.h" #include "core.h" #include "common.h" +#include "bcdc.h" #define IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000) @@ -488,7 +489,7 @@ static void brcmf_usb_tx_complete(struct urb *urb) spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags); if (devinfo->tx_freecount > devinfo->tx_high_watermark && devinfo->tx_flowblock) { - brcmf_txflowblock(devinfo->dev, false); + brcmf_proto_bcdc_txflowblock(devinfo->dev, false); devinfo->tx_flowblock = false; } spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags); @@ -635,7 +636,7 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags); if (devinfo->tx_freecount < devinfo->tx_low_watermark && !devinfo->tx_flowblock) { - brcmf_txflowblock(dev, true); + brcmf_proto_bcdc_txflowblock(dev, true); devinfo->tx_flowblock = true; } spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags); -- cgit From 7b584396b7a760bc77bbde4625f83ef173159d3e Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Fri, 10 Mar 2017 21:17:03 +0000 Subject: brcmfmac: move brcmf_txcomplete to bcdc layer brcmf_txcomplete is invoked by sdio and usb bus module which are using bcdc protocol. So move it from core module into bcdc layer. Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c | 20 ++++++++++++++++++++ .../net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h | 2 ++ .../net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 3 --- .../net/wireless/broadcom/brcm80211/brcmfmac/core.c | 18 ------------------ .../net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 3 ++- .../net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 2 +- 6 files changed, 25 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index 4c7dafa162ac..bee70f3bd4db 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -355,6 +355,26 @@ void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state) brcmf_fws_bus_blocked(drvr, state); } +void +brcmf_proto_bcdc_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; + 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_bcdc_hdrpull(drvr, false, txp, &ifp)) + brcmu_pkt_buf_free_skb(txp); + else + brcmf_txfinalize(ifp, txp, success); + } +} + static void brcmf_proto_bcdc_configure_addr_mode(struct brcmf_pub *drvr, int ifidx, enum proto_addr_mode addr_mode) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h index b5282cb08199..b6fa7a836cda 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h @@ -20,6 +20,8 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr); void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr); void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state); +void brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp, + bool success); #else static inline int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { return 0; } static inline void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) {} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index eeb6643eb1da..b55c3293c4b4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -230,9 +230,6 @@ void brcmf_detach(struct device *dev); /* Indication from bus module that dongle should be reset */ void brcmf_dev_reset(struct device *dev); -/* Notify the bus has transferred the tx packet to firmware */ -void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success); - /* Configure the "global" bus state used by upper layers */ void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 59831dc446d6..47cfb82dbfeb 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -383,24 +383,6 @@ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success) brcmu_pkt_buf_free_skb(txp); } -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; - 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, txp, &ifp)) - brcmu_pkt_buf_free_skb(txp); - else - brcmf_txfinalize(ifp, txp, success); - } -} - static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 2a38dd3eade3..a999f95062c7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -2266,7 +2266,8 @@ done: bus->tx_seq = (bus->tx_seq + pktq->qlen) % SDPCM_SEQ_WRAP; skb_queue_walk_safe(pktq, pkt_next, tmp) { __skb_unlink(pkt_next, pktq); - brcmf_txcomplete(bus->sdiodev->dev, pkt_next, ret == 0); + brcmf_proto_bcdc_txcomplete(bus->sdiodev->dev, pkt_next, + ret == 0); } return ret; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index c45c7c1c7864..e4d545f9edee 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -483,7 +483,7 @@ static void brcmf_usb_tx_complete(struct urb *urb) req->skb); brcmf_usb_del_fromq(devinfo, req); - brcmf_txcomplete(devinfo->dev, req->skb, urb->status == 0); + brcmf_proto_bcdc_txcomplete(devinfo->dev, req->skb, urb->status == 0); req->skb = NULL; brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount); spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags); -- cgit From 9fdc64bbdbe7bd546e0fbcedd2f1c03448c6df42 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Fri, 10 Mar 2017 21:17:04 +0000 Subject: brcmfmac: wrap brcmf_fws_add_interface into bcdc layer fwsignal is only used by bcdc. Create a new protocol interface function brcmf_proto_add_if for core module to notify protocol layer upon a new interface is created. Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c | 7 +++++++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c | 3 ++- drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h | 9 +++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index bee70f3bd4db..2e678a38dc6d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -399,6 +399,12 @@ static void brcmf_proto_bcdc_rxreorder(struct brcmf_if *ifp, brcmf_fws_rxreorder(ifp, skb); } +static void +brcmf_proto_bcdc_add_if(struct brcmf_if *ifp) +{ + brcmf_fws_add_interface(ifp); +} + int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { struct brcmf_bcdc *bcdc; @@ -422,6 +428,7 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer; drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer; drvr->proto->rxreorder = brcmf_proto_bcdc_rxreorder; + drvr->proto->add_if = brcmf_proto_bcdc_add_if; drvr->proto->pd = bcdc; drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 47cfb82dbfeb..08853cefb8e6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -990,7 +990,7 @@ int brcmf_bus_started(struct device *dev) if (ret < 0) goto fail; - brcmf_fws_add_interface(ifp); + brcmf_proto_add_if(drvr, ifp); drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev, drvr->settings->p2p_enable); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c index c79306b57532..f63f36f24c82 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c @@ -25,6 +25,7 @@ #include "fwsignal.h" #include "fweh.h" #include "fwil.h" +#include "proto.h" /** * struct brcmf_fweh_queue_item - event item on event queue. @@ -172,7 +173,7 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, if (IS_ERR(ifp)) return; if (!is_p2pdev) - brcmf_fws_add_interface(ifp); + brcmf_proto_add_if(drvr, ifp); if (!drvr->fweh.evt_handler[BRCMF_E_IF]) if (brcmf_net_attach(ifp, false) < 0) return; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h index 34b59feedeba..3d001f332003 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h @@ -44,6 +44,7 @@ struct brcmf_proto { void (*add_tdls_peer)(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN]); void (*rxreorder)(struct brcmf_if *ifp, struct sk_buff *skb); + void (*add_if)(struct brcmf_if *ifp); void *pd; }; @@ -118,4 +119,12 @@ brcmf_proto_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb) ifp->drvr->proto->rxreorder(ifp, skb); } +static inline void +brcmf_proto_add_if(struct brcmf_pub *drvr, struct brcmf_if *ifp) +{ + if (!drvr->proto->add_if) + return; + drvr->proto->add_if(ifp); +} + #endif /* BRCMFMAC_PROTO_H */ -- cgit From c02a5eb82056f75615cb48aa540bfd245f489b99 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Fri, 10 Mar 2017 21:17:05 +0000 Subject: brcmfmac: wrap brcmf_fws_del_interface into bcdc layer Create a new protocol interface function brcmf_proto_del_if for core module to notify protocol layer upon interface deletion. Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c | 7 +++++++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 4 ++-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h | 9 +++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index 2e678a38dc6d..ae041258cadb 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -405,6 +405,12 @@ brcmf_proto_bcdc_add_if(struct brcmf_if *ifp) brcmf_fws_add_interface(ifp); } +static void +brcmf_proto_bcdc_del_if(struct brcmf_if *ifp) +{ + brcmf_fws_del_interface(ifp); +} + int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { struct brcmf_bcdc *bcdc; @@ -429,6 +435,7 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer; drvr->proto->rxreorder = brcmf_proto_bcdc_rxreorder; drvr->proto->add_if = brcmf_proto_bcdc_add_if; + drvr->proto->del_if = brcmf_proto_bcdc_del_if; drvr->proto->pd = bcdc; drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 08853cefb8e6..60c6c7839cc2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -706,7 +706,7 @@ void brcmf_remove_interface(struct brcmf_if *ifp, bool rtnl_locked) return; brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", ifp->bsscfgidx, ifp->ifidx); - brcmf_fws_del_interface(ifp); + brcmf_proto_del_if(ifp->drvr, ifp); brcmf_del_if(ifp->drvr, ifp->bsscfgidx, rtnl_locked); } @@ -1035,7 +1035,7 @@ fail: drvr->config = NULL; } if (drvr->fws) { - brcmf_fws_del_interface(ifp); + brcmf_proto_del_if(ifp->drvr, ifp); brcmf_fws_deinit(drvr); } brcmf_net_detach(ifp->ndev, false); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h index 3d001f332003..2c76c9604b4c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h @@ -45,6 +45,7 @@ struct brcmf_proto { u8 peer[ETH_ALEN]); void (*rxreorder)(struct brcmf_if *ifp, struct sk_buff *skb); void (*add_if)(struct brcmf_if *ifp); + void (*del_if)(struct brcmf_if *ifp); void *pd; }; @@ -127,4 +128,12 @@ brcmf_proto_add_if(struct brcmf_pub *drvr, struct brcmf_if *ifp) drvr->proto->add_if(ifp); } +static inline void +brcmf_proto_del_if(struct brcmf_pub *drvr, struct brcmf_if *ifp) +{ + if (!drvr->proto->del_if) + return; + drvr->proto->del_if(ifp); +} + #endif /* BRCMFMAC_PROTO_H */ -- cgit From 66ded1f8b33cdd9d6d3e20f5f8dd23615a110e70 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Fri, 10 Mar 2017 21:17:06 +0000 Subject: brcmfmac: wrap brcmf_fws_reset_interface into bcdc layer Create a new protocol interface function brcmf_proto_reset_if for core module to notify protocol layer when interface role changes. Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c | 7 +++++++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c | 3 +-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h | 9 +++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index ae041258cadb..92eafccf087b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -411,6 +411,12 @@ brcmf_proto_bcdc_del_if(struct brcmf_if *ifp) brcmf_fws_del_interface(ifp); } +static void +brcmf_proto_bcdc_reset_if(struct brcmf_if *ifp) +{ + brcmf_fws_reset_interface(ifp); +} + int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { struct brcmf_bcdc *bcdc; @@ -436,6 +442,7 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) drvr->proto->rxreorder = brcmf_proto_bcdc_rxreorder; drvr->proto->add_if = brcmf_proto_bcdc_add_if; drvr->proto->del_if = brcmf_proto_bcdc_del_if; + drvr->proto->reset_if = brcmf_proto_bcdc_reset_if; drvr->proto->pd = bcdc; drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c index f63f36f24c82..4eb1e1ce9ace 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c @@ -22,7 +22,6 @@ #include "core.h" #include "debug.h" #include "tracepoint.h" -#include "fwsignal.h" #include "fweh.h" #include "fwil.h" #include "proto.h" @@ -180,7 +179,7 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, } if (ifp && ifevent->action == BRCMF_E_IF_CHANGE) - brcmf_fws_reset_interface(ifp); + brcmf_proto_reset_if(drvr, ifp); err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h index 2c76c9604b4c..3048ed529f95 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h @@ -46,6 +46,7 @@ struct brcmf_proto { void (*rxreorder)(struct brcmf_if *ifp, struct sk_buff *skb); void (*add_if)(struct brcmf_if *ifp); void (*del_if)(struct brcmf_if *ifp); + void (*reset_if)(struct brcmf_if *ifp); void *pd; }; @@ -136,4 +137,12 @@ brcmf_proto_del_if(struct brcmf_pub *drvr, struct brcmf_if *ifp) drvr->proto->del_if(ifp); } +static inline void +brcmf_proto_reset_if(struct brcmf_pub *drvr, struct brcmf_if *ifp) +{ + if (!drvr->proto->reset_if) + return; + drvr->proto->reset_if(ifp); +} + #endif /* BRCMFMAC_PROTO_H */ -- cgit From 889cdff72b85cc553d7820de071887593690b7d2 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 27 Feb 2017 23:40:19 +0000 Subject: ipw2200: remove redundant check of rc < 0 The check for rc < 0 is always false so the check is redundant and can be removed. Detected with CoverityScan, CID#101143 ("Logically dead code") Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/ipw2x00/ipw2200.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index 5ef3c5cc47c5..bbc579b647b6 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -3539,9 +3539,6 @@ static int ipw_load(struct ipw_priv *priv) fw_img = &fw->data[le32_to_cpu(fw->boot_size) + le32_to_cpu(fw->ucode_size)]; - if (rc < 0) - goto error; - if (!priv->rxq) priv->rxq = ipw_rx_queue_alloc(priv); else -- cgit From 5ce33b603063f36272fcfb1b4a5fde69f46eca88 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Thu, 9 Mar 2017 00:54:22 +0100 Subject: rt2x00: fix TX_PWR_CFG_4 register definition Some of the macros used to describe the TX_PWR_CFG_4 register accidentally refer to TX_PWR_CFG_3, probably a copy&paste error. Fix that. Signed-off-by: Daniel Golle Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800.h b/drivers/net/wireless/ralink/rt2x00/rt2800.h index 480b08601785..fd1dbd956bad 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h @@ -1171,10 +1171,10 @@ #define TX_PWR_CFG_4_UKNOWN7 FIELD32(0x00000f00) #define TX_PWR_CFG_4_UKNOWN8 FIELD32(0x0000f000) /* bits for 3T devices */ -#define TX_PWR_CFG_3_STBC4_CH0 FIELD32(0x0000000f) -#define TX_PWR_CFG_3_STBC4_CH1 FIELD32(0x000000f0) -#define TX_PWR_CFG_3_STBC6_CH0 FIELD32(0x00000f00) -#define TX_PWR_CFG_3_STBC6_CH1 FIELD32(0x0000f000) +#define TX_PWR_CFG_4_STBC4_CH0 FIELD32(0x0000000f) +#define TX_PWR_CFG_4_STBC4_CH1 FIELD32(0x000000f0) +#define TX_PWR_CFG_4_STBC6_CH0 FIELD32(0x00000f00) +#define TX_PWR_CFG_4_STBC6_CH1 FIELD32(0x0000f000) /* * TX_PIN_CFG: -- cgit From 48ce88022dbc28eebddc524d1fc2b913f03f64ba Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 4 Feb 2017 23:49:03 +0100 Subject: i40evf: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | 31 +++++++++++----------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index 272d600c1ed0..122efbd29a19 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -64,51 +64,50 @@ static const struct i40evf_stats i40evf_gstrings_stats[] = { (I40EVF_GLOBAL_STATS_LEN + I40EVF_QUEUE_STATS_LEN(_dev)) /** - * i40evf_get_settings - Get Link Speed and Duplex settings + * i40evf_get_link_ksettings - Get Link Speed and Duplex settings * @netdev: network interface device structure - * @ecmd: ethtool command + * @cmd: ethtool command * * Reports speed/duplex settings. Because this is a VF, we don't know what * kind of link we really have, so we fake it. **/ -static int i40evf_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int i40evf_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct i40evf_adapter *adapter = netdev_priv(netdev); - ecmd->supported = 0; - ecmd->autoneg = AUTONEG_DISABLE; - ecmd->transceiver = XCVR_DUMMY1; - ecmd->port = PORT_NONE; + ethtool_link_ksettings_zero_link_mode(cmd, supported); + cmd->base.autoneg = AUTONEG_DISABLE; + cmd->base.port = PORT_NONE; /* Set speed and duplex */ switch (adapter->link_speed) { case I40E_LINK_SPEED_40GB: - ethtool_cmd_speed_set(ecmd, SPEED_40000); + cmd->base.speed = SPEED_40000; break; case I40E_LINK_SPEED_25GB: #ifdef SPEED_25000 - ethtool_cmd_speed_set(ecmd, SPEED_25000); + cmd->base.speed = SPEED_25000; #else netdev_info(netdev, "Speed is 25G, display not supported by this version of ethtool.\n"); #endif break; case I40E_LINK_SPEED_20GB: - ethtool_cmd_speed_set(ecmd, SPEED_20000); + cmd->base.speed = SPEED_20000; break; case I40E_LINK_SPEED_10GB: - ethtool_cmd_speed_set(ecmd, SPEED_10000); + cmd->base.speed = SPEED_10000; break; case I40E_LINK_SPEED_1GB: - ethtool_cmd_speed_set(ecmd, SPEED_1000); + cmd->base.speed = SPEED_1000; break; case I40E_LINK_SPEED_100MB: - ethtool_cmd_speed_set(ecmd, SPEED_100); + cmd->base.speed = SPEED_100; break; default: break; } - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; return 0; } @@ -643,7 +642,6 @@ static int i40evf_set_rxfh(struct net_device *netdev, const u32 *indir, } static const struct ethtool_ops i40evf_ethtool_ops = { - .get_settings = i40evf_get_settings, .get_drvinfo = i40evf_get_drvinfo, .get_link = ethtool_op_get_link, .get_ringparam = i40evf_get_ringparam, @@ -663,6 +661,7 @@ static const struct ethtool_ops i40evf_ethtool_ops = { .set_rxfh = i40evf_set_rxfh, .get_channels = i40evf_get_channels, .get_rxfh_key_size = i40evf_get_rxfh_key_size, + .get_link_ksettings = i40evf_get_link_ksettings, }; /** -- cgit From fe0b0cd97b4f210ecd1693edba6eec10820e11f2 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:38 -0800 Subject: i40e: send correct port number to AdminQ when enabling UDP tunnels The firmware expects the port numbers for offloaded UDP tunnels in Little Endian format. We accidentally sent the value in Big Endian format which obviously will cause the wrong port number to be put into the UDP tunnels list. This results in VxLAN and Geneve tunnel Rx offloads being essentially disabled, unless the port number happens to be identical after byte swapping. Note that i40e_aq_add_udp_tunnel() will byteswap the parameter from host order into Little Endian so we don't need worry about passing strictly a __le16 value to the command. This patch essentially reverts b3f5c7bc88ba ("i40e: Fix for extra byte swap in tunnel setup", 2016-08-24), but in a way that makes the result much more clear to the reader. Fixes: b3f5c7bc88ba ("i40e: Fix for extra byte swap in tunnel setup", 2016-08-24) Signed-off-by: Jacob Keller Reviewed-by: Williams, Mitch A Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 3 ++- drivers/net/ethernet/intel/i40e/i40e_main.c | 17 ++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index a5cf5d11d0e7..fba8495a8787 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -244,7 +244,8 @@ struct i40e_tc_configuration { }; struct i40e_udp_port_config { - __be16 index; + /* AdminQ command interface expects port number in Host byte order */ + u16 index; u8 type; }; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 9df0d86812e7..6e63459ceb65 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7353,7 +7353,7 @@ static void i40e_sync_udp_filters_subtask(struct i40e_pf *pf) { struct i40e_hw *hw = &pf->hw; i40e_status ret; - __be16 port; + u16 port; int i; if (!(pf->flags & I40E_FLAG_UDP_FILTER_SYNC)) @@ -7377,7 +7377,7 @@ static void i40e_sync_udp_filters_subtask(struct i40e_pf *pf) "%s %s port %d, index %d failed, err %s aq_err %s\n", pf->udp_ports[i].type ? "vxlan" : "geneve", port ? "add" : "delete", - ntohs(port), i, + port, i, i40e_stat_str(&pf->hw, ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); @@ -9014,7 +9014,7 @@ static int i40e_set_features(struct net_device *netdev, * * Returns the index number or I40E_MAX_PF_UDP_OFFLOAD_PORTS if port not found **/ -static u8 i40e_get_udp_port_idx(struct i40e_pf *pf, __be16 port) +static u8 i40e_get_udp_port_idx(struct i40e_pf *pf, u16 port) { u8 i; @@ -9037,7 +9037,7 @@ static void i40e_udp_tunnel_add(struct net_device *netdev, struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - __be16 port = ti->port; + u16 port = ntohs(ti->port); u8 next_idx; u8 idx; @@ -9045,8 +9045,7 @@ static void i40e_udp_tunnel_add(struct net_device *netdev, /* Check if port already exists */ if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) { - netdev_info(netdev, "port %d already offloaded\n", - ntohs(port)); + netdev_info(netdev, "port %d already offloaded\n", port); return; } @@ -9055,7 +9054,7 @@ static void i40e_udp_tunnel_add(struct net_device *netdev, if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) { netdev_info(netdev, "maximum number of offloaded UDP ports reached, not adding port %d\n", - ntohs(port)); + port); return; } @@ -9089,7 +9088,7 @@ static void i40e_udp_tunnel_del(struct net_device *netdev, struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - __be16 port = ti->port; + u16 port = ntohs(ti->port); u8 idx; idx = i40e_get_udp_port_idx(pf, port); @@ -9121,7 +9120,7 @@ static void i40e_udp_tunnel_del(struct net_device *netdev, return; not_found: netdev_warn(netdev, "UDP port %d was not found, not deleting\n", - ntohs(port)); + port); } static int i40e_get_phys_port_id(struct net_device *netdev, -- cgit From 8ce43dce6f272202dcd127e0e288b37277ca87dd Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:39 -0800 Subject: i40e: don't use arrays for (src|dst)_ip The code originally included src_ip and dst_ip with enough space to support ipv6 filters. However, no actual support for ipv6 filters has been implemented. Thus, remove the arrays and just use __be32 values. Should ipv6 support be added in the future, we can replace these with a union that has sizes for both values. Change-Id: I1bc04032244a80eb6ebc8a4e6c723a4a665c1dd5 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 4 ++-- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 12 ++++++------ drivers/net/ethernet/intel/i40e/i40e_txrx.c | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index fba8495a8787..55ea1c0221e6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -208,8 +208,8 @@ struct i40e_fdir_filter { u8 flow_type; u8 ip4_proto; /* TX packet view of src and dst */ - __be32 dst_ip[4]; - __be32 src_ip[4]; + __be32 dst_ip; + __be32 src_ip; __be16 src_port; __be16 dst_port; __be32 sctp_v_tag; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index ceb57ad59e8f..7a22b473dbdd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2406,8 +2406,8 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf, */ fsp->h_u.tcp_ip4_spec.psrc = rule->dst_port; fsp->h_u.tcp_ip4_spec.pdst = rule->src_port; - fsp->h_u.tcp_ip4_spec.ip4src = rule->dst_ip[0]; - fsp->h_u.tcp_ip4_spec.ip4dst = rule->src_ip[0]; + fsp->h_u.tcp_ip4_spec.ip4src = rule->dst_ip; + fsp->h_u.tcp_ip4_spec.ip4dst = rule->src_ip; if (rule->dest_ctl == I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET) fsp->ring_cookie = RX_CLS_FLOW_DISC; @@ -2630,8 +2630,8 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc) static bool i40e_match_fdir_input_set(struct i40e_fdir_filter *rule, struct i40e_fdir_filter *input) { - if ((rule->dst_ip[0] != input->dst_ip[0]) || - (rule->src_ip[0] != input->src_ip[0]) || + if ((rule->dst_ip != input->dst_ip) || + (rule->src_ip != input->src_ip) || (rule->dst_port != input->dst_port) || (rule->src_port != input->src_port)) return false; @@ -2807,8 +2807,8 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, */ input->dst_port = fsp->h_u.tcp_ip4_spec.psrc; input->src_port = fsp->h_u.tcp_ip4_spec.pdst; - input->dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src; - input->src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst; + input->dst_ip = fsp->h_u.tcp_ip4_spec.ip4src; + input->src_ip = fsp->h_u.tcp_ip4_spec.ip4dst; if (ntohl(fsp->m_ext.data[1])) { vf_id = ntohl(fsp->h_ext.data[1]); diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 6eb5dc4168f3..c4d3a40a3f10 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -219,9 +219,9 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi, udp = (struct udphdr *)(raw_packet + IP_HEADER_OFFSET + sizeof(struct iphdr)); - ip->daddr = fd_data->dst_ip[0]; + ip->daddr = fd_data->dst_ip; udp->dest = fd_data->dst_port; - ip->saddr = fd_data->src_ip[0]; + ip->saddr = fd_data->src_ip; udp->source = fd_data->src_port; fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP; @@ -281,9 +281,9 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, tcp = (struct tcphdr *)(raw_packet + IP_HEADER_OFFSET + sizeof(struct iphdr)); - ip->daddr = fd_data->dst_ip[0]; + ip->daddr = fd_data->dst_ip; tcp->dest = fd_data->dst_port; - ip->saddr = fd_data->src_ip[0]; + ip->saddr = fd_data->src_ip; tcp->source = fd_data->src_port; if (add) { @@ -359,8 +359,8 @@ static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi, memcpy(raw_packet, packet, I40E_IP_DUMMY_PACKET_LEN); ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET); - ip->saddr = fd_data->src_ip[0]; - ip->daddr = fd_data->dst_ip[0]; + ip->saddr = fd_data->src_ip; + ip->daddr = fd_data->dst_ip; ip->protocol = 0; fd_data->pctype = i; -- cgit From 01016da1e58136518252822738fe833c662df916 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:40 -0800 Subject: i40e: rework exit flow of i40e_add_fdir_ethtool Refactor the exit flow of the i40e_add_fdir_ethtool function. Move the input_label to the end of the function, removing the dependency on having a non-zero return value. Add a comment explaining why it is ok not to free the fdir data structure, because the structure is now stored in the fdir_filter_list. Change-Id: I723342181d59cd0c9f3b31140c37961ba37bb242 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 7a22b473dbdd..d16a5a6b24fc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2828,12 +2828,19 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, } ret = i40e_add_del_fdir(vsi, input, true); -free_input: if (ret) - kfree(input); - else - i40e_update_ethtool_fdir_entry(vsi, input, fsp->location, NULL); + goto free_input; + + /* Add the input filter to the fdir_input_list, possibly replacing + * a previous filter. Do not free the input structure after adding it + * to the list as this would cause a use-after-free bug. + */ + i40e_update_ethtool_fdir_entry(vsi, input, fsp->location, NULL); + return 0; + +free_input: + kfree(input); return ret; } -- cgit From e5187ee3ee9a95b1aa32a9a3daf4f07a9f89c20c Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:41 -0800 Subject: i40e: return immediately when failing to add fdir filter Instead of setting err=true and checking this to determine when to free the raw_packet near the end of the function, simply kfree and return immediately. The resulting code is a bit cleaner and has one less variable. This also resolves a subtle bug in the ipv4 case which could fail to add the first filter and then never free the memory, resulting in a small memory leak. Change-ID: I7583aac033481dc794b4acaa14445059c8930ff1 Signed-off-by: Jacob Keller Reviewed-by: Avinash Dayanand Reviewed-by: Alan Brady Reviewed-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 33 ++++++++++++----------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index c4d3a40a3f10..005257b4f218 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -203,7 +203,6 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi, struct i40e_pf *pf = vsi->back; struct udphdr *udp; struct iphdr *ip; - bool err = false; u8 *raw_packet; int ret; static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0, @@ -230,7 +229,9 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi, dev_info(&pf->pdev->dev, "PCTYPE:%d, Filter command send failed for fd_id:%d (ret = %d)\n", fd_data->pctype, fd_data->fd_id, ret); - err = true; + /* Free the packet buffer since it wasn't added to the ring */ + kfree(raw_packet); + return -EOPNOTSUPP; } else if (I40E_DEBUG_FD & pf->hw.debug_mask) { if (add) dev_info(&pf->pdev->dev, @@ -241,10 +242,8 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi, "Filter deleted for PCTYPE %d loc = %d\n", fd_data->pctype, fd_data->fd_id); } - if (err) - kfree(raw_packet); - return err ? -EOPNOTSUPP : 0; + return 0; } #define I40E_TCPIP_DUMMY_PACKET_LEN 54 @@ -263,7 +262,6 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, struct i40e_pf *pf = vsi->back; struct tcphdr *tcp; struct iphdr *ip; - bool err = false; u8 *raw_packet; int ret; /* Dummy packet */ @@ -305,12 +303,13 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); - if (ret) { dev_info(&pf->pdev->dev, "PCTYPE:%d, Filter command send failed for fd_id:%d (ret = %d)\n", fd_data->pctype, fd_data->fd_id, ret); - err = true; + /* Free the packet buffer since it wasn't added to the ring */ + kfree(raw_packet); + return -EOPNOTSUPP; } else if (I40E_DEBUG_FD & pf->hw.debug_mask) { if (add) dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d loc = %d)\n", @@ -321,10 +320,7 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, fd_data->pctype, fd_data->fd_id); } - if (err) - kfree(raw_packet); - - return err ? -EOPNOTSUPP : 0; + return 0; } #define I40E_IP_DUMMY_PACKET_LEN 34 @@ -343,7 +339,6 @@ static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi, { struct i40e_pf *pf = vsi->back; struct iphdr *ip; - bool err = false; u8 *raw_packet; int ret; int i; @@ -365,12 +360,15 @@ static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi, fd_data->pctype = i; ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); - if (ret) { dev_info(&pf->pdev->dev, "PCTYPE:%d, Filter command send failed for fd_id:%d (ret = %d)\n", fd_data->pctype, fd_data->fd_id, ret); - err = true; + /* The packet buffer wasn't added to the ring so we + * need to free it now. + */ + kfree(raw_packet); + return -EOPNOTSUPP; } else if (I40E_DEBUG_FD & pf->hw.debug_mask) { if (add) dev_info(&pf->pdev->dev, @@ -383,10 +381,7 @@ static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi, } } - if (err) - kfree(raw_packet); - - return err ? -EOPNOTSUPP : 0; + return 0; } /** -- cgit From 377cc24980588d03abf3a2a843b8c53dfc8b5a1b Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:42 -0800 Subject: i40e: exit ATR mode only when adding TCP/IPv4 filter succeeds Move ATR exit check after we have sent the TCP/IPv4 filter to the ring successfully. This avoids an issue where we potentially update the filter count without actually succeeding in adding the filter. Now, we only increment the fd_tcp_rule after we've succeeded. Additionally, we will re-enable ATR mode only after deletion of the filter is actually posted to the FDIR ring. Change-ID: If5c1dea422081cc5e2de65618b01b4c3bf6bd586 Signed-off-by: Jacob Keller Reviewed-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 34 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 005257b4f218..05f3d0d5a004 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -284,23 +284,6 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, ip->saddr = fd_data->src_ip; tcp->source = fd_data->src_port; - if (add) { - pf->fd_tcp_rule++; - if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && - I40E_DEBUG_FD & pf->hw.debug_mask) - dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n"); - pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; - } else { - pf->fd_tcp_rule = (pf->fd_tcp_rule > 0) ? - (pf->fd_tcp_rule - 1) : 0; - if (pf->fd_tcp_rule == 0) { - if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && - I40E_DEBUG_FD & pf->hw.debug_mask) - dev_info(&pf->pdev->dev, "ATR re-enabled due to no sideband TCP/IPv4 rules\n"); - pf->hw_disabled_flags &= ~I40E_FLAG_FD_ATR_ENABLED; - } - } - fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); if (ret) { @@ -320,6 +303,23 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, fd_data->pctype, fd_data->fd_id); } + if (add) { + pf->fd_tcp_rule++; + if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && + I40E_DEBUG_FD & pf->hw.debug_mask) + dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n"); + pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; + } else { + pf->fd_tcp_rule = (pf->fd_tcp_rule > 0) ? + (pf->fd_tcp_rule - 1) : 0; + if (pf->fd_tcp_rule == 0) { + if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && + I40E_DEBUG_FD & pf->hw.debug_mask) + dev_info(&pf->pdev->dev, "ATR re-enabled due to no sideband TCP/IPv4 rules\n"); + pf->hw_disabled_flags &= ~I40E_FLAG_FD_ATR_ENABLED; + } + } + return 0; } -- cgit From e122eb7482d4af67bec77055ca2a9009867491e9 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:43 -0800 Subject: i40e: remove redundant check for fd_tcp_rule when restoring filters i40e_fdir_filter_restore re-adds all existing filters, which already checks when adding a TCPv4 filter to disable ATR. We don't need to make the check twice, so remove this redundant code. Change-ID: Ia0b0690e23523915199d601494557def135c9d7f Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 6e63459ceb65..221e1705c031 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5466,12 +5466,6 @@ static int i40e_up_complete(struct i40e_vsi *vsi) if (vsi->type == I40E_VSI_FDIR) { /* reset fd counters */ pf->fd_add_err = pf->fd_atr_cnt = 0; - if (pf->fd_tcp_rule > 0) { - pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; - if (I40E_DEBUG_FD & pf->hw.debug_mask) - dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 exist\n"); - pf->fd_tcp_rule = 0; - } i40e_fdir_filter_restore(vsi); } -- cgit From 6d069425f0fcafff5bfdd292e5f345ce55d46a43 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:44 -0800 Subject: i40e: reset fd_tcp_rule count when restoring filters Since we're about to reprogram the filters, we need to ensure that the fd_tcp_rule count is correctly reset to 0. Otherwise, we will keep a stale count that does not accurately reflect the number of programmed TCPv4 filters. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 221e1705c031..437b79eeb8b5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3283,6 +3283,9 @@ static void i40e_fdir_filter_restore(struct i40e_vsi *vsi) if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED)) return; + /* Reset FDir counters as we're replaying all existing filters */ + pf->fd_tcp_rule = 0; + hlist_for_each_entry_safe(filter, node, &pf->fdir_filter_list, fdir_node) { i40e_add_del_fdir(vsi, filter, true); -- cgit From 510dd4609f581329a421fa20e1b802b629bb7ee2 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:45 -0800 Subject: i40e: don't re-enable ATR when flushing filters if SB has TCP4/IPv4 rules When flushing and replaying FDIR filters, it is possible we would disable ATR, and then re-enable it even though we should have kept it disabled due to existing TCP/IPv4 filters. Fix this by checking whether we have TCP4/IPv4 filters before re-enabling. Alternatively, we could instead restore ATR and then replay filters, however, this would cause us to rapidly enable and then disable ATR in some cases. Change-ID: I076e4cc1e4409bce7f98f3c213295433a4ff43d8 Signed-off-by: Jacob Keller Reviewed-by: Avinash Dayanand Reviewed-by: Alan Brady Reviewed-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 437b79eeb8b5..cc33ac835181 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -6228,7 +6228,7 @@ static void i40e_fdir_flush_and_replay(struct i40e_pf *pf) } else { /* replay sideband filters */ i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]); - if (!disable_atr) + if (!disable_atr && !pf->fd_tcp_rule) pf->hw_disabled_flags &= ~I40E_FLAG_FD_ATR_ENABLED; clear_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); if (I40E_DEBUG_FD & pf->hw.debug_mask) -- cgit From 097dbf52505962d06f9b707a3984d48d4f25fbe9 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:46 -0800 Subject: i40e: add counters for UDP/IPv4 and IPv4 filters In preparation for adding code to properly check the mask values, we will need to know the number of active filters for each type. Add counters for each filter type. Rename the already existing fd_tcp_rule to fd_tcp4_filter_cnt to match the style of other names. To avoid style warnings, avoid assigning multiple parameters at once, and fix up one other case where we did so previously. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 9 ++++++++- drivers/net/ethernet/intel/i40e/i40e_main.c | 19 +++++++++++++------ drivers/net/ethernet/intel/i40e/i40e_txrx.c | 17 +++++++++++++---- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 55ea1c0221e6..c0f2286c2b72 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -286,7 +286,14 @@ struct i40e_pf { u32 fd_flush_cnt; u32 fd_add_err; u32 fd_atr_cnt; - u32 fd_tcp_rule; + + /* Book-keeping of side-band filter count per flow-type. + * This is used to detect and handle input set changes for + * respective flow-type. + */ + u16 fd_tcp4_filter_cnt; + u16 fd_udp4_filter_cnt; + u16 fd_ip4_filter_cnt; struct i40e_udp_port_config udp_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS]; u16 pending_udp_bitmap; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index cc33ac835181..caccb8e97f1b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3284,7 +3284,9 @@ static void i40e_fdir_filter_restore(struct i40e_vsi *vsi) return; /* Reset FDir counters as we're replaying all existing filters */ - pf->fd_tcp_rule = 0; + pf->fd_tcp4_filter_cnt = 0; + pf->fd_udp4_filter_cnt = 0; + pf->fd_ip4_filter_cnt = 0; hlist_for_each_entry_safe(filter, node, &pf->fdir_filter_list, fdir_node) { @@ -5468,7 +5470,8 @@ static int i40e_up_complete(struct i40e_vsi *vsi) /* replay FDIR SB filters */ if (vsi->type == I40E_VSI_FDIR) { /* reset fd counters */ - pf->fd_add_err = pf->fd_atr_cnt = 0; + pf->fd_add_err = 0; + pf->fd_atr_cnt = 0; i40e_fdir_filter_restore(vsi); } @@ -5751,7 +5754,11 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf) hlist_del(&filter->fdir_node); kfree(filter); } + pf->fdir_pf_active_filters = 0; + pf->fd_tcp4_filter_cnt = 0; + pf->fd_udp4_filter_cnt = 0; + pf->fd_ip4_filter_cnt = 0; } /** @@ -6156,7 +6163,7 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf) if (fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM * 2)) { if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && (pf->hw_disabled_flags & I40E_FLAG_FD_ATR_ENABLED) && - (pf->fd_tcp_rule == 0)) { + (pf->fd_tcp4_filter_cnt == 0)) { pf->hw_disabled_flags &= ~I40E_FLAG_FD_ATR_ENABLED; if (I40E_DEBUG_FD & pf->hw.debug_mask) dev_info(&pf->pdev->dev, "ATR is being enabled since we have space in the table and there are no conflicting ntuple rules\n"); @@ -6228,7 +6235,7 @@ static void i40e_fdir_flush_and_replay(struct i40e_pf *pf) } else { /* replay sideband filters */ i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]); - if (!disable_atr && !pf->fd_tcp_rule) + if (!disable_atr && !pf->fd_tcp4_filter_cnt) pf->hw_disabled_flags &= ~I40E_FLAG_FD_ATR_ENABLED; clear_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); if (I40E_DEBUG_FD & pf->hw.debug_mask) @@ -8937,8 +8944,8 @@ bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features) pf->flags &= ~I40E_FLAG_FD_SB_ENABLED; pf->hw_disabled_flags &= ~I40E_FLAG_FD_SB_ENABLED; /* reset fd counters */ - pf->fd_add_err = pf->fd_atr_cnt = pf->fd_tcp_rule = 0; - pf->fdir_pf_active_filters = 0; + pf->fd_add_err = 0; + pf->fd_atr_cnt = 0; /* if ATR was auto disabled it can be re-enabled. */ if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && (pf->hw_disabled_flags & I40E_FLAG_FD_ATR_ENABLED)) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 05f3d0d5a004..3880e417f167 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -243,6 +243,11 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi, fd_data->pctype, fd_data->fd_id); } + if (add) + pf->fd_udp4_filter_cnt++; + else + pf->fd_udp4_filter_cnt--; + return 0; } @@ -304,15 +309,14 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, } if (add) { - pf->fd_tcp_rule++; + pf->fd_tcp4_filter_cnt++; if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && I40E_DEBUG_FD & pf->hw.debug_mask) dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n"); pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; } else { - pf->fd_tcp_rule = (pf->fd_tcp_rule > 0) ? - (pf->fd_tcp_rule - 1) : 0; - if (pf->fd_tcp_rule == 0) { + pf->fd_tcp4_filter_cnt--; + if (pf->fd_tcp4_filter_cnt == 0) { if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && I40E_DEBUG_FD & pf->hw.debug_mask) dev_info(&pf->pdev->dev, "ATR re-enabled due to no sideband TCP/IPv4 rules\n"); @@ -381,6 +385,11 @@ static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi, } } + if (add) + pf->fd_ip4_filter_cnt++; + else + pf->fd_ip4_filter_cnt--; + return 0; } -- cgit From 1ec8deac8c63505194e773b9657824ed3c2fbdd8 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:39:12 -0800 Subject: i40e: explicitly fail on extended MAC field for ethtool_rx_flow_spec Although we will fail the filter later due to checking flow_type which will have a bogus invalid type, it is possible future refactoring will remove this hidden failure case. Avoid a possible issue in the future by explicitly checking the flow type at the start. Change-Id: Ia98eb26f7b93ccbe38c7141e8f203ef496fc6598 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index d16a5a6b24fc..905d66e87247 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2771,6 +2771,10 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, fsp = (struct ethtool_rx_flow_spec *)&cmd->fs; + /* Extended MAC field is not supported */ + if (fsp->flow_type & FLOW_MAC_EXT) + return -EINVAL; + if (fsp->location >= (pf->hw.func_caps.fd_filters_best_effort + pf->hw.func_caps.fd_filters_guaranteed)) { return -EINVAL; -- cgit From c6da525de724e3fbe439b8dad385395bb4fd2211 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:39:13 -0800 Subject: i40e: always remove old filter when adding new FDir filter The previous code relied on i40e_match_fdir_input_set to determine when determining whether to free the old filter. Change this code so that we simply unconditionally delete the old filter, even if it's identical to the new filter. This ensures that we don't leak any memory, and that we always update the filters as expected. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 32 ++++++-------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 905d66e87247..1c3805b4fcf3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2620,24 +2620,6 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc) return 0; } -/** - * i40e_match_fdir_input_set - Match a new filter against an existing one - * @rule: The filter already added - * @input: The new filter to comapre against - * - * Returns true if the two input set match - **/ -static bool i40e_match_fdir_input_set(struct i40e_fdir_filter *rule, - struct i40e_fdir_filter *input) -{ - if ((rule->dst_ip != input->dst_ip) || - (rule->src_ip != input->src_ip) || - (rule->dst_port != input->dst_port) || - (rule->src_port != input->src_port)) - return false; - return true; -} - /** * i40e_update_ethtool_fdir_entry - Updates the fdir filter entry * @vsi: Pointer to the targeted VSI @@ -2673,22 +2655,22 @@ static int i40e_update_ethtool_fdir_entry(struct i40e_vsi *vsi, /* if there is an old rule occupying our place remove it */ if (rule && (rule->fd_id == sw_idx)) { - if (input && !i40e_match_fdir_input_set(rule, input)) - err = i40e_add_del_fdir(vsi, rule, false); - else if (!input) - err = i40e_add_del_fdir(vsi, rule, false); + /* Remove this rule, since we're either deleting it, or + * replacing it. + */ + err = i40e_add_del_fdir(vsi, rule, false); hlist_del(&rule->fdir_node); kfree(rule); pf->fdir_pf_active_filters--; } - /* If no input this was a delete, err should be 0 if a rule was - * successfully found and removed from the list else -EINVAL + /* If we weren't given an input, this is a delete, so just return the + * error code indicating if there was an entry at the requested slot */ if (!input) return err; - /* initialize node and set software index */ + /* Otherwise, install the new rule as requested */ INIT_HLIST_NODE(&input->fdir_node); /* add filter to the list */ -- cgit From 6df014cffbe335ea7f41ee5324d8ba2047d3f0d0 Mon Sep 17 00:00:00 2001 From: Ezequiel Lara Gomez Date: Sat, 11 Mar 2017 20:06:54 +0000 Subject: Enable tx timestamping on loopback and dummy This enables developing code that uses SOF_TIMESTAMPING_TX_SOFTWARE by using localhost addresses (without needing to send packets outside), as well as enabling unit and functional testing of TX timestamping code without needing hardware support or network access. It also fulfills the expectation of software network devices supporting software-based timestamping. Tested on qemu using txtimestamping.c from the kernel selftests, and ethtool -T. Signed-off-by: Ezequiel Lara Gomez Signed-off-by: David S. Miller --- drivers/net/dummy.c | 15 +++++++++++++++ drivers/net/loopback.c | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index 2c80611b94ae..149244aac20a 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -125,6 +126,7 @@ static netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev) dstats->tx_bytes += skb->len; u64_stats_update_end(&dstats->syncp); + skb_tx_timestamp(skb); dev_kfree_skb(skb); return NETDEV_TX_OK; } @@ -304,8 +306,21 @@ static void dummy_get_drvinfo(struct net_device *dev, strlcpy(info->version, DRV_VERSION, sizeof(info->version)); } +static int dummy_get_ts_info(struct net_device *dev, + struct ethtool_ts_info *ts_info) +{ + ts_info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + + ts_info->phc_index = -1; + + return 0; +}; + static const struct ethtool_ops dummy_ethtool_ops = { .get_drvinfo = dummy_get_drvinfo, + .get_ts_info = dummy_get_ts_info, }; static void dummy_free_netdev(struct net_device *dev) diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index b23b71981fd5..8d179d616993 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include @@ -74,6 +75,7 @@ static netdev_tx_t loopback_xmit(struct sk_buff *skb, struct pcpu_lstats *lb_stats; int len; + skb_tx_timestamp(skb); skb_orphan(skb); /* Before queueing this packet to netif_rx(), @@ -129,8 +131,21 @@ static u32 always_on(struct net_device *dev) return 1; } +static int loopback_get_ts_info(struct net_device *netdev, + struct ethtool_ts_info *ts_info) +{ + ts_info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + + ts_info->phc_index = -1; + + return 0; +}; + static const struct ethtool_ops loopback_ethtool_ops = { .get_link = always_on, + .get_ts_info = loopback_get_ts_info, }; static int loopback_dev_init(struct net_device *dev) -- cgit From b3407c8e5eb78e4e0b57a97a4dd2e411354b60cd Mon Sep 17 00:00:00 2001 From: Ezequiel Lara Gomez Date: Sat, 11 Mar 2017 20:06:01 +0000 Subject: Cleanup some warning from timestamping code. Following checkpatch.pl recommendations (which include replacing with the , since linux/io.h includes it). Signed-off-by: Ezequiel Lara Gomez Signed-off-by: David S. Miller --- drivers/net/loopback.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 8d179d616993..224f65cb576b 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -13,7 +13,7 @@ * * Alan Cox : Fixed oddments for NET3.014 * Alan Cox : Rejig for NET3.029 snap #3 - * Alan Cox : Fixed NET3.029 bugs and sped up + * Alan Cox : Fixed NET3.029 bugs and sped up * Larry McVoy : Tiny tweak to double performance * Alan Cox : Backed out LMV's tweak - the linux mm * can't take it... @@ -41,7 +41,7 @@ #include #include -#include +#include #include #include @@ -65,8 +65,7 @@ struct pcpu_lstats { struct u64_stats_sync syncp; }; -/* - * The higher levels take care of making this non-reentrant (it's +/* The higher levels take care of making this non-reentrant (it's * called with bh's disabled). */ static netdev_tx_t loopback_xmit(struct sk_buff *skb, @@ -164,14 +163,13 @@ static void loopback_dev_free(struct net_device *dev) } static const struct net_device_ops loopback_ops = { - .ndo_init = loopback_dev_init, - .ndo_start_xmit= loopback_xmit, + .ndo_init = loopback_dev_init, + .ndo_start_xmit = loopback_xmit, .ndo_get_stats64 = loopback_get_stats64, .ndo_set_mac_address = eth_mac_addr, }; -/* - * The loopback device is special. There is only one instance +/* The loopback device is special. There is only one instance * per network namespace. */ static void loopback_setup(struct net_device *dev) @@ -185,7 +183,7 @@ static void loopback_setup(struct net_device *dev) dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE; netif_keep_dst(dev); dev->hw_features = NETIF_F_GSO_SOFTWARE; - dev->features = NETIF_F_SG | NETIF_F_FRAGLIST + dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | NETIF_F_HW_CSUM | NETIF_F_RXCSUM @@ -221,7 +219,6 @@ static __net_init int loopback_net_init(struct net *net) net->loopback_dev = dev; return 0; - out_free_netdev: free_netdev(dev); out: @@ -232,5 +229,5 @@ out: /* Registered in net/core/dev.c */ struct pernet_operations __net_initdata loopback_net_ops = { - .init = loopback_net_init, + .init = loopback_net_init, }; -- cgit From 88997e4208aea117627898e5f6f9801cf3cd42d2 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Wed, 15 Mar 2017 17:41:14 -0700 Subject: net/8021q: create device with all possible features in wanted_features wanted_features is a set of features which have to be enabled if a hardware allows that. Currently when a vlan device is created, its wanted_features is set to current features of its base device. The problem is that the base device can get new features and they are not propagated to vlan-s of this device. If we look at bonding devices, they doesn't have this problem and this patch suggests to fix this issue by the same way how it works for bonding devices. We meet this problem, when we try to create a vlan device over a bonding device. When a system are booting, real devices require time to be initialized, so bonding devices created without slaves, then vlan devices are created and only then ethernet devices are added to the bonding device. As a result we have vlan devices with disabled scatter-gather. * create a bonding device $ ip link add bond0 type bond $ ethtool -k bond0 | grep scatter scatter-gather: off tx-scatter-gather: off [requested on] tx-scatter-gather-fraglist: off [requested on] * create a vlan device $ ip link add link bond0 name bond0.10 type vlan id 10 $ ethtool -k bond0.10 | grep scatter scatter-gather: off tx-scatter-gather: off tx-scatter-gather-fraglist: off * Add a slave device to bond0 $ ip link set dev eth0 master bond0 And now we can see that the bond0 device has got the scatter-gather feature, but the bond0.10 hasn't got it. [root@laptop linux-task-diag]# ethtool -k bond0 | grep scatter scatter-gather: on tx-scatter-gather: on tx-scatter-gather-fraglist: on [root@laptop linux-task-diag]# ethtool -k bond0.10 | grep scatter scatter-gather: off tx-scatter-gather: off tx-scatter-gather-fraglist: off With this patch the vlan device will get all new features from the bonding device. Here is a call trace how features which are set in this patch reach dev->wanted_features. register_netdevice vlan_dev_init ... dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC | NETIF_F_ALL_FCOE; dev->features |= dev->hw_features; ... dev->wanted_features = dev->features & dev->hw_features; __netdev_update_features(dev); vlan_dev_fix_features ... Cc: Alexey Kuznetsov Cc: Patrick McHardy Cc: "David S. Miller" Signed-off-by: Andrei Vagin Signed-off-by: David S. Miller --- net/8021q/vlan_dev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index e97ab824e368..9ee5787634e5 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -562,8 +562,7 @@ static int vlan_dev_init(struct net_device *dev) NETIF_F_HIGHDMA | NETIF_F_SCTP_CRC | NETIF_F_ALL_FCOE; - dev->features |= real_dev->vlan_features | NETIF_F_LLTX | - NETIF_F_GSO_SOFTWARE; + dev->features |= dev->hw_features | NETIF_F_LLTX; dev->gso_max_size = real_dev->gso_max_size; dev->gso_max_segs = real_dev->gso_max_segs; if (dev->features & NETIF_F_VLAN_FEATURES) -- cgit From bf4e0a3db97eb882368fd82980b3b1fa0b5b9778 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Thu, 16 Mar 2017 15:28:00 +0200 Subject: net: ipv4: add support for ECMP hash policy choice This patch adds support for ECMP hash policy choice via a new sysctl called fib_multipath_hash_policy and also adds support for L4 hashes. The current values for fib_multipath_hash_policy are: 0 - layer 3 (default) 1 - layer 4 If there's an skb hash already set and it matches the chosen policy then it will be used instead of being calculated (currently only for L4). In L3 mode we always calculate the hash due to the ICMP error special case, the flow dissector's field consistentification should handle the address order thus we can remove the address reversals. If the skb is provided we always use it for the hash calculation, otherwise we fallback to fl4, that is if skb is NULL fl4 has to be set. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 8 +++ include/net/ip_fib.h | 14 ++---- include/net/netns/ipv4.h | 1 + include/net/route.h | 6 +-- net/ipv4/fib_semantics.c | 11 ++-- net/ipv4/icmp.c | 19 +------ net/ipv4/route.c | 92 ++++++++++++++++++++++++++-------- net/ipv4/sysctl_net_ipv4.c | 9 ++++ 8 files changed, 100 insertions(+), 60 deletions(-) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index ed3d0791eb27..b57308e76b1d 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -73,6 +73,14 @@ fib_multipath_use_neigh - BOOLEAN 0 - disabled 1 - enabled +fib_multipath_hash_policy - INTEGER + Controls which hash policy to use for multipath routes. Only valid + for kernels built with CONFIG_IP_ROUTE_MULTIPATH enabled. + Default: 0 (Layer 3) + Possible values: + 0 - Layer 3 + 1 - Layer 4 + route/max_size - INTEGER Maximum number of routes allowed in the kernel. Increase this when using large numbers of interfaces and/or routes. diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 272e62e139e0..6692c5758b33 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -395,17 +395,13 @@ int fib_sync_down_dev(struct net_device *dev, unsigned long event, bool force); int fib_sync_down_addr(struct net_device *dev, __be32 local); int fib_sync_up(struct net_device *dev, unsigned int nh_flags); -extern u32 fib_multipath_secret __read_mostly; - -static inline int fib_multipath_hash(__be32 saddr, __be32 daddr) -{ - return jhash_2words((__force u32)saddr, (__force u32)daddr, - fib_multipath_secret) >> 1; -} - +#ifdef CONFIG_IP_ROUTE_MULTIPATH +int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4, + const struct sk_buff *skb); +#endif 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); + struct flowi4 *fl4, const struct sk_buff *skb); /* Exported by fib_trie.c */ void fib_trie_init(void); diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 2e9d649ba169..a0e89190a3e9 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -151,6 +151,7 @@ struct netns_ipv4 { #endif #ifdef CONFIG_IP_ROUTE_MULTIPATH int sysctl_fib_multipath_use_neigh; + int sysctl_fib_multipath_hash_policy; #endif unsigned int fib_seq; /* protected by rtnl_mutex */ diff --git a/include/net/route.h b/include/net/route.h index c0874c87c173..2cc0e14c6359 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -113,13 +113,13 @@ 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_hash(struct net *, struct flowi4 *flp, - int mp_hash); +struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *flp, + const struct sk_buff *skb); static inline struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *flp) { - return __ip_route_output_key_hash(net, flp, -1); + return __ip_route_output_key_hash(net, flp, NULL); } struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp, diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 317026a39cfa..da449ddb8cc1 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -57,7 +57,6 @@ static unsigned int fib_info_cnt; static struct hlist_head fib_info_devhash[DEVINDEX_HASHSIZE]; #ifdef CONFIG_IP_ROUTE_MULTIPATH -u32 fib_multipath_secret __read_mostly; #define for_nexthops(fi) { \ int nhsel; const struct fib_nh *nh; \ @@ -576,9 +575,6 @@ static void fib_rebalance(struct fib_info *fi) 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, @@ -1641,7 +1637,7 @@ void fib_select_multipath(struct fib_result *res, int hash) #endif void fib_select_path(struct net *net, struct fib_result *res, - struct flowi4 *fl4, int mp_hash) + struct flowi4 *fl4, const struct sk_buff *skb) { bool oif_check; @@ -1650,10 +1646,9 @@ void fib_select_path(struct net *net, struct fib_result *res, #ifdef CONFIG_IP_ROUTE_MULTIPATH if (res->fi->fib_nhs > 1 && oif_check) { - if (mp_hash < 0) - mp_hash = get_hash_from_flowi4(fl4) >> 1; + int h = fib_multipath_hash(res->fi, fl4, skb); - fib_select_multipath(res, mp_hash); + fib_select_multipath(res, h); } else #endif diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index fc310db2708b..43318b5f5647 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -464,22 +464,6 @@ out_bh_enable: local_bh_enable(); } -#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, @@ -505,8 +489,7 @@ static struct rtable *icmp_route_lookup(struct net *net, fl4->flowi4_oif = l3mdev_master_ifindex(skb_dst(skb_in)->dev); security_skb_classify_flow(skb_in, flowi4_to_flowi(fl4)); - rt = __ip_route_output_key_hash(net, fl4, - icmp_multipath_hash_skb(skb_in)); + rt = __ip_route_output_key_hash(net, fl4, skb_in); if (IS_ERR(rt)) return rt; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 8471dd116771..5dda1ef81c7e 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1734,45 +1734,97 @@ out: } #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. + * calculated from the inner IP addresses. */ -static int ip_multipath_icmp_hash(struct sk_buff *skb) +static void ip_multipath_l3_keys(const struct sk_buff *skb, + struct flow_keys *hash_keys) { const struct iphdr *outer_iph = ip_hdr(skb); - struct icmphdr _icmph; + const struct iphdr *inner_iph; const struct icmphdr *icmph; struct iphdr _inner_iph; - const struct iphdr *inner_iph; + struct icmphdr _icmph; + + hash_keys->addrs.v4addrs.src = outer_iph->saddr; + hash_keys->addrs.v4addrs.dst = outer_iph->daddr; + if (likely(outer_iph->protocol != IPPROTO_ICMP)) + return; if (unlikely((outer_iph->frag_off & htons(IP_OFFSET)) != 0)) - goto standard_hash; + return; icmph = skb_header_pointer(skb, outer_iph->ihl * 4, sizeof(_icmph), &_icmph); if (!icmph) - goto standard_hash; + return; if (icmph->type != ICMP_DEST_UNREACH && icmph->type != ICMP_REDIRECT && icmph->type != ICMP_TIME_EXCEEDED && - icmph->type != ICMP_PARAMETERPROB) { - goto standard_hash; - } + icmph->type != ICMP_PARAMETERPROB) + return; inner_iph = skb_header_pointer(skb, outer_iph->ihl * 4 + sizeof(_icmph), sizeof(_inner_iph), &_inner_iph); if (!inner_iph) - goto standard_hash; + return; + hash_keys->addrs.v4addrs.src = inner_iph->saddr; + hash_keys->addrs.v4addrs.dst = inner_iph->daddr; +} - return fib_multipath_hash(inner_iph->daddr, inner_iph->saddr); +/* if skb is set it will be used and fl4 can be NULL */ +int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4, + const struct sk_buff *skb) +{ + struct net *net = fi->fib_net; + struct flow_keys hash_keys; + u32 mhash; -standard_hash: - return fib_multipath_hash(outer_iph->saddr, outer_iph->daddr); -} + switch (net->ipv4.sysctl_fib_multipath_hash_policy) { + case 0: + memset(&hash_keys, 0, sizeof(hash_keys)); + hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; + if (skb) { + ip_multipath_l3_keys(skb, &hash_keys); + } else { + hash_keys.addrs.v4addrs.src = fl4->saddr; + hash_keys.addrs.v4addrs.dst = fl4->daddr; + } + break; + case 1: + /* skb is currently provided only when forwarding */ + if (skb) { + unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP; + struct flow_keys keys; + + /* short-circuit if we already have L4 hash present */ + if (skb->l4_hash) + return skb_get_hash_raw(skb) >> 1; + memset(&hash_keys, 0, sizeof(hash_keys)); + skb_flow_dissect_flow_keys(skb, &keys, flag); + hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src; + hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst; + hash_keys.ports.src = keys.ports.src; + hash_keys.ports.dst = keys.ports.dst; + hash_keys.basic.ip_proto = keys.basic.ip_proto; + } else { + memset(&hash_keys, 0, sizeof(hash_keys)); + hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; + hash_keys.addrs.v4addrs.src = fl4->saddr; + hash_keys.addrs.v4addrs.dst = fl4->daddr; + hash_keys.ports.src = fl4->fl4_sport; + hash_keys.ports.dst = fl4->fl4_dport; + hash_keys.basic.ip_proto = fl4->flowi4_proto; + } + break; + } + mhash = flow_hash_from_keys(&hash_keys); + return mhash >> 1; +} +EXPORT_SYMBOL_GPL(fib_multipath_hash); #endif /* CONFIG_IP_ROUTE_MULTIPATH */ static int ip_mkroute_input(struct sk_buff *skb, @@ -1782,12 +1834,8 @@ static int ip_mkroute_input(struct sk_buff *skb, { #ifdef CONFIG_IP_ROUTE_MULTIPATH if (res->fi && res->fi->fib_nhs > 1) { - int h; + int h = fib_multipath_hash(res->fi, NULL, skb); - 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 @@ -2203,7 +2251,7 @@ add: */ struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *fl4, - int mp_hash) + const struct sk_buff *skb) { struct net_device *dev_out = NULL; __u8 tos = RT_FL_TOS(fl4); @@ -2365,7 +2413,7 @@ struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *fl4, goto make_route; } - fib_select_path(net, &res, fl4, mp_hash); + fib_select_path(net, &res, fl4, skb); dev_out = FIB_RES_DEV(res); fl4->flowi4_oif = dev_out->ifindex; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 11aaef0939b2..711c3e2e17b1 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -997,6 +997,15 @@ static struct ctl_table ipv4_net_table[] = { .extra1 = &zero, .extra2 = &one, }, + { + .procname = "fib_multipath_hash_policy", + .data = &init_net.ipv4.sysctl_fib_multipath_hash_policy, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, #endif { .procname = "ip_unprivileged_port_start", -- cgit From 5add2f9a1bcca38410bce1d376271be3fef8d23f Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 21 Jan 2017 16:06:03 +0100 Subject: e1000: use new API ethtool_{get|set}_link_ksettings The ethtool API {get|set}_settings is deprecated. We move this driver to new API {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000/e1000_ethtool.c | 117 +++++++++++------------ 1 file changed, 58 insertions(+), 59 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c index 975eeb885ca2..ec8aa4562cc9 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c +++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c @@ -103,104 +103,104 @@ static const char e1000_gstrings_test[][ETH_GSTRING_LEN] = { #define E1000_TEST_LEN ARRAY_SIZE(e1000_gstrings_test) -static int e1000_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int e1000_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + u32 supported, advertising; if (hw->media_type == e1000_media_type_copper) { - ecmd->supported = (SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Full| - SUPPORTED_Autoneg | - SUPPORTED_TP); - ecmd->advertising = ADVERTISED_TP; + supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full| + SUPPORTED_Autoneg | + SUPPORTED_TP); + advertising = ADVERTISED_TP; if (hw->autoneg == 1) { - ecmd->advertising |= ADVERTISED_Autoneg; + advertising |= ADVERTISED_Autoneg; /* the e1000 autoneg seems to match ethtool nicely */ - ecmd->advertising |= hw->autoneg_advertised; + advertising |= hw->autoneg_advertised; } - ecmd->port = PORT_TP; - ecmd->phy_address = hw->phy_addr; - - if (hw->mac_type == e1000_82543) - ecmd->transceiver = XCVR_EXTERNAL; - else - ecmd->transceiver = XCVR_INTERNAL; - + cmd->base.port = PORT_TP; + cmd->base.phy_address = hw->phy_addr; } else { - ecmd->supported = (SUPPORTED_1000baseT_Full | - SUPPORTED_FIBRE | - SUPPORTED_Autoneg); + supported = (SUPPORTED_1000baseT_Full | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg); - ecmd->advertising = (ADVERTISED_1000baseT_Full | - ADVERTISED_FIBRE | - ADVERTISED_Autoneg); + advertising = (ADVERTISED_1000baseT_Full | + ADVERTISED_FIBRE | + ADVERTISED_Autoneg); - ecmd->port = PORT_FIBRE; - - if (hw->mac_type >= e1000_82545) - ecmd->transceiver = XCVR_INTERNAL; - else - ecmd->transceiver = XCVR_EXTERNAL; + cmd->base.port = PORT_FIBRE; } if (er32(STATUS) & E1000_STATUS_LU) { e1000_get_speed_and_duplex(hw, &adapter->link_speed, &adapter->link_duplex); - ethtool_cmd_speed_set(ecmd, adapter->link_speed); + cmd->base.speed = adapter->link_speed; /* unfortunately FULL_DUPLEX != DUPLEX_FULL * and HALF_DUPLEX != DUPLEX_HALF */ if (adapter->link_duplex == FULL_DUPLEX) - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; else - ecmd->duplex = DUPLEX_HALF; + cmd->base.duplex = DUPLEX_HALF; } else { - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; } - ecmd->autoneg = ((hw->media_type == e1000_media_type_fiber) || + cmd->base.autoneg = ((hw->media_type == e1000_media_type_fiber) || hw->autoneg) ? AUTONEG_ENABLE : AUTONEG_DISABLE; /* MDI-X => 1; MDI => 0 */ if ((hw->media_type == e1000_media_type_copper) && netif_carrier_ok(netdev)) - ecmd->eth_tp_mdix = (!!adapter->phy_info.mdix_mode ? + cmd->base.eth_tp_mdix = (!!adapter->phy_info.mdix_mode ? ETH_TP_MDI_X : ETH_TP_MDI); else - ecmd->eth_tp_mdix = ETH_TP_MDI_INVALID; + cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID; if (hw->mdix == AUTO_ALL_MODES) - ecmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; + cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; else - ecmd->eth_tp_mdix_ctrl = hw->mdix; + cmd->base.eth_tp_mdix_ctrl = hw->mdix; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); + return 0; } -static int e1000_set_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int e1000_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + u32 advertising; + + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); /* MDI setting is only allowed when autoneg enabled because * some hardware doesn't allow MDI setting when speed or * duplex is forced. */ - if (ecmd->eth_tp_mdix_ctrl) { + if (cmd->base.eth_tp_mdix_ctrl) { if (hw->media_type != e1000_media_type_copper) return -EOPNOTSUPP; - if ((ecmd->eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO) && - (ecmd->autoneg != AUTONEG_ENABLE)) { + if ((cmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO) && + (cmd->base.autoneg != AUTONEG_ENABLE)) { e_err(drv, "forcing MDI/MDI-X state is not supported when link speed and/or duplex are forced\n"); return -EINVAL; } @@ -209,32 +209,31 @@ static int e1000_set_settings(struct net_device *netdev, while (test_and_set_bit(__E1000_RESETTING, &adapter->flags)) msleep(1); - if (ecmd->autoneg == AUTONEG_ENABLE) { + if (cmd->base.autoneg == AUTONEG_ENABLE) { hw->autoneg = 1; if (hw->media_type == e1000_media_type_fiber) hw->autoneg_advertised = ADVERTISED_1000baseT_Full | - ADVERTISED_FIBRE | - ADVERTISED_Autoneg; + ADVERTISED_FIBRE | + ADVERTISED_Autoneg; else - hw->autoneg_advertised = ecmd->advertising | + hw->autoneg_advertised = advertising | ADVERTISED_TP | ADVERTISED_Autoneg; - ecmd->advertising = hw->autoneg_advertised; } else { - u32 speed = ethtool_cmd_speed(ecmd); + u32 speed = cmd->base.speed; /* calling this overrides forced MDI setting */ - if (e1000_set_spd_dplx(adapter, speed, ecmd->duplex)) { + if (e1000_set_spd_dplx(adapter, speed, cmd->base.duplex)) { clear_bit(__E1000_RESETTING, &adapter->flags); return -EINVAL; } } /* MDI-X => 2; MDI => 1; Auto => 3 */ - if (ecmd->eth_tp_mdix_ctrl) { - if (ecmd->eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO) + if (cmd->base.eth_tp_mdix_ctrl) { + if (cmd->base.eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO) hw->mdix = AUTO_ALL_MODES; else - hw->mdix = ecmd->eth_tp_mdix_ctrl; + hw->mdix = cmd->base.eth_tp_mdix_ctrl; } /* reset the link */ @@ -1875,8 +1874,6 @@ static void e1000_get_strings(struct net_device *netdev, u32 stringset, } static const struct ethtool_ops e1000_ethtool_ops = { - .get_settings = e1000_get_settings, - .set_settings = e1000_set_settings, .get_drvinfo = e1000_get_drvinfo, .get_regs_len = e1000_get_regs_len, .get_regs = e1000_get_regs, @@ -1901,6 +1898,8 @@ static const struct ethtool_ops e1000_ethtool_ops = { .get_coalesce = e1000_get_coalesce, .set_coalesce = e1000_set_coalesce, .get_ts_info = ethtool_op_get_ts_info, + .get_link_ksettings = e1000_get_link_ksettings, + .set_link_ksettings = e1000_set_link_ksettings, }; void e1000_set_ethtool_ops(struct net_device *netdev) -- cgit From 37a30b435b92a6b577795c642b6326fd0ea96e3f Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 16 Mar 2017 10:27:08 -0700 Subject: net: bcmgenet: Track per TX/RX rings statistics __bcmgenet_tx_reclaim() is currently summing TX bytes/packets in a way that is not SMP friendly, mutliples CPUs could run __bcmgenet_tx_reclaim() independently and still update stats->tx_bytes and stats->tx_packets, cloberring the other CPUs statistics. Fix this by tracking per RX and TX rings the number of bytes, packets, dropped and errors statistics, and provide a bcmgenet_get_stats() function which aggregates everything and returns a consistent output. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 77 +++++++++++++++++++++++--- drivers/net/ethernet/broadcom/genet/bcmgenet.h | 6 ++ 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 44f9c0a1f85d..f493276d432a 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -707,6 +707,19 @@ struct bcmgenet_stats { .reg_offset = offset, \ } +#define STAT_GENET_Q(num) \ + STAT_GENET_SOFT_MIB("txq" __stringify(num) "_packets", \ + tx_rings[num].packets), \ + STAT_GENET_SOFT_MIB("txq" __stringify(num) "_bytes", \ + tx_rings[num].bytes), \ + STAT_GENET_SOFT_MIB("rxq" __stringify(num) "_bytes", \ + rx_rings[num].bytes), \ + STAT_GENET_SOFT_MIB("rxq" __stringify(num) "_packets", \ + rx_rings[num].packets), \ + STAT_GENET_SOFT_MIB("rxq" __stringify(num) "_errors", \ + rx_rings[num].errors), \ + STAT_GENET_SOFT_MIB("rxq" __stringify(num) "_dropped", \ + rx_rings[num].dropped) /* There is a 0xC gap between the end of RX and beginning of TX stats and then * between the end of TX stats and the beginning of the RX RUNT @@ -801,6 +814,12 @@ static const struct bcmgenet_stats bcmgenet_gstrings_stats[] = { STAT_GENET_SOFT_MIB("alloc_rx_buff_failed", mib.alloc_rx_buff_failed), STAT_GENET_SOFT_MIB("rx_dma_failed", mib.rx_dma_failed), STAT_GENET_SOFT_MIB("tx_dma_failed", mib.tx_dma_failed), + /* Per TX queues */ + STAT_GENET_Q(0), + STAT_GENET_Q(1), + STAT_GENET_Q(2), + STAT_GENET_Q(3), + STAT_GENET_Q(16), }; #define BCMGENET_STATS_LEN ARRAY_SIZE(bcmgenet_gstrings_stats) @@ -1298,8 +1317,8 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev, ring->free_bds += txbds_processed; ring->c_index = c_index; - dev->stats.tx_packets += pkts_compl; - dev->stats.tx_bytes += bytes_compl; + ring->packets += pkts_compl; + ring->bytes += bytes_compl; netdev_tx_completed_queue(netdev_get_tx_queue(dev, ring->queue), pkts_compl, bytes_compl); @@ -1694,8 +1713,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, DMA_P_INDEX_DISCARD_CNT_MASK; if (discards > ring->old_discards) { discards = discards - ring->old_discards; - dev->stats.rx_missed_errors += discards; - dev->stats.rx_errors += discards; + ring->errors += discards; ring->old_discards += discards; /* Clear HW register when we reach 75% of maximum 0xFFFF */ @@ -1718,7 +1736,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, skb = bcmgenet_rx_refill(priv, cb); if (unlikely(!skb)) { - dev->stats.rx_dropped++; + ring->dropped++; goto next; } @@ -1746,7 +1764,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, if (unlikely(!(dma_flag & DMA_EOP) || !(dma_flag & DMA_SOP))) { netif_err(priv, rx_status, dev, "dropping fragmented packet!\n"); - dev->stats.rx_errors++; + ring->errors++; dev_kfree_skb_any(skb); goto next; } @@ -1795,8 +1813,8 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring, /*Finish setting up the received SKB and send it to the kernel*/ skb->protocol = eth_type_trans(skb, priv->dev); - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; + ring->packets++; + ring->bytes += len; if (dma_flag & DMA_RX_MULT) dev->stats.multicast++; @@ -3134,6 +3152,48 @@ static int bcmgenet_set_mac_addr(struct net_device *dev, void *p) return 0; } +static struct net_device_stats *bcmgenet_get_stats(struct net_device *dev) +{ + struct bcmgenet_priv *priv = netdev_priv(dev); + unsigned long tx_bytes = 0, tx_packets = 0; + unsigned long rx_bytes = 0, rx_packets = 0; + unsigned long rx_errors = 0, rx_dropped = 0; + struct bcmgenet_tx_ring *tx_ring; + struct bcmgenet_rx_ring *rx_ring; + unsigned int q; + + for (q = 0; q < priv->hw_params->tx_queues; q++) { + tx_ring = &priv->tx_rings[q]; + tx_bytes += tx_ring->bytes; + tx_packets += tx_ring->packets; + } + tx_ring = &priv->tx_rings[DESC_INDEX]; + tx_bytes += tx_ring->bytes; + tx_packets += tx_ring->packets; + + for (q = 0; q < priv->hw_params->rx_queues; q++) { + rx_ring = &priv->rx_rings[q]; + + rx_bytes += rx_ring->bytes; + rx_packets += rx_ring->packets; + rx_errors += rx_ring->errors; + rx_dropped += rx_ring->dropped; + } + rx_ring = &priv->rx_rings[DESC_INDEX]; + rx_bytes += rx_ring->bytes; + rx_packets += rx_ring->packets; + rx_errors += rx_ring->errors; + rx_dropped += rx_ring->dropped; + + dev->stats.tx_bytes = tx_bytes; + dev->stats.tx_packets = tx_packets; + dev->stats.rx_bytes = rx_bytes; + dev->stats.rx_packets = rx_packets; + dev->stats.rx_errors = rx_errors; + dev->stats.rx_missed_errors = rx_errors; + return &dev->stats; +} + static const struct net_device_ops bcmgenet_netdev_ops = { .ndo_open = bcmgenet_open, .ndo_stop = bcmgenet_close, @@ -3146,6 +3206,7 @@ static const struct net_device_ops bcmgenet_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = bcmgenet_poll_controller, #endif + .ndo_get_stats = bcmgenet_get_stats, }; /* Array of GENET hardware parameters/characteristics */ diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 5692c0582434..efd07020b89f 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -552,6 +552,8 @@ struct bcmgenet_skb_cb { struct bcmgenet_tx_ring { spinlock_t lock; /* ring lock */ struct napi_struct napi; /* NAPI per tx queue */ + unsigned long packets; + unsigned long bytes; unsigned int index; /* ring index */ unsigned int queue; /* queue index */ struct enet_cb *cbs; /* tx ring buffer control block*/ @@ -570,6 +572,10 @@ struct bcmgenet_tx_ring { struct bcmgenet_rx_ring { struct napi_struct napi; /* Rx NAPI struct */ + unsigned long bytes; + unsigned long packets; + unsigned long errors; + unsigned long dropped; unsigned int index; /* Rx ring index */ struct enet_cb *cbs; /* Rx ring buffer control block */ unsigned int size; /* Rx ring size */ -- cgit From fb052fdd2662f7725b9b86889a6664523f872aab Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 26 Jan 2017 22:19:53 +0100 Subject: e1000e: use new API ethtool_{get|set}_link_ksettings The ethtool API {get|set}_settings is deprecated. We move this driver to new API {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/ethtool.c | 111 +++++++++++++++------------- 1 file changed, 59 insertions(+), 52 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index 7aff68a4a4df..e70b1ebff60d 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -117,55 +117,52 @@ static const char e1000_gstrings_test[][ETH_GSTRING_LEN] = { #define E1000_TEST_LEN ARRAY_SIZE(e1000_gstrings_test) -static int e1000_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int e1000_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - u32 speed; + u32 speed, supported, advertising; if (hw->phy.media_type == e1000_media_type_copper) { - ecmd->supported = (SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_Autoneg | - SUPPORTED_TP); + supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP); if (hw->phy.type == e1000_phy_ife) - ecmd->supported &= ~SUPPORTED_1000baseT_Full; - ecmd->advertising = ADVERTISED_TP; + supported &= ~SUPPORTED_1000baseT_Full; + advertising = ADVERTISED_TP; if (hw->mac.autoneg == 1) { - ecmd->advertising |= ADVERTISED_Autoneg; + advertising |= ADVERTISED_Autoneg; /* the e1000 autoneg seems to match ethtool nicely */ - ecmd->advertising |= hw->phy.autoneg_advertised; + advertising |= hw->phy.autoneg_advertised; } - ecmd->port = PORT_TP; - ecmd->phy_address = hw->phy.addr; - ecmd->transceiver = XCVR_INTERNAL; - + cmd->base.port = PORT_TP; + cmd->base.phy_address = hw->phy.addr; } else { - ecmd->supported = (SUPPORTED_1000baseT_Full | - SUPPORTED_FIBRE | - SUPPORTED_Autoneg); + supported = (SUPPORTED_1000baseT_Full | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg); - ecmd->advertising = (ADVERTISED_1000baseT_Full | - ADVERTISED_FIBRE | - ADVERTISED_Autoneg); + advertising = (ADVERTISED_1000baseT_Full | + ADVERTISED_FIBRE | + ADVERTISED_Autoneg); - ecmd->port = PORT_FIBRE; - ecmd->transceiver = XCVR_EXTERNAL; + cmd->base.port = PORT_FIBRE; } speed = SPEED_UNKNOWN; - ecmd->duplex = DUPLEX_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; if (netif_running(netdev)) { if (netif_carrier_ok(netdev)) { speed = adapter->link_speed; - ecmd->duplex = adapter->link_duplex - 1; + cmd->base.duplex = adapter->link_duplex - 1; } } else if (!pm_runtime_suspended(netdev->dev.parent)) { u32 status = er32(STATUS); @@ -179,30 +176,36 @@ static int e1000_get_settings(struct net_device *netdev, speed = SPEED_10; if (status & E1000_STATUS_FD) - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; else - ecmd->duplex = DUPLEX_HALF; + cmd->base.duplex = DUPLEX_HALF; } } - ethtool_cmd_speed_set(ecmd, speed); - ecmd->autoneg = ((hw->phy.media_type == e1000_media_type_fiber) || + cmd->base.speed = speed; + cmd->base.autoneg = ((hw->phy.media_type == e1000_media_type_fiber) || hw->mac.autoneg) ? AUTONEG_ENABLE : AUTONEG_DISABLE; /* MDI-X => 2; MDI =>1; Invalid =>0 */ if ((hw->phy.media_type == e1000_media_type_copper) && netif_carrier_ok(netdev)) - ecmd->eth_tp_mdix = hw->phy.is_mdix ? ETH_TP_MDI_X : ETH_TP_MDI; + cmd->base.eth_tp_mdix = hw->phy.is_mdix ? + ETH_TP_MDI_X : ETH_TP_MDI; else - ecmd->eth_tp_mdix = ETH_TP_MDI_INVALID; + cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID; if (hw->phy.mdix == AUTO_ALL_MODES) - ecmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; + cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; else - ecmd->eth_tp_mdix_ctrl = hw->phy.mdix; + cmd->base.eth_tp_mdix_ctrl = hw->phy.mdix; if (hw->phy.media_type != e1000_media_type_copper) - ecmd->eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; + cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return 0; } @@ -262,12 +265,16 @@ err_inval: return -EINVAL; } -static int e1000_set_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int e1000_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; int ret_val = 0; + u32 advertising; + + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); pm_runtime_get_sync(netdev->dev.parent); @@ -285,14 +292,14 @@ static int e1000_set_settings(struct net_device *netdev, * some hardware doesn't allow MDI setting when speed or * duplex is forced. */ - if (ecmd->eth_tp_mdix_ctrl) { + if (cmd->base.eth_tp_mdix_ctrl) { if (hw->phy.media_type != e1000_media_type_copper) { ret_val = -EOPNOTSUPP; goto out; } - if ((ecmd->eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO) && - (ecmd->autoneg != AUTONEG_ENABLE)) { + if ((cmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO) && + (cmd->base.autoneg != AUTONEG_ENABLE)) { e_err("forcing MDI/MDI-X state is not supported when link speed and/or duplex are forced\n"); ret_val = -EINVAL; goto out; @@ -302,35 +309,35 @@ static int e1000_set_settings(struct net_device *netdev, while (test_and_set_bit(__E1000_RESETTING, &adapter->state)) usleep_range(1000, 2000); - if (ecmd->autoneg == AUTONEG_ENABLE) { + if (cmd->base.autoneg == AUTONEG_ENABLE) { hw->mac.autoneg = 1; if (hw->phy.media_type == e1000_media_type_fiber) hw->phy.autoneg_advertised = ADVERTISED_1000baseT_Full | ADVERTISED_FIBRE | ADVERTISED_Autoneg; else - hw->phy.autoneg_advertised = ecmd->advertising | + hw->phy.autoneg_advertised = advertising | ADVERTISED_TP | ADVERTISED_Autoneg; - ecmd->advertising = hw->phy.autoneg_advertised; + advertising = hw->phy.autoneg_advertised; if (adapter->fc_autoneg) hw->fc.requested_mode = e1000_fc_default; } else { - u32 speed = ethtool_cmd_speed(ecmd); + u32 speed = cmd->base.speed; /* calling this overrides forced MDI setting */ - if (e1000_set_spd_dplx(adapter, speed, ecmd->duplex)) { + if (e1000_set_spd_dplx(adapter, speed, cmd->base.duplex)) { ret_val = -EINVAL; goto out; } } /* MDI-X => 2; MDI => 1; Auto => 3 */ - if (ecmd->eth_tp_mdix_ctrl) { + if (cmd->base.eth_tp_mdix_ctrl) { /* fix up the value for auto (3 => 0) as zero is mapped * internally to auto */ - if (ecmd->eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO) + if (cmd->base.eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO) hw->phy.mdix = AUTO_ALL_MODES; else - hw->phy.mdix = ecmd->eth_tp_mdix_ctrl; + hw->phy.mdix = cmd->base.eth_tp_mdix_ctrl; } /* reset the link */ @@ -2313,8 +2320,6 @@ static int e1000e_get_ts_info(struct net_device *netdev, } static const struct ethtool_ops e1000_ethtool_ops = { - .get_settings = e1000_get_settings, - .set_settings = e1000_set_settings, .get_drvinfo = e1000_get_drvinfo, .get_regs_len = e1000_get_regs_len, .get_regs = e1000_get_regs, @@ -2342,6 +2347,8 @@ static const struct ethtool_ops e1000_ethtool_ops = { .get_ts_info = e1000e_get_ts_info, .get_eee = e1000e_get_eee, .set_eee = e1000e_set_eee, + .get_link_ksettings = e1000_get_link_ksettings, + .set_link_ksettings = e1000_set_link_ksettings, }; void e1000e_set_ethtool_ops(struct net_device *netdev) -- cgit From c19153008ba0f7b86234820e8a87d58806707a15 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 5 Feb 2017 18:55:44 +0100 Subject: igb: use new API ethtool_{get|set}_link_ksettings The ethtool API {get|set}_settings is deprecated. We move this driver to new API {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_ethtool.c | 138 ++++++++++++++------------- 1 file changed, 73 insertions(+), 65 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 797b9daba224..0efb62db6efd 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -151,7 +151,8 @@ static const char igb_priv_flags_strings[][ETH_GSTRING_LEN] = { #define IGB_PRIV_FLAGS_STR_LEN ARRAY_SIZE(igb_priv_flags_strings) -static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +static int igb_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -159,76 +160,73 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) struct e1000_sfp_flags *eth_flags = &dev_spec->eth_flags; u32 status; u32 speed; + u32 supported, advertising; status = rd32(E1000_STATUS); if (hw->phy.media_type == e1000_media_type_copper) { - ecmd->supported = (SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Full| - SUPPORTED_Autoneg | - SUPPORTED_TP | - SUPPORTED_Pause); - ecmd->advertising = ADVERTISED_TP; + supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full| + SUPPORTED_Autoneg | + SUPPORTED_TP | + SUPPORTED_Pause); + advertising = ADVERTISED_TP; if (hw->mac.autoneg == 1) { - ecmd->advertising |= ADVERTISED_Autoneg; + advertising |= ADVERTISED_Autoneg; /* the e1000 autoneg seems to match ethtool nicely */ - ecmd->advertising |= hw->phy.autoneg_advertised; + advertising |= hw->phy.autoneg_advertised; } - ecmd->port = PORT_TP; - ecmd->phy_address = hw->phy.addr; - ecmd->transceiver = XCVR_INTERNAL; + cmd->base.port = PORT_TP; + cmd->base.phy_address = hw->phy.addr; } else { - ecmd->supported = (SUPPORTED_FIBRE | - SUPPORTED_1000baseKX_Full | - SUPPORTED_Autoneg | - SUPPORTED_Pause); - ecmd->advertising = (ADVERTISED_FIBRE | - ADVERTISED_1000baseKX_Full); + supported = (SUPPORTED_FIBRE | + SUPPORTED_1000baseKX_Full | + SUPPORTED_Autoneg | + SUPPORTED_Pause); + advertising = (ADVERTISED_FIBRE | + ADVERTISED_1000baseKX_Full); if (hw->mac.type == e1000_i354) { if ((hw->device_id == E1000_DEV_ID_I354_BACKPLANE_2_5GBPS) && !(status & E1000_STATUS_2P5_SKU_OVER)) { - ecmd->supported |= SUPPORTED_2500baseX_Full; - ecmd->supported &= - ~SUPPORTED_1000baseKX_Full; - ecmd->advertising |= ADVERTISED_2500baseX_Full; - ecmd->advertising &= - ~ADVERTISED_1000baseKX_Full; + supported |= SUPPORTED_2500baseX_Full; + supported &= ~SUPPORTED_1000baseKX_Full; + advertising |= ADVERTISED_2500baseX_Full; + advertising &= ~ADVERTISED_1000baseKX_Full; } } if (eth_flags->e100_base_fx) { - ecmd->supported |= SUPPORTED_100baseT_Full; - ecmd->advertising |= ADVERTISED_100baseT_Full; + supported |= SUPPORTED_100baseT_Full; + advertising |= ADVERTISED_100baseT_Full; } if (hw->mac.autoneg == 1) - ecmd->advertising |= ADVERTISED_Autoneg; + advertising |= ADVERTISED_Autoneg; - ecmd->port = PORT_FIBRE; - ecmd->transceiver = XCVR_EXTERNAL; + cmd->base.port = PORT_FIBRE; } if (hw->mac.autoneg != 1) - ecmd->advertising &= ~(ADVERTISED_Pause | - ADVERTISED_Asym_Pause); + advertising &= ~(ADVERTISED_Pause | + ADVERTISED_Asym_Pause); switch (hw->fc.requested_mode) { case e1000_fc_full: - ecmd->advertising |= ADVERTISED_Pause; + advertising |= ADVERTISED_Pause; break; case e1000_fc_rx_pause: - ecmd->advertising |= (ADVERTISED_Pause | - ADVERTISED_Asym_Pause); + advertising |= (ADVERTISED_Pause | + ADVERTISED_Asym_Pause); break; case e1000_fc_tx_pause: - ecmd->advertising |= ADVERTISED_Asym_Pause; + advertising |= ADVERTISED_Asym_Pause; break; default: - ecmd->advertising &= ~(ADVERTISED_Pause | - ADVERTISED_Asym_Pause); + advertising &= ~(ADVERTISED_Pause | + ADVERTISED_Asym_Pause); } if (status & E1000_STATUS_LU) { if ((status & E1000_STATUS_2P5_SKU) && @@ -243,39 +241,46 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) } if ((status & E1000_STATUS_FD) || hw->phy.media_type != e1000_media_type_copper) - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; else - ecmd->duplex = DUPLEX_HALF; + cmd->base.duplex = DUPLEX_HALF; } else { speed = SPEED_UNKNOWN; - ecmd->duplex = DUPLEX_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; } - ethtool_cmd_speed_set(ecmd, speed); + cmd->base.speed = speed; if ((hw->phy.media_type == e1000_media_type_fiber) || hw->mac.autoneg) - ecmd->autoneg = AUTONEG_ENABLE; + cmd->base.autoneg = AUTONEG_ENABLE; else - ecmd->autoneg = AUTONEG_DISABLE; + cmd->base.autoneg = AUTONEG_DISABLE; /* MDI-X => 2; MDI =>1; Invalid =>0 */ if (hw->phy.media_type == e1000_media_type_copper) - ecmd->eth_tp_mdix = hw->phy.is_mdix ? ETH_TP_MDI_X : + cmd->base.eth_tp_mdix = hw->phy.is_mdix ? ETH_TP_MDI_X : ETH_TP_MDI; else - ecmd->eth_tp_mdix = ETH_TP_MDI_INVALID; + cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID; if (hw->phy.mdix == AUTO_ALL_MODES) - ecmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; + cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO; else - ecmd->eth_tp_mdix_ctrl = hw->phy.mdix; + cmd->base.eth_tp_mdix_ctrl = hw->phy.mdix; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + advertising); return 0; } -static int igb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +static int igb_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + u32 advertising; /* When SoL/IDER sessions are active, autoneg/speed/duplex * cannot be changed @@ -290,12 +295,12 @@ static int igb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) * some hardware doesn't allow MDI setting when speed or * duplex is forced. */ - if (ecmd->eth_tp_mdix_ctrl) { + if (cmd->base.eth_tp_mdix_ctrl) { if (hw->phy.media_type != e1000_media_type_copper) return -EOPNOTSUPP; - if ((ecmd->eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO) && - (ecmd->autoneg != AUTONEG_ENABLE)) { + if ((cmd->base.eth_tp_mdix_ctrl != ETH_TP_MDI_AUTO) && + (cmd->base.autoneg != AUTONEG_ENABLE)) { dev_err(&adapter->pdev->dev, "forcing MDI/MDI-X state is not supported when link speed and/or duplex are forced\n"); return -EINVAL; } @@ -304,10 +309,13 @@ static int igb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) while (test_and_set_bit(__IGB_RESETTING, &adapter->state)) usleep_range(1000, 2000); - if (ecmd->autoneg == AUTONEG_ENABLE) { + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); + + if (cmd->base.autoneg == AUTONEG_ENABLE) { hw->mac.autoneg = 1; if (hw->phy.media_type == e1000_media_type_fiber) { - hw->phy.autoneg_advertised = ecmd->advertising | + hw->phy.autoneg_advertised = advertising | ADVERTISED_FIBRE | ADVERTISED_Autoneg; switch (adapter->link_speed) { @@ -327,31 +335,31 @@ static int igb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) break; } } else { - hw->phy.autoneg_advertised = ecmd->advertising | + hw->phy.autoneg_advertised = advertising | ADVERTISED_TP | ADVERTISED_Autoneg; } - ecmd->advertising = hw->phy.autoneg_advertised; + advertising = hw->phy.autoneg_advertised; if (adapter->fc_autoneg) hw->fc.requested_mode = e1000_fc_default; } else { - u32 speed = ethtool_cmd_speed(ecmd); + u32 speed = cmd->base.speed; /* calling this overrides forced MDI setting */ - if (igb_set_spd_dplx(adapter, speed, ecmd->duplex)) { + if (igb_set_spd_dplx(adapter, speed, cmd->base.duplex)) { clear_bit(__IGB_RESETTING, &adapter->state); return -EINVAL; } } /* MDI-X => 2; MDI => 1; Auto => 3 */ - if (ecmd->eth_tp_mdix_ctrl) { + if (cmd->base.eth_tp_mdix_ctrl) { /* fix up the value for auto (3 => 0) as zero is mapped * internally to auto */ - if (ecmd->eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO) + if (cmd->base.eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO) hw->phy.mdix = AUTO_ALL_MODES; else - hw->phy.mdix = ecmd->eth_tp_mdix_ctrl; + hw->phy.mdix = cmd->base.eth_tp_mdix_ctrl; } /* reset the link */ @@ -3444,8 +3452,6 @@ static int igb_set_priv_flags(struct net_device *netdev, u32 priv_flags) } static const struct ethtool_ops igb_ethtool_ops = { - .get_settings = igb_get_settings, - .set_settings = igb_set_settings, .get_drvinfo = igb_get_drvinfo, .get_regs_len = igb_get_regs_len, .get_regs = igb_get_regs, @@ -3485,6 +3491,8 @@ static const struct ethtool_ops igb_ethtool_ops = { .set_priv_flags = igb_set_priv_flags, .begin = igb_ethtool_begin, .complete = igb_ethtool_complete, + .get_link_ksettings = igb_get_link_ksettings, + .set_link_ksettings = igb_set_link_ksettings, }; void igb_set_ethtool_ops(struct net_device *netdev) -- cgit From 1120ecd5aa38c385e747b8fb300d5597dcb2d19c Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 5 Feb 2017 23:55:19 +0100 Subject: igbvf: use new API ethtool_{get|set}_link_ksettings The ethtool API {get|set}_settings is deprecated. We move this driver to new API {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igbvf/ethtool.c | 38 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c index 8dea1b1367ef..34faa113a8a0 100644 --- a/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -71,45 +71,45 @@ static const char igbvf_gstrings_test[][ETH_GSTRING_LEN] = { #define IGBVF_TEST_LEN ARRAY_SIZE(igbvf_gstrings_test) -static int igbvf_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int igbvf_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct igbvf_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; u32 status; - ecmd->supported = SUPPORTED_1000baseT_Full; + ethtool_link_ksettings_zero_link_mode(cmd, supported); + ethtool_link_ksettings_add_link_mode(cmd, supported, 1000baseT_Full); + ethtool_link_ksettings_zero_link_mode(cmd, advertising); + ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full); - ecmd->advertising = ADVERTISED_1000baseT_Full; - - ecmd->port = -1; - ecmd->transceiver = XCVR_DUMMY1; + cmd->base.port = -1; status = er32(STATUS); if (status & E1000_STATUS_LU) { if (status & E1000_STATUS_SPEED_1000) - ethtool_cmd_speed_set(ecmd, SPEED_1000); + cmd->base.speed = SPEED_1000; else if (status & E1000_STATUS_SPEED_100) - ethtool_cmd_speed_set(ecmd, SPEED_100); + cmd->base.speed = SPEED_100; else - ethtool_cmd_speed_set(ecmd, SPEED_10); + cmd->base.speed = SPEED_10; if (status & E1000_STATUS_FD) - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; else - ecmd->duplex = DUPLEX_HALF; + cmd->base.duplex = DUPLEX_HALF; } else { - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; } - ecmd->autoneg = AUTONEG_DISABLE; + cmd->base.autoneg = AUTONEG_DISABLE; return 0; } -static int igbvf_set_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int igbvf_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { return -EOPNOTSUPP; } @@ -443,8 +443,6 @@ static void igbvf_get_strings(struct net_device *netdev, u32 stringset, } static const struct ethtool_ops igbvf_ethtool_ops = { - .get_settings = igbvf_get_settings, - .set_settings = igbvf_set_settings, .get_drvinfo = igbvf_get_drvinfo, .get_regs_len = igbvf_get_regs_len, .get_regs = igbvf_get_regs, @@ -467,6 +465,8 @@ static const struct ethtool_ops igbvf_ethtool_ops = { .get_ethtool_stats = igbvf_get_ethtool_stats, .get_coalesce = igbvf_get_coalesce, .set_coalesce = igbvf_set_coalesce, + .get_link_ksettings = igbvf_get_link_ksettings, + .set_link_ksettings = igbvf_set_link_ksettings, }; void igbvf_set_ethtool_ops(struct net_device *netdev) -- cgit From f22913d0b5560ae621e8a7391e9547d5a6fd8893 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Mon, 6 Feb 2017 00:11:11 +0100 Subject: ixgb: use new API ethtool_{get|set}_link_ksettings The ethtool API {get|set}_settings is deprecated. We move this driver to new API {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c | 39 +++++++++++++++----------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c index e5d72559cca9..d10a0d242dda 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_ethtool.c @@ -94,24 +94,30 @@ static struct ixgb_stats ixgb_gstrings_stats[] = { #define IXGB_STATS_LEN ARRAY_SIZE(ixgb_gstrings_stats) static int -ixgb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +ixgb_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct ixgb_adapter *adapter = netdev_priv(netdev); - ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); - ecmd->advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE); - ecmd->port = PORT_FIBRE; - ecmd->transceiver = XCVR_EXTERNAL; + ethtool_link_ksettings_zero_link_mode(cmd, supported); + ethtool_link_ksettings_add_link_mode(cmd, supported, 10000baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); + + ethtool_link_ksettings_zero_link_mode(cmd, advertising); + ethtool_link_ksettings_add_link_mode(cmd, advertising, 10000baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE); + + cmd->base.port = PORT_FIBRE; if (netif_carrier_ok(adapter->netdev)) { - ethtool_cmd_speed_set(ecmd, SPEED_10000); - ecmd->duplex = DUPLEX_FULL; + cmd->base.speed = SPEED_10000; + cmd->base.duplex = DUPLEX_FULL; } else { - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; } - ecmd->autoneg = AUTONEG_DISABLE; + cmd->base.autoneg = AUTONEG_DISABLE; return 0; } @@ -126,13 +132,14 @@ void ixgb_set_speed_duplex(struct net_device *netdev) } static int -ixgb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +ixgb_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) { struct ixgb_adapter *adapter = netdev_priv(netdev); - u32 speed = ethtool_cmd_speed(ecmd); + u32 speed = cmd->base.speed; - if (ecmd->autoneg == AUTONEG_ENABLE || - (speed + ecmd->duplex != SPEED_10000 + DUPLEX_FULL)) + if (cmd->base.autoneg == AUTONEG_ENABLE || + (speed + cmd->base.duplex != SPEED_10000 + DUPLEX_FULL)) return -EINVAL; if (netif_running(adapter->netdev)) { @@ -630,8 +637,6 @@ ixgb_get_strings(struct net_device *netdev, u32 stringset, u8 *data) } static const struct ethtool_ops ixgb_ethtool_ops = { - .get_settings = ixgb_get_settings, - .set_settings = ixgb_set_settings, .get_drvinfo = ixgb_get_drvinfo, .get_regs_len = ixgb_get_regs_len, .get_regs = ixgb_get_regs, @@ -649,6 +654,8 @@ static const struct ethtool_ops ixgb_ethtool_ops = { .set_phys_id = ixgb_set_phys_id, .get_sset_count = ixgb_get_sset_count, .get_ethtool_stats = ixgb_get_ethtool_stats, + .get_link_ksettings = ixgb_get_link_ksettings, + .set_link_ksettings = ixgb_set_link_ksettings, }; void ixgb_set_ethtool_ops(struct net_device *netdev) -- cgit From 8bae3551e93de4e8a5b959c495b06de9264be0d5 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 16 Mar 2017 23:18:47 +0100 Subject: net: usb: usbnet: add new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We add the new api {get|set}_link_ksettings to this driver. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Acked-by: Oliver Neukum Signed-off-by: David S. Miller --- drivers/net/usb/usbnet.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/usb/usbnet.h | 4 ++++ 2 files changed, 40 insertions(+) diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 3de65ea6531a..1b40b189435a 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -980,6 +980,40 @@ int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd) } EXPORT_SYMBOL_GPL(usbnet_set_settings); +int usbnet_get_link_ksettings(struct net_device *net, + struct ethtool_link_ksettings *cmd) +{ + struct usbnet *dev = netdev_priv(net); + + if (!dev->mii.mdio_read) + return -EOPNOTSUPP; + + return mii_ethtool_get_link_ksettings(&dev->mii, cmd); +} +EXPORT_SYMBOL_GPL(usbnet_get_link_ksettings); + +int usbnet_set_link_ksettings(struct net_device *net, + const struct ethtool_link_ksettings *cmd) +{ + struct usbnet *dev = netdev_priv(net); + int retval; + + if (!dev->mii.mdio_write) + return -EOPNOTSUPP; + + retval = mii_ethtool_set_link_ksettings(&dev->mii, cmd); + + /* link speed/duplex might have changed */ + if (dev->driver_info->link_reset) + dev->driver_info->link_reset(dev); + + /* hard_mtu or rx_urb_size may change in link_reset() */ + usbnet_update_max_qlen(dev); + + return retval; +} +EXPORT_SYMBOL_GPL(usbnet_set_link_ksettings); + u32 usbnet_get_link (struct net_device *net) { struct usbnet *dev = netdev_priv(net); @@ -1046,6 +1080,8 @@ static const struct ethtool_ops usbnet_ethtool_ops = { .get_msglevel = usbnet_get_msglevel, .set_msglevel = usbnet_set_msglevel, .get_ts_info = ethtool_op_get_ts_info, + .get_link_ksettings = usbnet_get_link_ksettings, + .set_link_ksettings = usbnet_set_link_ksettings, }; /*-------------------------------------------------------------------------*/ diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index 6e0ce8c7b8cb..5bd80078b7fe 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -265,6 +265,10 @@ extern int usbnet_get_settings(struct net_device *net, struct ethtool_cmd *cmd); extern int usbnet_set_settings(struct net_device *net, struct ethtool_cmd *cmd); +extern int usbnet_get_link_ksettings(struct net_device *net, + struct ethtool_link_ksettings *cmd); +extern int usbnet_set_link_ksettings(struct net_device *net, + const struct ethtool_link_ksettings *cmd); extern u32 usbnet_get_link(struct net_device *net); extern u32 usbnet_get_msglevel(struct net_device *); extern void usbnet_set_msglevel(struct net_device *, u32); -- cgit From eaf9a32a448bf01468bee5737c9391110ec05725 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 16 Mar 2017 23:18:48 +0100 Subject: net: usb: smsc95xx: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/usb/smsc95xx.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 831aa33d078a..4a8bf960cbb9 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -853,32 +853,32 @@ static void set_mdix_status(struct net_device *net, __u8 mdix_ctrl) pdata->mdix_ctrl = mdix_ctrl; } -static int smsc95xx_get_settings(struct net_device *net, - struct ethtool_cmd *cmd) +static int smsc95xx_get_link_ksettings(struct net_device *net, + struct ethtool_link_ksettings *cmd) { struct usbnet *dev = netdev_priv(net); struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); int retval; - retval = usbnet_get_settings(net, cmd); + retval = usbnet_get_link_ksettings(net, cmd); - cmd->eth_tp_mdix = pdata->mdix_ctrl; - cmd->eth_tp_mdix_ctrl = pdata->mdix_ctrl; + cmd->base.eth_tp_mdix = pdata->mdix_ctrl; + cmd->base.eth_tp_mdix_ctrl = pdata->mdix_ctrl; return retval; } -static int smsc95xx_set_settings(struct net_device *net, - struct ethtool_cmd *cmd) +static int smsc95xx_set_link_ksettings(struct net_device *net, + const struct ethtool_link_ksettings *cmd) { struct usbnet *dev = netdev_priv(net); struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); int retval; - if (pdata->mdix_ctrl != cmd->eth_tp_mdix_ctrl) - set_mdix_status(net, cmd->eth_tp_mdix_ctrl); + if (pdata->mdix_ctrl != cmd->base.eth_tp_mdix_ctrl) + set_mdix_status(net, cmd->base.eth_tp_mdix_ctrl); - retval = usbnet_set_settings(net, cmd); + retval = usbnet_set_link_ksettings(net, cmd); return retval; } @@ -889,8 +889,6 @@ static const struct ethtool_ops smsc95xx_ethtool_ops = { .get_drvinfo = usbnet_get_drvinfo, .get_msglevel = usbnet_get_msglevel, .set_msglevel = usbnet_set_msglevel, - .get_settings = smsc95xx_get_settings, - .set_settings = smsc95xx_set_settings, .get_eeprom_len = smsc95xx_ethtool_get_eeprom_len, .get_eeprom = smsc95xx_ethtool_get_eeprom, .set_eeprom = smsc95xx_ethtool_set_eeprom, @@ -898,6 +896,8 @@ static const struct ethtool_ops smsc95xx_ethtool_ops = { .get_regs = smsc95xx_ethtool_getregs, .get_wol = smsc95xx_ethtool_get_wol, .set_wol = smsc95xx_ethtool_set_wol, + .get_link_ksettings = smsc95xx_get_link_ksettings, + .set_link_ksettings = smsc95xx_set_link_ksettings, }; static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -- cgit From d8c3de2e8603f31de502f63e6997e55e7439eab9 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 16 Mar 2017 23:18:49 +0100 Subject: net: usb: sr9800: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/usb/sr9800.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/sr9800.c b/drivers/net/usb/sr9800.c index a50df0d8fb9a..a696b628782c 100644 --- a/drivers/net/usb/sr9800.c +++ b/drivers/net/usb/sr9800.c @@ -524,9 +524,9 @@ static const struct ethtool_ops sr9800_ethtool_ops = { .set_wol = sr_set_wol, .get_eeprom_len = sr_get_eeprom_len, .get_eeprom = sr_get_eeprom, - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, .nway_reset = usbnet_nway_reset, + .get_link_ksettings = usbnet_get_link_ksettings, + .set_link_ksettings = usbnet_set_link_ksettings, }; static int sr9800_link_reset(struct usbnet *dev) -- cgit From d0b3ab309fd896f80fefa4b1d1a7e45de1fa8a36 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 16 Mar 2017 23:18:50 +0100 Subject: net: usb: cdc_ncm: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index f317984f7536..b6c1d3abad96 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -131,8 +131,6 @@ static void cdc_ncm_get_strings(struct net_device __always_unused *netdev, u32 s static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx); static const struct ethtool_ops cdc_ncm_ethtool_ops = { - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, .get_link = usbnet_get_link, .nway_reset = usbnet_nway_reset, .get_drvinfo = usbnet_get_drvinfo, @@ -142,6 +140,8 @@ static const struct ethtool_ops cdc_ncm_ethtool_ops = { .get_sset_count = cdc_ncm_get_sset_count, .get_strings = cdc_ncm_get_strings, .get_ethtool_stats = cdc_ncm_get_ethtool_stats, + .get_link_ksettings = usbnet_get_link_ksettings, + .set_link_ksettings = usbnet_set_link_ksettings, }; static u32 cdc_ncm_check_rx_max(struct usbnet *dev, u32 new_rx) -- cgit From bcb58f777eb1b7030fbe525477530cbf0ac6480d Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 16 Mar 2017 23:18:51 +0100 Subject: net: usb: dm9601: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/usb/dm9601.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index 0b4bdd39106b..fea1b64ca26a 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c @@ -281,9 +281,9 @@ static const struct ethtool_ops dm9601_ethtool_ops = { .set_msglevel = usbnet_set_msglevel, .get_eeprom_len = dm9601_get_eeprom_len, .get_eeprom = dm9601_get_eeprom, - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, .nway_reset = usbnet_nway_reset, + .get_link_ksettings = usbnet_get_link_ksettings, + .set_link_ksettings = usbnet_set_link_ksettings, }; static void dm9601_set_multicast(struct net_device *net) -- cgit From 17400f77b87cd2cf29fde358a05d5efc7b0eabab Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 16 Mar 2017 23:18:52 +0100 Subject: net: usb: mcs7830: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Tested-by: poma Signed-off-by: David S. Miller --- drivers/net/usb/mcs7830.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c index 4f345bd4e6e2..5771ff261fa8 100644 --- a/drivers/net/usb/mcs7830.c +++ b/drivers/net/usb/mcs7830.c @@ -464,9 +464,9 @@ static const struct ethtool_ops mcs7830_ethtool_ops = { .get_link = usbnet_get_link, .get_msglevel = usbnet_get_msglevel, .set_msglevel = usbnet_set_msglevel, - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, .nway_reset = usbnet_nway_reset, + .get_link_ksettings = usbnet_get_link_ksettings, + .set_link_ksettings = usbnet_set_link_ksettings, }; static const struct net_device_ops mcs7830_netdev_ops = { -- cgit From 90a8e95e87720eaf202fb40cc420da02d9072b23 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 16 Mar 2017 23:18:53 +0100 Subject: net: usb: sierra_net: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/usb/sierra_net.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c index ac69f28d92d2..c8f60b887c22 100644 --- a/drivers/net/usb/sierra_net.c +++ b/drivers/net/usb/sierra_net.c @@ -648,9 +648,9 @@ static const struct ethtool_ops sierra_net_ethtool_ops = { .get_link = sierra_net_get_link, .get_msglevel = usbnet_get_msglevel, .set_msglevel = usbnet_set_msglevel, - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, .nway_reset = usbnet_nway_reset, + .get_link_ksettings = usbnet_get_link_ksettings, + .set_link_ksettings = usbnet_set_link_ksettings, }; static int sierra_net_get_fw_attr(struct usbnet *dev, u16 *datap) -- cgit From a44017a504b4d9161eeefbae067af3ef0147526d Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 16 Mar 2017 23:18:54 +0100 Subject: net: usb: smsc75xx: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/usb/smsc75xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 0b17b40d7a4f..1ab0ff43c6a2 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -743,13 +743,13 @@ static const struct ethtool_ops smsc75xx_ethtool_ops = { .get_drvinfo = usbnet_get_drvinfo, .get_msglevel = usbnet_get_msglevel, .set_msglevel = usbnet_set_msglevel, - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, .get_eeprom_len = smsc75xx_ethtool_get_eeprom_len, .get_eeprom = smsc75xx_ethtool_get_eeprom, .set_eeprom = smsc75xx_ethtool_set_eeprom, .get_wol = smsc75xx_ethtool_get_wol, .set_wol = smsc75xx_ethtool_set_wol, + .get_link_ksettings = usbnet_get_link_ksettings, + .set_link_ksettings = usbnet_set_link_ksettings, }; static int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) -- cgit From 39f49eadc3e56609dc50c4abfeba0e4d77a590a3 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 16 Mar 2017 23:18:55 +0100 Subject: net: usb: sr9700: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/usb/sr9700.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c index 4a1e9c489f1f..950a3a9466bd 100644 --- a/drivers/net/usb/sr9700.c +++ b/drivers/net/usb/sr9700.c @@ -249,9 +249,9 @@ static const struct ethtool_ops sr9700_ethtool_ops = { .set_msglevel = usbnet_set_msglevel, .get_eeprom_len = sr9700_get_eeprom_len, .get_eeprom = sr9700_get_eeprom, - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, .nway_reset = usbnet_nway_reset, + .get_link_ksettings = usbnet_get_link_ksettings, + .set_link_ksettings = usbnet_set_link_ksettings, }; static void sr9700_set_multicast(struct net_device *netdev) -- cgit From fd4f0a75f5f3c722a84cea999b0e9b00853ef3bd Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 16 Mar 2017 23:18:56 +0100 Subject: net: usb: asix: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/usb/asix_devices.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 0dd510604118..38456d0bcfd2 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -136,9 +136,9 @@ static const struct ethtool_ops ax88172_ethtool_ops = { .get_eeprom_len = asix_get_eeprom_len, .get_eeprom = asix_get_eeprom, .set_eeprom = asix_set_eeprom, - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, .nway_reset = usbnet_nway_reset, + .get_link_ksettings = usbnet_get_link_ksettings, + .set_link_ksettings = usbnet_set_link_ksettings, }; static void ax88172_set_multicast(struct net_device *net) @@ -301,9 +301,9 @@ static const struct ethtool_ops ax88772_ethtool_ops = { .get_eeprom_len = asix_get_eeprom_len, .get_eeprom = asix_get_eeprom, .set_eeprom = asix_set_eeprom, - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, .nway_reset = usbnet_nway_reset, + .get_link_ksettings = usbnet_get_link_ksettings, + .set_link_ksettings = usbnet_set_link_ksettings, }; static int ax88772_link_reset(struct usbnet *dev) @@ -775,9 +775,9 @@ static const struct ethtool_ops ax88178_ethtool_ops = { .get_eeprom_len = asix_get_eeprom_len, .get_eeprom = asix_get_eeprom, .set_eeprom = asix_set_eeprom, - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, .nway_reset = usbnet_nway_reset, + .get_link_ksettings = usbnet_get_link_ksettings, + .set_link_ksettings = usbnet_set_link_ksettings, }; static int marvell_phy_init(struct usbnet *dev) -- cgit From bde87ad64c4cee7a1d5b41d217b440e21050813e Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Thu, 16 Mar 2017 23:18:57 +0100 Subject: net: usb: usb: remove old api ethtool_{get|set}_settings The function usbnet_{get|set}_settings is no longer used, so we remove it. Signed-off-by: Philippe Reynes Acked-by: Oliver Neukum Signed-off-by: David S. Miller --- drivers/net/usb/usbnet.c | 35 ----------------------------------- include/linux/usb/usbnet.h | 4 ---- 2 files changed, 39 deletions(-) diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 1b40b189435a..13d4ec5f6f34 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -947,39 +947,6 @@ EXPORT_SYMBOL_GPL(usbnet_open); * they'll probably want to use this base set. */ -int usbnet_get_settings (struct net_device *net, struct ethtool_cmd *cmd) -{ - struct usbnet *dev = netdev_priv(net); - - if (!dev->mii.mdio_read) - return -EOPNOTSUPP; - - return mii_ethtool_gset(&dev->mii, cmd); -} -EXPORT_SYMBOL_GPL(usbnet_get_settings); - -int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd) -{ - struct usbnet *dev = netdev_priv(net); - int retval; - - if (!dev->mii.mdio_write) - return -EOPNOTSUPP; - - retval = mii_ethtool_sset(&dev->mii, cmd); - - /* link speed/duplex might have changed */ - if (dev->driver_info->link_reset) - dev->driver_info->link_reset(dev); - - /* hard_mtu or rx_urb_size may change in link_reset() */ - usbnet_update_max_qlen(dev); - - return retval; - -} -EXPORT_SYMBOL_GPL(usbnet_set_settings); - int usbnet_get_link_ksettings(struct net_device *net, struct ethtool_link_ksettings *cmd) { @@ -1072,8 +1039,6 @@ EXPORT_SYMBOL_GPL(usbnet_set_msglevel); /* drivers may override default ethtool_ops in their bind() routine */ static const struct ethtool_ops usbnet_ethtool_ops = { - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, .get_link = usbnet_get_link, .nway_reset = usbnet_nway_reset, .get_drvinfo = usbnet_get_drvinfo, diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index 5bd80078b7fe..e2b56917450f 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -261,10 +261,6 @@ extern void usbnet_pause_rx(struct usbnet *); extern void usbnet_resume_rx(struct usbnet *); extern void usbnet_purge_paused_rxq(struct usbnet *); -extern int usbnet_get_settings(struct net_device *net, - struct ethtool_cmd *cmd); -extern int usbnet_set_settings(struct net_device *net, - struct ethtool_cmd *cmd); extern int usbnet_get_link_ksettings(struct net_device *net, struct ethtool_link_ksettings *cmd); extern int usbnet_set_link_ksettings(struct net_device *net, -- cgit From 33928eed8d934bb912d8168cd5e57fa80f9122c4 Mon Sep 17 00:00:00 2001 From: hayeswang Date: Fri, 17 Mar 2017 11:20:13 +0800 Subject: r8152: check hw version first Check hw version first in probe(). Do nothing if the driver doesn't support the chip. Signed-off-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 102 ++++++++++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 39 deletions(-) diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 4b85e95ab754..3262a326aae6 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -4236,44 +4236,6 @@ static const struct net_device_ops rtl8152_netdev_ops = { .ndo_features_check = rtl8152_features_check, }; -static void r8152b_get_version(struct r8152 *tp) -{ - u32 ocp_data; - u16 version; - - ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR1); - version = (u16)(ocp_data & VERSION_MASK); - - switch (version) { - case 0x4c00: - tp->version = RTL_VER_01; - break; - case 0x4c10: - tp->version = RTL_VER_02; - break; - case 0x5c00: - tp->version = RTL_VER_03; - tp->mii.supports_gmii = 1; - break; - case 0x5c10: - tp->version = RTL_VER_04; - tp->mii.supports_gmii = 1; - break; - case 0x5c20: - tp->version = RTL_VER_05; - tp->mii.supports_gmii = 1; - break; - case 0x5c30: - tp->version = RTL_VER_06; - tp->mii.supports_gmii = 1; - break; - default: - netif_info(tp, probe, tp->netdev, - "Unknown version 0x%04x\n", version); - break; - } -} - static void rtl8152_unload(struct r8152 *tp) { if (test_bit(RTL8152_UNPLUG, &tp->flags)) @@ -4338,14 +4300,66 @@ static int rtl_ops_init(struct r8152 *tp) return ret; } +static u8 rtl_get_version(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + u32 ocp_data = 0; + __le32 *tmp; + u8 version; + int ret; + + tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) + return 0; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, + PLA_TCR0, MCU_TYPE_PLA, tmp, sizeof(*tmp), 500); + if (ret > 0) + ocp_data = (__le32_to_cpu(*tmp) >> 16) & VERSION_MASK; + + kfree(tmp); + + switch (ocp_data) { + case 0x4c00: + version = RTL_VER_01; + break; + case 0x4c10: + version = RTL_VER_02; + break; + case 0x5c00: + version = RTL_VER_03; + break; + case 0x5c10: + version = RTL_VER_04; + break; + case 0x5c20: + version = RTL_VER_05; + break; + case 0x5c30: + version = RTL_VER_06; + break; + default: + version = RTL_VER_UNKNOWN; + dev_info(&intf->dev, "Unknown version 0x%04x\n", ocp_data); + break; + } + + return version; +} + static int rtl8152_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); + u8 version = rtl_get_version(intf); struct r8152 *tp; struct net_device *netdev; int ret; + if (version == RTL_VER_UNKNOWN) + return -ENODEV; + if (udev->actconfig->desc.bConfigurationValue != 1) { usb_driver_set_configuration(udev, 1); return -ENODEV; @@ -4365,8 +4379,18 @@ static int rtl8152_probe(struct usb_interface *intf, tp->udev = udev; tp->netdev = netdev; tp->intf = intf; + tp->version = version; + + switch (version) { + case RTL_VER_01: + case RTL_VER_02: + tp->mii.supports_gmii = 0; + break; + default: + tp->mii.supports_gmii = 1; + break; + } - r8152b_get_version(tp); ret = rtl_ops_init(tp); if (ret) goto out; -- cgit From bf95233e206983d5a05a2e27700d2678ffdef62c Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Fri, 17 Mar 2017 09:38:00 +0100 Subject: mlxsw: spectrum: Cosmetic naming change Currently the struct representing router interface "mlxsw_sp_rif" is reffered as "r" in various places in the driver. Furthermore it contains a member which specify the index which is called "rif". This patch change "r" to "rif" and "rif" to "rif_index". Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 8 +- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 4 +- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 282 +++++++++++---------- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 4 +- 4 files changed, 152 insertions(+), 146 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 28019f8b70f7..cbc1646ee1d9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4088,8 +4088,8 @@ static void mlxsw_sp_master_bridge_vlan_unlink(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *f; f = mlxsw_sp_fid_find(mlxsw_sp, fid); - if (f && f->r) - mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); + if (f && f->rif) + mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->rif); if (f && --f->ref_count == 0) mlxsw_sp_fid_destroy(mlxsw_sp, f); } @@ -4206,8 +4206,8 @@ static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp, clear_bit(vfid, mlxsw_sp->vfids.mapped); list_del(&f->list); - if (f->r) - mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); + if (f->rif) + mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->rif); kfree(f); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 0e223f6983c5..bffd9e698eff 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -103,7 +103,7 @@ struct mlxsw_sp_fid { struct list_head list; unsigned int ref_count; struct net_device *dev; - struct mlxsw_sp_rif *r; + struct mlxsw_sp_rif *rif; u16 fid; }; @@ -577,7 +577,7 @@ int mlxsw_sp_netdevice_router_port_event(struct net_device *dev); int mlxsw_sp_inetaddr_event(struct notifier_block *unused, unsigned long event, void *ptr); void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *r); + struct mlxsw_sp_rif *rif); int mlxsw_sp_vport_vrf_join(struct mlxsw_sp_port *mlxsw_sp_vport); void mlxsw_sp_vport_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_vport); int mlxsw_sp_port_vrf_join(struct mlxsw_sp_port *mlxsw_sp_port); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 488bc1fd7868..fe4a55e3272b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -60,7 +60,7 @@ struct mlxsw_sp_rif { struct mlxsw_sp_fid *f; unsigned char addr[ETH_ALEN]; int mtu; - u16 rif; + u16 rif_index; u16 vr_id; }; @@ -600,14 +600,14 @@ static struct mlxsw_sp_neigh_entry * mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n) { struct mlxsw_sp_neigh_entry *neigh_entry; - struct mlxsw_sp_rif *r; + struct mlxsw_sp_rif *rif; int err; - r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev); - if (!r) + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, n->dev); + if (!rif) return ERR_PTR(-EINVAL); - neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, r->rif); + neigh_entry = mlxsw_sp_neigh_entry_alloc(mlxsw_sp, n, rif->rif_index); if (!neigh_entry) return ERR_PTR(-ENOMEM); @@ -615,7 +615,7 @@ mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n) if (err) goto err_neigh_entry_insert; - list_add(&neigh_entry->rif_list_node, &r->neigh_list); + list_add(&neigh_entry->rif_list_node, &rif->neigh_list); return neigh_entry; @@ -1023,22 +1023,22 @@ static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp) } static int mlxsw_sp_neigh_rif_flush(struct mlxsw_sp *mlxsw_sp, - const struct mlxsw_sp_rif *r) + const struct mlxsw_sp_rif *rif) { char rauht_pl[MLXSW_REG_RAUHT_LEN]; mlxsw_reg_rauht_pack(rauht_pl, MLXSW_REG_RAUHT_OP_WRITE_DELETE_ALL, - r->rif, r->addr); + rif->rif_index, rif->addr); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl); } static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *r) + struct mlxsw_sp_rif *rif) { struct mlxsw_sp_neigh_entry *neigh_entry, *tmp; - mlxsw_sp_neigh_rif_flush(mlxsw_sp, r); - list_for_each_entry_safe(neigh_entry, tmp, &r->neigh_list, + mlxsw_sp_neigh_rif_flush(mlxsw_sp, rif); + list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list, rif_list_node) mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry); } @@ -1055,7 +1055,7 @@ struct mlxsw_sp_nexthop { */ struct rhash_head ht_node; struct mlxsw_sp_nexthop_key key; - struct mlxsw_sp_rif *r; + struct mlxsw_sp_rif *rif; u8 should_offload:1, /* set indicates this neigh is connected and * should be put to KVD linear area of this group. */ @@ -1082,7 +1082,7 @@ struct mlxsw_sp_nexthop_group { u16 ecmp_size; u16 count; struct mlxsw_sp_nexthop nexthops[0]; -#define nh_rif nexthops[0].r +#define nh_rif nexthops[0].rif }; static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = { @@ -1372,22 +1372,22 @@ mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp *mlxsw_sp, } static void mlxsw_sp_nexthop_rif_init(struct mlxsw_sp_nexthop *nh, - struct mlxsw_sp_rif *r) + struct mlxsw_sp_rif *rif) { - if (nh->r) + if (nh->rif) return; - nh->r = r; - list_add(&nh->rif_list_node, &r->nexthop_list); + nh->rif = rif; + list_add(&nh->rif_list_node, &rif->nexthop_list); } static void mlxsw_sp_nexthop_rif_fini(struct mlxsw_sp_nexthop *nh) { - if (!nh->r) + if (!nh->rif) return; list_del(&nh->rif_list_node); - nh->r = NULL; + nh->rif = NULL; } static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp, @@ -1478,7 +1478,7 @@ static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp, { struct net_device *dev = fib_nh->nh_dev; struct in_device *in_dev; - struct mlxsw_sp_rif *r; + struct mlxsw_sp_rif *rif; int err; nh->nh_grp = nh_grp; @@ -1495,10 +1495,10 @@ static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp, fib_nh->nh_flags & RTNH_F_LINKDOWN) return 0; - r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); - if (!r) + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); + if (!rif) return 0; - mlxsw_sp_nexthop_rif_init(nh, r); + mlxsw_sp_nexthop_rif_init(nh, rif); err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh); if (err) @@ -1524,7 +1524,7 @@ static void mlxsw_sp_nexthop_event(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_nexthop_key key; struct mlxsw_sp_nexthop *nh; - struct mlxsw_sp_rif *r; + struct mlxsw_sp_rif *rif; if (mlxsw_sp->router.aborted) return; @@ -1534,13 +1534,13 @@ static void mlxsw_sp_nexthop_event(struct mlxsw_sp *mlxsw_sp, if (WARN_ON_ONCE(!nh)) return; - r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev); - if (!r) + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev); + if (!rif) return; switch (event) { case FIB_EVENT_NH_ADD: - mlxsw_sp_nexthop_rif_init(nh, r); + mlxsw_sp_nexthop_rif_init(nh, rif); mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh); break; case FIB_EVENT_NH_DEL: @@ -1553,11 +1553,11 @@ static void mlxsw_sp_nexthop_event(struct mlxsw_sp *mlxsw_sp, } static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *r) + struct mlxsw_sp_rif *rif) { struct mlxsw_sp_nexthop *nh, *tmp; - list_for_each_entry_safe(nh, tmp, &r->nexthop_list, rif_list_node) { + list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) { mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); mlxsw_sp_nexthop_rif_fini(nh); mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp); @@ -1760,17 +1760,17 @@ static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry *fib_entry, enum mlxsw_reg_ralue_op op) { - struct mlxsw_sp_rif *r = fib_entry->nh_group->nh_rif; + struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif; struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib; enum mlxsw_reg_ralue_trap_action trap_action; char ralue_pl[MLXSW_REG_RALUE_LEN]; u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr; u16 trap_id = 0; - u16 rif = 0; + u16 rif_index = 0; if (mlxsw_sp_fib_entry_should_offload(fib_entry)) { trap_action = MLXSW_REG_RALUE_TRAP_ACTION_NOP; - rif = r->rif; + rif_index = rif->rif_index; } else { trap_action = MLXSW_REG_RALUE_TRAP_ACTION_TRAP; trap_id = MLXSW_TRAP_ID_RTR_INGRESS0; @@ -1780,7 +1780,8 @@ static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp, (enum mlxsw_reg_ralxx_protocol) fib->proto, op, fib->vr->id, fib_entry->fib_node->key.prefix_len, *p_dip); - mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id, rif); + mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id, + rif_index); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); } @@ -2644,24 +2645,25 @@ static int mlxsw_sp_router_rif_disable(struct mlxsw_sp *mlxsw_sp, u16 rif) } static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *r) + struct mlxsw_sp_rif *rif) { - mlxsw_sp_router_rif_disable(mlxsw_sp, r->rif); - mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, r); - mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, r); + mlxsw_sp_router_rif_disable(mlxsw_sp, rif->rif_index); + mlxsw_sp_nexthop_rif_gone_sync(mlxsw_sp, rif); + mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif); } -static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *r, +static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, const struct in_device *in_dev, unsigned long event) { switch (event) { case NETDEV_UP: - if (!r) + if (!rif) return true; return false; case NETDEV_DOWN: - if (r && !in_dev->ifa_list && !netif_is_l3_slave(r->dev)) + if (rif && !in_dev->ifa_list && + !netif_is_l3_slave(rif->dev)) return true; /* It is possible we already removed the RIF ourselves * if it was assigned to a netdev that is now a bridge @@ -2673,7 +2675,7 @@ static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *r, return false; } -#define MLXSW_SP_INVALID_RIF 0xffff +#define MLXSW_SP_INVALID_INDEX_RIF 0xffff static int mlxsw_sp_avail_rif_get(struct mlxsw_sp *mlxsw_sp) { int i; @@ -2682,7 +2684,7 @@ static int mlxsw_sp_avail_rif_get(struct mlxsw_sp *mlxsw_sp) if (!mlxsw_sp->rifs[i]) return i; - return MLXSW_SP_INVALID_RIF; + return MLXSW_SP_INVALID_INDEX_RIF; } static void mlxsw_sp_vport_rif_sp_attr_get(struct mlxsw_sp_port *mlxsw_sp_vport, @@ -2696,15 +2698,15 @@ static void mlxsw_sp_vport_rif_sp_attr_get(struct mlxsw_sp_port *mlxsw_sp_vport, static int mlxsw_sp_vport_rif_sp_op(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vr_id, struct net_device *l3_dev, - u16 rif, bool create) + u16 rif_index, bool create) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; bool lagged = mlxsw_sp_vport->lagged; char ritr_pl[MLXSW_REG_RITR_LEN]; u16 system_port; - mlxsw_reg_ritr_pack(ritr_pl, create, MLXSW_REG_RITR_SP_IF, rif, vr_id, - l3_dev->mtu, l3_dev->dev_addr); + mlxsw_reg_ritr_pack(ritr_pl, create, MLXSW_REG_RITR_SP_IF, rif_index, + vr_id, l3_dev->mtu, l3_dev->dev_addr); mlxsw_sp_vport_rif_sp_attr_get(mlxsw_sp_vport, &lagged, &system_port); mlxsw_reg_ritr_sp_if_pack(ritr_pl, lagged, system_port, @@ -2715,9 +2717,9 @@ static int mlxsw_sp_vport_rif_sp_op(struct mlxsw_sp_port *mlxsw_sp_vport, static void mlxsw_sp_vport_rif_sp_leave(struct mlxsw_sp_port *mlxsw_sp_vport); -static u16 mlxsw_sp_rif_sp_to_fid(u16 rif) +static u16 mlxsw_sp_rif_sp_to_fid(u16 rif_index) { - return MLXSW_SP_RFID_BASE + rif; + return MLXSW_SP_RFID_BASE + rif_index; } static struct mlxsw_sp_fid * @@ -2738,25 +2740,25 @@ mlxsw_sp_rfid_alloc(u16 fid, struct net_device *l3_dev) } static struct mlxsw_sp_rif * -mlxsw_sp_rif_alloc(u16 rif, u16 vr_id, struct net_device *l3_dev, +mlxsw_sp_rif_alloc(u16 rif_index, u16 vr_id, struct net_device *l3_dev, struct mlxsw_sp_fid *f) { - struct mlxsw_sp_rif *r; + struct mlxsw_sp_rif *rif; - r = kzalloc(sizeof(*r), GFP_KERNEL); - if (!r) + rif = kzalloc(sizeof(*rif), GFP_KERNEL); + if (!rif) return NULL; - INIT_LIST_HEAD(&r->nexthop_list); - INIT_LIST_HEAD(&r->neigh_list); - ether_addr_copy(r->addr, l3_dev->dev_addr); - r->mtu = l3_dev->mtu; - r->vr_id = vr_id; - r->dev = l3_dev; - r->rif = rif; - r->f = f; + INIT_LIST_HEAD(&rif->nexthop_list); + INIT_LIST_HEAD(&rif->neigh_list); + ether_addr_copy(rif->addr, l3_dev->dev_addr); + rif->mtu = l3_dev->mtu; + rif->vr_id = vr_id; + rif->dev = l3_dev; + rif->rif_index = rif_index; + rif->f = f; - return r; + return rif; } static struct mlxsw_sp_rif * @@ -2767,24 +2769,24 @@ mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport, u32 tb_id = l3mdev_fib_table(l3_dev); struct mlxsw_sp_vr *vr; struct mlxsw_sp_fid *f; - struct mlxsw_sp_rif *r; - u16 fid, rif; + struct mlxsw_sp_rif *rif; + u16 fid, rif_index; int err; - rif = mlxsw_sp_avail_rif_get(mlxsw_sp); - if (rif == MLXSW_SP_INVALID_RIF) + rif_index = mlxsw_sp_avail_rif_get(mlxsw_sp); + if (rif_index == MLXSW_SP_INVALID_INDEX_RIF) return ERR_PTR(-ERANGE); vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN); if (IS_ERR(vr)) return ERR_CAST(vr); - err = mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, vr->id, l3_dev, rif, - true); + err = mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, vr->id, l3_dev, + rif_index, true); if (err) goto err_vport_rif_sp_op; - fid = mlxsw_sp_rif_sp_to_fid(rif); + fid = mlxsw_sp_rif_sp_to_fid(rif_index); err = mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, true); if (err) goto err_rif_fdb_op; @@ -2795,53 +2797,54 @@ mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport, goto err_rfid_alloc; } - r = mlxsw_sp_rif_alloc(rif, vr->id, l3_dev, f); - if (!r) { + rif = mlxsw_sp_rif_alloc(rif_index, vr->id, l3_dev, f); + if (!rif) { err = -ENOMEM; goto err_rif_alloc; } - f->r = r; - mlxsw_sp->rifs[rif] = r; + f->rif = rif; + mlxsw_sp->rifs[rif_index] = rif; vr->rif_count++; - return r; + return rif; err_rif_alloc: kfree(f); err_rfid_alloc: mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false); err_rif_fdb_op: - mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, vr->id, l3_dev, rif, false); + mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, vr->id, l3_dev, rif_index, + false); err_vport_rif_sp_op: mlxsw_sp_vr_put(vr); return ERR_PTR(err); } static void mlxsw_sp_vport_rif_sp_destroy(struct mlxsw_sp_port *mlxsw_sp_vport, - struct mlxsw_sp_rif *r) + struct mlxsw_sp_rif *rif) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; - struct mlxsw_sp_vr *vr = &mlxsw_sp->router.vrs[r->vr_id]; - struct net_device *l3_dev = r->dev; - struct mlxsw_sp_fid *f = r->f; + struct mlxsw_sp_vr *vr = &mlxsw_sp->router.vrs[rif->vr_id]; + struct net_device *l3_dev = rif->dev; + struct mlxsw_sp_fid *f = rif->f; + u16 rif_index = rif->rif_index; u16 fid = f->fid; - u16 rif = r->rif; - mlxsw_sp_router_rif_gone_sync(mlxsw_sp, r); + mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif); vr->rif_count--; - mlxsw_sp->rifs[rif] = NULL; - f->r = NULL; + mlxsw_sp->rifs[rif_index] = NULL; + f->rif = NULL; - kfree(r); + kfree(rif); kfree(f); mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, fid, false); - mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, vr->id, l3_dev, rif, false); - + mlxsw_sp_vport_rif_sp_op(mlxsw_sp_vport, vr->id, l3_dev, rif_index, + false); mlxsw_sp_vr_put(vr); } @@ -2849,19 +2852,19 @@ static int mlxsw_sp_vport_rif_sp_join(struct mlxsw_sp_port *mlxsw_sp_vport, struct net_device *l3_dev) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_vport->mlxsw_sp; - struct mlxsw_sp_rif *r; + struct mlxsw_sp_rif *rif; - r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev); - if (!r) { - r = mlxsw_sp_vport_rif_sp_create(mlxsw_sp_vport, l3_dev); - if (IS_ERR(r)) - return PTR_ERR(r); + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev); + if (!rif) { + rif = mlxsw_sp_vport_rif_sp_create(mlxsw_sp_vport, l3_dev); + if (IS_ERR(rif)) + return PTR_ERR(rif); } - mlxsw_sp_vport_fid_set(mlxsw_sp_vport, r->f); - r->f->ref_count++; + mlxsw_sp_vport_fid_set(mlxsw_sp_vport, rif->f); + rif->f->ref_count++; - netdev_dbg(mlxsw_sp_vport->dev, "Joined FID=%d\n", r->f->fid); + netdev_dbg(mlxsw_sp_vport->dev, "Joined FID=%d\n", rif->f->fid); return 0; } @@ -2874,7 +2877,7 @@ static void mlxsw_sp_vport_rif_sp_leave(struct mlxsw_sp_port *mlxsw_sp_vport) mlxsw_sp_vport_fid_set(mlxsw_sp_vport, NULL); if (--f->ref_count == 0) - mlxsw_sp_vport_rif_sp_destroy(mlxsw_sp_vport, f->r); + mlxsw_sp_vport_rif_sp_destroy(mlxsw_sp_vport, f->rif); } static int mlxsw_sp_inetaddr_vport_event(struct net_device *l3_dev, @@ -3014,13 +3017,13 @@ static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *f) { u32 tb_id = l3mdev_fib_table(l3_dev); + struct mlxsw_sp_rif *rif; struct mlxsw_sp_vr *vr; - struct mlxsw_sp_rif *r; - u16 rif; + u16 rif_index; int err; - rif = mlxsw_sp_avail_rif_get(mlxsw_sp); - if (rif == MLXSW_SP_INVALID_RIF) + rif_index = mlxsw_sp_avail_rif_get(mlxsw_sp); + if (rif_index == MLXSW_SP_INVALID_INDEX_RIF) return -ERANGE; vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id ? : RT_TABLE_MAIN); @@ -3031,8 +3034,8 @@ static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp, if (err) goto err_port_flood_set; - err = mlxsw_sp_rif_bridge_op(mlxsw_sp, vr->id, l3_dev, f->fid, rif, - true); + err = mlxsw_sp_rif_bridge_op(mlxsw_sp, vr->id, l3_dev, f->fid, + rif_index, true); if (err) goto err_rif_bridge_op; @@ -3040,24 +3043,25 @@ static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp, if (err) goto err_rif_fdb_op; - r = mlxsw_sp_rif_alloc(rif, vr->id, l3_dev, f); - if (!r) { + rif = mlxsw_sp_rif_alloc(rif_index, vr->id, l3_dev, f); + if (!rif) { err = -ENOMEM; goto err_rif_alloc; } - f->r = r; - mlxsw_sp->rifs[rif] = r; + f->rif = rif; + mlxsw_sp->rifs[rif_index] = rif; vr->rif_count++; - netdev_dbg(l3_dev, "RIF=%d created\n", rif); + netdev_dbg(l3_dev, "RIF=%d created\n", rif_index); return 0; err_rif_alloc: mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false); err_rif_fdb_op: - mlxsw_sp_rif_bridge_op(mlxsw_sp, vr->id, l3_dev, f->fid, rif, false); + mlxsw_sp_rif_bridge_op(mlxsw_sp, vr->id, l3_dev, f->fid, rif_index, + false); err_rif_bridge_op: mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, false); err_port_flood_set: @@ -3066,30 +3070,31 @@ err_port_flood_set: } void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_rif *r) + struct mlxsw_sp_rif *rif) { - struct mlxsw_sp_vr *vr = &mlxsw_sp->router.vrs[r->vr_id]; - struct net_device *l3_dev = r->dev; - struct mlxsw_sp_fid *f = r->f; - u16 rif = r->rif; + struct mlxsw_sp_vr *vr = &mlxsw_sp->router.vrs[rif->vr_id]; + struct net_device *l3_dev = rif->dev; + struct mlxsw_sp_fid *f = rif->f; + u16 rif_index = rif->rif_index; - mlxsw_sp_router_rif_gone_sync(mlxsw_sp, r); + mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif); vr->rif_count--; - mlxsw_sp->rifs[rif] = NULL; - f->r = NULL; + mlxsw_sp->rifs[rif_index] = NULL; + f->rif = NULL; - kfree(r); + kfree(rif); mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false); - mlxsw_sp_rif_bridge_op(mlxsw_sp, vr->id, l3_dev, f->fid, rif, false); + mlxsw_sp_rif_bridge_op(mlxsw_sp, vr->id, l3_dev, f->fid, rif_index, + false); mlxsw_sp_router_port_flood_set(mlxsw_sp, f->fid, false); mlxsw_sp_vr_put(vr); - netdev_dbg(l3_dev, "RIF=%d destroyed\n", rif); + netdev_dbg(l3_dev, "RIF=%d destroyed\n", rif_index); } static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev, @@ -3111,7 +3116,7 @@ static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev, case NETDEV_UP: return mlxsw_sp_rif_bridge_create(mlxsw_sp, l3_dev, f); case NETDEV_DOWN: - mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); + mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->rif); break; } @@ -3145,15 +3150,15 @@ int mlxsw_sp_inetaddr_event(struct notifier_block *unused, struct in_ifaddr *ifa = (struct in_ifaddr *) ptr; struct net_device *dev = ifa->ifa_dev->dev; struct mlxsw_sp *mlxsw_sp; - struct mlxsw_sp_rif *r; + struct mlxsw_sp_rif *rif; int err = 0; mlxsw_sp = mlxsw_sp_lower_get(dev); if (!mlxsw_sp) goto out; - r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); - if (!mlxsw_sp_rif_should_config(r, ifa->ifa_dev, event)) + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); + if (!mlxsw_sp_rif_should_config(rif, ifa->ifa_dev, event)) goto out; if (mlxsw_sp_port_dev_check(dev)) @@ -3169,13 +3174,13 @@ out: return notifier_from_errno(err); } -static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif, +static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index, const char *mac, int mtu) { char ritr_pl[MLXSW_REG_RITR_LEN]; int err; - mlxsw_reg_ritr_rif_pack(ritr_pl, rif); + mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index); err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); if (err) return err; @@ -3189,40 +3194,41 @@ static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif, int mlxsw_sp_netdevice_router_port_event(struct net_device *dev) { struct mlxsw_sp *mlxsw_sp; - struct mlxsw_sp_rif *r; + struct mlxsw_sp_rif *rif; int err; mlxsw_sp = mlxsw_sp_lower_get(dev); if (!mlxsw_sp) return 0; - r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); - if (!r) + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); + if (!rif) return 0; - err = mlxsw_sp_rif_fdb_op(mlxsw_sp, r->addr, r->f->fid, false); + err = mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, rif->f->fid, false); if (err) return err; - err = mlxsw_sp_rif_edit(mlxsw_sp, r->rif, dev->dev_addr, dev->mtu); + err = mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, dev->dev_addr, + dev->mtu); if (err) goto err_rif_edit; - err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, r->f->fid, true); + err = mlxsw_sp_rif_fdb_op(mlxsw_sp, dev->dev_addr, rif->f->fid, true); if (err) goto err_rif_fdb_op; - ether_addr_copy(r->addr, dev->dev_addr); - r->mtu = dev->mtu; + ether_addr_copy(rif->addr, dev->dev_addr); + rif->mtu = dev->mtu; - netdev_dbg(dev, "Updated RIF=%d\n", r->rif); + netdev_dbg(dev, "Updated RIF=%d\n", rif->rif_index); return 0; err_rif_fdb_op: - mlxsw_sp_rif_edit(mlxsw_sp, r->rif, r->addr, r->mtu); + mlxsw_sp_rif_edit(mlxsw_sp, rif->rif_index, rif->addr, rif->mtu); err_rif_edit: - mlxsw_sp_rif_fdb_op(mlxsw_sp, r->addr, r->f->fid, true); + mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->addr, rif->f->fid, true); return err; } @@ -3234,7 +3240,7 @@ int mlxsw_sp_vport_vrf_join(struct mlxsw_sp_port *mlxsw_sp_vport) /* In case vPort already has a RIF, then we need to drop it. * A new one will be created using the VRF's VR. */ - if (f && f->r) + if (f && f->rif) mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport); return mlxsw_sp_vport_rif_sp_join(mlxsw_sp_vport, dev); @@ -3276,8 +3282,8 @@ int mlxsw_sp_bridge_vrf_join(struct mlxsw_sp *mlxsw_sp, if (WARN_ON(!f)) return -EINVAL; - if (f->r) - mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); + if (f->rif) + mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->rif); return mlxsw_sp_rif_bridge_create(mlxsw_sp, l3_dev, f); } @@ -3290,7 +3296,7 @@ void mlxsw_sp_bridge_vrf_leave(struct mlxsw_sp *mlxsw_sp, f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev); if (WARN_ON(!f)) return; - mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); + mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->rif); } static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 598727d578c1..d44d92fe7ff3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -568,8 +568,8 @@ void mlxsw_sp_fid_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *f) list_del(&f->list); - if (f->r) - mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r); + if (f->rif) + mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->rif); kfree(f); -- cgit From abbdf4bd7dd0d814f1af2bd90112e74a2be2c573 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Fri, 17 Mar 2017 09:38:01 +0100 Subject: mlxsw: spectrum: Align the matchall default case returned value Align the default case for matchall offload with what's there for flower. Signed-off-by: Or Gerlitz Acked-by: Yotam Gigi Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index cbc1646ee1d9..3ed77e10b4d6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1423,7 +1423,7 @@ static int mlxsw_sp_setup_tc(struct net_device *dev, u32 handle, tc->cls_mall); return 0; default: - return -EINVAL; + return -EOPNOTSUPP; } case TC_SETUP_CLSFLOWER: switch (tc->cls_flower->command) { -- cgit From de6b08fd82087778c579dc3f3c28bb1c878a993a Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 17 Mar 2017 11:52:15 +0100 Subject: net: ethoc: Use ether_addr_copy() Use ether_addr_copy() instead of memcpy() to set netdev->dev_addr (which is 2-byte aligned). Signed-off-by: Tobias Klauser Reviewed-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/ethoc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index 23d82748f52b..e863ba74d005 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -1148,14 +1148,14 @@ static int ethoc_probe(struct platform_device *pdev) /* Allow the platform setup code to pass in a MAC address. */ if (pdata) { - memcpy(netdev->dev_addr, pdata->hwaddr, IFHWADDRLEN); + ether_addr_copy(netdev->dev_addr, pdata->hwaddr); priv->phy_id = pdata->phy_id; } else { const void *mac; mac = of_get_mac_address(pdev->dev.of_node); if (mac) - memcpy(netdev->dev_addr, mac, IFHWADDRLEN); + ether_addr_copy(netdev->dev_addr, mac); priv->phy_id = -1; } -- cgit From aff3d9eff84399e433c4aca65a9bb236581bc082 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Fri, 17 Mar 2017 16:11:05 +0000 Subject: net: stmmac: enable multiple buffers This patch creates 2 new structures (stmmac_tx_queue and stmmac_rx_queue) in include/linux/stmmac.h, enabling that each RX and TX queue has its own buffers and data. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/chain_mode.c | 45 +- drivers/net/ethernet/stmicro/stmmac/ring_mode.c | 46 +- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 49 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 1306 ++++++++++++++------- 4 files changed, 973 insertions(+), 473 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c index 01a8c020d6db..37881f81319e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c @@ -26,12 +26,15 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) { - struct stmmac_priv *priv = (struct stmmac_priv *)p; - unsigned int entry = priv->cur_tx; - struct dma_desc *desc = priv->dma_tx + entry; + struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)p; unsigned int nopaged_len = skb_headlen(skb); + struct stmmac_priv *priv = tx_q->priv_data; + unsigned int entry = tx_q->cur_tx; unsigned int bmax, des2; unsigned int i = 1, len; + struct dma_desc *desc; + + desc = tx_q->dma_tx + entry; if (priv->plat->enh_desc) bmax = BUF_SIZE_8KiB; @@ -45,16 +48,16 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = des2; - priv->tx_skbuff_dma[entry].len = bmax; + tx_q->tx_skbuff_dma[entry].buf = des2; + tx_q->tx_skbuff_dma[entry].len = bmax; /* do not close the descriptor and do not set own bit */ priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE, 0, false); while (len != 0) { - priv->tx_skbuff[entry] = NULL; + tx_q->tx_skbuff[entry] = NULL; entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); - desc = priv->dma_tx + entry; + desc = tx_q->dma_tx + entry; if (len > bmax) { des2 = dma_map_single(priv->device, @@ -63,8 +66,8 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = des2; - priv->tx_skbuff_dma[entry].len = bmax; + tx_q->tx_skbuff_dma[entry].buf = des2; + tx_q->tx_skbuff_dma[entry].len = bmax; priv->hw->desc->prepare_tx_desc(desc, 0, bmax, csum, STMMAC_CHAIN_MODE, 1, false); @@ -77,8 +80,8 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = des2; - priv->tx_skbuff_dma[entry].len = len; + tx_q->tx_skbuff_dma[entry].buf = des2; + tx_q->tx_skbuff_dma[entry].len = len; /* last descriptor can be set now */ priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, STMMAC_CHAIN_MODE, 1, @@ -87,7 +90,7 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) } } - priv->cur_tx = entry; + tx_q->cur_tx = entry; return entry; } @@ -136,32 +139,34 @@ static void stmmac_init_dma_chain(void *des, dma_addr_t phy_addr, static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p) { - struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; + struct stmmac_rx_queue *rx_q = (struct stmmac_rx_queue *)priv_ptr; + struct stmmac_priv *priv = rx_q->priv_data; if (priv->hwts_rx_en && !priv->extend_desc) /* NOTE: Device will overwrite des3 with timestamp value if * 1588-2002 time stamping is enabled, hence reinitialize it * to keep explicit chaining in the descriptor. */ - p->des3 = cpu_to_le32((unsigned int)(priv->dma_rx_phy + - (((priv->dirty_rx) + 1) % + p->des3 = cpu_to_le32((unsigned int)(rx_q->dma_rx_phy + + (((rx_q->dirty_rx) + 1) % DMA_RX_SIZE) * sizeof(struct dma_desc))); } static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p) { - struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; - unsigned int entry = priv->dirty_tx; + struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)priv_ptr; + struct stmmac_priv *priv = tx_q->priv_data; + unsigned int entry = tx_q->dirty_tx; - if (priv->tx_skbuff_dma[entry].last_segment && !priv->extend_desc && + if (tx_q->tx_skbuff_dma[entry].last_segment && !priv->extend_desc && priv->hwts_tx_en) /* NOTE: Device will overwrite des3 with timestamp value if * 1588-2002 time stamping is enabled, hence reinitialize it * to keep explicit chaining in the descriptor. */ - p->des3 = cpu_to_le32((unsigned int)((priv->dma_tx_phy + - ((priv->dirty_tx + 1) % DMA_TX_SIZE)) + p->des3 = cpu_to_le32((unsigned int)((tx_q->dma_tx_phy + + ((tx_q->dirty_tx + 1) % DMA_TX_SIZE)) * sizeof(struct dma_desc))); } diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c index 452f256ff03f..31213e64513d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c @@ -26,16 +26,17 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) { - struct stmmac_priv *priv = (struct stmmac_priv *)p; - unsigned int entry = priv->cur_tx; - struct dma_desc *desc; + struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)p; unsigned int nopaged_len = skb_headlen(skb); + struct stmmac_priv *priv = tx_q->priv_data; + unsigned int entry = tx_q->cur_tx; unsigned int bmax, len, des2; + struct dma_desc *desc; if (priv->extend_desc) - desc = (struct dma_desc *)(priv->dma_etx + entry); + desc = (struct dma_desc *)(tx_q->dma_etx + entry); else - desc = priv->dma_tx + entry; + desc = tx_q->dma_tx + entry; if (priv->plat->enh_desc) bmax = BUF_SIZE_8KiB; @@ -52,29 +53,29 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = des2; - priv->tx_skbuff_dma[entry].len = bmax; - priv->tx_skbuff_dma[entry].is_jumbo = true; + tx_q->tx_skbuff_dma[entry].buf = des2; + tx_q->tx_skbuff_dma[entry].len = bmax; + tx_q->tx_skbuff_dma[entry].is_jumbo = true; desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_RING_MODE, 0, false); - priv->tx_skbuff[entry] = NULL; + tx_q->tx_skbuff[entry] = NULL; entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); if (priv->extend_desc) - desc = (struct dma_desc *)(priv->dma_etx + entry); + desc = (struct dma_desc *)(tx_q->dma_etx + entry); else - desc = priv->dma_tx + entry; + desc = tx_q->dma_tx + entry; des2 = dma_map_single(priv->device, skb->data + bmax, len, DMA_TO_DEVICE); desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = des2; - priv->tx_skbuff_dma[entry].len = len; - priv->tx_skbuff_dma[entry].is_jumbo = true; + tx_q->tx_skbuff_dma[entry].buf = des2; + tx_q->tx_skbuff_dma[entry].len = len; + tx_q->tx_skbuff_dma[entry].is_jumbo = true; desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, @@ -85,15 +86,15 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = des2; - priv->tx_skbuff_dma[entry].len = nopaged_len; - priv->tx_skbuff_dma[entry].is_jumbo = true; + tx_q->tx_skbuff_dma[entry].buf = des2; + tx_q->tx_skbuff_dma[entry].len = nopaged_len; + tx_q->tx_skbuff_dma[entry].is_jumbo = true; desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum, STMMAC_RING_MODE, 0, true); } - priv->cur_tx = entry; + tx_q->cur_tx = entry; return entry; } @@ -125,12 +126,13 @@ static void stmmac_init_desc3(struct dma_desc *p) static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p) { - struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; - unsigned int entry = priv->dirty_tx; + struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)priv_ptr; + struct stmmac_priv *priv = tx_q->priv_data; + unsigned int entry = tx_q->dirty_tx; /* des3 is only used for jumbo frames tx or time stamping */ - if (unlikely(priv->tx_skbuff_dma[entry].is_jumbo || - (priv->tx_skbuff_dma[entry].last_segment && + if (unlikely(tx_q->tx_skbuff_dma[entry].is_jumbo || + (tx_q->tx_skbuff_dma[entry].last_segment && !priv->extend_desc && priv->hwts_tx_en))) p->des3 = 0; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index cd8fb619b1e9..6ec671c9be84 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -46,6 +46,35 @@ struct stmmac_tx_info { bool is_jumbo; }; +/* Frequently used values are kept adjacent for cache effect */ +struct stmmac_tx_queue { + u32 queue_index; + struct stmmac_priv *priv_data; + struct dma_extended_desc *dma_etx ____cacheline_aligned_in_smp; + struct dma_desc *dma_tx; + struct sk_buff **tx_skbuff; + struct stmmac_tx_info *tx_skbuff_dma; + unsigned int cur_tx; + unsigned int dirty_tx; + dma_addr_t dma_tx_phy; + u32 tx_tail_addr; +}; + +struct stmmac_rx_queue { + u32 queue_index; + struct stmmac_priv *priv_data; + struct dma_extended_desc *dma_erx; + struct dma_desc *dma_rx ____cacheline_aligned_in_smp; + struct sk_buff **rx_skbuff; + dma_addr_t *rx_skbuff_dma; + struct napi_struct napi ____cacheline_aligned_in_smp; + unsigned int cur_rx; + unsigned int dirty_rx; + u32 rx_zeroc_thresh; + dma_addr_t dma_rx_phy; + u32 rx_tail_addr; +}; + struct stmmac_priv { /* Frequently used values are kept adjacent for cache effect */ struct dma_extended_desc *dma_etx ____cacheline_aligned_in_smp; @@ -56,28 +85,22 @@ struct stmmac_priv { u32 tx_count_frames; u32 tx_coal_frames; u32 tx_coal_timer; - struct stmmac_tx_info *tx_skbuff_dma; - dma_addr_t dma_tx_phy; int tx_coalesce; int hwts_tx_en; bool tx_path_in_lpi_mode; struct timer_list txtimer; bool tso; - struct dma_desc *dma_rx ____cacheline_aligned_in_smp; - struct dma_extended_desc *dma_erx; - struct sk_buff **rx_skbuff; - unsigned int cur_rx; - unsigned int dirty_rx; + /* TX Queue */ + struct stmmac_tx_queue *tx_queue; + + /* RX Queue */ + struct stmmac_rx_queue *rx_queue; + unsigned int dma_buf_sz; unsigned int rx_copybreak; - unsigned int rx_zeroc_thresh; u32 rx_riwt; int hwts_rx_en; - dma_addr_t *rx_skbuff_dma; - dma_addr_t dma_rx_phy; - - struct napi_struct napi ____cacheline_aligned_in_smp; void __iomem *ioaddr; struct net_device *dev; @@ -119,8 +142,6 @@ struct stmmac_priv { spinlock_t ptp_lock; void __iomem *mmcaddr; void __iomem *ptpaddr; - u32 rx_tail_addr; - u32 tx_tail_addr; u32 mss; #ifdef CONFIG_DEBUG_FS diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index d3a21519e4c0..a389dfbe630c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -185,26 +185,38 @@ static void print_pkt(unsigned char *buf, int len) print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); } -static inline u32 stmmac_tx_avail(struct stmmac_priv *priv) +/** + * stmmac_tx_avail - Get tx queue availability + * @priv: driver private structure + * @queue: TX queue index + */ +static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; u32 avail; - if (priv->dirty_tx > priv->cur_tx) - avail = priv->dirty_tx - priv->cur_tx - 1; + if (tx_q->dirty_tx > tx_q->cur_tx) + avail = tx_q->dirty_tx - tx_q->cur_tx - 1; else - avail = DMA_TX_SIZE - priv->cur_tx + priv->dirty_tx - 1; + avail = DMA_TX_SIZE - tx_q->cur_tx + tx_q->dirty_tx - 1; return avail; } -static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv) +/** + * stmmac_rx_dirty - Get RX queue dirty + * @priv: driver private structure + * @queue: RX queue index + */ +static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; u32 dirty; - if (priv->dirty_rx <= priv->cur_rx) - dirty = priv->cur_rx - priv->dirty_rx; + if (rx_q->dirty_rx <= rx_q->cur_rx) + dirty = rx_q->cur_rx - rx_q->dirty_rx; else - dirty = DMA_RX_SIZE - priv->dirty_rx + priv->cur_rx; + dirty = DMA_RX_SIZE - rx_q->dirty_rx + rx_q->cur_rx; return dirty; } @@ -232,9 +244,19 @@ static inline void stmmac_hw_fix_mac_speed(struct stmmac_priv *priv) */ static void stmmac_enable_eee_mode(struct stmmac_priv *priv) { + u32 tx_cnt = priv->plat->tx_queues_to_use; + u32 queue; + + /* check if all TX queues have the work finished */ + for (queue = 0; queue < tx_cnt; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + + if (tx_q->dirty_tx != tx_q->cur_tx) + return; /* still unfinished work */ + } + /* Check and enter in LPI mode */ - if ((priv->dirty_tx == priv->cur_tx) && - (priv->tx_path_in_lpi_mode == false)) + if (!priv->tx_path_in_lpi_mode) priv->hw->mac->set_eee_mode(priv->hw, priv->plat->en_tx_lpi_clockgating); } @@ -891,20 +913,40 @@ static int stmmac_init_phy(struct net_device *dev) static void stmmac_display_rings(struct stmmac_priv *priv) { + u32 rx_cnt = priv->plat->rx_queues_to_use; + u32 tx_cnt = priv->plat->tx_queues_to_use; void *head_rx, *head_tx; + u32 queue; - if (priv->extend_desc) { - head_rx = (void *)priv->dma_erx; - head_tx = (void *)priv->dma_etx; - } else { - head_rx = (void *)priv->dma_rx; - head_tx = (void *)priv->dma_tx; + /* Display RX rings */ + for (queue = 0; queue < rx_cnt; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + pr_info("\tRX Queue %d rings\n", queue); + + if (priv->extend_desc) + head_rx = (void *)rx_q->dma_erx; + else + head_rx = (void *)rx_q->dma_rx; + + /* Display Rx ring */ + priv->hw->desc->display_ring(head_rx, DMA_RX_SIZE, true); } - /* Display Rx ring */ - priv->hw->desc->display_ring(head_rx, DMA_RX_SIZE, true); - /* Display Tx ring */ - priv->hw->desc->display_ring(head_tx, DMA_TX_SIZE, false); + /* Display TX rings */ + for (queue = 0; queue < tx_cnt; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + + pr_info("\tTX Queue %d rings\n", queue); + + if (priv->extend_desc) + head_tx = (void *)tx_q->dma_etx; + else + head_tx = (void *)tx_q->dma_tx; + + /* Display Tx ring */ + priv->hw->desc->display_ring(head_tx, DMA_TX_SIZE, false); + } } static int stmmac_set_bfsize(int mtu, int bufsize) @@ -924,48 +966,86 @@ static int stmmac_set_bfsize(int mtu, int bufsize) } /** - * stmmac_clear_descriptors - clear descriptors + * stmmac_clear_rx_descriptors - clear the descriptors of a RX queue * @priv: driver private structure - * Description: this function is called to clear the tx and rx descriptors + * @queue: RX queue index + * Description: this function is called to clear the RX descriptors * in case of both basic and extended descriptors are used. */ -static void stmmac_clear_descriptors(struct stmmac_priv *priv) +static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue) { - int i; + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + u32 i = 0; - /* Clear the Rx/Tx descriptors */ + /* Clear the RX descriptors */ for (i = 0; i < DMA_RX_SIZE; i++) if (priv->extend_desc) - priv->hw->desc->init_rx_desc(&priv->dma_erx[i].basic, + priv->hw->desc->init_rx_desc(&rx_q->dma_erx[i].basic, priv->use_riwt, priv->mode, (i == DMA_RX_SIZE - 1)); else - priv->hw->desc->init_rx_desc(&priv->dma_rx[i], + priv->hw->desc->init_rx_desc(&rx_q->dma_rx[i], priv->use_riwt, priv->mode, (i == DMA_RX_SIZE - 1)); +} + +/** + * stmmac_clear_tx_descriptors - clear the descriptors of a TX queue + * @priv: driver private structure + * @queue: TX queue index + * Description: this function is called to clear the TX descriptors + * in case of both basic and extended descriptors are used. + */ +static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv, u32 queue) +{ + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + u32 i = 0; + + /* Clear the TX descriptors */ for (i = 0; i < DMA_TX_SIZE; i++) if (priv->extend_desc) - priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic, + priv->hw->desc->init_tx_desc(&tx_q->dma_etx[i].basic, priv->mode, (i == DMA_TX_SIZE - 1)); else - priv->hw->desc->init_tx_desc(&priv->dma_tx[i], + priv->hw->desc->init_tx_desc(&tx_q->dma_tx[i], priv->mode, (i == DMA_TX_SIZE - 1)); } +/** + * stmmac_clear_descriptors - clear descriptors + * @priv: driver private structure + * Description: this function is called to clear the tx and rx descriptors + * in case of both basic and extended descriptors are used. + */ +static void stmmac_clear_descriptors(struct stmmac_priv *priv) +{ + u32 rx_queue_cnt = priv->plat->rx_queues_to_use; + u32 tx_queue_cnt = priv->plat->tx_queues_to_use; + u32 queue; + + for (queue = 0; queue < rx_queue_cnt; queue++) + stmmac_clear_rx_descriptors(priv, queue); + + for (queue = 0; queue < tx_queue_cnt; queue++) + stmmac_clear_tx_descriptors(priv, queue); +} + /** * stmmac_init_rx_buffers - init the RX descriptor buffer. * @priv: driver private structure * @p: descriptor pointer * @i: descriptor index * @flags: gfp flag. + * @queue: RX queue index * Description: this function is called to allocate a receive buffer, perform * the DMA mapping and init the descriptor. */ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, - int i, gfp_t flags) + int i, gfp_t flags, u32 queue) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; struct sk_buff *skb; skb = __netdev_alloc_skb_ip_align(priv->dev, priv->dma_buf_sz, flags); @@ -974,20 +1054,20 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, "%s: Rx init fails; skb is NULL\n", __func__); return -ENOMEM; } - priv->rx_skbuff[i] = skb; - priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, + rx_q->rx_skbuff[i] = skb; + rx_q->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, priv->dma_buf_sz, DMA_FROM_DEVICE); - if (dma_mapping_error(priv->device, priv->rx_skbuff_dma[i])) { + if (dma_mapping_error(priv->device, rx_q->rx_skbuff_dma[i])) { netdev_err(priv->dev, "%s: DMA mapping error\n", __func__); dev_kfree_skb_any(skb); return -EINVAL; } if (priv->synopsys_id >= DWMAC_CORE_4_00) - p->des0 = cpu_to_le32(priv->rx_skbuff_dma[i]); + p->des0 = cpu_to_le32(rx_q->rx_skbuff_dma[i]); else - p->des2 = cpu_to_le32(priv->rx_skbuff_dma[i]); + p->des2 = cpu_to_le32(rx_q->rx_skbuff_dma[i]); if ((priv->hw->mode->init_desc3) && (priv->dma_buf_sz == BUF_SIZE_16KiB)) @@ -996,30 +1076,136 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, return 0; } -static void stmmac_free_rx_buffers(struct stmmac_priv *priv, int i) +/** + * stmmac_free_rx_buffers - free RX buffers. + * @priv: driver private structure + * @queue: RX queue index + * @i: buffer index + */ +static void stmmac_free_rx_buffers(struct stmmac_priv *priv, u32 queue, int i) { - if (priv->rx_skbuff[i]) { - dma_unmap_single(priv->device, priv->rx_skbuff_dma[i], + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + if (rx_q->rx_skbuff[i]) { + dma_unmap_single(priv->device, rx_q->rx_skbuff_dma[i], priv->dma_buf_sz, DMA_FROM_DEVICE); - dev_kfree_skb_any(priv->rx_skbuff[i]); + dev_kfree_skb_any(rx_q->rx_skbuff[i]); } - priv->rx_skbuff[i] = NULL; + rx_q->rx_skbuff[i] = NULL; } /** - * init_dma_desc_rings - init the RX/TX descriptor rings + * stmmac_free_tx_buffers - free RX buffers. + * @priv: driver private structure + * @queue: RX queue index + * @i: buffer index + */ +static void stmmac_free_tx_buffers(struct stmmac_priv *priv, u32 queue, u32 i) +{ + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + + if (tx_q->tx_skbuff_dma[i].buf) { + if (tx_q->tx_skbuff_dma[i].map_as_page) + dma_unmap_page(priv->device, + tx_q->tx_skbuff_dma[i].buf, + tx_q->tx_skbuff_dma[i].len, + DMA_TO_DEVICE); + else + dma_unmap_single(priv->device, + tx_q->tx_skbuff_dma[i].buf, + tx_q->tx_skbuff_dma[i].len, + DMA_TO_DEVICE); + } + + if (tx_q->tx_skbuff[i]) { + dev_kfree_skb_any(tx_q->tx_skbuff[i]); + tx_q->tx_skbuff[i] = NULL; + tx_q->tx_skbuff_dma[i].buf = 0; + tx_q->tx_skbuff_dma[i].map_as_page = false; + } +} + +/** + * init_tx_dma_desc_rings - init the TX descriptor rings + * @dev: net device structure + * Description: this function initializes the DMA TX descriptors + * and allocates the socket buffers. It suppors the chained and ring + * modes. + */ +static int init_tx_dma_desc_rings(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + u32 tx_queue_cnt = priv->plat->tx_queues_to_use; + u32 queue; + int i = 0; + + for (queue = 0; queue < tx_queue_cnt; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + + netif_dbg(priv, probe, priv->dev, + "(%s) dma_tx_phy=0x%08x\n", __func__, + (u32)tx_q->dma_tx_phy); + + /* Setup the chained descriptor addresses */ + if (priv->mode == STMMAC_CHAIN_MODE) { + if (priv->extend_desc) + priv->hw->mode->init(tx_q->dma_etx, + tx_q->dma_tx_phy, + DMA_TX_SIZE, 1); + else + priv->hw->mode->init(tx_q->dma_tx, + tx_q->dma_tx_phy, + DMA_TX_SIZE, 0); + } + + for (i = 0; i < DMA_TX_SIZE; i++) { + struct dma_desc *p; + + if (priv->extend_desc) + p = &((tx_q->dma_etx + i)->basic); + else + p = tx_q->dma_tx + i; + + if (priv->synopsys_id >= DWMAC_CORE_4_00) { + p->des0 = 0; + p->des1 = 0; + p->des2 = 0; + p->des3 = 0; + } else { + p->des2 = 0; + } + + tx_q->tx_skbuff_dma[i].buf = 0; + tx_q->tx_skbuff_dma[i].map_as_page = false; + tx_q->tx_skbuff_dma[i].len = 0; + tx_q->tx_skbuff_dma[i].last_segment = false; + tx_q->tx_skbuff[i] = NULL; + } + + tx_q->dirty_tx = 0; + tx_q->cur_tx = 0; + netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue)); + } + + return 0; +} + +/** + * init_rx_dma_desc_rings - init the RX descriptor rings * @dev: net device structure * @flags: gfp flag. - * Description: this function initializes the DMA RX/TX descriptors - * and allocates the socket buffers. It supports the chained and ring + * Description: this function initializes the DMA RX descriptors + * and allocates the socket buffers. It suppors the chained and ring * modes. */ -static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) +static int init_rx_dma_desc_rings(struct net_device *dev, gfp_t flags) { - int i; struct stmmac_priv *priv = netdev_priv(dev); + u32 rx_count = priv->plat->rx_queues_to_use; unsigned int bfsize = 0; int ret = -ENOMEM; + u32 queue; + int i; if (priv->hw->mode->set_16kib_bfsize) bfsize = priv->hw->mode->set_16kib_bfsize(dev->mtu); @@ -1029,235 +1215,350 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) priv->dma_buf_sz = bfsize; - netif_dbg(priv, probe, priv->dev, - "(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", - __func__, (u32)priv->dma_rx_phy, (u32)priv->dma_tx_phy); - /* RX INITIALIZATION */ netif_dbg(priv, probe, priv->dev, "SKB addresses:\nskb\t\tskb data\tdma data\n"); - for (i = 0; i < DMA_RX_SIZE; i++) { - struct dma_desc *p; - if (priv->extend_desc) - p = &((priv->dma_erx + i)->basic); - else - p = priv->dma_rx + i; + for (queue = 0; queue < rx_count; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - ret = stmmac_init_rx_buffers(priv, p, i, flags); - if (ret) - goto err_init_rx_buffers; + netif_dbg(priv, probe, priv->dev, + "(%s) dma_rx_phy=0x%08x\n", __func__, + (u32)rx_q->dma_rx_phy); - netif_dbg(priv, probe, priv->dev, "[%p]\t[%p]\t[%x]\n", - priv->rx_skbuff[i], priv->rx_skbuff[i]->data, - (unsigned int)priv->rx_skbuff_dma[i]); - } - priv->cur_rx = 0; - priv->dirty_rx = (unsigned int)(i - DMA_RX_SIZE); - buf_sz = bfsize; + for (i = 0; i < DMA_RX_SIZE; i++) { + struct dma_desc *p; - /* Setup the chained descriptor addresses */ - if (priv->mode == STMMAC_CHAIN_MODE) { - if (priv->extend_desc) { - priv->hw->mode->init(priv->dma_erx, priv->dma_rx_phy, - DMA_RX_SIZE, 1); - priv->hw->mode->init(priv->dma_etx, priv->dma_tx_phy, - DMA_TX_SIZE, 1); - } else { - priv->hw->mode->init(priv->dma_rx, priv->dma_rx_phy, - DMA_RX_SIZE, 0); - priv->hw->mode->init(priv->dma_tx, priv->dma_tx_phy, - DMA_TX_SIZE, 0); + if (priv->extend_desc) + p = &((rx_q->dma_erx + i)->basic); + else + p = rx_q->dma_rx + i; + + ret = stmmac_init_rx_buffers(priv, p, i, flags, queue); + if (ret) + goto err_init_rx_buffers; + + netif_dbg(priv, probe, priv->dev, "[%p]\t[%p]\t[%x]\n", + rx_q->rx_skbuff[i], + rx_q->rx_skbuff[i]->data, + (unsigned int)rx_q->rx_skbuff_dma[i]); } - } - /* TX INITIALIZATION */ - for (i = 0; i < DMA_TX_SIZE; i++) { - struct dma_desc *p; - if (priv->extend_desc) - p = &((priv->dma_etx + i)->basic); - else - p = priv->dma_tx + i; + rx_q->cur_rx = 0; + rx_q->dirty_rx = (unsigned int)(i - DMA_RX_SIZE); - if (priv->synopsys_id >= DWMAC_CORE_4_00) { - p->des0 = 0; - p->des1 = 0; - p->des2 = 0; - p->des3 = 0; - } else { - p->des2 = 0; + stmmac_clear_rx_descriptors(priv, queue); + + if (priv->mode == STMMAC_CHAIN_MODE) { + if (priv->extend_desc) + priv->hw->mode->init(rx_q->dma_erx, + rx_q->dma_rx_phy, + DMA_RX_SIZE, 1); + else + priv->hw->mode->init(rx_q->dma_rx, + rx_q->dma_rx_phy, + DMA_RX_SIZE, 0); } + } - priv->tx_skbuff_dma[i].buf = 0; - priv->tx_skbuff_dma[i].map_as_page = false; - priv->tx_skbuff_dma[i].len = 0; - priv->tx_skbuff_dma[i].last_segment = false; - priv->tx_skbuff[i] = NULL; + buf_sz = bfsize; + + return 0; + +err_init_rx_buffers: + while (queue-- >= 0) { + while (--i >= 0) + stmmac_free_rx_buffers(priv, queue, i); + + i = DMA_RX_SIZE; } - priv->dirty_tx = 0; - priv->cur_tx = 0; - netdev_reset_queue(priv->dev); + return ret; +} - stmmac_clear_descriptors(priv); +/** + * init_dma_desc_rings - init the RX/TX descriptor rings + * @dev: net device structure + * @flags: gfp flag. + * Description: this function initializes the DMA RX/TX descriptors + * and allocates the socket buffers. It suppors the chained and ring + * modes. + */ +static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) +{ + struct stmmac_priv *priv = netdev_priv(dev); + int ret = init_rx_dma_desc_rings(dev, flags); + + if (ret) + return ret; + + ret = init_tx_dma_desc_rings(dev); if (netif_msg_hw(priv)) stmmac_display_rings(priv); - return 0; -err_init_rx_buffers: - while (--i >= 0) - stmmac_free_rx_buffers(priv, i); return ret; } -static void dma_free_rx_skbufs(struct stmmac_priv *priv) +static void dma_free_rx_skbufs(struct stmmac_priv *priv, u32 queue) { int i; for (i = 0; i < DMA_RX_SIZE; i++) - stmmac_free_rx_buffers(priv, i); + stmmac_free_rx_buffers(priv, queue, i); } -static void dma_free_tx_skbufs(struct stmmac_priv *priv) +static void dma_free_tx_skbufs(struct stmmac_priv *priv, u32 queue) { int i; - for (i = 0; i < DMA_TX_SIZE; i++) { - if (priv->tx_skbuff_dma[i].buf) { - if (priv->tx_skbuff_dma[i].map_as_page) - dma_unmap_page(priv->device, - priv->tx_skbuff_dma[i].buf, - priv->tx_skbuff_dma[i].len, - DMA_TO_DEVICE); - else - dma_unmap_single(priv->device, - priv->tx_skbuff_dma[i].buf, - priv->tx_skbuff_dma[i].len, - DMA_TO_DEVICE); - } + for (i = 0; i < DMA_TX_SIZE; i++) + stmmac_free_tx_buffers(priv, queue, i); +} + +/** + * free_rx_dma_desc_resources - free RX DMA resources + * @priv: driver private structure + */ +static void free_rx_dma_desc_resources(struct stmmac_priv *priv) +{ + u32 rx_count = priv->plat->rx_queues_to_use; + u32 queue = 0; + + if (!priv->rx_queue) + return; + + /* Free RX queue resources */ + for (queue = 0; queue < rx_count; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + if (!rx_q) + break; + + /* Release the DMA RX socket buffers */ + dma_free_rx_skbufs(priv, queue); + + kfree(rx_q->rx_skbuff); + + kfree(rx_q->rx_skbuff_dma); + + if (!priv->extend_desc) + dma_free_coherent(priv->device, + DMA_RX_SIZE * sizeof(struct dma_desc), + rx_q->dma_rx, + rx_q->dma_rx_phy); + else + dma_free_coherent(priv->device, DMA_RX_SIZE * + sizeof(struct dma_extended_desc), + rx_q->dma_erx, + rx_q->dma_rx_phy); + } + + kfree(priv->rx_queue); +} + +/** + * free_tx_dma_desc_resources - free TX DMA resources + * @priv: driver private structure + */ +static void free_tx_dma_desc_resources(struct stmmac_priv *priv) +{ + u32 tx_count = priv->plat->tx_queues_to_use; + u32 queue = 0; + + if (!priv->tx_queue) + return; + + /* Free TX queue resources */ + for (queue = 0; queue < tx_count; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + + if (!tx_q) + break; + + /* Release the DMA TX socket buffers */ + dma_free_tx_skbufs(priv, queue); + + kfree(tx_q->tx_skbuff); + + kfree(tx_q->tx_skbuff_dma); + + if (!priv->extend_desc) + dma_free_coherent(priv->device, + DMA_TX_SIZE * sizeof(struct dma_desc), + tx_q->dma_tx, + tx_q->dma_tx_phy); + else + dma_free_coherent(priv->device, DMA_TX_SIZE * + sizeof(struct dma_extended_desc), + tx_q->dma_etx, + tx_q->dma_tx_phy); + } + + kfree(priv->tx_queue); +} - if (priv->tx_skbuff[i]) { - dev_kfree_skb_any(priv->tx_skbuff[i]); - priv->tx_skbuff[i] = NULL; - priv->tx_skbuff_dma[i].buf = 0; - priv->tx_skbuff_dma[i].map_as_page = false; +/** + * free_dma_desc_resources - free All DMA resources + * @priv: driver private structure + */ +static void free_dma_desc_resources(struct stmmac_priv *priv) +{ + free_rx_dma_desc_resources(priv); + free_tx_dma_desc_resources(priv); +} + +/** + * alloc_rx_dma_desc_resources - alloc RX resources. + * @priv: private structure + * Description: according to which descriptor can be used (extend or basic) + * this function allocates the resources for RX paths. It pre-allocates the + * RX socket buffer in order to allow zero-copy mechanism. + */ +static int alloc_rx_dma_desc_resources(struct stmmac_priv *priv) +{ + u32 rx_count = priv->plat->rx_queues_to_use; + int ret = -ENOMEM; + u32 queue = 0; + + /* Allocate RX queues array */ + priv->rx_queue = kmalloc_array(rx_count, + sizeof(struct stmmac_rx_queue), + GFP_KERNEL); + if (!priv->rx_queue) { + kfree(priv->rx_queue); + return -ENOMEM; + } + + /* RX queues buffers and DMA */ + for (queue = 0; queue < rx_count; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + rx_q->queue_index = queue; + rx_q->priv_data = priv; + + rx_q->rx_skbuff_dma = kmalloc_array(DMA_RX_SIZE, + sizeof(dma_addr_t), + GFP_KERNEL); + if (!rx_q->rx_skbuff_dma) + goto err_dma_buffers; + + rx_q->rx_skbuff = kmalloc_array(DMA_RX_SIZE, + sizeof(struct sk_buff *), + GFP_KERNEL); + if (!rx_q->rx_skbuff) + goto err_dma_buffers; + + if (priv->extend_desc) { + rx_q->dma_erx = dma_zalloc_coherent(priv->device, + (DMA_RX_SIZE * sizeof(struct dma_extended_desc)), + &rx_q->dma_rx_phy, GFP_KERNEL); + + if (!rx_q->dma_erx) + goto err_dma_buffers; + } else { + rx_q->dma_rx = dma_zalloc_coherent(priv->device, + (DMA_RX_SIZE * sizeof(struct dma_desc)), + &rx_q->dma_rx_phy, GFP_KERNEL); + + if (!rx_q->dma_rx) + goto err_dma_buffers; } } + + return 0; + +err_dma_buffers: + free_rx_dma_desc_resources(priv); + + return ret; } /** - * alloc_dma_desc_resources - alloc TX/RX resources. + * alloc_tx_dma_desc_resources - alloc TX resources. * @priv: private structure * Description: according to which descriptor can be used (extend or basic) - * this function allocates the resources for TX and RX paths. In case of - * reception, for example, it pre-allocated the RX socket buffer in order to - * allow zero-copy mechanism. + * this function allocates the resources for TX paths. */ -static int alloc_dma_desc_resources(struct stmmac_priv *priv) +static int alloc_tx_dma_desc_resources(struct stmmac_priv *priv) { + u32 tx_count = priv->plat->tx_queues_to_use; int ret = -ENOMEM; + u32 queue = 0; - priv->rx_skbuff_dma = kmalloc_array(DMA_RX_SIZE, sizeof(dma_addr_t), - GFP_KERNEL); - if (!priv->rx_skbuff_dma) + /* Allocate TX queues array */ + priv->tx_queue = kmalloc_array(tx_count, + sizeof(struct stmmac_tx_queue), + GFP_KERNEL); + if (!priv->tx_queue) return -ENOMEM; - priv->rx_skbuff = kmalloc_array(DMA_RX_SIZE, sizeof(struct sk_buff *), - GFP_KERNEL); - if (!priv->rx_skbuff) - goto err_rx_skbuff; - - priv->tx_skbuff_dma = kmalloc_array(DMA_TX_SIZE, - sizeof(*priv->tx_skbuff_dma), - GFP_KERNEL); - if (!priv->tx_skbuff_dma) - goto err_tx_skbuff_dma; - - priv->tx_skbuff = kmalloc_array(DMA_TX_SIZE, sizeof(struct sk_buff *), - GFP_KERNEL); - if (!priv->tx_skbuff) - goto err_tx_skbuff; - - if (priv->extend_desc) { - priv->dma_erx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE * - sizeof(struct - dma_extended_desc), - &priv->dma_rx_phy, - GFP_KERNEL); - if (!priv->dma_erx) - goto err_dma; + /* TX queues buffers and DMA */ + for (queue = 0; queue < tx_count; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + + tx_q->queue_index = queue; + tx_q->priv_data = priv; - priv->dma_etx = dma_zalloc_coherent(priv->device, DMA_TX_SIZE * - sizeof(struct - dma_extended_desc), - &priv->dma_tx_phy, + tx_q->tx_skbuff_dma = kmalloc_array(DMA_TX_SIZE, + sizeof(struct stmmac_tx_info), + GFP_KERNEL); + + if (!tx_q->tx_skbuff_dma) + goto err_dma_buffers; + + tx_q->tx_skbuff = kmalloc_array(DMA_TX_SIZE, + sizeof(struct sk_buff *), GFP_KERNEL); - if (!priv->dma_etx) { - dma_free_coherent(priv->device, DMA_RX_SIZE * - sizeof(struct dma_extended_desc), - priv->dma_erx, priv->dma_rx_phy); - goto err_dma; - } - } else { - priv->dma_rx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE * - sizeof(struct dma_desc), - &priv->dma_rx_phy, - GFP_KERNEL); - if (!priv->dma_rx) - goto err_dma; - - priv->dma_tx = dma_zalloc_coherent(priv->device, DMA_TX_SIZE * - sizeof(struct dma_desc), - &priv->dma_tx_phy, - GFP_KERNEL); - if (!priv->dma_tx) { - dma_free_coherent(priv->device, DMA_RX_SIZE * - sizeof(struct dma_desc), - priv->dma_rx, priv->dma_rx_phy); - goto err_dma; + if (!tx_q->tx_skbuff) + goto err_dma_buffers; + + if (priv->extend_desc) { + tx_q->dma_etx = + dma_zalloc_coherent(priv->device, + (DMA_TX_SIZE * sizeof(struct dma_extended_desc)), + &tx_q->dma_tx_phy, GFP_KERNEL); + + if (!tx_q->dma_etx) + goto err_dma_buffers; + } else { + tx_q->dma_tx = + dma_zalloc_coherent(priv->device, + (DMA_TX_SIZE * sizeof(struct dma_desc)), + &tx_q->dma_tx_phy, GFP_KERNEL); + + if (!tx_q->dma_tx) + goto err_dma_buffers; } } return 0; -err_dma: - kfree(priv->tx_skbuff); -err_tx_skbuff: - kfree(priv->tx_skbuff_dma); -err_tx_skbuff_dma: - kfree(priv->rx_skbuff); -err_rx_skbuff: - kfree(priv->rx_skbuff_dma); +err_dma_buffers: + free_tx_dma_desc_resources(priv); + return ret; } -static void free_dma_desc_resources(struct stmmac_priv *priv) +/** + * alloc_dma_desc_resources - alloc TX/RX resources. + * @priv: private structure + * Description: according to which descriptor can be used (extend or basic) + * this function allocates the resources for TX and RX paths. In case of + * reception, for example, it pre-allocated the RX socket buffer in order to + * allow zero-copy mechanism. + */ +static int alloc_dma_desc_resources(struct stmmac_priv *priv) { - /* Release the DMA TX/RX socket buffers */ - dma_free_rx_skbufs(priv); - dma_free_tx_skbufs(priv); - - /* Free DMA regions of consistent memory previously allocated */ - if (!priv->extend_desc) { - dma_free_coherent(priv->device, - DMA_TX_SIZE * sizeof(struct dma_desc), - priv->dma_tx, priv->dma_tx_phy); - dma_free_coherent(priv->device, - DMA_RX_SIZE * sizeof(struct dma_desc), - priv->dma_rx, priv->dma_rx_phy); - } else { - dma_free_coherent(priv->device, DMA_TX_SIZE * - sizeof(struct dma_extended_desc), - priv->dma_etx, priv->dma_tx_phy); - dma_free_coherent(priv->device, DMA_RX_SIZE * - sizeof(struct dma_extended_desc), - priv->dma_erx, priv->dma_rx_phy); - } - kfree(priv->rx_skbuff_dma); - kfree(priv->rx_skbuff); - kfree(priv->tx_skbuff_dma); - kfree(priv->tx_skbuff); + int ret = 0; + + ret = alloc_tx_dma_desc_resources(priv); + if (ret) + return ret; + + ret = alloc_rx_dma_desc_resources(priv); + + return ret; } /** @@ -1421,26 +1722,28 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv) /** * stmmac_tx_clean - to manage the transmission completion * @priv: driver private structure + * @queue: TX queue index * Description: it reclaims the transmit resources after transmission completes. */ -static void stmmac_tx_clean(struct stmmac_priv *priv) +static void stmmac_tx_clean(struct stmmac_priv *priv, u32 queue) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; unsigned int bytes_compl = 0, pkts_compl = 0; - unsigned int entry = priv->dirty_tx; + unsigned int entry = tx_q->dirty_tx; netif_tx_lock(priv->dev); priv->xstats.tx_clean++; - while (entry != priv->cur_tx) { - struct sk_buff *skb = priv->tx_skbuff[entry]; + while (entry != tx_q->cur_tx) { + struct sk_buff *skb = tx_q->tx_skbuff[entry]; struct dma_desc *p; int status; if (priv->extend_desc) - p = (struct dma_desc *)(priv->dma_etx + entry); + p = (struct dma_desc *)(tx_q->dma_etx + entry); else - p = priv->dma_tx + entry; + p = tx_q->dma_tx + entry; status = priv->hw->desc->tx_status(&priv->dev->stats, &priv->xstats, p, @@ -1461,48 +1764,50 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) stmmac_get_tx_hwtstamp(priv, p, skb); } - if (likely(priv->tx_skbuff_dma[entry].buf)) { - if (priv->tx_skbuff_dma[entry].map_as_page) + if (likely(tx_q->tx_skbuff_dma[entry].buf)) { + if (tx_q->tx_skbuff_dma[entry].map_as_page) dma_unmap_page(priv->device, - priv->tx_skbuff_dma[entry].buf, - priv->tx_skbuff_dma[entry].len, + tx_q->tx_skbuff_dma[entry].buf, + tx_q->tx_skbuff_dma[entry].len, DMA_TO_DEVICE); else dma_unmap_single(priv->device, - priv->tx_skbuff_dma[entry].buf, - priv->tx_skbuff_dma[entry].len, + tx_q->tx_skbuff_dma[entry].buf, + tx_q->tx_skbuff_dma[entry].len, DMA_TO_DEVICE); - priv->tx_skbuff_dma[entry].buf = 0; - priv->tx_skbuff_dma[entry].len = 0; - priv->tx_skbuff_dma[entry].map_as_page = false; + tx_q->tx_skbuff_dma[entry].buf = 0; + tx_q->tx_skbuff_dma[entry].len = 0; + tx_q->tx_skbuff_dma[entry].map_as_page = false; } if (priv->hw->mode->clean_desc3) - priv->hw->mode->clean_desc3(priv, p); + priv->hw->mode->clean_desc3(tx_q, p); - priv->tx_skbuff_dma[entry].last_segment = false; - priv->tx_skbuff_dma[entry].is_jumbo = false; + tx_q->tx_skbuff_dma[entry].last_segment = false; + tx_q->tx_skbuff_dma[entry].is_jumbo = false; if (likely(skb != NULL)) { pkts_compl++; bytes_compl += skb->len; dev_consume_skb_any(skb); - priv->tx_skbuff[entry] = NULL; + tx_q->tx_skbuff[entry] = NULL; } priv->hw->desc->release_tx_desc(p, priv->mode); entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); } - priv->dirty_tx = entry; + tx_q->dirty_tx = entry; - netdev_completed_queue(priv->dev, pkts_compl, bytes_compl); + netdev_tx_completed_queue(netdev_get_tx_queue(priv->dev, queue), + pkts_compl, bytes_compl); - if (unlikely(netif_queue_stopped(priv->dev) && - stmmac_tx_avail(priv) > STMMAC_TX_THRESH)) { + if (unlikely(netif_tx_queue_stopped(netdev_get_tx_queue(priv->dev, + queue))) && + stmmac_tx_avail(priv, queue) > STMMAC_TX_THRESH) { netif_dbg(priv, tx_done, priv->dev, "%s: restart transmit\n", __func__); - netif_wake_queue(priv->dev); + netif_tx_wake_queue(netdev_get_tx_queue(priv->dev, queue)); } if ((priv->eee_enabled) && (!priv->tx_path_in_lpi_mode)) { @@ -1525,33 +1830,36 @@ static inline void stmmac_disable_dma_irq(struct stmmac_priv *priv, u32 chan) /** * stmmac_tx_err - to manage the tx error * @priv: driver private structure - * @chan: channel index + * @queue: queue index * Description: it cleans the descriptors and restarts the transmission * in case of transmission errors. */ -static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan) +static void stmmac_tx_err(struct stmmac_priv *priv, u32 queue) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + u32 chan = queue; int i; - netif_stop_queue(priv->dev); + + netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); stmmac_stop_tx_dma(priv, chan); - dma_free_tx_skbufs(priv); + dma_free_tx_skbufs(priv, queue); for (i = 0; i < DMA_TX_SIZE; i++) if (priv->extend_desc) - priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic, + priv->hw->desc->init_tx_desc(&tx_q->dma_etx[i].basic, priv->mode, (i == DMA_TX_SIZE - 1)); else - priv->hw->desc->init_tx_desc(&priv->dma_tx[i], + priv->hw->desc->init_tx_desc(&tx_q->dma_tx[i], priv->mode, (i == DMA_TX_SIZE - 1)); - priv->dirty_tx = 0; - priv->cur_tx = 0; - netdev_reset_queue(priv->dev); + tx_q->dirty_tx = 0; + tx_q->cur_tx = 0; + netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue)); stmmac_start_tx_dma(priv, chan); priv->dev->stats.tx_errors++; - netif_wake_queue(priv->dev); + netif_tx_wake_queue(netdev_get_tx_queue(priv->dev, queue)); } /** @@ -1596,12 +1904,14 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv) u32 chan; for (chan = 0; chan < tx_channel_count; chan++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan]; + status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats, chan); if (likely((status & handle_rx)) || (status & handle_tx)) { - if (likely(napi_schedule_prep(&priv->napi))) { + if (likely(napi_schedule_prep(&rx_q->napi))) { stmmac_disable_dma_irq(priv, chan); - __napi_schedule(&priv->napi); + __napi_schedule(&rx_q->napi); } } @@ -1734,6 +2044,8 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) { u32 rx_channels_count = priv->plat->rx_queues_to_use; u32 tx_channels_count = priv->plat->tx_queues_to_use; + struct stmmac_rx_queue *rx_q; + struct stmmac_tx_queue *tx_q; u32 dummy_dma_rx_phy = 0; u32 dummy_dma_tx_phy = 0; u32 chan = 0; @@ -1761,36 +2073,43 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) /* DMA RX Channel Configuration */ for (chan = 0; chan < rx_channels_count; chan++) { + rx_q = &priv->rx_queue[chan]; + priv->hw->dma->init_rx_chan(priv->ioaddr, priv->plat->dma_cfg, - priv->dma_rx_phy, chan); + rx_q->dma_rx_phy, chan); - priv->rx_tail_addr = priv->dma_rx_phy + + rx_q->rx_tail_addr = rx_q->dma_rx_phy + (DMA_RX_SIZE * sizeof(struct dma_desc)); priv->hw->dma->set_rx_tail_ptr(priv->ioaddr, - priv->rx_tail_addr, + rx_q->rx_tail_addr, chan); } /* DMA TX Channel Configuration */ for (chan = 0; chan < tx_channels_count; chan++) { + tx_q = &priv->tx_queue[chan]; + priv->hw->dma->init_chan(priv->ioaddr, - priv->plat->dma_cfg, - chan); + priv->plat->dma_cfg, + chan); priv->hw->dma->init_tx_chan(priv->ioaddr, priv->plat->dma_cfg, - priv->dma_tx_phy, chan); + tx_q->dma_tx_phy, chan); - priv->tx_tail_addr = priv->dma_tx_phy + + tx_q->tx_tail_addr = tx_q->dma_tx_phy + (DMA_TX_SIZE * sizeof(struct dma_desc)); priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, - priv->tx_tail_addr, + tx_q->tx_tail_addr, chan); } } else { + rx_q = &priv->rx_queue[chan]; + tx_q = &priv->tx_queue[chan]; + priv->hw->dma->init(priv->ioaddr, priv->plat->dma_cfg, - priv->dma_tx_phy, priv->dma_rx_phy, atds); + tx_q->dma_tx_phy, rx_q->dma_rx_phy, atds); } if (priv->plat->axi && priv->hw->dma->axi) @@ -1808,8 +2127,70 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) static void stmmac_tx_timer(unsigned long data) { struct stmmac_priv *priv = (struct stmmac_priv *)data; + u32 tx_queues_count = priv->plat->tx_queues_to_use; + u32 queue; + + /* let's scan all the tx queues */ + for (queue = 0; queue < tx_queues_count; queue++) + stmmac_tx_clean(priv, queue); +} + +/** + * stmmac_stop_all_queues - Stop all queues + * @priv: driver private structure + */ +static void stmmac_stop_all_queues(struct stmmac_priv *priv) +{ + u32 tx_queues_cnt = priv->plat->tx_queues_to_use; + u32 queue; + + for (queue = 0; queue < tx_queues_cnt; queue++) + netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); +} + +/** + * stmmac_start_all_queues - Start all queues + * @priv: driver private structure + */ +static void stmmac_start_all_queues(struct stmmac_priv *priv) +{ + u32 tx_queues_cnt = priv->plat->tx_queues_to_use; + u32 queue; - stmmac_tx_clean(priv); + for (queue = 0; queue < tx_queues_cnt; queue++) + netif_tx_start_queue(netdev_get_tx_queue(priv->dev, queue)); +} + +/** + * stmmac_disable_all_queues - Disable all queues + * @priv: driver private structure + */ +static void stmmac_disable_all_queues(struct stmmac_priv *priv) +{ + u32 rx_queues_cnt = priv->plat->rx_queues_to_use; + u32 queue; + + for (queue = 0; queue < rx_queues_cnt; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + napi_disable(&rx_q->napi); + } +} + +/** + * stmmac_enable_all_queues - Enable all queues + * @priv: driver private structure + */ +static void stmmac_enable_all_queues(struct stmmac_priv *priv) +{ + u32 rx_queues_cnt = priv->plat->rx_queues_to_use; + u32 queue; + + for (queue = 0; queue < rx_queues_cnt; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + napi_enable(&rx_q->napi); + } } /** @@ -2098,23 +2479,8 @@ static int stmmac_open(struct net_device *dev) memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats)); priv->xstats.threshold = tc; - priv->dma_buf_sz = STMMAC_ALIGN(buf_sz); priv->rx_copybreak = STMMAC_RX_COPYBREAK; - ret = alloc_dma_desc_resources(priv); - if (ret < 0) { - netdev_err(priv->dev, "%s: DMA descriptors allocation failed\n", - __func__); - goto dma_desc_error; - } - - ret = init_dma_desc_rings(dev, GFP_KERNEL); - if (ret < 0) { - netdev_err(priv->dev, "%s: DMA descriptors initialization failed\n", - __func__); - goto init_error; - } - ret = stmmac_hw_setup(dev, true); if (ret < 0) { netdev_err(priv->dev, "%s: Hw setup failed\n", __func__); @@ -2160,8 +2526,8 @@ static int stmmac_open(struct net_device *dev) } } - napi_enable(&priv->napi); - netif_start_queue(dev); + stmmac_enable_all_queues(priv); + stmmac_start_all_queues(priv); return 0; @@ -2178,7 +2544,7 @@ irq_error: stmmac_hw_teardown(dev); init_error: free_dma_desc_resources(priv); -dma_desc_error: + if (dev->phydev) phy_disconnect(dev->phydev); @@ -2204,9 +2570,9 @@ static int stmmac_release(struct net_device *dev) phy_disconnect(dev->phydev); } - netif_stop_queue(dev); + stmmac_stop_all_queues(priv); - napi_disable(&priv->napi); + stmmac_disable_all_queues(priv); del_timer_sync(&priv->txtimer); @@ -2243,22 +2609,24 @@ static int stmmac_release(struct net_device *dev) * @des: buffer start address * @total_len: total length to fill in descriptors * @last_segmant: condition for the last descriptor + * @queue: TX queue index * Description: * This function fills descriptor and request new descriptors according to * buffer length to fill */ static void stmmac_tso_allocator(struct stmmac_priv *priv, unsigned int des, - int total_len, bool last_segment) + int total_len, bool last_segment, u32 queue) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; struct dma_desc *desc; - int tmp_len; u32 buff_size; + int tmp_len; tmp_len = total_len; while (tmp_len > 0) { - priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE); - desc = priv->dma_tx + priv->cur_tx; + tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE); + desc = tx_q->dma_tx + tx_q->cur_tx; desc->des0 = cpu_to_le32(des + (total_len - tmp_len)); buff_size = tmp_len >= TSO_MAX_BUFF_SIZE ? @@ -2302,23 +2670,27 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, unsigned int des, */ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) { - u32 pay_len, mss; - int tmp_pay_len = 0; + struct dma_desc *desc, *first, *mss_desc = NULL; struct stmmac_priv *priv = netdev_priv(dev); + u32 queue = skb_get_queue_mapping(skb); int nfrags = skb_shinfo(skb)->nr_frags; unsigned int first_entry, des; - struct dma_desc *desc, *first, *mss_desc = NULL; + struct stmmac_tx_queue *tx_q; + int tmp_pay_len = 0; + u32 pay_len, mss; u8 proto_hdr_len; int i; + tx_q = &priv->tx_queue[queue]; + /* Compute header lengths */ proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); /* Desc availability based on threshold should be enough safe */ - if (unlikely(stmmac_tx_avail(priv) < + if (unlikely(stmmac_tx_avail(priv, queue) < (((skb->len - proto_hdr_len) / TSO_MAX_BUFF_SIZE + 1)))) { - if (!netif_queue_stopped(dev)) { - netif_stop_queue(dev); + if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) { + netif_tx_stop_queue(netdev_get_tx_queue(dev, queue)); /* This is a hard error, log it. */ netdev_err(priv->dev, "%s: Tx Ring full when queue awake\n", @@ -2333,10 +2705,10 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) /* set new MSS value if needed */ if (mss != priv->mss) { - mss_desc = priv->dma_tx + priv->cur_tx; + mss_desc = tx_q->dma_tx + tx_q->cur_tx; priv->hw->desc->set_mss(mss_desc, mss); priv->mss = mss; - priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE); + tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE); } if (netif_msg_tx_queued(priv)) { @@ -2346,9 +2718,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) skb->data_len); } - first_entry = priv->cur_tx; + first_entry = tx_q->cur_tx; - desc = priv->dma_tx + first_entry; + desc = tx_q->dma_tx + first_entry; first = desc; /* first descriptor: fill Headers on Buf1 */ @@ -2357,9 +2729,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) if (dma_mapping_error(priv->device, des)) goto dma_map_err; - priv->tx_skbuff_dma[first_entry].buf = des; - priv->tx_skbuff_dma[first_entry].len = skb_headlen(skb); - priv->tx_skbuff[first_entry] = skb; + tx_q->tx_skbuff_dma[first_entry].buf = des; + tx_q->tx_skbuff_dma[first_entry].len = skb_headlen(skb); + tx_q->tx_skbuff[first_entry] = skb; first->des0 = cpu_to_le32(des); @@ -2370,7 +2742,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) /* If needed take extra descriptors to fill the remaining payload */ tmp_pay_len = pay_len - TSO_MAX_BUFF_SIZE; - stmmac_tso_allocator(priv, des, tmp_pay_len, (nfrags == 0)); + stmmac_tso_allocator(priv, des, tmp_pay_len, (nfrags == 0), queue); /* Prepare fragments */ for (i = 0; i < nfrags; i++) { @@ -2383,22 +2755,22 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) goto dma_map_err; stmmac_tso_allocator(priv, des, skb_frag_size(frag), - (i == nfrags - 1)); + (i == nfrags - 1), queue); - priv->tx_skbuff_dma[priv->cur_tx].buf = des; - priv->tx_skbuff_dma[priv->cur_tx].len = skb_frag_size(frag); - priv->tx_skbuff[priv->cur_tx] = NULL; - priv->tx_skbuff_dma[priv->cur_tx].map_as_page = true; + tx_q->tx_skbuff_dma[tx_q->cur_tx].buf = des; + tx_q->tx_skbuff_dma[tx_q->cur_tx].len = skb_frag_size(frag); + tx_q->tx_skbuff[tx_q->cur_tx] = NULL; + tx_q->tx_skbuff_dma[tx_q->cur_tx].map_as_page = true; } - priv->tx_skbuff_dma[priv->cur_tx].last_segment = true; + tx_q->tx_skbuff_dma[tx_q->cur_tx].last_segment = true; - priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE); + tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE); - if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { + if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) { netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", __func__); - netif_stop_queue(dev); + netif_tx_stop_queue(netdev_get_tx_queue(dev, queue)); } dev->stats.tx_bytes += skb->len; @@ -2430,7 +2802,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) priv->hw->desc->prepare_tso_tx_desc(first, 1, proto_hdr_len, pay_len, - 1, priv->tx_skbuff_dma[first_entry].last_segment, + 1, tx_q->tx_skbuff_dma[first_entry].last_segment, tcp_hdrlen(skb) / 4, (skb->len - proto_hdr_len)); /* If context desc is used to change MSS */ @@ -2445,20 +2817,20 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) if (netif_msg_pktdata(priv)) { pr_info("%s: curr=%d dirty=%d f=%d, e=%d, f_p=%p, nfrags %d\n", - __func__, priv->cur_tx, priv->dirty_tx, first_entry, - priv->cur_tx, first, nfrags); + __func__, tx_q->cur_tx, tx_q->dirty_tx, first_entry, + tx_q->cur_tx, first, nfrags); - priv->hw->desc->display_ring((void *)priv->dma_tx, DMA_TX_SIZE, + priv->hw->desc->display_ring((void *)tx_q->dma_tx, DMA_TX_SIZE, 0); pr_info(">>> frame to be transmitted: "); print_pkt(skb->data, skb_headlen(skb)); } - netdev_sent_queue(dev, skb->len); + netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len); - priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr, - STMMAC_CHAN0); + priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, tx_q->tx_tail_addr, + queue); return NETDEV_TX_OK; @@ -2482,21 +2854,25 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) struct stmmac_priv *priv = netdev_priv(dev); unsigned int nopaged_len = skb_headlen(skb); int i, csum_insertion = 0, is_jumbo = 0; + u32 queue = skb_get_queue_mapping(skb); int nfrags = skb_shinfo(skb)->nr_frags; unsigned int entry, first_entry; struct dma_desc *desc, *first; + struct stmmac_tx_queue *tx_q; unsigned int enh_desc; unsigned int des; + tx_q = &priv->tx_queue[queue]; + /* Manage oversized TCP frames for GMAC4 device */ if (skb_is_gso(skb) && priv->tso) { if (ip_hdr(skb)->protocol == IPPROTO_TCP) return stmmac_tso_xmit(skb, dev); } - if (unlikely(stmmac_tx_avail(priv) < nfrags + 1)) { - if (!netif_queue_stopped(dev)) { - netif_stop_queue(dev); + if (unlikely(stmmac_tx_avail(priv, queue) < nfrags + 1)) { + if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) { + netif_tx_stop_queue(netdev_get_tx_queue(dev, queue)); /* This is a hard error, log it. */ netdev_err(priv->dev, "%s: Tx Ring full when queue awake\n", @@ -2508,19 +2884,19 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (priv->tx_path_in_lpi_mode) stmmac_disable_eee_mode(priv); - entry = priv->cur_tx; + entry = tx_q->cur_tx; first_entry = entry; csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL); if (likely(priv->extend_desc)) - desc = (struct dma_desc *)(priv->dma_etx + entry); + desc = (struct dma_desc *)(tx_q->dma_etx + entry); else - desc = priv->dma_tx + entry; + desc = tx_q->dma_tx + entry; first = desc; - priv->tx_skbuff[first_entry] = skb; + tx_q->tx_skbuff[first_entry] = skb; enh_desc = priv->plat->enh_desc; /* To program the descriptors according to the size of the frame */ @@ -2529,7 +2905,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(is_jumbo) && likely(priv->synopsys_id < DWMAC_CORE_4_00)) { - entry = priv->hw->mode->jumbo_frm(priv, skb, csum_insertion); + entry = priv->hw->mode->jumbo_frm(tx_q, skb, csum_insertion); if (unlikely(entry < 0)) goto dma_map_err; } @@ -2542,26 +2918,26 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); if (likely(priv->extend_desc)) - desc = (struct dma_desc *)(priv->dma_etx + entry); + desc = (struct dma_desc *)(tx_q->dma_etx + entry); else - desc = priv->dma_tx + entry; + desc = tx_q->dma_tx + entry; des = skb_frag_dma_map(priv->device, frag, 0, len, DMA_TO_DEVICE); if (dma_mapping_error(priv->device, des)) goto dma_map_err; /* should reuse desc w/o issues */ - priv->tx_skbuff[entry] = NULL; + tx_q->tx_skbuff[entry] = NULL; - priv->tx_skbuff_dma[entry].buf = des; + tx_q->tx_skbuff_dma[entry].buf = des; if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) desc->des0 = cpu_to_le32(des); else desc->des2 = cpu_to_le32(des); - priv->tx_skbuff_dma[entry].map_as_page = true; - priv->tx_skbuff_dma[entry].len = len; - priv->tx_skbuff_dma[entry].last_segment = last_segment; + tx_q->tx_skbuff_dma[entry].map_as_page = true; + tx_q->tx_skbuff_dma[entry].len = len; + tx_q->tx_skbuff_dma[entry].last_segment = last_segment; /* Prepare the descriptor and set the own bit too */ priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion, @@ -2570,20 +2946,20 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); - priv->cur_tx = entry; + tx_q->cur_tx = entry; if (netif_msg_pktdata(priv)) { void *tx_head; netdev_dbg(priv->dev, "%s: curr=%d dirty=%d f=%d, e=%d, first=%p, nfrags=%d", - __func__, priv->cur_tx, priv->dirty_tx, first_entry, + __func__, tx_q->cur_tx, tx_q->dirty_tx, first_entry, entry, first, nfrags); if (priv->extend_desc) - tx_head = (void *)priv->dma_etx; + tx_head = (void *)tx_q->dma_etx; else - tx_head = (void *)priv->dma_tx; + tx_head = (void *)tx_q->dma_tx; priv->hw->desc->display_ring(tx_head, DMA_TX_SIZE, false); @@ -2591,10 +2967,10 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) print_pkt(skb->data, skb->len); } - if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { + if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) { netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", __func__); - netif_stop_queue(dev); + netif_tx_stop_queue(netdev_get_tx_queue(dev, queue)); } dev->stats.tx_bytes += skb->len; @@ -2629,14 +3005,14 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (dma_mapping_error(priv->device, des)) goto dma_map_err; - priv->tx_skbuff_dma[first_entry].buf = des; + tx_q->tx_skbuff_dma[first_entry].buf = des; if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) first->des0 = cpu_to_le32(des); else first->des2 = cpu_to_le32(des); - priv->tx_skbuff_dma[first_entry].len = nopaged_len; - priv->tx_skbuff_dma[first_entry].last_segment = last_segment; + tx_q->tx_skbuff_dma[first_entry].len = nopaged_len; + tx_q->tx_skbuff_dma[first_entry].last_segment = last_segment; if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en)) { @@ -2657,13 +3033,13 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) dma_wmb(); } - netdev_sent_queue(dev, skb->len); + netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len); if (priv->synopsys_id < DWMAC_CORE_4_00) priv->hw->dma->enable_dma_transmission(priv->ioaddr); else - priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr, - STMMAC_CHAN0); + priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, tx_q->tx_tail_addr, + queue); return NETDEV_TX_OK; @@ -2691,9 +3067,9 @@ static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb) } -static inline int stmmac_rx_threshold_count(struct stmmac_priv *priv) +static inline int stmmac_rx_threshold_count(struct stmmac_rx_queue *rx_q) { - if (priv->rx_zeroc_thresh < STMMAC_RX_THRESH) + if (rx_q->rx_zeroc_thresh < STMMAC_RX_THRESH) return 0; return 1; @@ -2702,30 +3078,32 @@ static inline int stmmac_rx_threshold_count(struct stmmac_priv *priv) /** * stmmac_rx_refill - refill used skb preallocated buffers * @priv: driver private structure + * @queue: RX queue index * Description : this is to reallocate the skb for the reception process * that is based on zero-copy. */ -static inline void stmmac_rx_refill(struct stmmac_priv *priv) +static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + int dirty = stmmac_rx_dirty(priv, queue); + unsigned int entry = rx_q->dirty_rx; int bfsize = priv->dma_buf_sz; - unsigned int entry = priv->dirty_rx; - int dirty = stmmac_rx_dirty(priv); while (dirty-- > 0) { struct dma_desc *p; if (priv->extend_desc) - p = (struct dma_desc *)(priv->dma_erx + entry); + p = (struct dma_desc *)(rx_q->dma_erx + entry); else - p = priv->dma_rx + entry; + p = rx_q->dma_rx + entry; - if (likely(priv->rx_skbuff[entry] == NULL)) { + if (!rx_q->rx_skbuff[entry]) { struct sk_buff *skb; skb = netdev_alloc_skb_ip_align(priv->dev, bfsize); if (unlikely(!skb)) { /* so for a while no zero-copy! */ - priv->rx_zeroc_thresh = STMMAC_RX_THRESH; + rx_q->rx_zeroc_thresh = STMMAC_RX_THRESH; if (unlikely(net_ratelimit())) dev_err(priv->device, "fail to alloc skb entry %d\n", @@ -2733,28 +3111,28 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv) break; } - priv->rx_skbuff[entry] = skb; - priv->rx_skbuff_dma[entry] = + rx_q->rx_skbuff[entry] = skb; + rx_q->rx_skbuff_dma[entry] = dma_map_single(priv->device, skb->data, bfsize, DMA_FROM_DEVICE); if (dma_mapping_error(priv->device, - priv->rx_skbuff_dma[entry])) { + rx_q->rx_skbuff_dma[entry])) { netdev_err(priv->dev, "Rx DMA map failed\n"); dev_kfree_skb(skb); break; } if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) { - p->des0 = cpu_to_le32(priv->rx_skbuff_dma[entry]); + p->des0 = cpu_to_le32(rx_q->rx_skbuff_dma[entry]); p->des1 = 0; } else { - p->des2 = cpu_to_le32(priv->rx_skbuff_dma[entry]); + p->des2 = cpu_to_le32(rx_q->rx_skbuff_dma[entry]); } if (priv->hw->mode->refill_desc3) - priv->hw->mode->refill_desc3(priv, p); + priv->hw->mode->refill_desc3(rx_q, p); - if (priv->rx_zeroc_thresh > 0) - priv->rx_zeroc_thresh--; + if (rx_q->rx_zeroc_thresh > 0) + rx_q->rx_zeroc_thresh--; netif_dbg(priv, rx_status, priv->dev, "refill entry #%d\n", entry); @@ -2770,7 +3148,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv) entry = STMMAC_GET_ENTRY(entry, DMA_RX_SIZE); } - priv->dirty_rx = entry; + rx_q->dirty_rx = entry; } /** @@ -2780,21 +3158,22 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv) * Description : this the function called by the napi poll method. * It gets all the frames inside the ring. */ -static int stmmac_rx(struct stmmac_priv *priv, int limit) +static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) { - unsigned int entry = priv->cur_rx; + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + unsigned int entry = rx_q->cur_rx; + int coe = priv->hw->rx_csum; unsigned int next_entry; unsigned int count = 0; - int coe = priv->hw->rx_csum; if (netif_msg_rx_status(priv)) { void *rx_head; netdev_dbg(priv->dev, "%s: descriptor ring:\n", __func__); if (priv->extend_desc) - rx_head = (void *)priv->dma_erx; + rx_head = (void *)rx_q->dma_erx; else - rx_head = (void *)priv->dma_rx; + rx_head = (void *)rx_q->dma_rx; priv->hw->desc->display_ring(rx_head, DMA_RX_SIZE, true); } @@ -2804,9 +3183,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) struct dma_desc *np; if (priv->extend_desc) - p = (struct dma_desc *)(priv->dma_erx + entry); + p = (struct dma_desc *)(rx_q->dma_erx + entry); else - p = priv->dma_rx + entry; + p = rx_q->dma_rx + entry; /* read the status of the incoming frame */ status = priv->hw->desc->rx_status(&priv->dev->stats, @@ -2817,20 +3196,20 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) count++; - priv->cur_rx = STMMAC_GET_ENTRY(priv->cur_rx, DMA_RX_SIZE); - next_entry = priv->cur_rx; + rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx, DMA_RX_SIZE); + next_entry = rx_q->cur_rx; if (priv->extend_desc) - np = (struct dma_desc *)(priv->dma_erx + next_entry); + np = (struct dma_desc *)(rx_q->dma_erx + next_entry); else - np = priv->dma_rx + next_entry; + np = rx_q->dma_rx + next_entry; prefetch(np); if ((priv->extend_desc) && (priv->hw->desc->rx_extended_status)) priv->hw->desc->rx_extended_status(&priv->dev->stats, &priv->xstats, - priv->dma_erx + + rx_q->dma_erx + entry); if (unlikely(status == discard_frame)) { priv->dev->stats.rx_errors++; @@ -2840,9 +3219,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) * them in stmmac_rx_refill() function so that * device can reuse it. */ - priv->rx_skbuff[entry] = NULL; + rx_q->rx_skbuff[entry] = NULL; dma_unmap_single(priv->device, - priv->rx_skbuff_dma[entry], + rx_q->rx_skbuff_dma[entry], priv->dma_buf_sz, DMA_FROM_DEVICE); } @@ -2890,7 +3269,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) */ if (unlikely(!priv->plat->has_gmac4 && ((frame_len < priv->rx_copybreak) || - stmmac_rx_threshold_count(priv)))) { + stmmac_rx_threshold_count(rx_q)))) { skb = netdev_alloc_skb_ip_align(priv->dev, frame_len); if (unlikely(!skb)) { @@ -2902,21 +3281,21 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) } dma_sync_single_for_cpu(priv->device, - priv->rx_skbuff_dma + rx_q->rx_skbuff_dma [entry], frame_len, DMA_FROM_DEVICE); skb_copy_to_linear_data(skb, - priv-> + rx_q-> rx_skbuff[entry]->data, frame_len); skb_put(skb, frame_len); dma_sync_single_for_device(priv->device, - priv->rx_skbuff_dma + rx_q->rx_skbuff_dma [entry], frame_len, DMA_FROM_DEVICE); } else { - skb = priv->rx_skbuff[entry]; + skb = rx_q->rx_skbuff[entry]; if (unlikely(!skb)) { netdev_err(priv->dev, "%s: Inconsistent Rx chain\n", @@ -2925,12 +3304,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) break; } prefetch(skb->data - NET_IP_ALIGN); - priv->rx_skbuff[entry] = NULL; - priv->rx_zeroc_thresh++; + rx_q->rx_skbuff[entry] = NULL; + rx_q->rx_zeroc_thresh++; skb_put(skb, frame_len); dma_unmap_single(priv->device, - priv->rx_skbuff_dma[entry], + rx_q->rx_skbuff_dma[entry], priv->dma_buf_sz, DMA_FROM_DEVICE); } @@ -2952,7 +3331,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) else skb->ip_summed = CHECKSUM_UNNECESSARY; - napi_gro_receive(&priv->napi, skb); + napi_gro_receive(&rx_q->napi, skb); priv->dev->stats.rx_packets++; priv->dev->stats.rx_bytes += frame_len; @@ -2960,7 +3339,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) entry = next_entry; } - stmmac_rx_refill(priv); + stmmac_rx_refill(priv, queue); priv->xstats.rx_pkt_n += count; @@ -2977,14 +3356,22 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) */ static int stmmac_poll(struct napi_struct *napi, int budget) { - struct stmmac_priv *priv = container_of(napi, struct stmmac_priv, napi); - int work_done = 0; - u32 chan = STMMAC_CHAN0; + struct stmmac_rx_queue *rx_q = + container_of(napi, struct stmmac_rx_queue, napi); + struct stmmac_priv *priv = rx_q->priv_data; + u32 tx_count = priv->dma_cap.number_tx_queues; + u32 chan = rx_q->queue_index; + u32 work_done = 0; + u32 queue = 0; priv->xstats.napi_poll++; - stmmac_tx_clean(priv); + /* check all the queues */ + for (queue = 0; queue < tx_count; queue++) + stmmac_tx_clean(priv, queue); + + /* Process RX packets from this queue */ + work_done = stmmac_rx(priv, budget, rx_q->queue_index); - work_done = stmmac_rx(priv, budget); if (work_done < budget) { napi_complete_done(napi, work_done); stmmac_enable_dma_irq(priv, chan); @@ -3003,10 +3390,12 @@ static int stmmac_poll(struct napi_struct *napi, int budget) static void stmmac_tx_timeout(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); - u32 chan = STMMAC_CHAN0; + u32 tx_count = priv->plat->tx_queues_to_use; + u32 chan; /* Clear Tx resources and restart transmitting again */ - stmmac_tx_err(priv, chan); + for (chan = 0; chan < tx_count; chan++) + stmmac_tx_err(priv, chan); } /** @@ -3145,6 +3534,9 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) if (priv->synopsys_id >= DWMAC_CORE_4_00) { for (queue = 0; queue < queues_count; queue++) { + struct stmmac_rx_queue *rx_q = + &priv->rx_queue[queue]; + status |= priv->hw->mac->host_mtl_irq_status(priv->hw, queue); @@ -3152,7 +3544,7 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) if (status & CORE_IRQ_MTL_RX_OVERFLOW && priv->hw->dma->set_rx_tail_ptr) priv->hw->dma->set_rx_tail_ptr(priv->ioaddr, - priv->rx_tail_addr, + rx_q->rx_tail_addr, queue); } } @@ -3252,17 +3644,40 @@ static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v) { struct net_device *dev = seq->private; struct stmmac_priv *priv = netdev_priv(dev); + u32 rx_count = priv->plat->rx_queues_to_use; + u32 tx_count = priv->plat->tx_queues_to_use; + u32 queue; - if (priv->extend_desc) { - seq_printf(seq, "Extended RX descriptor ring:\n"); - sysfs_display_ring((void *)priv->dma_erx, DMA_RX_SIZE, 1, seq); - seq_printf(seq, "Extended TX descriptor ring:\n"); - sysfs_display_ring((void *)priv->dma_etx, DMA_TX_SIZE, 1, seq); - } else { - seq_printf(seq, "RX descriptor ring:\n"); - sysfs_display_ring((void *)priv->dma_rx, DMA_RX_SIZE, 0, seq); - seq_printf(seq, "TX descriptor ring:\n"); - sysfs_display_ring((void *)priv->dma_tx, DMA_TX_SIZE, 0, seq); + for (queue = 0; queue < rx_count; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + seq_printf(seq, "RX Queue %d:\n", queue); + + if (priv->extend_desc) { + seq_printf(seq, "Extended descriptor ring:\n"); + sysfs_display_ring((void *)rx_q->dma_erx, + DMA_RX_SIZE, 1, seq); + } else { + seq_printf(seq, "Descriptor ring:\n"); + sysfs_display_ring((void *)rx_q->dma_rx, + DMA_RX_SIZE, 0, seq); + } + } + + for (queue = 0; queue < tx_count; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + + seq_printf(seq, "TX Queue %d:\n", queue); + + if (priv->extend_desc) { + seq_printf(seq, "Extended descriptor ring:\n"); + sysfs_display_ring((void *)tx_q->dma_etx, + DMA_TX_SIZE, 1, seq); + } else { + seq_printf(seq, "Descriptor ring:\n"); + sysfs_display_ring((void *)tx_q->dma_tx, + DMA_TX_SIZE, 0, seq); + } } return 0; @@ -3545,11 +3960,14 @@ int stmmac_dvr_probe(struct device *device, struct plat_stmmacenet_data *plat_dat, struct stmmac_resources *res) { - int ret = 0; struct net_device *ndev = NULL; struct stmmac_priv *priv; + int ret = 0; + u32 queue; - ndev = alloc_etherdev(sizeof(struct stmmac_priv)); + ndev = alloc_etherdev_mqs(sizeof(struct stmmac_priv), + MTL_MAX_TX_QUEUES, + MTL_MAX_RX_QUEUES); if (!ndev) return -ENOMEM; @@ -3591,6 +4009,12 @@ int stmmac_dvr_probe(struct device *device, if (ret) goto error_hw_init; + /* Configure real RX and TX queues */ + ndev->real_num_rx_queues = priv->plat->rx_queues_to_use; + ndev->real_num_tx_queues = priv->plat->tx_queues_to_use; + + priv->dma_buf_sz = STMMAC_ALIGN(buf_sz); + ndev->netdev_ops = &stmmac_netdev_ops; ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | @@ -3640,7 +4064,26 @@ int stmmac_dvr_probe(struct device *device, "Enable RX Mitigation via HW Watchdog Timer\n"); } - netif_napi_add(ndev, &priv->napi, stmmac_poll, 64); + ret = alloc_dma_desc_resources(priv); + if (ret < 0) { + netdev_err(priv->dev, "%s: DMA descriptors allocation failed\n", + __func__); + goto init_dma_error; + } + + ret = init_dma_desc_rings(priv->dev, GFP_KERNEL); + if (ret < 0) { + netdev_err(priv->dev, "%s: DMA descriptors initialization failed\n", + __func__); + goto init_dma_error; + } + + for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + netif_napi_add(ndev, &rx_q->napi, stmmac_poll, + (64 * priv->plat->rx_queues_to_use)); + } spin_lock_init(&priv->lock); @@ -3685,7 +4128,13 @@ error_netdev_register: priv->hw->pcs != STMMAC_PCS_RTBI) stmmac_mdio_unregister(ndev); error_mdio_register: - netif_napi_del(&priv->napi); + for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + netif_napi_del(&rx_q->napi); + } +init_dma_error: + free_dma_desc_resources(priv); error_hw_init: free_netdev(ndev); @@ -3747,9 +4196,9 @@ int stmmac_suspend(struct device *dev) spin_lock_irqsave(&priv->lock, flags); netif_device_detach(ndev); - netif_stop_queue(ndev); + stmmac_stop_all_queues(priv); - napi_disable(&priv->napi); + stmmac_disable_all_queues(priv); /* Stop TX/RX DMA */ stmmac_stop_all_dma(priv); @@ -3774,6 +4223,31 @@ int stmmac_suspend(struct device *dev) } EXPORT_SYMBOL_GPL(stmmac_suspend); +/** + * stmmac_reset_queues_param - reset queue parameters + * @dev: device pointer + */ +static void stmmac_reset_queues_param(struct stmmac_priv *priv) +{ + u32 rx_cnt = priv->plat->rx_queues_to_use; + u32 tx_cnt = priv->plat->tx_queues_to_use; + u32 queue; + + for (queue = 0; queue < rx_cnt; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + rx_q->cur_rx = 0; + rx_q->dirty_rx = 0; + } + + for (queue = 0; queue < tx_cnt; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + + tx_q->cur_tx = 0; + tx_q->dirty_tx = 0; + } +} + /** * stmmac_resume - resume callback * @dev: device pointer @@ -3814,10 +4288,8 @@ int stmmac_resume(struct device *dev) spin_lock_irqsave(&priv->lock, flags); - priv->cur_rx = 0; - priv->dirty_rx = 0; - priv->dirty_tx = 0; - priv->cur_tx = 0; + stmmac_reset_queues_param(priv); + /* reset private mss value to force mss context settings at * next tso xmit (only used for gmac4). */ @@ -3829,9 +4301,9 @@ int stmmac_resume(struct device *dev) stmmac_init_tx_coalesce(priv); stmmac_set_rx_mode(ndev); - napi_enable(&priv->napi); + stmmac_enable_all_queues(priv); - netif_start_queue(ndev); + stmmac_start_all_queues(priv); spin_unlock_irqrestore(&priv->lock, flags); -- cgit From a8f5102af2a7740a4b3200a27beddf27f23f921a Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Fri, 17 Mar 2017 16:11:06 +0000 Subject: net: stmmac: TX and RX queue priority configuration This patch adds the configuration of RX and TX queues' priority. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 5 +++ drivers/net/ethernet/stmicro/stmmac/common.h | 4 ++ drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 13 ++++++ drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 37 +++++++++++++++++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 48 ++++++++++++++++++++++ drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c | 4 ++ .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 16 ++++++++ include/linux/stmmac.h | 4 ++ 8 files changed, 131 insertions(+) diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index a7b0e41cb264..d11bd09f4ba6 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -83,6 +83,7 @@ Optional properties: - snps,dcb-algorithm: Queue to be enabled as DCB - snps,avb-algorithm: Queue to be enabled as AVB - snps,map-to-dma-channel: Channel to map + - snps,priority: RX queue priority (Range: 0x0 to 0xF) - Multiple TX Queues parameters: below the list of all the parameters to configure the multiple TX queues: - snps,tx-queues-to-use: number of TX queues to be used in the driver @@ -101,6 +102,7 @@ Optional properties: - snps,idle_slope: unlock on WoL - snps,high_credit: max write outstanding req. limit - snps,low_credit: max read outstanding req. limit + - snps,priority: TX queue priority (Range: 0x0 to 0xF) Examples: stmmac_axi_setup: stmmac-axi-config { @@ -115,6 +117,7 @@ Examples: queue0 { snps,dcb-algorithm; snps,map-to-dma-channel = <0x0>; + snps,priority = <0x0>; }; }; @@ -124,6 +127,7 @@ Examples: queue0 { snps,weight = <0x10>; snps,dcb-algorithm; + snps,priority = <0x0>; }; queue1 { @@ -132,6 +136,7 @@ Examples: snps,idle_slope = <0x1000>; snps,high_credit = <0x3E800>; snps,low_credit = <0xFFC18000>; + snps,priority = <0x1>; }; }; diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index badc4414d67b..e0b31e759c0e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -469,6 +469,10 @@ struct stmmac_ops { int (*rx_ipc)(struct mac_device_info *hw); /* Enable RX Queues */ void (*rx_queue_enable)(struct mac_device_info *hw, u8 mode, u32 queue); + /* RX Queues Priority */ + void (*rx_queue_prio)(struct mac_device_info *hw, u32 prio, u32 queue); + /* TX Queues Priority */ + void (*tx_queue_prio)(struct mac_device_info *hw, u32 prio, u32 queue); /* Program RX Algorithms */ void (*prog_mtl_rx_algorithms)(struct mac_device_info *hw, u32 rx_alg); /* Program TX Algorithms */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 54bcdb4d10db..a6c382d22ebf 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -22,7 +22,12 @@ #define GMAC_HASH_TAB_32_63 0x00000014 #define GMAC_RX_FLOW_CTRL 0x00000090 #define GMAC_QX_TX_FLOW_CTRL(x) (0x70 + x * 4) +#define GMAC_TXQ_PRTY_MAP0 0x98 +#define GMAC_TXQ_PRTY_MAP1 0x9C #define GMAC_RXQ_CTRL0 0x000000a0 +#define GMAC_RXQ_CTRL1 0x000000a4 +#define GMAC_RXQ_CTRL2 0x000000a8 +#define GMAC_RXQ_CTRL3 0x000000ac #define GMAC_INT_STATUS 0x000000b0 #define GMAC_INT_EN 0x000000b4 #define GMAC_1US_TIC_COUNTER 0x000000dc @@ -54,6 +59,14 @@ /* MAC Flow Control RX */ #define GMAC_RX_FLOW_CTRL_RFE BIT(0) +/* RX Queues Priorities */ +#define GMAC_RXQCTRL_PSRQX_MASK(x) GENMASK(7 + ((x) * 8), 0 + ((x) * 8)) +#define GMAC_RXQCTRL_PSRQX_SHIFT(x) ((x) * 8) + +/* TX Queues Priorities */ +#define GMAC_TXQCTRL_PSTQX_MASK(x) GENMASK(7 + ((x) * 8), 0 + ((x) * 8)) +#define GMAC_TXQCTRL_PSTQX_SHIFT(x) ((x) * 8) + /* MAC Flow Control TX */ #define GMAC_TX_FLOW_CTRL_TFE BIT(1) #define GMAC_TX_FLOW_CTRL_PT_SHIFT 16 diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 10599dbc232f..342f62abb9ca 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -74,6 +74,41 @@ static void dwmac4_rx_queue_enable(struct mac_device_info *hw, writel(value, ioaddr + GMAC_RXQ_CTRL0); } +static void dwmac4_rx_queue_priority(struct mac_device_info *hw, + u32 prio, u32 queue) +{ + void __iomem *ioaddr = hw->pcsr; + u32 base_register; + u32 value; + + base_register = (queue < 4) ? GMAC_RXQ_CTRL2 : GMAC_RXQ_CTRL3; + + value = readl(ioaddr + base_register); + + value &= ~GMAC_RXQCTRL_PSRQX_MASK(queue); + value |= (prio << GMAC_RXQCTRL_PSRQX_SHIFT(queue)) & + GMAC_RXQCTRL_PSRQX_MASK(queue); + writel(value, ioaddr + base_register); +} + +static void dwmac4_tx_queue_priority(struct mac_device_info *hw, + u32 prio, u32 queue) +{ + void __iomem *ioaddr = hw->pcsr; + u32 base_register; + u32 value; + + base_register = (queue < 4) ? GMAC_TXQ_PRTY_MAP0 : GMAC_TXQ_PRTY_MAP1; + + value = readl(ioaddr + base_register); + + value &= ~GMAC_TXQCTRL_PSTQX_MASK(queue); + value |= (prio << GMAC_TXQCTRL_PSTQX_SHIFT(queue)) & + GMAC_TXQCTRL_PSTQX_MASK(queue); + + writel(value, ioaddr + base_register); +} + static void dwmac4_prog_mtl_rx_algorithms(struct mac_device_info *hw, u32 rx_alg) { @@ -603,6 +638,8 @@ static const struct stmmac_ops dwmac4_ops = { .core_init = dwmac4_core_init, .rx_ipc = dwmac4_rx_ipc_enable, .rx_queue_enable = dwmac4_rx_queue_enable, + .rx_queue_prio = dwmac4_rx_queue_priority, + .tx_queue_prio = dwmac4_tx_queue_priority, .prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms, .prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms, .set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index a389dfbe630c..0f2c0d762e33 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2292,6 +2292,46 @@ static void stmmac_rx_queue_dma_chan_map(struct stmmac_priv *priv) } } +/** + * stmmac_mac_config_rx_queues_prio - Configure RX Queue priority + * @priv: driver private structure + * Description: It is used for configuring the RX Queue Priority + */ +static void stmmac_mac_config_rx_queues_prio(struct stmmac_priv *priv) +{ + u32 rx_queues_count = priv->plat->rx_queues_to_use; + u32 queue; + u32 prio; + + for (queue = 0; queue < rx_queues_count; queue++) { + if (!priv->plat->rx_queues_cfg[queue].use_prio) + continue; + + prio = priv->plat->rx_queues_cfg[queue].prio; + priv->hw->mac->rx_queue_prio(priv->hw, prio, queue); + } +} + +/** + * stmmac_mac_config_tx_queues_prio - Configure TX Queue priority + * @priv: driver private structure + * Description: It is used for configuring the TX Queue Priority + */ +static void stmmac_mac_config_tx_queues_prio(struct stmmac_priv *priv) +{ + u32 tx_queues_count = priv->plat->tx_queues_to_use; + u32 queue; + u32 prio; + + for (queue = 0; queue < tx_queues_count; queue++) { + if (!priv->plat->tx_queues_cfg[queue].use_prio) + continue; + + prio = priv->plat->tx_queues_cfg[queue].prio; + priv->hw->mac->tx_queue_prio(priv->hw, prio, queue); + } +} + /** * stmmac_mtl_configuration - Configure MTL * @priv: driver private structure @@ -2329,6 +2369,14 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv) /* Set the HW DMA mode and the COE */ stmmac_dma_operation_mode(priv); + + /* Set RX priorities */ + if (rx_queues_count > 1 && priv->hw->mac->rx_queue_prio) + stmmac_mac_config_rx_queues_prio(priv); + + /* Set TX priorities */ + if (tx_queues_count > 1 && priv->hw->mac->tx_queue_prio) + stmmac_mac_config_tx_queues_prio(priv); } /** diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c index cea472a7c335..ffe4fac22d3d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c @@ -92,6 +92,10 @@ static void stmmac_default_data(struct plat_stmmacenet_data *plat) /* Set default number of RX and TX queues to use */ plat->tx_queues_to_use = 1; plat->rx_queues_to_use = 1; + + /* Disable Priority config by default */ + plat->tx_queues_cfg[0].use_prio = false; + plat->rx_queues_cfg[0].use_prio = false; } static int quark_default_data(struct plat_stmmacenet_data *plat, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 37f550ae76a5..77b0468dd79f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -182,6 +182,14 @@ static void stmmac_mtl_setup(struct platform_device *pdev, plat->rx_queues_cfg[queue].chan = queue; /* TODO: Dynamic mapping to be included in the future */ + if (of_property_read_u32(q_node, "snps,priority", + &plat->rx_queues_cfg[queue].prio)) { + plat->rx_queues_cfg[queue].prio = 0; + plat->rx_queues_cfg[queue].use_prio = false; + } else { + plat->rx_queues_cfg[queue].use_prio = true; + } + queue++; } @@ -235,6 +243,14 @@ static void stmmac_mtl_setup(struct platform_device *pdev, plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB; } + if (of_property_read_u32(q_node, "snps,priority", + &plat->tx_queues_cfg[queue].prio)) { + plat->tx_queues_cfg[queue].prio = 0; + plat->tx_queues_cfg[queue].use_prio = false; + } else { + plat->tx_queues_cfg[queue].use_prio = true; + } + queue++; } diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index be47b859e954..b7d5e7ae9591 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -127,6 +127,8 @@ struct stmmac_axi { struct stmmac_rxq_cfg { u8 mode_to_use; u8 chan; + bool use_prio; + u32 prio; }; struct stmmac_txq_cfg { @@ -137,6 +139,8 @@ struct stmmac_txq_cfg { u32 idle_slope; u32 high_credit; u32 low_credit; + bool use_prio; + u32 prio; }; struct plat_stmmacenet_data { -- cgit From abe80fdc6ee664b2f8515f91b45e852b65dbb1a1 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Fri, 17 Mar 2017 16:11:07 +0000 Subject: net: stmmac: RX queue routing configuration This patch adds the configuration of RX queues' routing. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 6 ++++ drivers/net/ethernet/stmicro/stmmac/common.h | 17 +++++++++++ drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 16 ++++++++++ drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 34 ++++++++++++++++++++++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 25 ++++++++++++++++ drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c | 3 ++ .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 14 +++++++++ include/linux/stmmac.h | 1 + 8 files changed, 116 insertions(+) diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index d11bd09f4ba6..784d98862b52 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -83,6 +83,12 @@ Optional properties: - snps,dcb-algorithm: Queue to be enabled as DCB - snps,avb-algorithm: Queue to be enabled as AVB - snps,map-to-dma-channel: Channel to map + - Specifiy specific packet routing: + - snps,route-avcp: AV Untagged Control packets + - snps,route-ptp: PTP Packets + - snps,route-dcbcp: DCB Control Packets + - snps,route-up: Untagged Packets + - snps,route-multi-broad: Multicast & Broadcast Packets - snps,priority: RX queue priority (Range: 0x0 to 0xF) - Multiple TX Queues parameters: below the list of all the parameters to configure the multiple TX queues: diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index e0b31e759c0e..572cf8b61707 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -246,6 +246,15 @@ struct stmmac_extra_stats { #define STMMAC_TX_MAX_FRAMES 256 #define STMMAC_TX_FRAMES 64 +/* Packets types */ +enum packets_types { + PACKET_AVCPQ = 0x1, /* AV Untagged Control packets */ + PACKET_PTPQ = 0x2, /* PTP Packets */ + PACKET_DCBCPQ = 0x3, /* DCB Control Packets */ + PACKET_UPQ = 0x4, /* Untagged Packets */ + PACKET_MCBCQ = 0x5, /* Multicast & Broadcast Packets */ +}; + /* Rx IPC status */ enum rx_frame_status { good_frame = 0x0, @@ -473,6 +482,9 @@ struct stmmac_ops { void (*rx_queue_prio)(struct mac_device_info *hw, u32 prio, u32 queue); /* TX Queues Priority */ void (*tx_queue_prio)(struct mac_device_info *hw, u32 prio, u32 queue); + /* RX Queues Routing */ + void (*rx_queue_routing)(struct mac_device_info *hw, u8 packet, + u32 queue); /* Program RX Algorithms */ void (*prog_mtl_rx_algorithms)(struct mac_device_info *hw, u32 rx_alg); /* Program TX Algorithms */ @@ -581,6 +593,11 @@ struct mac_device_info { unsigned int ps; }; +struct stmmac_rx_routing { + u32 reg_mask; + u32 reg_shift; +}; + struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins, int perfect_uc_entries, int *synopsys_id); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index a6c382d22ebf..d74cedf2a397 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -44,6 +44,22 @@ #define GMAC_ADDR_HIGH(reg) (0x300 + reg * 8) #define GMAC_ADDR_LOW(reg) (0x304 + reg * 8) +/* RX Queues Routing */ +#define GMAC_RXQCTRL_AVCPQ_MASK GENMASK(2, 0) +#define GMAC_RXQCTRL_AVCPQ_SHIFT 0 +#define GMAC_RXQCTRL_PTPQ_MASK GENMASK(6, 4) +#define GMAC_RXQCTRL_PTPQ_SHIFT 4 +#define GMAC_RXQCTRL_DCBCPQ_MASK GENMASK(10, 8) +#define GMAC_RXQCTRL_DCBCPQ_SHIFT 8 +#define GMAC_RXQCTRL_UPQ_MASK GENMASK(14, 12) +#define GMAC_RXQCTRL_UPQ_SHIFT 12 +#define GMAC_RXQCTRL_MCBCQ_MASK GENMASK(18, 16) +#define GMAC_RXQCTRL_MCBCQ_SHIFT 16 +#define GMAC_RXQCTRL_MCBCQEN BIT(20) +#define GMAC_RXQCTRL_MCBCQEN_SHIFT 20 +#define GMAC_RXQCTRL_TACPQE BIT(21) +#define GMAC_RXQCTRL_TACPQE_SHIFT 21 + /* MAC Packet Filtering */ #define GMAC_PACKET_FILTER_PR BIT(0) #define GMAC_PACKET_FILTER_HMC BIT(2) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 342f62abb9ca..40ce20218402 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -109,6 +109,39 @@ static void dwmac4_tx_queue_priority(struct mac_device_info *hw, writel(value, ioaddr + base_register); } +static void dwmac4_tx_queue_routing(struct mac_device_info *hw, + u8 packet, u32 queue) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value; + + const struct stmmac_rx_routing route_possibilities[] = { + { GMAC_RXQCTRL_AVCPQ_MASK, GMAC_RXQCTRL_AVCPQ_SHIFT }, + { GMAC_RXQCTRL_PTPQ_MASK, GMAC_RXQCTRL_PTPQ_SHIFT }, + { GMAC_RXQCTRL_DCBCPQ_MASK, GMAC_RXQCTRL_DCBCPQ_SHIFT }, + { GMAC_RXQCTRL_UPQ_MASK, GMAC_RXQCTRL_UPQ_SHIFT }, + { GMAC_RXQCTRL_MCBCQ_MASK, GMAC_RXQCTRL_MCBCQ_SHIFT }, + }; + + value = readl(ioaddr + GMAC_RXQ_CTRL1); + + /* routing configuration */ + value &= ~route_possibilities[packet - 1].reg_mask; + value |= (queue << route_possibilities[packet-1].reg_shift) & + route_possibilities[packet - 1].reg_mask; + + /* some packets require extra ops */ + if (packet == PACKET_AVCPQ) { + value &= ~GMAC_RXQCTRL_TACPQE; + value |= 0x1 << GMAC_RXQCTRL_TACPQE_SHIFT; + } else if (packet == PACKET_MCBCQ) { + value &= ~GMAC_RXQCTRL_MCBCQEN; + value |= 0x1 << GMAC_RXQCTRL_MCBCQEN_SHIFT; + } + + writel(value, ioaddr + GMAC_RXQ_CTRL1); +} + static void dwmac4_prog_mtl_rx_algorithms(struct mac_device_info *hw, u32 rx_alg) { @@ -640,6 +673,7 @@ static const struct stmmac_ops dwmac4_ops = { .rx_queue_enable = dwmac4_rx_queue_enable, .rx_queue_prio = dwmac4_rx_queue_priority, .tx_queue_prio = dwmac4_tx_queue_priority, + .rx_queue_routing = dwmac4_tx_queue_routing, .prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms, .prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms, .set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 0f2c0d762e33..531bf1dc35cd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2332,6 +2332,27 @@ static void stmmac_mac_config_tx_queues_prio(struct stmmac_priv *priv) } } +/** + * stmmac_mac_config_rx_queues_routing - Configure RX Queue Routing + * @priv: driver private structure + * Description: It is used for configuring the RX queue routing + */ +static void stmmac_mac_config_rx_queues_routing(struct stmmac_priv *priv) +{ + u32 rx_queues_count = priv->plat->rx_queues_to_use; + u32 queue; + u8 packet; + + for (queue = 0; queue < rx_queues_count; queue++) { + /* no specific packet type routing specified for the queue */ + if (priv->plat->rx_queues_cfg[queue].pkt_route == 0x0) + continue; + + packet = priv->plat->rx_queues_cfg[queue].pkt_route; + priv->hw->mac->rx_queue_prio(priv->hw, packet, queue); + } +} + /** * stmmac_mtl_configuration - Configure MTL * @priv: driver private structure @@ -2377,6 +2398,10 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv) /* Set TX priorities */ if (tx_queues_count > 1 && priv->hw->mac->tx_queue_prio) stmmac_mac_config_tx_queues_prio(priv); + + /* Set RX routing */ + if (rx_queues_count > 1 && priv->hw->mac->rx_queue_routing) + stmmac_mac_config_rx_queues_routing(priv); } /** diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c index ffe4fac22d3d..a224d7bf1c1b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c @@ -96,6 +96,9 @@ static void stmmac_default_data(struct plat_stmmacenet_data *plat) /* Disable Priority config by default */ plat->tx_queues_cfg[0].use_prio = false; plat->rx_queues_cfg[0].use_prio = false; + + /* Disable RX queues routing by default */ + plat->rx_queues_cfg[0].pkt_route = 0x0; } static int quark_default_data(struct plat_stmmacenet_data *plat, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 77b0468dd79f..f5c8b1bca002 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -190,6 +190,20 @@ static void stmmac_mtl_setup(struct platform_device *pdev, plat->rx_queues_cfg[queue].use_prio = true; } + /* RX queue specific packet type routing */ + if (of_property_read_bool(q_node, "snps,route-avcp")) + plat->rx_queues_cfg[queue].pkt_route = PACKET_AVCPQ; + else if (of_property_read_bool(q_node, "snps,route-ptp")) + plat->rx_queues_cfg[queue].pkt_route = PACKET_PTPQ; + else if (of_property_read_bool(q_node, "snps,route-dcbcp")) + plat->rx_queues_cfg[queue].pkt_route = PACKET_DCBCPQ; + else if (of_property_read_bool(q_node, "snps,route-up")) + plat->rx_queues_cfg[queue].pkt_route = PACKET_UPQ; + else if (of_property_read_bool(q_node, "snps,route-multi-broad")) + plat->rx_queues_cfg[queue].pkt_route = PACKET_MCBCQ; + else + plat->rx_queues_cfg[queue].pkt_route = 0x0; + queue++; } diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index b7d5e7ae9591..cd98ee232ad1 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -127,6 +127,7 @@ struct stmmac_axi { struct stmmac_rxq_cfg { u8 mode_to_use; u8 chan; + u8 pkt_route; bool use_prio; u32 prio; }; -- cgit From 1f697ab109d60392f14c5e79eb6caf96805e68e9 Mon Sep 17 00:00:00 2001 From: Satanand Burla Date: Fri, 17 Mar 2017 10:50:05 -0700 Subject: liquidio: remove duplicate code Remove code duplicated in PF and VF; define that code once only in a common header file included by PF and VF. Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 36 ---------------------- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 36 ---------------------- .../net/ethernet/cavium/liquidio/octeon_network.h | 36 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 72 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 761061b96948..b2d6478ffbd9 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -60,12 +60,6 @@ MODULE_PARM_DESC(fw_type, "Type of firmware to be loaded. Default \"nic\""); static int ptp_enable = 1; -/* Bit mask values for lio->ifstate */ -#define LIO_IFSTATE_DROQ_OPS 0x01 -#define LIO_IFSTATE_REGISTERED 0x02 -#define LIO_IFSTATE_RUNNING 0x04 -#define LIO_IFSTATE_RX_TIMESTAMP_ENABLED 0x08 - /* Polling interval for determining when NIC application is alive */ #define LIQUIDIO_STARTER_POLL_INTERVAL_MS 100 @@ -530,36 +524,6 @@ static void liquidio_deinit_pci(void) pci_unregister_driver(&liquidio_pci_driver); } -/** - * \brief check interface state - * @param lio per-network private data - * @param state_flag flag state to check - */ -static inline int ifstate_check(struct lio *lio, int state_flag) -{ - return atomic_read(&lio->ifstate) & state_flag; -} - -/** - * \brief set interface state - * @param lio per-network private data - * @param state_flag flag state to set - */ -static inline void ifstate_set(struct lio *lio, int state_flag) -{ - atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) | state_flag)); -} - -/** - * \brief clear interface state - * @param lio per-network private data - * @param state_flag flag state to clear - */ -static inline void ifstate_reset(struct lio *lio, int state_flag) -{ - atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) & ~(state_flag))); -} - /** * \brief Stop Tx queues * @param netdev network device diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 5ec5c24cee5d..f72db33fcd3a 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -39,12 +39,6 @@ MODULE_PARM_DESC(debug, "NETIF_MSG debug bits"); #define DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) -/* Bit mask values for lio->ifstate */ -#define LIO_IFSTATE_DROQ_OPS 0x01 -#define LIO_IFSTATE_REGISTERED 0x02 -#define LIO_IFSTATE_RUNNING 0x04 -#define LIO_IFSTATE_RX_TIMESTAMP_ENABLED 0x08 - struct liquidio_if_cfg_context { int octeon_id; @@ -335,36 +329,6 @@ static struct pci_driver liquidio_vf_pci_driver = { .err_handler = &liquidio_vf_err_handler, /* For AER */ }; -/** - * \brief check interface state - * @param lio per-network private data - * @param state_flag flag state to check - */ -static int ifstate_check(struct lio *lio, int state_flag) -{ - return atomic_read(&lio->ifstate) & state_flag; -} - -/** - * \brief set interface state - * @param lio per-network private data - * @param state_flag flag state to set - */ -static void ifstate_set(struct lio *lio, int state_flag) -{ - atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) | state_flag)); -} - -/** - * \brief clear interface state - * @param lio per-network private data - * @param state_flag flag state to clear - */ -static void ifstate_reset(struct lio *lio, int state_flag) -{ - atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) & ~(state_flag))); -} - /** * \brief Stop Tx queues * @param netdev network device diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index eef2a1e8a7e3..ddb61bf25775 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -28,6 +28,12 @@ #define LIO_MAX_MTU_SIZE (OCTNET_MAX_FRM_SIZE - OCTNET_FRM_HEADER_SIZE) #define LIO_MIN_MTU_SIZE ETH_MIN_MTU +/* Bit mask values for lio->ifstate */ +#define LIO_IFSTATE_DROQ_OPS 0x01 +#define LIO_IFSTATE_REGISTERED 0x02 +#define LIO_IFSTATE_RUNNING 0x04 +#define LIO_IFSTATE_RX_TIMESTAMP_ENABLED 0x08 + struct oct_nic_stats_resp { u64 rh; struct oct_link_stats stats; @@ -438,4 +444,34 @@ static inline void octeon_fast_packet_next(struct octeon_droq *droq, get_rbd(droq->recv_buf_list[idx].buffer), copy_len); } +/** + * \brief check interface state + * @param lio per-network private data + * @param state_flag flag state to check + */ +static inline int ifstate_check(struct lio *lio, int state_flag) +{ + return atomic_read(&lio->ifstate) & state_flag; +} + +/** + * \brief set interface state + * @param lio per-network private data + * @param state_flag flag state to set + */ +static inline void ifstate_set(struct lio *lio, int state_flag) +{ + atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) | state_flag)); +} + +/** + * \brief clear interface state + * @param lio per-network private data + * @param state_flag flag state to clear + */ +static inline void ifstate_reset(struct lio *lio, int state_flag) +{ + atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) & ~(state_flag))); +} + #endif -- cgit From c5b71e633da381c299990341ec88694bd00ecad1 Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Fri, 17 Mar 2017 11:23:08 -0700 Subject: liquidio: add debug error messages to report command timeout Add timeout error message in lio_process_ordered_list(). Add host failure status in existing error message in if_cfg_callback(). Signed-off-by: Rick Farrington Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 4 ++-- drivers/net/ethernet/cavium/liquidio/response_manager.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index b2d6478ffbd9..afa173de1042 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -2225,8 +2225,8 @@ static void if_cfg_callback(struct octeon_device *oct, oct = lio_get_device(ctx->octeon_id); if (resp->status) - dev_err(&oct->pci_dev->dev, "nic if cfg instruction failed. Status: %llx\n", - CVM_CAST64(resp->status)); + dev_err(&oct->pci_dev->dev, "nic if cfg instruction failed. Status: 0x%llx (0x%08x)\n", + CVM_CAST64(resp->status), status); WRITE_ONCE(ctx->cond, 1); snprintf(oct->fw_info.liquidio_firmware_version, 32, "%s", diff --git a/drivers/net/ethernet/cavium/liquidio/response_manager.c b/drivers/net/ethernet/cavium/liquidio/response_manager.c index 1d987d7cd7bd..945633339723 100644 --- a/drivers/net/ethernet/cavium/liquidio/response_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/response_manager.c @@ -107,6 +107,8 @@ int lio_process_ordered_list(struct octeon_device *octeon_dev, } } else if (force_quit || (sc->timeout && time_after(jiffies, (unsigned long)sc->timeout))) { + dev_err(&octeon_dev->pci_dev->dev, "%s: cmd failed, timeout (%ld, %ld)\n", + __func__, (long)jiffies, (long)sc->timeout); status = OCTEON_REQUEST_TIMEOUT; } -- cgit From e74bad6b015b732d8269a8ddfb303c9b33092722 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Fri, 17 Mar 2017 22:54:04 +0100 Subject: net: sun: sungem: rix a possible null dereference The function gem_begin_auto_negotiation dereference the pointer ep before testing if it's null. This patch add a check on ep before dereferencing it. Fixes: 92552fdda557 ("net: sun: sungem: use new api ethtool_{get|set}_link_ksettings") Reported-by: Dan Carpenter Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sungem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c index dbfca0466760..fa607d062cb3 100644 --- a/drivers/net/ethernet/sun/sungem.c +++ b/drivers/net/ethernet/sun/sungem.c @@ -1259,8 +1259,9 @@ static void gem_begin_auto_negotiation(struct gem *gp, int duplex; u32 advertising; - ethtool_convert_link_mode_to_legacy_u32(&advertising, - ep->link_modes.advertising); + if (ep) + ethtool_convert_link_mode_to_legacy_u32( + &advertising, ep->link_modes.advertising); if (gp->phy_type != phy_mii_mdio0 && gp->phy_type != phy_mii_mdio1) -- cgit From bd0b6723139416fd22acd5849b93edbd32d561f3 Mon Sep 17 00:00:00 2001 From: John Allen Date: Fri, 17 Mar 2017 17:13:40 -0500 Subject: ibmvnic: Move login and queue negotiation into ibmvnic_open VNIC server expects LINK_STATE_UP to be sent within 30s of the login. If we exceed the timeout, VNIC server will attempt to fail over. Since time between probe and open of the device is indeterminate, move login and queue negotiation into ibmvnic open so we can guarantee that login and sending LINK_STATE_UP occur within the 30s window. Signed-off-by: John Allen Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 88 +++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 5f11b4dc95d2..61d9d4045b4c 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -110,6 +110,9 @@ static int ibmvnic_poll(struct napi_struct *napi, int data); static void send_map_query(struct ibmvnic_adapter *adapter); static void send_request_map(struct ibmvnic_adapter *, dma_addr_t, __be32, u8); static void send_request_unmap(struct ibmvnic_adapter *, u8); +static void send_login(struct ibmvnic_adapter *adapter); +static void send_cap_queries(struct ibmvnic_adapter *adapter); +static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter); struct ibmvnic_stat { char name[ETH_GSTRING_LEN]; @@ -371,14 +374,51 @@ static void free_rx_pool(struct ibmvnic_adapter *adapter, static int ibmvnic_open(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); + unsigned long timeout = msecs_to_jiffies(30000); struct device *dev = &adapter->vdev->dev; struct ibmvnic_tx_pool *tx_pool; union ibmvnic_crq crq; int rxadd_subcrqs; u64 *size_array; int tx_subcrqs; + int rc = 0; int i, j; + do { + if (adapter->renegotiate) { + adapter->renegotiate = false; + release_sub_crqs_no_irqs(adapter); + + reinit_completion(&adapter->init_done); + send_cap_queries(adapter); + if (!wait_for_completion_timeout(&adapter->init_done, + timeout)) { + dev_err(dev, "Capabilities query timeout\n"); + return -1; + } + } + + reinit_completion(&adapter->init_done); + send_login(adapter); + if (!wait_for_completion_timeout(&adapter->init_done, + timeout)) { + dev_err(dev, "Login timeout\n"); + return -1; + } + } while (adapter->renegotiate); + + rc = netif_set_real_num_tx_queues(netdev, adapter->req_tx_queues); + if (rc) { + dev_err(dev, "failed to set the number of tx queues\n"); + return -1; + } + + rc = init_sub_crq_irqs(adapter); + if (rc) { + dev_err(dev, "failed to initialize sub crq irqs\n"); + return -1; + } + rxadd_subcrqs = be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); tx_subcrqs = @@ -508,6 +548,7 @@ rx_pool_arr_alloc_failed: for (i = 0; i < adapter->req_rx_queues; i++) napi_disable(&adapter->napi[i]); alloc_napi_failed: + release_sub_crqs(adapter); return -ENOMEM; } @@ -3419,8 +3460,7 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq, dma_unmap_single(dev, adapter->ip_offload_ctrl_tok, sizeof(adapter->ip_offload_ctrl), DMA_TO_DEVICE); - /* We're done with the queries, perform the login */ - send_login(adapter); + complete(&adapter->init_done); break; case REQUEST_RAS_COMP_NUM_RSP: netdev_dbg(netdev, "Got Request RAS Comp Num Response\n"); @@ -3700,26 +3740,6 @@ static void handle_crq_init_rsp(struct work_struct *work) goto task_failed; } - do { - if (adapter->renegotiate) { - adapter->renegotiate = false; - release_sub_crqs_no_irqs(adapter); - - reinit_completion(&adapter->init_done); - send_cap_queries(adapter); - if (!wait_for_completion_timeout(&adapter->init_done, - timeout)) { - dev_err(dev, "Passive init timeout\n"); - goto task_failed; - } - } - } while (adapter->renegotiate); - rc = init_sub_crq_irqs(adapter); - - if (rc) - goto task_failed; - - netdev->real_num_tx_queues = adapter->req_tx_queues; netdev->mtu = adapter->req_mtu - ETH_HLEN; if (adapter->failover) { @@ -3840,39 +3860,17 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) if (!wait_for_completion_timeout(&adapter->init_done, timeout)) return 0; - do { - if (adapter->renegotiate) { - adapter->renegotiate = false; - release_sub_crqs_no_irqs(adapter); - - reinit_completion(&adapter->init_done); - send_cap_queries(adapter); - if (!wait_for_completion_timeout(&adapter->init_done, - timeout)) - return 0; - } - } while (adapter->renegotiate); - - rc = init_sub_crq_irqs(adapter); - if (rc) { - dev_err(&dev->dev, "failed to initialize sub crq irqs\n"); - goto free_debugfs; - } - - netdev->real_num_tx_queues = adapter->req_tx_queues; netdev->mtu = adapter->req_mtu - ETH_HLEN; rc = register_netdev(netdev); if (rc) { dev_err(&dev->dev, "failed to register netdev rc=%d\n", rc); - goto free_sub_crqs; + goto free_debugfs; } dev_info(&dev->dev, "ibmvnic registered\n"); return 0; -free_sub_crqs: - release_sub_crqs(adapter); free_debugfs: if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir)) debugfs_remove_recursive(adapter->debugfs_dir); -- cgit From a57a5d25a56bd9625722d17fecacf21cbe9818d8 Mon Sep 17 00:00:00 2001 From: John Allen Date: Fri, 17 Mar 2017 17:13:41 -0500 Subject: ibmvnic: Move login to its own routine Move the code that handles login and renegotiation of ibmvnic capabilities to its own routine. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 61d9d4045b4c..04f8feb0f2a3 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -371,18 +371,11 @@ static void free_rx_pool(struct ibmvnic_adapter *adapter, pool->rx_buff = NULL; } -static int ibmvnic_open(struct net_device *netdev) +static int ibmvnic_login(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); unsigned long timeout = msecs_to_jiffies(30000); struct device *dev = &adapter->vdev->dev; - struct ibmvnic_tx_pool *tx_pool; - union ibmvnic_crq crq; - int rxadd_subcrqs; - u64 *size_array; - int tx_subcrqs; - int rc = 0; - int i, j; do { if (adapter->renegotiate) { @@ -407,6 +400,25 @@ static int ibmvnic_open(struct net_device *netdev) } } while (adapter->renegotiate); + return 0; +} + +static int ibmvnic_open(struct net_device *netdev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_tx_pool *tx_pool; + union ibmvnic_crq crq; + int rxadd_subcrqs; + u64 *size_array; + int tx_subcrqs; + int rc = 0; + int i, j; + + rc = ibmvnic_login(netdev); + if (rc) + return rc; + rc = netif_set_real_num_tx_queues(netdev, adapter->req_tx_queues); if (rc) { dev_err(dev, "failed to set the number of tx queues\n"); -- cgit From f6ef6408e840f6e1a7c016d6dd1b4341bc1023f6 Mon Sep 17 00:00:00 2001 From: John Allen Date: Fri, 17 Mar 2017 17:13:42 -0500 Subject: ibmvnic: Move ibmvnic adapter intialization to its own routine The intialization of the ibmvnic driver with respect to the virtual server it connects to should be moved to its own routine. This will alolow the driver to initiate this process from places outside of the drivers probe routine. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 110 ++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 50 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 04f8feb0f2a3..42274bf4bc09 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -3783,14 +3783,65 @@ task_failed: dev_err(dev, "Passive initialization was not successful\n"); } -static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) +static int ibmvnic_init(struct ibmvnic_adapter *adapter) { + struct device *dev = &adapter->vdev->dev; unsigned long timeout = msecs_to_jiffies(30000); + struct dentry *ent; + char buf[17]; /* debugfs name buf */ + int rc; + + rc = ibmvnic_init_crq_queue(adapter); + if (rc) { + dev_err(dev, "Couldn't initialize crq. rc=%d\n", rc); + return rc; + } + + adapter->stats_token = dma_map_single(dev, &adapter->stats, + sizeof(struct ibmvnic_statistics), + DMA_FROM_DEVICE); + if (dma_mapping_error(dev, adapter->stats_token)) { + ibmvnic_release_crq_queue(adapter); + dev_err(dev, "Couldn't map stats buffer\n"); + return -ENOMEM; + } + + snprintf(buf, sizeof(buf), "ibmvnic_%x", adapter->vdev->unit_address); + ent = debugfs_create_dir(buf, NULL); + if (!ent || IS_ERR(ent)) { + dev_info(dev, "debugfs create directory failed\n"); + adapter->debugfs_dir = NULL; + } else { + adapter->debugfs_dir = ent; + ent = debugfs_create_file("dump", S_IRUGO, + adapter->debugfs_dir, + adapter->netdev, &ibmvnic_dump_ops); + if (!ent || IS_ERR(ent)) { + dev_info(dev, "debugfs create dump file failed\n"); + adapter->debugfs_dump = NULL; + } else { + adapter->debugfs_dump = ent; + } + } + + init_completion(&adapter->init_done); + ibmvnic_send_crq_init(adapter); + if (!wait_for_completion_timeout(&adapter->init_done, timeout)) { + dev_err(dev, "Initialization sequence timed out\n"); + if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir)) + debugfs_remove_recursive(adapter->debugfs_dir); + ibmvnic_release_crq_queue(adapter); + return -1; + } + + return 0; +} + +static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) +{ struct ibmvnic_adapter *adapter; struct net_device *netdev; unsigned char *mac_addr_p; - struct dentry *ent; - char buf[17]; /* debugfs name buf */ int rc; dev_dbg(&dev->dev, "entering ibmvnic_probe for UA 0x%x\n", @@ -3828,69 +3879,28 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) spin_lock_init(&adapter->stats_lock); - rc = ibmvnic_init_crq_queue(adapter); - if (rc) { - dev_err(&dev->dev, "Couldn't initialize crq. rc=%d\n", rc); - goto free_netdev; - } - INIT_LIST_HEAD(&adapter->errors); INIT_LIST_HEAD(&adapter->inflight); spin_lock_init(&adapter->error_list_lock); spin_lock_init(&adapter->inflight_lock); - adapter->stats_token = dma_map_single(&dev->dev, &adapter->stats, - sizeof(struct ibmvnic_statistics), - DMA_FROM_DEVICE); - if (dma_mapping_error(&dev->dev, adapter->stats_token)) { - if (!firmware_has_feature(FW_FEATURE_CMO)) - dev_err(&dev->dev, "Couldn't map stats buffer\n"); - rc = -ENOMEM; - goto free_crq; - } - - snprintf(buf, sizeof(buf), "ibmvnic_%x", dev->unit_address); - ent = debugfs_create_dir(buf, NULL); - if (!ent || IS_ERR(ent)) { - dev_info(&dev->dev, "debugfs create directory failed\n"); - adapter->debugfs_dir = NULL; - } else { - adapter->debugfs_dir = ent; - ent = debugfs_create_file("dump", S_IRUGO, adapter->debugfs_dir, - netdev, &ibmvnic_dump_ops); - if (!ent || IS_ERR(ent)) { - dev_info(&dev->dev, - "debugfs create dump file failed\n"); - adapter->debugfs_dump = NULL; - } else { - adapter->debugfs_dump = ent; - } + rc = ibmvnic_init(adapter); + if (rc) { + free_netdev(netdev); + return rc; } - init_completion(&adapter->init_done); - ibmvnic_send_crq_init(adapter); - if (!wait_for_completion_timeout(&adapter->init_done, timeout)) - return 0; - netdev->mtu = adapter->req_mtu - ETH_HLEN; rc = register_netdev(netdev); if (rc) { dev_err(&dev->dev, "failed to register netdev rc=%d\n", rc); - goto free_debugfs; + free_netdev(netdev); + return rc; } dev_info(&dev->dev, "ibmvnic registered\n"); return 0; - -free_debugfs: - if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir)) - debugfs_remove_recursive(adapter->debugfs_dir); -free_crq: - ibmvnic_release_crq_queue(adapter); -free_netdev: - free_netdev(netdev); - return rc; } static int ibmvnic_remove(struct vio_dev *dev) -- cgit From ea5509f53ce81662eb409c514086734d1ce16207 Mon Sep 17 00:00:00 2001 From: John Allen Date: Fri, 17 Mar 2017 17:13:43 -0500 Subject: ibmvnic: Correct ibmvnic handling of device open/close When closing the ibmvnic device we need to release the resources used in communicating to the virtual I/O server. These need to be re-negotiated with the server at open time. This patch moves the releasing of resources a separate routine and updates the open and close handlers to release all resources at close and re-negotiate and allocate these resources at open. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 114 +++++++++++++++++++++---------------- drivers/net/ethernet/ibm/ibmvnic.h | 1 + 2 files changed, 67 insertions(+), 48 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 42274bf4bc09..30e1699649b8 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -113,6 +113,8 @@ static void send_request_unmap(struct ibmvnic_adapter *, u8); static void send_login(struct ibmvnic_adapter *adapter); static void send_cap_queries(struct ibmvnic_adapter *adapter); static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter); +static int ibmvnic_init(struct ibmvnic_adapter *); +static void ibmvnic_release_crq_queue(struct ibmvnic_adapter *); struct ibmvnic_stat { char name[ETH_GSTRING_LEN]; @@ -415,6 +417,12 @@ static int ibmvnic_open(struct net_device *netdev) int rc = 0; int i, j; + if (adapter->is_closed) { + rc = ibmvnic_init(adapter); + if (rc) + return rc; + } + rc = ibmvnic_login(netdev); if (rc) return rc; @@ -525,6 +533,7 @@ static int ibmvnic_open(struct net_device *netdev) ibmvnic_send_crq(adapter, &crq); netif_tx_start_all_queues(netdev); + adapter->is_closed = false; return 0; @@ -564,21 +573,12 @@ alloc_napi_failed: return -ENOMEM; } -static int ibmvnic_close(struct net_device *netdev) +static void ibmvnic_release_resources(struct ibmvnic_adapter *adapter) { - struct ibmvnic_adapter *adapter = netdev_priv(netdev); struct device *dev = &adapter->vdev->dev; - union ibmvnic_crq crq; + int tx_scrqs, rx_scrqs; int i; - adapter->closing = true; - - for (i = 0; i < adapter->req_rx_queues; i++) - napi_disable(&adapter->napi[i]); - - if (!adapter->failover) - netif_tx_stop_all_queues(netdev); - if (adapter->bounce_buffer) { if (!dma_mapping_error(dev, adapter->bounce_buffer_dma)) { dma_unmap_single(&adapter->vdev->dev, @@ -591,33 +591,70 @@ static int ibmvnic_close(struct net_device *netdev) adapter->bounce_buffer = NULL; } - memset(&crq, 0, sizeof(crq)); - crq.logical_link_state.first = IBMVNIC_CRQ_CMD; - crq.logical_link_state.cmd = LOGICAL_LINK_STATE; - crq.logical_link_state.link_state = IBMVNIC_LOGICAL_LNK_DN; - ibmvnic_send_crq(adapter, &crq); + tx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); + for (i = 0; i < tx_scrqs; i++) { + struct ibmvnic_tx_pool *tx_pool = &adapter->tx_pool[i]; - for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); - i++) { - kfree(adapter->tx_pool[i].tx_buff); - free_long_term_buff(adapter, - &adapter->tx_pool[i].long_term_buff); - kfree(adapter->tx_pool[i].free_map); + kfree(tx_pool->tx_buff); + free_long_term_buff(adapter, &tx_pool->long_term_buff); + kfree(tx_pool->free_map); } kfree(adapter->tx_pool); adapter->tx_pool = NULL; - for (i = 0; i < be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); - i++) { - free_rx_pool(adapter, &adapter->rx_pool[i]); - free_long_term_buff(adapter, - &adapter->rx_pool[i].long_term_buff); + rx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); + for (i = 0; i < rx_scrqs; i++) { + struct ibmvnic_rx_pool *rx_pool = &adapter->rx_pool[i]; + + free_rx_pool(adapter, rx_pool); + free_long_term_buff(adapter, &rx_pool->long_term_buff); } kfree(adapter->rx_pool); adapter->rx_pool = NULL; - adapter->closing = false; + release_sub_crqs(adapter); + ibmvnic_release_crq_queue(adapter); + + if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir)) + debugfs_remove_recursive(adapter->debugfs_dir); + + if (adapter->stats_token) + dma_unmap_single(dev, adapter->stats_token, + sizeof(struct ibmvnic_statistics), + DMA_FROM_DEVICE); + + if (adapter->ras_comps) + dma_free_coherent(dev, adapter->ras_comp_num * + sizeof(struct ibmvnic_fw_component), + adapter->ras_comps, adapter->ras_comps_tok); + + kfree(adapter->ras_comp_int); +} + +static int ibmvnic_close(struct net_device *netdev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + union ibmvnic_crq crq; + int i; + + adapter->closing = true; + + for (i = 0; i < adapter->req_rx_queues; i++) + napi_disable(&adapter->napi[i]); + + if (!adapter->failover) + netif_tx_stop_all_queues(netdev); + memset(&crq, 0, sizeof(crq)); + crq.logical_link_state.first = IBMVNIC_CRQ_CMD; + crq.logical_link_state.cmd = LOGICAL_LINK_STATE; + crq.logical_link_state.link_state = IBMVNIC_LOGICAL_LNK_DN; + ibmvnic_send_crq(adapter, &crq); + + ibmvnic_release_resources(adapter); + + adapter->is_closed = true; + adapter->closing = false; return 0; } @@ -3891,6 +3928,7 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) } netdev->mtu = adapter->req_mtu - ETH_HLEN; + adapter->is_closed = false; rc = register_netdev(netdev); if (rc) { @@ -3906,28 +3944,8 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) static int ibmvnic_remove(struct vio_dev *dev) { struct net_device *netdev = dev_get_drvdata(&dev->dev); - struct ibmvnic_adapter *adapter = netdev_priv(netdev); unregister_netdev(netdev); - - release_sub_crqs(adapter); - - ibmvnic_release_crq_queue(adapter); - - if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir)) - debugfs_remove_recursive(adapter->debugfs_dir); - - dma_unmap_single(&dev->dev, adapter->stats_token, - sizeof(struct ibmvnic_statistics), DMA_FROM_DEVICE); - - if (adapter->ras_comps) - dma_free_coherent(&dev->dev, - adapter->ras_comp_num * - sizeof(struct ibmvnic_fw_component), - adapter->ras_comps, adapter->ras_comps_tok); - - kfree(adapter->ras_comp_int); - free_netdev(netdev); dev_set_drvdata(&dev->dev, NULL); diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 1993b42666f7..10ad259208cb 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -1052,4 +1052,5 @@ struct ibmvnic_adapter { struct work_struct ibmvnic_xport; struct tasklet_struct tasklet; bool failover; + bool is_closed; }; -- cgit From 71dbc3414caaf68d1914f63864b65d2a4d0bc39c Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Fri, 17 Mar 2017 23:34:04 +0100 Subject: net: usb: pegasus: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Acked-by: Petko Manolov Signed-off-by: David S. Miller --- drivers/net/usb/pegasus.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 36674484c6fb..321e059e13ae 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -953,20 +953,22 @@ static inline void pegasus_reset_wol(struct net_device *dev) } static int -pegasus_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +pegasus_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *ecmd) { pegasus_t *pegasus; pegasus = netdev_priv(dev); - mii_ethtool_gset(&pegasus->mii, ecmd); + mii_ethtool_get_link_ksettings(&pegasus->mii, ecmd); return 0; } static int -pegasus_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +pegasus_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *ecmd) { pegasus_t *pegasus = netdev_priv(dev); - return mii_ethtool_sset(&pegasus->mii, ecmd); + return mii_ethtool_set_link_ksettings(&pegasus->mii, ecmd); } static int pegasus_nway_reset(struct net_device *dev) @@ -995,14 +997,14 @@ static void pegasus_set_msglevel(struct net_device *dev, u32 v) static const struct ethtool_ops ops = { .get_drvinfo = pegasus_get_drvinfo, - .get_settings = pegasus_get_settings, - .set_settings = pegasus_set_settings, .nway_reset = pegasus_nway_reset, .get_link = pegasus_get_link, .get_msglevel = pegasus_get_msglevel, .set_msglevel = pegasus_set_msglevel, .get_wol = pegasus_get_wol, .set_wol = pegasus_set_wol, + .get_link_ksettings = pegasus_get_link_ksettings, + .set_link_ksettings = pegasus_set_link_ksettings, }; static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) -- cgit From 9549c6c872beee08ddb5bb92715bfde3b7161a88 Mon Sep 17 00:00:00 2001 From: Rick Farrington Date: Fri, 17 Mar 2017 15:43:26 -0700 Subject: liquidio: fix for vf mac addr command sent to nic firmware Change to support host<->firmware command return value. Fix for vf mac addr state command. 1. Added support for firmware commands to return a value: - previously, the returned code overlapped with host codes, thus commands were only returning 0 (success) or -1 (interpreted as timeout) - per 'response_manager.h', the error codes are split into two fields (major/minor) now, firmware commands are grouped into their own 'major' group, separate from the host's 'major' group, which allow f/w commands to return any 16-bit value 2. The command to set vf mac addr was logging a success message even if command failed. Now command uses a callback function to log the status message. 3. The command to set vf mac addr was not logging a message when set via the host 'ip' command. Now, the callback function will log an appropriate message. Signed-off-by: Rick Farrington Signed-off-by: Felix Manlunas Signed-off-by: Derek Chickles Signed-off-by: Satanand Burla Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 19 ++++++++++++++----- drivers/net/ethernet/cavium/liquidio/lio_main.c | 3 ++- .../net/ethernet/cavium/liquidio/response_manager.c | 11 +++++++++-- .../net/ethernet/cavium/liquidio/response_manager.h | 5 +++++ 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index f629c2fe04a4..65a1a9e7a159 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -131,11 +131,20 @@ void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr) case OCTNET_CMD_CHANGE_MACADDR: mac = ((u8 *)&nctrl->udd[0]) + 2; - netif_info(lio, probe, lio->netdev, - "MACAddr changed to %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", - mac[0], mac[1], - mac[2], mac[3], - mac[4], mac[5]); + if (nctrl->ncmd.s.param1) { + /* vfidx is 0 based, but vf_num (param1) is 1 based */ + int vfidx = nctrl->ncmd.s.param1 - 1; + bool mac_is_admin_assigned = nctrl->ncmd.s.param2; + + if (mac_is_admin_assigned) + netif_info(lio, probe, lio->netdev, + "MAC Address %pM is configured for VF %d\n", + mac, vfidx); + } else { + netif_info(lio, probe, lio->netdev, + " MACAddr changed to %pM\n", + mac); + } break; case OCTNET_CMD_CHANGE_MTU: diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index afa173de1042..b23485c3af13 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -3619,7 +3619,8 @@ static int __liquidio_set_vf_mac(struct net_device *netdev, int vfidx, nctrl.ncmd.s.param2 = (is_admin_assigned ? 1 : 0); nctrl.ncmd.s.more = 1; nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; - nctrl.cb_fn = 0; + nctrl.netpndev = (u64)netdev; + nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; nctrl.wait_time = LIO_CMD_WAIT_TM; nctrl.udd[0] = 0; diff --git a/drivers/net/ethernet/cavium/liquidio/response_manager.c b/drivers/net/ethernet/cavium/liquidio/response_manager.c index 945633339723..3d691c69f74d 100644 --- a/drivers/net/ethernet/cavium/liquidio/response_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/response_manager.c @@ -101,8 +101,15 @@ int lio_process_ordered_list(struct octeon_device *octeon_dev, if ((status64 & 0xff) != 0xff) { octeon_swap_8B_data(&status64, 1); if (((status64 & 0xff) != 0xff)) { - status = (u32)(status64 & - 0xffffffffULL); + /* retrieve 16-bit firmware status */ + status = (u32)(status64 & 0xffffULL); + if (status) { + status = + FIRMWARE_STATUS_CODE(status); + } else { + /* i.e. no error */ + status = OCTEON_REQUEST_DONE; + } } } } else if (force_quit || (sc->timeout && diff --git a/drivers/net/ethernet/cavium/liquidio/response_manager.h b/drivers/net/ethernet/cavium/liquidio/response_manager.h index cbb2d84e8932..9169c2815dba 100644 --- a/drivers/net/ethernet/cavium/liquidio/response_manager.h +++ b/drivers/net/ethernet/cavium/liquidio/response_manager.h @@ -78,6 +78,8 @@ enum { /*------------ Error codes used by host driver -----------------*/ #define DRIVER_MAJOR_ERROR_CODE 0x0000 +/*------ Error codes used by firmware (bits 15..0 set by firmware */ +#define FIRMWARE_MAJOR_ERROR_CODE 0x0001 /** A value of 0x00000000 indicates no error i.e. success */ #define DRIVER_ERROR_NONE 0x00000000 @@ -116,6 +118,9 @@ enum { }; +#define FIRMWARE_STATUS_CODE(status) \ + ((FIRMWARE_MAJOR_ERROR_CODE << 16) | (status)) + /** Initialize the response lists. The number of response lists to create is * given by count. * @param octeon_dev - the octeon device structure. -- cgit From f9fe1c12d126f9887441fa5bb165046f30ddd4b5 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Sat, 18 Mar 2017 00:36:15 +0100 Subject: rhashtable: Add rhashtable_lookup_get_insert_fast Add rhashtable_lookup_get_insert_fast for fixed keys, similar to rhashtable_lookup_get_insert_key for explicit keys. Signed-off-by: Andreas Gruenbacher Acked-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 092292b6675e..e507290cd2c7 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -915,6 +915,28 @@ static inline int rhashtable_lookup_insert_fast( return ret == NULL ? 0 : -EEXIST; } +/** + * rhashtable_lookup_get_insert_fast - lookup and insert object into hash table + * @ht: hash table + * @obj: pointer to hash head inside object + * @params: hash table parameters + * + * Just like rhashtable_lookup_insert_fast(), but this function returns the + * object if it exists, NULL if it did not and the insertion was successful, + * and an ERR_PTR otherwise. + */ +static inline void *rhashtable_lookup_get_insert_fast( + struct rhashtable *ht, struct rhash_head *obj, + const struct rhashtable_params params) +{ + const char *key = rht_obj(ht, obj); + + BUG_ON(ht->p.obj_hashfn); + + return __rhashtable_insert_fast(ht, key + ht->p.key_offset, obj, params, + false); +} + /** * rhashtable_lookup_insert_key - search and insert object to hash table * with explicit key -- cgit From 81e64ef673962826038d51845f3c1a56bc898fb2 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Sun, 19 Mar 2017 13:08:12 +0200 Subject: qed: Increase verbosity of VF -> PF errors VFs are currently logging errors when communicating with their PFs in a too-low verbosity that wouldn't be shown by default. As timeouts and failed commands are crucial for VF operability, make them appear by default. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_vf.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c index 15d2855ec563..798786562b1b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.c +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c @@ -134,14 +134,20 @@ static int qed_send_msg2pf(struct qed_hwfn *p_hwfn, u8 *done, u32 resp_size) } if (!*done) { - DP_VERBOSE(p_hwfn, QED_MSG_IOV, - "VF <-- PF Timeout [Type %d]\n", - p_req->first_tlv.tl.type); + DP_NOTICE(p_hwfn, + "VF <-- PF Timeout [Type %d]\n", + p_req->first_tlv.tl.type); rc = -EBUSY; } else { - DP_VERBOSE(p_hwfn, QED_MSG_IOV, - "PF response: %d [Type %d]\n", - *done, p_req->first_tlv.tl.type); + if ((*done != PFVF_STATUS_SUCCESS) && + (*done != PFVF_STATUS_NO_RESOURCE)) + DP_NOTICE(p_hwfn, + "PF response: %d [Type %d]\n", + *done, p_req->first_tlv.tl.type); + else + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "PF response: %d [Type %d]\n", + *done, p_req->first_tlv.tl.type); } return rc; -- cgit From 4e9b2a67269a26d17608411db6ae2539f34ca191 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Sun, 19 Mar 2017 13:08:13 +0200 Subject: qed: Clean VF malicious indication when disabling IOV When a VF is considered malicious, driver handling of the VF FLR flow would clean said indication - but not if the FLR is part of an sriov-disable flow. That leads to further issues, as PF wouldn't re-enable the previously malicious VF when sriov is re-enabled. No reason for that - simply clean malicious indications in the sriov-disable flow as well. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 16f503c9b0af..2403d58d9d11 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -693,6 +693,11 @@ static int qed_iov_enable_vf_access(struct qed_hwfn *p_hwfn, u32 igu_vf_conf = IGU_VF_CONF_FUNC_EN; int rc; + /* It's possible VF was previously considered malicious - + * clear the indication even if we're only going to disable VF. + */ + vf->b_malicious = false; + if (vf->to_disable) return 0; @@ -705,9 +710,6 @@ static int qed_iov_enable_vf_access(struct qed_hwfn *p_hwfn, qed_iov_vf_igu_reset(p_hwfn, p_ptt, vf); - /* It's possible VF was previously considered malicious */ - vf->b_malicious = false; - rc = qed_mcp_config_vf_msix(p_hwfn, p_ptt, vf->abs_vf_id, vf->num_sbs); if (rc) return rc; -- cgit From d91940818d81ca1761de855c17f3528b3ec7eabe Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Sun, 19 Mar 2017 13:08:14 +0200 Subject: qed: Set HW-channel to ready before ACKing VF When PF responds to the VF requests it also cleans the HW-channel indication in firmware to allow further VF messages to arrive, but the order currently applied is wrong - The PF is copying by DMAE the response the VF is polling on for completion, and only afterwards sets the HW-channel to ready state. This creates a race condition where the VF would be able to send an additional message to the PF before the channel would get ready again, causing the firmware to consider the VF as malicious. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 2403d58d9d11..aea1e4c45ead 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -1136,13 +1136,17 @@ static void qed_iov_send_response(struct qed_hwfn *p_hwfn, (sizeof(union pfvf_tlvs) - sizeof(u64)) / 4, ¶ms); - qed_dmae_host2host(p_hwfn, p_ptt, mbx->reply_phys, - mbx->req_virt->first_tlv.reply_address, - sizeof(u64) / 4, ¶ms); - + /* Once PF copies the rc to the VF, the latter can continue + * and send an additional message. So we have to make sure the + * channel would be re-set to ready prior to that. + */ REG_WR(p_hwfn, GTT_BAR0_MAP_REG_USDM_RAM + USTORM_VF_PF_CHANNEL_READY_OFFSET(eng_vf_id), 1); + + qed_dmae_host2host(p_hwfn, p_ptt, mbx->reply_phys, + mbx->req_virt->first_tlv.reply_address, + sizeof(u64) / 4, ¶ms); } static u16 qed_iov_vport_to_tlv(struct qed_hwfn *p_hwfn, -- cgit From b801b1591c10f546206d4db29a31793a4e1efbbb Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Sun, 19 Mar 2017 13:08:15 +0200 Subject: qed: Correct default VF coalescing configuration When starting the VF's vport, the PF would first configure the status blocks of the VF and then reset them. That would cause some of the configured information to be lost - specifically it would mean that all the VFs queues would use the Rx coalescing state-machine of the status block. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index aea1e4c45ead..098330e58b35 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -1755,6 +1755,8 @@ static void qed_iov_vf_mbx_start_vport(struct qed_hwfn *p_hwfn, vf->state = VF_ENABLED; start = &mbx->req_virt->start_vport; + qed_iov_enable_vf_traffic(p_hwfn, p_ptt, vf); + /* Initialize Status block in CAU */ for (sb_id = 0; sb_id < vf->num_sbs; sb_id++) { if (!start->sb_addr[sb_id]) { @@ -1768,7 +1770,6 @@ static void qed_iov_vf_mbx_start_vport(struct qed_hwfn *p_hwfn, start->sb_addr[sb_id], vf->igu_sbs[sb_id], vf->abs_vf_id, 1); } - qed_iov_enable_vf_traffic(p_hwfn, p_ptt, vf); vf->mtu = start->mtu; vf->shadow_config.inner_vlan_removal = start->inner_vlan_removal; -- cgit From f109c240c419b73c4dccab820e3525ef8e922f51 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Sun, 19 Mar 2017 13:08:16 +0200 Subject: qed: Uniform IOV queue validation PF needs to validate the status of VF queues before asking firmware to configure anything for them, but that validation is done in various different forms - sometimes inadequate. Add auxillary functions that can be used for testing of the queue state and convert the various flows to use those instead of current existing flows; Also, add missing validations where needed. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 123 +++++++++++++++++++++------- 1 file changed, 92 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 098330e58b35..7547a23b4a4d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -178,26 +178,59 @@ static struct qed_vf_info *qed_iov_get_vf_info(struct qed_hwfn *p_hwfn, return vf; } +enum qed_iov_validate_q_mode { + QED_IOV_VALIDATE_Q_NA, + QED_IOV_VALIDATE_Q_ENABLE, + QED_IOV_VALIDATE_Q_DISABLE, +}; + +static bool qed_iov_validate_queue_mode(struct qed_hwfn *p_hwfn, + struct qed_vf_info *p_vf, + u16 qid, + enum qed_iov_validate_q_mode mode, + bool b_is_tx) +{ + if (mode == QED_IOV_VALIDATE_Q_NA) + return true; + + if ((b_is_tx && p_vf->vf_queues[qid].p_tx_cid) || + (!b_is_tx && p_vf->vf_queues[qid].p_rx_cid)) + return mode == QED_IOV_VALIDATE_Q_ENABLE; + + /* In case we haven't found any valid cid, then its disabled */ + return mode == QED_IOV_VALIDATE_Q_DISABLE; +} + static bool qed_iov_validate_rxq(struct qed_hwfn *p_hwfn, - struct qed_vf_info *p_vf, u16 rx_qid) + struct qed_vf_info *p_vf, + u16 rx_qid, + enum qed_iov_validate_q_mode mode) { - if (rx_qid >= p_vf->num_rxqs) + if (rx_qid >= p_vf->num_rxqs) { DP_VERBOSE(p_hwfn, QED_MSG_IOV, "VF[0x%02x] - can't touch Rx queue[%04x]; Only 0x%04x are allocated\n", p_vf->abs_vf_id, rx_qid, p_vf->num_rxqs); - return rx_qid < p_vf->num_rxqs; + return false; + } + + return qed_iov_validate_queue_mode(p_hwfn, p_vf, rx_qid, mode, false); } static bool qed_iov_validate_txq(struct qed_hwfn *p_hwfn, - struct qed_vf_info *p_vf, u16 tx_qid) + struct qed_vf_info *p_vf, + u16 tx_qid, + enum qed_iov_validate_q_mode mode) { - if (tx_qid >= p_vf->num_txqs) + if (tx_qid >= p_vf->num_txqs) { DP_VERBOSE(p_hwfn, QED_MSG_IOV, "VF[0x%02x] - can't touch Tx queue[%04x]; Only 0x%04x are allocated\n", p_vf->abs_vf_id, tx_qid, p_vf->num_txqs); - return tx_qid < p_vf->num_txqs; + return false; + } + + return qed_iov_validate_queue_mode(p_hwfn, p_vf, tx_qid, mode, true); } static bool qed_iov_validate_sb(struct qed_hwfn *p_hwfn, @@ -217,6 +250,34 @@ static bool qed_iov_validate_sb(struct qed_hwfn *p_hwfn, return false; } +static bool qed_iov_validate_active_rxq(struct qed_hwfn *p_hwfn, + struct qed_vf_info *p_vf) +{ + u8 i; + + for (i = 0; i < p_vf->num_rxqs; i++) + if (qed_iov_validate_queue_mode(p_hwfn, p_vf, i, + QED_IOV_VALIDATE_Q_ENABLE, + false)) + return true; + + return false; +} + +static bool qed_iov_validate_active_txq(struct qed_hwfn *p_hwfn, + struct qed_vf_info *p_vf) +{ + u8 i; + + for (i = 0; i < p_vf->num_txqs; i++) + if (qed_iov_validate_queue_mode(p_hwfn, p_vf, i, + QED_IOV_VALIDATE_Q_ENABLE, + true)) + return true; + + return false; +} + static int qed_iov_post_vf_bulletin(struct qed_hwfn *p_hwfn, int vfid, struct qed_ptt *p_ptt) { @@ -1826,6 +1887,16 @@ static void qed_iov_vf_mbx_stop_vport(struct qed_hwfn *p_hwfn, vf->vport_instance--; vf->spoof_chk = false; + if ((qed_iov_validate_active_rxq(p_hwfn, vf)) || + (qed_iov_validate_active_txq(p_hwfn, vf))) { + vf->b_malicious = true; + DP_NOTICE(p_hwfn, + "VF [%02x] - considered malicious; Unable to stop RX/TX queuess\n", + vf->abs_vf_id); + status = PFVF_STATUS_MALICIOUS; + goto out; + } + rc = qed_sp_vport_stop(p_hwfn, vf->opaque_fid, vf->vport_id); if (rc) { DP_ERR(p_hwfn, "qed_iov_vf_mbx_stop_vport returned error %d\n", @@ -1837,6 +1908,7 @@ static void qed_iov_vf_mbx_stop_vport(struct qed_hwfn *p_hwfn, vf->configured_features = 0; memset(&vf->shadow_config, 0, sizeof(vf->shadow_config)); +out: qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_VPORT_TEARDOWN, sizeof(struct pfvf_def_resp_tlv), status); } @@ -1893,7 +1965,8 @@ static void qed_iov_vf_mbx_start_rxq(struct qed_hwfn *p_hwfn, req = &mbx->req_virt->start_rxq; - if (!qed_iov_validate_rxq(p_hwfn, vf, req->rx_qid) || + if (!qed_iov_validate_rxq(p_hwfn, vf, req->rx_qid, + QED_IOV_VALIDATE_Q_DISABLE) || !qed_iov_validate_sb(p_hwfn, vf, req->hw_sb)) goto out; @@ -2007,7 +2080,8 @@ static void qed_iov_vf_mbx_start_txq(struct qed_hwfn *p_hwfn, memset(¶ms, 0, sizeof(params)); req = &mbx->req_virt->start_txq; - if (!qed_iov_validate_txq(p_hwfn, vf, req->tx_qid) || + if (!qed_iov_validate_txq(p_hwfn, vf, req->tx_qid, + QED_IOV_VALIDATE_Q_DISABLE) || !qed_iov_validate_sb(p_hwfn, vf, req->hw_sb)) goto out; @@ -2164,22 +2238,17 @@ static void qed_iov_vf_mbx_update_rxqs(struct qed_hwfn *p_hwfn, complete_event_flg = !!(req->flags & VFPF_RXQ_UPD_COMPLETE_EVENT_FLAG); /* Validate inputs */ - if (req->num_rxqs + req->rx_qid > QED_MAX_VF_CHAINS_PER_PF || - !qed_iov_validate_rxq(p_hwfn, vf, req->rx_qid)) { - DP_INFO(p_hwfn, "VF[%d]: Incorrect Rxqs [%04x, %02x]\n", - vf->relative_vf_id, req->rx_qid, req->num_rxqs); - goto out; - } - - for (i = 0; i < req->num_rxqs; i++) { - qid = req->rx_qid + i; - if (!vf->vf_queues[qid].p_rx_cid) { - DP_INFO(p_hwfn, - "VF[%d] rx_qid = %d isn`t active!\n", - vf->relative_vf_id, qid); + for (i = req->rx_qid; i < req->rx_qid + req->num_rxqs; i++) + if (!qed_iov_validate_rxq(p_hwfn, vf, i, + QED_IOV_VALIDATE_Q_ENABLE)) { + DP_INFO(p_hwfn, "VF[%d]: Incorrect Rxqs [%04x, %02x]\n", + vf->relative_vf_id, req->rx_qid, req->num_rxqs); goto out; } + /* Prepare the handlers */ + for (i = 0; i < req->num_rxqs; i++) { + qid = req->rx_qid + i; handlers[i] = vf->vf_queues[qid].p_rx_cid; } @@ -2395,7 +2464,8 @@ qed_iov_vp_update_rss_param(struct qed_hwfn *p_hwfn, for (i = 0; i < table_size; i++) { q_idx = p_rss_tlv->rss_ind_table[i]; - if (!qed_iov_validate_rxq(p_hwfn, vf, q_idx)) { + if (!qed_iov_validate_rxq(p_hwfn, vf, q_idx, + QED_IOV_VALIDATE_Q_ENABLE)) { DP_VERBOSE(p_hwfn, QED_MSG_IOV, "VF[%d]: Omitting RSS due to wrong queue %04x\n", @@ -2404,15 +2474,6 @@ qed_iov_vp_update_rss_param(struct qed_hwfn *p_hwfn, goto out; } - if (!vf->vf_queues[q_idx].p_rx_cid) { - DP_VERBOSE(p_hwfn, - QED_MSG_IOV, - "VF[%d]: Omitting RSS due to inactive queue %08x\n", - vf->relative_vf_id, q_idx); - b_reject = true; - goto out; - } - p_rss->rss_ind_table[i] = vf->vf_queues[q_idx].p_rx_cid; } -- cgit From 4c4fa793002ea301421c029a72138576be213c19 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Sun, 19 Mar 2017 13:08:17 +0200 Subject: qed: Deprecate VF multiple queue-stop The PF<->VF interface allows for the VF to request multiple queues closure via a single message, but this has never been used by any official driver. We now deprecate this option, forcing each queue close to arrive via a different command; This would be required for future TLVs that are going to extend the queue TLVs with additional information on a per-queue basis. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 104 ++++++++++++++++------------ drivers/net/ethernet/qlogic/qed/qed_vf.h | 4 ++ 2 files changed, 62 insertions(+), 46 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 7547a23b4a4d..f8498a3e6855 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -2118,57 +2118,53 @@ out: static int qed_iov_vf_stop_rxqs(struct qed_hwfn *p_hwfn, struct qed_vf_info *vf, - u16 rxq_id, u8 num_rxqs, bool cqe_completion) + u16 rxq_id, bool cqe_completion) { struct qed_vf_q_info *p_queue; int rc = 0; - int qid; - if (rxq_id + num_rxqs > ARRAY_SIZE(vf->vf_queues)) + if (!qed_iov_validate_rxq(p_hwfn, vf, rxq_id, + QED_IOV_VALIDATE_Q_ENABLE)) { + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF[%d] Tried Closing Rx 0x%04x which is inactive\n", + vf->relative_vf_id, rxq_id); return -EINVAL; + } - for (qid = rxq_id; qid < rxq_id + num_rxqs; qid++) { - p_queue = &vf->vf_queues[qid]; - - if (!p_queue->p_rx_cid) - continue; + p_queue = &vf->vf_queues[rxq_id]; - rc = qed_eth_rx_queue_stop(p_hwfn, - p_queue->p_rx_cid, - false, cqe_completion); - if (rc) - return rc; + rc = qed_eth_rx_queue_stop(p_hwfn, + p_queue->p_rx_cid, + false, cqe_completion); + if (rc) + return rc; - vf->vf_queues[qid].p_rx_cid = NULL; - vf->num_active_rxqs--; - } + p_queue->p_rx_cid = NULL; + vf->num_active_rxqs--; - return rc; + return 0; } static int qed_iov_vf_stop_txqs(struct qed_hwfn *p_hwfn, - struct qed_vf_info *vf, u16 txq_id, u8 num_txqs) + struct qed_vf_info *vf, u16 txq_id) { - int rc = 0; struct qed_vf_q_info *p_queue; - int qid; + int rc = 0; - if (txq_id + num_txqs > ARRAY_SIZE(vf->vf_queues)) + if (!qed_iov_validate_txq(p_hwfn, vf, txq_id, + QED_IOV_VALIDATE_Q_ENABLE)) return -EINVAL; - for (qid = txq_id; qid < txq_id + num_txqs; qid++) { - p_queue = &vf->vf_queues[qid]; - if (!p_queue->p_tx_cid) - continue; + p_queue = &vf->vf_queues[txq_id]; - rc = qed_eth_tx_queue_stop(p_hwfn, p_queue->p_tx_cid); - if (rc) - return rc; + rc = qed_eth_tx_queue_stop(p_hwfn, p_queue->p_tx_cid); + if (rc) + return rc; - p_queue->p_tx_cid = NULL; - } + p_queue->p_tx_cid = NULL; - return rc; + return 0; } static void qed_iov_vf_mbx_stop_rxqs(struct qed_hwfn *p_hwfn, @@ -2177,20 +2173,28 @@ static void qed_iov_vf_mbx_stop_rxqs(struct qed_hwfn *p_hwfn, { u16 length = sizeof(struct pfvf_def_resp_tlv); struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; - u8 status = PFVF_STATUS_SUCCESS; + u8 status = PFVF_STATUS_FAILURE; struct vfpf_stop_rxqs_tlv *req; int rc; - /* We give the option of starting from qid != 0, in this case we - * need to make sure that qid + num_qs doesn't exceed the actual - * amount of queues that exist. + /* There has never been an official driver that used this interface + * for stopping multiple queues, and it is now considered deprecated. + * Validate this isn't used here. */ req = &mbx->req_virt->stop_rxqs; - rc = qed_iov_vf_stop_rxqs(p_hwfn, vf, req->rx_qid, - req->num_rxqs, req->cqe_completion); - if (rc) - status = PFVF_STATUS_FAILURE; + if (req->num_rxqs != 1) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Odd; VF[%d] tried stopping multiple Rx queues\n", + vf->relative_vf_id); + status = PFVF_STATUS_NOT_SUPPORTED; + goto out; + } + rc = qed_iov_vf_stop_rxqs(p_hwfn, vf, req->rx_qid, + req->cqe_completion); + if (!rc) + status = PFVF_STATUS_SUCCESS; +out: qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_STOP_RXQS, length, status); } @@ -2201,19 +2205,27 @@ static void qed_iov_vf_mbx_stop_txqs(struct qed_hwfn *p_hwfn, { u16 length = sizeof(struct pfvf_def_resp_tlv); struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; - u8 status = PFVF_STATUS_SUCCESS; + u8 status = PFVF_STATUS_FAILURE; struct vfpf_stop_txqs_tlv *req; int rc; - /* We give the option of starting from qid != 0, in this case we - * need to make sure that qid + num_qs doesn't exceed the actual - * amount of queues that exist. + /* There has never been an official driver that used this interface + * for stopping multiple queues, and it is now considered deprecated. + * Validate this isn't used here. */ req = &mbx->req_virt->stop_txqs; - rc = qed_iov_vf_stop_txqs(p_hwfn, vf, req->tx_qid, req->num_txqs); - if (rc) - status = PFVF_STATUS_FAILURE; + if (req->num_txqs != 1) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Odd; VF[%d] tried stopping multiple Tx queues\n", + vf->relative_vf_id); + status = PFVF_STATUS_NOT_SUPPORTED; + goto out; + } + rc = qed_iov_vf_stop_txqs(p_hwfn, vf, req->tx_qid); + if (!rc) + status = PFVF_STATUS_SUCCESS; +out: qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_STOP_TXQS, length, status); } diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.h b/drivers/net/ethernet/qlogic/qed/qed_vf.h index 7da0b165d8bc..105c0edd2a01 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.h +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.h @@ -275,6 +275,8 @@ struct vfpf_stop_rxqs_tlv { struct vfpf_first_tlv first_tlv; u16 rx_qid; + + /* this field is deprecated and should *always* be set to '1' */ u8 num_rxqs; u8 cqe_completion; u8 padding[4]; @@ -285,6 +287,8 @@ struct vfpf_stop_txqs_tlv { struct vfpf_first_tlv first_tlv; u16 tx_qid; + + /* this field is deprecated and should *always* be set to '1' */ u8 num_txqs; u8 padding[5]; }; -- cgit From cccf6f5cdc96ef6fa02f27ecd8073406a402929a Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Sun, 19 Mar 2017 13:08:18 +0200 Subject: qed: Make qed_iov_mark_vf_flr() return bool Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 9 +++++---- drivers/net/ethernet/qlogic/qed/qed_sriov.h | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index f8498a3e6855..d592de0d66a8 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -3138,9 +3138,10 @@ qed_iov_vf_flr_cleanup(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) return rc; } -int qed_iov_mark_vf_flr(struct qed_hwfn *p_hwfn, u32 *p_disabled_vfs) +bool qed_iov_mark_vf_flr(struct qed_hwfn *p_hwfn, u32 *p_disabled_vfs) { - u16 i, found = 0; + bool found = false; + u16 i; DP_VERBOSE(p_hwfn, QED_MSG_IOV, "Marking FLR-ed VFs\n"); for (i = 0; i < (VF_MAX_STATIC / 32); i++) @@ -3150,7 +3151,7 @@ int qed_iov_mark_vf_flr(struct qed_hwfn *p_hwfn, u32 *p_disabled_vfs) if (!p_hwfn->cdev->p_iov_info) { DP_NOTICE(p_hwfn, "VF flr but no IOV\n"); - return 0; + return false; } /* Mark VFs */ @@ -3179,7 +3180,7 @@ int qed_iov_mark_vf_flr(struct qed_hwfn *p_hwfn, u32 *p_disabled_vfs) * VF flr until ACKs, we're safe. */ p_flr[rel_vf_id / 64] |= 1ULL << (rel_vf_id % 64); - found = 1; + found = true; } } diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.h b/drivers/net/ethernet/qlogic/qed/qed_sriov.h index a89605821522..8e96b1d19308 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.h +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.h @@ -348,9 +348,9 @@ int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn, * @param p_hwfn * @param disabled_vfs - bitmask of all VFs on path that were FLRed * - * @return 1 iff one of the PF's vfs got FLRed. 0 otherwise. + * @return true iff one of the PF's vfs got FLRed. false otherwise. */ -int qed_iov_mark_vf_flr(struct qed_hwfn *p_hwfn, u32 *disabled_vfs); +bool qed_iov_mark_vf_flr(struct qed_hwfn *p_hwfn, u32 *disabled_vfs); /** * @brief Search extended TLVs in request/reply buffer. @@ -407,10 +407,10 @@ static inline int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn, return -EINVAL; } -static inline int qed_iov_mark_vf_flr(struct qed_hwfn *p_hwfn, - u32 *disabled_vfs) +static inline bool qed_iov_mark_vf_flr(struct qed_hwfn *p_hwfn, + u32 *disabled_vfs) { - return 0; + return false; } static inline void qed_iov_wq_stop(struct qed_dev *cdev, bool schedule_first) -- cgit From e99a21cb81214293f158a0ff09328818e24af47b Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Sun, 19 Mar 2017 13:08:19 +0200 Subject: qed: Raise verbosity of Malicious VF indications Malicious VF existance should be interesting enough for the hyperuser. Change the PF indication that one of its child VF became malicious to appear by default. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index d592de0d66a8..0d37d73335a2 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -3386,11 +3386,17 @@ static void qed_sriov_vfpf_malicious(struct qed_hwfn *p_hwfn, if (!p_vf) return; - DP_INFO(p_hwfn, - "VF [%d] - Malicious behavior [%02x]\n", - p_vf->abs_vf_id, p_data->err_id); + if (!p_vf->b_malicious) { + DP_NOTICE(p_hwfn, + "VF [%d] - Malicious behavior [%02x]\n", + p_vf->abs_vf_id, p_data->err_id); - p_vf->b_malicious = true; + p_vf->b_malicious = true; + } else { + DP_INFO(p_hwfn, + "VF [%d] - Malicious behavior [%02x]\n", + p_vf->abs_vf_id, p_data->err_id); + } } int qed_sriov_eqe_event(struct qed_hwfn *p_hwfn, -- cgit From e50728effe1126eae39445ba144078b1305b7047 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Sun, 19 Mar 2017 13:08:20 +0200 Subject: qed: Always publish VF link from leading hwfn The link information exists only on the leading hwfn, but some of its derivatives [e.g., min/max rate] need to be configured for each hwfn. When re-basing the VF link view, use the leading hwfn information as basis for all existing hwfns to allow said configurations to stick. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 0d37d73335a2..18fc6e62ca41 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -3945,6 +3945,7 @@ static int qed_get_vf_config(struct qed_dev *cdev, void qed_inform_vf_link_state(struct qed_hwfn *hwfn) { + struct qed_hwfn *lead_hwfn = QED_LEADING_HWFN(hwfn->cdev); struct qed_mcp_link_capabilities caps; struct qed_mcp_link_params params; struct qed_mcp_link_state link; @@ -3961,9 +3962,15 @@ void qed_inform_vf_link_state(struct qed_hwfn *hwfn) if (!vf_info) continue; - memcpy(¶ms, qed_mcp_get_link_params(hwfn), sizeof(params)); - memcpy(&link, qed_mcp_get_link_state(hwfn), sizeof(link)); - memcpy(&caps, qed_mcp_get_link_capabilities(hwfn), + /* Only hwfn0 is actually interested in the link speed. + * But since only it would receive an MFW indication of link, + * need to take configuration from it - otherwise things like + * rate limiting for hwfn1 VF would not work. + */ + memcpy(¶ms, qed_mcp_get_link_params(lead_hwfn), + sizeof(params)); + memcpy(&link, qed_mcp_get_link_state(lead_hwfn), sizeof(link)); + memcpy(&caps, qed_mcp_get_link_capabilities(lead_hwfn), sizeof(caps)); /* Modify link according to the VF's configured link state */ -- cgit From ca260ece6a57dc7d751e0685f51fa2c55d851873 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 13 Mar 2017 13:44:21 +0100 Subject: zd1211rw: fix NULL-deref at probe Make sure to check the number of endpoints to avoid dereferencing a NULL-pointer or accessing memory beyond the endpoint array should a malicious device lack the expected endpoints. Fixes: a1030e92c150 ("[PATCH] zd1211rw: Convert installer CDROM device into WLAN device") Cc: Daniel Drake Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/zydas/zd1211rw/zd_usb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c index c5effd6c6be9..01ca1d57b3d9 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c @@ -1278,6 +1278,9 @@ static int eject_installer(struct usb_interface *intf) u8 bulk_out_ep; int r; + if (iface_desc->desc.bNumEndpoints < 2) + return -ENODEV; + /* Find bulk out endpoint */ for (r = 1; r >= 0; r--) { endpoint = &iface_desc->endpoint[r].desc; -- cgit From 41977e86c984fcdddb454a3d7887de5d47b5f530 Mon Sep 17 00:00:00 2001 From: Roman Yeryomin Date: Tue, 21 Mar 2017 00:43:00 +0100 Subject: rt2x00: add support for MT7620 Basic support for MT7620 built-in wireless radio was added to OpenWrt in r41441. It has seen some heavy cleaning and refactoring since in order to match the Kernel's code quality standards. Signed-off-by: Roman Yeryomin Signed-off-by: Daniel Golle Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/Kconfig | 2 +- drivers/net/wireless/ralink/rt2x00/rt2800.h | 177 +++ drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 1421 +++++++++++++++++++++++- drivers/net/wireless/ralink/rt2x00/rt2800lib.h | 4 + drivers/net/wireless/ralink/rt2x00/rt2x00.h | 1 + 5 files changed, 1578 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/Kconfig b/drivers/net/wireless/ralink/rt2x00/Kconfig index de62f5dcb62f..a1d1cfe214d2 100644 --- a/drivers/net/wireless/ralink/rt2x00/Kconfig +++ b/drivers/net/wireless/ralink/rt2x00/Kconfig @@ -201,7 +201,7 @@ endif config RT2800SOC tristate "Ralink WiSoC support" - depends on SOC_RT288X || SOC_RT305X + depends on SOC_RT288X || SOC_RT305X || SOC_MT7620 select RT2X00_LIB_SOC select RT2X00_LIB_MMIO select RT2X00_LIB_CRYPTO diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800.h b/drivers/net/wireless/ralink/rt2x00/rt2800.h index fd1dbd956bad..6a8c93fb6a43 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h @@ -79,6 +79,7 @@ #define RF5372 0x5372 #define RF5390 0x5390 #define RF5392 0x5392 +#define RF7620 0x7620 /* * Chipset revisions. @@ -638,6 +639,24 @@ #define RF_CSR_CFG_WRITE FIELD32(0x00010000) #define RF_CSR_CFG_BUSY FIELD32(0x00020000) +/* + * MT7620 RF registers (reversed order) + */ +#define RF_CSR_CFG_DATA_MT7620 FIELD32(0x0000ff00) +#define RF_CSR_CFG_REGNUM_MT7620 FIELD32(0x03ff0000) +#define RF_CSR_CFG_WRITE_MT7620 FIELD32(0x00000010) +#define RF_CSR_CFG_BUSY_MT7620 FIELD32(0x00000001) + +/* undocumented registers for calibration of new MAC */ +#define RF_CONTROL0 0x0518 +#define RF_BYPASS0 0x051c +#define RF_CONTROL1 0x0520 +#define RF_BYPASS1 0x0524 +#define RF_CONTROL2 0x0528 +#define RF_BYPASS2 0x052c +#define RF_CONTROL3 0x0530 +#define RF_BYPASS3 0x0534 + /* * EFUSE_CSR: RT30x0 EEPROM */ @@ -1021,6 +1040,16 @@ #define AUTOWAKEUP_CFG_TBCN_BEFORE_WAKE FIELD32(0x00007f00) #define AUTOWAKEUP_CFG_AUTOWAKE FIELD32(0x00008000) +/* + * MIMO_PS_CFG: MIMO Power-save Configuration + */ +#define MIMO_PS_CFG 0x1210 +#define MIMO_PS_CFG_MMPS_BB_EN FIELD32(0x00000001) +#define MIMO_PS_CFG_MMPS_RX_ANT_NUM FIELD32(0x00000006) +#define MIMO_PS_CFG_MMPS_RF_EN FIELD32(0x00000008) +#define MIMO_PS_CFG_RX_STBY_POL FIELD32(0x00000010) +#define MIMO_PS_CFG_RX_RX_STBY0 FIELD32(0x00000020) + /* * EDCA_AC0_CFG: */ @@ -1095,6 +1124,12 @@ #define TX_PWR_CFG_0_OFDM6_CH1 FIELD32(0x00f00000) #define TX_PWR_CFG_0_OFDM12_CH0 FIELD32(0x0f000000) #define TX_PWR_CFG_0_OFDM12_CH1 FIELD32(0xf0000000) +/* bits for new 2T devices */ +#define TX_PWR_CFG_0B_1MBS_2MBS FIELD32(0x000000ff) +#define TX_PWR_CFG_0B_5MBS_11MBS FIELD32(0x0000ff00) +#define TX_PWR_CFG_0B_6MBS_9MBS FIELD32(0x00ff0000) +#define TX_PWR_CFG_0B_12MBS_18MBS FIELD32(0xff000000) + /* * TX_PWR_CFG_1: @@ -1117,6 +1152,11 @@ #define TX_PWR_CFG_1_MCS0_CH1 FIELD32(0x00f00000) #define TX_PWR_CFG_1_MCS2_CH0 FIELD32(0x0f000000) #define TX_PWR_CFG_1_MCS2_CH1 FIELD32(0xf0000000) +/* bits for new 2T devices */ +#define TX_PWR_CFG_1B_24MBS_36MBS FIELD32(0x000000ff) +#define TX_PWR_CFG_1B_48MBS FIELD32(0x0000ff00) +#define TX_PWR_CFG_1B_MCS0_MCS1 FIELD32(0x00ff0000) +#define TX_PWR_CFG_1B_MCS2_MCS3 FIELD32(0xff000000) /* * TX_PWR_CFG_2: @@ -1139,6 +1179,11 @@ #define TX_PWR_CFG_2_MCS8_CH1 FIELD32(0x00f00000) #define TX_PWR_CFG_2_MCS10_CH0 FIELD32(0x0f000000) #define TX_PWR_CFG_2_MCS10_CH1 FIELD32(0xf0000000) +/* bits for new 2T devices */ +#define TX_PWR_CFG_2B_MCS4_MCS5 FIELD32(0x000000ff) +#define TX_PWR_CFG_2B_MCS6_MCS7 FIELD32(0x0000ff00) +#define TX_PWR_CFG_2B_MCS8_MCS9 FIELD32(0x00ff0000) +#define TX_PWR_CFG_2B_MCS10_MCS11 FIELD32(0xff000000) /* * TX_PWR_CFG_3: @@ -1161,6 +1206,11 @@ #define TX_PWR_CFG_3_STBC0_CH1 FIELD32(0x00f00000) #define TX_PWR_CFG_3_STBC2_CH0 FIELD32(0x0f000000) #define TX_PWR_CFG_3_STBC2_CH1 FIELD32(0xf0000000) +/* bits for new 2T devices */ +#define TX_PWR_CFG_3B_MCS12_MCS13 FIELD32(0x000000ff) +#define TX_PWR_CFG_3B_MCS14 FIELD32(0x0000ff00) +#define TX_PWR_CFG_3B_STBC_MCS0_MCS1 FIELD32(0x00ff0000) +#define TX_PWR_CFG_3B_STBC_MCS2_MSC3 FIELD32(0xff000000) /* * TX_PWR_CFG_4: @@ -1175,6 +1225,9 @@ #define TX_PWR_CFG_4_STBC4_CH1 FIELD32(0x000000f0) #define TX_PWR_CFG_4_STBC6_CH0 FIELD32(0x00000f00) #define TX_PWR_CFG_4_STBC6_CH1 FIELD32(0x0000f000) +/* bits for new 2T devices */ +#define TX_PWR_CFG_4B_STBC_MCS4_MCS5 FIELD32(0x000000ff) +#define TX_PWR_CFG_4B_STBC_MCS6 FIELD32(0x0000ff00) /* * TX_PIN_CFG: @@ -1201,6 +1254,8 @@ #define TX_PIN_CFG_RFTR_POL FIELD32(0x00020000) #define TX_PIN_CFG_TRSW_EN FIELD32(0x00040000) #define TX_PIN_CFG_TRSW_POL FIELD32(0x00080000) +#define TX_PIN_CFG_RFRX_EN FIELD32(0x00100000) +#define TX_PIN_CFG_RFRX_POL FIELD32(0x00200000) #define TX_PIN_CFG_PA_PE_A2_EN FIELD32(0x01000000) #define TX_PIN_CFG_PA_PE_G2_EN FIELD32(0x02000000) #define TX_PIN_CFG_PA_PE_A2_POL FIELD32(0x04000000) @@ -1547,6 +1602,95 @@ #define TX_PWR_CFG_4_EXT_STBC4_CH2 FIELD32(0x0000000f) #define TX_PWR_CFG_4_EXT_STBC6_CH2 FIELD32(0x00000f00) +/* TXn_RF_GAIN_CORRECT: RF Gain Correction for each RF_ALC[3:2] + * Unit: 0.1 dB, Range: -3.2 dB to 3.1 dB + */ +#define TX0_RF_GAIN_CORRECT 0x13a0 +#define TX0_RF_GAIN_CORRECT_GAIN_CORR_0 FIELD32(0x0000003f) +#define TX0_RF_GAIN_CORRECT_GAIN_CORR_1 FIELD32(0x00003f00) +#define TX0_RF_GAIN_CORRECT_GAIN_CORR_2 FIELD32(0x003f0000) +#define TX0_RF_GAIN_CORRECT_GAIN_CORR_3 FIELD32(0x3f000000) + +#define TX1_RF_GAIN_CORRECT 0x13a4 +#define TX1_RF_GAIN_CORRECT_GAIN_CORR_0 FIELD32(0x0000003f) +#define TX1_RF_GAIN_CORRECT_GAIN_CORR_1 FIELD32(0x00003f00) +#define TX1_RF_GAIN_CORRECT_GAIN_CORR_2 FIELD32(0x003f0000) +#define TX1_RF_GAIN_CORRECT_GAIN_CORR_3 FIELD32(0x3f000000) + +/* TXn_RF_GAIN_ATTEN: TXn RF Gain Attenuation Level + * Format: 7-bit, signed value + * Unit: 0.5 dB, Range: -20 dB to -5 dB + */ +#define TX0_RF_GAIN_ATTEN 0x13a8 +#define TX0_RF_GAIN_ATTEN_LEVEL_0 FIELD32(0x0000007f) +#define TX0_RF_GAIN_ATTEN_LEVEL_1 FIELD32(0x00007f00) +#define TX0_RF_GAIN_ATTEN_LEVEL_2 FIELD32(0x007f0000) +#define TX0_RF_GAIN_ATTEN_LEVEL_3 FIELD32(0x7f000000) +#define TX1_RF_GAIN_ATTEN 0x13ac +#define TX1_RF_GAIN_ATTEN_LEVEL_0 FIELD32(0x0000007f) +#define TX1_RF_GAIN_ATTEN_LEVEL_1 FIELD32(0x00007f00) +#define TX1_RF_GAIN_ATTEN_LEVEL_2 FIELD32(0x007f0000) +#define TX1_RF_GAIN_ATTEN_LEVEL_3 FIELD32(0x7f000000) + +/* TX_ALC_CFG_0: TX Automatic Level Control Configuration 0 + * TX_ALC_LIMIT_n: TXn upper limit + * TX_ALC_CH_INIT_n: TXn channel initial transmission gain + * Unit: 0.5 dB, Range: 0 to 23.5 dB + */ +#define TX_ALC_CFG_0 0x13b0 +#define TX_ALC_CFG_0_CH_INIT_0 FIELD32(0x0000003f) +#define TX_ALC_CFG_0_CH_INIT_1 FIELD32(0x00003f00) +#define TX_ALC_CFG_0_LIMIT_0 FIELD32(0x003f0000) +#define TX_ALC_CFG_0_LIMIT_1 FIELD32(0x3f000000) + +/* TX_ALC_CFG_1: TX Automatic Level Control Configuration 1 + * TX_TEMP_COMP: TX Power Temperature Compensation + * Unit: 0.5 dB, Range: -10 dB to 10 dB + * TXn_GAIN_FINE: TXn Gain Fine Adjustment + * Unit: 0.1 dB, Range: -0.8 dB to 0.7 dB + * RF_TOS_DLY: Sets the RF_TOS_EN assertion delay after + * deassertion of PA_PE. + * Unit: 0.25 usec + * TXn_RF_GAIN_ATTEN: TXn RF gain attentuation selector + * RF_TOS_TIMEOUT: time-out value for RF_TOS_ENABLE + * deassertion if RF_TOS_DONE is missing. + * Unit: 0.25 usec + * RF_TOS_ENABLE: TX offset calibration enable + * ROS_BUSY_EN: RX offset calibration busy enable + */ +#define TX_ALC_CFG_1 0x13b4 +#define TX_ALC_CFG_1_TX_TEMP_COMP FIELD32(0x0000003f) +#define TX_ALC_CFG_1_TX0_GAIN_FINE FIELD32(0x00000f00) +#define TX_ALC_CFG_1_TX1_GAIN_FINE FIELD32(0x0000f000) +#define TX_ALC_CFG_1_RF_TOS_DLY FIELD32(0x00070000) +#define TX_ALC_CFG_1_TX0_RF_GAIN_ATTEN FIELD32(0x00300000) +#define TX_ALC_CFG_1_TX1_RF_GAIN_ATTEN FIELD32(0x00c00000) +#define TX_ALC_CFG_1_RF_TOS_TIMEOUT FIELD32(0x3f000000) +#define TX_ALC_CFG_1_RF_TOS_ENABLE FIELD32(0x40000000) +#define TX_ALC_CFG_1_ROS_BUSY_EN FIELD32(0x80000000) + +/* TXn_BB_GAIN_ATTEN: TXn RF Gain Attenuation Level + * Format: 5-bit signed values + * Unit: 0.5 dB, Range: -8 dB to 7 dB + */ +#define TX0_BB_GAIN_ATTEN 0x13c0 +#define TX0_BB_GAIN_ATTEN_LEVEL_0 FIELD32(0x0000001f) +#define TX0_BB_GAIN_ATTEN_LEVEL_1 FIELD32(0x00001f00) +#define TX0_BB_GAIN_ATTEN_LEVEL_2 FIELD32(0x001f0000) +#define TX0_BB_GAIN_ATTEN_LEVEL_3 FIELD32(0x1f000000) +#define TX1_BB_GAIN_ATTEN 0x13c4 +#define TX1_BB_GAIN_ATTEN_LEVEL_0 FIELD32(0x0000001f) +#define TX1_BB_GAIN_ATTEN_LEVEL_1 FIELD32(0x00001f00) +#define TX1_BB_GAIN_ATTEN_LEVEL_2 FIELD32(0x001f0000) +#define TX1_BB_GAIN_ATTEN_LEVEL_3 FIELD32(0x1f000000) + +/* TX_ALC_VGA3: TX Automatic Level Correction Variable Gain Amplifier 3 */ +#define TX_ALC_VGA3 0x13c8 +#define TX_ALC_VGA3_TX0_ALC_VGA3 FIELD32(0x0000001f) +#define TX_ALC_VGA3_TX1_ALC_VGA3 FIELD32(0x00001f00) +#define TX_ALC_VGA3_TX0_ALC_VGA2 FIELD32(0x001f0000) +#define TX_ALC_VGA3_TX1_ALC_VGA2 FIELD32(0x1f000000) + /* TX_PWR_CFG_7 */ #define TX_PWR_CFG_7 0x13d4 #define TX_PWR_CFG_7_OFDM54_CH0 FIELD32(0x0000000f) @@ -1555,6 +1699,10 @@ #define TX_PWR_CFG_7_MCS7_CH0 FIELD32(0x000f0000) #define TX_PWR_CFG_7_MCS7_CH1 FIELD32(0x00f00000) #define TX_PWR_CFG_7_MCS7_CH2 FIELD32(0x0f000000) +/* bits for new 2T devices */ +#define TX_PWR_CFG_7B_54MBS FIELD32(0x000000ff) +#define TX_PWR_CFG_7B_MCS7 FIELD32(0x00ff0000) + /* TX_PWR_CFG_8 */ #define TX_PWR_CFG_8 0x13d8 @@ -1564,12 +1712,17 @@ #define TX_PWR_CFG_8_MCS23_CH0 FIELD32(0x000f0000) #define TX_PWR_CFG_8_MCS23_CH1 FIELD32(0x00f00000) #define TX_PWR_CFG_8_MCS23_CH2 FIELD32(0x0f000000) +/* bits for new 2T devices */ +#define TX_PWR_CFG_8B_MCS15 FIELD32(0x000000ff) + /* TX_PWR_CFG_9 */ #define TX_PWR_CFG_9 0x13dc #define TX_PWR_CFG_9_STBC7_CH0 FIELD32(0x0000000f) #define TX_PWR_CFG_9_STBC7_CH1 FIELD32(0x000000f0) #define TX_PWR_CFG_9_STBC7_CH2 FIELD32(0x00000f00) +/* bits for new 2T devices */ +#define TX_PWR_CFG_9B_STBC_MCS7 FIELD32(0x000000ff) /* * RX_FILTER_CFG: RX configuration register. @@ -2137,11 +2290,14 @@ struct mac_iveiv_entry { #define RFCSR1_TX1_PD FIELD8(0x20) #define RFCSR1_RX2_PD FIELD8(0x40) #define RFCSR1_TX2_PD FIELD8(0x80) +#define RFCSR1_TX2_EN_MT7620 FIELD8(0x02) /* * RFCSR 2: */ #define RFCSR2_RESCAL_EN FIELD8(0x80) +#define RFCSR2_RX2_EN_MT7620 FIELD8(0x02) +#define RFCSR2_TX2_EN_MT7620 FIELD8(0x20) /* * RFCSR 3: @@ -2159,6 +2315,12 @@ struct mac_iveiv_entry { #define RFCSR3_BIT4 FIELD8(0x10) #define RFCSR3_BIT5 FIELD8(0x20) +/* + * RFCSR 4: + * VCOCAL_EN used by MT7620 + */ +#define RFCSR4_VCOCAL_EN FIELD8(0x80) + /* * FRCSR 5: */ @@ -2214,6 +2376,7 @@ struct mac_iveiv_entry { */ #define RFCSR13_TX_POWER FIELD8(0x1f) #define RFCSR13_DR0 FIELD8(0xe0) +#define RFCSR13_RDIV_MT7620 FIELD8(0x03) /* * RFCSR 15: @@ -2224,6 +2387,8 @@ struct mac_iveiv_entry { * RFCSR 16: */ #define RFCSR16_TXMIXER_GAIN FIELD8(0x07) +#define RFCSR16_RF_PLL_FREQ_SEL_MT7620 FIELD8(0x0F) +#define RFCSR16_SDM_MODE_MT7620 FIELD8(0xE0) /* * RFCSR 17: @@ -2236,6 +2401,8 @@ struct mac_iveiv_entry { /* RFCSR 18 */ #define RFCSR18_XO_TUNE_BYPASS FIELD8(0x40) +/* RFCSR 19 */ +#define RFCSR19_K FIELD8(0x03) /* * RFCSR 20: @@ -2246,11 +2413,14 @@ struct mac_iveiv_entry { * RFCSR 21: */ #define RFCSR21_RX_LO2_EN FIELD8(0x08) +#define RFCSR21_BIT1 FIELD8(0x01) +#define RFCSR21_BIT8 FIELD8(0x80) /* * RFCSR 22: */ #define RFCSR22_BASEBAND_LOOPBACK FIELD8(0x01) +#define RFCSR22_FREQPLAN_D_MT7620 FIELD8(0x07) /* * RFCSR 23: @@ -2272,6 +2442,11 @@ struct mac_iveiv_entry { #define RFCSR27_R3 FIELD8(0x30) #define RFCSR27_R4 FIELD8(0x40) +/* + * RFCSR 28: + */ +#define RFCSR28_CH11_HT40 FIELD8(0x04) + /* * RFCSR 29: */ @@ -2333,6 +2508,7 @@ struct mac_iveiv_entry { */ #define RFCSR42_BIT1 FIELD8(0x01) #define RFCSR42_BIT4 FIELD8(0x08) +#define RFCSR42_TX2_EN_MT7620 FIELD8(0x40) /* * RFCSR 49: @@ -2435,6 +2611,7 @@ enum rt2800_eeprom_word { EEPROM_TSSI_BOUND_BG5, EEPROM_TXPOWER_A1, EEPROM_TXPOWER_A2, + EEPROM_TXPOWER_INIT, EEPROM_TSSI_BOUND_A1, EEPROM_TSSI_BOUND_A2, EEPROM_TSSI_BOUND_A3, diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 8d00c599e47a..201b12ed90c6 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -59,6 +59,9 @@ rt2800_regbusy_read((__dev), BBP_CSR_CFG, BBP_CSR_CFG_BUSY, (__reg)) #define WAIT_FOR_RFCSR(__dev, __reg) \ rt2800_regbusy_read((__dev), RF_CSR_CFG, RF_CSR_CFG_BUSY, (__reg)) +#define WAIT_FOR_RFCSR_MT7620(__dev, __reg) \ + rt2800_regbusy_read((__dev), RF_CSR_CFG, RF_CSR_CFG_BUSY_MT7620, \ + (__reg)) #define WAIT_FOR_RF(__dev, __reg) \ rt2800_regbusy_read((__dev), RF_CSR_CFG0, RF_CSR_CFG0_BUSY, (__reg)) #define WAIT_FOR_MCU(__dev, __reg) \ @@ -150,19 +153,56 @@ static void rt2800_rfcsr_write(struct rt2x00_dev *rt2x00dev, * Wait until the RFCSR becomes available, afterwards we * can safely write the new data into the register. */ - if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, RF_CSR_CFG_DATA, value); - rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); - rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 1); - rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); + switch (rt2x00dev->chip.rt) { + case RT6352: + if (WAIT_FOR_RFCSR_MT7620(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RF_CSR_CFG_DATA_MT7620, value); + rt2x00_set_field32(®, RF_CSR_CFG_REGNUM_MT7620, + word); + rt2x00_set_field32(®, RF_CSR_CFG_WRITE_MT7620, 1); + rt2x00_set_field32(®, RF_CSR_CFG_BUSY_MT7620, 1); + + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + } + break; - rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + default: + if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RF_CSR_CFG_DATA, value); + rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 1); + rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); + + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + } + break; } mutex_unlock(&rt2x00dev->csr_mutex); } +static void rt2800_rfcsr_write_bank(struct rt2x00_dev *rt2x00dev, const u8 bank, + const unsigned int reg, const u8 value) +{ + rt2800_rfcsr_write(rt2x00dev, (reg | (bank << 6)), value); +} + +static void rt2800_rfcsr_write_chanreg(struct rt2x00_dev *rt2x00dev, + const unsigned int reg, const u8 value) +{ + rt2800_rfcsr_write_bank(rt2x00dev, 4, reg, value); + rt2800_rfcsr_write_bank(rt2x00dev, 6, reg, value); +} + +static void rt2800_rfcsr_write_dccal(struct rt2x00_dev *rt2x00dev, + const unsigned int reg, const u8 value) +{ + rt2800_rfcsr_write_bank(rt2x00dev, 5, reg, value); + rt2800_rfcsr_write_bank(rt2x00dev, 7, reg, value); +} + static void rt2800_rfcsr_read(struct rt2x00_dev *rt2x00dev, const unsigned int word, u8 *value) { @@ -178,22 +218,48 @@ static void rt2800_rfcsr_read(struct rt2x00_dev *rt2x00dev, * doesn't become available in time, reg will be 0xffffffff * which means we return 0xff to the caller. */ - if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { - reg = 0; - rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); - rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 0); - rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); + switch (rt2x00dev->chip.rt) { + case RT6352: + if (WAIT_FOR_RFCSR_MT7620(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RF_CSR_CFG_REGNUM_MT7620, + word); + rt2x00_set_field32(®, RF_CSR_CFG_WRITE_MT7620, 0); + rt2x00_set_field32(®, RF_CSR_CFG_BUSY_MT7620, 1); - rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); - WAIT_FOR_RFCSR(rt2x00dev, ®); - } + WAIT_FOR_RFCSR_MT7620(rt2x00dev, ®); + } - *value = rt2x00_get_field32(reg, RF_CSR_CFG_DATA); + *value = rt2x00_get_field32(reg, RF_CSR_CFG_DATA_MT7620); + break; + + default: + if (WAIT_FOR_RFCSR(rt2x00dev, ®)) { + reg = 0; + rt2x00_set_field32(®, RF_CSR_CFG_REGNUM, word); + rt2x00_set_field32(®, RF_CSR_CFG_WRITE, 0); + rt2x00_set_field32(®, RF_CSR_CFG_BUSY, 1); + + rt2800_register_write_lock(rt2x00dev, RF_CSR_CFG, reg); + + WAIT_FOR_RFCSR(rt2x00dev, ®); + } + + *value = rt2x00_get_field32(reg, RF_CSR_CFG_DATA); + break; + } mutex_unlock(&rt2x00dev->csr_mutex); } +static void rt2800_rfcsr_read_bank(struct rt2x00_dev *rt2x00dev, const u8 bank, + const unsigned int reg, u8 *value) +{ + rt2800_rfcsr_read(rt2x00dev, (reg | (bank << 6)), value); +} + static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u32 value) { @@ -250,6 +316,7 @@ static const unsigned int rt2800_eeprom_map[EEPROM_WORD_COUNT] = { [EEPROM_TSSI_BOUND_BG5] = 0x003b, [EEPROM_TXPOWER_A1] = 0x003c, [EEPROM_TXPOWER_A2] = 0x0053, + [EEPROM_TXPOWER_INIT] = 0x0068, [EEPROM_TSSI_BOUND_A1] = 0x006a, [EEPROM_TSSI_BOUND_A2] = 0x006b, [EEPROM_TSSI_BOUND_A3] = 0x006c, @@ -524,6 +591,7 @@ void rt2800_get_txwi_rxwi_size(struct rt2x00_dev *rt2x00dev, break; case RT5592: + case RT6352: *txwi_size = TXWI_DESC_SIZE_5WORDS; *rxwi_size = RXWI_DESC_SIZE_6WORDS; break; @@ -2810,7 +2878,8 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, rt2800_rfcsr_write(rt2x00dev, 59, r59_nonbt_rev[idx]); } else if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { + rt2x00_rt(rt2x00dev, RT5392) || + rt2x00_rt(rt2x00dev, RT6352)) { static const char r59_non_bt[] = {0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8d, 0x8a, 0x88, 0x88, 0x87, 0x87, 0x86}; @@ -3104,6 +3173,242 @@ static void rt2800_config_channel_rf55xx(struct rt2x00_dev *rt2x00dev, rt2800_bbp_write(rt2x00dev, 196, (rf->channel <= 14) ? 0x19 : 0x7F); } +static void rt2800_config_channel_rf7620(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 rx_agc_fc, tx_agc_fc; + u8 rfcsr; + + /* Frequeny plan setting */ + /* Rdiv setting (set 0x03 if Xtal==20) + * R13[1:0] + */ + rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR13_RDIV_MT7620, + rt2800_clk_is_20mhz(rt2x00dev) ? 3 : 0); + rt2800_rfcsr_write(rt2x00dev, 13, rfcsr); + + /* N setting + * R20[7:0] in rf->rf1 + * R21[0] always 0 + */ + rt2800_rfcsr_read(rt2x00dev, 20, &rfcsr); + rfcsr = (rf->rf1 & 0x00ff); + rt2800_rfcsr_write(rt2x00dev, 20, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 21, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR21_BIT1, 0); + rt2800_rfcsr_write(rt2x00dev, 21, rfcsr); + + /* K setting (always 0) + * R16[3:0] (RF PLL freq selection) + */ + rt2800_rfcsr_read(rt2x00dev, 16, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR16_RF_PLL_FREQ_SEL_MT7620, 0); + rt2800_rfcsr_write(rt2x00dev, 16, rfcsr); + + /* D setting (always 0) + * R22[2:0] (D=15, R22[2:0]=<111>) + */ + rt2800_rfcsr_read(rt2x00dev, 22, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR22_FREQPLAN_D_MT7620, 0); + rt2800_rfcsr_write(rt2x00dev, 22, rfcsr); + + /* Ksd setting + * Ksd: R17<7:0> in rf->rf2 + * R18<7:0> in rf->rf3 + * R19<1:0> in rf->rf4 + */ + rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); + rfcsr = rf->rf2; + rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 18, &rfcsr); + rfcsr = rf->rf3; + rt2800_rfcsr_write(rt2x00dev, 18, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 19, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR19_K, rf->rf4); + rt2800_rfcsr_write(rt2x00dev, 19, rfcsr); + + /* Default: XO=20MHz , SDM mode */ + rt2800_rfcsr_read(rt2x00dev, 16, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR16_SDM_MODE_MT7620, 0x80); + rt2800_rfcsr_write(rt2x00dev, 16, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 21, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR21_BIT8, 1); + rt2800_rfcsr_write(rt2x00dev, 21, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_EN_MT7620, + rt2x00dev->default_ant.tx_chain_num != 1); + rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 2, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR2_TX2_EN_MT7620, + rt2x00dev->default_ant.tx_chain_num != 1); + rt2x00_set_field8(&rfcsr, RFCSR2_RX2_EN_MT7620, + rt2x00dev->default_ant.rx_chain_num != 1); + rt2800_rfcsr_write(rt2x00dev, 2, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 42, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR42_TX2_EN_MT7620, + rt2x00dev->default_ant.tx_chain_num != 1); + rt2800_rfcsr_write(rt2x00dev, 42, rfcsr); + + /* RF for DC Cal BW */ + if (conf_is_ht40(conf)) { + rt2800_rfcsr_write_dccal(rt2x00dev, 6, 0x10); + rt2800_rfcsr_write_dccal(rt2x00dev, 7, 0x10); + rt2800_rfcsr_write_dccal(rt2x00dev, 8, 0x04); + rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x10); + rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x10); + } else { + rt2800_rfcsr_write_dccal(rt2x00dev, 6, 0x20); + rt2800_rfcsr_write_dccal(rt2x00dev, 7, 0x20); + rt2800_rfcsr_write_dccal(rt2x00dev, 8, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x20); + rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x20); + } + + if (conf_is_ht40(conf)) { + rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x08); + rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x08); + } else { + rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x28); + rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x28); + } + + rt2800_rfcsr_read(rt2x00dev, 28, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR28_CH11_HT40, + conf_is_ht40(conf) && (rf->channel == 11)); + rt2800_rfcsr_write(rt2x00dev, 28, rfcsr); + + if (!test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags)) { + if (conf_is_ht40(conf)) { + rx_agc_fc = drv_data->rx_calibration_bw40; + tx_agc_fc = drv_data->tx_calibration_bw40; + } else { + rx_agc_fc = drv_data->rx_calibration_bw20; + tx_agc_fc = drv_data->tx_calibration_bw20; + } + rt2800_rfcsr_read_bank(rt2x00dev, 5, 6, &rfcsr); + rfcsr &= (~0x3F); + rfcsr |= rx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 6, rfcsr); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 7, &rfcsr); + rfcsr &= (~0x3F); + rfcsr |= rx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 7, rfcsr); + rt2800_rfcsr_read_bank(rt2x00dev, 7, 6, &rfcsr); + rfcsr &= (~0x3F); + rfcsr |= rx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 7, 6, rfcsr); + rt2800_rfcsr_read_bank(rt2x00dev, 7, 7, &rfcsr); + rfcsr &= (~0x3F); + rfcsr |= rx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 7, 7, rfcsr); + + rt2800_rfcsr_read_bank(rt2x00dev, 5, 58, &rfcsr); + rfcsr &= (~0x3F); + rfcsr |= tx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 58, rfcsr); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 59, &rfcsr); + rfcsr &= (~0x3F); + rfcsr |= tx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 59, rfcsr); + rt2800_rfcsr_read_bank(rt2x00dev, 7, 58, &rfcsr); + rfcsr &= (~0x3F); + rfcsr |= tx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 7, 58, rfcsr); + rt2800_rfcsr_read_bank(rt2x00dev, 7, 59, &rfcsr); + rfcsr &= (~0x3F); + rfcsr |= tx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 7, 59, rfcsr); + } +} + +static void rt2800_config_alc(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *chan, + int power_level) { + u16 eeprom, target_power, max_power; + u32 mac_sys_ctrl, mac_status; + u32 reg; + u8 bbp; + int i; + + /* hardware unit is 0.5dBm, limited to 23.5dBm */ + power_level *= 2; + if (power_level > 0x2f) + power_level = 0x2f; + + max_power = chan->max_power * 2; + if (max_power > 0x2f) + max_power = 0x2f; + + rt2800_register_read(rt2x00dev, TX_ALC_CFG_0, ®); + rt2x00_set_field32(®, TX_ALC_CFG_0_CH_INIT_0, power_level); + rt2x00_set_field32(®, TX_ALC_CFG_0_CH_INIT_1, power_level); + rt2x00_set_field32(®, TX_ALC_CFG_0_LIMIT_0, max_power); + rt2x00_set_field32(®, TX_ALC_CFG_0_LIMIT_1, max_power); + + rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_INTERNAL_TX_ALC)) { + /* init base power by eeprom target power */ + rt2800_eeprom_read(rt2x00dev, EEPROM_TXPOWER_INIT, + &target_power); + rt2x00_set_field32(®, TX_ALC_CFG_0_CH_INIT_0, target_power); + rt2x00_set_field32(®, TX_ALC_CFG_0_CH_INIT_1, target_power); + } + rt2800_register_write(rt2x00dev, TX_ALC_CFG_0, reg); + + rt2800_register_read(rt2x00dev, TX_ALC_CFG_1, ®); + rt2x00_set_field32(®, TX_ALC_CFG_1_TX_TEMP_COMP, 0); + rt2800_register_write(rt2x00dev, TX_ALC_CFG_1, reg); + + /* Save MAC SYS CTRL registers */ + rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &mac_sys_ctrl); + /* Disable Tx/Rx */ + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0); + /* Check MAC Tx/Rx idle */ + for (i = 0; i < 10000; i++) { + rt2800_register_read(rt2x00dev, MAC_STATUS_CFG, + &mac_status); + if (mac_status & 0x3) + usleep_range(50, 200); + else + break; + } + + if (i == 10000) + rt2x00_warn(rt2x00dev, "Wait MAC Status to MAX !!!\n"); + + if (chan->center_freq > 2457) { + rt2800_bbp_read(rt2x00dev, 30, &bbp); + bbp = 0x40; + rt2800_bbp_write(rt2x00dev, 30, bbp); + rt2800_rfcsr_write(rt2x00dev, 39, 0); + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) + rt2800_rfcsr_write(rt2x00dev, 42, 0xfb); + else + rt2800_rfcsr_write(rt2x00dev, 42, 0x7b); + } else { + rt2800_bbp_read(rt2x00dev, 30, &bbp); + bbp = 0x1f; + rt2800_bbp_write(rt2x00dev, 30, bbp); + rt2800_rfcsr_write(rt2x00dev, 39, 0x80); + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) + rt2800_rfcsr_write(rt2x00dev, 42, 0xdb); + else + rt2800_rfcsr_write(rt2x00dev, 42, 0x5b); + } + rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, mac_sys_ctrl); +} + static void rt2800_bbp_write_with_rx_chain(struct rt2x00_dev *rt2x00dev, const unsigned int word, const u8 value) @@ -3228,7 +3533,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, struct channel_info *info) { u32 reg; - unsigned int tx_pin; + u32 tx_pin; u8 bbp, rfcsr; info->default_power1 = rt2800_txpower_to_dev(rt2x00dev, rf->channel, @@ -3273,6 +3578,9 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, case RF5592: rt2800_config_channel_rf55xx(rt2x00dev, conf, rf, info); break; + case RF7620: + rt2800_config_channel_rf7620(rt2x00dev, conf, rf, info); + break; default: rt2800_config_channel_rf2xxx(rt2x00dev, conf, rf, info); } @@ -3347,7 +3655,8 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, if (rf->channel <= 14) { if (!rt2x00_rt(rt2x00dev, RT5390) && - !rt2x00_rt(rt2x00dev, RT5392)) { + !rt2x00_rt(rt2x00dev, RT5392) && + !rt2x00_rt(rt2x00dev, RT6352)) { if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 75, 0x46); @@ -3367,7 +3676,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, rt2800_bbp_write(rt2x00dev, 82, 0x94); else if (rt2x00_rt(rt2x00dev, RT3593)) rt2800_bbp_write(rt2x00dev, 82, 0x82); - else + else if (!rt2x00_rt(rt2x00dev, RT6352)) rt2800_bbp_write(rt2x00dev, 82, 0xf2); if (rt2x00_rt(rt2x00dev, RT3593)) @@ -3388,7 +3697,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, if (rt2x00_rt(rt2x00dev, RT3572)) rt2800_rfcsr_write(rt2x00dev, 8, 0); - tx_pin = 0; + rt2800_register_read(rt2x00dev, TX_PIN_CFG, &tx_pin); switch (rt2x00dev->default_ant.tx_chain_num) { case 3: @@ -3437,6 +3746,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&tx_pin, TX_PIN_CFG_RFTR_EN, 1); rt2x00_set_field32(&tx_pin, TX_PIN_CFG_TRSW_EN, 1); + rt2x00_set_field32(&tx_pin, TX_PIN_CFG_RFRX_EN, 1); /* mt7620 */ rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); @@ -3495,7 +3805,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, usleep_range(1000, 1500); } - if (rt2x00_rt(rt2x00dev, RT5592)) { + if (rt2x00_rt(rt2x00dev, RT5592) || rt2x00_rt(rt2x00dev, RT6352)) { rt2800_bbp_write(rt2x00dev, 195, 141); rt2800_bbp_write(rt2x00dev, 196, conf_is_ht40(conf) ? 0x10 : 0x1a); @@ -4182,6 +4492,128 @@ static void rt2800_config_txpower_rt3593(struct rt2x00_dev *rt2x00dev, (unsigned long) regs[i]); } +static void rt2800_config_txpower_rt6352(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *chan, + int power_level) +{ + u32 reg, pwreg; + u16 eeprom; + u32 data, gdata; + u8 t, i; + enum nl80211_band band = chan->band; + int delta; + + /* Warn user if bw_comp is set in EEPROM */ + delta = rt2800_get_txpower_bw_comp(rt2x00dev, band); + + if (delta) + rt2x00_warn(rt2x00dev, "ignoring EEPROM HT40 power delta: %d\n", + delta); + + /* populate TX_PWR_CFG_0 up to TX_PWR_CFG_4 from EEPROM for HT20, limit + * value to 0x3f and replace 0x20 by 0x21 as this is what the vendor + * driver does as well, though it looks kinda wrong. + * Maybe some misunderstanding of what a signed 8-bit value is? Maybe + * the hardware has a problem handling 0x20, and as the code initially + * used a fixed offset between HT20 and HT40 rates they had to work- + * around that issue and most likely just forgot about it later on. + * Maybe we should use rt2800_get_txpower_bw_comp() here as well, + * however, the corresponding EEPROM value is not respected by the + * vendor driver, so maybe this is rather being taken care of the + * TXALC and the driver doesn't need to handle it...? + * Though this is all very awkward, just do as they did, as that's what + * board vendors expected when they populated the EEPROM... + */ + for (i = 0; i < 5; i++) { + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + i * 2, &eeprom); + + data = eeprom; + + t = eeprom & 0x3f; + if (t == 32) + t++; + + gdata = t; + + t = (eeprom & 0x3f00) >> 8; + if (t == 32) + t++; + + gdata |= (t << 8); + + rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE, + (i * 2) + 1, &eeprom); + + t = eeprom & 0x3f; + if (t == 32) + t++; + + gdata |= (t << 16); + + t = (eeprom & 0x3f00) >> 8; + if (t == 32) + t++; + + gdata |= (t << 24); + data |= (eeprom << 16); + + if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) { + /* HT20 */ + if (data != 0xffffffff) + rt2800_register_write(rt2x00dev, + TX_PWR_CFG_0 + (i * 4), + data); + } else { + /* HT40 */ + if (gdata != 0xffffffff) + rt2800_register_write(rt2x00dev, + TX_PWR_CFG_0 + (i * 4), + gdata); + } + } + + /* Aparently Ralink ran out of space in the BYRATE calibration section + * of the EERPOM which is copied to the corresponding TX_PWR_CFG_x + * registers. As recent 2T chips use 8-bit instead of 4-bit values for + * power-offsets more space would be needed. Ralink decided to keep the + * EEPROM layout untouched and rather have some shared values covering + * multiple bitrates. + * Populate the registers not covered by the EEPROM in the same way the + * vendor driver does. + */ + + /* For OFDM 54MBS use value from OFDM 48MBS */ + pwreg = 0; + rt2800_register_read(rt2x00dev, TX_PWR_CFG_1, ®); + t = rt2x00_get_field32(reg, TX_PWR_CFG_1B_48MBS); + rt2x00_set_field32(&pwreg, TX_PWR_CFG_7B_54MBS, t); + + /* For MCS 7 use value from MCS 6 */ + rt2800_register_read(rt2x00dev, TX_PWR_CFG_2, ®); + t = rt2x00_get_field32(reg, TX_PWR_CFG_2B_MCS6_MCS7); + rt2x00_set_field32(&pwreg, TX_PWR_CFG_7B_MCS7, t); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_7, pwreg); + + /* For MCS 15 use value from MCS 14 */ + pwreg = 0; + rt2800_register_read(rt2x00dev, TX_PWR_CFG_3, ®); + t = rt2x00_get_field32(reg, TX_PWR_CFG_3B_MCS14); + rt2x00_set_field32(&pwreg, TX_PWR_CFG_8B_MCS15, t); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_8, pwreg); + + /* For STBC MCS 7 use value from STBC MCS 6 */ + pwreg = 0; + rt2800_register_read(rt2x00dev, TX_PWR_CFG_4, ®); + t = rt2x00_get_field32(reg, TX_PWR_CFG_4B_STBC_MCS6); + rt2x00_set_field32(&pwreg, TX_PWR_CFG_9B_STBC_MCS7, t); + rt2800_register_write(rt2x00dev, TX_PWR_CFG_9, pwreg); + + rt2800_config_alc(rt2x00dev, chan, power_level); + + /* TODO: temperature compensation code! */ +} + /* * We configure transmit power using MAC TX_PWR_CFG_{0,...,N} registers and * BBP R1 register. TX_PWR_CFG_X allow to configure per rate TX power values, @@ -4378,6 +4810,8 @@ static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev, { if (rt2x00_rt(rt2x00dev, RT3593)) rt2800_config_txpower_rt3593(rt2x00dev, chan, power_level); + else if (rt2x00_rt(rt2x00dev, RT6352)) + rt2800_config_txpower_rt6352(rt2x00dev, chan, power_level); else rt2800_config_txpower_rt28xx(rt2x00dev, chan, power_level); } @@ -4393,6 +4827,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) { u32 tx_pin; u8 rfcsr; + unsigned long min_sleep = 0; /* * A voltage-controlled oscillator(VCO) is an electronic oscillator @@ -4431,6 +4866,15 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1); rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); + min_sleep = 1000; + break; + case RF7620: + rt2800_rfcsr_write(rt2x00dev, 5, 0x40); + rt2800_rfcsr_write(rt2x00dev, 4, 0x0C); + rt2800_rfcsr_read(rt2x00dev, 4, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR4_VCOCAL_EN, 1); + rt2800_rfcsr_write(rt2x00dev, 4, rfcsr); + min_sleep = 2000; break; default: WARN_ONCE(1, "Not supported RF chipet %x for VCO recalibration", @@ -4438,7 +4882,8 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) return; } - usleep_range(1000, 1500); + if (min_sleep > 0) + usleep_range(min_sleep, min_sleep * 2); rt2800_register_read(rt2x00dev, TX_PIN_CFG, &tx_pin); if (rt2x00dev->rf_channel <= 14) { @@ -4470,6 +4915,42 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) } rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); + if (rt2x00_rt(rt2x00dev, RT6352)) { + if (rt2x00dev->default_ant.tx_chain_num == 1) { + rt2800_bbp_write(rt2x00dev, 91, 0x07); + rt2800_bbp_write(rt2x00dev, 95, 0x1A); + rt2800_bbp_write(rt2x00dev, 195, 128); + rt2800_bbp_write(rt2x00dev, 196, 0xA0); + rt2800_bbp_write(rt2x00dev, 195, 170); + rt2800_bbp_write(rt2x00dev, 196, 0x12); + rt2800_bbp_write(rt2x00dev, 195, 171); + rt2800_bbp_write(rt2x00dev, 196, 0x10); + } else { + rt2800_bbp_write(rt2x00dev, 91, 0x06); + rt2800_bbp_write(rt2x00dev, 95, 0x9A); + rt2800_bbp_write(rt2x00dev, 195, 128); + rt2800_bbp_write(rt2x00dev, 196, 0xE0); + rt2800_bbp_write(rt2x00dev, 195, 170); + rt2800_bbp_write(rt2x00dev, 196, 0x30); + rt2800_bbp_write(rt2x00dev, 195, 171); + rt2800_bbp_write(rt2x00dev, 196, 0x30); + } + + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { + rt2800_bbp_write(rt2x00dev, 75, 0x60); + rt2800_bbp_write(rt2x00dev, 76, 0x44); + rt2800_bbp_write(rt2x00dev, 79, 0x1C); + rt2800_bbp_write(rt2x00dev, 80, 0x0C); + rt2800_bbp_write(rt2x00dev, 82, 0xB6); + } + + /* On 11A, We should delay and wait RF/BBP to be stable + * and the appropriate time should be 1000 micro seconds + * 2005/06/05 - On 11G, we also need this delay time. + * Otherwise it's difficult to pass the WHQL. + */ + usleep_range(1000, 1500); + } } EXPORT_SYMBOL_GPL(rt2800_vco_calibration); @@ -4568,7 +5049,8 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev) rt2x00_rt(rt2x00dev, RT3593) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392) || - rt2x00_rt(rt2x00dev, RT5592)) + rt2x00_rt(rt2x00dev, RT5592) || + rt2x00_rt(rt2x00dev, RT6352)) vgc = 0x1c + (2 * rt2x00dev->lna_gain); else vgc = 0x2e + rt2x00dev->lna_gain; @@ -4795,7 +5277,8 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) 0x00000000); } } else if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { + rt2x00_rt(rt2x00dev, RT5392) || + rt2x00_rt(rt2x00dev, RT6352)) { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); @@ -4805,6 +5288,24 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); } else if (rt2x00_rt(rt2x00dev, RT5350)) { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404); + } else if (rt2x00_rt(rt2x00dev, RT6352)) { + rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000401); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x000C0000); + rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); + rt2800_register_write(rt2x00dev, MIMO_PS_CFG, 0x00000002); + rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0x00150F0F); + rt2800_register_write(rt2x00dev, TX_ALC_VGA3, 0x06060606); + rt2800_register_write(rt2x00dev, TX0_BB_GAIN_ATTEN, 0x0); + rt2800_register_write(rt2x00dev, TX1_BB_GAIN_ATTEN, 0x0); + rt2800_register_write(rt2x00dev, TX0_RF_GAIN_ATTEN, 0x6C6C666C); + rt2800_register_write(rt2x00dev, TX1_RF_GAIN_ATTEN, 0x6C6C666C); + rt2800_register_write(rt2x00dev, TX0_RF_GAIN_CORRECT, + 0x3630363A); + rt2800_register_write(rt2x00dev, TX1_RF_GAIN_CORRECT, + 0x3630363A); + rt2800_register_read(rt2x00dev, TX_ALC_CFG_1, ®); + rt2x00_set_field32(®, TX_ALC_CFG_1_ROS_BUSY_EN, 0); + rt2800_register_write(rt2x00dev, TX_ALC_CFG_1, reg); } else { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000000); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); @@ -5786,6 +6287,231 @@ static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 103, 0xc0); } +static void rt2800_bbp_glrt_write(struct rt2x00_dev *rt2x00dev, + const u8 reg, const u8 value) +{ + rt2800_bbp_write(rt2x00dev, 195, reg); + rt2800_bbp_write(rt2x00dev, 196, value); +} + +static void rt2800_bbp_dcoc_write(struct rt2x00_dev *rt2x00dev, + const u8 reg, const u8 value) +{ + rt2800_bbp_write(rt2x00dev, 158, reg); + rt2800_bbp_write(rt2x00dev, 159, value); +} + +static void rt2800_bbp_dcoc_read(struct rt2x00_dev *rt2x00dev, + const u8 reg, u8 *value) +{ + rt2800_bbp_write(rt2x00dev, 158, reg); + rt2800_bbp_read(rt2x00dev, 159, value); +} + +static void rt2800_init_bbp_6352(struct rt2x00_dev *rt2x00dev) +{ + u8 bbp; + + /* Apply Maximum Likelihood Detection (MLD) for 2 stream case */ + rt2800_bbp_read(rt2x00dev, 105, &bbp); + rt2x00_set_field8(&bbp, BBP105_MLD, + rt2x00dev->default_ant.rx_chain_num == 2); + rt2800_bbp_write(rt2x00dev, 105, bbp); + + /* Avoid data loss and CRC errors */ + rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + /* Fix I/Q swap issue */ + rt2800_bbp_read(rt2x00dev, 1, &bbp); + bbp |= 0x04; + rt2800_bbp_write(rt2x00dev, 1, bbp); + + /* BBP for G band */ + rt2800_bbp_write(rt2x00dev, 3, 0x08); + rt2800_bbp_write(rt2x00dev, 4, 0x00); /* rt2800_bbp4_mac_if_ctrl? */ + rt2800_bbp_write(rt2x00dev, 6, 0x08); + rt2800_bbp_write(rt2x00dev, 14, 0x09); + rt2800_bbp_write(rt2x00dev, 15, 0xFF); + rt2800_bbp_write(rt2x00dev, 16, 0x01); + rt2800_bbp_write(rt2x00dev, 20, 0x06); + rt2800_bbp_write(rt2x00dev, 21, 0x00); + rt2800_bbp_write(rt2x00dev, 22, 0x00); + rt2800_bbp_write(rt2x00dev, 27, 0x00); + rt2800_bbp_write(rt2x00dev, 28, 0x00); + rt2800_bbp_write(rt2x00dev, 30, 0x00); + rt2800_bbp_write(rt2x00dev, 31, 0x48); + rt2800_bbp_write(rt2x00dev, 47, 0x40); + rt2800_bbp_write(rt2x00dev, 62, 0x00); + rt2800_bbp_write(rt2x00dev, 63, 0x00); + rt2800_bbp_write(rt2x00dev, 64, 0x00); + rt2800_bbp_write(rt2x00dev, 65, 0x2C); + rt2800_bbp_write(rt2x00dev, 66, 0x1C); + rt2800_bbp_write(rt2x00dev, 67, 0x20); + rt2800_bbp_write(rt2x00dev, 68, 0xDD); + rt2800_bbp_write(rt2x00dev, 69, 0x10); + rt2800_bbp_write(rt2x00dev, 70, 0x05); + rt2800_bbp_write(rt2x00dev, 73, 0x18); + rt2800_bbp_write(rt2x00dev, 74, 0x0F); + rt2800_bbp_write(rt2x00dev, 75, 0x60); + rt2800_bbp_write(rt2x00dev, 76, 0x44); + rt2800_bbp_write(rt2x00dev, 77, 0x59); + rt2800_bbp_write(rt2x00dev, 78, 0x1E); + rt2800_bbp_write(rt2x00dev, 79, 0x1C); + rt2800_bbp_write(rt2x00dev, 80, 0x0C); + rt2800_bbp_write(rt2x00dev, 81, 0x3A); + rt2800_bbp_write(rt2x00dev, 82, 0xB6); + rt2800_bbp_write(rt2x00dev, 83, 0x9A); + rt2800_bbp_write(rt2x00dev, 84, 0x9A); + rt2800_bbp_write(rt2x00dev, 86, 0x38); + rt2800_bbp_write(rt2x00dev, 88, 0x90); + rt2800_bbp_write(rt2x00dev, 91, 0x04); + rt2800_bbp_write(rt2x00dev, 92, 0x02); + rt2800_bbp_write(rt2x00dev, 95, 0x9A); + rt2800_bbp_write(rt2x00dev, 96, 0x00); + rt2800_bbp_write(rt2x00dev, 103, 0xC0); + rt2800_bbp_write(rt2x00dev, 104, 0x92); + /* FIXME BBP105 owerwrite */ + rt2800_bbp_write(rt2x00dev, 105, 0x3C); + rt2800_bbp_write(rt2x00dev, 106, 0x12); + rt2800_bbp_write(rt2x00dev, 109, 0x00); + rt2800_bbp_write(rt2x00dev, 134, 0x10); + rt2800_bbp_write(rt2x00dev, 135, 0xA6); + rt2800_bbp_write(rt2x00dev, 137, 0x04); + rt2800_bbp_write(rt2x00dev, 142, 0x30); + rt2800_bbp_write(rt2x00dev, 143, 0xF7); + rt2800_bbp_write(rt2x00dev, 160, 0xEC); + rt2800_bbp_write(rt2x00dev, 161, 0xC4); + rt2800_bbp_write(rt2x00dev, 162, 0x77); + rt2800_bbp_write(rt2x00dev, 163, 0xF9); + rt2800_bbp_write(rt2x00dev, 164, 0x00); + rt2800_bbp_write(rt2x00dev, 165, 0x00); + rt2800_bbp_write(rt2x00dev, 186, 0x00); + rt2800_bbp_write(rt2x00dev, 187, 0x00); + rt2800_bbp_write(rt2x00dev, 188, 0x00); + rt2800_bbp_write(rt2x00dev, 186, 0x00); + rt2800_bbp_write(rt2x00dev, 187, 0x01); + rt2800_bbp_write(rt2x00dev, 188, 0x00); + rt2800_bbp_write(rt2x00dev, 189, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x06); + rt2800_bbp_write(rt2x00dev, 92, 0x04); + rt2800_bbp_write(rt2x00dev, 93, 0x54); + rt2800_bbp_write(rt2x00dev, 99, 0x50); + rt2800_bbp_write(rt2x00dev, 148, 0x84); + rt2800_bbp_write(rt2x00dev, 167, 0x80); + rt2800_bbp_write(rt2x00dev, 178, 0xFF); + rt2800_bbp_write(rt2x00dev, 106, 0x13); + + /* BBP for G band GLRT function (BBP_128 ~ BBP_221) */ + rt2800_bbp_glrt_write(rt2x00dev, 0, 0x00); + rt2800_bbp_glrt_write(rt2x00dev, 1, 0x14); + rt2800_bbp_glrt_write(rt2x00dev, 2, 0x20); + rt2800_bbp_glrt_write(rt2x00dev, 3, 0x0A); + rt2800_bbp_glrt_write(rt2x00dev, 10, 0x16); + rt2800_bbp_glrt_write(rt2x00dev, 11, 0x06); + rt2800_bbp_glrt_write(rt2x00dev, 12, 0x02); + rt2800_bbp_glrt_write(rt2x00dev, 13, 0x07); + rt2800_bbp_glrt_write(rt2x00dev, 14, 0x05); + rt2800_bbp_glrt_write(rt2x00dev, 15, 0x09); + rt2800_bbp_glrt_write(rt2x00dev, 16, 0x20); + rt2800_bbp_glrt_write(rt2x00dev, 17, 0x08); + rt2800_bbp_glrt_write(rt2x00dev, 18, 0x4A); + rt2800_bbp_glrt_write(rt2x00dev, 19, 0x00); + rt2800_bbp_glrt_write(rt2x00dev, 20, 0x00); + rt2800_bbp_glrt_write(rt2x00dev, 128, 0xE0); + rt2800_bbp_glrt_write(rt2x00dev, 129, 0x1F); + rt2800_bbp_glrt_write(rt2x00dev, 130, 0x4F); + rt2800_bbp_glrt_write(rt2x00dev, 131, 0x32); + rt2800_bbp_glrt_write(rt2x00dev, 132, 0x08); + rt2800_bbp_glrt_write(rt2x00dev, 133, 0x28); + rt2800_bbp_glrt_write(rt2x00dev, 134, 0x19); + rt2800_bbp_glrt_write(rt2x00dev, 135, 0x0A); + rt2800_bbp_glrt_write(rt2x00dev, 138, 0x16); + rt2800_bbp_glrt_write(rt2x00dev, 139, 0x10); + rt2800_bbp_glrt_write(rt2x00dev, 140, 0x10); + rt2800_bbp_glrt_write(rt2x00dev, 141, 0x1A); + rt2800_bbp_glrt_write(rt2x00dev, 142, 0x36); + rt2800_bbp_glrt_write(rt2x00dev, 143, 0x2C); + rt2800_bbp_glrt_write(rt2x00dev, 144, 0x26); + rt2800_bbp_glrt_write(rt2x00dev, 145, 0x24); + rt2800_bbp_glrt_write(rt2x00dev, 146, 0x42); + rt2800_bbp_glrt_write(rt2x00dev, 147, 0x40); + rt2800_bbp_glrt_write(rt2x00dev, 148, 0x30); + rt2800_bbp_glrt_write(rt2x00dev, 149, 0x29); + rt2800_bbp_glrt_write(rt2x00dev, 150, 0x4C); + rt2800_bbp_glrt_write(rt2x00dev, 151, 0x46); + rt2800_bbp_glrt_write(rt2x00dev, 152, 0x3D); + rt2800_bbp_glrt_write(rt2x00dev, 153, 0x40); + rt2800_bbp_glrt_write(rt2x00dev, 154, 0x3E); + rt2800_bbp_glrt_write(rt2x00dev, 155, 0x38); + rt2800_bbp_glrt_write(rt2x00dev, 156, 0x3D); + rt2800_bbp_glrt_write(rt2x00dev, 157, 0x2F); + rt2800_bbp_glrt_write(rt2x00dev, 158, 0x3C); + rt2800_bbp_glrt_write(rt2x00dev, 159, 0x34); + rt2800_bbp_glrt_write(rt2x00dev, 160, 0x2C); + rt2800_bbp_glrt_write(rt2x00dev, 161, 0x2F); + rt2800_bbp_glrt_write(rt2x00dev, 162, 0x3C); + rt2800_bbp_glrt_write(rt2x00dev, 163, 0x35); + rt2800_bbp_glrt_write(rt2x00dev, 164, 0x2E); + rt2800_bbp_glrt_write(rt2x00dev, 165, 0x2F); + rt2800_bbp_glrt_write(rt2x00dev, 166, 0x49); + rt2800_bbp_glrt_write(rt2x00dev, 167, 0x41); + rt2800_bbp_glrt_write(rt2x00dev, 168, 0x36); + rt2800_bbp_glrt_write(rt2x00dev, 169, 0x39); + rt2800_bbp_glrt_write(rt2x00dev, 170, 0x30); + rt2800_bbp_glrt_write(rt2x00dev, 171, 0x30); + rt2800_bbp_glrt_write(rt2x00dev, 172, 0x0E); + rt2800_bbp_glrt_write(rt2x00dev, 173, 0x0D); + rt2800_bbp_glrt_write(rt2x00dev, 174, 0x28); + rt2800_bbp_glrt_write(rt2x00dev, 175, 0x21); + rt2800_bbp_glrt_write(rt2x00dev, 176, 0x1C); + rt2800_bbp_glrt_write(rt2x00dev, 177, 0x16); + rt2800_bbp_glrt_write(rt2x00dev, 178, 0x50); + rt2800_bbp_glrt_write(rt2x00dev, 179, 0x4A); + rt2800_bbp_glrt_write(rt2x00dev, 180, 0x43); + rt2800_bbp_glrt_write(rt2x00dev, 181, 0x50); + rt2800_bbp_glrt_write(rt2x00dev, 182, 0x10); + rt2800_bbp_glrt_write(rt2x00dev, 183, 0x10); + rt2800_bbp_glrt_write(rt2x00dev, 184, 0x10); + rt2800_bbp_glrt_write(rt2x00dev, 185, 0x10); + rt2800_bbp_glrt_write(rt2x00dev, 200, 0x7D); + rt2800_bbp_glrt_write(rt2x00dev, 201, 0x14); + rt2800_bbp_glrt_write(rt2x00dev, 202, 0x32); + rt2800_bbp_glrt_write(rt2x00dev, 203, 0x2C); + rt2800_bbp_glrt_write(rt2x00dev, 204, 0x36); + rt2800_bbp_glrt_write(rt2x00dev, 205, 0x4C); + rt2800_bbp_glrt_write(rt2x00dev, 206, 0x43); + rt2800_bbp_glrt_write(rt2x00dev, 207, 0x2C); + rt2800_bbp_glrt_write(rt2x00dev, 208, 0x2E); + rt2800_bbp_glrt_write(rt2x00dev, 209, 0x36); + rt2800_bbp_glrt_write(rt2x00dev, 210, 0x30); + rt2800_bbp_glrt_write(rt2x00dev, 211, 0x6E); + + /* BBP for G band DCOC function */ + rt2800_bbp_dcoc_write(rt2x00dev, 140, 0x0C); + rt2800_bbp_dcoc_write(rt2x00dev, 141, 0x00); + rt2800_bbp_dcoc_write(rt2x00dev, 142, 0x10); + rt2800_bbp_dcoc_write(rt2x00dev, 143, 0x10); + rt2800_bbp_dcoc_write(rt2x00dev, 144, 0x10); + rt2800_bbp_dcoc_write(rt2x00dev, 145, 0x10); + rt2800_bbp_dcoc_write(rt2x00dev, 146, 0x08); + rt2800_bbp_dcoc_write(rt2x00dev, 147, 0x40); + rt2800_bbp_dcoc_write(rt2x00dev, 148, 0x04); + rt2800_bbp_dcoc_write(rt2x00dev, 149, 0x04); + rt2800_bbp_dcoc_write(rt2x00dev, 150, 0x08); + rt2800_bbp_dcoc_write(rt2x00dev, 151, 0x08); + rt2800_bbp_dcoc_write(rt2x00dev, 152, 0x03); + rt2800_bbp_dcoc_write(rt2x00dev, 153, 0x03); + rt2800_bbp_dcoc_write(rt2x00dev, 154, 0x03); + rt2800_bbp_dcoc_write(rt2x00dev, 155, 0x02); + rt2800_bbp_dcoc_write(rt2x00dev, 156, 0x40); + rt2800_bbp_dcoc_write(rt2x00dev, 157, 0x40); + rt2800_bbp_dcoc_write(rt2x00dev, 158, 0x64); + rt2800_bbp_dcoc_write(rt2x00dev, 159, 0x64); + + rt2800_bbp4_mac_if_ctrl(rt2x00dev); +} + static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) { unsigned int i; @@ -5830,6 +6556,9 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) case RT5592: rt2800_init_bbp_5592(rt2x00dev); return; + case RT6352: + rt2800_init_bbp_6352(rt2x00dev); + break; } for (i = 0; i < EEPROM_BBP_SIZE; i++) { @@ -6901,6 +7630,615 @@ static void rt2800_init_rfcsr_5592(struct rt2x00_dev *rt2x00dev) rt2800_led_open_drain_enable(rt2x00dev); } +static void rt2800_bbp_core_soft_reset(struct rt2x00_dev *rt2x00dev, + bool set_bw, bool is_ht40) +{ + u8 bbp_val; + + rt2800_bbp_read(rt2x00dev, 21, &bbp_val); + bbp_val |= 0x1; + rt2800_bbp_write(rt2x00dev, 21, bbp_val); + usleep_range(100, 200); + + if (set_bw) { + rt2800_bbp_read(rt2x00dev, 4, &bbp_val); + rt2x00_set_field8(&bbp_val, BBP4_BANDWIDTH, 2 * is_ht40); + rt2800_bbp_write(rt2x00dev, 4, bbp_val); + usleep_range(100, 200); + } + + rt2800_bbp_read(rt2x00dev, 21, &bbp_val); + bbp_val &= (~0x1); + rt2800_bbp_write(rt2x00dev, 21, bbp_val); + usleep_range(100, 200); +} + +static int rt2800_rf_lp_config(struct rt2x00_dev *rt2x00dev, bool btxcal) +{ + u8 rf_val; + + if (btxcal) + rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x04); + else + rt2800_register_write(rt2x00dev, RF_CONTROL0, 0x02); + + rt2800_register_write(rt2x00dev, RF_BYPASS0, 0x06); + + rt2800_rfcsr_read_bank(rt2x00dev, 5, 17, &rf_val); + rf_val |= 0x80; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 17, rf_val); + + if (btxcal) { + rt2800_rfcsr_write_bank(rt2x00dev, 5, 18, 0xC1); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 19, 0x20); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 20, 0x02); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 3, &rf_val); + rf_val &= (~0x3F); + rf_val |= 0x3F; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 3, rf_val); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 4, &rf_val); + rf_val &= (~0x3F); + rf_val |= 0x3F; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 4, rf_val); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 5, 0x31); + } else { + rt2800_rfcsr_write_bank(rt2x00dev, 5, 18, 0xF1); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 19, 0x18); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 20, 0x02); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 3, &rf_val); + rf_val &= (~0x3F); + rf_val |= 0x34; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 3, rf_val); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 4, &rf_val); + rf_val &= (~0x3F); + rf_val |= 0x34; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 4, rf_val); + } + + return 0; +} + +static char rt2800_lp_tx_filter_bw_cal(struct rt2x00_dev *rt2x00dev) +{ + unsigned int cnt; + u8 bbp_val; + char cal_val; + + rt2800_bbp_dcoc_write(rt2x00dev, 0, 0x82); + + cnt = 0; + do { + usleep_range(500, 2000); + rt2800_bbp_read(rt2x00dev, 159, &bbp_val); + if (bbp_val == 0x02 || cnt == 20) + break; + + cnt++; + } while (cnt < 20); + + rt2800_bbp_dcoc_read(rt2x00dev, 0x39, &bbp_val); + cal_val = bbp_val & 0x7F; + if (cal_val >= 0x40) + cal_val -= 128; + + return cal_val; +} + +static void rt2800_bw_filter_calibration(struct rt2x00_dev *rt2x00dev, + bool btxcal) +{ + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u8 tx_agc_fc = 0, rx_agc_fc = 0, cmm_agc_fc; + u8 filter_target; + u8 tx_filter_target_20m = 0x09, tx_filter_target_40m = 0x02; + u8 rx_filter_target_20m = 0x27, rx_filter_target_40m = 0x31; + int loop = 0, is_ht40, cnt; + u8 bbp_val, rf_val; + char cal_r32_init, cal_r32_val, cal_diff; + u8 saverfb5r00, saverfb5r01, saverfb5r03, saverfb5r04, saverfb5r05; + u8 saverfb5r06, saverfb5r07; + u8 saverfb5r08, saverfb5r17, saverfb5r18, saverfb5r19, saverfb5r20; + u8 saverfb5r37, saverfb5r38, saverfb5r39, saverfb5r40, saverfb5r41; + u8 saverfb5r42, saverfb5r43, saverfb5r44, saverfb5r45, saverfb5r46; + u8 saverfb5r58, saverfb5r59; + u8 savebbp159r0, savebbp159r2, savebbpr23; + u32 MAC_RF_CONTROL0, MAC_RF_BYPASS0; + + /* Save MAC registers */ + rt2800_register_read(rt2x00dev, RF_CONTROL0, &MAC_RF_CONTROL0); + rt2800_register_read(rt2x00dev, RF_BYPASS0, &MAC_RF_BYPASS0); + + /* save BBP registers */ + rt2800_bbp_read(rt2x00dev, 23, &savebbpr23); + + rt2800_bbp_dcoc_read(rt2x00dev, 0, &savebbp159r0); + rt2800_bbp_dcoc_read(rt2x00dev, 2, &savebbp159r2); + + /* Save RF registers */ + rt2800_rfcsr_read_bank(rt2x00dev, 5, 0, &saverfb5r00); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 1, &saverfb5r01); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 3, &saverfb5r03); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 4, &saverfb5r04); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 5, &saverfb5r05); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 6, &saverfb5r06); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 7, &saverfb5r07); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 8, &saverfb5r08); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 17, &saverfb5r17); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 18, &saverfb5r18); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 19, &saverfb5r19); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 20, &saverfb5r20); + + rt2800_rfcsr_read_bank(rt2x00dev, 5, 37, &saverfb5r37); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 38, &saverfb5r38); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 39, &saverfb5r39); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 40, &saverfb5r40); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 41, &saverfb5r41); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 42, &saverfb5r42); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 43, &saverfb5r43); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 44, &saverfb5r44); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 45, &saverfb5r45); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 46, &saverfb5r46); + + rt2800_rfcsr_read_bank(rt2x00dev, 5, 58, &saverfb5r58); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 59, &saverfb5r59); + + rt2800_rfcsr_read_bank(rt2x00dev, 5, 0, &rf_val); + rf_val |= 0x3; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 0, rf_val); + + rt2800_rfcsr_read_bank(rt2x00dev, 5, 1, &rf_val); + rf_val |= 0x1; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 1, rf_val); + + cnt = 0; + do { + usleep_range(500, 2000); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 1, &rf_val); + if (((rf_val & 0x1) == 0x00) || (cnt == 40)) + break; + cnt++; + } while (cnt < 40); + + rt2800_rfcsr_read_bank(rt2x00dev, 5, 0, &rf_val); + rf_val &= (~0x3); + rf_val |= 0x1; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 0, rf_val); + + /* I-3 */ + rt2800_bbp_read(rt2x00dev, 23, &bbp_val); + bbp_val &= (~0x1F); + bbp_val |= 0x10; + rt2800_bbp_write(rt2x00dev, 23, bbp_val); + + do { + /* I-4,5,6,7,8,9 */ + if (loop == 0) { + is_ht40 = false; + + if (btxcal) + filter_target = tx_filter_target_20m; + else + filter_target = rx_filter_target_20m; + } else { + is_ht40 = true; + + if (btxcal) + filter_target = tx_filter_target_40m; + else + filter_target = rx_filter_target_40m; + } + + rt2800_rfcsr_read_bank(rt2x00dev, 5, 8, &rf_val); + rf_val &= (~0x04); + if (loop == 1) + rf_val |= 0x4; + + rt2800_rfcsr_write_bank(rt2x00dev, 5, 8, rf_val); + + rt2800_bbp_core_soft_reset(rt2x00dev, true, is_ht40); + + rt2800_rf_lp_config(rt2x00dev, btxcal); + if (btxcal) { + tx_agc_fc = 0; + rt2800_rfcsr_read_bank(rt2x00dev, 5, 58, &rf_val); + rf_val &= (~0x7F); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 58, rf_val); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 59, &rf_val); + rf_val &= (~0x7F); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 59, rf_val); + } else { + rx_agc_fc = 0; + rt2800_rfcsr_read_bank(rt2x00dev, 5, 6, &rf_val); + rf_val &= (~0x7F); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 6, rf_val); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 7, &rf_val); + rf_val &= (~0x7F); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 7, rf_val); + } + + usleep_range(1000, 2000); + + rt2800_bbp_dcoc_read(rt2x00dev, 2, &bbp_val); + bbp_val &= (~0x6); + rt2800_bbp_dcoc_write(rt2x00dev, 2, bbp_val); + + rt2800_bbp_core_soft_reset(rt2x00dev, false, is_ht40); + + cal_r32_init = rt2800_lp_tx_filter_bw_cal(rt2x00dev); + + rt2800_bbp_dcoc_read(rt2x00dev, 2, &bbp_val); + bbp_val |= 0x6; + rt2800_bbp_dcoc_write(rt2x00dev, 2, bbp_val); +do_cal: + if (btxcal) { + rt2800_rfcsr_read_bank(rt2x00dev, 5, 58, &rf_val); + rf_val &= (~0x7F); + rf_val |= tx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 58, rf_val); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 59, &rf_val); + rf_val &= (~0x7F); + rf_val |= tx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 59, rf_val); + } else { + rt2800_rfcsr_read_bank(rt2x00dev, 5, 6, &rf_val); + rf_val &= (~0x7F); + rf_val |= rx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 6, rf_val); + rt2800_rfcsr_read_bank(rt2x00dev, 5, 7, &rf_val); + rf_val &= (~0x7F); + rf_val |= rx_agc_fc; + rt2800_rfcsr_write_bank(rt2x00dev, 5, 7, rf_val); + } + + usleep_range(500, 1000); + + rt2800_bbp_core_soft_reset(rt2x00dev, false, is_ht40); + + cal_r32_val = rt2800_lp_tx_filter_bw_cal(rt2x00dev); + + cal_diff = cal_r32_init - cal_r32_val; + + if (btxcal) + cmm_agc_fc = tx_agc_fc; + else + cmm_agc_fc = rx_agc_fc; + + if (((cal_diff > filter_target) && (cmm_agc_fc == 0)) || + ((cal_diff < filter_target) && (cmm_agc_fc == 0x3f))) { + if (btxcal) + tx_agc_fc = 0; + else + rx_agc_fc = 0; + } else if ((cal_diff <= filter_target) && (cmm_agc_fc < 0x3f)) { + if (btxcal) + tx_agc_fc++; + else + rx_agc_fc++; + goto do_cal; + } + + if (btxcal) { + if (loop == 0) + drv_data->tx_calibration_bw20 = tx_agc_fc; + else + drv_data->tx_calibration_bw40 = tx_agc_fc; + } else { + if (loop == 0) + drv_data->rx_calibration_bw20 = rx_agc_fc; + else + drv_data->rx_calibration_bw40 = rx_agc_fc; + } + + loop++; + } while (loop <= 1); + + rt2800_rfcsr_write_bank(rt2x00dev, 5, 0, saverfb5r00); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 1, saverfb5r01); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 3, saverfb5r03); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 4, saverfb5r04); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 5, saverfb5r05); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 6, saverfb5r06); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 7, saverfb5r07); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 8, saverfb5r08); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 17, saverfb5r17); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 18, saverfb5r18); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 19, saverfb5r19); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 20, saverfb5r20); + + rt2800_rfcsr_write_bank(rt2x00dev, 5, 37, saverfb5r37); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 38, saverfb5r38); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 39, saverfb5r39); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 40, saverfb5r40); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 41, saverfb5r41); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 42, saverfb5r42); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 43, saverfb5r43); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 44, saverfb5r44); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 45, saverfb5r45); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 46, saverfb5r46); + + rt2800_rfcsr_write_bank(rt2x00dev, 5, 58, saverfb5r58); + rt2800_rfcsr_write_bank(rt2x00dev, 5, 59, saverfb5r59); + + rt2800_bbp_write(rt2x00dev, 23, savebbpr23); + + rt2800_bbp_dcoc_write(rt2x00dev, 0, savebbp159r0); + rt2800_bbp_dcoc_write(rt2x00dev, 2, savebbp159r2); + + rt2800_bbp_read(rt2x00dev, 4, &bbp_val); + rt2x00_set_field8(&bbp_val, BBP4_BANDWIDTH, + 2 * test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)); + rt2800_bbp_write(rt2x00dev, 4, bbp_val); + + rt2800_register_write(rt2x00dev, RF_CONTROL0, MAC_RF_CONTROL0); + rt2800_register_write(rt2x00dev, RF_BYPASS0, MAC_RF_BYPASS0); +} + +static void rt2800_init_rfcsr_6352(struct rt2x00_dev *rt2x00dev) +{ + /* Initialize RF central register to default value */ + rt2800_rfcsr_write(rt2x00dev, 0, 0x02); + rt2800_rfcsr_write(rt2x00dev, 1, 0x03); + rt2800_rfcsr_write(rt2x00dev, 2, 0x33); + rt2800_rfcsr_write(rt2x00dev, 3, 0xFF); + rt2800_rfcsr_write(rt2x00dev, 4, 0x0C); + rt2800_rfcsr_write(rt2x00dev, 5, 0x40); + rt2800_rfcsr_write(rt2x00dev, 6, 0x00); + rt2800_rfcsr_write(rt2x00dev, 7, 0x00); + rt2800_rfcsr_write(rt2x00dev, 8, 0x00); + rt2800_rfcsr_write(rt2x00dev, 9, 0x00); + rt2800_rfcsr_write(rt2x00dev, 10, 0x00); + rt2800_rfcsr_write(rt2x00dev, 11, 0x00); + rt2800_rfcsr_write(rt2x00dev, 12, rt2x00dev->freq_offset); + rt2800_rfcsr_write(rt2x00dev, 13, 0x00); + rt2800_rfcsr_write(rt2x00dev, 14, 0x40); + rt2800_rfcsr_write(rt2x00dev, 15, 0x22); + rt2800_rfcsr_write(rt2x00dev, 16, 0x4C); + rt2800_rfcsr_write(rt2x00dev, 17, 0x00); + rt2800_rfcsr_write(rt2x00dev, 18, 0x00); + rt2800_rfcsr_write(rt2x00dev, 19, 0x00); + rt2800_rfcsr_write(rt2x00dev, 20, 0xA0); + rt2800_rfcsr_write(rt2x00dev, 21, 0x12); + rt2800_rfcsr_write(rt2x00dev, 22, 0x07); + rt2800_rfcsr_write(rt2x00dev, 23, 0x13); + rt2800_rfcsr_write(rt2x00dev, 24, 0xFE); + rt2800_rfcsr_write(rt2x00dev, 25, 0x24); + rt2800_rfcsr_write(rt2x00dev, 26, 0x7A); + rt2800_rfcsr_write(rt2x00dev, 27, 0x00); + rt2800_rfcsr_write(rt2x00dev, 28, 0x00); + rt2800_rfcsr_write(rt2x00dev, 29, 0x05); + rt2800_rfcsr_write(rt2x00dev, 30, 0x00); + rt2800_rfcsr_write(rt2x00dev, 31, 0x00); + rt2800_rfcsr_write(rt2x00dev, 32, 0x00); + rt2800_rfcsr_write(rt2x00dev, 33, 0x00); + rt2800_rfcsr_write(rt2x00dev, 34, 0x00); + rt2800_rfcsr_write(rt2x00dev, 35, 0x00); + rt2800_rfcsr_write(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write(rt2x00dev, 37, 0x00); + rt2800_rfcsr_write(rt2x00dev, 38, 0x00); + rt2800_rfcsr_write(rt2x00dev, 39, 0x00); + rt2800_rfcsr_write(rt2x00dev, 40, 0x00); + rt2800_rfcsr_write(rt2x00dev, 41, 0xD0); + rt2800_rfcsr_write(rt2x00dev, 42, 0x5B); + rt2800_rfcsr_write(rt2x00dev, 43, 0x00); + + rt2800_rfcsr_write(rt2x00dev, 11, 0x21); + if (rt2800_clk_is_20mhz(rt2x00dev)) + rt2800_rfcsr_write(rt2x00dev, 13, 0x03); + else + rt2800_rfcsr_write(rt2x00dev, 13, 0x00); + rt2800_rfcsr_write(rt2x00dev, 14, 0x7C); + rt2800_rfcsr_write(rt2x00dev, 16, 0x80); + rt2800_rfcsr_write(rt2x00dev, 17, 0x99); + rt2800_rfcsr_write(rt2x00dev, 18, 0x99); + rt2800_rfcsr_write(rt2x00dev, 19, 0x09); + rt2800_rfcsr_write(rt2x00dev, 20, 0x50); + rt2800_rfcsr_write(rt2x00dev, 21, 0xB0); + rt2800_rfcsr_write(rt2x00dev, 22, 0x00); + rt2800_rfcsr_write(rt2x00dev, 23, 0x06); + rt2800_rfcsr_write(rt2x00dev, 24, 0x00); + rt2800_rfcsr_write(rt2x00dev, 25, 0x00); + rt2800_rfcsr_write(rt2x00dev, 26, 0x5D); + rt2800_rfcsr_write(rt2x00dev, 27, 0x00); + rt2800_rfcsr_write(rt2x00dev, 28, 0x61); + rt2800_rfcsr_write(rt2x00dev, 29, 0xB5); + rt2800_rfcsr_write(rt2x00dev, 43, 0x02); + + rt2800_rfcsr_write(rt2x00dev, 28, 0x62); + rt2800_rfcsr_write(rt2x00dev, 29, 0xAD); + rt2800_rfcsr_write(rt2x00dev, 39, 0x80); + + /* Initialize RF channel register to default value */ + rt2800_rfcsr_write_chanreg(rt2x00dev, 0, 0x03); + rt2800_rfcsr_write_chanreg(rt2x00dev, 1, 0x00); + rt2800_rfcsr_write_chanreg(rt2x00dev, 2, 0x00); + rt2800_rfcsr_write_chanreg(rt2x00dev, 3, 0x00); + rt2800_rfcsr_write_chanreg(rt2x00dev, 4, 0x00); + rt2800_rfcsr_write_chanreg(rt2x00dev, 5, 0x08); + rt2800_rfcsr_write_chanreg(rt2x00dev, 6, 0x00); + rt2800_rfcsr_write_chanreg(rt2x00dev, 7, 0x51); + rt2800_rfcsr_write_chanreg(rt2x00dev, 8, 0x53); + rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x16); + rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x61); + rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x53); + rt2800_rfcsr_write_chanreg(rt2x00dev, 12, 0x22); + rt2800_rfcsr_write_chanreg(rt2x00dev, 13, 0x3D); + rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x06); + rt2800_rfcsr_write_chanreg(rt2x00dev, 15, 0x13); + rt2800_rfcsr_write_chanreg(rt2x00dev, 16, 0x22); + rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x27); + rt2800_rfcsr_write_chanreg(rt2x00dev, 18, 0x02); + rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA7); + rt2800_rfcsr_write_chanreg(rt2x00dev, 20, 0x01); + rt2800_rfcsr_write_chanreg(rt2x00dev, 21, 0x52); + rt2800_rfcsr_write_chanreg(rt2x00dev, 22, 0x80); + rt2800_rfcsr_write_chanreg(rt2x00dev, 23, 0xB3); + rt2800_rfcsr_write_chanreg(rt2x00dev, 24, 0x00); + rt2800_rfcsr_write_chanreg(rt2x00dev, 25, 0x00); + rt2800_rfcsr_write_chanreg(rt2x00dev, 26, 0x00); + rt2800_rfcsr_write_chanreg(rt2x00dev, 27, 0x00); + rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x5C); + rt2800_rfcsr_write_chanreg(rt2x00dev, 29, 0x6B); + rt2800_rfcsr_write_chanreg(rt2x00dev, 30, 0x6B); + rt2800_rfcsr_write_chanreg(rt2x00dev, 31, 0x31); + rt2800_rfcsr_write_chanreg(rt2x00dev, 32, 0x5D); + rt2800_rfcsr_write_chanreg(rt2x00dev, 33, 0x00); + rt2800_rfcsr_write_chanreg(rt2x00dev, 34, 0xE6); + rt2800_rfcsr_write_chanreg(rt2x00dev, 35, 0x55); + rt2800_rfcsr_write_chanreg(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write_chanreg(rt2x00dev, 37, 0xBB); + rt2800_rfcsr_write_chanreg(rt2x00dev, 38, 0xB3); + rt2800_rfcsr_write_chanreg(rt2x00dev, 39, 0xB3); + rt2800_rfcsr_write_chanreg(rt2x00dev, 40, 0x03); + rt2800_rfcsr_write_chanreg(rt2x00dev, 41, 0x00); + rt2800_rfcsr_write_chanreg(rt2x00dev, 42, 0x00); + rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xB3); + rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xD3); + rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xD5); + rt2800_rfcsr_write_chanreg(rt2x00dev, 46, 0x07); + rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x68); + rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xEF); + rt2800_rfcsr_write_chanreg(rt2x00dev, 49, 0x1C); + rt2800_rfcsr_write_chanreg(rt2x00dev, 54, 0x07); + rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0xA8); + rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0x85); + rt2800_rfcsr_write_chanreg(rt2x00dev, 57, 0x10); + rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x07); + rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6A); + rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0x85); + rt2800_rfcsr_write_chanreg(rt2x00dev, 61, 0x10); + rt2800_rfcsr_write_chanreg(rt2x00dev, 62, 0x1C); + rt2800_rfcsr_write_chanreg(rt2x00dev, 63, 0x00); + + rt2800_rfcsr_write_bank(rt2x00dev, 6, 45, 0xC5); + + rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x47); + rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x71); + rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x33); + rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x0E); + rt2800_rfcsr_write_chanreg(rt2x00dev, 17, 0x23); + rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA4); + rt2800_rfcsr_write_chanreg(rt2x00dev, 20, 0x02); + rt2800_rfcsr_write_chanreg(rt2x00dev, 21, 0x12); + rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x1C); + rt2800_rfcsr_write_chanreg(rt2x00dev, 29, 0xEB); + rt2800_rfcsr_write_chanreg(rt2x00dev, 32, 0x7D); + rt2800_rfcsr_write_chanreg(rt2x00dev, 34, 0xD6); + rt2800_rfcsr_write_chanreg(rt2x00dev, 36, 0x08); + rt2800_rfcsr_write_chanreg(rt2x00dev, 38, 0xB4); + rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3); + rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xB3); + rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xD5); + rt2800_rfcsr_write_chanreg(rt2x00dev, 46, 0x27); + rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x69); + rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFF); + rt2800_rfcsr_write_chanreg(rt2x00dev, 54, 0x20); + rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66); + rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xFF); + rt2800_rfcsr_write_chanreg(rt2x00dev, 57, 0x1C); + rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x20); + rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B); + rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xF7); + rt2800_rfcsr_write_chanreg(rt2x00dev, 61, 0x09); + + rt2800_rfcsr_write_chanreg(rt2x00dev, 10, 0x51); + rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x06); + rt2800_rfcsr_write_chanreg(rt2x00dev, 19, 0xA7); + rt2800_rfcsr_write_chanreg(rt2x00dev, 28, 0x2C); + rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x64); + rt2800_rfcsr_write_chanreg(rt2x00dev, 8, 0x51); + rt2800_rfcsr_write_chanreg(rt2x00dev, 9, 0x36); + rt2800_rfcsr_write_chanreg(rt2x00dev, 11, 0x53); + rt2800_rfcsr_write_chanreg(rt2x00dev, 14, 0x16); + + rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x6C); + rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFC); + rt2800_rfcsr_write_chanreg(rt2x00dev, 49, 0x1F); + rt2800_rfcsr_write_chanreg(rt2x00dev, 54, 0x27); + rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66); + rt2800_rfcsr_write_chanreg(rt2x00dev, 59, 0x6B); + + /* Initialize RF channel register for DRQFN */ + rt2800_rfcsr_write_chanreg(rt2x00dev, 43, 0xD3); + rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xE3); + rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xE5); + rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x28); + rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x68); + rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xF7); + rt2800_rfcsr_write_chanreg(rt2x00dev, 58, 0x02); + rt2800_rfcsr_write_chanreg(rt2x00dev, 60, 0xC7); + + /* Initialize RF DC calibration register to default value */ + rt2800_rfcsr_write_dccal(rt2x00dev, 0, 0x47); + rt2800_rfcsr_write_dccal(rt2x00dev, 1, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 2, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 3, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 6, 0x10); + rt2800_rfcsr_write_dccal(rt2x00dev, 7, 0x10); + rt2800_rfcsr_write_dccal(rt2x00dev, 8, 0x04); + rt2800_rfcsr_write_dccal(rt2x00dev, 9, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 10, 0x07); + rt2800_rfcsr_write_dccal(rt2x00dev, 11, 0x01); + rt2800_rfcsr_write_dccal(rt2x00dev, 12, 0x07); + rt2800_rfcsr_write_dccal(rt2x00dev, 13, 0x07); + rt2800_rfcsr_write_dccal(rt2x00dev, 14, 0x07); + rt2800_rfcsr_write_dccal(rt2x00dev, 15, 0x20); + rt2800_rfcsr_write_dccal(rt2x00dev, 16, 0x22); + rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 18, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 19, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 20, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 21, 0xF1); + rt2800_rfcsr_write_dccal(rt2x00dev, 22, 0x11); + rt2800_rfcsr_write_dccal(rt2x00dev, 23, 0x02); + rt2800_rfcsr_write_dccal(rt2x00dev, 24, 0x41); + rt2800_rfcsr_write_dccal(rt2x00dev, 25, 0x20); + rt2800_rfcsr_write_dccal(rt2x00dev, 26, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 27, 0xD7); + rt2800_rfcsr_write_dccal(rt2x00dev, 28, 0xA2); + rt2800_rfcsr_write_dccal(rt2x00dev, 29, 0x20); + rt2800_rfcsr_write_dccal(rt2x00dev, 30, 0x49); + rt2800_rfcsr_write_dccal(rt2x00dev, 31, 0x20); + rt2800_rfcsr_write_dccal(rt2x00dev, 32, 0x04); + rt2800_rfcsr_write_dccal(rt2x00dev, 33, 0xF1); + rt2800_rfcsr_write_dccal(rt2x00dev, 34, 0xA1); + rt2800_rfcsr_write_dccal(rt2x00dev, 35, 0x01); + rt2800_rfcsr_write_dccal(rt2x00dev, 41, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 42, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 43, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 44, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 45, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 46, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 47, 0x3E); + rt2800_rfcsr_write_dccal(rt2x00dev, 48, 0x3D); + rt2800_rfcsr_write_dccal(rt2x00dev, 49, 0x3E); + rt2800_rfcsr_write_dccal(rt2x00dev, 50, 0x3D); + rt2800_rfcsr_write_dccal(rt2x00dev, 51, 0x3E); + rt2800_rfcsr_write_dccal(rt2x00dev, 52, 0x3D); + rt2800_rfcsr_write_dccal(rt2x00dev, 53, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 54, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 55, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 56, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 57, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 58, 0x10); + rt2800_rfcsr_write_dccal(rt2x00dev, 59, 0x10); + rt2800_rfcsr_write_dccal(rt2x00dev, 60, 0x0A); + rt2800_rfcsr_write_dccal(rt2x00dev, 61, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 62, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 63, 0x00); + + rt2800_rfcsr_write_dccal(rt2x00dev, 3, 0x08); + rt2800_rfcsr_write_dccal(rt2x00dev, 4, 0x04); + rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x20); + + rt2800_rfcsr_write_dccal(rt2x00dev, 5, 0x00); + rt2800_rfcsr_write_dccal(rt2x00dev, 17, 0x7C); + + rt2800_bw_filter_calibration(rt2x00dev, true); + rt2800_bw_filter_calibration(rt2x00dev, false); +} + static void rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) { if (rt2800_is_305x_soc(rt2x00dev)) { @@ -6941,6 +8279,9 @@ static void rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) case RT5592: rt2800_init_rfcsr_5592(rt2x00dev); break; + case RT6352: + rt2800_init_rfcsr_6352(rt2x00dev); + break; } } @@ -7307,7 +8648,8 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) */ if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) + rt2x00_rt(rt2x00dev, RT5392) || + rt2x00_rt(rt2x00dev, RT6352)) rt2800_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &rf); else if (rt2x00_rt(rt2x00dev, RT3352)) rf = RF3322; @@ -7339,6 +8681,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) case RF5390: case RF5392: case RF5592: + case RF7620: break; default: rt2x00_err(rt2x00dev, "Invalid RF chipset 0x%04x detected\n", @@ -7746,6 +9089,23 @@ static const struct rf_channel rf_vals_5592_xtal40[] = { {196, 83, 0, 12, 1}, }; +static const struct rf_channel rf_vals_7620[] = { + {1, 0x50, 0x99, 0x99, 1}, + {2, 0x50, 0x44, 0x44, 2}, + {3, 0x50, 0xEE, 0xEE, 2}, + {4, 0x50, 0x99, 0x99, 3}, + {5, 0x51, 0x44, 0x44, 0}, + {6, 0x51, 0xEE, 0xEE, 0}, + {7, 0x51, 0x99, 0x99, 1}, + {8, 0x51, 0x44, 0x44, 2}, + {9, 0x51, 0xEE, 0xEE, 2}, + {10, 0x51, 0x99, 0x99, 3}, + {11, 0x52, 0x44, 0x44, 0}, + {12, 0x52, 0xEE, 0xEE, 0}, + {13, 0x52, 0x99, 0x99, 1}, + {14, 0x52, 0x33, 0x33, 3}, +}; + static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; @@ -7849,6 +9209,11 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->channels = rf_vals_3x; break; + case RF7620: + spec->num_channels = ARRAY_SIZE(rf_vals_7620); + spec->channels = rf_vals_7620; + break; + case RF3052: case RF3053: spec->num_channels = ARRAY_SIZE(rf_vals_3x); @@ -7980,6 +9345,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) case RF5390: case RF5392: case RF5592: + case RF7620: __set_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags); break; } @@ -8024,6 +9390,9 @@ static int rt2800_probe_rt(struct rt2x00_dev *rt2x00dev) return -ENODEV; } + if (rt == RT5390 && rt2x00_is_soc(rt2x00dev)) + rt = RT6352; + rt2x00_set_rt(rt2x00dev, rt, rev); return 0; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h index d9ef260d542a..f357531d9488 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h @@ -33,6 +33,10 @@ struct rt2800_drv_data { u8 calibration_bw20; u8 calibration_bw40; + char rx_calibration_bw20; + char rx_calibration_bw40; + char tx_calibration_bw20; + char tx_calibration_bw40; u8 bbp25; u8 bbp26; u8 txmixer_gain_24g; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h index ce340bfd71a0..8fdd2f9726ee 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -174,6 +174,7 @@ struct rt2x00_chip { #define RT5390 0x5390 /* 2.4GHz */ #define RT5392 0x5392 /* 2.4GHz */ #define RT5592 0x5592 +#define RT6352 0x6352 /* WSOC 2.4GHz */ u16 rf; u16 rev; -- cgit From 00311de5fbf998877f2c3109688857a99f45fdd8 Mon Sep 17 00:00:00 2001 From: Andreas Pape Date: Mon, 5 Sep 2016 13:20:25 +0200 Subject: batman-adv: prevent multiple ARP replies sent by gateways if dat enabled If dat is enabled it must be made sure that only the backbone gw which has claimed the remote destination for the ARP request answers the ARP request directly if the MAC address is known due to the local dat table. This prevents multiple ARP replies in a common backbone if more than one gateway already knows the remote mac searched for in the ARP request. Signed-off-by: Andreas Pape Acked-by: Simon Wunderlich [sven@narfation.org: fix conflicts with current version] Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bridge_loop_avoidance.c | 49 ++++++++++++++++++++++++++++++++++ net/batman-adv/bridge_loop_avoidance.h | 11 ++++++++ net/batman-adv/distributed-arp-table.c | 15 +++++++++++ 3 files changed, 75 insertions(+) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 7332d284b0fc..546e66ecfabd 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -2449,3 +2449,52 @@ out: return ret; } + +#ifdef CONFIG_BATMAN_ADV_DAT +/** + * batadv_bla_check_claim - check if address is claimed + * + * @bat_priv: the bat priv with all the soft interface information + * @addr: mac address of which the claim status is checked + * @vid: the VLAN ID + * + * addr is checked if this address is claimed by the local device itself. + * + * Return: true if bla is disabled or the mac is claimed by the device, + * false if the device addr is already claimed by another gateway + */ +bool batadv_bla_check_claim(struct batadv_priv *bat_priv, + u8 *addr, unsigned short vid) +{ + struct batadv_bla_claim search_claim; + struct batadv_bla_claim *claim = NULL; + struct batadv_hard_iface *primary_if = NULL; + bool ret = true; + + if (!atomic_read(&bat_priv->bridge_loop_avoidance)) + return ret; + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + return ret; + + /* First look if the mac address is claimed */ + ether_addr_copy(search_claim.addr, addr); + search_claim.vid = vid; + + claim = batadv_claim_hash_find(bat_priv, &search_claim); + + /* If there is a claim and we are not owner of the claim, + * return false. + */ + if (claim) { + if (!batadv_compare_eth(claim->backbone_gw->orig, + primary_if->net_dev->dev_addr)) + ret = false; + batadv_claim_put(claim); + } + + batadv_hardif_put(primary_if); + return ret; +} +#endif diff --git a/net/batman-adv/bridge_loop_avoidance.h b/net/batman-adv/bridge_loop_avoidance.h index e157986bd01c..234775748b8e 100644 --- a/net/batman-adv/bridge_loop_avoidance.h +++ b/net/batman-adv/bridge_loop_avoidance.h @@ -69,6 +69,10 @@ void batadv_bla_status_update(struct net_device *net_dev); int batadv_bla_init(struct batadv_priv *bat_priv); void batadv_bla_free(struct batadv_priv *bat_priv); int batadv_bla_claim_dump(struct sk_buff *msg, struct netlink_callback *cb); +#ifdef CONFIG_BATMAN_ADV_DAT +bool batadv_bla_check_claim(struct batadv_priv *bat_priv, u8 *addr, + unsigned short vid); +#endif #define BATADV_BLA_CRC_INIT 0 #else /* ifdef CONFIG_BATMAN_ADV_BLA */ @@ -145,6 +149,13 @@ static inline int batadv_bla_backbone_dump(struct sk_buff *msg, return -EOPNOTSUPP; } +static inline +bool batadv_bla_check_claim(struct batadv_priv *bat_priv, u8 *addr, + unsigned short vid) +{ + return true; +} + #endif /* ifdef CONFIG_BATMAN_ADV_BLA */ #endif /* ifndef _NET_BATMAN_ADV_BLA_H_ */ diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 4f643fdb5bc6..28cfa53837e1 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -43,6 +43,7 @@ #include #include +#include "bridge_loop_avoidance.h" #include "hard-interface.h" #include "hash.h" #include "log.h" @@ -1040,6 +1041,20 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, goto out; } + /* If BLA is enabled, only send ARP replies if we have claimed + * the destination for the ARP request or if no one else of + * the backbone gws belonging to our backbone has claimed the + * destination. + */ + if (!batadv_bla_check_claim(bat_priv, + dat_entry->mac_addr, vid)) { + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "Device %pM claimed by another backbone gw. Don't send ARP reply!", + dat_entry->mac_addr); + ret = true; + goto out; + } + skb_new = batadv_dat_arp_create_reply(bat_priv, ip_dst, ip_src, dat_entry->mac_addr, hw_src, vid); -- cgit From 9aa5cd79b5abde1d0ebcff825726e242e0b7efea Mon Sep 17 00:00:00 2001 From: Andreas Pape Date: Mon, 5 Sep 2016 13:20:26 +0200 Subject: batman-adv: prevent duplication of ARP replies when DAT is used If none of the backbone gateways in a bla setup has already knowledge of the mac address searched for in an incoming ARP request from the backbone an address resolution via the DHT of DAT is started. The gateway can send several ARP requests to different DHT nodes and therefore can get several replies. This patch assures that not all of the possible ARP replies are returned to the backbone by checking the local DAT cache of the gateway. If there is an entry in the local cache the gateway has already learned the requested address and there is no need to forward the additional reply to the backbone. Furthermore it is checked if this gateway has claimed the source of the ARP reply and only forwards it to the backbone if it has claimed the source or if there is no claim at all. Signed-off-by: Andreas Pape Acked-by: Simon Wunderlich [sven@narfation.org: fix conflicts with current version] Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/distributed-arp-table.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 28cfa53837e1..77ede40ff529 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1203,6 +1203,7 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, struct sk_buff *skb, int hdr_size) { + struct batadv_dat_entry *dat_entry = NULL; u16 type; __be32 ip_src, ip_dst; u8 *hw_src, *hw_dst; @@ -1225,12 +1226,41 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, hw_dst = batadv_arp_hw_dst(skb, hdr_size); ip_dst = batadv_arp_ip_dst(skb, hdr_size); + /* If ip_dst is already in cache and has the right mac address, + * drop this frame if this ARP reply is destined for us because it's + * most probably an ARP reply generated by another node of the DHT. + * We have most probably received already a reply earlier. Delivering + * this frame would lead to doubled receive of an ARP reply. + */ + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_src, vid); + if (dat_entry && batadv_compare_eth(hw_src, dat_entry->mac_addr)) { + batadv_dbg(BATADV_DBG_DAT, bat_priv, "Doubled ARP reply removed: ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]; dat_entry: %pM-%pI4\n", + hw_src, &ip_src, hw_dst, &ip_dst, + dat_entry->mac_addr, &dat_entry->ip); + dropped = true; + goto out; + } + /* Update our internal cache with both the IP addresses the node got * within the ARP reply */ batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid); + /* If BLA is enabled, only forward ARP replies if we have claimed the + * source of the ARP reply or if no one else of the same backbone has + * already claimed that client. This prevents that different gateways + * to the same backbone all forward the ARP reply leading to multiple + * replies in the backbone. + */ + if (!batadv_bla_check_claim(bat_priv, hw_src, vid)) { + batadv_dbg(BATADV_DBG_DAT, bat_priv, + "Device %pM claimed by another backbone gw. Drop ARP reply.\n", + hw_src); + dropped = true; + goto out; + } + /* if this REPLY is directed to a client of mine, let's deliver the * packet to the interface */ @@ -1243,6 +1273,8 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, out: if (dropped) kfree_skb(skb); + if (dat_entry) + batadv_dat_entry_put(dat_entry); /* if dropped == false -> deliver to the interface */ return dropped; } -- cgit From 9e794b6bf4a2c65d698d7433ddfabc54a5d53a88 Mon Sep 17 00:00:00 2001 From: Andreas Pape Date: Mon, 5 Sep 2016 13:20:27 +0200 Subject: batman-adv: drop unicast packets from other backbone gw Additional dropping of unicast packets received from another backbone gw if the same backbone network before being forwarded to the same backbone again is necessary. It was observed in a test setup that in rare cases these frames lead to looping unicast traffic backbone->mesh->backbone. Signed-off-by: Andreas Pape Acked-by: Simon Wunderlich [sven@narfation.org: fix conflicts with current version] Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/routing.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 7fd740b6e36d..c85dc3102519 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -941,15 +941,17 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct batadv_unicast_packet *unicast_packet; struct batadv_unicast_4addr_packet *unicast_4addr_packet; - u8 *orig_addr; - struct batadv_orig_node *orig_node = NULL; + u8 *orig_addr, *orig_addr_gw; + struct batadv_orig_node *orig_node = NULL, *orig_node_gw = NULL; int check, hdr_size = sizeof(*unicast_packet); enum batadv_subtype subtype; - bool is4addr; + struct ethhdr *ethhdr; int ret = NET_RX_DROP; + bool is4addr, is_gw; unicast_packet = (struct batadv_unicast_packet *)skb->data; unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data; + ethhdr = eth_hdr(skb); is4addr = unicast_packet->packet_type == BATADV_UNICAST_4ADDR; /* the caller function should have already pulled 2 bytes */ @@ -972,6 +974,23 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, /* packet for me */ if (batadv_is_my_mac(bat_priv, unicast_packet->dest)) { + /* If this is a unicast packet from another backgone gw, + * drop it. + */ + orig_addr_gw = ethhdr->h_source; + orig_node_gw = batadv_orig_hash_find(bat_priv, orig_addr_gw); + if (orig_node_gw) { + is_gw = batadv_bla_is_backbone_gw(skb, orig_node_gw, + hdr_size); + batadv_orig_node_put(orig_node_gw); + if (is_gw) { + batadv_dbg(BATADV_DBG_BLA, bat_priv, + "Dropped unicast pkt received from another backbone gw %pM.\n", + orig_addr_gw); + return NET_RX_DROP; + } + } + if (is4addr) { subtype = unicast_4addr_packet->subtype; batadv_dat_inc_counter(bat_priv, subtype); -- cgit From 4dd72f73605412d81f94114a3eeb04fa60e29ae6 Mon Sep 17 00:00:00 2001 From: Andreas Pape Date: Mon, 5 Sep 2016 13:20:28 +0200 Subject: batman-adv: changed debug messages for easier bla debugging Some of the bla debug messages are extended and additional messages are added for easier bla debugging. Some debug messages introduced with the dat changes in prior patches of this patch series have been changed to be more compliant to other existing debug messages. Acked-by: Simon Wunderlich Signed-off-by: Andreas Pape [sven@narfation.org: fix conflicts with current version] Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bridge_loop_avoidance.c | 18 ++++++++++++++---- net/batman-adv/routing.c | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 546e66ecfabd..852a2be19845 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -739,8 +739,8 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, goto claim_free_ref; batadv_dbg(BATADV_DBG_BLA, bat_priv, - "bla_add_claim(): changing ownership for %pM, vid %d\n", - mac, batadv_print_vid(vid)); + "bla_add_claim(): changing ownership for %pM, vid %d to gw %pM\n", + mac, batadv_print_vid(vid), backbone_gw->orig); remove_crc = true; } @@ -1295,10 +1295,13 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv, goto skip; batadv_dbg(BATADV_DBG_BLA, bat_priv, - "bla_purge_claims(): %pM, vid %d, time out\n", - claim->addr, claim->vid); + "bla_purge_claims(): timed out.\n"); purge_now: + batadv_dbg(BATADV_DBG_BLA, bat_priv, + "bla_purge_claims(): %pM, vid %d\n", + claim->addr, claim->vid); + batadv_handle_unclaim(bat_priv, primary_if, backbone_gw->orig, claim->addr, claim->vid); @@ -1846,6 +1849,13 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, /* possible optimization: race for a claim */ /* No claim exists yet, claim it for us! */ + + batadv_dbg(BATADV_DBG_BLA, bat_priv, + "bla_rx(): Unclaimed MAC %pM found. Claim it. Local: %s\n", + ethhdr->h_source, + batadv_is_my_client(bat_priv, + ethhdr->h_source, vid) ? + "yes" : "no"); batadv_handle_claim(bat_priv, primary_if, primary_if->net_dev->dev_addr, ethhdr->h_source, vid); diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index c85dc3102519..e1ebe14ee2a6 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -985,7 +985,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, batadv_orig_node_put(orig_node_gw); if (is_gw) { batadv_dbg(BATADV_DBG_BLA, bat_priv, - "Dropped unicast pkt received from another backbone gw %pM.\n", + "recv_unicast_packet(): Dropped unicast pkt received from another backbone gw %pM.\n", orig_addr_gw); return NET_RX_DROP; } -- cgit From a3a5129e122709306cfa6409781716c2933df99b Mon Sep 17 00:00:00 2001 From: Andreas Pape Date: Mon, 5 Sep 2016 13:20:29 +0200 Subject: batman-adv: handle race condition for claims between gateways Consider the following situation which has been found in a test setup: Gateway B has claimed client C and gateway A has the same backbone network as B. C sends a broad- or multicast to B and directly after this packet decides to send another packet to A due to a better TQ value. B will forward the broad-/multicast into the backbone as it is the responsible gw and after that A will claim C as it has been chosen by C as the best gateway. If it now happens that A claims C before it has received the broad-/multicast forwarded by B (due to backbone topology or due to some delay in B when forwarding the packet) we get a critical situation: in the current code A will immediately unclaim C when receiving the multicast due to the roaming client scenario although the position of C has not changed in the mesh. If this happens the multi-/broadcast forwarded by B will be sent back into the mesh by A and we have looping packets until one of the gateways claims C again. In order to prevent this, unclaiming of a client due to the roaming client scenario is only done after a certain time is expired after the last claim of the client. 100 ms are used here, which should be slow enough for big backbones and slow gateways but fast enough not to break the roaming client use case. Acked-by: Simon Wunderlich Signed-off-by: Andreas Pape [sven@narfation.org: fix conflicts with current version] Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bridge_loop_avoidance.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 852a2be19845..d07e89ec8467 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -1973,10 +1973,22 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, /* if yes, the client has roamed and we have * to unclaim it. */ - batadv_handle_unclaim(bat_priv, primary_if, - primary_if->net_dev->dev_addr, - ethhdr->h_source, vid); - goto allow; + if (batadv_has_timed_out(claim->lasttime, 100)) { + /* only unclaim if the last claim entry is + * older than 100 ms to make sure we really + * have a roaming client here. + */ + batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_tx(): Roaming client %pM detected. Unclaim it.\n", + ethhdr->h_source); + batadv_handle_unclaim(bat_priv, primary_if, + primary_if->net_dev->dev_addr, + ethhdr->h_source, vid); + goto allow; + } else { + batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_tx(): Race for claim %pM detected. Drop packet.\n", + ethhdr->h_source); + goto handled; + } } /* check if it is a multicast/broadcast frame */ -- cgit From 7b8f7a402d4cfc3a1361a2766066127f9bccadc4 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Sun, 19 Mar 2017 22:01:28 -0700 Subject: neighbour: fix nlmsg_pid in notifications neigh notifications today carry pid 0 for nlmsg_pid in all cases. This patch fixes it to carry calling process pid when available. Applications (eg. quagga) rely on nlmsg_pid to ignore notifications generated by their own netlink operations. This patch follows the routing subsystem which already sets this correctly. Reported-by: Vivek Venkatraman Signed-off-by: Roopa Prabhu Signed-off-by: David S. Miller --- include/net/neighbour.h | 3 ++- net/atm/clip.c | 4 ++-- net/core/neighbour.c | 32 ++++++++++++++++++-------------- net/ipv4/arp.c | 6 +++--- net/ipv6/ndisc.c | 2 +- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 5ebf69491160..9496179c7b4e 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -314,7 +314,8 @@ static inline struct neighbour *neigh_create(struct neigh_table *tbl, } void neigh_destroy(struct neighbour *neigh); int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb); -int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, u32 flags); +int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, u32 flags, + u32 nlmsg_pid); void __neigh_set_probe_once(struct neighbour *neigh); void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev); int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev); diff --git a/net/atm/clip.c b/net/atm/clip.c index 53b4ac09e7b7..ec527b62f79d 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -106,7 +106,7 @@ static void unlink_clip_vcc(struct clip_vcc *clip_vcc) entry->expires = jiffies - 1; /* force resolution or expiration */ error = neigh_update(entry->neigh, NULL, NUD_NONE, - NEIGH_UPDATE_F_ADMIN); + NEIGH_UPDATE_F_ADMIN, 0); if (error) pr_crit("neigh_update failed with %d\n", error); goto out; @@ -481,7 +481,7 @@ static int clip_setentry(struct atm_vcc *vcc, __be32 ip) link_vcc(clip_vcc, entry); } error = neigh_update(neigh, llc_oui, NUD_PERMANENT, - NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN); + NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN, 0); neigh_release(neigh); return error; } diff --git a/net/core/neighbour.c b/net/core/neighbour.c index e7c12caa20c8..7069f5e4a361 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -52,8 +52,9 @@ do { \ #define PNEIGH_HASHMASK 0xF static void neigh_timer_handler(unsigned long arg); -static void __neigh_notify(struct neighbour *n, int type, int flags); -static void neigh_update_notify(struct neighbour *neigh); +static void __neigh_notify(struct neighbour *n, int type, int flags, + u32 pid); +static void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid); static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev); #ifdef CONFIG_PROC_FS @@ -99,7 +100,7 @@ static void neigh_cleanup_and_release(struct neighbour *neigh) if (neigh->parms->neigh_cleanup) neigh->parms->neigh_cleanup(neigh); - __neigh_notify(neigh, RTM_DELNEIGH, 0); + __neigh_notify(neigh, RTM_DELNEIGH, 0, 0); call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh); neigh_release(neigh); } @@ -948,7 +949,7 @@ out: } if (notify) - neigh_update_notify(neigh); + neigh_update_notify(neigh, 0); neigh_release(neigh); } @@ -1072,7 +1073,7 @@ static void neigh_update_hhs(struct neighbour *neigh) */ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, - u32 flags) + u32 flags, u32 nlmsg_pid) { u8 old; int err; @@ -1229,7 +1230,7 @@ out: write_unlock_bh(&neigh->lock); if (notify) - neigh_update_notify(neigh); + neigh_update_notify(neigh, nlmsg_pid); return err; } @@ -1260,7 +1261,7 @@ struct neighbour *neigh_event_ns(struct neigh_table *tbl, lladdr || !dev->addr_len); if (neigh) neigh_update(neigh, lladdr, NUD_STALE, - NEIGH_UPDATE_F_OVERRIDE); + NEIGH_UPDATE_F_OVERRIDE, 0); return neigh; } EXPORT_SYMBOL(neigh_event_ns); @@ -1638,7 +1639,8 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh) err = neigh_update(neigh, NULL, NUD_FAILED, NEIGH_UPDATE_F_OVERRIDE | - NEIGH_UPDATE_F_ADMIN); + NEIGH_UPDATE_F_ADMIN, + NETLINK_CB(skb).portid); neigh_release(neigh); out: @@ -1729,7 +1731,8 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh) neigh_event_send(neigh, NULL); err = 0; } else - err = neigh_update(neigh, lladdr, ndm->ndm_state, flags); + err = neigh_update(neigh, lladdr, ndm->ndm_state, flags, + NETLINK_CB(skb).portid); neigh_release(neigh); out: @@ -2229,10 +2232,10 @@ nla_put_failure: return -EMSGSIZE; } -static void neigh_update_notify(struct neighbour *neigh) +static void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid) { call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh); - __neigh_notify(neigh, RTM_NEWNEIGH, 0); + __neigh_notify(neigh, RTM_NEWNEIGH, 0, nlmsg_pid); } static bool neigh_master_filtered(struct net_device *dev, int master_idx) @@ -2830,7 +2833,8 @@ static inline size_t neigh_nlmsg_size(void) + nla_total_size(4); /* NDA_PROBES */ } -static void __neigh_notify(struct neighbour *n, int type, int flags) +static void __neigh_notify(struct neighbour *n, int type, int flags, + u32 pid) { struct net *net = dev_net(n->dev); struct sk_buff *skb; @@ -2840,7 +2844,7 @@ static void __neigh_notify(struct neighbour *n, int type, int flags) if (skb == NULL) goto errout; - err = neigh_fill_info(skb, n, 0, 0, type, flags); + err = neigh_fill_info(skb, n, pid, 0, type, flags); if (err < 0) { /* -EMSGSIZE implies BUG in neigh_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); @@ -2856,7 +2860,7 @@ errout: void neigh_app_ns(struct neighbour *n) { - __neigh_notify(n, RTM_GETNEIGH, NLM_F_REQUEST); + __neigh_notify(n, RTM_GETNEIGH, NLM_F_REQUEST, 0); } EXPORT_SYMBOL(neigh_app_ns); diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 51b27ae09fbd..0937b34c27ca 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -872,7 +872,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb) skb->pkt_type != PACKET_HOST) state = NUD_STALE; neigh_update(n, sha, state, - override ? NEIGH_UPDATE_F_OVERRIDE : 0); + override ? NEIGH_UPDATE_F_OVERRIDE : 0, 0); neigh_release(n); } @@ -1033,7 +1033,7 @@ static int arp_req_set(struct net *net, struct arpreq *r, err = neigh_update(neigh, (r->arp_flags & ATF_COM) ? r->arp_ha.sa_data : NULL, state, NEIGH_UPDATE_F_OVERRIDE | - NEIGH_UPDATE_F_ADMIN); + NEIGH_UPDATE_F_ADMIN, 0); neigh_release(neigh); } return err; @@ -1084,7 +1084,7 @@ static int arp_invalidate(struct net_device *dev, __be32 ip) if (neigh->nud_state & ~NUD_NOARP) err = neigh_update(neigh, NULL, NUD_FAILED, NEIGH_UPDATE_F_OVERRIDE| - NEIGH_UPDATE_F_ADMIN); + NEIGH_UPDATE_F_ADMIN, 0); neigh_release(neigh); } diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 7ebac630d3c6..112ccbc0a8ac 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -732,7 +732,7 @@ void ndisc_update(const struct net_device *dev, struct neighbour *neigh, const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type, struct ndisc_options *ndopts) { - neigh_update(neigh, lladdr, new, flags); + neigh_update(neigh, lladdr, new, flags, 0); /* report ndisc ops about neighbour update */ ndisc_ops_update(dev, neigh, flags, icmp6_type, ndopts); } -- cgit From 424fa00ea325b0153c321667f39643f68ae9d1b0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 20 Mar 2017 09:51:12 +0100 Subject: net: dwc-xlgmac: include dcbnl.h Without this header, we can run into a build error: drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c: In function 'xlgmac_config_queue_mapping': drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c:1548:36: error: 'IEEE_8021QAZ_MAX_TCS' undeclared (first use in this function) prio_queues = min_t(unsigned int, IEEE_8021QAZ_MAX_TCS, Fixes: 65e0ace2c5cd ("net: dwc-xlgmac: Initial driver for DesignWare Enterprise Ethernet") Signed-off-by: Arnd Bergmann Reviewed-by: Jie Deng Signed-off-by: David S. Miller --- drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c index 5cf3e90d4834..1e25a86f6a27 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "dwc-xlgmac.h" #include "dwc-xlgmac-reg.h" -- cgit From 3588f29e061cef19ac0092e4f6917717fed5b1d4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 20 Mar 2017 09:51:13 +0100 Subject: net: dwc-xlgmac: add module license When building the driver as a module, we get a warning about the lack of a license: WARNING: modpost: missing MODULE_LICENSE() in drivers/net/ethernet/synopsys/dwc-xlgmac.o see include/linux/module.h for more information Curiously the text in the .c files only mentions GPLv2+, while the license tag in the PCI driver contains both GPL and BSD. I picked the license text as the more definite reference here and put a GPL tag in there. Fixes: 65e0ace2c5cd ("net: dwc-xlgmac: Initial driver for DesignWare Enterprise Ethernet") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/ethernet/synopsys/dwc-xlgmac-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c index 726d78ac4907..b72196ab647f 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c @@ -25,6 +25,7 @@ static int debug = -1; module_param(debug, int, 0644); +MODULE_LICENSE("GPL"); MODULE_PARM_DESC(debug, "DWC ethernet debug level (0=none,...,16=all)"); static const u32 default_msg_level = (NETIF_MSG_LINK | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP); -- cgit From bb58d07964f2f09e133b46541447c567a7306dc1 Mon Sep 17 00:00:00 2001 From: Arjun Vynipadath Date: Mon, 20 Mar 2017 14:22:38 +0530 Subject: cxgb4: Update IngPad and IngPack values We are using the smallest padding boundary (8 bytes), which isn't smaller than the Memory Controller Read/Write Size We get best performance in 100G when the Packing Boundary is a multiple of the Maximum Payload Size. Its related to inefficient chopping of DMA packets by PCIe, that causes more overhead on bus. So driver is helping by making the starting address alignment to be MPS size. We will try to determine PCIE MaxPayloadSize capabiltiy and set IngPackBoundary based on this value. If cache line size is greater than MPS or determinig MPS fails, we will use cache line size to determine IngPackBoundary(as before). Signed-off-by: Arjun Vynipadath Signed-off-by: Casey Leedom Signed-off-by: Ganesh Goudar Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/t4_hw.c | 75 +++++++++++++++++++------- drivers/net/ethernet/chelsio/cxgb4/t4_values.h | 4 ++ 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 87000cd39737..0de8eb72325c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -6369,7 +6369,6 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, unsigned int stat_len = cache_line_size > 64 ? 128 : 64; unsigned int fl_align = cache_line_size < 32 ? 32 : cache_line_size; unsigned int fl_align_log = fls(fl_align) - 1; - unsigned int ingpad; t4_write_reg(adap, SGE_HOST_PAGE_SIZE_A, HOSTPAGESIZEPF0_V(sge_hps) | @@ -6389,6 +6388,10 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, INGPADBOUNDARY_SHIFT_X) | EGRSTATUSPAGESIZE_V(stat_len != 64)); } else { + unsigned int pack_align; + unsigned int ingpad, ingpack; + unsigned int pcie_cap; + /* T5 introduced the separation of the Free List Padding and * Packing Boundaries. Thus, we can select a smaller Padding * Boundary to avoid uselessly chewing up PCIe Link and Memory @@ -6401,27 +6404,62 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, * Size (the minimum unit of transfer to/from Memory). If we * have a Padding Boundary which is smaller than the Memory * Line Size, that'll involve a Read-Modify-Write cycle on the - * Memory Controller which is never good. For T5 the smallest - * Padding Boundary which we can select is 32 bytes which is - * larger than any known Memory Controller Line Size so we'll - * use that. - * - * T5 has a different interpretation of the "0" value for the - * Packing Boundary. This corresponds to 16 bytes instead of - * the expected 32 bytes. We never have a Packing Boundary - * less than 32 bytes so we can't use that special value but - * on the other hand, if we wanted 32 bytes, the best we can - * really do is 64 bytes. - */ - if (fl_align <= 32) { + * Memory Controller which is never good. + */ + + /* We want the Packing Boundary to be based on the Cache Line + * Size in order to help avoid False Sharing performance + * issues between CPUs, etc. We also want the Packing + * Boundary to incorporate the PCI-E Maximum Payload Size. We + * get best performance when the Packing Boundary is a + * multiple of the Maximum Payload Size. + */ + pack_align = fl_align; + pcie_cap = pci_find_capability(adap->pdev, PCI_CAP_ID_EXP); + if (pcie_cap) { + unsigned int mps, mps_log; + u16 devctl; + + /* The PCIe Device Control Maximum Payload Size field + * [bits 7:5] encodes sizes as powers of 2 starting at + * 128 bytes. + */ + pci_read_config_word(adap->pdev, + pcie_cap + PCI_EXP_DEVCTL, + &devctl); + mps_log = ((devctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5) + 7; + mps = 1 << mps_log; + if (mps > pack_align) + pack_align = mps; + } + + /* N.B. T5/T6 have a crazy special interpretation of the "0" + * value for the Packing Boundary. This corresponds to 16 + * bytes instead of the expected 32 bytes. So if we want 32 + * bytes, the best we can really do is 64 bytes ... + */ + if (pack_align <= 16) { + ingpack = INGPACKBOUNDARY_16B_X; + fl_align = 16; + } else if (pack_align == 32) { + ingpack = INGPACKBOUNDARY_64B_X; fl_align = 64; - fl_align_log = 6; + } else { + unsigned int pack_align_log = fls(pack_align) - 1; + + ingpack = pack_align_log - INGPACKBOUNDARY_SHIFT_X; + fl_align = pack_align; } + /* Use the smallest Ingress Padding which isn't smaller than + * the Memory Controller Read/Write Size. We'll take that as + * being 8 bytes since we don't know of any system with a + * wider Memory Controller Bus Width. + */ if (is_t5(adap->params.chip)) - ingpad = INGPCIEBOUNDARY_32B_X; + ingpad = INGPADBOUNDARY_32B_X; else - ingpad = T6_INGPADBOUNDARY_32B_X; + ingpad = T6_INGPADBOUNDARY_8B_X; t4_set_reg_field(adap, SGE_CONTROL_A, INGPADBOUNDARY_V(INGPADBOUNDARY_M) | @@ -6430,8 +6468,7 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, EGRSTATUSPAGESIZE_V(stat_len != 64)); t4_set_reg_field(adap, SGE_CONTROL2_A, INGPACKBOUNDARY_V(INGPACKBOUNDARY_M), - INGPACKBOUNDARY_V(fl_align_log - - INGPACKBOUNDARY_SHIFT_X)); + INGPACKBOUNDARY_V(ingpack)); } /* * Adjust various SGE Free List Host Buffer Sizes. diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h index 36cf3073ca37..f6558cbfc54e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h @@ -54,11 +54,15 @@ #define INGPADBOUNDARY_SHIFT_X 5 #define T6_INGPADBOUNDARY_SHIFT_X 3 +#define T6_INGPADBOUNDARY_8B_X 0 #define T6_INGPADBOUNDARY_32B_X 2 +#define INGPADBOUNDARY_32B_X 0 + /* CONTROL2 register */ #define INGPACKBOUNDARY_SHIFT_X 5 #define INGPACKBOUNDARY_16B_X 0 +#define INGPACKBOUNDARY_64B_X 1 /* GTS register */ #define SGE_TIMERREGS 6 -- cgit From c7cd4c9bf8df87027e739fe66d0a55951f6875d8 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 20 Mar 2017 11:37:22 +0000 Subject: mlxsw: spectrum: fix swapped order of arguments packets and bytes The arguments packets and bytes to call mlxsw_sp_acl_rule_get_stats are in the wrong order. Fix this by swapping them. Detected by CoverityScan, CID#1419705 ("Arguments in wrong order") Fixes: 7c1b8eb175b69add8ea ("mlxsw: spectrum: Add support for TC flower offload statistics") Signed-off-by: Colin Ian King Acked-by: Ido Schimmel Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index e724c6266247..3e7a0bcbba72 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -375,7 +375,7 @@ int mlxsw_sp_flower_stats(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, if (!rule) return -EINVAL; - err = mlxsw_sp_acl_rule_get_stats(mlxsw_sp, rule, &bytes, &packets, + err = mlxsw_sp_acl_rule_get_stats(mlxsw_sp, rule, &packets, &bytes, &lastuse); if (err) goto err_rule_get_stats; -- cgit From a2d133b1d465016d0d97560b11f54ba0ace56d3e Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Mon, 20 Mar 2017 15:22:03 -0400 Subject: sock: introduce SO_MEMINFO getsockopt Allows reading of SK_MEMINFO_VARS via socket option. This way an application can get all meminfo related information in single socket option call instead of multiple calls. Adds helper function, sk_get_meminfo(), and uses that for both getsockopt and sock_diag_put_meminfo(). Suggested by Eric Dumazet. Signed-off-by: Josh Hunt Reviewed-by: Jason Baron Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- arch/alpha/include/uapi/asm/socket.h | 2 ++ arch/avr32/include/uapi/asm/socket.h | 2 ++ arch/frv/include/uapi/asm/socket.h | 2 ++ arch/ia64/include/uapi/asm/socket.h | 2 ++ arch/m32r/include/uapi/asm/socket.h | 2 ++ arch/mips/include/uapi/asm/socket.h | 3 +++ arch/mn10300/include/uapi/asm/socket.h | 2 ++ arch/parisc/include/uapi/asm/socket.h | 2 ++ arch/powerpc/include/uapi/asm/socket.h | 2 ++ arch/s390/include/uapi/asm/socket.h | 2 ++ arch/sparc/include/uapi/asm/socket.h | 2 ++ arch/xtensa/include/uapi/asm/socket.h | 2 ++ include/net/sock.h | 2 ++ include/uapi/asm-generic/socket.h | 2 ++ net/core/sock.c | 30 ++++++++++++++++++++++++++++++ net/core/sock_diag.c | 10 +--------- 16 files changed, 60 insertions(+), 9 deletions(-) diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h index afc901b7a6f6..089db42c1b40 100644 --- a/arch/alpha/include/uapi/asm/socket.h +++ b/arch/alpha/include/uapi/asm/socket.h @@ -99,4 +99,6 @@ #define SCM_TIMESTAMPING_OPT_STATS 54 +#define SO_MEMINFO 55 + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/avr32/include/uapi/asm/socket.h b/arch/avr32/include/uapi/asm/socket.h index 5a650426f357..6eabcbd2f82a 100644 --- a/arch/avr32/include/uapi/asm/socket.h +++ b/arch/avr32/include/uapi/asm/socket.h @@ -92,4 +92,6 @@ #define SCM_TIMESTAMPING_OPT_STATS 54 +#define SO_MEMINFO 55 + #endif /* _UAPI__ASM_AVR32_SOCKET_H */ diff --git a/arch/frv/include/uapi/asm/socket.h b/arch/frv/include/uapi/asm/socket.h index 81e03530ed39..bd497f8356b9 100644 --- a/arch/frv/include/uapi/asm/socket.h +++ b/arch/frv/include/uapi/asm/socket.h @@ -92,5 +92,7 @@ #define SCM_TIMESTAMPING_OPT_STATS 54 +#define SO_MEMINFO 55 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h index 57feb0c1f7d7..f1bb54686168 100644 --- a/arch/ia64/include/uapi/asm/socket.h +++ b/arch/ia64/include/uapi/asm/socket.h @@ -101,4 +101,6 @@ #define SCM_TIMESTAMPING_OPT_STATS 54 +#define SO_MEMINFO 55 + #endif /* _ASM_IA64_SOCKET_H */ diff --git a/arch/m32r/include/uapi/asm/socket.h b/arch/m32r/include/uapi/asm/socket.h index 5853f8e92c20..459c46076f6f 100644 --- a/arch/m32r/include/uapi/asm/socket.h +++ b/arch/m32r/include/uapi/asm/socket.h @@ -92,4 +92,6 @@ #define SCM_TIMESTAMPING_OPT_STATS 54 +#define SO_MEMINFO 55 + #endif /* _ASM_M32R_SOCKET_H */ diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h index 566ecdcb5b4b..688c18dd62ef 100644 --- a/arch/mips/include/uapi/asm/socket.h +++ b/arch/mips/include/uapi/asm/socket.h @@ -110,4 +110,7 @@ #define SCM_TIMESTAMPING_OPT_STATS 54 +#define SO_MEMINFO 55 + + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/mn10300/include/uapi/asm/socket.h b/arch/mn10300/include/uapi/asm/socket.h index 0e12527c4b0e..312d2c457a04 100644 --- a/arch/mn10300/include/uapi/asm/socket.h +++ b/arch/mn10300/include/uapi/asm/socket.h @@ -92,4 +92,6 @@ #define SCM_TIMESTAMPING_OPT_STATS 54 +#define SO_MEMINFO 55 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h index 7a109b73ddf7..b98ec38f2083 100644 --- a/arch/parisc/include/uapi/asm/socket.h +++ b/arch/parisc/include/uapi/asm/socket.h @@ -91,4 +91,6 @@ #define SCM_TIMESTAMPING_OPT_STATS 0x402F +#define SO_MEMINFO 0x4030 + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/powerpc/include/uapi/asm/socket.h b/arch/powerpc/include/uapi/asm/socket.h index 44583a52f882..099a889240f6 100644 --- a/arch/powerpc/include/uapi/asm/socket.h +++ b/arch/powerpc/include/uapi/asm/socket.h @@ -99,4 +99,6 @@ #define SCM_TIMESTAMPING_OPT_STATS 54 +#define SO_MEMINFO 55 + #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h index b24a64cbfeb1..6199bb34f7fa 100644 --- a/arch/s390/include/uapi/asm/socket.h +++ b/arch/s390/include/uapi/asm/socket.h @@ -98,4 +98,6 @@ #define SCM_TIMESTAMPING_OPT_STATS 54 +#define SO_MEMINFO 55 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h index a25dc32f5d6a..12cd8c2ec422 100644 --- a/arch/sparc/include/uapi/asm/socket.h +++ b/arch/sparc/include/uapi/asm/socket.h @@ -88,6 +88,8 @@ #define SCM_TIMESTAMPING_OPT_STATS 0x0038 +#define SO_MEMINFO 0x0039 + /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 #define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002 diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h index 9fdbe1fe0473..d0b85f6c1484 100644 --- a/arch/xtensa/include/uapi/asm/socket.h +++ b/arch/xtensa/include/uapi/asm/socket.h @@ -103,4 +103,6 @@ #define SCM_TIMESTAMPING_OPT_STATS 54 +#define SO_MEMINFO 55 + #endif /* _XTENSA_SOCKET_H */ diff --git a/include/net/sock.h b/include/net/sock.h index 08142be8938e..cb241a0e8434 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2362,6 +2362,8 @@ bool sk_ns_capable(const struct sock *sk, bool sk_capable(const struct sock *sk, int cap); bool sk_net_capable(const struct sock *sk, int cap); +void sk_get_meminfo(const struct sock *sk, u32 *meminfo); + extern __u32 sysctl_wmem_max; extern __u32 sysctl_rmem_max; diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h index 2c748ddad5f8..8313702c1eae 100644 --- a/include/uapi/asm-generic/socket.h +++ b/include/uapi/asm-generic/socket.h @@ -94,4 +94,6 @@ #define SCM_TIMESTAMPING_OPT_STATS 54 +#define SO_MEMINFO 55 + #endif /* __ASM_GENERIC_SOCKET_H */ diff --git a/net/core/sock.c b/net/core/sock.c index a83731c36761..f8c0373a3a74 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1313,6 +1313,21 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.val = sk->sk_incoming_cpu; break; + case SO_MEMINFO: + { + u32 meminfo[SK_MEMINFO_VARS]; + + if (get_user(len, optlen)) + return -EFAULT; + + sk_get_meminfo(sk, meminfo); + + len = min_t(unsigned int, len, sizeof(meminfo)); + if (copy_to_user(optval, &meminfo, len)) + return -EFAULT; + + goto lenout; + } default: /* We implement the SO_SNDLOWAT etc to not be settable * (1003.1g 7). @@ -2861,6 +2876,21 @@ void sk_common_release(struct sock *sk) } EXPORT_SYMBOL(sk_common_release); +void sk_get_meminfo(const struct sock *sk, u32 *mem) +{ + memset(mem, 0, sizeof(*mem) * SK_MEMINFO_VARS); + + mem[SK_MEMINFO_RMEM_ALLOC] = sk_rmem_alloc_get(sk); + mem[SK_MEMINFO_RCVBUF] = sk->sk_rcvbuf; + mem[SK_MEMINFO_WMEM_ALLOC] = sk_wmem_alloc_get(sk); + mem[SK_MEMINFO_SNDBUF] = sk->sk_sndbuf; + mem[SK_MEMINFO_FWD_ALLOC] = sk->sk_forward_alloc; + mem[SK_MEMINFO_WMEM_QUEUED] = sk->sk_wmem_queued; + mem[SK_MEMINFO_OPTMEM] = atomic_read(&sk->sk_omem_alloc); + mem[SK_MEMINFO_BACKLOG] = sk->sk_backlog.len; + mem[SK_MEMINFO_DROPS] = atomic_read(&sk->sk_drops); +} + #ifdef CONFIG_PROC_FS #define PROTO_INUSE_NR 64 /* should be enough for the first time */ struct prot_inuse { diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index 6b10573cc9fa..8d11ee75a100 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -59,15 +59,7 @@ int sock_diag_put_meminfo(struct sock *sk, struct sk_buff *skb, int attrtype) { u32 mem[SK_MEMINFO_VARS]; - mem[SK_MEMINFO_RMEM_ALLOC] = sk_rmem_alloc_get(sk); - mem[SK_MEMINFO_RCVBUF] = sk->sk_rcvbuf; - mem[SK_MEMINFO_WMEM_ALLOC] = sk_wmem_alloc_get(sk); - mem[SK_MEMINFO_SNDBUF] = sk->sk_sndbuf; - mem[SK_MEMINFO_FWD_ALLOC] = sk->sk_forward_alloc; - mem[SK_MEMINFO_WMEM_QUEUED] = sk->sk_wmem_queued; - mem[SK_MEMINFO_OPTMEM] = atomic_read(&sk->sk_omem_alloc); - mem[SK_MEMINFO_BACKLOG] = sk->sk_backlog.len; - mem[SK_MEMINFO_DROPS] = atomic_read(&sk->sk_drops); + sk_get_meminfo(sk, mem); return nla_put(skb, attrtype, sizeof(mem), &mem); } -- cgit From dcdd43c41e60d7618ad54369d77ee39f122d41e4 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 20 Mar 2017 11:19:44 -0700 Subject: net: vrf: performance improvements for IPv4 The VRF driver allows users to implement device based features for an entire domain. For example, a qdisc or netfilter rules can be attached to a VRF device or tcpdump can be used to view packets for all devices in the L3 domain. The device-based features come with a performance penalty, most notably in the Tx path. The VRF driver uses the l3mdev_l3_out hook to switch the dst on an skb to its private dst. This allows the skb to traverse the xmit stack with the device set to the VRF device which in turn enables the netfilter and qdisc features. The VRF driver then performs the FIB lookup again and reinserts the packet. This patch avoids the redirect for IPv4 packets if a qdisc has not been attached to a VRF device which is the default config. In this case the netfilter hooks and network taps are directly traversed in the l3mdev_l3_out handler. If a qdisc is attached to a VRF device, then the redirect using the vrf dst is done. Additional overhead is removed by only checking packet taps if a socket is open on the device (vrf_dev->ptype_all list is not empty). Packet sockets bound to any device will still get a copy of the packet via the real ingress or egress interface. The end result of this change is a decrease in the overhead of VRF for the default, baseline case (ie., no netfilter rules, no packet sockets, no qdisc) to ~3% for UDP which has a lookup per packet and < 1% overhead for connected sockets that leverage early demux and avoid FIB lookups. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/vrf.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 10 deletions(-) diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 7f28021d9d93..cdf7253ae89e 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -104,6 +104,23 @@ static void vrf_get_stats64(struct net_device *dev, } } +/* by default VRF devices do not have a qdisc and are expected + * to be created with only a single queue. + */ +static bool qdisc_tx_is_default(const struct net_device *dev) +{ + struct netdev_queue *txq; + struct Qdisc *qdisc; + + if (dev->num_tx_queues > 1) + return false; + + txq = netdev_get_tx_queue(dev, 0); + qdisc = rcu_access_pointer(txq->qdisc); + + return !qdisc->enqueue; +} + /* Local traffic destined to local address. Reinsert the packet to rx * path, similar to loopback handling. */ @@ -357,6 +374,29 @@ static netdev_tx_t vrf_xmit(struct sk_buff *skb, struct net_device *dev) return ret; } +static int vrf_finish_direct(struct net *net, struct sock *sk, + struct sk_buff *skb) +{ + struct net_device *vrf_dev = skb->dev; + + if (!list_empty(&vrf_dev->ptype_all) && + likely(skb_headroom(skb) >= ETH_HLEN)) { + struct ethhdr *eth = (struct ethhdr *)skb_push(skb, ETH_HLEN); + + ether_addr_copy(eth->h_source, vrf_dev->dev_addr); + eth_zero_addr(eth->h_dest); + eth->h_proto = skb->protocol; + + rcu_read_lock_bh(); + dev_queue_xmit_nit(skb, vrf_dev); + rcu_read_unlock_bh(); + + skb_pull(skb, ETH_HLEN); + } + + return 1; +} + #if IS_ENABLED(CONFIG_IPV6) /* modelled after ip6_finish_output2 */ static int vrf_finish_output6(struct net *net, struct sock *sk, @@ -607,18 +647,13 @@ static int vrf_output(struct net *net, struct sock *sk, struct sk_buff *skb) * packet to go through device based features such as qdisc, netfilter * hooks and packet sockets with skb->dev set to vrf device. */ -static struct sk_buff *vrf_ip_out(struct net_device *vrf_dev, - struct sock *sk, - struct sk_buff *skb) +static struct sk_buff *vrf_ip_out_redirect(struct net_device *vrf_dev, + struct sk_buff *skb) { struct net_vrf *vrf = netdev_priv(vrf_dev); struct dst_entry *dst = NULL; struct rtable *rth; - /* don't divert multicast */ - if (ipv4_is_multicast(ip_hdr(skb)->daddr)) - return skb; - rcu_read_lock(); rth = rcu_dereference(vrf->rth); @@ -640,6 +675,55 @@ static struct sk_buff *vrf_ip_out(struct net_device *vrf_dev, return skb; } +static int vrf_output_direct(struct net *net, struct sock *sk, + struct sk_buff *skb) +{ + skb->protocol = htons(ETH_P_IP); + + return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, + net, sk, skb, NULL, skb->dev, + vrf_finish_direct, + !(IPCB(skb)->flags & IPSKB_REROUTED)); +} + +static struct sk_buff *vrf_ip_out_direct(struct net_device *vrf_dev, + struct sock *sk, + struct sk_buff *skb) +{ + struct net *net = dev_net(vrf_dev); + int err; + + skb->dev = vrf_dev; + + err = nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, net, sk, + skb, NULL, vrf_dev, vrf_output_direct); + + if (likely(err == 1)) + err = vrf_output_direct(net, sk, skb); + + /* reset skb device */ + if (likely(err == 1)) + nf_reset(skb); + else + skb = NULL; + + return skb; +} + +static struct sk_buff *vrf_ip_out(struct net_device *vrf_dev, + struct sock *sk, + struct sk_buff *skb) +{ + /* don't divert multicast */ + if (ipv4_is_multicast(ip_hdr(skb)->daddr)) + return skb; + + if (qdisc_tx_is_default(vrf_dev)) + return vrf_ip_out_direct(vrf_dev, sk, skb); + + return vrf_ip_out_redirect(vrf_dev, skb); +} + /* called with rcu lock held */ static struct sk_buff *vrf_l3_out(struct net_device *vrf_dev, struct sock *sk, @@ -1023,9 +1107,11 @@ static struct sk_buff *vrf_ip_rcv(struct net_device *vrf_dev, vrf_rx_stats(vrf_dev, skb->len); - skb_push(skb, skb->mac_len); - dev_queue_xmit_nit(skb, vrf_dev); - skb_pull(skb, skb->mac_len); + if (!list_empty(&vrf_dev->ptype_all)) { + skb_push(skb, skb->mac_len); + dev_queue_xmit_nit(skb, vrf_dev); + skb_pull(skb, skb->mac_len); + } skb = vrf_rcv_nfhook(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, vrf_dev); out: -- cgit From a9ec54d1b0cdfd94eda44c7d5d1ce9e8ede1e402 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 20 Mar 2017 11:19:45 -0700 Subject: net: vrf: performance improvements for IPv6 The VRF driver allows users to implement device based features for an entire domain. For example, a qdisc or netfilter rules can be attached to a VRF device or tcpdump can be used to view packets for all devices in the L3 domain. The device-based features come with a performance penalty, most notably in the Tx path. The VRF driver uses the l3mdev_l3_out hook to switch the dst on an skb to its private dst. This allows the skb to traverse the xmit stack with the device set to the VRF device which in turn enables the netfilter and qdisc features. The VRF driver then performs the FIB lookup again and reinserts the packet. This patch avoids the redirect for IPv6 packets if a qdisc has not been attached to a VRF device which is the default config. In this case the netfilter hooks and network taps are directly traversed in the l3mdev_l3_out handler. If a qdisc is attached to a VRF device, then the redirect using the vrf dst is done. Additional overhead is removed by only checking packet taps if a socket is open on the device (vrf_dev->ptype_all list is not empty). Packet sockets bound to any device will still get a copy of the packet via the real ingress or egress interface. The end result of this change is a decrease in the overhead of VRF for the default, baseline case (ie., no netfilter rules, no packet sockets, no qdisc) from a +3% improvement for UDP which has a lookup per packet (VRF being better than no l3mdev) to ~2% loss for TCP_CRR which connects a socket for each request-response. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/vrf.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index cdf7253ae89e..2c40cced3c86 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -445,18 +445,13 @@ static int vrf_output6(struct net *net, struct sock *sk, struct sk_buff *skb) * packet to go through device based features such as qdisc, netfilter * hooks and packet sockets with skb->dev set to vrf device. */ -static struct sk_buff *vrf_ip6_out(struct net_device *vrf_dev, - struct sock *sk, - struct sk_buff *skb) +static struct sk_buff *vrf_ip6_out_redirect(struct net_device *vrf_dev, + struct sk_buff *skb) { struct net_vrf *vrf = netdev_priv(vrf_dev); struct dst_entry *dst = NULL; struct rt6_info *rt6; - /* don't divert link scope packets */ - if (rt6_need_strict(&ipv6_hdr(skb)->daddr)) - return skb; - rcu_read_lock(); rt6 = rcu_dereference(vrf->rt6); @@ -478,6 +473,55 @@ static struct sk_buff *vrf_ip6_out(struct net_device *vrf_dev, return skb; } +static int vrf_output6_direct(struct net *net, struct sock *sk, + struct sk_buff *skb) +{ + skb->protocol = htons(ETH_P_IPV6); + + return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING, + net, sk, skb, NULL, skb->dev, + vrf_finish_direct, + !(IPCB(skb)->flags & IPSKB_REROUTED)); +} + +static struct sk_buff *vrf_ip6_out_direct(struct net_device *vrf_dev, + struct sock *sk, + struct sk_buff *skb) +{ + struct net *net = dev_net(vrf_dev); + int err; + + skb->dev = vrf_dev; + + err = nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, + skb, NULL, vrf_dev, vrf_output6_direct); + + if (likely(err == 1)) + err = vrf_output6_direct(net, sk, skb); + + /* reset skb device */ + if (likely(err == 1)) + nf_reset(skb); + else + skb = NULL; + + return skb; +} + +static struct sk_buff *vrf_ip6_out(struct net_device *vrf_dev, + struct sock *sk, + struct sk_buff *skb) +{ + /* don't divert link scope packets */ + if (rt6_need_strict(&ipv6_hdr(skb)->daddr)) + return skb; + + if (qdisc_tx_is_default(vrf_dev)) + return vrf_ip6_out_direct(vrf_dev, sk, skb); + + return vrf_ip6_out_redirect(vrf_dev, skb); +} + /* holding rtnl */ static void vrf_rt6_release(struct net_device *dev, struct net_vrf *vrf) { @@ -1064,9 +1108,11 @@ static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev, skb->dev = vrf_dev; skb->skb_iif = vrf_dev->ifindex; - skb_push(skb, skb->mac_len); - dev_queue_xmit_nit(skb, vrf_dev); - skb_pull(skb, skb->mac_len); + if (!list_empty(&vrf_dev->ptype_all)) { + skb_push(skb, skb->mac_len); + dev_queue_xmit_nit(skb, vrf_dev); + skb_pull(skb, skb->mac_len); + } IP6CB(skb)->flags |= IP6SKB_L3SLAVE; } -- cgit From 47c697aa2d07f4bf258e7ad53cdb6c77c339b843 Mon Sep 17 00:00:00 2001 From: andy zhou Date: Mon, 20 Mar 2017 16:32:27 -0700 Subject: openvswitch: Deferred fifo API change. add_deferred_actions() API currently requires actions to be passed in as a fully encoded netlink message. So far both 'sample' and 'recirc' actions happens to carry actions as fully encoded netlink messages. However, this requirement is more restrictive than necessary, future patch will need to pass in action lists that are not fully encoded by themselves. Signed-off-by: Andy Zhou Acked-by: Joe Stringer Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/openvswitch/actions.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index c82301ce3fff..75182e91ef45 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -51,6 +51,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, struct deferred_action { struct sk_buff *skb; const struct nlattr *actions; + int actions_len; /* Store pkt_key clone when creating deferred action. */ struct sw_flow_key pkt_key; @@ -119,8 +120,9 @@ static struct deferred_action *action_fifo_put(struct action_fifo *fifo) /* Return true if fifo is not full */ static struct deferred_action *add_deferred_actions(struct sk_buff *skb, - const struct sw_flow_key *key, - const struct nlattr *attr) + const struct sw_flow_key *key, + const struct nlattr *actions, + const int actions_len) { struct action_fifo *fifo; struct deferred_action *da; @@ -129,7 +131,8 @@ static struct deferred_action *add_deferred_actions(struct sk_buff *skb, da = action_fifo_put(fifo); if (da) { da->skb = skb; - da->actions = attr; + da->actions = actions; + da->actions_len = actions_len; da->pkt_key = *key; } @@ -966,7 +969,8 @@ static int sample(struct datapath *dp, struct sk_buff *skb, /* Skip the sample action when out of memory. */ return 0; - if (!add_deferred_actions(skb, key, a)) { + if (!add_deferred_actions(skb, key, nla_data(acts_list), + nla_len(acts_list))) { if (net_ratelimit()) pr_warn("%s: deferred actions limit reached, dropping sample action\n", ovs_dp_name(dp)); @@ -1123,7 +1127,7 @@ static int execute_recirc(struct datapath *dp, struct sk_buff *skb, return 0; } - da = add_deferred_actions(skb, key, NULL); + da = add_deferred_actions(skb, key, NULL, 0); if (da) { da->pkt_key.recirc_id = nla_get_u32(a); } else { @@ -1278,10 +1282,10 @@ static void process_deferred_actions(struct datapath *dp) struct sk_buff *skb = da->skb; struct sw_flow_key *key = &da->pkt_key; const struct nlattr *actions = da->actions; + int actions_len = da->actions_len; if (actions) - do_execute_actions(dp, skb, key, actions, - nla_len(actions)); + do_execute_actions(dp, skb, key, actions, actions_len); else ovs_dp_process_packet(skb, key); } while (!action_fifo_is_empty(fifo)); -- cgit From 4572ef52a00bf671fa0fb5a85ee75b1af30cc18b Mon Sep 17 00:00:00 2001 From: andy zhou Date: Mon, 20 Mar 2017 16:32:28 -0700 Subject: openvswitch: Refactor recirc key allocation. The logic of allocating and copy key for each 'exec_actions_level' was specific to execute_recirc(). However, future patches will reuse as well. Refactor the logic into its own function clone_key(). Signed-off-by: Andy Zhou Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/openvswitch/actions.c | 66 ++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 75182e91ef45..8c9c60cd359f 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2014 Nicira, Inc. + * Copyright (c) 2007-2017 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -83,14 +83,31 @@ struct action_fifo { struct deferred_action fifo[DEFERRED_ACTION_FIFO_SIZE]; }; -struct recirc_keys { +struct action_flow_keys { struct sw_flow_key key[OVS_DEFERRED_ACTION_THRESHOLD]; }; static struct action_fifo __percpu *action_fifos; -static struct recirc_keys __percpu *recirc_keys; +static struct action_flow_keys __percpu *flow_keys; static DEFINE_PER_CPU(int, exec_actions_level); +/* Make a clone of the 'key', using the pre-allocated percpu 'flow_keys' + * space. Return NULL if out of key spaces. + */ +static struct sw_flow_key *clone_key(const struct sw_flow_key *key_) +{ + struct action_flow_keys *keys = this_cpu_ptr(flow_keys); + int level = this_cpu_read(exec_actions_level); + struct sw_flow_key *key = NULL; + + if (level <= OVS_DEFERRED_ACTION_THRESHOLD) { + key = &keys->key[level - 1]; + *key = *key_; + } + + return key; +} + static void action_fifo_init(struct action_fifo *fifo) { fifo->head = 0; @@ -1090,8 +1107,8 @@ static int execute_recirc(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, const struct nlattr *a, int rem) { + struct sw_flow_key *recirc_key; struct deferred_action *da; - int level; if (!is_flow_key_valid(key)) { int err; @@ -1115,29 +1132,26 @@ static int execute_recirc(struct datapath *dp, struct sk_buff *skb, return 0; } - level = this_cpu_read(exec_actions_level); - if (level <= OVS_DEFERRED_ACTION_THRESHOLD) { - struct recirc_keys *rks = this_cpu_ptr(recirc_keys); - struct sw_flow_key *recirc_key = &rks->key[level - 1]; - - *recirc_key = *key; + /* If within the limit of 'OVS_DEFERRED_ACTION_THRESHOLD', + * recirc immediately, otherwise, defer it for later execution. + */ + recirc_key = clone_key(key); + if (recirc_key) { recirc_key->recirc_id = nla_get_u32(a); ovs_dp_process_packet(skb, recirc_key); - - return 0; - } - - da = add_deferred_actions(skb, key, NULL, 0); - if (da) { - da->pkt_key.recirc_id = nla_get_u32(a); } else { - kfree_skb(skb); - - if (net_ratelimit()) - pr_warn("%s: deferred action limit reached, drop recirc action\n", - ovs_dp_name(dp)); + da = add_deferred_actions(skb, key, NULL, 0); + if (da) { + recirc_key = &da->pkt_key; + recirc_key->recirc_id = nla_get_u32(a); + } else { + /* Log an error in case action fifo is full. */ + kfree_skb(skb); + if (net_ratelimit()) + pr_warn("%s: deferred action limit reached, drop recirc action\n", + ovs_dp_name(dp)); + } } - return 0; } @@ -1327,8 +1341,8 @@ int action_fifos_init(void) if (!action_fifos) return -ENOMEM; - recirc_keys = alloc_percpu(struct recirc_keys); - if (!recirc_keys) { + flow_keys = alloc_percpu(struct action_flow_keys); + if (!flow_keys) { free_percpu(action_fifos); return -ENOMEM; } @@ -1339,5 +1353,5 @@ int action_fifos_init(void) void action_fifos_exit(void) { free_percpu(action_fifos); - free_percpu(recirc_keys); + free_percpu(flow_keys); } -- cgit From 798c166173ffb50128993641fcf791df51bed48e Mon Sep 17 00:00:00 2001 From: andy zhou Date: Mon, 20 Mar 2017 16:32:29 -0700 Subject: openvswitch: Optimize sample action for the clone use cases With the introduction of open flow 'clone' action, the OVS user space can now translate the 'clone' action into kernel datapath 'sample' action, with 100% probability, to ensure that the clone semantics, which is that the packet seen by the clone action is the same as the packet seen by the action after clone, is faithfully carried out in the datapath. While the sample action in the datpath has the matching semantics, its implementation is only optimized for its original use. Specifically, there are two limitation: First, there is a 3 level of nesting restriction, enforced at the flow downloading time. This limit turns out to be too restrictive for the 'clone' use case. Second, the implementation avoid recursive call only if the sample action list has a single userspace action. The main optimization implemented in this series removes the static nesting limit check, instead, implement the run time recursion limit check, and recursion avoidance similar to that of the 'recirc' action. This optimization solve both #1 and #2 issues above. One related optimization attempts to avoid copying flow key as long as the actions enclosed does not change the flow key. The detection is performed only once at the flow downloading time. Another related optimization is to rewrite the action list at flow downloading time in order to save the fast path from parsing the sample action list in its original form repeatedly. Signed-off-by: Andy Zhou Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- include/uapi/linux/openvswitch.h | 15 +++++ net/openvswitch/actions.c | 107 ++++++++++++++--------------- net/openvswitch/datapath.h | 2 - net/openvswitch/flow_netlink.c | 141 +++++++++++++++++++++++++++------------ 4 files changed, 167 insertions(+), 98 deletions(-) diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 7f41f7d0000f..66d1c3ccfd8e 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -578,10 +578,25 @@ enum ovs_sample_attr { OVS_SAMPLE_ATTR_PROBABILITY, /* u32 number */ OVS_SAMPLE_ATTR_ACTIONS, /* Nested OVS_ACTION_ATTR_* attributes. */ __OVS_SAMPLE_ATTR_MAX, + +#ifdef __KERNEL__ + OVS_SAMPLE_ATTR_ARG /* struct sample_arg */ +#endif }; #define OVS_SAMPLE_ATTR_MAX (__OVS_SAMPLE_ATTR_MAX - 1) +#ifdef __KERNEL__ +struct sample_arg { + bool exec; /* When true, actions in sample will not + * change flow keys. False otherwise. + */ + u32 probability; /* Same value as + * 'OVS_SAMPLE_ATTR_PROBABILITY'. + */ +}; +#endif + /** * enum ovs_userspace_attr - Attributes for %OVS_ACTION_ATTR_USERSPACE action. * @OVS_USERSPACE_ATTR_PID: u32 Netlink PID to which the %OVS_PACKET_CMD_ACTION diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 8c9c60cd359f..3529f7b87a44 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -928,73 +928,70 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb, return ovs_dp_upcall(dp, skb, key, &upcall, cutlen); } +/* When 'last' is true, sample() should always consume the 'skb'. + * Otherwise, sample() should keep 'skb' intact regardless what + * actions are executed within sample(). + */ static int sample(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, const struct nlattr *attr, - const struct nlattr *actions, int actions_len) + bool last) { - const struct nlattr *acts_list = NULL; - const struct nlattr *a; - int rem; - u32 cutlen = 0; + struct nlattr *actions; + struct nlattr *sample_arg; + struct sw_flow_key *orig_key = key; + int rem = nla_len(attr); + int err = 0; + const struct sample_arg *arg; - for (a = nla_data(attr), rem = nla_len(attr); rem > 0; - a = nla_next(a, &rem)) { - u32 probability; + /* The first action is always 'OVS_SAMPLE_ATTR_ARG'. */ + sample_arg = nla_data(attr); + arg = nla_data(sample_arg); + actions = nla_next(sample_arg, &rem); - switch (nla_type(a)) { - case OVS_SAMPLE_ATTR_PROBABILITY: - probability = nla_get_u32(a); - if (!probability || prandom_u32() > probability) - return 0; - break; - - case OVS_SAMPLE_ATTR_ACTIONS: - acts_list = a; - break; - } + if ((arg->probability != U32_MAX) && + (!arg->probability || prandom_u32() > arg->probability)) { + if (last) + consume_skb(skb); + return 0; } - rem = nla_len(acts_list); - a = nla_data(acts_list); - - /* Actions list is empty, do nothing */ - if (unlikely(!rem)) + /* Unless the last action, sample works on the clone of SKB. */ + skb = last ? skb : skb_clone(skb, GFP_ATOMIC); + if (!skb) { + /* Out of memory, skip this sample action. + */ return 0; + } - /* The only known usage of sample action is having a single user-space - * action, or having a truncate action followed by a single user-space - * action. Treat this usage as a special case. - * The output_userspace() should clone the skb to be sent to the - * user space. This skb will be consumed by its caller. + /* In case the sample actions won't change 'key', + * it can be used directly to execute sample actions. + * Otherwise, allocate a new key from the + * next recursion level of 'flow_keys'. If + * successful, execute the sample actions without + * deferring. + * + * Defer the sample actions if the recursion + * limit has been reached. */ - if (unlikely(nla_type(a) == OVS_ACTION_ATTR_TRUNC)) { - struct ovs_action_trunc *trunc = nla_data(a); - - if (skb->len > trunc->max_len) - cutlen = skb->len - trunc->max_len; - - a = nla_next(a, &rem); + if (!arg->exec) { + __this_cpu_inc(exec_actions_level); + key = clone_key(key); } - if (likely(nla_type(a) == OVS_ACTION_ATTR_USERSPACE && - nla_is_last(a, rem))) - return output_userspace(dp, skb, key, a, actions, - actions_len, cutlen); + if (key) { + err = do_execute_actions(dp, skb, key, actions, rem); + } else if (!add_deferred_actions(skb, orig_key, actions, rem)) { - skb = skb_clone(skb, GFP_ATOMIC); - if (!skb) - /* Skip the sample action when out of memory. */ - return 0; - - if (!add_deferred_actions(skb, key, nla_data(acts_list), - nla_len(acts_list))) { if (net_ratelimit()) - pr_warn("%s: deferred actions limit reached, dropping sample action\n", + pr_warn("%s: deferred action limit reached, drop sample action\n", ovs_dp_name(dp)); - kfree_skb(skb); } - return 0; + + if (!arg->exec) + __this_cpu_dec(exec_actions_level); + + return err; } static void execute_hash(struct sk_buff *skb, struct sw_flow_key *key, @@ -1244,9 +1241,15 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, err = execute_masked_set_action(skb, key, nla_data(a)); break; - case OVS_ACTION_ATTR_SAMPLE: - err = sample(dp, skb, key, a, attr, len); + case OVS_ACTION_ATTR_SAMPLE: { + bool last = nla_is_last(a, rem); + + err = sample(dp, skb, key, a, last); + if (last) + return err; + break; + } case OVS_ACTION_ATTR_CT: if (!is_flow_key_valid(key)) { diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 1c6e9377436d..da931bdef8a7 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -34,8 +34,6 @@ #define DP_MAX_PORTS USHRT_MAX #define DP_VPORT_HASH_BUCKETS 1024 -#define SAMPLE_ACTION_DEPTH 3 - /** * struct dp_stats_percpu - per-cpu packet processing statistics for a given * datapath. diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 6f5fa50f716d..2acfb5af2c45 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2014 Nicira, Inc. + * Copyright (c) 2007-2017 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -59,6 +59,39 @@ struct ovs_len_tbl { #define OVS_ATTR_NESTED -1 #define OVS_ATTR_VARIABLE -2 +static bool actions_may_change_flow(const struct nlattr *actions) +{ + struct nlattr *nla; + int rem; + + nla_for_each_nested(nla, actions, rem) { + u16 action = nla_type(nla); + + switch (action) { + case OVS_ACTION_ATTR_OUTPUT: + case OVS_ACTION_ATTR_RECIRC: + case OVS_ACTION_ATTR_TRUNC: + case OVS_ACTION_ATTR_USERSPACE: + break; + + case OVS_ACTION_ATTR_CT: + case OVS_ACTION_ATTR_HASH: + case OVS_ACTION_ATTR_POP_ETH: + case OVS_ACTION_ATTR_POP_MPLS: + case OVS_ACTION_ATTR_POP_VLAN: + case OVS_ACTION_ATTR_PUSH_ETH: + case OVS_ACTION_ATTR_PUSH_MPLS: + case OVS_ACTION_ATTR_PUSH_VLAN: + case OVS_ACTION_ATTR_SAMPLE: + case OVS_ACTION_ATTR_SET: + case OVS_ACTION_ATTR_SET_MASKED: + default: + return true; + } + } + return false; +} + static void update_range(struct sw_flow_match *match, size_t offset, size_t size, bool is_mask) { @@ -2021,18 +2054,20 @@ static inline void add_nested_action_end(struct sw_flow_actions *sfa, static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, const struct sw_flow_key *key, - int depth, struct sw_flow_actions **sfa, + struct sw_flow_actions **sfa, __be16 eth_type, __be16 vlan_tci, bool log); static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, - const struct sw_flow_key *key, int depth, + const struct sw_flow_key *key, struct sw_flow_actions **sfa, - __be16 eth_type, __be16 vlan_tci, bool log) + __be16 eth_type, __be16 vlan_tci, + bool log, bool last) { const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1]; const struct nlattr *probability, *actions; const struct nlattr *a; - int rem, start, err, st_acts; + int rem, start, err; + struct sample_arg arg; memset(attrs, 0, sizeof(attrs)); nla_for_each_nested(a, attr, rem) { @@ -2056,20 +2091,32 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SAMPLE, log); if (start < 0) return start; - err = ovs_nla_add_action(sfa, OVS_SAMPLE_ATTR_PROBABILITY, - nla_data(probability), sizeof(u32), log); + + /* When both skb and flow may be changed, put the sample + * into a deferred fifo. On the other hand, if only skb + * may be modified, the actions can be executed in place. + * + * Do this analysis at the flow installation time. + * Set 'clone_action->exec' to true if the actions can be + * executed without being deferred. + * + * If the sample is the last action, it can always be excuted + * rather than deferred. + */ + arg.exec = last || !actions_may_change_flow(actions); + arg.probability = nla_get_u32(probability); + + err = ovs_nla_add_action(sfa, OVS_SAMPLE_ATTR_ARG, &arg, sizeof(arg), + log); if (err) return err; - st_acts = add_nested_action_start(sfa, OVS_SAMPLE_ATTR_ACTIONS, log); - if (st_acts < 0) - return st_acts; - err = __ovs_nla_copy_actions(net, actions, key, depth + 1, sfa, + err = __ovs_nla_copy_actions(net, actions, key, sfa, eth_type, vlan_tci, log); + if (err) return err; - add_nested_action_end(*sfa, st_acts); add_nested_action_end(*sfa, start); return 0; @@ -2406,16 +2453,13 @@ static int copy_action(const struct nlattr *from, static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, const struct sw_flow_key *key, - int depth, struct sw_flow_actions **sfa, + struct sw_flow_actions **sfa, __be16 eth_type, __be16 vlan_tci, bool log) { u8 mac_proto = ovs_key_mac_proto(key); const struct nlattr *a; int rem, err; - if (depth >= SAMPLE_ACTION_DEPTH) - return -EOVERFLOW; - nla_for_each_nested(a, attr, rem) { /* Expected argument lengths, (u32)-1 for variable length. */ static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = { @@ -2553,13 +2597,17 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, return err; break; - case OVS_ACTION_ATTR_SAMPLE: - err = validate_and_copy_sample(net, a, key, depth, sfa, - eth_type, vlan_tci, log); + case OVS_ACTION_ATTR_SAMPLE: { + bool last = nla_is_last(a, rem); + + err = validate_and_copy_sample(net, a, key, sfa, + eth_type, vlan_tci, + log, last); if (err) return err; skip_copy = true; break; + } case OVS_ACTION_ATTR_CT: err = ovs_ct_copy_action(net, a, key, sfa, log); @@ -2613,7 +2661,7 @@ int ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, return PTR_ERR(*sfa); (*sfa)->orig_len = nla_len(attr); - err = __ovs_nla_copy_actions(net, attr, key, 0, sfa, key->eth.type, + err = __ovs_nla_copy_actions(net, attr, key, sfa, key->eth.type, key->eth.vlan.tci, log); if (err) ovs_nla_free_flow_actions(*sfa); @@ -2621,39 +2669,44 @@ int ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, return err; } -static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb) +static int sample_action_to_attr(const struct nlattr *attr, + struct sk_buff *skb) { - const struct nlattr *a; - struct nlattr *start; - int err = 0, rem; + struct nlattr *start, *ac_start = NULL, *sample_arg; + int err = 0, rem = nla_len(attr); + const struct sample_arg *arg; + struct nlattr *actions; start = nla_nest_start(skb, OVS_ACTION_ATTR_SAMPLE); if (!start) return -EMSGSIZE; - nla_for_each_nested(a, attr, rem) { - int type = nla_type(a); - struct nlattr *st_sample; + sample_arg = nla_data(attr); + arg = nla_data(sample_arg); + actions = nla_next(sample_arg, &rem); - switch (type) { - case OVS_SAMPLE_ATTR_PROBABILITY: - if (nla_put(skb, OVS_SAMPLE_ATTR_PROBABILITY, - sizeof(u32), nla_data(a))) - return -EMSGSIZE; - break; - case OVS_SAMPLE_ATTR_ACTIONS: - st_sample = nla_nest_start(skb, OVS_SAMPLE_ATTR_ACTIONS); - if (!st_sample) - return -EMSGSIZE; - err = ovs_nla_put_actions(nla_data(a), nla_len(a), skb); - if (err) - return err; - nla_nest_end(skb, st_sample); - break; - } + if (nla_put_u32(skb, OVS_SAMPLE_ATTR_PROBABILITY, arg->probability)) { + err = -EMSGSIZE; + goto out; + } + + ac_start = nla_nest_start(skb, OVS_SAMPLE_ATTR_ACTIONS); + if (!ac_start) { + err = -EMSGSIZE; + goto out; + } + + err = ovs_nla_put_actions(actions, rem, skb); + +out: + if (err) { + nla_nest_cancel(skb, ac_start); + nla_nest_cancel(skb, start); + } else { + nla_nest_end(skb, ac_start); + nla_nest_end(skb, start); } - nla_nest_end(skb, start); return err; } -- cgit From bef7f7567a104a0b4dba5f51b8c12ce28947144b Mon Sep 17 00:00:00 2001 From: andy zhou Date: Mon, 20 Mar 2017 16:32:30 -0700 Subject: Openvswitch: Refactor sample and recirc actions implementation Added clone_execute() that both the sample and the recirc action implementation can use. Signed-off-by: Andy Zhou Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/openvswitch/actions.c | 176 ++++++++++++++++++++++++---------------------- 1 file changed, 93 insertions(+), 83 deletions(-) diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 3529f7b87a44..e4610676299b 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -44,10 +44,6 @@ #include "conntrack.h" #include "vport.h" -static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, - struct sw_flow_key *key, - const struct nlattr *attr, int len); - struct deferred_action { struct sk_buff *skb; const struct nlattr *actions; @@ -166,6 +162,12 @@ static bool is_flow_key_valid(const struct sw_flow_key *key) return !(key->mac_proto & SW_FLOW_KEY_INVALID); } +static int clone_execute(struct datapath *dp, struct sk_buff *skb, + struct sw_flow_key *key, + u32 recirc_id, + const struct nlattr *actions, int len, + bool last, bool clone_flow_key); + static void update_ethertype(struct sk_buff *skb, struct ethhdr *hdr, __be16 ethertype) { @@ -938,10 +940,9 @@ static int sample(struct datapath *dp, struct sk_buff *skb, { struct nlattr *actions; struct nlattr *sample_arg; - struct sw_flow_key *orig_key = key; int rem = nla_len(attr); - int err = 0; const struct sample_arg *arg; + bool clone_flow_key; /* The first action is always 'OVS_SAMPLE_ATTR_ARG'. */ sample_arg = nla_data(attr); @@ -955,43 +956,9 @@ static int sample(struct datapath *dp, struct sk_buff *skb, return 0; } - /* Unless the last action, sample works on the clone of SKB. */ - skb = last ? skb : skb_clone(skb, GFP_ATOMIC); - if (!skb) { - /* Out of memory, skip this sample action. - */ - return 0; - } - - /* In case the sample actions won't change 'key', - * it can be used directly to execute sample actions. - * Otherwise, allocate a new key from the - * next recursion level of 'flow_keys'. If - * successful, execute the sample actions without - * deferring. - * - * Defer the sample actions if the recursion - * limit has been reached. - */ - if (!arg->exec) { - __this_cpu_inc(exec_actions_level); - key = clone_key(key); - } - - if (key) { - err = do_execute_actions(dp, skb, key, actions, rem); - } else if (!add_deferred_actions(skb, orig_key, actions, rem)) { - - if (net_ratelimit()) - pr_warn("%s: deferred action limit reached, drop sample action\n", - ovs_dp_name(dp)); - kfree_skb(skb); - } - - if (!arg->exec) - __this_cpu_dec(exec_actions_level); - - return err; + clone_flow_key = !arg->exec; + return clone_execute(dp, skb, key, 0, actions, rem, last, + clone_flow_key); } static void execute_hash(struct sk_buff *skb, struct sw_flow_key *key, @@ -1102,10 +1069,9 @@ static int execute_masked_set_action(struct sk_buff *skb, static int execute_recirc(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, - const struct nlattr *a, int rem) + const struct nlattr *a, bool last) { - struct sw_flow_key *recirc_key; - struct deferred_action *da; + u32 recirc_id; if (!is_flow_key_valid(key)) { int err; @@ -1116,40 +1082,8 @@ static int execute_recirc(struct datapath *dp, struct sk_buff *skb, } BUG_ON(!is_flow_key_valid(key)); - if (!nla_is_last(a, rem)) { - /* Recirc action is the not the last action - * of the action list, need to clone the skb. - */ - skb = skb_clone(skb, GFP_ATOMIC); - - /* Skip the recirc action when out of memory, but - * continue on with the rest of the action list. - */ - if (!skb) - return 0; - } - - /* If within the limit of 'OVS_DEFERRED_ACTION_THRESHOLD', - * recirc immediately, otherwise, defer it for later execution. - */ - recirc_key = clone_key(key); - if (recirc_key) { - recirc_key->recirc_id = nla_get_u32(a); - ovs_dp_process_packet(skb, recirc_key); - } else { - da = add_deferred_actions(skb, key, NULL, 0); - if (da) { - recirc_key = &da->pkt_key; - recirc_key->recirc_id = nla_get_u32(a); - } else { - /* Log an error in case action fifo is full. */ - kfree_skb(skb); - if (net_ratelimit()) - pr_warn("%s: deferred action limit reached, drop recirc action\n", - ovs_dp_name(dp)); - } - } - return 0; + recirc_id = nla_get_u32(a); + return clone_execute(dp, skb, key, recirc_id, NULL, 0, last, true); } /* Execute a list of actions against 'skb'. */ @@ -1221,9 +1155,11 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, err = pop_vlan(skb, key); break; - case OVS_ACTION_ATTR_RECIRC: - err = execute_recirc(dp, skb, key, a, rem); - if (nla_is_last(a, rem)) { + case OVS_ACTION_ATTR_RECIRC: { + bool last = nla_is_last(a, rem); + + err = execute_recirc(dp, skb, key, a, last); + if (last) { /* If this is the last action, the skb has * been consumed or freed. * Return immediately. @@ -1231,6 +1167,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, return err; } break; + } case OVS_ACTION_ATTR_SET: err = execute_set_action(skb, key, nla_data(a)); @@ -1285,6 +1222,79 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, return 0; } +/* Execute the actions on the clone of the packet. The effect of the + * execution does not affect the original 'skb' nor the original 'key'. + * + * The execution may be deferred in case the actions can not be executed + * immediately. + */ +static int clone_execute(struct datapath *dp, struct sk_buff *skb, + struct sw_flow_key *key, u32 recirc_id, + const struct nlattr *actions, int len, + bool last, bool clone_flow_key) +{ + struct deferred_action *da; + struct sw_flow_key *clone; + + skb = last ? skb : skb_clone(skb, GFP_ATOMIC); + if (!skb) { + /* Out of memory, skip this action. + */ + return 0; + } + + /* When clone_flow_key is false, the 'key' will not be change + * by the actions, then the 'key' can be used directly. + * Otherwise, try to clone key from the next recursion level of + * 'flow_keys'. If clone is successful, execute the actions + * without deferring. + */ + clone = clone_flow_key ? clone_key(key) : key; + if (clone) { + int err = 0; + + if (actions) { /* Sample action */ + if (clone_flow_key) + __this_cpu_inc(exec_actions_level); + + err = do_execute_actions(dp, skb, clone, + actions, len); + + if (clone_flow_key) + __this_cpu_dec(exec_actions_level); + } else { /* Recirc action */ + clone->recirc_id = recirc_id; + ovs_dp_process_packet(skb, clone); + } + return err; + } + + /* Out of 'flow_keys' space. Defer actions */ + da = add_deferred_actions(skb, key, actions, len); + if (da) { + if (!actions) { /* Recirc action */ + key = &da->pkt_key; + key->recirc_id = recirc_id; + } + } else { + /* Out of per CPU action FIFO space. Drop the 'skb' and + * log an error. + */ + kfree_skb(skb); + + if (net_ratelimit()) { + if (actions) { /* Sample action */ + pr_warn("%s: deferred action limit reached, drop sample action\n", + ovs_dp_name(dp)); + } else { /* Recirc action */ + pr_warn("%s: deferred action limit reached, drop recirc action\n", + ovs_dp_name(dp)); + } + } + } + return 0; +} + static void process_deferred_actions(struct datapath *dp) { struct action_fifo *fifo = this_cpu_ptr(action_fifos); -- cgit From cfc62d878f8d436e6a2a99cef5559a7a98c43b3c Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Tue, 21 Mar 2017 09:28:03 +0800 Subject: net: tcp: Permit user set TCP_MAXSEG to default value When user_mss is zero, it means use the default value. But the current codes don't permit user set TCP_MAXSEG to the default value. It would return the -EINVAL when val is zero. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index cf4555581282..eccec53ef100 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2470,7 +2470,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level, /* Values greater than interface MTU won't take effect. However * at the point when this call is done we typically don't yet * know which interface is going to be used */ - if (val < TCP_MIN_MSS || val > MAX_TCP_WINDOW) { + if (val && (val < TCP_MIN_MSS || val > MAX_TCP_WINDOW)) { err = -EINVAL; break; } -- cgit From 58ad3198342bda5eeb97c070dde76335bf0b7203 Mon Sep 17 00:00:00 2001 From: Felix Manlunas Date: Mon, 20 Mar 2017 19:04:48 -0700 Subject: liquidio: fix Coverity scan errors Fix Coverity scan errors by not dereferencing lio->glists_dma_base pointer if it's NULL. See http://marc.info/?l=linux-netdev&m=149002294305614&w=2 Reported-by: Stephen Hemminger Signed-off-by: Felix Manlunas Signed-off-by: VSR Burru Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 3 ++- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index b23485c3af13..0bc76ad96a17 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -712,7 +712,8 @@ static void delete_glists(struct lio *lio) kfree(g); } while (g); - if (lio->glists_virt_base && lio->glists_virt_base[i]) { + if (lio->glists_virt_base && lio->glists_virt_base[i] && + lio->glists_dma_base && lio->glists_dma_base[i]) { lio_dma_free(lio->oct_dev, lio->glist_entry_size * lio->tx_qsize, lio->glists_virt_base[i], diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index f72db33fcd3a..e03855b8d1f5 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -470,7 +470,8 @@ static void delete_glists(struct lio *lio) kfree(g); } while (g); - if (lio->glists_virt_base && lio->glists_virt_base[i]) { + if (lio->glists_virt_base && lio->glists_virt_base[i] && + lio->glists_dma_base && lio->glists_dma_base[i]) { lio_dma_free(lio->oct_dev, lio->glist_entry_size * lio->tx_qsize, lio->glists_virt_base[i], -- cgit From 726bceca81ba09956226ea03c9fb58e037c3db7d Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 21 Mar 2017 11:24:38 +0100 Subject: net: greth: Utilize of_get_mac_address() Do not open code getting the MAC address exclusively from the "local-mac-address" property, but instead use of_get_mac_address() which looks up the MAC address using the 3 typical property names. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/aeroflex/greth.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index 9f7422ada704..d8e133ced7b8 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1454,11 +1455,10 @@ static int greth_of_probe(struct platform_device *ofdev) break; } if (i == 6) { - const unsigned char *addr; - int len; - addr = of_get_property(ofdev->dev.of_node, "local-mac-address", - &len); - if (addr != NULL && len == 6) { + const u8 *addr; + + addr = of_get_mac_address(ofdev->dev.of_node); + if (addr) { for (i = 0; i < 6; i++) macaddr[i] = (unsigned int) addr[i]; } else { -- cgit From 4c355cdfbba537971b5c3849680b1b6453a7a383 Mon Sep 17 00:00:00 2001 From: "Reshetova, Elena" Date: Tue, 21 Mar 2017 13:59:19 +0200 Subject: net: convert sk_filter.refcnt from atomic_t to refcount_t refcount_t type and corresponding API should be used instead of atomic_t when the variable is used as a reference counter. This allows to avoid accidental refcounter overflows that might lead to use-after-free situations. Signed-off-by: Elena Reshetova Signed-off-by: Hans Liljestrand Signed-off-by: Kees Cook Signed-off-by: David Windsor Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/filter.h | 3 ++- net/core/filter.c | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/linux/filter.h b/include/linux/filter.h index dffa072b7b79..511fe910bf1d 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -430,7 +431,7 @@ struct bpf_prog { }; struct sk_filter { - atomic_t refcnt; + refcount_t refcnt; struct rcu_head rcu; struct bpf_prog *prog; }; diff --git a/net/core/filter.c b/net/core/filter.c index ebaeaf2e46e8..c7f0ccd1c0d3 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -928,7 +928,7 @@ static void sk_filter_release_rcu(struct rcu_head *rcu) */ static void sk_filter_release(struct sk_filter *fp) { - if (atomic_dec_and_test(&fp->refcnt)) + if (refcount_dec_and_test(&fp->refcnt)) call_rcu(&fp->rcu, sk_filter_release_rcu); } @@ -943,20 +943,27 @@ void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp) /* try to charge the socket memory if there is space available * return true on success */ -bool sk_filter_charge(struct sock *sk, struct sk_filter *fp) +static bool __sk_filter_charge(struct sock *sk, struct sk_filter *fp) { u32 filter_size = bpf_prog_size(fp->prog->len); /* same check as in sock_kmalloc() */ if (filter_size <= sysctl_optmem_max && atomic_read(&sk->sk_omem_alloc) + filter_size < sysctl_optmem_max) { - atomic_inc(&fp->refcnt); atomic_add(filter_size, &sk->sk_omem_alloc); return true; } return false; } +bool sk_filter_charge(struct sock *sk, struct sk_filter *fp) +{ + bool ret = __sk_filter_charge(sk, fp); + if (ret) + refcount_inc(&fp->refcnt); + return ret; +} + static struct bpf_prog *bpf_migrate_filter(struct bpf_prog *fp) { struct sock_filter *old_prog; @@ -1179,12 +1186,12 @@ static int __sk_attach_prog(struct bpf_prog *prog, struct sock *sk) return -ENOMEM; fp->prog = prog; - atomic_set(&fp->refcnt, 0); - if (!sk_filter_charge(sk, fp)) { + if (!__sk_filter_charge(sk, fp)) { kfree(fp); return -ENOMEM; } + refcount_set(&fp->refcnt, 1); old_fp = rcu_dereference_protected(sk->sk_filter, lockdep_sock_is_held(sk)); -- cgit From f39768744fd6cd45291c957bd4bf2f68f073ed73 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 21 Mar 2017 16:12:09 +0100 Subject: net: stmmac: Always enable MAC RX queues The MAC RX queues always need to be enabled in order to receive network packets. Remove the condition that this only needs to be done for multi- queue configurations. Signed-off-by: Thierry Reding Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 531bf1dc35cd..98e0f80de9d8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2385,7 +2385,7 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv) stmmac_rx_queue_dma_chan_map(priv); /* Enable MAC RX Queues */ - if (rx_queues_count > 1 && priv->hw->mac->rx_queue_enable) + if (priv->hw->mac->rx_queue_enable) stmmac_mac_enable_rx_queues(priv); /* Set the HW DMA mode and the COE */ -- cgit From 33e85b8dd69e7f2fbf77f04bfc97ea7c76bccf9b Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 21 Mar 2017 16:12:10 +0100 Subject: net: stmmac: Restore DT backwards-compatibility Recent changes to support multiple queues in the device tree bindings resulted in the number of RX and TX queues to be initialized to zero for device trees not adhering to the new bindings. Restore backwards-compatibility with those device trees by falling back to a single RX and TX queues each. Signed-off-by: Thierry Reding Acked-By: Joao Pinto Tested-by: Corentin Labbe Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index f5c8b1bca002..7fc3a1ef395a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -143,6 +143,13 @@ static void stmmac_mtl_setup(struct platform_device *pdev, struct device_node *tx_node; u8 queue = 0; + /* For backwards-compatibility with device trees that don't have any + * snps,mtl-rx-config or snps,mtl-tx-config properties, we fall back + * to one RX and TX queues each. + */ + plat->rx_queues_to_use = 1; + plat->tx_queues_to_use = 1; + rx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-rx-config", 0); if (!rx_node) return; -- cgit From 2d72d5016f00fc7d64b95e79405787dea73669af Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 21 Mar 2017 16:12:11 +0100 Subject: net: stmmac: Use AVB mode by default Prior to the recent multi-queue changes the driver would configure the queues to use the AVB mode, but the mode then got switched to DCB. The hardware still works fine in DCB mode, but my testing capabilities are limited, so it's safer to revert to the prior setting anyway. Signed-off-by: Thierry Reding Acked-By: Joao Pinto Signed-off-by: David S. Miller --- include/linux/stmmac.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index cd98ee232ad1..3921cb9dfadb 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -56,8 +56,8 @@ #define MTL_RX_ALGORITHM_WSP 0x5 /* RX/TX Queue Mode */ -#define MTL_QUEUE_DCB 0x0 -#define MTL_QUEUE_AVB 0x1 +#define MTL_QUEUE_AVB 0x0 +#define MTL_QUEUE_DCB 0x1 /* The MDC clock could be set higher than the IEEE 802.3 * specified frequency limit 0f 2.5 MHz, by programming a clock divider -- cgit From 9860118b58241169f67ba77dfeb935fcf53ce4cd Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Mar 2017 16:36:37 +0000 Subject: net: phy: move phy MMD accessors to phy-core.c Move the phy_(read|write)__mmd() helpers out of line, they will become our main MMD accessor functions, and so will be a little more complex. This complexity doesn't belong in an inline function. Also move the _indirect variants as well to keep like functionality together. Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/Makefile | 2 +- drivers/net/phy/phy-core.c | 135 +++++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/phy.c | 85 ---------------------------- include/linux/phy.h | 20 +------ 4 files changed, 138 insertions(+), 104 deletions(-) create mode 100644 drivers/net/phy/phy-core.c diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 407b0b601ea8..82d915614646 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,7 +1,7 @@ # Makefile for Linux PHY drivers and MDIO bus drivers libphy-y := phy.o phy_device.o mdio_bus.o mdio_device.o \ - mdio-boardinfo.o + mdio-boardinfo.o phy-core.o libphy-$(CONFIG_SWPHY) += swphy.o libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_led_triggers.o diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c new file mode 100644 index 000000000000..b8d8276a3099 --- /dev/null +++ b/drivers/net/phy/phy-core.c @@ -0,0 +1,135 @@ +/* + * Core PHY library, taken from phy.c + * + * 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 + +static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, + int addr) +{ + /* Write the desired MMD Devad */ + bus->write(bus, addr, MII_MMD_CTRL, devad); + + /* Write the desired MMD register address */ + bus->write(bus, addr, MII_MMD_DATA, prtad); + + /* Select the Function : DATA with no post increment */ + bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); +} + +/** + * phy_read_mmd_indirect - reads data from the MMD registers + * @phydev: The PHY device bus + * @prtad: MMD Address + * @devad: MMD DEVAD + * + * Description: it reads data from the MMD registers (clause 22 to access to + * clause 45) of the specified phy address. + * To read these register we have: + * 1) Write reg 13 // DEVAD + * 2) Write reg 14 // MMD Address + * 3) Write reg 13 // MMD Data Command for MMD DEVAD + * 3) Read reg 14 // Read MMD data + */ +int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) +{ + struct phy_driver *phydrv = phydev->drv; + int addr = phydev->mdio.addr; + int value = -1; + + if (!phydrv->read_mmd_indirect) { + struct mii_bus *bus = phydev->mdio.bus; + + mutex_lock(&bus->mdio_lock); + mmd_phy_indirect(bus, prtad, devad, addr); + + /* Read the content of the MMD's selected register */ + value = bus->read(bus, addr, MII_MMD_DATA); + mutex_unlock(&bus->mdio_lock); + } else { + value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr); + } + return value; +} +EXPORT_SYMBOL(phy_read_mmd_indirect); + +/** + * phy_read_mmd - Convenience function for reading a register + * from an MMD on a given PHY. + * @phydev: The phy_device struct + * @devad: The MMD to read from + * @regnum: The register on the MMD to read + * + * Same rules as for phy_read(); + */ +int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) +{ + if (!phydev->is_c45) + return -EOPNOTSUPP; + + return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, + MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff)); +} +EXPORT_SYMBOL(phy_read_mmd); + +/** + * phy_write_mmd_indirect - writes data to the MMD registers + * @phydev: The PHY device + * @prtad: MMD Address + * @devad: MMD DEVAD + * @data: data to write in the MMD register + * + * Description: Write data from the MMD registers of the specified + * phy address. + * To write these register we have: + * 1) Write reg 13 // DEVAD + * 2) Write reg 14 // MMD Address + * 3) Write reg 13 // MMD Data Command for MMD DEVAD + * 3) Write reg 14 // Write MMD data + */ +void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, + int devad, u32 data) +{ + struct phy_driver *phydrv = phydev->drv; + int addr = phydev->mdio.addr; + + if (!phydrv->write_mmd_indirect) { + struct mii_bus *bus = phydev->mdio.bus; + + mutex_lock(&bus->mdio_lock); + mmd_phy_indirect(bus, prtad, devad, addr); + + /* Write the data into MMD's selected register */ + bus->write(bus, addr, MII_MMD_DATA, data); + mutex_unlock(&bus->mdio_lock); + } else { + phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data); + } +} +EXPORT_SYMBOL(phy_write_mmd_indirect); + +/** + * phy_write_mmd - Convenience function for writing a register + * on an MMD on a given PHY. + * @phydev: The phy_device struct + * @devad: The MMD to read from + * @regnum: The register on the MMD to read + * @val: value to write to @regnum + * + * Same rules as for phy_write(); + */ +int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) +{ + if (!phydev->is_c45) + return -EOPNOTSUPP; + + regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff); + + return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val); +} +EXPORT_SYMBOL(phy_write_mmd); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 1be69d8bc909..ffc28c42e2d1 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1192,91 +1192,6 @@ void phy_mac_interrupt(struct phy_device *phydev, int new_link) } EXPORT_SYMBOL(phy_mac_interrupt); -static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, - int addr) -{ - /* Write the desired MMD Devad */ - bus->write(bus, addr, MII_MMD_CTRL, devad); - - /* Write the desired MMD register address */ - bus->write(bus, addr, MII_MMD_DATA, prtad); - - /* Select the Function : DATA with no post increment */ - bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); -} - -/** - * phy_read_mmd_indirect - reads data from the MMD registers - * @phydev: The PHY device bus - * @prtad: MMD Address - * @devad: MMD DEVAD - * - * Description: it reads data from the MMD registers (clause 22 to access to - * clause 45) of the specified phy address. - * To read these register we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Read reg 14 // Read MMD data - */ -int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) -{ - struct phy_driver *phydrv = phydev->drv; - int addr = phydev->mdio.addr; - int value = -1; - - if (!phydrv->read_mmd_indirect) { - struct mii_bus *bus = phydev->mdio.bus; - - mutex_lock(&bus->mdio_lock); - mmd_phy_indirect(bus, prtad, devad, addr); - - /* Read the content of the MMD's selected register */ - value = bus->read(bus, addr, MII_MMD_DATA); - mutex_unlock(&bus->mdio_lock); - } else { - value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr); - } - return value; -} -EXPORT_SYMBOL(phy_read_mmd_indirect); - -/** - * phy_write_mmd_indirect - writes data to the MMD registers - * @phydev: The PHY device - * @prtad: MMD Address - * @devad: MMD DEVAD - * @data: data to write in the MMD register - * - * Description: Write data from the MMD registers of the specified - * phy address. - * To write these register we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Write reg 14 // Write MMD data - */ -void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, u32 data) -{ - struct phy_driver *phydrv = phydev->drv; - int addr = phydev->mdio.addr; - - if (!phydrv->write_mmd_indirect) { - struct mii_bus *bus = phydev->mdio.bus; - - mutex_lock(&bus->mdio_lock); - mmd_phy_indirect(bus, prtad, devad, addr); - - /* Write the data into MMD's selected register */ - bus->write(bus, addr, MII_MMD_DATA, data); - mutex_unlock(&bus->mdio_lock); - } else { - phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data); - } -} -EXPORT_SYMBOL(phy_write_mmd_indirect); - /** * phy_init_eee - init and check the EEE feature * @phydev: target phy_device struct diff --git a/include/linux/phy.h b/include/linux/phy.h index 43a774873aa9..bcb4549b41d6 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -651,14 +651,7 @@ struct phy_fixup { * * Same rules as for phy_read(); */ -static inline int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) -{ - if (!phydev->is_c45) - return -EOPNOTSUPP; - - return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, - MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff)); -} +int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum); /** * phy_read_mmd_indirect - reads data from the MMD registers @@ -752,16 +745,7 @@ static inline bool phy_is_pseudo_fixed_link(struct phy_device *phydev) * * Same rules as for phy_write(); */ -static inline int phy_write_mmd(struct phy_device *phydev, int devad, - u32 regnum, u16 val) -{ - if (!phydev->is_c45) - return -EOPNOTSUPP; - - regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff); - - return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val); -} +int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val); /** * phy_write_mmd_indirect - writes data to the MMD registers -- cgit From 1ee6b9bc6206cd0837bc16e46f580e40fe663384 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Mar 2017 16:36:43 +0000 Subject: net: phy: make phy_(read|write)_mmd() generic MMD accessors Make phy_(read|write)_mmd() generic 802.3 clause 45 register accessors for both Clause 22 and Clause 45 PHYs, using either the direct register reading for Clause 45, or the indirect method for Clause 22 PHYs. Allow this behaviour to be overriden by PHY drivers where necessary. Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy-core.c | 33 +++++++++++++++++++++++++-------- include/linux/phy.h | 24 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index b8d8276a3099..d791100afab2 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -69,11 +69,18 @@ EXPORT_SYMBOL(phy_read_mmd_indirect); */ int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) { - if (!phydev->is_c45) - return -EOPNOTSUPP; + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; - return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, - MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff)); + if (phydev->drv->read_mmd) + return phydev->drv->read_mmd(phydev, devad, regnum); + + if (phydev->is_c45) { + u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); + return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr); + } + + return phy_read_mmd_indirect(phydev, regnum, devad); } EXPORT_SYMBOL(phy_read_mmd); @@ -125,11 +132,21 @@ EXPORT_SYMBOL(phy_write_mmd_indirect); */ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) { - if (!phydev->is_c45) - return -EOPNOTSUPP; + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + + if (phydev->drv->read_mmd) + return phydev->drv->write_mmd(phydev, devad, regnum, val); + + if (phydev->is_c45) { + u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); + + return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, + addr, val); + } - regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff); + phy_write_mmd_indirect(phydev, regnum, devad, val); - return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val); + return 0; } EXPORT_SYMBOL(phy_write_mmd); diff --git a/include/linux/phy.h b/include/linux/phy.h index bcb4549b41d6..b8feeffeb64c 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -587,6 +587,30 @@ struct phy_driver { */ void (*link_change_notify)(struct phy_device *dev); + /* + * Phy specific driver override for reading a MMD register. + * This function is optional for PHY specific drivers. When + * not provided, the default MMD read function will be used + * by phy_read_mmd(), which will use either a direct read for + * Clause 45 PHYs or an indirect read for Clause 22 PHYs. + * devnum is the MMD device number within the PHY device, + * regnum is the register within the selected MMD device. + */ + int (*read_mmd)(struct phy_device *dev, int devnum, u16 regnum); + + /* + * Phy specific driver override for writing a MMD register. + * This function is optional for PHY specific drivers. When + * not provided, the default MMD write function will be used + * by phy_write_mmd(), which will use either a direct write for + * Clause 45 PHYs, or an indirect write for Clause 22 PHYs. + * devnum is the MMD device number within the PHY device, + * regnum is the register within the selected MMD device. + * val is the value to be written. + */ + int (*write_mmd)(struct phy_device *dev, int devnum, u16 regnum, + u16 val); + /* A function provided by a phy specific driver to override the * the PHY driver framework support for reading a MMD register * from the PHY. If not supported, return -1. This function is -- cgit From 5f61367729b8c6e8c5f7068d49ff5e57f1e8a925 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Mar 2017 16:36:48 +0000 Subject: net: lan78xx: update for phy_(read|write)_mmd_indirect() removal lan78xx appears to use phylib in a rather weird way, accessing the PHY partly through phylib, and partly by making direct accesses to it, including to the Clause 45 registers. As the indirect MMD accessors are going away, update this driver to use the plain phy_(read|write)_mmd() accessors instead. Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Acked-by: Woojung Huh Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/usb/lan78xx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 9889a70ff4f6..d885e0325422 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -1952,10 +1952,10 @@ static int lan8835_fixup(struct phy_device *phydev) struct lan78xx_net *dev = netdev_priv(phydev->attached_dev); /* LED2/PME_N/IRQ_N/RGMII_ID pin to IRQ_N mode */ - buf = phy_read_mmd_indirect(phydev, 0x8010, 3); + buf = phy_read_mmd(phydev, MDIO_MMD_PCS, 0x8010); buf &= ~0x1800; buf |= 0x0800; - phy_write_mmd_indirect(phydev, 0x8010, 3, buf); + phy_write_mmd(phydev, MDIO_MMD_PCS, 0x8010, buf); /* RGMII MAC TXC Delay Enable */ ret = lan78xx_write_reg(dev, MAC_RGMII_ID, @@ -1975,11 +1975,11 @@ static int ksz9031rnx_fixup(struct phy_device *phydev) /* Micrel9301RNX PHY configuration */ /* RGMII Control Signal Pad Skew */ - phy_write_mmd_indirect(phydev, 4, 2, 0x0077); + phy_write_mmd(phydev, MDIO_MMD_WIS, 4, 0x0077); /* RGMII RX Data Pad Skew */ - phy_write_mmd_indirect(phydev, 5, 2, 0x7777); + phy_write_mmd(phydev, MDIO_MMD_WIS, 5, 0x7777); /* RGMII RX Clock Pad Skew */ - phy_write_mmd_indirect(phydev, 8, 2, 0x1FF); + phy_write_mmd(phydev, MDIO_MMD_WIS, 8, 0x1FF); dev->interface = PHY_INTERFACE_MODE_RGMII_RXID; -- cgit From a6d99fcd3fc4f6e71630eba8e7f4d2b3b396c4c9 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Mar 2017 16:36:53 +0000 Subject: net: phy: switch remaining users to phy_(read|write)_mmd() Switch everyone over to using phy_read_mmd() and phy_write_mmd() now that they are able to handle both Clause 22 indirect addressing and Clause 45 direct addressing methods to the MMD registers. Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/bcm-phy-lib.c | 12 ++++-------- drivers/net/phy/dp83867.c | 25 +++++++++++-------------- drivers/net/phy/intel-xway.c | 26 +++++++++++++------------- drivers/net/phy/microchip.c | 5 ++--- drivers/net/phy/phy.c | 25 ++++++++++--------------- drivers/net/phy/phy_device.c | 4 ++-- 6 files changed, 42 insertions(+), 55 deletions(-) diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c index 9656dbeb5de5..171010eb4d9c 100644 --- a/drivers/net/phy/bcm-phy-lib.c +++ b/drivers/net/phy/bcm-phy-lib.c @@ -201,8 +201,7 @@ int bcm_phy_set_eee(struct phy_device *phydev, bool enable) int val; /* Enable EEE at PHY level */ - val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, - MDIO_MMD_AN); + val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL); if (val < 0) return val; @@ -211,12 +210,10 @@ int bcm_phy_set_eee(struct phy_device *phydev, bool enable) else val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X); - phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL, - MDIO_MMD_AN, (u32)val); + phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val); /* Advertise EEE */ - val = phy_read_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, - MDIO_MMD_AN); + val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV); if (val < 0) return val; @@ -225,8 +222,7 @@ int bcm_phy_set_eee(struct phy_device *phydev, bool enable) else val &= ~(MDIO_EEE_100TX | MDIO_EEE_1000T); - phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV, - MDIO_MMD_AN, (u32)val); + phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val); return 0; } diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 19865530e0b1..b57f20e552ba 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -133,14 +133,14 @@ static int dp83867_config_port_mirroring(struct phy_device *phydev) (struct dp83867_private *)phydev->priv; u16 val; - val = phy_read_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR); + val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4); if (dp83867->port_mirroring == DP83867_PORT_MIRROING_EN) val |= DP83867_CFG4_PORT_MIRROR_EN; else val &= ~DP83867_CFG4_PORT_MIRROR_EN; - phy_write_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR, val); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val); return 0; } @@ -231,8 +231,7 @@ static int dp83867_config_init(struct phy_device *phydev) * register's bit 11 (marked as RESERVED). */ - bs = phy_read_mmd_indirect(phydev, DP83867_STRAP_STS1, - DP83867_DEVADDR); + bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS1); if (bs & DP83867_STRAP_STS1_RESERVED) val &= ~DP83867_PHYCR_RESERVED_MASK; @@ -243,8 +242,7 @@ static int dp83867_config_init(struct phy_device *phydev) if ((phydev->interface >= PHY_INTERFACE_MODE_RGMII_ID) && (phydev->interface <= PHY_INTERFACE_MODE_RGMII_RXID)) { - val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR); + val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL); if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) val |= (DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN); @@ -255,25 +253,24 @@ static int dp83867_config_init(struct phy_device *phydev) if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) val |= DP83867_RGMII_RX_CLK_DELAY_EN; - phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, val); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, val); delay = (dp83867->rx_id_delay | (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT)); - phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL, - DP83867_DEVADDR, delay); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIIDCTL, + delay); if (dp83867->io_impedance >= 0) { - val = phy_read_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR); + val = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_IO_MUX_CFG); val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; val |= dp83867->io_impedance & DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; - phy_write_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR, val); + phy_write_mmd(phydev, DP83867_DEVADDR, + DP83867_IO_MUX_CFG, val); } } diff --git a/drivers/net/phy/intel-xway.c b/drivers/net/phy/intel-xway.c index b1fd7bb0e4db..55f8c52dd2f1 100644 --- a/drivers/net/phy/intel-xway.c +++ b/drivers/net/phy/intel-xway.c @@ -166,13 +166,13 @@ static int xway_gphy_config_init(struct phy_device *phydev) /* Clear all pending interrupts */ phy_read(phydev, XWAY_MDIO_ISTAT); - phy_write_mmd_indirect(phydev, XWAY_MMD_LEDCH, MDIO_MMD_VEND2, - XWAY_MMD_LEDCH_NACS_NONE | - XWAY_MMD_LEDCH_SBF_F02HZ | - XWAY_MMD_LEDCH_FBF_F16HZ); - phy_write_mmd_indirect(phydev, XWAY_MMD_LEDCL, MDIO_MMD_VEND2, - XWAY_MMD_LEDCH_CBLINK_NONE | - XWAY_MMD_LEDCH_SCAN_NONE); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCH, + XWAY_MMD_LEDCH_NACS_NONE | + XWAY_MMD_LEDCH_SBF_F02HZ | + XWAY_MMD_LEDCH_FBF_F16HZ); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCL, + XWAY_MMD_LEDCH_CBLINK_NONE | + XWAY_MMD_LEDCH_SCAN_NONE); /** * In most cases only one LED is connected to this phy, so @@ -183,12 +183,12 @@ static int xway_gphy_config_init(struct phy_device *phydev) ledxh = XWAY_MMD_LEDxH_BLINKF_NONE | XWAY_MMD_LEDxH_CON_LINK10XX; ledxl = XWAY_MMD_LEDxL_PULSE_TXACT | XWAY_MMD_LEDxL_PULSE_RXACT | XWAY_MMD_LEDxL_BLINKS_NONE; - phy_write_mmd_indirect(phydev, XWAY_MMD_LED0H, MDIO_MMD_VEND2, ledxh); - phy_write_mmd_indirect(phydev, XWAY_MMD_LED0L, MDIO_MMD_VEND2, ledxl); - phy_write_mmd_indirect(phydev, XWAY_MMD_LED1H, MDIO_MMD_VEND2, ledxh); - phy_write_mmd_indirect(phydev, XWAY_MMD_LED1L, MDIO_MMD_VEND2, ledxl); - phy_write_mmd_indirect(phydev, XWAY_MMD_LED2H, MDIO_MMD_VEND2, ledxh); - phy_write_mmd_indirect(phydev, XWAY_MMD_LED2L, MDIO_MMD_VEND2, ledxl); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0H, ledxh); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0L, ledxl); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1H, ledxh); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1L, ledxl); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, ledxh); + phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, ledxl); return 0; } diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c index 324fbf6ad8ff..2b2f543cf9f0 100644 --- a/drivers/net/phy/microchip.c +++ b/drivers/net/phy/microchip.c @@ -78,9 +78,8 @@ static int lan88xx_probe(struct phy_device *phydev) priv->wolopts = 0; /* these values can be used to identify internal PHY */ - priv->chip_id = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_ID, 3); - priv->chip_rev = phy_read_mmd_indirect(phydev, LAN88XX_MMD3_CHIP_REV, - 3); + priv->chip_id = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_ID); + priv->chip_rev = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_REV); phydev->priv = priv; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index ffc28c42e2d1..ba4676ee9018 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1227,8 +1227,7 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) return status; /* First check if the EEE ability is supported */ - eee_cap = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, - MDIO_MMD_PCS); + eee_cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); if (eee_cap <= 0) goto eee_exit_err; @@ -1239,13 +1238,11 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) /* Check which link settings negotiated and verify it in * the EEE advertising registers. */ - eee_lp = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE, - MDIO_MMD_AN); + eee_lp = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); if (eee_lp <= 0) goto eee_exit_err; - eee_adv = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, - MDIO_MMD_AN); + eee_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); if (eee_adv <= 0) goto eee_exit_err; @@ -1258,14 +1255,12 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) /* Configure the PHY to stop receiving xMII * clock while it is signaling LPI. */ - int val = phy_read_mmd_indirect(phydev, MDIO_CTRL1, - MDIO_MMD_PCS); + int val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); if (val < 0) return val; val |= MDIO_PCS_CTRL1_CLKSTOP_EN; - phy_write_mmd_indirect(phydev, MDIO_CTRL1, - MDIO_MMD_PCS, val); + phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, val); } return 0; /* EEE supported */ @@ -1287,7 +1282,7 @@ int phy_get_eee_err(struct phy_device *phydev) if (!phydev->drv) return -EIO; - return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR, MDIO_MMD_PCS); + return phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_WK_ERR); } EXPORT_SYMBOL(phy_get_eee_err); @@ -1307,19 +1302,19 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data) return -EIO; /* Get Supported EEE */ - val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, MDIO_MMD_PCS); + val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); if (val < 0) return val; data->supported = mmd_eee_cap_to_ethtool_sup_t(val); /* Get advertisement EEE */ - val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN); + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); if (val < 0) return val; data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); /* Get LP advertisement EEE */ - val = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_LPABLE, MDIO_MMD_AN); + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE); if (val < 0) return val; data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); @@ -1345,7 +1340,7 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) /* Mask prohibited EEE modes */ val &= ~phydev->eee_broken_modes; - phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, val); + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val); return 0; } diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 5198ccfa347f..1219eeab69d1 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1217,7 +1217,7 @@ static int genphy_config_eee_advert(struct phy_device *phydev) * supported by the phy. If we read 0, EEE is not advertised * In both case, we don't need to continue */ - adv = phy_read_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN); + adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); if (adv <= 0) return 0; @@ -1228,7 +1228,7 @@ static int genphy_config_eee_advert(struct phy_device *phydev) if (old_adv == adv) return 0; - phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, adv); + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); return 1; } -- cgit From d11437e0af73d7e4ed98e4b8373ff906622db5ae Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Mar 2017 16:36:58 +0000 Subject: net: phy: convert micrel to new read_mmd/write_mmd driver methods Convert micrel to the new read_mmd/write_mmd driver methods. This Clause 22 PHY does not support any MMD access method. Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/micrel.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 6742070ca676..b847184de6fc 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -637,8 +637,7 @@ static int ksz8873mll_config_aneg(struct phy_device *phydev) * MMD extended PHY registers. */ static int -ksz9021_rd_mmd_phyreg(struct phy_device *phydev, int ptrad, int devnum, - int regnum) +ksz9021_rd_mmd_phyreg(struct phy_device *phydev, int devad, u16 regnum) { return -1; } @@ -646,10 +645,10 @@ ksz9021_rd_mmd_phyreg(struct phy_device *phydev, int ptrad, int devnum, /* This routine does nothing since the Micrel ksz9021 does not support * standard IEEE MMD extended PHY registers. */ -static void -ksz9021_wr_mmd_phyreg(struct phy_device *phydev, int ptrad, int devnum, - int regnum, u32 val) +static int +ksz9021_wr_mmd_phyreg(struct phy_device *phydev, int devad, u16 regnum, u16 val) { + return -1; } static int kszphy_get_sset_count(struct phy_device *phydev) @@ -962,8 +961,8 @@ static struct phy_driver ksphy_driver[] = { .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = genphy_resume, - .read_mmd_indirect = ksz9021_rd_mmd_phyreg, - .write_mmd_indirect = ksz9021_wr_mmd_phyreg, + .read_mmd = ksz9021_rd_mmd_phyreg, + .write_mmd = ksz9021_wr_mmd_phyreg, }, { .phy_id = PHY_ID_KSZ9031, .phy_id_mask = MICREL_PHY_ID_MASK, -- cgit From 3b85d8df2655a4a5831ee8233108b53e69efa1ed Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Mar 2017 16:37:03 +0000 Subject: net: phy: remove the indirect MMD read/write methods Remove the indirect MMD read/write methods which are now no longer necessary. Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy-core.c | 119 +++++++++++++-------------------------------- include/linux/phy.h | 42 ---------------- 2 files changed, 34 insertions(+), 127 deletions(-) diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index d791100afab2..80795ccd3fab 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -22,103 +22,42 @@ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); } -/** - * phy_read_mmd_indirect - reads data from the MMD registers - * @phydev: The PHY device bus - * @prtad: MMD Address - * @devad: MMD DEVAD - * - * Description: it reads data from the MMD registers (clause 22 to access to - * clause 45) of the specified phy address. - * To read these register we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Read reg 14 // Read MMD data - */ -int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) -{ - struct phy_driver *phydrv = phydev->drv; - int addr = phydev->mdio.addr; - int value = -1; - - if (!phydrv->read_mmd_indirect) { - struct mii_bus *bus = phydev->mdio.bus; - - mutex_lock(&bus->mdio_lock); - mmd_phy_indirect(bus, prtad, devad, addr); - - /* Read the content of the MMD's selected register */ - value = bus->read(bus, addr, MII_MMD_DATA); - mutex_unlock(&bus->mdio_lock); - } else { - value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr); - } - return value; -} -EXPORT_SYMBOL(phy_read_mmd_indirect); - /** * phy_read_mmd - Convenience function for reading a register * from an MMD on a given PHY. * @phydev: The phy_device struct - * @devad: The MMD to read from - * @regnum: The register on the MMD to read + * @devad: The MMD to read from (0..31) + * @regnum: The register on the MMD to read (0..65535) * * Same rules as for phy_read(); */ int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) { + int val; + if (regnum > (u16)~0 || devad > 32) return -EINVAL; - if (phydev->drv->read_mmd) - return phydev->drv->read_mmd(phydev, devad, regnum); - - if (phydev->is_c45) { + if (phydev->drv->read_mmd) { + val = phydev->drv->read_mmd(phydev, devad, regnum); + } else if (phydev->is_c45) { u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); - return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr); - } - return phy_read_mmd_indirect(phydev, regnum, devad); -} -EXPORT_SYMBOL(phy_read_mmd); - -/** - * phy_write_mmd_indirect - writes data to the MMD registers - * @phydev: The PHY device - * @prtad: MMD Address - * @devad: MMD DEVAD - * @data: data to write in the MMD register - * - * Description: Write data from the MMD registers of the specified - * phy address. - * To write these register we have: - * 1) Write reg 13 // DEVAD - * 2) Write reg 14 // MMD Address - * 3) Write reg 13 // MMD Data Command for MMD DEVAD - * 3) Write reg 14 // Write MMD data - */ -void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, u32 data) -{ - struct phy_driver *phydrv = phydev->drv; - int addr = phydev->mdio.addr; - - if (!phydrv->write_mmd_indirect) { + val = mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr); + } else { struct mii_bus *bus = phydev->mdio.bus; + int phy_addr = phydev->mdio.addr; mutex_lock(&bus->mdio_lock); - mmd_phy_indirect(bus, prtad, devad, addr); + mmd_phy_indirect(bus, regnum, devad, phy_addr); - /* Write the data into MMD's selected register */ - bus->write(bus, addr, MII_MMD_DATA, data); + /* Read the content of the MMD's selected register */ + val = bus->read(bus, phy_addr, MII_MMD_DATA); mutex_unlock(&bus->mdio_lock); - } else { - phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data); } + return val; } -EXPORT_SYMBOL(phy_write_mmd_indirect); +EXPORT_SYMBOL(phy_read_mmd); /** * phy_write_mmd - Convenience function for writing a register @@ -132,21 +71,31 @@ EXPORT_SYMBOL(phy_write_mmd_indirect); */ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) { + int ret; + if (regnum > (u16)~0 || devad > 32) return -EINVAL; - if (phydev->drv->read_mmd) - return phydev->drv->write_mmd(phydev, devad, regnum, val); - - if (phydev->is_c45) { + if (phydev->drv->read_mmd) { + ret = phydev->drv->write_mmd(phydev, devad, regnum, val); + } else if (phydev->is_c45) { u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); - return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, - addr, val); - } + ret = mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, + addr, val); + } else { + struct mii_bus *bus = phydev->mdio.bus; + int phy_addr = phydev->mdio.addr; - phy_write_mmd_indirect(phydev, regnum, devad, val); + mutex_lock(&bus->mdio_lock); + mmd_phy_indirect(bus, regnum, devad, phy_addr); - return 0; + /* Write the data into MMD's selected register */ + bus->write(bus, phy_addr, MII_MMD_DATA, val); + mutex_unlock(&bus->mdio_lock); + + ret = 0; + } + return ret; } EXPORT_SYMBOL(phy_write_mmd); diff --git a/include/linux/phy.h b/include/linux/phy.h index b8feeffeb64c..2efca6b39fba 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -611,24 +611,6 @@ struct phy_driver { int (*write_mmd)(struct phy_device *dev, int devnum, u16 regnum, u16 val); - /* A function provided by a phy specific driver to override the - * the PHY driver framework support for reading a MMD register - * from the PHY. If not supported, return -1. This function is - * optional for PHY specific drivers, if not provided then the - * default MMD read function is used by the PHY framework. - */ - int (*read_mmd_indirect)(struct phy_device *dev, int ptrad, - int devnum, int regnum); - - /* A function provided by a phy specific driver to override the - * the PHY driver framework support for writing a MMD register - * from the PHY. This function is optional for PHY specific drivers, - * if not provided then the default MMD read function is used by - * the PHY framework. - */ - void (*write_mmd_indirect)(struct phy_device *dev, int ptrad, - int devnum, int regnum, u32 val); - /* Get the size and type of the eeprom contained within a plug-in * module */ int (*module_info)(struct phy_device *dev, @@ -677,17 +659,6 @@ struct phy_fixup { */ int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum); -/** - * phy_read_mmd_indirect - reads data from the MMD registers - * @phydev: The PHY device bus - * @prtad: MMD Address - * @addr: PHY address on the MII bus - * - * Description: it reads data from the MMD registers (clause 22 to access to - * clause 45) of the specified phy address. - */ -int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad); - /** * phy_read - Convenience function for reading a given PHY register * @phydev: the phy_device struct @@ -771,19 +742,6 @@ static inline bool phy_is_pseudo_fixed_link(struct phy_device *phydev) */ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val); -/** - * phy_write_mmd_indirect - writes data to the MMD registers - * @phydev: The PHY device - * @prtad: MMD Address - * @devad: MMD DEVAD - * @data: data to write in the MMD register - * - * Description: Write data from the MMD registers of the specified - * phy address. - */ -void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, - int devad, u32 data); - struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, bool is_c45, struct phy_c45_device_ids *c45_ids); -- cgit From 060fbc894bf9ac8a226d63c5b0c8059a527155e2 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Mar 2017 16:37:08 +0000 Subject: net: phy: clean up mmd_phy_indirect() Make mmd_phy_indirect() use the same terminology as the rest of the code, making clear what each address is - phy address, devad, and register number. While here, remove the "inline" from this static function, leaving it to the compiler to decide whether to inline this function, and get rid of unnecessary parens. Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy-core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index 80795ccd3fab..357a4d0d7641 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -9,17 +9,17 @@ #include #include -static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, - int addr) +static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad, + u16 regnum) { /* Write the desired MMD Devad */ - bus->write(bus, addr, MII_MMD_CTRL, devad); + bus->write(bus, phy_addr, MII_MMD_CTRL, devad); /* Write the desired MMD register address */ - bus->write(bus, addr, MII_MMD_DATA, prtad); + bus->write(bus, phy_addr, MII_MMD_DATA, regnum); /* Select the Function : DATA with no post increment */ - bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); + bus->write(bus, phy_addr, MII_MMD_CTRL, devad | MII_MMD_CTRL_NOINCR); } /** @@ -49,7 +49,7 @@ int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) int phy_addr = phydev->mdio.addr; mutex_lock(&bus->mdio_lock); - mmd_phy_indirect(bus, regnum, devad, phy_addr); + mmd_phy_indirect(bus, phy_addr, devad, regnum); /* Read the content of the MMD's selected register */ val = bus->read(bus, phy_addr, MII_MMD_DATA); @@ -88,7 +88,7 @@ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) int phy_addr = phydev->mdio.addr; mutex_lock(&bus->mdio_lock); - mmd_phy_indirect(bus, regnum, devad, phy_addr); + mmd_phy_indirect(bus, phy_addr, devad, regnum); /* Write the data into MMD's selected register */ bus->write(bus, phy_addr, MII_MMD_DATA, val); -- cgit From a7678c70ef624dde4f9f08ad6e99f2ff4dbdee57 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 21 Mar 2017 12:22:26 -0700 Subject: rtnetlink: Add dump all for netconf Use the rtnl_dump_all to dump all netconf handlers that have been registered. Allows userspace to send a dump request for PF_UNSPEC and get all families. Cc: Nicolas Dichtel Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index c4e84c558240..9c3947a43eff 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4185,6 +4185,7 @@ void __init rtnetlink_init(void) rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all, NULL); rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, NULL); + rtnl_register(PF_UNSPEC, RTM_GETNETCONF, NULL, rtnl_dump_all, NULL); rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, NULL); rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, NULL); -- cgit From 31c7ba9eff5d8a9f20057082eb44cc3c6a1fb3d6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 21 Mar 2017 23:42:27 +0300 Subject: net: dwc-xlgmac: fix an error code in xlgmac_alloc_pages() The dma_mapping_error() returns true if there is an error but we want to return -ENOMEM and not 1. Fixes: 65e0ace2c5cd ("net: dwc-xlgmac: Initial driver for DesignWare Enterprise Ethernet") Signed-off-by: Dan Carpenter Reviewed-by: Jie Deng Signed-off-by: David S. Miller --- drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c index 55c796ed7d26..39b5cb967bba 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c @@ -335,7 +335,6 @@ static int xlgmac_alloc_pages(struct xlgmac_pdata *pdata, { struct page *pages = NULL; dma_addr_t pages_dma; - int ret; /* Try to obtain pages, decreasing order if necessary */ gfp |= __GFP_COLD | __GFP_COMP | __GFP_NOWARN; @@ -352,10 +351,9 @@ static int xlgmac_alloc_pages(struct xlgmac_pdata *pdata, /* Map the pages */ pages_dma = dma_map_page(pdata->dev, pages, 0, PAGE_SIZE << order, DMA_FROM_DEVICE); - ret = dma_mapping_error(pdata->dev, pages_dma); - if (ret) { + if (dma_mapping_error(pdata->dev, pages_dma)) { put_page(pages); - return ret; + return -ENOMEM; } pa->pages = pages; -- cgit From 3d4fc6eb4bc15cd81a150b065494f0e67d2493c0 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:07 -0700 Subject: nfp: disallow sharing mutexes on the same machine NFP can be connected to multiple machines via PCI or other buses. Access to hardware resources is arbitrated using locks residing in device memory. Currently nfpcore only respects the mutexes when it comes to inter-host locking, but if we try to acquire the same lock again, on one host - it will simply return success because owner of the lock is already set to that host. This makes the locks useless for arbitration within one host and unfair because whichever host grabbed the lock will have a chance to reacquire it without others getting a shot. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c index 40108e66c654..e2267b421af0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c @@ -1736,11 +1736,5 @@ int nfp_cpp_mutex_trylock(struct nfp_cpp_mutex *mutex) return 0; } - /* Already locked by us? Success! */ - if (tmp == value) { - mutex->depth = 1; - return 0; - } - return nfp_mutex_is_locked(tmp) ? -EBUSY : -EINVAL; } -- cgit From f1ba63ec9073d5f270efde4cd86d811a441d54d0 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:08 -0700 Subject: nfp: fail graciously when someone tries to grab global lock The global device lock is acquired to search the resource table. The lock is actually itself part of the table (entry 0). Therefore if someone asks for resource 0 we would deadlock since double locking is no longer allowed. Currently the driver doesn't try to lock that resource so let's simply make sure we fail graciously and not add special handling of this case until really need. Hide the relevant defines in the source file. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h | 9 +-------- drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h index 42cb720b696d..f7ca8e374923 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h @@ -66,14 +66,7 @@ int nfp_nsp_write_eth_table(struct nfp_nsp *state, /* Implemented in nfp_resource.c */ -#define NFP_RESOURCE_TBL_TARGET NFP_CPP_TARGET_MU -#define NFP_RESOURCE_TBL_BASE 0x8100000000ULL - -/* NFP Resource Table self-identifier */ -#define NFP_RESOURCE_TBL_NAME "nfp.res" -#define NFP_RESOURCE_TBL_KEY 0x00000000 /* Special key for entry 0 */ - -/* All other keys are CRC32-POSIX of the 8-byte identification string */ +/* All keys are CRC32-POSIX of the 8-byte identification string */ /* ARM/PCI vNIC Interfaces 0..3 */ #define NFP_RESOURCE_VNIC_PCI_0 "vnic.p0" diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c index a2850344f8b4..2d15a7c9d0de 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_resource.c @@ -45,6 +45,13 @@ #include "nfp_cpp.h" #include "nfp6000/nfp6000.h" +#define NFP_RESOURCE_TBL_TARGET NFP_CPP_TARGET_MU +#define NFP_RESOURCE_TBL_BASE 0x8100000000ULL + +/* NFP Resource Table self-identifier */ +#define NFP_RESOURCE_TBL_NAME "nfp.res" +#define NFP_RESOURCE_TBL_KEY 0x00000000 /* Special key for entry 0 */ + #define NFP_RESOURCE_ENTRY_NAME_SZ 8 /** @@ -100,9 +107,11 @@ static int nfp_cpp_resource_find(struct nfp_cpp *cpp, struct nfp_resource *res) strncpy(name_pad, res->name, sizeof(name_pad)); /* Search for a matching entry */ - key = NFP_RESOURCE_TBL_KEY; - if (memcmp(name_pad, NFP_RESOURCE_TBL_NAME "\0\0\0\0\0\0\0\0", 8)) - key = crc32_posix(name_pad, sizeof(name_pad)); + if (!memcmp(name_pad, NFP_RESOURCE_TBL_NAME "\0\0\0\0\0\0\0\0", 8)) { + nfp_err(cpp, "Grabbing device lock not supported\n"); + return -EOPNOTSUPP; + } + key = crc32_posix(name_pad, sizeof(name_pad)); for (i = 0; i < NFP_RESOURCE_TBL_ENTRIES; i++) { u64 addr = NFP_RESOURCE_TBL_BASE + -- cgit From 832ff9482ea44370e8a7f7e0d205be6be73d2257 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:09 -0700 Subject: nfp: remove cpp mutex cache CPP mutex cache was introduced to work around the fact that the same host could successfully acquire a lock multiple times. It used to collapse multiple users to the same struct nfp_cpp_mutex and track use count. Unfortunately it's racy. Since we now force all nfp_mutex_lock() callers within the host to actually succeed at acquiring the lock we no longer need the cache, let's remove it. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/nfpcore/nfp_cppcore.c | 43 +--------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c index e2267b421af0..6337342c5b62 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c @@ -66,10 +66,8 @@ struct nfp_cpp_resource { }; struct nfp_cpp_mutex { - struct list_head list; struct nfp_cpp *cpp; int target; - u16 usage; u16 depth; unsigned long long address; u32 key; @@ -86,7 +84,6 @@ struct nfp_cpp { const struct nfp_cpp_operations *op; struct list_head resource_list; /* NFP CPP resource list */ - struct list_head mutex_cache; /* Mutex cache */ rwlock_t resource_lock; wait_queue_head_t waitq; @@ -187,24 +184,6 @@ void nfp_cpp_free(struct nfp_cpp *cpp) { struct nfp_cpp_area_cache *cache, *ctmp; struct nfp_cpp_resource *res, *rtmp; - struct nfp_cpp_mutex *mutex, *mtmp; - - /* There should be no mutexes in the cache at this point. */ - WARN_ON(!list_empty(&cpp->mutex_cache)); - /* .. but if there are, unlock them and complain. */ - list_for_each_entry_safe(mutex, mtmp, &cpp->mutex_cache, list) { - dev_err(cpp->dev.parent, "Dangling mutex: @%d::0x%llx, %d locks held by %d owners\n", - mutex->target, (unsigned long long)mutex->address, - mutex->depth, mutex->usage); - - /* Forcing an unlock */ - mutex->depth = 1; - nfp_cpp_mutex_unlock(mutex); - - /* Forcing a free */ - mutex->usage = 1; - nfp_cpp_mutex_free(mutex); - } /* Remove all caches */ list_for_each_entry_safe(cache, ctmp, &cpp->area_cache_list, entry) { @@ -1127,7 +1106,6 @@ nfp_cpp_from_operations(const struct nfp_cpp_operations *ops, rwlock_init(&cpp->resource_lock); init_waitqueue_head(&cpp->waitq); lockdep_set_class(&cpp->resource_lock, &nfp_cpp_resource_lock_key); - INIT_LIST_HEAD(&cpp->mutex_cache); INIT_LIST_HEAD(&cpp->resource_list); INIT_LIST_HEAD(&cpp->area_cache_list); mutex_init(&cpp->area_cache_mutex); @@ -1536,14 +1514,6 @@ struct nfp_cpp_mutex *nfp_cpp_mutex_alloc(struct nfp_cpp *cpp, int target, if (err) return NULL; - /* Look for mutex on cache list */ - list_for_each_entry(mutex, &cpp->mutex_cache, list) { - if (mutex->target == target && mutex->address == address) { - mutex->usage++; - return mutex; - } - } - err = nfp_cpp_readl(cpp, mur, address + 4, &tmp); if (err < 0) return NULL; @@ -1560,10 +1530,6 @@ struct nfp_cpp_mutex *nfp_cpp_mutex_alloc(struct nfp_cpp *cpp, int target, mutex->address = address; mutex->key = key; mutex->depth = 0; - mutex->usage = 1; - - /* Add mutex to cache list */ - list_add(&mutex->list, &cpp->mutex_cache); return mutex; } @@ -1574,11 +1540,6 @@ struct nfp_cpp_mutex *nfp_cpp_mutex_alloc(struct nfp_cpp *cpp, int target, */ void nfp_cpp_mutex_free(struct nfp_cpp_mutex *mutex) { - if (--mutex->usage) - return; - - /* Remove mutex from cache */ - list_del(&mutex->list); kfree(mutex); } @@ -1611,8 +1572,8 @@ int nfp_cpp_mutex_lock(struct nfp_cpp_mutex *mutex) if (time_is_before_eq_jiffies(warn_at)) { warn_at = jiffies + 60 * HZ; dev_warn(mutex->cpp->dev.parent, - "Warning: waiting for NFP mutex [usage:%hd depth:%hd target:%d addr:%llx key:%08x]\n", - mutex->usage, mutex->depth, + "Warning: waiting for NFP mutex [depth:%hd target:%d addr:%llx key:%08x]\n", + mutex->depth, mutex->target, mutex->address, mutex->key); } } -- cgit From 8672103f415535d0341005e411a1757865ddd220 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:10 -0700 Subject: nfp: move mutex code out of nfp_cppcore.c After mutex cache removal we can put the mutex code in a separate source file. This makes it clear it doesn't play with internals of struct nfp_cpp any more. No functional changes. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/Makefile | 1 + .../ethernet/netronome/nfp/nfpcore/nfp_cppcore.c | 304 ------------------ .../net/ethernet/netronome/nfp/nfpcore/nfp_mutex.c | 345 +++++++++++++++++++++ 3 files changed, 346 insertions(+), 304 deletions(-) create mode 100644 drivers/net/ethernet/netronome/nfp/nfpcore/nfp_mutex.c diff --git a/drivers/net/ethernet/netronome/nfp/Makefile b/drivers/net/ethernet/netronome/nfp/Makefile index 6933afa69df2..4a5d13ef92a4 100644 --- a/drivers/net/ethernet/netronome/nfp/Makefile +++ b/drivers/net/ethernet/netronome/nfp/Makefile @@ -6,6 +6,7 @@ nfp-objs := \ nfpcore/nfp_cpplib.o \ nfpcore/nfp_hwinfo.o \ nfpcore/nfp_mip.o \ + nfpcore/nfp_mutex.o \ nfpcore/nfp_nffw.o \ nfpcore/nfp_nsp.o \ nfpcore/nfp_nsp_eth.o \ diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c index 6337342c5b62..62aa7bcee93d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c @@ -65,14 +65,6 @@ struct nfp_cpp_resource { u64 end; }; -struct nfp_cpp_mutex { - struct nfp_cpp *cpp; - int target; - u16 depth; - unsigned long long address; - u32 key; -}; - struct nfp_cpp { struct device dev; @@ -1403,299 +1395,3 @@ void *nfp_cpp_explicit_priv(struct nfp_cpp_explicit *cpp_explicit) { return &cpp_explicit[1]; } - -/* THIS FUNCTION IS NOT EXPORTED */ -static u32 nfp_mutex_locked(u16 interface) -{ - return (u32)interface << 16 | 0x000f; -} - -static u32 nfp_mutex_unlocked(u16 interface) -{ - return (u32)interface << 16 | 0x0000; -} - -static bool nfp_mutex_is_locked(u32 val) -{ - return (val & 0xffff) == 0x000f; -} - -static bool nfp_mutex_is_unlocked(u32 val) -{ - return (val & 0xffff) == 0000; -} - -/* If you need more than 65536 recursive locks, please rethink your code. */ -#define MUTEX_DEPTH_MAX 0xffff - -static int -nfp_cpp_mutex_validate(u16 interface, int *target, unsigned long long address) -{ - /* Not permitted on invalid interfaces */ - if (NFP_CPP_INTERFACE_TYPE_of(interface) == - NFP_CPP_INTERFACE_TYPE_INVALID) - return -EINVAL; - - /* Address must be 64-bit aligned */ - if (address & 7) - return -EINVAL; - - if (*target != NFP_CPP_TARGET_MU) - return -EINVAL; - - return 0; -} - -/** - * nfp_cpp_mutex_init() - Initialize a mutex location - * @cpp: NFP CPP handle - * @target: NFP CPP target ID (ie NFP_CPP_TARGET_CLS or NFP_CPP_TARGET_MU) - * @address: Offset into the address space of the NFP CPP target ID - * @key: Unique 32-bit value for this mutex - * - * The CPP target:address must point to a 64-bit aligned location, and - * will initialize 64 bits of data at the location. - * - * This creates the initial mutex state, as locked by this - * nfp_cpp_interface(). - * - * This function should only be called when setting up - * the initial lock state upon boot-up of the system. - * - * Return: 0 on success, or -errno on failure - */ -int nfp_cpp_mutex_init(struct nfp_cpp *cpp, - int target, unsigned long long address, u32 key) -{ - const u32 muw = NFP_CPP_ID(target, 4, 0); /* atomic_write */ - u16 interface = nfp_cpp_interface(cpp); - int err; - - err = nfp_cpp_mutex_validate(interface, &target, address); - if (err) - return err; - - err = nfp_cpp_writel(cpp, muw, address + 4, key); - if (err) - return err; - - err = nfp_cpp_writel(cpp, muw, address, nfp_mutex_locked(interface)); - if (err) - return err; - - return 0; -} - -/** - * nfp_cpp_mutex_alloc() - Create a mutex handle - * @cpp: NFP CPP handle - * @target: NFP CPP target ID (ie NFP_CPP_TARGET_CLS or NFP_CPP_TARGET_MU) - * @address: Offset into the address space of the NFP CPP target ID - * @key: 32-bit unique key (must match the key at this location) - * - * The CPP target:address must point to a 64-bit aligned location, and - * reserve 64 bits of data at the location for use by the handle. - * - * Only target/address pairs that point to entities that support the - * MU Atomic Engine's CmpAndSwap32 command are supported. - * - * Return: A non-NULL struct nfp_cpp_mutex * on success, NULL on failure. - */ -struct nfp_cpp_mutex *nfp_cpp_mutex_alloc(struct nfp_cpp *cpp, int target, - unsigned long long address, u32 key) -{ - const u32 mur = NFP_CPP_ID(target, 3, 0); /* atomic_read */ - u16 interface = nfp_cpp_interface(cpp); - struct nfp_cpp_mutex *mutex; - int err; - u32 tmp; - - err = nfp_cpp_mutex_validate(interface, &target, address); - if (err) - return NULL; - - err = nfp_cpp_readl(cpp, mur, address + 4, &tmp); - if (err < 0) - return NULL; - - if (tmp != key) - return NULL; - - mutex = kzalloc(sizeof(*mutex), GFP_KERNEL); - if (!mutex) - return NULL; - - mutex->cpp = cpp; - mutex->target = target; - mutex->address = address; - mutex->key = key; - mutex->depth = 0; - - return mutex; -} - -/** - * nfp_cpp_mutex_free() - Free a mutex handle - does not alter the lock state - * @mutex: NFP CPP Mutex handle - */ -void nfp_cpp_mutex_free(struct nfp_cpp_mutex *mutex) -{ - kfree(mutex); -} - -/** - * nfp_cpp_mutex_lock() - Lock a mutex handle, using the NFP MU Atomic Engine - * @mutex: NFP CPP Mutex handle - * - * Return: 0 on success, or -errno on failure - */ -int nfp_cpp_mutex_lock(struct nfp_cpp_mutex *mutex) -{ - unsigned long warn_at = jiffies + 15 * HZ; - unsigned int timeout_ms = 1; - int err; - - /* We can't use a waitqueue here, because the unlocker - * might be on a separate CPU. - * - * So just wait for now. - */ - for (;;) { - err = nfp_cpp_mutex_trylock(mutex); - if (err != -EBUSY) - break; - - err = msleep_interruptible(timeout_ms); - if (err != 0) - return -ERESTARTSYS; - - if (time_is_before_eq_jiffies(warn_at)) { - warn_at = jiffies + 60 * HZ; - dev_warn(mutex->cpp->dev.parent, - "Warning: waiting for NFP mutex [depth:%hd target:%d addr:%llx key:%08x]\n", - mutex->depth, - mutex->target, mutex->address, mutex->key); - } - } - - return err; -} - -/** - * nfp_cpp_mutex_unlock() - Unlock a mutex handle, using the MU Atomic Engine - * @mutex: NFP CPP Mutex handle - * - * Return: 0 on success, or -errno on failure - */ -int nfp_cpp_mutex_unlock(struct nfp_cpp_mutex *mutex) -{ - const u32 muw = NFP_CPP_ID(mutex->target, 4, 0); /* atomic_write */ - const u32 mur = NFP_CPP_ID(mutex->target, 3, 0); /* atomic_read */ - struct nfp_cpp *cpp = mutex->cpp; - u32 key, value; - u16 interface; - int err; - - interface = nfp_cpp_interface(cpp); - - if (mutex->depth > 1) { - mutex->depth--; - return 0; - } - - err = nfp_cpp_readl(mutex->cpp, mur, mutex->address + 4, &key); - if (err < 0) - return err; - - if (key != mutex->key) - return -EPERM; - - err = nfp_cpp_readl(mutex->cpp, mur, mutex->address, &value); - if (err < 0) - return err; - - if (value != nfp_mutex_locked(interface)) - return -EACCES; - - err = nfp_cpp_writel(cpp, muw, mutex->address, - nfp_mutex_unlocked(interface)); - if (err < 0) - return err; - - mutex->depth = 0; - return 0; -} - -/** - * nfp_cpp_mutex_trylock() - Attempt to lock a mutex handle - * @mutex: NFP CPP Mutex handle - * - * Return: 0 if the lock succeeded, -errno on failure - */ -int nfp_cpp_mutex_trylock(struct nfp_cpp_mutex *mutex) -{ - const u32 muw = NFP_CPP_ID(mutex->target, 4, 0); /* atomic_write */ - const u32 mus = NFP_CPP_ID(mutex->target, 5, 3); /* test_set_imm */ - const u32 mur = NFP_CPP_ID(mutex->target, 3, 0); /* atomic_read */ - struct nfp_cpp *cpp = mutex->cpp; - u32 key, value, tmp; - int err; - - if (mutex->depth > 0) { - if (mutex->depth == MUTEX_DEPTH_MAX) - return -E2BIG; - mutex->depth++; - return 0; - } - - /* Verify that the lock marker is not damaged */ - err = nfp_cpp_readl(cpp, mur, mutex->address + 4, &key); - if (err < 0) - return err; - - if (key != mutex->key) - return -EPERM; - - /* Compare against the unlocked state, and if true, - * write the interface id into the top 16 bits, and - * mark as locked. - */ - value = nfp_mutex_locked(nfp_cpp_interface(cpp)); - - /* We use test_set_imm here, as it implies a read - * of the current state, and sets the bits in the - * bytemask of the command to 1s. Since the mutex - * is guaranteed to be 64-bit aligned, the bytemask - * of this 32-bit command is ensured to be 8'b00001111, - * which implies that the lower 4 bits will be set to - * ones regardless of the initial state. - * - * Since this is a 'Readback' operation, with no Pull - * data, we can treat this as a normal Push (read) - * atomic, which returns the original value. - */ - err = nfp_cpp_readl(cpp, mus, mutex->address, &tmp); - if (err < 0) - return err; - - /* Was it unlocked? */ - if (nfp_mutex_is_unlocked(tmp)) { - /* The read value can only be 0x....0000 in the unlocked state. - * If there was another contending for this lock, then - * the lock state would be 0x....000f - */ - - /* Write our owner ID into the lock - * While not strictly necessary, this helps with - * debug and bookkeeping. - */ - err = nfp_cpp_writel(cpp, muw, mutex->address, value); - if (err < 0) - return err; - - mutex->depth = 1; - return 0; - } - - return nfp_mutex_is_locked(tmp) ? -EBUSY : -EINVAL; -} diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_mutex.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_mutex.c new file mode 100644 index 000000000000..8a99c189efa8 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_mutex.c @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2015-2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "nfp_cpp.h" +#include "nfp6000/nfp6000.h" + +struct nfp_cpp_mutex { + struct nfp_cpp *cpp; + int target; + u16 depth; + unsigned long long address; + u32 key; +}; + +static u32 nfp_mutex_locked(u16 interface) +{ + return (u32)interface << 16 | 0x000f; +} + +static u32 nfp_mutex_unlocked(u16 interface) +{ + return (u32)interface << 16 | 0x0000; +} + +static bool nfp_mutex_is_locked(u32 val) +{ + return (val & 0xffff) == 0x000f; +} + +static bool nfp_mutex_is_unlocked(u32 val) +{ + return (val & 0xffff) == 0000; +} + +/* If you need more than 65536 recursive locks, please rethink your code. */ +#define NFP_MUTEX_DEPTH_MAX 0xffff + +static int +nfp_cpp_mutex_validate(u16 interface, int *target, unsigned long long address) +{ + /* Not permitted on invalid interfaces */ + if (NFP_CPP_INTERFACE_TYPE_of(interface) == + NFP_CPP_INTERFACE_TYPE_INVALID) + return -EINVAL; + + /* Address must be 64-bit aligned */ + if (address & 7) + return -EINVAL; + + if (*target != NFP_CPP_TARGET_MU) + return -EINVAL; + + return 0; +} + +/** + * nfp_cpp_mutex_init() - Initialize a mutex location + * @cpp: NFP CPP handle + * @target: NFP CPP target ID (ie NFP_CPP_TARGET_CLS or NFP_CPP_TARGET_MU) + * @address: Offset into the address space of the NFP CPP target ID + * @key: Unique 32-bit value for this mutex + * + * The CPP target:address must point to a 64-bit aligned location, and + * will initialize 64 bits of data at the location. + * + * This creates the initial mutex state, as locked by this + * nfp_cpp_interface(). + * + * This function should only be called when setting up + * the initial lock state upon boot-up of the system. + * + * Return: 0 on success, or -errno on failure + */ +int nfp_cpp_mutex_init(struct nfp_cpp *cpp, + int target, unsigned long long address, u32 key) +{ + const u32 muw = NFP_CPP_ID(target, 4, 0); /* atomic_write */ + u16 interface = nfp_cpp_interface(cpp); + int err; + + err = nfp_cpp_mutex_validate(interface, &target, address); + if (err) + return err; + + err = nfp_cpp_writel(cpp, muw, address + 4, key); + if (err) + return err; + + err = nfp_cpp_writel(cpp, muw, address, nfp_mutex_locked(interface)); + if (err) + return err; + + return 0; +} + +/** + * nfp_cpp_mutex_alloc() - Create a mutex handle + * @cpp: NFP CPP handle + * @target: NFP CPP target ID (ie NFP_CPP_TARGET_CLS or NFP_CPP_TARGET_MU) + * @address: Offset into the address space of the NFP CPP target ID + * @key: 32-bit unique key (must match the key at this location) + * + * The CPP target:address must point to a 64-bit aligned location, and + * reserve 64 bits of data at the location for use by the handle. + * + * Only target/address pairs that point to entities that support the + * MU Atomic Engine's CmpAndSwap32 command are supported. + * + * Return: A non-NULL struct nfp_cpp_mutex * on success, NULL on failure. + */ +struct nfp_cpp_mutex *nfp_cpp_mutex_alloc(struct nfp_cpp *cpp, int target, + unsigned long long address, u32 key) +{ + const u32 mur = NFP_CPP_ID(target, 3, 0); /* atomic_read */ + u16 interface = nfp_cpp_interface(cpp); + struct nfp_cpp_mutex *mutex; + int err; + u32 tmp; + + err = nfp_cpp_mutex_validate(interface, &target, address); + if (err) + return NULL; + + err = nfp_cpp_readl(cpp, mur, address + 4, &tmp); + if (err < 0) + return NULL; + + if (tmp != key) + return NULL; + + mutex = kzalloc(sizeof(*mutex), GFP_KERNEL); + if (!mutex) + return NULL; + + mutex->cpp = cpp; + mutex->target = target; + mutex->address = address; + mutex->key = key; + mutex->depth = 0; + + return mutex; +} + +/** + * nfp_cpp_mutex_free() - Free a mutex handle - does not alter the lock state + * @mutex: NFP CPP Mutex handle + */ +void nfp_cpp_mutex_free(struct nfp_cpp_mutex *mutex) +{ + kfree(mutex); +} + +/** + * nfp_cpp_mutex_lock() - Lock a mutex handle, using the NFP MU Atomic Engine + * @mutex: NFP CPP Mutex handle + * + * Return: 0 on success, or -errno on failure + */ +int nfp_cpp_mutex_lock(struct nfp_cpp_mutex *mutex) +{ + unsigned long warn_at = jiffies + 15 * HZ; + unsigned int timeout_ms = 1; + int err; + + /* We can't use a waitqueue here, because the unlocker + * might be on a separate CPU. + * + * So just wait for now. + */ + for (;;) { + err = nfp_cpp_mutex_trylock(mutex); + if (err != -EBUSY) + break; + + err = msleep_interruptible(timeout_ms); + if (err != 0) + return -ERESTARTSYS; + + if (time_is_before_eq_jiffies(warn_at)) { + warn_at = jiffies + 60 * HZ; + nfp_warn(mutex->cpp, + "Warning: waiting for NFP mutex [depth:%hd target:%d addr:%llx key:%08x]\n", + mutex->depth, + mutex->target, mutex->address, mutex->key); + } + } + + return err; +} + +/** + * nfp_cpp_mutex_unlock() - Unlock a mutex handle, using the MU Atomic Engine + * @mutex: NFP CPP Mutex handle + * + * Return: 0 on success, or -errno on failure + */ +int nfp_cpp_mutex_unlock(struct nfp_cpp_mutex *mutex) +{ + const u32 muw = NFP_CPP_ID(mutex->target, 4, 0); /* atomic_write */ + const u32 mur = NFP_CPP_ID(mutex->target, 3, 0); /* atomic_read */ + struct nfp_cpp *cpp = mutex->cpp; + u32 key, value; + u16 interface; + int err; + + interface = nfp_cpp_interface(cpp); + + if (mutex->depth > 1) { + mutex->depth--; + return 0; + } + + err = nfp_cpp_readl(mutex->cpp, mur, mutex->address + 4, &key); + if (err < 0) + return err; + + if (key != mutex->key) + return -EPERM; + + err = nfp_cpp_readl(mutex->cpp, mur, mutex->address, &value); + if (err < 0) + return err; + + if (value != nfp_mutex_locked(interface)) + return -EACCES; + + err = nfp_cpp_writel(cpp, muw, mutex->address, + nfp_mutex_unlocked(interface)); + if (err < 0) + return err; + + mutex->depth = 0; + return 0; +} + +/** + * nfp_cpp_mutex_trylock() - Attempt to lock a mutex handle + * @mutex: NFP CPP Mutex handle + * + * Return: 0 if the lock succeeded, -errno on failure + */ +int nfp_cpp_mutex_trylock(struct nfp_cpp_mutex *mutex) +{ + const u32 muw = NFP_CPP_ID(mutex->target, 4, 0); /* atomic_write */ + const u32 mus = NFP_CPP_ID(mutex->target, 5, 3); /* test_set_imm */ + const u32 mur = NFP_CPP_ID(mutex->target, 3, 0); /* atomic_read */ + struct nfp_cpp *cpp = mutex->cpp; + u32 key, value, tmp; + int err; + + if (mutex->depth > 0) { + if (mutex->depth == NFP_MUTEX_DEPTH_MAX) + return -E2BIG; + mutex->depth++; + return 0; + } + + /* Verify that the lock marker is not damaged */ + err = nfp_cpp_readl(cpp, mur, mutex->address + 4, &key); + if (err < 0) + return err; + + if (key != mutex->key) + return -EPERM; + + /* Compare against the unlocked state, and if true, + * write the interface id into the top 16 bits, and + * mark as locked. + */ + value = nfp_mutex_locked(nfp_cpp_interface(cpp)); + + /* We use test_set_imm here, as it implies a read + * of the current state, and sets the bits in the + * bytemask of the command to 1s. Since the mutex + * is guaranteed to be 64-bit aligned, the bytemask + * of this 32-bit command is ensured to be 8'b00001111, + * which implies that the lower 4 bits will be set to + * ones regardless of the initial state. + * + * Since this is a 'Readback' operation, with no Pull + * data, we can treat this as a normal Push (read) + * atomic, which returns the original value. + */ + err = nfp_cpp_readl(cpp, mus, mutex->address, &tmp); + if (err < 0) + return err; + + /* Was it unlocked? */ + if (nfp_mutex_is_unlocked(tmp)) { + /* The read value can only be 0x....0000 in the unlocked state. + * If there was another contending for this lock, then + * the lock state would be 0x....000f + */ + + /* Write our owner ID into the lock + * While not strictly necessary, this helps with + * debug and bookkeeping. + */ + err = nfp_cpp_writel(cpp, muw, mutex->address, value); + if (err < 0) + return err; + + mutex->depth = 1; + return 0; + } + + return nfp_mutex_is_locked(tmp) ? -EBUSY : -EINVAL; +} -- cgit From 61e81abdceff5c0d756c04855f825f516141b516 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:11 -0700 Subject: nfp: document expected locking in the core Document which fields of nfp_cpp are protected by which locks. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/nfpcore/nfp_cppcore.c | 33 ++++++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c index 62aa7bcee93d..4e08362d8c97 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c @@ -65,28 +65,49 @@ struct nfp_cpp_resource { u64 end; }; +/** + * struct nfp_cpp - main nfpcore device structure + * Following fields are read-only after probe() exits or netdevs are spawned. + * @dev: embedded device structure + * @op: low-level implementation ops + * @priv: private data of the low-level implementation + * @model: chip model + * @interface: chip interface id we are using to reach it + * @serial: chip serial number + * @imb_cat_table: CPP Mapping Table + * + * Following fields can be used only in probe() or with rtnl held: + * @hwinfo: HWInfo database fetched from the device + * @rtsym: firmware run time symbols + * + * Following fields use explicit locking: + * @resource_list: NFP CPP resource list + * @resource_lock: protects @resource_list + * + * @area_cache_list: cached areas for cpp/xpb read/write speed up + * @area_cache_mutex: protects @area_cache_list + * + * @waitq: area wait queue + */ struct nfp_cpp { struct device dev; - void *priv; /* Private data of the low-level implementation */ + void *priv; u32 model; u16 interface; u8 serial[NFP_SERIAL_LEN]; const struct nfp_cpp_operations *op; - struct list_head resource_list; /* NFP CPP resource list */ + struct list_head resource_list; rwlock_t resource_lock; wait_queue_head_t waitq; - /* NFP6000 CPP Mapping Table */ u32 imb_cat_table[16]; - /* Cached areas for cpp/xpb readl/writel speedups */ - struct mutex area_cache_mutex; /* Lock for the area cache */ + struct mutex area_cache_mutex; struct list_head area_cache_list; - /* Cached information */ void *hwinfo; void *rtsym; }; -- cgit From a831ffb56048fdd1d99178c34d05469421fd2a36 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:12 -0700 Subject: nfp: lock area cache earlier We shouldn't access area_cache_list without its lock even to check if it's empty. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c index 4e08362d8c97..5189fedb0f4f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c @@ -821,10 +821,7 @@ area_cache_get(struct nfp_cpp *cpp, u32 id, * the need for special case code below when * checking against available cache size. */ - if (length == 0) - return NULL; - - if (list_empty(&cpp->area_cache_list) || id == 0) + if (length == 0 || id == 0) return NULL; /* Remap from cpp_island to cpp_target */ @@ -832,10 +829,15 @@ area_cache_get(struct nfp_cpp *cpp, u32 id, if (err < 0) return NULL; - addr += *offset; - mutex_lock(&cpp->area_cache_mutex); + if (list_empty(&cpp->area_cache_list)) { + mutex_unlock(&cpp->area_cache_mutex); + return NULL; + } + + addr += *offset; + /* See if we have a match */ list_for_each_entry(cache, &cpp->area_cache_list, entry) { if (id == cache->id && -- cgit From 69a4aa89315819e9eabd3a43211506d7302121a6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:13 -0700 Subject: nfp: correct return codes when msleep gets interrupted msleep_interruptible() returns time left to wait, not error code. Return ERESTARTSYS when interrupted. While at it correct a comment and make the polling a bit more aggressive. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 34c50987c377..17822ae4a17f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -209,9 +209,8 @@ nfp_nsp_wait_reg(struct nfp_cpp *cpp, u64 *reg, if ((*reg & mask) == val) return 0; - err = msleep_interruptible(100); - if (err) - return err; + if (msleep_interruptible(25)) + return -ERESTARTSYS; if (time_after(start_time, wait_until)) return -ETIMEDOUT; @@ -228,7 +227,7 @@ nfp_nsp_wait_reg(struct nfp_cpp *cpp, u64 *reg, * * Return: 0 for success with no result * - * 1..255 for NSP completion with a result code + * positive value for NSP completion with a result code * * -EAGAIN if the NSP is not yet present * -ENODEV if the NSP is not a supported model @@ -380,9 +379,10 @@ int nfp_nsp_wait(struct nfp_nsp *state) if (err != -EAGAIN) break; - err = msleep_interruptible(100); - if (err) + if (msleep_interruptible(25)) { + err = -ERESTARTSYS; break; + } if (time_after(start_time, wait_until)) { err = -ETIMEDOUT; -- cgit From 76e8f93e8951779119de5e56a889ff30d2c4038e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:14 -0700 Subject: nfp: don't ignore return value of wait_event_interruptible When signal interrupts waiting for an area to become available we assume success. Pay attention to the return code. Unpack the code a little bit to make it more readable. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/nfpcore/nfp_cppcore.c | 56 +++++++++++++++------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c index 5189fedb0f4f..2e4796b52b84 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c @@ -411,9 +411,43 @@ nfp_cpp_area_alloc(struct nfp_cpp *cpp, u32 dest, */ void nfp_cpp_area_free(struct nfp_cpp_area *area) { + if (atomic_read(&area->refcount)) + nfp_warn(area->cpp, "Warning: freeing busy area\n"); nfp_cpp_area_put(area); } +static bool nfp_cpp_area_acquire_try(struct nfp_cpp_area *area, int *status) +{ + *status = area->cpp->op->area_acquire(area); + + return *status != -EAGAIN; +} + +static int __nfp_cpp_area_acquire(struct nfp_cpp_area *area) +{ + int err, status; + + if (atomic_inc_return(&area->refcount) > 1) + return 0; + + if (!area->cpp->op->area_acquire) + return 0; + + err = wait_event_interruptible(area->cpp->waitq, + nfp_cpp_area_acquire_try(area, &status)); + if (!err) + err = status; + if (err) { + nfp_warn(area->cpp, "Warning: area wait failed: %d\n", err); + atomic_dec(&area->refcount); + return err; + } + + nfp_cpp_area_get(area); + + return 0; +} + /** * nfp_cpp_area_acquire() - lock down a CPP area for access * @area: CPP area handle @@ -425,27 +459,13 @@ void nfp_cpp_area_free(struct nfp_cpp_area *area) */ int nfp_cpp_area_acquire(struct nfp_cpp_area *area) { - mutex_lock(&area->mutex); - if (atomic_inc_return(&area->refcount) == 1) { - int (*a_a)(struct nfp_cpp_area *); - - a_a = area->cpp->op->area_acquire; - if (a_a) { - int err; + int ret; - wait_event_interruptible(area->cpp->waitq, - (err = a_a(area)) != -EAGAIN); - if (err < 0) { - atomic_dec(&area->refcount); - mutex_unlock(&area->mutex); - return err; - } - } - } + mutex_lock(&area->mutex); + ret = __nfp_cpp_area_acquire(area); mutex_unlock(&area->mutex); - nfp_cpp_area_get(area); - return 0; + return ret; } /** -- cgit From 1bb665e3dd67f6f3477c55c2b247b42b1a5bfc95 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:15 -0700 Subject: nfp: fix invalid area detection Core should detect when someone is trying to request an access window which is too large for a given type of access. Otherwise the requester will be put on a wait queue for ever without any error message. Add const qualifiers to clarify that we are only looking at read- -only members in relevant functions. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c | 29 +++++++++++----------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c index 15cc3e77cf6a..43dc68e01274 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c @@ -217,7 +217,7 @@ static resource_size_t nfp_bar_resource_start(struct nfp_bar *bar) #define TARGET_WIDTH_64 8 static int -compute_bar(struct nfp6000_pcie *nfp, struct nfp_bar *bar, +compute_bar(const struct nfp6000_pcie *nfp, const struct nfp_bar *bar, u32 *bar_config, u64 *bar_base, int tgt, int act, int tok, u64 offset, size_t size, int width) { @@ -410,35 +410,36 @@ find_matching_bar(struct nfp6000_pcie *nfp, /* Return EAGAIN if no resource is available */ static int -find_unused_bar_noblock(struct nfp6000_pcie *nfp, +find_unused_bar_noblock(const struct nfp6000_pcie *nfp, int tgt, int act, int tok, u64 offset, size_t size, int width) { - int n, invalid = 0; + int n, busy = 0; for (n = 0; n < nfp->bars; n++) { - struct nfp_bar *bar = &nfp->bar[n]; + const struct nfp_bar *bar = &nfp->bar[n]; int err; - if (bar->bitsize == 0) { - invalid++; - continue; - } - - if (atomic_read(&bar->refcnt) != 0) + if (!bar->bitsize) continue; /* Just check to see if we can make it fit... */ err = compute_bar(nfp, bar, NULL, NULL, tgt, act, tok, offset, size, width); + if (err) + continue; - if (err < 0) - invalid++; - else + if (!atomic_read(&bar->refcnt)) return n; + + busy++; } - return (n == invalid) ? -EINVAL : -EAGAIN; + if (WARN(!busy, "No suitable BAR found for request tgt:0x%x act:0x%x tok:0x%x off:0x%llx size:%zd width:%d\n", + tgt, act, tok, offset, size, width)) + return -EINVAL; + + return -EAGAIN; } static int -- cgit From 7d2da6038218ea08f3df74046f32bdff3589cf00 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:16 -0700 Subject: nfp: fix nfp_cpp_read()/nfp_cpp_write() error paths When acquiring an area fails we can't call function doing both release and free. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c index 2e4796b52b84..e2abba4c3a3f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cppcore.c @@ -951,12 +951,14 @@ int nfp_cpp_read(struct nfp_cpp *cpp, u32 destination, return -ENOMEM; err = nfp_cpp_area_acquire(area); - if (err) - goto out; + if (err) { + nfp_cpp_area_free(area); + return err; + } } err = nfp_cpp_area_read(area, offset, kernel_vaddr, length); -out: + if (cache) area_cache_put(cpp, cache); else @@ -993,13 +995,14 @@ int nfp_cpp_write(struct nfp_cpp *cpp, u32 destination, return -ENOMEM; err = nfp_cpp_area_acquire(area); - if (err) - goto out; + if (err) { + nfp_cpp_area_free(area); + return err; + } } err = nfp_cpp_area_write(area, offset, kernel_vaddr, length); -out: if (cache) area_cache_put(cpp, cache); else -- cgit From 87232d9615194334df75547db9a433293132d779 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:17 -0700 Subject: nfp: don't use netdev_warn() before netdev is registered Fix warning which was using netdev_warn() instead of dev_warn() to early. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index f134f1808b9a..19f9d95faea4 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -336,9 +336,9 @@ nfp_net_irqs_assign(struct nfp_net *nn, struct msix_entry *irq_entries, if (dp->num_rx_rings > dp->num_r_vecs || dp->num_tx_rings > dp->num_r_vecs) - nn_warn(nn, "More rings (%d,%d) than vectors (%d).\n", - dp->num_rx_rings, dp->num_tx_rings, - dp->num_r_vecs); + dev_warn(nn->dp.dev, "More rings (%d,%d) than vectors (%d).\n", + dp->num_rx_rings, dp->num_tx_rings, + dp->num_r_vecs); dp->num_rx_rings = min(dp->num_r_vecs, dp->num_rx_rings); dp->num_tx_rings = min(dp->num_r_vecs, dp->num_tx_rings); -- cgit From 83d08a1d746522c3286c2f52608bea0f79c0e9df Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:18 -0700 Subject: nfp: remove RX queue pointers NFP6000 doesn't use queue pointers/doorbells for RX, it uses 'done' bit in descriptors. Remove the pointers from data structures. Since we are saving space in rx_ring structure make fields we previously compressed to 16bits word size again. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 8 ++------ drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 3 --- drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c | 15 ++++----------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 4d45f4573b57..8e04aa0e6e87 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -307,9 +307,7 @@ struct nfp_net_rx_buf { * @rd_p: FL/RX ring read pointer (free running) * @idx: Ring index from Linux's perspective * @fl_qcidx: Queue Controller Peripheral (QCP) queue index for the freelist - * @rx_qcidx: Queue Controller Peripheral (QCP) queue index for the RX queue * @qcp_fl: Pointer to base of the QCP freelist queue - * @qcp_rx: Pointer to base of the QCP RX queue * @wr_ptr_add: Accumulated number of buffers to add to QCP write pointer * (used for free list batching) * @rxbufs: Array of transmitted FL/RX buffers @@ -324,13 +322,11 @@ struct nfp_net_rx_ring { u32 wr_p; u32 rd_p; - u16 idx; - u16 wr_ptr_add; + u32 idx; + u32 wr_ptr_add; int fl_qcidx; - int rx_qcidx; u8 __iomem *qcp_fl; - u8 __iomem *qcp_rx; struct nfp_net_rx_buf *rxbufs; struct nfp_net_rx_desc *rxds; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 19f9d95faea4..255294b8bc5f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -479,10 +479,7 @@ nfp_net_rx_ring_init(struct nfp_net_rx_ring *rx_ring, rx_ring->r_vec = r_vec; rx_ring->fl_qcidx = rx_ring->idx * nn->stride_rx; - rx_ring->rx_qcidx = rx_ring->fl_qcidx + (nn->stride_rx - 1); - rx_ring->qcp_fl = nn->rx_bar + NFP_QCP_QUEUE_OFF(rx_ring->fl_qcidx); - rx_ring->qcp_rx = nn->rx_bar + NFP_QCP_QUEUE_OFF(rx_ring->rx_qcidx); } /** diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c index 74125584260b..4077c59bf782 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c @@ -40,9 +40,9 @@ static struct dentry *nfp_dir; static int nfp_net_debugfs_rx_q_read(struct seq_file *file, void *data) { - int fl_rd_p, fl_wr_p, rx_rd_p, rx_wr_p, rxd_cnt; struct nfp_net_r_vector *r_vec = file->private; struct nfp_net_rx_ring *rx_ring; + int fl_rd_p, fl_wr_p, rxd_cnt; struct nfp_net_rx_desc *rxd; struct nfp_net *nn; void *frag; @@ -61,14 +61,11 @@ static int nfp_net_debugfs_rx_q_read(struct seq_file *file, void *data) fl_rd_p = nfp_qcp_rd_ptr_read(rx_ring->qcp_fl); fl_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_fl); - rx_rd_p = nfp_qcp_rd_ptr_read(rx_ring->qcp_rx); - rx_wr_p = nfp_qcp_wr_ptr_read(rx_ring->qcp_rx); - seq_printf(file, "RX[%02d,%02d,%02d]: cnt=%d dma=%pad host=%p H_RD=%d H_WR=%d FL_RD=%d FL_WR=%d RX_RD=%d RX_WR=%d\n", - rx_ring->idx, rx_ring->fl_qcidx, rx_ring->rx_qcidx, + seq_printf(file, "RX[%02d,%02d]: cnt=%d dma=%pad host=%p H_RD=%d H_WR=%d FL_RD=%d FL_WR=%d\n", + rx_ring->idx, rx_ring->fl_qcidx, rx_ring->cnt, &rx_ring->dma, rx_ring->rxds, - rx_ring->rd_p, rx_ring->wr_p, - fl_rd_p, fl_wr_p, rx_rd_p, rx_wr_p); + rx_ring->rd_p, rx_ring->wr_p, fl_rd_p, fl_wr_p); for (i = 0; i < rxd_cnt; i++) { rxd = &rx_ring->rxds[i]; @@ -91,10 +88,6 @@ static int nfp_net_debugfs_rx_q_read(struct seq_file *file, void *data) seq_puts(file, " FL_RD"); if (i == fl_wr_p % rxd_cnt) seq_puts(file, " FL_WR"); - if (i == rx_rd_p % rxd_cnt) - seq_puts(file, " RX_RD"); - if (i == rx_wr_p % rxd_cnt) - seq_puts(file, " RX_WR"); seq_putc(file, '\n'); } -- cgit From 28b0cfee7b1b62ea214ebc50be5d5a92cbc30d42 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:19 -0700 Subject: nfp: flush xmit_more on error paths In case of ring full or DMA mapping error remember to flush xmit_more delayed kicks. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 255294b8bc5f..d35eeba86bac 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -759,6 +759,7 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) nn_dp_warn(dp, "TX ring %d busy. wrp=%u rdp=%u\n", qidx, tx_ring->wr_p, tx_ring->rd_p); netif_tx_stop_queue(nd_q); + nfp_net_tx_xmit_more_flush(tx_ring); u64_stats_update_begin(&r_vec->tx_sync); r_vec->tx_busy++; u64_stats_update_end(&r_vec->tx_sync); @@ -867,6 +868,7 @@ err_unmap: tx_ring->txbufs[wr_idx].fidx = -2; err_free: nn_dp_warn(dp, "Failed to map DMA TX buffer\n"); + nfp_net_tx_xmit_more_flush(tx_ring); u64_stats_update_begin(&r_vec->tx_sync); r_vec->tx_errors++; u64_stats_update_end(&r_vec->tx_sync); -- cgit From 219ad6c1c35c95ff99a0c65cfba0ca9996831af5 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:20 -0700 Subject: nfp: remove defensive checks around ndo_open()/ndo_close() Device open and close handlers check if the device is already in the desired state. Thanks to our reconfig infrastructure this should not be necessary, there doesn't seem to be any code in the driver which depends on it. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index d35eeba86bac..e12353a7c83c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2233,11 +2233,6 @@ static int nfp_net_netdev_open(struct net_device *netdev) struct nfp_net *nn = netdev_priv(netdev); int err, r; - if (nn->dp.ctrl & NFP_NET_CFG_CTRL_ENABLE) { - nn_err(nn, "Dev is already enabled: 0x%08x\n", nn->dp.ctrl); - return -EBUSY; - } - /* Step 1: Allocate resources for rings and the like * - Request interrupts * - Allocate RX and TX ring resources @@ -2368,11 +2363,6 @@ static int nfp_net_netdev_close(struct net_device *netdev) { struct nfp_net *nn = netdev_priv(netdev); - if (!(nn->dp.ctrl & NFP_NET_CFG_CTRL_ENABLE)) { - nn_err(nn, "Dev is not up: 0x%08x\n", nn->dp.ctrl); - return 0; - } - /* Step 1: Disable RX and TX rings from the Linux kernel perspective */ nfp_net_close_stack(nn); -- cgit From ac0488ef59a54e42ad744ae1a91fafbcb2566a06 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 21 Mar 2017 17:59:21 -0700 Subject: nfp: disable FW on reconfiguration errors Since we no longer need to keep the FW enabled for .ndo_close() to work we can always stop FW after reconfiguration failure. This seems to make most FWs more resilient to faults (at least in error injection scenarios). Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_common.c | 29 ++++++++-------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index e12353a7c83c..8f2da128ce0f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2127,7 +2127,11 @@ nfp_net_tx_ring_hw_cfg_write(struct nfp_net *nn, nn_writeb(nn, NFP_NET_CFG_TXR_VEC(idx), tx_ring->r_vec->irq_entry); } -static int __nfp_net_set_config_and_enable(struct nfp_net *nn) +/** + * nfp_net_set_config_and_enable() - Write control BAR and enable NFP + * @nn: NFP Net device to reconfigure + */ +static int nfp_net_set_config_and_enable(struct nfp_net *nn) { u32 new_ctrl, update = 0; unsigned int r; @@ -2176,6 +2180,10 @@ static int __nfp_net_set_config_and_enable(struct nfp_net *nn) nn_writel(nn, NFP_NET_CFG_CTRL, new_ctrl); err = nfp_net_reconfig(nn, update); + if (err) { + nfp_net_clear_config_and_disable(nn); + return err; + } nn->dp.ctrl = new_ctrl; @@ -2191,22 +2199,7 @@ static int __nfp_net_set_config_and_enable(struct nfp_net *nn) udp_tunnel_get_rx_info(nn->dp.netdev); } - return err; -} - -/** - * nfp_net_set_config_and_enable() - Write control BAR and enable NFP - * @nn: NFP Net device to reconfigure - */ -static int nfp_net_set_config_and_enable(struct nfp_net *nn) -{ - int err; - - err = __nfp_net_set_config_and_enable(nn); - if (err) - nfp_net_clear_config_and_disable(nn); - - return err; + return 0; } /** @@ -2447,7 +2440,7 @@ static int nfp_net_dp_swap_enable(struct nfp_net *nn, struct nfp_net_dp *dp) return err; } - return __nfp_net_set_config_and_enable(nn); + return nfp_net_set_config_and_enable(nn); } struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn) -- cgit From bbea124bc99df968011e76eba105fe964a4eceab Mon Sep 17 00:00:00 2001 From: Joel Scherpelz Date: Wed, 22 Mar 2017 18:19:04 +0900 Subject: net: ipv6: Add sysctl for minimum prefix len acceptable in RIOs. This commit adds a new sysctl accept_ra_rt_info_min_plen that defines the minimum acceptable prefix length of Route Information Options. The new sysctl is intended to be used together with accept_ra_rt_info_max_plen to configure a range of acceptable prefix lengths. It is useful to prevent misconfigurations from unintentionally blackholing too much of the IPv6 address space (e.g., home routers announcing RIOs for fc00::/7, which is incorrect). Signed-off-by: Joel Scherpelz Acked-by: Lorenzo Colitti Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 13 +++++++++++-- include/linux/ipv6.h | 1 + include/uapi/linux/ipv6.h | 1 + include/uapi/linux/sysctl.h | 1 + net/ipv6/addrconf.c | 10 ++++++++++ net/ipv6/ndisc.c | 2 ++ 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index b57308e76b1d..eaee2c8d4c00 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1461,11 +1461,20 @@ accept_ra_pinfo - BOOLEAN Functional default: enabled if accept_ra is enabled. disabled if accept_ra is disabled. +accept_ra_rt_info_min_plen - INTEGER + Minimum prefix length of Route Information in RA. + + Route Information w/ prefix smaller than this variable shall + be ignored. + + Functional default: 0 if accept_ra_rtr_pref is enabled. + -1 if accept_ra_rtr_pref is disabled. + accept_ra_rt_info_max_plen - INTEGER Maximum prefix length of Route Information in RA. - Route Information w/ prefix larger than or equal to this - variable shall be ignored. + Route Information w/ prefix larger than this variable shall + be ignored. Functional default: 0 if accept_ra_rtr_pref is enabled. -1 if accept_ra_rtr_pref is disabled. diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index f0d79bd054ca..e1b442996f81 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -37,6 +37,7 @@ struct ipv6_devconf { __s32 accept_ra_rtr_pref; __s32 rtr_probe_interval; #ifdef CONFIG_IPV6_ROUTE_INFO + __s32 accept_ra_rt_info_min_plen; __s32 accept_ra_rt_info_max_plen; #endif #endif diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index d8f6a1ac9af4..2ae59178189d 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -184,6 +184,7 @@ enum { DEVCONF_ENHANCED_DAD, DEVCONF_ADDR_GEN_MODE, DEVCONF_DISABLE_POLICY, + DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN, DEVCONF_MAX }; diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h index d2b12152e358..e13d48058b8d 100644 --- a/include/uapi/linux/sysctl.h +++ b/include/uapi/linux/sysctl.h @@ -568,6 +568,7 @@ enum { NET_IPV6_PROXY_NDP=23, NET_IPV6_ACCEPT_SOURCE_ROUTE=25, NET_IPV6_ACCEPT_RA_FROM_LOCAL=26, + NET_IPV6_ACCEPT_RA_RT_INFO_MIN_PLEN=27, __NET_IPV6_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 8c69768a5c46..dff5beb26a01 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -224,6 +224,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .accept_ra_rtr_pref = 1, .rtr_probe_interval = 60 * HZ, #ifdef CONFIG_IPV6_ROUTE_INFO + .accept_ra_rt_info_min_plen = 0, .accept_ra_rt_info_max_plen = 0, #endif #endif @@ -277,6 +278,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .accept_ra_rtr_pref = 1, .rtr_probe_interval = 60 * HZ, #ifdef CONFIG_IPV6_ROUTE_INFO + .accept_ra_rt_info_min_plen = 0, .accept_ra_rt_info_max_plen = 0, #endif #endif @@ -4979,6 +4981,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_RTR_PROBE_INTERVAL] = jiffies_to_msecs(cnf->rtr_probe_interval); #ifdef CONFIG_IPV6_ROUTE_INFO + array[DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN] = cnf->accept_ra_rt_info_min_plen; array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen; #endif #endif @@ -6121,6 +6124,13 @@ static const struct ctl_table addrconf_sysctl[] = { .proc_handler = proc_dointvec_jiffies, }, #ifdef CONFIG_IPV6_ROUTE_INFO + { + .procname = "accept_ra_rt_info_min_plen", + .data = &ipv6_devconf.accept_ra_rt_info_min_plen, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { .procname = "accept_ra_rt_info_max_plen", .data = &ipv6_devconf.accept_ra_rt_info_max_plen, diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 112ccbc0a8ac..b5812b3f7539 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1418,6 +1418,8 @@ skip_linkparms: if (ri->prefix_len == 0 && !in6_dev->cnf.accept_ra_defrtr) continue; + if (ri->prefix_len < in6_dev->cnf.accept_ra_rt_info_min_plen) + continue; if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen) continue; rt6_route_rcv(skb->dev, (u8 *)p, (p->nd_opt_len) << 3, -- cgit From b4f0a66155564aaf7e98492e027efad9f797c244 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Wed, 22 Mar 2017 11:56:05 +0000 Subject: net: stmmac: fix dma operation mode config for older versions The dma operation mode configuration routine was wrongly moved to a function (stmmac_mtl_configuration) that is only executed if the core version is >= 4.00. Fixes: 6deee2221e11 ("net: stmmac: prepare dma op mode config for multiple queues") Reported-by: Corentin Labbe Reviewed-by: Thierry Reding Signed-off-by: Joao Pinto Tested-by: Corentin Labbe Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 98e0f80de9d8..4b418d2aec38 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2388,9 +2388,6 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv) if (priv->hw->mac->rx_queue_enable) stmmac_mac_enable_rx_queues(priv); - /* Set the HW DMA mode and the COE */ - stmmac_dma_operation_mode(priv); - /* Set RX priorities */ if (rx_queues_count > 1 && priv->hw->mac->rx_queue_prio) stmmac_mac_config_rx_queues_prio(priv); @@ -2468,6 +2465,9 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) else stmmac_set_mac(priv->ioaddr, true); + /* Set the HW DMA mode and the COE */ + stmmac_dma_operation_mode(priv); + stmmac_mmc_setup(priv); if (init_ptp) { -- cgit From fad73a1a35ea61f13607a391aca669caad8c04ca Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Wed, 22 Mar 2017 10:00:32 -0700 Subject: bpf: Fix and simplifications on inline map lookup Fix in verifier: For the same bpf_map_lookup_elem() instruction (i.e. "call 1"), a broken case is "a different type of map could be used for the same lookup instruction". For example, an array in one case and a hashmap in another. We have to resort to the old dynamic call behavior in this case. The fix is to check for collision on insn_aux->map_ptr. If there is collision, don't inline the map lookup. Please see the "do_reg_lookup()" in test_map_in_map_kern.c in the later patch for how-to trigger the above case. Simplifications on array_map_gen_lookup(): 1. Calculate elem_size from map->value_size. It removes the need for 'struct bpf_array' which makes the later map-in-map implementation easier. 2. Remove the 'elem_size == 1' test Fixes: 81ed18ab3098 ("bpf: add helper inlining infra and optimize map_array lookup") Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- kernel/bpf/arraymap.c | 11 ++++------- kernel/bpf/verifier.c | 13 +++++++++++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index bcf9955fac95..4d7d5d0ed76a 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -117,20 +117,17 @@ static void *array_map_lookup_elem(struct bpf_map *map, void *key) /* emit BPF instructions equivalent to C code of array_map_lookup_elem() */ static u32 array_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf) { - struct bpf_array *array = container_of(map, struct bpf_array, map); struct bpf_insn *insn = insn_buf; - u32 elem_size = array->elem_size; + u32 elem_size = round_up(map->value_size, 8); const int ret = BPF_REG_0; const int map_ptr = BPF_REG_1; const int index = BPF_REG_2; *insn++ = BPF_ALU64_IMM(BPF_ADD, map_ptr, offsetof(struct bpf_array, value)); *insn++ = BPF_LDX_MEM(BPF_W, ret, index, 0); - *insn++ = BPF_JMP_IMM(BPF_JGE, ret, array->map.max_entries, - elem_size == 1 ? 2 : 3); - if (elem_size == 1) { - /* nop */ - } else if (is_power_of_2(elem_size)) { + *insn++ = BPF_JMP_IMM(BPF_JGE, ret, map->max_entries, 3); + + if (is_power_of_2(elem_size)) { *insn++ = BPF_ALU64_IMM(BPF_LSH, ret, ilog2(elem_size)); } else { *insn++ = BPF_ALU64_IMM(BPF_MUL, ret, elem_size); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 90bf46787603..9bf82267f2f9 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -143,6 +143,8 @@ struct bpf_verifier_stack_elem { #define BPF_COMPLEXITY_LIMIT_INSNS 65536 #define BPF_COMPLEXITY_LIMIT_STACK 1024 +#define BPF_MAP_PTR_POISON ((void *)0xeB9F + POISON_POINTER_DELTA) + struct bpf_call_arg_meta { struct bpf_map *map_ptr; bool raw_mode; @@ -1357,6 +1359,8 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) } else if (fn->ret_type == RET_VOID) { regs[BPF_REG_0].type = NOT_INIT; } else if (fn->ret_type == RET_PTR_TO_MAP_VALUE_OR_NULL) { + struct bpf_insn_aux_data *insn_aux; + regs[BPF_REG_0].type = PTR_TO_MAP_VALUE_OR_NULL; regs[BPF_REG_0].max_value = regs[BPF_REG_0].min_value = 0; /* remember map_ptr, so that check_map_access() @@ -1369,7 +1373,11 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) } regs[BPF_REG_0].map_ptr = meta.map_ptr; regs[BPF_REG_0].id = ++env->id_gen; - env->insn_aux_data[insn_idx].map_ptr = meta.map_ptr; + insn_aux = &env->insn_aux_data[insn_idx]; + if (!insn_aux->map_ptr) + insn_aux->map_ptr = meta.map_ptr; + else if (insn_aux->map_ptr != meta.map_ptr) + insn_aux->map_ptr = BPF_MAP_PTR_POISON; } else { verbose("unknown return type %d of func %s#%d\n", fn->ret_type, func_id_name(func_id), func_id); @@ -3307,7 +3315,8 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env) if (ebpf_jit_enabled() && insn->imm == BPF_FUNC_map_lookup_elem) { map_ptr = env->insn_aux_data[i + delta].map_ptr; - if (!map_ptr->ops->map_gen_lookup) + if (map_ptr == BPF_MAP_PTR_POISON || + !map_ptr->ops->map_gen_lookup) goto patch_call_imm; cnt = map_ptr->ops->map_gen_lookup(map_ptr, insn_buf); -- cgit From 56f668dfe00dcf086734f1c42ea999398fad6572 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Wed, 22 Mar 2017 10:00:33 -0700 Subject: bpf: Add array of maps support This patch adds a few helper funcs to enable map-in-map support (i.e. outer_map->inner_map). The first outer_map type BPF_MAP_TYPE_ARRAY_OF_MAPS is also added in this patch. The next patch will introduce a hash of maps type. Any bpf map type can be acted as an inner_map. The exception is BPF_MAP_TYPE_PROG_ARRAY because the extra level of indirection makes it harder to verify the owner_prog_type and owner_jited. Multi-level map-in-map is not supported (i.e. map->map is ok but not map->map->map). When adding an inner_map to an outer_map, it currently checks the map_type, key_size, value_size, map_flags, max_entries and ops. The verifier also uses those map's properties to do static analysis. map_flags is needed because we need to ensure BPF_PROG_TYPE_PERF_EVENT is using a preallocated hashtab for the inner_hash also. ops and max_entries are needed to generate inlined map-lookup instructions. For simplicity reason, a simple '==' test is used for both map_flags and max_entries. The equality of ops is implied by the equality of map_type. During outer_map creation time, an inner_map_fd is needed to create an outer_map. However, the inner_map_fd's life time does not depend on the outer_map. The inner_map_fd is merely used to initialize the inner_map_meta of the outer_map. Also, for the outer_map: * It allows element update and delete from syscall * It allows element lookup from bpf_prog The above is similar to the current fd_array pattern. Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf.h | 1 + include/uapi/linux/bpf.h | 2 + kernel/bpf/Makefile | 2 +- kernel/bpf/arraymap.c | 63 +++++++++++++++++++++++++++++++ kernel/bpf/map_in_map.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/map_in_map.h | 23 ++++++++++++ kernel/bpf/syscall.c | 7 +++- kernel/bpf/verifier.c | 42 ++++++++++++++++----- 8 files changed, 225 insertions(+), 12 deletions(-) create mode 100644 kernel/bpf/map_in_map.c create mode 100644 kernel/bpf/map_in_map.h diff --git a/include/linux/bpf.h b/include/linux/bpf.h index da8c64ca8dc9..3f3cdf9b15e8 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -50,6 +50,7 @@ struct bpf_map { const struct bpf_map_ops *ops; struct work_struct work; atomic_t usercnt; + struct bpf_map *inner_map_meta; }; struct bpf_map_type_list { diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 0539a0ceef38..1701ec1e7de3 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -96,6 +96,7 @@ enum bpf_map_type { BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, BPF_MAP_TYPE_LPM_TRIE, + BPF_MAP_TYPE_ARRAY_OF_MAPS, }; enum bpf_prog_type { @@ -152,6 +153,7 @@ union bpf_attr { __u32 value_size; /* size of value in bytes */ __u32 max_entries; /* max number of entries in a map */ __u32 map_flags; /* prealloc or not */ + __u32 inner_map_fd; /* fd pointing to the inner map */ }; struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index e1ce4f4fd7fd..e1e5e658f2db 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -1,7 +1,7 @@ obj-y := core.o obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o -obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o +obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o ifeq ($(CONFIG_PERF_EVENTS),y) obj-$(CONFIG_BPF_SYSCALL) += stackmap.o endif diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 4d7d5d0ed76a..bc9da93db403 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -17,6 +17,8 @@ #include #include +#include "map_in_map.h" + static void bpf_array_free_percpu(struct bpf_array *array) { int i; @@ -602,3 +604,64 @@ static int __init register_cgroup_array_map(void) } late_initcall(register_cgroup_array_map); #endif + +static struct bpf_map *array_of_map_alloc(union bpf_attr *attr) +{ + struct bpf_map *map, *inner_map_meta; + + inner_map_meta = bpf_map_meta_alloc(attr->inner_map_fd); + if (IS_ERR(inner_map_meta)) + return inner_map_meta; + + map = fd_array_map_alloc(attr); + if (IS_ERR(map)) { + bpf_map_meta_free(inner_map_meta); + return map; + } + + map->inner_map_meta = inner_map_meta; + + return map; +} + +static void array_of_map_free(struct bpf_map *map) +{ + /* map->inner_map_meta is only accessed by syscall which + * is protected by fdget/fdput. + */ + bpf_map_meta_free(map->inner_map_meta); + bpf_fd_array_map_clear(map); + fd_array_map_free(map); +} + +static void *array_of_map_lookup_elem(struct bpf_map *map, void *key) +{ + struct bpf_map **inner_map = array_map_lookup_elem(map, key); + + if (!inner_map) + return NULL; + + return READ_ONCE(*inner_map); +} + +static const struct bpf_map_ops array_of_map_ops = { + .map_alloc = array_of_map_alloc, + .map_free = array_of_map_free, + .map_get_next_key = array_map_get_next_key, + .map_lookup_elem = array_of_map_lookup_elem, + .map_delete_elem = fd_array_map_delete_elem, + .map_fd_get_ptr = bpf_map_fd_get_ptr, + .map_fd_put_ptr = bpf_map_fd_put_ptr, +}; + +static struct bpf_map_type_list array_of_map_type __ro_after_init = { + .ops = &array_of_map_ops, + .type = BPF_MAP_TYPE_ARRAY_OF_MAPS, +}; + +static int __init register_array_of_map(void) +{ + bpf_register_map_type(&array_of_map_type); + return 0; +} +late_initcall(register_array_of_map); diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c new file mode 100644 index 000000000000..59bcdf821ae4 --- /dev/null +++ b/kernel/bpf/map_in_map.c @@ -0,0 +1,97 @@ +/* Copyright (c) 2017 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include +#include + +#include "map_in_map.h" + +struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd) +{ + struct bpf_map *inner_map, *inner_map_meta; + struct fd f; + + f = fdget(inner_map_ufd); + inner_map = __bpf_map_get(f); + if (IS_ERR(inner_map)) + return inner_map; + + /* prog_array->owner_prog_type and owner_jited + * is a runtime binding. Doing static check alone + * in the verifier is not enough. + */ + if (inner_map->map_type == BPF_MAP_TYPE_PROG_ARRAY) { + fdput(f); + return ERR_PTR(-ENOTSUPP); + } + + /* Does not support >1 level map-in-map */ + if (inner_map->inner_map_meta) { + fdput(f); + return ERR_PTR(-EINVAL); + } + + inner_map_meta = kzalloc(sizeof(*inner_map_meta), GFP_USER); + if (!inner_map_meta) { + fdput(f); + return ERR_PTR(-ENOMEM); + } + + inner_map_meta->map_type = inner_map->map_type; + inner_map_meta->key_size = inner_map->key_size; + inner_map_meta->value_size = inner_map->value_size; + inner_map_meta->map_flags = inner_map->map_flags; + inner_map_meta->ops = inner_map->ops; + inner_map_meta->max_entries = inner_map->max_entries; + + fdput(f); + return inner_map_meta; +} + +void bpf_map_meta_free(struct bpf_map *map_meta) +{ + kfree(map_meta); +} + +bool bpf_map_meta_equal(const struct bpf_map *meta0, + const struct bpf_map *meta1) +{ + /* No need to compare ops because it is covered by map_type */ + return meta0->map_type == meta1->map_type && + meta0->key_size == meta1->key_size && + meta0->value_size == meta1->value_size && + meta0->map_flags == meta1->map_flags && + meta0->max_entries == meta1->max_entries; +} + +void *bpf_map_fd_get_ptr(struct bpf_map *map, + struct file *map_file /* not used */, + int ufd) +{ + struct bpf_map *inner_map; + struct fd f; + + f = fdget(ufd); + inner_map = __bpf_map_get(f); + if (IS_ERR(inner_map)) + return inner_map; + + if (bpf_map_meta_equal(map->inner_map_meta, inner_map)) + inner_map = bpf_map_inc(inner_map, false); + else + inner_map = ERR_PTR(-EINVAL); + + fdput(f); + return inner_map; +} + +void bpf_map_fd_put_ptr(void *ptr) +{ + /* ptr->ops->map_free() has to go through one + * rcu grace period by itself. + */ + bpf_map_put(ptr); +} diff --git a/kernel/bpf/map_in_map.h b/kernel/bpf/map_in_map.h new file mode 100644 index 000000000000..177fadb689dc --- /dev/null +++ b/kernel/bpf/map_in_map.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2017 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#ifndef __MAP_IN_MAP_H__ +#define __MAP_IN_MAP_H__ + +#include + +struct file; +struct bpf_map; + +struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd); +void bpf_map_meta_free(struct bpf_map *map_meta); +bool bpf_map_meta_equal(const struct bpf_map *meta0, + const struct bpf_map *meta1); +void *bpf_map_fd_get_ptr(struct bpf_map *map, struct file *map_file, + int ufd); +void bpf_map_fd_put_ptr(void *ptr); + +#endif diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 48c914b983bd..6e24fdf1f373 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -215,7 +215,7 @@ int bpf_map_new_fd(struct bpf_map *map) offsetof(union bpf_attr, CMD##_LAST_FIELD) - \ sizeof(attr->CMD##_LAST_FIELD)) != NULL -#define BPF_MAP_CREATE_LAST_FIELD map_flags +#define BPF_MAP_CREATE_LAST_FIELD inner_map_fd /* called via syscall */ static int map_create(union bpf_attr *attr) { @@ -352,6 +352,8 @@ static int map_lookup_elem(union bpf_attr *attr) err = bpf_percpu_array_copy(map, key, value); } else if (map->map_type == BPF_MAP_TYPE_STACK_TRACE) { err = bpf_stackmap_copy(map, key, value); + } else if (map->map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS) { + err = -ENOTSUPP; } else { rcu_read_lock(); ptr = map->ops->map_lookup_elem(map, key); @@ -438,7 +440,8 @@ static int map_update_elem(union bpf_attr *attr) err = bpf_percpu_array_update(map, key, value, attr->flags); } else if (map->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || map->map_type == BPF_MAP_TYPE_PROG_ARRAY || - map->map_type == BPF_MAP_TYPE_CGROUP_ARRAY) { + map->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || + map->map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS) { rcu_read_lock(); err = bpf_fd_array_map_update_elem(map, f.file, key, value, attr->flags); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 9bf82267f2f9..3b8f528c5473 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1199,6 +1199,9 @@ static int check_map_func_compatibility(struct bpf_map *map, int func_id) func_id != BPF_FUNC_current_task_under_cgroup) goto error; break; + case BPF_MAP_TYPE_ARRAY_OF_MAPS: + if (func_id != BPF_FUNC_map_lookup_elem) + goto error; default: break; } @@ -2101,14 +2104,19 @@ static void mark_map_reg(struct bpf_reg_state *regs, u32 regno, u32 id, struct bpf_reg_state *reg = ®s[regno]; if (reg->type == PTR_TO_MAP_VALUE_OR_NULL && reg->id == id) { - reg->type = type; + if (type == UNKNOWN_VALUE) { + __mark_reg_unknown_value(regs, regno); + } else if (reg->map_ptr->inner_map_meta) { + reg->type = CONST_PTR_TO_MAP; + reg->map_ptr = reg->map_ptr->inner_map_meta; + } else { + reg->type = type; + } /* We don't need id from this point onwards anymore, thus we * should better reset it, so that state pruning has chances * to take effect. */ reg->id = 0; - if (type == UNKNOWN_VALUE) - __mark_reg_unknown_value(regs, regno); } } @@ -3033,16 +3041,32 @@ process_bpf_exit: return 0; } +static int check_map_prealloc(struct bpf_map *map) +{ + return (map->map_type != BPF_MAP_TYPE_HASH && + map->map_type != BPF_MAP_TYPE_PERCPU_HASH) || + !(map->map_flags & BPF_F_NO_PREALLOC); +} + static int check_map_prog_compatibility(struct bpf_map *map, struct bpf_prog *prog) { - if (prog->type == BPF_PROG_TYPE_PERF_EVENT && - (map->map_type == BPF_MAP_TYPE_HASH || - map->map_type == BPF_MAP_TYPE_PERCPU_HASH) && - (map->map_flags & BPF_F_NO_PREALLOC)) { - verbose("perf_event programs can only use preallocated hash map\n"); - return -EINVAL; + /* Make sure that BPF_PROG_TYPE_PERF_EVENT programs only use + * preallocated hash maps, since doing memory allocation + * in overflow_handler can crash depending on where nmi got + * triggered. + */ + if (prog->type == BPF_PROG_TYPE_PERF_EVENT) { + if (!check_map_prealloc(map)) { + verbose("perf_event programs can only use preallocated hash map\n"); + return -EINVAL; + } + if (map->inner_map_meta && + !check_map_prealloc(map->inner_map_meta)) { + verbose("perf_event programs can only use preallocated inner hash map\n"); + return -EINVAL; + } } return 0; } -- cgit From bcc6b1b7ebf857a9fe56202e2be3361131588c15 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Wed, 22 Mar 2017 10:00:34 -0700 Subject: bpf: Add hash of maps support This patch adds hash of maps support (hashmap->bpf_map). BPF_MAP_TYPE_HASH_OF_MAPS is added. A map-in-map contains a pointer to another map and lets call this pointer 'inner_map_ptr'. Notes on deleting inner_map_ptr from a hash map: 1. For BPF_F_NO_PREALLOC map-in-map, when deleting an inner_map_ptr, the htab_elem itself will go through a rcu grace period and the inner_map_ptr resides in the htab_elem. 2. For pre-allocated htab_elem (!BPF_F_NO_PREALLOC), when deleting an inner_map_ptr, the htab_elem may get reused immediately. This situation is similar to the existing prealloc-ated use cases. However, the bpf_map_fd_put_ptr() calls bpf_map_put() which calls inner_map->ops->map_free(inner_map) which will go through a rcu grace period (i.e. all bpf_map's map_free currently goes through a rcu grace period). Hence, the inner_map_ptr is still safe for the rcu reader side. This patch also includes BPF_MAP_TYPE_HASH_OF_MAPS to the check_map_prealloc() in the verifier. preallocation is a must for BPF_PROG_TYPE_PERF_EVENT. Hence, even we don't expect heavy updates to map-in-map, enforcing BPF_F_NO_PREALLOC for map-in-map is impossible without disallowing BPF_PROG_TYPE_PERF_EVENT from using map-in-map first. Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf.h | 2 + include/uapi/linux/bpf.h | 1 + kernel/bpf/hashtab.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 8 +++- kernel/bpf/verifier.c | 4 +- 5 files changed, 134 insertions(+), 2 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 3f3cdf9b15e8..2ae39a3e9ead 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -277,6 +277,8 @@ int bpf_stackmap_copy(struct bpf_map *map, void *key, void *value); int bpf_fd_array_map_update_elem(struct bpf_map *map, struct file *map_file, void *key, void *value, u64 map_flags); void bpf_fd_array_map_clear(struct bpf_map *map); +int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file, + void *key, void *value, u64 map_flags); /* memcpy that is used with 8-byte aligned pointers, power-of-8 size and * forced to use 'long' read/writes to try to atomically copy long counters. diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 1701ec1e7de3..ce6f029ac368 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -97,6 +97,7 @@ enum bpf_map_type { BPF_MAP_TYPE_LRU_PERCPU_HASH, BPF_MAP_TYPE_LPM_TRIE, BPF_MAP_TYPE_ARRAY_OF_MAPS, + BPF_MAP_TYPE_HASH_OF_MAPS, }; enum bpf_prog_type { diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index 000153acb6d5..343fb5394c95 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -16,6 +16,7 @@ #include #include "percpu_freelist.h" #include "bpf_lru_list.h" +#include "map_in_map.h" struct bucket { struct hlist_nulls_head head; @@ -88,6 +89,11 @@ static inline void __percpu *htab_elem_get_ptr(struct htab_elem *l, u32 key_size return *(void __percpu **)(l->key + key_size); } +static void *fd_htab_map_get_ptr(const struct bpf_map *map, struct htab_elem *l) +{ + return *(void **)(l->key + roundup(map->key_size, 8)); +} + static struct htab_elem *get_htab_elem(struct bpf_htab *htab, int i) { return (struct htab_elem *) (htab->elems + i * htab->elem_size); @@ -603,6 +609,14 @@ static void htab_elem_free_rcu(struct rcu_head *head) static void free_htab_elem(struct bpf_htab *htab, struct htab_elem *l) { + struct bpf_map *map = &htab->map; + + if (map->ops->map_fd_put_ptr) { + void *ptr = fd_htab_map_get_ptr(map, l); + + map->ops->map_fd_put_ptr(ptr); + } + if (l->state == HTAB_EXTRA_ELEM_USED) { l->state = HTAB_EXTRA_ELEM_FREE; return; @@ -1057,6 +1071,7 @@ static void delete_all_elements(struct bpf_htab *htab) } } } + /* Called when map->refcnt goes to zero, either from workqueue or from syscall */ static void htab_map_free(struct bpf_map *map) { @@ -1213,12 +1228,118 @@ static struct bpf_map_type_list htab_lru_percpu_type __ro_after_init = { .type = BPF_MAP_TYPE_LRU_PERCPU_HASH, }; +static struct bpf_map *fd_htab_map_alloc(union bpf_attr *attr) +{ + struct bpf_map *map; + + if (attr->value_size != sizeof(u32)) + return ERR_PTR(-EINVAL); + + /* pointer is stored internally */ + attr->value_size = sizeof(void *); + map = htab_map_alloc(attr); + attr->value_size = sizeof(u32); + + return map; +} + +static void fd_htab_map_free(struct bpf_map *map) +{ + struct bpf_htab *htab = container_of(map, struct bpf_htab, map); + struct hlist_nulls_node *n; + struct hlist_nulls_head *head; + struct htab_elem *l; + int i; + + for (i = 0; i < htab->n_buckets; i++) { + head = select_bucket(htab, i); + + hlist_nulls_for_each_entry_safe(l, n, head, hash_node) { + void *ptr = fd_htab_map_get_ptr(map, l); + + map->ops->map_fd_put_ptr(ptr); + } + } + + htab_map_free(map); +} + +/* only called from syscall */ +int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file, + void *key, void *value, u64 map_flags) +{ + void *ptr; + int ret; + u32 ufd = *(u32 *)value; + + ptr = map->ops->map_fd_get_ptr(map, map_file, ufd); + if (IS_ERR(ptr)) + return PTR_ERR(ptr); + + ret = htab_map_update_elem(map, key, &ptr, map_flags); + if (ret) + map->ops->map_fd_put_ptr(ptr); + + return ret; +} + +static struct bpf_map *htab_of_map_alloc(union bpf_attr *attr) +{ + struct bpf_map *map, *inner_map_meta; + + inner_map_meta = bpf_map_meta_alloc(attr->inner_map_fd); + if (IS_ERR(inner_map_meta)) + return inner_map_meta; + + map = fd_htab_map_alloc(attr); + if (IS_ERR(map)) { + bpf_map_meta_free(inner_map_meta); + return map; + } + + map->inner_map_meta = inner_map_meta; + + return map; +} + +static void *htab_of_map_lookup_elem(struct bpf_map *map, void *key) +{ + struct bpf_map **inner_map = htab_map_lookup_elem(map, key); + + if (!inner_map) + return NULL; + + return READ_ONCE(*inner_map); +} + +static void htab_of_map_free(struct bpf_map *map) +{ + bpf_map_meta_free(map->inner_map_meta); + fd_htab_map_free(map); +} + +static const struct bpf_map_ops htab_of_map_ops = { + .map_alloc = htab_of_map_alloc, + .map_free = htab_of_map_free, + .map_get_next_key = htab_map_get_next_key, + .map_lookup_elem = htab_of_map_lookup_elem, + .map_delete_elem = htab_map_delete_elem, + .map_fd_get_ptr = bpf_map_fd_get_ptr, + .map_fd_put_ptr = bpf_map_fd_put_ptr, +}; + +static struct bpf_map_type_list htab_of_map_type __ro_after_init = { + .ops = &htab_of_map_ops, + .type = BPF_MAP_TYPE_HASH_OF_MAPS, +}; + static int __init register_htab_map(void) { bpf_register_map_type(&htab_type); bpf_register_map_type(&htab_percpu_type); bpf_register_map_type(&htab_lru_type); bpf_register_map_type(&htab_lru_percpu_type); + bpf_register_map_type(&htab_of_map_type); return 0; } late_initcall(register_htab_map); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 6e24fdf1f373..c35ebfe6d84d 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -352,7 +352,8 @@ static int map_lookup_elem(union bpf_attr *attr) err = bpf_percpu_array_copy(map, key, value); } else if (map->map_type == BPF_MAP_TYPE_STACK_TRACE) { err = bpf_stackmap_copy(map, key, value); - } else if (map->map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS) { + } else if (map->map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS || + map->map_type == BPF_MAP_TYPE_HASH_OF_MAPS) { err = -ENOTSUPP; } else { rcu_read_lock(); @@ -446,6 +447,11 @@ static int map_update_elem(union bpf_attr *attr) err = bpf_fd_array_map_update_elem(map, f.file, key, value, attr->flags); rcu_read_unlock(); + } else if (map->map_type == BPF_MAP_TYPE_HASH_OF_MAPS) { + rcu_read_lock(); + err = bpf_fd_htab_map_update_elem(map, f.file, key, value, + attr->flags); + rcu_read_unlock(); } else { rcu_read_lock(); err = map->ops->map_update_elem(map, key, value, attr->flags); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 3b8f528c5473..09923cc5c7c7 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1200,6 +1200,7 @@ static int check_map_func_compatibility(struct bpf_map *map, int func_id) goto error; break; case BPF_MAP_TYPE_ARRAY_OF_MAPS: + case BPF_MAP_TYPE_HASH_OF_MAPS: if (func_id != BPF_FUNC_map_lookup_elem) goto error; default: @@ -3044,7 +3045,8 @@ process_bpf_exit: static int check_map_prealloc(struct bpf_map *map) { return (map->map_type != BPF_MAP_TYPE_HASH && - map->map_type != BPF_MAP_TYPE_PERCPU_HASH) || + map->map_type != BPF_MAP_TYPE_PERCPU_HASH && + map->map_type != BPF_MAP_TYPE_HASH_OF_MAPS) || !(map->map_flags & BPF_F_NO_PREALLOC); } -- cgit From fb30d4b71214aa1811e997f8f753b14b46d5b912 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Wed, 22 Mar 2017 10:00:35 -0700 Subject: bpf: Add tests for map-in-map Test cases for array of maps and hash of maps. Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/Makefile | 4 + samples/bpf/bpf_helpers.h | 1 + samples/bpf/bpf_load.c | 22 +++- samples/bpf/test_map_in_map_kern.c | 173 ++++++++++++++++++++++++++++ samples/bpf/test_map_in_map_user.c | 116 +++++++++++++++++++ tools/include/uapi/linux/bpf.h | 3 + tools/lib/bpf/bpf.c | 17 +++ tools/lib/bpf/bpf.h | 2 + tools/testing/selftests/bpf/test_verifier.c | 131 ++++++++++++++++++--- 9 files changed, 451 insertions(+), 18 deletions(-) create mode 100644 samples/bpf/test_map_in_map_kern.c create mode 100644 samples/bpf/test_map_in_map_user.c diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 09e9d535bd74..91c1d616d975 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -34,6 +34,7 @@ hostprogs-y += sampleip hostprogs-y += tc_l2_redirect hostprogs-y += lwt_len_hist hostprogs-y += xdp_tx_iptunnel +hostprogs-y += test_map_in_map # Libbpf dependencies LIBBPF := ../../tools/lib/bpf/bpf.o @@ -72,6 +73,7 @@ sampleip-objs := bpf_load.o $(LIBBPF) sampleip_user.o tc_l2_redirect-objs := bpf_load.o $(LIBBPF) tc_l2_redirect_user.o lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o +test_map_in_map-objs := bpf_load.o $(LIBBPF) test_map_in_map_user.o # Tell kbuild to always build the programs always := $(hostprogs-y) @@ -105,6 +107,7 @@ always += trace_event_kern.o always += sampleip_kern.o always += lwt_len_hist_kern.o always += xdp_tx_iptunnel_kern.o +always += test_map_in_map_kern.o HOSTCFLAGS += -I$(objtree)/usr/include HOSTCFLAGS += -I$(srctree)/tools/lib/ @@ -139,6 +142,7 @@ HOSTLOADLIBES_sampleip += -lelf HOSTLOADLIBES_tc_l2_redirect += -l elf HOSTLOADLIBES_lwt_len_hist += -l elf HOSTLOADLIBES_xdp_tx_iptunnel += -lelf +HOSTLOADLIBES_test_map_in_map += -lelf # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h index faaffe2e139a..52de9d88c021 100644 --- a/samples/bpf/bpf_helpers.h +++ b/samples/bpf/bpf_helpers.h @@ -80,6 +80,7 @@ struct bpf_map_def { unsigned int value_size; unsigned int max_entries; unsigned int map_flags; + unsigned int inner_map_idx; }; static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c index b86ee54da2d1..dcdce1270d38 100644 --- a/samples/bpf/bpf_load.c +++ b/samples/bpf/bpf_load.c @@ -43,6 +43,7 @@ struct bpf_map_def { unsigned int value_size; unsigned int max_entries; unsigned int map_flags; + unsigned int inner_map_idx; }; static int populate_prog_array(const char *event, int prog_fd) @@ -198,11 +199,22 @@ static int load_maps(struct bpf_map_def *maps, int len) for (i = 0; i < len / sizeof(struct bpf_map_def); i++) { - map_fd[i] = bpf_create_map(maps[i].type, - maps[i].key_size, - maps[i].value_size, - maps[i].max_entries, - maps[i].map_flags); + if (maps[i].type == BPF_MAP_TYPE_ARRAY_OF_MAPS || + maps[i].type == BPF_MAP_TYPE_HASH_OF_MAPS) { + int inner_map_fd = map_fd[maps[i].inner_map_idx]; + + map_fd[i] = bpf_create_map_in_map(maps[i].type, + maps[i].key_size, + inner_map_fd, + maps[i].max_entries, + maps[i].map_flags); + } else { + map_fd[i] = bpf_create_map(maps[i].type, + maps[i].key_size, + maps[i].value_size, + maps[i].max_entries, + maps[i].map_flags); + } if (map_fd[i] < 0) { printf("failed to create a map: %d %s\n", errno, strerror(errno)); diff --git a/samples/bpf/test_map_in_map_kern.c b/samples/bpf/test_map_in_map_kern.c new file mode 100644 index 000000000000..42c44d091dd1 --- /dev/null +++ b/samples/bpf/test_map_in_map_kern.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#define KBUILD_MODNAME "foo" +#include +#include +#include +#include +#include "bpf_helpers.h" + +#define MAX_NR_PORTS 65536 + +/* map #0 */ +struct bpf_map_def SEC("maps") port_a = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(u32), + .value_size = sizeof(int), + .max_entries = MAX_NR_PORTS, +}; + +/* map #1 */ +struct bpf_map_def SEC("maps") port_h = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(u32), + .value_size = sizeof(int), + .max_entries = 1, +}; + +/* map #2 */ +struct bpf_map_def SEC("maps") reg_result_h = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(u32), + .value_size = sizeof(int), + .max_entries = 1, +}; + +/* map #3 */ +struct bpf_map_def SEC("maps") inline_result_h = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(u32), + .value_size = sizeof(int), + .max_entries = 1, +}; + +/* map #4 */ /* Test case #0 */ +struct bpf_map_def SEC("maps") a_of_port_a = { + .type = BPF_MAP_TYPE_ARRAY_OF_MAPS, + .key_size = sizeof(u32), + .inner_map_idx = 0, /* map_fd[0] is port_a */ + .max_entries = MAX_NR_PORTS, +}; + +/* map #5 */ /* Test case #1 */ +struct bpf_map_def SEC("maps") h_of_port_a = { + .type = BPF_MAP_TYPE_HASH_OF_MAPS, + .key_size = sizeof(u32), + .inner_map_idx = 0, /* map_fd[0] is port_a */ + .max_entries = 1, +}; + +/* map #6 */ /* Test case #2 */ +struct bpf_map_def SEC("maps") h_of_port_h = { + .type = BPF_MAP_TYPE_HASH_OF_MAPS, + .key_size = sizeof(u32), + .inner_map_idx = 1, /* map_fd[1] is port_h */ + .max_entries = 1, +}; + +static __always_inline int do_reg_lookup(void *inner_map, u32 port) +{ + int *result; + + result = bpf_map_lookup_elem(inner_map, &port); + return result ? *result : -ENOENT; +} + +static __always_inline int do_inline_array_lookup(void *inner_map, u32 port) +{ + int *result; + + if (inner_map != &port_a) + return -EINVAL; + + result = bpf_map_lookup_elem(&port_a, &port); + return result ? *result : -ENOENT; +} + +static __always_inline int do_inline_hash_lookup(void *inner_map, u32 port) +{ + int *result; + + if (inner_map != &port_h) + return -EINVAL; + + result = bpf_map_lookup_elem(&port_h, &port); + return result ? *result : -ENOENT; +} + +SEC("kprobe/sys_connect") +int trace_sys_connect(struct pt_regs *ctx) +{ + struct sockaddr_in6 *in6; + u16 test_case, port, dst6[8]; + int addrlen, ret, inline_ret, ret_key = 0; + u32 port_key; + void *outer_map, *inner_map; + bool inline_hash = false; + + in6 = (struct sockaddr_in6 *)PT_REGS_PARM2(ctx); + addrlen = (int)PT_REGS_PARM3(ctx); + + if (addrlen != sizeof(*in6)) + return 0; + + ret = bpf_probe_read(dst6, sizeof(dst6), &in6->sin6_addr); + if (ret) { + inline_ret = ret; + goto done; + } + + if (dst6[0] != 0xdead || dst6[1] != 0xbeef) + return 0; + + test_case = dst6[7]; + + ret = bpf_probe_read(&port, sizeof(port), &in6->sin6_port); + if (ret) { + inline_ret = ret; + goto done; + } + + port_key = port; + + ret = -ENOENT; + if (test_case == 0) { + outer_map = &a_of_port_a; + } else if (test_case == 1) { + outer_map = &h_of_port_a; + } else if (test_case == 2) { + outer_map = &h_of_port_h; + } else { + ret = __LINE__; + inline_ret = ret; + goto done; + } + + inner_map = bpf_map_lookup_elem(outer_map, &port_key); + if (!inner_map) { + ret = __LINE__; + inline_ret = ret; + goto done; + } + + ret = do_reg_lookup(inner_map, port_key); + + if (test_case == 0 || test_case == 1) + inline_ret = do_inline_array_lookup(inner_map, port_key); + else + inline_ret = do_inline_hash_lookup(inner_map, port_key); + +done: + bpf_map_update_elem(®_result_h, &ret_key, &ret, BPF_ANY); + bpf_map_update_elem(&inline_result_h, &ret_key, &inline_ret, BPF_ANY); + + return 0; +} + +char _license[] SEC("license") = "GPL"; +u32 _version SEC("version") = LINUX_VERSION_CODE; diff --git a/samples/bpf/test_map_in_map_user.c b/samples/bpf/test_map_in_map_user.c new file mode 100644 index 000000000000..f62fdc2bd428 --- /dev/null +++ b/samples/bpf/test_map_in_map_user.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "libbpf.h" +#include "bpf_load.h" + +#define PORT_A (map_fd[0]) +#define PORT_H (map_fd[1]) +#define REG_RESULT_H (map_fd[2]) +#define INLINE_RESULT_H (map_fd[3]) +#define A_OF_PORT_A (map_fd[4]) /* Test case #0 */ +#define H_OF_PORT_A (map_fd[5]) /* Test case #1 */ +#define H_OF_PORT_H (map_fd[6]) /* Test case #2 */ + +static const char * const test_names[] = { + "Array of Array", + "Hash of Array", + "Hash of Hash", +}; + +#define NR_TESTS (sizeof(test_names) / sizeof(*test_names)) + +static void populate_map(uint32_t port_key, int magic_result) +{ + int ret; + + ret = bpf_map_update_elem(PORT_A, &port_key, &magic_result, BPF_ANY); + assert(!ret); + + ret = bpf_map_update_elem(PORT_H, &port_key, &magic_result, + BPF_NOEXIST); + assert(!ret); + + ret = bpf_map_update_elem(A_OF_PORT_A, &port_key, &PORT_A, BPF_ANY); + assert(!ret); + + ret = bpf_map_update_elem(H_OF_PORT_A, &port_key, &PORT_A, BPF_NOEXIST); + assert(!ret); + + ret = bpf_map_update_elem(H_OF_PORT_H, &port_key, &PORT_H, BPF_NOEXIST); + assert(!ret); +} + +static void test_map_in_map(void) +{ + struct sockaddr_in6 in6 = { .sin6_family = AF_INET6 }; + uint32_t result_key = 0, port_key; + int result, inline_result; + int magic_result = 0xfaceb00c; + int ret; + int i; + + port_key = rand() & 0x00FF; + populate_map(port_key, magic_result); + + in6.sin6_addr.s6_addr16[0] = 0xdead; + in6.sin6_addr.s6_addr16[1] = 0xbeef; + in6.sin6_port = port_key; + + for (i = 0; i < NR_TESTS; i++) { + printf("%s: ", test_names[i]); + + in6.sin6_addr.s6_addr16[7] = i; + ret = connect(-1, (struct sockaddr *)&in6, sizeof(in6)); + assert(ret == -1 && errno == EBADF); + + ret = bpf_map_lookup_elem(REG_RESULT_H, &result_key, &result); + assert(!ret); + + ret = bpf_map_lookup_elem(INLINE_RESULT_H, &result_key, + &inline_result); + assert(!ret); + + if (result != magic_result || inline_result != magic_result) { + printf("Error. result:%d inline_result:%d\n", + result, inline_result); + exit(1); + } + + bpf_map_delete_elem(REG_RESULT_H, &result_key); + bpf_map_delete_elem(INLINE_RESULT_H, &result_key); + + printf("Pass\n"); + } +} + +int main(int argc, char **argv) +{ + struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; + char filename[256]; + + assert(!setrlimit(RLIMIT_MEMLOCK, &r)); + + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); + + if (load_bpf_file(filename)) { + printf("%s", bpf_log_buf); + return 1; + } + + test_map_in_map(); + + return 0; +} diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 0539a0ceef38..ce6f029ac368 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -96,6 +96,8 @@ enum bpf_map_type { BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, BPF_MAP_TYPE_LPM_TRIE, + BPF_MAP_TYPE_ARRAY_OF_MAPS, + BPF_MAP_TYPE_HASH_OF_MAPS, }; enum bpf_prog_type { @@ -152,6 +154,7 @@ union bpf_attr { __u32 value_size; /* size of value in bytes */ __u32 max_entries; /* max number of entries in a map */ __u32 map_flags; /* prealloc or not */ + __u32 inner_map_fd; /* fd pointing to the inner map */ }; struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 207c2eeddab0..9b58d20e8c93 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -69,6 +69,23 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size, return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); } +int bpf_create_map_in_map(enum bpf_map_type map_type, int key_size, + int inner_map_fd, int max_entries, __u32 map_flags) +{ + union bpf_attr attr; + + memset(&attr, '\0', sizeof(attr)); + + attr.map_type = map_type; + attr.key_size = key_size; + attr.value_size = 4; + attr.inner_map_fd = inner_map_fd; + attr.max_entries = max_entries; + attr.map_flags = map_flags; + + return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); +} + int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns, size_t insns_cnt, const char *license, __u32 kern_version, char *log_buf, size_t log_buf_sz) diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 09c3dcac0496..93f021932623 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -26,6 +26,8 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, int max_entries, __u32 map_flags); +int bpf_create_map_in_map(enum bpf_map_type map_type, int key_size, + int inner_map_fd, int max_entries, __u32 map_flags); /* Recommend log buffer size */ #define BPF_LOG_BUF_SIZE 65536 diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index d1555e4240c0..f4f43c98cf7f 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -38,6 +38,7 @@ #define MAX_INSNS 512 #define MAX_FIXUPS 8 +#define MAX_NR_MAPS 4 struct bpf_test { const char *descr; @@ -45,6 +46,7 @@ struct bpf_test { int fixup_map1[MAX_FIXUPS]; int fixup_map2[MAX_FIXUPS]; int fixup_prog[MAX_FIXUPS]; + int fixup_map_in_map[MAX_FIXUPS]; const char *errstr; const char *errstr_unpriv; enum { @@ -4452,7 +4454,76 @@ static struct bpf_test tests[] = { .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", .result = REJECT, .result_unpriv = REJECT, - } + }, + { + "map in map access", + .insns = { + BPF_ST_MEM(0, BPF_REG_10, -4, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), + BPF_ST_MEM(0, BPF_REG_10, -4, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_REG(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map_in_map = { 3 }, + .result = ACCEPT, + }, + { + "invalid inner map pointer", + .insns = { + BPF_ST_MEM(0, BPF_REG_10, -4, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), + BPF_ST_MEM(0, BPF_REG_10, -4, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_REG(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map_in_map = { 3 }, + .errstr = "R1 type=inv expected=map_ptr", + .errstr_unpriv = "R1 pointer arithmetic prohibited", + .result = REJECT, + }, + { + "forgot null checking on the inner map pointer", + .insns = { + BPF_ST_MEM(0, BPF_REG_10, -4, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_ST_MEM(0, BPF_REG_10, -4, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_MOV64_REG(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map_in_map = { 3 }, + .errstr = "R1 type=map_value_or_null expected=map_ptr", + .result = REJECT, + }, }; static int probe_filter_length(const struct bpf_insn *fp) @@ -4489,42 +4560,73 @@ static int create_prog_array(void) return fd; } +static int create_map_in_map(void) +{ + int inner_map_fd, outer_map_fd; + + inner_map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), + sizeof(int), 1, 0); + if (inner_map_fd < 0) { + printf("Failed to create array '%s'!\n", strerror(errno)); + return inner_map_fd; + } + + outer_map_fd = bpf_create_map_in_map(BPF_MAP_TYPE_ARRAY_OF_MAPS, + sizeof(int), inner_map_fd, 1, 0); + if (outer_map_fd < 0) + printf("Failed to create array of maps '%s'!\n", + strerror(errno)); + + close(inner_map_fd); + + return outer_map_fd; +} + static char bpf_vlog[32768]; static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog, - int *fd_f1, int *fd_f2, int *fd_f3) + int *map_fds) { int *fixup_map1 = test->fixup_map1; int *fixup_map2 = test->fixup_map2; int *fixup_prog = test->fixup_prog; + int *fixup_map_in_map = test->fixup_map_in_map; /* Allocating HTs with 1 elem is fine here, since we only test * for verifier and not do a runtime lookup, so the only thing * that really matters is value size in this case. */ if (*fixup_map1) { - *fd_f1 = create_map(sizeof(long long), 1); + map_fds[0] = create_map(sizeof(long long), 1); do { - prog[*fixup_map1].imm = *fd_f1; + prog[*fixup_map1].imm = map_fds[0]; fixup_map1++; } while (*fixup_map1); } if (*fixup_map2) { - *fd_f2 = create_map(sizeof(struct test_val), 1); + map_fds[1] = create_map(sizeof(struct test_val), 1); do { - prog[*fixup_map2].imm = *fd_f2; + prog[*fixup_map2].imm = map_fds[1]; fixup_map2++; } while (*fixup_map2); } if (*fixup_prog) { - *fd_f3 = create_prog_array(); + map_fds[2] = create_prog_array(); do { - prog[*fixup_prog].imm = *fd_f3; + prog[*fixup_prog].imm = map_fds[2]; fixup_prog++; } while (*fixup_prog); } + + if (*fixup_map_in_map) { + map_fds[3] = create_map_in_map(); + do { + prog[*fixup_map_in_map].imm = map_fds[3]; + fixup_map_in_map++; + } while (*fixup_map_in_map); + } } static void do_test_single(struct bpf_test *test, bool unpriv, @@ -4533,11 +4635,15 @@ static void do_test_single(struct bpf_test *test, bool unpriv, struct bpf_insn *prog = test->insns; int prog_len = probe_filter_length(prog); int prog_type = test->prog_type; - int fd_f1 = -1, fd_f2 = -1, fd_f3 = -1; + int map_fds[MAX_NR_MAPS]; int fd_prog, expected_ret; const char *expected_err; + int i; + + for (i = 0; i < MAX_NR_MAPS; i++) + map_fds[i] = -1; - do_test_fixup(test, prog, &fd_f1, &fd_f2, &fd_f3); + do_test_fixup(test, prog, map_fds); fd_prog = bpf_load_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER, prog, prog_len, "GPL", 0, bpf_vlog, @@ -4568,9 +4674,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv, printf("OK\n"); close_fds: close(fd_prog); - close(fd_f1); - close(fd_f2); - close(fd_f3); + for (i = 0; i < MAX_NR_MAPS; i++) + close(map_fds[i]); sched_yield(); return; fail_log: -- cgit From ea8ab16ab225a62bb4c4041f7f50556d1adfb0dc Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Tue, 21 Mar 2017 18:18:02 -0700 Subject: drivers: net: xgene-v2: Add MDIO support Added phy management support by using phy abstraction layer APIs. Signed-off-by: Iyappan Subramanian Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene-v2/Makefile | 2 +- drivers/net/ethernet/apm/xgene-v2/mac.c | 2 +- drivers/net/ethernet/apm/xgene-v2/mac.h | 1 + drivers/net/ethernet/apm/xgene-v2/main.c | 11 +- drivers/net/ethernet/apm/xgene-v2/main.h | 4 + drivers/net/ethernet/apm/xgene-v2/mdio.c | 167 +++++++++++++++++++++++++++++ 6 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 drivers/net/ethernet/apm/xgene-v2/mdio.c diff --git a/drivers/net/ethernet/apm/xgene-v2/Makefile b/drivers/net/ethernet/apm/xgene-v2/Makefile index 735309c0b8b1..0fa59750bcc8 100644 --- a/drivers/net/ethernet/apm/xgene-v2/Makefile +++ b/drivers/net/ethernet/apm/xgene-v2/Makefile @@ -2,5 +2,5 @@ # Makefile for APM X-Gene Ethernet v2 driver # -xgene-enet-v2-objs := main.o mac.o enet.o ring.o +xgene-enet-v2-objs := main.o mac.o enet.o ring.o mdio.o obj-$(CONFIG_NET_XGENE_V2) += xgene-enet-v2.o diff --git a/drivers/net/ethernet/apm/xgene-v2/mac.c b/drivers/net/ethernet/apm/xgene-v2/mac.c index c3189de3df55..ee431e397e57 100644 --- a/drivers/net/ethernet/apm/xgene-v2/mac.c +++ b/drivers/net/ethernet/apm/xgene-v2/mac.c @@ -27,7 +27,7 @@ void xge_mac_reset(struct xge_pdata *pdata) xge_wr_csr(pdata, MAC_CONFIG_1, 0); } -static void xge_mac_set_speed(struct xge_pdata *pdata) +void xge_mac_set_speed(struct xge_pdata *pdata) { u32 icm0, icm2, ecm0, mc2; u32 intf_ctrl, rgmii; diff --git a/drivers/net/ethernet/apm/xgene-v2/mac.h b/drivers/net/ethernet/apm/xgene-v2/mac.h index 0fce6ae15ce0..74397c961fa4 100644 --- a/drivers/net/ethernet/apm/xgene-v2/mac.h +++ b/drivers/net/ethernet/apm/xgene-v2/mac.h @@ -101,6 +101,7 @@ static inline u32 xgene_get_reg_bits(u32 var, int pos, int len) struct xge_pdata; void xge_mac_reset(struct xge_pdata *pdata); +void xge_mac_set_speed(struct xge_pdata *pdata); void xge_mac_enable(struct xge_pdata *pdata); void xge_mac_disable(struct xge_pdata *pdata); void xge_mac_init(struct xge_pdata *pdata); diff --git a/drivers/net/ethernet/apm/xgene-v2/main.c b/drivers/net/ethernet/apm/xgene-v2/main.c index ae76977d10b4..82ac5b4d3ae4 100644 --- a/drivers/net/ethernet/apm/xgene-v2/main.c +++ b/drivers/net/ethernet/apm/xgene-v2/main.c @@ -500,9 +500,10 @@ static int xge_open(struct net_device *ndev) xge_intr_enable(pdata); xge_wr_csr(pdata, DMARXCTRL, 1); + + phy_start(ndev->phydev); xge_mac_enable(pdata); netif_start_queue(ndev); - netif_carrier_on(ndev); return 0; } @@ -511,9 +512,9 @@ static int xge_close(struct net_device *ndev) { struct xge_pdata *pdata = netdev_priv(ndev); - netif_carrier_off(ndev); netif_stop_queue(ndev); xge_mac_disable(pdata); + phy_stop(ndev->phydev); xge_intr_disable(pdata); xge_free_irq(ndev); @@ -683,9 +684,12 @@ static int xge_probe(struct platform_device *pdev) if (ret) goto err; + ret = xge_mdio_config(ndev); + if (ret) + goto err; + netif_napi_add(ndev, &pdata->napi, xge_napi, NAPI_POLL_WEIGHT); - netif_carrier_off(ndev); ret = register_netdev(ndev); if (ret) { netdev_err(ndev, "Failed to register netdev\n"); @@ -713,6 +717,7 @@ static int xge_remove(struct platform_device *pdev) dev_close(ndev); rtnl_unlock(); + xge_mdio_remove(ndev); unregister_netdev(ndev); free_netdev(ndev); diff --git a/drivers/net/ethernet/apm/xgene-v2/main.h b/drivers/net/ethernet/apm/xgene-v2/main.h index ada7b0e82586..777f2546ceba 100644 --- a/drivers/net/ethernet/apm/xgene-v2/main.h +++ b/drivers/net/ethernet/apm/xgene-v2/main.h @@ -65,6 +65,7 @@ struct xge_pdata { struct xge_desc_ring *rx_ring; struct platform_device *pdev; char irq_name[IRQ_ID_SIZE]; + struct mii_bus *mdio_bus; struct net_device *ndev; struct napi_struct napi; struct xge_stats stats; @@ -72,4 +73,7 @@ struct xge_pdata { u8 nbufs; }; +int xge_mdio_config(struct net_device *ndev); +void xge_mdio_remove(struct net_device *ndev); + #endif /* __XGENE_ENET_V2_MAIN_H__ */ diff --git a/drivers/net/ethernet/apm/xgene-v2/mdio.c b/drivers/net/ethernet/apm/xgene-v2/mdio.c new file mode 100644 index 000000000000..a583c6a9a5ea --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/mdio.c @@ -0,0 +1,167 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian + * Keyur Chudgar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +static int xge_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data) +{ + struct xge_pdata *pdata = bus->priv; + u32 done, val = 0; + u8 wait = 10; + + SET_REG_BITS(&val, PHY_ADDR, phy_id); + SET_REG_BITS(&val, REG_ADDR, reg); + xge_wr_csr(pdata, MII_MGMT_ADDRESS, val); + + xge_wr_csr(pdata, MII_MGMT_CONTROL, data); + do { + usleep_range(5, 10); + done = xge_rd_csr(pdata, MII_MGMT_INDICATORS); + } while ((done & MII_MGMT_BUSY) && wait--); + + if (done & MII_MGMT_BUSY) { + dev_err(&bus->dev, "MII_MGMT write failed\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int xge_mdio_read(struct mii_bus *bus, int phy_id, int reg) +{ + struct xge_pdata *pdata = bus->priv; + u32 data, done, val = 0; + u8 wait = 10; + + SET_REG_BITS(&val, PHY_ADDR, phy_id); + SET_REG_BITS(&val, REG_ADDR, reg); + xge_wr_csr(pdata, MII_MGMT_ADDRESS, val); + + xge_wr_csr(pdata, MII_MGMT_COMMAND, MII_READ_CYCLE); + do { + usleep_range(5, 10); + done = xge_rd_csr(pdata, MII_MGMT_INDICATORS); + } while ((done & MII_MGMT_BUSY) && wait--); + + if (done & MII_MGMT_BUSY) { + dev_err(&bus->dev, "MII_MGMT read failed\n"); + return -ETIMEDOUT; + } + + data = xge_rd_csr(pdata, MII_MGMT_STATUS); + xge_wr_csr(pdata, MII_MGMT_COMMAND, 0); + + return data; +} + +static void xge_adjust_link(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct phy_device *phydev = ndev->phydev; + + if (phydev->link) { + if (pdata->phy_speed != phydev->speed) { + pdata->phy_speed = phydev->speed; + xge_mac_set_speed(pdata); + xge_mac_enable(pdata); + phy_print_status(phydev); + } + } else { + if (pdata->phy_speed != SPEED_UNKNOWN) { + pdata->phy_speed = SPEED_UNKNOWN; + xge_mac_disable(pdata); + phy_print_status(phydev); + } + } +} + +void xge_mdio_remove(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct mii_bus *mdio_bus = pdata->mdio_bus; + + if (ndev->phydev) + phy_disconnect(ndev->phydev); + + if (mdio_bus->state == MDIOBUS_REGISTERED) + mdiobus_unregister(mdio_bus); + + mdiobus_free(mdio_bus); +} + +int xge_mdio_config(struct net_device *ndev) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + struct mii_bus *mdio_bus; + struct phy_device *phydev; + int ret; + + mdio_bus = mdiobus_alloc(); + if (!mdio_bus) + return -ENOMEM; + + mdio_bus->name = "APM X-Gene Ethernet (v2) MDIO Bus"; + mdio_bus->read = xge_mdio_read; + mdio_bus->write = xge_mdio_write; + mdio_bus->priv = pdata; + mdio_bus->parent = dev; + snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev)); + pdata->mdio_bus = mdio_bus; + + mdio_bus->phy_mask = 0x1; + ret = mdiobus_register(mdio_bus); + if (ret) + goto err; + + phydev = phy_find_first(mdio_bus); + if (!phydev) { + dev_err(dev, "no PHY found\n"); + goto err; + } + phydev = phy_connect(ndev, phydev_name(phydev), + &xge_adjust_link, + pdata->resources.phy_mode); + + if (IS_ERR(phydev)) { + netdev_err(ndev, "Could not attach to PHY\n"); + ret = PTR_ERR(phydev); + goto err; + } + + phydev->supported &= ~(SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Half | + SUPPORTED_AUI | + SUPPORTED_MII | + SUPPORTED_FIBRE | + SUPPORTED_BNC); + phydev->advertising = phydev->supported; + pdata->phy_speed = SPEED_UNKNOWN; + + return 0; +err: + xge_mdio_remove(ndev); + + return ret; +} -- cgit From 617d795c7cb2d1b636db33d428f830f3ed18a36f Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Tue, 21 Mar 2017 18:18:03 -0700 Subject: drivers: net: xgene-v2: Add ethtool support Added basic ethtool support. Signed-off-by: Iyappan Subramanian Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene-v2/Makefile | 2 +- drivers/net/ethernet/apm/xgene-v2/ethtool.c | 121 ++++++++++++++++++++++++++++ drivers/net/ethernet/apm/xgene-v2/main.c | 1 + drivers/net/ethernet/apm/xgene-v2/main.h | 1 + 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/apm/xgene-v2/ethtool.c diff --git a/drivers/net/ethernet/apm/xgene-v2/Makefile b/drivers/net/ethernet/apm/xgene-v2/Makefile index 0fa59750bcc8..f16a2b3dde8b 100644 --- a/drivers/net/ethernet/apm/xgene-v2/Makefile +++ b/drivers/net/ethernet/apm/xgene-v2/Makefile @@ -2,5 +2,5 @@ # Makefile for APM X-Gene Ethernet v2 driver # -xgene-enet-v2-objs := main.o mac.o enet.o ring.o mdio.o +xgene-enet-v2-objs := main.o mac.o enet.o ring.o mdio.o ethtool.o obj-$(CONFIG_NET_XGENE_V2) += xgene-enet-v2.o diff --git a/drivers/net/ethernet/apm/xgene-v2/ethtool.c b/drivers/net/ethernet/apm/xgene-v2/ethtool.c new file mode 100644 index 000000000000..0c426f55ffdb --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/ethtool.c @@ -0,0 +1,121 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian + * Keyur Chudgar + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "main.h" + +struct xge_gstrings_stats { + char name[ETH_GSTRING_LEN]; + int offset; +}; + +#define XGE_STAT(m) { #m, offsetof(struct xge_pdata, stats.m) } + +static const struct xge_gstrings_stats gstrings_stats[] = { + XGE_STAT(rx_packets), + XGE_STAT(tx_packets), + XGE_STAT(rx_bytes), + XGE_STAT(tx_bytes), + XGE_STAT(rx_errors) +}; + +#define XGE_STATS_LEN ARRAY_SIZE(gstrings_stats) + +static void xge_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + struct xge_pdata *pdata = netdev_priv(ndev); + struct platform_device *pdev = pdata->pdev; + + strcpy(info->driver, "xgene-enet-v2"); + strcpy(info->version, XGENE_ENET_V2_VERSION); + snprintf(info->fw_version, ETHTOOL_FWVERS_LEN, "N/A"); + sprintf(info->bus_info, "%s", pdev->name); +} + +static void xge_get_strings(struct net_device *ndev, u32 stringset, u8 *data) +{ + u8 *p = data; + int i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < XGE_STATS_LEN; i++) { + memcpy(p, gstrings_stats[i].name, ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } +} + +static int xge_get_sset_count(struct net_device *ndev, int sset) +{ + if (sset != ETH_SS_STATS) + return -EINVAL; + + return XGE_STATS_LEN; +} + +static void xge_get_ethtool_stats(struct net_device *ndev, + struct ethtool_stats *dummy, + u64 *data) +{ + void *pdata = netdev_priv(ndev); + int i; + + for (i = 0; i < XGE_STATS_LEN; i++) + *data++ = *(u64 *)(pdata + gstrings_stats[i].offset); +} + +static int xge_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *cmd) +{ + struct phy_device *phydev = ndev->phydev; + + if (!phydev) + return -ENODEV; + + return phy_ethtool_ksettings_get(phydev, cmd); +} + +static int xge_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *cmd) +{ + struct phy_device *phydev = ndev->phydev; + + if (!phydev) + return -ENODEV; + + return phy_ethtool_ksettings_set(phydev, cmd); +} + +static const struct ethtool_ops xge_ethtool_ops = { + .get_drvinfo = xge_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_strings = xge_get_strings, + .get_sset_count = xge_get_sset_count, + .get_ethtool_stats = xge_get_ethtool_stats, + .get_link_ksettings = xge_get_link_ksettings, + .set_link_ksettings = xge_set_link_ksettings, +}; + +void xge_set_ethtool_ops(struct net_device *ndev) +{ + ndev->ethtool_ops = &xge_ethtool_ops; +} diff --git a/drivers/net/ethernet/apm/xgene-v2/main.c b/drivers/net/ethernet/apm/xgene-v2/main.c index 82ac5b4d3ae4..e764e58453dd 100644 --- a/drivers/net/ethernet/apm/xgene-v2/main.c +++ b/drivers/net/ethernet/apm/xgene-v2/main.c @@ -673,6 +673,7 @@ static int xge_probe(struct platform_device *pdev) goto err; ndev->hw_features = ndev->features; + xge_set_ethtool_ops(ndev); ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64)); if (ret) { diff --git a/drivers/net/ethernet/apm/xgene-v2/main.h b/drivers/net/ethernet/apm/xgene-v2/main.h index 777f2546ceba..db1178e82a0a 100644 --- a/drivers/net/ethernet/apm/xgene-v2/main.h +++ b/drivers/net/ethernet/apm/xgene-v2/main.h @@ -75,5 +75,6 @@ struct xge_pdata { int xge_mdio_config(struct net_device *ndev); void xge_mdio_remove(struct net_device *ndev); +void xge_set_ethtool_ops(struct net_device *ndev); #endif /* __XGENE_ENET_V2_MAIN_H__ */ -- cgit From b2180a8f28dbd66d6593a70ead791ad9b347bc74 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Tue, 21 Mar 2017 18:18:04 -0700 Subject: drivers: net: xgene-v2: Fix port reset Fixed port reset sequence by adding ECC init. Signed-off-by: Iyappan Subramanian Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene-v2/enet.c | 24 ++++++++++++++++++------ drivers/net/ethernet/apm/xgene-v2/enet.h | 2 ++ drivers/net/ethernet/apm/xgene-v2/mac.h | 1 - 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/apm/xgene-v2/enet.c b/drivers/net/ethernet/apm/xgene-v2/enet.c index b49edeeb6275..5998da014923 100644 --- a/drivers/net/ethernet/apm/xgene-v2/enet.c +++ b/drivers/net/ethernet/apm/xgene-v2/enet.c @@ -38,10 +38,24 @@ u32 xge_rd_csr(struct xge_pdata *pdata, u32 offset) int xge_port_reset(struct net_device *ndev) { struct xge_pdata *pdata = netdev_priv(ndev); + struct device *dev = &pdata->pdev->dev; + u32 data, wait = 10; - xge_wr_csr(pdata, ENET_SRST, 0x3); - xge_wr_csr(pdata, ENET_SRST, 0x2); - xge_wr_csr(pdata, ENET_SRST, 0x0); + xge_wr_csr(pdata, ENET_CLKEN, 0x3); + xge_wr_csr(pdata, ENET_SRST, 0xf); + xge_wr_csr(pdata, ENET_SRST, 0); + xge_wr_csr(pdata, CFG_MEM_RAM_SHUTDOWN, 1); + xge_wr_csr(pdata, CFG_MEM_RAM_SHUTDOWN, 0); + + do { + usleep_range(100, 110); + data = xge_rd_csr(pdata, BLOCK_MEM_RDY); + } while (data != MEM_RDY && wait--); + + if (data != MEM_RDY) { + dev_err(dev, "ECC init failed: %x\n", data); + return -ETIMEDOUT; + } xge_wr_csr(pdata, ENET_SHIM, DEVM_ARAUX_COH | DEVM_AWAUX_COH); @@ -59,13 +73,11 @@ static void xge_traffic_resume(struct net_device *ndev) xge_wr_csr(pdata, RX_DV_GATE_REG, 1); } -int xge_port_init(struct net_device *ndev) +void xge_port_init(struct net_device *ndev) { struct xge_pdata *pdata = netdev_priv(ndev); pdata->phy_speed = SPEED_1000; xge_mac_init(pdata); xge_traffic_resume(ndev); - - return 0; } diff --git a/drivers/net/ethernet/apm/xgene-v2/enet.h b/drivers/net/ethernet/apm/xgene-v2/enet.h index 40371cfcfce4..3fd36dc66a23 100644 --- a/drivers/net/ethernet/apm/xgene-v2/enet.h +++ b/drivers/net/ethernet/apm/xgene-v2/enet.h @@ -28,6 +28,7 @@ #define CFG_MEM_RAM_SHUTDOWN 0xd070 #define BLOCK_MEM_RDY 0xd074 +#define MEM_RDY 0xffffffff #define DEVM_ARAUX_COH BIT(19) #define DEVM_AWAUX_COH BIT(3) @@ -39,5 +40,6 @@ void xge_wr_csr(struct xge_pdata *pdata, u32 offset, u32 val); u32 xge_rd_csr(struct xge_pdata *pdata, u32 offset); int xge_port_reset(struct net_device *ndev); +void xge_port_init(struct net_device *ndev); #endif /* __XGENE_ENET_V2_ENET__H__ */ diff --git a/drivers/net/ethernet/apm/xgene-v2/mac.h b/drivers/net/ethernet/apm/xgene-v2/mac.h index 74397c961fa4..18a9c9d8a5e5 100644 --- a/drivers/net/ethernet/apm/xgene-v2/mac.h +++ b/drivers/net/ethernet/apm/xgene-v2/mac.h @@ -105,7 +105,6 @@ void xge_mac_set_speed(struct xge_pdata *pdata); void xge_mac_enable(struct xge_pdata *pdata); void xge_mac_disable(struct xge_pdata *pdata); void xge_mac_init(struct xge_pdata *pdata); -int xge_port_init(struct net_device *ndev); void xge_mac_set_station_addr(struct xge_pdata *pdata); #endif /* __XGENE_ENET_V2_MAC_H__ */ -- cgit From 1ffa8a7aa516473b3575ee8c30f4e0b1bb29a7d8 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Tue, 21 Mar 2017 18:18:05 -0700 Subject: drivers: net: xgene-v2: misc fixes Fixed review comments from the previous patch-set. - changed return value check of platform_get_irq() to < 0 - replaced devm_request(free)_irq() calls by request(free)_irq() since they are called from open() and close() - changed sizeof(struct mystruct) to sizeof(*mystruct) - reduced indentation on tx_timeout() Signed-off-by: Iyappan Subramanian Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene-v2/main.c | 55 +++++++++++++++----------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/apm/xgene-v2/main.c b/drivers/net/ethernet/apm/xgene-v2/main.c index e764e58453dd..0f2ad50f3bd7 100644 --- a/drivers/net/ethernet/apm/xgene-v2/main.c +++ b/drivers/net/ethernet/apm/xgene-v2/main.c @@ -66,9 +66,8 @@ static int xge_get_resources(struct xge_pdata *pdata) } ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - dev_err(dev, "Unable to get ENET IRQ\n"); - ret = ret ? : -ENXIO; + if (ret < 0) { + dev_err(dev, "Unable to get irq\n"); return ret; } pdata->resources.irq = ret; @@ -156,13 +155,12 @@ static irqreturn_t xge_irq(const int irq, void *data) static int xge_request_irq(struct net_device *ndev) { struct xge_pdata *pdata = netdev_priv(ndev); - struct device *dev = &pdata->pdev->dev; int ret; snprintf(pdata->irq_name, IRQ_ID_SIZE, "%s", ndev->name); - ret = devm_request_irq(dev, pdata->resources.irq, xge_irq, - 0, pdata->irq_name, pdata); + ret = request_irq(pdata->resources.irq, xge_irq, 0, pdata->irq_name, + pdata); if (ret) netdev_err(ndev, "Failed to request irq %s\n", pdata->irq_name); @@ -172,9 +170,8 @@ static int xge_request_irq(struct net_device *ndev) static void xge_free_irq(struct net_device *ndev) { struct xge_pdata *pdata = netdev_priv(ndev); - struct device *dev = &pdata->pdev->dev; - devm_free_irq(dev, pdata->resources.irq, pdata); + free_irq(pdata->resources.irq, pdata); } static bool is_tx_slot_available(struct xge_raw_desc *raw_desc) @@ -424,7 +421,7 @@ static struct xge_desc_ring *xge_create_desc_ring(struct net_device *ndev) struct xge_desc_ring *ring; u16 size; - ring = kzalloc(sizeof(struct xge_desc_ring), GFP_KERNEL); + ring = kzalloc(sizeof(*ring), GFP_KERNEL); if (!ring) return NULL; @@ -436,7 +433,7 @@ static struct xge_desc_ring *xge_create_desc_ring(struct net_device *ndev) if (!ring->desc_addr) goto err; - ring->pkt_info = kcalloc(XGENE_ENET_NUM_DESC, sizeof(struct pkt_info), + ring->pkt_info = kcalloc(XGENE_ENET_NUM_DESC, sizeof(*ring->pkt_info), GFP_KERNEL); if (!ring->pkt_info) goto err; @@ -598,28 +595,28 @@ static void xge_timeout(struct net_device *ndev) rtnl_lock(); - if (netif_running(ndev)) { - netif_carrier_off(ndev); - netif_stop_queue(ndev); - xge_intr_disable(pdata); - napi_disable(&pdata->napi); + if (!netif_running(ndev)) + goto out; - xge_wr_csr(pdata, DMATXCTRL, 0); - xge_txc_poll(ndev); - xge_free_pending_skb(ndev); - xge_wr_csr(pdata, DMATXSTATUS, ~0U); + netif_stop_queue(ndev); + xge_intr_disable(pdata); + napi_disable(&pdata->napi); - xge_setup_desc(pdata->tx_ring); - xge_update_tx_desc_addr(pdata); - xge_mac_init(pdata); + xge_wr_csr(pdata, DMATXCTRL, 0); + xge_txc_poll(ndev); + xge_free_pending_skb(ndev); + xge_wr_csr(pdata, DMATXSTATUS, ~0U); - napi_enable(&pdata->napi); - xge_intr_enable(pdata); - xge_mac_enable(pdata); - netif_start_queue(ndev); - netif_carrier_on(ndev); - } + xge_setup_desc(pdata->tx_ring); + xge_update_tx_desc_addr(pdata); + xge_mac_init(pdata); + + napi_enable(&pdata->napi); + xge_intr_enable(pdata); + xge_mac_enable(pdata); + netif_start_queue(ndev); +out: rtnl_unlock(); } @@ -653,7 +650,7 @@ static int xge_probe(struct platform_device *pdev) struct xge_pdata *pdata; int ret; - ndev = alloc_etherdev(sizeof(struct xge_pdata)); + ndev = alloc_etherdev(sizeof(*pdata)); if (!ndev) return -ENOMEM; -- cgit From ebb6b4b1ff50635a17f1322e25372ffbf9407c48 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Tue, 21 Mar 2017 23:24:24 +0100 Subject: net: virtio_net: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 48 +++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index ea9890d61967..b0d241d110ec 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1636,47 +1636,57 @@ static void virtnet_get_channels(struct net_device *dev, } /* Check if the user is trying to change anything besides speed/duplex */ -static bool virtnet_validate_ethtool_cmd(const struct ethtool_cmd *cmd) +static bool +virtnet_validate_ethtool_cmd(const struct ethtool_link_ksettings *cmd) { - struct ethtool_cmd diff1 = *cmd; - struct ethtool_cmd diff2 = {}; + struct ethtool_link_ksettings diff1 = *cmd; + struct ethtool_link_ksettings diff2 = {}; /* cmd is always set so we need to clear it, validate the port type * and also without autonegotiation we can ignore advertising */ - ethtool_cmd_speed_set(&diff1, 0); - diff2.port = PORT_OTHER; - diff1.advertising = 0; - diff1.duplex = 0; - diff1.cmd = 0; + diff1.base.speed = 0; + diff2.base.port = PORT_OTHER; + ethtool_link_ksettings_zero_link_mode(&diff1, advertising); + diff1.base.duplex = 0; + diff1.base.cmd = 0; + diff1.base.link_mode_masks_nwords = 0; - return !memcmp(&diff1, &diff2, sizeof(diff1)); + return !memcmp(&diff1.base, &diff2.base, sizeof(diff1.base)) && + bitmap_empty(diff1.link_modes.supported, + __ETHTOOL_LINK_MODE_MASK_NBITS) && + bitmap_empty(diff1.link_modes.advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS) && + bitmap_empty(diff1.link_modes.lp_advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS); } -static int virtnet_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int virtnet_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) { struct virtnet_info *vi = netdev_priv(dev); u32 speed; - speed = ethtool_cmd_speed(cmd); + speed = cmd->base.speed; /* don't allow custom speed and duplex */ if (!ethtool_validate_speed(speed) || - !ethtool_validate_duplex(cmd->duplex) || + !ethtool_validate_duplex(cmd->base.duplex) || !virtnet_validate_ethtool_cmd(cmd)) return -EINVAL; vi->speed = speed; - vi->duplex = cmd->duplex; + vi->duplex = cmd->base.duplex; return 0; } -static int virtnet_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int virtnet_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct virtnet_info *vi = netdev_priv(dev); - ethtool_cmd_speed_set(cmd, vi->speed); - cmd->duplex = vi->duplex; - cmd->port = PORT_OTHER; + cmd->base.speed = vi->speed; + cmd->base.duplex = vi->duplex; + cmd->base.port = PORT_OTHER; return 0; } @@ -1696,8 +1706,8 @@ static const struct ethtool_ops virtnet_ethtool_ops = { .set_channels = virtnet_set_channels, .get_channels = virtnet_get_channels, .get_ts_info = ethtool_op_get_ts_info, - .get_settings = virtnet_get_settings, - .set_settings = virtnet_set_settings, + .get_link_ksettings = virtnet_get_link_ksettings, + .set_link_ksettings = virtnet_set_link_ksettings, }; static void virtnet_freeze_down(struct virtio_device *vdev) -- cgit From 3426bd7277b987845e07e24ab6b4bc7afe8c1622 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Wed, 22 Mar 2017 08:27:57 +0100 Subject: net: vmxnet3: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/vmxnet3/vmxnet3_ethtool.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index f88ffafebfbf..2ff27314e047 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -471,22 +471,25 @@ vmxnet3_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) static int -vmxnet3_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +vmxnet3_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *ecmd) { struct vmxnet3_adapter *adapter = netdev_priv(netdev); - ecmd->supported = SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full | - SUPPORTED_TP; - ecmd->advertising = ADVERTISED_TP; - ecmd->port = PORT_TP; - ecmd->transceiver = XCVR_INTERNAL; + ethtool_link_ksettings_zero_link_mode(ecmd, supported); + ethtool_link_ksettings_add_link_mode(ecmd, supported, 10000baseT_Full); + ethtool_link_ksettings_add_link_mode(ecmd, supported, 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(ecmd, supported, TP); + ethtool_link_ksettings_zero_link_mode(ecmd, advertising); + ethtool_link_ksettings_add_link_mode(ecmd, advertising, TP); + ecmd->base.port = PORT_TP; if (adapter->link_speed) { - ethtool_cmd_speed_set(ecmd, adapter->link_speed); - ecmd->duplex = DUPLEX_FULL; + ecmd->base.speed = adapter->link_speed; + ecmd->base.duplex = DUPLEX_FULL; } else { - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; + ecmd->base.speed = SPEED_UNKNOWN; + ecmd->base.duplex = DUPLEX_UNKNOWN; } return 0; } @@ -880,7 +883,6 @@ done: } static const struct ethtool_ops vmxnet3_ethtool_ops = { - .get_settings = vmxnet3_get_settings, .get_drvinfo = vmxnet3_get_drvinfo, .get_regs_len = vmxnet3_get_regs_len, .get_regs = vmxnet3_get_regs, @@ -900,6 +902,7 @@ static const struct ethtool_ops vmxnet3_ethtool_ops = { .get_rxfh = vmxnet3_get_rss, .set_rxfh = vmxnet3_set_rss, #endif + .get_link_ksettings = vmxnet3_get_link_ksettings, }; void vmxnet3_set_ethtool_ops(struct net_device *netdev) -- cgit From 031d4f1210a0401e938a27b229de2e9839311cde Mon Sep 17 00:00:00 2001 From: Satanand Burla Date: Wed, 22 Mar 2017 11:31:13 -0700 Subject: liquidio: allocate RX buffers in OOM conditions in PF and VF Add workqueue that is periodically run to try to allocate RX buffers in OOM conditions in PF and VF. Signed-off-by: Satanand Burla Signed-off-by: Felix Manlunas Signed-off-by: David S. Miller --- .../ethernet/cavium/liquidio/cn23xx_pf_device.h | 2 + drivers/net/ethernet/cavium/liquidio/lio_core.c | 56 ++++++++++++++++++++++ drivers/net/ethernet/cavium/liquidio/lio_main.c | 5 ++ drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 5 ++ drivers/net/ethernet/cavium/liquidio/octeon_droq.c | 26 ++++++++++ drivers/net/ethernet/cavium/liquidio/octeon_droq.h | 2 + .../net/ethernet/cavium/liquidio/octeon_network.h | 7 +++ 7 files changed, 103 insertions(+) diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h index 2fedd91f3df8..dee604651ba7 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h @@ -43,6 +43,8 @@ struct octeon_cn23xx_pf { struct octeon_config *conf; }; +#define CN23XX_SLI_DEF_BP 0x40 + int setup_cn23xx_octeon_pf_device(struct octeon_device *oct); int validate_cn23xx_pf_config_info(struct octeon_device *oct, diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 65a1a9e7a159..08676df6cef0 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -26,6 +26,9 @@ #include "octeon_main.h" #include "octeon_network.h" +/* OOM task polling interval */ +#define LIO_OOM_POLL_INTERVAL_MS 250 + int liquidio_set_feature(struct net_device *netdev, int cmd, u16 param1) { struct lio *lio = GET_LIO(netdev); @@ -293,3 +296,56 @@ void octeon_pf_changed_vf_macaddr(struct octeon_device *oct, u8 *mac) * the PF did that already */ } + +static void octnet_poll_check_rxq_oom_status(struct work_struct *work) +{ + struct cavium_wk *wk = (struct cavium_wk *)work; + struct lio *lio = (struct lio *)wk->ctxptr; + struct octeon_device *oct = lio->oct_dev; + struct octeon_droq *droq; + int q, q_no = 0; + + if (ifstate_check(lio, LIO_IFSTATE_RUNNING)) { + for (q = 0; q < lio->linfo.num_rxpciq; q++) { + q_no = lio->linfo.rxpciq[q].s.q_no; + droq = oct->droq[q_no]; + if (!droq) + continue; + octeon_droq_check_oom(droq); + } + } + queue_delayed_work(lio->rxq_status_wq.wq, + &lio->rxq_status_wq.wk.work, + msecs_to_jiffies(LIO_OOM_POLL_INTERVAL_MS)); +} + +int setup_rx_oom_poll_fn(struct net_device *netdev) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + + lio->rxq_status_wq.wq = alloc_workqueue("rxq-oom-status", + WQ_MEM_RECLAIM, 0); + if (!lio->rxq_status_wq.wq) { + dev_err(&oct->pci_dev->dev, "unable to create cavium rxq oom status wq\n"); + return -ENOMEM; + } + INIT_DELAYED_WORK(&lio->rxq_status_wq.wk.work, + octnet_poll_check_rxq_oom_status); + lio->rxq_status_wq.wk.ctxptr = lio; + queue_delayed_work(lio->rxq_status_wq.wq, + &lio->rxq_status_wq.wk.work, + msecs_to_jiffies(LIO_OOM_POLL_INTERVAL_MS)); + return 0; +} + +void cleanup_rx_oom_poll_fn(struct net_device *netdev) +{ + struct lio *lio = GET_LIO(netdev); + + if (lio->rxq_status_wq.wq) { + cancel_delayed_work_sync(&lio->rxq_status_wq.wk.work); + flush_workqueue(lio->rxq_status_wq.wq); + destroy_workqueue(lio->rxq_status_wq.wq); + } +} diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 0bc76ad96a17..86ea86cfc133 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -1673,6 +1673,8 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) cleanup_link_status_change_wq(netdev); + cleanup_rx_oom_poll_fn(netdev); + delete_glists(lio); free_netdev(netdev); @@ -4147,6 +4149,9 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) if (setup_link_status_change_wq(netdev)) goto setup_nic_dev_fail; + if (setup_rx_oom_poll_fn(netdev)) + goto setup_nic_dev_fail; + /* Register the network device with the OS */ if (register_netdev(netdev)) { dev_err(&octeon_dev->pci_dev->dev, "Device registration failed\n"); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index e03855b8d1f5..65e6f4bfa1cf 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -1155,6 +1155,8 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) unregister_netdev(netdev); + cleanup_rx_oom_poll_fn(netdev); + cleanup_link_status_change_wq(netdev); delete_glists(lio); @@ -2995,6 +2997,9 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) if (setup_link_status_change_wq(netdev)) goto setup_nic_dev_fail; + if (setup_rx_oom_poll_fn(netdev)) + goto setup_nic_dev_fail; + /* Register the network device with the OS */ if (register_netdev(netdev)) { dev_err(&octeon_dev->pci_dev->dev, "Device registration failed\n"); diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c index 00970597ada8..286be5539cef 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c @@ -513,6 +513,32 @@ octeon_droq_refill(struct octeon_device *octeon_dev, struct octeon_droq *droq) return desc_refilled; } +/** check if we can allocate packets to get out of oom. + * @param droq - Droq being checked. + * @return does not return anything + */ +void octeon_droq_check_oom(struct octeon_droq *droq) +{ + int desc_refilled; + struct octeon_device *oct = droq->oct_dev; + + if (readl(droq->pkts_credit_reg) <= CN23XX_SLI_DEF_BP) { + spin_lock_bh(&droq->lock); + desc_refilled = octeon_droq_refill(oct, droq); + if (desc_refilled) { + /* Flush the droq descriptor data to memory to be sure + * that when we update the credits the data in memory + * is accurate. + */ + wmb(); + writel(desc_refilled, droq->pkts_credit_reg); + /* make sure mmio write completes */ + mmiowb(); + } + spin_unlock_bh(&droq->lock); + } +} + static inline u32 octeon_droq_get_bufcount(u32 buf_size, u32 total_len) { diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.h b/drivers/net/ethernet/cavium/liquidio/octeon_droq.h index 6982c0af5ecc..9781577115e7 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.h @@ -426,4 +426,6 @@ int octeon_droq_process_packets(struct octeon_device *oct, int octeon_process_droq_poll_cmd(struct octeon_device *oct, u32 q_no, int cmd, u32 arg); +void octeon_droq_check_oom(struct octeon_droq *droq); + #endif /*__OCTEON_DROQ_H__ */ diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index ddb61bf25775..454ec0ca56ab 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -129,6 +129,9 @@ struct lio { /* work queue for txq status */ struct cavium_wq txq_status_wq; + /* work queue for rxq oom status */ + struct cavium_wq rxq_status_wq; + /* work queue for link status */ struct cavium_wq link_status_wq; @@ -152,6 +155,10 @@ struct lio { */ int liquidio_set_feature(struct net_device *netdev, int cmd, u16 param1); +int setup_rx_oom_poll_fn(struct net_device *netdev); + +void cleanup_rx_oom_poll_fn(struct net_device *netdev); + /** * \brief Link control command completion callback * @param nctrl_ptr pointer to control packet structure -- cgit From 6069f3fbde03211f4b839e188eba2439f8b8326a Mon Sep 17 00:00:00 2001 From: VSR Burru Date: Wed, 22 Mar 2017 11:54:50 -0700 Subject: liquidio: fix tx completions in napi poll If there are no egress packets pending, then don't look for tx completions in napi poll. Also, fix broken tx queue wakeup logic. Signed-off-by: VSR Burru Signed-off-by: Felix Manlunas Signed-off-by: Satanand Burla Signed-off-by: Derek Chickles Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 20 +++++++++++--------- drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 19 +++++++++++-------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 86ea86cfc133..10732e0e48cf 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -932,14 +932,13 @@ static void update_txq_status(struct octeon_device *oct, int iq_num) INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq_num, tx_restart, 1); netif_wake_subqueue(netdev, iq->q_index); - } else { - if (!octnet_iq_is_full(oct, lio->txq)) { - INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, - lio->txq, - tx_restart, 1); - wake_q(netdev, lio->txq); - } } + } else if (netif_queue_stopped(netdev) && + lio->linfo.link.s.link_up && + (!octnet_iq_is_full(oct, lio->txq))) { + INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, + lio->txq, tx_restart, 1); + netif_wake_queue(netdev); } } @@ -2454,8 +2453,11 @@ static int liquidio_napi_poll(struct napi_struct *napi, int budget) /* Flush the instruction queue */ iq = oct->instr_queue[iq_no]; if (iq) { - /* Process iq buffers with in the budget limits */ - tx_done = octeon_flush_iq(oct, iq, budget); + if (atomic_read(&iq->instr_pending)) + /* Process iq buffers with in the budget limits */ + tx_done = octeon_flush_iq(oct, iq, budget); + else + tx_done = 1; /* Update iq read-index rather than waiting for next interrupt. * Return back if tx_done is false. */ diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 65e6f4bfa1cf..68794fa5d322 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -687,13 +687,12 @@ static void update_txq_status(struct octeon_device *oct, int iq_num) netif_wake_subqueue(netdev, iq->q_index); INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq_num, tx_restart, 1); - } else { - if (!octnet_iq_is_full(oct, lio->txq)) { - INCR_INSTRQUEUE_PKT_COUNT( - lio->oct_dev, lio->txq, tx_restart, 1); - wake_q(netdev, lio->txq); - } } + } else if (netif_queue_stopped(netdev) && lio->linfo.link.s.link_up && + (!octnet_iq_is_full(oct, lio->txq))) { + INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, + lio->txq, tx_restart, 1); + netif_wake_queue(netdev); } } @@ -1636,8 +1635,12 @@ static int liquidio_napi_poll(struct napi_struct *napi, int budget) /* Flush the instruction queue */ iq = oct->instr_queue[iq_no]; if (iq) { - /* Process iq buffers with in the budget limits */ - tx_done = octeon_flush_iq(oct, iq, budget); + if (atomic_read(&iq->instr_pending)) + /* Process iq buffers with in the budget limits */ + tx_done = octeon_flush_iq(oct, iq, budget); + else + tx_done = 1; + /* Update iq read-index rather than waiting for next interrupt. * Return back if tx_done is false. */ -- cgit From f4f1c23d6e41f5ee4973a6da65cba1e1c536ec29 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 22 Mar 2017 14:50:57 -0700 Subject: netvsc: fix NAPI performance regression When using NAPI, the single stream performance declined signifcantly because the poll routine was updating host after every burst of packets. This excess signalling caused host throttling. This fix restores the old behavior. Host is only signalled after the ring has been emptied. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 1 + drivers/net/hyperv/netvsc.c | 41 ++++++++++++++++++----------------------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 6b5f75217694..a33f2ee86044 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -723,6 +723,7 @@ struct net_device_context { /* Per channel data */ struct netvsc_channel { struct vmbus_channel *channel; + const struct vmpacket_descriptor *desc; struct napi_struct napi; struct multi_send_data msd; struct multi_recv_comp mrc; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 989b7cd99380..727762d0f13b 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -1173,7 +1173,6 @@ static int netvsc_process_raw_pkt(struct hv_device *device, struct vmbus_channel *channel, struct netvsc_device *net_device, struct net_device *ndev, - u64 request_id, const struct vmpacket_descriptor *desc) { struct net_device_context *net_device_ctx = netdev_priv(ndev); @@ -1195,7 +1194,7 @@ static int netvsc_process_raw_pkt(struct hv_device *device, default: netdev_err(ndev, "unhandled packet type %d, tid %llx\n", - desc->type, request_id); + desc->type, desc->trans_id); break; } @@ -1222,28 +1221,20 @@ int netvsc_poll(struct napi_struct *napi, int budget) u16 q_idx = channel->offermsg.offer.sub_channel_index; struct net_device *ndev = hv_get_drvdata(device); struct netvsc_device *net_device = net_device_to_netvsc_device(ndev); - const struct vmpacket_descriptor *desc; int work_done = 0; - desc = hv_pkt_iter_first(channel); - while (desc) { - int count; + /* If starting a new interval */ + if (!nvchan->desc) + nvchan->desc = hv_pkt_iter_first(channel); - count = netvsc_process_raw_pkt(device, channel, net_device, - ndev, desc->trans_id, desc); - work_done += count; - desc = __hv_pkt_iter_next(channel, desc); - - /* If receive packet budget is exhausted, reschedule */ - if (work_done >= budget) { - work_done = budget; - break; - } + while (nvchan->desc && work_done < budget) { + work_done += netvsc_process_raw_pkt(device, channel, net_device, + ndev, nvchan->desc); + nvchan->desc = hv_pkt_iter_next(channel, nvchan->desc); } - hv_pkt_iter_close(channel); - /* If budget was not exhausted and - * not doing busy poll + /* If receive ring was exhausted + * and not doing busy poll * then re-enable host interrupts * and reschedule if ring is not empty. */ @@ -1253,7 +1244,9 @@ int netvsc_poll(struct napi_struct *napi, int budget) napi_reschedule(napi); netvsc_chk_recv_comp(net_device, channel, q_idx); - return work_done; + + /* Driver may overshoot since multiple packets per descriptor */ + return min(work_done, budget); } /* Call back when data is available in host ring buffer. @@ -1263,10 +1256,12 @@ void netvsc_channel_cb(void *context) { struct netvsc_channel *nvchan = context; - /* disable interupts from host */ - hv_begin_read(&nvchan->channel->inbound); + if (napi_schedule_prep(&nvchan->napi)) { + /* disable interupts from host */ + hv_begin_read(&nvchan->channel->inbound); - napi_schedule(&nvchan->napi); + __napi_schedule(&nvchan->napi); + } } /* -- cgit From 163891d7d42935e7499daa0646a8eb3c44504300 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 22 Mar 2017 14:50:58 -0700 Subject: netvsc: handle offline mtu and channel change If device is not up, then changing MTU (or number of channels) should not re-enable the device. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 191372486a87..b3a7f508434b 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -743,6 +743,7 @@ static int netvsc_set_channels(struct net_device *net, struct hv_device *dev = net_device_ctx->device_ctx; struct netvsc_device *nvdev = net_device_ctx->nvdev; unsigned int count = channels->combined_count; + bool was_running; int ret; /* We do not support separate count for rx, tx, or other */ @@ -762,9 +763,12 @@ static int netvsc_set_channels(struct net_device *net, if (count > nvdev->max_chn) return -EINVAL; - ret = netvsc_close(net); - if (ret) - return ret; + was_running = netif_running(net); + if (was_running) { + ret = netvsc_close(net); + if (ret) + return ret; + } net_device_ctx->start_remove = true; rndis_filter_device_remove(dev, nvdev); @@ -775,9 +779,11 @@ static int netvsc_set_channels(struct net_device *net, else netvsc_set_queues(net, dev, nvdev->num_chn); - netvsc_open(net); net_device_ctx->start_remove = false; + if (was_running) + ret = netvsc_open(net); + /* We may have missed link change notifications */ schedule_delayed_work(&net_device_ctx->dwork, 0); @@ -845,14 +851,18 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) struct netvsc_device *nvdev = ndevctx->nvdev; struct hv_device *hdev = ndevctx->device_ctx; struct netvsc_device_info device_info; + bool was_running; int ret; if (ndevctx->start_remove || !nvdev || nvdev->destroy) return -ENODEV; - ret = netvsc_close(ndev); - if (ret) - goto out; + was_running = netif_running(ndev); + if (was_running) { + ret = netvsc_close(ndev); + if (ret) + return ret; + } memset(&device_info, 0, sizeof(device_info)); device_info.ring_size = ring_size; @@ -872,10 +882,11 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) rndis_filter_device_add(hdev, &device_info); -out: - netvsc_open(ndev); ndevctx->start_remove = false; + if (was_running) + ret = netvsc_open(ndev); + /* We may have missed link change notifications */ schedule_delayed_work(&ndevctx->dwork, 0); -- cgit From 3071ada4916e26a8961c1b99f7766a73b9007bfc Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 22 Mar 2017 14:50:59 -0700 Subject: netvsc: change max channel calculation The default number of maximum channels should be limited to the number of cpus available on the numa node of the primary channel. This also makes sure maximum channels <= num_online_cpus Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 3 +-- drivers/net/hyperv/rndis_filter.c | 25 ++++++++++--------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index b3a7f508434b..2f9de2e9f38e 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1503,8 +1503,7 @@ static int netvsc_probe(struct hv_device *dev, /* Notify the netvsc driver of the new device */ memset(&device_info, 0, sizeof(device_info)); device_info.ring_size = ring_size; - device_info.max_num_vrss_chns = min_t(u32, VRSS_CHANNEL_DEFAULT, - num_online_cpus()); + device_info.num_chn = VRSS_CHANNEL_DEFAULT; ret = rndis_filter_device_add(dev, &device_info); if (ret != 0) { netdev_err(net, "unable to add netvsc device (ret %d)\n", ret); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 382b9a62e3c4..d193d549cec6 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1181,33 +1181,28 @@ int rndis_filter_device_add(struct hv_device *dev, if (ret || rsscap.num_recv_que < 2) goto out; - net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, rsscap.num_recv_que); - - num_rss_qs = min(device_info->max_num_vrss_chns, net_device->max_chn); - /* * We will limit the VRSS channels to the number CPUs in the NUMA node * the primary channel is currently bound to. + * + * This also guarantees that num_possible_rss_qs <= num_online_cpus */ node_cpu_mask = cpumask_of_node(cpu_to_node(dev->channel->target_cpu)); - num_possible_rss_qs = cpumask_weight(node_cpu_mask); + num_possible_rss_qs = min_t(u32, cpumask_weight(node_cpu_mask), + rsscap.num_recv_que); - /* We will use the given number of channels if available. */ - if (device_info->num_chn && device_info->num_chn < net_device->max_chn) - net_device->num_chn = device_info->num_chn; - else - net_device->num_chn = min(num_possible_rss_qs, num_rss_qs); + net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, num_possible_rss_qs); - num_rss_qs = net_device->num_chn - 1; + /* We will use the given number of channels if available. */ + net_device->num_chn = min(net_device->max_chn, device_info->num_chn); for (i = 0; i < ITAB_NUM; i++) rndis_device->ind_table[i] = ethtool_rxfh_indir_default(i, net_device->num_chn); - net_device->num_sc_offered = num_rss_qs; - - if (net_device->num_chn == 1) - goto out; + num_rss_qs = net_device->num_chn - 1; + if (num_rss_qs == 0) + return 0; vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open); -- cgit From 545a8e79bd1cc8774877a26275171a2ec8881c9e Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 22 Mar 2017 14:51:00 -0700 Subject: netvsc: use RCU to protect inner device structure The netvsc driver has an internal structure (netvsc_device) which is created when device is opened and released when device is closed. And also opened/released when MTU or number of channels change. Since this is referenced in the receive and transmit path, it is safer to use RCU to protect/prevent use after free problems. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 4 ++- drivers/net/hyperv/netvsc.c | 16 +++++++---- drivers/net/hyperv/netvsc_drv.c | 61 +++++++++++++++++++++++++++++------------ 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index a33f2ee86044..0ade21f95d71 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -686,7 +686,7 @@ struct net_device_context { /* point back to our device context */ struct hv_device *device_ctx; /* netvsc_device */ - struct netvsc_device *nvdev; + struct netvsc_device __rcu *nvdev; /* reconfigure work */ struct delayed_work dwork; /* last reconfig time */ @@ -780,6 +780,8 @@ struct netvsc_device { atomic_t open_cnt; struct netvsc_channel chan_table[VRSS_CHANNEL_MAX]; + + struct rcu_head rcu; }; static inline struct netvsc_device * diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 727762d0f13b..ab9118d620ab 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -80,8 +80,10 @@ static struct netvsc_device *alloc_net_device(void) return net_device; } -static void free_netvsc_device(struct netvsc_device *nvdev) +static void free_netvsc_device(struct rcu_head *head) { + struct netvsc_device *nvdev + = container_of(head, struct netvsc_device, rcu); int i; for (i = 0; i < VRSS_CHANNEL_MAX; i++) @@ -90,6 +92,10 @@ static void free_netvsc_device(struct netvsc_device *nvdev) kfree(nvdev); } +static void free_netvsc_device_rcu(struct netvsc_device *nvdev) +{ + call_rcu(&nvdev->rcu, free_netvsc_device); +} static struct netvsc_device *get_outbound_net_device(struct hv_device *device) { @@ -551,7 +557,7 @@ void netvsc_device_remove(struct hv_device *device) netvsc_disconnect_vsp(device); - net_device_ctx->nvdev = NULL; + RCU_INIT_POINTER(net_device_ctx->nvdev, NULL); /* * At this point, no one should be accessing net_device @@ -566,7 +572,7 @@ void netvsc_device_remove(struct hv_device *device) napi_disable(&net_device->chan_table[i].napi); /* Release all resources */ - free_netvsc_device(net_device); + free_netvsc_device_rcu(net_device); } #define RING_AVAIL_PERCENT_HIWATER 20 @@ -1322,7 +1328,7 @@ int netvsc_device_add(struct hv_device *device, */ wmb(); - net_device_ctx->nvdev = net_device; + rcu_assign_pointer(net_device_ctx->nvdev, net_device); /* Connect with the NetVsp */ ret = netvsc_connect_vsp(device); @@ -1341,7 +1347,7 @@ close: vmbus_close(device->channel); cleanup: - free_netvsc_device(net_device); + free_netvsc_device(&net_device->rcu); return ret; } diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 2f9de2e9f38e..d8a70d07eeec 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -62,7 +62,7 @@ static void do_set_multicast(struct work_struct *w) container_of(w, struct net_device_context, work); struct hv_device *device_obj = ndevctx->device_ctx; struct net_device *ndev = hv_get_drvdata(device_obj); - struct netvsc_device *nvdev = ndevctx->nvdev; + struct netvsc_device *nvdev = rcu_dereference(ndevctx->nvdev); struct rndis_device *rdev; if (!nvdev) @@ -116,7 +116,7 @@ static int netvsc_open(struct net_device *net) static int netvsc_close(struct net_device *net) { struct net_device_context *net_device_ctx = netdev_priv(net); - struct netvsc_device *nvdev = net_device_ctx->nvdev; + struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev); int ret; u32 aread, awrite, i, msec = 10, retry = 0, retry_max = 20; struct vmbus_channel *chn; @@ -637,9 +637,9 @@ int netvsc_recv_callback(struct net_device *net, const struct ndis_pkt_8021q_info *vlan) { struct net_device_context *net_device_ctx = netdev_priv(net); - struct netvsc_device *net_device = net_device_ctx->nvdev; + struct netvsc_device *net_device; u16 q_idx = channel->offermsg.offer.sub_channel_index; - struct netvsc_channel *nvchan = &net_device->chan_table[q_idx]; + struct netvsc_channel *nvchan; struct net_device *vf_netdev; struct sk_buff *skb; struct netvsc_stats *rx_stats; @@ -655,6 +655,11 @@ int netvsc_recv_callback(struct net_device *net, * interface in the guest. */ rcu_read_lock(); + net_device = rcu_dereference(net_device_ctx->nvdev); + if (unlikely(!net_device)) + goto drop; + + nvchan = &net_device->chan_table[q_idx]; vf_netdev = rcu_dereference(net_device_ctx->vf_netdev); if (vf_netdev && (vf_netdev->flags & IFF_UP)) net = vf_netdev; @@ -663,6 +668,7 @@ int netvsc_recv_callback(struct net_device *net, skb = netvsc_alloc_recv_skb(net, &nvchan->napi, csum_info, vlan, data, len); if (unlikely(!skb)) { +drop: ++net->stats.rx_dropped; rcu_read_unlock(); return NVSP_STAT_FAIL; @@ -704,7 +710,7 @@ static void netvsc_get_channels(struct net_device *net, struct ethtool_channels *channel) { struct net_device_context *net_device_ctx = netdev_priv(net); - struct netvsc_device *nvdev = net_device_ctx->nvdev; + struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev); if (nvdev) { channel->max_combined = nvdev->max_chn; @@ -741,7 +747,7 @@ static int netvsc_set_channels(struct net_device *net, { struct net_device_context *net_device_ctx = netdev_priv(net); struct hv_device *dev = net_device_ctx->device_ctx; - struct netvsc_device *nvdev = net_device_ctx->nvdev; + struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev); unsigned int count = channels->combined_count; bool was_running; int ret; @@ -848,7 +854,7 @@ static int netvsc_set_link_ksettings(struct net_device *dev, static int netvsc_change_mtu(struct net_device *ndev, int mtu) { struct net_device_context *ndevctx = netdev_priv(ndev); - struct netvsc_device *nvdev = ndevctx->nvdev; + struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev); struct hv_device *hdev = ndevctx->device_ctx; struct netvsc_device_info device_info; bool was_running; @@ -897,7 +903,7 @@ static void netvsc_get_stats64(struct net_device *net, struct rtnl_link_stats64 *t) { struct net_device_context *ndev_ctx = netdev_priv(net); - struct netvsc_device *nvdev = ndev_ctx->nvdev; + struct netvsc_device *nvdev = rcu_dereference(ndev_ctx->nvdev); int i; if (!nvdev) @@ -982,7 +988,10 @@ static const struct { static int netvsc_get_sset_count(struct net_device *dev, int string_set) { struct net_device_context *ndc = netdev_priv(dev); - struct netvsc_device *nvdev = ndc->nvdev; + struct netvsc_device *nvdev = rcu_dereference(ndc->nvdev); + + if (!nvdev) + return -ENODEV; switch (string_set) { case ETH_SS_STATS: @@ -996,13 +1005,16 @@ static void netvsc_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct net_device_context *ndc = netdev_priv(dev); - struct netvsc_device *nvdev = ndc->nvdev; + struct netvsc_device *nvdev = rcu_dereference(ndc->nvdev); const void *nds = &ndc->eth_stats; const struct netvsc_stats *qstats; unsigned int start; u64 packets, bytes; int i, j; + if (!nvdev) + return; + for (i = 0; i < NETVSC_GLOBAL_STATS_LEN; i++) data[i] = *(unsigned long *)(nds + netvsc_stats[i].offset); @@ -1031,10 +1043,13 @@ static void netvsc_get_ethtool_stats(struct net_device *dev, static void netvsc_get_strings(struct net_device *dev, u32 stringset, u8 *data) { struct net_device_context *ndc = netdev_priv(dev); - struct netvsc_device *nvdev = ndc->nvdev; + struct netvsc_device *nvdev = rcu_dereference(ndc->nvdev); u8 *p = data; int i; + if (!nvdev) + return; + switch (stringset) { case ETH_SS_STATS: for (i = 0; i < ARRAY_SIZE(netvsc_stats); i++) @@ -1086,7 +1101,10 @@ netvsc_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, u32 *rules) { struct net_device_context *ndc = netdev_priv(dev); - struct netvsc_device *nvdev = ndc->nvdev; + struct netvsc_device *nvdev = rcu_dereference(ndc->nvdev); + + if (!nvdev) + return -ENODEV; switch (info->cmd) { case ETHTOOL_GRXRINGS: @@ -1122,10 +1140,13 @@ static int netvsc_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, u8 *hfunc) { struct net_device_context *ndc = netdev_priv(dev); - struct netvsc_device *ndev = ndc->nvdev; + struct netvsc_device *ndev = rcu_dereference(ndc->nvdev); struct rndis_device *rndis_dev = ndev->extension; int i; + if (!ndev) + return -ENODEV; + if (hfunc) *hfunc = ETH_RSS_HASH_TOP; /* Toeplitz */ @@ -1144,10 +1165,13 @@ static int netvsc_set_rxfh(struct net_device *dev, const u32 *indir, const u8 *key, const u8 hfunc) { struct net_device_context *ndc = netdev_priv(dev); - struct netvsc_device *ndev = ndc->nvdev; + struct netvsc_device *ndev = rtnl_dereference(ndc->nvdev); struct rndis_device *rndis_dev = ndev->extension; int i; + if (!ndev) + return -ENODEV; + if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) return -EOPNOTSUPP; @@ -1224,7 +1248,7 @@ static void netvsc_link_change(struct work_struct *w) if (ndev_ctx->start_remove) goto out_unlock; - net_device = ndev_ctx->nvdev; + net_device = rtnl_dereference(ndev_ctx->nvdev); rdev = net_device->extension; next_reconfig = ndev_ctx->last_reconfig + LINKCHANGE_INT; @@ -1365,7 +1389,7 @@ static int netvsc_register_vf(struct net_device *vf_netdev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); - netvsc_dev = net_device_ctx->nvdev; + netvsc_dev = rtnl_dereference(net_device_ctx->nvdev); if (!netvsc_dev || rtnl_dereference(net_device_ctx->vf_netdev)) return NOTIFY_DONE; @@ -1391,7 +1415,7 @@ static int netvsc_vf_up(struct net_device *vf_netdev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); - netvsc_dev = net_device_ctx->nvdev; + netvsc_dev = rtnl_dereference(net_device_ctx->nvdev); netdev_info(ndev, "VF up: %s\n", vf_netdev->name); @@ -1425,7 +1449,7 @@ static int netvsc_vf_down(struct net_device *vf_netdev) return NOTIFY_DONE; net_device_ctx = netdev_priv(ndev); - netvsc_dev = net_device_ctx->nvdev; + netvsc_dev = rtnl_dereference(net_device_ctx->nvdev); netdev_info(ndev, "VF down: %s\n", vf_netdev->name); netvsc_switch_datapath(ndev, false); @@ -1519,6 +1543,7 @@ static int netvsc_probe(struct hv_device *dev, NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; net->vlan_features = net->features; + /* RCU not necessary here, device not registered */ nvdev = net_device_ctx->nvdev; netif_set_real_num_tx_queues(net, nvdev->num_chn); netif_set_real_num_rx_queues(net, nvdev->num_chn); -- cgit From a0be450e19d397e9ff215e32ed31bc51339b460a Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 22 Mar 2017 14:51:01 -0700 Subject: netvsc: uses RCU instead of removal flag It is cleaner to use RCU protected pointer (nvdev_ctx->nvdev) to indicate device is in removed state, rather than having a separate boolean flag. By using the pointer the context can be checked by static checkers and dynamic lockdep. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 3 --- drivers/net/hyperv/netvsc.c | 4 ---- drivers/net/hyperv/netvsc_drv.c | 34 ++++++++++------------------------ 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 0ade21f95d71..907f55960ba8 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -708,9 +708,6 @@ struct net_device_context { u32 speed; struct netvsc_ethtool_stats eth_stats; - /* the device is going away */ - bool start_remove; - /* State to manage the associated VF interface. */ struct net_device __rcu *vf_netdev; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index ab9118d620ab..1f17d948f9b0 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -605,7 +605,6 @@ static void netvsc_send_tx_complete(struct netvsc_device *net_device, { struct sk_buff *skb = (struct sk_buff *)(unsigned long)desc->trans_id; struct net_device *ndev = hv_get_drvdata(device); - struct net_device_context *net_device_ctx = netdev_priv(ndev); struct vmbus_channel *channel = device->channel; u16 q_idx = 0; int queue_sends; @@ -639,7 +638,6 @@ static void netvsc_send_tx_complete(struct netvsc_device *net_device, wake_up(&net_device->wait_drain); if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) && - !net_device_ctx->start_remove && (hv_ringbuf_avail_percent(&channel->outbound) > RING_AVAIL_PERCENT_HIWATER || queue_sends < 1)) netif_tx_wake_queue(netdev_get_tx_queue(ndev, q_idx)); @@ -1326,8 +1324,6 @@ int netvsc_device_add(struct hv_device *device, /* Writing nvdev pointer unlocks netvsc_send(), make sure chn_table is * populated. */ - wmb(); - rcu_assign_pointer(net_device_ctx->nvdev, net_device); /* Connect with the NetVsp */ diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index d8a70d07eeec..eb7ae79d47bb 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -760,7 +760,7 @@ static int netvsc_set_channels(struct net_device *net, if (count > net->num_tx_queues || count > net->num_rx_queues) return -EINVAL; - if (net_device_ctx->start_remove || !nvdev || nvdev->destroy) + if (!nvdev || nvdev->destroy) return -ENODEV; if (nvdev->nvsp_version < NVSP_PROTOCOL_VERSION_5) @@ -776,7 +776,6 @@ static int netvsc_set_channels(struct net_device *net, return ret; } - net_device_ctx->start_remove = true; rndis_filter_device_remove(dev, nvdev); ret = netvsc_set_queues(net, dev, count); @@ -785,8 +784,6 @@ static int netvsc_set_channels(struct net_device *net, else netvsc_set_queues(net, dev, nvdev->num_chn); - net_device_ctx->start_remove = false; - if (was_running) ret = netvsc_open(net); @@ -860,7 +857,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) bool was_running; int ret; - if (ndevctx->start_remove || !nvdev || nvdev->destroy) + if (!nvdev || nvdev->destroy) return -ENODEV; was_running = netif_running(ndev); @@ -875,7 +872,6 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) device_info.num_chn = nvdev->num_chn; device_info.max_num_vrss_chns = nvdev->num_chn; - ndevctx->start_remove = true; rndis_filter_device_remove(hdev, nvdev); /* 'nvdev' has been freed in rndis_filter_device_remove() -> @@ -888,8 +884,6 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) rndis_filter_device_add(hdev, &device_info); - ndevctx->start_remove = false; - if (was_running) ret = netvsc_open(ndev); @@ -1245,10 +1239,10 @@ static void netvsc_link_change(struct work_struct *w) unsigned long flags, next_reconfig, delay; rtnl_lock(); - if (ndev_ctx->start_remove) + net_device = rtnl_dereference(ndev_ctx->nvdev); + if (!net_device) goto out_unlock; - net_device = rtnl_dereference(ndev_ctx->nvdev); rdev = net_device->extension; next_reconfig = ndev_ctx->last_reconfig + LINKCHANGE_INT; @@ -1509,8 +1503,6 @@ static int netvsc_probe(struct hv_device *dev, hv_set_drvdata(dev, net); - net_device_ctx->start_remove = false; - INIT_DELAYED_WORK(&net_device_ctx->dwork, netvsc_link_change); INIT_WORK(&net_device_ctx->work, do_set_multicast); @@ -1579,26 +1571,20 @@ static int netvsc_remove(struct hv_device *dev) ndev_ctx = netdev_priv(net); - /* Avoid racing with netvsc_change_mtu()/netvsc_set_channels() - * removing the device. - */ - rtnl_lock(); - ndev_ctx->start_remove = true; - rtnl_unlock(); + netif_device_detach(net); cancel_delayed_work_sync(&ndev_ctx->dwork); cancel_work_sync(&ndev_ctx->work); - /* Stop outbound asap */ - netif_tx_disable(net); - - unregister_netdev(net); - /* * Call to the vsc driver to let it know that the device is being - * removed + * removed. Also blocks mtu and channel changes. */ + rtnl_lock(); rndis_filter_device_remove(dev, ndev_ctx->nvdev); + rtnl_unlock(); + + unregister_netdev(net); hv_set_drvdata(dev, NULL); -- cgit From 43c7bd1ffcd1621c64cedf1be52156e2f95bba9b Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 22 Mar 2017 14:51:02 -0700 Subject: netvsc: use refcount_t for keeping track of sub channels Rather than a lock and variable, use a refcount_t to keep track of the number of sub channels. Don't need to wait for subchannels on device removal since wait was already done in device_add. Also fix the error handling; don't wait forever in case of an error on request to create sub channels. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 4 ++-- drivers/net/hyperv/rndis_filter.c | 41 ++++++++++----------------------------- 2 files changed, 12 insertions(+), 33 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 907f55960ba8..4747ad48b3cc 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -761,8 +761,8 @@ struct netvsc_device { u32 max_chn; u32 num_chn; - spinlock_t sc_lock; /* Protects num_sc_offered variable */ - u32 num_sc_offered; + + refcount_t sc_offered; /* Holds rndis device info */ void *extension; diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index d193d549cec6..3bd5447277ad 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -997,7 +997,6 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) struct netvsc_device *nvscdev = net_device_to_netvsc_device(ndev); u16 chn_index = new_sc->offermsg.offer.sub_channel_index; struct netvsc_channel *nvchan; - unsigned long flags; int ret; if (chn_index >= nvscdev->num_chn) @@ -1019,10 +1018,7 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) napi_enable(&nvchan->napi); - spin_lock_irqsave(&nvscdev->sc_lock, flags); - nvscdev->num_sc_offered--; - spin_unlock_irqrestore(&nvscdev->sc_lock, flags); - if (nvscdev->num_sc_offered == 0) + if (refcount_dec_and_test(&nvscdev->sc_offered)) complete(&nvscdev->channel_init_wait); } @@ -1039,12 +1035,9 @@ int rndis_filter_device_add(struct hv_device *dev, struct ndis_recv_scale_cap rsscap; u32 rsscap_size = sizeof(struct ndis_recv_scale_cap); unsigned int gso_max_size = GSO_MAX_SIZE; - u32 mtu, size; - u32 num_rss_qs; - u32 sc_delta; + u32 mtu, size, num_rss_qs; const struct cpumask *node_cpu_mask; u32 num_possible_rss_qs; - unsigned long flags; int i, ret; rndis_device = get_rndis_device(); @@ -1067,7 +1060,7 @@ int rndis_filter_device_add(struct hv_device *dev, net_device->max_chn = 1; net_device->num_chn = 1; - spin_lock_init(&net_device->sc_lock); + refcount_set(&net_device->sc_offered, 0); net_device->extension = rndis_device; rndis_device->ndev = net; @@ -1204,6 +1197,7 @@ int rndis_filter_device_add(struct hv_device *dev, if (num_rss_qs == 0) return 0; + refcount_set(&net_device->sc_offered, num_rss_qs); vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open); init_packet = &net_device->channel_init_pkt; @@ -1219,32 +1213,23 @@ int rndis_filter_device_add(struct hv_device *dev, VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); if (ret) goto out; - wait_for_completion(&net_device->channel_init_wait); - if (init_packet->msg.v5_msg.subchn_comp.status != - NVSP_STAT_SUCCESS) { + if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) { ret = -ENODEV; goto out; } + wait_for_completion(&net_device->channel_init_wait); + net_device->num_chn = 1 + init_packet->msg.v5_msg.subchn_comp.num_subchannels; - ret = rndis_filter_set_rss_param(rndis_device, netvsc_hash_key, - net_device->num_chn); - - /* - * Set the number of sub-channels to be received. - */ - spin_lock_irqsave(&net_device->sc_lock, flags); - sc_delta = num_rss_qs - (net_device->num_chn - 1); - net_device->num_sc_offered -= sc_delta; - spin_unlock_irqrestore(&net_device->sc_lock, flags); - + /* ignore failues from setting rss parameters, still have channels */ + rndis_filter_set_rss_param(rndis_device, netvsc_hash_key, + net_device->num_chn); out: if (ret) { net_device->max_chn = 1; net_device->num_chn = 1; - net_device->num_sc_offered = 0; } return 0; /* return 0 because primary channel can be used alone */ @@ -1259,12 +1244,6 @@ void rndis_filter_device_remove(struct hv_device *dev, { struct rndis_device *rndis_dev = net_dev->extension; - /* If not all subchannel offers are complete, wait for them until - * completion to avoid race. - */ - if (net_dev->num_sc_offered > 0) - wait_for_completion(&net_dev->channel_init_wait); - /* Halt and release the rndis device */ rndis_filter_halt_device(rndis_dev); -- cgit From 00ecfb3b34b69dd702dee1bd6de6fc100be384db Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 22 Mar 2017 14:51:03 -0700 Subject: netvsc: remove unnecessary lock on shutdown The channel inbound lock was not being used at all by the netvsc device, but the spin_lock was helpful by providing necessary barrier before waiting. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/rndis_filter.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 3bd5447277ad..cd7b83707e04 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -926,8 +926,6 @@ static void rndis_filter_halt_device(struct rndis_device *dev) struct rndis_halt_request *halt; struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); struct netvsc_device *nvdev = net_device_ctx->nvdev; - struct hv_device *hdev = net_device_ctx->device_ctx; - ulong flags; /* Attempt to do a rndis device halt */ request = get_rndis_request(dev, RNDIS_MSG_HALT, @@ -945,9 +943,10 @@ static void rndis_filter_halt_device(struct rndis_device *dev) dev->state = RNDIS_DEV_UNINITIALIZED; cleanup: - spin_lock_irqsave(&hdev->channel->inbound_lock, flags); nvdev->destroy = true; - spin_unlock_irqrestore(&hdev->channel->inbound_lock, flags); + + /* Force flag to be ordered before waiting */ + wmb(); /* Wait for all send completions */ wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev)); -- cgit From ebc1dcf6008e562a8f88a6b1f4a4705f4d4c4fdd Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 22 Mar 2017 14:51:04 -0700 Subject: netvsc: eliminate unnecessary skb == NULL checks Since there already is a special case goto for control messages (skb == NULL) in netvsc_send, there is no need for later checks in same code path. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 1f17d948f9b0..e998e2f7a619 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -706,8 +706,7 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, packet->page_buf_cnt; /* Add padding */ - if (skb && skb->xmit_more && remain && - !packet->cp_partial) { + if (skb->xmit_more && remain && !packet->cp_partial) { padding = net_device->pkt_align - remain; rndis_msg->msg_len += padding; packet->total_data_buflen += padding; @@ -865,9 +864,7 @@ int netvsc_send(struct hv_device *device, if (msdp->pkt) msd_len = msdp->pkt->total_data_buflen; - try_batch = (skb != NULL) && msd_len > 0 && msdp->count < - net_device->max_pkt; - + try_batch = msd_len > 0 && msdp->count < net_device->max_pkt; if (try_batch && msd_len + pktlen + net_device->pkt_align < net_device->send_section_size) { section_index = msdp->pkt->send_buf_index; @@ -877,7 +874,7 @@ int netvsc_send(struct hv_device *device, section_index = msdp->pkt->send_buf_index; packet->cp_partial = true; - } else if ((skb != NULL) && pktlen + net_device->pkt_align < + } else if (pktlen + net_device->pkt_align < net_device->send_section_size) { section_index = netvsc_get_next_send_section(net_device); if (section_index != NETVSC_INVALID_INDEX) { -- cgit From ce12b81061a0a2647ca90c04d131d90edd5c5063 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 22 Mar 2017 14:51:05 -0700 Subject: netvsc: fix and cleanup rndis_filter_set_packet_filter Fix warning from unused set_complete variable. And rearrange code to eliminate unnecessary goto's. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/rndis_filter.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index cd7b83707e04..91b3bcfd9acb 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -819,16 +819,14 @@ int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter) { struct rndis_request *request; struct rndis_set_request *set; - struct rndis_set_complete *set_complete; int ret; request = get_rndis_request(dev, RNDIS_MSG_SET, RNDIS_MESSAGE_SIZE(struct rndis_set_request) + sizeof(u32)); - if (!request) { - ret = -ENOMEM; - goto cleanup; - } + if (!request) + return -ENOMEM; + /* Setup the rndis set */ set = &request->request_msg.msg.set_req; @@ -840,15 +838,11 @@ int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter) &new_filter, sizeof(u32)); ret = rndis_filter_send_request(dev, request); - if (ret != 0) - goto cleanup; + if (ret == 0) + wait_for_completion(&request->wait_event); - wait_for_completion(&request->wait_event); + put_rndis_request(dev, request); - set_complete = &request->response_msg.msg.set_complete; -cleanup: - if (request) - put_rndis_request(dev, request); return ret; } -- cgit From 4ed1eea82a21ba460e53468cc1c73cfdf83d0e00 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Thu, 23 Mar 2017 15:50:15 +0200 Subject: qed: Revise MFW command locking Interaction of driver -> management firmware is based on a one-pending mailbox [per interface], and various mailbox commands need to be synchronized. Current scheme is messy, and there's a difficulty extending it as it deals differently with various commands as well as making assumption on the required behavior for load/unload requests. Drop the current scheme into a completion-list-based approach; Each flow would try sending the command when possible, allowing one flow to complete another flow's completion and relieve the mailbox before sending its own command. Signed-off-by: Tomer Tayar Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_mcp.c | 405 ++++++++++++++++++++---------- drivers/net/ethernet/qlogic/qed/qed_mcp.h | 11 +- 2 files changed, 280 insertions(+), 136 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 87fde205149f..0d157de83897 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -111,12 +111,71 @@ void qed_mcp_read_mb(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) } } +struct qed_mcp_cmd_elem { + struct list_head list; + struct qed_mcp_mb_params *p_mb_params; + u16 expected_seq_num; + bool b_is_completed; +}; + +/* Must be called while cmd_lock is acquired */ +static struct qed_mcp_cmd_elem * +qed_mcp_cmd_add_elem(struct qed_hwfn *p_hwfn, + struct qed_mcp_mb_params *p_mb_params, + u16 expected_seq_num) +{ + struct qed_mcp_cmd_elem *p_cmd_elem = NULL; + + p_cmd_elem = kzalloc(sizeof(*p_cmd_elem), GFP_ATOMIC); + if (!p_cmd_elem) + goto out; + + p_cmd_elem->p_mb_params = p_mb_params; + p_cmd_elem->expected_seq_num = expected_seq_num; + list_add(&p_cmd_elem->list, &p_hwfn->mcp_info->cmd_list); +out: + return p_cmd_elem; +} + +/* Must be called while cmd_lock is acquired */ +static void qed_mcp_cmd_del_elem(struct qed_hwfn *p_hwfn, + struct qed_mcp_cmd_elem *p_cmd_elem) +{ + list_del(&p_cmd_elem->list); + kfree(p_cmd_elem); +} + +/* Must be called while cmd_lock is acquired */ +static struct qed_mcp_cmd_elem *qed_mcp_cmd_get_elem(struct qed_hwfn *p_hwfn, + u16 seq_num) +{ + struct qed_mcp_cmd_elem *p_cmd_elem = NULL; + + list_for_each_entry(p_cmd_elem, &p_hwfn->mcp_info->cmd_list, list) { + if (p_cmd_elem->expected_seq_num == seq_num) + return p_cmd_elem; + } + + return NULL; +} + int qed_mcp_free(struct qed_hwfn *p_hwfn) { if (p_hwfn->mcp_info) { + struct qed_mcp_cmd_elem *p_cmd_elem, *p_tmp; + kfree(p_hwfn->mcp_info->mfw_mb_cur); kfree(p_hwfn->mcp_info->mfw_mb_shadow); + + spin_lock_bh(&p_hwfn->mcp_info->cmd_lock); + list_for_each_entry_safe(p_cmd_elem, + p_tmp, + &p_hwfn->mcp_info->cmd_list, list) { + qed_mcp_cmd_del_elem(p_hwfn, p_cmd_elem); + } + spin_unlock_bh(&p_hwfn->mcp_info->cmd_lock); } + kfree(p_hwfn->mcp_info); return 0; @@ -160,7 +219,7 @@ static int qed_load_mcp_offsets(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) p_info->drv_pulse_seq = DRV_MB_RD(p_hwfn, p_ptt, drv_pulse_mb) & DRV_PULSE_SEQ_MASK; - p_info->mcp_hist = (u16)qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0); + p_info->mcp_hist = qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0); return 0; } @@ -176,6 +235,12 @@ int qed_mcp_cmd_init(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) goto err; p_info = p_hwfn->mcp_info; + /* Initialize the MFW spinlock */ + spin_lock_init(&p_info->cmd_lock); + spin_lock_init(&p_info->link_lock); + + INIT_LIST_HEAD(&p_info->cmd_list); + if (qed_load_mcp_offsets(p_hwfn, p_ptt) != 0) { DP_NOTICE(p_hwfn, "MCP is not initialized\n"); /* Do not free mcp_info here, since public_base indicate that @@ -190,10 +255,6 @@ int qed_mcp_cmd_init(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) if (!p_info->mfw_mb_shadow || !p_info->mfw_mb_addr) goto err; - /* Initialize the MFW spinlock */ - spin_lock_init(&p_info->lock); - spin_lock_init(&p_info->link_lock); - return 0; err: @@ -201,68 +262,39 @@ err: return -ENOMEM; } -/* Locks the MFW mailbox of a PF to ensure a single access. - * The lock is achieved in most cases by holding a spinlock, causing other - * threads to wait till a previous access is done. - * In some cases (currently when a [UN]LOAD_REQ commands are sent), the single - * access is achieved by setting a blocking flag, which will fail other - * competing contexts to send their mailboxes. - */ -static int qed_mcp_mb_lock(struct qed_hwfn *p_hwfn, u32 cmd) +static void qed_mcp_reread_offsets(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt) { - spin_lock_bh(&p_hwfn->mcp_info->lock); - - /* The spinlock shouldn't be acquired when the mailbox command is - * [UN]LOAD_REQ, since the engine is locked by the MFW, and a parallel - * pending [UN]LOAD_REQ command of another PF together with a spinlock - * (i.e. interrupts are disabled) - can lead to a deadlock. - * It is assumed that for a single PF, no other mailbox commands can be - * sent from another context while sending LOAD_REQ, and that any - * parallel commands to UNLOAD_REQ can be cancelled. - */ - if (cmd == DRV_MSG_CODE_LOAD_DONE || cmd == DRV_MSG_CODE_UNLOAD_DONE) - p_hwfn->mcp_info->block_mb_sending = false; + u32 generic_por_0 = qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0); - if (p_hwfn->mcp_info->block_mb_sending) { - DP_NOTICE(p_hwfn, - "Trying to send a MFW mailbox command [0x%x] in parallel to [UN]LOAD_REQ. Aborting.\n", - cmd); - spin_unlock_bh(&p_hwfn->mcp_info->lock); - return -EBUSY; - } + /* Use MCP history register to check if MCP reset occurred between init + * time and now. + */ + if (p_hwfn->mcp_info->mcp_hist != generic_por_0) { + DP_VERBOSE(p_hwfn, + QED_MSG_SP, + "Rereading MCP offsets [mcp_hist 0x%08x, generic_por_0 0x%08x]\n", + p_hwfn->mcp_info->mcp_hist, generic_por_0); - if (cmd == DRV_MSG_CODE_LOAD_REQ || cmd == DRV_MSG_CODE_UNLOAD_REQ) { - p_hwfn->mcp_info->block_mb_sending = true; - spin_unlock_bh(&p_hwfn->mcp_info->lock); + qed_load_mcp_offsets(p_hwfn, p_ptt); + qed_mcp_cmd_port_init(p_hwfn, p_ptt); } - - return 0; -} - -static void qed_mcp_mb_unlock(struct qed_hwfn *p_hwfn, u32 cmd) -{ - if (cmd != DRV_MSG_CODE_LOAD_REQ && cmd != DRV_MSG_CODE_UNLOAD_REQ) - spin_unlock_bh(&p_hwfn->mcp_info->lock); } int qed_mcp_reset(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { - u32 seq = ++p_hwfn->mcp_info->drv_mb_seq; - u8 delay = CHIP_MCP_RESP_ITER_US; - u32 org_mcp_reset_seq, cnt = 0; + u32 org_mcp_reset_seq, seq, delay = CHIP_MCP_RESP_ITER_US, cnt = 0; int rc = 0; - /* Ensure that only a single thread is accessing the mailbox at a - * certain time. - */ - rc = qed_mcp_mb_lock(p_hwfn, DRV_MSG_CODE_MCP_RESET); - if (rc != 0) - return rc; + /* Ensure that only a single thread is accessing the mailbox */ + spin_lock_bh(&p_hwfn->mcp_info->cmd_lock); - /* Set drv command along with the updated sequence */ org_mcp_reset_seq = qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0); - DRV_MB_WR(p_hwfn, p_ptt, drv_mb_header, - (DRV_MSG_CODE_MCP_RESET | seq)); + + /* Set drv command along with the updated sequence */ + qed_mcp_reread_offsets(p_hwfn, p_ptt); + seq = ++p_hwfn->mcp_info->drv_mb_seq; + DRV_MB_WR(p_hwfn, p_ptt, drv_mb_header, (DRV_MSG_CODE_MCP_RESET | seq)); do { /* Wait for MFW response */ @@ -281,72 +313,205 @@ int qed_mcp_reset(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) rc = -EAGAIN; } - qed_mcp_mb_unlock(p_hwfn, DRV_MSG_CODE_MCP_RESET); + spin_unlock_bh(&p_hwfn->mcp_info->cmd_lock); return rc; } -static int qed_do_mcp_cmd(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - u32 cmd, - u32 param, - u32 *o_mcp_resp, - u32 *o_mcp_param) +/* Must be called while cmd_lock is acquired */ +static bool qed_mcp_has_pending_cmd(struct qed_hwfn *p_hwfn) { - u8 delay = CHIP_MCP_RESP_ITER_US; - u32 seq, cnt = 1, actual_mb_seq; - int rc = 0; + struct qed_mcp_cmd_elem *p_cmd_elem; - /* Get actual driver mailbox sequence */ - actual_mb_seq = DRV_MB_RD(p_hwfn, p_ptt, drv_mb_header) & - DRV_MSG_SEQ_NUMBER_MASK; - - /* Use MCP history register to check if MCP reset occurred between - * init time and now. + /* There is at most one pending command at a certain time, and if it + * exists - it is placed at the HEAD of the list. */ - if (p_hwfn->mcp_info->mcp_hist != - qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0)) { - DP_VERBOSE(p_hwfn, QED_MSG_SP, "Rereading MCP offsets\n"); - qed_load_mcp_offsets(p_hwfn, p_ptt); - qed_mcp_cmd_port_init(p_hwfn, p_ptt); + if (!list_empty(&p_hwfn->mcp_info->cmd_list)) { + p_cmd_elem = list_first_entry(&p_hwfn->mcp_info->cmd_list, + struct qed_mcp_cmd_elem, list); + return !p_cmd_elem->b_is_completed; } - seq = ++p_hwfn->mcp_info->drv_mb_seq; - /* Set drv param */ - DRV_MB_WR(p_hwfn, p_ptt, drv_mb_param, param); + return false; +} + +/* Must be called while cmd_lock is acquired */ +static int +qed_mcp_update_pending_cmd(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + struct qed_mcp_mb_params *p_mb_params; + struct qed_mcp_cmd_elem *p_cmd_elem; + u32 mcp_resp; + u16 seq_num; - /* Set drv command along with the updated sequence */ - DRV_MB_WR(p_hwfn, p_ptt, drv_mb_header, (cmd | seq)); + mcp_resp = DRV_MB_RD(p_hwfn, p_ptt, fw_mb_header); + seq_num = (u16)(mcp_resp & FW_MSG_SEQ_NUMBER_MASK); + + /* Return if no new non-handled response has been received */ + if (seq_num != p_hwfn->mcp_info->drv_mb_seq) + return -EAGAIN; + + p_cmd_elem = qed_mcp_cmd_get_elem(p_hwfn, seq_num); + if (!p_cmd_elem) { + DP_ERR(p_hwfn, + "Failed to find a pending mailbox cmd that expects sequence number %d\n", + seq_num); + return -EINVAL; + } + + p_mb_params = p_cmd_elem->p_mb_params; + + /* Get the MFW response along with the sequence number */ + p_mb_params->mcp_resp = mcp_resp; + + /* Get the MFW param */ + p_mb_params->mcp_param = DRV_MB_RD(p_hwfn, p_ptt, fw_mb_param); + + /* Get the union data */ + if (p_mb_params->p_data_dst != NULL) { + u32 union_data_addr = p_hwfn->mcp_info->drv_mb_addr + + offsetof(struct public_drv_mb, + union_data); + qed_memcpy_from(p_hwfn, p_ptt, p_mb_params->p_data_dst, + union_data_addr, sizeof(union drv_union_data)); + } + + p_cmd_elem->b_is_completed = true; + + return 0; +} + +/* Must be called while cmd_lock is acquired */ +static void __qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_mcp_mb_params *p_mb_params, + u16 seq_num) +{ + union drv_union_data union_data; + u32 union_data_addr; + + /* Set the union data */ + union_data_addr = p_hwfn->mcp_info->drv_mb_addr + + offsetof(struct public_drv_mb, union_data); + memset(&union_data, 0, sizeof(union_data)); + if (p_mb_params->p_data_src != NULL) + memcpy(&union_data, p_mb_params->p_data_src, + sizeof(union_data)); + qed_memcpy_to(p_hwfn, p_ptt, union_data_addr, &union_data, + sizeof(union_data)); + + /* Set the drv param */ + DRV_MB_WR(p_hwfn, p_ptt, drv_mb_param, p_mb_params->param); + + /* Set the drv command along with the sequence number */ + DRV_MB_WR(p_hwfn, p_ptt, drv_mb_header, (p_mb_params->cmd | seq_num)); DP_VERBOSE(p_hwfn, QED_MSG_SP, - "wrote command (%x) to MFW MB param 0x%08x\n", - (cmd | seq), param); + "MFW mailbox: command 0x%08x param 0x%08x\n", + (p_mb_params->cmd | seq_num), p_mb_params->param); +} +static int +_qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_mcp_mb_params *p_mb_params, + u32 max_retries, u32 delay) +{ + struct qed_mcp_cmd_elem *p_cmd_elem; + u32 cnt = 0; + u16 seq_num; + int rc = 0; + + /* Wait until the mailbox is non-occupied */ do { - /* Wait for MFW response */ + /* Exit the loop if there is no pending command, or if the + * pending command is completed during this iteration. + * The spinlock stays locked until the command is sent. + */ + + spin_lock_bh(&p_hwfn->mcp_info->cmd_lock); + + if (!qed_mcp_has_pending_cmd(p_hwfn)) + break; + + rc = qed_mcp_update_pending_cmd(p_hwfn, p_ptt); + if (!rc) + break; + else if (rc != -EAGAIN) + goto err; + + spin_unlock_bh(&p_hwfn->mcp_info->cmd_lock); udelay(delay); - *o_mcp_resp = DRV_MB_RD(p_hwfn, p_ptt, fw_mb_header); + } while (++cnt < max_retries); + + if (cnt >= max_retries) { + DP_NOTICE(p_hwfn, + "The MFW mailbox is occupied by an uncompleted command. Failed to send command 0x%08x [param 0x%08x].\n", + p_mb_params->cmd, p_mb_params->param); + return -EAGAIN; + } - /* Give the FW up to 5 second (500*10ms) */ - } while ((seq != (*o_mcp_resp & FW_MSG_SEQ_NUMBER_MASK)) && - (cnt++ < QED_DRV_MB_MAX_RETRIES)); + /* Send the mailbox command */ + qed_mcp_reread_offsets(p_hwfn, p_ptt); + seq_num = ++p_hwfn->mcp_info->drv_mb_seq; + p_cmd_elem = qed_mcp_cmd_add_elem(p_hwfn, p_mb_params, seq_num); + if (!p_cmd_elem) + goto err; - DP_VERBOSE(p_hwfn, QED_MSG_SP, - "[after %d ms] read (%x) seq is (%x) from FW MB\n", - cnt * delay, *o_mcp_resp, seq); - - /* Is this a reply to our command? */ - if (seq == (*o_mcp_resp & FW_MSG_SEQ_NUMBER_MASK)) { - *o_mcp_resp &= FW_MSG_CODE_MASK; - /* Get the MCP param */ - *o_mcp_param = DRV_MB_RD(p_hwfn, p_ptt, fw_mb_param); - } else { - /* FW BUG! */ - DP_ERR(p_hwfn, "MFW failed to respond [cmd 0x%x param 0x%x]\n", - cmd, param); - *o_mcp_resp = 0; - rc = -EAGAIN; + __qed_mcp_cmd_and_union(p_hwfn, p_ptt, p_mb_params, seq_num); + spin_unlock_bh(&p_hwfn->mcp_info->cmd_lock); + + /* Wait for the MFW response */ + do { + /* Exit the loop if the command is already completed, or if the + * command is completed during this iteration. + * The spinlock stays locked until the list element is removed. + */ + + udelay(delay); + spin_lock_bh(&p_hwfn->mcp_info->cmd_lock); + + if (p_cmd_elem->b_is_completed) + break; + + rc = qed_mcp_update_pending_cmd(p_hwfn, p_ptt); + if (!rc) + break; + else if (rc != -EAGAIN) + goto err; + + spin_unlock_bh(&p_hwfn->mcp_info->cmd_lock); + } while (++cnt < max_retries); + + if (cnt >= max_retries) { + DP_NOTICE(p_hwfn, + "The MFW failed to respond to command 0x%08x [param 0x%08x].\n", + p_mb_params->cmd, p_mb_params->param); + + spin_lock_bh(&p_hwfn->mcp_info->cmd_lock); + qed_mcp_cmd_del_elem(p_hwfn, p_cmd_elem); + spin_unlock_bh(&p_hwfn->mcp_info->cmd_lock); + + return -EAGAIN; } + + qed_mcp_cmd_del_elem(p_hwfn, p_cmd_elem); + spin_unlock_bh(&p_hwfn->mcp_info->cmd_lock); + + DP_VERBOSE(p_hwfn, + QED_MSG_SP, + "MFW mailbox: response 0x%08x param 0x%08x [after %d.%03d ms]\n", + p_mb_params->mcp_resp, + p_mb_params->mcp_param, + (cnt * delay) / 1000, (cnt * delay) % 1000); + + /* Clear the sequence number from the MFW response */ + p_mb_params->mcp_resp &= FW_MSG_CODE_MASK; + + return 0; + +err: + spin_unlock_bh(&p_hwfn->mcp_info->cmd_lock); return rc; } @@ -354,9 +519,8 @@ static int qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, struct qed_mcp_mb_params *p_mb_params) { - u32 union_data_addr; - - int rc; + u32 max_retries = QED_DRV_MB_MAX_RETRIES; + u32 delay = CHIP_MCP_RESP_ITER_US; /* MCP not initialized */ if (!qed_mcp_is_init(p_hwfn)) { @@ -364,33 +528,8 @@ static int qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, return -EBUSY; } - union_data_addr = p_hwfn->mcp_info->drv_mb_addr + - offsetof(struct public_drv_mb, union_data); - - /* Ensure that only a single thread is accessing the mailbox at a - * certain time. - */ - rc = qed_mcp_mb_lock(p_hwfn, p_mb_params->cmd); - if (rc) - return rc; - - if (p_mb_params->p_data_src != NULL) - qed_memcpy_to(p_hwfn, p_ptt, union_data_addr, - p_mb_params->p_data_src, - sizeof(*p_mb_params->p_data_src)); - - rc = qed_do_mcp_cmd(p_hwfn, p_ptt, p_mb_params->cmd, - p_mb_params->param, &p_mb_params->mcp_resp, - &p_mb_params->mcp_param); - - if (p_mb_params->p_data_dst != NULL) - qed_memcpy_from(p_hwfn, p_ptt, p_mb_params->p_data_dst, - union_data_addr, - sizeof(*p_mb_params->p_data_dst)); - - qed_mcp_mb_unlock(p_hwfn, p_mb_params->cmd); - - return rc; + return _qed_mcp_cmd_and_union(p_hwfn, p_ptt, p_mb_params, max_retries, + delay); } int qed_mcp_cmd(struct qed_hwfn *p_hwfn, diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index bdbfd6d4485e..0f7b26818acf 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -484,8 +484,13 @@ int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn, qed_device_num_engines((_p_hwfn)->cdev))) struct qed_mcp_info { - /* Spinlock used for protecting the access to the MFW mailbox */ - spinlock_t lock; + /* List for mailbox commands which were sent and wait for a response */ + struct list_head cmd_list; + + /* Spinlock used for protecting the access to the mailbox commands list + * and the sending of the commands. + */ + spinlock_t cmd_lock; /* Spinlock used for syncing SW link-changes and link-changes * originating from attention context. @@ -505,7 +510,7 @@ struct qed_mcp_info { u8 *mfw_mb_cur; u8 *mfw_mb_shadow; u16 mfw_mb_length; - u16 mcp_hist; + u32 mcp_hist; }; struct qed_mcp_mb_params { -- cgit From 2f67af8c596e16760dc7e7d28ee0edd7ce59f2b4 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Thu, 23 Mar 2017 15:50:16 +0200 Subject: qed: Pass src/dst sizes when interacting with MFW The driver interaction with management firmware involves a union of all the data-members relating to the commands the driver prepares. Current interface assumes the caller always passes such a union - but thats cumbersome as well as risky [chancing a stack corruption in case caller accidentally passes a smaller member instead of union]. Change implementation so that caller could pass a pointer to any of the members instead of the union. Signed-off-by: Tomer Tayar Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_mcp.c | 119 ++++++++++++++++-------------- drivers/net/ethernet/qlogic/qed/qed_mcp.h | 6 +- 2 files changed, 66 insertions(+), 59 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 0d157de83897..8d181025e78c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -368,12 +368,12 @@ qed_mcp_update_pending_cmd(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) p_mb_params->mcp_param = DRV_MB_RD(p_hwfn, p_ptt, fw_mb_param); /* Get the union data */ - if (p_mb_params->p_data_dst != NULL) { + if (p_mb_params->p_data_dst != NULL && p_mb_params->data_dst_size) { u32 union_data_addr = p_hwfn->mcp_info->drv_mb_addr + offsetof(struct public_drv_mb, union_data); qed_memcpy_from(p_hwfn, p_ptt, p_mb_params->p_data_dst, - union_data_addr, sizeof(union drv_union_data)); + union_data_addr, p_mb_params->data_dst_size); } p_cmd_elem->b_is_completed = true; @@ -394,9 +394,9 @@ static void __qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, union_data_addr = p_hwfn->mcp_info->drv_mb_addr + offsetof(struct public_drv_mb, union_data); memset(&union_data, 0, sizeof(union_data)); - if (p_mb_params->p_data_src != NULL) + if (p_mb_params->p_data_src != NULL && p_mb_params->data_src_size) memcpy(&union_data, p_mb_params->p_data_src, - sizeof(union_data)); + p_mb_params->data_src_size); qed_memcpy_to(p_hwfn, p_ptt, union_data_addr, &union_data, sizeof(union_data)); @@ -519,6 +519,7 @@ static int qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, struct qed_mcp_mb_params *p_mb_params) { + size_t union_data_size = sizeof(union drv_union_data); u32 max_retries = QED_DRV_MB_MAX_RETRIES; u32 delay = CHIP_MCP_RESP_ITER_US; @@ -528,6 +529,15 @@ static int qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, return -EBUSY; } + if (p_mb_params->data_src_size > union_data_size || + p_mb_params->data_dst_size > union_data_size) { + DP_ERR(p_hwfn, + "The provided size is larger than the union data size [src_size %u, dst_size %u, union_data_size %zu]\n", + p_mb_params->data_src_size, + p_mb_params->data_dst_size, union_data_size); + return -EINVAL; + } + return _qed_mcp_cmd_and_union(p_hwfn, p_ptt, p_mb_params, max_retries, delay); } @@ -540,11 +550,10 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn, u32 *o_mcp_param) { struct qed_mcp_mb_params mb_params; - union drv_union_data data_src; + struct mcp_mac wol_mac; int rc; memset(&mb_params, 0, sizeof(mb_params)); - memset(&data_src, 0, sizeof(data_src)); mb_params.cmd = cmd; mb_params.param = param; @@ -553,17 +562,18 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn, (p_hwfn->cdev->wol_config == QED_OV_WOL_ENABLED)) { u8 *p_mac = p_hwfn->cdev->wol_mac; - data_src.wol_mac.mac_upper = p_mac[0] << 8 | p_mac[1]; - data_src.wol_mac.mac_lower = p_mac[2] << 24 | p_mac[3] << 16 | - p_mac[4] << 8 | p_mac[5]; + memset(&wol_mac, 0, sizeof(wol_mac)); + wol_mac.mac_upper = p_mac[0] << 8 | p_mac[1]; + wol_mac.mac_lower = p_mac[2] << 24 | p_mac[3] << 16 | + p_mac[4] << 8 | p_mac[5]; DP_VERBOSE(p_hwfn, (QED_MSG_SP | NETIF_MSG_IFDOWN), "Setting WoL MAC: %pM --> [%08x,%08x]\n", - p_mac, data_src.wol_mac.mac_upper, - data_src.wol_mac.mac_lower); + p_mac, wol_mac.mac_upper, wol_mac.mac_lower); - mb_params.p_data_src = &data_src; + mb_params.p_data_src = &wol_mac; + mb_params.data_src_size = sizeof(wol_mac); } rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); @@ -584,13 +594,17 @@ int qed_mcp_nvm_rd_cmd(struct qed_hwfn *p_hwfn, u32 *o_mcp_param, u32 *o_txn_size, u32 *o_buf) { struct qed_mcp_mb_params mb_params; - union drv_union_data union_data; + u8 raw_data[MCP_DRV_NVM_BUF_LEN]; int rc; memset(&mb_params, 0, sizeof(mb_params)); mb_params.cmd = cmd; mb_params.param = param; - mb_params.p_data_dst = &union_data; + mb_params.p_data_dst = raw_data; + + /* Use the maximal value since the actual one is part of the response */ + mb_params.data_dst_size = MCP_DRV_NVM_BUF_LEN; + rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); if (rc) return rc; @@ -599,7 +613,7 @@ int qed_mcp_nvm_rd_cmd(struct qed_hwfn *p_hwfn, *o_mcp_param = mb_params.mcp_param; *o_txn_size = *o_mcp_param; - memcpy(o_buf, &union_data.raw_data, *o_txn_size); + memcpy(o_buf, raw_data, *o_txn_size); return 0; } @@ -619,6 +633,7 @@ int qed_mcp_load_req(struct qed_hwfn *p_hwfn, cdev->drv_type; memcpy(&union_data.ver_str, cdev->ver_str, MCP_DRV_VER_STR_SIZE); mb_params.p_data_src = &union_data; + mb_params.data_src_size = sizeof(union_data.ver_str); rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); /* if mcp fails to respond we must abort */ @@ -688,7 +703,6 @@ int qed_mcp_ack_vf_flr(struct qed_hwfn *p_hwfn, u32 func_addr = SECTION_ADDR(mfw_func_offsize, MCP_PF_ID(p_hwfn)); struct qed_mcp_mb_params mb_params; - union drv_union_data union_data; int rc; int i; @@ -699,8 +713,8 @@ int qed_mcp_ack_vf_flr(struct qed_hwfn *p_hwfn, memset(&mb_params, 0, sizeof(mb_params)); mb_params.cmd = DRV_MSG_CODE_VF_DISABLED_DONE; - memcpy(&union_data.ack_vf_disabled, vfs_to_ack, VF_MAX_STATIC / 8); - mb_params.p_data_src = &union_data; + mb_params.p_data_src = vfs_to_ack; + mb_params.data_src_size = VF_MAX_STATIC / 8; rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); if (rc) { DP_NOTICE(p_hwfn, "Failed to pass ACK for VF flr to MFW\n"); @@ -883,33 +897,31 @@ int qed_mcp_set_link(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, bool b_up) { struct qed_mcp_link_params *params = &p_hwfn->mcp_info->link_input; struct qed_mcp_mb_params mb_params; - union drv_union_data union_data; - struct eth_phy_cfg *phy_cfg; + struct eth_phy_cfg phy_cfg; int rc = 0; u32 cmd; /* Set the shmem configuration according to params */ - phy_cfg = &union_data.drv_phy_cfg; - memset(phy_cfg, 0, sizeof(*phy_cfg)); + memset(&phy_cfg, 0, sizeof(phy_cfg)); cmd = b_up ? DRV_MSG_CODE_INIT_PHY : DRV_MSG_CODE_LINK_RESET; if (!params->speed.autoneg) - phy_cfg->speed = params->speed.forced_speed; - phy_cfg->pause |= (params->pause.autoneg) ? ETH_PAUSE_AUTONEG : 0; - phy_cfg->pause |= (params->pause.forced_rx) ? ETH_PAUSE_RX : 0; - phy_cfg->pause |= (params->pause.forced_tx) ? ETH_PAUSE_TX : 0; - phy_cfg->adv_speed = params->speed.advertised_speeds; - phy_cfg->loopback_mode = params->loopback_mode; + phy_cfg.speed = params->speed.forced_speed; + phy_cfg.pause |= (params->pause.autoneg) ? ETH_PAUSE_AUTONEG : 0; + phy_cfg.pause |= (params->pause.forced_rx) ? ETH_PAUSE_RX : 0; + phy_cfg.pause |= (params->pause.forced_tx) ? ETH_PAUSE_TX : 0; + phy_cfg.adv_speed = params->speed.advertised_speeds; + phy_cfg.loopback_mode = params->loopback_mode; p_hwfn->b_drv_link_init = b_up; if (b_up) { DP_VERBOSE(p_hwfn, NETIF_MSG_LINK, "Configuring Link: Speed 0x%08x, Pause 0x%08x, adv_speed 0x%08x, loopback 0x%08x, features 0x%08x\n", - phy_cfg->speed, - phy_cfg->pause, - phy_cfg->adv_speed, - phy_cfg->loopback_mode, - phy_cfg->feature_config_flags); + phy_cfg.speed, + phy_cfg.pause, + phy_cfg.adv_speed, + phy_cfg.loopback_mode, + phy_cfg.feature_config_flags); } else { DP_VERBOSE(p_hwfn, NETIF_MSG_LINK, "Resetting link\n"); @@ -917,7 +929,8 @@ int qed_mcp_set_link(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, bool b_up) memset(&mb_params, 0, sizeof(mb_params)); mb_params.cmd = cmd; - mb_params.p_data_src = &union_data; + mb_params.p_data_src = &phy_cfg; + mb_params.data_src_size = sizeof(phy_cfg); rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); /* if mcp fails to respond we must abort */ @@ -944,7 +957,6 @@ static void qed_mcp_send_protocol_stats(struct qed_hwfn *p_hwfn, enum qed_mcp_protocol_type stats_type; union qed_mcp_protocol_stats stats; struct qed_mcp_mb_params mb_params; - union drv_union_data union_data; u32 hsi_param; switch (type) { @@ -974,8 +986,8 @@ static void qed_mcp_send_protocol_stats(struct qed_hwfn *p_hwfn, memset(&mb_params, 0, sizeof(mb_params)); mb_params.cmd = DRV_MSG_CODE_GET_STATS; mb_params.param = hsi_param; - memcpy(&union_data, &stats, sizeof(stats)); - mb_params.p_data_src = &union_data; + mb_params.p_data_src = &stats; + mb_params.data_src_size = sizeof(stats); qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); } @@ -1455,24 +1467,23 @@ qed_mcp_send_drv_version(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, struct qed_mcp_drv_version *p_ver) { - struct drv_version_stc *p_drv_version; struct qed_mcp_mb_params mb_params; - union drv_union_data union_data; + struct drv_version_stc drv_version; __be32 val; u32 i; int rc; - p_drv_version = &union_data.drv_version; - p_drv_version->version = p_ver->version; - + memset(&drv_version, 0, sizeof(drv_version)); + drv_version.version = p_ver->version; for (i = 0; i < (MCP_DRV_VER_STR_SIZE - 4) / sizeof(u32); i++) { val = cpu_to_be32(*((u32 *)&p_ver->name[i * sizeof(u32)])); - *(__be32 *)&p_drv_version->name[i * sizeof(u32)] = val; + *(__be32 *)&drv_version.name[i * sizeof(u32)] = val; } memset(&mb_params, 0, sizeof(mb_params)); mb_params.cmd = DRV_MSG_CODE_SET_VERSION; - mb_params.p_data_src = &union_data; + mb_params.p_data_src = &drv_version; + mb_params.data_src_size = sizeof(drv_version); rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); if (rc) DP_ERR(p_hwfn, "MCP response failure, aborting\n"); @@ -1589,7 +1600,6 @@ int qed_mcp_ov_update_mac(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u8 *mac) { struct qed_mcp_mb_params mb_params; - union drv_union_data union_data; int rc; memset(&mb_params, 0, sizeof(mb_params)); @@ -1597,8 +1607,9 @@ int qed_mcp_ov_update_mac(struct qed_hwfn *p_hwfn, mb_params.param = DRV_MSG_CODE_VMAC_TYPE_MAC << DRV_MSG_CODE_VMAC_TYPE_SHIFT; mb_params.param |= MCP_PF_ID(p_hwfn); - ether_addr_copy(&union_data.raw_data[0], mac); - mb_params.p_data_src = &union_data; + + mb_params.p_data_src = mac; + mb_params.data_src_size = 6; rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); if (rc) DP_ERR(p_hwfn, "Failed to send mac address, rc = %d\n", rc); @@ -1876,27 +1887,21 @@ int qed_mcp_get_resc_info(struct qed_hwfn *p_hwfn, u32 *p_mcp_resp, u32 *p_mcp_param) { struct qed_mcp_mb_params mb_params; - union drv_union_data union_data; int rc; memset(&mb_params, 0, sizeof(mb_params)); - memset(&union_data, 0, sizeof(union_data)); mb_params.cmd = DRV_MSG_GET_RESOURCE_ALLOC_MSG; mb_params.param = QED_RESC_ALLOC_VERSION; - /* Need to have a sufficient large struct, as the cmd_and_union - * is going to do memcpy from and to it. - */ - memcpy(&union_data.resource, p_resc_info, sizeof(*p_resc_info)); - - mb_params.p_data_src = &union_data; - mb_params.p_data_dst = &union_data; + mb_params.p_data_src = p_resc_info; + mb_params.data_src_size = sizeof(*p_resc_info); + mb_params.p_data_dst = p_resc_info; + mb_params.data_dst_size = sizeof(*p_resc_info); rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); if (rc) return rc; /* Copy the data back */ - memcpy(p_resc_info, &union_data.resource, sizeof(*p_resc_info)); *p_mcp_resp = mb_params.mcp_resp; *p_mcp_param = mb_params.mcp_param; diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index 0f7b26818acf..f63693d38934 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -516,8 +516,10 @@ struct qed_mcp_info { struct qed_mcp_mb_params { u32 cmd; u32 param; - union drv_union_data *p_data_src; - union drv_union_data *p_data_dst; + void *p_data_src; + u8 data_src_size; + void *p_data_dst; + u8 data_dst_size; u32 mcp_resp; u32 mcp_param; }; -- cgit From 179910020759bffe8d75f0b69e6307c25e4d660d Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Thu, 23 Mar 2017 15:50:17 +0200 Subject: qed: Correct endian order of MAC passed to MFW The management firmware is running on a Big Endian processor, and when running on LE platform HW is configured to swap access to memory shared between management firmware and driver on 32-bit granulariy. As a result, for matters of simplicity most of the APIs between driver and management firmware are based on 32-bit variables. MAC settings are one exception, as driver needs to fill a byte array when indicating to management firmware that primary MAC has changed. Due to the swap, driver must make sure that the mac that was provided in byte-order would be translated into native order, otherwise after the swap the management firmware would read it swapped. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_mcp.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 8d181025e78c..d1fcd874ce9a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -1600,6 +1600,7 @@ int qed_mcp_ov_update_mac(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u8 *mac) { struct qed_mcp_mb_params mb_params; + u32 mfw_mac[2]; int rc; memset(&mb_params, 0, sizeof(mb_params)); @@ -1608,8 +1609,16 @@ int qed_mcp_ov_update_mac(struct qed_hwfn *p_hwfn, DRV_MSG_CODE_VMAC_TYPE_SHIFT; mb_params.param |= MCP_PF_ID(p_hwfn); - mb_params.p_data_src = mac; - mb_params.data_src_size = 6; + /* MCP is BE, and on LE platforms PCI would swap access to SHMEM + * in 32-bit granularity. + * So the MAC has to be set in native order [and not byte order], + * otherwise it would be read incorrectly by MFW after swap. + */ + mfw_mac[0] = mac[0] << 24 | mac[1] << 16 | mac[2] << 8 | mac[3]; + mfw_mac[1] = mac[4] << 24 | mac[5] << 16; + + mb_params.p_data_src = (u8 *)mfw_mac; + mb_params.data_src_size = 8; rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); if (rc) DP_ERR(p_hwfn, "Failed to send mac address, rc = %d\n", rc); -- cgit From 3981594409f3ee9ee7364d3262a22f0f1e504eee Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Thu, 23 Mar 2017 15:50:18 +0200 Subject: qed: Reduce verbosity of unimplemented MFW messages Management firmware and driver are meant to be both backward and forward compatibile with each other. If a new mangement firmware would work with an older driver, it's possible that driver would receive indications which are meaningless to it. That's perfectly acceptible from the firmware part - so no need to log such messages at default verbosity; That would only serve to confuse users. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_mcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index d1fcd874ce9a..ccea0eae7b60 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -1114,7 +1114,7 @@ int qed_mcp_handle_events(struct qed_hwfn *p_hwfn, qed_mcp_update_bw(p_hwfn, p_ptt); break; default: - DP_NOTICE(p_hwfn, "Unimplemented MFW message %d\n", i); + DP_INFO(p_hwfn, "Unimplemented MFW message %d\n", i); rc = -EINVAL; } } -- cgit From 810bb1f0d32e6d1d30580d4812409133ebc6fb8e Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Thu, 23 Mar 2017 15:50:19 +0200 Subject: qed: Don't waste SBs unused by RoCE When RoCE is enabled on a given L2 interface, the interrupt lines are divided equally between L2 and RoCE - But in case number of lines needed for RoCE is limited by number of available CNQs, we can utilize the additional lines for L2. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dev.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 8b5df71aa3c1..6bdac4f91650 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1550,7 +1550,7 @@ static void qed_hw_set_feat(struct qed_hwfn *p_hwfn) { u32 *feat_num = p_hwfn->hw_info.feat_num; struct qed_sb_cnt_info sb_cnt_info; - int num_features = 1; + u32 non_l2_sbs = 0; if (IS_ENABLED(CONFIG_QED_RDMA) && p_hwfn->hw_info.personality == QED_PCI_ETH_ROCE) { @@ -1558,15 +1558,16 @@ static void qed_hw_set_feat(struct qed_hwfn *p_hwfn) * the status blocks equally between L2 / RoCE but with * consideration as to how many l2 queues / cnqs we have. */ - num_features++; - feat_num[QED_RDMA_CNQ] = - min_t(u32, RESC_NUM(p_hwfn, QED_SB) / num_features, + min_t(u32, RESC_NUM(p_hwfn, QED_SB) / 2, RESC_NUM(p_hwfn, QED_RDMA_CNQ_RAM)); + + non_l2_sbs = feat_num[QED_RDMA_CNQ]; } - feat_num[QED_PF_L2_QUE] = min_t(u32, RESC_NUM(p_hwfn, QED_SB) / - num_features, + feat_num[QED_PF_L2_QUE] = min_t(u32, + RESC_NUM(p_hwfn, QED_SB) - + non_l2_sbs, RESC_NUM(p_hwfn, QED_L2_QUEUE)); memset(&sb_cnt_info, 0, sizeof(sb_cnt_info)); @@ -1578,11 +1579,11 @@ static void qed_hw_set_feat(struct qed_hwfn *p_hwfn) DP_VERBOSE(p_hwfn, NETIF_MSG_PROBE, - "#PF_L2_QUEUES=%d VF_L2_QUEUES=%d #ROCE_CNQ=%d #SBS=%d num_features=%d\n", + "#PF_L2_QUEUES=%d VF_L2_QUEUES=%d #ROCE_CNQ=%d #SBS=%d\n", (int)FEAT_NUM(p_hwfn, QED_PF_L2_QUE), (int)FEAT_NUM(p_hwfn, QED_VF_L2_QUE), (int)FEAT_NUM(p_hwfn, QED_RDMA_CNQ), - RESC_NUM(p_hwfn, QED_SB), num_features); + RESC_NUM(p_hwfn, QED_SB)); } static enum resource_id_enum qed_hw_get_mfw_res_id(enum qed_resources res_id) -- cgit From dec26533ae5b00f3f15759bc195cae6fe89a1f42 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Thu, 23 Mar 2017 15:50:20 +0200 Subject: qed: Reserve VF feature before PF Align the driver feature distribution with the flow utilized by the management firmware - first reserve L2 queues for VFs and use all the remaining for the PF. The current distribution might lead to PFs with an enormous amount of queues, but at the same time leave us with insufficient resources for starting all VFs. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dev.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 6bdac4f91650..11e45f0f7779 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1565,17 +1565,22 @@ static void qed_hw_set_feat(struct qed_hwfn *p_hwfn) non_l2_sbs = feat_num[QED_RDMA_CNQ]; } - feat_num[QED_PF_L2_QUE] = min_t(u32, - RESC_NUM(p_hwfn, QED_SB) - - non_l2_sbs, - RESC_NUM(p_hwfn, QED_L2_QUEUE)); - - memset(&sb_cnt_info, 0, sizeof(sb_cnt_info)); - qed_int_get_num_sbs(p_hwfn, &sb_cnt_info); - feat_num[QED_VF_L2_QUE] = - min_t(u32, - RESC_NUM(p_hwfn, QED_L2_QUEUE) - - FEAT_NUM(p_hwfn, QED_PF_L2_QUE), sb_cnt_info.sb_iov_cnt); + if (p_hwfn->hw_info.personality == QED_PCI_ETH_ROCE || + p_hwfn->hw_info.personality == QED_PCI_ETH) { + /* Start by allocating VF queues, then PF's */ + memset(&sb_cnt_info, 0, sizeof(sb_cnt_info)); + qed_int_get_num_sbs(p_hwfn, &sb_cnt_info); + feat_num[QED_VF_L2_QUE] = min_t(u32, + RESC_NUM(p_hwfn, QED_L2_QUEUE), + sb_cnt_info.sb_iov_cnt); + feat_num[QED_PF_L2_QUE] = min_t(u32, + RESC_NUM(p_hwfn, QED_SB) - + non_l2_sbs, + RESC_NUM(p_hwfn, + QED_L2_QUEUE) - + FEAT_NUM(p_hwfn, + QED_VF_L2_QUE)); + } DP_VERBOSE(p_hwfn, NETIF_MSG_PROBE, -- cgit From 6f359f99b8c2ff3b09329611da00fe39a7c10e7e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 20 Mar 2017 09:49:27 +0100 Subject: qedf: fix wrong le16 conversion gcc points out that we are converting a 16-bit integer into a 32-bit little-endian type and assigning that to 16-bit little-endian will end up with a zero: drivers/scsi/qedf/drv_fcoe_fw_funcs.c: In function 'init_initiator_rw_fcoe_task': include/uapi/linux/byteorder/big_endian.h:32:26: error: large integer implicitly truncated to unsigned type [-Werror=overflow] t_st_ctx->read_write.rx_id = cpu_to_le32(FCOE_RX_ID); The correct solution appears to be to just use a 16-bit byte swap instead. Fixes: be086e7c53f1 ("qed*: Utilize Firmware 8.15.3.0") Signed-off-by: Arnd Bergmann Acked-by: Chad Dupuis Signed-off-by: David S. Miller --- drivers/scsi/qedf/drv_fcoe_fw_funcs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/qedf/drv_fcoe_fw_funcs.c b/drivers/scsi/qedf/drv_fcoe_fw_funcs.c index bb812db48da6..8c65e3b034dc 100644 --- a/drivers/scsi/qedf/drv_fcoe_fw_funcs.c +++ b/drivers/scsi/qedf/drv_fcoe_fw_funcs.c @@ -8,7 +8,7 @@ #include "drv_fcoe_fw_funcs.h" #include "drv_scsi_fw_funcs.h" -#define FCOE_RX_ID ((u32)0x0000FFFF) +#define FCOE_RX_ID (0xFFFFu) static inline void init_common_sqe(struct fcoe_task_params *task_params, enum fcoe_sqe_request_type request_type) @@ -59,7 +59,7 @@ int init_initiator_rw_fcoe_task(struct fcoe_task_params *task_params, t_st_ctx->read_only.task_type = task_params->task_type; SET_FIELD(t_st_ctx->read_write.flags, FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_EXP_FIRST_FRAME, 1); - t_st_ctx->read_write.rx_id = cpu_to_le32(FCOE_RX_ID); + t_st_ctx->read_write.rx_id = cpu_to_le16(FCOE_RX_ID); /* Ustorm ctx */ u_ag_ctx = &ctx->ustorm_ag_context; @@ -151,7 +151,7 @@ int init_initiator_midpath_unsolicited_fcoe_task( t_st_ctx->read_only.task_type = task_params->task_type; SET_FIELD(t_st_ctx->read_write.flags, FCOE_TSTORM_FCOE_TASK_ST_CTX_READ_WRITE_EXP_FIRST_FRAME, 1); - t_st_ctx->read_write.rx_id = cpu_to_le32(FCOE_RX_ID); + t_st_ctx->read_write.rx_id = cpu_to_le16(FCOE_RX_ID); /* Init Ustorm */ u_ag_ctx = &ctx->ustorm_ag_context; -- cgit From 91b8270f2a4d1d9b268de90451cdca63a70052d6 Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Wed, 22 Mar 2017 17:27:34 -0700 Subject: Add a helper function to get socket cookie in eBPF Retrieve the socket cookie generated by sock_gen_cookie() from a sk_buff with a known socket. Generates a new cookie if one was not yet set.If the socket pointer inside sk_buff is NULL, 0 is returned. The helper function coud be useful in monitoring per socket networking traffic statistics and provide a unique socket identifier per namespace. Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Acked-by: Willem de Bruijn Signed-off-by: Chenbo Feng Signed-off-by: David S. Miller --- include/linux/sock_diag.h | 1 + include/uapi/linux/bpf.h | 9 ++++++++- net/core/filter.c | 17 +++++++++++++++++ net/core/sock_diag.c | 2 +- tools/include/uapi/linux/bpf.h | 3 ++- 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/include/linux/sock_diag.h b/include/linux/sock_diag.h index a0596ca0e80a..a2f8109bb215 100644 --- a/include/linux/sock_diag.h +++ b/include/linux/sock_diag.h @@ -24,6 +24,7 @@ void sock_diag_unregister(const struct sock_diag_handler *h); void sock_diag_register_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh)); void sock_diag_unregister_inet_compat(int (*fn)(struct sk_buff *skb, struct nlmsghdr *nlh)); +u64 sock_gen_cookie(struct sock *sk); int sock_diag_check_cookie(struct sock *sk, const __u32 *cookie); void sock_diag_save_cookie(struct sock *sk, __u32 *cookie); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index ce6f029ac368..cdfc5595fbc1 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -459,6 +459,12 @@ union bpf_attr { * Return: * > 0 length of the string including the trailing NUL on success * < 0 error + * + * u64 bpf_bpf_get_socket_cookie(skb) + * Get the cookie for the socket stored inside sk_buff. + * @skb: pointer to skb + * Return: 8 Bytes non-decreasing number on success or 0 if the socket + * field is missing inside sk_buff */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -506,7 +512,8 @@ union bpf_attr { FN(get_numa_node_id), \ FN(skb_change_head), \ FN(xdp_adjust_head), \ - FN(probe_read_str), + FN(probe_read_str), \ + FN(get_socket_cookie), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/net/core/filter.c b/net/core/filter.c index c7f0ccd1c0d3..35b0f97c3fdf 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -2606,6 +2607,18 @@ static const struct bpf_func_proto bpf_xdp_event_output_proto = { .arg5_type = ARG_CONST_SIZE, }; +BPF_CALL_1(bpf_get_socket_cookie, struct sk_buff *, skb) +{ + return skb->sk ? sock_gen_cookie(skb->sk) : 0; +} + +static const struct bpf_func_proto bpf_get_socket_cookie_proto = { + .func = bpf_get_socket_cookie, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, +}; + static const struct bpf_func_proto * bpf_base_func_proto(enum bpf_func_id func_id) { @@ -2640,6 +2653,8 @@ sk_filter_func_proto(enum bpf_func_id func_id) switch (func_id) { case BPF_FUNC_skb_load_bytes: return &bpf_skb_load_bytes_proto; + case BPF_FUNC_get_socket_cookie: + return &bpf_get_socket_cookie_proto; default: return bpf_base_func_proto(func_id); } @@ -2699,6 +2714,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id) return &bpf_get_smp_processor_id_proto; case BPF_FUNC_skb_under_cgroup: return &bpf_skb_under_cgroup_proto; + case BPF_FUNC_get_socket_cookie: + return &bpf_get_socket_cookie_proto; default: return bpf_base_func_proto(func_id); } diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index 8d11ee75a100..fb9d0e2fd148 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -19,7 +19,7 @@ static int (*inet_rcv_compat)(struct sk_buff *skb, struct nlmsghdr *nlh); static DEFINE_MUTEX(sock_diag_table_mutex); static struct workqueue_struct *broadcast_wq; -static u64 sock_gen_cookie(struct sock *sk) +u64 sock_gen_cookie(struct sock *sk) { while (1) { u64 res = atomic64_read(&sk->sk_cookie); diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index ce6f029ac368..a3851859e5f3 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -506,7 +506,8 @@ union bpf_attr { FN(get_numa_node_id), \ FN(skb_change_head), \ FN(xdp_adjust_head), \ - FN(probe_read_str), + FN(probe_read_str), \ + FN(get_socket_cookie), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call -- cgit From 6acc5c2910689fc6ee181bf63085c5efff6a42bd Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Wed, 22 Mar 2017 17:27:35 -0700 Subject: Add a eBPF helper function to retrieve socket uid Returns the owner uid of the socket inside a sk_buff. This is useful to perform per-UID accounting of network traffic or per-UID packet filtering. The socket need to be a fullsock otherwise overflowuid is returned. Signed-off-by: Chenbo Feng Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 9 ++++++++- net/core/filter.c | 22 ++++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 3 ++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index cdfc5595fbc1..28317a04c34d 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -465,6 +465,12 @@ union bpf_attr { * @skb: pointer to skb * Return: 8 Bytes non-decreasing number on success or 0 if the socket * field is missing inside sk_buff + * + * u32 bpf_get_socket_uid(skb) + * Get the owner uid of the socket stored inside sk_buff. + * @skb: pointer to skb + * Return: uid of the socket owner on success or 0 if the socket pointer + * inside sk_buff is NULL */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -513,7 +519,8 @@ union bpf_attr { FN(skb_change_head), \ FN(xdp_adjust_head), \ FN(probe_read_str), \ - FN(get_socket_cookie), + FN(get_socket_cookie), \ + FN(get_socket_uid), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/net/core/filter.c b/net/core/filter.c index 35b0f97c3fdf..dfb9f61a2fd5 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2619,6 +2619,24 @@ static const struct bpf_func_proto bpf_get_socket_cookie_proto = { .arg1_type = ARG_PTR_TO_CTX, }; +BPF_CALL_1(bpf_get_socket_uid, struct sk_buff *, skb) +{ + struct sock *sk = sk_to_full_sk(skb->sk); + kuid_t kuid; + + if (!sk || !sk_fullsock(sk)) + return overflowuid; + kuid = sock_net_uid(sock_net(sk), sk); + return from_kuid_munged(sock_net(sk)->user_ns, kuid); +} + +static const struct bpf_func_proto bpf_get_socket_uid_proto = { + .func = bpf_get_socket_uid, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, +}; + static const struct bpf_func_proto * bpf_base_func_proto(enum bpf_func_id func_id) { @@ -2655,6 +2673,8 @@ sk_filter_func_proto(enum bpf_func_id func_id) return &bpf_skb_load_bytes_proto; case BPF_FUNC_get_socket_cookie: return &bpf_get_socket_cookie_proto; + case BPF_FUNC_get_socket_uid: + return &bpf_get_socket_uid_proto; default: return bpf_base_func_proto(func_id); } @@ -2716,6 +2736,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id) return &bpf_skb_under_cgroup_proto; case BPF_FUNC_get_socket_cookie: return &bpf_get_socket_cookie_proto; + case BPF_FUNC_get_socket_uid: + return &bpf_get_socket_uid_proto; default: return bpf_base_func_proto(func_id); } diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index a3851859e5f3..1ea08ce35567 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -507,7 +507,8 @@ union bpf_attr { FN(skb_change_head), \ FN(xdp_adjust_head), \ FN(probe_read_str), \ - FN(get_socket_cookie), + FN(get_socket_cookie), \ + FN(get_socket_uid), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call -- cgit From 51570a5ab2b74a1b929e5a6c25b4df93652ac0aa Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Wed, 22 Mar 2017 17:27:36 -0700 Subject: A Sample of using socket cookie and uid for traffic monitoring Add a sample program to demostrate the possible usage of get_socket_cookie and get_socket_uid helper function. The program will store bytes and packets counting of in/out traffic monitored by iptables and store the stats in a bpf map in per socket base. The owner uid of the socket will be stored as part of the data entry. A shell script for running the program is also included. Acked-by: Alexei Starovoitov Acked-by: Willem de Bruijn Signed-off-by: Chenbo Feng Signed-off-by: David S. Miller --- samples/bpf/Makefile | 3 + samples/bpf/cookie_uid_helper_example.c | 217 +++++++++++++++++++++++++++ samples/bpf/libbpf.h | 10 ++ samples/bpf/run_cookie_uid_helper_example.sh | 14 ++ 4 files changed, 244 insertions(+) create mode 100644 samples/bpf/cookie_uid_helper_example.c create mode 100644 samples/bpf/run_cookie_uid_helper_example.sh diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 91c1d616d975..d42b495b0992 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -35,6 +35,7 @@ hostprogs-y += tc_l2_redirect hostprogs-y += lwt_len_hist hostprogs-y += xdp_tx_iptunnel hostprogs-y += test_map_in_map +hostprogs-y += per_socket_stats_example # Libbpf dependencies LIBBPF := ../../tools/lib/bpf/bpf.o @@ -74,6 +75,7 @@ tc_l2_redirect-objs := bpf_load.o $(LIBBPF) tc_l2_redirect_user.o lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o test_map_in_map-objs := bpf_load.o $(LIBBPF) test_map_in_map_user.o +per_socket_stats_example-objs := $(LIBBPF) cookie_uid_helper_example.o # Tell kbuild to always build the programs always := $(hostprogs-y) @@ -108,6 +110,7 @@ always += sampleip_kern.o always += lwt_len_hist_kern.o always += xdp_tx_iptunnel_kern.o always += test_map_in_map_kern.o +always += cookie_uid_helper_example.o HOSTCFLAGS += -I$(objtree)/usr/include HOSTCFLAGS += -I$(srctree)/tools/lib/ diff --git a/samples/bpf/cookie_uid_helper_example.c b/samples/bpf/cookie_uid_helper_example.c new file mode 100644 index 000000000000..f6e5e58931c5 --- /dev/null +++ b/samples/bpf/cookie_uid_helper_example.c @@ -0,0 +1,217 @@ +/* This test is a demo of using get_socket_uid and get_socket_cookie + * helper function to do per socket based network traffic monitoring. + * It requires iptables version higher then 1.6.1. to load pinned eBPF + * program into the xt_bpf match. + * + * TEST: + * ./run_cookie_uid_helper_example.sh + * Then generate some traffic in variate ways. ping 0 -c 10 would work + * but the cookie and uid in this case could both be 0. A sample output + * with some traffic generated by web browser is shown below: + * + * cookie: 877, uid: 0x3e8, Pakcet Count: 20, Bytes Count: 11058 + * cookie: 132, uid: 0x0, Pakcet Count: 2, Bytes Count: 286 + * cookie: 812, uid: 0x3e8, Pakcet Count: 3, Bytes Count: 1726 + * cookie: 802, uid: 0x3e8, Pakcet Count: 2, Bytes Count: 104 + * cookie: 877, uid: 0x3e8, Pakcet Count: 20, Bytes Count: 11058 + * cookie: 831, uid: 0x3e8, Pakcet Count: 2, Bytes Count: 104 + * cookie: 0, uid: 0x0, Pakcet Count: 6, Bytes Count: 712 + * cookie: 880, uid: 0xfffe, Pakcet Count: 1, Bytes Count: 70 + * + * Clean up: if using shell script, the script file will delete the iptables + * rule and unmount the bpf program when exit. Else the iptables rule need + * to be deleted by hand, see run_cookie_uid_helper_example.sh for detail. + */ + +#define _GNU_SOURCE + +#define offsetof(type, member) __builtin_offsetof(type, member) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libbpf.h" + +struct stats { + uint32_t uid; + uint64_t packets; + uint64_t bytes; +}; + +static int map_fd, prog_fd; + +static void maps_create(void) +{ + map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(uint32_t), + sizeof(struct stats), 100, 0); + if (map_fd < 0) + error(1, errno, "map create failed!\n"); +} + +static void prog_load(void) +{ + static char log_buf[1 << 16]; + + struct bpf_insn prog[] = { + /* + * Save sk_buff for future usage. value stored in R6 to R10 will + * not be reset after a bpf helper function call. + */ + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), + /* + * pc1: BPF_FUNC_get_socket_cookie takes one parameter, + * R1: sk_buff + */ + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_socket_cookie), + /* pc2-4: save &socketCookie to r7 for future usage*/ + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8), + /* + * pc5-8: set up the registers for BPF_FUNC_map_lookup_elem, + * it takes two parameters (R1: map_fd, R2: &socket_cookie) + */ + BPF_LD_MAP_FD(BPF_REG_1, map_fd), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_7), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + /* + * pc9. if r0 != 0x0, go to pc+14, since we have the cookie + * stored already + * Otherwise do pc10-22 to setup a new data entry. + */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 14), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_get_socket_uid), + /* + * Place a struct stats in the R10 stack and sequentially + * place the member value into the memory. Packets value + * is set by directly place a IMM value 1 into the stack. + */ + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, + -32 + offsetof(struct stats, uid)), + BPF_ST_MEM(BPF_DW, BPF_REG_10, + -32 + offsetof(struct stats, packets), 1), + /* + * __sk_buff is a special struct used for eBPF program to + * directly access some sk_buff field. + */ + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, + offsetof(struct __sk_buff, len)), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, + -32 + offsetof(struct stats, bytes)), + /* + * add new map entry using BPF_FUNC_map_update_elem, it takes + * 4 parameters (R1: map_fd, R2: &socket_cookie, R3: &stats, + * R4: flags) + */ + BPF_LD_MAP_FD(BPF_REG_1, map_fd), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_7), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -32), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_update_elem), + BPF_JMP_IMM(BPF_JA, 0, 0, 5), + /* + * pc24-30 update the packet info to a exist data entry, it can + * be done by directly write to pointers instead of using + * BPF_FUNC_map_update_elem helper function + */ + BPF_MOV64_REG(BPF_REG_9, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_1, 1), + BPF_STX_XADD(BPF_DW, BPF_REG_9, BPF_REG_1, + offsetof(struct stats, packets)), + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, + offsetof(struct __sk_buff, len)), + BPF_STX_XADD(BPF_DW, BPF_REG_9, BPF_REG_1, + offsetof(struct stats, bytes)), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, + offsetof(struct __sk_buff, len)), + BPF_EXIT_INSN(), + }; + prog_fd = bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog, + ARRAY_SIZE(prog), "GPL", 0, + log_buf, sizeof(log_buf)); + if (prog_fd < 0) + error(1, errno, "failed to load prog\n%s\n", log_buf); +} + +static void prog_attach_iptables(char *file) +{ + int ret; + char rules[100]; + + if (bpf_obj_pin(prog_fd, file)) + error(1, errno, "bpf_obj_pin"); + if (strlen(file) > 50) { + printf("file path too long: %s\n", file); + exit(1); + } + sprintf(rules, "iptables -A INPUT -m bpf --object-pinned %s -j ACCEPT", + file); + ret = system(rules); + if (ret < 0) { + printf("iptables rule update failed: %d/n", WEXITSTATUS(ret)); + exit(1); + } +} + +static void print_table(void) +{ + struct stats curEntry; + uint32_t curN = UINT32_MAX; + uint32_t nextN, res; + + while (bpf_map_get_next_key(map_fd, &curN, &nextN) > -1) { + curN = nextN; + res = bpf_map_lookup_elem(map_fd, &curN, &curEntry); + if (res < 0) { + error(1, errno, "fail to get entry value of Key: %u\n", + curN); + } else { + printf("cookie: %u, uid: 0x%x, Packet Count: %lu," + " Bytes Count: %lu\n", curN, curEntry.uid, + curEntry.packets, curEntry.bytes); + } + } +} + +int main(int argc, char *argv[]) +{ + if (argc > 2) { + printf("Too many argument provided\n"); + return 1; + } else if (argc < 2) { + printf("Usage: %s bpfObjName\n", argv[0]); + return 1; + } + + maps_create(); + prog_load(); + prog_attach_iptables(argv[1]); + + while (true) { + print_table(); + printf("\n"); + sleep(1); + }; + + return 0; +} diff --git a/samples/bpf/libbpf.h b/samples/bpf/libbpf.h index 3705fba453a0..8ab36a04c174 100644 --- a/samples/bpf/libbpf.h +++ b/samples/bpf/libbpf.h @@ -135,6 +135,16 @@ struct bpf_insn; .off = OFF, \ .imm = 0 }) +/* Atomic memory add, *(uint *)(dst_reg + off16) += src_reg */ + +#define BPF_STX_XADD(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + /* Memory store, *(uint *) (dst_reg + off16) = imm32 */ #define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ diff --git a/samples/bpf/run_cookie_uid_helper_example.sh b/samples/bpf/run_cookie_uid_helper_example.sh new file mode 100644 index 000000000000..40da8aa75c44 --- /dev/null +++ b/samples/bpf/run_cookie_uid_helper_example.sh @@ -0,0 +1,14 @@ +#!/bin/bash +local_dir="$(pwd)" +root_dir=$local_dir/../.. +mnt_dir=$(mktemp -d --tmp) + +on_exit() { + iptables -D INPUT -m bpf --object-pinned ${mnt_dir}/bpf_prog -j ACCEPT + umount ${mnt_dir} + rm -r ${mnt_dir} +} + +trap on_exit EXIT +mount -t bpf bpf ${mnt_dir} +./per_socket_stats_example ${mnt_dir}/bpf_prog -- cgit From ea8c1c642ea501ccdbc31abaff8e624585e43711 Mon Sep 17 00:00:00 2001 From: Jie Deng Date: Thu, 23 Mar 2017 12:03:45 +0800 Subject: net: dwc-xlgmac: declaration of dual license in headers The driver "dwc-xlgmac" is dual-licensed. This patch adds declaration of dual license in file headers. Signed-off-by: Jie Deng Signed-off-by: David S. Miller --- drivers/net/ethernet/synopsys/dwc-xlgmac-common.c | 6 ++---- drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c | 6 ++---- drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c | 6 ++---- drivers/net/ethernet/synopsys/dwc-xlgmac-net.c | 6 ++---- drivers/net/ethernet/synopsys/dwc-xlgmac-pci.c | 6 ++---- drivers/net/ethernet/synopsys/dwc-xlgmac-reg.h | 6 ++---- drivers/net/ethernet/synopsys/dwc-xlgmac.h | 6 ++---- 7 files changed, 14 insertions(+), 28 deletions(-) diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c index b72196ab647f..cb1192815f01 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c @@ -2,10 +2,8 @@ * * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * This program is dual-licensed; you may select either version 2 of + * the GNU General Public License ("GPL") or BSD license ("BSD"). * * This Synopsys DWC XLGMAC software driver and associated documentation * (hereinafter the "Software") is an unsupported proprietary work of diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c index 39b5cb967bba..e9672b1f9968 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-desc.c @@ -2,10 +2,8 @@ * * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * This program is dual-licensed; you may select either version 2 of + * the GNU General Public License ("GPL") or BSD license ("BSD"). * * This Synopsys DWC XLGMAC software driver and associated documentation * (hereinafter the "Software") is an unsupported proprietary work of diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c index 1e25a86f6a27..0dec1dcf8457 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c @@ -2,10 +2,8 @@ * * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * This program is dual-licensed; you may select either version 2 of + * the GNU General Public License ("GPL") or BSD license ("BSD"). * * This Synopsys DWC XLGMAC software driver and associated documentation * (hereinafter the "Software") is an unsupported proprietary work of diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c index 5e8428be3d66..6acf86ced61d 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c @@ -2,10 +2,8 @@ * * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * This program is dual-licensed; you may select either version 2 of + * the GNU General Public License ("GPL") or BSD license ("BSD"). * * This Synopsys DWC XLGMAC software driver and associated documentation * (hereinafter the "Software") is an unsupported proprietary work of diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-pci.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-pci.c index 504e80de7bba..386bafe74c3f 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-pci.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-pci.c @@ -2,10 +2,8 @@ * * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * This program is dual-licensed; you may select either version 2 of + * the GNU General Public License ("GPL") or BSD license ("BSD"). * * This Synopsys DWC XLGMAC software driver and associated documentation * (hereinafter the "Software") is an unsupported proprietary work of diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-reg.h b/drivers/net/ethernet/synopsys/dwc-xlgmac-reg.h index 782448128a89..3754f220567d 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-reg.h +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-reg.h @@ -2,10 +2,8 @@ * * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * This program is dual-licensed; you may select either version 2 of + * the GNU General Public License ("GPL") or BSD license ("BSD"). * * This Synopsys DWC XLGMAC software driver and associated documentation * (hereinafter the "Software") is an unsupported proprietary work of diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac.h b/drivers/net/ethernet/synopsys/dwc-xlgmac.h index 7a4dc643b2b9..676b2fb8dfcc 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac.h +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac.h @@ -2,10 +2,8 @@ * * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * This program is dual-licensed; you may select either version 2 of + * the GNU General Public License ("GPL") or BSD license ("BSD"). * * This Synopsys DWC XLGMAC software driver and associated documentation * (hereinafter the "Software") is an unsupported proprietary work of -- cgit From 67ff2c71bbf21fae0f63b367f252653c4abc3b12 Mon Sep 17 00:00:00 2001 From: Jie Deng Date: Thu, 23 Mar 2017 12:03:46 +0800 Subject: net: dwc-xlgmac: use dual license The driver "dwc-xlgmac" is dual-licensed. Declare the dual license with MODULE_LICENSE(). Signed-off-by: Jie Deng Signed-off-by: David S. Miller --- drivers/net/ethernet/synopsys/dwc-xlgmac-common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c index cb1192815f01..07def2beabfa 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c @@ -21,9 +21,10 @@ #include "dwc-xlgmac.h" #include "dwc-xlgmac-reg.h" +MODULE_LICENSE("Dual BSD/GPL"); + static int debug = -1; module_param(debug, int, 0644); -MODULE_LICENSE("GPL"); MODULE_PARM_DESC(debug, "DWC ethernet debug level (0=none,...,16=all)"); static const u32 default_msg_level = (NETIF_MSG_LINK | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP); -- cgit From add641e7dee31b36aee83412c29e39dd1f5e0c9c Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Thu, 23 Mar 2017 10:39:40 +0100 Subject: sched: act_csum: don't mangle TCP and UDP GSO packets after act_csum computes the checksum on skbs carrying GSO TCP/UDP packets, subsequent segmentation fails because skb_needs_check(skb, true) returns true. Because of that, skb_warn_bad_offload() is invoked and the following message is displayed: WARNING: CPU: 3 PID: 28 at net/core/dev.c:2553 skb_warn_bad_offload+0xf0/0xfd <...> [] skb_warn_bad_offload+0xf0/0xfd [] __skb_gso_segment+0xec/0x110 [] validate_xmit_skb+0x12d/0x2b0 [] validate_xmit_skb_list+0x42/0x70 [] sch_direct_xmit+0xd0/0x1b0 [] __qdisc_run+0x120/0x270 [] __dev_queue_xmit+0x23d/0x690 [] dev_queue_xmit+0x10/0x20 Since GSO is able to compute checksum on individual segments of such skbs, we can simply skip mangling the packet. Signed-off-by: Davide Caratti Signed-off-by: David S. Miller --- net/sched/act_csum.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index e978ccd4402c..6c319a40c1cc 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -181,6 +181,9 @@ static int tcf_csum_ipv4_tcp(struct sk_buff *skb, unsigned int ihl, struct tcphdr *tcph; const struct iphdr *iph; + if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) + return 1; + tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); if (tcph == NULL) return 0; @@ -202,6 +205,9 @@ static int tcf_csum_ipv6_tcp(struct sk_buff *skb, unsigned int ihl, struct tcphdr *tcph; const struct ipv6hdr *ip6h; + if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) + return 1; + tcph = tcf_csum_skb_nextlayer(skb, ihl, ipl, sizeof(*tcph)); if (tcph == NULL) return 0; @@ -225,6 +231,9 @@ static int tcf_csum_ipv4_udp(struct sk_buff *skb, unsigned int ihl, const struct iphdr *iph; u16 ul; + if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP) + return 1; + /* * Support both UDP and UDPLITE checksum algorithms, Don't use * udph->len to get the real length without any protocol check, @@ -278,6 +287,9 @@ static int tcf_csum_ipv6_udp(struct sk_buff *skb, unsigned int ihl, const struct ipv6hdr *ip6h; u16 ul; + if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP) + return 1; + /* * Support both UDP and UDPLITE checksum algorithms, Don't use * udph->len to get the real length without any protocol check, -- cgit From faa16e0f384885e1e13a3e28e688347bf5456a8e Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 7 Mar 2017 15:05:22 -0800 Subject: i40e: correctly honor the mask fields for ETHTOOL_SRXCLSRLINS The current implementation of .set_rxnfc does not properly read the mask field for filter entries. This results in incorrect driver behavior, as we do not reject filters which have masks set to ignore some fields. The current implementation simply assumes that every part of the tuple or "input set" is specified. This results in filters not behaving as expected, and not working correctly. As a first step in supporting some partial filters, add code which checks the mask fields and rejects any filters which do not have an acceptable mask. For now, we just assume that all fields must be set. This will get the driver one step towards allowing some partial filters. At a minimum, the ethtool commands which previously installed filters that would not function will now return a non-zero exit code indicating failure instead. We should now be meeting the minimum requirements of the .set_rxnfc API, by ensuring that all filters we program have a valid mask value for each field. Finally, add code to report the mask correctly so that the ethtool command properly reports the mask to the user. Note that the typecast to (__be16) when checking source and destination port masks is required because the ~ bitwise negation operator does not correctly handle variables other than integer size. Change-Id: Ia020149e07c87aa3fcec7b2283621b887ef0546f Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 83 ++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 1c3805b4fcf3..ac8d5cf720aa 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2409,6 +2409,12 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf, fsp->h_u.tcp_ip4_spec.ip4src = rule->dst_ip; fsp->h_u.tcp_ip4_spec.ip4dst = rule->src_ip; + /* Set the mask fields */ + fsp->m_u.tcp_ip4_spec.psrc = htons(0xFFFF); + fsp->m_u.tcp_ip4_spec.pdst = htons(0xFFFF); + fsp->m_u.tcp_ip4_spec.ip4src = htonl(0xFFFFFFFF); + fsp->m_u.tcp_ip4_spec.ip4dst = htonl(0xFFFFFFFF); + if (rule->dest_ctl == I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET) fsp->ring_cookie = RX_CLS_FLOW_DISC; else @@ -2717,6 +2723,79 @@ static int i40e_del_fdir_entry(struct i40e_vsi *vsi, return ret; } +/** + * i40e_check_fdir_input_set - Check that a given rx_flow_spec mask is valid + * @fsp: pointer to Rx flow specification + * + * Ensures that a given ethtool_rx_flow_spec has a valid mask. + **/ +static int i40e_check_fdir_input_set(struct ethtool_rx_flow_spec *fsp) +{ + struct ethtool_tcpip4_spec *tcp_ip4_spec; + struct ethtool_usrip4_spec *usr_ip4_spec; + + /* Verify the provided mask is valid. */ + switch (fsp->flow_type & ~FLOW_EXT) { + case SCTP_V4_FLOW: + case TCP_V4_FLOW: + case UDP_V4_FLOW: + tcp_ip4_spec = &fsp->m_u.tcp_ip4_spec; + + /* IPv4 source address */ + if (!tcp_ip4_spec->ip4src || ~tcp_ip4_spec->ip4src) + return -EOPNOTSUPP; + + /* IPv4 destination address */ + if (!tcp_ip4_spec->ip4dst || ~tcp_ip4_spec->ip4dst) + return -EOPNOTSUPP; + + /* L4 source port */ + if (!tcp_ip4_spec->psrc || (__be16)~tcp_ip4_spec->psrc) + return -EOPNOTSUPP; + + /* L4 destination port */ + if (!tcp_ip4_spec->pdst || (__be16)~tcp_ip4_spec->pdst) + return -EOPNOTSUPP; + + /* Filtering on Type of Service is not supported. */ + if (tcp_ip4_spec->tos) + return -EOPNOTSUPP; + + break; + case IP_USER_FLOW: + usr_ip4_spec = &fsp->m_u.usr_ip4_spec; + + /* IPv4 source address */ + if (!usr_ip4_spec->ip4src || ~usr_ip4_spec->ip4src) + return -EOPNOTSUPP; + + /* IPv4 destination address */ + if (!usr_ip4_spec->ip4dst || ~usr_ip4_spec->ip4dst) + return -EOPNOTSUPP; + + /* First 4 bytes of L4 header */ + if (!usr_ip4_spec->l4_4_bytes || ~usr_ip4_spec->l4_4_bytes) + return -EOPNOTSUPP; + + /* Filtering on Type of Service is not supported. */ + if (usr_ip4_spec->tos) + return -EOPNOTSUPP; + + /* IP version does not have a mask field. */ + if (usr_ip4_spec->ip_ver) + return -EINVAL; + + /* L4 protocol doesn't have a mask field. */ + if (usr_ip4_spec->proto) + return -EINVAL; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + /** * i40e_add_fdir_ethtool - Add/Remove Flow Director filters * @vsi: pointer to the targeted VSI @@ -2757,6 +2836,10 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, if (fsp->flow_type & FLOW_MAC_EXT) return -EINVAL; + ret = i40e_check_fdir_input_set(fsp); + if (ret) + return ret; + if (fsp->location >= (pf->hw.func_caps.fd_filters_best_effort + pf->hw.func_caps.fd_filters_guaranteed)) { return -EINVAL; -- cgit From 36777d9fa24c1b823f6b4dc3b1ecf9078f182515 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 7 Mar 2017 15:05:23 -0800 Subject: i40e: check current configured input set when adding ntuple filters Do not assume that hardware has been programmed with the default mask, but instead read the input set registers to determine what is currently programmed. This ensures that all programmed filters match exactly how the hardware will interpret them, avoiding confusion regarding filter behavior. This sets the initial ground-work for allowing custom input sets where some fields are disabled. A future patch will fully implement this feature. Instead of using bitwise negation, we'll just explicitly check for the correct value. The use of htonl and htons are used to silence sparse warnings. The compiler should be able to handle the constant value and avoid actually performing a byteswap. Change-Id: I3d8db46cb28ea0afdaac8c5b31a2bfb90e3a4102 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 19 ++++ drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 117 +++++++++++++++++++++---- 2 files changed, 121 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index c0f2286c2b72..0cd21ea48e1d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -734,6 +734,25 @@ static inline int i40e_get_fd_cnt_all(struct i40e_pf *pf) return pf->hw.fdir_shared_filter_count + pf->fdir_pf_filter_count; } +/** + * i40e_read_fd_input_set - reads value of flow director input set register + * @pf: pointer to the PF struct + * @addr: register addr + * + * This function reads value of flow director input set register + * specified by 'addr' (which is specific to flow-type) + **/ +static inline u64 i40e_read_fd_input_set(struct i40e_pf *pf, u16 addr) +{ + u64 val; + + val = i40e_read_rx_ctl(&pf->hw, I40E_PRTQF_FD_INSET(addr, 1)); + val <<= 32; + val += i40e_read_rx_ctl(&pf->hw, I40E_PRTQF_FD_INSET(addr, 0)); + + return val; +} + /* needed by i40e_ethtool.c */ int i40e_up(struct i40e_vsi *vsi); void i40e_down(struct i40e_vsi *vsi); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index ac8d5cf720aa..1815c149040f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2384,6 +2384,8 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf, (struct ethtool_rx_flow_spec *)&cmd->fs; struct i40e_fdir_filter *rule = NULL; struct hlist_node *node2; + u64 input_set; + u16 index; hlist_for_each_entry_safe(rule, node2, &pf->fdir_filter_list, fdir_node) { @@ -2409,11 +2411,42 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf, fsp->h_u.tcp_ip4_spec.ip4src = rule->dst_ip; fsp->h_u.tcp_ip4_spec.ip4dst = rule->src_ip; - /* Set the mask fields */ - fsp->m_u.tcp_ip4_spec.psrc = htons(0xFFFF); - fsp->m_u.tcp_ip4_spec.pdst = htons(0xFFFF); - fsp->m_u.tcp_ip4_spec.ip4src = htonl(0xFFFFFFFF); - fsp->m_u.tcp_ip4_spec.ip4dst = htonl(0xFFFFFFFF); + switch (rule->flow_type) { + case TCP_V4_FLOW: + index = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; + break; + case UDP_V4_FLOW: + index = I40E_FILTER_PCTYPE_NONF_IPV4_UDP; + break; + case IP_USER_FLOW: + index = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER; + break; + default: + /* If we have stored a filter with a flow type not listed here + * it is almost certainly a driver bug. WARN(), and then + * assign the input_set as if all fields are enabled to avoid + * reading unassigned memory. + */ + WARN(1, "Missing input set index for flow_type %d\n", + rule->flow_type); + input_set = 0xFFFFFFFFFFFFFFFFULL; + goto no_input_set; + } + + input_set = i40e_read_fd_input_set(pf, index); + +no_input_set: + if (input_set & I40E_L3_SRC_MASK) + fsp->m_u.tcp_ip4_spec.ip4src = htonl(0xFFFF); + + if (input_set & I40E_L3_DST_MASK) + fsp->m_u.tcp_ip4_spec.ip4dst = htonl(0xFFFF); + + if (input_set & I40E_L4_SRC_MASK) + fsp->m_u.tcp_ip4_spec.psrc = htons(0xFFFFFFFF); + + if (input_set & I40E_L4_DST_MASK) + fsp->m_u.tcp_ip4_spec.pdst = htons(0xFFFFFFFF); if (rule->dest_ctl == I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET) fsp->ring_cookie = RX_CLS_FLOW_DISC; @@ -2725,36 +2758,74 @@ static int i40e_del_fdir_entry(struct i40e_vsi *vsi, /** * i40e_check_fdir_input_set - Check that a given rx_flow_spec mask is valid + * @vsi: pointer to the targeted VSI * @fsp: pointer to Rx flow specification * * Ensures that a given ethtool_rx_flow_spec has a valid mask. **/ -static int i40e_check_fdir_input_set(struct ethtool_rx_flow_spec *fsp) +static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, + struct ethtool_rx_flow_spec *fsp) { + struct i40e_pf *pf = vsi->back; struct ethtool_tcpip4_spec *tcp_ip4_spec; struct ethtool_usrip4_spec *usr_ip4_spec; + u64 current_mask, new_mask; + u16 index; + + switch (fsp->flow_type & ~FLOW_EXT) { + case TCP_V4_FLOW: + index = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; + break; + case UDP_V4_FLOW: + index = I40E_FILTER_PCTYPE_NONF_IPV4_UDP; + break; + case IP_USER_FLOW: + index = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER; + break; + default: + return -EOPNOTSUPP; + } + + /* Read the current input set from register memory. */ + current_mask = i40e_read_fd_input_set(pf, index); + new_mask = current_mask; /* Verify the provided mask is valid. */ switch (fsp->flow_type & ~FLOW_EXT) { - case SCTP_V4_FLOW: case TCP_V4_FLOW: case UDP_V4_FLOW: tcp_ip4_spec = &fsp->m_u.tcp_ip4_spec; /* IPv4 source address */ - if (!tcp_ip4_spec->ip4src || ~tcp_ip4_spec->ip4src) + if (tcp_ip4_spec->ip4src == htonl(0xFFFFFFFF)) + new_mask |= I40E_L3_SRC_MASK; + else if (!tcp_ip4_spec->ip4src) + new_mask &= ~I40E_L3_SRC_MASK; + else return -EOPNOTSUPP; /* IPv4 destination address */ - if (!tcp_ip4_spec->ip4dst || ~tcp_ip4_spec->ip4dst) + if (tcp_ip4_spec->ip4dst == htonl(0xFFFFFFFF)) + new_mask |= I40E_L3_DST_MASK; + else if (!tcp_ip4_spec->ip4dst) + new_mask &= ~I40E_L3_DST_MASK; + else return -EOPNOTSUPP; /* L4 source port */ - if (!tcp_ip4_spec->psrc || (__be16)~tcp_ip4_spec->psrc) + if (tcp_ip4_spec->psrc == htons(0xFFFF)) + new_mask |= I40E_L4_SRC_MASK; + else if (!tcp_ip4_spec->psrc) + new_mask &= ~I40E_L4_SRC_MASK; + else return -EOPNOTSUPP; /* L4 destination port */ - if (!tcp_ip4_spec->pdst || (__be16)~tcp_ip4_spec->pdst) + if (tcp_ip4_spec->pdst == htons(0xFFFF)) + new_mask |= I40E_L4_DST_MASK; + else if (!tcp_ip4_spec->pdst) + new_mask &= ~I40E_L4_DST_MASK; + else return -EOPNOTSUPP; /* Filtering on Type of Service is not supported. */ @@ -2766,15 +2837,27 @@ static int i40e_check_fdir_input_set(struct ethtool_rx_flow_spec *fsp) usr_ip4_spec = &fsp->m_u.usr_ip4_spec; /* IPv4 source address */ - if (!usr_ip4_spec->ip4src || ~usr_ip4_spec->ip4src) + if (usr_ip4_spec->ip4src == htonl(0xFFFFFFFF)) + new_mask |= I40E_L3_SRC_MASK; + else if (!usr_ip4_spec->ip4src) + new_mask &= ~I40E_L3_SRC_MASK; + else return -EOPNOTSUPP; /* IPv4 destination address */ - if (!usr_ip4_spec->ip4dst || ~usr_ip4_spec->ip4dst) + if (usr_ip4_spec->ip4dst == htonl(0xFFFFFFFF)) + new_mask |= I40E_L3_DST_MASK; + else if (!usr_ip4_spec->ip4dst) + new_mask &= ~I40E_L3_DST_MASK; + else return -EOPNOTSUPP; /* First 4 bytes of L4 header */ - if (!usr_ip4_spec->l4_4_bytes || ~usr_ip4_spec->l4_4_bytes) + if (usr_ip4_spec->l4_4_bytes == htonl(0xFFFFFFFF)) + new_mask |= I40E_L4_SRC_MASK | I40E_L4_DST_MASK; + else if (!usr_ip4_spec->l4_4_bytes) + new_mask &= ~(I40E_L4_SRC_MASK | I40E_L4_DST_MASK); + else return -EOPNOTSUPP; /* Filtering on Type of Service is not supported. */ @@ -2788,11 +2871,15 @@ static int i40e_check_fdir_input_set(struct ethtool_rx_flow_spec *fsp) /* L4 protocol doesn't have a mask field. */ if (usr_ip4_spec->proto) return -EINVAL; + break; default: return -EOPNOTSUPP; } + if (new_mask != current_mask) + return -EOPNOTSUPP; + return 0; } @@ -2836,7 +2923,7 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, if (fsp->flow_type & FLOW_MAC_EXT) return -EINVAL; - ret = i40e_check_fdir_input_set(fsp); + ret = i40e_check_fdir_input_set(vsi, fsp); if (ret) return ret; -- cgit From 3bcee1e653c1c1d78485cc7a298b392675a1a56d Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:46 -0800 Subject: i40e: restore default input set for each flow type Ensure that the default input set is correctly reprogrammed when cleaning up after disabling flow director support. This ensures that the programmed value will be in a clean state. Although we do not yet have support for SCTPv4 filters, a future patch will add support for this protocol, so we will correctly restore the SCTPv4 input set here as well. Note that strictly speaking the default hardware value for SCTP includes matching the verification tag. However, the ethtool API does not have support for specifying this value, so there is no reason to keep the verification field enabled. This patch is the next step on the way to enabling partial tuple filters which will be implemented in a following patch. Change-Id: Ic22e1c267ae37518bb036aca4a5694681449f283 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 18 ++++++++++++++++++ drivers/net/ethernet/intel/i40e/i40e_main.c | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 0cd21ea48e1d..95b1a1e7b906 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -753,6 +753,24 @@ static inline u64 i40e_read_fd_input_set(struct i40e_pf *pf, u16 addr) return val; } +/** + * i40e_write_fd_input_set - writes value into flow director input set register + * @pf: pointer to the PF struct + * @addr: register addr + * @val: value to be written + * + * This function writes specified value to the register specified by 'addr'. + * This register is input set register based on flow-type. + **/ +static inline void i40e_write_fd_input_set(struct i40e_pf *pf, + u16 addr, u64 val) +{ + i40e_write_rx_ctl(&pf->hw, I40E_PRTQF_FD_INSET(addr, 1), + (u32)(val >> 32)); + i40e_write_rx_ctl(&pf->hw, I40E_PRTQF_FD_INSET(addr, 0), + (u32)(val & 0xFFFFFFFFULL)); +} + /* needed by i40e_ethtool.c */ int i40e_up(struct i40e_vsi *vsi); void i40e_down(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 caccb8e97f1b..3fbecaa10286 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5759,6 +5759,25 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf) pf->fd_tcp4_filter_cnt = 0; pf->fd_udp4_filter_cnt = 0; pf->fd_ip4_filter_cnt = 0; + + /* Reprogram the default input set for TCP/IPv4 */ + i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_TCP, + I40E_L3_SRC_MASK | I40E_L3_DST_MASK | + I40E_L4_SRC_MASK | I40E_L4_DST_MASK); + + /* Reprogram the default input set for UDP/IPv4 */ + i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_UDP, + I40E_L3_SRC_MASK | I40E_L3_DST_MASK | + I40E_L4_SRC_MASK | I40E_L4_DST_MASK); + + /* Reprogram the default input set for SCTP/IPv4 */ + i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_SCTP, + I40E_L3_SRC_MASK | I40E_L3_DST_MASK | + I40E_L4_SRC_MASK | I40E_L4_DST_MASK); + + /* Reprogram the default input set for Other/IPv4 */ + i40e_write_fd_input_set(pf, I40E_FILTER_PCTYPE_NONF_IPV4_OTHER, + I40E_L3_SRC_MASK | I40E_L3_DST_MASK); } /** -- cgit From 9229e9933471faf211e42bb56b8101621c489841 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:47 -0800 Subject: i40e: allow changing input set for ntuple filters Add support to detect when we can update the input set for each flow type. Because the hardware only supports a single input set for all flows of that matching type, the driver shall only allow the input set to change if there are no other configured filters for that flow type. Thus, the first filter added for each flow type is allowed to change the input set, and all future filters must match the same input set. Display a diagnostic message whenever the filter input set changes, and a warning whenever a filter cannot be accepted because it does not match the configured input set. Change-Id: Ic22e1c267ae37518bb036aca4a5694681449f283 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 148 ++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 1815c149040f..a2ef49084d46 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2756,12 +2756,108 @@ static int i40e_del_fdir_entry(struct i40e_vsi *vsi, return ret; } +/** + * i40e_flow_str - Converts a flow_type into a human readable string + * @flow_type: the flow type from a flow specification + * + * Currently only flow types we support are included here, and the string + * value attempts to match what ethtool would use to configure this flow type. + **/ +static const char *i40e_flow_str(struct ethtool_rx_flow_spec *fsp) +{ + switch (fsp->flow_type & ~FLOW_EXT) { + case TCP_V4_FLOW: + return "tcp4"; + case UDP_V4_FLOW: + return "udp4"; + case SCTP_V4_FLOW: + return "sctp4"; + case IP_USER_FLOW: + return "ip4"; + default: + return "unknown"; + } +} + +/** + * i40e_print_input_set - Show changes between two input sets + * @vsi: the vsi being configured + * @old: the old input set + * @new: the new input set + * + * Print the difference between old and new input sets by showing which series + * of words are toggled on or off. Only displays the bits we actually support + * changing. + **/ +static void i40e_print_input_set(struct i40e_vsi *vsi, u64 old, u64 new) +{ + struct i40e_pf *pf = vsi->back; + bool old_value, new_value; + + old_value = !!(old & I40E_L3_SRC_MASK); + new_value = !!(new & I40E_L3_SRC_MASK); + if (old_value != new_value) + netif_info(pf, drv, vsi->netdev, "L3 source address: %s -> %s\n", + old_value ? "ON" : "OFF", + new_value ? "ON" : "OFF"); + + old_value = !!(old & I40E_L3_DST_MASK); + new_value = !!(new & I40E_L3_DST_MASK); + if (old_value != new_value) + netif_info(pf, drv, vsi->netdev, "L3 destination address: %s -> %s\n", + old_value ? "ON" : "OFF", + new_value ? "ON" : "OFF"); + + old_value = !!(old & I40E_L4_SRC_MASK); + new_value = !!(new & I40E_L4_SRC_MASK); + if (old_value != new_value) + netif_info(pf, drv, vsi->netdev, "L4 source port: %s -> %s\n", + old_value ? "ON" : "OFF", + new_value ? "ON" : "OFF"); + + old_value = !!(old & I40E_L4_DST_MASK); + new_value = !!(new & I40E_L4_DST_MASK); + if (old_value != new_value) + netif_info(pf, drv, vsi->netdev, "L4 destination port: %s -> %s\n", + old_value ? "ON" : "OFF", + new_value ? "ON" : "OFF"); + + old_value = !!(old & I40E_VERIFY_TAG_MASK); + new_value = !!(new & I40E_VERIFY_TAG_MASK); + if (old_value != new_value) + netif_info(pf, drv, vsi->netdev, "SCTP verification tag: %s -> %s\n", + old_value ? "ON" : "OFF", + new_value ? "ON" : "OFF"); + + netif_info(pf, drv, vsi->netdev, " Current input set: %0llx\n", + old); + netif_info(pf, drv, vsi->netdev, "Requested input set: %0llx\n", + new); +} + /** * i40e_check_fdir_input_set - Check that a given rx_flow_spec mask is valid * @vsi: pointer to the targeted VSI * @fsp: pointer to Rx flow specification * - * Ensures that a given ethtool_rx_flow_spec has a valid mask. + * Ensures that a given ethtool_rx_flow_spec has a valid mask. Some support + * for partial matches exists with a few limitations. First, hardware only + * supports masking by word boundary (2 bytes) and not per individual bit. + * Second, hardware is limited to using one mask for a flow type and cannot + * use a separate mask for each filter. + * + * To support these limitations, if we already have a configured filter for + * the specified type, this function enforces that new filters of the type + * match the configured input set. Otherwise, if we do not have a filter of + * the specified type, we allow the input set to be updated to match the + * desired filter. + * + * To help ensure that administrators understand why filters weren't displayed + * as supported, we print a diagnostic message displaying how the input set + * would change and warning to delete the preexisting filters if required. + * + * Returns 0 on successful input set match, and a negative return code on + * failure. **/ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, struct ethtool_rx_flow_spec *fsp) @@ -2770,17 +2866,21 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, struct ethtool_tcpip4_spec *tcp_ip4_spec; struct ethtool_usrip4_spec *usr_ip4_spec; u64 current_mask, new_mask; + u16 *fdir_filter_count; u16 index; switch (fsp->flow_type & ~FLOW_EXT) { case TCP_V4_FLOW: index = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; + fdir_filter_count = &pf->fd_tcp4_filter_cnt; break; case UDP_V4_FLOW: index = I40E_FILTER_PCTYPE_NONF_IPV4_UDP; + fdir_filter_count = &pf->fd_udp4_filter_cnt; break; case IP_USER_FLOW: index = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER; + fdir_filter_count = &pf->fd_ip4_filter_cnt; break; default: return -EOPNOTSUPP; @@ -2790,7 +2890,15 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, current_mask = i40e_read_fd_input_set(pf, index); new_mask = current_mask; - /* Verify the provided mask is valid. */ + /* Determine, if any, the required changes to the input set in order + * to support the provided mask. + * + * Hardware only supports masking at word (2 byte) granularity and does + * not support full bitwise masking. This implementation simplifies + * even further and only supports fully enabled or fully disabled + * masks for each field, even though we could split the ip4src and + * ip4dst fields. + */ switch (fsp->flow_type & ~FLOW_EXT) { case TCP_V4_FLOW: case UDP_V4_FLOW: @@ -2877,8 +2985,42 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, return -EOPNOTSUPP; } - if (new_mask != current_mask) + /* If the input set doesn't need any changes then this filter is safe + * to apply. + */ + if (new_mask == current_mask) + return 0; + + netif_info(pf, drv, vsi->netdev, "Input set change requested for %s flows:\n", + i40e_flow_str(fsp)); + i40e_print_input_set(vsi, current_mask, new_mask); + + /* Hardware input sets are global across multiple ports, so even the + * main port cannot change them when in MFP mode as this would impact + * any filters on the other ports. + */ + if (pf->flags & I40E_FLAG_MFP_ENABLED) { + netif_err(pf, drv, vsi->netdev, "Cannot change Flow Director input sets while MFP is enabled\n"); + return -EOPNOTSUPP; + } + + /* This filter requires us to update the input set. However, hardware + * only supports one input set per flow type, and does not support + * separate masks for each filter. This means that we can only support + * a single mask for all filters of a specific type. + * + * If we have preexisting filters, they obviously depend on the + * current programmed input set. Display a diagnostic message in this + * case explaining why the filter could not be accepted. + */ + if (*fdir_filter_count) { + netif_err(pf, drv, vsi->netdev, "Cannot change input set for %s flows until %d preexisting filters are removed\n", + i40e_flow_str(fsp), + *fdir_filter_count); return -EOPNOTSUPP; + } + + i40e_write_fd_input_set(pf, index, new_mask); return 0; } -- cgit From 43b15697a3993bf24b0190bccec369d314bc8c36 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:48 -0800 Subject: i40e: partition the ring_cookie to get VF index Do not use the user-def field for determining the VF target. Instead, similar to ixgbe, partition the ring_cookie value into 8bits of VF index, along with 32bits of queue number. This is better than using the user-def field, because it leaves the field open for extension in a future patch which will enable flexible data. Also, this matches with convention used by ixgbe and other drivers. Change-Id: Ie36745186d817216b12f0313b99ec95cb8a9130c Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 74 ++++++++++++++------------ 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index a2ef49084d46..44b4a2fe9fb6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2458,8 +2458,13 @@ no_input_set: vsi = i40e_find_vsi_from_id(pf, rule->dest_vsi); if (vsi && vsi->type == I40E_VSI_SRIOV) { - fsp->h_ext.data[1] = htonl(vsi->vf_id); - fsp->m_ext.data[1] = htonl(0x1); + /* VFs are zero-indexed by the driver, but ethtool + * expects them to be one-indexed, so add one here + */ + u64 ring_vf = vsi->vf_id + 1; + + ring_vf <<= ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF; + fsp->ring_cookie |= ring_vf; } } @@ -3038,9 +3043,10 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, { struct ethtool_rx_flow_spec *fsp; struct i40e_fdir_filter *input; + u16 dest_vsi = 0, q_index = 0; struct i40e_pf *pf; int ret = -EINVAL; - u16 vf_id; + u8 dest_ctl; if (!vsi) return -EINVAL; @@ -3074,9 +3080,32 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, return -EINVAL; } - if ((fsp->ring_cookie != RX_CLS_FLOW_DISC) && - (fsp->ring_cookie >= vsi->num_queue_pairs)) - return -EINVAL; + /* ring_cookie is either the drop index, or is a mask of the queue + * index and VF id we wish to target. + */ + if (fsp->ring_cookie == RX_CLS_FLOW_DISC) { + dest_ctl = I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET; + } else { + u32 ring = ethtool_get_flow_spec_ring(fsp->ring_cookie); + u8 vf = ethtool_get_flow_spec_ring_vf(fsp->ring_cookie); + + if (!vf) { + if (ring >= vsi->num_queue_pairs) + return -EINVAL; + dest_vsi = vsi->id; + } else { + /* VFs are zero-indexed, so we subtract one here */ + vf--; + + if (vf >= pf->num_alloc_vfs) + return -EINVAL; + if (ring >= pf->vf[vf].num_queue_pairs) + return -EINVAL; + dest_vsi = pf->vf[vf].lan_vsi_id; + } + dest_ctl = I40E_FILTER_PROGRAM_DESC_DEST_DIRECT_PACKET_QINDEX; + q_index = ring; + } input = kzalloc(sizeof(*input), GFP_KERNEL); @@ -3084,19 +3113,13 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, return -ENOMEM; input->fd_id = fsp->location; - - if (fsp->ring_cookie == RX_CLS_FLOW_DISC) - input->dest_ctl = I40E_FILTER_PROGRAM_DESC_DEST_DROP_PACKET; - else - input->dest_ctl = - I40E_FILTER_PROGRAM_DESC_DEST_DIRECT_PACKET_QINDEX; - - input->q_index = fsp->ring_cookie; - input->flex_off = 0; - input->pctype = 0; - input->dest_vsi = vsi->id; + input->q_index = q_index; + input->dest_vsi = dest_vsi; + input->dest_ctl = dest_ctl; input->fd_status = I40E_FILTER_PROGRAM_DESC_FD_STATUS_FD_ID; input->cnt_index = I40E_FD_SB_STAT_IDX(pf->hw.pf_id); + input->dst_ip = fsp->h_u.tcp_ip4_spec.ip4src; + input->src_ip = fsp->h_u.tcp_ip4_spec.ip4dst; input->flow_type = fsp->flow_type; input->ip4_proto = fsp->h_u.usr_ip4_spec.proto; @@ -3108,23 +3131,6 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, input->dst_ip = fsp->h_u.tcp_ip4_spec.ip4src; input->src_ip = fsp->h_u.tcp_ip4_spec.ip4dst; - if (ntohl(fsp->m_ext.data[1])) { - vf_id = ntohl(fsp->h_ext.data[1]); - if (vf_id >= pf->num_alloc_vfs) { - netif_info(pf, drv, vsi->netdev, - "Invalid VF id %d\n", vf_id); - goto free_input; - } - /* Find vsi id from vf id and override dest vsi */ - input->dest_vsi = pf->vf[vf_id].lan_vsi_id; - if (input->q_index >= pf->vf[vf_id].num_queue_pairs) { - netif_info(pf, drv, vsi->netdev, - "Invalid queue id %d for VF %d\n", - input->q_index, vf_id); - goto free_input; - } - } - ret = i40e_add_del_fdir(vsi, input, true); if (ret) goto free_input; -- cgit From e793095e8a576836da96ec6fc1d6064328d95929 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:49 -0800 Subject: i40e: add parsing of flexible filter fields from userdef Add code to parse the user-def field into a data structure format. This code is intended to allow future extensions of the user-def field by keeping all code that actually reads and writes the field into a single location. This ensures that we do not litter the driver with references to the user-def field and minimizes the amount of bitwise operations we need to do on the data. Add code which parses the lower 32bits into a flexible word and its offset. This will be used in a future patch to enable flexible filters which can match on some arbitrary data in the packet payload. For now, we just return -EOPNOTSUPP when this is used. Add code to fill in the user-def field when reporting the filter back, even though we don't actually implement any user-def fields yet. Additionally, ensure that we mask the extended FLOW_EXT bit from the flow_type now that we will be accepting filters which have the FLOW_EXT bit set (and thus make use of the user-def field). Change-Id: I238845035c179380a347baa8db8223304f5f6dd7 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 9 ++ drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 110 ++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 95b1a1e7b906..3bf0e7ed4eda 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -202,6 +202,15 @@ enum i40e_fd_stat_idx { #define I40E_FD_ATR_TUNNEL_STAT_IDX(pf_id) \ (I40E_FD_STAT_PF_IDX(pf_id) + I40E_FD_STAT_ATR_TUNNEL) +/* The following structure contains the data parsed from the user-defined + * field of the ethtool_rx_flow_spec structure. + */ +struct i40e_rx_flow_userdef { + bool flex_filter; + u16 flex_word; + u16 flex_offset; +}; + struct i40e_fdir_filter { struct hlist_node fdir_node; /* filter ipnut set */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 44b4a2fe9fb6..f04a06d0dbf8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2331,6 +2331,102 @@ static int i40e_get_rss_hash_opts(struct i40e_pf *pf, struct ethtool_rxnfc *cmd) return 0; } +/** + * i40e_check_mask - Check whether a mask field is set + * @mask: the full mask value + * @field; mask of the field to check + * + * If the given mask is fully set, return positive value. If the mask for the + * field is fully unset, return zero. Otherwise return a negative error code. + **/ +static int i40e_check_mask(u64 mask, u64 field) +{ + u64 value = mask & field; + + if (value == field) + return 1; + else if (!value) + return 0; + else + return -1; +} + +/** + * i40e_parse_rx_flow_user_data - Deconstruct user-defined data + * @fsp: pointer to rx flow specification + * @data: pointer to userdef data structure for storage + * + * Read the user-defined data and deconstruct the value into a structure. No + * other code should read the user-defined data, so as to ensure that every + * place consistently reads the value correctly. + * + * The user-defined field is a 64bit Big Endian format value, which we + * deconstruct by reading bits or bit fields from it. Single bit flags shall + * be defined starting from the highest bits, while small bit field values + * shall be defined starting from the lowest bits. + * + * Returns 0 if the data is valid, and non-zero if the userdef data is invalid + * and the filter should be rejected. The data structure will always be + * modified even if FLOW_EXT is not set. + * + **/ +static int i40e_parse_rx_flow_user_data(struct ethtool_rx_flow_spec *fsp, + struct i40e_rx_flow_userdef *data) +{ + u64 value, mask; + int valid; + + /* Zero memory first so it's always consistent. */ + memset(data, 0, sizeof(*data)); + + if (!(fsp->flow_type & FLOW_EXT)) + return 0; + + value = be64_to_cpu(*((__be64 *)fsp->h_ext.data)); + mask = be64_to_cpu(*((__be64 *)fsp->m_ext.data)); + +#define I40E_USERDEF_FLEX_WORD GENMASK_ULL(15, 0) +#define I40E_USERDEF_FLEX_OFFSET GENMASK_ULL(31, 16) +#define I40E_USERDEF_FLEX_FILTER GENMASK_ULL(31, 0) + + valid = i40e_check_mask(mask, I40E_USERDEF_FLEX_FILTER); + if (valid < 0) { + return -EINVAL; + } else if (valid) { + data->flex_word = value & I40E_USERDEF_FLEX_WORD; + data->flex_offset = + (value & I40E_USERDEF_FLEX_OFFSET) >> 16; + data->flex_filter = true; + } + + return 0; +} + +/** + * i40e_fill_rx_flow_user_data - Fill in user-defined data field + * @fsp: pointer to rx_flow specification + * + * Reads the userdef data structure and properly fills in the user defined + * fields of the rx_flow_spec. + **/ +static void i40e_fill_rx_flow_user_data(struct ethtool_rx_flow_spec *fsp, + struct i40e_rx_flow_userdef *data) +{ + u64 value = 0, mask = 0; + + if (data->flex_filter) { + value |= data->flex_word; + value |= (u64)data->flex_offset << 16; + mask |= I40E_USERDEF_FLEX_FILTER; + } + + if (value || mask) + fsp->flow_type |= FLOW_EXT; + + *((__be64 *)fsp->h_ext.data) = cpu_to_be64(value); + *((__be64 *)fsp->m_ext.data) = cpu_to_be64(mask); +} + /** * i40e_get_ethtool_fdir_all - Populates the rule count of a command * @pf: Pointer to the physical function struct @@ -2382,6 +2478,7 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf, { struct ethtool_rx_flow_spec *fsp = (struct ethtool_rx_flow_spec *)&cmd->fs; + struct i40e_rx_flow_userdef userdef = {0}; struct i40e_fdir_filter *rule = NULL; struct hlist_node *node2; u64 input_set; @@ -2468,6 +2565,8 @@ no_input_set: } } + i40e_fill_rx_flow_user_data(fsp, &userdef); + return 0; } @@ -3041,6 +3140,7 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, struct ethtool_rxnfc *cmd) { + struct i40e_rx_flow_userdef userdef; struct ethtool_rx_flow_spec *fsp; struct i40e_fdir_filter *input; u16 dest_vsi = 0, q_index = 0; @@ -3067,6 +3167,14 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, fsp = (struct ethtool_rx_flow_spec *)&cmd->fs; + /* Parse the user-defined field */ + if (i40e_parse_rx_flow_user_data(fsp, &userdef)) + return -EINVAL; + + /* Flexible filters not yet supported */ + if (userdef.flex_filter) + return -EOPNOTSUPP; + /* Extended MAC field is not supported */ if (fsp->flow_type & FLOW_MAC_EXT) return -EINVAL; @@ -3120,7 +3228,7 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, input->cnt_index = I40E_FD_SB_STAT_IDX(pf->hw.pf_id); input->dst_ip = fsp->h_u.tcp_ip4_spec.ip4src; input->src_ip = fsp->h_u.tcp_ip4_spec.ip4dst; - input->flow_type = fsp->flow_type; + input->flow_type = fsp->flow_type & ~FLOW_EXT; input->ip4_proto = fsp->h_u.usr_ip4_spec.proto; /* Reverse the src and dest notion, since the HW expects them to be from -- cgit From 0e588de17f086c32432d6ca7f4053b37c6fc487c Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:50 -0800 Subject: i40e: implement support for flexible word payload Add support for flexible payloads passed via ethtool user-def field. This support is somewhat limited due to hardware design. The input set can only be programmed once per filter type, and the flexible offset is part of this filter input set. This means that the user cannot program both a regular and a flexible filter at the same time for a given flow type. Additionally, the user may not program two flexible filters of the same flow type with different offsets, although they are allowed to configure different values at that offset location. We support a single flexible word (2byte) value per protocol type, and we handle the FLX_PIT register using a list of flexible entries so that each flow type may be configured separately. Due to hardware implementation, the flexible data is offset from the start of the packet payload, and thus may not be in part of the header data. For this reason, the offset provided by the user defined data is interpreted as a byte offset from the start of the matching payload. Previous implementations have tried to represent the offset as from the start of the frame, but this is not feasible because header sizes may change due to options. Change-Id: 36ed27995e97de63f9aea5ade5778ff038d6f811 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 83 ++++ drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 499 ++++++++++++++++++++++++- drivers/net/ethernet/intel/i40e/i40e_main.c | 16 + drivers/net/ethernet/intel/i40e/i40e_txrx.c | 27 ++ 4 files changed, 613 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 3bf0e7ed4eda..72c4a740b432 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -222,6 +222,12 @@ struct i40e_fdir_filter { __be16 src_port; __be16 dst_port; __be32 sctp_v_tag; + + /* Flexible data to match within the packet payload */ + __be16 flex_word; + u16 flex_offset; + bool flex_filter; + /* filter control */ u16 q_index; u8 flex_off; @@ -258,6 +264,75 @@ struct i40e_udp_port_config { u8 type; }; +/* macros related to FLX_PIT */ +#define I40E_FLEX_SET_FSIZE(fsize) (((fsize) << \ + I40E_PRTQF_FLX_PIT_FSIZE_SHIFT) & \ + I40E_PRTQF_FLX_PIT_FSIZE_MASK) +#define I40E_FLEX_SET_DST_WORD(dst) (((dst) << \ + I40E_PRTQF_FLX_PIT_DEST_OFF_SHIFT) & \ + I40E_PRTQF_FLX_PIT_DEST_OFF_MASK) +#define I40E_FLEX_SET_SRC_WORD(src) (((src) << \ + I40E_PRTQF_FLX_PIT_SOURCE_OFF_SHIFT) & \ + I40E_PRTQF_FLX_PIT_SOURCE_OFF_MASK) +#define I40E_FLEX_PREP_VAL(dst, fsize, src) (I40E_FLEX_SET_DST_WORD(dst) | \ + I40E_FLEX_SET_FSIZE(fsize) | \ + I40E_FLEX_SET_SRC_WORD(src)) + +#define I40E_FLEX_PIT_GET_SRC(flex) (((flex) & \ + I40E_PRTQF_FLX_PIT_SOURCE_OFF_MASK) >> \ + I40E_PRTQF_FLX_PIT_SOURCE_OFF_SHIFT) +#define I40E_FLEX_PIT_GET_DST(flex) (((flex) & \ + I40E_PRTQF_FLX_PIT_DEST_OFF_MASK) >> \ + I40E_PRTQF_FLX_PIT_DEST_OFF_SHIFT) +#define I40E_FLEX_PIT_GET_FSIZE(flex) (((flex) & \ + I40E_PRTQF_FLX_PIT_FSIZE_MASK) >> \ + I40E_PRTQF_FLX_PIT_FSIZE_SHIFT) + +#define I40E_MAX_FLEX_SRC_OFFSET 0x1F + +/* macros related to GLQF_ORT */ +#define I40E_ORT_SET_IDX(idx) (((idx) << \ + I40E_GLQF_ORT_PIT_INDX_SHIFT) & \ + I40E_GLQF_ORT_PIT_INDX_MASK) + +#define I40E_ORT_SET_COUNT(count) (((count) << \ + I40E_GLQF_ORT_FIELD_CNT_SHIFT) & \ + I40E_GLQF_ORT_FIELD_CNT_MASK) + +#define I40E_ORT_SET_PAYLOAD(payload) (((payload) << \ + I40E_GLQF_ORT_FLX_PAYLOAD_SHIFT) & \ + I40E_GLQF_ORT_FLX_PAYLOAD_MASK) + +#define I40E_ORT_PREP_VAL(idx, count, payload) (I40E_ORT_SET_IDX(idx) | \ + I40E_ORT_SET_COUNT(count) | \ + I40E_ORT_SET_PAYLOAD(payload)) + +#define I40E_L3_GLQF_ORT_IDX 34 +#define I40E_L4_GLQF_ORT_IDX 35 + +/* Flex PIT register index */ +#define I40E_FLEX_PIT_IDX_START_L2 0 +#define I40E_FLEX_PIT_IDX_START_L3 3 +#define I40E_FLEX_PIT_IDX_START_L4 6 + +#define I40E_FLEX_PIT_TABLE_SIZE 3 + +#define I40E_FLEX_DEST_UNUSED 63 + +#define I40E_FLEX_INDEX_ENTRIES 8 + +/* Flex MASK to disable all flexible entries */ +#define I40E_FLEX_INPUT_MASK (I40E_FLEX_50_MASK | I40E_FLEX_51_MASK | \ + I40E_FLEX_52_MASK | I40E_FLEX_53_MASK | \ + I40E_FLEX_54_MASK | I40E_FLEX_55_MASK | \ + I40E_FLEX_56_MASK | I40E_FLEX_57_MASK) + +struct i40e_flex_pit { + struct list_head list; + u16 src_offset; + u8 pit_index; +}; + /* struct that defines the Ethernet device */ struct i40e_pf { struct pci_dev *pdev; @@ -304,6 +379,14 @@ struct i40e_pf { u16 fd_udp4_filter_cnt; u16 fd_ip4_filter_cnt; + /* Flexible filter table values that need to be programmed into + * hardware, which expects L3 and L4 to be programmed separately. We + * need to ensure that the values are in ascended order and don't have + * duplicates, so we track each L3 and L4 values in separate lists. + */ + struct list_head l3_flex_pit_list; + struct list_head l4_flex_pit_list; + struct i40e_udp_port_config udp_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS]; u16 pending_udp_bitmap; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index f04a06d0dbf8..93b7854c220d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2565,6 +2565,12 @@ no_input_set: } } + if (rule->flex_filter) { + userdef.flex_filter = true; + userdef.flex_word = be16_to_cpu(rule->flex_word); + userdef.flex_offset = rule->flex_offset; + } + i40e_fill_rx_flow_user_data(fsp, &userdef); return 0; @@ -2829,6 +2835,69 @@ static int i40e_update_ethtool_fdir_entry(struct i40e_vsi *vsi, return 0; } +/** + * i40e_prune_flex_pit_list - Cleanup unused entries in FLX_PIT table + * @pf: pointer to PF structure + * + * This function searches the list of filters and determines which FLX_PIT + * entries are still required. It will prune any entries which are no longer + * in use after the deletion. + **/ +static void i40e_prune_flex_pit_list(struct i40e_pf *pf) +{ + struct i40e_flex_pit *entry, *tmp; + struct i40e_fdir_filter *rule; + + /* First, we'll check the l3 table */ + list_for_each_entry_safe(entry, tmp, &pf->l3_flex_pit_list, list) { + bool found = false; + + hlist_for_each_entry(rule, &pf->fdir_filter_list, fdir_node) { + if (rule->flow_type != IP_USER_FLOW) + continue; + if (rule->flex_filter && + rule->flex_offset == entry->src_offset) { + found = true; + break; + } + } + + /* If we didn't find the filter, then we can prune this entry + * from the list. + */ + if (!found) { + list_del(&entry->list); + kfree(entry); + } + } + + /* Followed by the L4 table */ + list_for_each_entry_safe(entry, tmp, &pf->l4_flex_pit_list, list) { + bool found = false; + + hlist_for_each_entry(rule, &pf->fdir_filter_list, fdir_node) { + /* Skip this filter if it's L3, since we already + * checked those in the above loop + */ + if (rule->flow_type == IP_USER_FLOW) + continue; + if (rule->flex_filter && + rule->flex_offset == entry->src_offset) { + found = true; + break; + } + } + + /* If we didn't find the filter, then we can prune this entry + * from the list. + */ + if (!found) { + list_del(&entry->list); + kfree(entry); + } + } +} + /** * i40e_del_fdir_entry - Deletes a Flow Director filter entry * @vsi: Pointer to the targeted VSI @@ -2856,10 +2925,252 @@ static int i40e_del_fdir_entry(struct i40e_vsi *vsi, ret = i40e_update_ethtool_fdir_entry(vsi, NULL, fsp->location, cmd); + i40e_prune_flex_pit_list(pf); + i40e_fdir_check_and_reenable(pf); return ret; } +/** + * i40e_unused_pit_index - Find an unused PIT index for given list + * @pf: the PF data structure + * + * Find the first unused flexible PIT index entry. We search both the L3 and + * L4 flexible PIT lists so that the returned index is unique and unused by + * either currently programmed L3 or L4 filters. We use a bit field as storage + * to track which indexes are already used. + **/ +static u8 i40e_unused_pit_index(struct i40e_pf *pf) +{ + unsigned long available_index = 0xFF; + struct i40e_flex_pit *entry; + + /* We need to make sure that the new index isn't in use by either L3 + * or L4 filters so that IP_USER_FLOW filters can program both L3 and + * L4 to use the same index. + */ + + list_for_each_entry(entry, &pf->l4_flex_pit_list, list) + clear_bit(entry->pit_index, &available_index); + + list_for_each_entry(entry, &pf->l3_flex_pit_list, list) + clear_bit(entry->pit_index, &available_index); + + return find_first_bit(&available_index, 8); +} + +/** + * i40e_find_flex_offset - Find an existing flex src_offset + * @flex_pit_list: L3 or L4 flex PIT list + * @src_offset: new src_offset to find + * + * Searches the flex_pit_list for an existing offset. If no offset is + * currently programmed, then this will return an ERR_PTR if there is no space + * to add a new offset, otherwise it returns NULL. + **/ +static +struct i40e_flex_pit *i40e_find_flex_offset(struct list_head *flex_pit_list, + u16 src_offset) +{ + struct i40e_flex_pit *entry; + int size = 0; + + /* Search for the src_offset first. If we find a matching entry + * already programmed, we can simply re-use it. + */ + list_for_each_entry(entry, flex_pit_list, list) { + size++; + if (entry->src_offset == src_offset) + return entry; + } + + /* If we haven't found an entry yet, then the provided src offset has + * not yet been programmed. We will program the src offset later on, + * but we need to indicate whether there is enough space to do so + * here. We'll make use of ERR_PTR for this purpose. + */ + if (size >= I40E_FLEX_PIT_TABLE_SIZE) + return ERR_PTR(-ENOSPC); + + return NULL; +} + +/** + * i40e_add_flex_offset - Add src_offset to flex PIT table list + * @flex_pit_list: L3 or L4 flex PIT list + * @src_offset: new src_offset to add + * @pit_index: the PIT index to program + * + * This function programs the new src_offset to the list. It is expected that + * i40e_find_flex_offset has already been tried and returned NULL, indicating + * that this offset is not programmed, and that the list has enough space to + * store another offset. + * + * Returns 0 on success, and negative value on error. + **/ +static int i40e_add_flex_offset(struct list_head *flex_pit_list, + u16 src_offset, + u8 pit_index) +{ + struct i40e_flex_pit *new_pit, *entry; + + new_pit = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!new_pit) + return -ENOMEM; + + new_pit->src_offset = src_offset; + new_pit->pit_index = pit_index; + + /* We need to insert this item such that the list is sorted by + * src_offset in ascending order. + */ + list_for_each_entry(entry, flex_pit_list, list) { + if (new_pit->src_offset < entry->src_offset) { + list_add_tail(&new_pit->list, &entry->list); + return 0; + } + + /* If we found an entry with our offset already programmed we + * can simply return here, after freeing the memory. However, + * if the pit_index does not match we need to report an error. + */ + if (new_pit->src_offset == entry->src_offset) { + int err = 0; + + /* If the PIT index is not the same we can't re-use + * the entry, so we must report an error. + */ + if (new_pit->pit_index != entry->pit_index) + err = -EINVAL; + + kfree(new_pit); + return err; + } + } + + /* If we reached here, then we haven't yet added the item. This means + * that we should add the item at the end of the list. + */ + list_add_tail(&new_pit->list, flex_pit_list); + return 0; +} + +/** + * __i40e_reprogram_flex_pit - Re-program specific FLX_PIT table + * @pf: Pointer to the PF structure + * @flex_pit_list: list of flexible src offsets in use + * #flex_pit_start: index to first entry for this section of the table + * + * In order to handle flexible data, the hardware uses a table of values + * called the FLX_PIT table. This table is used to indicate which sections of + * the input correspond to what PIT index values. Unfortunately, hardware is + * very restrictive about programming this table. Entries must be ordered by + * src_offset in ascending order, without duplicates. Additionally, unused + * entries must be set to the unused index value, and must have valid size and + * length according to the src_offset ordering. + * + * This function will reprogram the FLX_PIT register from a book-keeping + * structure that we guarantee is already ordered correctly, and has no more + * than 3 entries. + * + * To make things easier, we only support flexible values of one word length, + * rather than allowing variable length flexible values. + **/ +static void __i40e_reprogram_flex_pit(struct i40e_pf *pf, + struct list_head *flex_pit_list, + int flex_pit_start) +{ + struct i40e_flex_pit *entry = NULL; + u16 last_offset = 0; + int i = 0, j = 0; + + /* First, loop over the list of flex PIT entries, and reprogram the + * registers. + */ + list_for_each_entry(entry, flex_pit_list, list) { + /* We have to be careful when programming values for the + * largest SRC_OFFSET value. It is possible that adding + * additional empty values at the end would overflow the space + * for the SRC_OFFSET in the FLX_PIT register. To avoid this, + * we check here and add the empty values prior to adding the + * largest value. + * + * To determine this, we will use a loop from i+1 to 3, which + * will determine whether the unused entries would have valid + * SRC_OFFSET. Note that there cannot be extra entries past + * this value, because the only valid values would have been + * larger than I40E_MAX_FLEX_SRC_OFFSET, and thus would not + * have been added to the list in the first place. + */ + for (j = i + 1; j < 3; j++) { + u16 offset = entry->src_offset + j; + int index = flex_pit_start + i; + u32 value = I40E_FLEX_PREP_VAL(I40E_FLEX_DEST_UNUSED, + 1, + offset - 3); + + if (offset > I40E_MAX_FLEX_SRC_OFFSET) { + i40e_write_rx_ctl(&pf->hw, + I40E_PRTQF_FLX_PIT(index), + value); + i++; + } + } + + /* Now, we can program the actual value into the table */ + i40e_write_rx_ctl(&pf->hw, + I40E_PRTQF_FLX_PIT(flex_pit_start + i), + I40E_FLEX_PREP_VAL(entry->pit_index + 50, + 1, + entry->src_offset)); + i++; + } + + /* In order to program the last entries in the table, we need to + * determine the valid offset. If the list is empty, we'll just start + * with 0. Otherwise, we'll start with the last item offset and add 1. + * This ensures that all entries have valid sizes. If we don't do this + * correctly, the hardware will disable flexible field parsing. + */ + if (!list_empty(flex_pit_list)) + last_offset = list_prev_entry(entry, list)->src_offset + 1; + + for (; i < 3; i++, last_offset++) { + i40e_write_rx_ctl(&pf->hw, + I40E_PRTQF_FLX_PIT(flex_pit_start + i), + I40E_FLEX_PREP_VAL(I40E_FLEX_DEST_UNUSED, + 1, + last_offset)); + } +} + +/** + * i40e_reprogram_flex_pit - Reprogram all FLX_PIT tables after input set change + * @pf: pointer to the PF structure + * + * This function reprograms both the L3 and L4 FLX_PIT tables. See the + * internal helper function for implementation details. + **/ +static void i40e_reprogram_flex_pit(struct i40e_pf *pf) +{ + __i40e_reprogram_flex_pit(pf, &pf->l3_flex_pit_list, + I40E_FLEX_PIT_IDX_START_L3); + + __i40e_reprogram_flex_pit(pf, &pf->l4_flex_pit_list, + I40E_FLEX_PIT_IDX_START_L4); + + /* We also need to program the L3 and L4 GLQF ORT register */ + i40e_write_rx_ctl(&pf->hw, + I40E_GLQF_ORT(I40E_L3_GLQF_ORT_IDX), + I40E_ORT_PREP_VAL(I40E_FLEX_PIT_IDX_START_L3, + 3, 1)); + + i40e_write_rx_ctl(&pf->hw, + I40E_GLQF_ORT(I40E_L4_GLQF_ORT_IDX), + I40E_ORT_PREP_VAL(I40E_FLEX_PIT_IDX_START_L4, + 3, 1)); +} + /** * i40e_flow_str - Converts a flow_type into a human readable string * @flow_type: the flow type from a flow specification @@ -2883,6 +3194,37 @@ static const char *i40e_flow_str(struct ethtool_rx_flow_spec *fsp) } } +/** + * i40e_pit_index_to_mask - Return the FLEX mask for a given PIT index + * @pit_index: PIT index to convert + * + * Returns the mask for a given PIT index. Will return 0 if the pit_index is + * of range. + **/ +static u64 i40e_pit_index_to_mask(int pit_index) +{ + switch (pit_index) { + case 0: + return I40E_FLEX_50_MASK; + case 1: + return I40E_FLEX_51_MASK; + case 2: + return I40E_FLEX_52_MASK; + case 3: + return I40E_FLEX_53_MASK; + case 4: + return I40E_FLEX_54_MASK; + case 5: + return I40E_FLEX_55_MASK; + case 6: + return I40E_FLEX_56_MASK; + case 7: + return I40E_FLEX_57_MASK; + default: + return 0; + } +} + /** * i40e_print_input_set - Show changes between two input sets * @vsi: the vsi being configured @@ -2897,6 +3239,7 @@ static void i40e_print_input_set(struct i40e_vsi *vsi, u64 old, u64 new) { struct i40e_pf *pf = vsi->back; bool old_value, new_value; + int i; old_value = !!(old & I40E_L3_SRC_MASK); new_value = !!(new & I40E_L3_SRC_MASK); @@ -2933,6 +3276,19 @@ static void i40e_print_input_set(struct i40e_vsi *vsi, u64 old, u64 new) old_value ? "ON" : "OFF", new_value ? "ON" : "OFF"); + /* Show change of flexible filter entries */ + for (i = 0; i < I40E_FLEX_INDEX_ENTRIES; i++) { + u64 flex_mask = i40e_pit_index_to_mask(i); + + old_value = !!(old & flex_mask); + new_value = !!(new & flex_mask); + if (old_value != new_value) + netif_info(pf, drv, vsi->netdev, "FLEX index %d: %s -> %s\n", + i, + old_value ? "ON" : "OFF", + new_value ? "ON" : "OFF"); + } + netif_info(pf, drv, vsi->netdev, " Current input set: %0llx\n", old); netif_info(pf, drv, vsi->netdev, "Requested input set: %0llx\n", @@ -2943,6 +3299,7 @@ static void i40e_print_input_set(struct i40e_vsi *vsi, u64 old, u64 new) * i40e_check_fdir_input_set - Check that a given rx_flow_spec mask is valid * @vsi: pointer to the targeted VSI * @fsp: pointer to Rx flow specification + * @userdef: userdefined data from flow specification * * Ensures that a given ethtool_rx_flow_spec has a valid mask. Some support * for partial matches exists with a few limitations. First, hardware only @@ -2964,14 +3321,19 @@ static void i40e_print_input_set(struct i40e_vsi *vsi, u64 old, u64 new) * failure. **/ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, - struct ethtool_rx_flow_spec *fsp) + struct ethtool_rx_flow_spec *fsp, + struct i40e_rx_flow_userdef *userdef) { struct i40e_pf *pf = vsi->back; struct ethtool_tcpip4_spec *tcp_ip4_spec; struct ethtool_usrip4_spec *usr_ip4_spec; u64 current_mask, new_mask; + bool new_flex_offset = false; + bool flex_l3 = false; u16 *fdir_filter_count; - u16 index; + u16 index, src_offset = 0; + u8 pit_index = 0; + int err; switch (fsp->flow_type & ~FLOW_EXT) { case TCP_V4_FLOW: @@ -2985,6 +3347,7 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, case IP_USER_FLOW: index = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER; fdir_filter_count = &pf->fd_ip4_filter_cnt; + flex_l3 = true; break; default: return -EOPNOTSUPP; @@ -3076,11 +3439,11 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, if (usr_ip4_spec->tos) return -EOPNOTSUPP; - /* IP version does not have a mask field. */ + /* Filtering on IP version is not supported */ if (usr_ip4_spec->ip_ver) return -EINVAL; - /* L4 protocol doesn't have a mask field. */ + /* Filtering on L4 protocol is not supported */ if (usr_ip4_spec->proto) return -EINVAL; @@ -3089,15 +3452,107 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, return -EOPNOTSUPP; } - /* If the input set doesn't need any changes then this filter is safe - * to apply. + /* First, clear all flexible filter entries */ + new_mask &= ~I40E_FLEX_INPUT_MASK; + + /* If we have a flexible filter, try to add this offset to the correct + * flexible filter PIT list. Once finished, we can update the mask. + * If the src_offset changed, we will get a new mask value which will + * trigger an input set change. + */ + if (userdef->flex_filter) { + struct i40e_flex_pit *l3_flex_pit = NULL, *flex_pit = NULL; + + /* Flexible offset must be even, since the flexible payload + * must be aligned on 2-byte boundary. + */ + if (userdef->flex_offset & 0x1) { + dev_warn(&pf->pdev->dev, + "Flexible data offset must be 2-byte aligned\n"); + return -EINVAL; + } + + src_offset = userdef->flex_offset >> 1; + + /* FLX_PIT source offset value is only so large */ + if (src_offset > I40E_MAX_FLEX_SRC_OFFSET) { + dev_warn(&pf->pdev->dev, + "Flexible data must reside within first 64 bytes of the packet payload\n"); + return -EINVAL; + } + + /* See if this offset has already been programmed. If we get + * an ERR_PTR, then the filter is not safe to add. Otherwise, + * if we get a NULL pointer, this means we will need to add + * the offset. + */ + flex_pit = i40e_find_flex_offset(&pf->l4_flex_pit_list, + src_offset); + if (IS_ERR(flex_pit)) + return PTR_ERR(flex_pit); + + /* IP_USER_FLOW filters match both L4 (ICMP) and L3 (unknown) + * packet types, and thus we need to program both L3 and L4 + * flexible values. These must have identical flexible index, + * as otherwise we can't correctly program the input set. So + * we'll find both an L3 and L4 index and make sure they are + * the same. + */ + if (flex_l3) { + l3_flex_pit = + i40e_find_flex_offset(&pf->l3_flex_pit_list, + src_offset); + if (IS_ERR(l3_flex_pit)) + return PTR_ERR(l3_flex_pit); + + if (flex_pit) { + /* If we already had a matching L4 entry, we + * need to make sure that the L3 entry we + * obtained uses the same index. + */ + if (l3_flex_pit) { + if (l3_flex_pit->pit_index != + flex_pit->pit_index) { + return -EINVAL; + } + } else { + new_flex_offset = true; + } + } else { + flex_pit = l3_flex_pit; + } + } + + /* If we didn't find an existing flex offset, we need to + * program a new one. However, we don't immediately program it + * here because we will wait to program until after we check + * that it is safe to change the input set. + */ + if (!flex_pit) { + new_flex_offset = true; + pit_index = i40e_unused_pit_index(pf); + } else { + pit_index = flex_pit->pit_index; + } + + /* Update the mask with the new offset */ + new_mask |= i40e_pit_index_to_mask(pit_index); + } + + /* If the mask and flexible filter offsets for this filter match the + * currently programmed values we don't need any input set change, so + * this filter is safe to install. */ - if (new_mask == current_mask) + if (new_mask == current_mask && !new_flex_offset) return 0; netif_info(pf, drv, vsi->netdev, "Input set change requested for %s flows:\n", i40e_flow_str(fsp)); i40e_print_input_set(vsi, current_mask, new_mask); + if (new_flex_offset) { + netif_info(pf, drv, vsi->netdev, "FLEX index %d: Offset -> %d", + pit_index, src_offset); + } /* Hardware input sets are global across multiple ports, so even the * main port cannot change them when in MFP mode as this would impact @@ -3126,6 +3581,24 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, i40e_write_fd_input_set(pf, index, new_mask); + /* Add the new offset and update table, if necessary */ + if (new_flex_offset) { + err = i40e_add_flex_offset(&pf->l4_flex_pit_list, src_offset, + pit_index); + if (err) + return err; + + if (flex_l3) { + err = i40e_add_flex_offset(&pf->l3_flex_pit_list, + src_offset, + pit_index); + if (err) + return err; + } + + i40e_reprogram_flex_pit(pf); + } + return 0; } @@ -3171,15 +3644,11 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, if (i40e_parse_rx_flow_user_data(fsp, &userdef)) return -EINVAL; - /* Flexible filters not yet supported */ - if (userdef.flex_filter) - return -EOPNOTSUPP; - /* Extended MAC field is not supported */ if (fsp->flow_type & FLOW_MAC_EXT) return -EINVAL; - ret = i40e_check_fdir_input_set(vsi, fsp); + ret = i40e_check_fdir_input_set(vsi, fsp, &userdef); if (ret) return ret; @@ -3239,6 +3708,12 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, input->dst_ip = fsp->h_u.tcp_ip4_spec.ip4src; input->src_ip = fsp->h_u.tcp_ip4_spec.ip4dst; + if (userdef.flex_filter) { + input->flex_filter = true; + input->flex_word = cpu_to_be16(userdef.flex_word); + input->flex_offset = userdef.flex_offset; + } + ret = i40e_add_del_fdir(vsi, input, true); if (ret) goto free_input; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 3fbecaa10286..7f8b929c90bf 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5747,6 +5747,7 @@ err_setup_tx: static void i40e_fdir_filter_exit(struct i40e_pf *pf) { struct i40e_fdir_filter *filter; + struct i40e_flex_pit *pit_entry, *tmp; struct hlist_node *node2; hlist_for_each_entry_safe(filter, node2, @@ -5755,6 +5756,18 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf) kfree(filter); } + list_for_each_entry_safe(pit_entry, tmp, &pf->l3_flex_pit_list, list) { + list_del(&pit_entry->list); + kfree(pit_entry); + } + INIT_LIST_HEAD(&pf->l3_flex_pit_list); + + list_for_each_entry_safe(pit_entry, tmp, &pf->l4_flex_pit_list, list) { + list_del(&pit_entry->list); + kfree(pit_entry); + } + INIT_LIST_HEAD(&pf->l4_flex_pit_list); + pf->fdir_pf_active_filters = 0; pf->fd_tcp4_filter_cnt = 0; pf->fd_udp4_filter_cnt = 0; @@ -11144,6 +11157,9 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) hw->bus.bus_id = pdev->bus->number; pf->instance = pfs_found; + INIT_LIST_HEAD(&pf->l3_flex_pit_list); + INIT_LIST_HEAD(&pf->l4_flex_pit_list); + /* set up the locks for the AQ, do this only once in probe * and destroy them only once in remove */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 3880e417f167..855ae1e359df 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -71,6 +71,9 @@ static void i40e_fdir(struct i40e_ring *tx_ring, flex_ptype |= I40E_TXD_FLTR_QW0_PCTYPE_MASK & (fdata->pctype << I40E_TXD_FLTR_QW0_PCTYPE_SHIFT); + flex_ptype |= I40E_TXD_FLTR_QW0_PCTYPE_MASK & + (fdata->flex_offset << I40E_TXD_FLTR_QW0_FLEXOFF_SHIFT); + /* Use LAN VSI Id if not programmed by user */ flex_ptype |= I40E_TXD_FLTR_QW0_DEST_VSI_MASK & ((u32)(fdata->dest_vsi ? : pf->vsi[pf->lan_vsi]->id) << @@ -223,6 +226,14 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi, ip->saddr = fd_data->src_ip; udp->source = fd_data->src_port; + if (fd_data->flex_filter) { + u8 *payload = raw_packet + I40E_UDPIP_DUMMY_PACKET_LEN; + __be16 pattern = fd_data->flex_word; + u16 off = fd_data->flex_offset; + + *((__force __be16 *)(payload + off)) = pattern; + } + fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP; ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); if (ret) { @@ -289,6 +300,14 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, ip->saddr = fd_data->src_ip; tcp->source = fd_data->src_port; + if (fd_data->flex_filter) { + u8 *payload = raw_packet + I40E_TCPIP_DUMMY_PACKET_LEN; + __be16 pattern = fd_data->flex_word; + u16 off = fd_data->flex_offset; + + *((__force __be16 *)(payload + off)) = pattern; + } + fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); if (ret) { @@ -362,6 +381,14 @@ static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi, ip->daddr = fd_data->dst_ip; ip->protocol = 0; + if (fd_data->flex_filter) { + u8 *payload = raw_packet + I40E_IP_DUMMY_PACKET_LEN; + __be16 pattern = fd_data->flex_word; + u16 off = fd_data->flex_offset; + + *((__force __be16 *)(payload + off)) = pattern; + } + fd_data->pctype = i; ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); if (ret) { -- cgit From f223c8752a0b756b82ad8f077172054548a6d644 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:51 -0800 Subject: i40e: add support for SCTPv4 FDir filters Enable FDir filters for SCTPv4 packets using the ethtool ntuple interface to enable filters. The ethtool API does not allow masking on the verification tag. Change-Id: I093e88a8143994c7e6f4b7b17a0bd5cf861d18e4 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 + drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 10 ++++ drivers/net/ethernet/intel/i40e/i40e_main.c | 2 + drivers/net/ethernet/intel/i40e/i40e_txrx.c | 80 ++++++++++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 72c4a740b432..3133a1a8b8b3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -377,6 +377,7 @@ struct i40e_pf { */ u16 fd_tcp4_filter_cnt; u16 fd_udp4_filter_cnt; + u16 fd_sctp4_filter_cnt; u16 fd_ip4_filter_cnt; /* Flexible filter table values that need to be programmed into diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 93b7854c220d..8fac124ebcb5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2509,6 +2509,9 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf, fsp->h_u.tcp_ip4_spec.ip4dst = rule->src_ip; switch (rule->flow_type) { + case SCTP_V4_FLOW: + index = I40E_FILTER_PCTYPE_NONF_IPV4_SCTP; + break; case TCP_V4_FLOW: index = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; break; @@ -3336,6 +3339,10 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, int err; switch (fsp->flow_type & ~FLOW_EXT) { + case SCTP_V4_FLOW: + index = I40E_FILTER_PCTYPE_NONF_IPV4_SCTP; + fdir_filter_count = &pf->fd_sctp4_filter_cnt; + break; case TCP_V4_FLOW: index = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; fdir_filter_count = &pf->fd_tcp4_filter_cnt; @@ -3367,6 +3374,9 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi, * ip4dst fields. */ switch (fsp->flow_type & ~FLOW_EXT) { + case SCTP_V4_FLOW: + new_mask &= ~I40E_VERIFY_TAG_MASK; + /* Fall through */ case TCP_V4_FLOW: case UDP_V4_FLOW: tcp_ip4_spec = &fsp->m_u.tcp_ip4_spec; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 7f8b929c90bf..114481b67ad8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3286,6 +3286,7 @@ static void i40e_fdir_filter_restore(struct i40e_vsi *vsi) /* Reset FDir counters as we're replaying all existing filters */ pf->fd_tcp4_filter_cnt = 0; pf->fd_udp4_filter_cnt = 0; + pf->fd_sctp4_filter_cnt = 0; pf->fd_ip4_filter_cnt = 0; hlist_for_each_entry_safe(filter, node, @@ -5771,6 +5772,7 @@ static void i40e_fdir_filter_exit(struct i40e_pf *pf) pf->fdir_pf_active_filters = 0; pf->fd_tcp4_filter_cnt = 0; pf->fd_udp4_filter_cnt = 0; + pf->fd_sctp4_filter_cnt = 0; pf->fd_ip4_filter_cnt = 0; /* Reprogram the default input set for TCP/IPv4 */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 855ae1e359df..0ca307a6c731 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -346,6 +346,80 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, return 0; } +#define I40E_SCTPIP_DUMMY_PACKET_LEN 46 +/** + * i40e_add_del_fdir_sctpv4 - Add/Remove SCTPv4 Flow Director filters for + * a specific flow spec + * @vsi: pointer to the targeted VSI + * @fd_data: the flow director data required for the FDir descriptor + * @add: true adds a filter, false removes it + * + * Returns 0 if the filters were successfully added or removed + **/ +static int i40e_add_del_fdir_sctpv4(struct i40e_vsi *vsi, + struct i40e_fdir_filter *fd_data, + bool add) +{ + struct i40e_pf *pf = vsi->back; + struct sctphdr *sctp; + struct iphdr *ip; + u8 *raw_packet; + int ret; + /* Dummy packet */ + static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0, + 0x45, 0, 0, 0x20, 0, 0, 0x40, 0, 0x40, 0x84, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE, GFP_KERNEL); + if (!raw_packet) + return -ENOMEM; + memcpy(raw_packet, packet, I40E_SCTPIP_DUMMY_PACKET_LEN); + + ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET); + sctp = (struct sctphdr *)(raw_packet + IP_HEADER_OFFSET + + sizeof(struct iphdr)); + + ip->daddr = fd_data->dst_ip; + sctp->dest = fd_data->dst_port; + ip->saddr = fd_data->src_ip; + sctp->source = fd_data->src_port; + + if (fd_data->flex_filter) { + u8 *payload = raw_packet + I40E_SCTPIP_DUMMY_PACKET_LEN; + __be16 pattern = fd_data->flex_word; + u16 off = fd_data->flex_offset; + + *((__force __be16 *)(payload + off)) = pattern; + } + + fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_SCTP; + ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add); + if (ret) { + dev_info(&pf->pdev->dev, + "PCTYPE:%d, Filter command send failed for fd_id:%d (ret = %d)\n", + fd_data->pctype, fd_data->fd_id, ret); + /* Free the packet buffer since it wasn't added to the ring */ + kfree(raw_packet); + return -EOPNOTSUPP; + } else if (I40E_DEBUG_FD & pf->hw.debug_mask) { + if (add) + dev_info(&pf->pdev->dev, + "Filter OK for PCTYPE %d loc = %d\n", + fd_data->pctype, fd_data->fd_id); + else + dev_info(&pf->pdev->dev, + "Filter deleted for PCTYPE %d loc = %d\n", + fd_data->pctype, fd_data->fd_id); + } + + if (add) + pf->fd_sctp4_filter_cnt++; + else + pf->fd_sctp4_filter_cnt--; + + return 0; +} + #define I40E_IP_DUMMY_PACKET_LEN 34 /** * i40e_add_del_fdir_ipv4 - Add/Remove IPv4 Flow Director filters for @@ -440,6 +514,9 @@ int i40e_add_del_fdir(struct i40e_vsi *vsi, case UDP_V4_FLOW: ret = i40e_add_del_fdir_udpv4(vsi, input, add); break; + case SCTP_V4_FLOW: + ret = i40e_add_del_fdir_sctpv4(vsi, input, add); + break; case IP_USER_FLOW: switch (input->ip4_proto) { case IPPROTO_TCP: @@ -448,6 +525,9 @@ int i40e_add_del_fdir(struct i40e_vsi *vsi, case IPPROTO_UDP: ret = i40e_add_del_fdir_udpv4(vsi, input, add); break; + case IPPROTO_SCTP: + ret = i40e_add_del_fdir_sctpv4(vsi, input, add); + break; case IPPROTO_IP: ret = i40e_add_del_fdir_ipv4(vsi, input, add); break; -- cgit From 55877012d5588ce7427919d6b869922f1a5f60bc Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 6 Feb 2017 14:38:52 -0800 Subject: i40e: document drivers use of ntuple filters Add documentation describing the drivers use of ethtool ntuple filters, including the limitations that it has due to hardware, as well as how it reads and parses the user-def data block. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- Documentation/networking/i40e.txt | 72 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/Documentation/networking/i40e.txt b/Documentation/networking/i40e.txt index a251bf4fe9c9..57e616ed10b0 100644 --- a/Documentation/networking/i40e.txt +++ b/Documentation/networking/i40e.txt @@ -63,6 +63,78 @@ Additional Configurations The latest release of ethtool can be found from https://www.kernel.org/pub/software/network/ethtool + + Flow Director n-ntuple traffic filters (FDir) + --------------------------------------------- + The driver utilizes the ethtool interface for configuring ntuple filters, + via "ethtool -N ". + + The sctp4, ip4, udp4, and tcp4 flow types are supported with the standard + fields including src-ip, dst-ip, src-port and dst-port. The driver only + supports fully enabling or fully masking the fields, so use of the mask + fields for partial matches is not supported. + + Additionally, the driver supports using the action to specify filters for a + Virtual Function. You can specify the action as a 64bit value, where the + lower 32 bits represents the queue number, while the next 8 bits represent + which VF. Note that 0 is the PF, so the VF identifier is offset by 1. For + example: + + ... action 0x800000002 ... + + Would indicate to direct traffic for Virtual Function 7 (8 minus 1) on queue + 2 of that VF. + + The driver also supports using the user-defined field to specify 2 bytes of + arbitrary data to match within the packet payload in addition to the regular + fields. The data is specified in the lower 32bits of the user-def field in + the following way: + + +----------------------------+---------------------------+ + | 31 28 24 20 16 | 15 12 8 4 0| + +----------------------------+---------------------------+ + | offset into packet payload | 2 bytes of flexible data | + +----------------------------+---------------------------+ + + As an example, + + ... user-def 0x4FFFF .... + + means to match the value 0xFFFF 4 bytes into the packet payload. Note that + the offset is based on the beginning of the payload, and not the beginning + of the packet. Thus + + flow-type tcp4 ... user-def 0x8BEAF .... + + would match TCP/IPv4 packets which have the value 0xBEAF 8bytes into the + TCP/IPv4 payload. + + For ICMP, the hardware parses the ICMP header as 4 bytes of header and 4 + bytes of payload, so if you want to match an ICMP frames payload you may need + to add 4 to the offset in order to match the data. + + Furthermore, the offset can only be up to a value of 64, as the hardware + will only read up to 64 bytes of data from the payload. It must also be even + as the flexible data is 2 bytes long and must be aligned to byte 0 of the + packet payload. + + When programming filters, the hardware is limited to using a single input + set for each flow type. This means that it is an error to program two + different filters with the same type that don't match on the same fields. + Thus the second of the following two commands will fail: + + ethtool -N flow-type tcp4 src-ip 192.168.0.7 action 5 + ethtool -N flow-type tcp4 dst-ip 192.168.15.18 action 1 + + This is because the first filter will be accepted and reprogram the input + set for TCPv4 filters, but the second filter will be unable to reprogram the + input set until all the conflicting TCPv4 filters are first removed. + + Note that the user-defined flexible offset is also considered part of the + input set and cannot be programmed separately for multiple filters of the + same type. However, the flexible data is not part of the input set and + multiple filters may use the same offset but match against different data. + Data Center Bridging (DCB) -------------------------- DCB configuration is not currently supported. -- cgit From 584a88709bf4880ba5f8fed72da50512fbd9bdbb Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 7 Mar 2017 15:17:52 -0800 Subject: i40e: make use of hlist_for_each_entry_continue Replace a complex if->continue->else->break construction in i40e_next_filter. We can simply use hlist_for_each_entry_continue instead. This drops a lot of confusing code. The resulting code is much easier to understand the intention, and follows the more normal pattern for using hlist loops. We could have also used a break with a "return next" at the end of the function, instead of return NULL, but the current implementation is explicitly clear that when you reach the end of the loop you get a NULL value. The alternative construction is less clear since the reader would have to know that next is NULL at the end of the loop. Change-Id: Ife74ca451dd79d7f0d93c672bd42092d324d4a03 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 114481b67ad8..1d8febd721ac 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1883,19 +1883,12 @@ static void i40e_undo_add_filter_entries(struct i40e_vsi *vsi, static struct i40e_new_mac_filter *i40e_next_filter(struct i40e_new_mac_filter *next) { - while (next) { - next = hlist_entry(next->hlist.next, - typeof(struct i40e_new_mac_filter), - hlist); - - /* keep going if we found a broadcast filter */ - if (next && is_broadcast_ether_addr(next->f->macaddr)) - continue; - - break; + hlist_for_each_entry_continue(next, hlist) { + if (!is_broadcast_ether_addr(next->f->macaddr)) + return next; } - return next; + return NULL; } /** -- cgit From 9a32562becd95296a9403175d4992ee3b730e72a Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 23 Mar 2017 11:14:24 +0100 Subject: mlxsw: Remove debugfs interface We don't use it during development and we can't extend it either, so remove it. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 177 ----------------------------- drivers/net/ethernet/mellanox/mlxsw/pci.c | 138 ---------------------- 2 files changed, 315 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index a4c07841aaf6..fb8187d6ca13 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -40,9 +40,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -74,23 +71,9 @@ static DEFINE_SPINLOCK(mlxsw_core_driver_list_lock); static const char mlxsw_core_driver_name[] = "mlxsw_core"; -static struct dentry *mlxsw_core_dbg_root; - static struct workqueue_struct *mlxsw_wq; static struct workqueue_struct *mlxsw_owq; -struct mlxsw_core_pcpu_stats { - u64 trap_rx_packets[MLXSW_TRAP_ID_MAX]; - u64 trap_rx_bytes[MLXSW_TRAP_ID_MAX]; - u64 port_rx_packets[MLXSW_PORT_MAX_PORTS]; - u64 port_rx_bytes[MLXSW_PORT_MAX_PORTS]; - struct u64_stats_sync syncp; - u32 trap_rx_dropped[MLXSW_TRAP_ID_MAX]; - u32 port_rx_dropped[MLXSW_PORT_MAX_PORTS]; - u32 trap_rx_invalid; - u32 port_rx_invalid; -}; - struct mlxsw_core_port { struct devlink_port devlink_port; void *port_driver_priv; @@ -121,12 +104,6 @@ struct mlxsw_core { spinlock_t trans_list_lock; /* protects trans_list writes */ bool use_emad; } emad; - struct mlxsw_core_pcpu_stats __percpu *pcpu_stats; - struct dentry *dbg_dir; - struct { - struct debugfs_blob_wrapper vsd_blob; - struct debugfs_blob_wrapper psid_blob; - } dbg; struct { u8 *mapping; /* lag_id+port_index to local_port mapping */ } lag; @@ -703,91 +680,6 @@ err_out: * Core functions *****************/ -static int mlxsw_core_rx_stats_dbg_read(struct seq_file *file, void *data) -{ - struct mlxsw_core *mlxsw_core = file->private; - struct mlxsw_core_pcpu_stats *p; - u64 rx_packets, rx_bytes; - u64 tmp_rx_packets, tmp_rx_bytes; - u32 rx_dropped, rx_invalid; - unsigned int start; - int i; - int j; - static const char hdr[] = - " NUM RX_PACKETS RX_BYTES RX_DROPPED\n"; - - seq_printf(file, hdr); - for (i = 0; i < MLXSW_TRAP_ID_MAX; i++) { - rx_packets = 0; - rx_bytes = 0; - rx_dropped = 0; - for_each_possible_cpu(j) { - p = per_cpu_ptr(mlxsw_core->pcpu_stats, j); - do { - start = u64_stats_fetch_begin(&p->syncp); - tmp_rx_packets = p->trap_rx_packets[i]; - tmp_rx_bytes = p->trap_rx_bytes[i]; - } while (u64_stats_fetch_retry(&p->syncp, start)); - - rx_packets += tmp_rx_packets; - rx_bytes += tmp_rx_bytes; - rx_dropped += p->trap_rx_dropped[i]; - } - seq_printf(file, "trap %3d %12llu %12llu %10u\n", - i, rx_packets, rx_bytes, rx_dropped); - } - rx_invalid = 0; - for_each_possible_cpu(j) { - p = per_cpu_ptr(mlxsw_core->pcpu_stats, j); - rx_invalid += p->trap_rx_invalid; - } - seq_printf(file, "trap INV %10u\n", - rx_invalid); - - for (i = 0; i < MLXSW_PORT_MAX_PORTS; i++) { - rx_packets = 0; - rx_bytes = 0; - rx_dropped = 0; - for_each_possible_cpu(j) { - p = per_cpu_ptr(mlxsw_core->pcpu_stats, j); - do { - start = u64_stats_fetch_begin(&p->syncp); - tmp_rx_packets = p->port_rx_packets[i]; - tmp_rx_bytes = p->port_rx_bytes[i]; - } while (u64_stats_fetch_retry(&p->syncp, start)); - - rx_packets += tmp_rx_packets; - rx_bytes += tmp_rx_bytes; - rx_dropped += p->port_rx_dropped[i]; - } - seq_printf(file, "port %3d %12llu %12llu %10u\n", - i, rx_packets, rx_bytes, rx_dropped); - } - rx_invalid = 0; - for_each_possible_cpu(j) { - p = per_cpu_ptr(mlxsw_core->pcpu_stats, j); - rx_invalid += p->port_rx_invalid; - } - seq_printf(file, "port INV %10u\n", - rx_invalid); - return 0; -} - -static int mlxsw_core_rx_stats_dbg_open(struct inode *inode, struct file *f) -{ - struct mlxsw_core *mlxsw_core = inode->i_private; - - return single_open(f, mlxsw_core_rx_stats_dbg_read, mlxsw_core); -} - -static const struct file_operations mlxsw_core_rx_stats_dbg_ops = { - .owner = THIS_MODULE, - .open = mlxsw_core_rx_stats_dbg_open, - .release = single_release, - .read = seq_read, - .llseek = seq_lseek -}; - int mlxsw_core_driver_register(struct mlxsw_driver *mlxsw_driver) { spin_lock(&mlxsw_core_driver_list_lock); @@ -835,32 +727,6 @@ static void mlxsw_core_driver_put(const char *kind) spin_unlock(&mlxsw_core_driver_list_lock); } -static int mlxsw_core_debugfs_init(struct mlxsw_core *mlxsw_core) -{ - const struct mlxsw_bus_info *bus_info = mlxsw_core->bus_info; - - mlxsw_core->dbg_dir = debugfs_create_dir(bus_info->device_name, - mlxsw_core_dbg_root); - if (!mlxsw_core->dbg_dir) - return -ENOMEM; - debugfs_create_file("rx_stats", S_IRUGO, mlxsw_core->dbg_dir, - mlxsw_core, &mlxsw_core_rx_stats_dbg_ops); - mlxsw_core->dbg.vsd_blob.data = (void *) &bus_info->vsd; - mlxsw_core->dbg.vsd_blob.size = sizeof(bus_info->vsd); - debugfs_create_blob("vsd", S_IRUGO, mlxsw_core->dbg_dir, - &mlxsw_core->dbg.vsd_blob); - mlxsw_core->dbg.psid_blob.data = (void *) &bus_info->psid; - mlxsw_core->dbg.psid_blob.size = sizeof(bus_info->psid); - debugfs_create_blob("psid", S_IRUGO, mlxsw_core->dbg_dir, - &mlxsw_core->dbg.psid_blob); - return 0; -} - -static void mlxsw_core_debugfs_fini(struct mlxsw_core *mlxsw_core) -{ - debugfs_remove_recursive(mlxsw_core->dbg_dir); -} - static int mlxsw_devlink_port_split(struct devlink *devlink, unsigned int port_index, unsigned int count) @@ -1101,13 +967,6 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, mlxsw_core->bus_priv = bus_priv; mlxsw_core->bus_info = mlxsw_bus_info; - mlxsw_core->pcpu_stats = - netdev_alloc_pcpu_stats(struct mlxsw_core_pcpu_stats); - if (!mlxsw_core->pcpu_stats) { - err = -ENOMEM; - goto err_alloc_stats; - } - err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile, &mlxsw_core->res); if (err) @@ -1148,15 +1007,8 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, goto err_driver_init; } - err = mlxsw_core_debugfs_init(mlxsw_core); - if (err) - goto err_debugfs_init; - return 0; -err_debugfs_init: - if (mlxsw_core->driver->fini) - mlxsw_core->driver->fini(mlxsw_core); err_driver_init: mlxsw_thermal_fini(mlxsw_core->thermal); err_thermal_init: @@ -1169,8 +1021,6 @@ err_emad_init: err_alloc_lag_mapping: mlxsw_bus->fini(bus_priv); err_bus_init: - free_percpu(mlxsw_core->pcpu_stats); -err_alloc_stats: devlink_free(devlink); err_devlink_alloc: mlxsw_core_driver_put(device_kind); @@ -1183,7 +1033,6 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core) const char *device_kind = mlxsw_core->bus_info->device_kind; struct devlink *devlink = priv_to_devlink(mlxsw_core); - mlxsw_core_debugfs_fini(mlxsw_core); if (mlxsw_core->driver->fini) mlxsw_core->driver->fini(mlxsw_core); mlxsw_thermal_fini(mlxsw_core->thermal); @@ -1191,7 +1040,6 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core) mlxsw_emad_fini(mlxsw_core); kfree(mlxsw_core->lag.mapping); mlxsw_core->bus->fini(mlxsw_core->bus_priv); - free_percpu(mlxsw_core->pcpu_stats); devlink_free(devlink); mlxsw_core_driver_put(device_kind); } @@ -1639,7 +1487,6 @@ void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb, { struct mlxsw_rx_listener_item *rxl_item; const struct mlxsw_rx_listener *rxl; - struct mlxsw_core_pcpu_stats *pcpu_stats; u8 local_port; bool found = false; @@ -1678,26 +1525,10 @@ void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb, if (!found) goto drop; - pcpu_stats = this_cpu_ptr(mlxsw_core->pcpu_stats); - u64_stats_update_begin(&pcpu_stats->syncp); - pcpu_stats->port_rx_packets[local_port]++; - pcpu_stats->port_rx_bytes[local_port] += skb->len; - pcpu_stats->trap_rx_packets[rx_info->trap_id]++; - pcpu_stats->trap_rx_bytes[rx_info->trap_id] += skb->len; - u64_stats_update_end(&pcpu_stats->syncp); - rxl->func(skb, local_port, rxl_item->priv); return; drop: - if (rx_info->trap_id >= MLXSW_TRAP_ID_MAX) - this_cpu_inc(mlxsw_core->pcpu_stats->trap_rx_invalid); - else - this_cpu_inc(mlxsw_core->pcpu_stats->trap_rx_dropped[rx_info->trap_id]); - if (local_port >= MLXSW_PORT_MAX_PORTS) - this_cpu_inc(mlxsw_core->pcpu_stats->port_rx_invalid); - else - this_cpu_inc(mlxsw_core->pcpu_stats->port_rx_dropped[local_port]); dev_kfree_skb(skb); } EXPORT_SYMBOL(mlxsw_core_skb_receive); @@ -1926,15 +1757,8 @@ static int __init mlxsw_core_module_init(void) err = -ENOMEM; goto err_alloc_ordered_workqueue; } - mlxsw_core_dbg_root = debugfs_create_dir(mlxsw_core_driver_name, NULL); - if (!mlxsw_core_dbg_root) { - err = -ENOMEM; - goto err_debugfs_create_dir; - } return 0; -err_debugfs_create_dir: - destroy_workqueue(mlxsw_owq); err_alloc_ordered_workqueue: destroy_workqueue(mlxsw_wq); return err; @@ -1942,7 +1766,6 @@ err_alloc_ordered_workqueue: static void __exit mlxsw_core_module_exit(void) { - debugfs_remove_recursive(mlxsw_core_dbg_root); destroy_workqueue(mlxsw_owq); destroy_workqueue(mlxsw_wq); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index ffeb746fe2f4..eaa3e3bf5a2b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -44,8 +44,6 @@ #include #include #include -#include -#include #include #include "pci_hw.h" @@ -57,8 +55,6 @@ static const char mlxsw_pci_driver_name[] = "mlxsw_pci"; -static struct dentry *mlxsw_pci_dbg_root; - #define mlxsw_pci_write32(mlxsw_pci, reg, val) \ iowrite32be(val, (mlxsw_pci)->hw_addr + (MLXSW_PCI_ ## reg)) #define mlxsw_pci_read32(mlxsw_pci, reg) \ @@ -71,21 +67,6 @@ enum mlxsw_pci_queue_type { MLXSW_PCI_QUEUE_TYPE_EQ, }; -static const char *mlxsw_pci_queue_type_str(enum mlxsw_pci_queue_type q_type) -{ - switch (q_type) { - case MLXSW_PCI_QUEUE_TYPE_SDQ: - return "sdq"; - case MLXSW_PCI_QUEUE_TYPE_RDQ: - return "rdq"; - case MLXSW_PCI_QUEUE_TYPE_CQ: - return "cq"; - case MLXSW_PCI_QUEUE_TYPE_EQ: - return "eq"; - } - BUG(); -} - #define MLXSW_PCI_QUEUE_TYPE_COUNT 4 static const u16 mlxsw_pci_doorbell_type_offset[] = { @@ -174,7 +155,6 @@ struct mlxsw_pci { } comp; } cmd; struct mlxsw_bus_info bus_info; - struct dentry *dbg_dir; }; static void mlxsw_pci_queue_tasklet_schedule(struct mlxsw_pci_queue *q) @@ -261,21 +241,11 @@ static u8 mlxsw_pci_sdq_count(struct mlxsw_pci *mlxsw_pci) return __mlxsw_pci_queue_count(mlxsw_pci, MLXSW_PCI_QUEUE_TYPE_SDQ); } -static u8 mlxsw_pci_rdq_count(struct mlxsw_pci *mlxsw_pci) -{ - return __mlxsw_pci_queue_count(mlxsw_pci, MLXSW_PCI_QUEUE_TYPE_RDQ); -} - static u8 mlxsw_pci_cq_count(struct mlxsw_pci *mlxsw_pci) { return __mlxsw_pci_queue_count(mlxsw_pci, MLXSW_PCI_QUEUE_TYPE_CQ); } -static u8 mlxsw_pci_eq_count(struct mlxsw_pci *mlxsw_pci) -{ - return __mlxsw_pci_queue_count(mlxsw_pci, MLXSW_PCI_QUEUE_TYPE_EQ); -} - static struct mlxsw_pci_queue * __mlxsw_pci_queue_get(struct mlxsw_pci *mlxsw_pci, enum mlxsw_pci_queue_type q_type, u8 q_num) @@ -390,26 +360,6 @@ static void mlxsw_pci_sdq_fini(struct mlxsw_pci *mlxsw_pci, mlxsw_cmd_hw2sw_sdq(mlxsw_pci->core, q->num); } -static int mlxsw_pci_sdq_dbg_read(struct seq_file *file, void *data) -{ - struct mlxsw_pci *mlxsw_pci = dev_get_drvdata(file->private); - struct mlxsw_pci_queue *q; - int i; - static const char hdr[] = - "NUM PROD_COUNT CONS_COUNT COUNT\n"; - - seq_printf(file, hdr); - for (i = 0; i < mlxsw_pci_sdq_count(mlxsw_pci); i++) { - q = mlxsw_pci_sdq_get(mlxsw_pci, i); - spin_lock_bh(&q->lock); - seq_printf(file, "%3d %10d %10d %5d\n", - i, q->producer_counter, q->consumer_counter, - q->count); - spin_unlock_bh(&q->lock); - } - return 0; -} - static int mlxsw_pci_wqe_frag_map(struct mlxsw_pci *mlxsw_pci, char *wqe, int index, char *frag_data, size_t frag_len, int direction) @@ -544,26 +494,6 @@ static void mlxsw_pci_rdq_fini(struct mlxsw_pci *mlxsw_pci, } } -static int mlxsw_pci_rdq_dbg_read(struct seq_file *file, void *data) -{ - struct mlxsw_pci *mlxsw_pci = dev_get_drvdata(file->private); - struct mlxsw_pci_queue *q; - int i; - static const char hdr[] = - "NUM PROD_COUNT CONS_COUNT COUNT\n"; - - seq_printf(file, hdr); - for (i = 0; i < mlxsw_pci_rdq_count(mlxsw_pci); i++) { - q = mlxsw_pci_rdq_get(mlxsw_pci, i); - spin_lock_bh(&q->lock); - seq_printf(file, "%3d %10d %10d %5d\n", - i, q->producer_counter, q->consumer_counter, - q->count); - spin_unlock_bh(&q->lock); - } - return 0; -} - static int mlxsw_pci_cq_init(struct mlxsw_pci *mlxsw_pci, char *mbox, struct mlxsw_pci_queue *q) { @@ -601,27 +531,6 @@ static void mlxsw_pci_cq_fini(struct mlxsw_pci *mlxsw_pci, mlxsw_cmd_hw2sw_cq(mlxsw_pci->core, q->num); } -static int mlxsw_pci_cq_dbg_read(struct seq_file *file, void *data) -{ - struct mlxsw_pci *mlxsw_pci = dev_get_drvdata(file->private); - - struct mlxsw_pci_queue *q; - int i; - static const char hdr[] = - "NUM CONS_INDEX SDQ_COUNT RDQ_COUNT COUNT\n"; - - seq_printf(file, hdr); - for (i = 0; i < mlxsw_pci_cq_count(mlxsw_pci); i++) { - q = mlxsw_pci_cq_get(mlxsw_pci, i); - spin_lock_bh(&q->lock); - seq_printf(file, "%3d %10d %10d %10d %5d\n", - i, q->consumer_counter, q->u.cq.comp_sdq_count, - q->u.cq.comp_rdq_count, q->count); - spin_unlock_bh(&q->lock); - } - return 0; -} - static void mlxsw_pci_cqe_sdq_handle(struct mlxsw_pci *mlxsw_pci, struct mlxsw_pci_queue *q, u16 consumer_counter_limit, @@ -775,27 +684,6 @@ static void mlxsw_pci_eq_fini(struct mlxsw_pci *mlxsw_pci, mlxsw_cmd_hw2sw_eq(mlxsw_pci->core, q->num); } -static int mlxsw_pci_eq_dbg_read(struct seq_file *file, void *data) -{ - struct mlxsw_pci *mlxsw_pci = dev_get_drvdata(file->private); - struct mlxsw_pci_queue *q; - int i; - static const char hdr[] = - "NUM CONS_COUNT EV_CMD EV_COMP EV_OTHER COUNT\n"; - - seq_printf(file, hdr); - for (i = 0; i < mlxsw_pci_eq_count(mlxsw_pci); i++) { - q = mlxsw_pci_eq_get(mlxsw_pci, i); - spin_lock_bh(&q->lock); - seq_printf(file, "%3d %10d %10d %10d %10d %5d\n", - i, q->consumer_counter, q->u.eq.ev_cmd_count, - q->u.eq.ev_comp_count, q->u.eq.ev_other_count, - q->count); - spin_unlock_bh(&q->lock); - } - return 0; -} - static void mlxsw_pci_eq_cmd_event(struct mlxsw_pci *mlxsw_pci, char *eqe) { mlxsw_pci->cmd.comp.status = mlxsw_pci_eqe_cmd_status_get(eqe); @@ -866,7 +754,6 @@ struct mlxsw_pci_queue_ops { void (*fini)(struct mlxsw_pci *mlxsw_pci, struct mlxsw_pci_queue *q); void (*tasklet)(unsigned long data); - int (*dbg_read)(struct seq_file *s, void *data); u16 elem_count; u8 elem_size; }; @@ -875,7 +762,6 @@ static const struct mlxsw_pci_queue_ops mlxsw_pci_sdq_ops = { .type = MLXSW_PCI_QUEUE_TYPE_SDQ, .init = mlxsw_pci_sdq_init, .fini = mlxsw_pci_sdq_fini, - .dbg_read = mlxsw_pci_sdq_dbg_read, .elem_count = MLXSW_PCI_WQE_COUNT, .elem_size = MLXSW_PCI_WQE_SIZE, }; @@ -884,7 +770,6 @@ static const struct mlxsw_pci_queue_ops mlxsw_pci_rdq_ops = { .type = MLXSW_PCI_QUEUE_TYPE_RDQ, .init = mlxsw_pci_rdq_init, .fini = mlxsw_pci_rdq_fini, - .dbg_read = mlxsw_pci_rdq_dbg_read, .elem_count = MLXSW_PCI_WQE_COUNT, .elem_size = MLXSW_PCI_WQE_SIZE }; @@ -894,7 +779,6 @@ static const struct mlxsw_pci_queue_ops mlxsw_pci_cq_ops = { .init = mlxsw_pci_cq_init, .fini = mlxsw_pci_cq_fini, .tasklet = mlxsw_pci_cq_tasklet, - .dbg_read = mlxsw_pci_cq_dbg_read, .elem_count = MLXSW_PCI_CQE_COUNT, .elem_size = MLXSW_PCI_CQE_SIZE }; @@ -904,7 +788,6 @@ static const struct mlxsw_pci_queue_ops mlxsw_pci_eq_ops = { .init = mlxsw_pci_eq_init, .fini = mlxsw_pci_eq_fini, .tasklet = mlxsw_pci_eq_tasklet, - .dbg_read = mlxsw_pci_eq_dbg_read, .elem_count = MLXSW_PCI_EQE_COUNT, .elem_size = MLXSW_PCI_EQE_SIZE }; @@ -982,9 +865,7 @@ static int mlxsw_pci_queue_group_init(struct mlxsw_pci *mlxsw_pci, char *mbox, const struct mlxsw_pci_queue_ops *q_ops, u8 num_qs) { - struct pci_dev *pdev = mlxsw_pci->pdev; struct mlxsw_pci_queue_type_group *queue_group; - char tmp[16]; int i; int err; @@ -1001,10 +882,6 @@ static int mlxsw_pci_queue_group_init(struct mlxsw_pci *mlxsw_pci, char *mbox, } queue_group->count = num_qs; - sprintf(tmp, "%s_stats", mlxsw_pci_queue_type_str(q_ops->type)); - debugfs_create_devm_seqfile(&pdev->dev, tmp, mlxsw_pci->dbg_dir, - q_ops->dbg_read); - return 0; err_queue_init: @@ -1850,14 +1727,6 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mlxsw_pci->bus_info.device_name = pci_name(mlxsw_pci->pdev); mlxsw_pci->bus_info.dev = &pdev->dev; - mlxsw_pci->dbg_dir = debugfs_create_dir(mlxsw_pci->bus_info.device_name, - mlxsw_pci_dbg_root); - if (!mlxsw_pci->dbg_dir) { - dev_err(&pdev->dev, "Failed to create debugfs dir\n"); - err = -ENOMEM; - goto err_dbg_create_dir; - } - err = mlxsw_core_bus_device_register(&mlxsw_pci->bus_info, &mlxsw_pci_bus, mlxsw_pci); if (err) { @@ -1868,8 +1737,6 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; err_bus_device_register: - debugfs_remove_recursive(mlxsw_pci->dbg_dir); -err_dbg_create_dir: pci_disable_msix(mlxsw_pci->pdev); err_msix_init: err_sw_reset: @@ -1890,7 +1757,6 @@ static void mlxsw_pci_remove(struct pci_dev *pdev) struct mlxsw_pci *mlxsw_pci = pci_get_drvdata(pdev); mlxsw_core_bus_device_unregister(mlxsw_pci->core); - debugfs_remove_recursive(mlxsw_pci->dbg_dir); pci_disable_msix(mlxsw_pci->pdev); iounmap(mlxsw_pci->hw_addr); pci_release_regions(mlxsw_pci->pdev); @@ -1914,15 +1780,11 @@ EXPORT_SYMBOL(mlxsw_pci_driver_unregister); static int __init mlxsw_pci_module_init(void) { - mlxsw_pci_dbg_root = debugfs_create_dir(mlxsw_pci_driver_name, NULL); - if (!mlxsw_pci_dbg_root) - return -ENOMEM; return 0; } static void __exit mlxsw_pci_module_exit(void) { - debugfs_remove_recursive(mlxsw_pci_dbg_root); } module_init(mlxsw_pci_module_init); -- cgit From 1560875600b8aa88ff0f55f827a7741c026795ee Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 24 Mar 2017 01:29:40 +0300 Subject: xfrm: remove unused struct xfrm_mgr::id Signed-off-by: Alexey Dobriyan Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 1 - net/key/af_key.c | 1 - net/xfrm/xfrm_user.c | 1 - 3 files changed, 3 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 14d82bf16692..6e03a1a31eef 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -586,7 +586,6 @@ struct xfrm_migrate { struct xfrm_mgr { struct list_head list; - char *id; int (*notify)(struct xfrm_state *x, const struct km_event *c); int (*acquire)(struct xfrm_state *x, struct xfrm_tmpl *, struct xfrm_policy *xp); struct xfrm_policy *(*compile_policy)(struct sock *sk, int opt, u8 *data, int len, int *dir); diff --git a/net/key/af_key.c b/net/key/af_key.c index c6252ed42c1d..60cf2fb78d45 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3792,7 +3792,6 @@ static inline void pfkey_exit_proc(struct net *net) static struct xfrm_mgr pfkeyv2_mgr = { - .id = "pfkeyv2", .notify = pfkey_send_notify, .acquire = pfkey_send_acquire, .compile_policy = pfkey_compile_policy, diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 9705c279494b..8e696eafd067 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -3101,7 +3101,6 @@ static bool xfrm_is_alive(const struct km_event *c) } static struct xfrm_mgr netlink_mgr = { - .id = "netlink", .notify = xfrm_send_state_notify, .acquire = xfrm_send_acquire, .compile_policy = xfrm_compile_policy, -- cgit From d7f6946630bc324b9d791bee6dc41ff0d9469b0b Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 24 Mar 2017 01:53:09 +0300 Subject: xfrm: use "unsigned int" in __xfrm6_pref_hash() x86_64 is zero-extending arch so "unsigned int" is preferred over "int" for address calculations. Space savings: add/remove: 0/0 grow/shrink: 0/3 up/down: 0/-58 (-58) function old new delta xfrm_hash_resize 2752 2743 -9 policy_hash_bysel 985 973 -12 policy_hash_direct 1036 999 -37 Signed-off-by: Alexey Dobriyan Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_hash.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_hash.h b/net/xfrm/xfrm_hash.h index 666c5ffe929d..eaea9c4fb3b0 100644 --- a/net/xfrm/xfrm_hash.h +++ b/net/xfrm/xfrm_hash.h @@ -54,8 +54,8 @@ static inline unsigned int __xfrm4_dpref_spref_hash(const xfrm_address_t *daddr, static inline unsigned int __xfrm6_pref_hash(const xfrm_address_t *addr, __u8 prefixlen) { - int pdw; - int pbi; + unsigned int pdw; + unsigned int pbi; u32 initval = 0; pdw = prefixlen >> 5; /* num of whole u32 in prefix */ -- cgit From e1b0048e18d4637603cf6f43b9b24345abdeec5c Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 24 Mar 2017 02:07:50 +0300 Subject: xfrm: use "unsigned int" in addr_match() x86_64 is zero-extending arch so "unsigned int" is preferred over "int" for address calculations and extending to size_t. Space savings: add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-24 (-24) function old new delta xfrm_state_walk 708 696 -12 xfrm_selector_match 918 906 -12 Signed-off-by: Alexey Dobriyan Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 6e03a1a31eef..43b93d1134ed 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -816,12 +816,12 @@ static inline void xfrm_state_hold(struct xfrm_state *x) } static inline bool addr_match(const void *token1, const void *token2, - int prefixlen) + unsigned int prefixlen) { const __be32 *a1 = token1; const __be32 *a2 = token2; - int pdw; - int pbi; + unsigned int pdw; + unsigned int pbi; pdw = prefixlen >> 5; /* num of whole u32 in prefix */ pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */ -- cgit From 7e26bf45e4cb4ffaa113dc4a95a3d78bcc51e4cb Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Thu, 23 Mar 2017 12:27:12 +0200 Subject: net: bridge: allow SW learn to take over HW fdb entries Allow to take over an entry which was previously learned via HW when it shows up from a SW port. This is analogous to how HW takes over SW learned entries already. Suggested-by: Roopa Prabhu Signed-off-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- net/bridge/br_fdb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 6e08b7199dd7..daebce89dba8 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -594,6 +594,9 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, fdb->updated = now; if (unlikely(added_by_user)) fdb->added_by_user = 1; + /* Take over HW learned entry */ + if (unlikely(fdb->added_by_external_learn)) + fdb->added_by_external_learn = 0; if (unlikely(fdb_modified)) fdb_notify(br, fdb, RTM_NEWNEIGH); } -- cgit From eb100e0e24a23f309d0765061ea4dfd8ca9d400d Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Thu, 23 Mar 2017 12:27:13 +0200 Subject: net: bridge: allow to add externally learned entries from user-space The NTF_EXT_LEARNED flag was added for switchdev and externally learned entries, but it can also be used for entries learned via a software in user-space which requires dynamic entries that do not expire. One such case that we have is with quagga and evpn which need dynamic entries but also require to age them themselves. Signed-off-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- net/bridge/br_fdb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index daebce89dba8..5a40a87c4f4f 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -857,6 +857,8 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br, br_fdb_update(br, p, addr, vid, true); rcu_read_unlock(); local_bh_enable(); + } else if (ndm->ndm_flags & NTF_EXT_LEARNED) { + err = br_fdb_external_learn_add(br, p, addr, vid); } else { spin_lock_bh(&br->hash_lock); err = fdb_add_entry(br, p, addr, ndm->ndm_state, -- cgit From aff55a3638a2d13de5cf0b0c45993378282cbe91 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Thu, 23 Mar 2017 21:15:57 +0800 Subject: isdn: use setup_timer Use setup_timer() instead of init_timer() to simplify the code. Signed-off-by: Geliang Tang Signed-off-by: David S. Miller --- drivers/isdn/divert/isdn_divert.c | 9 +++------ drivers/isdn/hardware/eicon/divasi.c | 5 ++--- drivers/isdn/hardware/mISDN/hfcmulti.c | 10 ++++------ drivers/isdn/hardware/mISDN/hfcpci.c | 9 +++------ drivers/isdn/hardware/mISDN/mISDNipac.c | 5 ++--- drivers/isdn/hardware/mISDN/mISDNisar.c | 10 ++++------ drivers/isdn/hardware/mISDN/w6692.c | 5 ++--- drivers/isdn/hisax/amd7930_fn.c | 4 +--- drivers/isdn/hisax/arcofi.c | 4 +--- drivers/isdn/hisax/diva.c | 5 ++--- drivers/isdn/hisax/elsa.c | 4 +--- drivers/isdn/hisax/fsm.c | 4 +--- drivers/isdn/hisax/hfc4s8s_l1.c | 5 ++--- drivers/isdn/hisax/hfc_2bds0.c | 4 +--- drivers/isdn/hisax/hfc_pci.c | 8 ++------ drivers/isdn/hisax/hfc_sx.c | 8 ++------ drivers/isdn/hisax/hfc_usb.c | 8 ++------ drivers/isdn/hisax/hfcscard.c | 4 +--- drivers/isdn/hisax/icc.c | 4 +--- drivers/isdn/hisax/ipacx.c | 4 +--- drivers/isdn/hisax/isac.c | 4 +--- drivers/isdn/hisax/isar.c | 10 ++++------ drivers/isdn/hisax/isdnl3.c | 4 +--- drivers/isdn/hisax/teleint.c | 4 +--- drivers/isdn/hisax/w6692.c | 5 ++--- drivers/isdn/i4l/isdn_ppp.c | 5 ++--- drivers/isdn/i4l/isdn_tty.c | 5 ++--- drivers/isdn/mISDN/dsp_core.c | 4 +--- drivers/isdn/mISDN/fsm.c | 4 +--- drivers/isdn/mISDN/l1oip_core.c | 4 +--- 30 files changed, 54 insertions(+), 114 deletions(-) diff --git a/drivers/isdn/divert/isdn_divert.c b/drivers/isdn/divert/isdn_divert.c index 50749a70c5ca..060d357f107f 100644 --- a/drivers/isdn/divert/isdn_divert.c +++ b/drivers/isdn/divert/isdn_divert.c @@ -157,10 +157,8 @@ int cf_command(int drvid, int mode, /* allocate mem for information struct */ if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC))) return (-ENOMEM); /* no memory */ - init_timer(&cs->timer); + setup_timer(&cs->timer, deflect_timer_expire, (ulong)cs); cs->info[0] = '\0'; - cs->timer.function = deflect_timer_expire; - cs->timer.data = (ulong) cs; /* pointer to own structure */ cs->ics.driver = drvid; cs->ics.command = ISDN_CMD_PROT_IO; /* protocol specific io */ cs->ics.arg = DSS1_CMD_INVOKE; /* invoke supplementary service */ @@ -452,10 +450,9 @@ static int isdn_divert_icall(isdn_ctrl *ic) return (0); /* no external deflection needed */ if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC))) return (0); /* no memory */ - init_timer(&cs->timer); + setup_timer(&cs->timer, deflect_timer_expire, + (ulong)cs); cs->info[0] = '\0'; - cs->timer.function = deflect_timer_expire; - cs->timer.data = (ulong) cs; /* pointer to own structure */ cs->ics = *ic; /* copy incoming data */ if (!cs->ics.parm.setup.phone[0]) strcpy(cs->ics.parm.setup.phone, "0"); diff --git a/drivers/isdn/hardware/eicon/divasi.c b/drivers/isdn/hardware/eicon/divasi.c index cb88090f9cea..c61049585cbd 100644 --- a/drivers/isdn/hardware/eicon/divasi.c +++ b/drivers/isdn/hardware/eicon/divasi.c @@ -300,9 +300,8 @@ static int um_idi_open_adapter(struct file *file, int adapter_nr) p_os = (diva_um_idi_os_context_t *) diva_um_id_get_os_context(e); init_waitqueue_head(&p_os->read_wait); init_waitqueue_head(&p_os->close_wait); - init_timer(&p_os->diva_timer_id); - p_os->diva_timer_id.function = (void *) diva_um_timer_function; - p_os->diva_timer_id.data = (unsigned long) p_os; + setup_timer(&p_os->diva_timer_id, (void *)diva_um_timer_function, + (unsigned long)p_os); p_os->aborted = 0; p_os->adapter_nr = adapter_nr; return (1); diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index 480c2d7794eb..961c07ee47b7 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -3878,9 +3878,8 @@ hfcmulti_initmode(struct dchannel *dch) if (hc->dnum[pt]) { mode_hfcmulti(hc, dch->slot, dch->dev.D.protocol, -1, 0, -1, 0); - dch->timer.function = (void *) hfcmulti_dbusy_timer; - dch->timer.data = (long) dch; - init_timer(&dch->timer); + setup_timer(&dch->timer, (void *)hfcmulti_dbusy_timer, + (long)dch); } for (i = 1; i <= 31; i++) { if (!((1 << i) & hc->bmask[pt])) /* skip unused chan */ @@ -3986,9 +3985,8 @@ hfcmulti_initmode(struct dchannel *dch) hc->chan[i].slot_rx = -1; hc->chan[i].conf = -1; mode_hfcmulti(hc, i, dch->dev.D.protocol, -1, 0, -1, 0); - dch->timer.function = (void *) hfcmulti_dbusy_timer; - dch->timer.data = (long) dch; - init_timer(&dch->timer); + setup_timer(&dch->timer, (void *)hfcmulti_dbusy_timer, + (long)dch); hc->chan[i - 2].slot_tx = -1; hc->chan[i - 2].slot_rx = -1; hc->chan[i - 2].conf = -1; diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index ff48da61c94c..5dc246d71c16 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -1717,9 +1717,8 @@ static void inithfcpci(struct hfc_pci *hc) { printk(KERN_DEBUG "inithfcpci: entered\n"); - hc->dch.timer.function = (void *) hfcpci_dbusy_timer; - hc->dch.timer.data = (long) &hc->dch; - init_timer(&hc->dch.timer); + setup_timer(&hc->dch.timer, (void *)hfcpci_dbusy_timer, + (long)&hc->dch); hc->chanlimit = 2; mode_hfcpci(&hc->bch[0], 1, -1); mode_hfcpci(&hc->bch[1], 2, -1); @@ -2044,9 +2043,7 @@ setup_hw(struct hfc_pci *hc) Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); /* At this point the needed PCI config is done */ /* fifos are still not enabled */ - hc->hw.timer.function = (void *) hfcpci_Timer; - hc->hw.timer.data = (long) hc; - init_timer(&hc->hw.timer); + setup_timer(&hc->hw.timer, (void *)hfcpci_Timer, (long)hc); /* default PCM master */ test_and_set_bit(HFC_CFG_MASTER, &hc->cfg); return 0; diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c index 77dec28ba874..6742b0dc0821 100644 --- a/drivers/isdn/hardware/mISDN/mISDNipac.c +++ b/drivers/isdn/hardware/mISDN/mISDNipac.c @@ -796,9 +796,8 @@ isac_init(struct isac_hw *isac) } isac->mon_tx = NULL; isac->mon_rx = NULL; - isac->dch.timer.function = (void *) dbusy_timer_handler; - isac->dch.timer.data = (long)isac; - init_timer(&isac->dch.timer); + setup_timer(&isac->dch.timer, (void *)dbusy_timer_handler, + (long)isac); isac->mocr = 0xaa; if (isac->type & IPAC_TYPE_ISACX) { /* Disable all IRQ */ diff --git a/drivers/isdn/hardware/mISDN/mISDNisar.c b/drivers/isdn/hardware/mISDN/mISDNisar.c index feafa91c2ed9..5b078591b6ee 100644 --- a/drivers/isdn/hardware/mISDN/mISDNisar.c +++ b/drivers/isdn/hardware/mISDN/mISDNisar.c @@ -1635,13 +1635,11 @@ init_isar(struct isar_hw *isar) } if (isar->version != 1) return -EINVAL; - isar->ch[0].ftimer.function = &ftimer_handler; - isar->ch[0].ftimer.data = (long)&isar->ch[0]; - init_timer(&isar->ch[0].ftimer); + setup_timer(&isar->ch[0].ftimer, &ftimer_handler, + (long)&isar->ch[0]); test_and_set_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags); - isar->ch[1].ftimer.function = &ftimer_handler; - isar->ch[1].ftimer.data = (long)&isar->ch[1]; - init_timer(&isar->ch[1].ftimer); + setup_timer(&isar->ch[1].ftimer, &ftimer_handler, + (long)&isar->ch[1]); test_and_set_bit(FLG_INITIALIZED, &isar->ch[1].bch.Flags); return 0; } diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c index 3b067ea656bd..3052c836b89f 100644 --- a/drivers/isdn/hardware/mISDN/w6692.c +++ b/drivers/isdn/hardware/mISDN/w6692.c @@ -852,9 +852,8 @@ static void initW6692(struct w6692_hw *card) { u8 val; - card->dch.timer.function = (void *)dbusy_timer_handler; - card->dch.timer.data = (u_long)&card->dch; - init_timer(&card->dch.timer); + setup_timer(&card->dch.timer, (void *)dbusy_timer_handler, + (u_long)&card->dch); w6692_mode(&card->bc[0], ISDN_P_NONE); w6692_mode(&card->bc[1], ISDN_P_NONE); WriteW6692(card, W_D_CTL, 0x00); diff --git a/drivers/isdn/hisax/amd7930_fn.c b/drivers/isdn/hisax/amd7930_fn.c index 36817e0a0b94..3a4c2f9e19e9 100644 --- a/drivers/isdn/hisax/amd7930_fn.c +++ b/drivers/isdn/hisax/amd7930_fn.c @@ -789,7 +789,5 @@ void Amd7930_init(struct IsdnCardState *cs) void setup_Amd7930(struct IsdnCardState *cs) { INIT_WORK(&cs->tqueue, Amd7930_bh); - cs->dbusytimer.function = (void *) dbusy_timer_handler; - cs->dbusytimer.data = (long) cs; - init_timer(&cs->dbusytimer); + setup_timer(&cs->dbusytimer, (void *)dbusy_timer_handler, (long)cs); } diff --git a/drivers/isdn/hisax/arcofi.c b/drivers/isdn/hisax/arcofi.c index 29ec2dfbd155..9826bad49e2c 100644 --- a/drivers/isdn/hisax/arcofi.c +++ b/drivers/isdn/hisax/arcofi.c @@ -125,9 +125,7 @@ clear_arcofi(struct IsdnCardState *cs) { void init_arcofi(struct IsdnCardState *cs) { - cs->dc.isac.arcofitimer.function = (void *) arcofi_timer; - cs->dc.isac.arcofitimer.data = (long) cs; - init_timer(&cs->dc.isac.arcofitimer); + setup_timer(&cs->dc.isac.arcofitimer, (void *)arcofi_timer, (long)cs); init_waitqueue_head(&cs->dc.isac.arcofi_wait); test_and_set_bit(HW_ARCOFI, &cs->HW_Flags); } diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c index 4fc90de68d18..079336e593f9 100644 --- a/drivers/isdn/hisax/diva.c +++ b/drivers/isdn/hisax/diva.c @@ -976,9 +976,8 @@ static int setup_diva_common(struct IsdnCardState *cs) printk(KERN_INFO "Diva: IPACX Design Id: %x\n", MemReadISAC_IPACX(cs, IPACX_ID) & 0x3F); } else { /* DIVA 2.0 */ - cs->hw.diva.tl.function = (void *) diva_led_handler; - cs->hw.diva.tl.data = (long) cs; - init_timer(&cs->hw.diva.tl); + setup_timer(&cs->hw.diva.tl, (void *)diva_led_handler, + (long)cs); cs->readisac = &ReadISAC; cs->writeisac = &WriteISAC; cs->readisacfifo = &ReadISACfifo; diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c index d8ef64da26f1..03bc5d504e22 100644 --- a/drivers/isdn/hisax/elsa.c +++ b/drivers/isdn/hisax/elsa.c @@ -1147,9 +1147,7 @@ static int setup_elsa_common(struct IsdnCard *card) init_arcofi(cs); #endif setup_isac(cs); - cs->hw.elsa.tl.function = (void *) elsa_led_handler; - cs->hw.elsa.tl.data = (long) cs; - init_timer(&cs->hw.elsa.tl); + setup_timer(&cs->hw.elsa.tl, (void *)elsa_led_handler, (long)cs); /* Teste Timer */ if (cs->hw.elsa.timer) { byteout(cs->hw.elsa.trig, 0xff); diff --git a/drivers/isdn/hisax/fsm.c b/drivers/isdn/hisax/fsm.c index c7a94713e9ec..d63266fa8cbd 100644 --- a/drivers/isdn/hisax/fsm.c +++ b/drivers/isdn/hisax/fsm.c @@ -98,13 +98,11 @@ void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) { ft->fi = fi; - ft->tl.function = (void *) FsmExpireTimer; - ft->tl.data = (long) ft; #if FSM_TIMER_DEBUG if (ft->fi->debug) ft->fi->printdebug(ft->fi, "FsmInitTimer %lx", (long) ft); #endif - init_timer(&ft->tl); + setup_timer(&ft->tl, (void *)FsmExpireTimer, (long)ft); } void diff --git a/drivers/isdn/hisax/hfc4s8s_l1.c b/drivers/isdn/hisax/hfc4s8s_l1.c index e034ed847ff3..90f051ce0259 100644 --- a/drivers/isdn/hisax/hfc4s8s_l1.c +++ b/drivers/isdn/hisax/hfc4s8s_l1.c @@ -1396,9 +1396,8 @@ setup_instance(hfc4s8s_hw *hw) l1p = hw->l1 + i; spin_lock_init(&l1p->lock); l1p->hw = hw; - l1p->l1_timer.function = (void *) hfc_l1_timer; - l1p->l1_timer.data = (long) (l1p); - init_timer(&l1p->l1_timer); + setup_timer(&l1p->l1_timer, (void *)hfc_l1_timer, + (long)(l1p)); l1p->st_num = i; skb_queue_head_init(&l1p->d_tx_queue); l1p->d_if.ifc.priv = hw->l1 + i; diff --git a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c index a756e5cb6871..ad8597a1a07e 100644 --- a/drivers/isdn/hisax/hfc_2bds0.c +++ b/drivers/isdn/hisax/hfc_2bds0.c @@ -1073,8 +1073,6 @@ set_cs_func(struct IsdnCardState *cs) cs->writeisacfifo = &dummyf; cs->BC_Read_Reg = &ReadReg; cs->BC_Write_Reg = &WriteReg; - cs->dbusytimer.function = (void *) hfc_dbusy_timer; - cs->dbusytimer.data = (long) cs; - init_timer(&cs->dbusytimer); + setup_timer(&cs->dbusytimer, (void *)hfc_dbusy_timer, (long)cs); INIT_WORK(&cs->tqueue, hfcd_bh); } diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c index 90449e1e91e5..f9ca35cc32b1 100644 --- a/drivers/isdn/hisax/hfc_pci.c +++ b/drivers/isdn/hisax/hfc_pci.c @@ -1582,9 +1582,7 @@ inithfcpci(struct IsdnCardState *cs) cs->bcs[1].BC_SetStack = setstack_2b; cs->bcs[0].BC_Close = close_hfcpci; cs->bcs[1].BC_Close = close_hfcpci; - cs->dbusytimer.function = (void *) hfcpci_dbusy_timer; - cs->dbusytimer.data = (long) cs; - init_timer(&cs->dbusytimer); + setup_timer(&cs->dbusytimer, (void *)hfcpci_dbusy_timer, (long)cs); mode_hfcpci(cs->bcs, 0, 0); mode_hfcpci(cs->bcs + 1, 0, 1); } @@ -1746,9 +1744,7 @@ setup_hfcpci(struct IsdnCard *card) cs->BC_Write_Reg = NULL; cs->irq_func = &hfcpci_interrupt; cs->irq_flags |= IRQF_SHARED; - cs->hw.hfcpci.timer.function = (void *) hfcpci_Timer; - cs->hw.hfcpci.timer.data = (long) cs; - init_timer(&cs->hw.hfcpci.timer); + setup_timer(&cs->hw.hfcpci.timer, (void *)hfcpci_Timer, (long)cs); cs->cardmsg = &hfcpci_card_msg; cs->auxcmd = &hfcpci_auxcmd; diff --git a/drivers/isdn/hisax/hfc_sx.c b/drivers/isdn/hisax/hfc_sx.c index 13b2151c10f5..3aef8e1a90e4 100644 --- a/drivers/isdn/hisax/hfc_sx.c +++ b/drivers/isdn/hisax/hfc_sx.c @@ -1495,9 +1495,7 @@ int setup_hfcsx(struct IsdnCard *card) } else return (0); /* no valid card type */ - cs->dbusytimer.function = (void *) hfcsx_dbusy_timer; - cs->dbusytimer.data = (long) cs; - init_timer(&cs->dbusytimer); + setup_timer(&cs->dbusytimer, (void *)hfcsx_dbusy_timer, (long)cs); INIT_WORK(&cs->tqueue, hfcsx_bh); cs->readisac = NULL; cs->writeisac = NULL; @@ -1507,11 +1505,9 @@ int setup_hfcsx(struct IsdnCard *card) cs->BC_Write_Reg = NULL; cs->irq_func = &hfcsx_interrupt; - cs->hw.hfcsx.timer.function = (void *) hfcsx_Timer; - cs->hw.hfcsx.timer.data = (long) cs; cs->hw.hfcsx.b_fifo_size = 0; /* fifo size still unknown */ cs->hw.hfcsx.cirm = ccd_sp_irqtab[cs->irq & 0xF]; /* RAM not evaluated */ - init_timer(&cs->hw.hfcsx.timer); + setup_timer(&cs->hw.hfcsx.timer, (void *)hfcsx_Timer, (long)cs); reset_hfcsx(cs); cs->cardmsg = &hfcsx_card_msg; diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c index 678bd5224bc3..6dbd1f1da14f 100644 --- a/drivers/isdn/hisax/hfc_usb.c +++ b/drivers/isdn/hisax/hfc_usb.c @@ -1165,14 +1165,10 @@ hfc_usb_init(hfcusb_data *hfc) hfc->old_led_state = 0; /* init the t3 timer */ - init_timer(&hfc->t3_timer); - hfc->t3_timer.data = (long) hfc; - hfc->t3_timer.function = (void *) l1_timer_expire_t3; + setup_timer(&hfc->t3_timer, (void *)l1_timer_expire_t3, (long)hfc); /* init the t4 timer */ - init_timer(&hfc->t4_timer); - hfc->t4_timer.data = (long) hfc; - hfc->t4_timer.function = (void *) l1_timer_expire_t4; + setup_timer(&hfc->t4_timer, (void *)l1_timer_expire_t4, (long)hfc); /* init the background machinery for control requests */ hfc->ctrl_read.bRequestType = 0xc0; diff --git a/drivers/isdn/hisax/hfcscard.c b/drivers/isdn/hisax/hfcscard.c index 394da646e97b..467287096918 100644 --- a/drivers/isdn/hisax/hfcscard.c +++ b/drivers/isdn/hisax/hfcscard.c @@ -253,9 +253,7 @@ int setup_hfcs(struct IsdnCard *card) outb(0x57, cs->hw.hfcD.addr | 1); } set_cs_func(cs); - cs->hw.hfcD.timer.function = (void *) hfcs_Timer; - cs->hw.hfcD.timer.data = (long) cs; - init_timer(&cs->hw.hfcD.timer); + setup_timer(&cs->hw.hfcD.timer, (void *)hfcs_Timer, (long)cs); cs->cardmsg = &hfcs_card_msg; cs->irq_func = &hfcs_interrupt; return (1); diff --git a/drivers/isdn/hisax/icc.c b/drivers/isdn/hisax/icc.c index 96d1df05044f..c7c3797a817e 100644 --- a/drivers/isdn/hisax/icc.c +++ b/drivers/isdn/hisax/icc.c @@ -676,7 +676,5 @@ clear_pending_icc_ints(struct IsdnCardState *cs) void setup_icc(struct IsdnCardState *cs) { INIT_WORK(&cs->tqueue, icc_bh); - cs->dbusytimer.function = (void *) dbusy_timer_handler; - cs->dbusytimer.data = (long) cs; - init_timer(&cs->dbusytimer); + setup_timer(&cs->dbusytimer, (void *)dbusy_timer_handler, (long)cs); } diff --git a/drivers/isdn/hisax/ipacx.c b/drivers/isdn/hisax/ipacx.c index 9cc26b40a437..43effe7082ed 100644 --- a/drivers/isdn/hisax/ipacx.c +++ b/drivers/isdn/hisax/ipacx.c @@ -424,9 +424,7 @@ dch_init(struct IsdnCardState *cs) cs->setstack_d = dch_setstack; - cs->dbusytimer.function = (void *) dbusy_timer_handler; - cs->dbusytimer.data = (long) cs; - init_timer(&cs->dbusytimer); + setup_timer(&cs->dbusytimer, (void *)dbusy_timer_handler, (long)cs); cs->writeisac(cs, IPACX_TR_CONF0, 0x00); // clear LDD cs->writeisac(cs, IPACX_TR_CONF2, 0x00); // enable transmitter diff --git a/drivers/isdn/hisax/isac.c b/drivers/isdn/hisax/isac.c index df7e05ca8f9c..4273b4548825 100644 --- a/drivers/isdn/hisax/isac.c +++ b/drivers/isdn/hisax/isac.c @@ -677,7 +677,5 @@ void clear_pending_isac_ints(struct IsdnCardState *cs) void setup_isac(struct IsdnCardState *cs) { INIT_WORK(&cs->tqueue, isac_bh); - cs->dbusytimer.function = (void *) dbusy_timer_handler; - cs->dbusytimer.data = (long) cs; - init_timer(&cs->dbusytimer); + setup_timer(&cs->dbusytimer, (void *)dbusy_timer_handler, (long)cs); } diff --git a/drivers/isdn/hisax/isar.c b/drivers/isdn/hisax/isar.c index f4956c73aa11..0dc60b287c4b 100644 --- a/drivers/isdn/hisax/isar.c +++ b/drivers/isdn/hisax/isar.c @@ -1902,10 +1902,8 @@ void initisar(struct IsdnCardState *cs) cs->bcs[1].BC_SetStack = setstack_isar; cs->bcs[0].BC_Close = close_isarstate; cs->bcs[1].BC_Close = close_isarstate; - cs->bcs[0].hw.isar.ftimer.function = (void *) ftimer_handler; - cs->bcs[0].hw.isar.ftimer.data = (long) &cs->bcs[0]; - init_timer(&cs->bcs[0].hw.isar.ftimer); - cs->bcs[1].hw.isar.ftimer.function = (void *) ftimer_handler; - cs->bcs[1].hw.isar.ftimer.data = (long) &cs->bcs[1]; - init_timer(&cs->bcs[1].hw.isar.ftimer); + setup_timer(&cs->bcs[0].hw.isar.ftimer, (void *)ftimer_handler, + (long)&cs->bcs[0]); + setup_timer(&cs->bcs[1].hw.isar.ftimer, (void *)ftimer_handler, + (long)&cs->bcs[1]); } diff --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c index c754706f83cd..569ce52c567b 100644 --- a/drivers/isdn/hisax/isdnl3.c +++ b/drivers/isdn/hisax/isdnl3.c @@ -169,9 +169,7 @@ void L3InitTimer(struct l3_process *pc, struct L3Timer *t) { t->pc = pc; - t->tl.function = (void *) L3ExpireTimer; - t->tl.data = (long) t; - init_timer(&t->tl); + setup_timer(&t->tl, (void *)L3ExpireTimer, (long)t); } void diff --git a/drivers/isdn/hisax/teleint.c b/drivers/isdn/hisax/teleint.c index bf647545c70c..950399f066ef 100644 --- a/drivers/isdn/hisax/teleint.c +++ b/drivers/isdn/hisax/teleint.c @@ -278,9 +278,7 @@ int setup_TeleInt(struct IsdnCard *card) cs->bcs[0].hw.hfc.send = NULL; cs->bcs[1].hw.hfc.send = NULL; cs->hw.hfc.fifosize = 7 * 1024 + 512; - cs->hw.hfc.timer.function = (void *) TeleInt_Timer; - cs->hw.hfc.timer.data = (long) cs; - init_timer(&cs->hw.hfc.timer); + setup_timer(&cs->hw.hfc.timer, (void *)TeleInt_Timer, (long)cs); if (!request_region(cs->hw.hfc.addr, 2, "TeleInt isdn")) { printk(KERN_WARNING "HiSax: TeleInt config port %x-%x already in use\n", diff --git a/drivers/isdn/hisax/w6692.c b/drivers/isdn/hisax/w6692.c index a85895585d90..c99f0ec58a01 100644 --- a/drivers/isdn/hisax/w6692.c +++ b/drivers/isdn/hisax/w6692.c @@ -901,9 +901,8 @@ static void initW6692(struct IsdnCardState *cs, int part) if (part & 1) { cs->setstack_d = setstack_W6692; cs->DC_Close = DC_Close_W6692; - cs->dbusytimer.function = (void *) dbusy_timer_handler; - cs->dbusytimer.data = (long) cs; - init_timer(&cs->dbusytimer); + setup_timer(&cs->dbusytimer, (void *)dbusy_timer_handler, + (long)cs); resetW6692(cs); ph_command(cs, W_L1CMD_RST); cs->dc.w6692.ph_state = W_L1CMD_RST; diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index 9c1e8adaf4fc..d07dd5196ffc 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -2370,9 +2370,8 @@ static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_s rs->state = CCPResetIdle; rs->is = is; rs->id = id; - init_timer(&rs->timer); - rs->timer.data = (unsigned long)rs; - rs->timer.function = isdn_ppp_ccp_timer_callback; + setup_timer(&rs->timer, isdn_ppp_ccp_timer_callback, + (unsigned long)rs); is->reset->rs[id] = rs; } return rs; diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 1b169559a240..ddd8207e4e54 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1812,9 +1812,8 @@ isdn_tty_modem_init(void) info->isdn_channel = -1; info->drv_index = -1; info->xmit_size = ISDN_SERIAL_XMIT_SIZE; - init_timer(&info->nc_timer); - info->nc_timer.function = isdn_tty_modem_do_ncarrier; - info->nc_timer.data = (unsigned long) info; + setup_timer(&info->nc_timer, isdn_tty_modem_do_ncarrier, + (unsigned long)info); skb_queue_head_init(&info->xmit_queue); #ifdef CONFIG_ISDN_AUDIO skb_queue_head_init(&info->dtmf_queue); diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c index 9b85295aa657..880e9d367a39 100644 --- a/drivers/isdn/mISDN/dsp_core.c +++ b/drivers/isdn/mISDN/dsp_core.c @@ -1092,9 +1092,7 @@ dspcreate(struct channel_req *crq) ndsp->pcm_bank_tx = -1; ndsp->hfc_conf = -1; /* current conference number */ /* set tone timer */ - ndsp->tone.tl.function = (void *)dsp_tone_timeout; - ndsp->tone.tl.data = (long) ndsp; - init_timer(&ndsp->tone.tl); + setup_timer(&ndsp->tone.tl, (void *)dsp_tone_timeout, (long)ndsp); if (dtmfthreshold < 20 || dtmfthreshold > 500) dtmfthreshold = 200; diff --git a/drivers/isdn/mISDN/fsm.c b/drivers/isdn/mISDN/fsm.c index 26477d48bbda..78fc5d5e9051 100644 --- a/drivers/isdn/mISDN/fsm.c +++ b/drivers/isdn/mISDN/fsm.c @@ -110,13 +110,11 @@ void mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft) { ft->fi = fi; - ft->tl.function = (void *) FsmExpireTimer; - ft->tl.data = (long) ft; #if FSM_TIMER_DEBUG if (ft->fi->debug) ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft); #endif - init_timer(&ft->tl); + setup_timer(&ft->tl, (void *)FsmExpireTimer, (long)ft); } EXPORT_SYMBOL(mISDN_FsmInitTimer); diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c index 6ceca7db62ad..6be2041248d3 100644 --- a/drivers/isdn/mISDN/l1oip_core.c +++ b/drivers/isdn/mISDN/l1oip_core.c @@ -1443,9 +1443,7 @@ init_card(struct l1oip *hc, int pri, int bundle) hc->keep_tl.expires = jiffies + 2 * HZ; /* two seconds first time */ add_timer(&hc->keep_tl); - hc->timeout_tl.function = (void *)l1oip_timeout; - hc->timeout_tl.data = (ulong)hc; - init_timer(&hc->timeout_tl); + setup_timer(&hc->timeout_tl, (void *)l1oip_timeout, (ulong)hc); hc->timeout_on = 0; /* state that we have timer off */ return 0; -- cgit From 270c7759fbbc99e1ed00259c752a8c53f31cfb27 Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Thu, 23 Mar 2017 14:40:22 +0100 Subject: net: stmmac: add set_mac to the stmmac_ops Two different set_mac functions exists but stmmac_dwmac4_set_mac() is only used for enabling and never for disabling. So on dwmac4, the MAC RX/TX is never disabled. This patch add a generic function pointer set_mac() to stmmac_ops and replace all call to stmmac_set_mac/stmmac_dwmac4_set_mac by a call to this pointer. Since dwmac4_ops is const, set_mac cannot be modified after, and so dwmac4_ops is duplioacted like dwmac4_dma_ops. Signed-off-by: Corentin Labbe Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 2 ++ .../net/ethernet/stmicro/stmmac/dwmac1000_core.c | 1 + .../net/ethernet/stmicro/stmmac/dwmac100_core.c | 1 + drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 39 ++++++++++++++++++++-- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 11 +++--- 5 files changed, 45 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 572cf8b61707..90d28bcad880 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -474,6 +474,8 @@ struct mac_device_info; struct stmmac_ops { /* MAC core initialization */ void (*core_init)(struct mac_device_info *hw, int mtu); + /* Enable the MAC RX/TX */ + void (*set_mac)(void __iomem *ioaddr, bool enable); /* Enable and verify that the IPC module is supported */ int (*rx_ipc)(struct mac_device_info *hw); /* Enable RX Queues */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index 7f78f7746a5b..f3d9305e5f70 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -490,6 +490,7 @@ static void dwmac1000_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x, static const struct stmmac_ops dwmac1000_ops = { .core_init = dwmac1000_core_init, + .set_mac = stmmac_set_mac, .rx_ipc = dwmac1000_rx_ipc_enable, .dump_regs = dwmac1000_dump_regs, .host_irq_status = dwmac1000_irq_status, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c index 524135e6dd89..1b3609105484 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c @@ -150,6 +150,7 @@ static void dwmac100_pmt(struct mac_device_info *hw, unsigned long mode) static const struct stmmac_ops dwmac100_ops = { .core_init = dwmac100_core_init, + .set_mac = stmmac_set_mac, .rx_ipc = dwmac100_rx_ipc_enable, .dump_regs = dwmac100_dump_mac_regs, .host_irq_status = dwmac100_irq_status, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 40ce20218402..48793f2e9307 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -669,6 +669,38 @@ static void dwmac4_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x, static const struct stmmac_ops dwmac4_ops = { .core_init = dwmac4_core_init, + .set_mac = stmmac_set_mac, + .rx_ipc = dwmac4_rx_ipc_enable, + .rx_queue_enable = dwmac4_rx_queue_enable, + .rx_queue_prio = dwmac4_rx_queue_priority, + .tx_queue_prio = dwmac4_tx_queue_priority, + .rx_queue_routing = dwmac4_tx_queue_routing, + .prog_mtl_rx_algorithms = dwmac4_prog_mtl_rx_algorithms, + .prog_mtl_tx_algorithms = dwmac4_prog_mtl_tx_algorithms, + .set_mtl_tx_queue_weight = dwmac4_set_mtl_tx_queue_weight, + .map_mtl_to_dma = dwmac4_map_mtl_dma, + .config_cbs = dwmac4_config_cbs, + .dump_regs = dwmac4_dump_regs, + .host_irq_status = dwmac4_irq_status, + .host_mtl_irq_status = dwmac4_irq_mtl_status, + .flow_ctrl = dwmac4_flow_ctrl, + .pmt = dwmac4_pmt, + .set_umac_addr = dwmac4_set_umac_addr, + .get_umac_addr = dwmac4_get_umac_addr, + .set_eee_mode = dwmac4_set_eee_mode, + .reset_eee_mode = dwmac4_reset_eee_mode, + .set_eee_timer = dwmac4_set_eee_timer, + .set_eee_pls = dwmac4_set_eee_pls, + .pcs_ctrl_ane = dwmac4_ctrl_ane, + .pcs_rane = dwmac4_rane, + .pcs_get_adv_lp = dwmac4_get_adv_lp, + .debug = dwmac4_debug, + .set_filter = dwmac4_set_filter, +}; + +static const struct stmmac_ops dwmac410_ops = { + .core_init = dwmac4_core_init, + .set_mac = stmmac_dwmac4_set_mac, .rx_ipc = dwmac4_rx_ipc_enable, .rx_queue_enable = dwmac4_rx_queue_enable, .rx_queue_prio = dwmac4_rx_queue_priority, @@ -715,8 +747,6 @@ struct mac_device_info *dwmac4_setup(void __iomem *ioaddr, int mcbins, if (mac->multicast_filter_bins) mac->mcast_bits_log2 = ilog2(mac->multicast_filter_bins); - mac->mac = &dwmac4_ops; - mac->link.port = GMAC_CONFIG_PS; mac->link.duplex = GMAC_CONFIG_DM; mac->link.speed = GMAC_CONFIG_FES; @@ -737,5 +767,10 @@ struct mac_device_info *dwmac4_setup(void __iomem *ioaddr, int mcbins, else mac->dma = &dwmac4_dma_ops; + if (*synopsys_id >= DWMAC_CORE_4_00) + mac->mac = &dwmac410_ops; + else + mac->mac = &dwmac4_ops; + return mac; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 4b418d2aec38..c78f444ad423 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2460,10 +2460,7 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) } /* Enable the MAC Rx/Tx */ - if (priv->synopsys_id >= DWMAC_CORE_4_00) - stmmac_dwmac4_set_mac(priv->ioaddr, true); - else - stmmac_set_mac(priv->ioaddr, true); + priv->hw->mac->set_mac(priv->ioaddr, true); /* Set the HW DMA mode and the COE */ stmmac_dma_operation_mode(priv); @@ -2663,7 +2660,7 @@ static int stmmac_release(struct net_device *dev) free_dma_desc_resources(priv); /* Disable the MAC Rx/Tx */ - stmmac_set_mac(priv->ioaddr, false); + priv->hw->mac->set_mac(priv->ioaddr, false); netif_carrier_off(dev); @@ -4230,7 +4227,7 @@ int stmmac_dvr_remove(struct device *dev) stmmac_stop_all_dma(priv); - stmmac_set_mac(priv->ioaddr, false); + priv->hw->mac->set_mac(priv->ioaddr, false); netif_carrier_off(ndev); unregister_netdev(ndev); if (priv->plat->stmmac_rst) @@ -4281,7 +4278,7 @@ int stmmac_suspend(struct device *dev) priv->hw->mac->pmt(priv->hw, priv->wolopts); priv->irq_wake = 1; } else { - stmmac_set_mac(priv->ioaddr, false); + priv->hw->mac->set_mac(priv->ioaddr, false); pinctrl_pm_select_sleep_state(priv->device); /* Disable clock in case of PWM is off */ clk_disable(priv->plat->pclk); -- cgit From 5952fde10c35ecfefb0c3e61536170798d4a51c9 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 23 Mar 2017 17:02:16 +0100 Subject: net: sched: choke: remove dead filter classify code sch_choke is classless qdisc so it does not define cl_ops. Therefore filter_list cannot be ever changed, being NULL all the time. Reason is this check in tc_ctl_tfilter: /* Is it classful? */ cops = q->ops->cl_ops; if (!cops) return -EINVAL; So remove this dead code. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/sch_choke.c | 51 --------------------------------------------------- 1 file changed, 51 deletions(-) diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c index 3b86a97bc67c..03ce895d7ff5 100644 --- a/net/sched/sch_choke.c +++ b/net/sched/sch_choke.c @@ -58,7 +58,6 @@ struct choke_sched_data { /* Variables */ struct red_vars vars; - struct tcf_proto __rcu *filter_list; struct { u32 prob_drop; /* Early probability drops */ u32 prob_mark; /* Early probability marks */ @@ -152,11 +151,6 @@ static inline void choke_set_classid(struct sk_buff *skb, u16 classid) choke_skb_cb(skb)->classid = classid; } -static u16 choke_get_classid(const struct sk_buff *skb) -{ - return choke_skb_cb(skb)->classid; -} - /* * Compare flow of two packets * Returns true only if source and destination address and port match. @@ -187,40 +181,6 @@ static bool choke_match_flow(struct sk_buff *skb1, sizeof(choke_skb_cb(skb1)->keys)); } -/* - * Classify flow using either: - * 1. pre-existing classification result in skb - * 2. fast internal classification - * 3. use TC filter based classification - */ -static bool choke_classify(struct sk_buff *skb, - struct Qdisc *sch, int *qerr) - -{ - struct choke_sched_data *q = qdisc_priv(sch); - struct tcf_result res; - struct tcf_proto *fl; - int result; - - fl = rcu_dereference_bh(q->filter_list); - result = tc_classify(skb, fl, &res, false); - if (result >= 0) { -#ifdef CONFIG_NET_CLS_ACT - switch (result) { - case TC_ACT_STOLEN: - case TC_ACT_QUEUED: - *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN; - case TC_ACT_SHOT: - return false; - } -#endif - choke_set_classid(skb, TC_H_MIN(res.classid)); - return true; - } - - return false; -} - /* * Select a packet at random from queue * HACK: since queue can have holes from previous deletion; retry several @@ -257,9 +217,6 @@ static bool choke_match_random(const struct choke_sched_data *q, return false; oskb = choke_peek_random(q, pidx); - if (rcu_access_pointer(q->filter_list)) - return choke_get_classid(nskb) == choke_get_classid(oskb); - return choke_match_flow(oskb, nskb); } @@ -270,12 +227,6 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct choke_sched_data *q = qdisc_priv(sch); const struct red_parms *p = &q->parms; - if (rcu_access_pointer(q->filter_list)) { - /* If using external classifiers, get result and record it. */ - if (!choke_classify(skb, sch, &ret)) - goto other_drop; /* Packet was eaten by filter */ - } - choke_skb_cb(skb)->keys_valid = 0; /* Compute average queue usage (see RED) */ q->vars.qavg = red_calc_qavg(p, &q->vars, sch->q.qlen); @@ -340,7 +291,6 @@ congestion_drop: qdisc_drop(skb, sch, to_free); return NET_XMIT_CN; -other_drop: if (ret & __NET_XMIT_BYPASS) qdisc_qstats_drop(sch); __qdisc_drop(skb, to_free); @@ -538,7 +488,6 @@ static void choke_destroy(struct Qdisc *sch) { struct choke_sched_data *q = qdisc_priv(sch); - tcf_destroy_chain(&q->filter_list); choke_free(q->tab); } -- cgit From e6e14f63d744cede856ba5d517d6b266c9cfbf41 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 23 Mar 2017 10:01:17 -0700 Subject: of_mdio: Correct check against CONFIG_OF CONFIG_OF_MDIO is actually what triggers the build of drivers/of/of_mdio.c, so providing inline stubs when CONFIG_OF_MDIO=y should be based on that symbol as well. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- include/linux/of_mdio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/of_mdio.h b/include/linux/of_mdio.h index a58cca8bcb29..ba35ba520487 100644 --- a/include/linux/of_mdio.h +++ b/include/linux/of_mdio.h @@ -12,7 +12,7 @@ #include #include -#ifdef CONFIG_OF +#if IS_ENABLED(CONFIG_OF_MDIO) extern int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np); extern struct phy_device *of_phy_find_device(struct device_node *phy_np); extern struct phy_device *of_phy_connect(struct net_device *dev, @@ -32,7 +32,7 @@ extern int of_phy_register_fixed_link(struct device_node *np); extern void of_phy_deregister_fixed_link(struct device_node *np); extern bool of_phy_is_fixed_link(struct device_node *np); -#else /* CONFIG_OF */ +#else /* CONFIG_OF_MDIO */ static inline int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) { /* -- cgit From 17487eebaf5799f7a2e78364cc94fb84e37300a2 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 23 Mar 2017 10:01:18 -0700 Subject: net: phy: MDIO_BCM_UNIMAC should depend on OF_MDIO The Broadcom MDIO UniMAC driver uses routines provided by of_mdio.c which is guarded by CONFIG_OF_MDIO. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 8dbd59baa34d..7ab4b14a43b7 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -40,7 +40,7 @@ config MDIO_BCM_IPROC config MDIO_BCM_UNIMAC tristate "Broadcom UniMAC MDIO bus controller" - depends on HAS_IOMEM + depends on HAS_IOMEM && OF_MDIO help This module provides a driver for the Broadcom UniMAC MDIO busses. This hardware can be found in the Broadcom GENET Ethernet MAC -- cgit From 90eff9096c01ba90cdae504a6b95ee87fe2556a3 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 23 Mar 2017 10:01:19 -0700 Subject: net: phy: Allow splitting MDIO bus/device support from PHYs Introduce a new configuration symbol: MDIO_DEVICE which allows building the MDIO devices and bus code, without pulling in the entire Ethernet PHY library and devices code. PHYLIB nows select MDIO_DEVICE and the relevant Makefile files are updated to reflect that. When MDIO_DEVICE (MDIO bus/device only) is selected, but not PHYLIB, we have mdio-bus.ko as a loadable module, and it does not have a module_exit() function because the safety of removing a bus class is unclear. When both MDIO_DEVICE and PHYLIB are enabled, we need to assemble everything into a common loadable module: libphy.ko because of nasty circular dependencies between phy.c, phy_device.c and mdio_bus.c which are really tough to untangle. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/Makefile | 2 +- drivers/net/phy/Kconfig | 60 +++++++++++++++++++++++----------------- drivers/net/phy/Makefile | 13 +++++++-- drivers/net/phy/mdio-boardinfo.c | 1 + drivers/net/phy/mdio_bus.c | 9 ++++++ include/linux/phy.h | 21 ++++++++++++-- 6 files changed, 76 insertions(+), 30 deletions(-) diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 98ed4d96987c..55f75aea283c 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_MII) += mii.o obj-$(CONFIG_MDIO) += mdio.o obj-$(CONFIG_NET) += Space.o loopback.o obj-$(CONFIG_NETCONSOLE) += netconsole.o -obj-$(CONFIG_PHYLIB) += phy/ +obj-$(CONFIG_MDIO_DEVICE) += phy/ obj-$(CONFIG_RIONET) += rionet.o obj-$(CONFIG_NET_TEAM) += team/ obj-$(CONFIG_TUN) += tun.o diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 7ab4b14a43b7..60ffc9da6a28 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -2,33 +2,12 @@ # PHY Layer Configuration # -menuconfig PHYLIB - tristate "PHY Device support and infrastructure" - depends on NETDEVICES +menuconfig MDIO_DEVICE + tristate "MDIO bus device drivers" help - Ethernet controllers are usually attached to PHY - devices. This option provides infrastructure for - managing PHY devices. - -if PHYLIB - -config SWPHY - bool - -config LED_TRIGGER_PHY - bool "Support LED triggers for tracking link state" - depends on LEDS_TRIGGERS - ---help--- - Adds support for a set of LED trigger events per-PHY. Link - state change will trigger the events, for consumption by an - LED class driver. There are triggers for each link speed currently - supported by the phy, and are of the form: - :: - - Where speed is in the form: - Mbps or Gbps + MDIO devices and driver infrastructure code. -comment "MDIO bus device drivers" +if MDIO_DEVICE config MDIO_BCM_IPROC tristate "Broadcom iProc MDIO bus controller" @@ -49,6 +28,7 @@ config MDIO_BCM_UNIMAC config MDIO_BITBANG tristate "Bitbanged MDIO buses" + depends on !(MDIO_DEVICE=y && PHYLIB=m) help This module implements the MDIO bus protocol in software, for use by low level drivers that export the ability to @@ -160,6 +140,36 @@ config MDIO_XGENE This module provides a driver for the MDIO busses found in the APM X-Gene SoC's. +endif + +menuconfig PHYLIB + tristate "PHY Device support and infrastructure" + depends on NETDEVICES + select MDIO_DEVICE + help + Ethernet controllers are usually attached to PHY + devices. This option provides infrastructure for + managing PHY devices. + +if PHYLIB + +config SWPHY + bool + +config LED_TRIGGER_PHY + bool "Support LED triggers for tracking link state" + depends on LEDS_TRIGGERS + ---help--- + Adds support for a set of LED trigger events per-PHY. Link + state change will trigger the events, for consumption by an + LED class driver. There are triggers for each link speed currently + supported by the phy, and are of the form: + :: + + Where speed is in the form: + Mbps or Gbps + + comment "MII PHY device drivers" config AMD_PHY diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 82d915614646..0e1ec0438c23 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,7 +1,16 @@ # Makefile for Linux PHY drivers and MDIO bus drivers -libphy-y := phy.o phy_device.o mdio_bus.o mdio_device.o \ - mdio-boardinfo.o phy-core.o +libphy-y := phy.o phy-core.o phy_device.o +mdio-bus-y += mdio_bus.o mdio_device.o mdio-boardinfo.o + +# PHYLIB implies MDIO_DEVICE, in that case, we have a bunch of circular +# dependencies that does not make it possible to split mdio-bus objects into a +# dedicated loadable module, so we bundle them all together into libphy.ko +ifdef CONFIG_PHYLIB +libphy-y += $(mdio-bus-y) +else +obj-$(CONFIG_MDIO_DEVICE) += mdio-bus.o +endif libphy-$(CONFIG_SWPHY) += swphy.o libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_led_triggers.o diff --git a/drivers/net/phy/mdio-boardinfo.c b/drivers/net/phy/mdio-boardinfo.c index 6b988f77da08..61941e29daae 100644 --- a/drivers/net/phy/mdio-boardinfo.c +++ b/drivers/net/phy/mdio-boardinfo.c @@ -84,3 +84,4 @@ int mdiobus_register_board_info(const struct mdio_board_info *info, return 0; } +EXPORT_SYMBOL(mdiobus_register_board_info); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index fa7d51f14869..46b468eb6e12 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -648,9 +648,18 @@ int __init mdio_bus_init(void) return ret; } +EXPORT_SYMBOL_GPL(mdio_bus_init); +#if IS_ENABLED(CONFIG_PHYLIB) void mdio_bus_exit(void) { class_unregister(&mdio_bus_class); bus_unregister(&mdio_bus_type); } +EXPORT_SYMBOL_GPL(mdio_bus_exit); +#else +module_init(mdio_bus_init); +/* no module_exit, intentional */ +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MDIO bus/device layer"); +#endif diff --git a/include/linux/phy.h b/include/linux/phy.h index 2efca6b39fba..624cecf69c28 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -745,8 +745,24 @@ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val); struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, bool is_c45, struct phy_c45_device_ids *c45_ids); +#if IS_ENABLED(CONFIG_PHYLIB) struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); int phy_device_register(struct phy_device *phy); +void phy_device_free(struct phy_device *phydev); +#else +static inline +struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) +{ + return NULL; +} + +static inline int phy_device_register(struct phy_device *phy) +{ + return 0; +} + +static inline void phy_device_free(struct phy_device *phydev) { } +#endif /* CONFIG_PHYLIB */ void phy_device_remove(struct phy_device *phydev); int phy_init_hw(struct phy_device *phydev); int phy_suspend(struct phy_device *phydev); @@ -827,7 +843,6 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev, int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd); int phy_start_interrupts(struct phy_device *phydev); void phy_print_status(struct phy_device *phydev); -void phy_device_free(struct phy_device *phydev); int phy_set_max_speed(struct phy_device *phydev, u32 max_speed); int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, @@ -854,8 +869,10 @@ int phy_ethtool_set_link_ksettings(struct net_device *ndev, const struct ethtool_link_ksettings *cmd); int phy_ethtool_nway_reset(struct net_device *ndev); +#if IS_ENABLED(CONFIG_PHYLIB) int __init mdio_bus_init(void); void mdio_bus_exit(void); +#endif extern struct bus_type mdio_bus_type; @@ -866,7 +883,7 @@ struct mdio_board_info { const void *platform_data; }; -#if IS_ENABLED(CONFIG_PHYLIB) +#if IS_ENABLED(CONFIG_MDIO_DEVICE) int mdiobus_register_board_info(const struct mdio_board_info *info, unsigned int n); #else -- cgit From 30defeb2fb0a6219fb2c3575deb2da207b214f86 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 23 Mar 2017 10:36:46 -0700 Subject: net: systemport: Track per TX ring statistics bcm_sysport_tx_reclaim_one() is currently summing TX bytes/packets in a way that is not SMP friendly, mutliples CPUs could run bcm_sysport_tx_reclaim_one() independently and still update stats->tx_bytes and stats->tx_packets, cloberring the other CPUs statistics. Fix this by tracking per TX rings the number of bytes, packets, dropped and errors statistics, and provide a bcm_sysport_get_nstats() function which aggregates everything and returns a consistent output. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcmsysport.c | 65 ++++++++++++++++++++++++++---- drivers/net/ethernet/broadcom/bcmsysport.h | 5 +++ 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index a68d4889f5db..986fb05529fc 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -284,6 +284,7 @@ static const struct bcm_sysport_stats bcm_sysport_gstrings_stats[] = { STAT_MIB_SOFT("alloc_rx_buff_failed", mib.alloc_rx_buff_failed), STAT_MIB_SOFT("rx_dma_failed", mib.rx_dma_failed), STAT_MIB_SOFT("tx_dma_failed", mib.tx_dma_failed), + /* Per TX-queue statistics are dynamically appended */ }; #define BCM_SYSPORT_STATS_LEN ARRAY_SIZE(bcm_sysport_gstrings_stats) @@ -338,7 +339,8 @@ static int bcm_sysport_get_sset_count(struct net_device *dev, int string_set) continue; j++; } - return j; + /* Include per-queue statistics */ + return j + dev->num_tx_queues * NUM_SYSPORT_TXQ_STAT; default: return -EOPNOTSUPP; } @@ -349,6 +351,7 @@ static void bcm_sysport_get_strings(struct net_device *dev, { struct bcm_sysport_priv *priv = netdev_priv(dev); const struct bcm_sysport_stats *s; + char buf[128]; int i, j; switch (stringset) { @@ -363,6 +366,18 @@ static void bcm_sysport_get_strings(struct net_device *dev, ETH_GSTRING_LEN); j++; } + + for (i = 0; i < dev->num_tx_queues; i++) { + snprintf(buf, sizeof(buf), "txq%d_packets", i); + memcpy(data + j * ETH_GSTRING_LEN, buf, + ETH_GSTRING_LEN); + j++; + + snprintf(buf, sizeof(buf), "txq%d_bytes", i); + memcpy(data + j * ETH_GSTRING_LEN, buf, + ETH_GSTRING_LEN); + j++; + } break; default: break; @@ -418,6 +433,7 @@ static void bcm_sysport_get_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct bcm_sysport_priv *priv = netdev_priv(dev); + struct bcm_sysport_tx_ring *ring; int i, j; if (netif_running(dev)) @@ -436,6 +452,22 @@ static void bcm_sysport_get_stats(struct net_device *dev, data[j] = *(unsigned long *)p; j++; } + + /* For SYSTEMPORT Lite since we have holes in our statistics, j would + * be equal to BCM_SYSPORT_STATS_LEN at the end of the loop, but it + * needs to point to how many total statistics we have minus the + * number of per TX queue statistics + */ + j = bcm_sysport_get_sset_count(dev, ETH_SS_STATS) - + dev->num_tx_queues * NUM_SYSPORT_TXQ_STAT; + + for (i = 0; i < dev->num_tx_queues; i++) { + ring = &priv->tx_rings[i]; + data[j] = ring->packets; + j++; + data[j] = ring->bytes; + j++; + } } static void bcm_sysport_get_wol(struct net_device *dev, @@ -746,26 +778,26 @@ next: return processed; } -static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_priv *priv, +static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_tx_ring *ring, struct bcm_sysport_cb *cb, unsigned int *bytes_compl, unsigned int *pkts_compl) { + struct bcm_sysport_priv *priv = ring->priv; struct device *kdev = &priv->pdev->dev; - struct net_device *ndev = priv->netdev; if (cb->skb) { - ndev->stats.tx_bytes += cb->skb->len; + ring->bytes += cb->skb->len; *bytes_compl += cb->skb->len; dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr), dma_unmap_len(cb, dma_len), DMA_TO_DEVICE); - ndev->stats.tx_packets++; + ring->packets++; (*pkts_compl)++; bcm_sysport_free_cb(cb); /* SKB fragment */ } else if (dma_unmap_addr(cb, dma_addr)) { - ndev->stats.tx_bytes += dma_unmap_len(cb, dma_len); + ring->bytes += dma_unmap_len(cb, dma_len); dma_unmap_page(kdev, dma_unmap_addr(cb, dma_addr), dma_unmap_len(cb, dma_len), DMA_TO_DEVICE); dma_unmap_addr_set(cb, dma_addr, 0); @@ -803,7 +835,7 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, while (last_tx_cn-- > 0) { cb = ring->cbs + last_c_index; - bcm_sysport_tx_reclaim_one(priv, cb, &bytes_compl, &pkts_compl); + bcm_sysport_tx_reclaim_one(ring, cb, &bytes_compl, &pkts_compl); ring->desc_count++; last_c_index++; @@ -1632,6 +1664,24 @@ static int bcm_sysport_change_mac(struct net_device *dev, void *p) return 0; } +static struct net_device_stats *bcm_sysport_get_nstats(struct net_device *dev) +{ + struct bcm_sysport_priv *priv = netdev_priv(dev); + unsigned long tx_bytes = 0, tx_packets = 0; + struct bcm_sysport_tx_ring *ring; + unsigned int q; + + for (q = 0; q < dev->num_tx_queues; q++) { + ring = &priv->tx_rings[q]; + tx_bytes += ring->bytes; + tx_packets += ring->packets; + } + + dev->stats.tx_bytes = tx_bytes; + dev->stats.tx_packets = tx_packets; + return &dev->stats; +} + static void bcm_sysport_netif_start(struct net_device *dev) { struct bcm_sysport_priv *priv = netdev_priv(dev); @@ -1893,6 +1943,7 @@ static const struct net_device_ops bcm_sysport_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = bcm_sysport_poll_controller, #endif + .ndo_get_stats = bcm_sysport_get_nstats, }; #define REV_FMT "v%2x.%02x" diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h index 863ddd7870b7..77a51c167a69 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.h +++ b/drivers/net/ethernet/broadcom/bcmsysport.h @@ -647,6 +647,9 @@ enum bcm_sysport_stat_type { .reg_offset = ofs, \ } +/* TX bytes and packets */ +#define NUM_SYSPORT_TXQ_STAT 2 + struct bcm_sysport_stats { char stat_string[ETH_GSTRING_LEN]; int stat_sizeof; @@ -690,6 +693,8 @@ struct bcm_sysport_tx_ring { struct bcm_sysport_cb *cbs; /* Transmit control blocks */ struct dma_desc *desc_cpu; /* CPU view of the descriptor */ struct bcm_sysport_priv *priv; /* private context backpointer */ + unsigned long packets; /* packets statistics */ + unsigned long bytes; /* bytes statistics */ }; /* Driver private structure */ -- cgit From 6baa785a9c336f69c2aa36f4949856b3bb90bde3 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 23 Mar 2017 10:36:47 -0700 Subject: net: systemport: Clear status to reduce spurious interrupts Do something similar to commit d5810ca3252a ("net: bcmgenet: clear status to reduce spurious interrupts") and clear interrupts right before servicing them. This reduces the number of interrupts by 10K interrupts/sec for a TX TCP session 1Gbits/sec. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcmsysport.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 986fb05529fc..c915bcfae0af 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -669,6 +669,9 @@ static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv, u16 len, status; struct bcm_rsb *rsb; + /* Clear status before servicing to reduce spurious interrupts */ + intrl2_0_writel(priv, INTRL2_0_RDMA_MBDONE, INTRL2_CPU_CLEAR); + /* Determine how much we should process since last call, SYSTEMPORT Lite * groups the producer and consumer indexes into the same 32-bit * which we access using RDMA_CONS_INDEX @@ -814,6 +817,13 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, struct bcm_sysport_cb *cb; u32 hw_ind; + /* Clear status before servicing to reduce spurious interrupts */ + if (!ring->priv->is_lite) + intrl2_1_writel(ring->priv, BIT(ring->index), INTRL2_CPU_CLEAR); + else + intrl2_0_writel(ring->priv, BIT(ring->index + + INTRL2_0_TDMA_MBDONE_SHIFT), INTRL2_CPU_CLEAR); + /* Compute how many descriptors have been processed since last call */ hw_ind = tdma_readl(priv, TDMA_DESC_RING_PROD_CONS_INDEX(ring->index)); c_index = (hw_ind >> RING_CONS_INDEX_SHIFT) & RING_CONS_INDEX_MASK; -- cgit From e9d7af78b22e766a72ddb956a87789249a30a470 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 23 Mar 2017 10:36:48 -0700 Subject: net: systemport: Simplify circular pointer arithmetic Similar to c298ede2fe21 ("net: bcmgenet: simplify circular pointer arithmetic") we don't need to complex arthimetic since we always have a ring size that is a power of 2. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcmsysport.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index c915bcfae0af..61e26c6b26ab 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -682,11 +682,7 @@ static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv, p_index = rdma_readl(priv, RDMA_CONS_INDEX); p_index &= RDMA_PROD_INDEX_MASK; - if (p_index < priv->rx_c_index) - to_process = (RDMA_CONS_INDEX_MASK + 1) - - priv->rx_c_index + p_index; - else - to_process = p_index - priv->rx_c_index; + to_process = (p_index - priv->rx_c_index) & RDMA_CONS_INDEX_MASK; netif_dbg(priv, rx_status, ndev, "p_index=%d rx_c_index=%d to_process=%d\n", -- cgit From dddb64bcb34615bf48a2c9cb9881eb76795cc5c5 Mon Sep 17 00:00:00 2001 From: "subashab@codeaurora.org" Date: Thu, 23 Mar 2017 13:34:16 -0600 Subject: net: Add sysctl to toggle early demux for tcp and udp Certain system process significant unconnected UDP workload. It would be preferrable to disable UDP early demux for those systems and enable it for TCP only. By disabling UDP demux, we see these slight gains on an ARM64 system- 782 -> 788Mbps unconnected single stream UDPv4 633 -> 654Mbps unconnected UDPv4 different sources The performance impact can change based on CPU architecure and cache sizes. There will not much difference seen if entire UDP hash table is in cache. Both sysctls are enabled by default to preserve existing behavior. v1->v2: Change function pointer instead of adding conditional as suggested by Stephen. v2->v3: Read once in callers to avoid issues due to compiler optimizations. Also update commit message with the tests. v3->v4: Store and use read once result instead of querying pointer again incorrectly. v4->v5: Refactor to avoid errors due to compilation with IPV6={m,n} Signed-off-by: Subash Abhinov Kasiviswanathan Suggested-by: Eric Dumazet Cc: Stephen Hemminger Cc: Tom Herbert Cc: David Miller Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 11 +++++- include/net/netns/ipv4.h | 2 + include/net/protocol.h | 7 ++-- include/net/udp.h | 1 + net/ipv4/af_inet.c | 8 +++- net/ipv4/ip_input.c | 5 ++- net/ipv4/protocol.c | 2 +- net/ipv4/sysctl_net_ipv4.c | 67 ++++++++++++++++++++++++++++++++++ net/ipv6/ip6_input.c | 6 ++- net/ipv6/protocol.c | 2 +- net/ipv6/tcp_ipv6.c | 3 +- net/ipv6/udp.c | 3 +- 12 files changed, 103 insertions(+), 14 deletions(-) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index eaee2c8d4c00..b1c6500e7a8d 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -856,12 +856,21 @@ ip_dynaddr - BOOLEAN ip_early_demux - BOOLEAN Optimize input packet processing down to one demux for certain kinds of local sockets. Currently we only do this - for established TCP sockets. + for established TCP and connected UDP sockets. It may add an additional cost for pure routing workloads that reduces overall throughput, in such case you should disable it. Default: 1 +tcp_early_demux - BOOLEAN + Enable early demux for established TCP sockets. + Default: 1 + +udp_early_demux - BOOLEAN + Enable early demux for connected UDP sockets. Disable this if + your system could experience more unconnected load. + Default: 1 + icmp_echo_ignore_all - BOOLEAN If set non-zero, then the kernel will ignore all ICMP ECHO requests sent to it. diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index a0e89190a3e9..cd686c4fb32d 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -95,6 +95,8 @@ struct netns_ipv4 { /* Shall we try to damage output packets if routing dev changes? */ int sysctl_ip_dynaddr; int sysctl_ip_early_demux; + int sysctl_tcp_early_demux; + int sysctl_udp_early_demux; int sysctl_fwmark_reflect; int sysctl_tcp_fwmark_accept; diff --git a/include/net/protocol.h b/include/net/protocol.h index bf36ca34af7a..65ba335b0e7e 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -40,6 +40,7 @@ /* This is used to register protocols. */ struct net_protocol { void (*early_demux)(struct sk_buff *skb); + void (*early_demux_handler)(struct sk_buff *skb); int (*handler)(struct sk_buff *skb); void (*err_handler)(struct sk_buff *skb, u32 info); unsigned int no_policy:1, @@ -54,7 +55,7 @@ struct net_protocol { #if IS_ENABLED(CONFIG_IPV6) struct inet6_protocol { void (*early_demux)(struct sk_buff *skb); - + void (*early_demux_handler)(struct sk_buff *skb); int (*handler)(struct sk_buff *skb); void (*err_handler)(struct sk_buff *skb, @@ -92,12 +93,12 @@ struct inet_protosw { #define INET_PROTOSW_PERMANENT 0x02 /* Permanent protocols are unremovable. */ #define INET_PROTOSW_ICSK 0x04 /* Is this an inet_connection_sock? */ -extern const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS]; +extern struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS]; extern const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS]; extern const struct net_offload __rcu *inet6_offloads[MAX_INET_PROTOS]; #if IS_ENABLED(CONFIG_IPV6) -extern const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS]; +extern struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS]; #endif int inet_add_protocol(const struct net_protocol *prot, unsigned char num); diff --git a/include/net/udp.h b/include/net/udp.h index c9d8b8e848e0..3391dbd73959 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -372,4 +372,5 @@ void udp_encap_enable(void); #if IS_ENABLED(CONFIG_IPV6) void udpv6_encap_enable(void); #endif + #endif /* _UDP_H */ diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 6b1fc6e4278e..d1a11707a126 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1599,8 +1599,9 @@ static const struct net_protocol igmp_protocol = { }; #endif -static const struct net_protocol tcp_protocol = { +static struct net_protocol tcp_protocol = { .early_demux = tcp_v4_early_demux, + .early_demux_handler = tcp_v4_early_demux, .handler = tcp_v4_rcv, .err_handler = tcp_v4_err, .no_policy = 1, @@ -1608,8 +1609,9 @@ static const struct net_protocol tcp_protocol = { .icmp_strict_tag_validation = 1, }; -static const struct net_protocol udp_protocol = { +static struct net_protocol udp_protocol = { .early_demux = udp_v4_early_demux, + .early_demux_handler = udp_v4_early_demux, .handler = udp_rcv, .err_handler = udp_err, .no_policy = 1, @@ -1720,6 +1722,8 @@ static __net_init int inet_init_net(struct net *net) net->ipv4.sysctl_ip_default_ttl = IPDEFTTL; net->ipv4.sysctl_ip_dynaddr = 0; net->ipv4.sysctl_ip_early_demux = 1; + net->ipv4.sysctl_udp_early_demux = 1; + net->ipv4.sysctl_tcp_early_demux = 1; #ifdef CONFIG_SYSCTL net->ipv4.sysctl_ip_prot_sock = PROT_SOCK; #endif diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index d6feabb03516..fa2dc8f692c6 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -313,6 +313,7 @@ static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb) const struct iphdr *iph = ip_hdr(skb); struct rtable *rt; struct net_device *dev = skb->dev; + void (*edemux)(struct sk_buff *skb); /* if ingress device is enslaved to an L3 master device pass the * skb to its handler for processing @@ -329,8 +330,8 @@ static int ip_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb) int protocol = iph->protocol; ipprot = rcu_dereference(inet_protos[protocol]); - if (ipprot && ipprot->early_demux) { - ipprot->early_demux(skb); + if (ipprot && (edemux = READ_ONCE(ipprot->early_demux))) { + edemux(skb); /* must reload iph, skb->head might have changed */ iph = ip_hdr(skb); } diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c index 4b7c0ec65251..32a691b7ce2c 100644 --- a/net/ipv4/protocol.c +++ b/net/ipv4/protocol.c @@ -28,7 +28,7 @@ #include #include -const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly; +struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly; const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS] __read_mostly; EXPORT_SYMBOL(inet_offloads); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 711c3e2e17b1..6fb25693c00b 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -24,6 +24,7 @@ #include #include #include +#include static int zero; static int one = 1; @@ -294,6 +295,58 @@ bad_key: return ret; } +static void proc_configure_early_demux(int enabled, int protocol) +{ + struct net_protocol *ipprot; +#if IS_ENABLED(CONFIG_IPV6) + struct inet6_protocol *ip6prot; +#endif + + ipprot = rcu_dereference(inet_protos[protocol]); + if (ipprot) + ipprot->early_demux = enabled ? ipprot->early_demux_handler : + NULL; + +#if IS_ENABLED(CONFIG_IPV6) + ip6prot = rcu_dereference(inet6_protos[protocol]); + if (ip6prot) + ip6prot->early_demux = enabled ? ip6prot->early_demux_handler : + NULL; +#endif +} + +static int proc_tcp_early_demux(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int ret = 0; + + ret = proc_dointvec(table, write, buffer, lenp, ppos); + + if (write && !ret) { + int enabled = init_net.ipv4.sysctl_tcp_early_demux; + + proc_configure_early_demux(enabled, IPPROTO_TCP); + } + + return ret; +} + +static int proc_udp_early_demux(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int ret = 0; + + ret = proc_dointvec(table, write, buffer, lenp, ppos); + + if (write && !ret) { + int enabled = init_net.ipv4.sysctl_udp_early_demux; + + proc_configure_early_demux(enabled, IPPROTO_UDP); + } + + return ret; +} + static struct ctl_table ipv4_table[] = { { .procname = "tcp_timestamps", @@ -749,6 +802,20 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "udp_early_demux", + .data = &init_net.ipv4.sysctl_udp_early_demux, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_udp_early_demux + }, + { + .procname = "tcp_early_demux", + .data = &init_net.ipv4.sysctl_tcp_early_demux, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_tcp_early_demux + }, { .procname = "ip_default_ttl", .data = &init_net.ipv4.sysctl_ip_default_ttl, diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index aacfb4bce153..b04539dd4629 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -49,6 +49,8 @@ int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb) { + void (*edemux)(struct sk_buff *skb); + /* if ingress device is enslaved to an L3 master device pass the * skb to its handler for processing */ @@ -60,8 +62,8 @@ int ip6_rcv_finish(struct net *net, struct sock *sk, struct sk_buff *skb) const struct inet6_protocol *ipprot; ipprot = rcu_dereference(inet6_protos[ipv6_hdr(skb)->nexthdr]); - if (ipprot && ipprot->early_demux) - ipprot->early_demux(skb); + if (ipprot && (edemux = READ_ONCE(ipprot->early_demux))) + edemux(skb); } if (!skb_valid_dst(skb)) ip6_route_input(skb); diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c index e3770abe688a..b5d54d4f995c 100644 --- a/net/ipv6/protocol.c +++ b/net/ipv6/protocol.c @@ -26,7 +26,7 @@ #include #if IS_ENABLED(CONFIG_IPV6) -const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS] __read_mostly; +struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS] __read_mostly; EXPORT_SYMBOL(inet6_protos); int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char protocol) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 0f08d718a002..031a8c019f7a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1925,8 +1925,9 @@ struct proto tcpv6_prot = { .diag_destroy = tcp_abort, }; -static const struct inet6_protocol tcpv6_protocol = { +static struct inet6_protocol tcpv6_protocol = { .early_demux = tcp_v6_early_demux, + .early_demux_handler = tcp_v6_early_demux, .handler = tcp_v6_rcv, .err_handler = tcp_v6_err, .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index b793ed1d2a36..fd4b1c98a472 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1436,8 +1436,9 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname, } #endif -static const struct inet6_protocol udpv6_protocol = { +static struct inet6_protocol udpv6_protocol = { .early_demux = udp_v6_early_demux, + .early_demux_handler = udp_v6_early_demux, .handler = udpv6_rcv, .err_handler = udpv6_err, .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, -- cgit From 7cc61db9c7e6c985798d5419207fbd8cdb505c7b Mon Sep 17 00:00:00 2001 From: Felix Manlunas Date: Thu, 23 Mar 2017 13:26:28 -0700 Subject: liquidio: do not reset Octeon if NIC firmware was preloaded The PF driver is incorrectly resetting Octeon when the module parameter "fw_type=none" is there. "fw_type=none" means the PF should not load any firmware to the NIC because Octeon is already running preloaded firmware. Fix it by putting an if (fw_type != none) around the reset code. Because the Octeon reset is now conditionally gone, when unloading the driver, conditionally send the RESET_PF command to the firmware who will then free up PF-related data structures. Signed-off-by: Felix Manlunas Signed-off-by: Satanand Burla Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 45 +++++++++++++++------- .../net/ethernet/cavium/liquidio/liquidio_common.h | 1 + 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 10732e0e48cf..b06be91d9d2e 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -1380,6 +1380,12 @@ liquidio_probe(struct pci_dev *pdev, return 0; } +static bool fw_type_is_none(void) +{ + return strncmp(fw_type, LIO_FW_NAME_TYPE_NONE, + sizeof(LIO_FW_NAME_TYPE_NONE)) == 0; +} + /** *\brief Destroy resources associated with octeon device * @param pdev PCI device structure @@ -1522,9 +1528,12 @@ static void octeon_destroy_resources(struct octeon_device *oct) /* fallthrough */ case OCT_DEV_PCI_MAP_DONE: - /* Soft reset the octeon device before exiting */ - if ((!OCTEON_CN23XX_PF(oct)) || !oct->octeon_id) - oct->fn_list.soft_reset(oct); + if (!fw_type_is_none()) { + /* Soft reset the octeon device before exiting */ + if (!OCTEON_CN23XX_PF(oct) || + (OCTEON_CN23XX_PF(oct) && !oct->octeon_id)) + oct->fn_list.soft_reset(oct); + } octeon_unmap_pci_barx(oct, 0); octeon_unmap_pci_barx(oct, 1); @@ -1657,6 +1666,15 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) if (atomic_read(&lio->ifstate) & LIO_IFSTATE_RUNNING) liquidio_stop(netdev); + if (fw_type_is_none()) { + struct octnic_ctrl_pkt nctrl; + + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + nctrl.ncmd.s.cmd = OCTNET_CMD_RESET_PF; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + octnet_send_nic_ctrl_pkt(oct, &nctrl); + } + if (oct->props[lio->ifidx].napi_enabled == 1) { list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) napi_disable(napi); @@ -2142,8 +2160,7 @@ static int load_firmware(struct octeon_device *oct) char fw_name[LIO_MAX_FW_FILENAME_LEN]; char *tmp_fw_type; - if (strncmp(fw_type, LIO_FW_NAME_TYPE_NONE, - sizeof(LIO_FW_NAME_TYPE_NONE)) == 0) { + if (fw_type_is_none()) { dev_info(&oct->pci_dev->dev, "Skipping firmware load\n"); return ret; } @@ -4479,14 +4496,16 @@ static int octeon_device_init(struct octeon_device *octeon_dev) if (OCTEON_CN23XX_PF(octeon_dev)) { if (!cn23xx_fw_loaded(octeon_dev)) { fw_loaded = 0; - /* Do a soft reset of the Octeon device. */ - if (octeon_dev->fn_list.soft_reset(octeon_dev)) - return 1; - /* things might have changed */ - if (!cn23xx_fw_loaded(octeon_dev)) - fw_loaded = 0; - else - fw_loaded = 1; + if (!fw_type_is_none()) { + /* Do a soft reset of the Octeon device. */ + if (octeon_dev->fn_list.soft_reset(octeon_dev)) + return 1; + /* things might have changed */ + if (!cn23xx_fw_loaded(octeon_dev)) + fw_loaded = 0; + else + fw_loaded = 1; + } } else { fw_loaded = 1; } diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index 4a07c0ac9fab..17c9aff753d1 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -186,6 +186,7 @@ static inline void add_sg_size(struct octeon_sg_entry *sg_entry, #define OCTNET_CMD_Q 0 /* NIC Command types */ +#define OCTNET_CMD_RESET_PF 0x0 #define OCTNET_CMD_CHANGE_MTU 0x1 #define OCTNET_CMD_CHANGE_MACADDR 0x2 #define OCTNET_CMD_CHANGE_DEVFLAGS 0x3 -- cgit From e013fb7c4c9fa8aa914d85d8b466c990b2eaebb6 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 24 Mar 2017 00:58:26 +0300 Subject: net: make in_aton() 32-bit internally Converting IPv4 address doesn't need 64-bit arithmetic. Space savings: 10 bytes! add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-10 (-10) function old new delta in_aton 96 86 -10 Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- net/core/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/utils.c b/net/core/utils.c index 6592d7bbed39..d758880c09a7 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -51,7 +51,7 @@ EXPORT_SYMBOL(net_ratelimit); __be32 in_aton(const char *str) { - unsigned long l; + unsigned int l; unsigned int val; int i; -- cgit From c48367427a39ea0b85c7cf018fe4256627abfd9e Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Fri, 24 Mar 2017 07:05:12 +0800 Subject: tcp: sysctl: Fix a race to avoid unexpected 0 window from space Because sysctl_tcp_adv_win_scale could be changed any time, so there is one race in tcp_win_from_space. For example, 1.sysctl_tcp_adv_win_scale<=0 (sysctl_tcp_adv_win_scale is negative now) 2.space>>(-sysctl_tcp_adv_win_scale) (sysctl_tcp_adv_win_scale is postive now) As a result, tcp_win_from_space returns 0. It is unexpected. Certainly if the compiler put the sysctl_tcp_adv_win_scale into one register firstly, then use the register directly, it would be ok. But we could not depend on the compiler behavior. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- include/net/tcp.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index e614ad4d613e..582e3772c0d9 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1248,9 +1248,11 @@ void tcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, static inline int tcp_win_from_space(int space) { - return sysctl_tcp_adv_win_scale<=0 ? - (space>>(-sysctl_tcp_adv_win_scale)) : - space - (space>>sysctl_tcp_adv_win_scale); + int tcp_adv_win_scale = sysctl_tcp_adv_win_scale; + + return tcp_adv_win_scale <= 0 ? + (space>>(-tcp_adv_win_scale)) : + space - (space>>tcp_adv_win_scale); } /* Note: caller must be prepared to deal with negative returns */ -- cgit From 6a18c312320766b6d13a12c681f77df04894f1a5 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 23 Mar 2017 19:02:27 -0600 Subject: net: mpls: Fix setting ttl_propagate for rt2 Fix copy and paste error setting rt_ttl_propagate. Fixes: 5b441ac8784c1 ("mpls: allow TTL propagation to IP packets to be configured") Signed-off-by: David Ahern Acked-by: Robert Shearman Signed-off-by: David S. Miller --- net/mpls/af_mpls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 82589b2abf3c..cd8be8d5e4ad 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1949,7 +1949,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo); rt2->rt_protocol = RTPROT_KERNEL; rt2->rt_payload_type = MPT_IPV6; - rt0->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT; + rt2->rt_ttl_propagate = MPLS_TTL_PROP_DEFAULT; rt2->rt_nh->nh_via_table = NEIGH_LINK_TABLE; rt2->rt_nh->nh_via_alen = lo->addr_len; memcpy(__mpls_nh_via(rt2, rt2->rt_nh), lo->dev_addr, -- cgit From 8494ab06e06a8ef60db9ad678eb4bcd8def4c85b Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 24 Mar 2017 08:02:47 +0100 Subject: mlxsw: spectrum_router: Query number of LPM trees from firmware Instead of hard coding the number of LPM trees in the driver, query it from the firmware, as it may change in future devices. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/resources.h | 2 + drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 9 ++--- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 43 ++++++++++++++++++---- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h index 905a8e269f87..41553a79d99d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/resources.h +++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h @@ -61,6 +61,7 @@ enum mlxsw_res_id { MLXSW_RES_ID_MAX_CPU_POLICERS, MLXSW_RES_ID_MAX_VRS, MLXSW_RES_ID_MAX_RIFS, + MLXSW_RES_ID_MAX_LPM_TREES, /* Internal resources. * Determined by the SW, not queried from the HW. @@ -95,6 +96,7 @@ static u16 mlxsw_res_ids[] = { [MLXSW_RES_ID_MAX_CPU_POLICERS] = 0x2A13, [MLXSW_RES_ID_MAX_VRS] = 0x2C01, [MLXSW_RES_ID_MAX_RIFS] = 0x2C02, + [MLXSW_RES_ID_MAX_LPM_TREES] = 0x2C30, }; struct mlxsw_res { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index bffd9e698eff..b1713a4346be 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -63,10 +63,6 @@ #define MLXSW_SP_PORTS_PER_CLUSTER_MAX 4 -#define MLXSW_SP_LPM_TREE_MIN 2 /* trees 0 and 1 are reserved */ -#define MLXSW_SP_LPM_TREE_MAX 22 -#define MLXSW_SP_LPM_TREE_COUNT (MLXSW_SP_LPM_TREE_MAX - MLXSW_SP_LPM_TREE_MIN) - #define MLXSW_SP_PORT_BASE_SPEED 25000 /* Mb/s */ #define MLXSW_SP_BYTES_PER_CELL 96 @@ -230,11 +226,14 @@ struct mlxsw_sp_port_mall_tc_entry { }; struct mlxsw_sp_router { - struct mlxsw_sp_lpm_tree lpm_trees[MLXSW_SP_LPM_TREE_COUNT]; struct mlxsw_sp_vr *vrs; struct rhashtable neigh_ht; struct rhashtable nexthop_group_ht; struct rhashtable nexthop_ht; + struct { + struct mlxsw_sp_lpm_tree *trees; + unsigned int tree_count; + } lpm; struct { struct delayed_work dw; unsigned long interval; /* ms */ diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index fe4a55e3272b..2c666f9760b7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -206,8 +206,8 @@ mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp) static struct mlxsw_sp_lpm_tree *lpm_tree; int i; - for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) { - lpm_tree = &mlxsw_sp->router.lpm_trees[i]; + for (i = 0; i < mlxsw_sp->router.lpm.tree_count; i++) { + lpm_tree = &mlxsw_sp->router.lpm.trees[i]; if (lpm_tree->ref_count == 0) return lpm_tree; } @@ -303,8 +303,8 @@ mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_lpm_tree *lpm_tree; int i; - for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) { - lpm_tree = &mlxsw_sp->router.lpm_trees[i]; + for (i = 0; i < mlxsw_sp->router.lpm.tree_count; i++) { + lpm_tree = &mlxsw_sp->router.lpm.trees[i]; if (lpm_tree->ref_count != 0 && lpm_tree->proto == proto && mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage, @@ -329,15 +329,36 @@ static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp, return 0; } -static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp) +#define MLXSW_SP_LPM_TREE_MIN 2 /* trees 0 and 1 are reserved */ + +static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp) { struct mlxsw_sp_lpm_tree *lpm_tree; + u64 max_trees; int i; - for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) { - lpm_tree = &mlxsw_sp->router.lpm_trees[i]; + if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES)) + return -EIO; + + max_trees = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_LPM_TREES); + mlxsw_sp->router.lpm.tree_count = max_trees - MLXSW_SP_LPM_TREE_MIN; + mlxsw_sp->router.lpm.trees = kcalloc(mlxsw_sp->router.lpm.tree_count, + sizeof(struct mlxsw_sp_lpm_tree), + GFP_KERNEL); + if (!mlxsw_sp->router.lpm.trees) + return -ENOMEM; + + for (i = 0; i < mlxsw_sp->router.lpm.tree_count; i++) { + lpm_tree = &mlxsw_sp->router.lpm.trees[i]; lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN; } + + return 0; +} + +static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp) +{ + kfree(mlxsw_sp->router.lpm.trees); } static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr) @@ -3372,7 +3393,10 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) if (err) goto err_nexthop_group_ht_init; - mlxsw_sp_lpm_init(mlxsw_sp); + err = mlxsw_sp_lpm_init(mlxsw_sp); + if (err) + goto err_lpm_init; + err = mlxsw_sp_vrs_init(mlxsw_sp); if (err) goto err_vrs_init; @@ -3394,6 +3418,8 @@ err_register_fib_notifier: err_neigh_init: mlxsw_sp_vrs_fini(mlxsw_sp); err_vrs_init: + mlxsw_sp_lpm_fini(mlxsw_sp); +err_lpm_init: rhashtable_destroy(&mlxsw_sp->router.nexthop_group_ht); err_nexthop_group_ht_init: rhashtable_destroy(&mlxsw_sp->router.nexthop_ht); @@ -3407,6 +3433,7 @@ void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp) unregister_fib_notifier(&mlxsw_sp->fib_nb); mlxsw_sp_neigh_fini(mlxsw_sp); mlxsw_sp_vrs_fini(mlxsw_sp); + mlxsw_sp_lpm_fini(mlxsw_sp); rhashtable_destroy(&mlxsw_sp->router.nexthop_group_ht); rhashtable_destroy(&mlxsw_sp->router.nexthop_ht); __mlxsw_sp_router_fini(mlxsw_sp); -- cgit From 5ec2ee7dd26452c2852b395b323b0260c27c2e25 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 24 Mar 2017 08:02:48 +0100 Subject: mlxsw: Query maximum number of ports from firmware We currently hard code the maximum number of ports in the driver, but this may change in future devices, so query it from the firmware instead. Fallback to a maximum of 64 ports in case this number can't be queried. This should only happen in SwitchX-2 for which this number is correct. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/core.c | 46 +++++++++++++-- drivers/net/ethernet/mellanox/mlxsw/core.h | 2 + drivers/net/ethernet/mellanox/mlxsw/port.h | 10 +--- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 16 +++++- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 12 ++-- .../net/ethernet/mellanox/mlxsw/spectrum_buffers.c | 67 ++++++++++++++++------ .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 8 ++- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 2 +- drivers/net/ethernet/mellanox/mlxsw/switchx2.c | 7 ++- 9 files changed, 125 insertions(+), 45 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index fb8187d6ca13..affe84eb4bff 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -110,11 +110,42 @@ struct mlxsw_core { struct mlxsw_res res; struct mlxsw_hwmon *hwmon; struct mlxsw_thermal *thermal; - struct mlxsw_core_port ports[MLXSW_PORT_MAX_PORTS]; + struct mlxsw_core_port *ports; + unsigned int max_ports; unsigned long driver_priv[0]; /* driver_priv has to be always the last item */ }; +#define MLXSW_PORT_MAX_PORTS_DEFAULT 0x40 + +static int mlxsw_ports_init(struct mlxsw_core *mlxsw_core) +{ + /* Switch ports are numbered from 1 to queried value */ + if (MLXSW_CORE_RES_VALID(mlxsw_core, MAX_SYSTEM_PORT)) + mlxsw_core->max_ports = MLXSW_CORE_RES_GET(mlxsw_core, + MAX_SYSTEM_PORT) + 1; + else + mlxsw_core->max_ports = MLXSW_PORT_MAX_PORTS_DEFAULT + 1; + + mlxsw_core->ports = kcalloc(mlxsw_core->max_ports, + sizeof(struct mlxsw_core_port), GFP_KERNEL); + if (!mlxsw_core->ports) + return -ENOMEM; + + return 0; +} + +static void mlxsw_ports_fini(struct mlxsw_core *mlxsw_core) +{ + kfree(mlxsw_core->ports); +} + +unsigned int mlxsw_core_max_ports(const struct mlxsw_core *mlxsw_core) +{ + return mlxsw_core->max_ports; +} +EXPORT_SYMBOL(mlxsw_core_max_ports); + void *mlxsw_core_driver_priv(struct mlxsw_core *mlxsw_core) { return mlxsw_core->driver_priv; @@ -733,7 +764,7 @@ static int mlxsw_devlink_port_split(struct devlink *devlink, { struct mlxsw_core *mlxsw_core = devlink_priv(devlink); - if (port_index >= MLXSW_PORT_MAX_PORTS) + if (port_index >= mlxsw_core->max_ports) return -EINVAL; if (!mlxsw_core->driver->port_split) return -EOPNOTSUPP; @@ -745,7 +776,7 @@ static int mlxsw_devlink_port_unsplit(struct devlink *devlink, { struct mlxsw_core *mlxsw_core = devlink_priv(devlink); - if (port_index >= MLXSW_PORT_MAX_PORTS) + if (port_index >= mlxsw_core->max_ports) return -EINVAL; if (!mlxsw_core->driver->port_unsplit) return -EOPNOTSUPP; @@ -972,6 +1003,10 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, if (err) goto err_bus_init; + err = mlxsw_ports_init(mlxsw_core); + if (err) + goto err_ports_init; + if (MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG) && MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG_MEMBERS)) { alloc_size = sizeof(u8) * @@ -1019,6 +1054,8 @@ err_devlink_register: err_emad_init: kfree(mlxsw_core->lag.mapping); err_alloc_lag_mapping: + mlxsw_ports_fini(mlxsw_core); +err_ports_init: mlxsw_bus->fini(bus_priv); err_bus_init: devlink_free(devlink); @@ -1039,6 +1076,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core) devlink_unregister(devlink); mlxsw_emad_fini(mlxsw_core); kfree(mlxsw_core->lag.mapping); + mlxsw_ports_fini(mlxsw_core); mlxsw_core->bus->fini(mlxsw_core->bus_priv); devlink_free(devlink); mlxsw_core_driver_put(device_kind); @@ -1508,7 +1546,7 @@ void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb, __func__, local_port, rx_info->trap_id); if ((rx_info->trap_id >= MLXSW_TRAP_ID_MAX) || - (local_port >= MLXSW_PORT_MAX_PORTS)) + (local_port >= mlxsw_core->max_ports)) goto drop; rcu_read_lock(); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h index cf38cf9027f8..7fb35395adf5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core.h @@ -57,6 +57,8 @@ struct mlxsw_driver; struct mlxsw_bus; struct mlxsw_bus_info; +unsigned int mlxsw_core_max_ports(const struct mlxsw_core *mlxsw_core); + void *mlxsw_core_driver_priv(struct mlxsw_core *mlxsw_core); int mlxsw_core_driver_register(struct mlxsw_driver *mlxsw_driver); diff --git a/drivers/net/ethernet/mellanox/mlxsw/port.h b/drivers/net/ethernet/mellanox/mlxsw/port.h index 3d42146473b3..c580abba8d34 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/port.h +++ b/drivers/net/ethernet/mellanox/mlxsw/port.h @@ -49,20 +49,12 @@ #define MLXSW_PORT_MID 0xd000 -#define MLXSW_PORT_MAX_PHY_PORTS 0x40 -#define MLXSW_PORT_MAX_PORTS (MLXSW_PORT_MAX_PHY_PORTS + 1) - #define MLXSW_PORT_MAX_IB_PHY_PORTS 36 #define MLXSW_PORT_MAX_IB_PORTS (MLXSW_PORT_MAX_IB_PHY_PORTS + 1) -#define MLXSW_PORT_DEVID_BITS_OFFSET 10 -#define MLXSW_PORT_PHY_BITS_OFFSET 4 -#define MLXSW_PORT_PHY_BITS_MASK (MLXSW_PORT_MAX_PHY_PORTS - 1) - #define MLXSW_PORT_CPU_PORT 0x0 -#define MLXSW_PORT_ROUTER_PORT (MLXSW_PORT_MAX_PHY_PORTS + 2) -#define MLXSW_PORT_DONT_CARE (MLXSW_PORT_MAX_PORTS) +#define MLXSW_PORT_DONT_CARE 0xFF #define MLXSW_PORT_MODULE_MAX_WIDTH 4 diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 3ed77e10b4d6..7d0bd027cf1e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2595,25 +2595,33 @@ static void mlxsw_sp_ports_remove(struct mlxsw_sp *mlxsw_sp) { int i; - for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) + for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++) if (mlxsw_sp_port_created(mlxsw_sp, i)) mlxsw_sp_port_remove(mlxsw_sp, i); + kfree(mlxsw_sp->port_to_module); kfree(mlxsw_sp->ports); } static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp) { + unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core); u8 module, width, lane; size_t alloc_size; int i; int err; - alloc_size = sizeof(struct mlxsw_sp_port *) * MLXSW_PORT_MAX_PORTS; + alloc_size = sizeof(struct mlxsw_sp_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++) { + mlxsw_sp->port_to_module = kcalloc(max_ports, sizeof(u8), GFP_KERNEL); + if (!mlxsw_sp->port_to_module) { + err = -ENOMEM; + goto err_port_to_module_alloc; + } + + for (i = 1; i < max_ports; i++) { err = mlxsw_sp_port_module_info_get(mlxsw_sp, i, &module, &width, &lane); if (err) @@ -2633,6 +2641,8 @@ err_port_module_info_get: for (i--; i >= 1; i--) if (mlxsw_sp_port_created(mlxsw_sp, i)) mlxsw_sp_port_remove(mlxsw_sp, i); + kfree(mlxsw_sp->port_to_module); +err_port_to_module_alloc: kfree(mlxsw_sp->ports); return err; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index b1713a4346be..80a0d7d3e1b5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -152,12 +152,14 @@ struct mlxsw_sp_sb_pm { #define MLXSW_SP_SB_POOL_COUNT 4 #define MLXSW_SP_SB_TC_COUNT 8 +struct mlxsw_sp_sb_port { + struct mlxsw_sp_sb_cm cms[2][MLXSW_SP_SB_TC_COUNT]; + struct mlxsw_sp_sb_pm pms[2][MLXSW_SP_SB_POOL_COUNT]; +}; + struct mlxsw_sp_sb { struct mlxsw_sp_sb_pr prs[2][MLXSW_SP_SB_POOL_COUNT]; - struct { - struct mlxsw_sp_sb_cm cms[2][MLXSW_SP_SB_TC_COUNT]; - struct mlxsw_sp_sb_pm pms[2][MLXSW_SP_SB_POOL_COUNT]; - } ports[MLXSW_PORT_MAX_PORTS]; + struct mlxsw_sp_sb_port *ports; }; #define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE) @@ -273,7 +275,7 @@ struct mlxsw_sp { u32 ageing_time; struct mlxsw_sp_upper master_bridge; struct mlxsw_sp_upper *lags; - u8 port_to_module[MLXSW_PORT_MAX_PORTS]; + u8 *port_to_module; struct mlxsw_sp_sb sb; struct mlxsw_sp_router router; struct mlxsw_sp_acl *acl; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c index a7468262f118..3ab853db59ee 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c @@ -209,11 +209,25 @@ static int mlxsw_sp_port_headroom_init(struct mlxsw_sp_port *mlxsw_sp_port) return mlxsw_sp_port_pb_prio_init(mlxsw_sp_port); } -#define MLXSW_SP_SB_PR_INGRESS_SIZE \ - (15000000 - (2 * 20000 * MLXSW_PORT_MAX_PORTS)) +static int mlxsw_sp_sb_ports_init(struct mlxsw_sp *mlxsw_sp) +{ + unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core); + + mlxsw_sp->sb.ports = kcalloc(max_ports, sizeof(struct mlxsw_sp_sb_port), + GFP_KERNEL); + if (!mlxsw_sp->sb.ports) + return -ENOMEM; + return 0; +} + +static void mlxsw_sp_sb_ports_fini(struct mlxsw_sp *mlxsw_sp) +{ + kfree(mlxsw_sp->sb.ports); +} + +#define MLXSW_SP_SB_PR_INGRESS_SIZE 12440000 #define MLXSW_SP_SB_PR_INGRESS_MNG_SIZE (200 * 1000) -#define MLXSW_SP_SB_PR_EGRESS_SIZE \ - (14000000 - (8 * 1500 * MLXSW_PORT_MAX_PORTS)) +#define MLXSW_SP_SB_PR_EGRESS_SIZE 13232000 #define MLXSW_SP_SB_PR(_mode, _size) \ { \ @@ -528,26 +542,41 @@ int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp) { int err; - err = mlxsw_sp_sb_prs_init(mlxsw_sp); + err = mlxsw_sp_sb_ports_init(mlxsw_sp); if (err) return err; + err = mlxsw_sp_sb_prs_init(mlxsw_sp); + if (err) + goto err_sb_prs_init; err = mlxsw_sp_cpu_port_sb_cms_init(mlxsw_sp); if (err) - return err; + goto err_sb_cpu_port_sb_cms_init; err = mlxsw_sp_sb_mms_init(mlxsw_sp); if (err) - return err; - return devlink_sb_register(priv_to_devlink(mlxsw_sp->core), 0, - MLXSW_SP_SB_SIZE, - MLXSW_SP_SB_POOL_COUNT, - MLXSW_SP_SB_POOL_COUNT, - MLXSW_SP_SB_TC_COUNT, - MLXSW_SP_SB_TC_COUNT); + goto err_sb_mms_init; + err = devlink_sb_register(priv_to_devlink(mlxsw_sp->core), 0, + MLXSW_SP_SB_SIZE, + MLXSW_SP_SB_POOL_COUNT, + MLXSW_SP_SB_POOL_COUNT, + MLXSW_SP_SB_TC_COUNT, + MLXSW_SP_SB_TC_COUNT); + if (err) + goto err_devlink_sb_register; + + return 0; + +err_devlink_sb_register: +err_sb_mms_init: +err_sb_cpu_port_sb_cms_init: +err_sb_prs_init: + mlxsw_sp_sb_ports_fini(mlxsw_sp); + return err; } void mlxsw_sp_buffers_fini(struct mlxsw_sp *mlxsw_sp) { devlink_sb_unregister(priv_to_devlink(mlxsw_sp->core), 0); + mlxsw_sp_sb_ports_fini(mlxsw_sp); } int mlxsw_sp_port_buffers_init(struct mlxsw_sp_port *mlxsw_sp_port) @@ -761,7 +790,7 @@ static void mlxsw_sp_sb_sr_occ_query_cb(struct mlxsw_core *mlxsw_core, masked_count = 0; for (local_port = cb_ctx.local_port_1; - local_port < MLXSW_PORT_MAX_PORTS; local_port++) { + local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) { if (!mlxsw_sp->ports[local_port]) continue; for (i = 0; i < MLXSW_SP_SB_TC_COUNT; i++) { @@ -775,7 +804,7 @@ static void mlxsw_sp_sb_sr_occ_query_cb(struct mlxsw_core *mlxsw_core, } masked_count = 0; for (local_port = cb_ctx.local_port_1; - local_port < MLXSW_PORT_MAX_PORTS; local_port++) { + local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) { if (!mlxsw_sp->ports[local_port]) continue; for (i = 0; i < MLXSW_SP_SB_TC_COUNT; i++) { @@ -817,7 +846,7 @@ next_batch: mlxsw_reg_sbsr_pg_buff_mask_set(sbsr_pl, i, 1); mlxsw_reg_sbsr_tclass_mask_set(sbsr_pl, i, 1); } - for (; local_port < MLXSW_PORT_MAX_PORTS; local_port++) { + for (; local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) { if (!mlxsw_sp->ports[local_port]) continue; mlxsw_reg_sbsr_ingress_port_mask_set(sbsr_pl, local_port, 1); @@ -847,7 +876,7 @@ do_query: cb_priv); if (err) goto out; - if (local_port < MLXSW_PORT_MAX_PORTS) + if (local_port < mlxsw_core_max_ports(mlxsw_core)) goto next_batch; out: @@ -882,7 +911,7 @@ next_batch: mlxsw_reg_sbsr_pg_buff_mask_set(sbsr_pl, i, 1); mlxsw_reg_sbsr_tclass_mask_set(sbsr_pl, i, 1); } - for (; local_port < MLXSW_PORT_MAX_PORTS; local_port++) { + for (; local_port < mlxsw_core_max_ports(mlxsw_core); local_port++) { if (!mlxsw_sp->ports[local_port]) continue; mlxsw_reg_sbsr_ingress_port_mask_set(sbsr_pl, local_port, 1); @@ -908,7 +937,7 @@ do_query: &bulk_list, NULL, 0); if (err) goto out; - if (local_port < MLXSW_PORT_MAX_PORTS) + if (local_port < mlxsw_core_max_ports(mlxsw_core)) goto next_batch; out: diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 2c666f9760b7..8dbed1d4ef2f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2976,6 +2976,11 @@ static struct mlxsw_sp_fid *mlxsw_sp_bridge_fid_get(struct mlxsw_sp *mlxsw_sp, return mlxsw_sp_fid_find(mlxsw_sp, fid); } +static u8 mlxsw_sp_router_port(const struct mlxsw_sp *mlxsw_sp) +{ + return mlxsw_core_max_ports(mlxsw_sp->core) + 1; +} + static enum mlxsw_flood_table_type mlxsw_sp_flood_table_type_get(u16 fid) { return mlxsw_sp_fid_is_vfid(fid) ? MLXSW_REG_SFGC_TABLE_TYPE_FID : @@ -2990,6 +2995,7 @@ static u16 mlxsw_sp_flood_table_index_get(u16 fid) static int mlxsw_sp_router_port_flood_set(struct mlxsw_sp *mlxsw_sp, u16 fid, bool set) { + u8 router_port = mlxsw_sp_router_port(mlxsw_sp); enum mlxsw_flood_table_type table_type; char *sftr_pl; u16 index; @@ -3002,7 +3008,7 @@ static int mlxsw_sp_router_port_flood_set(struct mlxsw_sp *mlxsw_sp, u16 fid, table_type = mlxsw_sp_flood_table_type_get(fid); index = mlxsw_sp_flood_table_index_get(fid); mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BC, index, table_type, - 1, MLXSW_PORT_ROUTER_PORT, set); + 1, router_port, set); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl); kfree(sftr_pl); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index d44d92fe7ff3..05eaa15ad9d5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -1012,7 +1012,7 @@ static int mlxsw_sp_port_smid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mid, mlxsw_reg_smid_pack(smid_pl, mid, mlxsw_sp_port->local_port, add); if (clear_all_ports) { - for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) + for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++) if (mlxsw_sp->ports[i]) mlxsw_reg_smid_port_mask_set(smid_pl, i, 1); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index ec1e886d4566..3b0f72455681 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1321,7 +1321,7 @@ static void mlxsw_sx_ports_remove(struct mlxsw_sx *mlxsw_sx) { int i; - for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) + for (i = 1; i < mlxsw_core_max_ports(mlxsw_sx->core); i++) if (mlxsw_sx_port_created(mlxsw_sx, i)) mlxsw_sx_port_remove(mlxsw_sx, i); kfree(mlxsw_sx->ports); @@ -1329,17 +1329,18 @@ static void mlxsw_sx_ports_remove(struct mlxsw_sx *mlxsw_sx) static int mlxsw_sx_ports_create(struct mlxsw_sx *mlxsw_sx) { + unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sx->core); size_t alloc_size; u8 module, width; int i; int err; - alloc_size = sizeof(struct mlxsw_sx_port *) * MLXSW_PORT_MAX_PORTS; + alloc_size = sizeof(struct mlxsw_sx_port *) * max_ports; mlxsw_sx->ports = kzalloc(alloc_size, GFP_KERNEL); if (!mlxsw_sx->ports) return -ENOMEM; - for (i = 1; i < MLXSW_PORT_MAX_PORTS; i++) { + for (i = 1; i < max_ports; i++) { err = mlxsw_sx_port_module_info_get(mlxsw_sx, i, &module, &width); if (err) -- cgit From d3daae1b0808c82bc2875c0f249650d3efe6776f Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 24 Mar 2017 08:02:49 +0100 Subject: mlxsw: spectrum_buffers: Query shared buffer size from firmware Instead of hard coding the size of the shared buffer in the driver, query it from the firmware, as it may change in future devices. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c index 3ab853db59ee..7e67d0e5b7f3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c @@ -536,12 +536,15 @@ static int mlxsw_sp_sb_mms_init(struct mlxsw_sp *mlxsw_sp) return 0; } -#define MLXSW_SP_SB_SIZE (16 * 1024 * 1024) - int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp) { + u64 sb_size; int err; + if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_BUFFER_SIZE)) + return -EIO; + sb_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_BUFFER_SIZE); + err = mlxsw_sp_sb_ports_init(mlxsw_sp); if (err) return err; @@ -554,8 +557,7 @@ int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp) err = mlxsw_sp_sb_mms_init(mlxsw_sp); if (err) goto err_sb_mms_init; - err = devlink_sb_register(priv_to_devlink(mlxsw_sp->core), 0, - MLXSW_SP_SB_SIZE, + err = devlink_sb_register(priv_to_devlink(mlxsw_sp->core), 0, sb_size, MLXSW_SP_SB_POOL_COUNT, MLXSW_SP_SB_POOL_COUNT, MLXSW_SP_SB_TC_COUNT, -- cgit From f417f04da589f579fb0c0adea5e5474ed634c18f Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 24 Mar 2017 08:02:50 +0100 Subject: mlxsw: spectrum: Refactor port buffer configuration The sizes and thresholds of the priority group (PG) buffers are configured in cells, which represent a specific amount of bytes. The cell size can vary in different devices, so it's better to query it from the firmware than hard coding it. Refactor the code dealing with this value into different functions, so that it will be easier to make the conversion in the next patch. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 47 ++++++++++++++++++++------ drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 13 ------- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 7d0bd027cf1e..c440b351a0e4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -800,19 +800,40 @@ static int mlxsw_sp_port_set_mac_address(struct net_device *dev, void *p) return 0; } -static void mlxsw_sp_pg_buf_pack(char *pbmc_pl, int pg_index, int mtu, - bool pause_en, bool pfc_en, u16 delay) +static u16 mlxsw_sp_pg_buf_threshold_get(int mtu) { - u16 pg_size = 2 * MLXSW_SP_BYTES_TO_CELLS(mtu); + return 2 * MLXSW_SP_BYTES_TO_CELLS(mtu); +} - delay = pfc_en ? mlxsw_sp_pfc_delay_get(mtu, delay) : - MLXSW_SP_PAUSE_DELAY; +#define MLXSW_SP_CELL_FACTOR 2 /* 2 * cell_size / (IPG + cell_size + 1) */ +static u16 mlxsw_sp_pfc_delay_get(int mtu, u16 delay) +{ + delay = MLXSW_SP_BYTES_TO_CELLS(DIV_ROUND_UP(delay, BITS_PER_BYTE)); + return MLXSW_SP_CELL_FACTOR * delay + MLXSW_SP_BYTES_TO_CELLS(mtu); +} - if (pause_en || pfc_en) - mlxsw_reg_pbmc_lossless_buffer_pack(pbmc_pl, pg_index, - pg_size + delay, pg_size); +/* Maximum delay buffer needed in case of PAUSE frames, in cells. + * Assumes 100m cable and maximum MTU. + */ +#define MLXSW_SP_PAUSE_DELAY 612 +static u16 mlxsw_sp_pg_buf_delay_get(int mtu, u16 delay, bool pfc, bool pause) +{ + if (pfc) + return mlxsw_sp_pfc_delay_get(mtu, delay); + else if (pause) + return MLXSW_SP_PAUSE_DELAY; else - mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, pg_index, pg_size); + return 0; +} + +static void mlxsw_sp_pg_buf_pack(char *pbmc_pl, int index, u16 size, u16 thres, + bool lossy) +{ + if (lossy) + mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, index, size); + else + mlxsw_reg_pbmc_lossless_buffer_pack(pbmc_pl, index, size, + thres); } int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu, @@ -833,6 +854,8 @@ int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu, for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { bool configure = false; bool pfc = false; + bool lossy; + u16 thres; for (j = 0; j < IEEE_8021QAZ_MAX_TCS; j++) { if (prio_tc[j] == i) { @@ -844,7 +867,11 @@ int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu, if (!configure) continue; - mlxsw_sp_pg_buf_pack(pbmc_pl, i, mtu, pause_en, pfc, delay); + + lossy = !(pfc || pause_en); + thres = mlxsw_sp_pg_buf_threshold_get(mtu); + delay = mlxsw_sp_pg_buf_delay_get(mtu, delay, pfc, pause_en); + mlxsw_sp_pg_buf_pack(pbmc_pl, i, thres + delay, thres, lossy); } return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 80a0d7d3e1b5..958acac91ba8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -73,19 +73,6 @@ #define MLXSW_SP_KVD_LINEAR_SIZE 65536 /* entries */ #define MLXSW_SP_KVD_GRANULARITY 128 -/* Maximum delay buffer needed in case of PAUSE frames, in cells. - * Assumes 100m cable and maximum MTU. - */ -#define MLXSW_SP_PAUSE_DELAY 612 - -#define MLXSW_SP_CELL_FACTOR 2 /* 2 * cell_size / (IPG + cell_size + 1) */ - -static inline u16 mlxsw_sp_pfc_delay_get(int mtu, u16 delay) -{ - delay = MLXSW_SP_BYTES_TO_CELLS(DIV_ROUND_UP(delay, BITS_PER_BYTE)); - return MLXSW_SP_CELL_FACTOR * delay + MLXSW_SP_BYTES_TO_CELLS(mtu); -} - struct mlxsw_sp_port; struct mlxsw_sp_rif; -- cgit From 18281f2dabd0fe77556dc81247451eb6e69997e5 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 24 Mar 2017 08:02:51 +0100 Subject: mlxsw: spectrum: Query cell size from firmware As explained in the previous patch, the cell size may change in future devices, so query it from the firmware instead of hard coding it. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/resources.h | 2 + drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 68 +++++++----- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 18 +++- .../net/ethernet/mellanox/mlxsw/spectrum_buffers.c | 114 ++++++++++++--------- 4 files changed, 119 insertions(+), 83 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h index 41553a79d99d..ee097691fa8c 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/resources.h +++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h @@ -50,6 +50,7 @@ enum mlxsw_res_id { MLXSW_RES_ID_MAX_LAG, MLXSW_RES_ID_MAX_LAG_MEMBERS, MLXSW_RES_ID_MAX_BUFFER_SIZE, + MLXSW_RES_ID_CELL_SIZE, MLXSW_RES_ID_ACL_MAX_TCAM_REGIONS, MLXSW_RES_ID_ACL_MAX_TCAM_RULES, MLXSW_RES_ID_ACL_MAX_REGIONS, @@ -85,6 +86,7 @@ static u16 mlxsw_res_ids[] = { [MLXSW_RES_ID_MAX_LAG] = 0x2520, [MLXSW_RES_ID_MAX_LAG_MEMBERS] = 0x2521, [MLXSW_RES_ID_MAX_BUFFER_SIZE] = 0x2802, /* Bytes */ + [MLXSW_RES_ID_CELL_SIZE] = 0x2803, /* Bytes */ [MLXSW_RES_ID_ACL_MAX_TCAM_REGIONS] = 0x2901, [MLXSW_RES_ID_ACL_MAX_TCAM_RULES] = 0x2902, [MLXSW_RES_ID_ACL_MAX_REGIONS] = 0x2903, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index c440b351a0e4..eabfe5c22b05 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -359,9 +359,10 @@ static bool mlxsw_sp_span_is_egress_mirror(struct mlxsw_sp_port *port) return false; } -static int mlxsw_sp_span_mtu_to_buffsize(int mtu) +static int mlxsw_sp_span_mtu_to_buffsize(const struct mlxsw_sp *mlxsw_sp, + int mtu) { - return MLXSW_SP_BYTES_TO_CELLS(mtu * 5 / 2) + 1; + return mlxsw_sp_bytes_cells(mlxsw_sp, mtu * 5 / 2) + 1; } static int mlxsw_sp_span_port_mtu_update(struct mlxsw_sp_port *port, u16 mtu) @@ -374,8 +375,9 @@ static int mlxsw_sp_span_port_mtu_update(struct mlxsw_sp_port *port, u16 mtu) * updated according to the mtu value */ if (mlxsw_sp_span_is_egress_mirror(port)) { - mlxsw_reg_sbib_pack(sbib_pl, port->local_port, - mlxsw_sp_span_mtu_to_buffsize(mtu)); + u32 buffsize = mlxsw_sp_span_mtu_to_buffsize(mlxsw_sp, mtu); + + mlxsw_reg_sbib_pack(sbib_pl, port->local_port, buffsize); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl); if (err) { netdev_err(port->dev, "Could not update shared buffer for mirroring\n"); @@ -412,8 +414,10 @@ mlxsw_sp_span_inspected_port_bind(struct mlxsw_sp_port *port, /* if it is an egress SPAN, bind a shared buffer to it */ if (type == MLXSW_SP_SPAN_EGRESS) { - mlxsw_reg_sbib_pack(sbib_pl, port->local_port, - mlxsw_sp_span_mtu_to_buffsize(port->dev->mtu)); + u32 buffsize = mlxsw_sp_span_mtu_to_buffsize(mlxsw_sp, + port->dev->mtu); + + mlxsw_reg_sbib_pack(sbib_pl, port->local_port, buffsize); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbib), sbib_pl); if (err) { netdev_err(port->dev, "Could not create shared buffer for mirroring\n"); @@ -800,28 +804,35 @@ static int mlxsw_sp_port_set_mac_address(struct net_device *dev, void *p) return 0; } -static u16 mlxsw_sp_pg_buf_threshold_get(int mtu) +static u16 mlxsw_sp_pg_buf_threshold_get(const struct mlxsw_sp *mlxsw_sp, + int mtu) { - return 2 * MLXSW_SP_BYTES_TO_CELLS(mtu); + return 2 * mlxsw_sp_bytes_cells(mlxsw_sp, mtu); } #define MLXSW_SP_CELL_FACTOR 2 /* 2 * cell_size / (IPG + cell_size + 1) */ -static u16 mlxsw_sp_pfc_delay_get(int mtu, u16 delay) + +static u16 mlxsw_sp_pfc_delay_get(const struct mlxsw_sp *mlxsw_sp, int mtu, + u16 delay) { - delay = MLXSW_SP_BYTES_TO_CELLS(DIV_ROUND_UP(delay, BITS_PER_BYTE)); - return MLXSW_SP_CELL_FACTOR * delay + MLXSW_SP_BYTES_TO_CELLS(mtu); + delay = mlxsw_sp_bytes_cells(mlxsw_sp, DIV_ROUND_UP(delay, + BITS_PER_BYTE)); + return MLXSW_SP_CELL_FACTOR * delay + mlxsw_sp_bytes_cells(mlxsw_sp, + mtu); } -/* Maximum delay buffer needed in case of PAUSE frames, in cells. +/* Maximum delay buffer needed in case of PAUSE frames, in bytes. * Assumes 100m cable and maximum MTU. */ -#define MLXSW_SP_PAUSE_DELAY 612 -static u16 mlxsw_sp_pg_buf_delay_get(int mtu, u16 delay, bool pfc, bool pause) +#define MLXSW_SP_PAUSE_DELAY 58752 + +static u16 mlxsw_sp_pg_buf_delay_get(const struct mlxsw_sp *mlxsw_sp, int mtu, + u16 delay, bool pfc, bool pause) { if (pfc) - return mlxsw_sp_pfc_delay_get(mtu, delay); + return mlxsw_sp_pfc_delay_get(mlxsw_sp, mtu, delay); else if (pause) - return MLXSW_SP_PAUSE_DELAY; + return mlxsw_sp_bytes_cells(mlxsw_sp, MLXSW_SP_PAUSE_DELAY); else return 0; } @@ -869,8 +880,9 @@ int __mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port, int mtu, continue; lossy = !(pfc || pause_en); - thres = mlxsw_sp_pg_buf_threshold_get(mtu); - delay = mlxsw_sp_pg_buf_delay_get(mtu, delay, pfc, pause_en); + thres = mlxsw_sp_pg_buf_threshold_get(mlxsw_sp, mtu); + delay = mlxsw_sp_pg_buf_delay_get(mlxsw_sp, mtu, delay, pfc, + pause_en); mlxsw_sp_pg_buf_pack(pbmc_pl, i, thres + delay, thres, lossy); } @@ -1577,6 +1589,7 @@ err_port_pause_configure: struct mlxsw_sp_port_hw_stats { char str[ETH_GSTRING_LEN]; u64 (*getter)(const char *payload); + bool cells_bytes; }; static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_stats[] = { @@ -1697,17 +1710,11 @@ static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_prio_stats[] = { #define MLXSW_SP_PORT_HW_PRIO_STATS_LEN ARRAY_SIZE(mlxsw_sp_port_hw_prio_stats) -static u64 mlxsw_reg_ppcnt_tc_transmit_queue_bytes_get(const char *ppcnt_pl) -{ - u64 transmit_queue = mlxsw_reg_ppcnt_tc_transmit_queue_get(ppcnt_pl); - - return MLXSW_SP_CELLS_TO_BYTES(transmit_queue); -} - static struct mlxsw_sp_port_hw_stats mlxsw_sp_port_hw_tc_stats[] = { { .str = "tc_transmit_queue_tc", - .getter = mlxsw_reg_ppcnt_tc_transmit_queue_bytes_get, + .getter = mlxsw_reg_ppcnt_tc_transmit_queue_get, + .cells_bytes = true, }, { .str = "tc_no_buffer_discard_uc_tc", @@ -1819,6 +1826,8 @@ static void __mlxsw_sp_port_get_stats(struct net_device *dev, enum mlxsw_reg_ppcnt_grp grp, int prio, u64 *data, int data_index) { + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp_port_hw_stats *hw_stats; char ppcnt_pl[MLXSW_REG_PPCNT_LEN]; int i, len; @@ -1828,8 +1837,13 @@ static void __mlxsw_sp_port_get_stats(struct net_device *dev, if (err) return; mlxsw_sp_port_get_stats_raw(dev, grp, prio, ppcnt_pl); - for (i = 0; i < len; i++) + for (i = 0; i < len; i++) { data[data_index + i] = hw_stats[i].getter(ppcnt_pl); + if (!hw_stats[i].cells_bytes) + continue; + data[data_index + i] = mlxsw_sp_cells_bytes(mlxsw_sp, + data[data_index + i]); + } } static void mlxsw_sp_port_get_stats(struct net_device *dev, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 958acac91ba8..3025b766fea1 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -65,11 +65,6 @@ #define MLXSW_SP_PORT_BASE_SPEED 25000 /* Mb/s */ -#define MLXSW_SP_BYTES_PER_CELL 96 - -#define MLXSW_SP_BYTES_TO_CELLS(b) DIV_ROUND_UP(b, MLXSW_SP_BYTES_PER_CELL) -#define MLXSW_SP_CELLS_TO_BYTES(c) (c * MLXSW_SP_BYTES_PER_CELL) - #define MLXSW_SP_KVD_LINEAR_SIZE 65536 /* entries */ #define MLXSW_SP_KVD_GRANULARITY 128 @@ -147,6 +142,7 @@ struct mlxsw_sp_sb_port { struct mlxsw_sp_sb { struct mlxsw_sp_sb_pr prs[2][MLXSW_SP_SB_POOL_COUNT]; struct mlxsw_sp_sb_port *ports; + u32 cell_size; }; #define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE) @@ -284,6 +280,18 @@ mlxsw_sp_lag_get(struct mlxsw_sp *mlxsw_sp, u16 lag_id) return &mlxsw_sp->lags[lag_id]; } +static inline u32 mlxsw_sp_cells_bytes(const struct mlxsw_sp *mlxsw_sp, + u32 cells) +{ + return mlxsw_sp->sb.cell_size * cells; +} + +static inline u32 mlxsw_sp_bytes_cells(const struct mlxsw_sp *mlxsw_sp, + u32 bytes) +{ + return DIV_ROUND_UP(bytes, mlxsw_sp->sb.cell_size); +} + struct mlxsw_sp_port_pcpu_stats { u64 rx_packets; u64 rx_bytes; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c index 7e67d0e5b7f3..997189cfe7fd 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c @@ -162,8 +162,8 @@ static int mlxsw_sp_sb_pm_occ_query(struct mlxsw_sp *mlxsw_sp, u8 local_port, } static const u16 mlxsw_sp_pbs[] = { - [0] = 2 * MLXSW_SP_BYTES_TO_CELLS(ETH_FRAME_LEN), - [9] = 2 * MLXSW_SP_BYTES_TO_CELLS(MLXSW_PORT_MAX_MTU), + [0] = 2 * ETH_FRAME_LEN, + [9] = 2 * MLXSW_PORT_MAX_MTU, }; #define MLXSW_SP_PBS_LEN ARRAY_SIZE(mlxsw_sp_pbs) @@ -171,20 +171,22 @@ static const u16 mlxsw_sp_pbs[] = { static int mlxsw_sp_port_pb_init(struct mlxsw_sp_port *mlxsw_sp_port) { + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; 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++) { + u16 size = mlxsw_sp_bytes_cells(mlxsw_sp, mlxsw_sp_pbs[i]); + if (i == MLXSW_SP_PB_UNUSED) continue; - mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, i, mlxsw_sp_pbs[i]); + mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, i, size); } mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, MLXSW_REG_PBMC_PORT_SHARED_BUF_IDX, 0); - return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, - MLXSW_REG(pbmc), pbmc_pl); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl); } static int mlxsw_sp_port_pb_prio_init(struct mlxsw_sp_port *mlxsw_sp_port) @@ -237,18 +239,17 @@ static void mlxsw_sp_sb_ports_fini(struct mlxsw_sp *mlxsw_sp) static const struct mlxsw_sp_sb_pr mlxsw_sp_sb_prs_ingress[] = { MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, - MLXSW_SP_BYTES_TO_CELLS(MLXSW_SP_SB_PR_INGRESS_SIZE)), + MLXSW_SP_SB_PR_INGRESS_SIZE), MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0), MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0), MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, - MLXSW_SP_BYTES_TO_CELLS(MLXSW_SP_SB_PR_INGRESS_MNG_SIZE)), + MLXSW_SP_SB_PR_INGRESS_MNG_SIZE), }; #define MLXSW_SP_SB_PRS_INGRESS_LEN ARRAY_SIZE(mlxsw_sp_sb_prs_ingress) static const struct mlxsw_sp_sb_pr mlxsw_sp_sb_prs_egress[] = { - MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, - MLXSW_SP_BYTES_TO_CELLS(MLXSW_SP_SB_PR_EGRESS_SIZE)), + MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, MLXSW_SP_SB_PR_EGRESS_SIZE), MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0), MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0), MLXSW_SP_SB_PR(MLXSW_REG_SBPR_MODE_DYNAMIC, 0), @@ -265,11 +266,9 @@ static int __mlxsw_sp_sb_prs_init(struct mlxsw_sp *mlxsw_sp, int err; for (i = 0; i < prs_len; i++) { - const struct mlxsw_sp_sb_pr *pr; + u32 size = mlxsw_sp_bytes_cells(mlxsw_sp, prs[i].size); - pr = &prs[i]; - err = mlxsw_sp_sb_pr_write(mlxsw_sp, i, dir, - pr->mode, pr->size); + err = mlxsw_sp_sb_pr_write(mlxsw_sp, i, dir, prs[i].mode, size); if (err) return err; } @@ -298,7 +297,7 @@ static int mlxsw_sp_sb_prs_init(struct mlxsw_sp *mlxsw_sp) } static const struct mlxsw_sp_sb_cm mlxsw_sp_sb_cms_ingress[] = { - MLXSW_SP_SB_CM(MLXSW_SP_BYTES_TO_CELLS(10000), 8, 0), + MLXSW_SP_SB_CM(10000, 8, 0), MLXSW_SP_SB_CM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN, 0), MLXSW_SP_SB_CM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN, 0), MLXSW_SP_SB_CM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN, 0), @@ -307,20 +306,20 @@ static const struct mlxsw_sp_sb_cm mlxsw_sp_sb_cms_ingress[] = { MLXSW_SP_SB_CM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN, 0), MLXSW_SP_SB_CM(0, MLXSW_REG_SBXX_DYN_MAX_BUFF_MIN, 0), MLXSW_SP_SB_CM(0, 0, 0), /* dummy, this PG does not exist */ - MLXSW_SP_SB_CM(MLXSW_SP_BYTES_TO_CELLS(20000), 1, 3), + MLXSW_SP_SB_CM(20000, 1, 3), }; #define MLXSW_SP_SB_CMS_INGRESS_LEN ARRAY_SIZE(mlxsw_sp_sb_cms_ingress) static const struct mlxsw_sp_sb_cm mlxsw_sp_sb_cms_egress[] = { - MLXSW_SP_SB_CM(MLXSW_SP_BYTES_TO_CELLS(1500), 9, 0), - MLXSW_SP_SB_CM(MLXSW_SP_BYTES_TO_CELLS(1500), 9, 0), - MLXSW_SP_SB_CM(MLXSW_SP_BYTES_TO_CELLS(1500), 9, 0), - MLXSW_SP_SB_CM(MLXSW_SP_BYTES_TO_CELLS(1500), 9, 0), - MLXSW_SP_SB_CM(MLXSW_SP_BYTES_TO_CELLS(1500), 9, 0), - MLXSW_SP_SB_CM(MLXSW_SP_BYTES_TO_CELLS(1500), 9, 0), - MLXSW_SP_SB_CM(MLXSW_SP_BYTES_TO_CELLS(1500), 9, 0), - MLXSW_SP_SB_CM(MLXSW_SP_BYTES_TO_CELLS(1500), 9, 0), + MLXSW_SP_SB_CM(1500, 9, 0), + MLXSW_SP_SB_CM(1500, 9, 0), + MLXSW_SP_SB_CM(1500, 9, 0), + MLXSW_SP_SB_CM(1500, 9, 0), + MLXSW_SP_SB_CM(1500, 9, 0), + MLXSW_SP_SB_CM(1500, 9, 0), + MLXSW_SP_SB_CM(1500, 9, 0), + MLXSW_SP_SB_CM(1500, 9, 0), MLXSW_SP_SB_CM(0, 0, 0), MLXSW_SP_SB_CM(0, 0, 0), MLXSW_SP_SB_CM(0, 0, 0), @@ -344,7 +343,7 @@ static const struct mlxsw_sp_sb_cm mlxsw_sp_cpu_port_sb_cms[] = { MLXSW_SP_CPU_PORT_SB_CM, MLXSW_SP_CPU_PORT_SB_CM, MLXSW_SP_CPU_PORT_SB_CM, - MLXSW_SP_SB_CM(MLXSW_SP_BYTES_TO_CELLS(10000), 0, 0), + MLXSW_SP_SB_CM(10000, 0, 0), MLXSW_SP_CPU_PORT_SB_CM, MLXSW_SP_CPU_PORT_SB_CM, MLXSW_SP_CPU_PORT_SB_CM, @@ -384,13 +383,17 @@ static int __mlxsw_sp_sb_cms_init(struct mlxsw_sp *mlxsw_sp, u8 local_port, for (i = 0; i < cms_len; i++) { const struct mlxsw_sp_sb_cm *cm; + u32 min_buff; if (i == 8 && dir == MLXSW_REG_SBXX_DIR_INGRESS) continue; /* PG number 8 does not exist, skip it */ cm = &cms[i]; + /* All pools are initialized using dynamic thresholds, + * therefore 'max_buff' isn't specified in cells. + */ + min_buff = mlxsw_sp_bytes_cells(mlxsw_sp, cm->min_buff); err = mlxsw_sp_sb_cm_write(mlxsw_sp, local_port, i, dir, - cm->min_buff, cm->max_buff, - cm->pool); + min_buff, cm->max_buff, cm->pool); if (err) return err; } @@ -498,21 +501,21 @@ struct mlxsw_sp_sb_mm { } static const struct mlxsw_sp_sb_mm mlxsw_sp_sb_mms[] = { - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), - MLXSW_SP_SB_MM(MLXSW_SP_BYTES_TO_CELLS(20000), 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), + MLXSW_SP_SB_MM(20000, 0xff, 0), }; #define MLXSW_SP_SB_MMS_LEN ARRAY_SIZE(mlxsw_sp_sb_mms) @@ -525,10 +528,15 @@ static int mlxsw_sp_sb_mms_init(struct mlxsw_sp *mlxsw_sp) for (i = 0; i < MLXSW_SP_SB_MMS_LEN; i++) { const struct mlxsw_sp_sb_mm *mc; + u32 min_buff; mc = &mlxsw_sp_sb_mms[i]; - mlxsw_reg_sbmm_pack(sbmm_pl, i, mc->min_buff, - mc->max_buff, mc->pool); + /* All pools are initialized using dynamic thresholds, + * therefore 'max_buff' isn't specified in cells. + */ + min_buff = mlxsw_sp_bytes_cells(mlxsw_sp, mc->min_buff); + mlxsw_reg_sbmm_pack(sbmm_pl, i, min_buff, mc->max_buff, + mc->pool); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sbmm), sbmm_pl); if (err) return err; @@ -541,6 +549,10 @@ int mlxsw_sp_buffers_init(struct mlxsw_sp *mlxsw_sp) u64 sb_size; int err; + if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, CELL_SIZE)) + return -EIO; + mlxsw_sp->sb.cell_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, CELL_SIZE); + if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_BUFFER_SIZE)) return -EIO; sb_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_BUFFER_SIZE); @@ -627,7 +639,7 @@ int mlxsw_sp_sb_pool_get(struct mlxsw_core *mlxsw_core, struct mlxsw_sp_sb_pr *pr = mlxsw_sp_sb_pr_get(mlxsw_sp, pool, dir); pool_info->pool_type = (enum devlink_sb_pool_type) dir; - pool_info->size = MLXSW_SP_CELLS_TO_BYTES(pr->size); + pool_info->size = mlxsw_sp_cells_bytes(mlxsw_sp, pr->size); pool_info->threshold_type = (enum devlink_sb_threshold_type) pr->mode; return 0; } @@ -637,9 +649,9 @@ int mlxsw_sp_sb_pool_set(struct mlxsw_core *mlxsw_core, enum devlink_sb_threshold_type threshold_type) { struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); + u32 pool_size = mlxsw_sp_bytes_cells(mlxsw_sp, size); u8 pool = pool_get(pool_index); enum mlxsw_reg_sbxx_dir dir = dir_get(pool_index); - u32 pool_size = MLXSW_SP_BYTES_TO_CELLS(size); enum mlxsw_reg_sbpr_mode mode; if (size > MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_BUFFER_SIZE)) @@ -658,7 +670,7 @@ static u32 mlxsw_sp_sb_threshold_out(struct mlxsw_sp *mlxsw_sp, u8 pool, if (pr->mode == MLXSW_REG_SBPR_MODE_DYNAMIC) return max_buff - MLXSW_SP_SB_THRESHOLD_TO_ALPHA_OFFSET; - return MLXSW_SP_CELLS_TO_BYTES(max_buff); + return mlxsw_sp_cells_bytes(mlxsw_sp, max_buff); } static int mlxsw_sp_sb_threshold_in(struct mlxsw_sp *mlxsw_sp, u8 pool, @@ -676,7 +688,7 @@ static int mlxsw_sp_sb_threshold_in(struct mlxsw_sp *mlxsw_sp, u8 pool, return -EINVAL; *p_max_buff = val; } else { - *p_max_buff = MLXSW_SP_BYTES_TO_CELLS(threshold); + *p_max_buff = mlxsw_sp_bytes_cells(mlxsw_sp, threshold); } return 0; } @@ -963,8 +975,8 @@ int mlxsw_sp_sb_occ_port_pool_get(struct mlxsw_core_port *mlxsw_core_port, struct mlxsw_sp_sb_pm *pm = mlxsw_sp_sb_pm_get(mlxsw_sp, local_port, pool, dir); - *p_cur = MLXSW_SP_CELLS_TO_BYTES(pm->occ.cur); - *p_max = MLXSW_SP_CELLS_TO_BYTES(pm->occ.max); + *p_cur = mlxsw_sp_cells_bytes(mlxsw_sp, pm->occ.cur); + *p_max = mlxsw_sp_cells_bytes(mlxsw_sp, pm->occ.max); return 0; } @@ -982,7 +994,7 @@ int mlxsw_sp_sb_occ_tc_port_bind_get(struct mlxsw_core_port *mlxsw_core_port, struct mlxsw_sp_sb_cm *cm = mlxsw_sp_sb_cm_get(mlxsw_sp, local_port, pg_buff, dir); - *p_cur = MLXSW_SP_CELLS_TO_BYTES(cm->occ.cur); - *p_max = MLXSW_SP_CELLS_TO_BYTES(cm->occ.max); + *p_cur = mlxsw_sp_cells_bytes(mlxsw_sp, cm->occ.cur); + *p_max = mlxsw_sp_cells_bytes(mlxsw_sp, cm->occ.max); return 0; } -- cgit From 3b1af93cf193ca4a94df5f01f480a9d820a194bb Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Fri, 24 Mar 2017 22:14:36 +0800 Subject: net_sched: use setup_deferrable_timer Use setup_deferrable_timer() instead of init_timer_deferrable() to simplify the code. Signed-off-by: Geliang Tang Signed-off-by: David S. Miller --- net/sched/cls_flow.c | 5 ++--- net/sched/sch_sfq.c | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 3d6b9286c203..ca193af8634a 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -508,9 +508,8 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, get_random_bytes(&fnew->hashrnd, 4); } - fnew->perturb_timer.function = flow_perturbation; - fnew->perturb_timer.data = (unsigned long)fnew; - init_timer_deferrable(&fnew->perturb_timer); + setup_deferrable_timer(&fnew->perturb_timer, flow_perturbation, + (unsigned long)fnew); tcf_exts_change(tp, &fnew->exts, &e); tcf_em_tree_change(tp, &fnew->ematches, &t); diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 42e8c8615e65..b00e02c139de 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -714,9 +714,8 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt) struct sfq_sched_data *q = qdisc_priv(sch); int i; - q->perturb_timer.function = sfq_perturbation; - q->perturb_timer.data = (unsigned long)sch; - init_timer_deferrable(&q->perturb_timer); + setup_deferrable_timer(&q->perturb_timer, sfq_perturbation, + (unsigned long)sch); for (i = 0; i < SFQ_MAX_DEPTH + 1; i++) { q->dep[i].next = i + SFQ_MAX_FLOWS; -- cgit From 19d5a26f5ef8de5dcb78799feaf404d717b1aac3 Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Fri, 24 Mar 2017 10:46:26 +0100 Subject: ipv6: sr: expand skb head only if necessary To insert or encapsulate a packet with an SRH, we need a large enough skb headroom. Currently, we are using pskb_expand_head to inconditionally increase the size of the headroom by the amount needed by the SRH (and IPv6 header). If this reallocation is performed by another CPU than the one that initially allocated the skb, then when the initial CPU kfree the skb, it will enter the __slab_free slowpath, impacting performances. This patch replaces pskb_expand_head with skb_cow_head, that will reallocate the skb head only if the headroom is not large enough. Performances for SRH encapsulation before the patch: Result: OK: 7348320(c7347271+d1048) usec, 5000000 (1000byte,0frags) 680427pps 5443Mb/sec (5443416000bps) errors: 0 Performances after the patch: Result: OK: 5656067(c5655678+d388) usec, 5000000 (1000byte,0frags) 884006pps 7072Mb/sec (7072048000bps) errors: 0 Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/seg6_iptunnel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 85582257d3af..dda2c5147ef0 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -105,7 +105,7 @@ static int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) hdrlen = (osrh->hdrlen + 1) << 3; tot_len = hdrlen + sizeof(*hdr); - err = pskb_expand_head(skb, tot_len, 0, GFP_ATOMIC); + err = skb_cow_head(skb, tot_len); if (unlikely(err)) return err; @@ -156,7 +156,7 @@ static int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) hdrlen = (osrh->hdrlen + 1) << 3; - err = pskb_expand_head(skb, hdrlen, 0, GFP_ATOMIC); + err = skb_cow_head(skb, hdrlen); if (unlikely(err)) return err; -- cgit From af4a2209b1344939eaac11f269c261d347cbc3ee Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Fri, 24 Mar 2017 10:46:27 +0100 Subject: ipv6: sr: use dst_cache in seg6_input We already use dst_cache in seg6_output, when handling locally generated packets. We extend it in seg6_input, to also handle forwarded packets, and avoid unnecessary fib lookups. Performances for SRH encapsulation before the patch: Result: OK: 5656067(c5655678+d388) usec, 5000000 (1000byte,0frags) 884006pps 7072Mb/sec (7072048000bps) errors: 0 Performances after the patch: Result: OK: 4774543(c4774084+d459) usec, 5000000 (1000byte,0frags) 1047220pps 8377Mb/sec (8377760000bps) errors: 0 Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/seg6_iptunnel.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index dda2c5147ef0..30ef1d1d182e 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -237,6 +237,9 @@ static int seg6_do_srh(struct sk_buff *skb) static int seg6_input(struct sk_buff *skb) { + struct dst_entry *orig_dst = skb_dst(skb); + struct dst_entry *dst = NULL; + struct seg6_lwt *slwt; int err; err = seg6_do_srh(skb); @@ -245,8 +248,30 @@ static int seg6_input(struct sk_buff *skb) return err; } + slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); + +#ifdef CONFIG_DST_CACHE + preempt_disable(); + dst = dst_cache_get(&slwt->cache); + preempt_enable(); +#endif + skb_dst_drop(skb); - ip6_route_input(skb); + + if (!dst) { + ip6_route_input(skb); +#ifdef CONFIG_DST_CACHE + dst = skb_dst(skb); + if (!dst->error) { + preempt_disable(); + dst_cache_set_ip6(&slwt->cache, dst, + &ipv6_hdr(skb)->saddr); + preempt_enable(); + } +#endif + } else { + skb_dst_set(skb, dst); + } return dst_input(skb); } -- cgit From 68e498554f598d8a6596c85bb661e9a68cafd673 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 24 Mar 2017 14:57:22 -0700 Subject: net: dsa: bcm_sf2: Add missing OF_MDIO dependency bcm_sf2 does require the MDIO_BCM_UNIMAC driver which is now dependent on OF_MDIO but also internally uses of_mdio.c provided routines which are guarted with OF_MDIO. Reported-by: kbuild test robot Fixes: 90eff9096c01 ("net: phy: Allow splitting MDIO bus/device support from PHYs") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 065984670ff1..564b267c8428 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -11,7 +11,7 @@ config NET_DSA_MV88E6060 config NET_DSA_BCM_SF2 tristate "Broadcom Starfighter 2 Ethernet switch support" - depends on HAS_IOMEM && NET_DSA + depends on HAS_IOMEM && NET_DSA && OF_MDIO select NET_DSA_TAG_BRCM select FIXED_PHY select BCM7XXX_PHY -- cgit From 80fe326ab8f5ac6c41786fa534a220c6e3f5beaf Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sat, 25 Mar 2017 00:52:03 +0300 Subject: net/mlx5e: Use dma_rmb rather than rmb in CQE fetch routine Use dma_rmb in mlx5e_get_cqe rather than aggressive rmb (at least on some architectures), this should help improve the performance on such CPU archs where dma_rmb is optimized. Performance improvement: System: Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz Test case Baseline Now improvement --------------------------------------------------------------- TX packets (24 threads) 45Mpps 50Mpps 11% TC stack Drop (1 core) 3.45Mpps 3.6Mpps 5% XDP Drop (1 core) 14Mpps 16.9Mpps 20% XDP TX (1 core) 10.4Mpps 12Mpps 15% Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index e5c12a732aa1..d8cda2f6239b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -44,7 +44,7 @@ struct mlx5_cqe64 *mlx5e_get_cqe(struct mlx5e_cq *cq) return NULL; /* ensure cqe content is read after cqe ownership bit */ - rmb(); + dma_rmb(); return cqe; } -- cgit From 6982ab609768f4e7c84eab053314effa0bc500fc Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sat, 25 Mar 2017 00:52:04 +0300 Subject: net/mlx5e: Xmit, no write combining mlx5e netdev Blue Flame (write combining) support demands a lot of overhead for a little latency gain for some special cases, this overhead is hurting the common case. Here we remove xmit Blue Flame support by creating all bfregs with no write combining for all SQs, and we remove a lot of BF logic and conditions from xmit data path. Simplify mlx5e_tx_notify_hw (doorbell function) by removing BF related code and by removing one memory barrier needed for WC mapped SQ doorbell buffers, which no longer exist. Performance improvement: System: Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz Test case Before Now improvement --------------------------------------------------------------- TX packets (24 threads) 50Mpps 54Mpps 8% Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 20 ++--------- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 6 +--- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 4 +-- drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 42 ++--------------------- 4 files changed, 9 insertions(+), 63 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index dc52053128bc..85261e9ccf4a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -111,7 +111,6 @@ #define MLX5E_MAX_NUM_SQS (MLX5E_MAX_NUM_CHANNELS * MLX5E_MAX_NUM_TC) #define MLX5E_TX_CQ_POLL_BUDGET 128 #define MLX5E_UPDATE_STATS_INTERVAL 200 /* msecs */ -#define MLX5E_SQ_BF_BUDGET 16 #define MLX5E_ICOSQ_MAX_WQEBBS \ (DIV_ROUND_UP(sizeof(struct mlx5e_umr_wqe), MLX5_SEND_WQE_BB)) @@ -426,7 +425,6 @@ struct mlx5e_sq_dma { enum { MLX5E_SQ_STATE_ENABLED, - MLX5E_SQ_STATE_BF_ENABLE, }; struct mlx5e_sq_wqe_info { @@ -450,9 +448,6 @@ struct mlx5e_sq { /* dirtied @xmit */ u16 pc ____cacheline_aligned_in_smp; u32 dma_fifo_pc; - u16 bf_offset; - u16 prev_cc; - u8 bf_budget; struct mlx5e_sq_stats stats; struct mlx5e_cq cq; @@ -478,7 +473,6 @@ struct mlx5e_sq { void __iomem *uar_map; struct netdev_queue *txq; u32 sqn; - u16 bf_buf_size; u16 max_inline; u8 min_inline_mode; u16 edge; @@ -818,11 +812,9 @@ void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode); void mlx5e_set_rq_type_params(struct mlx5e_priv *priv, u8 rq_type); -static inline void mlx5e_tx_notify_hw(struct mlx5e_sq *sq, - struct mlx5_wqe_ctrl_seg *ctrl, int bf_sz) +static inline void +mlx5e_tx_notify_hw(struct mlx5e_sq *sq, struct mlx5_wqe_ctrl_seg *ctrl) { - u16 ofst = sq->bf_offset; - /* ensure wqe is visible to device before updating doorbell record */ dma_wmb(); @@ -832,14 +824,8 @@ static inline void mlx5e_tx_notify_hw(struct mlx5e_sq *sq, * doorbell */ wmb(); - if (bf_sz) - __iowrite64_copy(sq->uar_map + ofst, ctrl, bf_sz); - else - mlx5_write64((__be32 *)ctrl, sq->uar_map + ofst, NULL); - /* flush the write-combining mapped buffer */ - wmb(); - sq->bf_offset ^= sq->bf_buf_size; + mlx5_write64((__be32 *)ctrl, sq->uar_map, NULL); } static inline void mlx5e_cq_arm(struct mlx5e_cq *cq) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index ddd7464c6b45..f9bcbd277adb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1017,7 +1017,7 @@ static int mlx5e_create_sq(struct mlx5e_channel *c, sq->channel = c; sq->tc = tc; - err = mlx5_alloc_bfreg(mdev, &sq->bfreg, MLX5_CAP_GEN(mdev, bf), false); + err = mlx5_alloc_bfreg(mdev, &sq->bfreg, false, false); if (err) return err; @@ -1030,10 +1030,7 @@ static int mlx5e_create_sq(struct mlx5e_channel *c, goto err_unmap_free_uar; sq->wq.db = &sq->wq.db[MLX5_SND_DBR]; - if (sq->bfreg.wc) - set_bit(MLX5E_SQ_STATE_BF_ENABLE, &sq->state); - sq->bf_buf_size = (1 << MLX5_CAP_GEN(mdev, log_bf_reg_size)) / 2; sq->max_inline = param->max_inline; sq->min_inline_mode = param->min_inline_mode; @@ -1050,7 +1047,6 @@ static int mlx5e_create_sq(struct mlx5e_channel *c, } sq->edge = (sq->wq.sz_m1 + 1) - mlx5e_sq_get_max_wqebbs(sq->type); - sq->bf_budget = MLX5E_SQ_BF_BUDGET; return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index bafcb349a50c..873b3085756c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -353,7 +353,7 @@ static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix) sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_UMR; sq->db.ico_wqe[pi].num_wqebbs = num_wqebbs; sq->pc += num_wqebbs; - mlx5e_tx_notify_hw(sq, &wqe->ctrl, 0); + mlx5e_tx_notify_hw(sq, &wqe->ctrl); } static int mlx5e_alloc_rx_umr_mpwqe(struct mlx5e_rq *rq, @@ -646,7 +646,7 @@ static inline void mlx5e_xmit_xdp_doorbell(struct mlx5e_sq *sq) wqe = mlx5_wq_cyc_get_wqe(wq, pi); wqe->ctrl.fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE; - mlx5e_tx_notify_hw(sq, &wqe->ctrl, 0); + mlx5e_tx_notify_hw(sq, &wqe->ctrl); } static inline bool mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 57f5e2d7ebd1..eec4354208ee 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -57,7 +57,7 @@ void mlx5e_send_nop(struct mlx5e_sq *sq, bool notify_hw) if (notify_hw) { cseg->fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE; - mlx5e_tx_notify_hw(sq, &wqe->ctrl, 0); + mlx5e_tx_notify_hw(sq, &wqe->ctrl); } } @@ -175,25 +175,6 @@ static inline unsigned int mlx5e_calc_min_inline(enum mlx5_inline_modes mode, } } -static inline u16 mlx5e_get_inline_hdr_size(struct mlx5e_sq *sq, - struct sk_buff *skb, bool bf) -{ - /* Some NIC TX decisions, e.g loopback, are based on the packet - * headers and occur before the data gather. - * Therefore these headers must be copied into the WQE - */ - if (bf) { - u16 ihs = skb_headlen(skb); - - if (skb_vlan_tag_present(skb)) - ihs += VLAN_HLEN; - - if (ihs <= sq->max_inline) - return skb_headlen(skb); - } - return mlx5e_calc_min_inline(sq->min_inline_mode, skb); -} - static inline void mlx5e_tx_skb_pull_inline(unsigned char **skb_data, unsigned int *skb_len, unsigned int len) @@ -235,7 +216,6 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) u8 opcode = MLX5_OPCODE_SEND; dma_addr_t dma_addr = 0; unsigned int num_bytes; - bool bf = false; u16 headlen; u16 ds_cnt; u16 ihs; @@ -255,11 +235,6 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) } else sq->stats.csum_none++; - if (sq->cc != sq->prev_cc) { - sq->prev_cc = sq->cc; - sq->bf_budget = (sq->cc == sq->pc) ? MLX5E_SQ_BF_BUDGET : 0; - } - if (skb_is_gso(skb)) { eseg->mss = cpu_to_be16(skb_shinfo(skb)->gso_size); opcode = MLX5_OPCODE_LSO; @@ -277,10 +252,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) sq->stats.packets += skb_shinfo(skb)->gso_segs; num_bytes = skb->len + (skb_shinfo(skb)->gso_segs - 1) * ihs; } else { - bf = sq->bf_budget && - !skb->xmit_more && - !skb_shinfo(skb)->nr_frags; - ihs = mlx5e_get_inline_hdr_size(sq, skb, bf); + ihs = mlx5e_calc_min_inline(sq->min_inline_mode, skb); sq->stats.packets++; num_bytes = max_t(unsigned int, skb->len, ETH_ZLEN); } @@ -366,13 +338,8 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) sq->stats.xmit_more += skb->xmit_more; if (!skb->xmit_more || netif_xmit_stopped(sq->txq)) { - int bf_sz = 0; - - if (bf && test_bit(MLX5E_SQ_STATE_BF_ENABLE, &sq->state)) - bf_sz = wi->num_wqebbs << 3; - cseg->fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE; - mlx5e_tx_notify_hw(sq, &wqe->ctrl, bf_sz); + mlx5e_tx_notify_hw(sq, &wqe->ctrl); } /* fill sq edge with nops to avoid wqe wrap around */ @@ -381,9 +348,6 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) mlx5e_send_nop(sq, false); } - if (bf) - sq->bf_budget--; - return NETDEV_TX_OK; dma_unmap_wqe_err: -- cgit From aff2615763f206f897146e0ee1ddae8e22055ae3 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sat, 25 Mar 2017 00:52:05 +0300 Subject: net/mlx5e: Single bfreg (UAR) for all mlx5e SQs and netdevs One is sufficient since Blue Flame is not supported anymore. This will also come in handy for switchdev mode to save resources, since VF representors will use same single UAR as well for their own SQs. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 1 - drivers/net/ethernet/mellanox/mlx5/core/en_common.c | 9 +++++++++ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 17 +++-------------- include/linux/mlx5/driver.h | 1 + 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 85261e9ccf4a..22e4bad03f05 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -483,7 +483,6 @@ struct mlx5e_sq { /* control path */ struct mlx5_wq_ctrl wq_ctrl; - struct mlx5_sq_bfreg bfreg; struct mlx5e_channel *channel; int tc; u32 rate_limit; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c index bd898d8deda0..20bdbe685795 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c @@ -107,10 +107,18 @@ int mlx5e_create_mdev_resources(struct mlx5_core_dev *mdev) goto err_dealloc_transport_domain; } + err = mlx5_alloc_bfreg(mdev, &res->bfreg, false, false); + if (err) { + mlx5_core_err(mdev, "alloc bfreg failed, %d\n", err); + goto err_destroy_mkey; + } + INIT_LIST_HEAD(&mdev->mlx5e_res.td.tirs_list); return 0; +err_destroy_mkey: + mlx5_core_destroy_mkey(mdev, &res->mkey); err_dealloc_transport_domain: mlx5_core_dealloc_transport_domain(mdev, res->td.tdn); err_dealloc_pd: @@ -122,6 +130,7 @@ void mlx5e_destroy_mdev_resources(struct mlx5_core_dev *mdev) { struct mlx5e_resources *res = &mdev->mlx5e_res; + mlx5_free_bfreg(mdev, &res->bfreg); mlx5_core_destroy_mkey(mdev, &res->mkey); mlx5_core_dealloc_transport_domain(mdev, res->td.tdn); mlx5_core_dealloc_pd(mdev, res->pdn); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index f9bcbd277adb..49c1769d13b9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1016,18 +1016,14 @@ static int mlx5e_create_sq(struct mlx5e_channel *c, sq->mkey_be = c->mkey_be; sq->channel = c; sq->tc = tc; + sq->uar_map = mdev->mlx5e_res.bfreg.map; - err = mlx5_alloc_bfreg(mdev, &sq->bfreg, false, false); - if (err) - return err; - - sq->uar_map = sq->bfreg.map; param->wq.db_numa_node = cpu_to_node(c->cpu); err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, &sq->wq, &sq->wq_ctrl); if (err) - goto err_unmap_free_uar; + return err; sq->wq.db = &sq->wq.db[MLX5_SND_DBR]; @@ -1053,20 +1049,13 @@ static int mlx5e_create_sq(struct mlx5e_channel *c, err_sq_wq_destroy: mlx5_wq_destroy(&sq->wq_ctrl); -err_unmap_free_uar: - mlx5_free_bfreg(mdev, &sq->bfreg); - return err; } static void mlx5e_destroy_sq(struct mlx5e_sq *sq) { - struct mlx5e_channel *c = sq->channel; - struct mlx5e_priv *priv = c->priv; - mlx5e_free_sq_db(sq); mlx5_wq_destroy(&sq->wq_ctrl); - mlx5_free_bfreg(priv->mdev, &sq->bfreg); } static int mlx5e_enable_sq(struct mlx5e_sq *sq, struct mlx5e_sq_param *param) @@ -1103,7 +1092,7 @@ static int mlx5e_enable_sq(struct mlx5e_sq *sq, struct mlx5e_sq_param *param) MLX5_SET(sqc, sqc, tis_lst_sz, param->type == MLX5E_SQ_ICO ? 0 : 1); MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_CYCLIC); - MLX5_SET(wq, wq, uar_page, sq->bfreg.index); + MLX5_SET(wq, wq, uar_page, mdev->mlx5e_res.bfreg.index); MLX5_SET(wq, wq, log_wq_pg_sz, sq->wq_ctrl.buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT); MLX5_SET64(wq, wq, dbr_addr, sq->wq_ctrl.db.dma); diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 2fcff6b4503f..f50864626230 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -728,6 +728,7 @@ struct mlx5e_resources { u32 pdn; struct mlx5_td td; struct mlx5_core_mkey mkey; + struct mlx5_sq_bfreg bfreg; }; struct mlx5_core_dev { -- cgit From 1c4bf940455cd91a9fa100f09bd304f141fb4429 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sat, 25 Mar 2017 00:52:06 +0300 Subject: net/mlx5e: Move XDP completion functions to rx file XDP code belongs to RX path, move mlx5e_poll_xdp_tx_cq and mlx5e_free_xdp_tx_descs to en_rx.c. Rename them to mlx5e_poll_xdpsq_cq and mlx5e_free_xdpsq_descs. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 + drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 82 +++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 24 +------ drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 62 +---------------- 4 files changed, 86 insertions(+), 84 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 22e4bad03f05..fce0eca0701c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -737,6 +737,8 @@ void mlx5e_cq_error_event(struct mlx5_core_cq *mcq, enum mlx5_event event); int mlx5e_napi_poll(struct napi_struct *napi, int budget); bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget); int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget); +bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq); +void mlx5e_free_xdpsq_descs(struct mlx5e_sq *sq); void mlx5e_free_sq_descs(struct mlx5e_sq *sq); void mlx5e_page_release(struct mlx5e_rq *rq, struct mlx5e_dma_info *dma_info, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 873b3085756c..bc74d6032a5c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -989,3 +989,85 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) return work_done; } + +bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) +{ + struct mlx5e_sq *sq; + u16 sqcc; + int i; + + sq = container_of(cq, struct mlx5e_sq, cq); + + if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + return false; + + /* sq->cc must be updated only after mlx5_cqwq_update_db_record(), + * otherwise a cq overrun may occur + */ + sqcc = sq->cc; + + for (i = 0; i < MLX5E_TX_CQ_POLL_BUDGET; i++) { + struct mlx5_cqe64 *cqe; + u16 wqe_counter; + bool last_wqe; + + cqe = mlx5e_get_cqe(cq); + if (!cqe) + break; + + mlx5_cqwq_pop(&cq->wq); + + wqe_counter = be16_to_cpu(cqe->wqe_counter); + + do { + struct mlx5e_sq_wqe_info *wi; + struct mlx5e_dma_info *di; + u16 ci; + + last_wqe = (sqcc == wqe_counter); + + ci = sqcc & sq->wq.sz_m1; + di = &sq->db.xdp.di[ci]; + wi = &sq->db.xdp.wqe_info[ci]; + + if (unlikely(wi->opcode == MLX5_OPCODE_NOP)) { + sqcc++; + continue; + } + + sqcc += wi->num_wqebbs; + /* Recycle RX page */ + mlx5e_page_release(&sq->channel->rq, di, true); + } while (!last_wqe); + } + + mlx5_cqwq_update_db_record(&cq->wq); + + /* ensure cq space is freed before enabling more cqes */ + wmb(); + + sq->cc = sqcc; + return (i == MLX5E_TX_CQ_POLL_BUDGET); +} + +void mlx5e_free_xdpsq_descs(struct mlx5e_sq *sq) +{ + struct mlx5e_sq_wqe_info *wi; + struct mlx5e_dma_info *di; + u16 ci; + + while (sq->cc != sq->pc) { + ci = sq->cc & sq->wq.sz_m1; + di = &sq->db.xdp.di[ci]; + wi = &sq->db.xdp.wqe_info[ci]; + + if (wi->opcode == MLX5_OPCODE_NOP) { + sq->cc++; + continue; + } + + sq->cc += wi->num_wqebbs; + + mlx5e_page_release(&sq->channel->rq, di, false); + } +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index eec4354208ee..7497b6ac4382 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -493,28 +493,6 @@ static void mlx5e_free_txq_sq_descs(struct mlx5e_sq *sq) } } -static void mlx5e_free_xdp_sq_descs(struct mlx5e_sq *sq) -{ - struct mlx5e_sq_wqe_info *wi; - struct mlx5e_dma_info *di; - u16 ci; - - while (sq->cc != sq->pc) { - ci = sq->cc & sq->wq.sz_m1; - di = &sq->db.xdp.di[ci]; - wi = &sq->db.xdp.wqe_info[ci]; - - if (wi->opcode == MLX5_OPCODE_NOP) { - sq->cc++; - continue; - } - - sq->cc += wi->num_wqebbs; - - mlx5e_page_release(&sq->channel->rq, di, false); - } -} - void mlx5e_free_sq_descs(struct mlx5e_sq *sq) { switch (sq->type) { @@ -522,7 +500,7 @@ void mlx5e_free_sq_descs(struct mlx5e_sq *sq) mlx5e_free_txq_sq_descs(sq); break; case MLX5E_SQ_XDP: - mlx5e_free_xdp_sq_descs(sq); + mlx5e_free_xdpsq_descs(sq); break; } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index d8cda2f6239b..f23dedc58175 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -105,66 +105,6 @@ static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq) sq->cc = sqcc; } -static inline bool mlx5e_poll_xdp_tx_cq(struct mlx5e_cq *cq) -{ - struct mlx5e_sq *sq; - u16 sqcc; - int i; - - sq = container_of(cq, struct mlx5e_sq, cq); - - if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) - return false; - - /* sq->cc must be updated only after mlx5_cqwq_update_db_record(), - * otherwise a cq overrun may occur - */ - sqcc = sq->cc; - - for (i = 0; i < MLX5E_TX_CQ_POLL_BUDGET; i++) { - struct mlx5_cqe64 *cqe; - u16 wqe_counter; - bool last_wqe; - - cqe = mlx5e_get_cqe(cq); - if (!cqe) - break; - - mlx5_cqwq_pop(&cq->wq); - - wqe_counter = be16_to_cpu(cqe->wqe_counter); - - do { - struct mlx5e_sq_wqe_info *wi; - struct mlx5e_dma_info *di; - u16 ci; - - last_wqe = (sqcc == wqe_counter); - - ci = sqcc & sq->wq.sz_m1; - di = &sq->db.xdp.di[ci]; - wi = &sq->db.xdp.wqe_info[ci]; - - if (unlikely(wi->opcode == MLX5_OPCODE_NOP)) { - sqcc++; - continue; - } - - sqcc += wi->num_wqebbs; - /* Recycle RX page */ - mlx5e_page_release(&sq->channel->rq, di, true); - } while (!last_wqe); - } - - mlx5_cqwq_update_db_record(&cq->wq); - - /* ensure cq space is freed before enabling more cqes */ - wmb(); - - sq->cc = sqcc; - return (i == MLX5E_TX_CQ_POLL_BUDGET); -} - int mlx5e_napi_poll(struct napi_struct *napi, int budget) { struct mlx5e_channel *c = container_of(napi, struct mlx5e_channel, @@ -182,7 +122,7 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) busy |= work_done == budget; if (c->xdp) - busy |= mlx5e_poll_xdp_tx_cq(&c->xdp_sq.cq); + busy |= mlx5e_poll_xdpsq_cq(&c->xdp_sq.cq); mlx5e_poll_ico_cq(&c->icosq.cq); -- cgit From eba2db2bd2c81140e56f06889781f8ba953af6da Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sat, 25 Mar 2017 00:52:07 +0300 Subject: net/mlx5e: Move mlx5e_rq struct declaration Move struct mlx5e_rq and friends to appear after mlx5e_sq declaration in en.h. We will need this for next patch to move the mlx5e_sq instance into mlx5e_rq struct for XDP SQs. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 213 +++++++++++++-------------- 1 file changed, 105 insertions(+), 108 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index fce0eca0701c..8d789a25a1c0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -297,19 +297,113 @@ struct mlx5e_cq { struct mlx5_frag_wq_ctrl wq_ctrl; } ____cacheline_aligned_in_smp; -struct mlx5e_rq; -typedef void (*mlx5e_fp_handle_rx_cqe)(struct mlx5e_rq *rq, - struct mlx5_cqe64 *cqe); -typedef int (*mlx5e_fp_alloc_wqe)(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, - u16 ix); +struct mlx5e_tx_wqe_info { + u32 num_bytes; + u8 num_wqebbs; + u8 num_dma; +}; + +enum mlx5e_dma_map_type { + MLX5E_DMA_MAP_SINGLE, + MLX5E_DMA_MAP_PAGE +}; + +struct mlx5e_sq_dma { + dma_addr_t addr; + u32 size; + enum mlx5e_dma_map_type type; +}; + +enum { + MLX5E_SQ_STATE_ENABLED, +}; + +struct mlx5e_sq_wqe_info { + u8 opcode; + u8 num_wqebbs; +}; -typedef void (*mlx5e_fp_dealloc_wqe)(struct mlx5e_rq *rq, u16 ix); +enum mlx5e_sq_type { + MLX5E_SQ_TXQ, + MLX5E_SQ_ICO, + MLX5E_SQ_XDP +}; + +struct mlx5e_sq { + /* data path */ + + /* dirtied @completion */ + u16 cc; + u32 dma_fifo_cc; + + /* dirtied @xmit */ + u16 pc ____cacheline_aligned_in_smp; + u32 dma_fifo_pc; + struct mlx5e_sq_stats stats; + + struct mlx5e_cq cq; + + /* pointers to per tx element info: write@xmit, read@completion */ + union { + struct { + struct sk_buff **skb; + struct mlx5e_sq_dma *dma_fifo; + struct mlx5e_tx_wqe_info *wqe_info; + } txq; + struct mlx5e_sq_wqe_info *ico_wqe; + struct { + struct mlx5e_sq_wqe_info *wqe_info; + struct mlx5e_dma_info *di; + bool doorbell; + } xdp; + } db; + + /* read only */ + struct mlx5_wq_cyc wq; + u32 dma_fifo_mask; + void __iomem *uar_map; + struct netdev_queue *txq; + u32 sqn; + u16 max_inline; + u8 min_inline_mode; + u16 edge; + struct device *pdev; + struct mlx5e_tstamp *tstamp; + __be32 mkey_be; + unsigned long state; + + /* control path */ + struct mlx5_wq_ctrl wq_ctrl; + struct mlx5e_channel *channel; + int tc; + u32 rate_limit; + u8 type; +} ____cacheline_aligned_in_smp; + +static inline bool mlx5e_sq_has_room_for(struct mlx5e_sq *sq, u16 n) +{ + return (((sq->wq.sz_m1 & (sq->cc - sq->pc)) >= n) || + (sq->cc == sq->pc)); +} struct mlx5e_dma_info { struct page *page; dma_addr_t addr; }; +struct mlx5e_umr_dma_info { + __be64 *mtt; + dma_addr_t mtt_addr; + struct mlx5e_dma_info dma_info[MLX5_MPWRQ_PAGES_PER_WQE]; + struct mlx5e_umr_wqe wqe; +}; + +struct mlx5e_mpw_info { + struct mlx5e_umr_dma_info umr; + u16 consumed_strides; + u16 skbs_frags[MLX5_MPWRQ_PAGES_PER_WQE]; +}; + struct mlx5e_rx_am_stats { int ppms; /* packets per msec */ int epms; /* events per msec */ @@ -346,6 +440,11 @@ struct mlx5e_page_cache { struct mlx5e_dma_info page_cache[MLX5E_CACHE_SIZE]; }; +struct mlx5e_rq; +typedef void (*mlx5e_fp_handle_rx_cqe)(struct mlx5e_rq*, struct mlx5_cqe64*); +typedef int (*mlx5e_fp_alloc_wqe)(struct mlx5e_rq*, struct mlx5e_rx_wqe*, u16); +typedef void (*mlx5e_fp_dealloc_wqe)(struct mlx5e_rq*, u16); + struct mlx5e_rq { /* data path */ struct mlx5_wq_ll wq; @@ -393,108 +492,6 @@ struct mlx5e_rq { struct mlx5_core_mkey umr_mkey; } ____cacheline_aligned_in_smp; -struct mlx5e_umr_dma_info { - __be64 *mtt; - dma_addr_t mtt_addr; - struct mlx5e_dma_info dma_info[MLX5_MPWRQ_PAGES_PER_WQE]; - struct mlx5e_umr_wqe wqe; -}; - -struct mlx5e_mpw_info { - struct mlx5e_umr_dma_info umr; - u16 consumed_strides; - u16 skbs_frags[MLX5_MPWRQ_PAGES_PER_WQE]; -}; - -struct mlx5e_tx_wqe_info { - u32 num_bytes; - u8 num_wqebbs; - u8 num_dma; -}; - -enum mlx5e_dma_map_type { - MLX5E_DMA_MAP_SINGLE, - MLX5E_DMA_MAP_PAGE -}; - -struct mlx5e_sq_dma { - dma_addr_t addr; - u32 size; - enum mlx5e_dma_map_type type; -}; - -enum { - MLX5E_SQ_STATE_ENABLED, -}; - -struct mlx5e_sq_wqe_info { - u8 opcode; - u8 num_wqebbs; -}; - -enum mlx5e_sq_type { - MLX5E_SQ_TXQ, - MLX5E_SQ_ICO, - MLX5E_SQ_XDP -}; - -struct mlx5e_sq { - /* data path */ - - /* dirtied @completion */ - u16 cc; - u32 dma_fifo_cc; - - /* dirtied @xmit */ - u16 pc ____cacheline_aligned_in_smp; - u32 dma_fifo_pc; - struct mlx5e_sq_stats stats; - - struct mlx5e_cq cq; - - /* pointers to per tx element info: write@xmit, read@completion */ - union { - struct { - struct sk_buff **skb; - struct mlx5e_sq_dma *dma_fifo; - struct mlx5e_tx_wqe_info *wqe_info; - } txq; - struct mlx5e_sq_wqe_info *ico_wqe; - struct { - struct mlx5e_sq_wqe_info *wqe_info; - struct mlx5e_dma_info *di; - bool doorbell; - } xdp; - } db; - - /* read only */ - struct mlx5_wq_cyc wq; - u32 dma_fifo_mask; - void __iomem *uar_map; - struct netdev_queue *txq; - u32 sqn; - u16 max_inline; - u8 min_inline_mode; - u16 edge; - struct device *pdev; - struct mlx5e_tstamp *tstamp; - __be32 mkey_be; - unsigned long state; - - /* control path */ - struct mlx5_wq_ctrl wq_ctrl; - struct mlx5e_channel *channel; - int tc; - u32 rate_limit; - u8 type; -} ____cacheline_aligned_in_smp; - -static inline bool mlx5e_sq_has_room_for(struct mlx5e_sq *sq, u16 n) -{ - return (((sq->wq.sz_m1 & (sq->cc - sq->pc)) >= n) || - (sq->cc == sq->pc)); -} - enum channel_flags { MLX5E_CHANNEL_NAPI_SCHED = 1, }; -- cgit From 31871f87bb302efb26978f397bc2705138f0b73a Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sat, 25 Mar 2017 00:52:08 +0300 Subject: net/mlx5e: Move XDP SQ instance into RQ To save many rq->channel->sq dereferences in fast-path. And rename it to xdpsq. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 4 +++- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 12 ++++++------ drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 18 +++++++++++------- drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 2 +- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 8d789a25a1c0..5e4ae94c9f6a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -479,7 +479,10 @@ struct mlx5e_rq { u16 rx_headroom; struct mlx5e_rx_am am; /* Adaptive Moderation */ + + /* XDP */ struct bpf_prog *xdp_prog; + struct mlx5e_sq xdpsq; /* control */ struct mlx5_wq_ctrl wq_ctrl; @@ -499,7 +502,6 @@ enum channel_flags { struct mlx5e_channel { /* data path */ struct mlx5e_rq rq; - struct mlx5e_sq xdp_sq; struct mlx5e_sq sq[MLX5E_MAX_NUM_TC]; struct mlx5e_sq icosq; /* internal control operations */ bool xdp; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 49c1769d13b9..210033187bfe 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1562,7 +1562,7 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, goto err_close_tx_cqs; /* XDP SQ CQ params are same as normal TXQ sq CQ params */ - err = c->xdp ? mlx5e_open_cq(c, &cparam->tx_cq, &c->xdp_sq.cq, + err = c->xdp ? mlx5e_open_cq(c, &cparam->tx_cq, &c->rq.xdpsq.cq, priv->params.tx_cq_moderation) : 0; if (err) goto err_close_rx_cq; @@ -1587,7 +1587,7 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, } } - err = c->xdp ? mlx5e_open_sq(c, 0, &cparam->xdp_sq, &c->xdp_sq) : 0; + err = c->xdp ? mlx5e_open_sq(c, 0, &cparam->xdp_sq, &c->rq.xdpsq) : 0; if (err) goto err_close_sqs; @@ -1601,7 +1601,7 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, return 0; err_close_xdp_sq: if (c->xdp) - mlx5e_close_sq(&c->xdp_sq); + mlx5e_close_sq(&c->rq.xdpsq); err_close_sqs: mlx5e_close_sqs(c); @@ -1612,7 +1612,7 @@ err_close_icosq: err_disable_napi: napi_disable(&c->napi); if (c->xdp) - mlx5e_close_cq(&c->xdp_sq.cq); + mlx5e_close_cq(&c->rq.xdpsq.cq); err_close_rx_cq: mlx5e_close_cq(&c->rq.cq); @@ -1634,12 +1634,12 @@ static void mlx5e_close_channel(struct mlx5e_channel *c) { mlx5e_close_rq(&c->rq); if (c->xdp) - mlx5e_close_sq(&c->xdp_sq); + mlx5e_close_sq(&c->rq.xdpsq); mlx5e_close_sqs(c); mlx5e_close_sq(&c->icosq); napi_disable(&c->napi); if (c->xdp) - mlx5e_close_cq(&c->xdp_sq.cq); + mlx5e_close_cq(&c->rq.xdpsq.cq); mlx5e_close_cq(&c->rq.cq); mlx5e_close_tx_cqs(c); mlx5e_close_cq(&c->icosq.cq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index bc74d6032a5c..040074f36313 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -653,7 +653,7 @@ static inline bool mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq, struct mlx5e_dma_info *di, const struct xdp_buff *xdp) { - struct mlx5e_sq *sq = &rq->channel->xdp_sq; + struct mlx5e_sq *sq = &rq->xdpsq; struct mlx5_wq_cyc *wq = &sq->wq; u16 pi = sq->pc & wq->sz_m1; struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); @@ -950,7 +950,7 @@ mpwrq_cqe_out: int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) { struct mlx5e_rq *rq = container_of(cq, struct mlx5e_rq, cq); - struct mlx5e_sq *xdp_sq = &rq->channel->xdp_sq; + struct mlx5e_sq *xdpsq = &rq->xdpsq; int work_done = 0; if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) @@ -977,9 +977,9 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) rq->handle_rx_cqe(rq, cqe); } - if (xdp_sq->db.xdp.doorbell) { - mlx5e_xmit_xdp_doorbell(xdp_sq); - xdp_sq->db.xdp.doorbell = false; + if (xdpsq->db.xdp.doorbell) { + mlx5e_xmit_xdp_doorbell(xdpsq); + xdpsq->db.xdp.doorbell = false; } mlx5_cqwq_update_db_record(&cq->wq); @@ -993,6 +993,7 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) { struct mlx5e_sq *sq; + struct mlx5e_rq *rq; u16 sqcc; int i; @@ -1001,6 +1002,8 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) return false; + rq = container_of(sq, struct mlx5e_rq, xdpsq); + /* sq->cc must be updated only after mlx5_cqwq_update_db_record(), * otherwise a cq overrun may occur */ @@ -1037,7 +1040,7 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) sqcc += wi->num_wqebbs; /* Recycle RX page */ - mlx5e_page_release(&sq->channel->rq, di, true); + mlx5e_page_release(rq, di, true); } while (!last_wqe); } @@ -1052,6 +1055,7 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) void mlx5e_free_xdpsq_descs(struct mlx5e_sq *sq) { + struct mlx5e_rq *rq = container_of(sq, struct mlx5e_rq, xdpsq); struct mlx5e_sq_wqe_info *wi; struct mlx5e_dma_info *di; u16 ci; @@ -1068,6 +1072,6 @@ void mlx5e_free_xdpsq_descs(struct mlx5e_sq *sq) sq->cc += wi->num_wqebbs; - mlx5e_page_release(&sq->channel->rq, di, false); + mlx5e_page_release(rq, di, false); } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index f23dedc58175..9beeb4a1212f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -122,7 +122,7 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) busy |= work_done == budget; if (c->xdp) - busy |= mlx5e_poll_xdpsq_cq(&c->xdp_sq.cq); + busy |= mlx5e_poll_xdpsq_cq(&c->rq.xdpsq.cq); mlx5e_poll_ico_cq(&c->icosq.cq); -- cgit From 39e12351a308fe022930d0423d88e3aedde795b1 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sat, 25 Mar 2017 00:52:09 +0300 Subject: net/mlx5e: Poll XDP TX CQ before RX CQ Handle XDP TX completions before handling RX packets, to make sure more free space is available for XDP TX packets a moment before handling RX packets. Performance improvement: System: Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz Test case Before Now improvement --------------------------------------------------------------- XDP Drop (1 core) 16.9Mpps 16.9Mpps No change XDP TX (1 core) 12Mpps 13Mpps 8% Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 9beeb4a1212f..c880022bb21a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -118,12 +118,12 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) for (i = 0; i < c->num_tc; i++) busy |= mlx5e_poll_tx_cq(&c->sq[i].cq, budget); - work_done = mlx5e_poll_rx_cq(&c->rq.cq, budget); - busy |= work_done == budget; - if (c->xdp) busy |= mlx5e_poll_xdpsq_cq(&c->rq.xdpsq.cq); + work_done = mlx5e_poll_rx_cq(&c->rq.cq, budget); + busy |= work_done == budget; + mlx5e_poll_ico_cq(&c->icosq.cq); busy |= mlx5e_post_rx_wqes(&c->rq); -- cgit From 2239185ccd888a9934525e5b2d2f55890ab826f6 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sat, 25 Mar 2017 00:52:10 +0300 Subject: net/mlx5e: Optimize XDP frame xmit XDP SQ has a fixed size WQE (MLX5E_XDP_TX_WQEBBS = 1) and only posts one kind of WQE (MLX5_OPCODE_SEND), Also we initialize SQ descriptors static fields once on open_xdpsq, rather than every time on critical path. Optimize the code in light of those facts and add a prefetch of the TX descriptor first thing in the xdp xmit function. Performance improvement: System: Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHz Test case Before Now improvement --------------------------------------------------------------- XDP TX (1 core) 13Mpps 13.7Mpps 5% Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 5 --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 43 +++++++++++++++++++---- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 41 ++++++--------------- 3 files changed, 47 insertions(+), 42 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 5e4ae94c9f6a..f02d2cb8d148 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -116,12 +116,8 @@ (DIV_ROUND_UP(sizeof(struct mlx5e_umr_wqe), MLX5_SEND_WQE_BB)) #define MLX5E_XDP_MIN_INLINE (ETH_HLEN + VLAN_HLEN) -#define MLX5E_XDP_IHS_DS_COUNT \ - DIV_ROUND_UP(MLX5E_XDP_MIN_INLINE - 2, MLX5_SEND_WQE_DS) #define MLX5E_XDP_TX_DS_COUNT \ ((sizeof(struct mlx5e_tx_wqe) / MLX5_SEND_WQE_DS) + 1 /* SG DS */) -#define MLX5E_XDP_TX_WQEBBS \ - DIV_ROUND_UP(MLX5E_XDP_TX_DS_COUNT, MLX5_SEND_WQEBB_NUM_DS) #define MLX5E_NUM_MAIN_GROUPS 9 @@ -352,7 +348,6 @@ struct mlx5e_sq { } txq; struct mlx5e_sq_wqe_info *ico_wqe; struct { - struct mlx5e_sq_wqe_info *wqe_info; struct mlx5e_dma_info *di; bool doorbell; } xdp; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 210033187bfe..d39ee6669b8e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -894,7 +894,6 @@ static void mlx5e_close_rq(struct mlx5e_rq *rq) static void mlx5e_free_sq_xdp_db(struct mlx5e_sq *sq) { kfree(sq->db.xdp.di); - kfree(sq->db.xdp.wqe_info); } static int mlx5e_alloc_sq_xdp_db(struct mlx5e_sq *sq, int numa) @@ -903,9 +902,7 @@ static int mlx5e_alloc_sq_xdp_db(struct mlx5e_sq *sq, int numa) sq->db.xdp.di = kzalloc_node(sizeof(*sq->db.xdp.di) * wq_sz, GFP_KERNEL, numa); - sq->db.xdp.wqe_info = kzalloc_node(sizeof(*sq->db.xdp.wqe_info) * wq_sz, - GFP_KERNEL, numa); - if (!sq->db.xdp.di || !sq->db.xdp.wqe_info) { + if (!sq->db.xdp.di) { mlx5e_free_sq_xdp_db(sq); return -ENOMEM; } @@ -993,7 +990,7 @@ static int mlx5e_sq_get_max_wqebbs(u8 sq_type) case MLX5E_SQ_ICO: return MLX5E_ICOSQ_MAX_WQEBBS; case MLX5E_SQ_XDP: - return MLX5E_XDP_TX_WQEBBS; + return 1; } return MLX5_SEND_WQE_MAX_WQEBBS; } @@ -1513,6 +1510,40 @@ static inline int mlx5e_get_max_num_channels(struct mlx5_core_dev *mdev) MLX5E_MAX_NUM_CHANNELS); } +static int mlx5e_open_xdpsq(struct mlx5e_channel *c, + struct mlx5e_sq_param *param, + struct mlx5e_sq *sq) +{ + unsigned int ds_cnt = MLX5E_XDP_TX_DS_COUNT; + unsigned int inline_hdr_sz = 0; + int err; + int i; + + err = mlx5e_open_sq(c, 0, param, sq); + if (err) + return err; + + if (sq->min_inline_mode != MLX5_INLINE_MODE_NONE) { + inline_hdr_sz = MLX5E_XDP_MIN_INLINE; + ds_cnt++; + } + + /* Pre initialize fixed WQE fields */ + for (i = 0; i < mlx5_wq_cyc_get_size(&sq->wq); i++) { + struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(&sq->wq, i); + struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; + struct mlx5_wqe_eth_seg *eseg = &wqe->eth; + struct mlx5_wqe_data_seg *dseg; + + cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt); + eseg->inline_hdr.sz = cpu_to_be16(inline_hdr_sz); + + dseg = (struct mlx5_wqe_data_seg *)cseg + (ds_cnt - 1); + dseg->lkey = sq->mkey_be; + } + return 0; +} + static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, struct mlx5e_channel_param *cparam, struct mlx5e_channel **cp) @@ -1587,7 +1618,7 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, } } - err = c->xdp ? mlx5e_open_sq(c, 0, &cparam->xdp_sq, &c->rq.xdpsq) : 0; + err = c->xdp ? mlx5e_open_xdpsq(c, &cparam->xdp_sq, &c->rq.xdpsq) : 0; if (err) goto err_close_sqs; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 040074f36313..1b50c54614ac 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -641,7 +641,7 @@ static inline void mlx5e_xmit_xdp_doorbell(struct mlx5e_sq *sq) { struct mlx5_wq_cyc *wq = &sq->wq; struct mlx5e_tx_wqe *wqe; - u16 pi = (sq->pc - MLX5E_XDP_TX_WQEBBS) & wq->sz_m1; /* last pi */ + u16 pi = (sq->pc - 1) & wq->sz_m1; /* last pi */ wqe = mlx5_wq_cyc_get_wqe(wq, pi); @@ -657,17 +657,17 @@ static inline bool mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq, struct mlx5_wq_cyc *wq = &sq->wq; u16 pi = sq->pc & wq->sz_m1; struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); - struct mlx5e_sq_wqe_info *wi = &sq->db.xdp.wqe_info[pi]; struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; struct mlx5_wqe_eth_seg *eseg = &wqe->eth; struct mlx5_wqe_data_seg *dseg; - u8 ds_cnt = MLX5E_XDP_TX_DS_COUNT; ptrdiff_t data_offset = xdp->data - xdp->data_hard_start; dma_addr_t dma_addr = di->addr + data_offset; unsigned int dma_len = xdp->data_end - xdp->data; + prefetchw(wqe); + if (unlikely(dma_len < MLX5E_XDP_MIN_INLINE || MLX5E_SW2HW_MTU(rq->netdev->mtu) < dma_len)) { rq->stats.xdp_drop++; @@ -675,7 +675,7 @@ static inline bool mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq, return false; } - if (unlikely(!mlx5e_sq_has_room_for(sq, MLX5E_XDP_TX_WQEBBS))) { + if (unlikely(!mlx5e_sq_has_room_for(sq, 1))) { if (sq->db.xdp.doorbell) { /* SQ is full, ring doorbell */ mlx5e_xmit_xdp_doorbell(sq); @@ -686,35 +686,29 @@ static inline bool mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq, return false; } - dma_sync_single_for_device(sq->pdev, dma_addr, dma_len, - PCI_DMA_TODEVICE); + dma_sync_single_for_device(sq->pdev, dma_addr, dma_len, PCI_DMA_TODEVICE); - memset(wqe, 0, sizeof(*wqe)); + cseg->fm_ce_se = 0; dseg = (struct mlx5_wqe_data_seg *)eseg + 1; + /* copy the inline part if required */ if (sq->min_inline_mode != MLX5_INLINE_MODE_NONE) { memcpy(eseg->inline_hdr.start, xdp->data, MLX5E_XDP_MIN_INLINE); eseg->inline_hdr.sz = cpu_to_be16(MLX5E_XDP_MIN_INLINE); dma_len -= MLX5E_XDP_MIN_INLINE; dma_addr += MLX5E_XDP_MIN_INLINE; - - ds_cnt += MLX5E_XDP_IHS_DS_COUNT; dseg++; } /* write the dma part */ dseg->addr = cpu_to_be64(dma_addr); dseg->byte_count = cpu_to_be32(dma_len); - dseg->lkey = sq->mkey_be; cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | MLX5_OPCODE_SEND); - cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt); sq->db.xdp.di[pi] = *di; - wi->opcode = MLX5_OPCODE_SEND; - wi->num_wqebbs = MLX5E_XDP_TX_WQEBBS; - sq->pc += MLX5E_XDP_TX_WQEBBS; + sq->pc++; sq->db.xdp.doorbell = true; rq->stats.xdp_tx++; @@ -1023,7 +1017,6 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) wqe_counter = be16_to_cpu(cqe->wqe_counter); do { - struct mlx5e_sq_wqe_info *wi; struct mlx5e_dma_info *di; u16 ci; @@ -1031,14 +1024,8 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) ci = sqcc & sq->wq.sz_m1; di = &sq->db.xdp.di[ci]; - wi = &sq->db.xdp.wqe_info[ci]; - - if (unlikely(wi->opcode == MLX5_OPCODE_NOP)) { - sqcc++; - continue; - } - sqcc += wi->num_wqebbs; + sqcc++; /* Recycle RX page */ mlx5e_page_release(rq, di, true); } while (!last_wqe); @@ -1056,21 +1043,13 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) void mlx5e_free_xdpsq_descs(struct mlx5e_sq *sq) { struct mlx5e_rq *rq = container_of(sq, struct mlx5e_rq, xdpsq); - struct mlx5e_sq_wqe_info *wi; struct mlx5e_dma_info *di; u16 ci; while (sq->cc != sq->pc) { ci = sq->cc & sq->wq.sz_m1; di = &sq->db.xdp.di[ci]; - wi = &sq->db.xdp.wqe_info[ci]; - - if (wi->opcode == MLX5_OPCODE_NOP) { - sq->cc++; - continue; - } - - sq->cc += wi->num_wqebbs; + sq->cc++; mlx5e_page_release(rq, di, false); } -- cgit From 864b2d715300d9082747fb5de2bb277359c75bff Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sat, 25 Mar 2017 00:52:11 +0300 Subject: net/mlx5e: Generalize tx helper functions for different SQ types In the next patches we will introduce different SQ types, for that we here generalize some TX helper functions to work with more basic SQ parameters, in order to re-use them for the different SQ types. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 35 ++++++++++++++++----- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 13 +++++--- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 10 +++--- drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 37 +++++------------------ 4 files changed, 48 insertions(+), 47 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index f02d2cb8d148..50f895fa5f31 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -375,10 +375,10 @@ struct mlx5e_sq { u8 type; } ____cacheline_aligned_in_smp; -static inline bool mlx5e_sq_has_room_for(struct mlx5e_sq *sq, u16 n) +static inline bool +mlx5e_wqc_has_room_for(struct mlx5_wq_cyc *wq, u16 cc, u16 pc, u16 n) { - return (((sq->wq.sz_m1 & (sq->cc - sq->pc)) >= n) || - (sq->cc == sq->pc)); + return (((wq->sz_m1 & (cc - pc)) >= n) || (cc == pc)); } struct mlx5e_dma_info { @@ -721,7 +721,6 @@ struct mlx5e_priv { void mlx5e_build_ptys2ethtool_map(void); -void mlx5e_send_nop(struct mlx5e_sq *sq, bool notify_hw); u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb, void *accel_priv, select_queue_fallback_t fallback); netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev); @@ -807,20 +806,40 @@ void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode); void mlx5e_set_rq_type_params(struct mlx5e_priv *priv, u8 rq_type); -static inline void -mlx5e_tx_notify_hw(struct mlx5e_sq *sq, struct mlx5_wqe_ctrl_seg *ctrl) +static inline +struct mlx5e_tx_wqe *mlx5e_post_nop(struct mlx5_wq_cyc *wq, u32 sqn, u16 *pc) { + u16 pi = *pc & wq->sz_m1; + struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); + struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; + + memset(cseg, 0, sizeof(*cseg)); + + cseg->opmod_idx_opcode = cpu_to_be32((*pc << 8) | MLX5_OPCODE_NOP); + cseg->qpn_ds = cpu_to_be32((sqn << 8) | 0x01); + + (*pc)++; + + return wqe; +} + +static inline +void mlx5e_notify_hw(struct mlx5_wq_cyc *wq, u16 pc, + void __iomem *uar_map, + struct mlx5_wqe_ctrl_seg *ctrl) +{ + ctrl->fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE; /* ensure wqe is visible to device before updating doorbell record */ dma_wmb(); - *sq->wq.db = cpu_to_be32(sq->pc); + *wq->db = cpu_to_be32(pc); /* ensure doorbell record is visible to device before ringing the * doorbell */ wmb(); - mlx5_write64((__be32 *)ctrl, sq->uar_map, NULL); + mlx5_write64((__be32 *)ctrl, uar_map, NULL); } static inline void mlx5e_cq_arm(struct mlx5e_cq *cq) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index d39ee6669b8e..7faf2bcccfa6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -847,6 +847,7 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, { struct mlx5e_sq *sq = &c->icosq; u16 pi = sq->pc & sq->wq.sz_m1; + struct mlx5e_tx_wqe *nopwqe; int err; err = mlx5e_create_rq(c, param, rq); @@ -867,8 +868,9 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP; sq->db.ico_wqe[pi].num_wqebbs = 1; - mlx5e_send_nop(sq, true); /* trigger mlx5e_post_rx_wqes() */ - + nopwqe = mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); + mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &nopwqe->ctrl); + sq->stats.nop++; /* TODO no need for SQ stats in ico */ return 0; err_disable_rq: @@ -1202,9 +1204,12 @@ static void mlx5e_close_sq(struct mlx5e_sq *sq) netif_tx_disable_queue(sq->txq); /* last doorbell out, godspeed .. */ - if (mlx5e_sq_has_room_for(sq, 1)) { + if (mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, 1)) { + struct mlx5e_tx_wqe *nop; + sq->db.txq.skb[(sq->pc & sq->wq.sz_m1)] = NULL; - mlx5e_send_nop(sq, true); + nop = mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); + mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &nop->ctrl); } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 1b50c54614ac..141dcc486063 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -341,7 +341,8 @@ static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix) while ((pi = (sq->pc & wq->sz_m1)) > sq->edge) { sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP; sq->db.ico_wqe[pi].num_wqebbs = 1; - mlx5e_send_nop(sq, false); + mlx5e_post_nop(wq, sq->sqn, &sq->pc); + sq->stats.nop++; } wqe = mlx5_wq_cyc_get_wqe(wq, pi); @@ -353,7 +354,7 @@ static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix) sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_UMR; sq->db.ico_wqe[pi].num_wqebbs = num_wqebbs; sq->pc += num_wqebbs; - mlx5e_tx_notify_hw(sq, &wqe->ctrl); + mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &wqe->ctrl); } static int mlx5e_alloc_rx_umr_mpwqe(struct mlx5e_rq *rq, @@ -645,8 +646,7 @@ static inline void mlx5e_xmit_xdp_doorbell(struct mlx5e_sq *sq) wqe = mlx5_wq_cyc_get_wqe(wq, pi); - wqe->ctrl.fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE; - mlx5e_tx_notify_hw(sq, &wqe->ctrl); + mlx5e_notify_hw(wq, sq->pc, sq->uar_map, &wqe->ctrl); } static inline bool mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq, @@ -675,7 +675,7 @@ static inline bool mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq, return false; } - if (unlikely(!mlx5e_sq_has_room_for(sq, 1))) { + if (unlikely(!mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1))) { if (sq->db.xdp.doorbell) { /* SQ is full, ring doorbell */ mlx5e_xmit_xdp_doorbell(sq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 7497b6ac4382..897eaea6f51f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -38,29 +38,6 @@ #define MLX5E_SQ_STOP_ROOM (MLX5_SEND_WQE_MAX_WQEBBS +\ MLX5E_SQ_NOPS_ROOM) -void mlx5e_send_nop(struct mlx5e_sq *sq, bool notify_hw) -{ - struct mlx5_wq_cyc *wq = &sq->wq; - - u16 pi = sq->pc & wq->sz_m1; - struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); - - struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; - - memset(cseg, 0, sizeof(*cseg)); - - cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | MLX5_OPCODE_NOP); - cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | 0x01); - - sq->pc++; - sq->stats.nop++; - - if (notify_hw) { - cseg->fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE; - mlx5e_tx_notify_hw(sq, &wqe->ctrl); - } -} - static inline void mlx5e_tx_dma_unmap(struct device *pdev, struct mlx5e_sq_dma *dma) { @@ -331,21 +308,21 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; - if (unlikely(!mlx5e_sq_has_room_for(sq, MLX5E_SQ_STOP_ROOM))) { + if (unlikely(!mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, + MLX5E_SQ_STOP_ROOM))) { netif_tx_stop_queue(sq->txq); sq->stats.stopped++; } sq->stats.xmit_more += skb->xmit_more; - if (!skb->xmit_more || netif_xmit_stopped(sq->txq)) { - cseg->fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE; - mlx5e_tx_notify_hw(sq, &wqe->ctrl); - } + if (!skb->xmit_more || netif_xmit_stopped(sq->txq)) + mlx5e_notify_hw(wq, sq->pc, sq->uar_map, cseg); /* fill sq edge with nops to avoid wqe wrap around */ while ((pi = (sq->pc & wq->sz_m1)) > sq->edge) { sq->db.txq.skb[pi] = NULL; - mlx5e_send_nop(sq, false); + mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); + sq->stats.nop++; } return NETDEV_TX_OK; @@ -456,7 +433,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) netdev_tx_completed_queue(sq->txq, npkts, nbytes); if (netif_tx_queue_stopped(sq->txq) && - mlx5e_sq_has_room_for(sq, MLX5E_SQ_STOP_ROOM)) { + mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, MLX5E_SQ_STOP_ROOM)) { netif_tx_wake_queue(sq->txq); sq->stats.wake++; } -- cgit From 3b77235b94347e813e2d4d33363512127d32e899 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sat, 25 Mar 2017 00:52:12 +0300 Subject: net/mlx5e: Proper names for SQ/RQ/CQ functions Rename mlx5e_{create,destroy}_{sq,rq,cq} to mlx5e_{alloc,free}_{sq,rq,cq}. Rename mlx5e_{enable,disable}_{sq,rq,cq} to mlx5e_{create,destroy}_{sq,rq,cq}. mlx5e_{enable,disable}_{sq,rq,cq} used to actually create/destroy the SQ in FW, so we rename them to align the functions names with FW semantics. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 126 +++++++++++----------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 7faf2bcccfa6..d03afa535064 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -539,9 +539,9 @@ static int mlx5e_create_rq_umr_mkey(struct mlx5e_rq *rq) return mlx5e_create_umr_mkey(priv, num_mtts, PAGE_SHIFT, &rq->umr_mkey); } -static int mlx5e_create_rq(struct mlx5e_channel *c, - struct mlx5e_rq_param *param, - struct mlx5e_rq *rq) +static int mlx5e_alloc_rq(struct mlx5e_channel *c, + struct mlx5e_rq_param *param, + struct mlx5e_rq *rq) { struct mlx5e_priv *priv = c->priv; struct mlx5_core_dev *mdev = priv->mdev; @@ -674,7 +674,7 @@ err_rq_wq_destroy: return err; } -static void mlx5e_destroy_rq(struct mlx5e_rq *rq) +static void mlx5e_free_rq(struct mlx5e_rq *rq) { int i; @@ -699,7 +699,7 @@ static void mlx5e_destroy_rq(struct mlx5e_rq *rq) mlx5_wq_destroy(&rq->wq_ctrl); } -static int mlx5e_enable_rq(struct mlx5e_rq *rq, struct mlx5e_rq_param *param) +static int mlx5e_create_rq(struct mlx5e_rq *rq, struct mlx5e_rq_param *param) { struct mlx5e_priv *priv = rq->priv; struct mlx5_core_dev *mdev = priv->mdev; @@ -798,7 +798,7 @@ static int mlx5e_modify_rq_vsd(struct mlx5e_rq *rq, bool vsd) return err; } -static void mlx5e_disable_rq(struct mlx5e_rq *rq) +static void mlx5e_destroy_rq(struct mlx5e_rq *rq) { mlx5_core_destroy_rq(rq->priv->mdev, rq->rqn); } @@ -850,18 +850,18 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, struct mlx5e_tx_wqe *nopwqe; int err; - err = mlx5e_create_rq(c, param, rq); + err = mlx5e_alloc_rq(c, param, rq); if (err) return err; - err = mlx5e_enable_rq(rq, param); + err = mlx5e_create_rq(rq, param); if (err) - goto err_destroy_rq; + goto err_free_rq; set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); err = mlx5e_modify_rq_state(rq, MLX5_RQC_STATE_RST, MLX5_RQC_STATE_RDY); if (err) - goto err_disable_rq; + goto err_destroy_rq; if (param->am_enabled) set_bit(MLX5E_RQ_STATE_AM, &c->rq.state); @@ -873,11 +873,11 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, sq->stats.nop++; /* TODO no need for SQ stats in ico */ return 0; -err_disable_rq: - clear_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); - mlx5e_disable_rq(rq); err_destroy_rq: + clear_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); mlx5e_destroy_rq(rq); +err_free_rq: + mlx5e_free_rq(rq); return err; } @@ -888,9 +888,9 @@ static void mlx5e_close_rq(struct mlx5e_rq *rq) napi_synchronize(&rq->channel->napi); /* prevent mlx5e_post_rx_wqes */ cancel_work_sync(&rq->am.work); - mlx5e_disable_rq(rq); - mlx5e_free_rx_descs(rq); mlx5e_destroy_rq(rq); + mlx5e_free_rx_descs(rq); + mlx5e_free_rq(rq); } static void mlx5e_free_sq_xdp_db(struct mlx5e_sq *sq) @@ -997,10 +997,10 @@ static int mlx5e_sq_get_max_wqebbs(u8 sq_type) return MLX5_SEND_WQE_MAX_WQEBBS; } -static int mlx5e_create_sq(struct mlx5e_channel *c, - int tc, - struct mlx5e_sq_param *param, - struct mlx5e_sq *sq) +static int mlx5e_alloc_sq(struct mlx5e_channel *c, + int tc, + struct mlx5e_sq_param *param, + struct mlx5e_sq *sq) { struct mlx5e_priv *priv = c->priv; struct mlx5_core_dev *mdev = priv->mdev; @@ -1051,13 +1051,13 @@ err_sq_wq_destroy: return err; } -static void mlx5e_destroy_sq(struct mlx5e_sq *sq) +static void mlx5e_free_sq(struct mlx5e_sq *sq) { mlx5e_free_sq_db(sq); mlx5_wq_destroy(&sq->wq_ctrl); } -static int mlx5e_enable_sq(struct mlx5e_sq *sq, struct mlx5e_sq_param *param) +static int mlx5e_create_sq(struct mlx5e_sq *sq, struct mlx5e_sq_param *param) { struct mlx5e_channel *c = sq->channel; struct mlx5e_priv *priv = c->priv; @@ -1139,7 +1139,7 @@ static int mlx5e_modify_sq(struct mlx5e_sq *sq, int curr_state, return err; } -static void mlx5e_disable_sq(struct mlx5e_sq *sq) +static void mlx5e_destroy_sq(struct mlx5e_sq *sq) { struct mlx5e_channel *c = sq->channel; struct mlx5e_priv *priv = c->priv; @@ -1157,19 +1157,19 @@ static int mlx5e_open_sq(struct mlx5e_channel *c, { int err; - err = mlx5e_create_sq(c, tc, param, sq); + err = mlx5e_alloc_sq(c, tc, param, sq); if (err) return err; - err = mlx5e_enable_sq(sq, param); + err = mlx5e_create_sq(sq, param); if (err) - goto err_destroy_sq; + goto err_free_sq; set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); err = mlx5e_modify_sq(sq, MLX5_SQC_STATE_RST, MLX5_SQC_STATE_RDY, false, 0); if (err) - goto err_disable_sq; + goto err_destroy_sq; if (sq->txq) { netdev_tx_reset_queue(sq->txq); @@ -1178,11 +1178,11 @@ static int mlx5e_open_sq(struct mlx5e_channel *c, return 0; -err_disable_sq: - clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); - mlx5e_disable_sq(sq); err_destroy_sq: + clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); mlx5e_destroy_sq(sq); +err_free_sq: + mlx5e_free_sq(sq); return err; } @@ -1213,14 +1213,14 @@ static void mlx5e_close_sq(struct mlx5e_sq *sq) } } - mlx5e_disable_sq(sq); - mlx5e_free_sq_descs(sq); mlx5e_destroy_sq(sq); + mlx5e_free_sq_descs(sq); + mlx5e_free_sq(sq); } -static int mlx5e_create_cq(struct mlx5e_channel *c, - struct mlx5e_cq_param *param, - struct mlx5e_cq *cq) +static int mlx5e_alloc_cq(struct mlx5e_channel *c, + struct mlx5e_cq_param *param, + struct mlx5e_cq *cq) { struct mlx5e_priv *priv = c->priv; struct mlx5_core_dev *mdev = priv->mdev; @@ -1265,12 +1265,12 @@ static int mlx5e_create_cq(struct mlx5e_channel *c, return 0; } -static void mlx5e_destroy_cq(struct mlx5e_cq *cq) +static void mlx5e_free_cq(struct mlx5e_cq *cq) { mlx5_cqwq_destroy(&cq->wq_ctrl); } -static int mlx5e_enable_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param) +static int mlx5e_create_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param) { struct mlx5e_priv *priv = cq->priv; struct mlx5_core_dev *mdev = priv->mdev; @@ -1317,7 +1317,7 @@ static int mlx5e_enable_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param) return 0; } -static void mlx5e_disable_cq(struct mlx5e_cq *cq) +static void mlx5e_destroy_cq(struct mlx5e_cq *cq) { struct mlx5e_priv *priv = cq->priv; struct mlx5_core_dev *mdev = priv->mdev; @@ -1334,13 +1334,13 @@ static int mlx5e_open_cq(struct mlx5e_channel *c, struct mlx5e_priv *priv = c->priv; struct mlx5_core_dev *mdev = priv->mdev; - err = mlx5e_create_cq(c, param, cq); + err = mlx5e_alloc_cq(c, param, cq); if (err) return err; - err = mlx5e_enable_cq(cq, param); + err = mlx5e_create_cq(cq, param); if (err) - goto err_destroy_cq; + goto err_free_cq; if (MLX5_CAP_GEN(mdev, cq_moderation)) mlx5_core_modify_cq_moderation(mdev, &cq->mcq, @@ -1348,16 +1348,16 @@ static int mlx5e_open_cq(struct mlx5e_channel *c, moderation.pkts); return 0; -err_destroy_cq: - mlx5e_destroy_cq(cq); +err_free_cq: + mlx5e_free_cq(cq); return err; } static void mlx5e_close_cq(struct mlx5e_cq *cq) { - mlx5e_disable_cq(cq); mlx5e_destroy_cq(cq); + mlx5e_free_cq(cq); } static int mlx5e_get_cpu(struct mlx5e_priv *priv, int ix) @@ -2422,9 +2422,9 @@ int mlx5e_close(struct net_device *netdev) return err; } -static int mlx5e_create_drop_rq(struct mlx5e_priv *priv, - struct mlx5e_rq *rq, - struct mlx5e_rq_param *param) +static int mlx5e_alloc_drop_rq(struct mlx5e_priv *priv, + struct mlx5e_rq *rq, + struct mlx5e_rq_param *param) { struct mlx5_core_dev *mdev = priv->mdev; void *rqc = param->rqc; @@ -2443,9 +2443,9 @@ static int mlx5e_create_drop_rq(struct mlx5e_priv *priv, return 0; } -static int mlx5e_create_drop_cq(struct mlx5e_priv *priv, - struct mlx5e_cq *cq, - struct mlx5e_cq_param *param) +static int mlx5e_alloc_drop_cq(struct mlx5e_priv *priv, + struct mlx5e_cq *cq, + struct mlx5e_cq_param *param) { struct mlx5_core_dev *mdev = priv->mdev; struct mlx5_core_cq *mcq = &cq->mcq; @@ -2487,42 +2487,42 @@ static int mlx5e_open_drop_rq(struct mlx5e_priv *priv) memset(&rq_param, 0, sizeof(rq_param)); mlx5e_build_drop_rq_param(&rq_param); - err = mlx5e_create_drop_cq(priv, cq, &cq_param); + err = mlx5e_alloc_drop_cq(priv, cq, &cq_param); if (err) return err; - err = mlx5e_enable_cq(cq, &cq_param); + err = mlx5e_create_cq(cq, &cq_param); if (err) - goto err_destroy_cq; + goto err_free_cq; - err = mlx5e_create_drop_rq(priv, rq, &rq_param); + err = mlx5e_alloc_drop_rq(priv, rq, &rq_param); if (err) - goto err_disable_cq; + goto err_destroy_cq; - err = mlx5e_enable_rq(rq, &rq_param); + err = mlx5e_create_rq(rq, &rq_param); if (err) - goto err_destroy_rq; + goto err_free_rq; return 0; -err_destroy_rq: - mlx5e_destroy_rq(&priv->drop_rq); - -err_disable_cq: - mlx5e_disable_cq(&priv->drop_rq.cq); +err_free_rq: + mlx5e_free_rq(&priv->drop_rq); err_destroy_cq: mlx5e_destroy_cq(&priv->drop_rq.cq); +err_free_cq: + mlx5e_free_cq(&priv->drop_rq.cq); + return err; } static void mlx5e_close_drop_rq(struct mlx5e_priv *priv) { - mlx5e_disable_rq(&priv->drop_rq); mlx5e_destroy_rq(&priv->drop_rq); - mlx5e_disable_cq(&priv->drop_rq.cq); + mlx5e_free_rq(&priv->drop_rq); mlx5e_destroy_cq(&priv->drop_rq.cq); + mlx5e_free_cq(&priv->drop_rq.cq); } static int mlx5e_create_tis(struct mlx5e_priv *priv, int tc) -- cgit From 33ad9711861060d82d5c9c3a5ffb238f4d54b097 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sat, 25 Mar 2017 00:52:13 +0300 Subject: net/mlx5e: Generalize SQ create/modify/destroy functions In the next patches we will introduce different SQ types, and we would want to reuse those functions, in this patch we make them agnostic to SQ type (txq, xdp, ico). Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 111 ++++++++++++++-------- 1 file changed, 69 insertions(+), 42 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index d03afa535064..dcc67df54a5c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1057,10 +1057,19 @@ static void mlx5e_free_sq(struct mlx5e_sq *sq) mlx5_wq_destroy(&sq->wq_ctrl); } -static int mlx5e_create_sq(struct mlx5e_sq *sq, struct mlx5e_sq_param *param) +struct mlx5e_create_sq_param { + struct mlx5_wq_ctrl *wq_ctrl; + u32 cqn; + u32 tisn; + u8 tis_lst_sz; + u8 min_inline_mode; +}; + +static int mlx5e_create_sq(struct mlx5e_priv *priv, + struct mlx5e_sq_param *param, + struct mlx5e_create_sq_param *csp, + u32 *sqn) { - struct mlx5e_channel *c = sq->channel; - struct mlx5e_priv *priv = c->priv; struct mlx5_core_dev *mdev = priv->mdev; void *in; @@ -1070,7 +1079,7 @@ static int mlx5e_create_sq(struct mlx5e_sq *sq, struct mlx5e_sq_param *param) int err; inlen = MLX5_ST_SZ_BYTES(create_sq_in) + - sizeof(u64) * sq->wq_ctrl.buf.npages; + sizeof(u64) * csp->wq_ctrl->buf.npages; in = mlx5_vzalloc(inlen); if (!in) return -ENOMEM; @@ -1079,38 +1088,41 @@ static int mlx5e_create_sq(struct mlx5e_sq *sq, struct mlx5e_sq_param *param) wq = MLX5_ADDR_OF(sqc, sqc, wq); memcpy(sqc, param->sqc, sizeof(param->sqc)); - - MLX5_SET(sqc, sqc, tis_num_0, param->type == MLX5E_SQ_ICO ? - 0 : priv->tisn[sq->tc]); - MLX5_SET(sqc, sqc, cqn, sq->cq.mcq.cqn); + MLX5_SET(sqc, sqc, tis_lst_sz, csp->tis_lst_sz); + MLX5_SET(sqc, sqc, tis_num_0, csp->tisn); + MLX5_SET(sqc, sqc, cqn, csp->cqn); if (MLX5_CAP_ETH(mdev, wqe_inline_mode) == MLX5_CAP_INLINE_MODE_VPORT_CONTEXT) - MLX5_SET(sqc, sqc, min_wqe_inline_mode, sq->min_inline_mode); + MLX5_SET(sqc, sqc, min_wqe_inline_mode, csp->min_inline_mode); - MLX5_SET(sqc, sqc, state, MLX5_SQC_STATE_RST); - MLX5_SET(sqc, sqc, tis_lst_sz, param->type == MLX5E_SQ_ICO ? 0 : 1); + MLX5_SET(sqc, sqc, state, MLX5_SQC_STATE_RST); MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_CYCLIC); - MLX5_SET(wq, wq, uar_page, mdev->mlx5e_res.bfreg.index); - MLX5_SET(wq, wq, log_wq_pg_sz, sq->wq_ctrl.buf.page_shift - + MLX5_SET(wq, wq, uar_page, priv->mdev->mlx5e_res.bfreg.index); + MLX5_SET(wq, wq, log_wq_pg_sz, csp->wq_ctrl->buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT); - MLX5_SET64(wq, wq, dbr_addr, sq->wq_ctrl.db.dma); + MLX5_SET64(wq, wq, dbr_addr, csp->wq_ctrl->db.dma); - mlx5_fill_page_array(&sq->wq_ctrl.buf, - (__be64 *)MLX5_ADDR_OF(wq, wq, pas)); + mlx5_fill_page_array(&csp->wq_ctrl->buf, (__be64 *)MLX5_ADDR_OF(wq, wq, pas)); - err = mlx5_core_create_sq(mdev, in, inlen, &sq->sqn); + err = mlx5_core_create_sq(mdev, in, inlen, sqn); kvfree(in); return err; } -static int mlx5e_modify_sq(struct mlx5e_sq *sq, int curr_state, - int next_state, bool update_rl, int rl_index) +struct mlx5e_modify_sq_param { + int curr_state; + int next_state; + bool rl_update; + int rl_index; +}; + +static int mlx5e_modify_sq(struct mlx5e_priv *priv, + u32 sqn, + struct mlx5e_modify_sq_param *p) { - struct mlx5e_channel *c = sq->channel; - struct mlx5e_priv *priv = c->priv; struct mlx5_core_dev *mdev = priv->mdev; void *in; @@ -1125,29 +1137,24 @@ static int mlx5e_modify_sq(struct mlx5e_sq *sq, int curr_state, sqc = MLX5_ADDR_OF(modify_sq_in, in, ctx); - MLX5_SET(modify_sq_in, in, sq_state, curr_state); - MLX5_SET(sqc, sqc, state, next_state); - if (update_rl && next_state == MLX5_SQC_STATE_RDY) { + MLX5_SET(modify_sq_in, in, sq_state, p->curr_state); + MLX5_SET(sqc, sqc, state, p->next_state); + if (p->rl_update && p->next_state == MLX5_SQC_STATE_RDY) { MLX5_SET64(modify_sq_in, in, modify_bitmask, 1); - MLX5_SET(sqc, sqc, packet_pacing_rate_limit_index, rl_index); + MLX5_SET(sqc, sqc, packet_pacing_rate_limit_index, p->rl_index); } - err = mlx5_core_modify_sq(mdev, sq->sqn, in, inlen); + err = mlx5_core_modify_sq(mdev, sqn, in, inlen); kvfree(in); return err; } -static void mlx5e_destroy_sq(struct mlx5e_sq *sq) -{ - struct mlx5e_channel *c = sq->channel; - struct mlx5e_priv *priv = c->priv; - struct mlx5_core_dev *mdev = priv->mdev; - mlx5_core_destroy_sq(mdev, sq->sqn); - if (sq->rate_limit) - mlx5_rl_remove_rate(mdev, sq->rate_limit); +static void mlx5e_destroy_sq(struct mlx5e_priv *priv, u32 sqn) +{ + mlx5_core_destroy_sq(priv->mdev, sqn); } static int mlx5e_open_sq(struct mlx5e_channel *c, @@ -1155,19 +1162,29 @@ static int mlx5e_open_sq(struct mlx5e_channel *c, struct mlx5e_sq_param *param, struct mlx5e_sq *sq) { + struct mlx5e_create_sq_param csp = {0}; + struct mlx5e_modify_sq_param msp = {0}; + struct mlx5e_priv *priv = c->priv; int err; err = mlx5e_alloc_sq(c, tc, param, sq); if (err) return err; - err = mlx5e_create_sq(sq, param); + csp.tisn = param->type == MLX5E_SQ_ICO ? 0 : priv->tisn[sq->tc]; + csp.tis_lst_sz = param->type == MLX5E_SQ_ICO ? 0 : 1; + csp.cqn = sq->cq.mcq.cqn; + csp.wq_ctrl = &sq->wq_ctrl; + csp.min_inline_mode = sq->min_inline_mode; + + err = mlx5e_create_sq(c->priv, param, &csp, &sq->sqn); if (err) goto err_free_sq; set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); - err = mlx5e_modify_sq(sq, MLX5_SQC_STATE_RST, MLX5_SQC_STATE_RDY, - false, 0); + msp.curr_state = MLX5_SQC_STATE_RST; + msp.next_state = MLX5_SQC_STATE_RDY; + err = mlx5e_modify_sq(priv, sq->sqn, &msp); if (err) goto err_destroy_sq; @@ -1180,7 +1197,7 @@ static int mlx5e_open_sq(struct mlx5e_channel *c, err_destroy_sq: clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); - mlx5e_destroy_sq(sq); + mlx5e_destroy_sq(priv, sq->sqn); err_free_sq: mlx5e_free_sq(sq); @@ -1196,9 +1213,13 @@ static inline void netif_tx_disable_queue(struct netdev_queue *txq) static void mlx5e_close_sq(struct mlx5e_sq *sq) { + struct mlx5e_channel *c = sq->channel; + struct mlx5e_priv *priv = c->priv; + struct mlx5_core_dev *mdev = priv->mdev; + clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); /* prevent netif_tx_wake_queue */ - napi_synchronize(&sq->channel->napi); + napi_synchronize(&c->napi); if (sq->txq) { netif_tx_disable_queue(sq->txq); @@ -1213,7 +1234,9 @@ static void mlx5e_close_sq(struct mlx5e_sq *sq) } } - mlx5e_destroy_sq(sq); + mlx5e_destroy_sq(priv, sq->sqn); + if (sq->rate_limit) + mlx5_rl_remove_rate(mdev, sq->rate_limit); mlx5e_free_sq_descs(sq); mlx5e_free_sq(sq); } @@ -1439,6 +1462,7 @@ static int mlx5e_set_sq_maxrate(struct net_device *dev, { struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5e_modify_sq_param msp = {0}; u16 rl_index = 0; int err; @@ -1461,8 +1485,11 @@ static int mlx5e_set_sq_maxrate(struct net_device *dev, } } - err = mlx5e_modify_sq(sq, MLX5_SQC_STATE_RDY, - MLX5_SQC_STATE_RDY, true, rl_index); + msp.curr_state = MLX5_SQC_STATE_RDY; + msp.next_state = MLX5_SQC_STATE_RDY; + msp.rl_index = rl_index; + msp.rl_update = true; + err = mlx5e_modify_sq(priv, sq->sqn, &msp); if (err) { netdev_err(dev, "Failed configuring rate %u: %d\n", rate, err); -- cgit From 3139104861d9a8fed13f813237cc998c8272eafc Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sat, 25 Mar 2017 00:52:14 +0300 Subject: net/mlx5e: Different SQ types Different SQ types (tx, xdp, ico) are growing apart, we separate them and remove unwanted parts in each one of them, to simplify data path and utilize data cache. Remove DB union from SQ structures since it is not needed anymore as we now have different SQ data type for each SQ. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 99 +++-- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 464 +++++++++++++--------- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 33 +- drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 50 +-- drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 2 +- 5 files changed, 392 insertions(+), 256 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 50f895fa5f31..bace9233dc1f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -319,13 +319,7 @@ struct mlx5e_sq_wqe_info { u8 num_wqebbs; }; -enum mlx5e_sq_type { - MLX5E_SQ_TXQ, - MLX5E_SQ_ICO, - MLX5E_SQ_XDP -}; - -struct mlx5e_sq { +struct mlx5e_txqsq { /* data path */ /* dirtied @completion */ @@ -339,18 +333,11 @@ struct mlx5e_sq { struct mlx5e_cq cq; - /* pointers to per tx element info: write@xmit, read@completion */ - union { - struct { - struct sk_buff **skb; - struct mlx5e_sq_dma *dma_fifo; - struct mlx5e_tx_wqe_info *wqe_info; - } txq; - struct mlx5e_sq_wqe_info *ico_wqe; - struct { - struct mlx5e_dma_info *di; - bool doorbell; - } xdp; + /* write@xmit, read@completion */ + struct { + struct sk_buff **skb; + struct mlx5e_sq_dma *dma_fifo; + struct mlx5e_tx_wqe_info *wqe_info; } db; /* read only */ @@ -372,7 +359,67 @@ struct mlx5e_sq { struct mlx5e_channel *channel; int tc; u32 rate_limit; - u8 type; +} ____cacheline_aligned_in_smp; + +struct mlx5e_xdpsq { + /* data path */ + + /* dirtied @rx completion */ + u16 cc; + u16 pc; + + struct mlx5e_cq cq; + + /* write@xmit, read@completion */ + struct { + struct mlx5e_dma_info *di; + bool doorbell; + } db; + + /* read only */ + struct mlx5_wq_cyc wq; + void __iomem *uar_map; + u32 sqn; + struct device *pdev; + __be32 mkey_be; + u8 min_inline_mode; + unsigned long state; + + /* control path */ + struct mlx5_wq_ctrl wq_ctrl; + struct mlx5e_channel *channel; +} ____cacheline_aligned_in_smp; + +struct mlx5e_icosq { + /* data path */ + + /* dirtied @completion */ + u16 cc; + + /* dirtied @xmit */ + u16 pc ____cacheline_aligned_in_smp; + u32 dma_fifo_pc; + u16 prev_cc; + + struct mlx5e_cq cq; + + /* write@xmit, read@completion */ + struct { + struct mlx5e_sq_wqe_info *ico_wqe; + } db; + + /* read only */ + struct mlx5_wq_cyc wq; + void __iomem *uar_map; + u32 sqn; + u16 edge; + struct device *pdev; + __be32 mkey_be; + unsigned long state; + + /* control path */ + struct mlx5_wq_ctrl wq_ctrl; + struct mlx5e_channel *channel; } ____cacheline_aligned_in_smp; static inline bool @@ -477,7 +524,7 @@ struct mlx5e_rq { /* XDP */ struct bpf_prog *xdp_prog; - struct mlx5e_sq xdpsq; + struct mlx5e_xdpsq xdpsq; /* control */ struct mlx5_wq_ctrl wq_ctrl; @@ -497,8 +544,8 @@ enum channel_flags { struct mlx5e_channel { /* data path */ struct mlx5e_rq rq; - struct mlx5e_sq sq[MLX5E_MAX_NUM_TC]; - struct mlx5e_sq icosq; /* internal control operations */ + struct mlx5e_txqsq sq[MLX5E_MAX_NUM_TC]; + struct mlx5e_icosq icosq; /* internal control operations */ bool xdp; struct napi_struct napi; struct device *pdev; @@ -680,7 +727,7 @@ struct mlx5e_profile { struct mlx5e_priv { /* priv data path fields - start */ - struct mlx5e_sq **txq_to_sq_map; + struct mlx5e_txqsq **txq_to_sq_map; int channeltc_to_txq_map[MLX5E_MAX_NUM_CHANNELS][MLX5E_MAX_NUM_TC]; struct bpf_prog *xdp_prog; /* priv data path fields - end */ @@ -731,8 +778,8 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget); bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget); int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget); bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq); -void mlx5e_free_xdpsq_descs(struct mlx5e_sq *sq); -void mlx5e_free_sq_descs(struct mlx5e_sq *sq); +void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq); +void mlx5e_free_xdpsq_descs(struct mlx5e_xdpsq *sq); void mlx5e_page_release(struct mlx5e_rq *rq, struct mlx5e_dma_info *dma_info, bool recycle); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index dcc67df54a5c..e849a0fc2653 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -52,7 +52,6 @@ struct mlx5e_sq_param { struct mlx5_wq_param wq; u16 max_inline; u8 min_inline_mode; - enum mlx5e_sq_type type; }; struct mlx5e_cq_param { @@ -402,8 +401,10 @@ static inline int mlx5e_get_wqe_mtt_sz(void) MLX5_UMR_MTT_ALIGNMENT); } -static inline void mlx5e_build_umr_wqe(struct mlx5e_rq *rq, struct mlx5e_sq *sq, - struct mlx5e_umr_wqe *wqe, u16 ix) +static inline void mlx5e_build_umr_wqe(struct mlx5e_rq *rq, + struct mlx5e_icosq *sq, + struct mlx5e_umr_wqe *wqe, + u16 ix) { struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; struct mlx5_wqe_umr_ctrl_seg *ucseg = &wqe->uctrl; @@ -845,7 +846,7 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, struct mlx5e_rq_param *param, struct mlx5e_rq *rq) { - struct mlx5e_sq *sq = &c->icosq; + struct mlx5e_icosq *sq = &c->icosq; u16 pi = sq->pc & sq->wq.sz_m1; struct mlx5e_tx_wqe *nopwqe; int err; @@ -870,7 +871,6 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, sq->db.ico_wqe[pi].num_wqebbs = 1; nopwqe = mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &nopwqe->ctrl); - sq->stats.nop++; /* TODO no need for SQ stats in ico */ return 0; err_destroy_rq: @@ -893,31 +893,70 @@ static void mlx5e_close_rq(struct mlx5e_rq *rq) mlx5e_free_rq(rq); } -static void mlx5e_free_sq_xdp_db(struct mlx5e_sq *sq) +static void mlx5e_free_xdpsq_db(struct mlx5e_xdpsq *sq) { - kfree(sq->db.xdp.di); + kfree(sq->db.di); } -static int mlx5e_alloc_sq_xdp_db(struct mlx5e_sq *sq, int numa) +static int mlx5e_alloc_xdpsq_db(struct mlx5e_xdpsq *sq, int numa) { int wq_sz = mlx5_wq_cyc_get_size(&sq->wq); - sq->db.xdp.di = kzalloc_node(sizeof(*sq->db.xdp.di) * wq_sz, + sq->db.di = kzalloc_node(sizeof(*sq->db.di) * wq_sz, GFP_KERNEL, numa); - if (!sq->db.xdp.di) { - mlx5e_free_sq_xdp_db(sq); + if (!sq->db.di) { + mlx5e_free_xdpsq_db(sq); return -ENOMEM; } return 0; } -static void mlx5e_free_sq_ico_db(struct mlx5e_sq *sq) +static int mlx5e_alloc_xdpsq(struct mlx5e_channel *c, + struct mlx5e_sq_param *param, + struct mlx5e_xdpsq *sq) +{ + void *sqc_wq = MLX5_ADDR_OF(sqc, param->sqc, wq); + struct mlx5e_priv *priv = c->priv; + struct mlx5_core_dev *mdev = priv->mdev; + int err; + + sq->pdev = c->pdev; + sq->mkey_be = c->mkey_be; + sq->channel = c; + sq->uar_map = mdev->mlx5e_res.bfreg.map; + sq->min_inline_mode = param->min_inline_mode; + + param->wq.db_numa_node = cpu_to_node(c->cpu); + err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, &sq->wq, &sq->wq_ctrl); + if (err) + return err; + sq->wq.db = &sq->wq.db[MLX5_SND_DBR]; + + err = mlx5e_alloc_xdpsq_db(sq, cpu_to_node(c->cpu)); + if (err) + goto err_sq_wq_destroy; + + return 0; + +err_sq_wq_destroy: + mlx5_wq_destroy(&sq->wq_ctrl); + + return err; +} + +static void mlx5e_free_xdpsq(struct mlx5e_xdpsq *sq) +{ + mlx5e_free_xdpsq_db(sq); + mlx5_wq_destroy(&sq->wq_ctrl); +} + +static void mlx5e_free_icosq_db(struct mlx5e_icosq *sq) { kfree(sq->db.ico_wqe); } -static int mlx5e_alloc_sq_ico_db(struct mlx5e_sq *sq, int numa) +static int mlx5e_alloc_icosq_db(struct mlx5e_icosq *sq, int numa) { u8 wq_sz = mlx5_wq_cyc_get_size(&sq->wq); @@ -929,119 +968,110 @@ static int mlx5e_alloc_sq_ico_db(struct mlx5e_sq *sq, int numa) return 0; } -static void mlx5e_free_sq_txq_db(struct mlx5e_sq *sq) +static int mlx5e_alloc_icosq(struct mlx5e_channel *c, + int tc, + struct mlx5e_sq_param *param, + struct mlx5e_icosq *sq) { - kfree(sq->db.txq.wqe_info); - kfree(sq->db.txq.dma_fifo); - kfree(sq->db.txq.skb); -} + void *sqc_wq = MLX5_ADDR_OF(sqc, param->sqc, wq); + struct mlx5e_priv *priv = c->priv; + struct mlx5_core_dev *mdev = priv->mdev; + int err; -static int mlx5e_alloc_sq_txq_db(struct mlx5e_sq *sq, int numa) -{ - int wq_sz = mlx5_wq_cyc_get_size(&sq->wq); - int df_sz = wq_sz * MLX5_SEND_WQEBB_NUM_DS; + sq->pdev = c->pdev; + sq->mkey_be = c->mkey_be; + sq->channel = c; + sq->uar_map = mdev->mlx5e_res.bfreg.map; - sq->db.txq.skb = kzalloc_node(wq_sz * sizeof(*sq->db.txq.skb), - GFP_KERNEL, numa); - sq->db.txq.dma_fifo = kzalloc_node(df_sz * sizeof(*sq->db.txq.dma_fifo), - GFP_KERNEL, numa); - sq->db.txq.wqe_info = kzalloc_node(wq_sz * sizeof(*sq->db.txq.wqe_info), - GFP_KERNEL, numa); - if (!sq->db.txq.skb || !sq->db.txq.dma_fifo || !sq->db.txq.wqe_info) { - mlx5e_free_sq_txq_db(sq); - return -ENOMEM; - } + param->wq.db_numa_node = cpu_to_node(c->cpu); + err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, &sq->wq, &sq->wq_ctrl); + if (err) + return err; + sq->wq.db = &sq->wq.db[MLX5_SND_DBR]; - sq->dma_fifo_mask = df_sz - 1; + err = mlx5e_alloc_icosq_db(sq, cpu_to_node(c->cpu)); + if (err) + goto err_sq_wq_destroy; + + sq->edge = (sq->wq.sz_m1 + 1) - MLX5E_ICOSQ_MAX_WQEBBS; return 0; + +err_sq_wq_destroy: + mlx5_wq_destroy(&sq->wq_ctrl); + + return err; } -static void mlx5e_free_sq_db(struct mlx5e_sq *sq) +static void mlx5e_free_icosq(struct mlx5e_icosq *sq) { - switch (sq->type) { - case MLX5E_SQ_TXQ: - mlx5e_free_sq_txq_db(sq); - break; - case MLX5E_SQ_ICO: - mlx5e_free_sq_ico_db(sq); - break; - case MLX5E_SQ_XDP: - mlx5e_free_sq_xdp_db(sq); - break; - } + mlx5e_free_icosq_db(sq); + mlx5_wq_destroy(&sq->wq_ctrl); } -static int mlx5e_alloc_sq_db(struct mlx5e_sq *sq, int numa) +static void mlx5e_free_txqsq_db(struct mlx5e_txqsq *sq) { - switch (sq->type) { - case MLX5E_SQ_TXQ: - return mlx5e_alloc_sq_txq_db(sq, numa); - case MLX5E_SQ_ICO: - return mlx5e_alloc_sq_ico_db(sq, numa); - case MLX5E_SQ_XDP: - return mlx5e_alloc_sq_xdp_db(sq, numa); - } - - return 0; + kfree(sq->db.wqe_info); + kfree(sq->db.dma_fifo); + kfree(sq->db.skb); } -static int mlx5e_sq_get_max_wqebbs(u8 sq_type) +static int mlx5e_alloc_txqsq_db(struct mlx5e_txqsq *sq, int numa) { - switch (sq_type) { - case MLX5E_SQ_ICO: - return MLX5E_ICOSQ_MAX_WQEBBS; - case MLX5E_SQ_XDP: - return 1; + int wq_sz = mlx5_wq_cyc_get_size(&sq->wq); + int df_sz = wq_sz * MLX5_SEND_WQEBB_NUM_DS; + + sq->db.skb = kzalloc_node(wq_sz * sizeof(*sq->db.skb), + GFP_KERNEL, numa); + sq->db.dma_fifo = kzalloc_node(df_sz * sizeof(*sq->db.dma_fifo), + GFP_KERNEL, numa); + sq->db.wqe_info = kzalloc_node(wq_sz * sizeof(*sq->db.wqe_info), + GFP_KERNEL, numa); + if (!sq->db.skb || !sq->db.dma_fifo || !sq->db.wqe_info) { + mlx5e_free_txqsq_db(sq); + return -ENOMEM; } - return MLX5_SEND_WQE_MAX_WQEBBS; + + sq->dma_fifo_mask = df_sz - 1; + + return 0; } -static int mlx5e_alloc_sq(struct mlx5e_channel *c, - int tc, - struct mlx5e_sq_param *param, - struct mlx5e_sq *sq) +static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, + int tc, + struct mlx5e_sq_param *param, + struct mlx5e_txqsq *sq) { - struct mlx5e_priv *priv = c->priv; + void *sqc_wq = MLX5_ADDR_OF(sqc, param->sqc, wq); + struct mlx5e_priv *priv = c->priv; struct mlx5_core_dev *mdev = priv->mdev; - - void *sqc = param->sqc; - void *sqc_wq = MLX5_ADDR_OF(sqc, sqc, wq); + int txq_ix; int err; - sq->type = param->type; sq->pdev = c->pdev; sq->tstamp = &priv->tstamp; sq->mkey_be = c->mkey_be; sq->channel = c; sq->tc = tc; sq->uar_map = mdev->mlx5e_res.bfreg.map; + sq->max_inline = param->max_inline; + sq->min_inline_mode = param->min_inline_mode; param->wq.db_numa_node = cpu_to_node(c->cpu); - - err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, &sq->wq, - &sq->wq_ctrl); + err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, &sq->wq, &sq->wq_ctrl); if (err) return err; + sq->wq.db = &sq->wq.db[MLX5_SND_DBR]; - sq->wq.db = &sq->wq.db[MLX5_SND_DBR]; - - sq->max_inline = param->max_inline; - sq->min_inline_mode = param->min_inline_mode; - - err = mlx5e_alloc_sq_db(sq, cpu_to_node(c->cpu)); + err = mlx5e_alloc_txqsq_db(sq, cpu_to_node(c->cpu)); if (err) goto err_sq_wq_destroy; - if (sq->type == MLX5E_SQ_TXQ) { - int txq_ix; - - txq_ix = c->ix + tc * priv->params.num_channels; - sq->txq = netdev_get_tx_queue(priv->netdev, txq_ix); - priv->txq_to_sq_map[txq_ix] = sq; - } + txq_ix = c->ix + tc * priv->params.num_channels; + sq->txq = netdev_get_tx_queue(priv->netdev, txq_ix); + priv->txq_to_sq_map[txq_ix] = sq; - sq->edge = (sq->wq.sz_m1 + 1) - mlx5e_sq_get_max_wqebbs(sq->type); + sq->edge = (sq->wq.sz_m1 + 1) - MLX5_SEND_WQE_MAX_WQEBBS; return 0; @@ -1051,9 +1081,9 @@ err_sq_wq_destroy: return err; } -static void mlx5e_free_sq(struct mlx5e_sq *sq) +static void mlx5e_free_txqsq(struct mlx5e_txqsq *sq) { - mlx5e_free_sq_db(sq); + mlx5e_free_txqsq_db(sq); mlx5_wq_destroy(&sq->wq_ctrl); } @@ -1151,55 +1181,62 @@ static int mlx5e_modify_sq(struct mlx5e_priv *priv, return err; } - static void mlx5e_destroy_sq(struct mlx5e_priv *priv, u32 sqn) { mlx5_core_destroy_sq(priv->mdev, sqn); } -static int mlx5e_open_sq(struct mlx5e_channel *c, - int tc, - struct mlx5e_sq_param *param, - struct mlx5e_sq *sq) +static int mlx5e_create_sq_rdy(struct mlx5e_priv *priv, + struct mlx5e_sq_param *param, + struct mlx5e_create_sq_param *csp, + u32 *sqn) { - struct mlx5e_create_sq_param csp = {0}; struct mlx5e_modify_sq_param msp = {0}; + int err; + + err = mlx5e_create_sq(priv, param, csp, sqn); + if (err) + return err; + + msp.curr_state = MLX5_SQC_STATE_RST; + msp.next_state = MLX5_SQC_STATE_RDY; + err = mlx5e_modify_sq(priv, *sqn, &msp); + if (err) + mlx5e_destroy_sq(priv, *sqn); + + return err; +} + +static int mlx5e_open_txqsq(struct mlx5e_channel *c, + int tc, + struct mlx5e_sq_param *param, + struct mlx5e_txqsq *sq) +{ + struct mlx5e_create_sq_param csp = {}; struct mlx5e_priv *priv = c->priv; int err; - err = mlx5e_alloc_sq(c, tc, param, sq); + err = mlx5e_alloc_txqsq(c, tc, param, sq); if (err) return err; - csp.tisn = param->type == MLX5E_SQ_ICO ? 0 : priv->tisn[sq->tc]; - csp.tis_lst_sz = param->type == MLX5E_SQ_ICO ? 0 : 1; + csp.tisn = priv->tisn[sq->tc]; + csp.tis_lst_sz = 1; csp.cqn = sq->cq.mcq.cqn; csp.wq_ctrl = &sq->wq_ctrl; csp.min_inline_mode = sq->min_inline_mode; - - err = mlx5e_create_sq(c->priv, param, &csp, &sq->sqn); - if (err) - goto err_free_sq; - set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); - msp.curr_state = MLX5_SQC_STATE_RST; - msp.next_state = MLX5_SQC_STATE_RDY; - err = mlx5e_modify_sq(priv, sq->sqn, &msp); + err = mlx5e_create_sq_rdy(c->priv, param, &csp, &sq->sqn); if (err) - goto err_destroy_sq; - - if (sq->txq) { - netdev_tx_reset_queue(sq->txq); - netif_tx_start_queue(sq->txq); - } + goto err_free_txqsq; + netdev_tx_reset_queue(sq->txq); + netif_tx_start_queue(sq->txq); return 0; -err_destroy_sq: +err_free_txqsq: clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); - mlx5e_destroy_sq(priv, sq->sqn); -err_free_sq: - mlx5e_free_sq(sq); + mlx5e_free_txqsq(sq); return err; } @@ -1211,7 +1248,7 @@ static inline void netif_tx_disable_queue(struct netdev_queue *txq) __netif_tx_unlock_bh(txq); } -static void mlx5e_close_sq(struct mlx5e_sq *sq) +static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) { struct mlx5e_channel *c = sq->channel; struct mlx5e_priv *priv = c->priv; @@ -1221,24 +1258,127 @@ static void mlx5e_close_sq(struct mlx5e_sq *sq) /* prevent netif_tx_wake_queue */ napi_synchronize(&c->napi); - if (sq->txq) { - netif_tx_disable_queue(sq->txq); + netif_tx_disable_queue(sq->txq); - /* last doorbell out, godspeed .. */ - if (mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, 1)) { - struct mlx5e_tx_wqe *nop; + /* last doorbell out, godspeed .. */ + if (mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, 1)) { + struct mlx5e_tx_wqe *nop; - sq->db.txq.skb[(sq->pc & sq->wq.sz_m1)] = NULL; - nop = mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); - mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &nop->ctrl); - } + sq->db.skb[(sq->pc & sq->wq.sz_m1)] = NULL; + nop = mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); + mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &nop->ctrl); } mlx5e_destroy_sq(priv, sq->sqn); if (sq->rate_limit) mlx5_rl_remove_rate(mdev, sq->rate_limit); - mlx5e_free_sq_descs(sq); - mlx5e_free_sq(sq); + mlx5e_free_txqsq_descs(sq); + mlx5e_free_txqsq(sq); +} + +static int mlx5e_open_icosq(struct mlx5e_channel *c, + int tc, + struct mlx5e_sq_param *param, + struct mlx5e_icosq *sq) +{ + struct mlx5e_create_sq_param csp = {}; + int err; + + err = mlx5e_alloc_icosq(c, tc, param, sq); + if (err) + return err; + + csp.cqn = sq->cq.mcq.cqn; + csp.wq_ctrl = &sq->wq_ctrl; + csp.min_inline_mode = param->min_inline_mode; + set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); + err = mlx5e_create_sq_rdy(c->priv, param, &csp, &sq->sqn); + if (err) + goto err_free_icosq; + + return 0; + +err_free_icosq: + clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); + mlx5e_free_icosq(sq); + + return err; +} + +static void mlx5e_close_icosq(struct mlx5e_icosq *sq) +{ + struct mlx5e_channel *c = sq->channel; + + clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); + napi_synchronize(&c->napi); + + mlx5e_destroy_sq(c->priv, sq->sqn); + mlx5e_free_icosq(sq); +} + +static int mlx5e_open_xdpsq(struct mlx5e_channel *c, + struct mlx5e_sq_param *param, + struct mlx5e_xdpsq *sq) +{ + unsigned int ds_cnt = MLX5E_XDP_TX_DS_COUNT; + struct mlx5e_create_sq_param csp = {}; + struct mlx5e_priv *priv = c->priv; + unsigned int inline_hdr_sz = 0; + int err; + int i; + + err = mlx5e_alloc_xdpsq(c, param, sq); + if (err) + return err; + + csp.tis_lst_sz = 1; + csp.tisn = priv->tisn[0]; /* tc = 0 */ + csp.cqn = sq->cq.mcq.cqn; + csp.wq_ctrl = &sq->wq_ctrl; + csp.min_inline_mode = sq->min_inline_mode; + set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); + err = mlx5e_create_sq_rdy(c->priv, param, &csp, &sq->sqn); + if (err) + goto err_free_xdpsq; + + if (sq->min_inline_mode != MLX5_INLINE_MODE_NONE) { + inline_hdr_sz = MLX5E_XDP_MIN_INLINE; + ds_cnt++; + } + + /* Pre initialize fixed WQE fields */ + for (i = 0; i < mlx5_wq_cyc_get_size(&sq->wq); i++) { + struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(&sq->wq, i); + struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; + struct mlx5_wqe_eth_seg *eseg = &wqe->eth; + struct mlx5_wqe_data_seg *dseg; + + cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt); + eseg->inline_hdr.sz = cpu_to_be16(inline_hdr_sz); + + dseg = (struct mlx5_wqe_data_seg *)cseg + (ds_cnt - 1); + dseg->lkey = sq->mkey_be; + } + + return 0; + +err_free_xdpsq: + clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); + mlx5e_free_xdpsq(sq); + + return err; +} + +static void mlx5e_close_xdpsq(struct mlx5e_xdpsq *sq) +{ + struct mlx5e_channel *c = sq->channel; + + clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); + napi_synchronize(&c->napi); + + mlx5e_destroy_sq(c->priv, sq->sqn); + mlx5e_free_xdpsq_descs(sq); + mlx5e_free_xdpsq(sq); } static int mlx5e_alloc_cq(struct mlx5e_channel *c, @@ -1426,7 +1566,7 @@ static int mlx5e_open_sqs(struct mlx5e_channel *c, int tc; for (tc = 0; tc < c->num_tc; tc++) { - err = mlx5e_open_sq(c, tc, &cparam->sq, &c->sq[tc]); + err = mlx5e_open_txqsq(c, tc, &cparam->sq, &c->sq[tc]); if (err) goto err_close_sqs; } @@ -1435,7 +1575,7 @@ static int mlx5e_open_sqs(struct mlx5e_channel *c, err_close_sqs: for (tc--; tc >= 0; tc--) - mlx5e_close_sq(&c->sq[tc]); + mlx5e_close_txqsq(&c->sq[tc]); return err; } @@ -1445,7 +1585,7 @@ static void mlx5e_close_sqs(struct mlx5e_channel *c) int tc; for (tc = 0; tc < c->num_tc; tc++) - mlx5e_close_sq(&c->sq[tc]); + mlx5e_close_txqsq(&c->sq[tc]); } static void mlx5e_build_channeltc_to_txq_map(struct mlx5e_priv *priv, int ix) @@ -1458,7 +1598,7 @@ static void mlx5e_build_channeltc_to_txq_map(struct mlx5e_priv *priv, int ix) } static int mlx5e_set_sq_maxrate(struct net_device *dev, - struct mlx5e_sq *sq, u32 rate) + struct mlx5e_txqsq *sq, u32 rate) { struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5_core_dev *mdev = priv->mdev; @@ -1507,7 +1647,7 @@ static int mlx5e_set_tx_maxrate(struct net_device *dev, int index, u32 rate) { struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5_core_dev *mdev = priv->mdev; - struct mlx5e_sq *sq = priv->txq_to_sq_map[index]; + struct mlx5e_txqsq *sq = priv->txq_to_sq_map[index]; int err = 0; if (!mlx5_rl_is_supported(mdev)) { @@ -1542,40 +1682,6 @@ static inline int mlx5e_get_max_num_channels(struct mlx5_core_dev *mdev) MLX5E_MAX_NUM_CHANNELS); } -static int mlx5e_open_xdpsq(struct mlx5e_channel *c, - struct mlx5e_sq_param *param, - struct mlx5e_sq *sq) -{ - unsigned int ds_cnt = MLX5E_XDP_TX_DS_COUNT; - unsigned int inline_hdr_sz = 0; - int err; - int i; - - err = mlx5e_open_sq(c, 0, param, sq); - if (err) - return err; - - if (sq->min_inline_mode != MLX5_INLINE_MODE_NONE) { - inline_hdr_sz = MLX5E_XDP_MIN_INLINE; - ds_cnt++; - } - - /* Pre initialize fixed WQE fields */ - for (i = 0; i < mlx5_wq_cyc_get_size(&sq->wq); i++) { - struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(&sq->wq, i); - struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; - struct mlx5_wqe_eth_seg *eseg = &wqe->eth; - struct mlx5_wqe_data_seg *dseg; - - cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt); - eseg->inline_hdr.sz = cpu_to_be16(inline_hdr_sz); - - dseg = (struct mlx5_wqe_data_seg *)cseg + (ds_cnt - 1); - dseg->lkey = sq->mkey_be; - } - return 0; -} - static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, struct mlx5e_channel_param *cparam, struct mlx5e_channel **cp) @@ -1585,7 +1691,6 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, struct mlx5e_cq_moder rx_cq_profile; int cpu = mlx5e_get_cpu(priv, ix); struct mlx5e_channel *c; - struct mlx5e_sq *sq; int err; int i; @@ -1632,7 +1737,7 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, napi_enable(&c->napi); - err = mlx5e_open_sq(c, 0, &cparam->icosq, &c->icosq); + err = mlx5e_open_icosq(c, 0, &cparam->icosq, &c->icosq); if (err) goto err_disable_napi; @@ -1644,7 +1749,8 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, u32 txq_ix = priv->channeltc_to_txq_map[ix][i]; if (priv->tx_rates[txq_ix]) { - sq = priv->txq_to_sq_map[txq_ix]; + struct mlx5e_txqsq *sq = priv->txq_to_sq_map[txq_ix]; + mlx5e_set_sq_maxrate(priv->netdev, sq, priv->tx_rates[txq_ix]); } @@ -1664,13 +1770,13 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, return 0; err_close_xdp_sq: if (c->xdp) - mlx5e_close_sq(&c->rq.xdpsq); + mlx5e_close_xdpsq(&c->rq.xdpsq); err_close_sqs: mlx5e_close_sqs(c); err_close_icosq: - mlx5e_close_sq(&c->icosq); + mlx5e_close_icosq(&c->icosq); err_disable_napi: napi_disable(&c->napi); @@ -1697,9 +1803,9 @@ static void mlx5e_close_channel(struct mlx5e_channel *c) { mlx5e_close_rq(&c->rq); if (c->xdp) - mlx5e_close_sq(&c->rq.xdpsq); + mlx5e_close_xdpsq(&c->rq.xdpsq); mlx5e_close_sqs(c); - mlx5e_close_sq(&c->icosq); + mlx5e_close_icosq(&c->icosq); napi_disable(&c->napi); if (c->xdp) mlx5e_close_cq(&c->rq.xdpsq.cq); @@ -1773,7 +1879,6 @@ static void mlx5e_build_sq_param(struct mlx5e_priv *priv, param->max_inline = priv->params.tx_max_inline; param->min_inline_mode = priv->params.tx_min_inline_mode; - param->type = MLX5E_SQ_TXQ; } static void mlx5e_build_common_cq_param(struct mlx5e_priv *priv, @@ -1846,8 +1951,6 @@ static void mlx5e_build_icosq_param(struct mlx5e_priv *priv, MLX5_SET(wq, wq, log_wq_sz, log_wq_size); MLX5_SET(sqc, sqc, reg_umr, MLX5_CAP_ETH(priv->mdev, reg_umr_sq)); - - param->type = MLX5E_SQ_ICO; } static void mlx5e_build_xdpsq_param(struct mlx5e_priv *priv, @@ -1861,7 +1964,6 @@ static void mlx5e_build_xdpsq_param(struct mlx5e_priv *priv, param->max_inline = priv->params.tx_max_inline; param->min_inline_mode = priv->params.tx_min_inline_mode; - param->type = MLX5E_SQ_XDP; } static void mlx5e_build_channel_param(struct mlx5e_priv *priv, struct mlx5e_channel_param *cparam) @@ -3237,7 +3339,7 @@ static void mlx5e_tx_timeout(struct net_device *dev) netdev_err(dev, "TX timeout detected\n"); for (i = 0; i < priv->params.num_channels * priv->params.num_tc; i++) { - struct mlx5e_sq *sq = priv->txq_to_sq_map[i]; + struct mlx5e_txqsq *sq = priv->txq_to_sq_map[i]; if (!netif_xmit_stopped(netdev_get_tx_queue(dev, i))) continue; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 141dcc486063..3ecbe8c2d5e3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -331,7 +331,7 @@ mlx5e_copy_skb_header_mpwqe(struct device *pdev, static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix) { struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix]; - struct mlx5e_sq *sq = &rq->channel->icosq; + struct mlx5e_icosq *sq = &rq->channel->icosq; struct mlx5_wq_cyc *wq = &sq->wq; struct mlx5e_umr_wqe *wqe; u8 num_wqebbs = DIV_ROUND_UP(sizeof(*wqe), MLX5_SEND_WQE_BB); @@ -342,7 +342,6 @@ static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix) sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP; sq->db.ico_wqe[pi].num_wqebbs = 1; mlx5e_post_nop(wq, sq->sqn, &sq->pc); - sq->stats.nop++; } wqe = mlx5_wq_cyc_get_wqe(wq, pi); @@ -638,7 +637,7 @@ static inline void mlx5e_complete_rx_cqe(struct mlx5e_rq *rq, mlx5e_build_rx_skb(cqe, cqe_bcnt, rq, skb); } -static inline void mlx5e_xmit_xdp_doorbell(struct mlx5e_sq *sq) +static inline void mlx5e_xmit_xdp_doorbell(struct mlx5e_xdpsq *sq) { struct mlx5_wq_cyc *wq = &sq->wq; struct mlx5e_tx_wqe *wqe; @@ -653,9 +652,9 @@ static inline bool mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq, struct mlx5e_dma_info *di, const struct xdp_buff *xdp) { - struct mlx5e_sq *sq = &rq->xdpsq; + struct mlx5e_xdpsq *sq = &rq->xdpsq; struct mlx5_wq_cyc *wq = &sq->wq; - u16 pi = sq->pc & wq->sz_m1; + u16 pi = sq->pc & wq->sz_m1; struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; @@ -676,10 +675,10 @@ static inline bool mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq, } if (unlikely(!mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, 1))) { - if (sq->db.xdp.doorbell) { + if (sq->db.doorbell) { /* SQ is full, ring doorbell */ mlx5e_xmit_xdp_doorbell(sq); - sq->db.xdp.doorbell = false; + sq->db.doorbell = false; } rq->stats.xdp_tx_full++; mlx5e_page_release(rq, di, true); @@ -707,10 +706,10 @@ static inline bool mlx5e_xmit_xdp_frame(struct mlx5e_rq *rq, cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | MLX5_OPCODE_SEND); - sq->db.xdp.di[pi] = *di; + sq->db.di[pi] = *di; sq->pc++; - sq->db.xdp.doorbell = true; + sq->db.doorbell = true; rq->stats.xdp_tx++; return true; } @@ -944,7 +943,7 @@ mpwrq_cqe_out: int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) { struct mlx5e_rq *rq = container_of(cq, struct mlx5e_rq, cq); - struct mlx5e_sq *xdpsq = &rq->xdpsq; + struct mlx5e_xdpsq *xdpsq = &rq->xdpsq; int work_done = 0; if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) @@ -971,9 +970,9 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) rq->handle_rx_cqe(rq, cqe); } - if (xdpsq->db.xdp.doorbell) { + if (xdpsq->db.doorbell) { mlx5e_xmit_xdp_doorbell(xdpsq); - xdpsq->db.xdp.doorbell = false; + xdpsq->db.doorbell = false; } mlx5_cqwq_update_db_record(&cq->wq); @@ -986,12 +985,12 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) { - struct mlx5e_sq *sq; + struct mlx5e_xdpsq *sq; struct mlx5e_rq *rq; u16 sqcc; int i; - sq = container_of(cq, struct mlx5e_sq, cq); + sq = container_of(cq, struct mlx5e_xdpsq, cq); if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) return false; @@ -1023,7 +1022,7 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) last_wqe = (sqcc == wqe_counter); ci = sqcc & sq->wq.sz_m1; - di = &sq->db.xdp.di[ci]; + di = &sq->db.di[ci]; sqcc++; /* Recycle RX page */ @@ -1040,7 +1039,7 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) return (i == MLX5E_TX_CQ_POLL_BUDGET); } -void mlx5e_free_xdpsq_descs(struct mlx5e_sq *sq) +void mlx5e_free_xdpsq_descs(struct mlx5e_xdpsq *sq) { struct mlx5e_rq *rq = container_of(sq, struct mlx5e_rq, xdpsq); struct mlx5e_dma_info *di; @@ -1048,7 +1047,7 @@ void mlx5e_free_xdpsq_descs(struct mlx5e_sq *sq) while (sq->cc != sq->pc) { ci = sq->cc & sq->wq.sz_m1; - di = &sq->db.xdp.di[ci]; + di = &sq->db.di[ci]; sq->cc++; mlx5e_page_release(rq, di, false); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 897eaea6f51f..20f71b55651e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -53,25 +53,25 @@ static inline void mlx5e_tx_dma_unmap(struct device *pdev, } } -static inline void mlx5e_dma_push(struct mlx5e_sq *sq, +static inline void mlx5e_dma_push(struct mlx5e_txqsq *sq, dma_addr_t addr, u32 size, enum mlx5e_dma_map_type map_type) { u32 i = sq->dma_fifo_pc & sq->dma_fifo_mask; - sq->db.txq.dma_fifo[i].addr = addr; - sq->db.txq.dma_fifo[i].size = size; - sq->db.txq.dma_fifo[i].type = map_type; + sq->db.dma_fifo[i].addr = addr; + sq->db.dma_fifo[i].size = size; + sq->db.dma_fifo[i].type = map_type; sq->dma_fifo_pc++; } -static inline struct mlx5e_sq_dma *mlx5e_dma_get(struct mlx5e_sq *sq, u32 i) +static inline struct mlx5e_sq_dma *mlx5e_dma_get(struct mlx5e_txqsq *sq, u32 i) { - return &sq->db.txq.dma_fifo[i & sq->dma_fifo_mask]; + return &sq->db.dma_fifo[i & sq->dma_fifo_mask]; } -static void mlx5e_dma_unmap_wqe_err(struct mlx5e_sq *sq, u8 num_dma) +static void mlx5e_dma_unmap_wqe_err(struct mlx5e_txqsq *sq, u8 num_dma) { int i; @@ -176,13 +176,13 @@ static inline void mlx5e_insert_vlan(void *start, struct sk_buff *skb, u16 ihs, mlx5e_tx_skb_pull_inline(skb_data, skb_len, cpy2_sz); } -static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) +static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb) { struct mlx5_wq_cyc *wq = &sq->wq; u16 pi = sq->pc & wq->sz_m1; struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); - struct mlx5e_tx_wqe_info *wi = &sq->db.txq.wqe_info[pi]; + struct mlx5e_tx_wqe_info *wi = &sq->db.wqe_info[pi]; struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; struct mlx5_wqe_eth_seg *eseg = &wqe->eth; @@ -298,7 +298,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | opcode); cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt); - sq->db.txq.skb[pi] = skb; + sq->db.skb[pi] = skb; wi->num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS); sq->pc += wi->num_wqebbs; @@ -320,7 +320,7 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb) /* fill sq edge with nops to avoid wqe wrap around */ while ((pi = (sq->pc & wq->sz_m1)) > sq->edge) { - sq->db.txq.skb[pi] = NULL; + sq->db.skb[pi] = NULL; mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); sq->stats.nop++; } @@ -339,21 +339,21 @@ dma_unmap_wqe_err: netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev) { struct mlx5e_priv *priv = netdev_priv(dev); - struct mlx5e_sq *sq = priv->txq_to_sq_map[skb_get_queue_mapping(skb)]; + struct mlx5e_txqsq *sq = priv->txq_to_sq_map[skb_get_queue_mapping(skb)]; return mlx5e_sq_xmit(sq, skb); } bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) { - struct mlx5e_sq *sq; + struct mlx5e_txqsq *sq; u32 dma_fifo_cc; u32 nbytes; u16 npkts; u16 sqcc; int i; - sq = container_of(cq, struct mlx5e_sq, cq); + sq = container_of(cq, struct mlx5e_txqsq, cq); if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) return false; @@ -391,8 +391,8 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) last_wqe = (sqcc == wqe_counter); ci = sqcc & sq->wq.sz_m1; - skb = sq->db.txq.skb[ci]; - wi = &sq->db.txq.wqe_info[ci]; + skb = sq->db.skb[ci]; + wi = &sq->db.wqe_info[ci]; if (unlikely(!skb)) { /* nop */ sqcc++; @@ -441,7 +441,7 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) return (i == MLX5E_TX_CQ_POLL_BUDGET); } -static void mlx5e_free_txq_sq_descs(struct mlx5e_sq *sq) +void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq) { struct mlx5e_tx_wqe_info *wi; struct sk_buff *skb; @@ -450,8 +450,8 @@ static void mlx5e_free_txq_sq_descs(struct mlx5e_sq *sq) while (sq->cc != sq->pc) { ci = sq->cc & sq->wq.sz_m1; - skb = sq->db.txq.skb[ci]; - wi = &sq->db.txq.wqe_info[ci]; + skb = sq->db.skb[ci]; + wi = &sq->db.wqe_info[ci]; if (!skb) { /* nop */ sq->cc++; @@ -469,15 +469,3 @@ static void mlx5e_free_txq_sq_descs(struct mlx5e_sq *sq) sq->cc += wi->num_wqebbs; } } - -void mlx5e_free_sq_descs(struct mlx5e_sq *sq) -{ - switch (sq->type) { - case MLX5E_SQ_TXQ: - mlx5e_free_txq_sq_descs(sq); - break; - case MLX5E_SQ_XDP: - mlx5e_free_xdpsq_descs(sq); - break; - } -} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index c880022bb21a..3317ef561a75 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -51,7 +51,7 @@ struct mlx5_cqe64 *mlx5e_get_cqe(struct mlx5e_cq *cq) static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq) { - struct mlx5e_sq *sq = container_of(cq, struct mlx5e_sq, cq); + struct mlx5e_icosq *sq = container_of(cq, struct mlx5e_icosq, cq); struct mlx5_wq_cyc *wq; struct mlx5_cqe64 *cqe; u16 sqcc; -- cgit From 545cd5e5ec5477c325e4098b6fd21213dceda408 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 24 Mar 2017 10:07:53 -0700 Subject: net: Busy polling should ignore sender CPUs This patch is a cleanup/fix for NAPI IDs following the changes that made it so that sender_cpu and napi_id were doing a better job of sharing the same location in the sk_buff. One issue I found is that we weren't validating the napi_id as being valid before we started trying to setup the busy polling. This change corrects that by using the MIN_NAPI_ID value that is now used in both allocating the NAPI IDs, as well as validating them. Signed-off-by: Alexander Duyck Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/busy_poll.h | 9 +++++++-- net/core/dev.c | 13 +++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/include/net/busy_poll.h b/include/net/busy_poll.h index c0452de83086..3fcda9e70c3f 100644 --- a/include/net/busy_poll.h +++ b/include/net/busy_poll.h @@ -35,6 +35,12 @@ struct napi_struct; extern unsigned int sysctl_net_busy_read __read_mostly; extern unsigned int sysctl_net_busy_poll __read_mostly; +/* 0 - Reserved to indicate value not set + * 1..NR_CPUS - Reserved for sender_cpu + * NR_CPUS+1..~0 - Region available for NAPI IDs + */ +#define MIN_NAPI_ID ((unsigned int)(NR_CPUS + 1)) + static inline bool net_busy_loop_on(void) { return sysctl_net_busy_poll; @@ -58,10 +64,9 @@ static inline unsigned long busy_loop_end_time(void) static inline bool sk_can_busy_loop(const struct sock *sk) { - return sk->sk_ll_usec && sk->sk_napi_id && !signal_pending(current); + return sk->sk_ll_usec && !signal_pending(current); } - static inline bool busy_loop_timeout(unsigned long end_time) { unsigned long now = busy_loop_us_clock(); diff --git a/net/core/dev.c b/net/core/dev.c index 7869ae3837ca..ab337bf5bbf4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5066,15 +5066,20 @@ bool sk_busy_loop(struct sock *sk, int nonblock) int (*napi_poll)(struct napi_struct *napi, int budget); void *have_poll_lock = NULL; struct napi_struct *napi; + unsigned int napi_id; int rc; restart: + napi_id = READ_ONCE(sk->sk_napi_id); + if (napi_id < MIN_NAPI_ID) + return 0; + rc = false; napi_poll = NULL; rcu_read_lock(); - napi = napi_by_id(sk->sk_napi_id); + napi = napi_by_id(napi_id); if (!napi) goto out; @@ -5143,10 +5148,10 @@ static void napi_hash_add(struct napi_struct *napi) spin_lock(&napi_hash_lock); - /* 0..NR_CPUS+1 range is reserved for sender_cpu use */ + /* 0..NR_CPUS range is reserved for sender_cpu use */ do { - if (unlikely(++napi_gen_id < NR_CPUS + 1)) - napi_gen_id = NR_CPUS + 1; + if (unlikely(++napi_gen_id < MIN_NAPI_ID)) + napi_gen_id = MIN_NAPI_ID; } while (napi_by_id(napi_gen_id)); napi->napi_id = napi_gen_id; -- cgit From e5907459ce7e2b6bc397007865ad492f10c2aeac Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 24 Mar 2017 10:08:00 -0700 Subject: tcp: Record Rx hash and NAPI ID in tcp_child_process While working on some recent busy poll changes we found that child sockets were being instantiated without NAPI ID being set. In our first attempt to fix it, it was suggested that we should just pull programming the NAPI ID into the function itself since all callers will need to have it set. In addition to the NAPI ID change I have dropped the code that was populating the Rx hash since it was actually being populated in tcp_get_cookie_sock. Reported-by: Sridhar Samudrala Signed-off-by: Alexander Duyck Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_ipv4.c | 2 -- net/ipv4/tcp_minisocks.c | 4 ++++ net/ipv6/tcp_ipv6.c | 2 -- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 7482b5d11861..20cbd2f07f28 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1409,8 +1409,6 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) if (!nsk) goto discard; if (nsk != sk) { - sock_rps_save_rxhash(nsk, skb); - sk_mark_napi_id(nsk, skb); if (tcp_child_process(sk, nsk, skb)) { rsk = nsk; goto reset; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 1e217948be62..8f6373b0cd77 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -26,6 +26,7 @@ #include #include #include +#include int sysctl_tcp_abort_on_overflow __read_mostly; @@ -799,6 +800,9 @@ int tcp_child_process(struct sock *parent, struct sock *child, int ret = 0; int state = child->sk_state; + /* record NAPI ID of child */ + sk_mark_napi_id(child, skb); + tcp_segs_in(tcp_sk(child), skb); if (!sock_owned_by_user(child)) { ret = tcp_rcv_state_process(child, skb); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 031a8c019f7a..8e42e8f54b70 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1293,8 +1293,6 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) goto discard; if (nsk != sk) { - sock_rps_save_rxhash(nsk, skb); - sk_mark_napi_id(nsk, skb); if (tcp_child_process(sk, nsk, skb)) goto reset; if (opt_skb) -- cgit From d2e64dbbe95b2b51eb723134274de1d3f30bce80 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 24 Mar 2017 10:08:06 -0700 Subject: net: Only define skb_mark_napi_id in one spot instead of two Instead of defining two versions of skb_mark_napi_id I think it is more readable to just match the format of the sk_mark_napi_id functions and just wrap the contents of the function instead of defining two versions of the function. This way we can save a few lines of code since we only need 2 of the ifdef/endif but needed 5 for the extra function declaration. Signed-off-by: Alexander Duyck Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/busy_poll.h | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/include/net/busy_poll.h b/include/net/busy_poll.h index 3fcda9e70c3f..b82d6ba70a14 100644 --- a/include/net/busy_poll.h +++ b/include/net/busy_poll.h @@ -76,14 +76,6 @@ static inline bool busy_loop_timeout(unsigned long end_time) bool sk_busy_loop(struct sock *sk, int nonblock); -/* used in the NIC receive handler to mark the skb */ -static inline void skb_mark_napi_id(struct sk_buff *skb, - struct napi_struct *napi) -{ - skb->napi_id = napi->napi_id; -} - - #else /* CONFIG_NET_RX_BUSY_POLL */ static inline unsigned long net_busy_loop_on(void) { @@ -100,11 +92,6 @@ static inline bool sk_can_busy_loop(struct sock *sk) return false; } -static inline void skb_mark_napi_id(struct sk_buff *skb, - struct napi_struct *napi) -{ -} - static inline bool busy_loop_timeout(unsigned long end_time) { return true; @@ -117,6 +104,15 @@ static inline bool sk_busy_loop(struct sock *sk, int nonblock) #endif /* CONFIG_NET_RX_BUSY_POLL */ +/* used in the NIC receive handler to mark the skb */ +static inline void skb_mark_napi_id(struct sk_buff *skb, + struct napi_struct *napi) +{ +#ifdef CONFIG_NET_RX_BUSY_POLL + skb->napi_id = napi->napi_id; +#endif +} + /* used in the protocol hanlder to propagate the napi_id to the socket */ static inline void sk_mark_napi_id(struct sock *sk, const struct sk_buff *skb) { -- cgit From 2b5cd0dfa384242f78a396b90087368c9440cc9a Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 24 Mar 2017 10:08:12 -0700 Subject: net: Change return type of sk_busy_loop from bool to void checking the return value of sk_busy_loop. As there are only a few consumers of that data, and the data being checked for can be replaced with a check for !skb_queue_empty() we might as well just pull the code out of sk_busy_loop and place it in the spots that actually need it. Signed-off-by: Alexander Duyck Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/busy_poll.h | 5 ++--- net/core/datagram.c | 8 ++++++-- net/core/dev.c | 25 +++++++++++-------------- net/sctp/socket.c | 9 ++++++--- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/include/net/busy_poll.h b/include/net/busy_poll.h index b82d6ba70a14..c55760f4820f 100644 --- a/include/net/busy_poll.h +++ b/include/net/busy_poll.h @@ -74,7 +74,7 @@ static inline bool busy_loop_timeout(unsigned long end_time) return time_after(now, end_time); } -bool sk_busy_loop(struct sock *sk, int nonblock); +void sk_busy_loop(struct sock *sk, int nonblock); #else /* CONFIG_NET_RX_BUSY_POLL */ static inline unsigned long net_busy_loop_on(void) @@ -97,9 +97,8 @@ static inline bool busy_loop_timeout(unsigned long end_time) return true; } -static inline bool sk_busy_loop(struct sock *sk, int nonblock) +static inline void sk_busy_loop(struct sock *sk, int nonblock) { - return false; } #endif /* CONFIG_NET_RX_BUSY_POLL */ diff --git a/net/core/datagram.c b/net/core/datagram.c index ea633342ab0d..4608aa245410 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -256,8 +256,12 @@ struct sk_buff *__skb_try_recv_datagram(struct sock *sk, unsigned int flags, } spin_unlock_irqrestore(&queue->lock, cpu_flags); - } while (sk_can_busy_loop(sk) && - sk_busy_loop(sk, flags & MSG_DONTWAIT)); + + if (!sk_can_busy_loop(sk)) + break; + + sk_busy_loop(sk, flags & MSG_DONTWAIT); + } while (!skb_queue_empty(&sk->sk_receive_queue)); error = -EAGAIN; diff --git a/net/core/dev.c b/net/core/dev.c index ab337bf5bbf4..af70eb6ba682 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5060,21 +5060,19 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock) do_softirq(); } -bool sk_busy_loop(struct sock *sk, int nonblock) +void sk_busy_loop(struct sock *sk, int nonblock) { unsigned long end_time = !nonblock ? sk_busy_loop_end_time(sk) : 0; int (*napi_poll)(struct napi_struct *napi, int budget); void *have_poll_lock = NULL; struct napi_struct *napi; unsigned int napi_id; - int rc; restart: napi_id = READ_ONCE(sk->sk_napi_id); if (napi_id < MIN_NAPI_ID) - return 0; + return; - rc = false; napi_poll = NULL; rcu_read_lock(); @@ -5085,7 +5083,8 @@ restart: preempt_disable(); for (;;) { - rc = 0; + int work = 0; + local_bh_disable(); if (!napi_poll) { unsigned long val = READ_ONCE(napi->state); @@ -5103,12 +5102,12 @@ restart: have_poll_lock = netpoll_poll_lock(napi); napi_poll = napi->poll; } - rc = napi_poll(napi, BUSY_POLL_BUDGET); - trace_napi_poll(napi, rc, BUSY_POLL_BUDGET); + work = napi_poll(napi, BUSY_POLL_BUDGET); + trace_napi_poll(napi, work, BUSY_POLL_BUDGET); count: - if (rc > 0) + if (work > 0) __NET_ADD_STATS(sock_net(sk), - LINUX_MIB_BUSYPOLLRXPACKETS, rc); + LINUX_MIB_BUSYPOLLRXPACKETS, work); local_bh_enable(); if (nonblock || !skb_queue_empty(&sk->sk_receive_queue) || @@ -5121,9 +5120,9 @@ count: preempt_enable(); rcu_read_unlock(); cond_resched(); - rc = !skb_queue_empty(&sk->sk_receive_queue); - if (rc || busy_loop_timeout(end_time)) - return rc; + if (!skb_queue_empty(&sk->sk_receive_queue) || + busy_loop_timeout(end_time)) + return; goto restart; } cpu_relax(); @@ -5131,10 +5130,8 @@ count: if (napi_poll) busy_poll_stop(napi, have_poll_lock); preempt_enable(); - rc = !skb_queue_empty(&sk->sk_receive_queue); out: rcu_read_unlock(); - return rc; } EXPORT_SYMBOL(sk_busy_loop); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 72cc3ecf6516..ccc08fc39722 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -7518,9 +7518,12 @@ struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, if (sk->sk_shutdown & RCV_SHUTDOWN) break; - if (sk_can_busy_loop(sk) && - sk_busy_loop(sk, noblock)) - continue; + if (sk_can_busy_loop(sk)) { + sk_busy_loop(sk, noblock); + + if (!skb_queue_empty(&sk->sk_receive_queue)) + continue; + } /* User doesn't want to wait. */ error = -EAGAIN; -- cgit From 37056719bba500d0d2b8216fdf641e5507ec9a0e Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 24 Mar 2017 10:08:18 -0700 Subject: net: Track start of busy loop instead of when it should end This patch flips the logic we were using to determine if the busy polling has timed out. The main motivation for this is that we will need to support two different possible timeout values in the future and by recording the start time rather than when we would want to end we can focus on making the end_time specific to the task be it epoll or socket based polling. Signed-off-by: Alexander Duyck Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- fs/select.c | 16 ++++++------ include/net/busy_poll.h | 68 +++++++++++++++++++++++++++---------------------- net/core/dev.c | 6 ++--- 3 files changed, 49 insertions(+), 41 deletions(-) diff --git a/fs/select.c b/fs/select.c index e2112270d75a..9287d3a96e35 100644 --- a/fs/select.c +++ b/fs/select.c @@ -409,7 +409,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time) int retval, i, timed_out = 0; u64 slack = 0; unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0; - unsigned long busy_end = 0; + unsigned long busy_start = 0; rcu_read_lock(); retval = max_select_fd(n, fds); @@ -512,11 +512,11 @@ int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time) /* only if found POLL_BUSY_LOOP sockets && not out of time */ if (can_busy_loop && !need_resched()) { - if (!busy_end) { - busy_end = busy_loop_end_time(); + if (!busy_start) { + busy_start = busy_loop_current_time(); continue; } - if (!busy_loop_timeout(busy_end)) + if (!busy_loop_timeout(busy_start)) continue; } busy_flag = 0; @@ -800,7 +800,7 @@ static int do_poll(struct poll_list *list, struct poll_wqueues *wait, int timed_out = 0, count = 0; u64 slack = 0; unsigned int busy_flag = net_busy_loop_on() ? POLL_BUSY_LOOP : 0; - unsigned long busy_end = 0; + unsigned long busy_start = 0; /* Optimise the no-wait case */ if (end_time && !end_time->tv_sec && !end_time->tv_nsec) { @@ -853,11 +853,11 @@ static int do_poll(struct poll_list *list, struct poll_wqueues *wait, /* only if found POLL_BUSY_LOOP sockets && not out of time */ if (can_busy_loop && !need_resched()) { - if (!busy_end) { - busy_end = busy_loop_end_time(); + if (!busy_start) { + busy_start = busy_loop_current_time(); continue; } - if (!busy_loop_timeout(busy_end)) + if (!busy_loop_timeout(busy_start)) continue; } busy_flag = 0; diff --git a/include/net/busy_poll.h b/include/net/busy_poll.h index c55760f4820f..72c82f2ea536 100644 --- a/include/net/busy_poll.h +++ b/include/net/busy_poll.h @@ -46,62 +46,70 @@ static inline bool net_busy_loop_on(void) return sysctl_net_busy_poll; } -static inline u64 busy_loop_us_clock(void) +static inline bool sk_can_busy_loop(const struct sock *sk) { - return local_clock() >> 10; + return sk->sk_ll_usec && !signal_pending(current); } -static inline unsigned long sk_busy_loop_end_time(struct sock *sk) -{ - return busy_loop_us_clock() + ACCESS_ONCE(sk->sk_ll_usec); -} +void sk_busy_loop(struct sock *sk, int nonblock); -/* in poll/select we use the global sysctl_net_ll_poll value */ -static inline unsigned long busy_loop_end_time(void) +#else /* CONFIG_NET_RX_BUSY_POLL */ +static inline unsigned long net_busy_loop_on(void) { - return busy_loop_us_clock() + ACCESS_ONCE(sysctl_net_busy_poll); + return 0; } -static inline bool sk_can_busy_loop(const struct sock *sk) +static inline bool sk_can_busy_loop(struct sock *sk) { - return sk->sk_ll_usec && !signal_pending(current); + return false; } -static inline bool busy_loop_timeout(unsigned long end_time) +static inline void sk_busy_loop(struct sock *sk, int nonblock) { - unsigned long now = busy_loop_us_clock(); - - return time_after(now, end_time); } -void sk_busy_loop(struct sock *sk, int nonblock); +#endif /* CONFIG_NET_RX_BUSY_POLL */ -#else /* CONFIG_NET_RX_BUSY_POLL */ -static inline unsigned long net_busy_loop_on(void) +static inline unsigned long busy_loop_current_time(void) { +#ifdef CONFIG_NET_RX_BUSY_POLL + return (unsigned long)(local_clock() >> 10); +#else return 0; +#endif } -static inline unsigned long busy_loop_end_time(void) +/* in poll/select we use the global sysctl_net_ll_poll value */ +static inline bool busy_loop_timeout(unsigned long start_time) { - return 0; -} +#ifdef CONFIG_NET_RX_BUSY_POLL + unsigned long bp_usec = READ_ONCE(sysctl_net_busy_poll); -static inline bool sk_can_busy_loop(struct sock *sk) -{ - return false; -} + if (bp_usec) { + unsigned long end_time = start_time + bp_usec; + unsigned long now = busy_loop_current_time(); -static inline bool busy_loop_timeout(unsigned long end_time) -{ + return time_after(now, end_time); + } +#endif return true; } -static inline void sk_busy_loop(struct sock *sk, int nonblock) +static inline bool sk_busy_loop_timeout(struct sock *sk, + unsigned long start_time) { -} +#ifdef CONFIG_NET_RX_BUSY_POLL + unsigned long bp_usec = READ_ONCE(sk->sk_ll_usec); -#endif /* CONFIG_NET_RX_BUSY_POLL */ + if (bp_usec) { + unsigned long end_time = start_time + bp_usec; + unsigned long now = busy_loop_current_time(); + + return time_after(now, end_time); + } +#endif + return true; +} /* used in the NIC receive handler to mark the skb */ static inline void skb_mark_napi_id(struct sk_buff *skb, diff --git a/net/core/dev.c b/net/core/dev.c index af70eb6ba682..2d1b5613b7fd 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5062,7 +5062,7 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock) void sk_busy_loop(struct sock *sk, int nonblock) { - unsigned long end_time = !nonblock ? sk_busy_loop_end_time(sk) : 0; + unsigned long start_time = nonblock ? 0 : busy_loop_current_time(); int (*napi_poll)(struct napi_struct *napi, int budget); void *have_poll_lock = NULL; struct napi_struct *napi; @@ -5111,7 +5111,7 @@ count: local_bh_enable(); if (nonblock || !skb_queue_empty(&sk->sk_receive_queue) || - busy_loop_timeout(end_time)) + sk_busy_loop_timeout(sk, start_time)) break; if (unlikely(need_resched())) { @@ -5121,7 +5121,7 @@ count: rcu_read_unlock(); cond_resched(); if (!skb_queue_empty(&sk->sk_receive_queue) || - busy_loop_timeout(end_time)) + sk_busy_loop_timeout(sk, start_time)) return; goto restart; } -- cgit From 7db6b048da3b9f84fe1d22fb29ff7e7c2ec6c0e5 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Fri, 24 Mar 2017 10:08:24 -0700 Subject: net: Commonize busy polling code to focus on napi_id instead of socket Move the core functionality in sk_busy_loop() to napi_busy_loop() and make it independent of sk. This enables re-using this function in epoll busy loop implementation. Signed-off-by: Sridhar Samudrala Signed-off-by: Alexander Duyck Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/busy_poll.h | 20 +++++++++++++++----- net/core/dev.c | 21 ++++++++------------- net/core/sock.c | 11 +++++++++++ 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/include/net/busy_poll.h b/include/net/busy_poll.h index 72c82f2ea536..8ffd434676b7 100644 --- a/include/net/busy_poll.h +++ b/include/net/busy_poll.h @@ -51,7 +51,11 @@ static inline bool sk_can_busy_loop(const struct sock *sk) return sk->sk_ll_usec && !signal_pending(current); } -void sk_busy_loop(struct sock *sk, int nonblock); +bool sk_busy_loop_end(void *p, unsigned long start_time); + +void napi_busy_loop(unsigned int napi_id, + bool (*loop_end)(void *, unsigned long), + void *loop_end_arg); #else /* CONFIG_NET_RX_BUSY_POLL */ static inline unsigned long net_busy_loop_on(void) @@ -64,10 +68,6 @@ static inline bool sk_can_busy_loop(struct sock *sk) return false; } -static inline void sk_busy_loop(struct sock *sk, int nonblock) -{ -} - #endif /* CONFIG_NET_RX_BUSY_POLL */ static inline unsigned long busy_loop_current_time(void) @@ -111,6 +111,16 @@ static inline bool sk_busy_loop_timeout(struct sock *sk, return true; } +static inline void sk_busy_loop(struct sock *sk, int nonblock) +{ +#ifdef CONFIG_NET_RX_BUSY_POLL + unsigned int napi_id = READ_ONCE(sk->sk_napi_id); + + if (napi_id >= MIN_NAPI_ID) + napi_busy_loop(napi_id, nonblock ? NULL : sk_busy_loop_end, sk); +#endif +} + /* used in the NIC receive handler to mark the skb */ static inline void skb_mark_napi_id(struct sk_buff *skb, struct napi_struct *napi) diff --git a/net/core/dev.c b/net/core/dev.c index 2d1b5613b7fd..ef9fe60ee294 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5060,19 +5060,16 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock) do_softirq(); } -void sk_busy_loop(struct sock *sk, int nonblock) +void napi_busy_loop(unsigned int napi_id, + bool (*loop_end)(void *, unsigned long), + void *loop_end_arg) { - unsigned long start_time = nonblock ? 0 : busy_loop_current_time(); + unsigned long start_time = loop_end ? busy_loop_current_time() : 0; int (*napi_poll)(struct napi_struct *napi, int budget); void *have_poll_lock = NULL; struct napi_struct *napi; - unsigned int napi_id; restart: - napi_id = READ_ONCE(sk->sk_napi_id); - if (napi_id < MIN_NAPI_ID) - return; - napi_poll = NULL; rcu_read_lock(); @@ -5106,12 +5103,11 @@ restart: trace_napi_poll(napi, work, BUSY_POLL_BUDGET); count: if (work > 0) - __NET_ADD_STATS(sock_net(sk), + __NET_ADD_STATS(dev_net(napi->dev), LINUX_MIB_BUSYPOLLRXPACKETS, work); local_bh_enable(); - if (nonblock || !skb_queue_empty(&sk->sk_receive_queue) || - sk_busy_loop_timeout(sk, start_time)) + if (!loop_end || loop_end(loop_end_arg, start_time)) break; if (unlikely(need_resched())) { @@ -5120,8 +5116,7 @@ count: preempt_enable(); rcu_read_unlock(); cond_resched(); - if (!skb_queue_empty(&sk->sk_receive_queue) || - sk_busy_loop_timeout(sk, start_time)) + if (loop_end(loop_end_arg, start_time)) return; goto restart; } @@ -5133,7 +5128,7 @@ count: out: rcu_read_unlock(); } -EXPORT_SYMBOL(sk_busy_loop); +EXPORT_SYMBOL(napi_busy_loop); #endif /* CONFIG_NET_RX_BUSY_POLL */ diff --git a/net/core/sock.c b/net/core/sock.c index 1b9030ee6f4b..4b762f2a3552 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -3237,3 +3237,14 @@ static int __init proto_init(void) subsys_initcall(proto_init); #endif /* PROC_FS */ + +#ifdef CONFIG_NET_RX_BUSY_POLL +bool sk_busy_loop_end(void *p, unsigned long start_time) +{ + struct sock *sk = p; + + return !skb_queue_empty(&sk->sk_receive_queue) || + sk_busy_loop_timeout(sk, start_time); +} +EXPORT_SYMBOL(sk_busy_loop_end); +#endif /* CONFIG_NET_RX_BUSY_POLL */ -- cgit From bf3b9f6372c45b0fbf24d86b8794910d20170017 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Fri, 24 Mar 2017 10:08:30 -0700 Subject: epoll: Add busy poll support to epoll with socket fds. This patch adds busy poll support to epoll. The implementation is meant to be opportunistic in that it will take the NAPI ID from the last socket that is added to the ready list that contains a valid NAPI ID and it will use that for busy polling until the ready list goes empty. Once the ready list goes empty the NAPI ID is reset and busy polling is disabled until a new socket is added to the ready list. In addition when we insert a new socket into the epoll we record the NAPI ID and assume we are going to receive events on it. If that doesn't occur it will be evicted as the active NAPI ID and we will resume normal behavior. An application can use SO_INCOMING_CPU or SO_REUSEPORT_ATTACH_C/EBPF socket options to spread the incoming connections to specific worker threads based on the incoming queue. This enables epoll for each worker thread to have only sockets that receive packets from a single queue. So when an application calls epoll_wait() and there are no events available to report, busy polling is done on the associated queue to pull the packets. Signed-off-by: Sridhar Samudrala Signed-off-by: Alexander Duyck Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- fs/eventpoll.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 341251421ced..5420767c9b68 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -42,6 +42,7 @@ #include #include #include +#include /* * LOCKING: @@ -224,6 +225,11 @@ struct eventpoll { /* used to optimize loop detection check */ int visited; struct list_head visited_list_link; + +#ifdef CONFIG_NET_RX_BUSY_POLL + /* used to track busy poll napi_id */ + unsigned int napi_id; +#endif }; /* Wait structure used by the poll hooks */ @@ -384,6 +390,77 @@ static inline int ep_events_available(struct eventpoll *ep) return !list_empty(&ep->rdllist) || ep->ovflist != EP_UNACTIVE_PTR; } +#ifdef CONFIG_NET_RX_BUSY_POLL +static bool ep_busy_loop_end(void *p, unsigned long start_time) +{ + struct eventpoll *ep = p; + + return ep_events_available(ep) || busy_loop_timeout(start_time); +} +#endif /* CONFIG_NET_RX_BUSY_POLL */ + +/* + * Busy poll if globally on and supporting sockets found && no events, + * busy loop will return if need_resched or ep_events_available. + * + * we must do our busy polling with irqs enabled + */ +static void ep_busy_loop(struct eventpoll *ep, int nonblock) +{ +#ifdef CONFIG_NET_RX_BUSY_POLL + unsigned int napi_id = READ_ONCE(ep->napi_id); + + if ((napi_id >= MIN_NAPI_ID) && net_busy_loop_on()) + napi_busy_loop(napi_id, nonblock ? NULL : ep_busy_loop_end, ep); +#endif +} + +static inline void ep_reset_busy_poll_napi_id(struct eventpoll *ep) +{ +#ifdef CONFIG_NET_RX_BUSY_POLL + if (ep->napi_id) + ep->napi_id = 0; +#endif +} + +/* + * Set epoll busy poll NAPI ID from sk. + */ +static inline void ep_set_busy_poll_napi_id(struct epitem *epi) +{ +#ifdef CONFIG_NET_RX_BUSY_POLL + struct eventpoll *ep; + unsigned int napi_id; + struct socket *sock; + struct sock *sk; + int err; + + if (!net_busy_loop_on()) + return; + + sock = sock_from_file(epi->ffd.file, &err); + if (!sock) + return; + + sk = sock->sk; + if (!sk) + return; + + napi_id = READ_ONCE(sk->sk_napi_id); + ep = epi->ep; + + /* Non-NAPI IDs can be rejected + * or + * Nothing to do if we already have this ID + */ + if (napi_id < MIN_NAPI_ID || napi_id == ep->napi_id) + return; + + /* record NAPI ID for use in next busy poll */ + ep->napi_id = napi_id; +#endif +} + /** * ep_call_nested - Perform a bound (possibly) nested call, by checking * that the recursion limit is not exceeded, and that @@ -1022,6 +1099,8 @@ static int ep_poll_callback(wait_queue_t *wait, unsigned mode, int sync, void *k spin_lock_irqsave(&ep->lock, flags); + ep_set_busy_poll_napi_id(epi); + /* * If the event mask does not contain any poll(2) event, we consider the * descriptor to be disabled. This condition is likely the effect of the @@ -1363,6 +1442,9 @@ static int ep_insert(struct eventpoll *ep, struct epoll_event *event, /* We have to drop the new item inside our item list to keep track of it */ spin_lock_irqsave(&ep->lock, flags); + /* record NAPI ID of new item if present */ + ep_set_busy_poll_napi_id(epi); + /* If the file is already "ready" we drop it inside the ready list */ if ((revents & event->events) && !ep_is_linked(&epi->rdllink)) { list_add_tail(&epi->rdllink, &ep->rdllist); @@ -1637,9 +1719,20 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, } fetch_events: + + if (!ep_events_available(ep)) + ep_busy_loop(ep, timed_out); + spin_lock_irqsave(&ep->lock, flags); if (!ep_events_available(ep)) { + /* + * Busy poll timed out. Drop NAPI ID for now, we can add + * it back in when we have moved a socket with a valid NAPI + * ID onto the ready list. + */ + ep_reset_busy_poll_napi_id(ep); + /* * We don't have any available event to return to the caller. * We need to sleep here, and we will be wake up by -- cgit From 6d4339028b350efbf87c61e6d9e113e5373545c9 Mon Sep 17 00:00:00 2001 From: Sridhar Samudrala Date: Fri, 24 Mar 2017 10:08:36 -0700 Subject: net: Introduce SO_INCOMING_NAPI_ID This socket option returns the NAPI ID associated with the queue on which the last frame is received. This information can be used by the apps to split the incoming flows among the threads based on the Rx queue on which they are received. If the NAPI ID actually represents a sender_cpu then the value is ignored and 0 is returned. Signed-off-by: Sridhar Samudrala Signed-off-by: Alexander Duyck Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- arch/alpha/include/uapi/asm/socket.h | 2 ++ arch/avr32/include/uapi/asm/socket.h | 2 ++ arch/frv/include/uapi/asm/socket.h | 2 ++ arch/ia64/include/uapi/asm/socket.h | 2 ++ arch/m32r/include/uapi/asm/socket.h | 2 ++ arch/mips/include/uapi/asm/socket.h | 1 + arch/mn10300/include/uapi/asm/socket.h | 2 ++ arch/parisc/include/uapi/asm/socket.h | 2 ++ arch/powerpc/include/uapi/asm/socket.h | 2 ++ arch/s390/include/uapi/asm/socket.h | 2 ++ arch/sparc/include/uapi/asm/socket.h | 2 ++ arch/xtensa/include/uapi/asm/socket.h | 2 ++ include/uapi/asm-generic/socket.h | 2 ++ net/core/sock.c | 12 ++++++++++++ 14 files changed, 37 insertions(+) diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h index 089db42c1b40..1bb8cac61a28 100644 --- a/arch/alpha/include/uapi/asm/socket.h +++ b/arch/alpha/include/uapi/asm/socket.h @@ -101,4 +101,6 @@ #define SO_MEMINFO 55 +#define SO_INCOMING_NAPI_ID 56 + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/avr32/include/uapi/asm/socket.h b/arch/avr32/include/uapi/asm/socket.h index 6eabcbd2f82a..f824eeb0f2e4 100644 --- a/arch/avr32/include/uapi/asm/socket.h +++ b/arch/avr32/include/uapi/asm/socket.h @@ -94,4 +94,6 @@ #define SO_MEMINFO 55 +#define SO_INCOMING_NAPI_ID 56 + #endif /* _UAPI__ASM_AVR32_SOCKET_H */ diff --git a/arch/frv/include/uapi/asm/socket.h b/arch/frv/include/uapi/asm/socket.h index bd497f8356b9..a8ad9bebfc47 100644 --- a/arch/frv/include/uapi/asm/socket.h +++ b/arch/frv/include/uapi/asm/socket.h @@ -94,5 +94,7 @@ #define SO_MEMINFO 55 +#define SO_INCOMING_NAPI_ID 56 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h index f1bb54686168..6af3253e4209 100644 --- a/arch/ia64/include/uapi/asm/socket.h +++ b/arch/ia64/include/uapi/asm/socket.h @@ -103,4 +103,6 @@ #define SO_MEMINFO 55 +#define SO_INCOMING_NAPI_ID 56 + #endif /* _ASM_IA64_SOCKET_H */ diff --git a/arch/m32r/include/uapi/asm/socket.h b/arch/m32r/include/uapi/asm/socket.h index 459c46076f6f..e98b6bb897c0 100644 --- a/arch/m32r/include/uapi/asm/socket.h +++ b/arch/m32r/include/uapi/asm/socket.h @@ -94,4 +94,6 @@ #define SO_MEMINFO 55 +#define SO_INCOMING_NAPI_ID 56 + #endif /* _ASM_M32R_SOCKET_H */ diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h index 688c18dd62ef..ae2b62e39d4d 100644 --- a/arch/mips/include/uapi/asm/socket.h +++ b/arch/mips/include/uapi/asm/socket.h @@ -112,5 +112,6 @@ #define SO_MEMINFO 55 +#define SO_INCOMING_NAPI_ID 56 #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/mn10300/include/uapi/asm/socket.h b/arch/mn10300/include/uapi/asm/socket.h index 312d2c457a04..e4ac1843ee01 100644 --- a/arch/mn10300/include/uapi/asm/socket.h +++ b/arch/mn10300/include/uapi/asm/socket.h @@ -94,4 +94,6 @@ #define SO_MEMINFO 55 +#define SO_INCOMING_NAPI_ID 56 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h index b98ec38f2083..f754c793e82a 100644 --- a/arch/parisc/include/uapi/asm/socket.h +++ b/arch/parisc/include/uapi/asm/socket.h @@ -93,4 +93,6 @@ #define SO_MEMINFO 0x4030 +#define SO_INCOMING_NAPI_ID 0x4031 + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/powerpc/include/uapi/asm/socket.h b/arch/powerpc/include/uapi/asm/socket.h index 099a889240f6..5f84af7dcb2e 100644 --- a/arch/powerpc/include/uapi/asm/socket.h +++ b/arch/powerpc/include/uapi/asm/socket.h @@ -101,4 +101,6 @@ #define SO_MEMINFO 55 +#define SO_INCOMING_NAPI_ID 56 + #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h index 6199bb34f7fa..25ac4960e707 100644 --- a/arch/s390/include/uapi/asm/socket.h +++ b/arch/s390/include/uapi/asm/socket.h @@ -100,4 +100,6 @@ #define SO_MEMINFO 55 +#define SO_INCOMING_NAPI_ID 56 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h index 12cd8c2ec422..b05513acd589 100644 --- a/arch/sparc/include/uapi/asm/socket.h +++ b/arch/sparc/include/uapi/asm/socket.h @@ -90,6 +90,8 @@ #define SO_MEMINFO 0x0039 +#define SO_INCOMING_NAPI_ID 0x003a + /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 #define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002 diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h index d0b85f6c1484..786606c81edd 100644 --- a/arch/xtensa/include/uapi/asm/socket.h +++ b/arch/xtensa/include/uapi/asm/socket.h @@ -105,4 +105,6 @@ #define SO_MEMINFO 55 +#define SO_INCOMING_NAPI_ID 56 + #endif /* _XTENSA_SOCKET_H */ diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h index 8313702c1eae..c98a52fb572a 100644 --- a/include/uapi/asm-generic/socket.h +++ b/include/uapi/asm-generic/socket.h @@ -96,4 +96,6 @@ #define SO_MEMINFO 55 +#define SO_INCOMING_NAPI_ID 56 + #endif /* __ASM_GENERIC_SOCKET_H */ diff --git a/net/core/sock.c b/net/core/sock.c index 4b762f2a3552..1a58a9dc6888 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1328,6 +1328,18 @@ int sock_getsockopt(struct socket *sock, int level, int optname, goto lenout; } + +#ifdef CONFIG_NET_RX_BUSY_POLL + case SO_INCOMING_NAPI_ID: + v.val = READ_ONCE(sk->sk_napi_id); + + /* aggregate non-NAPI IDs down to 0 */ + if (v.val < MIN_NAPI_ID) + v.val = 0; + + break; +#endif + default: /* We implement the SO_SNDLOWAT etc to not be settable * (1003.1g 7). -- cgit From 1312444374241f0e4dd0406ad8967a8b13bb6d69 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sat, 25 Mar 2017 08:28:22 +0100 Subject: mlxsw: spectrum_kvdl: Cosmetic kvdl allocator API change Currently the return allocated index and err value are multiplexed. This patch changes the API to decouple the ret value from the allocated index. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 3 ++- drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 17 +++++++---------- drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c | 6 ++++-- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 6 ++---- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 3025b766fea1..c245e4c3d9ad 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -583,7 +583,8 @@ int mlxsw_sp_bridge_vrf_join(struct mlxsw_sp *mlxsw_sp, void mlxsw_sp_bridge_vrf_leave(struct mlxsw_sp *mlxsw_sp, struct net_device *l3_dev); -int mlxsw_sp_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, unsigned int entry_count); +int mlxsw_sp_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, unsigned int entry_count, + u32 *p_entry_index); void mlxsw_sp_kvdl_free(struct mlxsw_sp *mlxsw_sp, int entry_index); struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index 4d6920d45026..d3b791f69f5b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -595,7 +595,6 @@ static int mlxsw_sp_act_kvdl_set_add(void *priv, u32 *p_kvdl_index, struct mlxsw_sp *mlxsw_sp = priv; char pefa_pl[MLXSW_REG_PEFA_LEN]; u32 kvdl_index; - int ret; int err; /* The first action set of a TCAM entry is stored directly in TCAM, @@ -604,10 +603,10 @@ static int mlxsw_sp_act_kvdl_set_add(void *priv, u32 *p_kvdl_index, if (is_first) return 0; - ret = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KDVL_ACT_EXT_SIZE); - if (ret < 0) - return ret; - kvdl_index = ret; + err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KDVL_ACT_EXT_SIZE, + &kvdl_index); + if (err) + return err; mlxsw_reg_pefa_pack(pefa_pl, kvdl_index, enc_actions); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pefa), pefa_pl); if (err) @@ -636,13 +635,11 @@ static int mlxsw_sp_act_kvdl_fwd_entry_add(void *priv, u32 *p_kvdl_index, struct mlxsw_sp *mlxsw_sp = priv; char ppbs_pl[MLXSW_REG_PPBS_LEN]; u32 kvdl_index; - int ret; int err; - ret = mlxsw_sp_kvdl_alloc(mlxsw_sp, 1); - if (ret < 0) - return ret; - kvdl_index = ret; + err = mlxsw_sp_kvdl_alloc(mlxsw_sp, 1, &kvdl_index); + if (err) + return err; mlxsw_reg_ppbs_pack(ppbs_pl, kvdl_index, local_port); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbs), ppbs_pl); if (err) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c index ac321e8e5c1a..26c26cd30c3d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_kvdl.c @@ -45,7 +45,8 @@ (MLXSW_SP_KVD_LINEAR_SIZE - MLXSW_SP_KVDL_CHUNKS_BASE) #define MLXSW_SP_CHUNK_MAX 32 -int mlxsw_sp_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, unsigned int entry_count) +int mlxsw_sp_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, unsigned int entry_count, + u32 *p_entry_index) { int entry_index; int size; @@ -72,7 +73,8 @@ int mlxsw_sp_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, unsigned int entry_count) for (i = 0; i < type_entries; i++) set_bit(entry_index + i, mlxsw_sp->kvdl.usage); - return entry_index; + *p_entry_index = entry_index; + return 0; } return -ENOBUFS; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 8dbed1d4ef2f..905d45997058 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1274,7 +1274,6 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp, bool old_adj_index_valid; u32 old_adj_index; u16 old_ecmp_size; - int ret; int i; int err; @@ -1312,15 +1311,14 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp, */ goto set_trap; - ret = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size); - if (ret < 0) { + err = mlxsw_sp_kvdl_alloc(mlxsw_sp, ecmp_size, &adj_index); + if (err) { /* We ran out of KVD linear space, just set the * trap and let everything flow through kernel. */ dev_warn(mlxsw_sp->bus_info->dev, "Failed to allocate KVD linear area for nexthop group.\n"); goto set_trap; } - adj_index = ret; old_adj_index_valid = nh_grp->adj_index_valid; old_adj_index = nh_grp->adj_index; old_ecmp_size = nh_grp->ecmp_size; -- cgit From c6adf77953bcec0ad63d7782479452464e50f7a3 Mon Sep 17 00:00:00 2001 From: Daniele Palmas Date: Fri, 24 Mar 2017 14:22:45 +0100 Subject: net: usb: qmi_wwan: add qmap mux protocol support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for qmap mux protocol available in recent Qualcomm based modems. The qmap mux protocol can be used for multiplexing data packets in order to have multiple ip streams through the same physical device. Two new sysfs files are added for adding/removing the qmap mux based interfaces (named qmimux): - /sys/class/net//qmi/add_mux - /sys/class/net//qmi/del_mux Main patch author is Bjørn Mork Signed-off-by: Bjørn Mork Signed-off-by: Daniele Palmas Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 317 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 316 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 156f7f85e486..629fe64cd74a 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -58,12 +58,198 @@ struct qmi_wwan_state { enum qmi_wwan_flags { QMI_WWAN_FLAG_RAWIP = 1 << 0, + QMI_WWAN_FLAG_MUX = 1 << 1, }; enum qmi_wwan_quirks { QMI_WWAN_QUIRK_DTR = 1 << 0, /* needs "set DTR" request */ }; +struct qmimux_hdr { + u8 pad; + u8 mux_id; + __be16 pkt_len; +}; + +struct qmimux_priv { + struct net_device *real_dev; + u8 mux_id; +}; + +static int qmimux_open(struct net_device *dev) +{ + struct qmimux_priv *priv = netdev_priv(dev); + struct net_device *real_dev = priv->real_dev; + + if (!(priv->real_dev->flags & IFF_UP)) + return -ENETDOWN; + + if (netif_carrier_ok(real_dev)) + netif_carrier_on(dev); + return 0; +} + +static int qmimux_stop(struct net_device *dev) +{ + netif_carrier_off(dev); + return 0; +} + +static netdev_tx_t qmimux_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct qmimux_priv *priv = netdev_priv(dev); + unsigned int len = skb->len; + struct qmimux_hdr *hdr; + + hdr = (struct qmimux_hdr *)skb_push(skb, sizeof(struct qmimux_hdr)); + hdr->pad = 0; + hdr->mux_id = priv->mux_id; + hdr->pkt_len = cpu_to_be16(len); + skb->dev = priv->real_dev; + return dev_queue_xmit(skb); +} + +static const struct net_device_ops qmimux_netdev_ops = { + .ndo_open = qmimux_open, + .ndo_stop = qmimux_stop, + .ndo_start_xmit = qmimux_start_xmit, +}; + +static void qmimux_setup(struct net_device *dev) +{ + dev->header_ops = NULL; /* No header */ + dev->type = ARPHRD_NONE; + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->netdev_ops = &qmimux_netdev_ops; + dev->destructor = free_netdev; +} + +static struct net_device *qmimux_find_dev(struct usbnet *dev, u8 mux_id) +{ + struct qmimux_priv *priv; + struct list_head *iter; + struct net_device *ldev; + + rcu_read_lock(); + netdev_for_each_upper_dev_rcu(dev->net, ldev, iter) { + priv = netdev_priv(ldev); + if (priv->mux_id == mux_id) { + rcu_read_unlock(); + return ldev; + } + } + rcu_read_unlock(); + return NULL; +} + +static bool qmimux_has_slaves(struct usbnet *dev) +{ + return !list_empty(&dev->net->adj_list.upper); +} + +static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb) +{ + unsigned int len, offset = sizeof(struct qmimux_hdr); + struct qmimux_hdr *hdr; + struct net_device *net; + struct sk_buff *skbn; + + while (offset < skb->len) { + hdr = (struct qmimux_hdr *)skb->data; + len = be16_to_cpu(hdr->pkt_len); + + /* drop the packet, bogus length */ + if (offset + len > skb->len) + return 0; + + /* control packet, we do not know what to do */ + if (hdr->pad & 0x80) + goto skip; + + net = qmimux_find_dev(dev, hdr->mux_id); + if (!net) + goto skip; + skbn = netdev_alloc_skb(net, len); + if (!skbn) + return 0; + skbn->dev = net; + + switch (skb->data[offset] & 0xf0) { + case 0x40: + skbn->protocol = htons(ETH_P_IP); + break; + case 0x60: + skbn->protocol = htons(ETH_P_IPV6); + break; + default: + /* not ip - do not know what to do */ + goto skip; + } + + memcpy(skb_put(skbn, len), skb->data + offset, len); + if (netif_rx(skbn) != NET_RX_SUCCESS) + return 0; + +skip: + offset += len + sizeof(struct qmimux_hdr); + } + return 1; +} + +static int qmimux_register_device(struct net_device *real_dev, u8 mux_id) +{ + struct net_device *new_dev; + struct qmimux_priv *priv; + int err; + + new_dev = alloc_netdev(sizeof(struct qmimux_priv), + "qmimux%d", NET_NAME_UNKNOWN, qmimux_setup); + if (!new_dev) + return -ENOBUFS; + + dev_net_set(new_dev, dev_net(real_dev)); + priv = netdev_priv(new_dev); + priv->mux_id = mux_id; + priv->real_dev = real_dev; + + err = register_netdevice(new_dev); + if (err < 0) + goto out_free_newdev; + + /* Account for reference in struct qmimux_priv_priv */ + dev_hold(real_dev); + + err = netdev_upper_dev_link(real_dev, new_dev); + if (err) + goto out_unregister_netdev; + + netif_stacked_transfer_operstate(real_dev, new_dev); + + return 0; + +out_unregister_netdev: + unregister_netdevice(new_dev); + dev_put(real_dev); + +out_free_newdev: + free_netdev(new_dev); + return err; +} + +static void qmimux_unregister_device(struct net_device *dev) +{ + struct qmimux_priv *priv = netdev_priv(dev); + struct net_device *real_dev = priv->real_dev; + + netdev_upper_dev_unlink(real_dev, dev); + unregister_netdevice(dev); + + /* Get rid of the reference to real_dev */ + dev_put(real_dev); +} + static void qmi_wwan_netdev_setup(struct net_device *net) { struct usbnet *dev = netdev_priv(net); @@ -137,10 +323,114 @@ err: return ret; } +static ssize_t add_mux_show(struct device *d, struct device_attribute *attr, char *buf) +{ + struct net_device *dev = to_net_dev(d); + struct qmimux_priv *priv; + struct list_head *iter; + struct net_device *ldev; + ssize_t count = 0; + + rcu_read_lock(); + netdev_for_each_upper_dev_rcu(dev, ldev, iter) { + priv = netdev_priv(ldev); + count += scnprintf(&buf[count], PAGE_SIZE - count, + "0x%02x\n", priv->mux_id); + } + rcu_read_unlock(); + return count; +} + +static ssize_t add_mux_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len) +{ + struct usbnet *dev = netdev_priv(to_net_dev(d)); + struct qmi_wwan_state *info = (void *)&dev->data; + u8 mux_id; + int ret; + + if (kstrtou8(buf, 0, &mux_id)) + return -EINVAL; + + /* mux_id [1 - 0x7f] range empirically found */ + if (mux_id < 1 || mux_id > 0x7f) + return -EINVAL; + + if (!rtnl_trylock()) + return restart_syscall(); + + if (qmimux_find_dev(dev, mux_id)) { + netdev_err(dev->net, "mux_id already present\n"); + ret = -EINVAL; + goto err; + } + + /* we don't want to modify a running netdev */ + if (netif_running(dev->net)) { + netdev_err(dev->net, "Cannot change a running device\n"); + ret = -EBUSY; + goto err; + } + + ret = qmimux_register_device(dev->net, mux_id); + if (!ret) { + info->flags |= QMI_WWAN_FLAG_MUX; + ret = len; + } +err: + rtnl_unlock(); + return ret; +} + +static ssize_t del_mux_show(struct device *d, struct device_attribute *attr, char *buf) +{ + return add_mux_show(d, attr, buf); +} + +static ssize_t del_mux_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len) +{ + struct usbnet *dev = netdev_priv(to_net_dev(d)); + struct qmi_wwan_state *info = (void *)&dev->data; + struct net_device *del_dev; + u8 mux_id; + int ret = 0; + + if (kstrtou8(buf, 0, &mux_id)) + return -EINVAL; + + if (!rtnl_trylock()) + return restart_syscall(); + + /* we don't want to modify a running netdev */ + if (netif_running(dev->net)) { + netdev_err(dev->net, "Cannot change a running device\n"); + ret = -EBUSY; + goto err; + } + + del_dev = qmimux_find_dev(dev, mux_id); + if (!del_dev) { + netdev_err(dev->net, "mux_id not present\n"); + ret = -EINVAL; + goto err; + } + qmimux_unregister_device(del_dev); + + if (!qmimux_has_slaves(dev)) + info->flags &= ~QMI_WWAN_FLAG_MUX; + ret = len; +err: + rtnl_unlock(); + return ret; +} + static DEVICE_ATTR_RW(raw_ip); +static DEVICE_ATTR_RW(add_mux); +static DEVICE_ATTR_RW(del_mux); static struct attribute *qmi_wwan_sysfs_attrs[] = { &dev_attr_raw_ip.attr, + &dev_attr_add_mux.attr, + &dev_attr_del_mux.attr, NULL, }; @@ -184,6 +474,9 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb) if (skb->len < dev->net->hard_header_len) return 0; + if (info->flags & QMI_WWAN_FLAG_MUX) + return qmimux_rx_fixup(dev, skb); + switch (skb->data[0] & 0xf0) { case 0x40: proto = htons(ETH_P_IP); @@ -1036,11 +1329,33 @@ static int qmi_wwan_probe(struct usb_interface *intf, return usbnet_probe(intf, id); } +static void qmi_wwan_disconnect(struct usb_interface *intf) +{ + struct usbnet *dev = usb_get_intfdata(intf); + struct qmi_wwan_state *info = (void *)&dev->data; + struct list_head *iter; + struct net_device *ldev; + + if (info->flags & QMI_WWAN_FLAG_MUX) { + if (!rtnl_trylock()) { + restart_syscall(); + return; + } + rcu_read_lock(); + netdev_for_each_upper_dev_rcu(dev->net, ldev, iter) + qmimux_unregister_device(ldev); + rcu_read_unlock(); + rtnl_unlock(); + info->flags &= ~QMI_WWAN_FLAG_MUX; + } + usbnet_disconnect(intf); +} + static struct usb_driver qmi_wwan_driver = { .name = "qmi_wwan", .id_table = products, .probe = qmi_wwan_probe, - .disconnect = usbnet_disconnect, + .disconnect = qmi_wwan_disconnect, .suspend = qmi_wwan_suspend, .resume = qmi_wwan_resume, .reset_resume = qmi_wwan_resume, -- cgit From 416906fc28f5763b97c7d94bcde787bfcec006bf Mon Sep 17 00:00:00 2001 From: Daniele Palmas Date: Fri, 24 Mar 2017 14:22:46 +0100 Subject: Documentation: ABI: testing: sysfs-class-net-qmi: add new qmap mux files description This patch updates the documentation related to the new files added for qmap mux support. Signed-off-by: Daniele Palmas Signed-off-by: David S. Miller --- Documentation/ABI/testing/sysfs-class-net-qmi | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-net-qmi b/Documentation/ABI/testing/sysfs-class-net-qmi index fa5a00bb1143..7122d6264c49 100644 --- a/Documentation/ABI/testing/sysfs-class-net-qmi +++ b/Documentation/ABI/testing/sysfs-class-net-qmi @@ -21,3 +21,30 @@ Description: is responsible for coordination of driver and firmware link framing mode, changing this setting to 'Y' if the firmware is configured for 'raw-ip' mode. + +What: /sys/class/net//qmi/add_mux +Date: March 2017 +KernelVersion: 4.11 +Contact: Bjørn Mork +Description: + Unsigned integer. + + Write a number ranging from 1 to 127 to add a qmap mux + based network device, supported by recent Qualcomm based + modems. + + The network device will be called qmimux. + + Userspace is in charge of managing the qmux network device + activation and data stream setup on the modem side by + using the proper QMI protocol requests. + +What: /sys/class/net//qmi/del_mux +Date: March 2017 +KernelVersion: 4.11 +Contact: Bjørn Mork +Description: + Unsigned integer. + + Write a number ranging from 1 to 127 to delete a previously + created qmap mux based network device. -- cgit From ae6336b57ede8cdf801b04e6d943617bb945e3f9 Mon Sep 17 00:00:00 2001 From: Jonas Bonn Date: Fri, 24 Mar 2017 23:23:20 +0100 Subject: gtp: rename SGSN netlink attribute This is a mostly cosmetic rename of the SGSN netlink attribute to the GTP link. The justification for this is that we will be making the module support decapsulation of "downstream" SGSN packets, in which case the netlink parameter actually refers to the upstream GGSN peer. Renaming the parameter makes the relationship clearer. The legacy name is maintained as a define in the header file in order to not break existing code. Signed-off-by: Jonas Bonn Acked-by: Pablo Neira Ayuso Acked-by: Harald Welte Signed-off-by: David S. Miller --- drivers/net/gtp.c | 22 +++++++++++----------- include/uapi/linux/gtp.h | 3 ++- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 3e1854f34420..1f6d911e77c7 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -56,7 +56,7 @@ struct pdp_ctx { u16 af; struct in_addr ms_addr_ip4; - struct in_addr sgsn_addr_ip4; + struct in_addr peer_addr_ip4; struct sock *sk; struct net_device *dev; @@ -489,17 +489,17 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, } netdev_dbg(dev, "found PDP context %p\n", pctx); - rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->sgsn_addr_ip4.s_addr); + rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->peer_addr_ip4.s_addr); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to SSGN %pI4\n", - &pctx->sgsn_addr_ip4.s_addr); + &pctx->peer_addr_ip4.s_addr); dev->stats.tx_carrier_errors++; goto err; } if (rt->dst.dev == dev) { netdev_dbg(dev, "circular route to SSGN %pI4\n", - &pctx->sgsn_addr_ip4.s_addr); + &pctx->peer_addr_ip4.s_addr); dev->stats.collisions++; goto err_rt; } @@ -866,8 +866,8 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) { pctx->gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]); pctx->af = AF_INET; - pctx->sgsn_addr_ip4.s_addr = - nla_get_be32(info->attrs[GTPA_SGSN_ADDRESS]); + pctx->peer_addr_ip4.s_addr = + nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]); pctx->ms_addr_ip4.s_addr = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); @@ -957,13 +957,13 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk, switch (pctx->gtp_version) { case GTP_V0: netdev_dbg(dev, "GTPv0-U: new PDP ctx id=%llx ssgn=%pI4 ms=%pI4 (pdp=%p)\n", - pctx->u.v0.tid, &pctx->sgsn_addr_ip4, + pctx->u.v0.tid, &pctx->peer_addr_ip4, &pctx->ms_addr_ip4, pctx); break; case GTP_V1: netdev_dbg(dev, "GTPv1-U: new PDP ctx id=%x/%x ssgn=%pI4 ms=%pI4 (pdp=%p)\n", pctx->u.v1.i_tei, pctx->u.v1.o_tei, - &pctx->sgsn_addr_ip4, &pctx->ms_addr_ip4, pctx); + &pctx->peer_addr_ip4, &pctx->ms_addr_ip4, pctx); break; } @@ -994,7 +994,7 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[GTPA_VERSION] || !info->attrs[GTPA_LINK] || - !info->attrs[GTPA_SGSN_ADDRESS] || + !info->attrs[GTPA_PEER_ADDRESS] || !info->attrs[GTPA_MS_ADDRESS]) return -EINVAL; @@ -1126,7 +1126,7 @@ static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, goto nlmsg_failure; if (nla_put_u32(skb, GTPA_VERSION, pctx->gtp_version) || - nla_put_be32(skb, GTPA_SGSN_ADDRESS, pctx->sgsn_addr_ip4.s_addr) || + nla_put_be32(skb, GTPA_PEER_ADDRESS, pctx->peer_addr_ip4.s_addr) || nla_put_be32(skb, GTPA_MS_ADDRESS, pctx->ms_addr_ip4.s_addr)) goto nla_put_failure; @@ -1237,7 +1237,7 @@ static struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = { [GTPA_LINK] = { .type = NLA_U32, }, [GTPA_VERSION] = { .type = NLA_U32, }, [GTPA_TID] = { .type = NLA_U64, }, - [GTPA_SGSN_ADDRESS] = { .type = NLA_U32, }, + [GTPA_PEER_ADDRESS] = { .type = NLA_U32, }, [GTPA_MS_ADDRESS] = { .type = NLA_U32, }, [GTPA_FLOW] = { .type = NLA_U16, }, [GTPA_NET_NS_FD] = { .type = NLA_U32, }, diff --git a/include/uapi/linux/gtp.h b/include/uapi/linux/gtp.h index 72a04a0e8cce..57d1edb8efd9 100644 --- a/include/uapi/linux/gtp.h +++ b/include/uapi/linux/gtp.h @@ -19,7 +19,8 @@ enum gtp_attrs { GTPA_LINK, GTPA_VERSION, GTPA_TID, /* for GTPv0 only */ - GTPA_SGSN_ADDRESS, + GTPA_PEER_ADDRESS, /* Remote GSN peer, either SGSN or GGSN */ +#define GTPA_SGSN_ADDRESS GTPA_PEER_ADDRESS /* maintain legacy attr name */ GTPA_MS_ADDRESS, GTPA_FLOW, GTPA_NET_NS_FD, -- cgit From 91ed81f9abc76d5a61b07cb8286c680c9330b7a1 Mon Sep 17 00:00:00 2001 From: Jonas Bonn Date: Fri, 24 Mar 2017 23:23:21 +0100 Subject: gtp: support SGSN-side tunnels The GTP-tunnel driver is explicitly GGSN-side as it searches for PDP contexts based on the incoming packets _destination_ address. If we want to place ourselves on the SGSN side of the tunnel, then we want to be identifying PDP contexts based on _source_ address. Let it be noted that in a "real" configuration this module would never be used: the SGSN normally does not see IP packets as input. The justification for this functionality is for PGW load-testing applications where the input to the SGSN is locally generally IP traffic. This patch adds a "role" argument at GTP-link creation time to specify whether we are on the GGSN or SGSN side of the tunnel; this flag is then used to determine which part of the IP packet to use in determining the PDP context. Signed-off-by: Jonas Bonn Acked-by: Pablo Neira Ayuso Acked-by: Harald Welte Signed-off-by: David S. Miller --- drivers/net/gtp.c | 42 ++++++++++++++++++++++++++++++------------ include/uapi/linux/if_link.h | 7 +++++++ 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 1f6d911e77c7..4fea1b3dfbb4 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -74,6 +74,7 @@ struct gtp_dev { struct net_device *dev; + unsigned int role; unsigned int hash_size; struct hlist_head *tid_hash; struct hlist_head *addr_hash; @@ -154,8 +155,8 @@ static struct pdp_ctx *ipv4_pdp_find(struct gtp_dev *gtp, __be32 ms_addr) return NULL; } -static bool gtp_check_src_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx, - unsigned int hdrlen) +static bool gtp_check_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx, + unsigned int hdrlen, unsigned int role) { struct iphdr *iph; @@ -164,27 +165,31 @@ static bool gtp_check_src_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx, iph = (struct iphdr *)(skb->data + hdrlen); - return iph->saddr == pctx->ms_addr_ip4.s_addr; + if (role == GTP_ROLE_SGSN) + return iph->daddr == pctx->ms_addr_ip4.s_addr; + else + return iph->saddr == pctx->ms_addr_ip4.s_addr; } -/* Check if the inner IP source address in this packet is assigned to any +/* Check if the inner IP address in this packet is assigned to any * existing mobile subscriber. */ -static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx, - unsigned int hdrlen) +static bool gtp_check_ms(struct sk_buff *skb, struct pdp_ctx *pctx, + unsigned int hdrlen, unsigned int role) { switch (ntohs(skb->protocol)) { case ETH_P_IP: - return gtp_check_src_ms_ipv4(skb, pctx, hdrlen); + return gtp_check_ms_ipv4(skb, pctx, hdrlen, role); } return false; } -static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen) +static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, + unsigned int hdrlen, unsigned int role) { struct pcpu_sw_netstats *stats; - if (!gtp_check_src_ms(skb, pctx, hdrlen)) { + if (!gtp_check_ms(skb, pctx, hdrlen, role)) { netdev_dbg(pctx->dev, "No PDP ctx for this MS\n"); return 1; } @@ -239,7 +244,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) return 1; } - return gtp_rx(pctx, skb, hdrlen); + return gtp_rx(pctx, skb, hdrlen, gtp->role); } static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) @@ -281,7 +286,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) return 1; } - return gtp_rx(pctx, skb, hdrlen); + return gtp_rx(pctx, skb, hdrlen, gtp->role); } static void gtp_encap_destroy(struct sock *sk) @@ -481,7 +486,11 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, * Prepend PDP header with TEI/TID from PDP ctx. */ iph = ip_hdr(skb); - pctx = ipv4_pdp_find(gtp, iph->daddr); + if (gtp->role == GTP_ROLE_SGSN) + pctx = ipv4_pdp_find(gtp, iph->saddr); + else + pctx = ipv4_pdp_find(gtp, iph->daddr); + if (!pctx) { netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n", &iph->daddr); @@ -685,6 +694,7 @@ static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = { [IFLA_GTP_FD0] = { .type = NLA_U32 }, [IFLA_GTP_FD1] = { .type = NLA_U32 }, [IFLA_GTP_PDP_HASHSIZE] = { .type = NLA_U32 }, + [IFLA_GTP_ROLE] = { .type = NLA_U32 }, }; static int gtp_validate(struct nlattr *tb[], struct nlattr *data[]) @@ -810,6 +820,7 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) { struct sock *sk1u = NULL; struct sock *sk0 = NULL; + unsigned int role = GTP_ROLE_GGSN; if (data[IFLA_GTP_FD0]) { u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]); @@ -830,8 +841,15 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) } } + if (data[IFLA_GTP_ROLE]) { + role = nla_get_u32(data[IFLA_GTP_ROLE]); + if (role > GTP_ROLE_SGSN) + return -EINVAL; + } + gtp->sk0 = sk0; gtp->sk1u = sk1u; + gtp->role = role; return 0; } diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 320fc1e747ee..8b405afb2376 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -538,11 +538,18 @@ enum { #define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1) /* GTP section */ + +enum ifla_gtp_role { + GTP_ROLE_GGSN = 0, + GTP_ROLE_SGSN, +}; + enum { IFLA_GTP_UNSPEC, IFLA_GTP_FD0, IFLA_GTP_FD1, IFLA_GTP_PDP_HASHSIZE, + IFLA_GTP_ROLE, __IFLA_GTP_MAX, }; #define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1) -- cgit From b1dd90cea725f794185a66c4e4ccfd4fb84632b5 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 24 Mar 2017 20:54:36 -0700 Subject: netvsc: Fix a bug in sub-channel handling All netvsc channels are handled via NAPI. Setup the "read mode" correctly for the netvsc sub-channels. Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/rndis_filter.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 91b3bcfd9acb..983582526b37 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1002,6 +1002,11 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) if (!nvchan->mrc.buf) return; + /* Because the device uses NAPI, all the interrupt batching and + * control is done via Net softirq, not the channel handling + */ + set_channel_read_mode(new_sc, HV_CALL_ISR); + ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE, nvscdev->ring_size * PAGE_SIZE, NULL, 0, netvsc_channel_cb, nvchan); -- cgit From 386f57622cc3bbb39b9c00fc8762bc9ab28e0f8d Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Fri, 24 Mar 2017 20:54:37 -0700 Subject: netvsc: Properly initialize the return value Initialize the return value correctly. Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index eb7ae79d47bb..f830bbbd8ad4 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -855,7 +855,7 @@ static int netvsc_change_mtu(struct net_device *ndev, int mtu) struct hv_device *hdev = ndevctx->device_ctx; struct netvsc_device_info device_info; bool was_running; - int ret; + int ret = 0; if (!nvdev || nvdev->destroy) return -ENODEV; -- cgit From 99ba18ef0200a824e7bae73f358916b6e3624d62 Mon Sep 17 00:00:00 2001 From: Linus Lüssing Date: Fri, 17 Feb 2017 11:17:06 +0100 Subject: batman-adv: privatize forw_packet skb assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An skb is assigned to a forw_packet only once, shortly after the forw_packet allocation. With this patch the assignment is moved into the this allocation function. Signed-off-by: Linus Lüssing Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bat_iv_ogm.c | 17 +++++++++-------- net/batman-adv/send.c | 21 ++++++++++++--------- net/batman-adv/send.h | 3 ++- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 7c3d994e90d8..f665ff3ede53 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -679,15 +679,11 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct batadv_forw_packet *forw_packet_aggr; + struct sk_buff *skb; unsigned char *skb_buff; unsigned int skb_size; atomic_t *queue_left = own_packet ? NULL : &bat_priv->batman_queue_left; - forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing, - queue_left, bat_priv); - if (!forw_packet_aggr) - return; - if (atomic_read(&bat_priv->aggregated_ogms) && packet_len < BATADV_MAX_AGGREGATION_BYTES) skb_size = BATADV_MAX_AGGREGATION_BYTES; @@ -696,9 +692,14 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, skb_size += ETH_HLEN; - forw_packet_aggr->skb = netdev_alloc_skb_ip_align(NULL, skb_size); - if (!forw_packet_aggr->skb) { - batadv_forw_packet_free(forw_packet_aggr, true); + skb = netdev_alloc_skb_ip_align(NULL, skb_size); + if (!skb) + return; + + forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing, + queue_left, bat_priv, skb); + if (!forw_packet_aggr) { + kfree_skb(skb); return; } diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 1489ec27daff..7bf470204e10 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -482,6 +482,7 @@ void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet, * @if_outgoing: The (optional) if_outgoing to be grabbed * @queue_left: The (optional) queue counter to decrease * @bat_priv: The bat_priv for the mesh of this forw_packet + * @skb: The raw packet this forwarding packet shall contain * * Allocates a forwarding packet and tries to get a reference to the * (optional) if_incoming, if_outgoing and queue_left. If queue_left @@ -493,7 +494,8 @@ struct batadv_forw_packet * batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing, atomic_t *queue_left, - struct batadv_priv *bat_priv) + struct batadv_priv *bat_priv, + struct sk_buff *skb) { struct batadv_forw_packet *forw_packet; const char *qname; @@ -525,7 +527,7 @@ batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming, INIT_HLIST_NODE(&forw_packet->list); INIT_HLIST_NODE(&forw_packet->cleanup_list); - forw_packet->skb = NULL; + forw_packet->skb = skb; forw_packet->queue_left = queue_left; forw_packet->if_incoming = if_incoming; forw_packet->if_outgoing = if_outgoing; @@ -756,22 +758,23 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, if (!primary_if) goto err; + newskb = skb_copy(skb, GFP_ATOMIC); + if (!newskb) { + batadv_hardif_put(primary_if); + goto err; + } + forw_packet = batadv_forw_packet_alloc(primary_if, NULL, &bat_priv->bcast_queue_left, - bat_priv); + bat_priv, newskb); batadv_hardif_put(primary_if); if (!forw_packet) - goto err; - - newskb = skb_copy(skb, GFP_ATOMIC); - if (!newskb) goto err_packet_free; /* as we have a copy now, it is safe to decrease the TTL */ bcast_packet = (struct batadv_bcast_packet *)newskb->data; bcast_packet->ttl--; - forw_packet->skb = newskb; forw_packet->own = own_packet; INIT_DELAYED_WORK(&forw_packet->delayed_work, @@ -781,7 +784,7 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, return NETDEV_TX_OK; err_packet_free: - batadv_forw_packet_free(forw_packet, true); + kfree_skb(newskb); err: return NETDEV_TX_BUSY; } diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index f21166d10323..8e75890406d2 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -34,7 +34,8 @@ struct batadv_forw_packet * batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing, atomic_t *queue_left, - struct batadv_priv *bat_priv); + struct batadv_priv *bat_priv, + struct sk_buff *skb); bool batadv_forw_packet_steal(struct batadv_forw_packet *packet, spinlock_t *l); void batadv_forw_packet_ogmv1_queue(struct batadv_priv *bat_priv, struct batadv_forw_packet *forw_packet, -- cgit From e2d9ba43559e5fcd94a365ba86718332737b0817 Mon Sep 17 00:00:00 2001 From: Linus Lüssing Date: Fri, 17 Feb 2017 11:17:07 +0100 Subject: batman-adv: restructure rebroadcast counter into forw_packet API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch refactors the num_packets counter of a forw_packet in the following three ways: 1) Removed dual-use of forw_packet::num_packets: -> now for aggregation purposes only 2) Using forw_packet::skb::cb::num_bcasts instead: -> for easier access in aggregation code later 3) make access to num_bcasts private to batadv_forw_packet_*() Signed-off-by: Linus Lüssing [sven@narfation.org: Change num_bcasts to unsigned] Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/distributed-arp-table.c | 2 +- net/batman-adv/main.c | 3 ++ net/batman-adv/send.c | 55 ++++++++++++++++++++++++++++++++-- net/batman-adv/send.h | 1 + net/batman-adv/soft-interface.c | 3 ++ net/batman-adv/types.h | 4 ++- 6 files changed, 63 insertions(+), 5 deletions(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 77ede40ff529..0608fcf99e7b 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1303,7 +1303,7 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, /* If this packet is an ARP_REQUEST and the node already has the * information that it is going to ask, then the packet can be dropped */ - if (forw_packet->num_packets) + if (batadv_forw_packet_is_rebroadcast(forw_packet)) goto out; vid = batadv_dat_get_vid(forw_packet->skb, &hdr_size); diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 5000c540614d..fb381fb26a66 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -516,6 +516,9 @@ static void batadv_recv_handler_init(void) BUILD_BUG_ON(sizeof(struct batadv_tvlv_tt_change) != 12); BUILD_BUG_ON(sizeof(struct batadv_tvlv_roam_adv) != 8); + i = FIELD_SIZEOF(struct sk_buff, cb); + BUILD_BUG_ON(sizeof(struct batadv_skb_cb) > i); + /* broadcast packet */ batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet; diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 7bf470204e10..403df596a73d 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -789,6 +789,55 @@ err: return NETDEV_TX_BUSY; } +/** + * batadv_forw_packet_bcasts_left - check if a retransmission is necessary + * @forw_packet: the forwarding packet to check + * @hard_iface: the interface to check on + * + * Checks whether a given packet has any (re)transmissions left on the provided + * interface. + * + * hard_iface may be NULL: In that case the number of transmissions this skb had + * so far is compared with the maximum amount of retransmissions independent of + * any interface instead. + * + * Return: True if (re)transmissions are left, false otherwise. + */ +static bool +batadv_forw_packet_bcasts_left(struct batadv_forw_packet *forw_packet, + struct batadv_hard_iface *hard_iface) +{ + unsigned int max; + + if (hard_iface) + max = hard_iface->num_bcasts; + else + max = BATADV_NUM_BCASTS_MAX; + + return BATADV_SKB_CB(forw_packet->skb)->num_bcasts < max; +} + +/** + * batadv_forw_packet_bcasts_inc - increment retransmission counter of a packet + * @forw_packet: the packet to increase the counter for + */ +static void +batadv_forw_packet_bcasts_inc(struct batadv_forw_packet *forw_packet) +{ + BATADV_SKB_CB(forw_packet->skb)->num_bcasts++; +} + +/** + * batadv_forw_packet_is_rebroadcast - check packet for previous transmissions + * @forw_packet: the packet to check + * + * Return: True if this packet was transmitted before, false otherwise. + */ +bool batadv_forw_packet_is_rebroadcast(struct batadv_forw_packet *forw_packet) +{ + return BATADV_SKB_CB(forw_packet->skb)->num_bcasts > 0; +} + static void batadv_send_outstanding_bcast_packet(struct work_struct *work) { struct batadv_hard_iface *hard_iface; @@ -829,7 +878,7 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) if (hard_iface->soft_iface != soft_iface) continue; - if (forw_packet->num_packets >= hard_iface->num_bcasts) + if (!batadv_forw_packet_bcasts_left(forw_packet, hard_iface)) continue; if (forw_packet->own) { @@ -887,10 +936,10 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) } rcu_read_unlock(); - forw_packet->num_packets++; + batadv_forw_packet_bcasts_inc(forw_packet); /* if we still have some more bcasts to send */ - if (forw_packet->num_packets < BATADV_NUM_BCASTS_MAX) { + if (batadv_forw_packet_bcasts_left(forw_packet, NULL)) { batadv_forw_packet_bcast_queue(bat_priv, forw_packet, send_time); return; diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index 8e75890406d2..a16b34f473ef 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -40,6 +40,7 @@ bool batadv_forw_packet_steal(struct batadv_forw_packet *packet, spinlock_t *l); void batadv_forw_packet_ogmv1_queue(struct batadv_priv *bat_priv, struct batadv_forw_packet *forw_packet, unsigned long send_time); +bool batadv_forw_packet_is_rebroadcast(struct batadv_forw_packet *forw_packet); int batadv_send_skb_to_orig(struct sk_buff *skb, struct batadv_orig_node *orig_node, diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 8226495c6664..a9dbcc1590bd 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -230,6 +230,9 @@ static int batadv_interface_tx(struct sk_buff *skb, if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dropped; + /* reset control block to avoid left overs from previous users */ + memset(skb->cb, 0, sizeof(struct batadv_skb_cb)); + netif_trans_update(soft_iface); vid = batadv_get_vid(skb, 0); ethhdr = eth_hdr(skb); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 66b25e410a41..74b5af7dcd86 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1377,9 +1377,11 @@ struct batadv_nc_packet { * relevant to batman-adv in the skb->cb buffer in skbs. * @decoded: Marks a skb as decoded, which is checked when searching for coding * opportunities in network-coding.c + * @num_bcasts: Counter for broadcast packet retransmissions */ struct batadv_skb_cb { bool decoded; + unsigned int num_bcasts; }; /** @@ -1392,7 +1394,7 @@ struct batadv_skb_cb { * @skb: bcast packet's skb buffer * @packet_len: size of aggregated OGM packet inside the skb buffer * @direct_link_flags: direct link flags for aggregated OGM packets - * @num_packets: counter for bcast packet retransmission + * @num_packets: counter for aggregated OGMv1 packets * @delayed_work: work queue callback item for packet sending * @if_incoming: pointer to incoming hard-iface or primary iface if * locally generated packet -- cgit From 6c786bcb29dd684533ec165057f437d5bb34a4b2 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 25 Mar 2017 19:41:17 +0300 Subject: xfrm: branchless addr4_match() on 64-bit Current addr4_match() code has special test for /0 prefixes because of standard required undefined behaviour. However, it is possible to omit it on 64-bit because shifting can be done within a 64-bit register and then truncated to the expected value (which is 0 mask). Implicit truncation by htonl() fits nicely into R32-within-R64 model on x86-64. Space savings: none (coincidence) Branch savings: 1 Before: movzx eax,BYTE PTR [rdi+0x2a] # ->prefixlen_d test al,al jne xfrm_selector_match + 0x23f ... movzx eax,BYTE PTR [rbx+0x2b] # ->prefixlen_s test al,al je xfrm_selector_match + 0x1c7 After (no branches): mov r8d,0x20 mov rdx,0xffffffffffffffff mov esi,DWORD PTR [rsi+0x2c] mov ecx,r8d sub cl,BYTE PTR [rdi+0x2a] xor esi,DWORD PTR [rbx] mov rdi,rdx xor eax,eax shl rdi,cl bswap edi Signed-off-by: Alexey Dobriyan Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 43b93d1134ed..9e3dc7b81a4d 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -845,9 +845,9 @@ static inline bool addr_match(const void *token1, const void *token2, static inline bool addr4_match(__be32 a1, __be32 a2, u8 prefixlen) { /* C99 6.5.7 (3): u32 << 32 is undefined behaviour */ - if (prefixlen == 0) + if (sizeof(long) == 4 && prefixlen == 0) return true; - return !((a1 ^ a2) & htonl(0xFFFFFFFFu << (32 - prefixlen))); + return !((a1 ^ a2) & htonl(~0UL << (32 - prefixlen))); } static __inline__ -- cgit From 7f859ecfa8d6e647d6c794c8af44d209c8372e77 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Mon, 14 Nov 2016 13:42:02 +0200 Subject: net/mlx5e: Set SQ max rate on mlx5e_open_txqsq rather on open_channel Instead of iterating over the channel SQs to set their max rate, do it on SQ creation per TXQ SQ. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index e849a0fc2653..469d6c147db7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1207,6 +1207,9 @@ static int mlx5e_create_sq_rdy(struct mlx5e_priv *priv, return err; } +static int mlx5e_set_sq_maxrate(struct net_device *dev, + struct mlx5e_txqsq *sq, u32 rate); + static int mlx5e_open_txqsq(struct mlx5e_channel *c, int tc, struct mlx5e_sq_param *param, @@ -1214,6 +1217,8 @@ static int mlx5e_open_txqsq(struct mlx5e_channel *c, { struct mlx5e_create_sq_param csp = {}; struct mlx5e_priv *priv = c->priv; + u32 tx_rate; + int txq_ix; int err; err = mlx5e_alloc_txqsq(c, tc, param, sq); @@ -1230,6 +1235,11 @@ static int mlx5e_open_txqsq(struct mlx5e_channel *c, if (err) goto err_free_txqsq; + txq_ix = c->ix + tc * priv->params.num_channels; + tx_rate = priv->tx_rates[txq_ix]; + if (tx_rate) + mlx5e_set_sq_maxrate(priv->netdev, sq, tx_rate); + netdev_tx_reset_queue(sq->txq); netif_tx_start_queue(sq->txq); return 0; @@ -1692,7 +1702,6 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, int cpu = mlx5e_get_cpu(priv, ix); struct mlx5e_channel *c; int err; - int i; c = kzalloc_node(sizeof(*c), GFP_KERNEL, cpu_to_node(cpu)); if (!c) @@ -1745,17 +1754,6 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, if (err) goto err_close_icosq; - for (i = 0; i < priv->params.num_tc; i++) { - u32 txq_ix = priv->channeltc_to_txq_map[ix][i]; - - if (priv->tx_rates[txq_ix]) { - struct mlx5e_txqsq *sq = priv->txq_to_sq_map[txq_ix]; - - mlx5e_set_sq_maxrate(priv->netdev, sq, - priv->tx_rates[txq_ix]); - } - } - err = c->xdp ? mlx5e_open_xdpsq(c, &cparam->xdp_sq, &c->rq.xdpsq) : 0; if (err) goto err_close_sqs; -- cgit From be4891af77226d5909be11ab4297434cfd8c391d Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 7 Feb 2017 16:30:52 +0200 Subject: net/mlx5e: Set netdev->rx_cpu_rmap on netdev creation To simplify mlx5e_open_locked flow we set netdev->rx_cpu_rmap on netdev creation rather on netdev open, it is redundant to set it every time on mlx5e_open_locked. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 469d6c147db7..f0eff5e30729 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2478,9 +2478,7 @@ int mlx5e_open_locked(struct net_device *netdev) mlx5e_redirect_rqts(priv); mlx5e_update_carrier(priv); mlx5e_timestamp_init(priv); -#ifdef CONFIG_RFS_ACCEL - priv->netdev->rx_cpu_rmap = priv->mdev->rmap; -#endif + if (priv->profile->update_stats) queue_delayed_work(priv->wq, &priv->update_stats_work, 0); @@ -4022,6 +4020,10 @@ struct net_device *mlx5e_create_netdev(struct mlx5_core_dev *mdev, return NULL; } +#ifdef CONFIG_RFS_ACCEL + netdev->rx_cpu_rmap = mdev->rmap; +#endif + profile->init(mdev, netdev, profile, ppriv); netif_carrier_off(netdev); -- cgit From ff9c852f91d14d11a35514dda495999cfdb41a7a Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Mon, 6 Feb 2017 13:14:34 +0200 Subject: net/mlx5e: Introduce mlx5e_channels Have a dedicated "channels" handler that will serve as channels (RQs/SQs/etc..) holder to help with separating channels/parameters operations, for the downstream fail-safe configuration flow, where we will create a new instance of mlx5e_channels with the new requested parameters and switch to the new channels on the fly. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 9 ++- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 27 +++---- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 86 +++++++++++----------- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 14 ++-- 4 files changed, 71 insertions(+), 65 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index bace9233dc1f..b00c6688ddcf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -560,6 +560,11 @@ struct mlx5e_channel { int cpu; }; +struct mlx5e_channels { + struct mlx5e_channel **c; + unsigned int num; +}; + enum mlx5e_traffic_types { MLX5E_TT_IPV4_TCP, MLX5E_TT_IPV6_TCP, @@ -736,7 +741,7 @@ struct mlx5e_priv { struct mutex state_lock; /* Protects Interface state */ struct mlx5e_rq drop_rq; - struct mlx5e_channel **channel; + struct mlx5e_channels channels; u32 tisn[MLX5E_MAX_NUM_TC]; struct mlx5e_rqt indir_rqt; struct mlx5e_tir indir_tir[MLX5E_NUM_INDIR_TIRS]; @@ -836,7 +841,7 @@ int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __always_unused __be16 proto, void mlx5e_enable_vlan_filter(struct mlx5e_priv *priv); void mlx5e_disable_vlan_filter(struct mlx5e_priv *priv); -int mlx5e_modify_rqs_vsd(struct mlx5e_priv *priv, bool vsd); +int mlx5e_modify_channels_vsd(struct mlx5e_channels *chs, bool vsd); int mlx5e_redirect_rqt(struct mlx5e_priv *priv, u32 rqtn, int sz, int ix); void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_priv *priv, void *tirc, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index a004a5a1a4c2..2e54a6564d86 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -152,12 +152,9 @@ static bool mlx5e_query_global_pause_combined(struct mlx5e_priv *priv) } #define MLX5E_NUM_Q_CNTRS(priv) (NUM_Q_COUNTERS * (!!priv->q_counter)) -#define MLX5E_NUM_RQ_STATS(priv) \ - (NUM_RQ_STATS * priv->params.num_channels * \ - test_bit(MLX5E_STATE_OPENED, &priv->state)) +#define MLX5E_NUM_RQ_STATS(priv) (NUM_RQ_STATS * (priv)->channels.num) #define MLX5E_NUM_SQ_STATS(priv) \ - (NUM_SQ_STATS * priv->params.num_channels * priv->params.num_tc * \ - test_bit(MLX5E_STATE_OPENED, &priv->state)) + (NUM_SQ_STATS * (priv)->channels.num * (priv)->params.num_tc) #define MLX5E_NUM_PFC_COUNTERS(priv) \ ((mlx5e_query_global_pause_combined(priv) + hweight8(mlx5e_query_pfc_combined(priv))) * \ NUM_PPORT_PER_PRIO_PFC_COUNTERS) @@ -262,13 +259,13 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data) return; /* per channel counters */ - for (i = 0; i < priv->params.num_channels; i++) + for (i = 0; i < priv->channels.num; i++) for (j = 0; j < NUM_RQ_STATS; j++) sprintf(data + (idx++) * ETH_GSTRING_LEN, rq_stats_desc[j].format, i); for (tc = 0; tc < priv->params.num_tc; tc++) - for (i = 0; i < priv->params.num_channels; i++) + for (i = 0; i < priv->channels.num; i++) for (j = 0; j < NUM_SQ_STATS; j++) sprintf(data + (idx++) * ETH_GSTRING_LEN, sq_stats_desc[j].format, @@ -303,6 +300,7 @@ static void mlx5e_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5e_channels *channels; struct mlx5_priv *mlx5_priv; int i, j, tc, prio, idx = 0; unsigned long pfc_combined; @@ -313,6 +311,7 @@ static void mlx5e_get_ethtool_stats(struct net_device *dev, mutex_lock(&priv->state_lock); if (test_bit(MLX5E_STATE_OPENED, &priv->state)) mlx5e_update_stats(priv); + channels = &priv->channels; mutex_unlock(&priv->state_lock); for (i = 0; i < NUM_SW_COUNTERS; i++) @@ -382,16 +381,16 @@ static void mlx5e_get_ethtool_stats(struct net_device *dev, return; /* per channel counters */ - for (i = 0; i < priv->params.num_channels; i++) + for (i = 0; i < channels->num; i++) for (j = 0; j < NUM_RQ_STATS; j++) data[idx++] = - MLX5E_READ_CTR64_CPU(&priv->channel[i]->rq.stats, + MLX5E_READ_CTR64_CPU(&channels->c[i]->rq.stats, rq_stats_desc, j); for (tc = 0; tc < priv->params.num_tc; tc++) - for (i = 0; i < priv->params.num_channels; i++) + for (i = 0; i < channels->num; i++) for (j = 0; j < NUM_SQ_STATS; j++) - data[idx++] = MLX5E_READ_CTR64_CPU(&priv->channel[i]->sq[tc].stats, + data[idx++] = MLX5E_READ_CTR64_CPU(&channels->c[i]->sq[tc].stats, sq_stats_desc, j); } @@ -628,7 +627,6 @@ static int mlx5e_set_coalesce(struct net_device *netdev, { struct mlx5e_priv *priv = netdev_priv(netdev); struct mlx5_core_dev *mdev = priv->mdev; - struct mlx5e_channel *c; bool restart = !!coal->use_adaptive_rx_coalesce != priv->params.rx_am_enabled; bool was_opened; @@ -654,9 +652,8 @@ static int mlx5e_set_coalesce(struct net_device *netdev, if (!was_opened || restart) goto out; - - for (i = 0; i < priv->params.num_channels; ++i) { - c = priv->channel[i]; + for (i = 0; i < priv->channels.num; ++i) { + struct mlx5e_channel *c = priv->channels.c[i]; for (tc = 0; tc < c->num_tc; tc++) { mlx5_core_modify_cq_moderation(mdev, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index f0eff5e30729..920e72ae992e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -180,8 +180,10 @@ static void mlx5e_update_sw_counters(struct mlx5e_priv *priv) int i, j; memset(s, 0, sizeof(*s)); - for (i = 0; i < priv->params.num_channels; i++) { - rq_stats = &priv->channel[i]->rq.stats; + for (i = 0; i < priv->channels.num; i++) { + struct mlx5e_channel *c = priv->channels.c[i]; + + rq_stats = &c->rq.stats; s->rx_packets += rq_stats->packets; s->rx_bytes += rq_stats->bytes; @@ -204,7 +206,7 @@ static void mlx5e_update_sw_counters(struct mlx5e_priv *priv) s->rx_cache_busy += rq_stats->cache_busy; for (j = 0; j < priv->params.num_tc; j++) { - sq_stats = &priv->channel[i]->sq[j].stats; + sq_stats = &c->sq[j].stats; s->tx_packets += sq_stats->packets; s->tx_bytes += sq_stats->bytes; @@ -1067,7 +1069,7 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, if (err) goto err_sq_wq_destroy; - txq_ix = c->ix + tc * priv->params.num_channels; + txq_ix = c->ix + tc * priv->channels.num; sq->txq = netdev_get_tx_queue(priv->netdev, txq_ix); priv->txq_to_sq_map[txq_ix] = sq; @@ -1235,7 +1237,7 @@ static int mlx5e_open_txqsq(struct mlx5e_channel *c, if (err) goto err_free_txqsq; - txq_ix = c->ix + tc * priv->params.num_channels; + txq_ix = c->ix + tc * priv->channels.num; tx_rate = priv->tx_rates[txq_ix]; if (tx_rate) mlx5e_set_sq_maxrate(priv->netdev, sq, tx_rate); @@ -1603,8 +1605,7 @@ static void mlx5e_build_channeltc_to_txq_map(struct mlx5e_priv *priv, int ix) int i; for (i = 0; i < priv->profile->max_tc; i++) - priv->channeltc_to_txq_map[ix][i] = - ix + i * priv->params.num_channels; + priv->channeltc_to_txq_map[ix][i] = ix + i * priv->channels.num; } static int mlx5e_set_sq_maxrate(struct net_device *dev, @@ -1977,35 +1978,31 @@ static void mlx5e_build_channel_param(struct mlx5e_priv *priv, struct mlx5e_chan mlx5e_build_ico_cq_param(priv, &cparam->icosq_cq, icosq_log_wq_sz); } -static int mlx5e_open_channels(struct mlx5e_priv *priv) +static int mlx5e_open_channels(struct mlx5e_priv *priv, struct mlx5e_channels *chs) { struct mlx5e_channel_param *cparam; - int nch = priv->params.num_channels; int err = -ENOMEM; int i; int j; - priv->channel = kcalloc(nch, sizeof(struct mlx5e_channel *), - GFP_KERNEL); + chs->num = priv->params.num_channels; - priv->txq_to_sq_map = kcalloc(nch * priv->params.num_tc, + chs->c = kcalloc(chs->num, sizeof(struct mlx5e_channel *), GFP_KERNEL); + priv->txq_to_sq_map = kcalloc(chs->num * priv->params.num_tc, sizeof(struct mlx5e_sq *), GFP_KERNEL); - cparam = kzalloc(sizeof(struct mlx5e_channel_param), GFP_KERNEL); - - if (!priv->channel || !priv->txq_to_sq_map || !cparam) + if (!chs->c || !priv->txq_to_sq_map || !cparam) goto err_free_txq_to_sq_map; mlx5e_build_channel_param(priv, cparam); - - for (i = 0; i < nch; i++) { - err = mlx5e_open_channel(priv, i, cparam, &priv->channel[i]); + for (i = 0; i < chs->num; i++) { + err = mlx5e_open_channel(priv, i, cparam, &chs->c[i]); if (err) goto err_close_channels; } - for (j = 0; j < nch; j++) { - err = mlx5e_wait_for_min_rx_wqes(&priv->channel[j]->rq); + for (j = 0; j < chs->num; j++) { + err = mlx5e_wait_for_min_rx_wqes(&chs->c[j]->rq); if (err) goto err_close_channels; } @@ -2020,18 +2017,19 @@ static int mlx5e_open_channels(struct mlx5e_priv *priv) err_close_channels: for (i--; i >= 0; i--) - mlx5e_close_channel(priv->channel[i]); + mlx5e_close_channel(chs->c[i]); err_free_txq_to_sq_map: kfree(priv->txq_to_sq_map); - kfree(priv->channel); + kfree(chs->c); kfree(cparam); - + chs->num = 0; return err; } static void mlx5e_close_channels(struct mlx5e_priv *priv) { + struct mlx5e_channels *chs = &priv->channels; int i; /* FIXME: This is a W/A only for tx timeout watch dog false alarm when @@ -2040,11 +2038,12 @@ static void mlx5e_close_channels(struct mlx5e_priv *priv) netif_tx_stop_all_queues(priv->netdev); netif_tx_disable(priv->netdev); - for (i = 0; i < priv->params.num_channels; i++) - mlx5e_close_channel(priv->channel[i]); + for (i = 0; i < chs->num; i++) + mlx5e_close_channel(chs->c[i]); kfree(priv->txq_to_sq_map); - kfree(priv->channel); + kfree(chs->c); + chs->num = 0; } static int mlx5e_rx_hash_fn(int hfunc) @@ -2078,7 +2077,7 @@ static void mlx5e_fill_indir_rqt_rqns(struct mlx5e_priv *priv, void *rqtc) ix = priv->params.indirection_rqt[ix]; rqn = test_bit(MLX5E_STATE_OPENED, &priv->state) ? - priv->channel[ix]->rq.rqn : + priv->channels.c[ix]->rq.rqn : priv->drop_rq.rqn; MLX5_SET(rqtc, rqtc, rq_num[i], rqn); } @@ -2088,7 +2087,7 @@ static void mlx5e_fill_direct_rqt_rqn(struct mlx5e_priv *priv, void *rqtc, int ix) { u32 rqn = test_bit(MLX5E_STATE_OPENED, &priv->state) ? - priv->channel[ix]->rq.rqn : + priv->channels.c[ix]->rq.rqn : priv->drop_rq.rqn; MLX5_SET(rqtc, rqtc, rq_num[0], rqn); @@ -2461,7 +2460,7 @@ int mlx5e_open_locked(struct net_device *netdev) netif_set_real_num_tx_queues(netdev, num_txqs); netif_set_real_num_rx_queues(netdev, priv->params.num_channels); - err = mlx5e_open_channels(priv); + err = mlx5e_open_channels(priv, &priv->channels); if (err) { netdev_err(netdev, "%s: mlx5e_open_channels failed, %d\n", __func__, err); @@ -2815,16 +2814,13 @@ void mlx5e_destroy_direct_tirs(struct mlx5e_priv *priv) mlx5e_destroy_tir(priv->mdev, &priv->direct_tir[i]); } -int mlx5e_modify_rqs_vsd(struct mlx5e_priv *priv, bool vsd) +int mlx5e_modify_channels_vsd(struct mlx5e_channels *chs, bool vsd) { int err = 0; int i; - if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) - return 0; - - for (i = 0; i < priv->params.num_channels; i++) { - err = mlx5e_modify_rq_vsd(&priv->channel[i]->rq, vsd); + for (i = 0; i < chs->num; i++) { + err = mlx5e_modify_rq_vsd(&chs->c[i]->rq, vsd); if (err) return err; } @@ -3029,15 +3025,19 @@ static int set_feature_rx_all(struct net_device *netdev, bool enable) static int set_feature_rx_vlan(struct net_device *netdev, bool enable) { struct mlx5e_priv *priv = netdev_priv(netdev); - int err; + int err = 0; mutex_lock(&priv->state_lock); priv->params.vlan_strip_disable = !enable; - err = mlx5e_modify_rqs_vsd(priv, !enable); + if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) + goto unlock; + + err = mlx5e_modify_channels_vsd(&priv->channels, !enable); if (err) priv->params.vlan_strip_disable = enable; +unlock: mutex_unlock(&priv->state_lock); return err; @@ -3334,7 +3334,7 @@ static void mlx5e_tx_timeout(struct net_device *dev) netdev_err(dev, "TX timeout detected\n"); - for (i = 0; i < priv->params.num_channels * priv->params.num_tc; i++) { + for (i = 0; i < priv->channels.num * priv->params.num_tc; i++) { struct mlx5e_txqsq *sq = priv->txq_to_sq_map[i]; if (!netif_xmit_stopped(netdev_get_tx_queue(dev, i))) @@ -3401,8 +3401,8 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog) /* exchanging programs w/o reset, we update ref counts on behalf * of the channels RQs here. */ - for (i = 0; i < priv->params.num_channels; i++) { - struct mlx5e_channel *c = priv->channel[i]; + for (i = 0; i < priv->channels.num; i++) { + struct mlx5e_channel *c = priv->channels.c[i]; clear_bit(MLX5E_RQ_STATE_ENABLED, &c->rq.state); napi_synchronize(&c->napi); @@ -3451,10 +3451,12 @@ static int mlx5e_xdp(struct net_device *dev, struct netdev_xdp *xdp) static void mlx5e_netpoll(struct net_device *dev) { struct mlx5e_priv *priv = netdev_priv(dev); + struct mlx5e_channels *chs = &priv->channels; + int i; - for (i = 0; i < priv->params.num_channels; i++) - napi_schedule(&priv->channel[i]->napi); + for (i = 0; i < chs->num; i++) + napi_schedule(&chs->c[i]->napi); } #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index f621373bd7a5..0515b7f7d11f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -102,14 +102,16 @@ static void mlx5e_rep_update_sw_counters(struct mlx5e_priv *priv) int i, j; memset(s, 0, sizeof(*s)); - for (i = 0; i < priv->params.num_channels; i++) { - rq_stats = &priv->channel[i]->rq.stats; + for (i = 0; i < priv->channels.num; i++) { + struct mlx5e_channel *c = priv->channels.c[i]; + + rq_stats = &c->rq.stats; s->rx_packets += rq_stats->packets; s->rx_bytes += rq_stats->bytes; for (j = 0; j < priv->params.num_tc; j++) { - sq_stats = &priv->channel[i]->sq[j].stats; + sq_stats = &c->sq[j].stats; s->tx_packets += sq_stats->packets; s->tx_bytes += sq_stats->bytes; @@ -190,12 +192,12 @@ int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) int n, tc, err, num_sqs = 0; u16 *sqs; - sqs = kcalloc(priv->params.num_channels * priv->params.num_tc, sizeof(u16), GFP_KERNEL); + sqs = kcalloc(priv->channels.num * priv->params.num_tc, sizeof(u16), GFP_KERNEL); if (!sqs) return -ENOMEM; - for (n = 0; n < priv->params.num_channels; n++) { - c = priv->channel[n]; + for (n = 0; n < priv->channels.num; n++) { + c = priv->channels.c[n]; for (tc = 0; tc < c->num_tc; tc++) sqs[num_sqs++] = c->sq[tc].sqn; } -- cgit From a5f97fee743cd7ee9932036583dbe05298ff2648 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Mon, 19 Dec 2016 23:20:17 +0200 Subject: net/mlx5e: Redirect RQT refactoring RQ Tables are always created once (on netdev creation) pointing to drop RQ and at that stage, RQ tables (indirection tables) are always directed to drop RQ. We don't need to use mlx5e_fill_{direct,indir}_rqt_rqns to fill the drop RQ in create RQT procedure. Instead of having separate flows to redirect direct and indirect RQ Tables to the current active channels Receive Queues (RQs), we unify the two flows by introducing mlx5e_redirect_rqt function and redirect_rqt_param struct. Combined, they provide one generic logic to fill the RQ table RQ numbers regardless of the RQ table purpose (direct/indirect). Demonstrated the usage with mlx5e_redirect_rqts_to_channels which will be called on mlx5e_open and with mlx5e_redirect_rqts_to_drop which will be called on mlx5e_close. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 14 +- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 24 ++- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 169 ++++++++++++--------- 3 files changed, 129 insertions(+), 78 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index b00c6688ddcf..50dfc4c6c8e4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -843,7 +843,19 @@ void mlx5e_disable_vlan_filter(struct mlx5e_priv *priv); int mlx5e_modify_channels_vsd(struct mlx5e_channels *chs, bool vsd); -int mlx5e_redirect_rqt(struct mlx5e_priv *priv, u32 rqtn, int sz, int ix); +struct mlx5e_redirect_rqt_param { + bool is_rss; + union { + u32 rqn; /* Direct RQN (Non-RSS) */ + struct { + u8 hfunc; + struct mlx5e_channels *channels; + } rss; /* RSS data */ + }; +}; + +int mlx5e_redirect_rqt(struct mlx5e_priv *priv, u32 rqtn, int sz, + struct mlx5e_redirect_rqt_param rrp); void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_priv *priv, void *tirc, enum mlx5e_traffic_types tt); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 2e54a6564d86..faa21848c9dc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -1027,20 +1027,28 @@ static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir, mutex_lock(&priv->state_lock); - if (indir) { - u32 rqtn = priv->indir_rqt.rqtn; - - memcpy(priv->params.indirection_rqt, indir, - sizeof(priv->params.indirection_rqt)); - mlx5e_redirect_rqt(priv, rqtn, MLX5E_INDIR_RQT_SIZE, 0); - } - if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != priv->params.rss_hfunc) { priv->params.rss_hfunc = hfunc; hash_changed = true; } + if (indir) { + memcpy(priv->params.indirection_rqt, indir, + sizeof(priv->params.indirection_rqt)); + + if (test_bit(MLX5E_STATE_OPENED, &priv->state)) { + u32 rqtn = priv->indir_rqt.rqtn; + struct mlx5e_redirect_rqt_param rrp = { + .is_rss = true, + .rss.hfunc = priv->params.rss_hfunc, + .rss.channels = &priv->channels + }; + + mlx5e_redirect_rqt(priv, rqtn, MLX5E_INDIR_RQT_SIZE, rrp); + } + } + if (key) { memcpy(priv->params.toeplitz_hash_key, key, sizeof(priv->params.toeplitz_hash_key)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 920e72ae992e..aec77f075714 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2046,61 +2046,15 @@ static void mlx5e_close_channels(struct mlx5e_priv *priv) chs->num = 0; } -static int mlx5e_rx_hash_fn(int hfunc) -{ - return (hfunc == ETH_RSS_HASH_TOP) ? - MLX5_RX_HASH_FN_TOEPLITZ : - MLX5_RX_HASH_FN_INVERTED_XOR8; -} - -static int mlx5e_bits_invert(unsigned long a, int size) -{ - int inv = 0; - int i; - - for (i = 0; i < size; i++) - inv |= (test_bit(size - i - 1, &a) ? 1 : 0) << i; - - return inv; -} - -static void mlx5e_fill_indir_rqt_rqns(struct mlx5e_priv *priv, void *rqtc) -{ - int i; - - for (i = 0; i < MLX5E_INDIR_RQT_SIZE; i++) { - int ix = i; - u32 rqn; - - if (priv->params.rss_hfunc == ETH_RSS_HASH_XOR) - ix = mlx5e_bits_invert(i, MLX5E_LOG_INDIR_RQT_SIZE); - - ix = priv->params.indirection_rqt[ix]; - rqn = test_bit(MLX5E_STATE_OPENED, &priv->state) ? - priv->channels.c[ix]->rq.rqn : - priv->drop_rq.rqn; - MLX5_SET(rqtc, rqtc, rq_num[i], rqn); - } -} - -static void mlx5e_fill_direct_rqt_rqn(struct mlx5e_priv *priv, void *rqtc, - int ix) -{ - u32 rqn = test_bit(MLX5E_STATE_OPENED, &priv->state) ? - priv->channels.c[ix]->rq.rqn : - priv->drop_rq.rqn; - - MLX5_SET(rqtc, rqtc, rq_num[0], rqn); -} - -static int mlx5e_create_rqt(struct mlx5e_priv *priv, int sz, - int ix, struct mlx5e_rqt *rqt) +static int +mlx5e_create_rqt(struct mlx5e_priv *priv, int sz, struct mlx5e_rqt *rqt) { struct mlx5_core_dev *mdev = priv->mdev; void *rqtc; int inlen; int err; u32 *in; + int i; inlen = MLX5_ST_SZ_BYTES(create_rqt_in) + sizeof(u32) * sz; in = mlx5_vzalloc(inlen); @@ -2112,10 +2066,8 @@ static int mlx5e_create_rqt(struct mlx5e_priv *priv, int sz, MLX5_SET(rqtc, rqtc, rqt_actual_size, sz); MLX5_SET(rqtc, rqtc, rqt_max_size, sz); - if (sz > 1) /* RSS */ - mlx5e_fill_indir_rqt_rqns(priv, rqtc); - else - mlx5e_fill_direct_rqt_rqn(priv, rqtc, ix); + for (i = 0; i < sz; i++) + MLX5_SET(rqtc, rqtc, rq_num[i], priv->drop_rq.rqn); err = mlx5_core_create_rqt(mdev, in, inlen, &rqt->rqtn); if (!err) @@ -2135,7 +2087,7 @@ static int mlx5e_create_indirect_rqts(struct mlx5e_priv *priv) { struct mlx5e_rqt *rqt = &priv->indir_rqt; - return mlx5e_create_rqt(priv, MLX5E_INDIR_RQT_SIZE, 0, rqt); + return mlx5e_create_rqt(priv, MLX5E_INDIR_RQT_SIZE, rqt); } int mlx5e_create_direct_rqts(struct mlx5e_priv *priv) @@ -2146,7 +2098,7 @@ int mlx5e_create_direct_rqts(struct mlx5e_priv *priv) for (ix = 0; ix < priv->profile->max_nch(priv->mdev); ix++) { rqt = &priv->direct_tir[ix].rqt; - err = mlx5e_create_rqt(priv, 1 /*size */, ix, rqt); + err = mlx5e_create_rqt(priv, 1 /*size */, rqt); if (err) goto err_destroy_rqts; } @@ -2160,7 +2112,49 @@ err_destroy_rqts: return err; } -int mlx5e_redirect_rqt(struct mlx5e_priv *priv, u32 rqtn, int sz, int ix) +static int mlx5e_rx_hash_fn(int hfunc) +{ + return (hfunc == ETH_RSS_HASH_TOP) ? + MLX5_RX_HASH_FN_TOEPLITZ : + MLX5_RX_HASH_FN_INVERTED_XOR8; +} + +static int mlx5e_bits_invert(unsigned long a, int size) +{ + int inv = 0; + int i; + + for (i = 0; i < size; i++) + inv |= (test_bit(size - i - 1, &a) ? 1 : 0) << i; + + return inv; +} + +static void mlx5e_fill_rqt_rqns(struct mlx5e_priv *priv, int sz, + struct mlx5e_redirect_rqt_param rrp, void *rqtc) +{ + int i; + + for (i = 0; i < sz; i++) { + u32 rqn; + + if (rrp.is_rss) { + int ix = i; + + if (rrp.rss.hfunc == ETH_RSS_HASH_XOR) + ix = mlx5e_bits_invert(i, ilog2(sz)); + + ix = priv->params.indirection_rqt[ix]; + rqn = rrp.rss.channels->c[ix]->rq.rqn; + } else { + rqn = rrp.rqn; + } + MLX5_SET(rqtc, rqtc, rq_num[i], rqn); + } +} + +int mlx5e_redirect_rqt(struct mlx5e_priv *priv, u32 rqtn, int sz, + struct mlx5e_redirect_rqt_param rrp) { struct mlx5_core_dev *mdev = priv->mdev; void *rqtc; @@ -2176,38 +2170,75 @@ int mlx5e_redirect_rqt(struct mlx5e_priv *priv, u32 rqtn, int sz, int ix) rqtc = MLX5_ADDR_OF(modify_rqt_in, in, ctx); MLX5_SET(rqtc, rqtc, rqt_actual_size, sz); - if (sz > 1) /* RSS */ - mlx5e_fill_indir_rqt_rqns(priv, rqtc); - else - mlx5e_fill_direct_rqt_rqn(priv, rqtc, ix); - MLX5_SET(modify_rqt_in, in, bitmask.rqn_list, 1); - + mlx5e_fill_rqt_rqns(priv, sz, rrp, rqtc); err = mlx5_core_modify_rqt(mdev, rqtn, in, inlen); kvfree(in); - return err; } -static void mlx5e_redirect_rqts(struct mlx5e_priv *priv) +static u32 mlx5e_get_direct_rqn(struct mlx5e_priv *priv, int ix, + struct mlx5e_redirect_rqt_param rrp) +{ + if (!rrp.is_rss) + return rrp.rqn; + + if (ix >= rrp.rss.channels->num) + return priv->drop_rq.rqn; + + return rrp.rss.channels->c[ix]->rq.rqn; +} + +static void mlx5e_redirect_rqts(struct mlx5e_priv *priv, + struct mlx5e_redirect_rqt_param rrp) { u32 rqtn; int ix; if (priv->indir_rqt.enabled) { + /* RSS RQ table */ rqtn = priv->indir_rqt.rqtn; - mlx5e_redirect_rqt(priv, rqtn, MLX5E_INDIR_RQT_SIZE, 0); + mlx5e_redirect_rqt(priv, rqtn, MLX5E_INDIR_RQT_SIZE, rrp); } - for (ix = 0; ix < priv->params.num_channels; ix++) { + for (ix = 0; ix < priv->profile->max_nch(priv->mdev); ix++) { + struct mlx5e_redirect_rqt_param direct_rrp = { + .is_rss = false, + .rqn = mlx5e_get_direct_rqn(priv, ix, rrp) + }; + + /* Direct RQ Tables */ if (!priv->direct_tir[ix].rqt.enabled) continue; + rqtn = priv->direct_tir[ix].rqt.rqtn; - mlx5e_redirect_rqt(priv, rqtn, 1, ix); + mlx5e_redirect_rqt(priv, rqtn, 1, direct_rrp); } } +static void mlx5e_redirect_rqts_to_channels(struct mlx5e_priv *priv, + struct mlx5e_channels *chs) +{ + struct mlx5e_redirect_rqt_param rrp = { + .is_rss = true, + .rss.channels = chs, + .rss.hfunc = priv->params.rss_hfunc + }; + + mlx5e_redirect_rqts(priv, rrp); +} + +static void mlx5e_redirect_rqts_to_drop(struct mlx5e_priv *priv) +{ + struct mlx5e_redirect_rqt_param drop_rrp = { + .is_rss = false, + .rqn = priv->drop_rq.rqn + }; + + mlx5e_redirect_rqts(priv, drop_rrp); +} + static void mlx5e_build_tir_ctx_lro(void *tirc, struct mlx5e_priv *priv) { if (!priv->params.lro_en) @@ -2474,7 +2505,7 @@ int mlx5e_open_locked(struct net_device *netdev) goto err_close_channels; } - mlx5e_redirect_rqts(priv); + mlx5e_redirect_rqts_to_channels(priv, &priv->channels); mlx5e_update_carrier(priv); mlx5e_timestamp_init(priv); @@ -2525,7 +2556,7 @@ int mlx5e_close_locked(struct net_device *netdev) mlx5e_timestamp_cleanup(priv); netif_carrier_off(priv->netdev); - mlx5e_redirect_rqts(priv); + mlx5e_redirect_rqts_to_drop(priv); mlx5e_close_channels(priv); return 0; -- cgit From b676f653896aaf6b32b496709bd9ebbc771d7ed7 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 20 Dec 2016 17:30:20 +0200 Subject: net/mlx5e: Refactor refresh TIRs Rename mlx5e_refresh_tirs_self_loopback to mlx5e_refresh_tirs, as it will be used in downstream (Safe config flow) patches, and make it fail safe on mlx5e_open. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 3 +-- drivers/net/ethernet/mellanox/mlx5/core/en_common.c | 17 +++++++++++------ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 8 +------- drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c | 9 +++------ 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 50dfc4c6c8e4..5f7cc58d900c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -959,8 +959,7 @@ void mlx5e_destroy_tir(struct mlx5_core_dev *mdev, struct mlx5e_tir *tir); int mlx5e_create_mdev_resources(struct mlx5_core_dev *mdev); void mlx5e_destroy_mdev_resources(struct mlx5_core_dev *mdev); -int mlx5e_refresh_tirs_self_loopback(struct mlx5_core_dev *mdev, - bool enable_uc_lb); +int mlx5e_refresh_tirs(struct mlx5e_priv *priv, bool enable_uc_lb); struct mlx5_eswitch_rep; int mlx5e_vport_rep_load(struct mlx5_eswitch *esw, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c index 20bdbe685795..f1f17f7a3cd0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c @@ -136,18 +136,20 @@ void mlx5e_destroy_mdev_resources(struct mlx5_core_dev *mdev) mlx5_core_dealloc_pd(mdev, res->pdn); } -int mlx5e_refresh_tirs_self_loopback(struct mlx5_core_dev *mdev, - bool enable_uc_lb) +int mlx5e_refresh_tirs(struct mlx5e_priv *priv, bool enable_uc_lb) { + struct mlx5_core_dev *mdev = priv->mdev; struct mlx5e_tir *tir; - void *in; + int err = -ENOMEM; + u32 tirn = 0; int inlen; - int err = 0; + void *in; + inlen = MLX5_ST_SZ_BYTES(modify_tir_in); in = mlx5_vzalloc(inlen); if (!in) - return -ENOMEM; + goto out; if (enable_uc_lb) MLX5_SET(modify_tir_in, in, ctx.self_lb_block, @@ -156,13 +158,16 @@ int mlx5e_refresh_tirs_self_loopback(struct mlx5_core_dev *mdev, MLX5_SET(modify_tir_in, in, bitmask.self_lb_en, 1); list_for_each_entry(tir, &mdev->mlx5e_res.td.tirs_list, list) { - err = mlx5_core_modify_tir(mdev, tir->tirn, in, inlen); + tirn = tir->tirn; + err = mlx5_core_modify_tir(mdev, tirn, in, inlen); if (err) goto out; } out: kvfree(in); + if (err) + netdev_err(priv->netdev, "refresh tir(0x%x) failed, %d\n", tirn, err); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index aec77f075714..a98d01684247 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2498,13 +2498,7 @@ int mlx5e_open_locked(struct net_device *netdev) goto err_clear_state_opened_flag; } - err = mlx5e_refresh_tirs_self_loopback(priv->mdev, false); - if (err) { - netdev_err(netdev, "%s: mlx5e_refresh_tirs_self_loopback_enable failed, %d\n", - __func__, err); - goto err_close_channels; - } - + mlx5e_refresh_tirs(priv, false); mlx5e_redirect_rqts_to_channels(priv, &priv->channels); mlx5e_update_carrier(priv); mlx5e_timestamp_init(priv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c index 5621dcfda4f1..5225f2226a67 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c @@ -236,12 +236,9 @@ static int mlx5e_test_loopback_setup(struct mlx5e_priv *priv, { int err = 0; - err = mlx5e_refresh_tirs_self_loopback(priv->mdev, true); - if (err) { - netdev_err(priv->netdev, - "\tFailed to enable UC loopback err(%d)\n", err); + err = mlx5e_refresh_tirs(priv, true); + if (err) return err; - } lbtp->loopback_ok = false; init_completion(&lbtp->comp); @@ -258,7 +255,7 @@ static void mlx5e_test_loopback_cleanup(struct mlx5e_priv *priv, struct mlx5e_lbt_priv *lbtp) { dev_remove_pack(&lbtp->pt); - mlx5e_refresh_tirs_self_loopback(priv->mdev, false); + mlx5e_refresh_tirs(priv, false); } #define MLX5E_LB_VERIFY_TIMEOUT (msecs_to_jiffies(200)) -- cgit From acc6c5953af1949fc17c09cacd4842f149b4569d Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 20 Dec 2016 22:48:19 +0200 Subject: net/mlx5e: Split open/close channels to stages As a foundation for safe config flow, a simple clear API such as (Open then Activate) where the "Open" handles the heavy unsafe creation operation and the "activate" will be fast and fail safe, to enable the newly created channels. For this we split the RQs/TXQ SQs and channels open/close flows to open => activate, deactivate => close. This will simplify the ability to have fail safe configuration changes in downstream patches as follows: make_new_config(new_params) { old_channels = current_active_channels; new_channels = create_channels(new_params); if (!new_channels) return "Failed, but current channels still active :)" deactivate_channels(old_channels); /* Can't fail */ activate_channels(new_channels); /* Can't fail */ close_channels(old_channels); current_active_channels = new_channels; return "SUCCESS"; } Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 5 +- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 214 ++++++++++++++------- drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 4 +- 4 files changed, 148 insertions(+), 77 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 5f7cc58d900c..f1895ebe7fe5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -358,6 +358,7 @@ struct mlx5e_txqsq { struct mlx5_wq_ctrl wq_ctrl; struct mlx5e_channel *channel; int tc; + int txq_ix; u32 rate_limit; } ____cacheline_aligned_in_smp; @@ -732,8 +733,8 @@ struct mlx5e_profile { struct mlx5e_priv { /* priv data path fields - start */ - struct mlx5e_txqsq **txq_to_sq_map; - int channeltc_to_txq_map[MLX5E_MAX_NUM_CHANNELS][MLX5E_MAX_NUM_TC]; + struct mlx5e_txqsq *txq2sq[MLX5E_MAX_NUM_CHANNELS * MLX5E_MAX_NUM_TC]; + int channel_tc2txq[MLX5E_MAX_NUM_CHANNELS][MLX5E_MAX_NUM_TC]; struct bpf_prog *xdp_prog; /* priv data path fields - end */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index faa21848c9dc..5159358a242d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -269,7 +269,7 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data) for (j = 0; j < NUM_SQ_STATS; j++) sprintf(data + (idx++) * ETH_GSTRING_LEN, sq_stats_desc[j].format, - priv->channeltc_to_txq_map[i][tc]); + priv->channel_tc2txq[i][tc]); } static void mlx5e_get_strings(struct net_device *dev, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index a98d01684247..6be7c2367d41 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -820,6 +820,8 @@ static int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq) msleep(20); } + netdev_warn(priv->netdev, "Failed to get min RX wqes on RQN[0x%x] wq cur_sz(%d) min_rx_wqes(%d)\n", + rq->rqn, wq->cur_sz, priv->params.min_rx_wqes); return -ETIMEDOUT; } @@ -848,9 +850,6 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, struct mlx5e_rq_param *param, struct mlx5e_rq *rq) { - struct mlx5e_icosq *sq = &c->icosq; - u16 pi = sq->pc & sq->wq.sz_m1; - struct mlx5e_tx_wqe *nopwqe; int err; err = mlx5e_alloc_rq(c, param, rq); @@ -861,7 +860,6 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, if (err) goto err_free_rq; - set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); err = mlx5e_modify_rq_state(rq, MLX5_RQC_STATE_RST, MLX5_RQC_STATE_RDY); if (err) goto err_destroy_rq; @@ -869,14 +867,9 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, if (param->am_enabled) set_bit(MLX5E_RQ_STATE_AM, &c->rq.state); - sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP; - sq->db.ico_wqe[pi].num_wqebbs = 1; - nopwqe = mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); - mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &nopwqe->ctrl); return 0; err_destroy_rq: - clear_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); mlx5e_destroy_rq(rq); err_free_rq: mlx5e_free_rq(rq); @@ -884,12 +877,28 @@ err_free_rq: return err; } -static void mlx5e_close_rq(struct mlx5e_rq *rq) +static void mlx5e_activate_rq(struct mlx5e_rq *rq) +{ + struct mlx5e_icosq *sq = &rq->channel->icosq; + u16 pi = sq->pc & sq->wq.sz_m1; + struct mlx5e_tx_wqe *nopwqe; + + set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); + sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP; + sq->db.ico_wqe[pi].num_wqebbs = 1; + nopwqe = mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); + mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &nopwqe->ctrl); +} + +static void mlx5e_deactivate_rq(struct mlx5e_rq *rq) { clear_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); napi_synchronize(&rq->channel->napi); /* prevent mlx5e_post_rx_wqes */ - cancel_work_sync(&rq->am.work); +} +static void mlx5e_close_rq(struct mlx5e_rq *rq) +{ + cancel_work_sync(&rq->am.work); mlx5e_destroy_rq(rq); mlx5e_free_rx_descs(rq); mlx5e_free_rq(rq); @@ -1041,13 +1050,13 @@ static int mlx5e_alloc_txqsq_db(struct mlx5e_txqsq *sq, int numa) static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, int tc, + int txq_ix, struct mlx5e_sq_param *param, struct mlx5e_txqsq *sq) { void *sqc_wq = MLX5_ADDR_OF(sqc, param->sqc, wq); struct mlx5e_priv *priv = c->priv; struct mlx5_core_dev *mdev = priv->mdev; - int txq_ix; int err; sq->pdev = c->pdev; @@ -1055,6 +1064,7 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, sq->mkey_be = c->mkey_be; sq->channel = c; sq->tc = tc; + sq->txq_ix = txq_ix; sq->uar_map = mdev->mlx5e_res.bfreg.map; sq->max_inline = param->max_inline; sq->min_inline_mode = param->min_inline_mode; @@ -1069,10 +1079,6 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, if (err) goto err_sq_wq_destroy; - txq_ix = c->ix + tc * priv->channels.num; - sq->txq = netdev_get_tx_queue(priv->netdev, txq_ix); - priv->txq_to_sq_map[txq_ix] = sq; - sq->edge = (sq->wq.sz_m1 + 1) - MLX5_SEND_WQE_MAX_WQEBBS; return 0; @@ -1214,16 +1220,16 @@ static int mlx5e_set_sq_maxrate(struct net_device *dev, static int mlx5e_open_txqsq(struct mlx5e_channel *c, int tc, + int txq_ix, struct mlx5e_sq_param *param, struct mlx5e_txqsq *sq) { struct mlx5e_create_sq_param csp = {}; struct mlx5e_priv *priv = c->priv; u32 tx_rate; - int txq_ix; int err; - err = mlx5e_alloc_txqsq(c, tc, param, sq); + err = mlx5e_alloc_txqsq(c, tc, txq_ix, param, sq); if (err) return err; @@ -1232,18 +1238,14 @@ static int mlx5e_open_txqsq(struct mlx5e_channel *c, csp.cqn = sq->cq.mcq.cqn; csp.wq_ctrl = &sq->wq_ctrl; csp.min_inline_mode = sq->min_inline_mode; - set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); err = mlx5e_create_sq_rdy(c->priv, param, &csp, &sq->sqn); if (err) goto err_free_txqsq; - txq_ix = c->ix + tc * priv->channels.num; - tx_rate = priv->tx_rates[txq_ix]; + tx_rate = priv->tx_rates[sq->txq_ix]; if (tx_rate) mlx5e_set_sq_maxrate(priv->netdev, sq, tx_rate); - netdev_tx_reset_queue(sq->txq); - netif_tx_start_queue(sq->txq); return 0; err_free_txqsq: @@ -1253,6 +1255,16 @@ err_free_txqsq: return err; } +static void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq) +{ + struct mlx5e_priv *priv = sq->channel->priv; + + sq->txq = netdev_get_tx_queue(priv->netdev, sq->txq_ix); + set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); + netdev_tx_reset_queue(sq->txq); + netif_tx_start_queue(sq->txq); +} + static inline void netif_tx_disable_queue(struct netdev_queue *txq) { __netif_tx_lock_bh(txq); @@ -1260,11 +1272,9 @@ static inline void netif_tx_disable_queue(struct netdev_queue *txq) __netif_tx_unlock_bh(txq); } -static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) +static void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq) { struct mlx5e_channel *c = sq->channel; - struct mlx5e_priv *priv = c->priv; - struct mlx5_core_dev *mdev = priv->mdev; clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); /* prevent netif_tx_wake_queue */ @@ -1280,6 +1290,13 @@ static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) nop = mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &nop->ctrl); } +} + +static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) +{ + struct mlx5e_channel *c = sq->channel; + struct mlx5e_priv *priv = c->priv; + struct mlx5_core_dev *mdev = priv->mdev; mlx5e_destroy_sq(priv, sq->sqn); if (sq->rate_limit) @@ -1578,7 +1595,9 @@ static int mlx5e_open_sqs(struct mlx5e_channel *c, int tc; for (tc = 0; tc < c->num_tc; tc++) { - err = mlx5e_open_txqsq(c, tc, &cparam->sq, &c->sq[tc]); + int txq_ix = c->ix + tc * c->priv->channels.num; + + err = mlx5e_open_txqsq(c, tc, txq_ix, &cparam->sq, &c->sq[tc]); if (err) goto err_close_sqs; } @@ -1600,14 +1619,6 @@ static void mlx5e_close_sqs(struct mlx5e_channel *c) mlx5e_close_txqsq(&c->sq[tc]); } -static void mlx5e_build_channeltc_to_txq_map(struct mlx5e_priv *priv, int ix) -{ - int i; - - for (i = 0; i < priv->profile->max_tc; i++) - priv->channeltc_to_txq_map[ix][i] = ix + i * priv->channels.num; -} - static int mlx5e_set_sq_maxrate(struct net_device *dev, struct mlx5e_txqsq *sq, u32 rate) { @@ -1658,7 +1669,7 @@ static int mlx5e_set_tx_maxrate(struct net_device *dev, int index, u32 rate) { struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5_core_dev *mdev = priv->mdev; - struct mlx5e_txqsq *sq = priv->txq_to_sq_map[index]; + struct mlx5e_txqsq *sq = priv->txq2sq[index]; int err = 0; if (!mlx5_rl_is_supported(mdev)) { @@ -1722,8 +1733,6 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, else rx_cq_profile = priv->params.rx_cq_moderation; - mlx5e_build_channeltc_to_txq_map(priv, ix); - netif_napi_add(netdev, &c->napi, mlx5e_napi_poll, 64); err = mlx5e_open_cq(c, &cparam->icosq_cq, &c->icosq.cq, icosq_cq_moder); @@ -1763,7 +1772,6 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, if (err) goto err_close_xdp_sq; - netif_set_xps_queue(netdev, get_cpu_mask(c->cpu), ix); *cp = c; return 0; @@ -1798,6 +1806,25 @@ err_napi_del: return err; } +static void mlx5e_activate_channel(struct mlx5e_channel *c) +{ + int tc; + + for (tc = 0; tc < c->num_tc; tc++) + mlx5e_activate_txqsq(&c->sq[tc]); + mlx5e_activate_rq(&c->rq); + netif_set_xps_queue(c->priv->netdev, get_cpu_mask(c->cpu), c->ix); +} + +static void mlx5e_deactivate_channel(struct mlx5e_channel *c) +{ + int tc; + + mlx5e_deactivate_rq(&c->rq); + for (tc = 0; tc < c->num_tc; tc++) + mlx5e_deactivate_txqsq(&c->sq[tc]); +} + static void mlx5e_close_channel(struct mlx5e_channel *c) { mlx5e_close_rq(&c->rq); @@ -1983,16 +2010,13 @@ static int mlx5e_open_channels(struct mlx5e_priv *priv, struct mlx5e_channels *c struct mlx5e_channel_param *cparam; int err = -ENOMEM; int i; - int j; chs->num = priv->params.num_channels; chs->c = kcalloc(chs->num, sizeof(struct mlx5e_channel *), GFP_KERNEL); - priv->txq_to_sq_map = kcalloc(chs->num * priv->params.num_tc, - sizeof(struct mlx5e_sq *), GFP_KERNEL); cparam = kzalloc(sizeof(struct mlx5e_channel_param), GFP_KERNEL); - if (!chs->c || !priv->txq_to_sq_map || !cparam) - goto err_free_txq_to_sq_map; + if (!chs->c || !cparam) + goto err_free; mlx5e_build_channel_param(priv, cparam); for (i = 0; i < chs->num; i++) { @@ -2001,17 +2025,6 @@ static int mlx5e_open_channels(struct mlx5e_priv *priv, struct mlx5e_channels *c goto err_close_channels; } - for (j = 0; j < chs->num; j++) { - err = mlx5e_wait_for_min_rx_wqes(&chs->c[j]->rq); - if (err) - goto err_close_channels; - } - - /* FIXME: This is a W/A for tx timeout watch dog false alarm when - * polling for inactive tx queues. - */ - netif_tx_start_all_queues(priv->netdev); - kfree(cparam); return 0; @@ -2019,29 +2032,50 @@ err_close_channels: for (i--; i >= 0; i--) mlx5e_close_channel(chs->c[i]); -err_free_txq_to_sq_map: - kfree(priv->txq_to_sq_map); +err_free: kfree(chs->c); kfree(cparam); chs->num = 0; return err; } -static void mlx5e_close_channels(struct mlx5e_priv *priv) +static void mlx5e_activate_channels(struct mlx5e_channels *chs) { - struct mlx5e_channels *chs = &priv->channels; int i; - /* FIXME: This is a W/A only for tx timeout watch dog false alarm when - * polling for inactive tx queues. - */ - netif_tx_stop_all_queues(priv->netdev); - netif_tx_disable(priv->netdev); + for (i = 0; i < chs->num; i++) + mlx5e_activate_channel(chs->c[i]); +} + +static int mlx5e_wait_channels_min_rx_wqes(struct mlx5e_channels *chs) +{ + int err = 0; + int i; + + for (i = 0; i < chs->num; i++) { + err = mlx5e_wait_for_min_rx_wqes(&chs->c[i]->rq); + if (err) + break; + } + + return err; +} + +static void mlx5e_deactivate_channels(struct mlx5e_channels *chs) +{ + int i; + + for (i = 0; i < chs->num; i++) + mlx5e_deactivate_channel(chs->c[i]); +} + +static void mlx5e_close_channels(struct mlx5e_channels *chs) +{ + int i; for (i = 0; i < chs->num; i++) mlx5e_close_channel(chs->c[i]); - kfree(priv->txq_to_sq_map); kfree(chs->c); chs->num = 0; } @@ -2476,6 +2510,43 @@ static void mlx5e_netdev_set_tcs(struct net_device *netdev) netdev_set_tc_queue(netdev, tc, nch, 0); } +static void mlx5e_build_channels_tx_maps(struct mlx5e_priv *priv) +{ + struct mlx5e_channel *c; + struct mlx5e_txqsq *sq; + int i, tc; + + for (i = 0; i < priv->channels.num; i++) + for (tc = 0; tc < priv->profile->max_tc; tc++) + priv->channel_tc2txq[i][tc] = i + tc * priv->channels.num; + + for (i = 0; i < priv->channels.num; i++) { + c = priv->channels.c[i]; + for (tc = 0; tc < c->num_tc; tc++) { + sq = &c->sq[tc]; + priv->txq2sq[sq->txq_ix] = sq; + } + } +} + +static void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) +{ + mlx5e_build_channels_tx_maps(priv); + mlx5e_activate_channels(&priv->channels); + netif_tx_start_all_queues(priv->netdev); + mlx5e_wait_channels_min_rx_wqes(&priv->channels); +} + +static void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv) +{ + /* FIXME: This is a W/A only for tx timeout watch dog false alarm when + * polling for inactive tx queues. + */ + netif_tx_stop_all_queues(priv->netdev); + netif_tx_disable(priv->netdev); + mlx5e_deactivate_channels(&priv->channels); +} + int mlx5e_open_locked(struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); @@ -2492,13 +2563,11 @@ int mlx5e_open_locked(struct net_device *netdev) netif_set_real_num_rx_queues(netdev, priv->params.num_channels); err = mlx5e_open_channels(priv, &priv->channels); - if (err) { - netdev_err(netdev, "%s: mlx5e_open_channels failed, %d\n", - __func__, err); + if (err) goto err_clear_state_opened_flag; - } mlx5e_refresh_tirs(priv, false); + mlx5e_activate_priv_channels(priv); mlx5e_redirect_rqts_to_channels(priv, &priv->channels); mlx5e_update_carrier(priv); mlx5e_timestamp_init(priv); @@ -2514,7 +2583,7 @@ int mlx5e_open_locked(struct net_device *netdev) return 0; err_close_channels: - mlx5e_close_channels(priv); + mlx5e_close_channels(&priv->channels); err_clear_state_opened_flag: clear_bit(MLX5E_STATE_OPENED, &priv->state); return err; @@ -2551,7 +2620,8 @@ int mlx5e_close_locked(struct net_device *netdev) mlx5e_timestamp_cleanup(priv); netif_carrier_off(priv->netdev); mlx5e_redirect_rqts_to_drop(priv); - mlx5e_close_channels(priv); + mlx5e_deactivate_priv_channels(priv); + mlx5e_close_channels(&priv->channels); return 0; } @@ -3360,7 +3430,7 @@ static void mlx5e_tx_timeout(struct net_device *dev) netdev_err(dev, "TX timeout detected\n"); for (i = 0; i < priv->channels.num * priv->params.num_tc; i++) { - struct mlx5e_txqsq *sq = priv->txq_to_sq_map[i]; + struct mlx5e_txqsq *sq = priv->txq2sq[i]; if (!netif_xmit_stopped(netdev_get_tx_queue(dev, i))) continue; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 20f71b55651e..42743b114bcf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -103,7 +103,7 @@ u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb, channel_ix = reciprocal_scale(channel_ix, priv->params.num_channels); - return priv->channeltc_to_txq_map[channel_ix][up]; + return priv->channel_tc2txq[channel_ix][up]; } static inline int mlx5e_skb_l2_header_offset(struct sk_buff *skb) @@ -339,7 +339,7 @@ dma_unmap_wqe_err: netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev) { struct mlx5e_priv *priv = netdev_priv(dev); - struct mlx5e_txqsq *sq = priv->txq_to_sq_map[skb_get_queue_mapping(skb)]; + struct mlx5e_txqsq *sq = priv->txq2sq[skb_get_queue_mapping(skb)]; return mlx5e_sq_xmit(sq, skb); } -- cgit From 6a9764efb255f49a91e229799c38d5c1c9361987 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Wed, 21 Dec 2016 17:24:35 +0200 Subject: net/mlx5e: Isolate open_channels from priv->params In order to have a clean separation between channels resources creation flows and current active mlx5e netdev parameters, make sure each resource creation function do not access priv->params, and only works with on a new fresh set of parameters. For this we add "new" mlx5e_params field to mlx5e_channels structure and use it down the road to mlx5e_open_{cq,rq,sq} and so on. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 22 +- drivers/net/ethernet/mellanox/mlx5/core/en_clock.c | 2 +- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 119 +++--- .../ethernet/mellanox/mlx5/core/en_fs_ethtool.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 448 ++++++++++----------- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 61 ++- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 8 +- drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 7 +- 8 files changed, 328 insertions(+), 341 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index f1895ebe7fe5..007f91f54fda 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -182,15 +182,15 @@ enum mlx5e_priv_flag { MLX5E_PFLAG_RX_CQE_COMPRESS = (1 << 1), }; -#define MLX5E_SET_PFLAG(priv, pflag, enable) \ +#define MLX5E_SET_PFLAG(params, pflag, enable) \ do { \ if (enable) \ - (priv)->params.pflags |= (pflag); \ + (params)->pflags |= (pflag); \ else \ - (priv)->params.pflags &= ~(pflag); \ + (params)->pflags &= ~(pflag); \ } while (0) -#define MLX5E_GET_PFLAG(priv, pflag) (!!((priv)->params.pflags & (pflag))) +#define MLX5E_GET_PFLAG(params, pflag) (!!((params)->pflags & (pflag))) #ifdef CONFIG_MLX5_CORE_EN_DCB #define MLX5E_MAX_BW_ALLOC 100 /* Max percentage of BW allocation */ @@ -213,7 +213,6 @@ struct mlx5e_params { bool rx_cqe_compress_def; struct mlx5e_cq_moder rx_cq_moderation; struct mlx5e_cq_moder tx_cq_moderation; - u16 min_rx_wqes; bool lro_en; u32 lro_wqe_sz; u16 tx_max_inline; @@ -225,6 +224,7 @@ struct mlx5e_params { bool rx_am_enabled; u32 lro_timeout; u32 pflags; + struct bpf_prog *xdp_prog; }; #ifdef CONFIG_MLX5_CORE_EN_DCB @@ -357,7 +357,6 @@ struct mlx5e_txqsq { /* control path */ struct mlx5_wq_ctrl wq_ctrl; struct mlx5e_channel *channel; - int tc; int txq_ix; u32 rate_limit; } ____cacheline_aligned_in_smp; @@ -564,6 +563,7 @@ struct mlx5e_channel { struct mlx5e_channels { struct mlx5e_channel **c; unsigned int num; + struct mlx5e_params params; }; enum mlx5e_traffic_types { @@ -735,7 +735,6 @@ struct mlx5e_priv { /* priv data path fields - start */ struct mlx5e_txqsq *txq2sq[MLX5E_MAX_NUM_CHANNELS * MLX5E_MAX_NUM_TC]; int channel_tc2txq[MLX5E_MAX_NUM_CHANNELS][MLX5E_MAX_NUM_TC]; - struct bpf_prog *xdp_prog; /* priv data path fields - end */ unsigned long state; @@ -752,7 +751,6 @@ struct mlx5e_priv { struct mlx5e_flow_steering fs; struct mlx5e_vxlan_db vxlan; - struct mlx5e_params params; struct workqueue_struct *wq; struct work_struct update_carrier_work; struct work_struct set_rx_mode_work; @@ -857,8 +855,9 @@ struct mlx5e_redirect_rqt_param { int mlx5e_redirect_rqt(struct mlx5e_priv *priv, u32 rqtn, int sz, struct mlx5e_redirect_rqt_param rrp); -void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_priv *priv, void *tirc, - enum mlx5e_traffic_types tt); +void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_params *params, + enum mlx5e_traffic_types tt, + void *tirc); int mlx5e_open_locked(struct net_device *netdev); int mlx5e_close_locked(struct net_device *netdev); @@ -869,7 +868,8 @@ int mlx5e_get_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed); void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode); -void mlx5e_set_rq_type_params(struct mlx5e_priv *priv, u8 rq_type); +void mlx5e_set_rq_type_params(struct mlx5_core_dev *mdev, + struct mlx5e_params *params, u8 rq_type); static inline struct mlx5e_tx_wqe *mlx5e_post_nop(struct mlx5_wq_cyc *wq, u32 sqn, u16 *pc) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c index 37e66eef6fb5..485c23b59f93 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c @@ -111,7 +111,7 @@ int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr) switch (config.rx_filter) { case HWTSTAMP_FILTER_NONE: /* Reset CQE compression to Admin default */ - mlx5e_modify_rx_cqe_compression_locked(priv, priv->params.rx_cqe_compress_def); + mlx5e_modify_rx_cqe_compression_locked(priv, priv->channels.params.rx_cqe_compress_def); break; case HWTSTAMP_FILTER_ALL: case HWTSTAMP_FILTER_SOME: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 5159358a242d..b2cd0ef7921e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -154,7 +154,7 @@ static bool mlx5e_query_global_pause_combined(struct mlx5e_priv *priv) #define MLX5E_NUM_Q_CNTRS(priv) (NUM_Q_COUNTERS * (!!priv->q_counter)) #define MLX5E_NUM_RQ_STATS(priv) (NUM_RQ_STATS * (priv)->channels.num) #define MLX5E_NUM_SQ_STATS(priv) \ - (NUM_SQ_STATS * (priv)->channels.num * (priv)->params.num_tc) + (NUM_SQ_STATS * (priv)->channels.num * (priv)->channels.params.num_tc) #define MLX5E_NUM_PFC_COUNTERS(priv) \ ((mlx5e_query_global_pause_combined(priv) + hweight8(mlx5e_query_pfc_combined(priv))) * \ NUM_PPORT_PER_PRIO_PFC_COUNTERS) @@ -264,7 +264,7 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data) sprintf(data + (idx++) * ETH_GSTRING_LEN, rq_stats_desc[j].format, i); - for (tc = 0; tc < priv->params.num_tc; tc++) + for (tc = 0; tc < priv->channels.params.num_tc; tc++) for (i = 0; i < priv->channels.num; i++) for (j = 0; j < NUM_SQ_STATS; j++) sprintf(data + (idx++) * ETH_GSTRING_LEN, @@ -387,7 +387,7 @@ static void mlx5e_get_ethtool_stats(struct net_device *dev, MLX5E_READ_CTR64_CPU(&channels->c[i]->rq.stats, rq_stats_desc, j); - for (tc = 0; tc < priv->params.num_tc; tc++) + for (tc = 0; tc < priv->channels.params.num_tc; tc++) for (i = 0; i < channels->num; i++) for (j = 0; j < NUM_SQ_STATS; j++) data[idx++] = MLX5E_READ_CTR64_CPU(&channels->c[i]->sq[tc].stats, @@ -405,8 +405,8 @@ static u32 mlx5e_rx_wqes_to_packets(struct mlx5e_priv *priv, int rq_wq_type, if (rq_wq_type != MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) return num_wqe; - stride_size = 1 << priv->params.mpwqe_log_stride_sz; - num_strides = 1 << priv->params.mpwqe_log_num_strides; + stride_size = 1 << priv->channels.params.mpwqe_log_stride_sz; + num_strides = 1 << priv->channels.params.mpwqe_log_num_strides; wqe_size = stride_size * num_strides; packets_per_wqe = wqe_size / @@ -426,8 +426,8 @@ static u32 mlx5e_packets_to_rx_wqes(struct mlx5e_priv *priv, int rq_wq_type, if (rq_wq_type != MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) return num_packets; - stride_size = 1 << priv->params.mpwqe_log_stride_sz; - num_strides = 1 << priv->params.mpwqe_log_num_strides; + stride_size = 1 << priv->channels.params.mpwqe_log_stride_sz; + num_strides = 1 << priv->channels.params.mpwqe_log_num_strides; wqe_size = stride_size * num_strides; num_packets = (1 << order_base_2(num_packets)); @@ -442,26 +442,25 @@ static void mlx5e_get_ringparam(struct net_device *dev, struct ethtool_ringparam *param) { struct mlx5e_priv *priv = netdev_priv(dev); - int rq_wq_type = priv->params.rq_wq_type; + int rq_wq_type = priv->channels.params.rq_wq_type; param->rx_max_pending = mlx5e_rx_wqes_to_packets(priv, rq_wq_type, 1 << mlx5_max_log_rq_size(rq_wq_type)); param->tx_max_pending = 1 << MLX5E_PARAMS_MAXIMUM_LOG_SQ_SIZE; param->rx_pending = mlx5e_rx_wqes_to_packets(priv, rq_wq_type, - 1 << priv->params.log_rq_size); - param->tx_pending = 1 << priv->params.log_sq_size; + 1 << priv->channels.params.log_rq_size); + param->tx_pending = 1 << priv->channels.params.log_sq_size; } static int mlx5e_set_ringparam(struct net_device *dev, struct ethtool_ringparam *param) { struct mlx5e_priv *priv = netdev_priv(dev); - bool was_opened; - int rq_wq_type = priv->params.rq_wq_type; + int rq_wq_type = priv->channels.params.rq_wq_type; u32 rx_pending_wqes; + bool was_opened; u32 min_rq_size; u32 max_rq_size; - u16 min_rx_wqes; u8 log_rq_size; u8 log_sq_size; u32 num_mtts; @@ -499,7 +498,7 @@ static int mlx5e_set_ringparam(struct net_device *dev, } num_mtts = MLX5E_REQUIRED_MTTS(rx_pending_wqes); - if (priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ && + if (priv->channels.params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ && !MLX5E_VALID_NUM_MTTS(num_mtts)) { netdev_info(dev, "%s: rx_pending (%d) request can't be satisfied, try to reduce.\n", __func__, param->rx_pending); @@ -521,11 +520,9 @@ static int mlx5e_set_ringparam(struct net_device *dev, log_rq_size = order_base_2(rx_pending_wqes); log_sq_size = order_base_2(param->tx_pending); - min_rx_wqes = mlx5_min_rx_wqes(rq_wq_type, rx_pending_wqes); - if (log_rq_size == priv->params.log_rq_size && - log_sq_size == priv->params.log_sq_size && - min_rx_wqes == priv->params.min_rx_wqes) + if (log_rq_size == priv->channels.params.log_rq_size && + log_sq_size == priv->channels.params.log_sq_size) return 0; mutex_lock(&priv->state_lock); @@ -534,9 +531,8 @@ static int mlx5e_set_ringparam(struct net_device *dev, if (was_opened) mlx5e_close_locked(dev); - priv->params.log_rq_size = log_rq_size; - priv->params.log_sq_size = log_sq_size; - priv->params.min_rx_wqes = min_rx_wqes; + priv->channels.params.log_rq_size = log_rq_size; + priv->channels.params.log_sq_size = log_sq_size; if (was_opened) err = mlx5e_open_locked(dev); @@ -552,7 +548,7 @@ static void mlx5e_get_channels(struct net_device *dev, struct mlx5e_priv *priv = netdev_priv(dev); ch->max_combined = priv->profile->max_nch(priv->mdev); - ch->combined_count = priv->params.num_channels; + ch->combined_count = priv->channels.params.num_channels; } static int mlx5e_set_channels(struct net_device *dev, @@ -570,7 +566,7 @@ static int mlx5e_set_channels(struct net_device *dev, return -EINVAL; } - if (priv->params.num_channels == count) + if (priv->channels.params.num_channels == count) return 0; mutex_lock(&priv->state_lock); @@ -583,8 +579,8 @@ static int mlx5e_set_channels(struct net_device *dev, if (arfs_enabled) mlx5e_arfs_disable(priv); - priv->params.num_channels = count; - mlx5e_build_default_indir_rqt(priv->mdev, priv->params.indirection_rqt, + priv->channels.params.num_channels = count; + mlx5e_build_default_indir_rqt(priv->mdev, priv->channels.params.indirection_rqt, MLX5E_INDIR_RQT_SIZE, count); if (was_opened) @@ -613,11 +609,11 @@ static int mlx5e_get_coalesce(struct net_device *netdev, if (!MLX5_CAP_GEN(priv->mdev, cq_moderation)) return -EOPNOTSUPP; - coal->rx_coalesce_usecs = priv->params.rx_cq_moderation.usec; - coal->rx_max_coalesced_frames = priv->params.rx_cq_moderation.pkts; - coal->tx_coalesce_usecs = priv->params.tx_cq_moderation.usec; - coal->tx_max_coalesced_frames = priv->params.tx_cq_moderation.pkts; - coal->use_adaptive_rx_coalesce = priv->params.rx_am_enabled; + coal->rx_coalesce_usecs = priv->channels.params.rx_cq_moderation.usec; + coal->rx_max_coalesced_frames = priv->channels.params.rx_cq_moderation.pkts; + coal->tx_coalesce_usecs = priv->channels.params.tx_cq_moderation.usec; + coal->tx_max_coalesced_frames = priv->channels.params.tx_cq_moderation.pkts; + coal->use_adaptive_rx_coalesce = priv->channels.params.rx_am_enabled; return 0; } @@ -628,7 +624,7 @@ static int mlx5e_set_coalesce(struct net_device *netdev, struct mlx5e_priv *priv = netdev_priv(netdev); struct mlx5_core_dev *mdev = priv->mdev; bool restart = - !!coal->use_adaptive_rx_coalesce != priv->params.rx_am_enabled; + !!coal->use_adaptive_rx_coalesce != priv->channels.params.rx_am_enabled; bool was_opened; int err = 0; int tc; @@ -642,13 +638,13 @@ static int mlx5e_set_coalesce(struct net_device *netdev, was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); if (was_opened && restart) { mlx5e_close_locked(netdev); - priv->params.rx_am_enabled = !!coal->use_adaptive_rx_coalesce; + priv->channels.params.rx_am_enabled = !!coal->use_adaptive_rx_coalesce; } - priv->params.tx_cq_moderation.usec = coal->tx_coalesce_usecs; - priv->params.tx_cq_moderation.pkts = coal->tx_max_coalesced_frames; - priv->params.rx_cq_moderation.usec = coal->rx_coalesce_usecs; - priv->params.rx_cq_moderation.pkts = coal->rx_max_coalesced_frames; + priv->channels.params.tx_cq_moderation.usec = coal->tx_coalesce_usecs; + priv->channels.params.tx_cq_moderation.pkts = coal->tx_max_coalesced_frames; + priv->channels.params.rx_cq_moderation.usec = coal->rx_coalesce_usecs; + priv->channels.params.rx_cq_moderation.pkts = coal->rx_max_coalesced_frames; if (!was_opened || restart) goto out; @@ -965,7 +961,7 @@ static u32 mlx5e_get_rxfh_key_size(struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); - return sizeof(priv->params.toeplitz_hash_key); + return sizeof(priv->channels.params.toeplitz_hash_key); } static u32 mlx5e_get_rxfh_indir_size(struct net_device *netdev) @@ -979,15 +975,15 @@ static int mlx5e_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, struct mlx5e_priv *priv = netdev_priv(netdev); if (indir) - memcpy(indir, priv->params.indirection_rqt, - sizeof(priv->params.indirection_rqt)); + memcpy(indir, priv->channels.params.indirection_rqt, + sizeof(priv->channels.params.indirection_rqt)); if (key) - memcpy(key, priv->params.toeplitz_hash_key, - sizeof(priv->params.toeplitz_hash_key)); + memcpy(key, priv->channels.params.toeplitz_hash_key, + sizeof(priv->channels.params.toeplitz_hash_key)); if (hfunc) - *hfunc = priv->params.rss_hfunc; + *hfunc = priv->channels.params.rss_hfunc; return 0; } @@ -1003,7 +999,7 @@ static void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen) for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) { memset(tirc, 0, ctxlen); - mlx5e_build_indir_tir_ctx_hash(priv, tirc, tt); + mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc); mlx5_core_modify_tir(mdev, priv->indir_tir[tt].tirn, in, inlen); } } @@ -1028,20 +1024,20 @@ static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir, mutex_lock(&priv->state_lock); if (hfunc != ETH_RSS_HASH_NO_CHANGE && - hfunc != priv->params.rss_hfunc) { - priv->params.rss_hfunc = hfunc; + hfunc != priv->channels.params.rss_hfunc) { + priv->channels.params.rss_hfunc = hfunc; hash_changed = true; } if (indir) { - memcpy(priv->params.indirection_rqt, indir, - sizeof(priv->params.indirection_rqt)); + memcpy(priv->channels.params.indirection_rqt, indir, + sizeof(priv->channels.params.indirection_rqt)); if (test_bit(MLX5E_STATE_OPENED, &priv->state)) { u32 rqtn = priv->indir_rqt.rqtn; struct mlx5e_redirect_rqt_param rrp = { .is_rss = true, - .rss.hfunc = priv->params.rss_hfunc, + .rss.hfunc = priv->channels.params.rss_hfunc, .rss.channels = &priv->channels }; @@ -1050,10 +1046,10 @@ static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir, } if (key) { - memcpy(priv->params.toeplitz_hash_key, key, - sizeof(priv->params.toeplitz_hash_key)); + memcpy(priv->channels.params.toeplitz_hash_key, key, + sizeof(priv->channels.params.toeplitz_hash_key)); hash_changed = hash_changed || - priv->params.rss_hfunc == ETH_RSS_HASH_TOP; + priv->channels.params.rss_hfunc == ETH_RSS_HASH_TOP; } if (hash_changed) @@ -1074,7 +1070,7 @@ static int mlx5e_get_rxnfc(struct net_device *netdev, switch (info->cmd) { case ETHTOOL_GRXRINGS: - info->data = priv->params.num_channels; + info->data = priv->channels.params.num_channels; break; case ETHTOOL_GRXCLSRLCNT: info->rule_cnt = priv->fs.ethtool.tot_num_rules; @@ -1102,7 +1098,7 @@ static int mlx5e_get_tunable(struct net_device *dev, switch (tuna->id) { case ETHTOOL_TX_COPYBREAK: - *(u32 *)data = priv->params.tx_max_inline; + *(u32 *)data = priv->channels.params.tx_max_inline; break; default: err = -EINVAL; @@ -1136,7 +1132,7 @@ static int mlx5e_set_tunable(struct net_device *dev, if (was_opened) mlx5e_close_locked(dev); - priv->params.tx_max_inline = val; + priv->channels.params.tx_max_inline = val; if (was_opened) err = mlx5e_open_locked(dev); @@ -1455,7 +1451,7 @@ static int set_pflag_rx_cqe_based_moder(struct net_device *netdev, bool enable) rx_cq_period_mode = enable ? MLX5_CQ_PERIOD_MODE_START_FROM_CQE : MLX5_CQ_PERIOD_MODE_START_FROM_EQE; - rx_mode_changed = rx_cq_period_mode != priv->params.rx_cq_period_mode; + rx_mode_changed = rx_cq_period_mode != priv->channels.params.rx_cq_period_mode; if (rx_cq_period_mode == MLX5_CQ_PERIOD_MODE_START_FROM_CQE && !MLX5_CAP_GEN(mdev, cq_period_start_from_cqe)) @@ -1468,7 +1464,7 @@ static int set_pflag_rx_cqe_based_moder(struct net_device *netdev, bool enable) if (reset) mlx5e_close_locked(netdev); - mlx5e_set_rx_cq_mode_params(&priv->params, rx_cq_period_mode); + mlx5e_set_rx_cq_mode_params(&priv->channels.params, rx_cq_period_mode); if (reset) err = mlx5e_open_locked(netdev); @@ -1491,8 +1487,9 @@ static int set_pflag_rx_cqe_compress(struct net_device *netdev, } mlx5e_modify_rx_cqe_compression_locked(priv, enable); - priv->params.rx_cqe_compress_def = enable; - mlx5e_set_rq_type_params(priv, priv->params.rq_wq_type); + priv->channels.params.rx_cqe_compress_def = enable; + mlx5e_set_rq_type_params(priv->mdev, &priv->channels.params, + priv->channels.params.rq_wq_type); return 0; } @@ -1504,7 +1501,7 @@ static int mlx5e_handle_pflag(struct net_device *netdev, { struct mlx5e_priv *priv = netdev_priv(netdev); bool enable = !!(wanted_flags & flag); - u32 changes = wanted_flags ^ priv->params.pflags; + u32 changes = wanted_flags ^ priv->channels.params.pflags; int err; if (!(changes & flag)) @@ -1517,7 +1514,7 @@ static int mlx5e_handle_pflag(struct net_device *netdev, return err; } - MLX5E_SET_PFLAG(priv, flag, enable); + MLX5E_SET_PFLAG(&priv->channels.params, flag, enable); return 0; } @@ -1546,7 +1543,7 @@ static u32 mlx5e_get_priv_flags(struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); - return priv->params.pflags; + return priv->channels.params.pflags; } static int mlx5e_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c index d55fff0ba388..e73c97fea55c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c @@ -390,7 +390,7 @@ static int validate_flow(struct mlx5e_priv *priv, if (fs->location >= MAX_NUM_OF_ETHTOOL_RULES) return -EINVAL; - if (fs->ring_cookie >= priv->params.num_channels && + if (fs->ring_cookie >= priv->channels.params.num_channels && fs->ring_cookie != RX_CLS_FLOW_DISC) return -EINVAL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 6be7c2367d41..cf8df1d3275e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -44,14 +44,11 @@ struct mlx5e_rq_param { u32 rqc[MLX5_ST_SZ_DW(rqc)]; struct mlx5_wq_param wq; - bool am_enabled; }; struct mlx5e_sq_param { u32 sqc[MLX5_ST_SZ_DW(sqc)]; struct mlx5_wq_param wq; - u16 max_inline; - u8 min_inline_mode; }; struct mlx5e_cq_param { @@ -78,49 +75,47 @@ static bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev) MLX5_CAP_ETH(mdev, reg_umr_sq); } -void mlx5e_set_rq_type_params(struct mlx5e_priv *priv, u8 rq_type) +void mlx5e_set_rq_type_params(struct mlx5_core_dev *mdev, + struct mlx5e_params *params, u8 rq_type) { - priv->params.rq_wq_type = rq_type; - priv->params.lro_wqe_sz = MLX5E_PARAMS_DEFAULT_LRO_WQE_SZ; - switch (priv->params.rq_wq_type) { + params->rq_wq_type = rq_type; + params->lro_wqe_sz = MLX5E_PARAMS_DEFAULT_LRO_WQE_SZ; + switch (params->rq_wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: - priv->params.log_rq_size = is_kdump_kernel() ? + params->log_rq_size = is_kdump_kernel() ? MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE_MPW : MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE_MPW; - priv->params.mpwqe_log_stride_sz = - MLX5E_GET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS) ? - MLX5_MPWRQ_CQE_CMPRS_LOG_STRIDE_SZ(priv->mdev) : - MLX5_MPWRQ_DEF_LOG_STRIDE_SZ(priv->mdev); - priv->params.mpwqe_log_num_strides = MLX5_MPWRQ_LOG_WQE_SZ - - priv->params.mpwqe_log_stride_sz; + params->mpwqe_log_stride_sz = + MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS) ? + MLX5_MPWRQ_CQE_CMPRS_LOG_STRIDE_SZ(mdev) : + MLX5_MPWRQ_DEF_LOG_STRIDE_SZ(mdev); + params->mpwqe_log_num_strides = MLX5_MPWRQ_LOG_WQE_SZ - + params->mpwqe_log_stride_sz; break; default: /* MLX5_WQ_TYPE_LINKED_LIST */ - priv->params.log_rq_size = is_kdump_kernel() ? + params->log_rq_size = is_kdump_kernel() ? MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE : MLX5E_PARAMS_DEFAULT_LOG_RQ_SIZE; /* Extra room needed for build_skb */ - priv->params.lro_wqe_sz -= MLX5_RX_HEADROOM + + params->lro_wqe_sz -= MLX5_RX_HEADROOM + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); } - priv->params.min_rx_wqes = mlx5_min_rx_wqes(priv->params.rq_wq_type, - BIT(priv->params.log_rq_size)); - mlx5_core_info(priv->mdev, - "MLX5E: StrdRq(%d) RqSz(%ld) StrdSz(%ld) RxCqeCmprss(%d)\n", - priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ, - BIT(priv->params.log_rq_size), - BIT(priv->params.mpwqe_log_stride_sz), - MLX5E_GET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS)); + mlx5_core_info(mdev, "MLX5E: StrdRq(%d) RqSz(%ld) StrdSz(%ld) RxCqeCmprss(%d)\n", + params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ, + BIT(params->log_rq_size), + BIT(params->mpwqe_log_stride_sz), + MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS)); } -static void mlx5e_set_rq_priv_params(struct mlx5e_priv *priv) +static void mlx5e_set_rq_params(struct mlx5_core_dev *mdev, struct mlx5e_params *params) { - u8 rq_type = mlx5e_check_fragmented_striding_rq_cap(priv->mdev) && - !priv->xdp_prog ? + u8 rq_type = mlx5e_check_fragmented_striding_rq_cap(mdev) && + !params->xdp_prog ? MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ : MLX5_WQ_TYPE_LINKED_LIST; - mlx5e_set_rq_type_params(priv, rq_type); + mlx5e_set_rq_type_params(mdev, params, rq_type); } static void mlx5e_update_carrier(struct mlx5e_priv *priv) @@ -205,7 +200,7 @@ static void mlx5e_update_sw_counters(struct mlx5e_priv *priv) s->rx_cache_empty += rq_stats->cache_empty; s->rx_cache_busy += rq_stats->cache_busy; - for (j = 0; j < priv->params.num_tc; j++) { + for (j = 0; j < priv->channels.params.num_tc; j++) { sq_stats = &c->sq[j].stats; s->tx_packets += sq_stats->packets; @@ -537,18 +532,19 @@ static int mlx5e_create_umr_mkey(struct mlx5e_priv *priv, static int mlx5e_create_rq_umr_mkey(struct mlx5e_rq *rq) { struct mlx5e_priv *priv = rq->priv; - u64 num_mtts = MLX5E_REQUIRED_MTTS(BIT(priv->params.log_rq_size)); + u64 num_mtts = MLX5E_REQUIRED_MTTS(mlx5_wq_ll_get_size(&rq->wq)); return mlx5e_create_umr_mkey(priv, num_mtts, PAGE_SHIFT, &rq->umr_mkey); } static int mlx5e_alloc_rq(struct mlx5e_channel *c, - struct mlx5e_rq_param *param, + struct mlx5e_params *params, + struct mlx5e_rq_param *rqp, struct mlx5e_rq *rq) { struct mlx5e_priv *priv = c->priv; struct mlx5_core_dev *mdev = priv->mdev; - void *rqc = param->rqc; + void *rqc = rqp->rqc; void *rqc_wq = MLX5_ADDR_OF(rqc, rqc, wq); u32 byte_count; u32 frag_sz; @@ -557,9 +553,9 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, int err; int i; - param->wq.db_numa_node = cpu_to_node(c->cpu); + rqp->wq.db_numa_node = cpu_to_node(c->cpu); - err = mlx5_wq_ll_create(mdev, ¶m->wq, rqc_wq, &rq->wq, + err = mlx5_wq_ll_create(mdev, &rqp->wq, rqc_wq, &rq->wq, &rq->wq_ctrl); if (err) return err; @@ -568,7 +564,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, wq_sz = mlx5_wq_ll_get_size(&rq->wq); - rq->wq_type = priv->params.rq_wq_type; + rq->wq_type = params->rq_wq_type; rq->pdev = c->pdev; rq->netdev = c->netdev; rq->tstamp = &priv->tstamp; @@ -576,7 +572,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, rq->ix = c->ix; rq->priv = c->priv; - rq->xdp_prog = priv->xdp_prog ? bpf_prog_inc(priv->xdp_prog) : NULL; + rq->xdp_prog = params->xdp_prog ? bpf_prog_inc(params->xdp_prog) : NULL; if (IS_ERR(rq->xdp_prog)) { err = PTR_ERR(rq->xdp_prog); rq->xdp_prog = NULL; @@ -591,7 +587,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, rq->rx_headroom = MLX5_RX_HEADROOM; } - switch (priv->params.rq_wq_type) { + switch (rq->wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: if (mlx5e_is_vf_vport_rep(priv)) { err = -EINVAL; @@ -602,8 +598,8 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, rq->alloc_wqe = mlx5e_alloc_rx_mpwqe; rq->dealloc_wqe = mlx5e_dealloc_rx_mpwqe; - rq->mpwqe_stride_sz = BIT(priv->params.mpwqe_log_stride_sz); - rq->mpwqe_num_strides = BIT(priv->params.mpwqe_log_num_strides); + rq->mpwqe_stride_sz = BIT(params->mpwqe_log_stride_sz); + rq->mpwqe_num_strides = BIT(params->mpwqe_log_num_strides); rq->buff.wqe_sz = rq->mpwqe_stride_sz * rq->mpwqe_num_strides; byte_count = rq->buff.wqe_sz; @@ -633,8 +629,8 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, rq->alloc_wqe = mlx5e_alloc_rx_wqe; rq->dealloc_wqe = mlx5e_dealloc_rx_wqe; - rq->buff.wqe_sz = (priv->params.lro_en) ? - priv->params.lro_wqe_sz : + rq->buff.wqe_sz = params->lro_en ? + params->lro_wqe_sz : MLX5E_SW2HW_MTU(priv->netdev->mtu); byte_count = rq->buff.wqe_sz; @@ -659,8 +655,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, } INIT_WORK(&rq->am.work, mlx5e_rx_am_work); - rq->am.mode = priv->params.rx_cq_period_mode; - + rq->am.mode = params->rx_cq_period_mode; rq->page_cache.head = 0; rq->page_cache.tail = 0; @@ -702,7 +697,8 @@ static void mlx5e_free_rq(struct mlx5e_rq *rq) mlx5_wq_destroy(&rq->wq_ctrl); } -static int mlx5e_create_rq(struct mlx5e_rq *rq, struct mlx5e_rq_param *param) +static int mlx5e_create_rq(struct mlx5e_rq *rq, + struct mlx5e_rq_param *param) { struct mlx5e_priv *priv = rq->priv; struct mlx5_core_dev *mdev = priv->mdev; @@ -726,7 +722,6 @@ static int mlx5e_create_rq(struct mlx5e_rq *rq, struct mlx5e_rq_param *param) MLX5_SET(rqc, rqc, cqn, rq->cq.mcq.cqn); MLX5_SET(rqc, rqc, state, MLX5_RQC_STATE_RST); - MLX5_SET(rqc, rqc, vsd, priv->params.vlan_strip_disable); MLX5_SET(wq, wq, log_wq_pg_sz, rq->wq_ctrl.buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT); MLX5_SET64(wq, wq, dbr_addr, rq->wq_ctrl.db.dma); @@ -812,16 +807,17 @@ static int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq) struct mlx5e_channel *c = rq->channel; struct mlx5e_priv *priv = c->priv; struct mlx5_wq_ll *wq = &rq->wq; + u16 min_wqes = mlx5_min_rx_wqes(rq->wq_type, mlx5_wq_ll_get_size(wq)); while (time_before(jiffies, exp_time)) { - if (wq->cur_sz >= priv->params.min_rx_wqes) + if (wq->cur_sz >= min_wqes) return 0; msleep(20); } netdev_warn(priv->netdev, "Failed to get min RX wqes on RQN[0x%x] wq cur_sz(%d) min_rx_wqes(%d)\n", - rq->rqn, wq->cur_sz, priv->params.min_rx_wqes); + rq->rqn, wq->cur_sz, min_wqes); return -ETIMEDOUT; } @@ -847,12 +843,13 @@ static void mlx5e_free_rx_descs(struct mlx5e_rq *rq) } static int mlx5e_open_rq(struct mlx5e_channel *c, + struct mlx5e_params *params, struct mlx5e_rq_param *param, struct mlx5e_rq *rq) { int err; - err = mlx5e_alloc_rq(c, param, rq); + err = mlx5e_alloc_rq(c, params, param, rq); if (err) return err; @@ -864,7 +861,7 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, if (err) goto err_destroy_rq; - if (param->am_enabled) + if (params->rx_am_enabled) set_bit(MLX5E_RQ_STATE_AM, &c->rq.state); return 0; @@ -924,6 +921,7 @@ static int mlx5e_alloc_xdpsq_db(struct mlx5e_xdpsq *sq, int numa) } static int mlx5e_alloc_xdpsq(struct mlx5e_channel *c, + struct mlx5e_params *params, struct mlx5e_sq_param *param, struct mlx5e_xdpsq *sq) { @@ -936,7 +934,7 @@ static int mlx5e_alloc_xdpsq(struct mlx5e_channel *c, sq->mkey_be = c->mkey_be; sq->channel = c; sq->uar_map = mdev->mlx5e_res.bfreg.map; - sq->min_inline_mode = param->min_inline_mode; + sq->min_inline_mode = params->tx_min_inline_mode; param->wq.db_numa_node = cpu_to_node(c->cpu); err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, &sq->wq, &sq->wq_ctrl); @@ -980,7 +978,6 @@ static int mlx5e_alloc_icosq_db(struct mlx5e_icosq *sq, int numa) } static int mlx5e_alloc_icosq(struct mlx5e_channel *c, - int tc, struct mlx5e_sq_param *param, struct mlx5e_icosq *sq) { @@ -1049,8 +1046,8 @@ static int mlx5e_alloc_txqsq_db(struct mlx5e_txqsq *sq, int numa) } static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, - int tc, int txq_ix, + struct mlx5e_params *params, struct mlx5e_sq_param *param, struct mlx5e_txqsq *sq) { @@ -1063,11 +1060,10 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, sq->tstamp = &priv->tstamp; sq->mkey_be = c->mkey_be; sq->channel = c; - sq->tc = tc; sq->txq_ix = txq_ix; sq->uar_map = mdev->mlx5e_res.bfreg.map; - sq->max_inline = param->max_inline; - sq->min_inline_mode = param->min_inline_mode; + sq->max_inline = params->tx_max_inline; + sq->min_inline_mode = params->tx_min_inline_mode; param->wq.db_numa_node = cpu_to_node(c->cpu); err = mlx5_wq_cyc_create(mdev, ¶m->wq, sqc_wq, &sq->wq, &sq->wq_ctrl); @@ -1221,6 +1217,7 @@ static int mlx5e_set_sq_maxrate(struct net_device *dev, static int mlx5e_open_txqsq(struct mlx5e_channel *c, int tc, int txq_ix, + struct mlx5e_params *params, struct mlx5e_sq_param *param, struct mlx5e_txqsq *sq) { @@ -1229,11 +1226,11 @@ static int mlx5e_open_txqsq(struct mlx5e_channel *c, u32 tx_rate; int err; - err = mlx5e_alloc_txqsq(c, tc, txq_ix, param, sq); + err = mlx5e_alloc_txqsq(c, txq_ix, params, param, sq); if (err) return err; - csp.tisn = priv->tisn[sq->tc]; + csp.tisn = priv->tisn[tc]; csp.tis_lst_sz = 1; csp.cqn = sq->cq.mcq.cqn; csp.wq_ctrl = &sq->wq_ctrl; @@ -1306,20 +1303,20 @@ static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) } static int mlx5e_open_icosq(struct mlx5e_channel *c, - int tc, + struct mlx5e_params *params, struct mlx5e_sq_param *param, struct mlx5e_icosq *sq) { struct mlx5e_create_sq_param csp = {}; int err; - err = mlx5e_alloc_icosq(c, tc, param, sq); + err = mlx5e_alloc_icosq(c, param, sq); if (err) return err; csp.cqn = sq->cq.mcq.cqn; csp.wq_ctrl = &sq->wq_ctrl; - csp.min_inline_mode = param->min_inline_mode; + csp.min_inline_mode = params->tx_min_inline_mode; set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); err = mlx5e_create_sq_rdy(c->priv, param, &csp, &sq->sqn); if (err) @@ -1346,6 +1343,7 @@ static void mlx5e_close_icosq(struct mlx5e_icosq *sq) } static int mlx5e_open_xdpsq(struct mlx5e_channel *c, + struct mlx5e_params *params, struct mlx5e_sq_param *param, struct mlx5e_xdpsq *sq) { @@ -1356,7 +1354,7 @@ static int mlx5e_open_xdpsq(struct mlx5e_channel *c, int err; int i; - err = mlx5e_alloc_xdpsq(c, param, sq); + err = mlx5e_alloc_xdpsq(c, params, param, sq); if (err) return err; @@ -1518,9 +1516,9 @@ static void mlx5e_destroy_cq(struct mlx5e_cq *cq) } static int mlx5e_open_cq(struct mlx5e_channel *c, + struct mlx5e_cq_moder moder, struct mlx5e_cq_param *param, - struct mlx5e_cq *cq, - struct mlx5e_cq_moder moderation) + struct mlx5e_cq *cq) { int err; struct mlx5e_priv *priv = c->priv; @@ -1535,9 +1533,7 @@ static int mlx5e_open_cq(struct mlx5e_channel *c, goto err_free_cq; if (MLX5_CAP_GEN(mdev, cq_moderation)) - mlx5_core_modify_cq_moderation(mdev, &cq->mcq, - moderation.usec, - moderation.pkts); + mlx5_core_modify_cq_moderation(mdev, &cq->mcq, moder.usec, moder.pkts); return 0; err_free_cq: @@ -1558,15 +1554,15 @@ static int mlx5e_get_cpu(struct mlx5e_priv *priv, int ix) } static int mlx5e_open_tx_cqs(struct mlx5e_channel *c, + struct mlx5e_params *params, struct mlx5e_channel_param *cparam) { - struct mlx5e_priv *priv = c->priv; int err; int tc; for (tc = 0; tc < c->num_tc; tc++) { - err = mlx5e_open_cq(c, &cparam->tx_cq, &c->sq[tc].cq, - priv->params.tx_cq_moderation); + err = mlx5e_open_cq(c, params->tx_cq_moderation, + &cparam->tx_cq, &c->sq[tc].cq); if (err) goto err_close_tx_cqs; } @@ -1589,15 +1585,16 @@ static void mlx5e_close_tx_cqs(struct mlx5e_channel *c) } static int mlx5e_open_sqs(struct mlx5e_channel *c, + struct mlx5e_params *params, struct mlx5e_channel_param *cparam) { int err; int tc; - for (tc = 0; tc < c->num_tc; tc++) { - int txq_ix = c->ix + tc * c->priv->channels.num; + for (tc = 0; tc < params->num_tc; tc++) { + int txq_ix = c->ix + tc * params->num_channels; - err = mlx5e_open_txqsq(c, tc, txq_ix, &cparam->sq, &c->sq[tc]); + err = mlx5e_open_txqsq(c, tc, txq_ix, params, &cparam->sq, &c->sq[tc]); if (err) goto err_close_sqs; } @@ -1705,12 +1702,12 @@ static inline int mlx5e_get_max_num_channels(struct mlx5_core_dev *mdev) } static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, + struct mlx5e_params *params, struct mlx5e_channel_param *cparam, struct mlx5e_channel **cp) { - struct mlx5e_cq_moder icosq_cq_moder = {0, 0}; + struct mlx5e_cq_moder icocq_moder = {0, 0}; struct net_device *netdev = priv->netdev; - struct mlx5e_cq_moder rx_cq_profile; int cpu = mlx5e_get_cpu(priv, ix); struct mlx5e_channel *c; int err; @@ -1725,50 +1722,44 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, c->pdev = &priv->mdev->pdev->dev; c->netdev = priv->netdev; c->mkey_be = cpu_to_be32(priv->mdev->mlx5e_res.mkey.key); - c->num_tc = priv->params.num_tc; - c->xdp = !!priv->xdp_prog; - - if (priv->params.rx_am_enabled) - rx_cq_profile = mlx5e_am_get_def_profile(priv->params.rx_cq_period_mode); - else - rx_cq_profile = priv->params.rx_cq_moderation; + c->num_tc = params->num_tc; + c->xdp = !!params->xdp_prog; netif_napi_add(netdev, &c->napi, mlx5e_napi_poll, 64); - err = mlx5e_open_cq(c, &cparam->icosq_cq, &c->icosq.cq, icosq_cq_moder); + err = mlx5e_open_cq(c, icocq_moder, &cparam->icosq_cq, &c->icosq.cq); if (err) goto err_napi_del; - err = mlx5e_open_tx_cqs(c, cparam); + err = mlx5e_open_tx_cqs(c, params, cparam); if (err) goto err_close_icosq_cq; - err = mlx5e_open_cq(c, &cparam->rx_cq, &c->rq.cq, - rx_cq_profile); + err = mlx5e_open_cq(c, params->rx_cq_moderation, &cparam->rx_cq, &c->rq.cq); if (err) goto err_close_tx_cqs; /* XDP SQ CQ params are same as normal TXQ sq CQ params */ - err = c->xdp ? mlx5e_open_cq(c, &cparam->tx_cq, &c->rq.xdpsq.cq, - priv->params.tx_cq_moderation) : 0; + err = c->xdp ? mlx5e_open_cq(c, params->tx_cq_moderation, + &cparam->tx_cq, &c->rq.xdpsq.cq) : 0; if (err) goto err_close_rx_cq; napi_enable(&c->napi); - err = mlx5e_open_icosq(c, 0, &cparam->icosq, &c->icosq); + err = mlx5e_open_icosq(c, params, &cparam->icosq, &c->icosq); if (err) goto err_disable_napi; - err = mlx5e_open_sqs(c, cparam); + err = mlx5e_open_sqs(c, params, cparam); if (err) goto err_close_icosq; - err = c->xdp ? mlx5e_open_xdpsq(c, &cparam->xdp_sq, &c->rq.xdpsq) : 0; + err = c->xdp ? mlx5e_open_xdpsq(c, params, &cparam->xdp_sq, &c->rq.xdpsq) : 0; if (err) goto err_close_sqs; - err = mlx5e_open_rq(c, &cparam->rq, &c->rq); + err = mlx5e_open_rq(c, params, &cparam->rq, &c->rq); if (err) goto err_close_xdp_sq; @@ -1844,17 +1835,16 @@ static void mlx5e_close_channel(struct mlx5e_channel *c) } static void mlx5e_build_rq_param(struct mlx5e_priv *priv, + struct mlx5e_params *params, struct mlx5e_rq_param *param) { void *rqc = param->rqc; void *wq = MLX5_ADDR_OF(rqc, rqc, wq); - switch (priv->params.rq_wq_type) { + switch (params->rq_wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: - MLX5_SET(wq, wq, log_wqe_num_of_strides, - priv->params.mpwqe_log_num_strides - 9); - MLX5_SET(wq, wq, log_wqe_stride_size, - priv->params.mpwqe_log_stride_sz - 6); + MLX5_SET(wq, wq, log_wqe_num_of_strides, params->mpwqe_log_num_strides - 9); + MLX5_SET(wq, wq, log_wqe_stride_size, params->mpwqe_log_stride_sz - 6); MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ); break; default: /* MLX5_WQ_TYPE_LINKED_LIST */ @@ -1863,14 +1853,13 @@ static void mlx5e_build_rq_param(struct mlx5e_priv *priv, MLX5_SET(wq, wq, end_padding_mode, MLX5_WQ_END_PAD_MODE_ALIGN); MLX5_SET(wq, wq, log_wq_stride, ilog2(sizeof(struct mlx5e_rx_wqe))); - MLX5_SET(wq, wq, log_wq_sz, priv->params.log_rq_size); + MLX5_SET(wq, wq, log_wq_sz, params->log_rq_size); MLX5_SET(wq, wq, pd, priv->mdev->mlx5e_res.pdn); MLX5_SET(rqc, rqc, counter_set_id, priv->q_counter); + MLX5_SET(rqc, rqc, vsd, params->vlan_strip_disable); param->wq.buf_numa_node = dev_to_node(&priv->mdev->pdev->dev); param->wq.linear = 1; - - param->am_enabled = priv->params.rx_am_enabled; } static void mlx5e_build_drop_rq_param(struct mlx5e_rq_param *param) @@ -1895,16 +1884,14 @@ static void mlx5e_build_sq_param_common(struct mlx5e_priv *priv, } static void mlx5e_build_sq_param(struct mlx5e_priv *priv, + struct mlx5e_params *params, struct mlx5e_sq_param *param) { void *sqc = param->sqc; void *wq = MLX5_ADDR_OF(sqc, sqc, wq); mlx5e_build_sq_param_common(priv, param); - MLX5_SET(wq, wq, log_wq_sz, priv->params.log_sq_size); - - param->max_inline = priv->params.tx_max_inline; - param->min_inline_mode = priv->params.tx_min_inline_mode; + MLX5_SET(wq, wq, log_wq_sz, params->log_sq_size); } static void mlx5e_build_common_cq_param(struct mlx5e_priv *priv, @@ -1916,37 +1903,40 @@ static void mlx5e_build_common_cq_param(struct mlx5e_priv *priv, } static void mlx5e_build_rx_cq_param(struct mlx5e_priv *priv, + struct mlx5e_params *params, struct mlx5e_cq_param *param) { void *cqc = param->cqc; u8 log_cq_size; - switch (priv->params.rq_wq_type) { + switch (params->rq_wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: - log_cq_size = priv->params.log_rq_size + - priv->params.mpwqe_log_num_strides; + log_cq_size = params->log_rq_size + params->mpwqe_log_num_strides; break; default: /* MLX5_WQ_TYPE_LINKED_LIST */ - log_cq_size = priv->params.log_rq_size; + log_cq_size = params->log_rq_size; } MLX5_SET(cqc, cqc, log_cq_size, log_cq_size); - if (MLX5E_GET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS)) { + if (MLX5E_GET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS)) { MLX5_SET(cqc, cqc, mini_cqe_res_format, MLX5_CQE_FORMAT_CSUM); MLX5_SET(cqc, cqc, cqe_comp_en, 1); } mlx5e_build_common_cq_param(priv, param); - param->cq_period_mode = priv->params.rx_cq_period_mode; + if (params->rx_am_enabled) + params->rx_cq_moderation = + mlx5e_am_get_def_profile(params->rx_cq_period_mode); } static void mlx5e_build_tx_cq_param(struct mlx5e_priv *priv, + struct mlx5e_params *params, struct mlx5e_cq_param *param) { void *cqc = param->cqc; - MLX5_SET(cqc, cqc, log_cq_size, priv->params.log_sq_size); + MLX5_SET(cqc, cqc, log_cq_size, params->log_sq_size); mlx5e_build_common_cq_param(priv, param); @@ -1954,8 +1944,8 @@ static void mlx5e_build_tx_cq_param(struct mlx5e_priv *priv, } static void mlx5e_build_ico_cq_param(struct mlx5e_priv *priv, - struct mlx5e_cq_param *param, - u8 log_wq_size) + u8 log_wq_size, + struct mlx5e_cq_param *param) { void *cqc = param->cqc; @@ -1967,8 +1957,8 @@ static void mlx5e_build_ico_cq_param(struct mlx5e_priv *priv, } static void mlx5e_build_icosq_param(struct mlx5e_priv *priv, - struct mlx5e_sq_param *param, - u8 log_wq_size) + u8 log_wq_size, + struct mlx5e_sq_param *param) { void *sqc = param->sqc; void *wq = MLX5_ADDR_OF(sqc, sqc, wq); @@ -1980,47 +1970,48 @@ static void mlx5e_build_icosq_param(struct mlx5e_priv *priv, } static void mlx5e_build_xdpsq_param(struct mlx5e_priv *priv, + struct mlx5e_params *params, struct mlx5e_sq_param *param) { void *sqc = param->sqc; void *wq = MLX5_ADDR_OF(sqc, sqc, wq); mlx5e_build_sq_param_common(priv, param); - MLX5_SET(wq, wq, log_wq_sz, priv->params.log_sq_size); - - param->max_inline = priv->params.tx_max_inline; - param->min_inline_mode = priv->params.tx_min_inline_mode; + MLX5_SET(wq, wq, log_wq_sz, params->log_sq_size); } -static void mlx5e_build_channel_param(struct mlx5e_priv *priv, struct mlx5e_channel_param *cparam) +static void mlx5e_build_channel_param(struct mlx5e_priv *priv, + struct mlx5e_params *params, + struct mlx5e_channel_param *cparam) { u8 icosq_log_wq_sz = MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE; - mlx5e_build_rq_param(priv, &cparam->rq); - mlx5e_build_sq_param(priv, &cparam->sq); - mlx5e_build_xdpsq_param(priv, &cparam->xdp_sq); - mlx5e_build_icosq_param(priv, &cparam->icosq, icosq_log_wq_sz); - mlx5e_build_rx_cq_param(priv, &cparam->rx_cq); - mlx5e_build_tx_cq_param(priv, &cparam->tx_cq); - mlx5e_build_ico_cq_param(priv, &cparam->icosq_cq, icosq_log_wq_sz); + mlx5e_build_rq_param(priv, params, &cparam->rq); + mlx5e_build_sq_param(priv, params, &cparam->sq); + mlx5e_build_xdpsq_param(priv, params, &cparam->xdp_sq); + mlx5e_build_icosq_param(priv, icosq_log_wq_sz, &cparam->icosq); + mlx5e_build_rx_cq_param(priv, params, &cparam->rx_cq); + mlx5e_build_tx_cq_param(priv, params, &cparam->tx_cq); + mlx5e_build_ico_cq_param(priv, icosq_log_wq_sz, &cparam->icosq_cq); } -static int mlx5e_open_channels(struct mlx5e_priv *priv, struct mlx5e_channels *chs) +static int mlx5e_open_channels(struct mlx5e_priv *priv, + struct mlx5e_channels *chs) { struct mlx5e_channel_param *cparam; int err = -ENOMEM; int i; - chs->num = priv->params.num_channels; + chs->num = chs->params.num_channels; chs->c = kcalloc(chs->num, sizeof(struct mlx5e_channel *), GFP_KERNEL); cparam = kzalloc(sizeof(struct mlx5e_channel_param), GFP_KERNEL); if (!chs->c || !cparam) goto err_free; - mlx5e_build_channel_param(priv, cparam); + mlx5e_build_channel_param(priv, &chs->params, cparam); for (i = 0; i < chs->num; i++) { - err = mlx5e_open_channel(priv, i, cparam, &chs->c[i]); + err = mlx5e_open_channel(priv, i, &chs->params, cparam, &chs->c[i]); if (err) goto err_close_channels; } @@ -2178,7 +2169,7 @@ static void mlx5e_fill_rqt_rqns(struct mlx5e_priv *priv, int sz, if (rrp.rss.hfunc == ETH_RSS_HASH_XOR) ix = mlx5e_bits_invert(i, ilog2(sz)); - ix = priv->params.indirection_rqt[ix]; + ix = priv->channels.params.indirection_rqt[ix]; rqn = rrp.rss.channels->c[ix]->rq.rqn; } else { rqn = rrp.rqn; @@ -2257,7 +2248,7 @@ static void mlx5e_redirect_rqts_to_channels(struct mlx5e_priv *priv, struct mlx5e_redirect_rqt_param rrp = { .is_rss = true, .rss.channels = chs, - .rss.hfunc = priv->params.rss_hfunc + .rss.hfunc = chs->params.rss_hfunc }; mlx5e_redirect_rqts(priv, rrp); @@ -2273,9 +2264,9 @@ static void mlx5e_redirect_rqts_to_drop(struct mlx5e_priv *priv) mlx5e_redirect_rqts(priv, drop_rrp); } -static void mlx5e_build_tir_ctx_lro(void *tirc, struct mlx5e_priv *priv) +static void mlx5e_build_tir_ctx_lro(struct mlx5e_params *params, void *tirc) { - if (!priv->params.lro_en) + if (!params->lro_en) return; #define ROUGH_MAX_L2_L3_HDR_SZ 256 @@ -2284,13 +2275,13 @@ static void mlx5e_build_tir_ctx_lro(void *tirc, struct mlx5e_priv *priv) MLX5_TIRC_LRO_ENABLE_MASK_IPV4_LRO | MLX5_TIRC_LRO_ENABLE_MASK_IPV6_LRO); MLX5_SET(tirc, tirc, lro_max_ip_payload_size, - (priv->params.lro_wqe_sz - - ROUGH_MAX_L2_L3_HDR_SZ) >> 8); - MLX5_SET(tirc, tirc, lro_timeout_period_usecs, priv->params.lro_timeout); + (params->lro_wqe_sz - ROUGH_MAX_L2_L3_HDR_SZ) >> 8); + MLX5_SET(tirc, tirc, lro_timeout_period_usecs, params->lro_timeout); } -void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_priv *priv, void *tirc, - enum mlx5e_traffic_types tt) +void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_params *params, + enum mlx5e_traffic_types tt, + void *tirc) { void *hfso = MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_outer); @@ -2306,16 +2297,15 @@ void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_priv *priv, void *tirc, MLX5_HASH_FIELD_SEL_DST_IP |\ MLX5_HASH_FIELD_SEL_IPSEC_SPI) - MLX5_SET(tirc, tirc, rx_hash_fn, - mlx5e_rx_hash_fn(priv->params.rss_hfunc)); - if (priv->params.rss_hfunc == ETH_RSS_HASH_TOP) { + MLX5_SET(tirc, tirc, rx_hash_fn, mlx5e_rx_hash_fn(params->rss_hfunc)); + if (params->rss_hfunc == ETH_RSS_HASH_TOP) { void *rss_key = MLX5_ADDR_OF(tirc, tirc, rx_hash_toeplitz_key); size_t len = MLX5_FLD_SZ_BYTES(tirc, rx_hash_toeplitz_key); MLX5_SET(tirc, tirc, rx_hash_symmetric, 1); - memcpy(rss_key, priv->params.toeplitz_hash_key, len); + memcpy(rss_key, params->toeplitz_hash_key, len); } switch (tt) { @@ -2420,7 +2410,7 @@ static int mlx5e_modify_tirs_lro(struct mlx5e_priv *priv) MLX5_SET(modify_tir_in, in, bitmask.lro, 1); tirc = MLX5_ADDR_OF(modify_tir_in, in, ctx); - mlx5e_build_tir_ctx_lro(tirc, priv); + mlx5e_build_tir_ctx_lro(&priv->channels.params, tirc); for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) { err = mlx5_core_modify_tir(mdev, priv->indir_tir[tt].tirn, in, @@ -2492,8 +2482,8 @@ static int mlx5e_set_dev_port_mtu(struct net_device *netdev) static void mlx5e_netdev_set_tcs(struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); - int nch = priv->params.num_channels; - int ntc = priv->params.num_tc; + int nch = priv->channels.params.num_channels; + int ntc = priv->channels.params.num_tc; int tc; netdev_reset_tc(netdev); @@ -2558,9 +2548,9 @@ int mlx5e_open_locked(struct net_device *netdev) mlx5e_netdev_set_tcs(netdev); - num_txqs = priv->params.num_channels * priv->params.num_tc; + num_txqs = priv->channels.params.num_channels * priv->channels.params.num_tc; netif_set_real_num_tx_queues(netdev, num_txqs); - netif_set_real_num_rx_queues(netdev, priv->params.num_channels); + netif_set_real_num_rx_queues(netdev, priv->channels.params.num_channels); err = mlx5e_open_channels(priv, &priv->channels); if (err) @@ -2792,24 +2782,24 @@ void mlx5e_cleanup_nic_tx(struct mlx5e_priv *priv) mlx5e_destroy_tis(priv, tc); } -static void mlx5e_build_indir_tir_ctx(struct mlx5e_priv *priv, u32 *tirc, - enum mlx5e_traffic_types tt) +static void mlx5e_build_indir_tir_ctx(struct mlx5e_priv *priv, + enum mlx5e_traffic_types tt, + u32 *tirc) { MLX5_SET(tirc, tirc, transport_domain, priv->mdev->mlx5e_res.td.tdn); - mlx5e_build_tir_ctx_lro(tirc, priv); + mlx5e_build_tir_ctx_lro(&priv->channels.params, tirc); MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT); MLX5_SET(tirc, tirc, indirect_table, priv->indir_rqt.rqtn); - mlx5e_build_indir_tir_ctx_hash(priv, tirc, tt); + mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc); } -static void mlx5e_build_direct_tir_ctx(struct mlx5e_priv *priv, u32 *tirc, - u32 rqtn) +static void mlx5e_build_direct_tir_ctx(struct mlx5e_priv *priv, u32 rqtn, u32 *tirc) { MLX5_SET(tirc, tirc, transport_domain, priv->mdev->mlx5e_res.td.tdn); - mlx5e_build_tir_ctx_lro(tirc, priv); + mlx5e_build_tir_ctx_lro(&priv->channels.params, tirc); MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT); MLX5_SET(tirc, tirc, indirect_table, rqtn); @@ -2834,7 +2824,7 @@ static int mlx5e_create_indirect_tirs(struct mlx5e_priv *priv) memset(in, 0, inlen); tir = &priv->indir_tir[tt]; tirc = MLX5_ADDR_OF(create_tir_in, in, ctx); - mlx5e_build_indir_tir_ctx(priv, tirc, tt); + mlx5e_build_indir_tir_ctx(priv, tt, tirc); err = mlx5e_create_tir(priv->mdev, tir, in, inlen); if (err) goto err_destroy_tirs; @@ -2872,8 +2862,7 @@ int mlx5e_create_direct_tirs(struct mlx5e_priv *priv) memset(in, 0, inlen); tir = &priv->direct_tir[ix]; tirc = MLX5_ADDR_OF(create_tir_in, in, ctx); - mlx5e_build_direct_tir_ctx(priv, tirc, - priv->direct_tir[ix].rqt.rqtn); + mlx5e_build_direct_tir_ctx(priv, priv->direct_tir[ix].rqt.rqtn, tirc); err = mlx5e_create_tir(priv->mdev, tir, in, inlen); if (err) goto err_destroy_ch_tirs; @@ -2938,7 +2927,7 @@ static int mlx5e_setup_tc(struct net_device *netdev, u8 tc) if (was_opened) mlx5e_close_locked(priv->netdev); - priv->params.num_tc = tc ? tc : 1; + priv->channels.params.num_tc = tc ? tc : 1; if (was_opened) err = mlx5e_open_locked(priv->netdev); @@ -3066,17 +3055,17 @@ static int set_feature_lro(struct net_device *netdev, bool enable) mutex_lock(&priv->state_lock); - if (was_opened && (priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST)) + if (was_opened && (priv->channels.params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST)) mlx5e_close_locked(priv->netdev); - priv->params.lro_en = enable; + priv->channels.params.lro_en = enable; err = mlx5e_modify_tirs_lro(priv); if (err) { netdev_err(netdev, "lro modify failed, %d\n", err); - priv->params.lro_en = !enable; + priv->channels.params.lro_en = !enable; } - if (was_opened && (priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST)) + if (was_opened && (priv->channels.params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST)) mlx5e_open_locked(priv->netdev); mutex_unlock(&priv->state_lock); @@ -3124,13 +3113,13 @@ static int set_feature_rx_vlan(struct net_device *netdev, bool enable) mutex_lock(&priv->state_lock); - priv->params.vlan_strip_disable = !enable; + priv->channels.params.vlan_strip_disable = !enable; if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) goto unlock; err = mlx5e_modify_channels_vsd(&priv->channels, !enable); if (err) - priv->params.vlan_strip_disable = enable; + priv->channels.params.vlan_strip_disable = enable; unlock: mutex_unlock(&priv->state_lock); @@ -3209,8 +3198,8 @@ static int mlx5e_change_mtu(struct net_device *netdev, int new_mtu) mutex_lock(&priv->state_lock); - reset = !priv->params.lro_en && - (priv->params.rq_wq_type != + reset = !priv->channels.params.lro_en && + (priv->channels.params.rq_wq_type != MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ); was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); @@ -3429,7 +3418,7 @@ static void mlx5e_tx_timeout(struct net_device *dev) netdev_err(dev, "TX timeout detected\n"); - for (i = 0; i < priv->channels.num * priv->params.num_tc; i++) { + for (i = 0; i < priv->channels.num * priv->channels.params.num_tc; i++) { struct mlx5e_txqsq *sq = priv->txq2sq[i]; if (!netif_xmit_stopped(netdev_get_tx_queue(dev, i))) @@ -3462,7 +3451,7 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog) was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); /* no need for full reset when exchanging programs */ - reset = (!priv->xdp_prog || !prog); + reset = (!priv->channels.params.xdp_prog || !prog); if (was_opened && reset) mlx5e_close_locked(netdev); @@ -3470,7 +3459,7 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog) /* num_channels is invariant here, so we can take the * batched reference right upfront. */ - prog = bpf_prog_add(prog, priv->params.num_channels); + prog = bpf_prog_add(prog, priv->channels.num); if (IS_ERR(prog)) { err = PTR_ERR(prog); goto unlock; @@ -3480,12 +3469,12 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog) /* exchange programs, extra prog reference we got from caller * as long as we don't fail from this point onwards. */ - old_prog = xchg(&priv->xdp_prog, prog); + old_prog = xchg(&priv->channels.params.xdp_prog, prog); if (old_prog) bpf_prog_put(old_prog); if (reset) /* change RQ type according to priv->xdp_prog */ - mlx5e_set_rq_priv_params(priv); + mlx5e_set_rq_params(priv->mdev, &priv->channels.params); if (was_opened && reset) mlx5e_open_locked(netdev); @@ -3523,7 +3512,7 @@ static bool mlx5e_xdp_attached(struct net_device *dev) { struct mlx5e_priv *priv = netdev_priv(dev); - return !!priv->xdp_prog; + return !!priv->channels.params.xdp_prog; } static int mlx5e_xdp(struct net_device *dev, struct netdev_xdp *xdp) @@ -3720,6 +3709,9 @@ void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode) if (cq_period_mode == MLX5_CQ_PERIOD_MODE_START_FROM_CQE) params->rx_cq_moderation.usec = MLX5E_PARAMS_DEFAULT_RX_CQ_MODERATION_USEC_FROM_CQE; + + MLX5E_SET_PFLAG(params, MLX5E_PFLAG_RX_CQE_BASED_MODER, + params->rx_cq_period_mode == MLX5_CQ_PERIOD_MODE_START_FROM_CQE); } u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout) @@ -3734,75 +3726,79 @@ u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout) return MLX5_CAP_ETH(mdev, lro_timer_supported_periods[i]); } -static void mlx5e_build_nic_netdev_priv(struct mlx5_core_dev *mdev, - struct net_device *netdev, - const struct mlx5e_profile *profile, - void *ppriv) +static void mlx5e_build_nic_params(struct mlx5_core_dev *mdev, + struct mlx5e_params *params, + u16 max_channels) { - struct mlx5e_priv *priv = netdev_priv(netdev); + u8 cq_period_mode = 0; u32 link_speed = 0; u32 pci_bw = 0; - u8 cq_period_mode = MLX5_CAP_GEN(mdev, cq_period_start_from_cqe) ? - MLX5_CQ_PERIOD_MODE_START_FROM_CQE : - MLX5_CQ_PERIOD_MODE_START_FROM_EQE; - priv->mdev = mdev; - priv->netdev = netdev; - priv->params.num_channels = profile->max_nch(mdev); - priv->profile = profile; - priv->ppriv = ppriv; + params->num_channels = max_channels; + params->num_tc = 1; - priv->params.lro_timeout = - mlx5e_choose_lro_timeout(mdev, MLX5E_DEFAULT_LRO_TIMEOUT); - - priv->params.log_sq_size = is_kdump_kernel() ? + /* SQ */ + params->log_sq_size = is_kdump_kernel() ? MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE : MLX5E_PARAMS_DEFAULT_LOG_SQ_SIZE; /* set CQE compression */ - priv->params.rx_cqe_compress_def = false; + params->rx_cqe_compress_def = false; if (MLX5_CAP_GEN(mdev, cqe_compression) && - MLX5_CAP_GEN(mdev, vport_group_manager)) { + MLX5_CAP_GEN(mdev, vport_group_manager)) { mlx5e_get_max_linkspeed(mdev, &link_speed); mlx5e_get_pci_bw(mdev, &pci_bw); mlx5_core_dbg(mdev, "Max link speed = %d, PCI BW = %d\n", - link_speed, pci_bw); - priv->params.rx_cqe_compress_def = - cqe_compress_heuristic(link_speed, pci_bw); + link_speed, pci_bw); + params->rx_cqe_compress_def = cqe_compress_heuristic(link_speed, pci_bw); } - - MLX5E_SET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS, - priv->params.rx_cqe_compress_def); - - mlx5e_set_rq_priv_params(priv); - if (priv->params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) - priv->params.lro_en = true; - - priv->params.rx_am_enabled = MLX5_CAP_GEN(mdev, cq_moderation); - mlx5e_set_rx_cq_mode_params(&priv->params, cq_period_mode); - - priv->params.tx_cq_moderation.usec = - MLX5E_PARAMS_DEFAULT_TX_CQ_MODERATION_USEC; - priv->params.tx_cq_moderation.pkts = - MLX5E_PARAMS_DEFAULT_TX_CQ_MODERATION_PKTS; - priv->params.tx_max_inline = mlx5e_get_max_inline_cap(mdev); - mlx5_query_min_inline(mdev, &priv->params.tx_min_inline_mode); - if (priv->params.tx_min_inline_mode == MLX5_INLINE_MODE_NONE && + MLX5E_SET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS, params->rx_cqe_compress_def); + + /* RQ */ + mlx5e_set_rq_params(mdev, params); + + /* HW LRO */ + if (params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) + params->lro_en = true; + params->lro_timeout = mlx5e_choose_lro_timeout(mdev, MLX5E_DEFAULT_LRO_TIMEOUT); + + /* CQ moderation params */ + cq_period_mode = MLX5_CAP_GEN(mdev, cq_period_start_from_cqe) ? + MLX5_CQ_PERIOD_MODE_START_FROM_CQE : + MLX5_CQ_PERIOD_MODE_START_FROM_EQE; + params->rx_am_enabled = MLX5_CAP_GEN(mdev, cq_moderation); + mlx5e_set_rx_cq_mode_params(params, cq_period_mode); + + params->tx_cq_moderation.usec = MLX5E_PARAMS_DEFAULT_TX_CQ_MODERATION_USEC; + params->tx_cq_moderation.pkts = MLX5E_PARAMS_DEFAULT_TX_CQ_MODERATION_PKTS; + + /* TX inline */ + params->tx_max_inline = mlx5e_get_max_inline_cap(mdev); + mlx5_query_min_inline(mdev, ¶ms->tx_min_inline_mode); + if (params->tx_min_inline_mode == MLX5_INLINE_MODE_NONE && !MLX5_CAP_ETH(mdev, wqe_vlan_insert)) - priv->params.tx_min_inline_mode = MLX5_INLINE_MODE_L2; + params->tx_min_inline_mode = MLX5_INLINE_MODE_L2; - priv->params.num_tc = 1; - priv->params.rss_hfunc = ETH_RSS_HASH_XOR; + /* RSS */ + params->rss_hfunc = ETH_RSS_HASH_XOR; + netdev_rss_key_fill(params->toeplitz_hash_key, sizeof(params->toeplitz_hash_key)); + mlx5e_build_default_indir_rqt(mdev, params->indirection_rqt, + MLX5E_INDIR_RQT_SIZE, max_channels); +} - netdev_rss_key_fill(priv->params.toeplitz_hash_key, - sizeof(priv->params.toeplitz_hash_key)); +static void mlx5e_build_nic_netdev_priv(struct mlx5_core_dev *mdev, + struct net_device *netdev, + const struct mlx5e_profile *profile, + void *ppriv) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); - mlx5e_build_default_indir_rqt(mdev, priv->params.indirection_rqt, - MLX5E_INDIR_RQT_SIZE, profile->max_nch(mdev)); + priv->mdev = mdev; + priv->netdev = netdev; + priv->profile = profile; + priv->ppriv = ppriv; - /* Initialize pflags */ - MLX5E_SET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_BASED_MODER, - priv->params.rx_cq_period_mode == MLX5_CQ_PERIOD_MODE_START_FROM_CQE); + mlx5e_build_nic_params(mdev, &priv->channels.params, profile->max_nch(mdev)); mutex_init(&priv->state_lock); @@ -3888,7 +3884,7 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) netdev->hw_features |= NETIF_F_RXALL; netdev->features = netdev->hw_features; - if (!priv->params.lro_en) + if (!priv->channels.params.lro_en) netdev->features &= ~NETIF_F_LRO; if (fcs_enabled) @@ -3953,8 +3949,8 @@ static void mlx5e_nic_cleanup(struct mlx5e_priv *priv) { mlx5e_vxlan_cleanup(priv); - if (priv->xdp_prog) - bpf_prog_put(priv->xdp_prog); + if (priv->channels.params.xdp_prog) + bpf_prog_put(priv->channels.params.xdp_prog); } static int mlx5e_init_nic_rx(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 0515b7f7d11f..d277c1979b2a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -110,7 +110,7 @@ static void mlx5e_rep_update_sw_counters(struct mlx5e_priv *priv) s->rx_packets += rq_stats->packets; s->rx_bytes += rq_stats->bytes; - for (j = 0; j < priv->params.num_tc; j++) { + for (j = 0; j < priv->channels.params.num_tc; j++) { sq_stats = &c->sq[j].stats; s->tx_packets += sq_stats->packets; @@ -192,7 +192,7 @@ int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) int n, tc, err, num_sqs = 0; u16 *sqs; - sqs = kcalloc(priv->channels.num * priv->params.num_tc, sizeof(u16), GFP_KERNEL); + sqs = kcalloc(priv->channels.num * priv->channels.params.num_tc, sizeof(u16), GFP_KERNEL); if (!sqs) return -ENOMEM; @@ -399,42 +399,23 @@ static const struct net_device_ops mlx5e_netdev_ops_rep = { .ndo_get_offload_stats = mlx5e_get_offload_stats, }; -static void mlx5e_build_rep_netdev_priv(struct mlx5_core_dev *mdev, - struct net_device *netdev, - const struct mlx5e_profile *profile, - void *ppriv) +static void mlx5e_build_rep_params(struct mlx5_core_dev *mdev, + struct mlx5e_params *params) { - struct mlx5e_priv *priv = netdev_priv(netdev); u8 cq_period_mode = MLX5_CAP_GEN(mdev, cq_period_start_from_cqe) ? MLX5_CQ_PERIOD_MODE_START_FROM_CQE : MLX5_CQ_PERIOD_MODE_START_FROM_EQE; - priv->params.log_sq_size = - MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE; - priv->params.rq_wq_type = MLX5_WQ_TYPE_LINKED_LIST; - priv->params.log_rq_size = MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE; - - priv->params.min_rx_wqes = mlx5_min_rx_wqes(priv->params.rq_wq_type, - BIT(priv->params.log_rq_size)); - - priv->params.rx_am_enabled = MLX5_CAP_GEN(mdev, cq_moderation); - mlx5e_set_rx_cq_mode_params(&priv->params, cq_period_mode); - - priv->params.tx_max_inline = mlx5e_get_max_inline_cap(mdev); - priv->params.num_tc = 1; - - priv->params.lro_wqe_sz = - MLX5E_PARAMS_DEFAULT_LRO_WQE_SZ; - - priv->mdev = mdev; - priv->netdev = netdev; - priv->params.num_channels = profile->max_nch(mdev); - priv->profile = profile; - priv->ppriv = ppriv; + params->log_sq_size = MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE; + params->rq_wq_type = MLX5_WQ_TYPE_LINKED_LIST; + params->log_rq_size = MLX5E_PARAMS_MINIMUM_LOG_RQ_SIZE; - mutex_init(&priv->state_lock); + params->rx_am_enabled = MLX5_CAP_GEN(mdev, cq_moderation); + mlx5e_set_rx_cq_mode_params(params, cq_period_mode); - INIT_DELAYED_WORK(&priv->update_stats_work, mlx5e_update_stats_work); + params->tx_max_inline = mlx5e_get_max_inline_cap(mdev); + params->num_tc = 1; + params->lro_wqe_sz = MLX5E_PARAMS_DEFAULT_LRO_WQE_SZ; } static void mlx5e_build_rep_netdev(struct net_device *netdev) @@ -460,7 +441,19 @@ static void mlx5e_init_rep(struct mlx5_core_dev *mdev, const struct mlx5e_profile *profile, void *ppriv) { - mlx5e_build_rep_netdev_priv(mdev, netdev, profile, ppriv); + struct mlx5e_priv *priv = netdev_priv(netdev); + + priv->mdev = mdev; + priv->netdev = netdev; + priv->profile = profile; + priv->ppriv = ppriv; + + mutex_init(&priv->state_lock); + + INIT_DELAYED_WORK(&priv->update_stats_work, mlx5e_update_stats_work); + + priv->channels.params.num_channels = profile->max_nch(mdev); + mlx5e_build_rep_params(mdev, &priv->channels.params); mlx5e_build_rep_netdev(netdev); } @@ -505,7 +498,7 @@ err_del_flow_rule: err_destroy_direct_tirs: mlx5e_destroy_direct_tirs(priv); err_destroy_direct_rqts: - for (i = 0; i < priv->params.num_channels; i++) + for (i = 0; i < priv->channels.params.num_channels; i++) mlx5e_destroy_rqt(priv, &priv->direct_tir[i].rqt); return err; } @@ -518,7 +511,7 @@ static void mlx5e_cleanup_rep_rx(struct mlx5e_priv *priv) mlx5e_tc_cleanup(priv); mlx5_del_flow_rules(rep->vport_rx_rule); mlx5e_destroy_direct_tirs(priv); - for (i = 0; i < priv->params.num_channels; i++) + for (i = 0; i < priv->channels.params.num_channels; i++) mlx5e_destroy_rqt(priv, &priv->direct_tir[i].rqt); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 3ecbe8c2d5e3..0a40c42e1335 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -163,19 +163,19 @@ void mlx5e_modify_rx_cqe_compression_locked(struct mlx5e_priv *priv, bool val) if (!MLX5_CAP_GEN(priv->mdev, cqe_compression)) return; - if (MLX5E_GET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS) == val) + if (MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_RX_CQE_COMPRESS) == val) return; was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); if (was_opened) mlx5e_close_locked(priv->netdev); - MLX5E_SET_PFLAG(priv, MLX5E_PFLAG_RX_CQE_COMPRESS, val); - mlx5e_set_rq_type_params(priv, priv->params.rq_wq_type); + MLX5E_SET_PFLAG(&priv->channels.params, MLX5E_PFLAG_RX_CQE_COMPRESS, val); + mlx5e_set_rq_type_params(priv->mdev, &priv->channels.params, + priv->channels.params.rq_wq_type); if (was_opened) mlx5e_open_locked(priv->netdev); - } #define RQ_PAGE_SIZE(rq) ((1 << rq->buff.page_order) << PAGE_SHIFT) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 42743b114bcf..5bbc313e70c5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -88,6 +88,7 @@ u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb, { struct mlx5e_priv *priv = netdev_priv(dev); int channel_ix = fallback(dev, skb); + u16 num_channels; int up = 0; if (!netdev_get_num_tc(dev)) @@ -99,9 +100,9 @@ u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb, /* channel_ix can be larger than num_channels since * dev->num_real_tx_queues = num_channels * num_tc */ - if (channel_ix >= priv->params.num_channels) - channel_ix = reciprocal_scale(channel_ix, - priv->params.num_channels); + num_channels = priv->channels.params.num_channels; + if (channel_ix >= num_channels) + channel_ix = reciprocal_scale(channel_ix, num_channels); return priv->channel_tc2txq[channel_ix][up]; } -- cgit From a43b25daef78610ccef025d59f82ce8e0c42ab6c Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 14 Mar 2017 19:43:52 +0200 Subject: net/mlx5e: CQ and RQ don't need priv pointer Remove mlx5e_priv pointer from CQ and RQ structs, it was needed only to access mdev pointer from priv pointer. Instead we now pass mdev where needed. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 38 +++-- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 181 +++++++++------------ drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 3 +- 4 files changed, 99 insertions(+), 125 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 007f91f54fda..44c454b34754 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -280,7 +280,6 @@ struct mlx5e_cq { struct napi_struct *napi; struct mlx5_core_cq mcq; struct mlx5e_channel *channel; - struct mlx5e_priv *priv; /* cqe decompression */ struct mlx5_cqe64 title; @@ -290,6 +289,7 @@ struct mlx5e_cq { u16 decmprs_wqe_counter; /* control */ + struct mlx5_core_dev *mdev; struct mlx5_frag_wq_ctrl wq_ctrl; } ____cacheline_aligned_in_smp; @@ -533,7 +533,7 @@ struct mlx5e_rq { u32 mpwqe_num_strides; u32 rqn; struct mlx5e_channel *channel; - struct mlx5e_priv *priv; + struct mlx5_core_dev *mdev; struct mlx5_core_mkey umr_mkey; } ____cacheline_aligned_in_smp; @@ -556,6 +556,8 @@ struct mlx5e_channel { /* control */ struct mlx5e_priv *priv; + struct mlx5_core_dev *mdev; + struct mlx5e_tstamp *tstamp; int ix; int cpu; }; @@ -715,22 +717,6 @@ enum { MLX5E_NIC_PRIO }; -struct mlx5e_profile { - void (*init)(struct mlx5_core_dev *mdev, - struct net_device *netdev, - const struct mlx5e_profile *profile, void *ppriv); - void (*cleanup)(struct mlx5e_priv *priv); - int (*init_rx)(struct mlx5e_priv *priv); - void (*cleanup_rx)(struct mlx5e_priv *priv); - int (*init_tx)(struct mlx5e_priv *priv); - void (*cleanup_tx)(struct mlx5e_priv *priv); - void (*enable)(struct mlx5e_priv *priv); - void (*disable)(struct mlx5e_priv *priv); - void (*update_stats)(struct mlx5e_priv *priv); - int (*max_nch)(struct mlx5_core_dev *mdev); - int max_tc; -}; - struct mlx5e_priv { /* priv data path fields - start */ struct mlx5e_txqsq *txq2sq[MLX5E_MAX_NUM_CHANNELS * MLX5E_MAX_NUM_TC]; @@ -770,6 +756,22 @@ struct mlx5e_priv { void *ppriv; }; +struct mlx5e_profile { + void (*init)(struct mlx5_core_dev *mdev, + struct net_device *netdev, + const struct mlx5e_profile *profile, void *ppriv); + void (*cleanup)(struct mlx5e_priv *priv); + int (*init_rx)(struct mlx5e_priv *priv); + void (*cleanup_rx)(struct mlx5e_priv *priv); + int (*init_tx)(struct mlx5e_priv *priv); + void (*cleanup_tx)(struct mlx5e_priv *priv); + void (*enable)(struct mlx5e_priv *priv); + void (*disable)(struct mlx5e_priv *priv); + void (*update_stats)(struct mlx5e_priv *priv); + int (*max_nch)(struct mlx5_core_dev *mdev); + int max_tc; +}; + void mlx5e_build_ptys2ethtool_map(void); u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index cf8df1d3275e..a6e09c46440b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -491,11 +491,10 @@ static void mlx5e_rq_free_mpwqe_info(struct mlx5e_rq *rq) kfree(rq->mpwqe.info); } -static int mlx5e_create_umr_mkey(struct mlx5e_priv *priv, +static int mlx5e_create_umr_mkey(struct mlx5_core_dev *mdev, u64 npages, u8 page_shift, struct mlx5_core_mkey *umr_mkey) { - struct mlx5_core_dev *mdev = priv->mdev; int inlen = MLX5_ST_SZ_BYTES(create_mkey_in); void *mkc; u32 *in; @@ -529,12 +528,11 @@ static int mlx5e_create_umr_mkey(struct mlx5e_priv *priv, return err; } -static int mlx5e_create_rq_umr_mkey(struct mlx5e_rq *rq) +static int mlx5e_create_rq_umr_mkey(struct mlx5_core_dev *mdev, struct mlx5e_rq *rq) { - struct mlx5e_priv *priv = rq->priv; u64 num_mtts = MLX5E_REQUIRED_MTTS(mlx5_wq_ll_get_size(&rq->wq)); - return mlx5e_create_umr_mkey(priv, num_mtts, PAGE_SHIFT, &rq->umr_mkey); + return mlx5e_create_umr_mkey(mdev, num_mtts, PAGE_SHIFT, &rq->umr_mkey); } static int mlx5e_alloc_rq(struct mlx5e_channel *c, @@ -542,8 +540,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, struct mlx5e_rq_param *rqp, struct mlx5e_rq *rq) { - struct mlx5e_priv *priv = c->priv; - struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_core_dev *mdev = c->mdev; void *rqc = rqp->rqc; void *rqc_wq = MLX5_ADDR_OF(rqc, rqc, wq); u32 byte_count; @@ -567,10 +564,10 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, rq->wq_type = params->rq_wq_type; rq->pdev = c->pdev; rq->netdev = c->netdev; - rq->tstamp = &priv->tstamp; + rq->tstamp = c->tstamp; rq->channel = c; rq->ix = c->ix; - rq->priv = c->priv; + rq->mdev = mdev; rq->xdp_prog = params->xdp_prog ? bpf_prog_inc(params->xdp_prog) : NULL; if (IS_ERR(rq->xdp_prog)) { @@ -589,7 +586,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, switch (rq->wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: - if (mlx5e_is_vf_vport_rep(priv)) { + if (mlx5e_is_vf_vport_rep(c->priv)) { err = -EINVAL; goto err_rq_wq_destroy; } @@ -604,7 +601,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, rq->buff.wqe_sz = rq->mpwqe_stride_sz * rq->mpwqe_num_strides; byte_count = rq->buff.wqe_sz; - err = mlx5e_create_rq_umr_mkey(rq); + err = mlx5e_create_rq_umr_mkey(mdev, rq); if (err) goto err_rq_wq_destroy; rq->mkey_be = cpu_to_be32(rq->umr_mkey.key); @@ -621,7 +618,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, goto err_rq_wq_destroy; } - if (mlx5e_is_vf_vport_rep(priv)) + if (mlx5e_is_vf_vport_rep(c->priv)) rq->handle_rx_cqe = mlx5e_handle_rx_cqe_rep; else rq->handle_rx_cqe = mlx5e_handle_rx_cqe; @@ -631,7 +628,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, rq->buff.wqe_sz = params->lro_en ? params->lro_wqe_sz : - MLX5E_SW2HW_MTU(priv->netdev->mtu); + MLX5E_SW2HW_MTU(c->netdev->mtu); byte_count = rq->buff.wqe_sz; /* calc the required page order */ @@ -682,7 +679,7 @@ static void mlx5e_free_rq(struct mlx5e_rq *rq) switch (rq->wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: mlx5e_rq_free_mpwqe_info(rq); - mlx5_core_destroy_mkey(rq->priv->mdev, &rq->umr_mkey); + mlx5_core_destroy_mkey(rq->mdev, &rq->umr_mkey); break; default: /* MLX5_WQ_TYPE_LINKED_LIST */ kfree(rq->dma_info); @@ -700,8 +697,7 @@ static void mlx5e_free_rq(struct mlx5e_rq *rq) static int mlx5e_create_rq(struct mlx5e_rq *rq, struct mlx5e_rq_param *param) { - struct mlx5e_priv *priv = rq->priv; - struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_core_dev *mdev = rq->mdev; void *in; void *rqc; @@ -740,8 +736,7 @@ static int mlx5e_modify_rq_state(struct mlx5e_rq *rq, int curr_state, int next_state) { struct mlx5e_channel *c = rq->channel; - struct mlx5e_priv *priv = c->priv; - struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_core_dev *mdev = c->mdev; void *in; void *rqc; @@ -768,9 +763,7 @@ static int mlx5e_modify_rq_state(struct mlx5e_rq *rq, int curr_state, static int mlx5e_modify_rq_vsd(struct mlx5e_rq *rq, bool vsd) { struct mlx5e_channel *c = rq->channel; - struct mlx5e_priv *priv = c->priv; - struct mlx5_core_dev *mdev = priv->mdev; - + struct mlx5_core_dev *mdev = c->mdev; void *in; void *rqc; int inlen; @@ -798,14 +791,14 @@ static int mlx5e_modify_rq_vsd(struct mlx5e_rq *rq, bool vsd) static void mlx5e_destroy_rq(struct mlx5e_rq *rq) { - mlx5_core_destroy_rq(rq->priv->mdev, rq->rqn); + mlx5_core_destroy_rq(rq->mdev, rq->rqn); } static int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq) { unsigned long exp_time = jiffies + msecs_to_jiffies(20000); struct mlx5e_channel *c = rq->channel; - struct mlx5e_priv *priv = c->priv; + struct mlx5_wq_ll *wq = &rq->wq; u16 min_wqes = mlx5_min_rx_wqes(rq->wq_type, mlx5_wq_ll_get_size(wq)); @@ -816,7 +809,7 @@ static int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq) msleep(20); } - netdev_warn(priv->netdev, "Failed to get min RX wqes on RQN[0x%x] wq cur_sz(%d) min_rx_wqes(%d)\n", + netdev_warn(c->netdev, "Failed to get min RX wqes on RQN[0x%x] wq cur_sz(%d) min_rx_wqes(%d)\n", rq->rqn, wq->cur_sz, min_wqes); return -ETIMEDOUT; } @@ -926,8 +919,7 @@ static int mlx5e_alloc_xdpsq(struct mlx5e_channel *c, struct mlx5e_xdpsq *sq) { void *sqc_wq = MLX5_ADDR_OF(sqc, param->sqc, wq); - struct mlx5e_priv *priv = c->priv; - struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_core_dev *mdev = c->mdev; int err; sq->pdev = c->pdev; @@ -982,8 +974,7 @@ static int mlx5e_alloc_icosq(struct mlx5e_channel *c, struct mlx5e_icosq *sq) { void *sqc_wq = MLX5_ADDR_OF(sqc, param->sqc, wq); - struct mlx5e_priv *priv = c->priv; - struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_core_dev *mdev = c->mdev; int err; sq->pdev = c->pdev; @@ -1052,12 +1043,11 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, struct mlx5e_txqsq *sq) { void *sqc_wq = MLX5_ADDR_OF(sqc, param->sqc, wq); - struct mlx5e_priv *priv = c->priv; - struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_core_dev *mdev = c->mdev; int err; sq->pdev = c->pdev; - sq->tstamp = &priv->tstamp; + sq->tstamp = c->tstamp; sq->mkey_be = c->mkey_be; sq->channel = c; sq->txq_ix = txq_ix; @@ -1099,13 +1089,11 @@ struct mlx5e_create_sq_param { u8 min_inline_mode; }; -static int mlx5e_create_sq(struct mlx5e_priv *priv, +static int mlx5e_create_sq(struct mlx5_core_dev *mdev, struct mlx5e_sq_param *param, struct mlx5e_create_sq_param *csp, u32 *sqn) { - struct mlx5_core_dev *mdev = priv->mdev; - void *in; void *sqc; void *wq; @@ -1132,7 +1120,7 @@ static int mlx5e_create_sq(struct mlx5e_priv *priv, MLX5_SET(sqc, sqc, state, MLX5_SQC_STATE_RST); MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_CYCLIC); - MLX5_SET(wq, wq, uar_page, priv->mdev->mlx5e_res.bfreg.index); + MLX5_SET(wq, wq, uar_page, mdev->mlx5e_res.bfreg.index); MLX5_SET(wq, wq, log_wq_pg_sz, csp->wq_ctrl->buf.page_shift - MLX5_ADAPTER_PAGE_SHIFT); MLX5_SET64(wq, wq, dbr_addr, csp->wq_ctrl->db.dma); @@ -1153,12 +1141,9 @@ struct mlx5e_modify_sq_param { int rl_index; }; -static int mlx5e_modify_sq(struct mlx5e_priv *priv, - u32 sqn, +static int mlx5e_modify_sq(struct mlx5_core_dev *mdev, u32 sqn, struct mlx5e_modify_sq_param *p) { - struct mlx5_core_dev *mdev = priv->mdev; - void *in; void *sqc; int inlen; @@ -1185,12 +1170,12 @@ static int mlx5e_modify_sq(struct mlx5e_priv *priv, return err; } -static void mlx5e_destroy_sq(struct mlx5e_priv *priv, u32 sqn) +static void mlx5e_destroy_sq(struct mlx5_core_dev *mdev, u32 sqn) { - mlx5_core_destroy_sq(priv->mdev, sqn); + mlx5_core_destroy_sq(mdev, sqn); } -static int mlx5e_create_sq_rdy(struct mlx5e_priv *priv, +static int mlx5e_create_sq_rdy(struct mlx5_core_dev *mdev, struct mlx5e_sq_param *param, struct mlx5e_create_sq_param *csp, u32 *sqn) @@ -1198,15 +1183,15 @@ static int mlx5e_create_sq_rdy(struct mlx5e_priv *priv, struct mlx5e_modify_sq_param msp = {0}; int err; - err = mlx5e_create_sq(priv, param, csp, sqn); + err = mlx5e_create_sq(mdev, param, csp, sqn); if (err) return err; msp.curr_state = MLX5_SQC_STATE_RST; msp.next_state = MLX5_SQC_STATE_RDY; - err = mlx5e_modify_sq(priv, *sqn, &msp); + err = mlx5e_modify_sq(mdev, *sqn, &msp); if (err) - mlx5e_destroy_sq(priv, *sqn); + mlx5e_destroy_sq(mdev, *sqn); return err; } @@ -1215,14 +1200,13 @@ static int mlx5e_set_sq_maxrate(struct net_device *dev, struct mlx5e_txqsq *sq, u32 rate); static int mlx5e_open_txqsq(struct mlx5e_channel *c, - int tc, + u32 tisn, int txq_ix, struct mlx5e_params *params, struct mlx5e_sq_param *param, struct mlx5e_txqsq *sq) { struct mlx5e_create_sq_param csp = {}; - struct mlx5e_priv *priv = c->priv; u32 tx_rate; int err; @@ -1230,18 +1214,18 @@ static int mlx5e_open_txqsq(struct mlx5e_channel *c, if (err) return err; - csp.tisn = priv->tisn[tc]; + csp.tisn = tisn; csp.tis_lst_sz = 1; csp.cqn = sq->cq.mcq.cqn; csp.wq_ctrl = &sq->wq_ctrl; csp.min_inline_mode = sq->min_inline_mode; - err = mlx5e_create_sq_rdy(c->priv, param, &csp, &sq->sqn); + err = mlx5e_create_sq_rdy(c->mdev, param, &csp, &sq->sqn); if (err) goto err_free_txqsq; - tx_rate = priv->tx_rates[sq->txq_ix]; + tx_rate = c->priv->tx_rates[sq->txq_ix]; if (tx_rate) - mlx5e_set_sq_maxrate(priv->netdev, sq, tx_rate); + mlx5e_set_sq_maxrate(c->netdev, sq, tx_rate); return 0; @@ -1254,9 +1238,7 @@ err_free_txqsq: static void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq) { - struct mlx5e_priv *priv = sq->channel->priv; - - sq->txq = netdev_get_tx_queue(priv->netdev, sq->txq_ix); + sq->txq = netdev_get_tx_queue(sq->channel->netdev, sq->txq_ix); set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); netdev_tx_reset_queue(sq->txq); netif_tx_start_queue(sq->txq); @@ -1292,10 +1274,9 @@ static void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq) static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq) { struct mlx5e_channel *c = sq->channel; - struct mlx5e_priv *priv = c->priv; - struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_core_dev *mdev = c->mdev; - mlx5e_destroy_sq(priv, sq->sqn); + mlx5e_destroy_sq(mdev, sq->sqn); if (sq->rate_limit) mlx5_rl_remove_rate(mdev, sq->rate_limit); mlx5e_free_txqsq_descs(sq); @@ -1318,7 +1299,7 @@ static int mlx5e_open_icosq(struct mlx5e_channel *c, csp.wq_ctrl = &sq->wq_ctrl; csp.min_inline_mode = params->tx_min_inline_mode; set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); - err = mlx5e_create_sq_rdy(c->priv, param, &csp, &sq->sqn); + err = mlx5e_create_sq_rdy(c->mdev, param, &csp, &sq->sqn); if (err) goto err_free_icosq; @@ -1338,7 +1319,7 @@ static void mlx5e_close_icosq(struct mlx5e_icosq *sq) clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); napi_synchronize(&c->napi); - mlx5e_destroy_sq(c->priv, sq->sqn); + mlx5e_destroy_sq(c->mdev, sq->sqn); mlx5e_free_icosq(sq); } @@ -1349,7 +1330,6 @@ static int mlx5e_open_xdpsq(struct mlx5e_channel *c, { unsigned int ds_cnt = MLX5E_XDP_TX_DS_COUNT; struct mlx5e_create_sq_param csp = {}; - struct mlx5e_priv *priv = c->priv; unsigned int inline_hdr_sz = 0; int err; int i; @@ -1359,12 +1339,12 @@ static int mlx5e_open_xdpsq(struct mlx5e_channel *c, return err; csp.tis_lst_sz = 1; - csp.tisn = priv->tisn[0]; /* tc = 0 */ + csp.tisn = c->priv->tisn[0]; /* tc = 0 */ csp.cqn = sq->cq.mcq.cqn; csp.wq_ctrl = &sq->wq_ctrl; csp.min_inline_mode = sq->min_inline_mode; set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); - err = mlx5e_create_sq_rdy(c->priv, param, &csp, &sq->sqn); + err = mlx5e_create_sq_rdy(c->mdev, param, &csp, &sq->sqn); if (err) goto err_free_xdpsq; @@ -1403,7 +1383,7 @@ static void mlx5e_close_xdpsq(struct mlx5e_xdpsq *sq) clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); napi_synchronize(&c->napi); - mlx5e_destroy_sq(c->priv, sq->sqn); + mlx5e_destroy_sq(c->mdev, sq->sqn); mlx5e_free_xdpsq_descs(sq); mlx5e_free_xdpsq(sq); } @@ -1412,8 +1392,7 @@ static int mlx5e_alloc_cq(struct mlx5e_channel *c, struct mlx5e_cq_param *param, struct mlx5e_cq *cq) { - struct mlx5e_priv *priv = c->priv; - struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_core_dev *mdev = c->mdev; struct mlx5_core_cq *mcq = &cq->mcq; int eqn_not_used; unsigned int irqn; @@ -1450,7 +1429,7 @@ static int mlx5e_alloc_cq(struct mlx5e_channel *c, } cq->channel = c; - cq->priv = priv; + cq->mdev = mdev; return 0; } @@ -1462,8 +1441,7 @@ static void mlx5e_free_cq(struct mlx5e_cq *cq) static int mlx5e_create_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param) { - struct mlx5e_priv *priv = cq->priv; - struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_core_dev *mdev = cq->mdev; struct mlx5_core_cq *mcq = &cq->mcq; void *in; @@ -1509,10 +1487,7 @@ static int mlx5e_create_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param) static void mlx5e_destroy_cq(struct mlx5e_cq *cq) { - struct mlx5e_priv *priv = cq->priv; - struct mlx5_core_dev *mdev = priv->mdev; - - mlx5_core_destroy_cq(mdev, &cq->mcq); + mlx5_core_destroy_cq(cq->mdev, &cq->mcq); } static int mlx5e_open_cq(struct mlx5e_channel *c, @@ -1520,9 +1495,8 @@ static int mlx5e_open_cq(struct mlx5e_channel *c, struct mlx5e_cq_param *param, struct mlx5e_cq *cq) { + struct mlx5_core_dev *mdev = c->mdev; int err; - struct mlx5e_priv *priv = c->priv; - struct mlx5_core_dev *mdev = priv->mdev; err = mlx5e_alloc_cq(c, param, cq); if (err) @@ -1594,7 +1568,8 @@ static int mlx5e_open_sqs(struct mlx5e_channel *c, for (tc = 0; tc < params->num_tc; tc++) { int txq_ix = c->ix + tc * params->num_channels; - err = mlx5e_open_txqsq(c, tc, txq_ix, params, &cparam->sq, &c->sq[tc]); + err = mlx5e_open_txqsq(c, c->priv->tisn[tc], txq_ix, + params, &cparam->sq, &c->sq[tc]); if (err) goto err_close_sqs; } @@ -1648,7 +1623,7 @@ static int mlx5e_set_sq_maxrate(struct net_device *dev, msp.next_state = MLX5_SQC_STATE_RDY; msp.rl_index = rl_index; msp.rl_update = true; - err = mlx5e_modify_sq(priv, sq->sqn, &msp); + err = mlx5e_modify_sq(mdev, sq->sqn, &msp); if (err) { netdev_err(dev, "Failed configuring rate %u: %d\n", rate, err); @@ -1717,6 +1692,8 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, return -ENOMEM; c->priv = priv; + c->mdev = priv->mdev; + c->tstamp = &priv->tstamp; c->ix = ix; c->cpu = cpu; c->pdev = &priv->mdev->pdev->dev; @@ -1804,7 +1781,7 @@ static void mlx5e_activate_channel(struct mlx5e_channel *c) for (tc = 0; tc < c->num_tc; tc++) mlx5e_activate_txqsq(&c->sq[tc]); mlx5e_activate_rq(&c->rq); - netif_set_xps_queue(c->priv->netdev, get_cpu_mask(c->cpu), c->ix); + netif_set_xps_queue(c->netdev, get_cpu_mask(c->cpu), c->ix); } static void mlx5e_deactivate_channel(struct mlx5e_channel *c) @@ -2631,11 +2608,10 @@ int mlx5e_close(struct net_device *netdev) return err; } -static int mlx5e_alloc_drop_rq(struct mlx5e_priv *priv, +static int mlx5e_alloc_drop_rq(struct mlx5_core_dev *mdev, struct mlx5e_rq *rq, struct mlx5e_rq_param *param) { - struct mlx5_core_dev *mdev = priv->mdev; void *rqc = param->rqc; void *rqc_wq = MLX5_ADDR_OF(rqc, rqc, wq); int err; @@ -2647,16 +2623,15 @@ static int mlx5e_alloc_drop_rq(struct mlx5e_priv *priv, if (err) return err; - rq->priv = priv; + rq->mdev = mdev; return 0; } -static int mlx5e_alloc_drop_cq(struct mlx5e_priv *priv, +static int mlx5e_alloc_drop_cq(struct mlx5_core_dev *mdev, struct mlx5e_cq *cq, struct mlx5e_cq_param *param) { - struct mlx5_core_dev *mdev = priv->mdev; struct mlx5_core_cq *mcq = &cq->mcq; int eqn_not_used; unsigned int irqn; @@ -2679,24 +2654,22 @@ static int mlx5e_alloc_drop_cq(struct mlx5e_priv *priv, mcq->event = mlx5e_cq_error_event; mcq->irqn = irqn; - cq->priv = priv; + cq->mdev = mdev; return 0; } -static int mlx5e_open_drop_rq(struct mlx5e_priv *priv) +static int mlx5e_open_drop_rq(struct mlx5_core_dev *mdev, + struct mlx5e_rq *drop_rq) { - struct mlx5e_cq_param cq_param; - struct mlx5e_rq_param rq_param; - struct mlx5e_rq *rq = &priv->drop_rq; - struct mlx5e_cq *cq = &priv->drop_rq.cq; + struct mlx5e_cq_param cq_param = {}; + struct mlx5e_rq_param rq_param = {}; + struct mlx5e_cq *cq = &drop_rq->cq; int err; - memset(&cq_param, 0, sizeof(cq_param)); - memset(&rq_param, 0, sizeof(rq_param)); mlx5e_build_drop_rq_param(&rq_param); - err = mlx5e_alloc_drop_cq(priv, cq, &cq_param); + err = mlx5e_alloc_drop_cq(mdev, cq, &cq_param); if (err) return err; @@ -2704,34 +2677,34 @@ static int mlx5e_open_drop_rq(struct mlx5e_priv *priv) if (err) goto err_free_cq; - err = mlx5e_alloc_drop_rq(priv, rq, &rq_param); + err = mlx5e_alloc_drop_rq(mdev, drop_rq, &rq_param); if (err) goto err_destroy_cq; - err = mlx5e_create_rq(rq, &rq_param); + err = mlx5e_create_rq(drop_rq, &rq_param); if (err) goto err_free_rq; return 0; err_free_rq: - mlx5e_free_rq(&priv->drop_rq); + mlx5e_free_rq(drop_rq); err_destroy_cq: - mlx5e_destroy_cq(&priv->drop_rq.cq); + mlx5e_destroy_cq(cq); err_free_cq: - mlx5e_free_cq(&priv->drop_rq.cq); + mlx5e_free_cq(cq); return err; } -static void mlx5e_close_drop_rq(struct mlx5e_priv *priv) +static void mlx5e_close_drop_rq(struct mlx5e_rq *drop_rq) { - mlx5e_destroy_rq(&priv->drop_rq); - mlx5e_free_rq(&priv->drop_rq); - mlx5e_destroy_cq(&priv->drop_rq.cq); - mlx5e_free_cq(&priv->drop_rq.cq); + mlx5e_destroy_rq(drop_rq); + mlx5e_free_rq(drop_rq); + mlx5e_destroy_cq(&drop_rq->cq); + mlx5e_free_cq(&drop_rq->cq); } static int mlx5e_create_tis(struct mlx5e_priv *priv, int tc) @@ -4151,7 +4124,7 @@ int mlx5e_attach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev) if (err) goto out; - err = mlx5e_open_drop_rq(priv); + err = mlx5e_open_drop_rq(mdev, &priv->drop_rq); if (err) { mlx5_core_err(mdev, "open drop rq failed, %d\n", err); goto err_cleanup_tx; @@ -4184,7 +4157,7 @@ int mlx5e_attach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev) return 0; err_close_drop_rq: - mlx5e_close_drop_rq(priv); + mlx5e_close_drop_rq(&priv->drop_rq); err_cleanup_tx: profile->cleanup_tx(priv); @@ -4248,7 +4221,7 @@ void mlx5e_detach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev) mlx5e_destroy_q_counter(priv); profile->cleanup_rx(priv); - mlx5e_close_drop_rq(priv); + mlx5e_close_drop_rq(&priv->drop_rq); profile->cleanup_tx(priv); cancel_delayed_work_sync(&priv->update_stats_work); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c index cbfac06b7ffd..02dd3a95ed8f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx_am.c @@ -293,7 +293,7 @@ void mlx5e_rx_am_work(struct work_struct *work) struct mlx5e_rq *rq = container_of(am, struct mlx5e_rq, am); struct mlx5e_cq_moder cur_profile = profile[am->mode][am->profile_ix]; - mlx5_core_modify_cq_moderation(rq->priv->mdev, &rq->cq.mcq, + mlx5_core_modify_cq_moderation(rq->mdev, &rq->cq.mcq, cur_profile.usec, cur_profile.pkts); am->state = MLX5E_AM_START_MEASURE; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 3317ef561a75..43729ec35dfc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -164,8 +164,7 @@ void mlx5e_cq_error_event(struct mlx5_core_cq *mcq, enum mlx5_event event) { struct mlx5e_cq *cq = container_of(mcq, struct mlx5e_cq, mcq); struct mlx5e_channel *c = cq->channel; - struct mlx5e_priv *priv = c->priv; - struct net_device *netdev = priv->netdev; + struct net_device *netdev = c->netdev; netdev_err(netdev, "%s: cqn=0x%.6x event=0x%.2x\n", __func__, mcq->cqn, event); -- cgit From 9008ae074885ddaa2470f4c106245ddea4ae2a67 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 7 Feb 2017 16:35:49 +0200 Subject: net/mlx5e: Minimize mlx5e_{open/close}_locked mlx5e_redirect_rqts_to_{channels,drop} and mlx5e_{add,del}_sqs_fwd_rules and Set real num tx/rx queues belong to mlx5e_{activate,deactivate}_priv_channels, for that we move those functions and minimize mlx5e_open/close flows. This will be needed in downstream patches to replace old channels with new ones without the need to call mlx5e_close/open. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 40 +++++++++++------------ drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 10 ++++-- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index a6e09c46440b..a94f84ec2c1a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2498,14 +2498,33 @@ static void mlx5e_build_channels_tx_maps(struct mlx5e_priv *priv) static void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) { + int num_txqs = priv->channels.num * priv->channels.params.num_tc; + struct net_device *netdev = priv->netdev; + + mlx5e_netdev_set_tcs(netdev); + if (netdev->real_num_tx_queues != num_txqs) + netif_set_real_num_tx_queues(netdev, num_txqs); + if (netdev->real_num_rx_queues != priv->channels.num) + netif_set_real_num_rx_queues(netdev, priv->channels.num); + mlx5e_build_channels_tx_maps(priv); mlx5e_activate_channels(&priv->channels); netif_tx_start_all_queues(priv->netdev); + + if (MLX5_CAP_GEN(priv->mdev, vport_group_manager)) + mlx5e_add_sqs_fwd_rules(priv); + mlx5e_wait_channels_min_rx_wqes(&priv->channels); + mlx5e_redirect_rqts_to_channels(priv, &priv->channels); } static void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv) { + mlx5e_redirect_rqts_to_drop(priv); + + if (MLX5_CAP_GEN(priv->mdev, vport_group_manager)) + mlx5e_remove_sqs_fwd_rules(priv); + /* FIXME: This is a W/A only for tx timeout watch dog false alarm when * polling for inactive tx queues. */ @@ -2517,40 +2536,24 @@ static void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv) int mlx5e_open_locked(struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); - struct mlx5_core_dev *mdev = priv->mdev; - int num_txqs; int err; set_bit(MLX5E_STATE_OPENED, &priv->state); - mlx5e_netdev_set_tcs(netdev); - - num_txqs = priv->channels.params.num_channels * priv->channels.params.num_tc; - netif_set_real_num_tx_queues(netdev, num_txqs); - netif_set_real_num_rx_queues(netdev, priv->channels.params.num_channels); - err = mlx5e_open_channels(priv, &priv->channels); if (err) goto err_clear_state_opened_flag; mlx5e_refresh_tirs(priv, false); mlx5e_activate_priv_channels(priv); - mlx5e_redirect_rqts_to_channels(priv, &priv->channels); mlx5e_update_carrier(priv); mlx5e_timestamp_init(priv); if (priv->profile->update_stats) queue_delayed_work(priv->wq, &priv->update_stats_work, 0); - if (MLX5_CAP_GEN(mdev, vport_group_manager)) { - err = mlx5e_add_sqs_fwd_rules(priv); - if (err) - goto err_close_channels; - } return 0; -err_close_channels: - mlx5e_close_channels(&priv->channels); err_clear_state_opened_flag: clear_bit(MLX5E_STATE_OPENED, &priv->state); return err; @@ -2571,7 +2574,6 @@ int mlx5e_open(struct net_device *netdev) int mlx5e_close_locked(struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); - struct mlx5_core_dev *mdev = priv->mdev; /* May already be CLOSED in case a previous configuration operation * (e.g RX/TX queue size change) that involves close&open failed. @@ -2581,12 +2583,8 @@ int mlx5e_close_locked(struct net_device *netdev) clear_bit(MLX5E_STATE_OPENED, &priv->state); - if (MLX5_CAP_GEN(mdev, vport_group_manager)) - mlx5e_remove_sqs_fwd_rules(priv); - mlx5e_timestamp_cleanup(priv); netif_carrier_off(priv->netdev); - mlx5e_redirect_rqts_to_drop(priv); mlx5e_deactivate_priv_channels(priv); mlx5e_close_channels(&priv->channels); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index d277c1979b2a..53db5ec2c122 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -189,12 +189,13 @@ int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5_eswitch_rep *rep = priv->ppriv; struct mlx5e_channel *c; - int n, tc, err, num_sqs = 0; + int n, tc, num_sqs = 0; + int err = -ENOMEM; u16 *sqs; sqs = kcalloc(priv->channels.num * priv->channels.params.num_tc, sizeof(u16), GFP_KERNEL); if (!sqs) - return -ENOMEM; + goto out; for (n = 0; n < priv->channels.num; n++) { c = priv->channels.c[n]; @@ -203,8 +204,11 @@ int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) } err = mlx5_eswitch_sqs2vport_start(esw, rep, sqs, num_sqs); - kfree(sqs); + +out: + if (err) + netdev_warn(priv->netdev, "Failed to add SQs FWD rules %d\n", err); return err; } -- cgit From 55c2503dae1ac8aed14d261dc02f967b4d6b1f88 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 27 Dec 2016 14:57:03 +0200 Subject: net/mlx5e: Introduce switch channels A fail safe helper functions that allows switching to new channels on the fly, In simple words: make_new_config(new_params) { new_channels = open_channels(new_params); if (!new_channels) return "Failed, but current channels are still active :)" switch_channels(new_channels); return "SUCCESS"; } Demonstrate mlx5e_switch_priv_channels usage in set channels ethtool callback and make it fail-safe using the new switch channels mechanism. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 7 +++++ .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 29 ++++++++++++--------- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 30 +++++++++++++++++++--- 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 44c454b34754..2f259dfbf844 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -863,6 +863,13 @@ void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_params *params, int mlx5e_open_locked(struct net_device *netdev); int mlx5e_close_locked(struct net_device *netdev); + +int mlx5e_open_channels(struct mlx5e_priv *priv, + struct mlx5e_channels *chs); +void mlx5e_close_channels(struct mlx5e_channels *chs); +void mlx5e_switch_priv_channels(struct mlx5e_priv *priv, + struct mlx5e_channels *new_chs); + void mlx5e_build_default_indir_rqt(struct mlx5_core_dev *mdev, u32 *indirection_rqt, int len, int num_channels); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index b2cd0ef7921e..e5cee400a4d3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -556,8 +556,8 @@ static int mlx5e_set_channels(struct net_device *dev, { struct mlx5e_priv *priv = netdev_priv(dev); unsigned int count = ch->combined_count; + struct mlx5e_channels new_channels = {}; bool arfs_enabled; - bool was_opened; int err = 0; if (!count) { @@ -571,22 +571,27 @@ static int mlx5e_set_channels(struct net_device *dev, mutex_lock(&priv->state_lock); - was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); - if (was_opened) - mlx5e_close_locked(dev); + new_channels.params = priv->channels.params; + new_channels.params.num_channels = count; + mlx5e_build_default_indir_rqt(priv->mdev, new_channels.params.indirection_rqt, + MLX5E_INDIR_RQT_SIZE, count); + + if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { + priv->channels.params = new_channels.params; + goto out; + } + + /* Create fresh channels with new parameters */ + err = mlx5e_open_channels(priv, &new_channels); + if (err) + goto out; arfs_enabled = dev->features & NETIF_F_NTUPLE; if (arfs_enabled) mlx5e_arfs_disable(priv); - priv->channels.params.num_channels = count; - mlx5e_build_default_indir_rqt(priv->mdev, priv->channels.params.indirection_rqt, - MLX5E_INDIR_RQT_SIZE, count); - - if (was_opened) - err = mlx5e_open_locked(dev); - if (err) - goto out; + /* Switch to new channels, set new parameters and close old ones */ + mlx5e_switch_priv_channels(priv, &new_channels); if (arfs_enabled) { err = mlx5e_arfs_enable(priv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index a94f84ec2c1a..97e153209834 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1972,8 +1972,8 @@ static void mlx5e_build_channel_param(struct mlx5e_priv *priv, mlx5e_build_ico_cq_param(priv, icosq_log_wq_sz, &cparam->icosq_cq); } -static int mlx5e_open_channels(struct mlx5e_priv *priv, - struct mlx5e_channels *chs) +int mlx5e_open_channels(struct mlx5e_priv *priv, + struct mlx5e_channels *chs) { struct mlx5e_channel_param *cparam; int err = -ENOMEM; @@ -2037,7 +2037,7 @@ static void mlx5e_deactivate_channels(struct mlx5e_channels *chs) mlx5e_deactivate_channel(chs->c[i]); } -static void mlx5e_close_channels(struct mlx5e_channels *chs) +void mlx5e_close_channels(struct mlx5e_channels *chs) { int i; @@ -2533,6 +2533,30 @@ static void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv) mlx5e_deactivate_channels(&priv->channels); } +void mlx5e_switch_priv_channels(struct mlx5e_priv *priv, + struct mlx5e_channels *new_chs) +{ + struct net_device *netdev = priv->netdev; + int new_num_txqs; + + new_num_txqs = new_chs->num * new_chs->params.num_tc; + + netif_carrier_off(netdev); + + if (new_num_txqs < netdev->real_num_tx_queues) + netif_set_real_num_tx_queues(netdev, new_num_txqs); + + mlx5e_deactivate_priv_channels(priv); + mlx5e_close_channels(&priv->channels); + + priv->channels = *new_chs; + + mlx5e_refresh_tirs(priv, false); + mlx5e_activate_priv_channels(priv); + + mlx5e_update_carrier(priv); +} + int mlx5e_open_locked(struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); -- cgit From 546f18ed3fb55a6689488993d03b5576982790c3 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Sun, 12 Feb 2017 23:21:08 +0200 Subject: net/mlx5e: Fail safe ethtool settings Use the new fail-safe channels switch mechanism to set new ethtool settings: - ring parameters - coalesce parameters - tx copy break parameters Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 120 +++++++++++++-------- 1 file changed, 73 insertions(+), 47 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index e5cee400a4d3..457a796cc248 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -457,8 +457,8 @@ static int mlx5e_set_ringparam(struct net_device *dev, { struct mlx5e_priv *priv = netdev_priv(dev); int rq_wq_type = priv->channels.params.rq_wq_type; + struct mlx5e_channels new_channels = {}; u32 rx_pending_wqes; - bool was_opened; u32 min_rq_size; u32 max_rq_size; u8 log_rq_size; @@ -527,16 +527,22 @@ static int mlx5e_set_ringparam(struct net_device *dev, mutex_lock(&priv->state_lock); - was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); - if (was_opened) - mlx5e_close_locked(dev); + new_channels.params = priv->channels.params; + new_channels.params.log_rq_size = log_rq_size; + new_channels.params.log_sq_size = log_sq_size; - priv->channels.params.log_rq_size = log_rq_size; - priv->channels.params.log_sq_size = log_sq_size; + if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { + priv->channels.params = new_channels.params; + goto unlock; + } - if (was_opened) - err = mlx5e_open_locked(dev); + err = mlx5e_open_channels(priv, &new_channels); + if (err) + goto unlock; + + mlx5e_switch_priv_channels(priv, &new_channels); +unlock: mutex_unlock(&priv->state_lock); return err; @@ -623,36 +629,13 @@ static int mlx5e_get_coalesce(struct net_device *netdev, return 0; } -static int mlx5e_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *coal) +static void +mlx5e_set_priv_channels_coalesce(struct mlx5e_priv *priv, struct ethtool_coalesce *coal) { - struct mlx5e_priv *priv = netdev_priv(netdev); struct mlx5_core_dev *mdev = priv->mdev; - bool restart = - !!coal->use_adaptive_rx_coalesce != priv->channels.params.rx_am_enabled; - bool was_opened; - int err = 0; int tc; int i; - if (!MLX5_CAP_GEN(mdev, cq_moderation)) - return -EOPNOTSUPP; - - mutex_lock(&priv->state_lock); - - was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); - if (was_opened && restart) { - mlx5e_close_locked(netdev); - priv->channels.params.rx_am_enabled = !!coal->use_adaptive_rx_coalesce; - } - - priv->channels.params.tx_cq_moderation.usec = coal->tx_coalesce_usecs; - priv->channels.params.tx_cq_moderation.pkts = coal->tx_max_coalesced_frames; - priv->channels.params.rx_cq_moderation.usec = coal->rx_coalesce_usecs; - priv->channels.params.rx_cq_moderation.pkts = coal->rx_max_coalesced_frames; - - if (!was_opened || restart) - goto out; for (i = 0; i < priv->channels.num; ++i) { struct mlx5e_channel *c = priv->channels.c[i]; @@ -667,11 +650,50 @@ static int mlx5e_set_coalesce(struct net_device *netdev, coal->rx_coalesce_usecs, coal->rx_max_coalesced_frames); } +} -out: - if (was_opened && restart) - err = mlx5e_open_locked(netdev); +static int mlx5e_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *coal) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5e_channels new_channels = {}; + int err = 0; + bool reset; + if (!MLX5_CAP_GEN(mdev, cq_moderation)) + return -EOPNOTSUPP; + + mutex_lock(&priv->state_lock); + new_channels.params = priv->channels.params; + + new_channels.params.tx_cq_moderation.usec = coal->tx_coalesce_usecs; + new_channels.params.tx_cq_moderation.pkts = coal->tx_max_coalesced_frames; + new_channels.params.rx_cq_moderation.usec = coal->rx_coalesce_usecs; + new_channels.params.rx_cq_moderation.pkts = coal->rx_max_coalesced_frames; + new_channels.params.rx_am_enabled = !!coal->use_adaptive_rx_coalesce; + + if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { + priv->channels.params = new_channels.params; + goto out; + } + /* we are opened */ + + reset = !!coal->use_adaptive_rx_coalesce != priv->channels.params.rx_am_enabled; + if (!reset) { + mlx5e_set_priv_channels_coalesce(priv, coal); + priv->channels.params = new_channels.params; + goto out; + } + + /* open fresh channels with new coal parameters */ + err = mlx5e_open_channels(priv, &new_channels); + if (err) + goto out; + + mlx5e_switch_priv_channels(priv, &new_channels); + +out: mutex_unlock(&priv->state_lock); return err; } @@ -1119,9 +1141,11 @@ static int mlx5e_set_tunable(struct net_device *dev, { struct mlx5e_priv *priv = netdev_priv(dev); struct mlx5_core_dev *mdev = priv->mdev; - bool was_opened; - u32 val; + struct mlx5e_channels new_channels = {}; int err = 0; + u32 val; + + mutex_lock(&priv->state_lock); switch (tuna->id) { case ETHTOOL_TX_COPYBREAK: @@ -1131,24 +1155,26 @@ static int mlx5e_set_tunable(struct net_device *dev, break; } - mutex_lock(&priv->state_lock); + new_channels.params = priv->channels.params; + new_channels.params.tx_max_inline = val; - was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); - if (was_opened) - mlx5e_close_locked(dev); - - priv->channels.params.tx_max_inline = val; + if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { + priv->channels.params = new_channels.params; + break; + } - if (was_opened) - err = mlx5e_open_locked(dev); + err = mlx5e_open_channels(priv, &new_channels); + if (err) + break; + mlx5e_switch_priv_channels(priv, &new_channels); - mutex_unlock(&priv->state_lock); break; default: err = -EINVAL; break; } + mutex_unlock(&priv->state_lock); return err; } -- cgit From be7e87f92b5802df9302af7856990172091a385f Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Mon, 13 Feb 2017 00:42:54 +0200 Subject: net/mlx5e: Fail safe cqe compressing/moderation mode setting Use the new fail-safe channels switch mechanism to set new CQE compressing and CQE moderation mode settings. We also move RX CQE compression modify function out of en_rx file to a more appropriate place. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_clock.c | 8 +++- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 53 ++++++++++++++++++---- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 22 --------- 4 files changed, 51 insertions(+), 34 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 2f259dfbf844..8b93d8d02116 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -833,7 +833,7 @@ void mlx5e_pps_event_handler(struct mlx5e_priv *priv, struct ptp_clock_event *event); int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr); int mlx5e_hwstamp_get(struct net_device *dev, struct ifreq *ifr); -void mlx5e_modify_rx_cqe_compression_locked(struct mlx5e_priv *priv, bool val); +int mlx5e_modify_rx_cqe_compression_locked(struct mlx5e_priv *priv, bool val); int mlx5e_vlan_rx_add_vid(struct net_device *dev, __always_unused __be16 proto, u16 vid); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c index 485c23b59f93..e706a87fc8b2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c @@ -90,6 +90,7 @@ int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr) { struct mlx5e_priv *priv = netdev_priv(dev); struct hwtstamp_config config; + int err; if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) return -EOPNOTSUPP; @@ -129,7 +130,12 @@ int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr) case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: /* Disable CQE compression */ netdev_warn(dev, "Disabling cqe compression"); - mlx5e_modify_rx_cqe_compression_locked(priv, false); + err = mlx5e_modify_rx_cqe_compression_locked(priv, false); + if (err) { + netdev_err(dev, "Failed disabling cqe compression err=%d\n", err); + mutex_unlock(&priv->state_lock); + return err; + } config.rx_filter = HWTSTAMP_FILTER_ALL; break; default: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 457a796cc248..c5f49e294987 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -1474,10 +1474,10 @@ static int set_pflag_rx_cqe_based_moder(struct net_device *netdev, bool enable) { struct mlx5e_priv *priv = netdev_priv(netdev); struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5e_channels new_channels = {}; bool rx_mode_changed; u8 rx_cq_period_mode; int err = 0; - bool reset; rx_cq_period_mode = enable ? MLX5_CQ_PERIOD_MODE_START_FROM_CQE : @@ -1491,16 +1491,51 @@ static int set_pflag_rx_cqe_based_moder(struct net_device *netdev, bool enable) if (!rx_mode_changed) return 0; - reset = test_bit(MLX5E_STATE_OPENED, &priv->state); - if (reset) - mlx5e_close_locked(netdev); + new_channels.params = priv->channels.params; + mlx5e_set_rx_cq_mode_params(&new_channels.params, rx_cq_period_mode); - mlx5e_set_rx_cq_mode_params(&priv->channels.params, rx_cq_period_mode); + if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { + priv->channels.params = new_channels.params; + return 0; + } + + err = mlx5e_open_channels(priv, &new_channels); + if (err) + return err; - if (reset) - err = mlx5e_open_locked(netdev); + mlx5e_switch_priv_channels(priv, &new_channels); + return 0; +} - return err; +int mlx5e_modify_rx_cqe_compression_locked(struct mlx5e_priv *priv, bool new_val) +{ + bool curr_val = MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_RX_CQE_COMPRESS); + struct mlx5e_channels new_channels = {}; + int err = 0; + + if (!MLX5_CAP_GEN(priv->mdev, cqe_compression)) + return new_val ? -EOPNOTSUPP : 0; + + if (curr_val == new_val) + return 0; + + new_channels.params = priv->channels.params; + MLX5E_SET_PFLAG(&new_channels.params, MLX5E_PFLAG_RX_CQE_COMPRESS, new_val); + + mlx5e_set_rq_type_params(priv->mdev, &new_channels.params, + new_channels.params.rq_wq_type); + + if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { + priv->channels.params = new_channels.params; + return 0; + } + + err = mlx5e_open_channels(priv, &new_channels); + if (err) + return err; + + mlx5e_switch_priv_channels(priv, &new_channels); + return 0; } static int set_pflag_rx_cqe_compress(struct net_device *netdev, @@ -1519,8 +1554,6 @@ static int set_pflag_rx_cqe_compress(struct net_device *netdev, mlx5e_modify_rx_cqe_compression_locked(priv, enable); priv->channels.params.rx_cqe_compress_def = enable; - mlx5e_set_rq_type_params(priv->mdev, &priv->channels.params, - priv->channels.params.rq_wq_type); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 0a40c42e1335..1a9532b31635 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -156,28 +156,6 @@ static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, return mlx5e_decompress_cqes_cont(rq, cq, 1, budget_rem) - 1; } -void mlx5e_modify_rx_cqe_compression_locked(struct mlx5e_priv *priv, bool val) -{ - bool was_opened; - - if (!MLX5_CAP_GEN(priv->mdev, cqe_compression)) - return; - - if (MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_RX_CQE_COMPRESS) == val) - return; - - was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); - if (was_opened) - mlx5e_close_locked(priv->netdev); - - MLX5E_SET_PFLAG(&priv->channels.params, MLX5E_PFLAG_RX_CQE_COMPRESS, val); - mlx5e_set_rq_type_params(priv->mdev, &priv->channels.params, - priv->channels.params.rq_wq_type); - - if (was_opened) - mlx5e_open_locked(priv->netdev); -} - #define RQ_PAGE_SIZE(rq) ((1 << rq->buff.page_order) << PAGE_SHIFT) static inline bool mlx5e_rx_cache_put(struct mlx5e_rq *rq, -- cgit From 6f9485af4020a858ac91adc5286411b6dd3c2f06 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Mon, 13 Feb 2017 01:25:36 +0200 Subject: net/mlx5e: Fail safe tc setup Use the new fail-safe channels switch mechanism to set up new tc parameters. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 97e153209834..1e29f40d84ca 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2910,7 +2910,7 @@ int mlx5e_modify_channels_vsd(struct mlx5e_channels *chs, bool vsd) static int mlx5e_setup_tc(struct net_device *netdev, u8 tc) { struct mlx5e_priv *priv = netdev_priv(netdev); - bool was_opened; + struct mlx5e_channels new_channels = {}; int err = 0; if (tc && tc != MLX5E_MAX_NUM_TC) @@ -2918,17 +2918,21 @@ static int mlx5e_setup_tc(struct net_device *netdev, u8 tc) mutex_lock(&priv->state_lock); - was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); - if (was_opened) - mlx5e_close_locked(priv->netdev); + new_channels.params = priv->channels.params; + new_channels.params.num_tc = tc ? tc : 1; - priv->channels.params.num_tc = tc ? tc : 1; + if (test_bit(MLX5E_STATE_OPENED, &priv->state)) { + priv->channels.params = new_channels.params; + goto out; + } - if (was_opened) - err = mlx5e_open_locked(priv->netdev); + err = mlx5e_open_channels(priv, &new_channels); + if (err) + goto out; + mlx5e_switch_priv_channels(priv, &new_channels); +out: mutex_unlock(&priv->state_lock); - return err; } -- cgit From 2e20a151205be8e7efa9644cdb942381e7bec787 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Mon, 13 Feb 2017 01:19:14 +0200 Subject: net/mlx5e: Fail safe mtu and lro setting Use the new fail-safe channels switch mechanism to set new netdev mtu and lro settings. MTU and lro settings demand some HW configuration changes after new channels are created and ready for action. In order to unify switch channels routine for LRO and MTU changes, and maybe future configuration features, we now pass to it a modify HW function pointer to be invoked directly after old channels are de-activated and before new channels are activated. Signed-off-by: Saeed Mahameed Reviewed-by: Tariq Toukan --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 8 ++- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 12 ++-- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 70 ++++++++++++++-------- 3 files changed, 58 insertions(+), 32 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 8b93d8d02116..150fb52a0737 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -867,8 +867,14 @@ int mlx5e_close_locked(struct net_device *netdev); int mlx5e_open_channels(struct mlx5e_priv *priv, struct mlx5e_channels *chs); void mlx5e_close_channels(struct mlx5e_channels *chs); + +/* Function pointer to be used to modify WH settings while + * switching channels + */ +typedef int (*mlx5e_fp_hw_modify)(struct mlx5e_priv *priv); void mlx5e_switch_priv_channels(struct mlx5e_priv *priv, - struct mlx5e_channels *new_chs); + struct mlx5e_channels *new_chs, + mlx5e_fp_hw_modify hw_modify); void mlx5e_build_default_indir_rqt(struct mlx5_core_dev *mdev, u32 *indirection_rqt, int len, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index c5f49e294987..40912937d211 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -540,7 +540,7 @@ static int mlx5e_set_ringparam(struct net_device *dev, if (err) goto unlock; - mlx5e_switch_priv_channels(priv, &new_channels); + mlx5e_switch_priv_channels(priv, &new_channels, NULL); unlock: mutex_unlock(&priv->state_lock); @@ -597,7 +597,7 @@ static int mlx5e_set_channels(struct net_device *dev, mlx5e_arfs_disable(priv); /* Switch to new channels, set new parameters and close old ones */ - mlx5e_switch_priv_channels(priv, &new_channels); + mlx5e_switch_priv_channels(priv, &new_channels, NULL); if (arfs_enabled) { err = mlx5e_arfs_enable(priv); @@ -691,7 +691,7 @@ static int mlx5e_set_coalesce(struct net_device *netdev, if (err) goto out; - mlx5e_switch_priv_channels(priv, &new_channels); + mlx5e_switch_priv_channels(priv, &new_channels, NULL); out: mutex_unlock(&priv->state_lock); @@ -1166,7 +1166,7 @@ static int mlx5e_set_tunable(struct net_device *dev, err = mlx5e_open_channels(priv, &new_channels); if (err) break; - mlx5e_switch_priv_channels(priv, &new_channels); + mlx5e_switch_priv_channels(priv, &new_channels, NULL); break; default: @@ -1503,7 +1503,7 @@ static int set_pflag_rx_cqe_based_moder(struct net_device *netdev, bool enable) if (err) return err; - mlx5e_switch_priv_channels(priv, &new_channels); + mlx5e_switch_priv_channels(priv, &new_channels, NULL); return 0; } @@ -1534,7 +1534,7 @@ int mlx5e_modify_rx_cqe_compression_locked(struct mlx5e_priv *priv, bool new_val if (err) return err; - mlx5e_switch_priv_channels(priv, &new_channels); + mlx5e_switch_priv_channels(priv, &new_channels, NULL); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 1e29f40d84ca..68d6c3c58ba7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2437,9 +2437,9 @@ static void mlx5e_query_mtu(struct mlx5e_priv *priv, u16 *mtu) *mtu = MLX5E_HW2SW_MTU(hw_mtu); } -static int mlx5e_set_dev_port_mtu(struct net_device *netdev) +static int mlx5e_set_dev_port_mtu(struct mlx5e_priv *priv) { - struct mlx5e_priv *priv = netdev_priv(netdev); + struct net_device *netdev = priv->netdev; u16 mtu; int err; @@ -2534,7 +2534,8 @@ static void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv) } void mlx5e_switch_priv_channels(struct mlx5e_priv *priv, - struct mlx5e_channels *new_chs) + struct mlx5e_channels *new_chs, + mlx5e_fp_hw_modify hw_modify) { struct net_device *netdev = priv->netdev; int new_num_txqs; @@ -2551,6 +2552,10 @@ void mlx5e_switch_priv_channels(struct mlx5e_priv *priv, priv->channels = *new_chs; + /* New channels are ready to roll, modify HW settings if needed */ + if (hw_modify) + hw_modify(priv); + mlx5e_refresh_tirs(priv, false); mlx5e_activate_priv_channels(priv); @@ -2930,7 +2935,7 @@ static int mlx5e_setup_tc(struct net_device *netdev, u8 tc) if (err) goto out; - mlx5e_switch_priv_channels(priv, &new_channels); + mlx5e_switch_priv_channels(priv, &new_channels, NULL); out: mutex_unlock(&priv->state_lock); return err; @@ -3049,26 +3054,31 @@ typedef int (*mlx5e_feature_handler)(struct net_device *netdev, bool enable); static int set_feature_lro(struct net_device *netdev, bool enable) { struct mlx5e_priv *priv = netdev_priv(netdev); - bool was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); - int err; + struct mlx5e_channels new_channels = {}; + int err = 0; + bool reset; mutex_lock(&priv->state_lock); - if (was_opened && (priv->channels.params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST)) - mlx5e_close_locked(priv->netdev); + reset = (priv->channels.params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST); + reset = reset && test_bit(MLX5E_STATE_OPENED, &priv->state); - priv->channels.params.lro_en = enable; - err = mlx5e_modify_tirs_lro(priv); - if (err) { - netdev_err(netdev, "lro modify failed, %d\n", err); - priv->channels.params.lro_en = !enable; + new_channels.params = priv->channels.params; + new_channels.params.lro_en = enable; + + if (!reset) { + priv->channels.params = new_channels.params; + err = mlx5e_modify_tirs_lro(priv); + goto out; } - if (was_opened && (priv->channels.params.rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST)) - mlx5e_open_locked(priv->netdev); + err = mlx5e_open_channels(priv, &new_channels); + if (err) + goto out; + mlx5e_switch_priv_channels(priv, &new_channels, mlx5e_modify_tirs_lro); +out: mutex_unlock(&priv->state_lock); - return err; } @@ -3191,7 +3201,8 @@ static int mlx5e_set_features(struct net_device *netdev, static int mlx5e_change_mtu(struct net_device *netdev, int new_mtu) { struct mlx5e_priv *priv = netdev_priv(netdev); - bool was_opened; + struct mlx5e_channels new_channels = {}; + int curr_mtu; int err = 0; bool reset; @@ -3201,18 +3212,27 @@ static int mlx5e_change_mtu(struct net_device *netdev, int new_mtu) (priv->channels.params.rq_wq_type != MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ); - was_opened = test_bit(MLX5E_STATE_OPENED, &priv->state); - if (was_opened && reset) - mlx5e_close_locked(netdev); + reset = reset && test_bit(MLX5E_STATE_OPENED, &priv->state); + curr_mtu = netdev->mtu; netdev->mtu = new_mtu; - mlx5e_set_dev_port_mtu(netdev); - if (was_opened && reset) - err = mlx5e_open_locked(netdev); + if (!reset) { + mlx5e_set_dev_port_mtu(priv); + goto out; + } - mutex_unlock(&priv->state_lock); + new_channels.params = priv->channels.params; + err = mlx5e_open_channels(priv, &new_channels); + if (err) { + netdev->mtu = curr_mtu; + goto out; + } + + mlx5e_switch_priv_channels(priv, &new_channels, mlx5e_set_dev_port_mtu); +out: + mutex_unlock(&priv->state_lock); return err; } @@ -4169,7 +4189,7 @@ int mlx5e_attach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev) mlx5_query_port_max_mtu(priv->mdev, &max_mtu, 1); netdev->max_mtu = MLX5E_HW2SW_MTU(max_mtu); - mlx5e_set_dev_port_mtu(netdev); + mlx5e_set_dev_port_mtu(priv); if (profile->enable) profile->enable(priv); -- cgit From c00e51ddbfda73d2be6d61fea2e58aeb57527475 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 24 Mar 2017 15:21:56 -0700 Subject: net: mpls: Don't show nexthop if device has been deleted If the device for a nexthop in a multipath route is deleted, the nexthop is effectively removed from the route. Currently, a route dump still returns the nexhop though without the device set: $ ip -f mpls ro ls 100 nexthopvia inet 10.11.1.2 dev br0 nexthopvia inet 10.100.3.1 dev eth3 $ ip li del br0 $ ip -f mpls ro ls 100 nexthopvia inet 10.11.1.2 dev * dead linkdown nexthopvia inet 10.100.3.1 dev eth3 Since the nexthop is effectively deleted, drop the hop from the route dump. Signed-off-by: David Ahern Acked-by: Roopa Prabhu Signed-off-by: David S. Miller --- net/mpls/af_mpls.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index cd8be8d5e4ad..3861f8dfa9c1 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1769,13 +1769,15 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, goto nla_put_failure; for_nexthops(rt) { + dev = rtnl_dereference(nh->nh_dev); + if (!dev) + continue; + rtnh = nla_reserve_nohdr(skb, sizeof(*rtnh)); if (!rtnh) goto nla_put_failure; - dev = rtnl_dereference(nh->nh_dev); - if (dev) - rtnh->rtnh_ifindex = dev->ifindex; + rtnh->rtnh_ifindex = dev->ifindex; if (nh->nh_flags & RTNH_F_LINKDOWN) { rtnh->rtnh_flags |= RTNH_F_LINKDOWN; linkdown++; -- cgit From 4ea8efadf5690c1ab22f981a5fcff819bd09ff31 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 24 Mar 2017 15:21:57 -0700 Subject: net: mpls: Delete route when all nexthops have been deleted When all devices for all nexthops in a route have been deleted, the route is effectively dead, so remove it. Signed-off-by: David Ahern Acked-by: Roopa Prabhu Signed-off-by: David S. Miller --- net/mpls/af_mpls.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 3861f8dfa9c1..74755920c689 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1299,7 +1299,7 @@ static void mpls_ifdown(struct net_device *dev, int event) struct mpls_route __rcu **platform_label; struct net *net = dev_net(dev); unsigned int nh_flags = RTNH_F_DEAD | RTNH_F_LINKDOWN; - unsigned int alive; + unsigned int alive, deleted; unsigned index; platform_label = rtnl_dereference(net->mpls.platform_label); @@ -1310,6 +1310,7 @@ static void mpls_ifdown(struct net_device *dev, int event) continue; alive = 0; + deleted = 0; change_nexthops(rt) { if (rtnl_dereference(nh->nh_dev) != dev) goto next; @@ -1328,9 +1329,15 @@ static void mpls_ifdown(struct net_device *dev, int event) next: if (!(nh->nh_flags & nh_flags)) alive++; + if (!rtnl_dereference(nh->nh_dev)) + deleted++; } endfor_nexthops(rt); WRITE_ONCE(rt->rt_nhn_alive, alive); + + /* if there are no more nexthops, delete the route */ + if (event == NETDEV_UNREGISTER && deleted == rt->rt_nhn) + mpls_route_update(net, index, NULL, NULL); } } -- cgit From bb37056807cbcf3d5970d9aca48787a3ab9ec217 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sat, 25 Mar 2017 19:39:05 +0100 Subject: net: cris: eth_v10: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/cris/eth_v10.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c index 91c876a0a647..da020418a652 100644 --- a/drivers/net/cris/eth_v10.c +++ b/drivers/net/cris/eth_v10.c @@ -1412,31 +1412,39 @@ e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return rc; } -static int e100_get_settings(struct net_device *dev, - struct ethtool_cmd *cmd) +static int e100_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { struct net_local *np = netdev_priv(dev); + u32 supported; int err; spin_lock_irq(&np->lock); - err = mii_ethtool_gset(&np->mii_if, cmd); + err = mii_ethtool_get_link_ksettings(&np->mii_if, cmd); spin_unlock_irq(&np->lock); /* The PHY may support 1000baseT, but the Etrax100 does not. */ - cmd->supported &= ~(SUPPORTED_1000baseT_Half - | SUPPORTED_1000baseT_Full); + ethtool_convert_link_mode_to_legacy_u32(&supported, + cmd->link_modes.supported); + + supported &= ~(SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full); + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + return err; } -static int e100_set_settings(struct net_device *dev, - struct ethtool_cmd *ecmd) +static int e100_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *ecmd) { - if (ecmd->autoneg == AUTONEG_ENABLE) { + if (ecmd->base.autoneg == AUTONEG_ENABLE) { e100_set_duplex(dev, autoneg); e100_set_speed(dev, 0); } else { - e100_set_duplex(dev, ecmd->duplex == DUPLEX_HALF ? half : full); - e100_set_speed(dev, ecmd->speed == SPEED_10 ? 10: 100); + e100_set_duplex(dev, ecmd->base.duplex == DUPLEX_HALF ? + half : full); + e100_set_speed(dev, ecmd->base.speed == SPEED_10 ? 10 : 100); } return 0; @@ -1459,11 +1467,11 @@ static int e100_nway_reset(struct net_device *dev) } static const struct ethtool_ops e100_ethtool_ops = { - .get_settings = e100_get_settings, - .set_settings = e100_set_settings, .get_drvinfo = e100_get_drvinfo, .nway_reset = e100_nway_reset, .get_link = ethtool_op_get_link, + .get_link_ksettings = e100_get_link_ksettings, + .set_link_ksettings = e100_set_link_ksettings, }; static int -- cgit From 86573f615215a06f85ec33e5d3e951e9158e36ec Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Sun, 26 Mar 2017 22:03:13 +0200 Subject: net: tehuti: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Signed-off-by: David S. Miller --- drivers/net/ethernet/tehuti/tehuti.c | 43 +++++++++++++++--------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/tehuti/tehuti.c b/drivers/net/ethernet/tehuti/tehuti.c index f864fd0663db..711fbbbc4b1f 100644 --- a/drivers/net/ethernet/tehuti/tehuti.c +++ b/drivers/net/ethernet/tehuti/tehuti.c @@ -2124,33 +2124,26 @@ static const char }; /* - * bdx_get_settings - get device-specific settings + * bdx_get_link_ksettings - get device-specific settings * @netdev * @ecmd */ -static int bdx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) -{ - u32 rdintcm; - u32 tdintcm; - struct bdx_priv *priv = netdev_priv(netdev); - - rdintcm = priv->rdintcm; - tdintcm = priv->tdintcm; - - ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); - ecmd->advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE); - ethtool_cmd_speed_set(ecmd, SPEED_10000); - ecmd->duplex = DUPLEX_FULL; - ecmd->port = PORT_FIBRE; - ecmd->transceiver = XCVR_EXTERNAL; /* what does it mean? */ - ecmd->autoneg = AUTONEG_DISABLE; - - /* PCK_TH measures in multiples of FIFO bytes - We translate to packets */ - ecmd->maxtxpkt = - ((GET_PCK_TH(tdintcm) * PCK_TH_MULT) / BDX_TXF_DESC_SZ); - ecmd->maxrxpkt = - ((GET_PCK_TH(rdintcm) * PCK_TH_MULT) / sizeof(struct rxf_desc)); +static int bdx_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *ecmd) +{ + ethtool_link_ksettings_zero_link_mode(ecmd, supported); + ethtool_link_ksettings_add_link_mode(ecmd, supported, + 10000baseT_Full); + ethtool_link_ksettings_add_link_mode(ecmd, supported, FIBRE); + ethtool_link_ksettings_zero_link_mode(ecmd, advertising); + ethtool_link_ksettings_add_link_mode(ecmd, advertising, + 10000baseT_Full); + ethtool_link_ksettings_add_link_mode(ecmd, advertising, FIBRE); + + ecmd->base.speed = SPEED_10000; + ecmd->base.duplex = DUPLEX_FULL; + ecmd->base.port = PORT_FIBRE; + ecmd->base.autoneg = AUTONEG_DISABLE; return 0; } @@ -2384,7 +2377,6 @@ static void bdx_get_ethtool_stats(struct net_device *netdev, static void bdx_set_ethtool_ops(struct net_device *netdev) { static const struct ethtool_ops bdx_ethtool_ops = { - .get_settings = bdx_get_settings, .get_drvinfo = bdx_get_drvinfo, .get_link = ethtool_op_get_link, .get_coalesce = bdx_get_coalesce, @@ -2394,6 +2386,7 @@ static void bdx_set_ethtool_ops(struct net_device *netdev) .get_strings = bdx_get_strings, .get_sset_count = bdx_get_sset_count, .get_ethtool_stats = bdx_get_ethtool_stats, + .get_link_ksettings = bdx_get_link_ksettings, }; netdev->ethtool_ops = &bdx_ethtool_ops; -- cgit From eb996edb03a665d038de7bc318182412e44c52f9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 25 Mar 2017 14:26:39 +0000 Subject: netvsc: fix dereference before null check errors ndev is being checked to see if it is a null pointer however before the null check ndev is being dereferenced; hence there is a potential null pointer dereference bug that needs fixing. Fix this by only dereferencing ndev after the null check. Detected by CoverityScan, CID#1420760, CID#140761 ("Dereference before null check") Signed-off-by: Colin Ian King Reviewed-by: Haiyang Zhang Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index f830bbbd8ad4..f24c2891dd0c 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1135,7 +1135,7 @@ static int netvsc_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, { struct net_device_context *ndc = netdev_priv(dev); struct netvsc_device *ndev = rcu_dereference(ndc->nvdev); - struct rndis_device *rndis_dev = ndev->extension; + struct rndis_device *rndis_dev; int i; if (!ndev) @@ -1144,6 +1144,7 @@ static int netvsc_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, if (hfunc) *hfunc = ETH_RSS_HASH_TOP; /* Toeplitz */ + rndis_dev = ndev->extension; if (indir) { for (i = 0; i < ITAB_NUM; i++) indir[i] = rndis_dev->ind_table[i]; @@ -1160,7 +1161,7 @@ static int netvsc_set_rxfh(struct net_device *dev, const u32 *indir, { struct net_device_context *ndc = netdev_priv(dev); struct netvsc_device *ndev = rtnl_dereference(ndc->nvdev); - struct rndis_device *rndis_dev = ndev->extension; + struct rndis_device *rndis_dev; int i; if (!ndev) @@ -1169,6 +1170,7 @@ static int netvsc_set_rxfh(struct net_device *dev, const u32 *indir, if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) return -EOPNOTSUPP; + rndis_dev = ndev->extension; if (indir) { for (i = 0; i < ITAB_NUM; i++) if (indir[i] >= dev->num_rx_queues) -- cgit From 8570bcd83d9a13f5319a174137ff3cb69735c135 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 27 Mar 2017 08:55:11 +0200 Subject: net: bfin_mac: Remove unused stats member from struct bfin_mac_local The bfin_mac driver keeps its statistics in net_device->stats, so the stats member in struct bfin_mac_local is unused. Remove it, as well as the accompanying comment. Cc: adi-buildroot-devel@lists.sourceforge.net Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/adi/bfin_mac.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/net/ethernet/adi/bfin_mac.h b/drivers/net/ethernet/adi/bfin_mac.h index 8c3b56198e4b..4ad5b9be3f84 100644 --- a/drivers/net/ethernet/adi/bfin_mac.h +++ b/drivers/net/ethernet/adi/bfin_mac.h @@ -68,13 +68,6 @@ struct net_dma_desc_tx { }; struct bfin_mac_local { - /* - * these are things that the kernel wants me to keep, so users - * can find out semi-useless statistics of how well the card is - * performing - */ - struct net_device_stats stats; - spinlock_t lock; int wol; /* Wake On Lan */ -- cgit From 8c2ef1978fd4b818d3269554016bd29bfe880562 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 27 Mar 2017 08:56:15 +0200 Subject: net: ibmveth: Remove unused stats member from struct ibmveth_adapter The ibmveth driver keeps its statistics in net_device->stats, so the stats member in struct ibmveth_adapter is unused. Remove it. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmveth.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/ibm/ibmveth.h b/drivers/net/ethernet/ibm/ibmveth.h index 7acda04d034e..ed8780cca982 100644 --- a/drivers/net/ethernet/ibm/ibmveth.h +++ b/drivers/net/ethernet/ibm/ibmveth.h @@ -146,7 +146,6 @@ struct ibmveth_adapter { struct vio_dev *vdev; struct net_device *netdev; struct napi_struct napi; - struct net_device_stats stats; unsigned int mcastFilterSize; void * buffer_list_addr; void * filter_list_addr; -- cgit From 656455bf19f6a58e3fc5441a4cdcc8f598979329 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 27 Mar 2017 08:56:59 +0200 Subject: net: ibmvnic: Remove unused net_stats member from struct ibmvnic_adapter The ibmvnic driver keeps its statistics in net_device->stats, so the net_stats member in struct ibmvnic_adapter is unused. Remove it. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 10ad259208cb..42ad648c174d 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -953,7 +953,6 @@ struct ibmvnic_adapter { dma_addr_t bounce_buffer_dma; /* Statistics */ - struct net_device_stats net_stats; struct ibmvnic_statistics stats; dma_addr_t stats_token; struct completion stats_done; -- cgit From 402a5bc462d47f0b7c9e8a516c124c9c162fe2aa Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Mon, 27 Mar 2017 11:43:59 +0200 Subject: ipv6: sr: select DST_CACHE by default When CONFIG_IPV6_SEG6_LWTUNNEL is selected, automatically select DST_CACHE. This allows to remove multiple ifdefs. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/Kconfig | 1 + net/ipv6/seg6_iptunnel.c | 18 ------------------ 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index e2afe677a9d9..48c452959d2c 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -307,6 +307,7 @@ config IPV6_SEG6_LWTUNNEL bool "IPv6: Segment Routing Header encapsulation support" depends on IPV6 select LWTUNNEL + select DST_CACHE ---help--- Support for encapsulation of packets within an outer IPv6 header and a Segment Routing Header using the lightweight diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 30ef1d1d182e..a644aaecdfd3 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -26,17 +26,13 @@ #include #include #include -#ifdef CONFIG_DST_CACHE #include -#endif #ifdef CONFIG_IPV6_SEG6_HMAC #include #endif struct seg6_lwt { -#ifdef CONFIG_DST_CACHE struct dst_cache cache; -#endif struct seg6_iptunnel_encap tuninfo[0]; }; @@ -250,17 +246,14 @@ static int seg6_input(struct sk_buff *skb) slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); -#ifdef CONFIG_DST_CACHE preempt_disable(); dst = dst_cache_get(&slwt->cache); preempt_enable(); -#endif skb_dst_drop(skb); if (!dst) { ip6_route_input(skb); -#ifdef CONFIG_DST_CACHE dst = skb_dst(skb); if (!dst->error) { preempt_disable(); @@ -268,7 +261,6 @@ static int seg6_input(struct sk_buff *skb) &ipv6_hdr(skb)->saddr); preempt_enable(); } -#endif } else { skb_dst_set(skb, dst); } @@ -289,11 +281,9 @@ static int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb) slwt = seg6_lwt_lwtunnel(orig_dst->lwtstate); -#ifdef CONFIG_DST_CACHE preempt_disable(); dst = dst_cache_get(&slwt->cache); preempt_enable(); -#endif if (unlikely(!dst)) { struct ipv6hdr *hdr = ipv6_hdr(skb); @@ -312,11 +302,9 @@ static int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb) goto drop; } -#ifdef CONFIG_DST_CACHE preempt_disable(); dst_cache_set_ip6(&slwt->cache, dst, &fl6.saddr); preempt_enable(); -#endif } skb_dst_drop(skb); @@ -380,13 +368,11 @@ static int seg6_build_state(struct nlattr *nla, slwt = seg6_lwt_lwtunnel(newts); -#ifdef CONFIG_DST_CACHE err = dst_cache_init(&slwt->cache, GFP_KERNEL); if (err) { kfree(newts); return err; } -#endif memcpy(&slwt->tuninfo, tuninfo, tuninfo_len); @@ -400,12 +386,10 @@ static int seg6_build_state(struct nlattr *nla, return 0; } -#ifdef CONFIG_DST_CACHE static void seg6_destroy_state(struct lwtunnel_state *lwt) { dst_cache_destroy(&seg6_lwt_lwtunnel(lwt)->cache); } -#endif static int seg6_fill_encap_info(struct sk_buff *skb, struct lwtunnel_state *lwtstate) @@ -439,9 +423,7 @@ static int seg6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) static const struct lwtunnel_encap_ops seg6_iptun_ops = { .build_state = seg6_build_state, -#ifdef CONFIG_DST_CACHE .destroy_state = seg6_destroy_state, -#endif .output = seg6_output, .input = seg6_input, .fill_encap = seg6_fill_encap_info, -- cgit From 1793668c3b8c18ede309dfec30f7c486cca00d28 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 21 Feb 2017 15:55:39 -0800 Subject: i40e/i40evf: Update code to better handle incrementing page count Update the driver code so that we do bulk updates of the page reference count instead of just incrementing it by one reference at a time. The advantage to doing this is that we cut down on atomic operations and this in turn should give us a slight improvement in cycles per packet. In addition if we eventually move this over to using build_skb the gains will be more noticeable. I also found and fixed a store forwarding stall from where we were assigning "*new_buff = *old_buff". By breaking it up into individual copies we can avoid this and as a result the performance is slightly improved. Change-ID: I1d3880dece4133eca3c32423b04a5467321ccc52 Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 25 ++++++++++++++++++------- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 7 ++++++- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 24 ++++++++++++++++++------ drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 7 ++++++- 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 0ca307a6c731..e5c89770cbc2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1154,7 +1154,7 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring) PAGE_SIZE, DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); - __free_pages(rx_bi->page, 0); + __page_frag_cache_drain(rx_bi->page, rx_bi->pagecnt_bias); rx_bi->page = NULL; rx_bi->page_offset = 0; @@ -1299,6 +1299,7 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, bi->dma = dma; bi->page = page; bi->page_offset = 0; + bi->pagecnt_bias = 1; return true; } @@ -1604,7 +1605,10 @@ static void i40e_reuse_rx_page(struct i40e_ring *rx_ring, rx_ring->next_to_alloc = (nta < rx_ring->count) ? nta : 0; /* transfer page from old buffer to new buffer */ - *new_buff = *old_buff; + new_buff->dma = old_buff->dma; + new_buff->page = old_buff->page; + new_buff->page_offset = old_buff->page_offset; + new_buff->pagecnt_bias = old_buff->pagecnt_bias; } /** @@ -1656,6 +1660,7 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, #if (PAGE_SIZE >= 8192) unsigned int last_offset = PAGE_SIZE - I40E_RXBUFFER_2048; #endif + unsigned int pagecnt_bias = rx_buffer->pagecnt_bias--; /* Is any reuse possible? */ if (unlikely(!i40e_page_is_reusable(page))) @@ -1663,7 +1668,7 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, #if (PAGE_SIZE < 8192) /* if we are only owner of page we can reuse it */ - if (unlikely(page_count(page) != 1)) + if (unlikely(page_count(page) != pagecnt_bias)) return false; /* flip page offset to other buffer */ @@ -1676,9 +1681,14 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, return false; #endif - /* Inc ref count on page before passing it up to the stack */ - get_page(page); - + /* If we have drained the page fragment pool we need to update + * the pagecnt_bias and page count so that we fully restock the + * number of references the driver holds. + */ + if (unlikely(pagecnt_bias == 1)) { + page_ref_add(page, USHRT_MAX); + rx_buffer->pagecnt_bias = USHRT_MAX; + } return true; } @@ -1725,7 +1735,6 @@ static bool i40e_add_rx_frag(struct i40e_ring *rx_ring, return true; /* this page cannot be reused so discard it */ - __free_pages(page, 0); return false; } @@ -1819,6 +1828,8 @@ struct sk_buff *i40e_fetch_rx_buffer(struct i40e_ring *rx_ring, /* we are not reusing the buffer so unmap it */ dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, PAGE_SIZE, DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); + __page_frag_cache_drain(rx_buffer->page, + rx_buffer->pagecnt_bias); } /* clear contents of buffer_info */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 49c7b2089d8e..77c3e96f5172 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -258,7 +258,12 @@ struct i40e_tx_buffer { struct i40e_rx_buffer { dma_addr_t dma; struct page *page; - unsigned int page_offset; +#if (BITS_PER_LONG > 32) || (PAGE_SIZE >= 65536) + __u32 page_offset; +#else + __u16 page_offset; +#endif + __u16 pagecnt_bias; }; struct i40e_queue_stats { diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index d7790c08e523..d892922a2ed9 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -526,7 +526,7 @@ void i40evf_clean_rx_ring(struct i40e_ring *rx_ring) PAGE_SIZE, DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); - __free_pages(rx_bi->page, 0); + __page_frag_cache_drain(rx_bi->page, rx_bi->pagecnt_bias); rx_bi->page = NULL; rx_bi->page_offset = 0; @@ -671,6 +671,7 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, bi->dma = dma; bi->page = page; bi->page_offset = 0; + bi->pagecnt_bias = 1; return true; } @@ -966,7 +967,10 @@ static void i40e_reuse_rx_page(struct i40e_ring *rx_ring, rx_ring->next_to_alloc = (nta < rx_ring->count) ? nta : 0; /* transfer page from old buffer to new buffer */ - *new_buff = *old_buff; + new_buff->dma = old_buff->dma; + new_buff->page = old_buff->page; + new_buff->page_offset = old_buff->page_offset; + new_buff->pagecnt_bias = old_buff->pagecnt_bias; } /** @@ -1018,6 +1022,7 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, #if (PAGE_SIZE >= 8192) unsigned int last_offset = PAGE_SIZE - I40E_RXBUFFER_2048; #endif + unsigned int pagecnt_bias = rx_buffer->pagecnt_bias--; /* Is any reuse possible? */ if (unlikely(!i40e_page_is_reusable(page))) @@ -1025,7 +1030,7 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, #if (PAGE_SIZE < 8192) /* if we are only owner of page we can reuse it */ - if (unlikely(page_count(page) != 1)) + if (unlikely(page_count(page) != pagecnt_bias)) return false; /* flip page offset to other buffer */ @@ -1038,8 +1043,14 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, return false; #endif - /* Inc ref count on page before passing it up to the stack */ - get_page(page); + /* If we have drained the page fragment pool we need to update + * the pagecnt_bias and page count so that we fully restock the + * number of references the driver holds. + */ + if (unlikely(pagecnt_bias == 1)) { + page_ref_add(page, USHRT_MAX); + rx_buffer->pagecnt_bias = USHRT_MAX; + } return true; } @@ -1087,7 +1098,6 @@ static bool i40e_add_rx_frag(struct i40e_ring *rx_ring, return true; /* this page cannot be reused so discard it */ - __free_pages(page, 0); return false; } @@ -1181,6 +1191,8 @@ struct sk_buff *i40evf_fetch_rx_buffer(struct i40e_ring *rx_ring, /* we are not reusing the buffer so unmap it */ dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, PAGE_SIZE, DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); + __page_frag_cache_drain(rx_buffer->page, + rx_buffer->pagecnt_bias); } /* clear contents of buffer_info */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 013512124e6a..7b41df1909be 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -244,7 +244,12 @@ struct i40e_tx_buffer { struct i40e_rx_buffer { dma_addr_t dma; struct page *page; - unsigned int page_offset; +#if (BITS_PER_LONG > 32) || (PAGE_SIZE >= 65536) + __u32 page_offset; +#else + __u16 page_offset; +#endif + __u16 pagecnt_bias; }; struct i40e_queue_stats { -- cgit From f25571b576c72f4bfa8b0766b4d582ee9662bcbf Mon Sep 17 00:00:00 2001 From: Harshitha Ramamurthy Date: Tue, 21 Feb 2017 15:55:40 -0800 Subject: i40e: fix configuration of RSS table with DCB There exists a bug in the driver where the calculation of the RSS size was not taking into account the number of traffic classes enabled. This patch factors in the traffic classes both in the initial configuration of the table as well as reconfiguration. Change-ID: I34dcd345ce52faf1d6b9614bea28d450cfd5f621 Signed-off-by: Harshitha Ramamurthy Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 1d8febd721ac..5da990909a88 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -8577,9 +8577,12 @@ static int i40e_pf_config_rss(struct i40e_pf *pf) i40e_write_rx_ctl(hw, I40E_PFQF_CTL_0, reg_val); /* Determine the RSS size of the VSI */ - if (!vsi->rss_size) - vsi->rss_size = min_t(int, pf->alloc_rss_size, - vsi->num_queue_pairs); + if (!vsi->rss_size) { + u16 qcount; + + qcount = vsi->num_queue_pairs / vsi->tc_config.numtc; + vsi->rss_size = min_t(int, pf->alloc_rss_size, qcount); + } if (!vsi->rss_size) return -EINVAL; @@ -8625,6 +8628,8 @@ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count) new_rss_size = min_t(int, queue_count, pf->rss_size_max); if (queue_count != vsi->num_queue_pairs) { + u16 qcount; + vsi->req_queue_pairs = queue_count; i40e_prep_for_reset(pf); @@ -8642,8 +8647,8 @@ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count) } /* Reset vsi->rss_size, as number of enabled queues changed */ - vsi->rss_size = min_t(int, pf->alloc_rss_size, - vsi->num_queue_pairs); + qcount = vsi->num_queue_pairs / vsi->tc_config.numtc; + vsi->rss_size = min_t(int, pf->alloc_rss_size, qcount); i40e_pf_config_rss(pf); } -- cgit From 741b8b832a57402380be79d7d11a59eaf57fff3b Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 21 Feb 2017 15:55:41 -0800 Subject: i40e/i40evf: Fix use after free in Rx cleanup path We need to reset skb back to NULL when we have freed it in the Rx cleanup path. I found one spot where this wasn't occurring so this patch fixes it. Change-ID: Iaca68934200732cd4a63eb0bd83b539c95f8c4dd Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 1 + drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index e5c89770cbc2..9f2c9f1b8e06 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1941,6 +1941,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) */ if (unlikely(i40e_test_staterr(rx_desc, BIT(I40E_RXD_QW1_ERROR_SHIFT)))) { dev_kfree_skb_any(skb); + skb = NULL; continue; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index d892922a2ed9..38f93075f496 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1299,6 +1299,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) */ if (unlikely(i40e_test_staterr(rx_desc, BIT(I40E_RXD_QW1_ERROR_SHIFT)))) { dev_kfree_skb_any(skb); + skb = NULL; continue; } -- cgit From beff3e9d80138e332ca08d909e1648e82764dc0c Mon Sep 17 00:00:00 2001 From: Robert Konklewski Date: Tue, 21 Feb 2017 15:55:42 -0800 Subject: i40e: Fixed race conditions in VF reset First, this patch eliminates IOMMU DMAR Faults caused by VF hardware. This is done by enabling VF hardware only after VSI resources are freed. Otherwise, hardware could DMA into memory that is (or just has been) being freed. Then, the VF driver is activated only after VSI resources have been reallocated. That's because the VF driver can request resources immediately after it's activated. So they need to be ready at that point. The second race condition happens when the OS initiates a VF reset, and then before it's finished modifies VF's settings by changing its MAC, VLAN ID, bandwidth allocation, anti-spoof checking, etc. These functions needed to be blocked while VF is undergoing reset. Otherwise, they could operate on data structures that had just been freed or not yet fully initialized. Change-ID: I43ba5a7ae2c9a1cce3911611ffc4598ae33ae3ff Signed-off-by: Robert Konklewski Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 43 ++++++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index cfe8b78dac0e..d526940ff951 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -809,6 +809,11 @@ static void i40e_free_vf_res(struct i40e_vf *vf) u32 reg_idx, reg; int i, msix_vf; + /* Start by disabling VF's configuration API to prevent the OS from + * accessing the VF's VSI after it's freed / invalidated. + */ + clear_bit(I40E_VF_STAT_INIT, &vf->vf_states); + /* free vsi & disconnect it from the parent uplink */ if (vf->lan_vsi_idx) { i40e_vsi_release(pf->vsi[vf->lan_vsi_idx]); @@ -848,7 +853,6 @@ static void i40e_free_vf_res(struct i40e_vf *vf) /* reset some of the state variables keeping track of the resources */ vf->num_queue_pairs = 0; vf->vf_states = 0; - clear_bit(I40E_VF_STAT_INIT, &vf->vf_states); } /** @@ -939,6 +943,14 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) /* warn the VF */ clear_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states); + /* Disable VF's configuration API during reset. The flag is re-enabled + * in i40e_alloc_vf_res(), when it's safe again to access VF's VSI. + * It's normally disabled in i40e_free_vf_res(), but it's safer + * to do it earlier to give some time to finish to any VF config + * functions that may still be running at this point. + */ + clear_bit(I40E_VF_STAT_INIT, &vf->vf_states); + /* In the case of a VFLR, the HW has already reset the VF and we * just need to clean up, so don't hit the VFRTRIG register. */ @@ -982,11 +994,6 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) if (!rsd) dev_err(&pf->pdev->dev, "VF reset check timeout on VF %d\n", vf->vf_id); - wr32(hw, I40E_VFGEN_RSTAT1(vf->vf_id), I40E_VFR_COMPLETED); - /* clear the reset bit in the VPGEN_VFRTRIG reg */ - reg = rd32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id)); - reg &= ~I40E_VPGEN_VFRTRIG_VFSWR_MASK; - wr32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id), reg); /* On initial reset, we won't have any queues */ if (vf->lan_vsi_idx == 0) @@ -994,8 +1001,24 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) i40e_vsi_stop_rings(pf->vsi[vf->lan_vsi_idx]); complete_reset: - /* reallocate VF resources to reset the VSI state */ + /* free VF resources to begin resetting the VSI state */ i40e_free_vf_res(vf); + + /* Enable hardware by clearing the reset bit in the VPGEN_VFRTRIG reg. + * By doing this we allow HW to access VF memory at any point. If we + * did it any sooner, HW could access memory while it was being freed + * in i40e_free_vf_res(), causing an IOMMU fault. + * + * On the other hand, this needs to be done ASAP, because the VF driver + * is waiting for this to happen and may report a timeout. It's + * harmless, but it gets logged into Guest OS kernel log, so best avoid + * it. + */ + reg = rd32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id)); + reg &= ~I40E_VPGEN_VFRTRIG_VFSWR_MASK; + wr32(hw, I40E_VPGEN_VFRTRIG(vf->vf_id), reg); + + /* reallocate VF resources to finish resetting the VSI state */ if (!i40e_alloc_vf_res(vf)) { int abs_vf_id = vf->vf_id + hw->func_caps.vf_base_id; i40e_enable_vf_mappings(vf); @@ -1006,7 +1029,11 @@ complete_reset: i40e_notify_client_of_vf_reset(pf, abs_vf_id); vf->num_vlan = 0; } - /* tell the VF the reset is done */ + + /* Tell the VF driver the reset is done. This needs to be done only + * after VF has been fully initialized, because the VF driver may + * request resources immediately after setting this flag. + */ wr32(hw, I40E_VFGEN_RSTAT1(vf->vf_id), I40E_VFR_VFACTIVE); i40e_flush(hw); -- cgit From 0a25b7311d856c31dea2a4e92bf88982026d2afb Mon Sep 17 00:00:00 2001 From: Bimmy Pujari Date: Tue, 21 Feb 2017 15:55:45 -0800 Subject: i40e: removed no longer needed delays Removed no longer needed delays. At preproduction stage those delays were needed but now these delays are not needed. Signed-off-by: Bimmy Pujari Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 5da990909a88..0359f60b4792 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4101,8 +4101,6 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable) } } - if (hw->revision_id == 0) - mdelay(50); return ret; } -- cgit From a5b268e4b103e5dea4850ad732b6ed584a5562ea Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 21 Feb 2017 15:55:46 -0800 Subject: i40e/i40evf: Clean-up process_skb_fields This is a minor clean-up to make the i40e/i40evf process_skb_fields function look a little more like what we have in igb. The Rx checksum function called out a need for skb->protocol but I can't see where it actually needs it. I am assuming this is something that was likely refactored out some time ago as the Rx checksum code has gone through a few rewrites. Change-ID: I0b4668a34d90b61b66ded7c7c26e19a3e2d06251 Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 8 +++----- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 9f2c9f1b8e06..a40338fd0126 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1392,8 +1392,6 @@ no_buffers: * @vsi: the VSI we care about * @skb: skb currently being received and modified * @rx_desc: the receive descriptor - * - * skb->protocol must be set before this function is called **/ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, struct sk_buff *skb, @@ -1555,12 +1553,12 @@ void i40e_process_skb_fields(struct i40e_ring *rx_ring, i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); - /* modifies the skb - consumes the enet header */ - skb->protocol = eth_type_trans(skb, rx_ring->netdev); - i40e_rx_checksum(rx_ring->vsi, skb, rx_desc); skb_record_rx_queue(skb, rx_ring->queue_index); + + /* modifies the skb - consumes the enet header */ + skb->protocol = eth_type_trans(skb, rx_ring->netdev); } /** diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 38f93075f496..8915c5598d20 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -764,8 +764,6 @@ no_buffers: * @vsi: the VSI we care about * @skb: skb currently being received and modified * @rx_desc: the receive descriptor - * - * skb->protocol must be set before this function is called **/ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, struct sk_buff *skb, @@ -917,12 +915,12 @@ void i40evf_process_skb_fields(struct i40e_ring *rx_ring, { i40e_rx_hash(rx_ring, rx_desc, skb, rx_ptype); - /* modifies the skb - consumes the enet header */ - skb->protocol = eth_type_trans(skb, rx_ring->netdev); - i40e_rx_checksum(rx_ring->vsi, skb, rx_desc); skb_record_rx_queue(skb, rx_ring->queue_index); + + /* modifies the skb - consumes the enet header */ + skb->protocol = eth_type_trans(skb, rx_ring->netdev); } /** -- cgit From 9eed69a9147c27aeb016c55b30d810b39bf38662 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 21 Feb 2017 15:55:47 -0800 Subject: i40e: Drop FCoE code from core driver files Looking over the code for FCoE it looks like the Rx path has been broken at least since the last major Rx refactor almost a year ago. It seems like FCoE isn't supported for any of the Fortville/Fortpark hardware so there isn't much point in carrying the code around, especially if it is broken and untested. Change-ID: I892de8fa551cb129ce2361e738ff82ce55fa229e Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/Kconfig | 11 - drivers/net/ethernet/intel/i40e/Makefile | 1 - drivers/net/ethernet/intel/i40e/i40e.h | 61 +----- drivers/net/ethernet/intel/i40e/i40e_common.c | 27 --- drivers/net/ethernet/intel/i40e/i40e_debugfs.c | 19 -- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 35 --- drivers/net/ethernet/intel/i40e/i40e_main.c | 261 +---------------------- drivers/net/ethernet/intel/i40e/i40e_osdep.h | 3 - drivers/net/ethernet/intel/i40e/i40e_prototype.h | 3 - drivers/net/ethernet/intel/i40e/i40e_txrx.c | 26 --- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 17 -- drivers/net/ethernet/intel/i40e/i40e_type.h | 138 ------------ drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 12 -- 13 files changed, 2 insertions(+), 612 deletions(-) diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig index 1349b45f014d..1542a2158e96 100644 --- a/drivers/net/ethernet/intel/Kconfig +++ b/drivers/net/ethernet/intel/Kconfig @@ -235,17 +235,6 @@ config I40E_DCB If unsure, say N. -config I40E_FCOE - bool "Fibre Channel over Ethernet (FCoE)" - default n - depends on I40E && DCB && FCOE - ---help--- - Say Y here if you want to use Fibre Channel over Ethernet (FCoE) - in the driver. This will create new netdev for exclusive FCoE - use with XL710 FCoE offloads enabled. - - If unsure, say N. - config I40EVF tristate "Intel(R) XL710 X710 Virtual Function Ethernet support" depends on PCI_MSI diff --git a/drivers/net/ethernet/intel/i40e/Makefile b/drivers/net/ethernet/intel/i40e/Makefile index 3b3c63e54ed6..4f454d364d0d 100644 --- a/drivers/net/ethernet/intel/i40e/Makefile +++ b/drivers/net/ethernet/intel/i40e/Makefile @@ -45,4 +45,3 @@ i40e-objs := i40e_main.o \ i40e_virtchnl_pf.o i40e-$(CONFIG_I40E_DCB) += i40e_dcb.o i40e_dcb_nl.o -i40e-$(CONFIG_I40E_FCOE) += i40e_fcoe.o diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 3133a1a8b8b3..ee298adbb6db 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -56,9 +56,6 @@ #include #include "i40e_type.h" #include "i40e_prototype.h" -#ifdef I40E_FCOE -#include "i40e_fcoe.h" -#endif #include "i40e_client.h" #include "i40e_virtchnl.h" #include "i40e_virtchnl_pf.h" @@ -85,10 +82,6 @@ (((pf)->flags & I40E_FLAG_128_QP_RSS_CAPABLE) ? 128 : 64) #define I40E_FDIR_RING 0 #define I40E_FDIR_RING_COUNT 32 -#ifdef I40E_FCOE -#define I40E_DEFAULT_FCOE 8 /* default number of QPs for FCoE */ -#define I40E_MINIMUM_FCOE 1 /* minimum number of QPs for FCoE */ -#endif /* I40E_FCOE */ #define I40E_MAX_AQ_BUF_SIZE 4096 #define I40E_AQ_LEN 256 #define I40E_AQ_WORK_LIMIT 66 /* max number of VFs + a little */ @@ -347,10 +340,6 @@ struct i40e_pf { u16 num_vmdq_msix; /* num queue vectors per vmdq pool */ u16 num_req_vfs; /* num VFs requested for this VF */ u16 num_vf_qps; /* num queue pairs per VF */ -#ifdef I40E_FCOE - u16 num_fcoe_qps; /* num fcoe queues this PF has set up */ - u16 num_fcoe_msix; /* num queue vectors per fcoe pool */ -#endif /* I40E_FCOE */ u16 num_lan_qps; /* num lan queues this PF has set up */ u16 num_lan_msix; /* num queue vectors for the base PF vsi */ u16 num_fdsb_msix; /* num queue vectors for sideband Fdir */ @@ -411,9 +400,6 @@ struct i40e_pf { #define I40E_FLAG_FDIR_REQUIRES_REINIT BIT_ULL(8) #define I40E_FLAG_NEED_LINK_UPDATE BIT_ULL(9) #define I40E_FLAG_IWARP_ENABLED BIT_ULL(10) -#ifdef I40E_FCOE -#define I40E_FLAG_FCOE_ENABLED BIT_ULL(11) -#endif /* I40E_FCOE */ #define I40E_FLAG_CLEAN_ADMINQ BIT_ULL(14) #define I40E_FLAG_FILTER_SYNC BIT_ULL(15) #define I40E_FLAG_SERVICE_CLIENT_REQUESTED BIT_ULL(16) @@ -461,10 +447,6 @@ struct i40e_pf { */ u64 hw_disabled_flags; -#ifdef I40E_FCOE - struct i40e_fcoe fcoe; - -#endif /* I40E_FCOE */ struct i40e_client_instance *cinst; bool stat_offsets_loaded; struct i40e_hw_port_stats stats; @@ -522,6 +504,7 @@ struct i40e_pf { u32 fcoe_hmc_filt_num; u32 fcoe_hmc_cntx_num; + struct i40e_filter_control_settings filter_settings; struct ptp_clock *ptp_clock; @@ -641,11 +624,6 @@ struct i40e_vsi { struct rtnl_link_stats64 net_stats_offsets; struct i40e_eth_stats eth_stats; struct i40e_eth_stats eth_stats_offsets; -#ifdef I40E_FCOE - struct i40e_fcoe_stats fcoe_stats; - struct i40e_fcoe_stats fcoe_stats_offsets; - bool fcoe_stat_offsets_loaded; -#endif u32 tx_restart; u32 tx_busy; u64 tx_linearize; @@ -918,11 +896,6 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi); struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, u16 uplink, u32 param1); int i40e_vsi_release(struct i40e_vsi *vsi); -#ifdef I40E_FCOE -void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi, - struct i40e_vsi_context *ctxt, - u8 enabled_tc, bool is_add); -#endif void i40e_service_event_schedule(struct i40e_pf *pf); void i40e_notify_client_of_vf_msg(struct i40e_vsi *vsi, u32 vf_id, u8 *msg, u16 len); @@ -982,20 +955,7 @@ static inline void i40e_irq_dynamic_enable(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, bool clearpba); -#ifdef I40E_FCOE -void i40e_get_netdev_stats_struct(struct net_device *netdev, - struct rtnl_link_stats64 *storage); -int i40e_set_mac(struct net_device *netdev, void *p); -void i40e_set_rx_mode(struct net_device *netdev); -#endif int i40e_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd); -#ifdef I40E_FCOE -void i40e_tx_timeout(struct net_device *netdev); -int i40e_vlan_rx_add_vid(struct net_device *netdev, - __always_unused __be16 proto, u16 vid); -int i40e_vlan_rx_kill_vid(struct net_device *netdev, - __always_unused __be16 proto, u16 vid); -#endif int i40e_open(struct net_device *netdev); int i40e_close(struct net_device *netdev); int i40e_vsi_open(struct i40e_vsi *vsi); @@ -1009,25 +969,6 @@ struct i40e_mac_filter *i40e_add_mac_filter(struct i40e_vsi *vsi, int i40e_del_mac_filter(struct i40e_vsi *vsi, const u8 *macaddr); bool i40e_is_vsi_in_vlan(struct i40e_vsi *vsi); struct i40e_mac_filter *i40e_find_mac(struct i40e_vsi *vsi, const u8 *macaddr); -#ifdef I40E_FCOE -int __i40e_setup_tc(struct net_device *netdev, u32 handle, __be16 proto, - struct tc_to_netdev *tc); -void i40e_netpoll(struct net_device *netdev); -int i40e_fcoe_enable(struct net_device *netdev); -int i40e_fcoe_disable(struct net_device *netdev); -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); -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, - union i40e_rx_desc *rx_desc, - struct sk_buff *skb); -void i40e_fcoe_handle_status(struct i40e_ring *rx_ring, - union i40e_rx_desc *rx_desc, u8 prog_id); -#endif /* I40E_FCOE */ void i40e_vlan_stripping_enable(struct i40e_vsi *vsi); #ifdef CONFIG_I40E_DCB void i40e_dcbnl_flush_apps(struct i40e_pf *pf, diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index ece57d6a6e23..95946f41002b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -1088,33 +1088,6 @@ void i40e_pre_tx_queue_cfg(struct i40e_hw *hw, u32 queue, bool enable) wr32(hw, I40E_GLLAN_TXPRE_QDIS(reg_block), reg_val); } -#ifdef I40E_FCOE - -/** - * i40e_get_san_mac_addr - get SAN MAC address - * @hw: pointer to the HW structure - * @mac_addr: pointer to SAN MAC address - * - * Reads the adapter's SAN MAC address from NVM - **/ -i40e_status i40e_get_san_mac_addr(struct i40e_hw *hw, u8 *mac_addr) -{ - struct i40e_aqc_mac_address_read_data addrs; - i40e_status status; - u16 flags = 0; - - status = i40e_aq_mac_address_read(hw, &flags, &addrs, NULL); - if (status) - return status; - - if (flags & I40E_AQC_SAN_ADDR_VALID) - ether_addr_copy(mac_addr, addrs.pf_san_mac); - else - status = I40E_ERR_INVALID_MAC_ADDR; - - return status; -} -#endif /** * i40e_read_pba_string - Reads part number string from EEPROM diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index 267ad2588255..c5f68cc1edcd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -484,25 +484,6 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) vsi->bw_ets_limit_credits[i], vsi->bw_ets_max_quanta[i]); } -#ifdef I40E_FCOE - if (vsi->type == I40E_VSI_FCOE) { - dev_info(&pf->pdev->dev, - " fcoe_stats: rx_packets = %llu, rx_dwords = %llu, rx_dropped = %llu\n", - vsi->fcoe_stats.rx_fcoe_packets, - vsi->fcoe_stats.rx_fcoe_dwords, - vsi->fcoe_stats.rx_fcoe_dropped); - dev_info(&pf->pdev->dev, - " fcoe_stats: tx_packets = %llu, tx_dwords = %llu\n", - vsi->fcoe_stats.tx_fcoe_packets, - vsi->fcoe_stats.tx_fcoe_dwords); - dev_info(&pf->pdev->dev, - " fcoe_stats: bad_crc = %llu, last_error = %llu\n", - vsi->fcoe_stats.fcoe_bad_fccrc, - vsi->fcoe_stats.fcoe_last_error); - dev_info(&pf->pdev->dev, " fcoe_stats: ddp_count = %llu\n", - vsi->fcoe_stats.fcoe_ddp_count); - } -#endif } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 8fac124ebcb5..c8c566a0a6c3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -162,19 +162,6 @@ static const struct i40e_stats i40e_gstrings_stats[] = { I40E_PF_STAT("rx_lpi_count", stats.rx_lpi_count), }; -#ifdef I40E_FCOE -static const struct i40e_stats i40e_gstrings_fcoe_stats[] = { - I40E_VSI_STAT("fcoe_bad_fccrc", fcoe_stats.fcoe_bad_fccrc), - I40E_VSI_STAT("rx_fcoe_dropped", fcoe_stats.rx_fcoe_dropped), - I40E_VSI_STAT("rx_fcoe_packets", fcoe_stats.rx_fcoe_packets), - I40E_VSI_STAT("rx_fcoe_dwords", fcoe_stats.rx_fcoe_dwords), - I40E_VSI_STAT("fcoe_ddp_count", fcoe_stats.fcoe_ddp_count), - I40E_VSI_STAT("fcoe_last_error", fcoe_stats.fcoe_last_error), - I40E_VSI_STAT("tx_fcoe_packets", fcoe_stats.tx_fcoe_packets), - I40E_VSI_STAT("tx_fcoe_dwords", fcoe_stats.tx_fcoe_dwords), -}; - -#endif /* I40E_FCOE */ #define I40E_QUEUE_STATS_LEN(n) \ (((struct i40e_netdev_priv *)netdev_priv((n)))->vsi->num_queue_pairs \ * 2 /* Tx and Rx together */ \ @@ -182,17 +169,9 @@ static const struct i40e_stats i40e_gstrings_fcoe_stats[] = { #define I40E_GLOBAL_STATS_LEN ARRAY_SIZE(i40e_gstrings_stats) #define I40E_NETDEV_STATS_LEN ARRAY_SIZE(i40e_gstrings_net_stats) #define I40E_MISC_STATS_LEN ARRAY_SIZE(i40e_gstrings_misc_stats) -#ifdef I40E_FCOE -#define I40E_FCOE_STATS_LEN ARRAY_SIZE(i40e_gstrings_fcoe_stats) -#define I40E_VSI_STATS_LEN(n) (I40E_NETDEV_STATS_LEN + \ - I40E_FCOE_STATS_LEN + \ - I40E_MISC_STATS_LEN + \ - I40E_QUEUE_STATS_LEN((n))) -#else #define I40E_VSI_STATS_LEN(n) (I40E_NETDEV_STATS_LEN + \ I40E_MISC_STATS_LEN + \ I40E_QUEUE_STATS_LEN((n))) -#endif /* I40E_FCOE */ #define I40E_PFC_STATS_LEN ( \ (FIELD_SIZEOF(struct i40e_pf, stats.priority_xoff_rx) + \ FIELD_SIZEOF(struct i40e_pf, stats.priority_xon_rx) + \ @@ -1530,13 +1509,6 @@ static void i40e_get_ethtool_stats(struct net_device *netdev, data[i++] = (i40e_gstrings_misc_stats[j].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } -#ifdef I40E_FCOE - for (j = 0; j < I40E_FCOE_STATS_LEN; j++) { - p = (char *)vsi + i40e_gstrings_fcoe_stats[j].stat_offset; - data[i++] = (i40e_gstrings_fcoe_stats[j].sizeof_stat == - sizeof(u64)) ? *(u64 *)p : *(u32 *)p; - } -#endif rcu_read_lock(); for (j = 0; j < vsi->num_queue_pairs; j++) { tx_ring = ACCESS_ONCE(vsi->tx_rings[j]); @@ -1624,13 +1596,6 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset, i40e_gstrings_misc_stats[i].stat_string); p += ETH_GSTRING_LEN; } -#ifdef I40E_FCOE - for (i = 0; i < I40E_FCOE_STATS_LEN; i++) { - snprintf(p, ETH_GSTRING_LEN, "%s", - i40e_gstrings_fcoe_stats[i].stat_string); - p += ETH_GSTRING_LEN; - } -#endif for (i = 0; i < vsi->num_queue_pairs; i++) { snprintf(p, ETH_GSTRING_LEN, "tx-%d.tx_packets", i); p += ETH_GSTRING_LEN; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 0359f60b4792..b3520567a12f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -299,11 +299,7 @@ void i40e_service_event_schedule(struct i40e_pf *pf) * device is munged, not just the one netdev port, so go for the full * reset. **/ -#ifdef I40E_FCOE -void i40e_tx_timeout(struct net_device *netdev) -#else static void i40e_tx_timeout(struct net_device *netdev) -#endif { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; @@ -408,10 +404,7 @@ struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi) * Returns the address of the device statistics structure. * The statistics are actually updated from the service task. **/ -#ifndef I40E_FCOE -static -#endif -void i40e_get_netdev_stats_struct(struct net_device *netdev, +static void i40e_get_netdev_stats_struct(struct net_device *netdev, struct rtnl_link_stats64 *stats) { struct i40e_netdev_priv *np = netdev_priv(netdev); @@ -723,55 +716,6 @@ static void i40e_update_veb_stats(struct i40e_veb *veb) veb->stat_offsets_loaded = true; } -#ifdef I40E_FCOE -/** - * i40e_update_fcoe_stats - Update FCoE-specific ethernet statistics counters. - * @vsi: the VSI that is capable of doing FCoE - **/ -static void i40e_update_fcoe_stats(struct i40e_vsi *vsi) -{ - struct i40e_pf *pf = vsi->back; - struct i40e_hw *hw = &pf->hw; - struct i40e_fcoe_stats *ofs; - struct i40e_fcoe_stats *fs; /* device's eth stats */ - int idx; - - if (vsi->type != I40E_VSI_FCOE) - return; - - idx = hw->pf_id + I40E_FCOE_PF_STAT_OFFSET; - fs = &vsi->fcoe_stats; - ofs = &vsi->fcoe_stats_offsets; - - i40e_stat_update32(hw, I40E_GL_FCOEPRC(idx), - vsi->fcoe_stat_offsets_loaded, - &ofs->rx_fcoe_packets, &fs->rx_fcoe_packets); - i40e_stat_update48(hw, I40E_GL_FCOEDWRCH(idx), I40E_GL_FCOEDWRCL(idx), - vsi->fcoe_stat_offsets_loaded, - &ofs->rx_fcoe_dwords, &fs->rx_fcoe_dwords); - i40e_stat_update32(hw, I40E_GL_FCOERPDC(idx), - vsi->fcoe_stat_offsets_loaded, - &ofs->rx_fcoe_dropped, &fs->rx_fcoe_dropped); - i40e_stat_update32(hw, I40E_GL_FCOEPTC(idx), - vsi->fcoe_stat_offsets_loaded, - &ofs->tx_fcoe_packets, &fs->tx_fcoe_packets); - i40e_stat_update48(hw, I40E_GL_FCOEDWTCH(idx), I40E_GL_FCOEDWTCL(idx), - vsi->fcoe_stat_offsets_loaded, - &ofs->tx_fcoe_dwords, &fs->tx_fcoe_dwords); - i40e_stat_update32(hw, I40E_GL_FCOECRC(idx), - vsi->fcoe_stat_offsets_loaded, - &ofs->fcoe_bad_fccrc, &fs->fcoe_bad_fccrc); - i40e_stat_update32(hw, I40E_GL_FCOELAST(idx), - vsi->fcoe_stat_offsets_loaded, - &ofs->fcoe_last_error, &fs->fcoe_last_error); - i40e_stat_update32(hw, I40E_GL_FCOEDDPC(idx), - vsi->fcoe_stat_offsets_loaded, - &ofs->fcoe_ddp_count, &fs->fcoe_ddp_count); - - vsi->fcoe_stat_offsets_loaded = true; -} - -#endif /** * i40e_update_vsi_stats - Update the vsi statistics counters. * @vsi: the VSI to be updated @@ -1129,9 +1073,6 @@ void i40e_update_stats(struct i40e_vsi *vsi) i40e_update_pf_stats(pf); i40e_update_vsi_stats(vsi); -#ifdef I40E_FCOE - i40e_update_fcoe_stats(vsi); -#endif } /** @@ -1562,11 +1503,7 @@ int i40e_del_mac_filter(struct i40e_vsi *vsi, const u8 *macaddr) * * Returns 0 on success, negative on failure **/ -#ifdef I40E_FCOE -int i40e_set_mac(struct net_device *netdev, void *p) -#else static int i40e_set_mac(struct net_device *netdev, void *p) -#endif { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; @@ -1626,17 +1563,10 @@ static int i40e_set_mac(struct net_device *netdev, void *p) * * Setup VSI queue mapping for enabled traffic classes. **/ -#ifdef I40E_FCOE -void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi, - struct i40e_vsi_context *ctxt, - u8 enabled_tc, - bool is_add) -#else static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi, struct i40e_vsi_context *ctxt, u8 enabled_tc, bool is_add) -#endif { struct i40e_pf *pf = vsi->back; u16 sections = 0; @@ -1686,11 +1616,6 @@ static void i40e_vsi_setup_queue_map(struct i40e_vsi *vsi, qcount = min_t(int, pf->alloc_rss_size, num_tc_qps); break; -#ifdef I40E_FCOE - case I40E_VSI_FCOE: - qcount = num_tc_qps; - break; -#endif case I40E_VSI_FDIR: case I40E_VSI_SRIOV: case I40E_VSI_VMDQ2: @@ -1800,11 +1725,7 @@ static int i40e_addr_unsync(struct net_device *netdev, const u8 *addr) * i40e_set_rx_mode - NDO callback to set the netdev filters * @netdev: network interface device structure **/ -#ifdef I40E_FCOE -void i40e_set_rx_mode(struct net_device *netdev) -#else static void i40e_set_rx_mode(struct net_device *netdev) -#endif { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; @@ -2702,13 +2623,8 @@ void i40e_vsi_kill_vlan(struct i40e_vsi *vsi, u16 vid) * * net_device_ops implementation for adding vlan ids **/ -#ifdef I40E_FCOE -int i40e_vlan_rx_add_vid(struct net_device *netdev, - __always_unused __be16 proto, u16 vid) -#else static int i40e_vlan_rx_add_vid(struct net_device *netdev, __always_unused __be16 proto, u16 vid) -#endif { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; @@ -2739,13 +2655,8 @@ static int i40e_vlan_rx_add_vid(struct net_device *netdev, * * net_device_ops implementation for removing vlan ids **/ -#ifdef I40E_FCOE -int i40e_vlan_rx_kill_vid(struct net_device *netdev, - __always_unused __be16 proto, u16 vid) -#else static int i40e_vlan_rx_kill_vid(struct net_device *netdev, __always_unused __be16 proto, u16 vid) -#endif { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; @@ -2915,9 +2826,6 @@ static int i40e_vsi_setup_rx_resources(struct i40e_vsi *vsi) for (i = 0; i < vsi->num_queue_pairs && !err; i++) err = i40e_setup_rx_descriptors(vsi->rx_rings[i]); -#ifdef I40E_FCOE - i40e_fcoe_setup_ddp_resources(vsi); -#endif return err; } @@ -2937,9 +2845,6 @@ static void i40e_vsi_free_rx_resources(struct i40e_vsi *vsi) for (i = 0; i < vsi->num_queue_pairs; i++) if (vsi->rx_rings[i] && vsi->rx_rings[i]->desc) i40e_free_rx_resources(vsi->rx_rings[i]); -#ifdef I40E_FCOE - i40e_fcoe_free_ddp_resources(vsi); -#endif } /** @@ -3010,9 +2915,6 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) tx_ctx.qlen = ring->count; tx_ctx.fd_ena = !!(vsi->back->flags & (I40E_FLAG_FD_SB_ENABLED | I40E_FLAG_FD_ATR_ENABLED)); -#ifdef I40E_FCOE - tx_ctx.fc_ena = (vsi->type == I40E_VSI_FCOE); -#endif tx_ctx.timesync_ena = !!(vsi->back->flags & I40E_FLAG_PTP); /* FDIR VSI tx ring can still use RS bit and writebacks */ if (vsi->type != I40E_VSI_FDIR) @@ -3115,9 +3017,6 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) rx_ctx.l2tsel = 1; /* this controls whether VLAN is stripped from inner headers */ rx_ctx.showiv = 0; -#ifdef I40E_FCOE - rx_ctx.fc_ena = (vsi->type == I40E_VSI_FCOE); -#endif /* set the prefena field to 1 because the manual says to */ rx_ctx.prefena = 1; @@ -3184,15 +3083,6 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi) vsi->rx_buf_len = I40E_RXBUFFER_2048; -#ifdef I40E_FCOE - /* setup rx buffer for FCoE */ - if ((vsi->type == I40E_VSI_FCOE) && - (vsi->back->flags & I40E_FLAG_FCOE_ENABLED)) { - vsi->rx_buf_len = I40E_RXBUFFER_3072; - vsi->max_frame = I40E_RXBUFFER_3072; - } - -#endif /* I40E_FCOE */ /* round up for the chip's needs */ vsi->rx_buf_len = ALIGN(vsi->rx_buf_len, BIT_ULL(I40E_RXQ_CTX_DBUFF_SHIFT)); @@ -3994,11 +3884,7 @@ static int i40e_vsi_request_irq(struct i40e_vsi *vsi, char *basename) * 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. **/ -#ifdef I40E_FCOE -void i40e_netpoll(struct net_device *netdev) -#else static void i40e_netpoll(struct net_device *netdev) -#endif { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; @@ -5218,20 +5104,12 @@ static void i40e_dcb_reconfigure(struct i40e_pf *pf) continue; /* - Enable all TCs for the LAN VSI -#ifdef I40E_FCOE - * - For FCoE VSI only enable the TC configured - * as per the APP TLV -#endif * - For all others keep them at TC0 for now */ if (v == pf->lan_vsi) tc_map = i40e_pf_get_tc_map(pf); else tc_map = I40E_DEFAULT_TRAFFIC_CLASS; -#ifdef I40E_FCOE - if (pf->vsi[v]->type == I40E_VSI_FCOE) - tc_map = i40e_get_fcoe_tc_map(pf); -#endif /* #ifdef I40E_FCOE */ ret = i40e_vsi_config_tc(pf->vsi[v], tc_map); if (ret) { @@ -5595,13 +5473,8 @@ exit: return ret; } -#ifdef I40E_FCOE -int __i40e_setup_tc(struct net_device *netdev, u32 handle, __be16 proto, - struct tc_to_netdev *tc) -#else static int __i40e_setup_tc(struct net_device *netdev, u32 handle, __be16 proto, struct tc_to_netdev *tc) -#endif { if (tc->type != TC_SETUP_MQPRIO) return -EINVAL; @@ -6314,9 +6187,6 @@ static void i40e_vsi_link_event(struct i40e_vsi *vsi, bool link_up) switch (vsi->type) { case I40E_VSI_MAIN: -#ifdef I40E_FCOE - case I40E_VSI_FCOE: -#endif if (!vsi->netdev || !vsi->netdev_registered) break; @@ -7118,10 +6988,6 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) /* Continue without DCB enabled */ } #endif /* CONFIG_I40E_DCB */ -#ifdef I40E_FCOE - i40e_init_pf_fcoe(pf); - -#endif /* do basic switch setup */ ret = i40e_setup_pf_switch(pf, reinit); if (ret) @@ -7526,15 +7392,6 @@ static int i40e_set_num_rings_in_vsi(struct i40e_vsi *vsi) I40E_REQ_DESCRIPTOR_MULTIPLE); break; -#ifdef I40E_FCOE - case I40E_VSI_FCOE: - vsi->alloc_queue_pairs = pf->num_fcoe_qps; - vsi->num_desc = ALIGN(I40E_DEFAULT_NUM_DESCRIPTORS, - I40E_REQ_DESCRIPTOR_MULTIPLE); - vsi->num_q_vectors = pf->num_fcoe_msix; - break; - -#endif /* I40E_FCOE */ default: WARN_ON(1); return -ENODATA; @@ -7870,9 +7727,6 @@ static int i40e_init_msix(struct i40e_pf *pf) * - assumes symmetric Tx/Rx pairing * - The number of VMDq pairs * - The CPU count within the NUMA node if iWARP is enabled -#ifdef I40E_FCOE - * - The number of FCOE qps. -#endif * Once we count this up, try the request. * * If we can't get what we want, we'll simplify to nearly nothing @@ -7909,20 +7763,6 @@ static int i40e_init_msix(struct i40e_pf *pf) } } -#ifdef I40E_FCOE - /* can we reserve enough for FCoE? */ - if (pf->flags & I40E_FLAG_FCOE_ENABLED) { - if (!vectors_left) - pf->num_fcoe_msix = 0; - else if (vectors_left >= pf->num_fcoe_qps) - pf->num_fcoe_msix = pf->num_fcoe_qps; - else - pf->num_fcoe_msix = 1; - v_budget += pf->num_fcoe_msix; - vectors_left -= pf->num_fcoe_msix; - } - -#endif /* can we reserve enough for iWARP? */ if (pf->flags & I40E_FLAG_IWARP_ENABLED) { iwarp_requested = pf->num_iwarp_msix; @@ -8016,10 +7856,6 @@ static int i40e_init_msix(struct i40e_pf *pf) pf->num_vmdq_msix = 1; /* force VMDqs to only one vector */ pf->num_vmdq_vsis = 1; pf->num_vmdq_qps = 1; -#ifdef I40E_FCOE - pf->num_fcoe_qps = 0; - pf->num_fcoe_msix = 0; -#endif /* partition out the remaining vectors */ switch (vec) { @@ -8033,13 +7869,6 @@ static int i40e_init_msix(struct i40e_pf *pf) } else { pf->num_lan_msix = 2; } -#ifdef I40E_FCOE - /* give one vector to FCoE */ - if (pf->flags & I40E_FLAG_FCOE_ENABLED) { - pf->num_lan_msix = 1; - pf->num_fcoe_msix = 1; - } -#endif break; default: if (pf->flags & I40E_FLAG_IWARP_ENABLED) { @@ -8059,13 +7888,6 @@ static int i40e_init_msix(struct i40e_pf *pf) (vec - (pf->num_iwarp_msix + pf->num_vmdq_vsis)), pf->num_lan_msix); pf->num_lan_qps = pf->num_lan_msix; -#ifdef I40E_FCOE - /* give one vector to FCoE */ - if (pf->flags & I40E_FLAG_FCOE_ENABLED) { - pf->num_fcoe_msix = 1; - vec--; - } -#endif break; } } @@ -8086,13 +7908,6 @@ static int i40e_init_msix(struct i40e_pf *pf) dev_info(&pf->pdev->dev, "IWARP disabled, not enough MSI-X vectors\n"); pf->flags &= ~I40E_FLAG_IWARP_ENABLED; } -#ifdef I40E_FCOE - - if ((pf->flags & I40E_FLAG_FCOE_ENABLED) && (pf->num_fcoe_msix == 0)) { - dev_info(&pf->pdev->dev, "FCOE disabled, not enough MSI-X vectors\n"); - pf->flags &= ~I40E_FLAG_FCOE_ENABLED; - } -#endif i40e_debug(&pf->hw, I40E_DEBUG_INIT, "MSI-X vector distribution: PF %d, VMDq %d, FDSB %d, iWARP %d\n", pf->num_lan_msix, @@ -8191,9 +8006,6 @@ static int i40e_init_interrupt_scheme(struct i40e_pf *pf) if (vectors < 0) { pf->flags &= ~(I40E_FLAG_MSIX_ENABLED | I40E_FLAG_IWARP_ENABLED | -#ifdef I40E_FCOE - I40E_FLAG_FCOE_ENABLED | -#endif I40E_FLAG_RSS_ENABLED | I40E_FLAG_DCB_CAPABLE | I40E_FLAG_DCB_ENABLED | @@ -8879,10 +8691,6 @@ static int i40e_sw_init(struct i40e_pf *pf) pf->num_iwarp_msix = (int)num_online_cpus() + 1; } -#ifdef I40E_FCOE - i40e_init_pf_fcoe(pf); - -#endif /* I40E_FCOE */ #ifdef CONFIG_PCI_IOV if (pf->hw.func_caps.num_vfs && pf->hw.partition_id == 1) { pf->num_vf_qps = I40E_DEFAULT_QUEUES_PER_VF; @@ -9409,10 +9217,6 @@ static const struct net_device_ops i40e_netdev_ops = { .ndo_poll_controller = i40e_netpoll, #endif .ndo_setup_tc = __i40e_setup_tc, -#ifdef I40E_FCOE - .ndo_fcoe_enable = i40e_fcoe_enable, - .ndo_fcoe_disable = i40e_fcoe_disable, -#endif .ndo_set_features = i40e_set_features, .ndo_set_vf_mac = i40e_ndo_set_vf_mac, .ndo_set_vf_vlan = i40e_ndo_set_vf_port_vlan, @@ -9546,9 +9350,6 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) netdev->netdev_ops = &i40e_netdev_ops; netdev->watchdog_timeo = 5 * HZ; i40e_set_ethtool_ops(netdev); -#ifdef I40E_FCOE - i40e_fcoe_config_netdev(netdev, vsi); -#endif /* MTU range: 68 - 9706 */ netdev->min_mtu = ETH_MIN_MTU; @@ -9772,16 +9573,6 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) i40e_vsi_setup_queue_map(vsi, &ctxt, enabled_tc, true); break; -#ifdef I40E_FCOE - case I40E_VSI_FCOE: - ret = i40e_fcoe_vsi_init(vsi, &ctxt); - if (ret) { - dev_info(&pf->pdev->dev, "failed to initialize FCoE VSI\n"); - return ret; - } - break; - -#endif /* I40E_FCOE */ case I40E_VSI_IWARP: /* send down message to iWARP */ break; @@ -10198,7 +9989,6 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, } } case I40E_VSI_VMDQ2: - case I40E_VSI_FCOE: ret = i40e_config_netdev(vsi); if (ret) goto err_netdev; @@ -10858,9 +10648,6 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) int queues_left; pf->num_lan_qps = 0; -#ifdef I40E_FCOE - pf->num_fcoe_qps = 0; -#endif /* Find the max queues to be put into basic use. We'll always be * using TC0, whether or not DCB is running, and TC0 will get the @@ -10877,9 +10664,6 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) /* make sure all the fancies are disabled */ pf->flags &= ~(I40E_FLAG_RSS_ENABLED | I40E_FLAG_IWARP_ENABLED | -#ifdef I40E_FCOE - I40E_FLAG_FCOE_ENABLED | -#endif I40E_FLAG_FD_SB_ENABLED | I40E_FLAG_FD_ATR_ENABLED | I40E_FLAG_DCB_CAPABLE | @@ -10896,9 +10680,6 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) pf->flags &= ~(I40E_FLAG_RSS_ENABLED | I40E_FLAG_IWARP_ENABLED | -#ifdef I40E_FCOE - I40E_FLAG_FCOE_ENABLED | -#endif I40E_FLAG_FD_SB_ENABLED | I40E_FLAG_FD_ATR_ENABLED | I40E_FLAG_DCB_ENABLED | @@ -10919,22 +10700,6 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) queues_left -= pf->num_lan_qps; } -#ifdef I40E_FCOE - if (pf->flags & I40E_FLAG_FCOE_ENABLED) { - if (I40E_DEFAULT_FCOE <= queues_left) { - pf->num_fcoe_qps = I40E_DEFAULT_FCOE; - } else if (I40E_MINIMUM_FCOE <= queues_left) { - pf->num_fcoe_qps = I40E_MINIMUM_FCOE; - } else { - pf->num_fcoe_qps = 0; - pf->flags &= ~I40E_FLAG_FCOE_ENABLED; - dev_info(&pf->pdev->dev, "not enough queues for FCoE. FCoE feature will be disabled\n"); - } - - queues_left -= pf->num_fcoe_qps; - } - -#endif if (pf->flags & I40E_FLAG_FD_SB_ENABLED) { if (queues_left > 1) { queues_left -= 1; /* save 1 queue for FD */ @@ -10966,9 +10731,6 @@ static void i40e_determine_queue_usage(struct i40e_pf *pf) pf->num_lan_qps, pf->alloc_rss_size, pf->num_req_vfs, pf->num_vf_qps, pf->num_vmdq_vsis, pf->num_vmdq_qps, queues_left); -#ifdef I40E_FCOE - dev_dbg(&pf->pdev->dev, "fcoe queues = %d\n", pf->num_fcoe_qps); -#endif } /** @@ -11035,10 +10797,6 @@ static void i40e_print_features(struct i40e_pf *pf) i += snprintf(&buf[i], REMAIN(i), " Geneve"); if (pf->flags & I40E_FLAG_PTP) i += snprintf(&buf[i], REMAIN(i), " PTP"); -#ifdef I40E_FCOE - if (pf->flags & I40E_FLAG_FCOE_ENABLED) - i += snprintf(&buf[i], REMAIN(i), " FCOE"); -#endif if (pf->flags & I40E_FLAG_VEB_MODE_ENABLED) i += snprintf(&buf[i], REMAIN(i), " VEB"); else @@ -11292,18 +11050,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) i40e_get_port_mac_addr(hw, hw->mac.port_addr); if (is_valid_ether_addr(hw->mac.port_addr)) pf->flags |= I40E_FLAG_PORT_ID_VALID; -#ifdef I40E_FCOE - err = i40e_get_san_mac_addr(hw, hw->mac.san_addr); - if (err) - dev_info(&pdev->dev, - "(non-fatal) SAN MAC retrieval failed: %d\n", err); - if (!is_valid_ether_addr(hw->mac.san_addr)) { - dev_warn(&pdev->dev, "invalid SAN MAC address %pM, falling back to LAN MAC\n", - hw->mac.san_addr); - ether_addr_copy(hw->mac.san_addr, hw->mac.addr); - } - dev_info(&pf->pdev->dev, "SAN MAC: %pM\n", hw->mac.san_addr); -#endif /* I40E_FCOE */ pci_set_drvdata(pdev, pf); pci_save_state(pdev); @@ -11499,11 +11245,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev_info(&pdev->dev, "Failed to add PF to client API service list: %d\n", err); -#ifdef I40E_FCOE - /* create FCoE interface */ - i40e_fcoe_vsi_setup(pf); - -#endif #define PCI_SPEED_SIZE 8 #define PCI_WIDTH_SIZE 8 /* Devices on the IOSF bus do not have this information diff --git a/drivers/net/ethernet/intel/i40e/i40e_osdep.h b/drivers/net/ethernet/intel/i40e/i40e_osdep.h index fea81ed065db..80e66da6b145 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_osdep.h +++ b/drivers/net/ethernet/intel/i40e/i40e_osdep.h @@ -78,7 +78,4 @@ do { \ } while (0) typedef enum i40e_status_code i40e_status; -#ifdef CONFIG_I40E_FCOE -#define I40E_FCOE -#endif #endif /* _I40E_OSDEP_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index 2551fc827444..dfc5e5901be5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -304,9 +304,6 @@ i40e_status i40e_read_pba_string(struct i40e_hw *hw, u8 *pba_num, u32 pba_num_size); i40e_status i40e_validate_mac_addr(u8 *mac_addr); void i40e_pre_tx_queue_cfg(struct i40e_hw *hw, u32 queue, bool enable); -#ifdef I40E_FCOE -i40e_status i40e_get_san_mac_addr(struct i40e_hw *hw, u8 *mac_addr); -#endif /* prototype for functions used for NVM access */ i40e_status i40e_init_nvm(struct i40e_hw *hw); i40e_status i40e_acquire_nvm(struct i40e_hw *hw, diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index a40338fd0126..2ca8d13baea5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1062,11 +1062,6 @@ static void i40e_clean_programming_status(struct i40e_ring *rx_ring, if (id == I40E_RX_PROG_STATUS_DESC_FD_FILTER_STATUS) i40e_fd_handle_status(rx_ring, rx_desc, id); -#ifdef I40E_FCOE - else if ((id == I40E_RX_PROG_STATUS_DESC_FCOE_CTXT_PROG_STATUS) || - (id == I40E_RX_PROG_STATUS_DESC_FCOE_CTXT_INVL_STATUS)) - i40e_fcoe_handle_status(rx_ring, rx_desc, id); -#endif } /** @@ -1958,15 +1953,6 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) /* populate checksum, VLAN, and protocol */ i40e_process_skb_fields(rx_ring, rx_desc, skb, rx_ptype); -#ifdef I40E_FCOE - if (unlikely( - i40e_rx_is_fcoe(rx_ptype) && - !i40e_fcoe_handle_offload(rx_ring, rx_desc, skb))) { - dev_kfree_skb_any(skb); - continue; - } -#endif - vlan_tag = (qword & BIT(I40E_RX_DESC_STATUS_L2TAG1P_SHIFT)) ? le16_to_cpu(rx_desc->wb.qword0.lo_dword.l2tag1) : 0; @@ -2342,15 +2328,9 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, * Returns error code indicate the frame should be dropped upon error and the * otherwise returns 0 to indicate the flags has been set properly. **/ -#ifdef I40E_FCOE -inline int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, - struct i40e_ring *tx_ring, - u32 *flags) -#else static inline int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, struct i40e_ring *tx_ring, u32 *flags) -#endif { __be16 protocol = skb->protocol; u32 tx_flags = 0; @@ -2858,15 +2838,9 @@ bool __i40e_chk_linearize(struct sk_buff *skb) * @td_cmd: the command field in the descriptor * @td_offset: offset for checksum or crc **/ -#ifdef I40E_FCOE -inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, - struct i40e_tx_buffer *first, u32 tx_flags, - const u8 hdr_len, u32 td_cmd, u32 td_offset) -#else static inline void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, struct i40e_tx_buffer *first, u32 tx_flags, const u8 hdr_len, u32 td_cmd, u32 td_offset) -#endif { unsigned int data_len = skb->data_len; unsigned int size = skb_headlen(skb); diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 77c3e96f5172..eb733726637f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -401,13 +401,6 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring); void i40e_free_tx_resources(struct i40e_ring *tx_ring); void i40e_free_rx_resources(struct i40e_ring *rx_ring); int i40e_napi_poll(struct napi_struct *napi, int budget); -#ifdef I40E_FCOE -void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, - struct i40e_tx_buffer *first, u32 tx_flags, - const u8 hdr_len, u32 td_cmd, u32 td_offset); -int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, - struct i40e_ring *tx_ring, u32 *flags); -#endif void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector); u32 i40e_get_tx_pending(struct i40e_ring *ring, bool in_sw); int __i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size); @@ -490,16 +483,6 @@ static inline bool i40e_chk_linearize(struct sk_buff *skb, int count) return count != I40E_MAX_BUFFER_TXD; } -/** - * i40e_rx_is_fcoe - returns true if the Rx packet type is FCoE - * @ptype: the packet type field from Rx descriptor write-back - **/ -static inline bool i40e_rx_is_fcoe(u16 ptype) -{ - return (ptype >= I40E_RX_PTYPE_L2_FCOE_PAY3) && - (ptype <= I40E_RX_PTYPE_L2_FCOE_VFT_FCOTHER); -} - /** * txring_txq - Find the netdev Tx ring based on the i40e Tx ring * @ring: Tx ring to find the netdev equivalent of diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 939f9fdc8f85..9200f2d9c752 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -1213,25 +1213,6 @@ struct i40e_veb_tc_stats { u64 tc_tx_bytes[I40E_MAX_TRAFFIC_CLASS]; }; -#ifdef I40E_FCOE -/* Statistics collected per function for FCoE */ -struct i40e_fcoe_stats { - u64 rx_fcoe_packets; /* fcoeprc */ - u64 rx_fcoe_dwords; /* focedwrc */ - u64 rx_fcoe_dropped; /* fcoerpdc */ - u64 tx_fcoe_packets; /* fcoeptc */ - u64 tx_fcoe_dwords; /* focedwtc */ - u64 fcoe_bad_fccrc; /* fcoecrc */ - u64 fcoe_last_error; /* fcoelast */ - u64 fcoe_ddp_count; /* fcoeddpc */ -}; - -/* offset to per function FCoE statistics block */ -#define I40E_FCOE_VF_STAT_OFFSET 0 -#define I40E_FCOE_PF_STAT_OFFSET 128 -#define I40E_FCOE_STAT_MAX (I40E_FCOE_PF_STAT_OFFSET + I40E_MAX_PF) - -#endif /* Statistics collected by the MAC */ struct i40e_hw_port_stats { /* eth stats collected by the port */ @@ -1319,125 +1300,6 @@ struct i40e_hw_port_stats { #define I40E_SRRD_SRCTL_ATTEMPTS 100000 -#ifdef I40E_FCOE -/* FCoE Tx context descriptor - Use the i40e_tx_context_desc struct */ - -enum i40E_fcoe_tx_ctx_desc_cmd_bits { - I40E_FCOE_TX_CTX_DESC_OPCODE_SINGLE_SEND = 0x00, /* 4 BITS */ - I40E_FCOE_TX_CTX_DESC_OPCODE_TSO_FC_CLASS2 = 0x01, /* 4 BITS */ - I40E_FCOE_TX_CTX_DESC_OPCODE_TSO_FC_CLASS3 = 0x05, /* 4 BITS */ - I40E_FCOE_TX_CTX_DESC_OPCODE_ETSO_FC_CLASS2 = 0x02, /* 4 BITS */ - I40E_FCOE_TX_CTX_DESC_OPCODE_ETSO_FC_CLASS3 = 0x06, /* 4 BITS */ - I40E_FCOE_TX_CTX_DESC_OPCODE_DWO_FC_CLASS2 = 0x03, /* 4 BITS */ - I40E_FCOE_TX_CTX_DESC_OPCODE_DWO_FC_CLASS3 = 0x07, /* 4 BITS */ - I40E_FCOE_TX_CTX_DESC_OPCODE_DDP_CTX_INVL = 0x08, /* 4 BITS */ - I40E_FCOE_TX_CTX_DESC_OPCODE_DWO_CTX_INVL = 0x09, /* 4 BITS */ - I40E_FCOE_TX_CTX_DESC_RELOFF = 0x10, - I40E_FCOE_TX_CTX_DESC_CLRSEQ = 0x20, - I40E_FCOE_TX_CTX_DESC_DIFENA = 0x40, - I40E_FCOE_TX_CTX_DESC_IL2TAG2 = 0x80 -}; - -/* FCoE DDP Context descriptor */ -struct i40e_fcoe_ddp_context_desc { - __le64 rsvd; - __le64 type_cmd_foff_lsize; -}; - -#define I40E_FCOE_DDP_CTX_QW1_DTYPE_SHIFT 0 -#define I40E_FCOE_DDP_CTX_QW1_DTYPE_MASK (0xFULL << \ - I40E_FCOE_DDP_CTX_QW1_DTYPE_SHIFT) - -#define I40E_FCOE_DDP_CTX_QW1_CMD_SHIFT 4 -#define I40E_FCOE_DDP_CTX_QW1_CMD_MASK (0xFULL << \ - I40E_FCOE_DDP_CTX_QW1_CMD_SHIFT) - -enum i40e_fcoe_ddp_ctx_desc_cmd_bits { - I40E_FCOE_DDP_CTX_DESC_BSIZE_512B = 0x00, /* 2 BITS */ - I40E_FCOE_DDP_CTX_DESC_BSIZE_4K = 0x01, /* 2 BITS */ - I40E_FCOE_DDP_CTX_DESC_BSIZE_8K = 0x02, /* 2 BITS */ - I40E_FCOE_DDP_CTX_DESC_BSIZE_16K = 0x03, /* 2 BITS */ - I40E_FCOE_DDP_CTX_DESC_DIFENA = 0x04, /* 1 BIT */ - I40E_FCOE_DDP_CTX_DESC_LASTSEQH = 0x08, /* 1 BIT */ -}; - -#define I40E_FCOE_DDP_CTX_QW1_FOFF_SHIFT 16 -#define I40E_FCOE_DDP_CTX_QW1_FOFF_MASK (0x3FFFULL << \ - I40E_FCOE_DDP_CTX_QW1_FOFF_SHIFT) - -#define I40E_FCOE_DDP_CTX_QW1_LSIZE_SHIFT 32 -#define I40E_FCOE_DDP_CTX_QW1_LSIZE_MASK (0x3FFFULL << \ - I40E_FCOE_DDP_CTX_QW1_LSIZE_SHIFT) - -/* FCoE DDP/DWO Queue Context descriptor */ -struct i40e_fcoe_queue_context_desc { - __le64 dmaindx_fbase; /* 0:11 DMAINDX, 12:63 FBASE */ - __le64 flen_tph; /* 0:12 FLEN, 13:15 TPH */ -}; - -#define I40E_FCOE_QUEUE_CTX_QW0_DMAINDX_SHIFT 0 -#define I40E_FCOE_QUEUE_CTX_QW0_DMAINDX_MASK (0xFFFULL << \ - I40E_FCOE_QUEUE_CTX_QW0_DMAINDX_SHIFT) - -#define I40E_FCOE_QUEUE_CTX_QW0_FBASE_SHIFT 12 -#define I40E_FCOE_QUEUE_CTX_QW0_FBASE_MASK (0xFFFFFFFFFFFFFULL << \ - I40E_FCOE_QUEUE_CTX_QW0_FBASE_SHIFT) - -#define I40E_FCOE_QUEUE_CTX_QW1_FLEN_SHIFT 0 -#define I40E_FCOE_QUEUE_CTX_QW1_FLEN_MASK (0x1FFFULL << \ - I40E_FCOE_QUEUE_CTX_QW1_FLEN_SHIFT) - -#define I40E_FCOE_QUEUE_CTX_QW1_TPH_SHIFT 13 -#define I40E_FCOE_QUEUE_CTX_QW1_TPH_MASK (0x7ULL << \ - I40E_FCOE_QUEUE_CTX_QW1_FLEN_SHIFT) - -enum i40e_fcoe_queue_ctx_desc_tph_bits { - I40E_FCOE_QUEUE_CTX_DESC_TPHRDESC = 0x1, - I40E_FCOE_QUEUE_CTX_DESC_TPHDATA = 0x2 -}; - -#define I40E_FCOE_QUEUE_CTX_QW1_RECIPE_SHIFT 30 -#define I40E_FCOE_QUEUE_CTX_QW1_RECIPE_MASK (0x3ULL << \ - I40E_FCOE_QUEUE_CTX_QW1_RECIPE_SHIFT) - -/* FCoE DDP/DWO Filter Context descriptor */ -struct i40e_fcoe_filter_context_desc { - __le32 param; - __le16 seqn; - - /* 48:51(0:3) RSVD, 52:63(4:15) DMAINDX */ - __le16 rsvd_dmaindx; - - /* 0:7 FLAGS, 8:52 RSVD, 53:63 LANQ */ - __le64 flags_rsvd_lanq; -}; - -#define I40E_FCOE_FILTER_CTX_QW0_DMAINDX_SHIFT 4 -#define I40E_FCOE_FILTER_CTX_QW0_DMAINDX_MASK (0xFFF << \ - I40E_FCOE_FILTER_CTX_QW0_DMAINDX_SHIFT) - -enum i40e_fcoe_filter_ctx_desc_flags_bits { - I40E_FCOE_FILTER_CTX_DESC_CTYP_DDP = 0x00, - I40E_FCOE_FILTER_CTX_DESC_CTYP_DWO = 0x01, - I40E_FCOE_FILTER_CTX_DESC_ENODE_INIT = 0x00, - I40E_FCOE_FILTER_CTX_DESC_ENODE_RSP = 0x02, - I40E_FCOE_FILTER_CTX_DESC_FC_CLASS2 = 0x00, - I40E_FCOE_FILTER_CTX_DESC_FC_CLASS3 = 0x04 -}; - -#define I40E_FCOE_FILTER_CTX_QW1_FLAGS_SHIFT 0 -#define I40E_FCOE_FILTER_CTX_QW1_FLAGS_MASK (0xFFULL << \ - I40E_FCOE_FILTER_CTX_QW1_FLAGS_SHIFT) - -#define I40E_FCOE_FILTER_CTX_QW1_PCTYPE_SHIFT 8 -#define I40E_FCOE_FILTER_CTX_QW1_PCTYPE_MASK (0x3FULL << \ - I40E_FCOE_FILTER_CTX_QW1_PCTYPE_SHIFT) - -#define I40E_FCOE_FILTER_CTX_QW1_LANQINDX_SHIFT 53 -#define I40E_FCOE_FILTER_CTX_QW1_LANQINDX_MASK (0x7FFULL << \ - I40E_FCOE_FILTER_CTX_QW1_LANQINDX_SHIFT) - -#endif /* I40E_FCOE */ enum i40e_switch_element_types { I40E_SWITCH_ELEMENT_TYPE_MAC = 1, I40E_SWITCH_ELEMENT_TYPE_PF = 2, diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 7b41df1909be..fc959f964919 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -468,19 +468,7 @@ static inline bool i40e_chk_linearize(struct sk_buff *skb, int count) /* we can support up to 8 data buffers for a single send */ return count != I40E_MAX_BUFFER_TXD; } - -/** - * i40e_rx_is_fcoe - returns true if the Rx packet type is FCoE - * @ptype: the packet type field from Rx descriptor write-back - **/ -static inline bool i40e_rx_is_fcoe(u16 ptype) -{ - return (ptype >= I40E_RX_PTYPE_L2_FCOE_PAY3) && - (ptype <= I40E_RX_PTYPE_L2_FCOE_VFT_FCOTHER); -} - /** - * txring_txq - Find the netdev Tx ring based on the i40e Tx ring * @ring: Tx ring to find the netdev equivalent of **/ static inline struct netdev_queue *txring_txq(const struct i40e_ring *ring) -- cgit From c76cb6ed5431756071cc13635db70234597b9cf7 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 21 Feb 2017 15:55:48 -0800 Subject: i40e: Drop FCoE code that always evaluates to false or 0 Since FCoE isn't supported by the i40e products there isn't much point in carrying around code that will always evaluate to false. This patch goes through and strips out the code in several spots so that we don't go around carrying variables and/or code that is always going to evaluate to false or 0. Change-ID: I39d1d779c66c638b75525839db2b6208fdc809d7 Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 3 --- drivers/net/ethernet/intel/i40e/i40e_main.c | 17 +++-------------- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h | 1 - 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index ee298adbb6db..d7e84f99eb2d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -502,9 +502,6 @@ struct i40e_pf { */ u16 dcbx_cap; - u32 fcoe_hmc_filt_num; - u32 fcoe_hmc_cntx_num; - struct i40e_filter_control_settings filter_settings; struct ptp_clock *ptp_clock; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index b3520567a12f..96bedb54701c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4369,14 +4369,6 @@ static void i40e_quiesce_vsi(struct i40e_vsi *vsi) if (test_bit(__I40E_DOWN, &vsi->state)) return; - /* No need to disable FCoE VSI when Tx suspended */ - if ((test_bit(__I40E_PORT_TX_SUSPENDED, &vsi->back->state)) && - vsi->type == I40E_VSI_FCOE) { - dev_dbg(&vsi->back->pdev->dev, - "VSI seid %d skipping FCoE VSI disable\n", vsi->seid); - return; - } - set_bit(__I40E_NEEDS_RESTART, &vsi->state); if (vsi->netdev && netif_running(vsi->netdev)) vsi->netdev->netdev_ops->ndo_stop(vsi->netdev); @@ -4479,8 +4471,7 @@ static int i40e_pf_wait_queues_disabled(struct i40e_pf *pf) int v, ret = 0; for (v = 0; v < pf->hw.func_caps.num_vsis; v++) { - /* No need to wait for FCoE VSI queues */ - if (pf->vsi[v] && pf->vsi[v]->type != I40E_VSI_FCOE) { + if (pf->vsi[v]) { ret = i40e_vsi_wait_queues_disabled(pf->vsi[v]); if (ret) break; @@ -6968,8 +6959,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) goto end_core_reset; ret = i40e_init_lan_hmc(hw, hw->func_caps.num_tx_qp, - hw->func_caps.num_rx_qp, - pf->fcoe_hmc_cntx_num, pf->fcoe_hmc_filt_num); + hw->func_caps.num_rx_qp, 0, 0); if (ret) { dev_info(&pf->pdev->dev, "init_lan_hmc failed: %d\n", ret); goto end_core_reset; @@ -11014,8 +11004,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } err = i40e_init_lan_hmc(hw, hw->func_caps.num_tx_qp, - hw->func_caps.num_rx_qp, - pf->fcoe_hmc_cntx_num, pf->fcoe_hmc_filt_num); + hw->func_caps.num_rx_qp, 0, 0); if (err) { dev_info(&pdev->dev, "init_lan_hmc failed: %d\n", err); goto err_init_lan_hmc; diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h index 4012d069939a..37af437daa5d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h @@ -87,7 +87,6 @@ struct i40e_vf { u16 stag; struct i40e_virtchnl_ether_addr default_lan_addr; - struct i40e_virtchnl_ether_addr default_fcoe_addr; u16 port_vlan_id; bool pf_set_mac; /* The VMM admin set the VF MAC address */ bool trusted; -- cgit From 703ba88548082f91970ee91c9fb64ab582e391cd Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 20 Mar 2017 12:03:03 +0000 Subject: i40evf: dereference VSI after VSI has been null checked VSI is being dereferenced before the VSI null check; if VSI is null we end up with a null pointer dereference. Fix this by performing VSI deference after the VSI null check. Also remove the need for using adapter by using vsi->back->cinst. Detected by CoverityScan, CID#1419696, CID#1419697 ("Dereference before null check") Fixes: ed0e894de7c133 ("i40evf: add client interface") Signed-off-by: Colin Ian King Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_client.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_client.c b/drivers/net/ethernet/intel/i40evf/i40evf_client.c index 5b43e5b6e2eb..ee737680a0e9 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_client.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_client.c @@ -34,12 +34,12 @@ static struct i40e_ops i40evf_lan_ops = { **/ void i40evf_notify_client_message(struct i40e_vsi *vsi, u8 *msg, u16 len) { - struct i40evf_adapter *adapter = vsi->back; - struct i40e_client_instance *cinst = adapter->cinst; + struct i40e_client_instance *cinst; if (!vsi) return; + cinst = vsi->back->cinst; if (!cinst || !cinst->client || !cinst->client->ops || !cinst->client->ops->virtchnl_receive) { dev_dbg(&vsi->back->pdev->dev, @@ -58,12 +58,13 @@ void i40evf_notify_client_message(struct i40e_vsi *vsi, u8 *msg, u16 len) **/ void i40evf_notify_client_l2_params(struct i40e_vsi *vsi) { - struct i40evf_adapter *adapter = vsi->back; - struct i40e_client_instance *cinst = adapter->cinst; + struct i40e_client_instance *cinst; struct i40e_params params; if (!vsi) return; + + cinst = vsi->back->cinst; memset(¶ms, 0, sizeof(params)); params.mtu = vsi->netdev->mtu; params.link_up = vsi->back->link_up; -- cgit From 7be147dc143a9a0030a3881870425cae03e5dbff Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 20 Mar 2017 16:45:35 -0700 Subject: i40e: initialize params before notifying of l2_param_changes Probably due to some mis-merging fix a bug associated with commits d7ce6422d6e6 ("i40e: don't check params until after checking for client instance", 2017-02-09) and 3140aa9a78c9 ("i40e: KISS the client interface", 2017-03-14) The first commit tried to move the initialization of the params structure so that we didn't bother doing this if we didn't have a client interface. You can already see that it looks fishy because of the indentation. The second commit refactors a bunch of the interface, and incorrectly drops the params initialization. I believe what occurred is that internally the two patches were re-ordered, and the merge conflicts as a result were performed incorrectly. Fix the use of an uninitialized variable by correctly initializing the params variable via i40e_client_get_params(). Reported-by: Colin Ian King Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_client.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.c b/drivers/net/ethernet/intel/i40e/i40e_client.c index a9f0d22a7cf4..191028b1489b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.c +++ b/drivers/net/ethernet/intel/i40e/i40e_client.c @@ -147,6 +147,8 @@ void i40e_notify_client_of_l2_param_changes(struct i40e_vsi *vsi) dev_dbg(&vsi->back->pdev->dev, "Client is not open, abort l2 param change\n"); return; } + memset(¶ms, 0, sizeof(params)); + i40e_client_get_params(vsi, ¶ms); memcpy(&cdev->lan_info.params, ¶ms, sizeof(struct i40e_params)); cdev->client->ops->l2_param_change(&cdev->lan_info, cdev->client, ¶ms); -- cgit From f307668bfcb7e83b6f62bda6a703e09613a00bd0 Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Mon, 27 Mar 2017 11:37:30 -0700 Subject: bonding: split bond_set_slave_link_state into two parts Split the function into two (a) propose (b) commit phase without changing the semantics for the original API. Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- include/net/bonding.h | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/include/net/bonding.h b/include/net/bonding.h index 3c857778a6ca..fb2dd97857c4 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -153,7 +153,8 @@ struct slave { unsigned long last_link_up; unsigned long last_rx; unsigned long target_last_arp_rx[BOND_MAX_ARP_TARGETS]; - s8 link; /* one of BOND_LINK_XXXX */ + s8 link; /* one of BOND_LINK_XXXX */ + s8 link_new_state; /* one of BOND_LINK_XXXX */ s8 new_link; u8 backup:1, /* indicates backup slave. Value corresponds with BOND_STATE_ACTIVE and BOND_STATE_BACKUP */ @@ -504,13 +505,17 @@ static inline bool bond_is_slave_inactive(struct slave *slave) return slave->inactive; } -static inline void bond_set_slave_link_state(struct slave *slave, int state, - bool notify) +static inline void bond_propose_link_state(struct slave *slave, int state) +{ + slave->link_new_state = state; +} + +static inline void bond_commit_link_state(struct slave *slave, bool notify) { - if (slave->link == state) + if (slave->link == slave->link_new_state) return; - slave->link = state; + slave->link = slave->link_new_state; if (notify) { bond_queue_slave_event(slave); bond_lower_state_changed(slave); @@ -523,6 +528,13 @@ static inline void bond_set_slave_link_state(struct slave *slave, int state, } } +static inline void bond_set_slave_link_state(struct slave *slave, int state, + bool notify) +{ + bond_propose_link_state(slave, state); + bond_commit_link_state(slave, notify); +} + static inline void bond_slave_link_notify(struct bonding *bond) { struct list_head *iter; -- cgit From de77ecd4ef02ca783f7762e04e92b3d0964be66b Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Mon, 27 Mar 2017 11:37:33 -0700 Subject: bonding: improve link-status update in mii-monitoring The primary issue is that mii-inspect phase updates link-state and expects changes to be committed during the mii-commit phase. After the inspect phase if it fails to acquire rtnl-mutex, the commit phase (bond_mii_commit) doesn't get to run. This partially updated state stays and makes the internal-state inconsistent. e.g. setup bond0 => slaves: eth1, eth2 eth1 goes DOWN -> UP mii_monitor() mii-inspect() bond_set_slave_link_state(eth1, UP, DontNotify) rtnl_trylock() <- fails! Next mii-monitor round eth1: No change mii_monitor() mii-inspect() eth1->link == current-status (ethtool_ops->get_link) no-change-detected End result: eth1: Link = BOND_LINK_UP Speed = 0xfffff [SpeedUnknown] Duplex = 0xff [DuplexUnknown] This doesn't always happen but for some unlucky machines in a large set of machines it creates problems. The fix for this is to avoid making changes during inspect phase and postpone them until acquiring the rtnl-mutex / invoking commit phase. Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index ba934020dfaa..85999e479916 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2033,8 +2033,7 @@ static int bond_miimon_inspect(struct bonding *bond) if (link_state) continue; - bond_set_slave_link_state(slave, BOND_LINK_FAIL, - BOND_SLAVE_NOTIFY_LATER); + bond_propose_link_state(slave, BOND_LINK_FAIL); slave->delay = bond->params.downdelay; if (slave->delay) { netdev_info(bond->dev, "link status down for %sinterface %s, disabling it in %d ms\n", @@ -2049,8 +2048,7 @@ static int bond_miimon_inspect(struct bonding *bond) case BOND_LINK_FAIL: if (link_state) { /* recovered before downdelay expired */ - bond_set_slave_link_state(slave, BOND_LINK_UP, - BOND_SLAVE_NOTIFY_LATER); + bond_propose_link_state(slave, BOND_LINK_UP); slave->last_link_up = jiffies; netdev_info(bond->dev, "link status up again after %d ms for interface %s\n", (bond->params.downdelay - slave->delay) * @@ -2072,8 +2070,7 @@ static int bond_miimon_inspect(struct bonding *bond) if (!link_state) continue; - bond_set_slave_link_state(slave, BOND_LINK_BACK, - BOND_SLAVE_NOTIFY_LATER); + bond_propose_link_state(slave, BOND_LINK_BACK); slave->delay = bond->params.updelay; if (slave->delay) { @@ -2086,9 +2083,7 @@ static int bond_miimon_inspect(struct bonding *bond) /*FALLTHRU*/ case BOND_LINK_BACK: if (!link_state) { - bond_set_slave_link_state(slave, - BOND_LINK_DOWN, - BOND_SLAVE_NOTIFY_LATER); + bond_propose_link_state(slave, BOND_LINK_DOWN); netdev_info(bond->dev, "link status down again after %d ms for interface %s\n", (bond->params.updelay - slave->delay) * bond->params.miimon, @@ -2225,6 +2220,8 @@ static void bond_mii_monitor(struct work_struct *work) mii_work.work); bool should_notify_peers = false; unsigned long delay; + struct slave *slave; + struct list_head *iter; delay = msecs_to_jiffies(bond->params.miimon); @@ -2245,6 +2242,9 @@ static void bond_mii_monitor(struct work_struct *work) goto re_arm; } + bond_for_each_slave(bond, slave, iter) { + bond_commit_link_state(slave, BOND_SLAVE_NOTIFY_LATER); + } bond_miimon_commit(bond); rtnl_unlock(); /* might sleep, hold no other locks */ -- cgit From c4adfc822bf5d8e97660b6114b5a8892530ce8cb Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Mon, 27 Mar 2017 11:37:35 -0700 Subject: bonding: make speed, duplex setting consistent with link state bond_update_speed_duplex() retrieves speed and duplex settings. There is a possibility of failure in retrieving these values but caller has to assume it's always successful. This leads to having inconsistent slave link settings. If these (speed, duplex) values cannot be retrieved, then keeping the link UP causes problems. The updated bond_update_speed_duplex() returns 0 on success if it retrieves sane values for speed and duplex. On failure it returns 1 and marks the link down. Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 85999e479916..ad317bb63193 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -365,9 +365,10 @@ down: /* Get link speed and duplex from the slave's base driver * using ethtool. If for some reason the call fails or the * values are invalid, set speed and duplex to -1, - * and return. + * and return. Return 1 if speed or duplex settings are + * UNKNOWN; 0 otherwise. */ -static void bond_update_speed_duplex(struct slave *slave) +static int bond_update_speed_duplex(struct slave *slave) { struct net_device *slave_dev = slave->dev; struct ethtool_link_ksettings ecmd; @@ -377,24 +378,27 @@ static void bond_update_speed_duplex(struct slave *slave) slave->duplex = DUPLEX_UNKNOWN; res = __ethtool_get_link_ksettings(slave_dev, &ecmd); - if (res < 0) - return; - - if (ecmd.base.speed == 0 || ecmd.base.speed == ((__u32)-1)) - return; - + if (res < 0) { + slave->link = BOND_LINK_DOWN; + return 1; + } + if (ecmd.base.speed == 0 || ecmd.base.speed == ((__u32)-1)) { + slave->link = BOND_LINK_DOWN; + return 1; + } switch (ecmd.base.duplex) { case DUPLEX_FULL: case DUPLEX_HALF: break; default: - return; + slave->link = BOND_LINK_DOWN; + return 1; } slave->speed = ecmd.base.speed; slave->duplex = ecmd.base.duplex; - return; + return 0; } const char *bond_slave_link_status(s8 link) -- cgit From b5bf0f5b16b9c316c34df9f31d4be8729eb86845 Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Mon, 27 Mar 2017 11:37:37 -0700 Subject: bonding: correctly update link status during mii-commit phase bond_miimon_commit() marks the link UP after attempting to get the speed and duplex settings for the link. There is a possibility that bond_update_speed_duplex() could fail. This is another place where it could result into an inconsistent bonding link state. With this patch the link will be marked UP only if the speed and duplex values retrieved have sane values and processed further. Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index ad317bb63193..6cea964ab70a 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2125,7 +2125,12 @@ static void bond_miimon_commit(struct bonding *bond) continue; case BOND_LINK_UP: - bond_update_speed_duplex(slave); + if (bond_update_speed_duplex(slave)) { + netdev_warn(bond->dev, + "failed to get link speed/duplex for %s\n", + slave->dev->name); + continue; + } bond_set_slave_link_state(slave, BOND_LINK_UP, BOND_SLAVE_NOTIFY_NOW); slave->last_link_up = jiffies; -- cgit From e292dcae1648cd5b5c80031a154f594aa590667f Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Mon, 27 Mar 2017 11:37:40 -0700 Subject: bonding: avoid printing while holding a spinlock Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- drivers/net/bonding/bond_3ad.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 508713b4e533..c5fd4259da33 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -2446,9 +2446,9 @@ void bond_3ad_adapter_speed_duplex_changed(struct slave *slave) spin_lock_bh(&slave->bond->mode_lock); ad_update_actor_keys(port, false); + spin_unlock_bh(&slave->bond->mode_lock); netdev_dbg(slave->bond->dev, "Port %d slave %s changed speed/duplex\n", port->actor_port_number, slave->dev->name); - spin_unlock_bh(&slave->bond->mode_lock); } /** @@ -2492,12 +2492,12 @@ void bond_3ad_handle_link_change(struct slave *slave, char link) agg = __get_first_agg(port); ad_agg_selection_logic(agg, &dummy); + spin_unlock_bh(&slave->bond->mode_lock); + netdev_dbg(slave->bond->dev, "Port %d changed link status to %s\n", port->actor_port_number, link == BOND_LINK_UP ? "UP" : "DOWN"); - spin_unlock_bh(&slave->bond->mode_lock); - /* RTNL is held and mode_lock is released so it's safe * to update slave_array here. */ -- cgit From ecf5bb796b992454c34f555e76c29360ed9b886d Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 24 Jan 2017 20:54:09 +0200 Subject: net/mlx5e: Add prefix for e-switch offloaded TC flow attributes Add esw_ prefix to the flow attributes attached to offloaded e-switch TC flows. This is a pre-step to add attributes to offloaded NIC TC flows. Also, save one pointer space by using gcc's zero size array, this would be beneficial for environments where 100Ks (or Ms) of flows are offloaded. This patch doesn't change any functionality. Signed-off-by: Or Gerlitz Reviewed-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index fade7233dac5..b2501987988b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -58,7 +58,7 @@ struct mlx5e_tc_flow { u8 flags; struct mlx5_flow_handle *rule; struct list_head encap; /* flows sharing the same encap */ - struct mlx5_esw_flow_attr *attr; + struct mlx5_esw_flow_attr esw_attr[0]; }; enum { @@ -173,11 +173,11 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; - mlx5_eswitch_del_offloaded_rule(esw, flow->rule, flow->attr); + mlx5_eswitch_del_offloaded_rule(esw, flow->rule, flow->esw_attr); - mlx5_eswitch_del_vlan_action(esw, flow->attr); + mlx5_eswitch_del_vlan_action(esw, flow->esw_attr); - if (flow->attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) + if (flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) mlx5e_detach_encap(priv, flow); } @@ -1073,7 +1073,7 @@ out_err: static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, struct mlx5e_tc_flow *flow) { - struct mlx5_esw_flow_attr *attr = flow->attr; + struct mlx5_esw_flow_attr *attr = flow->esw_attr; struct ip_tunnel_info *info = NULL; const struct tc_action *a; LIST_HEAD(actions); @@ -1191,11 +1191,10 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, goto err_free; if (flow->flags & MLX5E_TC_FLOW_ESWITCH) { - flow->attr = (struct mlx5_esw_flow_attr *)(flow + 1); err = parse_tc_fdb_actions(priv, f->exts, flow); if (err < 0) goto err_free; - flow->rule = mlx5e_tc_add_fdb_flow(priv, spec, flow->attr); + flow->rule = mlx5e_tc_add_fdb_flow(priv, spec, flow->esw_attr); } else { err = parse_tc_nic_actions(priv, f->exts, &action, &flow_tag); if (err < 0) -- cgit From 3bc4b7bfa01f581f133fc678db9d4541dd794ae5 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 24 Jan 2017 21:04:50 +0200 Subject: net/mlx5e: Add NIC attributes for offloaded TC flows Add structure that contains the attributes related to offloaded NIC flows. Currently it has the actions and flow tag. While here, do xmas tree cleanup of the TC configure function. This patch doesn't change any functionality. Signed-off-by: Or Gerlitz Reviewed-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 51 +++++++++++++++---------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index b2501987988b..2a9df0a0b859 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -48,8 +48,14 @@ #include "eswitch.h" #include "vxlan.h" +struct mlx5_nic_flow_attr { + u32 action; + u32 flow_tag; +}; + enum { MLX5E_TC_FLOW_ESWITCH = BIT(0), + MLX5E_TC_FLOW_NIC = BIT(1), }; struct mlx5e_tc_flow { @@ -58,7 +64,10 @@ struct mlx5e_tc_flow { u8 flags; struct mlx5_flow_handle *rule; struct list_head encap; /* flows sharing the same encap */ - struct mlx5_esw_flow_attr esw_attr[0]; + union { + struct mlx5_esw_flow_attr esw_attr[0]; + struct mlx5_nic_flow_attr nic_attr[0]; + }; }; enum { @@ -72,23 +81,23 @@ enum { static struct mlx5_flow_handle * mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec, - u32 action, u32 flow_tag) + struct mlx5_nic_flow_attr *attr) { struct mlx5_core_dev *dev = priv->mdev; struct mlx5_flow_destination dest = { 0 }; struct mlx5_flow_act flow_act = { - .action = action, - .flow_tag = flow_tag, + .action = attr->action, + .flow_tag = attr->flow_tag, .encap_id = 0, }; struct mlx5_fc *counter = NULL; struct mlx5_flow_handle *rule; bool table_created = false; - if (action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; dest.ft = priv->fs.vlan.ft.t; - } else if (action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { + } else if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { counter = mlx5_fc_create(dev, true); if (IS_ERR(counter)) return ERR_CAST(counter); @@ -651,7 +660,7 @@ static int parse_cls_flower(struct mlx5e_priv *priv, } static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, - u32 *action, u32 *flow_tag) + struct mlx5_nic_flow_attr *attr) { const struct tc_action *a; LIST_HEAD(actions); @@ -659,20 +668,20 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, if (tc_no_actions(exts)) return -EINVAL; - *flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; - *action = 0; + attr->flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; + attr->action = 0; tcf_exts_to_list(exts, &actions); list_for_each_entry(a, &actions, list) { /* Only support a single action per rule */ - if (*action) + if (attr->action) return -EINVAL; if (is_tcf_gact_shot(a)) { - *action |= MLX5_FLOW_CONTEXT_ACTION_DROP; + attr->action |= MLX5_FLOW_CONTEXT_ACTION_DROP; if (MLX5_CAP_FLOWTABLE(priv->mdev, flow_table_properties_nic_receive.flow_counter)) - *action |= MLX5_FLOW_CONTEXT_ACTION_COUNT; + attr->action |= MLX5_FLOW_CONTEXT_ACTION_COUNT; continue; } @@ -685,8 +694,8 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, return -EINVAL; } - *flow_tag = mark; - *action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + attr->flow_tag = mark; + attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; continue; } @@ -1163,17 +1172,19 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, struct tc_cls_flower_offload *f) { + struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5e_tc_table *tc = &priv->fs.tc; - int err, attr_size = 0; - u32 flow_tag, action; - struct mlx5e_tc_flow *flow; struct mlx5_flow_spec *spec; - struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + struct mlx5e_tc_flow *flow; + int attr_size, err = 0; u8 flow_flags = 0; if (esw && esw->mode == SRIOV_OFFLOADS) { flow_flags = MLX5E_TC_FLOW_ESWITCH; attr_size = sizeof(struct mlx5_esw_flow_attr); + } else { + flow_flags = MLX5E_TC_FLOW_NIC; + attr_size = sizeof(struct mlx5_nic_flow_attr); } flow = kzalloc(sizeof(*flow) + attr_size, GFP_KERNEL); @@ -1196,10 +1207,10 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, goto err_free; flow->rule = mlx5e_tc_add_fdb_flow(priv, spec, flow->esw_attr); } else { - err = parse_tc_nic_actions(priv, f->exts, &action, &flow_tag); + err = parse_tc_nic_actions(priv, f->exts, flow->nic_attr); if (err < 0) goto err_free; - flow->rule = mlx5e_tc_add_nic_flow(priv, spec, action, flow_tag); + flow->rule = mlx5e_tc_add_nic_flow(priv, spec, flow->nic_attr); } if (IS_ERR(flow->rule)) { -- cgit From 17091853fc0bb8354283000e065a60b8cc682f5c Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Mon, 13 Mar 2017 08:33:03 +0200 Subject: net/mlx5e: Add intermediate struct for TC flow parsing attributes Add intermediate structure to store attributes parsed from TC filter matching/actions parts which are soon to be configured into the HW. Currently put there the flow matching spec after being parsed. More content to be added in down-stream patch. This patch doesn't change any functionality. Signed-off-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 29 +++++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 2a9df0a0b859..9f900afcd7ea 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -70,6 +70,10 @@ struct mlx5e_tc_flow { }; }; +struct mlx5e_tc_flow_parse_attr { + struct mlx5_flow_spec spec; +}; + enum { MLX5_HEADER_TYPE_VXLAN = 0x0, MLX5_HEADER_TYPE_NVGRE = 0x1, @@ -80,7 +84,7 @@ enum { static struct mlx5_flow_handle * mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, - struct mlx5_flow_spec *spec, + struct mlx5e_tc_flow_parse_attr *parse_attr, struct mlx5_nic_flow_attr *attr) { struct mlx5_core_dev *dev = priv->mdev; @@ -123,8 +127,9 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, table_created = true; } - spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; - rule = mlx5_add_flow_rules(priv->fs.tc.t, spec, &flow_act, &dest, 1); + parse_attr->spec.match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; + rule = mlx5_add_flow_rules(priv->fs.tc.t, &parse_attr->spec, + &flow_act, &dest, 1); if (IS_ERR(rule)) goto err_add_rule; @@ -161,7 +166,7 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv, static struct mlx5_flow_handle * mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, - struct mlx5_flow_spec *spec, + struct mlx5e_tc_flow_parse_attr *parse_attr, struct mlx5_esw_flow_attr *attr) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; @@ -171,7 +176,7 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, if (err) return ERR_PTR(err); - return mlx5_eswitch_add_offloaded_rule(esw, spec, attr); + return mlx5_eswitch_add_offloaded_rule(esw, &parse_attr->spec, attr); } static void mlx5e_detach_encap(struct mlx5e_priv *priv, @@ -1173,8 +1178,8 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, struct tc_cls_flower_offload *f) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + struct mlx5e_tc_flow_parse_attr *parse_attr; struct mlx5e_tc_table *tc = &priv->fs.tc; - struct mlx5_flow_spec *spec; struct mlx5e_tc_flow *flow; int attr_size, err = 0; u8 flow_flags = 0; @@ -1188,8 +1193,8 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, } flow = kzalloc(sizeof(*flow) + attr_size, GFP_KERNEL); - spec = mlx5_vzalloc(sizeof(*spec)); - if (!spec || !flow) { + parse_attr = mlx5_vzalloc(sizeof(*parse_attr)); + if (!parse_attr || !flow) { err = -ENOMEM; goto err_free; } @@ -1197,7 +1202,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, flow->cookie = f->cookie; flow->flags = flow_flags; - err = parse_cls_flower(priv, flow, spec, f); + err = parse_cls_flower(priv, flow, &parse_attr->spec, f); if (err < 0) goto err_free; @@ -1205,12 +1210,12 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, err = parse_tc_fdb_actions(priv, f->exts, flow); if (err < 0) goto err_free; - flow->rule = mlx5e_tc_add_fdb_flow(priv, spec, flow->esw_attr); + flow->rule = mlx5e_tc_add_fdb_flow(priv, parse_attr, flow->esw_attr); } else { err = parse_tc_nic_actions(priv, f->exts, flow->nic_attr); if (err < 0) goto err_free; - flow->rule = mlx5e_tc_add_nic_flow(priv, spec, flow->nic_attr); + flow->rule = mlx5e_tc_add_nic_flow(priv, parse_attr, flow->nic_attr); } if (IS_ERR(flow->rule)) { @@ -1231,7 +1236,7 @@ err_del_rule: err_free: kfree(flow); out: - kvfree(spec); + kvfree(parse_attr); return err; } -- cgit From aa0cbbae5d360ea23a15038d24f6f7c9573eecaf Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 14 Mar 2017 16:49:04 +0200 Subject: net/mlx5e: Properly deal with resource cleanup when adding TC flow fails The code for adding tc fdb flows leaves things half set when it fails in the middle. Currently we are not leaking things (e.g eswitch vlan reference, encap reference and HW resources) since the main code to add flower rules does a cleanup by calling mlx5e_tc_del_flow(). This cleanup further works just b/c we're checking there if the HW rule for the flow we are attempting to delete is valid before touching it, and since under the current possible combinations of supported actions it's okay to go and blidnly deref or delete all the action related resources (encap, vlan). Instead, do things properly, namely make sure that if add flow fails we clean all what was allocated or referenced. Now, the flow delete code can blindly deref/deallocate both the rule and the actions related resources and when more action combinations are introduced (such as the upcoming header re-write) we are fine with clear and robust code. While here, align all of nic/fdb parse actions/add flow functions to get mlx5e_tc_flow struct param and pick the attributes or whatever else needed from there. Signed-off-by: Or Gerlitz Reviewed-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 59 +++++++++++++--------- .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 23 +++++---- 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 9f900afcd7ea..af92d9c1a619 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -85,10 +85,11 @@ enum { static struct mlx5_flow_handle * mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, struct mlx5e_tc_flow_parse_attr *parse_attr, - struct mlx5_nic_flow_attr *attr) + struct mlx5e_tc_flow *flow) { + struct mlx5_nic_flow_attr *attr = flow->nic_attr; struct mlx5_core_dev *dev = priv->mdev; - struct mlx5_flow_destination dest = { 0 }; + struct mlx5_flow_destination dest = {}; struct mlx5_flow_act flow_act = { .action = attr->action, .flow_tag = attr->flow_tag, @@ -152,11 +153,9 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv, { struct mlx5_fc *counter = NULL; - if (!IS_ERR(flow->rule)) { - counter = mlx5_flow_rule_counter(flow->rule); - mlx5_del_flow_rules(flow->rule); - mlx5_fc_destroy(priv->mdev, counter); - } + counter = mlx5_flow_rule_counter(flow->rule); + mlx5_del_flow_rules(flow->rule); + mlx5_fc_destroy(priv->mdev, counter); if (!mlx5e_tc_num_filters(priv) && (priv->fs.tc.t)) { mlx5_destroy_flow_table(priv->fs.tc.t); @@ -164,23 +163,39 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv, } } +static void mlx5e_detach_encap(struct mlx5e_priv *priv, + struct mlx5e_tc_flow *flow); + static struct mlx5_flow_handle * mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, struct mlx5e_tc_flow_parse_attr *parse_attr, - struct mlx5_esw_flow_attr *attr) + struct mlx5e_tc_flow *flow) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + struct mlx5_esw_flow_attr *attr = flow->esw_attr; + struct mlx5_flow_handle *rule; int err; err = mlx5_eswitch_add_vlan_action(esw, attr); - if (err) - return ERR_PTR(err); + if (err) { + rule = ERR_PTR(err); + goto err_add_vlan; + } - return mlx5_eswitch_add_offloaded_rule(esw, &parse_attr->spec, attr); -} + rule = mlx5_eswitch_add_offloaded_rule(esw, &parse_attr->spec, attr); + if (IS_ERR(rule)) + goto err_add_rule; -static void mlx5e_detach_encap(struct mlx5e_priv *priv, - struct mlx5e_tc_flow *flow); + return rule; + +err_add_rule: + mlx5_eswitch_del_vlan_action(esw, attr); +err_add_vlan: + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) + mlx5e_detach_encap(priv, flow); + + return rule; +} static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow) @@ -214,10 +229,6 @@ static void mlx5e_detach_encap(struct mlx5e_priv *priv, } } -/* we get here also when setting rule to the FW failed, etc. It means that the - * flow rule itself might not exist, but some offloading related to the actions - * should be cleaned. - */ static void mlx5e_tc_del_flow(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow) { @@ -665,8 +676,10 @@ static int parse_cls_flower(struct mlx5e_priv *priv, } static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, - struct mlx5_nic_flow_attr *attr) + struct mlx5e_tc_flow_parse_attr *parse_attr, + struct mlx5e_tc_flow *flow) { + struct mlx5_nic_flow_attr *attr = flow->nic_attr; const struct tc_action *a; LIST_HEAD(actions); @@ -1210,17 +1223,17 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, err = parse_tc_fdb_actions(priv, f->exts, flow); if (err < 0) goto err_free; - flow->rule = mlx5e_tc_add_fdb_flow(priv, parse_attr, flow->esw_attr); + flow->rule = mlx5e_tc_add_fdb_flow(priv, parse_attr, flow); } else { - err = parse_tc_nic_actions(priv, f->exts, flow->nic_attr); + err = parse_tc_nic_actions(priv, f->exts, parse_attr, flow); if (err < 0) goto err_free; - flow->rule = mlx5e_tc_add_nic_flow(priv, parse_attr, flow->nic_attr); + flow->rule = mlx5e_tc_add_nic_flow(priv, parse_attr, flow); } if (IS_ERR(flow->rule)) { err = PTR_ERR(flow->rule); - goto err_del_rule; + goto err_free; } err = rhashtable_insert_fast(&tc->ht, &flow->node, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 307ec6c5fd3b..415a50150763 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -68,8 +68,10 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, } if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_COUNT) { counter = mlx5_fc_create(esw->dev, true); - if (IS_ERR(counter)) - return ERR_CAST(counter); + if (IS_ERR(counter)) { + rule = ERR_CAST(counter); + goto err_counter_alloc; + } dest[i].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; dest[i].counter = counter; i++; @@ -92,11 +94,16 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, rule = mlx5_add_flow_rules((struct mlx5_flow_table *)esw->fdb_table.fdb, spec, &flow_act, dest, i); if (IS_ERR(rule)) - mlx5_fc_destroy(esw->dev, counter); + goto err_add_rule; else esw->offloads.num_flows++; return rule; + +err_add_rule: + mlx5_fc_destroy(esw->dev, counter); +err_counter_alloc: + return rule; } void @@ -106,12 +113,10 @@ mlx5_eswitch_del_offloaded_rule(struct mlx5_eswitch *esw, { struct mlx5_fc *counter = NULL; - if (!IS_ERR(rule)) { - counter = mlx5_flow_rule_counter(rule); - mlx5_del_flow_rules(rule); - mlx5_fc_destroy(esw->dev, counter); - esw->offloads.num_flows--; - } + counter = mlx5_flow_rule_counter(rule); + mlx5_del_flow_rules(rule); + mlx5_fc_destroy(esw->dev, counter); + esw->offloads.num_flows--; } static int esw_set_global_vlan_pop(struct mlx5_eswitch *esw, u8 val) -- cgit From e753b2b50dc3c6582e9d5971555693db41a6d821 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 1 Feb 2017 19:01:18 +0200 Subject: net/mlx5: Add helper to initialize a flow steering actions struct instance There are bunch of places in the code where the intermediate struct that keeps the elements related to flow actions is initialized with the same default values. Put that into a small DECLARE type helper. This patch doesn't change any functionality. Signed-off-by: Or Gerlitz Reviewed-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c | 14 +++----------- drivers/net/ethernet/mellanox/mlx5/core/en_fs.c | 18 +++--------------- include/linux/mlx5/fs.h | 5 ++++- 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c index 68419a01db36..c4e9cc79f5c7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c @@ -174,13 +174,9 @@ static int arfs_add_default_rule(struct mlx5e_priv *priv, enum arfs_type type) { struct arfs_table *arfs_t = &priv->fs.arfs.arfs_tables[type]; - struct mlx5_flow_act flow_act = { - .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - .flow_tag = MLX5_FS_DEFAULT_FLOW_TAG, - .encap_id = 0, - }; - struct mlx5_flow_destination dest; struct mlx5e_tir *tir = priv->indir_tir; + struct mlx5_flow_destination dest; + MLX5_DECLARE_FLOW_ACT(flow_act); struct mlx5_flow_spec *spec; int err = 0; @@ -469,15 +465,11 @@ static struct arfs_table *arfs_get_table(struct mlx5e_arfs_tables *arfs, static struct mlx5_flow_handle *arfs_add_rule(struct mlx5e_priv *priv, struct arfs_rule *arfs_rule) { - struct mlx5_flow_act flow_act = { - .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - .flow_tag = MLX5_FS_DEFAULT_FLOW_TAG, - .encap_id = 0, - }; struct mlx5e_arfs_tables *arfs = &priv->fs.arfs; struct arfs_tuple *tuple = &arfs_rule->tuple; struct mlx5_flow_handle *rule = NULL; struct mlx5_flow_destination dest; + MLX5_DECLARE_FLOW_ACT(flow_act); struct arfs_table *arfs_table; struct mlx5_flow_spec *spec; struct mlx5_flow_table *ft; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index f2762e45c8ae..5376d69a6b1a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -159,14 +159,10 @@ static int __mlx5e_add_vlan_rule(struct mlx5e_priv *priv, enum mlx5e_vlan_rule_type rule_type, u16 vid, struct mlx5_flow_spec *spec) { - struct mlx5_flow_act flow_act = { - .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - .flow_tag = MLX5_FS_DEFAULT_FLOW_TAG, - .encap_id = 0, - }; struct mlx5_flow_table *ft = priv->fs.vlan.ft.t; struct mlx5_flow_destination dest; struct mlx5_flow_handle **rule_p; + MLX5_DECLARE_FLOW_ACT(flow_act); int err = 0; dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; @@ -659,11 +655,7 @@ mlx5e_generate_ttc_rule(struct mlx5e_priv *priv, u16 etype, u8 proto) { - struct mlx5_flow_act flow_act = { - .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - .flow_tag = MLX5_FS_DEFAULT_FLOW_TAG, - .encap_id = 0, - }; + MLX5_DECLARE_FLOW_ACT(flow_act); struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; int err = 0; @@ -848,13 +840,9 @@ static void mlx5e_del_l2_flow_rule(struct mlx5e_priv *priv, static int mlx5e_add_l2_flow_rule(struct mlx5e_priv *priv, struct mlx5e_l2_rule *ai, int type) { - struct mlx5_flow_act flow_act = { - .action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, - .flow_tag = MLX5_FS_DEFAULT_FLOW_TAG, - .encap_id = 0, - }; struct mlx5_flow_table *ft = priv->fs.l2.ft.t; struct mlx5_flow_destination dest; + MLX5_DECLARE_FLOW_ACT(flow_act); struct mlx5_flow_spec *spec; int err = 0; u8 *mc_dmac; diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index 949b24b6c479..5eea1ba2e593 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -136,6 +136,10 @@ struct mlx5_flow_act { u32 encap_id; }; +#define MLX5_DECLARE_FLOW_ACT(name) \ + struct mlx5_flow_act name = {MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,\ + MLX5_FS_DEFAULT_FLOW_TAG, 0} + /* Single destination per rule. * Group ID is implied by the match criteria. */ @@ -156,5 +160,4 @@ struct mlx5_fc *mlx5_fc_create(struct mlx5_core_dev *dev, bool aging); void mlx5_fc_destroy(struct mlx5_core_dev *dev, struct mlx5_fc *counter); void mlx5_fc_query_cached(struct mlx5_fc *counter, u64 *bytes, u64 *packets, u64 *lastuse); - #endif -- cgit From a750276f81918294a5cb0ffa37686cf25314cf53 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Thu, 19 Jan 2017 19:37:06 +0200 Subject: net/mlx5: Reorder few command cases to reflect their natural order Move the commands related to scheduling elements and vport qos to a suitable location (according to the MLX5_CMD_OP enum values) in the command string and internal error helpers. This patch doesn't change any functionality. Signed-off-by: Or Gerlitz Reviewed-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index a380353a78c2..c3c6e931cc35 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -279,6 +279,8 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, case MLX5_CMD_OP_DESTROY_XRC_SRQ: case MLX5_CMD_OP_DESTROY_DCT: case MLX5_CMD_OP_DEALLOC_Q_COUNTER: + case MLX5_CMD_OP_DESTROY_SCHEDULING_ELEMENT: + case MLX5_CMD_OP_DESTROY_QOS_PARA_VPORT: case MLX5_CMD_OP_DEALLOC_PD: case MLX5_CMD_OP_DEALLOC_UAR: case MLX5_CMD_OP_DETACH_FROM_MCG: @@ -305,8 +307,6 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, case MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY: case MLX5_CMD_OP_SET_FLOW_TABLE_ROOT: case MLX5_CMD_OP_DEALLOC_ENCAP_HEADER: - case MLX5_CMD_OP_DESTROY_SCHEDULING_ELEMENT: - case MLX5_CMD_OP_DESTROY_QOS_PARA_VPORT: return MLX5_CMD_STAT_OK; case MLX5_CMD_OP_QUERY_HCA_CAP: @@ -363,6 +363,10 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, case MLX5_CMD_OP_QUERY_Q_COUNTER: case MLX5_CMD_OP_SET_RATE_LIMIT: case MLX5_CMD_OP_QUERY_RATE_LIMIT: + case MLX5_CMD_OP_CREATE_SCHEDULING_ELEMENT: + case MLX5_CMD_OP_QUERY_SCHEDULING_ELEMENT: + case MLX5_CMD_OP_MODIFY_SCHEDULING_ELEMENT: + case MLX5_CMD_OP_CREATE_QOS_PARA_VPORT: case MLX5_CMD_OP_ALLOC_PD: case MLX5_CMD_OP_ALLOC_UAR: case MLX5_CMD_OP_CONFIG_INT_MODERATION: @@ -414,10 +418,6 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, case MLX5_CMD_OP_ALLOC_FLOW_COUNTER: case MLX5_CMD_OP_QUERY_FLOW_COUNTER: case MLX5_CMD_OP_ALLOC_ENCAP_HEADER: - case MLX5_CMD_OP_CREATE_SCHEDULING_ELEMENT: - case MLX5_CMD_OP_QUERY_SCHEDULING_ELEMENT: - case MLX5_CMD_OP_MODIFY_SCHEDULING_ELEMENT: - case MLX5_CMD_OP_CREATE_QOS_PARA_VPORT: *status = MLX5_DRIVER_STATUS_ABORTED; *synd = MLX5_DRIVER_SYND; return -EIO; @@ -501,6 +501,12 @@ const char *mlx5_command_str(int command) MLX5_COMMAND_STR_CASE(QUERY_Q_COUNTER); MLX5_COMMAND_STR_CASE(SET_RATE_LIMIT); MLX5_COMMAND_STR_CASE(QUERY_RATE_LIMIT); + MLX5_COMMAND_STR_CASE(CREATE_SCHEDULING_ELEMENT); + MLX5_COMMAND_STR_CASE(DESTROY_SCHEDULING_ELEMENT); + MLX5_COMMAND_STR_CASE(QUERY_SCHEDULING_ELEMENT); + MLX5_COMMAND_STR_CASE(MODIFY_SCHEDULING_ELEMENT); + MLX5_COMMAND_STR_CASE(CREATE_QOS_PARA_VPORT); + MLX5_COMMAND_STR_CASE(DESTROY_QOS_PARA_VPORT); MLX5_COMMAND_STR_CASE(ALLOC_PD); MLX5_COMMAND_STR_CASE(DEALLOC_PD); MLX5_COMMAND_STR_CASE(ALLOC_UAR); @@ -576,12 +582,6 @@ const char *mlx5_command_str(int command) MLX5_COMMAND_STR_CASE(MODIFY_FLOW_TABLE); MLX5_COMMAND_STR_CASE(ALLOC_ENCAP_HEADER); MLX5_COMMAND_STR_CASE(DEALLOC_ENCAP_HEADER); - MLX5_COMMAND_STR_CASE(CREATE_SCHEDULING_ELEMENT); - MLX5_COMMAND_STR_CASE(DESTROY_SCHEDULING_ELEMENT); - MLX5_COMMAND_STR_CASE(QUERY_SCHEDULING_ELEMENT); - MLX5_COMMAND_STR_CASE(MODIFY_SCHEDULING_ELEMENT); - MLX5_COMMAND_STR_CASE(CREATE_QOS_PARA_VPORT); - MLX5_COMMAND_STR_CASE(DESTROY_QOS_PARA_VPORT); default: return "unknown command opcode"; } } -- cgit From 2a69cb9ff7caac00f3bf7c865964228dd2a0c415 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Thu, 19 Jan 2017 19:31:25 +0200 Subject: net/mlx5: Introduce modify header structures, commands and steering action definitions Add the definitions related to creation/deletion of a modify header context and the modify header steering action which are used for HW packet header modify (re-write) as part of steering. Add as well the modify header id into two intermediate structs and set it to the FTE. Note that as the push/pop vlan steering actions are emulated by the ewitch management code, we're not breaking any compatibility while changing their values to make room for the modify header action which is not emulated and whose value is part of the FW API. The new bit values for the emulated actions are at the end of the possible range. Signed-off-by: Or Gerlitz Reviewed-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 4 +- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 1 + include/linux/mlx5/fs.h | 3 +- include/linux/mlx5/mlx5_ifc.h | 113 +++++++++++++++++++++- 6 files changed, 118 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index ad329b1680b4..cd9240c3a7f0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -285,8 +285,8 @@ enum { SET_VLAN_INSERT = BIT(1) }; -#define MLX5_FLOW_CONTEXT_ACTION_VLAN_POP 0x40 -#define MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH 0x80 +#define MLX5_FLOW_CONTEXT_ACTION_VLAN_POP 0x4000 +#define MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH 0x8000 struct mlx5_encap_entry { struct hlist_node encap_hlist; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index b64a781c7e85..20d1fd516d03 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -249,6 +249,7 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev, MLX5_SET(flow_context, in_flow_context, flow_tag, fte->flow_tag); MLX5_SET(flow_context, in_flow_context, action, fte->action); MLX5_SET(flow_context, in_flow_context, encap_id, fte->encap_id); + MLX5_SET(flow_context, in_flow_context, modify_header_id, fte->modify_id); in_match_value = MLX5_ADDR_OF(flow_context, in_flow_context, match_value); memcpy(in_match_value, &fte->val, MLX5_ST_SZ_BYTES(fte_match_param)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index ded27bb9a3b6..27ff815600f7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -476,6 +476,7 @@ static struct fs_fte *alloc_fte(struct mlx5_flow_act *flow_act, fte->index = index; fte->action = flow_act->action; fte->encap_id = flow_act->encap_id; + fte->modify_id = flow_act->modify_id; return fte; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 8e668c63f69e..03af2e7989f3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -152,6 +152,7 @@ struct fs_fte { u32 index; u32 action; u32 encap_id; + u32 modify_id; enum fs_fte_status status; struct mlx5_fc *counter; }; diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index 5eea1ba2e593..ae91a4bda1a3 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -134,11 +134,12 @@ struct mlx5_flow_act { u32 action; u32 flow_tag; u32 encap_id; + u32 modify_id; }; #define MLX5_DECLARE_FLOW_ACT(name) \ struct mlx5_flow_act name = {MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,\ - MLX5_FS_DEFAULT_FLOW_TAG, 0} + MLX5_FS_DEFAULT_FLOW_TAG, 0, 0} /* Single destination per rule. * Group ID is implied by the match criteria. diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 838242697541..56bc842b0620 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -227,6 +227,8 @@ enum { MLX5_CMD_OP_MODIFY_FLOW_TABLE = 0x93c, MLX5_CMD_OP_ALLOC_ENCAP_HEADER = 0x93d, MLX5_CMD_OP_DEALLOC_ENCAP_HEADER = 0x93e, + MLX5_CMD_OP_ALLOC_MODIFY_HEADER_CONTEXT = 0x940, + MLX5_CMD_OP_DEALLOC_MODIFY_HEADER_CONTEXT = 0x941, MLX5_CMD_OP_MAX }; @@ -302,7 +304,8 @@ struct mlx5_ifc_flow_table_prop_layout_bits { u8 reserved_at_20[0x2]; u8 log_max_ft_size[0x6]; - u8 reserved_at_28[0x10]; + u8 log_max_modify_header_context[0x8]; + u8 max_modify_header_actions[0x8]; u8 max_ft_level[0x8]; u8 reserved_at_40[0x20]; @@ -2190,6 +2193,7 @@ enum { MLX5_FLOW_CONTEXT_ACTION_COUNT = 0x8, MLX5_FLOW_CONTEXT_ACTION_ENCAP = 0x10, MLX5_FLOW_CONTEXT_ACTION_DECAP = 0x20, + MLX5_FLOW_CONTEXT_ACTION_MOD_HDR = 0x40, }; struct mlx5_ifc_flow_context_bits { @@ -2211,7 +2215,9 @@ struct mlx5_ifc_flow_context_bits { u8 encap_id[0x20]; - u8 reserved_at_e0[0x120]; + u8 modify_header_id[0x20]; + + u8 reserved_at_100[0x100]; struct mlx5_ifc_fte_match_param_bits match_value; @@ -4534,6 +4540,109 @@ struct mlx5_ifc_dealloc_encap_header_in_bits { u8 reserved_60[0x20]; }; +struct mlx5_ifc_set_action_in_bits { + u8 action_type[0x4]; + u8 field[0xc]; + u8 reserved_at_10[0x3]; + u8 offset[0x5]; + u8 reserved_at_18[0x3]; + u8 length[0x5]; + + u8 data[0x20]; +}; + +struct mlx5_ifc_add_action_in_bits { + u8 action_type[0x4]; + u8 field[0xc]; + u8 reserved_at_10[0x10]; + + u8 data[0x20]; +}; + +union mlx5_ifc_set_action_in_add_action_in_auto_bits { + struct mlx5_ifc_set_action_in_bits set_action_in; + struct mlx5_ifc_add_action_in_bits add_action_in; + u8 reserved_at_0[0x40]; +}; + +enum { + MLX5_ACTION_TYPE_SET = 0x1, + MLX5_ACTION_TYPE_ADD = 0x2, +}; + +enum { + MLX5_ACTION_IN_FIELD_OUT_SMAC_47_16 = 0x1, + MLX5_ACTION_IN_FIELD_OUT_SMAC_15_0 = 0x2, + MLX5_ACTION_IN_FIELD_OUT_ETHERTYPE = 0x3, + MLX5_ACTION_IN_FIELD_OUT_DMAC_47_16 = 0x4, + MLX5_ACTION_IN_FIELD_OUT_DMAC_15_0 = 0x5, + MLX5_ACTION_IN_FIELD_OUT_IP_DSCP = 0x6, + MLX5_ACTION_IN_FIELD_OUT_TCP_FLAGS = 0x7, + MLX5_ACTION_IN_FIELD_OUT_TCP_SPORT = 0x8, + MLX5_ACTION_IN_FIELD_OUT_TCP_DPORT = 0x9, + MLX5_ACTION_IN_FIELD_OUT_IP_TTL = 0xa, + MLX5_ACTION_IN_FIELD_OUT_UDP_SPORT = 0xb, + MLX5_ACTION_IN_FIELD_OUT_UDP_DPORT = 0xc, + MLX5_ACTION_IN_FIELD_OUT_SIPV6_127_96 = 0xd, + MLX5_ACTION_IN_FIELD_OUT_SIPV6_95_64 = 0xe, + MLX5_ACTION_IN_FIELD_OUT_SIPV6_63_32 = 0xf, + MLX5_ACTION_IN_FIELD_OUT_SIPV6_31_0 = 0x10, + MLX5_ACTION_IN_FIELD_OUT_DIPV6_127_96 = 0x11, + MLX5_ACTION_IN_FIELD_OUT_DIPV6_95_64 = 0x12, + MLX5_ACTION_IN_FIELD_OUT_DIPV6_63_32 = 0x13, + MLX5_ACTION_IN_FIELD_OUT_DIPV6_31_0 = 0x14, + MLX5_ACTION_IN_FIELD_OUT_SIPV4 = 0x15, + MLX5_ACTION_IN_FIELD_OUT_DIPV4 = 0x16, +}; + +struct mlx5_ifc_alloc_modify_header_context_out_bits { + u8 status[0x8]; + u8 reserved_at_8[0x18]; + + u8 syndrome[0x20]; + + u8 modify_header_id[0x20]; + + u8 reserved_at_60[0x20]; +}; + +struct mlx5_ifc_alloc_modify_header_context_in_bits { + u8 opcode[0x10]; + u8 reserved_at_10[0x10]; + + u8 reserved_at_20[0x10]; + u8 op_mod[0x10]; + + u8 reserved_at_40[0x20]; + + u8 table_type[0x8]; + u8 reserved_at_68[0x10]; + u8 num_of_actions[0x8]; + + union mlx5_ifc_set_action_in_add_action_in_auto_bits actions[0]; +}; + +struct mlx5_ifc_dealloc_modify_header_context_out_bits { + u8 status[0x8]; + u8 reserved_at_8[0x18]; + + u8 syndrome[0x20]; + + u8 reserved_at_40[0x40]; +}; + +struct mlx5_ifc_dealloc_modify_header_context_in_bits { + u8 opcode[0x10]; + u8 reserved_at_10[0x10]; + + u8 reserved_at_20[0x10]; + u8 op_mod[0x10]; + + u8 modify_header_id[0x20]; + + u8 reserved_at_60[0x20]; +}; + struct mlx5_ifc_query_dct_out_bits { u8 status[0x8]; u8 reserved_at_8[0x18]; -- cgit From 2de24fedb9e2f8bc03f85f120a38152393ca91c5 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Thu, 19 Jan 2017 19:49:53 +0200 Subject: net/mlx5: Introduce alloc/dealloc modify header context commands Implement the low-level commands to support packet header re-write. Signed-off-by: Or Gerlitz Reviewed-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 4 ++ drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 66 ++++++++++++++++++++++ .../net/ethernet/mellanox/mlx5/core/mlx5_core.h | 5 ++ 3 files changed, 75 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index c3c6e931cc35..5bdaf3d545b2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -307,6 +307,7 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, case MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY: case MLX5_CMD_OP_SET_FLOW_TABLE_ROOT: case MLX5_CMD_OP_DEALLOC_ENCAP_HEADER: + case MLX5_CMD_OP_DEALLOC_MODIFY_HEADER_CONTEXT: return MLX5_CMD_STAT_OK; case MLX5_CMD_OP_QUERY_HCA_CAP: @@ -418,6 +419,7 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op, case MLX5_CMD_OP_ALLOC_FLOW_COUNTER: case MLX5_CMD_OP_QUERY_FLOW_COUNTER: case MLX5_CMD_OP_ALLOC_ENCAP_HEADER: + case MLX5_CMD_OP_ALLOC_MODIFY_HEADER_CONTEXT: *status = MLX5_DRIVER_STATUS_ABORTED; *synd = MLX5_DRIVER_SYND; return -EIO; @@ -582,6 +584,8 @@ const char *mlx5_command_str(int command) MLX5_COMMAND_STR_CASE(MODIFY_FLOW_TABLE); MLX5_COMMAND_STR_CASE(ALLOC_ENCAP_HEADER); MLX5_COMMAND_STR_CASE(DEALLOC_ENCAP_HEADER); + MLX5_COMMAND_STR_CASE(ALLOC_MODIFY_HEADER_CONTEXT); + MLX5_COMMAND_STR_CASE(DEALLOC_MODIFY_HEADER_CONTEXT); default: return "unknown command opcode"; } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index 20d1fd516d03..c6178ea1a461 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -516,3 +516,69 @@ void mlx5_encap_dealloc(struct mlx5_core_dev *dev, u32 encap_id) mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); } + +int mlx5_modify_header_alloc(struct mlx5_core_dev *dev, + u8 namespace, u8 num_actions, + void *modify_actions, u32 *modify_header_id) +{ + u32 out[MLX5_ST_SZ_DW(alloc_modify_header_context_out)]; + int max_actions, actions_size, inlen, err; + void *actions_in; + u8 table_type; + u32 *in; + + switch (namespace) { + case MLX5_FLOW_NAMESPACE_FDB: + max_actions = MLX5_CAP_ESW_FLOWTABLE_FDB(dev, max_modify_header_actions); + table_type = FS_FT_FDB; + break; + case MLX5_FLOW_NAMESPACE_KERNEL: + max_actions = MLX5_CAP_FLOWTABLE_NIC_RX(dev, max_modify_header_actions); + table_type = FS_FT_NIC_RX; + break; + default: + return -EOPNOTSUPP; + } + + if (num_actions > max_actions) { + mlx5_core_warn(dev, "too many modify header actions %d, max supported %d\n", + num_actions, max_actions); + return -EOPNOTSUPP; + } + + actions_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto) * num_actions; + inlen = MLX5_ST_SZ_BYTES(alloc_modify_header_context_in) + actions_size; + + in = kzalloc(inlen, GFP_KERNEL); + if (!in) + return -ENOMEM; + + MLX5_SET(alloc_modify_header_context_in, in, opcode, + MLX5_CMD_OP_ALLOC_MODIFY_HEADER_CONTEXT); + MLX5_SET(alloc_modify_header_context_in, in, table_type, table_type); + MLX5_SET(alloc_modify_header_context_in, in, num_of_actions, num_actions); + + actions_in = MLX5_ADDR_OF(alloc_modify_header_context_in, in, actions); + memcpy(actions_in, modify_actions, actions_size); + + memset(out, 0, sizeof(out)); + err = mlx5_cmd_exec(dev, in, inlen, out, sizeof(out)); + + *modify_header_id = MLX5_GET(alloc_modify_header_context_out, out, modify_header_id); + kfree(in); + return err; +} + +void mlx5_modify_header_dealloc(struct mlx5_core_dev *dev, u32 modify_header_id) +{ + u32 in[MLX5_ST_SZ_DW(dealloc_modify_header_context_in)]; + u32 out[MLX5_ST_SZ_DW(dealloc_modify_header_context_out)]; + + memset(in, 0, sizeof(in)); + MLX5_SET(dealloc_modify_header_context_in, in, opcode, + MLX5_CMD_OP_DEALLOC_MODIFY_HEADER_CONTEXT); + MLX5_SET(dealloc_modify_header_context_in, in, modify_header_id, + modify_header_id); + + mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index b3dabe6e8836..fbc6e9e9e305 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -141,6 +141,11 @@ int mlx5_encap_alloc(struct mlx5_core_dev *dev, u32 *encap_id); void mlx5_encap_dealloc(struct mlx5_core_dev *dev, u32 encap_id); +int mlx5_modify_header_alloc(struct mlx5_core_dev *dev, + u8 namespace, u8 num_actions, + void *modify_actions, u32 *modify_header_id); +void mlx5_modify_header_dealloc(struct mlx5_core_dev *dev, u32 modify_header_id); + bool mlx5_lag_intf_add(struct mlx5_interface *intf, struct mlx5_priv *priv); int mlx5_query_mtpps(struct mlx5_core_dev *dev, u32 *mtpps, u32 mtpps_size); -- cgit From ffe2e217b8ded581be6083a117839f55cca96e73 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Sun, 22 Jan 2017 20:42:39 +0200 Subject: net/sched: Add accessor functions to pedit keys for offloading drivers HW drivers will use the header-type and command fields from the extended keys, and some fields (e.g mask, val, offset) from the legacy keys. Signed-off-by: Or Gerlitz Reviewed-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed --- include/net/tc_act/tc_pedit.h | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/include/net/tc_act/tc_pedit.h b/include/net/tc_act/tc_pedit.h index dfbd6ee0bc7c..a46c3f2ace70 100644 --- a/include/net/tc_act/tc_pedit.h +++ b/include/net/tc_act/tc_pedit.h @@ -2,6 +2,7 @@ #define __NET_TC_PED_H #include +#include struct tcf_pedit_key_ex { enum pedit_header_type htype; @@ -17,4 +18,48 @@ struct tcf_pedit { }; #define to_pedit(a) ((struct tcf_pedit *)a) +static inline bool is_tcf_pedit(const struct tc_action *a) +{ +#ifdef CONFIG_NET_CLS_ACT + if (a->ops && a->ops->type == TCA_ACT_PEDIT) + return true; +#endif + return false; +} + +static inline int tcf_pedit_nkeys(const struct tc_action *a) +{ + return to_pedit(a)->tcfp_nkeys; +} + +static inline u32 tcf_pedit_htype(const struct tc_action *a, int index) +{ + if (to_pedit(a)->tcfp_keys_ex) + return to_pedit(a)->tcfp_keys_ex[index].htype; + + return TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK; +} + +static inline u32 tcf_pedit_cmd(const struct tc_action *a, int index) +{ + if (to_pedit(a)->tcfp_keys_ex) + return to_pedit(a)->tcfp_keys_ex[index].cmd; + + return __PEDIT_CMD_MAX; +} + +static inline u32 tcf_pedit_mask(const struct tc_action *a, int index) +{ + return to_pedit(a)->tcfp_keys[index].mask; +} + +static inline u32 tcf_pedit_val(const struct tc_action *a, int index) +{ + return to_pedit(a)->tcfp_keys[index].val; +} + +static inline u32 tcf_pedit_offset(const struct tc_action *a, int index) +{ + return to_pedit(a)->tcfp_keys[index].off; +} #endif /* __NET_TC_PED_H */ -- cgit From d79b6df6b10a206e2fe14a13318283712cd42b71 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Sun, 22 Jan 2017 20:46:42 +0200 Subject: net/mlx5e: Add parsing of TC pedit actions to HW format Parse/translate a set of TC pedit actions to be formed in the HW API format. User-space provides set of keys where each one of them is made of: command (add or set), header-type, byte offset within that header along with a 32 bit mask and value. The mask dictates what bits in the 32 bit word that starts on the offset we should be dealing with, but under negative polarity (unset bits are to be modified). We do a 1st pass over the set of keys while using the header-type and offset to fill the masks and the values into a data-structure containting all the supported network headers. We then do a 2nd pass over the set of fields to re-write supported by the HW, where for each such candidate field, we use the masks filled on the 1st pass to realize if we should offloading re-write it. In case offloading is required, we fill a HW descriptor with the following: (1) the header field to modify (2) the bit offset within the field from where to modify (set command only) (3) the value to set/add (4) the length in bits 1...32 to modify (set command only) Note that it's possible for a given pedit mask to dictate modifying the same header field multiple times or to modify multiple header fields. Currently such combinations are not supported for offloading, hence, for set commands, the offset within the field is always zero, and the length to modify is the field size. Signed-off-by: Or Gerlitz Reviewed-by: Amir Vadai Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 273 ++++++++++++++++++++++++ 1 file changed, 273 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index af92d9c1a619..3a31195f0d9c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include "en.h" #include "en_tc.h" @@ -72,6 +73,8 @@ struct mlx5e_tc_flow { struct mlx5e_tc_flow_parse_attr { struct mlx5_flow_spec spec; + int num_mod_hdr_actions; + void *mod_hdr_actions; }; enum { @@ -675,6 +678,276 @@ static int parse_cls_flower(struct mlx5e_priv *priv, return err; } +struct pedit_headers { + struct ethhdr eth; + struct iphdr ip4; + struct ipv6hdr ip6; + struct tcphdr tcp; + struct udphdr udp; +}; + +static int pedit_header_offsets[] = { + [TCA_PEDIT_KEY_EX_HDR_TYPE_ETH] = offsetof(struct pedit_headers, eth), + [TCA_PEDIT_KEY_EX_HDR_TYPE_IP4] = offsetof(struct pedit_headers, ip4), + [TCA_PEDIT_KEY_EX_HDR_TYPE_IP6] = offsetof(struct pedit_headers, ip6), + [TCA_PEDIT_KEY_EX_HDR_TYPE_TCP] = offsetof(struct pedit_headers, tcp), + [TCA_PEDIT_KEY_EX_HDR_TYPE_UDP] = offsetof(struct pedit_headers, udp), +}; + +#define pedit_header(_ph, _htype) ((void *)(_ph) + pedit_header_offsets[_htype]) + +static int set_pedit_val(u8 hdr_type, u32 mask, u32 val, u32 offset, + struct pedit_headers *masks, + struct pedit_headers *vals) +{ + u32 *curr_pmask, *curr_pval; + + if (hdr_type >= __PEDIT_HDR_TYPE_MAX) + goto out_err; + + curr_pmask = (u32 *)(pedit_header(masks, hdr_type) + offset); + curr_pval = (u32 *)(pedit_header(vals, hdr_type) + offset); + + if (*curr_pmask & mask) /* disallow acting twice on the same location */ + goto out_err; + + *curr_pmask |= mask; + *curr_pval |= (val & mask); + + return 0; + +out_err: + return -EOPNOTSUPP; +} + +struct mlx5_fields { + u8 field; + u8 size; + u32 offset; +}; + +static struct mlx5_fields fields[] = { + {MLX5_ACTION_IN_FIELD_OUT_DMAC_47_16, 4, offsetof(struct pedit_headers, eth.h_dest[0])}, + {MLX5_ACTION_IN_FIELD_OUT_DMAC_15_0, 2, offsetof(struct pedit_headers, eth.h_dest[4])}, + {MLX5_ACTION_IN_FIELD_OUT_SMAC_47_16, 4, offsetof(struct pedit_headers, eth.h_source[0])}, + {MLX5_ACTION_IN_FIELD_OUT_SMAC_15_0, 2, offsetof(struct pedit_headers, eth.h_source[4])}, + {MLX5_ACTION_IN_FIELD_OUT_ETHERTYPE, 2, offsetof(struct pedit_headers, eth.h_proto)}, + + {MLX5_ACTION_IN_FIELD_OUT_IP_DSCP, 1, offsetof(struct pedit_headers, ip4.tos)}, + {MLX5_ACTION_IN_FIELD_OUT_IP_TTL, 1, offsetof(struct pedit_headers, ip4.ttl)}, + {MLX5_ACTION_IN_FIELD_OUT_SIPV4, 4, offsetof(struct pedit_headers, ip4.saddr)}, + {MLX5_ACTION_IN_FIELD_OUT_DIPV4, 4, offsetof(struct pedit_headers, ip4.daddr)}, + + {MLX5_ACTION_IN_FIELD_OUT_SIPV6_127_96, 4, offsetof(struct pedit_headers, ip6.saddr.s6_addr32[0])}, + {MLX5_ACTION_IN_FIELD_OUT_SIPV6_95_64, 4, offsetof(struct pedit_headers, ip6.saddr.s6_addr32[1])}, + {MLX5_ACTION_IN_FIELD_OUT_SIPV6_63_32, 4, offsetof(struct pedit_headers, ip6.saddr.s6_addr32[2])}, + {MLX5_ACTION_IN_FIELD_OUT_SIPV6_31_0, 4, offsetof(struct pedit_headers, ip6.saddr.s6_addr32[3])}, + {MLX5_ACTION_IN_FIELD_OUT_DIPV6_127_96, 4, offsetof(struct pedit_headers, ip6.daddr.s6_addr32[0])}, + {MLX5_ACTION_IN_FIELD_OUT_DIPV6_95_64, 4, offsetof(struct pedit_headers, ip6.daddr.s6_addr32[1])}, + {MLX5_ACTION_IN_FIELD_OUT_DIPV6_63_32, 4, offsetof(struct pedit_headers, ip6.daddr.s6_addr32[2])}, + {MLX5_ACTION_IN_FIELD_OUT_DIPV6_31_0, 4, offsetof(struct pedit_headers, ip6.daddr.s6_addr32[3])}, + + {MLX5_ACTION_IN_FIELD_OUT_TCP_SPORT, 2, offsetof(struct pedit_headers, tcp.source)}, + {MLX5_ACTION_IN_FIELD_OUT_TCP_DPORT, 2, offsetof(struct pedit_headers, tcp.dest)}, + {MLX5_ACTION_IN_FIELD_OUT_TCP_FLAGS, 1, offsetof(struct pedit_headers, tcp.ack_seq) + 5}, + + {MLX5_ACTION_IN_FIELD_OUT_UDP_SPORT, 2, offsetof(struct pedit_headers, udp.source)}, + {MLX5_ACTION_IN_FIELD_OUT_UDP_DPORT, 2, offsetof(struct pedit_headers, udp.dest)}, +}; + +/* On input attr->num_mod_hdr_actions tells how many HW actions can be parsed at + * max from the SW pedit action. On success, it says how many HW actions were + * actually parsed. + */ +static int offload_pedit_fields(struct pedit_headers *masks, + struct pedit_headers *vals, + struct mlx5e_tc_flow_parse_attr *parse_attr) +{ + struct pedit_headers *set_masks, *add_masks, *set_vals, *add_vals; + int i, action_size, nactions, max_actions, first, last; + void *s_masks_p, *a_masks_p, *vals_p; + u32 s_mask, a_mask, val; + struct mlx5_fields *f; + u8 cmd, field_bsize; + unsigned long mask; + void *action; + + set_masks = &masks[TCA_PEDIT_KEY_EX_CMD_SET]; + add_masks = &masks[TCA_PEDIT_KEY_EX_CMD_ADD]; + set_vals = &vals[TCA_PEDIT_KEY_EX_CMD_SET]; + add_vals = &vals[TCA_PEDIT_KEY_EX_CMD_ADD]; + + action_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto); + action = parse_attr->mod_hdr_actions; + max_actions = parse_attr->num_mod_hdr_actions; + nactions = 0; + + for (i = 0; i < ARRAY_SIZE(fields); i++) { + f = &fields[i]; + /* avoid seeing bits set from previous iterations */ + s_mask = a_mask = mask = val = 0; + + s_masks_p = (void *)set_masks + f->offset; + a_masks_p = (void *)add_masks + f->offset; + + memcpy(&s_mask, s_masks_p, f->size); + memcpy(&a_mask, a_masks_p, f->size); + + if (!s_mask && !a_mask) /* nothing to offload here */ + continue; + + if (s_mask && a_mask) { + printk(KERN_WARNING "mlx5: can't set and add to the same HW field (%x)\n", f->field); + return -EOPNOTSUPP; + } + + if (nactions == max_actions) { + printk(KERN_WARNING "mlx5: parsed %d pedit actions, can't do more\n", nactions); + return -EOPNOTSUPP; + } + + if (s_mask) { + cmd = MLX5_ACTION_TYPE_SET; + mask = s_mask; + vals_p = (void *)set_vals + f->offset; + /* clear to denote we consumed this field */ + memset(s_masks_p, 0, f->size); + } else { + cmd = MLX5_ACTION_TYPE_ADD; + mask = a_mask; + vals_p = (void *)add_vals + f->offset; + /* clear to denote we consumed this field */ + memset(a_masks_p, 0, f->size); + } + + memcpy(&val, vals_p, f->size); + + field_bsize = f->size * BITS_PER_BYTE; + first = find_first_bit(&mask, field_bsize); + last = find_last_bit(&mask, field_bsize); + if (first > 0 || last != (field_bsize - 1)) { + printk(KERN_WARNING "mlx5: partial rewrite (mask %lx) is currently not offloaded\n", + mask); + return -EOPNOTSUPP; + } + + MLX5_SET(set_action_in, action, action_type, cmd); + MLX5_SET(set_action_in, action, field, f->field); + + if (cmd == MLX5_ACTION_TYPE_SET) { + MLX5_SET(set_action_in, action, offset, 0); + /* length is num of bits to be written, zero means length of 32 */ + MLX5_SET(set_action_in, action, length, field_bsize); + } + + if (field_bsize == 32) + MLX5_SET(set_action_in, action, data, ntohl(val)); + else if (field_bsize == 16) + MLX5_SET(set_action_in, action, data, ntohs(val)); + else if (field_bsize == 8) + MLX5_SET(set_action_in, action, data, val); + + action += action_size; + nactions++; + } + + parse_attr->num_mod_hdr_actions = nactions; + return 0; +} + +static int alloc_mod_hdr_actions(struct mlx5e_priv *priv, + const struct tc_action *a, int namespace, + struct mlx5e_tc_flow_parse_attr *parse_attr) +{ + int nkeys, action_size, max_actions; + + nkeys = tcf_pedit_nkeys(a); + action_size = MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto); + + if (namespace == MLX5_FLOW_NAMESPACE_FDB) /* FDB offloading */ + max_actions = MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev, max_modify_header_actions); + else /* namespace is MLX5_FLOW_NAMESPACE_KERNEL - NIC offloading */ + max_actions = MLX5_CAP_FLOWTABLE_NIC_RX(priv->mdev, max_modify_header_actions); + + /* can get up to crazingly 16 HW actions in 32 bits pedit SW key */ + max_actions = min(max_actions, nkeys * 16); + + parse_attr->mod_hdr_actions = kcalloc(max_actions, action_size, GFP_KERNEL); + if (!parse_attr->mod_hdr_actions) + return -ENOMEM; + + parse_attr->num_mod_hdr_actions = max_actions; + return 0; +} + +static const struct pedit_headers zero_masks = {}; + +static int parse_tc_pedit_action(struct mlx5e_priv *priv, + const struct tc_action *a, int namespace, + struct mlx5e_tc_flow_parse_attr *parse_attr) +{ + struct pedit_headers masks[__PEDIT_CMD_MAX], vals[__PEDIT_CMD_MAX], *cmd_masks; + int nkeys, i, err = -EOPNOTSUPP; + u32 mask, val, offset; + u8 cmd, htype; + + nkeys = tcf_pedit_nkeys(a); + + memset(masks, 0, sizeof(struct pedit_headers) * __PEDIT_CMD_MAX); + memset(vals, 0, sizeof(struct pedit_headers) * __PEDIT_CMD_MAX); + + for (i = 0; i < nkeys; i++) { + htype = tcf_pedit_htype(a, i); + cmd = tcf_pedit_cmd(a, i); + err = -EOPNOTSUPP; /* can't be all optimistic */ + + if (htype == TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK) { + printk(KERN_WARNING "mlx5: legacy pedit isn't offloaded\n"); + goto out_err; + } + + if (cmd != TCA_PEDIT_KEY_EX_CMD_SET && cmd != TCA_PEDIT_KEY_EX_CMD_ADD) { + printk(KERN_WARNING "mlx5: pedit cmd %d isn't offloaded\n", cmd); + goto out_err; + } + + mask = tcf_pedit_mask(a, i); + val = tcf_pedit_val(a, i); + offset = tcf_pedit_offset(a, i); + + err = set_pedit_val(htype, ~mask, val, offset, &masks[cmd], &vals[cmd]); + if (err) + goto out_err; + } + + err = alloc_mod_hdr_actions(priv, a, namespace, parse_attr); + if (err) + goto out_err; + + err = offload_pedit_fields(masks, vals, parse_attr); + if (err < 0) + goto out_dealloc_parsed_actions; + + for (cmd = 0; cmd < __PEDIT_CMD_MAX; cmd++) { + cmd_masks = &masks[cmd]; + if (memcmp(cmd_masks, &zero_masks, sizeof(zero_masks))) { + printk(KERN_WARNING "mlx5: attempt to offload an unsupported field (cmd %d)\n", + cmd); + print_hex_dump(KERN_WARNING, "mask: ", DUMP_PREFIX_ADDRESS, + 16, 1, cmd_masks, sizeof(zero_masks), true); + err = -EOPNOTSUPP; + goto out_dealloc_parsed_actions; + } + } + + return 0; + +out_dealloc_parsed_actions: + kfree(parse_attr->mod_hdr_actions); +out_err: + return err; +} + static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, struct mlx5e_tc_flow_parse_attr *parse_attr, struct mlx5e_tc_flow *flow) -- cgit From 2f4fe4cab073c60c1a70cb540662c0a91d133946 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 25 Jan 2017 19:31:33 +0200 Subject: net/mlx5e: Add offloading of NIC TC pedit (header re-write) actions This includes calling the parsing code that translates from pedit speak to the HW API, allocation (deallocation) of a modify header context and setting the modify header id associated with this context to the FTE of that flow. Signed-off-by: Or Gerlitz Reviewed-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 3a31195f0d9c..4045b4768294 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -52,6 +52,7 @@ struct mlx5_nic_flow_attr { u32 action; u32 flow_tag; + u32 mod_hdr_id; }; enum { @@ -97,10 +98,12 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, .action = attr->action, .flow_tag = attr->flow_tag, .encap_id = 0, + .modify_id = attr->mod_hdr_id, }; struct mlx5_fc *counter = NULL; struct mlx5_flow_handle *rule; bool table_created = false; + int err; if (attr->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; @@ -114,6 +117,18 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, dest.counter = counter; } + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { + err = mlx5_modify_header_alloc(dev, MLX5_FLOW_NAMESPACE_KERNEL, + parse_attr->num_mod_hdr_actions, + parse_attr->mod_hdr_actions, + &attr->mod_hdr_id); + kfree(parse_attr->mod_hdr_actions); + if (err) { + rule = ERR_PTR(err); + goto err_create_mod_hdr_id; + } + } + if (IS_ERR_OR_NULL(priv->fs.tc.t)) { priv->fs.tc.t = mlx5_create_auto_grouped_flow_table(priv->fs.ns, @@ -146,6 +161,10 @@ err_add_rule: priv->fs.tc.t = NULL; } err_create_ft: + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) + mlx5_modify_header_dealloc(priv->mdev, + attr->mod_hdr_id); +err_create_mod_hdr_id: mlx5_fc_destroy(dev, counter); return rule; @@ -164,6 +183,10 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv, mlx5_destroy_flow_table(priv->fs.tc.t); priv->fs.tc.t = NULL; } + + if (flow->nic_attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) + mlx5_modify_header_dealloc(priv->mdev, + flow->nic_attr->mod_hdr_id); } static void mlx5e_detach_encap(struct mlx5e_priv *priv, @@ -955,6 +978,7 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, struct mlx5_nic_flow_attr *attr = flow->nic_attr; const struct tc_action *a; LIST_HEAD(actions); + int err; if (tc_no_actions(exts)) return -EINVAL; @@ -976,6 +1000,17 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, continue; } + if (is_tcf_pedit(a)) { + err = parse_tc_pedit_action(priv, a, MLX5_FLOW_NAMESPACE_KERNEL, + parse_attr); + if (err) + return err; + + attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR | + MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + continue; + } + if (is_tcf_skbedit_mark(a)) { u32 mark = tcf_skbedit_mark(a); -- cgit From d7e75a325cb2d2b72e7ac9a185abc1cd59bc9922 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 25 Jan 2017 20:24:20 +0200 Subject: net/mlx5e: Add offloading of E-Switch TC pedit (header re-write) actions This includes calling the parsing code that translates from pedit speak to the HW API, allocation (deallocation) of a modify header context and setting the modify header id associated with this context to the FTE of that flow. Signed-off-by: Or Gerlitz Reviewed-by: Hadar Hen Zion Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 37 ++++++++++++++++++++-- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 1 + .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 5 ++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 4045b4768294..9dec11c00a49 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -98,7 +98,6 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, .action = attr->action, .flow_tag = attr->flow_tag, .encap_id = 0, - .modify_id = attr->mod_hdr_id, }; struct mlx5_fc *counter = NULL; struct mlx5_flow_handle *rule; @@ -122,6 +121,7 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv, parse_attr->num_mod_hdr_actions, parse_attr->mod_hdr_actions, &attr->mod_hdr_id); + flow_act.modify_id = attr->mod_hdr_id; kfree(parse_attr->mod_hdr_actions); if (err) { rule = ERR_PTR(err); @@ -208,6 +208,18 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, goto err_add_vlan; } + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) { + err = mlx5_modify_header_alloc(priv->mdev, MLX5_FLOW_NAMESPACE_FDB, + parse_attr->num_mod_hdr_actions, + parse_attr->mod_hdr_actions, + &attr->mod_hdr_id); + kfree(parse_attr->mod_hdr_actions); + if (err) { + rule = ERR_PTR(err); + goto err_mod_hdr; + } + } + rule = mlx5_eswitch_add_offloaded_rule(esw, &parse_attr->spec, attr); if (IS_ERR(rule)) goto err_add_rule; @@ -215,11 +227,14 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv, return rule; err_add_rule: + if (flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) + mlx5_modify_header_dealloc(priv->mdev, + attr->mod_hdr_id); +err_mod_hdr: mlx5_eswitch_del_vlan_action(esw, attr); err_add_vlan: if (attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) mlx5e_detach_encap(priv, flow); - return rule; } @@ -227,6 +242,7 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + struct mlx5_esw_flow_attr *attr = flow->esw_attr; mlx5_eswitch_del_offloaded_rule(esw, flow->rule, flow->esw_attr); @@ -234,6 +250,10 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, if (flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) mlx5e_detach_encap(priv, flow); + + if (flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) + mlx5_modify_header_dealloc(priv->mdev, + attr->mod_hdr_id); } static void mlx5e_detach_encap(struct mlx5e_priv *priv, @@ -1406,6 +1426,7 @@ out_err: } static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, + struct mlx5e_tc_flow_parse_attr *parse_attr, struct mlx5e_tc_flow *flow) { struct mlx5_esw_flow_attr *attr = flow->esw_attr; @@ -1429,6 +1450,16 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, continue; } + if (is_tcf_pedit(a)) { + err = parse_tc_pedit_action(priv, a, MLX5_FLOW_NAMESPACE_FDB, + parse_attr); + if (err) + return err; + + attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; + continue; + } + if (is_tcf_mirred_egress_redirect(a)) { int ifindex = tcf_mirred_ifindex(a); struct net_device *out_dev; @@ -1528,7 +1559,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, goto err_free; if (flow->flags & MLX5E_TC_FLOW_ESWITCH) { - err = parse_tc_fdb_actions(priv, f->exts, flow); + err = parse_tc_fdb_actions(priv, f->exts, parse_attr, flow); if (err < 0) goto err_free; flow->rule = mlx5e_tc_add_fdb_flow(priv, parse_attr, flow); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index cd9240c3a7f0..1f56ed9f5a6f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -308,6 +308,7 @@ struct mlx5_esw_flow_attr { u16 vlan; bool vlan_handled; struct mlx5_encap_entry *encap; + u32 mod_hdr_id; }; int mlx5_eswitch_sqs2vport_start(struct mlx5_eswitch *esw, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 415a50150763..fff962dac8e3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -88,7 +88,10 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_DECAP) spec->match_criteria_enable |= MLX5_MATCH_INNER_HEADERS; - if (attr->encap) + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) + flow_act.modify_id = attr->mod_hdr_id; + + if (attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) flow_act.encap_id = attr->encap->encap_id; rule = mlx5_add_flow_rules((struct mlx5_flow_table *)esw->fdb_table.fdb, -- cgit From 1555d204e743b6956d2be294a317121f6112238d Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 28 Mar 2017 17:24:10 +0200 Subject: devlink: Support for pipeline debug (dpipe) The pipeline debug is used to export the pipeline abstractions for the main objects - tables, headers and entries. The only support for set is for changing the counter parameter on specific table. The basic structures: Header - can represent a real protocol header information or internal metadata. Generic protocol headers like IPv4 can be shared between drivers. Each driver can add local headers. Field - part of a header. Can represent protocol field or specific ASIC metadata field. Hardware special metadata fields can be mapped to different resources, for example switch ASIC ports can have internal number which from the systems point of view is mapped to netdeivce ifindex. Match - represent specific match rule. Can describe match on specific field or header. The header index should be specified as well in order to support several header instances of the same type (tunneling). Action - represents specific action rule. Actions can describe operations on specific field values for example like set, increment, etc. And header operation like add and delete. Value - represents value which can be associated with specific match or action. Table - represents a hardware block which can be described with match/ action behavior. The match/action can be done on the packets data or on the internal metadata that it gathered along the packets traversal throw the pipeline which is vendor specific and should be exported in order to provide understanding of ASICs behavior. Entry - represents single record in a specific table. The entry is identified by specific combination of values for match/action. Prior to accessing the tables/entries the drivers provide the header/ field data base which is used by driver to user-space. The data base is split between the shared headers and unique headers. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/devlink.h | 259 ++++++++++++++ include/uapi/linux/devlink.h | 67 +++- net/core/devlink.c | 836 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1161 insertions(+), 1 deletion(-) diff --git a/include/net/devlink.h b/include/net/devlink.h index d29e5fc82582..24de13f8c94f 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -25,6 +25,8 @@ struct devlink { struct list_head list; struct list_head port_list; struct list_head sb_list; + struct list_head dpipe_table_list; + struct devlink_dpipe_headers *dpipe_headers; const struct devlink_ops *ops; struct device *dev; possible_net_t _net; @@ -49,6 +51,178 @@ struct devlink_sb_pool_info { enum devlink_sb_threshold_type threshold_type; }; +/** + * struct devlink_dpipe_field - dpipe field object + * @name: field name + * @id: index inside the headers field array + * @bitwidth: bitwidth + * @mapping_type: mapping type + */ +struct devlink_dpipe_field { + const char *name; + unsigned int id; + unsigned int bitwidth; + enum devlink_dpipe_field_mapping_type mapping_type; +}; + +/** + * struct devlink_dpipe_header - dpipe header object + * @name: header name + * @id: index, global/local detrmined by global bit + * @fields: fields + * @fields_count: number of fields + * @global: indicates if header is shared like most protocol header + * or driver specific + */ +struct devlink_dpipe_header { + const char *name; + unsigned int id; + struct devlink_dpipe_field *fields; + unsigned int fields_count; + bool global; +}; + +/** + * struct devlink_dpipe_match - represents match operation + * @type: type of match + * @header_index: header index (packets can have several headers of same + * type like in case of tunnels) + * @header: header + * @fieled_id: field index + */ +struct devlink_dpipe_match { + enum devlink_dpipe_match_type type; + unsigned int header_index; + struct devlink_dpipe_header *header; + unsigned int field_id; +}; + +/** + * struct devlink_dpipe_action - represents action operation + * @type: type of action + * @header_index: header index (packets can have several headers of same + * type like in case of tunnels) + * @header: header + * @fieled_id: field index + */ +struct devlink_dpipe_action { + enum devlink_dpipe_action_type type; + unsigned int header_index; + struct devlink_dpipe_header *header; + unsigned int field_id; +}; + +/** + * struct devlink_dpipe_value - represents value of match/action + * @action: action + * @match: match + * @mapping_value: in case the field has some mapping this value + * specified the mapping value + * @mapping_valid: specify if mapping value is valid + * @value_size: value size + * @value: value + * @mask: bit mask + */ +struct devlink_dpipe_value { + union { + struct devlink_dpipe_action *action; + struct devlink_dpipe_match *match; + }; + unsigned int mapping_value; + bool mapping_valid; + unsigned int value_size; + void *value; + void *mask; +}; + +/** + * struct devlink_dpipe_entry - table entry object + * @index: index of the entry in the table + * @match_values: match values + * @matche_values_count: count of matches tuples + * @action_values: actions values + * @action_values_count: count of actions values + * @counter: value of counter + * @counter_valid: Specify if value is valid from hardware + */ +struct devlink_dpipe_entry { + u64 index; + struct devlink_dpipe_value *match_values; + unsigned int match_values_count; + struct devlink_dpipe_value *action_values; + unsigned int action_values_count; + u64 counter; + bool counter_valid; +}; + +/** + * struct devlink_dpipe_dump_ctx - context provided to driver in order + * to dump + * @info: info + * @cmd: devlink command + * @skb: skb + * @nest: top attribute + * @hdr: hdr + */ +struct devlink_dpipe_dump_ctx { + struct genl_info *info; + enum devlink_command cmd; + struct sk_buff *skb; + struct nlattr *nest; + void *hdr; +}; + +struct devlink_dpipe_table_ops; + +/** + * struct devlink_dpipe_table - table object + * @priv: private + * @name: table name + * @size: maximum number of entries + * @counters_enabled: indicates if counters are active + * @counter_control_extern: indicates if counter control is in dpipe or + * external tool + * @table_ops: table operations + * @rcu: rcu + */ +struct devlink_dpipe_table { + void *priv; + struct list_head list; + const char *name; + u64 size; + bool counters_enabled; + bool counter_control_extern; + struct devlink_dpipe_table_ops *table_ops; + struct rcu_head rcu; +}; + +/** + * struct devlink_dpipe_table_ops - dpipe_table ops + * @actions_dump - dumps all tables actions + * @matches_dump - dumps all tables matches + * @entries_dump - dumps all active entries in the table + * @counters_set_update - when changing the counter status hardware sync + * maybe needed to allocate/free counter related + * resources + */ +struct devlink_dpipe_table_ops { + int (*actions_dump)(void *priv, struct sk_buff *skb); + int (*matches_dump)(void *priv, struct sk_buff *skb); + int (*entries_dump)(void *priv, bool counters_enabled, + struct devlink_dpipe_dump_ctx *dump_ctx); + int (*counters_set_update)(void *priv, bool enable); +}; + +/** + * struct devlink_dpipe_headers - dpipe headers + * @headers - header array can be shared (global bit) or driver specific + * @headers_count - count of headers + */ +struct devlink_dpipe_headers { + struct devlink_dpipe_header **headers; + unsigned int headers_count; +}; + struct devlink_ops { int (*port_type_set)(struct devlink_port *devlink_port, enum devlink_port_type port_type); @@ -132,6 +306,26 @@ int devlink_sb_register(struct devlink *devlink, unsigned int sb_index, u16 egress_pools_count, u16 ingress_tc_count, u16 egress_tc_count); void devlink_sb_unregister(struct devlink *devlink, unsigned int sb_index); +int devlink_dpipe_table_register(struct devlink *devlink, + const char *table_name, + struct devlink_dpipe_table_ops *table_ops, + void *priv, u64 size, + bool counter_control_extern); +void devlink_dpipe_table_unregister(struct devlink *devlink, + const char *table_name); +int devlink_dpipe_headers_register(struct devlink *devlink, + struct devlink_dpipe_headers *dpipe_headers); +void devlink_dpipe_headers_unregister(struct devlink *devlink); +bool devlink_dpipe_table_counter_enabled(struct devlink *devlink, + const char *table_name); +int devlink_dpipe_entry_ctx_prepare(struct devlink_dpipe_dump_ctx *dump_ctx); +int devlink_dpipe_entry_ctx_append(struct devlink_dpipe_dump_ctx *dump_ctx, + struct devlink_dpipe_entry *entry); +int devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx); +int devlink_dpipe_action_put(struct sk_buff *skb, + struct devlink_dpipe_action *action); +int devlink_dpipe_match_put(struct sk_buff *skb, + struct devlink_dpipe_match *match); #else @@ -200,6 +394,71 @@ static inline void devlink_sb_unregister(struct devlink *devlink, { } +static inline int +devlink_dpipe_table_register(struct devlink *devlink, + const char *table_name, + struct devlink_dpipe_table_ops *table_ops, + void *priv, u64 size, + bool counter_control_extern) +{ + return 0; +} + +static inline void devlink_dpipe_table_unregister(struct devlink *devlink, + const char *table_name) +{ +} + +static inline int devlink_dpipe_headers_register(struct devlink *devlink, + struct devlink_dpipe_headers * + dpipe_headers) +{ + return 0; +} + +static inline void devlink_dpipe_headers_unregister(struct devlink *devlink) +{ +} + +static inline bool devlink_dpipe_table_counter_enabled(struct devlink *devlink, + const char *table_name) +{ + return false; +} + +static inline int +devlink_dpipe_entry_ctx_prepare(struct devlink_dpipe_dump_ctx *dump_ctx) +{ + return 0; +} + +static inline int +devlink_dpipe_entry_ctx_append(struct devlink_dpipe_dump_ctx *dump_ctx, + struct devlink_dpipe_entry *entry) +{ + return 0; +} + +static inline int +devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx) +{ + return 0; +} + +static inline int +devlink_dpipe_action_put(struct sk_buff *skb, + struct devlink_dpipe_action *action) +{ + return 0; +} + +static inline int +devlink_dpipe_match_put(struct sk_buff *skb, + struct devlink_dpipe_match *match) +{ + return 0; +} + #endif #endif /* _NET_DEVLINK_H_ */ diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index 0f1f3a12e23c..b47bee277347 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -65,8 +65,12 @@ enum devlink_command { #define DEVLINK_CMD_ESWITCH_MODE_SET /* obsolete, never use this! */ \ DEVLINK_CMD_ESWITCH_SET - /* add new commands above here */ + DEVLINK_CMD_DPIPE_TABLE_GET, + DEVLINK_CMD_DPIPE_ENTRIES_GET, + DEVLINK_CMD_DPIPE_HEADERS_GET, + DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET, + /* add new commands above here */ __DEVLINK_CMD_MAX, DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1 }; @@ -148,10 +152,71 @@ enum devlink_attr { DEVLINK_ATTR_ESWITCH_MODE, /* u16 */ DEVLINK_ATTR_ESWITCH_INLINE_MODE, /* u8 */ + DEVLINK_ATTR_DPIPE_TABLES, /* nested */ + DEVLINK_ATTR_DPIPE_TABLE, /* nested */ + DEVLINK_ATTR_DPIPE_TABLE_NAME, /* string */ + DEVLINK_ATTR_DPIPE_TABLE_SIZE, /* u64 */ + DEVLINK_ATTR_DPIPE_TABLE_MATCHES, /* nested */ + DEVLINK_ATTR_DPIPE_TABLE_ACTIONS, /* nested */ + DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED, /* u8 */ + + DEVLINK_ATTR_DPIPE_ENTRIES, /* nested */ + DEVLINK_ATTR_DPIPE_ENTRY, /* nested */ + DEVLINK_ATTR_DPIPE_ENTRY_INDEX, /* u64 */ + DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES, /* nested */ + DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES, /* nested */ + DEVLINK_ATTR_DPIPE_ENTRY_COUNTER, /* u64 */ + + DEVLINK_ATTR_DPIPE_MATCH, /* nested */ + DEVLINK_ATTR_DPIPE_MATCH_VALUE, /* nested */ + DEVLINK_ATTR_DPIPE_MATCH_TYPE, /* u32 */ + + DEVLINK_ATTR_DPIPE_ACTION, /* nested */ + DEVLINK_ATTR_DPIPE_ACTION_VALUE, /* nested */ + DEVLINK_ATTR_DPIPE_ACTION_TYPE, /* u32 */ + + DEVLINK_ATTR_DPIPE_VALUE, + DEVLINK_ATTR_DPIPE_VALUE_MASK, + DEVLINK_ATTR_DPIPE_VALUE_MAPPING, /* u32 */ + + DEVLINK_ATTR_DPIPE_HEADERS, /* nested */ + DEVLINK_ATTR_DPIPE_HEADER, /* nested */ + DEVLINK_ATTR_DPIPE_HEADER_NAME, /* string */ + DEVLINK_ATTR_DPIPE_HEADER_ID, /* u32 */ + DEVLINK_ATTR_DPIPE_HEADER_FIELDS, /* nested */ + DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, /* u8 */ + DEVLINK_ATTR_DPIPE_HEADER_INDEX, /* u32 */ + + DEVLINK_ATTR_DPIPE_FIELD, /* nested */ + DEVLINK_ATTR_DPIPE_FIELD_NAME, /* string */ + DEVLINK_ATTR_DPIPE_FIELD_ID, /* u32 */ + DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH, /* u32 */ + DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE, /* u32 */ + + DEVLINK_ATTR_PAD, + /* add new attributes above here, update the policy in devlink.c */ __DEVLINK_ATTR_MAX, DEVLINK_ATTR_MAX = __DEVLINK_ATTR_MAX - 1 }; +/* Mapping between internal resource described by the field and system + * structure + */ +enum devlink_dpipe_field_mapping_type { + DEVLINK_DPIPE_FIELD_MAPPING_TYPE_NONE, + DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX, +}; + +/* Match type - specify the type of the match */ +enum devlink_dpipe_match_type { + DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT, +}; + +/* Action type - specify the action type */ +enum devlink_dpipe_action_type { + DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY, +}; + #endif /* _UAPI_LINUX_DEVLINK_H_ */ diff --git a/net/core/devlink.c b/net/core/devlink.c index e9c1e6acfb6d..24b766003a61 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1493,8 +1493,686 @@ static int devlink_nl_cmd_eswitch_set_doit(struct sk_buff *skb, if (err) return err; } + return 0; +} + +int devlink_dpipe_match_put(struct sk_buff *skb, + struct devlink_dpipe_match *match) +{ + struct devlink_dpipe_header *header = match->header; + struct devlink_dpipe_field *field = &header->fields[match->field_id]; + struct nlattr *match_attr; + + match_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_MATCH); + if (!match_attr) + return -EMSGSIZE; + + if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_MATCH_TYPE, match->type) || + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_INDEX, match->header_index) || + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) || + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) || + nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global)) + goto nla_put_failure; + + nla_nest_end(skb, match_attr); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, match_attr); + return -EMSGSIZE; +} +EXPORT_SYMBOL_GPL(devlink_dpipe_match_put); + +static int devlink_dpipe_matches_put(struct devlink_dpipe_table *table, + struct sk_buff *skb) +{ + struct nlattr *matches_attr; + + matches_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_TABLE_MATCHES); + if (!matches_attr) + return -EMSGSIZE; + + if (table->table_ops->matches_dump(table->priv, skb)) + goto nla_put_failure; + + nla_nest_end(skb, matches_attr); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, matches_attr); + return -EMSGSIZE; +} + +int devlink_dpipe_action_put(struct sk_buff *skb, + struct devlink_dpipe_action *action) +{ + struct devlink_dpipe_header *header = action->header; + struct devlink_dpipe_field *field = &header->fields[action->field_id]; + struct nlattr *action_attr; + + action_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_ACTION); + if (!action_attr) + return -EMSGSIZE; + + if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_ACTION_TYPE, action->type) || + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_INDEX, action->header_index) || + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) || + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) || + nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global)) + goto nla_put_failure; + + nla_nest_end(skb, action_attr); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, action_attr); + return -EMSGSIZE; +} +EXPORT_SYMBOL_GPL(devlink_dpipe_action_put); + +static int devlink_dpipe_actions_put(struct devlink_dpipe_table *table, + struct sk_buff *skb) +{ + struct nlattr *actions_attr; + + actions_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_TABLE_ACTIONS); + if (!actions_attr) + return -EMSGSIZE; + + if (table->table_ops->actions_dump(table->priv, skb)) + goto nla_put_failure; + + nla_nest_end(skb, actions_attr); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, actions_attr); + return -EMSGSIZE; +} + +static int devlink_dpipe_table_put(struct sk_buff *skb, + struct devlink_dpipe_table *table) +{ + struct nlattr *table_attr; + + table_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_TABLE); + if (!table_attr) + return -EMSGSIZE; + + if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_TABLE_NAME, table->name) || + nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_TABLE_SIZE, table->size, + DEVLINK_ATTR_PAD)) + goto nla_put_failure; + if (nla_put_u8(skb, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED, + table->counters_enabled)) + goto nla_put_failure; + + if (devlink_dpipe_matches_put(table, skb)) + goto nla_put_failure; + + if (devlink_dpipe_actions_put(table, skb)) + goto nla_put_failure; + + nla_nest_end(skb, table_attr); + return 0; + +nla_put_failure: + nla_nest_cancel(skb, table_attr); + return -EMSGSIZE; +} + +static int devlink_dpipe_send_and_alloc_skb(struct sk_buff **pskb, + struct genl_info *info) +{ + int err; + + if (*pskb) { + err = genlmsg_reply(*pskb, info); + if (err) + return err; + } + *pskb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!*pskb) + return -ENOMEM; + return 0; +} + +static int devlink_dpipe_tables_fill(struct genl_info *info, + enum devlink_command cmd, int flags, + struct list_head *dpipe_tables, + const char *table_name) +{ + struct devlink *devlink = info->user_ptr[0]; + struct devlink_dpipe_table *table; + struct nlattr *tables_attr; + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh; + bool incomplete; + void *hdr; + int i; + int err; + + table = list_first_entry(dpipe_tables, + struct devlink_dpipe_table, list); +start_again: + err = devlink_dpipe_send_and_alloc_skb(&skb, info); + if (err) + return err; + + hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, + &devlink_nl_family, NLM_F_MULTI, cmd); + if (!hdr) + return -EMSGSIZE; + + if (devlink_nl_put_handle(skb, devlink)) + goto nla_put_failure; + tables_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_TABLES); + if (!tables_attr) + goto nla_put_failure; + + i = 0; + incomplete = false; + list_for_each_entry_from(table, dpipe_tables, list) { + if (!table_name) { + err = devlink_dpipe_table_put(skb, table); + if (err) { + if (!i) + goto err_table_put; + incomplete = true; + break; + } + } else { + if (!strcmp(table->name, table_name)) { + err = devlink_dpipe_table_put(skb, table); + if (err) + break; + } + } + i++; + } + + nla_nest_end(skb, tables_attr); + genlmsg_end(skb, hdr); + if (incomplete) + goto start_again; + +send_done: + nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq, + NLMSG_DONE, 0, flags | NLM_F_MULTI); + if (!nlh) { + err = devlink_dpipe_send_and_alloc_skb(&skb, info); + if (err) + goto err_skb_send_alloc; + goto send_done; + } + + return genlmsg_reply(skb, info); + +nla_put_failure: + err = -EMSGSIZE; +err_table_put: +err_skb_send_alloc: + genlmsg_cancel(skb, hdr); + nlmsg_free(skb); + return err; +} + +static int devlink_nl_cmd_dpipe_table_get(struct sk_buff *skb, + struct genl_info *info) +{ + struct devlink *devlink = info->user_ptr[0]; + const char *table_name = NULL; + + if (info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]) + table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]); + + return devlink_dpipe_tables_fill(info, DEVLINK_CMD_DPIPE_TABLE_GET, 0, + &devlink->dpipe_table_list, + table_name); +} + +static int devlink_dpipe_value_put(struct sk_buff *skb, + struct devlink_dpipe_value *value) +{ + if (nla_put(skb, DEVLINK_ATTR_DPIPE_VALUE, + value->value_size, value->value)) + return -EMSGSIZE; + if (value->mask) + if (nla_put(skb, DEVLINK_ATTR_DPIPE_VALUE_MASK, + value->value_size, value->mask)) + return -EMSGSIZE; + if (value->mapping_valid) + if (nla_put_u32(skb, DEVLINK_ATTR_DPIPE_VALUE_MAPPING, + value->mapping_value)) + return -EMSGSIZE; + return 0; +} + +static int devlink_dpipe_action_value_put(struct sk_buff *skb, + struct devlink_dpipe_value *value) +{ + if (!value->action) + return -EINVAL; + if (devlink_dpipe_action_put(skb, value->action)) + return -EMSGSIZE; + if (devlink_dpipe_value_put(skb, value)) + return -EMSGSIZE; + return 0; +} + +static int devlink_dpipe_action_values_put(struct sk_buff *skb, + struct devlink_dpipe_value *values, + unsigned int values_count) +{ + struct nlattr *action_attr; + int i; + int err; + + for (i = 0; i < values_count; i++) { + action_attr = nla_nest_start(skb, + DEVLINK_ATTR_DPIPE_ACTION_VALUE); + if (!action_attr) + return -EMSGSIZE; + err = devlink_dpipe_action_value_put(skb, &values[i]); + if (err) + goto err_action_value_put; + nla_nest_end(skb, action_attr); + } + return 0; + +err_action_value_put: + nla_nest_cancel(skb, action_attr); + return err; +} + +static int devlink_dpipe_match_value_put(struct sk_buff *skb, + struct devlink_dpipe_value *value) +{ + if (!value->match) + return -EINVAL; + if (devlink_dpipe_match_put(skb, value->match)) + return -EMSGSIZE; + if (devlink_dpipe_value_put(skb, value)) + return -EMSGSIZE; + return 0; +} + +static int devlink_dpipe_match_values_put(struct sk_buff *skb, + struct devlink_dpipe_value *values, + unsigned int values_count) +{ + struct nlattr *match_attr; + int i; + int err; + + for (i = 0; i < values_count; i++) { + match_attr = nla_nest_start(skb, + DEVLINK_ATTR_DPIPE_MATCH_VALUE); + if (!match_attr) + return -EMSGSIZE; + err = devlink_dpipe_match_value_put(skb, &values[i]); + if (err) + goto err_match_value_put; + nla_nest_end(skb, match_attr); + } + return 0; + +err_match_value_put: + nla_nest_cancel(skb, match_attr); + return err; +} + +static int devlink_dpipe_entry_put(struct sk_buff *skb, + struct devlink_dpipe_entry *entry) +{ + struct nlattr *entry_attr, *matches_attr, *actions_attr; + int err; + + entry_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_ENTRY); + if (!entry_attr) + return -EMSGSIZE; + + if (nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_ENTRY_INDEX, entry->index, + DEVLINK_ATTR_PAD)) + goto nla_put_failure; + if (entry->counter_valid) + if (nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_ENTRY_COUNTER, + entry->counter, DEVLINK_ATTR_PAD)) + goto nla_put_failure; + + matches_attr = nla_nest_start(skb, + DEVLINK_ATTR_DPIPE_ENTRY_MATCH_VALUES); + if (!matches_attr) + goto nla_put_failure; + + err = devlink_dpipe_match_values_put(skb, entry->match_values, + entry->match_values_count); + if (err) { + nla_nest_cancel(skb, matches_attr); + goto err_match_values_put; + } + nla_nest_end(skb, matches_attr); + + actions_attr = nla_nest_start(skb, + DEVLINK_ATTR_DPIPE_ENTRY_ACTION_VALUES); + if (!actions_attr) + goto nla_put_failure; + + err = devlink_dpipe_action_values_put(skb, entry->action_values, + entry->action_values_count); + if (err) { + nla_nest_cancel(skb, actions_attr); + goto err_action_values_put; + } + nla_nest_end(skb, actions_attr); + nla_nest_end(skb, entry_attr); return 0; + +nla_put_failure: + err = -EMSGSIZE; +err_match_values_put: +err_action_values_put: + nla_nest_cancel(skb, entry_attr); + return err; +} + +static struct devlink_dpipe_table * +devlink_dpipe_table_find(struct list_head *dpipe_tables, + const char *table_name) +{ + struct devlink_dpipe_table *table; + + list_for_each_entry_rcu(table, dpipe_tables, list) { + if (!strcmp(table->name, table_name)) + return table; + } + return NULL; +} + +int devlink_dpipe_entry_ctx_prepare(struct devlink_dpipe_dump_ctx *dump_ctx) +{ + struct devlink *devlink; + int err; + + err = devlink_dpipe_send_and_alloc_skb(&dump_ctx->skb, + dump_ctx->info); + if (err) + return err; + + dump_ctx->hdr = genlmsg_put(dump_ctx->skb, + dump_ctx->info->snd_portid, + dump_ctx->info->snd_seq, + &devlink_nl_family, NLM_F_MULTI, + dump_ctx->cmd); + if (!dump_ctx->hdr) + goto nla_put_failure; + + devlink = dump_ctx->info->user_ptr[0]; + if (devlink_nl_put_handle(dump_ctx->skb, devlink)) + goto nla_put_failure; + dump_ctx->nest = nla_nest_start(dump_ctx->skb, + DEVLINK_ATTR_DPIPE_ENTRIES); + if (!dump_ctx->nest) + goto nla_put_failure; + return 0; + +nla_put_failure: + genlmsg_cancel(dump_ctx->skb, dump_ctx->hdr); + nlmsg_free(dump_ctx->skb); + return -EMSGSIZE; +} +EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_prepare); + +int devlink_dpipe_entry_ctx_append(struct devlink_dpipe_dump_ctx *dump_ctx, + struct devlink_dpipe_entry *entry) +{ + return devlink_dpipe_entry_put(dump_ctx->skb, entry); +} +EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_append); + +int devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx) +{ + nla_nest_end(dump_ctx->skb, dump_ctx->nest); + genlmsg_end(dump_ctx->skb, dump_ctx->hdr); + return 0; +} +EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_close); + +static int devlink_dpipe_entries_fill(struct genl_info *info, + enum devlink_command cmd, int flags, + struct devlink_dpipe_table *table) +{ + struct devlink_dpipe_dump_ctx dump_ctx; + struct nlmsghdr *nlh; + int err; + + dump_ctx.skb = NULL; + dump_ctx.cmd = cmd; + dump_ctx.info = info; + + err = table->table_ops->entries_dump(table->priv, + table->counters_enabled, + &dump_ctx); + if (err) + goto err_entries_dump; + +send_done: + nlh = nlmsg_put(dump_ctx.skb, info->snd_portid, info->snd_seq, + NLMSG_DONE, 0, flags | NLM_F_MULTI); + if (!nlh) { + err = devlink_dpipe_send_and_alloc_skb(&dump_ctx.skb, info); + if (err) + goto err_skb_send_alloc; + goto send_done; + } + return genlmsg_reply(dump_ctx.skb, info); + +err_entries_dump: +err_skb_send_alloc: + genlmsg_cancel(dump_ctx.skb, dump_ctx.hdr); + nlmsg_free(dump_ctx.skb); + return err; +} + +static int devlink_nl_cmd_dpipe_entries_get(struct sk_buff *skb, + struct genl_info *info) +{ + struct devlink *devlink = info->user_ptr[0]; + struct devlink_dpipe_table *table; + const char *table_name; + + if (!info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]) + return -EINVAL; + + table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]); + table = devlink_dpipe_table_find(&devlink->dpipe_table_list, + table_name); + if (!table) + return -EINVAL; + + if (!table->table_ops->entries_dump) + return -EINVAL; + + return devlink_dpipe_entries_fill(info, DEVLINK_CMD_DPIPE_ENTRIES_GET, + 0, table); +} + +static int devlink_dpipe_fields_put(struct sk_buff *skb, + const struct devlink_dpipe_header *header) +{ + struct devlink_dpipe_field *field; + struct nlattr *field_attr; + int i; + + for (i = 0; i < header->fields_count; i++) { + field = &header->fields[i]; + field_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_FIELD); + if (!field_attr) + return -EMSGSIZE; + if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_FIELD_NAME, field->name) || + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_ID, field->id) || + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_BITWIDTH, field->bitwidth) || + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_FIELD_MAPPING_TYPE, field->mapping_type)) + goto nla_put_failure; + nla_nest_end(skb, field_attr); + } + return 0; + +nla_put_failure: + nla_nest_cancel(skb, field_attr); + return -EMSGSIZE; +} + +static int devlink_dpipe_header_put(struct sk_buff *skb, + struct devlink_dpipe_header *header) +{ + struct nlattr *fields_attr, *header_attr; + int err; + + header_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_HEADER); + if (!header) + return -EMSGSIZE; + + if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_HEADER_NAME, header->name) || + nla_put_u32(skb, DEVLINK_ATTR_DPIPE_HEADER_ID, header->id) || + nla_put_u8(skb, DEVLINK_ATTR_DPIPE_HEADER_GLOBAL, header->global)) + goto nla_put_failure; + + fields_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_HEADER_FIELDS); + if (!fields_attr) + goto nla_put_failure; + + err = devlink_dpipe_fields_put(skb, header); + if (err) { + nla_nest_cancel(skb, fields_attr); + goto nla_put_failure; + } + nla_nest_end(skb, fields_attr); + nla_nest_end(skb, header_attr); + return 0; + +nla_put_failure: + err = -EMSGSIZE; + nla_nest_cancel(skb, header_attr); + return err; +} + +static int devlink_dpipe_headers_fill(struct genl_info *info, + enum devlink_command cmd, int flags, + struct devlink_dpipe_headers * + dpipe_headers) +{ + struct devlink *devlink = info->user_ptr[0]; + struct nlattr *headers_attr; + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh; + void *hdr; + int i, j; + int err; + + i = 0; +start_again: + err = devlink_dpipe_send_and_alloc_skb(&skb, info); + if (err) + return err; + + hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq, + &devlink_nl_family, NLM_F_MULTI, cmd); + if (!hdr) + return -EMSGSIZE; + + if (devlink_nl_put_handle(skb, devlink)) + goto nla_put_failure; + headers_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_HEADERS); + if (!headers_attr) + goto nla_put_failure; + + j = 0; + for (; i < dpipe_headers->headers_count; i++) { + err = devlink_dpipe_header_put(skb, dpipe_headers->headers[i]); + if (err) { + if (!j) + goto err_table_put; + break; + } + j++; + } + nla_nest_end(skb, headers_attr); + genlmsg_end(skb, hdr); + if (i != dpipe_headers->headers_count) + goto start_again; + +send_done: + nlh = nlmsg_put(skb, info->snd_portid, info->snd_seq, + NLMSG_DONE, 0, flags | NLM_F_MULTI); + if (!nlh) { + err = devlink_dpipe_send_and_alloc_skb(&skb, info); + if (err) + goto err_skb_send_alloc; + goto send_done; + } + return genlmsg_reply(skb, info); + +nla_put_failure: + err = -EMSGSIZE; +err_table_put: +err_skb_send_alloc: + genlmsg_cancel(skb, hdr); + nlmsg_free(skb); + return err; +} + +static int devlink_nl_cmd_dpipe_headers_get(struct sk_buff *skb, + struct genl_info *info) +{ + struct devlink *devlink = info->user_ptr[0]; + + if (!devlink->dpipe_headers) + return -EOPNOTSUPP; + return devlink_dpipe_headers_fill(info, DEVLINK_CMD_DPIPE_HEADERS_GET, + 0, devlink->dpipe_headers); +} + +static int devlink_dpipe_table_counters_set(struct devlink *devlink, + const char *table_name, + bool enable) +{ + struct devlink_dpipe_table *table; + + table = devlink_dpipe_table_find(&devlink->dpipe_table_list, + table_name); + if (!table) + return -EINVAL; + + if (table->counter_control_extern) + return -EOPNOTSUPP; + + if (!(table->counters_enabled ^ enable)) + return 0; + + table->counters_enabled = enable; + if (table->table_ops->counters_set_update) + table->table_ops->counters_set_update(table->priv, enable); + return 0; +} + +static int devlink_nl_cmd_dpipe_table_counters_set(struct sk_buff *skb, + struct genl_info *info) +{ + struct devlink *devlink = info->user_ptr[0]; + const char *table_name; + bool counters_enable; + + if (!info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME] || + !info->attrs[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]) + return -EINVAL; + + table_name = nla_data(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_NAME]); + counters_enable = !!nla_get_u8(info->attrs[DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED]); + + return devlink_dpipe_table_counters_set(devlink, table_name, + counters_enable); } static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { @@ -1512,6 +2190,8 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { [DEVLINK_ATTR_SB_TC_INDEX] = { .type = NLA_U16 }, [DEVLINK_ATTR_ESWITCH_MODE] = { .type = NLA_U16 }, [DEVLINK_ATTR_ESWITCH_INLINE_MODE] = { .type = NLA_U8 }, + [DEVLINK_ATTR_DPIPE_TABLE_NAME] = { .type = NLA_NUL_STRING }, + [DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U8 }, }; static const struct genl_ops devlink_nl_ops[] = { @@ -1644,6 +2324,34 @@ static const struct genl_ops devlink_nl_ops[] = { .flags = GENL_ADMIN_PERM, .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, }, + { + .cmd = DEVLINK_CMD_DPIPE_TABLE_GET, + .doit = devlink_nl_cmd_dpipe_table_get, + .policy = devlink_nl_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, + }, + { + .cmd = DEVLINK_CMD_DPIPE_ENTRIES_GET, + .doit = devlink_nl_cmd_dpipe_entries_get, + .policy = devlink_nl_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, + }, + { + .cmd = DEVLINK_CMD_DPIPE_HEADERS_GET, + .doit = devlink_nl_cmd_dpipe_headers_get, + .policy = devlink_nl_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, + }, + { + .cmd = DEVLINK_CMD_DPIPE_TABLE_COUNTERS_SET, + .doit = devlink_nl_cmd_dpipe_table_counters_set, + .policy = devlink_nl_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = DEVLINK_NL_FLAG_NEED_DEVLINK, + }, }; static struct genl_family devlink_nl_family __ro_after_init = { @@ -1680,6 +2388,7 @@ struct devlink *devlink_alloc(const struct devlink_ops *ops, size_t priv_size) devlink_net_set(devlink, &init_net); INIT_LIST_HEAD(&devlink->port_list); INIT_LIST_HEAD(&devlink->sb_list); + INIT_LIST_HEAD_RCU(&devlink->dpipe_table_list); return devlink; } EXPORT_SYMBOL_GPL(devlink_alloc); @@ -1880,6 +2589,133 @@ void devlink_sb_unregister(struct devlink *devlink, unsigned int sb_index) } EXPORT_SYMBOL_GPL(devlink_sb_unregister); +/** + * devlink_dpipe_headers_register - register dpipe headers + * + * @devlink: devlink + * @dpipe_headers: dpipe header array + * + * Register the headers supported by hardware. + */ +int devlink_dpipe_headers_register(struct devlink *devlink, + struct devlink_dpipe_headers *dpipe_headers) +{ + mutex_lock(&devlink_mutex); + devlink->dpipe_headers = dpipe_headers; + mutex_unlock(&devlink_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(devlink_dpipe_headers_register); + +/** + * devlink_dpipe_headers_unregister - unregister dpipe headers + * + * @devlink: devlink + * + * Unregister the headers supported by hardware. + */ +void devlink_dpipe_headers_unregister(struct devlink *devlink) +{ + mutex_lock(&devlink_mutex); + devlink->dpipe_headers = NULL; + mutex_unlock(&devlink_mutex); +} +EXPORT_SYMBOL_GPL(devlink_dpipe_headers_unregister); + +/** + * devlink_dpipe_table_counter_enabled - check if counter allocation + * required + * @devlink: devlink + * @table_name: tables name + * + * Used by driver to check if counter allocation is required. + * After counter allocation is turned on the table entries + * are updated to include counter statistics. + * + * After that point on the driver must respect the counter + * state so that each entry added to the table is added + * with a counter. + */ +bool devlink_dpipe_table_counter_enabled(struct devlink *devlink, + const char *table_name) +{ + struct devlink_dpipe_table *table; + bool enabled; + + rcu_read_lock(); + table = devlink_dpipe_table_find(&devlink->dpipe_table_list, + table_name); + enabled = false; + if (table) + enabled = table->counters_enabled; + rcu_read_unlock(); + return enabled; +} +EXPORT_SYMBOL_GPL(devlink_dpipe_table_counter_enabled); + +/** + * devlink_dpipe_table_register - register dpipe table + * + * @devlink: devlink + * @table_name: table name + * @table_ops: table ops + * @priv: priv + * @size: size + * @counter_control_extern: external control for counters + */ +int devlink_dpipe_table_register(struct devlink *devlink, + const char *table_name, + struct devlink_dpipe_table_ops *table_ops, + void *priv, u64 size, + bool counter_control_extern) +{ + struct devlink_dpipe_table *table; + + if (devlink_dpipe_table_find(&devlink->dpipe_table_list, table_name)) + return -EEXIST; + + table = kzalloc(sizeof(*table), GFP_KERNEL); + if (!table) + return -ENOMEM; + + table->name = table_name; + table->table_ops = table_ops; + table->priv = priv; + table->size = size; + table->counter_control_extern = counter_control_extern; + + mutex_lock(&devlink_mutex); + list_add_tail_rcu(&table->list, &devlink->dpipe_table_list); + mutex_unlock(&devlink_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(devlink_dpipe_table_register); + +/** + * devlink_dpipe_table_unregister - unregister dpipe table + * + * @devlink: devlink + * @table_name: table name + */ +void devlink_dpipe_table_unregister(struct devlink *devlink, + const char *table_name) +{ + struct devlink_dpipe_table *table; + + mutex_lock(&devlink_mutex); + table = devlink_dpipe_table_find(&devlink->dpipe_table_list, + table_name); + if (!table) + goto unlock; + list_del_rcu(&table->list); + mutex_unlock(&devlink_mutex); + kfree_rcu(table, rcu); + return; +unlock: + mutex_unlock(&devlink_mutex); +} +EXPORT_SYMBOL_GPL(devlink_dpipe_table_unregister); + static int __init devlink_module_init(void) { return genl_register_family(&devlink_nl_family); -- cgit From 0f630fcbe5409aaab1a29b48434b28f41bc360ff Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 28 Mar 2017 17:24:11 +0200 Subject: mlxsw: reg: Add counter fields to RITR register Update RITR for counter support. This allows adding counters for ASIC's router ports. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 54 +++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index e7a652c43b5c..82aaa3e837d6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4125,6 +4125,60 @@ MLXSW_ITEM32(reg, ritr, sp_if_system_port, 0x08, 0, 16); */ MLXSW_ITEM32(reg, ritr, sp_if_vid, 0x18, 0, 12); +/* Shared between ingress/egress */ +enum mlxsw_reg_ritr_counter_set_type { + /* No Count. */ + MLXSW_REG_RITR_COUNTER_SET_TYPE_NO_COUNT = 0x0, + /* Basic. Used for router interfaces, counting the following: + * - Error and Discard counters. + * - Unicast, Multicast and Broadcast counters. Sharing the + * same set of counters for the different type of traffic + * (IPv4, IPv6 and mpls). + */ + MLXSW_REG_RITR_COUNTER_SET_TYPE_BASIC = 0x9, +}; + +/* reg_ritr_ingress_counter_index + * Counter Index for flow counter. + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, ingress_counter_index, 0x38, 0, 24); + +/* reg_ritr_ingress_counter_set_type + * Igress Counter Set Type for router interface counter. + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, ingress_counter_set_type, 0x38, 24, 8); + +/* reg_ritr_egress_counter_index + * Counter Index for flow counter. + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, egress_counter_index, 0x3C, 0, 24); + +/* reg_ritr_egress_counter_set_type + * Egress Counter Set Type for router interface counter. + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, egress_counter_set_type, 0x3C, 24, 8); + +static inline void mlxsw_reg_ritr_counter_pack(char *payload, u32 index, + bool enable, bool egress) +{ + enum mlxsw_reg_ritr_counter_set_type set_type; + + if (enable) + set_type = MLXSW_REG_RITR_COUNTER_SET_TYPE_BASIC; + else + set_type = MLXSW_REG_RITR_COUNTER_SET_TYPE_NO_COUNT; + mlxsw_reg_ritr_egress_counter_set_type_set(payload, set_type); + + if (egress) + mlxsw_reg_ritr_egress_counter_index_set(payload, index); + else + mlxsw_reg_ritr_ingress_counter_index_set(payload, index); +} + static inline void mlxsw_reg_ritr_rif_pack(char *payload, u16 rif) { MLXSW_REG_ZERO(ritr, payload); -- cgit From 230ead0141be9668fbaf6c0b708533064d46a9a2 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 28 Mar 2017 17:24:12 +0200 Subject: mlxsw: spectrum: Add placeholder for dpipe Add placeholder for dpipe. Support for specific tables and headers will be introduced in following patches. The headers are shared between all mlxsw_sp instances. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/Makefile | 2 +- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 10 ++++ .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 57 ++++++++++++++++++++++ .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.h | 41 ++++++++++++++++ 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index 95fcacf9c8be..2fb8c6585ac7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -16,7 +16,7 @@ mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \ spectrum_switchdev.o spectrum_router.o \ spectrum_kvdl.o spectrum_acl_tcam.o \ spectrum_acl.o spectrum_flower.o \ - spectrum_cnt.o + spectrum_cnt.o spectrum_dpipe.o mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB) += spectrum_dcb.o obj-$(CONFIG_MLXSW_MINIMAL) += mlxsw_minimal.o mlxsw_minimal-objs := minimal.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index eabfe5c22b05..b031f09bf4e6 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -67,6 +67,7 @@ #include "trap.h" #include "txheader.h" #include "spectrum_cnt.h" +#include "spectrum_dpipe.h" static const char mlxsw_sp_driver_name[] = "mlxsw_spectrum"; static const char mlxsw_sp_driver_version[] = "1.0"; @@ -3339,6 +3340,12 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, goto err_counter_pool_init; } + err = mlxsw_sp_dpipe_init(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to init pipeline debug\n"); + goto err_dpipe_init; + } + err = mlxsw_sp_ports_create(mlxsw_sp); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n"); @@ -3348,6 +3355,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, return 0; err_ports_create: + mlxsw_sp_dpipe_fini(mlxsw_sp); +err_dpipe_init: mlxsw_sp_counter_pool_fini(mlxsw_sp); err_counter_pool_init: mlxsw_sp_acl_fini(mlxsw_sp); @@ -3372,6 +3381,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core) struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); mlxsw_sp_ports_remove(mlxsw_sp); + mlxsw_sp_dpipe_fini(mlxsw_sp); mlxsw_sp_counter_pool_fini(mlxsw_sp); mlxsw_sp_acl_fini(mlxsw_sp); mlxsw_sp_span_fini(mlxsw_sp); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c new file mode 100644 index 000000000000..7c87bc81030a --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -0,0 +1,57 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. + * Copyright (c) 2017 Arkadi Sharshevsky + * + * 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 "spectrum_dpipe.h" + +static struct devlink_dpipe_header *mlxsw_dpipe_headers[] = {}; + +static struct devlink_dpipe_headers mlxsw_sp_dpipe_headers = { + .headers = mlxsw_dpipe_headers, + .headers_count = ARRAY_SIZE(mlxsw_dpipe_headers), +}; + +int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp) +{ + return devlink_dpipe_headers_register(priv_to_devlink(mlxsw_sp->core), + &mlxsw_sp_dpipe_headers); +} + +void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp) +{ + devlink_dpipe_headers_unregister(priv_to_devlink(mlxsw_sp->core)); +} diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h new file mode 100644 index 000000000000..ad16259a05c0 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h @@ -0,0 +1,41 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. + * Copyright (c) 2017 Arkadi Sharshevsky + * + * 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_PIPELINE_H_ +#define _MLXSW_PIPELINE_H_ + +int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp); +void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp); + +#endif /* _MLXSW_PIPELINE_H_*/ -- cgit From d54b70feb696f0d110626438432d0acec3cb4752 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 28 Mar 2017 17:24:13 +0200 Subject: mlxsw: spectrum: Add definition for egress rif table Add definition for egress router interface table. This table describes the final part in the routing pipeline. This table matches the egress interface index (rif index, which is set by the previous stages and determine the out port) and makes the decision of forwarding the packet towards the L2 logic or dropping it. The metadata header is added to represent this internal information. The rif index field is mapped logically to netdevice ifindex. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 117 ++++++++++++++++++++- .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.h | 2 + 2 files changed, 115 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 7c87bc81030a..5dde2222bf4e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -38,20 +38,129 @@ #include "spectrum.h" #include "spectrum_dpipe.h" -static struct devlink_dpipe_header *mlxsw_dpipe_headers[] = {}; +enum mlxsw_sp_field_metadata_id { + MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT, + MLXSW_SP_DPIPE_FIELD_METADATA_L3_FORWARD, + MLXSW_SP_DPIPE_FIELD_METADATA_L3_DROP, +}; + +static struct devlink_dpipe_field mlxsw_sp_dpipe_fields_metadata[] = { + { .name = "erif_port", + .id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT, + .bitwidth = 32, + .mapping_type = DEVLINK_DPIPE_FIELD_MAPPING_TYPE_IFINDEX, + }, + { .name = "l3_forward", + .id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_FORWARD, + .bitwidth = 1, + }, + { .name = "l3_drop", + .id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_DROP, + .bitwidth = 1, + }, +}; + +enum mlxsw_sp_dpipe_header_id { + MLXSW_SP_DPIPE_HEADER_METADATA, +}; + +static struct devlink_dpipe_header mlxsw_sp_dpipe_header_metadata = { + .name = "mlxsw_meta", + .id = MLXSW_SP_DPIPE_HEADER_METADATA, + .fields = mlxsw_sp_dpipe_fields_metadata, + .fields_count = ARRAY_SIZE(mlxsw_sp_dpipe_fields_metadata), +}; + +static struct devlink_dpipe_header *mlxsw_dpipe_headers[] = { + &mlxsw_sp_dpipe_header_metadata, +}; static struct devlink_dpipe_headers mlxsw_sp_dpipe_headers = { .headers = mlxsw_dpipe_headers, .headers_count = ARRAY_SIZE(mlxsw_dpipe_headers), }; +static int mlxsw_sp_dpipe_table_erif_actions_dump(void *priv, + struct sk_buff *skb) +{ + struct devlink_dpipe_action action = {0}; + int err; + + action.type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY; + action.header = &mlxsw_sp_dpipe_header_metadata; + action.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_FORWARD; + + err = devlink_dpipe_action_put(skb, &action); + if (err) + return err; + + action.type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY; + action.header = &mlxsw_sp_dpipe_header_metadata; + action.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_DROP; + + return devlink_dpipe_action_put(skb, &action); +} + +static int mlxsw_sp_dpipe_table_erif_matches_dump(void *priv, + struct sk_buff *skb) +{ + struct devlink_dpipe_match match = {0}; + + match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; + match.header = &mlxsw_sp_dpipe_header_metadata; + match.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT; + + return devlink_dpipe_match_put(skb, &match); +} + +static struct devlink_dpipe_table_ops mlxsw_sp_erif_ops = { + .matches_dump = mlxsw_sp_dpipe_table_erif_matches_dump, + .actions_dump = mlxsw_sp_dpipe_table_erif_actions_dump, +}; + +static int mlxsw_sp_dpipe_erif_table_init(struct mlxsw_sp *mlxsw_sp) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + u64 table_size; + + table_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); + return devlink_dpipe_table_register(devlink, + MLXSW_SP_DPIPE_TABLE_NAME_ERIF, + &mlxsw_sp_erif_ops, + mlxsw_sp, table_size, + false); +} + +static void mlxsw_sp_dpipe_erif_table_fini(struct mlxsw_sp *mlxsw_sp) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + + devlink_dpipe_table_unregister(devlink, MLXSW_SP_DPIPE_TABLE_NAME_ERIF); +} + int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp) { - return devlink_dpipe_headers_register(priv_to_devlink(mlxsw_sp->core), - &mlxsw_sp_dpipe_headers); + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + int err; + + err = devlink_dpipe_headers_register(devlink, + &mlxsw_sp_dpipe_headers); + if (err) + return err; + err = mlxsw_sp_dpipe_erif_table_init(mlxsw_sp); + if (err) + goto err_erif_register; + return 0; + +err_erif_register: + devlink_dpipe_headers_unregister(priv_to_devlink(mlxsw_sp->core)); + return err; } void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp) { - devlink_dpipe_headers_unregister(priv_to_devlink(mlxsw_sp->core)); + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + + mlxsw_sp_dpipe_erif_table_fini(mlxsw_sp); + devlink_dpipe_headers_unregister(devlink); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h index ad16259a05c0..d2089298cba3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h @@ -38,4 +38,6 @@ int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp); void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp); +#define MLXSW_SP_DPIPE_TABLE_NAME_ERIF "mlxsw_erif" + #endif /* _MLXSW_PIPELINE_H_*/ -- cgit From ba73e97a6376c2e0522ff372f2555388deab8ce6 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 28 Mar 2017 17:24:14 +0200 Subject: mlxsw: reg: Add Router Interface Counter Register The RICNT register retrieves per port performance counter. It will be used to query the router interfaces statistics. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/reg.h | 124 ++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 82aaa3e837d6..83b277c8090e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -4341,6 +4341,129 @@ static inline void mlxsw_reg_ratr_eth_entry_pack(char *payload, mlxsw_reg_ratr_eth_destination_mac_memcpy_to(payload, dest_mac); } +/* RICNT - Router Interface Counter Register + * ----------------------------------------- + * The RICNT register retrieves per port performance counters + */ +#define MLXSW_REG_RICNT_ID 0x800B +#define MLXSW_REG_RICNT_LEN 0x100 + +MLXSW_REG_DEFINE(ricnt, MLXSW_REG_RICNT_ID, MLXSW_REG_RICNT_LEN); + +/* reg_ricnt_counter_index + * Counter index + * Access: RW + */ +MLXSW_ITEM32(reg, ricnt, counter_index, 0x04, 0, 24); + +enum mlxsw_reg_ricnt_counter_set_type { + /* No Count. */ + MLXSW_REG_RICNT_COUNTER_SET_TYPE_NO_COUNT = 0x00, + /* Basic. Used for router interfaces, counting the following: + * - Error and Discard counters. + * - Unicast, Multicast and Broadcast counters. Sharing the + * same set of counters for the different type of traffic + * (IPv4, IPv6 and mpls). + */ + MLXSW_REG_RICNT_COUNTER_SET_TYPE_BASIC = 0x09, +}; + +/* reg_ricnt_counter_set_type + * Counter Set Type for router interface counter + * Access: RW + */ +MLXSW_ITEM32(reg, ricnt, counter_set_type, 0x04, 24, 8); + +enum mlxsw_reg_ricnt_opcode { + /* Nop. Supported only for read access*/ + MLXSW_REG_RICNT_OPCODE_NOP = 0x00, + /* Clear. Setting the clr bit will reset the counter value for + * all counters of the specified Router Interface. + */ + MLXSW_REG_RICNT_OPCODE_CLEAR = 0x08, +}; + +/* reg_ricnt_opcode + * Opcode + * Access: RW + */ +MLXSW_ITEM32(reg, ricnt, op, 0x00, 28, 4); + +/* reg_ricnt_good_unicast_packets + * good unicast packets. + * Access: RW + */ +MLXSW_ITEM64(reg, ricnt, good_unicast_packets, 0x08, 0, 64); + +/* reg_ricnt_good_multicast_packets + * good multicast packets. + * Access: RW + */ +MLXSW_ITEM64(reg, ricnt, good_multicast_packets, 0x10, 0, 64); + +/* reg_ricnt_good_broadcast_packets + * good broadcast packets + * Access: RW + */ +MLXSW_ITEM64(reg, ricnt, good_broadcast_packets, 0x18, 0, 64); + +/* reg_ricnt_good_unicast_bytes + * A count of L3 data and padding octets not including L2 headers + * for good unicast frames. + * Access: RW + */ +MLXSW_ITEM64(reg, ricnt, good_unicast_bytes, 0x20, 0, 64); + +/* reg_ricnt_good_multicast_bytes + * A count of L3 data and padding octets not including L2 headers + * for good multicast frames. + * Access: RW + */ +MLXSW_ITEM64(reg, ricnt, good_multicast_bytes, 0x28, 0, 64); + +/* reg_ritr_good_broadcast_bytes + * A count of L3 data and padding octets not including L2 headers + * for good broadcast frames. + * Access: RW + */ +MLXSW_ITEM64(reg, ricnt, good_broadcast_bytes, 0x30, 0, 64); + +/* reg_ricnt_error_packets + * A count of errored frames that do not pass the router checks. + * Access: RW + */ +MLXSW_ITEM64(reg, ricnt, error_packets, 0x38, 0, 64); + +/* reg_ricnt_discrad_packets + * A count of non-errored frames that do not pass the router checks. + * Access: RW + */ +MLXSW_ITEM64(reg, ricnt, discard_packets, 0x40, 0, 64); + +/* reg_ricnt_error_bytes + * A count of L3 data and padding octets not including L2 headers + * for errored frames. + * Access: RW + */ +MLXSW_ITEM64(reg, ricnt, error_bytes, 0x48, 0, 64); + +/* reg_ricnt_discard_bytes + * A count of L3 data and padding octets not including L2 headers + * for non-errored frames that do not pass the router checks. + * Access: RW + */ +MLXSW_ITEM64(reg, ricnt, discard_bytes, 0x50, 0, 64); + +static inline void mlxsw_reg_ricnt_pack(char *payload, u32 index, + enum mlxsw_reg_ricnt_opcode op) +{ + MLXSW_REG_ZERO(ricnt, payload); + mlxsw_reg_ricnt_op_set(payload, op); + mlxsw_reg_ricnt_counter_index_set(payload, index); + mlxsw_reg_ricnt_counter_set_type_set(payload, + MLXSW_REG_RICNT_COUNTER_SET_TYPE_BASIC); +} + /* RALTA - Router Algorithmic LPM Tree Allocation Register * ------------------------------------------------------- * RALTA is used to allocate the LPM trees of the SHSPM method. @@ -6080,6 +6203,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(rgcr), MLXSW_REG(ritr), MLXSW_REG(ratr), + MLXSW_REG(ricnt), MLXSW_REG(ralta), MLXSW_REG(ralst), MLXSW_REG(raltb), -- cgit From e0c0afd8aa4e2f0ecfb7f6d9c4c7f87e07d54132 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 28 Mar 2017 17:24:15 +0200 Subject: mlxsw: spectrum: Support for counters on router interfaces Add support for counter allocation on router interfaces. The allocation depends on the counter state of relevant table. In case the counting is disabled or no counters left the counter index will be set as invalid. Also a counter pool for router allocation is added. Signed-off-by: Arakdi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/resources.h | 2 + drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c | 9 ++ drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h | 1 + .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 164 +++++++++++++++++++++ .../net/ethernet/mellanox/mlxsw/spectrum_router.h | 56 +++++++ 5 files changed, 232 insertions(+) create mode 100644 drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h diff --git a/drivers/net/ethernet/mellanox/mlxsw/resources.h b/drivers/net/ethernet/mellanox/mlxsw/resources.h index ee097691fa8c..9556d934714b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/resources.h +++ b/drivers/net/ethernet/mellanox/mlxsw/resources.h @@ -46,6 +46,7 @@ enum mlxsw_res_id { MLXSW_RES_ID_COUNTER_POOL_SIZE, MLXSW_RES_ID_MAX_SPAN, MLXSW_RES_ID_COUNTER_SIZE_PACKETS_BYTES, + MLXSW_RES_ID_COUNTER_SIZE_ROUTER_BASIC, MLXSW_RES_ID_MAX_SYSTEM_PORT, MLXSW_RES_ID_MAX_LAG, MLXSW_RES_ID_MAX_LAG_MEMBERS, @@ -82,6 +83,7 @@ static u16 mlxsw_res_ids[] = { [MLXSW_RES_ID_COUNTER_POOL_SIZE] = 0x2410, [MLXSW_RES_ID_MAX_SPAN] = 0x2420, [MLXSW_RES_ID_COUNTER_SIZE_PACKETS_BYTES] = 0x2443, + [MLXSW_RES_ID_COUNTER_SIZE_ROUTER_BASIC] = 0x2449, [MLXSW_RES_ID_MAX_SYSTEM_PORT] = 0x2502, [MLXSW_RES_ID_MAX_LAG] = 0x2520, [MLXSW_RES_ID_MAX_LAG_MEMBERS] = 0x2521, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c index 1631e01908c0..0f46775e0307 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.c @@ -56,6 +56,9 @@ static struct mlxsw_sp_counter_sub_pool mlxsw_sp_counter_sub_pools[] = { [MLXSW_SP_COUNTER_SUB_POOL_FLOW] = { .bank_count = 6, }, + [MLXSW_SP_COUNTER_SUB_POOL_RIF] = { + .bank_count = 2, + } }; static int mlxsw_sp_counter_pool_validate(struct mlxsw_sp *mlxsw_sp) @@ -83,6 +86,12 @@ static int mlxsw_sp_counter_sub_pools_prepare(struct mlxsw_sp *mlxsw_sp) return -EIO; sub_pool->entry_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, COUNTER_SIZE_PACKETS_BYTES); + /* Prepare erif pool*/ + sub_pool = &mlxsw_sp_counter_sub_pools[MLXSW_SP_COUNTER_SUB_POOL_RIF]; + if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, COUNTER_SIZE_ROUTER_BASIC)) + return -EIO; + sub_pool->entry_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, + COUNTER_SIZE_ROUTER_BASIC); return 0; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h index 031bc4abbe2d..fd34d0a01073 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_cnt.h @@ -39,6 +39,7 @@ enum mlxsw_sp_counter_sub_pool_id { MLXSW_SP_COUNTER_SUB_POOL_FLOW, + MLXSW_SP_COUNTER_SUB_POOL_RIF, }; int mlxsw_sp_counter_alloc(struct mlxsw_sp *mlxsw_sp, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 905d45997058..b0e0439e250b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -52,6 +52,9 @@ #include "spectrum.h" #include "core.h" #include "reg.h" +#include "spectrum_cnt.h" +#include "spectrum_dpipe.h" +#include "spectrum_router.h" struct mlxsw_sp_rif { struct list_head nexthop_list; @@ -62,8 +65,157 @@ struct mlxsw_sp_rif { int mtu; u16 rif_index; u16 vr_id; + unsigned int counter_ingress; + bool counter_ingress_valid; + unsigned int counter_egress; + bool counter_egress_valid; }; +static unsigned int * +mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif, + enum mlxsw_sp_rif_counter_dir dir) +{ + switch (dir) { + case MLXSW_SP_RIF_COUNTER_EGRESS: + return &rif->counter_egress; + case MLXSW_SP_RIF_COUNTER_INGRESS: + return &rif->counter_ingress; + } + return NULL; +} + +static bool +mlxsw_sp_rif_counter_valid_get(struct mlxsw_sp_rif *rif, + enum mlxsw_sp_rif_counter_dir dir) +{ + switch (dir) { + case MLXSW_SP_RIF_COUNTER_EGRESS: + return rif->counter_egress_valid; + case MLXSW_SP_RIF_COUNTER_INGRESS: + return rif->counter_ingress_valid; + } + return false; +} + +static void +mlxsw_sp_rif_counter_valid_set(struct mlxsw_sp_rif *rif, + enum mlxsw_sp_rif_counter_dir dir, + bool valid) +{ + switch (dir) { + case MLXSW_SP_RIF_COUNTER_EGRESS: + rif->counter_egress_valid = valid; + break; + case MLXSW_SP_RIF_COUNTER_INGRESS: + rif->counter_ingress_valid = valid; + break; + } +} + +static int mlxsw_sp_rif_counter_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index, + unsigned int counter_index, bool enable, + enum mlxsw_sp_rif_counter_dir dir) +{ + char ritr_pl[MLXSW_REG_RITR_LEN]; + bool is_egress = false; + int err; + + if (dir == MLXSW_SP_RIF_COUNTER_EGRESS) + is_egress = true; + mlxsw_reg_ritr_rif_pack(ritr_pl, rif_index); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); + if (err) + return err; + + mlxsw_reg_ritr_counter_pack(ritr_pl, counter_index, enable, + is_egress); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); +} + +int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_rif *rif, + enum mlxsw_sp_rif_counter_dir dir, u64 *cnt) +{ + char ricnt_pl[MLXSW_REG_RICNT_LEN]; + unsigned int *p_counter_index; + bool valid; + int err; + + valid = mlxsw_sp_rif_counter_valid_get(rif, dir); + if (!valid) + return -EINVAL; + + p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir); + if (!p_counter_index) + return -EINVAL; + mlxsw_reg_ricnt_pack(ricnt_pl, *p_counter_index, + MLXSW_REG_RICNT_OPCODE_NOP); + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl); + if (err) + return err; + *cnt = mlxsw_reg_ricnt_good_unicast_packets_get(ricnt_pl); + return 0; +} + +static int mlxsw_sp_rif_counter_clear(struct mlxsw_sp *mlxsw_sp, + unsigned int counter_index) +{ + char ricnt_pl[MLXSW_REG_RICNT_LEN]; + + mlxsw_reg_ricnt_pack(ricnt_pl, counter_index, + MLXSW_REG_RICNT_OPCODE_CLEAR); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ricnt), ricnt_pl); +} + +int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_rif *rif, + enum mlxsw_sp_rif_counter_dir dir) +{ + unsigned int *p_counter_index; + int err; + + p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir); + if (!p_counter_index) + return -EINVAL; + err = mlxsw_sp_counter_alloc(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF, + p_counter_index); + if (err) + return err; + + err = mlxsw_sp_rif_counter_clear(mlxsw_sp, *p_counter_index); + if (err) + goto err_counter_clear; + + err = mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index, + *p_counter_index, true, dir); + if (err) + goto err_counter_edit; + mlxsw_sp_rif_counter_valid_set(rif, dir, true); + return 0; + +err_counter_edit: +err_counter_clear: + mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF, + *p_counter_index); + return err; +} + +void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_rif *rif, + enum mlxsw_sp_rif_counter_dir dir) +{ + unsigned int *p_counter_index; + + p_counter_index = mlxsw_sp_rif_p_counter_get(rif, dir); + if (WARN_ON(!p_counter_index)) + return; + mlxsw_sp_rif_counter_edit(mlxsw_sp, rif->rif_index, + *p_counter_index, false, dir); + mlxsw_sp_counter_free(mlxsw_sp, MLXSW_SP_COUNTER_SUB_POOL_RIF, + *p_counter_index); + mlxsw_sp_rif_counter_valid_set(rif, dir, false); +} + static struct mlxsw_sp_rif * mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp, const struct net_device *dev); @@ -2822,6 +2974,15 @@ mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport, goto err_rif_alloc; } + if (devlink_dpipe_table_counter_enabled(priv_to_devlink(mlxsw_sp->core), + MLXSW_SP_DPIPE_TABLE_NAME_ERIF)) { + err = mlxsw_sp_rif_counter_alloc(mlxsw_sp, rif, + MLXSW_SP_RIF_COUNTER_EGRESS); + if (err) + netdev_dbg(mlxsw_sp_vport->dev, + "Counter alloc Failed err=%d\n", err); + } + f->rif = rif; mlxsw_sp->rifs[rif_index] = rif; vr->rif_count++; @@ -2852,6 +3013,9 @@ static void mlxsw_sp_vport_rif_sp_destroy(struct mlxsw_sp_port *mlxsw_sp_vport, mlxsw_sp_router_rif_gone_sync(mlxsw_sp, rif); + mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_EGRESS); + mlxsw_sp_rif_counter_free(mlxsw_sp, rif, MLXSW_SP_RIF_COUNTER_INGRESS); + vr->rif_count--; mlxsw_sp->rifs[rif_index] = NULL; f->rif = NULL; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h new file mode 100644 index 000000000000..f469dc930097 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -0,0 +1,56 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. + * Copyright (c) 2017 Arkadi Sharshevsky + * + * 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_ROUTER_H_ +#define _MLXSW_ROUTER_H_ + +#include "spectrum.h" + +enum mlxsw_sp_rif_counter_dir { + MLXSW_SP_RIF_COUNTER_INGRESS, + MLXSW_SP_RIF_COUNTER_EGRESS, +}; + +int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_rif *rif, + enum mlxsw_sp_rif_counter_dir dir, + u64 *cnt); +void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_rif *rif, + enum mlxsw_sp_rif_counter_dir dir); +int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_rif *rif, + enum mlxsw_sp_rif_counter_dir dir); + +#endif /* _MLXSW_ROUTER_H_*/ -- cgit From fd1b9d41928824eda4694a1015e7ccf233bfff3e Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 28 Mar 2017 17:24:16 +0200 Subject: mlxsw: spectrum_router: Add rif helper functions Add rif helper function to access the rif index and rif devices ifindex. This functions will be used by dpipe in order to dump the rif table. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 10 ++++++++++ drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index b0e0439e250b..c70c59181014 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2932,6 +2932,16 @@ mlxsw_sp_rif_alloc(u16 rif_index, u16 vr_id, struct net_device *l3_dev, return rif; } +u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif) +{ + return rif->rif_index; +} + +int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif) +{ + return rif->dev->ifindex; +} + static struct mlxsw_sp_rif * mlxsw_sp_vport_rif_sp_create(struct mlxsw_sp_port *mlxsw_sp_vport, struct net_device *l3_dev) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index f469dc930097..c3095fef6697 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -42,6 +42,8 @@ enum mlxsw_sp_rif_counter_dir { MLXSW_SP_RIF_COUNTER_EGRESS, }; +u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif); +int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif); int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_rif *rif, enum mlxsw_sp_rif_counter_dir dir, -- cgit From 2ba5999f009d7e5e48dd348eab84ce155e13a83f Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Tue, 28 Mar 2017 17:24:17 +0200 Subject: mlxsw: spectrum: Add Support for erif table entries access Implement dpipe's table ops for erif table which provide: 1. Getting the entries in the table with the associate values. - match on "mlxsw_meta:erif_index" - action on "mlxsw_meta:forwared_out" 2. Synchronize the hardware in case of enabling/disabling counters which mean removing erif counters from all interfaces. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 185 +++++++++++++++++++++ 1 file changed, 185 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 5dde2222bf4e..ea56f6ade6b4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -37,6 +37,7 @@ #include "spectrum.h" #include "spectrum_dpipe.h" +#include "spectrum_router.h" enum mlxsw_sp_field_metadata_id { MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT, @@ -113,9 +114,193 @@ static int mlxsw_sp_dpipe_table_erif_matches_dump(void *priv, return devlink_dpipe_match_put(skb, &match); } +static void mlxsw_sp_erif_entry_clear(struct devlink_dpipe_entry *entry) +{ + unsigned int value_count, value_index; + struct devlink_dpipe_value *value; + + value = entry->action_values; + value_count = entry->action_values_count; + for (value_index = 0; value_index < value_count; value_index++) { + kfree(value[value_index].value); + kfree(value[value_index].mask); + } + + value = entry->match_values; + value_count = entry->match_values_count; + for (value_index = 0; value_index < value_count; value_index++) { + kfree(value[value_index].value); + kfree(value[value_index].mask); + } +} + +static void +mlxsw_sp_erif_match_action_prepare(struct devlink_dpipe_match *match, + struct devlink_dpipe_action *action) +{ + action->type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY; + action->header = &mlxsw_sp_dpipe_header_metadata; + action->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_L3_FORWARD; + + match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; + match->header = &mlxsw_sp_dpipe_header_metadata; + match->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT; +} + +static int mlxsw_sp_erif_entry_prepare(struct devlink_dpipe_entry *entry, + struct devlink_dpipe_value *match_value, + struct devlink_dpipe_match *match, + struct devlink_dpipe_value *action_value, + struct devlink_dpipe_action *action) +{ + entry->match_values = match_value; + entry->match_values_count = 1; + + entry->action_values = action_value; + entry->action_values_count = 1; + + match_value->match = match; + match_value->value_size = sizeof(u32); + match_value->value = kmalloc(match_value->value_size, GFP_KERNEL); + if (!match_value->value) + return -ENOMEM; + + action_value->action = action; + action_value->value_size = sizeof(u32); + action_value->value = kmalloc(action_value->value_size, GFP_KERNEL); + if (!action_value->value) + goto err_action_alloc; + return 0; + +err_action_alloc: + kfree(match_value->value); + return -ENOMEM; +} + +static int mlxsw_sp_erif_entry_get(struct mlxsw_sp *mlxsw_sp, + struct devlink_dpipe_entry *entry, + struct mlxsw_sp_rif *rif, + bool counters_enabled) +{ + u32 *action_value; + u32 *rif_value; + u64 cnt; + int err; + + /* Set Match RIF index */ + rif_value = entry->match_values->value; + *rif_value = mlxsw_sp_rif_index(rif); + entry->match_values->mapping_value = mlxsw_sp_rif_dev_ifindex(rif); + entry->match_values->mapping_valid = true; + + /* Set Action Forwarding */ + action_value = entry->action_values->value; + *action_value = 1; + + entry->counter_valid = false; + entry->counter = 0; + if (!counters_enabled) + return 0; + + entry->index = mlxsw_sp_rif_index(rif); + err = mlxsw_sp_rif_counter_value_get(mlxsw_sp, rif, + MLXSW_SP_RIF_COUNTER_EGRESS, + &cnt); + if (!err) { + entry->counter = cnt; + entry->counter_valid = true; + } + return 0; +} + +static int +mlxsw_sp_table_erif_entries_dump(void *priv, bool counters_enabled, + struct devlink_dpipe_dump_ctx *dump_ctx) +{ + struct devlink_dpipe_value match_value = {{0}}, action_value = {{0}}; + struct devlink_dpipe_action action = {0}; + struct devlink_dpipe_match match = {0}; + struct devlink_dpipe_entry entry = {0}; + struct mlxsw_sp *mlxsw_sp = priv; + unsigned int rif_count; + int i, j; + int err; + + mlxsw_sp_erif_match_action_prepare(&match, &action); + err = mlxsw_sp_erif_entry_prepare(&entry, &match_value, &match, + &action_value, &action); + if (err) + return err; + + rif_count = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); + rtnl_lock(); + i = 0; +start_again: + err = devlink_dpipe_entry_ctx_prepare(dump_ctx); + if (err) + return err; + j = 0; + for (; i < rif_count; i++) { + if (!mlxsw_sp->rifs[i]) + continue; + err = mlxsw_sp_erif_entry_get(mlxsw_sp, &entry, + mlxsw_sp->rifs[i], + counters_enabled); + if (err) + goto err_entry_get; + err = devlink_dpipe_entry_ctx_append(dump_ctx, &entry); + if (err) { + if (err == -EMSGSIZE) { + if (!j) + goto err_entry_append; + break; + } + goto err_entry_append; + } + j++; + } + + devlink_dpipe_entry_ctx_close(dump_ctx); + if (i != rif_count) + goto start_again; + rtnl_unlock(); + + mlxsw_sp_erif_entry_clear(&entry); + return 0; +err_entry_append: +err_entry_get: + rtnl_unlock(); + mlxsw_sp_erif_entry_clear(&entry); + return err; +} + +static int mlxsw_sp_table_erif_counters_update(void *priv, bool enable) +{ + struct mlxsw_sp *mlxsw_sp = priv; + int i; + + rtnl_lock(); + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) { + if (!mlxsw_sp->rifs[i]) + continue; + if (enable) + mlxsw_sp_rif_counter_alloc(mlxsw_sp, + mlxsw_sp->rifs[i], + MLXSW_SP_RIF_COUNTER_EGRESS); + else + mlxsw_sp_rif_counter_free(mlxsw_sp, + mlxsw_sp->rifs[i], + MLXSW_SP_RIF_COUNTER_EGRESS); + } + rtnl_unlock(); + return 0; +} + static struct devlink_dpipe_table_ops mlxsw_sp_erif_ops = { .matches_dump = mlxsw_sp_dpipe_table_erif_matches_dump, .actions_dump = mlxsw_sp_dpipe_table_erif_actions_dump, + .entries_dump = mlxsw_sp_table_erif_entries_dump, + .counters_set_update = mlxsw_sp_table_erif_counters_update, }; static int mlxsw_sp_dpipe_erif_table_init(struct mlxsw_sp *mlxsw_sp) -- cgit From def499c929a72ba11b25e26e26e900ba3d5c2762 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Mon, 27 Mar 2017 15:46:41 -0700 Subject: vxlan: don't age NTF_EXT_LEARNED fdb entries vxlan driver already implicitly supports installing of external fdb entries with NTF_EXT_LEARNED. This patch just makes sure these entries are not aged by the vxlan driver. An external entity managing these entries will age them out. This is consistent with the use of NTF_EXT_LEARNED in the bridge driver. Signed-off-by: Roopa Prabhu Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 09855be219e9..1e54fb5c883a 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2322,6 +2322,9 @@ static void vxlan_cleanup(unsigned long arg) if (f->state & (NUD_PERMANENT | NUD_NOARP)) continue; + if (f->flags & NTF_EXT_LEARNED) + continue; + timeout = f->used + vxlan->cfg.age_interval * HZ; if (time_before_eq(timeout, jiffies)) { netdev_dbg(vxlan->dev, -- cgit From 5052de8deff5619a9b7071f00084fd0264b58e17 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 27 Mar 2017 22:26:33 -0700 Subject: soc: qcom: smd: Transition client drivers from smd to rpmsg By moving these client drivers to use RPMSG instead of the direct SMD API we can reuse them ontop of the newly added GLINK wire-protocol support found in the 820 and 835 Qualcomm platforms. As the new (RPMSG-based) and old SMD implementations are mutually exclusive we have to change all client drivers in one commit, to make sure we have a working system before and after this transition. Acked-by: Andy Gross Acked-by: Kalle Valo Acked-by: Marcel Holtmann Signed-off-by: Bjorn Andersson Signed-off-by: David S. Miller --- drivers/bluetooth/Kconfig | 2 +- drivers/bluetooth/btqcomsmd.c | 32 +++++++++---------- drivers/net/wireless/ath/wcn36xx/Kconfig | 2 +- drivers/net/wireless/ath/wcn36xx/main.c | 6 ++-- drivers/net/wireless/ath/wcn36xx/smd.c | 10 +++--- drivers/net/wireless/ath/wcn36xx/smd.h | 6 ++-- drivers/net/wireless/ath/wcn36xx/wcn36xx.h | 2 +- drivers/soc/qcom/Kconfig | 6 ++-- drivers/soc/qcom/smd-rpm.c | 43 +++++++++++++------------ drivers/soc/qcom/wcnss_ctrl.c | 50 +++++++++++++++++------------- include/linux/soc/qcom/wcnss_ctrl.h | 11 ++++--- net/qrtr/Kconfig | 2 +- net/qrtr/smd.c | 42 ++++++++++++------------- 13 files changed, 110 insertions(+), 104 deletions(-) diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 08e054507d0b..a6a9dd4d0eef 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -344,7 +344,7 @@ config BT_WILINK config BT_QCOMSMD tristate "Qualcomm SMD based HCI support" - depends on QCOM_SMD || (COMPILE_TEST && QCOM_SMD=n) + depends on RPMSG || (COMPILE_TEST && RPMSG=n) depends on QCOM_WCNSS_CTRL || (COMPILE_TEST && QCOM_WCNSS_CTRL=n) select BT_QCA help diff --git a/drivers/bluetooth/btqcomsmd.c b/drivers/bluetooth/btqcomsmd.c index 8d4868af9bbd..ef730c173d4b 100644 --- a/drivers/bluetooth/btqcomsmd.c +++ b/drivers/bluetooth/btqcomsmd.c @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include @@ -26,8 +26,8 @@ struct btqcomsmd { struct hci_dev *hdev; - struct qcom_smd_channel *acl_channel; - struct qcom_smd_channel *cmd_channel; + struct rpmsg_endpoint *acl_channel; + struct rpmsg_endpoint *cmd_channel; }; static int btqcomsmd_recv(struct hci_dev *hdev, unsigned int type, @@ -48,19 +48,19 @@ static int btqcomsmd_recv(struct hci_dev *hdev, unsigned int type, return hci_recv_frame(hdev, skb); } -static int btqcomsmd_acl_callback(struct qcom_smd_channel *channel, - const void *data, size_t count) +static int btqcomsmd_acl_callback(struct rpmsg_device *rpdev, void *data, + int count, void *priv, u32 addr) { - struct btqcomsmd *btq = qcom_smd_get_drvdata(channel); + struct btqcomsmd *btq = priv; btq->hdev->stat.byte_rx += count; return btqcomsmd_recv(btq->hdev, HCI_ACLDATA_PKT, data, count); } -static int btqcomsmd_cmd_callback(struct qcom_smd_channel *channel, - const void *data, size_t count) +static int btqcomsmd_cmd_callback(struct rpmsg_device *rpdev, void *data, + int count, void *priv, u32 addr) { - struct btqcomsmd *btq = qcom_smd_get_drvdata(channel); + struct btqcomsmd *btq = priv; return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count); } @@ -72,12 +72,12 @@ static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb) switch (hci_skb_pkt_type(skb)) { case HCI_ACLDATA_PKT: - ret = qcom_smd_send(btq->acl_channel, skb->data, skb->len); + ret = rpmsg_send(btq->acl_channel, skb->data, skb->len); hdev->stat.acl_tx++; hdev->stat.byte_tx += skb->len; break; case HCI_COMMAND_PKT: - ret = qcom_smd_send(btq->cmd_channel, skb->data, skb->len); + ret = rpmsg_send(btq->cmd_channel, skb->data, skb->len); hdev->stat.cmd_tx++; break; default: @@ -114,18 +114,15 @@ static int btqcomsmd_probe(struct platform_device *pdev) wcnss = dev_get_drvdata(pdev->dev.parent); btq->acl_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_ACL", - btqcomsmd_acl_callback); + btqcomsmd_acl_callback, btq); if (IS_ERR(btq->acl_channel)) return PTR_ERR(btq->acl_channel); btq->cmd_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_CMD", - btqcomsmd_cmd_callback); + btqcomsmd_cmd_callback, btq); if (IS_ERR(btq->cmd_channel)) return PTR_ERR(btq->cmd_channel); - qcom_smd_set_drvdata(btq->acl_channel, btq); - qcom_smd_set_drvdata(btq->cmd_channel, btq); - hdev = hci_alloc_dev(); if (!hdev) return -ENOMEM; @@ -158,6 +155,9 @@ static int btqcomsmd_remove(struct platform_device *pdev) hci_unregister_dev(btq->hdev); hci_free_dev(btq->hdev); + rpmsg_destroy_ept(btq->cmd_channel); + rpmsg_destroy_ept(btq->acl_channel); + return 0; } diff --git a/drivers/net/wireless/ath/wcn36xx/Kconfig b/drivers/net/wireless/ath/wcn36xx/Kconfig index 4b83e87f0b94..20bf967a70b9 100644 --- a/drivers/net/wireless/ath/wcn36xx/Kconfig +++ b/drivers/net/wireless/ath/wcn36xx/Kconfig @@ -2,7 +2,7 @@ config WCN36XX tristate "Qualcomm Atheros WCN3660/3680 support" depends on MAC80211 && HAS_DMA depends on QCOM_WCNSS_CTRL || QCOM_WCNSS_CTRL=n - depends on QCOM_SMD || QCOM_SMD=n + depends on RPMSG || RPMSG=n ---help--- This module adds support for wireless adapters based on Qualcomm Atheros WCN3660 and WCN3680 mobile chipsets. diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 7a0c2e7da7f6..bb7110f7fc86 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include "wcn36xx.h" @@ -1218,15 +1218,13 @@ static int wcn36xx_probe(struct platform_device *pdev) INIT_WORK(&wcn->scan_work, wcn36xx_hw_scan_worker); - wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process); + wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process, hw); if (IS_ERR(wcn->smd_channel)) { wcn36xx_err("failed to open WLAN_CTRL channel\n"); ret = PTR_ERR(wcn->smd_channel); goto out_wq; } - qcom_smd_set_drvdata(wcn->smd_channel, hw); - addr = of_get_property(pdev->dev.of_node, "local-mac-address", &ret); if (addr && ret != ETH_ALEN) { wcn36xx_err("invalid local-mac-address\n"); diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 1c2966f7db7a..9c6590d5348a 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include "smd.h" struct wcn36xx_cfg_val { @@ -254,7 +254,7 @@ static int wcn36xx_smd_send_and_wait(struct wcn36xx *wcn, size_t len) init_completion(&wcn->hal_rsp_compl); start = jiffies; - ret = qcom_smd_send(wcn->smd_channel, wcn->hal_buf, len); + ret = rpmsg_send(wcn->smd_channel, wcn->hal_buf, len); if (ret) { wcn36xx_err("HAL TX failed\n"); goto out; @@ -2205,11 +2205,11 @@ out: return ret; } -int wcn36xx_smd_rsp_process(struct qcom_smd_channel *channel, - const void *buf, size_t len) +int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev, + void *buf, int len, void *priv, u32 addr) { const struct wcn36xx_hal_msg_header *msg_header = buf; - struct ieee80211_hw *hw = qcom_smd_get_drvdata(channel); + struct ieee80211_hw *hw = priv; struct wcn36xx *wcn = hw->priv; struct wcn36xx_hal_ind_msg *msg_ind; wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "SMD <<< ", buf, len); diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h index 8892ccd67b14..013fc9546f56 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.h +++ b/drivers/net/wireless/ath/wcn36xx/smd.h @@ -51,7 +51,7 @@ struct wcn36xx_hal_ind_msg { }; struct wcn36xx; -struct qcom_smd_channel; +struct rpmsg_device; int wcn36xx_smd_open(struct wcn36xx *wcn); void wcn36xx_smd_close(struct wcn36xx *wcn); @@ -129,8 +129,8 @@ int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index); int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value); -int wcn36xx_smd_rsp_process(struct qcom_smd_channel *channel, - const void *buf, size_t len); +int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev, + void *buf, int len, void *priv, u32 addr); int wcn36xx_smd_set_mc_list(struct wcn36xx *wcn, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index 7423998ddeb4..b52b4da9a967 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -195,7 +195,7 @@ struct wcn36xx { void __iomem *ccu_base; void __iomem *dxe_base; - struct qcom_smd_channel *smd_channel; + struct rpmsg_endpoint *smd_channel; struct qcom_smem_state *tx_enable_state; unsigned tx_enable_state_bit; diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 78b1bb7bcf20..4e090c697eb6 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -43,7 +43,8 @@ config QCOM_SMD config QCOM_SMD_RPM tristate "Qualcomm Resource Power Manager (RPM) over SMD" - depends on QCOM_SMD && OF + depends on ARCH_QCOM + depends on RPMSG && OF help If you say yes to this option, support will be included for the Resource Power Manager system found in the Qualcomm 8974 based @@ -76,7 +77,8 @@ config QCOM_SMSM config QCOM_WCNSS_CTRL tristate "Qualcomm WCNSS control driver" - depends on QCOM_SMD + depends on ARCH_QCOM + depends on RPMSG help Client driver for the WCNSS_CTRL SMD channel, used to download nv firmware to a newly booted WCNSS chip. diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c index 6609d7e0edb0..0dcf1bf33126 100644 --- a/drivers/soc/qcom/smd-rpm.c +++ b/drivers/soc/qcom/smd-rpm.c @@ -19,7 +19,7 @@ #include #include -#include +#include #include #define RPM_REQUEST_TIMEOUT (5 * HZ) @@ -32,7 +32,7 @@ * @ack_status: result of the rpm request */ struct qcom_smd_rpm { - struct qcom_smd_channel *rpm_channel; + struct rpmsg_endpoint *rpm_channel; struct device *dev; struct completion ack; @@ -133,7 +133,7 @@ int qcom_rpm_smd_write(struct qcom_smd_rpm *rpm, pkt->req.data_len = cpu_to_le32(count); memcpy(pkt->payload, buf, count); - ret = qcom_smd_send(rpm->rpm_channel, pkt, size); + ret = rpmsg_send(rpm->rpm_channel, pkt, size); if (ret) goto out; @@ -150,14 +150,16 @@ out: } EXPORT_SYMBOL(qcom_rpm_smd_write); -static int qcom_smd_rpm_callback(struct qcom_smd_channel *channel, - const void *data, - size_t count) +static int qcom_smd_rpm_callback(struct rpmsg_device *rpdev, + void *data, + int count, + void *priv, + u32 addr) { const struct qcom_rpm_header *hdr = data; size_t hdr_length = le32_to_cpu(hdr->length); const struct qcom_rpm_message *msg; - struct qcom_smd_rpm *rpm = qcom_smd_get_drvdata(channel); + struct qcom_smd_rpm *rpm = dev_get_drvdata(&rpdev->dev); const u8 *buf = data + sizeof(struct qcom_rpm_header); const u8 *end = buf + hdr_length; char msgbuf[32]; @@ -196,29 +198,27 @@ static int qcom_smd_rpm_callback(struct qcom_smd_channel *channel, return 0; } -static int qcom_smd_rpm_probe(struct qcom_smd_device *sdev) +static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev) { struct qcom_smd_rpm *rpm; - rpm = devm_kzalloc(&sdev->dev, sizeof(*rpm), GFP_KERNEL); + rpm = devm_kzalloc(&rpdev->dev, sizeof(*rpm), GFP_KERNEL); if (!rpm) return -ENOMEM; mutex_init(&rpm->lock); init_completion(&rpm->ack); - rpm->dev = &sdev->dev; - rpm->rpm_channel = sdev->channel; - qcom_smd_set_drvdata(sdev->channel, rpm); + rpm->dev = &rpdev->dev; + rpm->rpm_channel = rpdev->ept; + dev_set_drvdata(&rpdev->dev, rpm); - dev_set_drvdata(&sdev->dev, rpm); - - return of_platform_populate(sdev->dev.of_node, NULL, NULL, &sdev->dev); + return of_platform_populate(rpdev->dev.of_node, NULL, NULL, &rpdev->dev); } -static void qcom_smd_rpm_remove(struct qcom_smd_device *sdev) +static void qcom_smd_rpm_remove(struct rpmsg_device *rpdev) { - of_platform_depopulate(&sdev->dev); + of_platform_depopulate(&rpdev->dev); } static const struct of_device_id qcom_smd_rpm_of_match[] = { @@ -229,26 +229,25 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = { }; MODULE_DEVICE_TABLE(of, qcom_smd_rpm_of_match); -static struct qcom_smd_driver qcom_smd_rpm_driver = { +static struct rpmsg_driver qcom_smd_rpm_driver = { .probe = qcom_smd_rpm_probe, .remove = qcom_smd_rpm_remove, .callback = qcom_smd_rpm_callback, - .driver = { + .drv = { .name = "qcom_smd_rpm", - .owner = THIS_MODULE, .of_match_table = qcom_smd_rpm_of_match, }, }; static int __init qcom_smd_rpm_init(void) { - return qcom_smd_driver_register(&qcom_smd_rpm_driver); + return register_rpmsg_driver(&qcom_smd_rpm_driver); } arch_initcall(qcom_smd_rpm_init); static void __exit qcom_smd_rpm_exit(void) { - qcom_smd_driver_unregister(&qcom_smd_rpm_driver); + unregister_rpmsg_driver(&qcom_smd_rpm_driver); } module_exit(qcom_smd_rpm_exit); diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c index 520aedd29965..b9069184df19 100644 --- a/drivers/soc/qcom/wcnss_ctrl.c +++ b/drivers/soc/qcom/wcnss_ctrl.c @@ -14,10 +14,10 @@ #include #include #include -#include #include #include #include +#include #include #define WCNSS_REQUEST_TIMEOUT (5 * HZ) @@ -40,7 +40,7 @@ */ struct wcnss_ctrl { struct device *dev; - struct qcom_smd_channel *channel; + struct rpmsg_endpoint *channel; struct completion ack; struct completion cbc; @@ -122,11 +122,13 @@ struct wcnss_download_nv_resp { * * Handles any incoming packets from the remote WCNSS_CTRL service. */ -static int wcnss_ctrl_smd_callback(struct qcom_smd_channel *channel, - const void *data, - size_t count) +static int wcnss_ctrl_smd_callback(struct rpmsg_device *rpdev, + void *data, + int count, + void *priv, + u32 addr) { - struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(channel); + struct wcnss_ctrl *wcnss = dev_get_drvdata(&rpdev->dev); const struct wcnss_download_nv_resp *nvresp; const struct wcnss_version_resp *version; const struct wcnss_msg_hdr *hdr = data; @@ -180,7 +182,7 @@ static int wcnss_request_version(struct wcnss_ctrl *wcnss) msg.type = WCNSS_VERSION_REQ; msg.len = sizeof(msg); - ret = qcom_smd_send(wcnss->channel, &msg, sizeof(msg)); + ret = rpmsg_send(wcnss->channel, &msg, sizeof(msg)); if (ret < 0) return ret; @@ -238,7 +240,7 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc) memcpy(req->fragment, data, req->frag_size); - ret = qcom_smd_send(wcnss->channel, req, req->hdr.len); + ret = rpmsg_send(wcnss->channel, req, req->hdr.len); if (ret < 0) { dev_err(wcnss->dev, "failed to send smd packet\n"); goto release_fw; @@ -274,11 +276,16 @@ free_req: * @name: SMD channel name * @cb: callback to handle incoming data on the channel */ -struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb) +struct rpmsg_endpoint *qcom_wcnss_open_channel(void *wcnss, const char *name, rpmsg_rx_cb_t cb, void *priv) { + struct rpmsg_channel_info chinfo; struct wcnss_ctrl *_wcnss = wcnss; - return qcom_smd_open_channel(_wcnss->channel, name, cb); + strncpy(chinfo.name, name, sizeof(chinfo.name)); + chinfo.src = RPMSG_ADDR_ANY; + chinfo.dst = RPMSG_ADDR_ANY; + + return rpmsg_create_ept(_wcnss->channel->rpdev, cb, priv, chinfo); } EXPORT_SYMBOL(qcom_wcnss_open_channel); @@ -306,35 +313,34 @@ static void wcnss_async_probe(struct work_struct *work) of_platform_populate(wcnss->dev->of_node, NULL, NULL, wcnss->dev); } -static int wcnss_ctrl_probe(struct qcom_smd_device *sdev) +static int wcnss_ctrl_probe(struct rpmsg_device *rpdev) { struct wcnss_ctrl *wcnss; - wcnss = devm_kzalloc(&sdev->dev, sizeof(*wcnss), GFP_KERNEL); + wcnss = devm_kzalloc(&rpdev->dev, sizeof(*wcnss), GFP_KERNEL); if (!wcnss) return -ENOMEM; - wcnss->dev = &sdev->dev; - wcnss->channel = sdev->channel; + wcnss->dev = &rpdev->dev; + wcnss->channel = rpdev->ept; init_completion(&wcnss->ack); init_completion(&wcnss->cbc); INIT_WORK(&wcnss->probe_work, wcnss_async_probe); - qcom_smd_set_drvdata(sdev->channel, wcnss); - dev_set_drvdata(&sdev->dev, wcnss); + dev_set_drvdata(&rpdev->dev, wcnss); schedule_work(&wcnss->probe_work); return 0; } -static void wcnss_ctrl_remove(struct qcom_smd_device *sdev) +static void wcnss_ctrl_remove(struct rpmsg_device *rpdev) { - struct wcnss_ctrl *wcnss = qcom_smd_get_drvdata(sdev->channel); + struct wcnss_ctrl *wcnss = dev_get_drvdata(&rpdev->dev); cancel_work_sync(&wcnss->probe_work); - of_platform_depopulate(&sdev->dev); + of_platform_depopulate(&rpdev->dev); } static const struct of_device_id wcnss_ctrl_of_match[] = { @@ -342,18 +348,18 @@ static const struct of_device_id wcnss_ctrl_of_match[] = { {} }; -static struct qcom_smd_driver wcnss_ctrl_driver = { +static struct rpmsg_driver wcnss_ctrl_driver = { .probe = wcnss_ctrl_probe, .remove = wcnss_ctrl_remove, .callback = wcnss_ctrl_smd_callback, - .driver = { + .drv = { .name = "qcom_wcnss_ctrl", .owner = THIS_MODULE, .of_match_table = wcnss_ctrl_of_match, }, }; -module_qcom_smd_driver(wcnss_ctrl_driver); +module_rpmsg_driver(wcnss_ctrl_driver); MODULE_DESCRIPTION("Qualcomm WCNSS control driver"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/soc/qcom/wcnss_ctrl.h b/include/linux/soc/qcom/wcnss_ctrl.h index eab64976a73b..a4dd4d7c711d 100644 --- a/include/linux/soc/qcom/wcnss_ctrl.h +++ b/include/linux/soc/qcom/wcnss_ctrl.h @@ -1,16 +1,19 @@ #ifndef __WCNSS_CTRL_H__ #define __WCNSS_CTRL_H__ -#include +#include #if IS_ENABLED(CONFIG_QCOM_WCNSS_CTRL) -struct qcom_smd_channel *qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb); +struct rpmsg_endpoint *qcom_wcnss_open_channel(void *wcnss, const char *name, + rpmsg_rx_cb_t cb, void *priv); #else -static inline struct qcom_smd_channel* -qcom_wcnss_open_channel(void *wcnss, const char *name, qcom_smd_cb_t cb) +static struct rpmsg_endpoint *qcom_wcnss_open_channel(void *wcnss, + const char *name, + rpmsg_rx_cb_t cb, + void *priv) { WARN_ON(1); return ERR_PTR(-ENXIO); diff --git a/net/qrtr/Kconfig b/net/qrtr/Kconfig index b83c6807a5ae..326fd97444f5 100644 --- a/net/qrtr/Kconfig +++ b/net/qrtr/Kconfig @@ -16,7 +16,7 @@ if QRTR config QRTR_SMD tristate "SMD IPC Router channels" - depends on QCOM_SMD || (COMPILE_TEST && QCOM_SMD=n) + depends on RPMSG || (COMPILE_TEST && RPMSG=n) ---help--- Say Y here to support SMD based ipcrouter channels. SMD is the most common transport for IPC Router. diff --git a/net/qrtr/smd.c b/net/qrtr/smd.c index 0d11132b3370..50615d5efac1 100644 --- a/net/qrtr/smd.c +++ b/net/qrtr/smd.c @@ -14,21 +14,21 @@ #include #include -#include +#include #include "qrtr.h" struct qrtr_smd_dev { struct qrtr_endpoint ep; - struct qcom_smd_channel *channel; + struct rpmsg_endpoint *channel; struct device *dev; }; /* from smd to qrtr */ -static int qcom_smd_qrtr_callback(struct qcom_smd_channel *channel, - const void *data, size_t len) +static int qcom_smd_qrtr_callback(struct rpmsg_device *rpdev, + void *data, int len, void *priv, u32 addr) { - struct qrtr_smd_dev *qdev = qcom_smd_get_drvdata(channel); + struct qrtr_smd_dev *qdev = dev_get_drvdata(&rpdev->dev); int rc; if (!qdev) @@ -54,7 +54,7 @@ static int qcom_smd_qrtr_send(struct qrtr_endpoint *ep, struct sk_buff *skb) if (rc) goto out; - rc = qcom_smd_send(qdev->channel, skb->data, skb->len); + rc = rpmsg_send(qdev->channel, skb->data, skb->len); out: if (rc) @@ -64,57 +64,55 @@ out: return rc; } -static int qcom_smd_qrtr_probe(struct qcom_smd_device *sdev) +static int qcom_smd_qrtr_probe(struct rpmsg_device *rpdev) { struct qrtr_smd_dev *qdev; int rc; - qdev = devm_kzalloc(&sdev->dev, sizeof(*qdev), GFP_KERNEL); + qdev = devm_kzalloc(&rpdev->dev, sizeof(*qdev), GFP_KERNEL); if (!qdev) return -ENOMEM; - qdev->channel = sdev->channel; - qdev->dev = &sdev->dev; + qdev->channel = rpdev->ept; + qdev->dev = &rpdev->dev; qdev->ep.xmit = qcom_smd_qrtr_send; rc = qrtr_endpoint_register(&qdev->ep, QRTR_EP_NID_AUTO); if (rc) return rc; - qcom_smd_set_drvdata(sdev->channel, qdev); - dev_set_drvdata(&sdev->dev, qdev); + dev_set_drvdata(&rpdev->dev, qdev); - dev_dbg(&sdev->dev, "Qualcomm SMD QRTR driver probed\n"); + dev_dbg(&rpdev->dev, "Qualcomm SMD QRTR driver probed\n"); return 0; } -static void qcom_smd_qrtr_remove(struct qcom_smd_device *sdev) +static void qcom_smd_qrtr_remove(struct rpmsg_device *rpdev) { - struct qrtr_smd_dev *qdev = dev_get_drvdata(&sdev->dev); + struct qrtr_smd_dev *qdev = dev_get_drvdata(&rpdev->dev); qrtr_endpoint_unregister(&qdev->ep); - dev_set_drvdata(&sdev->dev, NULL); + dev_set_drvdata(&rpdev->dev, NULL); } -static const struct qcom_smd_id qcom_smd_qrtr_smd_match[] = { +static const struct rpmsg_device_id qcom_smd_qrtr_smd_match[] = { { "IPCRTR" }, {} }; -static struct qcom_smd_driver qcom_smd_qrtr_driver = { +static struct rpmsg_driver qcom_smd_qrtr_driver = { .probe = qcom_smd_qrtr_probe, .remove = qcom_smd_qrtr_remove, .callback = qcom_smd_qrtr_callback, - .smd_match_table = qcom_smd_qrtr_smd_match, - .driver = { + .id_table = qcom_smd_qrtr_smd_match, + .drv = { .name = "qcom_smd_qrtr", - .owner = THIS_MODULE, }, }; -module_qcom_smd_driver(qcom_smd_qrtr_driver); +module_rpmsg_driver(qcom_smd_qrtr_driver); MODULE_DESCRIPTION("Qualcomm IPC-Router SMD interface driver"); MODULE_LICENSE("GPL v2"); -- cgit From 395a48053af6c5e0f0217b610dcb7225ea3e3e42 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 27 Mar 2017 22:26:34 -0700 Subject: soc: qcom: smd: Remove standalone driver Remove the standalone SMD implementation as we have transitioned the client drivers to use the RPMSG based one. Also remove all dependencies on QCOM_SMD from Kconfig files, in order to keep them selectable in the absence of the removed symbol. Acked-by: Andy Gross Signed-off-by: Bjorn Andersson Signed-off-by: David S. Miller --- drivers/remoteproc/Kconfig | 6 +- drivers/rpmsg/Kconfig | 1 - drivers/soc/qcom/Kconfig | 8 - drivers/soc/qcom/Makefile | 1 - drivers/soc/qcom/smd.c | 1560 ---------------------------------------- include/linux/rpmsg/qcom_smd.h | 2 +- include/linux/soc/qcom/smd.h | 139 ---- 7 files changed, 4 insertions(+), 1713 deletions(-) delete mode 100644 drivers/soc/qcom/smd.c delete mode 100644 include/linux/soc/qcom/smd.h diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 1dc43fc5f65f..faad69a1a597 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -76,7 +76,7 @@ config QCOM_ADSP_PIL depends on OF && ARCH_QCOM depends on REMOTEPROC depends on QCOM_SMEM - depends on RPMSG_QCOM_SMD || QCOM_SMD || (COMPILE_TEST && QCOM_SMD=n && RPMSG_QCOM_SMD=n) + depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) select MFD_SYSCON select QCOM_MDT_LOADER select QCOM_RPROC_COMMON @@ -93,7 +93,7 @@ config QCOM_Q6V5_PIL depends on OF && ARCH_QCOM depends on QCOM_SMEM depends on REMOTEPROC - depends on RPMSG_QCOM_SMD || QCOM_SMD || (COMPILE_TEST && QCOM_SMD=n && RPMSG_QCOM_SMD=n) + depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) select MFD_SYSCON select QCOM_RPROC_COMMON select QCOM_SCM @@ -104,7 +104,7 @@ config QCOM_Q6V5_PIL config QCOM_WCNSS_PIL tristate "Qualcomm WCNSS Peripheral Image Loader" depends on OF && ARCH_QCOM - depends on RPMSG_QCOM_SMD || QCOM_SMD || (COMPILE_TEST && QCOM_SMD=n && RPMSG_QCOM_SMD=n) + depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) depends on QCOM_SMEM depends on REMOTEPROC select QCOM_MDT_LOADER diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index f12ac0b28263..edc008f55663 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -16,7 +16,6 @@ config RPMSG_CHAR config RPMSG_QCOM_SMD tristate "Qualcomm Shared Memory Driver (SMD)" depends on QCOM_SMEM - depends on QCOM_SMD=n select RPMSG help Say y here to enable support for the Qualcomm Shared Memory Driver diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 4e090c697eb6..9fca977ef18d 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -33,14 +33,6 @@ config QCOM_SMEM The driver provides an interface to items in a heap shared among all processors in a Qualcomm platform. -config QCOM_SMD - tristate "Qualcomm Shared Memory Driver (SMD)" - depends on QCOM_SMEM - help - Say y here to enable support for the Qualcomm Shared Memory Driver - providing communication channels to remote processors in Qualcomm - platforms. - config QCOM_SMD_RPM tristate "Qualcomm Resource Power Manager (RPM) over SMD" depends on ARCH_QCOM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 1f30260b06b8..414f0de274fa 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -1,7 +1,6 @@ obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_PM) += spm.o -obj-$(CONFIG_QCOM_SMD) += smd.o obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o obj-$(CONFIG_QCOM_SMEM) += smem.o obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o diff --git a/drivers/soc/qcom/smd.c b/drivers/soc/qcom/smd.c deleted file mode 100644 index 322034ab9d37..000000000000 --- a/drivers/soc/qcom/smd.c +++ /dev/null @@ -1,1560 +0,0 @@ -/* - * Copyright (c) 2015, Sony Mobile Communications AB. - * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * The Qualcomm Shared Memory communication solution provides point-to-point - * channels for clients to send and receive streaming or packet based data. - * - * Each channel consists of a control item (channel info) and a ring buffer - * pair. The channel info carry information related to channel state, flow - * control and the offsets within the ring buffer. - * - * All allocated channels are listed in an allocation table, identifying the - * pair of items by name, type and remote processor. - * - * Upon creating a new channel the remote processor allocates channel info and - * ring buffer items from the smem heap and populate the allocation table. An - * interrupt is sent to the other end of the channel and a scan for new - * channels should be done. A channel never goes away, it will only change - * state. - * - * The remote processor signals it intent for bring up the communication - * channel by setting the state of its end of the channel to "opening" and - * sends out an interrupt. We detect this change and register a smd device to - * consume the channel. Upon finding a consumer we finish the handshake and the - * channel is up. - * - * Upon closing a channel, the remote processor will update the state of its - * end of the channel and signal us, we will then unregister any attached - * device and close our end of the channel. - * - * Devices attached to a channel can use the qcom_smd_send function to push - * data to the channel, this is done by copying the data into the tx ring - * buffer, updating the pointers in the channel info and signaling the remote - * processor. - * - * The remote processor does the equivalent when it transfer data and upon - * receiving the interrupt we check the channel info for new data and delivers - * this to the attached device. If the device is not ready to receive the data - * we leave it in the ring buffer for now. - */ - -struct smd_channel_info; -struct smd_channel_info_pair; -struct smd_channel_info_word; -struct smd_channel_info_word_pair; - -#define SMD_ALLOC_TBL_COUNT 2 -#define SMD_ALLOC_TBL_SIZE 64 - -/* - * This lists the various smem heap items relevant for the allocation table and - * smd channel entries. - */ -static const struct { - unsigned alloc_tbl_id; - unsigned info_base_id; - unsigned fifo_base_id; -} smem_items[SMD_ALLOC_TBL_COUNT] = { - { - .alloc_tbl_id = 13, - .info_base_id = 14, - .fifo_base_id = 338 - }, - { - .alloc_tbl_id = 266, - .info_base_id = 138, - .fifo_base_id = 202, - }, -}; - -/** - * struct qcom_smd_edge - representing a remote processor - * @dev: device for this edge - * @of_node: of_node handle for information related to this edge - * @edge_id: identifier of this edge - * @remote_pid: identifier of remote processor - * @irq: interrupt for signals on this edge - * @ipc_regmap: regmap handle holding the outgoing ipc register - * @ipc_offset: offset within @ipc_regmap of the register for ipc - * @ipc_bit: bit in the register at @ipc_offset of @ipc_regmap - * @channels: list of all channels detected on this edge - * @channels_lock: guard for modifications of @channels - * @allocated: array of bitmaps representing already allocated channels - * @smem_available: last available amount of smem triggering a channel scan - * @scan_work: work item for discovering new channels - * @state_work: work item for edge state changes - */ -struct qcom_smd_edge { - struct device dev; - - struct device_node *of_node; - unsigned edge_id; - unsigned remote_pid; - - int irq; - - struct regmap *ipc_regmap; - int ipc_offset; - int ipc_bit; - - struct list_head channels; - spinlock_t channels_lock; - - DECLARE_BITMAP(allocated[SMD_ALLOC_TBL_COUNT], SMD_ALLOC_TBL_SIZE); - - unsigned smem_available; - - wait_queue_head_t new_channel_event; - - struct work_struct scan_work; - struct work_struct state_work; -}; - -#define to_smd_edge(d) container_of(d, struct qcom_smd_edge, dev) - -/* - * SMD channel states. - */ -enum smd_channel_state { - SMD_CHANNEL_CLOSED, - SMD_CHANNEL_OPENING, - SMD_CHANNEL_OPENED, - SMD_CHANNEL_FLUSHING, - SMD_CHANNEL_CLOSING, - SMD_CHANNEL_RESET, - SMD_CHANNEL_RESET_OPENING -}; - -/** - * struct qcom_smd_channel - smd channel struct - * @edge: qcom_smd_edge this channel is living on - * @qsdev: reference to a associated smd client device - * @name: name of the channel - * @state: local state of the channel - * @remote_state: remote state of the channel - * @info: byte aligned outgoing/incoming channel info - * @info_word: word aligned outgoing/incoming channel info - * @tx_lock: lock to make writes to the channel mutually exclusive - * @fblockread_event: wakeup event tied to tx fBLOCKREADINTR - * @tx_fifo: pointer to the outgoing ring buffer - * @rx_fifo: pointer to the incoming ring buffer - * @fifo_size: size of each ring buffer - * @bounce_buffer: bounce buffer for reading wrapped packets - * @cb: callback function registered for this channel - * @recv_lock: guard for rx info modifications and cb pointer - * @pkt_size: size of the currently handled packet - * @list: lite entry for @channels in qcom_smd_edge - */ -struct qcom_smd_channel { - struct qcom_smd_edge *edge; - - struct qcom_smd_device *qsdev; - - char *name; - enum smd_channel_state state; - enum smd_channel_state remote_state; - - struct smd_channel_info_pair *info; - struct smd_channel_info_word_pair *info_word; - - struct mutex tx_lock; - wait_queue_head_t fblockread_event; - - void *tx_fifo; - void *rx_fifo; - int fifo_size; - - void *bounce_buffer; - qcom_smd_cb_t cb; - - spinlock_t recv_lock; - - int pkt_size; - - void *drvdata; - - struct list_head list; -}; - -/* - * Format of the smd_info smem items, for byte aligned channels. - */ -struct smd_channel_info { - __le32 state; - u8 fDSR; - u8 fCTS; - u8 fCD; - u8 fRI; - u8 fHEAD; - u8 fTAIL; - u8 fSTATE; - u8 fBLOCKREADINTR; - __le32 tail; - __le32 head; -}; - -struct smd_channel_info_pair { - struct smd_channel_info tx; - struct smd_channel_info rx; -}; - -/* - * Format of the smd_info smem items, for word aligned channels. - */ -struct smd_channel_info_word { - __le32 state; - __le32 fDSR; - __le32 fCTS; - __le32 fCD; - __le32 fRI; - __le32 fHEAD; - __le32 fTAIL; - __le32 fSTATE; - __le32 fBLOCKREADINTR; - __le32 tail; - __le32 head; -}; - -struct smd_channel_info_word_pair { - struct smd_channel_info_word tx; - struct smd_channel_info_word rx; -}; - -#define GET_RX_CHANNEL_FLAG(channel, param) \ - ({ \ - BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u8)); \ - channel->info_word ? \ - le32_to_cpu(channel->info_word->rx.param) : \ - channel->info->rx.param; \ - }) - -#define GET_RX_CHANNEL_INFO(channel, param) \ - ({ \ - BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u32)); \ - le32_to_cpu(channel->info_word ? \ - channel->info_word->rx.param : \ - channel->info->rx.param); \ - }) - -#define SET_RX_CHANNEL_FLAG(channel, param, value) \ - ({ \ - BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u8)); \ - if (channel->info_word) \ - channel->info_word->rx.param = cpu_to_le32(value); \ - else \ - channel->info->rx.param = value; \ - }) - -#define SET_RX_CHANNEL_INFO(channel, param, value) \ - ({ \ - BUILD_BUG_ON(sizeof(channel->info->rx.param) != sizeof(u32)); \ - if (channel->info_word) \ - channel->info_word->rx.param = cpu_to_le32(value); \ - else \ - channel->info->rx.param = cpu_to_le32(value); \ - }) - -#define GET_TX_CHANNEL_FLAG(channel, param) \ - ({ \ - BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u8)); \ - channel->info_word ? \ - le32_to_cpu(channel->info_word->tx.param) : \ - channel->info->tx.param; \ - }) - -#define GET_TX_CHANNEL_INFO(channel, param) \ - ({ \ - BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u32)); \ - le32_to_cpu(channel->info_word ? \ - channel->info_word->tx.param : \ - channel->info->tx.param); \ - }) - -#define SET_TX_CHANNEL_FLAG(channel, param, value) \ - ({ \ - BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u8)); \ - if (channel->info_word) \ - channel->info_word->tx.param = cpu_to_le32(value); \ - else \ - channel->info->tx.param = value; \ - }) - -#define SET_TX_CHANNEL_INFO(channel, param, value) \ - ({ \ - BUILD_BUG_ON(sizeof(channel->info->tx.param) != sizeof(u32)); \ - if (channel->info_word) \ - channel->info_word->tx.param = cpu_to_le32(value); \ - else \ - channel->info->tx.param = cpu_to_le32(value); \ - }) - -/** - * struct qcom_smd_alloc_entry - channel allocation entry - * @name: channel name - * @cid: channel index - * @flags: channel flags and edge id - * @ref_count: reference count of the channel - */ -struct qcom_smd_alloc_entry { - u8 name[20]; - __le32 cid; - __le32 flags; - __le32 ref_count; -} __packed; - -#define SMD_CHANNEL_FLAGS_EDGE_MASK 0xff -#define SMD_CHANNEL_FLAGS_STREAM BIT(8) -#define SMD_CHANNEL_FLAGS_PACKET BIT(9) - -/* - * Each smd packet contains a 20 byte header, with the first 4 being the length - * of the packet. - */ -#define SMD_PACKET_HEADER_LEN 20 - -/* - * Signal the remote processor associated with 'channel'. - */ -static void qcom_smd_signal_channel(struct qcom_smd_channel *channel) -{ - struct qcom_smd_edge *edge = channel->edge; - - regmap_write(edge->ipc_regmap, edge->ipc_offset, BIT(edge->ipc_bit)); -} - -/* - * Initialize the tx channel info - */ -static void qcom_smd_channel_reset(struct qcom_smd_channel *channel) -{ - SET_TX_CHANNEL_INFO(channel, state, SMD_CHANNEL_CLOSED); - SET_TX_CHANNEL_FLAG(channel, fDSR, 0); - SET_TX_CHANNEL_FLAG(channel, fCTS, 0); - SET_TX_CHANNEL_FLAG(channel, fCD, 0); - SET_TX_CHANNEL_FLAG(channel, fRI, 0); - SET_TX_CHANNEL_FLAG(channel, fHEAD, 0); - SET_TX_CHANNEL_FLAG(channel, fTAIL, 0); - SET_TX_CHANNEL_FLAG(channel, fSTATE, 1); - SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 1); - SET_TX_CHANNEL_INFO(channel, head, 0); - SET_RX_CHANNEL_INFO(channel, tail, 0); - - qcom_smd_signal_channel(channel); - - channel->state = SMD_CHANNEL_CLOSED; - channel->pkt_size = 0; -} - -/* - * Set the callback for a channel, with appropriate locking - */ -static void qcom_smd_channel_set_callback(struct qcom_smd_channel *channel, - qcom_smd_cb_t cb) -{ - unsigned long flags; - - spin_lock_irqsave(&channel->recv_lock, flags); - channel->cb = cb; - spin_unlock_irqrestore(&channel->recv_lock, flags); -}; - -/* - * Calculate the amount of data available in the rx fifo - */ -static size_t qcom_smd_channel_get_rx_avail(struct qcom_smd_channel *channel) -{ - unsigned head; - unsigned tail; - - head = GET_RX_CHANNEL_INFO(channel, head); - tail = GET_RX_CHANNEL_INFO(channel, tail); - - return (head - tail) & (channel->fifo_size - 1); -} - -/* - * Set tx channel state and inform the remote processor - */ -static void qcom_smd_channel_set_state(struct qcom_smd_channel *channel, - int state) -{ - struct qcom_smd_edge *edge = channel->edge; - bool is_open = state == SMD_CHANNEL_OPENED; - - if (channel->state == state) - return; - - dev_dbg(&edge->dev, "set_state(%s, %d)\n", channel->name, state); - - SET_TX_CHANNEL_FLAG(channel, fDSR, is_open); - SET_TX_CHANNEL_FLAG(channel, fCTS, is_open); - SET_TX_CHANNEL_FLAG(channel, fCD, is_open); - - SET_TX_CHANNEL_INFO(channel, state, state); - SET_TX_CHANNEL_FLAG(channel, fSTATE, 1); - - channel->state = state; - qcom_smd_signal_channel(channel); -} - -/* - * Copy count bytes of data using 32bit accesses, if that's required. - */ -static void smd_copy_to_fifo(void __iomem *dst, - const void *src, - size_t count, - bool word_aligned) -{ - if (word_aligned) { - __iowrite32_copy(dst, src, count / sizeof(u32)); - } else { - memcpy_toio(dst, src, count); - } -} - -/* - * Copy count bytes of data using 32bit accesses, if that is required. - */ -static void smd_copy_from_fifo(void *dst, - const void __iomem *src, - size_t count, - bool word_aligned) -{ - if (word_aligned) { - __ioread32_copy(dst, src, count / sizeof(u32)); - } else { - memcpy_fromio(dst, src, count); - } -} - -/* - * Read count bytes of data from the rx fifo into buf, but don't advance the - * tail. - */ -static size_t qcom_smd_channel_peek(struct qcom_smd_channel *channel, - void *buf, size_t count) -{ - bool word_aligned; - unsigned tail; - size_t len; - - word_aligned = channel->info_word; - tail = GET_RX_CHANNEL_INFO(channel, tail); - - len = min_t(size_t, count, channel->fifo_size - tail); - if (len) { - smd_copy_from_fifo(buf, - channel->rx_fifo + tail, - len, - word_aligned); - } - - if (len != count) { - smd_copy_from_fifo(buf + len, - channel->rx_fifo, - count - len, - word_aligned); - } - - return count; -} - -/* - * Advance the rx tail by count bytes. - */ -static void qcom_smd_channel_advance(struct qcom_smd_channel *channel, - size_t count) -{ - unsigned tail; - - tail = GET_RX_CHANNEL_INFO(channel, tail); - tail += count; - tail &= (channel->fifo_size - 1); - SET_RX_CHANNEL_INFO(channel, tail, tail); -} - -/* - * Read out a single packet from the rx fifo and deliver it to the device - */ -static int qcom_smd_channel_recv_single(struct qcom_smd_channel *channel) -{ - unsigned tail; - size_t len; - void *ptr; - int ret; - - if (!channel->cb) - return 0; - - tail = GET_RX_CHANNEL_INFO(channel, tail); - - /* Use bounce buffer if the data wraps */ - if (tail + channel->pkt_size >= channel->fifo_size) { - ptr = channel->bounce_buffer; - len = qcom_smd_channel_peek(channel, ptr, channel->pkt_size); - } else { - ptr = channel->rx_fifo + tail; - len = channel->pkt_size; - } - - ret = channel->cb(channel, ptr, len); - if (ret < 0) - return ret; - - /* Only forward the tail if the client consumed the data */ - qcom_smd_channel_advance(channel, len); - - channel->pkt_size = 0; - - return 0; -} - -/* - * Per channel interrupt handling - */ -static bool qcom_smd_channel_intr(struct qcom_smd_channel *channel) -{ - bool need_state_scan = false; - int remote_state; - __le32 pktlen; - int avail; - int ret; - - /* Handle state changes */ - remote_state = GET_RX_CHANNEL_INFO(channel, state); - if (remote_state != channel->remote_state) { - channel->remote_state = remote_state; - need_state_scan = true; - } - /* Indicate that we have seen any state change */ - SET_RX_CHANNEL_FLAG(channel, fSTATE, 0); - - /* Signal waiting qcom_smd_send() about the interrupt */ - if (!GET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR)) - wake_up_interruptible(&channel->fblockread_event); - - /* Don't consume any data until we've opened the channel */ - if (channel->state != SMD_CHANNEL_OPENED) - goto out; - - /* Indicate that we've seen the new data */ - SET_RX_CHANNEL_FLAG(channel, fHEAD, 0); - - /* Consume data */ - for (;;) { - avail = qcom_smd_channel_get_rx_avail(channel); - - if (!channel->pkt_size && avail >= SMD_PACKET_HEADER_LEN) { - qcom_smd_channel_peek(channel, &pktlen, sizeof(pktlen)); - qcom_smd_channel_advance(channel, SMD_PACKET_HEADER_LEN); - channel->pkt_size = le32_to_cpu(pktlen); - } else if (channel->pkt_size && avail >= channel->pkt_size) { - ret = qcom_smd_channel_recv_single(channel); - if (ret) - break; - } else { - break; - } - } - - /* Indicate that we have seen and updated tail */ - SET_RX_CHANNEL_FLAG(channel, fTAIL, 1); - - /* Signal the remote that we've consumed the data (if requested) */ - if (!GET_RX_CHANNEL_FLAG(channel, fBLOCKREADINTR)) { - /* Ensure ordering of channel info updates */ - wmb(); - - qcom_smd_signal_channel(channel); - } - -out: - return need_state_scan; -} - -/* - * The edge interrupts are triggered by the remote processor on state changes, - * channel info updates or when new channels are created. - */ -static irqreturn_t qcom_smd_edge_intr(int irq, void *data) -{ - struct qcom_smd_edge *edge = data; - struct qcom_smd_channel *channel; - unsigned available; - bool kick_scanner = false; - bool kick_state = false; - - /* - * Handle state changes or data on each of the channels on this edge - */ - spin_lock(&edge->channels_lock); - list_for_each_entry(channel, &edge->channels, list) { - spin_lock(&channel->recv_lock); - kick_state |= qcom_smd_channel_intr(channel); - spin_unlock(&channel->recv_lock); - } - spin_unlock(&edge->channels_lock); - - /* - * Creating a new channel requires allocating an smem entry, so we only - * have to scan if the amount of available space in smem have changed - * since last scan. - */ - available = qcom_smem_get_free_space(edge->remote_pid); - if (available != edge->smem_available) { - edge->smem_available = available; - kick_scanner = true; - } - - if (kick_scanner) - schedule_work(&edge->scan_work); - if (kick_state) - schedule_work(&edge->state_work); - - return IRQ_HANDLED; -} - -/* - * Delivers any outstanding packets in the rx fifo, can be used after probe of - * the clients to deliver any packets that wasn't delivered before the client - * was setup. - */ -static void qcom_smd_channel_resume(struct qcom_smd_channel *channel) -{ - unsigned long flags; - - spin_lock_irqsave(&channel->recv_lock, flags); - qcom_smd_channel_intr(channel); - spin_unlock_irqrestore(&channel->recv_lock, flags); -} - -/* - * Calculate how much space is available in the tx fifo. - */ -static size_t qcom_smd_get_tx_avail(struct qcom_smd_channel *channel) -{ - unsigned head; - unsigned tail; - unsigned mask = channel->fifo_size - 1; - - head = GET_TX_CHANNEL_INFO(channel, head); - tail = GET_TX_CHANNEL_INFO(channel, tail); - - return mask - ((head - tail) & mask); -} - -/* - * Write count bytes of data into channel, possibly wrapping in the ring buffer - */ -static int qcom_smd_write_fifo(struct qcom_smd_channel *channel, - const void *data, - size_t count) -{ - bool word_aligned; - unsigned head; - size_t len; - - word_aligned = channel->info_word; - head = GET_TX_CHANNEL_INFO(channel, head); - - len = min_t(size_t, count, channel->fifo_size - head); - if (len) { - smd_copy_to_fifo(channel->tx_fifo + head, - data, - len, - word_aligned); - } - - if (len != count) { - smd_copy_to_fifo(channel->tx_fifo, - data + len, - count - len, - word_aligned); - } - - head += count; - head &= (channel->fifo_size - 1); - SET_TX_CHANNEL_INFO(channel, head, head); - - return count; -} - -/** - * qcom_smd_send - write data to smd channel - * @channel: channel handle - * @data: buffer of data to write - * @len: number of bytes to write - * - * This is a blocking write of len bytes into the channel's tx ring buffer and - * signal the remote end. It will sleep until there is enough space available - * in the tx buffer, utilizing the fBLOCKREADINTR signaling mechanism to avoid - * polling. - */ -int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len) -{ - __le32 hdr[5] = { cpu_to_le32(len), }; - int tlen = sizeof(hdr) + len; - int ret; - - /* Word aligned channels only accept word size aligned data */ - if (channel->info_word && len % 4) - return -EINVAL; - - /* Reject packets that are too big */ - if (tlen >= channel->fifo_size) - return -EINVAL; - - ret = mutex_lock_interruptible(&channel->tx_lock); - if (ret) - return ret; - - while (qcom_smd_get_tx_avail(channel) < tlen) { - if (channel->state != SMD_CHANNEL_OPENED) { - ret = -EPIPE; - goto out; - } - - SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 0); - - ret = wait_event_interruptible(channel->fblockread_event, - qcom_smd_get_tx_avail(channel) >= tlen || - channel->state != SMD_CHANNEL_OPENED); - if (ret) - goto out; - - SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 1); - } - - SET_TX_CHANNEL_FLAG(channel, fTAIL, 0); - - qcom_smd_write_fifo(channel, hdr, sizeof(hdr)); - qcom_smd_write_fifo(channel, data, len); - - SET_TX_CHANNEL_FLAG(channel, fHEAD, 1); - - /* Ensure ordering of channel info updates */ - wmb(); - - qcom_smd_signal_channel(channel); - -out: - mutex_unlock(&channel->tx_lock); - - return ret; -} -EXPORT_SYMBOL(qcom_smd_send); - -static struct qcom_smd_device *to_smd_device(struct device *dev) -{ - return container_of(dev, struct qcom_smd_device, dev); -} - -static struct qcom_smd_driver *to_smd_driver(struct device *dev) -{ - struct qcom_smd_device *qsdev = to_smd_device(dev); - - return container_of(qsdev->dev.driver, struct qcom_smd_driver, driver); -} - -static int qcom_smd_dev_match(struct device *dev, struct device_driver *drv) -{ - struct qcom_smd_device *qsdev = to_smd_device(dev); - struct qcom_smd_driver *qsdrv = container_of(drv, struct qcom_smd_driver, driver); - const struct qcom_smd_id *match = qsdrv->smd_match_table; - const char *name = qsdev->channel->name; - - if (match) { - while (match->name[0]) { - if (!strcmp(match->name, name)) - return 1; - match++; - } - } - - return of_driver_match_device(dev, drv); -} - -/* - * Helper for opening a channel - */ -static int qcom_smd_channel_open(struct qcom_smd_channel *channel, - qcom_smd_cb_t cb) -{ - size_t bb_size; - - /* - * Packets are maximum 4k, but reduce if the fifo is smaller - */ - bb_size = min(channel->fifo_size, SZ_4K); - channel->bounce_buffer = kmalloc(bb_size, GFP_KERNEL); - if (!channel->bounce_buffer) - return -ENOMEM; - - qcom_smd_channel_set_callback(channel, cb); - qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENING); - qcom_smd_channel_set_state(channel, SMD_CHANNEL_OPENED); - - return 0; -} - -/* - * Helper for closing and resetting a channel - */ -static void qcom_smd_channel_close(struct qcom_smd_channel *channel) -{ - qcom_smd_channel_set_callback(channel, NULL); - - kfree(channel->bounce_buffer); - channel->bounce_buffer = NULL; - - qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSED); - qcom_smd_channel_reset(channel); -} - -/* - * Probe the smd client. - * - * The remote side have indicated that it want the channel to be opened, so - * complete the state handshake and probe our client driver. - */ -static int qcom_smd_dev_probe(struct device *dev) -{ - struct qcom_smd_device *qsdev = to_smd_device(dev); - struct qcom_smd_driver *qsdrv = to_smd_driver(dev); - struct qcom_smd_channel *channel = qsdev->channel; - int ret; - - ret = qcom_smd_channel_open(channel, qsdrv->callback); - if (ret) - return ret; - - ret = qsdrv->probe(qsdev); - if (ret) - goto err; - - qcom_smd_channel_resume(channel); - - return 0; - -err: - dev_err(&qsdev->dev, "probe failed\n"); - - qcom_smd_channel_close(channel); - return ret; -} - -/* - * Remove the smd client. - * - * The channel is going away, for some reason, so remove the smd client and - * reset the channel state. - */ -static int qcom_smd_dev_remove(struct device *dev) -{ - struct qcom_smd_device *qsdev = to_smd_device(dev); - struct qcom_smd_driver *qsdrv = to_smd_driver(dev); - struct qcom_smd_channel *channel = qsdev->channel; - - qcom_smd_channel_set_state(channel, SMD_CHANNEL_CLOSING); - - /* - * Make sure we don't race with the code receiving data. - */ - qcom_smd_channel_set_callback(channel, NULL); - - /* Wake up any sleepers in qcom_smd_send() */ - wake_up_interruptible(&channel->fblockread_event); - - /* - * We expect that the client might block in remove() waiting for any - * outstanding calls to qcom_smd_send() to wake up and finish. - */ - if (qsdrv->remove) - qsdrv->remove(qsdev); - - /* The client is now gone, close the primary channel */ - qcom_smd_channel_close(channel); - channel->qsdev = NULL; - - return 0; -} - -static struct bus_type qcom_smd_bus = { - .name = "qcom_smd", - .match = qcom_smd_dev_match, - .probe = qcom_smd_dev_probe, - .remove = qcom_smd_dev_remove, -}; - -/* - * Release function for the qcom_smd_device object. - */ -static void qcom_smd_release_device(struct device *dev) -{ - struct qcom_smd_device *qsdev = to_smd_device(dev); - - kfree(qsdev); -} - -/* - * Finds the device_node for the smd child interested in this channel. - */ -static struct device_node *qcom_smd_match_channel(struct device_node *edge_node, - const char *channel) -{ - struct device_node *child; - const char *name; - const char *key; - int ret; - - for_each_available_child_of_node(edge_node, child) { - key = "qcom,smd-channels"; - ret = of_property_read_string(child, key, &name); - if (ret) - continue; - - if (strcmp(name, channel) == 0) - return child; - } - - return NULL; -} - -/* - * Create a smd client device for channel that is being opened. - */ -static int qcom_smd_create_device(struct qcom_smd_channel *channel) -{ - struct qcom_smd_device *qsdev; - struct qcom_smd_edge *edge = channel->edge; - struct device_node *node; - int ret; - - if (channel->qsdev) - return -EEXIST; - - dev_dbg(&edge->dev, "registering '%s'\n", channel->name); - - qsdev = kzalloc(sizeof(*qsdev), GFP_KERNEL); - if (!qsdev) - return -ENOMEM; - - node = qcom_smd_match_channel(edge->of_node, channel->name); - dev_set_name(&qsdev->dev, "%s.%s", - edge->of_node->name, - node ? node->name : channel->name); - - qsdev->dev.parent = &edge->dev; - qsdev->dev.bus = &qcom_smd_bus; - qsdev->dev.release = qcom_smd_release_device; - qsdev->dev.of_node = node; - - qsdev->channel = channel; - - channel->qsdev = qsdev; - - ret = device_register(&qsdev->dev); - if (ret) { - dev_err(&edge->dev, "device_register failed: %d\n", ret); - put_device(&qsdev->dev); - } - - return ret; -} - -/* - * Destroy a smd client device for a channel that's going away. - */ -static void qcom_smd_destroy_device(struct qcom_smd_channel *channel) -{ - struct device *dev; - - BUG_ON(!channel->qsdev); - - dev = &channel->qsdev->dev; - - device_unregister(dev); - of_node_put(dev->of_node); - put_device(dev); -} - -/** - * qcom_smd_driver_register - register a smd driver - * @qsdrv: qcom_smd_driver struct - */ -int qcom_smd_driver_register(struct qcom_smd_driver *qsdrv) -{ - qsdrv->driver.bus = &qcom_smd_bus; - return driver_register(&qsdrv->driver); -} -EXPORT_SYMBOL(qcom_smd_driver_register); - -void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel) -{ - return channel->drvdata; -} -EXPORT_SYMBOL(qcom_smd_get_drvdata); - -void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data) -{ - channel->drvdata = data; -} -EXPORT_SYMBOL(qcom_smd_set_drvdata); - -/** - * qcom_smd_driver_unregister - unregister a smd driver - * @qsdrv: qcom_smd_driver struct - */ -void qcom_smd_driver_unregister(struct qcom_smd_driver *qsdrv) -{ - driver_unregister(&qsdrv->driver); -} -EXPORT_SYMBOL(qcom_smd_driver_unregister); - -static struct qcom_smd_channel * -qcom_smd_find_channel(struct qcom_smd_edge *edge, const char *name) -{ - struct qcom_smd_channel *channel; - struct qcom_smd_channel *ret = NULL; - unsigned long flags; - unsigned state; - - spin_lock_irqsave(&edge->channels_lock, flags); - list_for_each_entry(channel, &edge->channels, list) { - if (strcmp(channel->name, name)) - continue; - - state = GET_RX_CHANNEL_INFO(channel, state); - if (state != SMD_CHANNEL_OPENING && - state != SMD_CHANNEL_OPENED) - continue; - - ret = channel; - break; - } - spin_unlock_irqrestore(&edge->channels_lock, flags); - - return ret; -} - -/** - * qcom_smd_open_channel() - claim additional channels on the same edge - * @sdev: smd_device handle - * @name: channel name - * @cb: callback method to use for incoming data - * - * Returns a channel handle on success, or -EPROBE_DEFER if the channel isn't - * ready. - * - * Any channels returned must be closed with a call to qcom_smd_close_channel() - */ -struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *parent, - const char *name, - qcom_smd_cb_t cb) -{ - struct qcom_smd_channel *channel; - struct qcom_smd_device *sdev = parent->qsdev; - struct qcom_smd_edge *edge = parent->edge; - int ret; - - /* Wait up to HZ for the channel to appear */ - ret = wait_event_interruptible_timeout(edge->new_channel_event, - (channel = qcom_smd_find_channel(edge, name)) != NULL, - HZ); - if (!ret) - return ERR_PTR(-ETIMEDOUT); - - if (channel->state != SMD_CHANNEL_CLOSED) { - dev_err(&sdev->dev, "channel %s is busy\n", channel->name); - return ERR_PTR(-EBUSY); - } - - channel->qsdev = sdev; - ret = qcom_smd_channel_open(channel, cb); - if (ret) { - channel->qsdev = NULL; - return ERR_PTR(ret); - } - - return channel; -} -EXPORT_SYMBOL(qcom_smd_open_channel); - -/** - * qcom_smd_close_channel() - close an additionally opened channel - * @channel: channel handle, returned by qcom_smd_open_channel() - */ -void qcom_smd_close_channel(struct qcom_smd_channel *channel) -{ - qcom_smd_channel_close(channel); - channel->qsdev = NULL; -} -EXPORT_SYMBOL(qcom_smd_close_channel); - -/* - * Allocate the qcom_smd_channel object for a newly found smd channel, - * retrieving and validating the smem items involved. - */ -static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *edge, - unsigned smem_info_item, - unsigned smem_fifo_item, - char *name) -{ - struct qcom_smd_channel *channel; - size_t fifo_size; - size_t info_size; - void *fifo_base; - void *info; - int ret; - - channel = devm_kzalloc(&edge->dev, sizeof(*channel), GFP_KERNEL); - if (!channel) - return ERR_PTR(-ENOMEM); - - channel->edge = edge; - channel->name = devm_kstrdup(&edge->dev, name, GFP_KERNEL); - if (!channel->name) - return ERR_PTR(-ENOMEM); - - mutex_init(&channel->tx_lock); - spin_lock_init(&channel->recv_lock); - init_waitqueue_head(&channel->fblockread_event); - - info = qcom_smem_get(edge->remote_pid, smem_info_item, &info_size); - if (IS_ERR(info)) { - ret = PTR_ERR(info); - goto free_name_and_channel; - } - - /* - * Use the size of the item to figure out which channel info struct to - * use. - */ - if (info_size == 2 * sizeof(struct smd_channel_info_word)) { - channel->info_word = info; - } else if (info_size == 2 * sizeof(struct smd_channel_info)) { - channel->info = info; - } else { - dev_err(&edge->dev, - "channel info of size %zu not supported\n", info_size); - ret = -EINVAL; - goto free_name_and_channel; - } - - fifo_base = qcom_smem_get(edge->remote_pid, smem_fifo_item, &fifo_size); - if (IS_ERR(fifo_base)) { - ret = PTR_ERR(fifo_base); - goto free_name_and_channel; - } - - /* The channel consist of a rx and tx fifo of equal size */ - fifo_size /= 2; - - dev_dbg(&edge->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n", - name, info_size, fifo_size); - - channel->tx_fifo = fifo_base; - channel->rx_fifo = fifo_base + fifo_size; - channel->fifo_size = fifo_size; - - qcom_smd_channel_reset(channel); - - return channel; - -free_name_and_channel: - devm_kfree(&edge->dev, channel->name); - devm_kfree(&edge->dev, channel); - - return ERR_PTR(ret); -} - -/* - * Scans the allocation table for any newly allocated channels, calls - * qcom_smd_create_channel() to create representations of these and add - * them to the edge's list of channels. - */ -static void qcom_channel_scan_worker(struct work_struct *work) -{ - struct qcom_smd_edge *edge = container_of(work, struct qcom_smd_edge, scan_work); - struct qcom_smd_alloc_entry *alloc_tbl; - struct qcom_smd_alloc_entry *entry; - struct qcom_smd_channel *channel; - unsigned long flags; - unsigned fifo_id; - unsigned info_id; - int tbl; - int i; - u32 eflags, cid; - - for (tbl = 0; tbl < SMD_ALLOC_TBL_COUNT; tbl++) { - alloc_tbl = qcom_smem_get(edge->remote_pid, - smem_items[tbl].alloc_tbl_id, NULL); - if (IS_ERR(alloc_tbl)) - continue; - - for (i = 0; i < SMD_ALLOC_TBL_SIZE; i++) { - entry = &alloc_tbl[i]; - eflags = le32_to_cpu(entry->flags); - if (test_bit(i, edge->allocated[tbl])) - continue; - - if (entry->ref_count == 0) - continue; - - if (!entry->name[0]) - continue; - - if (!(eflags & SMD_CHANNEL_FLAGS_PACKET)) - continue; - - if ((eflags & SMD_CHANNEL_FLAGS_EDGE_MASK) != edge->edge_id) - continue; - - cid = le32_to_cpu(entry->cid); - info_id = smem_items[tbl].info_base_id + cid; - fifo_id = smem_items[tbl].fifo_base_id + cid; - - channel = qcom_smd_create_channel(edge, info_id, fifo_id, entry->name); - if (IS_ERR(channel)) - continue; - - spin_lock_irqsave(&edge->channels_lock, flags); - list_add(&channel->list, &edge->channels); - spin_unlock_irqrestore(&edge->channels_lock, flags); - - dev_dbg(&edge->dev, "new channel found: '%s'\n", channel->name); - set_bit(i, edge->allocated[tbl]); - - wake_up_interruptible(&edge->new_channel_event); - } - } - - schedule_work(&edge->state_work); -} - -/* - * This per edge worker scans smem for any new channels and register these. It - * then scans all registered channels for state changes that should be handled - * by creating or destroying smd client devices for the registered channels. - * - * LOCKING: edge->channels_lock only needs to cover the list operations, as the - * worker is killed before any channels are deallocated - */ -static void qcom_channel_state_worker(struct work_struct *work) -{ - struct qcom_smd_channel *channel; - struct qcom_smd_edge *edge = container_of(work, - struct qcom_smd_edge, - state_work); - unsigned remote_state; - unsigned long flags; - - /* - * Register a device for any closed channel where the remote processor - * is showing interest in opening the channel. - */ - spin_lock_irqsave(&edge->channels_lock, flags); - list_for_each_entry(channel, &edge->channels, list) { - if (channel->state != SMD_CHANNEL_CLOSED) - continue; - - remote_state = GET_RX_CHANNEL_INFO(channel, state); - if (remote_state != SMD_CHANNEL_OPENING && - remote_state != SMD_CHANNEL_OPENED) - continue; - - spin_unlock_irqrestore(&edge->channels_lock, flags); - qcom_smd_create_device(channel); - spin_lock_irqsave(&edge->channels_lock, flags); - } - - /* - * Unregister the device for any channel that is opened where the - * remote processor is closing the channel. - */ - list_for_each_entry(channel, &edge->channels, list) { - if (channel->state != SMD_CHANNEL_OPENING && - channel->state != SMD_CHANNEL_OPENED) - continue; - - remote_state = GET_RX_CHANNEL_INFO(channel, state); - if (remote_state == SMD_CHANNEL_OPENING || - remote_state == SMD_CHANNEL_OPENED) - continue; - - spin_unlock_irqrestore(&edge->channels_lock, flags); - qcom_smd_destroy_device(channel); - spin_lock_irqsave(&edge->channels_lock, flags); - } - spin_unlock_irqrestore(&edge->channels_lock, flags); -} - -/* - * Parses an of_node describing an edge. - */ -static int qcom_smd_parse_edge(struct device *dev, - struct device_node *node, - struct qcom_smd_edge *edge) -{ - struct device_node *syscon_np; - const char *key; - int irq; - int ret; - - INIT_LIST_HEAD(&edge->channels); - spin_lock_init(&edge->channels_lock); - - INIT_WORK(&edge->scan_work, qcom_channel_scan_worker); - INIT_WORK(&edge->state_work, qcom_channel_state_worker); - - edge->of_node = of_node_get(node); - - key = "qcom,smd-edge"; - ret = of_property_read_u32(node, key, &edge->edge_id); - if (ret) { - dev_err(dev, "edge missing %s property\n", key); - return -EINVAL; - } - - edge->remote_pid = QCOM_SMEM_HOST_ANY; - key = "qcom,remote-pid"; - of_property_read_u32(node, key, &edge->remote_pid); - - syscon_np = of_parse_phandle(node, "qcom,ipc", 0); - if (!syscon_np) { - dev_err(dev, "no qcom,ipc node\n"); - return -ENODEV; - } - - edge->ipc_regmap = syscon_node_to_regmap(syscon_np); - if (IS_ERR(edge->ipc_regmap)) - return PTR_ERR(edge->ipc_regmap); - - key = "qcom,ipc"; - ret = of_property_read_u32_index(node, key, 1, &edge->ipc_offset); - if (ret < 0) { - dev_err(dev, "no offset in %s\n", key); - return -EINVAL; - } - - ret = of_property_read_u32_index(node, key, 2, &edge->ipc_bit); - if (ret < 0) { - dev_err(dev, "no bit in %s\n", key); - return -EINVAL; - } - - irq = irq_of_parse_and_map(node, 0); - if (irq < 0) { - dev_err(dev, "required smd interrupt missing\n"); - return -EINVAL; - } - - ret = devm_request_irq(dev, irq, - qcom_smd_edge_intr, IRQF_TRIGGER_RISING, - node->name, edge); - if (ret) { - dev_err(dev, "failed to request smd irq\n"); - return ret; - } - - edge->irq = irq; - - return 0; -} - -/* - * Release function for an edge. - * Reset the state of each associated channel and free the edge context. - */ -static void qcom_smd_edge_release(struct device *dev) -{ - struct qcom_smd_channel *channel; - struct qcom_smd_edge *edge = to_smd_edge(dev); - - list_for_each_entry(channel, &edge->channels, list) { - SET_RX_CHANNEL_INFO(channel, state, SMD_CHANNEL_CLOSED); - SET_RX_CHANNEL_INFO(channel, head, 0); - SET_RX_CHANNEL_INFO(channel, tail, 0); - } - - kfree(edge); -} - -/** - * qcom_smd_register_edge() - register an edge based on an device_node - * @parent: parent device for the edge - * @node: device_node describing the edge - * - * Returns an edge reference, or negative ERR_PTR() on failure. - */ -struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, - struct device_node *node) -{ - struct qcom_smd_edge *edge; - int ret; - - edge = kzalloc(sizeof(*edge), GFP_KERNEL); - if (!edge) - return ERR_PTR(-ENOMEM); - - init_waitqueue_head(&edge->new_channel_event); - - edge->dev.parent = parent; - edge->dev.release = qcom_smd_edge_release; - dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name); - ret = device_register(&edge->dev); - if (ret) { - pr_err("failed to register smd edge\n"); - return ERR_PTR(ret); - } - - ret = qcom_smd_parse_edge(&edge->dev, node, edge); - if (ret) { - dev_err(&edge->dev, "failed to parse smd edge\n"); - goto unregister_dev; - } - - schedule_work(&edge->scan_work); - - return edge; - -unregister_dev: - put_device(&edge->dev); - return ERR_PTR(ret); -} -EXPORT_SYMBOL(qcom_smd_register_edge); - -static int qcom_smd_remove_device(struct device *dev, void *data) -{ - device_unregister(dev); - of_node_put(dev->of_node); - put_device(dev); - - return 0; -} - -/** - * qcom_smd_unregister_edge() - release an edge and its children - * @edge: edge reference acquired from qcom_smd_register_edge - */ -int qcom_smd_unregister_edge(struct qcom_smd_edge *edge) -{ - int ret; - - disable_irq(edge->irq); - cancel_work_sync(&edge->scan_work); - cancel_work_sync(&edge->state_work); - - ret = device_for_each_child(&edge->dev, NULL, qcom_smd_remove_device); - if (ret) - dev_warn(&edge->dev, "can't remove smd device: %d\n", ret); - - device_unregister(&edge->dev); - - return 0; -} -EXPORT_SYMBOL(qcom_smd_unregister_edge); - -static int qcom_smd_probe(struct platform_device *pdev) -{ - struct device_node *node; - void *p; - - /* Wait for smem */ - p = qcom_smem_get(QCOM_SMEM_HOST_ANY, smem_items[0].alloc_tbl_id, NULL); - if (PTR_ERR(p) == -EPROBE_DEFER) - return PTR_ERR(p); - - for_each_available_child_of_node(pdev->dev.of_node, node) - qcom_smd_register_edge(&pdev->dev, node); - - return 0; -} - -static int qcom_smd_remove_edge(struct device *dev, void *data) -{ - struct qcom_smd_edge *edge = to_smd_edge(dev); - - return qcom_smd_unregister_edge(edge); -} - -/* - * Shut down all smd clients by making sure that each edge stops processing - * events and scanning for new channels, then call destroy on the devices. - */ -static int qcom_smd_remove(struct platform_device *pdev) -{ - int ret; - - ret = device_for_each_child(&pdev->dev, NULL, qcom_smd_remove_edge); - if (ret) - dev_warn(&pdev->dev, "can't remove smd device: %d\n", ret); - - return ret; -} - -static const struct of_device_id qcom_smd_of_match[] = { - { .compatible = "qcom,smd" }, - {} -}; -MODULE_DEVICE_TABLE(of, qcom_smd_of_match); - -static struct platform_driver qcom_smd_driver = { - .probe = qcom_smd_probe, - .remove = qcom_smd_remove, - .driver = { - .name = "qcom-smd", - .of_match_table = qcom_smd_of_match, - }, -}; - -static int __init qcom_smd_init(void) -{ - int ret; - - ret = bus_register(&qcom_smd_bus); - if (ret) { - pr_err("failed to register smd bus: %d\n", ret); - return ret; - } - - return platform_driver_register(&qcom_smd_driver); -} -postcore_initcall(qcom_smd_init); - -static void __exit qcom_smd_exit(void) -{ - platform_driver_unregister(&qcom_smd_driver); - bus_unregister(&qcom_smd_bus); -} -module_exit(qcom_smd_exit); - -MODULE_AUTHOR("Bjorn Andersson "); -MODULE_DESCRIPTION("Qualcomm Shared Memory Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/include/linux/rpmsg/qcom_smd.h b/include/linux/rpmsg/qcom_smd.h index 8ec8b6439b25..f27917e0a101 100644 --- a/include/linux/rpmsg/qcom_smd.h +++ b/include/linux/rpmsg/qcom_smd.h @@ -6,7 +6,7 @@ struct qcom_smd_edge; -#if IS_ENABLED(CONFIG_RPMSG_QCOM_SMD) || IS_ENABLED(CONFIG_QCOM_SMD) +#if IS_ENABLED(CONFIG_RPMSG_QCOM_SMD) struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, struct device_node *node); diff --git a/include/linux/soc/qcom/smd.h b/include/linux/soc/qcom/smd.h deleted file mode 100644 index f148e0ffbec7..000000000000 --- a/include/linux/soc/qcom/smd.h +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef __QCOM_SMD_H__ -#define __QCOM_SMD_H__ - -#include -#include - -struct qcom_smd; -struct qcom_smd_channel; -struct qcom_smd_lookup; - -/** - * struct qcom_smd_id - struct used for matching a smd device - * @name: name of the channel - */ -struct qcom_smd_id { - char name[20]; -}; - -/** - * struct qcom_smd_device - smd device struct - * @dev: the device struct - * @channel: handle to the smd channel for this device - */ -struct qcom_smd_device { - struct device dev; - struct qcom_smd_channel *channel; -}; - -typedef int (*qcom_smd_cb_t)(struct qcom_smd_channel *, const void *, size_t); - -/** - * struct qcom_smd_driver - smd driver struct - * @driver: underlying device driver - * @smd_match_table: static channel match table - * @probe: invoked when the smd channel is found - * @remove: invoked when the smd channel is closed - * @callback: invoked when an inbound message is received on the channel, - * should return 0 on success or -EBUSY if the data cannot be - * consumed at this time - */ -struct qcom_smd_driver { - struct device_driver driver; - const struct qcom_smd_id *smd_match_table; - - int (*probe)(struct qcom_smd_device *dev); - void (*remove)(struct qcom_smd_device *dev); - qcom_smd_cb_t callback; -}; - -#if IS_ENABLED(CONFIG_QCOM_SMD) - -int qcom_smd_driver_register(struct qcom_smd_driver *drv); -void qcom_smd_driver_unregister(struct qcom_smd_driver *drv); - -struct qcom_smd_channel *qcom_smd_open_channel(struct qcom_smd_channel *channel, - const char *name, - qcom_smd_cb_t cb); -void qcom_smd_close_channel(struct qcom_smd_channel *channel); -void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel); -void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data); -int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len); - - -struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, - struct device_node *node); -int qcom_smd_unregister_edge(struct qcom_smd_edge *edge); - -#else - -static inline int qcom_smd_driver_register(struct qcom_smd_driver *drv) -{ - return -ENXIO; -} - -static inline void qcom_smd_driver_unregister(struct qcom_smd_driver *drv) -{ - /* This shouldn't be possible */ - WARN_ON(1); -} - -static inline struct qcom_smd_channel * -qcom_smd_open_channel(struct qcom_smd_channel *channel, - const char *name, - qcom_smd_cb_t cb) -{ - /* This shouldn't be possible */ - WARN_ON(1); - return NULL; -} - -static inline void qcom_smd_close_channel(struct qcom_smd_channel *channel) -{ - /* This shouldn't be possible */ - WARN_ON(1); -} - -static inline void *qcom_smd_get_drvdata(struct qcom_smd_channel *channel) -{ - /* This shouldn't be possible */ - WARN_ON(1); - return NULL; -} - -static inline void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data) -{ - /* This shouldn't be possible */ - WARN_ON(1); -} - -static inline int qcom_smd_send(struct qcom_smd_channel *channel, - const void *data, int len) -{ - /* This shouldn't be possible */ - WARN_ON(1); - return -ENXIO; -} - -static inline struct qcom_smd_edge * -qcom_smd_register_edge(struct device *parent, - struct device_node *node) -{ - return ERR_PTR(-ENXIO); -} - -static inline int qcom_smd_unregister_edge(struct qcom_smd_edge *edge) -{ - /* This shouldn't be possible */ - WARN_ON(1); - return -ENXIO; -} - -#endif - -#define module_qcom_smd_driver(__smd_driver) \ - module_driver(__smd_driver, qcom_smd_driver_register, \ - qcom_smd_driver_unregister) - - -#endif -- cgit From 2b624250452537b65d056a6566baf2bc02322859 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 27 Mar 2017 22:26:35 -0700 Subject: soc: qcom: smd-rpm: Add msm8996 compatibility With the RPM driver transitioned to RPMSG we can reuse the SMD-RPM driver ontop of GLINK for 8996, without any modifications. Acked-by: Andy Gross Signed-off-by: Bjorn Andersson Signed-off-by: David S. Miller --- drivers/soc/qcom/smd-rpm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c index 0dcf1bf33126..c2346752b3ea 100644 --- a/drivers/soc/qcom/smd-rpm.c +++ b/drivers/soc/qcom/smd-rpm.c @@ -225,6 +225,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = { { .compatible = "qcom,rpm-apq8084" }, { .compatible = "qcom,rpm-msm8916" }, { .compatible = "qcom,rpm-msm8974" }, + { .compatible = "qcom,rpm-msm8996" }, {} }; MODULE_DEVICE_TABLE(of, qcom_smd_rpm_of_match); -- cgit From 589a1a2e63163bdd59f983d6cd89cec5f9457823 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 28 Mar 2017 11:48:21 +0200 Subject: stmmac: use netif_set_real_num_{rx,tx}_queues A driver must not access the two fields directly but should instead use the helper functions to set the values and keep a consistent internal state: ethernet/stmicro/stmmac/stmmac_main.c: In function 'stmmac_dvr_probe': ethernet/stmicro/stmmac/stmmac_main.c:4083:8: error: 'struct net_device' has no member named 'real_num_rx_queues'; did you mean 'real_num_tx_queues'? Fixes: a8f5102af2a7 ("net: stmmac: TX and RX queue priority configuration") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index c78f444ad423..fe1d9592956f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -4080,8 +4080,8 @@ int stmmac_dvr_probe(struct device *device, goto error_hw_init; /* Configure real RX and TX queues */ - ndev->real_num_rx_queues = priv->plat->rx_queues_to_use; - ndev->real_num_tx_queues = priv->plat->tx_queues_to_use; + netif_set_real_num_rx_queues(ndev, priv->plat->rx_queues_to_use); + netif_set_real_num_tx_queues(ndev, priv->plat->tx_queues_to_use); priv->dma_buf_sz = STMMAC_ALIGN(buf_sz); -- cgit From 139bb36f754adbf6d3c836db09d6459e25167b38 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Tue, 28 Mar 2017 12:28:27 +0200 Subject: tipc: advance the time of deleting subscription from subscriber->subscrp_list After a subscription object is created, it's inserted into its subscriber subscrp_list list under subscriber lock protection, similarly, before it's destroyed, it should be first removed from its subscriber->subscrp_list. Since the subscription list is accessed with subscriber lock, all the subscriptions are valid during the lock duration. Hence in tipc_subscrb_subscrp_delete(), we remove subscription get/put and the extra subscriber unlock/lock. After this change, the subscriptions refcount cleanup is very simple and does not access any lock. Acked-by: Jon Maloy Signed-off-by: Ying Xue Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/subscr.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c index 271cd66e4b3b..0649bc29c6bb 100644 --- a/net/tipc/subscr.c +++ b/net/tipc/subscr.c @@ -145,6 +145,7 @@ static void tipc_subscrp_timeout(unsigned long data) spin_lock_bh(&subscriber->lock); tipc_nametbl_unsubscribe(sub); + list_del(&sub->subscrp_list); spin_unlock_bh(&subscriber->lock); /* Notify subscriber of timeout */ @@ -177,10 +178,7 @@ static void tipc_subscrp_kref_release(struct kref *kref) struct tipc_net *tn = net_generic(sub->net, tipc_net_id); struct tipc_subscriber *subscriber = sub->subscriber; - spin_lock_bh(&subscriber->lock); - list_del(&sub->subscrp_list); atomic_dec(&tn->subscription_count); - spin_unlock_bh(&subscriber->lock); kfree(sub); tipc_subscrb_put(subscriber); } @@ -210,11 +208,8 @@ static void tipc_subscrb_subscrp_delete(struct tipc_subscriber *subscriber, continue; tipc_nametbl_unsubscribe(sub); - tipc_subscrp_get(sub); - spin_unlock_bh(&subscriber->lock); + list_del(&sub->subscrp_list); tipc_subscrp_delete(sub); - tipc_subscrp_put(sub); - spin_lock_bh(&subscriber->lock); if (s) break; -- cgit From 7efea60dcffc151870d1abbfccdb1f11cd4b7f21 Mon Sep 17 00:00:00 2001 From: Ying Xue Date: Tue, 28 Mar 2017 12:28:28 +0200 Subject: tipc: adjust the policy of holding subscription kref When a new subscription object is inserted into name_seq->subscriptions list, it's under name_seq->lock protection; when a subscription is deleted from the list, it's also under the same lock protection; similarly, when accessing a subscription by going through subscriptions list, the entire process is also protected by the name_seq->lock. Therefore, if subscription refcount is increased before it's inserted into subscriptions list, and its refcount is decreased after it's deleted from the list, it will be unnecessary to hold refcount at all before accessing subscription object which is obtained by going through subscriptions list under name_seq->lock protection. Signed-off-by: Ying Xue Reviewed-by: Jon Maloy Signed-off-by: David S. Miller --- net/tipc/name_table.c | 2 ++ net/tipc/subscr.c | 8 ++------ net/tipc/subscr.h | 3 +++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index 9be6592e4a6f..bd0aac87b41a 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -416,6 +416,7 @@ static void tipc_nameseq_subscribe(struct name_seq *nseq, tipc_subscrp_convert_seq(&s->evt.s.seq, s->swap, &ns); + tipc_subscrp_get(s); list_add(&s->nameseq_list, &nseq->subscriptions); if (!sseq) @@ -787,6 +788,7 @@ void tipc_nametbl_unsubscribe(struct tipc_subscription *s) if (seq != NULL) { spin_lock_bh(&seq->lock); list_del_init(&s->nameseq_list); + tipc_subscrp_put(s); if (!seq->first_free && list_empty(&seq->subscriptions)) { hlist_del_init_rcu(&seq->ns_list); kfree(seq->sseqs); diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c index 0649bc29c6bb..0bf91cd3733c 100644 --- a/net/tipc/subscr.c +++ b/net/tipc/subscr.c @@ -54,8 +54,6 @@ struct tipc_subscriber { static void tipc_subscrp_delete(struct tipc_subscription *sub); static void tipc_subscrb_put(struct tipc_subscriber *subscriber); -static void tipc_subscrp_put(struct tipc_subscription *subscription); -static void tipc_subscrp_get(struct tipc_subscription *subscription); /** * htohl - convert value to endianness used by destination @@ -125,7 +123,6 @@ void tipc_subscrp_report_overlap(struct tipc_subscription *sub, u32 found_lower, { struct tipc_name_seq seq; - tipc_subscrp_get(sub); tipc_subscrp_convert_seq(&sub->evt.s.seq, sub->swap, &seq); if (!tipc_subscrp_check_overlap(&seq, found_lower, found_upper)) return; @@ -135,7 +132,6 @@ void tipc_subscrp_report_overlap(struct tipc_subscription *sub, u32 found_lower, tipc_subscrp_send_event(sub, found_lower, found_upper, event, port_ref, node); - tipc_subscrp_put(sub); } static void tipc_subscrp_timeout(unsigned long data) @@ -183,12 +179,12 @@ static void tipc_subscrp_kref_release(struct kref *kref) tipc_subscrb_put(subscriber); } -static void tipc_subscrp_put(struct tipc_subscription *subscription) +void tipc_subscrp_put(struct tipc_subscription *subscription) { kref_put(&subscription->kref, tipc_subscrp_kref_release); } -static void tipc_subscrp_get(struct tipc_subscription *subscription) +void tipc_subscrp_get(struct tipc_subscription *subscription) { kref_get(&subscription->kref); } diff --git a/net/tipc/subscr.h b/net/tipc/subscr.h index ffdc214c117a..ee52957dc952 100644 --- a/net/tipc/subscr.h +++ b/net/tipc/subscr.h @@ -78,4 +78,7 @@ u32 tipc_subscrp_convert_seq_type(u32 type, int swap); int tipc_topsrv_start(struct net *net); void tipc_topsrv_stop(struct net *net); +void tipc_subscrp_put(struct tipc_subscription *subscription); +void tipc_subscrp_get(struct tipc_subscription *subscription); + #endif -- cgit From 1226337ad98ffc7982244a67a47faab1eacaca33 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Tue, 28 Mar 2017 15:12:50 +0300 Subject: qed: Correct HW stop flow Management firmware is used as arbiter between different PFs which are loading/unloading, but in order to use the synchronization it offers the contending configurations need to be applied either between their LOAD_REQ <-> LOAD_DONE or UNLOAD_REQ <-> UNLOAD_DONE management firmware commands. Existing HW stop flow utilizes 2 different functions: qed_hw_stop() and qed_hw_reset() which don't abide this requirement; Most of the closure is doing outside the scope of the unload request. This patch removes qed_hw_reset() and places the relevant stop functionality underneath the management firmware protection. Signed-off-by: Tomer Tayar Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 1 + drivers/net/ethernet/qlogic/qed/qed_dev.c | 146 ++++++++++---------------- drivers/net/ethernet/qlogic/qed/qed_dev_api.h | 8 -- drivers/net/ethernet/qlogic/qed/qed_main.c | 29 ++--- drivers/net/ethernet/qlogic/qed/qed_mcp.c | 73 +++++++++---- drivers/net/ethernet/qlogic/qed/qed_mcp.h | 20 ++++ 6 files changed, 142 insertions(+), 135 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index ca30a27df035..90a1988af909 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -732,5 +732,6 @@ void qed_get_protocol_stats(struct qed_dev *cdev, enum qed_mcp_protocol_type type, union qed_mcp_protocol_stats *stats); int qed_slowpath_irq_req(struct qed_hwfn *hwfn); +void qed_slowpath_irq_sync(struct qed_hwfn *p_hwfn); #endif /* _QED_H */ diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 11e45f0f7779..cf95da5679c3 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1303,27 +1303,53 @@ void qed_hw_timers_stop_all(struct qed_dev *cdev) int qed_hw_stop(struct qed_dev *cdev) { - int rc = 0, t_rc; + struct qed_hwfn *p_hwfn; + struct qed_ptt *p_ptt; + int rc, rc2 = 0; int j; for_each_hwfn(cdev, j) { - struct qed_hwfn *p_hwfn = &cdev->hwfns[j]; - struct qed_ptt *p_ptt = p_hwfn->p_main_ptt; + p_hwfn = &cdev->hwfns[j]; + p_ptt = p_hwfn->p_main_ptt; DP_VERBOSE(p_hwfn, NETIF_MSG_IFDOWN, "Stopping hw/fw\n"); if (IS_VF(cdev)) { qed_vf_pf_int_cleanup(p_hwfn); + rc = qed_vf_pf_reset(p_hwfn); + if (rc) { + DP_NOTICE(p_hwfn, + "qed_vf_pf_reset failed. rc = %d.\n", + rc); + rc2 = -EINVAL; + } continue; } /* mark the hw as uninitialized... */ p_hwfn->hw_init_done = false; + /* Send unload command to MCP */ + rc = qed_mcp_unload_req(p_hwfn, p_ptt); + if (rc) { + DP_NOTICE(p_hwfn, + "Failed sending a UNLOAD_REQ command. rc = %d.\n", + rc); + rc2 = -EINVAL; + } + + qed_slowpath_irq_sync(p_hwfn); + + /* After this point no MFW attentions are expected, e.g. prevent + * race between pf stop and dcbx pf update. + */ rc = qed_sp_pf_stop(p_hwfn); - if (rc) + if (rc) { DP_NOTICE(p_hwfn, - "Failed to close PF against FW. Continue to stop HW to prevent illegal host access by the device\n"); + "Failed to close PF against FW [rc = %d]. Continue to stop HW to prevent illegal host access by the device.\n", + rc); + rc2 = -EINVAL; + } qed_wr(p_hwfn, p_ptt, NIG_REG_RX_LLH_BRB_GATE_DNTFWD_PERPF, 0x1); @@ -1346,20 +1372,37 @@ int qed_hw_stop(struct qed_dev *cdev) /* Need to wait 1ms to guarantee SBs are cleared */ usleep_range(1000, 2000); + + /* Disable PF in HW blocks */ + qed_wr(p_hwfn, p_ptt, DORQ_REG_PF_DB_ENABLE, 0); + qed_wr(p_hwfn, p_ptt, QM_REG_PF_EN, 0); + + qed_mcp_unload_done(p_hwfn, p_ptt); + if (rc) { + DP_NOTICE(p_hwfn, + "Failed sending a UNLOAD_DONE command. rc = %d.\n", + rc); + rc2 = -EINVAL; + } } if (IS_PF(cdev)) { + p_hwfn = QED_LEADING_HWFN(cdev); + p_ptt = QED_LEADING_HWFN(cdev)->p_main_ptt; + /* Disable DMAE in PXP - in CMT, this should only be done for * first hw-function, and only after all transactions have * stopped for all active hw-functions. */ - t_rc = qed_change_pci_hwfn(&cdev->hwfns[0], - cdev->hwfns[0].p_main_ptt, false); - if (t_rc != 0) - rc = t_rc; + rc = qed_change_pci_hwfn(p_hwfn, p_ptt, false); + if (rc) { + DP_NOTICE(p_hwfn, + "qed_change_pci_hwfn failed. rc = %d.\n", rc); + rc2 = -EINVAL; + } } - return rc; + return rc2; } void qed_hw_stop_fastpath(struct qed_dev *cdev) @@ -1404,89 +1447,6 @@ void qed_hw_start_fastpath(struct qed_hwfn *p_hwfn) NIG_REG_RX_LLH_BRB_GATE_DNTFWD_PERPF, 0x0); } -static int qed_reg_assert(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, u32 reg, bool expected) -{ - u32 assert_val = qed_rd(p_hwfn, p_ptt, reg); - - if (assert_val != expected) { - DP_NOTICE(p_hwfn, "Value at address 0x%08x != 0x%08x\n", - reg, expected); - return -EINVAL; - } - - return 0; -} - -int qed_hw_reset(struct qed_dev *cdev) -{ - int rc = 0; - u32 unload_resp, unload_param; - u32 wol_param; - int i; - - switch (cdev->wol_config) { - case QED_OV_WOL_DISABLED: - wol_param = DRV_MB_PARAM_UNLOAD_WOL_DISABLED; - break; - case QED_OV_WOL_ENABLED: - wol_param = DRV_MB_PARAM_UNLOAD_WOL_ENABLED; - break; - default: - DP_NOTICE(cdev, - "Unknown WoL configuration %02x\n", cdev->wol_config); - /* Fallthrough */ - case QED_OV_WOL_DEFAULT: - wol_param = DRV_MB_PARAM_UNLOAD_WOL_MCP; - } - - for_each_hwfn(cdev, i) { - struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; - - if (IS_VF(cdev)) { - rc = qed_vf_pf_reset(p_hwfn); - if (rc) - return rc; - continue; - } - - DP_VERBOSE(p_hwfn, NETIF_MSG_IFDOWN, "Resetting hw/fw\n"); - - /* Check for incorrect states */ - qed_reg_assert(p_hwfn, p_hwfn->p_main_ptt, - QM_REG_USG_CNT_PF_TX, 0); - qed_reg_assert(p_hwfn, p_hwfn->p_main_ptt, - QM_REG_USG_CNT_PF_OTHER, 0); - - /* Disable PF in HW blocks */ - qed_wr(p_hwfn, p_hwfn->p_main_ptt, DORQ_REG_PF_DB_ENABLE, 0); - qed_wr(p_hwfn, p_hwfn->p_main_ptt, QM_REG_PF_EN, 0); - qed_wr(p_hwfn, p_hwfn->p_main_ptt, - TCFC_REG_STRONG_ENABLE_PF, 0); - qed_wr(p_hwfn, p_hwfn->p_main_ptt, - CCFC_REG_STRONG_ENABLE_PF, 0); - - /* Send unload command to MCP */ - rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt, - DRV_MSG_CODE_UNLOAD_REQ, wol_param, - &unload_resp, &unload_param); - if (rc) { - DP_NOTICE(p_hwfn, "qed_hw_reset: UNLOAD_REQ failed\n"); - unload_resp = FW_MSG_CODE_DRV_UNLOAD_ENGINE; - } - - rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt, - DRV_MSG_CODE_UNLOAD_DONE, - 0, &unload_resp, &unload_param); - if (rc) { - DP_NOTICE(p_hwfn, "qed_hw_reset: UNLOAD_DONE failed\n"); - return rc; - } - } - - return rc; -} - /* Free hwfn memory and resources acquired in hw_hwfn_prepare */ static void qed_hw_hwfn_free(struct qed_hwfn *p_hwfn) { diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h index 6812003411cd..e96eccff3a41 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h +++ b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h @@ -140,14 +140,6 @@ void qed_hw_stop_fastpath(struct qed_dev *cdev); */ void qed_hw_start_fastpath(struct qed_hwfn *p_hwfn); -/** - * @brief qed_hw_reset - - * - * @param cdev - * - * @return int - */ -int qed_hw_reset(struct qed_dev *cdev); /** * @brief qed_hw_prepare - diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 766c6f39ea63..f2ae90ab0977 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -589,6 +589,19 @@ int qed_slowpath_irq_req(struct qed_hwfn *hwfn) return rc; } +void qed_slowpath_irq_sync(struct qed_hwfn *p_hwfn) +{ + struct qed_dev *cdev = p_hwfn->cdev; + u8 id = p_hwfn->my_id; + u32 int_mode; + + int_mode = cdev->int_params.out.int_mode; + if (int_mode == QED_INT_MODE_MSIX) + synchronize_irq(cdev->int_params.msix_table[id].vector); + else + synchronize_irq(cdev->pdev->irq); +} + static void qed_slowpath_irq_free(struct qed_dev *cdev) { int i; @@ -631,19 +644,6 @@ static int qed_nic_stop(struct qed_dev *cdev) return rc; } -static int qed_nic_reset(struct qed_dev *cdev) -{ - int rc; - - rc = qed_hw_reset(cdev); - if (rc) - return rc; - - qed_resc_free(cdev); - - return 0; -} - static int qed_nic_setup(struct qed_dev *cdev) { int rc, i; @@ -1043,7 +1043,8 @@ static int qed_slowpath_stop(struct qed_dev *cdev) } qed_disable_msix(cdev); - qed_nic_reset(cdev); + + qed_resc_free(cdev); qed_iov_wq_stop(cdev, true); diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index ccea0eae7b60..f2e5ab90f574 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -550,32 +550,12 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn, u32 *o_mcp_param) { struct qed_mcp_mb_params mb_params; - struct mcp_mac wol_mac; int rc; memset(&mb_params, 0, sizeof(mb_params)); mb_params.cmd = cmd; mb_params.param = param; - /* In case of UNLOAD_DONE, set the primary MAC */ - if ((cmd == DRV_MSG_CODE_UNLOAD_DONE) && - (p_hwfn->cdev->wol_config == QED_OV_WOL_ENABLED)) { - u8 *p_mac = p_hwfn->cdev->wol_mac; - - memset(&wol_mac, 0, sizeof(wol_mac)); - wol_mac.mac_upper = p_mac[0] << 8 | p_mac[1]; - wol_mac.mac_lower = p_mac[2] << 24 | p_mac[3] << 16 | - p_mac[4] << 8 | p_mac[5]; - - DP_VERBOSE(p_hwfn, - (QED_MSG_SP | NETIF_MSG_IFDOWN), - "Setting WoL MAC: %pM --> [%08x,%08x]\n", - p_mac, wol_mac.mac_upper, wol_mac.mac_lower); - - mb_params.p_data_src = &wol_mac; - mb_params.data_src_size = sizeof(wol_mac); - } - rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); if (rc) return rc; @@ -663,6 +643,59 @@ int qed_mcp_load_req(struct qed_hwfn *p_hwfn, return 0; } +int qed_mcp_unload_req(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + u32 wol_param, mcp_resp, mcp_param; + + switch (p_hwfn->cdev->wol_config) { + case QED_OV_WOL_DISABLED: + wol_param = DRV_MB_PARAM_UNLOAD_WOL_DISABLED; + break; + case QED_OV_WOL_ENABLED: + wol_param = DRV_MB_PARAM_UNLOAD_WOL_ENABLED; + break; + default: + DP_NOTICE(p_hwfn, + "Unknown WoL configuration %02x\n", + p_hwfn->cdev->wol_config); + /* Fallthrough */ + case QED_OV_WOL_DEFAULT: + wol_param = DRV_MB_PARAM_UNLOAD_WOL_MCP; + } + + return qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_UNLOAD_REQ, wol_param, + &mcp_resp, &mcp_param); +} + +int qed_mcp_unload_done(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + struct qed_mcp_mb_params mb_params; + struct mcp_mac wol_mac; + + memset(&mb_params, 0, sizeof(mb_params)); + mb_params.cmd = DRV_MSG_CODE_UNLOAD_DONE; + + /* Set the primary MAC if WoL is enabled */ + if (p_hwfn->cdev->wol_config == QED_OV_WOL_ENABLED) { + u8 *p_mac = p_hwfn->cdev->wol_mac; + + memset(&wol_mac, 0, sizeof(wol_mac)); + wol_mac.mac_upper = p_mac[0] << 8 | p_mac[1]; + wol_mac.mac_lower = p_mac[2] << 24 | p_mac[3] << 16 | + p_mac[4] << 8 | p_mac[5]; + + DP_VERBOSE(p_hwfn, + (QED_MSG_SP | NETIF_MSG_IFDOWN), + "Setting WoL MAC: %pM --> [%08x,%08x]\n", + p_mac, wol_mac.mac_upper, wol_mac.mac_lower); + + mb_params.p_data_src = &wol_mac; + mb_params.data_src_size = sizeof(wol_mac); + } + + return qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); +} + static void qed_mcp_handle_vf_flr(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index f63693d38934..7d067d047e7a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -592,6 +592,26 @@ int qed_mcp_load_req(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u32 *p_load_code); +/** + * @brief Sends a UNLOAD_REQ message to the MFW + * + * @param p_hwfn + * @param p_ptt + * + * @return int - 0 - Operation was successful. + */ +int qed_mcp_unload_req(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); + +/** + * @brief Sends a UNLOAD_DONE message to the MFW + * + * @param p_hwfn + * @param p_ptt + * + * @return int - 0 - Operation was successful. + */ +int qed_mcp_unload_done(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); + /** * @brief Read the MFW mailbox into Current buffer. * -- cgit From c0c2d0b49edc3a11627ea63d3f1e4a2d91397792 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 28 Mar 2017 15:12:51 +0300 Subject: qed: hw_init() to receive parameter-struct We'll soon need additional information, so start by changing the infrastructure to receive the initializing variables via a parameter struct. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dev.c | 21 +++++++++--------- drivers/net/ethernet/qlogic/qed/qed_dev_api.h | 31 ++++++++++++++++----------- drivers/net/ethernet/qlogic/qed/qed_main.c | 12 ++++++++--- 3 files changed, 37 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index cf95da5679c3..109299833b14 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1106,25 +1106,20 @@ static void qed_reset_mb_shadow(struct qed_hwfn *p_hwfn, p_hwfn->mcp_info->mfw_mb_cur, p_hwfn->mcp_info->mfw_mb_length); } -int qed_hw_init(struct qed_dev *cdev, - struct qed_tunn_start_params *p_tunn, - bool b_hw_start, - enum qed_int_mode int_mode, - bool allow_npar_tx_switch, - const u8 *bin_fw_data) +int qed_hw_init(struct qed_dev *cdev, struct qed_hw_init_params *p_params) { u32 load_code, param, drv_mb_param; bool b_default_mtu = true; struct qed_hwfn *p_hwfn; int rc = 0, mfw_rc, i; - if ((int_mode == QED_INT_MODE_MSI) && (cdev->num_hwfns > 1)) { + if ((p_params->int_mode == QED_INT_MODE_MSI) && (cdev->num_hwfns > 1)) { DP_NOTICE(cdev, "MSI mode is not supported for CMT devices\n"); return -EINVAL; } if (IS_PF(cdev)) { - rc = qed_init_fw_data(cdev, bin_fw_data); + rc = qed_init_fw_data(cdev, p_params->bin_fw_data); if (rc) return rc; } @@ -1181,11 +1176,15 @@ int qed_hw_init(struct qed_dev *cdev, /* Fall into */ case FW_MSG_CODE_DRV_LOAD_FUNCTION: rc = qed_hw_init_pf(p_hwfn, p_hwfn->p_main_ptt, - p_tunn, p_hwfn->hw_info.hw_mode, - b_hw_start, int_mode, - allow_npar_tx_switch); + p_params->p_tunn, + p_hwfn->hw_info.hw_mode, + p_params->b_hw_start, + p_params->int_mode, + p_params->allow_npar_tx_switch); break; default: + DP_NOTICE(p_hwfn, + "Unexpected load code [0x%08x]", load_code); rc = -EINVAL; break; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h index e96eccff3a41..8f45f4655e2b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h +++ b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h @@ -82,26 +82,31 @@ int qed_resc_alloc(struct qed_dev *cdev); */ void qed_resc_setup(struct qed_dev *cdev); +struct qed_hw_init_params { + /* Tunneling parameters */ + struct qed_tunn_start_params *p_tunn; + + bool b_hw_start; + + /* Interrupt mode [msix, inta, etc.] to use */ + enum qed_int_mode int_mode; + + /* NPAR tx switching to be used for vports for tx-switching */ + bool allow_npar_tx_switch; + + /* Binary fw data pointer in binary fw file */ + const u8 *bin_fw_data; +}; + /** * @brief qed_hw_init - * * @param cdev - * @param p_tunn - * @param b_hw_start - * @param int_mode - interrupt mode [msix, inta, etc.] to use. - * @param allow_npar_tx_switch - npar tx switching to be used - * for vports configured for tx-switching. - * @param bin_fw_data - binary fw data pointer in binary fw file. - * Pass NULL if not using binary fw file. + * @param p_params * * @return int */ -int qed_hw_init(struct qed_dev *cdev, - struct qed_tunn_start_params *p_tunn, - bool b_hw_start, - enum qed_int_mode int_mode, - bool allow_npar_tx_switch, - const u8 *bin_fw_data); +int qed_hw_init(struct qed_dev *cdev, struct qed_hw_init_params *p_params); /** * @brief qed_hw_timers_stop_all - stop the timers HW block diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index f2ae90ab0977..ae0ab3b1601d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -901,6 +901,7 @@ static void qed_update_pf_params(struct qed_dev *cdev, static int qed_slowpath_start(struct qed_dev *cdev, struct qed_slowpath_params *params) { + struct qed_hw_init_params hw_init_params; struct qed_tunn_start_params tunn_info; struct qed_mcp_drv_version drv_version; const u8 *data = NULL; @@ -966,9 +967,14 @@ static int qed_slowpath_start(struct qed_dev *cdev, tunn_info.tunn_clss_ipgre = QED_TUNN_CLSS_MAC_VLAN; /* Start the slowpath */ - rc = qed_hw_init(cdev, &tunn_info, true, - cdev->int_params.out.int_mode, - true, data); + memset(&hw_init_params, 0, sizeof(hw_init_params)); + hw_init_params.p_tunn = &tunn_info; + hw_init_params.b_hw_start = true; + hw_init_params.int_mode = cdev->int_params.out.int_mode; + hw_init_params.allow_npar_tx_switch = true; + hw_init_params.bin_fw_data = data; + + rc = qed_hw_init(cdev, &hw_init_params); if (rc) goto err2; -- cgit From 5d24bcf1895cb5095ffb9e06a219a858abaa15da Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Tue, 28 Mar 2017 15:12:52 +0300 Subject: qed: Move to new load request scheme Management firmware is used as an arbiter between the various PFs in regard to loading - it causes the various PFs to load/unload sequentially and informs each of its appropriate rule in the init. But the existing flow is too weak to handle some scenarios where PFs aren't properly cleaned prior to loading. The significant scenarios falling under this criteria: a. Preboot drivers in some environment can't properly unload. b. Unexpected driver replacement [kdump, PDA]. Modern management firmware supports a more intricate loading flow, where the driver has the ability to overcome previous limitations. This moves qed into using this newer scheme. Notice new scheme is backward compatible, so new drivers would still be able to load properly on top of older management firmwares and vice versa. Signed-off-by: Tomer Tayar Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 29 ++- drivers/net/ethernet/qlogic/qed/qed_dcbx.h | 3 - drivers/net/ethernet/qlogic/qed/qed_dev.c | 35 ++- drivers/net/ethernet/qlogic/qed/qed_dev_api.h | 32 +++ drivers/net/ethernet/qlogic/qed/qed_hsi.h | 50 +++- drivers/net/ethernet/qlogic/qed/qed_main.c | 9 + drivers/net/ethernet/qlogic/qed/qed_mcp.c | 356 ++++++++++++++++++++++++-- drivers/net/ethernet/qlogic/qed/qed_mcp.h | 41 +-- 8 files changed, 497 insertions(+), 58 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 90a1988af909..187a84bc45ea 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -51,7 +51,19 @@ #include "qed_hsi.h" extern const struct qed_common_ops qed_common_ops_pass; -#define DRV_MODULE_VERSION "8.10.10.21" + +#define QED_MAJOR_VERSION 8 +#define QED_MINOR_VERSION 10 +#define QED_REVISION_VERSION 10 +#define QED_ENGINEERING_VERSION 21 + +#define QED_VERSION \ + ((QED_MAJOR_VERSION << 24) | (QED_MINOR_VERSION << 16) | \ + (QED_REVISION_VERSION << 8) | QED_ENGINEERING_VERSION) + +#define STORM_FW_VERSION \ + ((FW_MAJOR_VERSION << 24) | (FW_MINOR_VERSION << 16) | \ + (FW_REVISION_VERSION << 8) | FW_ENGINEERING_VERSION) #define MAX_HWFNS_PER_DEVICE (4) #define NAME_SIZE 16 @@ -76,6 +88,15 @@ union qed_mcp_protocol_stats; enum qed_mcp_protocol_type; /* helpers */ +#define QED_MFW_GET_FIELD(name, field) \ + (((name) & (field ## _MASK)) >> (field ## _SHIFT)) + +#define QED_MFW_SET_FIELD(name, field, value) \ + do { \ + (name) &= ~((field ## _MASK) << (field ## _SHIFT)); \ + (name) |= (((value) << (field ## _SHIFT)) & (field ## _MASK));\ + } while (0) + static inline u32 qed_db_addr(u32 cid, u32 DEMS) { u32 db_addr = FIELD_VALUE(DB_LEGACY_ADDR_DEMS, DEMS) | @@ -355,6 +376,12 @@ struct qed_fw_data { u32 init_ops_size; }; +#define DRV_MODULE_VERSION \ + __stringify(QED_MAJOR_VERSION) "." \ + __stringify(QED_MINOR_VERSION) "." \ + __stringify(QED_REVISION_VERSION) "." \ + __stringify(QED_ENGINEERING_VERSION) + struct qed_simd_fp_handler { void *token; void (*func)(void *); diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.h b/drivers/net/ethernet/qlogic/qed/qed_dcbx.h index 0fabe97f998d..2eb988fe1298 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.h +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.h @@ -85,9 +85,6 @@ struct qed_dcbx_app_metadata { enum qed_pci_personality personality; }; -#define QED_MFW_GET_FIELD(name, field) \ - (((name) & (field ## _MASK)) >> (field ## _SHIFT)) - struct qed_dcbx_info { struct lldp_status_params_s lldp_remote[LLDP_MAX_LLDP_AGENTS]; struct lldp_config_params_s lldp_local[LLDP_MAX_LLDP_AGENTS]; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 109299833b14..759b819935e4 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1106,8 +1106,22 @@ static void qed_reset_mb_shadow(struct qed_hwfn *p_hwfn, p_hwfn->mcp_info->mfw_mb_cur, p_hwfn->mcp_info->mfw_mb_length); } +static void +qed_fill_load_req_params(struct qed_load_req_params *p_load_req, + struct qed_drv_load_params *p_drv_load) +{ + memset(p_load_req, 0, sizeof(*p_load_req)); + + p_load_req->drv_role = p_drv_load->is_crash_kernel ? + QED_DRV_ROLE_KDUMP : QED_DRV_ROLE_OS; + p_load_req->timeout_val = p_drv_load->mfw_timeout_val; + p_load_req->avoid_eng_reset = p_drv_load->avoid_eng_reset; + p_load_req->override_force_load = p_drv_load->override_force_load; +} + int qed_hw_init(struct qed_dev *cdev, struct qed_hw_init_params *p_params) { + struct qed_load_req_params load_req_params; u32 load_code, param, drv_mb_param; bool b_default_mtu = true; struct qed_hwfn *p_hwfn; @@ -1145,17 +1159,21 @@ int qed_hw_init(struct qed_dev *cdev, struct qed_hw_init_params *p_params) if (rc) return rc; - rc = qed_mcp_load_req(p_hwfn, p_hwfn->p_main_ptt, &load_code); + qed_fill_load_req_params(&load_req_params, + p_params->p_drv_load_params); + rc = qed_mcp_load_req(p_hwfn, p_hwfn->p_main_ptt, + &load_req_params); if (rc) { - DP_NOTICE(p_hwfn, "Failed sending LOAD_REQ command\n"); + DP_NOTICE(p_hwfn, "Failed sending a LOAD_REQ command\n"); return rc; } - qed_reset_mb_shadow(p_hwfn, p_hwfn->p_main_ptt); - + load_code = load_req_params.load_code; DP_VERBOSE(p_hwfn, QED_MSG_SP, - "Load request was sent. Resp:0x%x, Load code: 0x%x\n", - rc, load_code); + "Load request was sent. Load code: 0x%x\n", + load_code); + + qed_reset_mb_shadow(p_hwfn, p_hwfn->p_main_ptt); p_hwfn->first_on_engine = (load_code == FW_MSG_CODE_DRV_LOAD_ENGINE); @@ -1224,10 +1242,7 @@ int qed_hw_init(struct qed_dev *cdev, struct qed_hw_init_params *p_params) if (IS_PF(cdev)) { p_hwfn = QED_LEADING_HWFN(cdev); - drv_mb_param = (FW_MAJOR_VERSION << 24) | - (FW_MINOR_VERSION << 16) | - (FW_REVISION_VERSION << 8) | - (FW_ENGINEERING_VERSION); + drv_mb_param = STORM_FW_VERSION; rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt, DRV_MSG_CODE_OV_UPDATE_STORM_FW_VER, drv_mb_param, &load_code, ¶m); diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h index 8f45f4655e2b..cb973495cb38 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h +++ b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h @@ -82,6 +82,35 @@ int qed_resc_alloc(struct qed_dev *cdev); */ void qed_resc_setup(struct qed_dev *cdev); +enum qed_override_force_load { + QED_OVERRIDE_FORCE_LOAD_NONE, + QED_OVERRIDE_FORCE_LOAD_ALWAYS, + QED_OVERRIDE_FORCE_LOAD_NEVER, +}; + +struct qed_drv_load_params { + /* Indicates whether the driver is running over a crash kernel. + * As part of the load request, this will be used for providing the + * driver role to the MFW. + * In case of a crash kernel over PDA - this should be set to false. + */ + bool is_crash_kernel; + + /* The timeout value that the MFW should use when locking the engine for + * the driver load process. + * A value of '0' means the default value, and '255' means no timeout. + */ + u8 mfw_timeout_val; +#define QED_LOAD_REQ_LOCK_TO_DEFAULT 0 +#define QED_LOAD_REQ_LOCK_TO_NONE 255 + + /* Avoid engine reset when first PF loads on it */ + bool avoid_eng_reset; + + /* Allow overriding the default force load behavior */ + enum qed_override_force_load override_force_load; +}; + struct qed_hw_init_params { /* Tunneling parameters */ struct qed_tunn_start_params *p_tunn; @@ -96,6 +125,9 @@ struct qed_hw_init_params { /* Binary fw data pointer in binary fw file */ const u8 *bin_fw_data; + + /* Driver load parameters */ + struct qed_drv_load_params *p_drv_load_params; }; /** diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index e9acdc96ba84..bf2b20167aca 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -9887,9 +9887,11 @@ struct public_func { #define DRV_ID_PDA_COMP_VER_MASK 0x0000ffff #define DRV_ID_PDA_COMP_VER_SHIFT 0 +#define LOAD_REQ_HSI_VERSION 2 #define DRV_ID_MCP_HSI_VER_MASK 0x00ff0000 #define DRV_ID_MCP_HSI_VER_SHIFT 16 -#define DRV_ID_MCP_HSI_VER_CURRENT (1 << DRV_ID_MCP_HSI_VER_SHIFT) +#define DRV_ID_MCP_HSI_VER_CURRENT (LOAD_REQ_HSI_VERSION << \ + DRV_ID_MCP_HSI_VER_SHIFT) #define DRV_ID_DRV_TYPE_MASK 0x7f000000 #define DRV_ID_DRV_TYPE_SHIFT 24 @@ -10001,6 +10003,46 @@ struct resource_info { #define RESOURCE_ELEMENT_STRICT (1 << 0) }; +#define DRV_ROLE_NONE 0 +#define DRV_ROLE_PREBOOT 1 +#define DRV_ROLE_OS 2 +#define DRV_ROLE_KDUMP 3 + +struct load_req_stc { + u32 drv_ver_0; + u32 drv_ver_1; + u32 fw_ver; + u32 misc0; +#define LOAD_REQ_ROLE_MASK 0x000000FF +#define LOAD_REQ_ROLE_SHIFT 0 +#define LOAD_REQ_LOCK_TO_MASK 0x0000FF00 +#define LOAD_REQ_LOCK_TO_SHIFT 8 +#define LOAD_REQ_LOCK_TO_DEFAULT 0 +#define LOAD_REQ_LOCK_TO_NONE 255 +#define LOAD_REQ_FORCE_MASK 0x000F0000 +#define LOAD_REQ_FORCE_SHIFT 16 +#define LOAD_REQ_FORCE_NONE 0 +#define LOAD_REQ_FORCE_PF 1 +#define LOAD_REQ_FORCE_ALL 2 +#define LOAD_REQ_FLAGS0_MASK 0x00F00000 +#define LOAD_REQ_FLAGS0_SHIFT 20 +#define LOAD_REQ_FLAGS0_AVOID_RESET (0x1 << 0) +}; + +struct load_rsp_stc { + u32 drv_ver_0; + u32 drv_ver_1; + u32 fw_ver; + u32 misc0; +#define LOAD_RSP_ROLE_MASK 0x000000FF +#define LOAD_RSP_ROLE_SHIFT 0 +#define LOAD_RSP_HSI_MASK 0x0000FF00 +#define LOAD_RSP_HSI_SHIFT 8 +#define LOAD_RSP_FLAGS0_MASK 0x000F0000 +#define LOAD_RSP_FLAGS0_SHIFT 16 +#define LOAD_RSP_FLAGS0_DRV_EXISTS (0x1 << 0) +}; + union drv_union_data { u32 ver_str[MCP_DRV_VER_STR_SIZE_DWORD]; struct mcp_mac wol_mac; @@ -10032,6 +10074,7 @@ struct public_drv_mb { #define DRV_MSG_CODE_LOAD_REQ 0x10000000 #define DRV_MSG_CODE_LOAD_DONE 0x11000000 #define DRV_MSG_CODE_INIT_HW 0x12000000 +#define DRV_MSG_CODE_CANCEL_LOAD_REQ 0x13000000 #define DRV_MSG_CODE_UNLOAD_REQ 0x20000000 #define DRV_MSG_CODE_UNLOAD_DONE 0x21000000 #define DRV_MSG_CODE_INIT_PHY 0x22000000 @@ -10167,8 +10210,11 @@ struct public_drv_mb { #define FW_MSG_CODE_DRV_LOAD_PORT 0x10110000 #define FW_MSG_CODE_DRV_LOAD_FUNCTION 0x10120000 #define FW_MSG_CODE_DRV_LOAD_REFUSED_PDA 0x10200000 -#define FW_MSG_CODE_DRV_LOAD_REFUSED_HSI 0x10210000 +#define FW_MSG_CODE_DRV_LOAD_REFUSED_HSI_1 0x10210000 #define FW_MSG_CODE_DRV_LOAD_REFUSED_DIAG 0x10220000 +#define FW_MSG_CODE_DRV_LOAD_REFUSED_HSI 0x10230000 +#define FW_MSG_CODE_DRV_LOAD_REFUSED_REQUIRES_FORCE 0x10300000 +#define FW_MSG_CODE_DRV_LOAD_REFUSED_REJECT 0x10310000 #define FW_MSG_CODE_DRV_LOAD_DONE 0x11100000 #define FW_MSG_CODE_DRV_UNLOAD_ENGINE 0x20110000 #define FW_MSG_CODE_DRV_UNLOAD_PORT 0x20120000 diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index ae0ab3b1601d..d4edb993b1b0 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -901,6 +902,7 @@ static void qed_update_pf_params(struct qed_dev *cdev, static int qed_slowpath_start(struct qed_dev *cdev, struct qed_slowpath_params *params) { + struct qed_drv_load_params drv_load_params; struct qed_hw_init_params hw_init_params; struct qed_tunn_start_params tunn_info; struct qed_mcp_drv_version drv_version; @@ -974,6 +976,13 @@ static int qed_slowpath_start(struct qed_dev *cdev, hw_init_params.allow_npar_tx_switch = true; hw_init_params.bin_fw_data = data; + memset(&drv_load_params, 0, sizeof(drv_load_params)); + drv_load_params.is_crash_kernel = is_kdump_kernel(); + drv_load_params.mfw_timeout_val = QED_LOAD_REQ_LOCK_TO_DEFAULT; + drv_load_params.avoid_eng_reset = false; + drv_load_params.override_force_load = QED_OVERRIDE_FORCE_LOAD_NONE; + hw_init_params.p_drv_load_params = &drv_load_params; + rc = qed_hw_init(cdev, &hw_init_params); if (rc) goto err2; diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index f2e5ab90f574..1a820b4b1b8c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -598,48 +598,352 @@ int qed_mcp_nvm_rd_cmd(struct qed_hwfn *p_hwfn, return 0; } -int qed_mcp_load_req(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, u32 *p_load_code) +static bool +qed_mcp_can_force_load(u8 drv_role, + u8 exist_drv_role, + enum qed_override_force_load override_force_load) +{ + bool can_force_load = false; + + switch (override_force_load) { + case QED_OVERRIDE_FORCE_LOAD_ALWAYS: + can_force_load = true; + break; + case QED_OVERRIDE_FORCE_LOAD_NEVER: + can_force_load = false; + break; + default: + can_force_load = (drv_role == DRV_ROLE_OS && + exist_drv_role == DRV_ROLE_PREBOOT) || + (drv_role == DRV_ROLE_KDUMP && + exist_drv_role == DRV_ROLE_OS); + break; + } + + return can_force_load; +} + +static int qed_mcp_cancel_load_req(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt) +{ + u32 resp = 0, param = 0; + int rc; + + rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_CANCEL_LOAD_REQ, 0, + &resp, ¶m); + if (rc) + DP_NOTICE(p_hwfn, + "Failed to send cancel load request, rc = %d\n", rc); + + return rc; +} + +#define CONFIG_QEDE_BITMAP_IDX BIT(0) +#define CONFIG_QED_SRIOV_BITMAP_IDX BIT(1) +#define CONFIG_QEDR_BITMAP_IDX BIT(2) +#define CONFIG_QEDF_BITMAP_IDX BIT(4) +#define CONFIG_QEDI_BITMAP_IDX BIT(5) +#define CONFIG_QED_LL2_BITMAP_IDX BIT(6) + +static u32 qed_get_config_bitmap(void) +{ + u32 config_bitmap = 0x0; + + if (IS_ENABLED(CONFIG_QEDE)) + config_bitmap |= CONFIG_QEDE_BITMAP_IDX; + + if (IS_ENABLED(CONFIG_QED_SRIOV)) + config_bitmap |= CONFIG_QED_SRIOV_BITMAP_IDX; + + if (IS_ENABLED(CONFIG_QED_RDMA)) + config_bitmap |= CONFIG_QEDR_BITMAP_IDX; + + if (IS_ENABLED(CONFIG_QED_FCOE)) + config_bitmap |= CONFIG_QEDF_BITMAP_IDX; + + if (IS_ENABLED(CONFIG_QED_ISCSI)) + config_bitmap |= CONFIG_QEDI_BITMAP_IDX; + + if (IS_ENABLED(CONFIG_QED_LL2)) + config_bitmap |= CONFIG_QED_LL2_BITMAP_IDX; + + return config_bitmap; +} + +struct qed_load_req_in_params { + u8 hsi_ver; +#define QED_LOAD_REQ_HSI_VER_DEFAULT 0 +#define QED_LOAD_REQ_HSI_VER_1 1 + u32 drv_ver_0; + u32 drv_ver_1; + u32 fw_ver; + u8 drv_role; + u8 timeout_val; + u8 force_cmd; + bool avoid_eng_reset; +}; + +struct qed_load_req_out_params { + u32 load_code; + u32 exist_drv_ver_0; + u32 exist_drv_ver_1; + u32 exist_fw_ver; + u8 exist_drv_role; + u8 mfw_hsi_ver; + bool drv_exists; +}; + +static int +__qed_mcp_load_req(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_load_req_in_params *p_in_params, + struct qed_load_req_out_params *p_out_params) { - struct qed_dev *cdev = p_hwfn->cdev; struct qed_mcp_mb_params mb_params; - union drv_union_data union_data; + struct load_req_stc load_req; + struct load_rsp_stc load_rsp; + u32 hsi_ver; int rc; + memset(&load_req, 0, sizeof(load_req)); + load_req.drv_ver_0 = p_in_params->drv_ver_0; + load_req.drv_ver_1 = p_in_params->drv_ver_1; + load_req.fw_ver = p_in_params->fw_ver; + QED_MFW_SET_FIELD(load_req.misc0, LOAD_REQ_ROLE, p_in_params->drv_role); + QED_MFW_SET_FIELD(load_req.misc0, LOAD_REQ_LOCK_TO, + p_in_params->timeout_val); + QED_MFW_SET_FIELD(load_req.misc0, LOAD_REQ_FORCE, + p_in_params->force_cmd); + QED_MFW_SET_FIELD(load_req.misc0, LOAD_REQ_FLAGS0, + p_in_params->avoid_eng_reset); + + hsi_ver = (p_in_params->hsi_ver == QED_LOAD_REQ_HSI_VER_DEFAULT) ? + DRV_ID_MCP_HSI_VER_CURRENT : + (p_in_params->hsi_ver << DRV_ID_MCP_HSI_VER_SHIFT); + memset(&mb_params, 0, sizeof(mb_params)); - /* Load Request */ mb_params.cmd = DRV_MSG_CODE_LOAD_REQ; - mb_params.param = PDA_COMP | DRV_ID_MCP_HSI_VER_CURRENT | - cdev->drv_type; - memcpy(&union_data.ver_str, cdev->ver_str, MCP_DRV_VER_STR_SIZE); - mb_params.p_data_src = &union_data; - mb_params.data_src_size = sizeof(union_data.ver_str); - rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); + mb_params.param = PDA_COMP | hsi_ver | p_hwfn->cdev->drv_type; + mb_params.p_data_src = &load_req; + mb_params.data_src_size = sizeof(load_req); + mb_params.p_data_dst = &load_rsp; + mb_params.data_dst_size = sizeof(load_rsp); - /* if mcp fails to respond we must abort */ + DP_VERBOSE(p_hwfn, QED_MSG_SP, + "Load Request: param 0x%08x [init_hw %d, drv_type %d, hsi_ver %d, pda 0x%04x]\n", + mb_params.param, + QED_MFW_GET_FIELD(mb_params.param, DRV_ID_DRV_INIT_HW), + QED_MFW_GET_FIELD(mb_params.param, DRV_ID_DRV_TYPE), + QED_MFW_GET_FIELD(mb_params.param, DRV_ID_MCP_HSI_VER), + QED_MFW_GET_FIELD(mb_params.param, DRV_ID_PDA_COMP_VER)); + + if (p_in_params->hsi_ver != QED_LOAD_REQ_HSI_VER_1) { + DP_VERBOSE(p_hwfn, QED_MSG_SP, + "Load Request: drv_ver 0x%08x_0x%08x, fw_ver 0x%08x, misc0 0x%08x [role %d, timeout %d, force %d, flags0 0x%x]\n", + load_req.drv_ver_0, + load_req.drv_ver_1, + load_req.fw_ver, + load_req.misc0, + QED_MFW_GET_FIELD(load_req.misc0, LOAD_REQ_ROLE), + QED_MFW_GET_FIELD(load_req.misc0, + LOAD_REQ_LOCK_TO), + QED_MFW_GET_FIELD(load_req.misc0, LOAD_REQ_FORCE), + QED_MFW_GET_FIELD(load_req.misc0, LOAD_REQ_FLAGS0)); + } + + rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); if (rc) { - DP_ERR(p_hwfn, "MCP response failure, aborting\n"); + DP_NOTICE(p_hwfn, "Failed to send load request, rc = %d\n", rc); return rc; } - *p_load_code = mb_params.mcp_resp; + DP_VERBOSE(p_hwfn, QED_MSG_SP, + "Load Response: resp 0x%08x\n", mb_params.mcp_resp); + p_out_params->load_code = mb_params.mcp_resp; - /* If MFW refused (e.g. other port is in diagnostic mode) we - * must abort. This can happen in the following cases: - * - Other port is in diagnostic mode - * - Previously loaded function on the engine is not compliant with - * the requester. - * - MFW cannot cope with the requester's DRV_MFW_HSI_VERSION. - * - + if (p_in_params->hsi_ver != QED_LOAD_REQ_HSI_VER_1 && + p_out_params->load_code != FW_MSG_CODE_DRV_LOAD_REFUSED_HSI_1) { + DP_VERBOSE(p_hwfn, + QED_MSG_SP, + "Load Response: exist_drv_ver 0x%08x_0x%08x, exist_fw_ver 0x%08x, misc0 0x%08x [exist_role %d, mfw_hsi %d, flags0 0x%x]\n", + load_rsp.drv_ver_0, + load_rsp.drv_ver_1, + load_rsp.fw_ver, + load_rsp.misc0, + QED_MFW_GET_FIELD(load_rsp.misc0, LOAD_RSP_ROLE), + QED_MFW_GET_FIELD(load_rsp.misc0, LOAD_RSP_HSI), + QED_MFW_GET_FIELD(load_rsp.misc0, LOAD_RSP_FLAGS0)); + + p_out_params->exist_drv_ver_0 = load_rsp.drv_ver_0; + p_out_params->exist_drv_ver_1 = load_rsp.drv_ver_1; + p_out_params->exist_fw_ver = load_rsp.fw_ver; + p_out_params->exist_drv_role = + QED_MFW_GET_FIELD(load_rsp.misc0, LOAD_RSP_ROLE); + p_out_params->mfw_hsi_ver = + QED_MFW_GET_FIELD(load_rsp.misc0, LOAD_RSP_HSI); + p_out_params->drv_exists = + QED_MFW_GET_FIELD(load_rsp.misc0, LOAD_RSP_FLAGS0) & + LOAD_RSP_FLAGS0_DRV_EXISTS; + } + + return 0; +} + +static int eocre_get_mfw_drv_role(struct qed_hwfn *p_hwfn, + enum qed_drv_role drv_role, + u8 *p_mfw_drv_role) +{ + switch (drv_role) { + case QED_DRV_ROLE_OS: + *p_mfw_drv_role = DRV_ROLE_OS; + break; + case QED_DRV_ROLE_KDUMP: + *p_mfw_drv_role = DRV_ROLE_KDUMP; + break; + default: + DP_ERR(p_hwfn, "Unexpected driver role %d\n", drv_role); + return -EINVAL; + } + + return 0; +} + +enum qed_load_req_force { + QED_LOAD_REQ_FORCE_NONE, + QED_LOAD_REQ_FORCE_PF, + QED_LOAD_REQ_FORCE_ALL, +}; + +static void qed_get_mfw_force_cmd(struct qed_hwfn *p_hwfn, + + enum qed_load_req_force force_cmd, + u8 *p_mfw_force_cmd) +{ + switch (force_cmd) { + case QED_LOAD_REQ_FORCE_NONE: + *p_mfw_force_cmd = LOAD_REQ_FORCE_NONE; + break; + case QED_LOAD_REQ_FORCE_PF: + *p_mfw_force_cmd = LOAD_REQ_FORCE_PF; + break; + case QED_LOAD_REQ_FORCE_ALL: + *p_mfw_force_cmd = LOAD_REQ_FORCE_ALL; + break; + } +} + +int qed_mcp_load_req(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_load_req_params *p_params) +{ + struct qed_load_req_out_params out_params; + struct qed_load_req_in_params in_params; + u8 mfw_drv_role, mfw_force_cmd; + int rc; + + memset(&in_params, 0, sizeof(in_params)); + in_params.hsi_ver = QED_LOAD_REQ_HSI_VER_DEFAULT; + in_params.drv_ver_0 = QED_VERSION; + in_params.drv_ver_1 = qed_get_config_bitmap(); + in_params.fw_ver = STORM_FW_VERSION; + rc = eocre_get_mfw_drv_role(p_hwfn, p_params->drv_role, &mfw_drv_role); + if (rc) + return rc; + + in_params.drv_role = mfw_drv_role; + in_params.timeout_val = p_params->timeout_val; + qed_get_mfw_force_cmd(p_hwfn, + QED_LOAD_REQ_FORCE_NONE, &mfw_force_cmd); + + in_params.force_cmd = mfw_force_cmd; + in_params.avoid_eng_reset = p_params->avoid_eng_reset; + + memset(&out_params, 0, sizeof(out_params)); + rc = __qed_mcp_load_req(p_hwfn, p_ptt, &in_params, &out_params); + if (rc) + return rc; + + /* First handle cases where another load request should/might be sent: + * - MFW expects the old interface [HSI version = 1] + * - MFW responds that a force load request is required */ - if (!(*p_load_code) || - ((*p_load_code) == FW_MSG_CODE_DRV_LOAD_REFUSED_HSI) || - ((*p_load_code) == FW_MSG_CODE_DRV_LOAD_REFUSED_PDA) || - ((*p_load_code) == FW_MSG_CODE_DRV_LOAD_REFUSED_DIAG)) { - DP_ERR(p_hwfn, "MCP refused load request, aborting\n"); + if (out_params.load_code == FW_MSG_CODE_DRV_LOAD_REFUSED_HSI_1) { + DP_INFO(p_hwfn, + "MFW refused a load request due to HSI > 1. Resending with HSI = 1\n"); + + in_params.hsi_ver = QED_LOAD_REQ_HSI_VER_1; + memset(&out_params, 0, sizeof(out_params)); + rc = __qed_mcp_load_req(p_hwfn, p_ptt, &in_params, &out_params); + if (rc) + return rc; + } else if (out_params.load_code == + FW_MSG_CODE_DRV_LOAD_REFUSED_REQUIRES_FORCE) { + if (qed_mcp_can_force_load(in_params.drv_role, + out_params.exist_drv_role, + p_params->override_force_load)) { + DP_INFO(p_hwfn, + "A force load is required [{role, fw_ver, drv_ver}: loading={%d, 0x%08x, x%08x_0x%08x}, existing={%d, 0x%08x, 0x%08x_0x%08x}]\n", + in_params.drv_role, in_params.fw_ver, + in_params.drv_ver_0, in_params.drv_ver_1, + out_params.exist_drv_role, + out_params.exist_fw_ver, + out_params.exist_drv_ver_0, + out_params.exist_drv_ver_1); + + qed_get_mfw_force_cmd(p_hwfn, + QED_LOAD_REQ_FORCE_ALL, + &mfw_force_cmd); + + in_params.force_cmd = mfw_force_cmd; + memset(&out_params, 0, sizeof(out_params)); + rc = __qed_mcp_load_req(p_hwfn, p_ptt, &in_params, + &out_params); + if (rc) + return rc; + } else { + DP_NOTICE(p_hwfn, + "A force load is required [{role, fw_ver, drv_ver}: loading={%d, 0x%08x, x%08x_0x%08x}, existing={%d, 0x%08x, 0x%08x_0x%08x}] - Avoid\n", + in_params.drv_role, in_params.fw_ver, + in_params.drv_ver_0, in_params.drv_ver_1, + out_params.exist_drv_role, + out_params.exist_fw_ver, + out_params.exist_drv_ver_0, + out_params.exist_drv_ver_1); + DP_NOTICE(p_hwfn, + "Avoid sending a force load request to prevent disruption of active PFs\n"); + + qed_mcp_cancel_load_req(p_hwfn, p_ptt); + return -EBUSY; + } + } + + /* Now handle the other types of responses. + * The "REFUSED_HSI_1" and "REFUSED_REQUIRES_FORCE" responses are not + * expected here after the additional revised load requests were sent. + */ + switch (out_params.load_code) { + case FW_MSG_CODE_DRV_LOAD_ENGINE: + case FW_MSG_CODE_DRV_LOAD_PORT: + case FW_MSG_CODE_DRV_LOAD_FUNCTION: + if (out_params.mfw_hsi_ver != QED_LOAD_REQ_HSI_VER_1 && + out_params.drv_exists) { + /* The role and fw/driver version match, but the PF is + * already loaded and has not been unloaded gracefully. + */ + DP_NOTICE(p_hwfn, + "PF is already loaded\n"); + return -EINVAL; + } + break; + default: + DP_NOTICE(p_hwfn, + "Unexpected refusal to load request [resp 0x%08x]. Aborting.\n", + out_params.load_code); return -EBUSY; } + p_params->load_code = out_params.load_code; + return 0; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index 7d067d047e7a..f76afec957da 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -39,6 +39,7 @@ #include #include #include "qed_hsi.h" +#include "qed_dev_api.h" struct qed_mcp_link_speed_params { bool autoneg; @@ -570,27 +571,35 @@ int qed_mcp_free(struct qed_hwfn *p_hwfn); int qed_mcp_handle_events(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); +enum qed_drv_role { + QED_DRV_ROLE_OS, + QED_DRV_ROLE_KDUMP, +}; + +struct qed_load_req_params { + /* Input params */ + enum qed_drv_role drv_role; + u8 timeout_val; + bool avoid_eng_reset; + enum qed_override_force_load override_force_load; + + /* Output params */ + u32 load_code; +}; + /** - * @brief Sends a LOAD_REQ to the MFW, and in case operation - * succeed, returns whether this PF is the first on the - * chip/engine/port or function. This function should be - * called when driver is ready to accept MFW events after - * Storms initializations are done. + * @brief Sends a LOAD_REQ to the MFW, and in case the operation succeeds, + * returns whether this PF is the first on the engine/port or function. * - * @param p_hwfn - hw function - * @param p_ptt - PTT required for register access - * @param p_load_code - The MCP response param containing one - * of the following: - * FW_MSG_CODE_DRV_LOAD_ENGINE - * FW_MSG_CODE_DRV_LOAD_PORT - * FW_MSG_CODE_DRV_LOAD_FUNCTION - * @return int - - * 0 - Operation was successul. - * -EBUSY - Operation failed + * @param p_hwfn + * @param p_ptt + * @param p_params + * + * @return int - 0 - Operation was successful. */ int qed_mcp_load_req(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - u32 *p_load_code); + struct qed_load_req_params *p_params); /** * @brief Sends a UNLOAD_REQ message to the MFW -- cgit From 18a69e368b22817575d2052b2c8dd8427bb4f827 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 28 Mar 2017 15:12:53 +0300 Subject: qed: Send pf-flr as part of initialization During HW initialization, driver would set various registers to their needed values - but it assumes all registers start at their reset-value, so there's no need to re-configure a register's default value. This assumption might be incorrect, e.g., in case of preboot driver running and initializing the driver prior to our driver. To overcome this, we now ask management firmware to initiate a PF-flr early during the initialization sequence. That would return everything in the PF's scope back to default and prevent previous configurations from still being applied. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dev.c | 9 +++++++++ drivers/net/ethernet/qlogic/qed/qed_hsi.h | 1 + drivers/net/ethernet/qlogic/qed/qed_mcp.c | 8 ++++++++ drivers/net/ethernet/qlogic/qed/qed_mcp.h | 10 ++++++++++ 4 files changed, 28 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 759b819935e4..e4b1450ace0c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -2280,6 +2280,15 @@ static int qed_hw_prepare_single(struct qed_hwfn *p_hwfn, goto err2; } + /* Sending a mailbox to the MFW should be done after qed_get_hw_info() + * is called as it sets the ports number in an engine. + */ + if (IS_LEAD_HWFN(p_hwfn)) { + rc = qed_mcp_initiate_pf_flr(p_hwfn, p_hwfn->p_main_ptt); + if (rc) + DP_NOTICE(p_hwfn, "Failed to initiate PF FLR\n"); + } + /* Allocate the init RT array and initialize the init-ops engine */ rc = qed_init_alloc(p_hwfn); if (rc) diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index bf2b20167aca..2de7aa17f46e 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -10093,6 +10093,7 @@ struct public_drv_mb { #define DRV_MSG_CODE_BW_UPDATE_ACK 0x32000000 #define DRV_MSG_CODE_NIG_DRAIN 0x30000000 #define DRV_MSG_GET_RESOURCE_ALLOC_MSG 0x34000000 +#define DRV_MSG_CODE_INITIATE_PF_FLR 0x02010000 #define DRV_MSG_CODE_VF_DISABLED_DONE 0xc0000000 #define DRV_MSG_CODE_CFG_VF_MSIX 0xc0010000 #define DRV_MSG_CODE_NVM_GET_FILE_ATT 0x00030000 diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 1a820b4b1b8c..490619f7e550 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -2263,3 +2263,11 @@ int qed_mcp_get_resc_info(struct qed_hwfn *p_hwfn, return 0; } + +int qed_mcp_initiate_pf_flr(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + u32 mcp_resp, mcp_param; + + return qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_INITIATE_PF_FLR, 0, + &mcp_resp, &mcp_param); +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index f76afec957da..0056cfe69b6a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -770,4 +770,14 @@ int qed_mcp_get_resc_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, struct resource_info *p_resc_info, u32 *p_mcp_resp, u32 *p_mcp_param); + +/** + * @brief - Initiates PF FLR + * + * @param p_hwfn + * @param p_ptt + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_initiate_pf_flr(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); #endif -- cgit From 95691c9cea3b14778bf699dcc1563cdc0c441105 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Tue, 28 Mar 2017 15:12:54 +0300 Subject: qed: Support management-based resource locking Global locking can't properly be used to synchronize between different PFs in all scenarios, as those instances might reside in different logical partitions [e.g., when a PF is assigned via PDA to some VM]. The management firmware provides a generic infrastructure for device locks. For each 'resource', it's guaranteed it could be acquired by at most a single PF at any given time [or by management firmware]. This patch adds the necessary logic in qed for utilizing said infrastructure, implementing lock/unlock internal APIs. Signed-off-by: Tomer Tayar Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_hsi.h | 28 +++++ drivers/net/ethernet/qlogic/qed/qed_mcp.c | 176 ++++++++++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_mcp.h | 65 +++++++++++ 3 files changed, 269 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index 2de7aa17f46e..434639936227 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -10119,6 +10119,33 @@ struct public_drv_mb { #define DRV_MSG_CODE_BIST_TEST 0x001e0000 #define DRV_MSG_CODE_SET_LED_MODE 0x00200000 +#define DRV_MSG_CODE_RESOURCE_CMD 0x00230000 + +#define RESOURCE_CMD_REQ_RESC_MASK 0x0000001F +#define RESOURCE_CMD_REQ_RESC_SHIFT 0 +#define RESOURCE_CMD_REQ_OPCODE_MASK 0x000000E0 +#define RESOURCE_CMD_REQ_OPCODE_SHIFT 5 +#define RESOURCE_OPCODE_REQ 1 +#define RESOURCE_OPCODE_REQ_WO_AGING 2 +#define RESOURCE_OPCODE_REQ_W_AGING 3 +#define RESOURCE_OPCODE_RELEASE 4 +#define RESOURCE_OPCODE_FORCE_RELEASE 5 +#define RESOURCE_CMD_REQ_AGE_MASK 0x0000FF00 +#define RESOURCE_CMD_REQ_AGE_SHIFT 8 + +#define RESOURCE_CMD_RSP_OWNER_MASK 0x000000FF +#define RESOURCE_CMD_RSP_OWNER_SHIFT 0 +#define RESOURCE_CMD_RSP_OPCODE_MASK 0x00000700 +#define RESOURCE_CMD_RSP_OPCODE_SHIFT 8 +#define RESOURCE_OPCODE_GNT 1 +#define RESOURCE_OPCODE_BUSY 2 +#define RESOURCE_OPCODE_RELEASED 3 +#define RESOURCE_OPCODE_RELEASED_PREVIOUS 4 +#define RESOURCE_OPCODE_WRONG_OWNER 5 +#define RESOURCE_OPCODE_UNKNOWN_CMD 255 + +#define RESOURCE_DUMP 0 + #define DRV_MSG_CODE_GET_PF_RDMA_PROTOCOL 0x002b0000 #define DRV_MSG_CODE_OS_WOL 0x002e0000 @@ -10207,6 +10234,7 @@ struct public_drv_mb { u32 fw_mb_header; #define FW_MSG_CODE_MASK 0xffff0000 +#define FW_MSG_CODE_UNSUPPORTED 0x00000000 #define FW_MSG_CODE_DRV_LOAD_ENGINE 0x10100000 #define FW_MSG_CODE_DRV_LOAD_PORT 0x10110000 #define FW_MSG_CODE_DRV_LOAD_FUNCTION 0x10120000 diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 490619f7e550..ddcbc2438474 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -2271,3 +2271,179 @@ int qed_mcp_initiate_pf_flr(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) return qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_INITIATE_PF_FLR, 0, &mcp_resp, &mcp_param); } + +static int qed_mcp_resource_cmd(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 param, u32 *p_mcp_resp, u32 *p_mcp_param) +{ + int rc; + + rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_RESOURCE_CMD, param, + p_mcp_resp, p_mcp_param); + if (rc) + return rc; + + if (*p_mcp_resp == FW_MSG_CODE_UNSUPPORTED) { + DP_INFO(p_hwfn, + "The resource command is unsupported by the MFW\n"); + return -EINVAL; + } + + if (*p_mcp_param == RESOURCE_OPCODE_UNKNOWN_CMD) { + u8 opcode = QED_MFW_GET_FIELD(param, RESOURCE_CMD_REQ_OPCODE); + + DP_NOTICE(p_hwfn, + "The resource command is unknown to the MFW [param 0x%08x, opcode %d]\n", + param, opcode); + return -EINVAL; + } + + return rc; +} + +int +__qed_mcp_resc_lock(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_resc_lock_params *p_params) +{ + u32 param = 0, mcp_resp, mcp_param; + u8 opcode; + int rc; + + switch (p_params->timeout) { + case QED_MCP_RESC_LOCK_TO_DEFAULT: + opcode = RESOURCE_OPCODE_REQ; + p_params->timeout = 0; + break; + case QED_MCP_RESC_LOCK_TO_NONE: + opcode = RESOURCE_OPCODE_REQ_WO_AGING; + p_params->timeout = 0; + break; + default: + opcode = RESOURCE_OPCODE_REQ_W_AGING; + break; + } + + QED_MFW_SET_FIELD(param, RESOURCE_CMD_REQ_RESC, p_params->resource); + QED_MFW_SET_FIELD(param, RESOURCE_CMD_REQ_OPCODE, opcode); + QED_MFW_SET_FIELD(param, RESOURCE_CMD_REQ_AGE, p_params->timeout); + + DP_VERBOSE(p_hwfn, + QED_MSG_SP, + "Resource lock request: param 0x%08x [age %d, opcode %d, resource %d]\n", + param, p_params->timeout, opcode, p_params->resource); + + /* Attempt to acquire the resource */ + rc = qed_mcp_resource_cmd(p_hwfn, p_ptt, param, &mcp_resp, &mcp_param); + if (rc) + return rc; + + /* Analyze the response */ + p_params->owner = QED_MFW_GET_FIELD(mcp_param, RESOURCE_CMD_RSP_OWNER); + opcode = QED_MFW_GET_FIELD(mcp_param, RESOURCE_CMD_RSP_OPCODE); + + DP_VERBOSE(p_hwfn, + QED_MSG_SP, + "Resource lock response: mcp_param 0x%08x [opcode %d, owner %d]\n", + mcp_param, opcode, p_params->owner); + + switch (opcode) { + case RESOURCE_OPCODE_GNT: + p_params->b_granted = true; + break; + case RESOURCE_OPCODE_BUSY: + p_params->b_granted = false; + break; + default: + DP_NOTICE(p_hwfn, + "Unexpected opcode in resource lock response [mcp_param 0x%08x, opcode %d]\n", + mcp_param, opcode); + return -EINVAL; + } + + return 0; +} + +int +qed_mcp_resc_lock(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, struct qed_resc_lock_params *p_params) +{ + u32 retry_cnt = 0; + int rc; + + do { + /* No need for an interval before the first iteration */ + if (retry_cnt) { + if (p_params->sleep_b4_retry) { + u16 retry_interval_in_ms = + DIV_ROUND_UP(p_params->retry_interval, + 1000); + + msleep(retry_interval_in_ms); + } else { + udelay(p_params->retry_interval); + } + } + + rc = __qed_mcp_resc_lock(p_hwfn, p_ptt, p_params); + if (rc) + return rc; + + if (p_params->b_granted) + break; + } while (retry_cnt++ < p_params->retry_num); + + return 0; +} + +int +qed_mcp_resc_unlock(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_resc_unlock_params *p_params) +{ + u32 param = 0, mcp_resp, mcp_param; + u8 opcode; + int rc; + + opcode = p_params->b_force ? RESOURCE_OPCODE_FORCE_RELEASE + : RESOURCE_OPCODE_RELEASE; + QED_MFW_SET_FIELD(param, RESOURCE_CMD_REQ_RESC, p_params->resource); + QED_MFW_SET_FIELD(param, RESOURCE_CMD_REQ_OPCODE, opcode); + + DP_VERBOSE(p_hwfn, QED_MSG_SP, + "Resource unlock request: param 0x%08x [opcode %d, resource %d]\n", + param, opcode, p_params->resource); + + /* Attempt to release the resource */ + rc = qed_mcp_resource_cmd(p_hwfn, p_ptt, param, &mcp_resp, &mcp_param); + if (rc) + return rc; + + /* Analyze the response */ + opcode = QED_MFW_GET_FIELD(mcp_param, RESOURCE_CMD_RSP_OPCODE); + + DP_VERBOSE(p_hwfn, QED_MSG_SP, + "Resource unlock response: mcp_param 0x%08x [opcode %d]\n", + mcp_param, opcode); + + switch (opcode) { + case RESOURCE_OPCODE_RELEASED_PREVIOUS: + DP_INFO(p_hwfn, + "Resource unlock request for an already released resource [%d]\n", + p_params->resource); + /* Fallthrough */ + case RESOURCE_OPCODE_RELEASED: + p_params->b_released = true; + break; + case RESOURCE_OPCODE_WRONG_OWNER: + p_params->b_released = false; + break; + default: + DP_NOTICE(p_hwfn, + "Unexpected opcode in resource unlock response [mcp_param 0x%08x, opcode %d]\n", + mcp_param, opcode); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index 0056cfe69b6a..8673ac10262a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -780,4 +780,69 @@ int qed_mcp_get_resc_info(struct qed_hwfn *p_hwfn, * @return int - 0 - operation was successful. */ int qed_mcp_initiate_pf_flr(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); +struct qed_resc_lock_params { + /* Resource number [valid values are 0..31] */ + u8 resource; + + /* Lock timeout value in seconds [default, none or 1..254] */ + u8 timeout; +#define QED_MCP_RESC_LOCK_TO_DEFAULT 0 +#define QED_MCP_RESC_LOCK_TO_NONE 255 + + /* Number of times to retry locking */ + u8 retry_num; + + /* The interval in usec between retries */ + u16 retry_interval; + + /* Use sleep or delay between retries */ + bool sleep_b4_retry; + + /* Will be set as true if the resource is free and granted */ + bool b_granted; + + /* Will be filled with the resource owner. + * [0..15 = PF0-15, 16 = MFW] + */ + u8 owner; +}; + +/** + * @brief Acquires MFW generic resource lock + * + * @param p_hwfn + * @param p_ptt + * @param p_params + * + * @return int - 0 - operation was successful. + */ +int +qed_mcp_resc_lock(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, struct qed_resc_lock_params *p_params); + +struct qed_resc_unlock_params { + /* Resource number [valid values are 0..31] */ + u8 resource; + + /* Allow to release a resource even if belongs to another PF */ + bool b_force; + + /* Will be set as true if the resource is released */ + bool b_released; +}; + +/** + * @brief Releases MFW generic resource lock + * + * @param p_hwfn + * @param p_ptt + * @param p_params + * + * @return int - 0 - operation was successful. + */ +int +qed_mcp_resc_unlock(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_resc_unlock_params *p_params); + #endif -- cgit From 9c8517c40f6873d5fcdfa0eafdf28c5a921d600b Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Tue, 28 Mar 2017 15:12:55 +0300 Subject: qed: Utilize resource-lock based scheme Management firmware is used as an arbiter between the various PFs in matters of resources, but some of the resources that need to be divided are dependent on the non-management firmware used, so management firmware first needs to be told how many resources there are before trying to divide them. As part of the initialization sequence, driver would first inform the management firmware of the available resources under a dedicated resource lock, and afterwards request for various resources which might be based on the previous set values. Signed-off-by: Tomer Tayar Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 1 + drivers/net/ethernet/qlogic/qed/qed_dev.c | 368 ++++++++++++++++---------- drivers/net/ethernet/qlogic/qed/qed_dev_api.h | 2 + drivers/net/ethernet/qlogic/qed/qed_hsi.h | 8 +- drivers/net/ethernet/qlogic/qed/qed_mcp.c | 206 ++++++++++++-- drivers/net/ethernet/qlogic/qed/qed_mcp.h | 49 +++- 6 files changed, 468 insertions(+), 166 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 187a84bc45ea..cf7129550f1c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -219,6 +219,7 @@ enum qed_resources { QED_LL2_QUEUE, QED_CMDQS_CQS, QED_RDMA_STATS_QUEUE, + QED_BDQ, QED_MAX_RESC, }; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index e4b1450ace0c..e75c83351d34 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1565,187 +1565,222 @@ static void qed_hw_set_feat(struct qed_hwfn *p_hwfn) RESC_NUM(p_hwfn, QED_SB)); } -static enum resource_id_enum qed_hw_get_mfw_res_id(enum qed_resources res_id) +const char *qed_hw_get_resc_name(enum qed_resources res_id) { - enum resource_id_enum mfw_res_id = RESOURCE_NUM_INVALID; - switch (res_id) { - case QED_SB: - mfw_res_id = RESOURCE_NUM_SB_E; - break; case QED_L2_QUEUE: - mfw_res_id = RESOURCE_NUM_L2_QUEUE_E; - break; + return "L2_QUEUE"; case QED_VPORT: - mfw_res_id = RESOURCE_NUM_VPORT_E; - break; + return "VPORT"; case QED_RSS_ENG: - mfw_res_id = RESOURCE_NUM_RSS_ENGINES_E; - break; + return "RSS_ENG"; case QED_PQ: - mfw_res_id = RESOURCE_NUM_PQ_E; - break; + return "PQ"; case QED_RL: - mfw_res_id = RESOURCE_NUM_RL_E; - break; + return "RL"; case QED_MAC: + return "MAC"; case QED_VLAN: - /* Each VFC resource can accommodate both a MAC and a VLAN */ - mfw_res_id = RESOURCE_VFC_FILTER_E; - break; + return "VLAN"; + case QED_RDMA_CNQ_RAM: + return "RDMA_CNQ_RAM"; case QED_ILT: - mfw_res_id = RESOURCE_ILT_E; - break; + return "ILT"; case QED_LL2_QUEUE: - mfw_res_id = RESOURCE_LL2_QUEUE_E; - break; - case QED_RDMA_CNQ_RAM: + return "LL2_QUEUE"; case QED_CMDQS_CQS: - /* CNQ/CMDQS are the same resource */ - mfw_res_id = RESOURCE_CQS_E; - break; + return "CMDQS_CQS"; case QED_RDMA_STATS_QUEUE: - mfw_res_id = RESOURCE_RDMA_STATS_QUEUE_E; - break; + return "RDMA_STATS_QUEUE"; + case QED_BDQ: + return "BDQ"; + case QED_SB: + return "SB"; default: - break; + return "UNKNOWN_RESOURCE"; + } +} + +static int +__qed_hw_set_soft_resc_size(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_resources res_id, + u32 resc_max_val, u32 *p_mcp_resp) +{ + int rc; + + rc = qed_mcp_set_resc_max_val(p_hwfn, p_ptt, res_id, + resc_max_val, p_mcp_resp); + if (rc) { + DP_NOTICE(p_hwfn, + "MFW response failure for a max value setting of resource %d [%s]\n", + res_id, qed_hw_get_resc_name(res_id)); + return rc; } - return mfw_res_id; + if (*p_mcp_resp != FW_MSG_CODE_RESOURCE_ALLOC_OK) + DP_INFO(p_hwfn, + "Failed to set the max value of resource %d [%s]. mcp_resp = 0x%08x.\n", + res_id, qed_hw_get_resc_name(res_id), *p_mcp_resp); + + return 0; } -static u32 qed_hw_get_dflt_resc_num(struct qed_hwfn *p_hwfn, - enum qed_resources res_id) +static int +qed_hw_set_soft_resc_size(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + bool b_ah = QED_IS_AH(p_hwfn->cdev); + u32 resc_max_val, mcp_resp; + u8 res_id; + int rc; + + for (res_id = 0; res_id < QED_MAX_RESC; res_id++) { + switch (res_id) { + case QED_LL2_QUEUE: + resc_max_val = MAX_NUM_LL2_RX_QUEUES; + break; + case QED_RDMA_CNQ_RAM: + /* No need for a case for QED_CMDQS_CQS since + * CNQ/CMDQS are the same resource. + */ + resc_max_val = NUM_OF_CMDQS_CQS; + break; + case QED_RDMA_STATS_QUEUE: + resc_max_val = b_ah ? RDMA_NUM_STATISTIC_COUNTERS_K2 + : RDMA_NUM_STATISTIC_COUNTERS_BB; + break; + case QED_BDQ: + resc_max_val = BDQ_NUM_RESOURCES; + break; + default: + continue; + } + + rc = __qed_hw_set_soft_resc_size(p_hwfn, p_ptt, res_id, + resc_max_val, &mcp_resp); + if (rc) + return rc; + + /* There's no point to continue to the next resource if the + * command is not supported by the MFW. + * We do continue if the command is supported but the resource + * is unknown to the MFW. Such a resource will be later + * configured with the default allocation values. + */ + if (mcp_resp == FW_MSG_CODE_UNSUPPORTED) + return -EINVAL; + } + + return 0; +} + +static +int qed_hw_get_dflt_resc(struct qed_hwfn *p_hwfn, + enum qed_resources res_id, + u32 *p_resc_num, u32 *p_resc_start) { u8 num_funcs = p_hwfn->num_funcs_on_engine; bool b_ah = QED_IS_AH(p_hwfn->cdev); struct qed_sb_cnt_info sb_cnt_info; - u32 dflt_resc_num = 0; switch (res_id) { - case QED_SB: - memset(&sb_cnt_info, 0, sizeof(sb_cnt_info)); - qed_int_get_num_sbs(p_hwfn, &sb_cnt_info); - dflt_resc_num = sb_cnt_info.sb_cnt; - break; case QED_L2_QUEUE: - dflt_resc_num = (b_ah ? MAX_NUM_L2_QUEUES_K2 - : MAX_NUM_L2_QUEUES_BB) / num_funcs; + *p_resc_num = (b_ah ? MAX_NUM_L2_QUEUES_K2 : + MAX_NUM_L2_QUEUES_BB) / num_funcs; break; case QED_VPORT: - dflt_resc_num = MAX_NUM_VPORTS_BB / num_funcs; - dflt_resc_num = (b_ah ? MAX_NUM_VPORTS_K2 - : MAX_NUM_VPORTS_BB) / num_funcs; + *p_resc_num = (b_ah ? MAX_NUM_VPORTS_K2 : + MAX_NUM_VPORTS_BB) / num_funcs; break; case QED_RSS_ENG: - dflt_resc_num = (b_ah ? ETH_RSS_ENGINE_NUM_K2 - : ETH_RSS_ENGINE_NUM_BB) / num_funcs; + *p_resc_num = (b_ah ? ETH_RSS_ENGINE_NUM_K2 : + ETH_RSS_ENGINE_NUM_BB) / num_funcs; break; case QED_PQ: - /* The granularity of the PQs is 8 */ - dflt_resc_num = (b_ah ? MAX_QM_TX_QUEUES_K2 - : MAX_QM_TX_QUEUES_BB) / num_funcs; - dflt_resc_num &= ~0x7; + *p_resc_num = (b_ah ? MAX_QM_TX_QUEUES_K2 : + MAX_QM_TX_QUEUES_BB) / num_funcs; + *p_resc_num &= ~0x7; /* The granularity of the PQs is 8 */ break; case QED_RL: - dflt_resc_num = MAX_QM_GLOBAL_RLS / num_funcs; + *p_resc_num = MAX_QM_GLOBAL_RLS / num_funcs; break; case QED_MAC: case QED_VLAN: /* Each VFC resource can accommodate both a MAC and a VLAN */ - dflt_resc_num = ETH_NUM_MAC_FILTERS / num_funcs; + *p_resc_num = ETH_NUM_MAC_FILTERS / num_funcs; break; case QED_ILT: - dflt_resc_num = (b_ah ? PXP_NUM_ILT_RECORDS_K2 - : PXP_NUM_ILT_RECORDS_BB) / num_funcs; + *p_resc_num = (b_ah ? PXP_NUM_ILT_RECORDS_K2 : + PXP_NUM_ILT_RECORDS_BB) / num_funcs; break; case QED_LL2_QUEUE: - dflt_resc_num = MAX_NUM_LL2_RX_QUEUES / num_funcs; + *p_resc_num = MAX_NUM_LL2_RX_QUEUES / num_funcs; break; case QED_RDMA_CNQ_RAM: case QED_CMDQS_CQS: /* CNQ/CMDQS are the same resource */ - dflt_resc_num = NUM_OF_CMDQS_CQS / num_funcs; + *p_resc_num = NUM_OF_CMDQS_CQS / num_funcs; break; case QED_RDMA_STATS_QUEUE: - dflt_resc_num = (b_ah ? RDMA_NUM_STATISTIC_COUNTERS_K2 - : RDMA_NUM_STATISTIC_COUNTERS_BB) / - num_funcs; - + *p_resc_num = (b_ah ? RDMA_NUM_STATISTIC_COUNTERS_K2 : + RDMA_NUM_STATISTIC_COUNTERS_BB) / num_funcs; break; - default: + case QED_BDQ: + if (p_hwfn->hw_info.personality != QED_PCI_ISCSI && + p_hwfn->hw_info.personality != QED_PCI_FCOE) + *p_resc_num = 0; + else + *p_resc_num = 1; break; + case QED_SB: + memset(&sb_cnt_info, 0, sizeof(sb_cnt_info)); + qed_int_get_num_sbs(p_hwfn, &sb_cnt_info); + *p_resc_num = sb_cnt_info.sb_cnt; + break; + default: + return -EINVAL; } - return dflt_resc_num; -} - -static const char *qed_hw_get_resc_name(enum qed_resources res_id) -{ switch (res_id) { - case QED_SB: - return "SB"; - case QED_L2_QUEUE: - return "L2_QUEUE"; - case QED_VPORT: - return "VPORT"; - case QED_RSS_ENG: - return "RSS_ENG"; - case QED_PQ: - return "PQ"; - case QED_RL: - return "RL"; - case QED_MAC: - return "MAC"; - case QED_VLAN: - return "VLAN"; - case QED_RDMA_CNQ_RAM: - return "RDMA_CNQ_RAM"; - case QED_ILT: - return "ILT"; - case QED_LL2_QUEUE: - return "LL2_QUEUE"; - case QED_CMDQS_CQS: - return "CMDQS_CQS"; - case QED_RDMA_STATS_QUEUE: - return "RDMA_STATS_QUEUE"; + case QED_BDQ: + if (!*p_resc_num) + *p_resc_start = 0; + else if (p_hwfn->cdev->num_ports_in_engines == 4) + *p_resc_start = p_hwfn->port_id; + else if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) + *p_resc_start = p_hwfn->port_id; + else if (p_hwfn->hw_info.personality == QED_PCI_FCOE) + *p_resc_start = p_hwfn->port_id + 2; + break; default: - return "UNKNOWN_RESOURCE"; + *p_resc_start = *p_resc_num * p_hwfn->enabled_func_idx; + break; } + + return 0; } -static int qed_hw_set_resc_info(struct qed_hwfn *p_hwfn, - enum qed_resources res_id) +static int __qed_hw_set_resc_info(struct qed_hwfn *p_hwfn, + enum qed_resources res_id) { - u32 dflt_resc_num = 0, dflt_resc_start = 0, mcp_resp, mcp_param; - u32 *p_resc_num, *p_resc_start; - struct resource_info resc_info; + u32 dflt_resc_num = 0, dflt_resc_start = 0; + u32 mcp_resp, *p_resc_num, *p_resc_start; int rc; p_resc_num = &RESC_NUM(p_hwfn, res_id); p_resc_start = &RESC_START(p_hwfn, res_id); - /* Default values assumes that each function received equal share */ - dflt_resc_num = qed_hw_get_dflt_resc_num(p_hwfn, res_id); - if (!dflt_resc_num) { + rc = qed_hw_get_dflt_resc(p_hwfn, res_id, &dflt_resc_num, + &dflt_resc_start); + if (rc) { DP_ERR(p_hwfn, "Failed to get default amount for resource %d [%s]\n", res_id, qed_hw_get_resc_name(res_id)); - return -EINVAL; - } - dflt_resc_start = dflt_resc_num * p_hwfn->enabled_func_idx; - - memset(&resc_info, 0, sizeof(resc_info)); - resc_info.res_id = qed_hw_get_mfw_res_id(res_id); - if (resc_info.res_id == RESOURCE_NUM_INVALID) { - DP_ERR(p_hwfn, - "Failed to match resource %d [%s] with the MFW resources\n", - res_id, qed_hw_get_resc_name(res_id)); - return -EINVAL; + return rc; } - rc = qed_mcp_get_resc_info(p_hwfn, p_hwfn->p_main_ptt, &resc_info, - &mcp_resp, &mcp_param); + rc = qed_mcp_get_resc_info(p_hwfn, p_hwfn->p_main_ptt, res_id, + &mcp_resp, p_resc_num, p_resc_start); if (rc) { DP_NOTICE(p_hwfn, "MFW response failure for an allocation request for resource %d [%s]\n", @@ -1758,13 +1793,12 @@ static int qed_hw_set_resc_info(struct qed_hwfn *p_hwfn, * - There is an internal error in the MFW while processing the request * - The resource ID is unknown to the MFW */ - if (mcp_resp != FW_MSG_CODE_RESOURCE_ALLOC_OK && - mcp_resp != FW_MSG_CODE_RESOURCE_ALLOC_DEPRECATED) { - DP_NOTICE(p_hwfn, - "Resource %d [%s]: No allocation info was received [mcp_resp 0x%x]. Applying default values [num %d, start %d].\n", - res_id, - qed_hw_get_resc_name(res_id), - mcp_resp, dflt_resc_num, dflt_resc_start); + if (mcp_resp != FW_MSG_CODE_RESOURCE_ALLOC_OK) { + DP_INFO(p_hwfn, + "Failed to receive allocation info for resource %d [%s]. mcp_resp = 0x%x. Applying default values [%d,%d].\n", + res_id, + qed_hw_get_resc_name(res_id), + mcp_resp, dflt_resc_num, dflt_resc_start); *p_resc_num = dflt_resc_num; *p_resc_start = dflt_resc_start; goto out; @@ -1772,13 +1806,9 @@ static int qed_hw_set_resc_info(struct qed_hwfn *p_hwfn, /* Special handling for status blocks; Would be revised in future */ if (res_id == QED_SB) { - resc_info.size -= 1; - resc_info.offset -= p_hwfn->enabled_func_idx; + *p_resc_num -= 1; + *p_resc_start -= p_hwfn->enabled_func_idx; } - - *p_resc_num = resc_info.size; - *p_resc_start = resc_info.offset; - out: /* PQs have to divide by 8 [that's the HW granularity]. * Reduce number so it would fit. @@ -1796,18 +1826,85 @@ out: return 0; } -static int qed_hw_get_resc(struct qed_hwfn *p_hwfn) +static int qed_hw_set_resc_info(struct qed_hwfn *p_hwfn) { - bool b_ah = QED_IS_AH(p_hwfn->cdev); - u8 res_id; int rc; + u8 res_id; for (res_id = 0; res_id < QED_MAX_RESC; res_id++) { - rc = qed_hw_set_resc_info(p_hwfn, res_id); + rc = __qed_hw_set_resc_info(p_hwfn, res_id); if (rc) return rc; } + return 0; +} + +#define QED_RESC_ALLOC_LOCK_RETRY_CNT 10 +#define QED_RESC_ALLOC_LOCK_RETRY_INTVL_US 10000 /* 10 msec */ + +static int qed_hw_get_resc(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + struct qed_resc_unlock_params resc_unlock_params; + struct qed_resc_lock_params resc_lock_params; + bool b_ah = QED_IS_AH(p_hwfn->cdev); + u8 res_id; + int rc; + + /* Setting the max values of the soft resources and the following + * resources allocation queries should be atomic. Since several PFs can + * run in parallel - a resource lock is needed. + * If either the resource lock or resource set value commands are not + * supported - skip the the max values setting, release the lock if + * needed, and proceed to the queries. Other failures, including a + * failure to acquire the lock, will cause this function to fail. + */ + memset(&resc_lock_params, 0, sizeof(resc_lock_params)); + resc_lock_params.resource = QED_RESC_LOCK_RESC_ALLOC; + resc_lock_params.retry_num = QED_RESC_ALLOC_LOCK_RETRY_CNT; + resc_lock_params.retry_interval = QED_RESC_ALLOC_LOCK_RETRY_INTVL_US; + resc_lock_params.sleep_b4_retry = true; + memset(&resc_unlock_params, 0, sizeof(resc_unlock_params)); + resc_unlock_params.resource = QED_RESC_LOCK_RESC_ALLOC; + + rc = qed_mcp_resc_lock(p_hwfn, p_ptt, &resc_lock_params); + if (rc && rc != -EINVAL) { + return rc; + } else if (rc == -EINVAL) { + DP_INFO(p_hwfn, + "Skip the max values setting of the soft resources since the resource lock is not supported by the MFW\n"); + } else if (!rc && !resc_lock_params.b_granted) { + DP_NOTICE(p_hwfn, + "Failed to acquire the resource lock for the resource allocation commands\n"); + return -EBUSY; + } else { + rc = qed_hw_set_soft_resc_size(p_hwfn, p_ptt); + if (rc && rc != -EINVAL) { + DP_NOTICE(p_hwfn, + "Failed to set the max values of the soft resources\n"); + goto unlock_and_exit; + } else if (rc == -EINVAL) { + DP_INFO(p_hwfn, + "Skip the max values setting of the soft resources since it is not supported by the MFW\n"); + rc = qed_mcp_resc_unlock(p_hwfn, p_ptt, + &resc_unlock_params); + if (rc) + DP_INFO(p_hwfn, + "Failed to release the resource lock for the resource allocation commands\n"); + } + } + + rc = qed_hw_set_resc_info(p_hwfn); + if (rc) + goto unlock_and_exit; + + if (resc_lock_params.b_granted && !resc_unlock_params.b_released) { + rc = qed_mcp_resc_unlock(p_hwfn, p_ptt, &resc_unlock_params); + if (rc) + DP_INFO(p_hwfn, + "Failed to release the resource lock for the resource allocation commands\n"); + } + /* Sanity for ILT */ if ((b_ah && (RESC_END(p_hwfn, QED_ILT) > PXP_NUM_ILT_RECORDS_K2)) || (!b_ah && (RESC_END(p_hwfn, QED_ILT) > PXP_NUM_ILT_RECORDS_BB))) { @@ -1819,8 +1916,6 @@ static int qed_hw_get_resc(struct qed_hwfn *p_hwfn) qed_hw_set_feat(p_hwfn); - DP_VERBOSE(p_hwfn, NETIF_MSG_PROBE, - "The numbers for each resource are:\n"); for (res_id = 0; res_id < QED_MAX_RESC; res_id++) DP_VERBOSE(p_hwfn, NETIF_MSG_PROBE, "%s = %d start = %d\n", qed_hw_get_resc_name(res_id), @@ -1828,6 +1923,11 @@ static int qed_hw_get_resc(struct qed_hwfn *p_hwfn) RESC_START(p_hwfn, res_id)); return 0; + +unlock_and_exit: + if (resc_lock_params.b_granted && !resc_unlock_params.b_released) + qed_mcp_resc_unlock(p_hwfn, p_ptt, &resc_unlock_params); + return rc; } static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) @@ -2158,7 +2258,7 @@ qed_get_hw_info(struct qed_hwfn *p_hwfn, if (qed_mcp_is_init(p_hwfn)) p_hwfn->hw_info.mtu = p_hwfn->mcp_info->func_info.mtu; - return qed_hw_get_resc(p_hwfn); + return qed_hw_get_resc(p_hwfn, p_ptt); } static int qed_get_dev_info(struct qed_dev *cdev) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h index cb973495cb38..2c6637fd7ef6 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h +++ b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h @@ -470,4 +470,6 @@ int qed_set_rxq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, */ int qed_set_txq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u16 coalesce, u8 qid, u16 sb_id); + +const char *qed_hw_get_resc_name(enum qed_resources res_id); #endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index 434639936227..c6b9a3fd4f46 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -9986,6 +9986,7 @@ enum resource_id_enum { RESOURCE_NUM_RSS_ENGINES_E = 14, RESOURCE_LL2_QUEUE_E = 15, RESOURCE_RDMA_STATS_QUEUE_E = 16, + RESOURCE_BDQ_E = 17, RESOURCE_MAX_NUM, RESOURCE_NUM_INVALID = 0xFFFFFFFF }; @@ -10087,12 +10088,13 @@ struct public_drv_mb { #define DRV_MSG_CODE_OV_UPDATE_DRIVER_STATE 0x31000000 #define DRV_MSG_CODE_BW_UPDATE_ACK 0x32000000 #define DRV_MSG_CODE_OV_UPDATE_MTU 0x33000000 +#define DRV_MSG_GET_RESOURCE_ALLOC_MSG 0x34000000 +#define DRV_MSG_SET_RESOURCE_VALUE_MSG 0x35000000 #define DRV_MSG_CODE_OV_UPDATE_WOL 0x38000000 #define DRV_MSG_CODE_OV_UPDATE_ESWITCH_MODE 0x39000000 #define DRV_MSG_CODE_BW_UPDATE_ACK 0x32000000 #define DRV_MSG_CODE_NIG_DRAIN 0x30000000 -#define DRV_MSG_GET_RESOURCE_ALLOC_MSG 0x34000000 #define DRV_MSG_CODE_INITIATE_PF_FLR 0x02010000 #define DRV_MSG_CODE_VF_DISABLED_DONE 0xc0000000 #define DRV_MSG_CODE_CFG_VF_MSIX 0xc0010000 @@ -10263,6 +10265,10 @@ struct public_drv_mb { #define FW_MSG_SEQ_NUMBER_MASK 0x0000ffff u32 fw_mb_param; +#define FW_MB_PARAM_RESOURCE_ALLOC_VERSION_MAJOR_MASK 0xFFFF0000 +#define FW_MB_PARAM_RESOURCE_ALLOC_VERSION_MAJOR_SHIFT 16 +#define FW_MB_PARAM_RESOURCE_ALLOC_VERSION_MINOR_MASK 0x0000FFFF +#define FW_MB_PARAM_RESOURCE_ALLOC_VERSION_MINOR_SHIFT 0 /* get pf rdma protocol command responce */ #define FW_MB_PARAM_GET_PF_RDMA_NONE 0x0 diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index ddcbc2438474..619eac845028 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -2220,46 +2220,212 @@ int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn, return rc; } -#define QED_RESC_ALLOC_VERSION_MAJOR 1 +static enum resource_id_enum qed_mcp_get_mfw_res_id(enum qed_resources res_id) +{ + enum resource_id_enum mfw_res_id = RESOURCE_NUM_INVALID; + + switch (res_id) { + case QED_SB: + mfw_res_id = RESOURCE_NUM_SB_E; + break; + case QED_L2_QUEUE: + mfw_res_id = RESOURCE_NUM_L2_QUEUE_E; + break; + case QED_VPORT: + mfw_res_id = RESOURCE_NUM_VPORT_E; + break; + case QED_RSS_ENG: + mfw_res_id = RESOURCE_NUM_RSS_ENGINES_E; + break; + case QED_PQ: + mfw_res_id = RESOURCE_NUM_PQ_E; + break; + case QED_RL: + mfw_res_id = RESOURCE_NUM_RL_E; + break; + case QED_MAC: + case QED_VLAN: + /* Each VFC resource can accommodate both a MAC and a VLAN */ + mfw_res_id = RESOURCE_VFC_FILTER_E; + break; + case QED_ILT: + mfw_res_id = RESOURCE_ILT_E; + break; + case QED_LL2_QUEUE: + mfw_res_id = RESOURCE_LL2_QUEUE_E; + break; + case QED_RDMA_CNQ_RAM: + case QED_CMDQS_CQS: + /* CNQ/CMDQS are the same resource */ + mfw_res_id = RESOURCE_CQS_E; + break; + case QED_RDMA_STATS_QUEUE: + mfw_res_id = RESOURCE_RDMA_STATS_QUEUE_E; + break; + case QED_BDQ: + mfw_res_id = RESOURCE_BDQ_E; + break; + default: + break; + } + + return mfw_res_id; +} + +#define QED_RESC_ALLOC_VERSION_MAJOR 2 #define QED_RESC_ALLOC_VERSION_MINOR 0 #define QED_RESC_ALLOC_VERSION \ ((QED_RESC_ALLOC_VERSION_MAJOR << \ DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MAJOR_SHIFT) | \ (QED_RESC_ALLOC_VERSION_MINOR << \ DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MINOR_SHIFT)) -int qed_mcp_get_resc_info(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - struct resource_info *p_resc_info, - u32 *p_mcp_resp, u32 *p_mcp_param) + +struct qed_resc_alloc_in_params { + u32 cmd; + enum qed_resources res_id; + u32 resc_max_val; +}; + +struct qed_resc_alloc_out_params { + u32 mcp_resp; + u32 mcp_param; + u32 resc_num; + u32 resc_start; + u32 vf_resc_num; + u32 vf_resc_start; + u32 flags; +}; + +static int +qed_mcp_resc_allocation_msg(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_resc_alloc_in_params *p_in_params, + struct qed_resc_alloc_out_params *p_out_params) { struct qed_mcp_mb_params mb_params; + struct resource_info mfw_resc_info; int rc; + memset(&mfw_resc_info, 0, sizeof(mfw_resc_info)); + + mfw_resc_info.res_id = qed_mcp_get_mfw_res_id(p_in_params->res_id); + if (mfw_resc_info.res_id == RESOURCE_NUM_INVALID) { + DP_ERR(p_hwfn, + "Failed to match resource %d [%s] with the MFW resources\n", + p_in_params->res_id, + qed_hw_get_resc_name(p_in_params->res_id)); + return -EINVAL; + } + + switch (p_in_params->cmd) { + case DRV_MSG_SET_RESOURCE_VALUE_MSG: + mfw_resc_info.size = p_in_params->resc_max_val; + /* Fallthrough */ + case DRV_MSG_GET_RESOURCE_ALLOC_MSG: + break; + default: + DP_ERR(p_hwfn, "Unexpected resource alloc command [0x%08x]\n", + p_in_params->cmd); + return -EINVAL; + } + memset(&mb_params, 0, sizeof(mb_params)); - mb_params.cmd = DRV_MSG_GET_RESOURCE_ALLOC_MSG; + mb_params.cmd = p_in_params->cmd; mb_params.param = QED_RESC_ALLOC_VERSION; + mb_params.p_data_src = &mfw_resc_info; + mb_params.data_src_size = sizeof(mfw_resc_info); + mb_params.p_data_dst = mb_params.p_data_src; + mb_params.data_dst_size = mb_params.data_src_size; + + DP_VERBOSE(p_hwfn, + QED_MSG_SP, + "Resource message request: cmd 0x%08x, res_id %d [%s], hsi_version %d.%d, val 0x%x\n", + p_in_params->cmd, + p_in_params->res_id, + qed_hw_get_resc_name(p_in_params->res_id), + QED_MFW_GET_FIELD(mb_params.param, + DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MAJOR), + QED_MFW_GET_FIELD(mb_params.param, + DRV_MB_PARAM_RESOURCE_ALLOC_VERSION_MINOR), + p_in_params->resc_max_val); - mb_params.p_data_src = p_resc_info; - mb_params.data_src_size = sizeof(*p_resc_info); - mb_params.p_data_dst = p_resc_info; - mb_params.data_dst_size = sizeof(*p_resc_info); rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); if (rc) return rc; - /* Copy the data back */ - *p_mcp_resp = mb_params.mcp_resp; - *p_mcp_param = mb_params.mcp_param; + p_out_params->mcp_resp = mb_params.mcp_resp; + p_out_params->mcp_param = mb_params.mcp_param; + p_out_params->resc_num = mfw_resc_info.size; + p_out_params->resc_start = mfw_resc_info.offset; + p_out_params->vf_resc_num = mfw_resc_info.vf_size; + p_out_params->vf_resc_start = mfw_resc_info.vf_offset; + p_out_params->flags = mfw_resc_info.flags; DP_VERBOSE(p_hwfn, QED_MSG_SP, - "MFW resource_info: version 0x%x, res_id 0x%x, size 0x%x, offset 0x%x, vf_size 0x%x, vf_offset 0x%x, flags 0x%x\n", - *p_mcp_param, - p_resc_info->res_id, - p_resc_info->size, - p_resc_info->offset, - p_resc_info->vf_size, - p_resc_info->vf_offset, p_resc_info->flags); + "Resource message response: mfw_hsi_version %d.%d, num 0x%x, start 0x%x, vf_num 0x%x, vf_start 0x%x, flags 0x%08x\n", + QED_MFW_GET_FIELD(p_out_params->mcp_param, + FW_MB_PARAM_RESOURCE_ALLOC_VERSION_MAJOR), + QED_MFW_GET_FIELD(p_out_params->mcp_param, + FW_MB_PARAM_RESOURCE_ALLOC_VERSION_MINOR), + p_out_params->resc_num, + p_out_params->resc_start, + p_out_params->vf_resc_num, + p_out_params->vf_resc_start, p_out_params->flags); + + return 0; +} + +int +qed_mcp_set_resc_max_val(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_resources res_id, + u32 resc_max_val, u32 *p_mcp_resp) +{ + struct qed_resc_alloc_out_params out_params; + struct qed_resc_alloc_in_params in_params; + int rc; + + memset(&in_params, 0, sizeof(in_params)); + in_params.cmd = DRV_MSG_SET_RESOURCE_VALUE_MSG; + in_params.res_id = res_id; + in_params.resc_max_val = resc_max_val; + memset(&out_params, 0, sizeof(out_params)); + rc = qed_mcp_resc_allocation_msg(p_hwfn, p_ptt, &in_params, + &out_params); + if (rc) + return rc; + + *p_mcp_resp = out_params.mcp_resp; + + return 0; +} + +int +qed_mcp_get_resc_info(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_resources res_id, + u32 *p_mcp_resp, u32 *p_resc_num, u32 *p_resc_start) +{ + struct qed_resc_alloc_out_params out_params; + struct qed_resc_alloc_in_params in_params; + int rc; + + memset(&in_params, 0, sizeof(in_params)); + in_params.cmd = DRV_MSG_GET_RESOURCE_ALLOC_MSG; + in_params.res_id = res_id; + memset(&out_params, 0, sizeof(out_params)); + rc = qed_mcp_resc_allocation_msg(p_hwfn, p_ptt, &in_params, + &out_params); + if (rc) + return rc; + + *p_mcp_resp = out_params.mcp_resp; + + if (*p_mcp_resp == FW_MSG_CODE_RESOURCE_ALLOC_OK) { + *p_resc_num = out_params.resc_num; + *p_resc_start = out_params.resc_start; + } return 0; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index 8673ac10262a..ac7d406be1ed 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -743,33 +743,60 @@ int qed_mcp_mask_parities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u32 mask_parities); /** - * @brief Send eswitch mode to MFW + * @brief - Sets the MFW's max value for the given resource * * @param p_hwfn * @param p_ptt - * @param eswitch - eswitch mode + * @param res_id + * @param resc_max_val + * @param p_mcp_resp * * @return int - 0 - operation was successful. */ -int qed_mcp_ov_update_eswitch(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - enum qed_ov_eswitch eswitch); +int +qed_mcp_set_resc_max_val(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_resources res_id, + u32 resc_max_val, u32 *p_mcp_resp); /** * @brief - Gets the MFW allocation info for the given resource * * @param p_hwfn * @param p_ptt - * @param p_resc_info - descriptor of requested resource + * @param res_id * @param p_mcp_resp - * @param p_mcp_param + * @param p_resc_num + * @param p_resc_start * * @return int - 0 - operation was successful. */ -int qed_mcp_get_resc_info(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - struct resource_info *p_resc_info, - u32 *p_mcp_resp, u32 *p_mcp_param); +int +qed_mcp_get_resc_info(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_resources res_id, + u32 *p_mcp_resp, u32 *p_resc_num, u32 *p_resc_start); + +/** + * @brief Send eswitch mode to MFW + * + * @param p_hwfn + * @param p_ptt + * @param eswitch - eswitch mode + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_ov_update_eswitch(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + enum qed_ov_eswitch eswitch); + +#define QED_MCP_RESC_LOCK_MIN_VAL RESOURCE_DUMP +#define QED_MCP_RESC_LOCK_MAX_VAL 31 + +enum qed_resc_lock { + QED_RESC_LOCK_DBG_DUMP = QED_MCP_RESC_LOCK_MIN_VAL, + QED_RESC_LOCK_RESC_ALLOC = QED_MCP_RESC_LOCK_MAX_VAL +}; /** * @brief - Initiates PF FLR -- cgit From d0d40a73f1e6061d892eca9a6cf1c85a9e4fd1eb Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Tue, 28 Mar 2017 15:12:56 +0300 Subject: qed: Use BDQ resource for storage protocols Until now, qed used some port-defined value as BDQ index for both iSCSI and FCoE. As management firmware now treats BDQ as a resource and tells each PF its BDQ-range, start using a valure from that range instead. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 2 -- drivers/net/ethernet/qlogic/qed/qed_fcoe.c | 30 +++++++++++++++++++-------- drivers/net/ethernet/qlogic/qed/qed_iscsi.c | 32 +++++++++++++++++++---------- 3 files changed, 42 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index cf7129550f1c..8c85fd2f3949 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -71,8 +71,6 @@ extern const struct qed_common_ops qed_common_ops_pass; #define QED_WFQ_UNIT 100 -#define ISCSI_BDQ_ID(_port_id) (_port_id) -#define FCOE_BDQ_ID(_port_id) ((_port_id) + 2) #define QED_WID_SIZE (1024) #define QED_PF_DEMS_SIZE (4) diff --git a/drivers/net/ethernet/qlogic/qed/qed_fcoe.c b/drivers/net/ethernet/qlogic/qed/qed_fcoe.c index cbc81412174f..60921b72c995 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_fcoe.c +++ b/drivers/net/ethernet/qlogic/qed/qed_fcoe.c @@ -191,7 +191,7 @@ qed_sp_fcoe_func_start(struct qed_hwfn *p_hwfn, p_data->q_params.cq_sb_pi = fcoe_pf_params->gl_rq_pi; p_data->q_params.cmdq_sb_pi = fcoe_pf_params->gl_cmd_pi; - p_data->q_params.bdq_resource_id = FCOE_BDQ_ID(p_hwfn->port_id); + p_data->q_params.bdq_resource_id = (u8)RESC_START(p_hwfn, QED_BDQ); DMA_REGPAIR_LE(p_data->q_params.bdq_pbl_base_address[BDQ_ID_RQ], fcoe_pf_params->bdq_pbl_base_addr[BDQ_ID_RQ]); @@ -512,19 +512,31 @@ static void __iomem *qed_fcoe_get_db_addr(struct qed_hwfn *p_hwfn, u32 cid) static void __iomem *qed_fcoe_get_primary_bdq_prod(struct qed_hwfn *p_hwfn, u8 bdq_id) { - u8 bdq_function_id = FCOE_BDQ_ID(p_hwfn->port_id); - - return (u8 __iomem *)p_hwfn->regview + GTT_BAR0_MAP_REG_MSDM_RAM + - MSTORM_SCSI_BDQ_EXT_PROD_OFFSET(bdq_function_id, bdq_id); + if (RESC_NUM(p_hwfn, QED_BDQ)) { + return (u8 __iomem *)p_hwfn->regview + + GTT_BAR0_MAP_REG_MSDM_RAM + + MSTORM_SCSI_BDQ_EXT_PROD_OFFSET(RESC_START(p_hwfn, + QED_BDQ), + bdq_id); + } else { + DP_NOTICE(p_hwfn, "BDQ is not allocated!\n"); + return NULL; + } } static void __iomem *qed_fcoe_get_secondary_bdq_prod(struct qed_hwfn *p_hwfn, u8 bdq_id) { - u8 bdq_function_id = FCOE_BDQ_ID(p_hwfn->port_id); - - return (u8 __iomem *)p_hwfn->regview + GTT_BAR0_MAP_REG_TSDM_RAM + - TSTORM_SCSI_BDQ_EXT_PROD_OFFSET(bdq_function_id, bdq_id); + if (RESC_NUM(p_hwfn, QED_BDQ)) { + return (u8 __iomem *)p_hwfn->regview + + GTT_BAR0_MAP_REG_TSDM_RAM + + TSTORM_SCSI_BDQ_EXT_PROD_OFFSET(RESC_START(p_hwfn, + QED_BDQ), + bdq_id); + } else { + DP_NOTICE(p_hwfn, "BDQ is not allocated!\n"); + return NULL; + } } struct qed_fcoe_info *qed_fcoe_alloc(struct qed_hwfn *p_hwfn) diff --git a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c index 098766f7fe88..2f8ac75ebd84 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c +++ b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c @@ -216,7 +216,7 @@ qed_sp_iscsi_func_start(struct qed_hwfn *p_hwfn, p_queue->cq_cmdq_sb_num_arr[i] = cpu_to_le16(val); } - p_queue->bdq_resource_id = ISCSI_BDQ_ID(p_hwfn->port_id); + p_queue->bdq_resource_id = (u8)RESC_START(p_hwfn, QED_BDQ); DMA_REGPAIR_LE(p_queue->bdq_pbl_base_address[BDQ_ID_RQ], p_params->bdq_pbl_base_addr[BDQ_ID_RQ]); @@ -593,21 +593,31 @@ static void __iomem *qed_iscsi_get_db_addr(struct qed_hwfn *p_hwfn, u32 cid) static void __iomem *qed_iscsi_get_primary_bdq_prod(struct qed_hwfn *p_hwfn, u8 bdq_id) { - u8 bdq_function_id = ISCSI_BDQ_ID(p_hwfn->port_id); - - return (u8 __iomem *)p_hwfn->regview + GTT_BAR0_MAP_REG_MSDM_RAM + - MSTORM_SCSI_BDQ_EXT_PROD_OFFSET(bdq_function_id, - bdq_id); + if (RESC_NUM(p_hwfn, QED_BDQ)) { + return (u8 __iomem *)p_hwfn->regview + + GTT_BAR0_MAP_REG_MSDM_RAM + + MSTORM_SCSI_BDQ_EXT_PROD_OFFSET(RESC_START(p_hwfn, + QED_BDQ), + bdq_id); + } else { + DP_NOTICE(p_hwfn, "BDQ is not allocated!\n"); + return NULL; + } } static void __iomem *qed_iscsi_get_secondary_bdq_prod(struct qed_hwfn *p_hwfn, u8 bdq_id) { - u8 bdq_function_id = ISCSI_BDQ_ID(p_hwfn->port_id); - - return (u8 __iomem *)p_hwfn->regview + GTT_BAR0_MAP_REG_TSDM_RAM + - TSTORM_SCSI_BDQ_EXT_PROD_OFFSET(bdq_function_id, - bdq_id); + if (RESC_NUM(p_hwfn, QED_BDQ)) { + return (u8 __iomem *)p_hwfn->regview + + GTT_BAR0_MAP_REG_TSDM_RAM + + TSTORM_SCSI_BDQ_EXT_PROD_OFFSET(RESC_START(p_hwfn, + QED_BDQ), + bdq_id); + } else { + DP_NOTICE(p_hwfn, "BDQ is not allocated!\n"); + return NULL; + } } static int qed_iscsi_setup_connection(struct qed_hwfn *p_hwfn, -- cgit From 990e27b033f7199af0e02d63aa61293f380c4e61 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 28 Mar 2017 13:50:32 -0400 Subject: net: dsa: mv88e6xxx: reorder 88E6141 definitions The related mv88e6xxx_ops and mv88e6xxx_info structure were misplaced. Reorder them correctly to fix this. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 92 ++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 2bca297d9296..df6d8604caf1 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2944,6 +2944,37 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .reset = mv88e6185_g1_reset, }; +static const struct mv88e6xxx_ops mv88e6141_ops = { + /* MV88E6XXX_FAMILY_6341 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .set_switch_mac = mv88e6xxx_g2_set_switch_mac, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, + .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .stats_snapshot = mv88e6390_g1_stats_snapshot, + .stats_get_sset_count = mv88e6320_stats_get_sset_count, + .stats_get_strings = mv88e6320_stats_get_strings, + .stats_get_stats = mv88e6390_stats_get_stats, + .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, + .g1_set_egress_port = mv88e6390_g1_set_egress_port, + .watchdog_ops = &mv88e6390_watchdog_ops, + .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, +}; + static const struct mv88e6xxx_ops mv88e6161_ops = { /* MV88E6XXX_FAMILY_6165 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, @@ -3436,37 +3467,6 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .reset = mv88e6352_g1_reset, }; -static const struct mv88e6xxx_ops mv88e6141_ops = { - /* MV88E6XXX_FAMILY_6341 */ - .get_eeprom = mv88e6xxx_g2_get_eeprom8, - .set_eeprom = mv88e6xxx_g2_set_eeprom8, - .set_switch_mac = mv88e6xxx_g2_set_switch_mac, - .phy_read = mv88e6xxx_g2_smi_phy_read, - .phy_write = mv88e6xxx_g2_smi_phy_write, - .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390_port_set_speed, - .port_tag_remap = mv88e6095_port_tag_remap, - .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_floods = mv88e6352_port_set_egress_floods, - .port_set_ether_type = mv88e6351_port_set_ether_type, - .port_jumbo_config = mv88e6165_port_jumbo_config, - .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, - .port_pause_config = mv88e6097_port_pause_config, - .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, - .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .stats_snapshot = mv88e6390_g1_stats_snapshot, - .stats_get_sset_count = mv88e6320_stats_get_sset_count, - .stats_get_strings = mv88e6320_stats_get_strings, - .stats_get_stats = mv88e6390_stats_get_stats, - .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, - .g1_set_egress_port = mv88e6390_g1_set_egress_port, - .watchdog_ops = &mv88e6390_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, - .reset = mv88e6352_g1_reset, -}; - static const struct mv88e6xxx_ops mv88e6341_ops = { /* MV88E6XXX_FAMILY_6341 */ .get_eeprom = mv88e6xxx_g2_get_eeprom8, @@ -3674,6 +3674,21 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .ops = &mv88e6131_ops, }, + [MV88E6141] = { + .prod_num = PORT_SWITCH_ID_PROD_NUM_6141, + .family = MV88E6XXX_FAMILY_6341, + .name = "Marvell 88E6341", + .num_databases = 4096, + .num_ports = 6, + .port_base_addr = 0x10, + .global1_addr = 0x1b, + .age_time_coeff = 3750, + .atu_move_port_mask = 0x1f, + .tag_protocol = DSA_TAG_PROTO_EDSA, + .flags = MV88E6XXX_FLAGS_FAMILY_6341, + .ops = &mv88e6141_ops, + }, + [MV88E6161] = { .prod_num = PORT_SWITCH_ID_PROD_NUM_6161, .family = MV88E6XXX_FAMILY_6165, @@ -3898,21 +3913,6 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .ops = &mv88e6321_ops, }, - [MV88E6141] = { - .prod_num = PORT_SWITCH_ID_PROD_NUM_6141, - .family = MV88E6XXX_FAMILY_6341, - .name = "Marvell 88E6341", - .num_databases = 4096, - .num_ports = 6, - .port_base_addr = 0x10, - .global1_addr = 0x1b, - .age_time_coeff = 3750, - .atu_move_port_mask = 0x1f, - .tag_protocol = DSA_TAG_PROTO_EDSA, - .flags = MV88E6XXX_FLAGS_FAMILY_6341, - .ops = &mv88e6141_ops, - }, - [MV88E6341] = { .prod_num = PORT_SWITCH_ID_PROD_NUM_6341, .family = MV88E6XXX_FAMILY_6341, -- cgit From 16e329aea90e4e404edce2828d701b1a3498487e Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 28 Mar 2017 13:50:33 -0400 Subject: net: dsa: mv88e6xxx: reorder 88E6341 definitions The related mv88e6xxx_ops structure was misplaced. Reorder it correctly to fix this. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 62 ++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index df6d8604caf1..3918790c4a75 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3378,6 +3378,37 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .reset = mv88e6352_g1_reset, }; +static const struct mv88e6xxx_ops mv88e6341_ops = { + /* MV88E6XXX_FAMILY_6341 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom8, + .set_eeprom = mv88e6xxx_g2_set_eeprom8, + .set_switch_mac = mv88e6xxx_g2_set_switch_mac, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, + .port_set_speed = mv88e6390_port_set_speed, + .port_tag_remap = mv88e6095_port_tag_remap, + .port_set_frame_mode = mv88e6351_port_set_frame_mode, + .port_set_egress_floods = mv88e6352_port_set_egress_floods, + .port_set_ether_type = mv88e6351_port_set_ether_type, + .port_jumbo_config = mv88e6165_port_jumbo_config, + .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, + .port_pause_config = mv88e6097_port_pause_config, + .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, + .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .stats_snapshot = mv88e6390_g1_stats_snapshot, + .stats_get_sset_count = mv88e6320_stats_get_sset_count, + .stats_get_strings = mv88e6320_stats_get_strings, + .stats_get_stats = mv88e6390_stats_get_stats, + .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, + .g1_set_egress_port = mv88e6390_g1_set_egress_port, + .watchdog_ops = &mv88e6390_watchdog_ops, + .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, + .reset = mv88e6352_g1_reset, +}; + static const struct mv88e6xxx_ops mv88e6350_ops = { /* MV88E6XXX_FAMILY_6351 */ .set_switch_mac = mv88e6xxx_g2_set_switch_mac, @@ -3467,37 +3498,6 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .reset = mv88e6352_g1_reset, }; -static const struct mv88e6xxx_ops mv88e6341_ops = { - /* MV88E6XXX_FAMILY_6341 */ - .get_eeprom = mv88e6xxx_g2_get_eeprom8, - .set_eeprom = mv88e6xxx_g2_set_eeprom8, - .set_switch_mac = mv88e6xxx_g2_set_switch_mac, - .phy_read = mv88e6xxx_g2_smi_phy_read, - .phy_write = mv88e6xxx_g2_smi_phy_write, - .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390_port_set_speed, - .port_tag_remap = mv88e6095_port_tag_remap, - .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_floods = mv88e6352_port_set_egress_floods, - .port_set_ether_type = mv88e6351_port_set_ether_type, - .port_jumbo_config = mv88e6165_port_jumbo_config, - .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, - .port_pause_config = mv88e6097_port_pause_config, - .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, - .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .stats_snapshot = mv88e6390_g1_stats_snapshot, - .stats_get_sset_count = mv88e6320_stats_get_sset_count, - .stats_get_strings = mv88e6320_stats_get_strings, - .stats_get_stats = mv88e6390_stats_get_stats, - .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, - .g1_set_egress_port = mv88e6390_g1_set_egress_port, - .watchdog_ops = &mv88e6390_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, - .reset = mv88e6352_g1_reset, -}; - static const struct mv88e6xxx_ops mv88e6390_ops = { /* MV88E6XXX_FAMILY_6390 */ .get_eeprom = mv88e6xxx_g2_get_eeprom8, -- cgit From 2cf4cefb69cba2dce4968c4517bf00957dd7fda8 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 28 Mar 2017 13:50:34 -0400 Subject: net: dsa: mv88e6xxx: fix 88E6191 ops The mv88e6xxx_info structure for the 88E6191 chip was pointing the mv88e6391_ops definition instead of mv88e6191_ops. Fix this. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 3918790c4a75..da7bf608cc58 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3846,7 +3846,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .atu_move_port_mask = 0x1f, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, - .ops = &mv88e6391_ops, + .ops = &mv88e6191_ops, }, [MV88E6240] = { -- cgit From 63709570cda3db1172695efeb049d43b7f756452 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 28 Mar 2017 13:50:35 -0400 Subject: net: dsa: mv88e6xxx: remove 88E6391 ops We don't support 88E6391 anywhere in the code, so remove the unused mv88e6391_ops structure. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index da7bf608cc58..323f8392e1ce 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3563,36 +3563,6 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .reset = mv88e6352_g1_reset, }; -static const struct mv88e6xxx_ops mv88e6391_ops = { - /* MV88E6XXX_FAMILY_6390 */ - .get_eeprom = mv88e6xxx_g2_get_eeprom8, - .set_eeprom = mv88e6xxx_g2_set_eeprom8, - .set_switch_mac = mv88e6xxx_g2_set_switch_mac, - .phy_read = mv88e6xxx_g2_smi_phy_read, - .phy_write = mv88e6xxx_g2_smi_phy_write, - .port_set_link = mv88e6xxx_port_set_link, - .port_set_duplex = mv88e6xxx_port_set_duplex, - .port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay, - .port_set_speed = mv88e6390_port_set_speed, - .port_tag_remap = mv88e6390_port_tag_remap, - .port_set_frame_mode = mv88e6351_port_set_frame_mode, - .port_set_egress_floods = mv88e6352_port_set_egress_floods, - .port_set_ether_type = mv88e6351_port_set_ether_type, - .port_pause_config = mv88e6390_port_pause_config, - .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, - .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, - .stats_snapshot = mv88e6390_g1_stats_snapshot, - .stats_set_histogram = mv88e6390_g1_stats_set_histogram, - .stats_get_sset_count = mv88e6320_stats_get_sset_count, - .stats_get_strings = mv88e6320_stats_get_strings, - .stats_get_stats = mv88e6390_stats_get_stats, - .g1_set_cpu_port = mv88e6390_g1_set_cpu_port, - .g1_set_egress_port = mv88e6390_g1_set_egress_port, - .watchdog_ops = &mv88e6390_watchdog_ops, - .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, - .reset = mv88e6352_g1_reset, -}; - static const struct mv88e6xxx_info mv88e6xxx_table[] = { [MV88E6085] = { .prod_num = PORT_SWITCH_ID_PROD_NUM_6085, -- cgit From 382ed72480e122421d759eb986690f04041d34bc Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Tue, 28 Mar 2017 14:49:16 -0400 Subject: ipv6: add support for NETDEV_RESEND_IGMP event This patch adds support for NETDEV_RESEND_IGMP event similar to how it works for IPv4. Signed-off-by: Vladislav Yasevich Signed-off-by: David S. Miller --- include/net/ndisc.h | 2 ++ net/ipv6/af_inet6.c | 8 +++++++- net/ipv6/mcast.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/include/net/ndisc.h b/include/net/ndisc.h index 8a0214654b6b..1036c902d2c9 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -439,8 +439,10 @@ void ndisc_update(const struct net_device *dev, struct neighbour *neigh, * IGMP */ int igmp6_init(void); +int igmp6_late_init(void); void igmp6_cleanup(void); +void igmp6_late_cleanup(void); int igmp6_event_query(struct sk_buff *skb); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index a9a9553ee63d..1635d218735e 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -1005,6 +1005,10 @@ static int __init inet6_init(void) if (err) goto seg6_fail; + err = igmp6_late_init(); + if (err) + goto igmp6_late_err; + #ifdef CONFIG_SYSCTL err = ipv6_sysctl_register(); if (err) @@ -1015,8 +1019,10 @@ out: #ifdef CONFIG_SYSCTL sysctl_fail: - seg6_exit(); + igmp6_late_cleanup(); #endif +igmp6_late_err: + seg6_exit(); seg6_fail: calipso_exit(); calipso_fail: diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 1bdc703cb966..07403fa164e1 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -2463,7 +2463,6 @@ static void mld_ifc_event(struct inet6_dev *idev) mld_ifc_start_timer(idev, 1); } - static void igmp6_timer_handler(unsigned long data) { struct ifmcaddr6 *ma = (struct ifmcaddr6 *) data; @@ -2599,6 +2598,44 @@ void ipv6_mc_destroy_dev(struct inet6_dev *idev) write_unlock_bh(&idev->lock); } +static void ipv6_mc_rejoin_groups(struct inet6_dev *idev) +{ + struct ifmcaddr6 *pmc; + + ASSERT_RTNL(); + + if (mld_in_v1_mode(idev)) { + read_lock_bh(&idev->lock); + for (pmc = idev->mc_list; pmc; pmc = pmc->next) + igmp6_join_group(pmc); + read_unlock_bh(&idev->lock); + } else + mld_send_report(idev, NULL); +} + +static int ipv6_mc_netdev_event(struct notifier_block *this, + unsigned long event, + void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct inet6_dev *idev = __in6_dev_get(dev); + + switch (event) { + case NETDEV_RESEND_IGMP: + if (idev) + ipv6_mc_rejoin_groups(idev); + break; + default: + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block igmp6_netdev_notifier = { + .notifier_call = ipv6_mc_netdev_event, +}; + #ifdef CONFIG_PROC_FS struct igmp6_mc_iter_state { struct seq_net_private p; @@ -2970,7 +3007,17 @@ int __init igmp6_init(void) return register_pernet_subsys(&igmp6_net_ops); } +int __init igmp6_late_init(void) +{ + return register_netdevice_notifier(&igmp6_netdev_notifier); +} + void igmp6_cleanup(void) { unregister_pernet_subsys(&igmp6_net_ops); } + +void igmp6_late_cleanup(void) +{ + unregister_netdevice_notifier(&igmp6_netdev_notifier); +} -- cgit From 64014fe67b0aafa129d80266fce58b60783d5c91 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 28 Mar 2017 15:09:43 -0400 Subject: net: dsa: mv88e6xxx: unconditionally set ATU trunk Set the trunk member of the mv88e6xxx_atu_entry structure regardless its value, so that uninitialized structures gets the correct boolean value. Note that no mainline code is affected by the current behavior. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/global1_atu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index 120b7f41a735..492e00dea3ce 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -121,9 +121,7 @@ static int mv88e6xxx_g1_atu_data_read(struct mv88e6xxx_chip *chip, entry->state = val & 0xf; if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) { - if (val & GLOBAL_ATU_DATA_TRUNK) - entry->trunk = true; - + entry->trunk = !!(val & GLOBAL_ATU_DATA_TRUNK); entry->portvec = (val >> 4) & mv88e6xxx_port_mask(chip); } -- cgit From 4333d619f9e30592426bc1315243fa0754e62c39 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 28 Mar 2017 15:10:36 -0400 Subject: net: dsa: fix copyright holder I do not hold the copyright of the DSA core and drivers source files, since these changes have been written as an initiative of my day job. Fix this. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Acked-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 3 +++ drivers/net/dsa/mv88e6xxx/global1.c | 3 ++- drivers/net/dsa/mv88e6xxx/global1.h | 3 ++- drivers/net/dsa/mv88e6xxx/global2.c | 3 ++- drivers/net/dsa/mv88e6xxx/global2.h | 3 ++- drivers/net/dsa/mv88e6xxx/port.c | 3 ++- drivers/net/dsa/mv88e6xxx/port.h | 3 ++- net/dsa/switch.c | 3 ++- 8 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 323f8392e1ce..3c946af1159d 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -8,6 +8,9 @@ * * Copyright (c) 2016 Andrew Lunn * + * Copyright (c) 2016-2017 Savoir-faire Linux Inc. + * Vivien Didelot + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index 75af86a7fad8..39825837a1c9 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -3,7 +3,8 @@ * * Copyright (c) 2008 Marvell Semiconductor * - * Copyright (c) 2016 Vivien Didelot + * Copyright (c) 2016-2017 Savoir-faire Linux Inc. + * Vivien Didelot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index eece7418e67d..e30cbe480d5b 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -3,7 +3,8 @@ * * Copyright (c) 2008 Marvell Semiconductor * - * Copyright (c) 2016 Vivien Didelot + * Copyright (c) 2016-2017 Savoir-faire Linux Inc. + * Vivien Didelot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 8f15bc7b1f5f..0b8601f8536e 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -4,7 +4,8 @@ * * Copyright (c) 2008 Marvell Semiconductor * - * Copyright (c) 2016 Vivien Didelot + * Copyright (c) 2016-2017 Savoir-faire Linux Inc. + * Vivien Didelot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index a8b2f9486a4a..f8b6dd93213a 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -3,7 +3,8 @@ * * Copyright (c) 2008 Marvell Semiconductor * - * Copyright (c) 2016 Vivien Didelot + * Copyright (c) 2016-2017 Savoir-faire Linux Inc. + * Vivien Didelot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index d4868bb50ed5..548a956637ee 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -3,7 +3,8 @@ * * Copyright (c) 2008 Marvell Semiconductor * - * Copyright (c) 2016 Vivien Didelot + * Copyright (c) 2016-2017 Savoir-faire Linux Inc. + * Vivien Didelot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index c2425ddab287..86f40887b6d2 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -3,7 +3,8 @@ * * Copyright (c) 2008 Marvell Semiconductor * - * Copyright (c) 2016 Vivien Didelot + * Copyright (c) 2016-2017 Savoir-faire Linux Inc. + * Vivien Didelot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/net/dsa/switch.c b/net/dsa/switch.c index 6456dacf9ae9..7b6f38e5fef6 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -1,7 +1,8 @@ /* * Handling of a single switch chip, part of a switch fabric * - * Copyright (c) 2017 Vivien Didelot + * Copyright (c) 2017 Savoir-faire Linux Inc. + * Vivien Didelot * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by -- cgit From 50c0add534d2f56547201c9f566ad4b4f40c3049 Mon Sep 17 00:00:00 2001 From: Prasad Kanneganti Date: Tue, 28 Mar 2017 12:14:06 -0700 Subject: liquidio: refactor interrupt moderation code Refactor interrupt moderation code for flexibility because parameters are different for 10G and 25G cards. Currently parameters (for 10G only) come from macros compiled-in to the PF and VF drivers; fix it so that parameters suitable for the card (10G or 25G) come from the NIC firmware via response to a command. Also bump up driver version to 1.5.1 to match newer NIC firmware version. Signed-off-by: Prasad Kanneganti Signed-off-by: Felix Manlunas Signed-off-by: Derek Chickles Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 447 +++++++++++++-------- drivers/net/ethernet/cavium/liquidio/lio_main.c | 17 - drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 17 - .../net/ethernet/cavium/liquidio/liquidio_common.h | 26 +- .../net/ethernet/cavium/liquidio/octeon_device.h | 13 +- 5 files changed, 276 insertions(+), 244 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index 6eef3b999130..fac02ed2c449 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -33,6 +33,19 @@ static int octnet_get_link_stats(struct net_device *netdev); +struct oct_intrmod_context { + int octeon_id; + wait_queue_head_t wc; + int cond; + int status; +}; + +struct oct_intrmod_resp { + u64 rh; + struct oct_intrmod_cfg intrmod; + u64 status; +}; + struct oct_mdio_cmd_context { int octeon_id; wait_queue_head_t wc; @@ -1298,95 +1311,103 @@ static int lio_vf_get_sset_count(struct net_device *netdev, int sset) } } -static int lio_get_intr_coalesce(struct net_device *netdev, - struct ethtool_coalesce *intr_coal) +/* Callback function for intrmod */ +static void octnet_intrmod_callback(struct octeon_device *oct_dev, + u32 status, + void *ptr) { - struct lio *lio = GET_LIO(netdev); - struct octeon_device *oct = lio->oct_dev; - struct octeon_instr_queue *iq; - struct oct_intrmod_cfg *intrmod_cfg; + struct octeon_soft_command *sc = (struct octeon_soft_command *)ptr; + struct oct_intrmod_context *ctx; - intrmod_cfg = &oct->intrmod; + ctx = (struct oct_intrmod_context *)sc->ctxptr; - switch (oct->chip_id) { - case OCTEON_CN23XX_PF_VID: - case OCTEON_CN23XX_VF_VID: - if (!intrmod_cfg->rx_enable) { - intr_coal->rx_coalesce_usecs = intrmod_cfg->rx_usecs; - intr_coal->rx_max_coalesced_frames = - intrmod_cfg->rx_frames; - } - if (!intrmod_cfg->tx_enable) - intr_coal->tx_max_coalesced_frames = - intrmod_cfg->tx_frames; - break; - case OCTEON_CN68XX: - case OCTEON_CN66XX: { - struct octeon_cn6xxx *cn6xxx = - (struct octeon_cn6xxx *)oct->chip; + ctx->status = status; - if (!intrmod_cfg->rx_enable) { - intr_coal->rx_coalesce_usecs = - CFG_GET_OQ_INTR_TIME(cn6xxx->conf); - intr_coal->rx_max_coalesced_frames = - CFG_GET_OQ_INTR_PKT(cn6xxx->conf); - } - iq = oct->instr_queue[lio->linfo.txpciq[0].s.q_no]; - intr_coal->tx_max_coalesced_frames = iq->fill_threshold; - break; - } - default: - netif_info(lio, drv, lio->netdev, "Unknown Chip !!\n"); + oct_dev = lio_get_device(ctx->octeon_id); + + WRITE_ONCE(ctx->cond, 1); + + /* This barrier is required to be sure that the response has been + * written fully before waking up the handler + */ + wmb(); + + wake_up_interruptible(&ctx->wc); +} + +/* get interrupt moderation parameters */ +static int octnet_get_intrmod_cfg(struct lio *lio, + struct oct_intrmod_cfg *intr_cfg) +{ + struct octeon_soft_command *sc; + struct oct_intrmod_context *ctx; + struct oct_intrmod_resp *resp; + int retval; + struct octeon_device *oct_dev = lio->oct_dev; + + /* Alloc soft command */ + sc = (struct octeon_soft_command *) + octeon_alloc_soft_command(oct_dev, + 0, + sizeof(struct oct_intrmod_resp), + sizeof(struct oct_intrmod_context)); + + if (!sc) + return -ENOMEM; + + resp = (struct oct_intrmod_resp *)sc->virtrptr; + memset(resp, 0, sizeof(struct oct_intrmod_resp)); + + ctx = (struct oct_intrmod_context *)sc->ctxptr; + memset(resp, 0, sizeof(struct oct_intrmod_context)); + WRITE_ONCE(ctx->cond, 0); + ctx->octeon_id = lio_get_device_id(oct_dev); + init_waitqueue_head(&ctx->wc); + + sc->iq_no = lio->linfo.txpciq[0].s.q_no; + + octeon_prepare_soft_command(oct_dev, sc, OPCODE_NIC, + OPCODE_NIC_INTRMOD_PARAMS, 0, 0, 0); + + sc->callback = octnet_intrmod_callback; + sc->callback_arg = sc; + sc->wait_time = 1000; + + retval = octeon_send_soft_command(oct_dev, sc); + if (retval == IQ_SEND_FAILED) { + octeon_free_soft_command(oct_dev, sc); return -EINVAL; } - if (intrmod_cfg->rx_enable) { - intr_coal->use_adaptive_rx_coalesce = - intrmod_cfg->rx_enable; - intr_coal->rate_sample_interval = - intrmod_cfg->check_intrvl; - intr_coal->pkt_rate_high = - intrmod_cfg->maxpkt_ratethr; - intr_coal->pkt_rate_low = - intrmod_cfg->minpkt_ratethr; - intr_coal->rx_max_coalesced_frames_high = - intrmod_cfg->rx_maxcnt_trigger; - intr_coal->rx_coalesce_usecs_high = - intrmod_cfg->rx_maxtmr_trigger; - intr_coal->rx_coalesce_usecs_low = - intrmod_cfg->rx_mintmr_trigger; - intr_coal->rx_max_coalesced_frames_low = - intrmod_cfg->rx_mincnt_trigger; + + /* Sleep on a wait queue till the cond flag indicates that the + * response arrived or timed-out. + */ + if (sleep_cond(&ctx->wc, &ctx->cond) == -EINTR) { + dev_err(&oct_dev->pci_dev->dev, "Wait interrupted\n"); + goto intrmod_info_wait_intr; } - if ((OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) && - (intrmod_cfg->tx_enable)) { - intr_coal->use_adaptive_tx_coalesce = intrmod_cfg->tx_enable; - intr_coal->tx_max_coalesced_frames_high = - intrmod_cfg->tx_maxcnt_trigger; - intr_coal->tx_max_coalesced_frames_low = - intrmod_cfg->tx_mincnt_trigger; + + retval = ctx->status || resp->status; + if (retval) { + dev_err(&oct_dev->pci_dev->dev, + "Get interrupt moderation parameters failed\n"); + goto intrmod_info_wait_fail; } - return 0; -} -/* Callback function for intrmod */ -static void octnet_intrmod_callback(struct octeon_device *oct_dev, - u32 status, - void *ptr) -{ - struct oct_intrmod_cmd *cmd = ptr; - struct octeon_soft_command *sc = cmd->sc; + octeon_swap_8B_data((u64 *)&resp->intrmod, + (sizeof(struct oct_intrmod_cfg)) / 8); + memcpy(intr_cfg, &resp->intrmod, sizeof(struct oct_intrmod_cfg)); + octeon_free_soft_command(oct_dev, sc); - oct_dev = cmd->oct_dev; + return 0; - if (status) - dev_err(&oct_dev->pci_dev->dev, "intrmod config failed. Status: %llx\n", - CVM_CAST64(status)); - else - dev_info(&oct_dev->pci_dev->dev, - "Rx-Adaptive Interrupt moderation enabled:%llx\n", - oct_dev->intrmod.rx_enable); +intrmod_info_wait_fail: octeon_free_soft_command(oct_dev, sc); + +intrmod_info_wait_intr: + + return -ENODEV; } /* Configure interrupt moderation parameters */ @@ -1394,7 +1415,7 @@ static int octnet_set_intrmod_cfg(struct lio *lio, struct oct_intrmod_cfg *intr_cfg) { struct octeon_soft_command *sc; - struct oct_intrmod_cmd *cmd; + struct oct_intrmod_context *ctx; struct oct_intrmod_cfg *cfg; int retval; struct octeon_device *oct_dev = lio->oct_dev; @@ -1404,19 +1425,21 @@ static int octnet_set_intrmod_cfg(struct lio *lio, octeon_alloc_soft_command(oct_dev, sizeof(struct oct_intrmod_cfg), 0, - sizeof(struct oct_intrmod_cmd)); + sizeof(struct oct_intrmod_context)); if (!sc) return -ENOMEM; - cmd = (struct oct_intrmod_cmd *)sc->ctxptr; + ctx = (struct oct_intrmod_context *)sc->ctxptr; + + WRITE_ONCE(ctx->cond, 0); + ctx->octeon_id = lio_get_device_id(oct_dev); + init_waitqueue_head(&ctx->wc); + cfg = (struct oct_intrmod_cfg *)sc->virtdptr; memcpy(cfg, intr_cfg, sizeof(struct oct_intrmod_cfg)); octeon_swap_8B_data((u64 *)cfg, (sizeof(struct oct_intrmod_cfg)) / 8); - cmd->sc = sc; - cmd->cfg = cfg; - cmd->oct_dev = oct_dev; sc->iq_no = lio->linfo.txpciq[0].s.q_no; @@ -1424,7 +1447,7 @@ static int octnet_set_intrmod_cfg(struct lio *lio, OPCODE_NIC_INTRMOD_CFG, 0, 0, 0); sc->callback = octnet_intrmod_callback; - sc->callback_arg = cmd; + sc->callback_arg = sc; sc->wait_time = 1000; retval = octeon_send_soft_command(oct_dev, sc); @@ -1433,7 +1456,29 @@ static int octnet_set_intrmod_cfg(struct lio *lio, return -EINVAL; } - return 0; + /* Sleep on a wait queue till the cond flag indicates that the + * response arrived or timed-out. + */ + if (sleep_cond(&ctx->wc, &ctx->cond) != -EINTR) { + retval = ctx->status; + if (retval) + dev_err(&oct_dev->pci_dev->dev, + "intrmod config failed. Status: %llx\n", + CVM_CAST64(retval)); + else + dev_info(&oct_dev->pci_dev->dev, + "Rx-Adaptive Interrupt moderation %s\n", + (intr_cfg->rx_enable) ? + "enabled" : "disabled"); + + octeon_free_soft_command(oct_dev, sc); + + return ((retval) ? -ENODEV : 0); + } + + dev_err(&oct_dev->pci_dev->dev, "iq/oq config failed\n"); + + return -EINTR; } static void @@ -1590,80 +1635,106 @@ static int octnet_get_link_stats(struct net_device *netdev) return 0; } -/* Enable/Disable auto interrupt Moderation */ -static int oct_cfg_adaptive_intr(struct lio *lio, struct ethtool_coalesce - *intr_coal) +static int lio_get_intr_coalesce(struct net_device *netdev, + struct ethtool_coalesce *intr_coal) { - int ret = 0; + struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; - struct oct_intrmod_cfg *intrmod_cfg; - - intrmod_cfg = &oct->intrmod; + struct octeon_instr_queue *iq; + struct oct_intrmod_cfg intrmod_cfg; - if (oct->intrmod.rx_enable || oct->intrmod.tx_enable) { - if (intr_coal->rate_sample_interval) - intrmod_cfg->check_intrvl = - intr_coal->rate_sample_interval; - else - intrmod_cfg->check_intrvl = - LIO_INTRMOD_CHECK_INTERVAL; + if (octnet_get_intrmod_cfg(lio, &intrmod_cfg)) + return -ENODEV; - if (intr_coal->pkt_rate_high) - intrmod_cfg->maxpkt_ratethr = - intr_coal->pkt_rate_high; - else - intrmod_cfg->maxpkt_ratethr = - LIO_INTRMOD_MAXPKT_RATETHR; - - if (intr_coal->pkt_rate_low) - intrmod_cfg->minpkt_ratethr = - intr_coal->pkt_rate_low; - else - intrmod_cfg->minpkt_ratethr = - LIO_INTRMOD_MINPKT_RATETHR; + switch (oct->chip_id) { + case OCTEON_CN23XX_PF_VID: + case OCTEON_CN23XX_VF_VID: { + if (!intrmod_cfg.rx_enable) { + intr_coal->rx_coalesce_usecs = oct->rx_coalesce_usecs; + intr_coal->rx_max_coalesced_frames = + oct->rx_max_coalesced_frames; + } + if (!intrmod_cfg.tx_enable) + intr_coal->tx_max_coalesced_frames = + oct->tx_max_coalesced_frames; + break; } - if (oct->intrmod.rx_enable) { - if (intr_coal->rx_max_coalesced_frames_high) - intrmod_cfg->rx_maxcnt_trigger = - intr_coal->rx_max_coalesced_frames_high; - else - intrmod_cfg->rx_maxcnt_trigger = - LIO_INTRMOD_RXMAXCNT_TRIGGER; + case OCTEON_CN68XX: + case OCTEON_CN66XX: { + struct octeon_cn6xxx *cn6xxx = + (struct octeon_cn6xxx *)oct->chip; - if (intr_coal->rx_coalesce_usecs_high) - intrmod_cfg->rx_maxtmr_trigger = - intr_coal->rx_coalesce_usecs_high; - else - intrmod_cfg->rx_maxtmr_trigger = - LIO_INTRMOD_RXMAXTMR_TRIGGER; + if (!intrmod_cfg.rx_enable) { + intr_coal->rx_coalesce_usecs = + CFG_GET_OQ_INTR_TIME(cn6xxx->conf); + intr_coal->rx_max_coalesced_frames = + CFG_GET_OQ_INTR_PKT(cn6xxx->conf); + } + iq = oct->instr_queue[lio->linfo.txpciq[0].s.q_no]; + intr_coal->tx_max_coalesced_frames = iq->fill_threshold; + break; + } + default: + netif_info(lio, drv, lio->netdev, "Unknown Chip !!\n"); + return -EINVAL; + } + if (intrmod_cfg.rx_enable) { + intr_coal->use_adaptive_rx_coalesce = + intrmod_cfg.rx_enable; + intr_coal->rate_sample_interval = + intrmod_cfg.check_intrvl; + intr_coal->pkt_rate_high = + intrmod_cfg.maxpkt_ratethr; + intr_coal->pkt_rate_low = + intrmod_cfg.minpkt_ratethr; + intr_coal->rx_max_coalesced_frames_high = + intrmod_cfg.rx_maxcnt_trigger; + intr_coal->rx_coalesce_usecs_high = + intrmod_cfg.rx_maxtmr_trigger; + intr_coal->rx_coalesce_usecs_low = + intrmod_cfg.rx_mintmr_trigger; + intr_coal->rx_max_coalesced_frames_low = + intrmod_cfg.rx_mincnt_trigger; + } + if ((OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) && + (intrmod_cfg.tx_enable)) { + intr_coal->use_adaptive_tx_coalesce = + intrmod_cfg.tx_enable; + intr_coal->tx_max_coalesced_frames_high = + intrmod_cfg.tx_maxcnt_trigger; + intr_coal->tx_max_coalesced_frames_low = + intrmod_cfg.tx_mincnt_trigger; + } + return 0; +} - if (intr_coal->rx_coalesce_usecs_low) - intrmod_cfg->rx_mintmr_trigger = - intr_coal->rx_coalesce_usecs_low; - else - intrmod_cfg->rx_mintmr_trigger = - LIO_INTRMOD_RXMINTMR_TRIGGER; +/* Enable/Disable auto interrupt Moderation */ +static int oct_cfg_adaptive_intr(struct lio *lio, + struct oct_intrmod_cfg *intrmod_cfg, + struct ethtool_coalesce *intr_coal) +{ + int ret = 0; - if (intr_coal->rx_max_coalesced_frames_low) - intrmod_cfg->rx_mincnt_trigger = - intr_coal->rx_max_coalesced_frames_low; - else - intrmod_cfg->rx_mincnt_trigger = - LIO_INTRMOD_RXMINCNT_TRIGGER; + if (intrmod_cfg->rx_enable || intrmod_cfg->tx_enable) { + intrmod_cfg->check_intrvl = intr_coal->rate_sample_interval; + intrmod_cfg->maxpkt_ratethr = intr_coal->pkt_rate_high; + intrmod_cfg->minpkt_ratethr = intr_coal->pkt_rate_low; } - if (oct->intrmod.tx_enable) { - if (intr_coal->tx_max_coalesced_frames_high) - intrmod_cfg->tx_maxcnt_trigger = - intr_coal->tx_max_coalesced_frames_high; - else - intrmod_cfg->tx_maxcnt_trigger = - LIO_INTRMOD_TXMAXCNT_TRIGGER; - if (intr_coal->tx_max_coalesced_frames_low) - intrmod_cfg->tx_mincnt_trigger = - intr_coal->tx_max_coalesced_frames_low; - else - intrmod_cfg->tx_mincnt_trigger = - LIO_INTRMOD_TXMINCNT_TRIGGER; + if (intrmod_cfg->rx_enable) { + intrmod_cfg->rx_maxcnt_trigger = + intr_coal->rx_max_coalesced_frames_high; + intrmod_cfg->rx_maxtmr_trigger = + intr_coal->rx_coalesce_usecs_high; + intrmod_cfg->rx_mintmr_trigger = + intr_coal->rx_coalesce_usecs_low; + intrmod_cfg->rx_mincnt_trigger = + intr_coal->rx_max_coalesced_frames_low; + } + if (intrmod_cfg->tx_enable) { + intrmod_cfg->tx_maxcnt_trigger = + intr_coal->tx_max_coalesced_frames_high; + intrmod_cfg->tx_mincnt_trigger = + intr_coal->tx_max_coalesced_frames_low; } ret = octnet_set_intrmod_cfg(lio, intrmod_cfg); @@ -1672,7 +1743,9 @@ static int oct_cfg_adaptive_intr(struct lio *lio, struct ethtool_coalesce } static int -oct_cfg_rx_intrcnt(struct lio *lio, struct ethtool_coalesce *intr_coal) +oct_cfg_rx_intrcnt(struct lio *lio, + struct oct_intrmod_cfg *intrmod, + struct ethtool_coalesce *intr_coal) { struct octeon_device *oct = lio->oct_dev; u32 rx_max_coalesced_frames; @@ -1698,7 +1771,7 @@ oct_cfg_rx_intrcnt(struct lio *lio, struct ethtool_coalesce *intr_coal) int q_no; if (!intr_coal->rx_max_coalesced_frames) - rx_max_coalesced_frames = oct->intrmod.rx_frames; + rx_max_coalesced_frames = intrmod->rx_frames; else rx_max_coalesced_frames = intr_coal->rx_max_coalesced_frames; @@ -1709,17 +1782,18 @@ oct_cfg_rx_intrcnt(struct lio *lio, struct ethtool_coalesce *intr_coal) (octeon_read_csr64( oct, CN23XX_SLI_OQ_PKT_INT_LEVELS(q_no)) & (0x3fffff00000000UL)) | - rx_max_coalesced_frames); + (rx_max_coalesced_frames - 1)); /*consider setting resend bit*/ } - oct->intrmod.rx_frames = rx_max_coalesced_frames; + intrmod->rx_frames = rx_max_coalesced_frames; + oct->rx_max_coalesced_frames = rx_max_coalesced_frames; break; } case OCTEON_CN23XX_VF_VID: { int q_no; if (!intr_coal->rx_max_coalesced_frames) - rx_max_coalesced_frames = oct->intrmod.rx_frames; + rx_max_coalesced_frames = intrmod->rx_frames; else rx_max_coalesced_frames = intr_coal->rx_max_coalesced_frames; @@ -1730,9 +1804,10 @@ oct_cfg_rx_intrcnt(struct lio *lio, struct ethtool_coalesce *intr_coal) oct, CN23XX_VF_SLI_OQ_PKT_INT_LEVELS(q_no)) & (0x3fffff00000000UL)) | rx_max_coalesced_frames); - /* consider writing to resend bit here */ + /*consider writing to resend bit here*/ } - oct->intrmod.rx_frames = rx_max_coalesced_frames; + intrmod->rx_frames = rx_max_coalesced_frames; + oct->rx_max_coalesced_frames = rx_max_coalesced_frames; break; } default: @@ -1742,6 +1817,7 @@ oct_cfg_rx_intrcnt(struct lio *lio, struct ethtool_coalesce *intr_coal) } static int oct_cfg_rx_intrtime(struct lio *lio, + struct oct_intrmod_cfg *intrmod, struct ethtool_coalesce *intr_coal) { struct octeon_device *oct = lio->oct_dev; @@ -1772,7 +1848,7 @@ static int oct_cfg_rx_intrtime(struct lio *lio, int q_no; if (!intr_coal->rx_coalesce_usecs) - rx_coalesce_usecs = oct->intrmod.rx_usecs; + rx_coalesce_usecs = intrmod->rx_usecs; else rx_coalesce_usecs = intr_coal->rx_coalesce_usecs; time_threshold = @@ -1781,11 +1857,12 @@ static int oct_cfg_rx_intrtime(struct lio *lio, q_no += oct->sriov_info.pf_srn; octeon_write_csr64(oct, CN23XX_SLI_OQ_PKT_INT_LEVELS(q_no), - (oct->intrmod.rx_frames | - (time_threshold << 32))); + (intrmod->rx_frames | + ((u64)time_threshold << 32))); /*consider writing to resend bit here*/ } - oct->intrmod.rx_usecs = rx_coalesce_usecs; + intrmod->rx_usecs = rx_coalesce_usecs; + oct->rx_coalesce_usecs = rx_coalesce_usecs; break; } case OCTEON_CN23XX_VF_VID: { @@ -1793,7 +1870,7 @@ static int oct_cfg_rx_intrtime(struct lio *lio, int q_no; if (!intr_coal->rx_coalesce_usecs) - rx_coalesce_usecs = oct->intrmod.rx_usecs; + rx_coalesce_usecs = intrmod->rx_usecs; else rx_coalesce_usecs = intr_coal->rx_coalesce_usecs; @@ -1802,11 +1879,12 @@ static int oct_cfg_rx_intrtime(struct lio *lio, for (q_no = 0; q_no < oct->num_oqs; q_no++) { octeon_write_csr64( oct, CN23XX_VF_SLI_OQ_PKT_INT_LEVELS(q_no), - (oct->intrmod.rx_frames | - (time_threshold << 32))); - /* consider setting resend bit */ + (intrmod->rx_frames | + ((u64)time_threshold << 32))); + /*consider setting resend bit*/ } - oct->intrmod.rx_usecs = rx_coalesce_usecs; + intrmod->rx_usecs = rx_coalesce_usecs; + oct->rx_coalesce_usecs = rx_coalesce_usecs; break; } default: @@ -1817,8 +1895,9 @@ static int oct_cfg_rx_intrtime(struct lio *lio, } static int -oct_cfg_tx_intrcnt(struct lio *lio, struct ethtool_coalesce *intr_coal - __attribute__((unused))) +oct_cfg_tx_intrcnt(struct lio *lio, + struct oct_intrmod_cfg *intrmod, + struct ethtool_coalesce *intr_coal) { struct octeon_device *oct = lio->oct_dev; u32 iq_intr_pkt; @@ -1845,12 +1924,13 @@ oct_cfg_tx_intrcnt(struct lio *lio, struct ethtool_coalesce *intr_coal val = readq(inst_cnt_reg); /*clear wmark and count.dont want to write count back*/ val = (val & 0xFFFF000000000000ULL) | - ((u64)iq_intr_pkt + ((u64)(iq_intr_pkt - 1) << CN23XX_PKT_IN_DONE_WMARK_BIT_POS); writeq(val, inst_cnt_reg); /*consider setting resend bit*/ } - oct->intrmod.tx_frames = iq_intr_pkt; + intrmod->tx_frames = iq_intr_pkt; + oct->tx_max_coalesced_frames = iq_intr_pkt; break; } default: @@ -1865,6 +1945,7 @@ static int lio_set_intr_coalesce(struct net_device *netdev, struct lio *lio = GET_LIO(netdev); int ret; struct octeon_device *oct = lio->oct_dev; + struct oct_intrmod_cfg intrmod = {0}; u32 j, q_no; int db_max, db_min; @@ -1883,8 +1964,8 @@ static int lio_set_intr_coalesce(struct net_device *netdev, } else { dev_err(&oct->pci_dev->dev, "LIQUIDIO: Invalid tx-frames:%d. Range is min:%d max:%d\n", - intr_coal->tx_max_coalesced_frames, db_min, - db_max); + intr_coal->tx_max_coalesced_frames, + db_min, db_max); return -EINVAL; } break; @@ -1895,24 +1976,36 @@ static int lio_set_intr_coalesce(struct net_device *netdev, return -EINVAL; } - oct->intrmod.rx_enable = intr_coal->use_adaptive_rx_coalesce ? 1 : 0; - oct->intrmod.tx_enable = intr_coal->use_adaptive_tx_coalesce ? 1 : 0; + intrmod.rx_enable = intr_coal->use_adaptive_rx_coalesce ? 1 : 0; + intrmod.tx_enable = intr_coal->use_adaptive_tx_coalesce ? 1 : 0; + intrmod.rx_frames = CFG_GET_OQ_INTR_PKT(octeon_get_conf(oct)); + intrmod.rx_usecs = CFG_GET_OQ_INTR_TIME(octeon_get_conf(oct)); + intrmod.tx_frames = CFG_GET_IQ_INTR_PKT(octeon_get_conf(oct)); - ret = oct_cfg_adaptive_intr(lio, intr_coal); + ret = oct_cfg_adaptive_intr(lio, &intrmod, intr_coal); if (!intr_coal->use_adaptive_rx_coalesce) { - ret = oct_cfg_rx_intrtime(lio, intr_coal); + ret = oct_cfg_rx_intrtime(lio, &intrmod, intr_coal); if (ret) goto ret_intrmod; - ret = oct_cfg_rx_intrcnt(lio, intr_coal); + ret = oct_cfg_rx_intrcnt(lio, &intrmod, intr_coal); if (ret) goto ret_intrmod; + } else { + oct->rx_coalesce_usecs = + CFG_GET_OQ_INTR_TIME(octeon_get_conf(oct)); + oct->rx_max_coalesced_frames = + CFG_GET_OQ_INTR_PKT(octeon_get_conf(oct)); } + if (!intr_coal->use_adaptive_tx_coalesce) { - ret = oct_cfg_tx_intrcnt(lio, intr_coal); + ret = oct_cfg_tx_intrcnt(lio, &intrmod, intr_coal); if (ret) goto ret_intrmod; + } else { + oct->tx_max_coalesced_frames = + CFG_GET_IQ_INTR_PKT(octeon_get_conf(oct)); } return 0; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index b06be91d9d2e..b22291906fcc 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -4320,7 +4320,6 @@ static int liquidio_enable_sriov(struct pci_dev *dev, int num_vfs) */ static int liquidio_init_nic_module(struct octeon_device *oct) { - struct oct_intrmod_cfg *intrmod_cfg; int i, retval = 0; int num_nic_ports = CFG_GET_NUM_NIC_PORTS(octeon_get_conf(oct)); @@ -4345,22 +4344,6 @@ static int liquidio_init_nic_module(struct octeon_device *oct) liquidio_ptp_init(oct); - /* Initialize interrupt moderation params */ - intrmod_cfg = &((struct octeon_device *)oct)->intrmod; - intrmod_cfg->rx_enable = 1; - intrmod_cfg->check_intrvl = LIO_INTRMOD_CHECK_INTERVAL; - intrmod_cfg->maxpkt_ratethr = LIO_INTRMOD_MAXPKT_RATETHR; - intrmod_cfg->minpkt_ratethr = LIO_INTRMOD_MINPKT_RATETHR; - intrmod_cfg->rx_maxcnt_trigger = LIO_INTRMOD_RXMAXCNT_TRIGGER; - intrmod_cfg->rx_maxtmr_trigger = LIO_INTRMOD_RXMAXTMR_TRIGGER; - intrmod_cfg->rx_mintmr_trigger = LIO_INTRMOD_RXMINTMR_TRIGGER; - intrmod_cfg->rx_mincnt_trigger = LIO_INTRMOD_RXMINCNT_TRIGGER; - intrmod_cfg->tx_enable = 1; - intrmod_cfg->tx_maxcnt_trigger = LIO_INTRMOD_TXMAXCNT_TRIGGER; - intrmod_cfg->tx_mincnt_trigger = LIO_INTRMOD_TXMINCNT_TRIGGER; - intrmod_cfg->rx_frames = CFG_GET_OQ_INTR_PKT(octeon_get_conf(oct)); - intrmod_cfg->rx_usecs = CFG_GET_OQ_INTR_TIME(octeon_get_conf(oct)); - intrmod_cfg->tx_frames = CFG_GET_IQ_INTR_PKT(octeon_get_conf(oct)); dev_dbg(&oct->pci_dev->dev, "Network interfaces ready\n"); return retval; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 68794fa5d322..89fd81abab9a 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -3057,7 +3057,6 @@ setup_nic_wait_intr: */ static int liquidio_init_nic_module(struct octeon_device *oct) { - struct oct_intrmod_cfg *intrmod_cfg; int num_nic_ports = 1; int i, retval = 0; @@ -3079,22 +3078,6 @@ static int liquidio_init_nic_module(struct octeon_device *oct) goto octnet_init_failure; } - /* Initialize interrupt moderation params */ - intrmod_cfg = &((struct octeon_device *)oct)->intrmod; - intrmod_cfg->rx_enable = 1; - intrmod_cfg->check_intrvl = LIO_INTRMOD_CHECK_INTERVAL; - intrmod_cfg->maxpkt_ratethr = LIO_INTRMOD_MAXPKT_RATETHR; - intrmod_cfg->minpkt_ratethr = LIO_INTRMOD_MINPKT_RATETHR; - intrmod_cfg->rx_maxcnt_trigger = LIO_INTRMOD_RXMAXCNT_TRIGGER; - intrmod_cfg->rx_maxtmr_trigger = LIO_INTRMOD_RXMAXTMR_TRIGGER; - intrmod_cfg->rx_mintmr_trigger = LIO_INTRMOD_RXMINTMR_TRIGGER; - intrmod_cfg->rx_mincnt_trigger = LIO_INTRMOD_RXMINCNT_TRIGGER; - intrmod_cfg->tx_enable = 1; - intrmod_cfg->tx_maxcnt_trigger = LIO_INTRMOD_TXMAXCNT_TRIGGER; - intrmod_cfg->tx_mincnt_trigger = LIO_INTRMOD_TXMINCNT_TRIGGER; - intrmod_cfg->rx_frames = CFG_GET_OQ_INTR_PKT(octeon_get_conf(oct)); - intrmod_cfg->rx_usecs = CFG_GET_OQ_INTR_TIME(octeon_get_conf(oct)); - intrmod_cfg->tx_frames = CFG_GET_IQ_INTR_PKT(octeon_get_conf(oct)); dev_dbg(&oct->pci_dev->dev, "Network interfaces ready\n"); return retval; diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index 17c9aff753d1..8ea2323d8d67 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -27,7 +27,7 @@ #define LIQUIDIO_PACKAGE "" #define LIQUIDIO_BASE_MAJOR_VERSION 1 -#define LIQUIDIO_BASE_MINOR_VERSION 4 +#define LIQUIDIO_BASE_MINOR_VERSION 5 #define LIQUIDIO_BASE_MICRO_VERSION 1 #define LIQUIDIO_BASE_VERSION __stringify(LIQUIDIO_BASE_MAJOR_VERSION) "." \ __stringify(LIQUIDIO_BASE_MINOR_VERSION) @@ -83,6 +83,7 @@ enum octeon_tag_type { #define OPCODE_NIC_INTRMOD_CFG 0x08 #define OPCODE_NIC_IF_CFG 0x09 #define OPCODE_NIC_VF_DRV_NOTICE 0x0A +#define OPCODE_NIC_INTRMOD_PARAMS 0x0B #define VF_DRV_LOADED 1 #define VF_DRV_REMOVED -1 #define VF_DRV_MACADDR_CHANGED 2 @@ -851,29 +852,6 @@ struct oct_mdio_cmd { #define OCT_LINK_STATS_SIZE (sizeof(struct oct_link_stats)) -/* intrmod: max. packet rate threshold */ -#define LIO_INTRMOD_MAXPKT_RATETHR 196608 -/* intrmod: min. packet rate threshold */ -#define LIO_INTRMOD_MINPKT_RATETHR 9216 -/* intrmod: max. packets to trigger interrupt */ -#define LIO_INTRMOD_RXMAXCNT_TRIGGER 384 -/* intrmod: min. packets to trigger interrupt */ -#define LIO_INTRMOD_RXMINCNT_TRIGGER 0 -/* intrmod: max. time to trigger interrupt */ -#define LIO_INTRMOD_RXMAXTMR_TRIGGER 128 -/* 66xx:intrmod: min. time to trigger interrupt - * (value of 1 is optimum for TCP_RR) - */ -#define LIO_INTRMOD_RXMINTMR_TRIGGER 1 - -/* intrmod: max. packets to trigger interrupt */ -#define LIO_INTRMOD_TXMAXCNT_TRIGGER 64 -/* intrmod: min. packets to trigger interrupt */ -#define LIO_INTRMOD_TXMINCNT_TRIGGER 0 - -/* intrmod: poll interval in seconds */ -#define LIO_INTRMOD_CHECK_INTERVAL 1 - struct oct_intrmod_cfg { u64 rx_enable; u64 tx_enable; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index 8c5d33e53cfa..dab35bfa4612 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -453,9 +453,6 @@ struct octeon_device { /** List of dispatch functions */ struct octeon_dispatch_list dispatch; - /* Interrupt Moderation */ - struct oct_intrmod_cfg intrmod; - u32 int_status; u64 droq_intr; @@ -541,6 +538,10 @@ struct octeon_device { u32 priv_flags; void *watchdog_task; + + u32 rx_coalesce_usecs; + u32 rx_max_coalesced_frames; + u32 tx_max_coalesced_frames; }; #define OCT_DRV_ONLINE 1 @@ -554,12 +555,6 @@ struct octeon_device { #define CHIP_CONF(oct, TYPE) \ (((struct octeon_ ## TYPE *)((oct)->chip))->conf) -struct oct_intrmod_cmd { - struct octeon_device *oct_dev; - struct octeon_soft_command *sc; - struct oct_intrmod_cfg *cfg; -}; - /*------------------ Function Prototypes ----------------------*/ /** Initialize device list memory */ -- cgit From 983701eb0676db96c604db8a3f7ae936a7029ff5 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 28 Mar 2017 14:28:01 -0700 Subject: rtnetlink: Add RTM_DELNETCONF Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/uapi/linux/rtnetlink.h | 2 ++ security/selinux/nlmsgtab.c | 1 + 2 files changed, 3 insertions(+) diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index 3dd72aee4d32..cce061382e40 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -122,6 +122,8 @@ enum { RTM_NEWNETCONF = 80, #define RTM_NEWNETCONF RTM_NEWNETCONF + RTM_DELNETCONF, +#define RTM_DELNETCONF RTM_DELNETCONF RTM_GETNETCONF = 82, #define RTM_GETNETCONF RTM_GETNETCONF diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 2ca9cde939d4..8e67bb4c9cab 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -69,6 +69,7 @@ static struct nlmsg_perm nlmsg_route_perms[] = { RTM_GETDCB, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_SETDCB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, { RTM_NEWNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, + { RTM_DELNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, { RTM_GETNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_READ }, { RTM_NEWMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, { RTM_DELMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, -- cgit From 3b0228656dcb07a1c9fc81e8516475c2d7c4300e Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 28 Mar 2017 14:28:02 -0700 Subject: net: devinet: Refactor inet_netconf_notify_devconf to take event Refactor inet_netconf_notify_devconf to take the event as an input arg. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/linux/inetdevice.h | 4 ++-- net/ipv4/devinet.c | 32 ++++++++++++++++++++------------ net/ipv4/ipmr.c | 12 +++++++----- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index ee971f335a8b..a2e9d6ea1349 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -153,8 +153,8 @@ struct in_ifaddr { int register_inetaddr_notifier(struct notifier_block *nb); int unregister_inetaddr_notifier(struct notifier_block *nb); -void inet_netconf_notify_devconf(struct net *net, int type, int ifindex, - struct ipv4_devconf *devconf); +void inet_netconf_notify_devconf(struct net *net, int event, int type, + int ifindex, struct ipv4_devconf *devconf); struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref); static inline struct net_device *ip_dev_find(struct net *net, __be32 addr) diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 927f1d4b8c80..fd3218cd1870 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1831,8 +1831,8 @@ nla_put_failure: return -EMSGSIZE; } -void inet_netconf_notify_devconf(struct net *net, int type, int ifindex, - struct ipv4_devconf *devconf) +void inet_netconf_notify_devconf(struct net *net, int event, int type, + int ifindex, struct ipv4_devconf *devconf) { struct sk_buff *skb; int err = -ENOBUFS; @@ -1842,7 +1842,7 @@ void inet_netconf_notify_devconf(struct net *net, int type, int ifindex, goto errout; err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, - RTM_NEWNETCONF, 0, type); + event, 0, type); if (err < 0) { /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ WARN_ON(err == -EMSGSIZE); @@ -2021,10 +2021,12 @@ static void inet_forward_change(struct net *net) IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; IPV4_DEVCONF_DFLT(net, FORWARDING) = on; - inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_FORWARDING, NETCONFA_IFINDEX_ALL, net->ipv4.devconf_all); - inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_FORWARDING, NETCONFA_IFINDEX_DEFAULT, net->ipv4.devconf_dflt); @@ -2037,7 +2039,8 @@ static void inet_forward_change(struct net *net) in_dev = __in_dev_get_rtnl(dev); if (in_dev) { IN_DEV_CONF_SET(in_dev, FORWARDING, on); - inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_FORWARDING, dev->ifindex, &in_dev->cnf); } } @@ -2082,19 +2085,22 @@ static int devinet_conf_proc(struct ctl_table *ctl, int write, if (i == IPV4_DEVCONF_RP_FILTER - 1 && new_value != old_value) { ifindex = devinet_conf_ifindex(net, cnf); - inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER, + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_RP_FILTER, ifindex, cnf); } if (i == IPV4_DEVCONF_PROXY_ARP - 1 && new_value != old_value) { ifindex = devinet_conf_ifindex(net, cnf); - inet_netconf_notify_devconf(net, NETCONFA_PROXY_NEIGH, + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_PROXY_NEIGH, ifindex, cnf); } if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 && new_value != old_value) { ifindex = devinet_conf_ifindex(net, cnf); - inet_netconf_notify_devconf(net, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, ifindex, cnf); } } @@ -2129,7 +2135,7 @@ static int devinet_sysctl_forward(struct ctl_table *ctl, int write, container_of(cnf, struct in_device, cnf); if (*valp) dev_disable_lro(idev->dev); - inet_netconf_notify_devconf(net, + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_FORWARDING, idev->dev->ifindex, cnf); @@ -2137,7 +2143,8 @@ static int devinet_sysctl_forward(struct ctl_table *ctl, int write, rtnl_unlock(); rt_cache_flush(net); } else - inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_FORWARDING, NETCONFA_IFINDEX_DEFAULT, net->ipv4.devconf_dflt); } @@ -2259,7 +2266,8 @@ static int __devinet_sysctl_register(struct net *net, char *dev_name, p->sysctl = t; - inet_netconf_notify_devconf(net, NETCONFA_ALL, ifindex, p); + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, + ifindex, p); return 0; free: diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index c0317c940bcd..5bca64fc71b7 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -631,7 +631,7 @@ static int vif_delete(struct mr_table *mrt, int vifi, int notify, in_dev = __in_dev_get_rtnl(dev); if (in_dev) { IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--; - inet_netconf_notify_devconf(dev_net(dev), + inet_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, NETCONFA_MC_FORWARDING, dev->ifindex, &in_dev->cnf); ip_rt_multicast_event(in_dev); @@ -820,8 +820,8 @@ static int vif_add(struct net *net, struct mr_table *mrt, return -EADDRNOTAVAIL; } IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++; - inet_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING, dev->ifindex, - &in_dev->cnf); + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_MC_FORWARDING, + dev->ifindex, &in_dev->cnf); ip_rt_multicast_event(in_dev); /* Fill in the VIF structures */ @@ -1282,7 +1282,8 @@ static void mrtsock_destruct(struct sock *sk) ipmr_for_each_table(mrt, net) { if (sk == rtnl_dereference(mrt->mroute_sk)) { IPV4_DEVCONF_ALL(net, MC_FORWARDING)--; - inet_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING, + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_MC_FORWARDING, NETCONFA_IFINDEX_ALL, net->ipv4.devconf_all); RCU_INIT_POINTER(mrt->mroute_sk, NULL); @@ -1344,7 +1345,8 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, if (ret == 0) { rcu_assign_pointer(mrt->mroute_sk, sk); IPV4_DEVCONF_ALL(net, MC_FORWARDING)++; - inet_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING, + inet_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_MC_FORWARDING, NETCONFA_IFINDEX_ALL, net->ipv4.devconf_all); } -- cgit From b5c9641d3d45e58dbcb35090345f863e5ade718d Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 28 Mar 2017 14:28:03 -0700 Subject: net: devinet: Add support for RTM_DELNETCONF Send RTM_DELNETCONF notifications when a device is deleted. The message only needs the device index, so modify inet_netconf_fill_devconf to skip devconf references if it is NULL. Allows a userspace cache to remove entries as devices are deleted. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/devinet.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index fd3218cd1870..6d3602ec640c 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1802,6 +1802,9 @@ static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) goto nla_put_failure; + if (!devconf) + goto out; + if ((all || type == NETCONFA_FORWARDING) && nla_put_s32(skb, NETCONFA_FORWARDING, IPV4_DEVCONF(*devconf, FORWARDING)) < 0) @@ -1823,6 +1826,7 @@ static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0) goto nla_put_failure; +out: nlmsg_end(skb, nlh); return 0; @@ -2276,16 +2280,18 @@ out: return -ENOBUFS; } -static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) +static void __devinet_sysctl_unregister(struct net *net, + struct ipv4_devconf *cnf, int ifindex) { struct devinet_sysctl_table *t = cnf->sysctl; - if (!t) - return; + if (t) { + cnf->sysctl = NULL; + unregister_net_sysctl_table(t->sysctl_header); + kfree(t); + } - cnf->sysctl = NULL; - unregister_net_sysctl_table(t->sysctl_header); - kfree(t); + inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL); } static int devinet_sysctl_register(struct in_device *idev) @@ -2307,7 +2313,9 @@ static int devinet_sysctl_register(struct in_device *idev) static void devinet_sysctl_unregister(struct in_device *idev) { - __devinet_sysctl_unregister(&idev->cnf); + struct net *net = dev_net(idev->dev); + + __devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex); neigh_sysctl_unregister(idev->arp_parms); } @@ -2382,9 +2390,9 @@ static __net_init int devinet_init_net(struct net *net) #ifdef CONFIG_SYSCTL err_reg_ctl: - __devinet_sysctl_unregister(dflt); + __devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT); err_reg_dflt: - __devinet_sysctl_unregister(all); + __devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL); err_reg_all: if (tbl != ctl_forward_entry) kfree(tbl); @@ -2406,8 +2414,10 @@ static __net_exit void devinet_exit_net(struct net *net) tbl = net->ipv4.forw_hdr->ctl_table_arg; unregister_net_sysctl_table(net->ipv4.forw_hdr); - __devinet_sysctl_unregister(net->ipv4.devconf_dflt); - __devinet_sysctl_unregister(net->ipv4.devconf_all); + __devinet_sysctl_unregister(net, net->ipv4.devconf_dflt, + NETCONFA_IFINDEX_DEFAULT); + __devinet_sysctl_unregister(net, net->ipv4.devconf_all, + NETCONFA_IFINDEX_ALL); kfree(tbl); #endif kfree(net->ipv4.devconf_dflt); -- cgit From 85b3daada4cab8cc36888f5d025058bbc8737497 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 28 Mar 2017 14:28:04 -0700 Subject: net: ipv6: Refactor inet6_netconf_notify_devconf to take event Refactor inet6_netconf_notify_devconf to take the event as an input arg. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/net/addrconf.h | 4 ++-- net/ipv6/addrconf.c | 33 ++++++++++++++++++++++----------- net/ipv6/ip6mr.c | 9 +++++---- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 17c6fd84e287..1aeb25dd42a7 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -262,8 +262,8 @@ int register_inet6addr_notifier(struct notifier_block *nb); int unregister_inet6addr_notifier(struct notifier_block *nb); int inet6addr_notifier_call_chain(unsigned long val, void *v); -void inet6_netconf_notify_devconf(struct net *net, int type, int ifindex, - struct ipv6_devconf *devconf); +void inet6_netconf_notify_devconf(struct net *net, int event, int type, + int ifindex, struct ipv6_devconf *devconf); /** * __in6_dev_get - get inet6_dev pointer from netdevice diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index dff5beb26a01..b8442be85e1b 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -575,8 +575,8 @@ nla_put_failure: return -EMSGSIZE; } -void inet6_netconf_notify_devconf(struct net *net, int type, int ifindex, - struct ipv6_devconf *devconf) +void inet6_netconf_notify_devconf(struct net *net, int event, int type, + int ifindex, struct ipv6_devconf *devconf) { struct sk_buff *skb; int err = -ENOBUFS; @@ -586,7 +586,7 @@ void inet6_netconf_notify_devconf(struct net *net, int type, int ifindex, goto errout; err = inet6_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, - RTM_NEWNETCONF, 0, type); + event, 0, type); if (err < 0) { /* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */ WARN_ON(err == -EMSGSIZE); @@ -769,7 +769,8 @@ static void dev_forward_change(struct inet6_dev *idev) else addrconf_leave_anycast(ifa); } - inet6_netconf_notify_devconf(dev_net(dev), NETCONFA_FORWARDING, + inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, + NETCONFA_FORWARDING, dev->ifindex, &idev->cnf); } @@ -804,7 +805,8 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf) if (p == &net->ipv6.devconf_dflt->forwarding) { if ((!newf) ^ (!old)) - inet6_netconf_notify_devconf(net, NETCONFA_FORWARDING, + inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_FORWARDING, NETCONFA_IFINDEX_DEFAULT, net->ipv6.devconf_dflt); rtnl_unlock(); @@ -816,13 +818,15 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf) net->ipv6.devconf_dflt->forwarding = newf; if ((!newf) ^ (!old_dflt)) - inet6_netconf_notify_devconf(net, NETCONFA_FORWARDING, + inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_FORWARDING, NETCONFA_IFINDEX_DEFAULT, net->ipv6.devconf_dflt); addrconf_forward_change(net, newf); if ((!newf) ^ (!old)) - inet6_netconf_notify_devconf(net, NETCONFA_FORWARDING, + inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_FORWARDING, NETCONFA_IFINDEX_ALL, net->ipv6.devconf_all); } else if ((!newf) ^ (!old)) @@ -847,6 +851,7 @@ static void addrconf_linkdown_change(struct net *net, __s32 newf) idev->cnf.ignore_routes_with_linkdown = newf; if (changed) inet6_netconf_notify_devconf(dev_net(dev), + RTM_NEWNETCONF, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, dev->ifindex, &idev->cnf); @@ -869,6 +874,7 @@ static int addrconf_fixup_linkdown(struct ctl_table *table, int *p, int newf) if (p == &net->ipv6.devconf_dflt->ignore_routes_with_linkdown) { if ((!newf) ^ (!old)) inet6_netconf_notify_devconf(net, + RTM_NEWNETCONF, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, NETCONFA_IFINDEX_DEFAULT, net->ipv6.devconf_dflt); @@ -881,6 +887,7 @@ static int addrconf_fixup_linkdown(struct ctl_table *table, int *p, int newf) addrconf_linkdown_change(net, newf); if ((!newf) ^ (!old)) inet6_netconf_notify_devconf(net, + RTM_NEWNETCONF, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, NETCONFA_IFINDEX_ALL, net->ipv6.devconf_all); @@ -5675,17 +5682,20 @@ int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write, return restart_syscall(); if (valp == &net->ipv6.devconf_dflt->proxy_ndp) - inet6_netconf_notify_devconf(net, NETCONFA_PROXY_NEIGH, + inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_PROXY_NEIGH, NETCONFA_IFINDEX_DEFAULT, net->ipv6.devconf_dflt); else if (valp == &net->ipv6.devconf_all->proxy_ndp) - inet6_netconf_notify_devconf(net, NETCONFA_PROXY_NEIGH, + inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_PROXY_NEIGH, NETCONFA_IFINDEX_ALL, net->ipv6.devconf_all); else { struct inet6_dev *idev = ctl->extra1; - inet6_netconf_notify_devconf(net, NETCONFA_PROXY_NEIGH, + inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_PROXY_NEIGH, idev->dev->ifindex, &idev->cnf); } @@ -6348,7 +6358,8 @@ static int __addrconf_sysctl_register(struct net *net, char *dev_name, ifindex = NETCONFA_IFINDEX_DEFAULT; else ifindex = idev->dev->ifindex; - inet6_netconf_notify_devconf(net, NETCONFA_ALL, ifindex, p); + inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, + ifindex, p); return 0; free: diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 6ba6c900ebcf..fb4546e80c82 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -815,7 +815,7 @@ static int mif6_delete(struct mr6_table *mrt, int vifi, struct list_head *head) in6_dev = __in6_dev_get(dev); if (in6_dev) { in6_dev->cnf.mc_forwarding--; - inet6_netconf_notify_devconf(dev_net(dev), + inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, NETCONFA_MC_FORWARDING, dev->ifindex, &in6_dev->cnf); } @@ -974,7 +974,7 @@ static int mif6_add(struct net *net, struct mr6_table *mrt, in6_dev = __in6_dev_get(dev); if (in6_dev) { in6_dev->cnf.mc_forwarding++; - inet6_netconf_notify_devconf(dev_net(dev), + inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, NETCONFA_MC_FORWARDING, dev->ifindex, &in6_dev->cnf); } @@ -1599,7 +1599,8 @@ static int ip6mr_sk_init(struct mr6_table *mrt, struct sock *sk) write_unlock_bh(&mrt_lock); if (!err) - inet6_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING, + inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_MC_FORWARDING, NETCONFA_IFINDEX_ALL, net->ipv6.devconf_all); rtnl_unlock(); @@ -1620,7 +1621,7 @@ int ip6mr_sk_done(struct sock *sk) mrt->mroute6_sk = NULL; net->ipv6.devconf_all->mc_forwarding--; write_unlock_bh(&mrt_lock); - inet6_netconf_notify_devconf(net, + inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_MC_FORWARDING, NETCONFA_IFINDEX_ALL, net->ipv6.devconf_all); -- cgit From 2345217026a093cdfd0f3e193ec9f2c82f22e1c2 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 28 Mar 2017 14:28:05 -0700 Subject: net: ipv6: Add support for RTM_DELNETCONF Send RTM_DELNETCONF notifications when a device is deleted. The message only needs the device index, so modify inet6_netconf_fill_devconf to skip devconf references if it is NULL. Allows a userspace cache to remove entries as devices are deleted. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/ipv6/addrconf.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index b8442be85e1b..67ec87ea5fb6 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -549,6 +549,9 @@ static int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex, if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) goto nla_put_failure; + if (!devconf) + goto out; + if ((all || type == NETCONFA_FORWARDING) && nla_put_s32(skb, NETCONFA_FORWARDING, devconf->forwarding) < 0) goto nla_put_failure; @@ -567,6 +570,7 @@ static int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex, devconf->ignore_routes_with_linkdown) < 0) goto nla_put_failure; +out: nlmsg_end(skb, nlh); return 0; @@ -6368,7 +6372,8 @@ out: return -ENOBUFS; } -static void __addrconf_sysctl_unregister(struct ipv6_devconf *p) +static void __addrconf_sysctl_unregister(struct net *net, + struct ipv6_devconf *p, int ifindex) { struct ctl_table *table; @@ -6379,6 +6384,8 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p) unregister_net_sysctl_table(p->sysctl_header); p->sysctl_header = NULL; kfree(table); + + inet6_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL); } static int addrconf_sysctl_register(struct inet6_dev *idev) @@ -6402,7 +6409,8 @@ static int addrconf_sysctl_register(struct inet6_dev *idev) static void addrconf_sysctl_unregister(struct inet6_dev *idev) { - __addrconf_sysctl_unregister(&idev->cnf); + __addrconf_sysctl_unregister(dev_net(idev->dev), &idev->cnf, + idev->dev->ifindex); neigh_sysctl_unregister(idev->nd_parms); } @@ -6445,7 +6453,7 @@ static int __net_init addrconf_init_net(struct net *net) #ifdef CONFIG_SYSCTL err_reg_dflt: - __addrconf_sysctl_unregister(all); + __addrconf_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL); err_reg_all: kfree(dflt); #endif @@ -6458,8 +6466,10 @@ err_alloc_all: static void __net_exit addrconf_exit_net(struct net *net) { #ifdef CONFIG_SYSCTL - __addrconf_sysctl_unregister(net->ipv6.devconf_dflt); - __addrconf_sysctl_unregister(net->ipv6.devconf_all); + __addrconf_sysctl_unregister(net, net->ipv6.devconf_dflt, + NETCONFA_IFINDEX_DEFAULT); + __addrconf_sysctl_unregister(net, net->ipv6.devconf_all, + NETCONFA_IFINDEX_ALL); #endif kfree(net->ipv6.devconf_dflt); kfree(net->ipv6.devconf_all); -- cgit From 823566aee085ca72bab65e9d9f7deb42faf80746 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 28 Mar 2017 14:28:06 -0700 Subject: net:mpls: Refactor mpls_netconf_notify_devconf to take event Refactor mpls_netconf_notify_devconf to take the event as an input arg. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/mpls/af_mpls.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 74755920c689..313cd0a93a63 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1040,8 +1040,8 @@ static int mpls_netconf_msgsize_devconf(int type) return size; } -static void mpls_netconf_notify_devconf(struct net *net, int type, - struct mpls_dev *mdev) +static void mpls_netconf_notify_devconf(struct net *net, int event, + int type, struct mpls_dev *mdev) { struct sk_buff *skb; int err = -ENOBUFS; @@ -1050,8 +1050,7 @@ static void mpls_netconf_notify_devconf(struct net *net, int type, if (!skb) goto errout; - err = mpls_netconf_fill_devconf(skb, mdev, 0, 0, RTM_NEWNETCONF, - 0, type); + err = mpls_netconf_fill_devconf(skb, mdev, 0, 0, event, 0, type); if (err < 0) { /* -EMSGSIZE implies BUG in mpls_netconf_msgsize_devconf() */ WARN_ON(err == -EMSGSIZE); @@ -1184,9 +1183,8 @@ static int mpls_conf_proc(struct ctl_table *ctl, int write, if (i == offsetof(struct mpls_dev, input_enabled) && val != oval) { - mpls_netconf_notify_devconf(net, - NETCONFA_INPUT, - mdev); + mpls_netconf_notify_devconf(net, RTM_NEWNETCONF, + NETCONFA_INPUT, mdev); } } -- cgit From 1182e4d0b4f8735184aecb27e4c0abd35054237c Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 28 Mar 2017 14:28:07 -0700 Subject: net: mpls: Send netconf messages on device register and unregister Send netconf notifications for MPLS when the device registers and unregisters. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/mpls/af_mpls.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 313cd0a93a63..673f3d990b5c 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1225,10 +1225,11 @@ static int mpls_dev_sysctl_register(struct net_device *dev, snprintf(path, sizeof(path), "net/mpls/conf/%s", dev->name); - mdev->sysctl = register_net_sysctl(dev_net(dev), path, table); + mdev->sysctl = register_net_sysctl(net, path, table); if (!mdev->sysctl) goto free; + mpls_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, mdev); return 0; free: @@ -1237,13 +1238,17 @@ out: return -ENOBUFS; } -static void mpls_dev_sysctl_unregister(struct mpls_dev *mdev) +static void mpls_dev_sysctl_unregister(struct net_device *dev, + struct mpls_dev *mdev) { + struct net *net = dev_net(dev); struct ctl_table *table; table = mdev->sysctl->ctl_table_arg; unregister_net_sysctl_table(mdev->sysctl); kfree(table); + + mpls_netconf_notify_devconf(net, RTM_DELNETCONF, 0, mdev); } static struct mpls_dev *mpls_add_dev(struct net_device *dev) @@ -1269,11 +1274,12 @@ static struct mpls_dev *mpls_add_dev(struct net_device *dev) u64_stats_init(&mpls_stats->syncp); } + mdev->dev = dev; + err = mpls_dev_sysctl_register(dev, mdev); if (err) goto free; - mdev->dev = dev; rcu_assign_pointer(dev->mpls_ptr, mdev); return mdev; @@ -1419,7 +1425,7 @@ static int mpls_dev_notify(struct notifier_block *this, unsigned long event, mpls_ifdown(dev, event); mdev = mpls_dev_get(dev); if (mdev) { - mpls_dev_sysctl_unregister(mdev); + mpls_dev_sysctl_unregister(dev, mdev); RCU_INIT_POINTER(dev->mpls_ptr, NULL); call_rcu(&mdev->rcu, mpls_dev_destroy_rcu); } @@ -1429,7 +1435,7 @@ static int mpls_dev_notify(struct notifier_block *this, unsigned long event, if (mdev) { int err; - mpls_dev_sysctl_unregister(mdev); + mpls_dev_sysctl_unregister(dev, mdev); err = mpls_dev_sysctl_register(dev, mdev); if (err) return notifier_from_errno(err); -- cgit From c6e970a04bdceb7ef1fdbac6be3bd4cd0a0a02bd Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Tue, 28 Mar 2017 23:45:06 +0200 Subject: net: break include loop netdevice.h, dsa.h, devlink.h There is an include loop between netdevice.h, dsa.h, devlink.h because of NETDEV_ALIGN, making it impossible to use devlink structures in dsa.h. Break this loop by taking dsa.h out of netdevice.h, add a forward declaration of dsa_switch_tree and netdev_set_default_ethtool_ops() function, which is what netdevice.h requires. No longer having dsa.h in netdevice.h means the includes in dsa.h no longer get included. This breaks a few other files which depend on these includes. Add these directly in the affected file. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2_cfp.c | 3 ++- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 1 + drivers/net/ethernet/broadcom/bcmsysport.c | 1 + drivers/net/ethernet/freescale/fman/fman_memac.h | 1 + drivers/net/ethernet/hisilicon/hns/hnae.c | 2 +- drivers/net/ieee802154/mrf24j40.c | 1 + drivers/net/phy/smsc.c | 1 + drivers/net/usb/lan78xx.c | 1 + fs/cifs/cifsfs.c | 1 + fs/cifs/connect.c | 1 + fs/cifs/smb2pdu.c | 1 + include/linux/netdevice.h | 12 ++---------- include/net/dsa.h | 9 +++++++++ net/bridge/br_if.c | 1 + net/core/netprio_cgroup.c | 1 + net/dsa/dsa.c | 3 ++- net/dsa/dsa2.c | 3 ++- net/dsa/slave.c | 1 + net/dsa/tag_brcm.c | 1 + net/dsa/tag_dsa.c | 1 + net/dsa/tag_edsa.c | 1 + net/dsa/tag_qca.c | 1 + net/dsa/tag_trailer.c | 1 + net/ipv4/ipconfig.c | 1 + 24 files changed, 36 insertions(+), 14 deletions(-) diff --git a/drivers/net/dsa/bcm_sf2_cfp.c b/drivers/net/dsa/bcm_sf2_cfp.c index 346dd9a1232d..2fb32d67065f 100644 --- a/drivers/net/dsa/bcm_sf2_cfp.c +++ b/drivers/net/dsa/bcm_sf2_cfp.c @@ -10,10 +10,11 @@ */ #include -#include #include #include #include +#include +#include #include #include "bcm_sf2.h" diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 75be2c339a49..55367d05374e 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -16,6 +16,7 @@ #include #include #include +#include #ifndef UINT64_MAX #define UINT64_MAX (u64)(~((u64)0)) diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 61e26c6b26ab..099b374c1b17 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.h b/drivers/net/ethernet/freescale/fman/fman_memac.h index 173d8e0fd716..c4a66469a907 100644 --- a/drivers/net/ethernet/freescale/fman/fman_memac.h +++ b/drivers/net/ethernet/freescale/fman/fman_memac.h @@ -36,6 +36,7 @@ #include "fman_mac.h" #include +#include struct fman_mac *memac_config(struct fman_mac_params *params); int memac_set_promiscuous(struct fman_mac *memac, bool new_val); diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c index b6ed818f78ff..120427a40883 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.c +++ b/drivers/net/ethernet/hisilicon/hns/hnae.c @@ -9,9 +9,9 @@ #include #include +#include #include #include - #include "hnae.h" #define cls_to_ae_dev(dev) container_of(dev, struct hnae_ae_dev, cls_dev) diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index 7b131f8e4093..bd63289c55e8 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index fb32eaf2255d..cef6967b0396 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index d885e0325422..a17e32bf5f92 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 15e1db8738ae..8c91f37ac0eb 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include "cifsfs.h" diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 9ae695ae3ed7..858698dcde3c 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 7446496850a3..fb75fe908225 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include "smb2pdu.h" diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b7365b587818..cc07c3be2705 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -41,7 +41,6 @@ #include #include -#include #ifdef CONFIG_DCB #include #endif @@ -57,6 +56,8 @@ struct netpoll_info; struct device; struct phy_device; +struct dsa_switch_tree; + /* 802.11 specific */ struct wireless_dev; /* 802.15.4 specific */ @@ -2004,15 +2005,6 @@ void dev_net_set(struct net_device *dev, struct net *net) write_pnet(&dev->nd_net, net); } -static inline bool netdev_uses_dsa(struct net_device *dev) -{ -#if IS_ENABLED(CONFIG_NET_DSA) - if (dev->dsa_ptr != NULL) - return dsa_uses_tagged_protocol(dev->dsa_ptr); -#endif - return false; -} - /** * netdev_priv - access network device private data * @dev: network device diff --git a/include/net/dsa.h b/include/net/dsa.h index e42897fd7a96..f80e81912b83 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -468,6 +468,15 @@ static inline bool dsa_uses_tagged_protocol(struct dsa_switch_tree *dst) return dst->rcv != NULL; } +static inline bool netdev_uses_dsa(struct net_device *dev) +{ +#if IS_ENABLED(CONFIG_NET_DSA) + if (dev->dsa_ptr != NULL) + return dsa_uses_tagged_protocol(dev->dsa_ptr); +#endif + return false; +} + struct dsa_switch *dsa_switch_alloc(struct device *dev, size_t n); void dsa_unregister_switch(struct dsa_switch *ds); int dsa_register_switch(struct dsa_switch *ds, struct device *dev); diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 8ac1770aa222..6eb52d422dd9 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index 0f9275ee5595..1c4810919a0a 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -11,6 +11,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index b6d4f6a23f06..95d1a756202c 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -14,15 +14,16 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include #include +#include #include "dsa_priv.h" static struct sk_buff *dsa_slave_notag_xmit(struct sk_buff *skb, diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 737be6470c7f..d039c8d7adfd 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -13,11 +13,12 @@ #include #include #include +#include #include #include -#include #include #include +#include #include "dsa_priv.h" static LIST_HEAD(dsa_switch_trees); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 78128acfbf63..7693182df81e 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index 5d925b6b2bb1..e2ed6cf68261 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "dsa_priv.h" /* This tag length is 4 bytes, older ones were 6 bytes, we do not diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index 72579ceea381..e42ba906100c 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "dsa_priv.h" #define DSA_HLEN 4 diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c index 648c051817a1..6a9b7a9e4e15 100644 --- a/net/dsa/tag_edsa.c +++ b/net/dsa/tag_edsa.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "dsa_priv.h" #define DSA_HLEN 4 diff --git a/net/dsa/tag_qca.c b/net/dsa/tag_qca.c index 30240f343aea..4e0dad759d04 100644 --- a/net/dsa/tag_qca.c +++ b/net/dsa/tag_qca.c @@ -12,6 +12,7 @@ */ #include +#include #include "dsa_priv.h" #define QCA_HDR_LEN 2 diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c index 26f977176978..74c948512550 100644 --- a/net/dsa/tag_trailer.c +++ b/net/dsa/tag_trailer.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "dsa_priv.h" static struct sk_buff *trailer_xmit(struct sk_buff *skb, struct net_device *dev) diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index fd9f34bbd740..9def8ed31c76 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include -- cgit From 96567d5dacf47fd628bc3115c8a7d81866674cd3 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Tue, 28 Mar 2017 23:45:07 +0200 Subject: net: dsa: dsa2: Add basic support of devlink Register the switch and its ports with devlink. Signed-off-by: Andrew Lunn Reviewed-by: Florian Fainelli Tested-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 5 +++++ net/dsa/dsa2.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index f80e81912b83..951b5e49e899 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -19,6 +19,7 @@ #include #include #include +#include struct tc_action; struct phy_device; @@ -182,6 +183,7 @@ struct dsa_port { unsigned int ageing_time; u8 stp_state; struct net_device *bridge_dev; + struct devlink_port devlink_port; }; struct dsa_switch { @@ -237,6 +239,9 @@ struct dsa_switch { unsigned int ageing_time_min; unsigned int ageing_time_max; + /* devlink used to represent this switch device */ + struct devlink *devlink; + /* Dynamically allocated ports, keep last */ size_t num_ports; struct dsa_port ports[]; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index d039c8d7adfd..033b3bfb63dc 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -24,6 +24,9 @@ static LIST_HEAD(dsa_switch_trees); static DEFINE_MUTEX(dsa2_mutex); +static const struct devlink_ops dsa_devlink_ops = { +}; + static struct dsa_switch_tree *dsa_get_dst(u32 tree) { struct dsa_switch_tree *dst; @@ -223,12 +226,18 @@ static int dsa_dsa_port_apply(struct dsa_port *port, u32 index, return err; } - return 0; + memset(&ds->ports[index].devlink_port, 0, + sizeof(ds->ports[index].devlink_port)); + + return devlink_port_register(ds->devlink, + &ds->ports[index].devlink_port, + index); } static void dsa_dsa_port_unapply(struct dsa_port *port, u32 index, struct dsa_switch *ds) { + devlink_port_unregister(&ds->ports[index].devlink_port); dsa_cpu_dsa_destroy(port); } @@ -246,12 +255,17 @@ static int dsa_cpu_port_apply(struct dsa_port *port, u32 index, ds->cpu_port_mask |= BIT(index); - return 0; + memset(&ds->ports[index].devlink_port, 0, + sizeof(ds->ports[index].devlink_port)); + err = devlink_port_register(ds->devlink, &ds->ports[index].devlink_port, + index); + return err; } static void dsa_cpu_port_unapply(struct dsa_port *port, u32 index, struct dsa_switch *ds) { + devlink_port_unregister(&ds->ports[index].devlink_port); dsa_cpu_dsa_destroy(port); ds->cpu_port_mask &= ~BIT(index); @@ -276,12 +290,23 @@ static int dsa_user_port_apply(struct dsa_port *port, u32 index, return err; } + memset(&ds->ports[index].devlink_port, 0, + sizeof(ds->ports[index].devlink_port)); + err = devlink_port_register(ds->devlink, &ds->ports[index].devlink_port, + index); + if (err) + return err; + + devlink_port_type_eth_set(&ds->ports[index].devlink_port, + ds->ports[index].netdev); + return 0; } static void dsa_user_port_unapply(struct dsa_port *port, u32 index, struct dsa_switch *ds) { + devlink_port_unregister(&ds->ports[index].devlink_port); if (ds->ports[index].netdev) { dsa_slave_destroy(ds->ports[index].netdev); ds->ports[index].netdev = NULL; @@ -302,6 +327,17 @@ static int dsa_ds_apply(struct dsa_switch_tree *dst, struct dsa_switch *ds) */ ds->phys_mii_mask = ds->enabled_port_mask; + /* Add the switch to devlink before calling setup, so that setup can + * add dpipe tables + */ + ds->devlink = devlink_alloc(&dsa_devlink_ops, 0); + if (!ds->devlink) + return -ENOMEM; + + err = devlink_register(ds->devlink, ds->dev); + if (err) + return err; + err = ds->ops->setup(ds); if (err < 0) return err; @@ -382,6 +418,13 @@ static void dsa_ds_unapply(struct dsa_switch_tree *dst, struct dsa_switch *ds) mdiobus_unregister(ds->slave_mii_bus); dsa_switch_unregister_notifier(ds); + + if (ds->devlink) { + devlink_unregister(ds->devlink); + devlink_free(ds->devlink); + ds->devlink = NULL; + } + } static int dsa_dst_apply(struct dsa_switch_tree *dst) -- cgit From ed92a9b5d4aaea4a4346db3ab520e8a631f734fd Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Thu, 16 Mar 2017 10:57:18 +0900 Subject: mac80211: mesh: drop new node with weak power On some practical cases, it is useful to drop new node in the distance. Because mesh metric is calculated with hop count and without RSSI information, a node far from local peer and near to destination node could be used as best path. For example, the nodes are located in linear. Distance of 0 - 1 and 1 - 2 and 2 - 3 is 20meters. 0 to 3 signal is very weak. 0 --- 1 --- 2 --- 3 Though most robust path from 0 to 3 is 0 -> 1 -> 2 -> 3, unfortunately, node 0 could recognize node 3 as neighbor. Then node 3 could be next of node 0. This patch aims to avoid such a case. [Johannes:] Dropping the node entirely isn't ideal, but at least with encryption there will be a limit on # of keys the hardware can deal with, and there might also be a limit on the number of stations it supports. Signed-off-by: Masashi Honma Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 6e7b6a07b7d5..281d834c7548 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1100,8 +1100,14 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) return; - if (mesh_matches_local(sdata, &elems)) - mesh_neighbour_update(sdata, mgmt->sa, &elems); + if (mesh_matches_local(sdata, &elems)) { + mpl_dbg(sdata, "rssi_threshold=%d,rx_status->signal=%d\n", + sdata->u.mesh.mshcfg.rssi_threshold, rx_status->signal); + if (!sdata->u.mesh.user_mpm || + sdata->u.mesh.mshcfg.rssi_threshold == 0 || + sdata->u.mesh.mshcfg.rssi_threshold < rx_status->signal) + mesh_neighbour_update(sdata, mgmt->sa, &elems); + } if (ifmsh->sync_ops) ifmsh->sync_ops->rx_bcn_presp(sdata, -- cgit From 667a2e6bfeafa7ce202c22c702fea30ca7741c21 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Thu, 23 Mar 2017 16:26:16 -0700 Subject: mac80211-hwsim: remove dmesg spam about get-survey. This message just fills up dmesg and/or kernel logs and does not provide any useful information. Signed-off-by: Ben Greear Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 67fc91dfcecd..4e58513d24e8 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1823,8 +1823,6 @@ static int mac80211_hwsim_get_survey(struct ieee80211_hw *hw, int idx, { struct mac80211_hwsim_data *hwsim = hw->priv; - wiphy_debug(hw->wiphy, "%s (idx=%d)\n", __func__, idx); - if (idx < 0 || idx >= ARRAY_SIZE(hwsim->survey_data)) return -ENOENT; -- cgit From b1cb07db6e2b3c982ec858b06d42d24c7e267fdc Mon Sep 17 00:00:00 2001 From: Preethi Banala Date: Fri, 10 Mar 2017 12:22:00 -0800 Subject: i40evf: enforce descriptor write-back mechanism for VF The current driver mode is to use a write-back mechanism for the head register which indicates transmit completions. The VF driver needs to be able to work on hardware that exclusively uses descriptor write-back, so change the default driver mode of operation to descriptor write-back for VF. In our analysis, performance wasn't significantly different with either write-back method. Change-ID: Ia92e4ec77c2df8dc4515c71d53746d57d77759af Signed-off-by: Preethi Banala Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 64 +++------------------- drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 14 ----- .../net/ethernet/intel/i40evf/i40evf_virtchnl.c | 4 -- 3 files changed, 7 insertions(+), 75 deletions(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 8915c5598d20..f1a99a8dc7ea 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -137,10 +137,7 @@ u32 i40evf_get_tx_pending(struct i40e_ring *ring, bool in_sw) { u32 head, tail; - if (!in_sw) - head = i40e_get_head(ring); - else - head = ring->next_to_clean; + head = ring->next_to_clean; tail = readl(ring->tail); if (head != tail) @@ -165,7 +162,6 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, { u16 i = tx_ring->next_to_clean; struct i40e_tx_buffer *tx_buf; - struct i40e_tx_desc *tx_head; struct i40e_tx_desc *tx_desc; unsigned int total_bytes = 0, total_packets = 0; unsigned int budget = vsi->work_limit; @@ -174,8 +170,6 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, tx_desc = I40E_TX_DESC(tx_ring, i); i -= tx_ring->count; - tx_head = I40E_TX_DESC(tx_ring, i40e_get_head(tx_ring)); - do { struct i40e_tx_desc *eop_desc = tx_buf->next_to_watch; @@ -186,8 +180,9 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, /* prevent any other reads prior to eop_desc */ read_barrier_depends(); - /* we have caught up to head, no work left to do */ - if (tx_head == tx_desc) + /* if the descriptor isn't done, no work yet to do */ + if (!(eop_desc->cmd_type_offset_bsz & + cpu_to_le64(I40E_TX_DESC_DTYPE_DESC_DONE))) break; /* clear next_to_watch to prevent false hangs */ @@ -464,10 +459,6 @@ int i40evf_setup_tx_descriptors(struct i40e_ring *tx_ring) /* round up to nearest 4K */ tx_ring->size = tx_ring->count * sizeof(struct i40e_tx_desc); - /* add u32 for head writeback, align after this takes care of - * guaranteeing this is at least one cache line in size - */ - tx_ring->size += sizeof(u32); tx_ring->size = ALIGN(tx_ring->size, 4096); tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size, &tx_ring->dma, GFP_KERNEL); @@ -2012,7 +2003,6 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, u16 i = tx_ring->next_to_use; u32 td_tag = 0; dma_addr_t dma; - u16 desc_count = 1; if (tx_flags & I40E_TX_FLAGS_HW_VLAN) { td_cmd |= I40E_TX_DESC_CMD_IL2TAG1; @@ -2048,7 +2038,6 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_desc++; i++; - desc_count++; if (i == tx_ring->count) { tx_desc = I40E_TX_DESC(tx_ring, 0); @@ -2070,7 +2059,6 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_desc++; i++; - desc_count++; if (i == tx_ring->count) { tx_desc = I40E_TX_DESC(tx_ring, 0); @@ -2096,46 +2084,8 @@ static inline void i40evf_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, i40e_maybe_stop_tx(tx_ring, DESC_NEEDED); - /* write last descriptor with EOP bit */ - td_cmd |= I40E_TX_DESC_CMD_EOP; - - /* We can OR these values together as they both are checked against - * 4 below and at this point desc_count will be used as a boolean value - * after this if/else block. - */ - desc_count |= ++tx_ring->packet_stride; - - /* Algorithm to optimize tail and RS bit setting: - * if queue is stopped - * mark RS bit - * reset packet counter - * else if xmit_more is supported and is true - * advance packet counter to 4 - * reset desc_count to 0 - * - * if desc_count >= 4 - * mark RS bit - * reset packet counter - * if desc_count > 0 - * update tail - * - * Note: If there are less than 4 descriptors - * pending and interrupts were disabled the service task will - * trigger a force WB. - */ - if (netif_xmit_stopped(txring_txq(tx_ring))) { - goto do_rs; - } else if (skb->xmit_more) { - /* set stride to arm on next packet and reset desc_count */ - tx_ring->packet_stride = WB_STRIDE; - desc_count = 0; - } else if (desc_count >= WB_STRIDE) { -do_rs: - /* write last descriptor with RS bit set */ - td_cmd |= I40E_TX_DESC_CMD_RS; - tx_ring->packet_stride = 0; - } - + /* write last descriptor with RS and EOP bits */ + td_cmd |= I40E_TXD_CMD; tx_desc->cmd_type_offset_bsz = build_ctob(td_cmd, td_offset, size, td_tag); @@ -2151,7 +2101,7 @@ do_rs: first->next_to_watch = tx_desc; /* notify HW of packet */ - if (desc_count) { + if (netif_xmit_stopped(txring_txq(tx_ring)) || !skb->xmit_more) { writel(i, tx_ring->tail); /* we need this if more than one processor can write to our tail diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index fc959f964919..aba40edb0e2e 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -392,20 +392,6 @@ u32 i40evf_get_tx_pending(struct i40e_ring *ring, bool in_sw); int __i40evf_maybe_stop_tx(struct i40e_ring *tx_ring, int size); bool __i40evf_chk_linearize(struct sk_buff *skb); -/** - * i40e_get_head - Retrieve head from head writeback - * @tx_ring: Tx ring to fetch head of - * - * Returns value of Tx ring head based on value stored - * in head write-back location - **/ -static inline u32 i40e_get_head(struct i40e_ring *tx_ring) -{ - void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count; - - return le32_to_cpu(*(volatile __le32 *)head); -} - /** * i40e_xmit_descriptor_count - calculate number of Tx descriptors needed * @skb: send buffer diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index a2a7354426a3..4bc2488bf709 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -260,10 +260,6 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter) vqpi->txq.queue_id = i; vqpi->txq.ring_len = adapter->tx_rings[i].count; vqpi->txq.dma_ring_addr = adapter->tx_rings[i].dma; - vqpi->txq.headwb_enabled = 1; - vqpi->txq.dma_headwb_addr = vqpi->txq.dma_ring_addr + - (vqpi->txq.ring_len * sizeof(struct i40e_tx_desc)); - vqpi->rxq.vsi_id = vqci->vsi_id; vqpi->rxq.queue_id = i; vqpi->rxq.ring_len = adapter->rx_rings[i].count; -- cgit From aca955d831a644dc1dc22b60b30ff669567580f9 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 10 Mar 2017 12:22:01 -0800 Subject: i40e: Clean up handling of private flags This patch cleans up and addresses several issues in the way that i40e handles private flags. Previously the code was choosing fixed bits and trying to match them up with strings in a somewhat haphazard way. This resulted in the possibility for adding a new bit and causing a mismatch as the private flags are linear bits starting at 0, and the private flags in the driver were split up over a group specific to the PF and a group that was global. What this change does is define an array of structs used to represent the private flags. Contained within the structs are the bits necessary to know which flags to set and/or clear depending on the state of the bit. By doing this we can add new bits in the future with minimal overhead and avoid creating possible mis-matches should we need to remove a flag based on compile options. Change-ID: Ia3214ab04f0ab2f70354ac0997a135f1d01b0acd Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 8 -- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 189 +++++++++++++++---------- 2 files changed, 112 insertions(+), 85 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index d7e84f99eb2d..f506e994861b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -91,14 +91,6 @@ #define I40E_QUEUE_WAIT_RETRY_LIMIT 10 #define I40E_INT_NAME_STR_LEN (IFNAMSIZ + 16) -/* Ethtool Private Flags */ -#define I40E_PRIV_FLAGS_MFP_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_PRIV_FLAGS_HW_ATR_EVICT BIT(4) -#define I40E_PRIV_FLAGS_TRUE_PROMISC_SUPPORT BIT(5) - #define I40E_NVM_VERSION_LO_SHIFT 0 #define I40E_NVM_VERSION_LO_MASK (0xff << I40E_NVM_VERSION_LO_SHIFT) #define I40E_NVM_VERSION_HI_SHIFT 12 diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index c8c566a0a6c3..9b2e9cef56a4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -207,22 +207,36 @@ static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = { #define I40E_TEST_LEN (sizeof(i40e_gstrings_test) / ETH_GSTRING_LEN) -static const char i40e_priv_flags_strings[][ETH_GSTRING_LEN] = { - "MFP", - "LinkPolling", - "flow-director-atr", - "veb-stats", - "hw-atr-eviction", +struct i40e_priv_flags { + char flag_string[ETH_GSTRING_LEN]; + u64 flag; + bool read_only; }; -#define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_priv_flags_strings) +#define I40E_PRIV_FLAG(_name, _flag, _read_only) { \ + .flag_string = _name, \ + .flag = _flag, \ + .read_only = _read_only, \ +} + +static const struct i40e_priv_flags i40e_gstrings_priv_flags[] = { + /* NOTE: MFP setting cannot be changed */ + I40E_PRIV_FLAG("MFP", I40E_FLAG_MFP_ENABLED, 1), + I40E_PRIV_FLAG("LinkPolling", I40E_FLAG_LINK_POLLING_ENABLED, 0), + I40E_PRIV_FLAG("flow-director-atr", I40E_FLAG_FD_ATR_ENABLED, 0), + I40E_PRIV_FLAG("veb-stats", I40E_FLAG_VEB_STATS_ENABLED, 0), + I40E_PRIV_FLAG("hw-atr-eviction", I40E_FLAG_HW_ATR_EVICT_CAPABLE, 0), +}; + +#define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_gstrings_priv_flags) /* Private flags with a global effect, restricted to PF 0 */ -static const char i40e_gl_priv_flags_strings[][ETH_GSTRING_LEN] = { - "vf-true-promisc-support", +static const struct i40e_priv_flags i40e_gl_gstrings_priv_flags[] = { + I40E_PRIV_FLAG("vf-true-promisc-support", + I40E_FLAG_TRUE_PROMISC_SUPPORT, 0), }; -#define I40E_GL_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_gl_priv_flags_strings) +#define I40E_GL_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_gl_gstrings_priv_flags) /** * i40e_partition_setting_complaint - generic complaint for MFP restriction @@ -1660,12 +1674,18 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset, /* BUG_ON(p - data != I40E_STATS_LEN * ETH_GSTRING_LEN); */ break; case ETH_SS_PRIV_FLAGS: - memcpy(data, i40e_priv_flags_strings, - I40E_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN); - data += I40E_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN; - if (pf->hw.pf_id == 0) - memcpy(data, i40e_gl_priv_flags_strings, - I40E_GL_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN); + for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) { + snprintf(p, ETH_GSTRING_LEN, "%s", + i40e_gstrings_priv_flags[i].flag_string); + p += ETH_GSTRING_LEN; + } + if (pf->hw.pf_id != 0) + break; + for (i = 0; i < I40E_GL_PRIV_FLAGS_STR_LEN; i++) { + snprintf(p, ETH_GSTRING_LEN, "%s", + i40e_gl_gstrings_priv_flags[i].flag_string); + p += ETH_GSTRING_LEN; + } break; default: break; @@ -3952,7 +3972,7 @@ static int i40e_set_rxfh(struct net_device *netdev, const u32 *indir, * @dev: network interface device structure * * The get string set count and the string set should be matched for each - * flag returned. Add new strings for each flag to the i40e_priv_flags_strings + * flag returned. Add new strings for each flag to the i40e_gstrings_priv_flags * array. * * Returns a u32 bitmap of flags. @@ -3962,19 +3982,27 @@ static u32 i40e_get_priv_flags(struct net_device *dev) struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - u32 ret_flags = 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; - ret_flags |= pf->flags & I40E_FLAG_VEB_STATS_ENABLED ? - I40E_PRIV_FLAGS_VEB_STATS : 0; - ret_flags |= pf->hw_disabled_flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE ? - 0 : I40E_PRIV_FLAGS_HW_ATR_EVICT; - if (pf->hw.pf_id == 0) { - ret_flags |= pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT ? - I40E_PRIV_FLAGS_TRUE_PROMISC_SUPPORT : 0; + u32 i, j, ret_flags = 0; + + for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) { + const struct i40e_priv_flags *priv_flags; + + priv_flags = &i40e_gstrings_priv_flags[i]; + + if (priv_flags->flag & pf->flags) + ret_flags |= BIT(i); + } + + if (pf->hw.pf_id != 0) + return ret_flags; + + for (j = 0; j < I40E_GL_PRIV_FLAGS_STR_LEN; j++) { + const struct i40e_priv_flags *priv_flags; + + priv_flags = &i40e_gl_gstrings_priv_flags[j]; + + if (priv_flags->flag & pf->flags) + ret_flags |= BIT(i + j); } return ret_flags; @@ -3990,54 +4018,65 @@ 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; - u16 sw_flags = 0, valid_flags = 0; - bool reset_required = false; - bool promisc_change = false; - int ret; + u64 changed_flags; + u32 i, j; - /* NOTE: MFP is not settable */ + changed_flags = pf->flags; - if (flags & I40E_PRIV_FLAGS_LINKPOLL_FLAG) - pf->flags |= I40E_FLAG_LINK_POLLING_ENABLED; - else - pf->flags &= ~I40E_FLAG_LINK_POLLING_ENABLED; + for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) { + const struct i40e_priv_flags *priv_flags; - /* 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->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; + priv_flags = &i40e_gstrings_priv_flags[i]; - /* flush current ATR settings */ - set_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); + if (priv_flags->read_only) + continue; + + if (flags & BIT(i)) + pf->flags |= priv_flags->flag; + else + pf->flags &= ~(priv_flags->flag); } - if ((flags & I40E_PRIV_FLAGS_VEB_STATS) && - !(pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) { - pf->flags |= I40E_FLAG_VEB_STATS_ENABLED; - reset_required = true; - } else if (!(flags & I40E_PRIV_FLAGS_VEB_STATS) && - (pf->flags & I40E_FLAG_VEB_STATS_ENABLED)) { - pf->flags &= ~I40E_FLAG_VEB_STATS_ENABLED; - reset_required = true; + if (pf->hw.pf_id != 0) + goto flags_complete; + + for (j = 0; j < I40E_GL_PRIV_FLAGS_STR_LEN; j++) { + const struct i40e_priv_flags *priv_flags; + + priv_flags = &i40e_gl_gstrings_priv_flags[j]; + + if (priv_flags->read_only) + continue; + + if (flags & BIT(i + j)) + pf->flags |= priv_flags->flag; + else + pf->flags &= ~(priv_flags->flag); } - if (pf->hw.pf_id == 0) { - if ((flags & I40E_PRIV_FLAGS_TRUE_PROMISC_SUPPORT) && - !(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) { - pf->flags |= I40E_FLAG_TRUE_PROMISC_SUPPORT; - promisc_change = true; - } else if (!(flags & I40E_PRIV_FLAGS_TRUE_PROMISC_SUPPORT) && - (pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) { - pf->flags &= ~I40E_FLAG_TRUE_PROMISC_SUPPORT; - promisc_change = true; - } +flags_complete: + changed_flags ^= pf->flags; + + /* Process any additional changes needed as a result of flag changes. + * The changed_flags value reflects the list of bits that were + * changed in the code above. + */ + + /* Flush current ATR settings if ATR was disabled */ + if ((changed_flags & I40E_FLAG_FD_ATR_ENABLED) && + !(pf->flags & I40E_FLAG_FD_ATR_ENABLED)) { + pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; + set_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); } - if (promisc_change) { + + /* Only allow ATR evict on hardware that is capable of handling it */ + if (pf->hw_disabled_flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) + pf->flags &= ~I40E_FLAG_HW_ATR_EVICT_CAPABLE; + + if (changed_flags & I40E_FLAG_TRUE_PROMISC_SUPPORT) { + u16 sw_flags = 0, valid_flags = 0; + int ret; + if (!(pf->flags & I40E_FLAG_TRUE_PROMISC_SUPPORT)) sw_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC; valid_flags = I40E_AQ_SET_SWITCH_CFG_PROMISC; @@ -4053,14 +4092,10 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) } } - if ((flags & I40E_PRIV_FLAGS_HW_ATR_EVICT) && - (pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE)) - pf->hw_disabled_flags &= ~I40E_FLAG_HW_ATR_EVICT_CAPABLE; - else - pf->hw_disabled_flags |= I40E_FLAG_HW_ATR_EVICT_CAPABLE; - - /* if needed, issue reset to cause things to take effect */ - if (reset_required) + /* Issue reset to cause things to take effect, as additional bits + * are added we will need to create a mask of bits requiring reset + */ + if (changed_flags & I40E_FLAG_VEB_STATS_ENABLED) i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED)); return 0; -- cgit From 6030308ef8e5917da2f606abdbb893435d119b28 Mon Sep 17 00:00:00 2001 From: Paul M Stillwell Jr Date: Fri, 10 Mar 2017 12:22:02 -0800 Subject: i40e: use register for XL722 control register read/write The XL722 doesn't support the AQ command to read/write the control register so enable it to bypass the check and use the direct read/write method. Change-ID: Iefecc737b57207485c90845af5989d5af518bf16 Signed-off-by: Paul M Stillwell Jr Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_common.c | 8 ++++++-- drivers/net/ethernet/intel/i40evf/i40e_common.c | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 95946f41002b..f9db95aa3a20 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -4963,7 +4963,9 @@ u32 i40e_read_rx_ctl(struct i40e_hw *hw, u32 reg_addr) int retry = 5; u32 val = 0; - use_register = (hw->aq.api_maj_ver == 1) && (hw->aq.api_min_ver < 5); + use_register = (((hw->aq.api_maj_ver == 1) && + (hw->aq.api_min_ver < 5)) || + (hw->mac.type == I40E_MAC_X722)); if (!use_register) { do_retry: status = i40e_aq_rx_ctl_read_register(hw, reg_addr, &val, NULL); @@ -5022,7 +5024,9 @@ void i40e_write_rx_ctl(struct i40e_hw *hw, u32 reg_addr, u32 reg_val) bool use_register; int retry = 5; - use_register = (hw->aq.api_maj_ver == 1) && (hw->aq.api_min_ver < 5); + use_register = (((hw->aq.api_maj_ver == 1) && + (hw->aq.api_min_ver < 5)) || + (hw->mac.type == I40E_MAC_X722)); if (!use_register) { do_retry: status = i40e_aq_rx_ctl_write_register(hw, reg_addr, diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c index 89dfdbca13db..626fbf1ead4d 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c @@ -958,7 +958,9 @@ u32 i40evf_read_rx_ctl(struct i40e_hw *hw, u32 reg_addr) int retry = 5; u32 val = 0; - use_register = (hw->aq.api_maj_ver == 1) && (hw->aq.api_min_ver < 5); + use_register = (((hw->aq.api_maj_ver == 1) && + (hw->aq.api_min_ver < 5)) || + (hw->mac.type == I40E_MAC_X722)); if (!use_register) { do_retry: status = i40evf_aq_rx_ctl_read_register(hw, reg_addr, @@ -1019,7 +1021,9 @@ void i40evf_write_rx_ctl(struct i40e_hw *hw, u32 reg_addr, u32 reg_val) bool use_register; int retry = 5; - use_register = (hw->aq.api_maj_ver == 1) && (hw->aq.api_min_ver < 5); + use_register = (((hw->aq.api_maj_ver == 1) && + (hw->aq.api_min_ver < 5)) || + (hw->mac.type == I40E_MAC_X722)); if (!use_register) { do_retry: status = i40evf_aq_rx_ctl_write_register(hw, reg_addr, -- cgit From 1fca3265be916f45bcdf4f0207bcc99d0f6c1b7d Mon Sep 17 00:00:00 2001 From: Christopher N Bednarz Date: Fri, 10 Mar 2017 12:22:03 -0800 Subject: i40e: Check for new arq elements before leaving the adminq subtask loop Fix a case where we miss an arq element if a new one is added before we enable interrupts and exit the arq subtask loop. This occurs frequently with RDMA running on Windows VF and causes long delays that prevent SMB from establishing connections. Change-ID: I3e1c8b2b960c12857d9b8275bea2c1563674392e Signed-off-by: Christopher N Bednarz Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 96bedb54701c..cdf36713f4d1 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -6519,9 +6519,11 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf) opcode); break; } - } while (pending && (i++ < pf->adminq_work_limit)); + } while (i++ < pf->adminq_work_limit); + + if (i < pf->adminq_work_limit) + clear_bit(__I40E_ADMINQ_EVENT_PENDING, &pf->state); - clear_bit(__I40E_ADMINQ_EVENT_PENDING, &pf->state); /* re-enable Admin queue interrupt cause */ val = rd32(hw, I40E_PFINT_ICR0_ENA); val |= I40E_PFINT_ICR0_ENA_ADMINQ_MASK; -- cgit From d9eaf12e853f8cd63633434fe9f753098012167f Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 10 Mar 2017 12:22:04 -0800 Subject: i40e: remove a useless goto statement The goto found here for when in MFP mode is pointless. It jumps to the end of a series of if blocks. However, right after this statement is a closing '}' for this if block, which will result in the program flow going to the exact same location as the goto statement indicates. Thus, regardless of whether we are in MFP mode, the program flow will resume from the same location. This arose due to various refactoring which did not notice that this goto became essentially a no-op. To properly understand this diff you will need to view a larger context than is given by default. Change-ID: I088f73c3831aa5c4e2281380c7a3ce605594300c Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index cdf36713f4d1..1dc02c5eee1c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5167,10 +5167,6 @@ static int i40e_init_pf_dcb(struct i40e_pf *pf) (hw->dcbx_status == I40E_DCBX_STATUS_DISABLED)) { dev_info(&pf->pdev->dev, "DCBX offload is not supported or is disabled for this PF.\n"); - - if (pf->flags & I40E_FLAG_MFP_ENABLED) - goto out; - } else { /* When status is not DISABLED then DCBX in FW */ pf->dcbx_cap = DCB_CAP_DCBX_LLD_MANAGED | -- cgit From 3a104f8df2ca87d6d116eae5f2442b57dc3baec4 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 10 Mar 2017 12:22:05 -0800 Subject: i40e: remove FDIR_REQUIRES_REINIT driver flag This flag hasn't been used since commit 1e1be8f622ee ("i40e: ATR policy change to flush the table to clean stale ATR rules"). Lets simplify things and just remove it. Change-ID: I76279d84db8a2fd96f445b96aa413059f9256879 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index f506e994861b..aa9ac2833edf 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -389,7 +389,6 @@ struct i40e_pf { #define I40E_FLAG_MSIX_ENABLED BIT_ULL(3) #define I40E_FLAG_RSS_ENABLED BIT_ULL(6) #define I40E_FLAG_VMDQ_ENABLED BIT_ULL(7) -#define I40E_FLAG_FDIR_REQUIRES_REINIT BIT_ULL(8) #define I40E_FLAG_NEED_LINK_UPDATE BIT_ULL(9) #define I40E_FLAG_IWARP_ENABLED BIT_ULL(10) #define I40E_FLAG_CLEAN_ADMINQ BIT_ULL(14) -- cgit From d57c0e08c70162feab9ccab085fc34095d2dfd11 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 14 Mar 2017 10:15:22 -0700 Subject: i40e/i40evf: Use length to determine if descriptor is done This change makes it so that we use the length of the packet instead of the DD status bit to determine if a new descriptor is ready to be processed. The obvious advantage is that it cuts down on reads as we don't really even need the DD bit if going from a 0 to a non-zero value on size is enough to inform us that the packet has been completed. Change-ID: Iebdf9cdb36c454ef092df27199b92ad09c374231 Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 24 ++++++++++++------------ drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 2ca8d13baea5..012e55354043 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1757,6 +1757,7 @@ add_tail_frag: * i40e_fetch_rx_buffer - Allocate skb and populate it * @rx_ring: rx descriptor ring to transact packets on * @rx_desc: descriptor containing info written by hardware + * @size: size of buffer to add to skb * * This function allocates an skb on the fly, and populates it with the page * data from the current receive descriptor, taking care to set up the skb @@ -1766,13 +1767,9 @@ add_tail_frag: static inline struct sk_buff *i40e_fetch_rx_buffer(struct i40e_ring *rx_ring, union i40e_rx_desc *rx_desc, - struct sk_buff *skb) + struct sk_buff *skb, + unsigned int size) { - u64 local_status_error_len = - le64_to_cpu(rx_desc->wb.qword1.status_error_len); - unsigned int size = - (local_status_error_len & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> - I40E_RXD_QW1_LENGTH_PBUF_SHIFT; struct i40e_rx_buffer *rx_buffer; struct page *page; @@ -1890,6 +1887,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) while (likely(total_rx_packets < budget)) { union i40e_rx_desc *rx_desc; + unsigned int size; u16 vlan_tag; u8 rx_ptype; u64 qword; @@ -1906,19 +1904,21 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) /* status_error_len will always be zero for unused descriptors * because it's cleared in cleanup, and overlaps with hdr_addr * which is always zero because packet split isn't used, if the - * hardware wrote DD then it will be non-zero + * hardware wrote DD then the length will be non-zero */ - if (!i40e_test_staterr(rx_desc, - BIT(I40E_RX_DESC_STATUS_DD_SHIFT))) + qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); + size = (qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> + I40E_RXD_QW1_LENGTH_PBUF_SHIFT; + if (!size) break; /* This memory barrier is needed to keep us from reading - * any other fields out of the rx_desc until we know the - * DD bit is set. + * any other fields out of the rx_desc until we have + * verified the descriptor has been written back. */ dma_rmb(); - skb = i40e_fetch_rx_buffer(rx_ring, rx_desc, skb); + skb = i40e_fetch_rx_buffer(rx_ring, rx_desc, skb, size); if (!skb) break; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index f1a99a8dc7ea..e41eb46b02fe 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1116,6 +1116,7 @@ add_tail_frag: * i40evf_fetch_rx_buffer - Allocate skb and populate it * @rx_ring: rx descriptor ring to transact packets on * @rx_desc: descriptor containing info written by hardware + * @size: size of buffer to add to skb * * This function allocates an skb on the fly, and populates it with the page * data from the current receive descriptor, taking care to set up the skb @@ -1125,13 +1126,9 @@ add_tail_frag: static inline struct sk_buff *i40evf_fetch_rx_buffer(struct i40e_ring *rx_ring, union i40e_rx_desc *rx_desc, - struct sk_buff *skb) + struct sk_buff *skb, + unsigned int size) { - u64 local_status_error_len = - le64_to_cpu(rx_desc->wb.qword1.status_error_len); - unsigned int size = - (local_status_error_len & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> - I40E_RXD_QW1_LENGTH_PBUF_SHIFT; struct i40e_rx_buffer *rx_buffer; struct page *page; @@ -1244,6 +1241,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) while (likely(total_rx_packets < budget)) { union i40e_rx_desc *rx_desc; + unsigned int size; u16 vlan_tag; u8 rx_ptype; u64 qword; @@ -1260,19 +1258,21 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) /* status_error_len will always be zero for unused descriptors * because it's cleared in cleanup, and overlaps with hdr_addr * which is always zero because packet split isn't used, if the - * hardware wrote DD then it will be non-zero + * hardware wrote DD then the length will be non-zero */ - if (!i40e_test_staterr(rx_desc, - BIT(I40E_RX_DESC_STATUS_DD_SHIFT))) + qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); + size = (qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> + I40E_RXD_QW1_LENGTH_PBUF_SHIFT; + if (!size) break; /* This memory barrier is needed to keep us from reading - * any other fields out of the rx_desc until we know the - * DD bit is set. + * any other fields out of the rx_desc until we have + * verified the descriptor has been written back. */ dma_rmb(); - skb = i40evf_fetch_rx_buffer(rx_ring, rx_desc, skb); + skb = i40evf_fetch_rx_buffer(rx_ring, rx_desc, skb, size); if (!skb) break; -- cgit From 9a064128fc8489e9066fde872f6fdeb3d1bbb84f Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 14 Mar 2017 10:15:23 -0700 Subject: i40e/i40evf: Pull code for grabbing and syncing rx_buffer from fetch_buffer This patch pulls the code responsible for fetching the Rx buffer and synchronizing DMA into a function, specifically called i40e_get_rx_buffer. The general idea is to allow for better code reuse by pulling this out of i40e_fetch_rx_buffer. We dropped a couple of prefetches since the time between the prefetch being called and the data being accessed was too small to be useful. Change-ID: I4885fce4b2637dbedc8e16431169d23d3d7e79b9 Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 58 ++++++++++++++++----------- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 58 ++++++++++++++++----------- 2 files changed, 68 insertions(+), 48 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 012e55354043..f2256d8c5e35 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1753,10 +1753,36 @@ add_tail_frag: return i40e_can_reuse_rx_page(rx_buffer, page, truesize); } +/** + * i40e_get_rx_buffer - Fetch Rx buffer and synchronize data for use + * @rx_ring: rx descriptor ring to transact packets on + * @size: size of buffer to add to skb + * + * This function will pull an Rx buffer from the ring and synchronize it + * for use by the CPU. + */ +static struct i40e_rx_buffer *i40e_get_rx_buffer(struct i40e_ring *rx_ring, + const unsigned int size) +{ + struct i40e_rx_buffer *rx_buffer; + + rx_buffer = &rx_ring->rx_bi[rx_ring->next_to_clean]; + prefetchw(rx_buffer->page); + + /* we are reusing so sync this buffer for CPU use */ + dma_sync_single_range_for_cpu(rx_ring->dev, + rx_buffer->dma, + rx_buffer->page_offset, + size, + DMA_FROM_DEVICE); + + return rx_buffer; +} + /** * i40e_fetch_rx_buffer - Allocate skb and populate it * @rx_ring: rx descriptor ring to transact packets on - * @rx_desc: descriptor containing info written by hardware + * @rx_buffer: rx buffer to pull data from * @size: size of buffer to add to skb * * This function allocates an skb on the fly, and populates it with the page @@ -1766,19 +1792,13 @@ add_tail_frag: */ static inline struct sk_buff *i40e_fetch_rx_buffer(struct i40e_ring *rx_ring, - union i40e_rx_desc *rx_desc, + struct i40e_rx_buffer *rx_buffer, struct sk_buff *skb, unsigned int size) { - struct i40e_rx_buffer *rx_buffer; - struct page *page; - - rx_buffer = &rx_ring->rx_bi[rx_ring->next_to_clean]; - page = rx_buffer->page; - prefetchw(page); - if (likely(!skb)) { - void *page_addr = page_address(page) + rx_buffer->page_offset; + void *page_addr = page_address(rx_buffer->page) + + rx_buffer->page_offset; /* prefetch first cache line of first page */ prefetch(page_addr); @@ -1794,21 +1814,8 @@ struct sk_buff *i40e_fetch_rx_buffer(struct i40e_ring *rx_ring, rx_ring->rx_stats.alloc_buff_failed++; return NULL; } - - /* we will be copying header into skb->data in - * pskb_may_pull so it is in our interest to prefetch - * it now to avoid a possible cache miss - */ - prefetchw(skb->data); } - /* we are reusing so sync this buffer for CPU use */ - dma_sync_single_range_for_cpu(rx_ring->dev, - rx_buffer->dma, - rx_buffer->page_offset, - size, - DMA_FROM_DEVICE); - /* pull page into skb */ if (i40e_add_rx_frag(rx_ring, rx_buffer, size, skb)) { /* hand second half of page back to the ring */ @@ -1886,6 +1893,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) bool failure = false; while (likely(total_rx_packets < budget)) { + struct i40e_rx_buffer *rx_buffer; union i40e_rx_desc *rx_desc; unsigned int size; u16 vlan_tag; @@ -1918,7 +1926,9 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) */ dma_rmb(); - skb = i40e_fetch_rx_buffer(rx_ring, rx_desc, skb, size); + rx_buffer = i40e_get_rx_buffer(rx_ring, size); + + skb = i40e_fetch_rx_buffer(rx_ring, rx_buffer, skb, size); if (!skb) break; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index e41eb46b02fe..2320ec4d95ee 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1112,10 +1112,36 @@ add_tail_frag: return i40e_can_reuse_rx_page(rx_buffer, page, truesize); } +/** + * i40e_get_rx_buffer - Fetch Rx buffer and synchronize data for use + * @rx_ring: rx descriptor ring to transact packets on + * @size: size of buffer to add to skb + * + * This function will pull an Rx buffer from the ring and synchronize it + * for use by the CPU. + */ +static struct i40e_rx_buffer *i40e_get_rx_buffer(struct i40e_ring *rx_ring, + const unsigned int size) +{ + struct i40e_rx_buffer *rx_buffer; + + rx_buffer = &rx_ring->rx_bi[rx_ring->next_to_clean]; + prefetchw(rx_buffer->page); + + /* we are reusing so sync this buffer for CPU use */ + dma_sync_single_range_for_cpu(rx_ring->dev, + rx_buffer->dma, + rx_buffer->page_offset, + size, + DMA_FROM_DEVICE); + + return rx_buffer; +} + /** * i40evf_fetch_rx_buffer - Allocate skb and populate it * @rx_ring: rx descriptor ring to transact packets on - * @rx_desc: descriptor containing info written by hardware + * @rx_buffer: rx buffer to pull data from * @size: size of buffer to add to skb * * This function allocates an skb on the fly, and populates it with the page @@ -1125,19 +1151,13 @@ add_tail_frag: */ static inline struct sk_buff *i40evf_fetch_rx_buffer(struct i40e_ring *rx_ring, - union i40e_rx_desc *rx_desc, + struct i40e_rx_buffer *rx_buffer, struct sk_buff *skb, unsigned int size) { - struct i40e_rx_buffer *rx_buffer; - struct page *page; - - rx_buffer = &rx_ring->rx_bi[rx_ring->next_to_clean]; - page = rx_buffer->page; - prefetchw(page); - if (likely(!skb)) { - void *page_addr = page_address(page) + rx_buffer->page_offset; + void *page_addr = page_address(rx_buffer->page) + + rx_buffer->page_offset; /* prefetch first cache line of first page */ prefetch(page_addr); @@ -1153,21 +1173,8 @@ struct sk_buff *i40evf_fetch_rx_buffer(struct i40e_ring *rx_ring, rx_ring->rx_stats.alloc_buff_failed++; return NULL; } - - /* we will be copying header into skb->data in - * pskb_may_pull so it is in our interest to prefetch - * it now to avoid a possible cache miss - */ - prefetchw(skb->data); } - /* we are reusing so sync this buffer for CPU use */ - dma_sync_single_range_for_cpu(rx_ring->dev, - rx_buffer->dma, - rx_buffer->page_offset, - size, - DMA_FROM_DEVICE); - /* pull page into skb */ if (i40e_add_rx_frag(rx_ring, rx_buffer, size, skb)) { /* hand second half of page back to the ring */ @@ -1240,6 +1247,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) bool failure = false; while (likely(total_rx_packets < budget)) { + struct i40e_rx_buffer *rx_buffer; union i40e_rx_desc *rx_desc; unsigned int size; u16 vlan_tag; @@ -1272,7 +1280,9 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) */ dma_rmb(); - skb = i40evf_fetch_rx_buffer(rx_ring, rx_desc, skb, size); + rx_buffer = i40e_get_rx_buffer(rx_ring, size); + + skb = i40evf_fetch_rx_buffer(rx_ring, rx_buffer, skb, size); if (!skb) break; -- cgit From a0cfc3130eef5406867b38d7e6ac25d1b87e2c76 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 14 Mar 2017 10:15:24 -0700 Subject: i40e/i40evf: Pull out code for cleaning up Rx buffers This patch pulls out the code responsible for handling buffer recycling and page counting and distributes it through several functions. This allows us to commonize the bits that handle either freeing or recycling the buffers. As far as the page count tracking one change to the logic is that pagecnt_bias is decremented as soon as we call i40e_get_rx_buffer. It is then the responsibility of the function that pulls the data to either increment the pagecnt_bias if the buffer can be recycled as-is, or to update page_offset so that we are pointing at the correct location for placement of the next buffer. Change-ID: Ibac576360cb7f0b1627f2a993d13c1a8a2bf60af Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 73 +++++++++++++++++---------- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 72 ++++++++++++++++---------- 2 files changed, 89 insertions(+), 56 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index f2256d8c5e35..bba41ce08124 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1294,6 +1294,8 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, bi->dma = dma; bi->page = page; bi->page_offset = 0; + + /* initialize pagecnt_bias to 1 representing we fully own page */ bi->pagecnt_bias = 1; return true; @@ -1622,8 +1624,6 @@ static inline bool i40e_page_is_reusable(struct page *page) * the adapter for another receive * * @rx_buffer: buffer containing the page - * @page: page address from rx_buffer - * @truesize: actual size of the buffer in this page * * If page is reusable, rx_buffer->page_offset is adjusted to point to * an unused region in the page. @@ -1646,14 +1646,13 @@ static inline bool i40e_page_is_reusable(struct page *page) * * In either case, if the page is reusable its refcount is increased. **/ -static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, - struct page *page, - const unsigned int truesize) +static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer) { #if (PAGE_SIZE >= 8192) unsigned int last_offset = PAGE_SIZE - I40E_RXBUFFER_2048; #endif - unsigned int pagecnt_bias = rx_buffer->pagecnt_bias--; + unsigned int pagecnt_bias = rx_buffer->pagecnt_bias; + struct page *page = rx_buffer->page; /* Is any reuse possible? */ if (unlikely(!i40e_page_is_reusable(page))) @@ -1661,15 +1660,9 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, #if (PAGE_SIZE < 8192) /* if we are only owner of page we can reuse it */ - if (unlikely(page_count(page) != pagecnt_bias)) + if (unlikely((page_count(page) - pagecnt_bias) > 1)) return false; - - /* flip page offset to other buffer */ - rx_buffer->page_offset ^= truesize; #else - /* move offset up to the next cache line */ - rx_buffer->page_offset += truesize; - if (rx_buffer->page_offset > last_offset) return false; #endif @@ -1678,10 +1671,11 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, * the pagecnt_bias and page count so that we fully restock the * number of references the driver holds. */ - if (unlikely(pagecnt_bias == 1)) { + if (unlikely(!pagecnt_bias)) { page_ref_add(page, USHRT_MAX); rx_buffer->pagecnt_bias = USHRT_MAX; } + return true; } @@ -1689,8 +1683,8 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, * i40e_add_rx_frag - Add contents of Rx buffer to sk_buff * @rx_ring: rx descriptor ring to transact packets on * @rx_buffer: buffer containing page to add - * @size: packet length from rx_desc * @skb: sk_buff to place the data into + * @size: packet length from rx_desc * * This function will add the data contained in rx_buffer->page to the skb. * This is done either through a direct copy if the data in the buffer is @@ -1700,10 +1694,10 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, * The function will then update the page offset if necessary and return * true if the buffer can be reused by the adapter. **/ -static bool i40e_add_rx_frag(struct i40e_ring *rx_ring, +static void i40e_add_rx_frag(struct i40e_ring *rx_ring, struct i40e_rx_buffer *rx_buffer, - unsigned int size, - struct sk_buff *skb) + struct sk_buff *skb, + unsigned int size) { struct page *page = rx_buffer->page; unsigned char *va = page_address(page) + rx_buffer->page_offset; @@ -1723,12 +1717,11 @@ static bool i40e_add_rx_frag(struct i40e_ring *rx_ring, if (size <= I40E_RX_HDR_SIZE) { memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long))); - /* page is reusable, we can reuse buffer as-is */ - if (likely(i40e_page_is_reusable(page))) - return true; - - /* this page cannot be reused so discard it */ - return false; + /* page is to be freed, increase pagecnt_bias instead of + * decreasing page count. + */ + rx_buffer->pagecnt_bias++; + return; } /* we need the header to contain the greater of either @@ -1750,7 +1743,12 @@ add_tail_frag: skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, (unsigned long)va & ~PAGE_MASK, size, truesize); - return i40e_can_reuse_rx_page(rx_buffer, page, truesize); + /* page is being used so we must update the page offset */ +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif } /** @@ -1776,6 +1774,9 @@ static struct i40e_rx_buffer *i40e_get_rx_buffer(struct i40e_ring *rx_ring, size, DMA_FROM_DEVICE); + /* We have pulled a buffer for use, so decrement pagecnt_bias */ + rx_buffer->pagecnt_bias--; + return rx_buffer; } @@ -1812,12 +1813,29 @@ struct sk_buff *i40e_fetch_rx_buffer(struct i40e_ring *rx_ring, GFP_ATOMIC | __GFP_NOWARN); if (unlikely(!skb)) { rx_ring->rx_stats.alloc_buff_failed++; + rx_buffer->pagecnt_bias++; return NULL; } } /* pull page into skb */ - if (i40e_add_rx_frag(rx_ring, rx_buffer, size, skb)) { + i40e_add_rx_frag(rx_ring, rx_buffer, skb, size); + + return skb; +} + +/** + * i40e_put_rx_buffer - Clean up used buffer and either recycle or free + * @rx_ring: rx descriptor ring to transact packets on + * @rx_buffer: rx buffer to pull data from + * + * This function will clean up the contents of the rx_buffer. It will + * either recycle the bufer or unmap it and free the associated resources. + */ +static void i40e_put_rx_buffer(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *rx_buffer) +{ + if (i40e_can_reuse_rx_page(rx_buffer)) { /* hand second half of page back to the ring */ i40e_reuse_rx_page(rx_ring, rx_buffer); rx_ring->rx_stats.page_reuse_count++; @@ -1831,8 +1849,6 @@ struct sk_buff *i40e_fetch_rx_buffer(struct i40e_ring *rx_ring, /* clear contents of buffer_info */ rx_buffer->page = NULL; - - return skb; } /** @@ -1932,6 +1948,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) if (!skb) break; + i40e_put_rx_buffer(rx_ring, rx_buffer); cleaned_count++; if (i40e_is_non_eop(rx_ring, rx_desc, skb)) diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 2320ec4d95ee..06b37790202a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -662,6 +662,8 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, bi->dma = dma; bi->page = page; bi->page_offset = 0; + + /* initialize pagecnt_bias to 1 representing we fully own page */ bi->pagecnt_bias = 1; return true; @@ -980,8 +982,6 @@ static inline bool i40e_page_is_reusable(struct page *page) * the adapter for another receive * * @rx_buffer: buffer containing the page - * @page: page address from rx_buffer - * @truesize: actual size of the buffer in this page * * If page is reusable, rx_buffer->page_offset is adjusted to point to * an unused region in the page. @@ -1004,14 +1004,13 @@ static inline bool i40e_page_is_reusable(struct page *page) * * In either case, if the page is reusable its refcount is increased. **/ -static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, - struct page *page, - const unsigned int truesize) +static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer) { #if (PAGE_SIZE >= 8192) unsigned int last_offset = PAGE_SIZE - I40E_RXBUFFER_2048; #endif - unsigned int pagecnt_bias = rx_buffer->pagecnt_bias--; + unsigned int pagecnt_bias = rx_buffer->pagecnt_bias; + struct page *page = rx_buffer->page; /* Is any reuse possible? */ if (unlikely(!i40e_page_is_reusable(page))) @@ -1019,15 +1018,9 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, #if (PAGE_SIZE < 8192) /* if we are only owner of page we can reuse it */ - if (unlikely(page_count(page) != pagecnt_bias)) + if (unlikely((page_count(page) - pagecnt_bias) > 1)) return false; - - /* flip page offset to other buffer */ - rx_buffer->page_offset ^= truesize; #else - /* move offset up to the next cache line */ - rx_buffer->page_offset += truesize; - if (rx_buffer->page_offset > last_offset) return false; #endif @@ -1036,7 +1029,7 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, * the pagecnt_bias and page count so that we fully restock the * number of references the driver holds. */ - if (unlikely(pagecnt_bias == 1)) { + if (unlikely(!pagecnt_bias)) { page_ref_add(page, USHRT_MAX); rx_buffer->pagecnt_bias = USHRT_MAX; } @@ -1048,8 +1041,8 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, * i40e_add_rx_frag - Add contents of Rx buffer to sk_buff * @rx_ring: rx descriptor ring to transact packets on * @rx_buffer: buffer containing page to add - * @size: packet length from rx_desc * @skb: sk_buff to place the data into + * @size: packet length from rx_desc * * This function will add the data contained in rx_buffer->page to the skb. * This is done either through a direct copy if the data in the buffer is @@ -1059,10 +1052,10 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer, * The function will then update the page offset if necessary and return * true if the buffer can be reused by the adapter. **/ -static bool i40e_add_rx_frag(struct i40e_ring *rx_ring, +static void i40e_add_rx_frag(struct i40e_ring *rx_ring, struct i40e_rx_buffer *rx_buffer, - unsigned int size, - struct sk_buff *skb) + struct sk_buff *skb, + unsigned int size) { struct page *page = rx_buffer->page; unsigned char *va = page_address(page) + rx_buffer->page_offset; @@ -1082,12 +1075,11 @@ static bool i40e_add_rx_frag(struct i40e_ring *rx_ring, if (size <= I40E_RX_HDR_SIZE) { memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long))); - /* page is reusable, we can reuse buffer as-is */ - if (likely(i40e_page_is_reusable(page))) - return true; - - /* this page cannot be reused so discard it */ - return false; + /* page is to be freed, increase pagecnt_bias instead of + * decreasing page count. + */ + rx_buffer->pagecnt_bias++; + return; } /* we need the header to contain the greater of either @@ -1109,7 +1101,12 @@ add_tail_frag: skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, (unsigned long)va & ~PAGE_MASK, size, truesize); - return i40e_can_reuse_rx_page(rx_buffer, page, truesize); + /* page is being used so we must update the page offset */ +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif } /** @@ -1135,6 +1132,9 @@ static struct i40e_rx_buffer *i40e_get_rx_buffer(struct i40e_ring *rx_ring, size, DMA_FROM_DEVICE); + /* We have pulled a buffer for use, so decrement pagecnt_bias */ + rx_buffer->pagecnt_bias--; + return rx_buffer; } @@ -1171,12 +1171,29 @@ struct sk_buff *i40evf_fetch_rx_buffer(struct i40e_ring *rx_ring, GFP_ATOMIC | __GFP_NOWARN); if (unlikely(!skb)) { rx_ring->rx_stats.alloc_buff_failed++; + rx_buffer->pagecnt_bias++; return NULL; } } /* pull page into skb */ - if (i40e_add_rx_frag(rx_ring, rx_buffer, size, skb)) { + i40e_add_rx_frag(rx_ring, rx_buffer, skb, size); + + return skb; +} + +/** + * i40e_put_rx_buffer - Clean up used buffer and either recycle or free + * @rx_ring: rx descriptor ring to transact packets on + * @rx_buffer: rx buffer to pull data from + * + * This function will clean up the contents of the rx_buffer. It will + * either recycle the bufer or unmap it and free the associated resources. + */ +static void i40e_put_rx_buffer(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *rx_buffer) +{ + if (i40e_can_reuse_rx_page(rx_buffer)) { /* hand second half of page back to the ring */ i40e_reuse_rx_page(rx_ring, rx_buffer); rx_ring->rx_stats.page_reuse_count++; @@ -1190,8 +1207,6 @@ struct sk_buff *i40evf_fetch_rx_buffer(struct i40e_ring *rx_ring, /* clear contents of buffer_info */ rx_buffer->page = NULL; - - return skb; } /** @@ -1286,6 +1301,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) if (!skb) break; + i40e_put_rx_buffer(rx_ring, rx_buffer); cleaned_count++; if (i40e_is_non_eop(rx_ring, rx_desc, skb)) -- cgit From fa2343e9034ce6c8d93ace00e6e7a6974394f0df Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 14 Mar 2017 10:15:25 -0700 Subject: i40e/i40evf: Break i40e_fetch_rx_buffer up to allow for reuse of frag code This patch is meant to clean up the code in preparation for us adding support for build_skb. Specifically we deconstruct i40e_fetch_buffer into several functions so that those functions can later be reused when we add a path for build_skb. Specifically with this change we split out the code for adding a page to an exiting skb. Change-ID: Iab1efbab6b8b97cb60ab9fdd0be1d37a056a154d Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 138 ++++++++++++-------------- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 138 ++++++++++++-------------- 2 files changed, 130 insertions(+), 146 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index bba41ce08124..ebffca0cefac 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1687,61 +1687,23 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer) * @size: packet length from rx_desc * * This function will add the data contained in rx_buffer->page to the skb. - * This is done either through a direct copy if the data in the buffer is - * less than the skb header size, otherwise it will just attach the page as - * a frag to the skb. + * It will just attach the page as a frag to the skb. * - * The function will then update the page offset if necessary and return - * true if the buffer can be reused by the adapter. + * The function will then update the page offset. **/ static void i40e_add_rx_frag(struct i40e_ring *rx_ring, struct i40e_rx_buffer *rx_buffer, struct sk_buff *skb, unsigned int size) { - struct page *page = rx_buffer->page; - unsigned char *va = page_address(page) + rx_buffer->page_offset; #if (PAGE_SIZE < 8192) unsigned int truesize = I40E_RXBUFFER_2048; #else - unsigned int truesize = ALIGN(size, L1_CACHE_BYTES); + unsigned int truesize = SKB_DATA_ALIGN(size); #endif - unsigned int pull_len; - - if (unlikely(skb_is_nonlinear(skb))) - goto add_tail_frag; - - /* will the data fit in the skb we allocated? if so, just - * copy it as it is pretty small anyway - */ - if (size <= I40E_RX_HDR_SIZE) { - memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long))); - - /* page is to be freed, increase pagecnt_bias instead of - * decreasing page count. - */ - rx_buffer->pagecnt_bias++; - return; - } - - /* we need the header to contain the greater of either - * ETH_HLEN or 60 bytes if the skb->len is less than - * 60 for skb_pad. - */ - pull_len = eth_get_headlen(va, I40E_RX_HDR_SIZE); - - /* align pull length to size of long to optimize - * memcpy performance - */ - memcpy(__skb_put(skb, pull_len), va, ALIGN(pull_len, sizeof(long))); - - /* update all of the pointers */ - va += pull_len; - size -= pull_len; -add_tail_frag: - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, - (unsigned long)va & ~PAGE_MASK, size, truesize); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, + rx_buffer->page_offset, size, truesize); /* page is being used so we must update the page offset */ #if (PAGE_SIZE < 8192) @@ -1781,45 +1743,66 @@ static struct i40e_rx_buffer *i40e_get_rx_buffer(struct i40e_ring *rx_ring, } /** - * i40e_fetch_rx_buffer - Allocate skb and populate it + * i40e_construct_skb - Allocate skb and populate it * @rx_ring: rx descriptor ring to transact packets on * @rx_buffer: rx buffer to pull data from * @size: size of buffer to add to skb * - * This function allocates an skb on the fly, and populates it with the page - * data from the current receive descriptor, taking care to set up the skb - * correctly, as well as handling calling the page recycle function if - * necessary. + * This function allocates an skb. It then populates it with the page + * data from the current receive descriptor, taking care to set up the + * skb correctly. */ -static inline -struct sk_buff *i40e_fetch_rx_buffer(struct i40e_ring *rx_ring, - struct i40e_rx_buffer *rx_buffer, - struct sk_buff *skb, - unsigned int size) +static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *rx_buffer, + unsigned int size) { - if (likely(!skb)) { - void *page_addr = page_address(rx_buffer->page) + - rx_buffer->page_offset; + void *va = page_address(rx_buffer->page) + rx_buffer->page_offset; +#if (PAGE_SIZE < 8192) + unsigned int truesize = I40E_RXBUFFER_2048; +#else + unsigned int truesize = SKB_DATA_ALIGN(size); +#endif + unsigned int headlen; + struct sk_buff *skb; - /* prefetch first cache line of first page */ - prefetch(page_addr); + /* prefetch first cache line of first page */ + prefetch(va); #if L1_CACHE_BYTES < 128 - prefetch(page_addr + L1_CACHE_BYTES); + prefetch(va + L1_CACHE_BYTES); #endif - /* allocate a skb to store the frags */ - skb = __napi_alloc_skb(&rx_ring->q_vector->napi, - I40E_RX_HDR_SIZE, - GFP_ATOMIC | __GFP_NOWARN); - if (unlikely(!skb)) { - rx_ring->rx_stats.alloc_buff_failed++; - rx_buffer->pagecnt_bias++; - return NULL; - } - } + /* allocate a skb to store the frags */ + skb = __napi_alloc_skb(&rx_ring->q_vector->napi, + I40E_RX_HDR_SIZE, + GFP_ATOMIC | __GFP_NOWARN); + if (unlikely(!skb)) + return NULL; + + /* Determine available headroom for copy */ + headlen = size; + if (headlen > I40E_RX_HDR_SIZE) + headlen = eth_get_headlen(va, I40E_RX_HDR_SIZE); - /* pull page into skb */ - i40e_add_rx_frag(rx_ring, rx_buffer, skb, size); + /* align pull length to size of long to optimize memcpy performance */ + memcpy(__skb_put(skb, headlen), va, ALIGN(headlen, sizeof(long))); + + /* update all of the pointers */ + size -= headlen; + if (size) { + skb_add_rx_frag(skb, 0, rx_buffer->page, + rx_buffer->page_offset + headlen, + size, truesize); + + /* buffer is used by skb, update page_offset */ +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif + } else { + /* buffer is unused, reset bias back to rx_buffer */ + rx_buffer->pagecnt_bias++; + } return skb; } @@ -1944,9 +1927,18 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) rx_buffer = i40e_get_rx_buffer(rx_ring, size); - skb = i40e_fetch_rx_buffer(rx_ring, rx_buffer, skb, size); - if (!skb) + /* retrieve a buffer from the ring */ + if (skb) + i40e_add_rx_frag(rx_ring, rx_buffer, skb, size); + else + skb = i40e_construct_skb(rx_ring, rx_buffer, size); + + /* exit if we failed to retrieve a buffer */ + if (!skb) { + rx_ring->rx_stats.alloc_buff_failed++; + rx_buffer->pagecnt_bias++; break; + } i40e_put_rx_buffer(rx_ring, rx_buffer); cleaned_count++; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 06b37790202a..95e383af41c4 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1045,61 +1045,23 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer) * @size: packet length from rx_desc * * This function will add the data contained in rx_buffer->page to the skb. - * This is done either through a direct copy if the data in the buffer is - * less than the skb header size, otherwise it will just attach the page as - * a frag to the skb. + * It will just attach the page as a frag to the skb. * - * The function will then update the page offset if necessary and return - * true if the buffer can be reused by the adapter. + * The function will then update the page offset. **/ static void i40e_add_rx_frag(struct i40e_ring *rx_ring, struct i40e_rx_buffer *rx_buffer, struct sk_buff *skb, unsigned int size) { - struct page *page = rx_buffer->page; - unsigned char *va = page_address(page) + rx_buffer->page_offset; #if (PAGE_SIZE < 8192) unsigned int truesize = I40E_RXBUFFER_2048; #else - unsigned int truesize = ALIGN(size, L1_CACHE_BYTES); + unsigned int truesize = SKB_DATA_ALIGN(size); #endif - unsigned int pull_len; - - if (unlikely(skb_is_nonlinear(skb))) - goto add_tail_frag; - - /* will the data fit in the skb we allocated? if so, just - * copy it as it is pretty small anyway - */ - if (size <= I40E_RX_HDR_SIZE) { - memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long))); - - /* page is to be freed, increase pagecnt_bias instead of - * decreasing page count. - */ - rx_buffer->pagecnt_bias++; - return; - } - - /* we need the header to contain the greater of either - * ETH_HLEN or 60 bytes if the skb->len is less than - * 60 for skb_pad. - */ - pull_len = eth_get_headlen(va, I40E_RX_HDR_SIZE); - - /* align pull length to size of long to optimize - * memcpy performance - */ - memcpy(__skb_put(skb, pull_len), va, ALIGN(pull_len, sizeof(long))); - - /* update all of the pointers */ - va += pull_len; - size -= pull_len; -add_tail_frag: - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, - (unsigned long)va & ~PAGE_MASK, size, truesize); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, + rx_buffer->page_offset, size, truesize); /* page is being used so we must update the page offset */ #if (PAGE_SIZE < 8192) @@ -1139,45 +1101,66 @@ static struct i40e_rx_buffer *i40e_get_rx_buffer(struct i40e_ring *rx_ring, } /** - * i40evf_fetch_rx_buffer - Allocate skb and populate it + * i40e_construct_skb - Allocate skb and populate it * @rx_ring: rx descriptor ring to transact packets on * @rx_buffer: rx buffer to pull data from * @size: size of buffer to add to skb * - * This function allocates an skb on the fly, and populates it with the page - * data from the current receive descriptor, taking care to set up the skb - * correctly, as well as handling calling the page recycle function if - * necessary. + * This function allocates an skb. It then populates it with the page + * data from the current receive descriptor, taking care to set up the + * skb correctly. */ -static inline -struct sk_buff *i40evf_fetch_rx_buffer(struct i40e_ring *rx_ring, - struct i40e_rx_buffer *rx_buffer, - struct sk_buff *skb, - unsigned int size) +static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *rx_buffer, + unsigned int size) { - if (likely(!skb)) { - void *page_addr = page_address(rx_buffer->page) + - rx_buffer->page_offset; + void *va = page_address(rx_buffer->page) + rx_buffer->page_offset; +#if (PAGE_SIZE < 8192) + unsigned int truesize = I40E_RXBUFFER_2048; +#else + unsigned int truesize = SKB_DATA_ALIGN(size); +#endif + unsigned int headlen; + struct sk_buff *skb; - /* prefetch first cache line of first page */ - prefetch(page_addr); + /* prefetch first cache line of first page */ + prefetch(va); #if L1_CACHE_BYTES < 128 - prefetch(page_addr + L1_CACHE_BYTES); + prefetch(va + L1_CACHE_BYTES); #endif - /* allocate a skb to store the frags */ - skb = __napi_alloc_skb(&rx_ring->q_vector->napi, - I40E_RX_HDR_SIZE, - GFP_ATOMIC | __GFP_NOWARN); - if (unlikely(!skb)) { - rx_ring->rx_stats.alloc_buff_failed++; - rx_buffer->pagecnt_bias++; - return NULL; - } - } + /* allocate a skb to store the frags */ + skb = __napi_alloc_skb(&rx_ring->q_vector->napi, + I40E_RX_HDR_SIZE, + GFP_ATOMIC | __GFP_NOWARN); + if (unlikely(!skb)) + return NULL; + + /* Determine available headroom for copy */ + headlen = size; + if (headlen > I40E_RX_HDR_SIZE) + headlen = eth_get_headlen(va, I40E_RX_HDR_SIZE); - /* pull page into skb */ - i40e_add_rx_frag(rx_ring, rx_buffer, skb, size); + /* align pull length to size of long to optimize memcpy performance */ + memcpy(__skb_put(skb, headlen), va, ALIGN(headlen, sizeof(long))); + + /* update all of the pointers */ + size -= headlen; + if (size) { + skb_add_rx_frag(skb, 0, rx_buffer->page, + rx_buffer->page_offset + headlen, + size, truesize); + + /* buffer is used by skb, update page_offset */ +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif + } else { + /* buffer is unused, reset bias back to rx_buffer */ + rx_buffer->pagecnt_bias++; + } return skb; } @@ -1297,9 +1280,18 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) rx_buffer = i40e_get_rx_buffer(rx_ring, size); - skb = i40evf_fetch_rx_buffer(rx_ring, rx_buffer, skb, size); - if (!skb) + /* retrieve a buffer from the ring */ + if (skb) + i40e_add_rx_frag(rx_ring, rx_buffer, skb, size); + else + skb = i40e_construct_skb(rx_ring, rx_buffer, size); + + /* exit if we failed to retrieve a buffer */ + if (!skb) { + rx_ring->rx_stats.alloc_buff_failed++; + rx_buffer->pagecnt_bias++; break; + } i40e_put_rx_buffer(rx_ring, rx_buffer); cleaned_count++; -- cgit From c424d4a3dd798958074bde7c1dcd8dc08962d820 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 14 Mar 2017 10:15:26 -0700 Subject: i40e/i40evf: Add legacy-rx private flag to allow fallback to old Rx flow This patch adds a control which will allow us to toggle into and out of the legacy Rx mode. The legacy Rx mode is what we currently do when performing Rx. As I make further changes what should happen is that the driver will fall back to the behavior for Rx as of this patch should the "legacy-rx" flag be set to on. Change-ID: I0342998849bbb31351cce05f6e182c99174e7751 Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 + drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 5 +- drivers/net/ethernet/intel/i40evf/i40evf.h | 2 + drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c | 104 +++++++++++++++++++++ 4 files changed, 111 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index aa9ac2833edf..421ea57128d3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -430,6 +430,7 @@ struct i40e_pf { #define I40E_FLAG_TEMP_LINK_POLLING BIT_ULL(55) #define I40E_FLAG_CLIENT_L2_CHANGE BIT_ULL(56) #define I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE BIT_ULL(57) +#define I40E_FLAG_LEGACY_RX BIT_ULL(58) /* Tracks features that are disabled due to hw limitations. * If a bit is set here, it means that the corresponding diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 9b2e9cef56a4..c0c1a0cdaa5b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -226,6 +226,7 @@ static const struct i40e_priv_flags i40e_gstrings_priv_flags[] = { I40E_PRIV_FLAG("flow-director-atr", I40E_FLAG_FD_ATR_ENABLED, 0), I40E_PRIV_FLAG("veb-stats", I40E_FLAG_VEB_STATS_ENABLED, 0), I40E_PRIV_FLAG("hw-atr-eviction", I40E_FLAG_HW_ATR_EVICT_CAPABLE, 0), + I40E_PRIV_FLAG("legacy-rx", I40E_FLAG_LEGACY_RX, 0), }; #define I40E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40e_gstrings_priv_flags) @@ -4055,6 +4056,7 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) } flags_complete: + /* check for flags that changed */ changed_flags ^= pf->flags; /* Process any additional changes needed as a result of flag changes. @@ -4095,7 +4097,8 @@ flags_complete: /* Issue reset to cause things to take effect, as additional bits * are added we will need to create a mask of bits requiring reset */ - if (changed_flags & I40E_FLAG_VEB_STATS_ENABLED) + if ((changed_flags & I40E_FLAG_VEB_STATS_ENABLED) || + ((changed_flags & I40E_FLAG_LEGACY_RX) && netif_running(dev))) i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED)); return 0; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index b2b48511f457..e60cbfa7e769 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -222,6 +222,7 @@ struct i40evf_adapter { #define I40EVF_FLAG_CLIENT_NEEDS_L2_PARAMS BIT(17) #define I40EVF_FLAG_PROMISC_ON BIT(18) #define I40EVF_FLAG_ALLMULTI_ON BIT(19) +#define I40EVF_FLAG_LEGACY_RX BIT(20) /* duplicates for common code */ #define I40E_FLAG_FDIR_ATR_ENABLED 0 #define I40E_FLAG_DCB_ENABLED 0 @@ -229,6 +230,7 @@ struct i40evf_adapter { #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 +#define I40E_FLAG_LEGACY_RX I40EVF_FLAG_LEGACY_RX /* flags for admin queue service task */ u32 aq_required; #define I40EVF_FLAG_AQ_ENABLE_QUEUES BIT(0) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index 122efbd29a19..9bb2cc7dd4e4 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -63,6 +63,29 @@ static const struct i40evf_stats i40evf_gstrings_stats[] = { #define I40EVF_STATS_LEN(_dev) \ (I40EVF_GLOBAL_STATS_LEN + I40EVF_QUEUE_STATS_LEN(_dev)) +/* For now we have one and only one private flag and it is only defined + * when we have support for the SKIP_CPU_SYNC DMA attribute. Instead + * of leaving all this code sitting around empty we will strip it unless + * our one private flag is actually available. + */ +struct i40evf_priv_flags { + char flag_string[ETH_GSTRING_LEN]; + u32 flag; + bool read_only; +}; + +#define I40EVF_PRIV_FLAG(_name, _flag, _read_only) { \ + .flag_string = _name, \ + .flag = _flag, \ + .read_only = _read_only, \ +} + +static const struct i40evf_priv_flags i40evf_gstrings_priv_flags[] = { + I40EVF_PRIV_FLAG("legacy-rx", I40EVF_FLAG_LEGACY_RX, 0), +}; + +#define I40EVF_PRIV_FLAGS_STR_LEN ARRAY_SIZE(i40evf_gstrings_priv_flags) + /** * i40evf_get_link_ksettings - Get Link Speed and Duplex settings * @netdev: network interface device structure @@ -124,6 +147,8 @@ static int i40evf_get_sset_count(struct net_device *netdev, int sset) { if (sset == ETH_SS_STATS) return I40EVF_STATS_LEN(netdev); + else if (sset == ETH_SS_PRIV_FLAGS) + return I40EVF_PRIV_FLAGS_STR_LEN; else return -EINVAL; } @@ -189,7 +214,83 @@ static void i40evf_get_strings(struct net_device *netdev, u32 sset, u8 *data) snprintf(p, ETH_GSTRING_LEN, "rx-%u.bytes", i); p += ETH_GSTRING_LEN; } + } else if (sset == ETH_SS_PRIV_FLAGS) { + for (i = 0; i < I40EVF_PRIV_FLAGS_STR_LEN; i++) { + snprintf(p, ETH_GSTRING_LEN, "%s", + i40evf_gstrings_priv_flags[i].flag_string); + p += ETH_GSTRING_LEN; + } + } +} + +/** + * i40evf_get_priv_flags - report device private flags + * @dev: network interface device structure + * + * The get string set count and the string set should be matched for each + * flag returned. Add new strings for each flag to the i40e_gstrings_priv_flags + * array. + * + * Returns a u32 bitmap of flags. + **/ +static u32 i40evf_get_priv_flags(struct net_device *netdev) +{ + struct i40evf_adapter *adapter = netdev_priv(netdev); + u32 i, ret_flags = 0; + + for (i = 0; i < I40EVF_PRIV_FLAGS_STR_LEN; i++) { + const struct i40evf_priv_flags *priv_flags; + + priv_flags = &i40evf_gstrings_priv_flags[i]; + + if (priv_flags->flag & adapter->flags) + ret_flags |= BIT(i); + } + + return ret_flags; +} + +/** + * i40evf_set_priv_flags - set private flags + * @dev: network interface device structure + * @flags: bit flags to be set + **/ +static int i40evf_set_priv_flags(struct net_device *netdev, u32 flags) +{ + struct i40evf_adapter *adapter = netdev_priv(netdev); + u64 changed_flags; + u32 i; + + changed_flags = adapter->flags; + + for (i = 0; i < I40EVF_PRIV_FLAGS_STR_LEN; i++) { + const struct i40evf_priv_flags *priv_flags; + + priv_flags = &i40evf_gstrings_priv_flags[i]; + + if (priv_flags->read_only) + continue; + + if (flags & BIT(i)) + adapter->flags |= priv_flags->flag; + else + adapter->flags &= ~(priv_flags->flag); + } + + /* check for flags that changed */ + changed_flags ^= adapter->flags; + + /* Process any additional changes needed as a result of flag changes. */ + + /* issue a reset to force legacy-rx change to take effect */ + if (changed_flags & I40EVF_FLAG_LEGACY_RX) { + if (netif_running(netdev)) { + adapter->flags |= I40EVF_FLAG_RESET_NEEDED; + schedule_work(&adapter->reset_task); + } } + + return 0; } /** @@ -238,6 +339,7 @@ static void i40evf_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->version, i40evf_driver_version, 32); strlcpy(drvinfo->fw_version, "N/A", 4); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); + drvinfo->n_priv_flags = I40EVF_PRIV_FLAGS_STR_LEN; } /** @@ -649,6 +751,8 @@ static const struct ethtool_ops i40evf_ethtool_ops = { .get_strings = i40evf_get_strings, .get_ethtool_stats = i40evf_get_ethtool_stats, .get_sset_count = i40evf_get_sset_count, + .get_priv_flags = i40evf_get_priv_flags, + .set_priv_flags = i40evf_set_priv_flags, .get_msglevel = i40evf_get_msglevel, .set_msglevel = i40evf_set_msglevel, .get_coalesce = i40evf_get_coalesce, -- cgit From dab86afdbbd1bc5d5a89b67ed141d2f46c3b4191 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 14 Mar 2017 10:15:27 -0700 Subject: i40e/i40evf: Change the way we limit the maximum frame size for Rx This patch changes the way we handle the maximum frame size for the Rx path. Previously we were rounding up to 2K for a 1500 MTU and then brining the max frame size down to MTU plus a fixed amount. With this patch applied what we now do is limit the maximum frame to 1.5K minus the value for NET_IP_ALIGN for standard MTU, and for any MTU greater than 1500 we allow up to the maximum frame size. This makes the behavior more consistent with the other drivers such as igb which had similar logic. In addition it reduces the test matrix for MTU since we only have two max frame sizes that are handled for Rx now. Change-ID: I23a9d3c857e7df04b0ef28c64df63e659c013f3f Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 26 ++++++++++++---------- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 4 +--- drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 4 +--- drivers/net/ethernet/intel/i40evf/i40evf.h | 4 ---- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 16 ++++++++++++- .../net/ethernet/intel/i40evf/i40evf_virtchnl.c | 14 ++++++++---- 6 files changed, 41 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 1dc02c5eee1c..1f89e416156d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2995,7 +2995,8 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) ring->rx_buf_len = vsi->rx_buf_len; - rx_ctx.dbuff = ring->rx_buf_len >> I40E_RXQ_CTX_DBUFF_SHIFT; + rx_ctx.dbuff = DIV_ROUND_UP(ring->rx_buf_len, + BIT_ULL(I40E_RXQ_CTX_DBUFF_SHIFT)); rx_ctx.base = (ring->dma / 128); rx_ctx.qlen = ring->count; @@ -3075,17 +3076,18 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi) int err = 0; u16 i; - if (vsi->netdev && (vsi->netdev->mtu > ETH_DATA_LEN)) - vsi->max_frame = vsi->netdev->mtu + ETH_HLEN - + ETH_FCS_LEN + VLAN_HLEN; - else - vsi->max_frame = I40E_RXBUFFER_2048; - - vsi->rx_buf_len = I40E_RXBUFFER_2048; - - /* round up for the chip's needs */ - vsi->rx_buf_len = ALIGN(vsi->rx_buf_len, - BIT_ULL(I40E_RXQ_CTX_DBUFF_SHIFT)); + if (!vsi->netdev || (vsi->back->flags & I40E_FLAG_LEGACY_RX)) { + vsi->max_frame = I40E_MAX_RXBUFFER; + vsi->rx_buf_len = I40E_RXBUFFER_2048; +#if (PAGE_SIZE < 8192) + } else if (vsi->netdev->mtu <= ETH_DATA_LEN) { + vsi->max_frame = I40E_RXBUFFER_1536 - NET_IP_ALIGN; + vsi->rx_buf_len = I40E_RXBUFFER_1536 - NET_IP_ALIGN; +#endif + } else { + vsi->max_frame = I40E_MAX_RXBUFFER; + vsi->rx_buf_len = I40E_RXBUFFER_2048; + } /* set up individual rings */ for (i = 0; i < vsi->num_queue_pairs && !err; i++) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index eb733726637f..d6609deace57 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -117,10 +117,8 @@ enum i40e_dyn_idx_t { /* Supported Rx Buffer Sizes (a multiple of 128) */ #define I40E_RXBUFFER_256 256 +#define I40E_RXBUFFER_1536 1536 /* 128B aligned standard Ethernet frame */ #define I40E_RXBUFFER_2048 2048 -#define I40E_RXBUFFER_3072 3072 /* For FCoE MTU of 2158 */ -#define I40E_RXBUFFER_4096 4096 -#define I40E_RXBUFFER_8192 8192 #define I40E_MAX_RXBUFFER 9728 /* largest size for single descriptor */ /* NOTE: netdev_alloc_skb reserves up to 64 bytes, NET_IP_ALIGN means we diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index aba40edb0e2e..3bb4d732e467 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -104,10 +104,8 @@ enum i40e_dyn_idx_t { /* Supported Rx Buffer Sizes (a multiple of 128) */ #define I40E_RXBUFFER_256 256 +#define I40E_RXBUFFER_1536 1536 /* 128B aligned standard Ethernet frame */ #define I40E_RXBUFFER_2048 2048 -#define I40E_RXBUFFER_3072 3072 /* For FCoE MTU of 2158 */ -#define I40E_RXBUFFER_4096 4096 -#define I40E_RXBUFFER_8192 8192 #define I40E_MAX_RXBUFFER 9728 /* largest size for single descriptor */ /* NOTE: netdev_alloc_skb reserves up to 64 bytes, NET_IP_ALIGN means we diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index e60cbfa7e769..d61ecf655091 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -72,10 +72,6 @@ struct i40e_vsi { #define I40EVF_MAX_RXD 4096 #define I40EVF_MIN_RXD 64 #define I40EVF_REQ_DESCRIPTOR_MULTIPLE 32 - -/* Supported Rx Buffer Sizes */ -#define I40EVF_RXBUFFER_2048 2048 -#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 20 /* times to try before resetting AQ */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 6d666bde9df5..fb2811c23024 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -686,12 +686,26 @@ static void i40evf_configure_tx(struct i40evf_adapter *adapter) **/ static void i40evf_configure_rx(struct i40evf_adapter *adapter) { + unsigned int rx_buf_len = I40E_RXBUFFER_2048; + struct net_device *netdev = adapter->netdev; struct i40e_hw *hw = &adapter->hw; int i; + /* Legacy Rx will always default to a 2048 buffer size. */ +#if (PAGE_SIZE < 8192) + if (!(adapter->flags & I40EVF_FLAG_LEGACY_RX)) { + /* We use a 1536 buffer size for configurations with + * standard Ethernet mtu. On x86 this gives us enough room + * for shared info and 192 bytes of padding. + */ + if (netdev->mtu <= ETH_DATA_LEN) + rx_buf_len = I40E_RXBUFFER_1536 - NET_IP_ALIGN; + } +#endif + for (i = 0; i < adapter->num_active_queues; i++) { adapter->rx_rings[i].tail = hw->hw_addr + I40E_QRX_TAIL1(i); - adapter->rx_rings[i].rx_buf_len = I40EVF_RXBUFFER_2048; + adapter->rx_rings[i].rx_buf_len = rx_buf_len; } } diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 4bc2488bf709..032be8d3928a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -234,7 +234,7 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter) struct i40e_virtchnl_vsi_queue_config_info *vqci; struct i40e_virtchnl_queue_pair_info *vqpi; int pairs = adapter->num_active_queues; - int i, len; + int i, len, max_frame = I40E_MAX_RXBUFFER; if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) { /* bail because we already have a command pending */ @@ -249,6 +249,11 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter) if (!vqci) return; + /* Limit maximum frame size when jumbo frames is not enabled */ + if (!(adapter->flags & I40EVF_FLAG_LEGACY_RX) && + (adapter->netdev->mtu <= ETH_DATA_LEN)) + max_frame = I40E_RXBUFFER_1536 - NET_IP_ALIGN; + vqci->vsi_id = adapter->vsi_res->vsi_id; vqci->num_queue_pairs = pairs; vqpi = vqci->qpair; @@ -264,9 +269,10 @@ void i40evf_configure_queues(struct i40evf_adapter *adapter) vqpi->rxq.queue_id = i; vqpi->rxq.ring_len = adapter->rx_rings[i].count; vqpi->rxq.dma_ring_addr = adapter->rx_rings[i].dma; - vqpi->rxq.max_pkt_size = adapter->netdev->mtu - + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN; - vqpi->rxq.databuffer_size = adapter->rx_rings[i].rx_buf_len; + vqpi->rxq.max_pkt_size = max_frame; + vqpi->rxq.databuffer_size = + ALIGN(adapter->rx_rings[i].rx_buf_len, + BIT_ULL(I40E_RXQ_CTX_DBUFF_SHIFT)); vqpi++; } -- cgit From d08a9f6cd1c8fc58fd57724f45841f77e49e1fa3 Mon Sep 17 00:00:00 2001 From: "Wyborny, Carolyn" Date: Tue, 28 Mar 2017 08:00:48 -0700 Subject: i40e: fix for queue timing delays This patch adds a delay to Rx queue disables to accommodate HW needs. v2: Added missing check for disable only, additional details on the need for the ugly delay and fixed spacing on comment. Change-ID: I2864ca667ce5dcc2cc44f8718113b719742a46a1 Signed-off-by: Carolyn Wyborny Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 1f89e416156d..a0506e28d167 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4067,6 +4067,12 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable) } } + /* Due to HW errata, on Rx disable only, the register can indicate done + * before it really is. Needs 50ms to be sure + */ + if (!enable) + mdelay(50); + return ret; } -- cgit From d0281a56b00c63ad51ebb550fba0351807475c47 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 28 Mar 2017 12:57:09 -0700 Subject: net: phy: Allow building mdio-boardinfo into the kernel mdio-boardinfo contains code that is helpful for platforms to register specific MDIO bus devices independent of how CONFIG_MDIO_DEVICE or CONFIG_PHYLIB will be selected (modular or built-in). In order to make that possible, let's do the following: - descend into drivers/net/phy/ unconditionally - make mdiobus_setup_mdiodev_from_board_info() take a callback argument which allows us not to expose the internal MDIO board info list and mutex, yet maintain the logic within the same file - relocate the code that creates a MDIO device into drivers/net/phy/mdio_bus.c - build mdio-boardinfo.o into the kernel as soon as MDIO_DEVICE is defined (y or m) Fixes: 90eff9096c01 ("net: phy: Allow splitting MDIO bus/device support from PHYs") Fixes: 648ea0134069 ("net: phy: Allow pre-declaration of MDIO devices") Signed-off-by: Florian Fainelli Tested-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/Makefile | 2 +- drivers/net/phy/Makefile | 6 +++++- drivers/net/phy/mdio-boardinfo.c | 21 +++++++-------------- drivers/net/phy/mdio-boardinfo.h | 5 ++++- drivers/net/phy/mdio_bus.c | 32 +++++++++++++++++++++++++++++++- 5 files changed, 48 insertions(+), 18 deletions(-) diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 55f75aea283c..57fc47ad5ab3 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_MII) += mii.o obj-$(CONFIG_MDIO) += mdio.o obj-$(CONFIG_NET) += Space.o loopback.o obj-$(CONFIG_NETCONSOLE) += netconsole.o -obj-$(CONFIG_MDIO_DEVICE) += phy/ +obj-y += phy/ obj-$(CONFIG_RIONET) += rionet.o obj-$(CONFIG_NET_TEAM) += team/ obj-$(CONFIG_TUN) += tun.o diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 0e1ec0438c23..e36db9a2ba38 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,7 +1,11 @@ # Makefile for Linux PHY drivers and MDIO bus drivers libphy-y := phy.o phy-core.o phy_device.o -mdio-bus-y += mdio_bus.o mdio_device.o mdio-boardinfo.o +mdio-bus-y += mdio_bus.o mdio_device.o + +ifdef CONFIG_MDIO_DEVICE +obj-y += mdio-boardinfo.o +endif # PHYLIB implies MDIO_DEVICE, in that case, we have a bunch of circular # dependencies that does not make it possible to split mdio-bus objects into a diff --git a/drivers/net/phy/mdio-boardinfo.c b/drivers/net/phy/mdio-boardinfo.c index 61941e29daae..1861f387820d 100644 --- a/drivers/net/phy/mdio-boardinfo.c +++ b/drivers/net/phy/mdio-boardinfo.c @@ -24,10 +24,12 @@ static DEFINE_MUTEX(mdio_board_lock); * @mdiodev: MDIO device pointer * Context: can sleep */ -void mdiobus_setup_mdiodev_from_board_info(struct mii_bus *bus) +void mdiobus_setup_mdiodev_from_board_info(struct mii_bus *bus, + int (*cb) + (struct mii_bus *bus, + struct mdio_board_info *bi)) { struct mdio_board_entry *be; - struct mdio_device *mdiodev; struct mdio_board_info *bi; int ret; @@ -38,23 +40,14 @@ void mdiobus_setup_mdiodev_from_board_info(struct mii_bus *bus) if (strcmp(bus->id, bi->bus_id)) continue; - mdiodev = mdio_device_create(bus, bi->mdio_addr); - if (IS_ERR(mdiodev)) + ret = cb(bus, bi); + if (ret) continue; - strncpy(mdiodev->modalias, bi->modalias, - sizeof(mdiodev->modalias)); - mdiodev->bus_match = mdio_device_bus_match; - mdiodev->dev.platform_data = (void *)bi->platform_data; - - ret = mdio_device_register(mdiodev); - if (ret) { - mdio_device_free(mdiodev); - continue; - } } mutex_unlock(&mdio_board_lock); } +EXPORT_SYMBOL(mdiobus_setup_mdiodev_from_board_info); /** * mdio_register_board_info - register MDIO devices for a given board diff --git a/drivers/net/phy/mdio-boardinfo.h b/drivers/net/phy/mdio-boardinfo.h index 00f98163e90e..3a7f143904e8 100644 --- a/drivers/net/phy/mdio-boardinfo.h +++ b/drivers/net/phy/mdio-boardinfo.h @@ -14,6 +14,9 @@ struct mdio_board_entry { struct mdio_board_info board_info; }; -void mdiobus_setup_mdiodev_from_board_info(struct mii_bus *bus); +void mdiobus_setup_mdiodev_from_board_info(struct mii_bus *bus, + int (*cb) + (struct mii_bus *bus, + struct mdio_board_info *bi)); #endif /* __MDIO_BOARD_INFO_H */ diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 46b468eb6e12..5a214f3b8671 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -289,6 +289,36 @@ static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio, } #endif +/** + * mdiobus_create_device_from_board_info - create a full MDIO device given + * a mdio_board_info structure + * @bus: MDIO bus to create the devices on + * @bi: mdio_board_info structure describing the devices + * + * Returns 0 on success or < 0 on error. + */ +static int mdiobus_create_device(struct mii_bus *bus, + struct mdio_board_info *bi) +{ + struct mdio_device *mdiodev; + int ret = 0; + + mdiodev = mdio_device_create(bus, bi->mdio_addr); + if (IS_ERR(mdiodev)) + return -ENODEV; + + strncpy(mdiodev->modalias, bi->modalias, + sizeof(mdiodev->modalias)); + mdiodev->bus_match = mdio_device_bus_match; + mdiodev->dev.platform_data = (void *)bi->platform_data; + + ret = mdio_device_register(mdiodev); + if (ret) + mdio_device_free(mdiodev); + + return ret; +} + /** * __mdiobus_register - bring up all the PHYs on a given bus and attach them to bus * @bus: target mii_bus @@ -345,7 +375,7 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner) } } - mdiobus_setup_mdiodev_from_board_info(bus); + mdiobus_setup_mdiodev_from_board_info(bus, mdiobus_create_device); bus->state = MDIOBUS_REGISTERED; pr_info("%s: probed\n", bus->name); -- cgit From e944e97afc66e619603390fc5f0c6bc02f961353 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 28 Mar 2017 15:19:49 -0700 Subject: net: mpls: Update lfib_nlmsg_size to skip deleted nexthops A recent commit skips nexthops in a route if the device has been deleted. Update lfib_nlmsg_size accordingly. Reported-by: Roopa Prabhu Signed-off-by: David Ahern Acked-by: Roopa Prabhu Acked-by: Robert Shearman Signed-off-by: David S. Miller --- net/mpls/af_mpls.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 673f3d990b5c..06ffafde70da 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1880,6 +1880,8 @@ static inline size_t lfib_nlmsg_size(struct mpls_route *rt) size_t nhsize = 0; for_nexthops(rt) { + if (!rtnl_dereference(nh->nh_dev)) + continue; nhsize += nla_total_size(sizeof(struct rtnexthop)); /* RTA_VIA */ if (nh->nh_via_table != MPLS_NEIGH_TABLE_UNSPEC) -- cgit From 56607b987b1f85aa57b570edd0aa35e6b8dfa6a6 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Wed, 29 Mar 2017 08:24:21 +0200 Subject: net: veth: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. Signed-off-by: Philippe Reynes Reviewed-by: Xin Long Signed-off-by: David S. Miller --- drivers/net/veth.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 8c39d6d690e5..317103680675 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -45,18 +45,13 @@ static struct { { "peer_ifindex" }, }; -static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +static int veth_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) { - cmd->supported = 0; - cmd->advertising = 0; - ethtool_cmd_speed_set(cmd, SPEED_10000); - cmd->duplex = DUPLEX_FULL; - cmd->port = PORT_TP; - cmd->phy_address = 0; - cmd->transceiver = XCVR_INTERNAL; - cmd->autoneg = AUTONEG_DISABLE; - cmd->maxtxpkt = 0; - cmd->maxrxpkt = 0; + cmd->base.speed = SPEED_10000; + cmd->base.duplex = DUPLEX_FULL; + cmd->base.port = PORT_TP; + cmd->base.autoneg = AUTONEG_DISABLE; return 0; } @@ -95,12 +90,12 @@ static void veth_get_ethtool_stats(struct net_device *dev, } static const struct ethtool_ops veth_ethtool_ops = { - .get_settings = veth_get_settings, .get_drvinfo = veth_get_drvinfo, .get_link = ethtool_op_get_link, .get_strings = veth_get_strings, .get_sset_count = veth_get_sset_count, .get_ethtool_stats = veth_get_ethtool_stats, + .get_link_ksettings = veth_get_link_ksettings, }; static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) -- cgit From a38d20d791fdcd79ebccda15a8308a6d8ada6e1c Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Wed, 29 Mar 2017 16:42:26 +0800 Subject: net: mvneta: add RGMII_RXID and RGMII_TXID support RGMII_RXID and RGMII_TX_ID share the same GMAC CTRL setting as RGMII or RGMII_ID. Signed-off-by: Jisheng Zhang Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index aebbc5399a06..7a6c65b44d7e 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -4099,6 +4099,8 @@ static int mvneta_port_power_up(struct mvneta_port *pp, int phy_mode) break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: ctrl |= MVNETA_GMAC2_PORT_RGMII; break; default: -- cgit From d6956ac87b5ff6841b09c273a70de86200d82019 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Wed, 29 Mar 2017 16:47:19 +0800 Subject: net: mvneta: set rx mode during resume if interface is running I found a bug by: 0. boot and start dhcp client 1. echo mem > /sys/power/state 2. resume back immediately 3. don't touch dhcp client to renew the lease 4. ping the gateway. No acks Usually, after step2, the DHCP lease isn't expired, so in theory we should resume all back. But in fact, it doesn't. It turns out the rx mode isn't resumed correctly. This patch fixes it by adding mvneta_set_rx_mode(dev) in the resume hook if interface is running. Signed-off-by: Jisheng Zhang Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 7a6c65b44d7e..34a3686d2ce6 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -4451,8 +4451,11 @@ static int mvneta_resume(struct device *device) mvneta_fixed_link_update(pp, dev->phydev); netif_device_attach(dev); - if (netif_running(dev)) + if (netif_running(dev)) { mvneta_open(dev); + mvneta_set_rx_mode(dev); + } + return 0; } #endif -- cgit From 70b03759e9ecfae400605fa34f3d7154cccbbba3 Mon Sep 17 00:00:00 2001 From: Erik Hugne Date: Wed, 29 Mar 2017 11:22:16 +0200 Subject: tipc: add support for stream/seqpacket socketpairs sockets A and B are connected back-to-back, similar to what AF_UNIX does. Signed-off-by: Erik Hugne Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 7130e73bd42c..1198dddf72e8 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -2511,6 +2511,16 @@ static int tipc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) } } +static int tipc_socketpair(struct socket *sock1, struct socket *sock2) +{ + struct tipc_sock *tsk2 = tipc_sk(sock2->sk); + struct tipc_sock *tsk1 = tipc_sk(sock1->sk); + + tipc_sk_finish_conn(tsk1, tsk2->portid, 0); + tipc_sk_finish_conn(tsk2, tsk1->portid, 0); + return 0; +} + /* Protocol switches for the various types of TIPC sockets */ static const struct proto_ops msg_ops = { @@ -2540,7 +2550,7 @@ static const struct proto_ops packet_ops = { .release = tipc_release, .bind = tipc_bind, .connect = tipc_connect, - .socketpair = sock_no_socketpair, + .socketpair = tipc_socketpair, .accept = tipc_accept, .getname = tipc_getname, .poll = tipc_poll, @@ -2561,7 +2571,7 @@ static const struct proto_ops stream_ops = { .release = tipc_release, .bind = tipc_bind, .connect = tipc_connect, - .socketpair = sock_no_socketpair, + .socketpair = tipc_socketpair, .accept = tipc_accept, .getname = tipc_getname, .poll = tipc_poll, -- cgit From 66bc1e8d5d1d156b1e85d8c6925225ad8cbdf523 Mon Sep 17 00:00:00 2001 From: Erik Hugne Date: Wed, 29 Mar 2017 11:22:17 +0200 Subject: tipc: allow rdm/dgram socketpairs for socketpairs using connectionless transport, we cache the respective node local TIPC portid to use in subsequent calls to send() in the socket's private data. Signed-off-by: Erik Hugne Signed-off-by: Parthasarathy Bhuvaragan Signed-off-by: David S. Miller --- net/tipc/socket.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 1198dddf72e8..15f6ce7bf868 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -2515,9 +2515,21 @@ static int tipc_socketpair(struct socket *sock1, struct socket *sock2) { struct tipc_sock *tsk2 = tipc_sk(sock2->sk); struct tipc_sock *tsk1 = tipc_sk(sock1->sk); - - tipc_sk_finish_conn(tsk1, tsk2->portid, 0); - tipc_sk_finish_conn(tsk2, tsk1->portid, 0); + u32 onode = tipc_own_addr(sock_net(sock1->sk)); + + tsk1->peer.family = AF_TIPC; + tsk1->peer.addrtype = TIPC_ADDR_ID; + tsk1->peer.scope = TIPC_NODE_SCOPE; + tsk1->peer.addr.id.ref = tsk2->portid; + tsk1->peer.addr.id.node = onode; + tsk2->peer.family = AF_TIPC; + tsk2->peer.addrtype = TIPC_ADDR_ID; + tsk2->peer.scope = TIPC_NODE_SCOPE; + tsk2->peer.addr.id.ref = tsk1->portid; + tsk2->peer.addr.id.node = onode; + + tipc_sk_finish_conn(tsk1, tsk2->portid, onode); + tipc_sk_finish_conn(tsk2, tsk1->portid, onode); return 0; } @@ -2529,7 +2541,7 @@ static const struct proto_ops msg_ops = { .release = tipc_release, .bind = tipc_bind, .connect = tipc_connect, - .socketpair = sock_no_socketpair, + .socketpair = tipc_socketpair, .accept = sock_no_accept, .getname = tipc_getname, .poll = tipc_poll, -- cgit From 41390895e50bc4f28abe384c6b35ac27464a20ec Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 28 Mar 2017 10:31:20 +0200 Subject: netfilter: ipvs: don't check for presence of nat extension Check for the NAT status bits, they are set once conntrack needs NAT in source or reply direction, this is slightly faster than nfct_nat() as that has to check the extension area. Signed-off-by: Florian Westphal --- net/netfilter/ipvs/ip_vs_ftp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index d30c327bb578..2e2bf7428cd1 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -260,7 +260,7 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, buf_len = strlen(buf); ct = nf_ct_get(skb, &ctinfo); - if (ct && !nf_ct_is_untracked(ct) && nfct_nat(ct)) { + if (ct && !nf_ct_is_untracked(ct) && (ct->status & IPS_NAT_MASK)) { /* If mangling fails this function will return 0 * which will cause the packet to be dropped. * Mangling can only fail under memory pressure, -- cgit From 848850a3e9161e2cdcbb5cef7edc21ba49252ad1 Mon Sep 17 00:00:00 2001 From: Varsha Rao Date: Tue, 28 Mar 2017 16:06:49 +0530 Subject: netfilter: ipvs: Replace kzalloc with kcalloc. Replace kzalloc with kcalloc. As kcalloc is preferred for allocating an array instead of kzalloc. This patch fixes the checkpatch issue. Signed-off-by: Varsha Rao --- net/netfilter/ipvs/ip_vs_sync.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index b03c28084f81..30d6b2cc00a0 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -1849,7 +1849,7 @@ int start_sync_thread(struct netns_ipvs *ipvs, struct ipvs_sync_daemon_cfg *c, if (state == IP_VS_STATE_MASTER) { struct ipvs_master_sync_state *ms; - ipvs->ms = kzalloc(count * sizeof(ipvs->ms[0]), GFP_KERNEL); + ipvs->ms = kcalloc(count, sizeof(ipvs->ms[0]), GFP_KERNEL); if (!ipvs->ms) goto out; ms = ipvs->ms; @@ -1862,7 +1862,7 @@ int start_sync_thread(struct netns_ipvs *ipvs, struct ipvs_sync_daemon_cfg *c, ms->ipvs = ipvs; } } else { - array = kzalloc(count * sizeof(struct task_struct *), + array = kcalloc(count, sizeof(struct task_struct *), GFP_KERNEL); if (!array) goto out; -- cgit From e24113769960980579610ecfd657bb17f19373a3 Mon Sep 17 00:00:00 2001 From: Arushi Singhal Date: Wed, 29 Mar 2017 20:27:52 +0530 Subject: ipvs: remove unused variable This patch uses the following coccinelle script to remove a variable that was simply used to store the return value of a function call before returning it: @@ identifier len,f; @@ -int len; ... when != len when strict -len = +return f(...); -return len; Signed-off-by: Arushi Singhal Signed-off-by: Simon Horman --- net/netfilter/ipvs/ip_vs_ftp.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 2e2bf7428cd1..6caf4459e981 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -482,11 +482,8 @@ static struct pernet_operations ip_vs_ftp_ops = { static int __init ip_vs_ftp_init(void) { - int rv; - - rv = register_pernet_subsys(&ip_vs_ftp_ops); /* rcu_barrier() is called by netns on error */ - return rv; + return register_pernet_subsys(&ip_vs_ftp_ops); } /* -- cgit From 5bacd77849173c8b7d4c75d5289557e73bd724db Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Wed, 29 Mar 2017 07:05:40 +0200 Subject: Revert "net: stmmac: enable multiple buffers" The commit aff3d9eff843 ("net: stmmac: enable multiple buffers") breaks numerous boards. while some patch exists for fixing some of it, dwmac-sunxi is still broken with it. Since this patch is very huge, it will be better to split it in smaller part. Signed-off-by: Corentin Labbe Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/chain_mode.c | 45 +- drivers/net/ethernet/stmicro/stmmac/ring_mode.c | 46 +- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 49 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 1306 +++++++-------------- 4 files changed, 473 insertions(+), 973 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c index 37881f81319e..01a8c020d6db 100644 --- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c @@ -26,15 +26,12 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) { - struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)p; + struct stmmac_priv *priv = (struct stmmac_priv *)p; + unsigned int entry = priv->cur_tx; + struct dma_desc *desc = priv->dma_tx + entry; unsigned int nopaged_len = skb_headlen(skb); - struct stmmac_priv *priv = tx_q->priv_data; - unsigned int entry = tx_q->cur_tx; unsigned int bmax, des2; unsigned int i = 1, len; - struct dma_desc *desc; - - desc = tx_q->dma_tx + entry; if (priv->plat->enh_desc) bmax = BUF_SIZE_8KiB; @@ -48,16 +45,16 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - tx_q->tx_skbuff_dma[entry].buf = des2; - tx_q->tx_skbuff_dma[entry].len = bmax; + priv->tx_skbuff_dma[entry].buf = des2; + priv->tx_skbuff_dma[entry].len = bmax; /* do not close the descriptor and do not set own bit */ priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE, 0, false); while (len != 0) { - tx_q->tx_skbuff[entry] = NULL; + priv->tx_skbuff[entry] = NULL; entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); - desc = tx_q->dma_tx + entry; + desc = priv->dma_tx + entry; if (len > bmax) { des2 = dma_map_single(priv->device, @@ -66,8 +63,8 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - tx_q->tx_skbuff_dma[entry].buf = des2; - tx_q->tx_skbuff_dma[entry].len = bmax; + priv->tx_skbuff_dma[entry].buf = des2; + priv->tx_skbuff_dma[entry].len = bmax; priv->hw->desc->prepare_tx_desc(desc, 0, bmax, csum, STMMAC_CHAIN_MODE, 1, false); @@ -80,8 +77,8 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - tx_q->tx_skbuff_dma[entry].buf = des2; - tx_q->tx_skbuff_dma[entry].len = len; + priv->tx_skbuff_dma[entry].buf = des2; + priv->tx_skbuff_dma[entry].len = len; /* last descriptor can be set now */ priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, STMMAC_CHAIN_MODE, 1, @@ -90,7 +87,7 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) } } - tx_q->cur_tx = entry; + priv->cur_tx = entry; return entry; } @@ -139,34 +136,32 @@ static void stmmac_init_dma_chain(void *des, dma_addr_t phy_addr, static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p) { - struct stmmac_rx_queue *rx_q = (struct stmmac_rx_queue *)priv_ptr; - struct stmmac_priv *priv = rx_q->priv_data; + struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; if (priv->hwts_rx_en && !priv->extend_desc) /* NOTE: Device will overwrite des3 with timestamp value if * 1588-2002 time stamping is enabled, hence reinitialize it * to keep explicit chaining in the descriptor. */ - p->des3 = cpu_to_le32((unsigned int)(rx_q->dma_rx_phy + - (((rx_q->dirty_rx) + 1) % + p->des3 = cpu_to_le32((unsigned int)(priv->dma_rx_phy + + (((priv->dirty_rx) + 1) % DMA_RX_SIZE) * sizeof(struct dma_desc))); } static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p) { - struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)priv_ptr; - struct stmmac_priv *priv = tx_q->priv_data; - unsigned int entry = tx_q->dirty_tx; + struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; + unsigned int entry = priv->dirty_tx; - if (tx_q->tx_skbuff_dma[entry].last_segment && !priv->extend_desc && + if (priv->tx_skbuff_dma[entry].last_segment && !priv->extend_desc && priv->hwts_tx_en) /* NOTE: Device will overwrite des3 with timestamp value if * 1588-2002 time stamping is enabled, hence reinitialize it * to keep explicit chaining in the descriptor. */ - p->des3 = cpu_to_le32((unsigned int)((tx_q->dma_tx_phy + - ((tx_q->dirty_tx + 1) % DMA_TX_SIZE)) + p->des3 = cpu_to_le32((unsigned int)((priv->dma_tx_phy + + ((priv->dirty_tx + 1) % DMA_TX_SIZE)) * sizeof(struct dma_desc))); } diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c index 31213e64513d..452f256ff03f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c @@ -26,17 +26,16 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) { - struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)p; + struct stmmac_priv *priv = (struct stmmac_priv *)p; + unsigned int entry = priv->cur_tx; + struct dma_desc *desc; unsigned int nopaged_len = skb_headlen(skb); - struct stmmac_priv *priv = tx_q->priv_data; - unsigned int entry = tx_q->cur_tx; unsigned int bmax, len, des2; - struct dma_desc *desc; if (priv->extend_desc) - desc = (struct dma_desc *)(tx_q->dma_etx + entry); + desc = (struct dma_desc *)(priv->dma_etx + entry); else - desc = tx_q->dma_tx + entry; + desc = priv->dma_tx + entry; if (priv->plat->enh_desc) bmax = BUF_SIZE_8KiB; @@ -53,29 +52,29 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) if (dma_mapping_error(priv->device, des2)) return -1; - tx_q->tx_skbuff_dma[entry].buf = des2; - tx_q->tx_skbuff_dma[entry].len = bmax; - tx_q->tx_skbuff_dma[entry].is_jumbo = true; + priv->tx_skbuff_dma[entry].buf = des2; + priv->tx_skbuff_dma[entry].len = bmax; + priv->tx_skbuff_dma[entry].is_jumbo = true; desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_RING_MODE, 0, false); - tx_q->tx_skbuff[entry] = NULL; + priv->tx_skbuff[entry] = NULL; entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); if (priv->extend_desc) - desc = (struct dma_desc *)(tx_q->dma_etx + entry); + desc = (struct dma_desc *)(priv->dma_etx + entry); else - desc = tx_q->dma_tx + entry; + desc = priv->dma_tx + entry; des2 = dma_map_single(priv->device, skb->data + bmax, len, DMA_TO_DEVICE); desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - tx_q->tx_skbuff_dma[entry].buf = des2; - tx_q->tx_skbuff_dma[entry].len = len; - tx_q->tx_skbuff_dma[entry].is_jumbo = true; + priv->tx_skbuff_dma[entry].buf = des2; + priv->tx_skbuff_dma[entry].len = len; + priv->tx_skbuff_dma[entry].is_jumbo = true; desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, @@ -86,15 +85,15 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - tx_q->tx_skbuff_dma[entry].buf = des2; - tx_q->tx_skbuff_dma[entry].len = nopaged_len; - tx_q->tx_skbuff_dma[entry].is_jumbo = true; + priv->tx_skbuff_dma[entry].buf = des2; + priv->tx_skbuff_dma[entry].len = nopaged_len; + priv->tx_skbuff_dma[entry].is_jumbo = true; desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum, STMMAC_RING_MODE, 0, true); } - tx_q->cur_tx = entry; + priv->cur_tx = entry; return entry; } @@ -126,13 +125,12 @@ static void stmmac_init_desc3(struct dma_desc *p) static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p) { - struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)priv_ptr; - struct stmmac_priv *priv = tx_q->priv_data; - unsigned int entry = tx_q->dirty_tx; + struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; + unsigned int entry = priv->dirty_tx; /* des3 is only used for jumbo frames tx or time stamping */ - if (unlikely(tx_q->tx_skbuff_dma[entry].is_jumbo || - (tx_q->tx_skbuff_dma[entry].last_segment && + if (unlikely(priv->tx_skbuff_dma[entry].is_jumbo || + (priv->tx_skbuff_dma[entry].last_segment && !priv->extend_desc && priv->hwts_tx_en))) p->des3 = 0; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 6ec671c9be84..cd8fb619b1e9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -46,35 +46,6 @@ struct stmmac_tx_info { bool is_jumbo; }; -/* Frequently used values are kept adjacent for cache effect */ -struct stmmac_tx_queue { - u32 queue_index; - struct stmmac_priv *priv_data; - struct dma_extended_desc *dma_etx ____cacheline_aligned_in_smp; - struct dma_desc *dma_tx; - struct sk_buff **tx_skbuff; - struct stmmac_tx_info *tx_skbuff_dma; - unsigned int cur_tx; - unsigned int dirty_tx; - dma_addr_t dma_tx_phy; - u32 tx_tail_addr; -}; - -struct stmmac_rx_queue { - u32 queue_index; - struct stmmac_priv *priv_data; - struct dma_extended_desc *dma_erx; - struct dma_desc *dma_rx ____cacheline_aligned_in_smp; - struct sk_buff **rx_skbuff; - dma_addr_t *rx_skbuff_dma; - struct napi_struct napi ____cacheline_aligned_in_smp; - unsigned int cur_rx; - unsigned int dirty_rx; - u32 rx_zeroc_thresh; - dma_addr_t dma_rx_phy; - u32 rx_tail_addr; -}; - struct stmmac_priv { /* Frequently used values are kept adjacent for cache effect */ struct dma_extended_desc *dma_etx ____cacheline_aligned_in_smp; @@ -85,22 +56,28 @@ struct stmmac_priv { u32 tx_count_frames; u32 tx_coal_frames; u32 tx_coal_timer; + struct stmmac_tx_info *tx_skbuff_dma; + dma_addr_t dma_tx_phy; int tx_coalesce; int hwts_tx_en; bool tx_path_in_lpi_mode; struct timer_list txtimer; bool tso; - /* TX Queue */ - struct stmmac_tx_queue *tx_queue; - - /* RX Queue */ - struct stmmac_rx_queue *rx_queue; - + struct dma_desc *dma_rx ____cacheline_aligned_in_smp; + struct dma_extended_desc *dma_erx; + struct sk_buff **rx_skbuff; + unsigned int cur_rx; + unsigned int dirty_rx; unsigned int dma_buf_sz; unsigned int rx_copybreak; + unsigned int rx_zeroc_thresh; u32 rx_riwt; int hwts_rx_en; + dma_addr_t *rx_skbuff_dma; + dma_addr_t dma_rx_phy; + + struct napi_struct napi ____cacheline_aligned_in_smp; void __iomem *ioaddr; struct net_device *dev; @@ -142,6 +119,8 @@ struct stmmac_priv { spinlock_t ptp_lock; void __iomem *mmcaddr; void __iomem *ptpaddr; + u32 rx_tail_addr; + u32 tx_tail_addr; u32 mss; #ifdef CONFIG_DEBUG_FS diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index fe1d9592956f..43361f324229 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -185,38 +185,26 @@ static void print_pkt(unsigned char *buf, int len) print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); } -/** - * stmmac_tx_avail - Get tx queue availability - * @priv: driver private structure - * @queue: TX queue index - */ -static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue) +static inline u32 stmmac_tx_avail(struct stmmac_priv *priv) { - struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; u32 avail; - if (tx_q->dirty_tx > tx_q->cur_tx) - avail = tx_q->dirty_tx - tx_q->cur_tx - 1; + if (priv->dirty_tx > priv->cur_tx) + avail = priv->dirty_tx - priv->cur_tx - 1; else - avail = DMA_TX_SIZE - tx_q->cur_tx + tx_q->dirty_tx - 1; + avail = DMA_TX_SIZE - priv->cur_tx + priv->dirty_tx - 1; return avail; } -/** - * stmmac_rx_dirty - Get RX queue dirty - * @priv: driver private structure - * @queue: RX queue index - */ -static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue) +static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; u32 dirty; - if (rx_q->dirty_rx <= rx_q->cur_rx) - dirty = rx_q->cur_rx - rx_q->dirty_rx; + if (priv->dirty_rx <= priv->cur_rx) + dirty = priv->cur_rx - priv->dirty_rx; else - dirty = DMA_RX_SIZE - rx_q->dirty_rx + rx_q->cur_rx; + dirty = DMA_RX_SIZE - priv->dirty_rx + priv->cur_rx; return dirty; } @@ -244,19 +232,9 @@ static inline void stmmac_hw_fix_mac_speed(struct stmmac_priv *priv) */ static void stmmac_enable_eee_mode(struct stmmac_priv *priv) { - u32 tx_cnt = priv->plat->tx_queues_to_use; - u32 queue; - - /* check if all TX queues have the work finished */ - for (queue = 0; queue < tx_cnt; queue++) { - struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; - - if (tx_q->dirty_tx != tx_q->cur_tx) - return; /* still unfinished work */ - } - /* Check and enter in LPI mode */ - if (!priv->tx_path_in_lpi_mode) + if ((priv->dirty_tx == priv->cur_tx) && + (priv->tx_path_in_lpi_mode == false)) priv->hw->mac->set_eee_mode(priv->hw, priv->plat->en_tx_lpi_clockgating); } @@ -913,40 +891,20 @@ static int stmmac_init_phy(struct net_device *dev) static void stmmac_display_rings(struct stmmac_priv *priv) { - u32 rx_cnt = priv->plat->rx_queues_to_use; - u32 tx_cnt = priv->plat->tx_queues_to_use; void *head_rx, *head_tx; - u32 queue; - - /* Display RX rings */ - for (queue = 0; queue < rx_cnt; queue++) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - - pr_info("\tRX Queue %d rings\n", queue); - - if (priv->extend_desc) - head_rx = (void *)rx_q->dma_erx; - else - head_rx = (void *)rx_q->dma_rx; - /* Display Rx ring */ - priv->hw->desc->display_ring(head_rx, DMA_RX_SIZE, true); + if (priv->extend_desc) { + head_rx = (void *)priv->dma_erx; + head_tx = (void *)priv->dma_etx; + } else { + head_rx = (void *)priv->dma_rx; + head_tx = (void *)priv->dma_tx; } - /* Display TX rings */ - for (queue = 0; queue < tx_cnt; queue++) { - struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; - - pr_info("\tTX Queue %d rings\n", queue); - - if (priv->extend_desc) - head_tx = (void *)tx_q->dma_etx; - else - head_tx = (void *)tx_q->dma_tx; - - /* Display Tx ring */ - priv->hw->desc->display_ring(head_tx, DMA_TX_SIZE, false); - } + /* Display Rx ring */ + priv->hw->desc->display_ring(head_rx, DMA_RX_SIZE, true); + /* Display Tx ring */ + priv->hw->desc->display_ring(head_tx, DMA_TX_SIZE, false); } static int stmmac_set_bfsize(int mtu, int bufsize) @@ -966,86 +924,48 @@ static int stmmac_set_bfsize(int mtu, int bufsize) } /** - * stmmac_clear_rx_descriptors - clear the descriptors of a RX queue + * stmmac_clear_descriptors - clear descriptors * @priv: driver private structure - * @queue: RX queue index - * Description: this function is called to clear the RX descriptors + * Description: this function is called to clear the tx and rx descriptors * in case of both basic and extended descriptors are used. */ -static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue) +static void stmmac_clear_descriptors(struct stmmac_priv *priv) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - u32 i = 0; + int i; - /* Clear the RX descriptors */ + /* Clear the Rx/Tx descriptors */ for (i = 0; i < DMA_RX_SIZE; i++) if (priv->extend_desc) - priv->hw->desc->init_rx_desc(&rx_q->dma_erx[i].basic, + priv->hw->desc->init_rx_desc(&priv->dma_erx[i].basic, priv->use_riwt, priv->mode, (i == DMA_RX_SIZE - 1)); else - priv->hw->desc->init_rx_desc(&rx_q->dma_rx[i], + priv->hw->desc->init_rx_desc(&priv->dma_rx[i], priv->use_riwt, priv->mode, (i == DMA_RX_SIZE - 1)); -} - -/** - * stmmac_clear_tx_descriptors - clear the descriptors of a TX queue - * @priv: driver private structure - * @queue: TX queue index - * Description: this function is called to clear the TX descriptors - * in case of both basic and extended descriptors are used. - */ -static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv, u32 queue) -{ - struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; - u32 i = 0; - - /* Clear the TX descriptors */ for (i = 0; i < DMA_TX_SIZE; i++) if (priv->extend_desc) - priv->hw->desc->init_tx_desc(&tx_q->dma_etx[i].basic, + priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic, priv->mode, (i == DMA_TX_SIZE - 1)); else - priv->hw->desc->init_tx_desc(&tx_q->dma_tx[i], + priv->hw->desc->init_tx_desc(&priv->dma_tx[i], priv->mode, (i == DMA_TX_SIZE - 1)); } -/** - * stmmac_clear_descriptors - clear descriptors - * @priv: driver private structure - * Description: this function is called to clear the tx and rx descriptors - * in case of both basic and extended descriptors are used. - */ -static void stmmac_clear_descriptors(struct stmmac_priv *priv) -{ - u32 rx_queue_cnt = priv->plat->rx_queues_to_use; - u32 tx_queue_cnt = priv->plat->tx_queues_to_use; - u32 queue; - - for (queue = 0; queue < rx_queue_cnt; queue++) - stmmac_clear_rx_descriptors(priv, queue); - - for (queue = 0; queue < tx_queue_cnt; queue++) - stmmac_clear_tx_descriptors(priv, queue); -} - /** * stmmac_init_rx_buffers - init the RX descriptor buffer. * @priv: driver private structure * @p: descriptor pointer * @i: descriptor index * @flags: gfp flag. - * @queue: RX queue index * Description: this function is called to allocate a receive buffer, perform * the DMA mapping and init the descriptor. */ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, - int i, gfp_t flags, u32 queue) + int i, gfp_t flags) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; struct sk_buff *skb; skb = __netdev_alloc_skb_ip_align(priv->dev, priv->dma_buf_sz, flags); @@ -1054,20 +974,20 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, "%s: Rx init fails; skb is NULL\n", __func__); return -ENOMEM; } - rx_q->rx_skbuff[i] = skb; - rx_q->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, + priv->rx_skbuff[i] = skb; + priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, priv->dma_buf_sz, DMA_FROM_DEVICE); - if (dma_mapping_error(priv->device, rx_q->rx_skbuff_dma[i])) { + if (dma_mapping_error(priv->device, priv->rx_skbuff_dma[i])) { netdev_err(priv->dev, "%s: DMA mapping error\n", __func__); dev_kfree_skb_any(skb); return -EINVAL; } if (priv->synopsys_id >= DWMAC_CORE_4_00) - p->des0 = cpu_to_le32(rx_q->rx_skbuff_dma[i]); + p->des0 = cpu_to_le32(priv->rx_skbuff_dma[i]); else - p->des2 = cpu_to_le32(rx_q->rx_skbuff_dma[i]); + p->des2 = cpu_to_le32(priv->rx_skbuff_dma[i]); if ((priv->hw->mode->init_desc3) && (priv->dma_buf_sz == BUF_SIZE_16KiB)) @@ -1076,136 +996,30 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, return 0; } -/** - * stmmac_free_rx_buffers - free RX buffers. - * @priv: driver private structure - * @queue: RX queue index - * @i: buffer index - */ -static void stmmac_free_rx_buffers(struct stmmac_priv *priv, u32 queue, int i) +static void stmmac_free_rx_buffers(struct stmmac_priv *priv, int i) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - - if (rx_q->rx_skbuff[i]) { - dma_unmap_single(priv->device, rx_q->rx_skbuff_dma[i], + if (priv->rx_skbuff[i]) { + dma_unmap_single(priv->device, priv->rx_skbuff_dma[i], priv->dma_buf_sz, DMA_FROM_DEVICE); - dev_kfree_skb_any(rx_q->rx_skbuff[i]); - } - rx_q->rx_skbuff[i] = NULL; -} - -/** - * stmmac_free_tx_buffers - free RX buffers. - * @priv: driver private structure - * @queue: RX queue index - * @i: buffer index - */ -static void stmmac_free_tx_buffers(struct stmmac_priv *priv, u32 queue, u32 i) -{ - struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; - - if (tx_q->tx_skbuff_dma[i].buf) { - if (tx_q->tx_skbuff_dma[i].map_as_page) - dma_unmap_page(priv->device, - tx_q->tx_skbuff_dma[i].buf, - tx_q->tx_skbuff_dma[i].len, - DMA_TO_DEVICE); - else - dma_unmap_single(priv->device, - tx_q->tx_skbuff_dma[i].buf, - tx_q->tx_skbuff_dma[i].len, - DMA_TO_DEVICE); - } - - if (tx_q->tx_skbuff[i]) { - dev_kfree_skb_any(tx_q->tx_skbuff[i]); - tx_q->tx_skbuff[i] = NULL; - tx_q->tx_skbuff_dma[i].buf = 0; - tx_q->tx_skbuff_dma[i].map_as_page = false; - } -} - -/** - * init_tx_dma_desc_rings - init the TX descriptor rings - * @dev: net device structure - * Description: this function initializes the DMA TX descriptors - * and allocates the socket buffers. It suppors the chained and ring - * modes. - */ -static int init_tx_dma_desc_rings(struct net_device *dev) -{ - struct stmmac_priv *priv = netdev_priv(dev); - u32 tx_queue_cnt = priv->plat->tx_queues_to_use; - u32 queue; - int i = 0; - - for (queue = 0; queue < tx_queue_cnt; queue++) { - struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; - - netif_dbg(priv, probe, priv->dev, - "(%s) dma_tx_phy=0x%08x\n", __func__, - (u32)tx_q->dma_tx_phy); - - /* Setup the chained descriptor addresses */ - if (priv->mode == STMMAC_CHAIN_MODE) { - if (priv->extend_desc) - priv->hw->mode->init(tx_q->dma_etx, - tx_q->dma_tx_phy, - DMA_TX_SIZE, 1); - else - priv->hw->mode->init(tx_q->dma_tx, - tx_q->dma_tx_phy, - DMA_TX_SIZE, 0); - } - - for (i = 0; i < DMA_TX_SIZE; i++) { - struct dma_desc *p; - - if (priv->extend_desc) - p = &((tx_q->dma_etx + i)->basic); - else - p = tx_q->dma_tx + i; - - if (priv->synopsys_id >= DWMAC_CORE_4_00) { - p->des0 = 0; - p->des1 = 0; - p->des2 = 0; - p->des3 = 0; - } else { - p->des2 = 0; - } - - tx_q->tx_skbuff_dma[i].buf = 0; - tx_q->tx_skbuff_dma[i].map_as_page = false; - tx_q->tx_skbuff_dma[i].len = 0; - tx_q->tx_skbuff_dma[i].last_segment = false; - tx_q->tx_skbuff[i] = NULL; - } - - tx_q->dirty_tx = 0; - tx_q->cur_tx = 0; - netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue)); + dev_kfree_skb_any(priv->rx_skbuff[i]); } - - return 0; + priv->rx_skbuff[i] = NULL; } /** - * init_rx_dma_desc_rings - init the RX descriptor rings + * init_dma_desc_rings - init the RX/TX descriptor rings * @dev: net device structure * @flags: gfp flag. - * Description: this function initializes the DMA RX descriptors - * and allocates the socket buffers. It suppors the chained and ring + * Description: this function initializes the DMA RX/TX descriptors + * and allocates the socket buffers. It supports the chained and ring * modes. */ -static int init_rx_dma_desc_rings(struct net_device *dev, gfp_t flags) +static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) { + int i; struct stmmac_priv *priv = netdev_priv(dev); - u32 rx_count = priv->plat->rx_queues_to_use; unsigned int bfsize = 0; int ret = -ENOMEM; - u32 queue; - int i; if (priv->hw->mode->set_16kib_bfsize) bfsize = priv->hw->mode->set_16kib_bfsize(dev->mtu); @@ -1215,350 +1029,235 @@ static int init_rx_dma_desc_rings(struct net_device *dev, gfp_t flags) priv->dma_buf_sz = bfsize; + netif_dbg(priv, probe, priv->dev, + "(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", + __func__, (u32)priv->dma_rx_phy, (u32)priv->dma_tx_phy); + /* RX INITIALIZATION */ netif_dbg(priv, probe, priv->dev, "SKB addresses:\nskb\t\tskb data\tdma data\n"); - for (queue = 0; queue < rx_count; queue++) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - - netif_dbg(priv, probe, priv->dev, - "(%s) dma_rx_phy=0x%08x\n", __func__, - (u32)rx_q->dma_rx_phy); - - for (i = 0; i < DMA_RX_SIZE; i++) { - struct dma_desc *p; - - if (priv->extend_desc) - p = &((rx_q->dma_erx + i)->basic); - else - p = rx_q->dma_rx + i; - - ret = stmmac_init_rx_buffers(priv, p, i, flags, queue); - if (ret) - goto err_init_rx_buffers; - - netif_dbg(priv, probe, priv->dev, "[%p]\t[%p]\t[%x]\n", - rx_q->rx_skbuff[i], - rx_q->rx_skbuff[i]->data, - (unsigned int)rx_q->rx_skbuff_dma[i]); - } + for (i = 0; i < DMA_RX_SIZE; i++) { + struct dma_desc *p; + if (priv->extend_desc) + p = &((priv->dma_erx + i)->basic); + else + p = priv->dma_rx + i; - rx_q->cur_rx = 0; - rx_q->dirty_rx = (unsigned int)(i - DMA_RX_SIZE); + ret = stmmac_init_rx_buffers(priv, p, i, flags); + if (ret) + goto err_init_rx_buffers; - stmmac_clear_rx_descriptors(priv, queue); + netif_dbg(priv, probe, priv->dev, "[%p]\t[%p]\t[%x]\n", + priv->rx_skbuff[i], priv->rx_skbuff[i]->data, + (unsigned int)priv->rx_skbuff_dma[i]); + } + priv->cur_rx = 0; + priv->dirty_rx = (unsigned int)(i - DMA_RX_SIZE); + buf_sz = bfsize; - if (priv->mode == STMMAC_CHAIN_MODE) { - if (priv->extend_desc) - priv->hw->mode->init(rx_q->dma_erx, - rx_q->dma_rx_phy, - DMA_RX_SIZE, 1); - else - priv->hw->mode->init(rx_q->dma_rx, - rx_q->dma_rx_phy, - DMA_RX_SIZE, 0); + /* Setup the chained descriptor addresses */ + if (priv->mode == STMMAC_CHAIN_MODE) { + if (priv->extend_desc) { + priv->hw->mode->init(priv->dma_erx, priv->dma_rx_phy, + DMA_RX_SIZE, 1); + priv->hw->mode->init(priv->dma_etx, priv->dma_tx_phy, + DMA_TX_SIZE, 1); + } else { + priv->hw->mode->init(priv->dma_rx, priv->dma_rx_phy, + DMA_RX_SIZE, 0); + priv->hw->mode->init(priv->dma_tx, priv->dma_tx_phy, + DMA_TX_SIZE, 0); } } - buf_sz = bfsize; - - return 0; + /* TX INITIALIZATION */ + for (i = 0; i < DMA_TX_SIZE; i++) { + struct dma_desc *p; + if (priv->extend_desc) + p = &((priv->dma_etx + i)->basic); + else + p = priv->dma_tx + i; -err_init_rx_buffers: - while (queue-- >= 0) { - while (--i >= 0) - stmmac_free_rx_buffers(priv, queue, i); + if (priv->synopsys_id >= DWMAC_CORE_4_00) { + p->des0 = 0; + p->des1 = 0; + p->des2 = 0; + p->des3 = 0; + } else { + p->des2 = 0; + } - i = DMA_RX_SIZE; + priv->tx_skbuff_dma[i].buf = 0; + priv->tx_skbuff_dma[i].map_as_page = false; + priv->tx_skbuff_dma[i].len = 0; + priv->tx_skbuff_dma[i].last_segment = false; + priv->tx_skbuff[i] = NULL; } - return ret; -} - -/** - * init_dma_desc_rings - init the RX/TX descriptor rings - * @dev: net device structure - * @flags: gfp flag. - * Description: this function initializes the DMA RX/TX descriptors - * and allocates the socket buffers. It suppors the chained and ring - * modes. - */ -static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) -{ - struct stmmac_priv *priv = netdev_priv(dev); - int ret = init_rx_dma_desc_rings(dev, flags); - - if (ret) - return ret; + priv->dirty_tx = 0; + priv->cur_tx = 0; + netdev_reset_queue(priv->dev); - ret = init_tx_dma_desc_rings(dev); + stmmac_clear_descriptors(priv); if (netif_msg_hw(priv)) stmmac_display_rings(priv); + return 0; +err_init_rx_buffers: + while (--i >= 0) + stmmac_free_rx_buffers(priv, i); return ret; } -static void dma_free_rx_skbufs(struct stmmac_priv *priv, u32 queue) +static void dma_free_rx_skbufs(struct stmmac_priv *priv) { int i; for (i = 0; i < DMA_RX_SIZE; i++) - stmmac_free_rx_buffers(priv, queue, i); + stmmac_free_rx_buffers(priv, i); } -static void dma_free_tx_skbufs(struct stmmac_priv *priv, u32 queue) +static void dma_free_tx_skbufs(struct stmmac_priv *priv) { int i; - for (i = 0; i < DMA_TX_SIZE; i++) - stmmac_free_tx_buffers(priv, queue, i); -} - -/** - * free_rx_dma_desc_resources - free RX DMA resources - * @priv: driver private structure - */ -static void free_rx_dma_desc_resources(struct stmmac_priv *priv) -{ - u32 rx_count = priv->plat->rx_queues_to_use; - u32 queue = 0; - - if (!priv->rx_queue) - return; - - /* Free RX queue resources */ - for (queue = 0; queue < rx_count; queue++) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - - if (!rx_q) - break; - - /* Release the DMA RX socket buffers */ - dma_free_rx_skbufs(priv, queue); - - kfree(rx_q->rx_skbuff); - - kfree(rx_q->rx_skbuff_dma); - - if (!priv->extend_desc) - dma_free_coherent(priv->device, - DMA_RX_SIZE * sizeof(struct dma_desc), - rx_q->dma_rx, - rx_q->dma_rx_phy); - else - dma_free_coherent(priv->device, DMA_RX_SIZE * - sizeof(struct dma_extended_desc), - rx_q->dma_erx, - rx_q->dma_rx_phy); - } - - kfree(priv->rx_queue); -} - -/** - * free_tx_dma_desc_resources - free TX DMA resources - * @priv: driver private structure - */ -static void free_tx_dma_desc_resources(struct stmmac_priv *priv) -{ - u32 tx_count = priv->plat->tx_queues_to_use; - u32 queue = 0; - - if (!priv->tx_queue) - return; - - /* Free TX queue resources */ - for (queue = 0; queue < tx_count; queue++) { - struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; - - if (!tx_q) - break; - - /* Release the DMA TX socket buffers */ - dma_free_tx_skbufs(priv, queue); - - kfree(tx_q->tx_skbuff); - - kfree(tx_q->tx_skbuff_dma); - - if (!priv->extend_desc) - dma_free_coherent(priv->device, - DMA_TX_SIZE * sizeof(struct dma_desc), - tx_q->dma_tx, - tx_q->dma_tx_phy); - else - dma_free_coherent(priv->device, DMA_TX_SIZE * - sizeof(struct dma_extended_desc), - tx_q->dma_etx, - tx_q->dma_tx_phy); - } - - kfree(priv->tx_queue); -} - -/** - * free_dma_desc_resources - free All DMA resources - * @priv: driver private structure - */ -static void free_dma_desc_resources(struct stmmac_priv *priv) -{ - free_rx_dma_desc_resources(priv); - free_tx_dma_desc_resources(priv); -} - -/** - * alloc_rx_dma_desc_resources - alloc RX resources. - * @priv: private structure - * Description: according to which descriptor can be used (extend or basic) - * this function allocates the resources for RX paths. It pre-allocates the - * RX socket buffer in order to allow zero-copy mechanism. - */ -static int alloc_rx_dma_desc_resources(struct stmmac_priv *priv) -{ - u32 rx_count = priv->plat->rx_queues_to_use; - int ret = -ENOMEM; - u32 queue = 0; - - /* Allocate RX queues array */ - priv->rx_queue = kmalloc_array(rx_count, - sizeof(struct stmmac_rx_queue), - GFP_KERNEL); - if (!priv->rx_queue) { - kfree(priv->rx_queue); - return -ENOMEM; - } - - /* RX queues buffers and DMA */ - for (queue = 0; queue < rx_count; queue++) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - - rx_q->queue_index = queue; - rx_q->priv_data = priv; - - rx_q->rx_skbuff_dma = kmalloc_array(DMA_RX_SIZE, - sizeof(dma_addr_t), - GFP_KERNEL); - if (!rx_q->rx_skbuff_dma) - goto err_dma_buffers; - - rx_q->rx_skbuff = kmalloc_array(DMA_RX_SIZE, - sizeof(struct sk_buff *), - GFP_KERNEL); - if (!rx_q->rx_skbuff) - goto err_dma_buffers; - - if (priv->extend_desc) { - rx_q->dma_erx = dma_zalloc_coherent(priv->device, - (DMA_RX_SIZE * sizeof(struct dma_extended_desc)), - &rx_q->dma_rx_phy, GFP_KERNEL); - - if (!rx_q->dma_erx) - goto err_dma_buffers; - } else { - rx_q->dma_rx = dma_zalloc_coherent(priv->device, - (DMA_RX_SIZE * sizeof(struct dma_desc)), - &rx_q->dma_rx_phy, GFP_KERNEL); + for (i = 0; i < DMA_TX_SIZE; i++) { + if (priv->tx_skbuff_dma[i].buf) { + if (priv->tx_skbuff_dma[i].map_as_page) + dma_unmap_page(priv->device, + priv->tx_skbuff_dma[i].buf, + priv->tx_skbuff_dma[i].len, + DMA_TO_DEVICE); + else + dma_unmap_single(priv->device, + priv->tx_skbuff_dma[i].buf, + priv->tx_skbuff_dma[i].len, + DMA_TO_DEVICE); + } - if (!rx_q->dma_rx) - goto err_dma_buffers; + if (priv->tx_skbuff[i]) { + dev_kfree_skb_any(priv->tx_skbuff[i]); + priv->tx_skbuff[i] = NULL; + priv->tx_skbuff_dma[i].buf = 0; + priv->tx_skbuff_dma[i].map_as_page = false; } } - - return 0; - -err_dma_buffers: - free_rx_dma_desc_resources(priv); - - return ret; } /** - * alloc_tx_dma_desc_resources - alloc TX resources. + * alloc_dma_desc_resources - alloc TX/RX resources. * @priv: private structure * Description: according to which descriptor can be used (extend or basic) - * this function allocates the resources for TX paths. + * this function allocates the resources for TX and RX paths. In case of + * reception, for example, it pre-allocated the RX socket buffer in order to + * allow zero-copy mechanism. */ -static int alloc_tx_dma_desc_resources(struct stmmac_priv *priv) +static int alloc_dma_desc_resources(struct stmmac_priv *priv) { - u32 tx_count = priv->plat->tx_queues_to_use; int ret = -ENOMEM; - u32 queue = 0; - /* Allocate TX queues array */ - priv->tx_queue = kmalloc_array(tx_count, - sizeof(struct stmmac_tx_queue), - GFP_KERNEL); - if (!priv->tx_queue) + priv->rx_skbuff_dma = kmalloc_array(DMA_RX_SIZE, sizeof(dma_addr_t), + GFP_KERNEL); + if (!priv->rx_skbuff_dma) return -ENOMEM; - /* TX queues buffers and DMA */ - for (queue = 0; queue < tx_count; queue++) { - struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; - - tx_q->queue_index = queue; - tx_q->priv_data = priv; - - tx_q->tx_skbuff_dma = kmalloc_array(DMA_TX_SIZE, - sizeof(struct stmmac_tx_info), - GFP_KERNEL); - - if (!tx_q->tx_skbuff_dma) - goto err_dma_buffers; - - tx_q->tx_skbuff = kmalloc_array(DMA_TX_SIZE, - sizeof(struct sk_buff *), + priv->rx_skbuff = kmalloc_array(DMA_RX_SIZE, sizeof(struct sk_buff *), + GFP_KERNEL); + if (!priv->rx_skbuff) + goto err_rx_skbuff; + + priv->tx_skbuff_dma = kmalloc_array(DMA_TX_SIZE, + sizeof(*priv->tx_skbuff_dma), + GFP_KERNEL); + if (!priv->tx_skbuff_dma) + goto err_tx_skbuff_dma; + + priv->tx_skbuff = kmalloc_array(DMA_TX_SIZE, sizeof(struct sk_buff *), + GFP_KERNEL); + if (!priv->tx_skbuff) + goto err_tx_skbuff; + + if (priv->extend_desc) { + priv->dma_erx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE * + sizeof(struct + dma_extended_desc), + &priv->dma_rx_phy, GFP_KERNEL); - if (!tx_q->tx_skbuff) - goto err_dma_buffers; - - if (priv->extend_desc) { - tx_q->dma_etx = - dma_zalloc_coherent(priv->device, - (DMA_TX_SIZE * sizeof(struct dma_extended_desc)), - &tx_q->dma_tx_phy, GFP_KERNEL); - - if (!tx_q->dma_etx) - goto err_dma_buffers; - } else { - tx_q->dma_tx = - dma_zalloc_coherent(priv->device, - (DMA_TX_SIZE * sizeof(struct dma_desc)), - &tx_q->dma_tx_phy, GFP_KERNEL); + if (!priv->dma_erx) + goto err_dma; - if (!tx_q->dma_tx) - goto err_dma_buffers; + priv->dma_etx = dma_zalloc_coherent(priv->device, DMA_TX_SIZE * + sizeof(struct + dma_extended_desc), + &priv->dma_tx_phy, + GFP_KERNEL); + if (!priv->dma_etx) { + dma_free_coherent(priv->device, DMA_RX_SIZE * + sizeof(struct dma_extended_desc), + priv->dma_erx, priv->dma_rx_phy); + goto err_dma; + } + } else { + priv->dma_rx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE * + sizeof(struct dma_desc), + &priv->dma_rx_phy, + GFP_KERNEL); + if (!priv->dma_rx) + goto err_dma; + + priv->dma_tx = dma_zalloc_coherent(priv->device, DMA_TX_SIZE * + sizeof(struct dma_desc), + &priv->dma_tx_phy, + GFP_KERNEL); + if (!priv->dma_tx) { + dma_free_coherent(priv->device, DMA_RX_SIZE * + sizeof(struct dma_desc), + priv->dma_rx, priv->dma_rx_phy); + goto err_dma; } } return 0; -err_dma_buffers: - free_tx_dma_desc_resources(priv); - +err_dma: + kfree(priv->tx_skbuff); +err_tx_skbuff: + kfree(priv->tx_skbuff_dma); +err_tx_skbuff_dma: + kfree(priv->rx_skbuff); +err_rx_skbuff: + kfree(priv->rx_skbuff_dma); return ret; } -/** - * alloc_dma_desc_resources - alloc TX/RX resources. - * @priv: private structure - * Description: according to which descriptor can be used (extend or basic) - * this function allocates the resources for TX and RX paths. In case of - * reception, for example, it pre-allocated the RX socket buffer in order to - * allow zero-copy mechanism. - */ -static int alloc_dma_desc_resources(struct stmmac_priv *priv) +static void free_dma_desc_resources(struct stmmac_priv *priv) { - int ret = 0; - - ret = alloc_tx_dma_desc_resources(priv); - if (ret) - return ret; - - ret = alloc_rx_dma_desc_resources(priv); - - return ret; + /* Release the DMA TX/RX socket buffers */ + dma_free_rx_skbufs(priv); + dma_free_tx_skbufs(priv); + + /* Free DMA regions of consistent memory previously allocated */ + if (!priv->extend_desc) { + dma_free_coherent(priv->device, + DMA_TX_SIZE * sizeof(struct dma_desc), + priv->dma_tx, priv->dma_tx_phy); + dma_free_coherent(priv->device, + DMA_RX_SIZE * sizeof(struct dma_desc), + priv->dma_rx, priv->dma_rx_phy); + } else { + dma_free_coherent(priv->device, DMA_TX_SIZE * + sizeof(struct dma_extended_desc), + priv->dma_etx, priv->dma_tx_phy); + dma_free_coherent(priv->device, DMA_RX_SIZE * + sizeof(struct dma_extended_desc), + priv->dma_erx, priv->dma_rx_phy); + } + kfree(priv->rx_skbuff_dma); + kfree(priv->rx_skbuff); + kfree(priv->tx_skbuff_dma); + kfree(priv->tx_skbuff); } /** @@ -1722,28 +1421,26 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv) /** * stmmac_tx_clean - to manage the transmission completion * @priv: driver private structure - * @queue: TX queue index * Description: it reclaims the transmit resources after transmission completes. */ -static void stmmac_tx_clean(struct stmmac_priv *priv, u32 queue) +static void stmmac_tx_clean(struct stmmac_priv *priv) { - struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; unsigned int bytes_compl = 0, pkts_compl = 0; - unsigned int entry = tx_q->dirty_tx; + unsigned int entry = priv->dirty_tx; netif_tx_lock(priv->dev); priv->xstats.tx_clean++; - while (entry != tx_q->cur_tx) { - struct sk_buff *skb = tx_q->tx_skbuff[entry]; + while (entry != priv->cur_tx) { + struct sk_buff *skb = priv->tx_skbuff[entry]; struct dma_desc *p; int status; if (priv->extend_desc) - p = (struct dma_desc *)(tx_q->dma_etx + entry); + p = (struct dma_desc *)(priv->dma_etx + entry); else - p = tx_q->dma_tx + entry; + p = priv->dma_tx + entry; status = priv->hw->desc->tx_status(&priv->dev->stats, &priv->xstats, p, @@ -1764,50 +1461,48 @@ static void stmmac_tx_clean(struct stmmac_priv *priv, u32 queue) stmmac_get_tx_hwtstamp(priv, p, skb); } - if (likely(tx_q->tx_skbuff_dma[entry].buf)) { - if (tx_q->tx_skbuff_dma[entry].map_as_page) + if (likely(priv->tx_skbuff_dma[entry].buf)) { + if (priv->tx_skbuff_dma[entry].map_as_page) dma_unmap_page(priv->device, - tx_q->tx_skbuff_dma[entry].buf, - tx_q->tx_skbuff_dma[entry].len, + priv->tx_skbuff_dma[entry].buf, + priv->tx_skbuff_dma[entry].len, DMA_TO_DEVICE); else dma_unmap_single(priv->device, - tx_q->tx_skbuff_dma[entry].buf, - tx_q->tx_skbuff_dma[entry].len, + priv->tx_skbuff_dma[entry].buf, + priv->tx_skbuff_dma[entry].len, DMA_TO_DEVICE); - tx_q->tx_skbuff_dma[entry].buf = 0; - tx_q->tx_skbuff_dma[entry].len = 0; - tx_q->tx_skbuff_dma[entry].map_as_page = false; + priv->tx_skbuff_dma[entry].buf = 0; + priv->tx_skbuff_dma[entry].len = 0; + priv->tx_skbuff_dma[entry].map_as_page = false; } if (priv->hw->mode->clean_desc3) - priv->hw->mode->clean_desc3(tx_q, p); + priv->hw->mode->clean_desc3(priv, p); - tx_q->tx_skbuff_dma[entry].last_segment = false; - tx_q->tx_skbuff_dma[entry].is_jumbo = false; + priv->tx_skbuff_dma[entry].last_segment = false; + priv->tx_skbuff_dma[entry].is_jumbo = false; if (likely(skb != NULL)) { pkts_compl++; bytes_compl += skb->len; dev_consume_skb_any(skb); - tx_q->tx_skbuff[entry] = NULL; + priv->tx_skbuff[entry] = NULL; } priv->hw->desc->release_tx_desc(p, priv->mode); entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); } - tx_q->dirty_tx = entry; + priv->dirty_tx = entry; - netdev_tx_completed_queue(netdev_get_tx_queue(priv->dev, queue), - pkts_compl, bytes_compl); + netdev_completed_queue(priv->dev, pkts_compl, bytes_compl); - if (unlikely(netif_tx_queue_stopped(netdev_get_tx_queue(priv->dev, - queue))) && - stmmac_tx_avail(priv, queue) > STMMAC_TX_THRESH) { + if (unlikely(netif_queue_stopped(priv->dev) && + stmmac_tx_avail(priv) > STMMAC_TX_THRESH)) { netif_dbg(priv, tx_done, priv->dev, "%s: restart transmit\n", __func__); - netif_tx_wake_queue(netdev_get_tx_queue(priv->dev, queue)); + netif_wake_queue(priv->dev); } if ((priv->eee_enabled) && (!priv->tx_path_in_lpi_mode)) { @@ -1830,36 +1525,33 @@ static inline void stmmac_disable_dma_irq(struct stmmac_priv *priv, u32 chan) /** * stmmac_tx_err - to manage the tx error * @priv: driver private structure - * @queue: queue index + * @chan: channel index * Description: it cleans the descriptors and restarts the transmission * in case of transmission errors. */ -static void stmmac_tx_err(struct stmmac_priv *priv, u32 queue) +static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan) { - struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; - u32 chan = queue; int i; - - netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); + netif_stop_queue(priv->dev); stmmac_stop_tx_dma(priv, chan); - dma_free_tx_skbufs(priv, queue); + dma_free_tx_skbufs(priv); for (i = 0; i < DMA_TX_SIZE; i++) if (priv->extend_desc) - priv->hw->desc->init_tx_desc(&tx_q->dma_etx[i].basic, + priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic, priv->mode, (i == DMA_TX_SIZE - 1)); else - priv->hw->desc->init_tx_desc(&tx_q->dma_tx[i], + priv->hw->desc->init_tx_desc(&priv->dma_tx[i], priv->mode, (i == DMA_TX_SIZE - 1)); - tx_q->dirty_tx = 0; - tx_q->cur_tx = 0; - netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue)); + priv->dirty_tx = 0; + priv->cur_tx = 0; + netdev_reset_queue(priv->dev); stmmac_start_tx_dma(priv, chan); priv->dev->stats.tx_errors++; - netif_tx_wake_queue(netdev_get_tx_queue(priv->dev, queue)); + netif_wake_queue(priv->dev); } /** @@ -1904,14 +1596,12 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv) u32 chan; for (chan = 0; chan < tx_channel_count; chan++) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan]; - status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats, chan); if (likely((status & handle_rx)) || (status & handle_tx)) { - if (likely(napi_schedule_prep(&rx_q->napi))) { + if (likely(napi_schedule_prep(&priv->napi))) { stmmac_disable_dma_irq(priv, chan); - __napi_schedule(&rx_q->napi); + __napi_schedule(&priv->napi); } } @@ -2044,8 +1734,6 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) { u32 rx_channels_count = priv->plat->rx_queues_to_use; u32 tx_channels_count = priv->plat->tx_queues_to_use; - struct stmmac_rx_queue *rx_q; - struct stmmac_tx_queue *tx_q; u32 dummy_dma_rx_phy = 0; u32 dummy_dma_tx_phy = 0; u32 chan = 0; @@ -2073,43 +1761,36 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) /* DMA RX Channel Configuration */ for (chan = 0; chan < rx_channels_count; chan++) { - rx_q = &priv->rx_queue[chan]; - priv->hw->dma->init_rx_chan(priv->ioaddr, priv->plat->dma_cfg, - rx_q->dma_rx_phy, chan); + priv->dma_rx_phy, chan); - rx_q->rx_tail_addr = rx_q->dma_rx_phy + + priv->rx_tail_addr = priv->dma_rx_phy + (DMA_RX_SIZE * sizeof(struct dma_desc)); priv->hw->dma->set_rx_tail_ptr(priv->ioaddr, - rx_q->rx_tail_addr, + priv->rx_tail_addr, chan); } /* DMA TX Channel Configuration */ for (chan = 0; chan < tx_channels_count; chan++) { - tx_q = &priv->tx_queue[chan]; - priv->hw->dma->init_chan(priv->ioaddr, - priv->plat->dma_cfg, - chan); + priv->plat->dma_cfg, + chan); priv->hw->dma->init_tx_chan(priv->ioaddr, priv->plat->dma_cfg, - tx_q->dma_tx_phy, chan); + priv->dma_tx_phy, chan); - tx_q->tx_tail_addr = tx_q->dma_tx_phy + + priv->tx_tail_addr = priv->dma_tx_phy + (DMA_TX_SIZE * sizeof(struct dma_desc)); priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, - tx_q->tx_tail_addr, + priv->tx_tail_addr, chan); } } else { - rx_q = &priv->rx_queue[chan]; - tx_q = &priv->tx_queue[chan]; - priv->hw->dma->init(priv->ioaddr, priv->plat->dma_cfg, - tx_q->dma_tx_phy, rx_q->dma_rx_phy, atds); + priv->dma_tx_phy, priv->dma_rx_phy, atds); } if (priv->plat->axi && priv->hw->dma->axi) @@ -2127,70 +1808,8 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) static void stmmac_tx_timer(unsigned long data) { struct stmmac_priv *priv = (struct stmmac_priv *)data; - u32 tx_queues_count = priv->plat->tx_queues_to_use; - u32 queue; - - /* let's scan all the tx queues */ - for (queue = 0; queue < tx_queues_count; queue++) - stmmac_tx_clean(priv, queue); -} - -/** - * stmmac_stop_all_queues - Stop all queues - * @priv: driver private structure - */ -static void stmmac_stop_all_queues(struct stmmac_priv *priv) -{ - u32 tx_queues_cnt = priv->plat->tx_queues_to_use; - u32 queue; - - for (queue = 0; queue < tx_queues_cnt; queue++) - netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); -} - -/** - * stmmac_start_all_queues - Start all queues - * @priv: driver private structure - */ -static void stmmac_start_all_queues(struct stmmac_priv *priv) -{ - u32 tx_queues_cnt = priv->plat->tx_queues_to_use; - u32 queue; - for (queue = 0; queue < tx_queues_cnt; queue++) - netif_tx_start_queue(netdev_get_tx_queue(priv->dev, queue)); -} - -/** - * stmmac_disable_all_queues - Disable all queues - * @priv: driver private structure - */ -static void stmmac_disable_all_queues(struct stmmac_priv *priv) -{ - u32 rx_queues_cnt = priv->plat->rx_queues_to_use; - u32 queue; - - for (queue = 0; queue < rx_queues_cnt; queue++) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - - napi_disable(&rx_q->napi); - } -} - -/** - * stmmac_enable_all_queues - Enable all queues - * @priv: driver private structure - */ -static void stmmac_enable_all_queues(struct stmmac_priv *priv) -{ - u32 rx_queues_cnt = priv->plat->rx_queues_to_use; - u32 queue; - - for (queue = 0; queue < rx_queues_cnt; queue++) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - - napi_enable(&rx_q->napi); - } + stmmac_tx_clean(priv); } /** @@ -2549,8 +2168,23 @@ static int stmmac_open(struct net_device *dev) memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats)); priv->xstats.threshold = tc; + priv->dma_buf_sz = STMMAC_ALIGN(buf_sz); priv->rx_copybreak = STMMAC_RX_COPYBREAK; + ret = alloc_dma_desc_resources(priv); + if (ret < 0) { + netdev_err(priv->dev, "%s: DMA descriptors allocation failed\n", + __func__); + goto dma_desc_error; + } + + ret = init_dma_desc_rings(dev, GFP_KERNEL); + if (ret < 0) { + netdev_err(priv->dev, "%s: DMA descriptors initialization failed\n", + __func__); + goto init_error; + } + ret = stmmac_hw_setup(dev, true); if (ret < 0) { netdev_err(priv->dev, "%s: Hw setup failed\n", __func__); @@ -2596,8 +2230,8 @@ static int stmmac_open(struct net_device *dev) } } - stmmac_enable_all_queues(priv); - stmmac_start_all_queues(priv); + napi_enable(&priv->napi); + netif_start_queue(dev); return 0; @@ -2614,7 +2248,7 @@ irq_error: stmmac_hw_teardown(dev); init_error: free_dma_desc_resources(priv); - +dma_desc_error: if (dev->phydev) phy_disconnect(dev->phydev); @@ -2640,9 +2274,9 @@ static int stmmac_release(struct net_device *dev) phy_disconnect(dev->phydev); } - stmmac_stop_all_queues(priv); + netif_stop_queue(dev); - stmmac_disable_all_queues(priv); + napi_disable(&priv->napi); del_timer_sync(&priv->txtimer); @@ -2679,24 +2313,22 @@ static int stmmac_release(struct net_device *dev) * @des: buffer start address * @total_len: total length to fill in descriptors * @last_segmant: condition for the last descriptor - * @queue: TX queue index * Description: * This function fills descriptor and request new descriptors according to * buffer length to fill */ static void stmmac_tso_allocator(struct stmmac_priv *priv, unsigned int des, - int total_len, bool last_segment, u32 queue) + int total_len, bool last_segment) { - struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; struct dma_desc *desc; - u32 buff_size; int tmp_len; + u32 buff_size; tmp_len = total_len; while (tmp_len > 0) { - tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE); - desc = tx_q->dma_tx + tx_q->cur_tx; + priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE); + desc = priv->dma_tx + priv->cur_tx; desc->des0 = cpu_to_le32(des + (total_len - tmp_len)); buff_size = tmp_len >= TSO_MAX_BUFF_SIZE ? @@ -2740,27 +2372,23 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, unsigned int des, */ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) { - struct dma_desc *desc, *first, *mss_desc = NULL; + u32 pay_len, mss; + int tmp_pay_len = 0; struct stmmac_priv *priv = netdev_priv(dev); - u32 queue = skb_get_queue_mapping(skb); int nfrags = skb_shinfo(skb)->nr_frags; unsigned int first_entry, des; - struct stmmac_tx_queue *tx_q; - int tmp_pay_len = 0; - u32 pay_len, mss; + struct dma_desc *desc, *first, *mss_desc = NULL; u8 proto_hdr_len; int i; - tx_q = &priv->tx_queue[queue]; - /* Compute header lengths */ proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); /* Desc availability based on threshold should be enough safe */ - if (unlikely(stmmac_tx_avail(priv, queue) < + if (unlikely(stmmac_tx_avail(priv) < (((skb->len - proto_hdr_len) / TSO_MAX_BUFF_SIZE + 1)))) { - if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) { - netif_tx_stop_queue(netdev_get_tx_queue(dev, queue)); + if (!netif_queue_stopped(dev)) { + netif_stop_queue(dev); /* This is a hard error, log it. */ netdev_err(priv->dev, "%s: Tx Ring full when queue awake\n", @@ -2775,10 +2403,10 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) /* set new MSS value if needed */ if (mss != priv->mss) { - mss_desc = tx_q->dma_tx + tx_q->cur_tx; + mss_desc = priv->dma_tx + priv->cur_tx; priv->hw->desc->set_mss(mss_desc, mss); priv->mss = mss; - tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE); + priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE); } if (netif_msg_tx_queued(priv)) { @@ -2788,9 +2416,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) skb->data_len); } - first_entry = tx_q->cur_tx; + first_entry = priv->cur_tx; - desc = tx_q->dma_tx + first_entry; + desc = priv->dma_tx + first_entry; first = desc; /* first descriptor: fill Headers on Buf1 */ @@ -2799,9 +2427,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) if (dma_mapping_error(priv->device, des)) goto dma_map_err; - tx_q->tx_skbuff_dma[first_entry].buf = des; - tx_q->tx_skbuff_dma[first_entry].len = skb_headlen(skb); - tx_q->tx_skbuff[first_entry] = skb; + priv->tx_skbuff_dma[first_entry].buf = des; + priv->tx_skbuff_dma[first_entry].len = skb_headlen(skb); + priv->tx_skbuff[first_entry] = skb; first->des0 = cpu_to_le32(des); @@ -2812,7 +2440,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) /* If needed take extra descriptors to fill the remaining payload */ tmp_pay_len = pay_len - TSO_MAX_BUFF_SIZE; - stmmac_tso_allocator(priv, des, tmp_pay_len, (nfrags == 0), queue); + stmmac_tso_allocator(priv, des, tmp_pay_len, (nfrags == 0)); /* Prepare fragments */ for (i = 0; i < nfrags; i++) { @@ -2825,22 +2453,22 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) goto dma_map_err; stmmac_tso_allocator(priv, des, skb_frag_size(frag), - (i == nfrags - 1), queue); + (i == nfrags - 1)); - tx_q->tx_skbuff_dma[tx_q->cur_tx].buf = des; - tx_q->tx_skbuff_dma[tx_q->cur_tx].len = skb_frag_size(frag); - tx_q->tx_skbuff[tx_q->cur_tx] = NULL; - tx_q->tx_skbuff_dma[tx_q->cur_tx].map_as_page = true; + priv->tx_skbuff_dma[priv->cur_tx].buf = des; + priv->tx_skbuff_dma[priv->cur_tx].len = skb_frag_size(frag); + priv->tx_skbuff[priv->cur_tx] = NULL; + priv->tx_skbuff_dma[priv->cur_tx].map_as_page = true; } - tx_q->tx_skbuff_dma[tx_q->cur_tx].last_segment = true; + priv->tx_skbuff_dma[priv->cur_tx].last_segment = true; - tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE); + priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE); - if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) { + if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", __func__); - netif_tx_stop_queue(netdev_get_tx_queue(dev, queue)); + netif_stop_queue(dev); } dev->stats.tx_bytes += skb->len; @@ -2872,7 +2500,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) priv->hw->desc->prepare_tso_tx_desc(first, 1, proto_hdr_len, pay_len, - 1, tx_q->tx_skbuff_dma[first_entry].last_segment, + 1, priv->tx_skbuff_dma[first_entry].last_segment, tcp_hdrlen(skb) / 4, (skb->len - proto_hdr_len)); /* If context desc is used to change MSS */ @@ -2887,20 +2515,20 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) if (netif_msg_pktdata(priv)) { pr_info("%s: curr=%d dirty=%d f=%d, e=%d, f_p=%p, nfrags %d\n", - __func__, tx_q->cur_tx, tx_q->dirty_tx, first_entry, - tx_q->cur_tx, first, nfrags); + __func__, priv->cur_tx, priv->dirty_tx, first_entry, + priv->cur_tx, first, nfrags); - priv->hw->desc->display_ring((void *)tx_q->dma_tx, DMA_TX_SIZE, + priv->hw->desc->display_ring((void *)priv->dma_tx, DMA_TX_SIZE, 0); pr_info(">>> frame to be transmitted: "); print_pkt(skb->data, skb_headlen(skb)); } - netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len); + netdev_sent_queue(dev, skb->len); - priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, tx_q->tx_tail_addr, - queue); + priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr, + STMMAC_CHAN0); return NETDEV_TX_OK; @@ -2924,25 +2552,21 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) struct stmmac_priv *priv = netdev_priv(dev); unsigned int nopaged_len = skb_headlen(skb); int i, csum_insertion = 0, is_jumbo = 0; - u32 queue = skb_get_queue_mapping(skb); int nfrags = skb_shinfo(skb)->nr_frags; unsigned int entry, first_entry; struct dma_desc *desc, *first; - struct stmmac_tx_queue *tx_q; unsigned int enh_desc; unsigned int des; - tx_q = &priv->tx_queue[queue]; - /* Manage oversized TCP frames for GMAC4 device */ if (skb_is_gso(skb) && priv->tso) { if (ip_hdr(skb)->protocol == IPPROTO_TCP) return stmmac_tso_xmit(skb, dev); } - if (unlikely(stmmac_tx_avail(priv, queue) < nfrags + 1)) { - if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) { - netif_tx_stop_queue(netdev_get_tx_queue(dev, queue)); + if (unlikely(stmmac_tx_avail(priv) < nfrags + 1)) { + if (!netif_queue_stopped(dev)) { + netif_stop_queue(dev); /* This is a hard error, log it. */ netdev_err(priv->dev, "%s: Tx Ring full when queue awake\n", @@ -2954,19 +2578,19 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (priv->tx_path_in_lpi_mode) stmmac_disable_eee_mode(priv); - entry = tx_q->cur_tx; + entry = priv->cur_tx; first_entry = entry; csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL); if (likely(priv->extend_desc)) - desc = (struct dma_desc *)(tx_q->dma_etx + entry); + desc = (struct dma_desc *)(priv->dma_etx + entry); else - desc = tx_q->dma_tx + entry; + desc = priv->dma_tx + entry; first = desc; - tx_q->tx_skbuff[first_entry] = skb; + priv->tx_skbuff[first_entry] = skb; enh_desc = priv->plat->enh_desc; /* To program the descriptors according to the size of the frame */ @@ -2975,7 +2599,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(is_jumbo) && likely(priv->synopsys_id < DWMAC_CORE_4_00)) { - entry = priv->hw->mode->jumbo_frm(tx_q, skb, csum_insertion); + entry = priv->hw->mode->jumbo_frm(priv, skb, csum_insertion); if (unlikely(entry < 0)) goto dma_map_err; } @@ -2988,26 +2612,26 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); if (likely(priv->extend_desc)) - desc = (struct dma_desc *)(tx_q->dma_etx + entry); + desc = (struct dma_desc *)(priv->dma_etx + entry); else - desc = tx_q->dma_tx + entry; + desc = priv->dma_tx + entry; des = skb_frag_dma_map(priv->device, frag, 0, len, DMA_TO_DEVICE); if (dma_mapping_error(priv->device, des)) goto dma_map_err; /* should reuse desc w/o issues */ - tx_q->tx_skbuff[entry] = NULL; + priv->tx_skbuff[entry] = NULL; - tx_q->tx_skbuff_dma[entry].buf = des; + priv->tx_skbuff_dma[entry].buf = des; if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) desc->des0 = cpu_to_le32(des); else desc->des2 = cpu_to_le32(des); - tx_q->tx_skbuff_dma[entry].map_as_page = true; - tx_q->tx_skbuff_dma[entry].len = len; - tx_q->tx_skbuff_dma[entry].last_segment = last_segment; + priv->tx_skbuff_dma[entry].map_as_page = true; + priv->tx_skbuff_dma[entry].len = len; + priv->tx_skbuff_dma[entry].last_segment = last_segment; /* Prepare the descriptor and set the own bit too */ priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion, @@ -3016,20 +2640,20 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); - tx_q->cur_tx = entry; + priv->cur_tx = entry; if (netif_msg_pktdata(priv)) { void *tx_head; netdev_dbg(priv->dev, "%s: curr=%d dirty=%d f=%d, e=%d, first=%p, nfrags=%d", - __func__, tx_q->cur_tx, tx_q->dirty_tx, first_entry, + __func__, priv->cur_tx, priv->dirty_tx, first_entry, entry, first, nfrags); if (priv->extend_desc) - tx_head = (void *)tx_q->dma_etx; + tx_head = (void *)priv->dma_etx; else - tx_head = (void *)tx_q->dma_tx; + tx_head = (void *)priv->dma_tx; priv->hw->desc->display_ring(tx_head, DMA_TX_SIZE, false); @@ -3037,10 +2661,10 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) print_pkt(skb->data, skb->len); } - if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) { + if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", __func__); - netif_tx_stop_queue(netdev_get_tx_queue(dev, queue)); + netif_stop_queue(dev); } dev->stats.tx_bytes += skb->len; @@ -3075,14 +2699,14 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (dma_mapping_error(priv->device, des)) goto dma_map_err; - tx_q->tx_skbuff_dma[first_entry].buf = des; + priv->tx_skbuff_dma[first_entry].buf = des; if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) first->des0 = cpu_to_le32(des); else first->des2 = cpu_to_le32(des); - tx_q->tx_skbuff_dma[first_entry].len = nopaged_len; - tx_q->tx_skbuff_dma[first_entry].last_segment = last_segment; + priv->tx_skbuff_dma[first_entry].len = nopaged_len; + priv->tx_skbuff_dma[first_entry].last_segment = last_segment; if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en)) { @@ -3103,13 +2727,13 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) dma_wmb(); } - netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len); + netdev_sent_queue(dev, skb->len); if (priv->synopsys_id < DWMAC_CORE_4_00) priv->hw->dma->enable_dma_transmission(priv->ioaddr); else - priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, tx_q->tx_tail_addr, - queue); + priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr, + STMMAC_CHAN0); return NETDEV_TX_OK; @@ -3137,9 +2761,9 @@ static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb) } -static inline int stmmac_rx_threshold_count(struct stmmac_rx_queue *rx_q) +static inline int stmmac_rx_threshold_count(struct stmmac_priv *priv) { - if (rx_q->rx_zeroc_thresh < STMMAC_RX_THRESH) + if (priv->rx_zeroc_thresh < STMMAC_RX_THRESH) return 0; return 1; @@ -3148,32 +2772,30 @@ static inline int stmmac_rx_threshold_count(struct stmmac_rx_queue *rx_q) /** * stmmac_rx_refill - refill used skb preallocated buffers * @priv: driver private structure - * @queue: RX queue index * Description : this is to reallocate the skb for the reception process * that is based on zero-copy. */ -static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) +static inline void stmmac_rx_refill(struct stmmac_priv *priv) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - int dirty = stmmac_rx_dirty(priv, queue); - unsigned int entry = rx_q->dirty_rx; int bfsize = priv->dma_buf_sz; + unsigned int entry = priv->dirty_rx; + int dirty = stmmac_rx_dirty(priv); while (dirty-- > 0) { struct dma_desc *p; if (priv->extend_desc) - p = (struct dma_desc *)(rx_q->dma_erx + entry); + p = (struct dma_desc *)(priv->dma_erx + entry); else - p = rx_q->dma_rx + entry; + p = priv->dma_rx + entry; - if (!rx_q->rx_skbuff[entry]) { + if (likely(priv->rx_skbuff[entry] == NULL)) { struct sk_buff *skb; skb = netdev_alloc_skb_ip_align(priv->dev, bfsize); if (unlikely(!skb)) { /* so for a while no zero-copy! */ - rx_q->rx_zeroc_thresh = STMMAC_RX_THRESH; + priv->rx_zeroc_thresh = STMMAC_RX_THRESH; if (unlikely(net_ratelimit())) dev_err(priv->device, "fail to alloc skb entry %d\n", @@ -3181,28 +2803,28 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) break; } - rx_q->rx_skbuff[entry] = skb; - rx_q->rx_skbuff_dma[entry] = + priv->rx_skbuff[entry] = skb; + priv->rx_skbuff_dma[entry] = dma_map_single(priv->device, skb->data, bfsize, DMA_FROM_DEVICE); if (dma_mapping_error(priv->device, - rx_q->rx_skbuff_dma[entry])) { + priv->rx_skbuff_dma[entry])) { netdev_err(priv->dev, "Rx DMA map failed\n"); dev_kfree_skb(skb); break; } if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) { - p->des0 = cpu_to_le32(rx_q->rx_skbuff_dma[entry]); + p->des0 = cpu_to_le32(priv->rx_skbuff_dma[entry]); p->des1 = 0; } else { - p->des2 = cpu_to_le32(rx_q->rx_skbuff_dma[entry]); + p->des2 = cpu_to_le32(priv->rx_skbuff_dma[entry]); } if (priv->hw->mode->refill_desc3) - priv->hw->mode->refill_desc3(rx_q, p); + priv->hw->mode->refill_desc3(priv, p); - if (rx_q->rx_zeroc_thresh > 0) - rx_q->rx_zeroc_thresh--; + if (priv->rx_zeroc_thresh > 0) + priv->rx_zeroc_thresh--; netif_dbg(priv, rx_status, priv->dev, "refill entry #%d\n", entry); @@ -3218,7 +2840,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) entry = STMMAC_GET_ENTRY(entry, DMA_RX_SIZE); } - rx_q->dirty_rx = entry; + priv->dirty_rx = entry; } /** @@ -3228,22 +2850,21 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) * Description : this the function called by the napi poll method. * It gets all the frames inside the ring. */ -static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) +static int stmmac_rx(struct stmmac_priv *priv, int limit) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - unsigned int entry = rx_q->cur_rx; - int coe = priv->hw->rx_csum; + unsigned int entry = priv->cur_rx; unsigned int next_entry; unsigned int count = 0; + int coe = priv->hw->rx_csum; if (netif_msg_rx_status(priv)) { void *rx_head; netdev_dbg(priv->dev, "%s: descriptor ring:\n", __func__); if (priv->extend_desc) - rx_head = (void *)rx_q->dma_erx; + rx_head = (void *)priv->dma_erx; else - rx_head = (void *)rx_q->dma_rx; + rx_head = (void *)priv->dma_rx; priv->hw->desc->display_ring(rx_head, DMA_RX_SIZE, true); } @@ -3253,9 +2874,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) struct dma_desc *np; if (priv->extend_desc) - p = (struct dma_desc *)(rx_q->dma_erx + entry); + p = (struct dma_desc *)(priv->dma_erx + entry); else - p = rx_q->dma_rx + entry; + p = priv->dma_rx + entry; /* read the status of the incoming frame */ status = priv->hw->desc->rx_status(&priv->dev->stats, @@ -3266,20 +2887,20 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) count++; - rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx, DMA_RX_SIZE); - next_entry = rx_q->cur_rx; + priv->cur_rx = STMMAC_GET_ENTRY(priv->cur_rx, DMA_RX_SIZE); + next_entry = priv->cur_rx; if (priv->extend_desc) - np = (struct dma_desc *)(rx_q->dma_erx + next_entry); + np = (struct dma_desc *)(priv->dma_erx + next_entry); else - np = rx_q->dma_rx + next_entry; + np = priv->dma_rx + next_entry; prefetch(np); if ((priv->extend_desc) && (priv->hw->desc->rx_extended_status)) priv->hw->desc->rx_extended_status(&priv->dev->stats, &priv->xstats, - rx_q->dma_erx + + priv->dma_erx + entry); if (unlikely(status == discard_frame)) { priv->dev->stats.rx_errors++; @@ -3289,9 +2910,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) * them in stmmac_rx_refill() function so that * device can reuse it. */ - rx_q->rx_skbuff[entry] = NULL; + priv->rx_skbuff[entry] = NULL; dma_unmap_single(priv->device, - rx_q->rx_skbuff_dma[entry], + priv->rx_skbuff_dma[entry], priv->dma_buf_sz, DMA_FROM_DEVICE); } @@ -3339,7 +2960,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) */ if (unlikely(!priv->plat->has_gmac4 && ((frame_len < priv->rx_copybreak) || - stmmac_rx_threshold_count(rx_q)))) { + stmmac_rx_threshold_count(priv)))) { skb = netdev_alloc_skb_ip_align(priv->dev, frame_len); if (unlikely(!skb)) { @@ -3351,21 +2972,21 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) } dma_sync_single_for_cpu(priv->device, - rx_q->rx_skbuff_dma + priv->rx_skbuff_dma [entry], frame_len, DMA_FROM_DEVICE); skb_copy_to_linear_data(skb, - rx_q-> + priv-> rx_skbuff[entry]->data, frame_len); skb_put(skb, frame_len); dma_sync_single_for_device(priv->device, - rx_q->rx_skbuff_dma + priv->rx_skbuff_dma [entry], frame_len, DMA_FROM_DEVICE); } else { - skb = rx_q->rx_skbuff[entry]; + skb = priv->rx_skbuff[entry]; if (unlikely(!skb)) { netdev_err(priv->dev, "%s: Inconsistent Rx chain\n", @@ -3374,12 +2995,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) break; } prefetch(skb->data - NET_IP_ALIGN); - rx_q->rx_skbuff[entry] = NULL; - rx_q->rx_zeroc_thresh++; + priv->rx_skbuff[entry] = NULL; + priv->rx_zeroc_thresh++; skb_put(skb, frame_len); dma_unmap_single(priv->device, - rx_q->rx_skbuff_dma[entry], + priv->rx_skbuff_dma[entry], priv->dma_buf_sz, DMA_FROM_DEVICE); } @@ -3401,7 +3022,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) else skb->ip_summed = CHECKSUM_UNNECESSARY; - napi_gro_receive(&rx_q->napi, skb); + napi_gro_receive(&priv->napi, skb); priv->dev->stats.rx_packets++; priv->dev->stats.rx_bytes += frame_len; @@ -3409,7 +3030,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) entry = next_entry; } - stmmac_rx_refill(priv, queue); + stmmac_rx_refill(priv); priv->xstats.rx_pkt_n += count; @@ -3426,22 +3047,14 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) */ static int stmmac_poll(struct napi_struct *napi, int budget) { - struct stmmac_rx_queue *rx_q = - container_of(napi, struct stmmac_rx_queue, napi); - struct stmmac_priv *priv = rx_q->priv_data; - u32 tx_count = priv->dma_cap.number_tx_queues; - u32 chan = rx_q->queue_index; - u32 work_done = 0; - u32 queue = 0; + struct stmmac_priv *priv = container_of(napi, struct stmmac_priv, napi); + int work_done = 0; + u32 chan = STMMAC_CHAN0; priv->xstats.napi_poll++; - /* check all the queues */ - for (queue = 0; queue < tx_count; queue++) - stmmac_tx_clean(priv, queue); - - /* Process RX packets from this queue */ - work_done = stmmac_rx(priv, budget, rx_q->queue_index); + stmmac_tx_clean(priv); + work_done = stmmac_rx(priv, budget); if (work_done < budget) { napi_complete_done(napi, work_done); stmmac_enable_dma_irq(priv, chan); @@ -3460,12 +3073,10 @@ static int stmmac_poll(struct napi_struct *napi, int budget) static void stmmac_tx_timeout(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); - u32 tx_count = priv->plat->tx_queues_to_use; - u32 chan; + u32 chan = STMMAC_CHAN0; /* Clear Tx resources and restart transmitting again */ - for (chan = 0; chan < tx_count; chan++) - stmmac_tx_err(priv, chan); + stmmac_tx_err(priv, chan); } /** @@ -3604,9 +3215,6 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) if (priv->synopsys_id >= DWMAC_CORE_4_00) { for (queue = 0; queue < queues_count; queue++) { - struct stmmac_rx_queue *rx_q = - &priv->rx_queue[queue]; - status |= priv->hw->mac->host_mtl_irq_status(priv->hw, queue); @@ -3614,7 +3222,7 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) if (status & CORE_IRQ_MTL_RX_OVERFLOW && priv->hw->dma->set_rx_tail_ptr) priv->hw->dma->set_rx_tail_ptr(priv->ioaddr, - rx_q->rx_tail_addr, + priv->rx_tail_addr, queue); } } @@ -3714,40 +3322,17 @@ static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v) { struct net_device *dev = seq->private; struct stmmac_priv *priv = netdev_priv(dev); - u32 rx_count = priv->plat->rx_queues_to_use; - u32 tx_count = priv->plat->tx_queues_to_use; - u32 queue; - - for (queue = 0; queue < rx_count; queue++) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - - seq_printf(seq, "RX Queue %d:\n", queue); - - if (priv->extend_desc) { - seq_printf(seq, "Extended descriptor ring:\n"); - sysfs_display_ring((void *)rx_q->dma_erx, - DMA_RX_SIZE, 1, seq); - } else { - seq_printf(seq, "Descriptor ring:\n"); - sysfs_display_ring((void *)rx_q->dma_rx, - DMA_RX_SIZE, 0, seq); - } - } - - for (queue = 0; queue < tx_count; queue++) { - struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; - - seq_printf(seq, "TX Queue %d:\n", queue); - if (priv->extend_desc) { - seq_printf(seq, "Extended descriptor ring:\n"); - sysfs_display_ring((void *)tx_q->dma_etx, - DMA_TX_SIZE, 1, seq); - } else { - seq_printf(seq, "Descriptor ring:\n"); - sysfs_display_ring((void *)tx_q->dma_tx, - DMA_TX_SIZE, 0, seq); - } + if (priv->extend_desc) { + seq_printf(seq, "Extended RX descriptor ring:\n"); + sysfs_display_ring((void *)priv->dma_erx, DMA_RX_SIZE, 1, seq); + seq_printf(seq, "Extended TX descriptor ring:\n"); + sysfs_display_ring((void *)priv->dma_etx, DMA_TX_SIZE, 1, seq); + } else { + seq_printf(seq, "RX descriptor ring:\n"); + sysfs_display_ring((void *)priv->dma_rx, DMA_RX_SIZE, 0, seq); + seq_printf(seq, "TX descriptor ring:\n"); + sysfs_display_ring((void *)priv->dma_tx, DMA_TX_SIZE, 0, seq); } return 0; @@ -4030,14 +3615,11 @@ int stmmac_dvr_probe(struct device *device, struct plat_stmmacenet_data *plat_dat, struct stmmac_resources *res) { + int ret = 0; struct net_device *ndev = NULL; struct stmmac_priv *priv; - int ret = 0; - u32 queue; - ndev = alloc_etherdev_mqs(sizeof(struct stmmac_priv), - MTL_MAX_TX_QUEUES, - MTL_MAX_RX_QUEUES); + ndev = alloc_etherdev(sizeof(struct stmmac_priv)); if (!ndev) return -ENOMEM; @@ -4079,12 +3661,6 @@ int stmmac_dvr_probe(struct device *device, if (ret) goto error_hw_init; - /* Configure real RX and TX queues */ - netif_set_real_num_rx_queues(ndev, priv->plat->rx_queues_to_use); - netif_set_real_num_tx_queues(ndev, priv->plat->tx_queues_to_use); - - priv->dma_buf_sz = STMMAC_ALIGN(buf_sz); - ndev->netdev_ops = &stmmac_netdev_ops; ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | @@ -4134,26 +3710,7 @@ int stmmac_dvr_probe(struct device *device, "Enable RX Mitigation via HW Watchdog Timer\n"); } - ret = alloc_dma_desc_resources(priv); - if (ret < 0) { - netdev_err(priv->dev, "%s: DMA descriptors allocation failed\n", - __func__); - goto init_dma_error; - } - - ret = init_dma_desc_rings(priv->dev, GFP_KERNEL); - if (ret < 0) { - netdev_err(priv->dev, "%s: DMA descriptors initialization failed\n", - __func__); - goto init_dma_error; - } - - for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - - netif_napi_add(ndev, &rx_q->napi, stmmac_poll, - (64 * priv->plat->rx_queues_to_use)); - } + netif_napi_add(ndev, &priv->napi, stmmac_poll, 64); spin_lock_init(&priv->lock); @@ -4198,13 +3755,7 @@ error_netdev_register: priv->hw->pcs != STMMAC_PCS_RTBI) stmmac_mdio_unregister(ndev); error_mdio_register: - for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - - netif_napi_del(&rx_q->napi); - } -init_dma_error: - free_dma_desc_resources(priv); + netif_napi_del(&priv->napi); error_hw_init: free_netdev(ndev); @@ -4266,9 +3817,9 @@ int stmmac_suspend(struct device *dev) spin_lock_irqsave(&priv->lock, flags); netif_device_detach(ndev); - stmmac_stop_all_queues(priv); + netif_stop_queue(ndev); - stmmac_disable_all_queues(priv); + napi_disable(&priv->napi); /* Stop TX/RX DMA */ stmmac_stop_all_dma(priv); @@ -4293,31 +3844,6 @@ int stmmac_suspend(struct device *dev) } EXPORT_SYMBOL_GPL(stmmac_suspend); -/** - * stmmac_reset_queues_param - reset queue parameters - * @dev: device pointer - */ -static void stmmac_reset_queues_param(struct stmmac_priv *priv) -{ - u32 rx_cnt = priv->plat->rx_queues_to_use; - u32 tx_cnt = priv->plat->tx_queues_to_use; - u32 queue; - - for (queue = 0; queue < rx_cnt; queue++) { - struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - - rx_q->cur_rx = 0; - rx_q->dirty_rx = 0; - } - - for (queue = 0; queue < tx_cnt; queue++) { - struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; - - tx_q->cur_tx = 0; - tx_q->dirty_tx = 0; - } -} - /** * stmmac_resume - resume callback * @dev: device pointer @@ -4358,8 +3884,10 @@ int stmmac_resume(struct device *dev) spin_lock_irqsave(&priv->lock, flags); - stmmac_reset_queues_param(priv); - + priv->cur_rx = 0; + priv->dirty_rx = 0; + priv->dirty_tx = 0; + priv->cur_tx = 0; /* reset private mss value to force mss context settings at * next tso xmit (only used for gmac4). */ @@ -4371,9 +3899,9 @@ int stmmac_resume(struct device *dev) stmmac_init_tx_coalesce(priv); stmmac_set_rx_mode(ndev); - stmmac_enable_all_queues(priv); + napi_enable(&priv->napi); - stmmac_start_all_queues(priv); + netif_start_queue(ndev); spin_unlock_irqrestore(&priv->lock, flags); -- cgit From eaf70ad14cbbb99d46b78b1307628a16a3f6075d Mon Sep 17 00:00:00 2001 From: Wadim Egorov Date: Wed, 29 Mar 2017 14:12:19 +0200 Subject: net: stmmac: dwmac-rk: Add handling for RGMII_ID/RXID/TXID ATM dwmac-rk will always set and enable it's internal delay lines. Using PHY internal delays in combination with the phy-mode rgmii-id/rxid/txid was not possible. Only rgmii was supported. Now we can disable rockchip's gmac delay lines and also use rgmii-id/rxid/txid. Tested only with a RK3288 based board. Signed-off-by: Wadim Egorov Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 53 ++++++++++++++++++-------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index e5db6ac36235..f0df5193f047 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -74,6 +74,10 @@ struct rk_priv_data { #define GRF_BIT(nr) (BIT(nr) | BIT(nr+16)) #define GRF_CLR_BIT(nr) (BIT(nr+16)) +#define DELAY_ENABLE(soc, tx, rx) \ + (((tx) ? soc##_GMAC_TXCLK_DLY_ENABLE : soc##_GMAC_TXCLK_DLY_DISABLE) | \ + ((rx) ? soc##_GMAC_RXCLK_DLY_ENABLE : soc##_GMAC_RXCLK_DLY_DISABLE)) + #define RK3228_GRF_MAC_CON0 0x0900 #define RK3228_GRF_MAC_CON1 0x0904 @@ -115,8 +119,7 @@ static void rk3228_set_to_rgmii(struct rk_priv_data *bsp_priv, regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON1, RK3228_GMAC_PHY_INTF_SEL_RGMII | RK3228_GMAC_RMII_MODE_CLR | - RK3228_GMAC_RXCLK_DLY_ENABLE | - RK3228_GMAC_TXCLK_DLY_ENABLE); + DELAY_ENABLE(RK3228, tx_delay, rx_delay)); regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON0, RK3228_GMAC_CLK_RX_DL_CFG(rx_delay) | @@ -232,8 +235,7 @@ static void rk3288_set_to_rgmii(struct rk_priv_data *bsp_priv, RK3288_GMAC_PHY_INTF_SEL_RGMII | RK3288_GMAC_RMII_MODE_CLR); regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3, - RK3288_GMAC_RXCLK_DLY_ENABLE | - RK3288_GMAC_TXCLK_DLY_ENABLE | + DELAY_ENABLE(RK3288, tx_delay, rx_delay) | RK3288_GMAC_CLK_RX_DL_CFG(rx_delay) | RK3288_GMAC_CLK_TX_DL_CFG(tx_delay)); } @@ -460,8 +462,7 @@ static void rk3366_set_to_rgmii(struct rk_priv_data *bsp_priv, RK3366_GMAC_PHY_INTF_SEL_RGMII | RK3366_GMAC_RMII_MODE_CLR); regmap_write(bsp_priv->grf, RK3366_GRF_SOC_CON7, - RK3366_GMAC_RXCLK_DLY_ENABLE | - RK3366_GMAC_TXCLK_DLY_ENABLE | + DELAY_ENABLE(RK3366, tx_delay, rx_delay) | RK3366_GMAC_CLK_RX_DL_CFG(rx_delay) | RK3366_GMAC_CLK_TX_DL_CFG(tx_delay)); } @@ -572,8 +573,7 @@ static void rk3368_set_to_rgmii(struct rk_priv_data *bsp_priv, RK3368_GMAC_PHY_INTF_SEL_RGMII | RK3368_GMAC_RMII_MODE_CLR); regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON16, - RK3368_GMAC_RXCLK_DLY_ENABLE | - RK3368_GMAC_TXCLK_DLY_ENABLE | + DELAY_ENABLE(RK3368, tx_delay, rx_delay) | RK3368_GMAC_CLK_RX_DL_CFG(rx_delay) | RK3368_GMAC_CLK_TX_DL_CFG(tx_delay)); } @@ -684,8 +684,7 @@ static void rk3399_set_to_rgmii(struct rk_priv_data *bsp_priv, RK3399_GMAC_PHY_INTF_SEL_RGMII | RK3399_GMAC_RMII_MODE_CLR); regmap_write(bsp_priv->grf, RK3399_GRF_SOC_CON6, - RK3399_GMAC_RXCLK_DLY_ENABLE | - RK3399_GMAC_TXCLK_DLY_ENABLE | + DELAY_ENABLE(RK3399, tx_delay, rx_delay) | RK3399_GMAC_CLK_RX_DL_CFG(rx_delay) | RK3399_GMAC_CLK_TX_DL_CFG(tx_delay)); } @@ -985,14 +984,29 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv) return ret; /*rmii or rgmii*/ - if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) { + switch (bsp_priv->phy_iface) { + case PHY_INTERFACE_MODE_RGMII: dev_info(dev, "init for RGMII\n"); bsp_priv->ops->set_to_rgmii(bsp_priv, bsp_priv->tx_delay, bsp_priv->rx_delay); - } else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) { + break; + case PHY_INTERFACE_MODE_RGMII_ID: + dev_info(dev, "init for RGMII_ID\n"); + bsp_priv->ops->set_to_rgmii(bsp_priv, 0, 0); + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + dev_info(dev, "init for RGMII_RXID\n"); + bsp_priv->ops->set_to_rgmii(bsp_priv, bsp_priv->tx_delay, 0); + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + dev_info(dev, "init for RGMII_TXID\n"); + bsp_priv->ops->set_to_rgmii(bsp_priv, 0, bsp_priv->rx_delay); + break; + case PHY_INTERFACE_MODE_RMII: dev_info(dev, "init for RMII\n"); bsp_priv->ops->set_to_rmii(bsp_priv); - } else { + break; + default: dev_err(dev, "NO interface defined!\n"); } @@ -1022,12 +1036,19 @@ static void rk_fix_speed(void *priv, unsigned int speed) struct rk_priv_data *bsp_priv = priv; struct device *dev = &bsp_priv->pdev->dev; - if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) + switch (bsp_priv->phy_iface) { + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: bsp_priv->ops->set_rgmii_speed(bsp_priv, speed); - else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) + break; + case PHY_INTERFACE_MODE_RMII: bsp_priv->ops->set_rmii_speed(bsp_priv, speed); - else + break; + default: dev_err(dev, "unsupported interface %d", bsp_priv->phy_iface); + } } static int rk_gmac_probe(struct platform_device *pdev) -- cgit From 282ccf6efb7c5d75b0283b66ed487957163ce8fe Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 29 Mar 2017 17:17:31 +0200 Subject: drivers: add explicit interrupt.h includes These files all use functions declared in interrupt.h, but currently rely on implicit inclusion of this file (via netns/xfrm.h). That won't work anymore when the flow cache is removed so include that header where needed. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- drivers/infiniband/hw/nes/nes.h | 1 + drivers/net/dsa/mv88e6xxx/global2.c | 1 + drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 1 + drivers/net/ethernet/amd/xgbe/xgbe-i2c.c | 1 + drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 1 + drivers/net/ethernet/broadcom/bgmac.c | 1 + drivers/net/ethernet/broadcom/bnxt/bnxt.h | 2 ++ drivers/net/ethernet/cavium/liquidio/lio_main.c | 1 + drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 1 + drivers/net/ethernet/ezchip/nps_enet.c | 1 + drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 1 + drivers/net/ethernet/qualcomm/emac/emac-sgmii.c | 1 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 1 + drivers/net/wireless/st/cw1200/cw1200_sdio.c | 1 + drivers/usb/gadget/function/f_ncm.c | 1 + net/mac802154/ieee802154_i.h | 1 + net/smc/smc_ib.h | 1 + 17 files changed, 18 insertions(+) diff --git a/drivers/infiniband/hw/nes/nes.h b/drivers/infiniband/hw/nes/nes.h index 85acd0843b50..3f9e56e8b379 100644 --- a/drivers/infiniband/hw/nes/nes.h +++ b/drivers/infiniband/hw/nes/nes.h @@ -36,6 +36,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 0b8601f8536e..132559d46b95 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -13,6 +13,7 @@ * (at your option) any later version. */ +#include #include #include "mv88e6xxx.h" #include "global2.h" diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 54593e03d821..c772420fa41c 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -118,6 +118,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c b/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c index 0c7088a426e9..417bdb5982a9 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c @@ -115,6 +115,7 @@ */ #include +#include #include #include #include diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 4c5b90eea4af..b672d9249539 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -114,6 +114,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index e1a24ee6ab8b..ba4d2e145bb9 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index c7a5b84a5cb2..3cb07778a690 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -18,6 +18,8 @@ #define DRV_VER_MIN 7 #define DRV_VER_UPD 0 +#include + struct tx_bd { __le32 tx_bd_len_flags_type; #define TX_BD_TYPE (0x3f << 0) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index b22291906fcc..a8426d3d05d0 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -16,6 +16,7 @@ * NONINFRINGEMENT. See the GNU General Public License for more details. ***********************************************************************/ #include +#include #include #include #include diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 89fd81abab9a..174d748b5928 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -16,6 +16,7 @@ * NONINFRINGEMENT. See the GNU General Public License for more details. ***********************************************************************/ #include +#include #include #include #include "liquidio_common.h" diff --git a/drivers/net/ethernet/ezchip/nps_enet.c b/drivers/net/ethernet/ezchip/nps_enet.c index 992ebe973d25..70165fcbff9c 100644 --- a/drivers/net/ethernet/ezchip/nps_enet.c +++ b/drivers/net/ethernet/ezchip/nps_enet.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index e372a5823480..60d9b6aaf63a 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -19,6 +19,7 @@ */ #include +#include #include #include "fm10k.h" diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c index 040b28977ee7..18c184ee1f3c 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c @@ -13,6 +13,7 @@ /* Qualcomm Technologies, Inc. EMAC SGMII Controller driver. */ +#include #include #include #include diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index 5bc2ba214735..9b970dc2b922 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/net/wireless/st/cw1200/cw1200_sdio.c b/drivers/net/wireless/st/cw1200/cw1200_sdio.c index d3acc85932a5..709f56e5ad87 100644 --- a/drivers/net/wireless/st/cw1200/cw1200_sdio.c +++ b/drivers/net/wireless/st/cw1200/cw1200_sdio.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 224717e63a53..864819ff9a7d 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h index 56ccffa3f2bf..62141dcec2d6 100644 --- a/net/mac802154/ieee802154_i.h +++ b/net/mac802154/ieee802154_i.h @@ -19,6 +19,7 @@ #ifndef __IEEE802154_I_H #define __IEEE802154_I_H +#include #include #include #include diff --git a/net/smc/smc_ib.h b/net/smc/smc_ib.h index a95f74bb5569..7e1f0e24d177 100644 --- a/net/smc/smc_ib.h +++ b/net/smc/smc_ib.h @@ -11,6 +11,7 @@ #ifndef _SMC_IB_H #define _SMC_IB_H +#include #include #include -- cgit From ed8bfd5c1ca6e2911c797da611fdada958ab44c0 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 29 Mar 2017 16:33:55 +0100 Subject: VSOCK: remove unnecessary ternary operator on return value Rather than assign the positive errno values to ret and then checking if it is positive and flip the sign, just return the errno value. Detected by CoverityScan, CID#986649 ("Logically Dead Code") Signed-off-by: Colin Ian King Reviewed-by: Stefan Hajnoczi Reviewed-by: Jorgen Hansen Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- net/vmw_vsock/vmci_transport.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index 4be4fbbc0b50..10ae7823a19d 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -96,31 +96,23 @@ static int PROTOCOL_OVERRIDE = -1; static s32 vmci_transport_error_to_vsock_error(s32 vmci_error) { - int err; - switch (vmci_error) { case VMCI_ERROR_NO_MEM: - err = ENOMEM; - break; + return -ENOMEM; case VMCI_ERROR_DUPLICATE_ENTRY: case VMCI_ERROR_ALREADY_EXISTS: - err = EADDRINUSE; - break; + return -EADDRINUSE; case VMCI_ERROR_NO_ACCESS: - err = EPERM; - break; + return -EPERM; case VMCI_ERROR_NO_RESOURCES: - err = ENOBUFS; - break; + return -ENOBUFS; case VMCI_ERROR_INVALID_RESOURCE: - err = EHOSTUNREACH; - break; + return -EHOSTUNREACH; case VMCI_ERROR_INVALID_ARGS: default: - err = EINVAL; + break; } - - return err > 0 ? -err : err; + return -EINVAL; } static u32 vmci_transport_peer_rid(u32 peer_cid) -- cgit From 142c6594acbcc32391af9c15f8cd65c6c177698f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 29 Mar 2017 10:45:44 -0700 Subject: bonding: refine bond_fold_stats() wrap detection Some device drivers reset their stats at down/up events, possibly fooling bonding stats, since they operate with relative deltas. It is nearly not possible to fix drivers, since some of them compute the tx/rx counters based on per rx/tx queue stats, and the queues can be reconfigured (ethtool -L) between the down/up sequence. Lets avoid accumulating 'negative' values that render bonding stats useless. It is better to lose small deltas, assuming the bonding stats are fetched at a reasonable frequency. Fixes: 5f0c5f73e5ef ("bonding: make global bonding stats more reliable") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 6cea964ab70a..27359dab78a1 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3332,12 +3332,17 @@ static void bond_fold_stats(struct rtnl_link_stats64 *_res, for (i = 0; i < sizeof(*_res) / sizeof(u64); i++) { u64 nv = new[i]; u64 ov = old[i]; + s64 delta = nv - ov; /* detects if this particular field is 32bit only */ if (((nv | ov) >> 32) == 0) - res[i] += (u32)nv - (u32)ov; - else - res[i] += nv - ov; + delta = (s64)(s32)((u32)nv - (u32)ov); + + /* filter anomalies, some drivers reset their stats + * at down/up events. + */ + if (delta > 0) + res[i] += delta; } } -- cgit From e704f0434ea60adedc07c847b46910d4840a7ecf Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Wed, 29 Mar 2017 15:14:55 -0400 Subject: ibmvnic: Remove debugfs support The debugfs support in the ibmvnic driver is not, and never has been, supported. Just remove it. The work done in the debugfs code for the driver was part of the original spec for the ibmvnic driver. The corresponding support for this from the server side was never supported and has been dropped. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 628 ------------------------------------- drivers/net/ethernet/ibm/ibmvnic.h | 30 -- 2 files changed, 658 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 3d7318278846..1e8ba784ec92 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -65,7 +65,6 @@ #include #include #include -#include #include #include #include @@ -615,20 +614,10 @@ static void ibmvnic_release_resources(struct ibmvnic_adapter *adapter) release_sub_crqs(adapter); ibmvnic_release_crq_queue(adapter); - if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir)) - debugfs_remove_recursive(adapter->debugfs_dir); - if (adapter->stats_token) dma_unmap_single(dev, adapter->stats_token, sizeof(struct ibmvnic_statistics), DMA_FROM_DEVICE); - - if (adapter->ras_comps) - dma_free_coherent(dev, adapter->ras_comp_num * - sizeof(struct ibmvnic_fw_component), - adapter->ras_comps, adapter->ras_comps_tok); - - kfree(adapter->ras_comp_int); } static int ibmvnic_close(struct net_device *netdev) @@ -2332,57 +2321,6 @@ static void handle_error_info_rsp(union ibmvnic_crq *crq, kfree(error_buff); } -static void handle_dump_size_rsp(union ibmvnic_crq *crq, - struct ibmvnic_adapter *adapter) -{ - int len = be32_to_cpu(crq->request_dump_size_rsp.len); - struct ibmvnic_inflight_cmd *inflight_cmd; - struct device *dev = &adapter->vdev->dev; - union ibmvnic_crq newcrq; - unsigned long flags; - - /* allocate and map buffer */ - adapter->dump_data = kmalloc(len, GFP_KERNEL); - if (!adapter->dump_data) { - complete(&adapter->fw_done); - return; - } - - adapter->dump_data_token = dma_map_single(dev, adapter->dump_data, len, - DMA_FROM_DEVICE); - - if (dma_mapping_error(dev, adapter->dump_data_token)) { - if (!firmware_has_feature(FW_FEATURE_CMO)) - dev_err(dev, "Couldn't map dump data\n"); - kfree(adapter->dump_data); - complete(&adapter->fw_done); - return; - } - - inflight_cmd = kmalloc(sizeof(*inflight_cmd), GFP_ATOMIC); - if (!inflight_cmd) { - dma_unmap_single(dev, adapter->dump_data_token, len, - DMA_FROM_DEVICE); - kfree(adapter->dump_data); - complete(&adapter->fw_done); - return; - } - - memset(&newcrq, 0, sizeof(newcrq)); - newcrq.request_dump.first = IBMVNIC_CRQ_CMD; - newcrq.request_dump.cmd = REQUEST_DUMP; - newcrq.request_dump.ioba = cpu_to_be32(adapter->dump_data_token); - newcrq.request_dump.len = cpu_to_be32(adapter->dump_data_size); - - memcpy(&inflight_cmd->crq, &newcrq, sizeof(newcrq)); - - spin_lock_irqsave(&adapter->inflight_lock, flags); - list_add_tail(&inflight_cmd->list, &adapter->inflight); - spin_unlock_irqrestore(&adapter->inflight_lock, flags); - - ibmvnic_send_crq(adapter, &newcrq); -} - static void handle_error_indication(union ibmvnic_crq *crq, struct ibmvnic_adapter *adapter) { @@ -2563,7 +2501,6 @@ static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq, struct device *dev = &adapter->vdev->dev; struct ibmvnic_login_rsp_buffer *login_rsp = adapter->login_rsp_buf; struct ibmvnic_login_buffer *login = adapter->login_buf; - union ibmvnic_crq crq; int i; dma_unmap_single(dev, adapter->login_buf_token, adapter->login_buf_sz, @@ -2598,11 +2535,6 @@ static int handle_login_rsp(union ibmvnic_crq *login_rsp_crq, } complete(&adapter->init_done); - memset(&crq, 0, sizeof(crq)); - crq.request_ras_comp_num.first = IBMVNIC_CRQ_CMD; - crq.request_ras_comp_num.cmd = REQUEST_RAS_COMP_NUM; - ibmvnic_send_crq(adapter, &crq); - return 0; } @@ -2838,476 +2770,6 @@ out: } } -static void handle_control_ras_rsp(union ibmvnic_crq *crq, - struct ibmvnic_adapter *adapter) -{ - u8 correlator = crq->control_ras_rsp.correlator; - struct device *dev = &adapter->vdev->dev; - bool found = false; - int i; - - if (crq->control_ras_rsp.rc.code) { - dev_warn(dev, "Control ras failed rc=%d\n", - crq->control_ras_rsp.rc.code); - return; - } - - for (i = 0; i < adapter->ras_comp_num; i++) { - if (adapter->ras_comps[i].correlator == correlator) { - found = true; - break; - } - } - - if (!found) { - dev_warn(dev, "Correlator not found on control_ras_rsp\n"); - return; - } - - switch (crq->control_ras_rsp.op) { - case IBMVNIC_TRACE_LEVEL: - adapter->ras_comps[i].trace_level = crq->control_ras.level; - break; - case IBMVNIC_ERROR_LEVEL: - adapter->ras_comps[i].error_check_level = - crq->control_ras.level; - break; - case IBMVNIC_TRACE_PAUSE: - adapter->ras_comp_int[i].paused = 1; - break; - case IBMVNIC_TRACE_RESUME: - adapter->ras_comp_int[i].paused = 0; - break; - case IBMVNIC_TRACE_ON: - adapter->ras_comps[i].trace_on = 1; - break; - case IBMVNIC_TRACE_OFF: - adapter->ras_comps[i].trace_on = 0; - break; - case IBMVNIC_CHG_TRACE_BUFF_SZ: - /* trace_buff_sz is 3 bytes, stuff it into an int */ - ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[0] = 0; - ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[1] = - crq->control_ras_rsp.trace_buff_sz[0]; - ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[2] = - crq->control_ras_rsp.trace_buff_sz[1]; - ((u8 *)(&adapter->ras_comps[i].trace_buff_size))[3] = - crq->control_ras_rsp.trace_buff_sz[2]; - break; - default: - dev_err(dev, "invalid op %d on control_ras_rsp", - crq->control_ras_rsp.op); - } -} - -static ssize_t trace_read(struct file *file, char __user *user_buf, size_t len, - loff_t *ppos) -{ - struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; - struct ibmvnic_adapter *adapter = ras_comp_int->adapter; - struct device *dev = &adapter->vdev->dev; - struct ibmvnic_fw_trace_entry *trace; - int num = ras_comp_int->num; - union ibmvnic_crq crq; - dma_addr_t trace_tok; - - if (*ppos >= be32_to_cpu(adapter->ras_comps[num].trace_buff_size)) - return 0; - - trace = - dma_alloc_coherent(dev, - be32_to_cpu(adapter->ras_comps[num]. - trace_buff_size), &trace_tok, - GFP_KERNEL); - if (!trace) { - dev_err(dev, "Couldn't alloc trace buffer\n"); - return 0; - } - - memset(&crq, 0, sizeof(crq)); - crq.collect_fw_trace.first = IBMVNIC_CRQ_CMD; - crq.collect_fw_trace.cmd = COLLECT_FW_TRACE; - crq.collect_fw_trace.correlator = adapter->ras_comps[num].correlator; - crq.collect_fw_trace.ioba = cpu_to_be32(trace_tok); - crq.collect_fw_trace.len = adapter->ras_comps[num].trace_buff_size; - - init_completion(&adapter->fw_done); - ibmvnic_send_crq(adapter, &crq); - wait_for_completion(&adapter->fw_done); - - if (*ppos + len > be32_to_cpu(adapter->ras_comps[num].trace_buff_size)) - len = - be32_to_cpu(adapter->ras_comps[num].trace_buff_size) - - *ppos; - - copy_to_user(user_buf, &((u8 *)trace)[*ppos], len); - - dma_free_coherent(dev, - be32_to_cpu(adapter->ras_comps[num].trace_buff_size), - trace, trace_tok); - *ppos += len; - return len; -} - -static const struct file_operations trace_ops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = trace_read, -}; - -static ssize_t paused_read(struct file *file, char __user *user_buf, size_t len, - loff_t *ppos) -{ - struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; - struct ibmvnic_adapter *adapter = ras_comp_int->adapter; - int num = ras_comp_int->num; - char buff[5]; /* 1 or 0 plus \n and \0 */ - int size; - - size = sprintf(buff, "%d\n", adapter->ras_comp_int[num].paused); - - if (*ppos >= size) - return 0; - - copy_to_user(user_buf, buff, size); - *ppos += size; - return size; -} - -static ssize_t paused_write(struct file *file, const char __user *user_buf, - size_t len, loff_t *ppos) -{ - struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; - struct ibmvnic_adapter *adapter = ras_comp_int->adapter; - int num = ras_comp_int->num; - union ibmvnic_crq crq; - unsigned long val; - char buff[9]; /* decimal max int plus \n and \0 */ - - copy_from_user(buff, user_buf, sizeof(buff)); - val = kstrtoul(buff, 10, NULL); - - adapter->ras_comp_int[num].paused = val ? 1 : 0; - - memset(&crq, 0, sizeof(crq)); - crq.control_ras.first = IBMVNIC_CRQ_CMD; - crq.control_ras.cmd = CONTROL_RAS; - crq.control_ras.correlator = adapter->ras_comps[num].correlator; - crq.control_ras.op = val ? IBMVNIC_TRACE_PAUSE : IBMVNIC_TRACE_RESUME; - ibmvnic_send_crq(adapter, &crq); - - return len; -} - -static const struct file_operations paused_ops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = paused_read, - .write = paused_write, -}; - -static ssize_t tracing_read(struct file *file, char __user *user_buf, - size_t len, loff_t *ppos) -{ - struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; - struct ibmvnic_adapter *adapter = ras_comp_int->adapter; - int num = ras_comp_int->num; - char buff[5]; /* 1 or 0 plus \n and \0 */ - int size; - - size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_on); - - if (*ppos >= size) - return 0; - - copy_to_user(user_buf, buff, size); - *ppos += size; - return size; -} - -static ssize_t tracing_write(struct file *file, const char __user *user_buf, - size_t len, loff_t *ppos) -{ - struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; - struct ibmvnic_adapter *adapter = ras_comp_int->adapter; - int num = ras_comp_int->num; - union ibmvnic_crq crq; - unsigned long val; - char buff[9]; /* decimal max int plus \n and \0 */ - - copy_from_user(buff, user_buf, sizeof(buff)); - val = kstrtoul(buff, 10, NULL); - - memset(&crq, 0, sizeof(crq)); - crq.control_ras.first = IBMVNIC_CRQ_CMD; - crq.control_ras.cmd = CONTROL_RAS; - crq.control_ras.correlator = adapter->ras_comps[num].correlator; - crq.control_ras.op = val ? IBMVNIC_TRACE_ON : IBMVNIC_TRACE_OFF; - - return len; -} - -static const struct file_operations tracing_ops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = tracing_read, - .write = tracing_write, -}; - -static ssize_t error_level_read(struct file *file, char __user *user_buf, - size_t len, loff_t *ppos) -{ - struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; - struct ibmvnic_adapter *adapter = ras_comp_int->adapter; - int num = ras_comp_int->num; - char buff[5]; /* decimal max char plus \n and \0 */ - int size; - - size = sprintf(buff, "%d\n", adapter->ras_comps[num].error_check_level); - - if (*ppos >= size) - return 0; - - copy_to_user(user_buf, buff, size); - *ppos += size; - return size; -} - -static ssize_t error_level_write(struct file *file, const char __user *user_buf, - size_t len, loff_t *ppos) -{ - struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; - struct ibmvnic_adapter *adapter = ras_comp_int->adapter; - int num = ras_comp_int->num; - union ibmvnic_crq crq; - unsigned long val; - char buff[9]; /* decimal max int plus \n and \0 */ - - copy_from_user(buff, user_buf, sizeof(buff)); - val = kstrtoul(buff, 10, NULL); - - if (val > 9) - val = 9; - - memset(&crq, 0, sizeof(crq)); - crq.control_ras.first = IBMVNIC_CRQ_CMD; - crq.control_ras.cmd = CONTROL_RAS; - crq.control_ras.correlator = adapter->ras_comps[num].correlator; - crq.control_ras.op = IBMVNIC_ERROR_LEVEL; - crq.control_ras.level = val; - ibmvnic_send_crq(adapter, &crq); - - return len; -} - -static const struct file_operations error_level_ops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = error_level_read, - .write = error_level_write, -}; - -static ssize_t trace_level_read(struct file *file, char __user *user_buf, - size_t len, loff_t *ppos) -{ - struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; - struct ibmvnic_adapter *adapter = ras_comp_int->adapter; - int num = ras_comp_int->num; - char buff[5]; /* decimal max char plus \n and \0 */ - int size; - - size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_level); - if (*ppos >= size) - return 0; - - copy_to_user(user_buf, buff, size); - *ppos += size; - return size; -} - -static ssize_t trace_level_write(struct file *file, const char __user *user_buf, - size_t len, loff_t *ppos) -{ - struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; - struct ibmvnic_adapter *adapter = ras_comp_int->adapter; - union ibmvnic_crq crq; - unsigned long val; - char buff[9]; /* decimal max int plus \n and \0 */ - - copy_from_user(buff, user_buf, sizeof(buff)); - val = kstrtoul(buff, 10, NULL); - if (val > 9) - val = 9; - - memset(&crq, 0, sizeof(crq)); - crq.control_ras.first = IBMVNIC_CRQ_CMD; - crq.control_ras.cmd = CONTROL_RAS; - crq.control_ras.correlator = - adapter->ras_comps[ras_comp_int->num].correlator; - crq.control_ras.op = IBMVNIC_TRACE_LEVEL; - crq.control_ras.level = val; - ibmvnic_send_crq(adapter, &crq); - - return len; -} - -static const struct file_operations trace_level_ops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = trace_level_read, - .write = trace_level_write, -}; - -static ssize_t trace_buff_size_read(struct file *file, char __user *user_buf, - size_t len, loff_t *ppos) -{ - struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; - struct ibmvnic_adapter *adapter = ras_comp_int->adapter; - int num = ras_comp_int->num; - char buff[9]; /* decimal max int plus \n and \0 */ - int size; - - size = sprintf(buff, "%d\n", adapter->ras_comps[num].trace_buff_size); - if (*ppos >= size) - return 0; - - copy_to_user(user_buf, buff, size); - *ppos += size; - return size; -} - -static ssize_t trace_buff_size_write(struct file *file, - const char __user *user_buf, size_t len, - loff_t *ppos) -{ - struct ibmvnic_fw_comp_internal *ras_comp_int = file->private_data; - struct ibmvnic_adapter *adapter = ras_comp_int->adapter; - union ibmvnic_crq crq; - unsigned long val; - char buff[9]; /* decimal max int plus \n and \0 */ - - copy_from_user(buff, user_buf, sizeof(buff)); - val = kstrtoul(buff, 10, NULL); - - memset(&crq, 0, sizeof(crq)); - crq.control_ras.first = IBMVNIC_CRQ_CMD; - crq.control_ras.cmd = CONTROL_RAS; - crq.control_ras.correlator = - adapter->ras_comps[ras_comp_int->num].correlator; - crq.control_ras.op = IBMVNIC_CHG_TRACE_BUFF_SZ; - /* trace_buff_sz is 3 bytes, stuff an int into it */ - crq.control_ras.trace_buff_sz[0] = ((u8 *)(&val))[5]; - crq.control_ras.trace_buff_sz[1] = ((u8 *)(&val))[6]; - crq.control_ras.trace_buff_sz[2] = ((u8 *)(&val))[7]; - ibmvnic_send_crq(adapter, &crq); - - return len; -} - -static const struct file_operations trace_size_ops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = trace_buff_size_read, - .write = trace_buff_size_write, -}; - -static void handle_request_ras_comps_rsp(union ibmvnic_crq *crq, - struct ibmvnic_adapter *adapter) -{ - struct device *dev = &adapter->vdev->dev; - struct dentry *dir_ent; - struct dentry *ent; - int i; - - debugfs_remove_recursive(adapter->ras_comps_ent); - - adapter->ras_comps_ent = debugfs_create_dir("ras_comps", - adapter->debugfs_dir); - if (!adapter->ras_comps_ent || IS_ERR(adapter->ras_comps_ent)) { - dev_info(dev, "debugfs create ras_comps dir failed\n"); - return; - } - - for (i = 0; i < adapter->ras_comp_num; i++) { - dir_ent = debugfs_create_dir(adapter->ras_comps[i].name, - adapter->ras_comps_ent); - if (!dir_ent || IS_ERR(dir_ent)) { - dev_info(dev, "debugfs create %s dir failed\n", - adapter->ras_comps[i].name); - continue; - } - - adapter->ras_comp_int[i].adapter = adapter; - adapter->ras_comp_int[i].num = i; - adapter->ras_comp_int[i].desc_blob.data = - &adapter->ras_comps[i].description; - adapter->ras_comp_int[i].desc_blob.size = - sizeof(adapter->ras_comps[i].description); - - /* Don't need to remember the dentry's because the debugfs dir - * gets removed recursively - */ - ent = debugfs_create_blob("description", S_IRUGO, dir_ent, - &adapter->ras_comp_int[i].desc_blob); - ent = debugfs_create_file("trace_buf_size", S_IRUGO | S_IWUSR, - dir_ent, &adapter->ras_comp_int[i], - &trace_size_ops); - ent = debugfs_create_file("trace_level", - S_IRUGO | - (adapter->ras_comps[i].trace_level != - 0xFF ? S_IWUSR : 0), - dir_ent, &adapter->ras_comp_int[i], - &trace_level_ops); - ent = debugfs_create_file("error_level", - S_IRUGO | - (adapter-> - ras_comps[i].error_check_level != - 0xFF ? S_IWUSR : 0), - dir_ent, &adapter->ras_comp_int[i], - &trace_level_ops); - ent = debugfs_create_file("tracing", S_IRUGO | S_IWUSR, - dir_ent, &adapter->ras_comp_int[i], - &tracing_ops); - ent = debugfs_create_file("paused", S_IRUGO | S_IWUSR, - dir_ent, &adapter->ras_comp_int[i], - &paused_ops); - ent = debugfs_create_file("trace", S_IRUGO, dir_ent, - &adapter->ras_comp_int[i], - &trace_ops); - } -} - -static void handle_request_ras_comp_num_rsp(union ibmvnic_crq *crq, - struct ibmvnic_adapter *adapter) -{ - int len = adapter->ras_comp_num * sizeof(struct ibmvnic_fw_component); - struct device *dev = &adapter->vdev->dev; - union ibmvnic_crq newcrq; - - adapter->ras_comps = dma_alloc_coherent(dev, len, - &adapter->ras_comps_tok, - GFP_KERNEL); - if (!adapter->ras_comps) { - if (!firmware_has_feature(FW_FEATURE_CMO)) - dev_err(dev, "Couldn't alloc fw comps buffer\n"); - return; - } - - adapter->ras_comp_int = kmalloc(adapter->ras_comp_num * - sizeof(struct ibmvnic_fw_comp_internal), - GFP_KERNEL); - if (!adapter->ras_comp_int) - dma_free_coherent(dev, len, adapter->ras_comps, - adapter->ras_comps_tok); - - memset(&newcrq, 0, sizeof(newcrq)); - newcrq.request_ras_comps.first = IBMVNIC_CRQ_CMD; - newcrq.request_ras_comps.cmd = REQUEST_RAS_COMPS; - newcrq.request_ras_comps.ioba = cpu_to_be32(adapter->ras_comps_tok); - newcrq.request_ras_comps.len = cpu_to_be32(len); - ibmvnic_send_crq(adapter, &newcrq); -} - static void ibmvnic_free_inflight(struct ibmvnic_adapter *adapter) { struct ibmvnic_inflight_cmd *inflight_cmd, *tmp1; @@ -3329,9 +2791,6 @@ static void ibmvnic_free_inflight(struct ibmvnic_adapter *adapter) kfree(adapter->login_rsp_buf); kfree(adapter->login_buf); break; - case REQUEST_DUMP: - complete(&adapter->fw_done); - break; case REQUEST_ERROR_INFO: spin_lock_irqsave(&adapter->error_list_lock, flags2); list_for_each_entry_safe(error_buff, tmp2, @@ -3491,14 +2950,6 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq, netdev_dbg(netdev, "Got Statistics Response\n"); complete(&adapter->stats_done); break; - case REQUEST_DUMP_SIZE_RSP: - netdev_dbg(netdev, "Got Request Dump Size Response\n"); - handle_dump_size_rsp(crq, adapter); - break; - case REQUEST_DUMP_RSP: - netdev_dbg(netdev, "Got Request Dump Response\n"); - complete(&adapter->fw_done); - break; case QUERY_IP_OFFLOAD_RSP: netdev_dbg(netdev, "Got Query IP offload Response\n"); handle_query_ip_offload_rsp(adapter); @@ -3513,24 +2964,6 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq, DMA_TO_DEVICE); complete(&adapter->init_done); break; - case REQUEST_RAS_COMP_NUM_RSP: - netdev_dbg(netdev, "Got Request RAS Comp Num Response\n"); - if (crq->request_ras_comp_num_rsp.rc.code == 10) { - netdev_dbg(netdev, "Request RAS Comp Num not supported\n"); - break; - } - adapter->ras_comp_num = - be32_to_cpu(crq->request_ras_comp_num_rsp.num_components); - handle_request_ras_comp_num_rsp(crq, adapter); - break; - case REQUEST_RAS_COMPS_RSP: - netdev_dbg(netdev, "Got Request RAS Comps Response\n"); - handle_request_ras_comps_rsp(crq, adapter); - break; - case CONTROL_RAS_RSP: - netdev_dbg(netdev, "Got Control RAS Response\n"); - handle_control_ras_rsp(crq, adapter); - break; case COLLECT_FW_TRACE_RSP: netdev_dbg(netdev, "Got Collect firmware trace Response\n"); complete(&adapter->fw_done); @@ -3725,45 +3158,6 @@ map_failed: return retrc; } -/* debugfs for dump */ -static int ibmvnic_dump_show(struct seq_file *seq, void *v) -{ - struct net_device *netdev = seq->private; - struct ibmvnic_adapter *adapter = netdev_priv(netdev); - struct device *dev = &adapter->vdev->dev; - union ibmvnic_crq crq; - - memset(&crq, 0, sizeof(crq)); - crq.request_dump_size.first = IBMVNIC_CRQ_CMD; - crq.request_dump_size.cmd = REQUEST_DUMP_SIZE; - - init_completion(&adapter->fw_done); - ibmvnic_send_crq(adapter, &crq); - wait_for_completion(&adapter->fw_done); - - seq_write(seq, adapter->dump_data, adapter->dump_data_size); - - dma_unmap_single(dev, adapter->dump_data_token, adapter->dump_data_size, - DMA_BIDIRECTIONAL); - - kfree(adapter->dump_data); - - return 0; -} - -static int ibmvnic_dump_open(struct inode *inode, struct file *file) -{ - return single_open(file, ibmvnic_dump_show, inode->i_private); -} - -static const struct file_operations ibmvnic_dump_ops = { - .owner = THIS_MODULE, - .open = ibmvnic_dump_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - static void handle_crq_init_rsp(struct work_struct *work) { struct ibmvnic_adapter *adapter = container_of(work, @@ -3826,8 +3220,6 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter) { struct device *dev = &adapter->vdev->dev; unsigned long timeout = msecs_to_jiffies(30000); - struct dentry *ent; - char buf[17]; /* debugfs name buf */ int rc; rc = ibmvnic_init_crq_queue(adapter); @@ -3845,30 +3237,10 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter) return -ENOMEM; } - snprintf(buf, sizeof(buf), "ibmvnic_%x", adapter->vdev->unit_address); - ent = debugfs_create_dir(buf, NULL); - if (!ent || IS_ERR(ent)) { - dev_info(dev, "debugfs create directory failed\n"); - adapter->debugfs_dir = NULL; - } else { - adapter->debugfs_dir = ent; - ent = debugfs_create_file("dump", S_IRUGO, - adapter->debugfs_dir, - adapter->netdev, &ibmvnic_dump_ops); - if (!ent || IS_ERR(ent)) { - dev_info(dev, "debugfs create dump file failed\n"); - adapter->debugfs_dump = NULL; - } else { - adapter->debugfs_dump = ent; - } - } - init_completion(&adapter->init_done); ibmvnic_send_crq_init(adapter); if (!wait_for_completion_timeout(&adapter->init_done, timeout)) { dev_err(dev, "Initialization sequence timed out\n"); - if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir)) - debugfs_remove_recursive(adapter->debugfs_dir); ibmvnic_release_crq_queue(adapter); return -1; } diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 42ad648c174d..b0d0b890d033 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -772,20 +772,10 @@ enum ibmvnic_commands { ERROR_INDICATION = 0x08, REQUEST_ERROR_INFO = 0x09, REQUEST_ERROR_RSP = 0x89, - REQUEST_DUMP_SIZE = 0x0A, - REQUEST_DUMP_SIZE_RSP = 0x8A, - REQUEST_DUMP = 0x0B, - REQUEST_DUMP_RSP = 0x8B, LOGICAL_LINK_STATE = 0x0C, LOGICAL_LINK_STATE_RSP = 0x8C, REQUEST_STATISTICS = 0x0D, REQUEST_STATISTICS_RSP = 0x8D, - REQUEST_RAS_COMP_NUM = 0x0E, - REQUEST_RAS_COMP_NUM_RSP = 0x8E, - REQUEST_RAS_COMPS = 0x0F, - REQUEST_RAS_COMPS_RSP = 0x8F, - CONTROL_RAS = 0x10, - CONTROL_RAS_RSP = 0x90, COLLECT_FW_TRACE = 0x11, COLLECT_FW_TRACE_RSP = 0x91, LINK_STATE_INDICATION = 0x12, @@ -806,8 +796,6 @@ enum ibmvnic_commands { ACL_CHANGE_INDICATION = 0x1A, ACL_QUERY = 0x1B, ACL_QUERY_RSP = 0x9B, - REQUEST_DEBUG_STATS = 0x1C, - REQUEST_DEBUG_STATS_RSP = 0x9C, QUERY_MAP = 0x1D, QUERY_MAP_RSP = 0x9D, REQUEST_MAP = 0x1E, @@ -925,13 +913,6 @@ struct ibmvnic_error_buff { __be32 error_id; }; -struct ibmvnic_fw_comp_internal { - struct ibmvnic_adapter *adapter; - int num; - struct debugfs_blob_wrapper desc_blob; - int paused; -}; - struct ibmvnic_inflight_cmd { union ibmvnic_crq crq; struct list_head list; @@ -995,18 +976,7 @@ struct ibmvnic_adapter { struct list_head errors; spinlock_t error_list_lock; - /* debugfs */ - struct dentry *debugfs_dir; - struct dentry *debugfs_dump; struct completion fw_done; - char *dump_data; - dma_addr_t dump_data_token; - int dump_data_size; - int ras_comp_num; - struct ibmvnic_fw_component *ras_comps; - struct ibmvnic_fw_comp_internal *ras_comp_int; - dma_addr_t ras_comps_tok; - struct dentry *ras_comps_ent; /* in-flight commands that allocate and/or map memory*/ struct list_head inflight; -- cgit From bae76dd95b69350f6342d8a4ea1bdeec4c218053 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Wed, 29 Mar 2017 15:38:37 -0400 Subject: net: dsa: mv88e6xxx: debug ATU Age Time The ATU ageing time value programmed in the switch is rounded up to the nearest multiple of its coefficient (variable depending on the model.) Add a debug message to inform the user about the exact programmed value. On 6352, "brctl setageing br0 18" gives "AgeTime set to 0x01 (15000 ms)" while on 6390 we get "AgeTime set to 0x05 (18750 ms)". Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/global1_atu.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/global1_atu.c b/drivers/net/dsa/mv88e6xxx/global1_atu.c index 492e00dea3ce..fa7e7db5171b 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_atu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_atu.c @@ -63,7 +63,14 @@ int mv88e6xxx_g1_atu_set_age_time(struct mv88e6xxx_chip *chip, val &= ~0xff0; val |= age_time << 4; - return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val); + err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val); + if (err) + return err; + + dev_dbg(chip->dev, "AgeTime set to 0x%02x (%d ms)\n", age_time, + age_time * coeff); + + return 0; } /* Offset 0x0B: ATU Operation Register */ -- cgit From 1935299d9c946ac37eba3f820d2ce20885ec0739 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Thu, 30 Mar 2017 06:49:19 +0800 Subject: net: tcp: Refine the __tcp_select_window 1. Move the "window = tp->rcv_wnd;" into the condition block without tp->rx_opt.rcv_wscale. Because it is unnecessary when enable wscale; 2. Use the macro ALIGN instead of two statements. The two statements are used to make window align to 1< Signed-off-by: David S. Miller --- net/ipv4/tcp_output.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 22548b5f05cb..13971942211b 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2561,7 +2561,6 @@ u32 __tcp_select_window(struct sock *sk) /* Don't do rounding if we are using window scaling, since the * scaled window will not line up with the MSS boundary anyway. */ - window = tp->rcv_wnd; if (tp->rx_opt.rcv_wscale) { window = free_space; @@ -2569,10 +2568,9 @@ u32 __tcp_select_window(struct sock *sk) * Import case: prevent zero window announcement if * 1< mss. */ - if (((window >> tp->rx_opt.rcv_wscale) << tp->rx_opt.rcv_wscale) != window) - window = (((window >> tp->rx_opt.rcv_wscale) + 1) - << tp->rx_opt.rcv_wscale); + window = ALIGN(window, (1 << tp->rx_opt.rcv_wscale)); } else { + window = tp->rcv_wnd; /* Get the largest window that is a nice multiple of mss. * Window clamp already applied above. * If our current window offering is within 1 mss of the @@ -2582,7 +2580,7 @@ u32 __tcp_select_window(struct sock *sk) * is too small. */ if (window <= free_space - mss || window > free_space) - window = (free_space / mss) * mss; + window = rounddown(free_space, mss); else if (mss == full_space && free_space > window + (full_space >> 1)) window = free_space; -- cgit From f992887c34a15d40a257c73fe59800826bcaf1a4 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Thu, 30 Mar 2017 02:48:54 -0400 Subject: ibmvnic: Update main crq initialization and release Update the initialization and release routines for the crq queue so that we validate the crq queue. Additionally this updates the naming of the init and release routines for the crq queue to drop the ibmvnic prefix. This matches the naming for similar routines in the driver Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 1e8ba784ec92..01ab60f4a92a 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -113,7 +113,7 @@ static void send_login(struct ibmvnic_adapter *adapter); static void send_cap_queries(struct ibmvnic_adapter *adapter); static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter); static int ibmvnic_init(struct ibmvnic_adapter *); -static void ibmvnic_release_crq_queue(struct ibmvnic_adapter *); +static void release_crq_queue(struct ibmvnic_adapter *); struct ibmvnic_stat { char name[ETH_GSTRING_LEN]; @@ -612,7 +612,7 @@ static void ibmvnic_release_resources(struct ibmvnic_adapter *adapter) adapter->rx_pool = NULL; release_sub_crqs(adapter); - ibmvnic_release_crq_queue(adapter); + release_crq_queue(adapter); if (adapter->stats_token) dma_unmap_single(dev, adapter->stats_token, @@ -3069,12 +3069,15 @@ static int ibmvnic_reset_crq(struct ibmvnic_adapter *adapter) return rc; } -static void ibmvnic_release_crq_queue(struct ibmvnic_adapter *adapter) +static void release_crq_queue(struct ibmvnic_adapter *adapter) { struct ibmvnic_crq_queue *crq = &adapter->crq; struct vio_dev *vdev = adapter->vdev; long rc; + if (!crq->msgs) + return; + netdev_dbg(adapter->netdev, "Releasing CRQ\n"); free_irq(vdev->irq, adapter); tasklet_kill(&adapter->tasklet); @@ -3085,15 +3088,19 @@ static void ibmvnic_release_crq_queue(struct ibmvnic_adapter *adapter) dma_unmap_single(&vdev->dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL); free_page((unsigned long)crq->msgs); + crq->msgs = NULL; } -static int ibmvnic_init_crq_queue(struct ibmvnic_adapter *adapter) +static int init_crq_queue(struct ibmvnic_adapter *adapter) { struct ibmvnic_crq_queue *crq = &adapter->crq; struct device *dev = &adapter->vdev->dev; struct vio_dev *vdev = adapter->vdev; int rc, retrc = -ENOMEM; + if (crq->msgs) + return 0; + crq->msgs = (union ibmvnic_crq *)get_zeroed_page(GFP_KERNEL); /* Should we allocate more than one page? */ @@ -3155,6 +3162,7 @@ reg_crq_failed: dma_unmap_single(dev, crq->msg_token, PAGE_SIZE, DMA_BIDIRECTIONAL); map_failed: free_page((unsigned long)crq->msgs); + crq->msgs = NULL; return retrc; } @@ -3222,7 +3230,7 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter) unsigned long timeout = msecs_to_jiffies(30000); int rc; - rc = ibmvnic_init_crq_queue(adapter); + rc = init_crq_queue(adapter); if (rc) { dev_err(dev, "Couldn't initialize crq. rc=%d\n", rc); return rc; @@ -3232,7 +3240,7 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter) sizeof(struct ibmvnic_statistics), DMA_FROM_DEVICE); if (dma_mapping_error(dev, adapter->stats_token)) { - ibmvnic_release_crq_queue(adapter); + release_crq_queue(adapter); dev_err(dev, "Couldn't map stats buffer\n"); return -ENOMEM; } @@ -3241,7 +3249,7 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter) ibmvnic_send_crq_init(adapter); if (!wait_for_completion_timeout(&adapter->init_done, timeout)) { dev_err(dev, "Initialization sequence timed out\n"); - ibmvnic_release_crq_queue(adapter); + release_crq_queue(adapter); return -1; } -- cgit From f0b8c96cbcc5fbd4f66abcbf4bc442a1066d8899 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Thu, 30 Mar 2017 02:49:00 -0400 Subject: ibmvnic: Create init and release routines for the bounce buffer Move the handling of initialization and releasing the bounce buffer to their own init and release routines. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 77 +++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 01ab60f4a92a..c3e5305604c8 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -372,6 +372,50 @@ static void free_rx_pool(struct ibmvnic_adapter *adapter, pool->rx_buff = NULL; } +static void release_bounce_buffer(struct ibmvnic_adapter *adapter) +{ + struct device *dev = &adapter->vdev->dev; + + if (!adapter->bounce_buffer) + return; + + if (!dma_mapping_error(dev, adapter->bounce_buffer_dma)) { + dma_unmap_single(dev, adapter->bounce_buffer_dma, + adapter->bounce_buffer_size, + DMA_BIDIRECTIONAL); + adapter->bounce_buffer_dma = DMA_ERROR_CODE; + } + + kfree(adapter->bounce_buffer); + adapter->bounce_buffer = NULL; +} + +static int init_bounce_buffer(struct net_device *netdev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + struct device *dev = &adapter->vdev->dev; + char *buf; + int buf_sz; + dma_addr_t map_addr; + + buf_sz = (netdev->mtu + ETH_HLEN - 1) / PAGE_SIZE + 1; + buf = kmalloc(adapter->bounce_buffer_size, GFP_KERNEL); + if (!buf) + return -1; + + map_addr = dma_map_single(dev, buf, buf_sz, DMA_TO_DEVICE); + if (dma_mapping_error(dev, map_addr)) { + dev_err(dev, "Couldn't map bounce buffer\n"); + kfree(buf); + return -1; + } + + adapter->bounce_buffer = buf; + adapter->bounce_buffer_size = buf_sz; + adapter->bounce_buffer_dma = map_addr; + return 0; +} + static int ibmvnic_login(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); @@ -500,20 +544,11 @@ static int ibmvnic_open(struct net_device *netdev) tx_pool->consumer_index = 0; tx_pool->producer_index = 0; } - adapter->bounce_buffer_size = - (netdev->mtu + ETH_HLEN - 1) / PAGE_SIZE + 1; - adapter->bounce_buffer = kmalloc(adapter->bounce_buffer_size, - GFP_KERNEL); - if (!adapter->bounce_buffer) - goto bounce_alloc_failed; - adapter->bounce_buffer_dma = dma_map_single(dev, adapter->bounce_buffer, - adapter->bounce_buffer_size, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, adapter->bounce_buffer_dma)) { - dev_err(dev, "Couldn't map tx bounce buffer\n"); - goto bounce_map_failed; - } + rc = init_bounce_buffer(netdev); + if (rc) + goto bounce_init_failed; + replenish_pools(adapter); /* We're ready to receive frames, enable the sub-crq interrupts and @@ -536,9 +571,7 @@ static int ibmvnic_open(struct net_device *netdev) return 0; -bounce_map_failed: - kfree(adapter->bounce_buffer); -bounce_alloc_failed: +bounce_init_failed: i = tx_subcrqs - 1; kfree(adapter->tx_pool[i].free_map); tx_fm_alloc_failed: @@ -578,17 +611,7 @@ static void ibmvnic_release_resources(struct ibmvnic_adapter *adapter) int tx_scrqs, rx_scrqs; int i; - if (adapter->bounce_buffer) { - if (!dma_mapping_error(dev, adapter->bounce_buffer_dma)) { - dma_unmap_single(&adapter->vdev->dev, - adapter->bounce_buffer_dma, - adapter->bounce_buffer_size, - DMA_BIDIRECTIONAL); - adapter->bounce_buffer_dma = DMA_ERROR_CODE; - } - kfree(adapter->bounce_buffer); - adapter->bounce_buffer = NULL; - } + release_bounce_buffer(adapter); tx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); for (i = 0; i < tx_scrqs; i++) { -- cgit From c657e32cd0555e97ae8903f8a5a9d7c2f3579650 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Thu, 30 Mar 2017 02:49:06 -0400 Subject: ibmvnic: Create init and release routines for the tx pool Move the initialization and the release of the tx pool to their own routines, and update them to do validation. This also adds validation to the release of the long term buffer. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 134 +++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 56 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index c3e5305604c8..a9399e96e942 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -206,6 +206,9 @@ static void free_long_term_buff(struct ibmvnic_adapter *adapter, { struct device *dev = &adapter->vdev->dev; + if (!ltb->buff) + return; + dma_free_coherent(dev, ltb->size, ltb->buff, ltb->addr); if (!adapter->failover) send_request_unmap(adapter, ltb->map_id); @@ -372,6 +375,75 @@ static void free_rx_pool(struct ibmvnic_adapter *adapter, pool->rx_buff = NULL; } +static void release_tx_pools(struct ibmvnic_adapter *adapter) +{ + struct ibmvnic_tx_pool *tx_pool; + int i, tx_scrqs; + + if (!adapter->tx_pool) + return; + + tx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); + for (i = 0; i < tx_scrqs; i++) { + tx_pool = &adapter->tx_pool[i]; + kfree(tx_pool->tx_buff); + free_long_term_buff(adapter, &tx_pool->long_term_buff); + kfree(tx_pool->free_map); + } + + kfree(adapter->tx_pool); + adapter->tx_pool = NULL; +} + +static int init_tx_pools(struct net_device *netdev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_tx_pool *tx_pool; + int tx_subcrqs; + int i, j; + + tx_subcrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); + adapter->tx_pool = kcalloc(tx_subcrqs, + sizeof(struct ibmvnic_tx_pool), GFP_KERNEL); + if (!adapter->tx_pool) + return -1; + + for (i = 0; i < tx_subcrqs; i++) { + tx_pool = &adapter->tx_pool[i]; + tx_pool->tx_buff = kcalloc(adapter->req_tx_entries_per_subcrq, + sizeof(struct ibmvnic_tx_buff), + GFP_KERNEL); + if (!tx_pool->tx_buff) { + dev_err(dev, "tx pool buffer allocation failed\n"); + release_tx_pools(adapter); + return -1; + } + + if (alloc_long_term_buff(adapter, &tx_pool->long_term_buff, + adapter->req_tx_entries_per_subcrq * + adapter->req_mtu)) { + release_tx_pools(adapter); + return -1; + } + + tx_pool->free_map = kcalloc(adapter->req_tx_entries_per_subcrq, + sizeof(int), GFP_KERNEL); + if (!tx_pool->free_map) { + release_tx_pools(adapter); + return -1; + } + + for (j = 0; j < adapter->req_tx_entries_per_subcrq; j++) + tx_pool->free_map[j] = j; + + tx_pool->consumer_index = 0; + tx_pool->producer_index = 0; + } + + return 0; +} + static void release_bounce_buffer(struct ibmvnic_adapter *adapter) { struct device *dev = &adapter->vdev->dev; @@ -452,7 +524,6 @@ static int ibmvnic_open(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); struct device *dev = &adapter->vdev->dev; - struct ibmvnic_tx_pool *tx_pool; union ibmvnic_crq crq; int rxadd_subcrqs; u64 *size_array; @@ -514,36 +585,10 @@ static int ibmvnic_open(struct net_device *netdev) goto rx_pool_alloc_failed; } } - adapter->tx_pool = - kcalloc(tx_subcrqs, sizeof(struct ibmvnic_tx_pool), GFP_KERNEL); - - if (!adapter->tx_pool) - goto tx_pool_arr_alloc_failed; - for (i = 0; i < tx_subcrqs; i++) { - tx_pool = &adapter->tx_pool[i]; - tx_pool->tx_buff = - kcalloc(adapter->req_tx_entries_per_subcrq, - sizeof(struct ibmvnic_tx_buff), GFP_KERNEL); - if (!tx_pool->tx_buff) - goto tx_pool_alloc_failed; - - if (alloc_long_term_buff(adapter, &tx_pool->long_term_buff, - adapter->req_tx_entries_per_subcrq * - adapter->req_mtu)) - goto tx_ltb_alloc_failed; - - tx_pool->free_map = - kcalloc(adapter->req_tx_entries_per_subcrq, - sizeof(int), GFP_KERNEL); - if (!tx_pool->free_map) - goto tx_fm_alloc_failed; - - for (j = 0; j < adapter->req_tx_entries_per_subcrq; j++) - tx_pool->free_map[j] = j; - tx_pool->consumer_index = 0; - tx_pool->producer_index = 0; - } + rc = init_tx_pools(netdev); + if (rc) + goto tx_pool_failed; rc = init_bounce_buffer(netdev); if (rc) @@ -574,20 +619,7 @@ static int ibmvnic_open(struct net_device *netdev) bounce_init_failed: i = tx_subcrqs - 1; kfree(adapter->tx_pool[i].free_map); -tx_fm_alloc_failed: - free_long_term_buff(adapter, &adapter->tx_pool[i].long_term_buff); -tx_ltb_alloc_failed: - kfree(adapter->tx_pool[i].tx_buff); -tx_pool_alloc_failed: - for (j = 0; j < i; j++) { - kfree(adapter->tx_pool[j].tx_buff); - free_long_term_buff(adapter, - &adapter->tx_pool[j].long_term_buff); - kfree(adapter->tx_pool[j].free_map); - } - kfree(adapter->tx_pool); - adapter->tx_pool = NULL; -tx_pool_arr_alloc_failed: +tx_pool_failed: i = rxadd_subcrqs; rx_pool_alloc_failed: for (j = 0; j < i; j++) { @@ -608,21 +640,11 @@ alloc_napi_failed: static void ibmvnic_release_resources(struct ibmvnic_adapter *adapter) { struct device *dev = &adapter->vdev->dev; - int tx_scrqs, rx_scrqs; + int rx_scrqs; int i; release_bounce_buffer(adapter); - - tx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); - for (i = 0; i < tx_scrqs; i++) { - struct ibmvnic_tx_pool *tx_pool = &adapter->tx_pool[i]; - - kfree(tx_pool->tx_buff); - free_long_term_buff(adapter, &tx_pool->long_term_buff); - kfree(tx_pool->free_map); - } - kfree(adapter->tx_pool); - adapter->tx_pool = NULL; + release_tx_pools(adapter); rx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); for (i = 0; i < rx_scrqs; i++) { -- cgit From 0ffe2cb7903b20e74a9f42c53016e61e187ee345 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Thu, 30 Mar 2017 02:49:12 -0400 Subject: ibmvnic: Create init and release routines for the rx pool Move the initialization and the release of the rx pool to their own routines, and update them to do validation. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 204 ++++++++++++++++++------------------- 1 file changed, 101 insertions(+), 103 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index a9399e96e942..6774b3cbc5f9 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -163,21 +163,6 @@ static long h_reg_sub_crq(unsigned long unit_address, unsigned long token, return rc; } -/* net_device_ops functions */ - -static void init_rx_pool(struct ibmvnic_adapter *adapter, - struct ibmvnic_rx_pool *rx_pool, int num, int index, - int buff_size, int active) -{ - netdev_dbg(adapter->netdev, - "Initializing rx_pool %d, %d buffs, %d bytes each\n", - index, num, buff_size); - rx_pool->size = num; - rx_pool->index = index; - rx_pool->buff_size = buff_size; - rx_pool->active = active; -} - static int alloc_long_term_buff(struct ibmvnic_adapter *adapter, struct ibmvnic_long_term_buff *ltb, int size) { @@ -214,42 +199,6 @@ static void free_long_term_buff(struct ibmvnic_adapter *adapter, send_request_unmap(adapter, ltb->map_id); } -static int alloc_rx_pool(struct ibmvnic_adapter *adapter, - struct ibmvnic_rx_pool *pool) -{ - struct device *dev = &adapter->vdev->dev; - int i; - - pool->free_map = kcalloc(pool->size, sizeof(int), GFP_KERNEL); - if (!pool->free_map) - return -ENOMEM; - - pool->rx_buff = kcalloc(pool->size, sizeof(struct ibmvnic_rx_buff), - GFP_KERNEL); - - if (!pool->rx_buff) { - dev_err(dev, "Couldn't alloc rx buffers\n"); - kfree(pool->free_map); - return -ENOMEM; - } - - if (alloc_long_term_buff(adapter, &pool->long_term_buff, - pool->size * pool->buff_size)) { - kfree(pool->free_map); - kfree(pool->rx_buff); - return -ENOMEM; - } - - for (i = 0; i < pool->size; ++i) - pool->free_map[i] = i; - - atomic_set(&pool->available, 0); - pool->next_alloc = 0; - pool->next_free = 0; - - return 0; -} - static void replenish_rx_pool(struct ibmvnic_adapter *adapter, struct ibmvnic_rx_pool *pool) { @@ -354,25 +303,105 @@ static void replenish_pools(struct ibmvnic_adapter *adapter) } } -static void free_rx_pool(struct ibmvnic_adapter *adapter, - struct ibmvnic_rx_pool *pool) +static void release_rx_pools(struct ibmvnic_adapter *adapter) { - int i; - - kfree(pool->free_map); - pool->free_map = NULL; + struct ibmvnic_rx_pool *rx_pool; + int rx_scrqs; + int i, j; - if (!pool->rx_buff) + if (!adapter->rx_pool) return; - for (i = 0; i < pool->size; i++) { - if (pool->rx_buff[i].skb) { - dev_kfree_skb_any(pool->rx_buff[i].skb); - pool->rx_buff[i].skb = NULL; + rx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); + for (i = 0; i < rx_scrqs; i++) { + rx_pool = &adapter->rx_pool[i]; + + kfree(rx_pool->free_map); + free_long_term_buff(adapter, &rx_pool->long_term_buff); + + if (!rx_pool->rx_buff) + continue; + + for (j = 0; j < rx_pool->size; j++) { + if (rx_pool->rx_buff[j].skb) { + dev_kfree_skb_any(rx_pool->rx_buff[i].skb); + rx_pool->rx_buff[i].skb = NULL; + } + } + + kfree(rx_pool->rx_buff); + } + + kfree(adapter->rx_pool); + adapter->rx_pool = NULL; +} + +static int init_rx_pools(struct net_device *netdev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_rx_pool *rx_pool; + int rxadd_subcrqs; + u64 *size_array; + int i, j; + + rxadd_subcrqs = + be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); + size_array = (u64 *)((u8 *)(adapter->login_rsp_buf) + + be32_to_cpu(adapter->login_rsp_buf->off_rxadd_buff_size)); + + adapter->rx_pool = kcalloc(rxadd_subcrqs, + sizeof(struct ibmvnic_rx_pool), + GFP_KERNEL); + if (!adapter->rx_pool) { + dev_err(dev, "Failed to allocate rx pools\n"); + return -1; + } + + for (i = 0; i < rxadd_subcrqs; i++) { + rx_pool = &adapter->rx_pool[i]; + + netdev_dbg(adapter->netdev, + "Initializing rx_pool %d, %lld buffs, %lld bytes each\n", + i, adapter->req_rx_add_entries_per_subcrq, + be64_to_cpu(size_array[i])); + + rx_pool->size = adapter->req_rx_add_entries_per_subcrq; + rx_pool->index = i; + rx_pool->buff_size = be64_to_cpu(size_array[i]); + rx_pool->active = 1; + + rx_pool->free_map = kcalloc(rx_pool->size, sizeof(int), + GFP_KERNEL); + if (!rx_pool->free_map) { + release_rx_pools(adapter); + return -1; + } + + rx_pool->rx_buff = kcalloc(rx_pool->size, + sizeof(struct ibmvnic_rx_buff), + GFP_KERNEL); + if (!rx_pool->rx_buff) { + dev_err(dev, "Couldn't alloc rx buffers\n"); + release_rx_pools(adapter); + return -1; + } + + if (alloc_long_term_buff(adapter, &rx_pool->long_term_buff, + rx_pool->size * rx_pool->buff_size)) { + release_rx_pools(adapter); + return -1; } + + for (j = 0; j < rx_pool->size; ++j) + rx_pool->free_map[j] = j; + + atomic_set(&rx_pool->available, 0); + rx_pool->next_alloc = 0; + rx_pool->next_free = 0; } - kfree(pool->rx_buff); - pool->rx_buff = NULL; + + return 0; } static void release_tx_pools(struct ibmvnic_adapter *adapter) @@ -526,10 +555,9 @@ static int ibmvnic_open(struct net_device *netdev) struct device *dev = &adapter->vdev->dev; union ibmvnic_crq crq; int rxadd_subcrqs; - u64 *size_array; int tx_subcrqs; int rc = 0; - int i, j; + int i; if (adapter->is_closed) { rc = ibmvnic_init(adapter); @@ -557,9 +585,7 @@ static int ibmvnic_open(struct net_device *netdev) be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); tx_subcrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); - size_array = (u64 *)((u8 *)(adapter->login_rsp_buf) + - be32_to_cpu(adapter->login_rsp_buf-> - off_rxadd_buff_size)); + adapter->map_id = 1; adapter->napi = kcalloc(adapter->req_rx_queues, sizeof(struct napi_struct), GFP_KERNEL); @@ -570,21 +596,12 @@ static int ibmvnic_open(struct net_device *netdev) NAPI_POLL_WEIGHT); napi_enable(&adapter->napi[i]); } - adapter->rx_pool = - kcalloc(rxadd_subcrqs, sizeof(struct ibmvnic_rx_pool), GFP_KERNEL); - if (!adapter->rx_pool) - goto rx_pool_arr_alloc_failed; send_map_query(adapter); - for (i = 0; i < rxadd_subcrqs; i++) { - init_rx_pool(adapter, &adapter->rx_pool[i], - adapter->req_rx_add_entries_per_subcrq, i, - be64_to_cpu(size_array[i]), 1); - if (alloc_rx_pool(adapter, &adapter->rx_pool[i])) { - dev_err(dev, "Couldn't alloc rx pool\n"); - goto rx_pool_alloc_failed; - } - } + + rc = init_rx_pools(netdev); + if (rc) + goto rx_pool_failed; rc = init_tx_pools(netdev); if (rc) @@ -621,15 +638,7 @@ bounce_init_failed: kfree(adapter->tx_pool[i].free_map); tx_pool_failed: i = rxadd_subcrqs; -rx_pool_alloc_failed: - for (j = 0; j < i; j++) { - free_rx_pool(adapter, &adapter->rx_pool[j]); - free_long_term_buff(adapter, - &adapter->rx_pool[j].long_term_buff); - } - kfree(adapter->rx_pool); - adapter->rx_pool = NULL; -rx_pool_arr_alloc_failed: +rx_pool_failed: for (i = 0; i < adapter->req_rx_queues; i++) napi_disable(&adapter->napi[i]); alloc_napi_failed: @@ -640,21 +649,10 @@ alloc_napi_failed: static void ibmvnic_release_resources(struct ibmvnic_adapter *adapter) { struct device *dev = &adapter->vdev->dev; - int rx_scrqs; - int i; release_bounce_buffer(adapter); release_tx_pools(adapter); - - rx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); - for (i = 0; i < rx_scrqs; i++) { - struct ibmvnic_rx_pool *rx_pool = &adapter->rx_pool[i]; - - free_rx_pool(adapter, rx_pool); - free_long_term_buff(adapter, &rx_pool->long_term_buff); - } - kfree(adapter->rx_pool); - adapter->rx_pool = NULL; + release_rx_pools(adapter); release_sub_crqs(adapter); release_crq_queue(adapter); -- cgit From b510888f9639588d30d48eaaa32502cdb1c9e9e0 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Thu, 30 Mar 2017 02:49:18 -0400 Subject: ibmvnic: Merge the two release_sub_crq_queue routines Keeping two routines for releasing sub crqs, one for when irqs are not initialized and one for when they are, is a bit of overkill. Merge the two routines to a common release routine that will check for an irq and release it if needed. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 54 ++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 6774b3cbc5f9..f2d2f1f1ce1c 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -88,7 +88,6 @@ MODULE_VERSION(IBMVNIC_DRIVER_VERSION); static int ibmvnic_version = IBMVNIC_INITIAL_VERSION; static int ibmvnic_remove(struct vio_dev *); static void release_sub_crqs(struct ibmvnic_adapter *); -static void release_sub_crqs_no_irqs(struct ibmvnic_adapter *); static int ibmvnic_reset_crq(struct ibmvnic_adapter *); static int ibmvnic_send_crq_init(struct ibmvnic_adapter *); static int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *); @@ -526,7 +525,7 @@ static int ibmvnic_login(struct net_device *netdev) do { if (adapter->renegotiate) { adapter->renegotiate = false; - release_sub_crqs_no_irqs(adapter); + release_sub_crqs(adapter); reinit_completion(&adapter->init_done); send_cap_queries(adapter); @@ -1371,49 +1370,40 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter) int i; if (adapter->tx_scrq) { - for (i = 0; i < adapter->req_tx_queues; i++) - if (adapter->tx_scrq[i]) { + for (i = 0; i < adapter->req_tx_queues; i++) { + if (!adapter->tx_scrq[i]) + continue; + + if (adapter->tx_scrq[i]->irq) { free_irq(adapter->tx_scrq[i]->irq, adapter->tx_scrq[i]); irq_dispose_mapping(adapter->tx_scrq[i]->irq); - release_sub_crq_queue(adapter, - adapter->tx_scrq[i]); + adapter->tx_scrq[i]->irq = 0; } + + release_sub_crq_queue(adapter, adapter->tx_scrq[i]); + } + kfree(adapter->tx_scrq); adapter->tx_scrq = NULL; } if (adapter->rx_scrq) { - for (i = 0; i < adapter->req_rx_queues; i++) - if (adapter->rx_scrq[i]) { + for (i = 0; i < adapter->req_rx_queues; i++) { + if (!adapter->rx_scrq[i]) + continue; + + if (adapter->rx_scrq[i]->irq) { free_irq(adapter->rx_scrq[i]->irq, adapter->rx_scrq[i]); irq_dispose_mapping(adapter->rx_scrq[i]->irq); - release_sub_crq_queue(adapter, - adapter->rx_scrq[i]); + adapter->rx_scrq[i]->irq = 0; } - kfree(adapter->rx_scrq); - adapter->rx_scrq = NULL; - } -} -static void release_sub_crqs_no_irqs(struct ibmvnic_adapter *adapter) -{ - int i; - - if (adapter->tx_scrq) { - for (i = 0; i < adapter->req_tx_queues; i++) - if (adapter->tx_scrq[i]) - release_sub_crq_queue(adapter, - adapter->tx_scrq[i]); - adapter->tx_scrq = NULL; - } + release_sub_crq_queue(adapter, adapter->rx_scrq[i]); + } - if (adapter->rx_scrq) { - for (i = 0; i < adapter->req_rx_queues; i++) - if (adapter->rx_scrq[i]) - release_sub_crq_queue(adapter, - adapter->rx_scrq[i]); + kfree(adapter->rx_scrq); adapter->rx_scrq = NULL; } } @@ -1609,7 +1599,7 @@ req_tx_irq_failed: free_irq(adapter->tx_scrq[j]->irq, adapter->tx_scrq[j]); irq_dispose_mapping(adapter->rx_scrq[j]->irq); } - release_sub_crqs_no_irqs(adapter); + release_sub_crqs(adapter); return rc; } @@ -2499,7 +2489,7 @@ static void handle_request_cap_rsp(union ibmvnic_crq *crq, *req_value, (long int)be64_to_cpu(crq->request_capability_rsp. number), name); - release_sub_crqs_no_irqs(adapter); + release_sub_crqs(adapter); *req_value = be64_to_cpu(crq->request_capability_rsp.number); init_sub_crqs(adapter, 1); return; -- cgit From 7bbc27a4961a7d5f8e4294929ce64d6c6e81e90c Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Thu, 30 Mar 2017 02:49:23 -0400 Subject: ibmvnic: Create init/release routines for stats token Create an initialization and a release routine for the stats token used by the ibmvnic driver. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 46 ++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index f2d2f1f1ce1c..a2f972d72e34 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -302,6 +302,36 @@ static void replenish_pools(struct ibmvnic_adapter *adapter) } } +static void release_stats_token(struct ibmvnic_adapter *adapter) +{ + struct device *dev = &adapter->vdev->dev; + + if (!adapter->stats_token) + return; + + dma_unmap_single(dev, adapter->stats_token, + sizeof(struct ibmvnic_statistics), + DMA_FROM_DEVICE); + adapter->stats_token = 0; +} + +static int init_stats_token(struct ibmvnic_adapter *adapter) +{ + struct device *dev = &adapter->vdev->dev; + dma_addr_t stok; + + stok = dma_map_single(dev, &adapter->stats, + sizeof(struct ibmvnic_statistics), + DMA_FROM_DEVICE); + if (dma_mapping_error(dev, stok)) { + dev_err(dev, "Couldn't map stats buffer\n"); + return -1; + } + + adapter->stats_token = stok; + return 0; +} + static void release_rx_pools(struct ibmvnic_adapter *adapter) { struct ibmvnic_rx_pool *rx_pool; @@ -647,8 +677,6 @@ alloc_napi_failed: static void ibmvnic_release_resources(struct ibmvnic_adapter *adapter) { - struct device *dev = &adapter->vdev->dev; - release_bounce_buffer(adapter); release_tx_pools(adapter); release_rx_pools(adapter); @@ -656,10 +684,7 @@ static void ibmvnic_release_resources(struct ibmvnic_adapter *adapter) release_sub_crqs(adapter); release_crq_queue(adapter); - if (adapter->stats_token) - dma_unmap_single(dev, adapter->stats_token, - sizeof(struct ibmvnic_statistics), - DMA_FROM_DEVICE); + release_stats_token(adapter); } static int ibmvnic_close(struct net_device *netdev) @@ -3269,13 +3294,10 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter) return rc; } - adapter->stats_token = dma_map_single(dev, &adapter->stats, - sizeof(struct ibmvnic_statistics), - DMA_FROM_DEVICE); - if (dma_mapping_error(dev, adapter->stats_token)) { + rc = init_stats_token(adapter); + if (rc) { release_crq_queue(adapter); - dev_err(dev, "Couldn't map stats buffer\n"); - return -ENOMEM; + return rc; } init_completion(&adapter->init_done); -- cgit From 1b8955ee5f6c1575c09b44c8253883394c78bef7 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Thu, 30 Mar 2017 02:49:29 -0400 Subject: ibmvnic: Cleanup failure path in ibmvnic_open Now that ibmvnic_release_resources will clean up all of our resources properly, even if they were not allocated, we can just call this for failues in ibmvnic_open. This patch also moves the ibmvnic_release_resources() routine up in the file to avoid creating a forward declaration ad re-names it to drop the ibmvnic prefix. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 51 ++++++++++++++------------------------ 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index a2f972d72e34..7ba43cfadf3a 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -578,13 +578,23 @@ static int ibmvnic_login(struct net_device *netdev) return 0; } +static void release_resources(struct ibmvnic_adapter *adapter) +{ + release_bounce_buffer(adapter); + release_tx_pools(adapter); + release_rx_pools(adapter); + + release_sub_crqs(adapter); + release_crq_queue(adapter); + + release_stats_token(adapter); +} + static int ibmvnic_open(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); struct device *dev = &adapter->vdev->dev; union ibmvnic_crq crq; - int rxadd_subcrqs; - int tx_subcrqs; int rc = 0; int i; @@ -610,16 +620,11 @@ static int ibmvnic_open(struct net_device *netdev) return -1; } - rxadd_subcrqs = - be32_to_cpu(adapter->login_rsp_buf->num_rxadd_subcrqs); - tx_subcrqs = - be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); - adapter->map_id = 1; adapter->napi = kcalloc(adapter->req_rx_queues, sizeof(struct napi_struct), GFP_KERNEL); if (!adapter->napi) - goto alloc_napi_failed; + goto ibmvnic_open_fail; for (i = 0; i < adapter->req_rx_queues; i++) { netif_napi_add(netdev, &adapter->napi[i], ibmvnic_poll, NAPI_POLL_WEIGHT); @@ -630,15 +635,15 @@ static int ibmvnic_open(struct net_device *netdev) rc = init_rx_pools(netdev); if (rc) - goto rx_pool_failed; + goto ibmvnic_open_fail; rc = init_tx_pools(netdev); if (rc) - goto tx_pool_failed; + goto ibmvnic_open_fail; rc = init_bounce_buffer(netdev); if (rc) - goto bounce_init_failed; + goto ibmvnic_open_fail; replenish_pools(adapter); @@ -662,31 +667,13 @@ static int ibmvnic_open(struct net_device *netdev) return 0; -bounce_init_failed: - i = tx_subcrqs - 1; - kfree(adapter->tx_pool[i].free_map); -tx_pool_failed: - i = rxadd_subcrqs; -rx_pool_failed: +ibmvnic_open_fail: for (i = 0; i < adapter->req_rx_queues; i++) napi_disable(&adapter->napi[i]); -alloc_napi_failed: - release_sub_crqs(adapter); + release_resources(adapter); return -ENOMEM; } -static void ibmvnic_release_resources(struct ibmvnic_adapter *adapter) -{ - release_bounce_buffer(adapter); - release_tx_pools(adapter); - release_rx_pools(adapter); - - release_sub_crqs(adapter); - release_crq_queue(adapter); - - release_stats_token(adapter); -} - static int ibmvnic_close(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); @@ -707,7 +694,7 @@ static int ibmvnic_close(struct net_device *netdev) crq.logical_link_state.link_state = IBMVNIC_LOGICAL_LNK_DN; ibmvnic_send_crq(adapter, &crq); - ibmvnic_release_resources(adapter); + release_resources(adapter); adapter->is_closed = true; adapter->closing = false; -- cgit From 6c7c98bad4883a4a8710c96b2b44de482865eb6e Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 30 Mar 2017 14:03:06 +0200 Subject: sock: avoid dirtying sk_stamp, if possible sock_recv_ts_and_drops() unconditionally set sk->sk_stamp for every packet, even if the SOCK_TIMESTAMP flag is not set in the related socket. If selinux is enabled, this cause a cache miss for every packet since sk->sk_stamp and sk->sk_security share the same cacheline. With this change sk_stamp is set only if the SOCK_TIMESTAMP flag is set, and is cleared for the first packet, so that the user perceived behavior is unchanged. This gives up to 5% speed-up under udp-flood with small packets. Signed-off-by: Paolo Abeni Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sock.h | 5 ++++- net/core/sock.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/net/sock.h b/include/net/sock.h index cb241a0e8434..8e53158a7d95 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2239,6 +2239,7 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) void __sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, struct sk_buff *skb); +#define SK_DEFAULT_STAMP (-1L * NSEC_PER_SEC) static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) { @@ -2249,8 +2250,10 @@ static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, if (sk->sk_flags & FLAGS_TS_OR_DROPS || sk->sk_tsflags & TSFLAGS_ANY) __sock_recv_ts_and_drops(msg, sk, skb); - else + else if (unlikely(sk->sk_flags & SOCK_TIMESTAMP)) sk->sk_stamp = skb->tstamp; + else if (unlikely(sk->sk_stamp == SK_DEFAULT_STAMP)) + sk->sk_stamp = 0; } void __sock_tx_timestamp(__u16 tsflags, __u8 *tx_flags); diff --git a/net/core/sock.c b/net/core/sock.c index 1a58a9dc6888..392f9b6f96e2 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2613,7 +2613,7 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT; sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT; - sk->sk_stamp = ktime_set(-1L, 0); + sk->sk_stamp = SK_DEFAULT_STAMP; #ifdef CONFIG_NET_RX_BUSY_POLL sk->sk_napi_id = 0; -- cgit From 5349a0f7bfbdd7d81b8418c707dcd1439c714647 Mon Sep 17 00:00:00 2001 From: Vidyullatha Kanchanapally Date: Fri, 31 Mar 2017 00:22:33 +0300 Subject: cfg80211: Use a structure to pass connect response params Currently the connect event from driver takes all the connection response parameters as arguments. With support for new features these response parameters can grow. Use a structure to pass these parameters rather than passing them as function arguments. Signed-off-by: Vidyullatha Kanchanapally Signed-off-by: Jouni Malinen [add to documentation] Signed-off-by: Johannes Berg --- Documentation/driver-api/80211/cfg80211.rst | 6 ++ include/net/cfg80211.h | 91 +++++++++++++++-- net/wireless/core.h | 20 +--- net/wireless/mlme.c | 20 ++-- net/wireless/nl80211.c | 30 +++--- net/wireless/nl80211.h | 7 +- net/wireless/sme.c | 149 +++++++++++++++------------- net/wireless/util.c | 12 +-- 8 files changed, 205 insertions(+), 130 deletions(-) diff --git a/Documentation/driver-api/80211/cfg80211.rst b/Documentation/driver-api/80211/cfg80211.rst index eca534ab6172..b101bc0c195b 100644 --- a/Documentation/driver-api/80211/cfg80211.rst +++ b/Documentation/driver-api/80211/cfg80211.rst @@ -179,6 +179,12 @@ Actions and configuration .. kernel-doc:: include/net/cfg80211.h :functions: cfg80211_ibss_joined +.. kernel-doc:: include/net/cfg80211.h + :functions: cfg80211_connect_resp_params + +.. kernel-doc:: include/net/cfg80211.h + :functions: cfg80211_connect_done + .. kernel-doc:: include/net/cfg80211.h :functions: cfg80211_connect_result diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ffc08687b31d..da12d5b86e1b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5135,6 +5135,60 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) #define CFG80211_TESTMODE_DUMP(cmd) #endif +/** + * struct cfg80211_connect_resp_params - Connection response params + * @status: Status code, %WLAN_STATUS_SUCCESS for successful connection, use + * %WLAN_STATUS_UNSPECIFIED_FAILURE if your device cannot give you + * the real status code for failures. If this call is used to report a + * failure due to a timeout (e.g., not receiving an Authentication frame + * from the AP) instead of an explicit rejection by the AP, -1 is used to + * indicate that this is a failure, but without a status code. + * @timeout_reason is used to report the reason for the timeout in that + * case. + * @bssid: The BSSID of the AP (may be %NULL) + * @bss: Entry of bss to which STA got connected to, can be obtained through + * cfg80211_get_bss() (may be %NULL). Only one parameter among @bssid and + * @bss needs to be specified. + * @req_ie: Association request IEs (may be %NULL) + * @req_ie_len: Association request IEs length + * @resp_ie: Association response IEs (may be %NULL) + * @resp_ie_len: Association response IEs length + * @timeout_reason: Reason for connection timeout. This is used when the + * connection fails due to a timeout instead of an explicit rejection from + * the AP. %NL80211_TIMEOUT_UNSPECIFIED is used when the timeout reason is + * not known. This value is used only if @status < 0 to indicate that the + * failure is due to a timeout and not due to explicit rejection by the AP. + * This value is ignored in other cases (@status >= 0). + */ +struct cfg80211_connect_resp_params { + int status; + const u8 *bssid; + struct cfg80211_bss *bss; + const u8 *req_ie; + size_t req_ie_len; + const u8 *resp_ie; + size_t resp_ie_len; + enum nl80211_timeout_reason timeout_reason; +}; + +/** + * cfg80211_connect_done - notify cfg80211 of connection result + * + * @dev: network device + * @params: connection response parameters + * @gfp: allocation flags + * + * It should be called by the underlying driver once execution of the connection + * request from connect() has been completed. This is similar to + * cfg80211_connect_bss(), but takes a structure pointer for connection response + * parameters. Only one of the functions among cfg80211_connect_bss(), + * cfg80211_connect_result(), cfg80211_connect_timeout(), + * and cfg80211_connect_done() should be called. + */ +void cfg80211_connect_done(struct net_device *dev, + struct cfg80211_connect_resp_params *params, + gfp_t gfp); + /** * cfg80211_connect_bss - notify cfg80211 of connection result * @@ -5165,13 +5219,31 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) * It should be called by the underlying driver once execution of the connection * request from connect() has been completed. This is similar to * cfg80211_connect_result(), but with the option of identifying the exact bss - * entry for the connection. Only one of these functions should be called. + * entry for the connection. Only one of the functions among + * cfg80211_connect_bss(), cfg80211_connect_result(), + * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called. */ -void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, - struct cfg80211_bss *bss, const u8 *req_ie, - size_t req_ie_len, const u8 *resp_ie, - size_t resp_ie_len, int status, gfp_t gfp, - enum nl80211_timeout_reason timeout_reason); +static inline void +cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, + struct cfg80211_bss *bss, const u8 *req_ie, + size_t req_ie_len, const u8 *resp_ie, + size_t resp_ie_len, int status, gfp_t gfp, + enum nl80211_timeout_reason timeout_reason) +{ + struct cfg80211_connect_resp_params params; + + memset(¶ms, 0, sizeof(params)); + params.status = status; + params.bssid = bssid; + params.bss = bss; + params.req_ie = req_ie; + params.req_ie_len = req_ie_len; + params.resp_ie = resp_ie; + params.resp_ie_len = resp_ie_len; + params.timeout_reason = timeout_reason; + + cfg80211_connect_done(dev, ¶ms, gfp); +} /** * cfg80211_connect_result - notify cfg80211 of connection result @@ -5190,7 +5262,8 @@ void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, * It should be called by the underlying driver once execution of the connection * request from connect() has been completed. This is similar to * cfg80211_connect_bss() which allows the exact bss entry to be specified. Only - * one of these functions should be called. + * one of the functions among cfg80211_connect_bss(), cfg80211_connect_result(), + * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called. */ static inline void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, @@ -5217,7 +5290,9 @@ cfg80211_connect_result(struct net_device *dev, const u8 *bssid, * in a sequence where no explicit authentication/association rejection was * received from the AP. This could happen, e.g., due to not being able to send * out the Authentication or Association Request frame or timing out while - * waiting for the response. + * waiting for the response. Only one of the functions among + * cfg80211_connect_bss(), cfg80211_connect_result(), + * cfg80211_connect_timeout(), and cfg80211_connect_done() should be called. */ static inline void cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid, diff --git a/net/wireless/core.h b/net/wireless/core.h index a2fe8fc93283..d614efb41726 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -226,16 +226,7 @@ struct cfg80211_event { enum cfg80211_event_type type; union { - struct { - u8 bssid[ETH_ALEN]; - const u8 *req_ie; - const u8 *resp_ie; - size_t req_ie_len; - size_t resp_ie_len; - struct cfg80211_bss *bss; - int status; /* -1 = failed; 0..65535 = status code */ - enum nl80211_timeout_reason timeout_reason; - } cr; + struct cfg80211_connect_resp_params cr; struct { const u8 *req_ie; const u8 *resp_ie; @@ -398,12 +389,9 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, struct cfg80211_connect_params *connect, struct cfg80211_cached_keys *connkeys, const u8 *prev_bssid); -void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - int status, bool wextev, - struct cfg80211_bss *bss, - enum nl80211_timeout_reason timeout_reason); +void __cfg80211_connect_result(struct net_device *dev, + struct cfg80211_connect_resp_params *params, + bool wextev); void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap); int cfg80211_disconnect(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 01ce4a69e44d..d8df7a5180a0 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -26,9 +26,16 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - u8 *ie = mgmt->u.assoc_resp.variable; - int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); - u16 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); + struct cfg80211_connect_resp_params cr; + + memset(&cr, 0, sizeof(cr)); + cr.status = (int)le16_to_cpu(mgmt->u.assoc_resp.status_code); + cr.bssid = mgmt->bssid; + cr.bss = bss; + cr.resp_ie = mgmt->u.assoc_resp.variable; + cr.resp_ie_len = + len - offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); + cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED; trace_cfg80211_send_rx_assoc(dev, bss); @@ -38,7 +45,7 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, * and got a reject -- we only try again with an assoc * frame instead of reassoc. */ - if (cfg80211_sme_rx_assoc_resp(wdev, status_code)) { + if (cfg80211_sme_rx_assoc_resp(wdev, cr.status)) { cfg80211_unhold_bss(bss_from_pub(bss)); cfg80211_put_bss(wiphy, bss); return; @@ -46,10 +53,7 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss, nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues); /* update current_bss etc., consumes the bss reference */ - __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, - status_code, - status_code == WLAN_STATUS_SUCCESS, bss, - NL80211_TIMEOUT_UNSPECIFIED); + __cfg80211_connect_result(dev, &cr, cr.status == WLAN_STATUS_SUCCESS); } EXPORT_SYMBOL(cfg80211_rx_assoc_resp); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index bd5959fd29c5..3d635c865281 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -13464,17 +13464,14 @@ void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, } void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - int status, - enum nl80211_timeout_reason timeout_reason, + struct net_device *netdev, + struct cfg80211_connect_resp_params *cr, gfp_t gfp) { struct sk_buff *msg; void *hdr; - msg = nlmsg_new(100 + req_ie_len + resp_ie_len, gfp); + msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len, gfp); if (!msg) return; @@ -13486,17 +13483,20 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || - (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) || + (cr->bssid && + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, cr->bssid)) || nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, - status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE : - status) || - (status < 0 && + cr->status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE : + cr->status) || + (cr->status < 0 && (nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) || - nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON, timeout_reason))) || - (req_ie && - nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) || - (resp_ie && - nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie))) + nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON, + cr->timeout_reason))) || + (cr->req_ie && + nla_put(msg, NL80211_ATTR_REQ_IE, cr->req_ie_len, cr->req_ie)) || + (cr->resp_ie && + nla_put(msg, NL80211_ATTR_RESP_IE, cr->resp_ie_len, + cr->resp_ie))) goto nla_put_failure; genlmsg_end(msg, hdr); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index e488dca87423..3cb17cd9577f 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -53,11 +53,8 @@ void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, gfp_t gfp); void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - int status, - enum nl80211_timeout_reason timeout_reason, + struct net_device *netdev, + struct cfg80211_connect_resp_params *params, gfp_t gfp); void nl80211_send_roamed(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index b347e63d7aaa..ebd7adc27246 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -253,10 +253,13 @@ void cfg80211_conn_work(struct work_struct *work) } treason = NL80211_TIMEOUT_UNSPECIFIED; if (cfg80211_conn_do_work(wdev, &treason)) { - __cfg80211_connect_result( - wdev->netdev, bssid, - NULL, 0, NULL, 0, -1, false, NULL, - treason); + struct cfg80211_connect_resp_params cr; + + memset(&cr, 0, sizeof(cr)); + cr.status = -1; + cr.bssid = bssid; + cr.timeout_reason = treason; + __cfg80211_connect_result(wdev->netdev, &cr, false); } wdev_unlock(wdev); } @@ -359,10 +362,13 @@ void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len) wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; schedule_work(&rdev->conn_work); } else if (status_code != WLAN_STATUS_SUCCESS) { - __cfg80211_connect_result(wdev->netdev, mgmt->bssid, - NULL, 0, NULL, 0, - status_code, false, NULL, - NL80211_TIMEOUT_UNSPECIFIED); + struct cfg80211_connect_resp_params cr; + + memset(&cr, 0, sizeof(cr)); + cr.status = status_code; + cr.bssid = mgmt->bssid; + cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED; + __cfg80211_connect_result(wdev->netdev, &cr, false); } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; schedule_work(&rdev->conn_work); @@ -669,12 +675,9 @@ static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); */ /* This method must consume bss one way or another */ -void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - int status, bool wextev, - struct cfg80211_bss *bss, - enum nl80211_timeout_reason timeout_reason) +void __cfg80211_connect_result(struct net_device *dev, + struct cfg80211_connect_resp_params *cr, + bool wextev) { struct wireless_dev *wdev = dev->ieee80211_ptr; const u8 *country_ie; @@ -686,48 +689,48 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) { - cfg80211_put_bss(wdev->wiphy, bss); + cfg80211_put_bss(wdev->wiphy, cr->bss); return; } - nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, - bssid, req_ie, req_ie_len, - resp_ie, resp_ie_len, - status, timeout_reason, GFP_KERNEL); + nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, cr, + GFP_KERNEL); #ifdef CONFIG_CFG80211_WEXT if (wextev) { - if (req_ie && status == WLAN_STATUS_SUCCESS) { + if (cr->req_ie && cr->status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = req_ie_len; - wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie); + wrqu.data.length = cr->req_ie_len; + wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, + cr->req_ie); } - if (resp_ie && status == WLAN_STATUS_SUCCESS) { + if (cr->resp_ie && cr->status == WLAN_STATUS_SUCCESS) { memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = resp_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); + wrqu.data.length = cr->resp_ie_len; + wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, + cr->resp_ie); } memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid && status == WLAN_STATUS_SUCCESS) { - memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); - memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); + if (cr->bssid && cr->status == WLAN_STATUS_SUCCESS) { + memcpy(wrqu.ap_addr.sa_data, cr->bssid, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, cr->bssid, ETH_ALEN); wdev->wext.prev_bssid_valid = true; } wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); } #endif - if (!bss && (status == WLAN_STATUS_SUCCESS)) { + if (!cr->bss && (cr->status == WLAN_STATUS_SUCCESS)) { WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect); - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, - wdev->ssid, wdev->ssid_len, - wdev->conn_bss_type, - IEEE80211_PRIVACY_ANY); - if (bss) - cfg80211_hold_bss(bss_from_pub(bss)); + cr->bss = cfg80211_get_bss(wdev->wiphy, NULL, cr->bssid, + wdev->ssid, wdev->ssid_len, + wdev->conn_bss_type, + IEEE80211_PRIVACY_ANY); + if (cr->bss) + cfg80211_hold_bss(bss_from_pub(cr->bss)); } if (wdev->current_bss) { @@ -736,29 +739,29 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, wdev->current_bss = NULL; } - if (status != WLAN_STATUS_SUCCESS) { + if (cr->status != WLAN_STATUS_SUCCESS) { kzfree(wdev->connect_keys); wdev->connect_keys = NULL; wdev->ssid_len = 0; wdev->conn_owner_nlportid = 0; - if (bss) { - cfg80211_unhold_bss(bss_from_pub(bss)); - cfg80211_put_bss(wdev->wiphy, bss); + if (cr->bss) { + cfg80211_unhold_bss(bss_from_pub(cr->bss)); + cfg80211_put_bss(wdev->wiphy, cr->bss); } cfg80211_sme_free(wdev); return; } - if (WARN_ON(!bss)) + if (WARN_ON(!cr->bss)) return; - wdev->current_bss = bss_from_pub(bss); + wdev->current_bss = bss_from_pub(cr->bss); if (!(wdev->wiphy->flags & WIPHY_FLAG_HAS_STATIC_WEP)) cfg80211_upload_connect_keys(wdev); rcu_read_lock(); - country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + country_ie = ieee80211_bss_get_ie(cr->bss, WLAN_EID_COUNTRY); if (!country_ie) { rcu_read_unlock(); return; @@ -775,64 +778,72 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, * - country_ie + 2, the start of the country ie data, and * - and country_ie[1] which is the IE length */ - regulatory_hint_country_ie(wdev->wiphy, bss->channel->band, + regulatory_hint_country_ie(wdev->wiphy, cr->bss->channel->band, country_ie + 2, country_ie[1]); kfree(country_ie); } /* Consumes bss object one way or another */ -void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid, - struct cfg80211_bss *bss, const u8 *req_ie, - size_t req_ie_len, const u8 *resp_ie, - size_t resp_ie_len, int status, gfp_t gfp, - enum nl80211_timeout_reason timeout_reason) +void cfg80211_connect_done(struct net_device *dev, + struct cfg80211_connect_resp_params *params, + gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_event *ev; unsigned long flags; + u8 *next; - if (bss) { + if (params->bss) { /* Make sure the bss entry provided by the driver is valid. */ - struct cfg80211_internal_bss *ibss = bss_from_pub(bss); + struct cfg80211_internal_bss *ibss = bss_from_pub(params->bss); if (WARN_ON(list_empty(&ibss->list))) { - cfg80211_put_bss(wdev->wiphy, bss); + cfg80211_put_bss(wdev->wiphy, params->bss); return; } } - ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); + ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) + + params->req_ie_len + params->resp_ie_len, gfp); if (!ev) { - cfg80211_put_bss(wdev->wiphy, bss); + cfg80211_put_bss(wdev->wiphy, params->bss); return; } ev->type = EVENT_CONNECT_RESULT; - if (bssid) - memcpy(ev->cr.bssid, bssid, ETH_ALEN); - if (req_ie_len) { - ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); - ev->cr.req_ie_len = req_ie_len; - memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); + next = ((u8 *)ev) + sizeof(*ev); + if (params->bssid) { + ev->cr.bssid = next; + memcpy((void *)ev->cr.bssid, params->bssid, ETH_ALEN); + next += ETH_ALEN; } - if (resp_ie_len) { - ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; - ev->cr.resp_ie_len = resp_ie_len; - memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); + if (params->req_ie_len) { + ev->cr.req_ie = next; + ev->cr.req_ie_len = params->req_ie_len; + memcpy((void *)ev->cr.req_ie, params->req_ie, + params->req_ie_len); + next += params->req_ie_len; } - if (bss) - cfg80211_hold_bss(bss_from_pub(bss)); - ev->cr.bss = bss; - ev->cr.status = status; - ev->cr.timeout_reason = timeout_reason; + if (params->resp_ie_len) { + ev->cr.resp_ie = next; + ev->cr.resp_ie_len = params->resp_ie_len; + memcpy((void *)ev->cr.resp_ie, params->resp_ie, + params->resp_ie_len); + next += params->resp_ie_len; + } + if (params->bss) + cfg80211_hold_bss(bss_from_pub(params->bss)); + ev->cr.bss = params->bss; + ev->cr.status = params->status; + ev->cr.timeout_reason = params->timeout_reason; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); spin_unlock_irqrestore(&wdev->event_lock, flags); queue_work(cfg80211_wq, &rdev->event_work); } -EXPORT_SYMBOL(cfg80211_connect_bss); +EXPORT_SYMBOL(cfg80211_connect_done); /* Consumes bss object one way or another */ void __cfg80211_roamed(struct wireless_dev *wdev, diff --git a/net/wireless/util.c b/net/wireless/util.c index 737c9c2c9cc9..8d6a0a7b1ca1 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -929,7 +929,6 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) { struct cfg80211_event *ev; unsigned long flags; - const u8 *bssid = NULL; spin_lock_irqsave(&wdev->event_lock, flags); while (!list_empty(&wdev->event_list)) { @@ -941,15 +940,10 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) wdev_lock(wdev); switch (ev->type) { case EVENT_CONNECT_RESULT: - if (!is_zero_ether_addr(ev->cr.bssid)) - bssid = ev->cr.bssid; __cfg80211_connect_result( - wdev->netdev, bssid, - ev->cr.req_ie, ev->cr.req_ie_len, - ev->cr.resp_ie, ev->cr.resp_ie_len, - ev->cr.status, - ev->cr.status == WLAN_STATUS_SUCCESS, - ev->cr.bss, ev->cr.timeout_reason); + wdev->netdev, + &ev->cr, + ev->cr.status == WLAN_STATUS_SUCCESS); break; case EVENT_ROAMED: __cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie, -- cgit From a3caf7440dedd2399f90f27ff11ac390bf03e6c4 Mon Sep 17 00:00:00 2001 From: Vidyullatha Kanchanapally Date: Fri, 31 Mar 2017 00:22:34 +0300 Subject: cfg80211: Add support for FILS shared key authentication offload Enhance nl80211 and cfg80211 connect request and response APIs to support FILS shared key authentication offload. The new nl80211 attributes can be used to provide additional information to the driver to establish a FILS connection. Also enhance the set/del PMKSA to allow support for adding and deleting PMKSA based on FILS cache identifier. Add a new feature flag that drivers can use to advertize support for FILS shared key authentication and association in station mode when using their own SME. Signed-off-by: Vidyullatha Kanchanapally Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 13 +++++++ include/net/cfg80211.h | 57 +++++++++++++++++++++++++++- include/uapi/linux/nl80211.h | 86 ++++++++++++++++++++++++++++++++++++++++-- net/wireless/nl80211.c | 90 +++++++++++++++++++++++++++++++++++++++++--- net/wireless/sme.c | 25 +++++++++++- 5 files changed, 259 insertions(+), 12 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 22bf0676d928..294fa6273a62 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1723,6 +1723,9 @@ enum ieee80211_statuscode { WLAN_STATUS_REJECT_DSE_BAND = 96, WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL = 99, WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT = 103, + /* 802.11ai */ + WLAN_STATUS_FILS_AUTHENTICATION_FAILURE = 108, + WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER = 109, }; @@ -2104,6 +2107,12 @@ enum ieee80211_key_len { #define FILS_NONCE_LEN 16 #define FILS_MAX_KEK_LEN 64 +#define FILS_ERP_MAX_USERNAME_LEN 16 +#define FILS_ERP_MAX_REALM_LEN 253 +#define FILS_ERP_MAX_RRK_LEN 64 + +#define PMK_MAX_LEN 48 + /* Public action codes */ enum ieee80211_pub_actioncode { WLAN_PUB_ACTION_EXT_CHANSW_ANN = 4, @@ -2355,6 +2364,10 @@ enum ieee80211_sa_query_action { #define WLAN_AKM_SUITE_TDLS SUITE(0x000FAC, 7) #define WLAN_AKM_SUITE_SAE SUITE(0x000FAC, 8) #define WLAN_AKM_SUITE_FT_OVER_SAE SUITE(0x000FAC, 9) +#define WLAN_AKM_SUITE_FILS_SHA256 SUITE(0x000FAC, 14) +#define WLAN_AKM_SUITE_FILS_SHA384 SUITE(0x000FAC, 15) +#define WLAN_AKM_SUITE_FT_FILS_SHA256 SUITE(0x000FAC, 16) +#define WLAN_AKM_SUITE_FT_FILS_SHA384 SUITE(0x000FAC, 17) #define WLAN_MAX_KEY_LEN 32 diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index da12d5b86e1b..042137d7d226 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2073,6 +2073,19 @@ struct cfg80211_bss_selection { * the BSSID of the current association, i.e., to the value that is * included in the Current AP address field of the Reassociation Request * frame. + * @fils_erp_username: EAP re-authentication protocol (ERP) username part of the + * NAI or %NULL if not specified. This is used to construct FILS wrapped + * data IE. + * @fils_erp_username_len: Length of @fils_erp_username in octets. + * @fils_erp_realm: EAP re-authentication protocol (ERP) realm part of NAI or + * %NULL if not specified. This specifies the domain name of ER server and + * is used to construct FILS wrapped data IE. + * @fils_erp_realm_len: Length of @fils_erp_realm in octets. + * @fils_erp_next_seq_num: The next sequence number to use in the FILS ERP + * messages. This is also used to construct FILS wrapped data IE. + * @fils_erp_rrk: ERP re-authentication Root Key (rRK) used to derive additional + * keys in FILS or %NULL if not specified. + * @fils_erp_rrk_len: Length of @fils_erp_rrk in octets. */ struct cfg80211_connect_params { struct ieee80211_channel *channel; @@ -2098,6 +2111,13 @@ struct cfg80211_connect_params { bool pbss; struct cfg80211_bss_selection bss_select; const u8 *prev_bssid; + const u8 *fils_erp_username; + size_t fils_erp_username_len; + const u8 *fils_erp_realm; + size_t fils_erp_realm_len; + u16 fils_erp_next_seq_num; + const u8 *fils_erp_rrk; + size_t fils_erp_rrk_len; }; /** @@ -2136,12 +2156,27 @@ enum wiphy_params_flags { * This structure is passed to the set/del_pmksa() method for PMKSA * caching. * - * @bssid: The AP's BSSID. - * @pmkid: The PMK material itself. + * @bssid: The AP's BSSID (may be %NULL). + * @pmkid: The identifier to refer a PMKSA. + * @pmk: The PMK for the PMKSA identified by @pmkid. This is used for key + * derivation by a FILS STA. Otherwise, %NULL. + * @pmk_len: Length of the @pmk. The length of @pmk can differ depending on + * the hash algorithm used to generate this. + * @ssid: SSID to specify the ESS within which a PMKSA is valid when using FILS + * cache identifier (may be %NULL). + * @ssid_len: Length of the @ssid in octets. + * @cache_id: 2-octet cache identifier advertized by a FILS AP identifying the + * scope of PMKSA. This is valid only if @ssid_len is non-zero (may be + * %NULL). */ struct cfg80211_pmksa { const u8 *bssid; const u8 *pmkid; + const u8 *pmk; + size_t pmk_len; + const u8 *ssid; + size_t ssid_len; + const u8 *cache_id; }; /** @@ -5153,6 +5188,17 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) * @req_ie_len: Association request IEs length * @resp_ie: Association response IEs (may be %NULL) * @resp_ie_len: Association response IEs length + * @fils_kek: KEK derived from a successful FILS connection (may be %NULL) + * @fils_kek_len: Length of @fils_kek in octets + * @update_erp_next_seq_num: Boolean value to specify whether the value in + * @fils_erp_next_seq_num is valid. + * @fils_erp_next_seq_num: The next sequence number to use in ERP message in + * FILS Authentication. This value should be specified irrespective of the + * status for a FILS connection. + * @pmk: A new PMK if derived from a successful FILS connection (may be %NULL). + * @pmk_len: Length of @pmk in octets + * @pmkid: A new PMKID if derived from a successful FILS connection or the PMKID + * used for this FILS connection (may be %NULL). * @timeout_reason: Reason for connection timeout. This is used when the * connection fails due to a timeout instead of an explicit rejection from * the AP. %NL80211_TIMEOUT_UNSPECIFIED is used when the timeout reason is @@ -5168,6 +5214,13 @@ struct cfg80211_connect_resp_params { size_t req_ie_len; const u8 *resp_ie; size_t resp_ie_len; + const u8 *fils_kek; + size_t fils_kek_len; + bool update_erp_next_seq_num; + u16 fils_erp_next_seq_num; + const u8 *pmk; + size_t pmk_len; + const u8 *pmkid; enum nl80211_timeout_reason timeout_reason; }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index cd4dfef58fab..6095a6c4c412 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -172,6 +172,42 @@ * Multiple such rules can be created. */ +/** + * DOC: FILS shared key authentication offload + * + * FILS shared key authentication offload can be advertized by drivers by + * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support + * FILS shared key authentication offload should be able to construct the + * authentication and association frames for FILS shared key authentication and + * eventually do a key derivation as per IEEE 802.11ai. The below additional + * parameters should be given to driver in %NL80211_CMD_CONNECT. + * %NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai + * %NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai + * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message + * %NL80211_ATTR_FILS_ERP_RRK - used to generate the rIK and rMSK + * rIK should be used to generate an authentication tag on the ERP message and + * rMSK should be used to derive a PMKSA. + * rIK, rMSK should be generated and keyname_nai, sequence number should be used + * as specified in IETF RFC 6696. + * + * When FILS shared key authentication is completed, driver needs to provide the + * below additional parameters to userspace. + * %NL80211_ATTR_FILS_KEK - used for key renewal + * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges + * %NL80211_ATTR_PMKID - used to identify the PMKSA used/generated + * %Nl80211_ATTR_PMK - used to update PMKSA cache in userspace + * The PMKSA can be maintained in userspace persistently so that it can be used + * later after reboots or wifi turn off/on also. + * + * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS + * capable AP supporting PMK caching. It specifies the scope within which the + * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and + * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based + * on FILS cache identifier. Additionally %NL80211_ATTR_PMK is used with + * %NL80211_SET_PMKSA to specify the PMK corresponding to a PMKSA for driver to + * use in a FILS shared key connection with PMKSA caching. + */ + /** * enum nl80211_commands - supported nl80211 commands * @@ -370,10 +406,18 @@ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) * - * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC - * (for the BSSID) and %NL80211_ATTR_PMKID. + * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry using %NL80211_ATTR_MAC + * (for the BSSID), %NL80211_ATTR_PMKID, and optionally %NL80211_ATTR_PMK + * (PMK is used for PTKSA derivation in case of FILS shared key offload) or + * using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID, + * %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS + * authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier + * advertized by a FILS capable AP identifying the scope of PMKSA in an + * ESS. * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC - * (for the BSSID) and %NL80211_ATTR_PMKID. + * (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID, + * %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS + * authentication. * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. * * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain @@ -2012,6 +2056,31 @@ enum nl80211_commands { * u32 attribute with an &enum nl80211_timeout_reason value. This is used, * e.g., with %NL80211_CMD_CONNECT event. * + * @NL80211_ATTR_FILS_ERP_USERNAME: EAP Re-authentication Protocol (ERP) + * username part of NAI used to refer keys rRK and rIK. This is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_REALM: EAP Re-authentication Protocol (ERP) realm part + * of NAI specifying the domain name of the ER server. This is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM: Unsigned 16-bit ERP next sequence number + * to use in ERP messages. This is used in generating the FILS wrapped data + * for FILS authentication and is used with %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_RRK: ERP re-authentication Root Key (rRK) for the + * NAI specified by %NL80211_ATTR_FILS_ERP_USERNAME and + * %NL80211_ATTR_FILS_ERP_REALM. This is used for generating rIK and rMSK + * from successful FILS authentication and is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP + * identifying the scope of PMKSAs. This is used with + * @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA. + * + * @NL80211_ATTR_PMK: PMK for the PMKSA identified by %NL80211_ATTR_PMKID. + * This is used with @NL80211_CMD_SET_PMKSA. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2423,6 +2492,14 @@ enum nl80211_attrs { NL80211_ATTR_TIMEOUT_REASON, + NL80211_ATTR_FILS_ERP_USERNAME, + NL80211_ATTR_FILS_ERP_REALM, + NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, + NL80211_ATTR_FILS_ERP_RRK, + NL80211_ATTR_FILS_CACHE_ID, + + NL80211_ATTR_PMK, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -4759,6 +4836,8 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the * %NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more * RSSI threshold values to monitor rather than exactly one threshold. + * @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key + * authentication with %NL80211_CMD_CONNECT. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -4778,6 +4857,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED, NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI, NL80211_EXT_FEATURE_CQM_RSSI_LIST, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 3d635c865281..9910aae08f1a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -410,6 +410,15 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { .len = sizeof(struct nl80211_bss_select_rssi_adjust) }, [NL80211_ATTR_TIMEOUT_REASON] = { .type = NLA_U32 }, + [NL80211_ATTR_FILS_ERP_USERNAME] = { .type = NLA_BINARY, + .len = FILS_ERP_MAX_USERNAME_LEN }, + [NL80211_ATTR_FILS_ERP_REALM] = { .type = NLA_BINARY, + .len = FILS_ERP_MAX_REALM_LEN }, + [NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] = { .type = NLA_U16 }, + [NL80211_ATTR_FILS_ERP_RRK] = { .type = NLA_BINARY, + .len = FILS_ERP_MAX_RRK_LEN }, + [NL80211_ATTR_FILS_CACHE_ID] = { .len = 2 }, + [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN }, }; /* policy for the key attributes */ @@ -3832,6 +3841,19 @@ static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev, return false; return true; case NL80211_CMD_CONNECT: + /* SAE not supported yet */ + if (auth_type == NL80211_AUTHTYPE_SAE) + return false; + /* FILS with SK PFS or PK not supported yet */ + if (auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || + auth_type == NL80211_AUTHTYPE_FILS_PK) + return false; + if (!wiphy_ext_feature_isset( + &rdev->wiphy, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) && + auth_type == NL80211_AUTHTYPE_FILS_SK) + return false; + return true; case NL80211_CMD_START_AP: /* SAE not supported yet */ if (auth_type == NL80211_AUTHTYPE_SAE) @@ -8906,6 +8928,35 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) } } + if (wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) && + info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] && + info->attrs[NL80211_ATTR_FILS_ERP_REALM] && + info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] && + info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { + connect.fils_erp_username = + nla_data(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); + connect.fils_erp_username_len = + nla_len(info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); + connect.fils_erp_realm = + nla_data(info->attrs[NL80211_ATTR_FILS_ERP_REALM]); + connect.fils_erp_realm_len = + nla_len(info->attrs[NL80211_ATTR_FILS_ERP_REALM]); + connect.fils_erp_next_seq_num = + nla_get_u16( + info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM]); + connect.fils_erp_rrk = + nla_data(info->attrs[NL80211_ATTR_FILS_ERP_RRK]); + connect.fils_erp_rrk_len = + nla_len(info->attrs[NL80211_ATTR_FILS_ERP_RRK]); + } else if (info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] || + info->attrs[NL80211_ATTR_FILS_ERP_REALM] || + info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] || + info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { + kzfree(connkeys); + return -EINVAL; + } + wdev_lock(dev->ieee80211_ptr); err = cfg80211_connect(rdev, dev, &connect, connkeys, @@ -9025,14 +9076,28 @@ static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info) memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - if (!info->attrs[NL80211_ATTR_PMKID]) return -EINVAL; pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]); - pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (info->attrs[NL80211_ATTR_MAC]) { + pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + } else if (info->attrs[NL80211_ATTR_SSID] && + info->attrs[NL80211_ATTR_FILS_CACHE_ID] && + (info->genlhdr->cmd == NL80211_CMD_DEL_PMKSA || + info->attrs[NL80211_ATTR_PMK])) { + pmksa.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + pmksa.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + pmksa.cache_id = + nla_data(info->attrs[NL80211_ATTR_FILS_CACHE_ID]); + } else { + return -EINVAL; + } + if (info->attrs[NL80211_ATTR_PMK]) { + pmksa.pmk = nla_data(info->attrs[NL80211_ATTR_PMK]); + pmksa.pmk_len = nla_len(info->attrs[NL80211_ATTR_PMK]); + } if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) @@ -13471,7 +13536,9 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, struct sk_buff *msg; void *hdr; - msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len, gfp); + msg = nlmsg_new(100 + cr->req_ie_len + cr->resp_ie_len + + cr->fils_kek_len + cr->pmk_len + + (cr->pmkid ? WLAN_PMKID_LEN : 0), gfp); if (!msg) return; @@ -13496,7 +13563,18 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, nla_put(msg, NL80211_ATTR_REQ_IE, cr->req_ie_len, cr->req_ie)) || (cr->resp_ie && nla_put(msg, NL80211_ATTR_RESP_IE, cr->resp_ie_len, - cr->resp_ie))) + cr->resp_ie)) || + (cr->update_erp_next_seq_num && + nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, + cr->fils_erp_next_seq_num)) || + (cr->status == WLAN_STATUS_SUCCESS && + ((cr->fils_kek && + nla_put(msg, NL80211_ATTR_FILS_KEK, cr->fils_kek_len, + cr->fils_kek)) || + (cr->pmk && + nla_put(msg, NL80211_ATTR_PMK, cr->pmk_len, cr->pmk)) || + (cr->pmkid && + nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->pmkid))))) goto nla_put_failure; genlmsg_end(msg, hdr); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index ebd7adc27246..6459bb7c21f7 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -805,7 +805,9 @@ void cfg80211_connect_done(struct net_device *dev, } ev = kzalloc(sizeof(*ev) + (params->bssid ? ETH_ALEN : 0) + - params->req_ie_len + params->resp_ie_len, gfp); + params->req_ie_len + params->resp_ie_len + + params->fils_kek_len + params->pmk_len + + (params->pmkid ? WLAN_PMKID_LEN : 0), gfp); if (!ev) { cfg80211_put_bss(wdev->wiphy, params->bss); return; @@ -832,6 +834,27 @@ void cfg80211_connect_done(struct net_device *dev, params->resp_ie_len); next += params->resp_ie_len; } + if (params->fils_kek_len) { + ev->cr.fils_kek = next; + ev->cr.fils_kek_len = params->fils_kek_len; + memcpy((void *)ev->cr.fils_kek, params->fils_kek, + params->fils_kek_len); + next += params->fils_kek_len; + } + if (params->pmk_len) { + ev->cr.pmk = next; + ev->cr.pmk_len = params->pmk_len; + memcpy((void *)ev->cr.pmk, params->pmk, params->pmk_len); + next += params->pmk_len; + } + if (params->pmkid) { + ev->cr.pmkid = next; + memcpy((void *)ev->cr.pmkid, params->pmkid, WLAN_PMKID_LEN); + next += WLAN_PMKID_LEN; + } + ev->cr.update_erp_next_seq_num = params->update_erp_next_seq_num; + if (params->update_erp_next_seq_num) + ev->cr.fils_erp_next_seq_num = params->fils_erp_next_seq_num; if (params->bss) cfg80211_hold_bss(bss_from_pub(params->bss)); ev->cr.bss = params->bss; -- cgit From 2754867792edfc9a6f2f49294be278d9be533ce2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 31 Mar 2017 09:12:39 +0200 Subject: cfg80211: add documentation for cfg80211_get_bss() This was missing, but is referenced a lot in the documentation. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 042137d7d226..273b1dca0861 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4699,12 +4699,22 @@ cfg80211_inform_bss(struct wiphy *wiphy, gfp); } +/** + * cfg80211_get_bss - get a BSS reference + * @wiphy: the wiphy this BSS struct belongs to + * @channel: the channel to search on (or %NULL) + * @bssid: the desired BSSID (or %NULL) + * @ssid: the desired SSID (or %NULL) + * @ssid_len: length of the SSID (or 0) + * @bss_type: type of BSS, see &enum ieee80211_bss_type + * @privacy: privacy filter, see &enum ieee80211_privacy + */ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, const u8 *ssid, size_t ssid_len, enum ieee80211_bss_type bss_type, - enum ieee80211_privacy); + enum ieee80211_privacy privacy); static inline struct cfg80211_bss * cfg80211_get_ibss(struct wiphy *wiphy, struct ieee80211_channel *channel, -- cgit From a339e4c226c0f2ea0c138e9ffc419874d370f57b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 31 Mar 2017 09:13:13 +0200 Subject: cfg80211: add intro to documentation This introduction section should be used in the documentation, do that at the beginning of the cfg80211 chapter. Signed-off-by: Johannes Berg --- Documentation/driver-api/80211/cfg80211.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/driver-api/80211/cfg80211.rst b/Documentation/driver-api/80211/cfg80211.rst index b101bc0c195b..8ffac57e1f5b 100644 --- a/Documentation/driver-api/80211/cfg80211.rst +++ b/Documentation/driver-api/80211/cfg80211.rst @@ -2,6 +2,9 @@ cfg80211 subsystem ================== +.. kernel-doc:: include/net/cfg80211.h + :doc: Introduction + Device registration =================== -- cgit From d6acfeb17d030bb3907e77c048b0e7783ad8e5a9 Mon Sep 17 00:00:00 2001 From: Felix Manlunas Date: Wed, 29 Mar 2017 17:56:43 -0700 Subject: vxlan: vxlan dev should inherit lowerdev's gso_max_size vxlan dev currently ignores lowerdev's gso_max_size, which adversely affects TSO performance of liquidio if it's the lowerdev. Egress TCP packets' skb->len often exceed liquidio's advertised gso_max_size. This may happen on other NIC drivers. Fix it by assigning lowerdev's gso_max_size to that of vxlan dev. Might as well do likewise for gso_max_segs. Single flow TSO throughput of liquidio as lowerdev (using iperf3): Before the patch: 139 Mbps After the patch : 8.68 Gbps Percent increase: 6,144 % Signed-off-by: Felix Manlunas Signed-off-by: Satanand Burla Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 1e54fb5c883a..714f74fb823a 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2926,6 +2926,11 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev, return -EINVAL; } + if (lowerdev) { + dev->gso_max_size = lowerdev->gso_max_size; + dev->gso_max_segs = lowerdev->gso_max_segs; + } + if (conf->mtu) { int max_mtu = ETH_MAX_MTU; -- cgit From b07e675b065c22ae232dbb6d5e3d670808a08dd4 Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Thu, 30 Mar 2017 16:21:40 +0300 Subject: fsl/fman: take into account all RGMII modes Accept the internal delay RGMII variants. Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fman/fman_dtsec.c | 8 +++++++- drivers/net/ethernet/freescale/fman/fman_memac.c | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c index 84ea130eed36..98bba10fc38c 100644 --- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -381,6 +381,9 @@ static int init(struct dtsec_regs __iomem *regs, struct dtsec_cfg *cfg, /* check RGMII support */ if (iface == PHY_INTERFACE_MODE_RGMII || + iface == PHY_INTERFACE_MODE_RGMII_ID || + iface == PHY_INTERFACE_MODE_RGMII_RXID || + iface == PHY_INTERFACE_MODE_RGMII_TXID || iface == PHY_INTERFACE_MODE_RMII) if (tmp & DTSEC_ID2_INT_REDUCED_OFF) return -EINVAL; @@ -390,7 +393,10 @@ static int init(struct dtsec_regs __iomem *regs, struct dtsec_cfg *cfg, if (tmp & DTSEC_ID2_INT_REDUCED_OFF) return -EINVAL; - is_rgmii = iface == PHY_INTERFACE_MODE_RGMII; + is_rgmii = iface == PHY_INTERFACE_MODE_RGMII || + iface == PHY_INTERFACE_MODE_RGMII_ID || + iface == PHY_INTERFACE_MODE_RGMII_RXID || + iface == PHY_INTERFACE_MODE_RGMII_TXID; is_sgmii = iface == PHY_INTERFACE_MODE_SGMII; is_qsgmii = iface == PHY_INTERFACE_MODE_QSGMII; diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c index cd6a53eaf161..c0296880feba 100644 --- a/drivers/net/ethernet/freescale/fman/fman_memac.c +++ b/drivers/net/ethernet/freescale/fman/fman_memac.c @@ -443,7 +443,10 @@ static int init(struct memac_regs __iomem *regs, struct memac_cfg *cfg, break; default: tmp |= IF_MODE_GMII; - if (phy_if == PHY_INTERFACE_MODE_RGMII) + if (phy_if == PHY_INTERFACE_MODE_RGMII || + phy_if == PHY_INTERFACE_MODE_RGMII_ID || + phy_if == PHY_INTERFACE_MODE_RGMII_RXID || + phy_if == PHY_INTERFACE_MODE_RGMII_TXID) tmp |= IF_MODE_RGMII | IF_MODE_RGMII_AUTO; } iowrite32be(tmp, ®s->if_mode); -- cgit From 58b7bd0f4b35284c64b70c65cc208f74af6080cb Mon Sep 17 00:00:00 2001 From: Madalin Bucur Date: Thu, 30 Mar 2017 16:24:15 +0300 Subject: dpaa_eth: use AVOIDBLOCK for Tx confirmation queues The AVOIDBLOCK flag determines the Tx confirmation queues processing to be redirected to any available CPU when the current one is slow in processing them. This may result in a higher Tx confirmation interrupt count but may reduce pressure on a certain CPU that with the previous setting would process all Tx confirmation frames. Signed-off-by: Madalin Bucur Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index d4bb8bf86a45..9a520e4f0df9 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -974,7 +974,7 @@ static int dpaa_fq_init(struct dpaa_fq *dpaa_fq, bool td_enable) * Tx Confirmation FQs. */ if (dpaa_fq->fq_type == FQ_TYPE_TX_CONFIRM) - initfq.fqd.fq_ctrl |= cpu_to_be16(QM_FQCTRL_HOLDACTIVE); + initfq.fqd.fq_ctrl |= cpu_to_be16(QM_FQCTRL_AVOIDBLOCK); /* FQ placement */ initfq.we_mask |= cpu_to_be16(QM_INITFQ_WE_DESTWQ); -- cgit From f3645652216839aabcbd864b81b25ba7fbab9e82 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:07 -0400 Subject: net: dsa: mv88e6xxx: move PVT description in info Not all Marvell switch chips feature a Cross-chip Port VLAN Table (PVT). Chips with a PVT use the same implementation, so a new mv88e6xxx_ops member won't be necessary yet. Add a "pvt" boolean member to the mv88e6xxx_info structure and kill the obsolete MV88E6XXX_FLAGS_PVT flag. Add a mv88e6xxx_has_pvt helper to wrap future checks of that condition. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 22 ++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/global2.c | 2 +- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 32 +++++++++++--------------------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 3c946af1159d..8f1f881d0375 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3578,6 +3578,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 8, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6097, .ops = &mv88e6085_ops, @@ -3610,6 +3611,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 8, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6097, .ops = &mv88e6097_ops, @@ -3626,6 +3628,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 9, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6123_ops, @@ -3657,6 +3660,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .atu_move_port_mask = 0x1f, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6341, .ops = &mv88e6141_ops, @@ -3673,6 +3677,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 9, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6161_ops, @@ -3689,6 +3694,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 9, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6165, .ops = &mv88e6165_ops, @@ -3705,6 +3711,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 9, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6171_ops, @@ -3721,6 +3728,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 9, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6172_ops, @@ -3737,6 +3745,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 9, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6175_ops, @@ -3753,6 +3762,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 9, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6176_ops, @@ -3785,6 +3795,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .tag_protocol = DSA_TAG_PROTO_DSA, .age_time_coeff = 3750, .g1_irqs = 9, + .pvt = true, .atu_move_port_mask = 0x1f, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6190_ops, @@ -3801,6 +3812,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 3750, .g1_irqs = 9, .atu_move_port_mask = 0x1f, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6190x_ops, @@ -3817,6 +3829,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 3750, .g1_irqs = 9, .atu_move_port_mask = 0x1f, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6191_ops, @@ -3833,6 +3846,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 9, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6240_ops, @@ -3849,6 +3863,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 3750, .g1_irqs = 9, .atu_move_port_mask = 0x1f, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6290_ops, @@ -3865,6 +3880,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 8, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6320, .ops = &mv88e6320_ops, @@ -3896,6 +3912,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .global1_addr = 0x1b, .age_time_coeff = 3750, .atu_move_port_mask = 0x1f, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6341, .ops = &mv88e6341_ops, @@ -3912,6 +3929,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 9, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6350_ops, @@ -3928,6 +3946,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 9, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6351, .ops = &mv88e6351_ops, @@ -3944,6 +3963,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 15000, .g1_irqs = 9, .atu_move_port_mask = 0xf, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_EDSA, .flags = MV88E6XXX_FLAGS_FAMILY_6352, .ops = &mv88e6352_ops, @@ -3959,6 +3979,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 3750, .g1_irqs = 9, .atu_move_port_mask = 0x1f, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6390_ops, @@ -3974,6 +3995,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .age_time_coeff = 3750, .g1_irqs = 9, .atu_move_port_mask = 0x1f, + .pvt = true, .tag_protocol = DSA_TAG_PROTO_DSA, .flags = MV88E6XXX_FLAGS_FAMILY_6390, .ops = &mv88e6390x_ops, diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 132559d46b95..6228aab2ad35 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -966,7 +966,7 @@ int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) return err; } - if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_PVT)) { + if (mv88e6xxx_has_pvt(chip)) { /* Initialize Cross-chip Port VLAN Table to reset defaults */ err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, GLOBAL2_PVT_ADDR_OP_INIT_ONES); diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 55367d05374e..97dd3e2d2a56 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -527,8 +527,6 @@ enum mv88e6xxx_cap { MV88E6XXX_CAP_G2_MGMT_EN_0X, /* (0x03) MGMT Enable Register 0x */ MV88E6XXX_CAP_G2_IRL_CMD, /* (0x09) Ingress Rate Command */ MV88E6XXX_CAP_G2_IRL_DATA, /* (0x0a) Ingress Rate Data */ - MV88E6XXX_CAP_G2_PVT_ADDR, /* (0x0b) Cross Chip Port VLAN Addr */ - MV88E6XXX_CAP_G2_PVT_DATA, /* (0x0c) Cross Chip Port VLAN Data */ MV88E6XXX_CAP_G2_POT, /* (0x0f) Priority Override Table */ /* Per VLAN Spanning Tree Unit (STU). @@ -561,8 +559,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAG_G2_MGMT_EN_0X BIT_ULL(MV88E6XXX_CAP_G2_MGMT_EN_0X) #define MV88E6XXX_FLAG_G2_IRL_CMD BIT_ULL(MV88E6XXX_CAP_G2_IRL_CMD) #define MV88E6XXX_FLAG_G2_IRL_DATA BIT_ULL(MV88E6XXX_CAP_G2_IRL_DATA) -#define MV88E6XXX_FLAG_G2_PVT_ADDR BIT_ULL(MV88E6XXX_CAP_G2_PVT_ADDR) -#define MV88E6XXX_FLAG_G2_PVT_DATA BIT_ULL(MV88E6XXX_CAP_G2_PVT_DATA) #define MV88E6XXX_FLAG_G2_POT BIT_ULL(MV88E6XXX_CAP_G2_POT) #define MV88E6XXX_FLAG_STU BIT_ULL(MV88E6XXX_CAP_STU) @@ -578,11 +574,6 @@ enum mv88e6xxx_cap { (MV88E6XXX_FLAG_SMI_CMD | \ MV88E6XXX_FLAG_SMI_DATA) -/* Cross-chip Port VLAN Table */ -#define MV88E6XXX_FLAGS_PVT \ - (MV88E6XXX_FLAG_G2_PVT_ADDR | \ - MV88E6XXX_FLAG_G2_PVT_DATA) - /* Fiber/SERDES Registers at SMI address F, page 1 */ #define MV88E6XXX_FLAGS_SERDES \ (MV88E6XXX_FLAG_PHY_PAGE | \ @@ -604,8 +595,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ - MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT) + MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6165 \ (MV88E6XXX_FLAG_G1_VTU_FID | \ @@ -617,8 +607,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ - MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT) + MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6185 \ (MV88E6XXX_FLAG_GLOBAL2 | \ @@ -635,8 +624,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ - MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT) + MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6341 \ (MV88E6XXX_FLAG_EEE | \ @@ -648,7 +636,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT | \ MV88E6XXX_FLAGS_SERDES) #define MV88E6XXX_FLAGS_FAMILY_6351 \ @@ -661,8 +648,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ - MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT) + MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6352 \ (MV88E6XXX_FLAG_EEE | \ @@ -676,7 +662,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT | \ MV88E6XXX_FLAGS_SERDES) #define MV88E6XXX_FLAGS_FAMILY_6390 \ @@ -686,8 +671,7 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ - MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAGS_PVT) + MV88E6XXX_FLAGS_MULTI_CHIP) struct mv88e6xxx_ops; @@ -701,6 +685,7 @@ struct mv88e6xxx_info { unsigned int global1_addr; unsigned int age_time_coeff; unsigned int g1_irqs; + bool pvt; enum dsa_tag_protocol tag_protocol; unsigned long long flags; @@ -936,6 +921,11 @@ static inline bool mv88e6xxx_has(struct mv88e6xxx_chip *chip, return (chip->info->flags & flags) == flags; } +static inline bool mv88e6xxx_has_pvt(struct mv88e6xxx_chip *chip) +{ + return chip->info->pvt; +} + static inline unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_chip *chip) { return chip->info->num_databases; -- cgit From 812289960f720c4a075f8766d40a3c6b5840c0cd Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:08 -0400 Subject: net: dsa: mv88e6xxx: use 4-bit port for PVT data The Cross-chip Port Based VLAN Table (PVT) supports two indexing modes, one using 5-bit for device and 4-bit for port, the other using 4-bit for device and 5-bit for port, configured via the Global 2 Misc register. Only 4 bits for the source port are needed when interconnecting 88E6xxx switch devices since they all support less than 16 physical ports. The full 5 bits are needed when interconnecting a device with 98DXxxx switch devices since they support more than 16 physical ports. Add a mv88e6xxx_pvt_setup helper to set the 4-bit port PVT mode, which will be extended later to also initialize the PVT content. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 15 +++++++++++++++ drivers/net/dsa/mv88e6xxx/global2.c | 25 +++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/global2.h | 7 +++++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 1 + 4 files changed, 48 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 8f1f881d0375..2a32bb490f92 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1198,6 +1198,17 @@ static int mv88e6xxx_atu_setup(struct mv88e6xxx_chip *chip) return mv88e6xxx_g1_atu_set_age_time(chip, 300000); } +static int mv88e6xxx_pvt_setup(struct mv88e6xxx_chip *chip) +{ + if (!mv88e6xxx_has_pvt(chip)) + return 0; + + /* Clear 5 Bit Port for usage with Marvell Link Street devices: + * use 4 bits for the Src_Port/Src_Trunk and 5 bits for the Src_Dev. + */ + return mv88e6xxx_g2_misc_4_bit_port(chip); +} + static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) { struct mv88e6xxx_chip *chip = ds->priv; @@ -2594,6 +2605,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) goto unlock; } + err = mv88e6xxx_pvt_setup(chip); + if (err) + goto unlock; + err = mv88e6xxx_atu_setup(chip); if (err) goto unlock; diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 6228aab2ad35..d64f4c15ccb7 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -784,6 +784,31 @@ static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip) return err; } +/* Offset 0x1D: Misc Register */ + +static int mv88e6xxx_g2_misc_5_bit_port(struct mv88e6xxx_chip *chip, + bool port_5_bit) +{ + u16 val; + int err; + + err = mv88e6xxx_g2_read(chip, GLOBAL2_MISC, &val); + if (err) + return err; + + if (port_5_bit) + val |= GLOBAL2_MISC_5_BIT_PORT; + else + val &= ~GLOBAL2_MISC_5_BIT_PORT; + + return mv88e6xxx_g2_write(chip, GLOBAL2_MISC, val); +} + +int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g2_misc_5_bit_port(chip, false); +} + static void mv88e6xxx_g2_irq_mask(struct irq_data *d) { struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index f8b6dd93213a..71fb2ff541ba 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -42,6 +42,8 @@ int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, struct ethtool_eeprom *eeprom, u8 *data); +int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip); + int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip); int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip); void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip); @@ -110,6 +112,11 @@ static inline int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, return -EOPNOTSUPP; } +int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip) +{ + return -EOPNOTSUPP; +} + static inline int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) { return -EOPNOTSUPP; diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 97dd3e2d2a56..bcaa55b20f5a 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -439,6 +439,7 @@ #define GLOBAL2_WDOG_FORCE_IRQ BIT(0) #define GLOBAL2_QOS_WEIGHT 0x1c #define GLOBAL2_MISC 0x1d +#define GLOBAL2_MISC_5_BIT_PORT BIT(14) #define MV88E6XXX_N_FID 4096 -- cgit From 17a1594e2d58f021d629da72d1a82b1441217460 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:09 -0400 Subject: net: dsa: mv88e6xxx: program the PVT with all ones The Cross-chip Port Based VLAN Table (PVT) is currently initialized with all ones, allowing any external ports to egress frames on local ports. This commit implements the PVT access functions and programs the PVT with all ones for the local switch ports only, instead of using the Init operation. The current behavior is unchanged for the moment. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 31 ++++++++++++++++++++- drivers/net/dsa/mv88e6xxx/global2.c | 52 +++++++++++++++++++++++++++++------ drivers/net/dsa/mv88e6xxx/global2.h | 8 ++++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 4 +++ 4 files changed, 86 insertions(+), 9 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 2a32bb490f92..fb6a723c2137 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1198,15 +1198,44 @@ static int mv88e6xxx_atu_setup(struct mv88e6xxx_chip *chip) return mv88e6xxx_g1_atu_set_age_time(chip, 300000); } +static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port) +{ + u16 pvlan = 0; + + if (!mv88e6xxx_has_pvt(chip)) + return -EOPNOTSUPP; + + /* Skip the local source device, which uses in-chip port VLAN */ + if (dev != chip->ds->index) + pvlan = mv88e6xxx_port_mask(chip); + + return mv88e6xxx_g2_pvt_write(chip, dev, port, pvlan); +} + static int mv88e6xxx_pvt_setup(struct mv88e6xxx_chip *chip) { + int dev, port; + int err; + if (!mv88e6xxx_has_pvt(chip)) return 0; /* Clear 5 Bit Port for usage with Marvell Link Street devices: * use 4 bits for the Src_Port/Src_Trunk and 5 bits for the Src_Dev. */ - return mv88e6xxx_g2_misc_4_bit_port(chip); + err = mv88e6xxx_g2_misc_4_bit_port(chip); + if (err) + return err; + + for (dev = 0; dev < MV88E6XXX_MAX_PVT_SWITCHES; ++dev) { + for (port = 0; port < MV88E6XXX_MAX_PVT_PORTS; ++port) { + err = mv88e6xxx_pvt_map(chip, dev, port); + if (err) + return err; + } + } + + return 0; } static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index d64f4c15ccb7..7c6bc33a9516 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -172,6 +172,50 @@ static int mv88e6xxx_g2_clear_irl(struct mv88e6xxx_chip *chip) return err; } +/* Offset 0x0B: Cross-chip Port VLAN (Addr) Register + * Offset 0x0C: Cross-chip Port VLAN Data Register + */ + +static int mv88e6xxx_g2_pvt_op_wait(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g2_wait(chip, GLOBAL2_PVT_ADDR, GLOBAL2_PVT_ADDR_BUSY); +} + +static int mv88e6xxx_g2_pvt_op(struct mv88e6xxx_chip *chip, int src_dev, + int src_port, u16 op) +{ + int err; + + /* 9-bit Cross-chip PVT pointer: with GLOBAL2_MISC_5_BIT_PORT cleared, + * source device is 5-bit, source port is 4-bit. + */ + op |= (src_dev & 0x1f) << 4; + op |= (src_port & 0xf); + + err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, op); + if (err) + return err; + + return mv88e6xxx_g2_pvt_op_wait(chip); +} + +int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev, + int src_port, u16 data) +{ + int err; + + err = mv88e6xxx_g2_pvt_op_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_DATA, data); + if (err) + return err; + + return mv88e6xxx_g2_pvt_op(chip, src_dev, src_port, + GLOBAL2_PVT_ADDR_OP_WRITE_PVLAN); +} + /* Offset 0x0D: Switch MAC/WoL/WoF register */ static int mv88e6xxx_g2_switch_mac_write(struct mv88e6xxx_chip *chip, @@ -991,14 +1035,6 @@ int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip) return err; } - if (mv88e6xxx_has_pvt(chip)) { - /* Initialize Cross-chip Port VLAN Table to reset defaults */ - err = mv88e6xxx_g2_write(chip, GLOBAL2_PVT_ADDR, - GLOBAL2_PVT_ADDR_OP_INIT_ONES); - if (err) - return err; - } - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_POT)) { /* Clear the priority override table. */ err = mv88e6xxx_g2_clear_pot(chip); diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index 71fb2ff541ba..96046bb12ca1 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -42,6 +42,8 @@ int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip, int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, struct ethtool_eeprom *eeprom, u8 *data); +int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev, + int src_port, u16 data); int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip); int mv88e6xxx_g2_setup(struct mv88e6xxx_chip *chip); @@ -112,6 +114,12 @@ static inline int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip, return -EOPNOTSUPP; } +int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip, int src_dev, + int src_port, u16 data) +{ + return -EOPNOTSUPP; +} + int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip) { return -EOPNOTSUPP; diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index bcaa55b20f5a..c8f54986996b 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -443,6 +443,10 @@ #define MV88E6XXX_N_FID 4096 +/* PVT limits for 4-bit port and 5-bit switch */ +#define MV88E6XXX_MAX_PVT_SWITCHES 32 +#define MV88E6XXX_MAX_PVT_PORTS 16 + enum mv88e6xxx_frame_mode { MV88E6XXX_FRAME_MODE_NORMAL, MV88E6XXX_FRAME_MODE_DSA, -- cgit From 73b1204d070d2970749beb3fa3ca6e639dc7a3b6 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:10 -0400 Subject: net: dsa: mv88e6xxx: allocate the number of ports The current code allocates DSA_MAX_PORTS ports for a Marvell dsa_switch structure. Provide the exact number of ports so the corresponding ds->num_ports is accurate. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index fb6a723c2137..28bdfadbf050 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -4286,7 +4286,7 @@ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) struct device *dev = chip->dev; struct dsa_switch *ds; - ds = dsa_switch_alloc(dev, DSA_MAX_PORTS); + ds = dsa_switch_alloc(dev, mv88e6xxx_num_ports(chip)); if (!ds) return -ENOMEM; -- cgit From e5887a2a110947cdd936fcc8722d8f08f3e8e392 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:11 -0400 Subject: net: dsa: mv88e6xxx: rework in-chip bridging All ports -- internal and external, for chips featuring a PVT -- have a mask restricting to which internal ports a frame is allowed to egress. Now that DSA exposes the number of ports and their bridge devices, it is possible to extract the code generating the VLAN map and make it generic so that it can be shared later with the cross-chip bridging code. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 49 ++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 28bdfadbf050..9b2d369715d7 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1123,27 +1123,42 @@ out: return err; } -static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port) +static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) { - struct dsa_switch *ds = chip->ds; - struct net_device *bridge = ds->ports[port].bridge_dev; - u16 output_ports = 0; + struct dsa_switch *ds = NULL; + struct net_device *br; + u16 pvlan; int i; - /* allow CPU port or DSA link(s) to send frames to every port */ - if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { - output_ports = ~0; - } else { - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { - /* allow sending frames to every group member */ - if (bridge && ds->ports[i].bridge_dev == bridge) - output_ports |= BIT(i); + if (dev < DSA_MAX_SWITCHES) + ds = chip->ds->dst->ds[dev]; - /* allow sending frames to CPU port and DSA link(s) */ - if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)) - output_ports |= BIT(i); - } - } + /* Prevent frames from unknown switch or port */ + if (!ds || port >= ds->num_ports) + return 0; + + /* Frames from DSA links and CPU ports can egress any local port */ + if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) + return mv88e6xxx_port_mask(chip); + + br = ds->ports[port].bridge_dev; + pvlan = 0; + + /* Frames from user ports can egress any local DSA links and CPU ports, + * as well as any local member of their bridge group. + */ + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) + if (dsa_is_cpu_port(chip->ds, i) || + dsa_is_dsa_port(chip->ds, i) || + (br && chip->ds->ports[i].bridge_dev == br)) + pvlan |= BIT(i); + + return pvlan; +} + +static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port) +{ + u16 output_ports = mv88e6xxx_port_vlan(chip, chip->ds->index, port); /* prevent frames from going back out of the port they came in on */ output_ports &= ~BIT(port); -- cgit From 240ea3ef70a4591ba0f77cfafe4b467d55ce80d0 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:12 -0400 Subject: net: dsa: mv88e6xxx: factorize in-chip bridge map Factorize the code in the DSA port_bridge_{join,leave} routines used to program the port VLAN map of all local ports of a given bridge group. At the same time shorten the _mv88e6xxx_port_based_vlan_map to get rid of the old underscore prefix naming convention. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 47 +++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 9b2d369715d7..3802e1bdd111 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1156,7 +1156,7 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) return pvlan; } -static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port) +static int mv88e6xxx_port_vlan_map(struct mv88e6xxx_chip *chip, int port) { u16 output_ports = mv88e6xxx_port_vlan(chip, chip->ds->index, port); @@ -2140,23 +2140,32 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, return err; } -static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, - struct net_device *br) +static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, + struct net_device *br) { - struct mv88e6xxx_chip *chip = ds->priv; - int i, err = 0; - - mutex_lock(&chip->reg_lock); + int port; + int err; - /* Remap each port's VLANTable */ - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { - if (ds->ports[i].bridge_dev == br) { - err = _mv88e6xxx_port_based_vlan_map(chip, i); + /* Remap the Port VLAN of each local bridge group member */ + for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) { + if (chip->ds->ports[port].bridge_dev == br) { + err = mv88e6xxx_port_vlan_map(chip, port); if (err) - break; + return err; } } + return 0; +} + +static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, + struct net_device *br) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + mutex_lock(&chip->reg_lock); + err = mv88e6xxx_bridge_map(chip, br); mutex_unlock(&chip->reg_lock); return err; @@ -2166,17 +2175,11 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br) { struct mv88e6xxx_chip *chip = ds->priv; - int i; mutex_lock(&chip->reg_lock); - - /* Remap each port's VLANTable */ - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) - if (i == port || ds->ports[i].bridge_dev == br) - if (_mv88e6xxx_port_based_vlan_map(chip, i)) - netdev_warn(ds->ports[i].netdev, - "failed to remap\n"); - + if (mv88e6xxx_bridge_map(chip, br) || + mv88e6xxx_port_vlan_map(chip, port)) + dev_err(ds->dev, "failed to remap in-chip Port VLAN\n"); mutex_unlock(&chip->reg_lock); } @@ -2490,7 +2493,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) if (err) return err; - err = _mv88e6xxx_port_based_vlan_map(chip, port); + err = mv88e6xxx_port_vlan_map(chip, port); if (err) return err; -- cgit From e96a6e027510279a5089b24e2f217693488327f3 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:13 -0400 Subject: net: dsa: mv88e6xxx: remap existing bridge members When a local port of a switch chip becomes a member of a bridge group, we need to reprogram the Cross-chip Port Based VLAN Table (PVT) to allow existing cross-chip bridge members to egress frames on the new ports. There is no functional changes yet, since the PVT is still programmed with all ones, allowing any external port to egress frames locally. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 3802e1bdd111..c6f45a2a9335 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2143,7 +2143,9 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, struct net_device *br) { + struct dsa_switch *ds; int port; + int dev; int err; /* Remap the Port VLAN of each local bridge group member */ @@ -2155,6 +2157,24 @@ static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip, } } + if (!mv88e6xxx_has_pvt(chip)) + return 0; + + /* Remap the Port VLAN of each cross-chip bridge group member */ + for (dev = 0; dev < DSA_MAX_SWITCHES; ++dev) { + ds = chip->ds->dst->ds[dev]; + if (!ds) + break; + + for (port = 0; port < ds->num_ports; ++port) { + if (ds->ports[port].bridge_dev == br) { + err = mv88e6xxx_pvt_map(chip, dev, port); + if (err) + return err; + } + } + } + return 0; } -- cgit From 40ef2c93395fcd25051643707d0956ce9120de41 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:14 -0400 Subject: net: dsa: add cross-chip bridging operations Introduce crosschip_bridge_{join,leave} operations in the dsa_switch_ops structure, which can be used by switches supporting interconnection. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 8 ++++++++ net/dsa/switch.c | 12 ++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index 951b5e49e899..ffe56cc338fe 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -456,6 +456,14 @@ struct dsa_switch_ops { bool ingress); void (*port_mirror_del)(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror); + + /* + * Cross-chip operations + */ + int (*crosschip_bridge_join)(struct dsa_switch *ds, int sw_index, + int port, struct net_device *br); + void (*crosschip_bridge_leave)(struct dsa_switch *ds, int sw_index, + int port, struct net_device *br); }; struct dsa_switch_driver { diff --git a/net/dsa/switch.c b/net/dsa/switch.c index 7b6f38e5fef6..ca6e26e514f0 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -20,9 +20,9 @@ static int dsa_switch_bridge_join(struct dsa_switch *ds, if (ds->index == info->sw_index && ds->ops->port_bridge_join) return ds->ops->port_bridge_join(ds, info->port, info->br); - if (ds->index != info->sw_index) - dev_dbg(ds->dev, "crosschip DSA port %d.%d bridged to %s\n", - info->sw_index, info->port, netdev_name(info->br)); + if (ds->index != info->sw_index && ds->ops->crosschip_bridge_join) + return ds->ops->crosschip_bridge_join(ds, info->sw_index, + info->port, info->br); return 0; } @@ -33,9 +33,9 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds, if (ds->index == info->sw_index && ds->ops->port_bridge_leave) ds->ops->port_bridge_leave(ds, info->port, info->br); - if (ds->index != info->sw_index) - dev_dbg(ds->dev, "crosschip DSA port %d.%d unbridged from %s\n", - info->sw_index, info->port, netdev_name(info->br)); + if (ds->index != info->sw_index && ds->ops->crosschip_bridge_leave) + ds->ops->crosschip_bridge_leave(ds, info->sw_index, info->port, + info->br); return 0; } -- cgit From aec5ac88d31b385d851f5f29939e556a07ca7d3d Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 30 Mar 2017 17:37:15 -0400 Subject: net: dsa: mv88e6xxx: add cross-chip bridging Implement the DSA cross-chip bridging operations by remapping the local ports an external source port can egress frames to, when this cross-chip port joins or leaves a bridge. The PVT is no longer configured with all ones allowing any external frame to egress any local port. Only DSA and CPU ports, as well as bridge group members, can egress frames on local ports. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index c6f45a2a9335..44ba8cff5631 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1222,7 +1222,7 @@ static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port) /* Skip the local source device, which uses in-chip port VLAN */ if (dev != chip->ds->index) - pvlan = mv88e6xxx_port_mask(chip); + pvlan = mv88e6xxx_port_vlan(chip, dev, port); return mv88e6xxx_g2_pvt_write(chip, dev, port, pvlan); } @@ -2203,6 +2203,36 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port, mutex_unlock(&chip->reg_lock); } +static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds, int dev, + int port, struct net_device *br) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + if (!mv88e6xxx_has_pvt(chip)) + return 0; + + mutex_lock(&chip->reg_lock); + err = mv88e6xxx_pvt_map(chip, dev, port); + mutex_unlock(&chip->reg_lock); + + return err; +} + +static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds, int dev, + int port, struct net_device *br) +{ + struct mv88e6xxx_chip *chip = ds->priv; + + if (!mv88e6xxx_has_pvt(chip)) + return; + + mutex_lock(&chip->reg_lock); + if (mv88e6xxx_pvt_map(chip, dev, port)) + dev_err(ds->dev, "failed to remap cross-chip Port VLAN\n"); + mutex_unlock(&chip->reg_lock); +} + static int mv88e6xxx_software_reset(struct mv88e6xxx_chip *chip) { if (chip->info->ops->reset) @@ -4313,6 +4343,8 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .port_mdb_add = mv88e6xxx_port_mdb_add, .port_mdb_del = mv88e6xxx_port_mdb_del, .port_mdb_dump = mv88e6xxx_port_mdb_dump, + .crosschip_bridge_join = mv88e6xxx_crosschip_bridge_join, + .crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave, }; static struct dsa_switch_driver mv88e6xxx_switch_drv = { -- cgit From 98cd1552ea27e512c7e99e2aa76042a26e4fb25c Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 30 Mar 2017 18:43:21 -0700 Subject: net: dsa: Mock-up driver This patch adds support for a DSA mock-up driver which essentially does the following: - registers/unregisters 4 fixed PHYs to the slave network devices - uses eth0 (configurable) as the master netdev - registers the switch as a fixed MDIO device against the fixed MDIO bus at address 31 - includes dynamic debug prints for dsa_switch_ops functions that can be enabled to get call traces This is a good way to test modular builds as well as exercise the DSA APIs without requiring access to real hardware. This does not test the data-path, although this could be added later on. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/Kconfig | 8 + drivers/net/dsa/Makefile | 2 +- drivers/net/dsa/dsa_loop.c | 328 ++++++++++++++++++++++++++++++++++++++ drivers/net/dsa/dsa_loop.h | 19 +++ drivers/net/dsa/dsa_loop_bdinfo.c | 34 ++++ 5 files changed, 390 insertions(+), 1 deletion(-) create mode 100644 drivers/net/dsa/dsa_loop.c create mode 100644 drivers/net/dsa/dsa_loop.h create mode 100644 drivers/net/dsa/dsa_loop_bdinfo.c diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 564b267c8428..ba2e655eec19 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -34,4 +34,12 @@ config NET_DSA_QCA8K This enables support for the Qualcomm Atheros QCA8K Ethernet switch chips. +config NET_DSA_LOOP + tristate "DSA mock-up Ethernet switch chip support" + depends on NET_DSA + select FIXED_PHY + ---help--- + This enables support for a fake mock-up switch chip which + exercises the DSA APIs. + endmenu diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index a3c941632217..5c8830991041 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm-sf2.o bcm-sf2-objs := bcm_sf2.o bcm_sf2_cfp.o obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o - obj-y += b53/ obj-y += mv88e6xxx/ +obj-$(CONFIG_NET_DSA_LOOP) += dsa_loop.o dsa_loop_bdinfo.o diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c new file mode 100644 index 000000000000..bc5acc15edbf --- /dev/null +++ b/drivers/net/dsa/dsa_loop.c @@ -0,0 +1,328 @@ +/* + * Distributed Switch Architecture loopback driver + * + * Copyright (C) 2016, Florian Fainelli + * + * 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 "dsa_loop.h" + +struct dsa_loop_vlan { + u16 members; + u16 untagged; +}; + +#define DSA_LOOP_VLANS 5 + +struct dsa_loop_priv { + struct mii_bus *bus; + unsigned int port_base; + struct dsa_loop_vlan vlans[DSA_LOOP_VLANS]; + struct net_device *netdev; + u16 pvid; +}; + +static struct phy_device *phydevs[PHY_MAX_ADDR]; + +static enum dsa_tag_protocol dsa_loop_get_protocol(struct dsa_switch *ds) +{ + dev_dbg(ds->dev, "%s\n", __func__); + + return DSA_TAG_PROTO_NONE; +} + +static int dsa_loop_setup(struct dsa_switch *ds) +{ + dev_dbg(ds->dev, "%s\n", __func__); + + return 0; +} + +static int dsa_loop_set_addr(struct dsa_switch *ds, u8 *addr) +{ + dev_dbg(ds->dev, "%s\n", __func__); + + return 0; +} + +static int dsa_loop_phy_read(struct dsa_switch *ds, int port, int regnum) +{ + struct dsa_loop_priv *ps = ds->priv; + struct mii_bus *bus = ps->bus; + + dev_dbg(ds->dev, "%s\n", __func__); + + return mdiobus_read_nested(bus, ps->port_base + port, regnum); +} + +static int dsa_loop_phy_write(struct dsa_switch *ds, int port, + int regnum, u16 value) +{ + struct dsa_loop_priv *ps = ds->priv; + struct mii_bus *bus = ps->bus; + + dev_dbg(ds->dev, "%s\n", __func__); + + return mdiobus_write_nested(bus, ps->port_base + port, regnum, value); +} + +static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port, + struct net_device *bridge) +{ + dev_dbg(ds->dev, "%s\n", __func__); + + return 0; +} + +static void dsa_loop_port_bridge_leave(struct dsa_switch *ds, int port, + struct net_device *bridge) +{ + dev_dbg(ds->dev, "%s\n", __func__); +} + +static void dsa_loop_port_stp_state_set(struct dsa_switch *ds, int port, + u8 state) +{ + dev_dbg(ds->dev, "%s\n", __func__); +} + +static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering) +{ + dev_dbg(ds->dev, "%s\n", __func__); + + return 0; +} + +static int dsa_loop_port_vlan_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans) +{ + struct dsa_loop_priv *ps = ds->priv; + struct mii_bus *bus = ps->bus; + + dev_dbg(ds->dev, "%s\n", __func__); + + /* Just do a sleeping operation to make lockdep checks effective */ + mdiobus_read(bus, ps->port_base + port, MII_BMSR); + + if (vlan->vid_end > DSA_LOOP_VLANS) + return -ERANGE; + + return 0; +} + +static void dsa_loop_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans) +{ + bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; + struct dsa_loop_priv *ps = ds->priv; + struct mii_bus *bus = ps->bus; + struct dsa_loop_vlan *vl; + u16 vid; + + dev_dbg(ds->dev, "%s\n", __func__); + + /* Just do a sleeping operation to make lockdep checks effective */ + mdiobus_read(bus, ps->port_base + port, MII_BMSR); + + for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { + vl = &ps->vlans[vid]; + + vl->members |= BIT(port); + if (untagged) + vl->untagged |= BIT(port); + else + vl->untagged &= ~BIT(port); + } + + if (pvid) + ps->pvid = vid; +} + +static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + struct dsa_loop_priv *ps = ds->priv; + struct mii_bus *bus = ps->bus; + struct dsa_loop_vlan *vl; + u16 vid, pvid; + + dev_dbg(ds->dev, "%s\n", __func__); + + /* Just do a sleeping operation to make lockdep checks effective */ + mdiobus_read(bus, ps->port_base + port, MII_BMSR); + + for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { + vl = &ps->vlans[vid]; + + vl->members &= ~BIT(port); + if (untagged) + vl->untagged &= ~BIT(port); + + if (pvid == vid) + pvid = 1; + } + ps->pvid = pvid; + + return 0; +} + +static int dsa_loop_port_vlan_dump(struct dsa_switch *ds, int port, + struct switchdev_obj_port_vlan *vlan, + int (*cb)(struct switchdev_obj *obj)) +{ + struct dsa_loop_priv *ps = ds->priv; + struct mii_bus *bus = ps->bus; + struct dsa_loop_vlan *vl; + u16 vid, vid_start = 0; + int err; + + dev_dbg(ds->dev, "%s\n", __func__); + + /* Just do a sleeping operation to make lockdep checks effective */ + mdiobus_read(bus, ps->port_base + port, MII_BMSR); + + for (vid = vid_start; vid < DSA_LOOP_VLANS; vid++) { + vl = &ps->vlans[vid]; + + if (!(vl->members & BIT(port))) + continue; + + vlan->vid_begin = vlan->vid_end = vid; + vlan->flags = 0; + + if (vl->untagged & BIT(port)) + vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED; + if (ps->pvid == vid) + vlan->flags |= BRIDGE_VLAN_INFO_PVID; + + err = cb(&vlan->obj); + if (err) + break; + } + + return err; +} + +static struct dsa_switch_ops dsa_loop_driver = { + .get_tag_protocol = dsa_loop_get_protocol, + .setup = dsa_loop_setup, + .set_addr = dsa_loop_set_addr, + .phy_read = dsa_loop_phy_read, + .phy_write = dsa_loop_phy_write, + .port_bridge_join = dsa_loop_port_bridge_join, + .port_bridge_leave = dsa_loop_port_bridge_leave, + .port_stp_state_set = dsa_loop_port_stp_state_set, + .port_vlan_filtering = dsa_loop_port_vlan_filtering, + .port_vlan_prepare = dsa_loop_port_vlan_prepare, + .port_vlan_add = dsa_loop_port_vlan_add, + .port_vlan_del = dsa_loop_port_vlan_del, + .port_vlan_dump = dsa_loop_port_vlan_dump, +}; + +static int dsa_loop_drv_probe(struct mdio_device *mdiodev) +{ + struct dsa_loop_pdata *pdata = mdiodev->dev.platform_data; + struct dsa_loop_priv *ps; + struct dsa_switch *ds; + + if (!pdata) + return -ENODEV; + + dev_info(&mdiodev->dev, "%s: 0x%0x\n", + pdata->name, pdata->enabled_ports); + + ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS); + if (!ds) + return -ENOMEM; + + ps = devm_kzalloc(&mdiodev->dev, sizeof(*ps), GFP_KERNEL); + ps->netdev = dev_get_by_name(&init_net, pdata->netdev); + if (!ps->netdev) + return -EPROBE_DEFER; + + pdata->cd.netdev[DSA_LOOP_CPU_PORT] = &ps->netdev->dev; + + ds->dev = &mdiodev->dev; + ds->ops = &dsa_loop_driver; + ds->priv = ps; + ps->bus = mdiodev->bus; + + dev_set_drvdata(&mdiodev->dev, ds); + + return dsa_register_switch(ds, ds->dev); +} + +static void dsa_loop_drv_remove(struct mdio_device *mdiodev) +{ + struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); + struct dsa_loop_priv *ps = ds->priv; + + dsa_unregister_switch(ds); + dev_put(ps->netdev); +} + +static struct mdio_driver dsa_loop_drv = { + .mdiodrv.driver = { + .name = "dsa-loop", + }, + .probe = dsa_loop_drv_probe, + .remove = dsa_loop_drv_remove, +}; + +#define NUM_FIXED_PHYS (DSA_LOOP_NUM_PORTS - 2) + +static void unregister_fixed_phys(void) +{ + unsigned int i; + + for (i = 0; i < NUM_FIXED_PHYS; i++) + if (phydevs[i]) + fixed_phy_unregister(phydevs[i]); +} + +static int __init dsa_loop_init(void) +{ + struct fixed_phy_status status = { + .link = 1, + .speed = SPEED_100, + .duplex = DUPLEX_FULL, + }; + unsigned int i; + + for (i = 0; i < NUM_FIXED_PHYS; i++) + phydevs[i] = fixed_phy_register(PHY_POLL, &status, -1, NULL); + + return mdio_driver_register(&dsa_loop_drv); +} +module_init(dsa_loop_init); + +static void __exit dsa_loop_exit(void) +{ + mdio_driver_unregister(&dsa_loop_drv); + unregister_fixed_phys(); +} +module_exit(dsa_loop_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Florian Fainelli"); +MODULE_DESCRIPTION("DSA loopback driver"); diff --git a/drivers/net/dsa/dsa_loop.h b/drivers/net/dsa/dsa_loop.h new file mode 100644 index 000000000000..dc396877fc95 --- /dev/null +++ b/drivers/net/dsa/dsa_loop.h @@ -0,0 +1,19 @@ +#ifndef __DSA_LOOP_H +#define __DSA_LOOP_H + +struct dsa_chip_data; + +struct dsa_loop_pdata { + /* Must be first, such that dsa_register_switch() can access this + * without gory pointer manipulations + */ + struct dsa_chip_data cd; + const char *name; + unsigned int enabled_ports; + const char *netdev; +}; + +#define DSA_LOOP_NUM_PORTS 6 +#define DSA_LOOP_CPU_PORT (DSA_LOOP_NUM_PORTS - 1) + +#endif /* __DSA_LOOP_H */ diff --git a/drivers/net/dsa/dsa_loop_bdinfo.c b/drivers/net/dsa/dsa_loop_bdinfo.c new file mode 100644 index 000000000000..fb8d5dc71013 --- /dev/null +++ b/drivers/net/dsa/dsa_loop_bdinfo.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +#include "dsa_loop.h" + +static struct dsa_loop_pdata dsa_loop_pdata = { + .cd = { + .port_names[0] = "lan1", + .port_names[1] = "lan2", + .port_names[2] = "lan3", + .port_names[3] = "lan4", + .port_names[DSA_LOOP_CPU_PORT] = "cpu", + }, + .name = "DSA mockup driver", + .enabled_ports = 0x1f, + .netdev = "eth0", +}; + +static const struct mdio_board_info bdinfo = { + .bus_id = "fixed-0", + .modalias = "dsa-loop", + .mdio_addr = 31, + .platform_data = &dsa_loop_pdata, +}; + +static int __init dsa_loop_bdinfo_init(void) +{ + return mdiobus_register_board_info(&bdinfo, 1); +} +arch_initcall(dsa_loop_bdinfo_init) + +MODULE_LICENSE("GPL"); -- cgit From 1cf1cae963c2e6032aebe1637e995bc2f5d330f4 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Thu, 30 Mar 2017 21:45:38 -0700 Subject: bpf: introduce BPF_PROG_TEST_RUN command development and testing of networking bpf programs is quite cumbersome. Despite availability of user space bpf interpreters the kernel is the ultimate authority and execution environment. Current test frameworks for TC include creation of netns, veth, qdiscs and use of various packet generators just to test functionality of a bpf program. XDP testing is even more complicated, since qemu needs to be started with gro/gso disabled and precise queue configuration, transferring of xdp program from host into guest, attaching to virtio/eth0 and generating traffic from the host while capturing the results from the guest. Moreover analyzing performance bottlenecks in XDP program is impossible in virtio environment, since cost of running the program is tiny comparing to the overhead of virtio packet processing, so performance testing can only be done on physical nic with another server generating traffic. Furthermore ongoing changes to user space control plane of production applications cannot be run on the test servers leaving bpf programs stubbed out for testing. Last but not least, the upstream llvm changes are validated by the bpf backend testsuite which has no ability to test the code generated. To improve this situation introduce BPF_PROG_TEST_RUN command to test and performance benchmark bpf programs. Joint work with Daniel Borkmann. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Acked-by: Martin KaFai Lau Signed-off-by: David S. Miller --- include/linux/bpf.h | 7 ++ include/uapi/linux/bpf.h | 12 ++++ kernel/bpf/syscall.c | 27 +++++++- net/Makefile | 2 +- net/bpf/Makefile | 1 + net/bpf/test_run.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++ net/core/filter.c | 5 ++ 7 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 net/bpf/Makefile create mode 100644 net/bpf/test_run.c diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 2ae39a3e9ead..bbb513da5075 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -169,6 +169,8 @@ struct bpf_verifier_ops { const struct bpf_insn *src, struct bpf_insn *dst, struct bpf_prog *prog); + int (*test_run)(struct bpf_prog *prog, const union bpf_attr *kattr, + union bpf_attr __user *uattr); }; struct bpf_prog_type_list { @@ -233,6 +235,11 @@ typedef unsigned long (*bpf_ctx_copy_t)(void *dst, const void *src, u64 bpf_event_output(struct bpf_map *map, u64 flags, void *meta, u64 meta_size, void *ctx, u64 ctx_size, bpf_ctx_copy_t ctx_copy); +int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, + union bpf_attr __user *uattr); +int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, + union bpf_attr __user *uattr); + #ifdef CONFIG_BPF_SYSCALL DECLARE_PER_CPU(int, bpf_prog_active); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 28317a04c34d..a1d95386f562 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -81,6 +81,7 @@ enum bpf_cmd { BPF_OBJ_GET, BPF_PROG_ATTACH, BPF_PROG_DETACH, + BPF_PROG_TEST_RUN, }; enum bpf_map_type { @@ -189,6 +190,17 @@ union bpf_attr { __u32 attach_type; __u32 attach_flags; }; + + struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */ + __u32 prog_fd; + __u32 retval; + __u32 data_size_in; + __u32 data_size_out; + __aligned_u64 data_in; + __aligned_u64 data_out; + __u32 repeat; + __u32 duration; + } test; } __attribute__((aligned(8))); /* BPF helper function descriptions: diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index c35ebfe6d84d..ab0cf4c43690 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -973,6 +973,28 @@ static int bpf_prog_detach(const union bpf_attr *attr) } #endif /* CONFIG_CGROUP_BPF */ +#define BPF_PROG_TEST_RUN_LAST_FIELD test.duration + +static int bpf_prog_test_run(const union bpf_attr *attr, + union bpf_attr __user *uattr) +{ + struct bpf_prog *prog; + int ret = -ENOTSUPP; + + if (CHECK_ATTR(BPF_PROG_TEST_RUN)) + return -EINVAL; + + prog = bpf_prog_get(attr->test.prog_fd); + if (IS_ERR(prog)) + return PTR_ERR(prog); + + if (prog->aux->ops->test_run) + ret = prog->aux->ops->test_run(prog, attr, uattr); + + bpf_prog_put(prog); + return ret; +} + SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) { union bpf_attr attr = {}; @@ -1039,7 +1061,6 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz case BPF_OBJ_GET: err = bpf_obj_get(&attr); break; - #ifdef CONFIG_CGROUP_BPF case BPF_PROG_ATTACH: err = bpf_prog_attach(&attr); @@ -1048,7 +1069,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz err = bpf_prog_detach(&attr); break; #endif - + case BPF_PROG_TEST_RUN: + err = bpf_prog_test_run(&attr, uattr); + break; default: err = -EINVAL; break; diff --git a/net/Makefile b/net/Makefile index 9b681550e3a3..9086ffbb5085 100644 --- a/net/Makefile +++ b/net/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_NET) += $(tmp-y) # LLC has to be linked before the files in net/802/ obj-$(CONFIG_LLC) += llc/ -obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/ +obj-$(CONFIG_NET) += ethernet/ 802/ sched/ netlink/ bpf/ obj-$(CONFIG_NETFILTER) += netfilter/ obj-$(CONFIG_INET) += ipv4/ obj-$(CONFIG_XFRM) += xfrm/ diff --git a/net/bpf/Makefile b/net/bpf/Makefile new file mode 100644 index 000000000000..27b2992a0692 --- /dev/null +++ b/net/bpf/Makefile @@ -0,0 +1 @@ +obj-y := test_run.o diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c new file mode 100644 index 000000000000..8a6d0a37c30c --- /dev/null +++ b/net/bpf/test_run.c @@ -0,0 +1,172 @@ +/* Copyright (c) 2017 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include + +static __always_inline u32 bpf_test_run_one(struct bpf_prog *prog, void *ctx) +{ + u32 ret; + + preempt_disable(); + rcu_read_lock(); + ret = BPF_PROG_RUN(prog, ctx); + rcu_read_unlock(); + preempt_enable(); + + return ret; +} + +static u32 bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, u32 *time) +{ + u64 time_start, time_spent = 0; + u32 ret = 0, i; + + if (!repeat) + repeat = 1; + time_start = ktime_get_ns(); + for (i = 0; i < repeat; i++) { + ret = bpf_test_run_one(prog, ctx); + if (need_resched()) { + if (signal_pending(current)) + break; + time_spent += ktime_get_ns() - time_start; + cond_resched(); + time_start = ktime_get_ns(); + } + } + time_spent += ktime_get_ns() - time_start; + do_div(time_spent, repeat); + *time = time_spent > U32_MAX ? U32_MAX : (u32)time_spent; + + return ret; +} + +static int bpf_test_finish(union bpf_attr __user *uattr, const void *data, + u32 size, u32 retval, u32 duration) +{ + void __user *data_out = u64_to_user_ptr(uattr->test.data_out); + int err = -EFAULT; + + if (data_out && copy_to_user(data_out, data, size)) + goto out; + if (copy_to_user(&uattr->test.data_size_out, &size, sizeof(size))) + goto out; + if (copy_to_user(&uattr->test.retval, &retval, sizeof(retval))) + goto out; + if (copy_to_user(&uattr->test.duration, &duration, sizeof(duration))) + goto out; + err = 0; +out: + return err; +} + +static void *bpf_test_init(const union bpf_attr *kattr, u32 size, + u32 headroom, u32 tailroom) +{ + void __user *data_in = u64_to_user_ptr(kattr->test.data_in); + void *data; + + if (size < ETH_HLEN || size > PAGE_SIZE - headroom - tailroom) + return ERR_PTR(-EINVAL); + + data = kzalloc(size + headroom + tailroom, GFP_USER); + if (!data) + return ERR_PTR(-ENOMEM); + + if (copy_from_user(data + headroom, data_in, size)) { + kfree(data); + return ERR_PTR(-EFAULT); + } + return data; +} + +int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, + union bpf_attr __user *uattr) +{ + bool is_l2 = false, is_direct_pkt_access = false; + u32 size = kattr->test.data_size_in; + u32 repeat = kattr->test.repeat; + u32 retval, duration; + struct sk_buff *skb; + void *data; + int ret; + + data = bpf_test_init(kattr, size, NET_SKB_PAD, + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (prog->type) { + case BPF_PROG_TYPE_SCHED_CLS: + case BPF_PROG_TYPE_SCHED_ACT: + is_l2 = true; + /* fall through */ + case BPF_PROG_TYPE_LWT_IN: + case BPF_PROG_TYPE_LWT_OUT: + case BPF_PROG_TYPE_LWT_XMIT: + is_direct_pkt_access = true; + break; + default: + break; + } + + skb = build_skb(data, 0); + if (!skb) { + kfree(data); + return -ENOMEM; + } + + skb_reserve(skb, NET_SKB_PAD); + __skb_put(skb, size); + skb->protocol = eth_type_trans(skb, current->nsproxy->net_ns->loopback_dev); + skb_reset_network_header(skb); + + if (is_l2) + __skb_push(skb, ETH_HLEN); + if (is_direct_pkt_access) + bpf_compute_data_end(skb); + retval = bpf_test_run(prog, skb, repeat, &duration); + if (!is_l2) + __skb_push(skb, ETH_HLEN); + size = skb->len; + /* bpf program can never convert linear skb to non-linear */ + if (WARN_ON_ONCE(skb_is_nonlinear(skb))) + size = skb_headlen(skb); + ret = bpf_test_finish(uattr, skb->data, size, retval, duration); + kfree_skb(skb); + return ret; +} + +int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, + union bpf_attr __user *uattr) +{ + u32 size = kattr->test.data_size_in; + u32 repeat = kattr->test.repeat; + struct xdp_buff xdp = {}; + u32 retval, duration; + void *data; + int ret; + + data = bpf_test_init(kattr, size, XDP_PACKET_HEADROOM, 0); + if (IS_ERR(data)) + return PTR_ERR(data); + + xdp.data_hard_start = data; + xdp.data = data + XDP_PACKET_HEADROOM; + xdp.data_end = xdp.data + size; + + retval = bpf_test_run(prog, &xdp, repeat, &duration); + if (xdp.data != data + XDP_PACKET_HEADROOM) + size = xdp.data_end - xdp.data; + ret = bpf_test_finish(uattr, xdp.data, size, retval, duration); + kfree(data); + return ret; +} diff --git a/net/core/filter.c b/net/core/filter.c index dfb9f61a2fd5..15e9a81ffebe 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3309,24 +3309,28 @@ static const struct bpf_verifier_ops tc_cls_act_ops = { .is_valid_access = tc_cls_act_is_valid_access, .convert_ctx_access = tc_cls_act_convert_ctx_access, .gen_prologue = tc_cls_act_prologue, + .test_run = bpf_prog_test_run_skb, }; static const struct bpf_verifier_ops xdp_ops = { .get_func_proto = xdp_func_proto, .is_valid_access = xdp_is_valid_access, .convert_ctx_access = xdp_convert_ctx_access, + .test_run = bpf_prog_test_run_xdp, }; static const struct bpf_verifier_ops cg_skb_ops = { .get_func_proto = cg_skb_func_proto, .is_valid_access = sk_filter_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, + .test_run = bpf_prog_test_run_skb, }; static const struct bpf_verifier_ops lwt_inout_ops = { .get_func_proto = lwt_inout_func_proto, .is_valid_access = lwt_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, + .test_run = bpf_prog_test_run_skb, }; static const struct bpf_verifier_ops lwt_xmit_ops = { @@ -3334,6 +3338,7 @@ static const struct bpf_verifier_ops lwt_xmit_ops = { .is_valid_access = lwt_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, .gen_prologue = tc_cls_act_prologue, + .test_run = bpf_prog_test_run_skb, }; static const struct bpf_verifier_ops cg_sock_ops = { -- cgit From 3084887378f5271daedd52cc3372cb8011ad39b6 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Thu, 30 Mar 2017 21:45:39 -0700 Subject: tools/lib/bpf: add support for BPF_PROG_TEST_RUN command add support for BPF_PROG_TEST_RUN command to libbpf.a Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Acked-by: Martin KaFai Lau Acked-by: Wang Nan Signed-off-by: David S. Miller --- tools/include/uapi/linux/bpf.h | 24 ++++++++++++++++++++++++ tools/lib/bpf/bpf.c | 24 ++++++++++++++++++++++++ tools/lib/bpf/bpf.h | 4 +++- 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 1ea08ce35567..a1d95386f562 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -81,6 +81,7 @@ enum bpf_cmd { BPF_OBJ_GET, BPF_PROG_ATTACH, BPF_PROG_DETACH, + BPF_PROG_TEST_RUN, }; enum bpf_map_type { @@ -189,6 +190,17 @@ union bpf_attr { __u32 attach_type; __u32 attach_flags; }; + + struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */ + __u32 prog_fd; + __u32 retval; + __u32 data_size_in; + __u32 data_size_out; + __aligned_u64 data_in; + __aligned_u64 data_out; + __u32 repeat; + __u32 duration; + } test; } __attribute__((aligned(8))); /* BPF helper function descriptions: @@ -459,6 +471,18 @@ union bpf_attr { * Return: * > 0 length of the string including the trailing NUL on success * < 0 error + * + * u64 bpf_bpf_get_socket_cookie(skb) + * Get the cookie for the socket stored inside sk_buff. + * @skb: pointer to skb + * Return: 8 Bytes non-decreasing number on success or 0 if the socket + * field is missing inside sk_buff + * + * u32 bpf_get_socket_uid(skb) + * Get the owner uid of the socket stored inside sk_buff. + * @skb: pointer to skb + * Return: uid of the socket owner on success or 0 if the socket pointer + * inside sk_buff is NULL */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index 9b58d20e8c93..f84c398c11f4 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -209,3 +209,27 @@ int bpf_prog_detach(int target_fd, enum bpf_attach_type type) return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr)); } + +int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size, + void *data_out, __u32 *size_out, __u32 *retval, + __u32 *duration) +{ + union bpf_attr attr; + int ret; + + bzero(&attr, sizeof(attr)); + attr.test.prog_fd = prog_fd; + attr.test.data_in = ptr_to_u64(data); + attr.test.data_out = ptr_to_u64(data_out); + attr.test.data_size_in = size; + attr.test.repeat = repeat; + + ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr)); + if (size_out) + *size_out = attr.test.data_size_out; + if (retval) + *retval = attr.test.retval; + if (duration) + *duration = attr.test.duration; + return ret; +} diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 93f021932623..edb4daeff7a5 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -47,6 +47,8 @@ int bpf_obj_get(const char *pathname); int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type, unsigned int flags); int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type); - +int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size, + void *data_out, __u32 *size_out, __u32 *retval, + __u32 *duration); #endif -- cgit From dd26b7f54a08fd1a9fd804b51b9fce8e16628349 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Thu, 30 Mar 2017 21:45:40 -0700 Subject: tools/lib/bpf: expose bpf_program__set_type() expose bpf_program__set_type() to set program type Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Acked-by: Martin KaFai Lau Signed-off-by: David S. Miller --- tools/lib/bpf/libbpf.c | 3 +-- tools/lib/bpf/libbpf.h | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index ac6eb863b2a4..1a2c07eb7795 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -1618,8 +1618,7 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n) return fd; } -static void bpf_program__set_type(struct bpf_program *prog, - enum bpf_prog_type type) +void bpf_program__set_type(struct bpf_program *prog, enum bpf_prog_type type) { prog->type = type; } diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index b30394f9947a..32c7252f734e 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -25,6 +25,7 @@ #include #include #include // for size_t +#include enum libbpf_errno { __LIBBPF_ERRNO__START = 4000, @@ -185,6 +186,7 @@ int bpf_program__set_sched_cls(struct bpf_program *prog); int bpf_program__set_sched_act(struct bpf_program *prog); int bpf_program__set_xdp(struct bpf_program *prog); int bpf_program__set_perf_event(struct bpf_program *prog); +void bpf_program__set_type(struct bpf_program *prog, enum bpf_prog_type type); bool bpf_program__is_socket_filter(struct bpf_program *prog); bool bpf_program__is_tracepoint(struct bpf_program *prog); -- cgit From 6882804c916beaa945bae90dfd3295e635f6b78f Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Thu, 30 Mar 2017 21:45:41 -0700 Subject: selftests/bpf: add a test for overlapping packet range checks add simple C test case for llvm and verifier range check fix from commit b1977682a385 ("bpf: improve verifier packet range checks") Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Acked-by: Martin KaFai Lau Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/Makefile | 17 +++- tools/testing/selftests/bpf/test_pkt_access.c | 64 ++++++++++++ tools/testing/selftests/bpf/test_progs.c | 138 ++++++++++++++++++++++++++ 3 files changed, 215 insertions(+), 4 deletions(-) create mode 100644 tools/testing/selftests/bpf/test_pkt_access.c create mode 100644 tools/testing/selftests/bpf/test_progs.c diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 6a1ad58cb66f..ff68c9419a67 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -1,16 +1,18 @@ LIBDIR := ../../../lib BPFDIR := $(LIBDIR)/bpf -CFLAGS += -Wall -O2 -I../../../include/uapi -I$(LIBDIR) -LDLIBS += -lcap +CFLAGS += -Wall -O2 -I../../../include/uapi -I$(LIBDIR) -I../../../include +LDLIBS += -lcap -lelf -TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map +TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs + +TEST_GEN_FILES = test_pkt_access.o TEST_PROGS := test_kmod.sh include ../lib.mk -BPFOBJ := $(OUTPUT)/bpf.o +BPFOBJ := $(OUTPUT)/libbpf.a $(TEST_GEN_PROGS): $(BPFOBJ) @@ -21,3 +23,10 @@ force: $(BPFOBJ): force $(MAKE) -C $(BPFDIR) OUTPUT=$(OUTPUT)/ + +CLANG ?= clang + +%.o: %.c + $(CLANG) -I../../../include/uapi -I../../../../samples/bpf/ \ + -D__x86_64__ -Wno-compare-distinct-pointer-types \ + -O2 -target bpf -c $< -o $@ diff --git a/tools/testing/selftests/bpf/test_pkt_access.c b/tools/testing/selftests/bpf/test_pkt_access.c new file mode 100644 index 000000000000..fd1e0832d409 --- /dev/null +++ b/tools/testing/selftests/bpf/test_pkt_access.c @@ -0,0 +1,64 @@ +/* Copyright (c) 2017 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bpf_helpers.h" + +#define _htons __builtin_bswap16 +#define barrier() __asm__ __volatile__("": : :"memory") +int _version SEC("version") = 1; + +SEC("test1") +int process(struct __sk_buff *skb) +{ + void *data_end = (void *)(long)skb->data_end; + void *data = (void *)(long)skb->data; + struct ethhdr *eth = (struct ethhdr *)(data); + struct tcphdr *tcp = NULL; + __u8 proto = 255; + __u64 ihl_len; + + if (eth + 1 > data_end) + return TC_ACT_SHOT; + + if (eth->h_proto == _htons(ETH_P_IP)) { + struct iphdr *iph = (struct iphdr *)(eth + 1); + + if (iph + 1 > data_end) + return TC_ACT_SHOT; + ihl_len = iph->ihl * 4; + proto = iph->protocol; + tcp = (struct tcphdr *)((void *)(iph) + ihl_len); + } else if (eth->h_proto == _htons(ETH_P_IPV6)) { + struct ipv6hdr *ip6h = (struct ipv6hdr *)(eth + 1); + + if (ip6h + 1 > data_end) + return TC_ACT_SHOT; + ihl_len = sizeof(*ip6h); + proto = ip6h->nexthdr; + tcp = (struct tcphdr *)((void *)(ip6h) + ihl_len); + } + + if (tcp) { + if (((void *)(tcp) + 20) > data_end || proto != 6) + return TC_ACT_SHOT; + barrier(); /* to force ordering of checks */ + if (((void *)(tcp) + 18) > data_end) + return TC_ACT_SHOT; + if (tcp->urg_ptr == 123) + return TC_ACT_OK; + } + + return TC_ACT_UNSPEC; +} diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c new file mode 100644 index 000000000000..1d9a310e71e5 --- /dev/null +++ b/tools/testing/selftests/bpf/test_progs.c @@ -0,0 +1,138 @@ +/* Copyright (c) 2017 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include + +#include +typedef __u16 __sum16; +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#define _htons __builtin_bswap16 + +static int error_cnt, pass_cnt; + +/* ipv4 test vector */ +static struct { + struct ethhdr eth; + struct iphdr iph; + struct tcphdr tcp; +} __packed pkt_v4 = { + .eth.h_proto = _htons(ETH_P_IP), + .iph.ihl = 5, + .iph.protocol = 6, + .tcp.urg_ptr = 123, +}; + +/* ipv6 test vector */ +static struct { + struct ethhdr eth; + struct ipv6hdr iph; + struct tcphdr tcp; +} __packed pkt_v6 = { + .eth.h_proto = _htons(ETH_P_IPV6), + .iph.nexthdr = 6, + .tcp.urg_ptr = 123, +}; + +#define CHECK(condition, tag, format...) ({ \ + int __ret = !!(condition); \ + if (__ret) { \ + error_cnt++; \ + printf("%s:FAIL:%s ", __func__, tag); \ + printf(format); \ + } else { \ + pass_cnt++; \ + printf("%s:PASS:%s %d nsec\n", __func__, tag, duration);\ + } \ +}) + +static int bpf_prog_load(const char *file, enum bpf_prog_type type, + struct bpf_object **pobj, int *prog_fd) +{ + struct bpf_program *prog; + struct bpf_object *obj; + int err; + + obj = bpf_object__open(file); + if (IS_ERR(obj)) { + error_cnt++; + return -ENOENT; + } + + prog = bpf_program__next(NULL, obj); + if (!prog) { + bpf_object__close(obj); + error_cnt++; + return -ENOENT; + } + + bpf_program__set_type(prog, type); + err = bpf_object__load(obj); + if (err) { + bpf_object__close(obj); + error_cnt++; + return -EINVAL; + } + + *pobj = obj; + *prog_fd = bpf_program__fd(prog); + return 0; +} + +static void test_pkt_access(void) +{ + const char *file = "./test_pkt_access.o"; + struct bpf_object *obj; + __u32 duration, retval; + int err, prog_fd; + + err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); + if (err) + return; + + err = bpf_prog_test_run(prog_fd, 100000, &pkt_v4, sizeof(pkt_v4), + NULL, NULL, &retval, &duration); + CHECK(err || errno || retval, "ipv4", + "err %d errno %d retval %d duration %d\n", + err, errno, retval, duration); + + err = bpf_prog_test_run(prog_fd, 100000, &pkt_v6, sizeof(pkt_v6), + NULL, NULL, &retval, &duration); + CHECK(err || errno || retval, "ipv6", + "err %d errno %d retval %d duration %d\n", + err, errno, retval, duration); + bpf_object__close(obj); +} + +int main(void) +{ + struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; + + setrlimit(RLIMIT_MEMLOCK, &rinf); + + test_pkt_access(); + + printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt); + return 0; +} -- cgit From 8d48f5e427923fa6f6482c716082d310b7f3bcd5 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Thu, 30 Mar 2017 21:45:42 -0700 Subject: selftests/bpf: add a test for basic XDP functionality add C test for xdp_adjust_head(), packet rewrite and map lookups Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Acked-by: Martin KaFai Lau Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/Makefile | 2 +- tools/testing/selftests/bpf/test_iptunnel_common.h | 37 ++++ tools/testing/selftests/bpf/test_progs.c | 58 +++++ tools/testing/selftests/bpf/test_xdp.c | 236 +++++++++++++++++++++ 4 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/test_iptunnel_common.h create mode 100644 tools/testing/selftests/bpf/test_xdp.c diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index ff68c9419a67..76cbe1d42dda 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -6,7 +6,7 @@ LDLIBS += -lcap -lelf TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs -TEST_GEN_FILES = test_pkt_access.o +TEST_GEN_FILES = test_pkt_access.o test_xdp.o TEST_PROGS := test_kmod.sh diff --git a/tools/testing/selftests/bpf/test_iptunnel_common.h b/tools/testing/selftests/bpf/test_iptunnel_common.h new file mode 100644 index 000000000000..e4cd252a1b20 --- /dev/null +++ b/tools/testing/selftests/bpf/test_iptunnel_common.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2016 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#ifndef _TEST_IPTNL_COMMON_H +#define _TEST_IPTNL_COMMON_H + +#include + +#define MAX_IPTNL_ENTRIES 256U + +struct vip { + union { + __u32 v6[4]; + __u32 v4; + } daddr; + __u16 dport; + __u16 family; + __u8 protocol; +}; + +struct iptnl_info { + union { + __u32 v6[4]; + __u32 v4; + } saddr; + union { + __u32 v6[4]; + __u32 v4; + } daddr; + __u16 family; + __u8 dmac[6]; +}; + +#endif diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index 1d9a310e71e5..defcb273242e 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -27,6 +27,7 @@ typedef __u16 __sum16; #include #include #include +#include "test_iptunnel_common.h" #define _htons __builtin_bswap16 @@ -100,6 +101,20 @@ static int bpf_prog_load(const char *file, enum bpf_prog_type type, return 0; } +static int bpf_find_map(const char *test, struct bpf_object *obj, + const char *name) +{ + struct bpf_map *map; + + map = bpf_object__find_map_by_name(obj, name); + if (!map) { + printf("%s:FAIL:map '%s' not found\n", test, name); + error_cnt++; + return -1; + } + return bpf_map__fd(map); +} + static void test_pkt_access(void) { const char *file = "./test_pkt_access.o"; @@ -125,6 +140,48 @@ static void test_pkt_access(void) bpf_object__close(obj); } +static void test_xdp(void) +{ + struct vip key4 = {.protocol = 6, .family = AF_INET}; + struct vip key6 = {.protocol = 6, .family = AF_INET6}; + struct iptnl_info value4 = {.family = AF_INET}; + struct iptnl_info value6 = {.family = AF_INET6}; + const char *file = "./test_xdp.o"; + struct bpf_object *obj; + char buf[128]; + struct ipv6hdr *iph6 = (void *)buf + sizeof(struct ethhdr); + struct iphdr *iph = (void *)buf + sizeof(struct ethhdr); + __u32 duration, retval, size; + int err, prog_fd, map_fd; + + err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); + if (err) + return; + + map_fd = bpf_find_map(__func__, obj, "vip2tnl"); + if (map_fd < 0) + goto out; + bpf_map_update_elem(map_fd, &key4, &value4, 0); + bpf_map_update_elem(map_fd, &key6, &value6, 0); + + err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), + buf, &size, &retval, &duration); + + CHECK(err || errno || retval != XDP_TX || size != 74 || + iph->protocol != IPPROTO_IPIP, "ipv4", + "err %d errno %d retval %d size %d\n", + err, errno, retval, size); + + err = bpf_prog_test_run(prog_fd, 1, &pkt_v6, sizeof(pkt_v6), + buf, &size, &retval, &duration); + CHECK(err || errno || retval != XDP_TX || size != 114 || + iph6->nexthdr != IPPROTO_IPV6, "ipv6", + "err %d errno %d retval %d size %d\n", + err, errno, retval, size); +out: + bpf_object__close(obj); +} + int main(void) { struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; @@ -132,6 +189,7 @@ int main(void) setrlimit(RLIMIT_MEMLOCK, &rinf); test_pkt_access(); + test_xdp(); printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt); return 0; diff --git a/tools/testing/selftests/bpf/test_xdp.c b/tools/testing/selftests/bpf/test_xdp.c new file mode 100644 index 000000000000..9a33b038cb31 --- /dev/null +++ b/tools/testing/selftests/bpf/test_xdp.c @@ -0,0 +1,236 @@ +/* Copyright (c) 2016,2017 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bpf_helpers.h" +#include "test_iptunnel_common.h" + +#define htons __builtin_bswap16 +#define ntohs __builtin_bswap16 +int _version SEC("version") = 1; + +struct bpf_map_def SEC("maps") rxcnt = { + .type = BPF_MAP_TYPE_PERCPU_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(__u64), + .max_entries = 256, +}; + +struct bpf_map_def SEC("maps") vip2tnl = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(struct vip), + .value_size = sizeof(struct iptnl_info), + .max_entries = MAX_IPTNL_ENTRIES, +}; + +static __always_inline void count_tx(__u32 protocol) +{ + __u64 *rxcnt_count; + + rxcnt_count = bpf_map_lookup_elem(&rxcnt, &protocol); + if (rxcnt_count) + *rxcnt_count += 1; +} + +static __always_inline int get_dport(void *trans_data, void *data_end, + __u8 protocol) +{ + struct tcphdr *th; + struct udphdr *uh; + + switch (protocol) { + case IPPROTO_TCP: + th = (struct tcphdr *)trans_data; + if (th + 1 > data_end) + return -1; + return th->dest; + case IPPROTO_UDP: + uh = (struct udphdr *)trans_data; + if (uh + 1 > data_end) + return -1; + return uh->dest; + default: + return 0; + } +} + +static __always_inline void set_ethhdr(struct ethhdr *new_eth, + const struct ethhdr *old_eth, + const struct iptnl_info *tnl, + __be16 h_proto) +{ + memcpy(new_eth->h_source, old_eth->h_dest, sizeof(new_eth->h_source)); + memcpy(new_eth->h_dest, tnl->dmac, sizeof(new_eth->h_dest)); + new_eth->h_proto = h_proto; +} + +static __always_inline int handle_ipv4(struct xdp_md *xdp) +{ + void *data_end = (void *)(long)xdp->data_end; + void *data = (void *)(long)xdp->data; + struct iptnl_info *tnl; + struct ethhdr *new_eth; + struct ethhdr *old_eth; + struct iphdr *iph = data + sizeof(struct ethhdr); + __u16 *next_iph; + __u16 payload_len; + struct vip vip = {}; + int dport; + __u32 csum = 0; + int i; + + if (iph + 1 > data_end) + return XDP_DROP; + + dport = get_dport(iph + 1, data_end, iph->protocol); + if (dport == -1) + return XDP_DROP; + + vip.protocol = iph->protocol; + vip.family = AF_INET; + vip.daddr.v4 = iph->daddr; + vip.dport = dport; + payload_len = ntohs(iph->tot_len); + + tnl = bpf_map_lookup_elem(&vip2tnl, &vip); + /* It only does v4-in-v4 */ + if (!tnl || tnl->family != AF_INET) + return XDP_PASS; + + if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct iphdr))) + return XDP_DROP; + + data = (void *)(long)xdp->data; + data_end = (void *)(long)xdp->data_end; + + new_eth = data; + iph = data + sizeof(*new_eth); + old_eth = data + sizeof(*iph); + + if (new_eth + 1 > data_end || + old_eth + 1 > data_end || + iph + 1 > data_end) + return XDP_DROP; + + set_ethhdr(new_eth, old_eth, tnl, htons(ETH_P_IP)); + + iph->version = 4; + iph->ihl = sizeof(*iph) >> 2; + iph->frag_off = 0; + iph->protocol = IPPROTO_IPIP; + iph->check = 0; + iph->tos = 0; + iph->tot_len = htons(payload_len + sizeof(*iph)); + iph->daddr = tnl->daddr.v4; + iph->saddr = tnl->saddr.v4; + iph->ttl = 8; + + next_iph = (__u16 *)iph; +#pragma clang loop unroll(full) + for (i = 0; i < sizeof(*iph) >> 1; i++) + csum += *next_iph++; + + iph->check = ~((csum & 0xffff) + (csum >> 16)); + + count_tx(vip.protocol); + + return XDP_TX; +} + +static __always_inline int handle_ipv6(struct xdp_md *xdp) +{ + void *data_end = (void *)(long)xdp->data_end; + void *data = (void *)(long)xdp->data; + struct iptnl_info *tnl; + struct ethhdr *new_eth; + struct ethhdr *old_eth; + struct ipv6hdr *ip6h = data + sizeof(struct ethhdr); + __u16 payload_len; + struct vip vip = {}; + int dport; + + if (ip6h + 1 > data_end) + return XDP_DROP; + + dport = get_dport(ip6h + 1, data_end, ip6h->nexthdr); + if (dport == -1) + return XDP_DROP; + + vip.protocol = ip6h->nexthdr; + vip.family = AF_INET6; + memcpy(vip.daddr.v6, ip6h->daddr.s6_addr32, sizeof(vip.daddr)); + vip.dport = dport; + payload_len = ip6h->payload_len; + + tnl = bpf_map_lookup_elem(&vip2tnl, &vip); + /* It only does v6-in-v6 */ + if (!tnl || tnl->family != AF_INET6) + return XDP_PASS; + + if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct ipv6hdr))) + return XDP_DROP; + + data = (void *)(long)xdp->data; + data_end = (void *)(long)xdp->data_end; + + new_eth = data; + ip6h = data + sizeof(*new_eth); + old_eth = data + sizeof(*ip6h); + + if (new_eth + 1 > data_end || old_eth + 1 > data_end || + ip6h + 1 > data_end) + return XDP_DROP; + + set_ethhdr(new_eth, old_eth, tnl, htons(ETH_P_IPV6)); + + ip6h->version = 6; + ip6h->priority = 0; + memset(ip6h->flow_lbl, 0, sizeof(ip6h->flow_lbl)); + ip6h->payload_len = htons(ntohs(payload_len) + sizeof(*ip6h)); + ip6h->nexthdr = IPPROTO_IPV6; + ip6h->hop_limit = 8; + memcpy(ip6h->saddr.s6_addr32, tnl->saddr.v6, sizeof(tnl->saddr.v6)); + memcpy(ip6h->daddr.s6_addr32, tnl->daddr.v6, sizeof(tnl->daddr.v6)); + + count_tx(vip.protocol); + + return XDP_TX; +} + +SEC("xdp_tx_iptunnel") +int _xdp_tx_iptunnel(struct xdp_md *xdp) +{ + void *data_end = (void *)(long)xdp->data_end; + void *data = (void *)(long)xdp->data; + struct ethhdr *eth = data; + __u16 h_proto; + + if (eth + 1 > data_end) + return XDP_DROP; + + h_proto = eth->h_proto; + + if (h_proto == htons(ETH_P_IP)) + return handle_ipv4(xdp); + else if (h_proto == htons(ETH_P_IPV6)) + + return handle_ipv6(xdp); + else + return XDP_DROP; +} + +char _license[] SEC("license") = "GPL"; -- cgit From 37821613626e99828491302c3a59a094ec427395 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Thu, 30 Mar 2017 21:45:43 -0700 Subject: selftests/bpf: add l4 load balancer test based on sched_cls this l4lb demo is a comprehensive test case for LLVM codegen and kernel verifier. It's using fully inlined jhash(), complex packet parsing and multiple map lookups of different types to stress llvm and verifier. The map sizes, map population and test vectors are artificial to exercise different paths through the bpf program. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Acked-by: Martin KaFai Lau Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/Makefile | 2 +- tools/testing/selftests/bpf/test_l4lb.c | 474 +++++++++++++++++++++++++++++++ tools/testing/selftests/bpf/test_progs.c | 88 ++++++ 3 files changed, 563 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/test_l4lb.c diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 76cbe1d42dda..32fb7a294f0f 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -6,7 +6,7 @@ LDLIBS += -lcap -lelf TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs -TEST_GEN_FILES = test_pkt_access.o test_xdp.o +TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o TEST_PROGS := test_kmod.sh diff --git a/tools/testing/selftests/bpf/test_l4lb.c b/tools/testing/selftests/bpf/test_l4lb.c new file mode 100644 index 000000000000..368bfe8b9842 --- /dev/null +++ b/tools/testing/selftests/bpf/test_l4lb.c @@ -0,0 +1,474 @@ +/* Copyright (c) 2017 Facebook + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bpf_helpers.h" +#include "test_iptunnel_common.h" + +#define htons __builtin_bswap16 +#define ntohs __builtin_bswap16 +int _version SEC("version") = 1; + +static inline __u32 rol32(__u32 word, unsigned int shift) +{ + return (word << shift) | (word >> ((-shift) & 31)); +} + +/* copy paste of jhash from kernel sources to make sure llvm + * can compile it into valid sequence of bpf instructions + */ +#define __jhash_mix(a, b, c) \ +{ \ + a -= c; a ^= rol32(c, 4); c += b; \ + b -= a; b ^= rol32(a, 6); a += c; \ + c -= b; c ^= rol32(b, 8); b += a; \ + a -= c; a ^= rol32(c, 16); c += b; \ + b -= a; b ^= rol32(a, 19); a += c; \ + c -= b; c ^= rol32(b, 4); b += a; \ +} + +#define __jhash_final(a, b, c) \ +{ \ + c ^= b; c -= rol32(b, 14); \ + a ^= c; a -= rol32(c, 11); \ + b ^= a; b -= rol32(a, 25); \ + c ^= b; c -= rol32(b, 16); \ + a ^= c; a -= rol32(c, 4); \ + b ^= a; b -= rol32(a, 14); \ + c ^= b; c -= rol32(b, 24); \ +} + +#define JHASH_INITVAL 0xdeadbeef + +typedef unsigned int u32; + +static inline u32 jhash(const void *key, u32 length, u32 initval) +{ + u32 a, b, c; + const unsigned char *k = key; + + a = b = c = JHASH_INITVAL + length + initval; + + while (length > 12) { + a += *(u32 *)(k); + b += *(u32 *)(k + 4); + c += *(u32 *)(k + 8); + __jhash_mix(a, b, c); + length -= 12; + k += 12; + } + switch (length) { + case 12: c += (u32)k[11]<<24; + case 11: c += (u32)k[10]<<16; + case 10: c += (u32)k[9]<<8; + case 9: c += k[8]; + case 8: b += (u32)k[7]<<24; + case 7: b += (u32)k[6]<<16; + case 6: b += (u32)k[5]<<8; + case 5: b += k[4]; + case 4: a += (u32)k[3]<<24; + case 3: a += (u32)k[2]<<16; + case 2: a += (u32)k[1]<<8; + case 1: a += k[0]; + __jhash_final(a, b, c); + case 0: /* Nothing left to add */ + break; + } + + return c; +} + +static inline u32 __jhash_nwords(u32 a, u32 b, u32 c, u32 initval) +{ + a += initval; + b += initval; + c += initval; + __jhash_final(a, b, c); + return c; +} + +static inline u32 jhash_2words(u32 a, u32 b, u32 initval) +{ + return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2)); +} + +#define PCKT_FRAGMENTED 65343 +#define IPV4_HDR_LEN_NO_OPT 20 +#define IPV4_PLUS_ICMP_HDR 28 +#define IPV6_PLUS_ICMP_HDR 48 +#define RING_SIZE 2 +#define MAX_VIPS 12 +#define MAX_REALS 5 +#define CTL_MAP_SIZE 16 +#define CH_RINGS_SIZE (MAX_VIPS * RING_SIZE) +#define F_IPV6 (1 << 0) +#define F_HASH_NO_SRC_PORT (1 << 0) +#define F_ICMP (1 << 0) +#define F_SYN_SET (1 << 1) + +struct packet_description { + union { + __be32 src; + __be32 srcv6[4]; + }; + union { + __be32 dst; + __be32 dstv6[4]; + }; + union { + __u32 ports; + __u16 port16[2]; + }; + __u8 proto; + __u8 flags; +}; + +struct ctl_value { + union { + __u64 value; + __u32 ifindex; + __u8 mac[6]; + }; +}; + +struct vip_meta { + __u32 flags; + __u32 vip_num; +}; + +struct real_definition { + union { + __be32 dst; + __be32 dstv6[4]; + }; + __u8 flags; +}; + +struct vip_stats { + __u64 bytes; + __u64 pkts; +}; + +struct eth_hdr { + unsigned char eth_dest[ETH_ALEN]; + unsigned char eth_source[ETH_ALEN]; + unsigned short eth_proto; +}; + +struct bpf_map_def SEC("maps") vip_map = { + .type = BPF_MAP_TYPE_HASH, + .key_size = sizeof(struct vip), + .value_size = sizeof(struct vip_meta), + .max_entries = MAX_VIPS, +}; + +struct bpf_map_def SEC("maps") ch_rings = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(__u32), + .max_entries = CH_RINGS_SIZE, +}; + +struct bpf_map_def SEC("maps") reals = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(struct real_definition), + .max_entries = MAX_REALS, +}; + +struct bpf_map_def SEC("maps") stats = { + .type = BPF_MAP_TYPE_PERCPU_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(struct vip_stats), + .max_entries = MAX_VIPS, +}; + +struct bpf_map_def SEC("maps") ctl_array = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(struct ctl_value), + .max_entries = CTL_MAP_SIZE, +}; + +static __always_inline __u32 get_packet_hash(struct packet_description *pckt, + bool ipv6) +{ + if (ipv6) + return jhash_2words(jhash(pckt->srcv6, 16, MAX_VIPS), + pckt->ports, CH_RINGS_SIZE); + else + return jhash_2words(pckt->src, pckt->ports, CH_RINGS_SIZE); +} + +static __always_inline bool get_packet_dst(struct real_definition **real, + struct packet_description *pckt, + struct vip_meta *vip_info, + bool is_ipv6) +{ + __u32 hash = get_packet_hash(pckt, is_ipv6) % RING_SIZE; + __u32 key = RING_SIZE * vip_info->vip_num + hash; + __u32 *real_pos; + + real_pos = bpf_map_lookup_elem(&ch_rings, &key); + if (!real_pos) + return false; + key = *real_pos; + *real = bpf_map_lookup_elem(&reals, &key); + if (!(*real)) + return false; + return true; +} + +static __always_inline int parse_icmpv6(void *data, void *data_end, __u64 off, + struct packet_description *pckt) +{ + struct icmp6hdr *icmp_hdr; + struct ipv6hdr *ip6h; + + icmp_hdr = data + off; + if (icmp_hdr + 1 > data_end) + return TC_ACT_SHOT; + if (icmp_hdr->icmp6_type != ICMPV6_PKT_TOOBIG) + return TC_ACT_OK; + off += sizeof(struct icmp6hdr); + ip6h = data + off; + if (ip6h + 1 > data_end) + return TC_ACT_SHOT; + pckt->proto = ip6h->nexthdr; + pckt->flags |= F_ICMP; + memcpy(pckt->srcv6, ip6h->daddr.s6_addr32, 16); + memcpy(pckt->dstv6, ip6h->saddr.s6_addr32, 16); + return TC_ACT_UNSPEC; +} + +static __always_inline int parse_icmp(void *data, void *data_end, __u64 off, + struct packet_description *pckt) +{ + struct icmphdr *icmp_hdr; + struct iphdr *iph; + + icmp_hdr = data + off; + if (icmp_hdr + 1 > data_end) + return TC_ACT_SHOT; + if (icmp_hdr->type != ICMP_DEST_UNREACH || + icmp_hdr->code != ICMP_FRAG_NEEDED) + return TC_ACT_OK; + off += sizeof(struct icmphdr); + iph = data + off; + if (iph + 1 > data_end) + return TC_ACT_SHOT; + if (iph->ihl != 5) + return TC_ACT_SHOT; + pckt->proto = iph->protocol; + pckt->flags |= F_ICMP; + pckt->src = iph->daddr; + pckt->dst = iph->saddr; + return TC_ACT_UNSPEC; +} + +static __always_inline bool parse_udp(void *data, __u64 off, void *data_end, + struct packet_description *pckt) +{ + struct udphdr *udp; + udp = data + off; + + if (udp + 1 > data_end) + return false; + + if (!(pckt->flags & F_ICMP)) { + pckt->port16[0] = udp->source; + pckt->port16[1] = udp->dest; + } else { + pckt->port16[0] = udp->dest; + pckt->port16[1] = udp->source; + } + return true; +} + +static __always_inline bool parse_tcp(void *data, __u64 off, void *data_end, + struct packet_description *pckt) +{ + struct tcphdr *tcp; + + tcp = data + off; + if (tcp + 1 > data_end) + return false; + + if (tcp->syn) + pckt->flags |= F_SYN_SET; + + if (!(pckt->flags & F_ICMP)) { + pckt->port16[0] = tcp->source; + pckt->port16[1] = tcp->dest; + } else { + pckt->port16[0] = tcp->dest; + pckt->port16[1] = tcp->source; + } + return true; +} + +static __always_inline int process_packet(void *data, __u64 off, void *data_end, + bool is_ipv6, struct __sk_buff *skb) +{ + void *pkt_start = (void *)(long)skb->data; + struct packet_description pckt = {}; + struct eth_hdr *eth = pkt_start; + struct bpf_tunnel_key tkey = {}; + struct vip_stats *data_stats; + struct real_definition *dst; + struct vip_meta *vip_info; + struct ctl_value *cval; + __u32 v4_intf_pos = 1; + __u32 v6_intf_pos = 2; + struct ipv6hdr *ip6h; + struct vip vip = {}; + struct iphdr *iph; + int tun_flag = 0; + __u16 pkt_bytes; + __u64 iph_len; + __u32 ifindex; + __u8 protocol; + __u32 vip_num; + int action; + + tkey.tunnel_ttl = 64; + if (is_ipv6) { + ip6h = data + off; + if (ip6h + 1 > data_end) + return TC_ACT_SHOT; + + iph_len = sizeof(struct ipv6hdr); + protocol = ip6h->nexthdr; + pckt.proto = protocol; + pkt_bytes = ntohs(ip6h->payload_len); + off += iph_len; + if (protocol == IPPROTO_FRAGMENT) { + return TC_ACT_SHOT; + } else if (protocol == IPPROTO_ICMPV6) { + action = parse_icmpv6(data, data_end, off, &pckt); + if (action >= 0) + return action; + off += IPV6_PLUS_ICMP_HDR; + } else { + memcpy(pckt.srcv6, ip6h->saddr.s6_addr32, 16); + memcpy(pckt.dstv6, ip6h->daddr.s6_addr32, 16); + } + } else { + iph = data + off; + if (iph + 1 > data_end) + return TC_ACT_SHOT; + if (iph->ihl != 5) + return TC_ACT_SHOT; + + protocol = iph->protocol; + pckt.proto = protocol; + pkt_bytes = ntohs(iph->tot_len); + off += IPV4_HDR_LEN_NO_OPT; + + if (iph->frag_off & PCKT_FRAGMENTED) + return TC_ACT_SHOT; + if (protocol == IPPROTO_ICMP) { + action = parse_icmp(data, data_end, off, &pckt); + if (action >= 0) + return action; + off += IPV4_PLUS_ICMP_HDR; + } else { + pckt.src = iph->saddr; + pckt.dst = iph->daddr; + } + } + protocol = pckt.proto; + + if (protocol == IPPROTO_TCP) { + if (!parse_tcp(data, off, data_end, &pckt)) + return TC_ACT_SHOT; + } else if (protocol == IPPROTO_UDP) { + if (!parse_udp(data, off, data_end, &pckt)) + return TC_ACT_SHOT; + } else { + return TC_ACT_SHOT; + } + + if (is_ipv6) + memcpy(vip.daddr.v6, pckt.dstv6, 16); + else + vip.daddr.v4 = pckt.dst; + + vip.dport = pckt.port16[1]; + vip.protocol = pckt.proto; + vip_info = bpf_map_lookup_elem(&vip_map, &vip); + if (!vip_info) { + vip.dport = 0; + vip_info = bpf_map_lookup_elem(&vip_map, &vip); + if (!vip_info) + return TC_ACT_SHOT; + pckt.port16[1] = 0; + } + + if (vip_info->flags & F_HASH_NO_SRC_PORT) + pckt.port16[0] = 0; + + if (!get_packet_dst(&dst, &pckt, vip_info, is_ipv6)) + return TC_ACT_SHOT; + + if (dst->flags & F_IPV6) { + cval = bpf_map_lookup_elem(&ctl_array, &v6_intf_pos); + if (!cval) + return TC_ACT_SHOT; + ifindex = cval->ifindex; + memcpy(tkey.remote_ipv6, dst->dstv6, 16); + tun_flag = BPF_F_TUNINFO_IPV6; + } else { + cval = bpf_map_lookup_elem(&ctl_array, &v4_intf_pos); + if (!cval) + return TC_ACT_SHOT; + ifindex = cval->ifindex; + tkey.remote_ipv4 = dst->dst; + } + vip_num = vip_info->vip_num; + data_stats = bpf_map_lookup_elem(&stats, &vip_num); + if (!data_stats) + return TC_ACT_SHOT; + data_stats->pkts++; + data_stats->bytes += pkt_bytes; + bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), tun_flag); + *(u32 *)eth->eth_dest = tkey.remote_ipv4; + return bpf_redirect(ifindex, 0); +} + +SEC("l4lb-demo") +int balancer_ingress(struct __sk_buff *ctx) +{ + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct eth_hdr *eth = data; + __u32 eth_proto; + __u32 nh_off; + + nh_off = sizeof(struct eth_hdr); + if (data + nh_off > data_end) + return TC_ACT_SHOT; + eth_proto = eth->eth_proto; + if (eth_proto == htons(ETH_P_IP)) + return process_packet(data, nh_off, data_end, false, ctx); + else if (eth_proto == htons(ETH_P_IPV6)) + return process_packet(data, nh_off, data_end, true, ctx); + else + return TC_ACT_SHOT; +} +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index defcb273242e..5275d4a1df24 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -28,11 +28,14 @@ typedef __u16 __sum16; #include #include #include "test_iptunnel_common.h" +#include "bpf_util.h" #define _htons __builtin_bswap16 static int error_cnt, pass_cnt; +#define MAGIC_BYTES 123 + /* ipv4 test vector */ static struct { struct ethhdr eth; @@ -42,6 +45,7 @@ static struct { .eth.h_proto = _htons(ETH_P_IP), .iph.ihl = 5, .iph.protocol = 6, + .iph.tot_len = _htons(MAGIC_BYTES), .tcp.urg_ptr = 123, }; @@ -53,6 +57,7 @@ static struct { } __packed pkt_v6 = { .eth.h_proto = _htons(ETH_P_IPV6), .iph.nexthdr = 6, + .iph.payload_len = _htons(MAGIC_BYTES), .tcp.urg_ptr = 123, }; @@ -182,6 +187,88 @@ out: bpf_object__close(obj); } +#define MAGIC_VAL 0x1234 +#define NUM_ITER 100000 +#define VIP_NUM 5 + +static void test_l4lb(void) +{ + unsigned int nr_cpus = bpf_num_possible_cpus(); + const char *file = "./test_l4lb.o"; + struct vip key = {.protocol = 6}; + struct vip_meta { + __u32 flags; + __u32 vip_num; + } value = {.vip_num = VIP_NUM}; + __u32 stats_key = VIP_NUM; + struct vip_stats { + __u64 bytes; + __u64 pkts; + } stats[nr_cpus]; + struct real_definition { + union { + __be32 dst; + __be32 dstv6[4]; + }; + __u8 flags; + } real_def = {.dst = MAGIC_VAL}; + __u32 ch_key = 11, real_num = 3; + __u32 duration, retval, size; + int err, i, prog_fd, map_fd; + __u64 bytes = 0, pkts = 0; + struct bpf_object *obj; + char buf[128]; + u32 *magic = (u32 *)buf; + + err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); + if (err) + return; + + map_fd = bpf_find_map(__func__, obj, "vip_map"); + if (map_fd < 0) + goto out; + bpf_map_update_elem(map_fd, &key, &value, 0); + + map_fd = bpf_find_map(__func__, obj, "ch_rings"); + if (map_fd < 0) + goto out; + bpf_map_update_elem(map_fd, &ch_key, &real_num, 0); + + map_fd = bpf_find_map(__func__, obj, "reals"); + if (map_fd < 0) + goto out; + bpf_map_update_elem(map_fd, &real_num, &real_def, 0); + + err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v4, sizeof(pkt_v4), + buf, &size, &retval, &duration); + CHECK(err || errno || retval != 7/*TC_ACT_REDIRECT*/ || size != 54 || + *magic != MAGIC_VAL, "ipv4", + "err %d errno %d retval %d size %d magic %x\n", + err, errno, retval, size, *magic); + + err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v6, sizeof(pkt_v6), + buf, &size, &retval, &duration); + CHECK(err || errno || retval != 7/*TC_ACT_REDIRECT*/ || size != 74 || + *magic != MAGIC_VAL, "ipv6", + "err %d errno %d retval %d size %d magic %x\n", + err, errno, retval, size, *magic); + + map_fd = bpf_find_map(__func__, obj, "stats"); + if (map_fd < 0) + goto out; + bpf_map_lookup_elem(map_fd, &stats_key, stats); + for (i = 0; i < nr_cpus; i++) { + bytes += stats[i].bytes; + pkts += stats[i].pkts; + } + if (bytes != MAGIC_BYTES * NUM_ITER * 2 || pkts != NUM_ITER * 2) { + error_cnt++; + printf("test_l4lb:FAIL:stats %lld %lld\n", bytes, pkts); + } +out: + bpf_object__close(obj); +} + int main(void) { struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; @@ -190,6 +277,7 @@ int main(void) test_pkt_access(); test_xdp(); + test_l4lb(); printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt); return 0; -- cgit From 9728ee92f740acc65d54e6c62b9124cd0676cba9 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Sat, 4 Feb 2017 10:15:22 +0900 Subject: nfc: Add support RC-S380P to port100 Signed-off-by: OGAWA Hirofumi Signed-off-by: Samuel Ortiz --- drivers/nfc/port100.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 2b2330b235e6..32258525513e 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -21,8 +21,9 @@ #define VERSION "0.1" -#define SONY_VENDOR_ID 0x054c -#define RCS380_PRODUCT_ID 0x06c1 +#define SONY_VENDOR_ID 0x054c +#define RCS380S_PRODUCT_ID 0x06c1 +#define RCS380P_PRODUCT_ID 0x06c3 #define PORT100_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ NFC_PROTO_MIFARE_MASK | \ @@ -1477,7 +1478,8 @@ static struct nfc_digital_ops port100_digital_ops = { }; static const struct usb_device_id port100_table[] = { - { USB_DEVICE(SONY_VENDOR_ID, RCS380_PRODUCT_ID), }, + { USB_DEVICE(SONY_VENDOR_ID, RCS380S_PRODUCT_ID), }, + { USB_DEVICE(SONY_VENDOR_ID, RCS380P_PRODUCT_ID), }, { } }; MODULE_DEVICE_TABLE(usb, port100_table); -- cgit From 85a2566d70a851c85806f687b93a9559bb8a3b11 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Sat, 4 Feb 2017 10:15:55 +0900 Subject: nfc: Send same info for both of NFC_CMD_GET_DEVICE and NFC_EVENT_DEVICE_ADDED Now, NFC_EVENT_DEVICE_ADDED doesn't send NFC_ATTR_RF_MODE. But NFC_CMD_GET_DEVICE send. To get NFC_ATTR_RF_MODE, we have to call NFC_CMD_GET_DEVICE just for NFC_ATTR_RF_MODE when get NFC_EVENT_DEVICE_ADDED. This fixes those inconsistent. Signed-off-by: OGAWA Hirofumi Signed-off-by: Samuel Ortiz --- net/nfc/netlink.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 03f3d5c7beb8..4c95319dc22b 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -303,6 +303,17 @@ free_msg: return -EMSGSIZE; } +static int nfc_genl_setup_device_added(struct nfc_dev *dev, struct sk_buff *msg) +{ + if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) || + nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || + nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) || + nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) || + nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode)) + return -1; + return 0; +} + int nfc_genl_device_added(struct nfc_dev *dev) { struct sk_buff *msg; @@ -317,10 +328,7 @@ int nfc_genl_device_added(struct nfc_dev *dev) if (!hdr) goto free_msg; - if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) || - nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || - nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) || - nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up)) + if (nfc_genl_setup_device_added(dev, msg)) goto nla_put_failure; genlmsg_end(msg, hdr); @@ -596,11 +604,7 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev, if (cb) genl_dump_check_consistent(cb, hdr, &nfc_genl_family); - if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) || - nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || - nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) || - nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) || - nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode)) + if (nfc_genl_setup_device_added(dev, msg)) goto nla_put_failure; genlmsg_end(msg, hdr); -- cgit From 0ada076819529203e11fcd5d3d52a2c9ada21879 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Sat, 4 Feb 2017 10:16:28 +0900 Subject: nfc: Fix RC-S380* needs zero-length packet If sent packet size is wMaxPacketSize boundary, this device doesn't answer. To fix this, we have to send zero-length packet in usb spec. Signed-off-by: OGAWA Hirofumi Signed-off-by: Samuel Ortiz --- drivers/nfc/port100.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 32258525513e..382985d3fee0 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -1540,6 +1540,7 @@ static int port100_probe(struct usb_interface *interface, usb_fill_bulk_urb(dev->out_urb, dev->udev, usb_sndbulkpipe(dev->udev, out_endpoint), NULL, 0, port100_send_complete, dev); + dev->out_urb->transfer_flags = URB_ZERO_PACKET; dev->skb_headroom = PORT100_FRAME_HEADER_LEN + PORT100_COMM_RF_HEAD_MAX_LEN; -- cgit From 2497128133f8169b24b928852ba6eae34fc495e5 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Sat, 4 Feb 2017 10:16:56 +0900 Subject: nfc: Fix hangup of RC-S380* in port100_send_ack() If port100_send_ack() was called twice or more, it has race to hangup. port100_send_ack() port100_send_ack() init_completion() [...] dev->cmd_cancel = true /* this removes previous from completion */ init_completion() [...] dev->cmd_cancel = true wait_for_completion() /* never be waked up */ wait_for_completion() Like above race, this code is not assuming port100_send_ack() is called twice or more. To fix, this checks dev->cmd_cancel to know if prior cancel is in-flight or not. And never be remove prior task from completion by using reinit_completion(), so this guarantees to be waked up properly soon or later. Signed-off-by: OGAWA Hirofumi Signed-off-by: Samuel Ortiz --- drivers/nfc/port100.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 382985d3fee0..19be93e177fe 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c @@ -726,23 +726,33 @@ static int port100_submit_urb_for_ack(struct port100 *dev, gfp_t flags) static int port100_send_ack(struct port100 *dev) { - int rc; + int rc = 0; mutex_lock(&dev->out_urb_lock); - init_completion(&dev->cmd_cancel_done); + /* + * If prior cancel is in-flight (dev->cmd_cancel == true), we + * can skip to send cancel. Then this will wait the prior + * cancel, or merged into the next cancel rarely if next + * cancel was started before waiting done. In any case, this + * will be waked up soon or later. + */ + if (!dev->cmd_cancel) { + reinit_completion(&dev->cmd_cancel_done); - usb_kill_urb(dev->out_urb); + usb_kill_urb(dev->out_urb); - dev->out_urb->transfer_buffer = ack_frame; - dev->out_urb->transfer_buffer_length = sizeof(ack_frame); - rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); + dev->out_urb->transfer_buffer = ack_frame; + dev->out_urb->transfer_buffer_length = sizeof(ack_frame); + rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); - /* Set the cmd_cancel flag only if the URB has been successfully - * submitted. It will be reset by the out URB completion callback - * port100_send_complete(). - */ - dev->cmd_cancel = !rc; + /* + * Set the cmd_cancel flag only if the URB has been + * successfully submitted. It will be reset by the out + * URB completion callback port100_send_complete(). + */ + dev->cmd_cancel = !rc; + } mutex_unlock(&dev->out_urb_lock); @@ -929,8 +939,8 @@ static void port100_send_complete(struct urb *urb) struct port100 *dev = urb->context; if (dev->cmd_cancel) { + complete_all(&dev->cmd_cancel_done); dev->cmd_cancel = false; - complete(&dev->cmd_cancel_done); } switch (urb->status) { @@ -1546,6 +1556,7 @@ static int port100_probe(struct usb_interface *interface, PORT100_COMM_RF_HEAD_MAX_LEN; dev->skb_tailroom = PORT100_FRAME_TAIL_LEN; + init_completion(&dev->cmd_cancel_done); INIT_WORK(&dev->cmd_complete_work, port100_wq_cmd_complete); /* The first thing to do with the Port-100 is to set the command type -- cgit From 0b73ef7992e2a18250c1ff2f67bbb2a6d2acbef1 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 25 Jan 2017 16:23:07 -0600 Subject: NFC: remove TI nfcwilink driver It appears that TI WiLink devices including NFC (WL185x/WL189x) never shipped. The only information I found were announcements in Feb 2012 about the parts. There's been no activity on this driver besided common changes since initially added in Jan 2012. There's also no in users that instantiate the platform device (nor DT bindings). This is a first step in removing TI ST (shared transport) driver in favor of extending the BT hci_ll driver to support WL183x chips. Cc: Ilan Elias Cc: Marcel Holtmann Cc: Samuel Ortiz Cc: Lauro Ramos Venancio Cc: Aloisio Almeida Jr Cc: linux-wireless@vger.kernel.org Signed-off-by: Rob Herring Signed-off-by: Samuel Ortiz --- drivers/nfc/Kconfig | 11 - drivers/nfc/Makefile | 1 - drivers/nfc/nfcwilink.c | 578 ------------------------------------------------ 3 files changed, 590 deletions(-) delete mode 100644 drivers/nfc/nfcwilink.c diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 9d2369269abf..c4208487fadc 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -5,17 +5,6 @@ menu "Near Field Communication (NFC) devices" depends on NFC -config NFC_WILINK - tristate "Texas Instruments NFC WiLink driver" - depends on TI_ST && NFC_NCI - help - This enables the NFC driver for Texas Instrument's BT/FM/GPS/NFC - combo devices. This makes use of shared transport line discipline - core driver to communicate with the NFC core of the combo chip. - - Say Y here to compile support for Texas Instrument's NFC WiLink driver - into the kernel or say M to compile it as module. - config NFC_TRF7970A tristate "Texas Instruments TRF7970a NFC driver" depends on SPI && NFC_DIGITAL diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index bab8ef06ae35..640b7274371c 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_NFC_FDP) += fdp/ obj-$(CONFIG_NFC_PN544) += pn544/ obj-$(CONFIG_NFC_MICROREAD) += microread/ obj-$(CONFIG_NFC_PN533) += pn533/ -obj-$(CONFIG_NFC_WILINK) += nfcwilink.o obj-$(CONFIG_NFC_MEI_PHY) += mei_phy.o obj-$(CONFIG_NFC_SIM) += nfcsim.o obj-$(CONFIG_NFC_PORT100) += port100.o diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c deleted file mode 100644 index 3fbd18b8e473..000000000000 --- a/drivers/nfc/nfcwilink.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * Texas Instrument's NFC Driver For Shared Transport. - * - * NFC Driver acts as interface between NCI core and - * TI Shared Transport Layer. - * - * Copyright (C) 2011 Texas Instruments, Inc. - * - * Written by Ilan Elias - * - * Acknowledgements: - * This file is based on btwilink.c, which was written - * by Raja Mani and Pavan Savoy. - * - * 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. - * - * 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, see . - * - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#define NFCWILINK_CHNL 12 -#define NFCWILINK_OPCODE 7 -#define NFCWILINK_MAX_FRAME_SIZE 300 -#define NFCWILINK_HDR_LEN 4 -#define NFCWILINK_OFFSET_LEN_IN_HDR 1 -#define NFCWILINK_LEN_SIZE 2 -#define NFCWILINK_REGISTER_TIMEOUT 8000 /* 8 sec */ -#define NFCWILINK_CMD_TIMEOUT 5000 /* 5 sec */ - -#define BTS_FILE_NAME_MAX_SIZE 40 -#define BTS_FILE_HDR_MAGIC 0x42535442 -#define BTS_FILE_CMD_MAX_LEN 0xff -#define BTS_FILE_ACTION_TYPE_SEND_CMD 1 - -#define NCI_VS_NFCC_INFO_CMD_GID 0x2f -#define NCI_VS_NFCC_INFO_CMD_OID 0x12 -#define NCI_VS_NFCC_INFO_RSP_GID 0x4f -#define NCI_VS_NFCC_INFO_RSP_OID 0x12 - -struct nfcwilink_hdr { - __u8 chnl; - __u8 opcode; - __le16 len; -} __packed; - -struct nci_vs_nfcc_info_cmd { - __u8 gid; - __u8 oid; - __u8 plen; -} __packed; - -struct nci_vs_nfcc_info_rsp { - __u8 gid; - __u8 oid; - __u8 plen; - __u8 status; - __u8 hw_id; - __u8 sw_ver_x; - __u8 sw_ver_z; - __u8 patch_id; -} __packed; - -struct bts_file_hdr { - __le32 magic; - __le32 ver; - __u8 rfu[24]; - __u8 actions[0]; -} __packed; - -struct bts_file_action { - __le16 type; - __le16 len; - __u8 data[0]; -} __packed; - -struct nfcwilink { - struct platform_device *pdev; - struct nci_dev *ndev; - unsigned long flags; - - int st_register_cb_status; - long (*st_write) (struct sk_buff *); - - struct completion completed; - - struct nci_vs_nfcc_info_rsp nfcc_info; -}; - -/* NFCWILINK driver flags */ -enum { - NFCWILINK_RUNNING, - NFCWILINK_FW_DOWNLOAD, -}; - -static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb); - -static inline struct sk_buff *nfcwilink_skb_alloc(unsigned int len, gfp_t how) -{ - struct sk_buff *skb; - - skb = alloc_skb(len + NFCWILINK_HDR_LEN, how); - if (skb) - skb_reserve(skb, NFCWILINK_HDR_LEN); - - return skb; -} - -static void nfcwilink_fw_download_receive(struct nfcwilink *drv, - struct sk_buff *skb) -{ - struct nci_vs_nfcc_info_rsp *rsp = (void *)skb->data; - - /* Detect NCI_VS_NFCC_INFO_RSP and store the result */ - if ((skb->len > 3) && (rsp->gid == NCI_VS_NFCC_INFO_RSP_GID) && - (rsp->oid == NCI_VS_NFCC_INFO_RSP_OID)) { - memcpy(&drv->nfcc_info, rsp, - sizeof(struct nci_vs_nfcc_info_rsp)); - } - - kfree_skb(skb); - - complete(&drv->completed); -} - -static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name) -{ - struct nci_vs_nfcc_info_cmd *cmd; - struct sk_buff *skb; - unsigned long comp_ret; - int rc; - - skb = nfcwilink_skb_alloc(sizeof(struct nci_vs_nfcc_info_cmd), - GFP_KERNEL); - if (!skb) { - nfc_err(&drv->pdev->dev, - "no memory for nci_vs_nfcc_info_cmd\n"); - return -ENOMEM; - } - - cmd = (struct nci_vs_nfcc_info_cmd *) - skb_put(skb, sizeof(struct nci_vs_nfcc_info_cmd)); - cmd->gid = NCI_VS_NFCC_INFO_CMD_GID; - cmd->oid = NCI_VS_NFCC_INFO_CMD_OID; - cmd->plen = 0; - - drv->nfcc_info.plen = 0; - - rc = nfcwilink_send(drv->ndev, skb); - if (rc) - return rc; - - comp_ret = wait_for_completion_timeout(&drv->completed, - msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT)); - dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n", - comp_ret); - if (comp_ret == 0) { - nfc_err(&drv->pdev->dev, - "timeout on wait_for_completion_timeout\n"); - return -ETIMEDOUT; - } - - dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d\n", - drv->nfcc_info.plen, drv->nfcc_info.status); - - if ((drv->nfcc_info.plen != 5) || (drv->nfcc_info.status != 0)) { - nfc_err(&drv->pdev->dev, "invalid nci_vs_nfcc_info_rsp\n"); - return -EINVAL; - } - - snprintf(file_name, BTS_FILE_NAME_MAX_SIZE, - "TINfcInit_%d.%d.%d.%d.bts", - drv->nfcc_info.hw_id, - drv->nfcc_info.sw_ver_x, - drv->nfcc_info.sw_ver_z, - drv->nfcc_info.patch_id); - - nfc_info(&drv->pdev->dev, "nfcwilink FW file name: %s\n", file_name); - - return 0; -} - -static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len) -{ - struct nfcwilink_hdr *hdr = (struct nfcwilink_hdr *)data; - struct sk_buff *skb; - unsigned long comp_ret; - int rc; - - /* verify valid cmd for the NFC channel */ - if ((len <= sizeof(struct nfcwilink_hdr)) || - (len > BTS_FILE_CMD_MAX_LEN) || - (hdr->chnl != NFCWILINK_CHNL) || - (hdr->opcode != NFCWILINK_OPCODE)) { - nfc_err(&drv->pdev->dev, - "ignoring invalid bts cmd, len %d, chnl %d, opcode %d\n", - len, hdr->chnl, hdr->opcode); - return 0; - } - - /* remove the ST header */ - len -= sizeof(struct nfcwilink_hdr); - data += sizeof(struct nfcwilink_hdr); - - skb = nfcwilink_skb_alloc(len, GFP_KERNEL); - if (!skb) { - nfc_err(&drv->pdev->dev, "no memory for bts cmd\n"); - return -ENOMEM; - } - - memcpy(skb_put(skb, len), data, len); - - rc = nfcwilink_send(drv->ndev, skb); - if (rc) - return rc; - - comp_ret = wait_for_completion_timeout(&drv->completed, - msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT)); - dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n", - comp_ret); - if (comp_ret == 0) { - nfc_err(&drv->pdev->dev, - "timeout on wait_for_completion_timeout\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int nfcwilink_download_fw(struct nfcwilink *drv) -{ - unsigned char file_name[BTS_FILE_NAME_MAX_SIZE]; - const struct firmware *fw; - __u16 action_type, action_len; - __u8 *ptr; - int len, rc; - - set_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags); - - rc = nfcwilink_get_bts_file_name(drv, file_name); - if (rc) - goto exit; - - rc = request_firmware(&fw, file_name, &drv->pdev->dev); - if (rc) { - nfc_err(&drv->pdev->dev, "request_firmware failed %d\n", rc); - - /* if the file is not found, don't exit with failure */ - if (rc == -ENOENT) - rc = 0; - - goto exit; - } - - len = fw->size; - ptr = (__u8 *)fw->data; - - if ((len == 0) || (ptr == NULL)) { - dev_dbg(&drv->pdev->dev, - "request_firmware returned size %d\n", len); - goto release_fw; - } - - if (__le32_to_cpu(((struct bts_file_hdr *)ptr)->magic) != - BTS_FILE_HDR_MAGIC) { - nfc_err(&drv->pdev->dev, "wrong bts magic number\n"); - rc = -EINVAL; - goto release_fw; - } - - /* remove the BTS header */ - len -= sizeof(struct bts_file_hdr); - ptr += sizeof(struct bts_file_hdr); - - while (len > 0) { - action_type = - __le16_to_cpu(((struct bts_file_action *)ptr)->type); - action_len = - __le16_to_cpu(((struct bts_file_action *)ptr)->len); - - dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d\n", - action_type, action_len); - - switch (action_type) { - case BTS_FILE_ACTION_TYPE_SEND_CMD: - rc = nfcwilink_send_bts_cmd(drv, - ((struct bts_file_action *)ptr)->data, - action_len); - if (rc) - goto release_fw; - break; - } - - /* advance to the next action */ - len -= (sizeof(struct bts_file_action) + action_len); - ptr += (sizeof(struct bts_file_action) + action_len); - } - -release_fw: - release_firmware(fw); - -exit: - clear_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags); - return rc; -} - -/* Called by ST when registration is complete */ -static void nfcwilink_register_complete(void *priv_data, int data) -{ - struct nfcwilink *drv = priv_data; - - /* store ST registration status */ - drv->st_register_cb_status = data; - - /* complete the wait in nfc_st_open() */ - complete(&drv->completed); -} - -/* Called by ST when receive data is available */ -static long nfcwilink_receive(void *priv_data, struct sk_buff *skb) -{ - struct nfcwilink *drv = priv_data; - int rc; - - if (!skb) - return -EFAULT; - - if (!drv) { - kfree_skb(skb); - return -EFAULT; - } - - dev_dbg(&drv->pdev->dev, "receive entry, len %d\n", skb->len); - - /* strip the ST header - (apart for the chnl byte, which is not received in the hdr) */ - skb_pull(skb, (NFCWILINK_HDR_LEN-1)); - - if (test_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags)) { - nfcwilink_fw_download_receive(drv, skb); - return 0; - } - - /* Forward skb to NCI core layer */ - rc = nci_recv_frame(drv->ndev, skb); - if (rc < 0) { - nfc_err(&drv->pdev->dev, "nci_recv_frame failed %d\n", rc); - return rc; - } - - return 0; -} - -/* protocol structure registered with ST */ -static struct st_proto_s nfcwilink_proto = { - .chnl_id = NFCWILINK_CHNL, - .max_frame_size = NFCWILINK_MAX_FRAME_SIZE, - .hdr_len = (NFCWILINK_HDR_LEN-1), /* not including chnl byte */ - .offset_len_in_hdr = NFCWILINK_OFFSET_LEN_IN_HDR, - .len_size = NFCWILINK_LEN_SIZE, - .reserve = 0, - .recv = nfcwilink_receive, - .reg_complete_cb = nfcwilink_register_complete, - .write = NULL, -}; - -static int nfcwilink_open(struct nci_dev *ndev) -{ - struct nfcwilink *drv = nci_get_drvdata(ndev); - unsigned long comp_ret; - int rc; - - if (test_and_set_bit(NFCWILINK_RUNNING, &drv->flags)) { - rc = -EBUSY; - goto exit; - } - - nfcwilink_proto.priv_data = drv; - - init_completion(&drv->completed); - drv->st_register_cb_status = -EINPROGRESS; - - rc = st_register(&nfcwilink_proto); - if (rc < 0) { - if (rc == -EINPROGRESS) { - comp_ret = wait_for_completion_timeout( - &drv->completed, - msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT)); - - dev_dbg(&drv->pdev->dev, - "wait_for_completion_timeout returned %ld\n", - comp_ret); - - if (comp_ret == 0) { - /* timeout */ - rc = -ETIMEDOUT; - goto clear_exit; - } else if (drv->st_register_cb_status != 0) { - rc = drv->st_register_cb_status; - nfc_err(&drv->pdev->dev, - "st_register_cb failed %d\n", rc); - goto clear_exit; - } - } else { - nfc_err(&drv->pdev->dev, "st_register failed %d\n", rc); - goto clear_exit; - } - } - - /* st_register MUST fill the write callback */ - BUG_ON(nfcwilink_proto.write == NULL); - drv->st_write = nfcwilink_proto.write; - - if (nfcwilink_download_fw(drv)) { - nfc_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d\n", - rc); - /* open should succeed, even if the FW download failed */ - } - - goto exit; - -clear_exit: - clear_bit(NFCWILINK_RUNNING, &drv->flags); - -exit: - return rc; -} - -static int nfcwilink_close(struct nci_dev *ndev) -{ - struct nfcwilink *drv = nci_get_drvdata(ndev); - int rc; - - if (!test_and_clear_bit(NFCWILINK_RUNNING, &drv->flags)) - return 0; - - rc = st_unregister(&nfcwilink_proto); - if (rc) - nfc_err(&drv->pdev->dev, "st_unregister failed %d\n", rc); - - drv->st_write = NULL; - - return rc; -} - -static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb) -{ - struct nfcwilink *drv = nci_get_drvdata(ndev); - struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000}; - long len; - - dev_dbg(&drv->pdev->dev, "send entry, len %d\n", skb->len); - - if (!test_bit(NFCWILINK_RUNNING, &drv->flags)) { - kfree_skb(skb); - return -EINVAL; - } - - /* add the ST hdr to the start of the buffer */ - hdr.len = cpu_to_le16(skb->len); - memcpy(skb_push(skb, NFCWILINK_HDR_LEN), &hdr, NFCWILINK_HDR_LEN); - - /* Insert skb to shared transport layer's transmit queue. - * Freeing skb memory is taken care in shared transport layer, - * so don't free skb memory here. - */ - len = drv->st_write(skb); - if (len < 0) { - kfree_skb(skb); - nfc_err(&drv->pdev->dev, "st_write failed %ld\n", len); - return -EFAULT; - } - - return 0; -} - -static struct nci_ops nfcwilink_ops = { - .open = nfcwilink_open, - .close = nfcwilink_close, - .send = nfcwilink_send, -}; - -static int nfcwilink_probe(struct platform_device *pdev) -{ - struct nfcwilink *drv; - int rc; - __u32 protocols; - - drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL); - if (!drv) { - rc = -ENOMEM; - goto exit; - } - - drv->pdev = pdev; - - protocols = NFC_PROTO_JEWEL_MASK - | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK - | NFC_PROTO_ISO14443_MASK - | NFC_PROTO_ISO14443_B_MASK - | NFC_PROTO_NFC_DEP_MASK; - - drv->ndev = nci_allocate_device(&nfcwilink_ops, - protocols, - NFCWILINK_HDR_LEN, - 0); - if (!drv->ndev) { - nfc_err(&pdev->dev, "nci_allocate_device failed\n"); - rc = -ENOMEM; - goto exit; - } - - nci_set_parent_dev(drv->ndev, &pdev->dev); - nci_set_drvdata(drv->ndev, drv); - - rc = nci_register_device(drv->ndev); - if (rc < 0) { - nfc_err(&pdev->dev, "nci_register_device failed %d\n", rc); - goto free_dev_exit; - } - - dev_set_drvdata(&pdev->dev, drv); - - goto exit; - -free_dev_exit: - nci_free_device(drv->ndev); - -exit: - return rc; -} - -static int nfcwilink_remove(struct platform_device *pdev) -{ - struct nfcwilink *drv = dev_get_drvdata(&pdev->dev); - struct nci_dev *ndev; - - if (!drv) - return -EFAULT; - - ndev = drv->ndev; - - nci_unregister_device(ndev); - nci_free_device(ndev); - - return 0; -} - -static struct platform_driver nfcwilink_driver = { - .probe = nfcwilink_probe, - .remove = nfcwilink_remove, - .driver = { - .name = "nfcwilink", - }, -}; - -module_platform_driver(nfcwilink_driver); - -/* ------ Module Info ------ */ - -MODULE_AUTHOR("Ilan Elias "); -MODULE_DESCRIPTION("NFC Driver for TI Shared Transport"); -MODULE_LICENSE("GPL"); -- cgit From d689530be68054ae2f927d539b69169d1f25fad0 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Thu, 24 Nov 2016 21:58:34 +0800 Subject: NFC: nfcmrvl: drop duplicate header gpio.h Drop duplicate header gpio.h from nfcmrvl/spi.c. Signed-off-by: Geliang Tang Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/spi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/nfc/nfcmrvl/spi.c b/drivers/nfc/nfcmrvl/spi.c index a7faa0bcc01e..e2881b15ce6c 100644 --- a/drivers/nfc/nfcmrvl/spi.c +++ b/drivers/nfc/nfcmrvl/spi.c @@ -26,7 +26,6 @@ #include #include #include -#include #include "nfcmrvl.h" #define SPI_WAIT_HANDSHAKE 1 -- cgit From d916d923724d59cde99ee588f15eec59dd863bbd Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 26 Oct 2016 11:00:12 +0200 Subject: NFC: nfcmrvl: Include unaligned.h instead of access_ok.h Including linux/unaligned/access_ok.h causes the allmodconfig build on ia64 (and maybe others) to fail with the following warnings: include/linux/unaligned/access_ok.h:7:19: error: redefinition of 'get_unaligned_le16' include/linux/unaligned/access_ok.h:12:19: error: redefinition of 'get_unaligned_le32' include/linux/unaligned/access_ok.h:17:19: error: redefinition of 'get_unaligned_le64' include/linux/unaligned/access_ok.h:22:19: error: redefinition of 'get_unaligned_be16' include/linux/unaligned/access_ok.h:27:19: error: redefinition of 'get_unaligned_be32' include/linux/unaligned/access_ok.h:32:19: error: redefinition of 'get_unaligned_be64' include/linux/unaligned/access_ok.h:37:20: error: redefinition of 'put_unaligned_le16' include/linux/unaligned/access_ok.h:42:20: error: redefinition of 'put_unaligned_le32' include/linux/unaligned/access_ok.h:42:20: error: redefinition of 'put_unaligned_le64' include/linux/unaligned/access_ok.h:42:20: error: redefinition of 'put_unaligned_be16' include/linux/unaligned/access_ok.h:42:20: error: redefinition of 'put_unaligned_be32' include/linux/unaligned/access_ok.h:42:20: error: redefinition of 'put_unaligned_be64' Fix these by including asm/unaligned.h instead and leave it up to the architecture to decide how to implement unaligned accesses. Fixes: 3194c6870158 ("NFC: nfcmrvl: add firmware download support") Reported-by: kbuild test robot Link: https://lkml.org/lkml/2016/10/22/247 Cc: Vincent Cuissard Signed-off-by: Tobias Klauser Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/fw_dnld.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nfc/nfcmrvl/fw_dnld.c b/drivers/nfc/nfcmrvl/fw_dnld.c index f8dcdf4b24f6..441c1b0ec7b5 100644 --- a/drivers/nfc/nfcmrvl/fw_dnld.c +++ b/drivers/nfc/nfcmrvl/fw_dnld.c @@ -17,7 +17,7 @@ */ #include -#include +#include #include #include #include -- cgit From 2eee74b7e2a496dea49847c36fd09320505f45b7 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 1 Aug 2015 06:59:29 -0700 Subject: NFC: nxp-nci: Include unaligned.h instead of access_ok.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Directly including access_ok.h can result in the following compile errors if an architecture such as ia64 does not support direct unaligned accesses. include/linux/unaligned/access_ok.h:7:19: error: redefinition of 'get_unaligned_le16' include/linux/unaligned/le_struct.h:6:19: note: previous definition of 'get_unaligned_le16' was here include/linux/unaligned/access_ok.h:12:19: error: redefinition of 'get_unaligned_le32' include/linux/unaligned/le_struct.h:11:19: note: previous definition of 'get_unaligned_le32' was here Include asm/unaligned.h instead and let the architecture decide which access functions to use. Cc: Clément Perrochaud Cc: Samuel Ortiz Signed-off-by: Guenter Roeck Signed-off-by: Samuel Ortiz --- drivers/nfc/nxp-nci/firmware.c | 2 +- drivers/nfc/nxp-nci/i2c.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/nxp-nci/firmware.c b/drivers/nfc/nxp-nci/firmware.c index 5291797324ba..553011f58339 100644 --- a/drivers/nfc/nxp-nci/firmware.c +++ b/drivers/nfc/nxp-nci/firmware.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include "nxp-nci.h" diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index 36099e557730..06a157c63416 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include -- cgit From f92cb58318745d9b59a30ff1e37d4fb298d4b75f Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Thu, 15 Dec 2016 15:22:44 +0100 Subject: nfc: nxp-nci: Remove unneeded linux/miscdevice.h include drivers/nfc/nxp-nci/i2c.c does not use any miscdevice, so this patch remove this unnecessary inclusion. Signed-off-by: Corentin Labbe Signed-off-by: Samuel Ortiz --- drivers/nfc/nxp-nci/i2c.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index 06a157c63416..ae69d2f73700 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include -- cgit From 17a0180be18050a96466671ed18430ad20d8346f Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Thu, 15 Dec 2016 15:22:45 +0100 Subject: nfc: pn544: Remove unneeded linux/miscdevice.h include drivers/nfc/pn544/i2c.c does not use any miscdevice, so this patch remove this unnecessary inclusion. Signed-off-by: Corentin Labbe Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/i2c.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index f837c39a8017..7030a47fe379 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include -- cgit From 52fdede5c9b96de355ab5e45e35503b9da6b86f6 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Thu, 15 Dec 2016 15:22:46 +0100 Subject: nfc: st21nfca: Remove unneeded linux/miscdevice.h include drivers/nfc/st21nfca/i2c.c does not use any miscdevice, so this patch remove this unnecessary inclusion. Signed-off-by: Corentin Labbe Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/i2c.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index 5a82f553906c..d16f58ac09bc 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include -- cgit From ca42fb9e52d155547e6cf18cf26bce3e1a6af4ea Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 8 Mar 2017 08:22:37 +0300 Subject: NFC: nfcmrvl: double free on error path The nci_spi_send() function calls kfree_skb(skb) on both error and success so this extra kfree_skb() is a double free. Fixes: caf6e49bf6d0 ("NFC: nfcmrvl: add spi driver") Signed-off-by: Dan Carpenter Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/spi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/nfcmrvl/spi.c b/drivers/nfc/nfcmrvl/spi.c index e2881b15ce6c..8e0ddb434770 100644 --- a/drivers/nfc/nfcmrvl/spi.c +++ b/drivers/nfc/nfcmrvl/spi.c @@ -95,10 +95,9 @@ static int nfcmrvl_spi_nci_send(struct nfcmrvl_private *priv, /* Send the SPI packet */ err = nci_spi_send(drv_data->nci_spi, &drv_data->handshake_completion, skb); - if (err != 0) { + if (err) nfc_err(priv->dev, "spi_send failed %d", err); - kfree_skb(skb); - } + return err; } -- cgit From 8f79ded95934154d4454b6e422a18a3fbcd7a268 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 19 Feb 2017 10:58:47 +0100 Subject: NFC: st21nfca: Fix potential memory leak If all bits of 'dev_mask' are already set, there is a memory leak because 'info' should be freed before returning. While fixing it, 'return -ENOMEM' directly if the first kzalloc fails. This makes the code more readable. Signed-off-by: Christophe JAILLET Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/nfc/st21nfca/core.c b/drivers/nfc/st21nfca/core.c index dacb9166081b..50be3b788f1c 100644 --- a/drivers/nfc/st21nfca/core.c +++ b/drivers/nfc/st21nfca/core.c @@ -959,10 +959,8 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, unsigned long quirks = 0; info = kzalloc(sizeof(struct st21nfca_hci_info), GFP_KERNEL); - if (!info) { - r = -ENOMEM; - goto err_alloc_hdev; - } + if (!info) + return -ENOMEM; info->phy_ops = phy_ops; info->phy_id = phy_id; @@ -978,8 +976,10 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, * persistent info to discriminate 2 identical chips */ dev_num = find_first_zero_bit(dev_mask, ST21NFCA_NUM_DEVICES); - if (dev_num >= ST21NFCA_NUM_DEVICES) - return -ENODEV; + if (dev_num >= ST21NFCA_NUM_DEVICES) { + r = -ENODEV; + goto err_alloc_hdev; + } set_bit(dev_num, dev_mask); -- cgit From 96bd0b5e550f26e8472c624d1aabe7448d0a7c27 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Sun, 22 Jan 2017 13:28:39 +0100 Subject: nfc: nxp-nci: use msleep for long delays ulseep_range() uses hrtimers and provides no advantage over msleep() for larger delays. For this large delay msleep() is preferable. Fixes: commit 6be88670fc59 ("NFC: nxp-nci_i2c: Add I2C support to NXP NCI driver") Link: http://lkml.org/lkml/2017/1/11/377 Signed-off-by: Nicholas Mc Guire Signed-off-by: Samuel Ortiz --- drivers/nfc/nxp-nci/i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index ae69d2f73700..c6a04a950225 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -85,7 +85,7 @@ static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb) r = i2c_master_send(client, skb->data, skb->len); if (r < 0) { /* Retry, chip was in standby */ - usleep_range(110000, 120000); + msleep(110); r = i2c_master_send(client, skb->data, skb->len); } -- cgit From ce69b95ca4e459f54b5afad717d2129d3ba1ff6e Mon Sep 17 00:00:00 2001 From: Guan Ben Date: Tue, 7 Feb 2017 06:22:04 +0100 Subject: NFC: Make EN2 pin optional in the TRF7970A driver Make the EN2 pin optional. This is useful for boards, which have this pin fix wired, for example to ground. Acked-by: Rob Herring Signed-off-by: Guan Ben Signed-off-by: Mark Jonas Signed-off-by: Heiko Schocher Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/net/nfc/trf7970a.txt | 4 ++-- drivers/nfc/trf7970a.c | 26 ++++++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Documentation/devicetree/bindings/net/nfc/trf7970a.txt b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt index 32b35a07abe4..5889a3d133b2 100644 --- a/Documentation/devicetree/bindings/net/nfc/trf7970a.txt +++ b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt @@ -5,8 +5,8 @@ Required properties: - spi-max-frequency: Maximum SPI frequency (<= 2000000). - interrupt-parent: phandle of parent interrupt handler. - interrupts: A single interrupt specifier. -- ti,enable-gpios: Two GPIO entries used for 'EN' and 'EN2' pins on the - TRF7970A. +- ti,enable-gpios: One or two GPIO entries used for 'EN' and 'EN2' pins on the + TRF7970A. EN2 is optional. - vin-supply: Regulator for supply voltage to VIN pin Optional SoC Specific Properties: diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c index 26c9dbbccb0c..75079fb16f22 100644 --- a/drivers/nfc/trf7970a.c +++ b/drivers/nfc/trf7970a.c @@ -1885,8 +1885,10 @@ static int trf7970a_power_up(struct trf7970a *trf) usleep_range(5000, 6000); if (!(trf->quirks & TRF7970A_QUIRK_EN2_MUST_STAY_LOW)) { - gpio_set_value(trf->en2_gpio, 1); - usleep_range(1000, 2000); + if (gpio_is_valid(trf->en2_gpio)) { + gpio_set_value(trf->en2_gpio, 1); + usleep_range(1000, 2000); + } } gpio_set_value(trf->en_gpio, 1); @@ -1914,7 +1916,8 @@ static int trf7970a_power_down(struct trf7970a *trf) } gpio_set_value(trf->en_gpio, 0); - gpio_set_value(trf->en2_gpio, 0); + if (gpio_is_valid(trf->en2_gpio)) + gpio_set_value(trf->en2_gpio, 0); ret = regulator_disable(trf->regulator); if (ret) @@ -2032,15 +2035,14 @@ static int trf7970a_probe(struct spi_device *spi) trf->en2_gpio = of_get_named_gpio(np, "ti,enable-gpios", 1); if (!gpio_is_valid(trf->en2_gpio)) { - dev_err(trf->dev, "No EN2 GPIO property\n"); - return trf->en2_gpio; - } - - ret = devm_gpio_request_one(trf->dev, trf->en2_gpio, - GPIOF_DIR_OUT | GPIOF_INIT_LOW, "trf7970a EN2"); - if (ret) { - dev_err(trf->dev, "Can't request EN2 GPIO: %d\n", ret); - return ret; + dev_info(trf->dev, "No EN2 GPIO property\n"); + } else { + ret = devm_gpio_request_one(trf->dev, trf->en2_gpio, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, "trf7970a EN2"); + if (ret) { + dev_err(trf->dev, "Can't request EN2 GPIO: %d\n", ret); + return ret; + } } if (of_property_read_bool(np, "en2-rf-quirk")) -- cgit From b6355fb3f5f40bbce165847d277e64896cab8f95 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Tue, 20 Dec 2016 21:09:04 +0000 Subject: nfc: fdp: fix NULL pointer dereference We are checking phy after dereferencing it. We can print the debug information after checking it. If phy is NULL then we will get a good stack trace to tell us that we are in this irq handler. Signed-off-by: Sudip Mukherjee Signed-off-by: Samuel Ortiz --- drivers/nfc/fdp/i2c.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c index 5e797d5c38ed..712936f5d2d6 100644 --- a/drivers/nfc/fdp/i2c.c +++ b/drivers/nfc/fdp/i2c.c @@ -210,14 +210,14 @@ static irqreturn_t fdp_nci_i2c_irq_thread_fn(int irq, void *phy_id) struct sk_buff *skb; int r; - client = phy->i2c_dev; - dev_dbg(&client->dev, "%s\n", __func__); - if (!phy || irq != phy->i2c_dev->irq) { WARN_ON_ONCE(1); return IRQ_NONE; } + client = phy->i2c_dev; + dev_dbg(&client->dev, "%s\n", __func__); + r = fdp_nci_i2c_read(phy, &skb); if (r == -EREMOTEIO) -- cgit From 068a496c4525c638ffab56449d905b88ef97fe32 Mon Sep 17 00:00:00 2001 From: Andrey Rusalin Date: Wed, 28 Dec 2016 20:10:57 +0300 Subject: NFC: pn533: change order of free_irq and dev unregistration Change order of free_irq and dev unregistration. It fixes situation when device already unregistered and an interrupt happens and nobody can handle it. Signed-off-by: Andrey Rusalin Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533/i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/pn533/i2c.c b/drivers/nfc/pn533/i2c.c index 1dc89248e58e..11d78b43cf76 100644 --- a/drivers/nfc/pn533/i2c.c +++ b/drivers/nfc/pn533/i2c.c @@ -242,10 +242,10 @@ static int pn533_i2c_remove(struct i2c_client *client) dev_dbg(&client->dev, "%s\n", __func__); - pn533_unregister_device(phy->priv); - free_irq(client->irq, phy); + pn533_unregister_device(phy->priv); + return 0; } -- cgit From 5dd9c1bd61a7b684e54897a3a2546124c4077eda Mon Sep 17 00:00:00 2001 From: Andrey Rusalin Date: Wed, 28 Dec 2016 20:10:58 +0300 Subject: NFC: pn533: improve cmd queue handling Make sure cmd is set before a frame is passed to the transport layer for sending. In addition pn533_send_async_complete checks if cmd is set before accessing its members. Signed-off-by: Michael Thalmeier Rework a little bit changes in pn532_send_async_complete. Signed-off-by: Andrey Rusalin Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533/pn533.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/nfc/pn533/pn533.c b/drivers/nfc/pn533/pn533.c index a966c6a85ea8..712aa67e1770 100644 --- a/drivers/nfc/pn533/pn533.c +++ b/drivers/nfc/pn533/pn533.c @@ -383,14 +383,18 @@ static void pn533_build_cmd_frame(struct pn533 *dev, u8 cmd_code, static int pn533_send_async_complete(struct pn533 *dev) { struct pn533_cmd *cmd = dev->cmd; - int status = cmd->status; + struct sk_buff *resp; + int status, rc = 0; - struct sk_buff *req = cmd->req; - struct sk_buff *resp = cmd->resp; + if (!cmd) { + dev_dbg(dev->dev, "%s: cmd not set\n", __func__); + goto done; + } - int rc; + dev_kfree_skb(cmd->req); - dev_kfree_skb(req); + status = cmd->status; + resp = cmd->resp; if (status < 0) { rc = cmd->complete_cb(dev, cmd->complete_cb_context, @@ -399,8 +403,14 @@ static int pn533_send_async_complete(struct pn533 *dev) goto done; } - skb_pull(resp, dev->ops->rx_header_len); - skb_trim(resp, resp->len - dev->ops->rx_tail_len); + /* when no response is set we got interrupted */ + if (!resp) + resp = ERR_PTR(-EINTR); + + if (!IS_ERR(resp)) { + skb_pull(resp, dev->ops->rx_header_len); + skb_trim(resp, resp->len - dev->ops->rx_tail_len); + } rc = cmd->complete_cb(dev, cmd->complete_cb_context, resp); @@ -434,12 +444,14 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code, mutex_lock(&dev->cmd_lock); if (!dev->cmd_pending) { + dev->cmd = cmd; rc = dev->phy_ops->send_frame(dev, req); - if (rc) + if (rc) { + dev->cmd = NULL; goto error; + } dev->cmd_pending = 1; - dev->cmd = cmd; goto unlock; } @@ -511,11 +523,12 @@ static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code, pn533_build_cmd_frame(dev, cmd_code, req); + dev->cmd = cmd; rc = dev->phy_ops->send_frame(dev, req); - if (rc < 0) + if (rc < 0) { + dev->cmd = NULL; kfree(cmd); - else - dev->cmd = cmd; + } return rc; } @@ -550,14 +563,15 @@ static void pn533_wq_cmd(struct work_struct *work) mutex_unlock(&dev->cmd_lock); + dev->cmd = cmd; rc = dev->phy_ops->send_frame(dev, cmd->req); if (rc < 0) { + dev->cmd = NULL; dev_kfree_skb(cmd->req); kfree(cmd); return; } - dev->cmd = cmd; } struct pn533_sync_cmd_response { -- cgit From 32ecc75ded72e0425713a7ffe2050fef6e54e564 Mon Sep 17 00:00:00 2001 From: Andrey Rusalin Date: Wed, 28 Dec 2016 20:10:59 +0300 Subject: NFC: pn533: change order operations in dev registation Sometimes during probing and registration of pn533_i2c NULL pointer dereference happens. Reproduced in cycle of inserting and removing pn533_i2c and pn533 modules. Backtrace: [<8004205c>] (__queue_work) from [<80042324>] (queue_work_on+0x50/0x5c) r10:acdc7c80 r9:8006b330 r8:ac0dfb40 r7:ac50c600 r6:00000004 r5:acbbee40 r4:600f0113 [<800422d4>] (queue_work_on) from [<7f7d5b6c>] (pn533_recv_frame+0x158/0x1fc [pn533]) r7:ffffff87 r6:00000000 r5:acbbee40 r4:acbbee00 [<7f7d5a14>] (pn533_recv_frame [pn533]) from [<7f7df4b8>] (pn533_i2c_irq_thread_fn+0x184/0x) r6:acb2a000 r5:00000000 r4:acdc7b90 [<7f7df334>] (pn533_i2c_irq_thread_fn [pn533_i2c]) from [<8006b354>] (irq_thread_fn+0x24/0x) r7:00000000 r6:accde000 r5:ac0dfb40 r4:acdc7c80 ... Seems there is some race condition due registration of irq handler until all data stuctures that could be needed are ready. So I re-ordered some ops. After this, problem has gone. Changes in USB part was not tested, but it should not break anything. Signed-off-by: Andrey Rusalin Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533/i2c.c | 28 ++++++++++++++++++---------- drivers/nfc/pn533/pn533.c | 42 +++++++++++++++++++++++++----------------- drivers/nfc/pn533/pn533.h | 1 + drivers/nfc/pn533/usb.c | 4 ++++ 4 files changed, 48 insertions(+), 27 deletions(-) diff --git a/drivers/nfc/pn533/i2c.c b/drivers/nfc/pn533/i2c.c index 11d78b43cf76..2c2fb9cfe10a 100644 --- a/drivers/nfc/pn533/i2c.c +++ b/drivers/nfc/pn533/i2c.c @@ -206,14 +206,6 @@ static int pn533_i2c_probe(struct i2c_client *client, phy->i2c_dev = client; i2c_set_clientdata(client, phy); - r = request_threaded_irq(client->irq, NULL, pn533_i2c_irq_thread_fn, - IRQF_TRIGGER_FALLING | - IRQF_SHARED | IRQF_ONESHOT, - PN533_I2C_DRIVER_NAME, phy); - - if (r < 0) - nfc_err(&client->dev, "Unable to register IRQ handler\n"); - priv = pn533_register_device(PN533_DEVICE_PN532, PN533_NO_TYPE_B_PROTOCOLS, PN533_PROTO_REQ_ACK_RESP, @@ -223,16 +215,32 @@ static int pn533_i2c_probe(struct i2c_client *client, if (IS_ERR(priv)) { r = PTR_ERR(priv); - goto err_register; + return r; } phy->priv = priv; + r = request_threaded_irq(client->irq, NULL, pn533_i2c_irq_thread_fn, + IRQF_TRIGGER_FALLING | + IRQF_SHARED | IRQF_ONESHOT, + PN533_I2C_DRIVER_NAME, phy); + if (r < 0) { + nfc_err(&client->dev, "Unable to register IRQ handler\n"); + goto irq_rqst_err; + } + + r = pn533_finalize_setup(priv); + if (r) + goto fn_setup_err; + return 0; -err_register: +fn_setup_err: free_irq(client->irq, phy); +irq_rqst_err: + pn533_unregister_device(phy->priv); + return r; } diff --git a/drivers/nfc/pn533/pn533.c b/drivers/nfc/pn533/pn533.c index 712aa67e1770..65bbaa5fcdda 100644 --- a/drivers/nfc/pn533/pn533.c +++ b/drivers/nfc/pn533/pn533.c @@ -2570,6 +2570,31 @@ static int pn533_setup(struct pn533 *dev) return 0; } +int pn533_finalize_setup(struct pn533 *dev) +{ + + struct pn533_fw_version fw_ver; + int rc; + + memset(&fw_ver, 0, sizeof(fw_ver)); + + rc = pn533_get_firmware_version(dev, &fw_ver); + if (rc) { + nfc_err(dev->dev, "Unable to get FW version\n"); + return rc; + } + + nfc_info(dev->dev, "NXP PN5%02X firmware ver %d.%d now attached\n", + fw_ver.ic, fw_ver.ver, fw_ver.rev); + + rc = pn533_setup(dev); + if (rc) + return rc; + + return 0; +} +EXPORT_SYMBOL_GPL(pn533_finalize_setup); + struct pn533 *pn533_register_device(u32 device_type, u32 protocols, enum pn533_protocol_type protocol_type, @@ -2579,7 +2604,6 @@ struct pn533 *pn533_register_device(u32 device_type, struct device *dev, struct device *parent) { - struct pn533_fw_version fw_ver; struct pn533 *priv; int rc = -ENOMEM; @@ -2622,15 +2646,6 @@ struct pn533 *pn533_register_device(u32 device_type, INIT_LIST_HEAD(&priv->cmd_queue); - memset(&fw_ver, 0, sizeof(fw_ver)); - rc = pn533_get_firmware_version(priv, &fw_ver); - if (rc < 0) - goto destroy_wq; - - nfc_info(dev, "NXP PN5%02X firmware ver %d.%d now attached\n", - fw_ver.ic, fw_ver.ver, fw_ver.rev); - - priv->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols, priv->ops->tx_header_len + PN533_CMD_DATAEXCH_HEAD_LEN, @@ -2647,15 +2662,8 @@ struct pn533 *pn533_register_device(u32 device_type, if (rc) goto free_nfc_dev; - rc = pn533_setup(priv); - if (rc) - goto unregister_nfc_dev; - return priv; -unregister_nfc_dev: - nfc_unregister_device(priv->nfc_dev); - free_nfc_dev: nfc_free_device(priv->nfc_dev); diff --git a/drivers/nfc/pn533/pn533.h b/drivers/nfc/pn533/pn533.h index 553c7d171fd1..88d569666c51 100644 --- a/drivers/nfc/pn533/pn533.h +++ b/drivers/nfc/pn533/pn533.h @@ -231,6 +231,7 @@ struct pn533 *pn533_register_device(u32 device_type, struct device *dev, struct device *parent); +int pn533_finalize_setup(struct pn533 *dev); void pn533_unregister_device(struct pn533 *priv); void pn533_recv_frame(struct pn533 *dev, struct sk_buff *skb, int status); diff --git a/drivers/nfc/pn533/usb.c b/drivers/nfc/pn533/usb.c index 33ed78be2750..000159ea9c5f 100644 --- a/drivers/nfc/pn533/usb.c +++ b/drivers/nfc/pn533/usb.c @@ -543,6 +543,10 @@ static int pn533_usb_probe(struct usb_interface *interface, phy->priv = priv; + rc = pn533_finalize_setup(priv); + if (rc) + goto error; + usb_set_intfdata(interface, phy); return 0; -- cgit From 4334d8fc638aea4f36fc69042f699e44fed98b16 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Sun, 2 Apr 2017 01:07:53 +0200 Subject: MAINTAINERS: Remove Lauro and Aloisio from the NFC maintainers list They are no longer active and their email addresses bounce. Signed-off-by: Samuel Ortiz --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index a1ce88e91444..a8eaa0a8e522 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8853,8 +8853,6 @@ S: Supported F: drivers/net/ethernet/qlogic/netxen/ NFC SUBSYSTEM -M: Lauro Ramos Venancio -M: Aloisio Almeida Jr M: Samuel Ortiz L: linux-wireless@vger.kernel.org L: linux-nfc@lists.01.org (subscribers-only) -- cgit From 83ea067fe2eae9a67c172aede6e11b9f194b1d52 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 31 Mar 2017 10:37:07 +0100 Subject: net: phy: avoid setting unsupported EEE advertisments We currently allow userspace to set any EEE advertisments it desires, whether or not the PHY supports them. For example: # ethtool --set-eee eth1 advertise 0xffffffff # ethtool --show-eee eth1 EEE Settings for eth1: EEE status: disabled Tx LPI: disabled Supported EEE link modes: 100baseT/Full 1000baseT/Full 10000baseT/Full Advertised EEE link modes: 100baseT/Full 1000baseT/Full 1000baseKX/Full 10000baseT/Full 10000baseKX4/Full 10000baseKR/Full Clearly, this is not sane, we should only allow link modes that are supported to be advertised (as we do elsewhere.) Ensure that we mask the MDIO_AN_EEE_ADV value with the capabilities retrieved from the MDIO_PCS_EEE_ABLE register. Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index ba4676ee9018..7b1c93b0233a 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1332,17 +1332,22 @@ EXPORT_SYMBOL(phy_ethtool_get_eee); */ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) { - int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised); + int cap, adv; if (!phydev->drv) return -EIO; - /* Mask prohibited EEE modes */ - val &= ~phydev->eee_broken_modes; + /* Get Supported EEE */ + cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE); + if (cap < 0) + return cap; - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val); + adv = ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap; - return 0; + /* Mask prohibited EEE modes */ + adv &= ~phydev->eee_broken_modes; + + return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); } EXPORT_SYMBOL(phy_ethtool_set_eee); -- cgit From f75abeb8338e2d5bcdfc393dff3950a7039eab5a Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 31 Mar 2017 10:37:12 +0100 Subject: net: phy: restart phy autonegotiation after EEE advertisment change When the EEE advertisment is changed, we should restart autonegotiation to update the link partner with the new EEE settings. Add this trigger but only if the advertisment has changed. Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 7b1c93b0233a..345251f21699 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1332,7 +1332,7 @@ EXPORT_SYMBOL(phy_ethtool_get_eee); */ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) { - int cap, adv; + int cap, old_adv, adv, ret; if (!phydev->drv) return -EIO; @@ -1342,12 +1342,29 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) if (cap < 0) return cap; + old_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV); + if (old_adv < 0) + return old_adv; + adv = ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap; /* Mask prohibited EEE modes */ adv &= ~phydev->eee_broken_modes; - return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); + if (old_adv != adv) { + ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv); + if (ret < 0) + return ret; + + /* Restart autonegotiation so the new modes get sent to the + * link partner. + */ + ret = genphy_restart_aneg(phydev); + if (ret < 0) + return ret; + } + + return 0; } EXPORT_SYMBOL(phy_ethtool_set_eee); -- cgit From 32d751412b8baf977deb3b2dce72025a98cbdd5e Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 31 Mar 2017 10:37:18 +0100 Subject: net: phy: allow EEE with any interface mode EEE is able to work in any PHY interface mode, there is nothing which fundamentally restricts it to only a few modes. For example, EEE works in SGMII mode with the Marvell 88E1512. Rather than just adding SGMII mode to the list, Florian suggests removing the list of interface modes entirely: It actually sounds like we should just kill the check entirely, it does not appear that any of the interface mode would not fundamentally be able to support EEE, because the "lowest" mode we support is MII, and even there it's quite possible to support EEE. Reviewed-by: Florian Fainelli Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 345251f21699..867c42154087 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1208,15 +1208,8 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) return -EIO; /* According to 802.3az,the EEE is supported only in full duplex-mode. - * Also EEE feature is active when core is operating with MII, GMII - * or RGMII (all kinds). Internal PHYs are also allowed to proceed and - * should return an error if they do not support EEE. */ - if ((phydev->duplex == DUPLEX_FULL) && - ((phydev->interface == PHY_INTERFACE_MODE_MII) || - (phydev->interface == PHY_INTERFACE_MODE_GMII) || - phy_interface_is_rgmii(phydev) || - phy_is_internal(phydev))) { + if (phydev->duplex == DUPLEX_FULL) { int eee_lp, eee_cap, eee_adv; u32 lp, cap, adv; int status; -- cgit From 768bfa2a061f0a3aeb1080015deffd57f294c55d Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Fri, 31 Mar 2017 11:44:52 +0200 Subject: net: dsa: fix build error with devlink build as module After commit 96567d5dacf4 ("net: dsa: dsa2: Add basic support of devlink") I see the following link error with CONFIG_NET_DSA=y and CONFIG_NET_DEVLINK=m: net/built-in.o: In function 'dsa_register_switch': (.text+0xe226b): undefined reference to `devlink_alloc' net/built-in.o: In function 'dsa_register_switch': (.text+0xe2284): undefined reference to `devlink_register' net/built-in.o: In function 'dsa_register_switch': (.text+0xe243e): undefined reference to `devlink_port_register' net/built-in.o: In function 'dsa_register_switch': (.text+0xe24e1): undefined reference to `devlink_port_register' net/built-in.o: In function 'dsa_register_switch': (.text+0xe24fa): undefined reference to `devlink_port_type_eth_set' net/built-in.o: In function 'dsa_dst_unapply.part.8': dsa2.c:(.text.unlikely+0x345): undefined reference to 'devlink_port_unregister' dsa2.c:(.text.unlikely+0x36c): undefined reference to 'devlink_port_unregister' dsa2.c:(.text.unlikely+0x38e): undefined reference to 'devlink_port_unregister' dsa2.c:(.text.unlikely+0x3f2): undefined reference to 'devlink_unregister' dsa2.c:(.text.unlikely+0x3fb): undefined reference to 'devlink_free' Fix this by adding a dependency on MAY_USE_DEVLINK so that CONFIG_NET_DSA get switched to be build as module when CONFIG_NET_DEVLINK=m. Fixes: 96567d5dacf4 ("net: dsa: dsa2: Add basic support of devlink") Signed-off-by: Tobias Regnery Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- net/dsa/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index 9649238eef40..da4d64f432db 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -6,7 +6,7 @@ config HAVE_NET_DSA config NET_DSA tristate "Distributed Switch Architecture" - depends on HAVE_NET_DSA + depends on HAVE_NET_DSA && MAY_USE_DEVLINK select NET_SWITCHDEV select PHYLIB ---help--- -- cgit From 3d8417d79e0da6a47ff29932ef80486be78af56e Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 31 Mar 2017 11:47:39 +0200 Subject: udp: use sk_protocol instead of pcflag to detect udplite sockets In the udp_sock struct, the 'forward_deficit' and 'pcflag' fields share the same cacheline. While the first is dirtied by udp_recvmsg, the latter is read, possibly several times, by the bottom half processing to discriminate between udp and udplite sockets. With this patch, sk->sk_protocol is used to check is the socket is really an udplite one, avoiding some cache misses per packet and improving the performance under udp_flood with small packet up to 10%. Signed-off-by: Paolo Abeni Signed-off-by: David S. Miller --- include/linux/udp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/udp.h b/include/linux/udp.h index c0f530809d1f..6cb4061a720d 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -115,6 +115,6 @@ static inline bool udp_get_no_check6_rx(struct sock *sk) #define udp_portaddr_for_each_entry_rcu(__sk, list) \ hlist_for_each_entry_rcu(__sk, list, __sk_common.skc_portaddr_node) -#define IS_UDPLITE(__sk) (udp_sk(__sk)->pcflag) +#define IS_UDPLITE(__sk) (__sk->sk_protocol == IPPROTO_UDPLITE) #endif /* _LINUX_UDP_H */ -- cgit From 39eb8cd1758886072ceb93607827722a11592ba2 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 31 Mar 2017 07:13:59 -0700 Subject: net: mpls: rt_nhn_alive and nh_flags should be accessed using READ_ONCE The number of alive nexthops for a route (rt->rt_nhn_alive) and the flags for a next hop (nh->nh_flags) are modified by netdev event handlers. The event handlers run with rtnl_lock held so updates are always done with the lock held. The packet path accesses the fields under the rcu lock. Since those fields can change at any moment in the packet path, both fields should be accessed using READ_ONCE. Updates to both fields should use WRITE_ONCE. Update mpls_select_multipath (packet path) and mpls_ifdown and mpls_ifup (event handlers) accordingly. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/mpls/af_mpls.c | 36 +++++++++++++++++++++++++----------- net/mpls/internal.h | 8 ++++++++ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 06ffafde70da..6bdd2f95b576 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -189,10 +189,15 @@ static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb) return hash; } +/* number of alive nexthops (rt->rt_nhn_alive) and the flags for + * a next hop (nh->nh_flags) are modified by netdev event handlers. + * Since those fields can change at any moment, use READ_ONCE to + * access both. + */ static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt, struct sk_buff *skb) { - int alive = ACCESS_ONCE(rt->rt_nhn_alive); + unsigned int alive; u32 hash = 0; int nh_index = 0; int n = 0; @@ -203,7 +208,8 @@ static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt, if (rt->rt_nhn == 1) goto out; - if (alive <= 0) + alive = READ_ONCE(rt->rt_nhn_alive); + if (alive == 0) return NULL; hash = mpls_multipath_hash(rt, skb); @@ -211,7 +217,9 @@ static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt, if (alive == rt->rt_nhn) goto out; for_nexthops(rt) { - if (nh->nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) + unsigned int nh_flags = READ_ONCE(nh->nh_flags); + + if (nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN)) continue; if (n == nh_index) return nh; @@ -1302,7 +1310,6 @@ static void mpls_ifdown(struct net_device *dev, int event) { struct mpls_route __rcu **platform_label; struct net *net = dev_net(dev); - unsigned int nh_flags = RTNH_F_DEAD | RTNH_F_LINKDOWN; unsigned int alive, deleted; unsigned index; @@ -1316,22 +1323,27 @@ static void mpls_ifdown(struct net_device *dev, int event) alive = 0; deleted = 0; change_nexthops(rt) { + unsigned int nh_flags = nh->nh_flags; + if (rtnl_dereference(nh->nh_dev) != dev) goto next; switch (event) { case NETDEV_DOWN: case NETDEV_UNREGISTER: - nh->nh_flags |= RTNH_F_DEAD; + nh_flags |= RTNH_F_DEAD; /* fall through */ case NETDEV_CHANGE: - nh->nh_flags |= RTNH_F_LINKDOWN; + nh_flags |= RTNH_F_LINKDOWN; break; } if (event == NETDEV_UNREGISTER) RCU_INIT_POINTER(nh->nh_dev, NULL); + + if (nh->nh_flags != nh_flags) + WRITE_ONCE(nh->nh_flags, nh_flags); next: - if (!(nh->nh_flags & nh_flags)) + if (!(nh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))) alive++; if (!rtnl_dereference(nh->nh_dev)) deleted++; @@ -1345,7 +1357,7 @@ next: } } -static void mpls_ifup(struct net_device *dev, unsigned int nh_flags) +static void mpls_ifup(struct net_device *dev, unsigned int flags) { struct mpls_route __rcu **platform_label; struct net *net = dev_net(dev); @@ -1361,20 +1373,22 @@ static void mpls_ifup(struct net_device *dev, unsigned int nh_flags) alive = 0; change_nexthops(rt) { + unsigned int nh_flags = nh->nh_flags; struct net_device *nh_dev = rtnl_dereference(nh->nh_dev); - if (!(nh->nh_flags & nh_flags)) { + if (!(nh_flags & flags)) { alive++; continue; } if (nh_dev != dev) continue; alive++; - nh->nh_flags &= ~nh_flags; + nh_flags &= ~flags; + WRITE_ONCE(nh->nh_flags, flags); } endfor_nexthops(rt); - ACCESS_ONCE(rt->rt_nhn_alive) = alive; + WRITE_ONCE(rt->rt_nhn_alive, alive); } } diff --git a/net/mpls/internal.h b/net/mpls/internal.h index 62928d8fabd1..91419fe63464 100644 --- a/net/mpls/internal.h +++ b/net/mpls/internal.h @@ -83,6 +83,10 @@ enum mpls_payload_type { struct mpls_nh { /* next hop label forwarding entry */ struct net_device __rcu *nh_dev; + + /* nh_flags is accessed under RCU in the packet path; it is + * modified handling netdev events with rtnl lock held + */ unsigned int nh_flags; u32 nh_label[MAX_NEW_LABELS]; u8 nh_labels; @@ -124,6 +128,10 @@ struct mpls_route { /* next hop label forwarding entry */ u8 rt_max_alen; u8 rt_ttl_propagate; unsigned int rt_nhn; + + /* rt_nhn_alive is accessed under RCU in the packet path; it + * is modified handling netdev events with rtnl lock held + */ unsigned int rt_nhn_alive; struct mpls_nh rt_nh[0]; }; -- cgit From 77ef013aadadd248843ad90022f36ba177b24e7f Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 31 Mar 2017 07:14:00 -0700 Subject: net: mpls: Convert number of nexthops to u8 Number of nexthops and number of alive nexthops are tracked using an unsigned int. A route should never have more than 255 nexthops so convert both to u8. Update all references and intermediate variables to consistently use u8 as well. Shrinks the size of mpls_route from 32 bytes to 24 bytes with a 2-byte hole before the nexthops. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/mpls/af_mpls.c | 28 +++++++++++++++++----------- net/mpls/internal.h | 5 +++-- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 6bdd2f95b576..665dec84f001 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -197,10 +197,10 @@ static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb) static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt, struct sk_buff *skb) { - unsigned int alive; u32 hash = 0; int nh_index = 0; int n = 0; + u8 alive; /* No need to look further into packet if there's only * one path @@ -466,7 +466,7 @@ struct mpls_route_config { int rc_mp_len; }; -static struct mpls_route *mpls_rt_alloc(int num_nh, u8 max_alen) +static struct mpls_route *mpls_rt_alloc(u8 num_nh, u8 max_alen) { u8 max_alen_aligned = ALIGN(max_alen, VIA_ALEN_ALIGN); struct mpls_route *rt; @@ -744,11 +744,11 @@ errout: return err; } -static int mpls_count_nexthops(struct rtnexthop *rtnh, int len, - u8 cfg_via_alen, u8 *max_via_alen) +static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len, + u8 cfg_via_alen, u8 *max_via_alen) { - int nhs = 0; int remaining = len; + u8 nhs = 0; if (!rtnh) { *max_via_alen = cfg_via_alen; @@ -773,7 +773,13 @@ static int mpls_count_nexthops(struct rtnexthop *rtnh, int len, via_alen); } + /* number of nexthops is tracked by a u8. + * Check for overflow. + */ + if (nhs == 255) + return 0; nhs++; + rtnh = rtnh_next(rtnh, &remaining); } @@ -787,8 +793,8 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg, struct rtnexthop *rtnh = cfg->rc_mp; struct nlattr *nla_via, *nla_newdst; int remaining = cfg->rc_mp_len; - int nhs = 0; int err = 0; + u8 nhs = 0; change_nexthops(rt) { int attrlen; @@ -842,7 +848,7 @@ static int mpls_route_add(struct mpls_route_config *cfg) int err = -EINVAL; u8 max_via_alen; unsigned index; - int nhs; + u8 nhs; index = cfg->rc_label; @@ -1310,7 +1316,7 @@ static void mpls_ifdown(struct net_device *dev, int event) { struct mpls_route __rcu **platform_label; struct net *net = dev_net(dev); - unsigned int alive, deleted; + u8 alive, deleted; unsigned index; platform_label = rtnl_dereference(net->mpls.platform_label); @@ -1362,7 +1368,7 @@ static void mpls_ifup(struct net_device *dev, unsigned int flags) struct mpls_route __rcu **platform_label; struct net *net = dev_net(dev); unsigned index; - int alive; + u8 alive; platform_label = rtnl_dereference(net->mpls.platform_label); for (index = 0; index < net->mpls.platform_labels; index++) { @@ -1786,8 +1792,8 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, } else { struct rtnexthop *rtnh; struct nlattr *mp; - int dead = 0; - int linkdown = 0; + u8 linkdown = 0; + u8 dead = 0; mp = nla_nest_start(skb, RTA_MULTIPATH); if (!mp) diff --git a/net/mpls/internal.h b/net/mpls/internal.h index 91419fe63464..2ac97433c3b7 100644 --- a/net/mpls/internal.h +++ b/net/mpls/internal.h @@ -127,12 +127,13 @@ struct mpls_route { /* next hop label forwarding entry */ u8 rt_payload_type; u8 rt_max_alen; u8 rt_ttl_propagate; - unsigned int rt_nhn; + u8 rt_nhn; /* rt_nhn_alive is accessed under RCU in the packet path; it * is modified handling netdev events with rtnl lock held */ - unsigned int rt_nhn_alive; + u8 rt_nhn_alive; + u16 rt_reserved1; struct mpls_nh rt_nh[0]; }; -- cgit From 59b209667a3bef240ca5da111ce1931f29fa179f Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 31 Mar 2017 07:14:01 -0700 Subject: net: mpls: change mpls_route layout Move labels to the end of mpls_nh as a 0-sized array and within mpls_route move the via for a nexthop after the mpls_nh. The new layout becomes: +----------------------+ | mpls_route | +----------------------+ | mpls_nh 0 | +----------------------+ | alignment padding | 4 bytes for odd number of labels; 0 for even +----------------------+ | via[rt_max_alen] 0 | +----------------------+ | alignment padding | via's aligned on sizeof(unsigned long) +----------------------+ | ... | +----------------------+ | mpls_nh n-1 | +----------------------+ | via[rt_max_alen] n-1 | +----------------------+ Memory allocated for nexthop + via is constant across all nexthops and their via. It is based on the maximum number of labels across all nexthops and the maximum via length. The size is saved in the mpls_route as rt_nh_size. Accessing a nexthop becomes rt->rt_nh + index * rt->rt_nh_size. The offset of the via address from a nexthop is saved as rt_via_offset so that given an mpls_nh pointer the via for that hop is simply nh + rt->rt_via_offset. With prior code, memory allocated per mpls_route with 1 nexthop: via is an ethernet address - 64 bytes via is an ipv4 address - 64 via is an ipv6 address - 72 With this patch set, memory allocated per mpls_route with 1 nexthop and 1 or 2 labels: via is an ethernet address - 56 bytes via is an ipv4 address - 56 via is an ipv6 address - 64 The 8-byte reduction is due to the previous patch; the change introduced by this patch has no impact on the size of allocations for 1 or 2 labels. Performance impact of this change was examined using network namespaces with veth pairs connecting namespaces. ns0 inserts the packet to the label-switched path using an lwt route with encap mpls. ns1 adds 1 or 2 labels depending on test, ns2 (and ns3 for 2-label test) pops the label and forwards. ns3 (or ns4) for a 2-label is the destination. Similar series of namespaces used for 2-nexthop test. Intent is to measure changes to latency (overhead in manipulating the packet) in the forwarding path. Tests used netperf with UDP_RR. IPv4: current patches 1 label, 1 nexthop 29908 30115 2 label, 1 nexthop 29071 29612 1 label, 2 nexthop 29582 29776 2 label, 2 nexthop 29086 29149 IPv6: current patches 1 label, 1 nexthop 24502 24960 2 label, 1 nexthop 24041 24407 1 label, 2 nexthop 23795 23899 2 label, 2 nexthop 23074 22959 In short, the change has no effect to a modest increase in performance. This is expected since this patch does not really have an impact on routes with 1 or 2 labels (the current limit) and 1 or 2 nexthops. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/mpls/af_mpls.c | 37 +++++++++++++++++++++---------------- net/mpls/internal.h | 45 ++++++++++++++++++++++++++++++--------------- 2 files changed, 51 insertions(+), 31 deletions(-) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 665dec84f001..1863b94133e4 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -24,6 +24,8 @@ #include #include "internal.h" +#define MAX_NEW_LABELS 2 + /* Maximum number of labels to look ahead at when selecting a path of * a multipath route */ @@ -60,10 +62,7 @@ EXPORT_SYMBOL_GPL(mpls_output_possible); static u8 *__mpls_nh_via(struct mpls_route *rt, struct mpls_nh *nh) { - u8 *nh0_via = PTR_ALIGN((u8 *)&rt->rt_nh[rt->rt_nhn], VIA_ALEN_ALIGN); - int nh_index = nh - rt->rt_nh; - - return nh0_via + rt->rt_max_alen * nh_index; + return (u8 *)nh + rt->rt_via_offset; } static const u8 *mpls_nh_via(const struct mpls_route *rt, @@ -189,6 +188,11 @@ static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb) return hash; } +static struct mpls_nh *mpls_get_nexthop(struct mpls_route *rt, u8 index) +{ + return (struct mpls_nh *)((u8 *)rt->rt_nh + index * rt->rt_nh_size); +} + /* number of alive nexthops (rt->rt_nhn_alive) and the flags for * a next hop (nh->nh_flags) are modified by netdev event handlers. * Since those fields can change at any moment, use READ_ONCE to @@ -206,7 +210,7 @@ static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt, * one path */ if (rt->rt_nhn == 1) - goto out; + return rt->rt_nh; alive = READ_ONCE(rt->rt_nhn_alive); if (alive == 0) @@ -227,7 +231,7 @@ static struct mpls_nh *mpls_select_multipath(struct mpls_route *rt, } endfor_nexthops(rt); out: - return &rt->rt_nh[nh_index]; + return mpls_get_nexthop(rt, nh_index); } static bool mpls_egress(struct net *net, struct mpls_route *rt, @@ -466,19 +470,20 @@ struct mpls_route_config { int rc_mp_len; }; -static struct mpls_route *mpls_rt_alloc(u8 num_nh, u8 max_alen) +/* all nexthops within a route have the same size based on max + * number of labels and max via length for a hop + */ +static struct mpls_route *mpls_rt_alloc(u8 num_nh, u8 max_alen, u8 max_labels) { - u8 max_alen_aligned = ALIGN(max_alen, VIA_ALEN_ALIGN); + u8 nh_size = MPLS_NH_SIZE(max_labels, max_alen); struct mpls_route *rt; - rt = kzalloc(ALIGN(sizeof(*rt) + num_nh * sizeof(*rt->rt_nh), - VIA_ALEN_ALIGN) + - num_nh * max_alen_aligned, - GFP_KERNEL); + rt = kzalloc(sizeof(*rt) + num_nh * nh_size, GFP_KERNEL); if (rt) { rt->rt_nhn = num_nh; rt->rt_nhn_alive = num_nh; - rt->rt_max_alen = max_alen_aligned; + rt->rt_nh_size = nh_size; + rt->rt_via_offset = MPLS_NH_VIA_OFF(max_labels); } return rt; @@ -892,7 +897,7 @@ static int mpls_route_add(struct mpls_route_config *cfg) goto errout; err = -ENOMEM; - rt = mpls_rt_alloc(nhs, max_via_alen); + rt = mpls_rt_alloc(nhs, max_via_alen, MAX_NEW_LABELS); if (!rt) goto errout; @@ -1964,7 +1969,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) /* In case the predefined labels need to be populated */ if (limit > MPLS_LABEL_IPV4NULL) { struct net_device *lo = net->loopback_dev; - rt0 = mpls_rt_alloc(1, lo->addr_len); + rt0 = mpls_rt_alloc(1, lo->addr_len, MAX_NEW_LABELS); if (!rt0) goto nort0; RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo); @@ -1978,7 +1983,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) } if (limit > MPLS_LABEL_IPV6NULL) { struct net_device *lo = net->loopback_dev; - rt2 = mpls_rt_alloc(1, lo->addr_len); + rt2 = mpls_rt_alloc(1, lo->addr_len, MAX_NEW_LABELS); if (!rt2) goto nort2; RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo); diff --git a/net/mpls/internal.h b/net/mpls/internal.h index 2ac97433c3b7..cc324c022049 100644 --- a/net/mpls/internal.h +++ b/net/mpls/internal.h @@ -64,7 +64,6 @@ struct mpls_dev { struct sk_buff; #define LABEL_NOT_SPECIFIED (1 << 20) -#define MAX_NEW_LABELS 2 /* This maximum ha length copied from the definition of struct neighbour */ #define VIA_ALEN_ALIGN sizeof(unsigned long) @@ -88,12 +87,26 @@ struct mpls_nh { /* next hop label forwarding entry */ * modified handling netdev events with rtnl lock held */ unsigned int nh_flags; - u32 nh_label[MAX_NEW_LABELS]; u8 nh_labels; u8 nh_via_alen; u8 nh_via_table; + u8 nh_reserved1; + + u32 nh_label[0]; }; +/* offset of via from beginning of mpls_nh */ +#define MPLS_NH_VIA_OFF(num_labels) \ + ALIGN(sizeof(struct mpls_nh) + (num_labels) * sizeof(u32), \ + VIA_ALEN_ALIGN) + +/* all nexthops within a route have the same size based on the + * max number of labels and max via length across all nexthops + */ +#define MPLS_NH_SIZE(num_labels, max_via_alen) \ + (MPLS_NH_VIA_OFF((num_labels)) + \ + ALIGN((max_via_alen), VIA_ALEN_ALIGN)) + enum mpls_ttl_propagation { MPLS_TTL_PROP_DEFAULT, MPLS_TTL_PROP_ENABLED, @@ -108,16 +121,16 @@ enum mpls_ttl_propagation { * +----------------------+ * | mpls_nh 0 | * +----------------------+ - * | ... | - * +----------------------+ - * | mpls_nh n-1 | - * +----------------------+ - * | alignment padding | + * | alignment padding | 4 bytes for odd number of labels * +----------------------+ * | via[rt_max_alen] 0 | * +----------------------+ + * | alignment padding | via's aligned on sizeof(unsigned long) + * +----------------------+ * | ... | * +----------------------+ + * | mpls_nh n-1 | + * +----------------------+ * | via[rt_max_alen] n-1 | * +----------------------+ */ @@ -128,26 +141,28 @@ struct mpls_route { /* next hop label forwarding entry */ u8 rt_max_alen; u8 rt_ttl_propagate; u8 rt_nhn; - /* rt_nhn_alive is accessed under RCU in the packet path; it * is modified handling netdev events with rtnl lock held */ u8 rt_nhn_alive; - u16 rt_reserved1; + u8 rt_nh_size; + u8 rt_via_offset; + u8 rt_reserved1; struct mpls_nh rt_nh[0]; }; #define for_nexthops(rt) { \ - int nhsel; struct mpls_nh *nh; \ - for (nhsel = 0, nh = (rt)->rt_nh; \ + int nhsel; struct mpls_nh *nh; u8 *__nh; \ + for (nhsel = 0, nh = (rt)->rt_nh, __nh = (u8 *)((rt)->rt_nh); \ nhsel < (rt)->rt_nhn; \ - nh++, nhsel++) + __nh += rt->rt_nh_size, nh = (struct mpls_nh *)__nh, nhsel++) #define change_nexthops(rt) { \ - int nhsel; struct mpls_nh *nh; \ - for (nhsel = 0, nh = (struct mpls_nh *)((rt)->rt_nh); \ + int nhsel; struct mpls_nh *nh; u8 *__nh; \ + for (nhsel = 0, nh = (struct mpls_nh *)((rt)->rt_nh), \ + __nh = (u8 *)((rt)->rt_nh); \ nhsel < (rt)->rt_nhn; \ - nh++, nhsel++) + __nh += rt->rt_nh_size, nh = (struct mpls_nh *)__nh, nhsel++) #define endfor_nexthops(rt) } -- cgit From df1c631648c55bfb247339279f9bc573c7f283f4 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 31 Mar 2017 07:14:02 -0700 Subject: net: mpls: Limit memory allocation for mpls_route Limit memory allocation size for mpls_route to 4096. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/mpls/af_mpls.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 1863b94133e4..f84c52b6eafc 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -26,6 +26,9 @@ #define MAX_NEW_LABELS 2 +/* max memory we will use for mpls_route */ +#define MAX_MPLS_ROUTE_MEM 4096 + /* Maximum number of labels to look ahead at when selecting a path of * a multipath route */ @@ -477,14 +480,20 @@ static struct mpls_route *mpls_rt_alloc(u8 num_nh, u8 max_alen, u8 max_labels) { u8 nh_size = MPLS_NH_SIZE(max_labels, max_alen); struct mpls_route *rt; + size_t size; - rt = kzalloc(sizeof(*rt) + num_nh * nh_size, GFP_KERNEL); - if (rt) { - rt->rt_nhn = num_nh; - rt->rt_nhn_alive = num_nh; - rt->rt_nh_size = nh_size; - rt->rt_via_offset = MPLS_NH_VIA_OFF(max_labels); - } + size = sizeof(*rt) + num_nh * nh_size; + if (size > MAX_MPLS_ROUTE_MEM) + return ERR_PTR(-EINVAL); + + rt = kzalloc(size, GFP_KERNEL); + if (!rt) + return ERR_PTR(-ENOMEM); + + rt->rt_nhn = num_nh; + rt->rt_nhn_alive = num_nh; + rt->rt_nh_size = nh_size; + rt->rt_via_offset = MPLS_NH_VIA_OFF(max_labels); return rt; } @@ -898,8 +907,10 @@ static int mpls_route_add(struct mpls_route_config *cfg) err = -ENOMEM; rt = mpls_rt_alloc(nhs, max_via_alen, MAX_NEW_LABELS); - if (!rt) + if (IS_ERR(rt)) { + err = PTR_ERR(rt); goto errout; + } rt->rt_protocol = cfg->rc_protocol; rt->rt_payload_type = cfg->rc_payload_type; @@ -1970,7 +1981,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) if (limit > MPLS_LABEL_IPV4NULL) { struct net_device *lo = net->loopback_dev; rt0 = mpls_rt_alloc(1, lo->addr_len, MAX_NEW_LABELS); - if (!rt0) + if (IS_ERR(rt0)) goto nort0; RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo); rt0->rt_protocol = RTPROT_KERNEL; @@ -1984,7 +1995,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) if (limit > MPLS_LABEL_IPV6NULL) { struct net_device *lo = net->loopback_dev; rt2 = mpls_rt_alloc(1, lo->addr_len, MAX_NEW_LABELS); - if (!rt2) + if (IS_ERR(rt2)) goto nort2; RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo); rt2->rt_protocol = RTPROT_KERNEL; -- cgit From a4ac8c986d3f72ccbaf6d6782511fb645e568306 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 31 Mar 2017 07:14:03 -0700 Subject: net: mpls: bump maximum number of labels Allow users to push down more labels per MPLS route. With the previous patches, no memory allocations are based on MAX_NEW_LABELS; the limit is only used to keep userspace in check. At this point MAX_NEW_LABELS is only used for mpls_route_config (copying route data from userspace) and processing nexthops looking for the max number of labels across the route spec. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/mpls/af_mpls.c | 103 +++++++++++++++++++++++++++++++++++----------------- net/mpls/internal.h | 2 +- 2 files changed, 71 insertions(+), 34 deletions(-) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index f84c52b6eafc..10daefd7f938 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -24,7 +24,10 @@ #include #include "internal.h" -#define MAX_NEW_LABELS 2 +/* put a reasonable limit on the number of labels + * we will accept from userspace + */ +#define MAX_NEW_LABELS 30 /* max memory we will use for mpls_route */ #define MAX_MPLS_ROUTE_MEM 4096 @@ -698,9 +701,6 @@ static int mpls_nh_build_from_cfg(struct mpls_route_config *cfg, return -ENOMEM; err = -EINVAL; - /* Ensure only a supported number of labels are present */ - if (cfg->rc_output_labels > MAX_NEW_LABELS) - goto errout; nh->nh_labels = cfg->rc_output_labels; for (i = 0; i < nh->nh_labels; i++) @@ -725,7 +725,7 @@ errout: static int mpls_nh_build(struct net *net, struct mpls_route *rt, struct mpls_nh *nh, int oif, struct nlattr *via, - struct nlattr *newdst) + struct nlattr *newdst, u8 max_labels) { int err = -ENOMEM; @@ -733,7 +733,7 @@ static int mpls_nh_build(struct net *net, struct mpls_route *rt, goto errout; if (newdst) { - err = nla_get_labels(newdst, MAX_NEW_LABELS, + err = nla_get_labels(newdst, max_labels, &nh->nh_labels, nh->nh_label); if (err) goto errout; @@ -759,21 +759,19 @@ errout: } static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len, - u8 cfg_via_alen, u8 *max_via_alen) + u8 cfg_via_alen, u8 *max_via_alen, + u8 *max_labels) { int remaining = len; u8 nhs = 0; - if (!rtnh) { - *max_via_alen = cfg_via_alen; - return 1; - } - *max_via_alen = 0; + *max_labels = 0; while (rtnh_ok(rtnh, remaining)) { struct nlattr *nla, *attrs = rtnh_attrs(rtnh); int attrlen; + u8 n_labels = 0; attrlen = rtnh_attrlen(rtnh); nla = nla_find(attrs, attrlen, RTA_VIA); @@ -787,6 +785,13 @@ static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len, via_alen); } + nla = nla_find(attrs, attrlen, RTA_NEWDST); + if (nla && + nla_get_labels(nla, MAX_NEW_LABELS, &n_labels, NULL) != 0) + return 0; + + *max_labels = max_t(u8, *max_labels, n_labels); + /* number of nexthops is tracked by a u8. * Check for overflow. */ @@ -802,7 +807,7 @@ static u8 mpls_count_nexthops(struct rtnexthop *rtnh, int len, } static int mpls_nh_build_multi(struct mpls_route_config *cfg, - struct mpls_route *rt) + struct mpls_route *rt, u8 max_labels) { struct rtnexthop *rtnh = cfg->rc_mp; struct nlattr *nla_via, *nla_newdst; @@ -835,7 +840,8 @@ static int mpls_nh_build_multi(struct mpls_route_config *cfg, } err = mpls_nh_build(cfg->rc_nlinfo.nl_net, rt, nh, - rtnh->rtnh_ifindex, nla_via, nla_newdst); + rtnh->rtnh_ifindex, nla_via, nla_newdst, + max_labels); if (err) goto errout; @@ -862,6 +868,7 @@ static int mpls_route_add(struct mpls_route_config *cfg) int err = -EINVAL; u8 max_via_alen; unsigned index; + u8 max_labels; u8 nhs; index = cfg->rc_label; @@ -900,13 +907,21 @@ static int mpls_route_add(struct mpls_route_config *cfg) goto errout; err = -EINVAL; - nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len, - cfg->rc_via_alen, &max_via_alen); + if (cfg->rc_mp) { + nhs = mpls_count_nexthops(cfg->rc_mp, cfg->rc_mp_len, + cfg->rc_via_alen, &max_via_alen, + &max_labels); + } else { + max_via_alen = cfg->rc_via_alen; + max_labels = cfg->rc_output_labels; + nhs = 1; + } + if (nhs == 0) goto errout; err = -ENOMEM; - rt = mpls_rt_alloc(nhs, max_via_alen, MAX_NEW_LABELS); + rt = mpls_rt_alloc(nhs, max_via_alen, max_labels); if (IS_ERR(rt)) { err = PTR_ERR(rt); goto errout; @@ -917,7 +932,7 @@ static int mpls_route_add(struct mpls_route_config *cfg) rt->rt_ttl_propagate = cfg->rc_ttl_propagate; if (cfg->rc_mp) - err = mpls_nh_build_multi(cfg, rt); + err = mpls_nh_build_multi(cfg, rt, max_labels); else err = mpls_nh_build_from_cfg(cfg, rt); if (err) @@ -1531,16 +1546,18 @@ int nla_put_labels(struct sk_buff *skb, int attrtype, EXPORT_SYMBOL_GPL(nla_put_labels); int nla_get_labels(const struct nlattr *nla, - u32 max_labels, u8 *labels, u32 label[]) + u8 max_labels, u8 *labels, u32 label[]) { unsigned len = nla_len(nla); - unsigned nla_labels; struct mpls_shim_hdr *nla_label; + u8 nla_labels; bool bos; int i; - /* len needs to be an even multiple of 4 (the label size) */ - if (len & 3) + /* len needs to be an even multiple of 4 (the label size). Number + * of labels is a u8 so check for overflow. + */ + if (len & 3 || len / 4 > 255) return -EINVAL; /* Limit the number of new labels allowed */ @@ -1548,6 +1565,10 @@ int nla_get_labels(const struct nlattr *nla, if (nla_labels > max_labels) return -EINVAL; + /* when label == NULL, caller wants number of labels */ + if (!label) + goto out; + nla_label = nla_data(nla); bos = true; for (i = nla_labels - 1; i >= 0; i--, bos = false) { @@ -1571,6 +1592,7 @@ int nla_get_labels(const struct nlattr *nla, label[i] = dec.label; } +out: *labels = nla_labels; return 0; } @@ -1632,7 +1654,6 @@ static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, err = -EINVAL; rtm = nlmsg_data(nlh); - memset(cfg, 0, sizeof(*cfg)); if (rtm->rtm_family != AF_MPLS) goto errout; @@ -1731,27 +1752,43 @@ errout: static int mpls_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) { - struct mpls_route_config cfg; + struct mpls_route_config *cfg; int err; - err = rtm_to_route_config(skb, nlh, &cfg); + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + err = rtm_to_route_config(skb, nlh, cfg); if (err < 0) - return err; + goto out; - return mpls_route_del(&cfg); + err = mpls_route_del(cfg); +out: + kfree(cfg); + + return err; } static int mpls_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) { - struct mpls_route_config cfg; + struct mpls_route_config *cfg; int err; - err = rtm_to_route_config(skb, nlh, &cfg); + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + err = rtm_to_route_config(skb, nlh, cfg); if (err < 0) - return err; + goto out; - return mpls_route_add(&cfg); + err = mpls_route_add(cfg); +out: + kfree(cfg); + + return err; } static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, @@ -1980,7 +2017,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) /* In case the predefined labels need to be populated */ if (limit > MPLS_LABEL_IPV4NULL) { struct net_device *lo = net->loopback_dev; - rt0 = mpls_rt_alloc(1, lo->addr_len, MAX_NEW_LABELS); + rt0 = mpls_rt_alloc(1, lo->addr_len, 0); if (IS_ERR(rt0)) goto nort0; RCU_INIT_POINTER(rt0->rt_nh->nh_dev, lo); @@ -1994,7 +2031,7 @@ static int resize_platform_label_table(struct net *net, size_t limit) } if (limit > MPLS_LABEL_IPV6NULL) { struct net_device *lo = net->loopback_dev; - rt2 = mpls_rt_alloc(1, lo->addr_len, MAX_NEW_LABELS); + rt2 = mpls_rt_alloc(1, lo->addr_len, 0); if (IS_ERR(rt2)) goto nort2; RCU_INIT_POINTER(rt2->rt_nh->nh_dev, lo); diff --git a/net/mpls/internal.h b/net/mpls/internal.h index cc324c022049..c5d2f5bc37ec 100644 --- a/net/mpls/internal.h +++ b/net/mpls/internal.h @@ -197,7 +197,7 @@ static inline struct mpls_dev *mpls_dev_get(const struct net_device *dev) int nla_put_labels(struct sk_buff *skb, int attrtype, u8 labels, const u32 label[]); -int nla_get_labels(const struct nlattr *nla, u32 max_labels, u8 *labels, +int nla_get_labels(const struct nlattr *nla, u8 max_labels, u8 *labels, u32 label[]); int nla_get_via(const struct nlattr *nla, u8 *via_alen, u8 *via_table, u8 via[]); -- cgit From 1511009cd68015c2e04135bfefa4bf5020baa8d9 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 31 Mar 2017 07:14:04 -0700 Subject: net: mpls: Increase max number of labels for lwt encap Alow users to push down more labels per MPLS encap. Similar to LSR case, move label array to the end of mpls_iptunnel_encap and allocate based on the number of labels for the route. For consistency with the LSR case, re-use the same maximum number of labels. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/net/mpls_iptunnel.h | 5 ++--- net/mpls/af_mpls.c | 5 ----- net/mpls/internal.h | 5 +++++ net/mpls/mpls_iptunnel.c | 13 ++++++++++--- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/include/net/mpls_iptunnel.h b/include/net/mpls_iptunnel.h index a18af6a16eb5..9d22bf67ac86 100644 --- a/include/net/mpls_iptunnel.h +++ b/include/net/mpls_iptunnel.h @@ -14,13 +14,12 @@ #ifndef _NET_MPLS_IPTUNNEL_H #define _NET_MPLS_IPTUNNEL_H 1 -#define MAX_NEW_LABELS 2 - struct mpls_iptunnel_encap { - u32 label[MAX_NEW_LABELS]; u8 labels; u8 ttl_propagate; u8 default_ttl; + u8 reserved1; + u32 label[0]; }; static inline struct mpls_iptunnel_encap *mpls_lwtunnel_encap(struct lwtunnel_state *lwtstate) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 10daefd7f938..5928d22ba9c8 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -24,11 +24,6 @@ #include #include "internal.h" -/* put a reasonable limit on the number of labels - * we will accept from userspace - */ -#define MAX_NEW_LABELS 30 - /* max memory we will use for mpls_route */ #define MAX_MPLS_ROUTE_MEM 4096 diff --git a/net/mpls/internal.h b/net/mpls/internal.h index c5d2f5bc37ec..4db6a5971322 100644 --- a/net/mpls/internal.h +++ b/net/mpls/internal.h @@ -2,6 +2,11 @@ #define MPLS_INTERNAL_H #include +/* put a reasonable limit on the number of labels + * we will accept from userspace + */ +#define MAX_NEW_LABELS 30 + struct mpls_entry_decoded { u32 label; u8 ttl; diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c index 22f71fce0bfb..fe00e98667cf 100644 --- a/net/mpls/mpls_iptunnel.c +++ b/net/mpls/mpls_iptunnel.c @@ -164,6 +164,7 @@ static int mpls_build_state(struct nlattr *nla, struct mpls_iptunnel_encap *tun_encap_info; struct nlattr *tb[MPLS_IPTUNNEL_MAX + 1]; struct lwtunnel_state *newts; + u8 n_labels; int ret; ret = nla_parse_nested(tb, MPLS_IPTUNNEL_MAX, nla, @@ -175,12 +176,18 @@ static int mpls_build_state(struct nlattr *nla, return -EINVAL; - newts = lwtunnel_state_alloc(sizeof(*tun_encap_info)); + /* determine number of labels */ + if (nla_get_labels(tb[MPLS_IPTUNNEL_DST], + MAX_NEW_LABELS, &n_labels, NULL)) + return -EINVAL; + + newts = lwtunnel_state_alloc(sizeof(*tun_encap_info) + + n_labels * sizeof(u32)); if (!newts) return -ENOMEM; tun_encap_info = mpls_lwtunnel_encap(newts); - ret = nla_get_labels(tb[MPLS_IPTUNNEL_DST], MAX_NEW_LABELS, + ret = nla_get_labels(tb[MPLS_IPTUNNEL_DST], n_labels, &tun_encap_info->labels, tun_encap_info->label); if (ret) goto errout; @@ -257,7 +264,7 @@ static int mpls_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b) a_hdr->default_ttl != b_hdr->default_ttl) return 1; - for (l = 0; l < MAX_NEW_LABELS; l++) + for (l = 0; l < a_hdr->labels; l++) if (a_hdr->label[l] != b_hdr->label[l]) return 1; return 0; -- cgit From 44781fef1378969ea974b1b000ce7f3167eee459 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Fri, 31 Mar 2017 14:22:02 +0100 Subject: net: stmmac: fix cbs configuration Sending again, because forgot to include net-dev. The QoS IP does not accept AVB capabilities to default/queue 0, this way we guarantee 75% bandwidth for AVB. This patch assures that only queues >= 1 gets CBS confgured. Additional info was also added to stmmac.txt. Reported-by: Niklas Cassel Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 2 ++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index 784d98862b52..f652b0c384ce 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -103,6 +103,8 @@ Optional properties: - Choose one of these modes: - snps,dcb-algorithm: TX queue will be working in DCB - snps,avb-algorithm: TX queue will be working in AVB + [Attention] Queue 0 is reserved for legacy traffic + and so no AVB is available in this queue. - Configure Credit Base Shaper (if AVB Mode selected): - snps,send_slope: enable Low Power Interface - snps,idle_slope: unlock on WoL diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 43361f324229..c1c63197ff73 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1880,7 +1880,8 @@ static void stmmac_configure_cbs(struct stmmac_priv *priv) u32 mode_to_use; u32 queue; - for (queue = 0; queue < tx_queues_count; queue++) { + /* queue 0 is reserved for legacy traffic */ + for (queue = 1; queue < tx_queues_count; queue++) { mode_to_use = priv->plat->tx_queues_cfg[queue].mode_to_use; if (mode_to_use == MTL_QUEUE_DCB) continue; -- cgit From 956327913c2e23048d7a586414bafe7ec0c2a887 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 31 Mar 2017 13:09:38 -0700 Subject: drivers/net/ethernet/mellanox/mlx5/core/en_main.c: fix build with gcc-4.4.4 drivers/net/ethernet/mellanox/mlx5/core/en_main.c: In function 'mlx5e_redirect_rqts': drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2210: error: unknown field 'rqn' specified in initializer drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2211: warning: missing braces around initializer drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2211: warning: (near initialization for 'direct_rrp.') drivers/net/ethernet/mellanox/mlx5/core/en_main.c: In function 'mlx5e_redirect_rqts_to_channels': drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2227: error: unknown field 'rss' specified in initializer drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2227: warning: missing braces around initializer drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2227: warning: (near initialization for 'rrp.') drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2227: warning: initialization makes integer from pointer without a cast drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2228: error: unknown field 'rss' specified in initializer drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2229: warning: excess elements in struct initializer drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2229: warning: (near initialization for 'rrp') drivers/net/ethernet/mellanox/mlx5/core/en_main.c: In function 'mlx5e_redirect_rqts_to_drop': drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2238: error: unknown field 'rqn' specified in initializer drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2239: warning: missing braces around initializer drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2239: warning: (near initialization for 'drop_rrp.') gcc-4.4.4 has issues with anonymous union initializers. Work around this. Cc: Saeed Mahameed Cc: Tariq Toukan Signed-off-by: Andrew Morton Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 68d6c3c58ba7..ec389b1b51cb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2207,7 +2207,9 @@ static void mlx5e_redirect_rqts(struct mlx5e_priv *priv, for (ix = 0; ix < priv->profile->max_nch(priv->mdev); ix++) { struct mlx5e_redirect_rqt_param direct_rrp = { .is_rss = false, - .rqn = mlx5e_get_direct_rqn(priv, ix, rrp) + { + .rqn = mlx5e_get_direct_rqn(priv, ix, rrp) + }, }; /* Direct RQ Tables */ @@ -2224,8 +2226,12 @@ static void mlx5e_redirect_rqts_to_channels(struct mlx5e_priv *priv, { struct mlx5e_redirect_rqt_param rrp = { .is_rss = true, - .rss.channels = chs, - .rss.hfunc = chs->params.rss_hfunc + { + .rss = { + .channels = chs, + .hfunc = chs->params.rss_hfunc, + } + }, }; mlx5e_redirect_rqts(priv, rrp); @@ -2235,7 +2241,9 @@ static void mlx5e_redirect_rqts_to_drop(struct mlx5e_priv *priv) { struct mlx5e_redirect_rqt_param drop_rrp = { .is_rss = false, - .rqn = priv->drop_rq.rqn + { + .rqn = priv->drop_rq.rqn, + }, }; mlx5e_redirect_rqts(priv, drop_rrp); -- cgit From e270e966868530d2667ac0a0b1080560b2965408 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 31 Mar 2017 13:09:41 -0700 Subject: drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c: fix build with gcc-4.4.4 drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c: In function 'mlx5e_set_rxfh': drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c:1067: error: unknown field 'rss' specified in initializer drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c:1067: warning: missing braces around initializer drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c:1067: warning: (near initialization for 'rrp.') drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c:1068: error: unknown field 'rss' specified in initializer drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c:1069: warning: excess elements in struct initializer drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c:1069: warning: (near initialization for 'rrp') gcc-4.4.4 has issues with anonymous union initializers. Work around this. Cc: Saeed Mahameed Cc: Tariq Toukan Signed-off-by: Andrew Morton Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 40912937d211..af039b6c0799 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -1064,8 +1064,12 @@ static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir, u32 rqtn = priv->indir_rqt.rqtn; struct mlx5e_redirect_rqt_param rrp = { .is_rss = true, - .rss.hfunc = priv->channels.params.rss_hfunc, - .rss.channels = &priv->channels + { + .rss = { + .hfunc = priv->channels.params.rss_hfunc, + .channels = &priv->channels, + }, + }, }; mlx5e_redirect_rqt(priv, rqtn, MLX5E_INDIR_RQT_SIZE, rrp); -- cgit From d3fbff306c215946cdbcf9ace4d0b78e9f72b5c4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 31 Mar 2017 14:59:25 -0700 Subject: sock: correctly test SOCK_TIMESTAMP in sock_recv_ts_and_drops() It seems the code does not match the intent. This broke packetdrill, and probably other programs. Fixes: 6c7c98bad488 ("sock: avoid dirtying sk_stamp, if possible") Signed-off-by: Eric Dumazet Cc: Paolo Abeni Acked-by: Paolo Abeni Signed-off-by: David S. Miller --- include/net/sock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/sock.h b/include/net/sock.h index 8e53158a7d95..66349e49d468 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2250,7 +2250,7 @@ static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, if (sk->sk_flags & FLAGS_TS_OR_DROPS || sk->sk_tsflags & TSFLAGS_ANY) __sock_recv_ts_and_drops(msg, sk, skb); - else if (unlikely(sk->sk_flags & SOCK_TIMESTAMP)) + else if (unlikely(sock_flag(sk, SOCK_TIMESTAMP))) sk->sk_stamp = skb->tstamp; else if (unlikely(sk->sk_stamp == SK_DEFAULT_STAMP)) sk->sk_stamp = 0; -- cgit From e97656d03ca0cea888a0b9d382abce8233771f31 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Fri, 31 Mar 2017 15:56:30 -0700 Subject: rds: tcp: allow progress of rds_conn_shutdown if the rds_connection is marked ERROR by an intervening FIN rds_conn_shutdown() runs in workq context, and marks the rds_connection as DISCONNECTING before quiescing Tx/Rx paths. However, after all I/O has quiesced, we may still find the rds_connection state to be RDS_CONN_ERROR if an intervening FIN was processed in softirq context. This is not a fatal error: rds_conn_shutdown() should continue the shutdown, and there is no need to log noisy messages about this event. Signed-off-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller --- net/rds/connection.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/rds/connection.c b/net/rds/connection.c index 1fa75ab7b733..6a5ebdea7d2e 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -333,11 +333,19 @@ void rds_conn_shutdown(struct rds_conn_path *cp) rds_conn_path_reset(cp); if (!rds_conn_path_transition(cp, RDS_CONN_DISCONNECTING, + RDS_CONN_DOWN) && + !rds_conn_path_transition(cp, RDS_CONN_ERROR, RDS_CONN_DOWN)) { /* This can happen - eg when we're in the middle of tearing * down the connection, and someone unloads the rds module. - * Quite reproduceable with loopback connections. + * Quite reproducible with loopback connections. * Mostly harmless. + * + * Note that this also happens with rds-tcp because + * we could have triggered rds_conn_path_drop in irq + * mode from rds_tcp_state change on the receipt of + * a FIN, thus we need to recheck for RDS_CONN_ERROR + * here. */ rds_conn_path_error(cp, "%s: failed to transition " "to state DOWN, current state " -- cgit From 087d975353d0cc9b7536d6ef46cd9303fceb2e17 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Fri, 31 Mar 2017 15:56:31 -0700 Subject: rds: tcp: canonical connection order for all paths with index > 0 The rds_connect_worker() has a bug in the check that enforces the canonical connection order described in the comments of rds_tcp_state_change(). The intention is to make sure that all the multipath connections are always initiated by the smaller IP address via rds_start_mprds. To achieve this, rds_connection_worker should check that cp_index > 0. Signed-off-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller --- net/rds/threads.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rds/threads.c b/net/rds/threads.c index e36e333a0aa0..3e447d056d09 100644 --- a/net/rds/threads.c +++ b/net/rds/threads.c @@ -156,7 +156,7 @@ void rds_connect_worker(struct work_struct *work) struct rds_connection *conn = cp->cp_conn; int ret; - if (cp->cp_index > 1 && cp->cp_conn->c_laddr > cp->cp_conn->c_faddr) + if (cp->cp_index > 0 && cp->cp_conn->c_laddr > cp->cp_conn->c_faddr) return; clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags); ret = rds_conn_path_transition(cp, RDS_CONN_DOWN, RDS_CONN_CONNECTING); -- cgit From 2f249e99c7cda7107ac52f0319c89bed9a6431d3 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 1 Apr 2017 14:47:03 +0200 Subject: batman-adv: Use ethtool helper to get link status The ethtool_ops of batman-adv never contained more than a stub for the get_link function pointer. It was always returning that a link exists even when the devices was not yet up and therefore nothing resampling a link could have been available. Instead use the ethtool helper which returns the current carrier state. Signed-off-by: Sven Eckelmann Acked-by: Marek Lindner Signed-off-by: Simon Wunderlich --- net/batman-adv/soft-interface.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index a9dbcc1590bd..eff23d519c6f 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -69,7 +69,6 @@ static void batadv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info); static u32 batadv_get_msglevel(struct net_device *dev); static void batadv_set_msglevel(struct net_device *dev, u32 value); -static u32 batadv_get_link(struct net_device *dev); static void batadv_get_strings(struct net_device *dev, u32 stringset, u8 *data); static void batadv_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data); @@ -80,7 +79,7 @@ static const struct ethtool_ops batadv_ethtool_ops = { .get_drvinfo = batadv_get_drvinfo, .get_msglevel = batadv_get_msglevel, .set_msglevel = batadv_set_msglevel, - .get_link = batadv_get_link, + .get_link = ethtool_op_get_link, .get_strings = batadv_get_strings, .get_ethtool_stats = batadv_get_ethtool_stats, .get_sset_count = batadv_get_sset_count, @@ -1119,11 +1118,6 @@ static void batadv_set_msglevel(struct net_device *dev, u32 value) { } -static u32 batadv_get_link(struct net_device *dev) -{ - return 1; -} - /* Inspired by drivers/net/ethernet/dlink/sundance.c:1702 * Declare each description string in struct.name[] to get fixed sized buffer * and compile time checking for strings longer than ETH_GSTRING_LEN. -- cgit From 40ad9842cb35d9b7ddfea5b246fcabf17f275686 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 1 Apr 2017 14:47:04 +0200 Subject: batman-adv: Remove ethtool msglevel functions batadv devices don't support msglevel. The ethtool stubs therefore returned that it isn't supported. But instead, the complete function can be dropped to avoid that bogus values are shown in ethtool. Signed-off-by: Sven Eckelmann Acked-by: Marek Lindner Signed-off-by: Simon Wunderlich --- net/batman-adv/soft-interface.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index eff23d519c6f..3e0ee2afb814 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -67,8 +67,6 @@ static int batadv_get_settings(struct net_device *dev, struct ethtool_cmd *cmd); static void batadv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info); -static u32 batadv_get_msglevel(struct net_device *dev); -static void batadv_set_msglevel(struct net_device *dev, u32 value); static void batadv_get_strings(struct net_device *dev, u32 stringset, u8 *data); static void batadv_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data); @@ -77,8 +75,6 @@ static int batadv_get_sset_count(struct net_device *dev, int stringset); static const struct ethtool_ops batadv_ethtool_ops = { .get_settings = batadv_get_settings, .get_drvinfo = batadv_get_drvinfo, - .get_msglevel = batadv_get_msglevel, - .set_msglevel = batadv_set_msglevel, .get_link = ethtool_op_get_link, .get_strings = batadv_get_strings, .get_ethtool_stats = batadv_get_ethtool_stats, @@ -1109,15 +1105,6 @@ static void batadv_get_drvinfo(struct net_device *dev, strlcpy(info->bus_info, "batman", sizeof(info->bus_info)); } -static u32 batadv_get_msglevel(struct net_device *dev) -{ - return -EOPNOTSUPP; -} - -static void batadv_set_msglevel(struct net_device *dev, u32 value) -{ -} - /* Inspired by drivers/net/ethernet/dlink/sundance.c:1702 * Declare each description string in struct.name[] to get fixed sized buffer * and compile time checking for strings longer than ETH_GSTRING_LEN. -- cgit From e2790a4b275d52e332e0ad71b5f2eff53fe21805 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 1 Apr 2017 14:47:05 +0200 Subject: batman-adv: Remove ethtool .get_settings stub The .get_settings function pointer and the related API was deprecated. Fortunately, batman-adv is a virtual interface and never provided any useful information via .get_settings. The stub can therefore be removed. This also avoids that incorrect information is shown in ethtool about the batadv interface. Signed-off-by: Sven Eckelmann Acked-by: Marek Lindner Signed-off-by: Simon Wunderlich --- net/batman-adv/soft-interface.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 3e0ee2afb814..6f22b1283c92 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -64,7 +64,6 @@ #include "sysfs.h" #include "translation-table.h" -static int batadv_get_settings(struct net_device *dev, struct ethtool_cmd *cmd); static void batadv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info); static void batadv_get_strings(struct net_device *dev, u32 stringset, u8 *data); @@ -73,7 +72,6 @@ static void batadv_get_ethtool_stats(struct net_device *dev, static int batadv_get_sset_count(struct net_device *dev, int stringset); static const struct ethtool_ops batadv_ethtool_ops = { - .get_settings = batadv_get_settings, .get_drvinfo = batadv_get_drvinfo, .get_link = ethtool_op_get_link, .get_strings = batadv_get_strings, @@ -1079,23 +1077,6 @@ struct rtnl_link_ops batadv_link_ops __read_mostly = { .dellink = batadv_softif_destroy_netlink, }; -/* ethtool */ -static int batadv_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - cmd->supported = 0; - cmd->advertising = 0; - ethtool_cmd_speed_set(cmd, SPEED_10); - cmd->duplex = DUPLEX_FULL; - cmd->port = PORT_TP; - cmd->phy_address = 0; - cmd->transceiver = XCVR_INTERNAL; - cmd->autoneg = AUTONEG_DISABLE; - cmd->maxtxpkt = 0; - cmd->maxrxpkt = 0; - - return 0; -} - static void batadv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { -- cgit From 5405e19e705909bba1a879174ffe93e241455426 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 1 Apr 2017 14:47:06 +0200 Subject: batman-adv: Group ethtool related code together The ethtool code was spread in soft-interface.c. This makes reading the code and working on it unnecessary complicated. Having everything in a common place next to the other code which references it, makes it slightly easier. Signed-off-by: Sven Eckelmann Acked-by: Marek Lindner Signed-off-by: Simon Wunderlich --- net/batman-adv/soft-interface.c | 191 +++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 99 deletions(-) diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 6f22b1283c92..f33bee08bd99 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -64,21 +64,6 @@ #include "sysfs.h" #include "translation-table.h" -static void batadv_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info); -static void batadv_get_strings(struct net_device *dev, u32 stringset, u8 *data); -static void batadv_get_ethtool_stats(struct net_device *dev, - struct ethtool_stats *stats, u64 *data); -static int batadv_get_sset_count(struct net_device *dev, int stringset); - -static const struct ethtool_ops batadv_ethtool_ops = { - .get_drvinfo = batadv_get_drvinfo, - .get_link = ethtool_op_get_link, - .get_strings = batadv_get_strings, - .get_ethtool_stats = batadv_get_ethtool_stats, - .get_sset_count = batadv_get_sset_count, -}; - int batadv_skb_head_push(struct sk_buff *skb, unsigned int len) { int result; @@ -944,6 +929,98 @@ static const struct net_device_ops batadv_netdev_ops = { .ndo_del_slave = batadv_softif_slave_del, }; +static void batadv_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, "B.A.T.M.A.N. advanced", sizeof(info->driver)); + strlcpy(info->version, BATADV_SOURCE_VERSION, sizeof(info->version)); + strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); + strlcpy(info->bus_info, "batman", sizeof(info->bus_info)); +} + +/* Inspired by drivers/net/ethernet/dlink/sundance.c:1702 + * Declare each description string in struct.name[] to get fixed sized buffer + * and compile time checking for strings longer than ETH_GSTRING_LEN. + */ +static const struct { + const char name[ETH_GSTRING_LEN]; +} batadv_counters_strings[] = { + { "tx" }, + { "tx_bytes" }, + { "tx_dropped" }, + { "rx" }, + { "rx_bytes" }, + { "forward" }, + { "forward_bytes" }, + { "mgmt_tx" }, + { "mgmt_tx_bytes" }, + { "mgmt_rx" }, + { "mgmt_rx_bytes" }, + { "frag_tx" }, + { "frag_tx_bytes" }, + { "frag_rx" }, + { "frag_rx_bytes" }, + { "frag_fwd" }, + { "frag_fwd_bytes" }, + { "tt_request_tx" }, + { "tt_request_rx" }, + { "tt_response_tx" }, + { "tt_response_rx" }, + { "tt_roam_adv_tx" }, + { "tt_roam_adv_rx" }, +#ifdef CONFIG_BATMAN_ADV_DAT + { "dat_get_tx" }, + { "dat_get_rx" }, + { "dat_put_tx" }, + { "dat_put_rx" }, + { "dat_cached_reply_tx" }, +#endif +#ifdef CONFIG_BATMAN_ADV_NC + { "nc_code" }, + { "nc_code_bytes" }, + { "nc_recode" }, + { "nc_recode_bytes" }, + { "nc_buffer" }, + { "nc_decode" }, + { "nc_decode_bytes" }, + { "nc_decode_failed" }, + { "nc_sniffed" }, +#endif +}; + +static void batadv_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + if (stringset == ETH_SS_STATS) + memcpy(data, batadv_counters_strings, + sizeof(batadv_counters_strings)); +} + +static void batadv_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct batadv_priv *bat_priv = netdev_priv(dev); + int i; + + for (i = 0; i < BATADV_CNT_NUM; i++) + data[i] = batadv_sum_counter(bat_priv, i); +} + +static int batadv_get_sset_count(struct net_device *dev, int stringset) +{ + if (stringset == ETH_SS_STATS) + return BATADV_CNT_NUM; + + return -EOPNOTSUPP; +} + +static const struct ethtool_ops batadv_ethtool_ops = { + .get_drvinfo = batadv_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_strings = batadv_get_strings, + .get_ethtool_stats = batadv_get_ethtool_stats, + .get_sset_count = batadv_get_sset_count, +}; + /** * batadv_softif_free - Deconstructor of batadv_soft_interface * @dev: Device to cleanup and remove @@ -1076,87 +1153,3 @@ struct rtnl_link_ops batadv_link_ops __read_mostly = { .setup = batadv_softif_init_early, .dellink = batadv_softif_destroy_netlink, }; - -static void batadv_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strlcpy(info->driver, "B.A.T.M.A.N. advanced", sizeof(info->driver)); - strlcpy(info->version, BATADV_SOURCE_VERSION, sizeof(info->version)); - strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); - strlcpy(info->bus_info, "batman", sizeof(info->bus_info)); -} - -/* Inspired by drivers/net/ethernet/dlink/sundance.c:1702 - * Declare each description string in struct.name[] to get fixed sized buffer - * and compile time checking for strings longer than ETH_GSTRING_LEN. - */ -static const struct { - const char name[ETH_GSTRING_LEN]; -} batadv_counters_strings[] = { - { "tx" }, - { "tx_bytes" }, - { "tx_dropped" }, - { "rx" }, - { "rx_bytes" }, - { "forward" }, - { "forward_bytes" }, - { "mgmt_tx" }, - { "mgmt_tx_bytes" }, - { "mgmt_rx" }, - { "mgmt_rx_bytes" }, - { "frag_tx" }, - { "frag_tx_bytes" }, - { "frag_rx" }, - { "frag_rx_bytes" }, - { "frag_fwd" }, - { "frag_fwd_bytes" }, - { "tt_request_tx" }, - { "tt_request_rx" }, - { "tt_response_tx" }, - { "tt_response_rx" }, - { "tt_roam_adv_tx" }, - { "tt_roam_adv_rx" }, -#ifdef CONFIG_BATMAN_ADV_DAT - { "dat_get_tx" }, - { "dat_get_rx" }, - { "dat_put_tx" }, - { "dat_put_rx" }, - { "dat_cached_reply_tx" }, -#endif -#ifdef CONFIG_BATMAN_ADV_NC - { "nc_code" }, - { "nc_code_bytes" }, - { "nc_recode" }, - { "nc_recode_bytes" }, - { "nc_buffer" }, - { "nc_decode" }, - { "nc_decode_bytes" }, - { "nc_decode_failed" }, - { "nc_sniffed" }, -#endif -}; - -static void batadv_get_strings(struct net_device *dev, u32 stringset, u8 *data) -{ - if (stringset == ETH_SS_STATS) - memcpy(data, batadv_counters_strings, - sizeof(batadv_counters_strings)); -} - -static void batadv_get_ethtool_stats(struct net_device *dev, - struct ethtool_stats *stats, u64 *data) -{ - struct batadv_priv *bat_priv = netdev_priv(dev); - int i; - - for (i = 0; i < BATADV_CNT_NUM; i++) - data[i] = batadv_sum_counter(bat_priv, i); -} - -static int batadv_get_sset_count(struct net_device *dev, int stringset) -{ - if (stringset == ETH_SS_STATS) - return BATADV_CNT_NUM; - - return -EOPNOTSUPP; -} -- cgit From ba2d079131b494fca98cba37a5356e596a7501a6 Mon Sep 17 00:00:00 2001 From: lipeng Date: Sat, 1 Apr 2017 12:03:31 +0100 Subject: net: hns: Fix the implementation of irq affinity function This patch fixes the implementation of the IRQ affinity function. This function is used to create the cpu mask which eventually is used to initialize the cpu<->queue association for XPS(Transmit Packet Steering). Signed-off-by: lipeng Signed-off-by: Kejian Yan Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_enet.c | 75 +++++++++++---------------- drivers/net/ethernet/hisilicon/hns/hns_enet.h | 1 + 2 files changed, 30 insertions(+), 46 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index fca37e2c7f01..73ec8c843920 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -1196,54 +1196,31 @@ static void hns_nic_ring_close(struct net_device *netdev, int idx) napi_disable(&priv->ring_data[idx].napi); } -static void hns_set_irq_affinity(struct hns_nic_priv *priv) +static int hns_nic_init_affinity_mask(int q_num, int ring_idx, + struct hnae_ring *ring, cpumask_t *mask) { - struct hnae_handle *h = priv->ae_handle; - struct hns_nic_ring_data *rd; - int i; int cpu; - cpumask_var_t mask; - if (!alloc_cpumask_var(&mask, GFP_KERNEL)) - return; - - /*diffrent irq banlance for 16core and 32core*/ - if (h->q_num == num_possible_cpus()) { - for (i = 0; i < h->q_num * 2; i++) { - rd = &priv->ring_data[i]; - if (cpu_online(rd->queue_index)) { - cpumask_clear(mask); - cpu = rd->queue_index; - cpumask_set_cpu(cpu, mask); - (void)irq_set_affinity_hint(rd->ring->irq, - mask); - } - } + /* Diffrent irq banlance between 16core and 32core. + * The cpu mask set by ring index according to the ring flag + * which indicate the ring is tx or rx. + */ + if (q_num == num_possible_cpus()) { + if (is_tx_ring(ring)) + cpu = ring_idx; + else + cpu = ring_idx - q_num; } else { - for (i = 0; i < h->q_num; i++) { - rd = &priv->ring_data[i]; - if (cpu_online(rd->queue_index * 2)) { - cpumask_clear(mask); - cpu = rd->queue_index * 2; - cpumask_set_cpu(cpu, mask); - (void)irq_set_affinity_hint(rd->ring->irq, - mask); - } - } - - for (i = h->q_num; i < h->q_num * 2; i++) { - rd = &priv->ring_data[i]; - if (cpu_online(rd->queue_index * 2 + 1)) { - cpumask_clear(mask); - cpu = rd->queue_index * 2 + 1; - cpumask_set_cpu(cpu, mask); - (void)irq_set_affinity_hint(rd->ring->irq, - mask); - } - } + if (is_tx_ring(ring)) + cpu = ring_idx * 2; + else + cpu = (ring_idx - q_num) * 2 + 1; } - free_cpumask_var(mask); + cpumask_clear(mask); + cpumask_set_cpu(cpu, mask); + + return cpu; } static int hns_nic_init_irq(struct hns_nic_priv *priv) @@ -1252,6 +1229,7 @@ static int hns_nic_init_irq(struct hns_nic_priv *priv) struct hns_nic_ring_data *rd; int i; int ret; + int cpu; for (i = 0; i < h->q_num * 2; i++) { rd = &priv->ring_data[i]; @@ -1261,7 +1239,7 @@ static int hns_nic_init_irq(struct hns_nic_priv *priv) snprintf(rd->ring->ring_name, RCB_RING_NAME_LEN, "%s-%s%d", priv->netdev->name, - (i < h->q_num ? "tx" : "rx"), rd->queue_index); + (is_tx_ring(rd->ring) ? "tx" : "rx"), rd->queue_index); rd->ring->ring_name[RCB_RING_NAME_LEN - 1] = '\0'; @@ -1273,12 +1251,17 @@ static int hns_nic_init_irq(struct hns_nic_priv *priv) return ret; } disable_irq(rd->ring->irq); + + cpu = hns_nic_init_affinity_mask(h->q_num, i, + rd->ring, &rd->mask); + + if (cpu_online(cpu)) + irq_set_affinity_hint(rd->ring->irq, + &rd->mask); + rd->ring->irq_init_flag = RCB_IRQ_INITED; } - /*set cpu affinity*/ - hns_set_irq_affinity(priv); - return 0; } diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h index 5b412de350aa..fff8f8a21bd0 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.h @@ -37,6 +37,7 @@ enum hns_nic_state { struct hns_nic_ring_data { struct hnae_ring *ring; struct napi_struct napi; + cpumask_t mask; /* affinity mask */ int queue_index; int (*poll_one)(struct hns_nic_ring_data *, int, void *); void (*ex_process)(struct hns_nic_ring_data *, struct sk_buff *); -- cgit From 87ff7e1f460d709100cf5203738d62629ab6949b Mon Sep 17 00:00:00 2001 From: lipeng Date: Sat, 1 Apr 2017 12:03:32 +0100 Subject: net: hns: Modify GMAC init TX threshold value This patch reduces GMAC TX threshold value to avoid gmac hang-up with speed 100M/duplex half. Signed-off-by: lipeng Signed-off-by: JinchuanTian Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c | 6 ++++++ drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c index 3382441fe7b5..a8dbe001725f 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c @@ -325,6 +325,12 @@ static void hns_gmac_init(void *mac_drv) hns_gmac_tx_loop_pkt_dis(mac_drv); if (drv->mac_cb->mac_type == HNAE_PORT_DEBUG) hns_gmac_set_uc_match(mac_drv, 0); + + /* reduce gmac tx water line to avoid gmac hang-up + * in speed 100M and duplex half. + */ + dsaf_set_dev_field(drv, GMAC_TX_WATER_LINE_REG, GMAC_TX_WATER_LINE_MASK, + GMAC_TX_WATER_LINE_SHIFT, 8); } void hns_gmac_update_stats(void *mac_drv) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h index 8fa18fc17cd2..4b8af68af02e 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -466,6 +466,7 @@ #define GMAC_DUPLEX_TYPE_REG 0x0008UL #define GMAC_FD_FC_TYPE_REG 0x000CUL +#define GMAC_TX_WATER_LINE_REG 0x0010UL #define GMAC_FC_TX_TIMER_REG 0x001CUL #define GMAC_FD_FC_ADDR_LOW_REG 0x0020UL #define GMAC_FD_FC_ADDR_HIGH_REG 0x0024UL @@ -912,6 +913,9 @@ #define GMAC_DUPLEX_TYPE_B 0 +#define GMAC_TX_WATER_LINE_MASK ((1UL << 8) - 1) +#define GMAC_TX_WATER_LINE_SHIFT 0 + #define GMAC_FC_TX_TIMER_S 0 #define GMAC_FC_TX_TIMER_M 0xffff -- cgit From de99208cc7ceb964ea4e912000f2102f927f75ea Mon Sep 17 00:00:00 2001 From: lipeng Date: Sat, 1 Apr 2017 12:03:33 +0100 Subject: net: hns: Optimize the code for GMAC pad and crc Config This patch optimises the init configuration code leg for gmac pad and crc set interface. Signed-off-by: lipeng Signed-off-by: JinchuanTian Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c | 36 ++++++++++------------ 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c index a8dbe001725f..723f3ae28d0b 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c @@ -148,6 +148,17 @@ static void hns_gmac_config_max_frame_length(void *mac_drv, u16 newval) GMAC_MAX_FRM_SIZE_S, newval); } +static void hns_gmac_config_pad_and_crc(void *mac_drv, u8 newval) +{ + u32 tx_ctrl; + struct mac_driver *drv = (struct mac_driver *)mac_drv; + + tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); + dsaf_set_bit(tx_ctrl, GMAC_TX_PAD_EN_B, !!newval); + dsaf_set_bit(tx_ctrl, GMAC_TX_CRC_ADD_B, !!newval); + dsaf_write_dev(drv, GMAC_TRANSMIT_CONTROL_REG, tx_ctrl); +} + static void hns_gmac_config_an_mode(void *mac_drv, u8 newval) { struct mac_driver *drv = (struct mac_driver *)mac_drv; @@ -250,7 +261,6 @@ static void hns_gmac_get_pausefrm_cfg(void *mac_drv, u32 *rx_pause_en, static int hns_gmac_adjust_link(void *mac_drv, enum mac_speed speed, u32 full_duplex) { - u32 tx_ctrl; struct mac_driver *drv = (struct mac_driver *)mac_drv; dsaf_set_dev_bit(drv, GMAC_DUPLEX_TYPE_REG, @@ -279,14 +289,6 @@ static int hns_gmac_adjust_link(void *mac_drv, enum mac_speed speed, return -EINVAL; } - tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); - dsaf_set_bit(tx_ctrl, GMAC_TX_PAD_EN_B, 1); - dsaf_set_bit(tx_ctrl, GMAC_TX_CRC_ADD_B, 1); - dsaf_write_dev(drv, GMAC_TRANSMIT_CONTROL_REG, tx_ctrl); - - dsaf_set_dev_bit(drv, GMAC_MODE_CHANGE_EN_REG, - GMAC_MODE_CHANGE_EB_B, 1); - return 0; } @@ -326,6 +328,11 @@ static void hns_gmac_init(void *mac_drv) if (drv->mac_cb->mac_type == HNAE_PORT_DEBUG) hns_gmac_set_uc_match(mac_drv, 0); + hns_gmac_config_pad_and_crc(mac_drv, 1); + + dsaf_set_dev_bit(drv, GMAC_MODE_CHANGE_EN_REG, + GMAC_MODE_CHANGE_EB_B, 1); + /* reduce gmac tx water line to avoid gmac hang-up * in speed 100M and duplex half. */ @@ -459,17 +466,6 @@ static int hns_gmac_config_loopback(void *mac_drv, enum hnae_loop loop_mode, return 0; } -static void hns_gmac_config_pad_and_crc(void *mac_drv, u8 newval) -{ - u32 tx_ctrl; - struct mac_driver *drv = (struct mac_driver *)mac_drv; - - tx_ctrl = dsaf_read_dev(drv, GMAC_TRANSMIT_CONTROL_REG); - dsaf_set_bit(tx_ctrl, GMAC_TX_PAD_EN_B, !!newval); - dsaf_set_bit(tx_ctrl, GMAC_TX_CRC_ADD_B, !!newval); - dsaf_write_dev(drv, GMAC_TRANSMIT_CONTROL_REG, tx_ctrl); -} - static void hns_gmac_get_id(void *mac_drv, u8 *mac_id) { struct mac_driver *drv = (struct mac_driver *)mac_drv; -- cgit From fb0672d11634dd072bfb66a0b546b4fc592f4158 Mon Sep 17 00:00:00 2001 From: lipeng Date: Sat, 1 Apr 2017 12:03:34 +0100 Subject: net: hns: Remove redundant memset during buffer release Because all members of desc_cb is assigned when xmit one package, so it can delete in hnae_free_buffer, as follows: - "dma, priv, length, type" are assigned in fill_v2_desc. - "page_offset, reuse_flag, buf" are not used in tx direction. Signed-off-by: lipeng Signed-off-by: Weiwei Deng Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c index 120427a40883..8138dde4e767 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.c +++ b/drivers/net/ethernet/hisilicon/hns/hnae.c @@ -61,7 +61,6 @@ static void hnae_free_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) dev_kfree_skb_any((struct sk_buff *)cb->priv); else if (unlikely(is_rx_ring(ring))) put_page((struct page *)cb->priv); - memset(cb, 0, sizeof(*cb)); } static int hnae_map_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) -- cgit From 4b7cdecaa441bc296a507371766109624c177ec0 Mon Sep 17 00:00:00 2001 From: Daode Huang Date: Sat, 1 Apr 2017 12:03:35 +0100 Subject: net: hns: bug fix of ethtool show the speed When run ethtool ethX on hns driver, the speed will show as "Unknown". The base.speed is not correct assigned, this patch fix this bug. Signed-off-by: Daode Huang Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index 3ac2183dbd21..3404eacaed7f 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -146,7 +146,7 @@ static int hns_nic_get_link_ksettings(struct net_device *net_dev, /* When there is no phy, autoneg is off. */ cmd->base.autoneg = false; - cmd->base.cmd = speed; + cmd->base.speed = speed; cmd->base.duplex = duplex; if (net_dev->phydev) -- cgit From 36eedfde1a3602e8054c16bc295c47764647a208 Mon Sep 17 00:00:00 2001 From: lipeng Date: Sat, 1 Apr 2017 12:03:36 +0100 Subject: net: hns: Optimize hns_nic_common_poll for better performance After polling less than buget packages, we need check again. If there are still some packages, we call napi_schedule add softirq queue, this is not better way. So we return buget value instead of napi_schedule. Signed-off-by: lipeng reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_enet.c | 50 ++++++++++++++++----------- drivers/net/ethernet/hisilicon/hns/hns_enet.h | 2 +- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 73ec8c843920..5f67db2dc09c 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -859,7 +859,7 @@ out: return recv_pkts; } -static void hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data) +static bool hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data) { struct hnae_ring *ring = ring_data->ring; int num = 0; @@ -873,22 +873,23 @@ static void hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data) ring_data->ring->q->handle->dev->ops->toggle_ring_irq( ring_data->ring, 1); - napi_schedule(&ring_data->napi); + return false; + } else { + return true; } } -static void hns_nic_rx_fini_pro_v2(struct hns_nic_ring_data *ring_data) +static bool hns_nic_rx_fini_pro_v2(struct hns_nic_ring_data *ring_data) { struct hnae_ring *ring = ring_data->ring; - int num = 0; + int num; num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); - if (num == 0) - ring_data->ring->q->handle->dev->ops->toggle_ring_irq( - ring, 0); + if (!num) + return true; else - napi_schedule(&ring_data->napi); + return false; } static inline void hns_nic_reclaim_one_desc(struct hnae_ring *ring, @@ -989,7 +990,7 @@ static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data, return 0; } -static void hns_nic_tx_fini_pro(struct hns_nic_ring_data *ring_data) +static bool hns_nic_tx_fini_pro(struct hns_nic_ring_data *ring_data) { struct hnae_ring *ring = ring_data->ring; int head; @@ -1002,20 +1003,21 @@ static void hns_nic_tx_fini_pro(struct hns_nic_ring_data *ring_data) ring_data->ring->q->handle->dev->ops->toggle_ring_irq( ring_data->ring, 1); - napi_schedule(&ring_data->napi); + return false; + } else { + return true; } } -static void hns_nic_tx_fini_pro_v2(struct hns_nic_ring_data *ring_data) +static bool hns_nic_tx_fini_pro_v2(struct hns_nic_ring_data *ring_data) { struct hnae_ring *ring = ring_data->ring; int head = readl_relaxed(ring->io_base + RCB_REG_HEAD); if (head == ring->next_to_clean) - ring_data->ring->q->handle->dev->ops->toggle_ring_irq( - ring, 0); + return true; else - napi_schedule(&ring_data->napi); + return false; } static void hns_nic_tx_clr_all_bufs(struct hns_nic_ring_data *ring_data) @@ -1042,15 +1044,23 @@ static void hns_nic_tx_clr_all_bufs(struct hns_nic_ring_data *ring_data) static int hns_nic_common_poll(struct napi_struct *napi, int budget) { + int clean_complete = 0; struct hns_nic_ring_data *ring_data = container_of(napi, struct hns_nic_ring_data, napi); - int clean_complete = ring_data->poll_one( - ring_data, budget, ring_data->ex_process); + struct hnae_ring *ring = ring_data->ring; - if (clean_complete >= 0 && clean_complete < budget) { - napi_complete(napi); - ring_data->fini_process(ring_data); - return 0; +try_again: + clean_complete += ring_data->poll_one( + ring_data, budget - clean_complete, + ring_data->ex_process); + + if (clean_complete < budget) { + if (ring_data->fini_process(ring_data)) { + napi_complete(napi); + ring->q->handle->dev->ops->toggle_ring_irq(ring, 0); + } else { + goto try_again; + } } return clean_complete; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h index fff8f8a21bd0..1b83232082b2 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.h @@ -41,7 +41,7 @@ struct hns_nic_ring_data { int queue_index; int (*poll_one)(struct hns_nic_ring_data *, int, void *); void (*ex_process)(struct hns_nic_ring_data *, struct sk_buff *); - void (*fini_process)(struct hns_nic_ring_data *); + bool (*fini_process)(struct hns_nic_ring_data *); }; /* compatible the difference between two versions */ -- cgit From b29bd41259f38fc1a22735cd69b374a75d6a213c Mon Sep 17 00:00:00 2001 From: lipeng Date: Sat, 1 Apr 2017 12:03:37 +0100 Subject: net: hns: Fix to adjust buf_size of ring according to mtu Because buf_size of ring set to 2048, the process of rx_poll_one can reuse the page, therefore the performance of XGE can improve. But the chip only supports three bds in one package, so the max mtu is 6K when it sets to 2048. For better performane in litter mtu, we need change buf_size according to mtu. When user change mtu, hns is only change the desc in memory. There are some desc has been fetched by the chip, these desc can not be changed by the code. So it needs set the port loopback and send some packages to let the chip consumes the wrong desc and fetch new desc. Because the Pv660 do not support rss indirection, we need add version check in mtu change process. Signed-off-by: lipeng reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.h | 37 ++++ drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c | 26 ++- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c | 3 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h | 2 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c | 41 +++- drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h | 3 + drivers/net/ethernet/hisilicon/hns/hns_enet.c | 249 +++++++++++++++++++++- 7 files changed, 337 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index 8016854796fb..c66581db72ac 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -67,6 +67,8 @@ do { \ #define AE_IS_VER1(ver) ((ver) == AE_VERSION_1) #define AE_NAME_SIZE 16 +#define BD_SIZE_2048_MAX_MTU 6000 + /* some said the RX and TX RCB format should not be the same in the future. But * it is the same now... */ @@ -646,6 +648,41 @@ static inline void hnae_reuse_buffer(struct hnae_ring *ring, int i) ring->desc[i].rx.ipoff_bnum_pid_flag = 0; } +/* when reinit buffer size, we should reinit buffer description */ +static inline void hnae_reinit_all_ring_desc(struct hnae_handle *h) +{ + int i, j; + struct hnae_ring *ring; + + for (i = 0; i < h->q_num; i++) { + ring = &h->qs[i]->rx_ring; + for (j = 0; j < ring->desc_num; j++) + ring->desc[j].addr = cpu_to_le64(ring->desc_cb[j].dma); + } + + wmb(); /* commit all data before submit */ +} + +/* when reinit buffer size, we should reinit page offset */ +static inline void hnae_reinit_all_ring_page_off(struct hnae_handle *h) +{ + int i, j; + struct hnae_ring *ring; + + for (i = 0; i < h->q_num; i++) { + ring = &h->qs[i]->rx_ring; + for (j = 0; j < ring->desc_num; j++) { + ring->desc_cb[j].page_offset = 0; + if (ring->desc[j].addr != + cpu_to_le64(ring->desc_cb[j].dma)) + ring->desc[j].addr = + cpu_to_le64(ring->desc_cb[j].dma); + } + } + + wmb(); /* commit all data before submit */ +} + #define hnae_set_field(origin, mask, shift, val) \ do { \ (origin) &= (~(mask)); \ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index 0a9cdf00b31a..cd7e88e433b6 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -267,8 +267,32 @@ static int hns_ae_clr_multicast(struct hnae_handle *handle) static int hns_ae_set_mtu(struct hnae_handle *handle, int new_mtu) { struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle); + struct hnae_queue *q; + u32 rx_buf_size; + int i, ret; + + /* when buf_size is 2048, max mtu is 6K for rx ring max bd num is 3. */ + if (!AE_IS_VER1(mac_cb->dsaf_dev->dsaf_ver)) { + if (new_mtu <= BD_SIZE_2048_MAX_MTU) + rx_buf_size = 2048; + else + rx_buf_size = 4096; + } else { + rx_buf_size = mac_cb->dsaf_dev->buf_size; + } + + ret = hns_mac_set_mtu(mac_cb, new_mtu, rx_buf_size); - return hns_mac_set_mtu(mac_cb, new_mtu); + if (!ret) { + /* reinit ring buf_size */ + for (i = 0; i < handle->q_num; i++) { + q = handle->qs[i]; + q->rx_ring.buf_size = rx_buf_size; + hns_rcb_set_rx_ring_bs(q, rx_buf_size); + } + } + + return ret; } static void hns_ae_set_tso_stats(struct hnae_handle *handle, int enable) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c index 3239d27143b9..edf9a23db9d0 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c @@ -491,10 +491,9 @@ void hns_mac_reset(struct hns_mac_cb *mac_cb) } } -int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu) +int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu, u32 buf_size) { struct mac_driver *drv = hns_mac_get_drv(mac_cb); - u32 buf_size = mac_cb->dsaf_dev->buf_size; u32 new_frm = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; u32 max_frm = AE_IS_VER1(mac_cb->dsaf_dev->dsaf_ver) ? MAC_MAX_MTU : MAC_MAX_MTU_V2; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h index 2bb3d1e93c64..7f14d9172e5e 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h @@ -444,7 +444,7 @@ void hns_mac_get_autoneg(struct hns_mac_cb *mac_cb, u32 *auto_neg); void hns_mac_get_pauseparam(struct hns_mac_cb *mac_cb, u32 *rx_en, u32 *tx_en); int hns_mac_set_autoneg(struct hns_mac_cb *mac_cb, u8 enable); int hns_mac_set_pauseparam(struct hns_mac_cb *mac_cb, u32 rx_en, u32 tx_en); -int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu); +int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu, u32 buf_size); int hns_mac_get_port_info(struct hns_mac_cb *mac_cb, u8 *auto_neg, u16 *speed, u8 *duplex); int hns_mac_config_mac_loopback(struct hns_mac_cb *mac_cb, diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c index f0ed80d6ef9c..a6ab1680d294 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c @@ -32,6 +32,9 @@ #define RCB_RESET_WAIT_TIMES 30 #define RCB_RESET_TRY_TIMES 10 +/* Because default mtu is 1500, rcb buffer size is set to 2048 enough */ +#define RCB_DEFAULT_BUFFER_SIZE 2048 + /** *hns_rcb_wait_fbd_clean - clean fbd *@qs: ring struct pointer array @@ -192,6 +195,30 @@ void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common) wmb(); /* Sync point after breakpoint */ } +/* hns_rcb_set_tx_ring_bs - init rcb ring buf size regester + *@q: hnae_queue + *@buf_size: buffer size set to hw + */ +void hns_rcb_set_tx_ring_bs(struct hnae_queue *q, u32 buf_size) +{ + u32 bd_size_type = hns_rcb_buf_size2type(buf_size); + + dsaf_write_dev(q, RCB_RING_TX_RING_BD_LEN_REG, + bd_size_type); +} + +/* hns_rcb_set_rx_ring_bs - init rcb ring buf size regester + *@q: hnae_queue + *@buf_size: buffer size set to hw + */ +void hns_rcb_set_rx_ring_bs(struct hnae_queue *q, u32 buf_size) +{ + u32 bd_size_type = hns_rcb_buf_size2type(buf_size); + + dsaf_write_dev(q, RCB_RING_RX_RING_BD_LEN_REG, + bd_size_type); +} + /** *hns_rcb_ring_init - init rcb ring *@ring_pair: ring pair control block @@ -200,8 +227,6 @@ void hns_rcb_common_init_commit_hw(struct rcb_common_cb *rcb_common) static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type) { struct hnae_queue *q = &ring_pair->q; - struct rcb_common_cb *rcb_common = ring_pair->rcb_common; - u32 bd_size_type = rcb_common->dsaf_dev->buf_size_type; struct hnae_ring *ring = (ring_type == RX_RING) ? &q->rx_ring : &q->tx_ring; dma_addr_t dma = ring->desc_dma_addr; @@ -212,8 +237,8 @@ static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type) dsaf_write_dev(q, RCB_RING_RX_RING_BASEADDR_H_REG, (u32)((dma >> 31) >> 1)); - dsaf_write_dev(q, RCB_RING_RX_RING_BD_LEN_REG, - bd_size_type); + hns_rcb_set_rx_ring_bs(q, ring->buf_size); + dsaf_write_dev(q, RCB_RING_RX_RING_BD_NUM_REG, ring_pair->port_id_in_comm); dsaf_write_dev(q, RCB_RING_RX_RING_PKTLINE_REG, @@ -224,8 +249,8 @@ static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type) dsaf_write_dev(q, RCB_RING_TX_RING_BASEADDR_H_REG, (u32)((dma >> 31) >> 1)); - dsaf_write_dev(q, RCB_RING_TX_RING_BD_LEN_REG, - bd_size_type); + hns_rcb_set_tx_ring_bs(q, ring->buf_size); + dsaf_write_dev(q, RCB_RING_TX_RING_BD_NUM_REG, ring_pair->port_id_in_comm); dsaf_write_dev(q, RCB_RING_TX_RING_PKTLINE_REG, @@ -380,7 +405,6 @@ static void hns_rcb_ring_get_cfg(struct hnae_queue *q, int ring_type) struct hnae_ring *ring; struct rcb_common_cb *rcb_common; struct ring_pair_cb *ring_pair_cb; - u32 buf_size; u16 desc_num, mdnum_ppkt; bool irq_idx, is_ver1; @@ -401,7 +425,6 @@ static void hns_rcb_ring_get_cfg(struct hnae_queue *q, int ring_type) } rcb_common = ring_pair_cb->rcb_common; - buf_size = rcb_common->dsaf_dev->buf_size; desc_num = rcb_common->dsaf_dev->desc_num; ring->desc = NULL; @@ -410,7 +433,7 @@ static void hns_rcb_ring_get_cfg(struct hnae_queue *q, int ring_type) ring->irq = ring_pair_cb->virq[irq_idx]; ring->desc_dma_addr = 0; - ring->buf_size = buf_size; + ring->buf_size = RCB_DEFAULT_BUFFER_SIZE; ring->desc_num = desc_num; ring->max_desc_num_per_pkt = mdnum_ppkt; ring->max_raw_data_sz_per_desc = HNS_RCB_MAX_PKT_SIZE; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h index 99b4e1ba0a94..afe563cf4c5d 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h @@ -146,4 +146,7 @@ int hns_rcb_get_ring_regs_count(void); void hns_rcb_get_ring_regs(struct hnae_queue *queue, void *data); void hns_rcb_get_strings(int stringset, u8 *data, int index); +void hns_rcb_set_rx_ring_bs(struct hnae_queue *q, u32 buf_size); +void hns_rcb_set_tx_ring_bs(struct hnae_queue *q, u32 buf_size); + #endif /* _HNS_DSAF_RCB_H */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 5f67db2dc09c..57b4d59d1097 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -1480,32 +1480,259 @@ static netdev_tx_t hns_nic_net_xmit(struct sk_buff *skb, return (netdev_tx_t)ret; } +static void hns_nic_drop_rx_fetch(struct hns_nic_ring_data *ring_data, + struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); +} + +#define HNS_LB_TX_RING 0 +static struct sk_buff *hns_assemble_skb(struct net_device *ndev) +{ + struct sk_buff *skb; + struct ethhdr *ethhdr; + int frame_len; + + /* allocate test skb */ + skb = alloc_skb(64, GFP_KERNEL); + if (!skb) + return NULL; + + skb_put(skb, 64); + skb->dev = ndev; + memset(skb->data, 0xFF, skb->len); + + /* must be tcp/ip package */ + ethhdr = (struct ethhdr *)skb->data; + ethhdr->h_proto = htons(ETH_P_IP); + + frame_len = skb->len & (~1ul); + memset(&skb->data[frame_len / 2], 0xAA, + frame_len / 2 - 1); + + skb->queue_mapping = HNS_LB_TX_RING; + + return skb; +} + +static int hns_enable_serdes_lb(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + struct hnae_ae_ops *ops = h->dev->ops; + int speed, duplex; + int ret; + + ret = ops->set_loopback(h, MAC_INTERNALLOOP_SERDES, 1); + if (ret) + return ret; + + ret = ops->start ? ops->start(h) : 0; + if (ret) + return ret; + + /* link adjust duplex*/ + if (h->phy_if != PHY_INTERFACE_MODE_XGMII) + speed = 1000; + else + speed = 10000; + duplex = 1; + + ops->adjust_link(h, speed, duplex); + + /* wait h/w ready */ + mdelay(300); + + return 0; +} + +static void hns_disable_serdes_lb(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + struct hnae_ae_ops *ops = h->dev->ops; + + ops->stop(h); + ops->set_loopback(h, MAC_INTERNALLOOP_SERDES, 0); +} + +/** + *hns_nic_clear_all_rx_fetch - clear the chip fetched descriptions. The + *function as follows: + * 1. if one rx ring has found the page_offset is not equal 0 between head + * and tail, it means that the chip fetched the wrong descs for the ring + * which buffer size is 4096. + * 2. we set the chip serdes loopback and set rss indirection to the ring. + * 3. construct 64-bytes ip broadcast packages, wait the associated rx ring + * recieving all packages and it will fetch new descriptions. + * 4. recover to the original state. + * + *@ndev: net device + */ +static int hns_nic_clear_all_rx_fetch(struct net_device *ndev) +{ + struct hns_nic_priv *priv = netdev_priv(ndev); + struct hnae_handle *h = priv->ae_handle; + struct hnae_ae_ops *ops = h->dev->ops; + struct hns_nic_ring_data *rd; + struct hnae_ring *ring; + struct sk_buff *skb; + u32 *org_indir; + u32 *cur_indir; + int indir_size; + int head, tail; + int fetch_num; + int i, j; + bool found; + int retry_times; + int ret = 0; + + /* alloc indir memory */ + indir_size = ops->get_rss_indir_size(h) * sizeof(*org_indir); + org_indir = kzalloc(indir_size, GFP_KERNEL); + if (!org_indir) + return -ENOMEM; + + /* store the orginal indirection */ + ops->get_rss(h, org_indir, NULL, NULL); + + cur_indir = kzalloc(indir_size, GFP_KERNEL); + if (!cur_indir) { + ret = -ENOMEM; + goto cur_indir_alloc_err; + } + + /* set loopback */ + if (hns_enable_serdes_lb(ndev)) { + ret = -EINVAL; + goto enable_serdes_lb_err; + } + + /* foreach every rx ring to clear fetch desc */ + for (i = 0; i < h->q_num; i++) { + ring = &h->qs[i]->rx_ring; + head = readl_relaxed(ring->io_base + RCB_REG_HEAD); + tail = readl_relaxed(ring->io_base + RCB_REG_TAIL); + found = false; + fetch_num = ring_dist(ring, head, tail); + + while (head != tail) { + if (ring->desc_cb[head].page_offset != 0) { + found = true; + break; + } + + head++; + if (head == ring->desc_num) + head = 0; + } + + if (found) { + for (j = 0; j < indir_size / sizeof(*org_indir); j++) + cur_indir[j] = i; + ops->set_rss(h, cur_indir, NULL, 0); + + for (j = 0; j < fetch_num; j++) { + /* alloc one skb and init */ + skb = hns_assemble_skb(ndev); + if (!skb) + goto out; + rd = &tx_ring_data(priv, skb->queue_mapping); + hns_nic_net_xmit_hw(ndev, skb, rd); + + retry_times = 0; + while (retry_times++ < 10) { + mdelay(10); + /* clean rx */ + rd = &rx_ring_data(priv, i); + if (rd->poll_one(rd, fetch_num, + hns_nic_drop_rx_fetch)) + break; + } + + retry_times = 0; + while (retry_times++ < 10) { + mdelay(10); + /* clean tx ring 0 send package */ + rd = &tx_ring_data(priv, + HNS_LB_TX_RING); + if (rd->poll_one(rd, fetch_num, NULL)) + break; + } + } + } + } + +out: + /* restore everything */ + ops->set_rss(h, org_indir, NULL, 0); + hns_disable_serdes_lb(ndev); +enable_serdes_lb_err: + kfree(cur_indir); +cur_indir_alloc_err: + kfree(org_indir); + + return ret; +} + static int hns_nic_change_mtu(struct net_device *ndev, int new_mtu) { struct hns_nic_priv *priv = netdev_priv(ndev); struct hnae_handle *h = priv->ae_handle; + bool if_running = netif_running(ndev); int ret; + /* MTU < 68 is an error and causes problems on some kernels */ + if (new_mtu < 68) + return -EINVAL; + + /* MTU no change */ + if (new_mtu == ndev->mtu) + return 0; + if (!h->dev->ops->set_mtu) return -ENOTSUPP; - if (netif_running(ndev)) { + if (if_running) { (void)hns_nic_net_stop(ndev); msleep(100); + } - ret = h->dev->ops->set_mtu(h, new_mtu); - if (ret) - netdev_err(ndev, "set mtu fail, return value %d\n", - ret); + if (priv->enet_ver != AE_VERSION_1 && + ndev->mtu <= BD_SIZE_2048_MAX_MTU && + new_mtu > BD_SIZE_2048_MAX_MTU) { + /* update desc */ + hnae_reinit_all_ring_desc(h); - if (hns_nic_net_open(ndev)) - netdev_err(ndev, "hns net open fail\n"); - } else { - ret = h->dev->ops->set_mtu(h, new_mtu); + /* clear the package which the chip has fetched */ + ret = hns_nic_clear_all_rx_fetch(ndev); + + /* the page offset must be consist with desc */ + hnae_reinit_all_ring_page_off(h); + + if (ret) { + netdev_err(ndev, "clear the fetched desc fail\n"); + goto out; + } + } + + ret = h->dev->ops->set_mtu(h, new_mtu); + if (ret) { + netdev_err(ndev, "set mtu fail, return value %d\n", + ret); + goto out; } - if (!ret) - ndev->mtu = new_mtu; + /* finally, set new mtu to netdevice */ + ndev->mtu = new_mtu; + +out: + if (if_running) { + if (hns_nic_net_open(ndev)) { + netdev_err(ndev, "hns net open fail\n"); + ret = -EINVAL; + } + } return ret; } -- cgit From f2aaed557ecff57f7523f889acf1981fb23c4047 Mon Sep 17 00:00:00 2001 From: lipeng Date: Sat, 1 Apr 2017 12:03:38 +0100 Subject: net: hns: Replace netif_tx_lock to ring spin lock netif_tx_lock is a global spin lock, it will take affect in all rings in the netdevice. In tx_poll_one process, it can only lock the current ring, in this case, we define a spin lock in hnae_ring struct for it. Signed-off-by: lipeng reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.c | 1 + drivers/net/ethernet/hisilicon/hns/hnae.h | 3 +++ drivers/net/ethernet/hisilicon/hns/hns_enet.c | 21 +++++++++++---------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c index 8138dde4e767..ef818b7d5021 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.c +++ b/drivers/net/ethernet/hisilicon/hns/hnae.c @@ -196,6 +196,7 @@ hnae_init_ring(struct hnae_queue *q, struct hnae_ring *ring, int flags) ring->q = q; ring->flags = flags; + spin_lock_init(&ring->lock); assert(!ring->desc && !ring->desc_cb && !ring->desc_dma_addr); /* not matter for tx or rx ring, the ntc and ntc start from 0 */ diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index c66581db72ac..859c53619ba2 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -275,6 +275,9 @@ struct hnae_ring { /* statistic */ struct ring_stats stats; + /* ring lock for poll one */ + spinlock_t lock; + dma_addr_t desc_dma_addr; u32 buf_size; /* size for hnae_desc->addr, preset by AE */ u16 desc_num; /* total number of desc */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 57b4d59d1097..e39c794d94e2 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -922,12 +922,13 @@ static int is_valid_clean_head(struct hnae_ring *ring, int h) /* netif_tx_lock will turn down the performance, set only when necessary */ #ifdef CONFIG_NET_POLL_CONTROLLER -#define NETIF_TX_LOCK(ndev) netif_tx_lock(ndev) -#define NETIF_TX_UNLOCK(ndev) netif_tx_unlock(ndev) +#define NETIF_TX_LOCK(ring) spin_lock(&ring->lock) +#define NETIF_TX_UNLOCK(ring) spin_unlock(&ring->lock) #else -#define NETIF_TX_LOCK(ndev) -#define NETIF_TX_UNLOCK(ndev) +#define NETIF_TX_LOCK(ring) +#define NETIF_TX_UNLOCK(ring) #endif + /* reclaim all desc in one budget * return error or number of desc left */ @@ -941,13 +942,13 @@ static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data, int head; int bytes, pkts; - NETIF_TX_LOCK(ndev); + NETIF_TX_LOCK(ring); head = readl_relaxed(ring->io_base + RCB_REG_HEAD); rmb(); /* make sure head is ready before touch any data */ if (is_ring_empty(ring) || head == ring->next_to_clean) { - NETIF_TX_UNLOCK(ndev); + NETIF_TX_UNLOCK(ring); return 0; /* no data to poll */ } @@ -955,7 +956,7 @@ static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data, netdev_err(ndev, "wrong head (%d, %d-%d)\n", head, ring->next_to_use, ring->next_to_clean); ring->stats.io_err_cnt++; - NETIF_TX_UNLOCK(ndev); + NETIF_TX_UNLOCK(ring); return -EIO; } @@ -967,7 +968,7 @@ static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data, prefetch(&ring->desc_cb[ring->next_to_clean]); } - NETIF_TX_UNLOCK(ndev); + NETIF_TX_UNLOCK(ring); dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index); netdev_tx_completed_queue(dev_queue, pkts, bytes); @@ -1028,7 +1029,7 @@ static void hns_nic_tx_clr_all_bufs(struct hns_nic_ring_data *ring_data) int head; int bytes, pkts; - NETIF_TX_LOCK(ndev); + NETIF_TX_LOCK(ring); head = ring->next_to_use; /* ntu :soft setted ring position*/ bytes = 0; @@ -1036,7 +1037,7 @@ static void hns_nic_tx_clr_all_bufs(struct hns_nic_ring_data *ring_data) while (head != ring->next_to_clean) hns_nic_reclaim_one_desc(ring, &bytes, &pkts); - NETIF_TX_UNLOCK(ndev); + NETIF_TX_UNLOCK(ring); dev_queue = netdev_get_tx_queue(ndev, ring_data->queue_index); netdev_tx_reset_queue(dev_queue); -- cgit From 64ec10dc2ab8ef5bc6e76b1d4bc8203c08a6da1e Mon Sep 17 00:00:00 2001 From: lipeng Date: Sat, 1 Apr 2017 12:03:39 +0100 Subject: net: hns: Correct HNS RSS key set function This patch fixes below ethtool configuration error: localhost:~ # ethtool -X eth0 hkey XX:XX:XX... Cannot set Rx flow hash configuration: Operation not supported Signed-off-by: lipeng Reviewed-by: Yisen Zhuang Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c | 23 ++++++++++++++--------- drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 9 ++++----- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index cd7e88e433b6..f0142e50048e 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -826,8 +826,9 @@ static int hns_ae_get_rss(struct hnae_handle *handle, u32 *indir, u8 *key, memcpy(key, ppe_cb->rss_key, HNS_PPEV2_RSS_KEY_SIZE); /* update the current hash->queue mappings from the shadow RSS table */ - memcpy(indir, ppe_cb->rss_indir_table, - HNS_PPEV2_RSS_IND_TBL_SIZE * sizeof(*indir)); + if (indir) + memcpy(indir, ppe_cb->rss_indir_table, + HNS_PPEV2_RSS_IND_TBL_SIZE * sizeof(*indir)); return 0; } @@ -838,15 +839,19 @@ static int hns_ae_set_rss(struct hnae_handle *handle, const u32 *indir, struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle); /* set the RSS Hash Key if specififed by the user */ - if (key) - hns_ppe_set_rss_key(ppe_cb, (u32 *)key); + if (key) { + memcpy(ppe_cb->rss_key, key, HNS_PPEV2_RSS_KEY_SIZE); + hns_ppe_set_rss_key(ppe_cb, ppe_cb->rss_key); + } - /* update the shadow RSS table with user specified qids */ - memcpy(ppe_cb->rss_indir_table, indir, - HNS_PPEV2_RSS_IND_TBL_SIZE * sizeof(*indir)); + if (indir) { + /* update the shadow RSS table with user specified qids */ + memcpy(ppe_cb->rss_indir_table, indir, + HNS_PPEV2_RSS_IND_TBL_SIZE * sizeof(*indir)); - /* now update the hardware */ - hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table); + /* now update the hardware */ + hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table); + } return 0; } diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index 3404eacaed7f..3a2a34250cc0 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -1244,6 +1244,7 @@ hns_set_rss(struct net_device *netdev, const u32 *indir, const u8 *key, { struct hns_nic_priv *priv = netdev_priv(netdev); struct hnae_ae_ops *ops; + int ret; if (AE_IS_VER1(priv->enet_ver)) { netdev_err(netdev, @@ -1253,12 +1254,10 @@ hns_set_rss(struct net_device *netdev, const u32 *indir, const u8 *key, ops = priv->ae_handle->dev->ops; - /* currently hfunc can only be Toeplitz hash */ - if (key || - (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, "Invalid hfunc!\n"); return -EOPNOTSUPP; - if (!indir) - return 0; + } return ops->set_rss(priv->ae_handle, indir, key, hfunc); } -- cgit From 040a3800aac0ca0f40a09ca751a037239f93896b Mon Sep 17 00:00:00 2001 From: Kejian Yan Date: Sat, 1 Apr 2017 12:03:40 +0100 Subject: net: hns: Remove the redundant adding and deleting mac function The functions (hns_dsaf_set_mac_mc_entry() and hns_mac_del_mac()) are not called by any functions. They are dead code in hns. And the same features are implemented by the patch (the id is 66355f5). Reported-by: Weiwei Deng Signed-off-by: Kejian Yan Reviewed-by: Salil Mehta Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c | 38 ---------- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h | 1 - drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 81 ---------------------- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h | 2 - 4 files changed, 122 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c index edf9a23db9d0..696f2ae8b075 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c @@ -332,44 +332,6 @@ int hns_mac_set_multi(struct hns_mac_cb *mac_cb, return 0; } -/** - *hns_mac_del_mac - delete mac address into dsaf table,can't delete the same - * address twice - *@net_dev: net device - *@vfn : vf lan - *@mac : mac address - *return status - */ -int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac) -{ - struct mac_entry_idx *old_mac; - struct dsaf_device *dsaf_dev; - u32 ret; - - dsaf_dev = mac_cb->dsaf_dev; - - if (vfn < DSAF_MAX_VM_NUM) { - old_mac = &mac_cb->addr_entry_idx[vfn]; - } else { - dev_err(mac_cb->dev, - "vf queue is too large, %s mac%d queue = %#x!\n", - mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id, vfn); - return -EINVAL; - } - - if (dsaf_dev) { - ret = hns_dsaf_del_mac_entry(dsaf_dev, old_mac->vlan_id, - mac_cb->mac_id, old_mac->addr); - if (ret) - return ret; - - if (memcmp(old_mac->addr, mac, sizeof(old_mac->addr)) == 0) - old_mac->valid = 0; - } - - return 0; -} - int hns_mac_clr_multicast(struct hns_mac_cb *mac_cb, int vfn) { struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h index 7f14d9172e5e..e6842c922e95 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h @@ -436,7 +436,6 @@ int hns_mac_set_multi(struct hns_mac_cb *mac_cb, int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vm, bool enable); void hns_mac_start(struct hns_mac_cb *mac_cb); void hns_mac_stop(struct hns_mac_cb *mac_cb); -int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac); void hns_mac_uninit(struct dsaf_device *dsaf_dev); void hns_mac_adjust_link(struct hns_mac_cb *mac_cb, int speed, int duplex); void hns_mac_reset(struct hns_mac_cb *mac_cb); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index 90dbda792614..6a069ffd8e5f 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -1647,87 +1647,6 @@ int hns_dsaf_rm_mac_addr( mac_entry->addr); } -/** - * hns_dsaf_set_mac_mc_entry - set mac mc-entry - * @dsaf_dev: dsa fabric device struct pointer - * @mac_entry: mc-mac entry - */ -int hns_dsaf_set_mac_mc_entry( - struct dsaf_device *dsaf_dev, - struct dsaf_drv_mac_multi_dest_entry *mac_entry) -{ - u16 entry_index = DSAF_INVALID_ENTRY_IDX; - struct dsaf_drv_tbl_tcam_key mac_key; - struct dsaf_tbl_tcam_mcast_cfg mac_data; - struct dsaf_drv_priv *priv = - (struct dsaf_drv_priv *)hns_dsaf_dev_priv(dsaf_dev); - struct dsaf_drv_soft_mac_tbl *soft_mac_entry = priv->soft_mac_tbl; - struct dsaf_drv_tbl_tcam_key tmp_mac_key; - struct dsaf_tbl_tcam_data tcam_data; - - /* mac addr check */ - if (MAC_IS_ALL_ZEROS(mac_entry->addr)) { - dev_err(dsaf_dev->dev, "set uc %s Mac %pM err!\n", - dsaf_dev->ae_dev.name, mac_entry->addr); - return -EINVAL; - } - - /*config key */ - hns_dsaf_set_mac_key(dsaf_dev, &mac_key, - mac_entry->in_vlan_id, - mac_entry->in_port_num, mac_entry->addr); - - /* entry ie exist? */ - entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); - if (entry_index == DSAF_INVALID_ENTRY_IDX) { - /*if hasnot, find enpty entry*/ - entry_index = hns_dsaf_find_empty_mac_entry(dsaf_dev); - if (entry_index == DSAF_INVALID_ENTRY_IDX) { - /*if hasnot empty, error*/ - dev_err(dsaf_dev->dev, - "set_uc_entry failed, %s Mac key(%#x:%#x)\n", - dsaf_dev->ae_dev.name, - mac_key.high.val, mac_key.low.val); - return -EINVAL; - } - - /* config hardware entry */ - memset(mac_data.tbl_mcast_port_msk, - 0, sizeof(mac_data.tbl_mcast_port_msk)); - } else { - /* config hardware entry */ - hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, &tcam_data, - &mac_data); - - tmp_mac_key.high.val = - le32_to_cpu(tcam_data.tbl_tcam_data_high); - tmp_mac_key.low.val = le32_to_cpu(tcam_data.tbl_tcam_data_low); - } - mac_data.tbl_mcast_old_en = 0; - mac_data.tbl_mcast_item_vld = 1; - dsaf_set_field(mac_data.tbl_mcast_port_msk[0], - 0x3F, 0, mac_entry->port_mask[0]); - - dev_dbg(dsaf_dev->dev, - "set_uc_entry, %s key(%#x:%#x) entry_index%d\n", - dsaf_dev->ae_dev.name, mac_key.high.val, - mac_key.low.val, entry_index); - - tcam_data.tbl_tcam_data_high = cpu_to_le32(mac_key.high.val); - tcam_data.tbl_tcam_data_low = cpu_to_le32(mac_key.low.val); - - hns_dsaf_tcam_mc_cfg(dsaf_dev, entry_index, &tcam_data, NULL, - &mac_data); - - /* config software entry */ - soft_mac_entry += entry_index; - soft_mac_entry->index = entry_index; - soft_mac_entry->tcam_key.high.val = mac_key.high.val; - soft_mac_entry->tcam_key.low.val = mac_key.low.val; - - return 0; -} - static void hns_dsaf_mc_mask_bit_clear(char *dst, const char *src) { u16 *a = (u16 *)dst; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h index cef6bf46ae93..e2d71be4b573 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -429,8 +429,6 @@ static inline struct hnae_vf_cb *hns_ae_get_vf_cb( int hns_dsaf_set_mac_uc_entry(struct dsaf_device *dsaf_dev, struct dsaf_drv_mac_single_dest_entry *mac_entry); -int hns_dsaf_set_mac_mc_entry(struct dsaf_device *dsaf_dev, - struct dsaf_drv_mac_multi_dest_entry *mac_entry); int hns_dsaf_add_mac_mc_port(struct dsaf_device *dsaf_dev, struct dsaf_drv_mac_single_dest_entry *mac_entry); int hns_dsaf_del_mac_entry(struct dsaf_device *dsaf_dev, u16 vlan_id, -- cgit From 20f0d4f736b7732709b8b9b25be6d88dcc3b48d3 Mon Sep 17 00:00:00 2001 From: Kejian Yan Date: Sat, 1 Apr 2017 12:03:41 +0100 Subject: net: hns: Remove redundant mac_get_id() There is a mac_id in mac control block structure, so the callback function mac_get_id() is useless. Here we remove this function. Reported-by: Weiwei Deng Signed-off-by: Kejian Yan Reviewed-by: Salil Mehta Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c | 8 -------- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h | 2 -- drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c | 13 ------------- 3 files changed, 23 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c index 723f3ae28d0b..035db868de04 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c @@ -466,13 +466,6 @@ static int hns_gmac_config_loopback(void *mac_drv, enum hnae_loop loop_mode, return 0; } -static void hns_gmac_get_id(void *mac_drv, u8 *mac_id) -{ - struct mac_driver *drv = (struct mac_driver *)mac_drv; - - *mac_id = drv->mac_id; -} - static void hns_gmac_get_info(void *mac_drv, struct mac_info *mac_info) { enum hns_gmac_duplex_mdoe duplex; @@ -714,7 +707,6 @@ void *hns_gmac_config(struct hns_mac_cb *mac_cb, struct mac_params *mac_param) mac_drv->config_pad_and_crc = hns_gmac_config_pad_and_crc; mac_drv->config_half_duplex = hns_gmac_set_duplex_type; mac_drv->set_rx_ignore_pause_frames = hns_gmac_set_rx_auto_pause_frames; - mac_drv->mac_get_id = hns_gmac_get_id; mac_drv->get_info = hns_gmac_get_info; mac_drv->autoneg_stat = hns_gmac_autoneg_stat; mac_drv->get_pause_enable = hns_gmac_get_pausefrm_cfg; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h index e6842c922e95..24dfba53a0f2 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h @@ -373,8 +373,6 @@ struct mac_driver { void (*set_rx_ignore_pause_frames)(void *mac_drv, u32 enable); /* config rx mode for promiscuous*/ void (*set_promiscuous)(void *mac_drv, u8 enable); - /* get mac id */ - void (*mac_get_id)(void *mac_drv, u8 *mac_id); void (*mac_pausefrm_cfg)(void *mac_drv, u32 rx_en, u32 tx_en); void (*autoneg_stat)(void *mac_drv, u32 *enable); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c index aae830a93050..37a2fc35148f 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c @@ -299,18 +299,6 @@ static void hns_xgmac_set_tx_auto_pause_frames(void *mac_drv, u16 enable) dsaf_write_dev(drv, XGMAC_MAC_PAUSE_TIME_REG, enable); } -/** - *hns_xgmac_get_id - get xgmac port id - *@mac_drv: mac driver - *@newval:xgmac max frame length - */ -static void hns_xgmac_get_id(void *mac_drv, u8 *mac_id) -{ - struct mac_driver *drv = (struct mac_driver *)mac_drv; - - *mac_id = drv->mac_id; -} - /** *hns_xgmac_config_max_frame_length - set xgmac max frame length *@mac_drv: mac driver @@ -833,7 +821,6 @@ void *hns_xgmac_config(struct hns_mac_cb *mac_cb, struct mac_params *mac_param) mac_drv->config_half_duplex = NULL; mac_drv->set_rx_ignore_pause_frames = hns_xgmac_set_rx_ignore_pause_frames; - mac_drv->mac_get_id = hns_xgmac_get_id; mac_drv->mac_free = hns_xgmac_free; mac_drv->adjust_link = NULL; mac_drv->set_tx_auto_pause_frames = hns_xgmac_set_tx_auto_pause_frames; -- cgit From 9f1607b8b53ef4e706e606ab20fff5496e005151 Mon Sep 17 00:00:00 2001 From: Kejian Yan Date: Sat, 1 Apr 2017 12:03:42 +0100 Subject: net: hns: Remove redundant mac table operations This patch removes redundant functions used only for debugging purposes. Reported-by: Weiwei Deng Signed-off-by: Kejian Yan Reviewed-by: Salil Mehta Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 160 --------------------- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h | 10 -- 2 files changed, 170 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index 6a069ffd8e5f..abd8aecdc5a8 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -2008,166 +2008,6 @@ int hns_dsaf_clr_mac_mc_port(struct dsaf_device *dsaf_dev, u8 mac_id, return ret; } -/** - * hns_dsaf_get_mac_uc_entry - get mac uc entry - * @dsaf_dev: dsa fabric device struct pointer - * @mac_entry: mac entry - */ -int hns_dsaf_get_mac_uc_entry(struct dsaf_device *dsaf_dev, - struct dsaf_drv_mac_single_dest_entry *mac_entry) -{ - u16 entry_index = DSAF_INVALID_ENTRY_IDX; - struct dsaf_drv_tbl_tcam_key mac_key; - - struct dsaf_tbl_tcam_ucast_cfg mac_data; - struct dsaf_tbl_tcam_data tcam_data; - - /* check macaddr */ - if (MAC_IS_ALL_ZEROS(mac_entry->addr) || - MAC_IS_BROADCAST(mac_entry->addr)) { - dev_err(dsaf_dev->dev, "get_entry failed,addr %pM\n", - mac_entry->addr); - return -EINVAL; - } - - /*config key */ - hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id, - mac_entry->in_port_num, mac_entry->addr); - - /*check exist? */ - entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); - if (entry_index == DSAF_INVALID_ENTRY_IDX) { - /*find none, error */ - dev_err(dsaf_dev->dev, - "get_uc_entry failed, %s Mac key(%#x:%#x)\n", - dsaf_dev->ae_dev.name, - mac_key.high.val, mac_key.low.val); - return -EINVAL; - } - dev_dbg(dsaf_dev->dev, - "get_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", - dsaf_dev->ae_dev.name, mac_key.high.val, - mac_key.low.val, entry_index); - - /* read entry */ - hns_dsaf_tcam_uc_get(dsaf_dev, entry_index, &tcam_data, &mac_data); - - mac_key.high.val = le32_to_cpu(tcam_data.tbl_tcam_data_high); - mac_key.low.val = le32_to_cpu(tcam_data.tbl_tcam_data_low); - - mac_entry->port_num = mac_data.tbl_ucast_out_port; - - return 0; -} - -/** - * hns_dsaf_get_mac_mc_entry - get mac mc entry - * @dsaf_dev: dsa fabric device struct pointer - * @mac_entry: mac entry - */ -int hns_dsaf_get_mac_mc_entry(struct dsaf_device *dsaf_dev, - struct dsaf_drv_mac_multi_dest_entry *mac_entry) -{ - u16 entry_index = DSAF_INVALID_ENTRY_IDX; - struct dsaf_drv_tbl_tcam_key mac_key; - - struct dsaf_tbl_tcam_mcast_cfg mac_data; - struct dsaf_tbl_tcam_data tcam_data; - - /*check mac addr */ - if (MAC_IS_ALL_ZEROS(mac_entry->addr) || - MAC_IS_BROADCAST(mac_entry->addr)) { - dev_err(dsaf_dev->dev, "get_entry failed,addr %pM\n", - mac_entry->addr); - return -EINVAL; - } - - /*config key */ - hns_dsaf_set_mac_key(dsaf_dev, &mac_key, mac_entry->in_vlan_id, - mac_entry->in_port_num, mac_entry->addr); - - /*check exist? */ - entry_index = hns_dsaf_find_soft_mac_entry(dsaf_dev, &mac_key); - if (entry_index == DSAF_INVALID_ENTRY_IDX) { - /* find none, error */ - dev_err(dsaf_dev->dev, - "get_mac_uc_entry failed, %s Mac key(%#x:%#x)\n", - dsaf_dev->ae_dev.name, mac_key.high.val, - mac_key.low.val); - return -EINVAL; - } - dev_dbg(dsaf_dev->dev, - "get_mac_uc_entry, %s Mac key(%#x:%#x) entry_index%d\n", - dsaf_dev->ae_dev.name, mac_key.high.val, - mac_key.low.val, entry_index); - - /*read entry */ - hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, &tcam_data, &mac_data); - - mac_key.high.val = le32_to_cpu(tcam_data.tbl_tcam_data_high); - mac_key.low.val = le32_to_cpu(tcam_data.tbl_tcam_data_low); - - mac_entry->port_mask[0] = mac_data.tbl_mcast_port_msk[0] & 0x3F; - return 0; -} - -/** - * hns_dsaf_get_mac_entry_by_index - get mac entry by tab index - * @dsaf_dev: dsa fabric device struct pointer - * @entry_index: tab entry index - * @mac_entry: mac entry - */ -int hns_dsaf_get_mac_entry_by_index( - struct dsaf_device *dsaf_dev, - u16 entry_index, struct dsaf_drv_mac_multi_dest_entry *mac_entry) -{ - struct dsaf_drv_tbl_tcam_key mac_key; - - struct dsaf_tbl_tcam_mcast_cfg mac_data; - struct dsaf_tbl_tcam_ucast_cfg mac_uc_data; - struct dsaf_tbl_tcam_data tcam_data; - char mac_addr[ETH_ALEN] = {0}; - - if (entry_index >= dsaf_dev->tcam_max_num) { - /* find none, del error */ - dev_err(dsaf_dev->dev, "get_uc_entry failed, %s\n", - dsaf_dev->ae_dev.name); - return -EINVAL; - } - - /* mc entry, do read opt */ - hns_dsaf_tcam_mc_get(dsaf_dev, entry_index, &tcam_data, &mac_data); - - mac_key.high.val = le32_to_cpu(tcam_data.tbl_tcam_data_high); - mac_key.low.val = le32_to_cpu(tcam_data.tbl_tcam_data_low); - - mac_entry->port_mask[0] = mac_data.tbl_mcast_port_msk[0] & 0x3F; - - /***get mac addr*/ - mac_addr[0] = mac_key.high.bits.mac_0; - mac_addr[1] = mac_key.high.bits.mac_1; - mac_addr[2] = mac_key.high.bits.mac_2; - mac_addr[3] = mac_key.high.bits.mac_3; - mac_addr[4] = mac_key.low.bits.mac_4; - mac_addr[5] = mac_key.low.bits.mac_5; - /**is mc or uc*/ - if (MAC_IS_MULTICAST((u8 *)mac_addr) || - MAC_IS_L3_MULTICAST((u8 *)mac_addr)) { - /**mc donot do*/ - } else { - /*is not mc, just uc... */ - hns_dsaf_tcam_uc_get(dsaf_dev, entry_index, &tcam_data, - &mac_uc_data); - - mac_key.high.val = le32_to_cpu(tcam_data.tbl_tcam_data_high); - mac_key.low.val = le32_to_cpu(tcam_data.tbl_tcam_data_low); - - mac_entry->port_mask[0] = (1 << mac_uc_data.tbl_ucast_out_port); - } - - return 0; -} - static struct dsaf_device *hns_dsaf_alloc_dev(struct device *dev, size_t sizeof_priv) { diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h index e2d71be4b573..4db02e2d1299 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -435,15 +435,6 @@ int hns_dsaf_del_mac_entry(struct dsaf_device *dsaf_dev, u16 vlan_id, u8 in_port_num, u8 *addr); int hns_dsaf_del_mac_mc_port(struct dsaf_device *dsaf_dev, struct dsaf_drv_mac_single_dest_entry *mac_entry); -int hns_dsaf_get_mac_uc_entry(struct dsaf_device *dsaf_dev, - struct dsaf_drv_mac_single_dest_entry *mac_entry); -int hns_dsaf_get_mac_mc_entry(struct dsaf_device *dsaf_dev, - struct dsaf_drv_mac_multi_dest_entry *mac_entry); -int hns_dsaf_get_mac_entry_by_index( - struct dsaf_device *dsaf_dev, - u16 entry_index, - struct dsaf_drv_mac_multi_dest_entry *mac_entry); - void hns_dsaf_fix_mac_mode(struct hns_mac_cb *mac_cb); int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev); @@ -473,5 +464,4 @@ int hns_dsaf_rm_mac_addr( int hns_dsaf_clr_mac_mc_port(struct dsaf_device *dsaf_dev, u8 mac_id, u8 port_num); - #endif /* __HNS_DSAF_MAIN_H__ */ -- cgit From 6961acfa5c2b34d421f8a48780efc1779cad73e4 Mon Sep 17 00:00:00 2001 From: Kejian Yan Date: Sat, 1 Apr 2017 12:03:43 +0100 Subject: net: hns: Clean redundant code from hns_mdio.c file This patch cleans the redundant code from hns_mdio.c. Reported-by: Ping Zhang Signed-off-by: Kejian Yan Reviewed-by: Salil Mehta Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns_mdio.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns_mdio.c b/drivers/net/ethernet/hisilicon/hns_mdio.c index 501eb2090ca6..fad1c5b1b991 100644 --- a/drivers/net/ethernet/hisilicon/hns_mdio.c +++ b/drivers/net/ethernet/hisilicon/hns_mdio.c @@ -23,17 +23,9 @@ #include #include #include -#include #define MDIO_DRV_NAME "Hi-HNS_MDIO" #define MDIO_BUS_NAME "Hisilicon MII Bus" -#define MDIO_DRV_VERSION "1.3.0" -#define MDIO_COPYRIGHT "Copyright(c) 2015 Huawei Corporation." -#define MDIO_DRV_STRING MDIO_BUS_NAME -#define MDIO_DEFAULT_DEVICE_DESCR MDIO_BUS_NAME - -#define MDIO_CTL_DEV_ADDR(x) (x & 0x1f) -#define MDIO_CTL_PORT_ADDR(x) ((x & 0x1f) << 5) #define MDIO_TIMEOUT 1000000 @@ -64,9 +56,7 @@ struct hns_mdio_device { #define MDIO_CMD_DEVAD_S 0 #define MDIO_CMD_PRTAD_M 0x1f #define MDIO_CMD_PRTAD_S 5 -#define MDIO_CMD_OP_M 0x3 #define MDIO_CMD_OP_S 10 -#define MDIO_CMD_ST_M 0x3 #define MDIO_CMD_ST_S 12 #define MDIO_CMD_START_B 14 -- cgit From d592a4a4b9f2727efd8d7718bb5c9ae0d03614bd Mon Sep 17 00:00:00 2001 From: Kejian Yan Date: Sat, 1 Apr 2017 12:03:44 +0100 Subject: net: hns: Optimise the code in hns_mdio_wait_ready() This patch fixes the code to clear pclint warning/info. Reported-by: Ping Zhang Signed-off-by: Kejian Yan Reviewed-by: Salil Mehta Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns_mdio.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns_mdio.c b/drivers/net/ethernet/hisilicon/hns_mdio.c index fad1c5b1b991..e5221d95afe1 100644 --- a/drivers/net/ethernet/hisilicon/hns_mdio.c +++ b/drivers/net/ethernet/hisilicon/hns_mdio.c @@ -175,18 +175,20 @@ static int mdio_sc_cfg_reg_write(struct hns_mdio_device *mdio_dev, static int hns_mdio_wait_ready(struct mii_bus *bus) { struct hns_mdio_device *mdio_dev = bus->priv; + u32 cmd_reg_value; int i; - u32 cmd_reg_value = 1; /* waitting for MDIO_COMMAND_REG 's mdio_start==0 */ /* after that can do read or write*/ - for (i = 0; cmd_reg_value; i++) { + for (i = 0; i < MDIO_TIMEOUT; i++) { cmd_reg_value = MDIO_GET_REG_BIT(mdio_dev, MDIO_COMMAND_REG, MDIO_CMD_START_B); - if (i == MDIO_TIMEOUT) - return -ETIMEDOUT; + if (!cmd_reg_value) + break; } + if ((i == MDIO_TIMEOUT) && cmd_reg_value) + return -ETIMEDOUT; return 0; } -- cgit From a2185587ade79b9649639721aa21c4cefc1aea6b Mon Sep 17 00:00:00 2001 From: Kejian Yan Date: Sat, 1 Apr 2017 12:03:45 +0100 Subject: net: hns: Simplify the exception sequence in hns_ppe_init() We need to free all ppe submodule if it fails to initialize ppe by any fault, so this patch will free all ppe resource before hns_ppe_init() returns exception situation Reported-by: JinchuanTian Signed-off-by: Kejian Yan Reviewed-by: Salil Mehta Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c index 6ea872287307..eba406bea52f 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c @@ -496,17 +496,17 @@ void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data) */ int hns_ppe_init(struct dsaf_device *dsaf_dev) { - int i, k; int ret; + int i; for (i = 0; i < HNS_PPE_COM_NUM; i++) { ret = hns_ppe_common_get_cfg(dsaf_dev, i); if (ret) - goto get_ppe_cfg_fail; + goto get_cfg_fail; ret = hns_rcb_common_get_cfg(dsaf_dev, i); if (ret) - goto get_rcb_cfg_fail; + goto get_cfg_fail; hns_ppe_get_cfg(dsaf_dev->ppe_common[i]); @@ -518,13 +518,12 @@ int hns_ppe_init(struct dsaf_device *dsaf_dev) return 0; -get_rcb_cfg_fail: - hns_ppe_common_free_cfg(dsaf_dev, i); -get_ppe_cfg_fail: - for (k = i - 1; k >= 0; k--) { - hns_rcb_common_free_cfg(dsaf_dev, k); - hns_ppe_common_free_cfg(dsaf_dev, k); +get_cfg_fail: + for (i = 0; i < HNS_PPE_COM_NUM; i++) { + hns_rcb_common_free_cfg(dsaf_dev, i); + hns_ppe_common_free_cfg(dsaf_dev, i); } + return ret; } -- cgit From 76b588bc523bee796834ebd319f6a71ad3eddbae Mon Sep 17 00:00:00 2001 From: Kejian Yan Date: Sat, 1 Apr 2017 12:03:46 +0100 Subject: net: hns: Adjust the SBM module buffer threshold HNS needs SMB Buffers to store at least two packets after sending pause frame because of the link delay. The MTU of HNS is 9728. As the processor user manual described, the SBM buffer threshold should be modified. Reported-by: Ping Zhang Signed-off-by: Kejian Yan Reviewed-by: Salil Mehta Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c index abd8aecdc5a8..d07b4fe45a44 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c @@ -510,10 +510,10 @@ static void hns_dsafv2_sbm_bp_wl_cfg(struct dsaf_device *dsaf_dev) o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_M, - DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 48); + DSAFV2_SBM_CFG3_SET_BUF_NUM_NO_PFC_S, 55); dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_M, - DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 80); + DSAFV2_SBM_CFG3_RESET_BUF_NUM_NO_PFC_S, 110); dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); /* for no enable pfc mode */ @@ -521,10 +521,10 @@ static void hns_dsafv2_sbm_bp_wl_cfg(struct dsaf_device *dsaf_dev) o_sbm_bp_cfg = dsaf_read_dev(dsaf_dev, reg); dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_M, - DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_S, 192); + DSAFV2_SBM_CFG4_SET_BUF_NUM_NO_PFC_S, 128); dsaf_set_field(o_sbm_bp_cfg, DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_M, - DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_S, 240); + DSAFV2_SBM_CFG4_RESET_BUF_NUM_NO_PFC_S, 192); dsaf_write_dev(dsaf_dev, reg, o_sbm_bp_cfg); } -- cgit From 820c90cb3e2e452c80824391953cab9d5b5af154 Mon Sep 17 00:00:00 2001 From: lipeng Date: Sat, 1 Apr 2017 12:03:47 +0100 Subject: net: hns: Avoid Hip06 chip TX packet line bug There is a bug on Hip06 that tx ring interrupts packets count will be clear when drivers send data to tx ring, so that the tx packets count will never upgrade to packets line, and cause the interrupts engendered was delayed. Sometimes, it will cause sending performance lower than expected. To fix this bug, we set tx ring interrupts packets line to 1 forever, to avoid count clear. And set the gap time to 20us, to solve the problem that too many interrupts engendered when packets line is 1. This patch could advance the send performance on ARM from 6.6G to 9.37G when an iperf send thread on ARM and an iperf send thread on X86 for XGE. Signed-off-by: lipeng Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.c | 5 ++ drivers/net/ethernet/hisilicon/hns/hnae.h | 6 +- drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c | 78 ++++++++++++----- drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c | 101 ++++++++++++++++------ drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h | 23 ++++- drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h | 2 +- drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 24 +++-- 7 files changed, 169 insertions(+), 70 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c index ef818b7d5021..9d9b6e6dd988 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.c +++ b/drivers/net/ethernet/hisilicon/hns/hnae.c @@ -57,10 +57,15 @@ static int hnae_alloc_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) static void hnae_free_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) { + if (unlikely(!cb->priv)) + return; + if (cb->type == DESC_TYPE_SKB) dev_kfree_skb_any((struct sk_buff *)cb->priv); else if (unlikely(is_rx_ring(ring))) put_page((struct page *)cb->priv); + + cb->priv = NULL; } static int hnae_map_buffer(struct hnae_ring *ring, struct hnae_desc_cb *cb) diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index 859c53619ba2..094313843583 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -488,11 +488,11 @@ struct hnae_ae_ops { u32 auto_neg, u32 rx_en, u32 tx_en); void (*get_coalesce_usecs)(struct hnae_handle *handle, u32 *tx_usecs, u32 *rx_usecs); - void (*get_rx_max_coalesced_frames)(struct hnae_handle *handle, - u32 *tx_frames, u32 *rx_frames); + void (*get_max_coalesced_frames)(struct hnae_handle *handle, + u32 *tx_frames, u32 *rx_frames); int (*set_coalesce_usecs)(struct hnae_handle *handle, u32 timeout); int (*set_coalesce_frames)(struct hnae_handle *handle, - u32 coalesce_frames); + u32 tx_frames, u32 rx_frames); void (*get_coalesce_range)(struct hnae_handle *handle, u32 *tx_frames_low, u32 *rx_frames_low, u32 *tx_frames_high, u32 *rx_frames_high, diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index f0142e50048e..ff864a187d5a 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -487,15 +487,21 @@ static void hns_ae_get_coalesce_usecs(struct hnae_handle *handle, ring_pair->port_id_in_comm); } -static void hns_ae_get_rx_max_coalesced_frames(struct hnae_handle *handle, - u32 *tx_frames, u32 *rx_frames) +static void hns_ae_get_max_coalesced_frames(struct hnae_handle *handle, + u32 *tx_frames, u32 *rx_frames) { struct ring_pair_cb *ring_pair = container_of(handle->qs[0], struct ring_pair_cb, q); + struct dsaf_device *dsaf_dev = hns_ae_get_dsaf_dev(handle->dev); - *tx_frames = hns_rcb_get_coalesced_frames(ring_pair->rcb_common, - ring_pair->port_id_in_comm); - *rx_frames = hns_rcb_get_coalesced_frames(ring_pair->rcb_common, + if (AE_IS_VER1(dsaf_dev->dsaf_ver) || + handle->port_type == HNAE_PORT_DEBUG) + *tx_frames = hns_rcb_get_rx_coalesced_frames( + ring_pair->rcb_common, ring_pair->port_id_in_comm); + else + *tx_frames = hns_rcb_get_tx_coalesced_frames( + ring_pair->rcb_common, ring_pair->port_id_in_comm); + *rx_frames = hns_rcb_get_rx_coalesced_frames(ring_pair->rcb_common, ring_pair->port_id_in_comm); } @@ -509,15 +515,34 @@ static int hns_ae_set_coalesce_usecs(struct hnae_handle *handle, ring_pair->rcb_common, ring_pair->port_id_in_comm, timeout); } -static int hns_ae_set_coalesce_frames(struct hnae_handle *handle, - u32 coalesce_frames) +static int hns_ae_set_coalesce_frames(struct hnae_handle *handle, + u32 tx_frames, u32 rx_frames) { + int ret; struct ring_pair_cb *ring_pair = container_of(handle->qs[0], struct ring_pair_cb, q); + struct dsaf_device *dsaf_dev = hns_ae_get_dsaf_dev(handle->dev); - return hns_rcb_set_coalesced_frames( - ring_pair->rcb_common, - ring_pair->port_id_in_comm, coalesce_frames); + if (AE_IS_VER1(dsaf_dev->dsaf_ver) || + handle->port_type == HNAE_PORT_DEBUG) { + if (tx_frames != rx_frames) + return -EINVAL; + return hns_rcb_set_rx_coalesced_frames( + ring_pair->rcb_common, + ring_pair->port_id_in_comm, rx_frames); + } else { + if (tx_frames != 1) + return -EINVAL; + ret = hns_rcb_set_tx_coalesced_frames( + ring_pair->rcb_common, + ring_pair->port_id_in_comm, tx_frames); + if (ret) + return ret; + + return hns_rcb_set_rx_coalesced_frames( + ring_pair->rcb_common, + ring_pair->port_id_in_comm, rx_frames); + } } static void hns_ae_get_coalesce_range(struct hnae_handle *handle, @@ -528,20 +553,27 @@ static void hns_ae_get_coalesce_range(struct hnae_handle *handle, { struct dsaf_device *dsaf_dev; + assert(handle); + dsaf_dev = hns_ae_get_dsaf_dev(handle->dev); - *tx_frames_low = HNS_RCB_MIN_COALESCED_FRAMES; - *rx_frames_low = HNS_RCB_MIN_COALESCED_FRAMES; - *tx_frames_high = - (dsaf_dev->desc_num - 1 > HNS_RCB_MAX_COALESCED_FRAMES) ? - HNS_RCB_MAX_COALESCED_FRAMES : dsaf_dev->desc_num - 1; - *rx_frames_high = - (dsaf_dev->desc_num - 1 > HNS_RCB_MAX_COALESCED_FRAMES) ? - HNS_RCB_MAX_COALESCED_FRAMES : dsaf_dev->desc_num - 1; - *tx_usecs_low = 0; - *rx_usecs_low = 0; - *tx_usecs_high = HNS_RCB_MAX_COALESCED_USECS; - *rx_usecs_high = HNS_RCB_MAX_COALESCED_USECS; + *tx_frames_low = HNS_RCB_TX_FRAMES_LOW; + *rx_frames_low = HNS_RCB_RX_FRAMES_LOW; + + if (AE_IS_VER1(dsaf_dev->dsaf_ver) || + handle->port_type == HNAE_PORT_DEBUG) + *tx_frames_high = + (dsaf_dev->desc_num - 1 > HNS_RCB_TX_FRAMES_HIGH) ? + HNS_RCB_TX_FRAMES_HIGH : dsaf_dev->desc_num - 1; + else + *tx_frames_high = 1; + + *rx_frames_high = (dsaf_dev->desc_num - 1 > HNS_RCB_RX_FRAMES_HIGH) ? + HNS_RCB_RX_FRAMES_HIGH : dsaf_dev->desc_num - 1; + *tx_usecs_low = HNS_RCB_TX_USECS_LOW; + *rx_usecs_low = HNS_RCB_RX_USECS_LOW; + *tx_usecs_high = HNS_RCB_TX_USECS_HIGH; + *rx_usecs_high = HNS_RCB_RX_USECS_HIGH; } void hns_ae_update_stats(struct hnae_handle *handle, @@ -875,7 +907,7 @@ static struct hnae_ae_ops hns_dsaf_ops = { .get_autoneg = hns_ae_get_autoneg, .set_pauseparam = hns_ae_set_pauseparam, .get_coalesce_usecs = hns_ae_get_coalesce_usecs, - .get_rx_max_coalesced_frames = hns_ae_get_rx_max_coalesced_frames, + .get_max_coalesced_frames = hns_ae_get_max_coalesced_frames, .set_coalesce_usecs = hns_ae_set_coalesce_usecs, .set_coalesce_frames = hns_ae_set_coalesce_frames, .get_coalesce_range = hns_ae_get_coalesce_range, diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c index a6ab1680d294..9b66057f08c6 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c @@ -254,7 +254,7 @@ static void hns_rcb_ring_init(struct ring_pair_cb *ring_pair, int ring_type) dsaf_write_dev(q, RCB_RING_TX_RING_BD_NUM_REG, ring_pair->port_id_in_comm); dsaf_write_dev(q, RCB_RING_TX_RING_PKTLINE_REG, - ring_pair->port_id_in_comm); + ring_pair->port_id_in_comm + HNS_RCB_TX_PKTLINE_OFFSET); } } @@ -284,13 +284,27 @@ static void hns_rcb_set_port_desc_cnt(struct rcb_common_cb *rcb_common, static void hns_rcb_set_port_timeout( struct rcb_common_cb *rcb_common, u32 port_idx, u32 timeout) { - if (AE_IS_VER1(rcb_common->dsaf_dev->dsaf_ver)) + if (AE_IS_VER1(rcb_common->dsaf_dev->dsaf_ver)) { dsaf_write_dev(rcb_common, RCB_CFG_OVERTIME_REG, timeout * HNS_RCB_CLK_FREQ_MHZ); - else + } else if (!HNS_DSAF_IS_DEBUG(rcb_common->dsaf_dev)) { + if (timeout > HNS_RCB_DEF_GAP_TIME_USECS) + dsaf_write_dev(rcb_common, + RCB_PORT_INT_GAPTIME_REG + port_idx * 4, + HNS_RCB_DEF_GAP_TIME_USECS); + else + dsaf_write_dev(rcb_common, + RCB_PORT_INT_GAPTIME_REG + port_idx * 4, + timeout); + dsaf_write_dev(rcb_common, RCB_PORT_CFG_OVERTIME_REG + port_idx * 4, timeout); + } else { + dsaf_write_dev(rcb_common, + RCB_PORT_CFG_OVERTIME_REG + port_idx * 4, + timeout); + } } static int hns_rcb_common_get_port_num(struct rcb_common_cb *rcb_common) @@ -352,8 +366,12 @@ int hns_rcb_common_init_hw(struct rcb_common_cb *rcb_common) for (i = 0; i < port_num; i++) { hns_rcb_set_port_desc_cnt(rcb_common, i, rcb_common->desc_num); - (void)hns_rcb_set_coalesced_frames( - rcb_common, i, HNS_RCB_DEF_COALESCED_FRAMES); + hns_rcb_set_rx_coalesced_frames( + rcb_common, i, HNS_RCB_DEF_RX_COALESCED_FRAMES); + if (!AE_IS_VER1(rcb_common->dsaf_dev->dsaf_ver) && + !HNS_DSAF_IS_DEBUG(rcb_common->dsaf_dev)) + hns_rcb_set_tx_coalesced_frames( + rcb_common, i, HNS_RCB_DEF_TX_COALESCED_FRAMES); hns_rcb_set_port_timeout( rcb_common, i, HNS_RCB_DEF_COALESCED_USECS); } @@ -507,18 +525,34 @@ void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common) } /** - *hns_rcb_get_coalesced_frames - get rcb port coalesced frames + *hns_rcb_get_rx_coalesced_frames - get rcb port rx coalesced frames *@rcb_common: rcb_common device *@port_idx:port id in comm * *Returns: coalesced_frames */ -u32 hns_rcb_get_coalesced_frames( +u32 hns_rcb_get_rx_coalesced_frames( struct rcb_common_cb *rcb_common, u32 port_idx) { return dsaf_read_dev(rcb_common, RCB_CFG_PKTLINE_REG + port_idx * 4); } +/** + *hns_rcb_get_tx_coalesced_frames - get rcb port tx coalesced frames + *@rcb_common: rcb_common device + *@port_idx:port id in comm + * + *Returns: coalesced_frames + */ +u32 hns_rcb_get_tx_coalesced_frames( + struct rcb_common_cb *rcb_common, u32 port_idx) +{ + u64 reg; + + reg = RCB_CFG_PKTLINE_REG + (port_idx + HNS_RCB_TX_PKTLINE_OFFSET) * 4; + return dsaf_read_dev(rcb_common, reg); +} + /** *hns_rcb_get_coalesce_usecs - get rcb port coalesced time_out *@rcb_common: rcb_common device @@ -561,33 +595,47 @@ int hns_rcb_set_coalesce_usecs( return -EINVAL; } } - if (timeout > HNS_RCB_MAX_COALESCED_USECS) { + if (timeout > HNS_RCB_MAX_COALESCED_USECS || timeout == 0) { dev_err(rcb_common->dsaf_dev->dev, - "error: coalesce_usecs setting supports 0~1023us\n"); + "error: coalesce_usecs setting supports 1~1023us\n"); return -EINVAL; } + hns_rcb_set_port_timeout(rcb_common, port_idx, timeout); + return 0; +} - if (!AE_IS_VER1(rcb_common->dsaf_dev->dsaf_ver)) { - if (timeout == 0) - /* set timeout to 0, Disable gap time */ - dsaf_set_reg_field(rcb_common->io_base, - RCB_INT_GAP_TIME_REG + port_idx * 4, - PPE_INT_GAPTIME_M, PPE_INT_GAPTIME_B, - 0); - else - /* set timeout non 0, restore gap time to 1 */ - dsaf_set_reg_field(rcb_common->io_base, - RCB_INT_GAP_TIME_REG + port_idx * 4, - PPE_INT_GAPTIME_M, PPE_INT_GAPTIME_B, - 1); +/** + *hns_rcb_set_tx_coalesced_frames - set rcb coalesced frames + *@rcb_common: rcb_common device + *@port_idx:port id in comm + *@coalesced_frames:tx/rx BD num for coalesced frames + * + * Returns: + * Zero for success, or an error code in case of failure + */ +int hns_rcb_set_tx_coalesced_frames( + struct rcb_common_cb *rcb_common, u32 port_idx, u32 coalesced_frames) +{ + u32 old_waterline = + hns_rcb_get_tx_coalesced_frames(rcb_common, port_idx); + u64 reg; + + if (coalesced_frames == old_waterline) + return 0; + + if (coalesced_frames != 1) { + dev_err(rcb_common->dsaf_dev->dev, + "error: not support tx coalesce_frames setting!\n"); + return -EINVAL; } - hns_rcb_set_port_timeout(rcb_common, port_idx, timeout); + reg = RCB_CFG_PKTLINE_REG + (port_idx + HNS_RCB_TX_PKTLINE_OFFSET) * 4; + dsaf_write_dev(rcb_common, reg, coalesced_frames); return 0; } /** - *hns_rcb_set_coalesced_frames - set rcb coalesced frames + *hns_rcb_set_rx_coalesced_frames - set rcb rx coalesced frames *@rcb_common: rcb_common device *@port_idx:port id in comm *@coalesced_frames:tx/rx BD num for coalesced frames @@ -595,10 +643,11 @@ int hns_rcb_set_coalesce_usecs( * Returns: * Zero for success, or an error code in case of failure */ -int hns_rcb_set_coalesced_frames( +int hns_rcb_set_rx_coalesced_frames( struct rcb_common_cb *rcb_common, u32 port_idx, u32 coalesced_frames) { - u32 old_waterline = hns_rcb_get_coalesced_frames(rcb_common, port_idx); + u32 old_waterline = + hns_rcb_get_rx_coalesced_frames(rcb_common, port_idx); if (coalesced_frames == old_waterline) return 0; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h index afe563cf4c5d..a664ee88ab45 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h @@ -35,12 +35,23 @@ struct rcb_common_cb; #define HNS_RCB_REG_OFFSET 0x10000 +#define HNS_RCB_TX_FRAMES_LOW 1 +#define HNS_RCB_RX_FRAMES_LOW 1 +#define HNS_RCB_TX_FRAMES_HIGH 1023 +#define HNS_RCB_RX_FRAMES_HIGH 1023 +#define HNS_RCB_TX_USECS_LOW 1 +#define HNS_RCB_RX_USECS_LOW 1 +#define HNS_RCB_TX_USECS_HIGH 1023 +#define HNS_RCB_RX_USECS_HIGH 1023 #define HNS_RCB_MAX_COALESCED_FRAMES 1023 #define HNS_RCB_MIN_COALESCED_FRAMES 1 -#define HNS_RCB_DEF_COALESCED_FRAMES 50 +#define HNS_RCB_DEF_RX_COALESCED_FRAMES 50 +#define HNS_RCB_DEF_TX_COALESCED_FRAMES 1 #define HNS_RCB_CLK_FREQ_MHZ 350 #define HNS_RCB_MAX_COALESCED_USECS 0x3ff -#define HNS_RCB_DEF_COALESCED_USECS 50 +#define HNS_RCB_DEF_COALESCED_USECS 30 +#define HNS_RCB_DEF_GAP_TIME_USECS 20 +#define HNS_RCB_TX_PKTLINE_OFFSET 8 #define HNS_RCB_COMMON_ENDIAN 1 @@ -125,13 +136,17 @@ void hns_rcbv2_int_clr_hw(struct hnae_queue *q, u32 flag); void hns_rcb_init_hw(struct ring_pair_cb *ring); void hns_rcb_reset_ring_hw(struct hnae_queue *q); void hns_rcb_wait_fbd_clean(struct hnae_queue **qs, int q_num, u32 flag); -u32 hns_rcb_get_coalesced_frames( +u32 hns_rcb_get_rx_coalesced_frames( + struct rcb_common_cb *rcb_common, u32 port_idx); +u32 hns_rcb_get_tx_coalesced_frames( struct rcb_common_cb *rcb_common, u32 port_idx); u32 hns_rcb_get_coalesce_usecs( struct rcb_common_cb *rcb_common, u32 port_idx); int hns_rcb_set_coalesce_usecs( struct rcb_common_cb *rcb_common, u32 port_idx, u32 timeout); -int hns_rcb_set_coalesced_frames( +int hns_rcb_set_rx_coalesced_frames( + struct rcb_common_cb *rcb_common, u32 port_idx, u32 coalesced_frames); +int hns_rcb_set_tx_coalesced_frames( struct rcb_common_cb *rcb_common, u32 port_idx, u32 coalesced_frames); void hns_rcb_update_stats(struct hnae_queue *queue); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h index 4b8af68af02e..46a52d9bb196 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h @@ -421,7 +421,7 @@ #define RCB_CFG_OVERTIME_REG 0x9300 #define RCB_CFG_PKTLINE_INT_NUM_REG 0x9304 #define RCB_CFG_OVERTIME_INT_NUM_REG 0x9308 -#define RCB_INT_GAP_TIME_REG 0x9400 +#define RCB_PORT_INT_GAPTIME_REG 0x9400 #define RCB_PORT_CFG_OVERTIME_REG 0x9430 #define RCB_RING_RX_RING_BASEADDR_L_REG 0x00000 diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index 3a2a34250cc0..36f33bdb76ac 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -764,14 +764,14 @@ static int hns_get_coalesce(struct net_device *net_dev, ec->use_adaptive_tx_coalesce = 1; if ((!ops->get_coalesce_usecs) || - (!ops->get_rx_max_coalesced_frames)) + (!ops->get_max_coalesced_frames)) return -ESRCH; ops->get_coalesce_usecs(priv->ae_handle, &ec->tx_coalesce_usecs, &ec->rx_coalesce_usecs); - ops->get_rx_max_coalesced_frames( + ops->get_max_coalesced_frames( priv->ae_handle, &ec->tx_max_coalesced_frames, &ec->rx_max_coalesced_frames); @@ -801,30 +801,28 @@ static int hns_set_coalesce(struct net_device *net_dev, { struct hns_nic_priv *priv = netdev_priv(net_dev); struct hnae_ae_ops *ops; - int ret; + int rc1, rc2; ops = priv->ae_handle->dev->ops; if (ec->tx_coalesce_usecs != ec->rx_coalesce_usecs) return -EINVAL; - if (ec->rx_max_coalesced_frames != ec->tx_max_coalesced_frames) - return -EINVAL; - if ((!ops->set_coalesce_usecs) || (!ops->set_coalesce_frames)) return -ESRCH; - ret = ops->set_coalesce_usecs(priv->ae_handle, + rc1 = ops->set_coalesce_usecs(priv->ae_handle, ec->rx_coalesce_usecs); - if (ret) - return ret; - ret = ops->set_coalesce_frames( - priv->ae_handle, - ec->rx_max_coalesced_frames); + rc2 = ops->set_coalesce_frames(priv->ae_handle, + ec->tx_max_coalesced_frames, + ec->rx_max_coalesced_frames); - return ret; + if (rc1 || rc2) + return -EINVAL; + + return 0; } /** -- cgit From b4957ab0826f6f7efdfdc648521e1c4c3fc6ceda Mon Sep 17 00:00:00 2001 From: Salil Date: Sat, 1 Apr 2017 12:03:48 +0100 Subject: net: hns: Some checkpatch.pl script & warning fixes This patch fixes some checkpatch.pl script caught errors and warnings during the compilation time. Signed-off-by: Salil Mehta Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hnae.h | 1 - drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c | 11 +++++------ drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h | 2 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c | 1 - drivers/net/ethernet/hisilicon/hns/hns_enet.c | 9 +++++---- drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 1 - 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index 094313843583..04211ac73b36 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -103,7 +103,6 @@ enum hnae_led_state { #define HNS_RX_FLAG_L4ID_TCP 0x1 #define HNS_RX_FLAG_L4ID_SCTP 0x3 - #define HNS_TXD_ASID_S 0 #define HNS_TXD_ASID_M (0xff << HNS_TXD_ASID_S) #define HNS_TXD_BUFNUM_S 8 diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c index 035db868de04..74bd260ca02a 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c @@ -86,12 +86,11 @@ static void hns_gmac_disable(void *mac_drv, enum mac_commom_mode mode) dsaf_set_dev_bit(drv, GMAC_PORT_EN_REG, GMAC_PORT_RX_EN_B, 0); } -/** -*hns_gmac_get_en - get port enable -*@mac_drv:mac device -*@rx:rx enable -*@tx:tx enable -*/ +/* hns_gmac_get_en - get port enable + * @mac_drv:mac device + * @rx:rx enable + * @tx:tx enable + */ static void hns_gmac_get_en(void *mac_drv, u32 *rx, u32 *tx) { struct mac_driver *drv = (struct mac_driver *)mac_drv; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h index 4db02e2d1299..4507e8222683 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h @@ -68,7 +68,7 @@ enum dsaf_roce_qos_sl { }; #define DSAF_STATS_READ(p, offset) (*((u64 *)((u8 *)(p) + (offset)))) -#define HNS_DSAF_IS_DEBUG(dev) (dev->dsaf_mode == DSAF_MODE_DISABLE_SP) +#define HNS_DSAF_IS_DEBUG(dev) ((dev)->dsaf_mode == DSAF_MODE_DISABLE_SP) enum hal_dsaf_mode { HRD_DSAF_NO_DSAF_MODE = 0x0, diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c index 9b66057f08c6..c20a0f4f8f02 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c @@ -471,7 +471,6 @@ static void hns_rcb_ring_pair_get_cfg(struct ring_pair_cb *ring_pair_cb) static int hns_rcb_get_port_in_comm( struct rcb_common_cb *rcb_common, int ring_idx) { - return ring_idx / (rcb_common->max_q_per_vf * rcb_common->max_vfn); } diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index e39c794d94e2..c6700b91a2df 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -512,7 +512,8 @@ static void hns_nic_reuse_page(struct sk_buff *skb, int i, int last_offset; bool twobufs; - twobufs = ((PAGE_SIZE < 8192) && hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048); + twobufs = ((PAGE_SIZE < 8192) && + hnae_buf_size(ring) == HNS_BUFFER_SIZE_2048); desc = &ring->desc[ring->next_to_clean]; size = le16_to_cpu(desc->rx.size); @@ -922,8 +923,8 @@ static int is_valid_clean_head(struct hnae_ring *ring, int h) /* netif_tx_lock will turn down the performance, set only when necessary */ #ifdef CONFIG_NET_POLL_CONTROLLER -#define NETIF_TX_LOCK(ring) spin_lock(&ring->lock) -#define NETIF_TX_UNLOCK(ring) spin_unlock(&ring->lock) +#define NETIF_TX_LOCK(ring) spin_lock(&(ring)->lock) +#define NETIF_TX_UNLOCK(ring) spin_unlock(&(ring)->lock) #else #define NETIF_TX_LOCK(ring) #define NETIF_TX_UNLOCK(ring) @@ -2012,7 +2013,7 @@ static void hns_nic_reset_subtask(struct hns_nic_priv *priv) static void hns_nic_service_event_complete(struct hns_nic_priv *priv) { WARN_ON(!test_bit(NIC_STATE_SERVICE_SCHED, &priv->state)); - + /* make sure to commit the things */ smp_mb__before_atomic(); clear_bit(NIC_STATE_SERVICE_SCHED, &priv->state); } diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index 36f33bdb76ac..b8fab149690f 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -1242,7 +1242,6 @@ hns_set_rss(struct net_device *netdev, const u32 *indir, const u8 *key, { struct hns_nic_priv *priv = netdev_priv(netdev); struct hnae_ae_ops *ops; - int ret; if (AE_IS_VER1(priv->enet_ver)) { netdev_err(netdev, -- cgit From d229d48d183fbc1391908decc7d2bcf09ca2f38f Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 1 Apr 2017 17:07:46 +0800 Subject: sctp: add SCTP_PR_STREAM_STATUS sockopt for prsctp Before when implementing sctp prsctp, SCTP_PR_STREAM_STATUS wasn't added, as it needs to save abandoned_(un)sent for every stream. After sctp stream reconf is added in sctp, assoc has structure sctp_stream_out to save per stream info. This patch is to add SCTP_PR_STREAM_STATUS by putting the prsctp per stream statistics into sctp_stream_out. v1->v2: fix an indent issue. Signed-off-by: Xin Long Acked-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 2 ++ include/uapi/linux/sctp.h | 1 + net/sctp/chunk.c | 14 +++++++++-- net/sctp/outqueue.c | 10 ++++++++ net/sctp/socket.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+), 2 deletions(-) diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 592decebac75..3e61a54424a1 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1315,6 +1315,8 @@ struct sctp_inithdr_host { struct sctp_stream_out { __u16 ssn; __u8 state; + __u64 abandoned_unsent[SCTP_PR_INDEX(MAX) + 1]; + __u64 abandoned_sent[SCTP_PR_INDEX(MAX) + 1]; }; struct sctp_stream_in { diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 7212870ef5d7..ced9d8b97426 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -115,6 +115,7 @@ typedef __s32 sctp_assoc_t; #define SCTP_PR_SUPPORTED 113 #define SCTP_DEFAULT_PRINFO 114 #define SCTP_PR_ASSOC_STATUS 115 +#define SCTP_PR_STREAM_STATUS 116 #define SCTP_RECONFIG_SUPPORTED 117 #define SCTP_ENABLE_STREAM_RESET 118 #define SCTP_RESET_STREAMS 119 diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index e3621cb4827f..697721a7a3f1 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -306,14 +306,24 @@ int sctp_chunk_abandoned(struct sctp_chunk *chunk) if (SCTP_PR_TTL_ENABLED(chunk->sinfo.sinfo_flags) && time_after(jiffies, chunk->msg->expires_at)) { - if (chunk->sent_count) + struct sctp_stream_out *streamout = + &chunk->asoc->stream->out[chunk->sinfo.sinfo_stream]; + + if (chunk->sent_count) { chunk->asoc->abandoned_sent[SCTP_PR_INDEX(TTL)]++; - else + streamout->abandoned_sent[SCTP_PR_INDEX(TTL)]++; + } else { chunk->asoc->abandoned_unsent[SCTP_PR_INDEX(TTL)]++; + streamout->abandoned_unsent[SCTP_PR_INDEX(TTL)]++; + } return 1; } else if (SCTP_PR_RTX_ENABLED(chunk->sinfo.sinfo_flags) && chunk->sent_count > chunk->sinfo.sinfo_timetolive) { + struct sctp_stream_out *streamout = + &chunk->asoc->stream->out[chunk->sinfo.sinfo_stream]; + chunk->asoc->abandoned_sent[SCTP_PR_INDEX(RTX)]++; + streamout->abandoned_sent[SCTP_PR_INDEX(RTX)]++; return 1; } else if (!SCTP_PR_POLICY(chunk->sinfo.sinfo_flags) && chunk->msg->expires_at && diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 025ccff67072..3f78d7f06e14 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -353,6 +353,8 @@ static int sctp_prsctp_prune_sent(struct sctp_association *asoc, struct sctp_chunk *chk, *temp; list_for_each_entry_safe(chk, temp, queue, transmitted_list) { + struct sctp_stream_out *streamout; + if (!SCTP_PR_PRIO_ENABLED(chk->sinfo.sinfo_flags) || chk->sinfo.sinfo_timetolive <= sinfo->sinfo_timetolive) continue; @@ -361,8 +363,10 @@ static int sctp_prsctp_prune_sent(struct sctp_association *asoc, sctp_insert_list(&asoc->outqueue.abandoned, &chk->transmitted_list); + streamout = &asoc->stream->out[chk->sinfo.sinfo_stream]; asoc->sent_cnt_removable--; asoc->abandoned_sent[SCTP_PR_INDEX(PRIO)]++; + streamout->abandoned_sent[SCTP_PR_INDEX(PRIO)]++; if (!chk->tsn_gap_acked) { if (chk->transport) @@ -396,6 +400,12 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc, q->out_qlen -= chk->skb->len; asoc->sent_cnt_removable--; asoc->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++; + if (chk->sinfo.sinfo_stream < asoc->stream->outcnt) { + struct sctp_stream_out *streamout = + &asoc->stream->out[chk->sinfo.sinfo_stream]; + + streamout->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++; + } msg_len -= SCTP_DATA_SNDSIZE(chk) + sizeof(struct sk_buff) + diff --git a/net/sctp/socket.c b/net/sctp/socket.c index ccc08fc39722..6489446925e6 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -6576,6 +6576,61 @@ out: return retval; } +static int sctp_getsockopt_pr_streamstatus(struct sock *sk, int len, + char __user *optval, + int __user *optlen) +{ + struct sctp_stream_out *streamout; + struct sctp_association *asoc; + struct sctp_prstatus params; + int retval = -EINVAL; + int policy; + + if (len < sizeof(params)) + goto out; + + len = sizeof(params); + if (copy_from_user(¶ms, optval, len)) { + retval = -EFAULT; + goto out; + } + + policy = params.sprstat_policy; + if (policy & ~SCTP_PR_SCTP_MASK) + goto out; + + asoc = sctp_id2assoc(sk, params.sprstat_assoc_id); + if (!asoc || params.sprstat_sid >= asoc->stream->outcnt) + goto out; + + streamout = &asoc->stream->out[params.sprstat_sid]; + if (policy == SCTP_PR_SCTP_NONE) { + params.sprstat_abandoned_unsent = 0; + params.sprstat_abandoned_sent = 0; + for (policy = 0; policy <= SCTP_PR_INDEX(MAX); policy++) { + params.sprstat_abandoned_unsent += + streamout->abandoned_unsent[policy]; + params.sprstat_abandoned_sent += + streamout->abandoned_sent[policy]; + } + } else { + params.sprstat_abandoned_unsent = + streamout->abandoned_unsent[__SCTP_PR_INDEX(policy)]; + params.sprstat_abandoned_sent = + streamout->abandoned_sent[__SCTP_PR_INDEX(policy)]; + } + + if (put_user(len, optlen) || copy_to_user(optval, ¶ms, len)) { + retval = -EFAULT; + goto out; + } + + retval = 0; + +out: + return retval; +} + static int sctp_getsockopt_reconfig_supported(struct sock *sk, int len, char __user *optval, int __user *optlen) @@ -6825,6 +6880,10 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, retval = sctp_getsockopt_pr_assocstatus(sk, len, optval, optlen); break; + case SCTP_PR_STREAM_STATUS: + retval = sctp_getsockopt_pr_streamstatus(sk, len, optval, + optlen); + break; case SCTP_RECONFIG_SUPPORTED: retval = sctp_getsockopt_reconfig_supported(sk, len, optval, optlen); -- cgit From f1fb08f6337ca9e3af371a7994b91a5786ba93f9 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Sun, 2 Apr 2017 11:00:06 +0200 Subject: vxlan: fix ND proxy when skb doesn't have transport header offset When an incoming frame is tagged or when GRO is disabled, the skb handled to vxlan_xmit() doesn't contain a valid transport header offset. This makes ND proxying fail. We combine two changes: replace use of skb_transport_offset() and ensure the necessary amount of skb is linear just before using it: - In vxlan_xmit(), when determining if we have an ICMPv6 neighbor discovery packet, just check if it is an ICMPv6 packet and rely on neigh_reduce() to do more checks if this is the case. The use of pskb_may_pull() is replaced by skb_header_pointer() for just the IPv6 header. - In neigh_reduce(), add pskb_may_pull() for IPv6 header and neighbor discovery message since this was removed from vxlan_xmit(). Replace skb_transport_header() with ipv6_hdr() + 1. - In vxlan_na_create(), replace first skb_transport_offset() with ipv6_hdr() + 1 and second with skb_network_offset() + sizeof(struct ipv6hdr). Additionally, ensure we pskb_may_pull() the whole skb as we need it to iterate over the options. Signed-off-by: Vincent Bernat Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 714f74fb823a..ebc98bb17a51 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1515,7 +1515,7 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request, int ns_olen; int i, len; - if (dev == NULL) + if (dev == NULL || !pskb_may_pull(request, request->len)) return NULL; len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) + @@ -1530,10 +1530,11 @@ static struct sk_buff *vxlan_na_create(struct sk_buff *request, skb_push(reply, sizeof(struct ethhdr)); skb_reset_mac_header(reply); - ns = (struct nd_msg *)skb_transport_header(request); + ns = (struct nd_msg *)(ipv6_hdr(request) + 1); daddr = eth_hdr(request)->h_source; - ns_olen = request->len - skb_transport_offset(request) - sizeof(*ns); + ns_olen = request->len - skb_network_offset(request) - + sizeof(struct ipv6hdr) - sizeof(*ns); for (i = 0; i < ns_olen-1; i += (ns->opt[i+1]<<3)) { if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) { daddr = ns->opt + i + sizeof(struct nd_opt_hdr); @@ -1604,10 +1605,13 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni) if (!in6_dev) goto out; + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + sizeof(struct nd_msg))) + goto out; + iphdr = ipv6_hdr(skb); daddr = &iphdr->daddr; - msg = (struct nd_msg *)skb_transport_header(skb); + msg = (struct nd_msg *)(iphdr + 1); if (msg->icmph.icmp6_code != 0 || msg->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION) goto out; @@ -2242,16 +2246,13 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) if (ntohs(eth->h_proto) == ETH_P_ARP) return arp_reduce(dev, skb, vni); #if IS_ENABLED(CONFIG_IPV6) - else if (ntohs(eth->h_proto) == ETH_P_IPV6 && - pskb_may_pull(skb, sizeof(struct ipv6hdr) - + sizeof(struct nd_msg)) && - ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) { - struct nd_msg *msg; - - msg = (struct nd_msg *)skb_transport_header(skb); - if (msg->icmph.icmp6_code == 0 && - msg->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) - return neigh_reduce(dev, skb, vni); + else if (ntohs(eth->h_proto) == ETH_P_IPV6) { + struct ipv6hdr *hdr, _hdr; + if ((hdr = skb_header_pointer(skb, + skb_network_offset(skb), + sizeof(_hdr), &_hdr)) && + hdr->nexthdr == IPPROTO_ICMPV6) + return neigh_reduce(dev, skb, vni); } #endif } -- cgit From d39004ab136ebb6949a7dda9d24376f3d6209295 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sun, 2 Apr 2017 20:20:47 +0200 Subject: net/faraday: Add missing include of of.h Breaking the include loop netdevice.h, dsa.h, devlink.h broke this driver, it depends on includes brought in by these headers. Adding linux/of.h fixes it. Fixes: ed0e39e97d34 ("net: break include loop netdevice.h, dsa.h, devlink.h") Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 928b0df2b8e0..333265060de1 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include -- cgit From 5a17d9ed9ad702af800f184b13817e056618ba03 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Mon, 3 Apr 2017 00:51:50 +0300 Subject: flowcache: make flow_key_size() return "unsigned int" Flow keys aren't 4GB+ numbers so 64-bit arithmetic is excessive. Space savings (I'm not sure what CSWTCH is): add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-48 (-48) function old new delta flow_cache_lookup 1163 1159 -4 CSWTCH 75997 75953 -44 Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- include/net/flow.h | 2 +- net/core/flow.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/net/flow.h b/include/net/flow.h index 6984f1913dc1..bae198b3039e 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -202,7 +202,7 @@ static inline struct flowi *flowidn_to_flowi(struct flowidn *fldn) typedef unsigned long flow_compare_t; -static inline size_t flow_key_size(u16 family) +static inline unsigned int flow_key_size(u16 family) { switch (family) { case AF_INET: diff --git a/net/core/flow.c b/net/core/flow.c index f765c11d8df5..98f977ec21b1 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -161,7 +161,7 @@ static void flow_new_hash_rnd(struct flow_cache *fc, static u32 flow_hash_code(struct flow_cache *fc, struct flow_cache_percpu *fcp, const struct flowi *key, - size_t keysize) + unsigned int keysize) { const u32 *k = (const u32 *) key; const u32 length = keysize * sizeof(flow_compare_t) / sizeof(u32); @@ -174,7 +174,7 @@ static u32 flow_hash_code(struct flow_cache *fc, * important assumptions that we can here, such as alignment. */ static int flow_key_compare(const struct flowi *key1, const struct flowi *key2, - size_t keysize) + unsigned int keysize) { const flow_compare_t *k1, *k1_lim, *k2; @@ -199,7 +199,7 @@ flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, struct flow_cache_percpu *fcp; struct flow_cache_entry *fle, *tfle; struct flow_cache_object *flo; - size_t keysize; + unsigned int keysize; unsigned int hash; local_bh_disable(); -- cgit From f31cc7e8155f392583a1e3cc2e83ddbc43bacbb3 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Mon, 3 Apr 2017 00:52:29 +0300 Subject: flowcache: make flow_cache_hash_size() return "unsigned int" Hash size can't negative so "unsigned int" is logically correct. Propagate "unsigned int" to loop counters. Space savings: add/remove: 0/0 grow/shrink: 2/2 up/down: 6/-18 (-12) function old new delta flow_cache_flush_tasklet 362 365 +3 __flow_cache_shrink 333 336 +3 flow_cache_cpu_up_prep 178 171 -7 flow_cache_lookup 1159 1148 -11 Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- net/core/flow.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/net/core/flow.c b/net/core/flow.c index 98f977ec21b1..923156d2a750 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -47,7 +47,7 @@ struct flow_flush_info { static struct kmem_cache *flow_cachep __read_mostly; -#define flow_cache_hash_size(cache) (1 << (cache)->hash_shift) +#define flow_cache_hash_size(cache) (1U << (cache)->hash_shift) #define FLOW_HASH_RND_PERIOD (10 * 60 * HZ) static void flow_cache_new_hashrnd(unsigned long arg) @@ -119,9 +119,10 @@ static void __flow_cache_shrink(struct flow_cache *fc, struct flow_cache_entry *fle; struct hlist_node *tmp; LIST_HEAD(gc_list); - int i, deleted = 0; + int deleted = 0; struct netns_xfrm *xfrm = container_of(fc, struct netns_xfrm, flow_cache_global); + unsigned int i; for (i = 0; i < flow_cache_hash_size(fc); i++) { int saved = 0; @@ -295,9 +296,10 @@ static void flow_cache_flush_tasklet(unsigned long data) struct flow_cache_entry *fle; struct hlist_node *tmp; LIST_HEAD(gc_list); - int i, deleted = 0; + int deleted = 0; struct netns_xfrm *xfrm = container_of(fc, struct netns_xfrm, flow_cache_global); + unsigned int i; fcp = this_cpu_ptr(fc->percpu); for (i = 0; i < flow_cache_hash_size(fc); i++) { @@ -327,7 +329,7 @@ static void flow_cache_flush_tasklet(unsigned long data) static int flow_cache_percpu_empty(struct flow_cache *fc, int cpu) { struct flow_cache_percpu *fcp; - int i; + unsigned int i; fcp = per_cpu_ptr(fc->percpu, cpu); for (i = 0; i < flow_cache_hash_size(fc); i++) @@ -402,12 +404,12 @@ void flow_cache_flush_deferred(struct net *net) static int flow_cache_cpu_prepare(struct flow_cache *fc, int cpu) { struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, cpu); - size_t sz = sizeof(struct hlist_head) * flow_cache_hash_size(fc); + unsigned int sz = sizeof(struct hlist_head) * flow_cache_hash_size(fc); if (!fcp->hash_table) { fcp->hash_table = kzalloc_node(sz, GFP_KERNEL, cpu_to_node(cpu)); if (!fcp->hash_table) { - pr_err("NET: failed to allocate flow cache sz %zu\n", sz); + pr_err("NET: failed to allocate flow cache sz %u\n", sz); return -ENOMEM; } fcp->hash_rnd_recalc = 1; -- cgit From ec2e45a978b05cd9711e804a41b8a5bc829a8650 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Mon, 3 Apr 2017 00:53:15 +0300 Subject: flowcache: more "unsigned int" Make ->hash_count, ->low_watermark and ->high_watermark unsigned int and propagate unsignedness to other variables. This change doesn't change code generation because these fields aren't used in 64-bit contexts but make it anyway: these fields can't be negative numbers. Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- include/net/flowcache.h | 6 +++--- net/core/flow.c | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/include/net/flowcache.h b/include/net/flowcache.h index 9caf3bfc8d2d..51eb971e8973 100644 --- a/include/net/flowcache.h +++ b/include/net/flowcache.h @@ -8,7 +8,7 @@ struct flow_cache_percpu { struct hlist_head *hash_table; - int hash_count; + unsigned int hash_count; u32 hash_rnd; int hash_rnd_recalc; struct tasklet_struct flush_tasklet; @@ -18,8 +18,8 @@ struct flow_cache { u32 hash_shift; struct flow_cache_percpu __percpu *percpu; struct hlist_node node; - int low_watermark; - int high_watermark; + unsigned int low_watermark; + unsigned int high_watermark; struct timer_list rnd_timer; }; #endif /* _NET_FLOWCACHE_H */ diff --git a/net/core/flow.c b/net/core/flow.c index 923156d2a750..f7f5d1932a27 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -99,7 +99,8 @@ static void flow_cache_gc_task(struct work_struct *work) } static void flow_cache_queue_garbage(struct flow_cache_percpu *fcp, - int deleted, struct list_head *gc_list, + unsigned int deleted, + struct list_head *gc_list, struct netns_xfrm *xfrm) { if (deleted) { @@ -114,18 +115,18 @@ static void flow_cache_queue_garbage(struct flow_cache_percpu *fcp, static void __flow_cache_shrink(struct flow_cache *fc, struct flow_cache_percpu *fcp, - int shrink_to) + unsigned int shrink_to) { struct flow_cache_entry *fle; struct hlist_node *tmp; LIST_HEAD(gc_list); - int deleted = 0; + unsigned int deleted = 0; struct netns_xfrm *xfrm = container_of(fc, struct netns_xfrm, flow_cache_global); unsigned int i; for (i = 0; i < flow_cache_hash_size(fc); i++) { - int saved = 0; + unsigned int saved = 0; hlist_for_each_entry_safe(fle, tmp, &fcp->hash_table[i], u.hlist) { @@ -146,7 +147,7 @@ static void __flow_cache_shrink(struct flow_cache *fc, static void flow_cache_shrink(struct flow_cache *fc, struct flow_cache_percpu *fcp) { - int shrink_to = fc->low_watermark / flow_cache_hash_size(fc); + unsigned int shrink_to = fc->low_watermark / flow_cache_hash_size(fc); __flow_cache_shrink(fc, fcp, shrink_to); } @@ -296,7 +297,7 @@ static void flow_cache_flush_tasklet(unsigned long data) struct flow_cache_entry *fle; struct hlist_node *tmp; LIST_HEAD(gc_list); - int deleted = 0; + unsigned int deleted = 0; struct netns_xfrm *xfrm = container_of(fc, struct netns_xfrm, flow_cache_global); unsigned int i; -- cgit From 822f9bb104c9d1d2dea3669f1941558c6304cf92 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Mon, 3 Apr 2017 01:18:23 +0300 Subject: soreuseport: use "unsigned int" in __reuseport_alloc() Number of sockets is limited by 16-bit, so 64-bit allocation will never happen. 16-bit ops are the worst code density-wise on x86_64 because of additional prefix (66). Space savings: add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-3 (-3) function old new delta reuseport_add_sock 539 536 -3 Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- net/core/sock_reuseport.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c index 9a1a352fd1eb..eed1ebf7f29d 100644 --- a/net/core/sock_reuseport.c +++ b/net/core/sock_reuseport.c @@ -13,9 +13,9 @@ static DEFINE_SPINLOCK(reuseport_lock); -static struct sock_reuseport *__reuseport_alloc(u16 max_socks) +static struct sock_reuseport *__reuseport_alloc(unsigned int max_socks) { - size_t size = sizeof(struct sock_reuseport) + + unsigned int size = sizeof(struct sock_reuseport) + sizeof(struct sock *) * max_socks; struct sock_reuseport *reuse = kzalloc(size, GFP_ATOMIC); -- cgit From c8b5d129ee293bcf972e7279ac996bb8a138505c Mon Sep 17 00:00:00 2001 From: Greg Ungerer Date: Mon, 3 Apr 2017 15:50:03 +1000 Subject: net: usbnet: support 64bit stats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the net stats64 counters to the usbnet core. With that in place put the hooks into every usbnet driver to use it. This is a strait forward addition of 64bit counters for RX and TX packet and byte counts. It is done in the same style as for the other net drivers that support stats64. Note that the other stats fields remain as 32bit sized values (error counts, etc). The motivation to add this is that it is not particularly difficult to get the RX and TX byte counts to wrap on 32bit platforms. Signed-off-by: Greg Ungerer Acked-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/asix_devices.c | 3 +++ drivers/net/usb/ax88172a.c | 1 + drivers/net/usb/ax88179_178a.c | 1 + drivers/net/usb/cdc_mbim.c | 1 + drivers/net/usb/cdc_ncm.c | 1 + drivers/net/usb/dm9601.c | 1 + drivers/net/usb/int51x1.c | 1 + drivers/net/usb/mcs7830.c | 1 + drivers/net/usb/qmi_wwan.c | 1 + drivers/net/usb/rndis_host.c | 1 + drivers/net/usb/sierra_net.c | 1 + drivers/net/usb/smsc75xx.c | 1 + drivers/net/usb/smsc95xx.c | 1 + drivers/net/usb/sr9700.c | 1 + drivers/net/usb/sr9800.c | 1 + drivers/net/usb/usbnet.c | 55 ++++++++++++++++++++++++++++++++++++--- drivers/net/wireless/rndis_wlan.c | 1 + include/linux/usb/usbnet.h | 4 +++ 18 files changed, 73 insertions(+), 4 deletions(-) diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index 38456d0bcfd2..a3aa0a27dfe5 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -206,6 +206,7 @@ static const struct net_device_ops ax88172_netdev_ops = { .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = asix_ioctl, @@ -591,6 +592,7 @@ static const struct net_device_ops ax88772_netdev_ops = { .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_set_mac_address = asix_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = asix_ioctl, @@ -1044,6 +1046,7 @@ static const struct net_device_ops ax88178_netdev_ops = { .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_set_mac_address = asix_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = asix_set_multicast, diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c index 6308386b09df..501576f53854 100644 --- a/drivers/net/usb/ax88172a.c +++ b/drivers/net/usb/ax88172a.c @@ -143,6 +143,7 @@ static const struct net_device_ops ax88172a_netdev_ops = { .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_set_mac_address = asix_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = ax88172a_ioctl, diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 4a0ae7ce83f6..51cf60092a18 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -959,6 +959,7 @@ static const struct net_device_ops ax88179_netdev_ops = { .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_change_mtu = ax88179_change_mtu, .ndo_set_mac_address = ax88179_set_mac_addr, .ndo_validate_addr = eth_validate_addr, diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 3a98f3762a4c..a6b997cffd3b 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -100,6 +100,7 @@ static const struct net_device_ops cdc_mbim_netdev_ops = { .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_change_mtu = cdc_ncm_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index b6c1d3abad96..bb3f71f9fbde 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -753,6 +753,7 @@ static const struct net_device_ops cdc_ncm_netdev_ops = { .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_change_mtu = cdc_ncm_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index fea1b64ca26a..b91f92e4e5f2 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c @@ -343,6 +343,7 @@ static const struct net_device_ops dm9601_netdev_ops = { .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = dm9601_ioctl, .ndo_set_rx_mode = dm9601_set_multicast, diff --git a/drivers/net/usb/int51x1.c b/drivers/net/usb/int51x1.c index 4ff70b22c6ee..5a43b77a6b9c 100644 --- a/drivers/net/usb/int51x1.c +++ b/drivers/net/usb/int51x1.c @@ -144,6 +144,7 @@ static const struct net_device_ops int51x1_netdev_ops = { .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = int51x1_set_multicast, diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c index 5771ff261fa8..5a47e5510ca8 100644 --- a/drivers/net/usb/mcs7830.c +++ b/drivers/net/usb/mcs7830.c @@ -475,6 +475,7 @@ static const struct net_device_ops mcs7830_netdev_ops = { .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = mcs7830_ioctl, .ndo_set_rx_mode = mcs7830_set_multicast, diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 629fe64cd74a..adbed261cc8a 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -542,6 +542,7 @@ static const struct net_device_ops qmi_wwan_netdev_ops = { .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_set_mac_address = qmi_wwan_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index c5b21138b7eb..e96e2e5673d7 100644 --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -291,6 +291,7 @@ static const struct net_device_ops rndis_netdev_ops = { .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c index c8f60b887c22..2110ab3513f0 100644 --- a/drivers/net/usb/sierra_net.c +++ b/drivers/net/usb/sierra_net.c @@ -199,6 +199,7 @@ static const struct net_device_ops sierra_net_device_ops = { .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 1ab0ff43c6a2..1ce01dbd494f 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -1381,6 +1381,7 @@ static const struct net_device_ops smsc75xx_netdev_ops = { .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_change_mtu = smsc75xx_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 4a8bf960cbb9..c2f67cecdf5b 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -1248,6 +1248,7 @@ static const struct net_device_ops smsc95xx_netdev_ops = { .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = smsc95xx_ioctl, diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c index 950a3a9466bd..317287f4409c 100644 --- a/drivers/net/usb/sr9700.c +++ b/drivers/net/usb/sr9700.c @@ -308,6 +308,7 @@ static const struct net_device_ops sr9700_netdev_ops = { .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = sr9700_ioctl, .ndo_set_rx_mode = sr9700_set_multicast, diff --git a/drivers/net/usb/sr9800.c b/drivers/net/usb/sr9800.c index a696b628782c..9277a0f228df 100644 --- a/drivers/net/usb/sr9800.c +++ b/drivers/net/usb/sr9800.c @@ -679,6 +679,7 @@ static const struct net_device_ops sr9800_netdev_ops = { .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_set_mac_address = sr_set_mac_address, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = sr_ioctl, diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 13d4ec5f6f34..9890656af735 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -316,6 +316,7 @@ static void __usbnet_status_stop_force(struct usbnet *dev) */ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) { + struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64); int status; if (test_bit(EVENT_RX_PAUSED, &dev->flags)) { @@ -327,8 +328,10 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) if (skb->protocol == 0) skb->protocol = eth_type_trans (skb, dev->net); - dev->net->stats.rx_packets++; - dev->net->stats.rx_bytes += skb->len; + u64_stats_update_begin(&stats64->syncp); + stats64->rx_packets++; + stats64->rx_bytes += skb->len; + u64_stats_update_end(&stats64->syncp); netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n", skb->len + sizeof (struct ethhdr), skb->protocol); @@ -981,6 +984,37 @@ int usbnet_set_link_ksettings(struct net_device *net, } EXPORT_SYMBOL_GPL(usbnet_set_link_ksettings); +void usbnet_get_stats64(struct net_device *net, struct rtnl_link_stats64 *stats) +{ + struct usbnet *dev = netdev_priv(net); + unsigned int start; + int cpu; + + netdev_stats_to_stats64(stats, &net->stats); + + for_each_possible_cpu(cpu) { + struct pcpu_sw_netstats *stats64; + u64 rx_packets, rx_bytes; + u64 tx_packets, tx_bytes; + + stats64 = per_cpu_ptr(dev->stats64, cpu); + + do { + start = u64_stats_fetch_begin_irq(&stats64->syncp); + rx_packets = stats64->rx_packets; + rx_bytes = stats64->rx_bytes; + tx_packets = stats64->tx_packets; + tx_bytes = stats64->tx_bytes; + } while (u64_stats_fetch_retry_irq(&stats64->syncp, start)); + + stats->rx_packets += rx_packets; + stats->rx_bytes += rx_bytes; + stats->tx_packets += tx_packets; + stats->tx_bytes += tx_bytes; + } +} +EXPORT_SYMBOL_GPL(usbnet_get_stats64); + u32 usbnet_get_link (struct net_device *net) { struct usbnet *dev = netdev_priv(net); @@ -1212,8 +1246,12 @@ static void tx_complete (struct urb *urb) struct usbnet *dev = entry->dev; if (urb->status == 0) { - dev->net->stats.tx_packets += entry->packets; - dev->net->stats.tx_bytes += entry->length; + struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->stats64); + + u64_stats_update_begin(&stats64->syncp); + stats64->tx_packets += entry->packets; + stats64->tx_bytes += entry->length; + u64_stats_update_end(&stats64->syncp); } else { dev->net->stats.tx_errors++; @@ -1570,6 +1608,7 @@ void usbnet_disconnect (struct usb_interface *intf) usb_free_urb(dev->interrupt); kfree(dev->padding_pkt); + free_percpu(dev->stats64); free_netdev(net); } EXPORT_SYMBOL_GPL(usbnet_disconnect); @@ -1581,6 +1620,7 @@ static const struct net_device_ops usbnet_netdev_ops = { .ndo_tx_timeout = usbnet_tx_timeout, .ndo_set_rx_mode = usbnet_set_rx_mode, .ndo_change_mtu = usbnet_change_mtu, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; @@ -1642,6 +1682,11 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) dev->intf = udev; dev->driver_info = info; dev->driver_name = name; + + dev->stats64 = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); + if (!dev->stats64) + goto out0; + dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK); init_waitqueue_head(&dev->wait); @@ -1781,6 +1826,8 @@ out1: */ cancel_work_sync(&dev->kevent); del_timer_sync(&dev->delay); + free_percpu(dev->stats64); +out0: free_netdev(net); out: return status; diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 785334f7a538..3b68eaffb48c 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -3392,6 +3392,7 @@ static const struct net_device_ops rndis_wlan_netdev_ops = { .ndo_stop = usbnet_stop, .ndo_start_xmit = usbnet_start_xmit, .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_get_stats64 = usbnet_get_stats64, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = rndis_wlan_set_multicast_list, diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index e2b56917450f..7dffa5624ea6 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -64,6 +64,8 @@ struct usbnet { struct usb_anchor deferred; struct tasklet_struct bh; + struct pcpu_sw_netstats __percpu *stats64; + struct work_struct kevent; unsigned long flags; # define EVENT_TX_HALT 0 @@ -278,5 +280,7 @@ extern int usbnet_status_start(struct usbnet *dev, gfp_t mem_flags); extern void usbnet_status_stop(struct usbnet *dev); extern void usbnet_update_max_qlen(struct usbnet *dev); +extern void usbnet_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats); #endif /* __LINUX_USB_USBNET_H */ -- cgit From b5a9ee7cf3be118ad9064583c2a0f10195ca422a Mon Sep 17 00:00:00 2001 From: Ariel Elior Date: Mon, 3 Apr 2017 12:21:09 +0300 Subject: qed: Revise QM cofiguration Refactor and clean up the queue manager initialization logic. Also, this adds support for RoC low latency queues, which later would be used for improving RoCE latency in high throughput scenarios. Signed-off-by: Ariel Elior Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 43 +- drivers/net/ethernet/qlogic/qed/qed_cxt.c | 13 +- drivers/net/ethernet/qlogic/qed/qed_dcbx.c | 14 +- drivers/net/ethernet/qlogic/qed/qed_dev.c | 749 +++++++++++++++++++++------- drivers/net/ethernet/qlogic/qed/qed_fcoe.c | 8 +- drivers/net/ethernet/qlogic/qed/qed_hsi.h | 11 +- drivers/net/ethernet/qlogic/qed/qed_hw.c | 52 -- drivers/net/ethernet/qlogic/qed/qed_hw.h | 3 - drivers/net/ethernet/qlogic/qed/qed_int.c | 3 +- drivers/net/ethernet/qlogic/qed/qed_iscsi.c | 17 +- drivers/net/ethernet/qlogic/qed/qed_l2.c | 5 +- drivers/net/ethernet/qlogic/qed/qed_ll2.c | 15 +- drivers/net/ethernet/qlogic/qed/qed_roce.c | 12 +- drivers/net/ethernet/qlogic/qed/qed_spq.c | 15 +- drivers/net/ethernet/qlogic/qed/qed_sriov.c | 8 +- 15 files changed, 665 insertions(+), 303 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 8c85fd2f3949..d8bcc21a4f69 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -271,9 +271,14 @@ struct qed_hw_info { RESC_NUM(_p_hwfn, resc)) #define FEAT_NUM(_p_hwfn, resc) ((_p_hwfn)->hw_info.feat_num[resc]) - u8 num_tc; + /* Amount of traffic classes HW supports */ + u8 num_hw_tc; + + /* Amount of TCs which should be active according to DCBx or upper + * layer driver configuration. + */ + u8 num_active_tc; u8 offload_tc; - u8 non_offload_tc; u32 concrete_fid; u16 opaque_fid; @@ -336,15 +341,19 @@ struct qed_qm_info { struct init_qm_port_params *qm_port_params; u16 start_pq; u8 start_vport; - u8 pure_lb_pq; - u8 offload_pq; - u8 pure_ack_pq; - u8 ooo_pq; - u8 vf_queues_offset; + u16 pure_lb_pq; + u16 offload_pq; + u16 low_latency_pq; + u16 pure_ack_pq; + u16 ooo_pq; + u16 first_vf_pq; + u16 first_mcos_pq; + u16 first_rl_pq; u16 num_pqs; u16 num_vf_pqs; u8 num_vports; u8 max_phys_tcs_per_port; + u8 ooo_tc; bool pf_rl_en; bool pf_wfq_en; bool vport_rl_en; @@ -729,9 +738,27 @@ void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, u32 min_pf_rate); void qed_clean_wfq_db(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); -#define QED_LEADING_HWFN(dev) (&dev->hwfns[0]) int qed_device_num_engines(struct qed_dev *cdev); +#define QED_LEADING_HWFN(dev) (&dev->hwfns[0]) + +/* Flags for indication of required queues */ +#define PQ_FLAGS_RLS (BIT(0)) +#define PQ_FLAGS_MCOS (BIT(1)) +#define PQ_FLAGS_LB (BIT(2)) +#define PQ_FLAGS_OOO (BIT(3)) +#define PQ_FLAGS_ACK (BIT(4)) +#define PQ_FLAGS_OFLD (BIT(5)) +#define PQ_FLAGS_VFS (BIT(6)) +#define PQ_FLAGS_LLT (BIT(7)) + +/* physical queue index for cm context intialization */ +u16 qed_get_cm_pq_idx(struct qed_hwfn *p_hwfn, u32 pq_flags); +u16 qed_get_cm_pq_idx_mcos(struct qed_hwfn *p_hwfn, u8 tc); +u16 qed_get_cm_pq_idx_vf(struct qed_hwfn *p_hwfn, u16 vf); + +#define QED_LEADING_HWFN(dev) (&dev->hwfns[0]) + /* Other Linux specific common definitions */ #define DP_NAME(cdev) ((cdev)->name) diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c index 9ff62cc5723d..1012b3cc358f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c @@ -1396,18 +1396,11 @@ void qed_qm_init_pf(struct qed_hwfn *p_hwfn) } /* CM PF */ -static int qed_cm_init_pf(struct qed_hwfn *p_hwfn) +void qed_cm_init_pf(struct qed_hwfn *p_hwfn) { - union qed_qm_pq_params pq_params; - u16 pq; - /* XCM pure-LB queue */ - memset(&pq_params, 0, sizeof(pq_params)); - pq_params.core.tc = LB_TC; - pq = qed_get_qm_pq(p_hwfn, PROTOCOLID_CORE, &pq_params); - STORE_RT_REG(p_hwfn, XCM_REG_CON_PHY_Q3_RT_OFFSET, pq); - - return 0; + STORE_RT_REG(p_hwfn, XCM_REG_CON_PHY_Q3_RT_OFFSET, + qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_LB)); } /* DQ PF */ diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c index 5bd36a4a8fcd..2fc1fde824bd 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c @@ -183,7 +183,7 @@ qed_dcbx_dp_protocol(struct qed_hwfn *p_hwfn, struct qed_dcbx_results *p_data) "%s info: update %d, enable %d, prio %d, tc %d, num_tc %d\n", qed_dcbx_app_update[i].name, p_data->arr[id].update, p_data->arr[id].enable, p_data->arr[id].priority, - p_data->arr[id].tc, p_hwfn->hw_info.num_tc); + p_data->arr[id].tc, p_hwfn->hw_info.num_active_tc); } } @@ -204,12 +204,8 @@ qed_dcbx_set_params(struct qed_dcbx_results *p_data, p_data->arr[type].tc = tc; /* QM reconf data */ - if (p_info->personality == personality) { - if (personality == QED_PCI_ETH) - p_info->non_offload_tc = tc; - else - p_info->offload_tc = tc; - } + if (p_info->personality == personality) + p_info->offload_tc = tc; } /* Update app protocol data and hw_info fields with the TLV info */ @@ -376,7 +372,9 @@ static int qed_dcbx_process_mib_info(struct qed_hwfn *p_hwfn) if (rc) return rc; - p_info->num_tc = QED_MFW_GET_FIELD(p_ets->flags, DCBX_ETS_MAX_TCS); + p_info->num_active_tc = QED_MFW_GET_FIELD(p_ets->flags, + DCBX_ETS_MAX_TCS); + p_hwfn->qm_info.ooo_tc = QED_MFW_GET_FIELD(p_ets->flags, DCBX_OOO_TC); data.pf_id = p_hwfn->rel_pf_id; data.dcbx_enabled = !!dcbx_version; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index e75c83351d34..b48c80ec4e5b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -186,195 +186,569 @@ void qed_resc_free(struct qed_dev *cdev) } } -static int qed_init_qm_info(struct qed_hwfn *p_hwfn, bool b_sleepable) +/******************** QM initialization *******************/ +#define ACTIVE_TCS_BMAP 0x9f +#define ACTIVE_TCS_BMAP_4PORT_K2 0xf + +/* determines the physical queue flags for a given PF. */ +static u32 qed_get_pq_flags(struct qed_hwfn *p_hwfn) { - u8 num_vports, vf_offset = 0, i, vport_id, num_ports, curr_queue = 0; - struct qed_qm_info *qm_info = &p_hwfn->qm_info; - struct init_qm_port_params *p_qm_port; - bool init_rdma_offload_pq = false; - bool init_pure_ack_pq = false; - bool init_ooo_pq = false; - u16 num_pqs, multi_cos_tcs = 1; - u8 pf_wfq = qm_info->pf_wfq; - u32 pf_rl = qm_info->pf_rl; - u16 num_pf_rls = 0; - u16 num_vfs = 0; - -#ifdef CONFIG_QED_SRIOV - if (p_hwfn->cdev->p_iov_info) - num_vfs = p_hwfn->cdev->p_iov_info->total_vfs; -#endif - memset(qm_info, 0, sizeof(*qm_info)); + u32 flags; - num_pqs = multi_cos_tcs + num_vfs + 1; /* The '1' is for pure-LB */ - num_vports = (u8)RESC_NUM(p_hwfn, QED_VPORT); + /* common flags */ + flags = PQ_FLAGS_LB; - if (p_hwfn->hw_info.personality == QED_PCI_ETH_ROCE) { - num_pqs++; /* for RoCE queue */ - init_rdma_offload_pq = true; - /* we subtract num_vfs because each require a rate limiter, - * and one default rate limiter - */ - if (p_hwfn->pf_params.rdma_pf_params.enable_dcqcn) - num_pf_rls = RESC_NUM(p_hwfn, QED_RL) - num_vfs - 1; + /* feature flags */ + if (IS_QED_SRIOV(p_hwfn->cdev)) + flags |= PQ_FLAGS_VFS; - num_pqs += num_pf_rls; - qm_info->num_pf_rls = (u8) num_pf_rls; + /* protocol flags */ + switch (p_hwfn->hw_info.personality) { + case QED_PCI_ETH: + flags |= PQ_FLAGS_MCOS; + break; + case QED_PCI_FCOE: + flags |= PQ_FLAGS_OFLD; + break; + case QED_PCI_ISCSI: + flags |= PQ_FLAGS_ACK | PQ_FLAGS_OOO | PQ_FLAGS_OFLD; + break; + case QED_PCI_ETH_ROCE: + flags |= PQ_FLAGS_MCOS | PQ_FLAGS_OFLD | PQ_FLAGS_LLT; + break; + default: + DP_ERR(p_hwfn, + "unknown personality %d\n", p_hwfn->hw_info.personality); + return 0; } - if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) { - num_pqs += 2; /* for iSCSI pure-ACK / OOO queue */ - init_pure_ack_pq = true; - init_ooo_pq = true; - } + return flags; +} - /* Sanity checking that setup requires legal number of resources */ - if (num_pqs > RESC_NUM(p_hwfn, QED_PQ)) { - DP_ERR(p_hwfn, - "Need too many Physical queues - 0x%04x when only %04x are available\n", - num_pqs, RESC_NUM(p_hwfn, QED_PQ)); - return -EINVAL; - } +/* Getters for resource amounts necessary for qm initialization */ +u8 qed_init_qm_get_num_tcs(struct qed_hwfn *p_hwfn) +{ + return p_hwfn->hw_info.num_hw_tc; +} - /* PQs will be arranged as follows: First per-TC PQ then pure-LB quete. - */ - qm_info->qm_pq_params = kcalloc(num_pqs, - sizeof(struct init_qm_pq_params), - b_sleepable ? GFP_KERNEL : GFP_ATOMIC); - if (!qm_info->qm_pq_params) - goto alloc_err; +u16 qed_init_qm_get_num_vfs(struct qed_hwfn *p_hwfn) +{ + return IS_QED_SRIOV(p_hwfn->cdev) ? + p_hwfn->cdev->p_iov_info->total_vfs : 0; +} - qm_info->qm_vport_params = kcalloc(num_vports, - sizeof(struct init_qm_vport_params), - b_sleepable ? GFP_KERNEL - : GFP_ATOMIC); - if (!qm_info->qm_vport_params) - goto alloc_err; +#define NUM_DEFAULT_RLS 1 - qm_info->qm_port_params = kcalloc(MAX_NUM_PORTS, - sizeof(struct init_qm_port_params), - b_sleepable ? GFP_KERNEL - : GFP_ATOMIC); - if (!qm_info->qm_port_params) - goto alloc_err; +u16 qed_init_qm_get_num_pf_rls(struct qed_hwfn *p_hwfn) +{ + u16 num_pf_rls, num_vfs = qed_init_qm_get_num_vfs(p_hwfn); - qm_info->wfq_data = kcalloc(num_vports, sizeof(struct qed_wfq_data), - b_sleepable ? GFP_KERNEL : GFP_ATOMIC); - if (!qm_info->wfq_data) - goto alloc_err; + /* num RLs can't exceed resource amount of rls or vports */ + num_pf_rls = (u16) min_t(u32, RESC_NUM(p_hwfn, QED_RL), + RESC_NUM(p_hwfn, QED_VPORT)); - vport_id = (u8)RESC_START(p_hwfn, QED_VPORT); + /* Make sure after we reserve there's something left */ + if (num_pf_rls < num_vfs + NUM_DEFAULT_RLS) + return 0; - /* First init rate limited queues */ - for (curr_queue = 0; curr_queue < num_pf_rls; curr_queue++) { - qm_info->qm_pq_params[curr_queue].vport_id = vport_id++; - qm_info->qm_pq_params[curr_queue].tc_id = - p_hwfn->hw_info.non_offload_tc; - qm_info->qm_pq_params[curr_queue].wrr_group = 1; - qm_info->qm_pq_params[curr_queue].rl_valid = 1; - } + /* subtract rls necessary for VFs and one default one for the PF */ + num_pf_rls -= num_vfs + NUM_DEFAULT_RLS; - /* First init per-TC PQs */ - for (i = 0; i < multi_cos_tcs; i++) { - struct init_qm_pq_params *params = - &qm_info->qm_pq_params[curr_queue++]; + return num_pf_rls; +} - if (p_hwfn->hw_info.personality == QED_PCI_ETH_ROCE || - p_hwfn->hw_info.personality == QED_PCI_ETH) { - params->vport_id = vport_id; - params->tc_id = p_hwfn->hw_info.non_offload_tc; - params->wrr_group = 1; - } else { - params->vport_id = vport_id; - params->tc_id = p_hwfn->hw_info.offload_tc; - params->wrr_group = 1; - } - } +u16 qed_init_qm_get_num_vports(struct qed_hwfn *p_hwfn) +{ + u32 pq_flags = qed_get_pq_flags(p_hwfn); + + /* all pqs share the same vport, except for vfs and pf_rl pqs */ + return (!!(PQ_FLAGS_RLS & pq_flags)) * + qed_init_qm_get_num_pf_rls(p_hwfn) + + (!!(PQ_FLAGS_VFS & pq_flags)) * + qed_init_qm_get_num_vfs(p_hwfn) + 1; +} + +/* calc amount of PQs according to the requested flags */ +u16 qed_init_qm_get_num_pqs(struct qed_hwfn *p_hwfn) +{ + u32 pq_flags = qed_get_pq_flags(p_hwfn); + + return (!!(PQ_FLAGS_RLS & pq_flags)) * + qed_init_qm_get_num_pf_rls(p_hwfn) + + (!!(PQ_FLAGS_MCOS & pq_flags)) * + qed_init_qm_get_num_tcs(p_hwfn) + + (!!(PQ_FLAGS_LB & pq_flags)) + (!!(PQ_FLAGS_OOO & pq_flags)) + + (!!(PQ_FLAGS_ACK & pq_flags)) + (!!(PQ_FLAGS_OFLD & pq_flags)) + + (!!(PQ_FLAGS_LLT & pq_flags)) + + (!!(PQ_FLAGS_VFS & pq_flags)) * qed_init_qm_get_num_vfs(p_hwfn); +} + +/* initialize the top level QM params */ +static void qed_init_qm_params(struct qed_hwfn *p_hwfn) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + bool four_port; + + /* pq and vport bases for this PF */ + qm_info->start_pq = (u16) RESC_START(p_hwfn, QED_PQ); + qm_info->start_vport = (u8) RESC_START(p_hwfn, QED_VPORT); - /* Then init pure-LB PQ */ - qm_info->pure_lb_pq = curr_queue; - qm_info->qm_pq_params[curr_queue].vport_id = - (u8) RESC_START(p_hwfn, QED_VPORT); - qm_info->qm_pq_params[curr_queue].tc_id = PURE_LB_TC; - qm_info->qm_pq_params[curr_queue].wrr_group = 1; - curr_queue++; - - qm_info->offload_pq = 0; - if (init_rdma_offload_pq) { - qm_info->offload_pq = curr_queue; - qm_info->qm_pq_params[curr_queue].vport_id = vport_id; - qm_info->qm_pq_params[curr_queue].tc_id = - p_hwfn->hw_info.offload_tc; - qm_info->qm_pq_params[curr_queue].wrr_group = 1; - curr_queue++; - } - - if (init_pure_ack_pq) { - qm_info->pure_ack_pq = curr_queue; - qm_info->qm_pq_params[curr_queue].vport_id = vport_id; - qm_info->qm_pq_params[curr_queue].tc_id = - p_hwfn->hw_info.offload_tc; - qm_info->qm_pq_params[curr_queue].wrr_group = 1; - curr_queue++; - } - - if (init_ooo_pq) { - qm_info->ooo_pq = curr_queue; - qm_info->qm_pq_params[curr_queue].vport_id = vport_id; - qm_info->qm_pq_params[curr_queue].tc_id = DCBX_ISCSI_OOO_TC; - qm_info->qm_pq_params[curr_queue].wrr_group = 1; - curr_queue++; - } - - /* Then init per-VF PQs */ - vf_offset = curr_queue; - for (i = 0; i < num_vfs; i++) { - /* First vport is used by the PF */ - qm_info->qm_pq_params[curr_queue].vport_id = vport_id + i + 1; - qm_info->qm_pq_params[curr_queue].tc_id = - p_hwfn->hw_info.non_offload_tc; - qm_info->qm_pq_params[curr_queue].wrr_group = 1; - qm_info->qm_pq_params[curr_queue].rl_valid = 1; - curr_queue++; - } - - qm_info->vf_queues_offset = vf_offset; - qm_info->num_pqs = num_pqs; - qm_info->num_vports = num_vports; + /* rate limiting and weighted fair queueing are always enabled */ + qm_info->vport_rl_en = 1; + qm_info->vport_wfq_en = 1; + + /* TC config is different for AH 4 port */ + four_port = p_hwfn->cdev->num_ports_in_engines == MAX_NUM_PORTS_K2; + + /* in AH 4 port we have fewer TCs per port */ + qm_info->max_phys_tcs_per_port = four_port ? NUM_PHYS_TCS_4PORT_K2 : + NUM_OF_PHYS_TCS; + + /* unless MFW indicated otherwise, ooo_tc == 3 for + * AH 4-port and 4 otherwise. + */ + if (!qm_info->ooo_tc) + qm_info->ooo_tc = four_port ? DCBX_TCP_OOO_K2_4PORT_TC : + DCBX_TCP_OOO_TC; +} + +/* initialize qm vport params */ +static void qed_init_qm_vport_params(struct qed_hwfn *p_hwfn) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + u8 i; + + /* all vports participate in weighted fair queueing */ + for (i = 0; i < qed_init_qm_get_num_vports(p_hwfn); i++) + qm_info->qm_vport_params[i].vport_wfq = 1; +} +/* initialize qm port params */ +static void qed_init_qm_port_params(struct qed_hwfn *p_hwfn) +{ /* Initialize qm port parameters */ - num_ports = p_hwfn->cdev->num_ports_in_engines; + u8 i, active_phys_tcs, num_ports = p_hwfn->cdev->num_ports_in_engines; + + /* indicate how ooo and high pri traffic is dealt with */ + active_phys_tcs = num_ports == MAX_NUM_PORTS_K2 ? + ACTIVE_TCS_BMAP_4PORT_K2 : + ACTIVE_TCS_BMAP; + for (i = 0; i < num_ports; i++) { - p_qm_port = &qm_info->qm_port_params[i]; + struct init_qm_port_params *p_qm_port = + &p_hwfn->qm_info.qm_port_params[i]; + p_qm_port->active = 1; - if (num_ports == 4) - p_qm_port->active_phys_tcs = 0x7; - else - p_qm_port->active_phys_tcs = 0x9f; + p_qm_port->active_phys_tcs = active_phys_tcs; p_qm_port->num_pbf_cmd_lines = PBF_MAX_CMD_LINES / num_ports; p_qm_port->num_btb_blocks = BTB_MAX_BLOCKS / num_ports; } +} + +/* Reset the params which must be reset for qm init. QM init may be called as + * a result of flows other than driver load (e.g. dcbx renegotiation). Other + * params may be affected by the init but would simply recalculate to the same + * values. The allocations made for QM init, ports, vports, pqs and vfqs are not + * affected as these amounts stay the same. + */ +static void qed_init_qm_reset_params(struct qed_hwfn *p_hwfn) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + + qm_info->num_pqs = 0; + qm_info->num_vports = 0; + qm_info->num_pf_rls = 0; + qm_info->num_vf_pqs = 0; + qm_info->first_vf_pq = 0; + qm_info->first_mcos_pq = 0; + qm_info->first_rl_pq = 0; +} + +static void qed_init_qm_advance_vport(struct qed_hwfn *p_hwfn) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + + qm_info->num_vports++; + + if (qm_info->num_vports > qed_init_qm_get_num_vports(p_hwfn)) + DP_ERR(p_hwfn, + "vport overflow! qm_info->num_vports %d, qm_init_get_num_vports() %d\n", + qm_info->num_vports, qed_init_qm_get_num_vports(p_hwfn)); +} + +/* initialize a single pq and manage qm_info resources accounting. + * The pq_init_flags param determines whether the PQ is rate limited + * (for VF or PF) and whether a new vport is allocated to the pq or not + * (i.e. vport will be shared). + */ + +/* flags for pq init */ +#define PQ_INIT_SHARE_VPORT (1 << 0) +#define PQ_INIT_PF_RL (1 << 1) +#define PQ_INIT_VF_RL (1 << 2) + +/* defines for pq init */ +#define PQ_INIT_DEFAULT_WRR_GROUP 1 +#define PQ_INIT_DEFAULT_TC 0 +#define PQ_INIT_OFLD_TC (p_hwfn->hw_info.offload_tc) + +static void qed_init_qm_pq(struct qed_hwfn *p_hwfn, + struct qed_qm_info *qm_info, + u8 tc, u32 pq_init_flags) +{ + u16 pq_idx = qm_info->num_pqs, max_pq = qed_init_qm_get_num_pqs(p_hwfn); + + if (pq_idx > max_pq) + DP_ERR(p_hwfn, + "pq overflow! pq %d, max pq %d\n", pq_idx, max_pq); + + /* init pq params */ + qm_info->qm_pq_params[pq_idx].vport_id = qm_info->start_vport + + qm_info->num_vports; + qm_info->qm_pq_params[pq_idx].tc_id = tc; + qm_info->qm_pq_params[pq_idx].wrr_group = PQ_INIT_DEFAULT_WRR_GROUP; + qm_info->qm_pq_params[pq_idx].rl_valid = + (pq_init_flags & PQ_INIT_PF_RL || pq_init_flags & PQ_INIT_VF_RL); + + /* qm params accounting */ + qm_info->num_pqs++; + if (!(pq_init_flags & PQ_INIT_SHARE_VPORT)) + qm_info->num_vports++; + + if (pq_init_flags & PQ_INIT_PF_RL) + qm_info->num_pf_rls++; + + if (qm_info->num_vports > qed_init_qm_get_num_vports(p_hwfn)) + DP_ERR(p_hwfn, + "vport overflow! qm_info->num_vports %d, qm_init_get_num_vports() %d\n", + qm_info->num_vports, qed_init_qm_get_num_vports(p_hwfn)); + + if (qm_info->num_pf_rls > qed_init_qm_get_num_pf_rls(p_hwfn)) + DP_ERR(p_hwfn, + "rl overflow! qm_info->num_pf_rls %d, qm_init_get_num_pf_rls() %d\n", + qm_info->num_pf_rls, qed_init_qm_get_num_pf_rls(p_hwfn)); +} + +/* get pq index according to PQ_FLAGS */ +static u16 *qed_init_qm_get_idx_from_flags(struct qed_hwfn *p_hwfn, + u32 pq_flags) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + + /* Can't have multiple flags set here */ + if (bitmap_weight((unsigned long *)&pq_flags, sizeof(pq_flags)) > 1) + goto err; + + switch (pq_flags) { + case PQ_FLAGS_RLS: + return &qm_info->first_rl_pq; + case PQ_FLAGS_MCOS: + return &qm_info->first_mcos_pq; + case PQ_FLAGS_LB: + return &qm_info->pure_lb_pq; + case PQ_FLAGS_OOO: + return &qm_info->ooo_pq; + case PQ_FLAGS_ACK: + return &qm_info->pure_ack_pq; + case PQ_FLAGS_OFLD: + return &qm_info->offload_pq; + case PQ_FLAGS_LLT: + return &qm_info->low_latency_pq; + case PQ_FLAGS_VFS: + return &qm_info->first_vf_pq; + default: + goto err; + } + +err: + DP_ERR(p_hwfn, "BAD pq flags %d\n", pq_flags); + return NULL; +} + +/* save pq index in qm info */ +static void qed_init_qm_set_idx(struct qed_hwfn *p_hwfn, + u32 pq_flags, u16 pq_val) +{ + u16 *base_pq_idx = qed_init_qm_get_idx_from_flags(p_hwfn, pq_flags); + + *base_pq_idx = p_hwfn->qm_info.start_pq + pq_val; +} + +/* get tx pq index, with the PQ TX base already set (ready for context init) */ +u16 qed_get_cm_pq_idx(struct qed_hwfn *p_hwfn, u32 pq_flags) +{ + u16 *base_pq_idx = qed_init_qm_get_idx_from_flags(p_hwfn, pq_flags); + + return *base_pq_idx + CM_TX_PQ_BASE; +} + +u16 qed_get_cm_pq_idx_mcos(struct qed_hwfn *p_hwfn, u8 tc) +{ + u8 max_tc = qed_init_qm_get_num_tcs(p_hwfn); + + if (tc > max_tc) + DP_ERR(p_hwfn, "tc %d must be smaller than %d\n", tc, max_tc); + + return qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_MCOS) + tc; +} + +u16 qed_get_cm_pq_idx_vf(struct qed_hwfn *p_hwfn, u16 vf) +{ + u16 max_vf = qed_init_qm_get_num_vfs(p_hwfn); + + if (vf > max_vf) + DP_ERR(p_hwfn, "vf %d must be smaller than %d\n", vf, max_vf); + + return qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_VFS) + vf; +} + +u16 qed_get_cm_pq_idx_rl(struct qed_hwfn *p_hwfn, u8 rl) +{ + u16 max_rl = qed_init_qm_get_num_pf_rls(p_hwfn); + + if (rl > max_rl) + DP_ERR(p_hwfn, "rl %d must be smaller than %d\n", rl, max_rl); + + return qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_RLS) + rl; +} - qm_info->max_phys_tcs_per_port = NUM_OF_PHYS_TCS; +/* Functions for creating specific types of pqs */ +static void qed_init_qm_lb_pq(struct qed_hwfn *p_hwfn) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + + if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_LB)) + return; + + qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_LB, qm_info->num_pqs); + qed_init_qm_pq(p_hwfn, qm_info, PURE_LB_TC, PQ_INIT_SHARE_VPORT); +} + +static void qed_init_qm_ooo_pq(struct qed_hwfn *p_hwfn) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + + if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_OOO)) + return; + + qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_OOO, qm_info->num_pqs); + qed_init_qm_pq(p_hwfn, qm_info, qm_info->ooo_tc, PQ_INIT_SHARE_VPORT); +} + +static void qed_init_qm_pure_ack_pq(struct qed_hwfn *p_hwfn) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + + if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_ACK)) + return; + + qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_ACK, qm_info->num_pqs); + qed_init_qm_pq(p_hwfn, qm_info, PQ_INIT_OFLD_TC, PQ_INIT_SHARE_VPORT); +} + +static void qed_init_qm_offload_pq(struct qed_hwfn *p_hwfn) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + + if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_OFLD)) + return; + + qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_OFLD, qm_info->num_pqs); + qed_init_qm_pq(p_hwfn, qm_info, PQ_INIT_OFLD_TC, PQ_INIT_SHARE_VPORT); +} + +static void qed_init_qm_low_latency_pq(struct qed_hwfn *p_hwfn) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + + if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_LLT)) + return; - qm_info->start_pq = (u16)RESC_START(p_hwfn, QED_PQ); + qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_LLT, qm_info->num_pqs); + qed_init_qm_pq(p_hwfn, qm_info, PQ_INIT_OFLD_TC, PQ_INIT_SHARE_VPORT); +} + +static void qed_init_qm_mcos_pqs(struct qed_hwfn *p_hwfn) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + u8 tc_idx; + + if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_MCOS)) + return; + + qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_MCOS, qm_info->num_pqs); + for (tc_idx = 0; tc_idx < qed_init_qm_get_num_tcs(p_hwfn); tc_idx++) + qed_init_qm_pq(p_hwfn, qm_info, tc_idx, PQ_INIT_SHARE_VPORT); +} +static void qed_init_qm_vf_pqs(struct qed_hwfn *p_hwfn) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + u16 vf_idx, num_vfs = qed_init_qm_get_num_vfs(p_hwfn); + + if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_VFS)) + return; + + qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_VFS, qm_info->num_pqs); qm_info->num_vf_pqs = num_vfs; - qm_info->start_vport = (u8) RESC_START(p_hwfn, QED_VPORT); + for (vf_idx = 0; vf_idx < num_vfs; vf_idx++) + qed_init_qm_pq(p_hwfn, + qm_info, PQ_INIT_DEFAULT_TC, PQ_INIT_VF_RL); +} - for (i = 0; i < qm_info->num_vports; i++) - qm_info->qm_vport_params[i].vport_wfq = 1; +static void qed_init_qm_rl_pqs(struct qed_hwfn *p_hwfn) +{ + u16 pf_rls_idx, num_pf_rls = qed_init_qm_get_num_pf_rls(p_hwfn); + struct qed_qm_info *qm_info = &p_hwfn->qm_info; - qm_info->vport_rl_en = 1; - qm_info->vport_wfq_en = 1; - qm_info->pf_rl = pf_rl; - qm_info->pf_wfq = pf_wfq; + if (!(qed_get_pq_flags(p_hwfn) & PQ_FLAGS_RLS)) + return; + + qed_init_qm_set_idx(p_hwfn, PQ_FLAGS_RLS, qm_info->num_pqs); + for (pf_rls_idx = 0; pf_rls_idx < num_pf_rls; pf_rls_idx++) + qed_init_qm_pq(p_hwfn, qm_info, PQ_INIT_OFLD_TC, PQ_INIT_PF_RL); +} + +static void qed_init_qm_pq_params(struct qed_hwfn *p_hwfn) +{ + /* rate limited pqs, must come first (FW assumption) */ + qed_init_qm_rl_pqs(p_hwfn); + + /* pqs for multi cos */ + qed_init_qm_mcos_pqs(p_hwfn); + + /* pure loopback pq */ + qed_init_qm_lb_pq(p_hwfn); + + /* out of order pq */ + qed_init_qm_ooo_pq(p_hwfn); + + /* pure ack pq */ + qed_init_qm_pure_ack_pq(p_hwfn); + + /* pq for offloaded protocol */ + qed_init_qm_offload_pq(p_hwfn); + + /* low latency pq */ + qed_init_qm_low_latency_pq(p_hwfn); + + /* done sharing vports */ + qed_init_qm_advance_vport(p_hwfn); + + /* pqs for vfs */ + qed_init_qm_vf_pqs(p_hwfn); +} + +/* compare values of getters against resources amounts */ +static int qed_init_qm_sanity(struct qed_hwfn *p_hwfn) +{ + if (qed_init_qm_get_num_vports(p_hwfn) > RESC_NUM(p_hwfn, QED_VPORT)) { + DP_ERR(p_hwfn, "requested amount of vports exceeds resource\n"); + return -EINVAL; + } + + if (qed_init_qm_get_num_pqs(p_hwfn) > RESC_NUM(p_hwfn, QED_PQ)) { + DP_ERR(p_hwfn, "requested amount of pqs exceeds resource\n"); + return -EINVAL; + } return 0; +} -alloc_err: - qed_qm_info_free(p_hwfn); - return -ENOMEM; +static void qed_dp_init_qm_params(struct qed_hwfn *p_hwfn) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + struct init_qm_vport_params *vport; + struct init_qm_port_params *port; + struct init_qm_pq_params *pq; + int i, tc; + + /* top level params */ + DP_VERBOSE(p_hwfn, + NETIF_MSG_HW, + "qm init top level params: start_pq %d, start_vport %d, pure_lb_pq %d, offload_pq %d, pure_ack_pq %d\n", + qm_info->start_pq, + qm_info->start_vport, + qm_info->pure_lb_pq, + qm_info->offload_pq, qm_info->pure_ack_pq); + DP_VERBOSE(p_hwfn, + NETIF_MSG_HW, + "ooo_pq %d, first_vf_pq %d, num_pqs %d, num_vf_pqs %d, num_vports %d, max_phys_tcs_per_port %d\n", + qm_info->ooo_pq, + qm_info->first_vf_pq, + qm_info->num_pqs, + qm_info->num_vf_pqs, + qm_info->num_vports, qm_info->max_phys_tcs_per_port); + DP_VERBOSE(p_hwfn, + NETIF_MSG_HW, + "pf_rl_en %d, pf_wfq_en %d, vport_rl_en %d, vport_wfq_en %d, pf_wfq %d, pf_rl %d, num_pf_rls %d, pq_flags %x\n", + qm_info->pf_rl_en, + qm_info->pf_wfq_en, + qm_info->vport_rl_en, + qm_info->vport_wfq_en, + qm_info->pf_wfq, + qm_info->pf_rl, + qm_info->num_pf_rls, qed_get_pq_flags(p_hwfn)); + + /* port table */ + for (i = 0; i < p_hwfn->cdev->num_ports_in_engines; i++) { + port = &(qm_info->qm_port_params[i]); + DP_VERBOSE(p_hwfn, + NETIF_MSG_HW, + "port idx %d, active %d, active_phys_tcs %d, num_pbf_cmd_lines %d, num_btb_blocks %d, reserved %d\n", + i, + port->active, + port->active_phys_tcs, + port->num_pbf_cmd_lines, + port->num_btb_blocks, port->reserved); + } + + /* vport table */ + for (i = 0; i < qm_info->num_vports; i++) { + vport = &(qm_info->qm_vport_params[i]); + DP_VERBOSE(p_hwfn, + NETIF_MSG_HW, + "vport idx %d, vport_rl %d, wfq %d, first_tx_pq_id [ ", + qm_info->start_vport + i, + vport->vport_rl, vport->vport_wfq); + for (tc = 0; tc < NUM_OF_TCS; tc++) + DP_VERBOSE(p_hwfn, + NETIF_MSG_HW, + "%d ", vport->first_tx_pq_id[tc]); + DP_VERBOSE(p_hwfn, NETIF_MSG_HW, "]\n"); + } + + /* pq table */ + for (i = 0; i < qm_info->num_pqs; i++) { + pq = &(qm_info->qm_pq_params[i]); + DP_VERBOSE(p_hwfn, + NETIF_MSG_HW, + "pq idx %d, vport_id %d, tc %d, wrr_grp %d, rl_valid %d\n", + qm_info->start_pq + i, + pq->vport_id, + pq->tc_id, pq->wrr_group, pq->rl_valid); + } +} + +static void qed_init_qm_info(struct qed_hwfn *p_hwfn) +{ + /* reset params required for init run */ + qed_init_qm_reset_params(p_hwfn); + + /* init QM top level params */ + qed_init_qm_params(p_hwfn); + + /* init QM port params */ + qed_init_qm_port_params(p_hwfn); + + /* init QM vport params */ + qed_init_qm_vport_params(p_hwfn); + + /* init QM physical queue params */ + qed_init_qm_pq_params(p_hwfn); + + /* display all that init */ + qed_dp_init_qm_params(p_hwfn); } /* This function reconfigures the QM pf on the fly. @@ -391,17 +765,8 @@ int qed_qm_reconf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) bool b_rc; int rc; - /* qm_info is allocated in qed_init_qm_info() which is already called - * from qed_resc_alloc() or previous call of qed_qm_reconf(). - * The allocated size may change each init, so we free it before next - * allocation. - */ - qed_qm_info_free(p_hwfn); - /* initialize qed's qm data structure */ - rc = qed_init_qm_info(p_hwfn, false); - if (rc) - return rc; + qed_init_qm_info(p_hwfn); /* stop PF's qm queues */ spin_lock_bh(&qm_lock); @@ -434,6 +799,47 @@ int qed_qm_reconf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) return 0; } +static int qed_alloc_qm_data(struct qed_hwfn *p_hwfn) +{ + struct qed_qm_info *qm_info = &p_hwfn->qm_info; + int rc; + + rc = qed_init_qm_sanity(p_hwfn); + if (rc) + goto alloc_err; + + qm_info->qm_pq_params = kzalloc(sizeof(*qm_info->qm_pq_params) * + qed_init_qm_get_num_pqs(p_hwfn), + GFP_KERNEL); + if (!qm_info->qm_pq_params) + goto alloc_err; + + qm_info->qm_vport_params = kzalloc(sizeof(*qm_info->qm_vport_params) * + qed_init_qm_get_num_vports(p_hwfn), + GFP_KERNEL); + if (!qm_info->qm_vport_params) + goto alloc_err; + + qm_info->qm_port_params = kzalloc(sizeof(qm_info->qm_port_params) * + p_hwfn->cdev->num_ports_in_engines, + GFP_KERNEL); + if (!qm_info->qm_port_params) + goto alloc_err; + + qm_info->wfq_data = kzalloc(sizeof(*qm_info->wfq_data) * + qed_init_qm_get_num_vports(p_hwfn), + GFP_KERNEL); + if (!qm_info->wfq_data) + goto alloc_err; + + return 0; + +alloc_err: + DP_NOTICE(p_hwfn, "Failed to allocate memory for QM params\n"); + qed_qm_info_free(p_hwfn); + return -ENOMEM; +} + int qed_resc_alloc(struct qed_dev *cdev) { struct qed_iscsi_info *p_iscsi_info; @@ -469,11 +875,13 @@ int qed_resc_alloc(struct qed_dev *cdev) if (rc) goto alloc_err; - /* Prepare and process QM requirements */ - rc = qed_init_qm_info(p_hwfn, true); + rc = qed_alloc_qm_data(p_hwfn); if (rc) goto alloc_err; + /* init qm info */ + qed_init_qm_info(p_hwfn); + /* Compute the ILT client partition */ rc = qed_cxt_cfg_ilt_compute(p_hwfn); if (rc) @@ -2253,6 +2661,9 @@ qed_get_hw_info(struct qed_hwfn *p_hwfn, p_hwfn->hw_info.personality = protocol; } + p_hwfn->hw_info.num_hw_tc = NUM_PHYS_TCS_4PORT_K2; + p_hwfn->hw_info.num_active_tc = 1; + qed_get_num_funcs(p_hwfn, p_ptt); if (qed_mcp_is_init(p_hwfn)) diff --git a/drivers/net/ethernet/qlogic/qed/qed_fcoe.c b/drivers/net/ethernet/qlogic/qed/qed_fcoe.c index 60921b72c995..f4b95345d1a5 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_fcoe.c +++ b/drivers/net/ethernet/qlogic/qed/qed_fcoe.c @@ -241,7 +241,7 @@ qed_sp_fcoe_conn_offload(struct qed_hwfn *p_hwfn, struct fcoe_conn_offload_ramrod_data *p_data; struct qed_spq_entry *p_ent = NULL; struct qed_sp_init_data init_data; - u16 pq_id = 0, tmp; + u16 physical_q0, tmp; int rc; /* Get SPQ entry */ @@ -261,9 +261,9 @@ qed_sp_fcoe_conn_offload(struct qed_hwfn *p_hwfn, p_data = &p_ramrod->offload_ramrod_data; /* Transmission PQ is the first of the PF */ - pq_id = qed_get_qm_pq(p_hwfn, PROTOCOLID_FCOE, NULL); - p_conn->physical_q0 = cpu_to_le16(pq_id); - p_data->physical_q0 = cpu_to_le16(pq_id); + physical_q0 = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_OFLD); + p_conn->physical_q0 = cpu_to_le16(physical_q0); + p_data->physical_q0 = cpu_to_le16(physical_q0); p_data->conn_id = cpu_to_le16(p_conn->conn_id); DMA_REGPAIR_LE(p_data->sq_pbl_addr, p_conn->sq_pbl_addr); diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index c6b9a3fd4f46..815c4ec5b458 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -9578,12 +9578,12 @@ struct dcbx_ets_feature { #define DCBX_ETS_CBS_SHIFT 3 #define DCBX_ETS_MAX_TCS_MASK 0x000000f0 #define DCBX_ETS_MAX_TCS_SHIFT 4 -#define DCBX_ISCSI_OOO_TC_MASK 0x00000f00 -#define DCBX_ISCSI_OOO_TC_SHIFT 8 +#define DCBX_OOO_TC_MASK 0x00000f00 +#define DCBX_OOO_TC_SHIFT 8 u32 pri_tc_tbl[1]; -#define DCBX_ISCSI_OOO_TC (4) +#define DCBX_TCP_OOO_TC (4) -#define NIG_ETS_ISCSI_OOO_CLIENT_OFFSET (DCBX_ISCSI_OOO_TC + 1) +#define NIG_ETS_ISCSI_OOO_CLIENT_OFFSET (DCBX_TCP_OOO_TC + 1) #define DCBX_CEE_STRICT_PRIORITY 0xf u32 tc_bw_tbl[2]; u32 tc_tsa_tbl[2]; @@ -9592,6 +9592,9 @@ struct dcbx_ets_feature { #define DCBX_ETS_TSA_ETS 2 }; +#define DCBX_TCP_OOO_TC (4) +#define DCBX_TCP_OOO_K2_4PORT_TC (3) + struct dcbx_app_priority_entry { u32 entry; #define DCBX_APP_PRI_MAP_MASK 0x000000ff diff --git a/drivers/net/ethernet/qlogic/qed/qed_hw.c b/drivers/net/ethernet/qlogic/qed/qed_hw.c index 899cad7f97ea..79e584a57d26 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hw.c +++ b/drivers/net/ethernet/qlogic/qed/qed_hw.c @@ -800,55 +800,3 @@ int qed_dmae_host2host(struct qed_hwfn *p_hwfn, return rc; } -u16 qed_get_qm_pq(struct qed_hwfn *p_hwfn, - enum protocol_type proto, union qed_qm_pq_params *p_params) -{ - u16 pq_id = 0; - - if ((proto == PROTOCOLID_CORE || - proto == PROTOCOLID_ETH || - proto == PROTOCOLID_ISCSI || - proto == PROTOCOLID_ROCE) && !p_params) { - DP_NOTICE(p_hwfn, - "Protocol %d received NULL PQ params\n", proto); - return 0; - } - - switch (proto) { - case PROTOCOLID_CORE: - if (p_params->core.tc == LB_TC) - pq_id = p_hwfn->qm_info.pure_lb_pq; - else if (p_params->core.tc == OOO_LB_TC) - pq_id = p_hwfn->qm_info.ooo_pq; - else - pq_id = p_hwfn->qm_info.offload_pq; - break; - case PROTOCOLID_ETH: - pq_id = p_params->eth.tc; - if (p_params->eth.is_vf) - pq_id += p_hwfn->qm_info.vf_queues_offset + - p_params->eth.vf_id; - break; - case PROTOCOLID_ISCSI: - if (p_params->iscsi.q_idx == 1) - pq_id = p_hwfn->qm_info.pure_ack_pq; - break; - case PROTOCOLID_ROCE: - if (p_params->roce.dcqcn) - pq_id = p_params->roce.qpid; - else - pq_id = p_hwfn->qm_info.offload_pq; - if (pq_id > p_hwfn->qm_info.num_pf_rls) - pq_id = p_hwfn->qm_info.offload_pq; - break; - case PROTOCOLID_FCOE: - pq_id = p_hwfn->qm_info.offload_pq; - break; - default: - pq_id = 0; - } - - pq_id = CM_TX_PQ_BASE + pq_id + RESC_START(p_hwfn, QED_PQ); - - return pq_id; -} diff --git a/drivers/net/ethernet/qlogic/qed/qed_hw.h b/drivers/net/ethernet/qlogic/qed/qed_hw.h index 9277264d2e65..f2505c691c26 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hw.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hw.h @@ -297,9 +297,6 @@ union qed_qm_pq_params { } roce; }; -u16 qed_get_qm_pq(struct qed_hwfn *p_hwfn, - enum protocol_type proto, union qed_qm_pq_params *params); - int qed_init_fw_data(struct qed_dev *cdev, const u8 *fw_data); #endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.c b/drivers/net/ethernet/qlogic/qed/qed_int.c index 84310b60849b..0ed24d6e6c65 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_int.c +++ b/drivers/net/ethernet/qlogic/qed/qed_int.c @@ -2500,8 +2500,9 @@ void qed_int_cau_conf_sb(struct qed_hwfn *p_hwfn, /* Configure pi coalescing if set */ if (p_hwfn->cdev->int_coalescing_mode == QED_COAL_MODE_ENABLE) { + u8 num_tc = p_hwfn->hw_info.num_hw_tc; u8 timeset, timer_res; - u8 num_tc = 1, i; + u8 i; /* timeset = (coalesce >> timer-res), timeset is 7bit wide */ if (p_hwfn->cdev->rx_coalesce_usecs <= 0x7F) diff --git a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c index 2f8ac75ebd84..112b96fba433 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c +++ b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c @@ -270,11 +270,10 @@ static int qed_sp_iscsi_conn_offload(struct qed_hwfn *p_hwfn, struct tcp_offload_params *p_tcp = NULL; struct qed_spq_entry *p_ent = NULL; struct qed_sp_init_data init_data; - union qed_qm_pq_params pq_params; - u16 pq0_id = 0, pq1_id = 0; dma_addr_t r2tq_pbl_addr; dma_addr_t xhq_pbl_addr; dma_addr_t uhq_pbl_addr; + u16 physical_q; int rc = 0; u32 dval; u16 wval; @@ -297,16 +296,14 @@ static int qed_sp_iscsi_conn_offload(struct qed_hwfn *p_hwfn, p_ramrod = &p_ent->ramrod.iscsi_conn_offload; /* Transmission PQ is the first of the PF */ - memset(&pq_params, 0, sizeof(pq_params)); - pq0_id = qed_get_qm_pq(p_hwfn, PROTOCOLID_ISCSI, &pq_params); - p_conn->physical_q0 = cpu_to_le16(pq0_id); - p_ramrod->iscsi.physical_q0 = cpu_to_le16(pq0_id); + physical_q = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_OFLD); + p_conn->physical_q0 = cpu_to_le16(physical_q); + p_ramrod->iscsi.physical_q0 = cpu_to_le16(physical_q); /* iSCSI Pure-ACK PQ */ - pq_params.iscsi.q_idx = 1; - pq1_id = qed_get_qm_pq(p_hwfn, PROTOCOLID_ISCSI, &pq_params); - p_conn->physical_q1 = cpu_to_le16(pq1_id); - p_ramrod->iscsi.physical_q1 = cpu_to_le16(pq1_id); + physical_q = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_ACK); + p_conn->physical_q1 = cpu_to_le16(physical_q); + p_ramrod->iscsi.physical_q1 = cpu_to_le16(physical_q); p_ramrod->hdr.op_code = ISCSI_RAMROD_CMD_ID_OFFLOAD_CONN; SET_FIELD(p_ramrod->hdr.flags, ISCSI_SLOW_PATH_HDR_LAYER_CODE, diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 4385ccbb5efb..9900f7a1f9f1 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -938,15 +938,12 @@ qed_eth_pf_tx_queue_start(struct qed_hwfn *p_hwfn, dma_addr_t pbl_addr, u16 pbl_size, void __iomem **pp_doorbell) { - union qed_qm_pq_params pq_params; int rc; - memset(&pq_params, 0, sizeof(pq_params)); rc = qed_eth_txq_start_ramrod(p_hwfn, p_cid, pbl_addr, pbl_size, - qed_get_qm_pq(p_hwfn, PROTOCOLID_ETH, - &pq_params)); + qed_get_cm_pq_idx_mcos(p_hwfn, tc)); if (rc) return rc; diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c index 178650aa0c6c..708c601e8ccf 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c @@ -1090,7 +1090,6 @@ static int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn, struct core_tx_start_ramrod_data *p_ramrod = NULL; struct qed_spq_entry *p_ent = NULL; struct qed_sp_init_data init_data; - union qed_qm_pq_params pq_params; u16 pq_id = 0, pbl_size; int rc = -EINVAL; @@ -1127,9 +1126,17 @@ static int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn, pbl_size = qed_chain_get_page_cnt(&p_tx->txq_chain); p_ramrod->pbl_size = cpu_to_le16(pbl_size); - memset(&pq_params, 0, sizeof(pq_params)); - pq_params.core.tc = p_ll2_conn->conn.tx_tc; - pq_id = qed_get_qm_pq(p_hwfn, PROTOCOLID_CORE, &pq_params); + switch (p_ll2_conn->conn.tx_tc) { + case LB_TC: + pq_id = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_LB); + break; + case OOO_LB_TC: + pq_id = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_OOO); + default: + pq_id = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_OFLD); + break; + } + p_ramrod->qm_pq_id = cpu_to_le16(pq_id); switch (conn_type) { diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c index 4bef5c59627c..b8c811f95205 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_roce.c +++ b/drivers/net/ethernet/qlogic/qed/qed_roce.c @@ -1224,7 +1224,6 @@ static int qed_roce_sp_create_responder(struct qed_hwfn *p_hwfn, { struct roce_create_qp_resp_ramrod_data *p_ramrod; struct qed_sp_init_data init_data; - union qed_qm_pq_params qm_params; enum roce_flavor roce_flavor; struct qed_spq_entry *p_ent; u16 regular_latency_queue; @@ -1313,10 +1312,7 @@ static int qed_roce_sp_create_responder(struct qed_hwfn *p_hwfn, p_ramrod->cq_cid = cpu_to_le32((p_hwfn->hw_info.opaque_fid << 16) | qp->rq_cq_id); - memset(&qm_params, 0, sizeof(qm_params)); - qm_params.roce.qpid = qp->icid >> 1; - regular_latency_queue = qed_get_qm_pq(p_hwfn, PROTOCOLID_ROCE, - &qm_params); + regular_latency_queue = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_OFLD); p_ramrod->regular_latency_phy_queue = cpu_to_le16(regular_latency_queue); @@ -1368,7 +1364,6 @@ static int qed_roce_sp_create_requester(struct qed_hwfn *p_hwfn, { struct roce_create_qp_req_ramrod_data *p_ramrod; struct qed_sp_init_data init_data; - union qed_qm_pq_params qm_params; enum roce_flavor roce_flavor; struct qed_spq_entry *p_ent; u16 regular_latency_queue; @@ -1446,10 +1441,7 @@ static int qed_roce_sp_create_requester(struct qed_hwfn *p_hwfn, p_ramrod->cq_cid = cpu_to_le32((p_hwfn->hw_info.opaque_fid << 16) | qp->sq_cq_id); - memset(&qm_params, 0, sizeof(qm_params)); - qm_params.roce.qpid = qp->icid >> 1; - regular_latency_queue = qed_get_qm_pq(p_hwfn, PROTOCOLID_ROCE, - &qm_params); + regular_latency_queue = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_OFLD); p_ramrod->regular_latency_phy_queue = cpu_to_le16(regular_latency_queue); diff --git a/drivers/net/ethernet/qlogic/qed/qed_spq.c b/drivers/net/ethernet/qlogic/qed/qed_spq.c index 54fbe3789cf3..13f715569253 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_spq.c +++ b/drivers/net/ethernet/qlogic/qed/qed_spq.c @@ -205,11 +205,10 @@ static int qed_spq_fill_entry(struct qed_hwfn *p_hwfn, static void qed_spq_hw_initialize(struct qed_hwfn *p_hwfn, struct qed_spq *p_spq) { - u16 pq; - struct qed_cxt_info cxt_info; - struct core_conn_context *p_cxt; - union qed_qm_pq_params pq_params; - int rc; + struct core_conn_context *p_cxt; + struct qed_cxt_info cxt_info; + u16 physical_q; + int rc; cxt_info.iid = p_spq->cid; @@ -231,10 +230,8 @@ static void qed_spq_hw_initialize(struct qed_hwfn *p_hwfn, XSTORM_CORE_CONN_AG_CTX_CONSOLID_PROD_CF_EN, 1); /* QM physical queue */ - memset(&pq_params, 0, sizeof(pq_params)); - pq_params.core.tc = LB_TC; - pq = qed_get_qm_pq(p_hwfn, PROTOCOLID_CORE, &pq_params); - p_cxt->xstorm_ag_context.physical_q0 = cpu_to_le16(pq); + physical_q = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_LB); + p_cxt->xstorm_ag_context.physical_q0 = cpu_to_le16(physical_q); p_cxt->xstorm_st_context.spq_base_lo = DMA_LO_LE(p_spq->chain.p_phys_addr); diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 18fc6e62ca41..92a3ee1715d9 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -2066,17 +2066,11 @@ static void qed_iov_vf_mbx_start_txq(struct qed_hwfn *p_hwfn, struct qed_queue_start_common_params params; struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; u8 status = PFVF_STATUS_NO_RESOURCE; - union qed_qm_pq_params pq_params; struct vfpf_start_txq_tlv *req; struct qed_vf_q_info *p_queue; int rc; u16 pq; - /* Prepare the parameters which would choose the right PQ */ - memset(&pq_params, 0, sizeof(pq_params)); - pq_params.eth.is_vf = 1; - pq_params.eth.vf_id = vf->relative_vf_id; - memset(¶ms, 0, sizeof(params)); req = &mbx->req_virt->start_txq; @@ -2101,7 +2095,7 @@ static void qed_iov_vf_mbx_start_txq(struct qed_hwfn *p_hwfn, if (!p_queue->p_tx_cid) goto out; - pq = qed_get_qm_pq(p_hwfn, PROTOCOLID_ETH, &pq_params); + pq = qed_get_cm_pq_idx_vf(p_hwfn, vf->relative_vf_id); rc = qed_eth_txq_start_ramrod(p_hwfn, p_queue->p_tx_cid, req->pbl_addr, req->pbl_size, pq); if (rc) { -- cgit From 44531ba45dbf3c23cc7ae0934ec9b33ef340ac56 Mon Sep 17 00:00:00 2001 From: Michal Kalderon Date: Mon, 3 Apr 2017 12:21:10 +0300 Subject: qed: Fix TM block ILT allocation When configuring the HW timers block we should set the number of CIDs up until the last CID that require timers, instead of only those CIDs whose protocol needs timers support. Today, the protocols that require HW timers' support have their CIDs before any other protocol, but that would change in future [when we add iWARP support]. Signed-off-by: Michal Kalderon Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_cxt.c | 32 ++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c index 1012b3cc358f..259dafa39e10 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c @@ -303,16 +303,34 @@ struct qed_tm_iids { u32 per_vf_tids; }; -static void qed_cxt_tm_iids(struct qed_cxt_mngr *p_mngr, +static void qed_cxt_tm_iids(struct qed_hwfn *p_hwfn, + struct qed_cxt_mngr *p_mngr, struct qed_tm_iids *iids) { - u32 i, j; - - for (i = 0; i < MAX_CONN_TYPES; i++) { + bool tm_vf_required = false; + bool tm_required = false; + int i, j; + + /* Timers is a special case -> we don't count how many cids require + * timers but what's the max cid that will be used by the timer block. + * therefore we traverse in reverse order, and once we hit a protocol + * that requires the timers memory, we'll sum all the protocols up + * to that one. + */ + for (i = MAX_CONN_TYPES - 1; i >= 0; i--) { struct qed_conn_type_cfg *p_cfg = &p_mngr->conn_cfg[i]; - if (tm_cid_proto(i)) { + if (tm_cid_proto(i) || tm_required) { + if (p_cfg->cid_count) + tm_required = true; + iids->pf_cids += p_cfg->cid_count; + } + + if (tm_cid_proto(i) || tm_vf_required) { + if (p_cfg->cids_per_vf) + tm_vf_required = true; + iids->per_vf_cids += p_cfg->cids_per_vf; } @@ -744,7 +762,7 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) /* TM PF */ p_cli = &p_mngr->clients[ILT_CLI_TM]; - qed_cxt_tm_iids(p_mngr, &tm_iids); + qed_cxt_tm_iids(p_hwfn, p_mngr, &tm_iids); total = tm_iids.pf_cids + tm_iids.pf_tids_total; if (total) { p_blk = &p_cli->pf_blks[0]; @@ -1632,7 +1650,7 @@ static void qed_tm_init_pf(struct qed_hwfn *p_hwfn) u8 i; memset(&tm_iids, 0, sizeof(tm_iids)); - qed_cxt_tm_iids(p_mngr, &tm_iids); + qed_cxt_tm_iids(p_hwfn, p_mngr, &tm_iids); /* @@@TBD No pre-scan for now */ -- cgit From 70566b42e94ad7de1328272c4b8454320f794a10 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Mon, 3 Apr 2017 12:21:11 +0300 Subject: qed: Correct TM ILT lines in presence of VFs As of today there's no protocol supported that requires support from the TM hardware block and enables SRIOV, but we should still correct the calculation to reflect the lines required for such future VFs instead of changing the PF's own lines. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_cxt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c index 259dafa39e10..f7b1a3732b04 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c @@ -783,8 +783,8 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) qed_ilt_cli_adv_line(p_hwfn, p_cli, p_blk, &curr_line, ILT_CLI_TM); - p_cli->pf_total_lines = curr_line - p_blk->start_line; + p_cli->vf_total_lines = curr_line - p_blk->start_line; for (i = 1; i < p_mngr->vf_count; i++) qed_ilt_cli_adv_line(p_hwfn, p_cli, p_blk, &curr_line, ILT_CLI_TM); -- cgit From 5f8cb033f4c814760c9f7f2345278111d06afe47 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Mon, 3 Apr 2017 12:21:12 +0300 Subject: qed: RoCE doesn't need to use SRC As RoCE doesn't need to use the SRC, allocating ILT memory on behalf of RoCE is wasting available ILT lines. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_cxt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c index f7b1a3732b04..8db023422bfd 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c @@ -241,8 +241,7 @@ struct qed_cxt_mngr { static bool src_proto(enum protocol_type type) { return type == PROTOCOLID_ISCSI || - type == PROTOCOLID_FCOE || - type == PROTOCOLID_ROCE; + type == PROTOCOLID_FCOE; } static bool tm_cid_proto(enum protocol_type type) -- cgit From f9dc4d1f0d6f75c102ee13c0a939d9ae880a3c1e Mon Sep 17 00:00:00 2001 From: Ram Amrani Date: Mon, 3 Apr 2017 12:21:13 +0300 Subject: qed: Manage with less memory regions for RoCE It's possible some configurations would prevent driver from utilizing all the Memory Regions due to a lack of ILT lines. In such a case, calculate how many memory regions would have to be dropped due to limit, and manage without those. Signed-off-by: Ram Amrani Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_cxt.c | 113 +++++++++++++++++++++-------- drivers/net/ethernet/qlogic/qed/qed_cxt.h | 15 +++- drivers/net/ethernet/qlogic/qed/qed_dev.c | 33 ++++++++- drivers/net/ethernet/qlogic/qed/qed_main.c | 1 - include/linux/qed/qed_if.h | 1 - 5 files changed, 125 insertions(+), 38 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c index 8db023422bfd..485b8b22ec7a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c @@ -543,7 +543,22 @@ static u32 qed_ilt_get_dynamic_line_cnt(struct qed_hwfn *p_hwfn, return lines_to_skip; } -int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) +static struct qed_ilt_client_cfg *qed_cxt_set_cli(struct qed_ilt_client_cfg + *p_cli) +{ + p_cli->active = false; + p_cli->first.val = 0; + p_cli->last.val = 0; + return p_cli; +} + +static struct qed_ilt_cli_blk *qed_cxt_set_blk(struct qed_ilt_cli_blk *p_blk) +{ + p_blk->total_size = 0; + return p_blk; +} + +int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn, u32 *line_count) { struct qed_cxt_mngr *p_mngr = p_hwfn->p_cxt_mngr; u32 curr_line, total, i, task_size, line; @@ -567,7 +582,8 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) p_hwfn->my_id, p_hwfn->p_cxt_mngr->pf_start_line); /* CDUC */ - p_cli = &p_mngr->clients[ILT_CLI_CDUC]; + p_cli = qed_cxt_set_cli(&p_mngr->clients[ILT_CLI_CDUC]); + curr_line = p_mngr->pf_start_line; /* CDUC PF */ @@ -576,7 +592,7 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) /* get the counters for the CDUC and QM clients */ qed_cxt_cdu_iids(p_mngr, &cdu_iids); - p_blk = &p_cli->pf_blks[CDUC_BLK]; + p_blk = qed_cxt_set_blk(&p_cli->pf_blks[CDUC_BLK]); total = cdu_iids.pf_cids * CONN_CXT_SIZE(p_hwfn); @@ -590,7 +606,7 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) ILT_CLI_CDUC); /* CDUC VF */ - p_blk = &p_cli->vf_blks[CDUC_BLK]; + p_blk = qed_cxt_set_blk(&p_cli->vf_blks[CDUC_BLK]); total = cdu_iids.per_vf_cids * CONN_CXT_SIZE(p_hwfn); qed_ilt_cli_blk_fill(p_cli, p_blk, curr_line, @@ -604,7 +620,7 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) ILT_CLI_CDUC); /* CDUT PF */ - p_cli = &p_mngr->clients[ILT_CLI_CDUT]; + p_cli = qed_cxt_set_cli(&p_mngr->clients[ILT_CLI_CDUT]); p_cli->first.val = curr_line; /* first the 'working' task memory */ @@ -613,7 +629,7 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) if (!p_seg || p_seg->count == 0) continue; - p_blk = &p_cli->pf_blks[CDUT_SEG_BLK(i)]; + p_blk = qed_cxt_set_blk(&p_cli->pf_blks[CDUT_SEG_BLK(i)]); total = p_seg->count * p_mngr->task_type_size[p_seg->type]; qed_ilt_cli_blk_fill(p_cli, p_blk, curr_line, total, p_mngr->task_type_size[p_seg->type]); @@ -628,7 +644,8 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) if (!p_seg || p_seg->count == 0) continue; - p_blk = &p_cli->pf_blks[CDUT_FL_SEG_BLK(i, PF)]; + p_blk = + qed_cxt_set_blk(&p_cli->pf_blks[CDUT_FL_SEG_BLK(i, PF)]); if (!p_seg->has_fl_mem) { /* The segment is active (total size pf 'working' @@ -673,7 +690,7 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) /* 'working' memory */ total = p_seg->count * p_mngr->task_type_size[p_seg->type]; - p_blk = &p_cli->vf_blks[CDUT_SEG_BLK(0)]; + p_blk = qed_cxt_set_blk(&p_cli->vf_blks[CDUT_SEG_BLK(0)]); qed_ilt_cli_blk_fill(p_cli, p_blk, curr_line, total, p_mngr->task_type_size[p_seg->type]); @@ -682,7 +699,8 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) ILT_CLI_CDUT); /* 'init' memory */ - p_blk = &p_cli->vf_blks[CDUT_FL_SEG_BLK(0, VF)]; + p_blk = + qed_cxt_set_blk(&p_cli->vf_blks[CDUT_FL_SEG_BLK(0, VF)]); if (!p_seg->has_fl_mem) { /* see comment above */ line = p_cli->vf_blks[CDUT_SEG_BLK(0)].start_line; @@ -710,8 +728,8 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) } /* QM */ - p_cli = &p_mngr->clients[ILT_CLI_QM]; - p_blk = &p_cli->pf_blks[0]; + p_cli = qed_cxt_set_cli(&p_mngr->clients[ILT_CLI_QM]); + p_blk = qed_cxt_set_blk(&p_cli->pf_blks[0]); qed_cxt_qm_iids(p_hwfn, &qm_iids); total = qed_qm_pf_mem_size(p_hwfn->rel_pf_id, qm_iids.cids, @@ -735,7 +753,7 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) p_cli->pf_total_lines = curr_line - p_blk->start_line; /* SRC */ - p_cli = &p_mngr->clients[ILT_CLI_SRC]; + p_cli = qed_cxt_set_cli(&p_mngr->clients[ILT_CLI_SRC]); qed_cxt_src_iids(p_mngr, &src_iids); /* Both the PF and VFs searcher connections are stored in the per PF @@ -749,7 +767,7 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) total = roundup_pow_of_two(local_max); - p_blk = &p_cli->pf_blks[0]; + p_blk = qed_cxt_set_blk(&p_cli->pf_blks[0]); qed_ilt_cli_blk_fill(p_cli, p_blk, curr_line, total * sizeof(struct src_ent), sizeof(struct src_ent)); @@ -760,11 +778,11 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) } /* TM PF */ - p_cli = &p_mngr->clients[ILT_CLI_TM]; + p_cli = qed_cxt_set_cli(&p_mngr->clients[ILT_CLI_TM]); qed_cxt_tm_iids(p_hwfn, p_mngr, &tm_iids); total = tm_iids.pf_cids + tm_iids.pf_tids_total; if (total) { - p_blk = &p_cli->pf_blks[0]; + p_blk = qed_cxt_set_blk(&p_cli->pf_blks[0]); qed_ilt_cli_blk_fill(p_cli, p_blk, curr_line, total * TM_ELEM_SIZE, TM_ELEM_SIZE); @@ -776,7 +794,7 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) /* TM VF */ total = tm_iids.per_vf_cids + tm_iids.per_vf_tids; if (total) { - p_blk = &p_cli->vf_blks[0]; + p_blk = qed_cxt_set_blk(&p_cli->vf_blks[0]); qed_ilt_cli_blk_fill(p_cli, p_blk, curr_line, total * TM_ELEM_SIZE, TM_ELEM_SIZE); @@ -793,8 +811,8 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) total = qed_cxt_get_srq_count(p_hwfn); if (total) { - p_cli = &p_mngr->clients[ILT_CLI_TSDM]; - p_blk = &p_cli->pf_blks[SRQ_BLK]; + p_cli = qed_cxt_set_cli(&p_mngr->clients[ILT_CLI_TSDM]); + p_blk = qed_cxt_set_blk(&p_cli->pf_blks[SRQ_BLK]); qed_ilt_cli_blk_fill(p_cli, p_blk, curr_line, total * SRQ_CXT_SIZE, SRQ_CXT_SIZE); @@ -803,13 +821,50 @@ int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn) p_cli->pf_total_lines = curr_line - p_blk->start_line; } + *line_count = curr_line - p_hwfn->p_cxt_mngr->pf_start_line; + if (curr_line - p_hwfn->p_cxt_mngr->pf_start_line > - RESC_NUM(p_hwfn, QED_ILT)) { - DP_ERR(p_hwfn, "too many ilt lines...#lines=%d\n", - curr_line - p_hwfn->p_cxt_mngr->pf_start_line); + RESC_NUM(p_hwfn, QED_ILT)) return -EINVAL; + + return 0; +} + +u32 qed_cxt_cfg_ilt_compute_excess(struct qed_hwfn *p_hwfn, u32 used_lines) +{ + struct qed_ilt_client_cfg *p_cli; + u32 excess_lines, available_lines; + struct qed_cxt_mngr *p_mngr; + u32 ilt_page_size, elem_size; + struct qed_tid_seg *p_seg; + int i; + + available_lines = RESC_NUM(p_hwfn, QED_ILT); + excess_lines = used_lines - available_lines; + + if (!excess_lines) + return 0; + + if (p_hwfn->hw_info.personality != QED_PCI_ETH_ROCE) + return 0; + + p_mngr = p_hwfn->p_cxt_mngr; + p_cli = &p_mngr->clients[ILT_CLI_CDUT]; + ilt_page_size = ILT_PAGE_IN_BYTES(p_cli->p_size.val); + + for (i = 0; i < NUM_TASK_PF_SEGMENTS; i++) { + p_seg = qed_cxt_tid_seg_info(p_hwfn, i); + if (!p_seg || p_seg->count == 0) + continue; + + elem_size = p_mngr->task_type_size[p_seg->type]; + if (!elem_size) + continue; + + return (ilt_page_size / elem_size) * excess_lines; } + DP_NOTICE(p_hwfn, "failed computing excess ILT lines\n"); return 0; } @@ -1893,13 +1948,12 @@ int qed_cxt_get_cid_info(struct qed_hwfn *p_hwfn, struct qed_cxt_info *p_info) } static void qed_rdma_set_pf_params(struct qed_hwfn *p_hwfn, - struct qed_rdma_pf_params *p_params) + struct qed_rdma_pf_params *p_params, + u32 num_tasks) { - u32 num_cons, num_tasks, num_qps, num_mrs, num_srqs; + u32 num_cons, num_qps, num_srqs; enum protocol_type proto; - num_mrs = min_t(u32, RDMA_MAX_TIDS, p_params->num_mrs); - num_tasks = num_mrs; /* each mr uses a single task id */ num_srqs = min_t(u32, 32 * 1024, p_params->num_srqs); switch (p_hwfn->hw_info.personality) { @@ -1928,7 +1982,7 @@ static void qed_rdma_set_pf_params(struct qed_hwfn *p_hwfn, } } -int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn) +int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn, u32 rdma_tasks) { /* Set the number of required CORE connections */ u32 core_cids = 1; /* SPQ */ @@ -1940,9 +1994,10 @@ int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn) switch (p_hwfn->hw_info.personality) { case QED_PCI_ETH_ROCE: { - qed_rdma_set_pf_params(p_hwfn, - &p_hwfn-> - pf_params.rdma_pf_params); + qed_rdma_set_pf_params(p_hwfn, + &p_hwfn-> + pf_params.rdma_pf_params, + rdma_tasks); /* no need for break since RoCE coexist with Ethernet */ } case QED_PCI_ETH: diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.h b/drivers/net/ethernet/qlogic/qed/qed_cxt.h index 8b010324268a..f34b2889f4bb 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.h +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.h @@ -105,19 +105,28 @@ u32 qed_cxt_get_proto_cid_count(struct qed_hwfn *p_hwfn, * @brief qed_cxt_set_pf_params - Set the PF params for cxt init * * @param p_hwfn - * + * @param rdma_tasks - requested maximum * @return int */ -int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn); +int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn, u32 rdma_tasks); /** * @brief qed_cxt_cfg_ilt_compute - compute ILT init parameters * * @param p_hwfn + * @param last_line * * @return int */ -int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn); +int qed_cxt_cfg_ilt_compute(struct qed_hwfn *p_hwfn, u32 *last_line); + +/** + * @brief qed_cxt_cfg_ilt_compute_excess - how many lines can be decreased + * + * @param p_hwfn + * @param used_lines + */ +u32 qed_cxt_cfg_ilt_compute_excess(struct qed_hwfn *p_hwfn, u32 used_lines); /** * @brief qed_cxt_mngr_alloc - Allocate and init the context manager struct diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index b48c80ec4e5b..249878533fd9 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -848,8 +848,10 @@ int qed_resc_alloc(struct qed_dev *cdev) #ifdef CONFIG_QED_LL2 struct qed_ll2_info *p_ll2_info; #endif + u32 rdma_tasks, excess_tasks; struct qed_consq *p_consq; struct qed_eq *p_eq; + u32 line_count; int i, rc = 0; if (IS_VF(cdev)) @@ -871,7 +873,7 @@ int qed_resc_alloc(struct qed_dev *cdev) /* Set the HW cid/tid numbers (in the contest manager) * Must be done prior to any further computations. */ - rc = qed_cxt_set_pf_params(p_hwfn); + rc = qed_cxt_set_pf_params(p_hwfn, RDMA_MAX_TIDS); if (rc) goto alloc_err; @@ -883,9 +885,32 @@ int qed_resc_alloc(struct qed_dev *cdev) qed_init_qm_info(p_hwfn); /* Compute the ILT client partition */ - rc = qed_cxt_cfg_ilt_compute(p_hwfn); - if (rc) - goto alloc_err; + rc = qed_cxt_cfg_ilt_compute(p_hwfn, &line_count); + if (rc) { + DP_NOTICE(p_hwfn, + "too many ILT lines; re-computing with less lines\n"); + /* In case there are not enough ILT lines we reduce the + * number of RDMA tasks and re-compute. + */ + excess_tasks = + qed_cxt_cfg_ilt_compute_excess(p_hwfn, line_count); + if (!excess_tasks) + goto alloc_err; + + rdma_tasks = RDMA_MAX_TIDS - excess_tasks; + rc = qed_cxt_set_pf_params(p_hwfn, rdma_tasks); + if (rc) + goto alloc_err; + + rc = qed_cxt_cfg_ilt_compute(p_hwfn, &line_count); + if (rc) { + DP_ERR(p_hwfn, + "failed ILT compute. Requested too many lines: %u\n", + line_count); + + goto alloc_err; + } + } /* CID map / ILT shadow table / T2 * The talbes sizes are determined by the computations above diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index d4edb993b1b0..634e7a2433a9 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -877,7 +877,6 @@ static void qed_update_pf_params(struct qed_dev *cdev, params->rdma_pf_params.num_qps = QED_ROCE_QPS; params->rdma_pf_params.min_dpis = QED_ROCE_DPIS; /* divide by 3 the MRs to avoid MF ILT overflow */ - params->rdma_pf_params.num_mrs = RDMA_MAX_TIDS; params->rdma_pf_params.gl_pi = QED_ROCE_PROTOCOL_INDEX; } diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index 8e0065c52857..625f80f08f91 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -263,7 +263,6 @@ struct qed_rdma_pf_params { * the doorbell BAR). */ u32 min_dpis; /* number of requested DPIs */ - u32 num_mrs; /* number of requested memory regions */ u32 num_qps; /* number of requested Queue Pairs */ u32 num_srqs; /* number of requested SRQ */ u8 roce_edpm_mode; /* see QED_ROCE_EDPM_MODE_ENABLE */ -- cgit From 869a78a2be13055173dc1fbe87f49e338e59f7ef Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Fri, 17 Feb 2017 16:52:32 +0100 Subject: can: ti_hecc: Add TI HECC DT binding documentation DT binding documentation for TI High End CAN Controller Signed-off-by: Anton Glukhov Signed-off-by: Yegor Yefremov Acked-by: Rob Herring Signed-off-by: Marc Kleine-Budde --- .../devicetree/bindings/net/can/ti_hecc.txt | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/can/ti_hecc.txt diff --git a/Documentation/devicetree/bindings/net/can/ti_hecc.txt b/Documentation/devicetree/bindings/net/can/ti_hecc.txt new file mode 100644 index 000000000000..e0f0a7cfe329 --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/ti_hecc.txt @@ -0,0 +1,32 @@ +Texas Instruments High End CAN Controller (HECC) +================================================ + +This file provides information, what the device node +for the hecc interface contains. + +Required properties: +- compatible: "ti,am3517-hecc" +- reg: addresses and lengths of the register spaces for 'hecc', 'hecc-ram' + and 'mbx' +- reg-names :"hecc", "hecc-ram", "mbx" +- interrupts: interrupt mapping for the hecc interrupts sources +- clocks: clock phandles (see clock bindings for details) + +Optional properties: +- ti,use-hecc1int: if provided configures HECC to produce all interrupts + on HECC1INT interrupt line. By default HECC0INT interrupt + line will be used. +- xceiver-supply: regulator that powers the CAN transceiver + +Example: + +For am3517evm board: + hecc: can@5c050000 { + compatible = "ti,am3517-hecc"; + reg = <0x5c050000 0x80>, + <0x5c053000 0x180>, + <0x5c052000 0x200>; + reg-names = "hecc", "hecc-ram", "mbx"; + interrupts = <24>; + clocks = <&hecc_ck>; + }; -- cgit From dabf54dd1c6369160f8d4c793a8613dfb4e7848a Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Fri, 17 Feb 2017 16:52:33 +0100 Subject: can: ti_hecc: Convert TI HECC driver to DT only driver This patch converts TI HECC driver to DT only driver. This results in removing ti_hecc.h containing now obsolete platform data. Former transceiver_switch callback function will be now modelled via regulator API. Signed-off-by: Anton Glukhov Signed-off-by: Yegor Yefremov Signed-off-by: Marc Kleine-Budde --- drivers/net/can/ti_hecc.c | 170 ++++++++++++++++++----------------- include/linux/can/platform/ti_hecc.h | 44 --------- 2 files changed, 88 insertions(+), 126 deletions(-) delete mode 100644 include/linux/can/platform/ti_hecc.h diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 6749b1829469..b8aac538275c 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -17,25 +17,6 @@ * */ -/* - * Your platform definitions should specify module ram offsets and interrupt - * number to use as follows: - * - * static struct ti_hecc_platform_data am3517_evm_hecc_pdata = { - * .scc_hecc_offset = 0, - * .scc_ram_offset = 0x3000, - * .hecc_ram_offset = 0x3000, - * .mbx_offset = 0x2000, - * .int_line = 0, - * .revision = 1, - * .transceiver_switch = hecc_phy_control, - * }; - * - * Please see include/linux/can/platform/ti_hecc.h for description of - * above fields. - * - */ - #include #include #include @@ -46,11 +27,13 @@ #include #include #include +#include +#include +#include #include #include #include -#include #define DRV_NAME "ti_hecc" #define HECC_MODULE_VERSION "0.7" @@ -214,15 +197,14 @@ struct ti_hecc_priv { struct net_device *ndev; struct clk *clk; void __iomem *base; - u32 scc_ram_offset; - u32 hecc_ram_offset; - u32 mbx_offset; - u32 int_line; + void __iomem *hecc_ram; + void __iomem *mbx; + bool use_hecc1int; spinlock_t mbx_lock; /* CANME register needs protection */ u32 tx_head; u32 tx_tail; u32 rx_next; - void (*transceiver_switch)(int); + struct regulator *reg_xceiver; }; static inline int get_tx_head_mb(struct ti_hecc_priv *priv) @@ -242,20 +224,18 @@ static inline int get_tx_head_prio(struct ti_hecc_priv *priv) static inline void hecc_write_lam(struct ti_hecc_priv *priv, u32 mbxno, u32 val) { - __raw_writel(val, priv->base + priv->hecc_ram_offset + mbxno * 4); + __raw_writel(val, priv->hecc_ram + mbxno * 4); } static inline void hecc_write_mbx(struct ti_hecc_priv *priv, u32 mbxno, u32 reg, u32 val) { - __raw_writel(val, priv->base + priv->mbx_offset + mbxno * 0x10 + - reg); + __raw_writel(val, priv->mbx + mbxno * 0x10 + reg); } static inline u32 hecc_read_mbx(struct ti_hecc_priv *priv, u32 mbxno, u32 reg) { - return __raw_readl(priv->base + priv->mbx_offset + mbxno * 0x10 + - reg); + return __raw_readl(priv->mbx + mbxno * 0x10 + reg); } static inline void hecc_write(struct ti_hecc_priv *priv, u32 reg, u32 val) @@ -311,11 +291,16 @@ static int ti_hecc_set_btc(struct ti_hecc_priv *priv) return 0; } -static void ti_hecc_transceiver_switch(const struct ti_hecc_priv *priv, - int on) +static int ti_hecc_transceiver_switch(const struct ti_hecc_priv *priv, + int on) { - if (priv->transceiver_switch) - priv->transceiver_switch(on); + if (!priv->reg_xceiver) + return 0; + + if (on) + return regulator_enable(priv->reg_xceiver); + else + return regulator_disable(priv->reg_xceiver); } static void ti_hecc_reset(struct net_device *ndev) @@ -409,7 +394,7 @@ static void ti_hecc_start(struct net_device *ndev) /* Prevent message over-write & Enable interrupts */ hecc_write(priv, HECC_CANOPC, HECC_SET_REG); - if (priv->int_line) { + if (priv->use_hecc1int) { hecc_write(priv, HECC_CANMIL, HECC_SET_REG); hecc_write(priv, HECC_CANGIM, HECC_CANGIM_DEF_MASK | HECC_CANGIM_I1EN | HECC_CANGIM_SIL); @@ -760,7 +745,7 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id) unsigned long ack, flags; int_status = hecc_read(priv, - (priv->int_line) ? HECC_CANGIF1 : HECC_CANGIF0); + (priv->use_hecc1int) ? HECC_CANGIF1 : HECC_CANGIF0); if (!int_status) return IRQ_NONE; @@ -806,7 +791,7 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id) } /* clear all interrupt conditions - read back to avoid spurious ints */ - if (priv->int_line) { + if (priv->use_hecc1int) { hecc_write(priv, HECC_CANGIF1, HECC_SET_REG); int_status = hecc_read(priv, HECC_CANGIF1); } else { @@ -872,58 +857,87 @@ static const struct net_device_ops ti_hecc_netdev_ops = { .ndo_change_mtu = can_change_mtu, }; +static const struct of_device_id ti_hecc_dt_ids[] = { + { + .compatible = "ti,am3517-hecc", + }, + { } +}; +MODULE_DEVICE_TABLE(of, ti_hecc_dt_ids); + static int ti_hecc_probe(struct platform_device *pdev) { struct net_device *ndev = (struct net_device *)0; struct ti_hecc_priv *priv; - struct ti_hecc_platform_data *pdata; - struct resource *mem, *irq; - void __iomem *addr; + struct device_node *np = pdev->dev.of_node; + struct resource *res, *irq; + struct regulator *reg_xceiver; int err = -ENODEV; - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) { - dev_err(&pdev->dev, "No platform data\n"); - goto probe_exit; + if (!IS_ENABLED(CONFIG_OF) || !np) + return -EINVAL; + + reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver"); + if (PTR_ERR(reg_xceiver) == -EPROBE_DEFER) + return -EPROBE_DEFER; + else if (IS_ERR(reg_xceiver)) + reg_xceiver = NULL; + + ndev = alloc_candev(sizeof(struct ti_hecc_priv), HECC_MAX_TX_MBOX); + if (!ndev) { + dev_err(&pdev->dev, "alloc_candev failed\n"); + return -ENOMEM; } + priv = netdev_priv(ndev); - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "No mem resources\n"); - goto probe_exit; + /* handle hecc memory */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hecc"); + if (!res) { + dev_err(&pdev->dev, "can't get IORESOURCE_MEM hecc\n"); + return -EINVAL; } - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq) { - dev_err(&pdev->dev, "No irq resource\n"); - goto probe_exit; + + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (!priv->base) { + dev_err(&pdev->dev, "hecc ioremap failed\n"); + return -ENOMEM; } - if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) { - dev_err(&pdev->dev, "HECC region already claimed\n"); - err = -EBUSY; - goto probe_exit; + + /* handle hecc-ram memory */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hecc-ram"); + if (!res) { + dev_err(&pdev->dev, "can't get IORESOURCE_MEM hecc-ram\n"); + return -EINVAL; } - addr = ioremap(mem->start, resource_size(mem)); - if (!addr) { - dev_err(&pdev->dev, "ioremap failed\n"); - err = -ENOMEM; - goto probe_exit_free_region; + + priv->hecc_ram = devm_ioremap_resource(&pdev->dev, res); + if (!priv->hecc_ram) { + dev_err(&pdev->dev, "hecc-ram ioremap failed\n"); + return -ENOMEM; } - ndev = alloc_candev(sizeof(struct ti_hecc_priv), HECC_MAX_TX_MBOX); - if (!ndev) { - dev_err(&pdev->dev, "alloc_candev failed\n"); - err = -ENOMEM; - goto probe_exit_iounmap; + /* handle mbx memory */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mbx"); + if (!res) { + dev_err(&pdev->dev, "can't get IORESOURCE_MEM mbx\n"); + return -EINVAL; + } + + priv->mbx = devm_ioremap_resource(&pdev->dev, res); + if (!priv->mbx) { + dev_err(&pdev->dev, "mbx ioremap failed\n"); + return -ENOMEM; + } + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "No irq resource\n"); + goto probe_exit; } - priv = netdev_priv(ndev); priv->ndev = ndev; - priv->base = addr; - priv->scc_ram_offset = pdata->scc_ram_offset; - priv->hecc_ram_offset = pdata->hecc_ram_offset; - priv->mbx_offset = pdata->mbx_offset; - priv->int_line = pdata->int_line; - priv->transceiver_switch = pdata->transceiver_switch; + priv->reg_xceiver = reg_xceiver; + priv->use_hecc1int = of_property_read_bool(np, "ti,use-hecc1int"); priv->can.bittiming_const = &ti_hecc_bittiming_const; priv->can.do_set_mode = ti_hecc_do_set_mode; @@ -971,32 +985,23 @@ probe_exit_clk: clk_put(priv->clk); probe_exit_candev: free_candev(ndev); -probe_exit_iounmap: - iounmap(addr); -probe_exit_free_region: - release_mem_region(mem->start, resource_size(mem)); probe_exit: return err; } static int ti_hecc_remove(struct platform_device *pdev) { - struct resource *res; struct net_device *ndev = platform_get_drvdata(pdev); struct ti_hecc_priv *priv = netdev_priv(ndev); unregister_candev(ndev); clk_disable_unprepare(priv->clk); clk_put(priv->clk); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - iounmap(priv->base); - release_mem_region(res->start, resource_size(res)); free_candev(ndev); return 0; } - #ifdef CONFIG_PM static int ti_hecc_suspend(struct platform_device *pdev, pm_message_t state) { @@ -1045,6 +1050,7 @@ static int ti_hecc_resume(struct platform_device *pdev) static struct platform_driver ti_hecc_driver = { .driver = { .name = DRV_NAME, + .of_match_table = ti_hecc_dt_ids, }, .probe = ti_hecc_probe, .remove = ti_hecc_remove, diff --git a/include/linux/can/platform/ti_hecc.h b/include/linux/can/platform/ti_hecc.h deleted file mode 100644 index a52f47ca6c8a..000000000000 --- a/include/linux/can/platform/ti_hecc.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _CAN_PLATFORM_TI_HECC_H -#define _CAN_PLATFORM_TI_HECC_H - -/* - * TI HECC (High End CAN Controller) driver platform header - * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation 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. - * - */ - -/** - * struct hecc_platform_data - HECC Platform Data - * - * @scc_hecc_offset: mostly 0 - should really never change - * @scc_ram_offset: SCC RAM offset - * @hecc_ram_offset: HECC RAM offset - * @mbx_offset: Mailbox RAM offset - * @int_line: Interrupt line to use - 0 or 1 - * @version: version for future use - * @transceiver_switch: platform specific callback fn for transceiver control - * - * Platform data structure to get all platform specific settings. - * this structure also accounts the fact that the IP may have different - * RAM and mailbox offsets for different SOC's - */ -struct ti_hecc_platform_data { - u32 scc_hecc_offset; - u32 scc_ram_offset; - u32 hecc_ram_offset; - u32 mbx_offset; - u32 int_line; - u32 version; - void (*transceiver_switch) (int); -}; -#endif /* !_CAN_PLATFORM_TI_HECC_H */ -- cgit From 8e8cda6d737d356054c9eeef642aec0e8ae7e6bc Mon Sep 17 00:00:00 2001 From: Mario Kicherer Date: Tue, 21 Feb 2017 12:19:47 +0100 Subject: can: initial support for network namespaces This patch adds initial support for network namespaces. The changes only enable support in the CAN raw, proc and af_can code. GW and BCM still have their checks that ensure that they are used only from the main namespace. The patch boils down to moving the global structures, i.e. the global filter list and their /proc stats, into a per-namespace structure and passing around the corresponding "struct net" in a lot of different places. Changes since v1: - rebased on current HEAD (2bfe01e) - fixed overlong line Signed-off-by: Mario Kicherer Signed-off-by: Marc Kleine-Budde --- include/linux/can/core.h | 7 ++- include/net/net_namespace.h | 4 ++ include/net/netns/can.h | 31 ++++++++++ net/can/af_can.c | 122 +++++++++++++++++++++---------------- net/can/af_can.h | 4 +- net/can/bcm.c | 13 ++-- net/can/gw.c | 4 +- net/can/proc.c | 144 +++++++++++++++++++++----------------------- net/can/raw.c | 92 ++++++++++++++++------------ 9 files changed, 242 insertions(+), 179 deletions(-) create mode 100644 include/net/netns/can.h diff --git a/include/linux/can/core.h b/include/linux/can/core.h index df08a41d5be5..319a0da827b8 100644 --- a/include/linux/can/core.h +++ b/include/linux/can/core.h @@ -45,12 +45,13 @@ struct can_proto { extern int can_proto_register(const struct can_proto *cp); extern void can_proto_unregister(const struct can_proto *cp); -int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask, +int can_rx_register(struct net *net, struct net_device *dev, + canid_t can_id, canid_t mask, void (*func)(struct sk_buff *, void *), void *data, char *ident, struct sock *sk); -extern void can_rx_unregister(struct net_device *dev, canid_t can_id, - canid_t mask, +extern void can_rx_unregister(struct net *net, struct net_device *dev, + canid_t can_id, canid_t mask, void (*func)(struct sk_buff *, void *), void *data); diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index af8fe8a909dc..fe80bb48ab1f 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -140,6 +141,9 @@ struct net { #endif #if IS_ENABLED(CONFIG_MPLS) struct netns_mpls mpls; +#endif +#if IS_ENABLED(CONFIG_CAN) + struct netns_can can; #endif struct sock *diag_nlsk; atomic_t fnhe_genid; diff --git a/include/net/netns/can.h b/include/net/netns/can.h new file mode 100644 index 000000000000..e8beba772f1a --- /dev/null +++ b/include/net/netns/can.h @@ -0,0 +1,31 @@ +/* + * can in net namespaces + */ + +#ifndef __NETNS_CAN_H__ +#define __NETNS_CAN_H__ + +#include + +struct dev_rcv_lists; + +struct netns_can { +#if IS_ENABLED(CONFIG_PROC_FS) + struct proc_dir_entry *proc_dir; + struct proc_dir_entry *pde_version; + struct proc_dir_entry *pde_stats; + struct proc_dir_entry *pde_reset_stats; + struct proc_dir_entry *pde_rcvlist_all; + struct proc_dir_entry *pde_rcvlist_fil; + struct proc_dir_entry *pde_rcvlist_inv; + struct proc_dir_entry *pde_rcvlist_sff; + struct proc_dir_entry *pde_rcvlist_eff; + struct proc_dir_entry *pde_rcvlist_err; +#endif + + /* receive filters subscribed for 'all' CAN devices */ + struct dev_rcv_lists *can_rx_alldev_list; + spinlock_t can_rcvlists_lock; +}; + +#endif /* __NETNS_CAN_H__ */ diff --git a/net/can/af_can.c b/net/can/af_can.c index 5488e4a6ccd0..abf7d854a94d 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -75,9 +75,7 @@ static int stats_timer __read_mostly = 1; module_param(stats_timer, int, S_IRUGO); MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)"); -/* receive filters subscribed for 'all' CAN devices */ -struct dev_rcv_lists can_rx_alldev_list; -static DEFINE_SPINLOCK(can_rcvlists_lock); +static int can_net_id; static struct kmem_cache *rcv_cache __read_mostly; @@ -145,9 +143,6 @@ static int can_create(struct net *net, struct socket *sock, int protocol, if (protocol < 0 || protocol >= CAN_NPROTO) return -EINVAL; - if (!net_eq(net, &init_net)) - return -EAFNOSUPPORT; - cp = can_get_proto(protocol); #ifdef CONFIG_MODULES @@ -331,10 +326,11 @@ EXPORT_SYMBOL(can_send); * af_can rx path */ -static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev) +static struct dev_rcv_lists *find_dev_rcv_lists(struct net *net, + struct net_device *dev) { if (!dev) - return &can_rx_alldev_list; + return net->can.can_rx_alldev_list; else return (struct dev_rcv_lists *)dev->ml_priv; } @@ -467,9 +463,9 @@ static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask, * -ENOMEM on missing cache mem to create subscription entry * -ENODEV unknown device */ -int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask, - void (*func)(struct sk_buff *, void *), void *data, - char *ident, struct sock *sk) +int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id, + canid_t mask, void (*func)(struct sk_buff *, void *), + void *data, char *ident, struct sock *sk) { struct receiver *r; struct hlist_head *rl; @@ -481,13 +477,16 @@ int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask, if (dev && dev->type != ARPHRD_CAN) return -ENODEV; + if (dev && !net_eq(net, dev_net(dev))) + return -ENODEV; + r = kmem_cache_alloc(rcv_cache, GFP_KERNEL); if (!r) return -ENOMEM; - spin_lock(&can_rcvlists_lock); + spin_lock(&net->can.can_rcvlists_lock); - d = find_dev_rcv_lists(dev); + d = find_dev_rcv_lists(net, dev); if (d) { rl = find_rcv_list(&can_id, &mask, d); @@ -510,7 +509,7 @@ int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask, err = -ENODEV; } - spin_unlock(&can_rcvlists_lock); + spin_unlock(&net->can.can_rcvlists_lock); return err; } @@ -540,8 +539,9 @@ static void can_rx_delete_receiver(struct rcu_head *rp) * Description: * Removes subscription entry depending on given (subscription) values. */ -void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask, - void (*func)(struct sk_buff *, void *), void *data) +void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id, + canid_t mask, void (*func)(struct sk_buff *, void *), + void *data) { struct receiver *r = NULL; struct hlist_head *rl; @@ -550,9 +550,12 @@ void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask, if (dev && dev->type != ARPHRD_CAN) return; - spin_lock(&can_rcvlists_lock); + if (dev && !net_eq(net, dev_net(dev))) + return; - d = find_dev_rcv_lists(dev); + spin_lock(&net->can.can_rcvlists_lock); + + d = find_dev_rcv_lists(net, dev); if (!d) { pr_err("BUG: receive list not found for " "dev %s, id %03X, mask %03X\n", @@ -598,7 +601,7 @@ void can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask, } out: - spin_unlock(&can_rcvlists_lock); + spin_unlock(&net->can.can_rcvlists_lock); /* schedule the receiver item for deletion */ if (r) { @@ -696,10 +699,10 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev) rcu_read_lock(); /* deliver the packet to sockets listening on all devices */ - matches = can_rcv_filter(&can_rx_alldev_list, skb); + matches = can_rcv_filter(dev_net(dev)->can.can_rx_alldev_list, skb); /* find receive list for this device */ - d = find_dev_rcv_lists(dev); + d = find_dev_rcv_lists(dev_net(dev), dev); if (d) matches += can_rcv_filter(d, skb); @@ -719,9 +722,6 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev, { struct canfd_frame *cfd = (struct canfd_frame *)skb->data; - if (unlikely(!net_eq(dev_net(dev), &init_net))) - goto drop; - if (WARN_ONCE(dev->type != ARPHRD_CAN || skb->len != CAN_MTU || cfd->len > CAN_MAX_DLEN, @@ -743,9 +743,6 @@ static int canfd_rcv(struct sk_buff *skb, struct net_device *dev, { struct canfd_frame *cfd = (struct canfd_frame *)skb->data; - if (unlikely(!net_eq(dev_net(dev), &init_net))) - goto drop; - if (WARN_ONCE(dev->type != ARPHRD_CAN || skb->len != CANFD_MTU || cfd->len > CANFD_MAX_DLEN, @@ -835,9 +832,6 @@ static int can_notifier(struct notifier_block *nb, unsigned long msg, struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct dev_rcv_lists *d; - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - if (dev->type != ARPHRD_CAN) return NOTIFY_DONE; @@ -855,7 +849,7 @@ static int can_notifier(struct notifier_block *nb, unsigned long msg, break; case NETDEV_UNREGISTER: - spin_lock(&can_rcvlists_lock); + spin_lock(&dev_net(dev)->can.can_rcvlists_lock); d = dev->ml_priv; if (d) { @@ -869,7 +863,7 @@ static int can_notifier(struct notifier_block *nb, unsigned long msg, pr_err("can: notifier: receive list not found for dev " "%s\n", dev->name); - spin_unlock(&can_rcvlists_lock); + spin_unlock(&dev_net(dev)->can.can_rcvlists_lock); break; } @@ -877,6 +871,40 @@ static int can_notifier(struct notifier_block *nb, unsigned long msg, return NOTIFY_DONE; } +static int can_pernet_init(struct net *net) +{ + net->can.can_rcvlists_lock = + __SPIN_LOCK_UNLOCKED(net->can.can_rcvlists_lock); + net->can.can_rx_alldev_list = + kzalloc(sizeof(struct dev_rcv_lists), GFP_KERNEL); + + if (IS_ENABLED(CONFIG_PROC_FS)) + can_init_proc(net); + + return 0; +} + +static void can_pernet_exit(struct net *net) +{ + struct net_device *dev; + + if (IS_ENABLED(CONFIG_PROC_FS)) + can_remove_proc(net); + + /* remove created dev_rcv_lists from still registered CAN devices */ + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { + if (dev->type == ARPHRD_CAN && dev->ml_priv) { + struct dev_rcv_lists *d = dev->ml_priv; + + BUG_ON(d->entries); + kfree(d); + dev->ml_priv = NULL; + } + } + rcu_read_unlock(); +} + /* * af_can module init/exit functions */ @@ -902,6 +930,13 @@ static struct notifier_block can_netdev_notifier __read_mostly = { .notifier_call = can_notifier, }; +static struct pernet_operations can_pernet_ops __read_mostly = { + .init = can_pernet_init, + .exit = can_pernet_exit, + .id = &can_net_id, + .size = 0, +}; + static __init int can_init(void) { /* check for correct padding to be able to use the structs similarly */ @@ -912,8 +947,6 @@ static __init int can_init(void) pr_info("can: controller area network core (" CAN_VERSION_STRING ")\n"); - memset(&can_rx_alldev_list, 0, sizeof(can_rx_alldev_list)); - rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver), 0, 0, NULL); if (!rcv_cache) @@ -925,9 +958,10 @@ static __init int can_init(void) setup_timer(&can_stattimer, can_stat_update, 0); mod_timer(&can_stattimer, round_jiffies(jiffies + HZ)); } - can_init_proc(); } + register_pernet_subsys(&can_pernet_ops); + /* protocol register */ sock_register(&can_family_ops); register_netdevice_notifier(&can_netdev_notifier); @@ -939,13 +973,9 @@ static __init int can_init(void) static __exit void can_exit(void) { - struct net_device *dev; - if (IS_ENABLED(CONFIG_PROC_FS)) { if (stats_timer) del_timer_sync(&can_stattimer); - - can_remove_proc(); } /* protocol unregister */ @@ -954,19 +984,7 @@ static __exit void can_exit(void) unregister_netdevice_notifier(&can_netdev_notifier); sock_unregister(PF_CAN); - /* remove created dev_rcv_lists from still registered CAN devices */ - rcu_read_lock(); - for_each_netdev_rcu(&init_net, dev) { - if (dev->type == ARPHRD_CAN && dev->ml_priv) { - - struct dev_rcv_lists *d = dev->ml_priv; - - BUG_ON(d->entries); - kfree(d); - dev->ml_priv = NULL; - } - } - rcu_read_unlock(); + unregister_pernet_subsys(&can_pernet_ops); rcu_barrier(); /* Wait for completion of call_rcu()'s */ diff --git a/net/can/af_can.h b/net/can/af_can.h index b86f5129e838..f273c9d9b129 100644 --- a/net/can/af_can.h +++ b/net/can/af_can.h @@ -114,8 +114,8 @@ struct s_pstats { extern struct dev_rcv_lists can_rx_alldev_list; /* function prototypes for the CAN networklayer procfs (proc.c) */ -void can_init_proc(void); -void can_remove_proc(void); +void can_init_proc(struct net *net); +void can_remove_proc(struct net *net); void can_stat_update(unsigned long data); /* structures and variables from af_can.c needed in proc.c for reading */ diff --git a/net/can/bcm.c b/net/can/bcm.c index 95d13b233c65..1976629a8463 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -764,8 +764,8 @@ static void bcm_remove_op(struct bcm_op *op) static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op) { if (op->rx_reg_dev == dev) { - can_rx_unregister(dev, op->can_id, REGMASK(op->can_id), - bcm_rx_handler, op); + can_rx_unregister(&init_net, dev, op->can_id, + REGMASK(op->can_id), bcm_rx_handler, op); /* mark as removed subscription */ op->rx_reg_dev = NULL; @@ -808,7 +808,7 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, } } } else - can_rx_unregister(NULL, op->can_id, + can_rx_unregister(&init_net, NULL, op->can_id, REGMASK(op->can_id), bcm_rx_handler, op); @@ -1222,7 +1222,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, dev = dev_get_by_index(&init_net, ifindex); if (dev) { - err = can_rx_register(dev, op->can_id, + err = can_rx_register(&init_net, dev, + op->can_id, REGMASK(op->can_id), bcm_rx_handler, op, "bcm", sk); @@ -1232,7 +1233,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } } else - err = can_rx_register(NULL, op->can_id, + err = can_rx_register(&init_net, NULL, op->can_id, REGMASK(op->can_id), bcm_rx_handler, op, "bcm", sk); if (err) { @@ -1528,7 +1529,7 @@ static int bcm_release(struct socket *sock) } } } else - can_rx_unregister(NULL, op->can_id, + can_rx_unregister(&init_net, NULL, op->can_id, REGMASK(op->can_id), bcm_rx_handler, op); diff --git a/net/can/gw.c b/net/can/gw.c index 7056a1a2bb70..3c117a33e15f 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -440,14 +440,14 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) static inline int cgw_register_filter(struct cgw_job *gwj) { - return can_rx_register(gwj->src.dev, gwj->ccgw.filter.can_id, + return can_rx_register(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id, gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj, "gw", NULL); } static inline void cgw_unregister_filter(struct cgw_job *gwj) { - can_rx_unregister(gwj->src.dev, gwj->ccgw.filter.can_id, + can_rx_unregister(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id, gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj); } diff --git a/net/can/proc.c b/net/can/proc.c index 85ef7bb0f176..9a8d54d57b22 100644 --- a/net/can/proc.c +++ b/net/can/proc.c @@ -62,17 +62,6 @@ #define CAN_PROC_RCVLIST_EFF "rcvlist_eff" #define CAN_PROC_RCVLIST_ERR "rcvlist_err" -static struct proc_dir_entry *can_dir; -static struct proc_dir_entry *pde_version; -static struct proc_dir_entry *pde_stats; -static struct proc_dir_entry *pde_reset_stats; -static struct proc_dir_entry *pde_rcvlist_all; -static struct proc_dir_entry *pde_rcvlist_fil; -static struct proc_dir_entry *pde_rcvlist_inv; -static struct proc_dir_entry *pde_rcvlist_sff; -static struct proc_dir_entry *pde_rcvlist_eff; -static struct proc_dir_entry *pde_rcvlist_err; - static int user_reset; static const char rx_list_name[][8] = { @@ -351,20 +340,21 @@ static inline void can_rcvlist_proc_show_one(struct seq_file *m, int idx, static int can_rcvlist_proc_show(struct seq_file *m, void *v) { /* double cast to prevent GCC warning */ - int idx = (int)(long)m->private; + int idx = (int)(long)PDE_DATA(m->file->f_inode); struct net_device *dev; struct dev_rcv_lists *d; + struct net *net = m->private; seq_printf(m, "\nreceive list '%s':\n", rx_list_name[idx]); rcu_read_lock(); /* receive list for 'all' CAN devices (dev == NULL) */ - d = &can_rx_alldev_list; + d = net->can.can_rx_alldev_list; can_rcvlist_proc_show_one(m, idx, NULL, d); /* receive list for registered CAN devices */ - for_each_netdev_rcu(&init_net, dev) { + for_each_netdev_rcu(net, dev) { if (dev->type == ARPHRD_CAN && dev->ml_priv) can_rcvlist_proc_show_one(m, idx, dev, dev->ml_priv); } @@ -377,7 +367,7 @@ static int can_rcvlist_proc_show(struct seq_file *m, void *v) static int can_rcvlist_proc_open(struct inode *inode, struct file *file) { - return single_open(file, can_rcvlist_proc_show, PDE_DATA(inode)); + return single_open_net(inode, file, can_rcvlist_proc_show); } static const struct file_operations can_rcvlist_proc_fops = { @@ -417,6 +407,7 @@ static int can_rcvlist_sff_proc_show(struct seq_file *m, void *v) { struct net_device *dev; struct dev_rcv_lists *d; + struct net *net = m->private; /* RX_SFF */ seq_puts(m, "\nreceive list 'rx_sff':\n"); @@ -424,11 +415,11 @@ static int can_rcvlist_sff_proc_show(struct seq_file *m, void *v) rcu_read_lock(); /* sff receive list for 'all' CAN devices (dev == NULL) */ - d = &can_rx_alldev_list; + d = net->can.can_rx_alldev_list; can_rcvlist_proc_show_array(m, NULL, d->rx_sff, ARRAY_SIZE(d->rx_sff)); /* sff receive list for registered CAN devices */ - for_each_netdev_rcu(&init_net, dev) { + for_each_netdev_rcu(net, dev) { if (dev->type == ARPHRD_CAN && dev->ml_priv) { d = dev->ml_priv; can_rcvlist_proc_show_array(m, dev, d->rx_sff, @@ -444,7 +435,7 @@ static int can_rcvlist_sff_proc_show(struct seq_file *m, void *v) static int can_rcvlist_sff_proc_open(struct inode *inode, struct file *file) { - return single_open(file, can_rcvlist_sff_proc_show, NULL); + return single_open_net(inode, file, can_rcvlist_sff_proc_show); } static const struct file_operations can_rcvlist_sff_proc_fops = { @@ -460,6 +451,7 @@ static int can_rcvlist_eff_proc_show(struct seq_file *m, void *v) { struct net_device *dev; struct dev_rcv_lists *d; + struct net *net = m->private; /* RX_EFF */ seq_puts(m, "\nreceive list 'rx_eff':\n"); @@ -467,11 +459,11 @@ static int can_rcvlist_eff_proc_show(struct seq_file *m, void *v) rcu_read_lock(); /* eff receive list for 'all' CAN devices (dev == NULL) */ - d = &can_rx_alldev_list; + d = net->can.can_rx_alldev_list; can_rcvlist_proc_show_array(m, NULL, d->rx_eff, ARRAY_SIZE(d->rx_eff)); /* eff receive list for registered CAN devices */ - for_each_netdev_rcu(&init_net, dev) { + for_each_netdev_rcu(net, dev) { if (dev->type == ARPHRD_CAN && dev->ml_priv) { d = dev->ml_priv; can_rcvlist_proc_show_array(m, dev, d->rx_eff, @@ -487,7 +479,7 @@ static int can_rcvlist_eff_proc_show(struct seq_file *m, void *v) static int can_rcvlist_eff_proc_open(struct inode *inode, struct file *file) { - return single_open(file, can_rcvlist_eff_proc_show, NULL); + return single_open_net(inode, file, can_rcvlist_eff_proc_show); } static const struct file_operations can_rcvlist_eff_proc_fops = { @@ -498,82 +490,86 @@ static const struct file_operations can_rcvlist_eff_proc_fops = { .release = single_release, }; -/* - * proc utility functions - */ - -static void can_remove_proc_readentry(const char *name) -{ - if (can_dir) - remove_proc_entry(name, can_dir); -} - /* * can_init_proc - create main CAN proc directory and procfs entries */ -void can_init_proc(void) +void can_init_proc(struct net *net) { /* create /proc/net/can directory */ - can_dir = proc_mkdir("can", init_net.proc_net); + net->can.proc_dir = proc_net_mkdir(net, "can", net->proc_net); - if (!can_dir) { - pr_info("can: failed to create /proc/net/can.\n"); + if (!net->can.proc_dir) { + printk(KERN_INFO "can: failed to create /proc/net/can . " + "CONFIG_PROC_FS missing?\n"); return; } /* own procfs entries from the AF_CAN core */ - pde_version = proc_create(CAN_PROC_VERSION, 0644, can_dir, - &can_version_proc_fops); - pde_stats = proc_create(CAN_PROC_STATS, 0644, can_dir, - &can_stats_proc_fops); - pde_reset_stats = proc_create(CAN_PROC_RESET_STATS, 0644, can_dir, - &can_reset_stats_proc_fops); - pde_rcvlist_err = proc_create_data(CAN_PROC_RCVLIST_ERR, 0644, can_dir, - &can_rcvlist_proc_fops, (void *)RX_ERR); - pde_rcvlist_all = proc_create_data(CAN_PROC_RCVLIST_ALL, 0644, can_dir, - &can_rcvlist_proc_fops, (void *)RX_ALL); - pde_rcvlist_fil = proc_create_data(CAN_PROC_RCVLIST_FIL, 0644, can_dir, - &can_rcvlist_proc_fops, (void *)RX_FIL); - pde_rcvlist_inv = proc_create_data(CAN_PROC_RCVLIST_INV, 0644, can_dir, - &can_rcvlist_proc_fops, (void *)RX_INV); - pde_rcvlist_eff = proc_create(CAN_PROC_RCVLIST_EFF, 0644, can_dir, - &can_rcvlist_eff_proc_fops); - pde_rcvlist_sff = proc_create(CAN_PROC_RCVLIST_SFF, 0644, can_dir, - &can_rcvlist_sff_proc_fops); + net->can.pde_version = proc_create(CAN_PROC_VERSION, 0644, + net->can.proc_dir, + &can_version_proc_fops); + net->can.pde_stats = proc_create(CAN_PROC_STATS, 0644, + net->can.proc_dir, + &can_stats_proc_fops); + net->can.pde_reset_stats = proc_create(CAN_PROC_RESET_STATS, 0644, + net->can.proc_dir, + &can_reset_stats_proc_fops); + net->can.pde_rcvlist_err = proc_create_data(CAN_PROC_RCVLIST_ERR, 0644, + net->can.proc_dir, + &can_rcvlist_proc_fops, + (void *)RX_ERR); + net->can.pde_rcvlist_all = proc_create_data(CAN_PROC_RCVLIST_ALL, 0644, + net->can.proc_dir, + &can_rcvlist_proc_fops, + (void *)RX_ALL); + net->can.pde_rcvlist_fil = proc_create_data(CAN_PROC_RCVLIST_FIL, 0644, + net->can.proc_dir, + &can_rcvlist_proc_fops, + (void *)RX_FIL); + net->can.pde_rcvlist_inv = proc_create_data(CAN_PROC_RCVLIST_INV, 0644, + net->can.proc_dir, + &can_rcvlist_proc_fops, + (void *)RX_INV); + net->can.pde_rcvlist_eff = proc_create(CAN_PROC_RCVLIST_EFF, 0644, + net->can.proc_dir, + &can_rcvlist_eff_proc_fops); + net->can.pde_rcvlist_sff = proc_create(CAN_PROC_RCVLIST_SFF, 0644, + net->can.proc_dir, + &can_rcvlist_sff_proc_fops); } /* * can_remove_proc - remove procfs entries and main CAN proc directory */ -void can_remove_proc(void) +void can_remove_proc(struct net *net) { - if (pde_version) - can_remove_proc_readentry(CAN_PROC_VERSION); + if (net->can.pde_version) + remove_proc_entry(CAN_PROC_VERSION, net->can.proc_dir); - if (pde_stats) - can_remove_proc_readentry(CAN_PROC_STATS); + if (net->can.pde_stats) + remove_proc_entry(CAN_PROC_STATS, net->can.proc_dir); - if (pde_reset_stats) - can_remove_proc_readentry(CAN_PROC_RESET_STATS); + if (net->can.pde_reset_stats) + remove_proc_entry(CAN_PROC_RESET_STATS, net->can.proc_dir); - if (pde_rcvlist_err) - can_remove_proc_readentry(CAN_PROC_RCVLIST_ERR); + if (net->can.pde_rcvlist_err) + remove_proc_entry(CAN_PROC_RCVLIST_ERR, net->can.proc_dir); - if (pde_rcvlist_all) - can_remove_proc_readentry(CAN_PROC_RCVLIST_ALL); + if (net->can.pde_rcvlist_all) + remove_proc_entry(CAN_PROC_RCVLIST_ALL, net->can.proc_dir); - if (pde_rcvlist_fil) - can_remove_proc_readentry(CAN_PROC_RCVLIST_FIL); + if (net->can.pde_rcvlist_fil) + remove_proc_entry(CAN_PROC_RCVLIST_FIL, net->can.proc_dir); - if (pde_rcvlist_inv) - can_remove_proc_readentry(CAN_PROC_RCVLIST_INV); + if (net->can.pde_rcvlist_inv) + remove_proc_entry(CAN_PROC_RCVLIST_INV, net->can.proc_dir); - if (pde_rcvlist_eff) - can_remove_proc_readentry(CAN_PROC_RCVLIST_EFF); + if (net->can.pde_rcvlist_eff) + remove_proc_entry(CAN_PROC_RCVLIST_EFF, net->can.proc_dir); - if (pde_rcvlist_sff) - can_remove_proc_readentry(CAN_PROC_RCVLIST_SFF); + if (net->can.pde_rcvlist_sff) + remove_proc_entry(CAN_PROC_RCVLIST_SFF, net->can.proc_dir); - if (can_dir) - remove_proc_entry("can", init_net.proc_net); + if (net->can.proc_dir) + remove_proc_entry("can", net->proc_net); } diff --git a/net/can/raw.c b/net/can/raw.c index 6dc546a06673..864c80dbdb72 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -181,20 +181,21 @@ static void raw_rcv(struct sk_buff *oskb, void *data) kfree_skb(skb); } -static int raw_enable_filters(struct net_device *dev, struct sock *sk, - struct can_filter *filter, int count) +static int raw_enable_filters(struct net *net, struct net_device *dev, + struct sock *sk, struct can_filter *filter, + int count) { int err = 0; int i; for (i = 0; i < count; i++) { - err = can_rx_register(dev, filter[i].can_id, + err = can_rx_register(net, dev, filter[i].can_id, filter[i].can_mask, raw_rcv, sk, "raw", sk); if (err) { /* clean up successfully registered filters */ while (--i >= 0) - can_rx_unregister(dev, filter[i].can_id, + can_rx_unregister(net, dev, filter[i].can_id, filter[i].can_mask, raw_rcv, sk); break; @@ -204,57 +205,62 @@ static int raw_enable_filters(struct net_device *dev, struct sock *sk, return err; } -static int raw_enable_errfilter(struct net_device *dev, struct sock *sk, - can_err_mask_t err_mask) +static int raw_enable_errfilter(struct net *net, struct net_device *dev, + struct sock *sk, can_err_mask_t err_mask) { int err = 0; if (err_mask) - err = can_rx_register(dev, 0, err_mask | CAN_ERR_FLAG, + err = can_rx_register(net, dev, 0, err_mask | CAN_ERR_FLAG, raw_rcv, sk, "raw", sk); return err; } -static void raw_disable_filters(struct net_device *dev, struct sock *sk, - struct can_filter *filter, int count) +static void raw_disable_filters(struct net *net, struct net_device *dev, + struct sock *sk, struct can_filter *filter, + int count) { int i; for (i = 0; i < count; i++) - can_rx_unregister(dev, filter[i].can_id, filter[i].can_mask, - raw_rcv, sk); + can_rx_unregister(net, dev, filter[i].can_id, + filter[i].can_mask, raw_rcv, sk); } -static inline void raw_disable_errfilter(struct net_device *dev, +static inline void raw_disable_errfilter(struct net *net, + struct net_device *dev, struct sock *sk, can_err_mask_t err_mask) { if (err_mask) - can_rx_unregister(dev, 0, err_mask | CAN_ERR_FLAG, + can_rx_unregister(net, dev, 0, err_mask | CAN_ERR_FLAG, raw_rcv, sk); } -static inline void raw_disable_allfilters(struct net_device *dev, +static inline void raw_disable_allfilters(struct net *net, + struct net_device *dev, struct sock *sk) { struct raw_sock *ro = raw_sk(sk); - raw_disable_filters(dev, sk, ro->filter, ro->count); - raw_disable_errfilter(dev, sk, ro->err_mask); + raw_disable_filters(net, dev, sk, ro->filter, ro->count); + raw_disable_errfilter(net, dev, sk, ro->err_mask); } -static int raw_enable_allfilters(struct net_device *dev, struct sock *sk) +static int raw_enable_allfilters(struct net *net, struct net_device *dev, + struct sock *sk) { struct raw_sock *ro = raw_sk(sk); int err; - err = raw_enable_filters(dev, sk, ro->filter, ro->count); + err = raw_enable_filters(net, dev, sk, ro->filter, ro->count); if (!err) { - err = raw_enable_errfilter(dev, sk, ro->err_mask); + err = raw_enable_errfilter(net, dev, sk, ro->err_mask); if (err) - raw_disable_filters(dev, sk, ro->filter, ro->count); + raw_disable_filters(net, dev, sk, ro->filter, + ro->count); } return err; @@ -267,7 +273,7 @@ static int raw_notifier(struct notifier_block *nb, struct raw_sock *ro = container_of(nb, struct raw_sock, notifier); struct sock *sk = &ro->sk; - if (!net_eq(dev_net(dev), &init_net)) + if (!net_eq(dev_net(dev), sock_net(sk))) return NOTIFY_DONE; if (dev->type != ARPHRD_CAN) @@ -282,7 +288,7 @@ static int raw_notifier(struct notifier_block *nb, lock_sock(sk); /* remove current filters & unregister */ if (ro->bound) - raw_disable_allfilters(dev, sk); + raw_disable_allfilters(dev_net(dev), dev, sk); if (ro->count > 1) kfree(ro->filter); @@ -358,13 +364,13 @@ static int raw_release(struct socket *sock) if (ro->ifindex) { struct net_device *dev; - dev = dev_get_by_index(&init_net, ro->ifindex); + dev = dev_get_by_index(sock_net(sk), ro->ifindex); if (dev) { - raw_disable_allfilters(dev, sk); + raw_disable_allfilters(dev_net(dev), dev, sk); dev_put(dev); } } else - raw_disable_allfilters(NULL, sk); + raw_disable_allfilters(sock_net(sk), NULL, sk); } if (ro->count > 1) @@ -404,7 +410,7 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len) if (addr->can_ifindex) { struct net_device *dev; - dev = dev_get_by_index(&init_net, addr->can_ifindex); + dev = dev_get_by_index(sock_net(sk), addr->can_ifindex); if (!dev) { err = -ENODEV; goto out; @@ -420,13 +426,13 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len) ifindex = dev->ifindex; /* filters set by default/setsockopt */ - err = raw_enable_allfilters(dev, sk); + err = raw_enable_allfilters(sock_net(sk), dev, sk); dev_put(dev); } else { ifindex = 0; /* filters set by default/setsockopt */ - err = raw_enable_allfilters(NULL, sk); + err = raw_enable_allfilters(sock_net(sk), NULL, sk); } if (!err) { @@ -435,13 +441,15 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len) if (ro->ifindex) { struct net_device *dev; - dev = dev_get_by_index(&init_net, ro->ifindex); + dev = dev_get_by_index(sock_net(sk), + ro->ifindex); if (dev) { - raw_disable_allfilters(dev, sk); + raw_disable_allfilters(dev_net(dev), + dev, sk); dev_put(dev); } } else - raw_disable_allfilters(NULL, sk); + raw_disable_allfilters(sock_net(sk), NULL, sk); } ro->ifindex = ifindex; ro->bound = 1; @@ -517,15 +525,16 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, lock_sock(sk); if (ro->bound && ro->ifindex) - dev = dev_get_by_index(&init_net, ro->ifindex); + dev = dev_get_by_index(sock_net(sk), ro->ifindex); if (ro->bound) { /* (try to) register the new filters */ if (count == 1) - err = raw_enable_filters(dev, sk, &sfilter, 1); + err = raw_enable_filters(sock_net(sk), dev, sk, + &sfilter, 1); else - err = raw_enable_filters(dev, sk, filter, - count); + err = raw_enable_filters(sock_net(sk), dev, sk, + filter, count); if (err) { if (count > 1) kfree(filter); @@ -533,7 +542,8 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, } /* remove old filter registrations */ - raw_disable_filters(dev, sk, ro->filter, ro->count); + raw_disable_filters(sock_net(sk), dev, sk, ro->filter, + ro->count); } /* remove old filter space */ @@ -569,18 +579,20 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, lock_sock(sk); if (ro->bound && ro->ifindex) - dev = dev_get_by_index(&init_net, ro->ifindex); + dev = dev_get_by_index(sock_net(sk), ro->ifindex); /* remove current error mask */ if (ro->bound) { /* (try to) register the new err_mask */ - err = raw_enable_errfilter(dev, sk, err_mask); + err = raw_enable_errfilter(sock_net(sk), dev, sk, + err_mask); if (err) goto out_err; /* remove old err_mask registration */ - raw_disable_errfilter(dev, sk, ro->err_mask); + raw_disable_errfilter(sock_net(sk), dev, sk, + ro->err_mask); } /* link new err_mask to the socket */ @@ -741,7 +753,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) return -EINVAL; } - dev = dev_get_by_index(&init_net, ifindex); + dev = dev_get_by_index(sock_net(sk), ifindex); if (!dev) return -ENXIO; -- cgit From 20a1e355116a8579bdf20839b3c07b51ad466e88 Mon Sep 17 00:00:00 2001 From: Akshay Bhat Date: Fri, 17 Mar 2017 17:10:39 -0400 Subject: can: holt_hi311x: document device tree bindings Document the HOLT HI-311x CAN device tree bindings. Signed-off-by: Akshay Bhat Acked-by: Rob Herring Signed-off-by: Marc Kleine-Budde --- .../devicetree/bindings/net/can/holt_hi311x.txt | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/can/holt_hi311x.txt diff --git a/Documentation/devicetree/bindings/net/can/holt_hi311x.txt b/Documentation/devicetree/bindings/net/can/holt_hi311x.txt new file mode 100644 index 000000000000..23aa94eab207 --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/holt_hi311x.txt @@ -0,0 +1,24 @@ +* Holt HI-311X stand-alone CAN controller device tree bindings + +Required properties: + - compatible: Should be one of the following: + - "holt,hi3110" for HI-3110 + - reg: SPI chip select. + - clocks: The clock feeding the CAN controller. + - interrupt-parent: The parent interrupt controller. + - interrupts: Should contain IRQ line for the CAN controller. + +Optional properties: + - vdd-supply: Regulator that powers the CAN controller. + - xceiver-supply: Regulator that powers the CAN transceiver. + +Example: + can0: can@1 { + compatible = "holt,hi3110"; + reg = <1>; + clocks = <&clk32m>; + interrupt-parent = <&gpio4>; + interrupts = <13 IRQ_TYPE_EDGE_RISING>; + vdd-supply = <®5v0>; + xceiver-supply = <®5v0>; + }; -- cgit From 57e83fb9b7468c75cb65cde1d23043553c346c6d Mon Sep 17 00:00:00 2001 From: Akshay Bhat Date: Fri, 17 Mar 2017 17:10:40 -0400 Subject: can: hi311x: Add Holt HI-311x CAN driver This patch adds support for the Holt HI-311x CAN controller. The HI311x CAN controller is capable of transmitting and receiving standard data frames, extended data frames and remote frames. The HI311x interfaces with the host over SPI. Datasheet: www.holtic.com/documents/371-hi-3110_v-rev-jpdf.do Signed-off-by: Akshay Bhat Acked-by: Wolfgang Grandegger Signed-off-by: Marc Kleine-Budde --- drivers/net/can/spi/Kconfig | 6 + drivers/net/can/spi/Makefile | 1 + drivers/net/can/spi/hi311x.c | 1076 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1083 insertions(+) create mode 100644 drivers/net/can/spi/hi311x.c diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig index 148cae5871a6..8f2e0dd7b756 100644 --- a/drivers/net/can/spi/Kconfig +++ b/drivers/net/can/spi/Kconfig @@ -1,6 +1,12 @@ menu "CAN SPI interfaces" depends on SPI +config CAN_HI311X + tristate "Holt HI311x SPI CAN controllers" + depends on CAN_DEV && SPI && HAS_DMA + ---help--- + Driver for the Holt HI311x SPI CAN controllers. + config CAN_MCP251X tristate "Microchip MCP251x SPI CAN controllers" depends on HAS_DMA diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile index 0e86040cdd8c..f59fa3731073 100644 --- a/drivers/net/can/spi/Makefile +++ b/drivers/net/can/spi/Makefile @@ -3,4 +3,5 @@ # +obj-$(CONFIG_CAN_HI311X) += hi311x.o obj-$(CONFIG_CAN_MCP251X) += mcp251x.o diff --git a/drivers/net/can/spi/hi311x.c b/drivers/net/can/spi/hi311x.c new file mode 100644 index 000000000000..5590c559a8ca --- /dev/null +++ b/drivers/net/can/spi/hi311x.c @@ -0,0 +1,1076 @@ +/* CAN bus driver for Holt HI3110 CAN Controller with SPI Interface + * + * Copyright(C) Timesys Corporation 2016 + * + * Based on Microchip 251x CAN Controller (mcp251x) Linux kernel driver + * Copyright 2009 Christian Pellegrin EVOL S.r.l. + * Copyright 2007 Raymarine UK, Ltd. All Rights Reserved. + * Copyright 2006 Arcom Control Systems Ltd. + * + * Based on CAN bus driver for the CCAN controller written by + * - Sascha Hauer, Marc Kleine-Budde, Pengutronix + * - Simon Kallweit, intefo AG + * Copyright 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HI3110_MASTER_RESET 0x56 +#define HI3110_READ_CTRL0 0xD2 +#define HI3110_READ_CTRL1 0xD4 +#define HI3110_READ_STATF 0xE2 +#define HI3110_WRITE_CTRL0 0x14 +#define HI3110_WRITE_CTRL1 0x16 +#define HI3110_WRITE_INTE 0x1C +#define HI3110_WRITE_BTR0 0x18 +#define HI3110_WRITE_BTR1 0x1A +#define HI3110_READ_BTR0 0xD6 +#define HI3110_READ_BTR1 0xD8 +#define HI3110_READ_INTF 0xDE +#define HI3110_READ_ERR 0xDC +#define HI3110_READ_FIFO_WOTIME 0x48 +#define HI3110_WRITE_FIFO 0x12 +#define HI3110_READ_MESSTAT 0xDA +#define HI3110_READ_REC 0xEA +#define HI3110_READ_TEC 0xEC + +#define HI3110_CTRL0_MODE_MASK (7 << 5) +#define HI3110_CTRL0_NORMAL_MODE (0 << 5) +#define HI3110_CTRL0_LOOPBACK_MODE (1 << 5) +#define HI3110_CTRL0_MONITOR_MODE (2 << 5) +#define HI3110_CTRL0_SLEEP_MODE (3 << 5) +#define HI3110_CTRL0_INIT_MODE (4 << 5) + +#define HI3110_CTRL1_TXEN BIT(7) + +#define HI3110_INT_RXTMP BIT(7) +#define HI3110_INT_RXFIFO BIT(6) +#define HI3110_INT_TXCPLT BIT(5) +#define HI3110_INT_BUSERR BIT(4) +#define HI3110_INT_MCHG BIT(3) +#define HI3110_INT_WAKEUP BIT(2) +#define HI3110_INT_F1MESS BIT(1) +#define HI3110_INT_F0MESS BIT(0) + +#define HI3110_ERR_BUSOFF BIT(7) +#define HI3110_ERR_TXERRP BIT(6) +#define HI3110_ERR_RXERRP BIT(5) +#define HI3110_ERR_BITERR BIT(4) +#define HI3110_ERR_FRMERR BIT(3) +#define HI3110_ERR_CRCERR BIT(2) +#define HI3110_ERR_ACKERR BIT(1) +#define HI3110_ERR_STUFERR BIT(0) +#define HI3110_ERR_PROTOCOL_MASK (0x1F) +#define HI3110_ERR_PASSIVE_MASK (0x60) + +#define HI3110_STAT_RXFMTY BIT(1) +#define HI3110_STAT_BUSOFF BIT(2) +#define HI3110_STAT_ERRP BIT(3) +#define HI3110_STAT_ERRW BIT(4) + +#define HI3110_BTR0_SJW_SHIFT 6 +#define HI3110_BTR0_BRP_SHIFT 0 + +#define HI3110_BTR1_SAMP_3PERBIT (1 << 7) +#define HI3110_BTR1_SAMP_1PERBIT (0 << 7) +#define HI3110_BTR1_TSEG2_SHIFT 4 +#define HI3110_BTR1_TSEG1_SHIFT 0 + +#define HI3110_FIFO_WOTIME_TAG_OFF 0 +#define HI3110_FIFO_WOTIME_ID_OFF 1 +#define HI3110_FIFO_WOTIME_DLC_OFF 5 +#define HI3110_FIFO_WOTIME_DAT_OFF 6 + +#define HI3110_FIFO_WOTIME_TAG_IDE BIT(7) +#define HI3110_FIFO_WOTIME_ID_RTR BIT(0) + +#define HI3110_FIFO_TAG_OFF 0 +#define HI3110_FIFO_ID_OFF 1 +#define HI3110_FIFO_STD_DLC_OFF 3 +#define HI3110_FIFO_STD_DATA_OFF 4 +#define HI3110_FIFO_EXT_DLC_OFF 5 +#define HI3110_FIFO_EXT_DATA_OFF 6 + +#define HI3110_CAN_MAX_DATA_LEN 8 +#define HI3110_RX_BUF_LEN 15 +#define HI3110_TX_STD_BUF_LEN 12 +#define HI3110_TX_EXT_BUF_LEN 14 +#define HI3110_CAN_FRAME_MAX_BITS 128 +#define HI3110_EFF_FLAGS 0x18 /* IDE + SRR */ + +#define HI3110_TX_ECHO_SKB_MAX 1 + +#define HI3110_OST_DELAY_MS (10) + +#define DEVICE_NAME "hi3110" + +static int hi3110_enable_dma = 1; /* Enable SPI DMA. Default: 1 (On) */ +module_param(hi3110_enable_dma, int, 0444); +MODULE_PARM_DESC(hi3110_enable_dma, "Enable SPI DMA. Default: 1 (On)"); + +static const struct can_bittiming_const hi3110_bittiming_const = { + .name = DEVICE_NAME, + .tseg1_min = 2, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +enum hi3110_model { + CAN_HI3110_HI3110 = 0x3110, +}; + +struct hi3110_priv { + struct can_priv can; + struct net_device *net; + struct spi_device *spi; + enum hi3110_model model; + + struct mutex hi3110_lock; /* SPI device lock */ + + u8 *spi_tx_buf; + u8 *spi_rx_buf; + dma_addr_t spi_tx_dma; + dma_addr_t spi_rx_dma; + + struct sk_buff *tx_skb; + int tx_len; + + struct workqueue_struct *wq; + struct work_struct tx_work; + struct work_struct restart_work; + + int force_quit; + int after_suspend; +#define HI3110_AFTER_SUSPEND_UP 1 +#define HI3110_AFTER_SUSPEND_DOWN 2 +#define HI3110_AFTER_SUSPEND_POWER 4 +#define HI3110_AFTER_SUSPEND_RESTART 8 + int restart_tx; + struct regulator *power; + struct regulator *transceiver; + struct clk *clk; +}; + +static void hi3110_clean(struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + + if (priv->tx_skb || priv->tx_len) + net->stats.tx_errors++; + if (priv->tx_skb) + dev_kfree_skb(priv->tx_skb); + if (priv->tx_len) + can_free_echo_skb(priv->net, 0); + priv->tx_skb = NULL; + priv->tx_len = 0; +} + +/* Note about handling of error return of hi3110_spi_trans: accessing + * registers via SPI is not really different conceptually than using + * normal I/O assembler instructions, although it's much more + * complicated from a practical POV. So it's not advisable to always + * check the return value of this function. Imagine that every + * read{b,l}, write{b,l} and friends would be bracketed in "if ( < 0) + * error();", it would be a great mess (well there are some situation + * when exception handling C++ like could be useful after all). So we + * just check that transfers are OK at the beginning of our + * conversation with the chip and to avoid doing really nasty things + * (like injecting bogus packets in the network stack). + */ +static int hi3110_spi_trans(struct spi_device *spi, int len) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + struct spi_transfer t = { + .tx_buf = priv->spi_tx_buf, + .rx_buf = priv->spi_rx_buf, + .len = len, + .cs_change = 0, + }; + struct spi_message m; + int ret; + + spi_message_init(&m); + + if (hi3110_enable_dma) { + t.tx_dma = priv->spi_tx_dma; + t.rx_dma = priv->spi_rx_dma; + m.is_dma_mapped = 1; + } + + spi_message_add_tail(&t, &m); + + ret = spi_sync(spi, &m); + + if (ret) + dev_err(&spi->dev, "spi transfer failed: ret = %d\n", ret); + return ret; +} + +static u8 hi3110_cmd(struct spi_device *spi, u8 command) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = command; + dev_dbg(&spi->dev, "hi3110_cmd: %02X\n", command); + + return hi3110_spi_trans(spi, 1); +} + +static u8 hi3110_read(struct spi_device *spi, u8 command) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + u8 val = 0; + + priv->spi_tx_buf[0] = command; + hi3110_spi_trans(spi, 2); + val = priv->spi_rx_buf[1]; + + return val; +} + +static void hi3110_write(struct spi_device *spi, u8 reg, u8 val) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = reg; + priv->spi_tx_buf[1] = val; + hi3110_spi_trans(spi, 2); +} + +static void hi3110_hw_tx_frame(struct spi_device *spi, u8 *buf, int len) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = HI3110_WRITE_FIFO; + memcpy(priv->spi_tx_buf + 1, buf, len); + hi3110_spi_trans(spi, len + 1); +} + +static void hi3110_hw_tx(struct spi_device *spi, struct can_frame *frame) +{ + u8 buf[HI3110_TX_EXT_BUF_LEN]; + + buf[HI3110_FIFO_TAG_OFF] = 0; + + if (frame->can_id & CAN_EFF_FLAG) { + /* Extended frame */ + buf[HI3110_FIFO_ID_OFF] = (frame->can_id & CAN_EFF_MASK) >> 21; + buf[HI3110_FIFO_ID_OFF + 1] = + (((frame->can_id & CAN_EFF_MASK) >> 13) & 0xe0) | + HI3110_EFF_FLAGS | + (((frame->can_id & CAN_EFF_MASK) >> 15) & 0x07); + buf[HI3110_FIFO_ID_OFF + 2] = + (frame->can_id & CAN_EFF_MASK) >> 7; + buf[HI3110_FIFO_ID_OFF + 3] = + ((frame->can_id & CAN_EFF_MASK) << 1) | + ((frame->can_id & CAN_RTR_FLAG) ? 1 : 0); + + buf[HI3110_FIFO_EXT_DLC_OFF] = frame->can_dlc; + + memcpy(buf + HI3110_FIFO_EXT_DATA_OFF, + frame->data, frame->can_dlc); + + hi3110_hw_tx_frame(spi, buf, HI3110_TX_EXT_BUF_LEN - + (HI3110_CAN_MAX_DATA_LEN - frame->can_dlc)); + } else { + /* Standard frame */ + buf[HI3110_FIFO_ID_OFF] = (frame->can_id & CAN_SFF_MASK) >> 3; + buf[HI3110_FIFO_ID_OFF + 1] = + ((frame->can_id & CAN_SFF_MASK) << 5) | + ((frame->can_id & CAN_RTR_FLAG) ? (1 << 4) : 0); + + buf[HI3110_FIFO_STD_DLC_OFF] = frame->can_dlc; + + memcpy(buf + HI3110_FIFO_STD_DATA_OFF, + frame->data, frame->can_dlc); + + hi3110_hw_tx_frame(spi, buf, HI3110_TX_STD_BUF_LEN - + (HI3110_CAN_MAX_DATA_LEN - frame->can_dlc)); + } +} + +static void hi3110_hw_rx_frame(struct spi_device *spi, u8 *buf) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = HI3110_READ_FIFO_WOTIME; + hi3110_spi_trans(spi, HI3110_RX_BUF_LEN); + memcpy(buf, priv->spi_rx_buf + 1, HI3110_RX_BUF_LEN - 1); +} + +static void hi3110_hw_rx(struct spi_device *spi) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + struct sk_buff *skb; + struct can_frame *frame; + u8 buf[HI3110_RX_BUF_LEN - 1]; + + skb = alloc_can_skb(priv->net, &frame); + if (!skb) { + priv->net->stats.rx_dropped++; + return; + } + + hi3110_hw_rx_frame(spi, buf); + if (buf[HI3110_FIFO_WOTIME_TAG_OFF] & HI3110_FIFO_WOTIME_TAG_IDE) { + /* IDE is recessive (1), indicating extended 29-bit frame */ + frame->can_id = CAN_EFF_FLAG; + frame->can_id |= + (buf[HI3110_FIFO_WOTIME_ID_OFF] << 21) | + (((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5) << 18) | + ((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0x07) << 15) | + (buf[HI3110_FIFO_WOTIME_ID_OFF + 2] << 7) | + (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] >> 1); + } else { + /* IDE is dominant (0), frame indicating standard 11-bit */ + frame->can_id = + (buf[HI3110_FIFO_WOTIME_ID_OFF] << 3) | + ((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5); + } + + /* Data length */ + frame->can_dlc = get_can_dlc(buf[HI3110_FIFO_WOTIME_DLC_OFF] & 0x0F); + + if (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] & HI3110_FIFO_WOTIME_ID_RTR) + frame->can_id |= CAN_RTR_FLAG; + else + memcpy(frame->data, buf + HI3110_FIFO_WOTIME_DAT_OFF, + frame->can_dlc); + + priv->net->stats.rx_packets++; + priv->net->stats.rx_bytes += frame->can_dlc; + + can_led_event(priv->net, CAN_LED_EVENT_RX); + + netif_rx_ni(skb); +} + +static void hi3110_hw_sleep(struct spi_device *spi) +{ + hi3110_write(spi, HI3110_WRITE_CTRL0, HI3110_CTRL0_SLEEP_MODE); +} + +static netdev_tx_t hi3110_hard_start_xmit(struct sk_buff *skb, + struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + + if (priv->tx_skb || priv->tx_len) { + dev_err(&spi->dev, "hard_xmit called while tx busy\n"); + return NETDEV_TX_BUSY; + } + + if (can_dropped_invalid_skb(net, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(net); + priv->tx_skb = skb; + queue_work(priv->wq, &priv->tx_work); + + return NETDEV_TX_OK; +} + +static int hi3110_do_set_mode(struct net_device *net, enum can_mode mode) +{ + struct hi3110_priv *priv = netdev_priv(net); + + switch (mode) { + case CAN_MODE_START: + hi3110_clean(net); + /* We have to delay work since SPI I/O may sleep */ + priv->can.state = CAN_STATE_ERROR_ACTIVE; + priv->restart_tx = 1; + if (priv->can.restart_ms == 0) + priv->after_suspend = HI3110_AFTER_SUSPEND_RESTART; + queue_work(priv->wq, &priv->restart_work); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int hi3110_get_berr_counter(const struct net_device *net, + struct can_berr_counter *bec) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + + bec->txerr = hi3110_read(spi, HI3110_READ_TEC); + bec->rxerr = hi3110_read(spi, HI3110_READ_REC); + + return 0; +} + +static int hi3110_set_normal_mode(struct spi_device *spi) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + u8 reg = 0; + + hi3110_write(spi, HI3110_WRITE_INTE, HI3110_INT_BUSERR | + HI3110_INT_RXFIFO | HI3110_INT_TXCPLT); + + /* Enable TX */ + hi3110_write(spi, HI3110_WRITE_CTRL1, HI3110_CTRL1_TXEN); + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + reg = HI3110_CTRL0_LOOPBACK_MODE; + else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + reg = HI3110_CTRL0_MONITOR_MODE; + else + reg = HI3110_CTRL0_NORMAL_MODE; + + hi3110_write(spi, HI3110_WRITE_CTRL0, reg); + + /* Wait for the device to enter the mode */ + mdelay(HI3110_OST_DELAY_MS); + reg = hi3110_read(spi, HI3110_READ_CTRL0); + if ((reg & HI3110_CTRL0_MODE_MASK) != reg) + return -EBUSY; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + return 0; +} + +static int hi3110_do_set_bittiming(struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct can_bittiming *bt = &priv->can.bittiming; + struct spi_device *spi = priv->spi; + + hi3110_write(spi, HI3110_WRITE_BTR0, + ((bt->sjw - 1) << HI3110_BTR0_SJW_SHIFT) | + ((bt->brp - 1) << HI3110_BTR0_BRP_SHIFT)); + + hi3110_write(spi, HI3110_WRITE_BTR1, + (priv->can.ctrlmode & + CAN_CTRLMODE_3_SAMPLES ? + HI3110_BTR1_SAMP_3PERBIT : HI3110_BTR1_SAMP_1PERBIT) | + ((bt->phase_seg1 + bt->prop_seg - 1) + << HI3110_BTR1_TSEG1_SHIFT) | + ((bt->phase_seg2 - 1) << HI3110_BTR1_TSEG2_SHIFT)); + + dev_dbg(&spi->dev, "BT: 0x%02x 0x%02x\n", + hi3110_read(spi, HI3110_READ_BTR0), + hi3110_read(spi, HI3110_READ_BTR1)); + + return 0; +} + +static int hi3110_setup(struct net_device *net) +{ + hi3110_do_set_bittiming(net); + return 0; +} + +static int hi3110_hw_reset(struct spi_device *spi) +{ + u8 reg; + int ret; + + /* Wait for oscillator startup timer after power up */ + mdelay(HI3110_OST_DELAY_MS); + + ret = hi3110_cmd(spi, HI3110_MASTER_RESET); + if (ret) + return ret; + + /* Wait for oscillator startup timer after reset */ + mdelay(HI3110_OST_DELAY_MS); + + reg = hi3110_read(spi, HI3110_READ_CTRL0); + if ((reg & HI3110_CTRL0_MODE_MASK) != HI3110_CTRL0_INIT_MODE) + return -ENODEV; + + /* As per the datasheet it appears the error flags are + * not cleared on reset. Explicitly clear them by performing a read + */ + hi3110_read(spi, HI3110_READ_ERR); + + return 0; +} + +static int hi3110_hw_probe(struct spi_device *spi) +{ + u8 statf; + + hi3110_hw_reset(spi); + + /* Confirm correct operation by checking against reset values + * in datasheet + */ + statf = hi3110_read(spi, HI3110_READ_STATF); + + dev_dbg(&spi->dev, "statf: %02X\n", statf); + + if (statf != 0x82) + return -ENODEV; + + return 0; +} + +static int hi3110_power_enable(struct regulator *reg, int enable) +{ + if (IS_ERR_OR_NULL(reg)) + return 0; + + if (enable) + return regulator_enable(reg); + else + return regulator_disable(reg); +} + +static int hi3110_stop(struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + + close_candev(net); + + priv->force_quit = 1; + free_irq(spi->irq, priv); + destroy_workqueue(priv->wq); + priv->wq = NULL; + + mutex_lock(&priv->hi3110_lock); + + /* Disable transmit, interrupts and clear flags */ + hi3110_write(spi, HI3110_WRITE_CTRL1, 0x0); + hi3110_write(spi, HI3110_WRITE_INTE, 0x0); + hi3110_read(spi, HI3110_READ_INTF); + + hi3110_clean(net); + + hi3110_hw_sleep(spi); + + hi3110_power_enable(priv->transceiver, 0); + + priv->can.state = CAN_STATE_STOPPED; + + mutex_unlock(&priv->hi3110_lock); + + can_led_event(net, CAN_LED_EVENT_STOP); + + return 0; +} + +static void hi3110_tx_work_handler(struct work_struct *ws) +{ + struct hi3110_priv *priv = container_of(ws, struct hi3110_priv, + tx_work); + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + struct can_frame *frame; + + mutex_lock(&priv->hi3110_lock); + if (priv->tx_skb) { + if (priv->can.state == CAN_STATE_BUS_OFF) { + hi3110_clean(net); + } else { + frame = (struct can_frame *)priv->tx_skb->data; + hi3110_hw_tx(spi, frame); + priv->tx_len = 1 + frame->can_dlc; + can_put_echo_skb(priv->tx_skb, net, 0); + priv->tx_skb = NULL; + } + } + mutex_unlock(&priv->hi3110_lock); +} + +static void hi3110_restart_work_handler(struct work_struct *ws) +{ + struct hi3110_priv *priv = container_of(ws, struct hi3110_priv, + restart_work); + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + + mutex_lock(&priv->hi3110_lock); + if (priv->after_suspend) { + hi3110_hw_reset(spi); + hi3110_setup(net); + if (priv->after_suspend & HI3110_AFTER_SUSPEND_RESTART) { + hi3110_set_normal_mode(spi); + } else if (priv->after_suspend & HI3110_AFTER_SUSPEND_UP) { + netif_device_attach(net); + hi3110_clean(net); + hi3110_set_normal_mode(spi); + netif_wake_queue(net); + } else { + hi3110_hw_sleep(spi); + } + priv->after_suspend = 0; + priv->force_quit = 0; + } + + if (priv->restart_tx) { + priv->restart_tx = 0; + hi3110_hw_reset(spi); + hi3110_setup(net); + hi3110_clean(net); + hi3110_set_normal_mode(spi); + netif_wake_queue(net); + } + mutex_unlock(&priv->hi3110_lock); +} + +static irqreturn_t hi3110_can_ist(int irq, void *dev_id) +{ + struct hi3110_priv *priv = dev_id; + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + + mutex_lock(&priv->hi3110_lock); + + while (!priv->force_quit) { + enum can_state new_state; + u8 intf, eflag, statf; + + while (!(HI3110_STAT_RXFMTY & + (statf = hi3110_read(spi, HI3110_READ_STATF)))) { + hi3110_hw_rx(spi); + } + + intf = hi3110_read(spi, HI3110_READ_INTF); + eflag = hi3110_read(spi, HI3110_READ_ERR); + /* Update can state */ + if (eflag & HI3110_ERR_BUSOFF) + new_state = CAN_STATE_BUS_OFF; + else if (eflag & HI3110_ERR_PASSIVE_MASK) + new_state = CAN_STATE_ERROR_PASSIVE; + else if (statf & HI3110_STAT_ERRW) + new_state = CAN_STATE_ERROR_WARNING; + else + new_state = CAN_STATE_ERROR_ACTIVE; + + if (new_state != priv->can.state) { + struct can_frame *cf; + struct sk_buff *skb; + enum can_state rx_state, tx_state; + u8 rxerr, txerr; + + skb = alloc_can_err_skb(net, &cf); + if (!skb) + break; + + txerr = hi3110_read(spi, HI3110_READ_TEC); + rxerr = hi3110_read(spi, HI3110_READ_REC); + cf->data[6] = txerr; + cf->data[7] = rxerr; + tx_state = txerr >= rxerr ? new_state : 0; + rx_state = txerr <= rxerr ? new_state : 0; + can_change_state(net, cf, tx_state, rx_state); + netif_rx_ni(skb); + + if (new_state == CAN_STATE_BUS_OFF) { + can_bus_off(net); + if (priv->can.restart_ms == 0) { + priv->force_quit = 1; + hi3110_hw_sleep(spi); + break; + } + } + } + + /* Update bus errors */ + if ((intf & HI3110_INT_BUSERR) && + (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) { + struct can_frame *cf; + struct sk_buff *skb; + + /* Check for protocol errors */ + if (eflag & HI3110_ERR_PROTOCOL_MASK) { + skb = alloc_can_err_skb(net, &cf); + if (!skb) + break; + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + priv->can.can_stats.bus_error++; + priv->net->stats.rx_errors++; + if (eflag & HI3110_ERR_BITERR) + cf->data[2] |= CAN_ERR_PROT_BIT; + else if (eflag & HI3110_ERR_FRMERR) + cf->data[2] |= CAN_ERR_PROT_FORM; + else if (eflag & HI3110_ERR_STUFERR) + cf->data[2] |= CAN_ERR_PROT_STUFF; + else if (eflag & HI3110_ERR_CRCERR) + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + else if (eflag & HI3110_ERR_ACKERR) + cf->data[3] |= CAN_ERR_PROT_LOC_ACK; + + cf->data[6] = hi3110_read(spi, HI3110_READ_TEC); + cf->data[7] = hi3110_read(spi, HI3110_READ_REC); + netdev_dbg(priv->net, "Bus Error\n"); + netif_rx_ni(skb); + } + } + + if (intf == 0) + break; + + if (intf & HI3110_INT_TXCPLT) { + net->stats.tx_packets++; + net->stats.tx_bytes += priv->tx_len - 1; + can_led_event(net, CAN_LED_EVENT_TX); + if (priv->tx_len) { + can_get_echo_skb(net, 0); + priv->tx_len = 0; + } + netif_wake_queue(net); + } + } + mutex_unlock(&priv->hi3110_lock); + return IRQ_HANDLED; +} + +static int hi3110_open(struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_RISING; + int ret; + + ret = open_candev(net); + if (ret) + return ret; + + mutex_lock(&priv->hi3110_lock); + hi3110_power_enable(priv->transceiver, 1); + + priv->force_quit = 0; + priv->tx_skb = NULL; + priv->tx_len = 0; + + ret = request_threaded_irq(spi->irq, NULL, hi3110_can_ist, + flags, DEVICE_NAME, priv); + if (ret) { + dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); + goto out_close; + } + + priv->wq = alloc_workqueue("hi3110_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM, + 0); + if (!priv->wq) { + ret = -ENOMEM; + goto out_free_irq; + } + INIT_WORK(&priv->tx_work, hi3110_tx_work_handler); + INIT_WORK(&priv->restart_work, hi3110_restart_work_handler); + + ret = hi3110_hw_reset(spi); + if (ret) + goto out_free_wq; + + ret = hi3110_setup(net); + if (ret) + goto out_free_wq; + + ret = hi3110_set_normal_mode(spi); + if (ret) + goto out_free_wq; + + can_led_event(net, CAN_LED_EVENT_OPEN); + netif_wake_queue(net); + mutex_unlock(&priv->hi3110_lock); + + return 0; + + out_free_wq: + destroy_workqueue(priv->wq); + out_free_irq: + free_irq(spi->irq, priv); + hi3110_hw_sleep(spi); + out_close: + hi3110_power_enable(priv->transceiver, 0); + close_candev(net); + mutex_unlock(&priv->hi3110_lock); + return ret; +} + +static const struct net_device_ops hi3110_netdev_ops = { + .ndo_open = hi3110_open, + .ndo_stop = hi3110_stop, + .ndo_start_xmit = hi3110_hard_start_xmit, +}; + +static const struct of_device_id hi3110_of_match[] = { + { + .compatible = "holt,hi3110", + .data = (void *)CAN_HI3110_HI3110, + }, + { } +}; +MODULE_DEVICE_TABLE(of, hi3110_of_match); + +static const struct spi_device_id hi3110_id_table[] = { + { + .name = "hi3110", + .driver_data = (kernel_ulong_t)CAN_HI3110_HI3110, + }, + { } +}; +MODULE_DEVICE_TABLE(spi, hi3110_id_table); + +static int hi3110_can_probe(struct spi_device *spi) +{ + const struct of_device_id *of_id = of_match_device(hi3110_of_match, + &spi->dev); + struct net_device *net; + struct hi3110_priv *priv; + struct clk *clk; + int freq, ret; + + clk = devm_clk_get(&spi->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&spi->dev, "no CAN clock source defined\n"); + return PTR_ERR(clk); + } + freq = clk_get_rate(clk); + + /* Sanity check */ + if (freq > 40000000) + return -ERANGE; + + /* Allocate can/net device */ + net = alloc_candev(sizeof(struct hi3110_priv), HI3110_TX_ECHO_SKB_MAX); + if (!net) + return -ENOMEM; + + if (!IS_ERR(clk)) { + ret = clk_prepare_enable(clk); + if (ret) + goto out_free; + } + + net->netdev_ops = &hi3110_netdev_ops; + net->flags |= IFF_ECHO; + + priv = netdev_priv(net); + priv->can.bittiming_const = &hi3110_bittiming_const; + priv->can.do_set_mode = hi3110_do_set_mode; + priv->can.do_get_berr_counter = hi3110_get_berr_counter; + priv->can.clock.freq = freq / 2; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_BERR_REPORTING; + + if (of_id) + priv->model = (enum hi3110_model)of_id->data; + else + priv->model = spi_get_device_id(spi)->driver_data; + priv->net = net; + priv->clk = clk; + + spi_set_drvdata(spi, priv); + + /* Configure the SPI bus */ + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret) + goto out_clk; + + priv->power = devm_regulator_get_optional(&spi->dev, "vdd"); + priv->transceiver = devm_regulator_get_optional(&spi->dev, "xceiver"); + if ((PTR_ERR(priv->power) == -EPROBE_DEFER) || + (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) { + ret = -EPROBE_DEFER; + goto out_clk; + } + + ret = hi3110_power_enable(priv->power, 1); + if (ret) + goto out_clk; + + priv->spi = spi; + mutex_init(&priv->hi3110_lock); + + /* If requested, allocate DMA buffers */ + if (hi3110_enable_dma) { + spi->dev.coherent_dma_mask = ~0; + + /* Minimum coherent DMA allocation is PAGE_SIZE, so allocate + * that much and share it between Tx and Rx DMA buffers. + */ + priv->spi_tx_buf = dmam_alloc_coherent(&spi->dev, + PAGE_SIZE, + &priv->spi_tx_dma, + GFP_DMA); + + if (priv->spi_tx_buf) { + priv->spi_rx_buf = (priv->spi_tx_buf + (PAGE_SIZE / 2)); + priv->spi_rx_dma = (dma_addr_t)(priv->spi_tx_dma + + (PAGE_SIZE / 2)); + } else { + /* Fall back to non-DMA */ + hi3110_enable_dma = 0; + } + } + + /* Allocate non-DMA buffers */ + if (!hi3110_enable_dma) { + priv->spi_tx_buf = devm_kzalloc(&spi->dev, HI3110_RX_BUF_LEN, + GFP_KERNEL); + if (!priv->spi_tx_buf) { + ret = -ENOMEM; + goto error_probe; + } + priv->spi_rx_buf = devm_kzalloc(&spi->dev, HI3110_RX_BUF_LEN, + GFP_KERNEL); + + if (!priv->spi_rx_buf) { + ret = -ENOMEM; + goto error_probe; + } + } + + SET_NETDEV_DEV(net, &spi->dev); + + ret = hi3110_hw_probe(spi); + if (ret) { + if (ret == -ENODEV) + dev_err(&spi->dev, "Cannot initialize %x. Wrong wiring?\n", + priv->model); + goto error_probe; + } + hi3110_hw_sleep(spi); + + ret = register_candev(net); + if (ret) + goto error_probe; + + devm_can_led_init(net); + netdev_info(net, "%x successfully initialized.\n", priv->model); + + return 0; + + error_probe: + hi3110_power_enable(priv->power, 0); + + out_clk: + if (!IS_ERR(clk)) + clk_disable_unprepare(clk); + + out_free: + free_candev(net); + + dev_err(&spi->dev, "Probe failed, err=%d\n", -ret); + return ret; +} + +static int hi3110_can_remove(struct spi_device *spi) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + struct net_device *net = priv->net; + + unregister_candev(net); + + hi3110_power_enable(priv->power, 0); + + if (!IS_ERR(priv->clk)) + clk_disable_unprepare(priv->clk); + + free_candev(net); + + return 0; +} + +static int __maybe_unused hi3110_can_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct hi3110_priv *priv = spi_get_drvdata(spi); + struct net_device *net = priv->net; + + priv->force_quit = 1; + disable_irq(spi->irq); + + /* Note: at this point neither IST nor workqueues are running. + * open/stop cannot be called anyway so locking is not needed + */ + if (netif_running(net)) { + netif_device_detach(net); + + hi3110_hw_sleep(spi); + hi3110_power_enable(priv->transceiver, 0); + priv->after_suspend = HI3110_AFTER_SUSPEND_UP; + } else { + priv->after_suspend = HI3110_AFTER_SUSPEND_DOWN; + } + + if (!IS_ERR_OR_NULL(priv->power)) { + regulator_disable(priv->power); + priv->after_suspend |= HI3110_AFTER_SUSPEND_POWER; + } + + return 0; +} + +static int __maybe_unused hi3110_can_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct hi3110_priv *priv = spi_get_drvdata(spi); + + if (priv->after_suspend & HI3110_AFTER_SUSPEND_POWER) + hi3110_power_enable(priv->power, 1); + + if (priv->after_suspend & HI3110_AFTER_SUSPEND_UP) { + hi3110_power_enable(priv->transceiver, 1); + queue_work(priv->wq, &priv->restart_work); + } else { + priv->after_suspend = 0; + } + + priv->force_quit = 0; + enable_irq(spi->irq); + return 0; +} + +static SIMPLE_DEV_PM_OPS(hi3110_can_pm_ops, hi3110_can_suspend, hi3110_can_resume); + +static struct spi_driver hi3110_can_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = hi3110_of_match, + .pm = &hi3110_can_pm_ops, + }, + .id_table = hi3110_id_table, + .probe = hi3110_can_probe, + .remove = hi3110_can_remove, +}; + +module_spi_driver(hi3110_can_driver); + +MODULE_AUTHOR("Akshay Bhat "); +MODULE_AUTHOR("Casey Fitzpatrick "); +MODULE_DESCRIPTION("Holt HI-3110 CAN driver"); +MODULE_LICENSE("GPL v2"); -- cgit From a7595a820b07db9ac0d8f479ff62002bdd32a05a Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Mon, 20 Mar 2017 20:52:46 +0530 Subject: ath10k: fix NAPI enable/disable symmetry for AHB interface Move NAPI enable to 'ath10k_ahb_hif_start' from 'ath10k_ahb_hif_power_up'. This is to maintain the symmetry of calling napi_enable() from ath10k_ahb_hif_start() so that it matches with napi_disable() being called from ath10k_pci_hif_stop(). This change is based on the crash fix from Kalle for PCI interface in commit 1427228d5869 ("ath10k: fix napi crash during rmmod when probe firmware fails"). Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ahb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c index 45226dbee5ce..da770af83036 100644 --- a/drivers/net/wireless/ath/ath10k/ahb.c +++ b/drivers/net/wireless/ath/ath10k/ahb.c @@ -640,6 +640,7 @@ static int ath10k_ahb_hif_start(struct ath10k *ar) { ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot ahb hif start\n"); + napi_enable(&ar->napi); ath10k_ce_enable_interrupts(ar); ath10k_pci_enable_legacy_irq(ar); @@ -692,7 +693,6 @@ static int ath10k_ahb_hif_power_up(struct ath10k *ar) ath10k_err(ar, "could not wake up target CPU: %d\n", ret); goto err_ce_deinit; } - napi_enable(&ar->napi); return 0; -- cgit From ebeb36670ecac36c179b5fb5d5c88ff03ba191ec Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 13 Mar 2017 13:44:20 +0100 Subject: ath9k_htc: fix NULL-deref at probe Make sure to check the number of endpoints to avoid dereferencing a NULL-pointer or accessing memory beyond the endpoint array should a malicious device lack the expected endpoints. Fixes: 36bcce430657 ("ath9k_htc: Handle storage devices") Cc: stable # 2.6.39+ Signed-off-by: Johan Hovold Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/hif_usb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 05dd056cab6e..12aa8abbcba4 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1220,6 +1220,9 @@ static int send_eject_command(struct usb_interface *interface) u8 bulk_out_ep; int r; + if (iface_desc->desc.bNumEndpoints < 2) + return -ENODEV; + /* Find bulk out endpoint */ for (r = 1; r >= 0; r--) { endpoint = &iface_desc->endpoint[r].desc; -- cgit From fefcd11535abc4cb45d1572afc2351114572493e Mon Sep 17 00:00:00 2001 From: Venkateswara Rao Naralasetty Date: Fri, 24 Mar 2017 13:27:28 +0530 Subject: ath10k: fix station nss computation If station advertises diffferent NSS capabilities in Rx_mcs set of HT and VHT IEs in assoc req, the current NSS computation logic configures the NSS support only based on Rx_mcs set of HT capabilities in the driver. This is configuring the station NSS capabilities incorreclty in the target. For example, if station advertise Rx_mcs set as 2 spatial streams in HT capabilities and 1 spatial streams in VHT capabilities in assoc request, as per current logic we are calculating nss from HT capabilities and the driver sets peer_num_spatial_streams as 2 for the station which is configured in VHT 1*1. This patchs fix this issue by calculating the nss from VHT cap if station supports vht. Signed-off-by: Venkateswara Rao Naralasetty Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 968b1d421225..3ca713e09ce9 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2451,6 +2451,8 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, enum nl80211_band band; const u16 *vht_mcs_mask; u8 ampdu_factor; + u8 max_nss, vht_mcs; + int i; if (WARN_ON(ath10k_mac_vif_chan(vif, &def))) return; @@ -2489,6 +2491,18 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, if (sta->bandwidth == IEEE80211_STA_RX_BW_160) arg->peer_flags |= ar->wmi.peer_flags->bw160; + /* Calculate peer NSS capability from VHT capabilities if STA + * supports VHT. + */ + for (i = 0, max_nss = 0, vht_mcs = 0; i < NL80211_VHT_NSS_MAX; i++) { + vht_mcs = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) >> + (2 * i) & 3; + + if ((vht_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) && + vht_mcs_mask[i]) + max_nss = i + 1; + } + arg->peer_num_spatial_streams = min(sta->rx_nss, max_nss); arg->peer_vht_rates.rx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.rx_highest); arg->peer_vht_rates.rx_mcs_set = -- cgit From d94475c2f95c8fbc6871aaf2df3fd093c329dde8 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Fri, 31 Mar 2017 17:28:41 +0530 Subject: ath10k: cancel coverage class work during stop and restart It seems set_coverage_class_work is not cancelled anywhere, though I could not find a crash/warning with this existing design, its safer to cancel it during stop() and also before restarting the hardware. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 7 +++++++ drivers/net/wireless/ath/ath10k/mac.c | 1 + 2 files changed, 8 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index f450ebbb28d5..85a14e2b3d04 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1627,6 +1627,13 @@ static void ath10k_core_restart(struct work_struct *work) wake_up(&ar->wmi.tx_credits_wq); wake_up(&ar->peer_mapping_wq); + /* TODO: We can have one instance of cancelling coverage_class_work by + * moving it to ath10k_halt(), so that both stop() and restart() would + * call that but it takes conf_mutex() and if we call cancel_work_sync() + * with conf_mutex it will deadlock. + */ + cancel_work_sync(&ar->set_coverage_class_work); + mutex_lock(&ar->conf_mutex); switch (ar->state) { diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 3ca713e09ce9..ddabec8022f4 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4717,6 +4717,7 @@ static void ath10k_stop(struct ieee80211_hw *hw) } mutex_unlock(&ar->conf_mutex); + cancel_work_sync(&ar->set_coverage_class_work); cancel_delayed_work_sync(&ar->scan.timeout); cancel_work_sync(&ar->restart_work); } -- cgit From 03e463a4197a05f196b1b9e9bb3b66fecbe50889 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Fri, 31 Mar 2017 17:29:26 +0530 Subject: ath10k: enable a HTC debug message during insufficient tx credits Add an ath10k HTC debug message when insufficient tx credits are available to send the WMI commands. This is very useful in debugging issues like 'tx credit starvation' that could possibly happen with multiclient setup with constant roaming Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 9f6a915f91bf..f56f60462b33 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -119,6 +119,9 @@ int ath10k_htc_send(struct ath10k_htc *htc, credits = DIV_ROUND_UP(skb->len, htc->target_credit_size); spin_lock_bh(&htc->tx_lock); if (ep->tx_credits < credits) { + ath10k_dbg(ar, ATH10K_DBG_HTC, + "htc insufficient credits ep %d required %d available %d\n", + eid, credits, ep->tx_credits); spin_unlock_bh(&htc->tx_lock); ret = -EAGAIN; goto err_pull; -- cgit From fb7fa766a8cf9dede60f71ba87241f37bd98cabe Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Fri, 31 Mar 2017 17:30:44 +0530 Subject: ath10k: remove obselete Copy Engine comments Remove obselete Copy Engine comments referring to the function ath10k_ce_sendlist_send as this function was removed long time back by the commit 2e761b5a5222 ("ath10k: remove ce_sendlist_send"). Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ce.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 4045657e0a6e..9ac0a73a3a9f 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -261,8 +261,7 @@ static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar, } /* - * Guts of ath10k_ce_send, used by both ath10k_ce_send and - * ath10k_ce_sendlist_send. + * Guts of ath10k_ce_send. * The caller takes responsibility for any needed locking. */ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, -- cgit From e871fb6396f7251ae4a90c20be38015c8b20e502 Mon Sep 17 00:00:00 2001 From: Maharaja Kennadyrajan Date: Fri, 31 Mar 2017 17:39:40 +0530 Subject: ath10k: fix the Transmit Power Control stats display format This patch helps to fix TPC stats to display the stats properly. Here cosmetic change has been done to print the TPC stats for all the cases 1.CDD 2.STBC 3.TXBF Signed-off-by: Maharaja Kennadyrajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 00b424d99126..1339cc383797 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1816,7 +1816,7 @@ static void ath10k_tpc_stats_fill(struct ath10k *ar, tpc_stats->num_tx_chain, tpc_stats->rate_max); - for (j = 0; j < tpc_stats->num_tx_chain ; j++) { + for (j = 0; j < WMI_TPC_FLAG; j++) { switch (j) { case WMI_TPC_TABLE_TYPE_CDD: if (tpc_stats->flag[j] == ATH10K_TPC_TABLE_TYPE_FLAG) { -- cgit From d6dfe25c8bb200027dfc5c793cbec81c9af6dd2e Mon Sep 17 00:00:00 2001 From: Marcin Rokicki Date: Mon, 20 Feb 2017 14:39:57 +0100 Subject: ath10k: fix block comments style Fix output from checkpatch.pl like: Block comments use a trailing */ on a separate lin Signed-off-by: Marcin Rokicki Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 9 ++++--- drivers/net/wireless/ath/ath10k/debug.c | 3 ++- drivers/net/wireless/ath/ath10k/htc.c | 3 ++- drivers/net/wireless/ath/ath10k/htt_rx.c | 9 ++++--- drivers/net/wireless/ath/ath10k/htt_tx.c | 6 +++-- drivers/net/wireless/ath/ath10k/mac.c | 43 +++++++++++++++++++------------ drivers/net/wireless/ath/ath10k/pci.c | 12 ++++++--- drivers/net/wireless/ath/ath10k/thermal.c | 3 ++- drivers/net/wireless/ath/ath10k/txrx.c | 3 ++- drivers/net/wireless/ath/ath10k/wmi.c | 16 +++++++----- 10 files changed, 69 insertions(+), 38 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 85a14e2b3d04..1b4c08b5eaa9 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1645,7 +1645,8 @@ static void ath10k_core_restart(struct work_struct *work) break; case ATH10K_STATE_OFF: /* this can happen if driver is being unloaded - * or if the crash happens during FW probing */ + * or if the crash happens during FW probing + */ ath10k_warn(ar, "cannot restart a device that hasn't been started\n"); break; case ATH10K_STATE_RESTARTING: @@ -2173,7 +2174,8 @@ EXPORT_SYMBOL(ath10k_core_stop); /* mac80211 manages fw/hw initialization through start/stop hooks. However in * order to know what hw capabilities should be advertised to mac80211 it is * necessary to load the firmware (and tear it down immediately since start - * hook will try to init it again) before registering */ + * hook will try to init it again) before registering + */ static int ath10k_core_probe_fw(struct ath10k *ar) { struct bmi_target_info target_info; @@ -2367,7 +2369,8 @@ void ath10k_core_unregister(struct ath10k *ar) /* We must unregister from mac80211 before we stop HTC and HIF. * Otherwise we will fail to submit commands to FW and mac80211 will be - * unhappy about callback failures. */ + * unhappy about callback failures. + */ ath10k_mac_unregister(ar); ath10k_testmode_destroy(ar); diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 1339cc383797..8cda518d1150 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1982,7 +1982,8 @@ void ath10k_debug_stop(struct ath10k *ar) /* Must not use _sync to avoid deadlock, we do that in * ath10k_debug_destroy(). The check for htt_stats_mask is to avoid - * warning from del_timer(). */ + * warning from del_timer(). + */ if (ar->debug.htt_stats_mask != 0) cancel_delayed_work(&ar->debug.htt_stats_dwork); diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index f56f60462b33..b7669b2e94aa 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -422,7 +422,8 @@ static void ath10k_htc_control_rx_complete(struct ath10k *ar, struct sk_buff *skb) { /* This is unexpected. FW is not supposed to send regular rx on this - * endpoint. */ + * endpoint. + */ ath10k_warn(ar, "unexpected htc rx\n"); kfree_skb(skb); } diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 02a3fc81fbe3..3448a3ce5919 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -177,7 +177,8 @@ static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt) * automatically balances load wrt to CPU power. * * This probably comes at a cost of lower maximum throughput but - * improves the average and stability. */ + * improves the average and stability. + */ spin_lock_bh(&htt->rx_ring.lock); num_deficit = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt; num_to_fill = min(ATH10K_HTT_MAX_NUM_REFILL, num_deficit); @@ -304,7 +305,8 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, rx_desc = (struct htt_rx_desc *)msdu->data; /* FIXME: we must report msdu payload since this is what caller - * expects now */ + * expects now + */ skb_put(msdu, offsetof(struct htt_rx_desc, msdu_payload)); skb_pull(msdu, offsetof(struct htt_rx_desc, msdu_payload)); @@ -639,7 +641,8 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, case HTT_RX_VHT: case HTT_RX_VHT_WITH_TXBF: /* VHT-SIG-A1 in info2, VHT-SIG-A2 in info3 - TODO check this */ + * TODO check this + */ bw = info2 & 3; sgi = info3 & 1; group_id = (info2 >> 4) & 0x3F; diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 86b427f5e2bc..685faac1368f 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -526,7 +526,8 @@ int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie) memset(req, 0, sizeof(*req)); /* currently we support only max 8 bit masks so no need to worry - * about endian support */ + * about endian support + */ req->upload_types[0] = mask; req->reset_types[0] = mask; req->stat_type = HTT_STATS_REQ_CFG_STAT_TYPE_INVALID; @@ -1008,7 +1009,8 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode, * There is simply no point in pushing HTT TX_FRM through HTC tx path * as it's a waste of resources. By bypassing HTC it is possible to * avoid extra memory allocations, compress data structures and thus - * improve performance. */ + * improve performance. + */ txbuf->htc_hdr.eid = htt->eid; txbuf->htc_hdr.len = __cpu_to_le16(sizeof(txbuf->cmd_hdr) + diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index ddabec8022f4..9dc06832b7f1 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -457,7 +457,8 @@ static int ath10k_clear_vdev_key(struct ath10k_vif *arvif, for (;;) { /* since ath10k_install_key we can't hold data_lock all the - * time, so we try to remove the keys incrementally */ + * time, so we try to remove the keys incrementally + */ spin_lock_bh(&ar->data_lock); i = 0; list_for_each_entry(peer, &ar->peers, list) { @@ -609,7 +610,8 @@ static u8 ath10k_parse_mpdudensity(u8 mpdudensity) case 2: case 3: /* Our lower layer calculations limit our precision to - 1 microsecond */ + * 1 microsecond + */ return 1; case 4: return 2; @@ -978,7 +980,8 @@ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id) arg.channel.band_center_freq2 = chandef->center_freq2; /* TODO setup this dynamically, what in case we - don't have any vifs? */ + * don't have any vifs? + */ arg.channel.mode = chan_to_phymode(chandef); arg.channel.chan_radar = !!(channel->flags & IEEE80211_CHAN_RADAR); @@ -2373,9 +2376,10 @@ static int ath10k_peer_assoc_qos_ap(struct ath10k *ar, } /* TODO setup this based on STA listen interval and - beacon interval. Currently we don't know - sta->listen_interval - mac80211 patch required. - Currently use 10 seconds */ + * beacon interval. Currently we don't know + * sta->listen_interval - mac80211 patch required. + * Currently use 10 seconds + */ ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, sta->addr, WMI_AP_PS_PEER_PARAM_AGEOUT_TIME, 10); @@ -2480,7 +2484,8 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, /* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to * zero in VHT IE. Using it would result in degraded throughput. * arg->peer_max_mpdu at this point contains HT max_mpdu so keep - * it if VHT max_mpdu is smaller. */ + * it if VHT max_mpdu is smaller. + */ arg->peer_max_mpdu = max(arg->peer_max_mpdu, (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR + ampdu_factor)) - 1); @@ -2793,7 +2798,8 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, } /* ap_sta must be accessed only within rcu section which must be left - * before calling ath10k_setup_peer_smps() which might sleep. */ + * before calling ath10k_setup_peer_smps() which might sleep. + */ ht_cap = ap_sta->ht_cap; vht_cap = ap_sta->vht_cap; @@ -3064,7 +3070,8 @@ static int ath10k_update_channel_list(struct ath10k *ar) /* FIXME: why use only legacy modes, why not any * HT/VHT modes? Would that even make any - * difference? */ + * difference? + */ if (channel->band == NL80211_BAND_2GHZ) ch->mode = MODE_11G; else @@ -3128,7 +3135,8 @@ static void ath10k_regd_update(struct ath10k *ar) } /* Target allows setting up per-band regdomain but ath_common provides - * a combined one only */ + * a combined one only + */ ret = ath10k_wmi_pdev_set_regdomain(ar, regpair->reg_domain, regpair->reg_domain, /* 2ghz */ @@ -3677,7 +3685,8 @@ void ath10k_offchan_tx_work(struct work_struct *work) * never transmitted. We delete the peer upon tx completion. * It is unlikely that a peer for offchannel tx will already be * present. However it may be in some rare cases so account for that. - * Otherwise we might remove a legitimate peer and break stuff. */ + * Otherwise we might remove a legitimate peer and break stuff. + */ for (;;) { skb = skb_dequeue(&ar->offchan_tx_queue); @@ -5717,7 +5726,8 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, } /* the peer should not disappear in mid-way (unless FW goes awry) since - * we already hold conf_mutex. we just make sure its there now. */ + * we already hold conf_mutex. we just make sure its there now. + */ spin_lock_bh(&ar->data_lock); peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr); spin_unlock_bh(&ar->data_lock); @@ -5729,8 +5739,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ret = -EOPNOTSUPP; goto exit; } else { - /* if the peer doesn't exist there is no key to disable - * anymore */ + /* if the peer doesn't exist there is no key to disable anymore */ goto exit; } } @@ -6589,7 +6598,8 @@ static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, long time_left; /* mac80211 doesn't care if we really xmit queued frames or not - * we'll collect those frames either way if we stop/delete vdevs */ + * we'll collect those frames either way if we stop/delete vdevs + */ if (drop) return; @@ -6640,7 +6650,8 @@ static void ath10k_reconfig_complete(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); /* If device failed to restart it will be in a different state, e.g. - * ATH10K_STATE_WEDGED */ + * ATH10K_STATE_WEDGED + */ if (ar->state == ATH10K_STATE_RESTARTED) { ath10k_info(ar, "device successfully recovered\n"); ar->state = ATH10K_STATE_ON; diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 52896c20ca4e..b20b66d9d7bc 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -720,14 +720,16 @@ void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar) { /* IMPORTANT: INTR_CLR register has to be set after * INTR_ENABLE is set to 0, otherwise interrupt can not be - * really cleared. */ + * really cleared. + */ ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, 0); ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_CLR_ADDRESS, PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); /* IMPORTANT: this extra read transaction is required to - * flush the posted write buffer. */ + * flush the posted write buffer. + */ (void)ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS); } @@ -739,7 +741,8 @@ void ath10k_pci_enable_legacy_irq(struct ath10k *ar) PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); /* IMPORTANT: this extra read transaction is required to - * flush the posted write buffer. */ + * flush the posted write buffer. + */ (void)ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS); } @@ -2908,7 +2911,8 @@ static int ath10k_pci_init_irq(struct ath10k *ar) * host won't know when target writes BAR to CORE_CTRL. * This write might get lost if target has NOT written BAR. * For now, fix the race by repeating the write in below - * synchronization checking. */ + * synchronization checking. + */ ar_pci->oper_irq_mode = ATH10K_PCI_IRQ_LEGACY; ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c index 0a47269be289..f719d7d43cb0 100644 --- a/drivers/net/wireless/ath/ath10k/thermal.c +++ b/drivers/net/wireless/ath/ath10k/thermal.c @@ -191,7 +191,8 @@ int ath10k_thermal_register(struct ath10k *ar) return 0; /* Avoid linking error on devm_hwmon_device_register_with_groups, I - * guess linux/hwmon.h is missing proper stubs. */ + * guess linux/hwmon.h is missing proper stubs. + */ if (!IS_REACHABLE(CONFIG_HWMON)) return 0; diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 9852c5d51139..d4986f626c35 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -34,7 +34,8 @@ static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb) /* If the original wait_for_completion() timed out before * {data,mgmt}_tx_completed() was called then we could complete * offchan_tx_completed for a different skb. Prevent this by using - * offchan_tx_skb. */ + * offchan_tx_skb. + */ spin_lock_bh(&ar->data_lock); if (ar->offchan_tx_skb != skb) { ath10k_warn(ar, "completed old offchannel frame\n"); diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 4e60caec7ab4..6afc8d27f0d5 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3210,7 +3210,8 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, tim_len = tim_info->tim_len ? __le32_to_cpu(tim_info->tim_len) : 1; /* if next SWBA has no tim_changed the tim_bitmap is garbage. - * we must copy the bitmap upon change and reuse it later */ + * we must copy the bitmap upon change and reuse it later + */ if (__le32_to_cpu(tim_info->tim_changed)) { int i; @@ -3529,7 +3530,8 @@ void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) * before telling mac80211 to decrement CSA counter * * Once CSA counter is completed stop sending beacons until - * actual channel switch is done */ + * actual channel switch is done + */ if (arvif->vif->csa_active && ieee80211_csa_is_complete(arvif->vif)) { ieee80211_csa_finish(arvif->vif); @@ -3691,7 +3693,8 @@ radar_detected: ATH10K_DFS_STAT_INC(ar, radar_detected); /* Control radar events reporting in debugfs file - dfs_block_radar_events */ + * dfs_block_radar_events + */ if (ar->dfs_block_radar_events) { ath10k_info(ar, "DFS Radar detected, but ignored as requested\n"); return; @@ -4769,9 +4772,10 @@ static void ath10k_wmi_event_service_ready_work(struct work_struct *work) num_units = ar->max_num_peers + 1; } else if (num_unit_info & NUM_UNITS_IS_NUM_PEERS) { /* number of units to allocate is number of - * peers, 1 extra for self peer on target */ - /* this needs to be tied, host and target - * can get out of sync */ + * peers, 1 extra for self peer on target + * this needs to be tied, host and target + * can get out of sync + */ num_units = ar->max_num_peers + 1; } else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS) { num_units = ar->max_num_vdevs + 1; -- cgit From 53c8d48bb72388e22110e5ef1f52dfc3fac6d97f Mon Sep 17 00:00:00 2001 From: Marcin Rokicki Date: Mon, 20 Feb 2017 14:40:27 +0100 Subject: ath10k: use octal permission representation Fix output from checkpatch.pl like: Symbolic permissions 'S_IRUSR' are not preferred. Consider using octal permissions '0400'. Signed-off-by: Marcin Rokicki Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/debug.c | 88 +++++++++++++-------------- drivers/net/wireless/ath/ath10k/debugfs_sta.c | 9 ++- drivers/net/wireless/ath/ath10k/spectral.c | 6 +- drivers/net/wireless/ath/ath10k/thermal.c | 2 +- 4 files changed, 50 insertions(+), 55 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 8cda518d1150..4cd2a0fd49d6 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -2444,86 +2444,82 @@ int ath10k_debug_register(struct ath10k *ar) init_completion(&ar->debug.tpc_complete); init_completion(&ar->debug.fw_stats_complete); - debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar, + debugfs_create_file("fw_stats", 0400, ar->debug.debugfs_phy, ar, &fops_fw_stats); - debugfs_create_file("fw_reset_stats", S_IRUSR, ar->debug.debugfs_phy, - ar, &fops_fw_reset_stats); + debugfs_create_file("fw_reset_stats", 0400, ar->debug.debugfs_phy, ar, + &fops_fw_reset_stats); - debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar, + debugfs_create_file("wmi_services", 0400, ar->debug.debugfs_phy, ar, &fops_wmi_services); - debugfs_create_file("simulate_fw_crash", S_IRUSR | S_IWUSR, - ar->debug.debugfs_phy, ar, &fops_simulate_fw_crash); + debugfs_create_file("simulate_fw_crash", 0600, ar->debug.debugfs_phy, ar, + &fops_simulate_fw_crash); - debugfs_create_file("fw_crash_dump", S_IRUSR, ar->debug.debugfs_phy, - ar, &fops_fw_crash_dump); + debugfs_create_file("fw_crash_dump", 0400, ar->debug.debugfs_phy, ar, + &fops_fw_crash_dump); - debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR, - ar->debug.debugfs_phy, ar, &fops_reg_addr); + debugfs_create_file("reg_addr", 0600, ar->debug.debugfs_phy, ar, + &fops_reg_addr); - debugfs_create_file("reg_value", S_IRUSR | S_IWUSR, - ar->debug.debugfs_phy, ar, &fops_reg_value); + debugfs_create_file("reg_value", 0600, ar->debug.debugfs_phy, ar, + &fops_reg_value); - debugfs_create_file("mem_value", S_IRUSR | S_IWUSR, - ar->debug.debugfs_phy, ar, &fops_mem_value); + debugfs_create_file("mem_value", 0600, ar->debug.debugfs_phy, ar, + &fops_mem_value); - debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy, - ar, &fops_chip_id); + debugfs_create_file("chip_id", 0400, ar->debug.debugfs_phy, ar, + &fops_chip_id); - debugfs_create_file("htt_stats_mask", S_IRUSR | S_IWUSR, - ar->debug.debugfs_phy, ar, &fops_htt_stats_mask); + debugfs_create_file("htt_stats_mask", 0600, ar->debug.debugfs_phy, ar, + &fops_htt_stats_mask); - debugfs_create_file("htt_max_amsdu_ampdu", S_IRUSR | S_IWUSR, - ar->debug.debugfs_phy, ar, + debugfs_create_file("htt_max_amsdu_ampdu", 0600, ar->debug.debugfs_phy, ar, &fops_htt_max_amsdu_ampdu); - debugfs_create_file("fw_dbglog", S_IRUSR | S_IWUSR, - ar->debug.debugfs_phy, ar, &fops_fw_dbglog); + debugfs_create_file("fw_dbglog", 0600, ar->debug.debugfs_phy, ar, + &fops_fw_dbglog); - debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy, - ar, &fops_cal_data); + debugfs_create_file("cal_data", 0400, ar->debug.debugfs_phy, ar, + &fops_cal_data); - debugfs_create_file("ani_enable", S_IRUSR | S_IWUSR, - ar->debug.debugfs_phy, ar, &fops_ani_enable); + debugfs_create_file("ani_enable", 0600, ar->debug.debugfs_phy, ar, + &fops_ani_enable); - debugfs_create_file("nf_cal_period", S_IRUSR | S_IWUSR, - ar->debug.debugfs_phy, ar, &fops_nf_cal_period); + debugfs_create_file("nf_cal_period", 0600, ar->debug.debugfs_phy, ar, + &fops_nf_cal_period); if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED)) { - debugfs_create_file("dfs_simulate_radar", S_IWUSR, - ar->debug.debugfs_phy, ar, - &fops_simulate_radar); + debugfs_create_file("dfs_simulate_radar", 0200, ar->debug.debugfs_phy, + ar, &fops_simulate_radar); - debugfs_create_bool("dfs_block_radar_events", S_IWUSR, + debugfs_create_bool("dfs_block_radar_events", 0200, ar->debug.debugfs_phy, &ar->dfs_block_radar_events); - debugfs_create_file("dfs_stats", S_IRUSR, - ar->debug.debugfs_phy, ar, + debugfs_create_file("dfs_stats", 0400, ar->debug.debugfs_phy, ar, &fops_dfs_stats); } - debugfs_create_file("pktlog_filter", S_IRUGO | S_IWUSR, - ar->debug.debugfs_phy, ar, &fops_pktlog_filter); + debugfs_create_file("pktlog_filter", 0644, ar->debug.debugfs_phy, ar, + &fops_pktlog_filter); - debugfs_create_file("quiet_period", S_IRUGO | S_IWUSR, - ar->debug.debugfs_phy, ar, &fops_quiet_period); + debugfs_create_file("quiet_period", 0644, ar->debug.debugfs_phy, ar, + &fops_quiet_period); - debugfs_create_file("tpc_stats", S_IRUSR, - ar->debug.debugfs_phy, ar, &fops_tpc_stats); + debugfs_create_file("tpc_stats", 0400, ar->debug.debugfs_phy, ar, + &fops_tpc_stats); if (test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map)) - debugfs_create_file("btcoex", S_IRUGO | S_IWUSR, - ar->debug.debugfs_phy, ar, &fops_btcoex); + debugfs_create_file("btcoex", 0644, ar->debug.debugfs_phy, ar, + &fops_btcoex); if (test_bit(WMI_SERVICE_PEER_STATS, ar->wmi.svc_map)) - debugfs_create_file("peer_stats", S_IRUGO | S_IWUSR, - ar->debug.debugfs_phy, ar, + debugfs_create_file("peer_stats", 0644, ar->debug.debugfs_phy, ar, &fops_peer_stats); - debugfs_create_file("fw_checksums", S_IRUSR, - ar->debug.debugfs_phy, ar, &fops_fw_checksums); + debugfs_create_file("fw_checksums", 0400, ar->debug.debugfs_phy, ar, + &fops_fw_checksums); return 0; } diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c index 7353e7ea88f1..d59ac6b83340 100644 --- a/drivers/net/wireless/ath/ath10k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c @@ -372,11 +372,10 @@ static const struct file_operations fops_peer_debug_trigger = { void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir) { - debugfs_create_file("aggr_mode", S_IRUGO | S_IWUSR, dir, sta, - &fops_aggr_mode); - debugfs_create_file("addba", S_IWUSR, dir, sta, &fops_addba); - debugfs_create_file("addba_resp", S_IWUSR, dir, sta, &fops_addba_resp); - debugfs_create_file("delba", S_IWUSR, dir, sta, &fops_delba); + debugfs_create_file("aggr_mode", 0644, dir, sta, &fops_aggr_mode); + debugfs_create_file("addba", 0200, dir, sta, &fops_addba); + debugfs_create_file("addba_resp", 0200, dir, sta, &fops_addba_resp); + debugfs_create_file("delba", 0200, dir, sta, &fops_delba); debugfs_create_file("peer_debug_trigger", 0600, dir, sta, &fops_peer_debug_trigger); } diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c index c061d6958bd1..46471ed30275 100644 --- a/drivers/net/wireless/ath/ath10k/spectral.c +++ b/drivers/net/wireless/ath/ath10k/spectral.c @@ -536,15 +536,15 @@ int ath10k_spectral_create(struct ath10k *ar) 1140, 2500, &rfs_spec_scan_cb, NULL); debugfs_create_file("spectral_scan_ctl", - S_IRUSR | S_IWUSR, + 0600, ar->debug.debugfs_phy, ar, &fops_spec_scan_ctl); debugfs_create_file("spectral_count", - S_IRUSR | S_IWUSR, + 0600, ar->debug.debugfs_phy, ar, &fops_spectral_count); debugfs_create_file("spectral_bins", - S_IRUSR | S_IWUSR, + 0600, ar->debug.debugfs_phy, ar, &fops_spectral_bins); diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c index f719d7d43cb0..87948aff1bd5 100644 --- a/drivers/net/wireless/ath/ath10k/thermal.c +++ b/drivers/net/wireless/ath/ath10k/thermal.c @@ -124,7 +124,7 @@ void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature) complete(&ar->thermal.wmi_sync); } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ath10k_thermal_show_temp, +static SENSOR_DEVICE_ATTR(temp1_input, 0444, ath10k_thermal_show_temp, NULL, 0); static struct attribute *ath10k_hwmon_attrs[] = { -- cgit From 37ff1b0df37af1f45d94674c83f65fc5ad4a3c73 Mon Sep 17 00:00:00 2001 From: Marcin Rokicki Date: Mon, 20 Feb 2017 15:38:50 +0100 Subject: ath10k: clean header files from bad block comments Fix output from checkpatch.pl like: Block comments use a trailing */ on a separate line Signed-off-by: Marcin Rokicki Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/bmi.h | 3 ++- drivers/net/wireless/ath/ath10k/core.h | 9 ++++++--- drivers/net/wireless/ath/ath10k/hif.h | 6 ++++-- drivers/net/wireless/ath/ath10k/htt.h | 24 +++++++++++++++-------- drivers/net/wireless/ath/ath10k/hw.h | 3 ++- drivers/net/wireless/ath/ath10k/rx_desc.h | 13 ++++++++----- drivers/net/wireless/ath/ath10k/targaddrs.h | 19 +++++++++--------- drivers/net/wireless/ath/ath10k/wmi-ops.h | 3 ++- drivers/net/wireless/ath/ath10k/wmi.h | 30 +++++++++++++++++++---------- 9 files changed, 70 insertions(+), 40 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index a65f26267fe3..cc45b63ade15 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -176,7 +176,8 @@ union bmi_resp { } rompatch_uninstall; struct { /* 0 = nothing executed - * otherwise = NVRAM segment return value */ + * otherwise = NVRAM segment return value + */ __le32 result; } nvram_process; u8 payload[BMI_MAX_CMDBUF_SIZE]; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index d4b9a0ec1bdc..bf091514ecc6 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -501,14 +501,16 @@ enum ath10k_state { * stopped in ath10k_core_restart() work holding conf_mutex. The state * RESTARTED means that the device is up and mac80211 has started hw * reconfiguration. Once mac80211 is done with the reconfiguration we - * set the state to STATE_ON in reconfig_complete(). */ + * set the state to STATE_ON in reconfig_complete(). + */ ATH10K_STATE_RESTARTING, ATH10K_STATE_RESTARTED, /* The device has crashed while restarting hw. This state is like ON * but commands are blocked in HTC and -ECOMM response is given. This * prevents completion timeouts and makes the driver more responsive to - * userspace commands. This is also prevents recursive recovery. */ + * userspace commands. This is also prevents recursive recovery. + */ ATH10K_STATE_WEDGED, /* factory tests */ @@ -920,7 +922,8 @@ struct ath10k { struct work_struct restart_work; /* cycle count is reported twice for each visited channel during scan. - * access protected by data_lock */ + * access protected by data_lock + */ u32 survey_last_rx_clear_count; u32 survey_last_cycle_count; struct survey_info survey[ATH10K_NUM_CHANS]; diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h index b2566b06e1e1..6679dd9cfd12 100644 --- a/drivers/net/wireless/ath/ath10k/hif.h +++ b/drivers/net/wireless/ath/ath10k/hif.h @@ -54,7 +54,8 @@ struct ath10k_hif_ops { int (*start)(struct ath10k *ar); /* Clean up what start() did. This does not revert to BMI phase. If - * desired so, call power_down() and power_up() */ + * desired so, call power_down() and power_up() + */ void (*stop)(struct ath10k *ar); int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id, @@ -82,7 +83,8 @@ struct ath10k_hif_ops { int (*power_up)(struct ath10k *ar); /* Power down the device and free up resources. stop() must be called - * before this if start() was called earlier */ + * before this if start() was called earlier + */ void (*power_down)(struct ath10k *ar); int (*suspend)(struct ath10k *ar); diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 90c2f72666b8..6305308422c4 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -51,7 +51,8 @@ enum htt_h2t_msg_type { /* host-to-target */ HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 6, /* This command is used for sending management frames in HTT < 3.0. - * HTT >= 3.0 uses TX_FRM for everything. */ + * HTT >= 3.0 uses TX_FRM for everything. + */ HTT_H2T_MSG_TYPE_MGMT_TX = 7, HTT_H2T_MSG_TYPE_TX_FETCH_RESP = 11, @@ -910,7 +911,8 @@ struct htt_rx_test { /* payload consists of 2 lists: * a) num_ints * sizeof(__le32) - * b) num_chars * sizeof(u8) aligned to 4bytes */ + * b) num_chars * sizeof(u8) aligned to 4bytes + */ u8 payload[0]; } __packed; @@ -1307,7 +1309,8 @@ struct htt_frag_desc_bank_id { } __packed; /* real is 16 but it wouldn't fit in the max htt message size - * so we use a conservatively safe value for now */ + * so we use a conservatively safe value for now + */ #define HTT_FRAG_DESC_BANK_MAX 4 #define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_MASK 0x03 @@ -1684,12 +1687,14 @@ struct ath10k_htt { DECLARE_KFIFO_PTR(txdone_fifo, struct htt_tx_done); /* set if host-fw communication goes haywire - * used to avoid further failures */ + * used to avoid further failures + */ bool rx_confused; atomic_t num_mpdus_ready; /* This is used to group tx/rx completions separately and process them - * in batches to reduce cache stalls */ + * in batches to reduce cache stalls + */ struct sk_buff_head rx_compl_q; struct sk_buff_head rx_in_ord_compl_q; struct sk_buff_head tx_fetch_ind_q; @@ -1725,11 +1730,13 @@ struct ath10k_htt { /* This structure layout is programmed via rx ring setup * so that FW knows how to transfer the rx descriptor to the host. - * Buffers like this are placed on the rx ring. */ + * Buffers like this are placed on the rx ring. + */ struct htt_rx_desc { union { /* This field is filled on the host using the msdu buffer - * from htt_rx_indication */ + * from htt_rx_indication + */ struct fw_rx_desc_base fw_desc; u32 pad; } __packed; @@ -1760,7 +1767,8 @@ struct htt_rx_desc { #define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc)) /* Refill a bunch of RX buffers for each refill round so that FW/HW can handle - * aggregated traffic more nicely. */ + * aggregated traffic more nicely. + */ #define ATH10K_HTT_MAX_NUM_REFILL 100 /* diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index d370b573e0f9..8aca62bca348 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -296,7 +296,8 @@ void ath10k_hw_fill_survey_time(struct ath10k *ar, struct survey_info *survey, * - raw appears in nwifi decap, raw and nwifi appear in ethernet decap * - raw have FCS, nwifi doesn't * - ethernet frames have 802.11 header decapped and parts (base hdr, cipher - * param, llc/snap) are aligned to 4byte boundaries each */ + * param, llc/snap) are aligned to 4byte boundaries each + */ enum ath10k_hw_txrx_mode { ATH10K_HW_TXRX_RAW = 0, diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h index 034e7a54c5b2..c1022a1cf855 100644 --- a/drivers/net/wireless/ath/ath10k/rx_desc.h +++ b/drivers/net/wireless/ath/ath10k/rx_desc.h @@ -439,19 +439,22 @@ struct rx_mpdu_end { * c) A-MSDU subframe header (14 bytes) if appliable * d) LLC/SNAP (RFC1042, 8 bytes) * - * In case of A-MSDU only first frame in sequence contains (a) and (b). */ + * In case of A-MSDU only first frame in sequence contains (a) and (b). + */ enum rx_msdu_decap_format { RX_MSDU_DECAP_RAW = 0, /* Note: QoS frames are reported as non-QoS. The rx_hdr_status in - * htt_rx_desc contains the original decapped 802.11 header. */ + * htt_rx_desc contains the original decapped 802.11 header. + */ RX_MSDU_DECAP_NATIVE_WIFI = 1, /* Payload contains an ethernet header (struct ethhdr). */ RX_MSDU_DECAP_ETHERNET2_DIX = 2, /* Payload contains two 48-bit addresses and 2-byte length (14 bytes - * total), followed by an RFC1042 header (8 bytes). */ + * total), followed by an RFC1042 header (8 bytes). + */ RX_MSDU_DECAP_8023_SNAP_LLC = 3 }; @@ -867,7 +870,7 @@ struct rx_ppdu_start { * * reserved_9 * Reserved: HW should fill with 0, FW should ignore. -*/ + */ #define RX_PPDU_END_FLAGS_PHY_ERR (1 << 0) #define RX_PPDU_END_FLAGS_RX_LOCATION (1 << 1) @@ -1207,7 +1210,7 @@ struct rx_ppdu_end { * Every time HW sets this bit in memory FW/SW must clear this * bit in memory. FW will initialize all the ppdu_done dword * to 0. -*/ + */ #define FW_RX_DESC_INFO0_DISCARD (1 << 0) #define FW_RX_DESC_INFO0_FORWARD (1 << 1) diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h index a47cab44d9c8..cbac9e4252d6 100644 --- a/drivers/net/wireless/ath/ath10k/targaddrs.h +++ b/drivers/net/wireless/ath/ath10k/targaddrs.h @@ -268,13 +268,13 @@ struct host_interest { #define HI_OPTION_FW_BRIDGE_SHIFT 0x04 /* -Fw Mode/SubMode Mask -|-----------------------------------------------------------------------------| -| SUB | SUB | SUB | SUB | | | | | -|MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0]| -| (2) | (2) | (2) | (2) | (2) | (2) | (2) | (2) | -|-----------------------------------------------------------------------------| -*/ + * Fw Mode/SubMode Mask + *----------------------------------------------------------------------------- + * SUB | SUB | SUB | SUB | | | | + *MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0] + * (2) | (2) | (2) | (2) | (2) | (2) | (2) | (2) + *----------------------------------------------------------------------------- + */ #define HI_OPTION_FW_MODE_BITS 0x2 #define HI_OPTION_FW_MODE_MASK 0x3 #define HI_OPTION_FW_MODE_SHIFT 0xC @@ -428,8 +428,9 @@ Fw Mode/SubMode Mask #define HI_PWR_SAVE_LPL_ENABLED 0x1 /*b1-b3 reserved*/ /*b4-b5 : dev0 LPL type : 0 - none - 1- Reduce Pwr Search - 2- Reduce Pwr Listen*/ + * 1- Reduce Pwr Search + * 2- Reduce Pwr Listen + */ /*b6-b7 : dev1 LPL type and so on for Max 8 devices*/ #define HI_PWR_SAVE_LPL_DEV0_LSB 4 #define HI_PWR_SAVE_LPL_DEV_MASK 0x3 diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h index c7956e181f80..2fc3f24ff1ca 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-ops.h +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -390,7 +390,8 @@ ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) return ret; /* FIXME There's no ACK event for Management Tx. This probably - * shouldn't be called here either. */ + * shouldn't be called here either. + */ info->flags |= IEEE80211_TX_STAT_ACK; ieee80211_tx_status_irqsafe(ar->hw, msdu); diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index cf385feb5707..1b4865a55595 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -1038,7 +1038,8 @@ enum wmi_cmd_id { WMI_STA_UAPSD_AUTO_TRIG_CMDID, /* STA Keep alive parameter configuration, - Requires WMI_SERVICE_STA_KEEP_ALIVE */ + * Requires WMI_SERVICE_STA_KEEP_ALIVE + */ WMI_STA_KEEPALIVE_CMD, /* misc command group */ @@ -1774,7 +1775,8 @@ static inline const char *ath10k_wmi_phymode_str(enum wmi_phy_mode mode) break; /* no default handler to allow compiler to check that the - * enum is fully handled */ + * enum is fully handled + */ }; return ""; @@ -2974,7 +2976,8 @@ struct wmi_start_scan_arg { /* When set, DFS channels will not be scanned */ #define WMI_SCAN_BYPASS_DFS_CHN 0x40 /* Different FW scan engine may choose to bail out on errors. - * Allow the driver to have influence over that. */ + * Allow the driver to have influence over that. + */ #define WMI_SCAN_CONTINUE_ON_ERROR 0x80 /* WMI_SCAN_CLASS_MASK must be the same value as IEEE80211_SCAN_CLASS_MASK */ @@ -4447,14 +4450,16 @@ enum wmi_vdev_subtype_10_4 { /* values for vdev_start_request flags */ /* * Indicates that AP VDEV uses hidden ssid. only valid for - * AP/GO */ + * AP/GO + */ #define WMI_VDEV_START_HIDDEN_SSID (1 << 0) /* * Indicates if robust management frame/management frame * protection is enabled. For GO/AP vdevs, it indicates that * it may support station/client associations with RMF enabled. * For STA/client vdevs, it indicates that sta will - * associate with AP with RMF enabled. */ + * associate with AP with RMF enabled. + */ #define WMI_VDEV_START_PMF_ENABLED (1 << 1) struct wmi_p2p_noa_descriptor { @@ -4814,7 +4819,8 @@ enum wmi_vdev_param { * An associated STA is considered unresponsive if there is no recent * TX/RX activity and downlink frames are buffered for it. Once a STA * exceeds the maximum unresponsive time, the AP will send a - * WMI_STA_KICKOUT event to the host so the STA can be deleted. */ + * WMI_STA_KICKOUT event to the host so the STA can be deleted. + */ WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, /* Enable NAWDS : MCAST INSPECT Enable, NAWDS Flag set */ @@ -4941,7 +4947,8 @@ enum wmi_10x_vdev_param { * An associated STA is considered unresponsive if there is no recent * TX/RX activity and downlink frames are buffered for it. Once a STA * exceeds the maximum unresponsive time, the AP will send a - * WMI_10X_STA_KICKOUT event to the host so the STA can be deleted. */ + * WMI_10X_STA_KICKOUT event to the host so the STA can be deleted. + */ WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, /* Enable NAWDS : MCAST INSPECT Enable, NAWDS Flag set */ @@ -5605,12 +5612,14 @@ struct wmi_tim_info_arg { struct wmi_p2p_noa_info { /* Bit 0 - Flag to indicate an update in NOA schedule - Bits 7-1 - Reserved */ + * Bits 7-1 - Reserved + */ u8 changed; /* NOA index */ u8 index; /* Bit 0 - Opp PS state of the AP - Bits 1-7 - Ctwindow in TUs */ + * Bits 1-7 - Ctwindow in TUs + */ u8 ctwindow_oppps; /* Number of NOA descriptors */ u8 num_descriptors; @@ -6000,7 +6009,8 @@ struct wmi_main_peer_assoc_complete_cmd { struct wmi_common_peer_assoc_complete_cmd cmd; /* HT Operation Element of the peer. Five bytes packed in 2 - * INT32 array and filled from lsb to msb. */ + * INT32 array and filled from lsb to msb. + */ __le32 peer_ht_info[2]; } __packed; -- cgit From e7f6ccaab127147c52ac4b624c54ad0059bd08e9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 Mar 2017 12:36:35 +0300 Subject: NFC: pn544: Get rid of platform data Legacy platform data must go away. We are on the safe side here since there are no users of it in the kernel. If anyone by any odd reason needs it the GPIO lookup tables and built-in device properties at your service. Signed-off-by: Andy Shevchenko Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/i2c.c | 43 ++++++------------------------------- include/linux/platform_data/pn544.h | 43 ------------------------------------- 2 files changed, 6 insertions(+), 80 deletions(-) delete mode 100644 include/linux/platform_data/pn544.h diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index 7030a47fe379..4afc92a54444 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -30,7 +30,7 @@ #include #include #include -#include + #include #include @@ -972,7 +972,6 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pn544_i2c_phy *phy; - struct pn544_nfc_platform_data *pdata; int r = 0; dev_dbg(&client->dev, "%s\n", __func__); @@ -994,32 +993,13 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, phy->i2c_dev = client; i2c_set_clientdata(client, phy); - pdata = client->dev.platform_data; - /* No platform data, using device tree. */ - if (!pdata && client->dev.of_node) { + if (client->dev.of_node) { r = pn544_hci_i2c_of_request_resources(client); if (r) { nfc_err(&client->dev, "No DT data\n"); return r; } - /* Using platform data. */ - } else if (pdata) { - - if (pdata->request_resources == NULL) { - nfc_err(&client->dev, "request_resources() missing\n"); - return -EINVAL; - } - - r = pdata->request_resources(client); - if (r) { - nfc_err(&client->dev, - "Cannot get platform resources\n"); - return r; - } - - phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE); - phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET); /* Using ACPI */ } else if (ACPI_HANDLE(&client->dev)) { r = pn544_hci_i2c_acpi_request_resources(client); @@ -1056,12 +1036,8 @@ err_hci: free_irq(client->irq, phy); err_rti: - if (!pdata) { - gpio_free(phy->gpio_en); - gpio_free(phy->gpio_fw); - } else if (pdata->free_resources) { - pdata->free_resources(); - } + gpio_free(phy->gpio_en); + gpio_free(phy->gpio_fw); return r; } @@ -1069,7 +1045,6 @@ err_rti: static int pn544_hci_i2c_remove(struct i2c_client *client) { struct pn544_i2c_phy *phy = i2c_get_clientdata(client); - struct pn544_nfc_platform_data *pdata = client->dev.platform_data; dev_dbg(&client->dev, "%s\n", __func__); @@ -1084,14 +1059,8 @@ static int pn544_hci_i2c_remove(struct i2c_client *client) free_irq(client->irq, phy); - /* No platform data, GPIOs have been requested by this driver */ - if (!pdata) { - gpio_free(phy->gpio_en); - gpio_free(phy->gpio_fw); - /* Using platform data */ - } else if (pdata->free_resources) { - pdata->free_resources(); - } + gpio_free(phy->gpio_en); + gpio_free(phy->gpio_fw); return 0; } diff --git a/include/linux/platform_data/pn544.h b/include/linux/platform_data/pn544.h deleted file mode 100644 index 5ce1ab983f44..000000000000 --- a/include/linux/platform_data/pn544.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Driver include for the PN544 NFC chip. - * - * Copyright (C) Nokia Corporation - * - * Author: Jari Vanhala - * Contact: Matti Aaltoenn - * - * 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. - * - * 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, see . - */ - -#ifndef _PN544_H_ -#define _PN544_H_ - -#include - -enum { - NFC_GPIO_ENABLE, - NFC_GPIO_FW_RESET, - NFC_GPIO_IRQ -}; - -/* board config */ -struct pn544_nfc_platform_data { - int (*request_resources) (struct i2c_client *client); - void (*free_resources) (void); - void (*enable) (int fw); - int (*test) (void); - void (*disable) (void); - int (*get_gpio)(int type); -}; - -#endif /* _PN544_H_ */ -- cgit From e2c518c6c9987bf832bb1475de38911da96bd3db Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 Mar 2017 12:36:36 +0300 Subject: NFC: pn544: Convert to use GPIO descriptor Since we got rid of platform data, the driver may use GPIO descriptor directly. This change fixes a potential issue of double freeing GPIOs in ACPI case by converting to devm_gpiod_get(). Signed-off-by: Andy Shevchenko Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/i2c.c | 126 +++++++++++++----------------------------------- 1 file changed, 33 insertions(+), 93 deletions(-) diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index 4afc92a54444..23485304ce91 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -21,9 +21,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -164,8 +161,9 @@ struct pn544_i2c_phy { struct i2c_client *i2c_dev; struct nfc_hci_dev *hdev; - unsigned int gpio_en; - unsigned int gpio_fw; + struct gpio_desc *gpiod_en; + struct gpio_desc *gpiod_fw; + unsigned int en_polarity; u8 hw_variant; @@ -207,19 +205,18 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy) nfc_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n"); /* Disable fw download */ - gpio_set_value_cansleep(phy->gpio_fw, 0); + gpiod_set_value_cansleep(phy->gpiod_fw, 0); for (polarity = 0; polarity < 2; polarity++) { phy->en_polarity = polarity; retry = 3; while (retry--) { /* power off */ - gpio_set_value_cansleep(phy->gpio_en, - !phy->en_polarity); + gpiod_set_value_cansleep(phy->gpiod_en, !phy->en_polarity); usleep_range(10000, 15000); /* power on */ - gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity); + gpiod_set_value_cansleep(phy->gpiod_en, phy->en_polarity); usleep_range(10000, 15000); /* send reset */ @@ -238,14 +235,13 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy) "Could not detect nfc_en polarity, fallback to active high\n"); out: - gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity); + gpiod_set_value_cansleep(phy->gpiod_en, !phy->en_polarity); } static void pn544_hci_i2c_enable_mode(struct pn544_i2c_phy *phy, int run_mode) { - gpio_set_value_cansleep(phy->gpio_fw, - run_mode == PN544_FW_MODE ? 1 : 0); - gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity); + gpiod_set_value_cansleep(phy->gpiod_fw, run_mode == PN544_FW_MODE ? 1 : 0); + gpiod_set_value_cansleep(phy->gpiod_en, phy->en_polarity); usleep_range(10000, 15000); phy->run_mode = run_mode; @@ -268,14 +264,14 @@ static void pn544_hci_i2c_disable(void *phy_id) { struct pn544_i2c_phy *phy = phy_id; - gpio_set_value_cansleep(phy->gpio_fw, 0); - gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity); + gpiod_set_value_cansleep(phy->gpiod_fw, 0); + gpiod_set_value_cansleep(phy->gpiod_en, !phy->en_polarity); usleep_range(10000, 15000); - gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity); + gpiod_set_value_cansleep(phy->gpiod_en, phy->en_polarity); usleep_range(10000, 15000); - gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity); + gpiod_set_value_cansleep(phy->gpiod_en, !phy->en_polarity); usleep_range(10000, 15000); phy->powered = 0; @@ -876,96 +872,47 @@ exit_state_wait_secure_write_answer: static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client) { struct pn544_i2c_phy *phy = i2c_get_clientdata(client); - struct gpio_desc *gpiod_en, *gpiod_fw; struct device *dev = &client->dev; /* Get EN GPIO from ACPI */ - gpiod_en = devm_gpiod_get_index(dev, PN544_GPIO_NAME_EN, 1, - GPIOD_OUT_LOW); - if (IS_ERR(gpiod_en)) { + phy->gpiod_en = devm_gpiod_get_index(dev, PN544_GPIO_NAME_EN, 1, + GPIOD_OUT_LOW); + if (IS_ERR(phy->gpiod_en)) { nfc_err(dev, "Unable to get EN GPIO\n"); - return -ENODEV; + return PTR_ERR(phy->gpiod_en); } - phy->gpio_en = desc_to_gpio(gpiod_en); - /* Get FW GPIO from ACPI */ - gpiod_fw = devm_gpiod_get_index(dev, PN544_GPIO_NAME_FW, 2, - GPIOD_OUT_LOW); - if (IS_ERR(gpiod_fw)) { + phy->gpiod_fw = devm_gpiod_get_index(dev, PN544_GPIO_NAME_FW, 2, + GPIOD_OUT_LOW); + if (IS_ERR(phy->gpiod_fw)) { nfc_err(dev, "Unable to get FW GPIO\n"); - return -ENODEV; + return PTR_ERR(phy->gpiod_fw); } - phy->gpio_fw = desc_to_gpio(gpiod_fw); - return 0; } static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) { struct pn544_i2c_phy *phy = i2c_get_clientdata(client); - struct device_node *pp; - int ret; - - pp = client->dev.of_node; - if (!pp) { - ret = -ENODEV; - goto err_dt; - } - - /* Obtention of EN GPIO from device tree */ - ret = of_get_named_gpio(pp, "enable-gpios", 0); - if (ret < 0) { - if (ret != -EPROBE_DEFER) - nfc_err(&client->dev, - "Failed to get EN gpio, error: %d\n", ret); - goto err_dt; - } - phy->gpio_en = ret; - - /* Configuration of EN GPIO */ - ret = gpio_request(phy->gpio_en, PN544_GPIO_NAME_EN); - if (ret) { - nfc_err(&client->dev, "Fail EN pin\n"); - goto err_dt; - } - ret = gpio_direction_output(phy->gpio_en, 0); - if (ret) { - nfc_err(&client->dev, "Fail EN pin direction\n"); - goto err_gpio_en; - } + struct device *dev = &client->dev; - /* Obtention of FW GPIO from device tree */ - ret = of_get_named_gpio(pp, "firmware-gpios", 0); - if (ret < 0) { - if (ret != -EPROBE_DEFER) - nfc_err(&client->dev, - "Failed to get FW gpio, error: %d\n", ret); - goto err_gpio_en; + /* Obtaining EN GPIO from device tree */ + phy->gpiod_en = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(phy->gpiod_en)) { + nfc_err(dev, "Failed to get EN gpio\n"); + return PTR_ERR(phy->gpiod_en); } - phy->gpio_fw = ret; - /* Configuration of FW GPIO */ - ret = gpio_request(phy->gpio_fw, PN544_GPIO_NAME_FW); - if (ret) { - nfc_err(&client->dev, "Fail FW pin\n"); - goto err_gpio_en; - } - ret = gpio_direction_output(phy->gpio_fw, 0); - if (ret) { - nfc_err(&client->dev, "Fail FW pin direction\n"); - goto err_gpio_fw; + /* Obtaining FW GPIO from device tree */ + phy->gpiod_fw = devm_gpiod_get(dev, "firmware", GPIOD_OUT_LOW); + if (IS_ERR(phy->gpiod_fw)) { + nfc_err(dev, "Failed to get FW gpio\n"); + return PTR_ERR(phy->gpiod_fw); } return 0; - -err_gpio_fw: - gpio_free(phy->gpio_fw); -err_gpio_en: - gpio_free(phy->gpio_en); -err_dt: - return ret; } static int pn544_hci_i2c_probe(struct i2c_client *client, @@ -1020,7 +967,7 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, PN544_HCI_I2C_DRIVER_NAME, phy); if (r < 0) { nfc_err(&client->dev, "Unable to register IRQ handler\n"); - goto err_rti; + return r; } r = pn544_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME, @@ -1035,10 +982,6 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, err_hci: free_irq(client->irq, phy); -err_rti: - gpio_free(phy->gpio_en); - gpio_free(phy->gpio_fw); - return r; } @@ -1059,9 +1002,6 @@ static int pn544_hci_i2c_remove(struct i2c_client *client) free_irq(client->irq, phy); - gpio_free(phy->gpio_en); - gpio_free(phy->gpio_fw); - return 0; } -- cgit From 182d4e860845bbf388f5f60d9e651611f3dbd351 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 Mar 2017 12:36:37 +0300 Subject: NFC: pn544: Convert to use devm_request_threaded_irq() The error handling will be neat and short when using managed resources. Convert the driver to use devm_request_threaded_irq(). Signed-off-by: Andy Shevchenko Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/i2c.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index 23485304ce91..327c33df5b22 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -962,9 +962,10 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, pn544_hci_i2c_platform_init(phy); - r = request_threaded_irq(client->irq, NULL, pn544_hci_i2c_irq_thread_fn, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, - PN544_HCI_I2C_DRIVER_NAME, phy); + r = devm_request_threaded_irq(&client->dev, client->irq, NULL, + pn544_hci_i2c_irq_thread_fn, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + PN544_HCI_I2C_DRIVER_NAME, phy); if (r < 0) { nfc_err(&client->dev, "Unable to register IRQ handler\n"); return r; @@ -975,14 +976,9 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, PN544_HCI_I2C_LLC_MAX_PAYLOAD, pn544_hci_i2c_fw_download, &phy->hdev); if (r < 0) - goto err_hci; + return r; return 0; - -err_hci: - free_irq(client->irq, phy); - - return r; } static int pn544_hci_i2c_remove(struct i2c_client *client) @@ -1000,8 +996,6 @@ static int pn544_hci_i2c_remove(struct i2c_client *client) if (phy->powered) pn544_hci_i2c_disable(phy); - free_irq(client->irq, phy); - return 0; } -- cgit From a4a0eb783b112e45563dcf020388dfa186961010 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 Mar 2017 12:36:38 +0300 Subject: NFC: pn544: Add GPIO ACPI mapping table In order to make GPIO ACPI library stricter prepare users of gpiod_get_index() to correctly behave when there no mapping is provided by firmware. Here we add explicit mapping between _CRS GpioIo() resources and their names used in the driver. Signed-off-by: Andy Shevchenko Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/i2c.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index 327c33df5b22..6f2a9dff6b71 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -869,22 +869,34 @@ exit_state_wait_secure_write_answer: } } +static const struct acpi_gpio_params enable_gpios = { 1, 0, false }; +static const struct acpi_gpio_params firmware_gpios = { 2, 0, false }; + +static const struct acpi_gpio_mapping acpi_pn544_gpios[] = { + { "enable-gpios", &enable_gpios, 1 }, + { "firmware-gpios", &firmware_gpios, 1 }, + { }, +}; + static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client) { struct pn544_i2c_phy *phy = i2c_get_clientdata(client); struct device *dev = &client->dev; + int ret; + + ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), acpi_pn544_gpios); + if (ret) + return ret; /* Get EN GPIO from ACPI */ - phy->gpiod_en = devm_gpiod_get_index(dev, PN544_GPIO_NAME_EN, 1, - GPIOD_OUT_LOW); + phy->gpiod_en = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(phy->gpiod_en)) { nfc_err(dev, "Unable to get EN GPIO\n"); return PTR_ERR(phy->gpiod_en); } /* Get FW GPIO from ACPI */ - phy->gpiod_fw = devm_gpiod_get_index(dev, PN544_GPIO_NAME_FW, 2, - GPIOD_OUT_LOW); + phy->gpiod_fw = devm_gpiod_get(dev, "firmware", GPIOD_OUT_LOW); if (IS_ERR(phy->gpiod_fw)) { nfc_err(dev, "Unable to get FW GPIO\n"); return PTR_ERR(phy->gpiod_fw); @@ -996,6 +1008,7 @@ static int pn544_hci_i2c_remove(struct i2c_client *client) if (phy->powered) pn544_hci_i2c_disable(phy); + acpi_dev_remove_driver_gpios(ACPI_COMPANION(&client->dev)); return 0; } -- cgit From 95129b6f0806d1ba6109dc1df4d9753ad3d4a94c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 28 Mar 2017 12:36:39 +0300 Subject: NFC: pn544: Get rid of code duplication in ->probe() Since OF and ACPI case almost the same get rid of code duplication by moving gpiod_get() calls directly to ->probe(). Signed-off-by: Andy Shevchenko Signed-off-by: Samuel Ortiz --- drivers/nfc/pn544/i2c.c | 84 ++++++++++--------------------------------------- 1 file changed, 17 insertions(+), 67 deletions(-) diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index 6f2a9dff6b71..71ac0836c9f4 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -878,58 +878,10 @@ static const struct acpi_gpio_mapping acpi_pn544_gpios[] = { { }, }; -static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client) -{ - struct pn544_i2c_phy *phy = i2c_get_clientdata(client); - struct device *dev = &client->dev; - int ret; - - ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), acpi_pn544_gpios); - if (ret) - return ret; - - /* Get EN GPIO from ACPI */ - phy->gpiod_en = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(phy->gpiod_en)) { - nfc_err(dev, "Unable to get EN GPIO\n"); - return PTR_ERR(phy->gpiod_en); - } - - /* Get FW GPIO from ACPI */ - phy->gpiod_fw = devm_gpiod_get(dev, "firmware", GPIOD_OUT_LOW); - if (IS_ERR(phy->gpiod_fw)) { - nfc_err(dev, "Unable to get FW GPIO\n"); - return PTR_ERR(phy->gpiod_fw); - } - - return 0; -} - -static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) -{ - struct pn544_i2c_phy *phy = i2c_get_clientdata(client); - struct device *dev = &client->dev; - - /* Obtaining EN GPIO from device tree */ - phy->gpiod_en = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(phy->gpiod_en)) { - nfc_err(dev, "Failed to get EN gpio\n"); - return PTR_ERR(phy->gpiod_en); - } - - /* Obtaining FW GPIO from device tree */ - phy->gpiod_fw = devm_gpiod_get(dev, "firmware", GPIOD_OUT_LOW); - if (IS_ERR(phy->gpiod_fw)) { - nfc_err(dev, "Failed to get FW gpio\n"); - return PTR_ERR(phy->gpiod_fw); - } - - return 0; -} - static int pn544_hci_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device *dev = &client->dev; struct pn544_i2c_phy *phy; int r = 0; @@ -952,24 +904,22 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, phy->i2c_dev = client; i2c_set_clientdata(client, phy); - /* No platform data, using device tree. */ - if (client->dev.of_node) { - r = pn544_hci_i2c_of_request_resources(client); - if (r) { - nfc_err(&client->dev, "No DT data\n"); - return r; - } - /* Using ACPI */ - } else if (ACPI_HANDLE(&client->dev)) { - r = pn544_hci_i2c_acpi_request_resources(client); - if (r) { - nfc_err(&client->dev, - "Cannot get ACPI data\n"); - return r; - } - } else { - nfc_err(&client->dev, "No platform data\n"); - return -EINVAL; + r = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), acpi_pn544_gpios); + if (r) + dev_dbg(dev, "Unable to add GPIO mapping table\n"); + + /* Get EN GPIO */ + phy->gpiod_en = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(phy->gpiod_en)) { + nfc_err(dev, "Unable to get EN GPIO\n"); + return PTR_ERR(phy->gpiod_en); + } + + /* Get FW GPIO */ + phy->gpiod_fw = devm_gpiod_get(dev, "firmware", GPIOD_OUT_LOW); + if (IS_ERR(phy->gpiod_fw)) { + nfc_err(dev, "Unable to get FW GPIO\n"); + return PTR_ERR(phy->gpiod_fw); } pn544_hci_i2c_platform_init(phy); -- cgit From bacf2a6a05726e3f10aa94ef7a20feba62ee5579 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 7 Mar 2017 12:25:42 +0200 Subject: NFC: st21nfca: Fix obvious typo when check error code We return -ENODEV if ACPI provides a GPIO resource. Looks really wrong. If it has even been tested? Signed-off-by: Andy Shevchenko Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index d16f58ac09bc..c4f0d048359b 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -513,9 +513,9 @@ static int st21nfca_hci_i2c_acpi_request_resources(struct i2c_client *client) /* Get EN GPIO from ACPI */ gpiod_ena = devm_gpiod_get_index(dev, ST21NFCA_GPIO_NAME_EN, 1, GPIOD_OUT_LOW); - if (!IS_ERR(gpiod_ena)) { + if (IS_ERR(gpiod_ena)) { nfc_err(dev, "Unable to get ENABLE GPIO\n"); - return -ENODEV; + return PTR_ERR(gpiod_ena); } phy->gpio_ena = desc_to_gpio(gpiod_ena); -- cgit From 79557b33cca2fa005235b45ab16b81f95f441bd8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 7 Mar 2017 12:25:43 +0200 Subject: NFC: st21nfca: Get rid of platform data Legacy platform data must go away. We are on the safe side here since there are no users of it in the kernel. If anyone by any odd reason needs it the GPIO lookup tables and built-in device properties at your service. Signed-off-by: Andy Shevchenko Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/i2c.c | 46 +++------------------------------- include/linux/platform_data/st21nfca.h | 33 ------------------------ 2 files changed, 3 insertions(+), 76 deletions(-) delete mode 100644 include/linux/platform_data/st21nfca.h diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index c4f0d048359b..443733502251 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -29,7 +28,7 @@ #include #include #include -#include + #include #include @@ -59,6 +58,7 @@ #define IS_START_OF_FRAME(buf) (buf[0] == ST21NFCA_SOF_EOF && \ buf[1] == 0) +#define ST21NFCA_HCI_DRIVER_NAME "st21nfca_hci" #define ST21NFCA_HCI_I2C_DRIVER_NAME "st21nfca_hci_i2c" #define ST21NFCA_GPIO_NAME_EN "enable" @@ -576,43 +576,10 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) return 0; } -static int st21nfca_hci_i2c_request_resources(struct i2c_client *client) -{ - struct st21nfca_nfc_platform_data *pdata; - struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); - int r; - - pdata = client->dev.platform_data; - if (pdata == NULL) { - nfc_err(&client->dev, "No platform data\n"); - return -EINVAL; - } - - /* store for later use */ - phy->gpio_ena = pdata->gpio_ena; - phy->irq_polarity = pdata->irq_polarity; - - if (phy->gpio_ena > 0) { - r = devm_gpio_request_one(&client->dev, phy->gpio_ena, - GPIOF_OUT_INIT_HIGH, - ST21NFCA_GPIO_NAME_EN); - if (r) { - pr_err("%s : ena gpio_request failed\n", __FILE__); - return r; - } - } - - phy->se_status.is_ese_present = pdata->is_ese_present; - phy->se_status.is_uicc_present = pdata->is_uicc_present; - - return 0; -} - static int st21nfca_hci_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct st21nfca_i2c_phy *phy; - struct st21nfca_nfc_platform_data *pdata; int r; dev_dbg(&client->dev, "%s\n", __func__); @@ -638,19 +605,12 @@ static int st21nfca_hci_i2c_probe(struct i2c_client *client, mutex_init(&phy->phy_lock); i2c_set_clientdata(client, phy); - pdata = client->dev.platform_data; - if (!pdata && client->dev.of_node) { + if (client->dev.of_node) { r = st21nfca_hci_i2c_of_request_resources(client); if (r) { nfc_err(&client->dev, "No platform data\n"); return r; } - } else if (pdata) { - r = st21nfca_hci_i2c_request_resources(client); - if (r) { - nfc_err(&client->dev, "Cannot get platform resources\n"); - return r; - } } else if (ACPI_HANDLE(&client->dev)) { r = st21nfca_hci_i2c_acpi_request_resources(client); if (r) { diff --git a/include/linux/platform_data/st21nfca.h b/include/linux/platform_data/st21nfca.h deleted file mode 100644 index cc2bdafb0c69..000000000000 --- a/include/linux/platform_data/st21nfca.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Driver include for the ST21NFCA NFC chip. - * - * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. - * - * 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 that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#ifndef _ST21NFCA_HCI_H_ -#define _ST21NFCA_HCI_H_ - -#include - -#define ST21NFCA_HCI_DRIVER_NAME "st21nfca_hci" - -struct st21nfca_nfc_platform_data { - unsigned int gpio_ena; - unsigned int irq_polarity; - bool is_ese_present; - bool is_uicc_present; -}; - -#endif /* _ST21NFCA_HCI_H_ */ -- cgit From 8e7836d030a2e08290a8df25d45a2fad00957738 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 7 Mar 2017 12:25:44 +0200 Subject: NFC: st21nfca: Get rid of "interesting" use of interrupt polarity I2C framework followed by IRQ framework does set interrupt polarity correctly if it's properly specified in firmware (ACPI or DT). Get rid of the redundant trick when requesting interrupt. Signed-off-by: Andy Shevchenko Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/i2c.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index 443733502251..6cfc7b9c68a2 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -68,7 +68,6 @@ struct st21nfca_i2c_phy { struct nfc_hci_dev *hdev; unsigned int gpio_ena; - unsigned int irq_polarity; struct st21nfca_se_status se_status; @@ -520,8 +519,6 @@ static int st21nfca_hci_i2c_acpi_request_resources(struct i2c_client *client) phy->gpio_ena = desc_to_gpio(gpiod_ena); - phy->irq_polarity = irq_get_trigger_type(client->irq); - phy->se_status.is_ese_present = false; phy->se_status.is_uicc_present = false; @@ -566,8 +563,6 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) phy->gpio_ena = gpio; - phy->irq_polarity = irq_get_trigger_type(client->irq); - phy->se_status.is_ese_present = of_property_read_bool(pp, "ese-present"); phy->se_status.is_uicc_present = @@ -630,7 +625,7 @@ static int st21nfca_hci_i2c_probe(struct i2c_client *client, r = devm_request_threaded_irq(&client->dev, client->irq, NULL, st21nfca_hci_irq_thread_fn, - phy->irq_polarity | IRQF_ONESHOT, + IRQF_ONESHOT, ST21NFCA_HCI_DRIVER_NAME, phy); if (r < 0) { nfc_err(&client->dev, "Unable to register IRQ handler\n"); -- cgit From 8d3c50e2f290585f000638c3e3a8a1b1208e49cf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 7 Mar 2017 12:25:45 +0200 Subject: NFC: st21nfca: Covert to use GPIO descriptor Since we got rid of platform data, the driver may use GPIO descriptor directly. Looking deeply to the use of the GPIO pin it looks like it should be a fixed voltage regulator rather than custom GPIO handling. But this is out of scope of the change. Signed-off-by: Andy Shevchenko Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/i2c.c | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index 6cfc7b9c68a2..4946816f0011 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -67,8 +67,7 @@ struct st21nfca_i2c_phy { struct i2c_client *i2c_dev; struct nfc_hci_dev *hdev; - unsigned int gpio_ena; - + struct gpio_desc *gpiod_ena; struct st21nfca_se_status se_status; struct sk_buff *pending_skb; @@ -149,7 +148,7 @@ static int st21nfca_hci_i2c_enable(void *phy_id) { struct st21nfca_i2c_phy *phy = phy_id; - gpio_set_value(phy->gpio_ena, 1); + gpiod_set_value(phy->gpiod_ena, 1); phy->powered = 1; phy->run_mode = ST21NFCA_HCI_MODE; @@ -162,7 +161,7 @@ static void st21nfca_hci_i2c_disable(void *phy_id) { struct st21nfca_i2c_phy *phy = phy_id; - gpio_set_value(phy->gpio_ena, 0); + gpiod_set_value(phy->gpiod_ena, 0); phy->powered = 0; } @@ -505,20 +504,17 @@ static struct nfc_phy_ops i2c_phy_ops = { static int st21nfca_hci_i2c_acpi_request_resources(struct i2c_client *client) { struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); - struct gpio_desc *gpiod_ena; struct device *dev = &client->dev; u8 tmp; /* Get EN GPIO from ACPI */ - gpiod_ena = devm_gpiod_get_index(dev, ST21NFCA_GPIO_NAME_EN, 1, - GPIOD_OUT_LOW); - if (IS_ERR(gpiod_ena)) { + phy->gpiod_ena = devm_gpiod_get_index(dev, ST21NFCA_GPIO_NAME_EN, 1, + GPIOD_OUT_LOW); + if (IS_ERR(phy->gpiod_ena)) { nfc_err(dev, "Unable to get ENABLE GPIO\n"); - return PTR_ERR(gpiod_ena); + return PTR_ERR(phy->gpiod_ena); } - phy->gpio_ena = desc_to_gpio(gpiod_ena); - phy->se_status.is_ese_present = false; phy->se_status.is_uicc_present = false; @@ -538,31 +534,21 @@ static int st21nfca_hci_i2c_acpi_request_resources(struct i2c_client *client) static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) { struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); + struct device *dev = &client->dev; struct device_node *pp; - int gpio; - int r; pp = client->dev.of_node; if (!pp) return -ENODEV; /* Get GPIO from device tree */ - gpio = of_get_named_gpio(pp, "enable-gpios", 0); - if (gpio < 0) { - nfc_err(&client->dev, "Failed to retrieve enable-gpios from device tree\n"); - return gpio; + phy->gpiod_ena = devm_gpiod_get_index(dev, ST21NFCA_GPIO_NAME_EN, 0, + GPIOD_OUT_HIGH); + if (IS_ERR(phy->gpiod_ena)) { + nfc_err(dev, "Failed to request enable pin\n"); + return PTR_ERR(phy->gpiod_ena); } - /* GPIO request and configuration */ - r = devm_gpio_request_one(&client->dev, gpio, GPIOF_OUT_INIT_HIGH, - ST21NFCA_GPIO_NAME_EN); - if (r) { - nfc_err(&client->dev, "Failed to request enable pin\n"); - return r; - } - - phy->gpio_ena = gpio; - phy->se_status.is_ese_present = of_property_read_bool(pp, "ese-present"); phy->se_status.is_uicc_present = -- cgit From 682fd6185053875bb16efb1037f3d2ae9680b015 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 7 Mar 2017 12:25:46 +0200 Subject: NFC: st21nfca: Use unified device property API meaningfully Another place in the code that unveils non-tested at all ACPI case. Use unified device property API in meaningful way. Signed-off-by: Andy Shevchenko Signed-off-by: Samuel Ortiz --- drivers/nfc/st21nfca/i2c.c | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index 4946816f0011..02a920ca07c8 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -505,7 +505,6 @@ static int st21nfca_hci_i2c_acpi_request_resources(struct i2c_client *client) { struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); struct device *dev = &client->dev; - u8 tmp; /* Get EN GPIO from ACPI */ phy->gpiod_ena = devm_gpiod_get_index(dev, ST21NFCA_GPIO_NAME_EN, 1, @@ -515,19 +514,6 @@ static int st21nfca_hci_i2c_acpi_request_resources(struct i2c_client *client) return PTR_ERR(phy->gpiod_ena); } - phy->se_status.is_ese_present = false; - phy->se_status.is_uicc_present = false; - - if (device_property_present(dev, "ese-present")) { - device_property_read_u8(dev, "ese-present", &tmp); - phy->se_status.is_ese_present = tmp; - } - - if (device_property_present(dev, "uicc-present")) { - device_property_read_u8(dev, "uicc-present", &tmp); - phy->se_status.is_uicc_present = tmp; - } - return 0; } @@ -535,11 +521,6 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) { struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client); struct device *dev = &client->dev; - struct device_node *pp; - - pp = client->dev.of_node; - if (!pp) - return -ENODEV; /* Get GPIO from device tree */ phy->gpiod_ena = devm_gpiod_get_index(dev, ST21NFCA_GPIO_NAME_EN, 0, @@ -549,11 +530,6 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) return PTR_ERR(phy->gpiod_ena); } - phy->se_status.is_ese_present = - of_property_read_bool(pp, "ese-present"); - phy->se_status.is_uicc_present = - of_property_read_bool(pp, "uicc-present"); - return 0; } @@ -603,6 +579,11 @@ static int st21nfca_hci_i2c_probe(struct i2c_client *client, return -ENODEV; } + phy->se_status.is_ese_present = + device_property_read_bool(&client->dev, "ese-present"); + phy->se_status.is_uicc_present = + device_property_read_bool(&client->dev, "uicc-present"); + r = st21nfca_hci_platform_init(phy); if (r < 0) { nfc_err(&client->dev, "Unable to reboot st21nfca\n"); -- cgit From 3267183c3d1a6612c00961d7334413f9976c751a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 22 Mar 2017 21:20:58 +0200 Subject: NFC: netlink: Use error code from nfc_activate_target() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It looks like a typo to assign a return code to a variable which is not used. Found due to a compiler warning: net/nfc/netlink.c: In function ‘nfc_genl_activate_target’: net/nfc/netlink.c:903:6: warning: variable ‘rc’ set but not used [-Wunused-but-set-variable] int rc; ^~ Signed-off-by: Andy Shevchenko Signed-off-by: Samuel Ortiz --- net/nfc/netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 4c95319dc22b..23a6e01f35ed 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -922,7 +922,7 @@ static int nfc_genl_activate_target(struct sk_buff *skb, struct genl_info *info) rc = nfc_activate_target(dev, target_idx, protocol); nfc_put_device(dev); - return 0; + return rc; } static int nfc_genl_dep_link_up(struct sk_buff *skb, struct genl_info *info) -- cgit From 2891f2d5c13b7386331250d59920bdf8a54bfccb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 22 Mar 2017 21:22:51 +0200 Subject: NFC: Add nfc_dbg() macro In some cases nfc_dbg() is useful. Add such macro to a header. Signed-off-by: Andy Shevchenko Signed-off-by: Samuel Ortiz --- include/net/nfc/nfc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 1a3de8b34ad2..bbdc73a3239d 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -27,6 +27,7 @@ #include #include +#define nfc_dbg(dev, fmt, ...) dev_dbg((dev), "NFC: " fmt, ##__VA_ARGS__) #define nfc_info(dev, fmt, ...) dev_info((dev), "NFC: " fmt, ##__VA_ARGS__) #define nfc_err(dev, fmt, ...) dev_err((dev), "NFC: " fmt, ##__VA_ARGS__) -- cgit From 837eb4d21ecde7bde7a1fe523594fdf509055f3f Mon Sep 17 00:00:00 2001 From: Geoff Lansberry Date: Wed, 21 Dec 2016 23:18:32 -0500 Subject: NFC: trf7970a: add device tree option for 27MHz clock The TRF7970A has configuration options to support hardware designs which use a 27.12MHz clock. This commit adds a device tree option 'clock-frequency' to support configuring the this chip for default 13.56MHz clock or the optional 27.12MHz clock. Acked-by: Rob Herring Acked-by: Mark Greer Signed-off-by: Geoff Lansberry Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/net/nfc/trf7970a.txt | 2 + drivers/nfc/trf7970a.c | 50 +++++++++++++++++----- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/Documentation/devicetree/bindings/net/nfc/trf7970a.txt b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt index 5889a3d133b2..1d186cd9817b 100644 --- a/Documentation/devicetree/bindings/net/nfc/trf7970a.txt +++ b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt @@ -21,6 +21,7 @@ Optional SoC Specific Properties: - t5t-rmb-extra-byte-quirk: Specify that the trf7970a has the erratum where an extra byte is returned by Read Multiple Block commands issued to Type 5 tags. +- clock-frequency: Set to specify that the input frequency to the trf7970a is 13560000Hz or 27120000Hz Example (for ARM-based BeagleBone with TRF7970A on SPI1): @@ -43,6 +44,7 @@ Example (for ARM-based BeagleBone with TRF7970A on SPI1): irq-status-read-quirk; en2-rf-quirk; t5t-rmb-extra-byte-quirk; + clock-frequency = <27120000>; status = "okay"; }; }; diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c index 75079fb16f22..ecb786283ffa 100644 --- a/drivers/nfc/trf7970a.c +++ b/drivers/nfc/trf7970a.c @@ -124,6 +124,9 @@ NFC_PROTO_ISO15693_MASK | NFC_PROTO_NFC_DEP_MASK) #define TRF7970A_AUTOSUSPEND_DELAY 30000 /* 30 seconds */ +#define TRF7970A_13MHZ_CLOCK_FREQUENCY 13560000 +#define TRF7970A_27MHZ_CLOCK_FREQUENCY 27120000 + #define TRF7970A_RX_SKB_ALLOC_SIZE 256 @@ -1056,12 +1059,11 @@ static int trf7970a_init(struct trf7970a *trf) trf->chip_status_ctrl &= ~TRF7970A_CHIP_STATUS_RF_ON; - ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL, 0); + ret = trf7970a_write(trf, TRF7970A_MODULATOR_SYS_CLK_CTRL, + trf->modulator_sys_clk_ctrl); if (ret) goto err_out; - trf->modulator_sys_clk_ctrl = 0; - ret = trf7970a_write(trf, TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS, TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLH_96 | TRF7970A_ADJUTABLE_FIFO_IRQ_LEVELS_WLL_32); @@ -1181,27 +1183,37 @@ static int trf7970a_in_config_rf_tech(struct trf7970a *trf, int tech) switch (tech) { case NFC_DIGITAL_RF_TECH_106A: trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_14443A_106; - trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK; + trf->modulator_sys_clk_ctrl = + (trf->modulator_sys_clk_ctrl & 0xf8) | + TRF7970A_MODULATOR_DEPTH_OOK; trf->guard_time = TRF7970A_GUARD_TIME_NFCA; break; case NFC_DIGITAL_RF_TECH_106B: trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_14443B_106; - trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10; + trf->modulator_sys_clk_ctrl = + (trf->modulator_sys_clk_ctrl & 0xf8) | + TRF7970A_MODULATOR_DEPTH_ASK10; trf->guard_time = TRF7970A_GUARD_TIME_NFCB; break; case NFC_DIGITAL_RF_TECH_212F: trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_FELICA_212; - trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10; + trf->modulator_sys_clk_ctrl = + (trf->modulator_sys_clk_ctrl & 0xf8) | + TRF7970A_MODULATOR_DEPTH_ASK10; trf->guard_time = TRF7970A_GUARD_TIME_NFCF; break; case NFC_DIGITAL_RF_TECH_424F: trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_FELICA_424; - trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10; + trf->modulator_sys_clk_ctrl = + (trf->modulator_sys_clk_ctrl & 0xf8) | + TRF7970A_MODULATOR_DEPTH_ASK10; trf->guard_time = TRF7970A_GUARD_TIME_NFCF; break; case NFC_DIGITAL_RF_TECH_ISO15693: trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_15693_SGL_1OF4_2648; - trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK; + trf->modulator_sys_clk_ctrl = + (trf->modulator_sys_clk_ctrl & 0xf8) | + TRF7970A_MODULATOR_DEPTH_OOK; trf->guard_time = TRF7970A_GUARD_TIME_15693; break; default: @@ -1571,17 +1583,23 @@ static int trf7970a_tg_config_rf_tech(struct trf7970a *trf, int tech) trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_NFC_NFC_CE_MODE | TRF7970A_ISO_CTRL_NFC_CE | TRF7970A_ISO_CTRL_NFC_CE_14443A; - trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_OOK; + trf->modulator_sys_clk_ctrl = + (trf->modulator_sys_clk_ctrl & 0xf8) | + TRF7970A_MODULATOR_DEPTH_OOK; break; case NFC_DIGITAL_RF_TECH_212F: trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_NFC_NFC_CE_MODE | TRF7970A_ISO_CTRL_NFC_NFCF_212; - trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10; + trf->modulator_sys_clk_ctrl = + (trf->modulator_sys_clk_ctrl & 0xf8) | + TRF7970A_MODULATOR_DEPTH_ASK10; break; case NFC_DIGITAL_RF_TECH_424F: trf->iso_ctrl_tech = TRF7970A_ISO_CTRL_NFC_NFC_CE_MODE | TRF7970A_ISO_CTRL_NFC_NFCF_424; - trf->modulator_sys_clk_ctrl = TRF7970A_MODULATOR_DEPTH_ASK10; + trf->modulator_sys_clk_ctrl = + (trf->modulator_sys_clk_ctrl & 0xf8) | + TRF7970A_MODULATOR_DEPTH_ASK10; break; default: dev_dbg(trf->dev, "Unsupported rf technology: %d\n", tech); @@ -1990,6 +2008,7 @@ static int trf7970a_probe(struct spi_device *spi) struct device_node *np = spi->dev.of_node; struct trf7970a *trf; int uvolts, autosuspend_delay, ret; + u32 clk_freq = TRF7970A_13MHZ_CLOCK_FREQUENCY; if (!np) { dev_err(&spi->dev, "No Device Tree entry\n"); @@ -2045,6 +2064,15 @@ static int trf7970a_probe(struct spi_device *spi) } } + of_property_read_u32(np, "clock-frequency", &clk_freq); + if ((clk_freq != TRF7970A_27MHZ_CLOCK_FREQUENCY) || + (clk_freq != TRF7970A_13MHZ_CLOCK_FREQUENCY)) { + dev_err(trf->dev, + "clock-frequency (%u Hz) unsupported\n", + clk_freq); + return -EINVAL; + } + if (of_property_read_bool(np, "en2-rf-quirk")) trf->quirks |= TRF7970A_QUIRK_EN2_MUST_STAY_LOW; -- cgit From 49d22c70aaf07cd7e33346b247f5df3ddb072ee0 Mon Sep 17 00:00:00 2001 From: Geoff Lansberry Date: Wed, 21 Dec 2016 23:18:33 -0500 Subject: NFC: trf7970a: Add device tree option of 1.8 Volt IO voltage The TRF7970A has configuration options for supporting hardware designs with 1.8 Volt or 3.3 Volt IO. This commit adds a device tree option, using a fixed regulator binding, for setting the io voltage to match the hardware configuration. If no option is supplied it defaults to 3.3 volt configuration. Acked-by: Rob Herring Acked-by: Mark Greer Signed-off-by: Geoff Lansberry Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/net/nfc/trf7970a.txt | 2 ++ drivers/nfc/trf7970a.c | 26 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/nfc/trf7970a.txt b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt index 1d186cd9817b..c627bbb3009e 100644 --- a/Documentation/devicetree/bindings/net/nfc/trf7970a.txt +++ b/Documentation/devicetree/bindings/net/nfc/trf7970a.txt @@ -21,6 +21,7 @@ Optional SoC Specific Properties: - t5t-rmb-extra-byte-quirk: Specify that the trf7970a has the erratum where an extra byte is returned by Read Multiple Block commands issued to Type 5 tags. +- vdd-io-supply: Regulator specifying voltage for vdd-io - clock-frequency: Set to specify that the input frequency to the trf7970a is 13560000Hz or 27120000Hz Example (for ARM-based BeagleBone with TRF7970A on SPI1): @@ -40,6 +41,7 @@ Example (for ARM-based BeagleBone with TRF7970A on SPI1): <&gpio2 5 GPIO_ACTIVE_LOW>; vin-supply = <&ldo3_reg>; vin-voltage-override = <5000000>; + vdd-io-supply = <&ldo2_reg>; autosuspend-delay = <30000>; irq-status-read-quirk; en2-rf-quirk; diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c index ecb786283ffa..2d1c8ca6e679 100644 --- a/drivers/nfc/trf7970a.c +++ b/drivers/nfc/trf7970a.c @@ -444,6 +444,7 @@ struct trf7970a { u8 iso_ctrl_tech; u8 modulator_sys_clk_ctrl; u8 special_fcn_reg1; + u8 io_ctrl; unsigned int guard_time; int technology; int framing; @@ -1051,6 +1052,11 @@ static int trf7970a_init(struct trf7970a *trf) if (ret) goto err_out; + ret = trf7970a_write(trf, TRF7970A_REG_IO_CTRL, + trf->io_ctrl | TRF7970A_REG_IO_CTRL_VRS(0x1)); + if (ret) + goto err_out; + ret = trf7970a_write(trf, TRF7970A_NFC_TARGET_LEVEL, 0); if (ret) goto err_out; @@ -1767,7 +1773,7 @@ static int _trf7970a_tg_listen(struct nfc_digital_dev *ddev, u16 timeout, goto out_err; ret = trf7970a_write(trf, TRF7970A_REG_IO_CTRL, - TRF7970A_REG_IO_CTRL_VRS(0x1)); + trf->io_ctrl | TRF7970A_REG_IO_CTRL_VRS(0x1)); if (ret) goto out_err; @@ -2107,6 +2113,24 @@ static int trf7970a_probe(struct spi_device *spi) if (uvolts > 4000000) trf->chip_status_ctrl = TRF7970A_CHIP_STATUS_VRS5_3; + trf->regulator = devm_regulator_get(&spi->dev, "vdd-io"); + if (IS_ERR(trf->regulator)) { + ret = PTR_ERR(trf->regulator); + dev_err(trf->dev, "Can't get VDD_IO regulator: %d\n", ret); + goto err_destroy_lock; + } + + ret = regulator_enable(trf->regulator); + if (ret) { + dev_err(trf->dev, "Can't enable VDD_IO: %d\n", ret); + goto err_destroy_lock; + } + + if (regulator_get_voltage(trf->regulator) == 1800000) { + trf->io_ctrl = TRF7970A_REG_IO_CTRL_IO_LOW; + dev_dbg(trf->dev, "trf7970a config vdd_io to 1.8V\n"); + } + trf->ddev = nfc_digital_allocate_device(&trf7970a_nfc_ops, TRF7970A_SUPPORTED_PROTOCOLS, NFC_DIGITAL_DRV_CAPS_IN_CRC | -- cgit From 1c92af00a56032c10d1a43277b39515393860b02 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Thu, 23 Mar 2017 11:29:28 +0100 Subject: brcmfmac: update BRCMFMAC symbol description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For quite some time now brcmfmac supports 802.11ac chipsets and it's not limited to embedded devices only. There are even standalone PCIe cards based on BCM43602 or BCM4366. Signed-off-by: Rafał Miłecki Reviewed-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/Kconfig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig index ab42b1fea03c..9d99eb42d917 100644 --- a/drivers/net/wireless/broadcom/brcm80211/Kconfig +++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig @@ -18,14 +18,14 @@ config BRCMSMAC module, the driver will be called brcmsmac.ko. config BRCMFMAC - tristate "Broadcom IEEE802.11n embedded FullMAC WLAN driver" + tristate "Broadcom FullMAC WLAN driver" depends on CFG80211 select BRCMUTIL ---help--- - This module adds support for embedded wireless adapters based on - Broadcom IEEE802.11n FullMAC chipsets. It has to work with at least - one of the bus interface support. If you choose to build a module, - it'll be called brcmfmac.ko. + This module adds support for wireless adapters based on Broadcom + FullMAC chipsets. It has to work with at least one of the bus + interface support. If you choose to build a module, it'll be called + brcmfmac.ko. config BRCMFMAC_PROTO_BCDC bool -- cgit From 182f569660548d16af895af9fede54367c377f47 Mon Sep 17 00:00:00 2001 From: Ganapathi Bhat Date: Mon, 27 Mar 2017 18:27:44 +0530 Subject: mwifiex: Support USB interrupt endpoint for command response/event USB firmware added support for sending command response/event through interrupt endpoint, to enhance RX throughput. Added corresponding changes required to support this feature. This change takes care of backward compatibility with older firmware. Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/usb.c | 45 +++++++++++++++++++++++------- drivers/net/wireless/marvell/mwifiex/usb.h | 4 +++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 9cf3334adf4d..2f7705c50161 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -306,9 +306,17 @@ static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size) } } - usb_fill_bulk_urb(ctx->urb, card->udev, - usb_rcvbulkpipe(card->udev, ctx->ep), ctx->skb->data, - size, mwifiex_usb_rx_complete, (void *)ctx); + if (card->rx_cmd_ep == ctx->ep && + card->rx_cmd_ep_type == USB_ENDPOINT_XFER_INT) + usb_fill_int_urb(ctx->urb, card->udev, + usb_rcvintpipe(card->udev, ctx->ep), + ctx->skb->data, size, mwifiex_usb_rx_complete, + (void *)ctx, card->rx_cmd_interval); + else + usb_fill_bulk_urb(ctx->urb, card->udev, + usb_rcvbulkpipe(card->udev, ctx->ep), + ctx->skb->data, size, mwifiex_usb_rx_complete, + (void *)ctx); if (card->rx_cmd_ep == ctx->ep) atomic_inc(&card->rx_cmd_urb_pending); @@ -424,10 +432,13 @@ static int mwifiex_usb_probe(struct usb_interface *intf, epd = &iface_desc->endpoint[i].desc; if (usb_endpoint_dir_in(epd) && usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && - usb_endpoint_xfer_bulk(epd)) { - pr_debug("info: bulk IN: max pkt size: %d, addr: %d\n", + (usb_endpoint_xfer_bulk(epd) || + usb_endpoint_xfer_int(epd))) { + card->rx_cmd_ep_type = usb_endpoint_type(epd); + card->rx_cmd_interval = epd->bInterval; + pr_debug("info: Rx CMD/EVT:: max pkt size: %d, addr: %d, ep_type: %d\n", le16_to_cpu(epd->wMaxPacketSize), - epd->bEndpointAddress); + epd->bEndpointAddress, card->rx_cmd_ep_type); card->rx_cmd_ep = usb_endpoint_num(epd); atomic_set(&card->rx_cmd_urb_pending, 0); } @@ -461,10 +472,16 @@ static int mwifiex_usb_probe(struct usb_interface *intf, } if (usb_endpoint_dir_out(epd) && usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && - usb_endpoint_xfer_bulk(epd)) { + (usb_endpoint_xfer_bulk(epd) || + usb_endpoint_xfer_int(epd))) { + card->tx_cmd_ep_type = usb_endpoint_type(epd); + card->tx_cmd_interval = epd->bInterval; pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", le16_to_cpu(epd->wMaxPacketSize), epd->bEndpointAddress); + pr_debug("info: Tx CMD:: max pkt size: %d, addr: %d, ep_type: %d\n", + le16_to_cpu(epd->wMaxPacketSize), + epd->bEndpointAddress, card->tx_cmd_ep_type); card->tx_cmd_ep = usb_endpoint_num(epd); atomic_set(&card->tx_cmd_urb_pending, 0); card->bulk_out_maxpktsize = @@ -884,9 +901,17 @@ static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, context->skb = skb; tx_urb = context->urb; - usb_fill_bulk_urb(tx_urb, card->udev, usb_sndbulkpipe(card->udev, ep), - data, skb->len, mwifiex_usb_tx_complete, - (void *)context); + if (ep == card->tx_cmd_ep && + card->tx_cmd_ep_type == USB_ENDPOINT_XFER_INT) + usb_fill_int_urb(tx_urb, card->udev, + usb_sndintpipe(card->udev, ep), data, + skb->len, mwifiex_usb_tx_complete, + (void *)context, card->tx_cmd_interval); + else + usb_fill_bulk_urb(tx_urb, card->udev, + usb_sndbulkpipe(card->udev, ep), data, + skb->len, mwifiex_usb_tx_complete, + (void *)context); tx_urb->transfer_flags |= URB_ZERO_PACKET; diff --git a/drivers/net/wireless/marvell/mwifiex/usb.h b/drivers/net/wireless/marvell/mwifiex/usb.h index 16017aeb8cfe..e36bd63172ff 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.h +++ b/drivers/net/wireless/marvell/mwifiex/usb.h @@ -90,6 +90,10 @@ struct usb_card_rec { struct urb_context tx_cmd; u8 mc_resync_flag; struct usb_tx_data_port port[MWIFIEX_TX_DATA_PORT]; + int rx_cmd_ep_type; + u8 rx_cmd_interval; + int tx_cmd_ep_type; + u8 tx_cmd_interval; }; struct fw_header { -- cgit From 36491b152c30fe0e804385e1ee9068be2c6d1bfa Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Mon, 27 Mar 2017 18:36:51 +0530 Subject: mwifiex: enable auto deep sleep mode for USB chipsets Chip goes into low power state when this feature is enabled. This was already enabled for SDIO and PCIe interface based chipsets. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sta_cmd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index f6056ab28efe..83916c1439af 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -2364,8 +2364,7 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) if (ret) return -1; - if (!disable_auto_ds && - first_sta && priv->adapter->iface_type != MWIFIEX_USB && + if (!disable_auto_ds && first_sta && priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { /* Enable auto deep sleep */ auto_ds.auto_ds = DEEP_SLEEP_ON; -- cgit From 62c50a34883c6b821d816b6a661e5d47c09d42b2 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Tue, 28 Mar 2017 11:43:24 +0100 Subject: brcmfmac: wrap brcmf_fws_init into bcdc layer Create a new protocol layer interface brcmf_proto_init_cb for protocol layer to finish initialzation after core module components(fweh and etc.) are initialized. Signed-off-by: Franky Lin Reviewed-by: Arend Van Spriel Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c | 7 +++++++ drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 2 +- drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h | 9 +++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index 92eafccf087b..a07c49fa745a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -417,6 +417,12 @@ brcmf_proto_bcdc_reset_if(struct brcmf_if *ifp) brcmf_fws_reset_interface(ifp); } +static int +brcmf_proto_bcdc_init_done(struct brcmf_pub *drvr) +{ + return brcmf_fws_init(drvr); +} + int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { struct brcmf_bcdc *bcdc; @@ -443,6 +449,7 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) drvr->proto->add_if = brcmf_proto_bcdc_add_if; drvr->proto->del_if = brcmf_proto_bcdc_del_if; drvr->proto->reset_if = brcmf_proto_bcdc_reset_if; + drvr->proto->init_done = brcmf_proto_bcdc_init_done; drvr->proto->pd = bcdc; drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 60c6c7839cc2..0c0536527839 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -986,7 +986,7 @@ int brcmf_bus_started(struct device *dev) } brcmf_feat_attach(drvr); - ret = brcmf_fws_init(drvr); + ret = brcmf_proto_init_done(drvr); if (ret < 0) goto fail; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h index 3048ed529f95..2404f8a7c31c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h @@ -47,6 +47,7 @@ struct brcmf_proto { void (*add_if)(struct brcmf_if *ifp); void (*del_if)(struct brcmf_if *ifp); void (*reset_if)(struct brcmf_if *ifp); + int (*init_done)(struct brcmf_pub *drvr); void *pd; }; @@ -145,4 +146,12 @@ brcmf_proto_reset_if(struct brcmf_pub *drvr, struct brcmf_if *ifp) drvr->proto->reset_if(ifp); } +static inline int +brcmf_proto_init_done(struct brcmf_pub *drvr) +{ + if (!drvr->proto->init_done) + return 0; + return drvr->proto->init_done(drvr); +} + #endif /* BRCMFMAC_PROTO_H */ -- cgit From 8f9dd1a974380ebe2d7bf82df4e6ba6bfb89c575 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Tue, 28 Mar 2017 11:43:25 +0100 Subject: brcmfmac: move brcmf_fws_deinit to bcdc layer Move brcmf_fws_deinit into brcmf_proto_bcdc_detach since it is a bcdc exclusive feature. Signed-off-by: Franky Lin Reviewed-by: Arend Van Spriel Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c | 1 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index a07c49fa745a..24da6276d29d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -464,6 +464,7 @@ fail: void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) { + brcmf_fws_deinit(drvr); kfree(drvr->proto->pd); drvr->proto->pd = NULL; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 0c0536527839..c2e98e77ca82 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -32,7 +32,6 @@ #include "p2p.h" #include "cfg80211.h" #include "fwil.h" -#include "fwsignal.h" #include "feature.h" #include "proto.h" #include "pcie.h" @@ -1034,10 +1033,6 @@ fail: brcmf_cfg80211_detach(drvr->config); drvr->config = NULL; } - if (drvr->fws) { - brcmf_proto_del_if(ifp->drvr, ifp); - brcmf_fws_deinit(drvr); - } brcmf_net_detach(ifp->ndev, false); if (p2p_ifp) brcmf_net_detach(p2p_ifp->ndev, false); @@ -1103,8 +1098,6 @@ void brcmf_detach(struct device *dev) brcmf_cfg80211_detach(drvr->config); - brcmf_fws_deinit(drvr); - brcmf_bus_stop(drvr->bus_if); brcmf_proto_detach(drvr); -- cgit From 0cc0236cf713a9ecfcf902e35bd098bc179265a8 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Tue, 28 Mar 2017 11:43:26 +0100 Subject: brcmfmac: add support to move wiphy instance into network namespace To support network namespace the driver must assure all created network interfaces are in the same namespace as the wiphy instance and flag the support using WIPHY_FLAG_NETNS_OK. Verified using two terminals: Terminal 1 Terminal 2 -------------------------- --------------------------------- # ip netns add brcm-wifi # iw dev phy#0 Interface wlan3 ifindex 11 wdev 0x1 # ip netns exec brcm-wifi bash # iw dev # echo $$ 20337 # iw phy0 set netns 20337 # iw dev phy#0 Interface wlan3 ifindex 11 wdev 0x2 # iw phy0 interface add wl3.ap type __ap # iw dev phy#0 Interface wl3.ap ifindex 2 wdev 0x3 Interface wlan3 ifindex 11 wdev 0x2 # iw dev # iw phy0 set netns 1 # iw dev # iw dev phy#0 Interface wl3.ap ifindex 2 wdev 0x5 Interface wlan3 ifindex 11 wdev 0x4 Note: increasing wdev identifier above indicates issue in cfg80211 which is addressed separately. Tested-by: Mark Asselstine Signed-off-by: Arend van Spriel Reviewed-by: Franky Lin Reviewed-by: Hante Meuleman Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 3 ++- drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 4e1ca69f6aa1..89ac12437c92 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -6453,7 +6453,8 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) BIT(NL80211_BSS_SELECT_ATTR_BAND_PREF) | BIT(NL80211_BSS_SELECT_ATTR_RSSI_ADJUST); - wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT | + wiphy->flags |= WIPHY_FLAG_NETNS_OK | + WIPHY_FLAG_PS_ON_BY_DEFAULT | WIPHY_FLAG_OFFCHAN_TX | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index c2e98e77ca82..24118ce72b4f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -475,8 +475,9 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) ndev->needed_headroom += drvr->hdrlen; ndev->ethtool_ops = &brcmf_ethtool_ops; - /* set the mac address */ + /* set the mac address & netns */ memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN); + dev_net_set(ndev, wiphy_net(cfg_to_wiphy(drvr->config))); INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list); INIT_WORK(&ifp->ndoffload_work, _brcmf_update_ndtable); -- cgit From 49fe9b59f0e9b750f173fbe44637c436ba1030d2 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Tue, 28 Mar 2017 11:43:27 +0100 Subject: brcmfmac: restore bus state when enter_D3 fails In brcmf_pcie_suspend() we inform the firmware on the device that it will enter in D3 state. Before this is done we already bring down the bus state. However, When entering D3 fails we abort the suspend and the bus state need to be restored. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 6fae4cf3f6ab..f36b96dc6acd 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -1877,6 +1877,7 @@ static int brcmf_pcie_pm_enter_D3(struct device *dev) BRCMF_PCIE_MBDATA_TIMEOUT); if (!devinfo->mbdata_completed) { brcmf_err("Timeout on response for entering D3 substate\n"); + brcmf_bus_change_state(bus, BRCMF_BUS_UP); return -EIO; } -- cgit From 78b9ccb81377ba908b2c18daf6e1a7beddc281e3 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Tue, 28 Mar 2017 11:43:28 +0100 Subject: brcmfmac: no need for d11inf instance in brcmf_pno_start_sched_scan() In brcmf_pno_start_sched_scan() a local variable is declared and assigned for struct brcmu_d11inf. However, there is no other reference to it so it is unnecessary. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c index 9a25e79a46cf..6c3bde83d070 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -182,7 +182,6 @@ int brcmf_pno_clean(struct brcmf_if *ifp) int brcmf_pno_start_sched_scan(struct brcmf_if *ifp, struct cfg80211_sched_scan_request *req) { - struct brcmu_d11inf *d11inf; struct brcmf_pno_config_le pno_cfg; struct cfg80211_ssid *ssid; u16 chan; @@ -209,7 +208,6 @@ int brcmf_pno_start_sched_scan(struct brcmf_if *ifp, } /* configure channels to use */ - d11inf = &ifp->drvr->config->d11inf; for (i = 0; i < req->n_channels; i++) { chan = req->channels[i]->hw_value; pno_cfg.channel_list[i] = cpu_to_le16(chan); -- cgit From ce8fad9a1f09009ec3918a99685d9e3176f50ce3 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 28 Mar 2017 16:59:32 -0700 Subject: mwifiex: fix use-after-free for FW reinit errors If we fail to reinit the FW when resetting the device (in the synchronous version of mwifiex_init_hw_fw() -> mwifiex_fw_dpc()), mwifiex_fw_dpc() will tear down the interface and free up the adapter. But we don't actually check for all failure cases of mwifiex_fw_dpc(), so some of them fall through and dereference adapter->fw_done with a freed adapter, causing a use-after-free bug. In any case, mwifiex_fw_dpc() will always signal FW completion -- in the error OR success case -- so at best, this was repeat work. Let's not do it. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 30f49944661f..98c83453ba5b 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -1475,7 +1475,6 @@ mwifiex_reinit_sw(struct mwifiex_adapter *adapter) } mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); - complete_all(adapter->fw_done); return 0; err_init_fw: -- cgit From 755b37c93a069ff0882411630a06e90b3193d092 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 28 Mar 2017 16:59:33 -0700 Subject: mwifiex: catch mwifiex_fw_dpc() errors properly in reset When resetting the device, we take a synchronous firmware-loading code path, which borrows a lot from the asynchronous path used at probe time. We don't catch errors correctly though, which means that in the PCIe driver, we may try to dereference the 'adapter' struct after mwifiex_fw_dpc() has freed it. See this (erronous) print in mwifiex_pcie_reset_notify(): mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); Let's instead refactor the synchronous (or "!req_fw_nowait") path so that we propagate errors and handle them properly. This fixes a use-after-free issue in the PCIe driver, as well as a misleading debug message ("successful"). It looks like the SDIO driver doesn't have these problems, since it doesn't do anything after mwifiex_reinit_sw(). Fixes: 4c5dae59d2e9 ("mwifiex: add PCIe function level reset support") Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 33 +++++++++++++++++++---------- drivers/net/wireless/marvell/mwifiex/pcie.c | 7 +++++- drivers/net/wireless/marvell/mwifiex/sdio.c | 5 ++++- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 98c83453ba5b..0dfbac850689 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -512,7 +512,7 @@ static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter) * - Download the correct firmware to card * - Issue the init commands to firmware */ -static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) +static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context) { int ret; char fmt[64]; @@ -665,11 +665,18 @@ done: mwifiex_free_adapter(adapter); /* Tell all current and future waiters we're finished */ complete_all(fw_done); - return; + + return init_failed ? -EIO : 0; +} + +static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) +{ + _mwifiex_fw_dpc(firmware, context); } /* - * This function initializes the hardware and gets firmware. + * This function gets the firmware and (if called asynchronously) kicks off the + * HW init when done. */ static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter, bool req_fw_nowait) @@ -692,20 +699,15 @@ static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter, ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name, adapter->dev, GFP_KERNEL, adapter, mwifiex_fw_dpc); - if (ret < 0) - mwifiex_dbg(adapter, ERROR, - "request_firmware_nowait error %d\n", ret); } else { ret = request_firmware(&adapter->firmware, adapter->fw_name, adapter->dev); - if (ret < 0) - mwifiex_dbg(adapter, ERROR, - "request_firmware error %d\n", ret); - else - mwifiex_fw_dpc(adapter->firmware, (void *)adapter); } + if (ret < 0) + mwifiex_dbg(adapter, ERROR, "request_firmware%s error %d\n", + req_fw_nowait ? "_nowait" : "", ret); return ret; } @@ -1427,6 +1429,8 @@ EXPORT_SYMBOL_GPL(mwifiex_shutdown_sw); int mwifiex_reinit_sw(struct mwifiex_adapter *adapter) { + int ret; + mwifiex_init_lock_list(adapter); if (adapter->if_ops.up_dev) adapter->if_ops.up_dev(adapter); @@ -1473,6 +1477,13 @@ mwifiex_reinit_sw(struct mwifiex_adapter *adapter) "%s: firmware init failed\n", __func__); goto err_init_fw; } + + /* _mwifiex_fw_dpc() does its own cleanup */ + ret = _mwifiex_fw_dpc(adapter->firmware, adapter); + if (ret) { + pr_err("Failed to bring up adapter: %d\n", ret); + return ret; + } mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); return 0; diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index e381deff37f3..f45ab125cd0d 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -350,6 +350,7 @@ static void mwifiex_pcie_reset_notify(struct pci_dev *pdev, bool prepare) { struct pcie_service_card *card = pci_get_drvdata(pdev); struct mwifiex_adapter *adapter = card->adapter; + int ret; if (!adapter) { dev_err(&pdev->dev, "%s: adapter structure is not valid\n", @@ -376,7 +377,11 @@ static void mwifiex_pcie_reset_notify(struct pci_dev *pdev, bool prepare) * and firmware including firmware redownload */ adapter->surprise_removed = false; - mwifiex_reinit_sw(adapter); + ret = mwifiex_reinit_sw(adapter); + if (ret) { + dev_err(&pdev->dev, "reinit failed: %d\n", ret); + return; + } } mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); } diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 58d3da09cfbd..424532b81c2d 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -2196,6 +2196,7 @@ static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; struct sdio_func *func = card->func; + int ret; mwifiex_shutdown_sw(adapter); @@ -2210,7 +2211,9 @@ static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter) clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &card->work_flags); clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags); - mwifiex_reinit_sw(adapter); + ret = mwifiex_reinit_sw(adapter); + if (ret) + dev_err(&func->dev, "reinit failed: %d\n", ret); } /* This function read/write firmware */ -- cgit From 5ea80789616009607fa1211750fe198a256c32b2 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 3 Apr 2017 10:47:58 +0100 Subject: wlcore: fix spelling mistakes in wl1271_warning trivial fix to spelling mistakes in wl1271_warning error message, change iligal to invalid and opperation to operation. Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/ti/wlcore/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 58e148d7bc7b..de7e2a5fdffa 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -1249,7 +1249,7 @@ static ssize_t fw_logger_write(struct file *file, } if (wl->conf.fwlog.output == 0) { - wl1271_warning("iligal opperation - fw logger disabled by default, please change mode via wlconf"); + wl1271_warning("invalid operation - fw logger disabled by default, please change mode via wlconf"); return -EINVAL; } -- cgit From 38c51d03cef47b5d33de0660ba24e371d1724874 Mon Sep 17 00:00:00 2001 From: Karthik Ananthapadmanabha Date: Mon, 3 Apr 2017 20:06:50 +0530 Subject: mwifiex: add missing IEs related to TDLS operation In mwifiex,IEs such as supported channels, supported operating classes 20/40 BSS COexistence are missing and also extra QOS capabilities IE is added during TDLS discovery response, TDLS setup request and TDLS setupresponse. This patch adds require IEs and also removes extra IE. Signed-off-by: Karthik Ananthapadmanabha Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/ioctl.h | 2 ++ drivers/net/wireless/marvell/mwifiex/tdls.c | 49 ++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/ioctl.h b/drivers/net/wireless/marvell/mwifiex/ioctl.h index 536ab834b126..48e154e1865d 100644 --- a/drivers/net/wireless/marvell/mwifiex/ioctl.h +++ b/drivers/net/wireless/marvell/mwifiex/ioctl.h @@ -91,6 +91,8 @@ struct wep_key { #define MWIFIEX_TDLS_DEF_QOS_CAPAB 0xf #define MWIFIEX_PRIO_BK 2 #define MWIFIEX_PRIO_VI 5 +#define MWIFIEX_SUPPORTED_CHANNELS 2 +#define MWIFIEX_OPERATING_CLASSES 16 struct mwifiex_uap_bss_param { u8 channel; diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c index e228c03f98f6..6e507c99e792 100644 --- a/drivers/net/wireless/marvell/mwifiex/tdls.c +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -431,6 +431,41 @@ mwifiex_add_wmm_info_ie(struct mwifiex_private *priv, struct sk_buff *skb, *buf++ = qosinfo; /* U-APSD no in use */ } +static void mwifiex_tdls_add_bss_co_2040(struct sk_buff *skb) +{ + struct ieee_types_bss_co_2040 *bssco; + + bssco = (void *)skb_put(skb, sizeof(struct ieee_types_bss_co_2040)); + bssco->ieee_hdr.element_id = WLAN_EID_BSS_COEX_2040; + bssco->ieee_hdr.len = sizeof(struct ieee_types_bss_co_2040) - + sizeof(struct ieee_types_header); + bssco->bss_2040co = 0x01; +} + +static void mwifiex_tdls_add_supported_chan(struct sk_buff *skb) +{ + struct ieee_types_generic *supp_chan; + u8 chan_supp[] = {1, 11}; + + supp_chan = (void *)skb_put(skb, (sizeof(struct ieee_types_header) + + sizeof(chan_supp))); + supp_chan->ieee_hdr.element_id = WLAN_EID_SUPPORTED_CHANNELS; + supp_chan->ieee_hdr.len = sizeof(chan_supp); + memcpy(supp_chan->data, chan_supp, sizeof(chan_supp)); +} + +static void mwifiex_tdls_add_oper_class(struct sk_buff *skb) +{ + struct ieee_types_generic *reg_class; + u8 rc_list[] = {1, + 1, 2, 3, 4, 12, 22, 23, 24, 25, 27, 28, 29, 30, 32, 33}; + reg_class = (void *)skb_put(skb, (sizeof(struct ieee_types_header) + + sizeof(rc_list))); + reg_class->ieee_hdr.element_id = WLAN_EID_SUPPORTED_REGULATORY_CLASSES; + reg_class->ieee_hdr.len = sizeof(rc_list); + memcpy(reg_class->data, rc_list, sizeof(rc_list)); +} + static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, const u8 *peer, u8 action_code, u8 dialog_token, @@ -484,7 +519,9 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, } mwifiex_tdls_add_ext_capab(priv, skb); - mwifiex_tdls_add_qos_capab(skb); + mwifiex_tdls_add_bss_co_2040(skb); + mwifiex_tdls_add_supported_chan(skb); + mwifiex_tdls_add_oper_class(skb); mwifiex_add_wmm_info_ie(priv, skb, 0); break; @@ -522,7 +559,9 @@ static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, } mwifiex_tdls_add_ext_capab(priv, skb); - mwifiex_tdls_add_qos_capab(skb); + mwifiex_tdls_add_bss_co_2040(skb); + mwifiex_tdls_add_supported_chan(skb); + mwifiex_tdls_add_oper_class(skb); mwifiex_add_wmm_info_ie(priv, skb, 0); break; @@ -612,6 +651,9 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, sizeof(struct ieee_types_bss_co_2040) + sizeof(struct ieee80211_ht_operation) + sizeof(struct ieee80211_tdls_lnkie) + + (2 * (sizeof(struct ieee_types_header))) + + MWIFIEX_SUPPORTED_CHANNELS + + MWIFIEX_OPERATING_CLASSES + sizeof(struct ieee80211_wmm_param_ie) + extra_ies_len; @@ -760,7 +802,10 @@ mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, } mwifiex_tdls_add_ext_capab(priv, skb); + mwifiex_tdls_add_bss_co_2040(skb); + mwifiex_tdls_add_supported_chan(skb); mwifiex_tdls_add_qos_capab(skb); + mwifiex_tdls_add_oper_class(skb); break; default: mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS action frame type\n"); -- cgit From 27a31a60a4de4c1b45e371152bb6e701e1a8cc40 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 3 Apr 2017 13:41:31 -0500 Subject: rtlwifi: btcoex: remove unused functions A number of functions in the Bluetooth Coexistence routines are not used, and can be removed. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8192e2ant.c | 592 +------------ .../realtek/rtlwifi/btcoexist/halbtc8723b1ant.c | 589 ------------- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 897 +------------------ .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 329 +------ .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 959 +-------------------- 5 files changed, 27 insertions(+), 3339 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c index ffa1f438424d..113eea8e25eb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c @@ -376,7 +376,7 @@ static void halbtc8192e2ant_limited_tx(struct btc_coexist *btcoexist, coex_dm->cur_sstype, ra_masktype); halbtc8192e2ant_Updatera_mask(btcoexist, force_exec, disra_mask); -btc8192e2ant_autorate_fallback_retry(btcoexist, force_exec, arfr_type); + btc8192e2ant_autorate_fallback_retry(btcoexist, force_exec, arfr_type); halbtc8192e2ant_retrylimit(btcoexist, force_exec, retrylimit_type); halbtc8192e2ant_ampdu_maxtime(btcoexist, force_exec, ampdutime_type); } @@ -1573,585 +1573,6 @@ static bool halbtc8192e2ant_is_common_action(struct btc_coexist *btcoexist) return common; } -static void btc8192e_int1(struct btc_coexist *btcoexist, bool tx_pause, - int result) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - if (tx_pause) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 1\n"); - - if (coex_dm->cur_ps_tdma == 71) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); - coex_dm->tdma_adj_type = 5; - } else if (coex_dm->cur_ps_tdma == 1) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); - coex_dm->tdma_adj_type = 5; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 4) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } - if (coex_dm->cur_ps_tdma == 9) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 13); - coex_dm->tdma_adj_type = 13; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 12) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - - if (result == -1) { - if (coex_dm->cur_ps_tdma == 5) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } else if (coex_dm->cur_ps_tdma == 13) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 8) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); - coex_dm->tdma_adj_type = 5; - } else if (coex_dm->cur_ps_tdma == 16) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 13); - coex_dm->tdma_adj_type = 13; - } - } - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 0\n"); - if (coex_dm->cur_ps_tdma == 5) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 71); - coex_dm->tdma_adj_type = 71; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 8) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } - if (coex_dm->cur_ps_tdma == 13) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 16) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - - if (result == -1) { - if (coex_dm->cur_ps_tdma == 71) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 1); - coex_dm->tdma_adj_type = 1; - } else if (coex_dm->cur_ps_tdma == 1) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } else if (coex_dm->cur_ps_tdma == 9) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 4) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 1); - coex_dm->tdma_adj_type = 1; - } else if (coex_dm->cur_ps_tdma == 1) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 71); - coex_dm->tdma_adj_type = 71; - } else if (coex_dm->cur_ps_tdma == 12) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - } - } - } -} - -static void btc8192e_int2(struct btc_coexist *btcoexist, bool tx_pause, - int result) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - if (tx_pause) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 1\n"); - if (coex_dm->cur_ps_tdma == 1) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 4) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } - if (coex_dm->cur_ps_tdma == 9) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 12) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - if (result == -1) { - if (coex_dm->cur_ps_tdma == 5) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } else if (coex_dm->cur_ps_tdma == 13) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 8) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 16) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } - } - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 0\n"); - if (coex_dm->cur_ps_tdma == 5) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 8) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } - if (coex_dm->cur_ps_tdma == 13) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 16) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - if (result == -1) { - if (coex_dm->cur_ps_tdma == 1) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } else if (coex_dm->cur_ps_tdma == 9) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 4) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 12) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } - } - } -} - -static void btc8192e_int3(struct btc_coexist *btcoexist, bool tx_pause, - int result) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - if (tx_pause) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 1\n"); - if (coex_dm->cur_ps_tdma == 1) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 4) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } - if (coex_dm->cur_ps_tdma == 9) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 12) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - if (result == -1) { - if (coex_dm->cur_ps_tdma == 5) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } else if (coex_dm->cur_ps_tdma == 13) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 8) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 16) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } - } - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 0\n"); - if (coex_dm->cur_ps_tdma == 5) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 8) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } - if (coex_dm->cur_ps_tdma == 13) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 16) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - if (result == -1) { - if (coex_dm->cur_ps_tdma == 1) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } else if (coex_dm->cur_ps_tdma == 9) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 4) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 12) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } - } - } -} - static void halbtc8192e2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, bool sco_hid, bool tx_pause, u8 max_interval) @@ -2322,12 +1743,6 @@ static void halbtc8192e2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], max Interval = %d\n", max_interval); - if (max_interval == 1) - btc8192e_int1(btcoexist, tx_pause, result); - else if (max_interval == 2) - btc8192e_int2(btcoexist, tx_pause, result); - else if (max_interval == 3) - btc8192e_int3(btcoexist, tx_pause, result); } /* if current PsTdma not match with @@ -3761,11 +3176,6 @@ void ex_halbtc8192e2ant_bt_info_notify(struct btc_coexist *btcoexist, halbtc8192e2ant_run_coexist_mechanism(btcoexist); } -void ex_halbtc8192e2ant_stack_operation_notify(struct btc_coexist *btcoexist, - u8 type) -{ -} - void ex_halbtc8192e2ant_halt_notify(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c index d67bbfb6ad8e..b58aeff7eb16 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c @@ -60,188 +60,6 @@ static u32 glcoex_ver_8723b_1ant = 0x47; /*************************************************************** * local function start with halbtc8723b1ant_ ***************************************************************/ -static u8 halbtc8723b1ant_bt_rssi_state(struct btc_coexist *btcoexist, - u8 level_num, u8 rssi_thresh, - u8 rssi_thresh1) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - s32 bt_rssi = 0; - u8 bt_rssi_state = coex_sta->pre_bt_rssi_state; - - bt_rssi = coex_sta->bt_rssi; - - if (level_num == 2) { - if ((coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_LOW) || - (coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { - if (bt_rssi >= rssi_thresh + - BTC_RSSI_COEX_THRESH_TOL_8723B_1ANT) { - bt_rssi_state = BTC_RSSI_STATE_HIGH; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Rssi state switch to High\n"); - } else { - bt_rssi_state = BTC_RSSI_STATE_STAY_LOW; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Rssi state stay at Low\n"); - } - } else { - if (bt_rssi < rssi_thresh) { - bt_rssi_state = BTC_RSSI_STATE_LOW; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Rssi state switch to Low\n"); - } else { - bt_rssi_state = BTC_RSSI_STATE_STAY_HIGH; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Rssi state stay at High\n"); - } - } - } else if (level_num == 3) { - if (rssi_thresh > rssi_thresh1) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Rssi thresh error!!\n"); - return coex_sta->pre_bt_rssi_state; - } - - if ((coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_LOW) || - (coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { - if (bt_rssi >= rssi_thresh + - BTC_RSSI_COEX_THRESH_TOL_8723B_1ANT) { - bt_rssi_state = BTC_RSSI_STATE_MEDIUM; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Rssi state switch to Medium\n"); - } else { - bt_rssi_state = BTC_RSSI_STATE_STAY_LOW; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Rssi state stay at Low\n"); - } - } else if ((coex_sta->pre_bt_rssi_state == - BTC_RSSI_STATE_MEDIUM) || - (coex_sta->pre_bt_rssi_state == - BTC_RSSI_STATE_STAY_MEDIUM)) { - if (bt_rssi >= rssi_thresh1 + - BTC_RSSI_COEX_THRESH_TOL_8723B_1ANT) { - bt_rssi_state = BTC_RSSI_STATE_HIGH; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Rssi state switch to High\n"); - } else if (bt_rssi < rssi_thresh) { - bt_rssi_state = BTC_RSSI_STATE_LOW; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Rssi state switch to Low\n"); - } else { - bt_rssi_state = BTC_RSSI_STATE_STAY_MEDIUM; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Rssi state stay at Medium\n"); - } - } else { - if (bt_rssi < rssi_thresh1) { - bt_rssi_state = BTC_RSSI_STATE_MEDIUM; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Rssi state switch to Medium\n"); - } else { - bt_rssi_state = BTC_RSSI_STATE_STAY_HIGH; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Rssi state stay at High\n"); - } - } - } - - coex_sta->pre_bt_rssi_state = bt_rssi_state; - - return bt_rssi_state; -} - -static u8 halbtc8723b1ant_wifi_rssi_state(struct btc_coexist *btcoexist, - u8 index, u8 level_num, - u8 rssi_thresh, u8 rssi_thresh1) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - s32 wifi_rssi = 0; - u8 wifi_rssi_state = coex_sta->pre_wifi_rssi_state[index]; - - btcoexist->btc_get(btcoexist, - BTC_GET_S4_WIFI_RSSI, &wifi_rssi); - - if (level_num == 2) { - if ((coex_sta->pre_wifi_rssi_state[index] == - BTC_RSSI_STATE_LOW) || - (coex_sta->pre_wifi_rssi_state[index] == - BTC_RSSI_STATE_STAY_LOW)) { - if (wifi_rssi >= rssi_thresh + - BTC_RSSI_COEX_THRESH_TOL_8723B_1ANT) { - wifi_rssi_state = BTC_RSSI_STATE_HIGH; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], wifi RSSI state switch to High\n"); - } else { - wifi_rssi_state = BTC_RSSI_STATE_STAY_LOW; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], wifi RSSI state stay at Low\n"); - } - } else { - if (wifi_rssi < rssi_thresh) { - wifi_rssi_state = BTC_RSSI_STATE_LOW; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], wifi RSSI state switch to Low\n"); - } else { - wifi_rssi_state = BTC_RSSI_STATE_STAY_HIGH; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], wifi RSSI state stay at High\n"); - } - } - } else if (level_num == 3) { - if (rssi_thresh > rssi_thresh1) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], wifi RSSI thresh error!!\n"); - return coex_sta->pre_wifi_rssi_state[index]; - } - - if ((coex_sta->pre_wifi_rssi_state[index] == - BTC_RSSI_STATE_LOW) || - (coex_sta->pre_wifi_rssi_state[index] == - BTC_RSSI_STATE_STAY_LOW)) { - if (wifi_rssi >= rssi_thresh + - BTC_RSSI_COEX_THRESH_TOL_8723B_1ANT) { - wifi_rssi_state = BTC_RSSI_STATE_MEDIUM; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], wifi RSSI state switch to Medium\n"); - } else { - wifi_rssi_state = BTC_RSSI_STATE_STAY_LOW; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], wifi RSSI state stay at Low\n"); - } - } else if ((coex_sta->pre_wifi_rssi_state[index] == - BTC_RSSI_STATE_MEDIUM) || - (coex_sta->pre_wifi_rssi_state[index] == - BTC_RSSI_STATE_STAY_MEDIUM)) { - if (wifi_rssi >= rssi_thresh1 + - BTC_RSSI_COEX_THRESH_TOL_8723B_1ANT) { - wifi_rssi_state = BTC_RSSI_STATE_HIGH; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], wifi RSSI state switch to High\n"); - } else if (wifi_rssi < rssi_thresh) { - wifi_rssi_state = BTC_RSSI_STATE_LOW; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], wifi RSSI state switch to Low\n"); - } else { - wifi_rssi_state = BTC_RSSI_STATE_STAY_MEDIUM; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], wifi RSSI state stay at Medium\n"); - } - } else { - if (wifi_rssi < rssi_thresh1) { - wifi_rssi_state = BTC_RSSI_STATE_MEDIUM; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], wifi RSSI state switch to Medium\n"); - } else { - wifi_rssi_state = BTC_RSSI_STATE_STAY_HIGH; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], wifi RSSI state stay at High\n"); - } - } - } - - coex_sta->pre_wifi_rssi_state[index] = wifi_rssi_state; - - return wifi_rssi_state; -} static void halbtc8723b1ant_updatera_mask(struct btc_coexist *btcoexist, bool force_exec, u32 dis_rate_mask) @@ -515,202 +333,6 @@ static void halbtc8723b1ant_update_bt_link_info(struct btc_coexist *btcoexist) bt_link_info->hid_only = false; } -static u8 halbtc8723b1ant_action_algorithm(struct btc_coexist *btcoexist) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; - bool bt_hs_on = false; - u8 algorithm = BT_8723B_1ANT_COEX_ALGO_UNDEFINED; - u8 numdiffprofile = 0; - - btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); - - if (!bt_link_info->bt_link_exist) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], No BT link exists!!!\n"); - return algorithm; - } - - if (bt_link_info->sco_exist) - numdiffprofile++; - if (bt_link_info->hid_exist) - numdiffprofile++; - if (bt_link_info->pan_exist) - numdiffprofile++; - if (bt_link_info->a2dp_exist) - numdiffprofile++; - - if (numdiffprofile == 1) { - if (bt_link_info->sco_exist) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Profile = SCO only\n"); - algorithm = BT_8723B_1ANT_COEX_ALGO_SCO; - } else { - if (bt_link_info->hid_exist) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Profile = HID only\n"); - algorithm = BT_8723B_1ANT_COEX_ALGO_HID; - } else if (bt_link_info->a2dp_exist) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Profile = A2DP only\n"); - algorithm = BT_8723B_1ANT_COEX_ALGO_A2DP; - } else if (bt_link_info->pan_exist) { - if (bt_hs_on) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = PAN(HS) only\n"); - algorithm = - BT_8723B_1ANT_COEX_ALGO_PANHS; - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = PAN(EDR) only\n"); - algorithm = - BT_8723B_1ANT_COEX_ALGO_PANEDR; - } - } - } - } else if (numdiffprofile == 2) { - if (bt_link_info->sco_exist) { - if (bt_link_info->hid_exist) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Profile = SCO + HID\n"); - algorithm = BT_8723B_1ANT_COEX_ALGO_HID; - } else if (bt_link_info->a2dp_exist) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Profile = SCO + A2DP ==> SCO\n"); - algorithm = BT_8723B_1ANT_COEX_ALGO_SCO; - } else if (bt_link_info->pan_exist) { - if (bt_hs_on) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = SCO + PAN(HS)\n"); - algorithm = BT_8723B_1ANT_COEX_ALGO_SCO; - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = SCO + PAN(EDR)\n"); - algorithm = - BT_8723B_1ANT_COEX_ALGO_PANEDR_HID; - } - } - } else { - if (bt_link_info->hid_exist && - bt_link_info->a2dp_exist) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Profile = HID + A2DP\n"); - algorithm = BT_8723B_1ANT_COEX_ALGO_HID_A2DP; - } else if (bt_link_info->hid_exist && - bt_link_info->pan_exist) { - if (bt_hs_on) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = HID + PAN(HS)\n"); - algorithm = - BT_8723B_1ANT_COEX_ALGO_HID_A2DP; - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = HID + PAN(EDR)\n"); - algorithm = - BT_8723B_1ANT_COEX_ALGO_PANEDR_HID; - } - } else if (bt_link_info->pan_exist && - bt_link_info->a2dp_exist) { - if (bt_hs_on) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = A2DP + PAN(HS)\n"); - algorithm = - BT_8723B_1ANT_COEX_ALGO_A2DP_PANHS; - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = A2DP + PAN(EDR)\n"); - algorithm = - BT_8723B_1ANT_COEX_ALGO_PANEDR_A2DP; - } - } - } - } else if (numdiffprofile == 3) { - if (bt_link_info->sco_exist) { - if (bt_link_info->hid_exist && - bt_link_info->a2dp_exist) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT Profile = SCO + HID + A2DP ==> HID\n"); - algorithm = BT_8723B_1ANT_COEX_ALGO_HID; - } else if (bt_link_info->hid_exist && - bt_link_info->pan_exist) { - if (bt_hs_on) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = SCO + HID + PAN(HS)\n"); - algorithm = - BT_8723B_1ANT_COEX_ALGO_HID_A2DP; - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = SCO + HID + PAN(EDR)\n"); - algorithm = - BT_8723B_1ANT_COEX_ALGO_PANEDR_HID; - } - } else if (bt_link_info->pan_exist && - bt_link_info->a2dp_exist) { - if (bt_hs_on) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = SCO + A2DP + PAN(HS)\n"); - algorithm = BT_8723B_1ANT_COEX_ALGO_SCO; - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = SCO + A2DP + PAN(EDR) ==> HID\n"); - algorithm = - BT_8723B_1ANT_COEX_ALGO_PANEDR_HID; - } - } - } else { - if (bt_link_info->hid_exist && - bt_link_info->pan_exist && - bt_link_info->a2dp_exist) { - if (bt_hs_on) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = HID + A2DP + PAN(HS)\n"); - algorithm = - BT_8723B_1ANT_COEX_ALGO_HID_A2DP; - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = HID + A2DP + PAN(EDR)\n"); - algorithm = - BT_8723B_1ANT_COEX_ALGO_HID_A2DP_PANEDR; - } - } - } - } else if (numdiffprofile >= 3) { - if (bt_link_info->sco_exist) { - if (bt_link_info->hid_exist && - bt_link_info->pan_exist && - bt_link_info->a2dp_exist) { - if (bt_hs_on) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], Error!!! BT Profile = SCO + HID + A2DP + PAN(HS)\n"); - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, - DBG_LOUD, - "[BTCoex], BT Profile = SCO + HID + A2DP + PAN(EDR)==>PAN(EDR)+HID\n"); - algorithm = - BT_8723B_1ANT_COEX_ALGO_PANEDR_HID; - } - } - } - } - - return algorithm; -} - static void btc8723b1ant_set_sw_pen_tx_rate_adapt(struct btc_coexist *btcoexist, bool low_penalty_ra) { @@ -1407,64 +1029,6 @@ static void halbtc8723b1ant_ps_tdma(struct btc_coexist *btcoexist, coex_dm->pre_ps_tdma = coex_dm->cur_ps_tdma; } -static bool halbtc8723b1ant_is_common_action(struct btc_coexist *btcoexist) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - bool commom = false, wifi_connected = false; - bool wifi_busy = false; - - btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, - &wifi_connected); - btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy); - - if (!wifi_connected && - BT_8723B_1ANT_BT_STATUS_NON_CONNECTED_IDLE == coex_dm->bt_status) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi non connected-idle + BT non connected-idle!!\n"); - halbtc8723b1ant_sw_mechanism(btcoexist, false); - commom = true; - } else if (wifi_connected && - (BT_8723B_1ANT_BT_STATUS_NON_CONNECTED_IDLE == - coex_dm->bt_status)) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi connected + BT non connected-idle!!\n"); - halbtc8723b1ant_sw_mechanism(btcoexist, false); - commom = true; - } else if (!wifi_connected && - (BT_8723B_1ANT_BT_STATUS_CONNECTED_IDLE == - coex_dm->bt_status)) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi non connected-idle + BT connected-idle!!\n"); - halbtc8723b1ant_sw_mechanism(btcoexist, false); - commom = true; - } else if (wifi_connected && - (BT_8723B_1ANT_BT_STATUS_CONNECTED_IDLE == - coex_dm->bt_status)) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi connected + BT connected-idle!!\n"); - halbtc8723b1ant_sw_mechanism(btcoexist, false); - commom = true; - } else if (!wifi_connected && - (BT_8723B_1ANT_BT_STATUS_CONNECTED_IDLE != - coex_dm->bt_status)) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi non connected-idle + BT Busy!!\n"); - halbtc8723b1ant_sw_mechanism(btcoexist, false); - commom = true; - } else { - if (wifi_busy) - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi Connected-Busy + BT Busy!!\n"); - else - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi Connected-Idle + BT Busy!!\n"); - - commom = false; - } - - return commom; -} - static void btc8723b1ant_tdma_dur_adj_for_acl(struct btc_coexist *btcoexist, u8 wifi_status) { @@ -1700,66 +1264,6 @@ static void halbtc8723b1ant_power_save_state(struct btc_coexist *btcoexist, } } -/*************************************************** - * - * Software Coex Mechanism start - * - ***************************************************/ -/* SCO only or SCO+PAN(HS) */ -static void halbtc8723b1ant_action_sco(struct btc_coexist *btcoexist) -{ - halbtc8723b1ant_sw_mechanism(btcoexist, true); -} - -static void halbtc8723b1ant_action_hid(struct btc_coexist *btcoexist) -{ - halbtc8723b1ant_sw_mechanism(btcoexist, true); -} - -/*A2DP only / PAN(EDR) only/ A2DP+PAN(HS) */ -static void halbtc8723b1ant_action_a2dp(struct btc_coexist *btcoexist) -{ - halbtc8723b1ant_sw_mechanism(btcoexist, false); -} - -static void halbtc8723b1ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) -{ - halbtc8723b1ant_sw_mechanism(btcoexist, false); -} - -static void halbtc8723b1ant_action_pan_edr(struct btc_coexist *btcoexist) -{ - halbtc8723b1ant_sw_mechanism(btcoexist, false); -} - -/* PAN(HS) only */ -static void halbtc8723b1ant_action_pan_hs(struct btc_coexist *btcoexist) -{ - halbtc8723b1ant_sw_mechanism(btcoexist, false); -} - -/*PAN(EDR)+A2DP */ -static void halbtc8723b1ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) -{ - halbtc8723b1ant_sw_mechanism(btcoexist, false); -} - -static void halbtc8723b1ant_action_pan_edr_hid(struct btc_coexist *btcoexist) -{ - halbtc8723b1ant_sw_mechanism(btcoexist, true); -} - -/* HID+A2DP+PAN(EDR) */ -static void btc8723b1ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) -{ - halbtc8723b1ant_sw_mechanism(btcoexist, true); -} - -static void halbtc8723b1ant_action_hid_a2dp(struct btc_coexist *btcoexist) -{ - halbtc8723b1ant_sw_mechanism(btcoexist, true); -} - /***************************************************** * * Non-Software Coex Mechanism start @@ -1840,11 +1344,8 @@ static void halbtc8723b1ant_action_wifi_connected_bt_acl_busy( struct btc_coexist *btcoexist, u8 wifi_status) { - u8 bt_rssi_state; - struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; - bt_rssi_state = halbtc8723b1ant_bt_rssi_state(btcoexist, 2, 28, 0); if (bt_link_info->hid_only) { /*HID */ btc8723b1ant_act_bt_sco_hid_only_busy(btcoexist, wifi_status); @@ -1857,12 +1358,6 @@ static void halbtc8723b1ant_action_wifi_connected_bt_acl_busy( halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); coex_dm->auto_tdma_adjust = false; - } else if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b1ant_tdma_dur_adj_for_acl(btcoexist, - wifi_status); - halbtc8723b1ant_coex_table_with_type(btcoexist, - NORMAL_EXEC, 1); } else { /*for low BT RSSI */ halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); @@ -2109,75 +1604,6 @@ static void halbtc8723b1ant_action_wifi_connected(struct btc_coexist *btcoexist) } } -static void btc8723b1ant_run_sw_coex_mech(struct btc_coexist *btcoexist) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - u8 algorithm = 0; - - algorithm = halbtc8723b1ant_action_algorithm(btcoexist); - coex_dm->cur_algorithm = algorithm; - - if (!halbtc8723b1ant_is_common_action(btcoexist)) { - switch (coex_dm->cur_algorithm) { - case BT_8723B_1ANT_COEX_ALGO_SCO: - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Action algorithm = SCO\n"); - halbtc8723b1ant_action_sco(btcoexist); - break; - case BT_8723B_1ANT_COEX_ALGO_HID: - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Action algorithm = HID\n"); - halbtc8723b1ant_action_hid(btcoexist); - break; - case BT_8723B_1ANT_COEX_ALGO_A2DP: - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Action algorithm = A2DP\n"); - halbtc8723b1ant_action_a2dp(btcoexist); - break; - case BT_8723B_1ANT_COEX_ALGO_A2DP_PANHS: - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Action algorithm = A2DP+PAN(HS)\n"); - halbtc8723b1ant_action_a2dp_pan_hs(btcoexist); - break; - case BT_8723B_1ANT_COEX_ALGO_PANEDR: - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Action algorithm = PAN(EDR)\n"); - halbtc8723b1ant_action_pan_edr(btcoexist); - break; - case BT_8723B_1ANT_COEX_ALGO_PANHS: - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Action algorithm = HS mode\n"); - halbtc8723b1ant_action_pan_hs(btcoexist); - break; - case BT_8723B_1ANT_COEX_ALGO_PANEDR_A2DP: - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Action algorithm = PAN+A2DP\n"); - halbtc8723b1ant_action_pan_edr_a2dp(btcoexist); - break; - case BT_8723B_1ANT_COEX_ALGO_PANEDR_HID: - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Action algorithm = PAN(EDR)+HID\n"); - halbtc8723b1ant_action_pan_edr_hid(btcoexist); - break; - case BT_8723B_1ANT_COEX_ALGO_HID_A2DP_PANEDR: - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Action algorithm = HID+A2DP+PAN\n"); - btc8723b1ant_action_hid_a2dp_pan_edr(btcoexist); - break; - case BT_8723B_1ANT_COEX_ALGO_HID_A2DP: - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Action algorithm = HID+A2DP\n"); - halbtc8723b1ant_action_hid_a2dp(btcoexist); - break; - default: - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Action algorithm = coexist All Off!!\n"); - break; - } - coex_dm->pre_algorithm = coex_dm->cur_algorithm; - } -} - static void halbtc8723b1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2186,7 +1612,6 @@ static void halbtc8723b1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) bool increase_scan_dev_num = false; bool bt_ctrl_agg_buf_size = false; u8 agg_buf_size = 5; - u8 wifi_rssi_state = BTC_RSSI_STATE_HIGH; u32 wifi_link_status = 0; u32 num_of_wifi_link = 0; @@ -2239,9 +1664,6 @@ static void halbtc8723b1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) halbtc8723b1ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); } else { if (wifi_connected) { - wifi_rssi_state = - halbtc8723b1ant_wifi_rssi_state(btcoexist, - 1, 2, 30, 0); halbtc8723b1ant_limited_tx(btcoexist, NORMAL_EXEC, 1, 1, 1, 1); } else { @@ -2263,8 +1685,6 @@ static void halbtc8723b1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) halbtc8723b1ant_limited_rx(btcoexist, NORMAL_EXEC, false, bt_ctrl_agg_buf_size, agg_buf_size); - btc8723b1ant_run_sw_coex_mech(btcoexist); - btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); if (coex_sta->c2h_bt_inquiry_page) { @@ -2375,12 +1795,6 @@ static void halbtc8723b1ant_init_hw_config(struct btc_coexist *btcoexist, halbtc8723b1ant_coex_table_with_type(btcoexist, FORCE_EXEC, 0); } -static void halbtc8723b1ant_wifi_off_hw_cfg(struct btc_coexist *btcoexist) -{ - /* set wlan_act to low */ - btcoexist->btc_write_1byte(btcoexist, 0x76e, 0x4); -} - /************************************************************** * work around function start with wa_halbtc8723b1ant_ **************************************************************/ @@ -2703,7 +2117,6 @@ void ex_halbtc8723b1ant_ips_notify(struct btc_coexist *btcoexist, u8 type) halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); - halbtc8723b1ant_wifi_off_hw_cfg(btcoexist); } else if (BTC_IPS_LEAVE == type) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], IPS LEAVE notify\n"); @@ -3091,7 +2504,6 @@ void ex_halbtc8723b1ant_halt_notify(struct btc_coexist *btcoexist) halbtc8723b1ant_SetAntPath(btcoexist, BTC_ANT_PATH_BT, false, true); - halbtc8723b1ant_wifi_off_hw_cfg(btcoexist); halbtc8723b1ant_ignore_wlan_act(btcoexist, FORCE_EXEC, true); halbtc8723b1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, @@ -3117,7 +2529,6 @@ void ex_halbtc8723b1ant_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state) 0x0, 0x0); halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); - halbtc8723b1ant_wifi_off_hw_cfg(btcoexist); } else if (BTC_WIFI_PNP_WAKE_UP == pnp_state) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Pnp notify to WAKE UP\n"); diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 12125966a911..4e5dfaa02da9 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -584,44 +584,6 @@ static u8 btc8723b2ant_action_algorithm(struct btc_coexist *btcoexist) return algorithm; } -static bool btc8723b_need_dec_pwr(struct btc_coexist *btcoexist) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - bool ret = false; - bool bt_hs_on = false, wifi_connected = false; - s32 bt_hs_rssi = 0; - u8 bt_rssi_state; - - if (!btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on)) - return false; - if (!btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, - &wifi_connected)) - return false; - if (!btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi)) - return false; - - bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, 29, 0); - - if (wifi_connected) { - if (bt_hs_on) { - if (bt_hs_rssi > 37) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Need to decrease bt power for HS mode!!\n"); - ret = true; - } - } else { - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Need to decrease bt power for Wifi is connected!!\n"); - ret = true; - } - } - } - - return ret; -} - static void btc8723b2ant_set_fw_dac_swing_level(struct btc_coexist *btcoexist, u8 dac_swing_lvl) { @@ -708,57 +670,6 @@ static void btc8723b2ant_fw_dac_swing_lvl(struct btc_coexist *btcoexist, coex_dm->pre_fw_dac_swing_lvl = coex_dm->cur_fw_dac_swing_lvl; } -static void btc8723b2ant_set_sw_rf_rx_lpf_corner(struct btc_coexist *btcoexist, - bool rx_rf_shrink_on) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - if (rx_rf_shrink_on) { - /* Shrink RF Rx LPF corner */ - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Shrink RF Rx LPF corner!!\n"); - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1e, - 0xfffff, 0xffffc); - } else { - /* Resume RF Rx LPF corner */ - /* After initialized, we can use coex_dm->btRf0x1eBackup */ - if (btcoexist->initilized) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Resume RF Rx LPF corner!!\n"); - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1e, - 0xfffff, - coex_dm->bt_rf0x1e_backup); - } - } -} - -static void btc8723b2ant_rf_shrink(struct btc_coexist *btcoexist, - bool force_exec, bool rx_rf_shrink_on) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], %s turn Rx RF Shrink = %s\n", - (force_exec ? "force to" : ""), (rx_rf_shrink_on ? - "ON" : "OFF")); - coex_dm->cur_rf_rx_lpf_shrink = rx_rf_shrink_on; - - if (!force_exec) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], bPreRfRxLpfShrink=%d, bCurRfRxLpfShrink=%d\n", - coex_dm->pre_rf_rx_lpf_shrink, - coex_dm->cur_rf_rx_lpf_shrink); - - if (coex_dm->pre_rf_rx_lpf_shrink == - coex_dm->cur_rf_rx_lpf_shrink) - return; - } - btc8723b2ant_set_sw_rf_rx_lpf_corner(btcoexist, - coex_dm->cur_rf_rx_lpf_shrink); - - coex_dm->pre_rf_rx_lpf_shrink = coex_dm->cur_rf_rx_lpf_shrink; -} - static void btc8723b_set_penalty_txrate(struct btc_coexist *btcoexist, bool low_penalty_ra) { @@ -863,105 +774,6 @@ static void btc8723b2ant_dac_swing(struct btc_coexist *btcoexist, coex_dm->pre_dac_swing_lvl = coex_dm->cur_dac_swing_lvl; } -static void btc8723b2ant_set_agc_table(struct btc_coexist *btcoexist, - bool agc_table_en) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - u8 rssi_adjust_val = 0; - - /* BB AGC Gain Table */ - if (agc_table_en) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BB Agc Table On!\n"); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6e1A0001); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6d1B0001); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6c1C0001); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6b1D0001); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6a1E0001); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x691F0001); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x68200001); - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BB Agc Table Off!\n"); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xaa1A0001); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa91B0001); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa81C0001); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa71D0001); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa61E0001); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa51F0001); - btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa4200001); - } - - /* RF Gain */ - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0xef, 0xfffff, 0x02000); - if (agc_table_en) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Agc Table On!\n"); - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x3b, - 0xfffff, 0x38fff); - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x3b, - 0xfffff, 0x38ffe); - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Agc Table Off!\n"); - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x3b, - 0xfffff, 0x380c3); - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x3b, - 0xfffff, 0x28ce6); - } - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0xef, 0xfffff, 0x0); - - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0xed, 0xfffff, 0x1); - - if (agc_table_en) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Agc Table On!\n"); - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x40, - 0xfffff, 0x38fff); - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x40, - 0xfffff, 0x38ffe); - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Agc Table Off!\n"); - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x40, - 0xfffff, 0x380c3); - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x40, - 0xfffff, 0x28ce6); - } - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0xed, 0xfffff, 0x0); - - /* set rssiAdjustVal for wifi module. */ - if (agc_table_en) - rssi_adjust_val = 8; - btcoexist->btc_set(btcoexist, BTC_SET_U1_RSSI_ADJ_VAL_FOR_AGC_TABLE_ON, - &rssi_adjust_val); -} - -static void btc8723b2ant_agc_table(struct btc_coexist *btcoexist, - bool force_exec, bool agc_table_en) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], %s %s Agc Table\n", - (force_exec ? "force to" : ""), - (agc_table_en ? "Enable" : "Disable")); - coex_dm->cur_agc_table_en = agc_table_en; - - if (!force_exec) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], bPreAgcTableEn=%d, bCurAgcTableEn=%d\n", - coex_dm->pre_agc_table_en, - coex_dm->cur_agc_table_en); - - if (coex_dm->pre_agc_table_en == coex_dm->cur_agc_table_en) - return; - } - btc8723b2ant_set_agc_table(btcoexist, agc_table_en); - - coex_dm->pre_agc_table_en = coex_dm->cur_agc_table_en; -} - static void btc8723b2ant_set_coex_table(struct btc_coexist *btcoexist, u32 val0x6c0, u32 val0x6c4, u32 val0x6c8, u8 val0x6cc) @@ -1159,19 +971,9 @@ static void btc8723b2ant_sw_mechanism1(struct btc_coexist *btcoexist, bool shrink_rx_lpf, bool low_penalty_ra, bool limited_dig, bool bt_lna_constrain) { - btc8723b2ant_rf_shrink(btcoexist, NORMAL_EXEC, shrink_rx_lpf); btc8723b2ant_low_penalty_ra(btcoexist, NORMAL_EXEC, low_penalty_ra); } -static void btc8723b2ant_sw_mechanism2(struct btc_coexist *btcoexist, - bool agc_table_shift, bool adc_backoff, - bool sw_dac_swing, u32 dac_swing_lvl) -{ - btc8723b2ant_agc_table(btcoexist, NORMAL_EXEC, agc_table_shift); - btc8723b2ant_dac_swing(btcoexist, NORMAL_EXEC, sw_dac_swing, - dac_swing_lvl); -} - static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, u8 antpos_type, bool init_hwcfg, bool wifi_off) @@ -1407,7 +1209,6 @@ static void btc8723b2ant_coex_alloff(struct btc_coexist *btcoexist) /* sw all off */ btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); /* hw all off */ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); @@ -1423,7 +1224,6 @@ static void btc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist) btc8723b2ant_dec_bt_pwr(btcoexist, FORCE_EXEC, false); btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); } static void btc8723b2ant_action_bt_inquiry(struct btc_coexist *btcoexist) @@ -1447,7 +1247,6 @@ static void btc8723b2ant_action_bt_inquiry(struct btc_coexist *btcoexist) btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); coex_dm->need_recover_0x948 = true; coex_dm->backup_0x948 = btcoexist->btc_read_2byte(btcoexist, 0x948); @@ -1485,8 +1284,6 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, false, - 0x18); common = true; } else { @@ -1511,8 +1308,6 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); common = true; } else if (BT_8723B_2ANT_BT_STATUS_CONNECTED_IDLE == @@ -1538,8 +1333,6 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) btc8723b2ant_sw_mechanism1(btcoexist, true, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); common = true; } else { @@ -1568,20 +1361,9 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 0xb); - if (btc8723b_need_dec_pwr(btcoexist)) - btc8723b2ant_dec_bt_pwr(btcoexist, - NORMAL_EXEC, - true); - else - btc8723b2ant_dec_bt_pwr(btcoexist, - NORMAL_EXEC, - false); btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, - false, false, - 0x18); common = true; } } @@ -1590,550 +1372,6 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) return common; } -static void set_tdma_int1(struct btc_coexist *btcoexist, bool tx_pause, - s32 result) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - /* Set PS TDMA for max interval == 1 */ - if (tx_pause) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 1\n"); - - if (coex_dm->cur_ps_tdma == 71) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); - coex_dm->tdma_adj_type = 5; - } else if (coex_dm->cur_ps_tdma == 1) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); - coex_dm->tdma_adj_type = 5; - } else if (coex_dm->cur_ps_tdma == 2) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 3) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 4) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } - - if (coex_dm->cur_ps_tdma == 9) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 13); - coex_dm->tdma_adj_type = 13; - } else if (coex_dm->cur_ps_tdma == 10) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 11) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 12) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - - if (result == -1) { - if (coex_dm->cur_ps_tdma == 5) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 6) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } else if (coex_dm->cur_ps_tdma == 13) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 14) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 8) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 6) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); - coex_dm->tdma_adj_type = 5; - } else if (coex_dm->cur_ps_tdma == 16) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 14) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 13); - coex_dm->tdma_adj_type = 13; - } - } - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 0\n"); - if (coex_dm->cur_ps_tdma == 5) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 71); - coex_dm->tdma_adj_type = 71; - } else if (coex_dm->cur_ps_tdma == 6) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 7) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 8) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 4); - coex_dm->tdma_adj_type = 4; - } - - if (coex_dm->cur_ps_tdma == 13) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); - coex_dm->tdma_adj_type = 9; - } else if (coex_dm->cur_ps_tdma == 14) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 15) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 16) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 12); - coex_dm->tdma_adj_type = 12; - } - - if (result == -1) { - if (coex_dm->cur_ps_tdma == 71) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 1); - coex_dm->tdma_adj_type = 1; - } else if (coex_dm->cur_ps_tdma == 1) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } else if (coex_dm->cur_ps_tdma == 9) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 10) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 4) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 1); - coex_dm->tdma_adj_type = 1; - } else if (coex_dm->cur_ps_tdma == 1) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 71); - coex_dm->tdma_adj_type = 71; - } else if (coex_dm->cur_ps_tdma == 12) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 10) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - } - } - } -} - -static void set_tdma_int2(struct btc_coexist *btcoexist, bool tx_pause, - s32 result) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - /* Set PS TDMA for max interval == 2 */ - if (tx_pause) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 1\n"); - if (coex_dm->cur_ps_tdma == 1) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 2) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 3) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 4) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 8); - coex_dm->tdma_adj_type = 8; - } - if (coex_dm->cur_ps_tdma == 9) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 10) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 11) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 12) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 16); - coex_dm->tdma_adj_type = 16; - } - if (result == -1) { - if (coex_dm->cur_ps_tdma == 5) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 6) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } else if (coex_dm->cur_ps_tdma == 13) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 14) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 8) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 6) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 16) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 14) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } - } - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 0\n"); - if (coex_dm->cur_ps_tdma == 5) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 6) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 7) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 8) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 4); - coex_dm->tdma_adj_type = 4; - } - if (coex_dm->cur_ps_tdma == 13) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 14) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 15) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 16) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 12); - coex_dm->tdma_adj_type = 12; - } - if (result == -1) { - if (coex_dm->cur_ps_tdma == 1) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } else if (coex_dm->cur_ps_tdma == 9) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 10) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 4) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 12) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 10) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } - } - } -} - -static void set_tdma_int3(struct btc_coexist *btcoexist, bool tx_pause, - s32 result) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - /* Set PS TDMA for max interval == 3 */ - if (tx_pause) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 1\n"); - if (coex_dm->cur_ps_tdma == 1) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 2) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 3) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 4) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 8); - coex_dm->tdma_adj_type = 8; - } - if (coex_dm->cur_ps_tdma == 9) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 10) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 11) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 12) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 16); - coex_dm->tdma_adj_type = 16; - } - if (result == -1) { - if (coex_dm->cur_ps_tdma == 5) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 6) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } else if (coex_dm->cur_ps_tdma == 13) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 14) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 8) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 6) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 16) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 14) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } - } - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 0\n"); - if (coex_dm->cur_ps_tdma == 5) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 6) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 7) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 8) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 4); - coex_dm->tdma_adj_type = 4; - } - if (coex_dm->cur_ps_tdma == 13) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 14) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 15) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 16) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 12); - coex_dm->tdma_adj_type = 12; - } - if (result == -1) { - if (coex_dm->cur_ps_tdma == 1) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 2) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } else if (coex_dm->cur_ps_tdma == 9) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 10) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 4) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 2) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 12) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 10) { - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } - } - } -} - static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, bool sco_hid, bool tx_pause, u8 max_interval) @@ -2302,12 +1540,6 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], max Interval = %d\n", max_interval); - if (max_interval == 1) - set_tdma_int1(btcoexist, tx_pause, result); - else if (max_interval == 2) - set_tdma_int2(btcoexist, tx_pause, result); - else if (max_interval == 3) - set_tdma_int3(btcoexist, tx_pause, result); } /*if current PsTdma not match with the recorded one (when scan, dhcp..), @@ -2345,11 +1577,6 @@ static void btc8723b2ant_action_sco(struct btc_coexist *btcoexist) btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 4); - if (btc8723b_need_dec_pwr(btcoexist)) - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); - else - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); /*for SCO quality at 11b/g mode*/ @@ -2367,26 +1594,18 @@ static void btc8723b2ant_action_sco(struct btc_coexist *btcoexist) (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, true, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - true, 0x4); } else { btc8723b2ant_sw_mechanism1(btcoexist, true, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - true, 0x4); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, false, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - true, 0x4); } else { btc8723b2ant_sw_mechanism1(btcoexist, false, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - true, 0x4); } } } @@ -2404,11 +1623,6 @@ static void btc8723b2ant_action_hid(struct btc_coexist *btcoexist) btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - if (btc8723b_need_dec_pwr(btcoexist)) - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); - else - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); if (BTC_WIFI_BW_LEGACY == wifi_bw) /*/for HID at 11b/g mode*/ @@ -2428,26 +1642,18 @@ static void btc8723b2ant_action_hid(struct btc_coexist *btcoexist) (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, true, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, true, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, false, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, false, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } } @@ -2482,13 +1688,9 @@ static void btc8723b2ant_action_a2dp(struct btc_coexist *btcoexist) if (BTC_WIFI_BW_HT40 == wifi_bw) { btc8723b2ant_sw_mechanism1(btcoexist, true, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - true, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - true, 0x18); } return; } @@ -2497,11 +1699,6 @@ static void btc8723b2ant_action_a2dp(struct btc_coexist *btcoexist) btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - if (btc8723b_need_dec_pwr(btcoexist)) - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); - else - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || @@ -2518,26 +1715,18 @@ static void btc8723b2ant_action_a2dp(struct btc_coexist *btcoexist) (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, true, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, true, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } } @@ -2554,11 +1743,6 @@ static void btc8723b2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - if (btc8723b_need_dec_pwr(btcoexist)) - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); - else - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); btc8723b2ant_tdma_duration_adjust(btcoexist, false, true, 2); @@ -2571,26 +1755,18 @@ static void btc8723b2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, true, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, true, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } } @@ -2608,10 +1784,10 @@ static void btc8723b2ant_action_pan_edr(struct btc_coexist *btcoexist) btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - if (btc8723b_need_dec_pwr(btcoexist)) - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + if (BTC_RSSI_HIGH(bt_rssi_state)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); else - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 10); @@ -2628,26 +1804,18 @@ static void btc8723b2ant_action_pan_edr(struct btc_coexist *btcoexist) (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, true, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, true, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } } @@ -2681,26 +1849,18 @@ static void btc8723b2ant_action_pan_hs(struct btc_coexist *btcoexist) (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, true, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, true, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } } @@ -2719,8 +1879,8 @@ static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - if (btc8723b_need_dec_pwr(btcoexist)) - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + if (BTC_RSSI_HIGH(bt_rssi_state)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); else btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); @@ -2746,26 +1906,18 @@ static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, true, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, true, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } } @@ -2780,8 +1932,8 @@ static void btc8723b2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, 29, 0); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - if (btc8723b_need_dec_pwr(btcoexist)) - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + if (BTC_RSSI_HIGH(bt_rssi_state)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); else btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); @@ -2815,26 +1967,18 @@ static void btc8723b2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, true, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, true, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, false, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, false, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } } @@ -2853,8 +1997,8 @@ static void btc8723b2ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - if (btc8723b_need_dec_pwr(btcoexist)) - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + if (BTC_RSSI_HIGH(bt_rssi_state)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); else btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); @@ -2880,26 +2024,18 @@ static void btc8723b2ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, true, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, true, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, false, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, false, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } } @@ -2917,11 +2053,6 @@ static void btc8723b2ant_action_hid_a2dp(struct btc_coexist *btcoexist) btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - if (btc8723b_need_dec_pwr(btcoexist)) - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); - else - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); @@ -2938,26 +2069,18 @@ static void btc8723b2ant_action_hid_a2dp(struct btc_coexist *btcoexist) (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, true, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, true, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8723b2ant_sw_mechanism1(btcoexist, false, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); } else { btc8723b2ant_sw_mechanism1(btcoexist, false, true, false, false); - btc8723b2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); } } } @@ -3677,8 +2800,6 @@ void ex_btc8723b2ant_periodical(struct btc_coexist *btcoexist) #if (BT_AUTO_REPORT_ONLY_8723B_2ANT == 0) btc8723b2ant_query_bt_info(btcoexist); - btc8723b2ant_monitor_bt_ctr(btcoexist); - btc8723b2ant_monitor_bt_enable_disable(btcoexist); #else if (btc8723b2ant_is_wifi_status_changed(btcoexist) || coex_dm->auto_tdma_adjust) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index 8b689ed9a629..4a8d8cfb34d3 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -670,51 +670,6 @@ static u8 halbtc8821a1ant_action_algorithm(struct btc_coexist *btcoexist) return algorithm; } -static void halbtc8821a1ant_set_bt_auto_report(struct btc_coexist *btcoexist, - bool enable_auto_report) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - u8 h2c_parameter[1] = {0}; - - h2c_parameter[0] = 0; - - if (enable_auto_report) - h2c_parameter[0] |= BIT0; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT FW auto report : %s, FW write 0x68 = 0x%x\n", - (enable_auto_report ? "Enabled!!" : "Disabled!!"), - h2c_parameter[0]); - - btcoexist->btc_fill_h2c(btcoexist, 0x68, 1, h2c_parameter); -} - -static void halbtc8821a1ant_bt_auto_report(struct btc_coexist *btcoexist, - bool force_exec, - bool enable_auto_report) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], %s BT Auto report = %s\n", - (force_exec ? "force to" : ""), ((enable_auto_report) ? - "Enabled" : "Disabled")); - coex_dm->cur_bt_auto_report = enable_auto_report; - - if (!force_exec) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], pre_bt_auto_report = %d, cur_bt_auto_report = %d\n", - coex_dm->pre_bt_auto_report, - coex_dm->cur_bt_auto_report); - - if (coex_dm->pre_bt_auto_report == coex_dm->cur_bt_auto_report) - return; - } - halbtc8821a1ant_set_bt_auto_report(btcoexist, coex_dm->cur_bt_auto_report); - - coex_dm->pre_bt_auto_report = coex_dm->cur_bt_auto_report; -} - static void btc8821a1ant_set_sw_pen_tx_rate(struct btc_coexist *btcoexist, bool low_penalty_ra) { @@ -1333,196 +1288,6 @@ static bool halbtc8821a1ant_is_common_action(struct btc_coexist *btcoexist) return common; } -static void btc8821a1ant_tdma_dur_adj(struct btc_coexist *btcoexist, - u8 wifi_status) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - static long up, dn, m, n, wait_count; - /*0: no change, +1: increase WiFi duration, -1: decrease WiFi duration*/ - long result; - u8 retry_count = 0, bt_info_ext; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TdmaDurationAdjustForAcl()\n"); - - if ((BT_8821A_1ANT_WIFI_STATUS_NON_CONNECTED_ASSO_AUTH_SCAN == - wifi_status) || - (BT_8821A_1ANT_WIFI_STATUS_CONNECTED_SCAN == - wifi_status) || - (BT_8821A_1ANT_WIFI_STATUS_CONNECTED_SPECIAL_PKT == - wifi_status)) { - if (coex_dm->cur_ps_tdma != 1 && - coex_dm->cur_ps_tdma != 2 && - coex_dm->cur_ps_tdma != 3 && - coex_dm->cur_ps_tdma != 9) { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - - up = 0; - dn = 0; - m = 1; - n = 3; - result = 0; - wait_count = 0; - } - return; - } - - if (!coex_dm->auto_tdma_adjust) { - coex_dm->auto_tdma_adjust = true; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], first run TdmaDurationAdjust()!!\n"); - - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 2); - coex_dm->tdma_adj_type = 2; - /*============*/ - up = 0; - dn = 0; - m = 1; - n = 3; - result = 0; - wait_count = 0; - } else { - /*accquire the BT TRx retry count from BT_Info byte2*/ - retry_count = coex_sta->bt_retry_cnt; - bt_info_ext = coex_sta->bt_info_ext; - result = 0; - wait_count++; - - if (retry_count == 0) { - /* no retry in the last 2-second duration*/ - up++; - dn--; - - if (dn <= 0) - dn = 0; - - if (up >= n) { - /* if (retry count == 0) for 2*n seconds , - * make WiFi duration wider - */ - wait_count = 0; - n = 3; - up = 0; - dn = 0; - result = 1; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Increase wifi duration!!\n"); - } - } else if (retry_count <= 3) { - /* <=3 retry in the last 2-second duration*/ - up--; - dn++; - - if (up <= 0) - up = 0; - - if (dn == 2) { - /* if retry count< 3 for 2*2 seconds, - * shrink wifi duration - */ - if (wait_count <= 2) - m++; /* avoid bounce in two levels */ - else - m = 1; - - if (m >= 20) { - /* m max value is 20, max time is 120 s, - * recheck if adjust WiFi duration. - */ - m = 20; - } - n = 3*m; - up = 0; - dn = 0; - wait_count = 0; - result = -1; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Decrease wifi duration for retryCounter<3!!\n"); - } - } else { - /* retry count > 3, if retry count > 3 happens once, - * shrink WiFi duration - */ - if (wait_count == 1) - m++; /* avoid bounce in two levels */ - else - m = 1; - /* m max value is 20, max time is 120 second, - * recheck if adjust WiFi duration. - */ - if (m >= 20) - m = 20; - - n = 3*m; - up = 0; - dn = 0; - wait_count = 0; - result = -1; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Decrease wifi duration for retryCounter>3!!\n"); - } - - if (result == -1) { - if ((BT_INFO_8821A_1ANT_A2DP_BASIC_RATE(bt_info_ext)) && - ((coex_dm->cur_ps_tdma == 1) || - (coex_dm->cur_ps_tdma == 2))) { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - } else if (coex_dm->cur_ps_tdma == 1) { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - } else if (coex_dm->cur_ps_tdma == 9) { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } - } else if (result == 1) { - if ((BT_INFO_8821A_1ANT_A2DP_BASIC_RATE(bt_info_ext)) && - ((coex_dm->cur_ps_tdma == 1) || - (coex_dm->cur_ps_tdma == 2))) { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - } else if (coex_dm->cur_ps_tdma == 9) { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 1); - coex_dm->tdma_adj_type = 1; - } - } else { - /*no change*/ - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], ********** TDMA(on, %d) **********\n", - coex_dm->cur_ps_tdma); - } - - if (coex_dm->cur_ps_tdma != 1 && - coex_dm->cur_ps_tdma != 2 && - coex_dm->cur_ps_tdma != 9 && - coex_dm->cur_ps_tdma != 11) { - /* recover to previous adjust type*/ - halbtc8821a1ant_ps_tdma(btcoexist, - NORMAL_EXEC, true, - coex_dm->tdma_adj_type); - } - } -} - static void btc8821a1ant_ps_tdma_check_for_pwr_save(struct btc_coexist *btcoex, bool new_ps_state) { @@ -1599,74 +1364,11 @@ static void halbtc8821a1ant_coex_under_5g(struct btc_coexist *btcoexist) halbtc8821a1ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 5); } -static void halbtc8821a1ant_action_wifi_only(struct btc_coexist *btcoexist) -{ - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 9); -} - -static void btc8821a1ant_mon_bt_en_dis(struct btc_coexist *btcoexist) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - static bool pre_bt_disabled; - static u32 bt_disable_cnt; - bool bt_active = true, bt_disabled = false; - - /* This function check if bt is disabled*/ - - if (coex_sta->high_priority_tx == 0 && - coex_sta->high_priority_rx == 0 && - coex_sta->low_priority_tx == 0 && - coex_sta->low_priority_rx == 0) { - bt_active = false; - } - if (coex_sta->high_priority_tx == 0xffff && - coex_sta->high_priority_rx == 0xffff && - coex_sta->low_priority_tx == 0xffff && - coex_sta->low_priority_rx == 0xffff) { - bt_active = false; - } - if (bt_active) { - bt_disable_cnt = 0; - bt_disabled = false; - btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_DISABLE, - &bt_disabled); - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT is enabled !!\n"); - } else { - bt_disable_cnt++; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], bt all counters = 0, %d times!!\n", - bt_disable_cnt); - if (bt_disable_cnt >= 2) { - bt_disabled = true; - btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_DISABLE, - &bt_disabled); - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT is disabled !!\n"); - halbtc8821a1ant_action_wifi_only(btcoexist); - } - } - if (pre_bt_disabled != bt_disabled) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT is from %s to %s!!\n", - (pre_bt_disabled ? "disabled" : "enabled"), - (bt_disabled ? "disabled" : "enabled")); - pre_bt_disabled = bt_disabled; - if (bt_disabled) { - btcoexist->btc_set(btcoexist, BTC_SET_ACT_LEAVE_LPS, - NULL); - btcoexist->btc_set(btcoexist, BTC_SET_ACT_NORMAL_LPS, - NULL); - } - } -} - -/*=============================================*/ -/**/ -/* Software Coex Mechanism start*/ -/**/ -/*=============================================*/ +/*********************************************** + * + * Software Coex Mechanism start + * + ***********************************************/ /* SCO only or SCO+PAN(HS)*/ static void halbtc8821a1ant_action_sco(struct btc_coexist *btcoexist) @@ -1788,10 +1490,8 @@ static void btc8821a1ant_act_wifi_con_bt_acl_busy(struct btc_coexist *btcoexist, return; } else if (bt_link_info->a2dp_only) { /*A2DP*/ - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a1ant_tdma_dur_adj(btcoexist, wifi_status); - } else { + if ((bt_rssi_state != BTC_RSSI_STATE_HIGH) && + (bt_rssi_state != BTC_RSSI_STATE_STAY_HIGH)) { /*for low BT RSSI*/ halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); @@ -2814,14 +2514,6 @@ void ex_halbtc8821a1ant_bt_info_notify(struct btc_coexist *btcoexist, false); } } -#if (BT_AUTO_REPORT_ONLY_8821A_1ANT == 0) - if (!(coex_sta->bt_info_ext & BIT4)) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT ext info bit4 check, set BT to enable Auto Report!!\n"); - halbtc8821a1ant_bt_auto_report(btcoexist, - FORCE_EXEC, true); - } -#endif } /* check BIT2 first ==> check if bt is under inquiry or page scan*/ @@ -2984,14 +2676,7 @@ void ex_halbtc8821a1ant_periodical(struct btc_coexist *btcoexist) #if (BT_AUTO_REPORT_ONLY_8821A_1ANT == 0) halbtc8821a1ant_query_bt_info(btcoexist); halbtc8821a1ant_monitor_bt_ctr(btcoexist); - btc8821a1ant_mon_bt_en_dis(btcoexist); #else - if (halbtc8821a1ant_Is_wifi_status_changed(btcoexist) || - coex_dm->auto_tdma_adjust) { - if (coex_sta->special_pkt_period_cnt > 2) - halbtc8821a1ant_run_coexist_mechanism(btcoexist); - } - coex_sta->special_pkt_period_cnt++; #endif } diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 1717e9ce96ca..7b7772b7d061 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -248,54 +248,6 @@ static u8 halbtc8821a2ant_wifi_rssi_state(struct btc_coexist *btcoexist, return wifi_rssi_state; } -static void btc8821a2ant_mon_bt_en_dis(struct btc_coexist *btcoexist) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - static bool pre_bt_disabled; - static u32 bt_disable_cnt; - bool bt_active = true, bt_disabled = false; - - /* This function check if bt is disabled*/ - - if (coex_sta->high_priority_tx == 0 && - coex_sta->high_priority_rx == 0 && - coex_sta->low_priority_tx == 0 && - coex_sta->low_priority_rx == 0) - bt_active = false; - if (coex_sta->high_priority_tx == 0xffff && - coex_sta->high_priority_rx == 0xffff && - coex_sta->low_priority_tx == 0xffff && - coex_sta->low_priority_rx == 0xffff) - bt_active = false; - if (bt_active) { - bt_disable_cnt = 0; - bt_disabled = false; - btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_DISABLE, - &bt_disabled); - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT is enabled !!\n"); - } else { - bt_disable_cnt++; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], bt all counters = 0, %d times!!\n", - bt_disable_cnt); - if (bt_disable_cnt >= 2) { - bt_disabled = true; - btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_DISABLE, - &bt_disabled); - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT is disabled !!\n"); - } - } - if (pre_bt_disabled != bt_disabled) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT is from %s to %s!!\n", - (pre_bt_disabled ? "disabled" : "enabled"), - (bt_disabled ? "disabled" : "enabled")); - pre_bt_disabled = bt_disabled; - } -} - static void halbtc8821a2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -536,43 +488,6 @@ static u8 halbtc8821a2ant_action_algorithm(struct btc_coexist *btcoexist) return algorithm; } -static bool halbtc8821a2ant_need_to_dec_bt_pwr(struct btc_coexist *btcoexist) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - bool ret = false; - bool bt_hs_on = false, wifi_connected = false; - long bt_hs_rssi = 0; - u8 bt_rssi_state; - - if (!btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on)) - return false; - if (!btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, - &wifi_connected)) - return false; - if (!btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi)) - return false; - - bt_rssi_state = halbtc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); - - if (wifi_connected) { - if (bt_hs_on) { - if (bt_hs_rssi > 37) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Need to decrease bt power for HS mode!!\n"); - ret = true; - } - } else { - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Need to decrease bt power for Wifi is connected!!\n"); - ret = true; - } - } - } - return ret; -} - static void btc8821a2ant_set_fw_dac_swing_lev(struct btc_coexist *btcoexist, u8 dac_swing_lvl) { @@ -634,140 +549,6 @@ static void halbtc8821a2ant_dec_bt_pwr(struct btc_coexist *btcoexist, coex_dm->pre_dec_bt_pwr = coex_dm->cur_dec_bt_pwr; } -static void btc8821a2ant_set_fw_bt_lna_constr(struct btc_coexist *btcoexist, - bool bt_lna_cons_on) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - u8 h2c_parameter[2] = {0}; - - h2c_parameter[0] = 0x3; /* opCode, 0x3 = BT_SET_LNA_CONSTRAIN */ - - if (bt_lna_cons_on) - h2c_parameter[1] |= BIT0; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], set BT LNA Constrain: %s, FW write 0x69 = 0x%x\n", - bt_lna_cons_on ? "ON!!" : "OFF!!", - h2c_parameter[0] << 8 | h2c_parameter[1]); - - btcoexist->btc_fill_h2c(btcoexist, 0x69, 2, h2c_parameter); -} - -static void btc8821a2_set_bt_lna_const(struct btc_coexist *btcoexist, - bool force_exec, bool bt_lna_cons_on) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], %s BT Constrain = %s\n", - (force_exec ? "force" : ""), - ((bt_lna_cons_on) ? "ON" : "OFF")); - coex_dm->cur_bt_lna_constrain = bt_lna_cons_on; - - if (!force_exec) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], pre_bt_lna_constrain = %d,cur_bt_lna_constrain = %d\n", - coex_dm->pre_bt_lna_constrain, - coex_dm->cur_bt_lna_constrain); - - if (coex_dm->pre_bt_lna_constrain == - coex_dm->cur_bt_lna_constrain) - return; - } - btc8821a2ant_set_fw_bt_lna_constr(btcoexist, - coex_dm->cur_bt_lna_constrain); - - coex_dm->pre_bt_lna_constrain = coex_dm->cur_bt_lna_constrain; -} - -static void halbtc8821a2ant_set_fw_bt_psd_mode(struct btc_coexist *btcoexist, - u8 bt_psd_mode) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - u8 h2c_parameter[2] = {0}; - - h2c_parameter[0] = 0x2; /* opCode, 0x2 = BT_SET_PSD_MODE */ - - h2c_parameter[1] = bt_psd_mode; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], set BT PSD mode = 0x%x, FW write 0x69 = 0x%x\n", - h2c_parameter[1], - h2c_parameter[0] << 8 | h2c_parameter[1]); - - btcoexist->btc_fill_h2c(btcoexist, 0x69, 2, h2c_parameter); -} - -static void halbtc8821a2ant_set_bt_psd_mode(struct btc_coexist *btcoexist, - bool force_exec, u8 bt_psd_mode) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], %s BT PSD mode = 0x%x\n", - (force_exec ? "force" : ""), bt_psd_mode); - coex_dm->cur_bt_psd_mode = bt_psd_mode; - - if (!force_exec) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], pre_bt_psd_mode = 0x%x, cur_bt_psd_mode = 0x%x\n", - coex_dm->pre_bt_psd_mode, coex_dm->cur_bt_psd_mode); - - if (coex_dm->pre_bt_psd_mode == coex_dm->cur_bt_psd_mode) - return; - } - halbtc8821a2ant_set_fw_bt_psd_mode(btcoexist, - coex_dm->cur_bt_psd_mode); - - coex_dm->pre_bt_psd_mode = coex_dm->cur_bt_psd_mode; -} - -static void halbtc8821a2ant_set_bt_auto_report(struct btc_coexist *btcoexist, - bool enable_auto_report) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - u8 h2c_parameter[1] = {0}; - - h2c_parameter[0] = 0; - - if (enable_auto_report) - h2c_parameter[0] |= BIT0; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT FW auto report : %s, FW write 0x68 = 0x%x\n", - (enable_auto_report ? "Enabled!!" : "Disabled!!"), - h2c_parameter[0]); - - btcoexist->btc_fill_h2c(btcoexist, 0x68, 1, h2c_parameter); -} - -static void halbtc8821a2ant_bt_auto_report(struct btc_coexist *btcoexist, - bool force_exec, - bool enable_auto_report) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], %s BT Auto report = %s\n", - (force_exec ? "force to" : ""), - ((enable_auto_report) ? "Enabled" : "Disabled")); - coex_dm->cur_bt_auto_report = enable_auto_report; - - if (!force_exec) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], pre_bt_auto_report = %d, cur_bt_auto_report = %d\n", - coex_dm->pre_bt_auto_report, - coex_dm->cur_bt_auto_report); - - if (coex_dm->pre_bt_auto_report == coex_dm->cur_bt_auto_report) - return; - } - halbtc8821a2ant_set_bt_auto_report(btcoexist, - coex_dm->cur_bt_auto_report); - - coex_dm->pre_bt_auto_report = coex_dm->cur_bt_auto_report; -} - static void halbtc8821a2ant_fw_dac_swing_lvl(struct btc_coexist *btcoexist, bool force_exec, u8 fw_dac_swing_lvl) @@ -796,58 +577,6 @@ static void halbtc8821a2ant_fw_dac_swing_lvl(struct btc_coexist *btcoexist, coex_dm->pre_fw_dac_swing_lvl = coex_dm->cur_fw_dac_swing_lvl; } -static void btc8821a2ant_set_sw_rf_rx_lpf_corner(struct btc_coexist *btcoexist, - bool rx_rf_shrink_on) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - if (rx_rf_shrink_on) { - /* Shrink RF Rx LPF corner */ - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Shrink RF Rx LPF corner!!\n"); - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1e, - 0xfffff, 0xffffc); - } else { - /* Resume RF Rx LPF corner - * After initialized, we can use coex_dm->bt_rf0x1e_backup - */ - if (btcoexist->initilized) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Resume RF Rx LPF corner!!\n"); - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, - 0x1e, 0xfffff, - coex_dm->bt_rf0x1e_backup); - } - } -} - -static void halbtc8821a2ant_RfShrink(struct btc_coexist *btcoexist, - bool force_exec, bool rx_rf_shrink_on) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], %s turn Rx RF Shrink = %s\n", - (force_exec ? "force to" : ""), - ((rx_rf_shrink_on) ? "ON" : "OFF")); - coex_dm->cur_rf_rx_lpf_shrink = rx_rf_shrink_on; - - if (!force_exec) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], pre_rf_rx_lpf_shrink = %d, cur_rf_rx_lpf_shrink = %d\n", - coex_dm->pre_rf_rx_lpf_shrink, - coex_dm->cur_rf_rx_lpf_shrink); - - if (coex_dm->pre_rf_rx_lpf_shrink == - coex_dm->cur_rf_rx_lpf_shrink) - return; - } - btc8821a2ant_set_sw_rf_rx_lpf_corner(btcoexist, - coex_dm->cur_rf_rx_lpf_shrink); - - coex_dm->pre_rf_rx_lpf_shrink = coex_dm->cur_rf_rx_lpf_shrink; -} - static void btc8821a2ant_SetSwPenTxRateAdapt(struct btc_coexist *btcoexist, bool low_penalty_ra) { @@ -958,47 +687,6 @@ static void halbtc8821a2ant_dac_swing(struct btc_coexist *btcoexist, coex_dm->pre_dac_swing_lvl = coex_dm->cur_dac_swing_lvl; } -static void halbtc8821a2ant_set_adc_back_off(struct btc_coexist *btcoexist, - bool adc_back_off) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - if (adc_back_off) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BB BackOff Level On!\n"); - btcoexist->btc_write_1byte_bitmask(btcoexist, 0x8db, 0x60, 0x3); - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BB BackOff Level Off!\n"); - btcoexist->btc_write_1byte_bitmask(btcoexist, 0x8db, 0x60, 0x1); - } -} - -static void halbtc8821a2ant_adc_back_off(struct btc_coexist *btcoexist, - bool force_exec, bool adc_back_off) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], %s turn AdcBackOff = %s\n", - (force_exec ? "force to" : ""), - ((adc_back_off) ? "ON" : "OFF")); - coex_dm->cur_adc_back_off = adc_back_off; - - if (!force_exec) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], pre_adc_back_off = %d, cur_adc_back_off = %d\n", - coex_dm->pre_adc_back_off, - coex_dm->cur_adc_back_off); - - if (coex_dm->pre_adc_back_off == coex_dm->cur_adc_back_off) - return; - } - halbtc8821a2ant_set_adc_back_off(btcoexist, coex_dm->cur_adc_back_off); - - coex_dm->pre_adc_back_off = coex_dm->cur_adc_back_off; -} - static void halbtc8821a2ant_set_coex_table(struct btc_coexist *btcoexist, u32 val0x6c0, u32 val0x6c4, u32 val0x6c8, u8 val0x6cc) @@ -1152,14 +840,8 @@ static void btc8821a2ant_sw_mech1(struct btc_coexist *btcoexist, shrink_rx_lpf = false; } - halbtc8821a2ant_RfShrink(btcoexist, NORMAL_EXEC, shrink_rx_lpf); halbtc8821a2ant_low_penalty_ra(btcoexist, NORMAL_EXEC, low_penalty_ra); - - /* no limited DIG - * btc8821a2_set_bt_lna_const(btcoexist, - NORMAL_EXEC, bBTLNAConstrain); - */ } static void btc8821a2ant_sw_mech2(struct btc_coexist *btcoexist, @@ -1167,10 +849,8 @@ static void btc8821a2ant_sw_mech2(struct btc_coexist *btcoexist, bool adc_back_off, bool sw_dac_swing, u32 dac_swing_lvl) { - /* halbtc8821a2ant_AgcTable(btcoexist, NORMAL_EXEC, bAGCTableShift); */ - halbtc8821a2ant_adc_back_off(btcoexist, NORMAL_EXEC, adc_back_off); halbtc8821a2ant_dac_swing(btcoexist, NORMAL_EXEC, sw_dac_swing, - sw_dac_swing); + sw_dac_swing); } static void halbtc8821a2ant_set_ant_path(struct btc_coexist *btcoexist, @@ -1541,12 +1221,6 @@ static bool halbtc8821a2ant_is_common_action(struct btc_coexist *btcoexist) halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 21); - if (halbtc8821a2ant_need_to_dec_bt_pwr(btcoexist)) - halbtc8821a2ant_dec_bt_pwr(btcoexist, - NORMAL_EXEC, true); - else - halbtc8821a2ant_dec_bt_pwr(btcoexist, - NORMAL_EXEC, false); common = true; } @@ -1555,585 +1229,6 @@ static bool halbtc8821a2ant_is_common_action(struct btc_coexist *btcoexist) return common; } -static void btc8821a2_int1(struct btc_coexist *btcoexist, bool tx_pause, - int result) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - if (tx_pause) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 1\n"); - - if (coex_dm->cur_ps_tdma == 71) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); - coex_dm->tdma_adj_type = 5; - } else if (coex_dm->cur_ps_tdma == 1) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); - coex_dm->tdma_adj_type = 5; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 4) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } - if (coex_dm->cur_ps_tdma == 9) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 13); - coex_dm->tdma_adj_type = 13; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 12) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - - if (result == -1) { - if (coex_dm->cur_ps_tdma == 5) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } else if (coex_dm->cur_ps_tdma == 13) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 8) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); - coex_dm->tdma_adj_type = 5; - } else if (coex_dm->cur_ps_tdma == 16) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 13); - coex_dm->tdma_adj_type = 13; - } - } - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 0\n"); - if (coex_dm->cur_ps_tdma == 5) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 71); - coex_dm->tdma_adj_type = 71; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 8) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } - if (coex_dm->cur_ps_tdma == 13) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 16) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - - if (result == -1) { - if (coex_dm->cur_ps_tdma == 71) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 1); - coex_dm->tdma_adj_type = 1; - } else if (coex_dm->cur_ps_tdma == 1) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } else if (coex_dm->cur_ps_tdma == 9) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 4) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 1); - coex_dm->tdma_adj_type = 1; - } else if (coex_dm->cur_ps_tdma == 1) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 71); - coex_dm->tdma_adj_type = 71; - } else if (coex_dm->cur_ps_tdma == 12) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - } - } - } -} - -static void btc8821a2_int2(struct btc_coexist *btcoexist, bool tx_pause, - int result) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - if (tx_pause) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 1\n"); - if (coex_dm->cur_ps_tdma == 1) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 4) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } - if (coex_dm->cur_ps_tdma == 9) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 12) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - if (result == -1) { - if (coex_dm->cur_ps_tdma == 5) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } else if (coex_dm->cur_ps_tdma == 13) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 8) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; - } else if (coex_dm->cur_ps_tdma == 16) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; - } - } - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 0\n"); - if (coex_dm->cur_ps_tdma == 5) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 8) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } - if (coex_dm->cur_ps_tdma == 13) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 16) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - if (result == -1) { - if (coex_dm->cur_ps_tdma == 1) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } else if (coex_dm->cur_ps_tdma == 9) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 4) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 12) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; - } - } - } -} - -static void btc8821a2_int3(struct btc_coexist *btcoexist, bool tx_pause, - int result) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - - if (tx_pause) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 1\n"); - if (coex_dm->cur_ps_tdma == 1) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 4) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } - if (coex_dm->cur_ps_tdma == 9) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 12) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - if (result == -1) { - if (coex_dm->cur_ps_tdma == 5) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 8); - coex_dm->tdma_adj_type = 8; - } else if (coex_dm->cur_ps_tdma == 13) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 16); - coex_dm->tdma_adj_type = 16; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 8) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; - } else if (coex_dm->cur_ps_tdma == 16) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; - } - } - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TxPause = 0\n"); - if (coex_dm->cur_ps_tdma == 5) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 6) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 7) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 8) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } - if (coex_dm->cur_ps_tdma == 13) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 14) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 15) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 16) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - if (result == -1) { - if (coex_dm->cur_ps_tdma == 1) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 4); - coex_dm->tdma_adj_type = 4; - } else if (coex_dm->cur_ps_tdma == 9) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 12); - coex_dm->tdma_adj_type = 12; - } - } else if (result == 1) { - if (coex_dm->cur_ps_tdma == 4) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 3) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; - } else if (coex_dm->cur_ps_tdma == 12) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } else if (coex_dm->cur_ps_tdma == 10) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } - } - } -} - static void btc8821a2ant_tdma_dur_adj(struct btc_coexist *btcoexist, bool sco_hid, bool tx_pause, u8 max_interval) @@ -2319,12 +1414,6 @@ static void btc8821a2ant_tdma_dur_adj(struct btc_coexist *btcoexist, RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], max Interval = %d\n", max_interval); - if (max_interval == 1) - btc8821a2_int1(btcoexist, tx_pause, result); - else if (max_interval == 2) - btc8821a2_int2(btcoexist, tx_pause, result); - else if (max_interval == 3) - btc8821a2_int3(btcoexist, tx_pause, result); } /* if current PsTdma not match with the recorded one @@ -2366,7 +1455,7 @@ static void halbtc8821a2ant_action_sco(struct btc_coexist *btcoexist) halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 4); - if (halbtc8821a2ant_need_to_dec_bt_pwr(btcoexist)) + if (BTC_RSSI_HIGH(bt_rssi_state)) halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); @@ -2444,7 +1533,7 @@ static void halbtc8821a2ant_action_hid(struct btc_coexist *btcoexist) halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - if (halbtc8821a2ant_need_to_dec_bt_pwr(btcoexist)) + if (BTC_RSSI_HIGH(bt_rssi_state)) halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); @@ -2526,7 +1615,7 @@ static void halbtc8821a2ant_action_a2dp(struct btc_coexist *btcoexist) * halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); */ - if (halbtc8821a2ant_need_to_dec_bt_pwr(btcoexist)) + if (BTC_RSSI_HIGH(bt_rssi_state)) halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); @@ -2594,7 +1683,7 @@ static void halbtc8821a2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) *halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); */ - if (halbtc8821a2ant_need_to_dec_bt_pwr(btcoexist)) + if (BTC_RSSI_HIGH(bt_rssi_state)) halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); @@ -2661,7 +1750,7 @@ static void halbtc8821a2ant_action_pan_edr(struct btc_coexist *btcoexist) halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - if (halbtc8821a2ant_need_to_dec_bt_pwr(btcoexist)) + if (BTC_RSSI_HIGH(bt_rssi_state)) halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); @@ -2810,7 +1899,7 @@ static void halbtc8821a2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - if (halbtc8821a2ant_need_to_dec_bt_pwr(btcoexist)) + if (BTC_RSSI_HIGH(bt_rssi_state)) halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); @@ -2878,7 +1967,7 @@ static void halbtc8821a2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - if (halbtc8821a2ant_need_to_dec_bt_pwr(btcoexist)) + if (BTC_RSSI_HIGH(bt_rssi_state)) halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); @@ -2954,7 +2043,7 @@ static void btc8821a2ant_act_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - if (halbtc8821a2ant_need_to_dec_bt_pwr(btcoexist)) + if (BTC_RSSI_HIGH(bt_rssi_state)) halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); @@ -3032,7 +2121,7 @@ static void halbtc8821a2ant_action_hid_a2dp(struct btc_coexist *btcoexist) 15, 0); bt_rssi_state = halbtc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); - if (halbtc8821a2ant_need_to_dec_bt_pwr(btcoexist)) + if (BTC_RSSI_HIGH(bt_rssi_state)) halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); @@ -3592,7 +2681,6 @@ void ex_halbtc8821a2ant_bt_info_notify(struct btc_coexist *btcoexist, struct rtl_priv *rtlpriv = btcoexist->adapter; u8 bt_info = 0; u8 i, rsp_source = 0; - static u32 set_bt_lna_cnt, set_bt_psd_mode; bool bt_busy = false, limited_dig = false; bool wifi_connected = false, bt_hs_on = false; @@ -3642,25 +2730,6 @@ void ex_halbtc8821a2ant_bt_info_notify(struct btc_coexist *btcoexist, BTC_MEDIA_DISCONNECT); } - set_bt_psd_mode = 0; - } - if (set_bt_psd_mode <= 3) { - halbtc8821a2ant_set_bt_psd_mode(btcoexist, FORCE_EXEC, - 0x0); /*fix CH-BW mode*/ - set_bt_psd_mode++; - } - - if (coex_dm->cur_bt_lna_constrain) { - if (!(coex_sta->bt_info_ext & BIT2)) { - if (set_bt_lna_cnt <= 3) { - btc8821a2_set_bt_lna_const(btcoexist, - FORCE_EXEC, - true); - set_bt_lna_cnt++; - } - } - } else { - set_bt_lna_cnt = 0; } if ((coex_sta->bt_info_ext & BIT3)) { @@ -3669,13 +2738,6 @@ void ex_halbtc8821a2ant_bt_info_notify(struct btc_coexist *btcoexist, } else { /* BT already NOT ignore Wlan active, do nothing here.*/ } - - if ((coex_sta->bt_info_ext & BIT4)) { - /* BT auto report already enabled, do nothing*/ - } else { - halbtc8821a2ant_bt_auto_report(btcoexist, - FORCE_EXEC, true); - } } btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); @@ -3787,5 +2849,4 @@ void ex_halbtc8821a2ant_periodical(struct btc_coexist *btcoexist) halbtc8821a2ant_query_bt_info(btcoexist); halbtc8821a2ant_monitor_bt_ctr(btcoexist); - btc8821a2ant_mon_bt_en_dis(btcoexist); } -- cgit From c6821613e653aae4f54c75689e229e3f063b7f69 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 3 Apr 2017 13:41:32 -0500 Subject: rtlwifi: btcoex: follow linux coding style Fix a number of checkpatch.pl warnings. In addition, some variable and function names are shortened, and/or renamed to be more consistent. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8192e2ant.c | 1878 ++++++++++---------- .../realtek/rtlwifi/btcoexist/halbtc8192e2ant.h | 24 +- .../realtek/rtlwifi/btcoexist/halbtc8723b1ant.c | 416 ++--- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 339 ++-- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.h | 1 + .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 1141 ++++++------ .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.h | 2 +- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 1347 +++++++------- .../realtek/rtlwifi/btcoexist/halbtcoutsrc.c | 2 +- .../realtek/rtlwifi/btcoexist/halbtcoutsrc.h | 2 +- 10 files changed, 2493 insertions(+), 2659 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c index 113eea8e25eb..57e633dbf9a9 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.c @@ -44,7 +44,7 @@ static struct coex_dm_8192e_2ant *coex_dm = &glcoex_dm_8192e_2ant; static struct coex_sta_8192e_2ant glcoex_sta_8192e_2ant; static struct coex_sta_8192e_2ant *coex_sta = &glcoex_sta_8192e_2ant; -static const char *const GLBtInfoSrc8192e2Ant[] = { +static const char *const glbt_info_src_8192e_2ant[] = { "BT Info[wifi fw]", "BT Info[bt rsp]", "BT Info[bt auto report]", @@ -57,31 +57,31 @@ static u32 glcoex_ver_8192e_2ant = 0x34; * local function proto type if needed **************************************************************/ /************************************************************** - * local function start with halbtc8192e2ant_ + * local function start with btc8192e2ant_ **************************************************************/ -static u8 halbtc8192e2ant_btrssi_state(struct btc_coexist *btcoexist, - u8 level_num, u8 rssi_thresh, - u8 rssi_thresh1) +static u8 btc8192e2ant_bt_rssi_state(struct btc_coexist *btcoexist, + u8 level_num, u8 rssi_thresh, + u8 rssi_thresh1) { struct rtl_priv *rtlpriv = btcoexist->adapter; - int btrssi = 0; - u8 btrssi_state = coex_sta->pre_bt_rssi_state; + int bt_rssi = 0; + u8 bt_rssi_state = coex_sta->pre_bt_rssi_state; - btrssi = coex_sta->bt_rssi; + bt_rssi = coex_sta->bt_rssi; if (level_num == 2) { if ((coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_LOW) || (coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { - if (btrssi >= + if (bt_rssi >= (rssi_thresh + BTC_RSSI_COEX_THRESH_TOL_8192E_2ANT)) - btrssi_state = BTC_RSSI_STATE_HIGH; + bt_rssi_state = BTC_RSSI_STATE_HIGH; else - btrssi_state = BTC_RSSI_STATE_STAY_LOW; + bt_rssi_state = BTC_RSSI_STATE_STAY_LOW; } else { - if (btrssi < rssi_thresh) - btrssi_state = BTC_RSSI_STATE_LOW; + if (bt_rssi < rssi_thresh) + bt_rssi_state = BTC_RSSI_STATE_LOW; else - btrssi_state = BTC_RSSI_STATE_STAY_HIGH; + bt_rssi_state = BTC_RSSI_STATE_STAY_HIGH; } } else if (level_num == 3) { if (rssi_thresh > rssi_thresh1) { @@ -89,62 +89,63 @@ static u8 halbtc8192e2ant_btrssi_state(struct btc_coexist *btcoexist, "[BTCoex], BT Rssi thresh error!!\n"); return coex_sta->pre_bt_rssi_state; } + if ((coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_LOW) || (coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { - if (btrssi >= + if (bt_rssi >= (rssi_thresh + BTC_RSSI_COEX_THRESH_TOL_8192E_2ANT)) - btrssi_state = BTC_RSSI_STATE_MEDIUM; + bt_rssi_state = BTC_RSSI_STATE_MEDIUM; else - btrssi_state = BTC_RSSI_STATE_STAY_LOW; + bt_rssi_state = BTC_RSSI_STATE_STAY_LOW; } else if ((coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_MEDIUM) || (coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { - if (btrssi >= (rssi_thresh1 + + if (bt_rssi >= (rssi_thresh1 + BTC_RSSI_COEX_THRESH_TOL_8192E_2ANT)) - btrssi_state = BTC_RSSI_STATE_HIGH; - else if (btrssi < rssi_thresh) - btrssi_state = BTC_RSSI_STATE_LOW; + bt_rssi_state = BTC_RSSI_STATE_HIGH; + else if (bt_rssi < rssi_thresh) + bt_rssi_state = BTC_RSSI_STATE_LOW; else - btrssi_state = BTC_RSSI_STATE_STAY_MEDIUM; + bt_rssi_state = BTC_RSSI_STATE_STAY_MEDIUM; } else { - if (btrssi < rssi_thresh1) - btrssi_state = BTC_RSSI_STATE_MEDIUM; + if (bt_rssi < rssi_thresh1) + bt_rssi_state = BTC_RSSI_STATE_MEDIUM; else - btrssi_state = BTC_RSSI_STATE_STAY_HIGH; + bt_rssi_state = BTC_RSSI_STATE_STAY_HIGH; } } - coex_sta->pre_bt_rssi_state = btrssi_state; + coex_sta->pre_bt_rssi_state = bt_rssi_state; - return btrssi_state; + return bt_rssi_state; } -static u8 halbtc8192e2ant_wifirssi_state(struct btc_coexist *btcoexist, - u8 index, u8 level_num, u8 rssi_thresh, - u8 rssi_thresh1) +static u8 btc8192e2ant_wifi_rssi_state(struct btc_coexist *btcoexist, + u8 index, u8 level_num, u8 rssi_thresh, + u8 rssi_thresh1) { struct rtl_priv *rtlpriv = btcoexist->adapter; - int wifirssi = 0; - u8 wifirssi_state = coex_sta->pre_wifi_rssi_state[index]; + int wifi_rssi = 0; + u8 wifi_rssi_state = coex_sta->pre_wifi_rssi_state[index]; - btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifirssi); + btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi); if (level_num == 2) { if ((coex_sta->pre_wifi_rssi_state[index] == BTC_RSSI_STATE_LOW) || (coex_sta->pre_wifi_rssi_state[index] == BTC_RSSI_STATE_STAY_LOW)) { - if (wifirssi >= (rssi_thresh + - BTC_RSSI_COEX_THRESH_TOL_8192E_2ANT)) - wifirssi_state = BTC_RSSI_STATE_HIGH; + if (wifi_rssi >= + (rssi_thresh + BTC_RSSI_COEX_THRESH_TOL_8192E_2ANT)) + wifi_rssi_state = BTC_RSSI_STATE_HIGH; else - wifirssi_state = BTC_RSSI_STATE_STAY_LOW; + wifi_rssi_state = BTC_RSSI_STATE_STAY_LOW; } else { - if (wifirssi < rssi_thresh) - wifirssi_state = BTC_RSSI_STATE_LOW; + if (wifi_rssi < rssi_thresh) + wifi_rssi_state = BTC_RSSI_STATE_LOW; else - wifirssi_state = BTC_RSSI_STATE_STAY_HIGH; + wifi_rssi_state = BTC_RSSI_STATE_STAY_HIGH; } } else if (level_num == 3) { if (rssi_thresh > rssi_thresh1) { @@ -157,36 +158,37 @@ static u8 halbtc8192e2ant_wifirssi_state(struct btc_coexist *btcoexist, BTC_RSSI_STATE_LOW) || (coex_sta->pre_wifi_rssi_state[index] == BTC_RSSI_STATE_STAY_LOW)) { - if (wifirssi >= (rssi_thresh + - BTC_RSSI_COEX_THRESH_TOL_8192E_2ANT)) - wifirssi_state = BTC_RSSI_STATE_MEDIUM; + if (wifi_rssi >= + (rssi_thresh + BTC_RSSI_COEX_THRESH_TOL_8192E_2ANT)) + wifi_rssi_state = BTC_RSSI_STATE_MEDIUM; else - wifirssi_state = BTC_RSSI_STATE_STAY_LOW; + wifi_rssi_state = BTC_RSSI_STATE_STAY_LOW; } else if ((coex_sta->pre_wifi_rssi_state[index] == BTC_RSSI_STATE_MEDIUM) || (coex_sta->pre_wifi_rssi_state[index] == BTC_RSSI_STATE_STAY_MEDIUM)) { - if (wifirssi >= (rssi_thresh1 + + if (wifi_rssi >= (rssi_thresh1 + BTC_RSSI_COEX_THRESH_TOL_8192E_2ANT)) - wifirssi_state = BTC_RSSI_STATE_HIGH; - else if (wifirssi < rssi_thresh) - wifirssi_state = BTC_RSSI_STATE_LOW; + wifi_rssi_state = BTC_RSSI_STATE_HIGH; + else if (wifi_rssi < rssi_thresh) + wifi_rssi_state = BTC_RSSI_STATE_LOW; else - wifirssi_state = BTC_RSSI_STATE_STAY_MEDIUM; + wifi_rssi_state = BTC_RSSI_STATE_STAY_MEDIUM; } else { - if (wifirssi < rssi_thresh1) - wifirssi_state = BTC_RSSI_STATE_MEDIUM; + if (wifi_rssi < rssi_thresh1) + wifi_rssi_state = BTC_RSSI_STATE_MEDIUM; else - wifirssi_state = BTC_RSSI_STATE_STAY_HIGH; + wifi_rssi_state = BTC_RSSI_STATE_STAY_HIGH; } } - coex_sta->pre_wifi_rssi_state[index] = wifirssi_state; + coex_sta->pre_wifi_rssi_state[index] = wifi_rssi_state; - return wifirssi_state; + return wifi_rssi_state; } -static void btc8192e2ant_monitor_bt_enable_dis(struct btc_coexist *btcoexist) +static void btc8192e2ant_monitor_bt_enable_disable(struct btc_coexist + *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; static bool pre_bt_disabled; @@ -236,57 +238,57 @@ static void btc8192e2ant_monitor_bt_enable_dis(struct btc_coexist *btcoexist) } } -static u32 halbtc8192e2ant_decidera_mask(struct btc_coexist *btcoexist, - u8 sstype, u32 ra_masktype) +static u32 btc8192e2ant_decide_ra_mask(struct btc_coexist *btcoexist, + u8 ss_type, u32 ra_mask_type) { - u32 disra_mask = 0x0; + u32 dis_ra_mask = 0x0; - switch (ra_masktype) { + switch (ra_mask_type) { case 0: /* normal mode */ - if (sstype == 2) - disra_mask = 0x0; /* enable 2ss */ + if (ss_type == 2) + dis_ra_mask = 0x0; /* enable 2ss */ else - disra_mask = 0xfff00000;/* disable 2ss */ + dis_ra_mask = 0xfff00000; /* disable 2ss */ break; case 1: /* disable cck 1/2 */ - if (sstype == 2) - disra_mask = 0x00000003;/* enable 2ss */ + if (ss_type == 2) + dis_ra_mask = 0x00000003; /* enable 2ss */ else - disra_mask = 0xfff00003;/* disable 2ss */ + dis_ra_mask = 0xfff00003; /* disable 2ss */ break; case 2: /* disable cck 1/2/5.5, ofdm 6/9/12/18/24, mcs 0/1/2/3/4 */ - if (sstype == 2) - disra_mask = 0x0001f1f7;/* enable 2ss */ + if (ss_type == 2) + dis_ra_mask = 0x0001f1f7; /* enable 2ss */ else - disra_mask = 0xfff1f1f7;/* disable 2ss */ + dis_ra_mask = 0xfff1f1f7; /* disable 2ss */ break; default: break; } - return disra_mask; + return dis_ra_mask; } -static void halbtc8192e2ant_Updatera_mask(struct btc_coexist *btcoexist, - bool force_exec, u32 dis_ratemask) +static void btc8192e2ant_update_ra_mask(struct btc_coexist *btcoexist, + bool force_exec, u32 dis_rate_mask) { - coex_dm->curra_mask = dis_ratemask; + coex_dm->cur_ra_mask = dis_rate_mask; - if (force_exec || (coex_dm->prera_mask != coex_dm->curra_mask)) - btcoexist->btc_set(btcoexist, BTC_SET_ACT_UPDATE_ra_mask, - &coex_dm->curra_mask); - coex_dm->prera_mask = coex_dm->curra_mask; + if (force_exec || (coex_dm->pre_ra_mask != coex_dm->cur_ra_mask)) + btcoexist->btc_set(btcoexist, BTC_SET_ACT_UPDATE_RAMASK, + &coex_dm->cur_ra_mask); + coex_dm->pre_ra_mask = coex_dm->cur_ra_mask; } -static void btc8192e2ant_autorate_fallback_retry(struct btc_coexist *btcoexist, - bool force_exec, u8 type) +static void btc8192e2ant_auto_rate_fallback_retry(struct btc_coexist *btcoexist, + bool force_exec, u8 type) { - bool wifi_under_bmode = false; + bool wifi_under_b_mode = false; - coex_dm->cur_arfrtype = type; + coex_dm->cur_arfr_type = type; - if (force_exec || (coex_dm->pre_arfrtype != coex_dm->cur_arfrtype)) { - switch (coex_dm->cur_arfrtype) { + if (force_exec || (coex_dm->pre_arfr_type != coex_dm->cur_arfr_type)) { + switch (coex_dm->cur_arfr_type) { case 0: /* normal mode */ btcoexist->btc_write_4byte(btcoexist, 0x430, coex_dm->backup_arfr_cnt1); @@ -296,8 +298,8 @@ static void btc8192e2ant_autorate_fallback_retry(struct btc_coexist *btcoexist, case 1: btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_B_MODE, - &wifi_under_bmode); - if (wifi_under_bmode) { + &wifi_under_b_mode); + if (wifi_under_b_mode) { btcoexist->btc_write_4byte(btcoexist, 0x430, 0x0); btcoexist->btc_write_4byte(btcoexist, 0x434, @@ -314,46 +316,45 @@ static void btc8192e2ant_autorate_fallback_retry(struct btc_coexist *btcoexist, } } - coex_dm->pre_arfrtype = coex_dm->cur_arfrtype; + coex_dm->pre_arfr_type = coex_dm->cur_arfr_type; } -static void halbtc8192e2ant_retrylimit(struct btc_coexist *btcoexist, - bool force_exec, u8 type) +static void btc8192e2ant_retry_limit(struct btc_coexist *btcoexist, + bool force_exec, u8 type) { - coex_dm->cur_retrylimit_type = type; + coex_dm->cur_retry_limit_type = type; - if (force_exec || (coex_dm->pre_retrylimit_type != - coex_dm->cur_retrylimit_type)) { - switch (coex_dm->cur_retrylimit_type) { + if (force_exec || (coex_dm->pre_retry_limit_type != + coex_dm->cur_retry_limit_type)) { + switch (coex_dm->cur_retry_limit_type) { case 0: /* normal mode */ - btcoexist->btc_write_2byte(btcoexist, 0x42a, - coex_dm->backup_retrylimit); - break; + btcoexist->btc_write_2byte(btcoexist, 0x42a, + coex_dm->backup_retry_limit); + break; case 1: /* retry limit = 8 */ - btcoexist->btc_write_2byte(btcoexist, 0x42a, - 0x0808); - break; + btcoexist->btc_write_2byte(btcoexist, 0x42a, 0x0808); + break; default: - break; + break; } } - coex_dm->pre_retrylimit_type = coex_dm->cur_retrylimit_type; + coex_dm->pre_retry_limit_type = coex_dm->cur_retry_limit_type; } -static void halbtc8192e2ant_ampdu_maxtime(struct btc_coexist *btcoexist, - bool force_exec, u8 type) +static void btc8192e2ant_ampdu_maxtime(struct btc_coexist *btcoexist, + bool force_exec, u8 type) { - coex_dm->cur_ampdutime_type = type; + coex_dm->cur_ampdu_time_type = type; - if (force_exec || (coex_dm->pre_ampdutime_type != - coex_dm->cur_ampdutime_type)) { - switch (coex_dm->cur_ampdutime_type) { + if (force_exec || (coex_dm->pre_ampdu_time_type != + coex_dm->cur_ampdu_time_type)) { + switch (coex_dm->cur_ampdu_time_type) { case 0: /* normal mode */ btcoexist->btc_write_1byte(btcoexist, 0x456, coex_dm->backup_ampdu_maxtime); break; - case 1: /* AMPDU timw = 0x38 * 32us */ + case 1: /* AMPDU time = 0x38 * 32us */ btcoexist->btc_write_1byte(btcoexist, 0x456, 0x38); break; default: @@ -361,30 +362,30 @@ static void halbtc8192e2ant_ampdu_maxtime(struct btc_coexist *btcoexist, } } - coex_dm->pre_ampdutime_type = coex_dm->cur_ampdutime_type; + coex_dm->pre_ampdu_time_type = coex_dm->cur_ampdu_time_type; } -static void halbtc8192e2ant_limited_tx(struct btc_coexist *btcoexist, - bool force_exec, u8 ra_masktype, - u8 arfr_type, u8 retrylimit_type, - u8 ampdutime_type) +static void btc8192e2ant_limited_tx(struct btc_coexist *btcoexist, + bool force_exec, u8 ra_mask_type, + u8 arfr_type, u8 retry_limit_type, + u8 ampdu_time_type) { - u32 disra_mask = 0x0; + u32 dis_ra_mask = 0x0; - coex_dm->curra_masktype = ra_masktype; - disra_mask = halbtc8192e2ant_decidera_mask(btcoexist, - coex_dm->cur_sstype, - ra_masktype); - halbtc8192e2ant_Updatera_mask(btcoexist, force_exec, disra_mask); - btc8192e2ant_autorate_fallback_retry(btcoexist, force_exec, arfr_type); - halbtc8192e2ant_retrylimit(btcoexist, force_exec, retrylimit_type); - halbtc8192e2ant_ampdu_maxtime(btcoexist, force_exec, ampdutime_type); + coex_dm->cur_ra_mask_type = ra_mask_type; + dis_ra_mask = + btc8192e2ant_decide_ra_mask(btcoexist, coex_dm->cur_ss_type, + ra_mask_type); + btc8192e2ant_update_ra_mask(btcoexist, force_exec, dis_ra_mask); + btc8192e2ant_auto_rate_fallback_retry(btcoexist, force_exec, arfr_type); + btc8192e2ant_retry_limit(btcoexist, force_exec, retry_limit_type); + btc8192e2ant_ampdu_maxtime(btcoexist, force_exec, ampdu_time_type); } -static void halbtc8192e2ant_limited_rx(struct btc_coexist *btcoexist, - bool force_exec, bool rej_ap_agg_pkt, - bool bt_ctrl_agg_buf_size, - u8 agg_buf_size) +static void btc8192e2ant_limited_rx(struct btc_coexist *btcoexist, + bool force_exec, bool rej_ap_agg_pkt, + bool bt_ctrl_agg_buf_size, + u8 agg_buf_size) { bool reject_rx_agg = rej_ap_agg_pkt; bool bt_ctrl_rx_agg_size = bt_ctrl_agg_buf_size; @@ -406,7 +407,7 @@ static void halbtc8192e2ant_limited_rx(struct btc_coexist *btcoexist, btcoexist->btc_set(btcoexist, BTC_SET_ACT_AGGREGATE_CTRL, NULL); } -static void halbtc8192e2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) +static void btc8192e2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; u32 reg_hp_txrx, reg_lp_txrx, u32tmp; @@ -417,11 +418,11 @@ static void halbtc8192e2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) u32tmp = btcoexist->btc_read_4byte(btcoexist, reg_hp_txrx); reg_hp_tx = u32tmp & MASKLWORD; - reg_hp_rx = (u32tmp & MASKHWORD)>>16; + reg_hp_rx = (u32tmp & MASKHWORD) >> 16; u32tmp = btcoexist->btc_read_4byte(btcoexist, reg_lp_txrx); reg_lp_tx = u32tmp & MASKLWORD; - reg_lp_rx = (u32tmp & MASKHWORD)>>16; + reg_lp_rx = (u32tmp & MASKHWORD) >> 16; coex_sta->high_priority_tx = reg_hp_tx; coex_sta->high_priority_rx = reg_hp_rx; @@ -439,14 +440,14 @@ static void halbtc8192e2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc); } -static void halbtc8192e2ant_querybt_info(struct btc_coexist *btcoexist) +static void btc8192e2ant_query_bt_info(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[1] = {0}; coex_sta->c2h_bt_info_req_sent = true; - h2c_parameter[0] |= BIT0; /* trigger */ + h2c_parameter[0] |= BIT0; /* trigger */ RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Query Bt Info, FW write 0x61 = 0x%x\n", @@ -455,12 +456,12 @@ static void halbtc8192e2ant_querybt_info(struct btc_coexist *btcoexist) btcoexist->btc_fill_h2c(btcoexist, 0x61, 1, h2c_parameter); } -static void halbtc8192e2ant_update_btlink_info(struct btc_coexist *btcoexist) +static void btc8192e2ant_update_bt_link_info(struct btc_coexist *btcoexist) { struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; - bool bt_hson = false; + bool bt_hs_on = false; - btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hson); + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); bt_link_info->bt_link_exist = coex_sta->bt_link_exist; bt_link_info->sco_exist = coex_sta->sco_exist; @@ -469,7 +470,7 @@ static void halbtc8192e2ant_update_btlink_info(struct btc_coexist *btcoexist) bt_link_info->hid_exist = coex_sta->hid_exist; /* work around for HS mode. */ - if (bt_hson) { + if (bt_hs_on) { bt_link_info->pan_exist = true; bt_link_info->bt_link_exist = true; } @@ -511,16 +512,16 @@ static void halbtc8192e2ant_update_btlink_info(struct btc_coexist *btcoexist) bt_link_info->hid_only = false; } -static u8 halbtc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) +static u8 btc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; struct btc_stack_info *stack_info = &btcoexist->stack_info; - bool bt_hson = false; + bool bt_hs_on = false; u8 algorithm = BT_8192E_2ANT_COEX_ALGO_UNDEFINED; - u8 numdiffprofile = 0; + u8 num_of_diff_profile = 0; - btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hson); + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); if (!bt_link_info->bt_link_exist) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -529,15 +530,15 @@ static u8 halbtc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) } if (bt_link_info->sco_exist) - numdiffprofile++; + num_of_diff_profile++; if (bt_link_info->hid_exist) - numdiffprofile++; + num_of_diff_profile++; if (bt_link_info->pan_exist) - numdiffprofile++; + num_of_diff_profile++; if (bt_link_info->a2dp_exist) - numdiffprofile++; + num_of_diff_profile++; - if (numdiffprofile == 1) { + if (num_of_diff_profile == 1) { if (bt_link_info->sco_exist) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "SCO only\n"); @@ -552,7 +553,7 @@ static u8 halbtc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) "A2DP only\n"); algorithm = BT_8192E_2ANT_COEX_ALGO_A2DP; } else if (bt_link_info->pan_exist) { - if (bt_hson) { + if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "PAN(HS) only\n"); @@ -567,7 +568,7 @@ static u8 halbtc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) } } } - } else if (numdiffprofile == 2) { + } else if (num_of_diff_profile == 2) { if (bt_link_info->sco_exist) { if (bt_link_info->hid_exist) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -578,7 +579,7 @@ static u8 halbtc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) "SCO + A2DP ==> SCO\n"); algorithm = BT_8192E_2ANT_COEX_ALGO_PANEDR_HID; } else if (bt_link_info->pan_exist) { - if (bt_hson) { + if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "SCO + PAN(HS)\n"); @@ -609,7 +610,7 @@ static u8 halbtc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) } } else if (bt_link_info->hid_exist && bt_link_info->pan_exist) { - if (bt_hson) { + if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "HID + PAN(HS)\n"); @@ -623,7 +624,7 @@ static u8 halbtc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) } } else if (bt_link_info->pan_exist && bt_link_info->a2dp_exist) { - if (bt_hson) { + if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "A2DP + PAN(HS)\n"); @@ -638,7 +639,7 @@ static u8 halbtc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) } } } - } else if (numdiffprofile == 3) { + } else if (num_of_diff_profile == 3) { if (bt_link_info->sco_exist) { if (bt_link_info->hid_exist && bt_link_info->a2dp_exist) { @@ -647,7 +648,7 @@ static u8 halbtc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) algorithm = BT_8192E_2ANT_COEX_ALGO_PANEDR_HID; } else if (bt_link_info->hid_exist && bt_link_info->pan_exist) { - if (bt_hson) { + if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "SCO + HID + PAN(HS)\n"); @@ -661,7 +662,7 @@ static u8 halbtc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) } } else if (bt_link_info->pan_exist && bt_link_info->a2dp_exist) { - if (bt_hson) { + if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "SCO + A2DP + PAN(HS)\n"); @@ -678,7 +679,7 @@ static u8 halbtc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) if (bt_link_info->hid_exist && bt_link_info->pan_exist && bt_link_info->a2dp_exist) { - if (bt_hson) { + if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "HID + A2DP + PAN(HS)\n"); @@ -693,12 +694,12 @@ static u8 halbtc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) } } } - } else if (numdiffprofile >= 3) { + } else if (num_of_diff_profile >= 3) { if (bt_link_info->sco_exist) { if (bt_link_info->hid_exist && bt_link_info->pan_exist && bt_link_info->a2dp_exist) { - if (bt_hson) { + if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "ErrorSCO+HID+A2DP+PAN(HS)\n"); @@ -717,8 +718,8 @@ static u8 halbtc8192e2ant_action_algorithm(struct btc_coexist *btcoexist) return algorithm; } -static void halbtc8192e2ant_setfw_dac_swinglevel(struct btc_coexist *btcoexist, - u8 dac_swinglvl) +static void btc8192e2ant_set_fw_dac_swing_level(struct btc_coexist *btcoexist, + u8 dac_swing_lvl) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[1] = {0}; @@ -726,81 +727,81 @@ static void halbtc8192e2ant_setfw_dac_swinglevel(struct btc_coexist *btcoexist, /* There are several type of dacswing * 0x18/ 0x10/ 0xc/ 0x8/ 0x4/ 0x6 */ - h2c_parameter[0] = dac_swinglvl; + h2c_parameter[0] = dac_swing_lvl; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Set Dac Swing Level = 0x%x\n", dac_swinglvl); + "[BTCoex], Set Dac Swing Level = 0x%x\n", dac_swing_lvl); RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], FW write 0x64 = 0x%x\n", h2c_parameter[0]); btcoexist->btc_fill_h2c(btcoexist, 0x64, 1, h2c_parameter); } -static void halbtc8192e2ant_set_fwdec_btpwr(struct btc_coexist *btcoexist, - u8 dec_btpwr_lvl) +static void btc8192e2ant_set_fw_dec_bt_pwr(struct btc_coexist *btcoexist, + u8 dec_bt_pwr_lvl) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[1] = {0}; - h2c_parameter[0] = dec_btpwr_lvl; + h2c_parameter[0] = dec_bt_pwr_lvl; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex] decrease Bt Power level = %d, FW write 0x62 = 0x%x\n", - dec_btpwr_lvl, h2c_parameter[0]); + dec_bt_pwr_lvl, h2c_parameter[0]); btcoexist->btc_fill_h2c(btcoexist, 0x62, 1, h2c_parameter); } -static void halbtc8192e2ant_dec_btpwr(struct btc_coexist *btcoexist, - bool force_exec, u8 dec_btpwr_lvl) +static void btc8192e2ant_dec_bt_pwr(struct btc_coexist *btcoexist, + bool force_exec, u8 dec_bt_pwr_lvl) { struct rtl_priv *rtlpriv = btcoexist->adapter; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], %s Dec BT power level = %d\n", - force_exec ? "force to" : "", dec_btpwr_lvl); - coex_dm->cur_dec_bt_pwr = dec_btpwr_lvl; + force_exec ? "force to" : "", dec_bt_pwr_lvl); + coex_dm->cur_dec_bt_pwr = dec_bt_pwr_lvl; if (!force_exec) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], preBtDecPwrLvl=%d, curBtDecPwrLvl=%d\n", coex_dm->pre_dec_bt_pwr, coex_dm->cur_dec_bt_pwr); } - halbtc8192e2ant_set_fwdec_btpwr(btcoexist, coex_dm->cur_dec_bt_pwr); + btc8192e2ant_set_fw_dec_bt_pwr(btcoexist, coex_dm->cur_dec_bt_pwr); coex_dm->pre_dec_bt_pwr = coex_dm->cur_dec_bt_pwr; } -static void halbtc8192e2ant_set_bt_autoreport(struct btc_coexist *btcoexist, - bool enable_autoreport) +static void btc8192e2ant_set_bt_auto_report(struct btc_coexist *btcoexist, + bool enable_auto_report) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[1] = {0}; h2c_parameter[0] = 0; - if (enable_autoreport) + if (enable_auto_report) h2c_parameter[0] |= BIT0; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], BT FW auto report : %s, FW write 0x68 = 0x%x\n", - (enable_autoreport ? "Enabled!!" : "Disabled!!"), + (enable_auto_report ? "Enabled!!" : "Disabled!!"), h2c_parameter[0]); btcoexist->btc_fill_h2c(btcoexist, 0x68, 1, h2c_parameter); } -static void halbtc8192e2ant_bt_autoreport(struct btc_coexist *btcoexist, - bool force_exec, - bool enable_autoreport) +static void btc8192e2ant_bt_auto_report(struct btc_coexist *btcoexist, + bool force_exec, + bool enable_auto_report) { struct rtl_priv *rtlpriv = btcoexist->adapter; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], %s BT Auto report = %s\n", (force_exec ? "force to" : ""), - ((enable_autoreport) ? "Enabled" : "Disabled")); - coex_dm->cur_bt_auto_report = enable_autoreport; + ((enable_auto_report) ? "Enabled" : "Disabled")); + coex_dm->cur_bt_auto_report = enable_auto_report; if (!force_exec) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -811,21 +812,21 @@ static void halbtc8192e2ant_bt_autoreport(struct btc_coexist *btcoexist, if (coex_dm->pre_bt_auto_report == coex_dm->cur_bt_auto_report) return; } - halbtc8192e2ant_set_bt_autoreport(btcoexist, - coex_dm->cur_bt_auto_report); + btc8192e2ant_set_bt_auto_report(btcoexist, + coex_dm->cur_bt_auto_report); coex_dm->pre_bt_auto_report = coex_dm->cur_bt_auto_report; } -static void halbtc8192e2ant_fw_dac_swinglvl(struct btc_coexist *btcoexist, - bool force_exec, u8 fw_dac_swinglvl) +static void btc8192e2ant_fw_dac_swing_lvl(struct btc_coexist *btcoexist, + bool force_exec, u8 fw_dac_swing_lvl) { struct rtl_priv *rtlpriv = btcoexist->adapter; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], %s set FW Dac Swing level = %d\n", - (force_exec ? "force to" : ""), fw_dac_swinglvl); - coex_dm->cur_fw_dac_swing_lvl = fw_dac_swinglvl; + (force_exec ? "force to" : ""), fw_dac_swing_lvl); + coex_dm->cur_fw_dac_swing_lvl = fw_dac_swing_lvl; if (!force_exec) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -838,8 +839,8 @@ static void halbtc8192e2ant_fw_dac_swinglvl(struct btc_coexist *btcoexist, return; } - halbtc8192e2ant_setfw_dac_swinglevel(btcoexist, - coex_dm->cur_fw_dac_swing_lvl); + btc8192e2ant_set_fw_dac_swing_level(btcoexist, + coex_dm->cur_fw_dac_swing_lvl); coex_dm->pre_fw_dac_swing_lvl = coex_dm->cur_fw_dac_swing_lvl; } @@ -869,8 +870,8 @@ static void btc8192e2ant_set_sw_rf_rx_lpf_corner(struct btc_coexist *btcoexist, } } -static void halbtc8192e2ant_rf_shrink(struct btc_coexist *btcoexist, - bool force_exec, bool rx_rf_shrink_on) +static void btc8192e2ant_rf_shrink(struct btc_coexist *btcoexist, + bool force_exec, bool rx_rf_shrink_on) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -896,8 +897,8 @@ static void halbtc8192e2ant_rf_shrink(struct btc_coexist *btcoexist, coex_dm->pre_rf_rx_lpf_shrink = coex_dm->cur_rf_rx_lpf_shrink; } -static void halbtc8192e2ant_set_dac_swingreg(struct btc_coexist *btcoexist, - u32 level) +static void btc8192e2ant_set_dac_swing_reg(struct btc_coexist *btcoexist, + u32 level) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 val = (u8)level; @@ -907,28 +908,28 @@ static void halbtc8192e2ant_set_dac_swingreg(struct btc_coexist *btcoexist, btcoexist->btc_write_1byte_bitmask(btcoexist, 0x883, 0x3e, val); } -static void btc8192e2ant_setsw_full_swing(struct btc_coexist *btcoexist, - bool sw_dac_swingon, - u32 sw_dac_swinglvl) +static void btc8192e2ant_set_sw_full_swing(struct btc_coexist *btcoexist, + bool sw_dac_swing_on, + u32 sw_dac_swing_lvl) { - if (sw_dac_swingon) - halbtc8192e2ant_set_dac_swingreg(btcoexist, sw_dac_swinglvl); + if (sw_dac_swing_on) + btc8192e2ant_set_dac_swing_reg(btcoexist, sw_dac_swing_lvl); else - halbtc8192e2ant_set_dac_swingreg(btcoexist, 0x18); + btc8192e2ant_set_dac_swing_reg(btcoexist, 0x18); } -static void halbtc8192e2ant_DacSwing(struct btc_coexist *btcoexist, - bool force_exec, bool dac_swingon, - u32 dac_swinglvl) +static void btc8192e2ant_dac_swing(struct btc_coexist *btcoexist, + bool force_exec, bool dac_swing_on, + u32 dac_swing_lvl) { struct rtl_priv *rtlpriv = btcoexist->adapter; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], %s turn DacSwing=%s, dac_swinglvl = 0x%x\n", + "[BTCoex], %s turn DacSwing=%s, dac_swing_lvl = 0x%x\n", (force_exec ? "force to" : ""), - ((dac_swingon) ? "ON" : "OFF"), dac_swinglvl); - coex_dm->cur_dac_swing_on = dac_swingon; - coex_dm->cur_dac_swing_lvl = dac_swinglvl; + ((dac_swing_on) ? "ON" : "OFF"), dac_swing_lvl); + coex_dm->cur_dac_swing_on = dac_swing_on; + coex_dm->cur_dac_swing_lvl = dac_swing_lvl; if (!force_exec) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -945,14 +946,14 @@ static void halbtc8192e2ant_DacSwing(struct btc_coexist *btcoexist, return; } mdelay(30); - btc8192e2ant_setsw_full_swing(btcoexist, dac_swingon, dac_swinglvl); + btc8192e2ant_set_sw_full_swing(btcoexist, dac_swing_on, dac_swing_lvl); coex_dm->pre_dac_swing_on = coex_dm->cur_dac_swing_on; coex_dm->pre_dac_swing_lvl = coex_dm->cur_dac_swing_lvl; } -static void halbtc8192e2ant_set_agc_table(struct btc_coexist *btcoexist, - bool agc_table_en) +static void btc8192e2ant_set_agc_table(struct btc_coexist *btcoexist, + bool agc_table_en) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -978,8 +979,8 @@ static void halbtc8192e2ant_set_agc_table(struct btc_coexist *btcoexist, } } -static void halbtc8192e2ant_AgcTable(struct btc_coexist *btcoexist, - bool force_exec, bool agc_table_en) +static void btc8192e2ant_agc_table(struct btc_coexist *btcoexist, + bool force_exec, bool agc_table_en) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -998,14 +999,14 @@ static void halbtc8192e2ant_AgcTable(struct btc_coexist *btcoexist, if (coex_dm->pre_agc_table_en == coex_dm->cur_agc_table_en) return; } - halbtc8192e2ant_set_agc_table(btcoexist, agc_table_en); + btc8192e2ant_set_agc_table(btcoexist, agc_table_en); coex_dm->pre_agc_table_en = coex_dm->cur_agc_table_en; } -static void halbtc8192e2ant_set_coex_table(struct btc_coexist *btcoexist, - u32 val0x6c0, u32 val0x6c4, - u32 val0x6c8, u8 val0x6cc) +static void btc8192e2ant_set_coex_table(struct btc_coexist *btcoexist, + u32 val0x6c0, u32 val0x6c4, + u32 val0x6c8, u8 val0x6cc) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -1026,10 +1027,9 @@ static void halbtc8192e2ant_set_coex_table(struct btc_coexist *btcoexist, btcoexist->btc_write_1byte(btcoexist, 0x6cc, val0x6cc); } -static void halbtc8192e2ant_coex_table(struct btc_coexist *btcoexist, - bool force_exec, - u32 val0x6c0, u32 val0x6c4, - u32 val0x6c8, u8 val0x6cc) +static void btc8192e2ant_coex_table(struct btc_coexist *btcoexist, + bool force_exec, u32 val0x6c0, u32 val0x6c4, + u32 val0x6c8, u8 val0x6cc) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -1064,8 +1064,8 @@ static void halbtc8192e2ant_coex_table(struct btc_coexist *btcoexist, (coex_dm->pre_val0x6cc == coex_dm->cur_val0x6cc)) return; } - halbtc8192e2ant_set_coex_table(btcoexist, val0x6c0, val0x6c4, - val0x6c8, val0x6cc); + btc8192e2ant_set_coex_table(btcoexist, val0x6c0, val0x6c4, val0x6c8, + val0x6cc); coex_dm->pre_val0x6c0 = coex_dm->cur_val0x6c0; coex_dm->pre_val0x6c4 = coex_dm->cur_val0x6c4; @@ -1073,37 +1073,37 @@ static void halbtc8192e2ant_coex_table(struct btc_coexist *btcoexist, coex_dm->pre_val0x6cc = coex_dm->cur_val0x6cc; } -static void btc8192e2ant_coex_tbl_w_type(struct btc_coexist *btcoexist, - bool force_exec, u8 type) +static void btc8192e2ant_coex_table_with_type(struct btc_coexist *btcoexist, + bool force_exec, u8 type) { switch (type) { case 0: - halbtc8192e2ant_coex_table(btcoexist, force_exec, 0x55555555, - 0x5a5a5a5a, 0xffffff, 0x3); + btc8192e2ant_coex_table(btcoexist, force_exec, 0x55555555, + 0x5a5a5a5a, 0xffffff, 0x3); break; case 1: - halbtc8192e2ant_coex_table(btcoexist, force_exec, 0x5a5a5a5a, - 0x5a5a5a5a, 0xffffff, 0x3); + btc8192e2ant_coex_table(btcoexist, force_exec, 0x5a5a5a5a, + 0x5a5a5a5a, 0xffffff, 0x3); break; case 2: - halbtc8192e2ant_coex_table(btcoexist, force_exec, 0x55555555, - 0x5ffb5ffb, 0xffffff, 0x3); + btc8192e2ant_coex_table(btcoexist, force_exec, 0x55555555, + 0x5ffb5ffb, 0xffffff, 0x3); break; case 3: - halbtc8192e2ant_coex_table(btcoexist, force_exec, 0xdfffdfff, - 0x5fdb5fdb, 0xffffff, 0x3); + btc8192e2ant_coex_table(btcoexist, force_exec, 0xdfffdfff, + 0x5fdb5fdb, 0xffffff, 0x3); break; case 4: - halbtc8192e2ant_coex_table(btcoexist, force_exec, 0xdfffdfff, - 0x5ffb5ffb, 0xffffff, 0x3); + btc8192e2ant_coex_table(btcoexist, force_exec, 0xdfffdfff, + 0x5ffb5ffb, 0xffffff, 0x3); break; default: break; } } -static void halbtc8192e2ant_set_fw_ignore_wlanact(struct btc_coexist *btcoexist, - bool enable) +static void btc8192e2ant_set_fw_ignore_wlan_act(struct btc_coexist *btcoexist, + bool enable) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[1] = {0}; @@ -1118,8 +1118,8 @@ static void halbtc8192e2ant_set_fw_ignore_wlanact(struct btc_coexist *btcoexist, btcoexist->btc_fill_h2c(btcoexist, 0x63, 1, h2c_parameter); } -static void halbtc8192e2ant_IgnoreWlanAct(struct btc_coexist *btcoexist, - bool force_exec, bool enable) +static void btc8192e2ant_ignore_wlan_act(struct btc_coexist *btcoexist, + bool force_exec, bool enable) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -1140,12 +1140,12 @@ static void halbtc8192e2ant_IgnoreWlanAct(struct btc_coexist *btcoexist, coex_dm->cur_ignore_wlan_act) return; } - halbtc8192e2ant_set_fw_ignore_wlanact(btcoexist, enable); + btc8192e2ant_set_fw_ignore_wlan_act(btcoexist, enable); coex_dm->pre_ignore_wlan_act = coex_dm->cur_ignore_wlan_act; } -static void halbtc8192e2ant_SetFwPstdma(struct btc_coexist *btcoexist, u8 byte1, +static void btc8192e2ant_set_fw_ps_tdma(struct btc_coexist *btcoexist, u8 byte1, u8 byte2, u8 byte3, u8 byte4, u8 byte5) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -1173,24 +1173,24 @@ static void halbtc8192e2ant_SetFwPstdma(struct btc_coexist *btcoexist, u8 byte1, btcoexist->btc_fill_h2c(btcoexist, 0x60, 5, h2c_parameter); } -static void btc8192e2ant_sw_mec1(struct btc_coexist *btcoexist, - bool shrink_rx_lpf, bool low_penalty_ra, - bool limited_dig, bool btlan_constrain) +static void btc8192e2ant_sw_mechanism1(struct btc_coexist *btcoexist, + bool shrink_rx_lpf, bool low_penalty_ra, + bool limited_dig, bool btlan_constrain) { - halbtc8192e2ant_rf_shrink(btcoexist, NORMAL_EXEC, shrink_rx_lpf); + btc8192e2ant_rf_shrink(btcoexist, NORMAL_EXEC, shrink_rx_lpf); } -static void btc8192e2ant_sw_mec2(struct btc_coexist *btcoexist, - bool agc_table_shift, bool adc_backoff, - bool sw_dac_swing, u32 dac_swinglvl) +static void btc8192e2ant_sw_mechanism2(struct btc_coexist *btcoexist, + bool agc_table_shift, bool adc_backoff, + bool sw_dac_swing, u32 dac_swing_lvl) { - halbtc8192e2ant_AgcTable(btcoexist, NORMAL_EXEC, agc_table_shift); - halbtc8192e2ant_DacSwing(btcoexist, NORMAL_EXEC, sw_dac_swing, - dac_swinglvl); + btc8192e2ant_agc_table(btcoexist, NORMAL_EXEC, agc_table_shift); + btc8192e2ant_dac_swing(btcoexist, NORMAL_EXEC, sw_dac_swing, + dac_swing_lvl); } -static void halbtc8192e2ant_ps_tdma(struct btc_coexist *btcoexist, - bool force_exec, bool turn_on, u8 type) +static void btc8192e2ant_ps_tdma(struct btc_coexist *btcoexist, + bool force_exec, bool turn_on, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -1217,91 +1217,91 @@ static void halbtc8192e2ant_ps_tdma(struct btc_coexist *btcoexist, switch (type) { case 1: default: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x1a, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, 0x1a, 0xe1, 0x90); break; case 2: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x12, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, 0x12, 0xe1, 0x90); break; case 3: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x1c, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, 0x3, 0xf1, 0x90); break; case 4: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x10, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x10, 0x3, 0xf1, 0x90); break; case 5: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x1a, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, 0x1a, 0x60, 0x90); break; case 6: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x12, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, 0x12, 0x60, 0x90); break; case 7: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x1c, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, 0x3, 0x70, 0x90); break; case 8: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xa3, 0x10, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xa3, 0x10, 0x3, 0x70, 0x90); break; case 9: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x1a, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, 0x1a, 0xe1, 0x10); break; case 10: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x12, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, 0x12, 0xe1, 0x10); break; case 11: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x1c, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, 0x3, 0xf1, 0x10); break; case 12: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x10, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x10, 0x3, 0xf1, 0x10); break; case 13: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x1a, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, 0x1a, 0xe0, 0x10); break; case 14: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x12, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, 0x12, 0xe0, 0x10); break; case 15: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x1c, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, 0x3, 0xf0, 0x10); break; case 16: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x12, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, 0x3, 0xf0, 0x10); break; case 17: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0x61, 0x20, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0x61, 0x20, 0x03, 0x10, 0x10); break; case 18: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x5, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, 0x5, 0xe1, 0x90); break; case 19: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x25, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x25, 0x25, 0xe1, 0x90); break; case 20: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x25, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x25, 0x25, 0x60, 0x90); break; case 21: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x15, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x15, 0x03, 0x70, 0x90); break; case 71: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0xe3, 0x1a, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, 0x1a, 0xe1, 0x90); break; } @@ -1310,12 +1310,12 @@ static void halbtc8192e2ant_ps_tdma(struct btc_coexist *btcoexist, switch (type) { default: case 0: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0x8, 0x0, 0x0, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0x8, 0x0, 0x0, 0x0, 0x0); btcoexist->btc_write_1byte(btcoexist, 0x92c, 0x4); break; case 1: - halbtc8192e2ant_SetFwPstdma(btcoexist, 0x0, 0x0, 0x0, + btc8192e2ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0, 0x8, 0x0); mdelay(5); btcoexist->btc_write_1byte(btcoexist, 0x92c, 0x20); @@ -1328,22 +1328,22 @@ static void halbtc8192e2ant_ps_tdma(struct btc_coexist *btcoexist, coex_dm->pre_ps_tdma = coex_dm->cur_ps_tdma; } -static void halbtc8192e2ant_set_switch_sstype(struct btc_coexist *btcoexist, - u8 sstype) +static void btc8192e2ant_set_switch_ss_type(struct btc_coexist *btcoexist, + u8 ss_type) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 mimops = BTC_MIMO_PS_DYNAMIC; - u32 disra_mask = 0x0; + u32 dis_ra_mask = 0x0; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], REAL set SS Type = %d\n", sstype); + "[BTCoex], REAL set SS Type = %d\n", ss_type); - disra_mask = halbtc8192e2ant_decidera_mask(btcoexist, sstype, - coex_dm->curra_masktype); - halbtc8192e2ant_Updatera_mask(btcoexist, FORCE_EXEC, disra_mask); + dis_ra_mask = btc8192e2ant_decide_ra_mask(btcoexist, ss_type, + coex_dm->cur_ra_mask_type); + btc8192e2ant_update_ra_mask(btcoexist, FORCE_EXEC, dis_ra_mask); - if (sstype == 1) { - halbtc8192e2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1); + if (ss_type == 1) { + btc8192e2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1); /* switch ofdm path */ btcoexist->btc_write_1byte(btcoexist, 0xc04, 0x11); btcoexist->btc_write_1byte(btcoexist, 0xd04, 0x1); @@ -1352,8 +1352,8 @@ static void halbtc8192e2ant_set_switch_sstype(struct btc_coexist *btcoexist, btcoexist->btc_write_1byte_bitmask(btcoexist, 0xe77, 0x4, 0x1); btcoexist->btc_write_1byte(btcoexist, 0xa07, 0x81); mimops = BTC_MIMO_PS_STATIC; - } else if (sstype == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 0); + } else if (ss_type == 2) { + btc8192e2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 0); btcoexist->btc_write_1byte(btcoexist, 0xc04, 0x33); btcoexist->btc_write_1byte(btcoexist, 0xd04, 0x3); btcoexist->btc_write_4byte(btcoexist, 0x90c, 0x81121313); @@ -1365,89 +1365,89 @@ static void halbtc8192e2ant_set_switch_sstype(struct btc_coexist *btcoexist, btcoexist->btc_set(btcoexist, BTC_SET_ACT_SEND_MIMO_PS, &mimops); } -static void halbtc8192e2ant_switch_sstype(struct btc_coexist *btcoexist, - bool force_exec, u8 new_sstype) +static void btc8192e2ant_switch_ss_type(struct btc_coexist *btcoexist, + bool force_exec, u8 new_ss_type) { struct rtl_priv *rtlpriv = btcoexist->adapter; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], %s Switch SS Type = %d\n", - (force_exec ? "force to" : ""), new_sstype); - coex_dm->cur_sstype = new_sstype; + (force_exec ? "force to" : ""), new_ss_type); + coex_dm->cur_ss_type = new_ss_type; if (!force_exec) { - if (coex_dm->pre_sstype == coex_dm->cur_sstype) + if (coex_dm->pre_ss_type == coex_dm->cur_ss_type) return; } - halbtc8192e2ant_set_switch_sstype(btcoexist, coex_dm->cur_sstype); + btc8192e2ant_set_switch_ss_type(btcoexist, coex_dm->cur_ss_type); - coex_dm->pre_sstype = coex_dm->cur_sstype; + coex_dm->pre_ss_type = coex_dm->cur_ss_type; } -static void halbtc8192e2ant_coex_alloff(struct btc_coexist *btcoexist) +static void btc8192e2ant_coex_all_off(struct btc_coexist *btcoexist) { /* fw all off */ - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, NORMAL_EXEC, 6); - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); /* sw all off */ - btc8192e2ant_sw_mec1(btcoexist, false, false, false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, false, false, false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); /* hw all off */ - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); } -static void halbtc8192e2ant_init_coex_dm(struct btc_coexist *btcoexist) +static void btc8192e2ant_init_coex_dm(struct btc_coexist *btcoexist) { /* force to reset coex mechanism */ - halbtc8192e2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, FORCE_EXEC, 6); - halbtc8192e2ant_dec_btpwr(btcoexist, FORCE_EXEC, 0); + btc8192e2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6); + btc8192e2ant_dec_bt_pwr(btcoexist, FORCE_EXEC, 0); - btc8192e2ant_coex_tbl_w_type(btcoexist, FORCE_EXEC, 0); - halbtc8192e2ant_switch_sstype(btcoexist, FORCE_EXEC, 2); + btc8192e2ant_coex_table_with_type(btcoexist, FORCE_EXEC, 0); + btc8192e2ant_switch_ss_type(btcoexist, FORCE_EXEC, 2); - btc8192e2ant_sw_mec1(btcoexist, false, false, false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, false, false, false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); } -static void halbtc8192e2ant_action_bt_inquiry(struct btc_coexist *btcoexist) +static void btc8192e2ant_action_bt_inquiry(struct btc_coexist *btcoexist) { bool low_pwr_disable = true; btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); - halbtc8192e2ant_switch_sstype(btcoexist, NORMAL_EXEC, 1); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 1); - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 2); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, NORMAL_EXEC, 6); - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); - btc8192e2ant_sw_mec1(btcoexist, false, false, false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, false, false, false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); } -static bool halbtc8192e2ant_is_common_action(struct btc_coexist *btcoexist) +static bool btc8192e2ant_is_common_action(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; bool common = false, wifi_connected = false, wifi_busy = false; - bool bt_hson = false, low_pwr_disable = false; + bool bt_hs_on = false, low_pwr_disable = false; - btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hson); + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy); if (bt_link_info->sco_exist || bt_link_info->hid_exist) - halbtc8192e2ant_limited_tx(btcoexist, NORMAL_EXEC, 1, 0, 0, 0); + btc8192e2ant_limited_tx(btcoexist, NORMAL_EXEC, 1, 0, 0, 0); else - halbtc8192e2ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); + btc8192e2ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); if (!wifi_connected) { low_pwr_disable = false; @@ -1461,26 +1461,24 @@ static bool halbtc8192e2ant_is_common_action(struct btc_coexist *btcoexist) coex_dm->bt_status) || (BT_8192E_2ANT_BT_STATUS_CONNECTED_IDLE == coex_dm->bt_status)) { - halbtc8192e2ant_switch_sstype(btcoexist, - NORMAL_EXEC, 2); - btc8192e2ant_coex_tbl_w_type(btcoexist, - NORMAL_EXEC, 1); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 0); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 1); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); } else { - halbtc8192e2ant_switch_sstype(btcoexist, - NORMAL_EXEC, 1); - btc8192e2ant_coex_tbl_w_type(btcoexist, - NORMAL_EXEC, 0); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 1); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 1); + btc8192e2ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 0); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); } - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, NORMAL_EXEC, 6); - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); - btc8192e2ant_sw_mec1(btcoexist, false, false, false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, false, false, false, + false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, false, + 0x18); common = true; } else { @@ -1494,20 +1492,18 @@ static bool halbtc8192e2ant_is_common_action(struct btc_coexist *btcoexist) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Wifi connected + BT non connected-idle!!\n"); - halbtc8192e2ant_switch_sstype(btcoexist, - NORMAL_EXEC, 2); - btc8192e2ant_coex_tbl_w_type(btcoexist, - NORMAL_EXEC, 1); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 0); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, - NORMAL_EXEC, 6); - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); - - btc8192e2ant_sw_mec1(btcoexist, false, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 1); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, + NORMAL_EXEC, 6); + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + + btc8192e2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); common = true; } else if (BT_8192E_2ANT_BT_STATUS_CONNECTED_IDLE == @@ -1517,25 +1513,25 @@ static bool halbtc8192e2ant_is_common_action(struct btc_coexist *btcoexist) BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); - if (bt_hson) + if (bt_hs_on) return false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Wifi connected + BT connected-idle!!\n"); - halbtc8192e2ant_switch_sstype(btcoexist, - NORMAL_EXEC, 2); - btc8192e2ant_coex_tbl_w_type(btcoexist, - NORMAL_EXEC, 1); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 0); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, - NORMAL_EXEC, 6); - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); - - btc8192e2ant_sw_mec1(btcoexist, true, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_switch_ss_type(btcoexist, + NORMAL_EXEC, 2); + btc8192e2ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 1); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, + false, 0); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, + NORMAL_EXEC, 6); + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + + btc8192e2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); common = true; } else { @@ -1552,20 +1548,21 @@ static bool halbtc8192e2ant_is_common_action(struct btc_coexist *btcoexist) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Wifi Connected-Idle + BT Busy!!\n"); - halbtc8192e2ant_switch_sstype(btcoexist, - NORMAL_EXEC, 1); - btc8192e2ant_coex_tbl_w_type(btcoexist, - NORMAL_EXEC, 2); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 21); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, - NORMAL_EXEC, 6); - halbtc8192e2ant_dec_btpwr(btcoexist, - NORMAL_EXEC, 0); - btc8192e2ant_sw_mec1(btcoexist, false, - false, false, false); - btc8192e2ant_sw_mec2(btcoexist, false, - false, false, 0x18); + btc8192e2ant_switch_ss_type(btcoexist, + NORMAL_EXEC, 1); + btc8192e2ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, + 2); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 21); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, + NORMAL_EXEC, 6); + btc8192e2ant_dec_bt_pwr(btcoexist, + NORMAL_EXEC, 0); + btc8192e2ant_sw_mechanism1(btcoexist, false, + false, false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, + false, false, 0x18); common = true; } } @@ -1573,9 +1570,9 @@ static bool halbtc8192e2ant_is_common_action(struct btc_coexist *btcoexist) return common; } -static void halbtc8192e2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, - bool sco_hid, bool tx_pause, - u8 max_interval) +static void btc8192e2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, + bool sco_hid, bool tx_pause, + u8 max_interval) { struct rtl_priv *rtlpriv = btcoexist->adapter; static int up, dn, m, n, wait_cnt; @@ -1595,72 +1592,72 @@ static void halbtc8192e2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, if (sco_hid) { if (tx_pause) { if (max_interval == 1) { - halbtc8192e2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 13); + btc8192e2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 13); coex_dm->tdma_adj_type = 13; } else if (max_interval == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 14); + btc8192e2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 14); coex_dm->tdma_adj_type = 14; } else { - halbtc8192e2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 15); + btc8192e2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 15); coex_dm->tdma_adj_type = 15; } } else { if (max_interval == 1) { - halbtc8192e2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 9); + btc8192e2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 9); coex_dm->tdma_adj_type = 9; } else if (max_interval == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 10); + btc8192e2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 10); coex_dm->tdma_adj_type = 10; } else { - halbtc8192e2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 11); + btc8192e2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 11); coex_dm->tdma_adj_type = 11; } } } else { if (tx_pause) { if (max_interval == 1) { - halbtc8192e2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 5); + btc8192e2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 5); coex_dm->tdma_adj_type = 5; } else if (max_interval == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 6); + btc8192e2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 6); coex_dm->tdma_adj_type = 6; } else { - halbtc8192e2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 7); + btc8192e2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 7); coex_dm->tdma_adj_type = 7; } } else { if (max_interval == 1) { - halbtc8192e2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 1); + btc8192e2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 1); coex_dm->tdma_adj_type = 1; } else if (max_interval == 2) { - halbtc8192e2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 2); + btc8192e2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 2); coex_dm->tdma_adj_type = 2; } else { - halbtc8192e2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 3); + btc8192e2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 3); coex_dm->tdma_adj_type = 3; } } @@ -1763,9 +1760,8 @@ static void halbtc8192e2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_ROAM, &roam); if (!scan && !link && !roam) - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, - coex_dm->tdma_adj_type); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, coex_dm->tdma_adj_type); else RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], roaming/link/scan is under progress, will adjust next time!!!\n"); @@ -1773,583 +1769,578 @@ static void halbtc8192e2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, } /* SCO only or SCO+PAN(HS) */ -static void halbtc8192e2ant_action_sco(struct btc_coexist *btcoexist) +static void btc8192e2ant_action_sco(struct btc_coexist *btcoexist) { - u8 wifirssi_state, btrssi_state = BTC_RSSI_STATE_STAY_LOW; + u8 wifi_rssi_state, bt_rssi_state = BTC_RSSI_STATE_STAY_LOW; u32 wifi_bw; - wifirssi_state = halbtc8192e2ant_wifirssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state = btc8192e2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); - halbtc8192e2ant_switch_sstype(btcoexist, NORMAL_EXEC, 1); - halbtc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 1); + btc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, NORMAL_EXEC, 6); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 4); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 4); - btrssi_state = halbtc8192e2ant_btrssi_state(btcoexist, 3, 34, 42); + bt_rssi_state = btc8192e2ant_bt_rssi_state(btcoexist, 3, 34, 42); - if ((btrssi_state == BTC_RSSI_STATE_LOW) || - (btrssi_state == BTC_RSSI_STATE_STAY_LOW)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 13); - } else if ((btrssi_state == BTC_RSSI_STATE_MEDIUM) || - (btrssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 2); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); - } else if ((btrssi_state == BTC_RSSI_STATE_HIGH) || - (btrssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 4); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); + if ((bt_rssi_state == BTC_RSSI_STATE_LOW) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 13); + } else if ((bt_rssi_state == BTC_RSSI_STATE_MEDIUM) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); + } else if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 4); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); } btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); /* sw mechanism */ if (BTC_WIFI_BW_HT40 == wifi_bw) { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, true, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x6); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x6); } else { - btc8192e2ant_sw_mec1(btcoexist, true, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x6); + btc8192e2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x6); } } else { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, false, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x6); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x6); } else { - btc8192e2ant_sw_mec1(btcoexist, false, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x6); + btc8192e2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x6); } } } -static void halbtc8192e2ant_action_sco_pan(struct btc_coexist *btcoexist) +static void btc8192e2ant_action_sco_pan(struct btc_coexist *btcoexist) { - u8 wifirssi_state, btrssi_state = BTC_RSSI_STATE_STAY_LOW; + u8 wifi_rssi_state, bt_rssi_state = BTC_RSSI_STATE_STAY_LOW; u32 wifi_bw; - wifirssi_state = halbtc8192e2ant_wifirssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state = btc8192e2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); - halbtc8192e2ant_switch_sstype(btcoexist, NORMAL_EXEC, 1); - halbtc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 1); + btc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, NORMAL_EXEC, 6); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 4); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 4); - btrssi_state = halbtc8192e2ant_btrssi_state(btcoexist, 3, 34, 42); + bt_rssi_state = btc8192e2ant_bt_rssi_state(btcoexist, 3, 34, 42); - if ((btrssi_state == BTC_RSSI_STATE_LOW) || - (btrssi_state == BTC_RSSI_STATE_STAY_LOW)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); - } else if ((btrssi_state == BTC_RSSI_STATE_MEDIUM) || - (btrssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 2); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10); - } else if ((btrssi_state == BTC_RSSI_STATE_HIGH) || - (btrssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 4); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10); + if ((bt_rssi_state == BTC_RSSI_STATE_LOW) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); + } else if ((bt_rssi_state == BTC_RSSI_STATE_MEDIUM) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10); + } else if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 4); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10); } btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); /* sw mechanism */ if (BTC_WIFI_BW_HT40 == wifi_bw) { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, true, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x6); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x6); } else { - btc8192e2ant_sw_mec1(btcoexist, true, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x6); + btc8192e2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x6); } } else { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, false, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x6); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x6); } else { - btc8192e2ant_sw_mec1(btcoexist, false, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x6); + btc8192e2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x6); } } } -static void halbtc8192e2ant_action_hid(struct btc_coexist *btcoexist) +static void btc8192e2ant_action_hid(struct btc_coexist *btcoexist) { - u8 wifirssi_state, btrssi_state = BTC_RSSI_STATE_HIGH; + u8 wifi_rssi_state, bt_rssi_state = BTC_RSSI_STATE_HIGH; u32 wifi_bw; - wifirssi_state = halbtc8192e2ant_wifirssi_state(btcoexist, 0, 2, 15, 0); - btrssi_state = halbtc8192e2ant_btrssi_state(btcoexist, 3, 34, 42); + wifi_rssi_state = btc8192e2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8192e2ant_bt_rssi_state(btcoexist, 3, 34, 42); - halbtc8192e2ant_switch_sstype(btcoexist, NORMAL_EXEC, 1); - halbtc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 1); + btc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, NORMAL_EXEC, 6); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 3); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 3); - if ((btrssi_state == BTC_RSSI_STATE_LOW) || - (btrssi_state == BTC_RSSI_STATE_STAY_LOW)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 13); - } else if ((btrssi_state == BTC_RSSI_STATE_MEDIUM) || - (btrssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 2); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); - } else if ((btrssi_state == BTC_RSSI_STATE_HIGH) || - (btrssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 4); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); + if ((bt_rssi_state == BTC_RSSI_STATE_LOW) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 13); + } else if ((bt_rssi_state == BTC_RSSI_STATE_MEDIUM) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); + } else if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 4); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); } /* sw mechanism */ if (BTC_WIFI_BW_HT40 == wifi_bw) { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, true, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, true, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, false, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, false, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } /* A2DP only / PAN(EDR) only/ A2DP+PAN(HS) */ -static void halbtc8192e2ant_action_a2dp(struct btc_coexist *btcoexist) +static void btc8192e2ant_action_a2dp(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; - u8 wifirssi_state, btrssi_state = BTC_RSSI_STATE_HIGH; + u8 wifi_rssi_state, bt_rssi_state = BTC_RSSI_STATE_HIGH; u32 wifi_bw; bool long_dist = false; - wifirssi_state = halbtc8192e2ant_wifirssi_state(btcoexist, 0, 2, 15, 0); - btrssi_state = halbtc8192e2ant_btrssi_state(btcoexist, 3, 34, 42); + wifi_rssi_state = btc8192e2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8192e2ant_bt_rssi_state(btcoexist, 3, 34, 42); - if ((btrssi_state == BTC_RSSI_STATE_LOW || - btrssi_state == BTC_RSSI_STATE_STAY_LOW) && - (wifirssi_state == BTC_RSSI_STATE_LOW || - wifirssi_state == BTC_RSSI_STATE_STAY_LOW)) { + if ((bt_rssi_state == BTC_RSSI_STATE_LOW || + bt_rssi_state == BTC_RSSI_STATE_STAY_LOW) && + (wifi_rssi_state == BTC_RSSI_STATE_LOW || + wifi_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], A2dp, wifi/bt rssi both LOW!!\n"); long_dist = true; } if (long_dist) { - halbtc8192e2ant_switch_sstype(btcoexist, NORMAL_EXEC, 2); - halbtc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, true, - 0x4); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, true, + 0x4); } else { - halbtc8192e2ant_switch_sstype(btcoexist, NORMAL_EXEC, 1); - halbtc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, - 0x8); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 1); + btc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, + 0x8); } - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, NORMAL_EXEC, 6); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (long_dist) - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); else - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); if (long_dist) { - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 17); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 17); coex_dm->auto_tdma_adjust = false; - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); } else { - if ((btrssi_state == BTC_RSSI_STATE_LOW) || - (btrssi_state == BTC_RSSI_STATE_STAY_LOW)) { - halbtc8192e2ant_tdma_duration_adjust(btcoexist, false, - true, 1); - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); - } else if ((btrssi_state == BTC_RSSI_STATE_MEDIUM) || - (btrssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { - halbtc8192e2ant_tdma_duration_adjust(btcoexist, false, - false, 1); - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 2); - } else if ((btrssi_state == BTC_RSSI_STATE_HIGH) || - (btrssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8192e2ant_tdma_duration_adjust(btcoexist, false, - false, 1); - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 4); + if ((bt_rssi_state == BTC_RSSI_STATE_LOW) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { + btc8192e2ant_tdma_duration_adjust(btcoexist, false, + true, 1); + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + } else if ((bt_rssi_state == BTC_RSSI_STATE_MEDIUM) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { + btc8192e2ant_tdma_duration_adjust(btcoexist, false, + false, 1); + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + } else if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_tdma_duration_adjust(btcoexist, false, + false, 1); + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 4); } } /* sw mechanism */ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); if (BTC_WIFI_BW_HT40 == wifi_bw) { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, true, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, true, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, false, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, false, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } -static void halbtc8192e2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) +static void btc8192e2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) { - u8 wifirssi_state, btrssi_state = BTC_RSSI_STATE_HIGH; + u8 wifi_rssi_state, bt_rssi_state = BTC_RSSI_STATE_HIGH; u32 wifi_bw; - wifirssi_state = halbtc8192e2ant_wifirssi_state(btcoexist, 0, 2, 15, 0); - btrssi_state = halbtc8192e2ant_btrssi_state(btcoexist, 3, 34, 42); + wifi_rssi_state = btc8192e2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8192e2ant_bt_rssi_state(btcoexist, 3, 34, 42); - halbtc8192e2ant_switch_sstype(btcoexist, NORMAL_EXEC, 1); - halbtc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 1); + btc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, NORMAL_EXEC, 6); - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); - if ((btrssi_state == BTC_RSSI_STATE_LOW) || - (btrssi_state == BTC_RSSI_STATE_STAY_LOW)) { - halbtc8192e2ant_tdma_duration_adjust(btcoexist, false, true, 2); - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); - } else if ((btrssi_state == BTC_RSSI_STATE_MEDIUM) || - (btrssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { - halbtc8192e2ant_tdma_duration_adjust(btcoexist, false, - false, 2); - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 2); - } else if ((btrssi_state == BTC_RSSI_STATE_HIGH) || - (btrssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8192e2ant_tdma_duration_adjust(btcoexist, false, - false, 2); - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 4); + if ((bt_rssi_state == BTC_RSSI_STATE_LOW) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { + btc8192e2ant_tdma_duration_adjust(btcoexist, false, true, 2); + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + } else if ((bt_rssi_state == BTC_RSSI_STATE_MEDIUM) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { + btc8192e2ant_tdma_duration_adjust(btcoexist, false, false, 2); + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + } else if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_tdma_duration_adjust(btcoexist, false, false, 2); + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 4); } /* sw mechanism */ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); if (BTC_WIFI_BW_HT40 == wifi_bw) { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, true, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - true, 0x6); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + true, 0x6); } else { - btc8192e2ant_sw_mec1(btcoexist, true, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - true, 0x6); + btc8192e2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + true, 0x6); } } else { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, false, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - true, 0x6); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + true, 0x6); } else { - btc8192e2ant_sw_mec1(btcoexist, false, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - true, 0x6); + btc8192e2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + true, 0x6); } } } -static void halbtc8192e2ant_action_pan_edr(struct btc_coexist *btcoexist) +static void btc8192e2ant_action_pan_edr(struct btc_coexist *btcoexist) { - u8 wifirssi_state, btrssi_state = BTC_RSSI_STATE_HIGH; + u8 wifi_rssi_state, bt_rssi_state = BTC_RSSI_STATE_HIGH; u32 wifi_bw; - wifirssi_state = halbtc8192e2ant_wifirssi_state(btcoexist, 0, 2, 15, 0); - btrssi_state = halbtc8192e2ant_btrssi_state(btcoexist, 3, 34, 42); + wifi_rssi_state = btc8192e2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8192e2ant_bt_rssi_state(btcoexist, 3, 34, 42); - halbtc8192e2ant_switch_sstype(btcoexist, NORMAL_EXEC, 1); - halbtc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 1); + btc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, NORMAL_EXEC, 6); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); - if ((btrssi_state == BTC_RSSI_STATE_LOW) || - (btrssi_state == BTC_RSSI_STATE_STAY_LOW)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); - } else if ((btrssi_state == BTC_RSSI_STATE_MEDIUM) || - (btrssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 2); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 1); - } else if ((btrssi_state == BTC_RSSI_STATE_HIGH) || - (btrssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 4); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 1); + if ((bt_rssi_state == BTC_RSSI_STATE_LOW) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); + } else if ((bt_rssi_state == BTC_RSSI_STATE_MEDIUM) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 1); + } else if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 4); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 1); } /* sw mechanism */ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); if (BTC_WIFI_BW_HT40 == wifi_bw) { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, true, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, true, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, false, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, false, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } /* PAN(HS) only */ -static void halbtc8192e2ant_action_pan_hs(struct btc_coexist *btcoexist) +static void btc8192e2ant_action_pan_hs(struct btc_coexist *btcoexist) { - u8 wifirssi_state, btrssi_state = BTC_RSSI_STATE_HIGH; + u8 wifi_rssi_state, bt_rssi_state = BTC_RSSI_STATE_HIGH; u32 wifi_bw; - wifirssi_state = halbtc8192e2ant_wifirssi_state(btcoexist, 0, 2, 15, 0); - btrssi_state = halbtc8192e2ant_btrssi_state(btcoexist, 3, 34, 42); + wifi_rssi_state = btc8192e2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8192e2ant_bt_rssi_state(btcoexist, 3, 34, 42); - halbtc8192e2ant_switch_sstype(btcoexist, NORMAL_EXEC, 1); - halbtc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 1); + btc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, NORMAL_EXEC, 6); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); - if ((btrssi_state == BTC_RSSI_STATE_LOW) || - (btrssi_state == BTC_RSSI_STATE_STAY_LOW)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); - } else if ((btrssi_state == BTC_RSSI_STATE_MEDIUM) || - (btrssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 2); - } else if ((btrssi_state == BTC_RSSI_STATE_HIGH) || - (btrssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 4); + if ((bt_rssi_state == BTC_RSSI_STATE_LOW) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + } else if ((bt_rssi_state == BTC_RSSI_STATE_MEDIUM) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + } else if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 4); } - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); if (BTC_WIFI_BW_HT40 == wifi_bw) { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, true, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, true, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, false, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, false, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } /* PAN(EDR)+A2DP */ -static void halbtc8192e2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) +static void btc8192e2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) { - u8 wifirssi_state, btrssi_state = BTC_RSSI_STATE_HIGH; + u8 wifi_rssi_state, bt_rssi_state = BTC_RSSI_STATE_HIGH; u32 wifi_bw; - wifirssi_state = halbtc8192e2ant_wifirssi_state(btcoexist, 0, 2, 15, 0); - btrssi_state = halbtc8192e2ant_btrssi_state(btcoexist, 3, 34, 42); + wifi_rssi_state = btc8192e2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8192e2ant_bt_rssi_state(btcoexist, 3, 34, 42); - halbtc8192e2ant_switch_sstype(btcoexist, NORMAL_EXEC, 1); - halbtc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 1); + btc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, NORMAL_EXEC, 6); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - if ((btrssi_state == BTC_RSSI_STATE_LOW) || - (btrssi_state == BTC_RSSI_STATE_STAY_LOW)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); - halbtc8192e2ant_tdma_duration_adjust(btcoexist, false, true, 3); - } else if ((btrssi_state == BTC_RSSI_STATE_MEDIUM) || - (btrssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 2); - halbtc8192e2ant_tdma_duration_adjust(btcoexist, false, - false, 3); - } else if ((btrssi_state == BTC_RSSI_STATE_HIGH) || - (btrssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 4); - halbtc8192e2ant_tdma_duration_adjust(btcoexist, false, - false, 3); + if ((bt_rssi_state == BTC_RSSI_STATE_LOW) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_tdma_duration_adjust(btcoexist, false, true, 3); + } else if ((bt_rssi_state == BTC_RSSI_STATE_MEDIUM) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_tdma_duration_adjust(btcoexist, false, false, 3); + } else if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 4); + btc8192e2ant_tdma_duration_adjust(btcoexist, false, false, 3); } /* sw mechanism */ if (BTC_WIFI_BW_HT40 == wifi_bw) { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, true, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, true, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, false, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, false, false, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } -static void halbtc8192e2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) +static void btc8192e2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) { - u8 wifirssi_state, btrssi_state = BTC_RSSI_STATE_HIGH; + u8 wifi_rssi_state, bt_rssi_state = BTC_RSSI_STATE_HIGH; u32 wifi_bw; - wifirssi_state = halbtc8192e2ant_wifirssi_state(btcoexist, 0, 2, 15, 0); - btrssi_state = halbtc8192e2ant_btrssi_state(btcoexist, 3, 34, 42); + wifi_rssi_state = btc8192e2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8192e2ant_bt_rssi_state(btcoexist, 3, 34, 42); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - halbtc8192e2ant_switch_sstype(btcoexist, NORMAL_EXEC, 1); - halbtc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 1); + btc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, NORMAL_EXEC, 6); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 3); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 3); - if ((btrssi_state == BTC_RSSI_STATE_LOW) || - (btrssi_state == BTC_RSSI_STATE_STAY_LOW)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); - } else if ((btrssi_state == BTC_RSSI_STATE_MEDIUM) || - (btrssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 2); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); - } else if ((btrssi_state == BTC_RSSI_STATE_HIGH) || - (btrssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 4); - halbtc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); + if ((bt_rssi_state == BTC_RSSI_STATE_LOW) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); + } else if ((bt_rssi_state == BTC_RSSI_STATE_MEDIUM) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10); + } else if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 4); + btc8192e2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 10); } /* sw mechanism */ if (BTC_WIFI_BW_HT40 == wifi_bw) { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, true, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, true, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, false, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, false, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } @@ -2357,125 +2348,125 @@ static void halbtc8192e2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) /* HID+A2DP+PAN(EDR) */ static void btc8192e2ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) { - u8 wifirssi_state, btrssi_state = BTC_RSSI_STATE_HIGH; + u8 wifi_rssi_state, bt_rssi_state = BTC_RSSI_STATE_HIGH; u32 wifi_bw; - wifirssi_state = halbtc8192e2ant_wifirssi_state(btcoexist, 0, 2, 15, 0); - btrssi_state = halbtc8192e2ant_btrssi_state(btcoexist, 3, 34, 42); + wifi_rssi_state = btc8192e2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8192e2ant_bt_rssi_state(btcoexist, 3, 34, 42); - halbtc8192e2ant_switch_sstype(btcoexist, NORMAL_EXEC, 1); - halbtc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 1); + btc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); - halbtc8192e2ant_fw_dac_swinglvl(btcoexist, NORMAL_EXEC, 6); + btc8192e2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 3); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 3); - if ((btrssi_state == BTC_RSSI_STATE_LOW) || - (btrssi_state == BTC_RSSI_STATE_STAY_LOW)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); - halbtc8192e2ant_tdma_duration_adjust(btcoexist, true, true, 3); - } else if ((btrssi_state == BTC_RSSI_STATE_MEDIUM) || - (btrssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 2); - halbtc8192e2ant_tdma_duration_adjust(btcoexist, true, false, 3); - } else if ((btrssi_state == BTC_RSSI_STATE_HIGH) || - (btrssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 4); - halbtc8192e2ant_tdma_duration_adjust(btcoexist, true, false, 3); + if ((bt_rssi_state == BTC_RSSI_STATE_LOW) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_tdma_duration_adjust(btcoexist, true, true, 3); + } else if ((bt_rssi_state == BTC_RSSI_STATE_MEDIUM) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_tdma_duration_adjust(btcoexist, true, false, 3); + } else if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 4); + btc8192e2ant_tdma_duration_adjust(btcoexist, true, false, 3); } /* sw mechanism */ if (BTC_WIFI_BW_HT40 == wifi_bw) { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, true, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, true, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, false, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, false, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } -static void halbtc8192e2ant_action_hid_a2dp(struct btc_coexist *btcoexist) +static void btc8192e2ant_action_hid_a2dp(struct btc_coexist *btcoexist) { - u8 wifirssi_state, btrssi_state = BTC_RSSI_STATE_HIGH; + u8 wifi_rssi_state, bt_rssi_state = BTC_RSSI_STATE_HIGH; u32 wifi_bw; - wifirssi_state = halbtc8192e2ant_wifirssi_state(btcoexist, 0, 2, 15, 0); - btrssi_state = halbtc8192e2ant_btrssi_state(btcoexist, 3, 34, 42); + wifi_rssi_state = btc8192e2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8192e2ant_bt_rssi_state(btcoexist, 3, 34, 42); - halbtc8192e2ant_switch_sstype(btcoexist, NORMAL_EXEC, 1); - halbtc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + btc8192e2ant_switch_ss_type(btcoexist, NORMAL_EXEC, 1); + btc8192e2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - btc8192e2ant_coex_tbl_w_type(btcoexist, NORMAL_EXEC, 3); + btc8192e2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 3); - if ((btrssi_state == BTC_RSSI_STATE_LOW) || - (btrssi_state == BTC_RSSI_STATE_STAY_LOW)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 0); - halbtc8192e2ant_tdma_duration_adjust(btcoexist, true, true, 2); - } else if ((btrssi_state == BTC_RSSI_STATE_MEDIUM) || - (btrssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 2); - halbtc8192e2ant_tdma_duration_adjust(btcoexist, true, false, 2); - } else if ((btrssi_state == BTC_RSSI_STATE_HIGH) || - (btrssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8192e2ant_dec_btpwr(btcoexist, NORMAL_EXEC, 4); - halbtc8192e2ant_tdma_duration_adjust(btcoexist, true, false, 2); + if ((bt_rssi_state == BTC_RSSI_STATE_LOW) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + btc8192e2ant_tdma_duration_adjust(btcoexist, true, true, 2); + } else if ((bt_rssi_state == BTC_RSSI_STATE_MEDIUM) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_MEDIUM)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + btc8192e2ant_tdma_duration_adjust(btcoexist, true, false, 2); + } else if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 4); + btc8192e2ant_tdma_duration_adjust(btcoexist, true, false, 2); } /* sw mechanism */ if (BTC_WIFI_BW_HT40 == wifi_bw) { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, true, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, true, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { - if ((wifirssi_state == BTC_RSSI_STATE_HIGH) || - (wifirssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8192e2ant_sw_mec1(btcoexist, false, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, true, false, - false, 0x18); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8192e2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8192e2ant_sw_mec1(btcoexist, false, true, - false, false); - btc8192e2ant_sw_mec2(btcoexist, false, false, - false, 0x18); + btc8192e2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8192e2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } -static void halbtc8192e2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) +static void btc8192e2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 algorithm = 0; @@ -2495,12 +2486,12 @@ static void halbtc8192e2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) return; } - algorithm = halbtc8192e2ant_action_algorithm(btcoexist); + algorithm = btc8192e2ant_action_algorithm(btcoexist); if (coex_sta->c2h_bt_inquiry_page && (BT_8192E_2ANT_COEX_ALGO_PANHS != algorithm)) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], BT is under inquiry/page scan !!\n"); - halbtc8192e2ant_action_bt_inquiry(btcoexist); + btc8192e2ant_action_bt_inquiry(btcoexist); return; } @@ -2508,7 +2499,7 @@ static void halbtc8192e2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Algorithm = %d\n", coex_dm->cur_algorithm); - if (halbtc8192e2ant_is_common_action(btcoexist)) { + if (btc8192e2ant_is_common_action(btcoexist)) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action 2-Ant common\n"); coex_dm->auto_tdma_adjust = false; @@ -2524,47 +2515,47 @@ static void halbtc8192e2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) case BT_8192E_2ANT_COEX_ALGO_SCO: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Action 2-Ant, algorithm = SCO\n"); - halbtc8192e2ant_action_sco(btcoexist); + btc8192e2ant_action_sco(btcoexist); break; case BT_8192E_2ANT_COEX_ALGO_SCO_PAN: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Action 2-Ant, algorithm = SCO+PAN(EDR)\n"); - halbtc8192e2ant_action_sco_pan(btcoexist); + btc8192e2ant_action_sco_pan(btcoexist); break; case BT_8192E_2ANT_COEX_ALGO_HID: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Action 2-Ant, algorithm = HID\n"); - halbtc8192e2ant_action_hid(btcoexist); + btc8192e2ant_action_hid(btcoexist); break; case BT_8192E_2ANT_COEX_ALGO_A2DP: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Action 2-Ant, algorithm = A2DP\n"); - halbtc8192e2ant_action_a2dp(btcoexist); + btc8192e2ant_action_a2dp(btcoexist); break; case BT_8192E_2ANT_COEX_ALGO_A2DP_PANHS: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Action 2-Ant, algorithm = A2DP+PAN(HS)\n"); - halbtc8192e2ant_action_a2dp_pan_hs(btcoexist); + btc8192e2ant_action_a2dp_pan_hs(btcoexist); break; case BT_8192E_2ANT_COEX_ALGO_PANEDR: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Action 2-Ant, algorithm = PAN(EDR)\n"); - halbtc8192e2ant_action_pan_edr(btcoexist); + btc8192e2ant_action_pan_edr(btcoexist); break; case BT_8192E_2ANT_COEX_ALGO_PANHS: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Action 2-Ant, algorithm = HS mode\n"); - halbtc8192e2ant_action_pan_hs(btcoexist); + btc8192e2ant_action_pan_hs(btcoexist); break; case BT_8192E_2ANT_COEX_ALGO_PANEDR_A2DP: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Action 2-Ant, algorithm = PAN+A2DP\n"); - halbtc8192e2ant_action_pan_edr_a2dp(btcoexist); + btc8192e2ant_action_pan_edr_a2dp(btcoexist); break; case BT_8192E_2ANT_COEX_ALGO_PANEDR_HID: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Action 2-Ant, algorithm = PAN(EDR)+HID\n"); - halbtc8192e2ant_action_pan_edr_hid(btcoexist); + btc8192e2ant_action_pan_edr_hid(btcoexist); break; case BT_8192E_2ANT_COEX_ALGO_HID_A2DP_PANEDR: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -2574,20 +2565,20 @@ static void halbtc8192e2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) case BT_8192E_2ANT_COEX_ALGO_HID_A2DP: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Action 2-Ant, algorithm = HID+A2DP\n"); - halbtc8192e2ant_action_hid_a2dp(btcoexist); + btc8192e2ant_action_hid_a2dp(btcoexist); break; default: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "Action 2-Ant, algorithm = unknown!!\n"); - /* halbtc8192e2ant_coex_alloff(btcoexist); */ + /* btc8192e2ant_coex_all_off(btcoexist); */ break; } coex_dm->pre_algorithm = coex_dm->cur_algorithm; } } -static void halbtc8192e2ant_init_hwconfig(struct btc_coexist *btcoexist, - bool backup) +static void btc8192e2ant_init_hwconfig(struct btc_coexist *btcoexist, + bool backup) { struct rtl_priv *rtlpriv = btcoexist->adapter; u16 u16tmp = 0; @@ -2606,7 +2597,7 @@ static void halbtc8192e2ant_init_hwconfig(struct btc_coexist *btcoexist, 0x430); coex_dm->backup_arfr_cnt2 = btcoexist->btc_read_4byte(btcoexist, 0x434); - coex_dm->backup_retrylimit = btcoexist->btc_read_2byte( + coex_dm->backup_retry_limit = btcoexist->btc_read_2byte( btcoexist, 0x42a); coex_dm->backup_ampdu_maxtime = btcoexist->btc_read_1byte( @@ -2624,7 +2615,7 @@ static void halbtc8192e2ant_init_hwconfig(struct btc_coexist *btcoexist, else btcoexist->btc_write_4byte(btcoexist, 0x64, 0x30030004); - btc8192e2ant_coex_tbl_w_type(btcoexist, FORCE_EXEC, 0); + btc8192e2ant_coex_table_with_type(btcoexist, FORCE_EXEC, 0); /* antenna switch control parameter */ btcoexist->btc_write_4byte(btcoexist, 0x858, 0x55555555); @@ -2647,7 +2638,7 @@ static void halbtc8192e2ant_init_hwconfig(struct btc_coexist *btcoexist, u16tmp |= BIT9; btcoexist->btc_write_2byte(btcoexist, 0x40, u16tmp); - /* enable PTA I2C mailbox */ + /* enable PTA I2C mailbox */ u8tmp = btcoexist->btc_read_1byte(btcoexist, 0x101); u8tmp |= BIT4; btcoexist->btc_write_1byte(btcoexist, 0x101, u8tmp); @@ -2662,29 +2653,25 @@ static void halbtc8192e2ant_init_hwconfig(struct btc_coexist *btcoexist, btcoexist->btc_write_1byte(btcoexist, 0x7, u8tmp); } -/************************************************************* - * work around function start with wa_halbtc8192e2ant_ - *************************************************************/ - /************************************************************ - * extern function start with EXhalbtc8192e2ant_ + * extern function start with ex_btc8192e2ant_ ************************************************************/ -void ex_halbtc8192e2ant_init_hwconfig(struct btc_coexist *btcoexist) +void ex_btc8192e2ant_init_hwconfig(struct btc_coexist *btcoexist) { - halbtc8192e2ant_init_hwconfig(btcoexist, true); + btc8192e2ant_init_hwconfig(btcoexist, true); } -void ex_halbtc8192e2ant_init_coex_dm(struct btc_coexist *btcoexist) +void ex_btc8192e2ant_init_coex_dm(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Coex Mechanism Init!!\n"); - halbtc8192e2ant_init_coex_dm(btcoexist); + btc8192e2ant_init_coex_dm(btcoexist); } -void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist) +void ex_btc8192e2ant_display_coex_info(struct btc_coexist *btcoexist) { struct btc_board_info *board_info = &btcoexist->board_info; struct btc_stack_info *stack_info = &btcoexist->stack_info; @@ -2693,8 +2680,8 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist) u16 u16tmp[4]; u32 u32tmp[4]; bool roam = false, scan = false, link = false, wifi_under_5g = false; - bool bt_hson = false, wifi_busy = false; - int wifirssi = 0, bt_hs_rssi = 0; + bool bt_hs_on = false, wifi_busy = false; + int wifi_rssi = 0, bt_hs_rssi = 0; u32 wifi_bw, wifi_traffic_dir; u8 wifi_dot11_chnl, wifi_hs_chnl; u32 fw_ver = 0, bt_patch_ver = 0; @@ -2731,21 +2718,21 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist) glcoex_ver_date_8192e_2ant, glcoex_ver_8192e_2ant, fw_ver, bt_patch_ver, bt_patch_ver); - btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hson); + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); btcoexist->btc_get(btcoexist, BTC_GET_U1_WIFI_DOT11_CHNL, &wifi_dot11_chnl); btcoexist->btc_get(btcoexist, BTC_GET_U1_WIFI_HS_CHNL, &wifi_hs_chnl); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d / %d(%d)", "Dot11 channel / HsMode(HsChnl)", - wifi_dot11_chnl, bt_hson, wifi_hs_chnl); + wifi_dot11_chnl, bt_hs_on, wifi_hs_chnl); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %3ph ", "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info); - btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifirssi); + btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi); btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d/ %d", - "Wifi rssi/ HS rssi", wifirssi, bt_hs_rssi); + "Wifi rssi/ HS rssi", wifi_rssi, bt_hs_rssi); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link); @@ -2792,7 +2779,7 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist) if (coex_sta->bt_info_c2h_cnt[i]) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %7ph(%d)", - GLBtInfoSrc8192e2Ant[i], + glbt_info_src_8192e_2ant[i], coex_sta->bt_info_c2h[i], coex_sta->bt_info_c2h_cnt[i]); } @@ -2805,7 +2792,7 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist) btcoexist->btc_disp_dbg_msg(btcoexist, BTC_DBG_DISP_FW_PWR_MODE_CMD); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = 0x%x ", "SS Type", - coex_dm->cur_sstype); + coex_dm->cur_ss_type); /* Sw mechanism */ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s", @@ -2844,7 +2831,7 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist) RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = 0x%x/0x%x/0x%x/0x%x", "backup ARFR1/ARFR2/RL/AMaxTime", coex_dm->backup_arfr_cnt1, - coex_dm->backup_arfr_cnt2, coex_dm->backup_retrylimit, + coex_dm->backup_arfr_cnt2, coex_dm->backup_retry_limit, coex_dm->backup_ampdu_maxtime); u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0x430); @@ -2900,12 +2887,12 @@ void ex_halbtc8192e2ant_display_coex_info(struct btc_coexist *btcoexist) "0x774(lp rx[31:16]/tx[15:0])", coex_sta->low_priority_rx, coex_sta->low_priority_tx); #if (BT_AUTO_REPORT_ONLY_8192E_2ANT == 1) - halbtc8192e2ant_monitor_bt_ctr(btcoexist); + btc8192e2ant_monitor_bt_ctr(btcoexist); #endif btcoexist->btc_disp_dbg_msg(btcoexist, BTC_DBG_DISP_COEX_STATISTICS); } -void ex_halbtc8192e2ant_ips_notify(struct btc_coexist *btcoexist, u8 type) +void ex_btc8192e2ant_ips_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2913,7 +2900,7 @@ void ex_halbtc8192e2ant_ips_notify(struct btc_coexist *btcoexist, u8 type) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], IPS ENTER notify\n"); coex_sta->under_ips = true; - halbtc8192e2ant_coex_alloff(btcoexist); + btc8192e2ant_coex_all_off(btcoexist); } else if (BTC_IPS_LEAVE == type) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], IPS LEAVE notify\n"); @@ -2921,7 +2908,7 @@ void ex_halbtc8192e2ant_ips_notify(struct btc_coexist *btcoexist, u8 type) } } -void ex_halbtc8192e2ant_lps_notify(struct btc_coexist *btcoexist, u8 type) +void ex_btc8192e2ant_lps_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2936,7 +2923,7 @@ void ex_halbtc8192e2ant_lps_notify(struct btc_coexist *btcoexist, u8 type) } } -void ex_halbtc8192e2ant_scan_notify(struct btc_coexist *btcoexist, u8 type) +void ex_btc8192e2ant_scan_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2948,7 +2935,7 @@ void ex_halbtc8192e2ant_scan_notify(struct btc_coexist *btcoexist, u8 type) "[BTCoex], SCAN FINISH notify\n"); } -void ex_halbtc8192e2ant_connect_notify(struct btc_coexist *btcoexist, u8 type) +void ex_btc8192e2ant_connect_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2960,8 +2947,8 @@ void ex_halbtc8192e2ant_connect_notify(struct btc_coexist *btcoexist, u8 type) "[BTCoex], CONNECT FINISH notify\n"); } -void ex_halbtc8192e2ant_media_status_notify(struct btc_coexist *btcoexist, - u8 type) +void ex_btc8192e2ant_media_status_notify(struct btc_coexist *btcoexist, + u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[3] = {0}; @@ -3006,8 +2993,8 @@ void ex_halbtc8192e2ant_media_status_notify(struct btc_coexist *btcoexist, btcoexist->btc_fill_h2c(btcoexist, 0x66, 3, h2c_parameter); } -void ex_halbtc8192e2ant_special_packet_notify(struct btc_coexist *btcoexist, - u8 type) +void ex_btc8192e2ant_special_packet_notify(struct btc_coexist *btcoexist, + u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -3016,8 +3003,8 @@ void ex_halbtc8192e2ant_special_packet_notify(struct btc_coexist *btcoexist, "[BTCoex], DHCP Packet notify\n"); } -void ex_halbtc8192e2ant_bt_info_notify(struct btc_coexist *btcoexist, - u8 *tmp_buf, u8 length) +void ex_btc8192e2ant_bt_info_notify(struct btc_coexist *btcoexist, + u8 *tmp_buf, u8 length) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 bt_info = 0; @@ -3048,7 +3035,8 @@ void ex_halbtc8192e2ant_bt_info_notify(struct btc_coexist *btcoexist, } if (BT_INFO_SRC_8192E_2ANT_WIFI_FW != rsp_source) { - coex_sta->bt_retry_cnt = /* [3:0] */ + /* [3:0] */ + coex_sta->bt_retry_cnt = coex_sta->bt_info_c2h[rsp_source][2] & 0xf; coex_sta->bt_rssi = @@ -3066,11 +3054,11 @@ void ex_halbtc8192e2ant_bt_info_notify(struct btc_coexist *btcoexist, btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); if (wifi_connected) - ex_halbtc8192e2ant_media_status_notify( + ex_btc8192e2ant_media_status_notify( btcoexist, BTC_MEDIA_CONNECT); else - ex_halbtc8192e2ant_media_status_notify( + ex_btc8192e2ant_media_status_notify( btcoexist, BTC_MEDIA_DISCONNECT); } @@ -3080,9 +3068,9 @@ void ex_halbtc8192e2ant_bt_info_notify(struct btc_coexist *btcoexist, !btcoexist->stop_coex_dm) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "bit3, BT NOT ignore Wlan active!\n"); - halbtc8192e2ant_IgnoreWlanAct(btcoexist, - FORCE_EXEC, - false); + btc8192e2ant_ignore_wlan_act(btcoexist, + FORCE_EXEC, + false); } } else { /* BT already NOT ignore Wlan active, @@ -3094,8 +3082,8 @@ void ex_halbtc8192e2ant_bt_info_notify(struct btc_coexist *btcoexist, if ((coex_sta->bt_info_ext & BIT4)) { /* BT auto report already enabled, do nothing */ } else { - halbtc8192e2ant_bt_autoreport(btcoexist, FORCE_EXEC, - true); + btc8192e2ant_bt_auto_report(btcoexist, FORCE_EXEC, + true); } #endif } @@ -3133,9 +3121,9 @@ void ex_halbtc8192e2ant_bt_info_notify(struct btc_coexist *btcoexist, coex_sta->sco_exist = false; } - halbtc8192e2ant_update_btlink_info(btcoexist); + btc8192e2ant_update_bt_link_info(btcoexist); - if (!(bt_info&BT_INFO_8192E_2ANT_B_CONNECTION)) { + if (!(bt_info & BT_INFO_8192E_2ANT_B_CONNECTION)) { coex_dm->bt_status = BT_8192E_2ANT_BT_STATUS_NON_CONNECTED_IDLE; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], BT Non-Connected idle!!!\n"); @@ -3143,12 +3131,12 @@ void ex_halbtc8192e2ant_bt_info_notify(struct btc_coexist *btcoexist, coex_dm->bt_status = BT_8192E_2ANT_BT_STATUS_CONNECTED_IDLE; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], bt_infoNotify(), BT Connected-idle!!!\n"); - } else if ((bt_info&BT_INFO_8192E_2ANT_B_SCO_ESCO) || - (bt_info&BT_INFO_8192E_2ANT_B_SCO_BUSY)) { + } else if ((bt_info & BT_INFO_8192E_2ANT_B_SCO_ESCO) || + (bt_info & BT_INFO_8192E_2ANT_B_SCO_BUSY)) { coex_dm->bt_status = BT_8192E_2ANT_BT_STATUS_SCO_BUSY; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], bt_infoNotify(), BT SCO busy!!!\n"); - } else if (bt_info&BT_INFO_8192E_2ANT_B_ACL_BUSY) { + } else if (bt_info & BT_INFO_8192E_2ANT_B_ACL_BUSY) { coex_dm->bt_status = BT_8192E_2ANT_BT_STATUS_ACL_BUSY; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], bt_infoNotify(), BT ACL busy!!!\n"); @@ -3173,7 +3161,7 @@ void ex_halbtc8192e2ant_bt_info_notify(struct btc_coexist *btcoexist, coex_dm->limited_dig = limited_dig; btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_LIMITED_DIG, &limited_dig); - halbtc8192e2ant_run_coexist_mechanism(btcoexist); + btc8192e2ant_run_coexist_mechanism(btcoexist); } void ex_halbtc8192e2ant_halt_notify(struct btc_coexist *btcoexist) @@ -3182,11 +3170,11 @@ void ex_halbtc8192e2ant_halt_notify(struct btc_coexist *btcoexist) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Halt notify\n"); - halbtc8192e2ant_IgnoreWlanAct(btcoexist, FORCE_EXEC, true); - ex_halbtc8192e2ant_media_status_notify(btcoexist, BTC_MEDIA_DISCONNECT); + btc8192e2ant_ignore_wlan_act(btcoexist, FORCE_EXEC, true); + ex_btc8192e2ant_media_status_notify(btcoexist, BTC_MEDIA_DISCONNECT); } -void ex_halbtc8192e2ant_periodical(struct btc_coexist *btcoexist) +void ex_btc8192e2ant_periodical(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; static u8 dis_ver_info_cnt; @@ -3220,12 +3208,12 @@ void ex_halbtc8192e2ant_periodical(struct btc_coexist *btcoexist) } #if (BT_AUTO_REPORT_ONLY_8192E_2ANT == 0) - halbtc8192e2ant_querybt_info(btcoexist); - halbtc8192e2ant_monitor_bt_ctr(btcoexist); - btc8192e2ant_monitor_bt_enable_dis(btcoexist); + btc8192e2ant_query_bt_info(btcoexist); + btc8192e2ant_monitor_bt_ctr(btcoexist); + btc8192e2ant_monitor_bt_enable_disable(btcoexist); #else - if (halbtc8192e2ant_iswifi_status_changed(btcoexist) || + if (btc8192e2ant_is_wifi_status_changed(btcoexist) || coex_dm->auto_tdma_adjust) - halbtc8192e2ant_run_coexist_mechanism(btcoexist); + btc8192e2ant_run_coexist_mechanism(btcoexist); #endif } diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.h index 75e1f7d0db06..fc0fa87ec404 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8192e2ant.h @@ -116,7 +116,7 @@ struct coex_dm_8192e_2ant { u32 backup_arfr_cnt1; /* Auto Rate Fallback Retry cnt */ u32 backup_arfr_cnt2; /* Auto Rate Fallback Retry cnt */ - u16 backup_retrylimit; + u16 backup_retry_limit; u8 backup_ampdu_maxtime; /* algorithm related */ @@ -125,18 +125,18 @@ struct coex_dm_8192e_2ant { u8 bt_status; u8 wifi_chnl_info[3]; - u8 pre_sstype; - u8 cur_sstype; + u8 pre_ss_type; + u8 cur_ss_type; - u32 prera_mask; - u32 curra_mask; - u8 curra_masktype; - u8 pre_arfrtype; - u8 cur_arfrtype; - u8 pre_retrylimit_type; - u8 cur_retrylimit_type; - u8 pre_ampdutime_type; - u8 cur_ampdutime_type; + u32 pre_ra_mask; + u32 cur_ra_mask; + u8 cur_ra_mask_type; + u8 pre_arfr_type; + u8 cur_arfr_type; + u8 pre_retry_limit_type; + u8 cur_retry_limit_type; + u8 pre_ampdu_time_type; + u8 cur_ampdu_time_type; }; struct coex_sta_8192e_2ant { diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c index b58aeff7eb16..2003c8c51dcc 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b1ant.c @@ -45,7 +45,7 @@ static struct coex_dm_8723b_1ant *coex_dm = &glcoex_dm_8723b_1ant; static struct coex_sta_8723b_1ant glcoex_sta_8723b_1ant; static struct coex_sta_8723b_1ant *coex_sta = &glcoex_sta_8723b_1ant; -static const char *const GLBtInfoSrc8723b1Ant[] = { +static const char *const glbt_info_src_8723b_1ant[] = { "BT Info[wifi fw]", "BT Info[bt rsp]", "BT Info[bt auto report]", @@ -67,7 +67,7 @@ static void halbtc8723b1ant_updatera_mask(struct btc_coexist *btcoexist, coex_dm->curra_mask = dis_rate_mask; if (force_exec || (coex_dm->prera_mask != coex_dm->curra_mask)) - btcoexist->btc_set(btcoexist, BTC_SET_ACT_UPDATE_ra_mask, + btcoexist->btc_set(btcoexist, BTC_SET_ACT_UPDATE_RAMASK, &coex_dm->curra_mask); coex_dm->prera_mask = coex_dm->curra_mask; @@ -144,15 +144,14 @@ static void halbtc8723b1ant_ampdu_maxtime(struct btc_coexist *btcoexist, coex_dm->cur_ampdu_time_type)) { switch (coex_dm->cur_ampdu_time_type) { case 0: /* normal mode */ - btcoexist->btc_write_1byte(btcoexist, 0x456, - coex_dm->backup_ampdu_max_time); - break; + btcoexist->btc_write_1byte(btcoexist, 0x456, + coex_dm->backup_ampdu_max_time); + break; case 1: /* AMPDU timw = 0x38 * 32us */ - btcoexist->btc_write_1byte(btcoexist, - 0x456, 0x38); - break; + btcoexist->btc_write_1byte(btcoexist, 0x456, 0x38); + break; default: - break; + break; } } @@ -172,7 +171,7 @@ static void halbtc8723b1ant_limited_tx(struct btc_coexist *btcoexist, halbtc8723b1ant_updatera_mask(btcoexist, force_exec, 0x00000003); break; - /* disable cck 1/2/5.5, ofdm 6/9/12/18/24, mcs 0/1/2/3/4*/ + /* disable cck 1/2/5.5, ofdm 6/9/12/18/24, mcs 0/1/2/3/4 */ case 2: halbtc8723b1ant_updatera_mask(btcoexist, force_exec, 0x0001f1f7); @@ -244,7 +243,8 @@ static void halbtc8723b1ant_query_bt_info(struct btc_coexist *btcoexist) coex_sta->c2h_bt_info_req_sent = true; - h2c_parameter[0] |= BIT0; /* trigger*/ + /* trigger */ + h2c_parameter[0] |= BIT0; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Query Bt Info, FW write 0x61 = 0x%x\n", @@ -343,11 +343,11 @@ static void btc8723b1ant_set_sw_pen_tx_rate_adapt(struct btc_coexist *btcoexist, if (low_penalty_ra) { h2c_parameter[1] |= BIT0; - /*normal rate except MCS7/6/5, OFDM54/48/36 */ + /* normal rate except MCS7/6/5, OFDM54/48/36 */ h2c_parameter[2] = 0x00; - h2c_parameter[3] = 0xf7; /*MCS7 or OFDM54 */ - h2c_parameter[4] = 0xf8; /*MCS6 or OFDM48 */ - h2c_parameter[5] = 0xf9; /*MCS5 or OFDM36 */ + h2c_parameter[3] = 0xf7; /* MCS7 or OFDM54 */ + h2c_parameter[4] = 0xf8; /* MCS6 or OFDM48 */ + h2c_parameter[5] = 0xf9; /* MCS5 or OFDM36 */ } RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -468,8 +468,9 @@ static void halbtc8723b1ant_coex_table_with_type(struct btc_coexist *btcoexist, } } -static void halbtc8723b1ant_SetFwIgnoreWlanAct(struct btc_coexist *btcoexist, - bool enable) +static void +halbtc8723b1ant_set_fw_ignore_wlan_act(struct btc_coexist *btcoexist, + bool enable) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[1] = {0}; @@ -504,7 +505,7 @@ static void halbtc8723b1ant_ignore_wlan_act(struct btc_coexist *btcoexist, coex_dm->cur_ignore_wlan_act) return; } - halbtc8723b1ant_SetFwIgnoreWlanAct(btcoexist, enable); + halbtc8723b1ant_set_fw_ignore_wlan_act(btcoexist, enable); coex_dm->pre_ignore_wlan_act = coex_dm->cur_ignore_wlan_act; } @@ -566,9 +567,9 @@ static void halbtc8723b1ant_set_lps_rpwm(struct btc_coexist *btcoexist, btcoexist->btc_set(btcoexist, BTC_SET_U1_RPWM_VAL, &rpwm); } -static void halbtc8723b1ant_LpsRpwm(struct btc_coexist *btcoexist, - bool force_exec, - u8 lps_val, u8 rpwm_val) +static void halbtc8723b1ant_lps_rpwm(struct btc_coexist *btcoexist, + bool force_exec, + u8 lps_val, u8 rpwm_val) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -609,9 +610,9 @@ static void halbtc8723b1ant_sw_mechanism(struct btc_coexist *btcoexist, halbtc8723b1ant_low_penalty_ra(btcoexist, NORMAL_EXEC, low_penalty_ra); } -static void halbtc8723b1ant_SetAntPath(struct btc_coexist *btcoexist, - u8 ant_pos_type, bool init_hw_cfg, - bool wifi_off) +static void halbtc8723b1ant_set_ant_path(struct btc_coexist *btcoexist, + u8 ant_pos_type, bool init_hw_cfg, + bool wifi_off) { struct btc_board_info *board_info = &btcoexist->board_info; u32 fw_ver = 0, u32tmp = 0; @@ -650,7 +651,7 @@ static void halbtc8723b1ant_SetAntPath(struct btc_coexist *btcoexist, if (use_ext_switch) { if (init_hw_cfg) { /* 0x4c[23] = 0, 0x4c[24] = 1 - * Antenna control by WL/BT + * Antenna control by WL/BT */ u32tmp = btcoexist->btc_read_4byte(btcoexist, 0x4c); u32tmp &= ~BIT23; @@ -659,35 +660,36 @@ static void halbtc8723b1ant_SetAntPath(struct btc_coexist *btcoexist, if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) { - /* Main Ant to BT for IPS case 0x4c[23] = 1 */ + /* Main Ant to BT for IPS case 0x4c[23] = 1 */ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, 0x1, 0x1); - /*tell firmware "no antenna inverse"*/ + /* tell firmware "no antenna inverse" */ h2c_parameter[0] = 0; h2c_parameter[1] = 1; /*ext switch type*/ btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); } else { - /*Aux Ant to BT for IPS case 0x4c[23] = 1 */ + /* Aux Ant to BT for IPS case 0x4c[23] = 1 */ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, 0x1, 0x0); - /*tell firmware "antenna inverse"*/ + /* tell firmware "antenna inverse" */ h2c_parameter[0] = 1; - h2c_parameter[1] = 1; /*ext switch type*/ + h2c_parameter[1] = 1; /* ext switch type */ btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); } } - /* fixed internal switch first*/ - /* fixed internal switch S1->WiFi, S0->BT*/ + /* fixed internal switch first + * fixed internal switch S1->WiFi, S0->BT + */ if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); - else/* fixed internal switch S0->WiFi, S1->BT*/ + else /* fixed internal switch S0->WiFi, S1->BT */ btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280); /* ext switch setting */ @@ -730,7 +732,7 @@ static void halbtc8723b1ant_SetAntPath(struct btc_coexist *btcoexist, } else { if (init_hw_cfg) { - /* 0x4c[23] = 1, 0x4c[24] = 0 Antenna control by 0x64*/ + /* 0x4c[23] = 1, 0x4c[24] = 0 Antenna control by 0x64 */ u32tmp = btcoexist->btc_read_4byte(btcoexist, 0x4c); u32tmp |= BIT23; u32tmp &= ~BIT24; @@ -738,41 +740,42 @@ static void halbtc8723b1ant_SetAntPath(struct btc_coexist *btcoexist, if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) { - /*Main Ant to WiFi for IPS case 0x4c[23] = 1*/ + /* Main Ant to WiFi for IPS case 0x4c[23] = 1 */ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, 0x1, 0x0); - /*tell firmware "no antenna inverse"*/ + /* tell firmware "no antenna inverse" */ h2c_parameter[0] = 0; - h2c_parameter[1] = 0; /*internal switch type*/ + h2c_parameter[1] = 0; /* internal switch type */ btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); } else { - /*Aux Ant to BT for IPS case 0x4c[23] = 1*/ + /* Aux Ant to BT for IPS case 0x4c[23] = 1 */ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, 0x1, 0x1); - /*tell firmware "antenna inverse"*/ + /* tell firmware "antenna inverse" */ h2c_parameter[0] = 1; - h2c_parameter[1] = 0; /*internal switch type*/ + h2c_parameter[1] = 0; /* internal switch type */ btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); } } - /* fixed external switch first*/ - /*Main->WiFi, Aux->BT*/ + /* fixed external switch first + * Main->WiFi, Aux->BT + */ if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) btcoexist->btc_write_1byte_bitmask(btcoexist, 0x92c, 0x3, 0x1); - else/*Main->BT, Aux->WiFi */ + else /* Main->BT, Aux->WiFi */ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x92c, 0x3, 0x2); - /* internal switch setting*/ + /* internal switch setting */ switch (ant_pos_type) { case BTC_ANT_PATH_WIFI: if (board_info->btdm_ant_pos == @@ -987,7 +990,7 @@ static void halbtc8723b1ant_ps_tdma(struct btc_coexist *btcoexist, halbtc8723b1ant_set_fw_ps_tdma(btcoexist, 0xd3, 0x12, 0x3, 0x14, 0x50); break; - /* SoftAP only with no sta associated,BT disable , + /* SoftAP only with no sta associated, BT disable, * TDMA mode for power saving * here softap mode screen off will cost 70-80mA for phone */ @@ -998,24 +1001,29 @@ static void halbtc8723b1ant_ps_tdma(struct btc_coexist *btcoexist, } } else { switch (type) { - case 8: /*PTA Control */ + case 8: /* PTA Control */ halbtc8723b1ant_set_fw_ps_tdma(btcoexist, 0x8, 0x0, 0x0, 0x0, 0x0); - halbtc8723b1ant_SetAntPath(btcoexist, BTC_ANT_PATH_PTA, - false, false); + halbtc8723b1ant_set_ant_path(btcoexist, + BTC_ANT_PATH_PTA, + false, false); break; case 0: - default: /*Software control, Antenna at BT side */ + default: + /* Software control, Antenna at BT side */ halbtc8723b1ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0, 0x0, 0x0); - halbtc8723b1ant_SetAntPath(btcoexist, BTC_ANT_PATH_BT, - false, false); + halbtc8723b1ant_set_ant_path(btcoexist, + BTC_ANT_PATH_BT, + false, false); break; - case 9: /*Software control, Antenna at WiFi side */ + case 9: + /* Software control, Antenna at WiFi side */ halbtc8723b1ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0, 0x0, 0x0); - halbtc8723b1ant_SetAntPath(btcoexist, BTC_ANT_PATH_WIFI, - false, false); + halbtc8723b1ant_set_ant_path(btcoexist, + BTC_ANT_PATH_WIFI, + false, false); break; } } @@ -1029,189 +1037,15 @@ static void halbtc8723b1ant_ps_tdma(struct btc_coexist *btcoexist, coex_dm->pre_ps_tdma = coex_dm->cur_ps_tdma; } -static void btc8723b1ant_tdma_dur_adj_for_acl(struct btc_coexist *btcoexist, - u8 wifi_status) -{ - struct rtl_priv *rtlpriv = btcoexist->adapter; - static s32 up, dn, m, n, wait_count; - /* 0: no change, +1: increase WiFi duration, - * -1: decrease WiFi duration - */ - s32 result; - u8 retry_count = 0, bt_info_ext; - bool wifi_busy = false; - - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], TdmaDurationAdjustForAcl()\n"); - - if (BT_8723B_1ANT_WIFI_STATUS_CONNECTED_BUSY == wifi_status) - wifi_busy = true; - else - wifi_busy = false; - - if ((BT_8723B_1ANT_WIFI_STATUS_NON_CONNECTED_ASSO_AUTH_SCAN == - wifi_status) || - (BT_8723B_1ANT_WIFI_STATUS_CONNECTED_SCAN == wifi_status) || - (BT_8723B_1ANT_WIFI_STATUS_CONNECTED_SPECIAL_PKT == wifi_status)) { - if (coex_dm->cur_ps_tdma != 1 && coex_dm->cur_ps_tdma != 2 && - coex_dm->cur_ps_tdma != 3 && coex_dm->cur_ps_tdma != 9) { - halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - - up = 0; - dn = 0; - m = 1; - n = 3; - result = 0; - wait_count = 0; - } - return; - } - - if (!coex_dm->auto_tdma_adjust) { - coex_dm->auto_tdma_adjust = true; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], first run TdmaDurationAdjust()!!\n"); - - halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 2); - coex_dm->tdma_adj_type = 2; - - up = 0; - dn = 0; - m = 1; - n = 3; - result = 0; - wait_count = 0; - } else { - /*accquire the BT TRx retry count from BT_Info byte2 */ - retry_count = coex_sta->bt_retry_cnt; - bt_info_ext = coex_sta->bt_info_ext; - result = 0; - wait_count++; - /* no retry in the last 2-second duration */ - if (retry_count == 0) { - up++; - dn--; - - if (dn <= 0) - dn = 0; - - if (up >= n) { - wait_count = 0; - n = 3; - up = 0; - dn = 0; - result = 1; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Increase wifi duration!!\n"); - } - } else if (retry_count <= 3) { - up--; - dn++; - - if (up <= 0) - up = 0; - - if (dn == 2) { - if (wait_count <= 2) - m++; - else - m = 1; - - if (m >= 20) - m = 20; - - n = 3 * m; - up = 0; - dn = 0; - wait_count = 0; - result = -1; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Decrease wifi duration for retryCounter<3!!\n"); - } - } else { - if (wait_count == 1) - m++; - else - m = 1; - - if (m >= 20) - m = 20; - - n = 3 * m; - up = 0; - dn = 0; - wait_count = 0; - result = -1; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Decrease wifi duration for retryCounter>3!!\n"); - } - - if (result == -1) { - if ((BT_INFO_8723B_1ANT_A2DP_BASIC_RATE(bt_info_ext)) && - ((coex_dm->cur_ps_tdma == 1) || - (coex_dm->cur_ps_tdma == 2))) { - halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - } else if (coex_dm->cur_ps_tdma == 1) { - halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - } else if (coex_dm->cur_ps_tdma == 9) { - halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; - } - } else if (result == 1) { - if ((BT_INFO_8723B_1ANT_A2DP_BASIC_RATE(bt_info_ext)) && - ((coex_dm->cur_ps_tdma == 1) || - (coex_dm->cur_ps_tdma == 2))) { - halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - } else if (coex_dm->cur_ps_tdma == 11) { - halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; - } else if (coex_dm->cur_ps_tdma == 9) { - halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; - } else if (coex_dm->cur_ps_tdma == 2) { - halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 1); - coex_dm->tdma_adj_type = 1; - } - } else { /*no change */ - /*if busy / idle change */ - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex],********* TDMA(on, %d) ********\n", - coex_dm->cur_ps_tdma); - } - - if (coex_dm->cur_ps_tdma != 1 && coex_dm->cur_ps_tdma != 2 && - coex_dm->cur_ps_tdma != 9 && coex_dm->cur_ps_tdma != 11) { - /* recover to previous adjust type */ - halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, - coex_dm->tdma_adj_type); - } - } -} - -static void btc8723b1ant_pstdmachkpwrsave(struct btc_coexist *btcoexist, - bool new_ps_state) +static void halbtc8723b1ant_ps_tdma_chk_pwr_save(struct btc_coexist *btcoexist, + bool new_ps_state) { u8 lps_mode = 0x0; btcoexist->btc_get(btcoexist, BTC_GET_U1_LPS_MODE, &lps_mode); - if (lps_mode) { /* already under LPS state */ + if (lps_mode) { + /* already under LPS state */ if (new_ps_state) { /* keep state under LPS, do nothing. */ } else { @@ -1219,7 +1053,8 @@ static void btc8723b1ant_pstdmachkpwrsave(struct btc_coexist *btcoexist, halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); } - } else { /* NO PS state */ + } else { + /* NO PS state */ if (new_ps_state) { /* will enter LPS state, turn off psTdma first */ halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, @@ -1245,18 +1080,18 @@ static void halbtc8723b1ant_power_save_state(struct btc_coexist *btcoexist, btcoexist->btc_set(btcoexist, BTC_SET_ACT_NORMAL_LPS, NULL); break; case BTC_PS_LPS_ON: - btc8723b1ant_pstdmachkpwrsave(btcoexist, true); - halbtc8723b1ant_LpsRpwm(btcoexist, NORMAL_EXEC, lps_val, - rpwm_val); - /* when coex force to enter LPS, do not enter 32k low power. */ + halbtc8723b1ant_ps_tdma_chk_pwr_save(btcoexist, true); + halbtc8723b1ant_lps_rpwm(btcoexist, NORMAL_EXEC, lps_val, + rpwm_val); + /* when coex force to enter LPS, do not enter 32k low power */ low_pwr_disable = true; btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); - /* power save must executed before psTdma. */ + /* power save must executed before psTdma */ btcoexist->btc_set(btcoexist, BTC_SET_ACT_ENTER_LPS, NULL); break; case BTC_PS_LPS_OFF: - btc8723b1ant_pstdmachkpwrsave(btcoexist, false); + halbtc8723b1ant_ps_tdma_chk_pwr_save(btcoexist, false); btcoexist->btc_set(btcoexist, BTC_SET_ACT_LEAVE_LPS, NULL); break; default: @@ -1330,11 +1165,11 @@ static void btc8723b1ant_act_bt_sco_hid_only_busy(struct btc_coexist *btcoexist, &wifi_connected); /* tdma and coex table */ - if (bt_link_info->sco_exist) { halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); - } else { /* HID */ + } else { + /* HID */ halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 6); halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 5); } @@ -1347,18 +1182,18 @@ static void halbtc8723b1ant_action_wifi_connected_bt_acl_busy( struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; - if (bt_link_info->hid_only) { /*HID */ + if (bt_link_info->hid_only) { /* HID */ btc8723b1ant_act_bt_sco_hid_only_busy(btcoexist, wifi_status); coex_dm->auto_tdma_adjust = false; return; - } else if (bt_link_info->a2dp_only) { /*A2DP */ - if (BT_8723B_1ANT_WIFI_STATUS_CONNECTED_IDLE == wifi_status) { + } else if (bt_link_info->a2dp_only) { /* A2DP */ + if (wifi_status == BT_8723B_1ANT_WIFI_STATUS_CONNECTED_IDLE) { halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); coex_dm->auto_tdma_adjust = false; - } else { /*for low BT RSSI */ + } else { /* for low BT RSSI */ halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); halbtc8723b1ant_coex_table_with_type(btcoexist, @@ -1366,18 +1201,18 @@ static void halbtc8723b1ant_action_wifi_connected_bt_acl_busy( coex_dm->auto_tdma_adjust = false; } } else if (bt_link_info->hid_exist && - bt_link_info->a2dp_exist) { /*HID+A2DP */ - halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); + bt_link_info->a2dp_exist) { /* HID + A2DP */ + halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); coex_dm->auto_tdma_adjust = false; halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 6); - /*PAN(OPP,FTP), HID+PAN(OPP,FTP) */ + /* PAN(OPP,FTP), HID + PAN(OPP,FTP) */ } else if (bt_link_info->pan_only || (bt_link_info->hid_exist && bt_link_info->pan_exist)) { halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 6); coex_dm->auto_tdma_adjust = false; - /*A2DP+PAN(OPP,FTP), HID+A2DP+PAN(OPP,FTP)*/ + /* A2DP + PAN(OPP,FTP), HID + A2DP + PAN(OPP,FTP) */ } else if ((bt_link_info->a2dp_exist && bt_link_info->pan_exist) || (bt_link_info->hid_exist && bt_link_info->a2dp_exist && bt_link_info->pan_exist)) { @@ -1402,57 +1237,59 @@ static void btc8723b1ant_action_wifi_not_conn(struct btc_coexist *btcoexist) halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); } -static void btc8723b1ant_action_wifi_not_conn_scan(struct btc_coexist *btcoex) +static void +btc8723b1ant_action_wifi_not_conn_scan(struct btc_coexist *btcoexist) { - struct btc_bt_link_info *bt_link_info = &btcoex->bt_link_info; + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; - halbtc8723b1ant_power_save_state(btcoex, BTC_PS_WIFI_NATIVE, + halbtc8723b1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); /* tdma and coex table */ if (BT_8723B_1ANT_BT_STATUS_ACL_BUSY == coex_dm->bt_status) { if (bt_link_info->a2dp_exist && bt_link_info->pan_exist) { - halbtc8723b1ant_ps_tdma(btcoex, NORMAL_EXEC, + halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); - halbtc8723b1ant_coex_table_with_type(btcoex, + halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); } else if (bt_link_info->pan_only) { - halbtc8723b1ant_ps_tdma(btcoex, NORMAL_EXEC, + halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 20); - halbtc8723b1ant_coex_table_with_type(btcoex, + halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); } else { - halbtc8723b1ant_ps_tdma(btcoex, NORMAL_EXEC, + halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 20); - halbtc8723b1ant_coex_table_with_type(btcoex, + halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); } } else if ((BT_8723B_1ANT_BT_STATUS_SCO_BUSY == coex_dm->bt_status) || (BT_8723B_1ANT_BT_STATUS_ACL_SCO_BUSY == coex_dm->bt_status)){ - btc8723b1ant_act_bt_sco_hid_only_busy(btcoex, + btc8723b1ant_act_bt_sco_hid_only_busy(btcoexist, BT_8723B_1ANT_WIFI_STATUS_CONNECTED_SCAN); } else { - halbtc8723b1ant_ps_tdma(btcoex, NORMAL_EXEC, false, 8); - halbtc8723b1ant_coex_table_with_type(btcoex, NORMAL_EXEC, 2); + halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); + halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); } } -static void btc8723b1ant_act_wifi_not_conn_asso_auth(struct btc_coexist *btcoex) +static void +btc8723b1ant_act_wifi_not_conn_asso_auth(struct btc_coexist *btcoexist) { - struct btc_bt_link_info *bt_link_info = &btcoex->bt_link_info; + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; - halbtc8723b1ant_power_save_state(btcoex, BTC_PS_WIFI_NATIVE, + halbtc8723b1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); if ((BT_8723B_1ANT_BT_STATUS_CONNECTED_IDLE == coex_dm->bt_status) || (bt_link_info->sco_exist) || (bt_link_info->hid_only) || (bt_link_info->a2dp_only) || (bt_link_info->pan_only)) { - halbtc8723b1ant_ps_tdma(btcoex, NORMAL_EXEC, false, 8); - halbtc8723b1ant_coex_table_with_type(btcoex, NORMAL_EXEC, 7); + halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); + halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); } else { - halbtc8723b1ant_ps_tdma(btcoex, NORMAL_EXEC, true, 20); - halbtc8723b1ant_coex_table_with_type(btcoex, NORMAL_EXEC, 1); + halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 20); + halbtc8723b1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); } } @@ -1663,13 +1500,12 @@ static void halbtc8723b1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) if (!bt_link_info->sco_exist && !bt_link_info->hid_exist) { halbtc8723b1ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); } else { - if (wifi_connected) { + if (wifi_connected) halbtc8723b1ant_limited_tx(btcoexist, NORMAL_EXEC, 1, 1, 1, 1); - } else { + else halbtc8723b1ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); - } } if (bt_link_info->sco_exist) { @@ -1784,22 +1620,19 @@ static void halbtc8723b1ant_init_hw_config(struct btc_coexist *btcoexist, btcoexist->btc_write_1byte(btcoexist, 0x790, u8tmp); /* Enable counter statistics */ - /*0x76e[3] =1, WLAN_Act control by PTA */ + /*0x76e[3] = 1, WLAN_Act control by PTA */ btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc); btcoexist->btc_write_1byte(btcoexist, 0x778, 0x1); btcoexist->btc_write_1byte_bitmask(btcoexist, 0x40, 0x20, 0x1); - /*Antenna config */ - halbtc8723b1ant_SetAntPath(btcoexist, BTC_ANT_PATH_PTA, true, false); + /* Antenna config */ + halbtc8723b1ant_set_ant_path(btcoexist, BTC_ANT_PATH_PTA, true, false); /* PTA parameter */ halbtc8723b1ant_coex_table_with_type(btcoexist, FORCE_EXEC, 0); } /************************************************************** - * work around function start with wa_halbtc8723b1ant_ - **************************************************************/ -/************************************************************** - * extern function start with EXhalbtc8723b1ant_ + * extern function start with ex_halbtc8723b1ant_ **************************************************************/ void ex_halbtc8723b1ant_init_hwconfig(struct btc_coexist *btcoexist) @@ -1953,7 +1786,7 @@ void ex_halbtc8723b1ant_display_coex_info(struct btc_coexist *btcoexist) if (coex_sta->bt_info_c2h_cnt[i]) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %7ph(%d)", - GLBtInfoSrc8723b1Ant[i], + glbt_info_src_8723b_1ant[i], coex_sta->bt_info_c2h[i], coex_sta->bt_info_c2h_cnt[i]); } @@ -2111,8 +1944,8 @@ void ex_halbtc8723b1ant_ips_notify(struct btc_coexist *btcoexist, u8 type) "[BTCoex], IPS ENTER notify\n"); coex_sta->under_ips = true; - halbtc8723b1ant_SetAntPath(btcoexist, BTC_ANT_PATH_BT, - false, true); + halbtc8723b1ant_set_ant_path(btcoexist, BTC_ANT_PATH_BT, + false, true); /* set PTA control */ halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); halbtc8723b1ant_coex_table_with_type(btcoexist, @@ -2187,14 +2020,17 @@ void ex_halbtc8723b1ant_scan_notify(struct btc_coexist *btcoexist, u8 type) if (BTC_SCAN_START == type) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCAN START notify\n"); - if (!wifi_connected) /* non-connected scan */ + if (!wifi_connected) + /* non-connected scan */ btc8723b1ant_action_wifi_not_conn_scan(btcoexist); - else /* wifi is connected */ + else + /* wifi is connected */ btc8723b1ant_action_wifi_conn_scan(btcoexist); } else if (BTC_SCAN_FINISH == type) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCAN FINISH notify\n"); - if (!wifi_connected) /* non-connected scan */ + if (!wifi_connected) + /* non-connected scan */ btc8723b1ant_action_wifi_not_conn(btcoexist); else halbtc8723b1ant_action_wifi_connected(btcoexist); @@ -2244,7 +2080,8 @@ void ex_halbtc8723b1ant_connect_notify(struct btc_coexist *btcoexist, u8 type) btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); - if (!wifi_connected) /* non-connected scan */ + if (!wifi_connected) + /* non-connected scan */ btc8723b1ant_action_wifi_not_conn(btcoexist); else halbtc8723b1ant_action_wifi_connected(btcoexist); @@ -2433,7 +2270,8 @@ void ex_halbtc8723b1ant_bt_info_notify(struct btc_coexist *btcoexist, coex_sta->a2dp_exist = false; coex_sta->hid_exist = false; coex_sta->sco_exist = false; - } else { /* connection exists */ + } else { + /* connection exists */ coex_sta->bt_link_exist = true; if (bt_info & BT_INFO_8723B_1ANT_B_FTP) coex_sta->pan_exist = true; @@ -2502,7 +2340,7 @@ void ex_halbtc8723b1ant_halt_notify(struct btc_coexist *btcoexist) btcoexist->stop_coex_dm = true; - halbtc8723b1ant_SetAntPath(btcoexist, BTC_ANT_PATH_BT, false, true); + halbtc8723b1ant_set_ant_path(btcoexist, BTC_ANT_PATH_BT, false, true); halbtc8723b1ant_ignore_wlan_act(btcoexist, FORCE_EXEC, true); @@ -2523,8 +2361,8 @@ void ex_halbtc8723b1ant_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Pnp notify to SLEEP\n"); btcoexist->stop_coex_dm = true; - halbtc8723b1ant_SetAntPath(btcoexist, BTC_ANT_PATH_BT, false, - true); + halbtc8723b1ant_set_ant_path(btcoexist, BTC_ANT_PATH_BT, false, + true); halbtc8723b1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); halbtc8723b1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 4e5dfaa02da9..9b09b94cbb43 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -676,15 +676,15 @@ static void btc8723b_set_penalty_txrate(struct btc_coexist *btcoexist, struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[6] = {0}; - h2c_parameter[0] = 0x6; /* opCode, 0x6= Retry_Penalty*/ + h2c_parameter[0] = 0x6; /* op_code, 0x6 = Retry_Penalty */ if (low_penalty_ra) { h2c_parameter[1] |= BIT0; - /*normal rate except MCS7/6/5, OFDM54/48/36*/ + /* normal rate except MCS7/6/5, OFDM54/48/36 */ h2c_parameter[2] = 0x00; - h2c_parameter[3] = 0xf7; /*MCS7 or OFDM54*/ - h2c_parameter[4] = 0xf8; /*MCS6 or OFDM48*/ - h2c_parameter[5] = 0xf9; /*MCS5 or OFDM36*/ + h2c_parameter[3] = 0xf7; /* MCS7 or OFDM54 */ + h2c_parameter[4] = 0xf8; /* MCS6 or OFDM48 */ + h2c_parameter[5] = 0xf9; /* MCS5 or OFDM36 */ } RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -699,7 +699,6 @@ static void btc8723b2ant_low_penalty_ra(struct btc_coexist *btcoexist, { struct rtl_priv *rtlpriv = btcoexist->adapter; - /*return; */ RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], %s turn LowPenaltyRA = %s\n", (force_exec ? "force to" : ""), (low_penalty_ra ? @@ -741,9 +740,9 @@ static void btc8723b2ant_set_sw_fulltime_dac_swing(struct btc_coexist *btcoex, btc8723b2ant_set_dac_swing_reg(btcoex, 0x18); } -static void btc8723b2ant_dac_swing(struct btc_coexist *btcoexist, - bool force_exec, bool dac_swing_on, - u32 dac_swing_lvl) +void btc8723b2ant_dac_swing(struct btc_coexist *btcoexist, + bool force_exec, bool dac_swing_on, + u32 dac_swing_lvl) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -838,8 +837,8 @@ static void btc8723b2ant_coex_table(struct btc_coexist *btcoexist, coex_dm->pre_val0x6cc = coex_dm->cur_val0x6cc; } -static void btc8723b_coex_tbl_type(struct btc_coexist *btcoexist, - bool force_exec, u8 type) +static void btc8723b2ant_coex_table_with_type(struct btc_coexist *btcoexist, + bool force_exec, u8 type) { switch (type) { case 0: @@ -906,7 +905,7 @@ static void btc8723b2ant_set_fw_ignore_wlan_act(struct btc_coexist *btcoexist, u8 h2c_parameter[1] = {0}; if (enable) - h2c_parameter[0] |= BIT0;/* function enable*/ + h2c_parameter[0] |= BIT0; /* function enable */ RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], set FW for BT Ignore Wlan_Act, FW write 0x63=0x%x\n", @@ -967,9 +966,9 @@ static void btc8723b2ant_set_fw_ps_tdma(struct btc_coexist *btcoexist, u8 byte1, btcoexist->btc_fill_h2c(btcoexist, 0x60, 5, h2c_parameter); } -static void btc8723b2ant_sw_mechanism1(struct btc_coexist *btcoexist, - bool shrink_rx_lpf, bool low_penalty_ra, - bool limited_dig, bool bt_lna_constrain) +static void btc8723b2ant_sw_mechanism(struct btc_coexist *btcoexist, + bool shrink_rx_lpf, bool low_penalty_ra, + bool limited_dig, bool bt_lna_constrain) { btc8723b2ant_low_penalty_ra(btcoexist, NORMAL_EXEC, low_penalty_ra); } @@ -1208,11 +1207,11 @@ static void btc8723b2ant_coex_alloff(struct btc_coexist *btcoexist) btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); /* sw all off */ - btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, false, false); /* hw all off */ btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); } static void btc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist) @@ -1223,7 +1222,9 @@ static void btc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist) btc8723b2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6); btc8723b2ant_dec_bt_pwr(btcoexist, FORCE_EXEC, false); - btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, false, false); + + coex_sta->pop_event_cnt = 0; } static void btc8723b2ant_action_bt_inquiry(struct btc_coexist *btcoexist) @@ -1237,22 +1238,16 @@ static void btc8723b2ant_action_bt_inquiry(struct btc_coexist *btcoexist) &wifi_connected); if (wifi_connected) { - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); } else { - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); } btc8723b2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6); btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); - - coex_dm->need_recover_0x948 = true; - coex_dm->backup_0x948 = btcoexist->btc_read_2byte(btcoexist, 0x948); - - btc8723b2ant_set_ant_path(btcoexist, BTC_ANT_WIFI_AT_AUX, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, false, false); } static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) @@ -1277,13 +1272,13 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, - false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, false, + false); common = true; } else { @@ -1299,15 +1294,16 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0); + btc8723b2ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 0); btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 0xb); btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btc8723b2ant_sw_mechanism1(btcoexist, false, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, + false, false); common = true; } else if (BT_8723B_2ANT_BT_STATUS_CONNECTED_IDLE == @@ -1324,15 +1320,16 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0); + btc8723b2ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 0); btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 0xb); btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btc8723b2ant_sw_mechanism1(btcoexist, true, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, false, + false, false); common = true; } else { @@ -1354,16 +1351,17 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, - 7); + btc8723b2ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, + 7); btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 21); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 0xb); - btc8723b2ant_sw_mechanism1(btcoexist, false, - false, false, - false); + btc8723b2ant_sw_mechanism(btcoexist, false, + false, false, + false); common = true; } } @@ -1542,8 +1540,11 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, "[BTCoex], max Interval = %d\n", max_interval); } - /*if current PsTdma not match with the recorded one (when scan, dhcp..), - *then we have to adjust it back to the previous recorded one. + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], max Interval = %d\n", max_interval); + + /* if current PsTdma not match with the recorded one (scan, dhcp, ...), + * then we have to adjust it back to the previous recorded one. */ if (coex_dm->cur_ps_tdma != coex_dm->tdma_adj_type) { bool scan = false, link = false, roam = false; @@ -1579,33 +1580,34 @@ static void btc8723b2ant_action_sco(struct btc_coexist *btcoexist) btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - /*for SCO quality at 11b/g mode*/ if (BTC_WIFI_BW_LEGACY == wifi_bw) - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 2); - else /*for SCO quality & wifi performance balance at 11n mode*/ - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 8); + /* for SCO quality at 11b/g mode */ + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); + else + /* for SCO quality & wifi performance balance at 11n mode */ + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 8); - /*for voice quality */ + /* for voice quality */ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); /* sw mechanism */ if (BTC_WIFI_BW_HT40 == wifi_bw) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, true, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, true, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, true, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, true, + false, false); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, false, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, true, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, false, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, true, + false, false); } } } @@ -1625,10 +1627,13 @@ static void btc8723b2ant_action_hid(struct btc_coexist *btcoexist) btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - if (BTC_WIFI_BW_LEGACY == wifi_bw) /*/for HID at 11b/g mode*/ - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); - else /*for HID quality & wifi performance balance at 11n mode*/ - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 9); + if (wifi_bw == BTC_WIFI_BW_LEGACY) + /* for HID at 11b/g mode */ + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); + else + /* for HID quality & wifi performance balance at 11n mode */ + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 9); + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) @@ -1640,25 +1645,25 @@ static void btc8723b2ant_action_hid(struct btc_coexist *btcoexist) if (BTC_WIFI_BW_HT40 == wifi_bw) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, true, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, true, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, true, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, true, + false, false); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, false, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, true, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, false, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, true, + false, false); } } } -/*A2DP only / PAN(EDR) only/ A2DP+PAN(HS)*/ +/* A2DP only / PAN(EDR) only/ A2DP+PAN(HS) */ static void btc8723b2ant_action_a2dp(struct btc_coexist *btcoexist) { u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; @@ -1680,17 +1685,17 @@ static void btc8723b2ant_action_a2dp(struct btc_coexist *btcoexist) 0x0); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); /* sw mechanism */ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); if (BTC_WIFI_BW_HT40 == wifi_bw) { - btc8723b2ant_sw_mechanism1(btcoexist, true, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, false, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, false, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, + false, false); } return; } @@ -1699,7 +1704,7 @@ static void btc8723b2ant_action_a2dp(struct btc_coexist *btcoexist) btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) @@ -1713,20 +1718,20 @@ static void btc8723b2ant_action_a2dp(struct btc_coexist *btcoexist) if (BTC_WIFI_BW_HT40 == wifi_bw) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, true, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, false, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, true, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, false, + false, false); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, false, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, false, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, + false, false); } } } @@ -1743,30 +1748,29 @@ static void btc8723b2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); btc8723b2ant_tdma_duration_adjust(btcoexist, false, true, 2); /* sw mechanism */ - btcoexist->btc_get(btcoexist, - BTC_GET_U4_WIFI_BW, &wifi_bw); + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); if (BTC_WIFI_BW_HT40 == wifi_bw) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, true, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, false, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, true, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, false, + false, false); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, false, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, false, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, + false, false); } } } @@ -1789,7 +1793,7 @@ static void btc8723b2ant_action_pan_edr(struct btc_coexist *btcoexist) else btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 10); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 10); if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) @@ -1802,25 +1806,25 @@ static void btc8723b2ant_action_pan_edr(struct btc_coexist *btcoexist) if (BTC_WIFI_BW_HT40 == wifi_bw) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, true, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, false, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, true, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, false, + false, false); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, false, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, false, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, + false, false); } } } -/*PAN(HS) only*/ +/* PAN(HS) only */ static void btc8723b2ant_action_pan_hs(struct btc_coexist *btcoexist) { u8 wifi_rssi_state; @@ -1839,33 +1843,32 @@ static void btc8723b2ant_action_pan_hs(struct btc_coexist *btcoexist) else btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); - + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); if (BTC_WIFI_BW_HT40 == wifi_bw) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, true, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, false, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, true, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, false, + false, false); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, false, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, false, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, + false, false); } } } -/*PAN(EDR)+A2DP*/ +/* PAN(EDR) + A2DP */ static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) { u8 wifi_rssi_state, bt_rssi_state; @@ -1888,7 +1891,7 @@ static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 12); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 12); if (BTC_WIFI_BW_HT40 == wifi_bw) btc8723b2ant_tdma_duration_adjust(btcoexist, false, true, 3); @@ -1896,7 +1899,7 @@ static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) btc8723b2ant_tdma_duration_adjust(btcoexist, false, false, 3); } else { - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); btc8723b2ant_tdma_duration_adjust(btcoexist, false, true, 3); } @@ -1904,20 +1907,20 @@ static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) if (BTC_WIFI_BW_HT40 == wifi_bw) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, true, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, false, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, true, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, false, + false, false); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, false, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, false, false, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, false, + false, false); } } } @@ -1942,20 +1945,22 @@ static void btc8723b2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) if (BTC_WIFI_BW_HT40 == wifi_bw) { btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 3); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 11); + btc8723b2ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 11); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x780); } else { btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 7); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); } btc8723b2ant_tdma_duration_adjust(btcoexist, true, false, 2); } else { btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 11); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 11); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); btc8723b2ant_tdma_duration_adjust(btcoexist, true, true, 2); @@ -1965,25 +1970,25 @@ static void btc8723b2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) if (BTC_WIFI_BW_HT40 == wifi_bw) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, true, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, true, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, true, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, true, + false, false); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, false, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, true, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, false, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, true, + false, false); } } } -/* HID+A2DP+PAN(EDR) */ +/* HID + A2DP + PAN(EDR) */ static void btc8723b2ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) { u8 wifi_rssi_state, bt_rssi_state; @@ -2004,7 +2009,7 @@ static void btc8723b2ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { @@ -2022,20 +2027,20 @@ static void btc8723b2ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) if (BTC_WIFI_BW_HT40 == wifi_bw) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, true, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, true, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, true, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, true, + false, false); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, false, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, true, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, false, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, true, + false, false); } } } @@ -2055,7 +2060,7 @@ static void btc8723b2ant_action_hid_a2dp(struct btc_coexist *btcoexist) btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) @@ -2067,20 +2072,20 @@ static void btc8723b2ant_action_hid_a2dp(struct btc_coexist *btcoexist) if (BTC_WIFI_BW_HT40 == wifi_bw) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, true, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, true, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, true, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, true, true, + false, false); } } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8723b2ant_sw_mechanism1(btcoexist, false, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, true, + false, false); } else { - btc8723b2ant_sw_mechanism1(btcoexist, false, true, - false, false); + btc8723b2ant_sw_mechanism(btcoexist, false, true, + false, false); } } } @@ -2209,10 +2214,7 @@ static void btc8723b2ant_wifioff_hwcfg(struct btc_coexist *btcoexist) } /********************************************************************* - * work around function start with wa_btc8723b2ant_ - *********************************************************************/ -/********************************************************************* - * extern function start with EXbtc8723b2ant_ + * extern function start with ex_btc8723b2ant_ *********************************************************************/ void ex_btc8723b2ant_init_hwconfig(struct btc_coexist *btcoexist) { @@ -2230,14 +2232,14 @@ void ex_btc8723b2ant_init_hwconfig(struct btc_coexist *btcoexist) u8tmp |= 0x5; btcoexist->btc_write_1byte(btcoexist, 0x790, u8tmp); - /*Antenna config */ + /* Antenna config */ btc8723b2ant_set_ant_path(btcoexist, BTC_ANT_WIFI_AT_MAIN, true, false); /* PTA parameter */ - btc8723b_coex_tbl_type(btcoexist, FORCE_EXEC, 0); + btc8723b2ant_coex_table_with_type(btcoexist, FORCE_EXEC, 0); /* Enable counter statistics */ - /*0x76e[3] =1, WLAN_Act control by PTA*/ + /* 0x76e[3] = 1, WLAN_ACT controlled by PTA */ btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc); btcoexist->btc_write_1byte(btcoexist, 0x778, 0x3); btcoexist->btc_write_1byte_bitmask(btcoexist, 0x40, 0x20, 0x1); @@ -2615,7 +2617,7 @@ void ex_btc8723b2ant_bt_info_notify(struct btc_coexist *btcoexist, coex_sta->bt_info_c2h[rsp_source][i] = tmpbuf[i]; if (i == 1) bt_info = tmpbuf[i]; - if (i == length-1) + if (i == length - 1) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "0x%02x]\n", tmpbuf[i]); else @@ -2630,7 +2632,7 @@ void ex_btc8723b2ant_bt_info_notify(struct btc_coexist *btcoexist, } if (BT_INFO_SRC_8723B_2ANT_WIFI_FW != rsp_source) { - coex_sta->bt_retry_cnt = /* [3:0]*/ + coex_sta->bt_retry_cnt = coex_sta->bt_info_c2h[rsp_source][2] & 0xf; coex_sta->bt_rssi = @@ -2640,7 +2642,7 @@ void ex_btc8723b2ant_bt_info_notify(struct btc_coexist *btcoexist, coex_sta->bt_info_c2h[rsp_source][4]; /* Here we need to resend some wifi info to BT - because bt is reset and loss of the info. + * because BT is reset and loss of the info. */ if ((coex_sta->bt_info_ext & BIT1)) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -2675,20 +2677,21 @@ void ex_btc8723b2ant_bt_info_notify(struct btc_coexist *btcoexist, #endif } - /* check BIT2 first ==> check if bt is under inquiry or page scan*/ + /* check BIT2 first ==> check if bt is under inquiry or page scan */ if (bt_info & BT_INFO_8723B_2ANT_B_INQ_PAGE) coex_sta->c2h_bt_inquiry_page = true; else coex_sta->c2h_bt_inquiry_page = false; - /* set link exist status*/ if (!(bt_info & BT_INFO_8723B_2ANT_B_CONNECTION)) { + /* set link exist status */ coex_sta->bt_link_exist = false; coex_sta->pan_exist = false; coex_sta->a2dp_exist = false; coex_sta->hid_exist = false; coex_sta->sco_exist = false; - } else { /* connection exists */ + } else { + /* connection exists */ coex_sta->bt_link_exist = true; if (bt_info & BT_INFO_8723B_2ANT_B_FTP) coex_sta->pan_exist = true; diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h index 567f354caf95..d787ae3bced2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h @@ -148,6 +148,7 @@ struct coex_sta_8723b_2ant { bool c2h_bt_inquiry_page; u8 bt_retry_cnt; u8 bt_info_ext; + u32 pop_event_cnt; }; /********************************************************************* diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index 4a8d8cfb34d3..6f6ab0738fbb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -23,7 +23,7 @@ * *****************************************************************************/ -/*============================================================ +/************************************************************** * Description: * * This file is for RTL8821A Co-exist mechanism @@ -31,17 +31,15 @@ * History * 2012/11/15 Cosa first check in. * - *============================================================ -*/ -/*============================================================ + **************************************************************/ + +/************************************************************** * include files - *============================================================ - */ + **************************************************************/ #include "halbt_precomp.h" -/*============================================================ +/************************************************************** * Global variables, these are static variables - *============================================================ - */ + **************************************************************/ static struct coex_dm_8821a_1ant glcoex_dm_8821a_1ant; static struct coex_dm_8821a_1ant *coex_dm = &glcoex_dm_8821a_1ant; static struct coex_sta_8821a_1ant glcoex_sta_8821a_1ant; @@ -53,22 +51,21 @@ static const char *const glbt_info_src_8821a_1ant[] = { "BT Info[bt auto report]", }; -static u32 glcoex_ver_date_8821a_1ant = 20130816; -static u32 glcoex_ver_8821a_1ant = 0x41; +static u32 glcoex_ver_date_8821a_1ant = 20130816; +static u32 glcoex_ver_8821a_1ant = 0x41; -/*============================================================ +/************************************************************** * local function proto type if needed * - * local function start with halbtc8821a1ant_ - *============================================================ - */ -static u8 halbtc8821a1ant_bt_rssi_state(struct btc_coexist *btcoexist, - u8 level_num, u8 rssi_thresh, - u8 rssi_thresh1) + * local function start with btc8821a1ant_ + **************************************************************/ +static u8 btc8821a1ant_bt_rssi_state(struct btc_coexist *btcoexist, + u8 level_num, u8 rssi_thresh, + u8 rssi_thresh1) { struct rtl_priv *rtlpriv = btcoexist->adapter; - long bt_rssi = 0; - u8 bt_rssi_state = coex_sta->pre_bt_rssi_state; + long bt_rssi = 0; + u8 bt_rssi_state = coex_sta->pre_bt_rssi_state; bt_rssi = coex_sta->bt_rssi; @@ -150,9 +147,9 @@ static u8 halbtc8821a1ant_bt_rssi_state(struct btc_coexist *btcoexist, return bt_rssi_state; } -static u8 halbtc8821a1ant_WifiRssiState(struct btc_coexist *btcoexist, - u8 index, u8 level_num, u8 rssi_thresh, - u8 rssi_thresh1) +static u8 btc8821a1ant_wifi_rssi_state(struct btc_coexist *btcoexist, + u8 index, u8 level_num, u8 rssi_thresh, + u8 rssi_thresh1) { struct rtl_priv *rtlpriv = btcoexist->adapter; long wifi_rssi = 0; @@ -165,8 +162,8 @@ static u8 halbtc8821a1ant_WifiRssiState(struct btc_coexist *btcoexist, BTC_RSSI_STATE_LOW) || (coex_sta->pre_wifi_rssi_state[index] == BTC_RSSI_STATE_STAY_LOW)) { - if (wifi_rssi >= - (rssi_thresh+BTC_RSSI_COEX_THRESH_TOL_8821A_1ANT)) { + if (wifi_rssi >= (rssi_thresh + + BTC_RSSI_COEX_THRESH_TOL_8821A_1ANT)) { wifi_rssi_state = BTC_RSSI_STATE_HIGH; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], wifi RSSI state switch to High\n"); @@ -197,8 +194,8 @@ static u8 halbtc8821a1ant_WifiRssiState(struct btc_coexist *btcoexist, BTC_RSSI_STATE_LOW) || (coex_sta->pre_wifi_rssi_state[index] == BTC_RSSI_STATE_STAY_LOW)) { - if (wifi_rssi >= - (rssi_thresh+BTC_RSSI_COEX_THRESH_TOL_8821A_1ANT)) { + if (wifi_rssi >= (rssi_thresh + + BTC_RSSI_COEX_THRESH_TOL_8821A_1ANT)) { wifi_rssi_state = BTC_RSSI_STATE_MEDIUM; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], wifi RSSI state switch to Medium\n"); @@ -211,9 +208,8 @@ static u8 halbtc8821a1ant_WifiRssiState(struct btc_coexist *btcoexist, BTC_RSSI_STATE_MEDIUM) || (coex_sta->pre_wifi_rssi_state[index] == BTC_RSSI_STATE_STAY_MEDIUM)) { - if (wifi_rssi >= - (rssi_thresh1 + - BTC_RSSI_COEX_THRESH_TOL_8821A_1ANT)) { + if (wifi_rssi >= (rssi_thresh1 + + BTC_RSSI_COEX_THRESH_TOL_8821A_1ANT)) { wifi_rssi_state = BTC_RSSI_STATE_HIGH; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], wifi RSSI state switch to High\n"); @@ -243,14 +239,14 @@ static u8 halbtc8821a1ant_WifiRssiState(struct btc_coexist *btcoexist, return wifi_rssi_state; } -static void halbtc8821a1ant_update_ra_mask(struct btc_coexist *btcoexist, - bool force_exec, u32 dis_rate_mask) +static void btc8821a1ant_update_ra_mask(struct btc_coexist *btcoexist, + bool force_exec, u32 dis_rate_mask) { coex_dm->cur_ra_mask = dis_rate_mask; if (force_exec || (coex_dm->pre_ra_mask != coex_dm->cur_ra_mask)) { - btcoexist->btc_set(btcoexist, BTC_SET_ACT_UPDATE_ra_mask, + btcoexist->btc_set(btcoexist, BTC_SET_ACT_UPDATE_RAMASK, &coex_dm->cur_ra_mask); } coex_dm->pre_ra_mask = coex_dm->cur_ra_mask; @@ -259,14 +255,14 @@ static void halbtc8821a1ant_update_ra_mask(struct btc_coexist *btcoexist, static void btc8821a1ant_auto_rate_fb_retry(struct btc_coexist *btcoexist, bool force_exec, u8 type) { - bool wifi_under_b_mode = false; + bool wifi_under_b_mode = false; coex_dm->cur_arfr_type = type; if (force_exec || (coex_dm->pre_arfr_type != coex_dm->cur_arfr_type)) { switch (coex_dm->cur_arfr_type) { - case 0: /* normal mode*/ + case 0: /* normal mode */ btcoexist->btc_write_4byte(btcoexist, 0x430, coex_dm->backup_arfr_cnt1); btcoexist->btc_write_4byte(btcoexist, 0x434, @@ -296,19 +292,19 @@ static void btc8821a1ant_auto_rate_fb_retry(struct btc_coexist *btcoexist, coex_dm->pre_arfr_type = coex_dm->cur_arfr_type; } -static void halbtc8821a1ant_retry_limit(struct btc_coexist *btcoexist, - bool force_exec, u8 type) +static void btc8821a1ant_retry_limit(struct btc_coexist *btcoexist, + bool force_exec, u8 type) { coex_dm->cur_retry_limit_type = type; if (force_exec || (coex_dm->pre_retry_limit_type != coex_dm->cur_retry_limit_type)) { switch (coex_dm->cur_retry_limit_type) { - case 0: /* normal mode*/ + case 0: /* normal mode */ btcoexist->btc_write_2byte(btcoexist, 0x42a, coex_dm->backup_retry_limit); break; - case 1: /* retry limit = 8*/ + case 1: /* retry limit = 8 */ btcoexist->btc_write_2byte(btcoexist, 0x42a, 0x0808); break; default: @@ -318,19 +314,19 @@ static void halbtc8821a1ant_retry_limit(struct btc_coexist *btcoexist, coex_dm->pre_retry_limit_type = coex_dm->cur_retry_limit_type; } -static void halbtc8821a1ant_ampdu_max_time(struct btc_coexist *btcoexist, - bool force_exec, u8 type) +static void btc8821a1ant_ampdu_max_time(struct btc_coexist *btcoexist, + bool force_exec, u8 type) { coex_dm->cur_ampdu_time_type = type; if (force_exec || (coex_dm->pre_ampdu_time_type != coex_dm->cur_ampdu_time_type)) { switch (coex_dm->cur_ampdu_time_type) { - case 0: /* normal mode*/ + case 0: /* normal mode */ btcoexist->btc_write_1byte(btcoexist, 0x456, coex_dm->backup_ampdu_max_time); break; - case 1: /* AMPDU timw = 0x38 * 32us*/ + case 1: /* AMPDU time = 0x38 * 32us */ btcoexist->btc_write_1byte(btcoexist, 0x456, 0x38); break; default: @@ -341,88 +337,85 @@ static void halbtc8821a1ant_ampdu_max_time(struct btc_coexist *btcoexist, coex_dm->pre_ampdu_time_type = coex_dm->cur_ampdu_time_type; } -static void halbtc8821a1ant_limited_tx(struct btc_coexist *btcoexist, - bool force_exec, u8 ra_mask_type, - u8 arfr_type, u8 retry_limit_type, - u8 ampdu_time_type) +static void btc8821a1ant_limited_tx(struct btc_coexist *btcoexist, + bool force_exec, u8 ra_mask_type, + u8 arfr_type, u8 retry_limit_type, + u8 ampdu_time_type) { switch (ra_mask_type) { - case 0: /* normal mode*/ - halbtc8821a1ant_update_ra_mask(btcoexist, force_exec, 0x0); + case 0: /* normal mode */ + btc8821a1ant_update_ra_mask(btcoexist, force_exec, 0x0); break; - case 1: /* disable cck 1/2*/ - halbtc8821a1ant_update_ra_mask(btcoexist, force_exec, - 0x00000003); + case 1: /* disable cck 1/2 */ + btc8821a1ant_update_ra_mask(btcoexist, force_exec, + 0x00000003); break; - case 2: /* disable cck 1/2/5.5, ofdm 6/9/12/18/24, mcs 0/1/2/3/4*/ - halbtc8821a1ant_update_ra_mask(btcoexist, force_exec, - 0x0001f1f7); + case 2: /* disable cck 1/2/5.5, ofdm 6/9/12/18/24, mcs 0/1/2/3/4 */ + btc8821a1ant_update_ra_mask(btcoexist, force_exec, + 0x0001f1f7); break; default: break; } btc8821a1ant_auto_rate_fb_retry(btcoexist, force_exec, arfr_type); - halbtc8821a1ant_retry_limit(btcoexist, force_exec, retry_limit_type); - halbtc8821a1ant_ampdu_max_time(btcoexist, force_exec, ampdu_time_type); + btc8821a1ant_retry_limit(btcoexist, force_exec, retry_limit_type); + btc8821a1ant_ampdu_max_time(btcoexist, force_exec, ampdu_time_type); } -static void halbtc8821a1ant_limited_rx(struct btc_coexist *btcoexist, - bool force_exec, bool rej_ap_agg_pkt, - bool bt_ctrl_agg_buf_size, - u8 agg_buf_size) +static void btc8821a1ant_limited_rx(struct btc_coexist *btcoexist, + bool force_exec, bool rej_ap_agg_pkt, + bool bt_ctrl_agg_buf_size, u8 agg_buf_size) { bool reject_rx_agg = rej_ap_agg_pkt; bool bt_ctrl_rx_agg_size = bt_ctrl_agg_buf_size; u8 rx_agg_size = agg_buf_size; - /*============================================*/ - /* Rx Aggregation related setting*/ - /*============================================*/ + /* Rx Aggregation related setting */ btcoexist->btc_set(btcoexist, BTC_SET_BL_TO_REJ_AP_AGG_PKT, &reject_rx_agg); - /* decide BT control aggregation buf size or not*/ + /* decide BT control aggregation buf size or not */ btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_CTRL_AGG_SIZE, &bt_ctrl_rx_agg_size); - /* aggregation buf size, only work when BT control Rx agg size.*/ + /* aggregation buf size, only work when BT control Rx agg size */ btcoexist->btc_set(btcoexist, BTC_SET_U1_AGG_BUF_SIZE, &rx_agg_size); - /* real update aggregation setting*/ + /* real update aggregation setting */ btcoexist->btc_set(btcoexist, BTC_SET_ACT_AGGREGATE_CTRL, NULL); } -static void halbtc8821a1ant_monitor_bt_ctr(struct btc_coexist *btcoexist) +static void btc8821a1ant_monitor_bt_ctr(struct btc_coexist *btcoexist) { - u32 reg_hp_tx_rx, reg_lp_tx_rx, u4_tmp; - u32 reg_hp_tx = 0, reg_hp_rx = 0, reg_lp_tx = 0, reg_lp_rx = 0; + u32 reg_hp_tx_rx, reg_lp_tx_rx, u4_tmp; + u32 reg_hp_tx = 0, reg_hp_rx = 0, reg_lp_tx = 0, reg_lp_rx = 0; reg_hp_tx_rx = 0x770; reg_lp_tx_rx = 0x774; u4_tmp = btcoexist->btc_read_4byte(btcoexist, reg_hp_tx_rx); reg_hp_tx = u4_tmp & MASKLWORD; - reg_hp_rx = (u4_tmp & MASKHWORD)>>16; + reg_hp_rx = (u4_tmp & MASKHWORD) >> 16; u4_tmp = btcoexist->btc_read_4byte(btcoexist, reg_lp_tx_rx); reg_lp_tx = u4_tmp & MASKLWORD; - reg_lp_rx = (u4_tmp & MASKHWORD)>>16; + reg_lp_rx = (u4_tmp & MASKHWORD) >> 16; coex_sta->high_priority_tx = reg_hp_tx; coex_sta->high_priority_rx = reg_hp_rx; coex_sta->low_priority_tx = reg_lp_tx; coex_sta->low_priority_rx = reg_lp_rx; - /* reset counter*/ + /* reset counter */ btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc); } -static void halbtc8821a1ant_query_bt_info(struct btc_coexist *btcoexist) +static void btc8821a1ant_query_bt_info(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[1] = {0}; coex_sta->c2h_bt_info_req_sent = true; - h2c_parameter[0] |= BIT0; /* trigger*/ + h2c_parameter[0] |= BIT0; /* trigger */ RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Query Bt Info, FW write 0x61 = 0x%x\n", @@ -431,10 +424,10 @@ static void halbtc8821a1ant_query_bt_info(struct btc_coexist *btcoexist) btcoexist->btc_fill_h2c(btcoexist, 0x61, 1, h2c_parameter); } -static void halbtc8821a1ant_update_bt_link_info(struct btc_coexist *btcoexist) +static void btc8821a1ant_update_bt_link_info(struct btc_coexist *btcoexist) { struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; - bool bt_hs_on = false; + bool bt_hs_on = false; btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); @@ -444,13 +437,13 @@ static void halbtc8821a1ant_update_bt_link_info(struct btc_coexist *btcoexist) bt_link_info->pan_exist = coex_sta->pan_exist; bt_link_info->hid_exist = coex_sta->hid_exist; - /* work around for HS mode.*/ + /* work around for HS mode */ if (bt_hs_on) { bt_link_info->pan_exist = true; bt_link_info->bt_link_exist = true; } - /* check if Sco only*/ + /* check if Sco only */ if (bt_link_info->sco_exist && !bt_link_info->a2dp_exist && !bt_link_info->pan_exist && @@ -459,7 +452,7 @@ static void halbtc8821a1ant_update_bt_link_info(struct btc_coexist *btcoexist) else bt_link_info->sco_only = false; - /* check if A2dp only*/ + /* check if A2dp only */ if (!bt_link_info->sco_exist && bt_link_info->a2dp_exist && !bt_link_info->pan_exist && @@ -468,7 +461,7 @@ static void halbtc8821a1ant_update_bt_link_info(struct btc_coexist *btcoexist) else bt_link_info->a2dp_only = false; - /* check if Pan only*/ + /* check if Pan only */ if (!bt_link_info->sco_exist && !bt_link_info->a2dp_exist && bt_link_info->pan_exist && @@ -477,7 +470,7 @@ static void halbtc8821a1ant_update_bt_link_info(struct btc_coexist *btcoexist) else bt_link_info->pan_only = false; - /* check if Hid only*/ + /* check if Hid only */ if (!bt_link_info->sco_exist && !bt_link_info->a2dp_exist && !bt_link_info->pan_exist && @@ -487,13 +480,13 @@ static void halbtc8821a1ant_update_bt_link_info(struct btc_coexist *btcoexist) bt_link_info->hid_only = false; } -static u8 halbtc8821a1ant_action_algorithm(struct btc_coexist *btcoexist) +static u8 btc8821a1ant_action_algorithm(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; - bool bt_hs_on = false; - u8 algorithm = BT_8821A_1ANT_COEX_ALGO_UNDEFINED; - u8 num_of_diff_profile = 0; + bool bt_hs_on = false; + u8 algorithm = BT_8821A_1ANT_COEX_ALGO_UNDEFINED; + u8 num_of_diff_profile = 0; btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); @@ -605,7 +598,7 @@ static u8 halbtc8821a1ant_action_algorithm(struct btc_coexist *btcoexist) "[BTCoex], BT Profile = SCO + HID + A2DP ==> HID\n"); algorithm = BT_8821A_1ANT_COEX_ALGO_HID; } else if (bt_link_info->hid_exist && - bt_link_info->pan_exist) { + bt_link_info->pan_exist) { if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -618,7 +611,7 @@ static u8 halbtc8821a1ant_action_algorithm(struct btc_coexist *btcoexist) algorithm = BT_8821A_1ANT_COEX_ALGO_PANEDR_HID; } } else if (bt_link_info->pan_exist && - bt_link_info->a2dp_exist) { + bt_link_info->a2dp_exist) { if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -670,8 +663,8 @@ static u8 halbtc8821a1ant_action_algorithm(struct btc_coexist *btcoexist) return algorithm; } -static void btc8821a1ant_set_sw_pen_tx_rate(struct btc_coexist *btcoexist, - bool low_penalty_ra) +static void btc8821a1ant_set_sw_penalty_tx_rate(struct btc_coexist *btcoexist, + bool low_penalty_ra) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[6] = {0}; @@ -680,11 +673,11 @@ static void btc8821a1ant_set_sw_pen_tx_rate(struct btc_coexist *btcoexist, if (low_penalty_ra) { h2c_parameter[1] |= BIT0; - /*normal rate except MCS7/6/5, OFDM54/48/36*/ + /* normal rate except MCS7/6/5, OFDM54/48/36 */ h2c_parameter[2] = 0x00; - h2c_parameter[3] = 0xf7; /*MCS7 or OFDM54*/ - h2c_parameter[4] = 0xf8; /*MCS6 or OFDM48*/ - h2c_parameter[5] = 0xf9; /*MCS5 or OFDM36*/ + h2c_parameter[3] = 0xf7; /* MCS7 or OFDM54 */ + h2c_parameter[4] = 0xf8; /* MCS6 or OFDM48 */ + h2c_parameter[5] = 0xf9; /* MCS5 or OFDM36 */ } RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -694,8 +687,8 @@ static void btc8821a1ant_set_sw_pen_tx_rate(struct btc_coexist *btcoexist, btcoexist->btc_fill_h2c(btcoexist, 0x69, 6, h2c_parameter); } -static void halbtc8821a1ant_low_penalty_ra(struct btc_coexist *btcoexist, - bool force_exec, bool low_penalty_ra) +static void btc8821a1ant_low_penalty_ra(struct btc_coexist *btcoexist, + bool force_exec, bool low_penalty_ra) { coex_dm->cur_low_penalty_ra = low_penalty_ra; @@ -703,14 +696,15 @@ static void halbtc8821a1ant_low_penalty_ra(struct btc_coexist *btcoexist, if (coex_dm->pre_low_penalty_ra == coex_dm->cur_low_penalty_ra) return; } - btc8821a1ant_set_sw_pen_tx_rate(btcoexist, coex_dm->cur_low_penalty_ra); + btc8821a1ant_set_sw_penalty_tx_rate(btcoexist, + coex_dm->cur_low_penalty_ra); coex_dm->pre_low_penalty_ra = coex_dm->cur_low_penalty_ra; } -static void halbtc8821a1ant_set_coex_table(struct btc_coexist *btcoexist, - u32 val0x6c0, u32 val0x6c4, - u32 val0x6c8, u8 val0x6cc) +static void btc8821a1ant_set_coex_table(struct btc_coexist *btcoexist, + u32 val0x6c0, u32 val0x6c4, + u32 val0x6c8, u8 val0x6cc) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -731,9 +725,9 @@ static void halbtc8821a1ant_set_coex_table(struct btc_coexist *btcoexist, btcoexist->btc_write_1byte(btcoexist, 0x6cc, val0x6cc); } -static void halbtc8821a1ant_coex_table(struct btc_coexist *btcoexist, - bool force_exec, u32 val0x6c0, - u32 val0x6c4, u32 val0x6c8, u8 val0x6cc) +static void btc8821a1ant_coex_table(struct btc_coexist *btcoexist, + bool force_exec, u32 val0x6c0, u32 val0x6c4, + u32 val0x6c8, u8 val0x6cc) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -753,8 +747,8 @@ static void halbtc8821a1ant_coex_table(struct btc_coexist *btcoexist, (coex_dm->pre_val_0x6cc == coex_dm->cur_val_0x6cc)) return; } - halbtc8821a1ant_set_coex_table(btcoexist, val0x6c0, val0x6c4, - val0x6c8, val0x6cc); + btc8821a1ant_set_coex_table(btcoexist, val0x6c0, val0x6c4, + val0x6c8, val0x6cc); coex_dm->pre_val_0x6c0 = coex_dm->cur_val_0x6c0; coex_dm->pre_val_0x6c4 = coex_dm->cur_val_0x6c4; @@ -762,42 +756,41 @@ static void halbtc8821a1ant_coex_table(struct btc_coexist *btcoexist, coex_dm->pre_val_0x6cc = coex_dm->cur_val_0x6cc; } -static void halbtc8821a1ant_coex_table_with_type(struct btc_coexist *btcoexist, - bool force_exec, u8 type) +static void btc8821a1ant_coex_table_with_type(struct btc_coexist *btcoexist, + bool force_exec, u8 type) { switch (type) { case 0: - halbtc8821a1ant_coex_table(btcoexist, force_exec, 0x55555555, - 0x55555555, 0xffffff, 0x3); + btc8821a1ant_coex_table(btcoexist, force_exec, 0x55555555, + 0x55555555, 0xffffff, 0x3); break; case 1: - halbtc8821a1ant_coex_table(btcoexist, force_exec, - 0x55555555, 0x5a5a5a5a, - 0xffffff, 0x3); - break; + btc8821a1ant_coex_table(btcoexist, force_exec, 0x55555555, + 0x5a5a5a5a, 0xffffff, 0x3); + break; case 2: - halbtc8821a1ant_coex_table(btcoexist, force_exec, 0x5a5a5a5a, - 0x5a5a5a5a, 0xffffff, 0x3); + btc8821a1ant_coex_table(btcoexist, force_exec, 0x5a5a5a5a, + 0x5a5a5a5a, 0xffffff, 0x3); break; case 3: - halbtc8821a1ant_coex_table(btcoexist, force_exec, 0x55555555, - 0xaaaaaaaa, 0xffffff, 0x3); + btc8821a1ant_coex_table(btcoexist, force_exec, 0x55555555, + 0xaaaaaaaa, 0xffffff, 0x3); break; case 4: - halbtc8821a1ant_coex_table(btcoexist, force_exec, 0xffffffff, - 0xffffffff, 0xffffff, 0x3); + btc8821a1ant_coex_table(btcoexist, force_exec, 0xffffffff, + 0xffffffff, 0xffffff, 0x3); break; case 5: - halbtc8821a1ant_coex_table(btcoexist, force_exec, 0x5fff5fff, - 0x5fff5fff, 0xffffff, 0x3); + btc8821a1ant_coex_table(btcoexist, force_exec, 0x5fff5fff, + 0x5fff5fff, 0xffffff, 0x3); break; case 6: - halbtc8821a1ant_coex_table(btcoexist, force_exec, 0x55ff55ff, - 0x5a5a5a5a, 0xffffff, 0x3); + btc8821a1ant_coex_table(btcoexist, force_exec, 0x55ff55ff, + 0x5a5a5a5a, 0xffffff, 0x3); break; case 7: - halbtc8821a1ant_coex_table(btcoexist, force_exec, 0x5afa5afa, - 0x5afa5afa, 0xffffff, 0x3); + btc8821a1ant_coex_table(btcoexist, force_exec, 0x5afa5afa, + 0x5afa5afa, 0xffffff, 0x3); break; default: break; @@ -808,10 +801,10 @@ static void btc8821a1ant_set_fw_ignore_wlan_act(struct btc_coexist *btcoexist, bool enable) { struct rtl_priv *rtlpriv = btcoexist->adapter; - u8 h2c_parameter[1] = {0}; + u8 h2c_parameter[1] = {0}; if (enable) - h2c_parameter[0] |= BIT0; /* function enable*/ + h2c_parameter[0] |= BIT0; /* function enable */ RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], set FW for BT Ignore Wlan_Act, FW write 0x63 = 0x%x\n", @@ -820,8 +813,8 @@ static void btc8821a1ant_set_fw_ignore_wlan_act(struct btc_coexist *btcoexist, btcoexist->btc_fill_h2c(btcoexist, 0x63, 1, h2c_parameter); } -static void halbtc8821a1ant_ignore_wlan_act(struct btc_coexist *btcoexist, - bool force_exec, bool enable) +static void btc8821a1ant_ignore_wlan_act(struct btc_coexist *btcoexist, + bool force_exec, bool enable) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -845,9 +838,8 @@ static void halbtc8821a1ant_ignore_wlan_act(struct btc_coexist *btcoexist, coex_dm->pre_ignore_wlan_act = coex_dm->cur_ignore_wlan_act; } -static void halbtc8821a1ant_set_fw_pstdma(struct btc_coexist *btcoexist, - u8 byte1, u8 byte2, u8 byte3, - u8 byte4, u8 byte5) +static void btc8821a1ant_set_fw_ps_tdma(struct btc_coexist *btcoexist, u8 byte1, + u8 byte2, u8 byte3, u8 byte4, u8 byte5) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[5] = {0}; @@ -874,18 +866,18 @@ static void halbtc8821a1ant_set_fw_pstdma(struct btc_coexist *btcoexist, btcoexist->btc_fill_h2c(btcoexist, 0x60, 5, h2c_parameter); } -static void halbtc8821a1ant_set_lps_rpwm(struct btc_coexist *btcoexist, - u8 lps_val, u8 rpwm_val) +static void btc8821a1ant_set_lps_rpwm(struct btc_coexist *btcoexist, + u8 lps_val, u8 rpwm_val) { - u8 lps = lps_val; - u8 rpwm = rpwm_val; + u8 lps = lps_val; + u8 rpwm = rpwm_val; btcoexist->btc_set(btcoexist, BTC_SET_U1_LPS_VAL, &lps); btcoexist->btc_set(btcoexist, BTC_SET_U1_RPWM_VAL, &rpwm); } -static void halbtc8821a1ant_lps_rpwm(struct btc_coexist *btcoexist, - bool force_exec, u8 lps_val, u8 rpwm_val) +static void btc8821a1ant_lps_rpwm(struct btc_coexist *btcoexist, + bool force_exec, u8 lps_val, u8 rpwm_val) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -909,33 +901,33 @@ static void halbtc8821a1ant_lps_rpwm(struct btc_coexist *btcoexist, return; } } - halbtc8821a1ant_set_lps_rpwm(btcoexist, lps_val, rpwm_val); + btc8821a1ant_set_lps_rpwm(btcoexist, lps_val, rpwm_val); coex_dm->pre_lps = coex_dm->cur_lps; coex_dm->pre_rpwm = coex_dm->cur_rpwm; } -static void halbtc8821a1ant_sw_mechanism(struct btc_coexist *btcoexist, - bool low_penalty_ra) +static void btc8821a1ant_sw_mechanism(struct btc_coexist *btcoexist, + bool low_penalty_ra) { struct rtl_priv *rtlpriv = btcoexist->adapter; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SM[LpRA] = %d\n", low_penalty_ra); - halbtc8821a1ant_low_penalty_ra(btcoexist, NORMAL_EXEC, low_penalty_ra); + btc8821a1ant_low_penalty_ra(btcoexist, NORMAL_EXEC, low_penalty_ra); } -static void halbtc8821a1ant_set_ant_path(struct btc_coexist *btcoexist, - u8 ant_pos_type, bool init_hw_cfg, - bool wifi_off) +static void btc8821a1ant_set_ant_path(struct btc_coexist *btcoexist, + u8 ant_pos_type, bool init_hw_cfg, + bool wifi_off) { struct btc_board_info *board_info = &btcoexist->board_info; u32 u4_tmp = 0; u8 h2c_parameter[2] = {0}; if (init_hw_cfg) { - /* 0x4c[23] = 0, 0x4c[24] = 1 Antenna control by WL/BT*/ + /* 0x4c[23] = 0, 0x4c[24] = 1 Antenna control by WL/BT */ u4_tmp = btcoexist->btc_read_4byte(btcoexist, 0x4c); u4_tmp &= ~BIT23; u4_tmp |= BIT24; @@ -945,31 +937,31 @@ static void halbtc8821a1ant_set_ant_path(struct btc_coexist *btcoexist, btcoexist->btc_write_1byte(btcoexist, 0xcb4, 0x77); if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) { - /*tell firmware "antenna inverse" ==> - * WRONG firmware antenna control code.==>need fw to fix + /* tell firmware "antenna inverse" + * WRONG firmware antenna control code, need fw to fix */ h2c_parameter[0] = 1; h2c_parameter[1] = 1; btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); - /*Main Ant to BT for IPS case 0x4c[23] = 1*/ + /* Main Ant to BT for IPS case 0x4c[23] = 1 */ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, 0x1, 0x1); } else { - /*tell firmware "no antenna inverse" ==> - * WRONG firmware antenna control code.==>need fw to fix + /* tell firmware "no antenna inverse" + * WRONG firmware antenna control code, need fw to fix */ h2c_parameter[0] = 0; h2c_parameter[1] = 1; btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); - /*Aux Ant to BT for IPS case 0x4c[23] = 1*/ + /* Aux Ant to BT for IPS case 0x4c[23] = 1 */ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, 0x1, 0x0); } } else if (wifi_off) { /* 0x4c[24:23] = 00, Set Antenna control - * by BT_RFE_CTRL BT Vendor 0xac = 0xf002 + * by BT_RFE_CTRL BT Vendor 0xac = 0xf002 */ u4_tmp = btcoexist->btc_read_4byte(btcoexist, 0x4c); u4_tmp &= ~BIT23; @@ -977,7 +969,7 @@ static void halbtc8821a1ant_set_ant_path(struct btc_coexist *btcoexist, btcoexist->btc_write_4byte(btcoexist, 0x4c, u4_tmp); } - /* ext switch setting*/ + /* ext switch setting */ switch (ant_pos_type) { case BTC_ANT_PATH_WIFI: if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) @@ -1007,8 +999,8 @@ static void halbtc8821a1ant_set_ant_path(struct btc_coexist *btcoexist, } } -static void halbtc8821a1ant_ps_tdma(struct btc_coexist *btcoexist, - bool force_exec, bool turn_on, u8 type) +static void btc8821a1ant_ps_tdma(struct btc_coexist *btcoexist, + bool force_exec, bool turn_on, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 rssi_adjust_val = 0; @@ -1033,185 +1025,189 @@ static void halbtc8821a1ant_ps_tdma(struct btc_coexist *btcoexist, if (turn_on) { switch (type) { default: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x51, 0x1a, - 0x1a, 0x0, 0x50); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x51, 0x1a, + 0x1a, 0x0, 0x50); break; case 1: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x51, 0x3a, - 0x03, 0x10, 0x50); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x51, 0x3a, + 0x03, 0x10, 0x50); rssi_adjust_val = 11; break; case 2: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x51, 0x2b, - 0x03, 0x10, 0x50); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x51, 0x2b, + 0x03, 0x10, 0x50); rssi_adjust_val = 14; break; case 3: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x51, 0x1d, - 0x1d, 0x0, 0x10); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x51, 0x1d, + 0x1d, 0x0, 0x10); break; case 4: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x93, 0x15, - 0x3, 0x14, 0x0); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x93, 0x15, + 0x3, 0x14, 0x0); rssi_adjust_val = 17; break; case 5: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x61, 0x15, - 0x3, 0x11, 0x10); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x61, 0x15, + 0x3, 0x11, 0x10); break; case 6: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x13, 0xa, - 0x3, 0x0, 0x0); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x13, 0xa, + 0x3, 0x0, 0x0); break; case 7: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x13, 0xc, - 0x5, 0x0, 0x0); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x13, 0xc, + 0x5, 0x0, 0x0); break; case 8: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x93, 0x25, - 0x3, 0x10, 0x0); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x93, 0x25, + 0x3, 0x10, 0x0); break; case 9: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x51, 0x21, - 0x3, 0x10, 0x50); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x51, 0x21, + 0x3, 0x10, 0x50); rssi_adjust_val = 18; break; case 10: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x13, 0xa, - 0xa, 0x0, 0x40); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x13, 0xa, + 0xa, 0x0, 0x40); break; case 11: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x51, 0x14, - 0x03, 0x10, 0x10); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x51, 0x14, + 0x03, 0x10, 0x10); rssi_adjust_val = 20; break; case 12: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x51, 0x0a, - 0x0a, 0x0, 0x50); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x51, 0x0a, + 0x0a, 0x0, 0x50); break; case 13: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x51, 0x18, - 0x18, 0x0, 0x10); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x51, 0x18, + 0x18, 0x0, 0x10); break; case 14: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x51, 0x21, - 0x3, 0x10, 0x10); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x51, 0x21, + 0x3, 0x10, 0x10); break; case 15: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x13, 0xa, - 0x3, 0x8, 0x0); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x13, 0xa, + 0x3, 0x8, 0x0); break; case 16: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x93, 0x15, - 0x3, 0x10, 0x0); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x93, 0x15, + 0x3, 0x10, 0x0); rssi_adjust_val = 18; break; case 18: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x93, 0x25, - 0x3, 0x10, 0x0); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x93, 0x25, + 0x3, 0x10, 0x0); rssi_adjust_val = 14; break; case 20: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x61, 0x35, - 0x03, 0x11, 0x10); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x61, 0x35, + 0x03, 0x11, 0x10); break; case 21: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x61, 0x15, - 0x03, 0x11, 0x10); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x61, 0x15, + 0x03, 0x11, 0x10); break; case 22: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x61, 0x25, - 0x03, 0x11, 0x10); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x61, 0x25, + 0x03, 0x11, 0x10); break; case 23: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0xe3, 0x25, - 0x3, 0x31, 0x18); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x25, + 0x3, 0x31, 0x18); rssi_adjust_val = 22; break; case 24: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0xe3, 0x15, - 0x3, 0x31, 0x18); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x15, + 0x3, 0x31, 0x18); rssi_adjust_val = 22; break; case 25: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0xe3, 0xa, - 0x3, 0x31, 0x18); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0xe3, 0xa, + 0x3, 0x31, 0x18); rssi_adjust_val = 22; break; case 26: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0xe3, 0xa, - 0x3, 0x31, 0x18); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0xe3, 0xa, + 0x3, 0x31, 0x18); rssi_adjust_val = 22; break; case 27: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0xe3, 0x25, - 0x3, 0x31, 0x98); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x25, + 0x3, 0x31, 0x98); rssi_adjust_val = 22; break; case 28: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x69, 0x25, - 0x3, 0x31, 0x0); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x69, 0x25, + 0x3, 0x31, 0x0); break; case 29: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0xab, 0x1a, - 0x1a, 0x1, 0x10); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0xab, 0x1a, + 0x1a, 0x1, 0x10); break; case 30: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x51, 0x14, - 0x3, 0x10, 0x50); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x51, 0x14, + 0x3, 0x10, 0x50); break; case 31: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0xd3, 0x1a, - 0x1a, 0, 0x58); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0xd3, 0x1a, + 0x1a, 0, 0x58); break; case 32: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x61, 0xa, - 0x3, 0x10, 0x0); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x61, 0xa, + 0x3, 0x10, 0x0); break; case 33: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0xa3, 0x25, - 0x3, 0x30, 0x90); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0xa3, 0x25, + 0x3, 0x30, 0x90); break; case 34: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x53, 0x1a, - 0x1a, 0x0, 0x10); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x53, 0x1a, + 0x1a, 0x0, 0x10); break; case 35: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x63, 0x1a, - 0x1a, 0x0, 0x10); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x63, 0x1a, + 0x1a, 0x0, 0x10); break; case 36: - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0xd3, 0x12, - 0x3, 0x14, 0x50); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0xd3, 0x12, + 0x3, 0x14, 0x50); break; } } else { - /* disable PS tdma*/ + /* disable PS tdma */ switch (type) { - case 8: /*PTA Control*/ - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x8, 0x0, 0x0, - 0x0, 0x0); - halbtc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_PTA, - false, false); + case 8: + /* PTA Control */ + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x8, 0x0, 0x0, + 0x0, 0x0); + btc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_PTA, + false, false); break; case 0: - default: /*Software control, Antenna at BT side*/ - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x0, 0x0, 0x0, - 0x0, 0x0); - halbtc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_BT, - false, false); + default: + /* Software control, Antenna at BT side */ + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0, + 0x0, 0x0); + btc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_BT, + false, false); break; - case 9: /*Software control, Antenna at WiFi side*/ - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x0, 0x0, 0x0, - 0x0, 0x0); - halbtc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_WIFI, - false, false); + case 9: + /* Software control, Antenna at WiFi side */ + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0, + 0x0, 0x0); + btc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_WIFI, + false, false); break; - case 10: /* under 5G*/ - halbtc8821a1ant_set_fw_pstdma(btcoexist, 0x0, 0x0, 0x0, - 0x8, 0x0); - halbtc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_BT, - false, false); + case 10: + /* under 5G */ + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0, + 0x8, 0x0); + btc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_BT, + false, false); break; } } @@ -1219,15 +1215,15 @@ static void halbtc8821a1ant_ps_tdma(struct btc_coexist *btcoexist, btcoexist->btc_set(btcoexist, BTC_SET_U1_RSSI_ADJ_VAL_FOR_1ANT_COEX_TYPE, &rssi_adjust_val); - /* update pre state*/ + /* update pre state */ coex_dm->pre_ps_tdma_on = coex_dm->cur_ps_tdma_on; coex_dm->pre_ps_tdma = coex_dm->cur_ps_tdma; } -static bool halbtc8821a1ant_is_common_action(struct btc_coexist *btcoexist) +static bool btc8821a1ant_is_common_action(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; - bool common = false, wifi_connected = false, wifi_busy = false; + bool common = false, wifi_connected = false, wifi_busy = false; btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); @@ -1238,7 +1234,7 @@ static bool halbtc8821a1ant_is_common_action(struct btc_coexist *btcoexist) coex_dm->bt_status) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi non connected-idle + BT non connected-idle!!\n"); - halbtc8821a1ant_sw_mechanism(btcoexist, false); + btc8821a1ant_sw_mechanism(btcoexist, false); common = true; } else if (wifi_connected && @@ -1246,7 +1242,7 @@ static bool halbtc8821a1ant_is_common_action(struct btc_coexist *btcoexist) coex_dm->bt_status)) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi connected + BT non connected-idle!!\n"); - halbtc8821a1ant_sw_mechanism(btcoexist, false); + btc8821a1ant_sw_mechanism(btcoexist, false); common = true; } else if (!wifi_connected && @@ -1254,15 +1250,15 @@ static bool halbtc8821a1ant_is_common_action(struct btc_coexist *btcoexist) coex_dm->bt_status)) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi non connected-idle + BT connected-idle!!\n"); - halbtc8821a1ant_sw_mechanism(btcoexist, false); + btc8821a1ant_sw_mechanism(btcoexist, false); common = true; } else if (wifi_connected && (BT_8821A_1ANT_BT_STATUS_CONNECTED_IDLE == - coex_dm->bt_status)) { + coex_dm->bt_status)) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi connected + BT connected-idle!!\n"); - halbtc8821a1ant_sw_mechanism(btcoexist, false); + btc8821a1ant_sw_mechanism(btcoexist, false); common = true; } else if (!wifi_connected && @@ -1270,7 +1266,7 @@ static bool halbtc8821a1ant_is_common_action(struct btc_coexist *btcoexist) coex_dm->bt_status)) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi non connected-idle + BT Busy!!\n"); - halbtc8821a1ant_sw_mechanism(btcoexist, false); + btc8821a1ant_sw_mechanism(btcoexist, false); common = true; } else { @@ -1291,38 +1287,37 @@ static bool halbtc8821a1ant_is_common_action(struct btc_coexist *btcoexist) static void btc8821a1ant_ps_tdma_check_for_pwr_save(struct btc_coexist *btcoex, bool new_ps_state) { - u8 lps_mode = 0x0; + u8 lps_mode = 0x0; btcoex->btc_get(btcoex, BTC_GET_U1_LPS_MODE, &lps_mode); if (lps_mode) { - /* already under LPS state*/ + /* already under LPS state */ if (new_ps_state) { - /* keep state under LPS, do nothing.*/ + /* keep state under LPS, do nothing */ } else { - /* will leave LPS state, turn off psTdma first*/ - halbtc8821a1ant_ps_tdma(btcoex, NORMAL_EXEC, false, 0); + /* will leave LPS state, turn off psTdma first */ + btc8821a1ant_ps_tdma(btcoex, NORMAL_EXEC, false, 0); } } else { /* NO PS state*/ if (new_ps_state) { - /* will enter LPS state, turn off psTdma first*/ - halbtc8821a1ant_ps_tdma(btcoex, NORMAL_EXEC, false, 0); + /* will enter LPS state, turn off psTdma first */ + btc8821a1ant_ps_tdma(btcoex, NORMAL_EXEC, false, 0); } else { - /* keep state under NO PS state, do nothing.*/ + /* keep state under NO PS state, do nothing */ } } } -static void halbtc8821a1ant_power_save_state(struct btc_coexist *btcoexist, - u8 ps_type, u8 lps_val, - u8 rpwm_val) +static void btc8821a1ant_power_save_state(struct btc_coexist *btcoexist, + u8 ps_type, u8 lps_val, u8 rpwm_val) { bool low_pwr_disable = false; switch (ps_type) { case BTC_PS_WIFI_NATIVE: - /* recover to original 32k low power setting*/ + /* recover to original 32k low power setting */ low_pwr_disable = false; btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); @@ -1331,13 +1326,13 @@ static void halbtc8821a1ant_power_save_state(struct btc_coexist *btcoexist, case BTC_PS_LPS_ON: btc8821a1ant_ps_tdma_check_for_pwr_save(btcoexist, true); - halbtc8821a1ant_lps_rpwm(btcoexist, - NORMAL_EXEC, lps_val, rpwm_val); - /* when coex force to enter LPS, do not enter 32k low power.*/ + btc8821a1ant_lps_rpwm(btcoexist, NORMAL_EXEC, lps_val, + rpwm_val); + /* when coex force to enter LPS, do not enter 32k low power */ low_pwr_disable = true; btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); - /* power save must executed before psTdma.*/ + /* power save must executed before psTdma */ btcoexist->btc_set(btcoexist, BTC_SET_ACT_ENTER_LPS, NULL); break; case BTC_PS_LPS_OFF: @@ -1349,19 +1344,19 @@ static void halbtc8821a1ant_power_save_state(struct btc_coexist *btcoexist, } } -static void halbtc8821a1ant_coex_under_5g(struct btc_coexist *btcoexist) +static void btc8821a1ant_coex_under_5g(struct btc_coexist *btcoexist) { - halbtc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, - 0x0, 0x0); - halbtc8821a1ant_ignore_wlan_act(btcoexist, NORMAL_EXEC, true); + btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + btc8821a1ant_ignore_wlan_act(btcoexist, NORMAL_EXEC, true); - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 10); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 10); - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); - halbtc8821a1ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); + btc8821a1ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); - halbtc8821a1ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 5); + btc8821a1ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 5); } /*********************************************** @@ -1370,74 +1365,74 @@ static void halbtc8821a1ant_coex_under_5g(struct btc_coexist *btcoexist) * ***********************************************/ -/* SCO only or SCO+PAN(HS)*/ -static void halbtc8821a1ant_action_sco(struct btc_coexist *btcoexist) +/* SCO only or SCO+PAN(HS) */ +static void btc8821a1ant_action_sco(struct btc_coexist *btcoexist) { - halbtc8821a1ant_sw_mechanism(btcoexist, true); + btc8821a1ant_sw_mechanism(btcoexist, true); } -static void halbtc8821a1ant_action_hid(struct btc_coexist *btcoexist) +static void btc8821a1ant_action_hid(struct btc_coexist *btcoexist) { - halbtc8821a1ant_sw_mechanism(btcoexist, true); + btc8821a1ant_sw_mechanism(btcoexist, true); } -/*A2DP only / PAN(EDR) only/ A2DP+PAN(HS)*/ -static void halbtc8821a1ant_action_a2dp(struct btc_coexist *btcoexist) +/* A2DP only / PAN(EDR) only/ A2DP+PAN(HS) */ +static void btc8821a1ant_action_a2dp(struct btc_coexist *btcoexist) { - halbtc8821a1ant_sw_mechanism(btcoexist, false); + btc8821a1ant_sw_mechanism(btcoexist, false); } -static void halbtc8821a1ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) +static void btc8821a1ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) { - halbtc8821a1ant_sw_mechanism(btcoexist, false); + btc8821a1ant_sw_mechanism(btcoexist, false); } -static void halbtc8821a1ant_action_pan_edr(struct btc_coexist *btcoexist) +static void btc8821a1ant_action_pan_edr(struct btc_coexist *btcoexist) { - halbtc8821a1ant_sw_mechanism(btcoexist, false); + btc8821a1ant_sw_mechanism(btcoexist, false); } -/*PAN(HS) only*/ -static void halbtc8821a1ant_action_pan_hs(struct btc_coexist *btcoexist) +/* PAN(HS) only */ +static void btc8821a1ant_action_pan_hs(struct btc_coexist *btcoexist) { - halbtc8821a1ant_sw_mechanism(btcoexist, false); + btc8821a1ant_sw_mechanism(btcoexist, false); } -/*PAN(EDR)+A2DP*/ -static void halbtc8821a1ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) +/* PAN(EDR)+A2DP */ +static void btc8821a1ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) { - halbtc8821a1ant_sw_mechanism(btcoexist, false); + btc8821a1ant_sw_mechanism(btcoexist, false); } -static void halbtc8821a1ant_action_pan_edr_hid(struct btc_coexist *btcoexist) +static void btc8821a1ant_action_pan_edr_hid(struct btc_coexist *btcoexist) { - halbtc8821a1ant_sw_mechanism(btcoexist, true); + btc8821a1ant_sw_mechanism(btcoexist, true); } -/* HID+A2DP+PAN(EDR)*/ +/* HID+A2DP+PAN(EDR) */ static void btc8821a1ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) { - halbtc8821a1ant_sw_mechanism(btcoexist, true); + btc8821a1ant_sw_mechanism(btcoexist, true); } -static void halbtc8821a1ant_action_hid_a2dp(struct btc_coexist *btcoexist) +static void btc8821a1ant_action_hid_a2dp(struct btc_coexist *btcoexist) { - halbtc8821a1ant_sw_mechanism(btcoexist, true); + btc8821a1ant_sw_mechanism(btcoexist, true); } -/*=============================================*/ -/**/ -/* Non-Software Coex Mechanism start*/ -/**/ -/*=============================================*/ +/*********************************************** + * + * Non-Software Coex Mechanism start + * + ***********************************************/ -static void halbtc8821a1ant_action_hs(struct btc_coexist *btcoexist) +static void btc8821a1ant_action_hs(struct btc_coexist *btcoexist) { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); - halbtc8821a1ant_coex_table_with_type(btcoexist, FORCE_EXEC, 2); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); + btc8821a1ant_coex_table_with_type(btcoexist, FORCE_EXEC, 2); } -static void halbtc8821a1ant_action_bt_inquiry(struct btc_coexist *btcoexist) +static void btc8821a1ant_action_bt_inquiry(struct btc_coexist *btcoexist) { struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; bool wifi_connected = false; @@ -1446,133 +1441,136 @@ static void halbtc8821a1ant_action_bt_inquiry(struct btc_coexist *btcoexist) BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); if (!wifi_connected) { - halbtc8821a1ant_power_save_state(btcoexist, - BTC_PS_WIFI_NATIVE, 0x0, 0x0); - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + btc8821a1ant_power_save_state(btcoexist, + BTC_PS_WIFI_NATIVE, 0x0, 0x0); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); } else if ((bt_link_info->sco_exist) || (bt_link_info->hid_only)) { - /* SCO/HID-only busy*/ - halbtc8821a1ant_power_save_state(btcoexist, - BTC_PS_WIFI_NATIVE, 0x0, 0x0); - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 32); - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + /* SCO/HID-only busy */ + btc8821a1ant_power_save_state(btcoexist, + BTC_PS_WIFI_NATIVE, 0x0, 0x0); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 32); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); } else { - halbtc8821a1ant_power_save_state(btcoexist, BTC_PS_LPS_ON, - 0x50, 0x4); - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 30); - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + btc8821a1ant_power_save_state(btcoexist, BTC_PS_LPS_ON, + 0x50, 0x4); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 30); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); } } static void btc8821a1ant_act_bt_sco_hid_only_busy(struct btc_coexist *btcoexist, u8 wifi_status) { - /* tdma and coex table*/ - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); + /* tdma and coex table */ + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + if (BT_8821A_1ANT_WIFI_STATUS_NON_CONNECTED_ASSO_AUTH_SCAN == + wifi_status) + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + else + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); } static void btc8821a1ant_act_wifi_con_bt_acl_busy(struct btc_coexist *btcoexist, u8 wifi_status) { - u8 bt_rssi_state; + u8 bt_rssi_state; struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; - bt_rssi_state = halbtc8821a1ant_bt_rssi_state(btcoexist, 2, 28, 0); + bt_rssi_state = btc8821a1ant_bt_rssi_state(btcoexist, 2, 28, 0); if (bt_link_info->hid_only) { - /*HID*/ + /* HID */ btc8821a1ant_act_bt_sco_hid_only_busy(btcoexist, wifi_status); coex_dm->auto_tdma_adjust = false; return; } else if (bt_link_info->a2dp_only) { - /*A2DP*/ + /* A2DP */ if ((bt_rssi_state != BTC_RSSI_STATE_HIGH) && (bt_rssi_state != BTC_RSSI_STATE_STAY_HIGH)) { - /*for low BT RSSI*/ - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); + /* for low BT RSSI */ + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 11); coex_dm->auto_tdma_adjust = false; } - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); } else if (bt_link_info->hid_exist && bt_link_info->a2dp_exist) { - /*HID+A2DP*/ + /* HID+A2DP */ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 14); coex_dm->auto_tdma_adjust = false; } else { /*for low BT RSSI*/ - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 11); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 11); coex_dm->auto_tdma_adjust = false; } - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); } else if ((bt_link_info->pan_only) || (bt_link_info->hid_exist && bt_link_info->pan_exist)) { - /*PAN(OPP, FTP), HID+PAN(OPP, FTP)*/ - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + /* PAN(OPP, FTP), HID+PAN(OPP, FTP) */ + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); coex_dm->auto_tdma_adjust = false; } else if (((bt_link_info->a2dp_exist) && (bt_link_info->pan_exist)) || (bt_link_info->hid_exist && bt_link_info->a2dp_exist && bt_link_info->pan_exist)) { - /*A2DP+PAN(OPP, FTP), HID+A2DP+PAN(OPP, FTP)*/ - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 13); - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + /* A2DP+PAN(OPP, FTP), HID+A2DP+PAN(OPP, FTP) */ + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 13); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); coex_dm->auto_tdma_adjust = false; } else { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); coex_dm->auto_tdma_adjust = false; } } -static void halbtc8821a1ant_action_wifi_not_connected( - struct btc_coexist *btcoexist) +static +void btc8821a1ant_action_wifi_not_connected(struct btc_coexist *btcoexist) { - /* power save state*/ - halbtc8821a1ant_power_save_state(btcoexist, - BTC_PS_WIFI_NATIVE, 0x0, 0x0); + /* power save state */ + btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); - /* tdma and coex table*/ - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); + /* tdma and coex table */ + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); } static void btc8821a1ant_act_wifi_not_conn_scan(struct btc_coexist *btcoexist) { - halbtc8821a1ant_power_save_state(btcoexist, - BTC_PS_WIFI_NATIVE, 0x0, 0x0); + btc8821a1ant_power_save_state(btcoexist, + BTC_PS_WIFI_NATIVE, 0x0, 0x0); - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); } -static void halbtc8821a1ant_action_wifi_connected_scan( - struct btc_coexist *btcoexist) { +static +void btc8821a1ant_action_wifi_connected_scan(struct btc_coexist *btcoexist) +{ struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; - /* power save state*/ - halbtc8821a1ant_power_save_state(btcoexist, - BTC_PS_WIFI_NATIVE, 0x0, 0x0); + /* power save state */ + btc8821a1ant_power_save_state(btcoexist, + BTC_PS_WIFI_NATIVE, 0x0, 0x0); - /* tdma and coex table*/ + /* tdma and coex table */ if (BT_8821A_1ANT_BT_STATUS_ACL_BUSY == coex_dm->bt_status) { if (bt_link_info->a2dp_exist && bt_link_info->pan_exist) { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 22); - halbtc8821a1ant_coex_table_with_type(btcoexist, - NORMAL_EXEC, 1); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); + btc8821a1ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 1); } else { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 20); - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 20); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); } } else if ((BT_8821A_1ANT_BT_STATUS_SCO_BUSY == coex_dm->bt_status) || @@ -1581,52 +1579,52 @@ static void halbtc8821a1ant_action_wifi_connected_scan( btc8821a1ant_act_bt_sco_hid_only_busy(btcoexist, BT_8821A_1ANT_WIFI_STATUS_CONNECTED_SCAN); } else { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 20); - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 20); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); } } static void btc8821a1ant_act_wifi_conn_sp_pkt(struct btc_coexist *btcoexist) { struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; - bool hs_connecting = false; + bool hs_connecting = false; btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_CONNECTING, &hs_connecting); - halbtc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, - 0x0, 0x0); + btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); - /* tdma and coex table*/ - if (BT_8821A_1ANT_BT_STATUS_ACL_BUSY == coex_dm->bt_status) { + /* tdma and coex table */ + if (coex_dm->bt_status == BT_8821A_1ANT_BT_STATUS_ACL_BUSY) { if (bt_link_info->a2dp_exist && bt_link_info->pan_exist) { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 22); - halbtc8821a1ant_coex_table_with_type(btcoexist, - NORMAL_EXEC, 1); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 22); + btc8821a1ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 1); } else { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 20); - halbtc8821a1ant_coex_table_with_type(btcoexist, - NORMAL_EXEC, 1); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 20); + btc8821a1ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 1); } } else { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 20); - halbtc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 20); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); } } -static void halbtc8821a1ant_action_wifi_connected(struct btc_coexist *btcoexist) +static void btc8821a1ant_action_wifi_connected(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; - bool wifi_busy = false; - bool scan = false, link = false, roam = false; - bool under_4way = false; + bool wifi_busy = false; + bool scan = false, link = false, roam = false; + bool under_4way = false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], CoexForWifiConnect()===>\n"); - btcoexist->btc_get(btcoexist, - BTC_GET_BL_WIFI_4_WAY_PROGRESS, &under_4way); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_4_WAY_PROGRESS, + &under_4way); if (under_4way) { btc8821a1ant_act_wifi_conn_sp_pkt(btcoexist); RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -1638,7 +1636,7 @@ static void halbtc8821a1ant_action_wifi_connected(struct btc_coexist *btcoexist) btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_ROAM, &roam); if (scan || link || roam) { - halbtc8821a1ant_action_wifi_connected_scan(btcoexist); + btc8821a1ant_action_wifi_connected_scan(btcoexist); RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], CoexForWifiConnect(), return for wifi is under scan<===\n"); return; @@ -1647,14 +1645,14 @@ static void halbtc8821a1ant_action_wifi_connected(struct btc_coexist *btcoexist) /* power save state*/ if (BT_8821A_1ANT_BT_STATUS_ACL_BUSY == coex_dm->bt_status && !btcoexist->bt_link_info.hid_only) - halbtc8821a1ant_power_save_state(btcoexist, - BTC_PS_LPS_ON, 0x50, 0x4); + btc8821a1ant_power_save_state(btcoexist, + BTC_PS_LPS_ON, 0x50, 0x4); else - halbtc8821a1ant_power_save_state(btcoexist, - BTC_PS_WIFI_NATIVE, - 0x0, 0x0); + btc8821a1ant_power_save_state(btcoexist, + BTC_PS_WIFI_NATIVE, + 0x0, 0x0); - /* tdma and coex table*/ + /* tdma and coex table */ btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy); if (!wifi_busy) { if (BT_8821A_1ANT_BT_STATUS_ACL_BUSY == coex_dm->bt_status) { @@ -1667,10 +1665,10 @@ static void halbtc8821a1ant_action_wifi_connected(struct btc_coexist *btcoexist) btc8821a1ant_act_bt_sco_hid_only_busy(btcoexist, BT_8821A_1ANT_WIFI_STATUS_CONNECTED_IDLE); } else { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); - halbtc8821a1ant_coex_table_with_type(btcoexist, - NORMAL_EXEC, 2); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 5); + btc8821a1ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 2); } } else { if (BT_8821A_1ANT_BT_STATUS_ACL_BUSY == coex_dm->bt_status) { @@ -1683,10 +1681,9 @@ static void halbtc8821a1ant_action_wifi_connected(struct btc_coexist *btcoexist) btc8821a1ant_act_bt_sco_hid_only_busy(btcoexist, BT_8821A_1ANT_WIFI_STATUS_CONNECTED_BUSY); } else { - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); - halbtc8821a1ant_coex_table_with_type(btcoexist, - NORMAL_EXEC, 2); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); + btc8821a1ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 2); } } } @@ -1694,52 +1691,52 @@ static void halbtc8821a1ant_action_wifi_connected(struct btc_coexist *btcoexist) static void btc8821a1ant_run_sw_coex_mech(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; - u8 algorithm = 0; + u8 algorithm = 0; - algorithm = halbtc8821a1ant_action_algorithm(btcoexist); + algorithm = btc8821a1ant_action_algorithm(btcoexist); coex_dm->cur_algorithm = algorithm; - if (!halbtc8821a1ant_is_common_action(btcoexist)) { + if (!btc8821a1ant_is_common_action(btcoexist)) { switch (coex_dm->cur_algorithm) { case BT_8821A_1ANT_COEX_ALGO_SCO: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action algorithm = SCO\n"); - halbtc8821a1ant_action_sco(btcoexist); + btc8821a1ant_action_sco(btcoexist); break; case BT_8821A_1ANT_COEX_ALGO_HID: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action algorithm = HID\n"); - halbtc8821a1ant_action_hid(btcoexist); + btc8821a1ant_action_hid(btcoexist); break; case BT_8821A_1ANT_COEX_ALGO_A2DP: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action algorithm = A2DP\n"); - halbtc8821a1ant_action_a2dp(btcoexist); + btc8821a1ant_action_a2dp(btcoexist); break; case BT_8821A_1ANT_COEX_ALGO_A2DP_PANHS: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action algorithm = A2DP+PAN(HS)\n"); - halbtc8821a1ant_action_a2dp_pan_hs(btcoexist); + btc8821a1ant_action_a2dp_pan_hs(btcoexist); break; case BT_8821A_1ANT_COEX_ALGO_PANEDR: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action algorithm = PAN(EDR)\n"); - halbtc8821a1ant_action_pan_edr(btcoexist); + btc8821a1ant_action_pan_edr(btcoexist); break; case BT_8821A_1ANT_COEX_ALGO_PANHS: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action algorithm = HS mode\n"); - halbtc8821a1ant_action_pan_hs(btcoexist); + btc8821a1ant_action_pan_hs(btcoexist); break; case BT_8821A_1ANT_COEX_ALGO_PANEDR_A2DP: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action algorithm = PAN+A2DP\n"); - halbtc8821a1ant_action_pan_edr_a2dp(btcoexist); + btc8821a1ant_action_pan_edr_a2dp(btcoexist); break; case BT_8821A_1ANT_COEX_ALGO_PANEDR_HID: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action algorithm = PAN(EDR)+HID\n"); - halbtc8821a1ant_action_pan_edr_hid(btcoexist); + btc8821a1ant_action_pan_edr_hid(btcoexist); break; case BT_8821A_1ANT_COEX_ALGO_HID_A2DP_PANEDR: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -1749,28 +1746,28 @@ static void btc8821a1ant_run_sw_coex_mech(struct btc_coexist *btcoexist) case BT_8821A_1ANT_COEX_ALGO_HID_A2DP: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action algorithm = HID+A2DP\n"); - halbtc8821a1ant_action_hid_a2dp(btcoexist); + btc8821a1ant_action_hid_a2dp(btcoexist); break; default: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action algorithm = coexist All Off!!\n"); - /*halbtc8821a1ant_coex_all_off(btcoexist);*/ + /*btc8821a1ant_coex_all_off(btcoexist);*/ break; } coex_dm->pre_algorithm = coex_dm->cur_algorithm; } } -static void halbtc8821a1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) +static void btc8821a1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; - bool wifi_connected = false, bt_hs_on = false; - bool increase_scan_dev_num = false; - bool bt_ctrl_agg_buf_size = false; - u8 agg_buf_size = 5; - u8 wifi_rssi_state = BTC_RSSI_STATE_HIGH; - bool wifi_under_5g = false; + bool wifi_connected = false, bt_hs_on = false; + bool increase_scan_dev_num = false; + bool bt_ctrl_agg_buf_size = false; + u8 agg_buf_size = 5; + u8 wifi_rssi_state = BTC_RSSI_STATE_HIGH; + bool wifi_under_5g = false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], RunCoexistMechanism()===>\n"); @@ -1797,7 +1794,7 @@ static void halbtc8821a1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) if (wifi_under_5g) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], RunCoexistMechanism(), return for 5G <===\n"); - halbtc8821a1ant_coex_under_5g(btcoexist); + btc8821a1ant_coex_under_5g(btcoexist); return; } @@ -1809,21 +1806,29 @@ static void halbtc8821a1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) btcoexist->btc_set(btcoexist, BTC_SET_BL_INC_SCAN_DEV_NUM, &increase_scan_dev_num); - btcoexist->btc_get(btcoexist, - BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, + &wifi_connected); if (!bt_link_info->sco_exist && !bt_link_info->hid_exist) { - halbtc8821a1ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); + btc8821a1ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); } else { if (wifi_connected) { wifi_rssi_state = - halbtc8821a1ant_WifiRssiState(btcoexist, 1, 2, - 30, 0); - halbtc8821a1ant_limited_tx(btcoexist, - NORMAL_EXEC, 1, 1, 1, 1); + btc8821a1ant_wifi_rssi_state(btcoexist, 1, 2, + 30, 0); + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8821a1ant_limited_tx(btcoexist, + NORMAL_EXEC, 1, 1, + 1, 1); + } else { + btc8821a1ant_limited_tx(btcoexist, + NORMAL_EXEC, 1, 1, + 1, 1); + } } else { - halbtc8821a1ant_limited_tx(btcoexist, NORMAL_EXEC, - 0, 0, 0, 0); + btc8821a1ant_limited_tx(btcoexist, NORMAL_EXEC, + 0, 0, 0, 0); } } @@ -1837,22 +1842,22 @@ static void halbtc8821a1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) bt_ctrl_agg_buf_size = true; agg_buf_size = 0x8; } - halbtc8821a1ant_limited_rx(btcoexist, NORMAL_EXEC, false, - bt_ctrl_agg_buf_size, agg_buf_size); + btc8821a1ant_limited_rx(btcoexist, NORMAL_EXEC, false, + bt_ctrl_agg_buf_size, agg_buf_size); btc8821a1ant_run_sw_coex_mech(btcoexist); btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); if (coex_sta->c2h_bt_inquiry_page) { - halbtc8821a1ant_action_bt_inquiry(btcoexist); + btc8821a1ant_action_bt_inquiry(btcoexist); return; } else if (bt_hs_on) { - halbtc8821a1ant_action_hs(btcoexist); + btc8821a1ant_action_hs(btcoexist); return; } if (!wifi_connected) { - bool scan = false, link = false, roam = false; + bool scan = false, link = false, roam = false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], wifi is non connected-idle !!!\n"); @@ -1864,29 +1869,30 @@ static void halbtc8821a1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) if (scan || link || roam) btc8821a1ant_act_wifi_not_conn_scan(btcoexist); else - halbtc8821a1ant_action_wifi_not_connected(btcoexist); + btc8821a1ant_action_wifi_not_connected(btcoexist); } else { - /* wifi LPS/Busy*/ - halbtc8821a1ant_action_wifi_connected(btcoexist); + /* wifi LPS/Busy */ + btc8821a1ant_action_wifi_connected(btcoexist); } } -static void halbtc8821a1ant_init_coex_dm(struct btc_coexist *btcoexist) +static void btc8821a1ant_init_coex_dm(struct btc_coexist *btcoexist) { - /* force to reset coex mechanism*/ - /* sw all off*/ - halbtc8821a1ant_sw_mechanism(btcoexist, false); + /* force to reset coex mechanism + * sw all off + */ + btc8821a1ant_sw_mechanism(btcoexist, false); - halbtc8821a1ant_ps_tdma(btcoexist, FORCE_EXEC, false, 8); - halbtc8821a1ant_coex_table_with_type(btcoexist, FORCE_EXEC, 0); + btc8821a1ant_ps_tdma(btcoexist, FORCE_EXEC, false, 8); + btc8821a1ant_coex_table_with_type(btcoexist, FORCE_EXEC, 0); } -static void halbtc8821a1ant_init_hw_config(struct btc_coexist *btcoexist, - bool back_up) +static void btc8821a1ant_init_hw_config(struct btc_coexist *btcoexist, + bool back_up) { struct rtl_priv *rtlpriv = btcoexist->adapter; - u8 u1_tmp = 0; - bool wifi_under_5g = false; + u8 u1_tmp = 0; + bool wifi_under_5g = false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], 1Ant Init HW Config!!\n"); @@ -1897,12 +1903,12 @@ static void halbtc8821a1ant_init_hw_config(struct btc_coexist *btcoexist, coex_dm->backup_arfr_cnt2 = btcoexist->btc_read_4byte(btcoexist, 0x434); coex_dm->backup_retry_limit = - btcoexist->btc_read_2byte(btcoexist, 0x42a); + btcoexist->btc_read_2byte(btcoexist, 0x42a); coex_dm->backup_ampdu_max_time = - btcoexist->btc_read_1byte(btcoexist, 0x456); + btcoexist->btc_read_1byte(btcoexist, 0x456); } - /* 0x790[5:0] = 0x5*/ + /* 0x790[5:0] = 0x5 */ u1_tmp = btcoexist->btc_read_1byte(btcoexist, 0x790); u1_tmp &= 0xc0; u1_tmp |= 0x5; @@ -1910,35 +1916,33 @@ static void halbtc8821a1ant_init_hw_config(struct btc_coexist *btcoexist, btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g); - /*Antenna config*/ + /* Antenna config */ if (wifi_under_5g) - halbtc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_BT, - true, false); + btc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_BT, + true, false); else - halbtc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_PTA, - true, false); - /* PTA parameter*/ - halbtc8821a1ant_coex_table_with_type(btcoexist, FORCE_EXEC, 0); - - /* Enable counter statistics*/ - /*0x76e[3] =1, WLAN_Act control by PTA*/ + btc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_PTA, + true, false); + /* PTA parameter */ + btc8821a1ant_coex_table_with_type(btcoexist, FORCE_EXEC, 0); + + /* Enable counter statistics + * 0x76e[3] =1, WLAN_Act control by PTA + */ btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc); btcoexist->btc_write_1byte(btcoexist, 0x778, 0x3); btcoexist->btc_write_1byte_bitmask(btcoexist, 0x40, 0x20, 0x1); } -/*============================================================*/ -/* work around function start with wa_halbtc8821a1ant_*/ -/*============================================================*/ -/*============================================================*/ -/* extern function start with EXhalbtc8821a1ant_*/ -/*============================================================*/ -void ex_halbtc8821a1ant_init_hwconfig(struct btc_coexist *btcoexist) +/************************************************************** + * extern function start with ex_btc8821a1ant_ + **************************************************************/ +void ex_btc8821a1ant_init_hwconfig(struct btc_coexist *btcoexist) { - halbtc8821a1ant_init_hw_config(btcoexist, true); + btc8821a1ant_init_hw_config(btcoexist, true); } -void ex_halbtc8821a1ant_init_coex_dm(struct btc_coexist *btcoexist) +void ex_btc8821a1ant_init_coex_dm(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -1947,12 +1951,12 @@ void ex_halbtc8821a1ant_init_coex_dm(struct btc_coexist *btcoexist) btcoexist->stop_coex_dm = false; - halbtc8821a1ant_init_coex_dm(btcoexist); + btc8821a1ant_init_coex_dm(btcoexist); - halbtc8821a1ant_query_bt_info(btcoexist); + btc8821a1ant_query_bt_info(btcoexist); } -void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist) +void ex_btc8821a1ant_display_coex_info(struct btc_coexist *btcoexist) { struct btc_board_info *board_info = &btcoexist->board_info; struct btc_stack_info *stack_info = &btcoexist->stack_info; @@ -2097,7 +2101,7 @@ void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist) "\r\n %-35s = %s/%s, (0x%x/0x%x)", "PS state, IPS/LPS, (lps/rpwm)", ((coex_sta->under_ips ? "IPS ON" : "IPS OFF")), - ((coex_sta->under_Lps ? "LPS ON" : "LPS OFF")), + ((coex_sta->under_lps ? "LPS ON" : "LPS OFF")), btcoexist->bt_info.lps_val, btcoexist->bt_info.rpwm_val); btcoexist->btc_disp_dbg_msg(btcoexist, BTC_DBG_DISP_FW_PWR_MODE_CMD); @@ -2122,7 +2126,7 @@ void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist) "\r\n %-35s = 0x%x ", "Rate Mask", btcoexist->bt_info.ra_mask); - /* Fw mechanism*/ + /* Fw mechanism */ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s", "============[Fw mechanism]============"); @@ -2144,7 +2148,7 @@ void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist) coex_dm->cur_ignore_wlan_act); } - /* Hw setting*/ + /* Hw setting */ RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s", "============[Hw setting]============"); @@ -2227,12 +2231,12 @@ void ex_halbtc8821a1ant_display_coex_info(struct btc_coexist *btcoexist) "\r\n %-35s = %d/ %d", "0x774(low-pri rx/tx)", coex_sta->low_priority_rx, coex_sta->low_priority_tx); #if (BT_AUTO_REPORT_ONLY_8821A_1ANT == 1) - halbtc8821a1ant_monitor_bt_ctr(btcoexist); + btc8821a1ant_monitor_bt_ctr(btcoexist); #endif btcoexist->btc_disp_dbg_msg(btcoexist, BTC_DBG_DISP_COEX_STATISTICS); } -void ex_halbtc8821a1ant_ips_notify(struct btc_coexist *btcoexist, u8 type) +void ex_btc8821a1ant_ips_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2243,22 +2247,22 @@ void ex_halbtc8821a1ant_ips_notify(struct btc_coexist *btcoexist, u8 type) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], IPS ENTER notify\n"); coex_sta->under_ips = true; - halbtc8821a1ant_set_ant_path(btcoexist, - BTC_ANT_PATH_BT, false, true); - /*set PTA control*/ - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); - halbtc8821a1ant_coex_table_with_type(btcoexist, - NORMAL_EXEC, 0); + btc8821a1ant_set_ant_path(btcoexist, + BTC_ANT_PATH_BT, false, true); + /* set PTA control */ + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); + btc8821a1ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 0); } else if (BTC_IPS_LEAVE == type) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], IPS LEAVE notify\n"); coex_sta->under_ips = false; - halbtc8821a1ant_run_coexist_mechanism(btcoexist); + btc8821a1ant_run_coexist_mechanism(btcoexist); } } -void ex_halbtc8821a1ant_lps_notify(struct btc_coexist *btcoexist, u8 type) +void ex_btc8821a1ant_lps_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2268,15 +2272,15 @@ void ex_halbtc8821a1ant_lps_notify(struct btc_coexist *btcoexist, u8 type) if (BTC_LPS_ENABLE == type) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], LPS ENABLE notify\n"); - coex_sta->under_Lps = true; + coex_sta->under_lps = true; } else if (BTC_LPS_DISABLE == type) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], LPS DISABLE notify\n"); - coex_sta->under_Lps = false; + coex_sta->under_lps = false; } } -void ex_halbtc8821a1ant_scan_notify(struct btc_coexist *btcoexist, u8 type) +void ex_btc8821a1ant_scan_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; bool wifi_connected = false, bt_hs_on = false; @@ -2291,13 +2295,13 @@ void ex_halbtc8821a1ant_scan_notify(struct btc_coexist *btcoexist, u8 type) btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); - halbtc8821a1ant_query_bt_info(btcoexist); + btc8821a1ant_query_bt_info(btcoexist); if (coex_sta->c2h_bt_inquiry_page) { - halbtc8821a1ant_action_bt_inquiry(btcoexist); + btc8821a1ant_action_bt_inquiry(btcoexist); return; } else if (bt_hs_on) { - halbtc8821a1ant_action_hs(btcoexist); + btc8821a1ant_action_hs(btcoexist); return; } @@ -2305,25 +2309,25 @@ void ex_halbtc8821a1ant_scan_notify(struct btc_coexist *btcoexist, u8 type) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCAN START notify\n"); if (!wifi_connected) { - /* non-connected scan*/ + /* non-connected scan */ btc8821a1ant_act_wifi_not_conn_scan(btcoexist); } else { - /* wifi is connected*/ - halbtc8821a1ant_action_wifi_connected_scan(btcoexist); + /* wifi is connected */ + btc8821a1ant_action_wifi_connected_scan(btcoexist); } } else if (BTC_SCAN_FINISH == type) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCAN FINISH notify\n"); if (!wifi_connected) { - /* non-connected scan*/ - halbtc8821a1ant_action_wifi_not_connected(btcoexist); + /* non-connected scan */ + btc8821a1ant_action_wifi_not_connected(btcoexist); } else { - halbtc8821a1ant_action_wifi_connected(btcoexist); + btc8821a1ant_action_wifi_connected(btcoexist); } } } -void ex_halbtc8821a1ant_connect_notify(struct btc_coexist *btcoexist, u8 type) +void ex_btc8821a1ant_connect_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; bool wifi_connected = false, bt_hs_on = false; @@ -2335,10 +2339,10 @@ void ex_halbtc8821a1ant_connect_notify(struct btc_coexist *btcoexist, u8 type) btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); if (coex_sta->c2h_bt_inquiry_page) { - halbtc8821a1ant_action_bt_inquiry(btcoexist); + btc8821a1ant_action_bt_inquiry(btcoexist); return; } else if (bt_hs_on) { - halbtc8821a1ant_action_hs(btcoexist); + btc8821a1ant_action_hs(btcoexist); return; } @@ -2353,16 +2357,16 @@ void ex_halbtc8821a1ant_connect_notify(struct btc_coexist *btcoexist, u8 type) btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); if (!wifi_connected) { - /* non-connected scan*/ - halbtc8821a1ant_action_wifi_not_connected(btcoexist); + /* non-connected scan */ + btc8821a1ant_action_wifi_not_connected(btcoexist); } else { - halbtc8821a1ant_action_wifi_connected(btcoexist); + btc8821a1ant_action_wifi_connected(btcoexist); } } } -void ex_halbtc8821a1ant_media_status_notify(struct btc_coexist *btcoexist, - u8 type) +void ex_btc8821a1ant_media_status_notify(struct btc_coexist *btcoexist, + u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[3] = {0}; @@ -2382,17 +2386,16 @@ void ex_halbtc8821a1ant_media_status_notify(struct btc_coexist *btcoexist, "[BTCoex], MEDIA disconnect notify\n"); } - /* only 2.4G we need to inform bt the chnl mask*/ + /* only 2.4G we need to inform bt the chnl mask */ btcoexist->btc_get(btcoexist, BTC_GET_U1_WIFI_CENTRAL_CHNL, &wifi_central_chnl); - if ((BTC_MEDIA_CONNECT == type) && + if ((type == BTC_MEDIA_CONNECT) && (wifi_central_chnl <= 14)) { - /*h2c_parameter[0] = 0x1;*/ h2c_parameter[0] = 0x0; h2c_parameter[1] = wifi_central_chnl; btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - if (BTC_WIFI_BW_HT40 == wifi_bw) + if (wifi_bw == BTC_WIFI_BW_HT40) h2c_parameter[2] = 0x30; else h2c_parameter[2] = 0x20; @@ -2411,8 +2414,8 @@ void ex_halbtc8821a1ant_media_status_notify(struct btc_coexist *btcoexist, btcoexist->btc_fill_h2c(btcoexist, 0x66, 3, h2c_parameter); } -void ex_halbtc8821a1ant_special_packet_notify(struct btc_coexist *btcoexist, - u8 type) +void ex_btc8821a1ant_special_packet_notify(struct btc_coexist *btcoexist, + u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; bool bt_hs_on = false; @@ -2426,10 +2429,10 @@ void ex_halbtc8821a1ant_special_packet_notify(struct btc_coexist *btcoexist, btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); if (coex_sta->c2h_bt_inquiry_page) { - halbtc8821a1ant_action_bt_inquiry(btcoexist); + btc8821a1ant_action_bt_inquiry(btcoexist); return; } else if (bt_hs_on) { - halbtc8821a1ant_action_hs(btcoexist); + btc8821a1ant_action_hs(btcoexist); return; } @@ -2441,12 +2444,13 @@ void ex_halbtc8821a1ant_special_packet_notify(struct btc_coexist *btcoexist, } } -void ex_halbtc8821a1ant_bt_info_notify(struct btc_coexist *btcoexist, - u8 *tmp_buf, u8 length) +void ex_btc8821a1ant_bt_info_notify(struct btc_coexist *btcoexist, + u8 *tmp_buf, u8 length) { struct rtl_priv *rtlpriv = btcoexist->adapter; + u8 i; u8 bt_info = 0; - u8 i, rsp_source = 0; + u8 rsp_source = 0; bool wifi_connected = false; bool bt_busy = false; bool wifi_under_5g = false; @@ -2456,7 +2460,7 @@ void ex_halbtc8821a1ant_bt_info_notify(struct btc_coexist *btcoexist, btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g); - rsp_source = tmp_buf[0]&0xf; + rsp_source = tmp_buf[0] & 0xf; if (rsp_source >= BT_INFO_SRC_8821A_1ANT_MAX) rsp_source = BT_INFO_SRC_8821A_1ANT_WIFI_FW; coex_sta->bt_info_c2h_cnt[rsp_source]++; @@ -2468,7 +2472,7 @@ void ex_halbtc8821a1ant_bt_info_notify(struct btc_coexist *btcoexist, coex_sta->bt_info_c2h[rsp_source][i] = tmp_buf[i]; if (i == 1) bt_info = tmp_buf[i]; - if (i == length-1) { + if (i == length - 1) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "0x%02x]\n", tmp_buf[i]); } else { @@ -2487,19 +2491,19 @@ void ex_halbtc8821a1ant_bt_info_notify(struct btc_coexist *btcoexist, coex_sta->bt_info_ext = coex_sta->bt_info_c2h[rsp_source][4]; - /* Here we need to resend some wifi info to BT*/ - /* because bt is reset and loss of the info.*/ + /* Here we need to resend some wifi info to BT + * because bt is reset and lost the info + */ if (coex_sta->bt_info_ext & BIT1) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], BT ext info bit1 check, send wifi BW&Chnl to BT!!\n"); - btcoexist->btc_get(btcoexist, - BTC_GET_BL_WIFI_CONNECTED, + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); if (wifi_connected) { - ex_halbtc8821a1ant_media_status_notify(btcoexist, + ex_btc8821a1ant_media_status_notify(btcoexist, BTC_MEDIA_CONNECT); } else { - ex_halbtc8821a1ant_media_status_notify(btcoexist, + ex_btc8821a1ant_media_status_notify(btcoexist, BTC_MEDIA_DISCONNECT); } } @@ -2509,28 +2513,28 @@ void ex_halbtc8821a1ant_bt_info_notify(struct btc_coexist *btcoexist, !btcoexist->stop_coex_dm) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], BT ext info bit3 check, set BT NOT to ignore Wlan active!!\n"); - halbtc8821a1ant_ignore_wlan_act(btcoexist, - FORCE_EXEC, - false); + btc8821a1ant_ignore_wlan_act(btcoexist, + FORCE_EXEC, + false); } } } - /* check BIT2 first ==> check if bt is under inquiry or page scan*/ + /* check BIT2 first ==> check if bt is under inquiry or page scan */ if (bt_info & BT_INFO_8821A_1ANT_B_INQ_PAGE) coex_sta->c2h_bt_inquiry_page = true; else coex_sta->c2h_bt_inquiry_page = false; - /* set link exist status*/ - if (!(bt_info&BT_INFO_8821A_1ANT_B_CONNECTION)) { + /* set link exist status */ + if (!(bt_info & BT_INFO_8821A_1ANT_B_CONNECTION)) { coex_sta->bt_link_exist = false; coex_sta->pan_exist = false; coex_sta->a2dp_exist = false; coex_sta->hid_exist = false; coex_sta->sco_exist = false; } else { - /* connection exists*/ + /* connection exists */ coex_sta->bt_link_exist = true; if (bt_info & BT_INFO_8821A_1ANT_B_FTP) coex_sta->pan_exist = true; @@ -2550,14 +2554,14 @@ void ex_halbtc8821a1ant_bt_info_notify(struct btc_coexist *btcoexist, coex_sta->sco_exist = false; } - halbtc8821a1ant_update_bt_link_info(btcoexist); + btc8821a1ant_update_bt_link_info(btcoexist); if (!(bt_info&BT_INFO_8821A_1ANT_B_CONNECTION)) { coex_dm->bt_status = BT_8821A_1ANT_BT_STATUS_NON_CONNECTED_IDLE; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], BtInfoNotify(), BT Non-Connected idle!!!\n"); } else if (bt_info == BT_INFO_8821A_1ANT_B_CONNECTION) { - /* connection exists but no busy*/ + /* connection exists but no busy */ coex_dm->bt_status = BT_8821A_1ANT_BT_STATUS_CONNECTED_IDLE; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], BtInfoNotify(), BT Connected-idle!!!\n"); @@ -2587,10 +2591,10 @@ void ex_halbtc8821a1ant_bt_info_notify(struct btc_coexist *btcoexist, btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_TRAFFIC_BUSY, &bt_busy); - halbtc8821a1ant_run_coexist_mechanism(btcoexist); + btc8821a1ant_run_coexist_mechanism(btcoexist); } -void ex_halbtc8821a1ant_halt_notify(struct btc_coexist *btcoexist) +void ex_btc8821a1ant_halt_notify(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2599,19 +2603,16 @@ void ex_halbtc8821a1ant_halt_notify(struct btc_coexist *btcoexist) btcoexist->stop_coex_dm = true; - halbtc8821a1ant_set_ant_path(btcoexist, - BTC_ANT_PATH_BT, false, true); - halbtc8821a1ant_ignore_wlan_act(btcoexist, FORCE_EXEC, true); + btc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_BT, false, true); + btc8821a1ant_ignore_wlan_act(btcoexist, FORCE_EXEC, true); - halbtc8821a1ant_power_save_state(btcoexist, - BTC_PS_WIFI_NATIVE, 0x0, 0x0); - halbtc8821a1ant_ps_tdma(btcoexist, FORCE_EXEC, false, 0); + btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); + btc8821a1ant_ps_tdma(btcoexist, FORCE_EXEC, false, 0); - ex_halbtc8821a1ant_media_status_notify(btcoexist, - BTC_MEDIA_DISCONNECT); + ex_btc8821a1ant_media_status_notify(btcoexist, BTC_MEDIA_DISCONNECT); } -void ex_halbtc8821a1ant_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state) +void ex_btc8821a1ant_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2622,25 +2623,25 @@ void ex_halbtc8821a1ant_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Pnp notify to SLEEP\n"); btcoexist->stop_coex_dm = true; - halbtc8821a1ant_ignore_wlan_act(btcoexist, FORCE_EXEC, true); - halbtc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, - 0x0, 0x0); - halbtc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 9); + btc8821a1ant_ignore_wlan_act(btcoexist, FORCE_EXEC, true); + btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 9); } else if (BTC_WIFI_PNP_WAKE_UP == pnp_state) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Pnp notify to WAKE UP\n"); btcoexist->stop_coex_dm = false; - halbtc8821a1ant_init_hw_config(btcoexist, false); - halbtc8821a1ant_init_coex_dm(btcoexist); - halbtc8821a1ant_query_bt_info(btcoexist); + btc8821a1ant_init_hw_config(btcoexist, false); + btc8821a1ant_init_coex_dm(btcoexist); + btc8821a1ant_query_bt_info(btcoexist); } } -void ex_halbtc8821a1ant_periodical(struct btc_coexist *btcoexist) +void ex_btc8821a1ant_periodical(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; - static u8 dis_ver_info_cnt; - u32 fw_ver = 0, bt_patch_ver = 0; + static u8 dis_ver_info_cnt; + u32 fw_ver = 0, bt_patch_ver = 0; struct btc_board_info *board_info = &btcoexist->board_info; struct btc_stack_info *stack_info = &btcoexist->stack_info; @@ -2674,8 +2675,8 @@ void ex_halbtc8821a1ant_periodical(struct btc_coexist *btcoexist) } #if (BT_AUTO_REPORT_ONLY_8821A_1ANT == 0) - halbtc8821a1ant_query_bt_info(btcoexist); - halbtc8821a1ant_monitor_bt_ctr(btcoexist); + btc8821a1ant_query_bt_info(btcoexist); + btc8821a1ant_monitor_bt_ctr(btcoexist); #else coex_sta->special_pkt_period_cnt++; #endif diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.h index 20e904890fc2..9f50a1427388 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.h @@ -146,7 +146,7 @@ struct coex_sta_8821a_1ant { bool hid_exist; bool pan_exist; - bool under_Lps; + bool under_lps; bool under_ips; u32 special_pkt_period_cnt; u32 high_priority_tx; diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 7b7772b7d061..f36dab9291b1 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -23,7 +23,7 @@ * *****************************************************************************/ -/*============================================================ +/************************************************************ * Description: * * This file is for RTL8821A Co-exist mechanism @@ -32,22 +32,19 @@ * 2012/08/22 Cosa first check in. * 2012/11/14 Cosa Revise for 8821A 2Ant out sourcing. * - *============================================================ - */ + ************************************************************/ -/*============================================================ +/************************************************************ * include files - *============================================================ -*/ + ************************************************************/ #include "halbt_precomp.h" -/*============================================================ +/************************************************************ * Global variables, these are static variables - *============================================================ - */ -static struct coex_dm_8821a_2ant glcoex_dm_8821a_2ant; -static struct coex_dm_8821a_2ant *coex_dm = &glcoex_dm_8821a_2ant; -static struct coex_sta_8821a_2ant glcoex_sta_8821a_2ant; -static struct coex_sta_8821a_2ant *coex_sta = &glcoex_sta_8821a_2ant; + ************************************************************/ +static struct coex_dm_8821a_2ant glcoex_dm_8821a_2ant; +static struct coex_dm_8821a_2ant *coex_dm = &glcoex_dm_8821a_2ant; +static struct coex_sta_8821a_2ant glcoex_sta_8821a_2ant; +static struct coex_sta_8821a_2ant *coex_sta = &glcoex_sta_8821a_2ant; static const char *const glbt_info_src_8821a_2ant[] = { "BT Info[wifi fw]", @@ -55,32 +52,29 @@ static const char *const glbt_info_src_8821a_2ant[] = { "BT Info[bt auto report]", }; -static u32 glcoex_ver_date_8821a_2ant = 20130618; -static u32 glcoex_ver_8821a_2ant = 0x5050; +static u32 glcoex_ver_date_8821a_2ant = 20130618; +static u32 glcoex_ver_8821a_2ant = 0x5050; -/*============================================================ +/************************************************************ * local function proto type if needed - *============================================================ - *============================================================ - * local function start with halbtc8821a2ant_ - *============================================================ - */ -static u8 halbtc8821a2ant_bt_rssi_state(struct btc_coexist *btcoexist, - u8 level_num, u8 rssi_thresh, - u8 rssi_thresh1) + * + * local function start with btc8821a2ant_ + ************************************************************/ +static u8 btc8821a2ant_bt_rssi_state(struct btc_coexist *btcoexist, + u8 level_num, u8 rssi_thresh, + u8 rssi_thresh1) { struct rtl_priv *rtlpriv = btcoexist->adapter; - long bt_rssi = 0; - u8 bt_rssi_state = coex_sta->pre_bt_rssi_state; + long bt_rssi = 0; + u8 bt_rssi_state = coex_sta->pre_bt_rssi_state; bt_rssi = coex_sta->bt_rssi; if (level_num == 2) { if ((coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_LOW) || (coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { - long tmp = rssi_thresh + - BTC_RSSI_COEX_THRESH_TOL_8821A_2ANT; - if (bt_rssi >= tmp) { + if (bt_rssi >= + rssi_thresh + BTC_RSSI_COEX_THRESH_TOL_8821A_2ANT) { bt_rssi_state = BTC_RSSI_STATE_HIGH; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], BT Rssi state switch to High\n"); @@ -110,7 +104,8 @@ static u8 halbtc8821a2ant_bt_rssi_state(struct btc_coexist *btcoexist, if ((coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_LOW) || (coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { if (bt_rssi >= - (rssi_thresh+BTC_RSSI_COEX_THRESH_TOL_8821A_2ANT)) { + (rssi_thresh + + BTC_RSSI_COEX_THRESH_TOL_8821A_2ANT)) { bt_rssi_state = BTC_RSSI_STATE_MEDIUM; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], BT Rssi state switch to Medium\n"); @@ -156,13 +151,13 @@ static u8 halbtc8821a2ant_bt_rssi_state(struct btc_coexist *btcoexist, return bt_rssi_state; } -static u8 halbtc8821a2ant_wifi_rssi_state(struct btc_coexist *btcoexist, - u8 index, u8 level_num, - u8 rssi_thresh, u8 rssi_thresh1) +static u8 btc8821a2ant_wifi_rssi_state(struct btc_coexist *btcoexist, + u8 index, u8 level_num, + u8 rssi_thresh, u8 rssi_thresh1) { struct rtl_priv *rtlpriv = btcoexist->adapter; - long wifi_rssi = 0; - u8 wifi_rssi_state = coex_sta->pre_wifi_rssi_state[index]; + long wifi_rssi = 0; + u8 wifi_rssi_state = coex_sta->pre_wifi_rssi_state[index]; btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi); @@ -204,7 +199,8 @@ static u8 halbtc8821a2ant_wifi_rssi_state(struct btc_coexist *btcoexist, (coex_sta->pre_wifi_rssi_state[index] == BTC_RSSI_STATE_STAY_LOW)) { if (wifi_rssi >= - (rssi_thresh+BTC_RSSI_COEX_THRESH_TOL_8821A_2ANT)) { + (rssi_thresh + + BTC_RSSI_COEX_THRESH_TOL_8821A_2ANT)) { wifi_rssi_state = BTC_RSSI_STATE_MEDIUM; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], wifi RSSI state switch to Medium\n"); @@ -248,22 +244,22 @@ static u8 halbtc8821a2ant_wifi_rssi_state(struct btc_coexist *btcoexist, return wifi_rssi_state; } -static void halbtc8821a2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) +static void btc8821a2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; - u32 reg_hp_txrx, reg_lp_txrx, u4tmp; - u32 reg_hp_tx = 0, reg_hp_rx = 0, reg_lp_tx = 0, reg_lp_rx = 0; + u32 reg_hp_txrx, reg_lp_txrx, u4tmp; + u32 reg_hp_tx = 0, reg_hp_rx = 0, reg_lp_tx = 0, reg_lp_rx = 0; reg_hp_txrx = 0x770; reg_lp_txrx = 0x774; u4tmp = btcoexist->btc_read_4byte(btcoexist, reg_hp_txrx); reg_hp_tx = u4tmp & MASKLWORD; - reg_hp_rx = (u4tmp & MASKHWORD)>>16; + reg_hp_rx = (u4tmp & MASKHWORD) >> 16; u4tmp = btcoexist->btc_read_4byte(btcoexist, reg_lp_txrx); reg_lp_tx = u4tmp & MASKLWORD; - reg_lp_rx = (u4tmp & MASKHWORD)>>16; + reg_lp_rx = (u4tmp & MASKHWORD) >> 16; coex_sta->high_priority_tx = reg_hp_tx; coex_sta->high_priority_rx = reg_hp_rx; @@ -281,14 +277,14 @@ static void halbtc8821a2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc); } -static void halbtc8821a2ant_query_bt_info(struct btc_coexist *btcoexist) +static void btc8821a2ant_query_bt_info(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[1] = {0}; coex_sta->c2h_bt_info_req_sent = true; - h2c_parameter[0] |= BIT0; /* trigger */ + h2c_parameter[0] |= BIT0; /* trigger */ RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Query Bt Info, FW write 0x61 = 0x%x\n", @@ -297,7 +293,7 @@ static void halbtc8821a2ant_query_bt_info(struct btc_coexist *btcoexist) btcoexist->btc_fill_h2c(btcoexist, 0x61, 1, h2c_parameter); } -static u8 halbtc8821a2ant_action_algorithm(struct btc_coexist *btcoexist) +static u8 btc8821a2ant_action_algorithm(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; struct btc_stack_info *stack_info = &btcoexist->stack_info; @@ -307,7 +303,6 @@ static u8 halbtc8821a2ant_action_algorithm(struct btc_coexist *btcoexist) btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); - /*for win-8 stack HID report error*/ /* sync BTInfo with BT firmware and stack */ if (!stack_info->hid_exist) stack_info->hid_exist = coex_sta->hid_exist; @@ -488,7 +483,7 @@ static u8 halbtc8821a2ant_action_algorithm(struct btc_coexist *btcoexist) return algorithm; } -static void btc8821a2ant_set_fw_dac_swing_lev(struct btc_coexist *btcoexist, +static void btc8821a2ant_set_fw_dac_swing_lvl(struct btc_coexist *btcoexist, u8 dac_swing_lvl) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -507,8 +502,8 @@ static void btc8821a2ant_set_fw_dac_swing_lev(struct btc_coexist *btcoexist, btcoexist->btc_fill_h2c(btcoexist, 0x64, 1, h2c_parameter); } -static void halbtc8821a2ant_set_fw_dec_bt_pwr(struct btc_coexist *btcoexist, - bool dec_bt_pwr) +static void btc8821a2ant_set_fw_dec_bt_pwr(struct btc_coexist *btcoexist, + bool dec_bt_pwr) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[1] = {0}; @@ -525,8 +520,8 @@ static void halbtc8821a2ant_set_fw_dec_bt_pwr(struct btc_coexist *btcoexist, btcoexist->btc_fill_h2c(btcoexist, 0x62, 1, h2c_parameter); } -static void halbtc8821a2ant_dec_bt_pwr(struct btc_coexist *btcoexist, - bool force_exec, bool dec_bt_pwr) +static void btc8821a2ant_dec_bt_pwr(struct btc_coexist *btcoexist, + bool force_exec, bool dec_bt_pwr) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -544,14 +539,13 @@ static void halbtc8821a2ant_dec_bt_pwr(struct btc_coexist *btcoexist, if (coex_dm->pre_dec_bt_pwr == coex_dm->cur_dec_bt_pwr) return; } - halbtc8821a2ant_set_fw_dec_bt_pwr(btcoexist, coex_dm->cur_dec_bt_pwr); + btc8821a2ant_set_fw_dec_bt_pwr(btcoexist, coex_dm->cur_dec_bt_pwr); coex_dm->pre_dec_bt_pwr = coex_dm->cur_dec_bt_pwr; } -static void halbtc8821a2ant_fw_dac_swing_lvl(struct btc_coexist *btcoexist, - bool force_exec, - u8 fw_dac_swing_lvl) +static void btc8821a2ant_fw_dac_swing_lvl(struct btc_coexist *btcoexist, + bool force_exec, u8 fw_dac_swing_lvl) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -571,14 +565,14 @@ static void halbtc8821a2ant_fw_dac_swing_lvl(struct btc_coexist *btcoexist, return; } - btc8821a2ant_set_fw_dac_swing_lev(btcoexist, + btc8821a2ant_set_fw_dac_swing_lvl(btcoexist, coex_dm->cur_fw_dac_swing_lvl); coex_dm->pre_fw_dac_swing_lvl = coex_dm->cur_fw_dac_swing_lvl; } -static void btc8821a2ant_SetSwPenTxRateAdapt(struct btc_coexist *btcoexist, - bool low_penalty_ra) +static void btc8821a2ant_set_sw_penalty_tx_rate_adaptive( + struct btc_coexist *btcoexist, bool low_penalty_ra) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[6] = {0}; @@ -587,13 +581,13 @@ static void btc8821a2ant_SetSwPenTxRateAdapt(struct btc_coexist *btcoexist, if (low_penalty_ra) { h2c_parameter[1] |= BIT0; - /*normal rate except MCS7/6/5, OFDM54/48/36 */ + /* normal rate except MCS7/6/5, OFDM54/48/36 */ h2c_parameter[2] = 0x00; - /*MCS7 or OFDM54 */ + /* MCS7 or OFDM54 */ h2c_parameter[3] = 0xf7; - /*MCS6 or OFDM48 */ + /* MCS6 or OFDM48 */ h2c_parameter[4] = 0xf8; - /*MCS5 or OFDM36 */ + /* MCS5 or OFDM36 */ h2c_parameter[5] = 0xf9; } @@ -604,12 +598,11 @@ static void btc8821a2ant_SetSwPenTxRateAdapt(struct btc_coexist *btcoexist, btcoexist->btc_fill_h2c(btcoexist, 0x69, 6, h2c_parameter); } -static void halbtc8821a2ant_low_penalty_ra(struct btc_coexist *btcoexist, - bool force_exec, bool low_penalty_ra) +static void btc8821a2ant_low_penalty_ra(struct btc_coexist *btcoexist, + bool force_exec, bool low_penalty_ra) { struct rtl_priv *rtlpriv = btcoexist->adapter; - /*return;*/ RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], %s turn LowPenaltyRA = %s\n", (force_exec ? "force to" : ""), @@ -620,19 +613,19 @@ static void halbtc8821a2ant_low_penalty_ra(struct btc_coexist *btcoexist, RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], pre_low_penalty_ra = %d, cur_low_penalty_ra = %d\n", coex_dm->pre_low_penalty_ra, - coex_dm->cur_low_penalty_ra); + coex_dm->cur_low_penalty_ra); if (coex_dm->pre_low_penalty_ra == coex_dm->cur_low_penalty_ra) return; } - btc8821a2ant_SetSwPenTxRateAdapt(btcoexist, + btc8821a2ant_set_sw_penalty_tx_rate_adaptive(btcoexist, coex_dm->cur_low_penalty_ra); coex_dm->pre_low_penalty_ra = coex_dm->cur_low_penalty_ra; } -static void halbtc8821a2ant_set_dac_swing_reg(struct btc_coexist *btcoexist, - u32 level) +static void btc8821a2ant_set_dac_swing_reg(struct btc_coexist *btcoexist, + u32 level) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 val = (u8)level; @@ -647,14 +640,14 @@ static void btc8821a2ant_set_sw_full_dac_swing(struct btc_coexist *btcoexist, u32 sw_dac_swing_lvl) { if (sw_dac_swing_on) - halbtc8821a2ant_set_dac_swing_reg(btcoexist, sw_dac_swing_lvl); + btc8821a2ant_set_dac_swing_reg(btcoexist, sw_dac_swing_lvl); else - halbtc8821a2ant_set_dac_swing_reg(btcoexist, 0x18); + btc8821a2ant_set_dac_swing_reg(btcoexist, 0x18); } -static void halbtc8821a2ant_dac_swing(struct btc_coexist *btcoexist, - bool force_exec, bool dac_swing_on, - u32 dac_swing_lvl) +static void btc8821a2ant_dac_swing(struct btc_coexist *btcoexist, + bool force_exec, bool dac_swing_on, + u32 dac_swing_lvl) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -687,9 +680,9 @@ static void halbtc8821a2ant_dac_swing(struct btc_coexist *btcoexist, coex_dm->pre_dac_swing_lvl = coex_dm->cur_dac_swing_lvl; } -static void halbtc8821a2ant_set_coex_table(struct btc_coexist *btcoexist, - u32 val0x6c0, u32 val0x6c4, - u32 val0x6c8, u8 val0x6cc) +static void btc8821a2ant_set_coex_table(struct btc_coexist *btcoexist, + u32 val0x6c0, u32 val0x6c4, + u32 val0x6c8, u8 val0x6cc) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -710,9 +703,9 @@ static void halbtc8821a2ant_set_coex_table(struct btc_coexist *btcoexist, btcoexist->btc_write_1byte(btcoexist, 0x6cc, val0x6cc); } -static void halbtc8821a2ant_coex_table(struct btc_coexist *btcoexist, - bool force_exec, u32 val0x6c0, - u32 val0x6c4, u32 val0x6c8, u8 val0x6cc) +static void btc8821a2ant_coex_table(struct btc_coexist *btcoexist, + bool force_exec, u32 val0x6c0, + u32 val0x6c4, u32 val0x6c8, u8 val0x6cc) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -745,8 +738,8 @@ static void halbtc8821a2ant_coex_table(struct btc_coexist *btcoexist, (coex_dm->pre_val0x6cc == coex_dm->cur_val0x6cc)) return; } - halbtc8821a2ant_set_coex_table(btcoexist, val0x6c0, val0x6c4, val0x6c8, - val0x6cc); + btc8821a2ant_set_coex_table(btcoexist, val0x6c0, val0x6c4, val0x6c8, + val0x6cc); coex_dm->pre_val0x6c0 = coex_dm->cur_val0x6c0; coex_dm->pre_val0x6c4 = coex_dm->cur_val0x6c4; @@ -754,14 +747,14 @@ static void halbtc8821a2ant_coex_table(struct btc_coexist *btcoexist, coex_dm->pre_val0x6cc = coex_dm->cur_val0x6cc; } -static void halbtc8821a2ant_set_fw_ignore_wlan_act(struct btc_coexist *btcoex, - bool enable) +static void btc8821a2ant_set_fw_ignore_wlan_act(struct btc_coexist *btcoex, + bool enable) { struct rtl_priv *rtlpriv = btcoex->adapter; u8 h2c_parameter[1] = {0}; if (enable) - h2c_parameter[0] |= BIT0;/* function enable */ + h2c_parameter[0] |= BIT0; /* function enable */ RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], set FW for BT Ignore Wlan_Act, FW write 0x63 = 0x%x\n", @@ -770,8 +763,8 @@ static void halbtc8821a2ant_set_fw_ignore_wlan_act(struct btc_coexist *btcoex, btcoex->btc_fill_h2c(btcoex, 0x63, 1, h2c_parameter); } -static void halbtc8821a2ant_ignore_wlan_act(struct btc_coexist *btcoexist, - bool force_exec, bool enable) +static void btc8821a2ant_ignore_wlan_act(struct btc_coexist *btcoexist, + bool force_exec, bool enable) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -790,14 +783,14 @@ static void halbtc8821a2ant_ignore_wlan_act(struct btc_coexist *btcoexist, coex_dm->cur_ignore_wlan_act) return; } - halbtc8821a2ant_set_fw_ignore_wlan_act(btcoexist, enable); + btc8821a2ant_set_fw_ignore_wlan_act(btcoexist, enable); coex_dm->pre_ignore_wlan_act = coex_dm->cur_ignore_wlan_act; } -static void halbtc8821a2ant_set_fw_pstdma(struct btc_coexist *btcoexist, - u8 byte1, u8 byte2, u8 byte3, - u8 byte4, u8 byte5) +static void btc8821a2ant_set_fw_ps_tdma(struct btc_coexist *btcoexist, + u8 byte1, u8 byte2, u8 byte3, + u8 byte4, u8 byte5) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[5]; @@ -825,10 +818,9 @@ static void halbtc8821a2ant_set_fw_pstdma(struct btc_coexist *btcoexist, btcoexist->btc_fill_h2c(btcoexist, 0x60, 5, h2c_parameter); } -static void btc8821a2ant_sw_mech1(struct btc_coexist *btcoexist, - bool shrink_rx_lpf, - bool low_penalty_ra, bool limited_dig, - bool bt_lna_constrain) +static void btc8821a2ant_sw_mechanism1(struct btc_coexist *btcoexist, + bool shrink_rx_lpf, bool low_penalty_ra, + bool limited_dig, bool bt_lna_constrain) { u32 wifi_bw; @@ -840,22 +832,20 @@ static void btc8821a2ant_sw_mech1(struct btc_coexist *btcoexist, shrink_rx_lpf = false; } - halbtc8821a2ant_low_penalty_ra(btcoexist, - NORMAL_EXEC, low_penalty_ra); + btc8821a2ant_low_penalty_ra(btcoexist, NORMAL_EXEC, low_penalty_ra); } -static void btc8821a2ant_sw_mech2(struct btc_coexist *btcoexist, - bool agc_table_shift, - bool adc_back_off, bool sw_dac_swing, - u32 dac_swing_lvl) +static void btc8821a2ant_sw_mechanism2(struct btc_coexist *btcoexist, + bool agc_table_shift, bool adc_back_off, + bool sw_dac_swing, u32 dac_swing_lvl) { - halbtc8821a2ant_dac_swing(btcoexist, NORMAL_EXEC, sw_dac_swing, + btc8821a2ant_dac_swing(btcoexist, NORMAL_EXEC, sw_dac_swing, sw_dac_swing); } -static void halbtc8821a2ant_set_ant_path(struct btc_coexist *btcoexist, - u8 ant_pos_type, bool init_hw_cfg, - bool wifi_off) +static void btc8821a2ant_set_ant_path(struct btc_coexist *btcoexist, + u8 ant_pos_type, bool init_hw_cfg, + bool wifi_off) { struct btc_board_info *board_info = &btcoexist->board_info; u32 u4tmp = 0; @@ -872,18 +862,16 @@ static void halbtc8821a2ant_set_ant_path(struct btc_coexist *btcoexist, btcoexist->btc_write_1byte(btcoexist, 0xcb4, 0x77); if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) { - /* tell firmware "antenna inverse" ==> - * WRONG firmware antenna control code. - * ==>need fw to fix + /* tell firmware "antenna inverse" ==> WRONG firmware + * antenna control code ==>need fw to fix */ h2c_parameter[0] = 1; h2c_parameter[1] = 1; btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); } else { - /* tell firmware "no antenna inverse" - * ==> WRONG firmware antenna control code. - * ==>need fw to fix + /* tell firmware "no antenna inverse" ==> WRONG firmware + * antenna control code ==>need fw to fix */ h2c_parameter[0] = 0; h2c_parameter[1] = 1; @@ -903,8 +891,8 @@ static void halbtc8821a2ant_set_ant_path(struct btc_coexist *btcoexist, } } -static void halbtc8821a2ant_ps_tdma(struct btc_coexist *btcoexist, - bool force_exec, bool turn_on, u8 type) +static void btc8821a2ant_ps_tdma(struct btc_coexist *btcoexist, + bool force_exec, bool turn_on, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -931,108 +919,108 @@ static void halbtc8821a2ant_ps_tdma(struct btc_coexist *btcoexist, switch (type) { case 1: default: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, + 0x1a, 0xe1, 0x90); break; case 2: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x12, - 0x12, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, + 0x12, 0xe1, 0x90); break; case 3: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x1c, - 0x3, 0xf1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, + 0x3, 0xf1, 0x90); break; case 4: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x10, - 0x03, 0xf1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x10, + 0x03, 0xf1, 0x90); break; case 5: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, + 0x1a, 0x60, 0x90); break; case 6: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x12, - 0x12, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, + 0x12, 0x60, 0x90); break; case 7: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x1c, - 0x3, 0x70, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, + 0x3, 0x70, 0x90); break; case 8: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xa3, 0x10, - 0x3, 0x70, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xa3, 0x10, + 0x3, 0x70, 0x90); break; case 9: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, + 0x1a, 0xe1, 0x90); break; case 10: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x12, - 0x12, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, + 0x12, 0xe1, 0x90); break; case 11: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0xa, - 0xa, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0xa, + 0xa, 0xe1, 0x90); break; case 12: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x5, - 0x5, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, + 0x5, 0xe1, 0x90); break; case 13: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, + 0x1a, 0x60, 0x90); break; case 14: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, - 0x12, 0x12, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, + 0x12, 0x12, 0x60, 0x90); break; case 15: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0xa, - 0xa, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0xa, + 0xa, 0x60, 0x90); break; case 16: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x5, - 0x5, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, + 0x5, 0x60, 0x90); break; case 17: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xa3, 0x2f, - 0x2f, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xa3, 0x2f, + 0x2f, 0x60, 0x90); break; case 18: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x5, - 0x5, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, + 0x5, 0xe1, 0x90); break; case 19: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x25, - 0x25, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x25, + 0x25, 0xe1, 0x90); break; case 20: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x25, - 0x25, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x25, + 0x25, 0x60, 0x90); break; case 21: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x15, - 0x03, 0x70, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x15, + 0x03, 0x70, 0x90); break; case 71: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, + 0x1a, 0xe1, 0x90); break; } } else { /* disable PS tdma */ switch (type) { case 0: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0x0, 0x0, 0x0, - 0x40, 0x0); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0, + 0x40, 0x0); break; case 1: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0x0, 0x0, 0x0, - 0x48, 0x0); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0, + 0x48, 0x0); break; default: - halbtc8821a2ant_set_fw_pstdma(btcoexist, 0x0, 0x0, 0x0, - 0x40, 0x0); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0, + 0x40, 0x0); break; } } @@ -1042,54 +1030,54 @@ static void halbtc8821a2ant_ps_tdma(struct btc_coexist *btcoexist, coex_dm->pre_ps_tdma = coex_dm->cur_ps_tdma; } -static void halbtc8821a2ant_coex_all_off(struct btc_coexist *btcoexist) +static void btc8821a2ant_coex_all_off(struct btc_coexist *btcoexist) { /* fw all off */ - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); /* sw all off */ - btc8821a2ant_sw_mech1(btcoexist, false, false, false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); /* hw all off */ - halbtc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, - 0x55555555, 0x55555555, 0xffff, 0x3); + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, + 0x55555555, 0x55555555, 0xffff, 0x3); } -static void halbtc8821a2ant_coex_under_5g(struct btc_coexist *btcoexist) +static void btc8821a2ant_coex_under_5g(struct btc_coexist *btcoexist) { - halbtc8821a2ant_coex_all_off(btcoexist); + btc8821a2ant_coex_all_off(btcoexist); } -static void halbtc8821a2ant_init_coex_dm(struct btc_coexist *btcoexist) +static void btc8821a2ant_init_coex_dm(struct btc_coexist *btcoexist) { /* force to reset coex mechanism */ - halbtc8821a2ant_coex_table(btcoexist, FORCE_EXEC, 0x55555555, - 0x55555555, 0xffff, 0x3); + btc8821a2ant_coex_table(btcoexist, FORCE_EXEC, 0x55555555, + 0x55555555, 0xffff, 0x3); - halbtc8821a2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1); - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6); - halbtc8821a2ant_dec_bt_pwr(btcoexist, FORCE_EXEC, false); + btc8821a2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6); + btc8821a2ant_dec_bt_pwr(btcoexist, FORCE_EXEC, false); - btc8821a2ant_sw_mech1(btcoexist, false, false, false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); } -static void halbtc8821a2ant_bt_inquiry_page(struct btc_coexist *btcoexist) +static void btc8821a2ant_bt_inquiry_page(struct btc_coexist *btcoexist) { bool low_pwr_disable = true; btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); - halbtc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5afa5afa, 0xffff, 0x3); - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5afa5afa, 0xffff, 0x3); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); } -static bool halbtc8821a2ant_is_common_action(struct btc_coexist *btcoexist) +static bool btc8821a2ant_is_common_action(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; bool common = false, wifi_connected = false, wifi_busy = false; @@ -1099,8 +1087,8 @@ static bool halbtc8821a2ant_is_common_action(struct btc_coexist *btcoexist) &wifi_connected); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy); - halbtc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5afa5afa, 0xffff, 0x3); + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5afa5afa, 0xffff, 0x3); if (!wifi_connected && BT_8821A_2ANT_BT_STATUS_IDLE == coex_dm->bt_status) { @@ -1111,12 +1099,14 @@ static bool halbtc8821a2ant_is_common_action(struct btc_coexist *btcoexist) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi IPS + BT IPS!!\n"); - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btc8821a2ant_sw_mech1(btcoexist, false, false, false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, + false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, + 0x18); common = true; } else if (wifi_connected && @@ -1128,20 +1118,22 @@ static bool halbtc8821a2ant_is_common_action(struct btc_coexist *btcoexist) if (wifi_busy) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi Busy + BT IPS!!\n"); - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 1); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + false, 1); } else { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi LPS + BT IPS!!\n"); - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 1); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + false, 1); } - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btc8821a2ant_sw_mech1(btcoexist, false, false, false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, + false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, + 0x18); common = true; } else if (!wifi_connected && @@ -1153,12 +1145,14 @@ static bool halbtc8821a2ant_is_common_action(struct btc_coexist *btcoexist) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi IPS + BT LPS!!\n"); - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btc8821a2ant_sw_mech1(btcoexist, false, false, false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, + false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, + 0x18); common = true; } else if (wifi_connected && (BT_8821A_2ANT_BT_STATUS_CON_IDLE == coex_dm->bt_status)) { @@ -1169,40 +1163,40 @@ static bool halbtc8821a2ant_is_common_action(struct btc_coexist *btcoexist) if (wifi_busy) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi Busy + BT LPS!!\n"); - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 1); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + false, 1); } else { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi LPS + BT LPS!!\n"); - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 1); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + false, 1); } - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btc8821a2ant_sw_mech1(btcoexist, true, true, true, true); - btc8821a2ant_sw_mech2(btcoexist, false, false, false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, true, true, true); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, + 0x18); common = true; } else if (!wifi_connected && - (BT_8821A_2ANT_BT_STATUS_NON_IDLE == - coex_dm->bt_status)) { + (coex_dm->bt_status == BT_8821A_2ANT_BT_STATUS_NON_IDLE)) { low_pwr_disable = false; - btcoexist->btc_set(btcoexist, - BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); + btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, + &low_pwr_disable); RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi IPS + BT Busy!!\n"); - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btc8821a2ant_sw_mech1(btcoexist, false, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); common = true; } else { @@ -1218,13 +1212,12 @@ static bool halbtc8821a2ant_is_common_action(struct btc_coexist *btcoexist) } else { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi LPS + BT Busy!!\n"); - halbtc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, true, 21); - + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 21); common = true; } - btc8821a2ant_sw_mech1(btcoexist, true, true, true, true); + btc8821a2ant_sw_mechanism1(btcoexist, true, true, true, true); } return common; } @@ -1234,12 +1227,13 @@ static void btc8821a2ant_tdma_dur_adj(struct btc_coexist *btcoexist, u8 max_interval) { struct rtl_priv *rtlpriv = btcoexist->adapter; - static long up, dn, m, n, wait_count; - /* 0: no change, +1: increase WiFi duration, + static long up, dn, m, n, wait_count; + /* 0 : no change + * +1: increase WiFi duration * -1: decrease WiFi duration */ - int result; - u8 retry_count = 0; + int result; + u8 retry_count = 0; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], TdmaDurationAdjust()\n"); @@ -1251,72 +1245,72 @@ static void btc8821a2ant_tdma_dur_adj(struct btc_coexist *btcoexist, if (sco_hid) { if (tx_pause) { if (max_interval == 1) { - halbtc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 13); + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 13); coex_dm->tdma_adj_type = 13; } else if (max_interval == 2) { - halbtc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 14); + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 14); coex_dm->tdma_adj_type = 14; } else { - halbtc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 15); + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 15); coex_dm->tdma_adj_type = 15; } } else { if (max_interval == 1) { - halbtc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 9); + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 9); coex_dm->tdma_adj_type = 9; } else if (max_interval == 2) { - halbtc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 10); + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 10); coex_dm->tdma_adj_type = 10; } else { - halbtc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 11); + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 11); coex_dm->tdma_adj_type = 11; } } } else { if (tx_pause) { if (max_interval == 1) { - halbtc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 5); + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 5); coex_dm->tdma_adj_type = 5; } else if (max_interval == 2) { - halbtc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 6); + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 6); coex_dm->tdma_adj_type = 6; } else { - halbtc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 7); + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 7); coex_dm->tdma_adj_type = 7; } } else { if (max_interval == 1) { - halbtc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 1); + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 1); coex_dm->tdma_adj_type = 1; } else if (max_interval == 2) { - halbtc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 2); + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 2); coex_dm->tdma_adj_type = 2; } else { - halbtc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 3); + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 3); coex_dm->tdma_adj_type = 3; } } @@ -1368,7 +1362,7 @@ static void btc8821a2ant_tdma_dur_adj(struct btc_coexist *btcoexist, up = 0; if (dn == 2) { - /* if retry count< 3 for 2*2 seconds, + /* if retry count < 3 for 2*2 seconds, * shrink wifi duration */ if (wait_count <= 2) @@ -1381,7 +1375,7 @@ static void btc8821a2ant_tdma_dur_adj(struct btc_coexist *btcoexist, if (m >= 20) m = 20; - n = 3*m; + n = 3 * m; up = 0; dn = 0; wait_count = 0; @@ -1403,7 +1397,7 @@ static void btc8821a2ant_tdma_dur_adj(struct btc_coexist *btcoexist, if (m >= 20) m = 20; - n = 3*m; + n = 3 * m; up = 0; dn = 0; wait_count = 0; @@ -1425,200 +1419,201 @@ static void btc8821a2ant_tdma_dur_adj(struct btc_coexist *btcoexist, RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], PsTdma type dismatch!!!, cur_ps_tdma = %d, recordPsTdma = %d\n", - coex_dm->cur_ps_tdma, coex_dm->tdma_adj_type); + coex_dm->cur_ps_tdma, coex_dm->tdma_adj_type); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_ROAM, &roam); if (!scan && !link && !roam) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, - coex_dm->tdma_adj_type); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, + coex_dm->tdma_adj_type); } else { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], roaming/link/scan is under progress, will adjust next time!!!\n"); } } - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 0x6); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 0x6); } /* SCO only or SCO+PAN(HS)*/ -static void halbtc8821a2ant_action_sco(struct btc_coexist *btcoexist) +static void btc8821a2ant_action_sco(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; + u8 wifi_rssi_state, bt_rssi_state; u32 wifi_bw; - wifi_rssi_state = halbtc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, - 15, 0); - bt_rssi_state = halbtc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, + 15, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 4); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 4); if (BTC_RSSI_HIGH(bt_rssi_state)) - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - if (BTC_WIFI_BW_LEGACY == wifi_bw) { + if (wifi_bw == BTC_WIFI_BW_LEGACY) { /* for SCO quality at 11b/g mode */ - halbtc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, - 0x5a5a5a5a, 0x5a5a5a5a, 0xffff, 0x3); + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, + 0x5a5a5a5a, 0x5a5a5a5a, 0xffff, 0x3); } else { /* for SCO quality & wifi performance balance at 11n mode */ - halbtc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, - 0x5aea5aea, 0x5aea5aea, 0xffff, 0x3); + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, + 0x5aea5aea, 0x5aea5aea, 0xffff, 0x3); } - if (BTC_WIFI_BW_HT40 == wifi_bw) { - /* fw mechanism - * halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); - */ + if (wifi_bw == BTC_WIFI_BW_HT40) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 0); /*for voice quality*/ + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + /* for voice quality */ + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + false, 0); + } else { + /* for voice quality */ + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + false, 0); + } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, true, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, true, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { - /* fw mechanism - * halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); - */ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 0); /*for voice quality*/ + /* for voice quality */ + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); } else { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 0); /*for voice quality*/ + /* for voice quality */ + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, false, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, false, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } -static void halbtc8821a2ant_action_hid(struct btc_coexist *btcoexist) +static void btc8821a2ant_action_hid(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; - u32 wifi_bw; + u8 wifi_rssi_state, bt_rssi_state; + u32 wifi_bw; - wifi_rssi_state = halbtc8821a2ant_wifi_rssi_state(btcoexist, - 0, 2, 15, 0); - bt_rssi_state = halbtc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - if (BTC_WIFI_BW_LEGACY == wifi_bw) { + if (wifi_bw == BTC_WIFI_BW_LEGACY) { /* for HID at 11b/g mode */ - halbtc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5a5a5a5a, 0xffff, 0x3); + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5a5a5a5a, 0xffff, 0x3); } else { /* for HID quality & wifi performance balance at 11n mode */ - halbtc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5aea5aea, 0xffff, 0x3); + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5aea5aea, 0xffff, 0x3); } - if (BTC_WIFI_BW_HT40 == wifi_bw) { + if (wifi_bw == BTC_WIFI_BW_HT40) { /* fw mechanism */ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 9); } else { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 13); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 13); } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, true, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, true, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { /* fw mechanism */ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 9); } else { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 13); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 13); } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, false, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, false, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } /* A2DP only / PAN(EDR) only/ A2DP+PAN(HS) */ -static void halbtc8821a2ant_action_a2dp(struct btc_coexist *btcoexist) +static void btc8821a2ant_action_a2dp(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; - u32 wifi_bw; + u8 wifi_rssi_state, bt_rssi_state; + u32 wifi_bw; - wifi_rssi_state = halbtc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, - 15, 0); - bt_rssi_state = halbtc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, + 15, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); /* fw dac swing is called in btc8821a2ant_tdma_dur_adj() - * halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + * btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); */ if (BTC_RSSI_HIGH(bt_rssi_state)) - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); @@ -1634,15 +1629,15 @@ static void halbtc8821a2ant_action_a2dp(struct btc_coexist *btcoexist) /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, true, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, true, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { /* fw mechanism */ @@ -1656,62 +1651,57 @@ static void halbtc8821a2ant_action_a2dp(struct btc_coexist *btcoexist) /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, false, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, false, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } -static void halbtc8821a2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) +static void btc8821a2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state, bt_info_ext; - u32 wifi_bw; + u8 wifi_rssi_state, bt_rssi_state, bt_info_ext; + u32 wifi_bw; bt_info_ext = coex_sta->bt_info_ext; - wifi_rssi_state = halbtc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, - 15, 0); - bt_rssi_state = halbtc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); - - /*fw dac swing is called in btc8821a2ant_tdma_dur_adj() - *halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - */ + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); if (BTC_RSSI_HIGH(bt_rssi_state)) - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - if (BTC_WIFI_BW_HT40 == wifi_bw) { + if (wifi_bw == BTC_WIFI_BW_HT40) { /* fw mechanism */ if (bt_info_ext&BIT0) { - /*a2dp basic rate*/ + /* a2dp basic rate */ btc8821a2ant_tdma_dur_adj(btcoexist, false, true, 2); } else { - /*a2dp edr rate*/ + /* a2dp edr rate */ btc8821a2ant_tdma_dur_adj(btcoexist, false, true, 1); } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, true, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, true, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { /* fw mechanism */ @@ -1726,109 +1716,107 @@ static void halbtc8821a2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, false, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, false, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } -static void halbtc8821a2ant_action_pan_edr(struct btc_coexist *btcoexist) +static void btc8821a2ant_action_pan_edr(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; - u32 wifi_bw; + u8 wifi_rssi_state, bt_rssi_state; + u32 wifi_bw; - wifi_rssi_state = halbtc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, - 15, 0); - bt_rssi_state = halbtc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); if (BTC_WIFI_BW_LEGACY == wifi_bw) { /* for HID at 11b/g mode */ - halbtc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5aff5aff, 0xffff, 0x3); + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5aff5aff, 0xffff, 0x3); } else { /* for HID quality & wifi performance balance at 11n mode */ - halbtc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5aff5aff, 0xffff, 0x3); + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5aff5aff, 0xffff, 0x3); } if (BTC_WIFI_BW_HT40 == wifi_bw) { /* fw mechanism */ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 1); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 1); } else { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 5); } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, true, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, true, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { /* fw mechanism */ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 1); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 1); } else { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 5); } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, false, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, false, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } /* PAN(HS) only */ -static void halbtc8821a2ant_action_pan_hs(struct btc_coexist *btcoexist) +static void btc8821a2ant_action_pan_hs(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; - u32 wifi_bw; + u8 wifi_rssi_state, bt_rssi_state; + u32 wifi_bw; - wifi_rssi_state = halbtc8821a2ant_wifi_rssi_state(btcoexist, - 0, 2, 15, 0); - bt_rssi_state = halbtc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); @@ -1836,78 +1824,85 @@ static void halbtc8821a2ant_action_pan_hs(struct btc_coexist *btcoexist) /* fw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, - true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); } else { - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, - false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); } - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, true, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, true, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { /* fw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8821a2ant_dec_bt_pwr(btcoexist, - NORMAL_EXEC, true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); } else { - halbtc8821a2ant_dec_bt_pwr(btcoexist, - NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); } - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + } else { + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, false, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, false, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } /* PAN(EDR)+A2DP */ -static void halbtc8821a2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) +static void btc8821a2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) { u8 wifi_rssi_state, bt_rssi_state, bt_info_ext; u32 wifi_bw; bt_info_ext = coex_sta->bt_info_ext; - wifi_rssi_state = halbtc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, - 15, 0); - bt_rssi_state = halbtc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - halbtc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5afa5afa, 0xffff, 0x3); + if (wifi_bw == BTC_WIFI_BW_LEGACY) { + /* for HID at 11b/g mode */ + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5afa5afa, 0xffff, 0x3); + } else { + /* for HID quality & wifi performance balance at 11n mode */ + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5afa5afa, 0xffff, 0x3); + } if (BTC_WIFI_BW_HT40 == wifi_bw) { /* fw mechanism */ @@ -1922,16 +1917,16 @@ static void halbtc8821a2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, true, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, true, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); - } + btc8821a2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + }; } else { /* fw mechanism */ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || @@ -1943,89 +1938,92 @@ static void halbtc8821a2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, false, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, false, false, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } -static void halbtc8821a2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) +static void btc8821a2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; - u32 wifi_bw; + u8 wifi_rssi_state, bt_rssi_state; + u32 wifi_bw; - wifi_rssi_state = halbtc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, - 15, 0); - bt_rssi_state = halbtc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - halbtc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5a5f5a5f, 0xffff, 0x3); + if (wifi_bw == BTC_WIFI_BW_LEGACY) { + /* for HID at 11b/g mode */ + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5a5f5a5f, 0xffff, 0x3); + } else { + /* for HID quality & wifi performance balance at 11n mode */ + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5a5f5a5f, 0xffff, 0x3); + } - if (BTC_WIFI_BW_HT40 == wifi_bw) { - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 3); + if (wifi_bw == BTC_WIFI_BW_HT40) { + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 3); /* fw mechanism */ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 10); } else { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, true, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, true, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); /* fw mechanism */ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 10); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10); } else { - halbtc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 14); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, false, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, false, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } @@ -2033,25 +2031,31 @@ static void halbtc8821a2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) /* HID+A2DP+PAN(EDR) */ static void btc8821a2ant_act_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state, bt_info_ext; - u32 wifi_bw; + u8 wifi_rssi_state, bt_rssi_state, bt_info_ext; + u32 wifi_bw; bt_info_ext = coex_sta->bt_info_ext; - wifi_rssi_state = halbtc8821a2ant_wifi_rssi_state(btcoexist, - 0, 2, 15, 0); - bt_rssi_state = halbtc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); - halbtc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - halbtc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5a5a5a5a, 0xffff, 0x3); + if (wifi_bw == BTC_WIFI_BW_LEGACY) { + /* for HID at 11b/g mode */ + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5a5a5a5a, 0xffff, 0x3); + } else { + /* for HID quality & wifi performance balance at 11n mode */ + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5a5a5a5a, 0xffff, 0x3); + } if (BTC_WIFI_BW_HT40 == wifi_bw) { /* fw mechanism */ @@ -2060,15 +2064,15 @@ static void btc8821a2ant_act_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, true, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, true, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { /* fw mechanism */ @@ -2098,38 +2102,44 @@ static void btc8821a2ant_act_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, false, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, false, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } -static void halbtc8821a2ant_action_hid_a2dp(struct btc_coexist *btcoexist) +static void btc8821a2ant_action_hid_a2dp(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state, bt_info_ext; - u32 wifi_bw; + u8 wifi_rssi_state, bt_rssi_state, bt_info_ext; + u32 wifi_bw; bt_info_ext = coex_sta->bt_info_ext; - wifi_rssi_state = halbtc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, - 15, 0); - bt_rssi_state = halbtc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); if (BTC_RSSI_HIGH(bt_rssi_state)) - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else - halbtc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - halbtc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5f5b5f5b, 0xffffff, 0x3); + if (wifi_bw == BTC_WIFI_BW_LEGACY) { + /* for HID at 11b/g mode */ + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5f5b5f5b, 0xffffff, 0x3); + } else { + /* for HID quality & wifi performance balance at 11n mode */ + btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, + 0x5f5b5f5b, 0xffffff, 0x3); + } if (BTC_WIFI_BW_HT40 == wifi_bw) { /* fw mechanism */ @@ -2138,15 +2148,15 @@ static void halbtc8821a2ant_action_hid_a2dp(struct btc_coexist *btcoexist) /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, true, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, true, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } else { /* fw mechanism */ @@ -2155,24 +2165,24 @@ static void halbtc8821a2ant_action_hid_a2dp(struct btc_coexist *btcoexist) /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_sw_mech1(btcoexist, false, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, true, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); } else { - btc8821a2ant_sw_mech1(btcoexist, false, true, - false, false); - btc8821a2ant_sw_mech2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); } } } -static void halbtc8821a2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) +static void btc8821a2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; - bool wifi_under_5g = false; - u8 algorithm = 0; + bool wifi_under_5g = false; + u8 algorithm = 0; if (btcoexist->manual_control) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -2186,16 +2196,16 @@ static void halbtc8821a2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) if (wifi_under_5g) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], RunCoexistMechanism(), run 5G coex setting!!<===\n"); - halbtc8821a2ant_coex_under_5g(btcoexist); + btc8821a2ant_coex_under_5g(btcoexist); return; } - algorithm = halbtc8821a2ant_action_algorithm(btcoexist); + algorithm = btc8821a2ant_action_algorithm(btcoexist); if (coex_sta->c2h_bt_inquiry_page && (BT_8821A_2ANT_COEX_ALGO_PANHS != algorithm)) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], BT is under inquiry/page scan !!\n"); - halbtc8821a2ant_bt_inquiry_page(btcoexist); + btc8821a2ant_bt_inquiry_page(btcoexist); return; } @@ -2203,7 +2213,7 @@ static void halbtc8821a2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Algorithm = %d\n", coex_dm->cur_algorithm); - if (halbtc8821a2ant_is_common_action(btcoexist)) { + if (btc8821a2ant_is_common_action(btcoexist)) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action 2-Ant common\n"); coex_dm->reset_tdma_adjust = true; @@ -2219,42 +2229,42 @@ static void halbtc8821a2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) case BT_8821A_2ANT_COEX_ALGO_SCO: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action 2-Ant, algorithm = SCO\n"); - halbtc8821a2ant_action_sco(btcoexist); + btc8821a2ant_action_sco(btcoexist); break; case BT_8821A_2ANT_COEX_ALGO_HID: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action 2-Ant, algorithm = HID\n"); - halbtc8821a2ant_action_hid(btcoexist); + btc8821a2ant_action_hid(btcoexist); break; case BT_8821A_2ANT_COEX_ALGO_A2DP: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action 2-Ant, algorithm = A2DP\n"); - halbtc8821a2ant_action_a2dp(btcoexist); + btc8821a2ant_action_a2dp(btcoexist); break; case BT_8821A_2ANT_COEX_ALGO_A2DP_PANHS: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action 2-Ant, algorithm = A2DP+PAN(HS)\n"); - halbtc8821a2ant_action_a2dp_pan_hs(btcoexist); + btc8821a2ant_action_a2dp_pan_hs(btcoexist); break; case BT_8821A_2ANT_COEX_ALGO_PANEDR: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action 2-Ant, algorithm = PAN(EDR)\n"); - halbtc8821a2ant_action_pan_edr(btcoexist); + btc8821a2ant_action_pan_edr(btcoexist); break; case BT_8821A_2ANT_COEX_ALGO_PANHS: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action 2-Ant, algorithm = HS mode\n"); - halbtc8821a2ant_action_pan_hs(btcoexist); + btc8821a2ant_action_pan_hs(btcoexist); break; case BT_8821A_2ANT_COEX_ALGO_PANEDR_A2DP: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action 2-Ant, algorithm = PAN+A2DP\n"); - halbtc8821a2ant_action_pan_edr_a2dp(btcoexist); + btc8821a2ant_action_pan_edr_a2dp(btcoexist); break; case BT_8821A_2ANT_COEX_ALGO_PANEDR_HID: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action 2-Ant, algorithm = PAN(EDR)+HID\n"); - halbtc8821a2ant_action_pan_edr_hid(btcoexist); + btc8821a2ant_action_pan_edr_hid(btcoexist); break; case BT_8821A_2ANT_COEX_ALGO_HID_A2DP_PANEDR: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -2264,26 +2274,22 @@ static void halbtc8821a2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) case BT_8821A_2ANT_COEX_ALGO_HID_A2DP: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action 2-Ant, algorithm = HID+A2DP\n"); - halbtc8821a2ant_action_hid_a2dp(btcoexist); + btc8821a2ant_action_hid_a2dp(btcoexist); break; default: RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Action 2-Ant, algorithm = coexist All Off!!\n"); - halbtc8821a2ant_coex_all_off(btcoexist); + btc8821a2ant_coex_all_off(btcoexist); break; } coex_dm->pre_algorithm = coex_dm->cur_algorithm; } } -/*============================================================ - *work around function start with wa_halbtc8821a2ant_ - *============================================================ - *============================================================ - * extern function start with EXhalbtc8821a2ant_ - *============================================================ - */ -void ex_halbtc8821a2ant_init_hwconfig(struct btc_coexist *btcoexist) +/************************************************************** + * extern function start with ex_btc8821a2ant_ + **************************************************************/ +void ex_btc8821a2ant_init_hwconfig(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 u1tmp = 0; @@ -2302,13 +2308,11 @@ void ex_halbtc8821a2ant_init_hwconfig(struct btc_coexist *btcoexist) btcoexist->btc_write_1byte(btcoexist, 0x790, u1tmp); /*Antenna config */ - halbtc8821a2ant_set_ant_path(btcoexist, - BTC_ANT_WIFI_AT_MAIN, true, false); + btc8821a2ant_set_ant_path(btcoexist, BTC_ANT_WIFI_AT_MAIN, true, false); /* PTA parameter */ - halbtc8821a2ant_coex_table(btcoexist, - FORCE_EXEC, 0x55555555, 0x55555555, - 0xffff, 0x3); + btc8821a2ant_coex_table(btcoexist, FORCE_EXEC, 0x55555555, 0x55555555, + 0xffff, 0x3); /* Enable counter statistics */ /*0x76e[3] = 1, WLAN_Act control by PTA*/ @@ -2317,20 +2321,17 @@ void ex_halbtc8821a2ant_init_hwconfig(struct btc_coexist *btcoexist) btcoexist->btc_write_1byte_bitmask(btcoexist, 0x40, 0x20, 0x1); } -void ex_halbtc8821a2ant_init_coex_dm(struct btc_coexist *btcoexist) +void ex_btc8821a2ant_init_coex_dm(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Coex Mechanism Init!!\n"); - halbtc8821a2ant_init_coex_dm(btcoexist); + btc8821a2ant_init_coex_dm(btcoexist); } -void -ex_halbtc8821a2ant_display_coex_info( - struct btc_coexist *btcoexist - ) +void ex_btc8821a2ant_display_coex_info(struct btc_coexist *btcoexist) { struct btc_board_info *board_info = &btcoexist->board_info; struct btc_stack_info *stack_info = &btcoexist->stack_info; @@ -2564,7 +2565,7 @@ ex_halbtc8821a2ant_display_coex_info( btcoexist->btc_disp_dbg_msg(btcoexist, BTC_DBG_DISP_COEX_STATISTICS); } -void ex_halbtc8821a2ant_ips_notify(struct btc_coexist *btcoexist, u8 type) +void ex_btc8821a2ant_ips_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2572,16 +2573,15 @@ void ex_halbtc8821a2ant_ips_notify(struct btc_coexist *btcoexist, u8 type) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], IPS ENTER notify\n"); coex_sta->under_ips = true; - halbtc8821a2ant_coex_all_off(btcoexist); + btc8821a2ant_coex_all_off(btcoexist); } else if (BTC_IPS_LEAVE == type) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], IPS LEAVE notify\n"); coex_sta->under_ips = false; - /*halbtc8821a2ant_init_coex_dm(btcoexist);*/ } } -void ex_halbtc8821a2ant_lps_notify(struct btc_coexist *btcoexist, u8 type) +void ex_btc8821a2ant_lps_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2596,7 +2596,7 @@ void ex_halbtc8821a2ant_lps_notify(struct btc_coexist *btcoexist, u8 type) } } -void ex_halbtc8821a2ant_scan_notify(struct btc_coexist *btcoexist, u8 type) +void ex_btc8821a2ant_scan_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2609,7 +2609,7 @@ void ex_halbtc8821a2ant_scan_notify(struct btc_coexist *btcoexist, u8 type) } } -void ex_halbtc8821a2ant_connect_notify(struct btc_coexist *btcoexist, u8 type) +void ex_btc8821a2ant_connect_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2622,13 +2622,13 @@ void ex_halbtc8821a2ant_connect_notify(struct btc_coexist *btcoexist, u8 type) } } -void ex_halbtc8821a2ant_media_status_notify(struct btc_coexist *btcoexist, - u8 type) +void ex_btc8821a2ant_media_status_notify(struct btc_coexist *btcoexist, + u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; - u8 h2c_parameter[3] = {0}; - u32 wifi_bw; - u8 wifi_central_chnl; + u8 h2c_parameter[3] = {0}; + u32 wifi_bw; + u8 wifi_central_chnl; if (BTC_MEDIA_CONNECT == type) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -2638,7 +2638,7 @@ void ex_halbtc8821a2ant_media_status_notify(struct btc_coexist *btcoexist, "[BTCoex], MEDIA disconnect notify\n"); } - /* only 2.4G we need to inform bt the chnl mask*/ + /* only 2.4G we need to inform bt the chnl mask */ btcoexist->btc_get(btcoexist, BTC_GET_U1_WIFI_CENTRAL_CHNL, &wifi_central_chnl); if ((BTC_MEDIA_CONNECT == type) && @@ -2665,8 +2665,9 @@ void ex_halbtc8821a2ant_media_status_notify(struct btc_coexist *btcoexist, btcoexist->btc_fill_h2c(btcoexist, 0x66, 3, h2c_parameter); } -void ex_halbtc8821a2ant_special_packet_notify(struct btc_coexist *btcoexist, - u8 type) { +void ex_btc8821a2ant_special_packet_notify(struct btc_coexist *btcoexist, + u8 type) +{ struct rtl_priv *rtlpriv = btcoexist->adapter; if (type == BTC_PACKET_DHCP) { @@ -2675,18 +2676,18 @@ void ex_halbtc8821a2ant_special_packet_notify(struct btc_coexist *btcoexist, } } -void ex_halbtc8821a2ant_bt_info_notify(struct btc_coexist *btcoexist, - u8 *tmp_buf, u8 length) +void ex_btc8821a2ant_bt_info_notify(struct btc_coexist *btcoexist, + u8 *tmp_buf, u8 length) { struct rtl_priv *rtlpriv = btcoexist->adapter; - u8 bt_info = 0; - u8 i, rsp_source = 0; - bool bt_busy = false, limited_dig = false; - bool wifi_connected = false, bt_hs_on = false; + u8 bt_info = 0; + u8 i, rsp_source = 0; + bool bt_busy = false, limited_dig = false; + bool wifi_connected = false, bt_hs_on = false; coex_sta->c2h_bt_info_req_sent = false; - rsp_source = tmp_buf[0]&0xf; + rsp_source = tmp_buf[0] & 0xf; if (rsp_source >= BT_INFO_SRC_8821A_2ANT_MAX) rsp_source = BT_INFO_SRC_8821A_2ANT_WIFI_FW; coex_sta->bt_info_c2h_cnt[rsp_source]++; @@ -2698,7 +2699,7 @@ void ex_halbtc8821a2ant_bt_info_notify(struct btc_coexist *btcoexist, coex_sta->bt_info_c2h[rsp_source][i] = tmp_buf[i]; if (i == 1) bt_info = tmp_buf[i]; - if (i == length-1) { + if (i == length - 1) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "0x%02x]\n", tmp_buf[i]); } else { @@ -2708,7 +2709,8 @@ void ex_halbtc8821a2ant_bt_info_notify(struct btc_coexist *btcoexist, } if (BT_INFO_SRC_8821A_2ANT_WIFI_FW != rsp_source) { - coex_sta->bt_retry_cnt = /* [3:0]*/ + /* [3:0] */ + coex_sta->bt_retry_cnt = coex_sta->bt_info_c2h[rsp_source][2]&0xf; coex_sta->bt_rssi = @@ -2717,24 +2719,25 @@ void ex_halbtc8821a2ant_bt_info_notify(struct btc_coexist *btcoexist, coex_sta->bt_info_ext = coex_sta->bt_info_c2h[rsp_source][4]; - /* Here we need to resend some wifi info to BT*/ - /* because bt is reset and loss of the info.*/ + /* Here we need to resend some wifi info to BT + * because bt is reset and loss of the info + */ if ((coex_sta->bt_info_ext & BIT1)) { btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); if (wifi_connected) { - ex_halbtc8821a2ant_media_status_notify(btcoexist, + ex_btc8821a2ant_media_status_notify(btcoexist, BTC_MEDIA_CONNECT); } else { - ex_halbtc8821a2ant_media_status_notify(btcoexist, + ex_btc8821a2ant_media_status_notify(btcoexist, BTC_MEDIA_DISCONNECT); } } if ((coex_sta->bt_info_ext & BIT3)) { - halbtc8821a2ant_ignore_wlan_act(btcoexist, - FORCE_EXEC, false); + btc8821a2ant_ignore_wlan_act(btcoexist, + FORCE_EXEC, false); } else { /* BT already NOT ignore Wlan active, do nothing here.*/ } @@ -2798,27 +2801,27 @@ void ex_halbtc8821a2ant_bt_info_notify(struct btc_coexist *btcoexist, btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_LIMITED_DIG, &limited_dig); - halbtc8821a2ant_run_coexist_mechanism(btcoexist); + btc8821a2ant_run_coexist_mechanism(btcoexist); } -void ex_halbtc8821a2ant_halt_notify(struct btc_coexist *btcoexist) +void ex_btc8821a2ant_halt_notify(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Halt notify\n"); - halbtc8821a2ant_ignore_wlan_act(btcoexist, FORCE_EXEC, true); - ex_halbtc8821a2ant_media_status_notify(btcoexist, BTC_MEDIA_DISCONNECT); + btc8821a2ant_ignore_wlan_act(btcoexist, FORCE_EXEC, true); + ex_btc8821a2ant_media_status_notify(btcoexist, BTC_MEDIA_DISCONNECT); } -void ex_halbtc8821a2ant_periodical(struct btc_coexist *btcoexist) +void ex_btc8821a2ant_periodical(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; - static u8 dis_ver_info_cnt; - u32 fw_ver = 0, bt_patch_ver = 0; + static u8 dis_ver_info_cnt; struct btc_board_info *board_info = &btcoexist->board_info; struct btc_stack_info *stack_info = &btcoexist->stack_info; + u32 fw_ver = 0, bt_patch_ver = 0; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], ==========================Periodical===========================\n"); @@ -2847,6 +2850,6 @@ void ex_halbtc8821a2ant_periodical(struct btc_coexist *btcoexist) "[BTCoex], ****************************************************************\n"); } - halbtc8821a2ant_query_bt_info(btcoexist); - halbtc8821a2ant_monitor_bt_ctr(btcoexist); + btc8821a2ant_query_bt_info(btcoexist); + btc8821a2ant_monitor_bt_ctr(btcoexist); } diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c index 150aeb8e79d1..f13000612913 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c @@ -466,7 +466,7 @@ static bool halbtc_set(void *void_btcoexist, u8 set_type, void *in_buf) case BTC_SET_ACT_DISABLE_LOW_POWER: halbtc_disable_low_power(); break; - case BTC_SET_ACT_UPDATE_ra_mask: + case BTC_SET_ACT_UPDATE_RAMASK: btcoexist->bt_info.ra_mask = *u32_tmp; break; case BTC_SET_ACT_SEND_MIMO_PS: diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h index 601bbe1d22b3..f74864f54edb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h @@ -275,7 +275,7 @@ enum btc_set_type { BTC_SET_ACT_NORMAL_LPS, BTC_SET_ACT_INC_FORCE_EXEC_PWR_CMD_CNT, BTC_SET_ACT_DISABLE_LOW_POWER, - BTC_SET_ACT_UPDATE_ra_mask, + BTC_SET_ACT_UPDATE_RAMASK, BTC_SET_ACT_SEND_MIMO_PS, /* BT Coex related */ BTC_SET_ACT_CTRL_BT_INFO, -- cgit From 155305f5b0eb4f849f2552a45578b35c5998d399 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 3 Apr 2017 13:41:33 -0500 Subject: rtlwifi: btcoex: 23b 2ant: add btc8723b2ant_limited_rx to reduce agg size For wifi and bt coexistence, if the aggregation size of wifi is too large, the transmission time of the aggregated packet could be too long and the bt packets might "cut off" the wifi packet. We need to reduce the aggregation size to improve wifi performance. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 9b09b94cbb43..b182c61edf91 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -240,6 +240,29 @@ static u8 btc8723b2ant_wifi_rssi_state(struct btc_coexist *btcoexist, return wifi_rssi_state; } +static +void btc8723b2ant_limited_rx(struct btc_coexist *btcoexist, bool force_exec, + bool rej_ap_agg_pkt, bool bt_ctrl_agg_buf_size, + u8 agg_buf_size) +{ + bool reject_rx_agg = rej_ap_agg_pkt; + bool bt_ctrl_rx_agg_size = bt_ctrl_agg_buf_size; + u8 rx_agg_size = agg_buf_size; + + /* ============================================ */ + /* Rx Aggregation related setting */ + /* ============================================ */ + btcoexist->btc_set(btcoexist, BTC_SET_BL_TO_REJ_AP_AGG_PKT, + &reject_rx_agg); + /* decide BT control aggregation buf size or not */ + btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_CTRL_AGG_SIZE, + &bt_ctrl_rx_agg_size); + /* aggregate buf size, only work when BT control Rx aggregate size */ + btcoexist->btc_set(btcoexist, BTC_SET_U1_AGG_BUF_SIZE, &rx_agg_size); + /* real update aggregation setting */ + btcoexist->btc_set(btcoexist, BTC_SET_ACT_AGGREGATE_CTRL, NULL); +} + static void btc8723b2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -1266,6 +1289,8 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) low_pwr_disable = false; btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, + false, false, 0x8); RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi non-connected idle!!\n"); @@ -1288,6 +1313,8 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, + false, false, 0x8); RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi connected + BT non connected-idle!!\n"); @@ -1317,6 +1344,8 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) return false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi connected + BT connected-idle!!\n"); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, + false, false, 0x8); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); @@ -1576,6 +1605,7 @@ static void btc8723b2ant_action_sco(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 4); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); @@ -1623,6 +1653,7 @@ static void btc8723b2ant_action_hid(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, true, 0x5); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); @@ -1701,6 +1732,7 @@ static void btc8723b2ant_action_a2dp(struct btc_coexist *btcoexist) } btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); @@ -1746,6 +1778,7 @@ static void btc8723b2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); @@ -1786,6 +1819,7 @@ static void btc8723b2ant_action_pan_edr(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) @@ -1835,6 +1869,7 @@ static void btc8723b2ant_action_pan_hs(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || @@ -1880,6 +1915,7 @@ static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) @@ -1935,6 +1971,8 @@ static void btc8723b2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, 29, 0); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + if (BTC_RSSI_HIGH(bt_rssi_state)) btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); else @@ -2000,6 +2038,7 @@ static void btc8723b2ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) @@ -2056,6 +2095,7 @@ static void btc8723b2ant_action_hid_a2dp(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); -- cgit From 61d802619bc6cd0ee0849a57eec34605d35c2b8e Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 3 Apr 2017 13:41:34 -0500 Subject: rtlwifi: btcoex: 23b 2ant: detect if bt is slave, and modify tdma if the bt is slave, it may receive packet at any time, so we need to mark them as high priority packets to avoid packet loss. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 91 +++++++++++++++------- .../realtek/rtlwifi/btcoexist/halbtcoutsrc.h | 1 + 2 files changed, 62 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index b182c61edf91..2c9f626c3ec7 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -266,6 +266,7 @@ void btc8723b2ant_limited_rx(struct btc_coexist *btcoexist, bool force_exec, static void btc8723b2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; u32 reg_hp_txrx, reg_lp_txrx, u32tmp; u32 reg_hp_tx = 0, reg_hp_rx = 0; u32 reg_lp_tx = 0, reg_lp_rx = 0; @@ -286,6 +287,17 @@ static void btc8723b2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) coex_sta->low_priority_tx = reg_lp_tx; coex_sta->low_priority_rx = reg_lp_rx; + if ((coex_sta->low_priority_tx > 1050) && + (!coex_sta->c2h_bt_inquiry_page)) + coex_sta->pop_event_cnt++; + + if ((coex_sta->low_priority_rx >= 950) && + (coex_sta->low_priority_rx >= coex_sta->low_priority_tx) && + (!coex_sta->under_ips)) + bt_link_info->slave_role = true; + else + bt_link_info->slave_role = false; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], High Priority Tx/Rx(reg 0x%x)=0x%x(%d)/0x%x(%d)\n", reg_hp_txrx, reg_hp_tx, reg_hp_tx, reg_hp_rx, reg_hp_rx); @@ -1084,6 +1096,8 @@ static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec, bool turn_on, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; + u8 tdma_byte4_modify = 0x0; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], %s turn %s PS TDMA, type=%d\n", @@ -1104,75 +1118,92 @@ static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec, (coex_dm->pre_ps_tdma == coex_dm->cur_ps_tdma)) return; } + + if ((bt_link_info->slave_role) && (bt_link_info->a2dp_exist)) + /* 0x778 = 0x1 at wifi slot (no blocking BT Low-Pri pkts) */ + tdma_byte4_modify = 0x1; + if (turn_on) { switch (type) { case 1: default: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0xe1, 0x90); + btc8723b2ant_set_fw_ps_tdma( + btcoexist, 0xe3, 0x3c, + 0x03, 0xf1, 0x90 | tdma_byte4_modify); break; case 2: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, - 0x12, 0xe1, 0x90); + btc8723b2ant_set_fw_ps_tdma( + btcoexist, 0xe3, 0x2d, + 0x03, 0xf1, 0x90 | tdma_byte4_modify); break; case 3: - /* This call breaks BT when wireless is active - - * comment it out for now until a better fix is found: - * btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, - * 0x3, 0xf1, 0x90); - */ + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, + 0x3, 0xf1, + 0x90 | tdma_byte4_modify); break; case 4: btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x10, - 0x03, 0xf1, 0x90); + 0x03, 0xf1, + 0x90 | tdma_byte4_modify); break; case 5: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0x60, 0x90); + btc8723b2ant_set_fw_ps_tdma( + btcoexist, 0xe3, 0x3c, + 0x3, 0x70, 0x90 | tdma_byte4_modify); break; case 6: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, - 0x12, 0x60, 0x90); + btc8723b2ant_set_fw_ps_tdma( + btcoexist, 0xe3, 0x2d, + 0x3, 0x70, 0x90 | tdma_byte4_modify); break; case 7: btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, - 0x3, 0x70, 0x90); + 0x3, 0x70, + 0x90 | tdma_byte4_modify); break; case 8: btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xa3, 0x10, - 0x3, 0x70, 0x90); + 0x3, 0x70, + 0x90 | tdma_byte4_modify); break; case 9: btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, 0x1a, 0xe1, 0x90); break; case 10: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, - 0x12, 0xe1, 0x90); + btc8723b2ant_set_fw_ps_tdma( + btcoexist, 0xe3, 0x2d, + 0x03, 0xf1, 0x90 | tdma_byte4_modify); break; case 11: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0xa, - 0xa, 0xe1, 0x90); + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, + 0x3, 0xf1, + 0x90 | tdma_byte4_modify); break; case 12: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, - 0x5, 0xe1, 0x90); + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x10, + 0x3, 0xf1, + 0x90 | tdma_byte4_modify); break; case 13: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0x60, 0x90); + btc8723b2ant_set_fw_ps_tdma( + btcoexist, 0xe3, 0x3c, + 0x3, 0x70, 0x90 | tdma_byte4_modify); break; case 14: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, - 0x12, 0x60, 0x90); + btc8723b2ant_set_fw_ps_tdma( + btcoexist, 0xe3, 0x2d, + 0x3, 0x70, 0x90 | tdma_byte4_modify); break; case 15: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0xa, - 0xa, 0x60, 0x90); + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, + 0x3, 0x70, + 0x90 | tdma_byte4_modify); break; case 16: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, - 0x5, 0x60, 0x90); + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x10, + 0x3, 0x70, + 0x90 | tdma_byte4_modify); break; case 17: btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xa3, 0x2f, diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h index f74864f54edb..d7ba6ad1e66f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h @@ -459,6 +459,7 @@ struct btc_bt_link_info { bool hid_only; bool pan_exist; bool pan_only; + bool slave_role; }; enum btc_antenna_pos { -- cgit From 55e9e37da2cea6b4248610397165f3b59cb34e51 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 3 Apr 2017 13:41:35 -0500 Subject: rtlwifi: btcoex: 23b 2ant: monitor wifi counter for allocate tdma time If there are too many CRC error packets, it means the wifi is operating under low quality circumstances. If so, we need to reallocate the resources of wifi and bt Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 40 ++++++++++++++++++++++ .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.h | 10 ++++++ 2 files changed, 50 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 2c9f626c3ec7..e9c692a5b13e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -309,6 +309,43 @@ static void btc8723b2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc); } +static void btc8723b2ant_monitor_wifi_ctr(struct btc_coexist *btcoexist) +{ + if (coex_sta->under_ips) { + coex_sta->crc_ok_cck = 0; + coex_sta->crc_ok_11g = 0; + coex_sta->crc_ok_11n = 0; + coex_sta->crc_ok_11n_agg = 0; + + coex_sta->crc_err_cck = 0; + coex_sta->crc_err_11g = 0; + coex_sta->crc_err_11n = 0; + coex_sta->crc_err_11n_agg = 0; + } else { + coex_sta->crc_ok_cck = + btcoexist->btc_read_4byte(btcoexist, 0xf88); + coex_sta->crc_ok_11g = + btcoexist->btc_read_2byte(btcoexist, 0xf94); + coex_sta->crc_ok_11n = + btcoexist->btc_read_2byte(btcoexist, 0xf90); + coex_sta->crc_ok_11n_agg = + btcoexist->btc_read_2byte(btcoexist, 0xfb8); + + coex_sta->crc_err_cck = + btcoexist->btc_read_4byte(btcoexist, 0xf84); + coex_sta->crc_err_11g = + btcoexist->btc_read_2byte(btcoexist, 0xf96); + coex_sta->crc_err_11n = + btcoexist->btc_read_2byte(btcoexist, 0xf92); + coex_sta->crc_err_11n_agg = + btcoexist->btc_read_2byte(btcoexist, 0xfba); + } + + /* reset counter */ + btcoexist->btc_write_1byte_bitmask(btcoexist, 0xf16, 0x1, 0x1); + btcoexist->btc_write_1byte_bitmask(btcoexist, 0xf16, 0x1, 0x0); +} + static void btc8723b2ant_query_bt_info(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2875,6 +2912,9 @@ void ex_btc8723b2ant_periodical(struct btc_coexist *btcoexist) #if (BT_AUTO_REPORT_ONLY_8723B_2ANT == 0) btc8723b2ant_query_bt_info(btcoexist); #else + btc8723b2ant_monitor_bt_ctr(btcoexist); + btc8723b2ant_monitor_wifi_ctr(btcoexist); + if (btc8723b2ant_is_wifi_status_changed(btcoexist) || coex_dm->auto_tdma_adjust) btc8723b2ant_run_coexist_mechanism(btcoexist); diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h index d787ae3bced2..d1bfdbec82b0 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h @@ -149,6 +149,16 @@ struct coex_sta_8723b_2ant { u8 bt_retry_cnt; u8 bt_info_ext; u32 pop_event_cnt; + + u32 crc_ok_cck; + u32 crc_ok_11g; + u32 crc_ok_11n; + u32 crc_ok_11n_agg; + + u32 crc_err_cck; + u32 crc_err_11g; + u32 crc_err_11n; + u32 crc_err_11n_agg; }; /********************************************************************* -- cgit From e4782bc6ce9a0a3586e1556351461fd3141b203b Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 3 Apr 2017 13:41:36 -0500 Subject: rtlwifi: btcoex: 23b 2ant: less sensitive to tx rate penalty modify h2c parameter so that the rate suffers less penalty from retry Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index e9c692a5b13e..2264b24fea9a 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -754,9 +754,9 @@ static void btc8723b_set_penalty_txrate(struct btc_coexist *btcoexist, h2c_parameter[1] |= BIT0; /* normal rate except MCS7/6/5, OFDM54/48/36 */ h2c_parameter[2] = 0x00; - h2c_parameter[3] = 0xf7; /* MCS7 or OFDM54 */ - h2c_parameter[4] = 0xf8; /* MCS6 or OFDM48 */ - h2c_parameter[5] = 0xf9; /* MCS5 or OFDM36 */ + h2c_parameter[3] = 0xf4; /* MCS7 or OFDM54 */ + h2c_parameter[4] = 0xf5; /* MCS6 or OFDM48 */ + h2c_parameter[5] = 0xf6; /* MCS5 or OFDM36 */ } RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, -- cgit From 3f957062c96c7c5ecfc59a72626d0ba419218d19 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 3 Apr 2017 13:41:37 -0500 Subject: rtlwifi: btcoex: 23b 2ant: adjust wifi duration for bt a2dp The larger the bt a2dp bit pool is, the more time bt needs to receive them. If we do not adjust the wifi duration, the voice quality will be low. Hence we reduce the time that wifi holds, to improve the a2dp service. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 72 +++++++++++++++++++--- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.h | 3 + 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 2264b24fea9a..57eed18e19c8 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1134,6 +1134,7 @@ static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec, { struct rtl_priv *rtlpriv = btcoexist->adapter; struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; + s8 wifi_duration_adjust = 0x0; u8 tdma_byte4_modify = 0x0; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -1156,6 +1157,36 @@ static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec, return; } + if (coex_sta->scan_ap_num <= 5) { + if (coex_sta->a2dp_bit_pool >= 45) + wifi_duration_adjust = -15; + else if (coex_sta->a2dp_bit_pool >= 35) + wifi_duration_adjust = -10; + else + wifi_duration_adjust = 5; + } else if (coex_sta->scan_ap_num <= 20) { + if (coex_sta->a2dp_bit_pool >= 45) + wifi_duration_adjust = -15; + else if (coex_sta->a2dp_bit_pool >= 35) + wifi_duration_adjust = -10; + else + wifi_duration_adjust = 0; + } else if (coex_sta->scan_ap_num <= 40) { + if (coex_sta->a2dp_bit_pool >= 45) + wifi_duration_adjust = -15; + else if (coex_sta->a2dp_bit_pool >= 35) + wifi_duration_adjust = -10; + else + wifi_duration_adjust = -5; + } else { + if (coex_sta->a2dp_bit_pool >= 45) + wifi_duration_adjust = -15; + else if (coex_sta->a2dp_bit_pool >= 35) + wifi_duration_adjust = -10; + else + wifi_duration_adjust = -10; + } + if ((bt_link_info->slave_role) && (bt_link_info->a2dp_exist)) /* 0x778 = 0x1 at wifi slot (no blocking BT Low-Pri pkts) */ tdma_byte4_modify = 0x1; @@ -1204,8 +1235,9 @@ static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec, 0x90 | tdma_byte4_modify); break; case 9: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0xe1, 0x90); + btc8723b2ant_set_fw_ps_tdma( + btcoexist, 0xe3, 0x3c + wifi_duration_adjust, + 0x03, 0xf1, 0x90 | tdma_byte4_modify); break; case 10: btc8723b2ant_set_fw_ps_tdma( @@ -1247,8 +1279,8 @@ static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec, 0x2f, 0x60, 0x90); break; case 18: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, - 0x5, 0xe1, 0x90); + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, 0x5, + 0xe1, 0x90); break; case 19: btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x25, @@ -1263,8 +1295,25 @@ static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec, 0x03, 0x70, 0x90); break; case 71: - btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0xe1, 0x90); + btc8723b2ant_set_fw_ps_tdma( + btcoexist, 0xe3, 0x3c + wifi_duration_adjust, + 0x03, 0xf1, 0x90); + break; + case 101: + case 105: + case 113: + case 171: + btc8723b2ant_set_fw_ps_tdma( + btcoexist, 0xd3, 0x3a + wifi_duration_adjust, + 0x03, 0x70, 0x50 | tdma_byte4_modify); + break; + case 102: + case 106: + case 110: + case 114: + btc8723b2ant_set_fw_ps_tdma( + btcoexist, 0xd3, 0x2d + wifi_duration_adjust, + 0x03, 0x70, 0x50 | tdma_byte4_modify); break; } } else { @@ -1733,7 +1782,6 @@ static void btc8723b2ant_action_hid(struct btc_coexist *btcoexist) /* for HID quality & wifi performance balance at 11n mode */ btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 9); - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); @@ -2448,7 +2496,6 @@ void ex_btc8723b2ant_display_coex_info(struct btc_coexist *btcoexist) ((wifi_traffic_dir == BTC_WIFI_TRAFFIC_TX) ? "uplink" : "downlink"))); - RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d / %d / %d / %d", "SCO/HID/PAN/A2DP", bt_link_info->sco_exist, bt_link_info->hid_exist, @@ -2636,6 +2683,8 @@ void ex_btc8723b2ant_scan_notify(struct btc_coexist *btcoexist, u8 type) else if (BTC_SCAN_FINISH == type) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCAN FINISH notify\n"); + btcoexist->btc_get(btcoexist, BTC_GET_U1_AP_NUM, + &coex_sta->scan_ap_num); } void ex_btc8723b2ant_connect_notify(struct btc_coexist *btcoexist, u8 type) @@ -2746,8 +2795,11 @@ void ex_btc8723b2ant_bt_info_notify(struct btc_coexist *btcoexist, coex_sta->bt_rssi = coex_sta->bt_info_c2h[rsp_source][3] * 2 + 10; - coex_sta->bt_info_ext = - coex_sta->bt_info_c2h[rsp_source][4]; + if (coex_sta->bt_info_c2h[rsp_source][1] == 0x49) + coex_sta->a2dp_bit_pool = + coex_sta->bt_info_c2h[rsp_source][6]; + else + coex_sta->a2dp_bit_pool = 0; /* Here we need to resend some wifi info to BT * because BT is reset and loss of the info. diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h index d1bfdbec82b0..e36ab4205771 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h @@ -149,6 +149,7 @@ struct coex_sta_8723b_2ant { u8 bt_retry_cnt; u8 bt_info_ext; u32 pop_event_cnt; + u8 scan_ap_num; u32 crc_ok_cck; u32 crc_ok_11g; @@ -159,6 +160,8 @@ struct coex_sta_8723b_2ant { u32 crc_err_11g; u32 crc_err_11n; u32 crc_err_11n_agg; + + u8 a2dp_bit_pool; }; /********************************************************************* -- cgit From f36714fbfa6d6002e57d879469e9ae84e052e972 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 3 Apr 2017 13:41:38 -0500 Subject: rtlwifi: btcoex: 23b 2ant: coex table fine tune set coex table fine tune, for register settings Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 52 +++++++++++++--------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 57eed18e19c8..4139ddf6022b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -915,55 +915,67 @@ static void btc8723b2ant_coex_table_with_type(struct btc_coexist *btcoexist, switch (type) { case 0: btc8723b2ant_coex_table(btcoexist, force_exec, 0x55555555, - 0x55555555, 0xffff, 0x3); + 0x55555555, 0xffffff, 0x3); break; case 1: btc8723b2ant_coex_table(btcoexist, force_exec, 0x55555555, - 0x5afa5afa, 0xffff, 0x3); + 0x5afa5afa, 0xffffff, 0x3); break; case 2: - btc8723b2ant_coex_table(btcoexist, force_exec, 0x5a5a5a5a, - 0x5a5a5a5a, 0xffff, 0x3); + btc8723b2ant_coex_table(btcoexist, force_exec, 0x5ada5ada, + 0x5ada5ada, 0xffffff, 0x3); break; case 3: btc8723b2ant_coex_table(btcoexist, force_exec, 0xaaaaaaaa, - 0xaaaaaaaa, 0xffff, 0x3); + 0xaaaaaaaa, 0xffffff, 0x3); break; case 4: btc8723b2ant_coex_table(btcoexist, force_exec, 0xffffffff, - 0xffffffff, 0xffff, 0x3); + 0xffffffff, 0xffffff, 0x3); break; case 5: btc8723b2ant_coex_table(btcoexist, force_exec, 0x5fff5fff, - 0x5fff5fff, 0xffff, 0x3); + 0x5fff5fff, 0xffffff, 0x3); break; case 6: btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff, - 0x5a5a5a5a, 0xffff, 0x3); + 0x5a5a5a5a, 0xffffff, 0x3); break; case 7: - btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff, - 0x5afa5afa, 0xffff, 0x3); + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0x5ada5ada, 0xffffff, 0x3); break; case 8: - btc8723b2ant_coex_table(btcoexist, force_exec, 0x5aea5aea, - 0x5aea5aea, 0xffff, 0x3); + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0x5ada5ada, 0xffffff, 0x3); break; case 9: - btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff, - 0x5aea5aea, 0xffff, 0x3); + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0x5ada5ada, 0xffffff, 0x3); break; case 10: - btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff, - 0x5aff5aff, 0xffff, 0x3); + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0x5ada5ada, 0xffffff, 0x3); break; case 11: - btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff, - 0x5a5f5a5f, 0xffff, 0x3); + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0x5ada5ada, 0xffffff, 0x3); break; case 12: - btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff, - 0x5f5f5f5f, 0xffff, 0x3); + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0x5ada5ada, 0xffffff, 0x3); + break; + case 13: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x5fff5fff, + 0xaaaaaaaa, 0xffffff, 0x3); + break; + case 14: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x5fff5fff, + 0x5ada5ada, 0xffffff, 0x3); + break; + case 15: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0xaaaaaaaa, 0xffffff, 0x3); break; default: break; -- cgit From 7ff98093220b12e611aa945e5aec03f50bc13907 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 3 Apr 2017 13:41:39 -0500 Subject: rtlwifi: btcoex: 23b 2ant: remove redundant bt stack report New ICs have hardware mailbox to deliver bt information instead of doing it by driver itself, so remove them. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 4139ddf6022b..e72baa00b42f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -399,11 +399,9 @@ static bool btc8723b2ant_is_wifi_status_changed(struct btc_coexist *btcoexist) static void btc8723b2ant_update_bt_link_info(struct btc_coexist *btcoexist) { - /*struct btc_stack_info *stack_info = &btcoexist->stack_info;*/ struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; bool bt_hs_on = false; -#if (BT_AUTO_REPORT_ONLY_8723B_2ANT == 1) /* profile from bt patch */ btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); bt_link_info->bt_link_exist = coex_sta->bt_link_exist; @@ -417,21 +415,7 @@ static void btc8723b2ant_update_bt_link_info(struct btc_coexist *btcoexist) bt_link_info->pan_exist = true; bt_link_info->bt_link_exist = true; } -#else /* profile from bt stack */ - bt_link_info->bt_link_exist = stack_info->bt_link_exist; - bt_link_info->sco_exist = stack_info->sco_exist; - bt_link_info->a2dp_exist = stack_info->a2dp_exist; - bt_link_info->pan_exist = stack_info->pan_exist; - bt_link_info->hid_exist = stack_info->hid_exist; - - /*for win-8 stack HID report error*/ - if (!stack_info->hid_exist) - stack_info->hid_exist = coex_sta->hid_exist; - /*sync BTInfo with BT firmware and stack*/ - /* when stack HID report error, here we use the info from bt fw.*/ - if (!stack_info->bt_link_exist) - stack_info->bt_link_exist = coex_sta->bt_link_exist; -#endif + /* check if Sco only */ if (bt_link_info->sco_exist && !bt_link_info->a2dp_exist && !bt_link_info->pan_exist && !bt_link_info->hid_exist) -- cgit From 44e9a5340e6859a2a00cc470a8acfcbcaf64050a Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 3 Apr 2017 13:41:40 -0500 Subject: rtlwifi: btcoex: 23b 2ant: finer bt power adjustment The bt can specify a power level to decrease. Rather than decreasing by a fixed value (usually 2), this change makes the driver fit to the environment more quickly. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 52 ++++++++++------------ .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.h | 4 +- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index e72baa00b42f..b1a4c8466bc6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -660,44 +660,40 @@ static void btc8723b2ant_set_fw_dac_swing_level(struct btc_coexist *btcoexist, } static void btc8723b2ant_set_fw_dec_bt_pwr(struct btc_coexist *btcoexist, - bool dec_bt_pwr) + u8 dec_bt_pwr_lvl) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[1] = {0}; - h2c_parameter[0] = 0; - - if (dec_bt_pwr) - h2c_parameter[0] |= BIT1; + h2c_parameter[0] = dec_bt_pwr_lvl; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], decrease Bt Power : %s, FW write 0x62=0x%x\n", - (dec_bt_pwr ? "Yes!!" : "No!!"), h2c_parameter[0]); + "[BTCoex], decrease Bt Power Level : %u\n", dec_bt_pwr_lvl); btcoexist->btc_fill_h2c(btcoexist, 0x62, 1, h2c_parameter); } static void btc8723b2ant_dec_bt_pwr(struct btc_coexist *btcoexist, - bool force_exec, bool dec_bt_pwr) + bool force_exec, u8 dec_bt_pwr_lvl) { struct rtl_priv *rtlpriv = btcoexist->adapter; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], %s Dec BT power = %s\n", - force_exec ? "force to" : "", dec_bt_pwr ? "ON" : "OFF"); - coex_dm->cur_dec_bt_pwr = dec_bt_pwr; + "[BTCoex], Dec BT power level = %u\n", dec_bt_pwr_lvl); + coex_dm->cur_dec_bt_pwr_lvl = dec_bt_pwr_lvl; if (!force_exec) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], bPreDecBtPwr=%d, bCurDecBtPwr=%d\n", - coex_dm->pre_dec_bt_pwr, coex_dm->cur_dec_bt_pwr); + "[BTCoex], PreDecBtPwrLvl=%d, CurDecBtPwrLvl=%d\n", + coex_dm->pre_dec_bt_pwr_lvl, + coex_dm->cur_dec_bt_pwr_lvl); - if (coex_dm->pre_dec_bt_pwr == coex_dm->cur_dec_bt_pwr) + if (coex_dm->pre_dec_bt_pwr_lvl == coex_dm->cur_dec_bt_pwr_lvl) return; } - btc8723b2ant_set_fw_dec_bt_pwr(btcoexist, coex_dm->cur_dec_bt_pwr); + btc8723b2ant_set_fw_dec_bt_pwr(btcoexist, coex_dm->cur_dec_bt_pwr_lvl); - coex_dm->pre_dec_bt_pwr = coex_dm->cur_dec_bt_pwr; + coex_dm->pre_dec_bt_pwr_lvl = coex_dm->cur_dec_bt_pwr_lvl; } static void btc8723b2ant_fw_dac_swing_lvl(struct btc_coexist *btcoexist, @@ -1340,7 +1336,7 @@ static void btc8723b2ant_coex_alloff(struct btc_coexist *btcoexist) /* fw all off */ btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); /* sw all off */ btc8723b2ant_sw_mechanism(btcoexist, false, false, false, false); @@ -1356,7 +1352,7 @@ static void btc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist) btc8723b2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1); btc8723b2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6); - btc8723b2ant_dec_bt_pwr(btcoexist, FORCE_EXEC, false); + btc8723b2ant_dec_bt_pwr(btcoexist, FORCE_EXEC, 0); btc8723b2ant_sw_mechanism(btcoexist, false, false, false, false); @@ -1381,7 +1377,7 @@ static void btc8723b2ant_action_bt_inquiry(struct btc_coexist *btcoexist) btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); } btc8723b2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6); - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); btc8723b2ant_sw_mechanism(btcoexist, false, false, false, false); } @@ -1413,7 +1409,7 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); btc8723b2ant_sw_mechanism(btcoexist, false, false, false, false); @@ -1439,8 +1435,7 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 0xb); - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, - false); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); btc8723b2ant_sw_mechanism(btcoexist, false, false, false, false); @@ -1467,8 +1462,7 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 0xb); - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, - false); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); btc8723b2ant_sw_mechanism(btcoexist, true, false, false, false); @@ -1988,7 +1982,7 @@ static void btc8723b2ant_action_pan_hs(struct btc_coexist *btcoexist) (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); else - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); @@ -2033,7 +2027,7 @@ static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) if (BTC_RSSI_HIGH(bt_rssi_state)) btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); else - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); @@ -2088,7 +2082,7 @@ static void btc8723b2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) if (BTC_RSSI_HIGH(bt_rssi_state)) btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); else - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { @@ -2156,7 +2150,7 @@ static void btc8723b2ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) if (BTC_RSSI_HIGH(bt_rssi_state)) btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); else - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); @@ -2541,7 +2535,7 @@ void ex_btc8723b2ant_display_coex_info(struct btc_coexist *btcoexist) ps_tdma_case, coex_dm->auto_tdma_adjust); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d/ %d ", - "DecBtPwr/ IgnWlanAct", coex_dm->cur_dec_bt_pwr, + "DecBtPwr/ IgnWlanAct", coex_dm->cur_dec_bt_pwr_lvl, coex_dm->cur_ignore_wlan_act); /* Hw setting */ diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h index e36ab4205771..746930d0d244 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h @@ -75,8 +75,8 @@ enum BT_8723B_2ANT_COEX_ALGO { struct coex_dm_8723b_2ant { /* fw mechanism */ - bool pre_dec_bt_pwr; - bool cur_dec_bt_pwr; + bool pre_dec_bt_pwr_lvl; + bool cur_dec_bt_pwr_lvl; u8 pre_fw_dac_swing_lvl; u8 cur_fw_dac_swing_lvl; bool cur_ignore_wlan_act; -- cgit From a66a4176b74cfbcfc91b7c0b303d480226da125a Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 3 Apr 2017 13:41:41 -0500 Subject: rtlwifi: btcoex: 23b 2ant: add comments to describe how duration adjusted Since btcoex uses static variables to store the cumulative information on the wifi status, some tricks are used that are a bit obscure. We add some comments about the criteria we use to adjust wifi duration. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index b1a4c8466bc6..fb72bcd4090c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1622,6 +1622,9 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, dn = 0; if (up >= n) { + /* if retry count during continuous n*2 + * seconds is 0, enlarge WiFi duration + */ wait_count = 0; n = 3; up = 0; @@ -1638,12 +1641,20 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, up = 0; if (dn == 2) { + /* if continuous 2 retry count(every 2 + * seconds) >0 and < 3, reduce WiFi duration + */ if (wait_count <= 2) + /* avoid loop between the two levels */ m++; else m = 1; if (m >= 20) + /* maximum of m = 20 ' will recheck if + * need to adjust wifi duration in + * maximum time interval 120 seconds + */ m = 20; n = 3 * m; @@ -1655,12 +1666,20 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, "[BTCoex], Decrease wifi duration for retry_counter<3!!\n"); } } else { + /* retry count > 3, once retry count > 3, to reduce + * WiFi duration + */ if (wait_count == 1) + /* to avoid loop between the two levels */ m++; else m = 1; if (m >= 20) + /* maximum of m = 20 ' will recheck if need to + * adjust wifi duration in maximum time interval + * 120 seconds + */ m = 20; n = 3 * m; -- cgit From 495d36e66814e77c4ef8ef9711f3d4c358ef0902 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 3 Apr 2017 13:41:42 -0500 Subject: rtlwifi: btcoex: 23b 2ant: new fw use h2c to control GNT_BT Earlier versions of the FW did not support h2c to set GNT_BT, but later versions have that capability. Hence we check the FW version and decide whether to use h2c or just write to the register. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 37 +++++++++++++++++----- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index fb72bcd4090c..988f276531dd 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1065,8 +1065,14 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, btcoexist->btc_write_1byte(btcoexist, 0x930, 0x77); btcoexist->btc_write_1byte_bitmask(btcoexist, 0x67, 0x20, 0x1); - /* Force GNT_BT to low */ - btcoexist->btc_write_1byte_bitmask(btcoexist, 0x765, 0x18, 0x0); + if (fw_ver >= 0x180000) { + /* Use H2C to set GNT_BT to High to avoid A2DP click */ + h2c_parameter[0] = 1; + btcoexist->btc_fill_h2c(btcoexist, 0x6E, 1, + h2c_parameter); + } else { + btcoexist->btc_write_1byte(btcoexist, 0x765, 0x18); + } if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) { /* tell firmware "no antenna inverse" */ @@ -1078,8 +1084,12 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, } else { /* tell firmware "antenna inverse" */ h2c_parameter[0] = 1; - h2c_parameter[1] = 1; /* ext switch type */ - btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, + } + } else { + if (fw_ver >= 0x180000) { + /* Use H2C to set GNT_BT to "Control by PTA"*/ + h2c_parameter[0] = 0; + btcoexist->btc_fill_h2c(btcoexist, 0x6E, 1, h2c_parameter); btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280); } @@ -2370,12 +2380,23 @@ static void btc8723b2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) static void btc8723b2ant_wifioff_hwcfg(struct btc_coexist *btcoexist) { + u8 h2c_parameter[2] = {0}; + u32 fw_ver = 0; + /* set wlan_act to low */ btcoexist->btc_write_1byte(btcoexist, 0x76e, 0x4); - /* Force GNT_BT to High */ - btcoexist->btc_write_1byte_bitmask(btcoexist, 0x765, 0x18, 0x3); - /* BT select s0/s1 is controlled by BT */ - btcoexist->btc_write_1byte_bitmask(btcoexist, 0x67, 0x20, 0x0); + + /* WiFi standby while GNT_BT 0 -> 1 */ + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x780); + + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_FW_VER, &fw_ver); + if (fw_ver >= 0x180000) { + /* Use H2C to set GNT_BT to HIGH */ + h2c_parameter[0] = 1; + btcoexist->btc_fill_h2c(btcoexist, 0x6E, 1, h2c_parameter); + } else { + btcoexist->btc_write_1byte(btcoexist, 0x765, 0x18); + } } /********************************************************************* -- cgit From 1aed89640a899cd695bbfc976a4356affa474646 Mon Sep 17 00:00:00 2001 From: Karthik Ananthapadmanabha Date: Wed, 5 Apr 2017 17:06:20 +0530 Subject: mwifiex: apply radar flag When IEEE80211_CHAN_RADAR is set by cfg80211, passive scan must be performed. In mwifiex,active scan is performed even though flag is set from cfg80211. mwifiex_reg_apply_radar_flags() function added in this patch correctly uses radar flag. Signed-off-by: Karthik Ananthapadmanabha Signed-off-by: Amitkumar Karwar Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 1e3bd435a694..44d06177859e 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -594,6 +594,24 @@ int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) return 0; } +static void mwifiex_reg_apply_radar_flags(struct wiphy *wiphy) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *chan; + unsigned int i; + + if (!wiphy->bands[NL80211_BAND_5GHZ]) + return; + sband = wiphy->bands[NL80211_BAND_5GHZ]; + + for (i = 0; i < sband->n_channels; i++) { + chan = &sband->channels[i]; + if ((!(chan->flags & IEEE80211_CHAN_DISABLED)) && + (chan->flags & IEEE80211_CHAN_RADAR)) + chan->flags |= IEEE80211_CHAN_NO_IR; + } +} + /* * CFG802.11 regulatory domain callback function. * @@ -613,6 +631,7 @@ static void mwifiex_reg_notifier(struct wiphy *wiphy, mwifiex_dbg(adapter, INFO, "info: cfg80211 regulatory domain callback for %c%c\n", request->alpha2[0], request->alpha2[1]); + mwifiex_reg_apply_radar_flags(wiphy); switch (request->initiator) { case NL80211_REGDOM_SET_BY_DRIVER: -- cgit From 8eb992e876a88de7539b1b9e132dd171d865cd2f Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 4 Apr 2017 18:14:06 -0400 Subject: bnxt_en: Update firmware interface spec to 1.7.6.2. Features added include WoL and selftest. Signed-off-by: Deepak Khungar Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h | 325 +++++++++++++++++++++--- drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c | 8 +- drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h | 1 + 3 files changed, 297 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index 6e275c23d68b..7dc71bb95837 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -11,19 +11,21 @@ #ifndef BNXT_HSI_H #define BNXT_HSI_H -/* HSI and HWRM Specification 1.7.0 */ +/* HSI and HWRM Specification 1.7.6 */ #define HWRM_VERSION_MAJOR 1 #define HWRM_VERSION_MINOR 7 -#define HWRM_VERSION_UPDATE 0 +#define HWRM_VERSION_UPDATE 6 -#define HWRM_VERSION_STR "1.7.0" +#define HWRM_VERSION_RSVD 2 /* non-zero means beta version */ + +#define HWRM_VERSION_STR "1.7.6.2" /* * Following is the signature for HWRM message field that indicates not * applicable (All F's). Need to cast it the size of the field if needed. */ #define HWRM_NA_SIGNATURE ((__le32)(-1)) #define HWRM_MAX_REQ_LEN (128) /* hwrm_func_buf_rgtr */ -#define HWRM_MAX_RESP_LEN (176) /* hwrm_func_qstats */ +#define HWRM_MAX_RESP_LEN (248) /* hwrm_selftest_qlist */ #define HW_HASH_INDEX_SIZE 0x80 /* 7 bit indirection table index. */ #define HW_HASH_KEY_SIZE 40 #define HWRM_RESP_VALID_KEY 1 /* valid key for HWRM response */ @@ -571,9 +573,10 @@ struct hwrm_ver_get_output { __le16 max_req_win_len; __le16 max_resp_len; __le16 def_req_timeout; + u8 init_pending; + #define VER_GET_RESP_INIT_PENDING_DEV_NOT_RDY 0x1UL u8 unused_0; u8 unused_1; - u8 unused_2; u8 valid; }; @@ -809,6 +812,8 @@ struct hwrm_func_qcfg_output { #define FUNC_QCFG_RESP_FLAGS_OOB_WOL_BMP_ENABLED 0x2UL #define FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED 0x4UL #define FUNC_QCFG_RESP_FLAGS_STD_TX_RING_MODE_ENABLED 0x8UL + #define FUNC_QCFG_RESP_FLAGS_FW_LLDP_AGENT_ENABLED 0x10UL + #define FUNC_QCFG_RESP_FLAGS_MULTI_HOST 0x20UL u8 mac_address[6]; __le16 pci_id; __le16 alloc_rsscos_ctx; @@ -827,10 +832,12 @@ struct hwrm_func_qcfg_output { #define FUNC_QCFG_RESP_PORT_PARTITION_TYPE_NPAR1_5 0x3UL #define FUNC_QCFG_RESP_PORT_PARTITION_TYPE_NPAR2_0 0x4UL #define FUNC_QCFG_RESP_PORT_PARTITION_TYPE_UNKNOWN 0xffUL - u8 unused_0; + u8 port_pf_cnt; + #define FUNC_QCFG_RESP_PORT_PF_CNT_UNAVAIL 0x0UL __le16 dflt_vnic_id; - u8 unused_1; - u8 unused_2; + u8 host_cnt; + #define FUNC_QCFG_RESP_HOST_CNT_UNAVAIL 0x0UL + u8 unused_0; __le32 min_bw; #define FUNC_QCFG_RESP_MIN_BW_BW_VALUE_MASK 0xfffffffUL #define FUNC_QCFG_RESP_MIN_BW_BW_VALUE_SFT 0 @@ -867,12 +874,12 @@ struct hwrm_func_qcfg_output { #define FUNC_QCFG_RESP_EVB_MODE_NO_EVB 0x0UL #define FUNC_QCFG_RESP_EVB_MODE_VEB 0x1UL #define FUNC_QCFG_RESP_EVB_MODE_VEPA 0x2UL - u8 unused_3; + u8 unused_1; __le16 alloc_vfs; __le32 alloc_mcast_filters; __le32 alloc_hw_ring_grps; __le16 alloc_sp_tx_rings; - u8 unused_4; + u8 unused_2; u8 valid; }; @@ -888,16 +895,13 @@ struct hwrm_func_cfg_input { u8 unused_0; u8 unused_1; __le32 flags; - #define FUNC_CFG_REQ_FLAGS_PROM_MODE 0x1UL - #define FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK 0x2UL - #define FUNC_CFG_REQ_FLAGS_SRC_IP_ADDR_CHECK 0x4UL - #define FUNC_CFG_REQ_FLAGS_VLAN_PRI_MATCH 0x8UL - #define FUNC_CFG_REQ_FLAGS_DFLT_PRI_NOMATCH 0x10UL - #define FUNC_CFG_REQ_FLAGS_DISABLE_PAUSE 0x20UL - #define FUNC_CFG_REQ_FLAGS_DISABLE_STP 0x40UL - #define FUNC_CFG_REQ_FLAGS_DISABLE_LLDP 0x80UL - #define FUNC_CFG_REQ_FLAGS_DISABLE_PTPV2 0x100UL - #define FUNC_CFG_REQ_FLAGS_STD_TX_RING_MODE 0x200UL + #define FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK_DISABLE 0x1UL + #define FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK_ENABLE 0x2UL + #define FUNC_CFG_REQ_FLAGS_RSVD_MASK 0x1fcUL + #define FUNC_CFG_REQ_FLAGS_RSVD_SFT 2 + #define FUNC_CFG_REQ_FLAGS_STD_TX_RING_MODE_ENABLE 0x200UL + #define FUNC_CFG_REQ_FLAGS_STD_TX_RING_MODE_DISABLE 0x400UL + #define FUNC_CFG_REQ_FLAGS_VIRT_MAC_PERSIST 0x800UL __le32 enables; #define FUNC_CFG_REQ_ENABLES_MTU 0x1UL #define FUNC_CFG_REQ_ENABLES_MRU 0x2UL @@ -1013,7 +1017,7 @@ struct hwrm_func_qstats_output { __le64 tx_ucast_pkts; __le64 tx_mcast_pkts; __le64 tx_bcast_pkts; - __le64 tx_err_pkts; + __le64 tx_discard_pkts; __le64 tx_drop_pkts; __le64 tx_ucast_bytes; __le64 tx_mcast_bytes; @@ -1021,7 +1025,7 @@ struct hwrm_func_qstats_output { __le64 rx_ucast_pkts; __le64 rx_mcast_pkts; __le64 rx_bcast_pkts; - __le64 rx_err_pkts; + __le64 rx_discard_pkts; __le64 rx_drop_pkts; __le64 rx_ucast_bytes; __le64 rx_mcast_bytes; @@ -4743,25 +4747,72 @@ struct hwrm_temp_monitor_query_output { u8 valid; }; -/* hwrm_nvm_read */ -/* Input (40 bytes) */ -struct hwrm_nvm_read_input { +/* hwrm_wol_filter_alloc */ +/* Input (64 bytes) */ +struct hwrm_wol_filter_alloc_input { __le16 req_type; __le16 cmpl_ring; __le16 seq_id; __le16 target_id; __le64 resp_addr; - __le64 host_dest_addr; - __le16 dir_idx; + __le32 flags; + __le32 enables; + #define WOL_FILTER_ALLOC_REQ_ENABLES_MAC_ADDRESS 0x1UL + #define WOL_FILTER_ALLOC_REQ_ENABLES_PATTERN_OFFSET 0x2UL + #define WOL_FILTER_ALLOC_REQ_ENABLES_PATTERN_BUF_SIZE 0x4UL + #define WOL_FILTER_ALLOC_REQ_ENABLES_PATTERN_BUF_ADDR 0x8UL + #define WOL_FILTER_ALLOC_REQ_ENABLES_PATTERN_MASK_ADDR 0x10UL + #define WOL_FILTER_ALLOC_REQ_ENABLES_PATTERN_MASK_SIZE 0x20UL + __le16 port_id; + u8 wol_type; + #define WOL_FILTER_ALLOC_REQ_WOL_TYPE_MAGICPKT 0x0UL + #define WOL_FILTER_ALLOC_REQ_WOL_TYPE_BMP 0x1UL + #define WOL_FILTER_ALLOC_REQ_WOL_TYPE_INVALID 0xffUL u8 unused_0; - u8 unused_1; - __le32 offset; - __le32 len; + __le32 unused_1; + u8 mac_address[6]; + __le16 pattern_offset; + __le16 pattern_buf_size; + __le16 pattern_mask_size; __le32 unused_2; + __le64 pattern_buf_addr; + __le64 pattern_mask_addr; }; /* Output (16 bytes) */ -struct hwrm_nvm_read_output { +struct hwrm_wol_filter_alloc_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 wol_filter_id; + u8 unused_0; + __le16 unused_1; + u8 unused_2; + u8 unused_3; + u8 unused_4; + u8 valid; +}; + +/* hwrm_wol_filter_free */ +/* Input (32 bytes) */ +struct hwrm_wol_filter_free_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le32 flags; + #define WOL_FILTER_FREE_REQ_FLAGS_FREE_ALL_WOL_FILTERS 0x1UL + __le32 enables; + #define WOL_FILTER_FREE_REQ_ENABLES_WOL_FILTER_ID 0x1UL + __le16 port_id; + u8 wol_filter_id; + u8 unused_0[5]; +}; + +/* Output (16 bytes) */ +struct hwrm_wol_filter_free_output { __le16 error_code; __le16 req_type; __le16 seq_id; @@ -4773,21 +4824,107 @@ struct hwrm_nvm_read_output { u8 valid; }; -/* hwrm_nvm_raw_dump */ -/* Input (32 bytes) */ -struct hwrm_nvm_raw_dump_input { +/* hwrm_wol_filter_qcfg */ +/* Input (56 bytes) */ +struct hwrm_wol_filter_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 port_id; + __le16 handle; + __le32 unused_0; + __le64 pattern_buf_addr; + __le16 pattern_buf_size; + u8 unused_1; + u8 unused_2; + u8 unused_3[3]; + u8 unused_4; + __le64 pattern_mask_addr; + __le16 pattern_mask_size; + __le16 unused_5[3]; +}; + +/* Output (32 bytes) */ +struct hwrm_wol_filter_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 next_handle; + u8 wol_filter_id; + u8 wol_type; + #define WOL_FILTER_QCFG_RESP_WOL_TYPE_MAGICPKT 0x0UL + #define WOL_FILTER_QCFG_RESP_WOL_TYPE_BMP 0x1UL + #define WOL_FILTER_QCFG_RESP_WOL_TYPE_INVALID 0xffUL + __le32 unused_0; + u8 mac_address[6]; + __le16 pattern_offset; + __le16 pattern_size; + __le16 pattern_mask_size; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + +/* hwrm_wol_reason_qcfg */ +/* Input (40 bytes) */ +struct hwrm_wol_reason_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 port_id; + u8 unused_0; + u8 unused_1; + u8 unused_2[3]; + u8 unused_3; + __le64 wol_pkt_buf_addr; + __le16 wol_pkt_buf_size; + __le16 unused_4[3]; +}; + +/* Output (16 bytes) */ +struct hwrm_wol_reason_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 wol_filter_id; + u8 wol_reason; + #define WOL_REASON_QCFG_RESP_WOL_REASON_MAGICPKT 0x0UL + #define WOL_REASON_QCFG_RESP_WOL_REASON_BMP 0x1UL + #define WOL_REASON_QCFG_RESP_WOL_REASON_INVALID 0xffUL + u8 wol_pkt_len; + u8 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + +/* hwrm_nvm_read */ +/* Input (40 bytes) */ +struct hwrm_nvm_read_input { __le16 req_type; __le16 cmpl_ring; __le16 seq_id; __le16 target_id; __le64 resp_addr; __le64 host_dest_addr; + __le16 dir_idx; + u8 unused_0; + u8 unused_1; __le32 offset; __le32 len; + __le32 unused_2; }; /* Output (16 bytes) */ -struct hwrm_nvm_raw_dump_output { +struct hwrm_nvm_read_output { __le16 error_code; __le16 req_type; __le16 seq_id; @@ -4881,6 +5018,15 @@ struct hwrm_nvm_write_output { u8 valid; }; +/* Command specific Error Codes (8 bytes) */ +struct hwrm_nvm_write_cmd_err { + u8 code; + #define NVM_WRITE_CMD_ERR_CODE_UNKNOWN 0x0UL + #define NVM_WRITE_CMD_ERR_CODE_FRAG_ERR 0x1UL + #define NVM_WRITE_CMD_ERR_CODE_NO_SPACE 0x2UL + u8 unused_0[7]; +}; + /* hwrm_nvm_modify */ /* Input (40 bytes) */ struct hwrm_nvm_modify_input { @@ -5112,6 +5258,100 @@ struct hwrm_nvm_install_update_cmd_err { u8 unused_0[7]; }; +/* hwrm_selftest_qlist */ +/* Input (16 bytes) */ +struct hwrm_selftest_qlist_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; +}; + +/* Output (248 bytes) */ +struct hwrm_selftest_qlist_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 num_tests; + u8 available_tests; + #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_NVM_TEST 0x1UL + #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_LINK_TEST 0x2UL + #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_REGISTER_TEST 0x4UL + #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_MEMORY_TEST 0x8UL + u8 offline_tests; + #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_NVM_TEST 0x1UL + #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_LINK_TEST 0x2UL + #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_REGISTER_TEST 0x4UL + #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_MEMORY_TEST 0x8UL + u8 unused_0; + __le16 test_timeout; + u8 unused_1; + u8 unused_2; + char test0_name[32]; + char test1_name[32]; + char test2_name[32]; + char test3_name[32]; + char test4_name[32]; + char test5_name[32]; + char test6_name[32]; + char test7_name[32]; +}; + +/* hwrm_selftest_exec */ +/* Input (24 bytes) */ +struct hwrm_selftest_exec_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + u8 flags; + #define SELFTEST_EXEC_REQ_FLAGS_NVM_TEST 0x1UL + #define SELFTEST_EXEC_REQ_FLAGS_LINK_TEST 0x2UL + #define SELFTEST_EXEC_REQ_FLAGS_REGISTER_TEST 0x4UL + #define SELFTEST_EXEC_REQ_FLAGS_MEMORY_TEST 0x8UL + u8 unused_0[7]; +}; + +/* Output (16 bytes) */ +struct hwrm_selftest_exec_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 requested_tests; + #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_NVM_TEST 0x1UL + #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_LINK_TEST 0x2UL + #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_REGISTER_TEST 0x4UL + #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_MEMORY_TEST 0x8UL + u8 test_success; + #define SELFTEST_EXEC_RESP_TEST_SUCCESS_NVM_TEST 0x1UL + #define SELFTEST_EXEC_RESP_TEST_SUCCESS_LINK_TEST 0x2UL + #define SELFTEST_EXEC_RESP_TEST_SUCCESS_REGISTER_TEST 0x4UL + #define SELFTEST_EXEC_RESP_TEST_SUCCESS_MEMORY_TEST 0x8UL + __le16 unused_0[3]; +}; + +/* hwrm_selftest_irq */ +/* Input (16 bytes) */ +struct hwrm_selftest_irq_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; +}; + +/* Output (8 bytes) */ +struct hwrm_selftest_irq_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; +}; + /* Hardware Resource Manager Specification */ /* Input (16 bytes) */ struct input { @@ -5130,6 +5370,16 @@ struct output { __le16 resp_len; }; +/* Short Command Structure (16 bytes) */ +struct hwrm_short_input { + __le16 req_type; + __le16 signature; + #define SHORT_REQ_SIGNATURE_SHORT_CMD 0x4321UL + __le16 unused_0; + __le16 size; + __le64 req_addr; +}; + /* Command numbering (8 bytes) */ struct cmd_nums { __le16 req_type; @@ -5252,11 +5502,15 @@ struct cmd_nums { #define HWRM_CFA_FLOW_FLUSH (0x105UL) #define HWRM_CFA_FLOW_STATS (0x106UL) #define HWRM_CFA_FLOW_INFO (0x107UL) + #define HWRM_SELFTEST_QLIST (0x200UL) + #define HWRM_SELFTEST_EXEC (0x201UL) + #define HWRM_SELFTEST_IRQ (0x202UL) #define HWRM_DBG_READ_DIRECT (0xff10UL) #define HWRM_DBG_READ_INDIRECT (0xff11UL) #define HWRM_DBG_WRITE_DIRECT (0xff12UL) #define HWRM_DBG_WRITE_INDIRECT (0xff13UL) #define HWRM_DBG_DUMP (0xff14UL) + #define HWRM_NVM_FACTORY_DEFAULTS (0xffeeUL) #define HWRM_NVM_VALIDATE_OPTION (0xffefUL) #define HWRM_NVM_FLUSH (0xfff0UL) #define HWRM_NVM_GET_VARIABLE (0xfff1UL) @@ -5464,6 +5718,7 @@ struct hwrm_struct_hdr { #define STRUCT_HDR_STRUCT_ID_DCBX_FEATURE_STATE 0x422UL #define STRUCT_HDR_STRUCT_ID_LLDP_GENERIC 0x424UL #define STRUCT_HDR_STRUCT_ID_LLDP_DEVICE 0x426UL + #define STRUCT_HDR_STRUCT_ID_AFM_OPAQUE 0x1UL #define STRUCT_HDR_STRUCT_ID_PORT_DESCRIPTION 0xaUL __le16 len; u8 version; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index 0b8cd7443843..f89353175e6b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -1,6 +1,7 @@ /* Broadcom NetXtreme-C/E network driver. * * Copyright (c) 2014-2016 Broadcom Corporation + * Copyright (c) 2016-2017 Broadcom Limited * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -84,6 +85,9 @@ int bnxt_set_vf_spoofchk(struct net_device *dev, int vf_id, bool setting) u32 func_flags; int rc; + if (bp->hwrm_spec_code < 0x10701) + return -ENOTSUPP; + rc = bnxt_vf_ndo_prep(bp, vf_id); if (rc) return rc; @@ -96,9 +100,9 @@ int bnxt_set_vf_spoofchk(struct net_device *dev, int vf_id, bool setting) func_flags = vf->func_flags; if (setting) - func_flags |= FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK; + func_flags |= FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK_ENABLE; else - func_flags &= ~FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK; + func_flags |= FUNC_CFG_REQ_FLAGS_SRC_MAC_ADDR_CHECK_DISABLE; /*TODO: if the driver supports VLAN filter on guest VLAN, * the spoof check should also include vlan anti-spoofing */ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h index 1ab72e4820af..dbc8d977fc5a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.h @@ -1,6 +1,7 @@ /* Broadcom NetXtreme-C/E network driver. * * Copyright (c) 2014-2016 Broadcom Corporation + * Copyright (c) 2016-2017 Broadcom Limited * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by -- cgit From c1ef146a5bd3b286d5c3eb2c9f631b38647c76d3 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 4 Apr 2017 18:14:07 -0400 Subject: bnxt_en: Add basic WoL infrastructure. Add code to driver probe function to check if the device is WoL capable and if Magic packet WoL filter is currently set. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 43 +++++++++++++++++++++++++++++++ drivers/net/ethernet/broadcom/bnxt/bnxt.h | 4 +++ 2 files changed, 47 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 174ec8f84637..70cc3132cc4c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4532,6 +4532,9 @@ static int bnxt_hwrm_func_qcaps(struct bnxt *bp) pf->max_tx_wm_flows = le32_to_cpu(resp->max_tx_wm_flows); pf->max_rx_em_flows = le32_to_cpu(resp->max_rx_em_flows); pf->max_rx_wm_flows = le32_to_cpu(resp->max_rx_wm_flows); + if (resp->flags & + cpu_to_le32(FUNC_QCAPS_RESP_FLAGS_WOL_MAGICPKT_SUPPORTED)) + bp->flags |= BNXT_FLAG_WOL_CAP; } else { #ifdef CONFIG_BNXT_SRIOV struct bnxt_vf_info *vf = &bp->vf; @@ -5839,6 +5842,44 @@ static int bnxt_hwrm_port_led_qcaps(struct bnxt *bp) return 0; } +static u16 bnxt_hwrm_get_wol_fltrs(struct bnxt *bp, u16 handle) +{ + struct hwrm_wol_filter_qcfg_input req = {0}; + struct hwrm_wol_filter_qcfg_output *resp = bp->hwrm_cmd_resp_addr; + u16 next_handle = 0; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_WOL_FILTER_QCFG, -1, -1); + req.port_id = cpu_to_le16(bp->pf.port_id); + req.handle = cpu_to_le16(handle); + mutex_lock(&bp->hwrm_cmd_lock); + rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (!rc) { + next_handle = le16_to_cpu(resp->next_handle); + if (next_handle != 0) { + if (resp->wol_type == + WOL_FILTER_ALLOC_REQ_WOL_TYPE_MAGICPKT) { + bp->wol = 1; + bp->wol_filter_id = resp->wol_filter_id; + } + } + } + mutex_unlock(&bp->hwrm_cmd_lock); + return next_handle; +} + +static void bnxt_get_wol_settings(struct bnxt *bp) +{ + u16 handle = 0; + + if (!BNXT_PF(bp) || !(bp->flags & BNXT_FLAG_WOL_CAP)) + return; + + do { + handle = bnxt_hwrm_get_wol_fltrs(bp, handle); + } while (handle && handle != 0xffff); +} + static bool bnxt_eee_config_ok(struct bnxt *bp) { struct ethtool_eee *eee = &bp->eee; @@ -7575,6 +7616,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) goto init_err_pci_clean; + bnxt_get_wol_settings(bp); + rc = register_netdev(dev); if (rc) goto init_err_clr_int; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 3cb07778a690..02de812dd966 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -989,6 +989,7 @@ struct bnxt { #define BNXT_FLAG_UDP_RSS_CAP 0x800 #define BNXT_FLAG_EEE_CAP 0x1000 #define BNXT_FLAG_NEW_RSS_CAP 0x2000 + #define BNXT_FLAG_WOL_CAP 0x4000 #define BNXT_FLAG_ROCEV1_CAP 0x8000 #define BNXT_FLAG_ROCEV2_CAP 0x10000 #define BNXT_FLAG_ROCE_CAP (BNXT_FLAG_ROCEV1_CAP | \ @@ -1180,6 +1181,9 @@ struct bnxt { u32 lpi_tmr_lo; u32 lpi_tmr_hi; + u8 wol_filter_id; + u8 wol; + u8 num_leds; struct bnxt_led_info leds[BNXT_MAX_LED]; -- cgit From d196ece740bf337aa25731cd8cb44660a2a227dd Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 4 Apr 2017 18:14:08 -0400 Subject: bnxt_en: Add pci shutdown method. Add pci shutdown method to put device in the proper WoL and power state. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 70cc3132cc4c..10a9cdaf2c17 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7617,6 +7617,10 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto init_err_pci_clean; bnxt_get_wol_settings(bp); + if (bp->flags & BNXT_FLAG_WOL_CAP) + device_set_wakeup_enable(&pdev->dev, bp->wol); + else + device_set_wakeup_capable(&pdev->dev, false); rc = register_netdev(dev); if (rc) @@ -7641,6 +7645,32 @@ init_err_free: return rc; } +static void bnxt_shutdown(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct bnxt *bp; + + if (!dev) + return; + + rtnl_lock(); + bp = netdev_priv(dev); + if (!bp) + goto shutdown_exit; + + if (netif_running(dev)) + dev_close(dev); + + if (system_state == SYSTEM_POWER_OFF) { + bnxt_clear_int_mode(bp); + pci_wake_from_d3(pdev, bp->wol); + pci_set_power_state(pdev, PCI_D3hot); + } + +shutdown_exit: + rtnl_unlock(); +} + /** * bnxt_io_error_detected - called when PCI error is detected * @pdev: Pointer to PCI device @@ -7757,6 +7787,7 @@ static struct pci_driver bnxt_pci_driver = { .id_table = bnxt_pci_tbl, .probe = bnxt_init_one, .remove = bnxt_remove_one, + .shutdown = bnxt_shutdown, .err_handler = &bnxt_err_handler, #if defined(CONFIG_BNXT_SRIOV) .sriov_configure = bnxt_sriov_configure, -- cgit From 8e202366dd752564d7f090ba280cc51cbf7bbbd9 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 4 Apr 2017 18:14:09 -0400 Subject: bnxt_en: Add ethtool get_wol method. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 16 ++++++++++++++++ drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h | 1 + 2 files changed, 17 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 6903a873f072..2b947048aaba 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -1,6 +1,7 @@ /* Broadcom NetXtreme-C/E network driver. * * Copyright (c) 2014-2016 Broadcom Corporation + * Copyright (c) 2016-2017 Broadcom Limited * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -832,6 +833,20 @@ static void bnxt_get_drvinfo(struct net_device *dev, kfree(pkglog); } +static void bnxt_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct bnxt *bp = netdev_priv(dev); + + wol->supported = 0; + wol->wolopts = 0; + memset(&wol->sopass, 0, sizeof(wol->sopass)); + if (bp->flags & BNXT_FLAG_WOL_CAP) { + wol->supported = WAKE_MAGIC; + if (bp->wol) + wol->wolopts = WAKE_MAGIC; + } +} + u32 _bnxt_fw_to_ethtool_adv_spds(u16 fw_speeds, u8 fw_pause) { u32 speed_mask = 0; @@ -2134,6 +2149,7 @@ const struct ethtool_ops bnxt_ethtool_ops = { .get_pauseparam = bnxt_get_pauseparam, .set_pauseparam = bnxt_set_pauseparam, .get_drvinfo = bnxt_get_drvinfo, + .get_wol = bnxt_get_wol, .get_coalesce = bnxt_get_coalesce, .set_coalesce = bnxt_set_coalesce, .get_msglevel = bnxt_get_msglevel, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h index ed1e555292e9..27621710e07b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h @@ -1,6 +1,7 @@ /* Broadcom NetXtreme-C/E network driver. * * Copyright (c) 2014-2016 Broadcom Corporation + * Copyright (c) 2016-2017 Broadcom Limited * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by -- cgit From 5282db6c794fed3ea8b399bc5305c4078e084f7b Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 4 Apr 2017 18:14:10 -0400 Subject: bnxt_en: Add ethtool set_wol method. And add functions to set and free magic packet filter. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 32 +++++++++++++++++++++++ drivers/net/ethernet/broadcom/bnxt/bnxt.h | 2 ++ drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 26 ++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 10a9cdaf2c17..e432d0a92d52 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5842,6 +5842,38 @@ static int bnxt_hwrm_port_led_qcaps(struct bnxt *bp) return 0; } +int bnxt_hwrm_alloc_wol_fltr(struct bnxt *bp) +{ + struct hwrm_wol_filter_alloc_input req = {0}; + struct hwrm_wol_filter_alloc_output *resp = bp->hwrm_cmd_resp_addr; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_WOL_FILTER_ALLOC, -1, -1); + req.port_id = cpu_to_le16(bp->pf.port_id); + req.wol_type = WOL_FILTER_ALLOC_REQ_WOL_TYPE_MAGICPKT; + req.enables = cpu_to_le32(WOL_FILTER_ALLOC_REQ_ENABLES_MAC_ADDRESS); + memcpy(req.mac_address, bp->dev->dev_addr, ETH_ALEN); + mutex_lock(&bp->hwrm_cmd_lock); + rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (!rc) + bp->wol_filter_id = resp->wol_filter_id; + mutex_unlock(&bp->hwrm_cmd_lock); + return rc; +} + +int bnxt_hwrm_free_wol_fltr(struct bnxt *bp) +{ + struct hwrm_wol_filter_free_input req = {0}; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_WOL_FILTER_FREE, -1, -1); + req.port_id = cpu_to_le16(bp->pf.port_id); + req.enables = cpu_to_le32(WOL_FILTER_FREE_REQ_ENABLES_WOL_FILTER_ID); + req.wol_filter_id = bp->wol_filter_id; + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + return rc; +} + static u16 bnxt_hwrm_get_wol_fltrs(struct bnxt *bp, u16 handle) { struct hwrm_wol_filter_qcfg_input req = {0}; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 02de812dd966..aba25ba365fc 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1242,6 +1242,8 @@ void bnxt_tx_disable(struct bnxt *bp); void bnxt_tx_enable(struct bnxt *bp); int bnxt_hwrm_set_pause(struct bnxt *); int bnxt_hwrm_set_link_setting(struct bnxt *, bool, bool); +int bnxt_hwrm_alloc_wol_fltr(struct bnxt *bp); +int bnxt_hwrm_free_wol_fltr(struct bnxt *bp); int bnxt_hwrm_fw_set_time(struct bnxt *); int bnxt_open_nic(struct bnxt *, bool, bool); int bnxt_close_nic(struct bnxt *, bool, bool); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 2b947048aaba..84cd4ca43c04 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -847,6 +847,31 @@ static void bnxt_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) } } +static int bnxt_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct bnxt *bp = netdev_priv(dev); + + if (wol->wolopts & ~WAKE_MAGIC) + return -EINVAL; + + if (wol->wolopts & WAKE_MAGIC) { + if (!(bp->flags & BNXT_FLAG_WOL_CAP)) + return -EINVAL; + if (!bp->wol) { + if (bnxt_hwrm_alloc_wol_fltr(bp)) + return -EBUSY; + bp->wol = 1; + } + } else { + if (bp->wol) { + if (bnxt_hwrm_free_wol_fltr(bp)) + return -EBUSY; + bp->wol = 0; + } + } + return 0; +} + u32 _bnxt_fw_to_ethtool_adv_spds(u16 fw_speeds, u8 fw_pause) { u32 speed_mask = 0; @@ -2150,6 +2175,7 @@ const struct ethtool_ops bnxt_ethtool_ops = { .set_pauseparam = bnxt_set_pauseparam, .get_drvinfo = bnxt_get_drvinfo, .get_wol = bnxt_get_wol, + .set_wol = bnxt_set_wol, .get_coalesce = bnxt_get_coalesce, .set_coalesce = bnxt_set_coalesce, .get_msglevel = bnxt_get_msglevel, -- cgit From f65a2044a8c988adf16788c51c04ac10dbbdb494 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 4 Apr 2017 18:14:11 -0400 Subject: bnxt_en: Add suspend/resume callbacks. Add suspend/resume callbacks using the newer dev_pm_ops method. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index e432d0a92d52..4e77bbf7adf0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7703,6 +7703,62 @@ shutdown_exit: rtnl_unlock(); } +#ifdef CONFIG_PM_SLEEP +static int bnxt_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct net_device *dev = pci_get_drvdata(pdev); + struct bnxt *bp = netdev_priv(dev); + int rc = 0; + + rtnl_lock(); + if (netif_running(dev)) { + netif_device_detach(dev); + rc = bnxt_close(dev); + } + bnxt_hwrm_func_drv_unrgtr(bp); + rtnl_unlock(); + return rc; +} + +static int bnxt_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct net_device *dev = pci_get_drvdata(pdev); + struct bnxt *bp = netdev_priv(dev); + int rc = 0; + + rtnl_lock(); + if (bnxt_hwrm_ver_get(bp) || bnxt_hwrm_func_drv_rgtr(bp)) { + rc = -ENODEV; + goto resume_exit; + } + rc = bnxt_hwrm_func_reset(bp); + if (rc) { + rc = -EBUSY; + goto resume_exit; + } + bnxt_get_wol_settings(bp); + if (netif_running(dev)) { + rc = bnxt_open(dev); + if (!rc) + netif_device_attach(dev); + } + +resume_exit: + rtnl_unlock(); + return rc; +} + +static SIMPLE_DEV_PM_OPS(bnxt_pm_ops, bnxt_suspend, bnxt_resume); +#define BNXT_PM_OPS (&bnxt_pm_ops) + +#else + +#define BNXT_PM_OPS NULL + +#endif /* CONFIG_PM_SLEEP */ + /** * bnxt_io_error_detected - called when PCI error is detected * @pdev: Pointer to PCI device @@ -7820,6 +7876,7 @@ static struct pci_driver bnxt_pci_driver = { .probe = bnxt_init_one, .remove = bnxt_remove_one, .shutdown = bnxt_shutdown, + .driver.pm = BNXT_PM_OPS, .err_handler = &bnxt_err_handler, #if defined(CONFIG_BNXT_SRIOV) .sriov_configure = bnxt_sriov_configure, -- cgit From eb51365846bc418687af4c4f41b68b6e84cdd449 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 4 Apr 2017 18:14:12 -0400 Subject: bnxt_en: Add basic ethtool -t selftest support. Add the basic infrastructure and only firmware tests initially. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 + drivers/net/ethernet/broadcom/bnxt/bnxt.h | 13 ++- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 136 +++++++++++++++++++++- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h | 2 + 4 files changed, 150 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 4e77bbf7adf0..7b72ba9fd0aa 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7281,6 +7281,7 @@ static void bnxt_remove_one(struct pci_dev *pdev) bnxt_clear_int_mode(bp); bnxt_hwrm_func_drv_unrgtr(bp); bnxt_free_hwrm_resources(bp); + bnxt_ethtool_free(bp); bnxt_dcb_free(bp); kfree(bp->edev); bp->edev = NULL; @@ -7603,6 +7604,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) bnxt_hwrm_func_qcfg(bp); bnxt_hwrm_port_led_qcaps(bp); + bnxt_ethtool_init(bp); bnxt_set_rx_skb_mode(bp, false); bnxt_set_tpa_flags(bp); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index aba25ba365fc..4affaacb7aa9 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -426,8 +426,6 @@ struct rx_tpa_end_cmp_ext { #define BNXT_MIN_PKT_SIZE 52 -#define BNXT_NUM_TESTS(bp) 0 - #define BNXT_DEFAULT_RX_RING_SIZE 511 #define BNXT_DEFAULT_TX_RING_SIZE 511 @@ -911,6 +909,14 @@ struct bnxt_led_info { __le16 led_color_caps; }; +#define BNXT_MAX_TEST 8 + +struct bnxt_test_info { + u8 offline_mask; + u16 timeout; + char string[BNXT_MAX_TEST][ETH_GSTRING_LEN]; +}; + #define BNXT_GRCPF_REG_WINDOW_BASE_OUT 0x400 #define BNXT_CAG_REG_LEGACY_INT_STATUS 0x4014 #define BNXT_CAG_REG_BASE 0x300000 @@ -1181,6 +1187,9 @@ struct bnxt { u32 lpi_tmr_lo; u32 lpi_tmr_hi; + u8 num_tests; + struct bnxt_test_info *test_info; + u8 wol_filter_id; u8 wol; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 84cd4ca43c04..711d7fd56d26 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -210,6 +210,10 @@ static int bnxt_get_sset_count(struct net_device *dev, int sset) return num_stats; } + case ETH_SS_TEST: + if (!bp->num_tests) + return -EOPNOTSUPP; + return bp->num_tests; default: return -EOPNOTSUPP; } @@ -307,6 +311,11 @@ static void bnxt_get_strings(struct net_device *dev, u32 stringset, u8 *buf) } } break; + case ETH_SS_TEST: + if (bp->num_tests) + memcpy(buf, bp->test_info->string, + bp->num_tests * ETH_GSTRING_LEN); + break; default: netdev_err(bp->dev, "bnxt_get_strings invalid request %x\n", stringset); @@ -825,7 +834,7 @@ static void bnxt_get_drvinfo(struct net_device *dev, sizeof(info->fw_version)); strlcpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); info->n_stats = BNXT_NUM_STATS * bp->cp_nr_rings; - info->testinfo_len = BNXT_NUM_TESTS(bp); + info->testinfo_len = bp->num_tests; /* TODO CHIMP_FW: eeprom dump details */ info->eedump_len = 0; /* TODO CHIMP FW: reg dump details */ @@ -2168,6 +2177,130 @@ static int bnxt_set_phys_id(struct net_device *dev, return rc; } +static int bnxt_run_fw_tests(struct bnxt *bp, u8 test_mask, u8 *test_results) +{ + struct hwrm_selftest_exec_output *resp = bp->hwrm_cmd_resp_addr; + struct hwrm_selftest_exec_input req = {0}; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_SELFTEST_EXEC, -1, -1); + mutex_lock(&bp->hwrm_cmd_lock); + resp->test_success = 0; + req.flags = test_mask; + rc = _hwrm_send_message(bp, &req, sizeof(req), bp->test_info->timeout); + *test_results = resp->test_success; + mutex_unlock(&bp->hwrm_cmd_lock); + return rc; +} + +#define BNXT_DRV_TESTS 0 + +static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, + u64 *buf) +{ + struct bnxt *bp = netdev_priv(dev); + bool offline = false; + u8 test_results = 0; + u8 test_mask = 0; + int rc, i; + + if (!bp->num_tests || !BNXT_SINGLE_PF(bp)) + return; + memset(buf, 0, sizeof(u64) * bp->num_tests); + if (!netif_running(dev)) { + etest->flags |= ETH_TEST_FL_FAILED; + return; + } + + if (etest->flags & ETH_TEST_FL_OFFLINE) { + if (bp->pf.active_vfs) { + etest->flags |= ETH_TEST_FL_FAILED; + netdev_warn(dev, "Offline tests cannot be run with active VFs\n"); + return; + } + offline = true; + } + + for (i = 0; i < bp->num_tests - BNXT_DRV_TESTS; i++) { + u8 bit_val = 1 << i; + + if (!(bp->test_info->offline_mask & bit_val)) + test_mask |= bit_val; + else if (offline) + test_mask |= bit_val; + } + if (!offline) { + bnxt_run_fw_tests(bp, test_mask, &test_results); + } else { + rc = bnxt_close_nic(bp, false, false); + if (rc) + return; + bnxt_run_fw_tests(bp, test_mask, &test_results); + bnxt_open_nic(bp, false, true); + } + for (i = 0; i < bp->num_tests - BNXT_DRV_TESTS; i++) { + u8 bit_val = 1 << i; + + if ((test_mask & bit_val) && !(test_results & bit_val)) { + buf[i] = 1; + etest->flags |= ETH_TEST_FL_FAILED; + } + } +} + +void bnxt_ethtool_init(struct bnxt *bp) +{ + struct hwrm_selftest_qlist_output *resp = bp->hwrm_cmd_resp_addr; + struct hwrm_selftest_qlist_input req = {0}; + struct bnxt_test_info *test_info; + int i, rc; + + if (bp->hwrm_spec_code < 0x10704 || !BNXT_SINGLE_PF(bp)) + return; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_SELFTEST_QLIST, -1, -1); + mutex_lock(&bp->hwrm_cmd_lock); + rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (rc) + goto ethtool_init_exit; + + test_info = kzalloc(sizeof(*bp->test_info), GFP_KERNEL); + if (!test_info) + goto ethtool_init_exit; + + bp->test_info = test_info; + bp->num_tests = resp->num_tests + BNXT_DRV_TESTS; + if (bp->num_tests > BNXT_MAX_TEST) + bp->num_tests = BNXT_MAX_TEST; + + test_info->offline_mask = resp->offline_tests; + test_info->timeout = le16_to_cpu(resp->test_timeout); + if (!test_info->timeout) + test_info->timeout = HWRM_CMD_TIMEOUT; + for (i = 0; i < bp->num_tests; i++) { + char *str = test_info->string[i]; + char *fw_str = resp->test0_name + i * 32; + + strlcpy(str, fw_str, ETH_GSTRING_LEN); + strncat(str, " test", ETH_GSTRING_LEN - strlen(str)); + if (test_info->offline_mask & (1 << i)) + strncat(str, " (offline)", + ETH_GSTRING_LEN - strlen(str)); + else + strncat(str, " (online)", + ETH_GSTRING_LEN - strlen(str)); + } + +ethtool_init_exit: + mutex_unlock(&bp->hwrm_cmd_lock); +} + +void bnxt_ethtool_free(struct bnxt *bp) +{ + kfree(bp->test_info); + bp->test_info = NULL; +} + const struct ethtool_ops bnxt_ethtool_ops = { .get_link_ksettings = bnxt_get_link_ksettings, .set_link_ksettings = bnxt_set_link_ksettings, @@ -2203,4 +2336,5 @@ const struct ethtool_ops bnxt_ethtool_ops = { .get_module_eeprom = bnxt_get_module_eeprom, .nway_reset = bnxt_nway_reset, .set_phys_id = bnxt_set_phys_id, + .self_test = bnxt_self_test, }; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h index 27621710e07b..f1bc90b6fb5b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.h @@ -39,5 +39,7 @@ extern const struct ethtool_ops bnxt_ethtool_ops; u32 _bnxt_fw_to_ethtool_adv_spds(u16, u8); u32 bnxt_fw_to_ethtool_speed(u16); u16 bnxt_get_fw_auto_link_speeds(u32); +void bnxt_ethtool_init(struct bnxt *bp); +void bnxt_ethtool_free(struct bnxt *bp); #endif -- cgit From f7dc1ea6c4c1f31371b7098d6fae0d49dc6cdff1 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 4 Apr 2017 18:14:13 -0400 Subject: bnxt_en: Add ethtool mac loopback self test. The mac loopback self test operates in polling mode. To support that, we need to add functions to open and close the NIC half way. The half open mode allows the rings to operate without IRQ and NAPI. We use the XDP transmit function to send the loopback packet. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 37 +++++ drivers/net/ethernet/broadcom/bnxt/bnxt.h | 2 + drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 165 ++++++++++++++++++++-- drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c | 4 +- drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h | 2 + 5 files changed, 199 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 7b72ba9fd0aa..9d71c19f5496 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -6097,6 +6097,43 @@ int bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) return rc; } +/* rtnl_lock held, open the NIC half way by allocating all resources, but + * NAPI, IRQ, and TX are not enabled. This is mainly used for offline + * self tests. + */ +int bnxt_half_open_nic(struct bnxt *bp) +{ + int rc = 0; + + rc = bnxt_alloc_mem(bp, false); + if (rc) { + netdev_err(bp->dev, "bnxt_alloc_mem err: %x\n", rc); + goto half_open_err; + } + rc = bnxt_init_nic(bp, false); + if (rc) { + netdev_err(bp->dev, "bnxt_init_nic err: %x\n", rc); + goto half_open_err; + } + return 0; + +half_open_err: + bnxt_free_skbs(bp); + bnxt_free_mem(bp, false); + dev_close(bp->dev); + return rc; +} + +/* rtnl_lock held, this call can only be made after a previous successful + * call to bnxt_half_open_nic(). + */ +void bnxt_half_close_nic(struct bnxt *bp) +{ + bnxt_hwrm_resource_free(bp, false, false); + bnxt_free_skbs(bp); + bnxt_free_mem(bp, false); +} + static int bnxt_open(struct net_device *dev) { struct bnxt *bp = netdev_priv(dev); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 4affaacb7aa9..c9a1688a65de 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1255,6 +1255,8 @@ int bnxt_hwrm_alloc_wol_fltr(struct bnxt *bp); int bnxt_hwrm_free_wol_fltr(struct bnxt *bp); int bnxt_hwrm_fw_set_time(struct bnxt *); int bnxt_open_nic(struct bnxt *, bool, bool); +int bnxt_half_open_nic(struct bnxt *bp); +void bnxt_half_close_nic(struct bnxt *bp); int bnxt_close_nic(struct bnxt *, bool, bool); int bnxt_reserve_rings(struct bnxt *bp, int tx, int rx, int tcs, int tx_xdp); int bnxt_setup_mq_tc(struct net_device *dev, u8 tc); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 711d7fd56d26..ecb441724b10 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -18,6 +18,7 @@ #include #include "bnxt_hsi.h" #include "bnxt.h" +#include "bnxt_xdp.h" #include "bnxt_ethtool.h" #include "bnxt_nvm_defs.h" /* NVRAM content constant and structure defs */ #include "bnxt_fw_hdr.h" /* Firmware hdr constant and structure defs */ @@ -2177,6 +2178,130 @@ static int bnxt_set_phys_id(struct net_device *dev, return rc; } +static int bnxt_hwrm_mac_loopback(struct bnxt *bp, bool enable) +{ + struct hwrm_port_mac_cfg_input req = {0}; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_MAC_CFG, -1, -1); + + req.enables = cpu_to_le32(PORT_MAC_CFG_REQ_ENABLES_LPBK); + if (enable) + req.lpbk = PORT_MAC_CFG_REQ_LPBK_LOCAL; + else + req.lpbk = PORT_MAC_CFG_REQ_LPBK_NONE; + return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); +} + +static int bnxt_rx_loopback(struct bnxt *bp, struct bnxt_napi *bnapi, + u32 raw_cons, int pkt_size) +{ + struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring; + struct bnxt_rx_ring_info *rxr = bnapi->rx_ring; + struct bnxt_sw_rx_bd *rx_buf; + struct rx_cmp *rxcmp; + u16 cp_cons, cons; + u8 *data; + u32 len; + int i; + + cp_cons = RING_CMP(raw_cons); + rxcmp = (struct rx_cmp *) + &cpr->cp_desc_ring[CP_RING(cp_cons)][CP_IDX(cp_cons)]; + cons = rxcmp->rx_cmp_opaque; + rx_buf = &rxr->rx_buf_ring[cons]; + data = rx_buf->data_ptr; + len = le32_to_cpu(rxcmp->rx_cmp_len_flags_type) >> RX_CMP_LEN_SHIFT; + if (len != pkt_size) + return -EIO; + i = ETH_ALEN; + if (!ether_addr_equal(data + i, bnapi->bp->dev->dev_addr)) + return -EIO; + i += ETH_ALEN; + for ( ; i < pkt_size; i++) { + if (data[i] != (u8)(i & 0xff)) + return -EIO; + } + return 0; +} + +static int bnxt_poll_loopback(struct bnxt *bp, int pkt_size) +{ + struct bnxt_napi *bnapi = bp->bnapi[0]; + struct bnxt_cp_ring_info *cpr; + struct tx_cmp *txcmp; + int rc = -EIO; + u32 raw_cons; + u32 cons; + int i; + + cpr = &bnapi->cp_ring; + raw_cons = cpr->cp_raw_cons; + for (i = 0; i < 200; i++) { + cons = RING_CMP(raw_cons); + txcmp = &cpr->cp_desc_ring[CP_RING(cons)][CP_IDX(cons)]; + + if (!TX_CMP_VALID(txcmp, raw_cons)) { + udelay(5); + continue; + } + + /* The valid test of the entry must be done first before + * reading any further. + */ + dma_rmb(); + if (TX_CMP_TYPE(txcmp) == CMP_TYPE_RX_L2_CMP) { + rc = bnxt_rx_loopback(bp, bnapi, raw_cons, pkt_size); + raw_cons = NEXT_RAW_CMP(raw_cons); + raw_cons = NEXT_RAW_CMP(raw_cons); + break; + } + raw_cons = NEXT_RAW_CMP(raw_cons); + } + cpr->cp_raw_cons = raw_cons; + return rc; +} + +static int bnxt_run_loopback(struct bnxt *bp) +{ + struct bnxt_tx_ring_info *txr = &bp->tx_ring[0]; + int pkt_size, i = 0; + struct sk_buff *skb; + dma_addr_t map; + u8 *data; + int rc; + + pkt_size = min(bp->dev->mtu + ETH_HLEN, bp->rx_copy_thresh); + skb = netdev_alloc_skb(bp->dev, pkt_size); + if (!skb) + return -ENOMEM; + data = skb_put(skb, pkt_size); + eth_broadcast_addr(data); + i += ETH_ALEN; + ether_addr_copy(&data[i], bp->dev->dev_addr); + i += ETH_ALEN; + for ( ; i < pkt_size; i++) + data[i] = (u8)(i & 0xff); + + map = dma_map_single(&bp->pdev->dev, skb->data, pkt_size, + PCI_DMA_TODEVICE); + if (dma_mapping_error(&bp->pdev->dev, map)) { + dev_kfree_skb(skb); + return -EIO; + } + bnxt_xmit_xdp(bp, txr, map, pkt_size, 0); + + /* Sync BD data before updating doorbell */ + wmb(); + + writel(DB_KEY_TX | txr->tx_prod, txr->tx_doorbell); + writel(DB_KEY_TX | txr->tx_prod, txr->tx_doorbell); + rc = bnxt_poll_loopback(bp, pkt_size); + + dma_unmap_single(&bp->pdev->dev, map, pkt_size, PCI_DMA_TODEVICE); + dev_kfree_skb(skb); + return rc; +} + static int bnxt_run_fw_tests(struct bnxt *bp, u8 test_mask, u8 *test_results) { struct hwrm_selftest_exec_output *resp = bp->hwrm_cmd_resp_addr; @@ -2193,7 +2318,8 @@ static int bnxt_run_fw_tests(struct bnxt *bp, u8 test_mask, u8 *test_results) return rc; } -#define BNXT_DRV_TESTS 0 +#define BNXT_DRV_TESTS 1 +#define BNXT_MACLPBK_TEST_IDX (bp->num_tests - BNXT_DRV_TESTS) static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, u64 *buf) @@ -2236,6 +2362,23 @@ static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, if (rc) return; bnxt_run_fw_tests(bp, test_mask, &test_results); + + buf[BNXT_MACLPBK_TEST_IDX] = 1; + bnxt_hwrm_mac_loopback(bp, true); + msleep(250); + rc = bnxt_half_open_nic(bp); + if (rc) { + bnxt_hwrm_mac_loopback(bp, false); + etest->flags |= ETH_TEST_FL_FAILED; + return; + } + if (bnxt_run_loopback(bp)) + etest->flags |= ETH_TEST_FL_FAILED; + else + buf[BNXT_MACLPBK_TEST_IDX] = 0; + + bnxt_half_close_nic(bp); + bnxt_hwrm_mac_loopback(bp, false); bnxt_open_nic(bp, false, true); } for (i = 0; i < bp->num_tests - BNXT_DRV_TESTS; i++) { @@ -2281,14 +2424,18 @@ void bnxt_ethtool_init(struct bnxt *bp) char *str = test_info->string[i]; char *fw_str = resp->test0_name + i * 32; - strlcpy(str, fw_str, ETH_GSTRING_LEN); - strncat(str, " test", ETH_GSTRING_LEN - strlen(str)); - if (test_info->offline_mask & (1 << i)) - strncat(str, " (offline)", - ETH_GSTRING_LEN - strlen(str)); - else - strncat(str, " (online)", - ETH_GSTRING_LEN - strlen(str)); + if (i == BNXT_MACLPBK_TEST_IDX) { + strcpy(str, "Mac loopback test (offline)"); + } else { + strlcpy(str, fw_str, ETH_GSTRING_LEN); + strncat(str, " test", ETH_GSTRING_LEN - strlen(str)); + if (test_info->offline_mask & (1 << i)) + strncat(str, " (offline)", + ETH_GSTRING_LEN - strlen(str)); + else + strncat(str, " (online)", + ETH_GSTRING_LEN - strlen(str)); + } } ethtool_init_exit: diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index 899c30fb5188..8b27137c63ac 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -19,8 +19,8 @@ #include "bnxt.h" #include "bnxt_xdp.h" -static void bnxt_xmit_xdp(struct bnxt *bp, struct bnxt_tx_ring_info *txr, - dma_addr_t mapping, u32 len, u16 rx_prod) +void bnxt_xmit_xdp(struct bnxt *bp, struct bnxt_tx_ring_info *txr, + dma_addr_t mapping, u32 len, u16 rx_prod) { struct bnxt_sw_tx_bd *tx_buf; struct tx_bd_ext *txbd1; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h index b529f2c5355b..12a5ad66b564 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h @@ -10,6 +10,8 @@ #ifndef BNXT_XDP_H #define BNXT_XDP_H +void bnxt_xmit_xdp(struct bnxt *bp, struct bnxt_tx_ring_info *txr, + dma_addr_t mapping, u32 len, u16 rx_prod); void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts); bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, struct page *page, u8 **data_ptr, unsigned int *len, -- cgit From 91725d89b97acea168a94c577d999801c3b3bcfb Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 4 Apr 2017 18:14:14 -0400 Subject: bnxt_en: Add PHY loopback to ethtool self-test. It is necessary to disable autoneg before enabling PHY loopback, otherwise link won't come up. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 62 ++++++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index ecb441724b10..dde3e21d8c56 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2192,6 +2192,54 @@ static int bnxt_hwrm_mac_loopback(struct bnxt *bp, bool enable) return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); } +static int bnxt_disable_an_for_lpbk(struct bnxt *bp, + struct hwrm_port_phy_cfg_input *req) +{ + struct bnxt_link_info *link_info = &bp->link_info; + u16 fw_advertising = link_info->advertising; + u16 fw_speed; + int rc; + + if (!link_info->autoneg) + return 0; + + fw_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_1GB; + if (netif_carrier_ok(bp->dev)) + fw_speed = bp->link_info.link_speed; + else if (fw_advertising & BNXT_LINK_SPEED_MSK_10GB) + fw_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_10GB; + else if (fw_advertising & BNXT_LINK_SPEED_MSK_25GB) + fw_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_25GB; + else if (fw_advertising & BNXT_LINK_SPEED_MSK_40GB) + fw_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_40GB; + else if (fw_advertising & BNXT_LINK_SPEED_MSK_50GB) + fw_speed = PORT_PHY_CFG_REQ_FORCE_LINK_SPEED_50GB; + + req->force_link_speed = cpu_to_le16(fw_speed); + req->flags |= cpu_to_le32(PORT_PHY_CFG_REQ_FLAGS_FORCE | + PORT_PHY_CFG_REQ_FLAGS_RESET_PHY); + rc = hwrm_send_message(bp, req, sizeof(*req), HWRM_CMD_TIMEOUT); + req->flags = 0; + req->force_link_speed = cpu_to_le16(0); + return rc; +} + +static int bnxt_hwrm_phy_loopback(struct bnxt *bp, bool enable) +{ + struct hwrm_port_phy_cfg_input req = {0}; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_PORT_PHY_CFG, -1, -1); + + if (enable) { + bnxt_disable_an_for_lpbk(bp, &req); + req.lpbk = PORT_PHY_CFG_REQ_LPBK_LOCAL; + } else { + req.lpbk = PORT_PHY_CFG_REQ_LPBK_NONE; + } + req.enables = cpu_to_le32(PORT_PHY_CFG_REQ_ENABLES_LPBK); + return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); +} + static int bnxt_rx_loopback(struct bnxt *bp, struct bnxt_napi *bnapi, u32 raw_cons, int pkt_size) { @@ -2318,8 +2366,9 @@ static int bnxt_run_fw_tests(struct bnxt *bp, u8 test_mask, u8 *test_results) return rc; } -#define BNXT_DRV_TESTS 1 +#define BNXT_DRV_TESTS 2 #define BNXT_MACLPBK_TEST_IDX (bp->num_tests - BNXT_DRV_TESTS) +#define BNXT_PHYLPBK_TEST_IDX (BNXT_MACLPBK_TEST_IDX + 1) static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, u64 *buf) @@ -2377,8 +2426,15 @@ static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, else buf[BNXT_MACLPBK_TEST_IDX] = 0; - bnxt_half_close_nic(bp); bnxt_hwrm_mac_loopback(bp, false); + bnxt_hwrm_phy_loopback(bp, true); + msleep(1000); + if (bnxt_run_loopback(bp)) { + buf[BNXT_PHYLPBK_TEST_IDX] = 1; + etest->flags |= ETH_TEST_FL_FAILED; + } + bnxt_hwrm_phy_loopback(bp, false); + bnxt_half_close_nic(bp); bnxt_open_nic(bp, false, true); } for (i = 0; i < bp->num_tests - BNXT_DRV_TESTS; i++) { @@ -2426,6 +2482,8 @@ void bnxt_ethtool_init(struct bnxt *bp) if (i == BNXT_MACLPBK_TEST_IDX) { strcpy(str, "Mac loopback test (offline)"); + } else if (i == BNXT_PHYLPBK_TEST_IDX) { + strcpy(str, "Phy loopback test (offline)"); } else { strlcpy(str, fw_str, ETH_GSTRING_LEN); strncat(str, " test", ETH_GSTRING_LEN - strlen(str)); -- cgit From 67fea463fd873492ab641459a6d1af0e9ea3c9ce Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 4 Apr 2017 18:14:15 -0400 Subject: bnxt_en: Add interrupt test to ethtool -t selftest. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 32 ++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index dde3e21d8c56..848ecf212b8f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -2178,6 +2178,29 @@ static int bnxt_set_phys_id(struct net_device *dev, return rc; } +static int bnxt_hwrm_selftest_irq(struct bnxt *bp, u16 cmpl_ring) +{ + struct hwrm_selftest_irq_input req = {0}; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_SELFTEST_IRQ, cmpl_ring, -1); + return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); +} + +static int bnxt_test_irq(struct bnxt *bp) +{ + int i; + + for (i = 0; i < bp->cp_nr_rings; i++) { + u16 cmpl_ring = bp->grp_info[i].cp_fw_ring_id; + int rc; + + rc = bnxt_hwrm_selftest_irq(bp, cmpl_ring); + if (rc) + return rc; + } + return 0; +} + static int bnxt_hwrm_mac_loopback(struct bnxt *bp, bool enable) { struct hwrm_port_mac_cfg_input req = {0}; @@ -2366,9 +2389,10 @@ static int bnxt_run_fw_tests(struct bnxt *bp, u8 test_mask, u8 *test_results) return rc; } -#define BNXT_DRV_TESTS 2 +#define BNXT_DRV_TESTS 3 #define BNXT_MACLPBK_TEST_IDX (bp->num_tests - BNXT_DRV_TESTS) #define BNXT_PHYLPBK_TEST_IDX (BNXT_MACLPBK_TEST_IDX + 1) +#define BNXT_IRQ_TEST_IDX (BNXT_MACLPBK_TEST_IDX + 2) static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, u64 *buf) @@ -2437,6 +2461,10 @@ static void bnxt_self_test(struct net_device *dev, struct ethtool_test *etest, bnxt_half_close_nic(bp); bnxt_open_nic(bp, false, true); } + if (bnxt_test_irq(bp)) { + buf[BNXT_IRQ_TEST_IDX] = 1; + etest->flags |= ETH_TEST_FL_FAILED; + } for (i = 0; i < bp->num_tests - BNXT_DRV_TESTS; i++) { u8 bit_val = 1 << i; @@ -2484,6 +2512,8 @@ void bnxt_ethtool_init(struct bnxt *bp) strcpy(str, "Mac loopback test (offline)"); } else if (i == BNXT_PHYLPBK_TEST_IDX) { strcpy(str, "Phy loopback test (offline)"); + } else if (i == BNXT_IRQ_TEST_IDX) { + strcpy(str, "Interrupt_test (offline)"); } else { strlcpy(str, fw_str, ETH_GSTRING_LEN); strncat(str, " test", ETH_GSTRING_LEN - strlen(str)); -- cgit From 932dbf83ba18bdb871e0c03a4ffdd9785f7a9c07 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 4 Apr 2017 18:14:16 -0400 Subject: bnxt_en: Use short TX BDs for the XDP TX ring. No offload is performed on the XDP_TX ring so we can use the short TX BDs. This has the effect of doubling the size of the XDP TX ring so that it now matches the size of the rx ring by default. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index 8b27137c63ac..9dae32756767 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -23,7 +23,6 @@ void bnxt_xmit_xdp(struct bnxt *bp, struct bnxt_tx_ring_info *txr, dma_addr_t mapping, u32 len, u16 rx_prod) { struct bnxt_sw_tx_bd *tx_buf; - struct tx_bd_ext *txbd1; struct tx_bd *txbd; u32 flags; u16 prod; @@ -33,22 +32,12 @@ void bnxt_xmit_xdp(struct bnxt *bp, struct bnxt_tx_ring_info *txr, tx_buf->rx_prod = rx_prod; txbd = &txr->tx_desc_ring[TX_RING(prod)][TX_IDX(prod)]; - flags = (len << TX_BD_LEN_SHIFT) | TX_BD_TYPE_LONG_TX_BD | - (2 << TX_BD_FLAGS_BD_CNT_SHIFT) | TX_BD_FLAGS_COAL_NOW | + flags = (len << TX_BD_LEN_SHIFT) | (1 << TX_BD_FLAGS_BD_CNT_SHIFT) | TX_BD_FLAGS_PACKET_END | bnxt_lhint_arr[len >> 9]; txbd->tx_bd_len_flags_type = cpu_to_le32(flags); txbd->tx_bd_opaque = prod; txbd->tx_bd_haddr = cpu_to_le64(mapping); - prod = NEXT_TX(prod); - txbd1 = (struct tx_bd_ext *) - &txr->tx_desc_ring[TX_RING(prod)][TX_IDX(prod)]; - - txbd1->tx_bd_hsize_lflags = cpu_to_le32(0); - txbd1->tx_bd_mss = cpu_to_le32(0); - txbd1->tx_bd_cfa_action = cpu_to_le32(0); - txbd1->tx_bd_cfa_meta = cpu_to_le32(0); - prod = NEXT_TX(prod); txr->tx_prod = prod; } @@ -66,7 +55,6 @@ void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts) for (i = 0; i < nr_pkts; i++) { last_tx_cons = tx_cons; tx_cons = NEXT_TX(tx_cons); - tx_cons = NEXT_TX(tx_cons); } txr->tx_cons = tx_cons; if (bnxt_tx_avail(bp, txr) == bp->tx_ring_size) { @@ -133,7 +121,7 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, return false; case XDP_TX: - if (tx_avail < 2) { + if (tx_avail < 1) { trace_xdp_exception(bp->dev, xdp_prog, act); bnxt_reuse_rx_data(rxr, cons, page); return true; -- cgit From 68a946bb81e07ed0e59a99e0c068d091ed42cc1b Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Tue, 4 Apr 2017 18:14:17 -0400 Subject: bnxt_en: Cap the msix vector with the max completion rings. The current code enables up to the maximum MSIX vectors in the PCIE config space without considering the max completion rings available. An MSIX vector is only useful when it has an associated completion ring, so it is better to cap it. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 9d71c19f5496..43b7342c6e82 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5183,9 +5183,10 @@ static unsigned int bnxt_get_max_func_irqs(struct bnxt *bp) { #if defined(CONFIG_BNXT_SRIOV) if (BNXT_VF(bp)) - return bp->vf.max_irqs; + return min_t(unsigned int, bp->vf.max_irqs, + bp->vf.max_cp_rings); #endif - return bp->pf.max_irqs; + return min_t(unsigned int, bp->pf.max_irqs, bp->pf.max_cp_rings); } void bnxt_set_max_func_irqs(struct bnxt *bp, unsigned int max_irqs) -- cgit From 1f37b177fd36790be4f281d538a8c9de67013606 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 2 Apr 2017 14:30:06 -0700 Subject: phy/ethtool: Add missing SPEED_ strings Add all the currently available SPEED_ strings. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 14 ++++++++++++++ include/uapi/linux/ethtool.h | 1 + 2 files changed, 15 insertions(+) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 867c42154087..6811d1ef4ef2 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -50,8 +50,22 @@ static const char *phy_speed_to_str(int speed) return "1Gbps"; case SPEED_2500: return "2.5Gbps"; + case SPEED_5000: + return "5Gbps"; case SPEED_10000: return "10Gbps"; + case SPEED_20000: + return "20Gbps"; + case SPEED_25000: + return "25Gbps"; + case SPEED_40000: + return "40Gbps"; + case SPEED_50000: + return "50Gbps"; + case SPEED_56000: + return "56Gbps"; + case SPEED_100000: + return "100Gbps"; case SPEED_UNKNOWN: return "Unknown"; default: diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 3dc91a46e8b8..5f4ea28eabe4 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -1487,6 +1487,7 @@ enum ethtool_link_mode_bit_indices { */ /* The forced speed, in units of 1Mb. All values 0 to INT_MAX are legal. */ +/* Update drivers/net/phy/phy.c:phy_speed_to_str() when adding new values */ #define SPEED_10 10 #define SPEED_100 100 #define SPEED_1000 1000 -- cgit From ab044f8e3eaf84c4cc95d7606fadfdfa006dc8ec Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 5 Apr 2017 13:46:31 +0200 Subject: batman-adv: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct batadv_priv, use stats from struct net_device. Signed-off-by: Tobias Klauser Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/distributed-arp-table.c | 9 +++++---- net/batman-adv/soft-interface.c | 2 +- net/batman-adv/types.h | 2 -- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 0608fcf99e7b..013e970eff39 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1003,6 +1003,7 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, bool ret = false; struct batadv_dat_entry *dat_entry = NULL; struct sk_buff *skb_new; + struct net_device *soft_iface = bat_priv->soft_iface; int hdr_size = 0; unsigned short vid; @@ -1061,10 +1062,10 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, if (!skb_new) goto out; - skb_new->protocol = eth_type_trans(skb_new, - bat_priv->soft_iface); - bat_priv->stats.rx_packets++; - bat_priv->stats.rx_bytes += skb->len + ETH_HLEN + hdr_size; + skb_new->protocol = eth_type_trans(skb_new, soft_iface); + + soft_iface->stats.rx_packets++; + soft_iface->stats.rx_bytes += skb->len + ETH_HLEN + hdr_size; netif_rx(skb_new); batadv_dbg(BATADV_DBG_DAT, bat_priv, "ARP request replied locally\n"); diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index f33bee08bd99..b223d3cacbae 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -118,7 +118,7 @@ static u64 batadv_sum_counter(struct batadv_priv *bat_priv, size_t idx) static struct net_device_stats *batadv_interface_stats(struct net_device *dev) { struct batadv_priv *bat_priv = netdev_priv(dev); - struct net_device_stats *stats = &bat_priv->stats; + struct net_device_stats *stats = &dev->stats; stats->tx_packets = batadv_sum_counter(bat_priv, BATADV_CNT_TX); stats->tx_bytes = batadv_sum_counter(bat_priv, BATADV_CNT_TX_BYTES); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 74b5af7dcd86..76873bf39d75 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1000,7 +1000,6 @@ struct batadv_priv_bat_v { * struct batadv_priv - per mesh interface data * @mesh_state: current status of the mesh (inactive/active/deactivating) * @soft_iface: net device which holds this struct as private data - * @stats: structure holding the data for the ndo_get_stats() call * @bat_counters: mesh internal traffic statistic counters (see batadv_counters) * @aggregated_ogms: bool indicating whether OGM aggregation is enabled * @bonding: bool indicating whether traffic bonding is enabled @@ -1055,7 +1054,6 @@ struct batadv_priv_bat_v { struct batadv_priv { atomic_t mesh_state; struct net_device *soft_iface; - struct net_device_stats stats; u64 __percpu *bat_counters; /* Per cpu counters */ atomic_t aggregated_ogms; atomic_t bonding; -- cgit From 03cf65a95937772b27d8a82836fec2b827ea1ecb Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Mon, 3 Apr 2017 16:34:04 +0100 Subject: net: stmmac: rx queue to dma channel mapping fix In hardware configurations where multiple queues are active, the rx queue needs to be mapped into a dma channel, even if a single rx queue is used. Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index c1c63197ff73..7cbda41dc996 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2001,7 +2001,7 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv) stmmac_configure_cbs(priv); /* Map RX MTL to DMA channels */ - if (rx_queues_count > 1 && priv->hw->mac->map_mtl_to_dma) + if (priv->hw->mac->map_mtl_to_dma) stmmac_rx_queue_dma_chan_map(priv); /* Enable MAC RX Queues */ -- cgit From 781159fb9c61b7a3b197c2046745fec4773806b4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 3 Apr 2017 21:18:27 +0300 Subject: liquidio: clear the correct memory There is a cut and paste bug here so we accidentally clear the first few bytes of "resp" a second time instead clearing "ctx". Fixes: 50c0add534d2 ("liquidio: refactor interrupt moderation code") Signed-off-by: Dan Carpenter Acked-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index fac02ed2c449..dab10c7e4443 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -1359,7 +1359,7 @@ static int octnet_get_intrmod_cfg(struct lio *lio, memset(resp, 0, sizeof(struct oct_intrmod_resp)); ctx = (struct oct_intrmod_context *)sc->ctxptr; - memset(resp, 0, sizeof(struct oct_intrmod_context)); + memset(ctx, 0, sizeof(struct oct_intrmod_context)); WRITE_ONCE(ctx->cond, 0); ctx->octeon_id = lio_get_device_id(oct_dev); init_waitqueue_head(&ctx->wc); -- cgit From 2c099ccbd093ffce056987a227fcc96e6a77b101 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 3 Apr 2017 21:18:41 +0300 Subject: net: sched: choke: remove some dead code We accidentally left this dead code behind after commit 5952fde10c35 ("net: sched: choke: remove dead filter classify code"). Signed-off-by: Dan Carpenter Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/sch_choke.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c index 03ce895d7ff5..593183a5b5b5 100644 --- a/net/sched/sch_choke.c +++ b/net/sched/sch_choke.c @@ -223,7 +223,6 @@ static bool choke_match_random(const struct choke_sched_data *q, static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) { - int ret = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS; struct choke_sched_data *q = qdisc_priv(sch); const struct red_parms *p = &q->parms; @@ -290,11 +289,6 @@ static int choke_enqueue(struct sk_buff *skb, struct Qdisc *sch, congestion_drop: qdisc_drop(skb, sch, to_free); return NET_XMIT_CN; - - if (ret & __NET_XMIT_BYPASS) - qdisc_qstats_drop(sch); - __qdisc_drop(skb, to_free); - return ret; } static struct sk_buff *choke_dequeue(struct Qdisc *sch) -- cgit From c800460086d1db3579b747d9297cecd2bbf70119 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 3 Apr 2017 21:25:22 +0300 Subject: qed: Add a missing error code We should be returning -ENOMEM if qed_mcp_cmd_add_elem() fails. The current code returns success. Fixes: 4ed1eea82a21 ("qed: Revise MFW command locking") Signed-off-by: Dan Carpenter Acked-by: Tomer Tayar Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_mcp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 619eac845028..ff6080df2246 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -455,8 +455,10 @@ _qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, qed_mcp_reread_offsets(p_hwfn, p_ptt); seq_num = ++p_hwfn->mcp_info->drv_mb_seq; p_cmd_elem = qed_mcp_cmd_add_elem(p_hwfn, p_mb_params, seq_num); - if (!p_cmd_elem) + if (!p_cmd_elem) { + rc = -ENOMEM; goto err; + } __qed_mcp_cmd_and_union(p_hwfn, p_ptt, p_mb_params, seq_num); spin_unlock_bh(&p_hwfn->mcp_info->cmd_lock); -- cgit From 457c79e54487b076cafa0e1ec5f177e751c54087 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Mon, 3 Apr 2017 18:13:32 -0700 Subject: netlink/diag: report flags for netlink sockets cb_running is reported in /proc/self/net/netlink and it is reported by the ss tool, when it gets information from the proc files. sock_diag is a new interface which is used instead of proc files, so it looks reasonable that this interface has to report no less information about sockets than proc files. We use these flags to dump and restore netlink sockets. Signed-off-by: Andrei Vagin Signed-off-by: David S. Miller --- include/uapi/linux/netlink_diag.h | 10 ++++++++++ net/netlink/af_netlink.c | 8 -------- net/netlink/af_netlink.h | 8 ++++++++ net/netlink/diag.c | 25 +++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/include/uapi/linux/netlink_diag.h b/include/uapi/linux/netlink_diag.h index 76b4d87c83a8..6dcd4de3397b 100644 --- a/include/uapi/linux/netlink_diag.h +++ b/include/uapi/linux/netlink_diag.h @@ -38,6 +38,7 @@ enum { NETLINK_DIAG_GROUPS, NETLINK_DIAG_RX_RING, NETLINK_DIAG_TX_RING, + NETLINK_DIAG_FLAGS, __NETLINK_DIAG_MAX, }; @@ -52,5 +53,14 @@ enum { /* deprecated since 4.6 */ #define NDIAG_SHOW_RING_CFG 0x00000004 /* show ring configuration */ #endif +#define NDIAG_SHOW_FLAGS 0x00000008 /* show flags of a netlink socket */ + +/* flags */ +#define NDIAG_FLAG_CB_RUNNING 0x00000001 +#define NDIAG_FLAG_PKTINFO 0x00000002 +#define NDIAG_FLAG_BROADCAST_ERROR 0x00000004 +#define NDIAG_FLAG_NO_ENOBUFS 0x00000008 +#define NDIAG_FLAG_LISTEN_ALL_NSID 0x00000010 +#define NDIAG_FLAG_CAP_ACK 0x00000020 #endif diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 596eaff66649..fc232441cf23 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -78,14 +78,6 @@ struct listeners { /* state bits */ #define NETLINK_S_CONGESTED 0x0 -/* flags */ -#define NETLINK_F_KERNEL_SOCKET 0x1 -#define NETLINK_F_RECV_PKTINFO 0x2 -#define NETLINK_F_BROADCAST_SEND_ERROR 0x4 -#define NETLINK_F_RECV_NO_ENOBUFS 0x8 -#define NETLINK_F_LISTEN_ALL_NSID 0x10 -#define NETLINK_F_CAP_ACK 0x20 - static inline int netlink_is_kernel(struct sock *sk) { return nlk_sk(sk)->flags & NETLINK_F_KERNEL_SOCKET; diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h index 4fdb38318977..f792f8d7f982 100644 --- a/net/netlink/af_netlink.h +++ b/net/netlink/af_netlink.h @@ -6,6 +6,14 @@ #include #include +/* flags */ +#define NETLINK_F_KERNEL_SOCKET 0x1 +#define NETLINK_F_RECV_PKTINFO 0x2 +#define NETLINK_F_BROADCAST_SEND_ERROR 0x4 +#define NETLINK_F_RECV_NO_ENOBUFS 0x8 +#define NETLINK_F_LISTEN_ALL_NSID 0x10 +#define NETLINK_F_CAP_ACK 0x20 + #define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8) #define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long)) diff --git a/net/netlink/diag.c b/net/netlink/diag.c index a5546249fb10..8faa20b4d457 100644 --- a/net/netlink/diag.c +++ b/net/netlink/diag.c @@ -19,6 +19,27 @@ static int sk_diag_dump_groups(struct sock *sk, struct sk_buff *nlskb) nlk->groups); } +static int sk_diag_put_flags(struct sock *sk, struct sk_buff *skb) +{ + struct netlink_sock *nlk = nlk_sk(sk); + u32 flags = 0; + + if (nlk->cb_running) + flags |= NDIAG_FLAG_CB_RUNNING; + if (nlk->flags & NETLINK_F_RECV_PKTINFO) + flags |= NDIAG_FLAG_PKTINFO; + if (nlk->flags & NETLINK_F_BROADCAST_SEND_ERROR) + flags |= NDIAG_FLAG_BROADCAST_ERROR; + if (nlk->flags & NETLINK_F_RECV_NO_ENOBUFS) + flags |= NDIAG_FLAG_NO_ENOBUFS; + if (nlk->flags & NETLINK_F_LISTEN_ALL_NSID) + flags |= NDIAG_FLAG_LISTEN_ALL_NSID; + if (nlk->flags & NETLINK_F_CAP_ACK) + flags |= NDIAG_FLAG_CAP_ACK; + + return nla_put_u32(skb, NETLINK_DIAG_FLAGS, flags); +} + static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct netlink_diag_req *req, u32 portid, u32 seq, u32 flags, int sk_ino) @@ -52,6 +73,10 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, sock_diag_put_meminfo(sk, skb, NETLINK_DIAG_MEMINFO)) goto out_nlmsg_trim; + if ((req->ndiag_show & NDIAG_SHOW_FLAGS) && + sk_diag_put_flags(sk, skb)) + goto out_nlmsg_trim; + nlmsg_end(skb, nlh); return 0; -- cgit From 3f3c278c94dd994fe0d9f21679ae19b9c0a55292 Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Mon, 3 Apr 2017 18:38:39 -0700 Subject: bonding: fix active-backup transition Earlier patch c4adfc822bf5 ("bonding: make speed, duplex setting consistent with link state") made an attempt to keep slave state consistent with speed and duplex settings. Unfortunately link-state transition is used to change the active link especially when used in conjunction with mii-mon. The above mentioned patch broke that logic. Also when speed and duplex settings for a link are updated during a link-event, the link-status should not be changed to invoke correct transition logic. This patch fixes this issue by moving the link-state update outside of the bond_update_speed_duplex() fn and to the places where this fn is called and update link-state selectively. Fixes: c4adfc822bf5 ("bonding: make speed, duplex setting consistent with link state") Signed-off-by: Mahesh Bandewar Reviewed-by: Andy Gospodarek Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 27359dab78a1..535388b15cde 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -378,20 +378,15 @@ static int bond_update_speed_duplex(struct slave *slave) slave->duplex = DUPLEX_UNKNOWN; res = __ethtool_get_link_ksettings(slave_dev, &ecmd); - if (res < 0) { - slave->link = BOND_LINK_DOWN; + if (res < 0) return 1; - } - if (ecmd.base.speed == 0 || ecmd.base.speed == ((__u32)-1)) { - slave->link = BOND_LINK_DOWN; + if (ecmd.base.speed == 0 || ecmd.base.speed == ((__u32)-1)) return 1; - } switch (ecmd.base.duplex) { case DUPLEX_FULL: case DUPLEX_HALF: break; default: - slave->link = BOND_LINK_DOWN; return 1; } @@ -1563,7 +1558,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) new_slave->delay = 0; new_slave->link_failure_count = 0; - bond_update_speed_duplex(new_slave); + if (bond_update_speed_duplex(new_slave)) + new_slave->link = BOND_LINK_DOWN; new_slave->last_rx = jiffies - (msecs_to_jiffies(bond->params.arp_interval) + 1); @@ -2126,6 +2122,7 @@ static void bond_miimon_commit(struct bonding *bond) case BOND_LINK_UP: if (bond_update_speed_duplex(slave)) { + slave->link = BOND_LINK_DOWN; netdev_warn(bond->dev, "failed to get link speed/duplex for %s\n", slave->dev->name); -- cgit From 5e351410667ab0bf0dd1845730cba8b2211781e7 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 3 Apr 2017 22:50:20 -0700 Subject: net: ibm: emac: remove unused sysrq handler for 'c' key Since commit d6580a9f1523 ("kexec: sysrq: simplify sysrq-c handler"), the sysrq handler for the 'c' key has been sysrq_crash_op. Debugging code in the ibm_emac driver also tries to register a handler for the 'c' key, but this has no effect because register_sysrq_key() doesn't replace existing handlers. Since evidently no one has cared enough to fix this in the last 8 years, and it's very rare for drivers to register sysrq handlers (for good reason), just remove the dead code. Signed-off-by: Eric Biggers Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/emac/Makefile | 1 - drivers/net/ethernet/ibm/emac/core.c | 7 - drivers/net/ethernet/ibm/emac/debug.c | 270 --------------------------------- drivers/net/ethernet/ibm/emac/debug.h | 23 --- drivers/net/ethernet/ibm/emac/mal.c | 4 - drivers/tty/sysrq.c | 2 +- 6 files changed, 1 insertion(+), 306 deletions(-) delete mode 100644 drivers/net/ethernet/ibm/emac/debug.c diff --git a/drivers/net/ethernet/ibm/emac/Makefile b/drivers/net/ethernet/ibm/emac/Makefile index eba21835d90d..98768ba0955a 100644 --- a/drivers/net/ethernet/ibm/emac/Makefile +++ b/drivers/net/ethernet/ibm/emac/Makefile @@ -8,4 +8,3 @@ ibm_emac-y := mal.o core.o phy.o ibm_emac-$(CONFIG_IBM_EMAC_ZMII) += zmii.o ibm_emac-$(CONFIG_IBM_EMAC_RGMII) += rgmii.o ibm_emac-$(CONFIG_IBM_EMAC_TAH) += tah.o -ibm_emac-$(CONFIG_IBM_EMAC_DEBUG) += debug.o diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index c44036d5761a..0f9cd92fdc5e 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -3173,8 +3173,6 @@ static int emac_probe(struct platform_device *ofdev) printk("%s: found %s PHY (0x%02x)\n", ndev->name, dev->phy.def->name, dev->phy.address); - emac_dbg_register(dev); - /* Life is good */ return 0; @@ -3243,7 +3241,6 @@ static int emac_remove(struct platform_device *ofdev) mal_unregister_commac(dev->mal, &dev->commac); emac_put_deps(dev); - emac_dbg_unregister(dev); iounmap(dev->emacp); if (dev->wol_irq) @@ -3326,9 +3323,6 @@ static int __init emac_init(void) printk(KERN_INFO DRV_DESC ", version " DRV_VERSION "\n"); - /* Init debug stuff */ - emac_init_debug(); - /* Build EMAC boot list */ emac_make_bootlist(); @@ -3373,7 +3367,6 @@ static void __exit emac_exit(void) rgmii_exit(); zmii_exit(); mal_exit(); - emac_fini_debug(); /* Destroy EMAC boot list */ for (i = 0; i < EMAC_BOOT_LIST_SIZE; i++) diff --git a/drivers/net/ethernet/ibm/emac/debug.c b/drivers/net/ethernet/ibm/emac/debug.c deleted file mode 100644 index a559f326bf63..000000000000 --- a/drivers/net/ethernet/ibm/emac/debug.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * drivers/net/ethernet/ibm/emac/debug.c - * - * Driver for PowerPC 4xx on-chip ethernet controller, debug print routines. - * - * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. - * - * - * Based on the arch/ppc version of the driver: - * - * Copyright (c) 2004, 2005 Zultys Technologies - * Eugene Surovegin or - * - * 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 "core.h" - -static DEFINE_SPINLOCK(emac_dbg_lock); - -static void emac_desc_dump(struct emac_instance *p) -{ - int i; - printk("** EMAC %s TX BDs **\n" - " tx_cnt = %d tx_slot = %d ack_slot = %d\n", - p->ofdev->dev.of_node->full_name, - p->tx_cnt, p->tx_slot, p->ack_slot); - for (i = 0; i < NUM_TX_BUFF / 2; ++i) - printk - ("bd[%2d] 0x%08x %c 0x%04x %4u - bd[%2d] 0x%08x %c 0x%04x %4u\n", - i, p->tx_desc[i].data_ptr, p->tx_skb[i] ? 'V' : ' ', - p->tx_desc[i].ctrl, p->tx_desc[i].data_len, - NUM_TX_BUFF / 2 + i, - p->tx_desc[NUM_TX_BUFF / 2 + i].data_ptr, - p->tx_skb[NUM_TX_BUFF / 2 + i] ? 'V' : ' ', - p->tx_desc[NUM_TX_BUFF / 2 + i].ctrl, - p->tx_desc[NUM_TX_BUFF / 2 + i].data_len); - - printk("** EMAC %s RX BDs **\n" - " rx_slot = %d flags = 0x%lx rx_skb_size = %d rx_sync_size = %d\n" - " rx_sg_skb = 0x%p\n", - p->ofdev->dev.of_node->full_name, - p->rx_slot, p->commac.flags, p->rx_skb_size, - p->rx_sync_size, p->rx_sg_skb); - for (i = 0; i < NUM_RX_BUFF / 2; ++i) - printk - ("bd[%2d] 0x%08x %c 0x%04x %4u - bd[%2d] 0x%08x %c 0x%04x %4u\n", - i, p->rx_desc[i].data_ptr, p->rx_skb[i] ? 'V' : ' ', - p->rx_desc[i].ctrl, p->rx_desc[i].data_len, - NUM_RX_BUFF / 2 + i, - p->rx_desc[NUM_RX_BUFF / 2 + i].data_ptr, - p->rx_skb[NUM_RX_BUFF / 2 + i] ? 'V' : ' ', - p->rx_desc[NUM_RX_BUFF / 2 + i].ctrl, - p->rx_desc[NUM_RX_BUFF / 2 + i].data_len); -} - -static void emac_mac_dump(struct emac_instance *dev) -{ - struct emac_regs __iomem *p = dev->emacp; - const int xaht_regs = EMAC_XAHT_REGS(dev); - u32 *gaht_base = emac_gaht_base(dev); - u32 *iaht_base = emac_iaht_base(dev); - int emac4sync = emac_has_feature(dev, EMAC_FTR_EMAC4SYNC); - int n; - - printk("** EMAC %s registers **\n" - "MR0 = 0x%08x MR1 = 0x%08x TMR0 = 0x%08x TMR1 = 0x%08x\n" - "RMR = 0x%08x ISR = 0x%08x ISER = 0x%08x\n" - "IAR = %04x%08x VTPID = 0x%04x VTCI = 0x%04x\n", - dev->ofdev->dev.of_node->full_name, - in_be32(&p->mr0), in_be32(&p->mr1), - in_be32(&p->tmr0), in_be32(&p->tmr1), - in_be32(&p->rmr), in_be32(&p->isr), in_be32(&p->iser), - in_be32(&p->iahr), in_be32(&p->ialr), in_be32(&p->vtpid), - in_be32(&p->vtci) - ); - - if (emac4sync) - printk("MAR = %04x%08x MMAR = %04x%08x\n", - in_be32(&p->u0.emac4sync.mahr), - in_be32(&p->u0.emac4sync.malr), - in_be32(&p->u0.emac4sync.mmahr), - in_be32(&p->u0.emac4sync.mmalr) - ); - - for (n = 0; n < xaht_regs; n++) - printk("IAHT%02d = 0x%08x\n", n + 1, in_be32(iaht_base + n)); - - for (n = 0; n < xaht_regs; n++) - printk("GAHT%02d = 0x%08x\n", n + 1, in_be32(gaht_base + n)); - - printk("LSA = %04x%08x IPGVR = 0x%04x\n" - "STACR = 0x%08x TRTR = 0x%08x RWMR = 0x%08x\n" - "OCTX = 0x%08x OCRX = 0x%08x\n", - in_be32(&p->lsah), in_be32(&p->lsal), in_be32(&p->ipgvr), - in_be32(&p->stacr), in_be32(&p->trtr), in_be32(&p->rwmr), - in_be32(&p->octx), in_be32(&p->ocrx) - ); - - if (!emac4sync) { - printk("IPCR = 0x%08x\n", - in_be32(&p->u1.emac4.ipcr) - ); - } else { - printk("REVID = 0x%08x TPC = 0x%08x\n", - in_be32(&p->u1.emac4sync.revid), - in_be32(&p->u1.emac4sync.tpc) - ); - } - - emac_desc_dump(dev); -} - -static void emac_mal_dump(struct mal_instance *mal) -{ - int i; - - printk("** MAL %s Registers **\n" - "CFG = 0x%08x ESR = 0x%08x IER = 0x%08x\n" - "TX|CASR = 0x%08x CARR = 0x%08x EOBISR = 0x%08x DEIR = 0x%08x\n" - "RX|CASR = 0x%08x CARR = 0x%08x EOBISR = 0x%08x DEIR = 0x%08x\n", - mal->ofdev->dev.of_node->full_name, - get_mal_dcrn(mal, MAL_CFG), get_mal_dcrn(mal, MAL_ESR), - get_mal_dcrn(mal, MAL_IER), - get_mal_dcrn(mal, MAL_TXCASR), get_mal_dcrn(mal, MAL_TXCARR), - get_mal_dcrn(mal, MAL_TXEOBISR), get_mal_dcrn(mal, MAL_TXDEIR), - get_mal_dcrn(mal, MAL_RXCASR), get_mal_dcrn(mal, MAL_RXCARR), - get_mal_dcrn(mal, MAL_RXEOBISR), get_mal_dcrn(mal, MAL_RXDEIR) - ); - - printk("TX|"); - for (i = 0; i < mal->num_tx_chans; ++i) { - if (i && !(i % 4)) - printk("\n "); - printk("CTP%d = 0x%08x ", i, get_mal_dcrn(mal, MAL_TXCTPR(i))); - } - printk("\nRX|"); - for (i = 0; i < mal->num_rx_chans; ++i) { - if (i && !(i % 4)) - printk("\n "); - printk("CTP%d = 0x%08x ", i, get_mal_dcrn(mal, MAL_RXCTPR(i))); - } - printk("\n "); - for (i = 0; i < mal->num_rx_chans; ++i) { - u32 r = get_mal_dcrn(mal, MAL_RCBS(i)); - if (i && !(i % 3)) - printk("\n "); - printk("RCBS%d = 0x%08x (%d) ", i, r, r * 16); - } - printk("\n"); -} - -static struct emac_instance *__emacs[4]; -static struct mal_instance *__mals[1]; - -void emac_dbg_register(struct emac_instance *dev) -{ - unsigned long flags; - int i; - - spin_lock_irqsave(&emac_dbg_lock, flags); - for (i = 0; i < ARRAY_SIZE(__emacs); i++) - if (__emacs[i] == NULL) { - __emacs[i] = dev; - break; - } - spin_unlock_irqrestore(&emac_dbg_lock, flags); -} - -void emac_dbg_unregister(struct emac_instance *dev) -{ - unsigned long flags; - int i; - - spin_lock_irqsave(&emac_dbg_lock, flags); - for (i = 0; i < ARRAY_SIZE(__emacs); i++) - if (__emacs[i] == dev) { - __emacs[i] = NULL; - break; - } - spin_unlock_irqrestore(&emac_dbg_lock, flags); -} - -void mal_dbg_register(struct mal_instance *mal) -{ - unsigned long flags; - int i; - - spin_lock_irqsave(&emac_dbg_lock, flags); - for (i = 0; i < ARRAY_SIZE(__mals); i++) - if (__mals[i] == NULL) { - __mals[i] = mal; - break; - } - spin_unlock_irqrestore(&emac_dbg_lock, flags); -} - -void mal_dbg_unregister(struct mal_instance *mal) -{ - unsigned long flags; - int i; - - spin_lock_irqsave(&emac_dbg_lock, flags); - for (i = 0; i < ARRAY_SIZE(__mals); i++) - if (__mals[i] == mal) { - __mals[i] = NULL; - break; - } - spin_unlock_irqrestore(&emac_dbg_lock, flags); -} - -void emac_dbg_dump_all(void) -{ - unsigned int i; - unsigned long flags; - - spin_lock_irqsave(&emac_dbg_lock, flags); - - for (i = 0; i < ARRAY_SIZE(__mals); ++i) - if (__mals[i]) - emac_mal_dump(__mals[i]); - - for (i = 0; i < ARRAY_SIZE(__emacs); ++i) - if (__emacs[i]) - emac_mac_dump(__emacs[i]); - - spin_unlock_irqrestore(&emac_dbg_lock, flags); -} - -#if defined(CONFIG_MAGIC_SYSRQ) -static void emac_sysrq_handler(int key) -{ - emac_dbg_dump_all(); -} - -static struct sysrq_key_op emac_sysrq_op = { - .handler = emac_sysrq_handler, - .help_msg = "emac(c)", - .action_msg = "Show EMAC(s) status", -}; - -int __init emac_init_debug(void) -{ - return register_sysrq_key('c', &emac_sysrq_op); -} - -void __exit emac_fini_debug(void) -{ - unregister_sysrq_key('c', &emac_sysrq_op); -} - -#else -int __init emac_init_debug(void) -{ - return 0; -} -void __exit emac_fini_debug(void) -{ -} -#endif /* CONFIG_MAGIC_SYSRQ */ diff --git a/drivers/net/ethernet/ibm/emac/debug.h b/drivers/net/ethernet/ibm/emac/debug.h index 9c45efe4c8fe..5bdfc174a07e 100644 --- a/drivers/net/ethernet/ibm/emac/debug.h +++ b/drivers/net/ethernet/ibm/emac/debug.h @@ -25,32 +25,9 @@ #include "core.h" #if defined(CONFIG_IBM_EMAC_DEBUG) - -struct emac_instance; -struct mal_instance; - -void emac_dbg_register(struct emac_instance *dev); -void emac_dbg_unregister(struct emac_instance *dev); -void mal_dbg_register(struct mal_instance *mal); -void mal_dbg_unregister(struct mal_instance *mal); -int emac_init_debug(void) __init; -void emac_fini_debug(void) __exit; -void emac_dbg_dump_all(void); - # define DBG_LEVEL 1 - #else - -# define emac_dbg_register(x) do { } while(0) -# define emac_dbg_unregister(x) do { } while(0) -# define mal_dbg_register(x) do { } while(0) -# define mal_dbg_unregister(x) do { } while(0) -# define emac_init_debug() do { } while(0) -# define emac_fini_debug() do { } while(0) -# define emac_dbg_dump_all() do { } while(0) - # define DBG_LEVEL 0 - #endif #define EMAC_DBG(d, name, fmt, arg...) \ diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c index cd3227b088b7..91b1a558f37d 100644 --- a/drivers/net/ethernet/ibm/emac/mal.c +++ b/drivers/net/ethernet/ibm/emac/mal.c @@ -695,8 +695,6 @@ static int mal_probe(struct platform_device *ofdev) wmb(); platform_set_drvdata(ofdev, mal); - mal_dbg_register(mal); - return 0; fail6: @@ -740,8 +738,6 @@ static int mal_remove(struct platform_device *ofdev) mal_reset(mal); - mal_dbg_unregister(mal); - dma_free_coherent(&ofdev->dev, sizeof(struct mal_descriptor) * (NUM_TX_BUFF * mal->num_tx_chans + diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index c6fc7141d7b2..677f0ddc986c 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -446,7 +446,7 @@ static struct sysrq_key_op *sysrq_key_table[36] = { */ NULL, /* a */ &sysrq_reboot_op, /* b */ - &sysrq_crash_op, /* c & ibm_emac driver debug */ + &sysrq_crash_op, /* c */ &sysrq_showlocks_op, /* d */ &sysrq_term_op, /* e */ &sysrq_moom_op, /* f */ -- cgit From 589c49cbf9674808fd4ac9b7c17155abc0686f86 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Tue, 4 Apr 2017 21:09:48 +0800 Subject: net: tcp: Define the TCP_MAX_WSCALE instead of literal number 14 Define one new macro TCP_MAX_WSCALE instead of literal number '14', and use U16_MAX instead of 65535 as the max value of TCP window. There is another minor change, use rounddown(space, mss) instead of (space / mss) * mss; Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- include/net/tcp.h | 3 +++ net/ipv4/tcp.c | 2 +- net/ipv4/tcp_input.c | 9 +++++---- net/ipv4/tcp_output.c | 12 +++++------- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 582e3772c0d9..cc6ae0a95201 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -78,6 +78,9 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); /* Maximal number of ACKs sent quickly to accelerate slow-start. */ #define TCP_MAX_QUICKACKS 16U +/* Maximal number of window scale according to RFC1323 */ +#define TCP_MAX_WSCALE 14U + /* urg_data states */ #define TCP_URG_VALID 0x0100 #define TCP_URG_NOTYET 0x0200 diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1665948dff8c..94f0b5b50e0d 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2393,7 +2393,7 @@ static int tcp_repair_options_est(struct tcp_sock *tp, u16 snd_wscale = opt.opt_val & 0xFFFF; u16 rcv_wscale = opt.opt_val >> 16; - if (snd_wscale > 14 || rcv_wscale > 14) + if (snd_wscale > TCP_MAX_WSCALE || rcv_wscale > TCP_MAX_WSCALE) return -EFBIG; tp->rx_opt.snd_wscale = snd_wscale; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a75c48f62e27..ed6606cf5b3e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3759,11 +3759,12 @@ void tcp_parse_options(const struct sk_buff *skb, !estab && sysctl_tcp_window_scaling) { __u8 snd_wscale = *(__u8 *)ptr; opt_rx->wscale_ok = 1; - if (snd_wscale > 14) { - net_info_ratelimited("%s: Illegal window scaling value %d >14 received\n", + if (snd_wscale > TCP_MAX_WSCALE) { + net_info_ratelimited("%s: Illegal window scaling value %d > %u received\n", __func__, - snd_wscale); - snd_wscale = 14; + snd_wscale, + TCP_MAX_WSCALE); + snd_wscale = TCP_MAX_WSCALE; } opt_rx->snd_wscale = snd_wscale; } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 13971942211b..0e807a83c1bc 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -212,12 +212,12 @@ void tcp_select_initial_window(int __space, __u32 mss, /* If no clamp set the clamp to the max possible scaled window */ if (*window_clamp == 0) - (*window_clamp) = (65535 << 14); + (*window_clamp) = (U16_MAX << TCP_MAX_WSCALE); space = min(*window_clamp, space); /* Quantize space offering to a multiple of mss if possible. */ if (space > mss) - space = (space / mss) * mss; + space = rounddown(space, mss); /* NOTE: offering an initial window larger than 32767 * will break some buggy TCP stacks. If the admin tells us @@ -234,13 +234,11 @@ void tcp_select_initial_window(int __space, __u32 mss, (*rcv_wscale) = 0; if (wscale_ok) { - /* Set window scaling on max possible window - * See RFC1323 for an explanation of the limit to 14 - */ + /* Set window scaling on max possible window */ space = max_t(u32, space, sysctl_tcp_rmem[2]); space = max_t(u32, space, sysctl_rmem_max); space = min_t(u32, space, *window_clamp); - while (space > 65535 && (*rcv_wscale) < 14) { + while (space > U16_MAX && (*rcv_wscale) < TCP_MAX_WSCALE) { space >>= 1; (*rcv_wscale)++; } @@ -253,7 +251,7 @@ void tcp_select_initial_window(int __space, __u32 mss, } /* Set the clamp no higher than max representable value */ - (*window_clamp) = min(65535U << (*rcv_wscale), *window_clamp); + (*window_clamp) = min_t(__u32, U16_MAX << (*rcv_wscale), *window_clamp); } EXPORT_SYMBOL(tcp_select_initial_window); -- cgit From 5138e86f176055e8194bf30fa2e05bc839ce1a1f Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Tue, 4 Apr 2017 09:23:41 -0400 Subject: rtnetlink: Convert rtnetlink_event to white list The rtnetlink_event currently functions as a blacklist where we block cerntain netdev events from being sent to user space. As a result, events have been added to the system that userspace probably doesn't care about. This patch converts the implementation to the white list so that newly events would have to be specifically added to the list to be sent to userspace. This would force new event implementers to consider whether a given event is usefull to user space or if it's just a kernel event. Signed-off-by: Vladislav Yasevich Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 9c3947a43eff..58419da7961b 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4116,22 +4116,25 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi struct net_device *dev = netdev_notifier_info_to_dev(ptr); switch (event) { - case NETDEV_UP: - case NETDEV_DOWN: - case NETDEV_PRE_UP: - case NETDEV_POST_INIT: - case NETDEV_REGISTER: - case NETDEV_CHANGE: - case NETDEV_PRE_TYPE_CHANGE: - case NETDEV_GOING_DOWN: - case NETDEV_UNREGISTER: - case NETDEV_UNREGISTER_FINAL: - case NETDEV_RELEASE: - case NETDEV_JOIN: - case NETDEV_BONDING_INFO: + case NETDEV_REBOOT: + case NETDEV_CHANGEMTU: + case NETDEV_CHANGEADDR: + case NETDEV_CHANGENAME: + case NETDEV_FEAT_CHANGE: + case NETDEV_BONDING_FAILOVER: + case NETDEV_POST_TYPE_CHANGE: + case NETDEV_NOTIFY_PEERS: + case NETDEV_CHANGEUPPER: + case NETDEV_RESEND_IGMP: + case NETDEV_PRECHANGEMTU: + case NETDEV_CHANGEINFODATA: + case NETDEV_PRECHANGEUPPER: + case NETDEV_CHANGELOWERSTATE: + case NETDEV_UDP_TUNNEL_PUSH_INFO: + case NETDEV_CHANGE_TX_QUEUE_LEN: + rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL); break; default: - rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL); break; } return NOTIFY_DONE; -- cgit From def12888c161e6fec0702e5ec9c3962846e3a21d Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Tue, 4 Apr 2017 09:23:42 -0400 Subject: rtnl: Add support for netdev event to link messages When netdev events happen, a rtnetlink_event() handler will send messages for every event in it's white list. These messages contain current information about a particular device, but they do not include the iformation about which event just happened. The consumer of the message has to try to infer this information. In some cases (ex: NETDEV_NOTIFY_PEERS), that is not possible. This patch adds a new extension to RTM_NEWLINK message called IFLA_EVENT that would have an encoding of the which event triggered this message. This would allow the the message consumer to easily determine if it is interested in a particular event or not. Signed-off-by: Vladislav Yasevich Signed-off-by: David S. Miller --- include/linux/rtnetlink.h | 3 +- include/uapi/linux/if_link.h | 21 ++++++++++ net/core/dev.c | 2 +- net/core/rtnetlink.c | 92 +++++++++++++++++++++++++++++++++++++++----- 4 files changed, 107 insertions(+), 11 deletions(-) diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 57e54847b0b9..0459018173cf 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -18,7 +18,8 @@ extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change, gfp_t flags); struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, - unsigned change, gfp_t flags); + unsigned change, unsigned long event, + gfp_t flags); void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, gfp_t flags); diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 8b405afb2376..97f6d302f627 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -157,6 +157,7 @@ enum { IFLA_GSO_MAX_SIZE, IFLA_PAD, IFLA_XDP, + IFLA_EVENT, __IFLA_MAX }; @@ -899,4 +900,24 @@ enum { #define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1) +enum { + IFLA_EVENT_UNSPEC, + IFLA_EVENT_REBOOT, + IFLA_EVENT_CHANGE_MTU, + IFLA_EVENT_CHANGE_ADDR, + IFLA_EVENT_CHANGE_NAME, + IFLA_EVENT_FEAT_CHANGE, + IFLA_EVENT_BONDING_FAILOVER, + IFLA_EVENT_POST_TYPE_CHANGE, + IFLA_EVENT_NOTIFY_PEERS, + IFLA_EVENT_CHANGE_UPPER, + IFLA_EVENT_RESEND_IGMP, + IFLA_EVENT_PRE_CHANGE_MTU, + IFLA_EVENT_CHANGE_INFO_DATA, + IFLA_EVENT_PRE_CHANGE_UPPER, + IFLA_EVENT_CHANGE_LOWER_STATE, + IFLA_EVENT_UDP_TUNNEL_PUSH_INFO, + IFLA_EVENT_CHANGE_TX_QUEUE_LEN, +}; + #endif /* _UAPI_LINUX_IF_LINK_H */ diff --git a/net/core/dev.c b/net/core/dev.c index ef9fe60ee294..7efb4178ffef 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6840,7 +6840,7 @@ static void rollback_registered_many(struct list_head *head) if (!dev->rtnl_link_ops || dev->rtnl_link_state == RTNL_LINK_INITIALIZED) - skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U, + skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U, 0, GFP_KERNEL); /* diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 58419da7961b..b2bd4c9ee860 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -944,6 +944,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_SWITCH_ID */ + nla_total_size(IFNAMSIZ) /* IFLA_PHYS_PORT_NAME */ + rtnl_xdp_size(dev) /* IFLA_XDP */ + + nla_total_size(4) /* IFLA_EVENT */ + nla_total_size(1); /* IFLA_PROTO_DOWN */ } @@ -1276,9 +1277,70 @@ err_cancel: return err; } +static int rtnl_fill_link_event(struct sk_buff *skb, unsigned long event) +{ + u32 rtnl_event; + + switch (event) { + case NETDEV_REBOOT: + rtnl_event = IFLA_EVENT_REBOOT; + break; + case NETDEV_CHANGEMTU: + rtnl_event = IFLA_EVENT_CHANGE_MTU; + break; + case NETDEV_CHANGEADDR: + rtnl_event = IFLA_EVENT_CHANGE_ADDR; + break; + case NETDEV_CHANGENAME: + rtnl_event = IFLA_EVENT_CHANGE_NAME; + break; + case NETDEV_FEAT_CHANGE: + rtnl_event = IFLA_EVENT_FEAT_CHANGE; + break; + case NETDEV_BONDING_FAILOVER: + rtnl_event = IFLA_EVENT_BONDING_FAILOVER; + break; + case NETDEV_POST_TYPE_CHANGE: + rtnl_event = IFLA_EVENT_POST_TYPE_CHANGE; + break; + case NETDEV_NOTIFY_PEERS: + rtnl_event = IFLA_EVENT_NOTIFY_PEERS; + break; + case NETDEV_CHANGEUPPER: + rtnl_event = IFLA_EVENT_CHANGE_UPPER; + break; + case NETDEV_RESEND_IGMP: + rtnl_event = IFLA_EVENT_RESEND_IGMP; + break; + case NETDEV_PRECHANGEMTU: + rtnl_event = IFLA_EVENT_PRE_CHANGE_MTU; + break; + case NETDEV_CHANGEINFODATA: + rtnl_event = IFLA_EVENT_CHANGE_INFO_DATA; + break; + case NETDEV_PRECHANGEUPPER: + rtnl_event = IFLA_EVENT_PRE_CHANGE_UPPER; + break; + case NETDEV_CHANGELOWERSTATE: + rtnl_event = IFLA_EVENT_CHANGE_LOWER_STATE; + break; + case NETDEV_UDP_TUNNEL_PUSH_INFO: + rtnl_event = IFLA_EVENT_UDP_TUNNEL_PUSH_INFO; + break; + case NETDEV_CHANGE_TX_QUEUE_LEN: + rtnl_event = IFLA_EVENT_CHANGE_TX_QUEUE_LEN; + break; + default: + return 0; + } + + return nla_put_u32(skb, IFLA_EVENT, rtnl_event); +} + static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, int type, u32 pid, u32 seq, u32 change, - unsigned int flags, u32 ext_filter_mask) + unsigned int flags, u32 ext_filter_mask, + unsigned long event) { struct ifinfomsg *ifm; struct nlmsghdr *nlh; @@ -1327,6 +1389,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, nla_put_u8(skb, IFLA_PROTO_DOWN, dev->proto_down)) goto nla_put_failure; + if (rtnl_fill_link_event(skb, event)) + goto nla_put_failure; + if (rtnl_fill_link_ifmap(skb, dev)) goto nla_put_failure; @@ -1461,6 +1526,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_LINK_NETNSID] = { .type = NLA_S32 }, [IFLA_PROTO_DOWN] = { .type = NLA_U8 }, [IFLA_XDP] = { .type = NLA_NESTED }, + [IFLA_EVENT] = { .type = NLA_U32 }, }; static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { @@ -1619,7 +1685,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 0, flags, - ext_filter_mask); + ext_filter_mask, 0); /* If we ran out of room on the first message, * we're in trouble */ @@ -2710,7 +2776,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh) return -ENOBUFS; err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).portid, - nlh->nlmsg_seq, 0, 0, ext_filter_mask); + nlh->nlmsg_seq, 0, 0, ext_filter_mask, 0); if (err < 0) { /* -EMSGSIZE implies BUG in if_nlmsg_size */ WARN_ON(err == -EMSGSIZE); @@ -2782,7 +2848,8 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) } struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, - unsigned int change, gfp_t flags) + unsigned int change, + unsigned long event, gfp_t flags) { struct net *net = dev_net(dev); struct sk_buff *skb; @@ -2793,7 +2860,7 @@ struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, if (skb == NULL) goto errout; - err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0, 0); + err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0, 0, event); if (err < 0) { /* -EMSGSIZE implies BUG in if_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); @@ -2814,18 +2881,25 @@ void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, gfp_t flags) rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, flags); } -void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change, - gfp_t flags) +static void rtmsg_ifinfo_event(int type, struct net_device *dev, + unsigned int change, unsigned long event, + gfp_t flags) { struct sk_buff *skb; if (dev->reg_state != NETREG_REGISTERED) return; - skb = rtmsg_ifinfo_build_skb(type, dev, change, flags); + skb = rtmsg_ifinfo_build_skb(type, dev, change, event, flags); if (skb) rtmsg_ifinfo_send(skb, dev, flags); } + +void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change, + gfp_t flags) +{ + rtmsg_ifinfo_event(type, dev, change, 0, flags); +} EXPORT_SYMBOL(rtmsg_ifinfo); static int nlmsg_populate_fdb_fill(struct sk_buff *skb, @@ -4132,7 +4206,7 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_CHANGELOWERSTATE: case NETDEV_UDP_TUNNEL_PUSH_INFO: case NETDEV_CHANGE_TX_QUEUE_LEN: - rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL); + rtmsg_ifinfo_event(RTM_NEWLINK, dev, 0, event, GFP_KERNEL); break; default: break; -- cgit From af0e54619d9b6c23f084d310d35fca2e68faa95c Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Tue, 4 Apr 2017 15:32:47 +0200 Subject: selftests: add a generic testsuite for ethernet device This patch add a generic testsuite for testing ethernet network device driver. Signed-off-by: Corentin Labbe Tested-by: Andrew Lunn Signed-off-by: David S. Miller --- tools/testing/selftests/net/Makefile | 2 +- tools/testing/selftests/net/netdevice.sh | 200 +++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+), 1 deletion(-) create mode 100755 tools/testing/selftests/net/netdevice.sh diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index fbfe5d0d5c2e..35cbb4cba410 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -5,7 +5,7 @@ CFLAGS += -I../../../../usr/include/ reuseport_bpf_numa: LDFLAGS += -lnuma -TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh +TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh TEST_GEN_FILES = socket TEST_GEN_FILES += psock_fanout psock_tpacket TEST_GEN_FILES += reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa diff --git a/tools/testing/selftests/net/netdevice.sh b/tools/testing/selftests/net/netdevice.sh new file mode 100755 index 000000000000..4e00568d70c2 --- /dev/null +++ b/tools/testing/selftests/net/netdevice.sh @@ -0,0 +1,200 @@ +#!/bin/sh +# +# This test is for checking network interface +# For the moment it tests only ethernet interface (but wifi could be easily added) +# +# We assume that all network driver are loaded +# if not they probably have failed earlier in the boot process and their logged error will be catched by another test +# + +# this function will try to up the interface +# if already up, nothing done +# arg1: network interface name +kci_net_start() +{ + netdev=$1 + + ip link show "$netdev" |grep -q UP + if [ $? -eq 0 ];then + echo "SKIP: $netdev: interface already up" + return 0 + fi + + ip link set "$netdev" up + if [ $? -ne 0 ];then + echo "FAIL: $netdev: Fail to up interface" + return 1 + else + echo "PASS: $netdev: set interface up" + NETDEV_STARTED=1 + fi + return 0 +} + +# this function will try to setup an IP and MAC address on a network interface +# Doing nothing if the interface was already up +# arg1: network interface name +kci_net_setup() +{ + netdev=$1 + + # do nothing if the interface was already up + if [ $NETDEV_STARTED -eq 0 ];then + return 0 + fi + + MACADDR='02:03:04:05:06:07' + ip link set dev $netdev address "$MACADDR" + if [ $? -ne 0 ];then + echo "FAIL: $netdev: Cannot set MAC address" + else + ip link show $netdev |grep -q "$MACADDR" + if [ $? -eq 0 ];then + echo "PASS: $netdev: set MAC address" + else + echo "FAIL: $netdev: Cannot set MAC address" + fi + fi + + #check that the interface did not already have an IP + ip address show "$netdev" |grep '^[[:space:]]*inet' + if [ $? -eq 0 ];then + echo "SKIP: $netdev: already have an IP" + return 0 + fi + + # TODO what ipaddr to set ? DHCP ? + echo "SKIP: $netdev: set IP address" + return 0 +} + +# test an ethtool command +# arg1: return code for not supported (see ethtool code source) +# arg2: summary of the command +# arg3: command to execute +kci_netdev_ethtool_test() +{ + if [ $# -le 2 ];then + echo "SKIP: $netdev: ethtool: invalid number of arguments" + return 1 + fi + $3 >/dev/null + ret=$? + if [ $ret -ne 0 ];then + if [ $ret -eq "$1" ];then + echo "SKIP: $netdev: ethtool $2 not supported" + else + echo "FAIL: $netdev: ethtool $2" + return 1 + fi + else + echo "PASS: $netdev: ethtool $2" + fi + return 0 +} + +# test ethtool commands +# arg1: network interface name +kci_netdev_ethtool() +{ + netdev=$1 + + #check presence of ethtool + ethtool --version 2>/dev/null >/dev/null + if [ $? -ne 0 ];then + echo "SKIP: ethtool not present" + return 1 + fi + + TMP_ETHTOOL_FEATURES="$(mktemp)" + if [ ! -e "$TMP_ETHTOOL_FEATURES" ];then + echo "SKIP: Cannot create a tmp file" + return 1 + fi + + ethtool -k "$netdev" > "$TMP_ETHTOOL_FEATURES" + if [ $? -ne 0 ];then + echo "FAIL: $netdev: ethtool list features" + rm "$TMP_ETHTOOL_FEATURES" + return 1 + fi + echo "PASS: $netdev: ethtool list features" + #TODO for each non fixed features, try to turn them on/off + rm "$TMP_ETHTOOL_FEATURES" + + kci_netdev_ethtool_test 74 'dump' "ethtool -d $netdev" + kci_netdev_ethtool_test 94 'stats' "ethtool -S $netdev" + return 0 +} + +# stop a netdev +# arg1: network interface name +kci_netdev_stop() +{ + netdev=$1 + + if [ $NETDEV_STARTED -eq 0 ];then + echo "SKIP: $netdev: interface kept up" + return 0 + fi + + ip link set "$netdev" down + if [ $? -ne 0 ];then + echo "FAIL: $netdev: stop interface" + return 1 + fi + echo "PASS: $netdev: stop interface" + return 0 +} + +# run all test on a netdev +# arg1: network interface name +kci_test_netdev() +{ + NETDEV_STARTED=0 + IFACE_TO_UPDOWN="$1" + IFACE_TO_TEST="$1" + #check for VLAN interface + MASTER_IFACE="$(echo $1 | cut -d@ -f2)" + if [ ! -z "$MASTER_IFACE" ];then + IFACE_TO_UPDOWN="$MASTER_IFACE" + IFACE_TO_TEST="$(echo $1 | cut -d@ -f1)" + fi + + NETDEV_STARTED=0 + kci_net_start "$IFACE_TO_UPDOWN" + + kci_net_setup "$IFACE_TO_TEST" + + kci_netdev_ethtool "$IFACE_TO_TEST" + + kci_netdev_stop "$IFACE_TO_UPDOWN" + return 0 +} + +#check for needed privileges +if [ "$(id -u)" -ne 0 ];then + echo "SKIP: Need root privileges" + exit 0 +fi + +ip -Version 2>/dev/null >/dev/null +if [ $? -ne 0 ];then + echo "SKIP: Could not run test without the ip tool" + exit 0 +fi + +TMP_LIST_NETDEV="$(mktemp)" +if [ ! -e "$TMP_LIST_NETDEV" ];then + echo "FAIL: Cannot create a tmp file" + exit 1 +fi + +ip link show |grep '^[0-9]' | grep -oE '[[:space:]].*eth[0-9]*:|[[:space:]].*enp[0-9]s[0-9]:' | cut -d\ -f2 | cut -d: -f1> "$TMP_LIST_NETDEV" +while read netdev +do + kci_test_netdev "$netdev" +done < "$TMP_LIST_NETDEV" + +rm "$TMP_LIST_NETDEV" +exit 0 -- cgit From 265aeb511bd590f193661c6cffff43a8a80755e4 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:22 -0700 Subject: nfp: add support for .get_link_ksettings() Read link speed from the BAR. This provides very basic information and works for both PFs and VFs. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h | 13 ++++++ .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 49 ++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index 71d86171b4ee..d04ccc9f6116 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -177,6 +177,19 @@ #define NFP_NET_CFG_VERSION_MINOR(x) (((x) & 0xff) << 0) #define NFP_NET_CFG_STS 0x0034 #define NFP_NET_CFG_STS_LINK (0x1 << 0) /* Link up or down */ +/* Link rate */ +#define NFP_NET_CFG_STS_LINK_RATE_SHIFT 1 +#define NFP_NET_CFG_STS_LINK_RATE_MASK 0xF +#define NFP_NET_CFG_STS_LINK_RATE \ + (NFP_NET_CFG_STS_LINK_RATE_MASK << NFP_NET_CFG_STS_LINK_RATE_SHIFT) +#define NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED 0 +#define NFP_NET_CFG_STS_LINK_RATE_UNKNOWN 1 +#define NFP_NET_CFG_STS_LINK_RATE_1G 2 +#define NFP_NET_CFG_STS_LINK_RATE_10G 3 +#define NFP_NET_CFG_STS_LINK_RATE_25G 4 +#define NFP_NET_CFG_STS_LINK_RATE_40G 5 +#define NFP_NET_CFG_STS_LINK_RATE_50G 6 +#define NFP_NET_CFG_STS_LINK_RATE_100G 7 #define NFP_NET_CFG_CAP 0x0038 #define NFP_NET_CFG_MAX_TXRINGS 0x003c #define NFP_NET_CFG_MAX_RXRINGS 0x0040 diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index ed22a813e579..d3cec0d4a978 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -173,6 +173,54 @@ static void nfp_net_get_drvinfo(struct net_device *netdev, drvinfo->regdump_len = NFP_NET_CFG_BAR_SZ; } +/** + * nfp_net_get_link_ksettings - Get Link Speed settings + * @netdev: network interface device structure + * @cmd: ethtool command + * + * Reports speed settings based on info in the BAR provided by the fw. + */ +static int +nfp_net_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + static const u32 ls_to_ethtool[] = { + [NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED] = 0, + [NFP_NET_CFG_STS_LINK_RATE_UNKNOWN] = SPEED_UNKNOWN, + [NFP_NET_CFG_STS_LINK_RATE_1G] = SPEED_1000, + [NFP_NET_CFG_STS_LINK_RATE_10G] = SPEED_10000, + [NFP_NET_CFG_STS_LINK_RATE_25G] = SPEED_25000, + [NFP_NET_CFG_STS_LINK_RATE_40G] = SPEED_40000, + [NFP_NET_CFG_STS_LINK_RATE_50G] = SPEED_50000, + [NFP_NET_CFG_STS_LINK_RATE_100G] = SPEED_100000, + }; + struct nfp_net *nn = netdev_priv(netdev); + u32 sts, ls; + + ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); + cmd->base.port = PORT_OTHER; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; + + if (!netif_carrier_ok(netdev)) + return 0; + + sts = nn_readl(nn, NFP_NET_CFG_STS); + + ls = FIELD_GET(NFP_NET_CFG_STS_LINK_RATE, sts); + if (ls == NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED) + return -EOPNOTSUPP; + + if (ls == NFP_NET_CFG_STS_LINK_RATE_UNKNOWN || + ls >= ARRAY_SIZE(ls_to_ethtool)) + return 0; + + cmd->base.speed = ls_to_ethtool[sts]; + cmd->base.duplex = DUPLEX_FULL; + + return 0; +} + static void nfp_net_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) { @@ -814,6 +862,7 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { .set_coalesce = nfp_net_set_coalesce, .get_channels = nfp_net_get_channels, .set_channels = nfp_net_set_channels, + .get_link_ksettings = nfp_net_get_link_ksettings, }; void nfp_net_set_ethtool_ops(struct net_device *netdev) -- cgit From b9de00770db50ce11f64cd7676dbcaf295a1926f Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:23 -0700 Subject: nfp: don't spawn netdevs for reconfigured ports After port reconfiguration (port split, media type change) firmware will continue to report old configuration until reboot. NSP will inform us that reconfiguration is pending. To avoid user confusion refuse to spawn netdevs until the new configuration is applied (reboot). We need to split the netdev to eth_table port matching from MAC search and move it earlier in the probe() flow. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_main.h | 5 +- drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 79 +++++++++++++--------- .../ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c | 12 +++- .../ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h | 3 + 4 files changed, 62 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index 39105d0435e9..bb15a5724bf7 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -64,7 +64,8 @@ struct nfp_eth_table; * @fw_loaded: Is the firmware loaded? * @eth_tbl: NSP ETH table * @ddir: Per-device debugfs directory - * @num_ports: Number of adapter ports + * @num_ports: Number of adapter ports app firmware supports + * @num_netdevs: Number of netdevs spawned * @ports: Linked list of port structures (struct nfp_net) */ struct nfp_pf { @@ -88,6 +89,8 @@ struct nfp_pf { struct dentry *ddir; unsigned int num_ports; + unsigned int num_netdevs; + struct list_head ports; }; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 2025cb7c6d90..1644954f52cd 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -129,14 +129,29 @@ err_area: return (u8 __iomem *)ERR_PTR(err); } +/** + * nfp_net_get_mac_addr() - Get the MAC address. + * @nn: NFP Network structure + * @cpp: NFP CPP handle + * @id: NFP port id + * + * First try to get the MAC address from NSP ETH table. If that + * fails try HWInfo. As a last resort generate a random address. + */ static void -nfp_net_get_mac_addr_hwinfo(struct nfp_net_dp *dp, struct nfp_cpp *cpp, - unsigned int id) +nfp_net_get_mac_addr(struct nfp_net *nn, struct nfp_cpp *cpp, unsigned int id) { + struct nfp_net_dp *dp = &nn->dp; u8 mac_addr[ETH_ALEN]; const char *mac_str; char name[32]; + if (nn->eth_port) { + ether_addr_copy(dp->netdev->dev_addr, nn->eth_port->mac_addr); + ether_addr_copy(dp->netdev->perm_addr, nn->eth_port->mac_addr); + return; + } + snprintf(name, sizeof(name), "eth%d.mac", id); mac_str = nfp_hwinfo_lookup(cpp, name); @@ -159,32 +174,16 @@ nfp_net_get_mac_addr_hwinfo(struct nfp_net_dp *dp, struct nfp_cpp *cpp, ether_addr_copy(dp->netdev->perm_addr, mac_addr); } -/** - * nfp_net_get_mac_addr() - Get the MAC address. - * @nn: NFP Network structure - * @pf: NFP PF device structure - * @id: NFP port id - * - * First try to get the MAC address from NSP ETH table. If that - * fails try HWInfo. As a last resort generate a random address. - */ -static void -nfp_net_get_mac_addr(struct nfp_net *nn, struct nfp_pf *pf, unsigned int id) +static struct nfp_eth_table_port * +nfp_net_find_port(struct nfp_pf *pf, unsigned int id) { int i; for (i = 0; pf->eth_tbl && i < pf->eth_tbl->count; i++) - if (pf->eth_tbl->ports[i].eth_index == id) { - const u8 *mac_addr = pf->eth_tbl->ports[i].mac_addr; - - nn->eth_port = &pf->eth_tbl->ports[i]; + if (pf->eth_tbl->ports[i].eth_index == id) + return &pf->eth_tbl->ports[i]; - ether_addr_copy(nn->dp.netdev->dev_addr, mac_addr); - ether_addr_copy(nn->dp.netdev->perm_addr, mac_addr); - return; - } - - nfp_net_get_mac_addr_hwinfo(&nn->dp, pf->cpp, id); + return NULL; } static unsigned int nfp_net_pf_get_num_ports(struct nfp_pf *pf) @@ -283,6 +282,7 @@ static void nfp_net_pf_free_netdevs(struct nfp_pf *pf) while (!list_empty(&pf->ports)) { nn = list_first_entry(&pf->ports, struct nfp_net, port_list); list_del(&nn->port_list); + pf->num_netdevs--; nfp_net_netdev_free(nn); } @@ -291,7 +291,8 @@ static void nfp_net_pf_free_netdevs(struct nfp_pf *pf) static struct nfp_net * nfp_net_pf_alloc_port_netdev(struct nfp_pf *pf, void __iomem *ctrl_bar, void __iomem *tx_bar, void __iomem *rx_bar, - int stride, struct nfp_net_fw_version *fw_ver) + int stride, struct nfp_net_fw_version *fw_ver, + struct nfp_eth_table_port *eth_port) { u32 n_tx_rings, n_rx_rings; struct nfp_net *nn; @@ -312,6 +313,7 @@ nfp_net_pf_alloc_port_netdev(struct nfp_pf *pf, void __iomem *ctrl_bar, nn->dp.is_vf = 0; nn->stride_rx = stride; nn->stride_tx = stride; + nn->eth_port = eth_port; return nn; } @@ -323,7 +325,7 @@ nfp_net_pf_init_port_netdev(struct nfp_pf *pf, struct nfp_net *nn, int err; /* Get MAC address */ - nfp_net_get_mac_addr(nn, pf, id); + nfp_net_get_mac_addr(nn, pf->cpp, id); /* Get ME clock frequency from ctrl BAR * XXX for now frequency is hardcoded until we figure out how @@ -348,6 +350,7 @@ nfp_net_pf_alloc_netdevs(struct nfp_pf *pf, void __iomem *ctrl_bar, int stride, struct nfp_net_fw_version *fw_ver) { u32 prev_tx_base, prev_rx_base, tgt_tx_base, tgt_rx_base; + struct nfp_eth_table_port *eth_port; struct nfp_net *nn; unsigned int i; int err; @@ -363,17 +366,27 @@ nfp_net_pf_alloc_netdevs(struct nfp_pf *pf, void __iomem *ctrl_bar, prev_tx_base = tgt_tx_base; prev_rx_base = tgt_rx_base; - nn = nfp_net_pf_alloc_port_netdev(pf, ctrl_bar, tx_bar, rx_bar, - stride, fw_ver); - if (IS_ERR(nn)) { - err = PTR_ERR(nn); - goto err_free_prev; + eth_port = nfp_net_find_port(pf, i); + if (eth_port && eth_port->override_changed) { + nfp_warn(pf->cpp, "Config changed for port #%d, reboot required before port will be operational\n", i); + } else { + nn = nfp_net_pf_alloc_port_netdev(pf, ctrl_bar, tx_bar, + rx_bar, stride, + fw_ver, eth_port); + if (IS_ERR(nn)) { + err = PTR_ERR(nn); + goto err_free_prev; + } + list_add_tail(&nn->port_list, &pf->ports); + pf->num_netdevs++; } - list_add_tail(&nn->port_list, &pf->ports); ctrl_bar += NFP_PF_CSR_SLICE_SIZE; } + if (list_empty(&pf->ports)) + return -ENODEV; + return 0; err_free_prev: @@ -409,7 +422,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf, } num_irqs = nfp_net_irqs_alloc(pf->pdev, pf->irq_entries, - NFP_NET_MIN_PORT_IRQS * pf->num_ports, + NFP_NET_MIN_PORT_IRQS * pf->num_netdevs, wanted_irqs); if (!num_irqs) { nn_warn(nn, "Unable to allocate MSI-X Vectors. Exiting\n"); @@ -419,7 +432,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf, /* Distribute IRQs to ports */ irqs_left = num_irqs; - ports_left = pf->num_ports; + ports_left = pf->num_netdevs; list_for_each_entry(nn, &pf->ports, port_list) { unsigned int n; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 38bd80077e33..932772fbd27e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -62,6 +62,7 @@ #define NSP_ETH_STATE_TX_ENABLED BIT_ULL(2) #define NSP_ETH_STATE_RX_ENABLED BIT_ULL(3) #define NSP_ETH_STATE_RATE GENMASK_ULL(11, 8) +#define NSP_ETH_STATE_OVRD_CHNG BIT_ULL(22) #define NSP_ETH_CTRL_ENABLED BIT_ULL(1) #define NSP_ETH_CTRL_TX_ENABLED BIT_ULL(2) @@ -110,8 +111,8 @@ static void nfp_eth_copy_mac_reverse(u8 *dst, const u8 *src) } static void -nfp_eth_port_translate(const struct eth_table_entry *src, unsigned int index, - struct nfp_eth_table_port *dst) +nfp_eth_port_translate(struct nfp_nsp *nsp, const struct eth_table_entry *src, + unsigned int index, struct nfp_eth_table_port *dst) { unsigned int rate; u64 port, state; @@ -136,6 +137,11 @@ nfp_eth_port_translate(const struct eth_table_entry *src, unsigned int index, dst->label_port = FIELD_GET(NSP_ETH_PORT_PHYLABEL, port); dst->label_subport = FIELD_GET(NSP_ETH_PORT_LABEL, port); + + if (nfp_nsp_get_abi_ver_minor(nsp) < 17) + return; + + dst->override_changed = FIELD_GET(NSP_ETH_STATE_OVRD_CHNG, state); } static void @@ -225,7 +231,7 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) table->count = cnt; for (i = 0, j = 0; i < NSP_ETH_MAX_COUNT; i++) if (entries[i].port & NSP_ETH_PORT_LANES_MASK) - nfp_eth_port_translate(&entries[i], i, + nfp_eth_port_translate(nsp, &entries[i], i, &table->ports[j++]); nfp_eth_mark_split_ports(cpp, table); diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h index 325e841ca90a..6838741fadd7 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h @@ -54,6 +54,7 @@ * @enabled: is enabled? * @tx_enabled: is TX enabled? * @rx_enabled: is RX enabled? + * @override_changed: is media reconfig pending? * * @is_split: is interface part of a split port */ @@ -76,6 +77,8 @@ struct nfp_eth_table { bool tx_enabled; bool rx_enabled; + bool override_changed; + /* Computed fields */ bool is_split; } ports[0]; -- cgit From d12537df343ec21054769f59bda3b15879644f52 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:24 -0700 Subject: nfp: add mutex protection for the port list We will want to unregister netdevs after their port got reconfigured. For that we need to make sure manipulations of port list from the port reconfiguration flow will not race with driver's .remove() callback. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_main.c | 3 +-- drivers/net/ethernet/netronome/nfp/nfp_main.h | 3 +++ drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 19 +++++++++++++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index dedac720fb29..96266796fd09 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -385,8 +385,7 @@ static void nfp_pci_remove(struct pci_dev *pdev) { struct nfp_pf *pf = pci_get_drvdata(pdev); - if (!list_empty(&pf->ports)) - nfp_net_pci_remove(pf); + nfp_net_pci_remove(pf); nfp_pcie_sriov_disable(pdev); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index bb15a5724bf7..b7ceec9a5783 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -42,6 +42,7 @@ #include #include #include +#include #include struct dentry; @@ -67,6 +68,7 @@ struct nfp_eth_table; * @num_ports: Number of adapter ports app firmware supports * @num_netdevs: Number of netdevs spawned * @ports: Linked list of port structures (struct nfp_net) + * @port_lock: Protects @ports, @num_ports, @num_netdevs */ struct nfp_pf { struct pci_dev *pdev; @@ -92,6 +94,7 @@ struct nfp_pf { unsigned int num_netdevs; struct list_head ports; + struct mutex port_lock; }; extern struct pci_driver nfp_netvf_pci_driver; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 1644954f52cd..4d602b1ddc90 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -481,17 +481,22 @@ int nfp_net_pci_probe(struct nfp_pf *pf) int stride; int err; + mutex_init(&pf->port_lock); + /* Verify that the board has completed initialization */ if (!nfp_is_ready(pf->cpp)) { nfp_err(pf->cpp, "NFP is not ready for NIC operation.\n"); return -EINVAL; } + mutex_lock(&pf->port_lock); pf->num_ports = nfp_net_pf_get_num_ports(pf); ctrl_bar = nfp_net_pf_map_ctrl_bar(pf); - if (!ctrl_bar) - return pf->fw_loaded ? -EINVAL : -EPROBE_DEFER; + if (!ctrl_bar) { + err = pf->fw_loaded ? -EINVAL : -EPROBE_DEFER; + goto err_unlock; + } nfp_net_get_fw_version(&fw_ver, ctrl_bar); if (fw_ver.resv || fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) { @@ -565,6 +570,8 @@ int nfp_net_pci_probe(struct nfp_pf *pf) if (err) goto err_clean_ddir; + mutex_unlock(&pf->port_lock); + return 0; err_clean_ddir: @@ -574,6 +581,8 @@ err_unmap_tx: nfp_cpp_area_release_free(pf->tx_area); err_ctrl_unmap: nfp_cpp_area_release_free(pf->ctrl_area); +err_unlock: + mutex_unlock(&pf->port_lock); return err; } @@ -581,6 +590,10 @@ void nfp_net_pci_remove(struct nfp_pf *pf) { struct nfp_net *nn; + mutex_lock(&pf->port_lock); + if (list_empty(&pf->ports)) + goto out; + list_for_each_entry(nn, &pf->ports, port_list) { nfp_net_debugfs_dir_clean(&nn->debugfs_dir); @@ -597,4 +610,6 @@ void nfp_net_pci_remove(struct nfp_pf *pf) nfp_cpp_area_release_free(pf->rx_area); nfp_cpp_area_release_free(pf->tx_area); nfp_cpp_area_release_free(pf->ctrl_area); +out: + mutex_unlock(&pf->port_lock); } -- cgit From cee4295133ba3b85b2f22b542d8855232deba4a3 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:25 -0700 Subject: nfp: track link state changes For caching link settings - remember if we have seen link events since the last time the eth_port information was refreshed. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 6 +++++- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 8e04aa0e6e87..91e963b5104f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -523,7 +523,8 @@ struct nfp_net_dp { * @reconfig_sync_present: Some thread is performing synchronous reconfig * @reconfig_timer: Timer for async reading of reconfig results * @link_up: Is the link up? - * @link_status_lock: Protects @link_up and ensures atomicity with BAR reading + * @link_changed: Has link state changes since last port refresh? + * @link_status_lock: Protects @link_* and ensures atomicity with BAR reading * @rx_coalesce_usecs: RX interrupt moderation usecs delay parameter * @rx_coalesce_max_frames: RX interrupt moderation frame count parameter * @tx_coalesce_usecs: TX interrupt moderation usecs delay parameter @@ -580,6 +581,7 @@ struct nfp_net { u32 me_freq_mhz; bool link_up; + bool link_changed; spinlock_t link_status_lock; spinlock_t reconfig_lock; @@ -810,6 +812,8 @@ nfp_net_irqs_assign(struct nfp_net *nn, struct msix_entry *irq_entries, struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn); int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new); +bool nfp_net_link_changed_read_clear(struct nfp_net *nn); + #ifdef CONFIG_NFP_DEBUG void nfp_net_debugfs_create(void); void nfp_net_debugfs_destroy(void); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 8f2da128ce0f..36028b752503 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -376,6 +376,19 @@ static irqreturn_t nfp_net_irq_rxtx(int irq, void *data) return IRQ_HANDLED; } +bool nfp_net_link_changed_read_clear(struct nfp_net *nn) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(&nn->link_status_lock, flags); + ret = nn->link_changed; + nn->link_changed = false; + spin_unlock_irqrestore(&nn->link_status_lock, flags); + + return ret; +} + /** * nfp_net_read_link_status() - Reread link status from control BAR * @nn: NFP Network structure @@ -395,6 +408,7 @@ static void nfp_net_read_link_status(struct nfp_net *nn) goto out; nn->link_up = link_up; + nn->link_changed = true; if (nn->link_up) { netif_carrier_on(nn->dp.netdev); -- cgit From 172f638c93dd0b90b231d07662e64025b1e53e38 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:26 -0700 Subject: nfp: add port state refresh We will need a way of refreshing port state for link settings get/set. For get we need to refresh port speed and type. When settings are changed the reconfiguration may require reboot before it's effective. Unregister netdevs affected by reconfiguration from a workqueue. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_main.h | 3 + drivers/net/ethernet/netronome/nfp/nfp_net.h | 1 + drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 89 +++++++++++++++++++++-- 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index b7ceec9a5783..b57de047b002 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -44,6 +44,7 @@ #include #include #include +#include struct dentry; struct pci_dev; @@ -69,6 +70,7 @@ struct nfp_eth_table; * @num_netdevs: Number of netdevs spawned * @ports: Linked list of port structures (struct nfp_net) * @port_lock: Protects @ports, @num_ports, @num_netdevs + * @port_refresh_work: Work entry for taking netdevs out */ struct nfp_pf { struct pci_dev *pdev; @@ -94,6 +96,7 @@ struct nfp_pf { unsigned int num_netdevs; struct list_head ports; + struct work_struct port_refresh_work; struct mutex port_lock; }; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 91e963b5104f..052db9208fbb 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -813,6 +813,7 @@ struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn); int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new); bool nfp_net_link_changed_read_clear(struct nfp_net *nn); +void nfp_net_refresh_port_config(struct nfp_net *nn); #ifdef CONFIG_NFP_DEBUG void nfp_net_debugfs_create(void); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 4d602b1ddc90..8e975c36877c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -47,6 +47,7 @@ #include #include #include +#include #include "nfpcore/nfp.h" #include "nfpcore/nfp_cpp.h" @@ -468,6 +469,82 @@ err_nn_free: return err; } +static void nfp_net_pci_remove_finish(struct nfp_pf *pf) +{ + nfp_net_debugfs_dir_clean(&pf->ddir); + + nfp_net_irqs_disable(pf->pdev); + kfree(pf->irq_entries); + + nfp_cpp_area_release_free(pf->rx_area); + nfp_cpp_area_release_free(pf->tx_area); + nfp_cpp_area_release_free(pf->ctrl_area); +} + +static void nfp_net_refresh_netdevs(struct work_struct *work) +{ + struct nfp_pf *pf = container_of(work, struct nfp_pf, + port_refresh_work); + struct nfp_net *nn, *next; + + mutex_lock(&pf->port_lock); + + /* Check for nfp_net_pci_remove() racing against us */ + if (list_empty(&pf->ports)) + goto out; + + list_for_each_entry_safe(nn, next, &pf->ports, port_list) { + if (!nn->eth_port) { + nfp_warn(pf->cpp, "Warning: port %d not present after reconfig\n", + nn->eth_port->eth_index); + continue; + } + if (!nn->eth_port->override_changed) + continue; + + nn_warn(nn, "Port config changed, unregistering. Reboot required before port will be operational again.\n"); + + nfp_net_debugfs_dir_clean(&nn->debugfs_dir); + nfp_net_netdev_clean(nn->dp.netdev); + + list_del(&nn->port_list); + pf->num_netdevs--; + nfp_net_netdev_free(nn); + } + + if (list_empty(&pf->ports)) + nfp_net_pci_remove_finish(pf); +out: + mutex_unlock(&pf->port_lock); +} + +void nfp_net_refresh_port_config(struct nfp_net *nn) +{ + struct nfp_pf *pf = pci_get_drvdata(nn->pdev); + struct nfp_eth_table *old_table; + + ASSERT_RTNL(); + + old_table = pf->eth_tbl; + + list_for_each_entry(nn, &pf->ports, port_list) + nfp_net_link_changed_read_clear(nn); + + pf->eth_tbl = nfp_eth_read_ports(pf->cpp); + if (!pf->eth_tbl) { + pf->eth_tbl = old_table; + nfp_err(pf->cpp, "Error refreshing port config!\n"); + return; + } + + list_for_each_entry(nn, &pf->ports, port_list) + nn->eth_port = nfp_net_find_port(pf, nn->eth_port->eth_index); + + kfree(old_table); + + schedule_work(&pf->port_refresh_work); +} + /* * PCI device functions */ @@ -481,6 +558,7 @@ int nfp_net_pci_probe(struct nfp_pf *pf) int stride; int err; + INIT_WORK(&pf->port_refresh_work, nfp_net_refresh_netdevs); mutex_init(&pf->port_lock); /* Verify that the board has completed initialization */ @@ -602,14 +680,9 @@ void nfp_net_pci_remove(struct nfp_pf *pf) nfp_net_pf_free_netdevs(pf); - nfp_net_debugfs_dir_clean(&pf->ddir); - - nfp_net_irqs_disable(pf->pdev); - kfree(pf->irq_entries); - - nfp_cpp_area_release_free(pf->rx_area); - nfp_cpp_area_release_free(pf->tx_area); - nfp_cpp_area_release_free(pf->ctrl_area); + nfp_net_pci_remove_finish(pf); out: mutex_unlock(&pf->port_lock); + + cancel_work_sync(&pf->port_refresh_work); } -- cgit From 21d529d5eb31f905f8c028956204a0d1bdafbc4b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:27 -0700 Subject: nfp: report link speed from NSP On the PF prefer the link speed value provided by the NSP. Refresh port table if needed. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index d3cec0d4a978..0fdc14e7b576 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -49,6 +49,7 @@ #include #include "nfpcore/nfp.h" +#include "nfpcore/nfp_nsp_eth.h" #include "nfp_net_ctrl.h" #include "nfp_net.h" @@ -205,6 +206,16 @@ nfp_net_get_link_ksettings(struct net_device *netdev, if (!netif_carrier_ok(netdev)) return 0; + /* Use link speed from ETH table if available, otherwise try the BAR */ + if (nn->eth_port && nfp_net_link_changed_read_clear(nn)) + nfp_net_refresh_port_config(nn); + /* Separate if - on FW error the port could've disappeared from table */ + if (nn->eth_port) { + cmd->base.speed = nn->eth_port->speed; + cmd->base.duplex = DUPLEX_FULL; + return 0; + } + sts = nn_readl(nn, NFP_NET_CFG_STS); ls = FIELD_GET(NFP_NET_CFG_STS_LINK_RATE, sts); -- cgit From 42b1e6aa4672beba79882673c687985b884f7904 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:28 -0700 Subject: nfp: report auto-negotiation in ethtool NSP ABI version 0.17 is exposing the autonegotiation settings. Report whether autoneg is on via ethtool. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c | 4 ++++ drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c | 2 ++ drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h | 11 +++++++++++ 3 files changed, 17 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 0fdc14e7b576..563ced3c99e1 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -203,6 +203,10 @@ nfp_net_get_link_ksettings(struct net_device *netdev, cmd->base.speed = SPEED_UNKNOWN; cmd->base.duplex = DUPLEX_UNKNOWN; + if (nn->eth_port) + cmd->base.autoneg = nn->eth_port->aneg != NFP_ANEG_DISABLED ? + AUTONEG_ENABLE : AUTONEG_DISABLE; + if (!netif_carrier_ok(netdev)) return 0; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 932772fbd27e..dcb1bc81e554 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -63,6 +63,7 @@ #define NSP_ETH_STATE_RX_ENABLED BIT_ULL(3) #define NSP_ETH_STATE_RATE GENMASK_ULL(11, 8) #define NSP_ETH_STATE_OVRD_CHNG BIT_ULL(22) +#define NSP_ETH_STATE_ANEG GENMASK_ULL(25, 23) #define NSP_ETH_CTRL_ENABLED BIT_ULL(1) #define NSP_ETH_CTRL_TX_ENABLED BIT_ULL(2) @@ -142,6 +143,7 @@ nfp_eth_port_translate(struct nfp_nsp *nsp, const struct eth_table_entry *src, return; dst->override_changed = FIELD_GET(NSP_ETH_STATE_OVRD_CHNG, state); + dst->aneg = FIELD_GET(NSP_ETH_STATE_ANEG, state); } static void diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h index 6838741fadd7..6b3e954e70b3 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h @@ -37,6 +37,14 @@ #include #include +enum nfp_eth_aneg { + NFP_ANEG_AUTO = 0, + NFP_ANEG_SEARCH, + NFP_ANEG_25G_CONSORTIUM, + NFP_ANEG_25G_IEEE, + NFP_ANEG_DISABLED, +}; + /** * struct nfp_eth_table - ETH table information * @count: number of table entries @@ -48,6 +56,7 @@ * @base: first channel index (within NBI) * @lanes: number of channels * @speed: interface speed (in Mbps) + * @aneg: auto negotiation mode * @mac_addr: interface MAC address * @label_port: port id * @label_subport: id of interface within port (for split ports) @@ -68,6 +77,8 @@ struct nfp_eth_table { unsigned int lanes; unsigned int speed; + enum nfp_eth_aneg aneg; + u8 mac_addr[ETH_ALEN]; u8 label_port; -- cgit From 9f9e0da57ef1207b26b13c243c5f398c9432156c Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:29 -0700 Subject: nfp: report port type in ethtool Service process firmware provides us with information about media and interface (SFP module) plugged in, translate that to Linux's PORT_* defines and report via ethtool. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 1 + .../ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c | 21 +++++++++++++++++++ .../ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h | 24 ++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 563ced3c99e1..3b2a09821a59 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -215,6 +215,7 @@ nfp_net_get_link_ksettings(struct net_device *netdev, nfp_net_refresh_port_config(nn); /* Separate if - on FW error the port could've disappeared from table */ if (nn->eth_port) { + cmd->base.port = nn->eth_port->port_type; cmd->base.speed = nn->eth_port->speed; cmd->base.duplex = DUPLEX_FULL; return 0; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index dcb1bc81e554..07b4ded01514 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -62,6 +62,8 @@ #define NSP_ETH_STATE_TX_ENABLED BIT_ULL(2) #define NSP_ETH_STATE_RX_ENABLED BIT_ULL(3) #define NSP_ETH_STATE_RATE GENMASK_ULL(11, 8) +#define NSP_ETH_STATE_INTERFACE GENMASK_ULL(19, 12) +#define NSP_ETH_STATE_MEDIA GENMASK_ULL(21, 20) #define NSP_ETH_STATE_OVRD_CHNG BIT_ULL(22) #define NSP_ETH_STATE_ANEG GENMASK_ULL(25, 23) @@ -134,6 +136,9 @@ nfp_eth_port_translate(struct nfp_nsp *nsp, const struct eth_table_entry *src, rate = nfp_eth_rate(FIELD_GET(NSP_ETH_STATE_RATE, state)); dst->speed = dst->lanes * rate; + dst->interface = FIELD_GET(NSP_ETH_STATE_INTERFACE, state); + dst->media = FIELD_GET(NSP_ETH_STATE_MEDIA, state); + nfp_eth_copy_mac_reverse(dst->mac_addr, src->mac_addr); dst->label_port = FIELD_GET(NSP_ETH_PORT_PHYLABEL, port); @@ -170,6 +175,20 @@ nfp_eth_mark_split_ports(struct nfp_cpp *cpp, struct nfp_eth_table *table) } } +static void +nfp_eth_calc_port_type(struct nfp_cpp *cpp, struct nfp_eth_table_port *entry) +{ + if (entry->interface == NFP_INTERFACE_NONE) { + entry->port_type = PORT_NONE; + return; + } + + if (entry->media == NFP_MEDIA_FIBRE) + entry->port_type = PORT_FIBRE; + else + entry->port_type = PORT_DA; +} + /** * nfp_eth_read_ports() - retrieve port information * @cpp: NFP CPP handle @@ -237,6 +256,8 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) &table->ports[j++]); nfp_eth_mark_split_ports(cpp, table); + for (i = 0; i < table->count; i++) + nfp_eth_calc_port_type(cpp, &table->ports[i]); kfree(entries); diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h index 6b3e954e70b3..57eb3cfa6a0a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h @@ -37,6 +37,22 @@ #include #include +enum nfp_eth_interface { + NFP_INTERFACE_NONE = 0, + NFP_INTERFACE_SFP = 1, + NFP_INTERFACE_SFPP = 10, + NFP_INTERFACE_SFP28 = 28, + NFP_INTERFACE_QSFP = 40, + NFP_INTERFACE_CXP = 100, + NFP_INTERFACE_QSFP28 = 112, +}; + +enum nfp_eth_media { + NFP_MEDIA_DAC_PASSIVE = 0, + NFP_MEDIA_DAC_ACTIVE, + NFP_MEDIA_FIBRE, +}; + enum nfp_eth_aneg { NFP_ANEG_AUTO = 0, NFP_ANEG_SEARCH, @@ -56,6 +72,8 @@ enum nfp_eth_aneg { * @base: first channel index (within NBI) * @lanes: number of channels * @speed: interface speed (in Mbps) + * @interface: interface (module) plugged in + * @media: media type of the @interface * @aneg: auto negotiation mode * @mac_addr: interface MAC address * @label_port: port id @@ -65,6 +83,7 @@ enum nfp_eth_aneg { * @rx_enabled: is RX enabled? * @override_changed: is media reconfig pending? * + * @port_type: one of %PORT_* defines for ethtool * @is_split: is interface part of a split port */ struct nfp_eth_table { @@ -77,6 +96,9 @@ struct nfp_eth_table { unsigned int lanes; unsigned int speed; + unsigned int interface; + enum nfp_eth_media media; + enum nfp_eth_aneg aneg; u8 mac_addr[ETH_ALEN]; @@ -91,6 +113,8 @@ struct nfp_eth_table { bool override_changed; /* Computed fields */ + u8 port_type; + bool is_split; } ports[0]; }; -- cgit From ce22f5a2cbe3c62746da7d3f8272abd03e8c975a Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:30 -0700 Subject: nfp: separate high level and low level NSP headers We will soon add more NSP commands and structure definitions. Move all high-level NSP header contents to a common nfp_nsp.h file. Right now it mostly boils down to renaming nfp_nsp_eth.h and moving some functions from nfp.h there. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_main.c | 2 +- .../net/ethernet/netronome/nfp/nfp_net_common.c | 2 +- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 2 +- drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 2 +- drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h | 12 +- .../net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c | 1 + .../net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h | 139 +++++++++++++++++++++ .../ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c | 2 +- .../ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h | 130 ------------------- 9 files changed, 147 insertions(+), 145 deletions(-) create mode 100644 drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h delete mode 100644 drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index 96266796fd09..bea2a1a6c211 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -48,7 +48,7 @@ #include "nfpcore/nfp.h" #include "nfpcore/nfp_cpp.h" #include "nfpcore/nfp_nffw.h" -#include "nfpcore/nfp_nsp_eth.h" +#include "nfpcore/nfp_nsp.h" #include "nfpcore/nfp6000_pcie.h" diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 36028b752503..61e5741f935c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -67,7 +67,7 @@ #include #include -#include "nfpcore/nfp_nsp_eth.h" +#include "nfpcore/nfp_nsp.h" #include "nfp_net_ctrl.h" #include "nfp_net.h" diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 3b2a09821a59..963d6dd97cec 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -49,7 +49,7 @@ #include #include "nfpcore/nfp.h" -#include "nfpcore/nfp_nsp_eth.h" +#include "nfpcore/nfp_nsp.h" #include "nfp_net_ctrl.h" #include "nfp_net.h" diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 8e975c36877c..3e1f97e88710 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -52,7 +52,7 @@ #include "nfpcore/nfp.h" #include "nfpcore/nfp_cpp.h" #include "nfpcore/nfp_nffw.h" -#include "nfpcore/nfp_nsp_eth.h" +#include "nfpcore/nfp_nsp.h" #include "nfpcore/nfp6000_pcie.h" #include "nfp_net_ctrl.h" diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h index f7ca8e374923..778bd9424d5d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h @@ -48,18 +48,10 @@ const char *nfp_hwinfo_lookup(struct nfp_cpp *cpp, const char *lookup); -/* Implemented in nfp_nsp.c */ +/* Implemented in nfp_nsp.c, low level functions */ struct nfp_nsp; -struct firmware; - -struct nfp_nsp *nfp_nsp_open(struct nfp_cpp *cpp); -void nfp_nsp_close(struct nfp_nsp *state); -u16 nfp_nsp_get_abi_ver_major(struct nfp_nsp *state); -u16 nfp_nsp_get_abi_ver_minor(struct nfp_nsp *state); -int nfp_nsp_wait(struct nfp_nsp *state); -int nfp_nsp_device_soft_reset(struct nfp_nsp *state); -int nfp_nsp_load_fw(struct nfp_nsp *state, const struct firmware *fw); + int nfp_nsp_read_eth_table(struct nfp_nsp *state, void *buf, unsigned int size); int nfp_nsp_write_eth_table(struct nfp_nsp *state, const void *buf, unsigned int size); diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 17822ae4a17f..6482831282b2 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -49,6 +49,7 @@ #include "nfp.h" #include "nfp_cpp.h" +#include "nfp_nsp.h" /* Offsets relative to the CSR base */ #define NSP_STATUS 0x00 diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h new file mode 100644 index 000000000000..e3baec3cccc2 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2015-2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef NSP_NSP_H +#define NSP_NSP_H 1 + +#include +#include + +struct firmware; +struct nfp_cpp; +struct nfp_nsp; + +struct nfp_nsp *nfp_nsp_open(struct nfp_cpp *cpp); +void nfp_nsp_close(struct nfp_nsp *state); +u16 nfp_nsp_get_abi_ver_major(struct nfp_nsp *state); +u16 nfp_nsp_get_abi_ver_minor(struct nfp_nsp *state); +int nfp_nsp_wait(struct nfp_nsp *state); +int nfp_nsp_device_soft_reset(struct nfp_nsp *state); +int nfp_nsp_load_fw(struct nfp_nsp *state, const struct firmware *fw); + +enum nfp_eth_interface { + NFP_INTERFACE_NONE = 0, + NFP_INTERFACE_SFP = 1, + NFP_INTERFACE_SFPP = 10, + NFP_INTERFACE_SFP28 = 28, + NFP_INTERFACE_QSFP = 40, + NFP_INTERFACE_CXP = 100, + NFP_INTERFACE_QSFP28 = 112, +}; + +enum nfp_eth_media { + NFP_MEDIA_DAC_PASSIVE = 0, + NFP_MEDIA_DAC_ACTIVE, + NFP_MEDIA_FIBRE, +}; + +enum nfp_eth_aneg { + NFP_ANEG_AUTO = 0, + NFP_ANEG_SEARCH, + NFP_ANEG_25G_CONSORTIUM, + NFP_ANEG_25G_IEEE, + NFP_ANEG_DISABLED, +}; + +/** + * struct nfp_eth_table - ETH table information + * @count: number of table entries + * @ports: table of ports + * + * @eth_index: port index according to legacy ethX numbering + * @index: chip-wide first channel index + * @nbi: NBI index + * @base: first channel index (within NBI) + * @lanes: number of channels + * @speed: interface speed (in Mbps) + * @interface: interface (module) plugged in + * @media: media type of the @interface + * @aneg: auto negotiation mode + * @mac_addr: interface MAC address + * @label_port: port id + * @label_subport: id of interface within port (for split ports) + * @enabled: is enabled? + * @tx_enabled: is TX enabled? + * @rx_enabled: is RX enabled? + * @override_changed: is media reconfig pending? + * + * @port_type: one of %PORT_* defines for ethtool + * @is_split: is interface part of a split port + */ +struct nfp_eth_table { + unsigned int count; + struct nfp_eth_table_port { + unsigned int eth_index; + unsigned int index; + unsigned int nbi; + unsigned int base; + unsigned int lanes; + unsigned int speed; + + unsigned int interface; + enum nfp_eth_media media; + + enum nfp_eth_aneg aneg; + + u8 mac_addr[ETH_ALEN]; + + u8 label_port; + u8 label_subport; + + bool enabled; + bool tx_enabled; + bool rx_enabled; + + bool override_changed; + + /* Computed fields */ + u8 port_type; + + bool is_split; + } ports[0]; +}; + +struct nfp_eth_table *nfp_eth_read_ports(struct nfp_cpp *cpp); +struct nfp_eth_table * +__nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp); +int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable); + +#endif diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 07b4ded01514..837de15ed720 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -43,7 +43,7 @@ #include #include "nfp.h" -#include "nfp_nsp_eth.h" +#include "nfp_nsp.h" #include "nfp6000/nfp6000.h" #define NSP_ETH_NBI_PORT_COUNT 24 diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h deleted file mode 100644 index 57eb3cfa6a0a..000000000000 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2015-2017 Netronome Systems, Inc. - * - * This software is dual licensed under the GNU General License Version 2, - * June 1991 as shown in the file COPYING in the top-level directory of this - * source tree or the BSD 2-Clause License provided below. You have the - * option to license this software under the complete terms of either license. - * - * The BSD 2-Clause License: - * - * 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. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef NSP_NSP_ETH_H -#define NSP_NSP_ETH_H 1 - -#include -#include - -enum nfp_eth_interface { - NFP_INTERFACE_NONE = 0, - NFP_INTERFACE_SFP = 1, - NFP_INTERFACE_SFPP = 10, - NFP_INTERFACE_SFP28 = 28, - NFP_INTERFACE_QSFP = 40, - NFP_INTERFACE_CXP = 100, - NFP_INTERFACE_QSFP28 = 112, -}; - -enum nfp_eth_media { - NFP_MEDIA_DAC_PASSIVE = 0, - NFP_MEDIA_DAC_ACTIVE, - NFP_MEDIA_FIBRE, -}; - -enum nfp_eth_aneg { - NFP_ANEG_AUTO = 0, - NFP_ANEG_SEARCH, - NFP_ANEG_25G_CONSORTIUM, - NFP_ANEG_25G_IEEE, - NFP_ANEG_DISABLED, -}; - -/** - * struct nfp_eth_table - ETH table information - * @count: number of table entries - * @ports: table of ports - * - * @eth_index: port index according to legacy ethX numbering - * @index: chip-wide first channel index - * @nbi: NBI index - * @base: first channel index (within NBI) - * @lanes: number of channels - * @speed: interface speed (in Mbps) - * @interface: interface (module) plugged in - * @media: media type of the @interface - * @aneg: auto negotiation mode - * @mac_addr: interface MAC address - * @label_port: port id - * @label_subport: id of interface within port (for split ports) - * @enabled: is enabled? - * @tx_enabled: is TX enabled? - * @rx_enabled: is RX enabled? - * @override_changed: is media reconfig pending? - * - * @port_type: one of %PORT_* defines for ethtool - * @is_split: is interface part of a split port - */ -struct nfp_eth_table { - unsigned int count; - struct nfp_eth_table_port { - unsigned int eth_index; - unsigned int index; - unsigned int nbi; - unsigned int base; - unsigned int lanes; - unsigned int speed; - - unsigned int interface; - enum nfp_eth_media media; - - enum nfp_eth_aneg aneg; - - u8 mac_addr[ETH_ALEN]; - - u8 label_port; - u8 label_subport; - - bool enabled; - bool tx_enabled; - bool rx_enabled; - - bool override_changed; - - /* Computed fields */ - u8 port_type; - - bool is_split; - } ports[0]; -}; - -struct nfp_cpp; -struct nfp_nsp; - -struct nfp_eth_table *nfp_eth_read_ports(struct nfp_cpp *cpp); -struct nfp_eth_table * -__nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp); -int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable); - -#endif -- cgit From 30a029217de994db889b7e598b54b674b2ad86b4 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:31 -0700 Subject: nfp: allow multi-stage NSP configuration NSP commands may be slow to respond, we should try to avoid doing a command-per-item when user requested to change multiple parameters for instance with an ethtool .set_settings() command. Introduce a way of internal NSP code to carry state in NSP structure and add start/finish calls to perform the initialization and kick off of the configuration request, with potentially many parameters being modified in between. nfp_eth_set_mod_enable() will make use of the new code internally, other "set" functions to follow. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h | 8 ++ .../net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c | 43 ++++++++ .../net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h | 4 + .../ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c | 114 +++++++++++++++------ 4 files changed, 138 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h index 778bd9424d5d..8afef7593f13 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h @@ -52,6 +52,14 @@ const char *nfp_hwinfo_lookup(struct nfp_cpp *cpp, const char *lookup); struct nfp_nsp; +struct nfp_cpp *nfp_nsp_cpp(struct nfp_nsp *state); +bool nfp_nsp_config_modified(struct nfp_nsp *state); +void nfp_nsp_config_set_modified(struct nfp_nsp *state, bool modified); +void *nfp_nsp_config_entries(struct nfp_nsp *state); +unsigned int nfp_nsp_config_idx(struct nfp_nsp *state); +void nfp_nsp_config_set_state(struct nfp_nsp *state, void *entries, + unsigned int idx); +void nfp_nsp_config_clear_state(struct nfp_nsp *state); int nfp_nsp_read_eth_table(struct nfp_nsp *state, void *buf, unsigned int size); int nfp_nsp_write_eth_table(struct nfp_nsp *state, const void *buf, unsigned int size); diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 6482831282b2..225d07815375 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -104,8 +104,51 @@ struct nfp_nsp { u16 major; u16 minor; } ver; + + /* Eth table config state */ + bool modified; + unsigned int idx; + void *entries; }; +struct nfp_cpp *nfp_nsp_cpp(struct nfp_nsp *state) +{ + return state->cpp; +} + +bool nfp_nsp_config_modified(struct nfp_nsp *state) +{ + return state->modified; +} + +void nfp_nsp_config_set_modified(struct nfp_nsp *state, bool modified) +{ + state->modified = modified; +} + +void *nfp_nsp_config_entries(struct nfp_nsp *state) +{ + return state->entries; +} + +unsigned int nfp_nsp_config_idx(struct nfp_nsp *state) +{ + return state->idx; +} + +void +nfp_nsp_config_set_state(struct nfp_nsp *state, void *entries, unsigned int idx) +{ + state->entries = entries; + state->idx = idx; +} + +void nfp_nsp_config_clear_state(struct nfp_nsp *state) +{ + state->entries = NULL; + state->idx = 0; +} + static int nfp_nsp_check(struct nfp_nsp *state) { struct nfp_cpp *cpp = state->cpp; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h index e3baec3cccc2..c452ad311993 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h @@ -136,4 +136,8 @@ struct nfp_eth_table * __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp); int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable); +struct nfp_nsp *nfp_eth_config_start(struct nfp_cpp *cpp, unsigned int idx); +int nfp_eth_config_commit_end(struct nfp_nsp *nsp); +void nfp_eth_config_cleanup_end(struct nfp_nsp *nsp); + #endif diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 837de15ed720..55d8e073ccbd 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -268,63 +268,115 @@ err: return NULL; } -/** - * nfp_eth_set_mod_enable() - set PHY module enable control bit - * @cpp: NFP CPP handle - * @idx: NFP chip-wide port index - * @enable: Desired state - * - * Enable or disable PHY module (this usually means setting the TX lanes - * disable bits). - * - * Return: 0 or -ERRNO. - */ -int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable) +struct nfp_nsp *nfp_eth_config_start(struct nfp_cpp *cpp, unsigned int idx) { struct eth_table_entry *entries; struct nfp_nsp *nsp; - u64 reg; int ret; entries = kzalloc(NSP_ETH_TABLE_SIZE, GFP_KERNEL); if (!entries) - return -ENOMEM; + return ERR_PTR(-ENOMEM); nsp = nfp_nsp_open(cpp); if (IS_ERR(nsp)) { kfree(entries); - return PTR_ERR(nsp); + return nsp; } ret = nfp_nsp_read_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); if (ret < 0) { nfp_err(cpp, "reading port table failed %d\n", ret); - goto exit_close_nsp; + goto err; } if (!(entries[idx].port & NSP_ETH_PORT_LANES_MASK)) { nfp_warn(cpp, "trying to set port state on disabled port %d\n", idx); - ret = -EINVAL; - goto exit_close_nsp; + goto err; } - /* Check if we are already in requested state */ - reg = le64_to_cpu(entries[idx].state); - if (enable == FIELD_GET(NSP_ETH_CTRL_ENABLED, reg)) { - ret = 0; - goto exit_close_nsp; - } + nfp_nsp_config_set_state(nsp, entries, idx); + return nsp; + +err: + nfp_nsp_close(nsp); + kfree(entries); + return ERR_PTR(-EIO); +} - reg = le64_to_cpu(entries[idx].control); - reg &= ~NSP_ETH_CTRL_ENABLED; - reg |= FIELD_PREP(NSP_ETH_CTRL_ENABLED, enable); - entries[idx].control = cpu_to_le64(reg); +void nfp_eth_config_cleanup_end(struct nfp_nsp *nsp) +{ + struct eth_table_entry *entries = nfp_nsp_config_entries(nsp); - ret = nfp_nsp_write_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); -exit_close_nsp: + nfp_nsp_config_set_modified(nsp, false); + nfp_nsp_config_clear_state(nsp); nfp_nsp_close(nsp); kfree(entries); +} + +/** + * nfp_eth_config_commit_end() - perform recorded configuration changes + * @nsp: NFP NSP handle returned from nfp_eth_config_start() + * + * Perform the configuration which was requested with __nfp_eth_set_*() + * helpers and recorded in @nsp state. If device was already configured + * as requested or no __nfp_eth_set_*() operations were made no NSP command + * will be performed. + * + * Return: + * 0 - configuration successful; + * 1 - no changes were needed; + * -ERRNO - configuration failed. + */ +int nfp_eth_config_commit_end(struct nfp_nsp *nsp) +{ + struct eth_table_entry *entries = nfp_nsp_config_entries(nsp); + int ret = 1; + + if (nfp_nsp_config_modified(nsp)) { + ret = nfp_nsp_write_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); + ret = ret < 0 ? ret : 0; + } + + nfp_eth_config_cleanup_end(nsp); + + return ret; +} + +/** + * nfp_eth_set_mod_enable() - set PHY module enable control bit + * @cpp: NFP CPP handle + * @idx: NFP chip-wide port index + * @enable: Desired state + * + * Enable or disable PHY module (this usually means setting the TX lanes + * disable bits). + * + * Return: 0 or -ERRNO. + */ +int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable) +{ + struct eth_table_entry *entries; + struct nfp_nsp *nsp; + u64 reg; + + nsp = nfp_eth_config_start(cpp, idx); + if (IS_ERR(nsp)) + return PTR_ERR(nsp); + + entries = nfp_nsp_config_entries(nsp); + + /* Check if we are already in requested state */ + reg = le64_to_cpu(entries[idx].state); + if (enable != FIELD_GET(NSP_ETH_CTRL_ENABLED, reg)) { + reg = le64_to_cpu(entries[idx].control); + reg &= ~NSP_ETH_CTRL_ENABLED; + reg |= FIELD_PREP(NSP_ETH_CTRL_ENABLED, enable); + entries[idx].control = cpu_to_le64(reg); + + nfp_nsp_config_set_modified(nsp, true); + } - return ret < 0 ? ret : 0; + return nfp_eth_config_commit_end(nsp); } -- cgit From e890ae8e497a535a8ab692a66f7fc2aee4361728 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:32 -0700 Subject: nfp: turn NSP port entry into a union Make NSP port structure a union to simplify accessing the fields from generic macros. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c | 38 ++++++++++++++-------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 55d8e073ccbd..ca5c041e64a4 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -49,7 +49,7 @@ #define NSP_ETH_NBI_PORT_COUNT 24 #define NSP_ETH_MAX_COUNT (2 * NSP_ETH_NBI_PORT_COUNT) #define NSP_ETH_TABLE_SIZE (NSP_ETH_MAX_COUNT * \ - sizeof(struct eth_table_entry)) + sizeof(union eth_table_entry)) #define NSP_ETH_PORT_LANES GENMASK_ULL(3, 0) #define NSP_ETH_PORT_INDEX GENMASK_ULL(15, 8) @@ -71,6 +71,15 @@ #define NSP_ETH_CTRL_TX_ENABLED BIT_ULL(2) #define NSP_ETH_CTRL_RX_ENABLED BIT_ULL(3) +enum nfp_eth_raw { + NSP_ETH_RAW_PORT = 0, + NSP_ETH_RAW_STATE, + NSP_ETH_RAW_MAC, + NSP_ETH_RAW_CONTROL, + + NSP_ETH_NUM_RAW +}; + enum nfp_eth_rate { RATE_INVALID = 0, RATE_10M, @@ -80,12 +89,15 @@ enum nfp_eth_rate { RATE_25G, }; -struct eth_table_entry { - __le64 port; - __le64 state; - u8 mac_addr[6]; - u8 resv[2]; - __le64 control; +union eth_table_entry { + struct { + __le64 port; + __le64 state; + u8 mac_addr[6]; + u8 resv[2]; + __le64 control; + }; + __le64 raw[NSP_ETH_NUM_RAW]; }; static unsigned int nfp_eth_rate(enum nfp_eth_rate rate) @@ -114,7 +126,7 @@ static void nfp_eth_copy_mac_reverse(u8 *dst, const u8 *src) } static void -nfp_eth_port_translate(struct nfp_nsp *nsp, const struct eth_table_entry *src, +nfp_eth_port_translate(struct nfp_nsp *nsp, const union eth_table_entry *src, unsigned int index, struct nfp_eth_table_port *dst) { unsigned int rate; @@ -216,7 +228,7 @@ struct nfp_eth_table *nfp_eth_read_ports(struct nfp_cpp *cpp) struct nfp_eth_table * __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) { - struct eth_table_entry *entries; + union eth_table_entry *entries; struct nfp_eth_table *table; int i, j, ret, cnt = 0; @@ -270,7 +282,7 @@ err: struct nfp_nsp *nfp_eth_config_start(struct nfp_cpp *cpp, unsigned int idx) { - struct eth_table_entry *entries; + union eth_table_entry *entries; struct nfp_nsp *nsp; int ret; @@ -307,7 +319,7 @@ err: void nfp_eth_config_cleanup_end(struct nfp_nsp *nsp) { - struct eth_table_entry *entries = nfp_nsp_config_entries(nsp); + union eth_table_entry *entries = nfp_nsp_config_entries(nsp); nfp_nsp_config_set_modified(nsp, false); nfp_nsp_config_clear_state(nsp); @@ -331,7 +343,7 @@ void nfp_eth_config_cleanup_end(struct nfp_nsp *nsp) */ int nfp_eth_config_commit_end(struct nfp_nsp *nsp) { - struct eth_table_entry *entries = nfp_nsp_config_entries(nsp); + union eth_table_entry *entries = nfp_nsp_config_entries(nsp); int ret = 1; if (nfp_nsp_config_modified(nsp)) { @@ -357,7 +369,7 @@ int nfp_eth_config_commit_end(struct nfp_nsp *nsp) */ int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable) { - struct eth_table_entry *entries; + union eth_table_entry *entries; struct nfp_nsp *nsp; u64 reg; -- cgit From 85eb97dd2f073e72784505e51b048171ab9bda61 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:33 -0700 Subject: nfp: add extended error messages Allow NSP to set option code even when error is reported. This provides a way for NSP to give user more precise information about why command failed. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c | 37 +++++++++++++++++----- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 225d07815375..96bb5f6bd87b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -97,6 +97,13 @@ enum nfp_nsp_cmd { __MAX_SPCODE, }; +static const struct { + int code; + const char *msg; +} nsp_errors[] = { + { 0, "success" } /* placeholder to avoid warnings */ +}; + struct nfp_nsp { struct nfp_cpp *cpp; struct nfp_resource *res; @@ -149,6 +156,18 @@ void nfp_nsp_config_clear_state(struct nfp_nsp *state) state->idx = 0; } +static void nfp_nsp_print_extended_error(struct nfp_nsp *state, u32 ret_val) +{ + int i; + + if (!ret_val) + return; + + for (i = 0; i < ARRAY_SIZE(nsp_errors); i++) + if (ret_val == nsp_errors[i].code) + nfp_err(state->cpp, "err msg: %s\n", nsp_errors[i].msg); +} + static int nfp_nsp_check(struct nfp_nsp *state) { struct nfp_cpp *cpp = state->cpp; @@ -282,7 +301,7 @@ nfp_nsp_wait_reg(struct nfp_cpp *cpp, u64 *reg, static int nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option, u32 buff_cpp, u64 buff_addr) { - u64 reg, nsp_base, nsp_buffer, nsp_status, nsp_command; + u64 reg, ret_val, nsp_base, nsp_buffer, nsp_status, nsp_command; struct nfp_cpp *cpp = state->cpp; u32 nsp_cpp; int err; @@ -335,18 +354,20 @@ static int nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option, return err; } + err = nfp_cpp_readq(cpp, nsp_cpp, nsp_command, &ret_val); + if (err < 0) + return err; + ret_val = FIELD_GET(NSP_COMMAND_OPTION, ret_val); + err = FIELD_GET(NSP_STATUS_RESULT, reg); if (err) { - nfp_warn(cpp, "Result (error) code set: %d command: %d\n", - -err, code); + nfp_warn(cpp, "Result (error) code set: %d (%d) command: %d\n", + -err, (int)ret_val, code); + nfp_nsp_print_extended_error(state, ret_val); return -err; } - err = nfp_cpp_readq(cpp, nsp_cpp, nsp_command, ®); - if (err < 0) - return err; - - return FIELD_GET(NSP_COMMAND_OPTION, reg); + return ret_val; } static int nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option, -- cgit From 5a560832ebf8317eb2af0493b259aba37af05bb1 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:34 -0700 Subject: nfp: NSP backend for link configuration operations Add NSP backend for upcoming link configuration operations. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c | 6 +- .../net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h | 7 + .../ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c | 180 +++++++++++++++++++-- 3 files changed, 179 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 96bb5f6bd87b..4635f42e15b0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -101,7 +101,11 @@ static const struct { int code; const char *msg; } nsp_errors[] = { - { 0, "success" } /* placeholder to avoid warnings */ + { 6010, "could not map to phy for port" }, + { 6011, "not an allowed rate/lanes for port" }, + { 6012, "not an allowed rate/lanes for port" }, + { 6013, "high/low error, change other port first" }, + { 6014, "config not found in flash" }, }; struct nfp_nsp { diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h index c452ad311993..7d34ff145fd7 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h @@ -134,10 +134,17 @@ struct nfp_eth_table { struct nfp_eth_table *nfp_eth_read_ports(struct nfp_cpp *cpp); struct nfp_eth_table * __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp); + int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable); +int nfp_eth_set_configured(struct nfp_cpp *cpp, unsigned int idx, + bool configed); struct nfp_nsp *nfp_eth_config_start(struct nfp_cpp *cpp, unsigned int idx); int nfp_eth_config_commit_end(struct nfp_nsp *nsp); void nfp_eth_config_cleanup_end(struct nfp_nsp *nsp); +int __nfp_eth_set_aneg(struct nfp_nsp *nsp, enum nfp_eth_aneg mode); +int __nfp_eth_set_speed(struct nfp_nsp *nsp, unsigned int speed); +int __nfp_eth_set_split(struct nfp_nsp *nsp, unsigned int lanes); + #endif diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index ca5c041e64a4..639438d8313a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -58,6 +58,7 @@ #define NSP_ETH_PORT_LANES_MASK cpu_to_le64(NSP_ETH_PORT_LANES) +#define NSP_ETH_STATE_CONFIGURED BIT_ULL(0) #define NSP_ETH_STATE_ENABLED BIT_ULL(1) #define NSP_ETH_STATE_TX_ENABLED BIT_ULL(2) #define NSP_ETH_STATE_RX_ENABLED BIT_ULL(3) @@ -67,9 +68,13 @@ #define NSP_ETH_STATE_OVRD_CHNG BIT_ULL(22) #define NSP_ETH_STATE_ANEG GENMASK_ULL(25, 23) +#define NSP_ETH_CTRL_CONFIGURED BIT_ULL(0) #define NSP_ETH_CTRL_ENABLED BIT_ULL(1) #define NSP_ETH_CTRL_TX_ENABLED BIT_ULL(2) #define NSP_ETH_CTRL_RX_ENABLED BIT_ULL(3) +#define NSP_ETH_CTRL_SET_RATE BIT_ULL(4) +#define NSP_ETH_CTRL_SET_LANES BIT_ULL(5) +#define NSP_ETH_CTRL_SET_ANEG BIT_ULL(6) enum nfp_eth_raw { NSP_ETH_RAW_PORT = 0, @@ -100,21 +105,38 @@ union eth_table_entry { __le64 raw[NSP_ETH_NUM_RAW]; }; -static unsigned int nfp_eth_rate(enum nfp_eth_rate rate) +static const struct { + enum nfp_eth_rate rate; + unsigned int speed; +} nsp_eth_rate_tbl[] = { + { RATE_INVALID, 0, }, + { RATE_10M, SPEED_10, }, + { RATE_100M, SPEED_100, }, + { RATE_1G, SPEED_1000, }, + { RATE_10G, SPEED_10000, }, + { RATE_25G, SPEED_25000, }, +}; + +static unsigned int nfp_eth_rate2speed(enum nfp_eth_rate rate) { - unsigned int rate_xlate[] = { - [RATE_INVALID] = 0, - [RATE_10M] = SPEED_10, - [RATE_100M] = SPEED_100, - [RATE_1G] = SPEED_1000, - [RATE_10G] = SPEED_10000, - [RATE_25G] = SPEED_25000, - }; + int i; - if (rate >= ARRAY_SIZE(rate_xlate)) - return 0; + for (i = 0; i < ARRAY_SIZE(nsp_eth_rate_tbl); i++) + if (nsp_eth_rate_tbl[i].rate == rate) + return nsp_eth_rate_tbl[i].speed; + + return 0; +} + +static unsigned int nfp_eth_speed2rate(unsigned int speed) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(nsp_eth_rate_tbl); i++) + if (nsp_eth_rate_tbl[i].speed == speed) + return nsp_eth_rate_tbl[i].rate; - return rate_xlate[rate]; + return RATE_INVALID; } static void nfp_eth_copy_mac_reverse(u8 *dst, const u8 *src) @@ -145,7 +167,7 @@ nfp_eth_port_translate(struct nfp_nsp *nsp, const union eth_table_entry *src, dst->tx_enabled = FIELD_GET(NSP_ETH_STATE_TX_ENABLED, state); dst->rx_enabled = FIELD_GET(NSP_ETH_STATE_RX_ENABLED, state); - rate = nfp_eth_rate(FIELD_GET(NSP_ETH_STATE_RATE, state)); + rate = nfp_eth_rate2speed(FIELD_GET(NSP_ETH_STATE_RATE, state)); dst->speed = dst->lanes * rate; dst->interface = FIELD_GET(NSP_ETH_STATE_INTERFACE, state); @@ -392,3 +414,135 @@ int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable) return nfp_eth_config_commit_end(nsp); } + +/** + * nfp_eth_set_configured() - set PHY module configured control bit + * @cpp: NFP CPP handle + * @idx: NFP chip-wide port index + * @configed: Desired state + * + * Set the ifup/ifdown state on the PHY. + * + * Return: 0 or -ERRNO. + */ +int nfp_eth_set_configured(struct nfp_cpp *cpp, unsigned int idx, bool configed) +{ + union eth_table_entry *entries; + struct nfp_nsp *nsp; + u64 reg; + + nsp = nfp_eth_config_start(cpp, idx); + if (IS_ERR(nsp)) + return PTR_ERR(nsp); + + entries = nfp_nsp_config_entries(nsp); + + /* Check if we are already in requested state */ + reg = le64_to_cpu(entries[idx].state); + if (configed != FIELD_GET(NSP_ETH_STATE_CONFIGURED, reg)) { + reg = le64_to_cpu(entries[idx].control); + reg &= ~NSP_ETH_CTRL_CONFIGURED; + reg |= FIELD_PREP(NSP_ETH_CTRL_CONFIGURED, configed); + entries[idx].control = cpu_to_le64(reg); + + nfp_nsp_config_set_modified(nsp, true); + } + + return nfp_eth_config_commit_end(nsp); +} + +/* Force inline, FIELD_* macroes require masks to be compilation-time known */ +static __always_inline int +nfp_eth_set_bit_config(struct nfp_nsp *nsp, unsigned int raw_idx, + const u64 mask, unsigned int val, const u64 ctrl_bit) +{ + union eth_table_entry *entries = nfp_nsp_config_entries(nsp); + unsigned int idx = nfp_nsp_config_idx(nsp); + u64 reg; + + /* Note: set features were added in ABI 0.14 but the error + * codes were initially not populated correctly. + */ + if (nfp_nsp_get_abi_ver_minor(nsp) < 17) { + nfp_err(nfp_nsp_cpp(nsp), + "set operations not supported, please update flash\n"); + return -EOPNOTSUPP; + } + + /* Check if we are already in requested state */ + reg = le64_to_cpu(entries[idx].raw[raw_idx]); + if (val == FIELD_GET(mask, reg)) + return 0; + + reg &= ~mask; + reg |= FIELD_PREP(mask, val); + entries[idx].raw[raw_idx] = cpu_to_le64(reg); + + entries[idx].control |= cpu_to_le64(ctrl_bit); + + nfp_nsp_config_set_modified(nsp, true); + + return 0; +} + +/** + * __nfp_eth_set_aneg() - set PHY autonegotiation control bit + * @nsp: NFP NSP handle returned from nfp_eth_config_start() + * @mode: Desired autonegotiation mode + * + * Allow/disallow PHY module to advertise/perform autonegotiation. + * Will write to hwinfo overrides in the flash (persistent config). + * + * Return: 0 or -ERRNO. + */ +int __nfp_eth_set_aneg(struct nfp_nsp *nsp, enum nfp_eth_aneg mode) +{ + return nfp_eth_set_bit_config(nsp, NSP_ETH_RAW_STATE, + NSP_ETH_STATE_ANEG, mode, + NSP_ETH_CTRL_SET_ANEG); +} + +/** + * __nfp_eth_set_speed() - set interface speed/rate + * @nsp: NFP NSP handle returned from nfp_eth_config_start() + * @speed: Desired speed (per lane) + * + * Set lane speed. Provided @speed value should be subport speed divided + * by number of lanes this subport is spanning (i.e. 10000 for 40G, 25000 for + * 50G, etc.) + * Will write to hwinfo overrides in the flash (persistent config). + * + * Return: 0 or -ERRNO. + */ +int __nfp_eth_set_speed(struct nfp_nsp *nsp, unsigned int speed) +{ + enum nfp_eth_rate rate; + + rate = nfp_eth_speed2rate(speed); + if (rate == RATE_INVALID) { + nfp_warn(nfp_nsp_cpp(nsp), + "could not find matching lane rate for speed %u\n", + speed); + return -EINVAL; + } + + return nfp_eth_set_bit_config(nsp, NSP_ETH_RAW_STATE, + NSP_ETH_STATE_RATE, rate, + NSP_ETH_CTRL_SET_RATE); +} + +/** + * __nfp_eth_set_split() - set interface lane split + * @nsp: NFP NSP handle returned from nfp_eth_config_start() + * @lanes: Desired lanes per port + * + * Set number of lanes in the port. + * Will write to hwinfo overrides in the flash (persistent config). + * + * Return: 0 or -ERRNO. + */ +int __nfp_eth_set_split(struct nfp_nsp *nsp, unsigned int lanes) +{ + return nfp_eth_set_bit_config(nsp, NSP_ETH_RAW_PORT, NSP_ETH_PORT_LANES, + lanes, NSP_ETH_CTRL_SET_LANES); +} -- cgit From 7c698737270fee01963f26ea5d168a6a6c4b1269 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:35 -0700 Subject: nfp: add support for .set_link_ksettings() Support setting link speed and autonegotiation through set_link_ksettings() ethtool op. If the port is reconfigured in incompatible way and reboot is required the netdev will get unregistered and not come back until user reboots the system. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 963d6dd97cec..3328041ec290 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -237,6 +237,51 @@ nfp_net_get_link_ksettings(struct net_device *netdev, return 0; } +static int +nfp_net_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) +{ + struct nfp_net *nn = netdev_priv(netdev); + struct nfp_nsp *nsp; + int err; + + if (!nn->eth_port) + return -EOPNOTSUPP; + + if (netif_running(netdev)) { + nn_warn(nn, "Changing settings not allowed on an active interface. It may cause the port to be disabled until reboot.\n"); + return -EBUSY; + } + + nsp = nfp_eth_config_start(nn->cpp, nn->eth_port->index); + if (IS_ERR(nsp)) + return PTR_ERR(nsp); + + err = __nfp_eth_set_aneg(nsp, cmd->base.autoneg == AUTONEG_ENABLE ? + NFP_ANEG_AUTO : NFP_ANEG_DISABLED); + if (err) + goto err_bad_set; + if (cmd->base.speed != SPEED_UNKNOWN) { + u32 speed = cmd->base.speed / nn->eth_port->lanes; + + err = __nfp_eth_set_speed(nsp, speed); + if (err) + goto err_bad_set; + } + + err = nfp_eth_config_commit_end(nsp); + if (err > 0) + return 0; /* no change */ + + nfp_net_refresh_port_config(nn); + + return err; + +err_bad_set: + nfp_eth_config_cleanup_end(nsp); + return err; +} + static void nfp_net_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) { @@ -879,6 +924,7 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { .get_channels = nfp_net_get_channels, .set_channels = nfp_net_set_channels, .get_link_ksettings = nfp_net_get_link_ksettings, + .set_link_ksettings = nfp_net_set_link_ksettings, }; void nfp_net_set_ethtool_ops(struct net_device *netdev) -- cgit From 148cbab6cffc8247d7dfd0f2da86c2eb8c55709c Mon Sep 17 00:00:00 2001 From: Edward Cree Date: Tue, 4 Apr 2017 17:02:49 +0100 Subject: sfc: don't insert mc_list on low-latency firmware if it's too long If the mc_list is longer than 256 addresses, we enter mc_promisc mode. If we're in mc_promisc mode and the firmware doesn't support cascaded multicast, normally we also insert our mc_list, to prevent stealing by another VI. However, if the mc_list was too long, this isn't really helpful - the MC groups that didn't fit in the list can still get stolen, and having only some of them stealable will probably cause more confusing behaviour than having them all stealable. Since inserting 256 multicast filters takes a long time and can lead to MCDI state machine timeouts, just skip the mc_list insert in this overflow condition. Signed-off-by: Edward Cree Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/ef10.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index c60c2d4c646a..78efb2822b86 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -119,6 +119,7 @@ struct efx_ef10_filter_table { bool mc_promisc; /* Whether in multicast promiscuous mode when last changed */ bool mc_promisc_last; + bool mc_overflow; /* Too many MC addrs; should always imply mc_promisc */ bool vlan_filter; struct list_head vlan_list; }; @@ -5058,6 +5059,7 @@ static void efx_ef10_filter_mc_addr_list(struct efx_nic *efx) struct netdev_hw_addr *mc; unsigned int i, addr_count; + table->mc_overflow = false; table->mc_promisc = !!(net_dev->flags & (IFF_PROMISC | IFF_ALLMULTI)); addr_count = netdev_mc_count(net_dev); @@ -5065,6 +5067,7 @@ static void efx_ef10_filter_mc_addr_list(struct efx_nic *efx) netdev_for_each_mc_addr(mc, net_dev) { if (i >= EFX_EF10_FILTER_DEV_MC_MAX) { table->mc_promisc = true; + table->mc_overflow = true; break; } ether_addr_copy(table->dev_mc_list[i].addr, mc->addr); @@ -5469,12 +5472,15 @@ static void efx_ef10_filter_vlan_sync_rx_mode(struct efx_nic *efx, } } else { /* If we failed to insert promiscuous filters, don't - * rollback. Regardless, also insert the mc_list + * rollback. Regardless, also insert the mc_list, + * unless it's incomplete due to overflow */ efx_ef10_filter_insert_def(efx, vlan, EFX_ENCAP_TYPE_NONE, true, false); - efx_ef10_filter_insert_addr_list(efx, vlan, true, false); + if (!table->mc_overflow) + efx_ef10_filter_insert_addr_list(efx, vlan, + true, false); } } else { /* If any filters failed to insert, rollback and fall back to -- cgit From faeeb317a5615076dff1ff44b51e862e6064dbd0 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Tue, 4 Apr 2017 17:32:42 -0400 Subject: bonding: attempt to better support longer hw addresses People are using bonding over Infiniband IPoIB connections, and who knows what else. Infiniband has a hardware address length of 20 octets (INFINIBAND_ALEN), and the network core defines a MAX_ADDR_LEN of 32. Various places in the bonding code are currently hard-wired to 6 octets (ETH_ALEN), such as the 3ad code, which I've left untouched here. Besides, only alb is currently possible on Infiniband links right now anyway, due to commit 1533e7731522, so the alb code is where most of the changes are. One major component of this change is the addition of a bond_hw_addr_copy function that takes a length argument, instead of using ether_addr_copy everywhere that hardware addresses need to be copied about. The other major component of this change is converting the bonding code from using struct sockaddr for address storage to struct sockaddr_storage, as the former has an address storage space of only 14, while the latter is 128 minus a few, which is necessary to support bonding over device with up to MAX_ADDR_LEN octet hardware addresses. Additionally, this probably fixes up some memory corruption issues with the current code, where it's possible to write an infiniband hardware address into a sockaddr declared on the stack. Lightly tested on a dual mlx4 IPoIB setup, which properly shows a 20-octet hardware address now: $ cat /proc/net/bonding/bond0 Ethernet Channel Bonding Driver: v3.7.1 (April 27, 2011) Bonding Mode: fault-tolerance (active-backup) (fail_over_mac active) Primary Slave: mlx4_ib0 (primary_reselect always) Currently Active Slave: mlx4_ib0 MII Status: up MII Polling Interval (ms): 100 Up Delay (ms): 100 Down Delay (ms): 100 Slave Interface: mlx4_ib0 MII Status: up Speed: Unknown Duplex: Unknown Link Failure Count: 0 Permanent HW addr: 80:00:02:08:fe:80:00:00:00:00:00:00:e4:1d:2d:03:00:1d:67:01 Slave queue ID: 0 Slave Interface: mlx4_ib1 MII Status: up Speed: Unknown Duplex: Unknown Link Failure Count: 0 Permanent HW addr: 80:00:02:09:fe:80:00:00:00:00:00:01:e4:1d:2d:03:00:1d:67:02 Slave queue ID: 0 Also tested with a standard 1Gbps NIC bonding setup (with a mix of e1000 and e1000e cards), running LNST's bonding tests. CC: Jay Vosburgh CC: Veaceslav Falico CC: Andy Gospodarek CC: netdev@vger.kernel.org Signed-off-by: Jarod Wilson Signed-off-by: David S. Miller --- drivers/net/bonding/bond_alb.c | 88 +++++++++++++++++++++++---------------- drivers/net/bonding/bond_main.c | 73 ++++++++++++++++++-------------- drivers/net/bonding/bond_procfs.c | 3 +- include/net/bonding.h | 12 +++++- 4 files changed, 108 insertions(+), 68 deletions(-) diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index c80b023092dd..7d7a3cec149a 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -687,7 +687,8 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) /* the arp must be sent on the selected rx channel */ tx_slave = rlb_choose_channel(skb, bond); if (tx_slave) - ether_addr_copy(arp->mac_src, tx_slave->dev->dev_addr); + bond_hw_addr_copy(arp->mac_src, tx_slave->dev->dev_addr, + tx_slave->dev->addr_len); netdev_dbg(bond->dev, "Server sent ARP Reply packet\n"); } else if (arp->op_code == htons(ARPOP_REQUEST)) { /* Create an entry in the rx_hashtbl for this client as a @@ -1017,22 +1018,23 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[], rcu_read_unlock(); } -static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[]) +static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[], + unsigned int len) { struct net_device *dev = slave->dev; - struct sockaddr s_addr; + struct sockaddr_storage ss; if (BOND_MODE(slave->bond) == BOND_MODE_TLB) { - memcpy(dev->dev_addr, addr, dev->addr_len); + memcpy(dev->dev_addr, addr, len); return 0; } /* for rlb each slave must have a unique hw mac addresses so that * each slave will receive packets destined to a different mac */ - memcpy(s_addr.sa_data, addr, dev->addr_len); - s_addr.sa_family = dev->type; - if (dev_set_mac_address(dev, &s_addr)) { + memcpy(ss.__data, addr, len); + ss.ss_family = dev->type; + if (dev_set_mac_address(dev, (struct sockaddr *)&ss)) { netdev_err(slave->bond->dev, "dev_set_mac_address of dev %s failed! ALB mode requires that the base driver support setting the hw address also when the network device's interface is open\n", dev->name); return -EOPNOTSUPP; @@ -1046,11 +1048,14 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[]) */ static void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2) { - u8 tmp_mac_addr[ETH_ALEN]; + u8 tmp_mac_addr[MAX_ADDR_LEN]; - ether_addr_copy(tmp_mac_addr, slave1->dev->dev_addr); - alb_set_slave_mac_addr(slave1, slave2->dev->dev_addr); - alb_set_slave_mac_addr(slave2, tmp_mac_addr); + bond_hw_addr_copy(tmp_mac_addr, slave1->dev->dev_addr, + slave1->dev->addr_len); + alb_set_slave_mac_addr(slave1, slave2->dev->dev_addr, + slave2->dev->addr_len); + alb_set_slave_mac_addr(slave2, tmp_mac_addr, + slave1->dev->addr_len); } @@ -1177,7 +1182,8 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav /* Try setting slave mac to bond address and fall-through * to code handling that situation below... */ - alb_set_slave_mac_addr(slave, bond->dev->dev_addr); + alb_set_slave_mac_addr(slave, bond->dev->dev_addr, + bond->dev->addr_len); } /* The slave's address is equal to the address of the bond. @@ -1202,7 +1208,8 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav } if (free_mac_slave) { - alb_set_slave_mac_addr(slave, free_mac_slave->perm_hwaddr); + alb_set_slave_mac_addr(slave, free_mac_slave->perm_hwaddr, + free_mac_slave->dev->addr_len); netdev_warn(bond->dev, "the hw address of slave %s is in use by the bond; giving it the hw address of %s\n", slave->dev->name, free_mac_slave->dev->name); @@ -1234,8 +1241,8 @@ static int alb_set_mac_address(struct bonding *bond, void *addr) { struct slave *slave, *rollback_slave; struct list_head *iter; - struct sockaddr sa; - char tmp_addr[ETH_ALEN]; + struct sockaddr_storage ss; + char tmp_addr[MAX_ADDR_LEN]; int res; if (bond->alb_info.rlb_enabled) @@ -1243,12 +1250,14 @@ static int alb_set_mac_address(struct bonding *bond, void *addr) bond_for_each_slave(bond, slave, iter) { /* save net_device's current hw address */ - ether_addr_copy(tmp_addr, slave->dev->dev_addr); + bond_hw_addr_copy(tmp_addr, slave->dev->dev_addr, + slave->dev->addr_len); res = dev_set_mac_address(slave->dev, addr); /* restore net_device's hw address */ - ether_addr_copy(slave->dev->dev_addr, tmp_addr); + bond_hw_addr_copy(slave->dev->dev_addr, tmp_addr, + slave->dev->addr_len); if (res) goto unwind; @@ -1257,16 +1266,19 @@ static int alb_set_mac_address(struct bonding *bond, void *addr) return 0; unwind: - memcpy(sa.sa_data, bond->dev->dev_addr, bond->dev->addr_len); - sa.sa_family = bond->dev->type; + memcpy(ss.__data, bond->dev->dev_addr, bond->dev->addr_len); + ss.ss_family = bond->dev->type; /* unwind from head to the slave that failed */ bond_for_each_slave(bond, rollback_slave, iter) { if (rollback_slave == slave) break; - ether_addr_copy(tmp_addr, rollback_slave->dev->dev_addr); - dev_set_mac_address(rollback_slave->dev, &sa); - ether_addr_copy(rollback_slave->dev->dev_addr, tmp_addr); + bond_hw_addr_copy(tmp_addr, rollback_slave->dev->dev_addr, + rollback_slave->dev->addr_len); + dev_set_mac_address(rollback_slave->dev, + (struct sockaddr *)&ss); + bond_hw_addr_copy(rollback_slave->dev->dev_addr, tmp_addr, + rollback_slave->dev->addr_len); } return res; @@ -1582,7 +1594,8 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave) { int res; - res = alb_set_slave_mac_addr(slave, slave->perm_hwaddr); + res = alb_set_slave_mac_addr(slave, slave->perm_hwaddr, + slave->dev->addr_len); if (res) return res; @@ -1696,17 +1709,20 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave * and thus filter bond->dev_addr's packets, so force bond's mac */ if (BOND_MODE(bond) == BOND_MODE_TLB) { - struct sockaddr sa; - u8 tmp_addr[ETH_ALEN]; + struct sockaddr_storage ss; + u8 tmp_addr[MAX_ADDR_LEN]; - ether_addr_copy(tmp_addr, new_slave->dev->dev_addr); + bond_hw_addr_copy(tmp_addr, new_slave->dev->dev_addr, + new_slave->dev->addr_len); - memcpy(sa.sa_data, bond->dev->dev_addr, bond->dev->addr_len); - sa.sa_family = bond->dev->type; + bond_hw_addr_copy(ss.__data, bond->dev->dev_addr, + bond->dev->addr_len); + ss.ss_family = bond->dev->type; /* we don't care if it can't change its mac, best effort */ - dev_set_mac_address(new_slave->dev, &sa); + dev_set_mac_address(new_slave->dev, (struct sockaddr *)&ss); - ether_addr_copy(new_slave->dev->dev_addr, tmp_addr); + bond_hw_addr_copy(new_slave->dev->dev_addr, tmp_addr, + new_slave->dev->addr_len); } /* curr_active_slave must be set before calling alb_swap_mac_addr */ @@ -1716,7 +1732,8 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave alb_fasten_mac_swap(bond, swap_slave, new_slave); } else { /* set the new_slave to the bond mac address */ - alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr); + alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr, + bond->dev->addr_len); alb_send_learning_packets(new_slave, bond->dev->dev_addr, false); } @@ -1726,19 +1743,19 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr) { struct bonding *bond = netdev_priv(bond_dev); - struct sockaddr *sa = addr; + struct sockaddr_storage *ss = addr; struct slave *curr_active; struct slave *swap_slave; int res; - if (!is_valid_ether_addr(sa->sa_data)) + if (!is_valid_ether_addr(ss->__data)) return -EADDRNOTAVAIL; res = alb_set_mac_address(bond, addr); if (res) return res; - memcpy(bond_dev->dev_addr, sa->sa_data, bond_dev->addr_len); + bond_hw_addr_copy(bond_dev->dev_addr, ss->__data, bond_dev->addr_len); /* If there is no curr_active_slave there is nothing else to do. * Otherwise we'll need to pass the new address to it and handle @@ -1754,7 +1771,8 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr) alb_swap_mac_addr(swap_slave, curr_active); alb_fasten_mac_swap(bond, swap_slave, curr_active); } else { - alb_set_slave_mac_addr(curr_active, bond_dev->dev_addr); + alb_set_slave_mac_addr(curr_active, bond_dev->dev_addr, + bond_dev->addr_len); alb_send_learning_packets(curr_active, bond_dev->dev_addr, false); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 535388b15cde..aba7352906a5 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -645,8 +645,8 @@ static void bond_do_fail_over_mac(struct bonding *bond, struct slave *new_active, struct slave *old_active) { - u8 tmp_mac[ETH_ALEN]; - struct sockaddr saddr; + u8 tmp_mac[MAX_ADDR_LEN]; + struct sockaddr_storage ss; int rv; switch (bond->params.fail_over_mac) { @@ -666,16 +666,20 @@ static void bond_do_fail_over_mac(struct bonding *bond, old_active = bond_get_old_active(bond, new_active); if (old_active) { - ether_addr_copy(tmp_mac, new_active->dev->dev_addr); - ether_addr_copy(saddr.sa_data, - old_active->dev->dev_addr); - saddr.sa_family = new_active->dev->type; + bond_hw_addr_copy(tmp_mac, new_active->dev->dev_addr, + new_active->dev->addr_len); + bond_hw_addr_copy(ss.__data, + old_active->dev->dev_addr, + old_active->dev->addr_len); + ss.ss_family = new_active->dev->type; } else { - ether_addr_copy(saddr.sa_data, bond->dev->dev_addr); - saddr.sa_family = bond->dev->type; + bond_hw_addr_copy(ss.__data, bond->dev->dev_addr, + bond->dev->addr_len); + ss.ss_family = bond->dev->type; } - rv = dev_set_mac_address(new_active->dev, &saddr); + rv = dev_set_mac_address(new_active->dev, + (struct sockaddr *)&ss); if (rv) { netdev_err(bond->dev, "Error %d setting MAC of slave %s\n", -rv, new_active->dev->name); @@ -685,10 +689,12 @@ static void bond_do_fail_over_mac(struct bonding *bond, if (!old_active) goto out; - ether_addr_copy(saddr.sa_data, tmp_mac); - saddr.sa_family = old_active->dev->type; + bond_hw_addr_copy(ss.__data, tmp_mac, + new_active->dev->addr_len); + ss.ss_family = old_active->dev->type; - rv = dev_set_mac_address(old_active->dev, &saddr); + rv = dev_set_mac_address(old_active->dev, + (struct sockaddr *)&ss); if (rv) netdev_err(bond->dev, "Error %d setting MAC of slave %s\n", -rv, new_active->dev->name); @@ -1184,7 +1190,8 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) kfree_skb(skb); return RX_HANDLER_CONSUMED; } - ether_addr_copy(eth_hdr(skb)->h_dest, bond->dev->dev_addr); + bond_hw_addr_copy(eth_hdr(skb)->h_dest, bond->dev->dev_addr, + bond->dev->addr_len); } return ret; @@ -1323,7 +1330,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) struct bonding *bond = netdev_priv(bond_dev); const struct net_device_ops *slave_ops = slave_dev->netdev_ops; struct slave *new_slave = NULL, *prev_slave; - struct sockaddr addr; + struct sockaddr_storage ss; int link_reporting; int res = 0, i; @@ -1474,16 +1481,17 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) * that need it, and for restoring it upon release, and then * set it to the master's address */ - ether_addr_copy(new_slave->perm_hwaddr, slave_dev->dev_addr); + bond_hw_addr_copy(new_slave->perm_hwaddr, slave_dev->dev_addr, + slave_dev->addr_len); if (!bond->params.fail_over_mac || BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) { /* Set slave to master's mac address. The application already * set the master's mac address to that of the first slave */ - memcpy(addr.sa_data, bond_dev->dev_addr, bond_dev->addr_len); - addr.sa_family = slave_dev->type; - res = dev_set_mac_address(slave_dev, &addr); + memcpy(ss.__data, bond_dev->dev_addr, bond_dev->addr_len); + ss.ss_family = slave_dev->type; + res = dev_set_mac_address(slave_dev, (struct sockaddr *)&ss); if (res) { netdev_dbg(bond_dev, "Error %d calling set_mac_address\n", res); goto err_restore_mtu; @@ -1767,9 +1775,10 @@ err_restore_mac: * MAC if this slave's MAC is in use by the bond, or at * least print a warning. */ - ether_addr_copy(addr.sa_data, new_slave->perm_hwaddr); - addr.sa_family = slave_dev->type; - dev_set_mac_address(slave_dev, &addr); + bond_hw_addr_copy(ss.__data, new_slave->perm_hwaddr, + new_slave->dev->addr_len); + ss.ss_family = slave_dev->type; + dev_set_mac_address(slave_dev, (struct sockaddr *)&ss); } err_restore_mtu: @@ -1812,7 +1821,7 @@ static int __bond_release_one(struct net_device *bond_dev, { struct bonding *bond = netdev_priv(bond_dev); struct slave *slave, *oldcurrent; - struct sockaddr addr; + struct sockaddr_storage ss; int old_flags = bond_dev->flags; netdev_features_t old_features = bond_dev->features; @@ -1947,9 +1956,10 @@ static int __bond_release_one(struct net_device *bond_dev, if (bond->params.fail_over_mac != BOND_FOM_ACTIVE || BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) { /* restore original ("permanent") mac address */ - ether_addr_copy(addr.sa_data, slave->perm_hwaddr); - addr.sa_family = slave_dev->type; - dev_set_mac_address(slave_dev, &addr); + bond_hw_addr_copy(ss.__data, slave->perm_hwaddr, + slave->dev->addr_len); + ss.ss_family = slave_dev->type; + dev_set_mac_address(slave_dev, (struct sockaddr *)&ss); } dev_set_mtu(slave_dev, slave->original_mtu); @@ -3626,7 +3636,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr) { struct bonding *bond = netdev_priv(bond_dev); struct slave *slave, *rollback_slave; - struct sockaddr *sa = addr, tmp_sa; + struct sockaddr_storage *ss = addr, tmp_ss; struct list_head *iter; int res = 0; @@ -3643,7 +3653,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr) BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) return 0; - if (!is_valid_ether_addr(sa->sa_data)) + if (!is_valid_ether_addr(ss->__data)) return -EADDRNOTAVAIL; bond_for_each_slave(bond, slave, iter) { @@ -3662,12 +3672,12 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr) } /* success */ - memcpy(bond_dev->dev_addr, sa->sa_data, bond_dev->addr_len); + memcpy(bond_dev->dev_addr, ss->__data, bond_dev->addr_len); return 0; unwind: - memcpy(tmp_sa.sa_data, bond_dev->dev_addr, bond_dev->addr_len); - tmp_sa.sa_family = bond_dev->type; + memcpy(tmp_ss.__data, bond_dev->dev_addr, bond_dev->addr_len); + tmp_ss.ss_family = bond_dev->type; /* unwind from head to the slave that failed */ bond_for_each_slave(bond, rollback_slave, iter) { @@ -3676,7 +3686,8 @@ unwind: if (rollback_slave == slave) break; - tmp_res = dev_set_mac_address(rollback_slave->dev, &tmp_sa); + tmp_res = dev_set_mac_address(rollback_slave->dev, + (struct sockaddr *)&tmp_ss); if (tmp_res) { netdev_dbg(bond_dev, "unwind err %d dev %s\n", tmp_res, rollback_slave->dev->name); diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index f514fe5e80a5..d8d4ada034b7 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c @@ -183,7 +183,8 @@ static void bond_info_show_slave(struct seq_file *seq, seq_printf(seq, "Link Failure Count: %u\n", slave->link_failure_count); - seq_printf(seq, "Permanent HW addr: %pM\n", slave->perm_hwaddr); + seq_printf(seq, "Permanent HW addr: %*phC\n", + slave->dev->addr_len, slave->perm_hwaddr); seq_printf(seq, "Slave queue ID: %d\n", slave->queue_id); if (BOND_MODE(bond) == BOND_MODE_8023AD) { diff --git a/include/net/bonding.h b/include/net/bonding.h index fb2dd97857c4..04a21e8048be 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -166,7 +166,7 @@ struct slave { u32 link_failure_count; u32 speed; u16 queue_id; - u8 perm_hwaddr[ETH_ALEN]; + u8 perm_hwaddr[MAX_ADDR_LEN]; struct ad_slave_info *ad_info; struct tlb_slave_info tlb_info; #ifdef CONFIG_NET_POLL_CONTROLLER @@ -402,6 +402,16 @@ static inline bool bond_slave_can_tx(struct slave *slave) bond_is_active_slave(slave); } +static inline void bond_hw_addr_copy(u8 *dst, const u8 *src, unsigned int len) +{ + if (len == ETH_ALEN) { + ether_addr_copy(dst, src); + return; + } + + memcpy(dst, src, len); +} + #define BOND_PRI_RESELECT_ALWAYS 0 #define BOND_PRI_RESELECT_BETTER 1 #define BOND_PRI_RESELECT_FAILURE 2 -- cgit From 540fca35e38d15777b310f450f63f056e63039f5 Mon Sep 17 00:00:00 2001 From: Phil Turnbull Date: Wed, 23 Nov 2016 13:33:58 -0500 Subject: fm10k: correctly check if interface is removed FM10K_REMOVED expects a hardware address, not a 'struct fm10k_hw'. Fixes: 5cb8db4a4cbc ("fm10k: Add support for VF") Signed-off-by: Phil Turnbull Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index 0c84fef750f4..078e8d93db5a 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -939,7 +939,7 @@ static void fm10k_self_test(struct net_device *dev, memset(data, 0, sizeof(*data) * FM10K_TEST_LEN); - if (FM10K_REMOVED(hw)) { + if (FM10K_REMOVED(hw->hw_addr)) { netif_err(interface, drv, dev, "Interface removed - test blocked\n"); eth_test->flags |= ETH_TEST_FL_FAILED; -- cgit From 3ee7b3a3b9deb26278af308743ec927847c70366 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 12 Jan 2017 15:59:38 -0800 Subject: fm10k: use a BITMAP for flags to avoid race conditions Replace bitwise operators and #defines with a BITMAP and enumeration values. This is similar to how we handle the "state" values as well. This has two distinct advantages over the old method. First, we ensure correctness of operations which are currently problematic due to race conditions. Suppose that two kernel threads are running, such as the watchdog and an ethtool ioctl, and both modify flags. We'll say that the watchdog is CPU A, and the ethtool ioctl is CPU B. CPU A sets FLAG_1, which can be seen as CPU A read FLAGS CPU A write FLAGS | FLAG_1 CPU B sets FLAG_2, which can be seen as CPU B read FLAGS CPU A write FLAGS | FLAG_2 However, "|=" and "&=" operators are not actually atomic. So this could be ordered like the following: CPU A read FLAGS -> variable CPU B read FLAGS -> variable CPU A write FLAGS (variable | FLAG_1) CPU B write FLAGS (variable | FLAG_2) Notice how the 2nd write from CPU B could actually undo the write from CPU A because it isn't guaranteed that the |= operation is atomic. In practice the race windows for most flag writes is incredibly narrow so it is not easy to isolate issues. However, the more flags we have, the more likely they will cause problems. Additionally, if such a problem were to arise, it would be incredibly difficult to track down. Second, there is an additional advantage beyond code correctness. We can now automatically size the BITMAP if more flags were added, so that we do not need to remember that flags is u32 and thus if we added too many flags we would over-run the variable. This is not a likely occurrence for fm10k driver, but this patch can serve as an example for other drivers which have many more flags. This particular change does have a bit of trouble converting some of the idioms previously used with the #defines for flags. Specifically, when converting FM10K_FLAG_RSS_FIELD_IPV[46]_UDP flags. This whole operation was actually quite problematic, because we actually stored flags separately. This could more easily show the problem of the above re-ordering issue. This is really difficult to test whether atomics make a difference in practical scenarios, but you can ensure that basic functionality remains the same. This patch has a lot of code coverage, but most of it is relatively simple. While we are modifying these files, update their copyright year. Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k.h | 27 ++++++++--- drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | 62 ++++++++++++++++-------- drivers/net/ethernet/intel/fm10k/fm10k_main.c | 6 +-- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 4 +- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 31 ++++++------ 5 files changed, 82 insertions(+), 48 deletions(-) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index 52b979443cde..d6db1c48d4dc 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -1,5 +1,5 @@ /* Intel(R) Ethernet Switch Host Interface Driver - * Copyright(c) 2013 - 2016 Intel Corporation. + * Copyright(c) 2013 - 2017 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, @@ -249,6 +249,23 @@ struct fm10k_udp_port { /* one work queue for entire driver */ extern struct workqueue_struct *fm10k_workqueue; +/* The following enumeration contains flags which indicate or enable modified + * driver behaviors. To avoid race conditions, the flags are stored in + * a BITMAP in the fm10k_intfc structure. The BITMAP should be accessed using + * atomic *_bit() operations. + */ +enum fm10k_flags_t { + FM10K_FLAG_RESET_REQUESTED, + FM10K_FLAG_RSS_FIELD_IPV4_UDP, + FM10K_FLAG_RSS_FIELD_IPV6_UDP, + FM10K_FLAG_SWPRI_CONFIG, + /* __FM10K_FLAGS_SIZE__ is used to calculate the size of + * interface->flags and must be the last value in this + * enumeration. + */ + __FM10K_FLAGS_SIZE__ +}; + struct fm10k_intfc { unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; struct net_device *netdev; @@ -256,11 +273,9 @@ struct fm10k_intfc { struct pci_dev *pdev; unsigned long state; - u32 flags; -#define FM10K_FLAG_RESET_REQUESTED (u32)(BIT(0)) -#define FM10K_FLAG_RSS_FIELD_IPV4_UDP (u32)(BIT(1)) -#define FM10K_FLAG_RSS_FIELD_IPV6_UDP (u32)(BIT(2)) -#define FM10K_FLAG_SWPRI_CONFIG (u32)(BIT(3)) + /* Access flag values using atomic *_bit() operations */ + DECLARE_BITMAP(flags, __FM10K_FLAGS_SIZE__); + int xcast_mode; /* Tx fast path data */ diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index 078e8d93db5a..5269f3ed4682 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -1,5 +1,5 @@ /* Intel(R) Ethernet Switch Host Interface Driver - * Copyright(c) 2013 - 2016 Intel Corporation. + * Copyright(c) 2013 - 2017 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, @@ -716,7 +716,8 @@ static int fm10k_get_rss_hash_opts(struct fm10k_intfc *interface, cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; /* fall through */ case UDP_V4_FLOW: - if (interface->flags & FM10K_FLAG_RSS_FIELD_IPV4_UDP) + if (test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, + interface->flags)) cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; /* fall through */ case SCTP_V4_FLOW: @@ -732,7 +733,8 @@ static int fm10k_get_rss_hash_opts(struct fm10k_intfc *interface, cmd->data |= RXH_IP_SRC | RXH_IP_DST; break; case UDP_V6_FLOW: - if (interface->flags & FM10K_FLAG_RSS_FIELD_IPV6_UDP) + if (test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, + interface->flags)) cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; cmd->data |= RXH_IP_SRC | RXH_IP_DST; break; @@ -764,12 +766,13 @@ static int fm10k_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, return ret; } -#define UDP_RSS_FLAGS (FM10K_FLAG_RSS_FIELD_IPV4_UDP | \ - FM10K_FLAG_RSS_FIELD_IPV6_UDP) static int fm10k_set_rss_hash_opt(struct fm10k_intfc *interface, struct ethtool_rxnfc *nfc) { - u32 flags = interface->flags; + int rss_ipv4_udp = test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, + interface->flags); + int rss_ipv6_udp = test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, + interface->flags); /* RSS does not support anything other than hashing * to queues on src and dst IPs and ports @@ -793,10 +796,12 @@ static int fm10k_set_rss_hash_opt(struct fm10k_intfc *interface, return -EINVAL; switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { case 0: - flags &= ~FM10K_FLAG_RSS_FIELD_IPV4_UDP; + clear_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, + interface->flags); break; case (RXH_L4_B_0_1 | RXH_L4_B_2_3): - flags |= FM10K_FLAG_RSS_FIELD_IPV4_UDP; + set_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, + interface->flags); break; default: return -EINVAL; @@ -808,10 +813,12 @@ static int fm10k_set_rss_hash_opt(struct fm10k_intfc *interface, return -EINVAL; switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { case 0: - flags &= ~FM10K_FLAG_RSS_FIELD_IPV6_UDP; + clear_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, + interface->flags); break; case (RXH_L4_B_0_1 | RXH_L4_B_2_3): - flags |= FM10K_FLAG_RSS_FIELD_IPV6_UDP; + set_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, + interface->flags); break; default: return -EINVAL; @@ -835,28 +842,41 @@ static int fm10k_set_rss_hash_opt(struct fm10k_intfc *interface, return -EINVAL; } - /* if we changed something we need to update flags */ - if (flags != interface->flags) { + /* If something changed we need to update the MRQC register. Note that + * test_bit() is guaranteed to return strictly 0 or 1, so testing for + * equality is safe. + */ + if ((rss_ipv4_udp != test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, + interface->flags)) || + (rss_ipv6_udp != test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, + interface->flags))) { struct fm10k_hw *hw = &interface->hw; + bool warn = false; u32 mrqc; - if ((flags & UDP_RSS_FLAGS) && - !(interface->flags & UDP_RSS_FLAGS)) - netif_warn(interface, drv, interface->netdev, - "enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n"); - - interface->flags = flags; - /* Perform hash on these packet types */ mrqc = FM10K_MRQC_IPV4 | FM10K_MRQC_TCP_IPV4 | FM10K_MRQC_IPV6 | FM10K_MRQC_TCP_IPV6; - if (flags & FM10K_FLAG_RSS_FIELD_IPV4_UDP) + if (test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, + interface->flags)) { mrqc |= FM10K_MRQC_UDP_IPV4; - if (flags & FM10K_FLAG_RSS_FIELD_IPV6_UDP) + warn = true; + } + if (test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, + interface->flags)) { mrqc |= FM10K_MRQC_UDP_IPV6; + warn = true; + } + + /* If we enable UDP RSS display a warning that this may cause + * fragmented UDP packets to arrive out of order. + */ + if (warn) + netif_warn(interface, drv, interface->netdev, + "enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n"); fm10k_write_reg(hw, FM10K_MRQC(0), mrqc); } diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index 5bb233a9614c..f9612a6b8524 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -1,5 +1,5 @@ /* Intel(R) Ethernet Switch Host Interface Driver - * Copyright(c) 2013 - 2016 Intel Corporation. + * Copyright(c) 2013 - 2017 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, @@ -34,7 +34,7 @@ const char fm10k_driver_version[] = DRV_VERSION; char fm10k_driver_name[] = "fm10k"; static const char fm10k_driver_string[] = DRV_SUMMARY; static const char fm10k_copyright[] = - "Copyright (c) 2013 - 2016 Intel Corporation."; + "Copyright(c) 2013 - 2017 Intel Corporation."; MODULE_AUTHOR("Intel Corporation, "); MODULE_DESCRIPTION(DRV_SUMMARY); @@ -1193,7 +1193,7 @@ void fm10k_tx_timeout_reset(struct fm10k_intfc *interface) /* Do the reset outside of interrupt context */ if (!test_bit(__FM10K_DOWN, &interface->state)) { interface->tx_timeout_count++; - interface->flags |= FM10K_FLAG_RESET_REQUESTED; + set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags); fm10k_service_event_schedule(interface); } } diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 72481670478c..008408dd6a56 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1,5 +1,5 @@ /* Intel(R) Ethernet Switch Host Interface Driver - * Copyright(c) 2013 - 2016 Intel Corporation. + * Copyright(c) 2013 - 2017 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, @@ -1207,7 +1207,7 @@ int fm10k_setup_tc(struct net_device *dev, u8 tc) goto err_open; /* flag to indicate SWPRI has yet to be updated */ - interface->flags |= FM10K_FLAG_SWPRI_CONFIG; + set_bit(FM10K_FLAG_SWPRI_CONFIG, interface->flags); return 0; err_open: diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 60d9b6aaf63a..7ae22dd5dd7c 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -1,5 +1,5 @@ /* Intel(R) Ethernet Switch Host Interface Driver - * Copyright(c) 2013 - 2016 Intel Corporation. + * Copyright(c) 2013 - 2017 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, @@ -137,7 +137,7 @@ static void fm10k_detach_subtask(struct fm10k_intfc *interface) if (~value) { interface->hw.hw_addr = interface->uc_addr; netif_device_attach(netdev); - interface->flags |= FM10K_FLAG_RESET_REQUESTED; + set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags); netdev_warn(netdev, "PCIe link restored, device now attached\n"); return; } @@ -273,11 +273,10 @@ static void fm10k_reinit(struct fm10k_intfc *interface) static void fm10k_reset_subtask(struct fm10k_intfc *interface) { - if (!(interface->flags & FM10K_FLAG_RESET_REQUESTED)) + if (!test_and_clear_bit(FM10K_FLAG_RESET_REQUESTED, + interface->flags)) return; - interface->flags &= ~FM10K_FLAG_RESET_REQUESTED; - netdev_err(interface->netdev, "Reset interface\n"); fm10k_reinit(interface); @@ -296,7 +295,7 @@ static void fm10k_configure_swpri_map(struct fm10k_intfc *interface) int i; /* clear flag indicating update is needed */ - interface->flags &= ~FM10K_FLAG_SWPRI_CONFIG; + clear_bit(FM10K_FLAG_SWPRI_CONFIG, interface->flags); /* these registers are only available on the PF */ if (hw->mac.type != fm10k_mac_pf) @@ -324,7 +323,7 @@ static void fm10k_watchdog_update_host_state(struct fm10k_intfc *interface) clear_bit(__FM10K_LINK_DOWN, &interface->state); } - if (interface->flags & FM10K_FLAG_SWPRI_CONFIG) { + if (test_bit(FM10K_FLAG_SWPRI_CONFIG, interface->flags)) { if (rtnl_trylock()) { fm10k_configure_swpri_map(interface); rtnl_unlock(); @@ -336,7 +335,7 @@ static void fm10k_watchdog_update_host_state(struct fm10k_intfc *interface) err = hw->mac.ops.get_host_state(hw, &interface->host_ready); if (err && time_is_before_jiffies(interface->last_reset)) - interface->flags |= FM10K_FLAG_RESET_REQUESTED; + set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags); /* free the lock */ fm10k_mbx_unlock(interface); @@ -523,7 +522,7 @@ static void fm10k_watchdog_flush_tx(struct fm10k_intfc *interface) * controller to flush Tx. */ if (some_tx_pending) - interface->flags |= FM10K_FLAG_RESET_REQUESTED; + set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags); } /** @@ -864,9 +863,9 @@ static void fm10k_configure_dglort(struct fm10k_intfc *interface) FM10K_MRQC_IPV6 | FM10K_MRQC_TCP_IPV6; - if (interface->flags & FM10K_FLAG_RSS_FIELD_IPV4_UDP) + if (test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, interface->flags)) mrqc |= FM10K_MRQC_UDP_IPV4; - if (interface->flags & FM10K_FLAG_RSS_FIELD_IPV6_UDP) + if (test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, interface->flags)) mrqc |= FM10K_MRQC_UDP_IPV6; fm10k_write_reg(hw, FM10K_MRQC(0), mrqc); @@ -1168,7 +1167,7 @@ static irqreturn_t fm10k_msix_mbx_pf(int __always_unused irq, void *data) } if (err == FM10K_ERR_RESET_REQUESTED) - interface->flags |= FM10K_FLAG_RESET_REQUESTED; + set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags); /* if switch toggled state we should reset GLORTs */ if (eicr & FM10K_EICR_SWITCHNOTREADY) { @@ -1247,12 +1246,12 @@ static s32 fm10k_mbx_mac_addr(struct fm10k_hw *hw, u32 **results, /* MAC was changed so we need reset */ if (is_valid_ether_addr(hw->mac.perm_addr) && !ether_addr_equal(hw->mac.perm_addr, hw->mac.addr)) - interface->flags |= FM10K_FLAG_RESET_REQUESTED; + set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags); /* VLAN override was changed, or default VLAN changed */ if ((vlan_override != hw->mac.vlan_override) || (default_vid != hw->mac.default_vid)) - interface->flags |= FM10K_FLAG_RESET_REQUESTED; + set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags); return 0; } @@ -1357,7 +1356,7 @@ static s32 fm10k_lport_map(struct fm10k_hw *hw, u32 **results, /* we need to reset if port count was just updated */ if (dglort_map != hw->mac.dglort_map) - interface->flags |= FM10K_FLAG_RESET_REQUESTED; + set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags); return 0; } @@ -1396,7 +1395,7 @@ static s32 fm10k_update_pvid(struct fm10k_hw *hw, u32 **results, /* we need to reset if default VLAN was just updated */ if (pvid != hw->mac.default_vid) - interface->flags |= FM10K_FLAG_RESET_REQUESTED; + set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags); hw->mac.default_vid = pvid; -- cgit From 469295578790150cf0ce3395bf815edce6e37fb1 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 12 Jan 2017 15:59:39 -0800 Subject: fm10k: future-proof state bitmaps using DECLARE_BITMAP This ensures that future programmers do not have to remember to re-size the bitmaps due to adding new values. Although this is unlikely for this driver, it may happen and it's best to prevent it from ever being an issue. Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k.h | 40 ++++++++------- drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | 4 +- drivers/net/ethernet/intel/fm10k/fm10k_main.c | 10 ++-- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 2 +- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 62 ++++++++++++------------ 5 files changed, 61 insertions(+), 57 deletions(-) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index d6db1c48d4dc..b496300d0268 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -65,14 +65,16 @@ enum fm10k_ring_state_t { __FM10K_TX_DETECT_HANG, __FM10K_HANG_CHECK_ARMED, __FM10K_TX_XPS_INIT_DONE, + /* This must be last and is used to calculate BITMAP size */ + __FM10K_TX_STATE_SIZE__, }; #define check_for_tx_hang(ring) \ - test_bit(__FM10K_TX_DETECT_HANG, &(ring)->state) + test_bit(__FM10K_TX_DETECT_HANG, (ring)->state) #define set_check_for_tx_hang(ring) \ - set_bit(__FM10K_TX_DETECT_HANG, &(ring)->state) + set_bit(__FM10K_TX_DETECT_HANG, (ring)->state) #define clear_check_for_tx_hang(ring) \ - clear_bit(__FM10K_TX_DETECT_HANG, &(ring)->state) + clear_bit(__FM10K_TX_DETECT_HANG, (ring)->state) struct fm10k_tx_buffer { struct fm10k_tx_desc *next_to_watch; @@ -126,7 +128,7 @@ struct fm10k_ring { struct fm10k_rx_buffer *rx_buffer; }; u32 __iomem *tail; - unsigned long state; + DECLARE_BITMAP(state, __FM10K_TX_STATE_SIZE__); dma_addr_t dma; /* phys. address of descriptor ring */ unsigned int size; /* length in bytes */ @@ -266,12 +268,24 @@ enum fm10k_flags_t { __FM10K_FLAGS_SIZE__ }; +enum fm10k_state_t { + __FM10K_RESETTING, + __FM10K_DOWN, + __FM10K_SERVICE_SCHED, + __FM10K_SERVICE_DISABLE, + __FM10K_MBX_LOCK, + __FM10K_LINK_DOWN, + __FM10K_UPDATING_STATS, + /* This value must be last and determines the BITMAP size */ + __FM10K_STATE_SIZE__, +}; + struct fm10k_intfc { unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; struct net_device *netdev; struct fm10k_l2_accel *l2_accel; /* pointer to L2 acceleration list */ struct pci_dev *pdev; - unsigned long state; + DECLARE_BITMAP(state, __FM10K_STATE_SIZE__); /* Access flag values using atomic *_bit() operations */ DECLARE_BITMAP(flags, __FM10K_FLAGS_SIZE__); @@ -367,22 +381,12 @@ struct fm10k_intfc { u16 vid; }; -enum fm10k_state_t { - __FM10K_RESETTING, - __FM10K_DOWN, - __FM10K_SERVICE_SCHED, - __FM10K_SERVICE_DISABLE, - __FM10K_MBX_LOCK, - __FM10K_LINK_DOWN, - __FM10K_UPDATING_STATS, -}; - static inline void fm10k_mbx_lock(struct fm10k_intfc *interface) { /* busy loop if we cannot obtain the lock as some calls * such as ndo_set_rx_mode may be made in atomic context */ - while (test_and_set_bit(__FM10K_MBX_LOCK, &interface->state)) + while (test_and_set_bit(__FM10K_MBX_LOCK, interface->state)) udelay(20); } @@ -390,12 +394,12 @@ static inline void fm10k_mbx_unlock(struct fm10k_intfc *interface) { /* flush memory to make sure state is correct */ smp_mb__before_atomic(); - clear_bit(__FM10K_MBX_LOCK, &interface->state); + clear_bit(__FM10K_MBX_LOCK, interface->state); } static inline int fm10k_mbx_trylock(struct fm10k_intfc *interface) { - return !test_and_set_bit(__FM10K_MBX_LOCK, &interface->state); + return !test_and_set_bit(__FM10K_MBX_LOCK, interface->state); } /* fm10k_test_staterr - test bits in Rx descriptor status and error fields */ diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index 5269f3ed4682..c7234f35f8ff 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -562,7 +562,7 @@ static int fm10k_set_ringparam(struct net_device *netdev, return 0; } - while (test_and_set_bit(__FM10K_RESETTING, &interface->state)) + while (test_and_set_bit(__FM10K_RESETTING, interface->state)) usleep_range(1000, 2000); if (!netif_running(interface->netdev)) { @@ -648,7 +648,7 @@ err_setup: fm10k_up(interface); vfree(temp_ring); clear_reset: - clear_bit(__FM10K_RESETTING, &interface->state); + clear_bit(__FM10K_RESETTING, interface->state); return err; } diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index f9612a6b8524..9dffaba85ae6 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -1175,13 +1175,13 @@ bool fm10k_check_tx_hang(struct fm10k_ring *tx_ring) /* update completed stats and continue */ tx_ring->tx_stats.tx_done_old = tx_done; /* reset the countdown */ - clear_bit(__FM10K_HANG_CHECK_ARMED, &tx_ring->state); + clear_bit(__FM10K_HANG_CHECK_ARMED, tx_ring->state); return false; } /* make sure it is true for two checks in a row */ - return test_and_set_bit(__FM10K_HANG_CHECK_ARMED, &tx_ring->state); + return test_and_set_bit(__FM10K_HANG_CHECK_ARMED, tx_ring->state); } /** @@ -1191,7 +1191,7 @@ bool fm10k_check_tx_hang(struct fm10k_ring *tx_ring) void fm10k_tx_timeout_reset(struct fm10k_intfc *interface) { /* Do the reset outside of interrupt context */ - if (!test_bit(__FM10K_DOWN, &interface->state)) { + if (!test_bit(__FM10K_DOWN, interface->state)) { interface->tx_timeout_count++; set_bit(FM10K_FLAG_RESET_REQUESTED, interface->flags); fm10k_service_event_schedule(interface); @@ -1214,7 +1214,7 @@ static bool fm10k_clean_tx_irq(struct fm10k_q_vector *q_vector, unsigned int budget = q_vector->tx.work_limit; unsigned int i = tx_ring->next_to_clean; - if (test_bit(__FM10K_DOWN, &interface->state)) + if (test_bit(__FM10K_DOWN, interface->state)) return true; tx_buffer = &tx_ring->tx_buffer[i]; @@ -1344,7 +1344,7 @@ static bool fm10k_clean_tx_irq(struct fm10k_q_vector *q_vector, smp_mb(); if (__netif_subqueue_stopped(tx_ring->netdev, tx_ring->queue_index) && - !test_bit(__FM10K_DOWN, &interface->state)) { + !test_bit(__FM10K_DOWN, interface->state)) { netif_wake_subqueue(tx_ring->netdev, tx_ring->queue_index); ++tx_ring->tx_stats.restart_queue; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 008408dd6a56..344a392f7a76 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -822,7 +822,7 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set) /* Do not throw an error if the interface is down. We will sync once * we come up */ - if (test_bit(__FM10K_DOWN, &interface->state)) + if (test_bit(__FM10K_DOWN, interface->state)) return 0; fm10k_mbx_lock(interface); diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 7ae22dd5dd7c..2a54372975f6 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -93,18 +93,18 @@ static int fm10k_hw_ready(struct fm10k_intfc *interface) void fm10k_service_event_schedule(struct fm10k_intfc *interface) { - if (!test_bit(__FM10K_SERVICE_DISABLE, &interface->state) && - !test_and_set_bit(__FM10K_SERVICE_SCHED, &interface->state)) + if (!test_bit(__FM10K_SERVICE_DISABLE, interface->state) && + !test_and_set_bit(__FM10K_SERVICE_SCHED, interface->state)) queue_work(fm10k_workqueue, &interface->service_task); } static void fm10k_service_event_complete(struct fm10k_intfc *interface) { - WARN_ON(!test_bit(__FM10K_SERVICE_SCHED, &interface->state)); + WARN_ON(!test_bit(__FM10K_SERVICE_SCHED, interface->state)); /* flush memory to make sure state is correct before next watchog */ smp_mb__before_atomic(); - clear_bit(__FM10K_SERVICE_SCHED, &interface->state); + clear_bit(__FM10K_SERVICE_SCHED, interface->state); } /** @@ -159,7 +159,7 @@ static void fm10k_prepare_for_reset(struct fm10k_intfc *interface) /* put off any impending NetWatchDogTimeout */ netif_trans_update(netdev); - while (test_and_set_bit(__FM10K_RESETTING, &interface->state)) + while (test_and_set_bit(__FM10K_RESETTING, interface->state)) usleep_range(1000, 2000); rtnl_lock(); @@ -242,7 +242,7 @@ static int fm10k_handle_reset(struct fm10k_intfc *interface) rtnl_unlock(); - clear_bit(__FM10K_RESETTING, &interface->state); + clear_bit(__FM10K_RESETTING, interface->state); return err; err_open: @@ -254,7 +254,7 @@ reinit_err: rtnl_unlock(); - clear_bit(__FM10K_RESETTING, &interface->state); + clear_bit(__FM10K_RESETTING, interface->state); return err; } @@ -316,11 +316,11 @@ static void fm10k_watchdog_update_host_state(struct fm10k_intfc *interface) struct fm10k_hw *hw = &interface->hw; s32 err; - if (test_bit(__FM10K_LINK_DOWN, &interface->state)) { + if (test_bit(__FM10K_LINK_DOWN, interface->state)) { interface->host_ready = false; if (time_is_after_jiffies(interface->link_down_event)) return; - clear_bit(__FM10K_LINK_DOWN, &interface->state); + clear_bit(__FM10K_LINK_DOWN, interface->state); } if (test_bit(FM10K_FLAG_SWPRI_CONFIG, interface->flags)) { @@ -411,7 +411,7 @@ void fm10k_update_stats(struct fm10k_intfc *interface) int i; /* ensure only one thread updates stats at a time */ - if (test_and_set_bit(__FM10K_UPDATING_STATS, &interface->state)) + if (test_and_set_bit(__FM10K_UPDATING_STATS, interface->state)) return; /* do not allow stats update via service task for next second */ @@ -492,7 +492,7 @@ void fm10k_update_stats(struct fm10k_intfc *interface) net_stats->rx_errors = rx_errors; net_stats->rx_dropped = interface->stats.nodesc_drop.count; - clear_bit(__FM10K_UPDATING_STATS, &interface->state); + clear_bit(__FM10K_UPDATING_STATS, interface->state); } /** @@ -532,8 +532,8 @@ static void fm10k_watchdog_flush_tx(struct fm10k_intfc *interface) static void fm10k_watchdog_subtask(struct fm10k_intfc *interface) { /* if interface is down do nothing */ - if (test_bit(__FM10K_DOWN, &interface->state) || - test_bit(__FM10K_RESETTING, &interface->state)) + if (test_bit(__FM10K_DOWN, interface->state) || + test_bit(__FM10K_RESETTING, interface->state)) return; if (interface->host_ready) @@ -563,8 +563,8 @@ static void fm10k_check_hang_subtask(struct fm10k_intfc *interface) int i; /* If we're down or resetting, just bail */ - if (test_bit(__FM10K_DOWN, &interface->state) || - test_bit(__FM10K_RESETTING, &interface->state)) + if (test_bit(__FM10K_DOWN, interface->state) || + test_bit(__FM10K_RESETTING, interface->state)) return; /* rate limit tx hang checks to only once every 2 seconds */ @@ -663,7 +663,7 @@ static void fm10k_configure_tx_ring(struct fm10k_intfc *interface, FM10K_PFVTCTL_FTAG_DESC_ENABLE); /* Initialize XPS */ - if (!test_and_set_bit(__FM10K_TX_XPS_INIT_DONE, &ring->state) && + if (!test_and_set_bit(__FM10K_TX_XPS_INIT_DONE, ring->state) && ring->q_vector) netif_set_xps_queue(ring->netdev, &ring->q_vector->affinity_mask, @@ -980,7 +980,7 @@ void fm10k_netpoll(struct net_device *netdev) int i; /* if interface is down do nothing */ - if (test_bit(__FM10K_DOWN, &interface->state)) + if (test_bit(__FM10K_DOWN, interface->state)) return; for (i = 0; i < interface->num_q_vectors; i++) @@ -1173,7 +1173,7 @@ static irqreturn_t fm10k_msix_mbx_pf(int __always_unused irq, void *data) if (eicr & FM10K_EICR_SWITCHNOTREADY) { /* force link down for at least 4 seconds */ interface->link_down_event = jiffies + (4 * HZ); - set_bit(__FM10K_LINK_DOWN, &interface->state); + set_bit(__FM10K_LINK_DOWN, interface->state); /* reset dglort_map back to no config */ hw->mac.dglort_map = FM10K_DGLORTMAP_NONE; @@ -1325,7 +1325,7 @@ static s32 fm10k_lport_map(struct fm10k_hw *hw, u32 **results, if (!err && hw->swapi.status) { /* force link down for a reasonable delay */ interface->link_down_event = jiffies + (2 * HZ); - set_bit(__FM10K_LINK_DOWN, &interface->state); + set_bit(__FM10K_LINK_DOWN, interface->state); /* reset dglort_map back to no config */ hw->mac.dglort_map = FM10K_DGLORTMAP_NONE; @@ -1623,10 +1623,10 @@ void fm10k_up(struct fm10k_intfc *interface) hw->mac.ops.update_int_moderator(hw); /* enable statistics capture again */ - clear_bit(__FM10K_UPDATING_STATS, &interface->state); + clear_bit(__FM10K_UPDATING_STATS, interface->state); /* clear down bit to indicate we are ready to go */ - clear_bit(__FM10K_DOWN, &interface->state); + clear_bit(__FM10K_DOWN, interface->state); /* enable polling cleanups */ fm10k_napi_enable_all(interface); @@ -1660,7 +1660,7 @@ void fm10k_down(struct fm10k_intfc *interface) int err, i = 0, count = 0; /* signal that we are down to the interrupt handler and service task */ - if (test_and_set_bit(__FM10K_DOWN, &interface->state)) + if (test_and_set_bit(__FM10K_DOWN, interface->state)) return; /* call carrier off first to avoid false dev_watchdog timeouts */ @@ -1680,7 +1680,7 @@ void fm10k_down(struct fm10k_intfc *interface) fm10k_update_stats(interface); /* prevent updating statistics while we're down */ - while (test_and_set_bit(__FM10K_UPDATING_STATS, &interface->state)) + while (test_and_set_bit(__FM10K_UPDATING_STATS, interface->state)) usleep_range(1000, 2000); /* skip waiting for TX DMA if we lost PCIe link */ @@ -1849,8 +1849,8 @@ static int fm10k_sw_init(struct fm10k_intfc *interface, memcpy(interface->rssrk, rss_key, sizeof(rss_key)); /* Start off interface as being down */ - set_bit(__FM10K_DOWN, &interface->state); - set_bit(__FM10K_UPDATING_STATS, &interface->state); + set_bit(__FM10K_DOWN, interface->state); + set_bit(__FM10K_UPDATING_STATS, interface->state); return 0; } @@ -2027,7 +2027,7 @@ static int fm10k_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * must ensure it is disabled since we haven't yet requested the timer * or work item. */ - set_bit(__FM10K_SERVICE_DISABLE, &interface->state); + set_bit(__FM10K_SERVICE_DISABLE, interface->state); err = fm10k_mbx_request_irq(interface); if (err) @@ -2068,7 +2068,7 @@ static int fm10k_probe(struct pci_dev *pdev, const struct pci_device_id *ent) fm10k_iov_configure(pdev, 0); /* clear the service task disable bit to allow service task to start */ - clear_bit(__FM10K_SERVICE_DISABLE, &interface->state); + clear_bit(__FM10K_SERVICE_DISABLE, interface->state); return 0; @@ -2106,7 +2106,7 @@ static void fm10k_remove(struct pci_dev *pdev) del_timer_sync(&interface->service_timer); - set_bit(__FM10K_SERVICE_DISABLE, &interface->state); + set_bit(__FM10K_SERVICE_DISABLE, interface->state); cancel_work_sync(&interface->service_task); /* free netdev, this may bounce the interrupts due to setup_tc */ @@ -2145,7 +2145,7 @@ static void fm10k_prepare_suspend(struct fm10k_intfc *interface) * stopped. We stop the watchdog task until after we resume software * activity. */ - set_bit(__FM10K_SERVICE_DISABLE, &interface->state); + set_bit(__FM10K_SERVICE_DISABLE, interface->state); cancel_work_sync(&interface->service_task); fm10k_prepare_for_reset(interface); @@ -2171,10 +2171,10 @@ static int fm10k_handle_resume(struct fm10k_intfc *interface) /* force link to stay down for a second to prevent link flutter */ interface->link_down_event = jiffies + (HZ); - set_bit(__FM10K_LINK_DOWN, &interface->state); + set_bit(__FM10K_LINK_DOWN, interface->state); /* clear the service task disable bit to allow service task to start */ - clear_bit(__FM10K_SERVICE_DISABLE, &interface->state); + clear_bit(__FM10K_SERVICE_DISABLE, interface->state); fm10k_service_event_schedule(interface); return err; -- cgit From b4fd8ffc11c0686d04fb83d3c25572ea502efed9 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 12 Jan 2017 15:59:40 -0800 Subject: fm10k: allow service task to reschedule itself If some code path executes fm10k_service_event_schedule(), it is guaranteed that we only queue the service task once, since we use __FM10K_SERVICE_SCHED flag. Unfortunately this has a side effect that if a service request occurs while we are currently running the watchdog, it is possible that we will fail to notice the request and ignore it until the next time the request occurs. This can cause problems with pf/vf mailbox communication and other service event tasks. To avoid this, introduce a FM10K_SERVICE_REQUEST bit. When we successfully schedule (and set the _SCHED bit) the service task, we will clear this bit. However, if we are unable to currently schedule the service event, we just set the new SERVICE_REQUEST bit. Finally, after the service event completes, we will re-schedule if the request bit has been set. This should ensure that we do not miss any service event schedules, since we will re-schedule it once the currently running task finishes. This means that for each request, we will always schedule the service task to run at least once in full after the request came in. This will avoid timing issues that can occur with the service event scheduling. We do pay a cost in re-running many tasks, but all the service event tasks use either flags to avoid duplicate work, or are tolerant of being run multiple times. Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k.h | 1 + drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index b496300d0268..689c413b7782 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -272,6 +272,7 @@ enum fm10k_state_t { __FM10K_RESETTING, __FM10K_DOWN, __FM10K_SERVICE_SCHED, + __FM10K_SERVICE_REQUEST, __FM10K_SERVICE_DISABLE, __FM10K_MBX_LOCK, __FM10K_LINK_DOWN, diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 2a54372975f6..c62d1884f75b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -94,8 +94,12 @@ static int fm10k_hw_ready(struct fm10k_intfc *interface) void fm10k_service_event_schedule(struct fm10k_intfc *interface) { if (!test_bit(__FM10K_SERVICE_DISABLE, interface->state) && - !test_and_set_bit(__FM10K_SERVICE_SCHED, interface->state)) + !test_and_set_bit(__FM10K_SERVICE_SCHED, interface->state)) { + clear_bit(__FM10K_SERVICE_REQUEST, interface->state); queue_work(fm10k_workqueue, &interface->service_task); + } else { + set_bit(__FM10K_SERVICE_REQUEST, interface->state); + } } static void fm10k_service_event_complete(struct fm10k_intfc *interface) @@ -105,6 +109,13 @@ static void fm10k_service_event_complete(struct fm10k_intfc *interface) /* flush memory to make sure state is correct before next watchog */ smp_mb__before_atomic(); clear_bit(__FM10K_SERVICE_SCHED, interface->state); + + /* If a service event was requested since we started, immediately + * re-schedule now. This ensures we don't drop a request until the + * next timer event. + */ + if (test_bit(__FM10K_SERVICE_REQUEST, interface->state)) + fm10k_service_event_schedule(interface); } /** -- cgit From 02957703ca31a93689f1ea418aa7dd66d6a23221 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 12 Jan 2017 15:59:41 -0800 Subject: fm10k: update function header comment for fm10k_get_stats64 Re-word the comment to avoid stating that we return a value for this void function. Additionally, there is no need to mention older kernels, since this is the upstream kernel. Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 344a392f7a76..70cd121d465f 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1115,8 +1115,8 @@ void fm10k_reset_rx_state(struct fm10k_intfc *interface) * @netdev: network interface device structure * @stats: storage space for 64bit statistics * - * Returns 64bit statistics, for use in the ndo_get_stats64 callback. This - * function replaces fm10k_get_stats for kernels which support it. + * Obtain 64bit statistics in a way that is safe for both 32bit and 64bit + * architectures. */ static void fm10k_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) -- cgit From 16b1889f8bf655a9f4facfeff2979007f5e63974 Mon Sep 17 00:00:00 2001 From: Ngai-Mint Kwan Date: Thu, 12 Jan 2017 15:59:42 -0800 Subject: fm10k: disable receive queue when configuring ring Write to RXQCTL register to disable the receive queue when configuring the RX ring. Signed-off-by: Ngai-Mint Kwan Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index c62d1884f75b..3e26d27ad213 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -754,6 +754,7 @@ static void fm10k_configure_rx_ring(struct fm10k_intfc *interface, /* disable queue to avoid issues while updating state */ rxqctl = fm10k_read_reg(hw, FM10K_RXQCTL(reg_idx)); rxqctl &= ~FM10K_RXQCTL_ENABLE; + fm10k_write_reg(hw, FM10K_RXQCTL(reg_idx), rxqctl); fm10k_write_flush(hw); /* possible poll here to verify ring resources have been cleaned */ -- cgit From 7d4fe0d123c318ad4282c9ac731b3383d983bf24 Mon Sep 17 00:00:00 2001 From: Ngai-Mint Kwan Date: Mon, 6 Feb 2017 14:21:13 -0800 Subject: fm10k: do not enqueue mailbox when host not ready Interfaces will reset whenever the TX mailbox FIFO has become full. This occurs more frequently whenever the IES API application is not running to process and clear the messages in the FIFO. Thus, this could lead to situations where the interface would enter an infinite reset loop. That is: if the interface is trying to synchronize a huge number of unicast and multicast entries with the IES API application, the TX mailbox FIFO will become full and the interface resets. Once the interface exits reset, it'll try to synchronize the unicast and multicast entries again. Ergo, this creates an infinite loop. Other actions such as multiple mulitcast mode or up/down transitions will fill the TX mailbox FIFO and induce the interface to reset. To correct these situations, check if the interface's "host_ready" flag is enabled before enqueuing any messages to the TX mailbox FIFO. This check will be conducted by a function call. Lastly, this issue mainly affects the PF and, thus, the VF is exempt. Signed-off-by: Ngai-Mint Kwan Signed-off-by: Jacob Keller Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 105 ++++++++++++++++++------ 1 file changed, 80 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 70cd121d465f..24f2f6f86f5a 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -737,6 +737,23 @@ static void fm10k_tx_timeout(struct net_device *netdev) } } +/** + * fm10k_host_mbx_ready - Check PF interface's mailbox readiness + * @interface: board private structure + * + * This function checks if the PF interface's mailbox is ready before queueing + * mailbox messages for transmission. This will prevent filling the TX mailbox + * queue when the receiver is not ready. VF interfaces are exempt from this + * check since it will block all PF-VF mailbox messages from being sent from + * the VF to the PF at initialization. + **/ +static bool fm10k_host_mbx_ready(struct fm10k_intfc *interface) +{ + struct fm10k_hw *hw = &interface->hw; + + return (hw->mac.type == fm10k_mac_vf || interface->host_ready); +} + static int fm10k_uc_vlan_unsync(struct net_device *netdev, const unsigned char *uc_addr) { @@ -745,12 +762,15 @@ static int fm10k_uc_vlan_unsync(struct net_device *netdev, u16 glort = interface->glort; u16 vid = interface->vid; bool set = !!(vid / VLAN_N_VID); - int err; + int err = -EHOSTDOWN; /* drop any leading bits on the VLAN ID */ vid &= VLAN_N_VID - 1; - err = hw->mac.ops.update_uc_addr(hw, glort, uc_addr, vid, set, 0); + if (fm10k_host_mbx_ready(interface)) + err = hw->mac.ops.update_uc_addr(hw, glort, uc_addr, + vid, set, 0); + if (err) return err; @@ -766,12 +786,14 @@ static int fm10k_mc_vlan_unsync(struct net_device *netdev, u16 glort = interface->glort; u16 vid = interface->vid; bool set = !!(vid / VLAN_N_VID); - int err; + int err = -EHOSTDOWN; /* drop any leading bits on the VLAN ID */ vid &= VLAN_N_VID - 1; - err = hw->mac.ops.update_mc_addr(hw, glort, mc_addr, vid, set); + if (fm10k_host_mbx_ready(interface)) + err = hw->mac.ops.update_mc_addr(hw, glort, mc_addr, vid, set); + if (err) return err; @@ -834,9 +856,13 @@ static int fm10k_update_vid(struct net_device *netdev, u16 vid, bool set) goto err_out; } - /* update our base MAC address */ - err = hw->mac.ops.update_uc_addr(hw, interface->glort, hw->mac.addr, - vid, set, 0); + /* update our base MAC address if host's mailbox is ready */ + if (fm10k_host_mbx_ready(interface)) + err = hw->mac.ops.update_uc_addr(hw, interface->glort, + hw->mac.addr, vid, set, 0); + else + err = -EHOSTDOWN; + if (err) goto err_out; @@ -907,12 +933,15 @@ static int __fm10k_uc_sync(struct net_device *dev, if (!is_valid_ether_addr(addr)) return -EADDRNOTAVAIL; - /* update table with current entries */ + /* update table with current entries if host's mailbox is ready */ + if (!fm10k_host_mbx_ready(interface)) + return -EHOSTDOWN; + for (vid = hw->mac.default_vid ? fm10k_find_next_vlan(interface, 0) : 1; vid < VLAN_N_VID; vid = fm10k_find_next_vlan(interface, vid)) { err = hw->mac.ops.update_uc_addr(hw, glort, addr, - vid, sync, 0); + vid, sync, 0); if (err) return err; } @@ -970,7 +999,10 @@ static int __fm10k_mc_sync(struct net_device *dev, struct fm10k_hw *hw = &interface->hw; u16 vid, glort = interface->glort; - /* update table with current entries */ + /* update table with current entries if host's mailbox is ready */ + if (!fm10k_host_mbx_ready(interface)) + return 0; + for (vid = hw->mac.default_vid ? fm10k_find_next_vlan(interface, 0) : 1; vid < VLAN_N_VID; vid = fm10k_find_next_vlan(interface, vid)) { @@ -1018,8 +1050,10 @@ static void fm10k_set_rx_mode(struct net_device *dev) if (interface->xcast_mode == FM10K_XCAST_MODE_PROMISC) fm10k_clear_unused_vlans(interface); - /* update xcast mode */ - hw->mac.ops.update_xcast_mode(hw, interface->glort, xcast_mode); + /* update xcast mode if host's mailbox is ready */ + if (fm10k_host_mbx_ready(interface)) + hw->mac.ops.update_xcast_mode(hw, interface->glort, + xcast_mode); /* record updated xcast mode state */ interface->xcast_mode = xcast_mode; @@ -1054,8 +1088,10 @@ void fm10k_restore_rx_state(struct fm10k_intfc *interface) fm10k_mbx_lock(interface); - /* Enable logical port */ - hw->mac.ops.update_lport_state(hw, glort, interface->glort_count, true); + /* Enable logical port if host's mailbox is ready */ + if (fm10k_host_mbx_ready(interface)) + hw->mac.ops.update_lport_state(hw, glort, + interface->glort_count, true); /* update VLAN table */ hw->mac.ops.update_vlan(hw, FM10K_VLAN_ALL, 0, @@ -1069,12 +1105,18 @@ void fm10k_restore_rx_state(struct fm10k_intfc *interface) vid < VLAN_N_VID; vid = fm10k_find_next_vlan(interface, vid)) { hw->mac.ops.update_vlan(hw, vid, 0, true); - hw->mac.ops.update_uc_addr(hw, glort, hw->mac.addr, - vid, true, 0); + + /* Update unicast entries if host's mailbox is ready */ + if (fm10k_host_mbx_ready(interface)) + hw->mac.ops.update_uc_addr(hw, glort, hw->mac.addr, + vid, true, 0); } - /* update xcast mode before synchronizing addresses */ - hw->mac.ops.update_xcast_mode(hw, glort, xcast_mode); + /* update xcast mode before synchronizing addresses if host's mailbox + * is ready + */ + if (fm10k_host_mbx_ready(interface)) + hw->mac.ops.update_xcast_mode(hw, glort, xcast_mode); /* synchronize all of the addresses */ __dev_uc_sync(netdev, fm10k_uc_sync, fm10k_uc_unsync); @@ -1096,9 +1138,12 @@ void fm10k_reset_rx_state(struct fm10k_intfc *interface) fm10k_mbx_lock(interface); - /* clear the logical port state on lower device */ - hw->mac.ops.update_lport_state(hw, interface->glort, - interface->glort_count, false); + /* clear the logical port state on lower device if host's mailbox is + * ready + */ + if (fm10k_host_mbx_ready(interface)) + hw->mac.ops.update_lport_state(hw, interface->glort, + interface->glort_count, false); fm10k_mbx_unlock(interface); @@ -1319,8 +1364,13 @@ static void *fm10k_dfwd_add_station(struct net_device *dev, fm10k_mbx_lock(interface); glort = l2_accel->dglort + 1 + i; - hw->mac.ops.update_xcast_mode(hw, glort, FM10K_XCAST_MODE_MULTI); - hw->mac.ops.update_uc_addr(hw, glort, sdev->dev_addr, 0, true, 0); + + if (fm10k_host_mbx_ready(interface)) { + hw->mac.ops.update_xcast_mode(hw, glort, + FM10K_XCAST_MODE_MULTI); + hw->mac.ops.update_uc_addr(hw, glort, sdev->dev_addr, + 0, true, 0); + } fm10k_mbx_unlock(interface); @@ -1354,8 +1404,13 @@ static void fm10k_dfwd_del_station(struct net_device *dev, void *priv) fm10k_mbx_lock(interface); glort = l2_accel->dglort + 1 + i; - hw->mac.ops.update_xcast_mode(hw, glort, FM10K_XCAST_MODE_NONE); - hw->mac.ops.update_uc_addr(hw, glort, sdev->dev_addr, 0, false, 0); + + if (fm10k_host_mbx_ready(interface)) { + hw->mac.ops.update_xcast_mode(hw, glort, + FM10K_XCAST_MODE_NONE); + hw->mac.ops.update_uc_addr(hw, glort, sdev->dev_addr, + 0, false, 0); + } fm10k_mbx_unlock(interface); -- cgit From 3a92789af0d625caff1e0bf5701aec8edf0d057d Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 6 Apr 2017 10:11:56 +0100 Subject: rxrpc: Use negative error codes in rxrpc_call struct Use negative error codes in struct rxrpc_call::error because that's what the kernel normally deals with and to make the code consistent. We only turn them positive when transcribing into a cmsg for userspace recvmsg. Signed-off-by: David Howells --- fs/afs/rxrpc.c | 12 ++++++------ net/rxrpc/call_accept.c | 6 +++--- net/rxrpc/call_event.c | 2 +- net/rxrpc/call_object.c | 4 ++-- net/rxrpc/conn_event.c | 8 ++++---- net/rxrpc/input.c | 6 +++--- net/rxrpc/peer_event.c | 2 +- net/rxrpc/recvmsg.c | 6 +++--- net/rxrpc/rxkad.c | 18 +++++++++--------- net/rxrpc/sendmsg.c | 2 +- 10 files changed, 33 insertions(+), 33 deletions(-) diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 8f76b13d5549..d5990eb160bd 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -419,7 +419,7 @@ error_do_abort: call->state = AFS_CALL_COMPLETE; if (ret != -ECONNABORTED) { rxrpc_kernel_abort_call(afs_socket, rxcall, RX_USER_ABORT, - -ret, "KSD"); + ret, "KSD"); } else { abort_code = 0; offset = 0; @@ -478,12 +478,12 @@ static void afs_deliver_to_call(struct afs_call *call) case -ENOTCONN: abort_code = RX_CALL_DEAD; rxrpc_kernel_abort_call(afs_socket, call->rxcall, - abort_code, -ret, "KNC"); + abort_code, ret, "KNC"); goto save_error; case -ENOTSUPP: abort_code = RXGEN_OPCODE; rxrpc_kernel_abort_call(afs_socket, call->rxcall, - abort_code, -ret, "KIV"); + abort_code, ret, "KIV"); goto save_error; case -ENODATA: case -EBADMSG: @@ -493,7 +493,7 @@ static void afs_deliver_to_call(struct afs_call *call) if (call->state != AFS_CALL_AWAIT_REPLY) abort_code = RXGEN_SS_UNMARSHAL; rxrpc_kernel_abort_call(afs_socket, call->rxcall, - abort_code, EBADMSG, "KUM"); + abort_code, -EBADMSG, "KUM"); goto save_error; } } @@ -754,7 +754,7 @@ void afs_send_empty_reply(struct afs_call *call) case -ENOMEM: _debug("oom"); rxrpc_kernel_abort_call(afs_socket, call->rxcall, - RX_USER_ABORT, ENOMEM, "KOO"); + RX_USER_ABORT, -ENOMEM, "KOO"); default: _leave(" [error]"); return; @@ -792,7 +792,7 @@ void afs_send_simple_reply(struct afs_call *call, const void *buf, size_t len) if (n == -ENOMEM) { _debug("oom"); rxrpc_kernel_abort_call(afs_socket, call->rxcall, - RX_USER_ABORT, ENOMEM, "KOO"); + RX_USER_ABORT, -ENOMEM, "KOO"); } _leave(" [error]"); } diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 0ed181f53f32..1752fcf8e8f1 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -413,11 +413,11 @@ found_service: case RXRPC_CONN_REMOTELY_ABORTED: rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, - conn->remote_abort, ECONNABORTED); + conn->remote_abort, -ECONNABORTED); break; case RXRPC_CONN_LOCALLY_ABORTED: rxrpc_abort_call("CON", call, sp->hdr.seq, - conn->local_abort, ECONNABORTED); + conn->local_abort, -ECONNABORTED); break; default: BUG(); @@ -600,7 +600,7 @@ int rxrpc_reject_call(struct rxrpc_sock *rx) write_lock_bh(&call->state_lock); switch (call->state) { case RXRPC_CALL_SERVER_ACCEPTING: - __rxrpc_abort_call("REJ", call, 1, RX_USER_ABORT, ECONNABORTED); + __rxrpc_abort_call("REJ", call, 1, RX_USER_ABORT, -ECONNABORTED); abort = true; /* fall through */ case RXRPC_CALL_COMPLETE: diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 97a17ada4431..7a77844aab16 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -386,7 +386,7 @@ recheck_state: now = ktime_get_real(); if (ktime_before(call->expire_at, now)) { - rxrpc_abort_call("EXP", call, 0, RX_CALL_TIMEOUT, ETIME); + rxrpc_abort_call("EXP", call, 0, RX_CALL_TIMEOUT, -ETIME); set_bit(RXRPC_CALL_EV_ABORT, &call->events); goto recheck_state; } diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index d79cd36987a9..47f7f4205653 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -486,7 +486,7 @@ void rxrpc_release_calls_on_socket(struct rxrpc_sock *rx) call = list_entry(rx->to_be_accepted.next, struct rxrpc_call, accept_link); list_del(&call->accept_link); - rxrpc_abort_call("SKR", call, 0, RX_CALL_DEAD, ECONNRESET); + rxrpc_abort_call("SKR", call, 0, RX_CALL_DEAD, -ECONNRESET); rxrpc_put_call(call, rxrpc_call_put); } @@ -494,7 +494,7 @@ void rxrpc_release_calls_on_socket(struct rxrpc_sock *rx) call = list_entry(rx->sock_calls.next, struct rxrpc_call, sock_link); rxrpc_get_call(call, rxrpc_call_got); - rxrpc_abort_call("SKT", call, 0, RX_CALL_DEAD, ECONNRESET); + rxrpc_abort_call("SKT", call, 0, RX_CALL_DEAD, -ECONNRESET); rxrpc_send_abort_packet(call); rxrpc_release_call(rx, call); rxrpc_put_call(call, rxrpc_call_put); diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index b099b64366f3..f9d1d9cc86d8 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -168,7 +168,7 @@ static void rxrpc_abort_calls(struct rxrpc_connection *conn, * generate a connection-level abort */ static int rxrpc_abort_connection(struct rxrpc_connection *conn, - u32 error, u32 abort_code) + int error, u32 abort_code) { struct rxrpc_wire_header whdr; struct msghdr msg; @@ -288,7 +288,7 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, conn->state = RXRPC_CONN_REMOTELY_ABORTED; rxrpc_abort_calls(conn, RXRPC_CALL_REMOTELY_ABORTED, - abort_code, ECONNABORTED); + abort_code, -ECONNABORTED); return -ECONNABORTED; case RXRPC_PACKET_TYPE_CHALLENGE: @@ -370,7 +370,7 @@ static void rxrpc_secure_connection(struct rxrpc_connection *conn) abort: _debug("abort %d, %d", ret, abort_code); - rxrpc_abort_connection(conn, -ret, abort_code); + rxrpc_abort_connection(conn, ret, abort_code); _leave(" [aborted]"); } @@ -419,7 +419,7 @@ requeue_and_leave: goto out; protocol_error: - if (rxrpc_abort_connection(conn, -ret, abort_code) < 0) + if (rxrpc_abort_connection(conn, ret, abort_code) < 0) goto requeue_and_leave; rxrpc_free_skb(skb, rxrpc_skb_rx_freed); _leave(" [EPROTO]"); diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 18b2ad8be8e2..3a7754c87aef 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -30,7 +30,7 @@ static void rxrpc_proto_abort(const char *why, struct rxrpc_call *call, rxrpc_seq_t seq) { - if (rxrpc_abort_call(why, call, seq, RX_PROTOCOL_ERROR, EBADMSG)) { + if (rxrpc_abort_call(why, call, seq, RX_PROTOCOL_ERROR, -EBADMSG)) { set_bit(RXRPC_CALL_EV_ABORT, &call->events); rxrpc_queue_call(call); } @@ -895,7 +895,7 @@ static void rxrpc_input_abort(struct rxrpc_call *call, struct sk_buff *skb) _proto("Rx ABORT %%%u { %x }", sp->hdr.serial, abort_code); if (rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, - abort_code, ECONNABORTED)) + abort_code, -ECONNABORTED)) rxrpc_notify_socket(call); } @@ -958,7 +958,7 @@ static void rxrpc_input_implicit_end_call(struct rxrpc_connection *conn, case RXRPC_CALL_COMPLETE: break; default: - if (rxrpc_abort_call("IMP", call, 0, RX_CALL_DEAD, ESHUTDOWN)) { + if (rxrpc_abort_call("IMP", call, 0, RX_CALL_DEAD, -ESHUTDOWN)) { set_bit(RXRPC_CALL_EV_ABORT, &call->events); rxrpc_queue_call(call); } diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index bf13b8470c9a..1ed9c0c2e94f 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -296,7 +296,7 @@ void rxrpc_peer_error_distributor(struct work_struct *work) hlist_del_init(&call->error_link); rxrpc_see_call(call); - if (rxrpc_set_call_completion(call, compl, 0, error)) + if (rxrpc_set_call_completion(call, compl, 0, -error)) rxrpc_notify_socket(call); } diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index 3e2f1a8e9c5b..ad1a815b9706 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -83,11 +83,11 @@ static int rxrpc_recvmsg_term(struct rxrpc_call *call, struct msghdr *msg) ret = put_cmsg(msg, SOL_RXRPC, RXRPC_ABORT, 4, &tmp); break; case RXRPC_CALL_NETWORK_ERROR: - tmp = call->error; + tmp = -call->error; ret = put_cmsg(msg, SOL_RXRPC, RXRPC_NET_ERROR, 4, &tmp); break; case RXRPC_CALL_LOCAL_ERROR: - tmp = call->error; + tmp = -call->error; ret = put_cmsg(msg, SOL_RXRPC, RXRPC_LOCAL_ERROR, 4, &tmp); break; default: @@ -689,7 +689,7 @@ excess_data: goto out; call_complete: *_abort = call->abort_code; - ret = -call->error; + ret = call->error; if (call->completion == RXRPC_CALL_SUCCEEDED) { ret = 1; if (size > 0) diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 4374e7b9c7bf..2d5838a3dc24 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -330,7 +330,7 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, _enter(""); if (len < 8) { - rxrpc_abort_call("V1H", call, seq, RXKADSEALEDINCON, EPROTO); + rxrpc_abort_call("V1H", call, seq, RXKADSEALEDINCON, -EPROTO); goto protocol_error; } @@ -355,7 +355,7 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, /* Extract the decrypted packet length */ if (skb_copy_bits(skb, offset, &sechdr, sizeof(sechdr)) < 0) { - rxrpc_abort_call("XV1", call, seq, RXKADDATALEN, EPROTO); + rxrpc_abort_call("XV1", call, seq, RXKADDATALEN, -EPROTO); goto protocol_error; } offset += sizeof(sechdr); @@ -368,12 +368,12 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, check ^= seq ^ call->call_id; check &= 0xffff; if (check != 0) { - rxrpc_abort_call("V1C", call, seq, RXKADSEALEDINCON, EPROTO); + rxrpc_abort_call("V1C", call, seq, RXKADSEALEDINCON, -EPROTO); goto protocol_error; } if (data_size > len) { - rxrpc_abort_call("V1L", call, seq, RXKADDATALEN, EPROTO); + rxrpc_abort_call("V1L", call, seq, RXKADDATALEN, -EPROTO); goto protocol_error; } @@ -410,7 +410,7 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, _enter(",{%d}", skb->len); if (len < 8) { - rxrpc_abort_call("V2H", call, seq, RXKADSEALEDINCON, EPROTO); + rxrpc_abort_call("V2H", call, seq, RXKADSEALEDINCON, -EPROTO); goto protocol_error; } @@ -445,7 +445,7 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, /* Extract the decrypted packet length */ if (skb_copy_bits(skb, offset, &sechdr, sizeof(sechdr)) < 0) { - rxrpc_abort_call("XV2", call, seq, RXKADDATALEN, EPROTO); + rxrpc_abort_call("XV2", call, seq, RXKADDATALEN, -EPROTO); goto protocol_error; } offset += sizeof(sechdr); @@ -458,12 +458,12 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, check ^= seq ^ call->call_id; check &= 0xffff; if (check != 0) { - rxrpc_abort_call("V2C", call, seq, RXKADSEALEDINCON, EPROTO); + rxrpc_abort_call("V2C", call, seq, RXKADSEALEDINCON, -EPROTO); goto protocol_error; } if (data_size > len) { - rxrpc_abort_call("V2L", call, seq, RXKADDATALEN, EPROTO); + rxrpc_abort_call("V2L", call, seq, RXKADDATALEN, -EPROTO); goto protocol_error; } @@ -522,7 +522,7 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb, cksum = 1; /* zero checksums are not permitted */ if (cksum != expected_cksum) { - rxrpc_abort_call("VCK", call, seq, RXKADSEALEDINCON, EPROTO); + rxrpc_abort_call("VCK", call, seq, RXKADSEALEDINCON, -EPROTO); rxrpc_send_abort_packet(call); _leave(" = -EPROTO [csum failed]"); return -EPROTO; diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index 97ab214ca411..601c0a3e31a2 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -556,7 +556,7 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len) ret = -ESHUTDOWN; } else if (cmd == RXRPC_CMD_SEND_ABORT) { ret = 0; - if (rxrpc_abort_call("CMD", call, 0, abort_code, ECONNABORTED)) + if (rxrpc_abort_call("CMD", call, 0, abort_code, -ECONNABORTED)) ret = rxrpc_send_abort_packet(call); } else if (cmd != RXRPC_CMD_SEND_DATA) { ret = -EINVAL; -- cgit From 84a4c09c38903a92ba670375efea5165949a465b Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 6 Apr 2017 10:11:59 +0100 Subject: rxrpc: Note a successfully aborted kernel operation Make rxrpc_kernel_abort_call() return an indication as to whether it actually aborted the operation or not so that kafs can trace the failure of the operation. Note that 'success' in this context means changing the state of the call, not necessarily successfully transmitting an ABORT packet. Signed-off-by: David Howells --- include/net/af_rxrpc.h | 2 +- net/rxrpc/sendmsg.c | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h index 1061a472a3e3..b5f5187f488c 100644 --- a/include/net/af_rxrpc.h +++ b/include/net/af_rxrpc.h @@ -39,7 +39,7 @@ int rxrpc_kernel_send_data(struct socket *, struct rxrpc_call *, struct msghdr *, size_t); int rxrpc_kernel_recv_data(struct socket *, struct rxrpc_call *, void *, size_t, size_t *, bool, u32 *); -void rxrpc_kernel_abort_call(struct socket *, struct rxrpc_call *, +bool rxrpc_kernel_abort_call(struct socket *, struct rxrpc_call *, u32, int, const char *); void rxrpc_kernel_end_call(struct socket *, struct rxrpc_call *); void rxrpc_kernel_get_peer(struct socket *, struct rxrpc_call *, diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index 601c0a3e31a2..e836fa00dc5a 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -642,20 +642,24 @@ EXPORT_SYMBOL(rxrpc_kernel_send_data); * @error: Local error value * @why: 3-char string indicating why. * - * Allow a kernel service to abort a call, if it's still in an abortable state. + * Allow a kernel service to abort a call, if it's still in an abortable state + * and return true if the call was aborted, false if it was already complete. */ -void rxrpc_kernel_abort_call(struct socket *sock, struct rxrpc_call *call, +bool rxrpc_kernel_abort_call(struct socket *sock, struct rxrpc_call *call, u32 abort_code, int error, const char *why) { + bool aborted; + _enter("{%d},%d,%d,%s", call->debug_id, abort_code, error, why); mutex_lock(&call->user_mutex); - if (rxrpc_abort_call(why, call, 0, abort_code, error)) + aborted = rxrpc_abort_call(why, call, 0, abort_code, error); + if (aborted) rxrpc_send_abort_packet(call); mutex_unlock(&call->user_mutex); - _leave(""); + return aborted; } EXPORT_SYMBOL(rxrpc_kernel_abort_call); -- cgit From ef68622da9cc0c4e5202f90093a3a5314e41e9e9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 6 Apr 2017 10:11:59 +0100 Subject: rxrpc: Handle temporary errors better in rxkad security In the rxkad security module, when we encounter a temporary error (such as ENOMEM) from which we could conceivably recover, don't abort the connection, but rather permit retransmission of the relevant packets to induce a retry. Note that I'm leaving some places that could be merged together to insert tracing in the next patch. Signed-off-by; David Howells --- net/rxrpc/rxkad.c | 78 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 2d5838a3dc24..988903f1dc80 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -759,16 +759,14 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, _enter("{%d,%x}", conn->debug_id, key_serial(conn->params.key)); - if (!conn->params.key) { - _leave(" = -EPROTO [no key]"); - return -EPROTO; - } + abort_code = RX_PROTOCOL_ERROR; + if (!conn->params.key) + goto protocol_error; + abort_code = RXKADEXPIRED; ret = key_validate(conn->params.key); - if (ret < 0) { - *_abort_code = RXKADEXPIRED; - return ret; - } + if (ret < 0) + goto other_error; abort_code = RXKADPACKETSHORT; if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), @@ -787,8 +785,9 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, goto protocol_error; abort_code = RXKADLEVELFAIL; + ret = -EACCES; if (conn->params.security_level < min_level) - goto protocol_error; + goto other_error; token = conn->params.key->payload.data[0]; @@ -815,9 +814,10 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, return rxkad_send_response(conn, &sp->hdr, &resp, token->kad); protocol_error: + ret = -EPROTO; +other_error: *_abort_code = abort_code; - _leave(" = -EPROTO [%d]", abort_code); - return -EPROTO; + return ret; } /* @@ -848,10 +848,10 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, switch (ret) { case -EKEYEXPIRED: *_abort_code = RXKADEXPIRED; - goto error; + goto other_error; default: *_abort_code = RXKADNOAUTH; - goto error; + goto other_error; } } @@ -860,13 +860,11 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, memcpy(&iv, &conn->server_key->payload.data[2], sizeof(iv)); + ret = -ENOMEM; req = skcipher_request_alloc(conn->server_key->payload.data[0], GFP_NOFS); - if (!req) { - *_abort_code = RXKADNOAUTH; - ret = -ENOMEM; - goto error; - } + if (!req) + goto temporary_error; sg_init_one(&sg[0], ticket, ticket_len); skcipher_request_set_callback(req, 0, NULL, NULL); @@ -943,13 +941,13 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, if (issue > now) { *_abort_code = RXKADNOAUTH; ret = -EKEYREJECTED; - goto error; + goto other_error; } if (issue < now - life) { *_abort_code = RXKADEXPIRED; ret = -EKEYEXPIRED; - goto error; + goto other_error; } *_expiry = issue + life; @@ -961,16 +959,15 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, /* get the service instance name */ name = Z(INST_SZ); _debug("KIV SINST: %s", name); - - ret = 0; -error: - _leave(" = %d", ret); - return ret; + return 0; bad_ticket: *_abort_code = RXKADBADTICKET; - ret = -EBADMSG; - goto error; + ret = -EPROTO; +other_error: + return ret; +temporary_error: + return ret; } /* @@ -1054,9 +1051,10 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, goto protocol_error; /* extract the kerberos ticket and decrypt and decode it */ + ret = -ENOMEM; ticket = kmalloc(ticket_len, GFP_NOFS); if (!ticket) - return -ENOMEM; + goto temporary_error; abort_code = RXKADPACKETSHORT; if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), @@ -1064,12 +1062,9 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, goto protocol_error_free; ret = rxkad_decrypt_ticket(conn, ticket, ticket_len, &session_key, - &expiry, &abort_code); - if (ret < 0) { - *_abort_code = abort_code; - kfree(ticket); - return ret; - } + &expiry, _abort_code); + if (ret < 0) + goto temporary_error_free; /* use the session key from inside the ticket to decrypt the * response */ @@ -1123,10 +1118,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, * this the connection security can be handled in exactly the same way * as for a client connection */ ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno); - if (ret < 0) { - kfree(ticket); - return ret; - } + if (ret < 0) + goto temporary_error_free; kfree(ticket); _leave(" = 0"); @@ -1140,6 +1133,15 @@ protocol_error: *_abort_code = abort_code; _leave(" = -EPROTO [%d]", abort_code); return -EPROTO; + +temporary_error_free: + kfree(ticket); +temporary_error: + /* Ignore the response packet if we got a temporary error such as + * ENOMEM. We just want to send the challenge again. Note that we + * also come out this way if the ticket decryption fails. + */ + return ret; } /* -- cgit From fb46f6ee10e78799ea38cf97e4bf52cb70f1228a Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 6 Apr 2017 10:12:00 +0100 Subject: rxrpc: Trace protocol errors in received packets Add a tracepoint (rxrpc_rx_proto) to record protocol errors in received packets. The following changes are made: (1) Add a function, __rxrpc_abort_eproto(), to note a protocol error on a call and mark the call aborted. This is wrapped by rxrpc_abort_eproto() that makes the why string usable in trace. (2) Add trace_rxrpc_rx_proto() or rxrpc_abort_eproto() to protocol error generation points, replacing rxrpc_abort_call() with the latter. (3) Only send an abort packet in rxkad_verify_packet*() if we actually managed to abort the call. Note that a trace event is also emitted if a kernel user (e.g. afs) tries to send data through a call when it's not in the transmission phase, though it's not technically a receive event. Signed-off-by: David Howells --- include/trace/events/rxrpc.h | 24 ++++++++++ net/rxrpc/ar-internal.h | 19 ++++++++ net/rxrpc/conn_event.c | 9 ++-- net/rxrpc/input.c | 5 +- net/rxrpc/insecure.c | 10 +++- net/rxrpc/recvmsg.c | 2 + net/rxrpc/rxkad.c | 106 +++++++++++++++++++++++++++++-------------- net/rxrpc/sendmsg.c | 3 +- 8 files changed, 138 insertions(+), 40 deletions(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 39123c06a566..626af97863e8 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -1087,6 +1087,30 @@ TRACE_EVENT(rxrpc_improper_term, __entry->abort_code) ); +TRACE_EVENT(rxrpc_rx_eproto, + TP_PROTO(struct rxrpc_call *call, rxrpc_serial_t serial, + const char *why), + + TP_ARGS(call, serial, why), + + TP_STRUCT__entry( + __field(struct rxrpc_call *, call ) + __field(rxrpc_serial_t, serial ) + __field(const char *, why ) + ), + + TP_fast_assign( + __entry->call = call; + __entry->serial = serial; + __entry->why = why; + ), + + TP_printk("c=%p EPROTO %08x %s", + __entry->call, + __entry->serial, + __entry->why) + ); + #endif /* _TRACE_RXRPC_H */ /* This part must be outside protection */ diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 26a7b1db1361..7486926e60a8 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -739,6 +739,25 @@ static inline bool rxrpc_abort_call(const char *why, struct rxrpc_call *call, return ret; } +/* + * Abort a call due to a protocol error. + */ +static inline bool __rxrpc_abort_eproto(struct rxrpc_call *call, + struct sk_buff *skb, + const char *eproto_why, + const char *why, + u32 abort_code) +{ + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + + trace_rxrpc_rx_eproto(call, sp->hdr.serial, eproto_why); + return rxrpc_abort_call(why, call, sp->hdr.seq, abort_code, -EPROTO); +} + +#define rxrpc_abort_eproto(call, skb, eproto_why, abort_why, abort_code) \ + __rxrpc_abort_eproto((call), (skb), tracepoint_string(eproto_why), \ + (abort_why), (abort_code)) + /* * conn_client.c */ diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index f9d1d9cc86d8..46babcf82ce8 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -281,8 +281,11 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, case RXRPC_PACKET_TYPE_ABORT: if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), - &wtmp, sizeof(wtmp)) < 0) + &wtmp, sizeof(wtmp)) < 0) { + trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, + tracepoint_string("bad_abort")); return -EPROTO; + } abort_code = ntohl(wtmp); _proto("Rx ABORT %%%u { ac=%d }", sp->hdr.serial, abort_code); @@ -327,7 +330,8 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, return 0; default: - _leave(" = -EPROTO [%u]", sp->hdr.type); + trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, + tracepoint_string("bad_conn_pkt")); return -EPROTO; } } @@ -422,6 +426,5 @@ protocol_error: if (rxrpc_abort_connection(conn, ret, abort_code) < 0) goto requeue_and_leave; rxrpc_free_skb(skb, rxrpc_skb_rx_freed); - _leave(" [EPROTO]"); goto out; } diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 3a7754c87aef..3685dbe05a8f 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -1017,8 +1017,11 @@ int rxrpc_extract_header(struct rxrpc_skb_priv *sp, struct sk_buff *skb) struct rxrpc_wire_header whdr; /* dig out the RxRPC connection details */ - if (skb_copy_bits(skb, 0, &whdr, sizeof(whdr)) < 0) + if (skb_copy_bits(skb, 0, &whdr, sizeof(whdr)) < 0) { + trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, + tracepoint_string("bad_hdr")); return -EBADMSG; + } memset(sp, 0, sizeof(*sp)); sp->hdr.epoch = ntohl(whdr.epoch); diff --git a/net/rxrpc/insecure.c b/net/rxrpc/insecure.c index 7d4375e557e6..af276f173b10 100644 --- a/net/rxrpc/insecure.c +++ b/net/rxrpc/insecure.c @@ -46,7 +46,10 @@ static int none_respond_to_challenge(struct rxrpc_connection *conn, struct sk_buff *skb, u32 *_abort_code) { - *_abort_code = RX_PROTOCOL_ERROR; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + + trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, + tracepoint_string("chall_none")); return -EPROTO; } @@ -54,7 +57,10 @@ static int none_verify_response(struct rxrpc_connection *conn, struct sk_buff *skb, u32 *_abort_code) { - *_abort_code = RX_PROTOCOL_ERROR; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + + trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, + tracepoint_string("resp_none")); return -EPROTO; } diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index ad1a815b9706..f9caf3b77509 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -682,9 +682,11 @@ out: return ret; short_data: + trace_rxrpc_rx_eproto(call, 0, tracepoint_string("short_data")); ret = -EBADMSG; goto out; excess_data: + trace_rxrpc_rx_eproto(call, 0, tracepoint_string("excess_data")); ret = -EMSGSIZE; goto out; call_complete: diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 988903f1dc80..1bb9b2ccc267 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -148,15 +148,13 @@ static int rxkad_secure_packet_auth(const struct rxrpc_call *call, u32 data_size, void *sechdr) { - struct rxrpc_skb_priv *sp; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); SKCIPHER_REQUEST_ON_STACK(req, call->conn->cipher); struct rxkad_level1_hdr hdr; struct rxrpc_crypt iv; struct scatterlist sg; u16 check; - sp = rxrpc_skb(skb); - _enter(""); check = sp->hdr.seq ^ call->call_id; @@ -323,6 +321,7 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, struct rxrpc_crypt iv; struct scatterlist sg[16]; struct sk_buff *trailer; + bool aborted; u32 data_size, buf; u16 check; int nsg; @@ -330,7 +329,8 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, _enter(""); if (len < 8) { - rxrpc_abort_call("V1H", call, seq, RXKADSEALEDINCON, -EPROTO); + aborted = rxrpc_abort_eproto(call, skb, "rxkad_1_hdr", "V1H", + RXKADSEALEDINCON); goto protocol_error; } @@ -355,7 +355,8 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, /* Extract the decrypted packet length */ if (skb_copy_bits(skb, offset, &sechdr, sizeof(sechdr)) < 0) { - rxrpc_abort_call("XV1", call, seq, RXKADDATALEN, -EPROTO); + aborted = rxrpc_abort_eproto(call, skb, "rxkad_1_len", "XV1", + RXKADDATALEN); goto protocol_error; } offset += sizeof(sechdr); @@ -368,12 +369,14 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, check ^= seq ^ call->call_id; check &= 0xffff; if (check != 0) { - rxrpc_abort_call("V1C", call, seq, RXKADSEALEDINCON, -EPROTO); + aborted = rxrpc_abort_eproto(call, skb, "rxkad_1_check", "V1C", + RXKADSEALEDINCON); goto protocol_error; } if (data_size > len) { - rxrpc_abort_call("V1L", call, seq, RXKADDATALEN, -EPROTO); + aborted = rxrpc_abort_eproto(call, skb, "rxkad_1_datalen", "V1L", + RXKADDATALEN); goto protocol_error; } @@ -381,8 +384,8 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, return 0; protocol_error: - rxrpc_send_abort_packet(call); - _leave(" = -EPROTO"); + if (aborted) + rxrpc_send_abort_packet(call); return -EPROTO; nomem: @@ -403,6 +406,7 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, struct rxrpc_crypt iv; struct scatterlist _sg[4], *sg; struct sk_buff *trailer; + bool aborted; u32 data_size, buf; u16 check; int nsg; @@ -410,7 +414,8 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, _enter(",{%d}", skb->len); if (len < 8) { - rxrpc_abort_call("V2H", call, seq, RXKADSEALEDINCON, -EPROTO); + aborted = rxrpc_abort_eproto(call, skb, "rxkad_2_hdr", "V2H", + RXKADSEALEDINCON); goto protocol_error; } @@ -445,7 +450,8 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, /* Extract the decrypted packet length */ if (skb_copy_bits(skb, offset, &sechdr, sizeof(sechdr)) < 0) { - rxrpc_abort_call("XV2", call, seq, RXKADDATALEN, -EPROTO); + aborted = rxrpc_abort_eproto(call, skb, "rxkad_2_len", "XV2", + RXKADDATALEN); goto protocol_error; } offset += sizeof(sechdr); @@ -458,12 +464,14 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, check ^= seq ^ call->call_id; check &= 0xffff; if (check != 0) { - rxrpc_abort_call("V2C", call, seq, RXKADSEALEDINCON, -EPROTO); + aborted = rxrpc_abort_eproto(call, skb, "rxkad_2_check", "V2C", + RXKADSEALEDINCON); goto protocol_error; } if (data_size > len) { - rxrpc_abort_call("V2L", call, seq, RXKADDATALEN, -EPROTO); + aborted = rxrpc_abort_eproto(call, skb, "rxkad_2_datalen", "V2L", + RXKADDATALEN); goto protocol_error; } @@ -471,8 +479,8 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, return 0; protocol_error: - rxrpc_send_abort_packet(call); - _leave(" = -EPROTO"); + if (aborted) + rxrpc_send_abort_packet(call); return -EPROTO; nomem: @@ -491,6 +499,7 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb, SKCIPHER_REQUEST_ON_STACK(req, call->conn->cipher); struct rxrpc_crypt iv; struct scatterlist sg; + bool aborted; u16 cksum; u32 x, y; @@ -522,10 +531,9 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb, cksum = 1; /* zero checksums are not permitted */ if (cksum != expected_cksum) { - rxrpc_abort_call("VCK", call, seq, RXKADSEALEDINCON, -EPROTO); - rxrpc_send_abort_packet(call); - _leave(" = -EPROTO [csum failed]"); - return -EPROTO; + aborted = rxrpc_abort_eproto(call, skb, "rxkad_csum", "VCK", + RXKADSEALEDINCON); + goto protocol_error; } switch (call->conn->params.security_level) { @@ -538,6 +546,11 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb, default: return -ENOANO; } + +protocol_error: + if (aborted) + rxrpc_send_abort_packet(call); + return -EPROTO; } /* @@ -754,11 +767,13 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, struct rxkad_response resp __attribute__((aligned(8))); /* must be aligned for crypto */ struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + const char *eproto; u32 version, nonce, min_level, abort_code; int ret; _enter("{%d,%x}", conn->debug_id, key_serial(conn->params.key)); + eproto = tracepoint_string("chall_no_key"); abort_code = RX_PROTOCOL_ERROR; if (!conn->params.key) goto protocol_error; @@ -768,6 +783,7 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, if (ret < 0) goto other_error; + eproto = tracepoint_string("chall_short"); abort_code = RXKADPACKETSHORT; if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), &challenge, sizeof(challenge)) < 0) @@ -780,6 +796,7 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, _proto("Rx CHALLENGE %%%u { v=%u n=%u ml=%u }", sp->hdr.serial, version, nonce, min_level); + eproto = tracepoint_string("chall_ver"); abort_code = RXKADINCONSISTENCY; if (version != RXKAD_VERSION) goto protocol_error; @@ -814,6 +831,7 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, return rxkad_send_response(conn, &sp->hdr, &resp, token->kad); protocol_error: + trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto); ret = -EPROTO; other_error: *_abort_code = abort_code; @@ -824,19 +842,23 @@ other_error: * decrypt the kerberos IV ticket in the response */ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, + struct sk_buff *skb, void *ticket, size_t ticket_len, struct rxrpc_crypt *_session_key, time_t *_expiry, u32 *_abort_code) { struct skcipher_request *req; + struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_crypt iv, key; struct scatterlist sg[1]; struct in_addr addr; unsigned int life; + const char *eproto; time_t issue, now; bool little_endian; int ret; + u32 abort_code; u8 *p, *q, *name, *end; _enter("{%d},{%x}", conn->debug_id, key_serial(conn->server_key)); @@ -847,10 +869,10 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, if (ret < 0) { switch (ret) { case -EKEYEXPIRED: - *_abort_code = RXKADEXPIRED; + abort_code = RXKADEXPIRED; goto other_error; default: - *_abort_code = RXKADNOAUTH; + abort_code = RXKADNOAUTH; goto other_error; } } @@ -875,11 +897,12 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, p = ticket; end = p + ticket_len; -#define Z(size) \ +#define Z(field) \ ({ \ u8 *__str = p; \ + eproto = tracepoint_string("rxkad_bad_"#field); \ q = memchr(p, 0, end - p); \ - if (!q || q - p > (size)) \ + if (!q || q - p > (field##_SZ)) \ goto bad_ticket; \ for (; p < q; p++) \ if (!isprint(*p)) \ @@ -894,17 +917,18 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, p++; /* extract the authentication name */ - name = Z(ANAME_SZ); + name = Z(ANAME); _debug("KIV ANAME: %s", name); /* extract the principal's instance */ - name = Z(INST_SZ); + name = Z(INST); _debug("KIV INST : %s", name); /* extract the principal's authentication domain */ - name = Z(REALM_SZ); + name = Z(REALM); _debug("KIV REALM: %s", name); + eproto = tracepoint_string("rxkad_bad_len"); if (end - p < 4 + 8 + 4 + 2) goto bad_ticket; @@ -939,13 +963,13 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, /* check the ticket is in date */ if (issue > now) { - *_abort_code = RXKADNOAUTH; + abort_code = RXKADNOAUTH; ret = -EKEYREJECTED; goto other_error; } if (issue < now - life) { - *_abort_code = RXKADEXPIRED; + abort_code = RXKADEXPIRED; ret = -EKEYEXPIRED; goto other_error; } @@ -953,18 +977,20 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, *_expiry = issue + life; /* get the service name */ - name = Z(SNAME_SZ); + name = Z(SNAME); _debug("KIV SNAME: %s", name); /* get the service instance name */ - name = Z(INST_SZ); + name = Z(INST); _debug("KIV SINST: %s", name); return 0; bad_ticket: - *_abort_code = RXKADBADTICKET; + trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto); + abort_code = RXKADBADTICKET; ret = -EPROTO; other_error: + *_abort_code = abort_code; return ret; temporary_error: return ret; @@ -1017,6 +1043,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, __attribute__((aligned(8))); /* must be aligned for crypto */ struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_crypt session_key; + const char *eproto; time_t expiry; void *ticket; u32 abort_code, version, kvno, ticket_len, level; @@ -1025,6 +1052,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, _enter("{%d,%x}", conn->debug_id, key_serial(conn->server_key)); + eproto = tracepoint_string("rxkad_rsp_short"); abort_code = RXKADPACKETSHORT; if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), &response, sizeof(response)) < 0) @@ -1038,14 +1066,17 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, _proto("Rx RESPONSE %%%u { v=%u kv=%u tl=%u }", sp->hdr.serial, version, kvno, ticket_len); + eproto = tracepoint_string("rxkad_rsp_ver"); abort_code = RXKADINCONSISTENCY; if (version != RXKAD_VERSION) goto protocol_error; + eproto = tracepoint_string("rxkad_rsp_tktlen"); abort_code = RXKADTICKETLEN; if (ticket_len < 4 || ticket_len > MAXKRB5TICKETLEN) goto protocol_error; + eproto = tracepoint_string("rxkad_rsp_unkkey"); abort_code = RXKADUNKNOWNKEY; if (kvno >= RXKAD_TKT_TYPE_KERBEROS_V5) goto protocol_error; @@ -1056,12 +1087,13 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, if (!ticket) goto temporary_error; + eproto = tracepoint_string("rxkad_tkt_short"); abort_code = RXKADPACKETSHORT; if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), ticket, ticket_len) < 0) goto protocol_error_free; - ret = rxkad_decrypt_ticket(conn, ticket, ticket_len, &session_key, + ret = rxkad_decrypt_ticket(conn, skb, ticket, ticket_len, &session_key, &expiry, _abort_code); if (ret < 0) goto temporary_error_free; @@ -1070,6 +1102,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, * response */ rxkad_decrypt_response(conn, &response, &session_key); + eproto = tracepoint_string("rxkad_rsp_param"); abort_code = RXKADSEALEDINCON; if (ntohl(response.encrypted.epoch) != conn->proto.epoch) goto protocol_error_free; @@ -1080,6 +1113,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, csum = response.encrypted.checksum; response.encrypted.checksum = 0; rxkad_calc_response_checksum(&response); + eproto = tracepoint_string("rxkad_rsp_csum"); if (response.encrypted.checksum != csum) goto protocol_error_free; @@ -1088,11 +1122,15 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, struct rxrpc_call *call; u32 call_id = ntohl(response.encrypted.call_id[i]); + eproto = tracepoint_string("rxkad_rsp_callid"); if (call_id > INT_MAX) goto protocol_error_unlock; + eproto = tracepoint_string("rxkad_rsp_callctr"); if (call_id < conn->channels[i].call_counter) goto protocol_error_unlock; + + eproto = tracepoint_string("rxkad_rsp_callst"); if (call_id > conn->channels[i].call_counter) { call = rcu_dereference_protected( conn->channels[i].call, @@ -1104,10 +1142,12 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, } spin_unlock(&conn->channel_lock); + eproto = tracepoint_string("rxkad_rsp_seq"); abort_code = RXKADOUTOFSEQUENCE; if (ntohl(response.encrypted.inc_nonce) != conn->security_nonce + 1) goto protocol_error_free; + eproto = tracepoint_string("rxkad_rsp_level"); abort_code = RXKADLEVELFAIL; level = ntohl(response.encrypted.level); if (level > RXRPC_SECURITY_ENCRYPT) @@ -1130,8 +1170,8 @@ protocol_error_unlock: protocol_error_free: kfree(ticket); protocol_error: + trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto); *_abort_code = abort_code; - _leave(" = -EPROTO [%d]", abort_code); return -EPROTO; temporary_error_free: diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index e836fa00dc5a..96ffa5d5733b 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -623,7 +623,8 @@ int rxrpc_kernel_send_data(struct socket *sock, struct rxrpc_call *call, read_unlock_bh(&call->state_lock); break; default: - /* Request phase complete for this client call */ + /* Request phase complete for this client call */ + trace_rxrpc_rx_eproto(call, 0, tracepoint_string("late_send")); ret = -EPROTO; break; } -- cgit From 005ede286f1b801be21d9667d6080bca79ef2a26 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 6 Apr 2017 10:12:00 +0100 Subject: rxrpc: Trace received aborts Add a tracepoint (rxrpc_rx_abort) to record received aborts. Signed-off-by: David Howells --- include/trace/events/rxrpc.h | 24 ++++++++++++++++++++++++ net/rxrpc/input.c | 4 +++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 626af97863e8..85e0148c88a8 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -683,6 +683,30 @@ TRACE_EVENT(rxrpc_rx_ack, __entry->n_acks) ); +TRACE_EVENT(rxrpc_rx_abort, + TP_PROTO(struct rxrpc_call *call, rxrpc_serial_t serial, + u32 abort_code), + + TP_ARGS(call, serial, abort_code), + + TP_STRUCT__entry( + __field(struct rxrpc_call *, call ) + __field(rxrpc_serial_t, serial ) + __field(u32, abort_code ) + ), + + TP_fast_assign( + __entry->call = call; + __entry->serial = serial; + __entry->abort_code = abort_code; + ), + + TP_printk("c=%p ABORT %08x ac=%d", + __entry->call, + __entry->serial, + __entry->abort_code) + ); + TRACE_EVENT(rxrpc_tx_data, TP_PROTO(struct rxrpc_call *call, rxrpc_seq_t seq, rxrpc_serial_t serial, u8 flags, bool retrans, bool lose), diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 3685dbe05a8f..241e989597f2 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -877,7 +877,7 @@ static void rxrpc_input_ackall(struct rxrpc_call *call, struct sk_buff *skb) } /* - * Process an ABORT packet. + * Process an ABORT packet directed at a call. */ static void rxrpc_input_abort(struct rxrpc_call *call, struct sk_buff *skb) { @@ -892,6 +892,8 @@ static void rxrpc_input_abort(struct rxrpc_call *call, struct sk_buff *skb) &wtmp, sizeof(wtmp)) >= 0) abort_code = ntohl(wtmp); + trace_rxrpc_rx_abort(call, sp->hdr.serial, abort_code); + _proto("Rx ABORT %%%u { %x }", sp->hdr.serial, abort_code); if (rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED, -- cgit From 740586d290cb43d941c10274f2b65976bd94dacd Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 6 Apr 2017 10:12:00 +0100 Subject: rxrpc: Trace changes in a call's receive window size Add a tracepoint (rxrpc_rx_rwind_change) to log changes in a call's receive window size as imposed by the peer through an ACK packet. Signed-off-by: David Howells --- include/trace/events/rxrpc.h | 27 +++++++++++++++++++++++++++ net/rxrpc/input.c | 2 ++ 2 files changed, 29 insertions(+) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 85e0148c88a8..15ba7387c243 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -707,6 +707,33 @@ TRACE_EVENT(rxrpc_rx_abort, __entry->abort_code) ); +TRACE_EVENT(rxrpc_rx_rwind_change, + TP_PROTO(struct rxrpc_call *call, rxrpc_serial_t serial, + u32 rwind, bool wake), + + TP_ARGS(call, serial, rwind, wake), + + TP_STRUCT__entry( + __field(struct rxrpc_call *, call ) + __field(rxrpc_serial_t, serial ) + __field(u32, rwind ) + __field(bool, wake ) + ), + + TP_fast_assign( + __entry->call = call; + __entry->serial = serial; + __entry->rwind = rwind; + __entry->wake = wake; + ), + + TP_printk("c=%p %08x rw=%u%s", + __entry->call, + __entry->serial, + __entry->rwind, + __entry->wake ? " wake" : "") + ); + TRACE_EVENT(rxrpc_tx_data, TP_PROTO(struct rxrpc_call *call, rxrpc_seq_t seq, rxrpc_serial_t serial, u8 flags, bool retrans, bool lose), diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index 241e989597f2..45dba732a3b4 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -665,6 +665,8 @@ static void rxrpc_input_ackinfo(struct rxrpc_call *call, struct sk_buff *skb, rwind = RXRPC_RXTX_BUFF_SIZE - 1; if (rwind > call->tx_winsize) wake = true; + trace_rxrpc_rx_rwind_change(call, sp->hdr.serial, + ntohl(ackinfo->rwind), wake); call->tx_winsize = rwind; } -- cgit From 89ca694806943728d5969982537acd824be8ce1e Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 6 Apr 2017 10:12:00 +0100 Subject: rxrpc: Trace client call connection Add a tracepoint (rxrpc_connect_call) to log the combination of rxrpc_call pointer, afs_call pointer/user data and wire call parameters to make it easier to match the tracebuffer contents to captured network packets. Signed-off-by: David Howells --- include/trace/events/rxrpc.h | 26 ++++++++++++++++++++++++++ net/rxrpc/conn_client.c | 1 + 2 files changed, 27 insertions(+) diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 15ba7387c243..29a3d53a4015 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -1162,6 +1162,32 @@ TRACE_EVENT(rxrpc_rx_eproto, __entry->why) ); +TRACE_EVENT(rxrpc_connect_call, + TP_PROTO(struct rxrpc_call *call), + + TP_ARGS(call), + + TP_STRUCT__entry( + __field(struct rxrpc_call *, call ) + __field(unsigned long, user_call_ID ) + __field(u32, cid ) + __field(u32, call_id ) + ), + + TP_fast_assign( + __entry->call = call; + __entry->user_call_ID = call->user_call_ID; + __entry->cid = call->cid; + __entry->call_id = call->call_id; + ), + + TP_printk("c=%p u=%p %08x:%08x", + __entry->call, + (void *)__entry->user_call_ID, + __entry->cid, + __entry->call_id) + ); + #endif /* _TRACE_RXRPC_H */ /* This part must be outside protection */ diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index c3be03e8d098..e8dea0d49e7f 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -550,6 +550,7 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn, call->cid = conn->proto.cid | channel; call->call_id = call_id; + trace_rxrpc_connect_call(call); _net("CONNECT call %08x:%08x as call %d on conn %d", call->cid, call->call_id, call->debug_id, conn->debug_id); -- cgit From f323d9546927a012cafbb7e503e6aa0e9fbff94b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 20 Mar 2017 18:10:29 +0100 Subject: netfilter: nf_tables: add nft_is_base_chain() helper This new helper function allows us to check if this is a basechain. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 5 +++++ net/netfilter/nf_tables_api.c | 30 +++++++++++++++--------------- net/netfilter/nf_tables_netdev.c | 2 +- net/netfilter/nft_compat.c | 11 ++++++----- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index f713a053f89d..028faec8fc27 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -911,6 +911,11 @@ static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chai return container_of(chain, struct nft_base_chain, chain); } +static inline bool nft_is_base_chain(const struct nft_chain *chain) +{ + return chain->flags & NFT_BASE_CHAIN; +} + int __nft_release_basechain(struct nft_ctx *ctx); unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 2d822d2fd830..bf52acfe4eff 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -144,7 +144,7 @@ static int nf_tables_register_hooks(struct net *net, unsigned int hook_nops) { if (table->flags & NFT_TABLE_F_DORMANT || - !(chain->flags & NFT_BASE_CHAIN)) + !nft_is_base_chain(chain)) return 0; return nf_register_net_hooks(net, nft_base_chain(chain)->ops, @@ -157,7 +157,7 @@ static void nf_tables_unregister_hooks(struct net *net, unsigned int hook_nops) { if (table->flags & NFT_TABLE_F_DORMANT || - !(chain->flags & NFT_BASE_CHAIN)) + !nft_is_base_chain(chain)) return; nf_unregister_net_hooks(net, nft_base_chain(chain)->ops, hook_nops); @@ -587,7 +587,7 @@ static void _nf_tables_table_disable(struct net *net, list_for_each_entry(chain, &table->chains, list) { if (!nft_is_active_next(net, chain)) continue; - if (!(chain->flags & NFT_BASE_CHAIN)) + if (!nft_is_base_chain(chain)) continue; if (cnt && i++ == cnt) @@ -608,7 +608,7 @@ static int nf_tables_table_enable(struct net *net, list_for_each_entry(chain, &table->chains, list) { if (!nft_is_active_next(net, chain)) continue; - if (!(chain->flags & NFT_BASE_CHAIN)) + if (!nft_is_base_chain(chain)) continue; err = nf_register_net_hooks(net, nft_base_chain(chain)->ops, @@ -1007,7 +1007,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net, if (nla_put_string(skb, NFTA_CHAIN_NAME, chain->name)) goto nla_put_failure; - if (chain->flags & NFT_BASE_CHAIN) { + if (nft_is_base_chain(chain)) { const struct nft_base_chain *basechain = nft_base_chain(chain); const struct nf_hook_ops *ops = &basechain->ops[0]; struct nlattr *nest; @@ -1226,7 +1226,7 @@ static void nf_tables_chain_destroy(struct nft_chain *chain) { BUG_ON(chain->use > 0); - if (chain->flags & NFT_BASE_CHAIN) { + if (nft_is_base_chain(chain)) { struct nft_base_chain *basechain = nft_base_chain(chain); module_put(basechain->type->owner); @@ -1364,8 +1364,8 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, } if (nla[NFTA_CHAIN_POLICY]) { - if ((chain != NULL && - !(chain->flags & NFT_BASE_CHAIN))) + if (chain != NULL && + !nft_is_base_chain(chain)) return -EOPNOTSUPP; if (chain == NULL && @@ -1396,7 +1396,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, struct nft_chain_hook hook; struct nf_hook_ops *ops; - if (!(chain->flags & NFT_BASE_CHAIN)) + if (!nft_is_base_chain(chain)) return -EBUSY; err = nft_chain_parse_hook(net, nla, afi, &hook, @@ -1433,7 +1433,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, } if (nla[NFTA_CHAIN_COUNTERS]) { - if (!(chain->flags & NFT_BASE_CHAIN)) + if (!nft_is_base_chain(chain)) return -EOPNOTSUPP; stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]); @@ -4708,7 +4708,7 @@ static void nft_chain_commit_update(struct nft_trans *trans) if (nft_trans_chain_name(trans)[0]) strcpy(trans->ctx.chain->name, nft_trans_chain_name(trans)); - if (!(trans->ctx.chain->flags & NFT_BASE_CHAIN)) + if (!nft_is_base_chain(trans->ctx.chain)) return; basechain = nft_base_chain(trans->ctx.chain); @@ -5022,7 +5022,7 @@ int nft_chain_validate_dependency(const struct nft_chain *chain, { const struct nft_base_chain *basechain; - if (chain->flags & NFT_BASE_CHAIN) { + if (nft_is_base_chain(chain)) { basechain = nft_base_chain(chain); if (basechain->type->type != type) return -EOPNOTSUPP; @@ -5036,7 +5036,7 @@ int nft_chain_validate_hooks(const struct nft_chain *chain, { struct nft_base_chain *basechain; - if (chain->flags & NFT_BASE_CHAIN) { + if (nft_is_base_chain(chain)) { basechain = nft_base_chain(chain); if ((1 << basechain->ops[0].hooknum) & hook_flags) @@ -5345,7 +5345,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, tb[NFTA_VERDICT_CHAIN], genmask); if (IS_ERR(chain)) return PTR_ERR(chain); - if (chain->flags & NFT_BASE_CHAIN) + if (nft_is_base_chain(chain)) return -EOPNOTSUPP; chain->use++; @@ -5518,7 +5518,7 @@ int __nft_release_basechain(struct nft_ctx *ctx) { struct nft_rule *rule, *nr; - BUG_ON(!(ctx->chain->flags & NFT_BASE_CHAIN)); + BUG_ON(!nft_is_base_chain(ctx->chain)); nf_tables_unregister_hooks(ctx->net, ctx->chain->table, ctx->chain, ctx->afi->nops); diff --git a/net/netfilter/nf_tables_netdev.c b/net/netfilter/nf_tables_netdev.c index 9e2ae424b640..403432988313 100644 --- a/net/netfilter/nf_tables_netdev.c +++ b/net/netfilter/nf_tables_netdev.c @@ -128,7 +128,7 @@ static int nf_tables_netdev_event(struct notifier_block *this, list_for_each_entry(table, &afi->tables, list) { ctx.table = table; list_for_each_entry_safe(chain, nr, &table->chains, list) { - if (!(chain->flags & NFT_BASE_CHAIN)) + if (!nft_is_base_chain(chain)) continue; ctx.chain = chain; diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index fab6bf3f955e..f443f9d22faf 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -42,7 +42,8 @@ static int nft_compat_chain_validate_dependency(const char *tablename, { const struct nft_base_chain *basechain; - if (!tablename || !(chain->flags & NFT_BASE_CHAIN)) + if (!tablename || + !nft_is_base_chain(chain)) return 0; basechain = nft_base_chain(chain); @@ -165,7 +166,7 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par, par->entryinfo = entry; par->target = target; par->targinfo = info; - if (ctx->chain->flags & NFT_BASE_CHAIN) { + if (nft_is_base_chain(ctx->chain)) { const struct nft_base_chain *basechain = nft_base_chain(ctx->chain); const struct nf_hook_ops *ops = &basechain->ops[0]; @@ -298,7 +299,7 @@ static int nft_target_validate(const struct nft_ctx *ctx, unsigned int hook_mask = 0; int ret; - if (ctx->chain->flags & NFT_BASE_CHAIN) { + if (nft_is_base_chain(ctx->chain)) { const struct nft_base_chain *basechain = nft_base_chain(ctx->chain); const struct nf_hook_ops *ops = &basechain->ops[0]; @@ -379,7 +380,7 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx, par->entryinfo = entry; par->match = match; par->matchinfo = info; - if (ctx->chain->flags & NFT_BASE_CHAIN) { + if (nft_is_base_chain(ctx->chain)) { const struct nft_base_chain *basechain = nft_base_chain(ctx->chain); const struct nf_hook_ops *ops = &basechain->ops[0]; @@ -477,7 +478,7 @@ static int nft_match_validate(const struct nft_ctx *ctx, unsigned int hook_mask = 0; int ret; - if (ctx->chain->flags & NFT_BASE_CHAIN) { + if (nft_is_base_chain(ctx->chain)) { const struct nft_base_chain *basechain = nft_base_chain(ctx->chain); const struct nf_hook_ops *ops = &basechain->ops[0]; -- cgit From 92f73221f9e9e143d242e3eca9c512dac969765e Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Fri, 24 Mar 2017 21:32:19 +0800 Subject: netfilter: expect: Make sure the max_expected limit is effective Because the type of expecting, the member of nf_conn_help, is u8, it would overflow after reach U8_MAX(255). So it doesn't work when we configure the max_expected exceeds 255 with expect policy. Now add the check for max_expected. Return the -EINVAL when it exceeds the limit. Signed-off-by: Gao Feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_expect.h | 1 + net/netfilter/nf_conntrack_helper.c | 3 +++ net/netfilter/nf_conntrack_irc.c | 6 ++++++ net/netfilter/nfnetlink_cthelper.c | 6 ++++++ 4 files changed, 16 insertions(+) diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index 65cc2cb005d9..e84df8d3bf37 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -73,6 +73,7 @@ struct nf_conntrack_expect_policy { }; #define NF_CT_EXPECT_CLASS_DEFAULT 0 +#define NF_CT_EXPECT_MAX_CNT 255 int nf_conntrack_expect_pernet_init(struct net *net); void nf_conntrack_expect_pernet_fini(struct net *net); diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 6dc44d9b4190..752a977e9eef 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -385,6 +385,9 @@ int nf_conntrack_helper_register(struct nf_conntrack_helper *me) BUG_ON(me->expect_class_max >= NF_CT_MAX_EXPECT_CLASSES); BUG_ON(strlen(me->name) > NF_CT_HELPER_NAME_LEN - 1); + if (me->expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT) + return -EINVAL; + mutex_lock(&nf_ct_helper_mutex); hlist_for_each_entry(cur, &nf_ct_helper_hash[h], hnode) { if (nf_ct_tuple_src_mask_cmp(&cur->tuple, &me->tuple, &mask)) { diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index 1972a149f958..1a5af4d4af2d 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -243,6 +243,12 @@ static int __init nf_conntrack_irc_init(void) return -EINVAL; } + if (max_dcc_channels > NF_CT_EXPECT_MAX_CNT) { + pr_err("max_dcc_channels must not be more than %u\n", + NF_CT_EXPECT_MAX_CNT); + return -EINVAL; + } + irc_exp_policy.max_expected = max_dcc_channels; irc_exp_policy.timeout = dcc_timeout; diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index d45558178da5..d5025cc25df3 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -150,6 +150,9 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy, nla_data(tb[NFCTH_POLICY_NAME]), NF_CT_HELPER_NAME_LEN); expect_policy->max_expected = ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX])); + if (expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT) + return -EINVAL; + expect_policy->timeout = ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_TIMEOUT])); @@ -290,6 +293,9 @@ nfnl_cthelper_update_policy_one(const struct nf_conntrack_expect_policy *policy, new_policy->max_expected = ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX])); + if (new_policy->max_expected > NF_CT_EXPECT_MAX_CNT) + return -EINVAL; + new_policy->timeout = ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_TIMEOUT])); -- cgit From ec0e3f01114ad327112432a4da8840eb22fed577 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Mon, 27 Mar 2017 10:31:26 +0800 Subject: netfilter: nf_ct_expect: Add nf_ct_remove_expect() When remove one expect, it needs three statements. And there are multiple duplicated codes in current code. So add one common function nf_ct_remove_expect to consolidate this. Signed-off-by: Gao Feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_expect.h | 1 + net/netfilter/nf_conntrack_expect.c | 32 ++++++++++++++--------------- net/netfilter/nf_conntrack_helper.c | 7 ++----- net/netfilter/nf_conntrack_sip.c | 4 +--- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index e84df8d3bf37..2ba54feaccd8 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -105,6 +105,7 @@ static inline void nf_ct_unlink_expect(struct nf_conntrack_expect *exp) void nf_ct_remove_expectations(struct nf_conn *ct); void nf_ct_unexpect_related(struct nf_conntrack_expect *exp); +bool nf_ct_remove_expect(struct nf_conntrack_expect *exp); /* Allocate space for an expectation: this is mandatory before calling nf_ct_expect_related. You will have to call put afterwards. */ diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index cb29e598605f..71d136469be0 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -103,6 +103,17 @@ nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple, nf_ct_zone_equal_any(i->master, zone); } +bool nf_ct_remove_expect(struct nf_conntrack_expect *exp) +{ + if (del_timer(&exp->timeout)) { + nf_ct_unlink_expect(exp); + nf_ct_expect_put(exp); + return true; + } + return false; +} +EXPORT_SYMBOL_GPL(nf_ct_remove_expect); + struct nf_conntrack_expect * __nf_ct_expect_find(struct net *net, const struct nf_conntrack_zone *zone, @@ -211,10 +222,7 @@ void nf_ct_remove_expectations(struct nf_conn *ct) spin_lock_bh(&nf_conntrack_expect_lock); hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { - if (del_timer(&exp->timeout)) { - nf_ct_unlink_expect(exp); - nf_ct_expect_put(exp); - } + nf_ct_remove_expect(exp); } spin_unlock_bh(&nf_conntrack_expect_lock); } @@ -255,10 +263,7 @@ static inline int expect_matches(const struct nf_conntrack_expect *a, void nf_ct_unexpect_related(struct nf_conntrack_expect *exp) { spin_lock_bh(&nf_conntrack_expect_lock); - if (del_timer(&exp->timeout)) { - nf_ct_unlink_expect(exp); - nf_ct_expect_put(exp); - } + nf_ct_remove_expect(exp); spin_unlock_bh(&nf_conntrack_expect_lock); } EXPORT_SYMBOL_GPL(nf_ct_unexpect_related); @@ -394,10 +399,8 @@ static void evict_oldest_expect(struct nf_conn *master, last = exp; } - if (last && del_timer(&last->timeout)) { - nf_ct_unlink_expect(last); - nf_ct_expect_put(last); - } + if (last) + nf_ct_remove_expect(last); } static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect) @@ -419,11 +422,8 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect) h = nf_ct_expect_dst_hash(net, &expect->tuple); hlist_for_each_entry_safe(i, next, &nf_ct_expect_hash[h], hnode) { if (expect_matches(i, expect)) { - if (del_timer(&i->timeout)) { - nf_ct_unlink_expect(i); - nf_ct_expect_put(i); + if (nf_ct_remove_expect(expect)) break; - } } else if (expect_clash(i, expect)) { ret = -EBUSY; goto out; diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 752a977e9eef..33ebb78649f8 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -451,11 +451,8 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) if ((rcu_dereference_protected( help->helper, lockdep_is_held(&nf_conntrack_expect_lock) - ) == me || exp->helper == me) && - del_timer(&exp->timeout)) { - nf_ct_unlink_expect(exp); - nf_ct_expect_put(exp); - } + ) == me || exp->helper == me)) + nf_ct_remove_expect(exp); } } spin_unlock_bh(&nf_conntrack_expect_lock); diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 0d17894798b5..91a9c97b7e9a 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -829,10 +829,8 @@ static void flush_expectations(struct nf_conn *ct, bool media) hlist_for_each_entry_safe(exp, next, &help->expectations, lnode) { if ((exp->class != SIP_EXPECT_SIGNALLING) ^ media) continue; - if (!del_timer(&exp->timeout)) + if (!nf_ct_remove_expect(exp)) continue; - nf_ct_unlink_expect(exp); - nf_ct_expect_put(exp); if (!media) break; } -- cgit From 89c0a361301f81d76be5ab77c3f5470b88792670 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Thu, 6 Apr 2017 12:20:26 -0700 Subject: selftests/bpf: fix merge conflict fix artifact of merge resolution Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/test_verifier.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 0963f8ffd25c..6178b65fee59 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -4904,12 +4904,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv, struct bpf_insn *prog = test->insns; int prog_len = probe_filter_length(prog); int prog_type = test->prog_type; -<<<<<<< HEAD int map_fds[MAX_NR_MAPS]; - int fd_prog, expected_ret; -======= - int fd_f1 = -1, fd_f2 = -1, fd_f3 = -1; ->>>>>>> ea6b1720ce25f92f7a17b2e0c2b653d20773d10a const char *expected_err; int i; -- cgit From b73b3cde0e8b77af0d3b3758cfa7774a50abd640 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 4 Apr 2017 18:16:57 -0700 Subject: net: usbnet: Remove unused driver_name variable With GCC 6.3, we can get the following warning: drivers/net/usb/usbnet.c:85:19: warning: 'driver_name' defined but not used [-Wunused-const-variable=] static const char driver_name [] = "usbnet"; ^~~~~~~~~~~ Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/usb/usbnet.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 9890656af735..1cc945cbeaa3 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -82,8 +82,6 @@ // randomly generated ethernet address static u8 node_id [ETH_ALEN]; -static const char driver_name [] = "usbnet"; - /* use ethtool to change the level for any given device */ static int msg_level = -1; module_param (msg_level, int, 0); -- cgit From bb54be589c7a8451a0504924703abdffeb06b79f Mon Sep 17 00:00:00 2001 From: Felix Manlunas Date: Tue, 4 Apr 2017 19:26:57 -0700 Subject: liquidio: fix Octeon core watchdog timeout false alarm Detection of watchdog timeout of Octeon cores is flawed and susceptible to false alarms. Refactor by removing the detection code, and in its place, leverage existing code that monitors for an indication from the NIC firmware that an Octeon core crashed; expand the meaning of the indication to "an Octeon core crashed or its watchdog timer expired". Detection of watchdog timeout is now delegated to an exception handler in the NIC firmware; this is free of false alarms. Also if there's an Octeon core crash or watchdog timeout: (1) Disable VF Ethernet links. (2) Decrement the module refcount by an amount equal to the number of active VFs of the NIC whose Octeon core crashed or had a watchdog timeout. The refcount will continue to reflect the active VFs of other liquidio NIC(s) (if present) whose Octeon cores are faultless. Item (2) is needed to avoid the case of not being able to unload the driver because the module refcount is stuck at some non-zero number. There is code that, in normal cases, decrements the refcount upon receiving a message from the firmware that a VF driver was unloaded. But in exceptional cases like an Octeon core crash or watchdog timeout, arrival of that particular message from the firmware might be unreliable. That normal case code is changed to not touch the refcount in the exceptional case to avoid contention (over the refcount) with the liquidio_watchdog kernel thread who will carry out item (2). Signed-off-by: Felix Manlunas Signed-off-by: Derek Chickles Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_main.c | 178 ++++++++++++--------- .../net/ethernet/cavium/liquidio/octeon_device.h | 2 + .../net/ethernet/cavium/liquidio/octeon_network.h | 4 - 3 files changed, 107 insertions(+), 77 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index a8426d3d05d0..fa673a1de24d 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -173,6 +173,8 @@ static int liquidio_stop(struct net_device *netdev); static void liquidio_remove(struct pci_dev *pdev); static int liquidio_probe(struct pci_dev *pdev, const struct pci_device_id *ent); +static int liquidio_set_vf_link_state(struct net_device *netdev, int vfidx, + int linkstate); static struct handshake handshake[MAX_OCTEON_DEVICES]; static struct completion first_stage; @@ -1199,97 +1201,122 @@ static int octeon_setup_interrupt(struct octeon_device *oct) return 0; } +static struct octeon_device *get_other_octeon_device(struct octeon_device *oct) +{ + struct octeon_device *other_oct; + + other_oct = lio_get_device(oct->octeon_id + 1); + + if (other_oct && other_oct->pci_dev) { + int oct_busnum, other_oct_busnum; + + oct_busnum = oct->pci_dev->bus->number; + other_oct_busnum = other_oct->pci_dev->bus->number; + + if (oct_busnum == other_oct_busnum) { + int oct_slot, other_oct_slot; + + oct_slot = PCI_SLOT(oct->pci_dev->devfn); + other_oct_slot = PCI_SLOT(other_oct->pci_dev->devfn); + + if (oct_slot == other_oct_slot) + return other_oct; + } + } + + return NULL; +} + +static void disable_all_vf_links(struct octeon_device *oct) +{ + struct net_device *netdev; + int max_vfs, vf, i; + + if (!oct) + return; + + max_vfs = oct->sriov_info.max_vfs; + + for (i = 0; i < oct->ifcount; i++) { + netdev = oct->props[i].netdev; + if (!netdev) + continue; + + for (vf = 0; vf < max_vfs; vf++) + liquidio_set_vf_link_state(netdev, vf, + IFLA_VF_LINK_STATE_DISABLE); + } +} + static int liquidio_watchdog(void *param) { - u64 wdog; - u16 mask_of_stuck_cores = 0; - u16 mask_of_crashed_cores = 0; - int core_num; - u8 core_is_stuck[LIO_MAX_CORES]; - u8 core_crashed[LIO_MAX_CORES]; + bool err_msg_was_printed[LIO_MAX_CORES]; + u16 mask_of_crashed_or_stuck_cores = 0; + bool all_vf_links_are_disabled = false; struct octeon_device *oct = param; + struct octeon_device *other_oct; +#ifdef CONFIG_MODULE_UNLOAD + long refcount, vfs_referencing_pf; + u64 vfs_mask1, vfs_mask2; +#endif + int core; - memset(core_is_stuck, 0, sizeof(core_is_stuck)); - memset(core_crashed, 0, sizeof(core_crashed)); + memset(err_msg_was_printed, 0, sizeof(err_msg_was_printed)); while (!kthread_should_stop()) { - mask_of_crashed_cores = + /* sleep for a couple of seconds so that we don't hog the CPU */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(2000)); + + mask_of_crashed_or_stuck_cores = (u16)octeon_read_csr64(oct, CN23XX_SLI_SCRATCH2); - for (core_num = 0; core_num < LIO_MAX_CORES; core_num++) { - if (!core_is_stuck[core_num]) { - wdog = lio_pci_readq(oct, CIU3_WDOG(core_num)); - - /* look at watchdog state field */ - wdog &= CIU3_WDOG_MASK; - if (wdog) { - /* this watchdog timer has expired */ - core_is_stuck[core_num] = - LIO_MONITOR_WDOG_EXPIRE; - mask_of_stuck_cores |= (1 << core_num); - } - } + if (!mask_of_crashed_or_stuck_cores) + continue; - if (!core_crashed[core_num]) - core_crashed[core_num] = - (mask_of_crashed_cores >> core_num) & 1; - } + WRITE_ONCE(oct->cores_crashed, true); + other_oct = get_other_octeon_device(oct); + if (other_oct) + WRITE_ONCE(other_oct->cores_crashed, true); - if (mask_of_stuck_cores) { - for (core_num = 0; core_num < LIO_MAX_CORES; - core_num++) { - if (core_is_stuck[core_num] == 1) { - dev_err(&oct->pci_dev->dev, - "ERROR: Octeon core %d is stuck!\n", - core_num); - /* 2 means we have printk'd an error - * so no need to repeat the same printk - */ - core_is_stuck[core_num] = - LIO_MONITOR_CORE_STUCK_MSGD; - } - } - } + for (core = 0; core < LIO_MAX_CORES; core++) { + bool core_crashed_or_got_stuck; - if (mask_of_crashed_cores) { - for (core_num = 0; core_num < LIO_MAX_CORES; - core_num++) { - if (core_crashed[core_num] == 1) { - dev_err(&oct->pci_dev->dev, - "ERROR: Octeon core %d crashed! See oct-fwdump for details.\n", - core_num); - /* 2 means we have printk'd an error - * so no need to repeat the same printk - */ - core_crashed[core_num] = - LIO_MONITOR_CORE_STUCK_MSGD; - } + core_crashed_or_got_stuck = + (mask_of_crashed_or_stuck_cores + >> core) & 1; + + if (core_crashed_or_got_stuck && + !err_msg_was_printed[core]) { + dev_err(&oct->pci_dev->dev, + "ERROR: Octeon core %d crashed or got stuck! See oct-fwdump for details.\n", + core); + err_msg_was_printed[core] = true; } } + + if (all_vf_links_are_disabled) + continue; + + disable_all_vf_links(oct); + disable_all_vf_links(other_oct); + all_vf_links_are_disabled = true; + #ifdef CONFIG_MODULE_UNLOAD - if (mask_of_stuck_cores || mask_of_crashed_cores) { - /* make module refcount=0 so that rmmod will work */ - long refcount; + vfs_mask1 = READ_ONCE(oct->sriov_info.vf_drv_loaded_mask); + vfs_mask2 = READ_ONCE(other_oct->sriov_info.vf_drv_loaded_mask); - refcount = module_refcount(THIS_MODULE); + vfs_referencing_pf = hweight64(vfs_mask1); + vfs_referencing_pf += hweight64(vfs_mask2); - while (refcount > 0) { + refcount = module_refcount(THIS_MODULE); + if (refcount >= vfs_referencing_pf) { + while (vfs_referencing_pf) { module_put(THIS_MODULE); - refcount = module_refcount(THIS_MODULE); - } - - /* compensate for and withstand an unlikely (but still - * possible) race condition - */ - while (refcount < 0) { - try_module_get(THIS_MODULE); - refcount = module_refcount(THIS_MODULE); + vfs_referencing_pf--; } } #endif - /* sleep for two seconds */ - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(2 * HZ); } return 0; @@ -4406,6 +4433,7 @@ octeon_recv_vf_drv_notice(struct octeon_recv_info *recv_info, void *buf) struct octeon_device *oct = (struct octeon_device *)buf; struct octeon_recv_pkt *recv_pkt = recv_info->recv_pkt; int i, notice, vf_idx; + bool cores_crashed; u64 *data, vf_num; notice = recv_pkt->rh.r.ossp; @@ -4416,19 +4444,23 @@ octeon_recv_vf_drv_notice(struct octeon_recv_info *recv_info, void *buf) octeon_swap_8B_data(&vf_num, 1); vf_idx = (int)vf_num - 1; + cores_crashed = READ_ONCE(oct->cores_crashed); + if (notice == VF_DRV_LOADED) { if (!(oct->sriov_info.vf_drv_loaded_mask & BIT_ULL(vf_idx))) { oct->sriov_info.vf_drv_loaded_mask |= BIT_ULL(vf_idx); dev_info(&oct->pci_dev->dev, "driver for VF%d was loaded\n", vf_idx); - try_module_get(THIS_MODULE); + if (!cores_crashed) + try_module_get(THIS_MODULE); } } else if (notice == VF_DRV_REMOVED) { if (oct->sriov_info.vf_drv_loaded_mask & BIT_ULL(vf_idx)) { oct->sriov_info.vf_drv_loaded_mask &= ~BIT_ULL(vf_idx); dev_info(&oct->pci_dev->dev, "driver for VF%d was removed\n", vf_idx); - module_put(THIS_MODULE); + if (!cores_crashed) + module_put(THIS_MODULE); } } else if (notice == VF_DRV_MACADDR_CHANGED) { u8 *b = (u8 *)&data[1]; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index dab35bfa4612..92f67de111aa 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -542,6 +542,8 @@ struct octeon_device { u32 rx_coalesce_usecs; u32 rx_max_coalesced_frames; u32 tx_max_coalesced_frames; + + bool cores_crashed; }; #define OCT_DRV_ONLINE 1 diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index 454ec0ca56ab..bf483932ff25 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -141,10 +141,6 @@ struct lio { #define LIO_SIZE (sizeof(struct lio)) #define GET_LIO(netdev) ((struct lio *)netdev_priv(netdev)) -#define CIU3_WDOG(c) (0x1010000020000ULL + ((c) << 3)) -#define CIU3_WDOG_MASK 12ULL -#define LIO_MONITOR_WDOG_EXPIRE 1 -#define LIO_MONITOR_CORE_STUCK_MSGD 2 #define LIO_MAX_CORES 12 /** -- cgit From 60b28a1167749c5fef257add4ebb1d7c1c1c6629 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 12:28:41 +1000 Subject: ftgmac100: Use netdev->irq instead of private copy There's a placeholder already for the irq, use it Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index ade6b3e4ed13..e1c3f4694b63 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -57,7 +57,6 @@ struct ftgmac100_descs { struct ftgmac100 { struct resource *res; void __iomem *base; - int irq; struct ftgmac100_descs *descs; dma_addr_t descs_dma_addr; @@ -1121,9 +1120,9 @@ static int ftgmac100_open(struct net_device *netdev) goto err_alloc; } - err = request_irq(priv->irq, ftgmac100_interrupt, 0, netdev->name, netdev); + err = request_irq(netdev->irq, ftgmac100_interrupt, 0, netdev->name, netdev); if (err) { - netdev_err(netdev, "failed to request irq %d\n", priv->irq); + netdev_err(netdev, "failed to request irq %d\n", netdev->irq); goto err_irq; } @@ -1170,7 +1169,7 @@ err_ncsi: netif_stop_queue(netdev); iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); err_hw: - free_irq(priv->irq, netdev); + free_irq(netdev->irq, netdev); err_irq: ftgmac100_free_buffers(priv); err_alloc: @@ -1196,7 +1195,7 @@ static int ftgmac100_stop(struct net_device *netdev) ncsi_stop_dev(priv->ndev); ftgmac100_stop_hw(priv); - free_irq(priv->irq, netdev); + free_irq(netdev->irq, netdev); ftgmac100_free_buffers(priv); return 0; @@ -1383,7 +1382,7 @@ static int ftgmac100_probe(struct platform_device *pdev) goto err_ioremap; } - priv->irq = irq; + netdev->irq = irq; /* MAC address from chip or random one */ ftgmac100_setup_mac(priv); @@ -1440,7 +1439,7 @@ static int ftgmac100_probe(struct platform_device *pdev) goto err_register_netdev; } - netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base); + netdev_info(netdev, "irq %d, mapped at %p\n", netdev->irq, priv->base); return 0; -- cgit From 3f6af0eede3051a84ffb6d3e2090098130568896 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 12:28:42 +1000 Subject: ftgmac100: Remove "banner" comments The divisions they represent are not particularily meaningful and things are going to be moving around with upcoming changes making these comments more a burden than anything else. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 42 -------------------------------- 1 file changed, 42 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index e1c3f4694b63..99aafcc33a0b 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -46,9 +46,6 @@ #define MAX_PKT_SIZE 1518 #define RX_BUF_SIZE PAGE_SIZE /* must be smaller than 0x3fff */ -/****************************************************************************** - * private data - *****************************************************************************/ struct ftgmac100_descs { struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES]; struct ftgmac100_txdes txdes[TX_QUEUE_ENTRIES]; @@ -88,9 +85,6 @@ struct ftgmac100 { static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, struct ftgmac100_rxdes *rxdes, gfp_t gfp); -/****************************************************************************** - * internal functions (hardware register access) - *****************************************************************************/ static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr) { iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR); @@ -245,9 +239,6 @@ static void ftgmac100_stop_hw(struct ftgmac100 *priv) iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR); } -/****************************************************************************** - * internal functions (receive descriptor) - *****************************************************************************/ static bool ftgmac100_rxdes_first_segment(struct ftgmac100_rxdes *rxdes) { return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FRS); @@ -372,9 +363,6 @@ static struct page *ftgmac100_rxdes_get_page(struct ftgmac100 *priv, return *ftgmac100_rxdes_page_slot(priv, rxdes); } -/****************************************************************************** - * internal functions (receive) - *****************************************************************************/ static int ftgmac100_next_rx_pointer(int pointer) { return (pointer + 1) & (RX_QUEUE_ENTRIES - 1); @@ -559,9 +547,6 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) return true; } -/****************************************************************************** - * internal functions (transmit descriptor) - *****************************************************************************/ static void ftgmac100_txdes_reset(const struct ftgmac100 *priv, struct ftgmac100_txdes *txdes) { @@ -655,9 +640,6 @@ static struct sk_buff *ftgmac100_txdes_get_skb(struct ftgmac100_txdes *txdes) return (struct sk_buff *)txdes->txdes2; } -/****************************************************************************** - * internal functions (transmit) - *****************************************************************************/ static int ftgmac100_next_tx_pointer(int pointer) { return (pointer + 1) & (TX_QUEUE_ENTRIES - 1); @@ -773,9 +755,6 @@ static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb, return NETDEV_TX_OK; } -/****************************************************************************** - * internal functions (buffer) - *****************************************************************************/ static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, struct ftgmac100_rxdes *rxdes, gfp_t gfp) { @@ -867,9 +846,6 @@ err: return -ENOMEM; } -/****************************************************************************** - * internal functions (mdio) - *****************************************************************************/ static void ftgmac100_adjust_link(struct net_device *netdev) { struct ftgmac100 *priv = netdev_priv(netdev); @@ -919,9 +895,6 @@ static int ftgmac100_mii_probe(struct ftgmac100 *priv) return 0; } -/****************************************************************************** - * struct mii_bus functions - *****************************************************************************/ static int ftgmac100_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) { struct net_device *netdev = bus->priv; @@ -993,9 +966,6 @@ static int ftgmac100_mdiobus_write(struct mii_bus *bus, int phy_addr, return -EIO; } -/****************************************************************************** - * struct ethtool_ops functions - *****************************************************************************/ static void ftgmac100_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { @@ -1011,9 +981,6 @@ static const struct ethtool_ops ftgmac100_ethtool_ops = { .set_link_ksettings = phy_ethtool_set_link_ksettings, }; -/****************************************************************************** - * interrupt handler - *****************************************************************************/ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) { struct net_device *netdev = dev_id; @@ -1031,9 +998,6 @@ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -/****************************************************************************** - * struct napi_struct functions - *****************************************************************************/ static int ftgmac100_poll(struct napi_struct *napi, int budget) { struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi); @@ -1105,9 +1069,6 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget) return rx; } -/****************************************************************************** - * struct net_device_ops functions - *****************************************************************************/ static int ftgmac100_open(struct net_device *netdev) { struct ftgmac100 *priv = netdev_priv(netdev); @@ -1320,9 +1281,6 @@ static void ftgmac100_ncsi_handler(struct ncsi_dev *nd) nd->link_up ? "up" : "down"); } -/****************************************************************************** - * struct platform_driver functions - *****************************************************************************/ static int ftgmac100_probe(struct platform_device *pdev) { struct resource *res; -- cgit From 831fb3388c1ecf192fcd9772c2de1697e9af5dda Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 12:28:43 +1000 Subject: ftgmac100: Reorder struct fields and comment Reorder the fields in struct ftgmac in slightly more logical groups. Will make more sense as I add/remove some. No code change. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 99aafcc33a0b..89148246a739 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -52,34 +52,39 @@ struct ftgmac100_descs { }; struct ftgmac100 { + /* Registers */ struct resource *res; void __iomem *base; struct ftgmac100_descs *descs; dma_addr_t descs_dma_addr; + /* Rx ring */ struct page *rx_pages[RX_QUEUE_ENTRIES]; - unsigned int rx_pointer; + u32 rxdes0_edorr_mask; + + /* Tx ring */ unsigned int tx_clean_pointer; unsigned int tx_pointer; unsigned int tx_pending; - + u32 txdes0_edotr_mask; spinlock_t tx_lock; + /* Component structures */ struct net_device *netdev; struct device *dev; struct ncsi_dev *ndev; struct napi_struct napi; - struct mii_bus *mii_bus; + + /* Link management */ int old_speed; - int int_mask_all; bool use_ncsi; - bool enabled; - u32 rxdes0_edorr_mask; - u32 txdes0_edotr_mask; + /* Misc */ + int int_mask_all; + bool enabled; }; static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, -- cgit From 8396e1cb0b9f7505b0cd56e26df35279cb3bc1cb Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 12:28:44 +1000 Subject: ftgmac100: Remove "enabled" flags It's not used in any meaningful way Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 89148246a739..283edb1a7f7a 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -84,7 +84,6 @@ struct ftgmac100 { /* Misc */ int int_mask_all; - bool enabled; }; static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, @@ -1126,8 +1125,6 @@ static int ftgmac100_open(struct net_device *netdev) goto err_ncsi; } - priv->enabled = true; - return 0; err_ncsi: @@ -1146,11 +1143,7 @@ static int ftgmac100_stop(struct net_device *netdev) { struct ftgmac100 *priv = netdev_priv(netdev); - if (!priv->enabled) - return 0; - /* disable all interrupts */ - priv->enabled = false; iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); netif_stop_queue(netdev); -- cgit From 51764777354664b20963359ad3b78e564d14e1a5 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 12:28:45 +1000 Subject: ftgmac100: Cleanup speed/duplex tracking and fix duplex config Keep track of both the current speed and duplex settings instead of only speed and properly apply the duplex setting to the HW. This reworks the adjust_link() function to also avoid trying to reconfigure the HW when there is no link and to display the link state to the user. Signed-off-by: Benjamin Herrenschmidt Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 52 +++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 283edb1a7f7a..69daf2a50573 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -79,7 +79,8 @@ struct ftgmac100 { struct mii_bus *mii_bus; /* Link management */ - int old_speed; + int cur_speed; + int cur_duplex; bool use_ncsi; /* Misc */ @@ -212,16 +213,15 @@ static void ftgmac100_init_hw(struct ftgmac100 *priv) FTGMAC100_MACCR_RXDMA_EN | \ FTGMAC100_MACCR_TXMAC_EN | \ FTGMAC100_MACCR_RXMAC_EN | \ - FTGMAC100_MACCR_FULLDUP | \ FTGMAC100_MACCR_CRC_APD | \ FTGMAC100_MACCR_RX_RUNT | \ FTGMAC100_MACCR_RX_BROADPKT) -static void ftgmac100_start_hw(struct ftgmac100 *priv, int speed) +static void ftgmac100_start_hw(struct ftgmac100 *priv) { int maccr = MACCR_ENABLE_ALL; - switch (speed) { + switch (priv->cur_speed) { default: case 10: break; @@ -235,6 +235,9 @@ static void ftgmac100_start_hw(struct ftgmac100 *priv, int speed) break; } + if (priv->cur_duplex == DUPLEX_FULL) + maccr |= FTGMAC100_MACCR_FULLDUP; + iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR); } @@ -854,12 +857,31 @@ static void ftgmac100_adjust_link(struct net_device *netdev) { struct ftgmac100 *priv = netdev_priv(netdev); struct phy_device *phydev = netdev->phydev; + int new_speed; int ier; - if (phydev->speed == priv->old_speed) + /* We store "no link" as speed 0 */ + if (!phydev->link) + new_speed = 0; + else + new_speed = phydev->speed; + + if (phydev->speed == priv->cur_speed && + phydev->duplex == priv->cur_duplex) return; - priv->old_speed = phydev->speed; + /* Print status if we have a link or we had one and just lost it, + * don't print otherwise. + */ + if (new_speed || priv->cur_speed) + phy_print_status(phydev); + + priv->cur_speed = new_speed; + priv->cur_duplex = phydev->duplex; + + /* Link is down, do nothing else */ + if (!new_speed) + return; ier = ioread32(priv->base + FTGMAC100_OFFSET_IER); @@ -871,7 +893,7 @@ static void ftgmac100_adjust_link(struct net_device *netdev) netif_start_queue(netdev); ftgmac100_init_hw(priv); - ftgmac100_start_hw(priv, phydev->speed); + ftgmac100_start_hw(priv); /* re-enable interrupts */ iowrite32(ier, priv->base + FTGMAC100_OFFSET_IER); @@ -1091,6 +1113,20 @@ static int ftgmac100_open(struct net_device *netdev) goto err_irq; } + /* When using NC-SI we force the speed to 100Mbit/s full duplex, + * + * Otherwise we leave it set to 0 (no link), the link + * message from the PHY layer will handle setting it up to + * something else if needed. + */ + if (priv->use_ncsi) { + priv->cur_duplex = DUPLEX_FULL; + priv->cur_speed = SPEED_100; + } else { + priv->cur_duplex = 0; + priv->cur_speed = 0; + } + priv->rx_pointer = 0; priv->tx_clean_pointer = 0; priv->tx_pointer = 0; @@ -1101,7 +1137,7 @@ static int ftgmac100_open(struct net_device *netdev) goto err_hw; ftgmac100_init_hw(priv); - ftgmac100_start_hw(priv, priv->use_ncsi ? 100 : 10); + ftgmac100_start_hw(priv); /* Clear stale interrupts */ status = ioread32(priv->base + FTGMAC100_OFFSET_ISR); -- cgit From 87d18757ec1677c594ada9b5882ab815893da1e2 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 12:28:46 +1000 Subject: ftgmac100: Split ring alloc, init and rx buffer alloc Currently, a single function is used to allocate the rings themselves, initialize them, populate the rx ring, and allocate the rx buffers. The same happens on free. This splits them into separate functions. This will be useful when properly implementing re-initialization on link changes and error handling when the rings will be repopulated but not freed. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 68 ++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 69daf2a50573..a533a7d5a5cc 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -794,6 +794,7 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) { int i; + /* Free all RX buffers */ for (i = 0; i < RX_QUEUE_ENTRIES; i++) { struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; struct page *page = ftgmac100_rxdes_get_page(priv, rxdes); @@ -806,6 +807,7 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) __free_page(page); } + /* Free all TX buffers */ for (i = 0; i < TX_QUEUE_ENTRIES; i++) { struct ftgmac100_txdes *txdes = &priv->descs->txdes[i]; struct sk_buff *skb = ftgmac100_txdes_get_skb(txdes); @@ -817,40 +819,54 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); kfree_skb(skb); } - - dma_free_coherent(priv->dev, sizeof(struct ftgmac100_descs), - priv->descs, priv->descs_dma_addr); } -static int ftgmac100_alloc_buffers(struct ftgmac100 *priv) +static void ftgmac100_free_rings(struct ftgmac100 *priv) { - int i; + /* Free descriptors */ + if (priv->descs) + dma_free_coherent(priv->dev, sizeof(struct ftgmac100_descs), + priv->descs, priv->descs_dma_addr); +} +static int ftgmac100_alloc_rings(struct ftgmac100 *priv) +{ + /* Allocate descriptors */ priv->descs = dma_zalloc_coherent(priv->dev, sizeof(struct ftgmac100_descs), &priv->descs_dma_addr, GFP_KERNEL); if (!priv->descs) return -ENOMEM; - /* initialize RX ring */ - ftgmac100_rxdes_set_end_of_ring(priv, - &priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]); + return 0; +} + +static void ftgmac100_init_rings(struct ftgmac100 *priv) +{ + int i; + + /* Initialize RX ring */ + for (i = 0; i < RX_QUEUE_ENTRIES; i++) + priv->descs->rxdes[i].rxdes0 = 0; + ftgmac100_rxdes_set_end_of_ring(priv, &priv->descs->rxdes[i - 1]); + + /* Initialize TX ring */ + for (i = 0; i < TX_QUEUE_ENTRIES; i++) + priv->descs->txdes[i].txdes0 = 0; + ftgmac100_txdes_set_end_of_ring(priv, &priv->descs->txdes[i -1]); +} + +static int ftgmac100_alloc_rx_buffers(struct ftgmac100 *priv) +{ + int i; for (i = 0; i < RX_QUEUE_ENTRIES; i++) { struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; if (ftgmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL)) - goto err; + return -ENOMEM; } - - /* initialize TX ring */ - ftgmac100_txdes_set_end_of_ring(priv, - &priv->descs->txdes[TX_QUEUE_ENTRIES - 1]); return 0; - -err: - ftgmac100_free_buffers(priv); - return -ENOMEM; } static void ftgmac100_adjust_link(struct net_device *netdev) @@ -1101,12 +1117,20 @@ static int ftgmac100_open(struct net_device *netdev) unsigned int status; int err; - err = ftgmac100_alloc_buffers(priv); + /* Allocate ring buffers */ + err = ftgmac100_alloc_rings(priv); if (err) { - netdev_err(netdev, "failed to allocate buffers\n"); - goto err_alloc; + netdev_err(netdev, "Failed to allocate descriptors\n"); + return err; } + /* Initialize the rings */ + ftgmac100_init_rings(priv); + + /* Allocate receive buffers */ + if (ftgmac100_alloc_rx_buffers(priv)) + goto err_alloc; + err = request_irq(netdev->irq, ftgmac100_interrupt, 0, netdev->name, netdev); if (err) { netdev_err(netdev, "failed to request irq %d\n", netdev->irq); @@ -1170,8 +1194,9 @@ err_ncsi: err_hw: free_irq(netdev->irq, netdev); err_irq: - ftgmac100_free_buffers(priv); err_alloc: + ftgmac100_free_buffers(priv); + ftgmac100_free_rings(priv); return err; } @@ -1192,6 +1217,7 @@ static int ftgmac100_stop(struct net_device *netdev) ftgmac100_stop_hw(priv); free_irq(netdev->irq, netdev); ftgmac100_free_buffers(priv); + ftgmac100_free_rings(priv); return 0; } -- cgit From b8dbecff9bab8251d63bc7e7d0f6028f5afd7152 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 12:28:47 +1000 Subject: ftgmac100: Move napi_add/del to open/close Rather than probe/remove Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index a533a7d5a5cc..c8b4e6488545 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1160,6 +1160,9 @@ static int ftgmac100_open(struct net_device *netdev) if (err) goto err_hw; + /* Initialize NAPI */ + netif_napi_add(netdev, &priv->napi, ftgmac100_poll, 64); + ftgmac100_init_hw(priv); ftgmac100_start_hw(priv); @@ -1190,6 +1193,7 @@ static int ftgmac100_open(struct net_device *netdev) err_ncsi: napi_disable(&priv->napi); netif_stop_queue(netdev); + netif_napi_del(&priv->napi); iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); err_hw: free_irq(netdev->irq, netdev); @@ -1209,6 +1213,7 @@ static int ftgmac100_stop(struct net_device *netdev) netif_stop_queue(netdev); napi_disable(&priv->napi); + netif_napi_del(&priv->napi); if (netdev->phydev) phy_stop(netdev->phydev); else if (priv->use_ncsi) @@ -1381,9 +1386,6 @@ static int ftgmac100_probe(struct platform_device *pdev) spin_lock_init(&priv->tx_lock); - /* initialize NAPI */ - netif_napi_add(netdev, &priv->napi, ftgmac100_poll, 64); - /* map io memory */ priv->res = request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev)); -- cgit From 81f1eca663c070ea07eb390ca2a0b205287c8fc1 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 12:28:48 +1000 Subject: ftgmac100: Request the interrupt only after HW is reset The interrupt isn't shared, so this will keep it masked until we have the HW in a known sane state. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index c8b4e6488545..09d06e2b78bd 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1131,12 +1131,6 @@ static int ftgmac100_open(struct net_device *netdev) if (ftgmac100_alloc_rx_buffers(priv)) goto err_alloc; - err = request_irq(netdev->irq, ftgmac100_interrupt, 0, netdev->name, netdev); - if (err) { - netdev_err(netdev, "failed to request irq %d\n", netdev->irq); - goto err_irq; - } - /* When using NC-SI we force the speed to 100Mbit/s full duplex, * * Otherwise we leave it set to 0 (no link), the link @@ -1163,6 +1157,13 @@ static int ftgmac100_open(struct net_device *netdev) /* Initialize NAPI */ netif_napi_add(netdev, &priv->napi, ftgmac100_poll, 64); + /* Grab our interrupt */ + err = request_irq(netdev->irq, ftgmac100_interrupt, 0, netdev->name, netdev); + if (err) { + netdev_err(netdev, "failed to request irq %d\n", netdev->irq); + goto err_irq; + } + ftgmac100_init_hw(priv); ftgmac100_start_hw(priv); @@ -1193,12 +1194,12 @@ static int ftgmac100_open(struct net_device *netdev) err_ncsi: napi_disable(&priv->napi); netif_stop_queue(netdev); - netif_napi_del(&priv->napi); - iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); -err_hw: free_irq(netdev->irq, netdev); err_irq: + netif_napi_del(&priv->napi); +err_hw: err_alloc: + iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); ftgmac100_free_buffers(priv); ftgmac100_free_rings(priv); return err; -- cgit From da40d9d4b5932d27d8cf3fea685d4cd466b88ec3 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 12:28:49 +1000 Subject: ftgmac100: Move the bulk of inits to a separate function The link monitoring and error handling code will have to redo the ring inits and HW setup so move the code out of ftgmac100_open() into a dedicated function. This forces a bit of re-ordering of ftgmac100_open() but nothing dramatic. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 71 +++++++++++++++++++------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 09d06e2b78bd..7f46d9c18f11 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1111,10 +1111,35 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget) return rx; } +static int ftgmac100_init_all(struct ftgmac100 *priv, bool ignore_alloc_err) +{ + int err = 0; + + /* Re-init descriptors (adjust queue sizes) */ + ftgmac100_init_rings(priv); + + /* Realloc rx descriptors */ + err = ftgmac100_alloc_rx_buffers(priv); + if (err && !ignore_alloc_err) + return err; + + /* Reinit and restart HW */ + ftgmac100_init_hw(priv); + ftgmac100_start_hw(priv); + + /* Re-enable the device */ + napi_enable(&priv->napi); + netif_start_queue(priv->netdev); + + /* Enable all interrupts */ + iowrite32(priv->int_mask_all, priv->base + FTGMAC100_OFFSET_IER); + + return err; +} + static int ftgmac100_open(struct net_device *netdev) { struct ftgmac100 *priv = netdev_priv(netdev); - unsigned int status; int err; /* Allocate ring buffers */ @@ -1124,13 +1149,6 @@ static int ftgmac100_open(struct net_device *netdev) return err; } - /* Initialize the rings */ - ftgmac100_init_rings(priv); - - /* Allocate receive buffers */ - if (ftgmac100_alloc_rx_buffers(priv)) - goto err_alloc; - /* When using NC-SI we force the speed to 100Mbit/s full duplex, * * Otherwise we leave it set to 0 (no link), the link @@ -1164,26 +1182,21 @@ static int ftgmac100_open(struct net_device *netdev) goto err_irq; } - ftgmac100_init_hw(priv); - ftgmac100_start_hw(priv); - - /* Clear stale interrupts */ - status = ioread32(priv->base + FTGMAC100_OFFSET_ISR); - iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR); + /* Start things up */ + err = ftgmac100_init_all(priv, false); + if (err) { + netdev_err(netdev, "Failed to allocate packet buffers\n"); + goto err_alloc; + } - if (netdev->phydev) + if (netdev->phydev) { + /* If we have a PHY, start polling */ phy_start(netdev->phydev); - else if (priv->use_ncsi) + } else if (priv->use_ncsi) { + /* If using NC-SI, set our carrier on and start the stack */ netif_carrier_on(netdev); - napi_enable(&priv->napi); - netif_start_queue(netdev); - - /* enable all interrupts */ - iowrite32(priv->int_mask_all, priv->base + FTGMAC100_OFFSET_IER); - - /* Start the NCSI device */ - if (priv->use_ncsi) { + /* Start the NCSI device */ err = ncsi_start_dev(priv->ndev); if (err) goto err_ncsi; @@ -1191,16 +1204,16 @@ static int ftgmac100_open(struct net_device *netdev) return 0; -err_ncsi: + err_ncsi: napi_disable(&priv->napi); netif_stop_queue(netdev); + err_alloc: + ftgmac100_free_buffers(priv); free_irq(netdev->irq, netdev); -err_irq: + err_irq: netif_napi_del(&priv->napi); -err_hw: -err_alloc: + err_hw: iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); - ftgmac100_free_buffers(priv); ftgmac100_free_rings(priv); return err; } -- cgit From 855944ce1cc4037d58361b7b1cd0f07c2eb40734 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 12:28:50 +1000 Subject: ftgmac100: Add a reset task and use it for link changes Link speed changes require a full HW reset. This isn't done properly at the moment. It will involve delays and thus isn't suitable to do from the link poll callback. So let's create a reset_task that we can queue up when the link changes. It will be useful for various cases of error handling as well. Signed-off-by: Benjamin Herrenschmidt Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 87 +++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 7f46d9c18f11..88c492e956da 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -76,6 +76,7 @@ struct ftgmac100 { struct device *dev; struct ncsi_dev *ndev; struct napi_struct napi; + struct work_struct reset_task; struct mii_bus *mii_bus; /* Link management */ @@ -874,7 +875,6 @@ static void ftgmac100_adjust_link(struct net_device *netdev) struct ftgmac100 *priv = netdev_priv(netdev); struct phy_device *phydev = netdev->phydev; int new_speed; - int ier; /* We store "no link" as speed 0 */ if (!phydev->link) @@ -899,20 +899,11 @@ static void ftgmac100_adjust_link(struct net_device *netdev) if (!new_speed) return; - ier = ioread32(priv->base + FTGMAC100_OFFSET_IER); - - /* disable all interrupts */ + /* Disable all interrupts */ iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); - netif_stop_queue(netdev); - ftgmac100_stop_hw(priv); - - netif_start_queue(netdev); - ftgmac100_init_hw(priv); - ftgmac100_start_hw(priv); - - /* re-enable interrupts */ - iowrite32(ier, priv->base + FTGMAC100_OFFSET_IER); + /* Reset the adapter asynchronously */ + schedule_work(&priv->reset_task); } static int ftgmac100_mii_probe(struct ftgmac100 *priv) @@ -1137,6 +1128,61 @@ static int ftgmac100_init_all(struct ftgmac100 *priv, bool ignore_alloc_err) return err; } +static void ftgmac100_reset_task(struct work_struct *work) +{ + struct ftgmac100 *priv = container_of(work, struct ftgmac100, + reset_task); + struct net_device *netdev = priv->netdev; + int err; + + netdev_dbg(netdev, "Resetting NIC...\n"); + + /* Lock the world */ + rtnl_lock(); + if (netdev->phydev) + mutex_lock(&netdev->phydev->lock); + if (priv->mii_bus) + mutex_lock(&priv->mii_bus->mdio_lock); + + + /* Check if the interface is still up */ + if (!netif_running(netdev)) + goto bail; + + /* Stop the network stack */ + netif_trans_update(netdev); + napi_disable(&priv->napi); + netif_tx_disable(netdev); + + /* Stop and reset the MAC */ + ftgmac100_stop_hw(priv); + err = ftgmac100_reset_hw(priv); + if (err) { + /* Not much we can do ... it might come back... */ + netdev_err(netdev, "attempting to continue...\n"); + } + + /* Free all rx and tx buffers */ + ftgmac100_free_buffers(priv); + + /* The ring pointers have been reset in HW, reflect this here */ + priv->rx_pointer = 0; + priv->tx_clean_pointer = 0; + priv->tx_pointer = 0; + priv->tx_pending = 0; + + /* Setup everything again and restart chip */ + ftgmac100_init_all(priv, true); + + netdev_dbg(netdev, "Reset done !\n"); + bail: + if (priv->mii_bus) + mutex_unlock(&priv->mii_bus->mdio_lock); + if (netdev->phydev) + mutex_unlock(&netdev->phydev->lock); + rtnl_unlock(); +} + static int ftgmac100_open(struct net_device *netdev) { struct ftgmac100 *priv = netdev_priv(netdev); @@ -1222,6 +1268,14 @@ static int ftgmac100_stop(struct net_device *netdev) { struct ftgmac100 *priv = netdev_priv(netdev); + /* Note about the reset task: We are called with the rtnl lock + * held, so we are synchronized against the core of the reset + * task. We must not try to synchronously cancel it otherwise + * we can deadlock. But since it will test for netif_running() + * which has already been cleared by the net core, we don't + * anything special to do. + */ + /* disable all interrupts */ iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); @@ -1397,6 +1451,7 @@ static int ftgmac100_probe(struct platform_device *pdev) priv = netdev_priv(netdev); priv->netdev = netdev; priv->dev = &pdev->dev; + INIT_WORK(&priv->reset_task, ftgmac100_reset_task); spin_lock_init(&priv->tx_lock); @@ -1500,6 +1555,12 @@ static int ftgmac100_remove(struct platform_device *pdev) priv = netdev_priv(netdev); unregister_netdev(netdev); + + /* There's a small chance the reset task will have been re-queued, + * during stop, make sure it's gone before we free the structure. + */ + cancel_work_sync(&priv->reset_task); + ftgmac100_destroy_mdio(netdev); iounmap(priv->base); -- cgit From 874b55bf62330ca215758845d8abe93d139f874a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 12:28:51 +1000 Subject: ftgmac100: Rework MAC reset and init The HW requires a full MAC reset when changing the speed. Additionally the Aspeed documentation spells out that the MAC needs to be reset twice with a 10us interval. We thus move the speed setting and top level reset code into a new ftgmac100_reset_and_config_mac() function which handles both. Move the ring pointers initialization there too in order to reflect the HW change. Also reduce the timeout for the MAC reset as it shouldn't take more than 300 clock cycles according to the doc. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 98 +++++++++++++++++++------------- 1 file changed, 59 insertions(+), 39 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 88c492e956da..2c08bacd066c 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -114,27 +114,64 @@ static void ftgmac100_txdma_normal_prio_start_polling(struct ftgmac100 *priv) iowrite32(1, priv->base + FTGMAC100_OFFSET_NPTXPD); } -static int ftgmac100_reset_hw(struct ftgmac100 *priv) +static int ftgmac100_reset_mac(struct ftgmac100 *priv, u32 maccr) { struct net_device *netdev = priv->netdev; int i; /* NOTE: reset clears all registers */ - iowrite32(FTGMAC100_MACCR_SW_RST, priv->base + FTGMAC100_OFFSET_MACCR); - for (i = 0; i < 5; i++) { + iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR); + iowrite32(maccr | FTGMAC100_MACCR_SW_RST, + priv->base + FTGMAC100_OFFSET_MACCR); + for (i = 0; i < 50; i++) { unsigned int maccr; maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR); if (!(maccr & FTGMAC100_MACCR_SW_RST)) return 0; - udelay(1000); + udelay(1); } - netdev_err(netdev, "software reset failed\n"); + netdev_err(netdev, "Hardware reset failed\n"); return -EIO; } +static int ftgmac100_reset_and_config_mac(struct ftgmac100 *priv) +{ + u32 maccr = 0; + + switch (priv->cur_speed) { + case SPEED_10: + case 0: /* no link */ + break; + + case SPEED_100: + maccr |= FTGMAC100_MACCR_FAST_MODE; + break; + + case SPEED_1000: + maccr |= FTGMAC100_MACCR_GIGA_MODE; + break; + default: + netdev_err(priv->netdev, "Unknown speed %d !\n", + priv->cur_speed); + break; + } + + /* (Re)initialize the queue pointers */ + priv->rx_pointer = 0; + priv->tx_clean_pointer = 0; + priv->tx_pointer = 0; + priv->tx_pending = 0; + + /* The doc says reset twice with 10us interval */ + if (ftgmac100_reset_mac(priv, maccr)) + return -EIO; + usleep_range(10, 1000); + return ftgmac100_reset_mac(priv, maccr); +} + static void ftgmac100_set_mac(struct ftgmac100 *priv, const unsigned char *mac) { unsigned int maddr = mac[0] << 8 | mac[1]; @@ -210,35 +247,28 @@ static void ftgmac100_init_hw(struct ftgmac100 *priv) ftgmac100_set_mac(priv, priv->netdev->dev_addr); } -#define MACCR_ENABLE_ALL (FTGMAC100_MACCR_TXDMA_EN | \ - FTGMAC100_MACCR_RXDMA_EN | \ - FTGMAC100_MACCR_TXMAC_EN | \ - FTGMAC100_MACCR_RXMAC_EN | \ - FTGMAC100_MACCR_CRC_APD | \ - FTGMAC100_MACCR_RX_RUNT | \ - FTGMAC100_MACCR_RX_BROADPKT) - static void ftgmac100_start_hw(struct ftgmac100 *priv) { - int maccr = MACCR_ENABLE_ALL; + u32 maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR); - switch (priv->cur_speed) { - default: - case 10: - break; + /* Keep the original GMAC and FAST bits */ + maccr &= (FTGMAC100_MACCR_FAST_MODE | FTGMAC100_MACCR_GIGA_MODE); - case 100: - maccr |= FTGMAC100_MACCR_FAST_MODE; - break; - - case 1000: - maccr |= FTGMAC100_MACCR_GIGA_MODE; - break; - } + /* Add all the main enable bits */ + maccr |= FTGMAC100_MACCR_TXDMA_EN | + FTGMAC100_MACCR_RXDMA_EN | + FTGMAC100_MACCR_TXMAC_EN | + FTGMAC100_MACCR_RXMAC_EN | + FTGMAC100_MACCR_CRC_APD | + FTGMAC100_MACCR_PHY_LINK_LEVEL | + FTGMAC100_MACCR_RX_RUNT | + FTGMAC100_MACCR_RX_BROADPKT; + /* Add other bits as needed */ if (priv->cur_duplex == DUPLEX_FULL) maccr |= FTGMAC100_MACCR_FULLDUP; + /* Hit the HW */ iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR); } @@ -1156,7 +1186,7 @@ static void ftgmac100_reset_task(struct work_struct *work) /* Stop and reset the MAC */ ftgmac100_stop_hw(priv); - err = ftgmac100_reset_hw(priv); + err = ftgmac100_reset_and_config_mac(priv); if (err) { /* Not much we can do ... it might come back... */ netdev_err(netdev, "attempting to continue...\n"); @@ -1165,12 +1195,6 @@ static void ftgmac100_reset_task(struct work_struct *work) /* Free all rx and tx buffers */ ftgmac100_free_buffers(priv); - /* The ring pointers have been reset in HW, reflect this here */ - priv->rx_pointer = 0; - priv->tx_clean_pointer = 0; - priv->tx_pointer = 0; - priv->tx_pending = 0; - /* Setup everything again and restart chip */ ftgmac100_init_all(priv, true); @@ -1209,12 +1233,8 @@ static int ftgmac100_open(struct net_device *netdev) priv->cur_speed = 0; } - priv->rx_pointer = 0; - priv->tx_clean_pointer = 0; - priv->tx_pointer = 0; - priv->tx_pending = 0; - - err = ftgmac100_reset_hw(priv); + /* Reset the hardware */ + err = ftgmac100_reset_and_config_mac(priv); if (err) goto err_hw; -- cgit From 9b86785d1e8b2a971c0ac77a76fb49d78db50916 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 12:28:52 +1000 Subject: ftgmac100: Remove useless tests in interrupt handler The interrupt is neither enabled nor registered when the interface isn't running (regardless of whether we use nc-si or not) so the test isn't useful. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 2c08bacd066c..6625012d734e 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1049,14 +1049,9 @@ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) struct net_device *netdev = dev_id; struct ftgmac100 *priv = netdev_priv(netdev); - /* When running in NCSI mode, the interface should be ready for - * receiving or transmitting NCSI packets before it's opened. - */ - if (likely(priv->use_ncsi || netif_running(netdev))) { - /* Disable interrupts for polling */ - iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); - napi_schedule(&priv->napi); - } + /* Disable interrupts for polling */ + iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); + napi_schedule(&priv->napi); return IRQ_HANDLED; } -- cgit From 10cbd640760991b8563dbe1d94160641fa6287b7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Apr 2017 12:28:53 +1000 Subject: ftgmac100: Rework NAPI & interrupts handling First, don't look at the interrupt status in the poll loop to decide what to poll. It's wrong. If we have run out of budget, we may still have RX packets to unqueue but no more RX interrupt pending. So instead move the code looking at the interrupt status into the interrupt handler where it belongs. That avoids a slow MMIO read in the NAPI fast path. We keep the abnormal interrupts enabled while NAPI is scheduled. While at it, actually do something useful in the "error" cases: On AHB bus error, trigger the new reset task, that's about all we can do. On RX packet fifo or descriptor overflows, we need to restart the MAC after having freed things up. So set a flag that NAPI will see and use to perform that restart after harvesting the RX ring. Finally, we shouldn't complete NAPI if there are still outgoing packets that will need harvesting. Waiting for more interrupts is less efficient than letting NAPI run a while longer while the queue drains. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 137 +++++++++++++++++-------------- drivers/net/ethernet/faraday/ftgmac100.h | 14 ++++ 2 files changed, 90 insertions(+), 61 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 6625012d734e..20fc7c8d1a50 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -85,7 +85,7 @@ struct ftgmac100 { bool use_ncsi; /* Misc */ - int int_mask_all; + bool need_mac_restart; }; static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, @@ -1048,10 +1048,49 @@ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) { struct net_device *netdev = dev_id; struct ftgmac100 *priv = netdev_priv(netdev); + unsigned int status, new_mask = FTGMAC100_INT_BAD; - /* Disable interrupts for polling */ - iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); - napi_schedule(&priv->napi); + /* Fetch and clear interrupt bits, process abnormal ones */ + status = ioread32(priv->base + FTGMAC100_OFFSET_ISR); + iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR); + if (unlikely(status & FTGMAC100_INT_BAD)) { + + /* RX buffer unavailable */ + if (status & FTGMAC100_INT_NO_RXBUF) + netdev->stats.rx_over_errors++; + + /* received packet lost due to RX FIFO full */ + if (status & FTGMAC100_INT_RPKT_LOST) + netdev->stats.rx_fifo_errors++; + + /* sent packet lost due to excessive TX collision */ + if (status & FTGMAC100_INT_XPKT_LOST) + netdev->stats.tx_fifo_errors++; + + /* AHB error -> Reset the chip */ + if (status & FTGMAC100_INT_AHB_ERR) { + if (net_ratelimit()) + netdev_warn(netdev, + "AHB bus error ! Resetting chip.\n"); + iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); + schedule_work(&priv->reset_task); + return IRQ_HANDLED; + } + + /* We may need to restart the MAC after such errors, delay + * this until after we have freed some Rx buffers though + */ + priv->need_mac_restart = true; + + /* Disable those errors until we restart */ + new_mask &= ~status; + } + + /* Only enable "bad" interrupts while NAPI is on */ + iowrite32(new_mask, priv->base + FTGMAC100_OFFSET_IER); + + /* Schedule NAPI bh */ + napi_schedule_irqoff(&priv->napi); return IRQ_HANDLED; } @@ -1059,68 +1098,51 @@ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) static int ftgmac100_poll(struct napi_struct *napi, int budget) { struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi); - struct net_device *netdev = priv->netdev; - unsigned int status; - bool completed = true; + bool more, completed = true; int rx = 0; - status = ioread32(priv->base + FTGMAC100_OFFSET_ISR); - iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR); - - if (status & (FTGMAC100_INT_RPKT_BUF | FTGMAC100_INT_NO_RXBUF)) { - /* - * FTGMAC100_INT_RPKT_BUF: - * RX DMA has received packets into RX buffer successfully - * - * FTGMAC100_INT_NO_RXBUF: - * RX buffer unavailable - */ - bool retry; + ftgmac100_tx_complete(priv); - do { - retry = ftgmac100_rx_packet(priv, &rx); - } while (retry && rx < budget); + do { + more = ftgmac100_rx_packet(priv, &rx); + } while (more && rx < budget); - if (retry && rx == budget) - completed = false; - } + if (more && rx == budget) + completed = false; - if (status & (FTGMAC100_INT_XPKT_ETH | FTGMAC100_INT_XPKT_LOST)) { - /* - * FTGMAC100_INT_XPKT_ETH: - * packet transmitted to ethernet successfully - * - * FTGMAC100_INT_XPKT_LOST: - * packet transmitted to ethernet lost due to late - * collision or excessive collision - */ - ftgmac100_tx_complete(priv); - } - - if (status & priv->int_mask_all & (FTGMAC100_INT_NO_RXBUF | - FTGMAC100_INT_RPKT_LOST | FTGMAC100_INT_AHB_ERR)) { - if (net_ratelimit()) - netdev_info(netdev, "[ISR] = 0x%x: %s%s%s\n", status, - status & FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "", - status & FTGMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "", - status & FTGMAC100_INT_AHB_ERR ? "AHB_ERR " : ""); - if (status & FTGMAC100_INT_NO_RXBUF) { - /* RX buffer unavailable */ - netdev->stats.rx_over_errors++; - } + /* The interrupt is telling us to kick the MAC back to life + * after an RX overflow + */ + if (unlikely(priv->need_mac_restart)) { + ftgmac100_start_hw(priv); - if (status & FTGMAC100_INT_RPKT_LOST) { - /* received packet lost due to RX FIFO full */ - netdev->stats.rx_fifo_errors++; - } + /* Re-enable "bad" interrupts */ + iowrite32(FTGMAC100_INT_BAD, + priv->base + FTGMAC100_OFFSET_IER); } + /* Keep NAPI going if we have still packets to reclaim */ + if (priv->tx_pending) + return budget; + if (completed) { + /* We are about to re-enable all interrupts. However + * the HW has been latching RX/TX packet interrupts while + * they were masked. So we clear them first, then we need + * to re-check if there's something to process + */ + iowrite32(FTGMAC100_INT_RXTX, + priv->base + FTGMAC100_OFFSET_ISR); + if (ftgmac100_rxdes_packet_ready + (ftgmac100_current_rxdes(priv)) || priv->tx_pending) + return budget; + + /* deschedule NAPI */ napi_complete(napi); /* enable all interrupts */ - iowrite32(priv->int_mask_all, + iowrite32(FTGMAC100_INT_ALL, priv->base + FTGMAC100_OFFSET_IER); } @@ -1148,7 +1170,7 @@ static int ftgmac100_init_all(struct ftgmac100 *priv, bool ignore_alloc_err) netif_start_queue(priv->netdev); /* Enable all interrupts */ - iowrite32(priv->int_mask_all, priv->base + FTGMAC100_OFFSET_IER); + iowrite32(FTGMAC100_INT_ALL, priv->base + FTGMAC100_OFFSET_IER); return err; } @@ -1491,13 +1513,6 @@ static int ftgmac100_probe(struct platform_device *pdev) /* MAC address from chip or random one */ ftgmac100_setup_mac(priv); - priv->int_mask_all = (FTGMAC100_INT_RPKT_LOST | - FTGMAC100_INT_XPKT_ETH | - FTGMAC100_INT_XPKT_LOST | - FTGMAC100_INT_AHB_ERR | - FTGMAC100_INT_RPKT_BUF | - FTGMAC100_INT_NO_RXBUF); - if (of_machine_is_compatible("aspeed,ast2400") || of_machine_is_compatible("aspeed,ast2500")) { priv->rxdes0_edorr_mask = BIT(30); diff --git a/drivers/net/ethernet/faraday/ftgmac100.h b/drivers/net/ethernet/faraday/ftgmac100.h index a7ce0ac8858a..c4d5cc11062f 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.h +++ b/drivers/net/ethernet/faraday/ftgmac100.h @@ -86,6 +86,20 @@ #define FTGMAC100_INT_PHYSTS_CHG (1 << 9) #define FTGMAC100_INT_NO_HPTXBUF (1 << 10) +/* Interrupts we care about in NAPI mode */ +#define FTGMAC100_INT_BAD (FTGMAC100_INT_RPKT_LOST | \ + FTGMAC100_INT_XPKT_LOST | \ + FTGMAC100_INT_AHB_ERR | \ + FTGMAC100_INT_NO_RXBUF) + +/* Normal RX/TX interrupts, enabled when NAPI off */ +#define FTGMAC100_INT_RXTX (FTGMAC100_INT_XPKT_ETH | \ + FTGMAC100_INT_RPKT_BUF) + +/* All the interrupts we care about */ +#define FTGMAC100_INT_ALL (FTGMAC100_INT_RPKT_BUF | \ + FTGMAC100_INT_BAD) + /* * Interrupt timer control register */ -- cgit From 82fe0d2b44e00820c4ecb40a30e6014df6a1dd1f Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 4 Apr 2017 22:12:09 -0700 Subject: af_unix: Use designated initializers Prepare to mark sensitive kernel structures for randomization by making sure they're using designated initializers. These were identified during allyesconfig builds of x86, arm, and arm64, and the initializer fixes were extracted from grsecurity. In this case, NULL initialize with { } instead of undesignated NULLs. Signed-off-by: Kees Cook Signed-off-by: David S. Miller --- net/unix/af_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 928691c43408..6a7fe7660551 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -996,7 +996,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) unsigned int hash; struct unix_address *addr; struct hlist_head *list; - struct path path = { NULL, NULL }; + struct path path = { }; err = -EINVAL; if (sunaddr->sun_family != AF_UNIX) -- cgit From 053ee0a7035781a27eedb2b06cac638c8c65ce83 Mon Sep 17 00:00:00 2001 From: Tobias Regnery Date: Wed, 5 Apr 2017 11:11:10 +0200 Subject: net/mlx5e: fix build error without CONFIG_SYSFS Commit 9008ae074885 ("net/mlx5e: Minimize mlx5e_{open/close}_locked") copied the calls to netif_set_real_num_{tx,rx}_queues from mlx5e_open_locked to mlx5e_activate_priv_channels and wraps them in an if condition to test for netdev->real_num_{tx,rx}_queues. But netdev->real_num_rx_queues is conditionally compiled in if CONFIG_SYSFS is set. Without CONFIG_SYSFS the build fails: drivers/net/ethernet/mellanox/mlx5/core/en_main.c: In function 'mlx5e_activate_priv_channels': drivers/net/ethernet/mellanox/mlx5/core/en_main.c:2515:12: error: 'struct net_device' has no member named 'real_num_rx_queues'; did you mean 'real_num_tx_queues'? Fix this by unconditionally call netif_set_real_num{tx,rx}_queues like before commit 9008ae074885. Fixes: 9008ae074885 ("net/mlx5e: Minimize mlx5e_{open/close}_locked") Signed-off-by: Tobias Regnery Acked-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index ec389b1b51cb..d5248637d44f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2510,10 +2510,8 @@ static void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) struct net_device *netdev = priv->netdev; mlx5e_netdev_set_tcs(netdev); - if (netdev->real_num_tx_queues != num_txqs) - netif_set_real_num_tx_queues(netdev, num_txqs); - if (netdev->real_num_rx_queues != priv->channels.num) - netif_set_real_num_rx_queues(netdev, priv->channels.num); + netif_set_real_num_tx_queues(netdev, num_txqs); + netif_set_real_num_rx_queues(netdev, priv->channels.num); mlx5e_build_channels_tx_maps(priv); mlx5e_activate_channels(&priv->channels); -- cgit From cba81cc4c95fefa4805163bb19c0f43d2a8ca52c Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Mon, 27 Mar 2017 23:12:08 +0800 Subject: netfilter: nat: nf_nat_mangle_{udp,tcp}_packet returns boolean nf_nat_mangle_{udp,tcp}_packet() returns int. However, it is used as bool type in many spots. Fix this by consistently handle this return value as a boolean. Signed-off-by: Gao Feng Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_nat_helper.h | 36 +++++++++++++++---------------- net/ipv4/netfilter/nf_nat_pptp.c | 20 +++++++++--------- net/netfilter/ipvs/ip_vs_ftp.c | 13 +++++++----- net/netfilter/nf_nat_amanda.c | 11 +++++----- net/netfilter/nf_nat_helper.c | 40 +++++++++++++++++------------------ net/netfilter/nf_nat_irc.c | 9 ++++---- 6 files changed, 65 insertions(+), 64 deletions(-) diff --git a/include/net/netfilter/nf_nat_helper.h b/include/net/netfilter/nf_nat_helper.h index 01bcc6bfbcc9..fbfa5acf4f14 100644 --- a/include/net/netfilter/nf_nat_helper.h +++ b/include/net/netfilter/nf_nat_helper.h @@ -7,31 +7,31 @@ struct sk_buff; /* These return true or false. */ -int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff, unsigned int match_offset, - unsigned int match_len, const char *rep_buffer, - unsigned int rep_len, bool adjust); +bool __nf_nat_mangle_tcp_packet(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int match_offset, + unsigned int match_len, const char *rep_buffer, + unsigned int rep_len, bool adjust); -static inline int nf_nat_mangle_tcp_packet(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len) +static inline bool nf_nat_mangle_tcp_packet(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int protoff, + unsigned int match_offset, + unsigned int match_len, + const char *rep_buffer, + unsigned int rep_len) { return __nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, match_offset, match_len, rep_buffer, rep_len, true); } -int nf_nat_mangle_udp_packet(struct sk_buff *skb, struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff, unsigned int match_offset, - unsigned int match_len, const char *rep_buffer, - unsigned int rep_len); +bool nf_nat_mangle_udp_packet(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int match_offset, + unsigned int match_len, const char *rep_buffer, + unsigned int rep_len); /* Setup NAT on this expected conntrack so it follows master, but goes * to port ct->master->saved_proto. */ diff --git a/net/ipv4/netfilter/nf_nat_pptp.c b/net/ipv4/netfilter/nf_nat_pptp.c index b3ca21b2ba9b..211fee5fe59d 100644 --- a/net/ipv4/netfilter/nf_nat_pptp.c +++ b/net/ipv4/netfilter/nf_nat_pptp.c @@ -177,11 +177,11 @@ pptp_outbound_pkt(struct sk_buff *skb, ntohs(REQ_CID(pptpReq, cid_off)), ntohs(new_callid)); /* mangle packet */ - if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, - cid_off + sizeof(struct pptp_pkt_hdr) + - sizeof(struct PptpControlHeader), - sizeof(new_callid), (char *)&new_callid, - sizeof(new_callid)) == 0) + if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, + cid_off + sizeof(struct pptp_pkt_hdr) + + sizeof(struct PptpControlHeader), + sizeof(new_callid), (char *)&new_callid, + sizeof(new_callid))) return NF_DROP; return NF_ACCEPT; } @@ -271,11 +271,11 @@ pptp_inbound_pkt(struct sk_buff *skb, pr_debug("altering peer call id from 0x%04x to 0x%04x\n", ntohs(REQ_CID(pptpReq, pcid_off)), ntohs(new_pcid)); - if (nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, - pcid_off + sizeof(struct pptp_pkt_hdr) + - sizeof(struct PptpControlHeader), - sizeof(new_pcid), (char *)&new_pcid, - sizeof(new_pcid)) == 0) + if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, + pcid_off + sizeof(struct pptp_pkt_hdr) + + sizeof(struct PptpControlHeader), + sizeof(new_pcid), (char *)&new_pcid, + sizeof(new_pcid))) return NF_DROP; return NF_ACCEPT; } diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index d30c327bb578..e9e721e63844 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -261,6 +261,8 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, ct = nf_ct_get(skb, &ctinfo); if (ct && !nf_ct_is_untracked(ct) && nfct_nat(ct)) { + bool mangled; + /* If mangling fails this function will return 0 * which will cause the packet to be dropped. * Mangling can only fail under memory pressure, @@ -268,12 +270,13 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, * packet. */ rcu_read_lock(); - ret = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, - iph->ihl * 4, - start-data, end-start, - buf, buf_len); + mangled = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, + iph->ihl * 4, + start - data, + end - start, + buf, buf_len); rcu_read_unlock(); - if (ret) { + if (mangled) { ip_vs_nfct_expect_related(skb, ct, n_cp, IPPROTO_TCP, 0, 0); if (skb->ip_summed == CHECKSUM_COMPLETE) diff --git a/net/netfilter/nf_nat_amanda.c b/net/netfilter/nf_nat_amanda.c index eb772380a202..e4d61a7a5258 100644 --- a/net/netfilter/nf_nat_amanda.c +++ b/net/netfilter/nf_nat_amanda.c @@ -33,7 +33,6 @@ static unsigned int help(struct sk_buff *skb, { char buffer[sizeof("65535")]; u_int16_t port; - unsigned int ret; /* Connection comes from client. */ exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; @@ -63,14 +62,14 @@ static unsigned int help(struct sk_buff *skb, } sprintf(buffer, "%u", port); - ret = nf_nat_mangle_udp_packet(skb, exp->master, ctinfo, - protoff, matchoff, matchlen, - buffer, strlen(buffer)); - if (ret != NF_ACCEPT) { + if (!nf_nat_mangle_udp_packet(skb, exp->master, ctinfo, + protoff, matchoff, matchlen, + buffer, strlen(buffer))) { nf_ct_helper_log(skb, exp->master, "cannot mangle packet"); nf_ct_unexpect_related(exp); + return NF_DROP; } - return ret; + return NF_ACCEPT; } static void __exit nf_nat_amanda_fini(void) diff --git a/net/netfilter/nf_nat_helper.c b/net/netfilter/nf_nat_helper.c index 211661cb2c90..607a373379b4 100644 --- a/net/netfilter/nf_nat_helper.c +++ b/net/netfilter/nf_nat_helper.c @@ -70,15 +70,15 @@ static void mangle_contents(struct sk_buff *skb, } /* Unusual, but possible case. */ -static int enlarge_skb(struct sk_buff *skb, unsigned int extra) +static bool enlarge_skb(struct sk_buff *skb, unsigned int extra) { if (skb->len + extra > 65535) - return 0; + return false; if (pskb_expand_head(skb, 0, extra - skb_tailroom(skb), GFP_ATOMIC)) - return 0; + return false; - return 1; + return true; } /* Generic function for mangling variable-length address changes inside @@ -89,26 +89,26 @@ static int enlarge_skb(struct sk_buff *skb, unsigned int extra) * skb enlargement, ... * * */ -int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len, bool adjust) +bool __nf_nat_mangle_tcp_packet(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int protoff, + unsigned int match_offset, + unsigned int match_len, + const char *rep_buffer, + unsigned int rep_len, bool adjust) { const struct nf_nat_l3proto *l3proto; struct tcphdr *tcph; int oldlen, datalen; if (!skb_make_writable(skb, skb->len)) - return 0; + return false; if (rep_len > match_len && rep_len - match_len > skb_tailroom(skb) && !enlarge_skb(skb, rep_len - match_len)) - return 0; + return false; SKB_LINEAR_ASSERT(skb); @@ -128,7 +128,7 @@ int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, nf_ct_seqadj_set(ct, ctinfo, tcph->seq, (int)rep_len - (int)match_len); - return 1; + return true; } EXPORT_SYMBOL(__nf_nat_mangle_tcp_packet); @@ -142,7 +142,7 @@ EXPORT_SYMBOL(__nf_nat_mangle_tcp_packet); * XXX - This function could be merged with nf_nat_mangle_tcp_packet which * should be fairly easy to do. */ -int +bool nf_nat_mangle_udp_packet(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, @@ -157,12 +157,12 @@ nf_nat_mangle_udp_packet(struct sk_buff *skb, int datalen, oldlen; if (!skb_make_writable(skb, skb->len)) - return 0; + return false; if (rep_len > match_len && rep_len - match_len > skb_tailroom(skb) && !enlarge_skb(skb, rep_len - match_len)) - return 0; + return false; udph = (void *)skb->data + protoff; @@ -176,13 +176,13 @@ nf_nat_mangle_udp_packet(struct sk_buff *skb, /* fix udp checksum if udp checksum was previously calculated */ if (!udph->check && skb->ip_summed != CHECKSUM_PARTIAL) - return 1; + return true; l3proto = __nf_nat_l3proto_find(nf_ct_l3num(ct)); l3proto->csum_recalc(skb, IPPROTO_UDP, udph, &udph->check, datalen, oldlen); - return 1; + return true; } EXPORT_SYMBOL(nf_nat_mangle_udp_packet); diff --git a/net/netfilter/nf_nat_irc.c b/net/netfilter/nf_nat_irc.c index 1fb2258c3535..0648cb096bd8 100644 --- a/net/netfilter/nf_nat_irc.c +++ b/net/netfilter/nf_nat_irc.c @@ -37,7 +37,6 @@ static unsigned int help(struct sk_buff *skb, struct nf_conn *ct = exp->master; union nf_inet_addr newaddr; u_int16_t port; - unsigned int ret; /* Reply comes from server. */ newaddr = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3; @@ -83,14 +82,14 @@ static unsigned int help(struct sk_buff *skb, pr_debug("nf_nat_irc: inserting '%s' == %pI4, port %u\n", buffer, &newaddr.ip, port); - ret = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, matchoff, - matchlen, buffer, strlen(buffer)); - if (ret != NF_ACCEPT) { + if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, matchoff, + matchlen, buffer, strlen(buffer))) { nf_ct_helper_log(skb, ct, "cannot mangle packet"); nf_ct_unexpect_related(exp); + return NF_DROP; } - return ret; + return NF_ACCEPT; } static void __exit nf_nat_irc_fini(void) -- cgit From 6e699867f84c0f358fed233fe6162173aca28e04 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 28 Mar 2017 10:31:03 +0200 Subject: netfilter: nat: avoid use of nf_conn_nat extension successful insert into the bysource hash sets IPS_SRC_NAT_DONE status bit so we can check that instead of presence of nat extension which requires extra deref. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_core.c | 2 +- net/netfilter/nf_nat_core.c | 18 ++++-------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 3d621b8d7b8a..bcf1d2a6539e 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -706,7 +706,7 @@ static int nf_ct_resolve_clash(struct net *net, struct sk_buff *skb, l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); if (l4proto->allow_clash && - !nfct_nat(ct) && + ((ct->status & IPS_NAT_DONE_MASK) == 0) && !nf_ct_is_dying(ct) && atomic_inc_not_zero(&ct->ct_general.use)) { enum ip_conntrack_info oldinfo; diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 82802e4a6640..376c1b36f222 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -549,10 +549,6 @@ struct nf_nat_proto_clean { static int nf_nat_proto_remove(struct nf_conn *i, void *data) { const struct nf_nat_proto_clean *clean = data; - struct nf_conn_nat *nat = nfct_nat(i); - - if (!nat) - return 0; if ((clean->l3proto && nf_ct_l3num(i) != clean->l3proto) || (clean->l4proto && nf_ct_protonum(i) != clean->l4proto)) @@ -563,12 +559,10 @@ static int nf_nat_proto_remove(struct nf_conn *i, void *data) static int nf_nat_proto_clean(struct nf_conn *ct, void *data) { - struct nf_conn_nat *nat = nfct_nat(ct); - if (nf_nat_proto_remove(ct, data)) return 1; - if (!nat) + if ((ct->status & IPS_SRC_NAT_DONE) == 0) return 0; /* This netns is being destroyed, and conntrack has nat null binding. @@ -716,13 +710,9 @@ EXPORT_SYMBOL_GPL(nf_nat_l3proto_unregister); /* No one using conntrack by the time this called. */ static void nf_nat_cleanup_conntrack(struct nf_conn *ct) { - struct nf_conn_nat *nat = nf_ct_ext_find(ct, NF_CT_EXT_NAT); - - if (!nat) - return; - - rhltable_remove(&nf_nat_bysource_table, &ct->nat_bysource, - nf_nat_bysource_params); + if (ct->status & IPS_SRC_NAT_DONE) + rhltable_remove(&nf_nat_bysource_table, &ct->nat_bysource, + nf_nat_bysource_params); } static struct nf_ct_ext_type nat_extend __read_mostly = { -- cgit From 2c62e0bc685fcb400a0b5cffb39860290bc902a8 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Tue, 28 Mar 2017 09:52:52 +0800 Subject: netfilter: ctnetlink: Expectations must have a conntrack helper area The expect check function __nf_ct_expect_check() asks the master_help is necessary. So it is unnecessary to go ahead in ctnetlink_alloc_expect when there is no help. Actually the commit bc01befdcf3e ("netfilter: ctnetlink: add support for user-space expectation helpers") permits ctnetlink create one expect even though there is no master help. But the latter commit 3d058d7bc2c5 ("netfilter: rework user-space expectation helper support") disables it again. Signed-off-by: Gao Feng Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_netlink.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index ecdc324c7785..cd0a6d270ebe 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -3038,6 +3038,10 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, struct nf_conn_help *help; int err; + help = nfct_help(ct); + if (!help) + return ERR_PTR(-EOPNOTSUPP); + if (cda[CTA_EXPECT_CLASS] && helper) { class = ntohl(nla_get_be32(cda[CTA_EXPECT_CLASS])); if (class > helper->expect_class_max) @@ -3047,26 +3051,11 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, if (!exp) return ERR_PTR(-ENOMEM); - help = nfct_help(ct); - if (!help) { - if (!cda[CTA_EXPECT_TIMEOUT]) { - err = -EINVAL; - goto err_out; - } - exp->timeout.expires = - jiffies + ntohl(nla_get_be32(cda[CTA_EXPECT_TIMEOUT])) * HZ; - - exp->flags = NF_CT_EXPECT_USERSPACE; - if (cda[CTA_EXPECT_FLAGS]) { - exp->flags |= - ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS])); - } + if (cda[CTA_EXPECT_FLAGS]) { + exp->flags = ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS])); + exp->flags &= ~NF_CT_EXPECT_USERSPACE; } else { - if (cda[CTA_EXPECT_FLAGS]) { - exp->flags = ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS])); - exp->flags &= ~NF_CT_EXPECT_USERSPACE; - } else - exp->flags = 0; + exp->flags = 0; } if (cda[CTA_EXPECT_FN]) { const char *name = nla_data(cda[CTA_EXPECT_FN]); -- cgit From 827d240a232d27cc12e9657d012f2e5ba953e98a Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 5 Apr 2017 13:35:44 +0100 Subject: qed: fix missing break in OOO_LB_TC case There seems to be a missing break on the OOO_LB_TC case, pq_id is being assigned and then re-assigned on the fall through default case and that seems suspect. Detected by CoverityScan, CID#1424402 ("Missing break in switch") Fixes: b5a9ee7cf3be1 ("qed: Revise QM cofiguration") Signed-off-by: Colin Ian King Acked-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_ll2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c index 708c601e8ccf..13e65d446ab3 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c @@ -1132,6 +1132,7 @@ static int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn, break; case OOO_LB_TC: pq_id = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_OOO); + break; default: pq_id = qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_OFLD); break; -- cgit From 75d04aa368d9e945923819e9cc420fac125967ea Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 5 Apr 2017 08:49:02 -0700 Subject: mlx4: trust shinfo->gso_segs mlx4 is the only driver in the tree making a point to recompute shinfo->gso_segs. Lets remove superfluous code. Signed-off-by: Eric Dumazet Cc: Tariq Toukan Cc: Saeed Mahameed Reviewed-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_tx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index e0c5ffb3e3a6..3ba89bc43d74 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -978,8 +978,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) ring->tso_packets++; - i = ((skb->len - lso_header_size) / shinfo->gso_size) + - !!((skb->len - lso_header_size) % shinfo->gso_size); + i = shinfo->gso_segs; tx_info->nr_bytes = skb->len + (i - 1) * lso_header_size; ring->packets += i; } else { -- cgit From 91e91beff6908a1e54536613b318efd56f6e27c4 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 5 Apr 2017 19:09:25 +0300 Subject: net/sched: Removed unused vlan actions definition Commit c7e2b9689ef "sched: introduce vlan action" added both the UAPI values for the vlan actions (TCA_VLAN_ACT_) and these two in-kernel ones which are not used, remove them. Signed-off-by: Or Gerlitz Acked-by: Jiri Pirko Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- include/net/tc_act/tc_vlan.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/net/tc_act/tc_vlan.h b/include/net/tc_act/tc_vlan.h index 9690c047b6cf..c2090df944ff 100644 --- a/include/net/tc_act/tc_vlan.h +++ b/include/net/tc_act/tc_vlan.h @@ -13,9 +13,6 @@ #include #include -#define VLAN_F_POP 0x1 -#define VLAN_F_PUSH 0x2 - struct tcf_vlan { struct tc_action common; int tcfv_action; -- cgit From 5865ccce7eac9bb520d7dacfb48cb72764ed10c3 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 5 Apr 2017 11:19:30 -0700 Subject: net: dsa: loop: Fix uninitialized pvid variable Dan's static analyzer reported the following: drivers/net/dsa/dsa_loop.c:181 dsa_loop_port_vlan_del() error: XXX uninitialized symbol 'pvid'. we were missing the assignment of pvid to ps->vid, so add that. Reported-by: Dan Carpenter Fixes: 98cd1552ea27 ("net: dsa: Mock-up driver") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/dsa_loop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index bc5acc15edbf..ee55a902fa66 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -164,7 +164,7 @@ static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port, struct dsa_loop_priv *ps = ds->priv; struct mii_bus *bus = ps->bus; struct dsa_loop_vlan *vl; - u16 vid, pvid; + u16 vid, pvid = ps->pvid; dev_dbg(ds->dev, "%s\n", __func__); -- cgit From d1db799e968bf6677ac88a90729945ce0eb3ede5 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 5 Apr 2017 11:19:31 -0700 Subject: net: dsa: loop: Initialize err in dsa_loop_vlan_dump Dan's static checker reported the following: drivers/net/dsa/dsa_loop.c:223 dsa_loop_port_vlan_dump() error: uninitialized symbol 'err'. which could happen if we do hit the continue statement for each iteration of the loop. Initialize err to 0 here. Reported-by: Dan Carpenter Fixes: 98cd1552ea27 ("net: dsa: Mock-up driver") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/dsa_loop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c index ee55a902fa66..f0fc4de4fc9a 100644 --- a/drivers/net/dsa/dsa_loop.c +++ b/drivers/net/dsa/dsa_loop.c @@ -194,7 +194,7 @@ static int dsa_loop_port_vlan_dump(struct dsa_switch *ds, int port, struct mii_bus *bus = ps->bus; struct dsa_loop_vlan *vl; u16 vid, vid_start = 0; - int err; + int err = 0; dev_dbg(ds->dev, "%s\n", __func__); -- cgit From 2f78227874754b1e10cd348fd6e7693b0dabb3f6 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Wed, 5 Apr 2017 21:20:11 +0300 Subject: qed: Correct MSI-x for storage When qedr is enabled, qed would try dividing the msi-x vectors between L2 and RoCE, starting with L2 and providing it with sufficient vectors for its queues. Problem is qed would also do that for storage partitions, and as those don't need queues it would lead qed to award those partitions with 0 msi-x vectors, causing them to believe theye're using INTa and preventing them from operating. Fixes: 51ff17251c9c ("qed: Add support for RoCE hw init") Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 634e7a2433a9..aab89ded3aa4 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -745,7 +745,8 @@ static int qed_slowpath_setup_int(struct qed_dev *cdev, cdev->int_params.fp_msix_cnt = cdev->int_params.out.num_vectors - cdev->num_hwfns; - if (!IS_ENABLED(CONFIG_QED_RDMA)) + if (!IS_ENABLED(CONFIG_QED_RDMA) || + QED_LEADING_HWFN(cdev)->hw_info.personality != QED_PCI_ETH_ROCE) return 0; for_each_hwfn(cdev, i) -- cgit From df656bf6fbcf12524e5b2428c92ed3d59cc7b810 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 5 Apr 2017 14:39:03 -0700 Subject: qlge: avoid format string exposure in workqueue While unlikely, this makes sure the workqueue name won't be processed as a format string. Signed-off-by: Kees Cook Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlge/qlge_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index e9e647072596..1188d420fe53 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -4686,7 +4686,8 @@ static int ql_init_device(struct pci_dev *pdev, struct net_device *ndev, /* * Set up the operating parameters. */ - qdev->workqueue = alloc_ordered_workqueue(ndev->name, WQ_MEM_RECLAIM); + qdev->workqueue = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, + ndev->name); INIT_DELAYED_WORK(&qdev->asic_reset_work, ql_asic_reset_work); INIT_DELAYED_WORK(&qdev->mpi_reset_work, ql_mpi_reset_work); INIT_DELAYED_WORK(&qdev->mpi_work, ql_mpi_work); -- cgit From 129858fa0b1ab3072155718b74bf91e440c2d73f Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 5 Apr 2017 14:39:35 -0700 Subject: net: ethernet: wiznet: avoid format string exposure While unlikely, this makes sure any format strings in the device name can't exposure information via the resulting workqueue name. Signed-off-by: Kees Cook Signed-off-by: David S. Miller --- drivers/net/ethernet/wiznet/w5100.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c index f90267f0519f..2bdfb39215e9 100644 --- a/drivers/net/ethernet/wiznet/w5100.c +++ b/drivers/net/ethernet/wiznet/w5100.c @@ -1152,7 +1152,8 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops, if (err < 0) goto err_register; - priv->xfer_wq = alloc_workqueue(netdev_name(ndev), WQ_MEM_RECLAIM, 0); + priv->xfer_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, + netdev_name(ndev)); if (!priv->xfer_wq) { err = -ENOMEM; goto err_wq; -- cgit From 113c3075931a334f899008f6c753abe70a3a9323 Mon Sep 17 00:00:00 2001 From: "R. Parameswaran" Date: Wed, 5 Apr 2017 16:50:35 -0700 Subject: New kernel function to get IP overhead on a socket. A new function, kernel_sock_ip_overhead(), is provided to calculate the cumulative overhead imposed by the IP Header and IP options, if any, on a socket's payload. The new function returns an overhead of zero for sockets that do not belong to the IPv4 or IPv6 address families. This is used in the L2TP code path to compute the total outer IP overhead on the L2TP tunnel socket when calculating the default MTU for Ethernet pseudowires. Signed-off-by: R. Parameswaran Signed-off-by: David S. Miller --- include/linux/net.h | 3 +++ net/socket.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/include/linux/net.h b/include/linux/net.h index 0620f5e18c96..a42fab24c8af 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -298,6 +298,9 @@ int kernel_sendpage(struct socket *sock, struct page *page, int offset, int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how); +/* Following routine returns the IP overhead imposed by a socket. */ +u32 kernel_sock_ip_overhead(struct sock *sk); + #define MODULE_ALIAS_NETPROTO(proto) \ MODULE_ALIAS("net-pf-" __stringify(proto)) diff --git a/net/socket.c b/net/socket.c index 985ef06792d6..eea997036ada 100644 --- a/net/socket.c +++ b/net/socket.c @@ -3356,3 +3356,49 @@ int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how) return sock->ops->shutdown(sock, how); } EXPORT_SYMBOL(kernel_sock_shutdown); + +/* This routine returns the IP overhead imposed by a socket i.e. + * the length of the underlying IP header, depending on whether + * this is an IPv4 or IPv6 socket and the length from IP options turned + * on at the socket. + */ +u32 kernel_sock_ip_overhead(struct sock *sk) +{ + struct inet_sock *inet; + struct ip_options_rcu *opt; + u32 overhead = 0; + bool owned_by_user; +#if IS_ENABLED(CONFIG_IPV6) + struct ipv6_pinfo *np; + struct ipv6_txoptions *optv6 = NULL; +#endif /* IS_ENABLED(CONFIG_IPV6) */ + + if (!sk) + return overhead; + + owned_by_user = sock_owned_by_user(sk); + switch (sk->sk_family) { + case AF_INET: + inet = inet_sk(sk); + overhead += sizeof(struct iphdr); + opt = rcu_dereference_protected(inet->inet_opt, + owned_by_user); + if (opt) + overhead += opt->opt.optlen; + return overhead; +#if IS_ENABLED(CONFIG_IPV6) + case AF_INET6: + np = inet6_sk(sk); + overhead += sizeof(struct ipv6hdr); + if (np) + optv6 = rcu_dereference_protected(np->opt, + owned_by_user); + if (optv6) + overhead += (optv6->opt_flen + optv6->opt_nflen); + return overhead; +#endif /* IS_ENABLED(CONFIG_IPV6) */ + default: /* Returns 0 overhead if the socket is not ipv4 or ipv6 */ + return overhead; + } +} +EXPORT_SYMBOL(kernel_sock_ip_overhead); -- cgit From b784e7ebfce8cfb16c6f95e14e8532d0768ab7ff Mon Sep 17 00:00:00 2001 From: "R. Parameswaran" Date: Wed, 5 Apr 2017 17:00:07 -0700 Subject: L2TP:Adjust intf MTU, add underlay L3, L2 hdrs. Existing L2TP kernel code does not derive the optimal MTU for Ethernet pseudowires and instead leaves this to a userspace L2TP daemon or operator. If an MTU is not specified, the existing kernel code chooses an MTU that does not take account of all tunnel header overheads, which can lead to unwanted IP fragmentation. When L2TP is used without a control plane (userspace daemon), we would prefer that the kernel does a better job of choosing a default pseudowire MTU, taking account of all tunnel header overheads, including IP header options, if any. This patch addresses this. Change-set here uses the new kernel function, kernel_sock_ip_overhead(), to factor the outer IP overhead on the L2TP tunnel socket (including IP Options, if any) when calculating the default MTU for an Ethernet pseudowire, along with consideration of the inner Ethernet header. Signed-off-by: R. Parameswaran Signed-off-by: David S. Miller --- net/l2tp/l2tp_eth.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index 6fd41d7afe1e..138566a63123 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -30,6 +30,9 @@ #include #include #include +#include +#include +#include #include "l2tp_core.h" @@ -204,6 +207,53 @@ static void l2tp_eth_show(struct seq_file *m, void *arg) } #endif +static void l2tp_eth_adjust_mtu(struct l2tp_tunnel *tunnel, + struct l2tp_session *session, + struct net_device *dev) +{ + unsigned int overhead = 0; + struct dst_entry *dst; + u32 l3_overhead = 0; + + /* if the encap is UDP, account for UDP header size */ + if (tunnel->encap == L2TP_ENCAPTYPE_UDP) { + overhead += sizeof(struct udphdr); + dev->needed_headroom += sizeof(struct udphdr); + } + if (session->mtu != 0) { + dev->mtu = session->mtu; + dev->needed_headroom += session->hdr_len; + return; + } + l3_overhead = kernel_sock_ip_overhead(tunnel->sock); + if (l3_overhead == 0) { + /* L3 Overhead couldn't be identified, this could be + * because tunnel->sock was NULL or the socket's + * address family was not IPv4 or IPv6, + * dev mtu stays at 1500. + */ + return; + } + /* Adjust MTU, factor overhead - underlay L3, overlay L2 hdr + * UDP overhead, if any, was already factored in above. + */ + overhead += session->hdr_len + ETH_HLEN + l3_overhead; + + /* If PMTU discovery was enabled, use discovered MTU on L2TP device */ + dst = sk_dst_get(tunnel->sock); + if (dst) { + /* dst_mtu will use PMTU if found, else fallback to intf MTU */ + u32 pmtu = dst_mtu(dst); + + if (pmtu != 0) + dev->mtu = pmtu; + dst_release(dst); + } + session->mtu = dev->mtu - overhead; + dev->mtu = session->mtu; + dev->needed_headroom += session->hdr_len; +} + static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) { struct net_device *dev; @@ -247,12 +297,9 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p } dev_net_set(dev, net); - if (session->mtu == 0) - session->mtu = dev->mtu - session->hdr_len; - dev->mtu = session->mtu; - dev->needed_headroom += session->hdr_len; dev->min_mtu = 0; dev->max_mtu = ETH_MAX_MTU; + l2tp_eth_adjust_mtu(tunnel, session, dev); priv = netdev_priv(dev); priv->dev = dev; -- cgit From 3a50d3518dcba44f8a0f9356b7140fe1499984ea Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Thu, 6 Apr 2017 15:58:28 +0300 Subject: qed: Warn PTT usage by wrong hw-function PTT entries are per-hwfn; If some errneous flow is trying to use a PTT belonging to a differnet hwfn warn user, as this can break every register accessing flow later and is very hard to root-cause. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_hw.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed_hw.c b/drivers/net/ethernet/qlogic/qed/qed_hw.c index 79e584a57d26..a05feb38c6ee 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hw.c +++ b/drivers/net/ethernet/qlogic/qed/qed_hw.c @@ -58,6 +58,7 @@ struct qed_ptt { struct list_head list_entry; unsigned int idx; struct pxp_ptt_entry pxp; + u8 hwfn_id; }; struct qed_ptt_pool { @@ -79,6 +80,7 @@ int qed_ptt_pool_alloc(struct qed_hwfn *p_hwfn) p_pool->ptts[i].idx = i; p_pool->ptts[i].pxp.offset = QED_BAR_INVALID_OFFSET; p_pool->ptts[i].pxp.pretend.control = 0; + p_pool->ptts[i].hwfn_id = p_hwfn->my_id; if (i >= RESERVED_PTT_MAX) list_add(&p_pool->ptts[i].list_entry, &p_pool->free_list); @@ -193,6 +195,11 @@ static u32 qed_set_ptt(struct qed_hwfn *p_hwfn, offset = hw_addr - win_hw_addr; + if (p_ptt->hwfn_id != p_hwfn->my_id) + DP_NOTICE(p_hwfn, + "ptt[%d] of hwfn[%02x] is used by hwfn[%02x]!\n", + p_ptt->idx, p_ptt->hwfn_id, p_hwfn->my_id); + /* Verify the address is within the window */ if (hw_addr < win_hw_addr || offset >= PXP_EXTERNAL_BAR_PF_WINDOW_SINGLE_SIZE) { -- cgit From 1558296251207bb0def2ae7cc045f8159ee0c204 Mon Sep 17 00:00:00 2001 From: Rahul Verma Date: Thu, 6 Apr 2017 15:58:29 +0300 Subject: qed: Don't use main-ptt in unrelated flows In order to access HW registers driver needs to acquire a PTT entry [mapping between bar memory and internal chip address]. Since acquiring PTT entries could fail [at least in theory] as their number is finite and other flows can hold them, we reserve special PTT entries for 'important' enough flows - ones we want to guarantee that would not be susceptible to such issues. One such special entry is the 'main' PTT which is meant to be used in flows such as chip initialization and de-initialization. However, there are other flows that are also using that same entry for their own purpose, and might run concurrently with the original flows [notice that for most cases using the main-ptt by mistake, such a race is still impossible, at least today]. This patch re-organizes the various functions that currently use the main_ptt in one of two ways: - If a function shouldn't use the main_ptt it starts acquiring and releasing it's own PTT entry and use it instead. Notice if those functions previously couldn't fail, they now can [as acquisition might fail]. - Change the prototypes so that the main_ptt would be received as a parameter [instead of explicitly accessing it]. This prevents the future risk of adding codes that introduces new use-cases for flows using the main_ptt, ones that might be in race with the actual 'main' flows. Signed-off-by: Rahul Verma Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_cxt.c | 8 ++-- drivers/net/ethernet/qlogic/qed/qed_cxt.h | 9 ++-- drivers/net/ethernet/qlogic/qed/qed_dev.c | 67 ++++++++++++++++----------- drivers/net/ethernet/qlogic/qed/qed_dev_api.h | 8 ++-- drivers/net/ethernet/qlogic/qed/qed_fcoe.c | 10 +++- drivers/net/ethernet/qlogic/qed/qed_l2.c | 14 +++++- drivers/net/ethernet/qlogic/qed/qed_ll2.c | 49 ++++++++++++++------ drivers/net/ethernet/qlogic/qed/qed_spq.c | 20 ++++++-- 8 files changed, 124 insertions(+), 61 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c index 485b8b22ec7a..15ef6ebed6bb 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c @@ -1438,7 +1438,7 @@ static void qed_cdu_init_pf(struct qed_hwfn *p_hwfn) } } -void qed_qm_init_pf(struct qed_hwfn *p_hwfn) +void qed_qm_init_pf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { struct qed_qm_pf_rt_init_params params; struct qed_qm_info *qm_info = &p_hwfn->qm_info; @@ -1464,7 +1464,7 @@ void qed_qm_init_pf(struct qed_hwfn *p_hwfn) params.pq_params = qm_info->qm_pq_params; params.vport_params = qm_info->qm_vport_params; - qed_qm_pf_rt_init(p_hwfn, p_hwfn->p_main_ptt, ¶ms); + qed_qm_pf_rt_init(p_hwfn, p_ptt, ¶ms); } /* CM PF */ @@ -1822,9 +1822,9 @@ void qed_cxt_hw_init_common(struct qed_hwfn *p_hwfn) qed_prs_init_common(p_hwfn); } -void qed_cxt_hw_init_pf(struct qed_hwfn *p_hwfn) +void qed_cxt_hw_init_pf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { - qed_qm_init_pf(p_hwfn); + qed_qm_init_pf(p_hwfn, p_ptt); qed_cm_init_pf(p_hwfn); qed_dq_init_pf(p_hwfn); qed_cdu_init_pf(p_hwfn); diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.h b/drivers/net/ethernet/qlogic/qed/qed_cxt.h index f34b2889f4bb..53ad532dc212 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.h +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.h @@ -172,19 +172,18 @@ void qed_cxt_hw_init_common(struct qed_hwfn *p_hwfn); /** * @brief qed_cxt_hw_init_pf - Initailze ILT and DQ, PF phase, per path. * - * - * * @param p_hwfn + * @param p_ptt */ -void qed_cxt_hw_init_pf(struct qed_hwfn *p_hwfn); +void qed_cxt_hw_init_pf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); /** * @brief qed_qm_init_pf - Initailze the QM PF phase, per path * * @param p_hwfn + * @param p_ptt */ - -void qed_qm_init_pf(struct qed_hwfn *p_hwfn); +void qed_qm_init_pf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); /** * @brief Reconfigures QM pf on the fly diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 249878533fd9..2df83be3ccf0 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -75,7 +75,8 @@ enum BAR_ID { BAR_ID_1 /* Used for doorbells */ }; -static u32 qed_hw_bar_size(struct qed_hwfn *p_hwfn, enum BAR_ID bar_id) +static u32 qed_hw_bar_size(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, enum BAR_ID bar_id) { u32 bar_reg = (bar_id == BAR_ID_0 ? PGLUE_B_REG_PF_BAR0_SIZE : PGLUE_B_REG_PF_BAR1_SIZE); @@ -84,7 +85,7 @@ static u32 qed_hw_bar_size(struct qed_hwfn *p_hwfn, enum BAR_ID bar_id) if (IS_VF(p_hwfn->cdev)) return 1 << 17; - val = qed_rd(p_hwfn, p_hwfn->p_main_ptt, bar_reg); + val = qed_rd(p_hwfn, p_ptt, bar_reg); if (val) return 1 << (val + 15); @@ -780,7 +781,7 @@ int qed_qm_reconf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) qed_init_clear_rt_data(p_hwfn); /* prepare QM portion of runtime array */ - qed_qm_init_pf(p_hwfn); + qed_qm_init_pf(p_hwfn, p_ptt); /* activate init tool on runtime array */ rc = qed_init_run(p_hwfn, p_ptt, PHASE_QM_PF, p_hwfn->rel_pf_id, @@ -1320,7 +1321,7 @@ qed_hw_init_pf_doorbell_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) int rc = 0; u8 cond; - db_bar_size = qed_hw_bar_size(p_hwfn, BAR_ID_1); + db_bar_size = qed_hw_bar_size(p_hwfn, p_ptt, BAR_ID_1); if (p_hwfn->cdev->num_hwfns > 1) db_bar_size /= 2; @@ -1431,7 +1432,7 @@ static int qed_hw_init_pf(struct qed_hwfn *p_hwfn, p_hwfn->qm_info.pf_rl = 100000; } - qed_cxt_hw_init_pf(p_hwfn); + qed_cxt_hw_init_pf(p_hwfn, p_ptt); qed_int_igu_init_rt(p_hwfn); @@ -1852,18 +1853,21 @@ int qed_hw_stop(struct qed_dev *cdev) return rc2; } -void qed_hw_stop_fastpath(struct qed_dev *cdev) +int qed_hw_stop_fastpath(struct qed_dev *cdev) { int j; for_each_hwfn(cdev, j) { struct qed_hwfn *p_hwfn = &cdev->hwfns[j]; - struct qed_ptt *p_ptt = p_hwfn->p_main_ptt; + struct qed_ptt *p_ptt; if (IS_VF(cdev)) { qed_vf_pf_int_cleanup(p_hwfn); continue; } + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) + return -EAGAIN; DP_VERBOSE(p_hwfn, NETIF_MSG_IFDOWN, "Shutting down the fastpath\n"); @@ -1881,17 +1885,28 @@ void qed_hw_stop_fastpath(struct qed_dev *cdev) /* Need to wait 1ms to guarantee SBs are cleared */ usleep_range(1000, 2000); + qed_ptt_release(p_hwfn, p_ptt); } + + return 0; } -void qed_hw_start_fastpath(struct qed_hwfn *p_hwfn) +int qed_hw_start_fastpath(struct qed_hwfn *p_hwfn) { + struct qed_ptt *p_ptt; + if (IS_VF(p_hwfn->cdev)) - return; + return 0; + + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) + return -EAGAIN; /* Re-open incoming traffic */ - qed_wr(p_hwfn, p_hwfn->p_main_ptt, - NIG_REG_RX_LLH_BRB_GATE_DNTFWD_PERPF, 0x0); + qed_wr(p_hwfn, p_ptt, NIG_REG_RX_LLH_BRB_GATE_DNTFWD_PERPF, 0x0); + qed_ptt_release(p_hwfn, p_ptt); + + return 0; } /* Free hwfn memory and resources acquired in hw_hwfn_prepare */ @@ -2697,9 +2712,9 @@ qed_get_hw_info(struct qed_hwfn *p_hwfn, return qed_hw_get_resc(p_hwfn, p_ptt); } -static int qed_get_dev_info(struct qed_dev *cdev) +static int qed_get_dev_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { - struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); + struct qed_dev *cdev = p_hwfn->cdev; u16 device_id_mask; u32 tmp; @@ -2721,15 +2736,13 @@ static int qed_get_dev_info(struct qed_dev *cdev) return -EBUSY; } - cdev->chip_num = (u16)qed_rd(p_hwfn, p_hwfn->p_main_ptt, - MISCS_REG_CHIP_NUM); - cdev->chip_rev = (u16)qed_rd(p_hwfn, p_hwfn->p_main_ptt, - MISCS_REG_CHIP_REV); + cdev->chip_num = (u16)qed_rd(p_hwfn, p_ptt, MISCS_REG_CHIP_NUM); + cdev->chip_rev = (u16)qed_rd(p_hwfn, p_ptt, MISCS_REG_CHIP_REV); + MASK_FIELD(CHIP_REV, cdev->chip_rev); /* Learn number of HW-functions */ - tmp = qed_rd(p_hwfn, p_hwfn->p_main_ptt, - MISCS_REG_CMT_ENABLED_FOR_PAIR); + tmp = qed_rd(p_hwfn, p_ptt, MISCS_REG_CMT_ENABLED_FOR_PAIR); if (tmp & (1 << p_hwfn->rel_pf_id)) { DP_NOTICE(cdev->hwfns, "device in CMT mode\n"); @@ -2738,11 +2751,10 @@ static int qed_get_dev_info(struct qed_dev *cdev) cdev->num_hwfns = 1; } - cdev->chip_bond_id = qed_rd(p_hwfn, p_hwfn->p_main_ptt, + cdev->chip_bond_id = qed_rd(p_hwfn, p_ptt, MISCS_REG_CHIP_TEST_REG) >> 4; MASK_FIELD(CHIP_BOND_ID, cdev->chip_bond_id); - cdev->chip_metal = (u16)qed_rd(p_hwfn, p_hwfn->p_main_ptt, - MISCS_REG_CHIP_METAL); + cdev->chip_metal = (u16)qed_rd(p_hwfn, p_ptt, MISCS_REG_CHIP_METAL); MASK_FIELD(CHIP_METAL, cdev->chip_metal); DP_INFO(cdev->hwfns, @@ -2795,7 +2807,7 @@ static int qed_hw_prepare_single(struct qed_hwfn *p_hwfn, /* First hwfn learns basic information, e.g., number of hwfns */ if (!p_hwfn->my_id) { - rc = qed_get_dev_info(p_hwfn->cdev); + rc = qed_get_dev_info(p_hwfn, p_hwfn->p_main_ptt); if (rc) goto err1; } @@ -2866,11 +2878,14 @@ int qed_hw_prepare(struct qed_dev *cdev, u8 __iomem *addr; /* adjust bar offset for second engine */ - addr = cdev->regview + qed_hw_bar_size(p_hwfn, BAR_ID_0) / 2; + addr = cdev->regview + + qed_hw_bar_size(p_hwfn, p_hwfn->p_main_ptt, + BAR_ID_0) / 2; p_regview = addr; - /* adjust doorbell bar offset for second engine */ - addr = cdev->doorbells + qed_hw_bar_size(p_hwfn, BAR_ID_1) / 2; + addr = cdev->doorbells + + qed_hw_bar_size(p_hwfn, p_hwfn->p_main_ptt, + BAR_ID_1) / 2; p_doorbell = addr; /* prepare second hw function */ diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h index 2c6637fd7ef6..341636da9964 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h +++ b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h @@ -165,17 +165,19 @@ int qed_hw_stop(struct qed_dev *cdev); * * @param cdev * + * @return int */ -void qed_hw_stop_fastpath(struct qed_dev *cdev); +int qed_hw_stop_fastpath(struct qed_dev *cdev); /** * @brief qed_hw_start_fastpath -restart fastpath traffic, * only if hw_stop_fastpath was called * - * @param cdev + * @param p_hwfn * + * @return int */ -void qed_hw_start_fastpath(struct qed_hwfn *p_hwfn); +int qed_hw_start_fastpath(struct qed_hwfn *p_hwfn); /** diff --git a/drivers/net/ethernet/qlogic/qed/qed_fcoe.c b/drivers/net/ethernet/qlogic/qed/qed_fcoe.c index f4b95345d1a5..21a58fffd02b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_fcoe.c +++ b/drivers/net/ethernet/qlogic/qed/qed_fcoe.c @@ -340,10 +340,10 @@ qed_sp_fcoe_conn_destroy(struct qed_hwfn *p_hwfn, static int qed_sp_fcoe_func_stop(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, enum spq_mode comp_mode, struct qed_spq_comp_cb *p_comp_addr) { - struct qed_ptt *p_ptt = p_hwfn->p_main_ptt; struct qed_spq_entry *p_ent = NULL; struct qed_sp_init_data init_data; u32 active_segs = 0; @@ -765,6 +765,7 @@ static struct qed_hash_fcoe_con *qed_fcoe_get_hash(struct qed_dev *cdev, static int qed_fcoe_stop(struct qed_dev *cdev) { + struct qed_ptt *p_ptt; int rc; if (!(cdev->flags & QED_FLAG_STORAGE_STARTED)) { @@ -778,10 +779,15 @@ static int qed_fcoe_stop(struct qed_dev *cdev) return -EINVAL; } + p_ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev)); + if (!p_ptt) + return -EAGAIN; + /* Stop the fcoe */ - rc = qed_sp_fcoe_func_stop(QED_LEADING_HWFN(cdev), + rc = qed_sp_fcoe_func_stop(QED_LEADING_HWFN(cdev), p_ptt, QED_SPQ_MODE_EBLOCK, NULL); cdev->flags &= ~QED_FLAG_STORAGE_STARTED; + qed_ptt_release(QED_LEADING_HWFN(cdev), p_ptt); return rc; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 9900f7a1f9f1..d56441da87c5 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -1929,7 +1929,11 @@ static int qed_start_vport(struct qed_dev *cdev, return rc; } - qed_hw_start_fastpath(p_hwfn); + rc = qed_hw_start_fastpath(p_hwfn); + if (rc) { + DP_ERR(cdev, "Failed to start VPORT fastpath\n"); + return rc; + } DP_VERBOSE(cdev, (QED_MSG_SPQ | NETIF_MSG_IFUP), "Started V-PORT %d with MTU %d\n", @@ -2172,7 +2176,13 @@ static int qed_start_txq(struct qed_dev *cdev, #define QED_HW_STOP_RETRY_LIMIT (10) static int qed_fastpath_stop(struct qed_dev *cdev) { - qed_hw_stop_fastpath(cdev); + int rc; + + rc = qed_hw_stop_fastpath(cdev); + if (rc) { + DP_ERR(cdev, "Failed to stop Fastpath\n"); + return rc; + } return 0; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c index 13e65d446ab3..09c86411918c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c @@ -1408,13 +1408,21 @@ int qed_ll2_establish_connection(struct qed_hwfn *p_hwfn, u8 connection_handle) struct qed_ll2_info *p_ll2_conn; struct qed_ll2_rx_queue *p_rx; struct qed_ll2_tx_queue *p_tx; + struct qed_ptt *p_ptt; int rc = -EINVAL; u32 i, capacity; u8 qid; + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) + return -EAGAIN; + p_ll2_conn = qed_ll2_handle_sanity_lock(p_hwfn, connection_handle); - if (!p_ll2_conn) - return -EINVAL; + if (!p_ll2_conn) { + rc = -EINVAL; + goto out; + } + p_rx = &p_ll2_conn->rx_queue; p_tx = &p_ll2_conn->tx_queue; @@ -1447,7 +1455,9 @@ int qed_ll2_establish_connection(struct qed_hwfn *p_hwfn, u8 connection_handle) p_tx->cur_completing_frag_num = 0; *p_tx->p_fw_cons = 0; - qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_CORE, &p_ll2_conn->cid); + rc = qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_CORE, &p_ll2_conn->cid); + if (rc) + goto out; qid = p_hwfn->hw_info.resc_start[QED_LL2_QUEUE] + connection_handle; p_ll2_conn->queue_id = qid; @@ -1461,26 +1471,28 @@ int qed_ll2_establish_connection(struct qed_hwfn *p_hwfn, u8 connection_handle) rc = qed_ll2_establish_connection_rx(p_hwfn, p_ll2_conn); if (rc) - return rc; + goto out; rc = qed_sp_ll2_tx_queue_start(p_hwfn, p_ll2_conn); if (rc) - return rc; + goto out; if (p_hwfn->hw_info.personality != QED_PCI_ETH_ROCE) - qed_wr(p_hwfn, p_hwfn->p_main_ptt, PRS_REG_USE_LIGHT_L2, 1); + qed_wr(p_hwfn, p_ptt, PRS_REG_USE_LIGHT_L2, 1); qed_ll2_establish_connection_ooo(p_hwfn, p_ll2_conn); if (p_ll2_conn->conn.conn_type == QED_LL2_TYPE_FCOE) { - qed_llh_add_protocol_filter(p_hwfn, p_hwfn->p_main_ptt, + qed_llh_add_protocol_filter(p_hwfn, p_ptt, 0x8906, 0, QED_LLH_FILTER_ETHERTYPE); - qed_llh_add_protocol_filter(p_hwfn, p_hwfn->p_main_ptt, + qed_llh_add_protocol_filter(p_hwfn, p_ptt, 0x8914, 0, QED_LLH_FILTER_ETHERTYPE); } +out: + qed_ptt_release(p_hwfn, p_ptt); return rc; } @@ -1831,23 +1843,30 @@ int qed_ll2_terminate_connection(struct qed_hwfn *p_hwfn, u8 connection_handle) { struct qed_ll2_info *p_ll2_conn = NULL; int rc = -EINVAL; + struct qed_ptt *p_ptt; + + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) + return -EAGAIN; p_ll2_conn = qed_ll2_handle_sanity_lock(p_hwfn, connection_handle); - if (!p_ll2_conn) - return -EINVAL; + if (!p_ll2_conn) { + rc = -EINVAL; + goto out; + } /* Stop Tx & Rx of connection, if needed */ if (QED_LL2_TX_REGISTERED(p_ll2_conn)) { rc = qed_sp_ll2_tx_queue_stop(p_hwfn, p_ll2_conn); if (rc) - return rc; + goto out; qed_ll2_txq_flush(p_hwfn, connection_handle); } if (QED_LL2_RX_REGISTERED(p_ll2_conn)) { rc = qed_sp_ll2_rx_queue_stop(p_hwfn, p_ll2_conn); if (rc) - return rc; + goto out; qed_ll2_rxq_flush(p_hwfn, connection_handle); } @@ -1855,14 +1874,16 @@ int qed_ll2_terminate_connection(struct qed_hwfn *p_hwfn, u8 connection_handle) qed_ooo_release_all_isles(p_hwfn, p_hwfn->p_ooo_info); if (p_ll2_conn->conn.conn_type == QED_LL2_TYPE_FCOE) { - qed_llh_remove_protocol_filter(p_hwfn, p_hwfn->p_main_ptt, + qed_llh_remove_protocol_filter(p_hwfn, p_ptt, 0x8906, 0, QED_LLH_FILTER_ETHERTYPE); - qed_llh_remove_protocol_filter(p_hwfn, p_hwfn->p_main_ptt, + qed_llh_remove_protocol_filter(p_hwfn, p_ptt, 0x8914, 0, QED_LLH_FILTER_ETHERTYPE); } +out: + qed_ptt_release(p_hwfn, p_ptt); return rc; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_spq.c b/drivers/net/ethernet/qlogic/qed/qed_spq.c index 13f715569253..f6423a139ca0 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_spq.c +++ b/drivers/net/ethernet/qlogic/qed/qed_spq.c @@ -119,6 +119,7 @@ static int qed_spq_block(struct qed_hwfn *p_hwfn, u8 *p_fw_ret, bool skip_quick_poll) { struct qed_spq_comp_done *comp_done; + struct qed_ptt *p_ptt; int rc; /* A relatively short polling period w/o sleeping, to allow the FW to @@ -135,8 +136,14 @@ static int qed_spq_block(struct qed_hwfn *p_hwfn, if (!rc) return 0; + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) { + DP_NOTICE(p_hwfn, "ptt, failed to acquire\n"); + return -EAGAIN; + } + DP_INFO(p_hwfn, "Ramrod is stuck, requesting MCP drain\n"); - rc = qed_mcp_drain(p_hwfn, p_hwfn->p_main_ptt); + rc = qed_mcp_drain(p_hwfn, p_ptt); if (rc) { DP_NOTICE(p_hwfn, "MCP drain failed\n"); goto err; @@ -145,15 +152,18 @@ static int qed_spq_block(struct qed_hwfn *p_hwfn, /* Retry after drain */ rc = __qed_spq_block(p_hwfn, p_ent, p_fw_ret, true); if (!rc) - return 0; + goto out; comp_done = (struct qed_spq_comp_done *)p_ent->comp_cb.cookie; - if (comp_done->done == 1) { + if (comp_done->done == 1) if (p_fw_ret) *p_fw_ret = comp_done->fw_return_code; - return 0; - } +out: + qed_ptt_release(p_hwfn, p_ptt); + return 0; + err: + qed_ptt_release(p_hwfn, p_ptt); DP_NOTICE(p_hwfn, "Ramrod is stuck [CID %08x cmd %02x protocol %02x echo %04x]\n", le32_to_cpu(p_ent->elem.hdr.cid), -- cgit From 60afed72f51c7445aa06dc953b05f5672b607860 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Thu, 6 Apr 2017 15:58:30 +0300 Subject: qed: Configure cacheline size in HW Default HW configuration is optimal for an architecture where cache line size is 64B. During chip initialization, properly initialize the cache line size in HW to avoid possible redundant PCI transactions. Signed-off-by: Tomer Tayar Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dev.c | 53 ++++++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_reg_addr.h | 1 + 2 files changed, 54 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 2df83be3ccf0..c5f48860a934 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1192,6 +1192,57 @@ static void qed_init_cau_rt_data(struct qed_dev *cdev) } } +static void qed_init_cache_line_size(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt) +{ + u32 val, wr_mbs, cache_line_size; + + val = qed_rd(p_hwfn, p_ptt, PSWRQ2_REG_WR_MBS0); + switch (val) { + case 0: + wr_mbs = 128; + break; + case 1: + wr_mbs = 256; + break; + case 2: + wr_mbs = 512; + break; + default: + DP_INFO(p_hwfn, + "Unexpected value of PSWRQ2_REG_WR_MBS0 [0x%x]. Avoid configuring PGLUE_B_REG_CACHE_LINE_SIZE.\n", + val); + return; + } + + cache_line_size = min_t(u32, L1_CACHE_BYTES, wr_mbs); + switch (cache_line_size) { + case 32: + val = 0; + break; + case 64: + val = 1; + break; + case 128: + val = 2; + break; + case 256: + val = 3; + break; + default: + DP_INFO(p_hwfn, + "Unexpected value of cache line size [0x%x]. Avoid configuring PGLUE_B_REG_CACHE_LINE_SIZE.\n", + cache_line_size); + } + + if (L1_CACHE_BYTES > wr_mbs) + DP_INFO(p_hwfn, + "The cache line size for padding is suboptimal for performance [OS cache line size 0x%x, wr mbs 0x%x]\n", + L1_CACHE_BYTES, wr_mbs); + + STORE_RT_REG(p_hwfn, PGLUE_REG_B_CACHE_LINE_SIZE_RT_OFFSET, val); +} + static int qed_hw_init_common(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, int hw_mode) { @@ -1240,6 +1291,8 @@ static int qed_hw_init_common(struct qed_hwfn *p_hwfn, qed_wr(p_hwfn, p_ptt, NIG_REG_STORM_OUT_EN, 0); qed_port_unpretend(p_hwfn, p_ptt); + qed_init_cache_line_size(p_hwfn, p_ptt); + rc = qed_init_run(p_hwfn, p_ptt, PHASE_ENGINE, ANY_PHASE_ID, hw_mode); if (rc) return rc; diff --git a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h index 6d4ac7e2ee83..e65397360ab4 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h +++ b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h @@ -1551,6 +1551,7 @@ #define NIG_REG_TSGEN_FREE_CNT_VALUE_LSB 0x5088a8UL #define NIG_REG_TSGEN_FREE_CNT_VALUE_MSB 0x5088acUL #define NIG_REG_PTP_LATCH_OSTS_PKT_TIME 0x509040UL +#define PSWRQ2_REG_WR_MBS0 0x240400UL #define PGLUE_B_REG_PGL_ADDR_E8_F0_K2 0x2aaf98UL #define PGLUE_B_REG_PGL_ADDR_EC_F0_K2 0x2aaf9cUL -- cgit From bd1cc771f9d2374f9f1c0edbd01d27893723e8da Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Thu, 6 Apr 2017 15:58:31 +0300 Subject: qed: Don't close the OUT_EN during init Before initializing the chip's engine, driver currently closes a set of registers on the HW's ingress flow to prevent packets from slipping in while they're not supposed to. This configuration is insufficient, as there are some scenarios where packets would still arrive even when said registers are set, but the management firmware already closes other per-port registers that do suffice, making this setting unnecessray. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dev.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index c5f48860a934..fb759ba34b8d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1279,18 +1279,6 @@ static int qed_hw_init_common(struct qed_hwfn *p_hwfn, qed_cxt_hw_init_common(p_hwfn); - /* Close gate from NIG to BRB/Storm; By default they are open, but - * we close them to prevent NIG from passing data to reset blocks. - * Should have been done in the ENGINE phase, but init-tool lacks - * proper port-pretend capabilities. - */ - qed_wr(p_hwfn, p_ptt, NIG_REG_RX_BRB_OUT_EN, 0); - qed_wr(p_hwfn, p_ptt, NIG_REG_STORM_OUT_EN, 0); - qed_port_pretend(p_hwfn, p_ptt, p_hwfn->port_id ^ 1); - qed_wr(p_hwfn, p_ptt, NIG_REG_RX_BRB_OUT_EN, 0); - qed_wr(p_hwfn, p_ptt, NIG_REG_STORM_OUT_EN, 0); - qed_port_unpretend(p_hwfn, p_ptt); - qed_init_cache_line_size(p_hwfn, p_ptt); rc = qed_init_run(p_hwfn, p_ptt, PHASE_ENGINE, ANY_PHASE_ID, hw_mode); -- cgit From 3ddc48dc1953c364ec0ac4c21a75e8063fddb53e Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Thu, 6 Apr 2017 15:58:32 +0300 Subject: qed: Add missing stat for new isles Firmware provides a statistic for the number of out-of-order isles it used - fill it in the iscsi-related statistics. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_iscsi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c index 112b96fba433..38273f41201d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c +++ b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c @@ -864,6 +864,8 @@ static void _qed_iscsi_get_tstats(struct qed_hwfn *p_hwfn, HILO_64_REGPAIR(tstats.iscsi_rx_bytes_cnt); p_stats->iscsi_rx_packet_cnt = HILO_64_REGPAIR(tstats.iscsi_rx_packet_cnt); + p_stats->iscsi_rx_new_ooo_isle_events_cnt = + HILO_64_REGPAIR(tstats.iscsi_rx_new_ooo_isle_events_cnt); p_stats->iscsi_cmdq_threshold_cnt = le32_to_cpu(tstats.iscsi_cmdq_threshold_cnt); p_stats->iscsi_rq_threshold_cnt = -- cgit From 08737a3fa30a4c6c10b4c4b682125c7d3c494094 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Thu, 6 Apr 2017 15:58:33 +0300 Subject: qed: Inform qedi the number of possible CQs Now that management firmware is capable of telling us the number of CQs available for a given PF, qed needs to communicate the number to qedi so it would know have many to use. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 3 ++- drivers/net/ethernet/qlogic/qed/qed_dev.c | 7 ++++++- drivers/net/ethernet/qlogic/qed/qed_iscsi.c | 11 +++++++++++ include/linux/qed/qed_iscsi_if.h | 2 ++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index d8bcc21a4f69..4896ee0cc458 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -225,8 +225,9 @@ enum QED_FEATURE { QED_PF_L2_QUE, QED_VF, QED_RDMA_CNQ, - QED_VF_L2_QUE, + QED_ISCSI_CQ, QED_FCOE_CQ, + QED_VF_L2_QUE, QED_MAX_FEATURES, }; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index fb759ba34b8d..fad73195010d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -2045,12 +2045,17 @@ static void qed_hw_set_feat(struct qed_hwfn *p_hwfn) QED_VF_L2_QUE)); } + if (p_hwfn->hw_info.personality == QED_PCI_ISCSI) + feat_num[QED_ISCSI_CQ] = min_t(u32, RESC_NUM(p_hwfn, QED_SB), + RESC_NUM(p_hwfn, + QED_CMDQS_CQS)); DP_VERBOSE(p_hwfn, NETIF_MSG_PROBE, - "#PF_L2_QUEUES=%d VF_L2_QUEUES=%d #ROCE_CNQ=%d #SBS=%d\n", + "#PF_L2_QUEUES=%d VF_L2_QUEUES=%d #ROCE_CNQ=%d ISCSI_CQ=%d #SBS=%d\n", (int)FEAT_NUM(p_hwfn, QED_PF_L2_QUE), (int)FEAT_NUM(p_hwfn, QED_VF_L2_QUE), (int)FEAT_NUM(p_hwfn, QED_RDMA_CNQ), + (int)FEAT_NUM(p_hwfn, QED_ISCSI_CQ), RESC_NUM(p_hwfn, QED_SB)); } diff --git a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c index 38273f41201d..417691cf4fd6 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c +++ b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c @@ -181,6 +181,15 @@ qed_sp_iscsi_func_start(struct qed_hwfn *p_hwfn, p_params = &p_hwfn->pf_params.iscsi_pf_params; p_queue = &p_init->q_params; + /* Sanity */ + if (p_params->num_queues > p_hwfn->hw_info.feat_num[QED_ISCSI_CQ]) { + DP_ERR(p_hwfn, + "Cannot satisfy CQ amount. Queues requested %d, CQs available %d. Aborting function start\n", + p_params->num_queues, + p_hwfn->hw_info.resc_num[QED_ISCSI_CQ]); + return -EINVAL; + } + SET_FIELD(p_init->hdr.flags, ISCSI_SLOW_PATH_HDR_LAYER_CODE, ISCSI_SLOW_PATH_LAYER_CODE); p_init->hdr.op_code = ISCSI_RAMROD_CMD_ID_INIT_FUNC; @@ -1012,6 +1021,8 @@ static int qed_fill_iscsi_dev_info(struct qed_dev *cdev, info->secondary_bdq_rq_addr = qed_iscsi_get_secondary_bdq_prod(hwfn, BDQ_ID_RQ); + info->num_cqs = FEAT_NUM(hwfn, QED_ISCSI_CQ); + return rc; } diff --git a/include/linux/qed/qed_iscsi_if.h b/include/linux/qed/qed_iscsi_if.h index f70bb81b8b6a..3414649133d2 100644 --- a/include/linux/qed/qed_iscsi_if.h +++ b/include/linux/qed/qed_iscsi_if.h @@ -67,6 +67,8 @@ struct qed_dev_iscsi_info { void __iomem *primary_dbq_rq_addr; void __iomem *secondary_bdq_rq_addr; + + u8 num_cqs; }; struct qed_iscsi_id_params { -- cgit From 2f2b2614e8937e95c29ac733d8956a48771613d2 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Thu, 6 Apr 2017 15:58:34 +0300 Subject: qed: Provide iSCSI statistics to management Management firmware can query for some basic iSCSI-related statistics. Provide those just as we do for other protocols. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_iscsi.c | 20 ++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_iscsi.h | 14 +++++++++++++- drivers/net/ethernet/qlogic/qed/qed_main.c | 5 +++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c index 417691cf4fd6..339c91dfa658 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_iscsi.c +++ b/drivers/net/ethernet/qlogic/qed/qed_iscsi.c @@ -1324,6 +1324,26 @@ static int qed_iscsi_stats(struct qed_dev *cdev, struct qed_iscsi_stats *stats) return qed_iscsi_get_stats(QED_LEADING_HWFN(cdev), stats); } +void qed_get_protocol_stats_iscsi(struct qed_dev *cdev, + struct qed_mcp_iscsi_stats *stats) +{ + struct qed_iscsi_stats proto_stats; + + /* Retrieve FW statistics */ + memset(&proto_stats, 0, sizeof(proto_stats)); + if (qed_iscsi_stats(cdev, &proto_stats)) { + DP_VERBOSE(cdev, QED_MSG_STORAGE, + "Failed to collect ISCSI statistics\n"); + return; + } + + /* Translate FW statistics into struct */ + stats->rx_pdus = proto_stats.iscsi_rx_total_pdu_cnt; + stats->tx_pdus = proto_stats.iscsi_tx_total_pdu_cnt; + stats->rx_bytes = proto_stats.iscsi_rx_bytes_cnt; + stats->tx_bytes = proto_stats.iscsi_tx_bytes_cnt; +} + static const struct qed_iscsi_ops qed_iscsi_ops_pass = { .common = &qed_common_ops_pass, .ll2 = &qed_ll2_ops_pass, diff --git a/drivers/net/ethernet/qlogic/qed/qed_iscsi.h b/drivers/net/ethernet/qlogic/qed/qed_iscsi.h index 20c187f4ed0b..ae98f772cbc0 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_iscsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_iscsi.h @@ -64,13 +64,25 @@ void qed_iscsi_setup(struct qed_hwfn *p_hwfn, void qed_iscsi_free(struct qed_hwfn *p_hwfn, struct qed_iscsi_info *p_iscsi_info); + +/** + * @brief - Fills provided statistics struct with statistics. + * + * @param cdev + * @param stats - points to struct that will be filled with statistics. + */ +void qed_get_protocol_stats_iscsi(struct qed_dev *cdev, + struct qed_mcp_iscsi_stats *stats); #else /* IS_ENABLED(CONFIG_QED_ISCSI) */ static inline struct qed_iscsi_info *qed_iscsi_alloc( struct qed_hwfn *p_hwfn) { return NULL; } static inline void qed_iscsi_setup(struct qed_hwfn *p_hwfn, struct qed_iscsi_info *p_iscsi_info) {} static inline void qed_iscsi_free(struct qed_hwfn *p_hwfn, - struct qed_iscsi_info *p_iscsi_info) {} + struct qed_iscsi_info *p_iscsi_info) {} +static inline void +qed_get_protocol_stats_iscsi(struct qed_dev *cdev, + struct qed_mcp_iscsi_stats *stats) {} #endif /* IS_ENABLED(CONFIG_QED_ISCSI) */ #endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index aab89ded3aa4..029f431e89ec 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -55,6 +55,8 @@ #include "qed_dev_api.h" #include "qed_ll2.h" #include "qed_fcoe.h" +#include "qed_iscsi.h" + #include "qed_mcp.h" #include "qed_hw.h" #include "qed_selftest.h" @@ -1679,6 +1681,9 @@ void qed_get_protocol_stats(struct qed_dev *cdev, case QED_MCP_FCOE_STATS: qed_get_protocol_stats_fcoe(cdev, &stats->fcoe_stats); break; + case QED_MCP_ISCSI_STATS: + qed_get_protocol_stats_iscsi(cdev, &stats->iscsi_stats); + break; default: DP_ERR(cdev, "Invalid protocol type = %d\n", type); return; -- cgit From 1eec2437d14c76af3b1271011940036eb0d7d3cf Mon Sep 17 00:00:00 2001 From: Michal Kalderon Date: Thu, 6 Apr 2017 15:58:35 +0300 Subject: qed: Make OOO archipelagos into an array No need to maintain the various open archipelagos as a list - The maximal number of them is known, and we can use the CID as key for random-access into the array. Signed-off-by: Michal Kalderon Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_ooo.c | 102 ++++++++---------------------- drivers/net/ethernet/qlogic/qed/qed_ooo.h | 6 +- 2 files changed, 30 insertions(+), 78 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_ooo.c b/drivers/net/ethernet/qlogic/qed/qed_ooo.c index 378afce58b3f..db96670192c7 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ooo.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ooo.c @@ -41,6 +41,7 @@ #include "qed_iscsi.h" #include "qed_ll2.h" #include "qed_ooo.h" +#include "qed_cxt.h" static struct qed_ooo_archipelago *qed_ooo_seek_archipelago(struct qed_hwfn *p_hwfn, @@ -48,15 +49,18 @@ static struct qed_ooo_archipelago *p_ooo_info, u32 cid) { - struct qed_ooo_archipelago *p_archipelago = NULL; + u32 idx = (cid & 0xffff) - p_ooo_info->cid_base; + struct qed_ooo_archipelago *p_archipelago; - list_for_each_entry(p_archipelago, - &p_ooo_info->archipelagos_list, list_entry) { - if (p_archipelago->cid == cid) - return p_archipelago; - } + if (idx >= p_ooo_info->max_num_archipelagos) + return NULL; - return NULL; + p_archipelago = &p_ooo_info->p_archipelagos_mem[idx]; + + if (list_empty(&p_archipelago->isles_list)) + return NULL; + + return p_archipelago; } static struct qed_ooo_isle *qed_ooo_seek_isle(struct qed_hwfn *p_hwfn, @@ -97,8 +101,8 @@ void qed_ooo_save_history_entry(struct qed_hwfn *p_hwfn, struct qed_ooo_info *qed_ooo_alloc(struct qed_hwfn *p_hwfn) { + u16 max_num_archipelagos = 0, cid_base; struct qed_ooo_info *p_ooo_info; - u16 max_num_archipelagos = 0; u16 max_num_isles = 0; u32 i; @@ -110,6 +114,7 @@ struct qed_ooo_info *qed_ooo_alloc(struct qed_hwfn *p_hwfn) max_num_archipelagos = p_hwfn->pf_params.iscsi_pf_params.num_cons; max_num_isles = QED_MAX_NUM_ISLES + max_num_archipelagos; + cid_base = (u16)qed_cxt_get_proto_cid_start(p_hwfn, PROTOCOLID_ISCSI); if (!max_num_archipelagos) { DP_NOTICE(p_hwfn, @@ -121,11 +126,12 @@ struct qed_ooo_info *qed_ooo_alloc(struct qed_hwfn *p_hwfn) if (!p_ooo_info) return NULL; + p_ooo_info->cid_base = cid_base; + p_ooo_info->max_num_archipelagos = max_num_archipelagos; + INIT_LIST_HEAD(&p_ooo_info->free_buffers_list); INIT_LIST_HEAD(&p_ooo_info->ready_buffers_list); INIT_LIST_HEAD(&p_ooo_info->free_isles_list); - INIT_LIST_HEAD(&p_ooo_info->free_archipelagos_list); - INIT_LIST_HEAD(&p_ooo_info->archipelagos_list); p_ooo_info->p_isles_mem = kcalloc(max_num_isles, sizeof(struct qed_ooo_isle), @@ -146,11 +152,8 @@ struct qed_ooo_info *qed_ooo_alloc(struct qed_hwfn *p_hwfn) if (!p_ooo_info->p_archipelagos_mem) goto no_archipelagos_mem; - for (i = 0; i < max_num_archipelagos; i++) { + for (i = 0; i < max_num_archipelagos; i++) INIT_LIST_HEAD(&p_ooo_info->p_archipelagos_mem[i].isles_list); - list_add_tail(&p_ooo_info->p_archipelagos_mem[i].list_entry, - &p_ooo_info->free_archipelagos_list); - } p_ooo_info->ooo_history.p_cqes = kcalloc(QED_MAX_NUM_OOO_HISTORY_ENTRIES, @@ -178,21 +181,9 @@ void qed_ooo_release_connection_isles(struct qed_hwfn *p_hwfn, struct qed_ooo_archipelago *p_archipelago; struct qed_ooo_buffer *p_buffer; struct qed_ooo_isle *p_isle; - bool b_found = false; - - if (list_empty(&p_ooo_info->archipelagos_list)) - return; - list_for_each_entry(p_archipelago, - &p_ooo_info->archipelagos_list, list_entry) { - if (p_archipelago->cid == cid) { - list_del(&p_archipelago->list_entry); - b_found = true; - break; - } - } - - if (!b_found) + p_archipelago = qed_ooo_seek_archipelago(p_hwfn, p_ooo_info, cid); + if (!p_archipelago) return; while (!list_empty(&p_archipelago->isles_list)) { @@ -216,27 +207,21 @@ void qed_ooo_release_connection_isles(struct qed_hwfn *p_hwfn, list_add_tail(&p_isle->list_entry, &p_ooo_info->free_isles_list); } - - list_add_tail(&p_archipelago->list_entry, - &p_ooo_info->free_archipelagos_list); } void qed_ooo_release_all_isles(struct qed_hwfn *p_hwfn, struct qed_ooo_info *p_ooo_info) { - struct qed_ooo_archipelago *p_arch; + struct qed_ooo_archipelago *p_archipelago; struct qed_ooo_buffer *p_buffer; struct qed_ooo_isle *p_isle; + u32 i; - while (!list_empty(&p_ooo_info->archipelagos_list)) { - p_arch = list_first_entry(&p_ooo_info->archipelagos_list, - struct qed_ooo_archipelago, - list_entry); - - list_del(&p_arch->list_entry); + for (i = 0; i < p_ooo_info->max_num_archipelagos; i++) { + p_archipelago = &(p_ooo_info->p_archipelagos_mem[i]); - while (!list_empty(&p_arch->isles_list)) { - p_isle = list_first_entry(&p_arch->isles_list, + while (!list_empty(&p_archipelago->isles_list)) { + p_isle = list_first_entry(&p_archipelago->isles_list, struct qed_ooo_isle, list_entry); @@ -258,8 +243,6 @@ void qed_ooo_release_all_isles(struct qed_hwfn *p_hwfn, list_add_tail(&p_isle->list_entry, &p_ooo_info->free_isles_list); } - list_add_tail(&p_arch->list_entry, - &p_ooo_info->free_archipelagos_list); } if (!list_empty(&p_ooo_info->ready_buffers_list)) list_splice_tail_init(&p_ooo_info->ready_buffers_list, @@ -378,12 +361,6 @@ void qed_ooo_delete_isles(struct qed_hwfn *p_hwfn, p_ooo_info->cur_isles_number--; list_add(&p_isle->list_entry, &p_ooo_info->free_isles_list); } - - if (list_empty(&p_archipelago->isles_list)) { - list_del(&p_archipelago->list_entry); - list_add(&p_archipelago->list_entry, - &p_ooo_info->free_archipelagos_list); - } } void qed_ooo_add_new_isle(struct qed_hwfn *p_hwfn, @@ -426,28 +403,10 @@ void qed_ooo_add_new_isle(struct qed_hwfn *p_hwfn, return; } - if (!p_archipelago && - !list_empty(&p_ooo_info->free_archipelagos_list)) { - p_archipelago = - list_first_entry(&p_ooo_info->free_archipelagos_list, - struct qed_ooo_archipelago, list_entry); + if (!p_archipelago) { + u32 idx = (cid & 0xffff) - p_ooo_info->cid_base; - list_del(&p_archipelago->list_entry); - if (!list_empty(&p_archipelago->isles_list)) { - DP_NOTICE(p_hwfn, - "Free OOO connection is not empty\n"); - INIT_LIST_HEAD(&p_archipelago->isles_list); - } - p_archipelago->cid = cid; - list_add(&p_archipelago->list_entry, - &p_ooo_info->archipelagos_list); - } else if (!p_archipelago) { - DP_NOTICE(p_hwfn, "No more free OOO connections\n"); - list_add(&p_isle->list_entry, - &p_ooo_info->free_isles_list); - list_add(&p_buffer->list_entry, - &p_ooo_info->free_buffers_list); - return; + p_archipelago = &p_ooo_info->p_archipelagos_mem[idx]; } list_add(&p_buffer->list_entry, &p_isle->buffers_list); @@ -517,11 +476,6 @@ void qed_ooo_join_isles(struct qed_hwfn *p_hwfn, } else { list_splice_tail_init(&p_right_isle->buffers_list, &p_ooo_info->ready_buffers_list); - if (list_empty(&p_archipelago->isles_list)) { - list_del(&p_archipelago->list_entry); - list_add(&p_archipelago->list_entry, - &p_ooo_info->free_archipelagos_list); - } } list_add_tail(&p_right_isle->list_entry, &p_ooo_info->free_isles_list); } diff --git a/drivers/net/ethernet/qlogic/qed/qed_ooo.h b/drivers/net/ethernet/qlogic/qed/qed_ooo.h index 4f138fb5f533..791ad0f8b759 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ooo.h +++ b/drivers/net/ethernet/qlogic/qed/qed_ooo.h @@ -60,9 +60,7 @@ struct qed_ooo_isle { }; struct qed_ooo_archipelago { - struct list_head list_entry; struct list_head isles_list; - u32 cid; }; struct qed_ooo_history { @@ -75,14 +73,14 @@ struct qed_ooo_info { struct list_head free_buffers_list; struct list_head ready_buffers_list; struct list_head free_isles_list; - struct list_head free_archipelagos_list; - struct list_head archipelagos_list; struct qed_ooo_archipelago *p_archipelagos_mem; struct qed_ooo_isle *p_isles_mem; struct qed_ooo_history ooo_history; u32 cur_isles_number; u32 max_isles_number; u32 gen_isles_number; + u16 max_num_archipelagos; + u16 cid_base; }; #if IS_ENABLED(CONFIG_QED_ISCSI) -- cgit From d0dd989f97d8d4ca44cd9bf2f34596e159236db2 Mon Sep 17 00:00:00 2001 From: Majd Dibbiny Date: Thu, 23 Feb 2017 10:52:19 +0200 Subject: net/mlx5: Update the list of the PCI supported devices Rename the ConnectX-5 PCIe 4.0 to be ConnectX-5 Ex. Also add the upcoming ConnectX-6 and it's VF IDs to the list. Signed-off-by: Majd Dibbiny Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 60154a175bd3..9c2bec732af9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1514,8 +1514,10 @@ static const struct pci_device_id mlx5_core_pci_table[] = { { PCI_VDEVICE(MELLANOX, 0x1016), MLX5_PCI_DEV_IS_VF}, /* ConnectX-4LX VF */ { PCI_VDEVICE(MELLANOX, 0x1017) }, /* ConnectX-5, PCIe 3.0 */ { PCI_VDEVICE(MELLANOX, 0x1018), MLX5_PCI_DEV_IS_VF}, /* ConnectX-5 VF */ - { PCI_VDEVICE(MELLANOX, 0x1019) }, /* ConnectX-5, PCIe 4.0 */ - { PCI_VDEVICE(MELLANOX, 0x101a), MLX5_PCI_DEV_IS_VF}, /* ConnectX-5, PCIe 4.0 VF */ + { PCI_VDEVICE(MELLANOX, 0x1019) }, /* ConnectX-5 Ex */ + { PCI_VDEVICE(MELLANOX, 0x101a), MLX5_PCI_DEV_IS_VF}, /* ConnectX-5 Ex VF */ + { PCI_VDEVICE(MELLANOX, 0x101b) }, /* ConnectX-6 */ + { PCI_VDEVICE(MELLANOX, 0x101c), MLX5_PCI_DEV_IS_VF}, /* ConnectX-6 VF */ { 0, } }; -- cgit From 102722fc6832a16850c05595b98c9232549d99f3 Mon Sep 17 00:00:00 2001 From: Guy Ergas Date: Mon, 20 Feb 2017 16:18:17 +0200 Subject: net/mlx5e: Add support for RXFCS feature flag Add support for rx-fcs flag from ethtool. In case this flag is set, update all RQs to scatter the FCS data into the packet. Signed-off-by: Guy Ergas Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 1 + drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 71 +++++++++++++++++++++++ include/linux/mlx5/mlx5_ifc.h | 1 + 3 files changed, 73 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 150fb52a0737..a58031da7fad 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -221,6 +221,7 @@ struct mlx5e_params { u8 toeplitz_hash_key[40]; u32 indirection_rqt[MLX5E_INDIR_RQT_SIZE]; bool vlan_strip_disable; + bool scatter_fcs_en; bool rx_am_enabled; u32 lro_timeout; u32 pflags; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index d5248637d44f..83796ce17fc3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -760,6 +760,37 @@ static int mlx5e_modify_rq_state(struct mlx5e_rq *rq, int curr_state, return err; } +static int mlx5e_modify_rq_scatter_fcs(struct mlx5e_rq *rq, bool enable) +{ + struct mlx5e_channel *c = rq->channel; + struct mlx5e_priv *priv = c->priv; + struct mlx5_core_dev *mdev = priv->mdev; + + void *in; + void *rqc; + int inlen; + int err; + + inlen = MLX5_ST_SZ_BYTES(modify_rq_in); + in = mlx5_vzalloc(inlen); + if (!in) + return -ENOMEM; + + rqc = MLX5_ADDR_OF(modify_rq_in, in, ctx); + + MLX5_SET(modify_rq_in, in, rq_state, MLX5_RQC_STATE_RDY); + MLX5_SET64(modify_rq_in, in, modify_bitmask, + MLX5_MODIFY_RQ_IN_MODIFY_BITMASK_SCATTER_FCS); + MLX5_SET(rqc, rqc, scatter_fcs, enable); + MLX5_SET(rqc, rqc, state, MLX5_RQC_STATE_RDY); + + err = mlx5_core_modify_rq(mdev, rq->rqn, in, inlen); + + kvfree(in); + + return err; +} + static int mlx5e_modify_rq_vsd(struct mlx5e_rq *rq, bool vsd) { struct mlx5e_channel *c = rq->channel; @@ -1834,6 +1865,7 @@ static void mlx5e_build_rq_param(struct mlx5e_priv *priv, MLX5_SET(wq, wq, pd, priv->mdev->mlx5e_res.pdn); MLX5_SET(rqc, rqc, counter_set_id, priv->q_counter); MLX5_SET(rqc, rqc, vsd, params->vlan_strip_disable); + MLX5_SET(rqc, rqc, scatter_fcs, params->scatter_fcs_en); param->wq.buf_numa_node = dev_to_node(&priv->mdev->pdev->dev); param->wq.linear = 1; @@ -2904,6 +2936,20 @@ void mlx5e_destroy_direct_tirs(struct mlx5e_priv *priv) mlx5e_destroy_tir(priv->mdev, &priv->direct_tir[i]); } +static int mlx5e_modify_channels_scatter_fcs(struct mlx5e_channels *chs, bool enable) +{ + int err = 0; + int i; + + for (i = 0; i < chs->num; i++) { + err = mlx5e_modify_rq_scatter_fcs(&chs->c[i]->rq, enable); + if (err) + return err; + } + + return 0; +} + int mlx5e_modify_channels_vsd(struct mlx5e_channels *chs, bool vsd) { int err = 0; @@ -3121,6 +3167,23 @@ static int set_feature_rx_all(struct net_device *netdev, bool enable) return mlx5_set_port_fcs(mdev, !enable); } +static int set_feature_rx_fcs(struct net_device *netdev, bool enable) +{ + struct mlx5e_priv *priv = netdev_priv(netdev); + int err; + + mutex_lock(&priv->state_lock); + + priv->channels.params.scatter_fcs_en = enable; + err = mlx5e_modify_channels_scatter_fcs(&priv->channels, enable); + if (err) + priv->channels.params.scatter_fcs_en = !enable; + + mutex_unlock(&priv->state_lock); + + return err; +} + static int set_feature_rx_vlan(struct net_device *netdev, bool enable) { struct mlx5e_priv *priv = netdev_priv(netdev); @@ -3194,6 +3257,8 @@ static int mlx5e_set_features(struct net_device *netdev, set_feature_tc_num_filters); err |= mlx5e_handle_feature(netdev, features, NETIF_F_RXALL, set_feature_rx_all); + err |= mlx5e_handle_feature(netdev, features, NETIF_F_RXFCS, + set_feature_rx_fcs); err |= mlx5e_handle_feature(netdev, features, NETIF_F_HW_VLAN_CTAG_RX, set_feature_rx_vlan); #ifdef CONFIG_RFS_ACCEL @@ -3908,6 +3973,9 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) if (fcs_supported) netdev->hw_features |= NETIF_F_RXALL; + if (MLX5_CAP_ETH(mdev, scatter_fcs)) + netdev->hw_features |= NETIF_F_RXFCS; + netdev->features = netdev->hw_features; if (!priv->channels.params.lro_en) netdev->features &= ~NETIF_F_LRO; @@ -3915,6 +3983,9 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) if (fcs_enabled) netdev->features &= ~NETIF_F_RXALL; + if (!priv->channels.params.scatter_fcs_en) + netdev->features &= ~NETIF_F_RXFCS; + #define FT_CAP(f) MLX5_CAP_FLOWTABLE(mdev, flow_table_properties_nic_receive.f) if (FT_CAP(flow_modify_en) && FT_CAP(modify_root) && diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 56bc842b0620..1993adbd2c82 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -5122,6 +5122,7 @@ struct mlx5_ifc_modify_rq_out_bits { enum { MLX5_MODIFY_RQ_IN_MODIFY_BITMASK_VSD = 1ULL << 1, + MLX5_MODIFY_RQ_IN_MODIFY_BITMASK_SCATTER_FCS = 1ULL << 2, MLX5_MODIFY_RQ_IN_MODIFY_BITMASK_RQ_COUNTER_SET_ID = 1ULL << 3, }; -- cgit From f6d96a2092bf448be417575b4725523072f4fc19 Mon Sep 17 00:00:00 2001 From: Guy Ergas Date: Mon, 20 Feb 2017 17:31:20 +0200 Subject: net/mlx5e: Make mlx5e_modify_rqs_vsd a static function Make mlx5e_modify_rqs_vsd a static function and remove from en.h in order to reduce redundant exposure of functions. Signed-off-by: Guy Ergas Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 -- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index a58031da7fad..b7feecfbb5a5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -843,8 +843,6 @@ int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __always_unused __be16 proto, void mlx5e_enable_vlan_filter(struct mlx5e_priv *priv); void mlx5e_disable_vlan_filter(struct mlx5e_priv *priv); -int mlx5e_modify_channels_vsd(struct mlx5e_channels *chs, bool vsd); - struct mlx5e_redirect_rqt_param { bool is_rss; union { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 83796ce17fc3..b57a6e72cc86 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2950,7 +2950,7 @@ static int mlx5e_modify_channels_scatter_fcs(struct mlx5e_channels *chs, bool en return 0; } -int mlx5e_modify_channels_vsd(struct mlx5e_channels *chs, bool vsd) +static int mlx5e_modify_channels_vsd(struct mlx5e_channels *chs, bool vsd) { int err = 0; int i; -- cgit From 6543b78ea1e93913bcc94bc2c3ba996373db43df Mon Sep 17 00:00:00 2001 From: Eran Ben Elisha Date: Thu, 2 Feb 2017 15:19:34 +0200 Subject: net/mlx5e: Change FW sub_minor display to 4 zeros padding FW version should be reported as X.Y.ZZZZ, add leading zeroes to sub minor in order to fix it. Signed-off-by: Eran Ben Elisha Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index af039b6c0799..167a8379156e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -42,7 +42,7 @@ static void mlx5e_get_drvinfo(struct net_device *dev, strlcpy(drvinfo->version, DRIVER_VERSION " (" DRIVER_RELDATE ")", sizeof(drvinfo->version)); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), - "%d.%d.%d", + "%d.%d.%04d", fw_rev_maj(mdev), fw_rev_min(mdev), fw_rev_sub(mdev)); strlcpy(drvinfo->bus_info, pci_name(mdev->pdev), sizeof(drvinfo->bus_info)); -- cgit From 84e11edb71dec60644e62a07b588777a604e1a00 Mon Sep 17 00:00:00 2001 From: Inbar Karmy Date: Mon, 13 Mar 2017 15:36:57 +0200 Subject: net/mlx5e: Show board id in ethtool driver information Add the board id (PSID) to the firmware-version field in the ethtool -i (driver information). The PSID is shown in parentheses, next to the fw-version. $ ethtool -i ens6 firmware-version: 12.14.1101 (MT_2190110032) Signed-off-by: Inbar Karmy Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 167a8379156e..ce7b09d72ff6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -42,8 +42,9 @@ static void mlx5e_get_drvinfo(struct net_device *dev, strlcpy(drvinfo->version, DRIVER_VERSION " (" DRIVER_RELDATE ")", sizeof(drvinfo->version)); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), - "%d.%d.%04d", - fw_rev_maj(mdev), fw_rev_min(mdev), fw_rev_sub(mdev)); + "%d.%d.%04d (%.16s)", + fw_rev_maj(mdev), fw_rev_min(mdev), fw_rev_sub(mdev), + mdev->board_id); strlcpy(drvinfo->bus_info, pci_name(mdev->pdev), sizeof(drvinfo->bus_info)); } -- cgit From 95b6c6a519a300dc667960740ebd43b960b32883 Mon Sep 17 00:00:00 2001 From: Eran Ben Elisha Date: Tue, 28 Mar 2017 11:23:55 +0300 Subject: net/mlx5e: Reuse alloc cq code for all CQs allocation Reuse the code for mlx5e_alloc_cq and mlx5e_alloc_drop_cq, as they have a similar flow. Prior to this patch, the CQEs in the "drop CQ" were not initialized, fixed it with the shared flow of alloc CQ. This is not a critical bug as the RQ connected to this CQ never moved to RTS, but still better to have this right. Signed-off-by: Eran Ben Elisha Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 59 +++++++++-------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index b57a6e72cc86..57844ffca37f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1419,21 +1419,16 @@ static void mlx5e_close_xdpsq(struct mlx5e_xdpsq *sq) mlx5e_free_xdpsq(sq); } -static int mlx5e_alloc_cq(struct mlx5e_channel *c, - struct mlx5e_cq_param *param, - struct mlx5e_cq *cq) +static int mlx5e_alloc_cq_common(struct mlx5_core_dev *mdev, + struct mlx5e_cq_param *param, + struct mlx5e_cq *cq) { - struct mlx5_core_dev *mdev = c->mdev; struct mlx5_core_cq *mcq = &cq->mcq; int eqn_not_used; unsigned int irqn; int err; u32 i; - param->wq.buf_numa_node = cpu_to_node(c->cpu); - param->wq.db_numa_node = cpu_to_node(c->cpu); - param->eq_ix = c->ix; - err = mlx5_cqwq_create(mdev, ¶m->wq, param->cqc, &cq->wq, &cq->wq_ctrl); if (err) @@ -1441,8 +1436,6 @@ static int mlx5e_alloc_cq(struct mlx5e_channel *c, mlx5_vector2eqn(mdev, param->eq_ix, &eqn_not_used, &irqn); - cq->napi = &c->napi; - mcq->cqe_sz = 64; mcq->set_ci_db = cq->wq_ctrl.db.db; mcq->arm_db = cq->wq_ctrl.db.db + 1; @@ -1459,12 +1452,30 @@ static int mlx5e_alloc_cq(struct mlx5e_channel *c, cqe->op_own = 0xf1; } - cq->channel = c; cq->mdev = mdev; return 0; } +static int mlx5e_alloc_cq(struct mlx5e_channel *c, + struct mlx5e_cq_param *param, + struct mlx5e_cq *cq) +{ + struct mlx5_core_dev *mdev = c->priv->mdev; + int err; + + param->wq.buf_numa_node = cpu_to_node(c->cpu); + param->wq.db_numa_node = cpu_to_node(c->cpu); + param->eq_ix = c->ix; + + err = mlx5e_alloc_cq_common(mdev, param, cq); + + cq->napi = &c->napi; + cq->channel = c; + + return err; +} + static void mlx5e_free_cq(struct mlx5e_cq *cq) { mlx5_cqwq_destroy(&cq->wq_ctrl); @@ -2697,31 +2708,7 @@ static int mlx5e_alloc_drop_cq(struct mlx5_core_dev *mdev, struct mlx5e_cq *cq, struct mlx5e_cq_param *param) { - struct mlx5_core_cq *mcq = &cq->mcq; - int eqn_not_used; - unsigned int irqn; - int err; - - err = mlx5_cqwq_create(mdev, ¶m->wq, param->cqc, &cq->wq, - &cq->wq_ctrl); - if (err) - return err; - - mlx5_vector2eqn(mdev, param->eq_ix, &eqn_not_used, &irqn); - - mcq->cqe_sz = 64; - mcq->set_ci_db = cq->wq_ctrl.db.db; - mcq->arm_db = cq->wq_ctrl.db.db + 1; - *mcq->set_ci_db = 0; - *mcq->arm_db = 0; - mcq->vector = param->eq_ix; - mcq->comp = mlx5e_completion_event; - mcq->event = mlx5e_cq_error_event; - mcq->irqn = irqn; - - cq->mdev = mdev; - - return 0; + return mlx5e_alloc_cq_common(mdev, param, cq); } static int mlx5e_open_drop_rq(struct mlx5_core_dev *mdev, -- cgit From 457fcd8a11f7f7f2483b2366d8a98f269c085302 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 30 Mar 2017 19:23:41 +0300 Subject: net/mlx5e: Set default RX moderation parameters on driver load RX moderation default parameters shouldn't be set in mlx5e_build_rx_cq_param since it would reset the values every time on netdev open/close. Instead, it should be set in mlx5e_set_rx_cq_mode_params which is called on driver load only. Fixes: 6a9764efb255 ("net/mlx5e: Isolate open_channels from priv->params") Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 57844ffca37f..8b7b7e604ea0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1944,10 +1944,6 @@ static void mlx5e_build_rx_cq_param(struct mlx5e_priv *priv, } mlx5e_build_common_cq_param(priv, param); - - if (params->rx_am_enabled) - params->rx_cq_moderation = - mlx5e_am_get_def_profile(params->rx_cq_period_mode); } static void mlx5e_build_tx_cq_param(struct mlx5e_priv *priv, @@ -3787,6 +3783,10 @@ void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode) params->rx_cq_moderation.usec = MLX5E_PARAMS_DEFAULT_RX_CQ_MODERATION_USEC_FROM_CQE; + if (params->rx_am_enabled) + params->rx_cq_moderation = + mlx5e_am_get_def_profile(params->rx_cq_period_mode); + MLX5E_SET_PFLAG(params, MLX5E_PFLAG_RX_CQE_BASED_MODER, params->rx_cq_period_mode == MLX5_CQ_PERIOD_MODE_START_FROM_CQE); } -- cgit From c06f73fba15f441ef59dfec21286aaf18b99434b Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 6 Apr 2017 11:02:43 +1000 Subject: ftgmac100: Move ftgmac100_alloc_rx_page() before its users Avoids a forward declaration Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 59 +++++++++++++++----------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 20fc7c8d1a50..298f6f0da0d2 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -88,9 +88,6 @@ struct ftgmac100 { bool need_mac_restart; }; -static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, - struct ftgmac100_rxdes *rxdes, gfp_t gfp); - static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr) { iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR); @@ -401,6 +398,34 @@ static struct page *ftgmac100_rxdes_get_page(struct ftgmac100 *priv, return *ftgmac100_rxdes_page_slot(priv, rxdes); } +static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, + struct ftgmac100_rxdes *rxdes, gfp_t gfp) +{ + struct net_device *netdev = priv->netdev; + struct page *page; + dma_addr_t map; + + page = alloc_page(gfp); + if (!page) { + if (net_ratelimit()) + netdev_err(netdev, "failed to allocate rx page\n"); + return -ENOMEM; + } + + map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(priv->dev, map))) { + if (net_ratelimit()) + netdev_err(netdev, "failed to map rx page\n"); + __free_page(page); + return -ENOMEM; + } + + ftgmac100_rxdes_set_page(priv, rxdes, page); + ftgmac100_rxdes_set_dma_addr(rxdes, map); + ftgmac100_rxdes_set_dma_own(priv, rxdes); + return 0; +} + static int ftgmac100_next_rx_pointer(int pointer) { return (pointer + 1) & (RX_QUEUE_ENTRIES - 1); @@ -793,34 +818,6 @@ static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb, return NETDEV_TX_OK; } -static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, - struct ftgmac100_rxdes *rxdes, gfp_t gfp) -{ - struct net_device *netdev = priv->netdev; - struct page *page; - dma_addr_t map; - - page = alloc_page(gfp); - if (!page) { - if (net_ratelimit()) - netdev_err(netdev, "failed to allocate rx page\n"); - return -ENOMEM; - } - - map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(priv->dev, map))) { - if (net_ratelimit()) - netdev_err(netdev, "failed to map rx page\n"); - __free_page(page); - return -ENOMEM; - } - - ftgmac100_rxdes_set_page(priv, rxdes, page); - ftgmac100_rxdes_set_dma_addr(rxdes, map); - ftgmac100_rxdes_set_dma_own(priv, rxdes); - return 0; -} - static void ftgmac100_free_buffers(struct ftgmac100 *priv) { int i; -- cgit From b1977bfbca569426651ac47e0f99d279f00f8a94 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 6 Apr 2017 11:02:44 +1000 Subject: ftgmac100: Drop support for fragmented receive We don't support jumbo frames, we will never receive a fragmented packet, the RX buffer is always big enough, if not then it's a runaway packet that can be dropped. So take out the loop that handles such things in ftgmac100_rx_packet() which will help with subsequent simplifications and improvements to the RX path Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 37 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 298f6f0da0d2..1c2093c53157 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -534,13 +534,19 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) struct net_device *netdev = priv->netdev; struct ftgmac100_rxdes *rxdes; struct sk_buff *skb; - bool done = false; + struct page *page; + unsigned int size; + dma_addr_t map; rxdes = ftgmac100_rx_locate_first_segment(priv); if (!rxdes) return false; - if (unlikely(ftgmac100_rx_packet_error(priv, rxdes))) { + /* We don't support segmented rx frames, so drop these + * along with packets with errors. + */ + if (unlikely(!ftgmac100_rxdes_last_segment(rxdes) || + ftgmac100_rx_packet_error(priv, rxdes))) { ftgmac100_rx_drop_packet(priv); return true; } @@ -567,28 +573,21 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) (ftgmac100_rxdes_is_udp(rxdes) && !ftgmac100_rxdes_udpcs_err(rxdes))) skb->ip_summed = CHECKSUM_UNNECESSARY; - do { - dma_addr_t map = ftgmac100_rxdes_get_dma_addr(rxdes); - struct page *page = ftgmac100_rxdes_get_page(priv, rxdes); - unsigned int size; + map = ftgmac100_rxdes_get_dma_addr(rxdes); - dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); - - size = ftgmac100_rxdes_data_length(rxdes); - skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 0, size); + dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); - skb->len += size; - skb->data_len += size; - skb->truesize += PAGE_SIZE; + size = ftgmac100_rxdes_data_length(rxdes); + skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 0, size); - if (ftgmac100_rxdes_last_segment(rxdes)) - done = true; + skb->len += size; + skb->data_len += size; + skb->truesize += PAGE_SIZE; - ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC); + ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC); - ftgmac100_rx_pointer_advance(priv); - rxdes = ftgmac100_current_rxdes(priv); - } while (!done); + ftgmac100_rx_pointer_advance(priv); + rxdes = ftgmac100_current_rxdes(priv); /* Small frames are copied into linear part of skb to free one page */ if (skb->len <= 128) { -- cgit From d72e01a0430f8a1ae7adb3cbf0b2e73fcd99252e Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 6 Apr 2017 11:02:45 +1000 Subject: ftgmac100: Use a scratch buffer for failed RX allocations We can occasionally fail to allocate new RX buffers at runtime or when starting the driver. At the moment the latter just fails to open which is fine but the former leaves stale DMA pointers in the ring. Instead, use a scratch page and have all RX ring descriptors point to it by default unless a proper buffer can be allocated. It will help later on when re-initializing the whole ring at runtime on link changes since there is no clean failure path there unlike open(). Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 42 +++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 1c2093c53157..876f54aa42f7 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -71,6 +71,10 @@ struct ftgmac100 { u32 txdes0_edotr_mask; spinlock_t tx_lock; + /* Scratch page to use when rx skb alloc fails */ + void *rx_scratch; + dma_addr_t rx_scratch_dma; + /* Component structures */ struct net_device *netdev; struct device *dev; @@ -404,12 +408,14 @@ static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, struct net_device *netdev = priv->netdev; struct page *page; dma_addr_t map; + int err; page = alloc_page(gfp); if (!page) { if (net_ratelimit()) netdev_err(netdev, "failed to allocate rx page\n"); - return -ENOMEM; + err = -ENOMEM; + map = priv->rx_scratch_dma; } map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE); @@ -417,7 +423,9 @@ static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, if (net_ratelimit()) netdev_err(netdev, "failed to map rx page\n"); __free_page(page); - return -ENOMEM; + err = -ENOMEM; + map = priv->rx_scratch_dma; + page = NULL; } ftgmac100_rxdes_set_page(priv, rxdes, page); @@ -551,6 +559,16 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) return true; } + /* If the packet had no buffer (failed to allocate earlier) + * then try to allocate one and skip + */ + page = ftgmac100_rxdes_get_page(priv, rxdes); + if (!page) { + ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC); + ftgmac100_rx_pointer_advance(priv); + return true; + } + /* start processing */ skb = netdev_alloc_skb_ip_align(netdev, 128); if (unlikely(!skb)) { @@ -854,6 +872,11 @@ static void ftgmac100_free_rings(struct ftgmac100 *priv) if (priv->descs) dma_free_coherent(priv->dev, sizeof(struct ftgmac100_descs), priv->descs, priv->descs_dma_addr); + + /* Free scratch packet buffer */ + if (priv->rx_scratch) + dma_free_coherent(priv->dev, RX_BUF_SIZE, + priv->rx_scratch, priv->rx_scratch_dma); } static int ftgmac100_alloc_rings(struct ftgmac100 *priv) @@ -865,6 +888,14 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv) if (!priv->descs) return -ENOMEM; + /* Allocate scratch packet buffer */ + priv->rx_scratch = dma_alloc_coherent(priv->dev, + RX_BUF_SIZE, + &priv->rx_scratch_dma, + GFP_KERNEL); + if (!priv->rx_scratch) + return -ENOMEM; + return 0; } @@ -873,8 +904,11 @@ static void ftgmac100_init_rings(struct ftgmac100 *priv) int i; /* Initialize RX ring */ - for (i = 0; i < RX_QUEUE_ENTRIES; i++) - priv->descs->rxdes[i].rxdes0 = 0; + for (i = 0; i < RX_QUEUE_ENTRIES; i++) { + struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; + ftgmac100_rxdes_set_dma_addr(rxdes, priv->rx_scratch_dma); + rxdes->rxdes0 = 0; + } ftgmac100_rxdes_set_end_of_ring(priv, &priv->descs->rxdes[i - 1]); /* Initialize TX ring */ -- cgit From 672021943c453c4ae04bf131ccf155a7d4f967ab Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 6 Apr 2017 11:02:46 +1000 Subject: ftgmac100: Cleanup rx checksum handling Read the descriptor field only once and check for IP header checksum errors as well Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 63 +++++++++++++------------------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 876f54aa42f7..6277f6e70f6b 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -352,31 +352,11 @@ static dma_addr_t ftgmac100_rxdes_get_dma_addr(struct ftgmac100_rxdes *rxdes) return le32_to_cpu(rxdes->rxdes3); } -static bool ftgmac100_rxdes_is_tcp(struct ftgmac100_rxdes *rxdes) +static inline bool ftgmac100_rxdes_csum_err(struct ftgmac100_rxdes *rxdes) { - return (rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) == - cpu_to_le32(FTGMAC100_RXDES1_PROT_TCPIP); -} - -static bool ftgmac100_rxdes_is_udp(struct ftgmac100_rxdes *rxdes) -{ - return (rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) == - cpu_to_le32(FTGMAC100_RXDES1_PROT_UDPIP); -} - -static bool ftgmac100_rxdes_tcpcs_err(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_TCP_CHKSUM_ERR); -} - -static bool ftgmac100_rxdes_udpcs_err(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_UDP_CHKSUM_ERR); -} - -static bool ftgmac100_rxdes_ipcs_err(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_IP_CHKSUM_ERR); + return !!(rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_TCP_CHKSUM_ERR | + FTGMAC100_RXDES1_UDP_CHKSUM_ERR | + FTGMAC100_RXDES1_IP_CHKSUM_ERR)); } static inline struct page **ftgmac100_rxdes_page_slot(struct ftgmac100 *priv, @@ -486,11 +466,6 @@ static bool ftgmac100_rx_packet_error(struct ftgmac100 *priv, netdev->stats.rx_crc_errors++; error = true; - } else if (unlikely(ftgmac100_rxdes_ipcs_err(rxdes))) { - if (net_ratelimit()) - netdev_info(netdev, "rx IP checksum err\n"); - - error = true; } if (unlikely(ftgmac100_rxdes_frame_too_long(rxdes))) { @@ -582,14 +557,23 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) if (unlikely(ftgmac100_rxdes_multicast(rxdes))) netdev->stats.multicast++; - /* - * It seems that HW does checksum incorrectly with fragmented packets, - * so we are conservative here - if HW checksum error, let software do - * the checksum again. + /* If the HW found checksum errors, bounce it to software. + * + * If we didn't, we need to see if the packet was recognized + * by HW as one of the supported checksummed protocols before + * we accept the HW test results. */ - if ((ftgmac100_rxdes_is_tcp(rxdes) && !ftgmac100_rxdes_tcpcs_err(rxdes)) || - (ftgmac100_rxdes_is_udp(rxdes) && !ftgmac100_rxdes_udpcs_err(rxdes))) - skb->ip_summed = CHECKSUM_UNNECESSARY; + if (netdev->features & NETIF_F_RXCSUM) { + __le32 csum_vlan = rxdes->rxdes1; + __le32 err_bits = cpu_to_le32(FTGMAC100_RXDES1_TCP_CHKSUM_ERR | + FTGMAC100_RXDES1_UDP_CHKSUM_ERR | + FTGMAC100_RXDES1_IP_CHKSUM_ERR); + if ((csum_vlan & err_bits) || + !(csum_vlan & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK))) + skb->ip_summed = CHECKSUM_NONE; + else + skb->ip_summed = CHECKSUM_UNNECESSARY; + } map = ftgmac100_rxdes_get_dma_addr(rxdes); @@ -621,7 +605,10 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) netdev->stats.rx_bytes += skb->len; /* push packet to protocol stack */ - napi_gro_receive(&priv->napi, skb); + if (skb->ip_summed == CHECKSUM_NONE) + netif_receive_skb(skb); + else + napi_gro_receive(&priv->napi, skb); (*processed)++; return true; @@ -1575,7 +1562,7 @@ static int ftgmac100_probe(struct platform_device *pdev) * when NCSI is enabled on the interface. It doesn't work * in that case. */ - netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO; + netdev->features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_GRO; if (priv->use_ncsi && of_get_property(pdev->dev.of_node, "no-hw-checksum", NULL)) netdev->features &= ~NETIF_F_IP_CSUM; -- cgit From eb50af20442bdaaa2dadd658dc8d556550e862ae Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 6 Apr 2017 11:02:47 +1000 Subject: ftgmac100: Simplify rx packets error handling The fast path has a single unlikely() test for any error bit, calling into a helper that sets the appropriate statistics. The various netdev_info aren't particularly interesting. If we want to differentiate the various length errors later we can introduce driver specific stats using ethtool. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 61 +++++++++++++------------------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 6277f6e70f6b..82ab3676974c 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -300,6 +300,18 @@ static void ftgmac100_rxdes_set_dma_own(const struct ftgmac100 *priv, rxdes->rxdes0 &= cpu_to_le32(priv->rxdes0_edorr_mask); } +#define RXDES0_ANY_ERROR ( \ + FTGMAC100_RXDES0_RX_ERR | \ + FTGMAC100_RXDES0_CRC_ERR | \ + FTGMAC100_RXDES0_FTL | \ + FTGMAC100_RXDES0_RUNT | \ + FTGMAC100_RXDES0_RX_ODD_NB) + +static inline bool ftgmac100_rxdes_any_error(struct ftgmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(RXDES0_ANY_ERROR); +} + static bool ftgmac100_rxdes_rx_error(struct ftgmac100_rxdes *rxdes) { return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RX_ERR); @@ -446,49 +458,22 @@ ftgmac100_rx_locate_first_segment(struct ftgmac100 *priv) return NULL; } -static bool ftgmac100_rx_packet_error(struct ftgmac100 *priv, + +static void ftgmac100_rx_packet_error(struct ftgmac100 *priv, struct ftgmac100_rxdes *rxdes) { struct net_device *netdev = priv->netdev; - bool error = false; - - if (unlikely(ftgmac100_rxdes_rx_error(rxdes))) { - if (net_ratelimit()) - netdev_info(netdev, "rx err\n"); + if (ftgmac100_rxdes_rx_error(rxdes)) netdev->stats.rx_errors++; - error = true; - } - - if (unlikely(ftgmac100_rxdes_crc_error(rxdes))) { - if (net_ratelimit()) - netdev_info(netdev, "rx crc err\n"); + if (ftgmac100_rxdes_crc_error(rxdes)) netdev->stats.rx_crc_errors++; - error = true; - } - - if (unlikely(ftgmac100_rxdes_frame_too_long(rxdes))) { - if (net_ratelimit()) - netdev_info(netdev, "rx frame too long\n"); - - netdev->stats.rx_length_errors++; - error = true; - } else if (unlikely(ftgmac100_rxdes_runt(rxdes))) { - if (net_ratelimit()) - netdev_info(netdev, "rx runt\n"); - - netdev->stats.rx_length_errors++; - error = true; - } else if (unlikely(ftgmac100_rxdes_odd_nibble(rxdes))) { - if (net_ratelimit()) - netdev_info(netdev, "rx odd nibble\n"); + if (ftgmac100_rxdes_frame_too_long(rxdes) || + ftgmac100_rxdes_runt(rxdes) || + ftgmac100_rxdes_odd_nibble(rxdes)) netdev->stats.rx_length_errors++; - error = true; - } - - return error; } static void ftgmac100_rx_drop_packet(struct ftgmac100 *priv) @@ -528,8 +513,12 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) /* We don't support segmented rx frames, so drop these * along with packets with errors. */ - if (unlikely(!ftgmac100_rxdes_last_segment(rxdes) || - ftgmac100_rx_packet_error(priv, rxdes))) { + if (unlikely(!ftgmac100_rxdes_last_segment(rxdes))) { + ftgmac100_rx_drop_packet(priv); + return true; + } + if (unlikely(ftgmac100_rxdes_any_error(rxdes))) { + ftgmac100_rx_packet_error(priv, rxdes); ftgmac100_rx_drop_packet(priv); return true; } -- cgit From 01dd70b52a43f033798e3ce53e576b0c882d5f26 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 6 Apr 2017 11:02:48 +1000 Subject: ftgmac100: Simplify rx pointer handling in the rx path We don't handle fragmented RX packets, so the "looping" helpers to locate the first segment of a packet or to drop a packet aren't actually helping. Take them out and simplify ftgmac100_rx_packet() further as a result. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 92 +++++++++----------------------- 1 file changed, 24 insertions(+), 68 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 82ab3676974c..44be11da9d64 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -431,34 +431,6 @@ static int ftgmac100_next_rx_pointer(int pointer) return (pointer + 1) & (RX_QUEUE_ENTRIES - 1); } -static void ftgmac100_rx_pointer_advance(struct ftgmac100 *priv) -{ - priv->rx_pointer = ftgmac100_next_rx_pointer(priv->rx_pointer); -} - -static struct ftgmac100_rxdes *ftgmac100_current_rxdes(struct ftgmac100 *priv) -{ - return &priv->descs->rxdes[priv->rx_pointer]; -} - -static struct ftgmac100_rxdes * -ftgmac100_rx_locate_first_segment(struct ftgmac100 *priv) -{ - struct ftgmac100_rxdes *rxdes = ftgmac100_current_rxdes(priv); - - while (ftgmac100_rxdes_packet_ready(rxdes)) { - if (ftgmac100_rxdes_first_segment(rxdes)) - return rxdes; - - ftgmac100_rxdes_set_dma_own(priv, rxdes); - ftgmac100_rx_pointer_advance(priv); - rxdes = ftgmac100_current_rxdes(priv); - } - - return NULL; -} - - static void ftgmac100_rx_packet_error(struct ftgmac100 *priv, struct ftgmac100_rxdes *rxdes) { @@ -476,51 +448,32 @@ static void ftgmac100_rx_packet_error(struct ftgmac100 *priv, netdev->stats.rx_length_errors++; } -static void ftgmac100_rx_drop_packet(struct ftgmac100 *priv) -{ - struct net_device *netdev = priv->netdev; - struct ftgmac100_rxdes *rxdes = ftgmac100_current_rxdes(priv); - bool done = false; - - if (net_ratelimit()) - netdev_dbg(netdev, "drop packet %p\n", rxdes); - - do { - if (ftgmac100_rxdes_last_segment(rxdes)) - done = true; - - ftgmac100_rxdes_set_dma_own(priv, rxdes); - ftgmac100_rx_pointer_advance(priv); - rxdes = ftgmac100_current_rxdes(priv); - } while (!done && ftgmac100_rxdes_packet_ready(rxdes)); - - netdev->stats.rx_dropped++; -} - static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) { struct net_device *netdev = priv->netdev; struct ftgmac100_rxdes *rxdes; struct sk_buff *skb; struct page *page; - unsigned int size; + unsigned int pointer, size; dma_addr_t map; - rxdes = ftgmac100_rx_locate_first_segment(priv); - if (!rxdes) + /* Grab next RX descriptor */ + pointer = priv->rx_pointer; + rxdes = &priv->descs->rxdes[pointer]; + + /* Do we have a packet ? */ + if (!ftgmac100_rxdes_packet_ready(rxdes)) return false; - /* We don't support segmented rx frames, so drop these - * along with packets with errors. - */ - if (unlikely(!ftgmac100_rxdes_last_segment(rxdes))) { - ftgmac100_rx_drop_packet(priv); - return true; - } + /* We don't cope with fragmented RX packets */ + if (unlikely(!ftgmac100_rxdes_first_segment(rxdes) || + !ftgmac100_rxdes_last_segment(rxdes))) + goto drop; + + /* Any error (other than csum offload) flagged ? */ if (unlikely(ftgmac100_rxdes_any_error(rxdes))) { ftgmac100_rx_packet_error(priv, rxdes); - ftgmac100_rx_drop_packet(priv); - return true; + goto drop; } /* If the packet had no buffer (failed to allocate earlier) @@ -529,8 +482,7 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) page = ftgmac100_rxdes_get_page(priv, rxdes); if (!page) { ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC); - ftgmac100_rx_pointer_advance(priv); - return true; + goto drop; } /* start processing */ @@ -538,9 +490,7 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) if (unlikely(!skb)) { if (net_ratelimit()) netdev_err(netdev, "rx skb alloc failed\n"); - - ftgmac100_rx_drop_packet(priv); - return true; + goto drop; } if (unlikely(ftgmac100_rxdes_multicast(rxdes))) @@ -577,8 +527,7 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC); - ftgmac100_rx_pointer_advance(priv); - rxdes = ftgmac100_current_rxdes(priv); + priv->rx_pointer = ftgmac100_next_rx_pointer(pointer); /* Small frames are copied into linear part of skb to free one page */ if (skb->len <= 128) { @@ -601,6 +550,13 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) (*processed)++; return true; + + drop: + /* Clean rxdes0 (which resets own bit) */ + rxdes->rxdes0 &= cpu_to_le32(priv->rxdes0_edorr_mask); + priv->rx_pointer = ftgmac100_next_rx_pointer(pointer); + netdev->stats.rx_dropped++; + return true; } static void ftgmac100_txdes_reset(const struct ftgmac100 *priv, -- cgit From 7b49cd1c9eca4acd4dc36b6c2c532cad3576171d Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 6 Apr 2017 11:02:49 +1000 Subject: ftgmac100: Directly receive into sk_buffs The current driver receive path allocates pages and stashes them into SKB fragments. This is not particularly useful as we don't support jumbo frames (which wouldn't be great with the small FIFOs on all the known implementations) anyway. It also makes us flush the caches and allocate more memory for RX than necessary. So set our RX buf to our max packet size instead (which we bump to 1536 bytes to account for packets with vlan tags etc...) like most other ethernet drivers. Then allocate skbs when populating the receive ring and DMA directly into them. This simplifies the RX path further. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 142 ++++++++++++------------------- 1 file changed, 56 insertions(+), 86 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 44be11da9d64..5cd854e740a9 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -43,8 +43,8 @@ #define RX_QUEUE_ENTRIES 256 /* must be power of 2 */ #define TX_QUEUE_ENTRIES 512 /* must be power of 2 */ -#define MAX_PKT_SIZE 1518 -#define RX_BUF_SIZE PAGE_SIZE /* must be smaller than 0x3fff */ +#define MAX_PKT_SIZE 1536 +#define RX_BUF_SIZE MAX_PKT_SIZE /* must be smaller than 0x3fff */ struct ftgmac100_descs { struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES]; @@ -60,7 +60,7 @@ struct ftgmac100 { dma_addr_t descs_dma_addr; /* Rx ring */ - struct page *rx_pages[RX_QUEUE_ENTRIES]; + struct sk_buff *rx_skbs[RX_QUEUE_ENTRIES]; unsigned int rx_pointer; u32 rxdes0_edorr_mask; @@ -293,13 +293,6 @@ static bool ftgmac100_rxdes_packet_ready(struct ftgmac100_rxdes *rxdes) return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY); } -static void ftgmac100_rxdes_set_dma_own(const struct ftgmac100 *priv, - struct ftgmac100_rxdes *rxdes) -{ - /* clear status bits */ - rxdes->rxdes0 &= cpu_to_le32(priv->rxdes0_edorr_mask); -} - #define RXDES0_ANY_ERROR ( \ FTGMAC100_RXDES0_RX_ERR | \ FTGMAC100_RXDES0_CRC_ERR | \ @@ -371,58 +364,45 @@ static inline bool ftgmac100_rxdes_csum_err(struct ftgmac100_rxdes *rxdes) FTGMAC100_RXDES1_IP_CHKSUM_ERR)); } -static inline struct page **ftgmac100_rxdes_page_slot(struct ftgmac100 *priv, - struct ftgmac100_rxdes *rxdes) -{ - return &priv->rx_pages[rxdes - priv->descs->rxdes]; -} - -/* - * rxdes2 is not used by hardware. We use it to keep track of page. - * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). - */ -static void ftgmac100_rxdes_set_page(struct ftgmac100 *priv, - struct ftgmac100_rxdes *rxdes, - struct page *page) -{ - *ftgmac100_rxdes_page_slot(priv, rxdes) = page; -} - -static struct page *ftgmac100_rxdes_get_page(struct ftgmac100 *priv, - struct ftgmac100_rxdes *rxdes) -{ - return *ftgmac100_rxdes_page_slot(priv, rxdes); -} - -static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, - struct ftgmac100_rxdes *rxdes, gfp_t gfp) +static int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv, unsigned int entry, + struct ftgmac100_rxdes *rxdes, gfp_t gfp) { struct net_device *netdev = priv->netdev; - struct page *page; + struct sk_buff *skb; dma_addr_t map; int err; - page = alloc_page(gfp); - if (!page) { + skb = netdev_alloc_skb_ip_align(netdev, RX_BUF_SIZE); + if (unlikely(!skb)) { if (net_ratelimit()) - netdev_err(netdev, "failed to allocate rx page\n"); + netdev_warn(netdev, "failed to allocate rx skb\n"); err = -ENOMEM; map = priv->rx_scratch_dma; + } else { + map = dma_map_single(priv->dev, skb->data, RX_BUF_SIZE, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(priv->dev, map))) { + if (net_ratelimit()) + netdev_err(netdev, "failed to map rx page\n"); + dev_kfree_skb_any(skb); + map = priv->rx_scratch_dma; + skb = NULL; + err = -ENOMEM; + } } - map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(priv->dev, map))) { - if (net_ratelimit()) - netdev_err(netdev, "failed to map rx page\n"); - __free_page(page); - err = -ENOMEM; - map = priv->rx_scratch_dma; - page = NULL; - } + /* Store skb */ + priv->rx_skbs[entry] = skb; - ftgmac100_rxdes_set_page(priv, rxdes, page); + /* Store DMA address into RX desc */ ftgmac100_rxdes_set_dma_addr(rxdes, map); - ftgmac100_rxdes_set_dma_own(priv, rxdes); + + /* Ensure the above is ordered vs clearing the OWN bit */ + dma_wmb(); + + /* Clean rxdes0 (which resets own bit) */ + rxdes->rxdes0 &= cpu_to_le32(priv->rxdes0_edorr_mask); + return 0; } @@ -453,7 +433,6 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) struct net_device *netdev = priv->netdev; struct ftgmac100_rxdes *rxdes; struct sk_buff *skb; - struct page *page; unsigned int pointer, size; dma_addr_t map; @@ -476,20 +455,12 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) goto drop; } - /* If the packet had no buffer (failed to allocate earlier) + /* If the packet had no skb (failed to allocate earlier) * then try to allocate one and skip */ - page = ftgmac100_rxdes_get_page(priv, rxdes); - if (!page) { - ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC); - goto drop; - } - - /* start processing */ - skb = netdev_alloc_skb_ip_align(netdev, 128); - if (unlikely(!skb)) { - if (net_ratelimit()) - netdev_err(netdev, "rx skb alloc failed\n"); + skb = priv->rx_skbs[pointer]; + if (!unlikely(skb)) { + ftgmac100_alloc_rx_buf(priv, pointer, rxdes, GFP_ATOMIC); goto drop; } @@ -514,33 +485,31 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) skb->ip_summed = CHECKSUM_UNNECESSARY; } - map = ftgmac100_rxdes_get_dma_addr(rxdes); - - dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); - + /* Grab received size annd transfer to skb */ size = ftgmac100_rxdes_data_length(rxdes); - skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 0, size); + skb_put(skb, size); - skb->len += size; - skb->data_len += size; - skb->truesize += PAGE_SIZE; + /* Tear down DMA mapping, do necessary cache management */ + map = ftgmac100_rxdes_get_dma_addr(rxdes); +#if defined(CONFIG_ARM) && !defined(CONFIG_ARM_DMA_USE_IOMMU) + /* When we don't have an iommu, we can save cycles by not + * invalidating the cache for the part of the packet that + * wasn't received. + */ + dma_unmap_single(priv->dev, map, size, DMA_FROM_DEVICE); +#else + dma_unmap_single(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); +#endif - ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC); + /* Resplenish rx ring */ + ftgmac100_alloc_rx_buf(priv, pointer, rxdes, GFP_ATOMIC); priv->rx_pointer = ftgmac100_next_rx_pointer(pointer); - /* Small frames are copied into linear part of skb to free one page */ - if (skb->len <= 128) { - skb->truesize -= PAGE_SIZE; - __pskb_pull_tail(skb, skb->len); - } else { - /* We pull the minimum amount into linear part */ - __pskb_pull_tail(skb, ETH_HLEN); - } skb->protocol = eth_type_trans(skb, netdev); netdev->stats.rx_packets++; - netdev->stats.rx_bytes += skb->len; + netdev->stats.rx_bytes += size; /* push packet to protocol stack */ if (skb->ip_summed == CHECKSUM_NONE) @@ -774,14 +743,15 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) /* Free all RX buffers */ for (i = 0; i < RX_QUEUE_ENTRIES; i++) { struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; - struct page *page = ftgmac100_rxdes_get_page(priv, rxdes); + struct sk_buff *skb = priv->rx_skbs[i]; dma_addr_t map = ftgmac100_rxdes_get_dma_addr(rxdes); - if (!page) + if (!skb) continue; - dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); - __free_page(page); + priv->rx_skbs[i] = NULL; + dma_unmap_single(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); } /* Free all TX buffers */ @@ -856,7 +826,7 @@ static int ftgmac100_alloc_rx_buffers(struct ftgmac100 *priv) for (i = 0; i < RX_QUEUE_ENTRIES; i++) { struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; - if (ftgmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL)) + if (ftgmac100_alloc_rx_buf(priv, i, rxdes, GFP_KERNEL)) return -ENOMEM; } return 0; -- cgit From 027f426d547967cd1b6899597367aa64af51c28e Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 6 Apr 2017 11:02:50 +1000 Subject: ftgmac100: Add missing barrier in ftgmac100_rx_packet() Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 5cd854e740a9..535e8a00d404 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -444,6 +444,9 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) if (!ftgmac100_rxdes_packet_ready(rxdes)) return false; + /* Order subsequent reads with the test for the ready bit */ + dma_rmb(); + /* We don't cope with fragmented RX packets */ if (unlikely(!ftgmac100_rxdes_first_segment(rxdes) || !ftgmac100_rxdes_last_segment(rxdes))) -- cgit From 4ca24152d8919508bc53827a79e68b353ac6e333 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 6 Apr 2017 11:02:51 +1000 Subject: ftgmac100: Remove rx descriptor accessors Directly access the fields when needed. The accessors add clutter not clarity and in some cases cause unnecessary read-modify-write type access on the slow (uncached) descriptor memory. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 152 +++++++++---------------------- drivers/net/ethernet/faraday/ftgmac100.h | 16 +++- 2 files changed, 53 insertions(+), 115 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 535e8a00d404..41e8467f272a 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -278,92 +278,6 @@ static void ftgmac100_stop_hw(struct ftgmac100 *priv) iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR); } -static bool ftgmac100_rxdes_first_segment(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FRS); -} - -static bool ftgmac100_rxdes_last_segment(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_LRS); -} - -static bool ftgmac100_rxdes_packet_ready(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY); -} - -#define RXDES0_ANY_ERROR ( \ - FTGMAC100_RXDES0_RX_ERR | \ - FTGMAC100_RXDES0_CRC_ERR | \ - FTGMAC100_RXDES0_FTL | \ - FTGMAC100_RXDES0_RUNT | \ - FTGMAC100_RXDES0_RX_ODD_NB) - -static inline bool ftgmac100_rxdes_any_error(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(RXDES0_ANY_ERROR); -} - -static bool ftgmac100_rxdes_rx_error(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RX_ERR); -} - -static bool ftgmac100_rxdes_crc_error(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_CRC_ERR); -} - -static bool ftgmac100_rxdes_frame_too_long(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FTL); -} - -static bool ftgmac100_rxdes_runt(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RUNT); -} - -static bool ftgmac100_rxdes_odd_nibble(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RX_ODD_NB); -} - -static unsigned int ftgmac100_rxdes_data_length(struct ftgmac100_rxdes *rxdes) -{ - return le32_to_cpu(rxdes->rxdes0) & FTGMAC100_RXDES0_VDBC; -} - -static bool ftgmac100_rxdes_multicast(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_MULTICAST); -} - -static void ftgmac100_rxdes_set_end_of_ring(const struct ftgmac100 *priv, - struct ftgmac100_rxdes *rxdes) -{ - rxdes->rxdes0 |= cpu_to_le32(priv->rxdes0_edorr_mask); -} - -static void ftgmac100_rxdes_set_dma_addr(struct ftgmac100_rxdes *rxdes, - dma_addr_t addr) -{ - rxdes->rxdes3 = cpu_to_le32(addr); -} - -static dma_addr_t ftgmac100_rxdes_get_dma_addr(struct ftgmac100_rxdes *rxdes) -{ - return le32_to_cpu(rxdes->rxdes3); -} - -static inline bool ftgmac100_rxdes_csum_err(struct ftgmac100_rxdes *rxdes) -{ - return !!(rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_TCP_CHKSUM_ERR | - FTGMAC100_RXDES1_UDP_CHKSUM_ERR | - FTGMAC100_RXDES1_IP_CHKSUM_ERR)); -} - static int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv, unsigned int entry, struct ftgmac100_rxdes *rxdes, gfp_t gfp) { @@ -395,13 +309,16 @@ static int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv, unsigned int entry, priv->rx_skbs[entry] = skb; /* Store DMA address into RX desc */ - ftgmac100_rxdes_set_dma_addr(rxdes, map); + rxdes->rxdes3 = cpu_to_le32(map); /* Ensure the above is ordered vs clearing the OWN bit */ dma_wmb(); - /* Clean rxdes0 (which resets own bit) */ - rxdes->rxdes0 &= cpu_to_le32(priv->rxdes0_edorr_mask); + /* Clean status (which resets own bit) */ + if (entry == (RX_QUEUE_ENTRIES - 1)) + rxdes->rxdes0 = cpu_to_le32(priv->rxdes0_edorr_mask); + else + rxdes->rxdes0 = 0; return 0; } @@ -411,20 +328,19 @@ static int ftgmac100_next_rx_pointer(int pointer) return (pointer + 1) & (RX_QUEUE_ENTRIES - 1); } -static void ftgmac100_rx_packet_error(struct ftgmac100 *priv, - struct ftgmac100_rxdes *rxdes) +static void ftgmac100_rx_packet_error(struct ftgmac100 *priv, u32 status) { struct net_device *netdev = priv->netdev; - if (ftgmac100_rxdes_rx_error(rxdes)) + if (status & FTGMAC100_RXDES0_RX_ERR) netdev->stats.rx_errors++; - if (ftgmac100_rxdes_crc_error(rxdes)) + if (status & FTGMAC100_RXDES0_CRC_ERR) netdev->stats.rx_crc_errors++; - if (ftgmac100_rxdes_frame_too_long(rxdes) || - ftgmac100_rxdes_runt(rxdes) || - ftgmac100_rxdes_odd_nibble(rxdes)) + if (status & (FTGMAC100_RXDES0_FTL | + FTGMAC100_RXDES0_RUNT | + FTGMAC100_RXDES0_RX_ODD_NB)) netdev->stats.rx_length_errors++; } @@ -434,27 +350,31 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) struct ftgmac100_rxdes *rxdes; struct sk_buff *skb; unsigned int pointer, size; + u32 status; dma_addr_t map; /* Grab next RX descriptor */ pointer = priv->rx_pointer; rxdes = &priv->descs->rxdes[pointer]; + /* Grab descriptor status */ + status = le32_to_cpu(rxdes->rxdes0); + /* Do we have a packet ? */ - if (!ftgmac100_rxdes_packet_ready(rxdes)) + if (!(status & FTGMAC100_RXDES0_RXPKT_RDY)) return false; /* Order subsequent reads with the test for the ready bit */ dma_rmb(); /* We don't cope with fragmented RX packets */ - if (unlikely(!ftgmac100_rxdes_first_segment(rxdes) || - !ftgmac100_rxdes_last_segment(rxdes))) + if (unlikely(!(status & FTGMAC100_RXDES0_FRS) || + !(status & FTGMAC100_RXDES0_LRS))) goto drop; /* Any error (other than csum offload) flagged ? */ - if (unlikely(ftgmac100_rxdes_any_error(rxdes))) { - ftgmac100_rx_packet_error(priv, rxdes); + if (unlikely(status & RXDES0_ANY_ERROR)) { + ftgmac100_rx_packet_error(priv, status); goto drop; } @@ -467,7 +387,7 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) goto drop; } - if (unlikely(ftgmac100_rxdes_multicast(rxdes))) + if (unlikely(status & FTGMAC100_RXDES0_MULTICAST)) netdev->stats.multicast++; /* If the HW found checksum errors, bounce it to software. @@ -489,11 +409,12 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) } /* Grab received size annd transfer to skb */ - size = ftgmac100_rxdes_data_length(rxdes); + size = status & FTGMAC100_RXDES0_VDBC; skb_put(skb, size); /* Tear down DMA mapping, do necessary cache management */ - map = ftgmac100_rxdes_get_dma_addr(rxdes); + map = le32_to_cpu(rxdes->rxdes3); + #if defined(CONFIG_ARM) && !defined(CONFIG_ARM_DMA_USE_IOMMU) /* When we don't have an iommu, we can save cycles by not * invalidating the cache for the part of the packet that @@ -525,7 +446,7 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) drop: /* Clean rxdes0 (which resets own bit) */ - rxdes->rxdes0 &= cpu_to_le32(priv->rxdes0_edorr_mask); + rxdes->rxdes0 = cpu_to_le32(status & priv->rxdes0_edorr_mask); priv->rx_pointer = ftgmac100_next_rx_pointer(pointer); netdev->stats.rx_dropped++; return true; @@ -747,7 +668,7 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) for (i = 0; i < RX_QUEUE_ENTRIES; i++) { struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; struct sk_buff *skb = priv->rx_skbs[i]; - dma_addr_t map = ftgmac100_rxdes_get_dma_addr(rxdes); + dma_addr_t map = le32_to_cpu(rxdes->rxdes3); if (!skb) continue; @@ -806,15 +727,17 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv) static void ftgmac100_init_rings(struct ftgmac100 *priv) { + struct ftgmac100_rxdes *rxdes; int i; /* Initialize RX ring */ for (i = 0; i < RX_QUEUE_ENTRIES; i++) { - struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; - ftgmac100_rxdes_set_dma_addr(rxdes, priv->rx_scratch_dma); + rxdes = &priv->descs->rxdes[i]; rxdes->rxdes0 = 0; + rxdes->rxdes3 = cpu_to_le32(priv->rx_scratch_dma); } - ftgmac100_rxdes_set_end_of_ring(priv, &priv->descs->rxdes[i - 1]); + /* Mark the end of the ring */ + rxdes->rxdes0 |= cpu_to_le32(priv->rxdes0_edorr_mask); /* Initialize TX ring */ for (i = 0; i < TX_QUEUE_ENTRIES; i++) @@ -1030,6 +953,14 @@ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static bool ftgmac100_check_rx(struct ftgmac100 *priv) +{ + struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[priv->rx_pointer]; + + /* Do we have a packet ? */ + return !!(rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY)); +} + static int ftgmac100_poll(struct napi_struct *napi, int budget) { struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi); @@ -1069,8 +1000,7 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget) */ iowrite32(FTGMAC100_INT_RXTX, priv->base + FTGMAC100_OFFSET_ISR); - if (ftgmac100_rxdes_packet_ready - (ftgmac100_current_rxdes(priv)) || priv->tx_pending) + if (ftgmac100_check_rx(priv) || priv->tx_pending) return budget; /* deschedule NAPI */ diff --git a/drivers/net/ethernet/faraday/ftgmac100.h b/drivers/net/ethernet/faraday/ftgmac100.h index c4d5cc11062f..9124785a4ab9 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.h +++ b/drivers/net/ethernet/faraday/ftgmac100.h @@ -227,10 +227,10 @@ struct ftgmac100_txdes { * Receive descriptor, aligned to 16 bytes */ struct ftgmac100_rxdes { - unsigned int rxdes0; - unsigned int rxdes1; - unsigned int rxdes2; /* not used by HW */ - unsigned int rxdes3; /* RXBUF_BADR */ + __le32 rxdes0; /* Control & status bits */ + __le32 rxdes1; /* Checksum and vlan status */ + __le32 rxdes2; /* length/type on AST2500 */ + __le32 rxdes3; /* DMA buffer address */ } __attribute__ ((aligned(16))); #define FTGMAC100_RXDES0_VDBC 0x3fff @@ -248,6 +248,14 @@ struct ftgmac100_rxdes { #define FTGMAC100_RXDES0_FRS (1 << 29) #define FTGMAC100_RXDES0_RXPKT_RDY (1 << 31) +/* Errors we care about for dropping packets */ +#define RXDES0_ANY_ERROR ( \ + FTGMAC100_RXDES0_RX_ERR | \ + FTGMAC100_RXDES0_CRC_ERR | \ + FTGMAC100_RXDES0_FTL | \ + FTGMAC100_RXDES0_RUNT | \ + FTGMAC100_RXDES0_RX_ODD_NB) + #define FTGMAC100_RXDES1_VLANTAG_CI 0xffff #define FTGMAC100_RXDES1_PROT_MASK (0x3 << 20) #define FTGMAC100_RXDES1_PROT_NONIP (0x0 << 20) -- cgit From d930655d4a25021b70285976745e6eee107ac0bb Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 6 Apr 2017 11:02:52 +1000 Subject: ftgmac100: Work around HW bug in runt frame detection The HW incorrectly calculates the frame size without the vlan tag and compares that against 64. It will thus flag 64-bytes frames with a vlan tag as 60-bytes frames "runt" packets which we'll then drop. Thus we end up dropping ARP packets on vlan's ... It does that whether vlan tag stripping is enabled or not. This works around it by ignoring the "runt" error bit of the frame has been vlan tagged and is at least 60 bytes. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 35 +++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 41e8467f272a..8447987577bd 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -350,7 +350,7 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) struct ftgmac100_rxdes *rxdes; struct sk_buff *skb; unsigned int pointer, size; - u32 status; + u32 status, csum_vlan; dma_addr_t map; /* Grab next RX descriptor */ @@ -372,10 +372,27 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) !(status & FTGMAC100_RXDES0_LRS))) goto drop; + /* Grab received size and csum vlan field in the descriptor */ + size = status & FTGMAC100_RXDES0_VDBC; + csum_vlan = le32_to_cpu(rxdes->rxdes1); + /* Any error (other than csum offload) flagged ? */ if (unlikely(status & RXDES0_ANY_ERROR)) { - ftgmac100_rx_packet_error(priv, status); - goto drop; + /* Correct for incorrect flagging of runt packets + * with vlan tags... Just accept a runt packet that + * has been flagged as vlan and whose size is at + * least 60 bytes. + */ + if ((status & FTGMAC100_RXDES0_RUNT) && + (csum_vlan & FTGMAC100_RXDES1_VLANTAG_AVAIL) && + (size >= 60)) + status &= ~FTGMAC100_RXDES0_RUNT; + + /* Any error still in there ? */ + if (status & RXDES0_ANY_ERROR) { + ftgmac100_rx_packet_error(priv, status); + goto drop; + } } /* If the packet had no skb (failed to allocate earlier) @@ -397,19 +414,17 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) * we accept the HW test results. */ if (netdev->features & NETIF_F_RXCSUM) { - __le32 csum_vlan = rxdes->rxdes1; - __le32 err_bits = cpu_to_le32(FTGMAC100_RXDES1_TCP_CHKSUM_ERR | - FTGMAC100_RXDES1_UDP_CHKSUM_ERR | - FTGMAC100_RXDES1_IP_CHKSUM_ERR); + u32 err_bits = FTGMAC100_RXDES1_TCP_CHKSUM_ERR | + FTGMAC100_RXDES1_UDP_CHKSUM_ERR | + FTGMAC100_RXDES1_IP_CHKSUM_ERR; if ((csum_vlan & err_bits) || - !(csum_vlan & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK))) + !(csum_vlan & FTGMAC100_RXDES1_PROT_MASK)) skb->ip_summed = CHECKSUM_NONE; else skb->ip_summed = CHECKSUM_UNNECESSARY; } - /* Grab received size annd transfer to skb */ - size = status & FTGMAC100_RXDES0_VDBC; + /* Transfer received size to skb */ skb_put(skb, size); /* Tear down DMA mapping, do necessary cache management */ -- cgit From bacd75cfac8af9fc91b7fde4b0cad8960c77986f Mon Sep 17 00:00:00 2001 From: Preethi Banala Date: Mon, 27 Mar 2017 14:43:18 -0700 Subject: i40e/i40evf: Add capability exchange for outer checksum This patch adds a capability negotiation between VF and PF using ENCAP/ ENCAP_CSUM offload flags in order for the VF to support outer checksum and TSO offloads for encapsulated packets. These capabilities were assumed by default and enabled in current hardware. Going forward, these features needs to be negotiated with PF before advertising to the stack. Additionally, strip out the mac.type checks for X722 since outer checksums are enabled based on the ENCAP_CSUM offload negotiation flag and maintain consistency between drivers in how the features are configured. Change-ID: Ie380a6f57eca557a2bb575b66b12fae36d308920 Signed-off-by: Preethi Banala Signed-off-by: Alan Brady Signed-off-by: Jesse Brandeburg Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 52 +++++++++--------- drivers/net/ethernet/intel/i40e/i40e_virtchnl.h | 3 +- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 7 +++ drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h | 3 +- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 61 ++++++++++++---------- .../net/ethernet/intel/i40evf/i40evf_virtchnl.c | 4 +- 6 files changed, 74 insertions(+), 56 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 703444e92964..7147c67a939d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -9253,6 +9253,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) u8 broadcast[ETH_ALEN]; u8 mac_addr[ETH_ALEN]; int etherdev_size; + netdev_features_t hw_enc_features; + netdev_features_t hw_features; etherdev_size = sizeof(struct i40e_netdev_priv); netdev = alloc_etherdev_mq(etherdev_size, vsi->alloc_queue_pairs); @@ -9263,43 +9265,43 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) np = netdev_priv(netdev); np->vsi = vsi; - netdev->hw_enc_features |= NETIF_F_SG | - NETIF_F_IP_CSUM | - NETIF_F_IPV6_CSUM | - NETIF_F_HIGHDMA | - NETIF_F_SOFT_FEATURES | - NETIF_F_TSO | - NETIF_F_TSO_ECN | - NETIF_F_TSO6 | - NETIF_F_GSO_GRE | - NETIF_F_GSO_GRE_CSUM | - NETIF_F_GSO_IPXIP4 | - NETIF_F_GSO_IPXIP6 | - NETIF_F_GSO_UDP_TUNNEL | - NETIF_F_GSO_UDP_TUNNEL_CSUM | - NETIF_F_GSO_PARTIAL | - NETIF_F_SCTP_CRC | - NETIF_F_RXHASH | - NETIF_F_RXCSUM | - 0; + hw_enc_features = NETIF_F_SG | + NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | + NETIF_F_HIGHDMA | + NETIF_F_SOFT_FEATURES | + NETIF_F_TSO | + NETIF_F_TSO_ECN | + NETIF_F_TSO6 | + NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM | + NETIF_F_GSO_PARTIAL | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_SCTP_CRC | + NETIF_F_RXHASH | + NETIF_F_RXCSUM | + 0; if (!(pf->flags & I40E_FLAG_OUTER_UDP_CSUM_CAPABLE)) netdev->gso_partial_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM; netdev->gso_partial_features |= NETIF_F_GSO_GRE_CSUM; + netdev->hw_enc_features |= hw_enc_features; + /* record features VLANs can make use of */ - netdev->vlan_features |= netdev->hw_enc_features | - NETIF_F_TSO_MANGLEID; + netdev->vlan_features |= hw_enc_features | NETIF_F_TSO_MANGLEID; if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) netdev->hw_features |= NETIF_F_NTUPLE; + hw_features = hw_enc_features | + NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_CTAG_RX; - netdev->hw_features |= netdev->hw_enc_features | - NETIF_F_HW_VLAN_CTAG_TX | - NETIF_F_HW_VLAN_CTAG_RX; + netdev->hw_features |= hw_features; - netdev->features |= netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER; + netdev->features |= hw_features | NETIF_F_HW_VLAN_CTAG_FILTER; netdev->hw_enc_features |= NETIF_F_TSO_MANGLEID; if (vsi->type == I40E_VSI_MAIN) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h index 974ba2baf6ea..8552192a5bde 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl.h @@ -163,7 +163,8 @@ struct i40e_virtchnl_vsi_resource { #define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 0x00040000 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF 0X00080000 -#define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM 0X00100000 +#define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP 0X00100000 +#define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM 0X00200000 #define I40E_VF_BASE_MODE_OFFLOADS (I40E_VIRTCHNL_VF_OFFLOAD_L2 | \ I40E_VIRTCHNL_VF_OFFLOAD_VLAN | \ diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index d526940ff951..65c95ffc15ec 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1408,6 +1408,13 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2; } + if (vf->driver_caps & I40E_VIRTCHNL_VF_OFFLOAD_ENCAP) + vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_ENCAP; + + if ((pf->flags & I40E_FLAG_OUTER_UDP_CSUM_CAPABLE) && + (vf->driver_caps & I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM)) + vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM; + if (vf->driver_caps & I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING) { if (pf->flags & I40E_FLAG_MFP_ENABLED) { dev_err(&pf->pdev->dev, diff --git a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h index f431fbc4a3e7..c5ad0388c3d5 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_virtchnl.h @@ -163,7 +163,8 @@ struct i40e_virtchnl_vsi_resource { #define I40E_VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 0x00040000 #define I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF 0X00080000 -#define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM 0X00100000 +#define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP 0X00100000 +#define I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM 0X00200000 #define I40E_VF_BASE_MODE_OFFLOADS (I40E_VIRTCHNL_VF_OFFLOAD_L2 | \ I40E_VIRTCHNL_VF_OFFLOAD_VLAN | \ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index fb2811c23024..c690aba8e8d1 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2401,6 +2401,8 @@ int i40evf_process_config(struct i40evf_adapter *adapter) struct net_device *netdev = adapter->netdev; struct i40e_vsi *vsi = &adapter->vsi; int i; + netdev_features_t hw_enc_features; + netdev_features_t hw_features; /* got VF config message back from PF, now we can parse it */ for (i = 0; i < vfres->num_vsis; i++) { @@ -2412,46 +2414,52 @@ int i40evf_process_config(struct i40evf_adapter *adapter) return -ENODEV; } - netdev->hw_enc_features |= NETIF_F_SG | - NETIF_F_IP_CSUM | - NETIF_F_IPV6_CSUM | - NETIF_F_HIGHDMA | - NETIF_F_SOFT_FEATURES | - NETIF_F_TSO | - NETIF_F_TSO_ECN | - NETIF_F_TSO6 | + hw_enc_features = NETIF_F_SG | + NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | + NETIF_F_HIGHDMA | + NETIF_F_SOFT_FEATURES | + NETIF_F_TSO | + NETIF_F_TSO_ECN | + NETIF_F_TSO6 | + NETIF_F_SCTP_CRC | + NETIF_F_RXHASH | + NETIF_F_RXCSUM | + 0; + + /* advertise to stack only if offloads for encapsulated packets is + * supported + */ + if (vfres->vf_offload_flags & I40E_VIRTCHNL_VF_OFFLOAD_ENCAP) { + hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE | NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_IPXIP4 | NETIF_F_GSO_IPXIP6 | - NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_PARTIAL | - NETIF_F_SCTP_CRC | - NETIF_F_RXHASH | - NETIF_F_RXCSUM | 0; - if (!(adapter->flags & I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE)) - netdev->gso_partial_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM; - - netdev->gso_partial_features |= NETIF_F_GSO_GRE_CSUM; + if (!(vfres->vf_offload_flags & + I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM)) + netdev->gso_partial_features |= + NETIF_F_GSO_UDP_TUNNEL_CSUM; + netdev->gso_partial_features |= NETIF_F_GSO_GRE_CSUM; + netdev->hw_enc_features |= NETIF_F_TSO_MANGLEID; + netdev->hw_enc_features |= hw_enc_features; + } /* record features VLANs can make use of */ - netdev->vlan_features |= netdev->hw_enc_features | - NETIF_F_TSO_MANGLEID; + netdev->vlan_features |= hw_enc_features | NETIF_F_TSO_MANGLEID; /* Write features and hw_features separately to avoid polluting - * with, or dropping, features that are set when we registgered. + * with, or dropping, features that are set when we registered. */ - netdev->hw_features |= netdev->hw_enc_features; + hw_features = hw_enc_features; - netdev->features |= netdev->hw_enc_features | I40EVF_VLAN_FEATURES; - netdev->hw_enc_features |= NETIF_F_TSO_MANGLEID; + netdev->hw_features |= hw_features; - /* disable VLAN features if not supported */ - if (!(vfres->vf_offload_flags & I40E_VIRTCHNL_VF_OFFLOAD_VLAN)) - netdev->features ^= I40EVF_VLAN_FEATURES; + netdev->features |= hw_features | I40EVF_VLAN_FEATURES; adapter->vsi.id = adapter->vsi_res->vsi_id; @@ -2592,9 +2600,6 @@ static void i40evf_init_task(struct work_struct *work) goto err_alloc; } - if (hw->mac.type == I40E_MAC_X722_VF) - adapter->flags |= I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE; - if (i40evf_process_config(adapter)) goto err_alloc; adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 032be8d3928a..3bccfbb1db14 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -159,7 +159,9 @@ int i40evf_send_vf_config_msg(struct i40evf_adapter *adapter) I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG | I40E_VIRTCHNL_VF_OFFLOAD_VLAN | I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR | - I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2; + I40E_VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 | + I40E_VIRTCHNL_VF_OFFLOAD_ENCAP | + I40E_VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM; adapter->current_op = I40E_VIRTCHNL_OP_GET_VF_RESOURCES; adapter->aq_required &= ~I40EVF_FLAG_AQ_GET_CONFIG; -- cgit From 295c0a555062384449cb2b4670b7aac08c3624ac Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 30 Mar 2017 00:46:06 -0700 Subject: i40e: remove client instance on driver unload When the driver is unloaded, we need to remove the client instance, otherwise we leak memory. Change-ID: If1e7882ac1f6ce15d004722fafbe31afbe0adc9a Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_client.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.c b/drivers/net/ethernet/intel/i40e/i40e_client.c index 191028b1489b..d05296a7078e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.c +++ b/drivers/net/ethernet/intel/i40e/i40e_client.c @@ -459,6 +459,9 @@ int i40e_lan_del_device(struct i40e_pf *pf) struct i40e_device *ldev, *tmp; int ret = -ENODEV; + /* First, remove any client instance. */ + i40e_client_del_instance(pf); + mutex_lock(&i40e_device_mutex); list_for_each_entry_safe(ldev, tmp, &i40e_devices, list) { if (ldev->pf == pf) { -- cgit From 8090f6183c56dd133a0fd6a9bcc09b1da8dbb0e8 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 30 Mar 2017 00:46:07 -0700 Subject: i40e: register existing client on probe In some cases, a client (i40iw) may already be present when probe is called. Check for this, and add a client instance if necessary. Change-ID: I2009312694b7ad81f1023919e4c6c86181f21689 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_client.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.c b/drivers/net/ethernet/intel/i40e/i40e_client.c index d05296a7078e..eb2896fd52a6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.c +++ b/drivers/net/ethernet/intel/i40e/i40e_client.c @@ -436,6 +436,12 @@ int i40e_lan_add_device(struct i40e_pf *pf) pf->hw.pf_id, pf->hw.bus.bus_id, pf->hw.bus.device, pf->hw.bus.func); + /* If a client has already been registered, we need to add an instance + * of it to our new LAN device. + */ + if (registered_client) + i40e_client_add_instance(pf); + /* Since in some cases register may have happened before a device gets * added, we can schedule a subtask to go initiate the clients if * they can be launched at probe time. -- cgit From 921c467c6bf8f6fe5cd139b0535ad42b952330f0 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Thu, 30 Mar 2017 00:46:08 -0700 Subject: i40e: close client on remove and shutdown When the driver is removed or shut down, close any attached clients (i.e. i40iw). This prevents a panic seen sometimes on forced driver removal or system shutdown when iWarp is running. Change-ID: I4f6161e5a73ffbb2fd5883567b007310302bfcb5 Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 7147c67a939d..d83430faaa41 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -11395,6 +11395,11 @@ static void i40e_remove(struct pci_dev *pdev) if (pf->service_task.func) cancel_work_sync(&pf->service_task); + /* Client close must be called explicitly here because the timer + * has been stopped. + */ + i40e_notify_client_of_netdev_close(pf->vsi[pf->lan_vsi], false); + if (pf->flags & I40E_FLAG_SRIOV_ENABLED) { i40e_free_vfs(pf); pf->flags &= ~I40E_FLAG_SRIOV_ENABLED; @@ -11635,6 +11640,11 @@ static void i40e_shutdown(struct pci_dev *pdev) cancel_work_sync(&pf->service_task); i40e_fdir_teardown(pf); + /* Client close must be called explicitly here because the timer + * has been stopped. + */ + i40e_notify_client_of_netdev_close(pf->vsi[pf->lan_vsi], false); + if (pf->wol_en && (pf->flags & I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE)) i40e_enable_mc_magic_wake(pf); -- cgit From aa59004bdc2e6569a7101fe196a56c6bd58b12d8 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Fri, 7 Apr 2017 09:15:32 +0200 Subject: s390/qeth: use QDIO_*_QFMT defines better use the constant definitions. Signed-off-by: Julian Wiedmann Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 9a5f99ccb122..7142204293b0 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4775,12 +4775,10 @@ static int qeth_query_card_info(struct qeth_card *card, static inline int qeth_get_qdio_q_format(struct qeth_card *card) { - switch (card->info.type) { - case QETH_CARD_TYPE_IQD: - return 2; - default: - return 0; - } + if (card->info.type == QETH_CARD_TYPE_IQD) + return QDIO_IQDIO_QFMT; + else + return QDIO_QETH_QFMT; } static void qeth_determine_capabilities(struct qeth_card *card) -- cgit From bbeb24145135583e405a1f9c041ebf935697983d Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Fri, 7 Apr 2017 09:15:33 +0200 Subject: s390/qeth: fix up ssqd tracing The ac fields are bitmaps, so format them as hex. While at it, also print the ac2 field. Signed-off-by: Julian Wiedmann Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 7142204293b0..510947b1fee4 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4817,8 +4817,9 @@ static void qeth_determine_capabilities(struct qeth_card *card) QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); QETH_DBF_TEXT_(SETUP, 2, "qfmt%d", card->ssqd.qfmt); - QETH_DBF_TEXT_(SETUP, 2, "%d", card->ssqd.qdioac1); - QETH_DBF_TEXT_(SETUP, 2, "%d", card->ssqd.qdioac3); + QETH_DBF_TEXT_(SETUP, 2, "ac1:%02x", card->ssqd.qdioac1); + QETH_DBF_TEXT_(SETUP, 2, "ac2:%04x", card->ssqd.qdioac2); + QETH_DBF_TEXT_(SETUP, 2, "ac3:%04x", card->ssqd.qdioac3); QETH_DBF_TEXT_(SETUP, 2, "icnt%d", card->ssqd.icnt); if (!((card->ssqd.qfmt != QDIO_IQDIO_QFMT) || ((card->ssqd.qdioac1 & CHSC_AC1_INITIATE_INPUTQ) == 0) || -- cgit From d7a39937beca4bed9c8d7f877e099c5ac29afd56 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Fri, 7 Apr 2017 09:15:34 +0200 Subject: s390/qeth: remove unused return value qeth_qdio_output_handler() is the only caller of qeth_handle_send_error() and doesn't care about the return value. Signed-off-by: Julian Wiedmann Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 7 ------- drivers/s390/net/qeth_core_main.c | 7 +++---- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index d9561e39c3b2..d311bdc3bb6c 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -503,13 +503,6 @@ struct qeth_qdio_info { int default_out_queue; }; -enum qeth_send_errors { - QETH_SEND_ERROR_NONE, - QETH_SEND_ERROR_LINK_FAILURE, - QETH_SEND_ERROR_RETRY, - QETH_SEND_ERROR_KICK_IT, -}; - #define QETH_ETH_MAC_V4 0x0100 /* like v4 */ #define QETH_ETH_MAC_V6 0x3333 /* like v6 */ /* tr mc mac is longer, but that will be enough to detect mc frames */ diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 510947b1fee4..9262d94af7f8 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3322,7 +3322,7 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index) } EXPORT_SYMBOL_GPL(qeth_queue_input_buffer); -static int qeth_handle_send_error(struct qeth_card *card, +static void qeth_handle_send_error(struct qeth_card *card, struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err) { int sbalf15 = buffer->buffer->element[15].sflags; @@ -3338,15 +3338,14 @@ static int qeth_handle_send_error(struct qeth_card *card, qeth_check_qdio_errors(card, buffer->buffer, qdio_err, "qouterr"); if (!qdio_err) - return QETH_SEND_ERROR_NONE; + return; if ((sbalf15 >= 15) && (sbalf15 <= 31)) - return QETH_SEND_ERROR_RETRY; + return; QETH_CARD_TEXT(card, 1, "lnkfail"); QETH_CARD_TEXT_(card, 1, "%04x %02x", (u16)qdio_err, (u8)sbalf15); - return QETH_SEND_ERROR_LINK_FAILURE; } /* -- cgit From ff581f82330af0c0b8c10fe2fc80c9317dfd3577 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Fri, 7 Apr 2017 09:15:35 +0200 Subject: s390/qeth: Remove unused code 1. options.add_hhlen is set but never used, drop it 2. clean up no longer required forward declarations 3. delete all sorts of unused defines Signed-off-by: Julian Wiedmann Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 5 ----- drivers/s390/net/qeth_core_main.c | 2 -- drivers/s390/net/qeth_core_mpc.h | 17 ----------------- drivers/s390/net/qeth_l2_main.c | 1 - drivers/s390/net/qeth_l2_sys.c | 3 --- drivers/s390/net/qeth_l3_main.c | 4 ---- 6 files changed, 32 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index d311bdc3bb6c..3277edad54bc 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -509,9 +509,6 @@ struct qeth_qdio_info { #define QETH_TR_MAC_NC 0xc000 /* non-canonical */ #define QETH_TR_MAC_C 0x0300 /* canonical */ -#define DEFAULT_ADD_HHLEN 0 -#define MAX_ADD_HHLEN 1024 - /** * buffer stuff for read channel */ @@ -637,7 +634,6 @@ struct qeth_reply { atomic_t refcnt; }; - struct qeth_card_blkt { int time_total; int inter_packet; @@ -678,7 +674,6 @@ struct qeth_card_options { struct qeth_ipa_info ipa6; struct qeth_sbp_info sbp; /* SETBRIDGEPORT options */ int fake_broadcast; - int add_hhlen; int layer2; int performance_stats; int rx_sg_cb; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 9262d94af7f8..95f9cc189a3a 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -55,7 +55,6 @@ static struct mutex qeth_mod_mutex; static void qeth_send_control_data_cb(struct qeth_channel *, struct qeth_cmd_buffer *); -static int qeth_issue_next_read(struct qeth_card *); static struct qeth_cmd_buffer *qeth_get_buffer(struct qeth_channel *); static void qeth_setup_ccw(struct qeth_channel *, unsigned char *, __u32); static void qeth_free_buffer_pool(struct qeth_card *); @@ -1396,7 +1395,6 @@ static void qeth_set_intial_options(struct qeth_card *card) card->options.route4.type = NO_ROUTER; card->options.route6.type = NO_ROUTER; card->options.fake_broadcast = 0; - card->options.add_hhlen = DEFAULT_ADD_HHLEN; card->options.performance_stats = 0; card->options.rx_sg_cb = QETH_RX_SG_CB; card->options.isolation = ISOLATION_MODE_NONE; diff --git a/drivers/s390/net/qeth_core_mpc.h b/drivers/s390/net/qeth_core_mpc.h index bc69d0a338ad..4accb0a61ce0 100644 --- a/drivers/s390/net/qeth_core_mpc.h +++ b/drivers/s390/net/qeth_core_mpc.h @@ -29,7 +29,6 @@ extern unsigned char IPA_PDU_HEADER[]; #define QETH_TIMEOUT (10 * HZ) #define QETH_IPA_TIMEOUT (45 * HZ) #define QETH_IDX_COMMAND_SEQNO 0xffff0000 -#define SR_INFO_LEN 16 #define QETH_CLEAR_CHANNEL_PARM -10 #define QETH_HALT_CHANNEL_PARM -11 @@ -65,7 +64,6 @@ enum qeth_link_types { QETH_LINK_TYPE_LANE_TR = 0x82, QETH_LINK_TYPE_LANE_ETH1000 = 0x83, QETH_LINK_TYPE_LANE = 0x88, - QETH_LINK_TYPE_ATM_NATIVE = 0x90, }; /* @@ -185,8 +183,6 @@ enum qeth_ipa_return_codes { IPA_RC_ENOMEM = 0xfffe, IPA_RC_FFFF = 0xffff }; -/* for DELIP */ -#define IPA_RC_IP_ADDRESS_NOT_DEFINED IPA_RC_PRIMARY_ALREADY_DEFINED /* for SET_DIAGNOSTIC_ASSIST */ #define IPA_RC_INVALID_SUBCMD IPA_RC_IP_TABLE_FULL #define IPA_RC_HARDWARE_AUTH_ERROR IPA_RC_UNKNOWN_ERROR @@ -631,14 +627,6 @@ enum qeth_ipa_addr_change_code { IPA_ADDR_CHANGE_CODE_MACADDR = 0x02, IPA_ADDR_CHANGE_CODE_REMOVAL = 0x80, /* else addition */ }; -enum qeth_ipa_addr_change_retcode { - IPA_ADDR_CHANGE_RETCODE_OK = 0x0000, - IPA_ADDR_CHANGE_RETCODE_LOSTEVENTS = 0x0010, -}; -enum qeth_ipa_addr_change_lostmask { - IPA_ADDR_CHANGE_MASK_OVERFLOW = 0x01, - IPA_ADDR_CHANGE_MASK_STATECHANGE = 0x02, -}; struct qeth_ipacmd_addr_change_entry { struct net_if_token token; @@ -817,9 +805,4 @@ extern unsigned char IDX_ACTIVATE_WRITE[]; ((buffer) && \ (*(buffer + ((*(buffer + 0x0b)) + 4)) == 0xc1)) -#define ADDR_FRAME_TYPE_DIX 1 -#define ADDR_FRAME_TYPE_802_3 2 -#define ADDR_FRAME_TYPE_TR_WITHOUT_SR 0x10 -#define ADDR_FRAME_TYPE_TR_WITH_SR 0x20 - #endif diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index af4e6a639fec..cd0ba9e0d3e5 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -28,7 +28,6 @@ static int qeth_l2_set_offline(struct ccwgroup_device *); static int qeth_l2_stop(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, struct qeth_ipa_cmd *cmd); diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c index 692db49e3d2a..687972356d6b 100644 --- a/drivers/s390/net/qeth_l2_sys.c +++ b/drivers/s390/net/qeth_l2_sys.c @@ -8,9 +8,6 @@ #include "qeth_core.h" #include "qeth_l2.h" -#define QETH_DEVICE_ATTR(_id, _name, _mode, _show, _store) \ -struct device_attribute dev_attr_##_id = __ATTR(_name, _mode, _show, _store) - static ssize_t qeth_bridge_port_role_state_show(struct device *dev, struct device_attribute *attr, char *buf, int show_state) diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 653f0fb76573..cba1e0907809 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -36,16 +36,12 @@ static int qeth_l3_set_offline(struct ccwgroup_device *); -static int qeth_l3_recover(void *); static int qeth_l3_stop(struct net_device *); static void qeth_l3_set_multicast_list(struct net_device *); -static int qeth_l3_neigh_setup(struct net_device *, struct neigh_parms *); static int qeth_l3_register_addr_entry(struct qeth_card *, struct qeth_ipaddr *); static int qeth_l3_deregister_addr_entry(struct qeth_card *, struct qeth_ipaddr *); -static int __qeth_l3_set_online(struct ccwgroup_device *, int); -static int __qeth_l3_set_offline(struct ccwgroup_device *, int); static int qeth_l3_isxdigit(char *buf) { -- cgit From 6bee4e26a39c51ffe698072ded1f5ab2062e4225 Mon Sep 17 00:00:00 2001 From: Hans Wippel Date: Fri, 7 Apr 2017 09:15:36 +0200 Subject: s390/qeth: improve endianness handling Avoid endianness warnings reported by sparse by (1) using endianness conversions for assigning and using network packet fields, and (2) removing unnecessary endianness conversions from qeth_l3_rebuild_skb. No functional changes. Signed-off-by: Hans Wippel Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 4 ++-- drivers/s390/net/qeth_core_main.c | 11 +++++----- drivers/s390/net/qeth_l3_main.c | 44 +++++++++++++++++++-------------------- drivers/s390/net/qeth_l3_sys.c | 4 ++-- 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 3277edad54bc..ed5b3582adba 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -844,9 +844,9 @@ static inline int qeth_get_ip_version(struct sk_buff *skb) { __be16 *p = &((struct ethhdr *)skb->data)->h_proto; - if (*p == ETH_P_8021Q) + if (be16_to_cpu(*p) == ETH_P_8021Q) p += 2; - switch (*p) { + switch (be16_to_cpu(*p)) { case ETH_P_IPV6: return 6; case ETH_P_IP: diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 95f9cc189a3a..6b22b05a6953 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1201,7 +1201,7 @@ static void qeth_notify_skbs(struct qeth_qdio_out_q *q, while (skb) { QETH_CARD_TEXT_(q->card, 5, "skbn%d", notification); QETH_CARD_TEXT_(q->card, 5, "%lx", (long) skb); - if (skb->protocol == ETH_P_AF_IUCV) { + if (be16_to_cpu(skb->protocol) == ETH_P_AF_IUCV) { if (skb->sk) { struct iucv_sock *iucv = iucv_sk(skb->sk); iucv->sk_txnotify(skb, notification); @@ -1232,7 +1232,8 @@ static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf) while (skb) { QETH_CARD_TEXT(buf->q->card, 5, "skbr"); QETH_CARD_TEXT_(buf->q->card, 5, "%lx", (long) skb); - if (notify_general_error && skb->protocol == ETH_P_AF_IUCV) { + if (notify_general_error && + be16_to_cpu(skb->protocol) == ETH_P_AF_IUCV) { if (skb->sk) { iucv = iucv_sk(skb->sk); iucv->sk_txnotify(skb, TX_NOTIFY_GENERALERROR); @@ -3796,9 +3797,9 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, return qeth_cut_iqd_prio(card, ~skb->priority >> 1 & 3); case QETH_PRIO_Q_ING_VLAN: tci = &((struct ethhdr *)skb->data)->h_proto; - if (*tci == ETH_P_8021Q) - return qeth_cut_iqd_prio(card, ~*(tci + 1) >> - (VLAN_PRIO_SHIFT + 1) & 3); + if (be16_to_cpu(*tci) == ETH_P_8021Q) + return qeth_cut_iqd_prio(card, + ~be16_to_cpu(*(tci + 1)) >> (VLAN_PRIO_SHIFT + 1) & 3); break; default: break; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index cba1e0907809..c29525838b7c 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1337,7 +1337,7 @@ qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd) return qeth_send_ipa_cmd(card, iob, qeth_diags_trace_cb, NULL); } -static void qeth_l3_get_mac_for_ipm(__u32 ipm, char *mac) +static void qeth_l3_get_mac_for_ipm(__be32 ipm, char *mac) { ip_eth_mc_map(ipm, mac); } @@ -1410,7 +1410,7 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev) im4 = rcu_dereference(im4->next_rcu)) { qeth_l3_get_mac_for_ipm(im4->multiaddr, buf); - tmp->u.a4.addr = im4->multiaddr; + tmp->u.a4.addr = be32_to_cpu(im4->multiaddr); memcpy(tmp->mac, buf, sizeof(tmp->mac)); ipm = qeth_l3_ip_from_hash(card, tmp); @@ -1421,7 +1421,7 @@ qeth_l3_add_mc_to_hash(struct qeth_card *card, struct in_device *in4_dev) if (!ipm) continue; memcpy(ipm->mac, buf, sizeof(tmp->mac)); - ipm->u.a4.addr = im4->multiaddr; + ipm->u.a4.addr = be32_to_cpu(im4->multiaddr); ipm->is_multicast = 1; ipm->disp_flag = QETH_DISP_ADDR_ADD; hash_add(card->ip_mc_htable, @@ -1594,8 +1594,8 @@ static void qeth_l3_free_vlan_addresses4(struct qeth_card *card, spin_lock_bh(&card->ip_lock); for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { - addr->u.a4.addr = ifa->ifa_address; - addr->u.a4.mask = ifa->ifa_mask; + addr->u.a4.addr = be32_to_cpu(ifa->ifa_address); + addr->u.a4.mask = be32_to_cpu(ifa->ifa_mask); addr->type = QETH_IP_TYPE_NORMAL; qeth_l3_delete_ip(card, addr); } @@ -1686,25 +1686,25 @@ static inline int qeth_l3_rebuild_skb(struct qeth_card *card, struct sk_buff *skb, struct qeth_hdr *hdr, unsigned short *vlan_id) { - __be16 prot; + __u16 prot; struct iphdr *ip_hdr; unsigned char tg_addr[MAX_ADDR_LEN]; int is_vlan = 0; if (!(hdr->hdr.l3.flags & QETH_HDR_PASSTHRU)) { - prot = htons((hdr->hdr.l3.flags & QETH_HDR_IPV6)? ETH_P_IPV6 : - ETH_P_IP); + prot = (hdr->hdr.l3.flags & QETH_HDR_IPV6) ? ETH_P_IPV6 : + ETH_P_IP; switch (hdr->hdr.l3.flags & QETH_HDR_CAST_MASK) { case QETH_CAST_MULTICAST: switch (prot) { #ifdef CONFIG_QETH_IPV6 - case __constant_htons(ETH_P_IPV6): + case ETH_P_IPV6: ndisc_mc_map((struct in6_addr *) skb->data + 24, tg_addr, card->dev, 0); break; #endif - case __constant_htons(ETH_P_IP): + case ETH_P_IP: ip_hdr = (struct iphdr *)skb->data; ip_eth_mc_map(ip_hdr->daddr, tg_addr); break; @@ -1791,7 +1791,7 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card, magic = *(__u16 *)skb->data; if ((card->info.type == QETH_CARD_TYPE_IQD) && (magic == ETH_P_AF_IUCV)) { - skb->protocol = ETH_P_AF_IUCV; + skb->protocol = cpu_to_be16(ETH_P_AF_IUCV); skb->pkt_type = PACKET_HOST; skb->mac_header = NET_SKB_PAD; skb->dev = card->dev; @@ -2568,10 +2568,10 @@ int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb) rcu_read_unlock(); /* try something else */ - if (skb->protocol == ETH_P_IPV6) + if (be16_to_cpu(skb->protocol) == ETH_P_IPV6) return (skb_network_header(skb)[24] == 0xff) ? RTN_MULTICAST : 0; - else if (skb->protocol == ETH_P_IP) + else if (be16_to_cpu(skb->protocol) == ETH_P_IP) return ((skb_network_header(skb)[16] & 0xf0) == 0xe0) ? RTN_MULTICAST : 0; /* ... */ @@ -2722,7 +2722,7 @@ static void qeth_tso_fill_header(struct qeth_card *card, hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len - sizeof(struct qeth_hdr_tso)); tcph->check = 0; - if (skb->protocol == ETH_P_IPV6) { + if (be16_to_cpu(skb->protocol) == ETH_P_IPV6) { ip6h->payload_len = 0; tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, 0, IPPROTO_TCP, 0); @@ -2769,7 +2769,7 @@ static int qeth_l3_get_elements_no_tso(struct qeth_card *card, static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { int rc; - u16 *tag; + __be16 *tag; struct qeth_hdr *hdr = NULL; int hdr_elements = 0; int elements; @@ -2790,7 +2790,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (((card->info.type == QETH_CARD_TYPE_IQD) && (((card->options.cq != QETH_CQ_ENABLED) && !ipv) || ((card->options.cq == QETH_CQ_ENABLED) && - (skb->protocol != ETH_P_AF_IUCV)))) || + (be16_to_cpu(skb->protocol) != ETH_P_AF_IUCV)))) || card->options.sniffer) goto tx_drop; @@ -2843,9 +2843,9 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) new_skb->data + 8, 4); skb_copy_to_linear_data_offset(new_skb, 8, new_skb->data + 12, 4); - tag = (u16 *)(new_skb->data + 12); - *tag = __constant_htons(ETH_P_8021Q); - *(tag + 1) = htons(skb_vlan_tag_get(new_skb)); + tag = (__be16 *)(new_skb->data + 12); + *tag = cpu_to_be16(ETH_P_8021Q); + *(tag + 1) = cpu_to_be16(skb_vlan_tag_get(new_skb)); } } @@ -2883,7 +2883,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) qeth_l3_fill_header(card, hdr, new_skb, ipv, cast_type); } else { - if (new_skb->protocol == ETH_P_AF_IUCV) + if (be16_to_cpu(new_skb->protocol) == ETH_P_AF_IUCV) qeth_l3_fill_af_iucv_hdr(card, hdr, new_skb); else { qeth_l3_fill_header(card, hdr, new_skb, ipv, @@ -3462,8 +3462,8 @@ static int qeth_l3_ip_event(struct notifier_block *this, addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV4); if (addr) { - addr->u.a4.addr = ifa->ifa_address; - addr->u.a4.mask = ifa->ifa_mask; + addr->u.a4.addr = be32_to_cpu(ifa->ifa_address); + addr->u.a4.mask = be32_to_cpu(ifa->ifa_mask); addr->type = QETH_IP_TYPE_NORMAL; } else return NOTIFY_DONE; diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index 05e9471e3d3f..ff29a4b416b4 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -286,7 +286,7 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, if (!addr) return -ENOMEM; - addr->u.a6.addr.s6_addr32[0] = 0xfe800000; + addr->u.a6.addr.s6_addr32[0] = cpu_to_be32(0xfe800000); addr->u.a6.addr.s6_addr32[1] = 0x00000000; for (i = 8; i < 16; i++) addr->u.a6.addr.s6_addr[i] = @@ -320,7 +320,7 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, addr = qeth_l3_get_addr_buffer(QETH_PROT_IPV6); if (addr != NULL) { - addr->u.a6.addr.s6_addr32[0] = 0xfe800000; + addr->u.a6.addr.s6_addr32[0] = cpu_to_be32(0xfe800000); addr->u.a6.addr.s6_addr32[1] = 0x00000000; for (i = 8; i < 16; i++) addr->u.a6.addr.s6_addr[i] = card->options.hsuid[i - 8]; -- cgit From 5cd77c135627f8d2b24df6d3487b3ac9219ed1a4 Mon Sep 17 00:00:00 2001 From: Hans Wippel Date: Fri, 7 Apr 2017 09:15:37 +0200 Subject: s390/ctcm: improve endianness handling Use endianness conversions for SKB protocol assignments and usage to avoid endianness warnings reported by sparse. No functional changes. Signed-off-by: Hans Wippel Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/ctcm_fsms.c | 2 +- drivers/s390/net/ctcm_main.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c index fd5944bbe224..730d9619400e 100644 --- a/drivers/s390/net/ctcm_fsms.c +++ b/drivers/s390/net/ctcm_fsms.c @@ -1283,7 +1283,7 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg) p_header = (struct pdu *) (skb_tail_pointer(ch->trans_skb) - skb->len); p_header->pdu_flag = 0x00; - if (skb->protocol == ntohs(ETH_P_SNAP)) + if (be16_to_cpu(skb->protocol) == ETH_P_SNAP) p_header->pdu_flag |= 0x60; else p_header->pdu_flag |= 0x20; diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index ac65f12bcd43..198842ce6876 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -106,7 +106,7 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb) priv->stats.rx_frame_errors++; return; } - pskb->protocol = ntohs(header->type); + pskb->protocol = cpu_to_be16(header->type); if ((header->length <= LL_HEADER_LENGTH) || (len <= LL_HEADER_LENGTH)) { if (!(ch->logflags & LOG_FLAG_ILLEGALSIZE)) { @@ -125,7 +125,7 @@ void ctcm_unpack_skb(struct channel *ch, struct sk_buff *pskb) header->length -= LL_HEADER_LENGTH; len -= LL_HEADER_LENGTH; if ((header->length > skb_tailroom(pskb)) || - (header->length > len)) { + (header->length > len)) { if (!(ch->logflags & LOG_FLAG_OVERRUN)) { CTCM_DBF_TEXT_(ERROR, CTC_DBF_ERROR, "%s(%s): Packet size %d (overrun)" @@ -485,7 +485,7 @@ static int ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb) } else { atomic_inc(&skb->users); header.length = l; - header.type = skb->protocol; + header.type = be16_to_cpu(skb->protocol); header.unused = 0; memcpy(skb_push(skb, LL_HEADER_LENGTH), &header, LL_HEADER_LENGTH); @@ -503,7 +503,7 @@ static int ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb) atomic_inc(&skb->users); ch->prof.txlen += skb->len; header.length = skb->len + LL_HEADER_LENGTH; - header.type = skb->protocol; + header.type = be16_to_cpu(skb->protocol); header.unused = 0; memcpy(skb_push(skb, LL_HEADER_LENGTH), &header, LL_HEADER_LENGTH); block_len = skb->len + 2; @@ -690,7 +690,7 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb) p_header->pdu_offset = skb->len; p_header->pdu_proto = 0x01; p_header->pdu_flag = 0x00; - if (skb->protocol == ntohs(ETH_P_SNAP)) { + if (be16_to_cpu(skb->protocol) == ETH_P_SNAP) { p_header->pdu_flag |= PDU_FIRST | PDU_CNTL; } else { p_header->pdu_flag |= PDU_FIRST; @@ -745,7 +745,7 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb) p_header->pdu_proto = 0x01; p_header->pdu_flag = 0x00; p_header->pdu_seq = 0; - if (skb->protocol == ntohs(ETH_P_SNAP)) { + if (be16_to_cpu(skb->protocol) == ETH_P_SNAP) { p_header->pdu_flag |= PDU_FIRST | PDU_CNTL; } else { p_header->pdu_flag |= PDU_FIRST; -- cgit From 6c37c60c2d3cd064b5abe838a0d5dc6255ea36fa Mon Sep 17 00:00:00 2001 From: Hans Wippel Date: Fri, 7 Apr 2017 09:15:38 +0200 Subject: s390/netiucv: improve endianness handling Replace ntohs with endianness conversion for the SKB protocol assignment to avoid an endianness warning reported by sparse. No functional change. Signed-off-by: Hans Wippel Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/netiucv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 3f85b97ab8d2..dba94b486f05 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -635,7 +635,7 @@ static void netiucv_unpack_skb(struct iucv_connection *conn, skb_put(pskb, NETIUCV_HDRLEN); pskb->dev = dev; pskb->ip_summed = CHECKSUM_NONE; - pskb->protocol = ntohs(ETH_P_IP); + pskb->protocol = cpu_to_be16(ETH_P_IP); while (1) { struct sk_buff *skb; -- cgit From 10a0176e4e6eb6243c4b1c55e50372e03139d592 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Fri, 7 Apr 2017 11:04:57 +0300 Subject: qede: Update receive statistic once per NAPI Currently, each time an ingress packet is passed to networking stack the driver increments a per-queue SW statistic. As we want to have additional fields in the first cache-line of the Rx-queue struct, change flow so this statistic would be updated once per NAPI run. We will later push the statistic to a different cache line. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_fp.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index 1e65038c8fc0..c77e6972ab48 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -624,7 +624,6 @@ static inline void qede_skb_receive(struct qede_dev *edev, __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag); napi_gro_receive(&fp->napi, skb); - rxq->rcv_pkts++; } static void qede_set_gro_params(struct qede_dev *edev, @@ -884,9 +883,9 @@ static inline void qede_tpa_cont(struct qede_dev *edev, "Strange - TPA cont with more than a single len_list entry\n"); } -static void qede_tpa_end(struct qede_dev *edev, - struct qede_fastpath *fp, - struct eth_fast_path_rx_tpa_end_cqe *cqe) +static int qede_tpa_end(struct qede_dev *edev, + struct qede_fastpath *fp, + struct eth_fast_path_rx_tpa_end_cqe *cqe) { struct qede_rx_queue *rxq = fp->rxq; struct qede_agg_info *tpa_info; @@ -934,11 +933,12 @@ static void qede_tpa_end(struct qede_dev *edev, tpa_info->state = QEDE_AGG_STATE_NONE; - return; + return 1; err: tpa_info->state = QEDE_AGG_STATE_NONE; dev_kfree_skb_any(tpa_info->skb); tpa_info->skb = NULL; + return 0; } static u8 qede_check_notunn_csum(u16 flag) @@ -1178,8 +1178,7 @@ static int qede_rx_process_tpa_cqe(struct qede_dev *edev, qede_tpa_cont(edev, rxq, &cqe->fast_path_tpa_cont); return 0; case ETH_RX_CQE_TYPE_TPA_END: - qede_tpa_end(edev, fp, &cqe->fast_path_tpa_end); - return 1; + return qede_tpa_end(edev, fp, &cqe->fast_path_tpa_end); default: return 0; } @@ -1229,7 +1228,7 @@ static int qede_rx_process_cqe(struct qede_dev *edev, /* Run eBPF program if one is attached */ if (xdp_prog) if (!qede_rx_xdp(edev, fp, rxq, xdp_prog, bd, fp_cqe)) - return 1; + return 0; /* If this is an error packet then drop it */ flags = cqe->fast_path_regular.pars_flags.flags; @@ -1290,8 +1289,8 @@ static int qede_rx_int(struct qede_fastpath *fp, int budget) { struct qede_rx_queue *rxq = fp->rxq; struct qede_dev *edev = fp->edev; + int work_done = 0, rcv_pkts = 0; u16 hw_comp_cons, sw_comp_cons; - int work_done = 0; hw_comp_cons = le16_to_cpu(*rxq->hw_cons_ptr); sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring); @@ -1305,12 +1304,14 @@ static int qede_rx_int(struct qede_fastpath *fp, int budget) /* Loop to complete all indicated BDs */ while ((sw_comp_cons != hw_comp_cons) && (work_done < budget)) { - qede_rx_process_cqe(edev, fp, rxq); + rcv_pkts += qede_rx_process_cqe(edev, fp, rxq); qed_chain_recycle_consumed(&rxq->rx_comp_ring); sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring); work_done++; } + rxq->rcv_pkts += rcv_pkts; + /* Allocate replacement buffers */ while (rxq->num_rx_buffers - rxq->filled_buffers) if (qede_alloc_rx_buffer(rxq, false)) -- cgit From 89e1afc44765d8b9f509d15df096494f14463e17 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Fri, 7 Apr 2017 11:04:58 +0300 Subject: qede: Correct XDP forward unmapping Driver is currently using dma_unmap_single() with the address it passed to device for the purpose of forwarding, but the XDP transmission buffer was originally a page allocated for the rx-queue. The mapped address is likely to differ from the original mapped address due to the placement offset. This difference is going to get even bigger once we support headroom. Cache the original mapped address of the page, and use it for unmapping of the buffer when completion arrives for the XDP forwarded packet. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 9 +++++++-- drivers/net/ethernet/qlogic/qede/qede_fp.c | 17 +++++++++-------- drivers/net/ethernet/qlogic/qede/qede_main.c | 8 ++++---- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index e73a4a5165ee..7ab2201a43b2 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -349,6 +349,11 @@ struct sw_tx_bd { #define QEDE_TSO_SPLIT_BD BIT(0) }; +struct sw_tx_xdp { + struct page *page; + dma_addr_t mapping; +}; + struct qede_tx_queue { u8 is_xdp; bool is_legacy; @@ -372,11 +377,11 @@ struct qede_tx_queue { #define QEDE_TXQ_IDX_TO_XDP(edev, idx) ((idx) + QEDE_MAX_TSS_CNT(edev)) /* Regular Tx requires skb + metadata for release purpose, - * while XDP requires only the pages themselves. + * while XDP requires the pages and the mapped address. */ union { struct sw_tx_bd *skbs; - struct page **pages; + struct sw_tx_xdp *xdp; } sw_tx_ring; struct qed_chain tx_pbl; diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index c77e6972ab48..c61cfcfbbd56 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -360,7 +360,8 @@ static int qede_xdp_xmit(struct qede_dev *edev, struct qede_fastpath *fp, metadata->mapping + padding, length, PCI_DMA_TODEVICE); - txq->sw_tx_ring.pages[idx] = metadata->data; + txq->sw_tx_ring.xdp[idx].page = metadata->data; + txq->sw_tx_ring.xdp[idx].mapping = metadata->mapping; txq->sw_tx_prod++; /* Mark the fastpath for future XDP doorbell */ @@ -384,19 +385,19 @@ int qede_txq_has_work(struct qede_tx_queue *txq) static void qede_xdp_tx_int(struct qede_dev *edev, struct qede_tx_queue *txq) { - struct eth_tx_1st_bd *bd; - u16 hw_bd_cons; + u16 hw_bd_cons, idx; hw_bd_cons = le16_to_cpu(*txq->hw_cons_ptr); barrier(); while (hw_bd_cons != qed_chain_get_cons_idx(&txq->tx_pbl)) { - bd = (struct eth_tx_1st_bd *)qed_chain_consume(&txq->tx_pbl); + qed_chain_consume(&txq->tx_pbl); + idx = txq->sw_tx_cons & NUM_TX_BDS_MAX; - dma_unmap_single(&edev->pdev->dev, BD_UNMAP_ADDR(bd), - PAGE_SIZE, DMA_BIDIRECTIONAL); - __free_page(txq->sw_tx_ring.pages[txq->sw_tx_cons & - NUM_TX_BDS_MAX]); + dma_unmap_page(&edev->pdev->dev, + txq->sw_tx_ring.xdp[idx].mapping, + PAGE_SIZE, DMA_BIDIRECTIONAL); + __free_page(txq->sw_tx_ring.xdp[idx].page); txq->sw_tx_cons++; txq->xmit_pkts++; diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index abd99109e532..fa62c37dac7a 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -1251,7 +1251,7 @@ static void qede_free_mem_txq(struct qede_dev *edev, struct qede_tx_queue *txq) { /* Free the parallel SW ring */ if (txq->is_xdp) - kfree(txq->sw_tx_ring.pages); + kfree(txq->sw_tx_ring.xdp); else kfree(txq->sw_tx_ring.skbs); @@ -1269,9 +1269,9 @@ static int qede_alloc_mem_txq(struct qede_dev *edev, struct qede_tx_queue *txq) /* Allocate the parallel driver ring for Tx buffers */ if (txq->is_xdp) { - size = sizeof(*txq->sw_tx_ring.pages) * TX_RING_SIZE; - txq->sw_tx_ring.pages = kzalloc(size, GFP_KERNEL); - if (!txq->sw_tx_ring.pages) + size = sizeof(*txq->sw_tx_ring.xdp) * TX_RING_SIZE; + txq->sw_tx_ring.xdp = kzalloc(size, GFP_KERNEL); + if (!txq->sw_tx_ring.xdp) goto err; } else { size = sizeof(*txq->sw_tx_ring.skbs) * TX_RING_SIZE; -- cgit From 40b8c45492efc97d9d397475bf475d497fbb9174 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Fri, 7 Apr 2017 11:04:59 +0300 Subject: qede: Prevent VFs from using XDP Current implementation of VFs is very tight in regard to queue resources. VFs support for XDP would require quite a bit of additional infrastructure in qede and qed [sharing of queue-zones between queues, more VF cids, mapping of the doorbell bar, etc.]. For now, prevent XDP programs from being attached to VFs. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_filter.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index 107c3fda4792..ecd5b3ab783f 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -537,6 +537,11 @@ int qede_xdp(struct net_device *dev, struct netdev_xdp *xdp) { struct qede_dev *edev = netdev_priv(dev); + if (IS_VF(edev)) { + DP_NOTICE(edev, "VFs don't support XDP\n"); + return -EOPNOTSUPP; + } + switch (xdp->command) { case XDP_SETUP_PROG: return qede_xdp_set(edev, xdp->prog); -- cgit From 15ed8a47ff0571dd268e37002511993b47e996bd Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Fri, 7 Apr 2017 11:05:00 +0300 Subject: qede: Add support for ingress headroom Driver currently doesn't support any headroom; The only 'available' space it has in the head of the buffer is due to the placement offset. In order to allow [later] support of XDP adjustment of headroom, modify the the ingress flow to properly handle a scenario where the packets would have such. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 13 ++++++++----- drivers/net/ethernet/qlogic/qede/qede_fp.c | 23 +++++++++++++---------- drivers/net/ethernet/qlogic/qede/qede_main.c | 5 +++-- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index 7ab2201a43b2..5e7ad25db8ad 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -313,21 +313,24 @@ struct qede_rx_queue { u8 data_direction; u8 rxq_id; + /* Used once per each NAPI run */ + u16 num_rx_buffers; + + u16 rx_headroom; + u32 rx_buf_size; u32 rx_buf_seg_size; - u64 rcv_pkts; - struct sw_rx_data *sw_rx_ring; struct qed_chain rx_bd_ring; struct qed_chain rx_comp_ring ____cacheline_aligned; - /* Used once per each NAPI run */ - u16 num_rx_buffers; - /* GRO */ struct qede_agg_info tpa_info[ETH_TPA_MAX_AGGS_NUM]; + /* Used once per each NAPI run */ + u64 rcv_pkts; + u64 rx_hw_errors; u64 rx_alloc_errors; u64 rx_ip_frags; diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index c61cfcfbbd56..e382d4bdf430 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -87,7 +87,8 @@ int qede_alloc_rx_buffer(struct qede_rx_queue *rxq, bool allow_lazy) rx_bd = (struct eth_rx_bd *)qed_chain_produce(&rxq->rx_bd_ring); WARN_ON(!rx_bd); rx_bd->addr.hi = cpu_to_le32(upper_32_bits(mapping)); - rx_bd->addr.lo = cpu_to_le32(lower_32_bits(mapping)); + rx_bd->addr.lo = cpu_to_le32(lower_32_bits(mapping) + + rxq->rx_headroom); rxq->sw_rx_prod++; rxq->filled_buffers++; @@ -509,7 +510,8 @@ static inline void qede_reuse_page(struct qede_rx_queue *rxq, new_mapping = curr_prod->mapping + curr_prod->page_offset; rx_bd_prod->addr.hi = cpu_to_le32(upper_32_bits(new_mapping)); - rx_bd_prod->addr.lo = cpu_to_le32(lower_32_bits(new_mapping)); + rx_bd_prod->addr.lo = cpu_to_le32(lower_32_bits(new_mapping) + + rxq->rx_headroom); rxq->sw_rx_prod++; curr_cons->data = NULL; @@ -991,13 +993,14 @@ static bool qede_rx_xdp(struct qede_dev *edev, struct qede_rx_queue *rxq, struct bpf_prog *prog, struct sw_rx_data *bd, - struct eth_fast_path_rx_reg_cqe *cqe) + struct eth_fast_path_rx_reg_cqe *cqe, + u16 data_offset) { u16 len = le16_to_cpu(cqe->len_on_first_bd); struct xdp_buff xdp; enum xdp_action act; - xdp.data = page_address(bd->data) + cqe->placement_offset; + xdp.data = page_address(bd->data) + data_offset; xdp.data_end = xdp.data + len; /* Queues always have a full reset currently, so for the time @@ -1026,7 +1029,7 @@ static bool qede_rx_xdp(struct qede_dev *edev, /* Now if there's a transmission problem, we'd still have to * throw current buffer, as replacement was already allocated. */ - if (qede_xdp_xmit(edev, fp, bd, cqe->placement_offset, len)) { + if (qede_xdp_xmit(edev, fp, bd, data_offset, len)) { dma_unmap_page(rxq->dev, bd->mapping, PAGE_SIZE, DMA_BIDIRECTIONAL); __free_page(bd->data); @@ -1053,7 +1056,7 @@ static struct sk_buff *qede_rx_allocate_skb(struct qede_dev *edev, struct sw_rx_data *bd, u16 len, u16 pad) { - unsigned int offset = bd->page_offset; + unsigned int offset = bd->page_offset + pad; struct skb_frag_struct *frag; struct page *page = bd->data; unsigned int pull_len; @@ -1070,7 +1073,7 @@ static struct sk_buff *qede_rx_allocate_skb(struct qede_dev *edev, */ if (len + pad <= edev->rx_copybreak) { memcpy(skb_put(skb, len), - page_address(page) + pad + offset, len); + page_address(page) + offset, len); qede_reuse_page(rxq, bd); goto out; } @@ -1078,7 +1081,7 @@ static struct sk_buff *qede_rx_allocate_skb(struct qede_dev *edev, frag = &skb_shinfo(skb)->frags[0]; skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - page, pad + offset, len, rxq->rx_buf_seg_size); + page, offset, len, rxq->rx_buf_seg_size); va = skb_frag_address(frag); pull_len = eth_get_headlen(va, QEDE_RX_HDR_SIZE); @@ -1224,11 +1227,11 @@ static int qede_rx_process_cqe(struct qede_dev *edev, fp_cqe = &cqe->fast_path_regular; len = le16_to_cpu(fp_cqe->len_on_first_bd); - pad = fp_cqe->placement_offset; + pad = fp_cqe->placement_offset + rxq->rx_headroom; /* Run eBPF program if one is attached */ if (xdp_prog) - if (!qede_rx_xdp(edev, fp, rxq, xdp_prog, bd, fp_cqe)) + if (!qede_rx_xdp(edev, fp, rxq, xdp_prog, bd, fp_cqe, pad)) return 0; /* If this is an error packet then drop it */ diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index fa62c37dac7a..91c3078c1ae4 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -1188,8 +1188,9 @@ static int qede_alloc_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq) rxq->rx_buf_size = NET_IP_ALIGN + ETH_OVERHEAD + edev->ndev->mtu; - if (rxq->rx_buf_size > PAGE_SIZE) - rxq->rx_buf_size = PAGE_SIZE; + /* Make sure that the headroom and payload fit in a single page */ + if (rxq->rx_buf_size + rxq->rx_headroom > PAGE_SIZE) + rxq->rx_buf_size = PAGE_SIZE - rxq->rx_headroom; /* Segment size to spilt a page in multiple equal parts, * unless XDP is used in which case we'd use the entire page. -- cgit From 059eeb07e175086db1f84c1d8d29bb9aa8057797 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Fri, 7 Apr 2017 11:05:01 +0300 Subject: qede: Support XDP adjustment of headers In case an XDP program is attached, reserve XDP_PACKET_HEADROOM bytes at the beginning of the packet for the program to play with. Modify the XDP logic in the driver to fill-in the missing bits and re-calculate offsets and length after the program has finished running to properly reflect the current status of the packet. We can then go and remove the limitation of not supporting XDP programs where xdp_adjust_head is set. Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_filter.c | 5 ----- drivers/net/ethernet/qlogic/qede/qede_fp.c | 17 +++++++++++------ drivers/net/ethernet/qlogic/qede/qede_main.c | 1 + 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index ecd5b3ab783f..b00a4fce44b7 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -520,11 +520,6 @@ static int qede_xdp_set(struct qede_dev *edev, struct bpf_prog *prog) { struct qede_reload_args args; - if (prog && prog->xdp_adjust_head) { - DP_ERR(edev, "Does not support bpf_xdp_adjust_head()\n"); - return -EOPNOTSUPP; - } - /* If we're called, there was already a bpf reference increment */ args.func = &qede_xdp_reload_func; args.u.new_prog = prog; diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index e382d4bdf430..961b1d36b9eb 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -994,14 +994,14 @@ static bool qede_rx_xdp(struct qede_dev *edev, struct bpf_prog *prog, struct sw_rx_data *bd, struct eth_fast_path_rx_reg_cqe *cqe, - u16 data_offset) + u16 *data_offset, u16 *len) { - u16 len = le16_to_cpu(cqe->len_on_first_bd); struct xdp_buff xdp; enum xdp_action act; - xdp.data = page_address(bd->data) + data_offset; - xdp.data_end = xdp.data + len; + xdp.data_hard_start = page_address(bd->data); + xdp.data = xdp.data_hard_start + *data_offset; + xdp.data_end = xdp.data + *len; /* Queues always have a full reset currently, so for the time * being until there's atomic program replace just mark read @@ -1011,6 +1011,10 @@ static bool qede_rx_xdp(struct qede_dev *edev, act = bpf_prog_run_xdp(prog, &xdp); rcu_read_unlock(); + /* Recalculate, as XDP might have changed the headers */ + *data_offset = xdp.data - xdp.data_hard_start; + *len = xdp.data_end - xdp.data; + if (act == XDP_PASS) return true; @@ -1029,7 +1033,7 @@ static bool qede_rx_xdp(struct qede_dev *edev, /* Now if there's a transmission problem, we'd still have to * throw current buffer, as replacement was already allocated. */ - if (qede_xdp_xmit(edev, fp, bd, data_offset, len)) { + if (qede_xdp_xmit(edev, fp, bd, *data_offset, *len)) { dma_unmap_page(rxq->dev, bd->mapping, PAGE_SIZE, DMA_BIDIRECTIONAL); __free_page(bd->data); @@ -1231,7 +1235,8 @@ static int qede_rx_process_cqe(struct qede_dev *edev, /* Run eBPF program if one is attached */ if (xdp_prog) - if (!qede_rx_xdp(edev, fp, rxq, xdp_prog, bd, fp_cqe, pad)) + if (!qede_rx_xdp(edev, fp, rxq, xdp_prog, bd, fp_cqe, + &pad, &len)) return 0; /* If this is an error packet then drop it */ diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 91c3078c1ae4..8c2baf8b2a08 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -1187,6 +1187,7 @@ static int qede_alloc_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq) rxq->num_rx_buffers = edev->q_num_rx_buffers; rxq->rx_buf_size = NET_IP_ALIGN + ETH_OVERHEAD + edev->ndev->mtu; + rxq->rx_headroom = edev->xdp_prog ? XDP_PACKET_HEADROOM : 0; /* Make sure that the headroom and payload fit in a single page */ if (rxq->rx_buf_size + rxq->rx_headroom > PAGE_SIZE) -- cgit From a3bb4560026cb3accd0097266eab21fe7526658e Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:26 +0200 Subject: net: cxgb: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct port_info, use stats from struct net_device. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb/common.h | 1 - drivers/net/ethernet/chelsio/cxgb/cxgb2.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb/common.h b/drivers/net/ethernet/chelsio/cxgb/common.h index 6916c62f2487..94b9482f14a5 100644 --- a/drivers/net/ethernet/chelsio/cxgb/common.h +++ b/drivers/net/ethernet/chelsio/cxgb/common.h @@ -223,7 +223,6 @@ struct port_info { struct cmac *mac; struct cphy *phy; struct link_config link_config; - struct net_device_stats netstats; }; struct sge; diff --git a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c index d8aff7a4b3c7..8623be13bf86 100644 --- a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c +++ b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c @@ -296,7 +296,7 @@ static struct net_device_stats *t1_get_stats(struct net_device *dev) { struct adapter *adapter = dev->ml_priv; struct port_info *p = &adapter->port[dev->if_port]; - struct net_device_stats *ns = &p->netstats; + struct net_device_stats *ns = &dev->stats; const struct cmac_statistics *pstats; /* Do a full update of the MAC stats */ -- cgit From a73be7fe497915867712fa10e57638caeb11c231 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:27 +0200 Subject: net: cxgb3: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct port_info, use stats from struct net_device. Cc: Santosh Raspatur Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb3/adapter.h | 1 - drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb3/adapter.h b/drivers/net/ethernet/chelsio/cxgb3/adapter.h index 8b395b537330..087ff0ffb597 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/adapter.h +++ b/drivers/net/ethernet/chelsio/cxgb3/adapter.h @@ -72,7 +72,6 @@ struct port_info { struct cphy phy; struct cmac mac; struct link_config link_config; - struct net_device_stats netstats; int activity; __be32 iscsi_ipv4addr; struct iscsi_config iscsic; diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index d76491676b51..2ff6bd139c96 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -1489,7 +1489,7 @@ static struct net_device_stats *cxgb_get_stats(struct net_device *dev) { struct port_info *pi = netdev_priv(dev); struct adapter *adapter = pi->adapter; - struct net_device_stats *ns = &pi->netstats; + struct net_device_stats *ns = &dev->stats; const struct mac_stats *pstats; spin_lock(&adapter->stats_lock); -- cgit From a548779bb7da7d4041dc0015989e319ea4a3ae8a Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:28 +0200 Subject: net: dl2k: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct netdev_private, use stats from struct net_device. Also remove the now unnecessary .ndo_get_stats function and the #ifdef'ed increment of the collisions16 counter which doesn't exist in struct net_device_stats. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/dlink/dl2k.c | 45 +++++++++++++++++---------------------- drivers/net/ethernet/dlink/dl2k.h | 1 - 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c index 1e350135f11d..778f974e2928 100644 --- a/drivers/net/ethernet/dlink/dl2k.c +++ b/drivers/net/ethernet/dlink/dl2k.c @@ -878,10 +878,10 @@ tx_error (struct net_device *dev, int tx_status) frame_id = (tx_status & 0xffff0000); printk (KERN_ERR "%s: Transmit error, TxStatus %4.4x, FrameId %d.\n", dev->name, tx_status, frame_id); - np->stats.tx_errors++; + dev->stats.tx_errors++; /* Ttransmit Underrun */ if (tx_status & 0x10) { - np->stats.tx_fifo_errors++; + dev->stats.tx_fifo_errors++; dw16(TxStartThresh, dr16(TxStartThresh) + 0x10); /* Transmit Underrun need to set TxReset, DMARest, FIFOReset */ dw16(ASICCtrl + 2, @@ -903,7 +903,7 @@ tx_error (struct net_device *dev, int tx_status) } /* Late Collision */ if (tx_status & 0x04) { - np->stats.tx_fifo_errors++; + dev->stats.tx_fifo_errors++; /* TxReset and clear FIFO */ dw16(ASICCtrl + 2, TxReset | FIFOReset); /* Wait reset done */ @@ -916,13 +916,8 @@ tx_error (struct net_device *dev, int tx_status) /* Let TxStartThresh stay default value */ } /* Maximum Collisions */ -#ifdef ETHER_STATS if (tx_status & 0x08) - np->stats.collisions16++; -#else - if (tx_status & 0x08) - np->stats.collisions++; -#endif + dev->stats.collisions++; /* Restart the Tx */ dw32(MACCtrl, dr16(MACCtrl) | TxEnable); } @@ -952,15 +947,15 @@ receive_packet (struct net_device *dev) break; /* Update rx error statistics, drop packet. */ if (frame_status & RFS_Errors) { - np->stats.rx_errors++; + dev->stats.rx_errors++; if (frame_status & (RxRuntFrame | RxLengthError)) - np->stats.rx_length_errors++; + dev->stats.rx_length_errors++; if (frame_status & RxFCSError) - np->stats.rx_crc_errors++; + dev->stats.rx_crc_errors++; if (frame_status & RxAlignmentError && np->speed != 1000) - np->stats.rx_frame_errors++; + dev->stats.rx_frame_errors++; if (frame_status & RxFIFOOverrun) - np->stats.rx_fifo_errors++; + dev->stats.rx_fifo_errors++; } else { struct sk_buff *skb; @@ -1096,23 +1091,23 @@ get_stats (struct net_device *dev) /* All statistics registers need to be acknowledged, else statistic overflow could cause problems */ - np->stats.rx_packets += dr32(FramesRcvOk); - np->stats.tx_packets += dr32(FramesXmtOk); - np->stats.rx_bytes += dr32(OctetRcvOk); - np->stats.tx_bytes += dr32(OctetXmtOk); + dev->stats.rx_packets += dr32(FramesRcvOk); + dev->stats.tx_packets += dr32(FramesXmtOk); + dev->stats.rx_bytes += dr32(OctetRcvOk); + dev->stats.tx_bytes += dr32(OctetXmtOk); - np->stats.multicast = dr32(McstFramesRcvdOk); - np->stats.collisions += dr32(SingleColFrames) + dev->stats.multicast = dr32(McstFramesRcvdOk); + dev->stats.collisions += dr32(SingleColFrames) + dr32(MultiColFrames); /* detailed tx errors */ stat_reg = dr16(FramesAbortXSColls); - np->stats.tx_aborted_errors += stat_reg; - np->stats.tx_errors += stat_reg; + dev->stats.tx_aborted_errors += stat_reg; + dev->stats.tx_errors += stat_reg; stat_reg = dr16(CarrierSenseErrors); - np->stats.tx_carrier_errors += stat_reg; - np->stats.tx_errors += stat_reg; + dev->stats.tx_carrier_errors += stat_reg; + dev->stats.tx_errors += stat_reg; /* Clear all other statistic register. */ dr32(McstOctetXmtOk); @@ -1142,7 +1137,7 @@ get_stats (struct net_device *dev) dr16(TCPCheckSumErrors); dr16(UDPCheckSumErrors); dr16(IPCheckSumErrors); - return &np->stats; + return &dev->stats; } static int diff --git a/drivers/net/ethernet/dlink/dl2k.h b/drivers/net/ethernet/dlink/dl2k.h index 5d8ae5320242..10e98ba33ebf 100644 --- a/drivers/net/ethernet/dlink/dl2k.h +++ b/drivers/net/ethernet/dlink/dl2k.h @@ -377,7 +377,6 @@ struct netdev_private { void __iomem *eeprom_addr; spinlock_t tx_lock; spinlock_t rx_lock; - struct net_device_stats stats; unsigned int rx_buf_sz; /* Based on MTU+slack. */ unsigned int speed; /* Operating speed */ unsigned int vlan; /* VLAN Id */ -- cgit From 065f4b6992d02ba4ef27295687bdfbdfc7e85964 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:29 +0200 Subject: net: emac: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct emac_instance, use stats from struct net_device. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/emac/core.c | 2 +- drivers/net/ethernet/ibm/emac/core.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 0f9cd92fdc5e..508923f39ccf 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -1929,7 +1929,7 @@ static struct net_device_stats *emac_stats(struct net_device *ndev) struct emac_instance *dev = netdev_priv(ndev); struct emac_stats *st = &dev->stats; struct emac_error_stats *est = &dev->estats; - struct net_device_stats *nst = &dev->nstats; + struct net_device_stats *nst = &ndev->stats; unsigned long flags; DBG2(dev, "stats" NL); diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h index 0710a6685489..f10e156641d5 100644 --- a/drivers/net/ethernet/ibm/emac/core.h +++ b/drivers/net/ethernet/ibm/emac/core.h @@ -265,7 +265,6 @@ struct emac_instance { /* Stats */ struct emac_error_stats estats; - struct net_device_stats nstats; struct emac_stats stats; /* Misc -- cgit From 5f1d3a5c5ec3e020c1babbeb9d0f435fdf13bc39 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:30 +0200 Subject: net: macb: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct macb, use stats from struct net_device. Cc: Nicolas Ferre Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb.c | 40 ++++++++++++++++++------------------- drivers/net/ethernet/cadence/macb.h | 1 - 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 30606b11b128..5cbd1e7a926a 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -684,8 +684,8 @@ static void macb_tx_error_task(struct work_struct *work) netdev_vdbg(bp->dev, "txerr skb %u (data %p) TX complete\n", macb_tx_ring_wrap(bp, tail), skb->data); - bp->stats.tx_packets++; - bp->stats.tx_bytes += skb->len; + bp->dev->stats.tx_packets++; + bp->dev->stats.tx_bytes += skb->len; } } else { /* "Buffers exhausted mid-frame" errors may only happen @@ -778,8 +778,8 @@ static void macb_tx_interrupt(struct macb_queue *queue) netdev_vdbg(bp->dev, "skb %u (data %p) TX complete\n", macb_tx_ring_wrap(bp, tail), skb->data); - bp->stats.tx_packets++; - bp->stats.tx_bytes += skb->len; + bp->dev->stats.tx_packets++; + bp->dev->stats.tx_bytes += skb->len; } /* Now we can safely release resources */ @@ -911,14 +911,14 @@ static int gem_rx(struct macb *bp, int budget) if (!(ctrl & MACB_BIT(RX_SOF) && ctrl & MACB_BIT(RX_EOF))) { netdev_err(bp->dev, "not whole frame pointed by descriptor\n"); - bp->stats.rx_dropped++; + bp->dev->stats.rx_dropped++; break; } skb = bp->rx_skbuff[entry]; if (unlikely(!skb)) { netdev_err(bp->dev, "inconsistent Rx descriptor chain\n"); - bp->stats.rx_dropped++; + bp->dev->stats.rx_dropped++; break; } /* now everything is ready for receiving packet */ @@ -938,8 +938,8 @@ static int gem_rx(struct macb *bp, int budget) GEM_BFEXT(RX_CSUM, ctrl) & GEM_RX_CSUM_CHECKED_MASK) skb->ip_summed = CHECKSUM_UNNECESSARY; - bp->stats.rx_packets++; - bp->stats.rx_bytes += skb->len; + bp->dev->stats.rx_packets++; + bp->dev->stats.rx_bytes += skb->len; #if defined(DEBUG) && defined(VERBOSE_DEBUG) netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n", @@ -984,7 +984,7 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, */ skb = netdev_alloc_skb(bp->dev, len + NET_IP_ALIGN); if (!skb) { - bp->stats.rx_dropped++; + bp->dev->stats.rx_dropped++; for (frag = first_frag; ; frag++) { desc = macb_rx_desc(bp, frag); desc->addr &= ~MACB_BIT(RX_USED); @@ -1030,8 +1030,8 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, __skb_pull(skb, NET_IP_ALIGN); skb->protocol = eth_type_trans(skb, bp->dev); - bp->stats.rx_packets++; - bp->stats.rx_bytes += skb->len; + bp->dev->stats.rx_packets++; + bp->dev->stats.rx_bytes += skb->len; netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n", skb->len, skb->csum); netif_receive_skb(skb); @@ -2210,7 +2210,7 @@ static void gem_update_stats(struct macb *bp) static struct net_device_stats *gem_get_stats(struct macb *bp) { struct gem_stats *hwstat = &bp->hw_stats.gem; - struct net_device_stats *nstat = &bp->stats; + struct net_device_stats *nstat = &bp->dev->stats; gem_update_stats(bp); @@ -2281,7 +2281,7 @@ static void gem_get_ethtool_strings(struct net_device *dev, u32 sset, u8 *p) static struct net_device_stats *macb_get_stats(struct net_device *dev) { struct macb *bp = netdev_priv(dev); - struct net_device_stats *nstat = &bp->stats; + struct net_device_stats *nstat = &bp->dev->stats; struct macb_stats *hwstat = &bp->hw_stats.macb; if (macb_is_gem(bp)) @@ -2993,15 +2993,15 @@ static void at91ether_rx(struct net_device *dev) memcpy(skb_put(skb, pktlen), p_recv, pktlen); skb->protocol = eth_type_trans(skb, dev); - lp->stats.rx_packets++; - lp->stats.rx_bytes += pktlen; + dev->stats.rx_packets++; + dev->stats.rx_bytes += pktlen; netif_rx(skb); } else { - lp->stats.rx_dropped++; + dev->stats.rx_dropped++; } if (desc->ctrl & MACB_BIT(RX_MHASH_MATCH)) - lp->stats.multicast++; + dev->stats.multicast++; /* reset ownership bit */ desc->addr &= ~MACB_BIT(RX_USED); @@ -3036,15 +3036,15 @@ static irqreturn_t at91ether_interrupt(int irq, void *dev_id) if (intstatus & MACB_BIT(TCOMP)) { /* The TCOM bit is set even if the transmission failed */ if (intstatus & (MACB_BIT(ISR_TUND) | MACB_BIT(ISR_RLE))) - lp->stats.tx_errors++; + dev->stats.tx_errors++; if (lp->skb) { dev_kfree_skb_irq(lp->skb); lp->skb = NULL; dma_unmap_single(NULL, lp->skb_physaddr, lp->skb_length, DMA_TO_DEVICE); - lp->stats.tx_packets++; - lp->stats.tx_bytes += lp->skb_length; + dev->stats.tx_packets++; + dev->stats.tx_bytes += lp->skb_length; } netif_wake_queue(dev); } diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 234a49eaccfd..ec037b0fa2a4 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -919,7 +919,6 @@ struct macb { struct clk *rx_clk; struct net_device *dev; struct napi_struct napi; - struct net_device_stats stats; union { struct macb_stats macb; struct gem_stats gem; -- cgit From 8bf66b9d791d2de7ed43d34097ea6c2669e923d9 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:31 +0200 Subject: net: moxa: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct moxart_mac_priv_t, use stats from struct net_device. Also remove the now unnecessary .ndo_get_stats function. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/moxa/moxart_ether.c | 28 ++++++++++------------------ drivers/net/ethernet/moxa/moxart_ether.h | 1 - 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index 6ad44be08b33..c0d7d5eec7e7 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -228,8 +228,8 @@ static int moxart_rx_poll(struct napi_struct *napi, int budget) if (desc0 & (RX_DESC0_ERR | RX_DESC0_CRC_ERR | RX_DESC0_FTL | RX_DESC0_RUNT | RX_DESC0_ODD_NB)) { net_dbg_ratelimited("packet error\n"); - priv->stats.rx_dropped++; - priv->stats.rx_errors++; + ndev->stats.rx_dropped++; + ndev->stats.rx_errors++; goto rx_next; } @@ -245,8 +245,8 @@ static int moxart_rx_poll(struct napi_struct *napi, int budget) if (unlikely(!skb)) { net_dbg_ratelimited("netdev_alloc_skb_ip_align failed\n"); - priv->stats.rx_dropped++; - priv->stats.rx_errors++; + ndev->stats.rx_dropped++; + ndev->stats.rx_errors++; goto rx_next; } @@ -256,10 +256,10 @@ static int moxart_rx_poll(struct napi_struct *napi, int budget) napi_gro_receive(&priv->napi, skb); rx++; - priv->stats.rx_packets++; - priv->stats.rx_bytes += len; + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += len; if (desc0 & RX_DESC0_MULTICAST) - priv->stats.multicast++; + ndev->stats.multicast++; rx_next: wmb(); /* prevent setting ownership back too early */ @@ -296,8 +296,8 @@ static void moxart_tx_finished(struct net_device *ndev) dma_unmap_single(&ndev->dev, priv->tx_mapping[tx_tail], priv->tx_len[tx_tail], DMA_TO_DEVICE); - priv->stats.tx_packets++; - priv->stats.tx_bytes += priv->tx_skb[tx_tail]->len; + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += priv->tx_skb[tx_tail]->len; dev_kfree_skb_irq(priv->tx_skb[tx_tail]); priv->tx_skb[tx_tail] = NULL; @@ -349,7 +349,7 @@ static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev) if (moxart_desc_read(desc + TX_REG_OFFSET_DESC0) & TX_DESC0_DMA_OWN) { net_dbg_ratelimited("no TX space for packet\n"); - priv->stats.tx_dropped++; + ndev->stats.tx_dropped++; goto out_unlock; } rmb(); /* ensure data is only read that had TX_DESC0_DMA_OWN cleared */ @@ -400,13 +400,6 @@ out_unlock: return ret; } -static struct net_device_stats *moxart_mac_get_stats(struct net_device *ndev) -{ - struct moxart_mac_priv_t *priv = netdev_priv(ndev); - - return &priv->stats; -} - static void moxart_mac_setmulticast(struct net_device *ndev) { struct moxart_mac_priv_t *priv = netdev_priv(ndev); @@ -456,7 +449,6 @@ static const struct net_device_ops moxart_netdev_ops = { .ndo_open = moxart_mac_open, .ndo_stop = moxart_mac_stop, .ndo_start_xmit = moxart_mac_start_xmit, - .ndo_get_stats = moxart_mac_get_stats, .ndo_set_rx_mode = moxart_mac_set_rx_mode, .ndo_set_mac_address = moxart_set_mac_address, .ndo_validate_addr = eth_validate_addr, diff --git a/drivers/net/ethernet/moxa/moxart_ether.h b/drivers/net/ethernet/moxa/moxart_ether.h index afc32ec998c0..686b8957d5cf 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.h +++ b/drivers/net/ethernet/moxa/moxart_ether.h @@ -293,7 +293,6 @@ struct moxart_mac_priv_t { void __iomem *base; - struct net_device_stats stats; unsigned int reg_maccr; unsigned int reg_imr; struct napi_struct napi; -- cgit From 71ef3cbb30444ebfba04f2a3afcf8c4743210ddd Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:32 +0200 Subject: net: nmlan_cs: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct _mace_private, use stats from struct net_device. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/nmclan_cs.c | 49 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c index b556c926557a..9c152d85840d 100644 --- a/drivers/net/ethernet/amd/nmclan_cs.c +++ b/drivers/net/ethernet/amd/nmclan_cs.c @@ -359,7 +359,6 @@ typedef struct _mace_statistics { typedef struct _mace_private { struct pcmcia_device *p_dev; - struct net_device_stats linux_stats; /* Linux statistics counters */ mace_statistics mace_stats; /* MACE chip statistics counters */ /* restore_multicast_list() state variables */ @@ -879,7 +878,7 @@ static netdev_tx_t mace_start_xmit(struct sk_buff *skb, service a transmit interrupt while we are in here. */ - lp->linux_stats.tx_bytes += skb->len; + dev->stats.tx_bytes += skb->len; lp->tx_free_frames--; /* WARNING: Write the _exact_ number of bytes written in the header! */ @@ -967,7 +966,7 @@ static irqreturn_t mace_interrupt(int irq, void *dev_id) fifofc = inb(ioaddr + AM2150_MACE_BASE + MACE_FIFOFC); if ((fifofc & MACE_FIFOFC_XMTFC)==0) { - lp->linux_stats.tx_errors++; + dev->stats.tx_errors++; outb(0xFF, ioaddr + AM2150_XMT_SKIP); } @@ -1016,7 +1015,7 @@ static irqreturn_t mace_interrupt(int irq, void *dev_id) } /* if (xmtfs & MACE_XMTFS_XMTSV) */ - lp->linux_stats.tx_packets++; + dev->stats.tx_packets++; lp->tx_free_frames++; netif_wake_queue(dev); } /* if (status & MACE_IR_XMTINT) */ @@ -1077,7 +1076,7 @@ static int mace_rx(struct net_device *dev, unsigned char RxCnt) " 0x%X.\n", dev->name, rx_framecnt, rx_status); if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */ - lp->linux_stats.rx_errors++; + dev->stats.rx_errors++; if (rx_status & MACE_RCVFS_OFLO) { lp->mace_stats.oflo++; } @@ -1114,14 +1113,14 @@ static int mace_rx(struct net_device *dev, unsigned char RxCnt) netif_rx(skb); /* Send the packet to the upper (protocol) layers. */ - lp->linux_stats.rx_packets++; - lp->linux_stats.rx_bytes += pkt_len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */ continue; } else { pr_debug("%s: couldn't allocate a sk_buff of size" " %d.\n", dev->name, pkt_len); - lp->linux_stats.rx_dropped++; + dev->stats.rx_dropped++; } } outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */ @@ -1231,13 +1230,13 @@ static void update_stats(unsigned int ioaddr, struct net_device *dev) lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC); lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC); /* At this point, mace_stats is fully updated for this call. - We may now update the linux_stats. */ + We may now update the netdev stats. */ - /* The MACE has no equivalent for linux_stats field which are commented + /* The MACE has no equivalent for netdev stats field which are commented out. */ - /* lp->linux_stats.multicast; */ - lp->linux_stats.collisions = + /* dev->stats.multicast; */ + dev->stats.collisions = lp->mace_stats.rcvcco * 256 + lp->mace_stats.rcvcc; /* Collision: The MACE may retry sending a packet 15 times before giving up. The retry count is in XMTRC. @@ -1245,22 +1244,22 @@ static void update_stats(unsigned int ioaddr, struct net_device *dev) If so, why doesn't the RCVCC record these collisions? */ /* detailed rx_errors: */ - lp->linux_stats.rx_length_errors = + dev->stats.rx_length_errors = lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc; - /* lp->linux_stats.rx_over_errors */ - lp->linux_stats.rx_crc_errors = lp->mace_stats.fcs; - lp->linux_stats.rx_frame_errors = lp->mace_stats.fram; - lp->linux_stats.rx_fifo_errors = lp->mace_stats.oflo; - lp->linux_stats.rx_missed_errors = + /* dev->stats.rx_over_errors */ + dev->stats.rx_crc_errors = lp->mace_stats.fcs; + dev->stats.rx_frame_errors = lp->mace_stats.fram; + dev->stats.rx_fifo_errors = lp->mace_stats.oflo; + dev->stats.rx_missed_errors = lp->mace_stats.mpco * 256 + lp->mace_stats.mpc; /* detailed tx_errors */ - lp->linux_stats.tx_aborted_errors = lp->mace_stats.rtry; - lp->linux_stats.tx_carrier_errors = lp->mace_stats.lcar; + dev->stats.tx_aborted_errors = lp->mace_stats.rtry; + dev->stats.tx_carrier_errors = lp->mace_stats.lcar; /* LCAR usually results from bad cabling. */ - lp->linux_stats.tx_fifo_errors = lp->mace_stats.uflo; - lp->linux_stats.tx_heartbeat_errors = lp->mace_stats.cerr; - /* lp->linux_stats.tx_window_errors; */ + dev->stats.tx_fifo_errors = lp->mace_stats.uflo; + dev->stats.tx_heartbeat_errors = lp->mace_stats.cerr; + /* dev->stats.tx_window_errors; */ } /* update_stats */ /* ---------------------------------------------------------------------------- @@ -1274,10 +1273,10 @@ static struct net_device_stats *mace_get_stats(struct net_device *dev) update_stats(dev->base_addr, dev); pr_debug("%s: updating the statistics.\n", dev->name); - pr_linux_stats(&lp->linux_stats); + pr_linux_stats(&dev->stats); pr_mace_stats(&lp->mace_stats); - return &lp->linux_stats; + return &dev->stats; } /* net_device_stats */ /* ---------------------------------------------------------------------------- -- cgit From b09a9537c9925cc89815620b0bba773154edcad7 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:33 +0200 Subject: net: nuvoton: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct w90p910_ether, use stats from struct net_device. Also remove the now unnecessary .ndo_get_stats function. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/nuvoton/w90p910_ether.c | 33 ++++++++++------------------ 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/nuvoton/w90p910_ether.c b/drivers/net/ethernet/nuvoton/w90p910_ether.c index 9709c8ca0774..159564d8dcdb 100644 --- a/drivers/net/ethernet/nuvoton/w90p910_ether.c +++ b/drivers/net/ethernet/nuvoton/w90p910_ether.c @@ -152,7 +152,6 @@ struct w90p910_ether { struct tran_pdesc *tdesc; dma_addr_t rdesc_phys; dma_addr_t tdesc_phys; - struct net_device_stats stats; struct platform_device *pdev; struct resource *res; struct sk_buff *skb; @@ -584,15 +583,6 @@ static int w90p910_ether_close(struct net_device *dev) return 0; } -static struct net_device_stats *w90p910_ether_stats(struct net_device *dev) -{ - struct w90p910_ether *ether; - - ether = netdev_priv(dev); - - return ðer->stats; -} - static int w90p910_send_frame(struct net_device *dev, unsigned char *data, int length) { @@ -671,10 +661,10 @@ static irqreturn_t w90p910_tx_interrupt(int irq, void *dev_id) ether->finish_tx = 0; if (txbd->sl & TXDS_TXCP) { - ether->stats.tx_packets++; - ether->stats.tx_bytes += txbd->sl & 0xFFFF; + dev->stats.tx_packets++; + dev->stats.tx_bytes += txbd->sl & 0xFFFF; } else { - ether->stats.tx_errors++; + dev->stats.tx_errors++; } txbd->sl = 0x0; @@ -730,7 +720,7 @@ static void netdev_rx(struct net_device *dev) data = ether->rdesc->recv_buf[ether->cur_rx]; skb = netdev_alloc_skb(dev, length + 2); if (!skb) { - ether->stats.rx_dropped++; + dev->stats.rx_dropped++; return; } @@ -738,24 +728,24 @@ static void netdev_rx(struct net_device *dev) skb_put(skb, length); skb_copy_to_linear_data(skb, data, length); skb->protocol = eth_type_trans(skb, dev); - ether->stats.rx_packets++; - ether->stats.rx_bytes += length; + dev->stats.rx_packets++; + dev->stats.rx_bytes += length; netif_rx(skb); } else { - ether->stats.rx_errors++; + dev->stats.rx_errors++; if (status & RXDS_RP) { dev_err(&pdev->dev, "rx runt err\n"); - ether->stats.rx_length_errors++; + dev->stats.rx_length_errors++; } else if (status & RXDS_CRCE) { dev_err(&pdev->dev, "rx crc err\n"); - ether->stats.rx_crc_errors++; + dev->stats.rx_crc_errors++; } else if (status & RXDS_ALIE) { dev_err(&pdev->dev, "rx alignment err\n"); - ether->stats.rx_frame_errors++; + dev->stats.rx_frame_errors++; } else if (status & RXDS_PTLE) { dev_err(&pdev->dev, "rx longer err\n"); - ether->stats.rx_over_errors++; + dev->stats.rx_over_errors++; } } @@ -912,7 +902,6 @@ static const struct net_device_ops w90p910_ether_netdev_ops = { .ndo_open = w90p910_ether_open, .ndo_stop = w90p910_ether_close, .ndo_start_xmit = w90p910_ether_start_xmit, - .ndo_get_stats = w90p910_ether_stats, .ndo_set_rx_mode = w90p910_ether_set_multicast_list, .ndo_set_mac_address = w90p910_set_mac_address, .ndo_do_ioctl = w90p910_ether_ioctl, -- cgit From 0ffa9373a06c65e32a05d35656b2260857d1e5ce Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:34 +0200 Subject: net: sunbmac: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct bigmac, use stats from struct net_device. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunbmac.c | 18 +++++++++--------- drivers/net/ethernet/sun/sunbmac.h | 1 - 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/sun/sunbmac.c b/drivers/net/ethernet/sun/sunbmac.c index c4caf486cbef..3189722110c2 100644 --- a/drivers/net/ethernet/sun/sunbmac.c +++ b/drivers/net/ethernet/sun/sunbmac.c @@ -169,7 +169,7 @@ static void bigmac_stop(struct bigmac *bp) static void bigmac_get_counters(struct bigmac *bp, void __iomem *bregs) { - struct net_device_stats *stats = &bp->enet_stats; + struct net_device_stats *stats = &bp->dev->stats; stats->rx_crc_errors += sbus_readl(bregs + BMAC_RCRCECTR); sbus_writel(0, bregs + BMAC_RCRCECTR); @@ -774,8 +774,8 @@ static void bigmac_tx(struct bigmac *bp) if (this->tx_flags & TXD_OWN) break; skb = bp->tx_skbs[elem]; - bp->enet_stats.tx_packets++; - bp->enet_stats.tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; dma_unmap_single(&bp->bigmac_op->dev, this->tx_addr, skb->len, DMA_TO_DEVICE); @@ -811,12 +811,12 @@ static void bigmac_rx(struct bigmac *bp) /* Check for errors. */ if (len < ETH_ZLEN) { - bp->enet_stats.rx_errors++; - bp->enet_stats.rx_length_errors++; + bp->dev->stats.rx_errors++; + bp->dev->stats.rx_length_errors++; drop_it: /* Return it to the BigMAC. */ - bp->enet_stats.rx_dropped++; + bp->dev->stats.rx_dropped++; this->rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); goto next; @@ -875,8 +875,8 @@ static void bigmac_rx(struct bigmac *bp) /* No checksums done by the BigMAC ;-( */ skb->protocol = eth_type_trans(skb, bp->dev); netif_rx(skb); - bp->enet_stats.rx_packets++; - bp->enet_stats.rx_bytes += len; + bp->dev->stats.rx_packets++; + bp->dev->stats.rx_bytes += len; next: elem = NEXT_RX(elem); this = &rxbase[elem]; @@ -987,7 +987,7 @@ static struct net_device_stats *bigmac_get_stats(struct net_device *dev) struct bigmac *bp = netdev_priv(dev); bigmac_get_counters(bp, bp->bregs); - return &bp->enet_stats; + return &dev->stats; } static void bigmac_set_multicast(struct net_device *dev) diff --git a/drivers/net/ethernet/sun/sunbmac.h b/drivers/net/ethernet/sun/sunbmac.h index 532fc56830cf..ee56930475a8 100644 --- a/drivers/net/ethernet/sun/sunbmac.h +++ b/drivers/net/ethernet/sun/sunbmac.h @@ -311,7 +311,6 @@ struct bigmac { enum bigmac_timer_state timer_state; unsigned int timer_ticks; - struct net_device_stats enet_stats; struct platform_device *qec_op; struct platform_device *bigmac_op; struct net_device *dev; -- cgit From e807bcc7b98b1f9f293c2b587de1c36f258b12bf Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:35 +0200 Subject: net: sunhme: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct happy_meal, use stats from struct net_device. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunhme.c | 22 +++++++++++----------- drivers/net/ethernet/sun/sunhme.h | 2 -- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/sun/sunhme.c b/drivers/net/ethernet/sun/sunhme.c index 53ff66ef53ac..a6cc9a2d41c1 100644 --- a/drivers/net/ethernet/sun/sunhme.c +++ b/drivers/net/ethernet/sun/sunhme.c @@ -933,7 +933,7 @@ static void happy_meal_stop(struct happy_meal *hp, void __iomem *gregs) /* hp->happy_lock must be held */ static void happy_meal_get_counters(struct happy_meal *hp, void __iomem *bregs) { - struct net_device_stats *stats = &hp->net_stats; + struct net_device_stats *stats = &hp->dev->stats; stats->rx_crc_errors += hme_read32(hp, bregs + BMAC_RCRCECTR); hme_write32(hp, bregs + BMAC_RCRCECTR, 0); @@ -1947,7 +1947,7 @@ static void happy_meal_tx(struct happy_meal *hp) break; } hp->tx_skbs[elem] = NULL; - hp->net_stats.tx_bytes += skb->len; + dev->stats.tx_bytes += skb->len; for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) { dma_addr = hme_read_desc32(hp, &this->tx_addr); @@ -1964,7 +1964,7 @@ static void happy_meal_tx(struct happy_meal *hp) } dev_kfree_skb_irq(skb); - hp->net_stats.tx_packets++; + dev->stats.tx_packets++; } hp->tx_old = elem; TXD((">")); @@ -2009,17 +2009,17 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev) /* Check for errors. */ if ((len < ETH_ZLEN) || (flags & RXFLAG_OVERFLOW)) { RXD(("ERR(%08x)]", flags)); - hp->net_stats.rx_errors++; + dev->stats.rx_errors++; if (len < ETH_ZLEN) - hp->net_stats.rx_length_errors++; + dev->stats.rx_length_errors++; if (len & (RXFLAG_OVERFLOW >> 16)) { - hp->net_stats.rx_over_errors++; - hp->net_stats.rx_fifo_errors++; + dev->stats.rx_over_errors++; + dev->stats.rx_fifo_errors++; } /* Return it to the Happy meal. */ drop_it: - hp->net_stats.rx_dropped++; + dev->stats.rx_dropped++; hme_write_rxd(hp, this, (RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)), dma_addr); @@ -2084,8 +2084,8 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev) skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); - hp->net_stats.rx_packets++; - hp->net_stats.rx_bytes += len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; next: elem = NEXT_RX(elem); this = &rxbase[elem]; @@ -2396,7 +2396,7 @@ static struct net_device_stats *happy_meal_get_stats(struct net_device *dev) happy_meal_get_counters(hp, hp->bigmacregs); spin_unlock_irq(&hp->happy_lock); - return &hp->net_stats; + return &dev->stats; } static void happy_meal_set_multicast(struct net_device *dev) diff --git a/drivers/net/ethernet/sun/sunhme.h b/drivers/net/ethernet/sun/sunhme.h index 4a8d5b18dfd5..3af540adb3c5 100644 --- a/drivers/net/ethernet/sun/sunhme.h +++ b/drivers/net/ethernet/sun/sunhme.h @@ -418,8 +418,6 @@ struct happy_meal { int rx_new, tx_new, rx_old, tx_old; - struct net_device_stats net_stats; /* Statistical counters */ - #if defined(CONFIG_SBUS) && defined(CONFIG_PCI) u32 (*read32)(void __iomem *); void (*write32)(void __iomem *, u32); -- cgit From ae9eb1a7e778e737d92a2b3b1fa6e798bd4c3a3b Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:36 +0200 Subject: net: tulip: de2104x: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct de_private, use stats from struct net_device. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/dec/tulip/de2104x.c | 42 +++++++++++++++----------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/dec/tulip/de2104x.c b/drivers/net/ethernet/dec/tulip/de2104x.c index 127ce9707378..91b8f6f5a765 100644 --- a/drivers/net/ethernet/dec/tulip/de2104x.c +++ b/drivers/net/ethernet/dec/tulip/de2104x.c @@ -312,8 +312,6 @@ struct de_private { u32 msg_enable; - struct net_device_stats net_stats; - struct pci_dev *pdev; u16 setup_frame[DE_SETUP_FRAME_WORDS]; @@ -388,14 +386,14 @@ static void de_rx_err_acct (struct de_private *de, unsigned rx_tail, netif_warn(de, rx_err, de->dev, "Oversized Ethernet frame spanned multiple buffers, status %08x!\n", status); - de->net_stats.rx_length_errors++; + de->dev->stats.rx_length_errors++; } } else if (status & RxError) { /* There was a fatal error. */ - de->net_stats.rx_errors++; /* end of a packet.*/ - if (status & 0x0890) de->net_stats.rx_length_errors++; - if (status & RxErrCRC) de->net_stats.rx_crc_errors++; - if (status & RxErrFIFO) de->net_stats.rx_fifo_errors++; + de->dev->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) de->dev->stats.rx_length_errors++; + if (status & RxErrCRC) de->dev->stats.rx_crc_errors++; + if (status & RxErrFIFO) de->dev->stats.rx_fifo_errors++; } } @@ -423,7 +421,7 @@ static void de_rx (struct de_private *de) mapping = de->rx_skb[rx_tail].mapping; if (unlikely(drop)) { - de->net_stats.rx_dropped++; + de->dev->stats.rx_dropped++; goto rx_next; } @@ -441,7 +439,7 @@ static void de_rx (struct de_private *de) buflen = copying_skb ? (len + RX_OFFSET) : de->rx_buf_sz; copy_skb = netdev_alloc_skb(de->dev, buflen); if (unlikely(!copy_skb)) { - de->net_stats.rx_dropped++; + de->dev->stats.rx_dropped++; drop = 1; rx_work = 100; goto rx_next; @@ -470,8 +468,8 @@ static void de_rx (struct de_private *de) skb->protocol = eth_type_trans (skb, de->dev); - de->net_stats.rx_packets++; - de->net_stats.rx_bytes += skb->len; + de->dev->stats.rx_packets++; + de->dev->stats.rx_bytes += skb->len; rc = netif_rx (skb); if (rc == NET_RX_DROP) drop = 1; @@ -572,18 +570,18 @@ static void de_tx (struct de_private *de) netif_dbg(de, tx_err, de->dev, "tx err, status 0x%x\n", status); - de->net_stats.tx_errors++; + de->dev->stats.tx_errors++; if (status & TxOWC) - de->net_stats.tx_window_errors++; + de->dev->stats.tx_window_errors++; if (status & TxMaxCol) - de->net_stats.tx_aborted_errors++; + de->dev->stats.tx_aborted_errors++; if (status & TxLinkFail) - de->net_stats.tx_carrier_errors++; + de->dev->stats.tx_carrier_errors++; if (status & TxFIFOUnder) - de->net_stats.tx_fifo_errors++; + de->dev->stats.tx_fifo_errors++; } else { - de->net_stats.tx_packets++; - de->net_stats.tx_bytes += skb->len; + de->dev->stats.tx_packets++; + de->dev->stats.tx_bytes += skb->len; netif_dbg(de, tx_done, de->dev, "tx done, slot %d\n", tx_tail); } @@ -814,9 +812,9 @@ static void de_set_rx_mode (struct net_device *dev) static inline void de_rx_missed(struct de_private *de, u32 rx_missed) { if (unlikely(rx_missed & RxMissedOver)) - de->net_stats.rx_missed_errors += RxMissedMask; + de->dev->stats.rx_missed_errors += RxMissedMask; else - de->net_stats.rx_missed_errors += (rx_missed & RxMissedMask); + de->dev->stats.rx_missed_errors += (rx_missed & RxMissedMask); } static void __de_get_stats(struct de_private *de) @@ -836,7 +834,7 @@ static struct net_device_stats *de_get_stats(struct net_device *dev) __de_get_stats(de); spin_unlock_irq(&de->lock); - return &de->net_stats; + return &dev->stats; } static inline int de_is_running (struct de_private *de) @@ -1348,7 +1346,7 @@ static void de_clean_rings (struct de_private *de) struct sk_buff *skb = de->tx_skb[i].skb; if ((skb) && (skb != DE_DUMMY_SKB)) { if (skb != DE_SETUP_SKB) { - de->net_stats.tx_dropped++; + de->dev->stats.tx_dropped++; pci_unmap_single(de->pdev, de->tx_skb[i].mapping, skb->len, PCI_DMA_TODEVICE); -- cgit From 730826bfc3c227fc24436bb5d631a37412655838 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:37 +0200 Subject: net: typhoon: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct typhoon, use stats from struct net_device. Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/ethernet/3com/typhoon.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c index 084a6d58543a..be823c186517 100644 --- a/drivers/net/ethernet/3com/typhoon.c +++ b/drivers/net/ethernet/3com/typhoon.c @@ -283,7 +283,6 @@ struct typhoon { spinlock_t command_lock ____cacheline_aligned; struct basic_ring cmdRing; struct basic_ring respRing; - struct net_device_stats stats; struct net_device_stats stats_saved; struct typhoon_shared * shared; dma_addr_t shared_dma; @@ -898,7 +897,7 @@ typhoon_set_rx_mode(struct net_device *dev) static int typhoon_do_get_stats(struct typhoon *tp) { - struct net_device_stats *stats = &tp->stats; + struct net_device_stats *stats = &tp->dev->stats; struct net_device_stats *saved = &tp->stats_saved; struct cmd_desc xp_cmd; struct resp_desc xp_resp[7]; @@ -951,7 +950,7 @@ static struct net_device_stats * typhoon_get_stats(struct net_device *dev) { struct typhoon *tp = netdev_priv(dev); - struct net_device_stats *stats = &tp->stats; + struct net_device_stats *stats = &tp->dev->stats; struct net_device_stats *saved = &tp->stats_saved; smp_rmb(); @@ -1991,7 +1990,7 @@ typhoon_stop_runtime(struct typhoon *tp, int wait_type) tp->card_state = Sleeping; smp_wmb(); typhoon_do_get_stats(tp); - memcpy(&tp->stats_saved, &tp->stats, sizeof(struct net_device_stats)); + memcpy(&tp->stats_saved, &tp->dev->stats, sizeof(struct net_device_stats)); INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_HALT); typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL); -- cgit From 6ffa770e311a11e760777b8a527a8d9562628b20 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:38 +0200 Subject: usbnet: kaweth: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct kaweth_device, use stats from struct net_device. Also remove the now unnecessary .ndo_get_stats function. Cc: linux-usb@vger.kernel.org Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/usb/kaweth.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index 876f02f4945e..3d8ea18df696 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -245,8 +245,6 @@ struct kaweth_device __u16 packet_filter_bitmap; struct kaweth_ethernet_configuration configuration; - - struct net_device_stats stats; }; /**************************************************************** @@ -598,7 +596,7 @@ static void kaweth_usb_receive(struct urb *urb) struct sk_buff *skb; if (unlikely(status == -EPIPE)) { - kaweth->stats.rx_errors++; + net->stats.rx_errors++; kaweth->end = 1; wake_up(&kaweth->term_wait); dev_dbg(dev, "Status was -EPIPE.\n"); @@ -613,12 +611,12 @@ static void kaweth_usb_receive(struct urb *urb) } if (unlikely(status == -EPROTO || status == -ETIME || status == -EILSEQ)) { - kaweth->stats.rx_errors++; + net->stats.rx_errors++; dev_dbg(dev, "Status was -EPROTO, -ETIME, or -EILSEQ.\n"); return; } if (unlikely(status == -EOVERFLOW)) { - kaweth->stats.rx_errors++; + net->stats.rx_errors++; dev_dbg(dev, "Status was -EOVERFLOW.\n"); } spin_lock(&kaweth->device_lock); @@ -663,8 +661,8 @@ static void kaweth_usb_receive(struct urb *urb) netif_rx(skb); - kaweth->stats.rx_packets++; - kaweth->stats.rx_bytes += pkt_len; + net->stats.rx_packets++; + net->stats.rx_bytes += pkt_len; } kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC); @@ -810,7 +808,7 @@ static netdev_tx_t kaweth_start_xmit(struct sk_buff *skb, dev_kfree_skb_irq(skb); skb = copied_skb; if (!copied_skb) { - kaweth->stats.tx_errors++; + net->stats.tx_errors++; netif_start_queue(net); spin_unlock_irq(&kaweth->device_lock); return NETDEV_TX_OK; @@ -834,15 +832,15 @@ static netdev_tx_t kaweth_start_xmit(struct sk_buff *skb, { dev_warn(&net->dev, "kaweth failed tx_urb %d\n", res); skip: - kaweth->stats.tx_errors++; + net->stats.tx_errors++; netif_start_queue(net); dev_kfree_skb_irq(skb); } else { - kaweth->stats.tx_packets++; - kaweth->stats.tx_bytes += skb->len; + net->stats.tx_packets++; + net->stats.tx_bytes += skb->len; } spin_unlock_irq(&kaweth->device_lock); @@ -911,15 +909,6 @@ static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth) } } -/**************************************************************** - * kaweth_netdev_stats - ****************************************************************/ -static struct net_device_stats *kaweth_netdev_stats(struct net_device *dev) -{ - struct kaweth_device *kaweth = netdev_priv(dev); - return &kaweth->stats; -} - /**************************************************************** * kaweth_tx_timeout ****************************************************************/ @@ -928,7 +917,7 @@ static void kaweth_tx_timeout(struct net_device *net) struct kaweth_device *kaweth = netdev_priv(net); dev_warn(&net->dev, "%s: Tx timed out. Resetting.\n", net->name); - kaweth->stats.tx_errors++; + net->stats.tx_errors++; netif_trans_update(net); usb_unlink_urb(kaweth->tx_urb); @@ -981,7 +970,6 @@ static const struct net_device_ops kaweth_netdev_ops = { .ndo_start_xmit = kaweth_start_xmit, .ndo_tx_timeout = kaweth_tx_timeout, .ndo_set_rx_mode = kaweth_set_rx_mode, - .ndo_get_stats = kaweth_netdev_stats, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; -- cgit From 82d8293895cf3c23ec4957eeed484d5a5ce9021a Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Fri, 7 Apr 2017 10:17:39 +0200 Subject: usbnet: pegasus: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct pegasus, use stats from struct net_device. Also remove the now unnecessary .ndo_get_stats function. Cc: Petko Manolov Cc: linux-usb@vger.kernel.org Signed-off-by: Tobias Klauser Signed-off-by: David S. Miller --- drivers/net/usb/pegasus.c | 36 +++++++++++++++--------------------- drivers/net/usb/pegasus.h | 1 - 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 321e059e13ae..6514c86f043e 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -501,13 +501,13 @@ static void read_bulk_callback(struct urb *urb) if (rx_status & 0x1e) { netif_dbg(pegasus, rx_err, net, "RX packet error %x\n", rx_status); - pegasus->stats.rx_errors++; + net->stats.rx_errors++; if (rx_status & 0x06) /* long or runt */ - pegasus->stats.rx_length_errors++; + net->stats.rx_length_errors++; if (rx_status & 0x08) - pegasus->stats.rx_crc_errors++; + net->stats.rx_crc_errors++; if (rx_status & 0x10) /* extra bits */ - pegasus->stats.rx_frame_errors++; + net->stats.rx_frame_errors++; goto goon; } if (pegasus->chip == 0x8513) { @@ -535,8 +535,8 @@ static void read_bulk_callback(struct urb *urb) skb_put(pegasus->rx_skb, pkt_len); pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net); netif_rx(pegasus->rx_skb); - pegasus->stats.rx_packets++; - pegasus->stats.rx_bytes += pkt_len; + net->stats.rx_packets++; + net->stats.rx_bytes += pkt_len; if (pegasus->flags & PEGASUS_UNPLUG) return; @@ -670,13 +670,13 @@ static void intr_callback(struct urb *urb) /* byte 0 == tx_status1, reg 2B */ if (d[0] & (TX_UNDERRUN|EXCESSIVE_COL |LATE_COL|JABBER_TIMEOUT)) { - pegasus->stats.tx_errors++; + net->stats.tx_errors++; if (d[0] & TX_UNDERRUN) - pegasus->stats.tx_fifo_errors++; + net->stats.tx_fifo_errors++; if (d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT)) - pegasus->stats.tx_aborted_errors++; + net->stats.tx_aborted_errors++; if (d[0] & LATE_COL) - pegasus->stats.tx_window_errors++; + net->stats.tx_window_errors++; } /* d[5].LINK_STATUS lies on some adapters. @@ -685,7 +685,7 @@ static void intr_callback(struct urb *urb) */ /* bytes 3-4 == rx_lostpkt, reg 2E/2F */ - pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4]; + net->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4]; } res = usb_submit_urb(urb, GFP_ATOMIC); @@ -701,7 +701,7 @@ static void pegasus_tx_timeout(struct net_device *net) pegasus_t *pegasus = netdev_priv(net); netif_warn(pegasus, timer, net, "tx timeout\n"); usb_unlink_urb(pegasus->tx_urb); - pegasus->stats.tx_errors++; + net->stats.tx_errors++; } static netdev_tx_t pegasus_start_xmit(struct sk_buff *skb, @@ -731,23 +731,18 @@ static netdev_tx_t pegasus_start_xmit(struct sk_buff *skb, netif_device_detach(pegasus->net); break; default: - pegasus->stats.tx_errors++; + net->stats.tx_errors++; netif_start_queue(net); } } else { - pegasus->stats.tx_packets++; - pegasus->stats.tx_bytes += skb->len; + net->stats.tx_packets++; + net->stats.tx_bytes += skb->len; } dev_kfree_skb(skb); return NETDEV_TX_OK; } -static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) -{ - return &((pegasus_t *) netdev_priv(dev))->stats; -} - static inline void disable_net_traffic(pegasus_t *pegasus) { __le16 tmp = cpu_to_le16(0); @@ -1294,7 +1289,6 @@ static const struct net_device_ops pegasus_netdev_ops = { .ndo_do_ioctl = pegasus_ioctl, .ndo_start_xmit = pegasus_start_xmit, .ndo_set_rx_mode = pegasus_set_multicast, - .ndo_get_stats = pegasus_netdev_stats, .ndo_tx_timeout = pegasus_tx_timeout, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, diff --git a/drivers/net/usb/pegasus.h b/drivers/net/usb/pegasus.h index d15646244fdf..9b7ea9c9167d 100644 --- a/drivers/net/usb/pegasus.h +++ b/drivers/net/usb/pegasus.h @@ -83,7 +83,6 @@ typedef struct pegasus { struct usb_device *usb; struct usb_interface *intf; struct net_device *net; - struct net_device_stats stats; struct mii_if_info mii; unsigned flags; unsigned features; -- cgit From 71fedb0198cbbdacc7e2d6b95e124a3821b5dca2 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Thu, 6 Apr 2017 09:49:08 +0100 Subject: net: stmmac: break some functions into RX and TX scopes This patch breaks several functions into RX and TX scopes, which will be useful when adding multiple buffers mechanism. Signed-off-by: Joao Pinto Tested-by: Niklas Cassel Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 352 ++++++++++++++++------ 1 file changed, 266 insertions(+), 86 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 7cbda41dc996..ff839e1a5017 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -889,24 +889,41 @@ static int stmmac_init_phy(struct net_device *dev) return 0; } -static void stmmac_display_rings(struct stmmac_priv *priv) +static void stmmac_display_rx_rings(struct stmmac_priv *priv) { - void *head_rx, *head_tx; + void *head_rx; - if (priv->extend_desc) { + if (priv->extend_desc) head_rx = (void *)priv->dma_erx; - head_tx = (void *)priv->dma_etx; - } else { + else head_rx = (void *)priv->dma_rx; - head_tx = (void *)priv->dma_tx; - } - /* Display Rx ring */ + /* Display RX ring */ priv->hw->desc->display_ring(head_rx, DMA_RX_SIZE, true); - /* Display Tx ring */ +} + +static void stmmac_display_tx_rings(struct stmmac_priv *priv) +{ + void *head_tx; + + if (priv->extend_desc) + head_tx = (void *)priv->dma_etx; + else + head_tx = (void *)priv->dma_tx; + + /* Display TX ring */ priv->hw->desc->display_ring(head_tx, DMA_TX_SIZE, false); } +static void stmmac_display_rings(struct stmmac_priv *priv) +{ + /* Display RX ring */ + stmmac_display_rx_rings(priv); + + /* Display TX ring */ + stmmac_display_tx_rings(priv); +} + static int stmmac_set_bfsize(int mtu, int bufsize) { int ret = bufsize; @@ -924,16 +941,16 @@ static int stmmac_set_bfsize(int mtu, int bufsize) } /** - * stmmac_clear_descriptors - clear descriptors + * stmmac_clear_rx_descriptors - clear RX descriptors * @priv: driver private structure - * Description: this function is called to clear the tx and rx descriptors + * Description: this function is called to clear the RX descriptors * in case of both basic and extended descriptors are used. */ -static void stmmac_clear_descriptors(struct stmmac_priv *priv) +static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv) { int i; - /* Clear the Rx/Tx descriptors */ + /* Clear the RX descriptors */ for (i = 0; i < DMA_RX_SIZE; i++) if (priv->extend_desc) priv->hw->desc->init_rx_desc(&priv->dma_erx[i].basic, @@ -943,6 +960,19 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv) priv->hw->desc->init_rx_desc(&priv->dma_rx[i], priv->use_riwt, priv->mode, (i == DMA_RX_SIZE - 1)); +} + +/** + * stmmac_clear_tx_descriptors - clear tx descriptors + * @priv: driver private structure + * Description: this function is called to clear the TX descriptors + * in case of both basic and extended descriptors are used. + */ +static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv) +{ + int i; + + /* Clear the TX descriptors */ for (i = 0; i < DMA_TX_SIZE; i++) if (priv->extend_desc) priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic, @@ -954,6 +984,21 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv) (i == DMA_TX_SIZE - 1)); } +/** + * stmmac_clear_descriptors - clear descriptors + * @priv: driver private structure + * Description: this function is called to clear the TX and RX descriptors + * in case of both basic and extended descriptors are used. + */ +static void stmmac_clear_descriptors(struct stmmac_priv *priv) +{ + /* Clear the RX descriptors */ + stmmac_clear_rx_descriptors(priv); + + /* Clear the TX descriptors */ + stmmac_clear_tx_descriptors(priv); +} + /** * stmmac_init_rx_buffers - init the RX descriptor buffer. * @priv: driver private structure @@ -996,7 +1041,12 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, return 0; } -static void stmmac_free_rx_buffers(struct stmmac_priv *priv, int i) +/** + * stmmac_free_rx_buffer - free RX dma buffers + * @priv: private structure + * @i: buffer index. + */ +static void stmmac_free_rx_buffer(struct stmmac_priv *priv, int i) { if (priv->rx_skbuff[i]) { dma_unmap_single(priv->device, priv->rx_skbuff_dma[i], @@ -1007,14 +1057,42 @@ static void stmmac_free_rx_buffers(struct stmmac_priv *priv, int i) } /** - * init_dma_desc_rings - init the RX/TX descriptor rings + * stmmac_free_tx_buffer - free RX dma buffers + * @priv: private structure + * @i: buffer index. + */ +static void stmmac_free_tx_buffer(struct stmmac_priv *priv, int i) +{ + if (priv->tx_skbuff_dma[i].buf) { + if (priv->tx_skbuff_dma[i].map_as_page) + dma_unmap_page(priv->device, + priv->tx_skbuff_dma[i].buf, + priv->tx_skbuff_dma[i].len, + DMA_TO_DEVICE); + else + dma_unmap_single(priv->device, + priv->tx_skbuff_dma[i].buf, + priv->tx_skbuff_dma[i].len, + DMA_TO_DEVICE); + } + + if (priv->tx_skbuff[i]) { + dev_kfree_skb_any(priv->tx_skbuff[i]); + priv->tx_skbuff[i] = NULL; + priv->tx_skbuff_dma[i].buf = 0; + priv->tx_skbuff_dma[i].map_as_page = false; + } +} + +/** + * init_dma_rx_desc_rings - init the RX descriptor rings * @dev: net device structure * @flags: gfp flag. - * Description: this function initializes the DMA RX/TX descriptors + * Description: this function initializes the DMA RX descriptors * and allocates the socket buffers. It supports the chained and ring * modes. */ -static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) +static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags) { int i; struct stmmac_priv *priv = netdev_priv(dev); @@ -1030,10 +1108,8 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) priv->dma_buf_sz = bfsize; netif_dbg(priv, probe, priv->dev, - "(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", - __func__, (u32)priv->dma_rx_phy, (u32)priv->dma_tx_phy); + "(%s) dma_rx_phy=0x%08x\n", __func__, (u32)priv->dma_rx_phy); - /* RX INITIALIZATION */ netif_dbg(priv, probe, priv->dev, "SKB addresses:\nskb\t\tskb data\tdma data\n"); @@ -1058,20 +1134,46 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) /* Setup the chained descriptor addresses */ if (priv->mode == STMMAC_CHAIN_MODE) { - if (priv->extend_desc) { + if (priv->extend_desc) priv->hw->mode->init(priv->dma_erx, priv->dma_rx_phy, DMA_RX_SIZE, 1); - priv->hw->mode->init(priv->dma_etx, priv->dma_tx_phy, - DMA_TX_SIZE, 1); - } else { + else priv->hw->mode->init(priv->dma_rx, priv->dma_rx_phy, DMA_RX_SIZE, 0); + } + + return 0; +err_init_rx_buffers: + while (--i >= 0) + stmmac_free_rx_buffer(priv, i); + return ret; +} + +/** + * init_dma_tx_desc_rings - init the TX descriptor rings + * @dev: net device structure. + * Description: this function initializes the DMA TX descriptors + * and allocates the socket buffers. It supports the chained and ring + * modes. + */ +static int init_dma_tx_desc_rings(struct net_device *dev) +{ + struct stmmac_priv *priv = netdev_priv(dev); + int i; + + netif_dbg(priv, probe, priv->dev, + "(%s) dma_tx_phy=0x%08x\n", __func__, (u32)priv->dma_tx_phy); + + /* Setup the chained descriptor addresses */ + if (priv->mode == STMMAC_CHAIN_MODE) { + if (priv->extend_desc) + priv->hw->mode->init(priv->dma_etx, priv->dma_tx_phy, + DMA_TX_SIZE, 1); + else priv->hw->mode->init(priv->dma_tx, priv->dma_tx_phy, DMA_TX_SIZE, 0); - } } - /* TX INITIALIZATION */ for (i = 0; i < DMA_TX_SIZE; i++) { struct dma_desc *p; if (priv->extend_desc) @@ -1099,62 +1201,69 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) priv->cur_tx = 0; netdev_reset_queue(priv->dev); + return 0; +} + +/** + * init_dma_desc_rings - init the RX/TX descriptor rings + * @dev: net device structure + * @flags: gfp flag. + * Description: this function initializes the DMA RX/TX descriptors + * and allocates the socket buffers. It supports the chained and ring + * modes. + */ +static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) +{ + struct stmmac_priv *priv = netdev_priv(dev); + int ret; + + ret = init_dma_rx_desc_rings(dev, flags); + if (ret) + return ret; + + ret = init_dma_tx_desc_rings(dev); + stmmac_clear_descriptors(priv); if (netif_msg_hw(priv)) stmmac_display_rings(priv); - return 0; -err_init_rx_buffers: - while (--i >= 0) - stmmac_free_rx_buffers(priv, i); return ret; } +/** + * dma_free_rx_skbufs - free RX dma buffers + * @priv: private structure + */ static void dma_free_rx_skbufs(struct stmmac_priv *priv) { int i; for (i = 0; i < DMA_RX_SIZE; i++) - stmmac_free_rx_buffers(priv, i); + stmmac_free_rx_buffer(priv, i); } +/** + * dma_free_tx_skbufs - free TX dma buffers + * @priv: private structure + */ static void dma_free_tx_skbufs(struct stmmac_priv *priv) { int i; - for (i = 0; i < DMA_TX_SIZE; i++) { - if (priv->tx_skbuff_dma[i].buf) { - if (priv->tx_skbuff_dma[i].map_as_page) - dma_unmap_page(priv->device, - priv->tx_skbuff_dma[i].buf, - priv->tx_skbuff_dma[i].len, - DMA_TO_DEVICE); - else - dma_unmap_single(priv->device, - priv->tx_skbuff_dma[i].buf, - priv->tx_skbuff_dma[i].len, - DMA_TO_DEVICE); - } - - if (priv->tx_skbuff[i]) { - dev_kfree_skb_any(priv->tx_skbuff[i]); - priv->tx_skbuff[i] = NULL; - priv->tx_skbuff_dma[i].buf = 0; - priv->tx_skbuff_dma[i].map_as_page = false; - } - } + for (i = 0; i < DMA_TX_SIZE; i++) + stmmac_free_tx_buffer(priv, i); } /** - * alloc_dma_desc_resources - alloc TX/RX resources. + * alloc_dma_rx_desc_resources - alloc RX resources. * @priv: private structure * Description: according to which descriptor can be used (extend or basic) * this function allocates the resources for TX and RX paths. In case of * reception, for example, it pre-allocated the RX socket buffer in order to * allow zero-copy mechanism. */ -static int alloc_dma_desc_resources(struct stmmac_priv *priv) +static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv) { int ret = -ENOMEM; @@ -1168,11 +1277,50 @@ static int alloc_dma_desc_resources(struct stmmac_priv *priv) if (!priv->rx_skbuff) goto err_rx_skbuff; + if (priv->extend_desc) { + priv->dma_erx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE * + sizeof(struct + dma_extended_desc), + &priv->dma_rx_phy, + GFP_KERNEL); + if (!priv->dma_erx) + goto err_dma; + + } else { + priv->dma_rx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE * + sizeof(struct dma_desc), + &priv->dma_rx_phy, + GFP_KERNEL); + if (!priv->dma_rx) + goto err_dma; + } + + return 0; + +err_dma: + kfree(priv->rx_skbuff); +err_rx_skbuff: + kfree(priv->rx_skbuff_dma); + return ret; +} + +/** + * alloc_dma_tx_desc_resources - alloc TX resources. + * @priv: private structure + * Description: according to which descriptor can be used (extend or basic) + * this function allocates the resources for TX and RX paths. In case of + * reception, for example, it pre-allocated the RX socket buffer in order to + * allow zero-copy mechanism. + */ +static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv) +{ + int ret = -ENOMEM; + priv->tx_skbuff_dma = kmalloc_array(DMA_TX_SIZE, sizeof(*priv->tx_skbuff_dma), GFP_KERNEL); if (!priv->tx_skbuff_dma) - goto err_tx_skbuff_dma; + return -ENOMEM; priv->tx_skbuff = kmalloc_array(DMA_TX_SIZE, sizeof(struct sk_buff *), GFP_KERNEL); @@ -1180,14 +1328,6 @@ static int alloc_dma_desc_resources(struct stmmac_priv *priv) goto err_tx_skbuff; if (priv->extend_desc) { - priv->dma_erx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE * - sizeof(struct - dma_extended_desc), - &priv->dma_rx_phy, - GFP_KERNEL); - if (!priv->dma_erx) - goto err_dma; - priv->dma_etx = dma_zalloc_coherent(priv->device, DMA_TX_SIZE * sizeof(struct dma_extended_desc), @@ -1200,13 +1340,6 @@ static int alloc_dma_desc_resources(struct stmmac_priv *priv) goto err_dma; } } else { - priv->dma_rx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE * - sizeof(struct dma_desc), - &priv->dma_rx_phy, - GFP_KERNEL); - if (!priv->dma_rx) - goto err_dma; - priv->dma_tx = dma_zalloc_coherent(priv->device, DMA_TX_SIZE * sizeof(struct dma_desc), &priv->dma_tx_phy, @@ -1225,41 +1358,88 @@ err_dma: kfree(priv->tx_skbuff); err_tx_skbuff: kfree(priv->tx_skbuff_dma); -err_tx_skbuff_dma: - kfree(priv->rx_skbuff); -err_rx_skbuff: - kfree(priv->rx_skbuff_dma); return ret; } -static void free_dma_desc_resources(struct stmmac_priv *priv) +/** + * alloc_dma_desc_resources - alloc TX/RX resources. + * @priv: private structure + * Description: according to which descriptor can be used (extend or basic) + * this function allocates the resources for TX and RX paths. In case of + * reception, for example, it pre-allocated the RX socket buffer in order to + * allow zero-copy mechanism. + */ +static int alloc_dma_desc_resources(struct stmmac_priv *priv) { - /* Release the DMA TX/RX socket buffers */ + int ret = alloc_dma_rx_desc_resources(priv); + + if (ret) + return ret; + + ret = alloc_dma_tx_desc_resources(priv); + + return ret; +} + +/** + * free_dma_rx_desc_resources - free RX dma desc resources + * @priv: private structure + */ +static void free_dma_rx_desc_resources(struct stmmac_priv *priv) +{ + /* Release the DMA RX socket buffers */ dma_free_rx_skbufs(priv); - dma_free_tx_skbufs(priv); /* Free DMA regions of consistent memory previously allocated */ - if (!priv->extend_desc) { - dma_free_coherent(priv->device, - DMA_TX_SIZE * sizeof(struct dma_desc), - priv->dma_tx, priv->dma_tx_phy); + if (!priv->extend_desc) dma_free_coherent(priv->device, DMA_RX_SIZE * sizeof(struct dma_desc), priv->dma_rx, priv->dma_rx_phy); - } else { - dma_free_coherent(priv->device, DMA_TX_SIZE * - sizeof(struct dma_extended_desc), - priv->dma_etx, priv->dma_tx_phy); + else dma_free_coherent(priv->device, DMA_RX_SIZE * sizeof(struct dma_extended_desc), priv->dma_erx, priv->dma_rx_phy); - } + kfree(priv->rx_skbuff_dma); kfree(priv->rx_skbuff); +} + +/** + * free_dma_tx_desc_resources - free TX dma desc resources + * @priv: private structure + */ +static void free_dma_tx_desc_resources(struct stmmac_priv *priv) +{ + /* Release the DMA TX socket buffers */ + dma_free_tx_skbufs(priv); + + /* Free DMA regions of consistent memory previously allocated */ + if (!priv->extend_desc) + dma_free_coherent(priv->device, + DMA_TX_SIZE * sizeof(struct dma_desc), + priv->dma_tx, priv->dma_tx_phy); + else + dma_free_coherent(priv->device, DMA_TX_SIZE * + sizeof(struct dma_extended_desc), + priv->dma_etx, priv->dma_tx_phy); + kfree(priv->tx_skbuff_dma); kfree(priv->tx_skbuff); } +/** + * free_dma_desc_resources - free dma desc resources + * @priv: private structure + */ +static void free_dma_desc_resources(struct stmmac_priv *priv) +{ + /* Release the DMA RX socket buffers */ + free_dma_rx_desc_resources(priv); + + /* Release the DMA TX socket buffers */ + free_dma_tx_desc_resources(priv); +} + /** * stmmac_mac_enable_rx_queues - Enable MAC rx queues * @priv: driver private structure -- cgit From 54139cf3bb33fad075737000f22749cafe3e83a0 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Thu, 6 Apr 2017 09:49:09 +0100 Subject: net: stmmac: adding multiple buffers for rx This patch adds the structure stmmac_rx_queue which contains rx queues specific data (previously in stmmac_priv). Signed-off-by: Joao Pinto Tested-by: Niklas Cassel Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/chain_mode.c | 7 +- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 26 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 457 ++++++++++++++-------- 3 files changed, 306 insertions(+), 184 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c index 01a8c020d6db..8db5a80aa8ec 100644 --- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c @@ -136,15 +136,16 @@ static void stmmac_init_dma_chain(void *des, dma_addr_t phy_addr, static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p) { - struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; + struct stmmac_rx_queue *rx_q = (struct stmmac_rx_queue *)priv_ptr; + struct stmmac_priv *priv = rx_q->priv_data; if (priv->hwts_rx_en && !priv->extend_desc) /* NOTE: Device will overwrite des3 with timestamp value if * 1588-2002 time stamping is enabled, hence reinitialize it * to keep explicit chaining in the descriptor. */ - p->des3 = cpu_to_le32((unsigned int)(priv->dma_rx_phy + - (((priv->dirty_rx) + 1) % + p->des3 = cpu_to_le32((unsigned int)(rx_q->dma_rx_phy + + (((rx_q->dirty_rx) + 1) % DMA_RX_SIZE) * sizeof(struct dma_desc))); } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index cd8fb619b1e9..c7ad9e4f93d2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -46,6 +46,20 @@ struct stmmac_tx_info { bool is_jumbo; }; +struct stmmac_rx_queue { + u32 queue_index; + struct stmmac_priv *priv_data; + struct dma_extended_desc *dma_erx; + struct dma_desc *dma_rx ____cacheline_aligned_in_smp; + struct sk_buff **rx_skbuff; + dma_addr_t *rx_skbuff_dma; + unsigned int cur_rx; + unsigned int dirty_rx; + u32 rx_zeroc_thresh; + dma_addr_t dma_rx_phy; + u32 rx_tail_addr; +}; + struct stmmac_priv { /* Frequently used values are kept adjacent for cache effect */ struct dma_extended_desc *dma_etx ____cacheline_aligned_in_smp; @@ -64,18 +78,10 @@ struct stmmac_priv { struct timer_list txtimer; bool tso; - struct dma_desc *dma_rx ____cacheline_aligned_in_smp; - struct dma_extended_desc *dma_erx; - struct sk_buff **rx_skbuff; - unsigned int cur_rx; - unsigned int dirty_rx; unsigned int dma_buf_sz; unsigned int rx_copybreak; - unsigned int rx_zeroc_thresh; u32 rx_riwt; int hwts_rx_en; - dma_addr_t *rx_skbuff_dma; - dma_addr_t dma_rx_phy; struct napi_struct napi ____cacheline_aligned_in_smp; @@ -85,6 +91,9 @@ struct stmmac_priv { struct mac_device_info *hw; spinlock_t lock; + /* RX Queue */ + struct stmmac_rx_queue rx_queue[MTL_MAX_RX_QUEUES]; + int oldlink; int speed; int oldduplex; @@ -119,7 +128,6 @@ struct stmmac_priv { spinlock_t ptp_lock; void __iomem *mmcaddr; void __iomem *ptpaddr; - u32 rx_tail_addr; u32 tx_tail_addr; u32 mss; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index ff839e1a5017..77caba4cab18 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -197,14 +197,20 @@ static inline u32 stmmac_tx_avail(struct stmmac_priv *priv) return avail; } -static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv) +/** + * stmmac_rx_dirty - Get RX queue dirty + * @priv: driver private structure + * @queue: RX queue index + */ +static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; u32 dirty; - if (priv->dirty_rx <= priv->cur_rx) - dirty = priv->cur_rx - priv->dirty_rx; + if (rx_q->dirty_rx <= rx_q->cur_rx) + dirty = rx_q->cur_rx - rx_q->dirty_rx; else - dirty = DMA_RX_SIZE - priv->dirty_rx + priv->cur_rx; + dirty = DMA_RX_SIZE - rx_q->dirty_rx + rx_q->cur_rx; return dirty; } @@ -891,15 +897,24 @@ static int stmmac_init_phy(struct net_device *dev) static void stmmac_display_rx_rings(struct stmmac_priv *priv) { + u32 rx_cnt = priv->plat->rx_queues_to_use; void *head_rx; + u32 queue; - if (priv->extend_desc) - head_rx = (void *)priv->dma_erx; - else - head_rx = (void *)priv->dma_rx; + /* Display RX rings */ + for (queue = 0; queue < rx_cnt; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - /* Display RX ring */ - priv->hw->desc->display_ring(head_rx, DMA_RX_SIZE, true); + pr_info("\tRX Queue %u rings\n", queue); + + if (priv->extend_desc) + head_rx = (void *)rx_q->dma_erx; + else + head_rx = (void *)rx_q->dma_rx; + + /* Display RX ring */ + priv->hw->desc->display_ring(head_rx, DMA_RX_SIZE, true); + } } static void stmmac_display_tx_rings(struct stmmac_priv *priv) @@ -943,21 +958,23 @@ static int stmmac_set_bfsize(int mtu, int bufsize) /** * stmmac_clear_rx_descriptors - clear RX descriptors * @priv: driver private structure + * @queue: RX queue index * Description: this function is called to clear the RX descriptors * in case of both basic and extended descriptors are used. */ -static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv) +static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; int i; /* Clear the RX descriptors */ for (i = 0; i < DMA_RX_SIZE; i++) if (priv->extend_desc) - priv->hw->desc->init_rx_desc(&priv->dma_erx[i].basic, + priv->hw->desc->init_rx_desc(&rx_q->dma_erx[i].basic, priv->use_riwt, priv->mode, (i == DMA_RX_SIZE - 1)); else - priv->hw->desc->init_rx_desc(&priv->dma_rx[i], + priv->hw->desc->init_rx_desc(&rx_q->dma_rx[i], priv->use_riwt, priv->mode, (i == DMA_RX_SIZE - 1)); } @@ -992,8 +1009,12 @@ static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv) */ static void stmmac_clear_descriptors(struct stmmac_priv *priv) { + u32 rx_queue_cnt = priv->plat->rx_queues_to_use; + u32 queue; + /* Clear the RX descriptors */ - stmmac_clear_rx_descriptors(priv); + for (queue = 0; queue < rx_queue_cnt; queue++) + stmmac_clear_rx_descriptors(priv, queue); /* Clear the TX descriptors */ stmmac_clear_tx_descriptors(priv); @@ -1004,13 +1025,15 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv) * @priv: driver private structure * @p: descriptor pointer * @i: descriptor index - * @flags: gfp flag. + * @flags: gfp flag + * @queue: RX queue index * Description: this function is called to allocate a receive buffer, perform * the DMA mapping and init the descriptor. */ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, - int i, gfp_t flags) + int i, gfp_t flags, u32 queue) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; struct sk_buff *skb; skb = __netdev_alloc_skb_ip_align(priv->dev, priv->dma_buf_sz, flags); @@ -1019,20 +1042,20 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, "%s: Rx init fails; skb is NULL\n", __func__); return -ENOMEM; } - priv->rx_skbuff[i] = skb; - priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, + rx_q->rx_skbuff[i] = skb; + rx_q->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, priv->dma_buf_sz, DMA_FROM_DEVICE); - if (dma_mapping_error(priv->device, priv->rx_skbuff_dma[i])) { + if (dma_mapping_error(priv->device, rx_q->rx_skbuff_dma[i])) { netdev_err(priv->dev, "%s: DMA mapping error\n", __func__); dev_kfree_skb_any(skb); return -EINVAL; } if (priv->synopsys_id >= DWMAC_CORE_4_00) - p->des0 = cpu_to_le32(priv->rx_skbuff_dma[i]); + p->des0 = cpu_to_le32(rx_q->rx_skbuff_dma[i]); else - p->des2 = cpu_to_le32(priv->rx_skbuff_dma[i]); + p->des2 = cpu_to_le32(rx_q->rx_skbuff_dma[i]); if ((priv->hw->mode->init_desc3) && (priv->dma_buf_sz == BUF_SIZE_16KiB)) @@ -1044,16 +1067,19 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, /** * stmmac_free_rx_buffer - free RX dma buffers * @priv: private structure + * @queue: RX queue index * @i: buffer index. */ -static void stmmac_free_rx_buffer(struct stmmac_priv *priv, int i) +static void stmmac_free_rx_buffer(struct stmmac_priv *priv, u32 queue, int i) { - if (priv->rx_skbuff[i]) { - dma_unmap_single(priv->device, priv->rx_skbuff_dma[i], + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + if (rx_q->rx_skbuff[i]) { + dma_unmap_single(priv->device, rx_q->rx_skbuff_dma[i], priv->dma_buf_sz, DMA_FROM_DEVICE); - dev_kfree_skb_any(priv->rx_skbuff[i]); + dev_kfree_skb_any(rx_q->rx_skbuff[i]); } - priv->rx_skbuff[i] = NULL; + rx_q->rx_skbuff[i] = NULL; } /** @@ -1094,10 +1120,12 @@ static void stmmac_free_tx_buffer(struct stmmac_priv *priv, int i) */ static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags) { - int i; struct stmmac_priv *priv = netdev_priv(dev); + u32 rx_count = priv->plat->rx_queues_to_use; unsigned int bfsize = 0; int ret = -ENOMEM; + u32 queue; + int i; if (priv->hw->mode->set_16kib_bfsize) bfsize = priv->hw->mode->set_16kib_bfsize(dev->mtu); @@ -1107,45 +1135,69 @@ static int init_dma_rx_desc_rings(struct net_device *dev, gfp_t flags) priv->dma_buf_sz = bfsize; - netif_dbg(priv, probe, priv->dev, - "(%s) dma_rx_phy=0x%08x\n", __func__, (u32)priv->dma_rx_phy); - + /* RX INITIALIZATION */ netif_dbg(priv, probe, priv->dev, "SKB addresses:\nskb\t\tskb data\tdma data\n"); - for (i = 0; i < DMA_RX_SIZE; i++) { - struct dma_desc *p; - if (priv->extend_desc) - p = &((priv->dma_erx + i)->basic); - else - p = priv->dma_rx + i; + for (queue = 0; queue < rx_count; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - ret = stmmac_init_rx_buffers(priv, p, i, flags); - if (ret) - goto err_init_rx_buffers; + netif_dbg(priv, probe, priv->dev, + "(%s) dma_rx_phy=0x%08x\n", __func__, + (u32)rx_q->dma_rx_phy); - netif_dbg(priv, probe, priv->dev, "[%p]\t[%p]\t[%x]\n", - priv->rx_skbuff[i], priv->rx_skbuff[i]->data, - (unsigned int)priv->rx_skbuff_dma[i]); - } - priv->cur_rx = 0; - priv->dirty_rx = (unsigned int)(i - DMA_RX_SIZE); - buf_sz = bfsize; + for (i = 0; i < DMA_RX_SIZE; i++) { + struct dma_desc *p; - /* Setup the chained descriptor addresses */ - if (priv->mode == STMMAC_CHAIN_MODE) { - if (priv->extend_desc) - priv->hw->mode->init(priv->dma_erx, priv->dma_rx_phy, - DMA_RX_SIZE, 1); - else - priv->hw->mode->init(priv->dma_rx, priv->dma_rx_phy, - DMA_RX_SIZE, 0); + if (priv->extend_desc) + p = &((rx_q->dma_erx + i)->basic); + else + p = rx_q->dma_rx + i; + + ret = stmmac_init_rx_buffers(priv, p, i, flags, + queue); + if (ret) + goto err_init_rx_buffers; + + netif_dbg(priv, probe, priv->dev, "[%p]\t[%p]\t[%x]\n", + rx_q->rx_skbuff[i], rx_q->rx_skbuff[i]->data, + (unsigned int)rx_q->rx_skbuff_dma[i]); + } + + rx_q->cur_rx = 0; + rx_q->dirty_rx = (unsigned int)(i - DMA_RX_SIZE); + + stmmac_clear_rx_descriptors(priv, queue); + + /* Setup the chained descriptor addresses */ + if (priv->mode == STMMAC_CHAIN_MODE) { + if (priv->extend_desc) + priv->hw->mode->init(rx_q->dma_erx, + rx_q->dma_rx_phy, + DMA_RX_SIZE, 1); + else + priv->hw->mode->init(rx_q->dma_rx, + rx_q->dma_rx_phy, + DMA_RX_SIZE, 0); + } } + buf_sz = bfsize; + return 0; + err_init_rx_buffers: - while (--i >= 0) - stmmac_free_rx_buffer(priv, i); + while (queue >= 0) { + while (--i >= 0) + stmmac_free_rx_buffer(priv, queue, i); + + if (queue == 0) + break; + + i = DMA_RX_SIZE; + queue--; + } + return ret; } @@ -1234,13 +1286,14 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) /** * dma_free_rx_skbufs - free RX dma buffers * @priv: private structure + * @queue: RX queue index */ -static void dma_free_rx_skbufs(struct stmmac_priv *priv) +static void dma_free_rx_skbufs(struct stmmac_priv *priv, u32 queue) { int i; for (i = 0; i < DMA_RX_SIZE; i++) - stmmac_free_rx_buffer(priv, i); + stmmac_free_rx_buffer(priv, queue, i); } /** @@ -1255,6 +1308,37 @@ static void dma_free_tx_skbufs(struct stmmac_priv *priv) stmmac_free_tx_buffer(priv, i); } +/** + * free_dma_rx_desc_resources - free RX dma desc resources + * @priv: private structure + */ +static void free_dma_rx_desc_resources(struct stmmac_priv *priv) +{ + u32 rx_count = priv->plat->rx_queues_to_use; + u32 queue; + + /* Free RX queue resources */ + for (queue = 0; queue < rx_count; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + /* Release the DMA RX socket buffers */ + dma_free_rx_skbufs(priv, queue); + + /* Free DMA regions of consistent memory previously allocated */ + if (!priv->extend_desc) + dma_free_coherent(priv->device, + DMA_RX_SIZE * sizeof(struct dma_desc), + rx_q->dma_rx, rx_q->dma_rx_phy); + else + dma_free_coherent(priv->device, DMA_RX_SIZE * + sizeof(struct dma_extended_desc), + rx_q->dma_erx, rx_q->dma_rx_phy); + + kfree(rx_q->rx_skbuff_dma); + kfree(rx_q->rx_skbuff); + } +} + /** * alloc_dma_rx_desc_resources - alloc RX resources. * @priv: private structure @@ -1265,42 +1349,56 @@ static void dma_free_tx_skbufs(struct stmmac_priv *priv) */ static int alloc_dma_rx_desc_resources(struct stmmac_priv *priv) { + u32 rx_count = priv->plat->rx_queues_to_use; int ret = -ENOMEM; + u32 queue; - priv->rx_skbuff_dma = kmalloc_array(DMA_RX_SIZE, sizeof(dma_addr_t), - GFP_KERNEL); - if (!priv->rx_skbuff_dma) - return -ENOMEM; + /* RX queues buffers and DMA */ + for (queue = 0; queue < rx_count; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; - priv->rx_skbuff = kmalloc_array(DMA_RX_SIZE, sizeof(struct sk_buff *), - GFP_KERNEL); - if (!priv->rx_skbuff) - goto err_rx_skbuff; + rx_q->queue_index = queue; + rx_q->priv_data = priv; - if (priv->extend_desc) { - priv->dma_erx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE * - sizeof(struct - dma_extended_desc), - &priv->dma_rx_phy, + rx_q->rx_skbuff_dma = kmalloc_array(DMA_RX_SIZE, + sizeof(dma_addr_t), GFP_KERNEL); - if (!priv->dma_erx) - goto err_dma; + if (!rx_q->rx_skbuff_dma) + return -ENOMEM; - } else { - priv->dma_rx = dma_zalloc_coherent(priv->device, DMA_RX_SIZE * - sizeof(struct dma_desc), - &priv->dma_rx_phy, - GFP_KERNEL); - if (!priv->dma_rx) + rx_q->rx_skbuff = kmalloc_array(DMA_RX_SIZE, + sizeof(struct sk_buff *), + GFP_KERNEL); + if (!rx_q->rx_skbuff) goto err_dma; + + if (priv->extend_desc) { + rx_q->dma_erx = dma_zalloc_coherent(priv->device, + DMA_RX_SIZE * + sizeof(struct + dma_extended_desc), + &rx_q->dma_rx_phy, + GFP_KERNEL); + if (!rx_q->dma_erx) + goto err_dma; + + } else { + rx_q->dma_rx = dma_zalloc_coherent(priv->device, + DMA_RX_SIZE * + sizeof(struct + dma_desc), + &rx_q->dma_rx_phy, + GFP_KERNEL); + if (!rx_q->dma_rx) + goto err_dma; + } } return 0; err_dma: - kfree(priv->rx_skbuff); -err_rx_skbuff: - kfree(priv->rx_skbuff_dma); + free_dma_rx_desc_resources(priv); + return ret; } @@ -1333,23 +1431,15 @@ static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv) dma_extended_desc), &priv->dma_tx_phy, GFP_KERNEL); - if (!priv->dma_etx) { - dma_free_coherent(priv->device, DMA_RX_SIZE * - sizeof(struct dma_extended_desc), - priv->dma_erx, priv->dma_rx_phy); + if (!priv->dma_etx) goto err_dma; - } } else { priv->dma_tx = dma_zalloc_coherent(priv->device, DMA_TX_SIZE * sizeof(struct dma_desc), &priv->dma_tx_phy, GFP_KERNEL); - if (!priv->dma_tx) { - dma_free_coherent(priv->device, DMA_RX_SIZE * - sizeof(struct dma_desc), - priv->dma_rx, priv->dma_rx_phy); + if (!priv->dma_tx) goto err_dma; - } } return 0; @@ -1371,6 +1461,7 @@ err_tx_skbuff: */ static int alloc_dma_desc_resources(struct stmmac_priv *priv) { + /* RX Allocation */ int ret = alloc_dma_rx_desc_resources(priv); if (ret) @@ -1381,29 +1472,6 @@ static int alloc_dma_desc_resources(struct stmmac_priv *priv) return ret; } -/** - * free_dma_rx_desc_resources - free RX dma desc resources - * @priv: private structure - */ -static void free_dma_rx_desc_resources(struct stmmac_priv *priv) -{ - /* Release the DMA RX socket buffers */ - dma_free_rx_skbufs(priv); - - /* Free DMA regions of consistent memory previously allocated */ - if (!priv->extend_desc) - dma_free_coherent(priv->device, - DMA_RX_SIZE * sizeof(struct dma_desc), - priv->dma_rx, priv->dma_rx_phy); - else - dma_free_coherent(priv->device, DMA_RX_SIZE * - sizeof(struct dma_extended_desc), - priv->dma_erx, priv->dma_rx_phy); - - kfree(priv->rx_skbuff_dma); - kfree(priv->rx_skbuff); -} - /** * free_dma_tx_desc_resources - free TX dma desc resources * @priv: private structure @@ -1914,6 +1982,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) { u32 rx_channels_count = priv->plat->rx_queues_to_use; u32 tx_channels_count = priv->plat->tx_queues_to_use; + struct stmmac_rx_queue *rx_q; u32 dummy_dma_rx_phy = 0; u32 dummy_dma_tx_phy = 0; u32 chan = 0; @@ -1941,14 +2010,16 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) /* DMA RX Channel Configuration */ for (chan = 0; chan < rx_channels_count; chan++) { + rx_q = &priv->rx_queue[chan]; + priv->hw->dma->init_rx_chan(priv->ioaddr, priv->plat->dma_cfg, - priv->dma_rx_phy, chan); + rx_q->dma_rx_phy, chan); - priv->rx_tail_addr = priv->dma_rx_phy + + rx_q->rx_tail_addr = rx_q->dma_rx_phy + (DMA_RX_SIZE * sizeof(struct dma_desc)); priv->hw->dma->set_rx_tail_ptr(priv->ioaddr, - priv->rx_tail_addr, + rx_q->rx_tail_addr, chan); } @@ -1969,8 +2040,9 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) chan); } } else { + rx_q = &priv->rx_queue[chan]; priv->hw->dma->init(priv->ioaddr, priv->plat->dma_cfg, - priv->dma_tx_phy, priv->dma_rx_phy, atds); + priv->dma_tx_phy, rx_q->dma_rx_phy, atds); } if (priv->plat->axi && priv->hw->dma->axi) @@ -2942,9 +3014,9 @@ static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb) } -static inline int stmmac_rx_threshold_count(struct stmmac_priv *priv) +static inline int stmmac_rx_threshold_count(struct stmmac_rx_queue *rx_q) { - if (priv->rx_zeroc_thresh < STMMAC_RX_THRESH) + if (rx_q->rx_zeroc_thresh < STMMAC_RX_THRESH) return 0; return 1; @@ -2953,30 +3025,33 @@ static inline int stmmac_rx_threshold_count(struct stmmac_priv *priv) /** * stmmac_rx_refill - refill used skb preallocated buffers * @priv: driver private structure + * @queue: RX queue index * Description : this is to reallocate the skb for the reception process * that is based on zero-copy. */ -static inline void stmmac_rx_refill(struct stmmac_priv *priv) +static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + int dirty = stmmac_rx_dirty(priv, queue); + unsigned int entry = rx_q->dirty_rx; + int bfsize = priv->dma_buf_sz; - unsigned int entry = priv->dirty_rx; - int dirty = stmmac_rx_dirty(priv); while (dirty-- > 0) { struct dma_desc *p; if (priv->extend_desc) - p = (struct dma_desc *)(priv->dma_erx + entry); + p = (struct dma_desc *)(rx_q->dma_erx + entry); else - p = priv->dma_rx + entry; + p = rx_q->dma_rx + entry; - if (likely(priv->rx_skbuff[entry] == NULL)) { + if (likely(!rx_q->rx_skbuff[entry])) { struct sk_buff *skb; skb = netdev_alloc_skb_ip_align(priv->dev, bfsize); if (unlikely(!skb)) { /* so for a while no zero-copy! */ - priv->rx_zeroc_thresh = STMMAC_RX_THRESH; + rx_q->rx_zeroc_thresh = STMMAC_RX_THRESH; if (unlikely(net_ratelimit())) dev_err(priv->device, "fail to alloc skb entry %d\n", @@ -2984,28 +3059,28 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv) break; } - priv->rx_skbuff[entry] = skb; - priv->rx_skbuff_dma[entry] = + rx_q->rx_skbuff[entry] = skb; + rx_q->rx_skbuff_dma[entry] = dma_map_single(priv->device, skb->data, bfsize, DMA_FROM_DEVICE); if (dma_mapping_error(priv->device, - priv->rx_skbuff_dma[entry])) { + rx_q->rx_skbuff_dma[entry])) { netdev_err(priv->dev, "Rx DMA map failed\n"); dev_kfree_skb(skb); break; } if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) { - p->des0 = cpu_to_le32(priv->rx_skbuff_dma[entry]); + p->des0 = cpu_to_le32(rx_q->rx_skbuff_dma[entry]); p->des1 = 0; } else { - p->des2 = cpu_to_le32(priv->rx_skbuff_dma[entry]); + p->des2 = cpu_to_le32(rx_q->rx_skbuff_dma[entry]); } if (priv->hw->mode->refill_desc3) - priv->hw->mode->refill_desc3(priv, p); + priv->hw->mode->refill_desc3(rx_q, p); - if (priv->rx_zeroc_thresh > 0) - priv->rx_zeroc_thresh--; + if (rx_q->rx_zeroc_thresh > 0) + rx_q->rx_zeroc_thresh--; netif_dbg(priv, rx_status, priv->dev, "refill entry #%d\n", entry); @@ -3021,31 +3096,33 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv) entry = STMMAC_GET_ENTRY(entry, DMA_RX_SIZE); } - priv->dirty_rx = entry; + rx_q->dirty_rx = entry; } /** * stmmac_rx - manage the receive process * @priv: driver private structure - * @limit: napi bugget. + * @limit: napi bugget + * @queue: RX queue index. * Description : this the function called by the napi poll method. * It gets all the frames inside the ring. */ -static int stmmac_rx(struct stmmac_priv *priv, int limit) +static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) { - unsigned int entry = priv->cur_rx; + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + unsigned int entry = rx_q->cur_rx; + int coe = priv->hw->rx_csum; unsigned int next_entry; unsigned int count = 0; - int coe = priv->hw->rx_csum; if (netif_msg_rx_status(priv)) { void *rx_head; netdev_dbg(priv->dev, "%s: descriptor ring:\n", __func__); if (priv->extend_desc) - rx_head = (void *)priv->dma_erx; + rx_head = (void *)rx_q->dma_erx; else - rx_head = (void *)priv->dma_rx; + rx_head = (void *)rx_q->dma_rx; priv->hw->desc->display_ring(rx_head, DMA_RX_SIZE, true); } @@ -3055,9 +3132,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) struct dma_desc *np; if (priv->extend_desc) - p = (struct dma_desc *)(priv->dma_erx + entry); + p = (struct dma_desc *)(rx_q->dma_erx + entry); else - p = priv->dma_rx + entry; + p = rx_q->dma_rx + entry; /* read the status of the incoming frame */ status = priv->hw->desc->rx_status(&priv->dev->stats, @@ -3068,20 +3145,20 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) count++; - priv->cur_rx = STMMAC_GET_ENTRY(priv->cur_rx, DMA_RX_SIZE); - next_entry = priv->cur_rx; + rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx, DMA_RX_SIZE); + next_entry = rx_q->cur_rx; if (priv->extend_desc) - np = (struct dma_desc *)(priv->dma_erx + next_entry); + np = (struct dma_desc *)(rx_q->dma_erx + next_entry); else - np = priv->dma_rx + next_entry; + np = rx_q->dma_rx + next_entry; prefetch(np); if ((priv->extend_desc) && (priv->hw->desc->rx_extended_status)) priv->hw->desc->rx_extended_status(&priv->dev->stats, &priv->xstats, - priv->dma_erx + + rx_q->dma_erx + entry); if (unlikely(status == discard_frame)) { priv->dev->stats.rx_errors++; @@ -3091,9 +3168,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) * them in stmmac_rx_refill() function so that * device can reuse it. */ - priv->rx_skbuff[entry] = NULL; + rx_q->rx_skbuff[entry] = NULL; dma_unmap_single(priv->device, - priv->rx_skbuff_dma[entry], + rx_q->rx_skbuff_dma[entry], priv->dma_buf_sz, DMA_FROM_DEVICE); } @@ -3141,7 +3218,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) */ if (unlikely(!priv->plat->has_gmac4 && ((frame_len < priv->rx_copybreak) || - stmmac_rx_threshold_count(priv)))) { + stmmac_rx_threshold_count(rx_q)))) { skb = netdev_alloc_skb_ip_align(priv->dev, frame_len); if (unlikely(!skb)) { @@ -3153,21 +3230,21 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) } dma_sync_single_for_cpu(priv->device, - priv->rx_skbuff_dma + rx_q->rx_skbuff_dma [entry], frame_len, DMA_FROM_DEVICE); skb_copy_to_linear_data(skb, - priv-> + rx_q-> rx_skbuff[entry]->data, frame_len); skb_put(skb, frame_len); dma_sync_single_for_device(priv->device, - priv->rx_skbuff_dma + rx_q->rx_skbuff_dma [entry], frame_len, DMA_FROM_DEVICE); } else { - skb = priv->rx_skbuff[entry]; + skb = rx_q->rx_skbuff[entry]; if (unlikely(!skb)) { netdev_err(priv->dev, "%s: Inconsistent Rx chain\n", @@ -3176,12 +3253,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) break; } prefetch(skb->data - NET_IP_ALIGN); - priv->rx_skbuff[entry] = NULL; - priv->rx_zeroc_thresh++; + rx_q->rx_skbuff[entry] = NULL; + rx_q->rx_zeroc_thresh++; skb_put(skb, frame_len); dma_unmap_single(priv->device, - priv->rx_skbuff_dma[entry], + rx_q->rx_skbuff_dma[entry], priv->dma_buf_sz, DMA_FROM_DEVICE); } @@ -3211,7 +3288,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) entry = next_entry; } - stmmac_rx_refill(priv); + stmmac_rx_refill(priv, queue); priv->xstats.rx_pkt_n += count; @@ -3229,13 +3306,14 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) static int stmmac_poll(struct napi_struct *napi, int budget) { struct stmmac_priv *priv = container_of(napi, struct stmmac_priv, napi); - int work_done = 0; u32 chan = STMMAC_CHAN0; + int work_done = 0; + u32 queue = chan; priv->xstats.napi_poll++; stmmac_tx_clean(priv); - work_done = stmmac_rx(priv, budget); + work_done = stmmac_rx(priv, budget, queue); if (work_done < budget) { napi_complete_done(napi, work_done); stmmac_enable_dma_irq(priv, chan); @@ -3396,6 +3474,9 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) if (priv->synopsys_id >= DWMAC_CORE_4_00) { for (queue = 0; queue < queues_count; queue++) { + struct stmmac_rx_queue *rx_q = + &priv->rx_queue[queue]; + status |= priv->hw->mac->host_mtl_irq_status(priv->hw, queue); @@ -3403,7 +3484,7 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) if (status & CORE_IRQ_MTL_RX_OVERFLOW && priv->hw->dma->set_rx_tail_ptr) priv->hw->dma->set_rx_tail_ptr(priv->ioaddr, - priv->rx_tail_addr, + rx_q->rx_tail_addr, queue); } } @@ -3503,15 +3584,29 @@ static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v) { struct net_device *dev = seq->private; struct stmmac_priv *priv = netdev_priv(dev); + u32 rx_count = priv->plat->rx_queues_to_use; + u32 queue; + + for (queue = 0; queue < rx_count; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + seq_printf(seq, "RX Queue %d:\n", queue); + + if (priv->extend_desc) { + seq_printf(seq, "Extended descriptor ring:\n"); + sysfs_display_ring((void *)rx_q->dma_erx, + DMA_RX_SIZE, 1, seq); + } else { + seq_printf(seq, "Descriptor ring:\n"); + sysfs_display_ring((void *)rx_q->dma_rx, + DMA_RX_SIZE, 0, seq); + } + } if (priv->extend_desc) { - seq_printf(seq, "Extended RX descriptor ring:\n"); - sysfs_display_ring((void *)priv->dma_erx, DMA_RX_SIZE, 1, seq); seq_printf(seq, "Extended TX descriptor ring:\n"); sysfs_display_ring((void *)priv->dma_etx, DMA_TX_SIZE, 1, seq); } else { - seq_printf(seq, "RX descriptor ring:\n"); - sysfs_display_ring((void *)priv->dma_rx, DMA_RX_SIZE, 0, seq); seq_printf(seq, "TX descriptor ring:\n"); sysfs_display_ring((void *)priv->dma_tx, DMA_TX_SIZE, 0, seq); } @@ -4025,6 +4120,26 @@ int stmmac_suspend(struct device *dev) } EXPORT_SYMBOL_GPL(stmmac_suspend); +/** + * stmmac_reset_queues_param - reset queue parameters + * @dev: device pointer + */ +static void stmmac_reset_queues_param(struct stmmac_priv *priv) +{ + u32 rx_cnt = priv->plat->rx_queues_to_use; + u32 queue; + + for (queue = 0; queue < rx_cnt; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + rx_q->cur_rx = 0; + rx_q->dirty_rx = 0; + } + + priv->dirty_tx = 0; + priv->cur_tx = 0; +} + /** * stmmac_resume - resume callback * @dev: device pointer @@ -4065,10 +4180,8 @@ int stmmac_resume(struct device *dev) spin_lock_irqsave(&priv->lock, flags); - priv->cur_rx = 0; - priv->dirty_rx = 0; - priv->dirty_tx = 0; - priv->cur_tx = 0; + stmmac_reset_queues_param(priv); + /* reset private mss value to force mss context settings at * next tso xmit (only used for gmac4). */ -- cgit From ce736788e8a92c12639311a9f01d55ccc997729c Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Thu, 6 Apr 2017 09:49:10 +0100 Subject: net: stmmac: adding multiple buffers for TX This patch adds the structure stmmac_tx_queue which contains tx queues specific data (previously in stmmac_priv). Signed-off-by: Joao Pinto Tested-by: Niklas Cassel Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/chain_mode.c | 38 +- drivers/net/ethernet/stmicro/stmmac/ring_mode.c | 46 +- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 26 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 519 +++++++++++++--------- 4 files changed, 374 insertions(+), 255 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c index 8db5a80aa8ec..37881f81319e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c @@ -26,12 +26,15 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) { - struct stmmac_priv *priv = (struct stmmac_priv *)p; - unsigned int entry = priv->cur_tx; - struct dma_desc *desc = priv->dma_tx + entry; + struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)p; unsigned int nopaged_len = skb_headlen(skb); + struct stmmac_priv *priv = tx_q->priv_data; + unsigned int entry = tx_q->cur_tx; unsigned int bmax, des2; unsigned int i = 1, len; + struct dma_desc *desc; + + desc = tx_q->dma_tx + entry; if (priv->plat->enh_desc) bmax = BUF_SIZE_8KiB; @@ -45,16 +48,16 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = des2; - priv->tx_skbuff_dma[entry].len = bmax; + tx_q->tx_skbuff_dma[entry].buf = des2; + tx_q->tx_skbuff_dma[entry].len = bmax; /* do not close the descriptor and do not set own bit */ priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE, 0, false); while (len != 0) { - priv->tx_skbuff[entry] = NULL; + tx_q->tx_skbuff[entry] = NULL; entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); - desc = priv->dma_tx + entry; + desc = tx_q->dma_tx + entry; if (len > bmax) { des2 = dma_map_single(priv->device, @@ -63,8 +66,8 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = des2; - priv->tx_skbuff_dma[entry].len = bmax; + tx_q->tx_skbuff_dma[entry].buf = des2; + tx_q->tx_skbuff_dma[entry].len = bmax; priv->hw->desc->prepare_tx_desc(desc, 0, bmax, csum, STMMAC_CHAIN_MODE, 1, false); @@ -77,8 +80,8 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = des2; - priv->tx_skbuff_dma[entry].len = len; + tx_q->tx_skbuff_dma[entry].buf = des2; + tx_q->tx_skbuff_dma[entry].len = len; /* last descriptor can be set now */ priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, STMMAC_CHAIN_MODE, 1, @@ -87,7 +90,7 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) } } - priv->cur_tx = entry; + tx_q->cur_tx = entry; return entry; } @@ -152,17 +155,18 @@ static void stmmac_refill_desc3(void *priv_ptr, struct dma_desc *p) static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p) { - struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; - unsigned int entry = priv->dirty_tx; + struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)priv_ptr; + struct stmmac_priv *priv = tx_q->priv_data; + unsigned int entry = tx_q->dirty_tx; - if (priv->tx_skbuff_dma[entry].last_segment && !priv->extend_desc && + if (tx_q->tx_skbuff_dma[entry].last_segment && !priv->extend_desc && priv->hwts_tx_en) /* NOTE: Device will overwrite des3 with timestamp value if * 1588-2002 time stamping is enabled, hence reinitialize it * to keep explicit chaining in the descriptor. */ - p->des3 = cpu_to_le32((unsigned int)((priv->dma_tx_phy + - ((priv->dirty_tx + 1) % DMA_TX_SIZE)) + p->des3 = cpu_to_le32((unsigned int)((tx_q->dma_tx_phy + + ((tx_q->dirty_tx + 1) % DMA_TX_SIZE)) * sizeof(struct dma_desc))); } diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c index 452f256ff03f..31213e64513d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c @@ -26,16 +26,17 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) { - struct stmmac_priv *priv = (struct stmmac_priv *)p; - unsigned int entry = priv->cur_tx; - struct dma_desc *desc; + struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)p; unsigned int nopaged_len = skb_headlen(skb); + struct stmmac_priv *priv = tx_q->priv_data; + unsigned int entry = tx_q->cur_tx; unsigned int bmax, len, des2; + struct dma_desc *desc; if (priv->extend_desc) - desc = (struct dma_desc *)(priv->dma_etx + entry); + desc = (struct dma_desc *)(tx_q->dma_etx + entry); else - desc = priv->dma_tx + entry; + desc = tx_q->dma_tx + entry; if (priv->plat->enh_desc) bmax = BUF_SIZE_8KiB; @@ -52,29 +53,29 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = des2; - priv->tx_skbuff_dma[entry].len = bmax; - priv->tx_skbuff_dma[entry].is_jumbo = true; + tx_q->tx_skbuff_dma[entry].buf = des2; + tx_q->tx_skbuff_dma[entry].len = bmax; + tx_q->tx_skbuff_dma[entry].is_jumbo = true; desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_RING_MODE, 0, false); - priv->tx_skbuff[entry] = NULL; + tx_q->tx_skbuff[entry] = NULL; entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); if (priv->extend_desc) - desc = (struct dma_desc *)(priv->dma_etx + entry); + desc = (struct dma_desc *)(tx_q->dma_etx + entry); else - desc = priv->dma_tx + entry; + desc = tx_q->dma_tx + entry; des2 = dma_map_single(priv->device, skb->data + bmax, len, DMA_TO_DEVICE); desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = des2; - priv->tx_skbuff_dma[entry].len = len; - priv->tx_skbuff_dma[entry].is_jumbo = true; + tx_q->tx_skbuff_dma[entry].buf = des2; + tx_q->tx_skbuff_dma[entry].len = len; + tx_q->tx_skbuff_dma[entry].is_jumbo = true; desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, @@ -85,15 +86,15 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des2 = cpu_to_le32(des2); if (dma_mapping_error(priv->device, des2)) return -1; - priv->tx_skbuff_dma[entry].buf = des2; - priv->tx_skbuff_dma[entry].len = nopaged_len; - priv->tx_skbuff_dma[entry].is_jumbo = true; + tx_q->tx_skbuff_dma[entry].buf = des2; + tx_q->tx_skbuff_dma[entry].len = nopaged_len; + tx_q->tx_skbuff_dma[entry].is_jumbo = true; desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum, STMMAC_RING_MODE, 0, true); } - priv->cur_tx = entry; + tx_q->cur_tx = entry; return entry; } @@ -125,12 +126,13 @@ static void stmmac_init_desc3(struct dma_desc *p) static void stmmac_clean_desc3(void *priv_ptr, struct dma_desc *p) { - struct stmmac_priv *priv = (struct stmmac_priv *)priv_ptr; - unsigned int entry = priv->dirty_tx; + struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)priv_ptr; + struct stmmac_priv *priv = tx_q->priv_data; + unsigned int entry = tx_q->dirty_tx; /* des3 is only used for jumbo frames tx or time stamping */ - if (unlikely(priv->tx_skbuff_dma[entry].is_jumbo || - (priv->tx_skbuff_dma[entry].last_segment && + if (unlikely(tx_q->tx_skbuff_dma[entry].is_jumbo || + (tx_q->tx_skbuff_dma[entry].last_segment && !priv->extend_desc && priv->hwts_tx_en))) p->des3 = 0; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index c7ad9e4f93d2..359f8fdde6aa 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -46,6 +46,20 @@ struct stmmac_tx_info { bool is_jumbo; }; +/* Frequently used values are kept adjacent for cache effect */ +struct stmmac_tx_queue { + u32 queue_index; + struct stmmac_priv *priv_data; + struct dma_extended_desc *dma_etx ____cacheline_aligned_in_smp; + struct dma_desc *dma_tx; + struct sk_buff **tx_skbuff; + struct stmmac_tx_info *tx_skbuff_dma; + unsigned int cur_tx; + unsigned int dirty_tx; + dma_addr_t dma_tx_phy; + u32 tx_tail_addr; +}; + struct stmmac_rx_queue { u32 queue_index; struct stmmac_priv *priv_data; @@ -62,16 +76,10 @@ struct stmmac_rx_queue { struct stmmac_priv { /* Frequently used values are kept adjacent for cache effect */ - struct dma_extended_desc *dma_etx ____cacheline_aligned_in_smp; - struct dma_desc *dma_tx; - struct sk_buff **tx_skbuff; - unsigned int cur_tx; - unsigned int dirty_tx; u32 tx_count_frames; u32 tx_coal_frames; u32 tx_coal_timer; - struct stmmac_tx_info *tx_skbuff_dma; - dma_addr_t dma_tx_phy; + int tx_coalesce; int hwts_tx_en; bool tx_path_in_lpi_mode; @@ -94,6 +102,9 @@ struct stmmac_priv { /* RX Queue */ struct stmmac_rx_queue rx_queue[MTL_MAX_RX_QUEUES]; + /* TX Queue */ + struct stmmac_tx_queue tx_queue[MTL_MAX_TX_QUEUES]; + int oldlink; int speed; int oldduplex; @@ -128,7 +139,6 @@ struct stmmac_priv { spinlock_t ptp_lock; void __iomem *mmcaddr; void __iomem *ptpaddr; - u32 tx_tail_addr; u32 mss; #ifdef CONFIG_DEBUG_FS diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 77caba4cab18..56a081f937d2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -185,14 +185,15 @@ static void print_pkt(unsigned char *buf, int len) print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); } -static inline u32 stmmac_tx_avail(struct stmmac_priv *priv) +static inline u32 stmmac_tx_avail(struct stmmac_priv *priv, u32 queue) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; u32 avail; - if (priv->dirty_tx > priv->cur_tx) - avail = priv->dirty_tx - priv->cur_tx - 1; + if (tx_q->dirty_tx > tx_q->cur_tx) + avail = tx_q->dirty_tx - tx_q->cur_tx - 1; else - avail = DMA_TX_SIZE - priv->cur_tx + priv->dirty_tx - 1; + avail = DMA_TX_SIZE - tx_q->cur_tx + tx_q->dirty_tx - 1; return avail; } @@ -238,9 +239,19 @@ static inline void stmmac_hw_fix_mac_speed(struct stmmac_priv *priv) */ static void stmmac_enable_eee_mode(struct stmmac_priv *priv) { + u32 tx_cnt = priv->plat->tx_queues_to_use; + u32 queue; + + /* check if all TX queues have the work finished */ + for (queue = 0; queue < tx_cnt; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + + if (tx_q->dirty_tx != tx_q->cur_tx) + return; /* still unfinished work */ + } + /* Check and enter in LPI mode */ - if ((priv->dirty_tx == priv->cur_tx) && - (priv->tx_path_in_lpi_mode == false)) + if (!priv->tx_path_in_lpi_mode) priv->hw->mac->set_eee_mode(priv->hw, priv->plat->en_tx_lpi_clockgating); } @@ -919,15 +930,23 @@ static void stmmac_display_rx_rings(struct stmmac_priv *priv) static void stmmac_display_tx_rings(struct stmmac_priv *priv) { + u32 tx_cnt = priv->plat->tx_queues_to_use; void *head_tx; + u32 queue; - if (priv->extend_desc) - head_tx = (void *)priv->dma_etx; - else - head_tx = (void *)priv->dma_tx; + /* Display TX rings */ + for (queue = 0; queue < tx_cnt; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; - /* Display TX ring */ - priv->hw->desc->display_ring(head_tx, DMA_TX_SIZE, false); + pr_info("\tTX Queue %d rings\n", queue); + + if (priv->extend_desc) + head_tx = (void *)tx_q->dma_etx; + else + head_tx = (void *)tx_q->dma_tx; + + priv->hw->desc->display_ring(head_tx, DMA_TX_SIZE, false); + } } static void stmmac_display_rings(struct stmmac_priv *priv) @@ -982,21 +1001,23 @@ static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue) /** * stmmac_clear_tx_descriptors - clear tx descriptors * @priv: driver private structure + * @queue: TX queue index. * Description: this function is called to clear the TX descriptors * in case of both basic and extended descriptors are used. */ -static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv) +static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv, u32 queue) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; int i; /* Clear the TX descriptors */ for (i = 0; i < DMA_TX_SIZE; i++) if (priv->extend_desc) - priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic, + priv->hw->desc->init_tx_desc(&tx_q->dma_etx[i].basic, priv->mode, (i == DMA_TX_SIZE - 1)); else - priv->hw->desc->init_tx_desc(&priv->dma_tx[i], + priv->hw->desc->init_tx_desc(&tx_q->dma_tx[i], priv->mode, (i == DMA_TX_SIZE - 1)); } @@ -1010,6 +1031,7 @@ static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv) static void stmmac_clear_descriptors(struct stmmac_priv *priv) { u32 rx_queue_cnt = priv->plat->rx_queues_to_use; + u32 tx_queue_cnt = priv->plat->tx_queues_to_use; u32 queue; /* Clear the RX descriptors */ @@ -1017,7 +1039,8 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv) stmmac_clear_rx_descriptors(priv, queue); /* Clear the TX descriptors */ - stmmac_clear_tx_descriptors(priv); + for (queue = 0; queue < tx_queue_cnt; queue++) + stmmac_clear_tx_descriptors(priv, queue); } /** @@ -1085,28 +1108,31 @@ static void stmmac_free_rx_buffer(struct stmmac_priv *priv, u32 queue, int i) /** * stmmac_free_tx_buffer - free RX dma buffers * @priv: private structure + * @queue: RX queue index * @i: buffer index. */ -static void stmmac_free_tx_buffer(struct stmmac_priv *priv, int i) +static void stmmac_free_tx_buffer(struct stmmac_priv *priv, u32 queue, int i) { - if (priv->tx_skbuff_dma[i].buf) { - if (priv->tx_skbuff_dma[i].map_as_page) + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + + if (tx_q->tx_skbuff_dma[i].buf) { + if (tx_q->tx_skbuff_dma[i].map_as_page) dma_unmap_page(priv->device, - priv->tx_skbuff_dma[i].buf, - priv->tx_skbuff_dma[i].len, + tx_q->tx_skbuff_dma[i].buf, + tx_q->tx_skbuff_dma[i].len, DMA_TO_DEVICE); else dma_unmap_single(priv->device, - priv->tx_skbuff_dma[i].buf, - priv->tx_skbuff_dma[i].len, + tx_q->tx_skbuff_dma[i].buf, + tx_q->tx_skbuff_dma[i].len, DMA_TO_DEVICE); } - if (priv->tx_skbuff[i]) { - dev_kfree_skb_any(priv->tx_skbuff[i]); - priv->tx_skbuff[i] = NULL; - priv->tx_skbuff_dma[i].buf = 0; - priv->tx_skbuff_dma[i].map_as_page = false; + if (tx_q->tx_skbuff[i]) { + dev_kfree_skb_any(tx_q->tx_skbuff[i]); + tx_q->tx_skbuff[i] = NULL; + tx_q->tx_skbuff_dma[i].buf = 0; + tx_q->tx_skbuff_dma[i].map_as_page = false; } } @@ -1211,46 +1237,57 @@ err_init_rx_buffers: static int init_dma_tx_desc_rings(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); + u32 tx_queue_cnt = priv->plat->tx_queues_to_use; + u32 queue; int i; - netif_dbg(priv, probe, priv->dev, - "(%s) dma_tx_phy=0x%08x\n", __func__, (u32)priv->dma_tx_phy); + for (queue = 0; queue < tx_queue_cnt; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; - /* Setup the chained descriptor addresses */ - if (priv->mode == STMMAC_CHAIN_MODE) { - if (priv->extend_desc) - priv->hw->mode->init(priv->dma_etx, priv->dma_tx_phy, - DMA_TX_SIZE, 1); - else - priv->hw->mode->init(priv->dma_tx, priv->dma_tx_phy, - DMA_TX_SIZE, 0); - } + netif_dbg(priv, probe, priv->dev, + "(%s) dma_tx_phy=0x%08x\n", __func__, + (u32)tx_q->dma_tx_phy); - for (i = 0; i < DMA_TX_SIZE; i++) { - struct dma_desc *p; - if (priv->extend_desc) - p = &((priv->dma_etx + i)->basic); - else - p = priv->dma_tx + i; + /* Setup the chained descriptor addresses */ + if (priv->mode == STMMAC_CHAIN_MODE) { + if (priv->extend_desc) + priv->hw->mode->init(tx_q->dma_etx, + tx_q->dma_tx_phy, + DMA_TX_SIZE, 1); + else + priv->hw->mode->init(tx_q->dma_tx, + tx_q->dma_tx_phy, + DMA_TX_SIZE, 0); + } - if (priv->synopsys_id >= DWMAC_CORE_4_00) { - p->des0 = 0; - p->des1 = 0; - p->des2 = 0; - p->des3 = 0; - } else { - p->des2 = 0; + for (i = 0; i < DMA_TX_SIZE; i++) { + struct dma_desc *p; + + if (priv->extend_desc) + p = &((tx_q->dma_etx + i)->basic); + else + p = tx_q->dma_tx + i; + + if (priv->synopsys_id >= DWMAC_CORE_4_00) { + p->des0 = 0; + p->des1 = 0; + p->des2 = 0; + p->des3 = 0; + } else { + p->des2 = 0; + } + + tx_q->tx_skbuff_dma[i].buf = 0; + tx_q->tx_skbuff_dma[i].map_as_page = false; + tx_q->tx_skbuff_dma[i].len = 0; + tx_q->tx_skbuff_dma[i].last_segment = false; + tx_q->tx_skbuff[i] = NULL; } - priv->tx_skbuff_dma[i].buf = 0; - priv->tx_skbuff_dma[i].map_as_page = false; - priv->tx_skbuff_dma[i].len = 0; - priv->tx_skbuff_dma[i].last_segment = false; - priv->tx_skbuff[i] = NULL; + tx_q->dirty_tx = 0; + tx_q->cur_tx = 0; } - priv->dirty_tx = 0; - priv->cur_tx = 0; netdev_reset_queue(priv->dev); return 0; @@ -1299,13 +1336,14 @@ static void dma_free_rx_skbufs(struct stmmac_priv *priv, u32 queue) /** * dma_free_tx_skbufs - free TX dma buffers * @priv: private structure + * @queue: TX queue index */ -static void dma_free_tx_skbufs(struct stmmac_priv *priv) +static void dma_free_tx_skbufs(struct stmmac_priv *priv, u32 queue) { int i; for (i = 0; i < DMA_TX_SIZE; i++) - stmmac_free_tx_buffer(priv, i); + stmmac_free_tx_buffer(priv, queue, i); } /** @@ -1339,6 +1377,37 @@ static void free_dma_rx_desc_resources(struct stmmac_priv *priv) } } +/** + * free_dma_tx_desc_resources - free TX dma desc resources + * @priv: private structure + */ +static void free_dma_tx_desc_resources(struct stmmac_priv *priv) +{ + u32 tx_count = priv->plat->tx_queues_to_use; + u32 queue = 0; + + /* Free TX queue resources */ + for (queue = 0; queue < tx_count; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + + /* Release the DMA TX socket buffers */ + dma_free_tx_skbufs(priv, queue); + + /* Free DMA regions of consistent memory previously allocated */ + if (!priv->extend_desc) + dma_free_coherent(priv->device, + DMA_TX_SIZE * sizeof(struct dma_desc), + tx_q->dma_tx, tx_q->dma_tx_phy); + else + dma_free_coherent(priv->device, DMA_TX_SIZE * + sizeof(struct dma_extended_desc), + tx_q->dma_etx, tx_q->dma_tx_phy); + + kfree(tx_q->tx_skbuff_dma); + kfree(tx_q->tx_skbuff); + } +} + /** * alloc_dma_rx_desc_resources - alloc RX resources. * @priv: private structure @@ -1412,42 +1481,55 @@ err_dma: */ static int alloc_dma_tx_desc_resources(struct stmmac_priv *priv) { + u32 tx_count = priv->plat->tx_queues_to_use; int ret = -ENOMEM; + u32 queue; - priv->tx_skbuff_dma = kmalloc_array(DMA_TX_SIZE, - sizeof(*priv->tx_skbuff_dma), - GFP_KERNEL); - if (!priv->tx_skbuff_dma) - return -ENOMEM; + /* TX queues buffers and DMA */ + for (queue = 0; queue < tx_count; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; - priv->tx_skbuff = kmalloc_array(DMA_TX_SIZE, sizeof(struct sk_buff *), - GFP_KERNEL); - if (!priv->tx_skbuff) - goto err_tx_skbuff; + tx_q->queue_index = queue; + tx_q->priv_data = priv; - if (priv->extend_desc) { - priv->dma_etx = dma_zalloc_coherent(priv->device, DMA_TX_SIZE * - sizeof(struct - dma_extended_desc), - &priv->dma_tx_phy, + tx_q->tx_skbuff_dma = kmalloc_array(DMA_TX_SIZE, + sizeof(*tx_q->tx_skbuff_dma), GFP_KERNEL); - if (!priv->dma_etx) - goto err_dma; - } else { - priv->dma_tx = dma_zalloc_coherent(priv->device, DMA_TX_SIZE * - sizeof(struct dma_desc), - &priv->dma_tx_phy, - GFP_KERNEL); - if (!priv->dma_tx) - goto err_dma; + if (!tx_q->tx_skbuff_dma) + return -ENOMEM; + + tx_q->tx_skbuff = kmalloc_array(DMA_TX_SIZE, + sizeof(struct sk_buff *), + GFP_KERNEL); + if (!tx_q->tx_skbuff) + goto err_dma_buffers; + + if (priv->extend_desc) { + tx_q->dma_etx = dma_zalloc_coherent(priv->device, + DMA_TX_SIZE * + sizeof(struct + dma_extended_desc), + &tx_q->dma_tx_phy, + GFP_KERNEL); + if (!tx_q->dma_etx) + goto err_dma_buffers; + } else { + tx_q->dma_tx = dma_zalloc_coherent(priv->device, + DMA_TX_SIZE * + sizeof(struct + dma_desc), + &tx_q->dma_tx_phy, + GFP_KERNEL); + if (!tx_q->dma_tx) + goto err_dma_buffers; + } } return 0; -err_dma: - kfree(priv->tx_skbuff); -err_tx_skbuff: - kfree(priv->tx_skbuff_dma); +err_dma_buffers: + free_dma_tx_desc_resources(priv); + return ret; } @@ -1472,29 +1554,6 @@ static int alloc_dma_desc_resources(struct stmmac_priv *priv) return ret; } -/** - * free_dma_tx_desc_resources - free TX dma desc resources - * @priv: private structure - */ -static void free_dma_tx_desc_resources(struct stmmac_priv *priv) -{ - /* Release the DMA TX socket buffers */ - dma_free_tx_skbufs(priv); - - /* Free DMA regions of consistent memory previously allocated */ - if (!priv->extend_desc) - dma_free_coherent(priv->device, - DMA_TX_SIZE * sizeof(struct dma_desc), - priv->dma_tx, priv->dma_tx_phy); - else - dma_free_coherent(priv->device, DMA_TX_SIZE * - sizeof(struct dma_extended_desc), - priv->dma_etx, priv->dma_tx_phy); - - kfree(priv->tx_skbuff_dma); - kfree(priv->tx_skbuff); -} - /** * free_dma_desc_resources - free dma desc resources * @priv: private structure @@ -1669,26 +1728,28 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv) /** * stmmac_tx_clean - to manage the transmission completion * @priv: driver private structure + * @queue: TX queue index * Description: it reclaims the transmit resources after transmission completes. */ -static void stmmac_tx_clean(struct stmmac_priv *priv) +static void stmmac_tx_clean(struct stmmac_priv *priv, u32 queue) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; unsigned int bytes_compl = 0, pkts_compl = 0; - unsigned int entry = priv->dirty_tx; + unsigned int entry = tx_q->dirty_tx; netif_tx_lock(priv->dev); priv->xstats.tx_clean++; - while (entry != priv->cur_tx) { - struct sk_buff *skb = priv->tx_skbuff[entry]; + while (entry != tx_q->cur_tx) { + struct sk_buff *skb = tx_q->tx_skbuff[entry]; struct dma_desc *p; int status; if (priv->extend_desc) - p = (struct dma_desc *)(priv->dma_etx + entry); + p = (struct dma_desc *)(tx_q->dma_etx + entry); else - p = priv->dma_tx + entry; + p = tx_q->dma_tx + entry; status = priv->hw->desc->tx_status(&priv->dev->stats, &priv->xstats, p, @@ -1709,45 +1770,45 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) stmmac_get_tx_hwtstamp(priv, p, skb); } - if (likely(priv->tx_skbuff_dma[entry].buf)) { - if (priv->tx_skbuff_dma[entry].map_as_page) + if (likely(tx_q->tx_skbuff_dma[entry].buf)) { + if (tx_q->tx_skbuff_dma[entry].map_as_page) dma_unmap_page(priv->device, - priv->tx_skbuff_dma[entry].buf, - priv->tx_skbuff_dma[entry].len, + tx_q->tx_skbuff_dma[entry].buf, + tx_q->tx_skbuff_dma[entry].len, DMA_TO_DEVICE); else dma_unmap_single(priv->device, - priv->tx_skbuff_dma[entry].buf, - priv->tx_skbuff_dma[entry].len, + tx_q->tx_skbuff_dma[entry].buf, + tx_q->tx_skbuff_dma[entry].len, DMA_TO_DEVICE); - priv->tx_skbuff_dma[entry].buf = 0; - priv->tx_skbuff_dma[entry].len = 0; - priv->tx_skbuff_dma[entry].map_as_page = false; + tx_q->tx_skbuff_dma[entry].buf = 0; + tx_q->tx_skbuff_dma[entry].len = 0; + tx_q->tx_skbuff_dma[entry].map_as_page = false; } if (priv->hw->mode->clean_desc3) - priv->hw->mode->clean_desc3(priv, p); + priv->hw->mode->clean_desc3(tx_q, p); - priv->tx_skbuff_dma[entry].last_segment = false; - priv->tx_skbuff_dma[entry].is_jumbo = false; + tx_q->tx_skbuff_dma[entry].last_segment = false; + tx_q->tx_skbuff_dma[entry].is_jumbo = false; if (likely(skb != NULL)) { pkts_compl++; bytes_compl += skb->len; dev_consume_skb_any(skb); - priv->tx_skbuff[entry] = NULL; + tx_q->tx_skbuff[entry] = NULL; } priv->hw->desc->release_tx_desc(p, priv->mode); entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); } - priv->dirty_tx = entry; + tx_q->dirty_tx = entry; netdev_completed_queue(priv->dev, pkts_compl, bytes_compl); if (unlikely(netif_queue_stopped(priv->dev) && - stmmac_tx_avail(priv) > STMMAC_TX_THRESH)) { + stmmac_tx_avail(priv, queue) > STMMAC_TX_THRESH)) { netif_dbg(priv, tx_done, priv->dev, "%s: restart transmit\n", __func__); netif_wake_queue(priv->dev); @@ -1779,22 +1840,24 @@ static inline void stmmac_disable_dma_irq(struct stmmac_priv *priv, u32 chan) */ static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; int i; + netif_stop_queue(priv->dev); stmmac_stop_tx_dma(priv, chan); - dma_free_tx_skbufs(priv); + dma_free_tx_skbufs(priv, chan); for (i = 0; i < DMA_TX_SIZE; i++) if (priv->extend_desc) - priv->hw->desc->init_tx_desc(&priv->dma_etx[i].basic, + priv->hw->desc->init_tx_desc(&tx_q->dma_etx[i].basic, priv->mode, (i == DMA_TX_SIZE - 1)); else - priv->hw->desc->init_tx_desc(&priv->dma_tx[i], + priv->hw->desc->init_tx_desc(&tx_q->dma_tx[i], priv->mode, (i == DMA_TX_SIZE - 1)); - priv->dirty_tx = 0; - priv->cur_tx = 0; + tx_q->dirty_tx = 0; + tx_q->cur_tx = 0; netdev_reset_queue(priv->dev); stmmac_start_tx_dma(priv, chan); @@ -1983,6 +2046,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) u32 rx_channels_count = priv->plat->rx_queues_to_use; u32 tx_channels_count = priv->plat->tx_queues_to_use; struct stmmac_rx_queue *rx_q; + struct stmmac_tx_queue *tx_q; u32 dummy_dma_rx_phy = 0; u32 dummy_dma_tx_phy = 0; u32 chan = 0; @@ -2025,24 +2089,27 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) /* DMA TX Channel Configuration */ for (chan = 0; chan < tx_channels_count; chan++) { + tx_q = &priv->tx_queue[chan]; + priv->hw->dma->init_chan(priv->ioaddr, - priv->plat->dma_cfg, - chan); + priv->plat->dma_cfg, + chan); priv->hw->dma->init_tx_chan(priv->ioaddr, priv->plat->dma_cfg, - priv->dma_tx_phy, chan); + tx_q->dma_tx_phy, chan); - priv->tx_tail_addr = priv->dma_tx_phy + + tx_q->tx_tail_addr = tx_q->dma_tx_phy + (DMA_TX_SIZE * sizeof(struct dma_desc)); priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, - priv->tx_tail_addr, + tx_q->tx_tail_addr, chan); } } else { rx_q = &priv->rx_queue[chan]; + tx_q = &priv->tx_queue[chan]; priv->hw->dma->init(priv->ioaddr, priv->plat->dma_cfg, - priv->dma_tx_phy, rx_q->dma_rx_phy, atds); + tx_q->dma_tx_phy, rx_q->dma_rx_phy, atds); } if (priv->plat->axi && priv->hw->dma->axi) @@ -2060,8 +2127,12 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) static void stmmac_tx_timer(unsigned long data) { struct stmmac_priv *priv = (struct stmmac_priv *)data; + u32 tx_queues_count = priv->plat->tx_queues_to_use; + u32 queue; - stmmac_tx_clean(priv); + /* let's scan all the tx queues */ + for (queue = 0; queue < tx_queues_count; queue++) + stmmac_tx_clean(priv, queue); } /** @@ -2566,22 +2637,24 @@ static int stmmac_release(struct net_device *dev) * @des: buffer start address * @total_len: total length to fill in descriptors * @last_segmant: condition for the last descriptor + * @queue: TX queue index * Description: * This function fills descriptor and request new descriptors according to * buffer length to fill */ static void stmmac_tso_allocator(struct stmmac_priv *priv, unsigned int des, - int total_len, bool last_segment) + int total_len, bool last_segment, u32 queue) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; struct dma_desc *desc; - int tmp_len; u32 buff_size; + int tmp_len; tmp_len = total_len; while (tmp_len > 0) { - priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE); - desc = priv->dma_tx + priv->cur_tx; + tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE); + desc = tx_q->dma_tx + tx_q->cur_tx; desc->des0 = cpu_to_le32(des + (total_len - tmp_len)); buff_size = tmp_len >= TSO_MAX_BUFF_SIZE ? @@ -2625,20 +2698,24 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, unsigned int des, */ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) { - u32 pay_len, mss; - int tmp_pay_len = 0; + struct dma_desc *desc, *first, *mss_desc = NULL; struct stmmac_priv *priv = netdev_priv(dev); int nfrags = skb_shinfo(skb)->nr_frags; + u32 queue = skb_get_queue_mapping(skb); unsigned int first_entry, des; - struct dma_desc *desc, *first, *mss_desc = NULL; + struct stmmac_tx_queue *tx_q; + int tmp_pay_len = 0; + u32 pay_len, mss; u8 proto_hdr_len; int i; + tx_q = &priv->tx_queue[queue]; + /* Compute header lengths */ proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); /* Desc availability based on threshold should be enough safe */ - if (unlikely(stmmac_tx_avail(priv) < + if (unlikely(stmmac_tx_avail(priv, queue) < (((skb->len - proto_hdr_len) / TSO_MAX_BUFF_SIZE + 1)))) { if (!netif_queue_stopped(dev)) { netif_stop_queue(dev); @@ -2656,10 +2733,10 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) /* set new MSS value if needed */ if (mss != priv->mss) { - mss_desc = priv->dma_tx + priv->cur_tx; + mss_desc = tx_q->dma_tx + tx_q->cur_tx; priv->hw->desc->set_mss(mss_desc, mss); priv->mss = mss; - priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE); + tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE); } if (netif_msg_tx_queued(priv)) { @@ -2669,9 +2746,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) skb->data_len); } - first_entry = priv->cur_tx; + first_entry = tx_q->cur_tx; - desc = priv->dma_tx + first_entry; + desc = tx_q->dma_tx + first_entry; first = desc; /* first descriptor: fill Headers on Buf1 */ @@ -2680,9 +2757,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) if (dma_mapping_error(priv->device, des)) goto dma_map_err; - priv->tx_skbuff_dma[first_entry].buf = des; - priv->tx_skbuff_dma[first_entry].len = skb_headlen(skb); - priv->tx_skbuff[first_entry] = skb; + tx_q->tx_skbuff_dma[first_entry].buf = des; + tx_q->tx_skbuff_dma[first_entry].len = skb_headlen(skb); + tx_q->tx_skbuff[first_entry] = skb; first->des0 = cpu_to_le32(des); @@ -2693,7 +2770,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) /* If needed take extra descriptors to fill the remaining payload */ tmp_pay_len = pay_len - TSO_MAX_BUFF_SIZE; - stmmac_tso_allocator(priv, des, tmp_pay_len, (nfrags == 0)); + stmmac_tso_allocator(priv, des, tmp_pay_len, (nfrags == 0), queue); /* Prepare fragments */ for (i = 0; i < nfrags; i++) { @@ -2706,19 +2783,19 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) goto dma_map_err; stmmac_tso_allocator(priv, des, skb_frag_size(frag), - (i == nfrags - 1)); + (i == nfrags - 1), queue); - priv->tx_skbuff_dma[priv->cur_tx].buf = des; - priv->tx_skbuff_dma[priv->cur_tx].len = skb_frag_size(frag); - priv->tx_skbuff[priv->cur_tx] = NULL; - priv->tx_skbuff_dma[priv->cur_tx].map_as_page = true; + tx_q->tx_skbuff_dma[tx_q->cur_tx].buf = des; + tx_q->tx_skbuff_dma[tx_q->cur_tx].len = skb_frag_size(frag); + tx_q->tx_skbuff[tx_q->cur_tx] = NULL; + tx_q->tx_skbuff_dma[tx_q->cur_tx].map_as_page = true; } - priv->tx_skbuff_dma[priv->cur_tx].last_segment = true; + tx_q->tx_skbuff_dma[tx_q->cur_tx].last_segment = true; - priv->cur_tx = STMMAC_GET_ENTRY(priv->cur_tx, DMA_TX_SIZE); + tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, DMA_TX_SIZE); - if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { + if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) { netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", __func__); netif_stop_queue(dev); @@ -2753,7 +2830,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) priv->hw->desc->prepare_tso_tx_desc(first, 1, proto_hdr_len, pay_len, - 1, priv->tx_skbuff_dma[first_entry].last_segment, + 1, tx_q->tx_skbuff_dma[first_entry].last_segment, tcp_hdrlen(skb) / 4, (skb->len - proto_hdr_len)); /* If context desc is used to change MSS */ @@ -2768,10 +2845,10 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) if (netif_msg_pktdata(priv)) { pr_info("%s: curr=%d dirty=%d f=%d, e=%d, f_p=%p, nfrags %d\n", - __func__, priv->cur_tx, priv->dirty_tx, first_entry, - priv->cur_tx, first, nfrags); + __func__, tx_q->cur_tx, tx_q->dirty_tx, first_entry, + tx_q->cur_tx, first, nfrags); - priv->hw->desc->display_ring((void *)priv->dma_tx, DMA_TX_SIZE, + priv->hw->desc->display_ring((void *)tx_q->dma_tx, DMA_TX_SIZE, 0); pr_info(">>> frame to be transmitted: "); @@ -2780,8 +2857,8 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) netdev_sent_queue(dev, skb->len); - priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr, - STMMAC_CHAN0); + priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, tx_q->tx_tail_addr, + queue); return NETDEV_TX_OK; @@ -2805,19 +2882,23 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) struct stmmac_priv *priv = netdev_priv(dev); unsigned int nopaged_len = skb_headlen(skb); int i, csum_insertion = 0, is_jumbo = 0; + u32 queue = skb_get_queue_mapping(skb); int nfrags = skb_shinfo(skb)->nr_frags; unsigned int entry, first_entry; struct dma_desc *desc, *first; + struct stmmac_tx_queue *tx_q; unsigned int enh_desc; unsigned int des; + tx_q = &priv->tx_queue[queue]; + /* Manage oversized TCP frames for GMAC4 device */ if (skb_is_gso(skb) && priv->tso) { if (ip_hdr(skb)->protocol == IPPROTO_TCP) return stmmac_tso_xmit(skb, dev); } - if (unlikely(stmmac_tx_avail(priv) < nfrags + 1)) { + if (unlikely(stmmac_tx_avail(priv, queue) < nfrags + 1)) { if (!netif_queue_stopped(dev)) { netif_stop_queue(dev); /* This is a hard error, log it. */ @@ -2831,19 +2912,19 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (priv->tx_path_in_lpi_mode) stmmac_disable_eee_mode(priv); - entry = priv->cur_tx; + entry = tx_q->cur_tx; first_entry = entry; csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL); if (likely(priv->extend_desc)) - desc = (struct dma_desc *)(priv->dma_etx + entry); + desc = (struct dma_desc *)(tx_q->dma_etx + entry); else - desc = priv->dma_tx + entry; + desc = tx_q->dma_tx + entry; first = desc; - priv->tx_skbuff[first_entry] = skb; + tx_q->tx_skbuff[first_entry] = skb; enh_desc = priv->plat->enh_desc; /* To program the descriptors according to the size of the frame */ @@ -2852,7 +2933,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(is_jumbo) && likely(priv->synopsys_id < DWMAC_CORE_4_00)) { - entry = priv->hw->mode->jumbo_frm(priv, skb, csum_insertion); + entry = priv->hw->mode->jumbo_frm(tx_q, skb, csum_insertion); if (unlikely(entry < 0)) goto dma_map_err; } @@ -2865,26 +2946,26 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); if (likely(priv->extend_desc)) - desc = (struct dma_desc *)(priv->dma_etx + entry); + desc = (struct dma_desc *)(tx_q->dma_etx + entry); else - desc = priv->dma_tx + entry; + desc = tx_q->dma_tx + entry; des = skb_frag_dma_map(priv->device, frag, 0, len, DMA_TO_DEVICE); if (dma_mapping_error(priv->device, des)) goto dma_map_err; /* should reuse desc w/o issues */ - priv->tx_skbuff[entry] = NULL; + tx_q->tx_skbuff[entry] = NULL; - priv->tx_skbuff_dma[entry].buf = des; + tx_q->tx_skbuff_dma[entry].buf = des; if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) desc->des0 = cpu_to_le32(des); else desc->des2 = cpu_to_le32(des); - priv->tx_skbuff_dma[entry].map_as_page = true; - priv->tx_skbuff_dma[entry].len = len; - priv->tx_skbuff_dma[entry].last_segment = last_segment; + tx_q->tx_skbuff_dma[entry].map_as_page = true; + tx_q->tx_skbuff_dma[entry].len = len; + tx_q->tx_skbuff_dma[entry].last_segment = last_segment; /* Prepare the descriptor and set the own bit too */ priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion, @@ -2893,20 +2974,20 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); - priv->cur_tx = entry; + tx_q->cur_tx = entry; if (netif_msg_pktdata(priv)) { void *tx_head; netdev_dbg(priv->dev, "%s: curr=%d dirty=%d f=%d, e=%d, first=%p, nfrags=%d", - __func__, priv->cur_tx, priv->dirty_tx, first_entry, + __func__, tx_q->cur_tx, tx_q->dirty_tx, first_entry, entry, first, nfrags); if (priv->extend_desc) - tx_head = (void *)priv->dma_etx; + tx_head = (void *)tx_q->dma_etx; else - tx_head = (void *)priv->dma_tx; + tx_head = (void *)tx_q->dma_tx; priv->hw->desc->display_ring(tx_head, DMA_TX_SIZE, false); @@ -2914,7 +2995,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) print_pkt(skb->data, skb->len); } - if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { + if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) { netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", __func__); netif_stop_queue(dev); @@ -2952,14 +3033,14 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (dma_mapping_error(priv->device, des)) goto dma_map_err; - priv->tx_skbuff_dma[first_entry].buf = des; + tx_q->tx_skbuff_dma[first_entry].buf = des; if (unlikely(priv->synopsys_id >= DWMAC_CORE_4_00)) first->des0 = cpu_to_le32(des); else first->des2 = cpu_to_le32(des); - priv->tx_skbuff_dma[first_entry].len = nopaged_len; - priv->tx_skbuff_dma[first_entry].last_segment = last_segment; + tx_q->tx_skbuff_dma[first_entry].len = nopaged_len; + tx_q->tx_skbuff_dma[first_entry].last_segment = last_segment; if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en)) { @@ -2985,8 +3066,8 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (priv->synopsys_id < DWMAC_CORE_4_00) priv->hw->dma->enable_dma_transmission(priv->ioaddr); else - priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, priv->tx_tail_addr, - STMMAC_CHAN0); + priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, tx_q->tx_tail_addr, + queue); return NETDEV_TX_OK; @@ -3306,12 +3387,18 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) static int stmmac_poll(struct napi_struct *napi, int budget) { struct stmmac_priv *priv = container_of(napi, struct stmmac_priv, napi); + u32 tx_count = priv->plat->tx_queues_to_use; u32 chan = STMMAC_CHAN0; int work_done = 0; u32 queue = chan; priv->xstats.napi_poll++; - stmmac_tx_clean(priv); + + /* check all the queues */ + for (queue = 0; queue < tx_count; queue++) + stmmac_tx_clean(priv, queue); + + queue = chan; work_done = stmmac_rx(priv, budget, queue); if (work_done < budget) { @@ -3332,10 +3419,12 @@ static int stmmac_poll(struct napi_struct *napi, int budget) static void stmmac_tx_timeout(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); - u32 chan = STMMAC_CHAN0; + u32 tx_count = priv->plat->tx_queues_to_use; + u32 chan; /* Clear Tx resources and restart transmitting again */ - stmmac_tx_err(priv, chan); + for (chan = 0; chan < tx_count; chan++) + stmmac_tx_err(priv, chan); } /** @@ -3585,6 +3674,7 @@ static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v) struct net_device *dev = seq->private; struct stmmac_priv *priv = netdev_priv(dev); u32 rx_count = priv->plat->rx_queues_to_use; + u32 tx_count = priv->plat->tx_queues_to_use; u32 queue; for (queue = 0; queue < rx_count; queue++) { @@ -3603,12 +3693,20 @@ static int stmmac_sysfs_ring_read(struct seq_file *seq, void *v) } } - if (priv->extend_desc) { - seq_printf(seq, "Extended TX descriptor ring:\n"); - sysfs_display_ring((void *)priv->dma_etx, DMA_TX_SIZE, 1, seq); - } else { - seq_printf(seq, "TX descriptor ring:\n"); - sysfs_display_ring((void *)priv->dma_tx, DMA_TX_SIZE, 0, seq); + for (queue = 0; queue < tx_count; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + + seq_printf(seq, "TX Queue %d:\n", queue); + + if (priv->extend_desc) { + seq_printf(seq, "Extended descriptor ring:\n"); + sysfs_display_ring((void *)tx_q->dma_etx, + DMA_TX_SIZE, 1, seq); + } else { + seq_printf(seq, "Descriptor ring:\n"); + sysfs_display_ring((void *)tx_q->dma_tx, + DMA_TX_SIZE, 0, seq); + } } return 0; @@ -4127,6 +4225,7 @@ EXPORT_SYMBOL_GPL(stmmac_suspend); static void stmmac_reset_queues_param(struct stmmac_priv *priv) { u32 rx_cnt = priv->plat->rx_queues_to_use; + u32 tx_cnt = priv->plat->tx_queues_to_use; u32 queue; for (queue = 0; queue < rx_cnt; queue++) { @@ -4136,8 +4235,12 @@ static void stmmac_reset_queues_param(struct stmmac_priv *priv) rx_q->dirty_rx = 0; } - priv->dirty_tx = 0; - priv->cur_tx = 0; + for (queue = 0; queue < tx_cnt; queue++) { + struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; + + tx_q->cur_tx = 0; + tx_q->dirty_tx = 0; + } } /** -- cgit From c22a3f48ef99ea4b02389f7f53080ed3ba220e77 Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Thu, 6 Apr 2017 09:49:11 +0100 Subject: net: stmmac: adding multiple napi mechanism This patch adds the napi variable to the stmmac_rx_queue structure and forces that operations like netif_queue_stopped, netif_wake_queue, netif_stop_queue, netdev_reset_queue and netdev_sent_queue be made by queue. Signed-off-by: Joao Pinto Tested-by: Niklas Cassel Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac.h | 3 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 158 ++++++++++++++++------ 2 files changed, 120 insertions(+), 41 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 359f8fdde6aa..33efe7038cab 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -72,6 +72,7 @@ struct stmmac_rx_queue { u32 rx_zeroc_thresh; dma_addr_t dma_rx_phy; u32 rx_tail_addr; + struct napi_struct napi ____cacheline_aligned_in_smp; }; struct stmmac_priv { @@ -91,8 +92,6 @@ struct stmmac_priv { u32 rx_riwt; int hwts_rx_en; - struct napi_struct napi ____cacheline_aligned_in_smp; - void __iomem *ioaddr; struct net_device *dev; struct device *device; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 56a081f937d2..a89f76b27ea2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -138,6 +138,64 @@ static void stmmac_verify_args(void) eee_timer = STMMAC_DEFAULT_LPI_TIMER; } +/** + * stmmac_disable_all_queues - Disable all queues + * @priv: driver private structure + */ +static void stmmac_disable_all_queues(struct stmmac_priv *priv) +{ + u32 rx_queues_cnt = priv->plat->rx_queues_to_use; + u32 queue; + + for (queue = 0; queue < rx_queues_cnt; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + napi_disable(&rx_q->napi); + } +} + +/** + * stmmac_enable_all_queues - Enable all queues + * @priv: driver private structure + */ +static void stmmac_enable_all_queues(struct stmmac_priv *priv) +{ + u32 rx_queues_cnt = priv->plat->rx_queues_to_use; + u32 queue; + + for (queue = 0; queue < rx_queues_cnt; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + napi_enable(&rx_q->napi); + } +} + +/** + * stmmac_stop_all_queues - Stop all queues + * @priv: driver private structure + */ +static void stmmac_stop_all_queues(struct stmmac_priv *priv) +{ + u32 tx_queues_cnt = priv->plat->tx_queues_to_use; + u32 queue; + + for (queue = 0; queue < tx_queues_cnt; queue++) + netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); +} + +/** + * stmmac_start_all_queues - Start all queues + * @priv: driver private structure + */ +static void stmmac_start_all_queues(struct stmmac_priv *priv) +{ + u32 tx_queues_cnt = priv->plat->tx_queues_to_use; + u32 queue; + + for (queue = 0; queue < tx_queues_cnt; queue++) + netif_tx_start_queue(netdev_get_tx_queue(priv->dev, queue)); +} + /** * stmmac_clk_csr_set - dynamically set the MDC clock * @priv: driver private structure @@ -1262,7 +1320,6 @@ static int init_dma_tx_desc_rings(struct net_device *dev) for (i = 0; i < DMA_TX_SIZE; i++) { struct dma_desc *p; - if (priv->extend_desc) p = &((tx_q->dma_etx + i)->basic); else @@ -1286,9 +1343,9 @@ static int init_dma_tx_desc_rings(struct net_device *dev) tx_q->dirty_tx = 0; tx_q->cur_tx = 0; - } - netdev_reset_queue(priv->dev); + netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, queue)); + } return 0; } @@ -1805,13 +1862,16 @@ static void stmmac_tx_clean(struct stmmac_priv *priv, u32 queue) } tx_q->dirty_tx = entry; - netdev_completed_queue(priv->dev, pkts_compl, bytes_compl); + netdev_tx_completed_queue(netdev_get_tx_queue(priv->dev, queue), + pkts_compl, bytes_compl); + + if (unlikely(netif_tx_queue_stopped(netdev_get_tx_queue(priv->dev, + queue))) && + stmmac_tx_avail(priv, queue) > STMMAC_TX_THRESH) { - if (unlikely(netif_queue_stopped(priv->dev) && - stmmac_tx_avail(priv, queue) > STMMAC_TX_THRESH)) { netif_dbg(priv, tx_done, priv->dev, "%s: restart transmit\n", __func__); - netif_wake_queue(priv->dev); + netif_tx_wake_queue(netdev_get_tx_queue(priv->dev, queue)); } if ((priv->eee_enabled) && (!priv->tx_path_in_lpi_mode)) { @@ -1843,7 +1903,7 @@ static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan) struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; int i; - netif_stop_queue(priv->dev); + netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan)); stmmac_stop_tx_dma(priv, chan); dma_free_tx_skbufs(priv, chan); @@ -1858,11 +1918,11 @@ static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan) (i == DMA_TX_SIZE - 1)); tx_q->dirty_tx = 0; tx_q->cur_tx = 0; - netdev_reset_queue(priv->dev); + netdev_tx_reset_queue(netdev_get_tx_queue(priv->dev, chan)); stmmac_start_tx_dma(priv, chan); priv->dev->stats.tx_errors++; - netif_wake_queue(priv->dev); + netif_tx_wake_queue(netdev_get_tx_queue(priv->dev, chan)); } /** @@ -1907,12 +1967,14 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv) u32 chan; for (chan = 0; chan < tx_channel_count; chan++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[chan]; + status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats, chan); if (likely((status & handle_rx)) || (status & handle_tx)) { - if (likely(napi_schedule_prep(&priv->napi))) { + if (likely(napi_schedule_prep(&rx_q->napi))) { stmmac_disable_dma_irq(priv, chan); - __napi_schedule(&priv->napi); + __napi_schedule(&rx_q->napi); } } @@ -2554,8 +2616,8 @@ static int stmmac_open(struct net_device *dev) } } - napi_enable(&priv->napi); - netif_start_queue(dev); + stmmac_enable_all_queues(priv); + stmmac_start_all_queues(priv); return 0; @@ -2598,9 +2660,9 @@ static int stmmac_release(struct net_device *dev) phy_disconnect(dev->phydev); } - netif_stop_queue(dev); + stmmac_stop_all_queues(priv); - napi_disable(&priv->napi); + stmmac_disable_all_queues(priv); del_timer_sync(&priv->txtimer); @@ -2717,8 +2779,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) /* Desc availability based on threshold should be enough safe */ if (unlikely(stmmac_tx_avail(priv, queue) < (((skb->len - proto_hdr_len) / TSO_MAX_BUFF_SIZE + 1)))) { - if (!netif_queue_stopped(dev)) { - netif_stop_queue(dev); + if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) { + netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, + queue)); /* This is a hard error, log it. */ netdev_err(priv->dev, "%s: Tx Ring full when queue awake\n", @@ -2798,7 +2861,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) { netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", __func__); - netif_stop_queue(dev); + netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); } dev->stats.tx_bytes += skb->len; @@ -2855,7 +2918,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) print_pkt(skb->data, skb_headlen(skb)); } - netdev_sent_queue(dev, skb->len); + netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len); priv->hw->dma->set_tx_tail_ptr(priv->ioaddr, tx_q->tx_tail_addr, queue); @@ -2899,8 +2962,9 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) } if (unlikely(stmmac_tx_avail(priv, queue) < nfrags + 1)) { - if (!netif_queue_stopped(dev)) { - netif_stop_queue(dev); + if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, queue))) { + netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, + queue)); /* This is a hard error, log it. */ netdev_err(priv->dev, "%s: Tx Ring full when queue awake\n", @@ -2998,7 +3062,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) { netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", __func__); - netif_stop_queue(dev); + netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); } dev->stats.tx_bytes += skb->len; @@ -3061,7 +3125,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) dma_wmb(); } - netdev_sent_queue(dev, skb->len); + netdev_tx_sent_queue(netdev_get_tx_queue(dev, queue), skb->len); if (priv->synopsys_id < DWMAC_CORE_4_00) priv->hw->dma->enable_dma_transmission(priv->ioaddr); @@ -3361,7 +3425,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) else skb->ip_summed = CHECKSUM_UNNECESSARY; - napi_gro_receive(&priv->napi, skb); + napi_gro_receive(&rx_q->napi, skb); priv->dev->stats.rx_packets++; priv->dev->stats.rx_bytes += frame_len; @@ -3386,11 +3450,13 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) */ static int stmmac_poll(struct napi_struct *napi, int budget) { - struct stmmac_priv *priv = container_of(napi, struct stmmac_priv, napi); + struct stmmac_rx_queue *rx_q = + container_of(napi, struct stmmac_rx_queue, napi); + struct stmmac_priv *priv = rx_q->priv_data; u32 tx_count = priv->plat->tx_queues_to_use; - u32 chan = STMMAC_CHAN0; + u32 chan = rx_q->queue_index; int work_done = 0; - u32 queue = chan; + u32 queue; priv->xstats.napi_poll++; @@ -3398,9 +3464,7 @@ static int stmmac_poll(struct napi_struct *napi, int budget) for (queue = 0; queue < tx_count; queue++) stmmac_tx_clean(priv, queue); - queue = chan; - - work_done = stmmac_rx(priv, budget, queue); + work_done = stmmac_rx(priv, budget, rx_q->queue_index); if (work_done < budget) { napi_complete_done(napi, work_done); stmmac_enable_dma_irq(priv, chan); @@ -3989,11 +4053,14 @@ int stmmac_dvr_probe(struct device *device, struct plat_stmmacenet_data *plat_dat, struct stmmac_resources *res) { - int ret = 0; struct net_device *ndev = NULL; struct stmmac_priv *priv; + int ret = 0; + u32 queue; - ndev = alloc_etherdev(sizeof(struct stmmac_priv)); + ndev = alloc_etherdev_mqs(sizeof(struct stmmac_priv), + MTL_MAX_TX_QUEUES, + MTL_MAX_RX_QUEUES); if (!ndev) return -ENOMEM; @@ -4035,6 +4102,10 @@ int stmmac_dvr_probe(struct device *device, if (ret) goto error_hw_init; + /* Configure real RX and TX queues */ + ndev->real_num_rx_queues = priv->plat->rx_queues_to_use; + ndev->real_num_tx_queues = priv->plat->tx_queues_to_use; + ndev->netdev_ops = &stmmac_netdev_ops; ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | @@ -4084,7 +4155,12 @@ int stmmac_dvr_probe(struct device *device, "Enable RX Mitigation via HW Watchdog Timer\n"); } - netif_napi_add(ndev, &priv->napi, stmmac_poll, 64); + for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + netif_napi_add(ndev, &rx_q->napi, stmmac_poll, + (8 * priv->plat->rx_queues_to_use)); + } spin_lock_init(&priv->lock); @@ -4129,7 +4205,11 @@ error_netdev_register: priv->hw->pcs != STMMAC_PCS_RTBI) stmmac_mdio_unregister(ndev); error_mdio_register: - netif_napi_del(&priv->napi); + for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) { + struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue]; + + netif_napi_del(&rx_q->napi); + } error_hw_init: free_netdev(ndev); @@ -4191,9 +4271,9 @@ int stmmac_suspend(struct device *dev) spin_lock_irqsave(&priv->lock, flags); netif_device_detach(ndev); - netif_stop_queue(ndev); + stmmac_stop_all_queues(priv); - napi_disable(&priv->napi); + stmmac_disable_all_queues(priv); /* Stop TX/RX DMA */ stmmac_stop_all_dma(priv); @@ -4296,9 +4376,9 @@ int stmmac_resume(struct device *dev) stmmac_init_tx_coalesce(priv); stmmac_set_rx_mode(ndev); - napi_enable(&priv->napi); + stmmac_enable_all_queues(priv); - netif_start_queue(ndev); + stmmac_start_all_queues(priv); spin_unlock_irqrestore(&priv->lock, flags); -- cgit From dedb67c4b4e5fa2e6e149a2ce93e7848aaa9d762 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 28 Mar 2017 22:27:32 +0530 Subject: netfilter: Add nfnl_msg_type() helper function Add and use nfnl_msg_type() function to replace opencoded nfnetlink message type. I suggested this change, Arushi Singhal made an initial patch to address this but was missing several spots. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nfnetlink.h | 5 +++++ net/netfilter/ipset/ip_set_core.c | 2 +- net/netfilter/nf_conntrack_netlink.c | 16 +++++++++------- net/netfilter/nf_tables_api.c | 20 +++++++++----------- net/netfilter/nf_tables_trace.c | 3 ++- net/netfilter/nfnetlink_acct.c | 2 +- net/netfilter/nfnetlink_cthelper.c | 2 +- net/netfilter/nfnetlink_cttimeout.c | 4 ++-- net/netfilter/nfnetlink_log.c | 2 +- net/netfilter/nfnetlink_queue.c | 2 +- net/netfilter/nft_compat.c | 2 +- 11 files changed, 33 insertions(+), 27 deletions(-) diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index 1b49209dd5c7..996711d8a7b4 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -41,6 +41,11 @@ int nfnetlink_set_err(struct net *net, u32 portid, u32 group, int error); int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid, int flags); +static inline u16 nfnl_msg_type(u8 subsys, u8 msg_type) +{ + return subsys << 8 | msg_type; +} + void nfnl_lock(__u8 subsys_id); void nfnl_unlock(__u8 subsys_id); #ifdef CONFIG_PROVE_LOCKING diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index c296f9b606d4..731ba9c0cf9b 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -769,7 +769,7 @@ start_msg(struct sk_buff *skb, u32 portid, u32 seq, unsigned int flags, struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; - nlh = nlmsg_put(skb, portid, seq, cmd | (NFNL_SUBSYS_IPSET << 8), + nlh = nlmsg_put(skb, portid, seq, nfnl_msg_type(NFNL_SUBSYS_IPSET, cmd), sizeof(*nfmsg), flags); if (!nlh) return NULL; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index cd0a6d270ebe..773d2187a5ea 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -467,7 +467,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, struct nlattr *nest_parms; unsigned int flags = portid ? NLM_F_MULTI : 0, event; - event = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_NEW); + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_NEW); nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -652,7 +652,7 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) if (skb == NULL) goto errout; - type |= NFNL_SUBSYS_CTNETLINK << 8; + type = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK, type); nlh = nlmsg_put(skb, item->portid, 0, type, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -1983,7 +1983,8 @@ ctnetlink_ct_stat_cpu_fill_info(struct sk_buff *skb, u32 portid, u32 seq, struct nfgenmsg *nfmsg; unsigned int flags = portid ? NLM_F_MULTI : 0, event; - event = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_GET_STATS_CPU); + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_CT_GET_STATS_CPU); nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -2066,7 +2067,7 @@ ctnetlink_stat_ct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, unsigned int flags = portid ? NLM_F_MULTI : 0, event; unsigned int nr_conntracks = atomic_read(&net->ct.count); - event = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_GET_STATS); + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET_STATS); nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -2576,7 +2577,7 @@ ctnetlink_exp_fill_info(struct sk_buff *skb, u32 portid, u32 seq, struct nfgenmsg *nfmsg; unsigned int flags = portid ? NLM_F_MULTI : 0; - event |= NFNL_SUBSYS_CTNETLINK_EXP << 8; + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK_EXP, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -2627,7 +2628,7 @@ ctnetlink_expect_event(unsigned int events, struct nf_exp_event *item) if (skb == NULL) goto errout; - type |= NFNL_SUBSYS_CTNETLINK_EXP << 8; + type = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK_EXP, type); nlh = nlmsg_put(skb, item->portid, 0, type, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -3212,7 +3213,8 @@ ctnetlink_exp_stat_fill_info(struct sk_buff *skb, u32 portid, u32 seq, int cpu, struct nfgenmsg *nfmsg; unsigned int flags = portid ? NLM_F_MULTI : 0, event; - event = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_EXP_GET_STATS_CPU); + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_EXP_GET_STATS_CPU); nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index bf52acfe4eff..7ba76da96cc2 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -438,7 +438,7 @@ static int nf_tables_fill_table_info(struct sk_buff *skb, struct net *net, struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; - event |= NFNL_SUBSYS_NFTABLES << 8; + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); if (nlh == NULL) goto nla_put_failure; @@ -989,7 +989,7 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, struct net *net, struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; - event |= NFNL_SUBSYS_NFTABLES << 8; + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); if (nlh == NULL) goto nla_put_failure; @@ -1885,10 +1885,9 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net, const struct nft_expr *expr, *next; struct nlattr *list; const struct nft_rule *prule; - int type = event | NFNL_SUBSYS_NFTABLES << 8; + u16 type = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); - nlh = nlmsg_put(skb, portid, seq, type, sizeof(struct nfgenmsg), - flags); + nlh = nlmsg_put(skb, portid, seq, type, sizeof(struct nfgenmsg), flags); if (nlh == NULL) goto nla_put_failure; @@ -2645,7 +2644,7 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, u32 portid = ctx->portid; u32 seq = ctx->seq; - event |= NFNL_SUBSYS_NFTABLES << 8; + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); if (nlh == NULL) @@ -3395,8 +3394,7 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) if (IS_ERR(set)) return PTR_ERR(set); - event = NFT_MSG_NEWSETELEM; - event |= NFNL_SUBSYS_NFTABLES << 8; + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWSETELEM); portid = NETLINK_CB(cb->skb).portid; seq = cb->nlh->nlmsg_seq; @@ -3481,7 +3479,7 @@ static int nf_tables_fill_setelem_info(struct sk_buff *skb, struct nlattr *nest; int err; - event |= NFNL_SUBSYS_NFTABLES << 8; + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); if (nlh == NULL) @@ -4253,7 +4251,7 @@ static int nf_tables_fill_obj_info(struct sk_buff *skb, struct net *net, struct nfgenmsg *nfmsg; struct nlmsghdr *nlh; - event |= NFNL_SUBSYS_NFTABLES << 8; + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); if (nlh == NULL) goto nla_put_failure; @@ -4526,7 +4524,7 @@ static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net, { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; - int event = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWGEN; + int event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWGEN); nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), 0); if (nlh == NULL) diff --git a/net/netfilter/nf_tables_trace.c b/net/netfilter/nf_tables_trace.c index 12eb9041dca2..e1b15e7a5793 100644 --- a/net/netfilter/nf_tables_trace.c +++ b/net/netfilter/nf_tables_trace.c @@ -169,7 +169,7 @@ void nft_trace_notify(struct nft_traceinfo *info) struct nlmsghdr *nlh; struct sk_buff *skb; unsigned int size; - int event = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_TRACE; + u16 event; if (!nfnetlink_has_listeners(nft_net(pkt), NFNLGRP_NFTRACE)) return; @@ -198,6 +198,7 @@ void nft_trace_notify(struct nft_traceinfo *info) if (!skb) return; + event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_TRACE); nlh = nlmsg_put(skb, 0, 0, event, sizeof(struct nfgenmsg), 0); if (!nlh) goto nla_put_failure; diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c index c86da174a5fc..1b9a5d6099dc 100644 --- a/net/netfilter/nfnetlink_acct.c +++ b/net/netfilter/nfnetlink_acct.c @@ -139,7 +139,7 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, u64 pkts, bytes; u32 old_flags; - event |= NFNL_SUBSYS_ACCT << 8; + event = nfnl_msg_type(NFNL_SUBSYS_ACCT, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index d5025cc25df3..9a50bf93dd16 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -507,7 +507,7 @@ nfnl_cthelper_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, unsigned int flags = portid ? NLM_F_MULTI : 0; int status; - event |= NFNL_SUBSYS_CTHELPER << 8; + event = nfnl_msg_type(NFNL_SUBSYS_CTHELPER, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index 57c2cdf7b691..0927a6ae6177 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -158,7 +158,7 @@ ctnl_timeout_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, unsigned int flags = portid ? NLM_F_MULTI : 0; struct nf_conntrack_l4proto *l4proto = timeout->l4proto; - event |= NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8; + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK_TIMEOUT, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; @@ -431,7 +431,7 @@ cttimeout_default_fill_info(struct net *net, struct sk_buff *skb, u32 portid, struct nfgenmsg *nfmsg; unsigned int flags = portid ? NLM_F_MULTI : 0; - event |= NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8; + event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK_TIMEOUT, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index ecd857b75ffe..e7648e90d162 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -411,7 +411,7 @@ __build_packet_message(struct nfnl_log_net *log, const unsigned char *hwhdrp; nlh = nlmsg_put(inst->skb, 0, 0, - NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET, + nfnl_msg_type(NFNL_SUBSYS_ULOG, NFULNL_MSG_PACKET), sizeof(struct nfgenmsg), 0); if (!nlh) return -1; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 933509ebf3d3..05e82004ab62 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -447,7 +447,7 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, } nlh = nlmsg_put(skb, 0, 0, - NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET, + nfnl_msg_type(NFNL_SUBSYS_QUEUE, NFQNL_MSG_PACKET), sizeof(struct nfgenmsg), 0); if (!nlh) { skb_tx_error(entskb); diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index f443f9d22faf..ed969caf01be 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -504,7 +504,7 @@ nfnl_compat_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, struct nfgenmsg *nfmsg; unsigned int flags = portid ? NLM_F_MULTI : 0; - event |= NFNL_SUBSYS_NFT_COMPAT << 8; + event = nfnl_msg_type(NFNL_SUBSYS_NFT_COMPAT, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); if (nlh == NULL) goto nlmsg_failure; -- cgit From bbadb9a222b4f0c3fec01483b57cbb403df0076a Mon Sep 17 00:00:00 2001 From: Florian Larysch Date: Fri, 7 Apr 2017 14:42:20 +0200 Subject: net: ipv4: fix multipath RTM_GETROUTE behavior when iif is given inet_rtm_getroute synthesizes a skeletal ICMP skb, which is passed to ip_route_input when iif is given. If a multipath route is present for the designated destination, fib_multipath_hash ends up being called with that skb. However, as that skb contains no information beyond the protocol type, the calculated hash does not match the one we would see for a real packet. There is currently no way to fix this for layer 4 hashing, as RTM_GETROUTE doesn't have the necessary information to create layer 4 headers. To fix this for layer 3 hashing, set appropriate saddr/daddrs in the skb and also change the protocol to UDP to avoid special treatment for ICMP. Signed-off-by: Florian Larysch Signed-off-by: David S. Miller --- net/ipv4/route.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 5dda1ef81c7e..5e1e60546fce 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2667,10 +2667,6 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) skb_reset_mac_header(skb); skb_reset_network_header(skb); - /* Bugfix: need to give ip_route_input enough of an IP header to not gag. */ - ip_hdr(skb)->protocol = IPPROTO_ICMP; - skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr)); - src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0; dst = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0; iif = tb[RTA_IIF] ? nla_get_u32(tb[RTA_IIF]) : 0; @@ -2680,6 +2676,15 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) else uid = (iif ? INVALID_UID : current_uid()); + /* Bugfix: need to give ip_route_input enough of an IP header to + * not gag. + */ + ip_hdr(skb)->protocol = IPPROTO_UDP; + ip_hdr(skb)->saddr = src; + ip_hdr(skb)->daddr = dst; + + skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr)); + memset(&fl4, 0, sizeof(fl4)); fl4.daddr = dst; fl4.saddr = src; -- cgit From 68ad546aefddb4dacdb78074df9dddb51424c427 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Wed, 29 Mar 2017 00:35:16 +0530 Subject: netfilter: Remove unnecessary cast on void pointer The following Coccinelle script was used to detect this: @r@ expression x; void* e; type T; identifier f; @@ ( *((T *)e) | ((T *)x)[...] | ((T*)x)->f | - (T*) e ) Unnecessary parantheses are also remove. Signed-off-by: simran singhal Reviewed-by: Stephen Hemminger Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/ebtables.c | 2 +- net/ipv4/netfilter/arp_tables.c | 21 ++++++++------------- net/ipv4/netfilter/ip_tables.c | 20 ++++++++------------ net/ipv6/netfilter/ip6_tables.c | 20 ++++++++------------ net/netfilter/ipset/ip_set_bitmap_gen.h | 5 ++--- net/netfilter/ipset/ip_set_core.c | 2 +- net/netfilter/nf_conntrack_proto.c | 2 +- net/netfilter/nft_set_hash.c | 2 +- net/netfilter/xt_hashlimit.c | 10 +++++----- 9 files changed, 35 insertions(+), 49 deletions(-) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 79b69917f521..bdc629eb0207 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1713,7 +1713,7 @@ static int compat_copy_entry_to_user(struct ebt_entry *e, void __user **dstptr, if (*size < sizeof(*ce)) return -EINVAL; - ce = (struct ebt_entry __user *)*dstptr; + ce = *dstptr; if (copy_to_user(ce, e, sizeof(*ce))) return -EFAULT; diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index f17dab1dee6e..0bc3c3d73e61 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -309,8 +309,7 @@ static int mark_source_chains(const struct xt_table_info *newinfo, */ for (hook = 0; hook < NF_ARP_NUMHOOKS; hook++) { unsigned int pos = newinfo->hook_entry[hook]; - struct arpt_entry *e - = (struct arpt_entry *)(entry0 + pos); + struct arpt_entry *e = entry0 + pos; if (!(valid_hooks & (1 << hook))) continue; @@ -354,14 +353,12 @@ static int mark_source_chains(const struct xt_table_info *newinfo, if (pos == oldpos) goto next; - e = (struct arpt_entry *) - (entry0 + pos); + e = entry0 + pos; } while (oldpos == pos + e->next_offset); /* Move along one */ size = e->next_offset; - e = (struct arpt_entry *) - (entry0 + pos + size); + e = entry0 + pos + size; if (pos + size >= newinfo->size) return 0; e->counters.pcnt = pos; @@ -376,16 +373,14 @@ static int mark_source_chains(const struct xt_table_info *newinfo, if (!xt_find_jump_offset(offsets, newpos, newinfo->number)) return 0; - e = (struct arpt_entry *) - (entry0 + newpos); + e = entry0 + newpos; } else { /* ... this is a fallthru */ newpos = pos + e->next_offset; if (newpos >= newinfo->size) return 0; } - e = (struct arpt_entry *) - (entry0 + newpos); + e = entry0 + newpos; e->counters.pcnt = pos; pos = newpos; } @@ -681,7 +676,7 @@ static int copy_entries_to_user(unsigned int total_size, for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){ const struct xt_entry_target *t; - e = (struct arpt_entry *)(loc_cpu_entry + off); + e = loc_cpu_entry + off; if (copy_to_user(userptr + off, e, sizeof(*e))) { ret = -EFAULT; goto free_counters; @@ -1128,7 +1123,7 @@ compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr, int h; origsize = *size; - de = (struct arpt_entry *)*dstptr; + de = *dstptr; memcpy(de, e, sizeof(struct arpt_entry)); memcpy(&de->counters, &e->counters, sizeof(e->counters)); @@ -1322,7 +1317,7 @@ static int compat_copy_entry_to_user(struct arpt_entry *e, void __user **dstptr, int ret; origsize = *size; - ce = (struct compat_arpt_entry __user *)*dstptr; + ce = *dstptr; if (copy_to_user(ce, e, sizeof(struct arpt_entry)) != 0 || copy_to_user(&ce->counters, &counters[i], sizeof(counters[i])) != 0) diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 384b85713e06..2a55a40211cb 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -382,7 +382,7 @@ mark_source_chains(const struct xt_table_info *newinfo, to 0 as we leave), and comefrom to save source hook bitmask */ for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) { unsigned int pos = newinfo->hook_entry[hook]; - struct ipt_entry *e = (struct ipt_entry *)(entry0 + pos); + struct ipt_entry *e = entry0 + pos; if (!(valid_hooks & (1 << hook))) continue; @@ -424,14 +424,12 @@ mark_source_chains(const struct xt_table_info *newinfo, if (pos == oldpos) goto next; - e = (struct ipt_entry *) - (entry0 + pos); + e = entry0 + pos; } while (oldpos == pos + e->next_offset); /* Move along one */ size = e->next_offset; - e = (struct ipt_entry *) - (entry0 + pos + size); + e = entry0 + pos + size; if (pos + size >= newinfo->size) return 0; e->counters.pcnt = pos; @@ -446,16 +444,14 @@ mark_source_chains(const struct xt_table_info *newinfo, if (!xt_find_jump_offset(offsets, newpos, newinfo->number)) return 0; - e = (struct ipt_entry *) - (entry0 + newpos); + e = entry0 + newpos; } else { /* ... this is a fallthru */ newpos = pos + e->next_offset; if (newpos >= newinfo->size) return 0; } - e = (struct ipt_entry *) - (entry0 + newpos); + e = entry0 + newpos; e->counters.pcnt = pos; pos = newpos; } @@ -834,7 +830,7 @@ copy_entries_to_user(unsigned int total_size, const struct xt_entry_match *m; const struct xt_entry_target *t; - e = (struct ipt_entry *)(loc_cpu_entry + off); + e = loc_cpu_entry + off; if (copy_to_user(userptr + off, e, sizeof(*e))) { ret = -EFAULT; goto free_counters; @@ -1229,7 +1225,7 @@ compat_copy_entry_to_user(struct ipt_entry *e, void __user **dstptr, int ret = 0; origsize = *size; - ce = (struct compat_ipt_entry __user *)*dstptr; + ce = *dstptr; if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 || copy_to_user(&ce->counters, &counters[i], sizeof(counters[i])) != 0) @@ -1366,7 +1362,7 @@ compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr, struct xt_entry_match *ematch; origsize = *size; - de = (struct ipt_entry *)*dstptr; + de = *dstptr; memcpy(de, e, sizeof(struct ipt_entry)); memcpy(&de->counters, &e->counters, sizeof(e->counters)); diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 1e15c54fd5e2..d862e3471935 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -411,7 +411,7 @@ mark_source_chains(const struct xt_table_info *newinfo, to 0 as we leave), and comefrom to save source hook bitmask */ for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) { unsigned int pos = newinfo->hook_entry[hook]; - struct ip6t_entry *e = (struct ip6t_entry *)(entry0 + pos); + struct ip6t_entry *e = entry0 + pos; if (!(valid_hooks & (1 << hook))) continue; @@ -453,14 +453,12 @@ mark_source_chains(const struct xt_table_info *newinfo, if (pos == oldpos) goto next; - e = (struct ip6t_entry *) - (entry0 + pos); + e = entry0 + pos; } while (oldpos == pos + e->next_offset); /* Move along one */ size = e->next_offset; - e = (struct ip6t_entry *) - (entry0 + pos + size); + e = entry0 + pos + size; if (pos + size >= newinfo->size) return 0; e->counters.pcnt = pos; @@ -475,16 +473,14 @@ mark_source_chains(const struct xt_table_info *newinfo, if (!xt_find_jump_offset(offsets, newpos, newinfo->number)) return 0; - e = (struct ip6t_entry *) - (entry0 + newpos); + e = entry0 + newpos; } else { /* ... this is a fallthru */ newpos = pos + e->next_offset; if (newpos >= newinfo->size) return 0; } - e = (struct ip6t_entry *) - (entry0 + newpos); + e = entry0 + newpos; e->counters.pcnt = pos; pos = newpos; } @@ -863,7 +859,7 @@ copy_entries_to_user(unsigned int total_size, const struct xt_entry_match *m; const struct xt_entry_target *t; - e = (struct ip6t_entry *)(loc_cpu_entry + off); + e = loc_cpu_entry + off; if (copy_to_user(userptr + off, e, sizeof(*e))) { ret = -EFAULT; goto free_counters; @@ -1258,7 +1254,7 @@ compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr, int ret = 0; origsize = *size; - ce = (struct compat_ip6t_entry __user *)*dstptr; + ce = *dstptr; if (copy_to_user(ce, e, sizeof(struct ip6t_entry)) != 0 || copy_to_user(&ce->counters, &counters[i], sizeof(counters[i])) != 0) @@ -1394,7 +1390,7 @@ compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr, struct xt_entry_match *ematch; origsize = *size; - de = (struct ip6t_entry *)*dstptr; + de = *dstptr; memcpy(de, e, sizeof(struct ip6t_entry)); memcpy(&de->counters, &e->counters, sizeof(e->counters)); diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index 6f09a99298cd..8ad2b52a0b32 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -232,7 +232,7 @@ mtype_list(const struct ip_set *set, if (!test_bit(id, map->members) || (SET_WITH_TIMEOUT(set) && #ifdef IP_SET_BITMAP_STORED_TIMEOUT - mtype_is_filled((const struct mtype_elem *)x) && + mtype_is_filled(x) && #endif ip_set_timeout_expired(ext_timeout(x, set)))) continue; @@ -248,8 +248,7 @@ mtype_list(const struct ip_set *set, } if (mtype_do_list(skb, map, id, set->dsize)) goto nla_put_failure; - if (ip_set_put_extensions(skb, set, x, - mtype_is_filled((const struct mtype_elem *)x))) + if (ip_set_put_extensions(skb, set, x, mtype_is_filled(x))) goto nla_put_failure; ipset_nest_end(skb, nested); } diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 731ba9c0cf9b..c637710d861c 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1915,7 +1915,7 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) ret = -EFAULT; goto done; } - op = (unsigned int *)data; + op = data; if (*op < IP_SET_OP_VERSION) { /* Check the version at the beginning of operations */ diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 2d6ee1803415..1329e090fd5e 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -202,7 +202,7 @@ static int kill_l3proto(struct nf_conn *i, void *data) static int kill_l4proto(struct nf_conn *i, void *data) { struct nf_conntrack_l4proto *l4proto; - l4proto = (struct nf_conntrack_l4proto *)data; + l4proto = data; return nf_ct_protonum(i) == l4proto->l4proto && nf_ct_l3num(i) == l4proto->l3proto; } diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c index 5f652720fc78..8ec086b6b56b 100644 --- a/net/netfilter/nft_set_hash.c +++ b/net/netfilter/nft_set_hash.c @@ -352,7 +352,7 @@ static int nft_hash_init(const struct nft_set *set, static void nft_hash_elem_destroy(void *ptr, void *arg) { - nft_set_elem_destroy((const struct nft_set *)arg, ptr, true); + nft_set_elem_destroy(arg, ptr, true); } static void nft_hash_destroy(const struct nft_set *set) diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 2a6dfe8b74d3..762e1874f28b 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -119,7 +119,7 @@ static int cfg_copy(struct hashlimit_cfg2 *to, void *from, int revision) { if (revision == 1) { - struct hashlimit_cfg1 *cfg = (struct hashlimit_cfg1 *)from; + struct hashlimit_cfg1 *cfg = from; to->mode = cfg->mode; to->avg = cfg->avg; @@ -895,7 +895,7 @@ static void *dl_seq_start(struct seq_file *s, loff_t *pos) static void *dl_seq_next(struct seq_file *s, void *v, loff_t *pos) { struct xt_hashlimit_htable *htable = s->private; - unsigned int *bucket = (unsigned int *)v; + unsigned int *bucket = v; *pos = ++(*bucket); if (*pos >= htable->cfg.size) { @@ -909,7 +909,7 @@ static void dl_seq_stop(struct seq_file *s, void *v) __releases(htable->lock) { struct xt_hashlimit_htable *htable = s->private; - unsigned int *bucket = (unsigned int *)v; + unsigned int *bucket = v; if (!IS_ERR(bucket)) kfree(bucket); @@ -980,7 +980,7 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family, static int dl_seq_show_v1(struct seq_file *s, void *v) { struct xt_hashlimit_htable *htable = s->private; - unsigned int *bucket = (unsigned int *)v; + unsigned int *bucket = v; struct dsthash_ent *ent; if (!hlist_empty(&htable->hash[*bucket])) { @@ -994,7 +994,7 @@ static int dl_seq_show_v1(struct seq_file *s, void *v) static int dl_seq_show(struct seq_file *s, void *v) { struct xt_hashlimit_htable *htable = s->private; - unsigned int *bucket = (unsigned int *)v; + unsigned int *bucket = v; struct dsthash_ent *ent; if (!hlist_empty(&htable->hash[*bucket])) { -- cgit From cdec26858e7bd9340ad4a9c6e73670aa47c2b6ec Mon Sep 17 00:00:00 2001 From: simran singhal Date: Wed, 29 Mar 2017 03:25:17 +0530 Subject: netfilter: Use seq_puts()/seq_putc() where possible For string without format specifiers, use seq_puts(). For seq_printf("\n"), use seq_putc('\n'). Signed-off-by: simran singhal Acked-by: Simon Horman Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_ctl.c | 8 ++++---- net/netfilter/nf_conntrack_expect.c | 4 ++-- net/netfilter/nf_conntrack_standalone.c | 6 +++--- net/netfilter/nf_log.c | 4 ++-- net/netfilter/nf_synproxy_core.c | 6 +++--- net/netfilter/xt_recent.c | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 541aa7694775..c578b6c0dc41 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -2130,8 +2130,8 @@ static int ip_vs_stats_show(struct seq_file *seq, void *v) /* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ seq_puts(seq, " Total Incoming Outgoing Incoming Outgoing\n"); - seq_printf(seq, - " Conns Packets Packets Bytes Bytes\n"); + seq_puts(seq, + " Conns Packets Packets Bytes Bytes\n"); ip_vs_copy_stats(&show, &net_ipvs(net)->tot_stats); seq_printf(seq, "%8LX %8LX %8LX %16LX %16LX\n\n", @@ -2178,8 +2178,8 @@ static int ip_vs_stats_percpu_show(struct seq_file *seq, void *v) /* 01234567 01234567 01234567 0123456701234567 0123456701234567 */ seq_puts(seq, " Total Incoming Outgoing Incoming Outgoing\n"); - seq_printf(seq, - "CPU Conns Packets Packets Bytes Bytes\n"); + seq_puts(seq, + "CPU Conns Packets Packets Bytes Bytes\n"); for_each_possible_cpu(i) { struct ip_vs_cpu_stats *u = per_cpu_ptr(cpustats, i); diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 71d136469be0..7f12c8a78112 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -549,7 +549,7 @@ static int exp_seq_show(struct seq_file *s, void *v) seq_printf(s, "%ld ", timer_pending(&expect->timeout) ? (long)(expect->timeout.expires - jiffies)/HZ : 0); else - seq_printf(s, "- "); + seq_puts(s, "- "); seq_printf(s, "l3proto = %u proto=%u ", expect->tuple.src.l3num, expect->tuple.dst.protonum); @@ -559,7 +559,7 @@ static int exp_seq_show(struct seq_file *s, void *v) expect->tuple.dst.protonum)); if (expect->flags & NF_CT_EXPECT_PERMANENT) { - seq_printf(s, "PERMANENT"); + seq_puts(s, "PERMANENT"); delim = ","; } if (expect->flags & NF_CT_EXPECT_INACTIVE) { diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 2256147dcaad..ccb5cb9043e0 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -250,7 +250,7 @@ static int ct_seq_show(struct seq_file *s, void *v) goto release; if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) - seq_printf(s, "[UNREPLIED] "); + seq_puts(s, "[UNREPLIED] "); print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple, l3proto, l4proto); @@ -261,7 +261,7 @@ static int ct_seq_show(struct seq_file *s, void *v) goto release; if (test_bit(IPS_ASSURED_BIT, &ct->status)) - seq_printf(s, "[ASSURED] "); + seq_puts(s, "[ASSURED] "); if (seq_has_overflowed(s)) goto release; @@ -350,7 +350,7 @@ static int ct_cpu_seq_show(struct seq_file *seq, void *v) const struct ip_conntrack_stat *st = v; if (v == SEQ_START_TOKEN) { - seq_printf(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete search_restart\n"); + seq_puts(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete search_restart\n"); return 0; } diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 8d85a0598b60..cc32727e3f32 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -376,13 +376,13 @@ static int seq_show(struct seq_file *s, void *v) logger = nft_log_dereference(loggers[*pos][i]); seq_printf(s, "%s", logger->name); if (i == 0 && loggers[*pos][i + 1] != NULL) - seq_printf(s, ","); + seq_puts(s, ","); if (seq_has_overflowed(s)) return -ENOSPC; } - seq_printf(s, ")\n"); + seq_puts(s, ")\n"); if (seq_has_overflowed(s)) return -ENOSPC; diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index 7c6d1fbe38b9..abe03e869f7b 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -287,9 +287,9 @@ static int synproxy_cpu_seq_show(struct seq_file *seq, void *v) struct synproxy_stats *stats = v; if (v == SEQ_START_TOKEN) { - seq_printf(seq, "entries\t\tsyn_received\t" - "cookie_invalid\tcookie_valid\t" - "cookie_retrans\tconn_reopened\n"); + seq_puts(seq, "entries\t\tsyn_received\t" + "cookie_invalid\tcookie_valid\t" + "cookie_retrans\tconn_reopened\n"); return 0; } diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index 1d89a4eaf841..37d581a31cff 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -532,7 +532,7 @@ static int recent_seq_show(struct seq_file *seq, void *v) &e->addr.in6, e->ttl, e->stamps[i], e->index); for (i = 0; i < e->nstamps; i++) seq_printf(seq, "%s %lu", i ? "," : "", e->stamps[i]); - seq_printf(seq, "\n"); + seq_putc(seq, '\n'); return 0; } -- cgit From cbbb40e2ec264bd63c410ff41cc8240c25419c49 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Wed, 29 Mar 2017 11:15:40 +0530 Subject: net: netfilter: Use list_{next/prev}_entry instead of list_entry This patch replace list_entry with list_prev_entry as it makes the code more clear to read. Signed-off-by: simran singhal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 7ba76da96cc2..22e191ad4468 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1905,7 +1905,7 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net, goto nla_put_failure; if ((event != NFT_MSG_DELRULE) && (rule->list.prev != &chain->rules)) { - prule = list_entry(rule->list.prev, struct nft_rule, list); + prule = list_prev_entry(rule, list); if (nla_put_be64(skb, NFTA_RULE_POSITION, cpu_to_be64(prule->handle), NFTA_RULE_PAD)) -- cgit From d4ef38354120d873f5db14ca6e13d051ef4ab068 Mon Sep 17 00:00:00 2001 From: Arushi Singhal Date: Sun, 2 Apr 2017 14:52:12 +0530 Subject: netfilter: Remove exceptional & on function name Remove & from function pointers to conform to the style found elsewhere in the file. Done using the following semantic patch // @r@ identifier f; @@ f(...) { ... } @@ identifier r.f; @@ - &f + f // Signed-off-by: Arushi Singhal Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/nft_meta_bridge.c | 2 +- net/ipv4/netfilter/nft_fib_ipv4.c | 2 +- net/ipv6/netfilter/nft_fib_ipv6.c | 2 +- net/netfilter/ipset/ip_set_core.c | 2 +- net/netfilter/ipvs/ip_vs_ctl.c | 4 ++-- net/netfilter/nfnetlink.c | 2 +- net/netfilter/nfnetlink_log.c | 2 +- net/netfilter/nfnetlink_queue.c | 4 ++-- net/netfilter/nft_ct.c | 2 +- net/netfilter/nft_exthdr.c | 2 +- net/netfilter/nft_hash.c | 2 +- net/netfilter/nft_meta.c | 2 +- net/netfilter/nft_numgen.c | 2 +- net/netfilter/nft_queue.c | 2 +- 14 files changed, 16 insertions(+), 16 deletions(-) diff --git a/net/bridge/netfilter/nft_meta_bridge.c b/net/bridge/netfilter/nft_meta_bridge.c index 5974dbc1ea24..bb63c9aed55d 100644 --- a/net/bridge/netfilter/nft_meta_bridge.c +++ b/net/bridge/netfilter/nft_meta_bridge.c @@ -111,7 +111,7 @@ nft_meta_bridge_select_ops(const struct nft_ctx *ctx, static struct nft_expr_type nft_meta_bridge_type __read_mostly = { .family = NFPROTO_BRIDGE, .name = "meta", - .select_ops = &nft_meta_bridge_select_ops, + .select_ops = nft_meta_bridge_select_ops, .policy = nft_meta_policy, .maxattr = NFTA_META_MAX, .owner = THIS_MODULE, diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c index f4e4462cb5bb..de3681df2ce7 100644 --- a/net/ipv4/netfilter/nft_fib_ipv4.c +++ b/net/ipv4/netfilter/nft_fib_ipv4.c @@ -212,7 +212,7 @@ nft_fib4_select_ops(const struct nft_ctx *ctx, static struct nft_expr_type nft_fib4_type __read_mostly = { .name = "fib", - .select_ops = &nft_fib4_select_ops, + .select_ops = nft_fib4_select_ops, .policy = nft_fib_policy, .maxattr = NFTA_FIB_MAX, .family = NFPROTO_IPV4, diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c index e8d88d82636b..43f91d9b086c 100644 --- a/net/ipv6/netfilter/nft_fib_ipv6.c +++ b/net/ipv6/netfilter/nft_fib_ipv6.c @@ -246,7 +246,7 @@ nft_fib6_select_ops(const struct nft_ctx *ctx, static struct nft_expr_type nft_fib6_type __read_mostly = { .name = "fib", - .select_ops = &nft_fib6_select_ops, + .select_ops = nft_fib6_select_ops, .policy = nft_fib_policy, .maxattr = NFTA_FIB_MAX, .family = NFPROTO_IPV6, diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index c637710d861c..cb120c3c040e 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -2013,7 +2013,7 @@ static struct nf_sockopt_ops so_set __read_mostly = { .pf = PF_INET, .get_optmin = SO_IP_SET, .get_optmax = SO_IP_SET + 1, - .get = &ip_set_sockfn_get, + .get = ip_set_sockfn_get, .owner = THIS_MODULE, }; diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index c578b6c0dc41..90ac6bc1bd28 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1774,13 +1774,13 @@ static struct ctl_table vs_vars[] = { .procname = "sync_version", .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_do_sync_mode, + .proc_handler = proc_do_sync_mode, }, { .procname = "sync_ports", .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_do_sync_ports, + .proc_handler = proc_do_sync_ports, }, { .procname = "sync_persist_mode", diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 68eda920160e..185f9786a5a4 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -500,7 +500,7 @@ static void nfnetlink_rcv(struct sk_buff *skb) if (nlh->nlmsg_type == NFNL_MSG_BATCH_BEGIN) nfnetlink_rcv_skb_batch(skb, nlh); else - netlink_rcv_skb(skb, &nfnetlink_rcv_msg); + netlink_rcv_skb(skb, nfnetlink_rcv_msg); } #ifdef CONFIG_MODULES diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index e7648e90d162..896741206a50 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -803,7 +803,7 @@ static int nfulnl_recv_unsupp(struct net *net, struct sock *ctnl, static struct nf_logger nfulnl_logger __read_mostly = { .name = "nfnetlink_log", .type = NF_LOG_TYPE_ULOG, - .logfn = &nfulnl_log_packet, + .logfn = nfulnl_log_packet, .me = THIS_MODULE, }; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 05e82004ab62..d09ab49e102a 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1213,8 +1213,8 @@ static const struct nla_policy nfqa_cfg_policy[NFQA_CFG_MAX+1] = { }; static const struct nf_queue_handler nfqh = { - .outfn = &nfqnl_enqueue_packet, - .nf_hook_drop = &nfqnl_nf_hook_drop, + .outfn = nfqnl_enqueue_packet, + .nf_hook_drop = nfqnl_nf_hook_drop, }; static int nfqnl_recv_config(struct net *net, struct sock *ctnl, diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 640fe5a5865e..6e23dbbedd7f 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -702,7 +702,7 @@ nft_ct_select_ops(const struct nft_ctx *ctx, static struct nft_expr_type nft_ct_type __read_mostly = { .name = "ct", - .select_ops = &nft_ct_select_ops, + .select_ops = nft_ct_select_ops, .policy = nft_ct_policy, .maxattr = NFTA_CT_MAX, .owner = THIS_MODULE, diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index d212a85d2f33..1ec49fe5845f 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -232,7 +232,7 @@ nft_exthdr_select_ops(const struct nft_ctx *ctx, static struct nft_expr_type nft_exthdr_type __read_mostly = { .name = "exthdr", - .select_ops = &nft_exthdr_select_ops, + .select_ops = nft_exthdr_select_ops, .policy = nft_exthdr_policy, .maxattr = NFTA_EXTHDR_MAX, .owner = THIS_MODULE, diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index a6a4633725bb..4cfe524d8729 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -224,7 +224,7 @@ nft_hash_select_ops(const struct nft_ctx *ctx, static struct nft_expr_type nft_hash_type __read_mostly = { .name = "hash", - .select_ops = &nft_hash_select_ops, + .select_ops = nft_hash_select_ops, .policy = nft_hash_policy, .maxattr = NFTA_HASH_MAX, .owner = THIS_MODULE, diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 9563ce3c23aa..5a60eb23a7ed 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -467,7 +467,7 @@ nft_meta_select_ops(const struct nft_ctx *ctx, static struct nft_expr_type nft_meta_type __read_mostly = { .name = "meta", - .select_ops = &nft_meta_select_ops, + .select_ops = nft_meta_select_ops, .policy = nft_meta_policy, .maxattr = NFTA_META_MAX, .owner = THIS_MODULE, diff --git a/net/netfilter/nft_numgen.c b/net/netfilter/nft_numgen.c index a66b36097b8f..5a3a52c71545 100644 --- a/net/netfilter/nft_numgen.c +++ b/net/netfilter/nft_numgen.c @@ -188,7 +188,7 @@ nft_ng_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) static struct nft_expr_type nft_ng_type __read_mostly = { .name = "numgen", - .select_ops = &nft_ng_select_ops, + .select_ops = nft_ng_select_ops, .policy = nft_ng_policy, .maxattr = NFTA_NG_MAX, .owner = THIS_MODULE, diff --git a/net/netfilter/nft_queue.c b/net/netfilter/nft_queue.c index dbb6aaff67ec..98613658d4ac 100644 --- a/net/netfilter/nft_queue.c +++ b/net/netfilter/nft_queue.c @@ -197,7 +197,7 @@ nft_queue_select_ops(const struct nft_ctx *ctx, static struct nft_expr_type nft_queue_type __read_mostly = { .name = "queue", - .select_ops = &nft_queue_select_ops, + .select_ops = nft_queue_select_ops, .policy = nft_queue_policy, .maxattr = NFTA_QUEUE_MAX, .owner = THIS_MODULE, -- cgit From c898693a5a79c518255be91049be1e54dfab9f1f Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 7 Apr 2017 16:45:05 +0800 Subject: dt-bindings: net: dsa: add Mediatek MT7530 binding Add device-tree binding for Mediatek MT7530 switch. Cc: devicetree@vger.kernel.org Signed-off-by: Sean Wang Acked-by: Rob Herring Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- .../devicetree/bindings/net/dsa/mt7530.txt | 92 ++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/dsa/mt7530.txt diff --git a/Documentation/devicetree/bindings/net/dsa/mt7530.txt b/Documentation/devicetree/bindings/net/dsa/mt7530.txt new file mode 100644 index 000000000000..a9bc27b93ee3 --- /dev/null +++ b/Documentation/devicetree/bindings/net/dsa/mt7530.txt @@ -0,0 +1,92 @@ +Mediatek MT7530 Ethernet switch +================================ + +Required properties: + +- compatible: Must be compatible = "mediatek,mt7530"; +- #address-cells: Must be 1. +- #size-cells: Must be 0. +- mediatek,mcm: Boolean; if defined, indicates that either MT7530 is the part + on multi-chip module belong to MT7623A has or the remotely standalone + chip as the function MT7623N reference board provided for. +- core-supply: Phandle to the regulator node necessary for the core power. +- io-supply: Phandle to the regulator node necessary for the I/O power. + See Documentation/devicetree/bindings/regulator/mt6323-regulator.txt + for details for the regulator setup on these boards. + +If the property mediatek,mcm isn't defined, following property is required + +- reset-gpios: Should be a gpio specifier for a reset line. + +Else, following properties are required + +- resets : Phandle pointing to the system reset controller with + line index for the ethsys. +- reset-names : Should be set to "mcm". + +Required properties for the child nodes within ports container: + +- reg: Port address described must be 6 for CPU port and from 0 to 5 for + user ports. +- phy-mode: String, must be either "trgmii" or "rgmii" for port labeled + "cpu". + +See Documentation/devicetree/bindings/dsa/dsa.txt for a list of additional +required, optional properties and how the integrated switch subnodes must +be specified. + +Example: + + &mdio0 { + switch@0 { + compatible = "mediatek,mt7530"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + core-supply = <&mt6323_vpa_reg>; + io-supply = <&mt6323_vemc3v3_reg>; + reset-gpios = <&pio 33 0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + port@0 { + reg = <0>; + label = "lan0"; + }; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@4 { + reg = <4>; + label = "wan"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "trgmii"; + fixed-link { + speed = <1000>; + full-duplex; + }; + }; + }; + }; + }; -- cgit From 5cd8985a19090f2b0ce8700ae3ec19e06a4fc5e9 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 7 Apr 2017 16:45:06 +0800 Subject: net-next: dsa: add Mediatek tag RX/TX handler Add the support for the 4-bytes tag for DSA port distinguishing inserted allowing receiving and transmitting the packet via the particular port. The tag is being added after the source MAC address in the ethernet header. Signed-off-by: Sean Wang Signed-off-by: Landen Chao Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 1 + net/dsa/Kconfig | 2 + net/dsa/Makefile | 1 + net/dsa/dsa.c | 3 ++ net/dsa/dsa_priv.h | 3 ++ net/dsa/tag_mtk.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 128 insertions(+) create mode 100644 net/dsa/tag_mtk.c diff --git a/include/net/dsa.h b/include/net/dsa.h index ffe56cc338fe..7ba9b1fb565c 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -32,6 +32,7 @@ enum dsa_tag_protocol { DSA_TAG_PROTO_EDSA, DSA_TAG_PROTO_BRCM, DSA_TAG_PROTO_QCA, + DSA_TAG_PROTO_MTK, DSA_TAG_LAST, /* MUST BE LAST */ }; diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index da4d64f432db..aa21f49f1215 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -31,4 +31,6 @@ config NET_DSA_TAG_TRAILER config NET_DSA_TAG_QCA bool +config NET_DSA_TAG_MTK + bool endif diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 31d343796251..9b1d478f3713 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -8,3 +8,4 @@ dsa_core-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o dsa_core-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o dsa_core-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o +dsa_core-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 95d1a756202c..6cad15da5892 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -53,6 +53,9 @@ const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = { #endif #ifdef CONFIG_NET_DSA_TAG_QCA [DSA_TAG_PROTO_QCA] = &qca_netdev_ops, +#endif +#ifdef CONFIG_NET_DSA_TAG_MTK + [DSA_TAG_PROTO_MTK] = &mtk_netdev_ops, #endif [DSA_TAG_PROTO_NONE] = &none_ops, }; diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 0706a511244e..2a3139921811 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -85,4 +85,7 @@ extern const struct dsa_device_ops brcm_netdev_ops; /* tag_qca.c */ extern const struct dsa_device_ops qca_netdev_ops; +/* tag_mtk.c */ +extern const struct dsa_device_ops mtk_netdev_ops; + #endif diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c new file mode 100644 index 000000000000..44ae6353a521 --- /dev/null +++ b/net/dsa/tag_mtk.c @@ -0,0 +1,118 @@ +/* + * Mediatek DSA Tag support + * Copyright (C) 2017 Landen Chao + * Sean Wang + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "dsa_priv.h" + +#define MTK_HDR_LEN 4 +#define MTK_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0) +#define MTK_HDR_XMIT_DP_BIT_MASK GENMASK(5, 0) + +static struct sk_buff *mtk_tag_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + u8 *mtk_tag; + + if (skb_cow_head(skb, MTK_HDR_LEN) < 0) + goto out_free; + + skb_push(skb, MTK_HDR_LEN); + + memmove(skb->data, skb->data + MTK_HDR_LEN, 2 * ETH_ALEN); + + /* Build the tag after the MAC Source Address */ + mtk_tag = skb->data + 2 * ETH_ALEN; + mtk_tag[0] = 0; + mtk_tag[1] = (1 << p->dp->index) & MTK_HDR_XMIT_DP_BIT_MASK; + mtk_tag[2] = 0; + mtk_tag[3] = 0; + + return skb; + +out_free: + kfree_skb(skb); + return NULL; +} + +static int mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct dsa_switch_tree *dst = dev->dsa_ptr; + struct dsa_switch *ds; + int port; + __be16 *phdr, hdr; + + if (unlikely(!dst)) + goto out_drop; + + skb = skb_unshare(skb, GFP_ATOMIC); + if (!skb) + goto out; + + if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN))) + goto out_drop; + + /* The MTK header is added by the switch between src addr + * and ethertype at this point, skb->data points to 2 bytes + * after src addr so header should be 2 bytes right before. + */ + phdr = (__be16 *)(skb->data - 2); + hdr = ntohs(*phdr); + + /* Remove MTK tag and recalculate checksum. */ + skb_pull_rcsum(skb, MTK_HDR_LEN); + + memmove(skb->data - ETH_HLEN, + skb->data - ETH_HLEN - MTK_HDR_LEN, + 2 * ETH_ALEN); + + /* This protocol doesn't support cascading multiple + * switches so it's safe to assume the switch is first + * in the tree. + */ + ds = dst->ds[0]; + if (!ds) + goto out_drop; + + /* Get source port information */ + port = (hdr & MTK_HDR_RECV_SOURCE_PORT_MASK); + if (!ds->ports[port].netdev) + goto out_drop; + + /* Update skb & forward the frame accordingly */ + skb_push(skb, ETH_HLEN); + + skb->pkt_type = PACKET_HOST; + skb->dev = ds->ports[port].netdev; + skb->protocol = eth_type_trans(skb, skb->dev); + + skb->dev->stats.rx_packets++; + skb->dev->stats.rx_bytes += skb->len; + + netif_receive_skb(skb); + + return 0; + +out_drop: + kfree_skb(skb); +out: + return 0; +} + +const struct dsa_device_ops mtk_netdev_ops = { + .xmit = mtk_tag_xmit, + .rcv = mtk_tag_rcv, +}; -- cgit From 87e3df4961f45888c48a0eecfbb532626c148d1e Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 7 Apr 2017 16:45:07 +0800 Subject: net-next: ethernet: mediatek: add CDM able to recognize the tag for DSA The patch adds the setup for allowing CDM can recognize these packets with carrying port-distinguishing tag. Otherwise, these tagging packets will be handled incorrectly by CDM. The setup is also working out for general untag packets as well. Signed-off-by: Sean Wang Signed-off-by: Landen Chao Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 6 ++++++ drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index bf6317eca2f6..f60e545e02f1 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -1846,6 +1846,12 @@ static int mtk_hw_init(struct mtk_eth *eth) /* GE2, Force 1000M/FD, FC ON */ mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(1)); + /* Indicates CDM to parse the MTK special tag from CPU + * which also is working out for untag packets. + */ + val = mtk_r32(eth, MTK_CDMQ_IG_CTRL); + mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL); + /* Enable RX VLan Offloading */ mtk_w32(eth, 1, MTK_CDMP_EG_CTRL); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 99b1c8e9f16f..996024d02668 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -70,6 +70,10 @@ /* Frame Engine Interrupt Grouping Register */ #define MTK_FE_INT_GRP 0x20 +/* CDMP Ingress Control Register */ +#define MTK_CDMQ_IG_CTRL 0x1400 +#define MTK_CDMQ_STAG_EN BIT(0) + /* CDMP Exgress Control Register */ #define MTK_CDMP_EG_CTRL 0x404 -- cgit From 3174b3b58cba69e5a5b806206cd37f7a8e05153e Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 7 Apr 2017 16:45:08 +0800 Subject: net-next: ethernet: mediatek: add device_node of GMAC pointing into the netdev instance the patch adds the setup of the corresponding device node of GMAC into the netdev instance which could allow other modules such as DSA to find the instance through the node in dt-bindings using of_find_net_device_by_node() call. Signed-off-by: Sean Wang Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index f60e545e02f1..d81d3b6dfd87 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -2322,6 +2322,8 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) eth->netdev[id]->ethtool_ops = &mtk_ethtool_ops; eth->netdev[id]->irq = eth->irq[0]; + eth->netdev[id]->dev.of_node = np; + return 0; free_netdev: -- cgit From b8f126a8d54318b82703783e76d28fd7c1ae6bed Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 7 Apr 2017 16:45:09 +0800 Subject: net-next: dsa: add dsa support for Mediatek MT7530 switch MT7530 is a 7-ports Gigabit Ethernet Switch that could be found on Mediatek router platforms such as MT7623A or MT7623N platform which includes 7-port Gigabit Ethernet MAC and 5-port Gigabit Ethernet PHY. Among these ports, The port from 0 to 4 are the user ports connecting with the remote devices while the port 5 and 6 are the CPU ports connecting into Mediatek Ethernet GMAC. For port 6, it can communicate with the CPU via Mediatek Ethernet GMAC through either the TRGMII or RGMII which could be controlled by phy-mode in the dt-bindings to specify which mode is preferred to use. And for port 5, only RGMII can be specified. However, currently, only port 6 is being supported in this DSA driver. The driver is made with the reference to qca8k and other existing DSA driver. The most of the essential callbacks of the DSA are already support in the driver, including tag insert for user port distinguishing, port control, bridge offloading, STP setup and ethtool operation to allow DSA to model each user port into a standalone netdevice as the other DSA driver had done. Signed-off-by: Sean Wang Signed-off-by: Landen Chao Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/Kconfig | 8 + drivers/net/dsa/Makefile | 1 + drivers/net/dsa/mt7530.c | 1125 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/net/dsa/mt7530.h | 402 +++++++++++++++++ 4 files changed, 1536 insertions(+) create mode 100644 drivers/net/dsa/mt7530.c create mode 100644 drivers/net/dsa/mt7530.h diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index ba2e655eec19..31a2b229106d 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -42,4 +42,12 @@ config NET_DSA_LOOP This enables support for a fake mock-up switch chip which exercises the DSA APIs. +config NET_DSA_MT7530 + tristate "Mediatek MT7530 Ethernet switch support" + depends on NET_DSA + select NET_DSA_TAG_MTK + ---help--- + This enables support for the Mediatek MT7530 Ethernet switch + chip. + endmenu diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index 5c8830991041..2ae07f4fbf63 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm-sf2.o bcm-sf2-objs := bcm_sf2.o bcm_sf2_cfp.o obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o +obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o obj-y += b53/ obj-y += mv88e6xxx/ obj-$(CONFIG_NET_DSA_LOOP) += dsa_loop.o dsa_loop_bdinfo.o diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c new file mode 100644 index 000000000000..a4f3b0b8c28e --- /dev/null +++ b/drivers/net/dsa/mt7530.c @@ -0,0 +1,1125 @@ +/* + * Mediatek MT7530 DSA Switch driver + * Copyright (C) 2017 Sean Wang + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mt7530.h" + +/* String, offset, and register size in bytes if different from 4 bytes */ +static const struct mt7530_mib_desc mt7530_mib[] = { + MIB_DESC(1, 0x00, "TxDrop"), + MIB_DESC(1, 0x04, "TxCrcErr"), + MIB_DESC(1, 0x08, "TxUnicast"), + MIB_DESC(1, 0x0c, "TxMulticast"), + MIB_DESC(1, 0x10, "TxBroadcast"), + MIB_DESC(1, 0x14, "TxCollision"), + MIB_DESC(1, 0x18, "TxSingleCollision"), + MIB_DESC(1, 0x1c, "TxMultipleCollision"), + MIB_DESC(1, 0x20, "TxDeferred"), + MIB_DESC(1, 0x24, "TxLateCollision"), + MIB_DESC(1, 0x28, "TxExcessiveCollistion"), + MIB_DESC(1, 0x2c, "TxPause"), + MIB_DESC(1, 0x30, "TxPktSz64"), + MIB_DESC(1, 0x34, "TxPktSz65To127"), + MIB_DESC(1, 0x38, "TxPktSz128To255"), + MIB_DESC(1, 0x3c, "TxPktSz256To511"), + MIB_DESC(1, 0x40, "TxPktSz512To1023"), + MIB_DESC(1, 0x44, "Tx1024ToMax"), + MIB_DESC(2, 0x48, "TxBytes"), + MIB_DESC(1, 0x60, "RxDrop"), + MIB_DESC(1, 0x64, "RxFiltering"), + MIB_DESC(1, 0x6c, "RxMulticast"), + MIB_DESC(1, 0x70, "RxBroadcast"), + MIB_DESC(1, 0x74, "RxAlignErr"), + MIB_DESC(1, 0x78, "RxCrcErr"), + MIB_DESC(1, 0x7c, "RxUnderSizeErr"), + MIB_DESC(1, 0x80, "RxFragErr"), + MIB_DESC(1, 0x84, "RxOverSzErr"), + MIB_DESC(1, 0x88, "RxJabberErr"), + MIB_DESC(1, 0x8c, "RxPause"), + MIB_DESC(1, 0x90, "RxPktSz64"), + MIB_DESC(1, 0x94, "RxPktSz65To127"), + MIB_DESC(1, 0x98, "RxPktSz128To255"), + MIB_DESC(1, 0x9c, "RxPktSz256To511"), + MIB_DESC(1, 0xa0, "RxPktSz512To1023"), + MIB_DESC(1, 0xa4, "RxPktSz1024ToMax"), + MIB_DESC(2, 0xa8, "RxBytes"), + MIB_DESC(1, 0xb0, "RxCtrlDrop"), + MIB_DESC(1, 0xb4, "RxIngressDrop"), + MIB_DESC(1, 0xb8, "RxArlDrop"), +}; + +static int +mt7623_trgmii_write(struct mt7530_priv *priv, u32 reg, u32 val) +{ + int ret; + + ret = regmap_write(priv->ethernet, TRGMII_BASE(reg), val); + if (ret < 0) + dev_err(priv->dev, + "failed to priv write register\n"); + return ret; +} + +static u32 +mt7623_trgmii_read(struct mt7530_priv *priv, u32 reg) +{ + int ret; + u32 val; + + ret = regmap_read(priv->ethernet, TRGMII_BASE(reg), &val); + if (ret < 0) { + dev_err(priv->dev, + "failed to priv read register\n"); + return ret; + } + + return val; +} + +static void +mt7623_trgmii_rmw(struct mt7530_priv *priv, u32 reg, + u32 mask, u32 set) +{ + u32 val; + + val = mt7623_trgmii_read(priv, reg); + val &= ~mask; + val |= set; + mt7623_trgmii_write(priv, reg, val); +} + +static void +mt7623_trgmii_set(struct mt7530_priv *priv, u32 reg, u32 val) +{ + mt7623_trgmii_rmw(priv, reg, 0, val); +} + +static void +mt7623_trgmii_clear(struct mt7530_priv *priv, u32 reg, u32 val) +{ + mt7623_trgmii_rmw(priv, reg, val, 0); +} + +static int +core_read_mmd_indirect(struct mt7530_priv *priv, int prtad, int devad) +{ + struct mii_bus *bus = priv->bus; + int value, ret; + + /* Write the desired MMD Devad */ + ret = bus->write(bus, 0, MII_MMD_CTRL, devad); + if (ret < 0) + goto err; + + /* Write the desired MMD register address */ + ret = bus->write(bus, 0, MII_MMD_DATA, prtad); + if (ret < 0) + goto err; + + /* Select the Function : DATA with no post increment */ + ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); + if (ret < 0) + goto err; + + /* Read the content of the MMD's selected register */ + value = bus->read(bus, 0, MII_MMD_DATA); + + return value; +err: + dev_err(&bus->dev, "failed to read mmd register\n"); + + return ret; +} + +static int +core_write_mmd_indirect(struct mt7530_priv *priv, int prtad, + int devad, u32 data) +{ + struct mii_bus *bus = priv->bus; + int ret; + + /* Write the desired MMD Devad */ + ret = bus->write(bus, 0, MII_MMD_CTRL, devad); + if (ret < 0) + goto err; + + /* Write the desired MMD register address */ + ret = bus->write(bus, 0, MII_MMD_DATA, prtad); + if (ret < 0) + goto err; + + /* Select the Function : DATA with no post increment */ + ret = bus->write(bus, 0, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); + if (ret < 0) + goto err; + + /* Write the data into MMD's selected register */ + ret = bus->write(bus, 0, MII_MMD_DATA, data); +err: + if (ret < 0) + dev_err(&bus->dev, + "failed to write mmd register\n"); + return ret; +} + +static void +core_write(struct mt7530_priv *priv, u32 reg, u32 val) +{ + struct mii_bus *bus = priv->bus; + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + + core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val); + + mutex_unlock(&bus->mdio_lock); +} + +static void +core_rmw(struct mt7530_priv *priv, u32 reg, u32 mask, u32 set) +{ + struct mii_bus *bus = priv->bus; + u32 val; + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + + val = core_read_mmd_indirect(priv, reg, MDIO_MMD_VEND2); + val &= ~mask; + val |= set; + core_write_mmd_indirect(priv, reg, MDIO_MMD_VEND2, val); + + mutex_unlock(&bus->mdio_lock); +} + +static void +core_set(struct mt7530_priv *priv, u32 reg, u32 val) +{ + core_rmw(priv, reg, 0, val); +} + +static void +core_clear(struct mt7530_priv *priv, u32 reg, u32 val) +{ + core_rmw(priv, reg, val, 0); +} + +static int +mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val) +{ + struct mii_bus *bus = priv->bus; + u16 page, r, lo, hi; + int ret; + + page = (reg >> 6) & 0x3ff; + r = (reg >> 2) & 0xf; + lo = val & 0xffff; + hi = val >> 16; + + /* MT7530 uses 31 as the pseudo port */ + ret = bus->write(bus, 0x1f, 0x1f, page); + if (ret < 0) + goto err; + + ret = bus->write(bus, 0x1f, r, lo); + if (ret < 0) + goto err; + + ret = bus->write(bus, 0x1f, 0x10, hi); +err: + if (ret < 0) + dev_err(&bus->dev, + "failed to write mt7530 register\n"); + return ret; +} + +static u32 +mt7530_mii_read(struct mt7530_priv *priv, u32 reg) +{ + struct mii_bus *bus = priv->bus; + u16 page, r, lo, hi; + int ret; + + page = (reg >> 6) & 0x3ff; + r = (reg >> 2) & 0xf; + + /* MT7530 uses 31 as the pseudo port */ + ret = bus->write(bus, 0x1f, 0x1f, page); + if (ret < 0) { + dev_err(&bus->dev, + "failed to read mt7530 register\n"); + return ret; + } + + lo = bus->read(bus, 0x1f, r); + hi = bus->read(bus, 0x1f, 0x10); + + return (hi << 16) | (lo & 0xffff); +} + +static void +mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val) +{ + struct mii_bus *bus = priv->bus; + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + + mt7530_mii_write(priv, reg, val); + + mutex_unlock(&bus->mdio_lock); +} + +static u32 +_mt7530_read(struct mt7530_dummy_poll *p) +{ + struct mii_bus *bus = p->priv->bus; + u32 val; + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + + val = mt7530_mii_read(p->priv, p->reg); + + mutex_unlock(&bus->mdio_lock); + + return val; +} + +static u32 +mt7530_read(struct mt7530_priv *priv, u32 reg) +{ + struct mt7530_dummy_poll p; + + INIT_MT7530_DUMMY_POLL(&p, priv, reg); + return _mt7530_read(&p); +} + +static void +mt7530_rmw(struct mt7530_priv *priv, u32 reg, + u32 mask, u32 set) +{ + struct mii_bus *bus = priv->bus; + u32 val; + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + + val = mt7530_mii_read(priv, reg); + val &= ~mask; + val |= set; + mt7530_mii_write(priv, reg, val); + + mutex_unlock(&bus->mdio_lock); +} + +static void +mt7530_set(struct mt7530_priv *priv, u32 reg, u32 val) +{ + mt7530_rmw(priv, reg, 0, val); +} + +static void +mt7530_clear(struct mt7530_priv *priv, u32 reg, u32 val) +{ + mt7530_rmw(priv, reg, val, 0); +} + +static int +mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp) +{ + u32 val; + int ret; + struct mt7530_dummy_poll p; + + /* Set the command operating upon the MAC address entries */ + val = ATC_BUSY | ATC_MAT(0) | cmd; + mt7530_write(priv, MT7530_ATC, val); + + INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_ATC); + ret = readx_poll_timeout(_mt7530_read, &p, val, + !(val & ATC_BUSY), 20, 20000); + if (ret < 0) { + dev_err(priv->dev, "reset timeout\n"); + return ret; + } + + /* Additional sanity for read command if the specified + * entry is invalid + */ + val = mt7530_read(priv, MT7530_ATC); + if ((cmd == MT7530_FDB_READ) && (val & ATC_INVALID)) + return -EINVAL; + + if (rsp) + *rsp = val; + + return 0; +} + +static void +mt7530_fdb_read(struct mt7530_priv *priv, struct mt7530_fdb *fdb) +{ + u32 reg[3]; + int i; + + /* Read from ARL table into an array */ + for (i = 0; i < 3; i++) { + reg[i] = mt7530_read(priv, MT7530_TSRA1 + (i * 4)); + + dev_dbg(priv->dev, "%s(%d) reg[%d]=0x%x\n", + __func__, __LINE__, i, reg[i]); + } + + fdb->vid = (reg[1] >> CVID) & CVID_MASK; + fdb->aging = (reg[2] >> AGE_TIMER) & AGE_TIMER_MASK; + fdb->port_mask = (reg[2] >> PORT_MAP) & PORT_MAP_MASK; + fdb->mac[0] = (reg[0] >> MAC_BYTE_0) & MAC_BYTE_MASK; + fdb->mac[1] = (reg[0] >> MAC_BYTE_1) & MAC_BYTE_MASK; + fdb->mac[2] = (reg[0] >> MAC_BYTE_2) & MAC_BYTE_MASK; + fdb->mac[3] = (reg[0] >> MAC_BYTE_3) & MAC_BYTE_MASK; + fdb->mac[4] = (reg[1] >> MAC_BYTE_4) & MAC_BYTE_MASK; + fdb->mac[5] = (reg[1] >> MAC_BYTE_5) & MAC_BYTE_MASK; + fdb->noarp = ((reg[2] >> ENT_STATUS) & ENT_STATUS_MASK) == STATIC_ENT; +} + +static void +mt7530_fdb_write(struct mt7530_priv *priv, u16 vid, + u8 port_mask, const u8 *mac, + u8 aging, u8 type) +{ + u32 reg[3] = { 0 }; + int i; + + reg[1] |= vid & CVID_MASK; + reg[2] |= (aging & AGE_TIMER_MASK) << AGE_TIMER; + reg[2] |= (port_mask & PORT_MAP_MASK) << PORT_MAP; + /* STATIC_ENT indicate that entry is static wouldn't + * be aged out and STATIC_EMP specified as erasing an + * entry + */ + reg[2] |= (type & ENT_STATUS_MASK) << ENT_STATUS; + reg[1] |= mac[5] << MAC_BYTE_5; + reg[1] |= mac[4] << MAC_BYTE_4; + reg[0] |= mac[3] << MAC_BYTE_3; + reg[0] |= mac[2] << MAC_BYTE_2; + reg[0] |= mac[1] << MAC_BYTE_1; + reg[0] |= mac[0] << MAC_BYTE_0; + + /* Write array into the ARL table */ + for (i = 0; i < 3; i++) + mt7530_write(priv, MT7530_ATA1 + (i * 4), reg[i]); +} + +static int +mt7530_pad_clk_setup(struct dsa_switch *ds, int mode) +{ + struct mt7530_priv *priv = ds->priv; + u32 ncpo1, ssc_delta, trgint, i; + + switch (mode) { + case PHY_INTERFACE_MODE_RGMII: + trgint = 0; + ncpo1 = 0x0c80; + ssc_delta = 0x87; + break; + case PHY_INTERFACE_MODE_TRGMII: + trgint = 1; + ncpo1 = 0x1400; + ssc_delta = 0x57; + break; + default: + dev_err(priv->dev, "xMII mode %d not supported\n", mode); + return -EINVAL; + } + + mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, + P6_INTF_MODE(trgint)); + + /* Lower Tx Driving for TRGMII path */ + for (i = 0 ; i < NUM_TRGMII_CTRL ; i++) + mt7530_write(priv, MT7530_TRGMII_TD_ODT(i), + TD_DM_DRVP(8) | TD_DM_DRVN(8)); + + /* Setup core clock for MT7530 */ + if (!trgint) { + /* Disable MT7530 core clock */ + core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN); + + /* Disable PLL, since phy_device has not yet been created + * provided for phy_[read,write]_mmd_indirect is called, we + * provide our own core_write_mmd_indirect to complete this + * function. + */ + core_write_mmd_indirect(priv, + CORE_GSWPLL_GRP1, + MDIO_MMD_VEND2, + 0); + + /* Set core clock into 500Mhz */ + core_write(priv, CORE_GSWPLL_GRP2, + RG_GSWPLL_POSDIV_500M(1) | + RG_GSWPLL_FBKDIV_500M(25)); + + /* Enable PLL */ + core_write(priv, CORE_GSWPLL_GRP1, + RG_GSWPLL_EN_PRE | + RG_GSWPLL_POSDIV_200M(2) | + RG_GSWPLL_FBKDIV_200M(32)); + + /* Enable MT7530 core clock */ + core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN); + } + + /* Setup the MT7530 TRGMII Tx Clock */ + core_set(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN); + core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1)); + core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0)); + core_write(priv, CORE_PLL_GROUP10, RG_LCDDS_SSC_DELTA(ssc_delta)); + core_write(priv, CORE_PLL_GROUP11, RG_LCDDS_SSC_DELTA1(ssc_delta)); + core_write(priv, CORE_PLL_GROUP4, + RG_SYSPLL_DDSFBK_EN | RG_SYSPLL_BIAS_EN | + RG_SYSPLL_BIAS_LPF_EN); + core_write(priv, CORE_PLL_GROUP2, + RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN | + RG_SYSPLL_POSDIV(1)); + core_write(priv, CORE_PLL_GROUP7, + RG_LCDDS_PCW_NCPO_CHG | RG_LCCDS_C(3) | + RG_LCDDS_PWDB | RG_LCDDS_ISO_EN); + core_set(priv, CORE_TRGMII_GSW_CLK_CG, + REG_GSWCK_EN | REG_TRGMIICK_EN); + + if (!trgint) + for (i = 0 ; i < NUM_TRGMII_CTRL; i++) + mt7530_rmw(priv, MT7530_TRGMII_RD(i), + RD_TAP_MASK, RD_TAP(16)); + else + mt7623_trgmii_set(priv, GSW_INTF_MODE, INTF_MODE_TRGMII); + + return 0; +} + +static int +mt7623_pad_clk_setup(struct dsa_switch *ds) +{ + struct mt7530_priv *priv = ds->priv; + int i; + + for (i = 0 ; i < NUM_TRGMII_CTRL; i++) + mt7623_trgmii_write(priv, GSW_TRGMII_TD_ODT(i), + TD_DM_DRVP(8) | TD_DM_DRVN(8)); + + mt7623_trgmii_set(priv, GSW_TRGMII_RCK_CTRL, RX_RST | RXC_DQSISEL); + mt7623_trgmii_clear(priv, GSW_TRGMII_RCK_CTRL, RX_RST); + + return 0; +} + +static void +mt7530_mib_reset(struct dsa_switch *ds) +{ + struct mt7530_priv *priv = ds->priv; + + mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_FLUSH); + mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_ACTIVATE); +} + +static void +mt7530_port_set_status(struct mt7530_priv *priv, int port, int enable) +{ + u32 mask = PMCR_TX_EN | PMCR_RX_EN; + + if (enable) + mt7530_set(priv, MT7530_PMCR_P(port), mask); + else + mt7530_clear(priv, MT7530_PMCR_P(port), mask); +} + +static int mt7530_phy_read(struct dsa_switch *ds, int port, int regnum) +{ + struct mt7530_priv *priv = ds->priv; + + return mdiobus_read_nested(priv->bus, port, regnum); +} + +int mt7530_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val) +{ + struct mt7530_priv *priv = ds->priv; + + return mdiobus_write_nested(priv->bus, port, regnum, val); +} + +static void +mt7530_get_strings(struct dsa_switch *ds, int port, uint8_t *data) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt7530_mib); i++) + strncpy(data + i * ETH_GSTRING_LEN, mt7530_mib[i].name, + ETH_GSTRING_LEN); +} + +static void +mt7530_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *data) +{ + struct mt7530_priv *priv = ds->priv; + const struct mt7530_mib_desc *mib; + u32 reg, i; + u64 hi; + + for (i = 0; i < ARRAY_SIZE(mt7530_mib); i++) { + mib = &mt7530_mib[i]; + reg = MT7530_PORT_MIB_COUNTER(port) + mib->offset; + + data[i] = mt7530_read(priv, reg); + if (mib->size == 2) { + hi = mt7530_read(priv, reg + 4); + data[i] |= hi << 32; + } + } +} + +static int +mt7530_get_sset_count(struct dsa_switch *ds) +{ + return ARRAY_SIZE(mt7530_mib); +} + +static void mt7530_adjust_link(struct dsa_switch *ds, int port, + struct phy_device *phydev) +{ + struct mt7530_priv *priv = ds->priv; + + if (phy_is_pseudo_fixed_link(phydev)) { + dev_dbg(priv->dev, "phy-mode for master device = %x\n", + phydev->interface); + + /* Setup TX circuit incluing relevant PAD and driving */ + mt7530_pad_clk_setup(ds, phydev->interface); + + /* Setup RX circuit, relevant PAD and driving on the host + * which must be placed after the setup on the device side is + * all finished. + */ + mt7623_pad_clk_setup(ds); + } +} + +static int +mt7530_cpu_port_enable(struct mt7530_priv *priv, + int port) +{ + /* Enable Mediatek header mode on the cpu port */ + mt7530_write(priv, MT7530_PVC_P(port), + PORT_SPEC_TAG); + + /* Setup the MAC by default for the cpu port */ + mt7530_write(priv, MT7530_PMCR_P(port), PMCR_CPUP_LINK); + + /* Disable auto learning on the cpu port */ + mt7530_set(priv, MT7530_PSC_P(port), SA_DIS); + + /* Unknown unicast frame fordwarding to the cpu port */ + mt7530_set(priv, MT7530_MFC, UNU_FFP(BIT(port))); + + /* CPU port gets connected to all user ports of + * the switch + */ + mt7530_write(priv, MT7530_PCR_P(port), + PCR_MATRIX(priv->ds->enabled_port_mask)); + + return 0; +} + +static int +mt7530_port_enable(struct dsa_switch *ds, int port, + struct phy_device *phy) +{ + struct mt7530_priv *priv = ds->priv; + + mutex_lock(&priv->reg_mutex); + + /* Setup the MAC for the user port */ + mt7530_write(priv, MT7530_PMCR_P(port), PMCR_USERP_LINK); + + /* Allow the user port gets connected to the cpu port and also + * restore the port matrix if the port is the member of a certain + * bridge. + */ + priv->ports[port].pm |= PCR_MATRIX(BIT(MT7530_CPU_PORT)); + priv->ports[port].enable = true; + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, + priv->ports[port].pm); + mt7530_port_set_status(priv, port, 1); + + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static void +mt7530_port_disable(struct dsa_switch *ds, int port, + struct phy_device *phy) +{ + struct mt7530_priv *priv = ds->priv; + + mutex_lock(&priv->reg_mutex); + + /* Clear up all port matrix which could be restored in the next + * enablement for the port. + */ + priv->ports[port].enable = false; + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, + PCR_MATRIX_CLR); + mt7530_port_set_status(priv, port, 0); + + mutex_unlock(&priv->reg_mutex); +} + +static void +mt7530_stp_state_set(struct dsa_switch *ds, int port, u8 state) +{ + struct mt7530_priv *priv = ds->priv; + u32 stp_state; + + switch (state) { + case BR_STATE_DISABLED: + stp_state = MT7530_STP_DISABLED; + break; + case BR_STATE_BLOCKING: + stp_state = MT7530_STP_BLOCKING; + break; + case BR_STATE_LISTENING: + stp_state = MT7530_STP_LISTENING; + break; + case BR_STATE_LEARNING: + stp_state = MT7530_STP_LEARNING; + break; + case BR_STATE_FORWARDING: + default: + stp_state = MT7530_STP_FORWARDING; + break; + } + + mt7530_rmw(priv, MT7530_SSP_P(port), FID_PST_MASK, stp_state); +} + +static int +mt7530_port_bridge_join(struct dsa_switch *ds, int port, + struct net_device *bridge) +{ + struct mt7530_priv *priv = ds->priv; + u32 port_bitmap = BIT(MT7530_CPU_PORT); + int i; + + mutex_lock(&priv->reg_mutex); + + for (i = 0; i < MT7530_NUM_PORTS; i++) { + /* Add this port to the port matrix of the other ports in the + * same bridge. If the port is disabled, port matrix is kept + * and not being setup until the port becomes enabled. + */ + if (ds->enabled_port_mask & BIT(i) && i != port) { + if (ds->ports[i].bridge_dev != bridge) + continue; + if (priv->ports[i].enable) + mt7530_set(priv, MT7530_PCR_P(i), + PCR_MATRIX(BIT(port))); + priv->ports[i].pm |= PCR_MATRIX(BIT(port)); + + port_bitmap |= BIT(i); + } + } + + /* Add the all other ports to this port matrix. */ + if (priv->ports[port].enable) + mt7530_rmw(priv, MT7530_PCR_P(port), + PCR_MATRIX_MASK, PCR_MATRIX(port_bitmap)); + priv->ports[port].pm |= PCR_MATRIX(port_bitmap); + + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static void +mt7530_port_bridge_leave(struct dsa_switch *ds, int port, + struct net_device *bridge) +{ + struct mt7530_priv *priv = ds->priv; + int i; + + mutex_lock(&priv->reg_mutex); + + for (i = 0; i < MT7530_NUM_PORTS; i++) { + /* Remove this port from the port matrix of the other ports + * in the same bridge. If the port is disabled, port matrix + * is kept and not being setup until the port becomes enabled. + */ + if (ds->enabled_port_mask & BIT(i) && i != port) { + if (ds->ports[i].bridge_dev != bridge) + continue; + if (priv->ports[i].enable) + mt7530_clear(priv, MT7530_PCR_P(i), + PCR_MATRIX(BIT(port))); + priv->ports[i].pm &= ~PCR_MATRIX(BIT(port)); + } + } + + /* Set the cpu port to be the only one in the port matrix of + * this port. + */ + if (priv->ports[port].enable) + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, + PCR_MATRIX(BIT(MT7530_CPU_PORT))); + priv->ports[port].pm = PCR_MATRIX(BIT(MT7530_CPU_PORT)); + + mutex_unlock(&priv->reg_mutex); +} + +static int +mt7530_port_fdb_prepare(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) +{ + struct mt7530_priv *priv = ds->priv; + int ret; + + /* Because auto-learned entrie shares the same FDB table. + * an entry is reserved with no port_mask to make sure fdb_add + * is called while the entry is still available. + */ + mutex_lock(&priv->reg_mutex); + mt7530_fdb_write(priv, fdb->vid, 0, fdb->addr, -1, STATIC_ENT); + ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); + mutex_unlock(&priv->reg_mutex); + + return ret; +} + +static void +mt7530_port_fdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb, + struct switchdev_trans *trans) +{ + struct mt7530_priv *priv = ds->priv; + u8 port_mask = BIT(port); + + mutex_lock(&priv->reg_mutex); + mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_ENT); + mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); + mutex_unlock(&priv->reg_mutex); +} + +static int +mt7530_port_fdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_fdb *fdb) +{ + struct mt7530_priv *priv = ds->priv; + int ret; + u8 port_mask = BIT(port); + + mutex_lock(&priv->reg_mutex); + mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_EMP); + ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); + mutex_unlock(&priv->reg_mutex); + + return ret; +} + +static int +mt7530_port_fdb_dump(struct dsa_switch *ds, int port, + struct switchdev_obj_port_fdb *fdb, + int (*cb)(struct switchdev_obj *obj)) +{ + struct mt7530_priv *priv = ds->priv; + struct mt7530_fdb _fdb = { 0 }; + int cnt = MT7530_NUM_FDB_RECORDS; + int ret = 0; + u32 rsp = 0; + + mutex_lock(&priv->reg_mutex); + + ret = mt7530_fdb_cmd(priv, MT7530_FDB_START, &rsp); + if (ret < 0) + goto err; + + do { + if (rsp & ATC_SRCH_HIT) { + mt7530_fdb_read(priv, &_fdb); + if (_fdb.port_mask & BIT(port)) { + ether_addr_copy(fdb->addr, _fdb.mac); + fdb->vid = _fdb.vid; + fdb->ndm_state = _fdb.noarp ? + NUD_NOARP : NUD_REACHABLE; + ret = cb(&fdb->obj); + if (ret < 0) + break; + } + } + } while (--cnt && + !(rsp & ATC_SRCH_END) && + !mt7530_fdb_cmd(priv, MT7530_FDB_NEXT, &rsp)); +err: + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +static enum dsa_tag_protocol +mtk_get_tag_protocol(struct dsa_switch *ds) +{ + struct mt7530_priv *priv = ds->priv; + + if (!dsa_is_cpu_port(ds, MT7530_CPU_PORT)) { + dev_warn(priv->dev, + "port not matched with tagging CPU port\n"); + return DSA_TAG_PROTO_NONE; + } else { + return DSA_TAG_PROTO_MTK; + } +} + +static int +mt7530_setup(struct dsa_switch *ds) +{ + struct mt7530_priv *priv = ds->priv; + int ret, i; + u32 id, val; + struct device_node *dn; + struct mt7530_dummy_poll p; + + /* The parent node of master_netdev which holds the common system + * controller also is the container for two GMACs nodes representing + * as two netdev instances. + */ + dn = ds->master_netdev->dev.of_node->parent; + priv->ethernet = syscon_node_to_regmap(dn); + if (IS_ERR(priv->ethernet)) + return PTR_ERR(priv->ethernet); + + regulator_set_voltage(priv->core_pwr, 1000000, 1000000); + ret = regulator_enable(priv->core_pwr); + if (ret < 0) { + dev_err(priv->dev, + "Failed to enable core power: %d\n", ret); + return ret; + } + + regulator_set_voltage(priv->io_pwr, 3300000, 3300000); + ret = regulator_enable(priv->io_pwr); + if (ret < 0) { + dev_err(priv->dev, "Failed to enable io pwr: %d\n", + ret); + return ret; + } + + /* Reset whole chip through gpio pin or memory-mapped registers for + * different type of hardware + */ + if (priv->mcm) { + reset_control_assert(priv->rstc); + usleep_range(1000, 1100); + reset_control_deassert(priv->rstc); + } else { + gpiod_set_value_cansleep(priv->reset, 0); + usleep_range(1000, 1100); + gpiod_set_value_cansleep(priv->reset, 1); + } + + /* Waiting for MT7530 got to stable */ + INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_HWTRAP); + ret = readx_poll_timeout(_mt7530_read, &p, val, val != 0, + 20, 1000000); + if (ret < 0) { + dev_err(priv->dev, "reset timeout\n"); + return ret; + } + + id = mt7530_read(priv, MT7530_CREV); + id >>= CHIP_NAME_SHIFT; + if (id != MT7530_ID) { + dev_err(priv->dev, "chip %x can't be supported\n", id); + return -ENODEV; + } + + /* Reset the switch through internal reset */ + mt7530_write(priv, MT7530_SYS_CTRL, + SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | + SYS_CTRL_REG_RST); + + /* Enable Port 6 only; P5 as GMAC5 which currently is not supported */ + val = mt7530_read(priv, MT7530_MHWTRAP); + val &= ~MHWTRAP_P6_DIS & ~MHWTRAP_PHY_ACCESS; + val |= MHWTRAP_MANUAL; + mt7530_write(priv, MT7530_MHWTRAP, val); + + /* Enable and reset MIB counters */ + mt7530_mib_reset(ds); + + mt7530_clear(priv, MT7530_MFC, UNU_FFP_MASK); + + for (i = 0; i < MT7530_NUM_PORTS; i++) { + /* Disable forwarding by default on all ports */ + mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, + PCR_MATRIX_CLR); + + if (dsa_is_cpu_port(ds, i)) + mt7530_cpu_port_enable(priv, i); + else + mt7530_port_disable(ds, i, NULL); + } + + /* Flush the FDB table */ + ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, 0); + if (ret < 0) + return ret; + + return 0; +} + +static struct dsa_switch_ops mt7530_switch_ops = { + .get_tag_protocol = mtk_get_tag_protocol, + .setup = mt7530_setup, + .get_strings = mt7530_get_strings, + .phy_read = mt7530_phy_read, + .phy_write = mt7530_phy_write, + .get_ethtool_stats = mt7530_get_ethtool_stats, + .get_sset_count = mt7530_get_sset_count, + .adjust_link = mt7530_adjust_link, + .port_enable = mt7530_port_enable, + .port_disable = mt7530_port_disable, + .port_stp_state_set = mt7530_stp_state_set, + .port_bridge_join = mt7530_port_bridge_join, + .port_bridge_leave = mt7530_port_bridge_leave, + .port_fdb_prepare = mt7530_port_fdb_prepare, + .port_fdb_add = mt7530_port_fdb_add, + .port_fdb_del = mt7530_port_fdb_del, + .port_fdb_dump = mt7530_port_fdb_dump, +}; + +static int +mt7530_probe(struct mdio_device *mdiodev) +{ + struct mt7530_priv *priv; + struct device_node *dn; + + dn = mdiodev->dev.of_node; + + priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ds = dsa_switch_alloc(&mdiodev->dev, DSA_MAX_PORTS); + if (!priv->ds) + return -ENOMEM; + + /* Use medatek,mcm property to distinguish hardware type that would + * casues a little bit differences on power-on sequence. + */ + priv->mcm = of_property_read_bool(dn, "mediatek,mcm"); + if (priv->mcm) { + dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n"); + + priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm"); + if (IS_ERR(priv->rstc)) { + dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); + return PTR_ERR(priv->rstc); + } + } + + priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core"); + if (IS_ERR(priv->core_pwr)) + return PTR_ERR(priv->core_pwr); + + priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io"); + if (IS_ERR(priv->io_pwr)) + return PTR_ERR(priv->io_pwr); + + /* Not MCM that indicates switch works as the remote standalone + * integrated circuit so the GPIO pin would be used to complete + * the reset, otherwise memory-mapped register accessing used + * through syscon provides in the case of MCM. + */ + if (!priv->mcm) { + priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) { + dev_err(&mdiodev->dev, "Couldn't get our reset line\n"); + return PTR_ERR(priv->reset); + } + } + + priv->bus = mdiodev->bus; + priv->dev = &mdiodev->dev; + priv->ds->priv = priv; + priv->ds->ops = &mt7530_switch_ops; + mutex_init(&priv->reg_mutex); + dev_set_drvdata(&mdiodev->dev, priv); + + return dsa_register_switch(priv->ds, &mdiodev->dev); +} + +static void +mt7530_remove(struct mdio_device *mdiodev) +{ + struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); + int ret = 0; + + ret = regulator_disable(priv->core_pwr); + if (ret < 0) + dev_err(priv->dev, + "Failed to disable core power: %d\n", ret); + + ret = regulator_disable(priv->io_pwr); + if (ret < 0) + dev_err(priv->dev, "Failed to disable io pwr: %d\n", + ret); + + dsa_unregister_switch(priv->ds); + mutex_destroy(&priv->reg_mutex); +} + +static const struct of_device_id mt7530_of_match[] = { + { .compatible = "mediatek,mt7530" }, + { /* sentinel */ }, +}; + +static struct mdio_driver mt7530_mdio_driver = { + .probe = mt7530_probe, + .remove = mt7530_remove, + .mdiodrv.driver = { + .name = "mt7530", + .of_match_table = mt7530_of_match, + }, +}; + +mdio_module_driver(mt7530_mdio_driver); + +MODULE_AUTHOR("Sean Wang "); +MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mediatek-mt7530"); diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h new file mode 100644 index 000000000000..b83d76b99802 --- /dev/null +++ b/drivers/net/dsa/mt7530.h @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2017 Sean Wang + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MT7530_H +#define __MT7530_H + +#define MT7530_NUM_PORTS 7 +#define MT7530_CPU_PORT 6 +#define MT7530_NUM_FDB_RECORDS 2048 + +#define NUM_TRGMII_CTRL 5 + +#define TRGMII_BASE(x) (0x10000 + (x)) + +/* Registers to ethsys access */ +#define ETHSYS_CLKCFG0 0x2c +#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11) + +#define SYSC_REG_RSTCTRL 0x34 +#define RESET_MCM BIT(2) + +/* Registers to mac forward control for unknown frames */ +#define MT7530_MFC 0x10 +#define BC_FFP(x) (((x) & 0xff) << 24) +#define UNM_FFP(x) (((x) & 0xff) << 16) +#define UNU_FFP(x) (((x) & 0xff) << 8) +#define UNU_FFP_MASK UNU_FFP(~0) + +/* Registers for address table access */ +#define MT7530_ATA1 0x74 +#define STATIC_EMP 0 +#define STATIC_ENT 3 +#define MT7530_ATA2 0x78 + +/* Register for address table write data */ +#define MT7530_ATWD 0x7c + +/* Register for address table control */ +#define MT7530_ATC 0x80 +#define ATC_HASH (((x) & 0xfff) << 16) +#define ATC_BUSY BIT(15) +#define ATC_SRCH_END BIT(14) +#define ATC_SRCH_HIT BIT(13) +#define ATC_INVALID BIT(12) +#define ATC_MAT(x) (((x) & 0xf) << 8) +#define ATC_MAT_MACTAB ATC_MAT(0) + +enum mt7530_fdb_cmd { + MT7530_FDB_READ = 0, + MT7530_FDB_WRITE = 1, + MT7530_FDB_FLUSH = 2, + MT7530_FDB_START = 4, + MT7530_FDB_NEXT = 5, +}; + +/* Registers for table search read address */ +#define MT7530_TSRA1 0x84 +#define MAC_BYTE_0 24 +#define MAC_BYTE_1 16 +#define MAC_BYTE_2 8 +#define MAC_BYTE_3 0 +#define MAC_BYTE_MASK 0xff + +#define MT7530_TSRA2 0x88 +#define MAC_BYTE_4 24 +#define MAC_BYTE_5 16 +#define CVID 0 +#define CVID_MASK 0xfff + +#define MT7530_ATRD 0x8C +#define AGE_TIMER 24 +#define AGE_TIMER_MASK 0xff +#define PORT_MAP 4 +#define PORT_MAP_MASK 0xff +#define ENT_STATUS 2 +#define ENT_STATUS_MASK 0x3 + +/* Register for vlan table control */ +#define MT7530_VTCR 0x90 +#define VTCR_BUSY BIT(31) +#define VTCR_FUNC (((x) & 0xf) << 12) +#define VTCR_FUNC_RD_VID 0x1 +#define VTCR_FUNC_WR_VID 0x2 +#define VTCR_FUNC_INV_VID 0x3 +#define VTCR_FUNC_VAL_VID 0x4 +#define VTCR_VID ((x) & 0xfff) + +/* Register for setup vlan and acl write data */ +#define MT7530_VAWD1 0x94 +#define PORT_STAG BIT(31) +#define IVL_MAC BIT(30) +#define PORT_MEM(x) (((x) & 0xff) << 16) +#define VALID BIT(1) + +#define MT7530_VAWD2 0x98 + +/* Register for port STP state control */ +#define MT7530_SSP_P(x) (0x2000 + ((x) * 0x100)) +#define FID_PST(x) ((x) & 0x3) +#define FID_PST_MASK FID_PST(0x3) + +enum mt7530_stp_state { + MT7530_STP_DISABLED = 0, + MT7530_STP_BLOCKING = 1, + MT7530_STP_LISTENING = 1, + MT7530_STP_LEARNING = 2, + MT7530_STP_FORWARDING = 3 +}; + +/* Register for port control */ +#define MT7530_PCR_P(x) (0x2004 + ((x) * 0x100)) +#define PORT_VLAN(x) ((x) & 0x3) +#define PCR_MATRIX(x) (((x) & 0xff) << 16) +#define PORT_PRI(x) (((x) & 0x7) << 24) +#define EG_TAG(x) (((x) & 0x3) << 28) +#define PCR_MATRIX_MASK PCR_MATRIX(0xff) +#define PCR_MATRIX_CLR PCR_MATRIX(0) + +/* Register for port security control */ +#define MT7530_PSC_P(x) (0x200c + ((x) * 0x100)) +#define SA_DIS BIT(4) + +/* Register for port vlan control */ +#define MT7530_PVC_P(x) (0x2010 + ((x) * 0x100)) +#define PORT_SPEC_TAG BIT(5) +#define VLAN_ATTR(x) (((x) & 0x3) << 6) +#define STAG_VPID (((x) & 0xffff) << 16) + +/* Register for port port-and-protocol based vlan 1 control */ +#define MT7530_PPBV1_P(x) (0x2014 + ((x) * 0x100)) + +/* Register for port MAC control register */ +#define MT7530_PMCR_P(x) (0x3000 + ((x) * 0x100)) +#define PMCR_IFG_XMIT(x) (((x) & 0x3) << 18) +#define PMCR_MAC_MODE BIT(16) +#define PMCR_FORCE_MODE BIT(15) +#define PMCR_TX_EN BIT(14) +#define PMCR_RX_EN BIT(13) +#define PMCR_BACKOFF_EN BIT(9) +#define PMCR_BACKPR_EN BIT(8) +#define PMCR_TX_FC_EN BIT(5) +#define PMCR_RX_FC_EN BIT(4) +#define PMCR_FORCE_SPEED_1000 BIT(3) +#define PMCR_FORCE_FDX BIT(1) +#define PMCR_FORCE_LNK BIT(0) +#define PMCR_COMMON_LINK (PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \ + PMCR_BACKOFF_EN | PMCR_BACKPR_EN | \ + PMCR_TX_EN | PMCR_RX_EN | \ + PMCR_TX_FC_EN | PMCR_RX_FC_EN) +#define PMCR_CPUP_LINK (PMCR_COMMON_LINK | PMCR_FORCE_MODE | \ + PMCR_FORCE_SPEED_1000 | \ + PMCR_FORCE_FDX | \ + PMCR_FORCE_LNK) +#define PMCR_USERP_LINK PMCR_COMMON_LINK +#define PMCR_FIXED_LINK (PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \ + PMCR_FORCE_MODE | PMCR_TX_EN | \ + PMCR_RX_EN | PMCR_BACKPR_EN | \ + PMCR_BACKOFF_EN | \ + PMCR_FORCE_SPEED_1000 | \ + PMCR_FORCE_FDX | \ + PMCR_FORCE_LNK) +#define PMCR_FIXED_LINK_FC (PMCR_FIXED_LINK | \ + PMCR_TX_FC_EN | PMCR_RX_FC_EN) + +#define MT7530_PMSR_P(x) (0x3008 + (x) * 0x100) + +/* Register for MIB */ +#define MT7530_PORT_MIB_COUNTER(x) (0x4000 + (x) * 0x100) +#define MT7530_MIB_CCR 0x4fe0 +#define CCR_MIB_ENABLE BIT(31) +#define CCR_RX_OCT_CNT_GOOD BIT(7) +#define CCR_RX_OCT_CNT_BAD BIT(6) +#define CCR_TX_OCT_CNT_GOOD BIT(5) +#define CCR_TX_OCT_CNT_BAD BIT(4) +#define CCR_MIB_FLUSH (CCR_RX_OCT_CNT_GOOD | \ + CCR_RX_OCT_CNT_BAD | \ + CCR_TX_OCT_CNT_GOOD | \ + CCR_TX_OCT_CNT_BAD) +#define CCR_MIB_ACTIVATE (CCR_MIB_ENABLE | \ + CCR_RX_OCT_CNT_GOOD | \ + CCR_RX_OCT_CNT_BAD | \ + CCR_TX_OCT_CNT_GOOD | \ + CCR_TX_OCT_CNT_BAD) +/* Register for system reset */ +#define MT7530_SYS_CTRL 0x7000 +#define SYS_CTRL_PHY_RST BIT(2) +#define SYS_CTRL_SW_RST BIT(1) +#define SYS_CTRL_REG_RST BIT(0) + +/* Register for hw trap status */ +#define MT7530_HWTRAP 0x7800 + +/* Register for hw trap modification */ +#define MT7530_MHWTRAP 0x7804 +#define MHWTRAP_MANUAL BIT(16) +#define MHWTRAP_P5_MAC_SEL BIT(13) +#define MHWTRAP_P6_DIS BIT(8) +#define MHWTRAP_P5_RGMII_MODE BIT(7) +#define MHWTRAP_P5_DIS BIT(6) +#define MHWTRAP_PHY_ACCESS BIT(5) + +/* Register for TOP signal control */ +#define MT7530_TOP_SIG_CTRL 0x7808 +#define TOP_SIG_CTRL_NORMAL (BIT(17) | BIT(16)) + +#define MT7530_IO_DRV_CR 0x7810 +#define P5_IO_CLK_DRV(x) ((x) & 0x3) +#define P5_IO_DATA_DRV(x) (((x) & 0x3) << 4) + +#define MT7530_P6ECR 0x7830 +#define P6_INTF_MODE_MASK 0x3 +#define P6_INTF_MODE(x) ((x) & 0x3) + +/* Registers for TRGMII on the both side */ +#define MT7530_TRGMII_RCK_CTRL 0x7a00 +#define GSW_TRGMII_RCK_CTRL 0x300 +#define RX_RST BIT(31) +#define RXC_DQSISEL BIT(30) +#define DQSI1_TAP_MASK (0x7f << 8) +#define DQSI0_TAP_MASK 0x7f +#define DQSI1_TAP(x) (((x) & 0x7f) << 8) +#define DQSI0_TAP(x) ((x) & 0x7f) + +#define MT7530_TRGMII_RCK_RTT 0x7a04 +#define GSW_TRGMII_RCK_RTT 0x304 +#define DQS1_GATE BIT(31) +#define DQS0_GATE BIT(30) + +#define MT7530_TRGMII_RD(x) (0x7a10 + (x) * 8) +#define GSW_TRGMII_RD(x) (0x310 + (x) * 8) +#define BSLIP_EN BIT(31) +#define EDGE_CHK BIT(30) +#define RD_TAP_MASK 0x7f +#define RD_TAP(x) ((x) & 0x7f) + +#define GSW_TRGMII_TXCTRL 0x340 +#define MT7530_TRGMII_TXCTRL 0x7a40 +#define TRAIN_TXEN BIT(31) +#define TXC_INV BIT(30) +#define TX_RST BIT(28) + +#define MT7530_TRGMII_TD_ODT(i) (0x7a54 + 8 * (i)) +#define GSW_TRGMII_TD_ODT(i) (0x354 + 8 * (i)) +#define TD_DM_DRVP(x) ((x) & 0xf) +#define TD_DM_DRVN(x) (((x) & 0xf) << 4) + +#define GSW_INTF_MODE 0x390 +#define INTF_MODE_TRGMII BIT(1) + +#define MT7530_TRGMII_TCK_CTRL 0x7a78 +#define TCK_TAP(x) (((x) & 0xf) << 8) + +#define MT7530_P5RGMIIRXCR 0x7b00 +#define CSR_RGMII_EDGE_ALIGN BIT(8) +#define CSR_RGMII_RXC_0DEG_CFG(x) ((x) & 0xf) + +#define MT7530_P5RGMIITXCR 0x7b04 +#define CSR_RGMII_TXC_CFG(x) ((x) & 0x1f) + +#define MT7530_CREV 0x7ffc +#define CHIP_NAME_SHIFT 16 +#define MT7530_ID 0x7530 + +/* Registers for core PLL access through mmd indirect */ +#define CORE_PLL_GROUP2 0x401 +#define RG_SYSPLL_EN_NORMAL BIT(15) +#define RG_SYSPLL_VODEN BIT(14) +#define RG_SYSPLL_LF BIT(13) +#define RG_SYSPLL_RST_DLY(x) (((x) & 0x3) << 12) +#define RG_SYSPLL_LVROD_EN BIT(10) +#define RG_SYSPLL_PREDIV(x) (((x) & 0x3) << 8) +#define RG_SYSPLL_POSDIV(x) (((x) & 0x3) << 5) +#define RG_SYSPLL_FBKSEL BIT(4) +#define RT_SYSPLL_EN_AFE_OLT BIT(0) + +#define CORE_PLL_GROUP4 0x403 +#define RG_SYSPLL_DDSFBK_EN BIT(12) +#define RG_SYSPLL_BIAS_EN BIT(11) +#define RG_SYSPLL_BIAS_LPF_EN BIT(10) + +#define CORE_PLL_GROUP5 0x404 +#define RG_LCDDS_PCW_NCPO1(x) ((x) & 0xffff) + +#define CORE_PLL_GROUP6 0x405 +#define RG_LCDDS_PCW_NCPO0(x) ((x) & 0xffff) + +#define CORE_PLL_GROUP7 0x406 +#define RG_LCDDS_PWDB BIT(15) +#define RG_LCDDS_ISO_EN BIT(13) +#define RG_LCCDS_C(x) (((x) & 0x7) << 4) +#define RG_LCDDS_PCW_NCPO_CHG BIT(3) + +#define CORE_PLL_GROUP10 0x409 +#define RG_LCDDS_SSC_DELTA(x) ((x) & 0xfff) + +#define CORE_PLL_GROUP11 0x40a +#define RG_LCDDS_SSC_DELTA1(x) ((x) & 0xfff) + +#define CORE_GSWPLL_GRP1 0x40d +#define RG_GSWPLL_PREDIV(x) (((x) & 0x3) << 14) +#define RG_GSWPLL_POSDIV_200M(x) (((x) & 0x3) << 12) +#define RG_GSWPLL_EN_PRE BIT(11) +#define RG_GSWPLL_FBKSEL BIT(10) +#define RG_GSWPLL_BP BIT(9) +#define RG_GSWPLL_BR BIT(8) +#define RG_GSWPLL_FBKDIV_200M(x) ((x) & 0xff) + +#define CORE_GSWPLL_GRP2 0x40e +#define RG_GSWPLL_POSDIV_500M(x) (((x) & 0x3) << 8) +#define RG_GSWPLL_FBKDIV_500M(x) ((x) & 0xff) + +#define CORE_TRGMII_GSW_CLK_CG 0x410 +#define REG_GSWCK_EN BIT(0) +#define REG_TRGMIICK_EN BIT(1) + +#define MIB_DESC(_s, _o, _n) \ + { \ + .size = (_s), \ + .offset = (_o), \ + .name = (_n), \ + } + +struct mt7530_mib_desc { + unsigned int size; + unsigned int offset; + const char *name; +}; + +struct mt7530_fdb { + u16 vid; + u8 port_mask; + u8 aging; + u8 mac[6]; + bool noarp; +}; + +struct mt7530_port { + bool enable; + u32 pm; +}; + +/* struct mt7530_priv - This is the main data structure for holding the state + * of the driver + * @dev: The device pointer + * @ds: The pointer to the dsa core structure + * @bus: The bus used for the device and built-in PHY + * @rstc: The pointer to reset control used by MCM + * @ethernet: The regmap used for access TRGMII-based registers + * @core_pwr: The power supplied into the core + * @io_pwr: The power supplied into the I/O + * @reset: The descriptor for GPIO line tied to its reset pin + * @mcm: Flag for distinguishing if standalone IC or module + * coupling + * @ports: Holding the state among ports + * @reg_mutex: The lock for protecting among process accessing + * registers + */ +struct mt7530_priv { + struct device *dev; + struct dsa_switch *ds; + struct mii_bus *bus; + struct reset_control *rstc; + struct regmap *ethernet; + struct regulator *core_pwr; + struct regulator *io_pwr; + struct gpio_desc *reset; + bool mcm; + + struct mt7530_port ports[MT7530_NUM_PORTS]; + /* protect among processes for registers access*/ + struct mutex reg_mutex; +}; + +struct mt7530_hw_stats { + const char *string; + u16 reg; + u8 sizeof_stat; +}; + +struct mt7530_dummy_poll { + struct mt7530_priv *priv; + u32 reg; +}; + +static inline void INIT_MT7530_DUMMY_POLL(struct mt7530_dummy_poll *p, + struct mt7530_priv *priv, u32 reg) +{ + p->priv = priv; + p->reg = reg; +} + +#endif /* __MT7530_H */ -- cgit From 3a9024f52c2e92a143195db43d2abbd5d0792c06 Mon Sep 17 00:00:00 2001 From: Thanneeru Srinivasulu Date: Thu, 6 Apr 2017 16:12:26 +0530 Subject: net: thunderx: Enable TSO and checksum offloads for ipv6 Adding support for TSO and checksum hardware offloads for ipv6. Signed-off-by: Thanneeru Srinivasulu Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nicvf_main.c | 8 +++++--- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 10 +++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 24017588f531..330d8a03ea11 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -1665,8 +1665,9 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) goto err_unregister_interrupts; - netdev->hw_features = (NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG | - NETIF_F_TSO | NETIF_F_GRO | + netdev->hw_features = (NETIF_F_RXCSUM | NETIF_F_SG | + NETIF_F_TSO | NETIF_F_GRO | NETIF_F_TSO6 | + NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_HW_VLAN_CTAG_RX); netdev->hw_features |= NETIF_F_RXHASH; @@ -1674,7 +1675,8 @@ static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->features |= netdev->hw_features; netdev->hw_features |= NETIF_F_LOOPBACK; - netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; + netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6; netdev->netdev_ops = &nicvf_netdev_ops; netdev->watchdog_timeo = NICVF_TX_TIMEOUT; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index f13289f0d238..7b0fd8d871cc 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -1094,7 +1094,13 @@ nicvf_sq_add_hdr_subdesc(struct nicvf *nic, struct snd_queue *sq, int qentry, { int proto; struct sq_hdr_subdesc *hdr; + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } ip; + ip.hdr = skb_network_header(skb); hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, qentry); memset(hdr, 0, SND_QUEUE_DESC_SIZE); hdr->subdesc_type = SQ_DESC_TYPE_HEADER; @@ -1119,7 +1125,9 @@ nicvf_sq_add_hdr_subdesc(struct nicvf *nic, struct snd_queue *sq, int qentry, hdr->l3_offset = skb_network_offset(skb); hdr->l4_offset = skb_transport_offset(skb); - proto = ip_hdr(skb)->protocol; + proto = (ip.v4->version == 4) ? ip.v4->protocol : + ip.v6->nexthdr; + switch (proto) { case IPPROTO_TCP: hdr->csum_l4 = SEND_L4_CSUM_TCP; -- cgit From 004eb614c4d2fcc12a98714fd887a860582f203a Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Tue, 4 Apr 2017 12:40:16 -0700 Subject: i40e: only register client on iWarp-capable devices The client interface is only intended for use on devices that support iWarp. Only register with the client if this is the case. This fixes a panic when loading i40iw on X710 devices. Signed-off-by: Mitch Williams Reported-by: Stefan Assmann Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index d83430faaa41..1ee2759c38f7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -11245,10 +11245,12 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) round_jiffies(jiffies + pf->service_timer_period)); /* add this PF to client device list and launch a client service task */ - err = i40e_lan_add_device(pf); - if (err) - dev_info(&pdev->dev, "Failed to add PF to client API service list: %d\n", - err); + if (pf->flags & I40E_FLAG_IWARP_ENABLED) { + err = i40e_lan_add_device(pf); + if (err) + dev_info(&pdev->dev, "Failed to add PF to client API service list: %d\n", + err); + } #define PCI_SPEED_SIZE 8 #define PCI_WIDTH_SIZE 8 @@ -11426,10 +11428,11 @@ static void i40e_remove(struct pci_dev *pdev) i40e_vsi_release(pf->vsi[pf->lan_vsi]); /* remove attached clients */ - ret_code = i40e_lan_del_device(pf); - if (ret_code) { - dev_warn(&pdev->dev, "Failed to delete client device: %d\n", - ret_code); + if (pf->flags & I40E_FLAG_IWARP_ENABLED) { + ret_code = i40e_lan_del_device(pf); + if (ret_code) + dev_warn(&pdev->dev, "Failed to delete client device: %d\n", + ret_code); } /* shutdown and destroy the HMC */ -- cgit From a346fb836c712b43fc7bd925534eb8c23b3b61f0 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Apr 2017 07:50:53 -0400 Subject: i40e: update error message when trying to add invalid filters Re-word the error message displayed when adding a filter with an invalid flow type. Additionally, report a distinct error message when the IPv4 protocol is at fault. Change-ID: Iba3d85b87f8d383c97c8bdd180df34a6adf3ee67 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index ebffca0cefac..d45f48c84ff7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -533,14 +533,15 @@ int i40e_add_del_fdir(struct i40e_vsi *vsi, break; default: /* We cannot support masking based on protocol */ - goto unsupported_flow; + dev_info(&pf->pdev->dev, "Unsupported IPv4 protocol 0x%02x\n", + input->ip4_proto); + return -EINVAL; } break; default: -unsupported_flow: - dev_info(&pf->pdev->dev, "Could not specify spec type %d\n", + dev_info(&pf->pdev->dev, "Unsupported flow type 0x%02x\n", input->flow_type); - ret = -EINVAL; + return -EINVAL; } /* The buffer allocated here will be normally be freed by -- cgit From e8c5f7231cc03153fee1b5fcb173585354c08ee8 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 5 Apr 2017 07:50:54 -0400 Subject: i40e: Swap use of pf->flags and pf->hw_disabled_flags for ATR Eviction This is a minor cleanup so that we are always updating pf->flags when we make a change to the private flags instead of updating a mix of either pf->flags and/or pf->hw_disabled_flags. In addition I went through and cleaned out all the spots where we were using the X722 define in regards to this flag. Lastly since we changed the logic I went through and flushed out any redundancy and cleaned up the handling of the flags in the Tx path. Change-ID: I79ff95a7272bb2533251ff11ef91e89ccb80b610 Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index d45f48c84ff7..a9a97dd2c0a5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2263,8 +2263,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, /* Due to lack of space, no more new filters can be programmed */ if (th->syn && (pf->hw_disabled_flags & I40E_FLAG_FD_ATR_ENABLED)) return; - if ((pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) && - (!(pf->hw_disabled_flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE))) { + if (pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) { /* HW ATR eviction will take care of removing filters on FIN * and RST packets. */ @@ -2326,8 +2325,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) & I40E_TXD_FLTR_QW1_CNTINDEX_MASK; - if ((pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) && - (!(pf->hw_disabled_flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE))) + if (pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) dtype_cmd |= I40E_TXD_FLTR_QW1_ATR_MASK; fdir_desc->qindex_flex_ptype_vsi = cpu_to_le32(flex_ptype); -- cgit From 373149fc99a077700339e18839484a852e7b0971 Mon Sep 17 00:00:00 2001 From: Maciej Sosin Date: Wed, 5 Apr 2017 07:50:55 -0400 Subject: i40e: Decrease the scope of rtnl lock Previously rtnl lock was held during whole reset procedure that was stopping other PFs running their reset procedures. In the result reset was not handled properly and host reset was the only way to recover. Change-ID: I23c0771c0303caaa7bd64badbf0c667e25142954 Signed-off-by: Maciej Sosin Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 2 +- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 6 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 138 +++++++++++++++++-------- 3 files changed, 101 insertions(+), 45 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 421ea57128d3..686327c031fa 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -837,7 +837,7 @@ void i40e_down(struct i40e_vsi *vsi); extern const char i40e_driver_name[]; extern const char i40e_driver_version_str[]; void i40e_do_reset_safe(struct i40e_pf *pf, u32 reset_flags); -void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags); +void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags, bool lock_acquired); int i40e_config_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size); int i40e_get_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size); void i40e_fill_rss_lut(struct i40e_pf *pf, u8 *lut, diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index c0c1a0cdaa5b..68c0f204f93e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1852,7 +1852,7 @@ static void i40e_diag_test(struct net_device *netdev, * link then the following link test would have * to be moved to before the reset */ - i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED)); + i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED), true); if (i40e_link_test(netdev, &data[I40E_ETH_TEST_LINK])) eth_test->flags |= ETH_TEST_FL_FAILED; @@ -1868,7 +1868,7 @@ static void i40e_diag_test(struct net_device *netdev, eth_test->flags |= ETH_TEST_FL_FAILED; clear_bit(__I40E_TESTING, &pf->state); - i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED)); + i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED), true); if (if_running) i40e_open(netdev); @@ -4099,7 +4099,7 @@ flags_complete: */ if ((changed_flags & I40E_FLAG_VEB_STATS_ENABLED) || ((changed_flags & I40E_FLAG_LEGACY_RX) && netif_running(dev))) - i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED)); + i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED), true); return 0; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 1ee2759c38f7..8181647f512e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -50,13 +50,16 @@ static const char i40e_copyright[] = "Copyright (c) 2013 - 2014 Intel Corporatio /* a bit of forward declarations */ static void i40e_vsi_reinit_locked(struct i40e_vsi *vsi); -static void i40e_handle_reset_warning(struct i40e_pf *pf); +static void i40e_handle_reset_warning(struct i40e_pf *pf, bool lock_acquired); static int i40e_add_vsi(struct i40e_vsi *vsi); static int i40e_add_veb(struct i40e_veb *veb, struct i40e_vsi *vsi); static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit); static int i40e_setup_misc_vector(struct i40e_pf *pf); static void i40e_determine_queue_usage(struct i40e_pf *pf); static int i40e_setup_pf_filter_control(struct i40e_pf *pf); +static void i40e_prep_for_reset(struct i40e_pf *pf, bool lock_acquired); +static int i40e_reset(struct i40e_pf *pf); +static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired); static void i40e_fdir_sb_setup(struct i40e_pf *pf); static int i40e_veb_get_bw_info(struct i40e_veb *veb); @@ -5537,6 +5540,8 @@ int i40e_open(struct net_device *netdev) * Finish initialization of the VSI. * * Returns 0 on success, negative value on failure + * + * Note: expects to be called while under rtnl_lock() **/ int i40e_vsi_open(struct i40e_vsi *vsi) { @@ -5600,7 +5605,7 @@ err_setup_rx: err_setup_tx: i40e_vsi_free_tx_resources(vsi); if (vsi == pf->vsi[pf->lan_vsi]) - i40e_do_reset(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED)); + i40e_do_reset(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED), true); return err; } @@ -5686,12 +5691,14 @@ int i40e_close(struct net_device *netdev) * i40e_do_reset - Start a PF or Core Reset sequence * @pf: board private structure * @reset_flags: which reset is requested + * @lock_acquired: indicates whether or not the lock has been acquired + * before this function was called. * * The essential difference in resets is that the PF Reset * doesn't clear the packet buffers, doesn't reset the PE * firmware, and doesn't bother the other PFs on the chip. **/ -void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags) +void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags, bool lock_acquired) { u32 val; @@ -5737,7 +5744,7 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags) * for the Core Reset. */ dev_dbg(&pf->pdev->dev, "PFR requested\n"); - i40e_handle_reset_warning(pf); + i40e_handle_reset_warning(pf, lock_acquired); } else if (reset_flags & BIT_ULL(__I40E_REINIT_REQUESTED)) { int v; @@ -5946,7 +5953,7 @@ exit: void i40e_do_reset_safe(struct i40e_pf *pf, u32 reset_flags) { rtnl_lock(); - i40e_do_reset(pf, reset_flags); + i40e_do_reset(pf, reset_flags, true); rtnl_unlock(); } @@ -6348,7 +6355,6 @@ static void i40e_reset_subtask(struct i40e_pf *pf) { u32 reset_flags = 0; - rtnl_lock(); if (test_bit(__I40E_REINIT_REQUESTED, &pf->state)) { reset_flags |= BIT(__I40E_REINIT_REQUESTED); clear_bit(__I40E_REINIT_REQUESTED, &pf->state); @@ -6374,18 +6380,19 @@ static void i40e_reset_subtask(struct i40e_pf *pf) * precedence before starting a new reset sequence. */ if (test_bit(__I40E_RESET_INTR_RECEIVED, &pf->state)) { - i40e_handle_reset_warning(pf); - goto unlock; + i40e_prep_for_reset(pf, false); + i40e_reset(pf); + i40e_rebuild(pf, false, false); } /* If we're already down or resetting, just bail */ if (reset_flags && !test_bit(__I40E_DOWN, &pf->state) && - !test_bit(__I40E_CONFIG_BUSY, &pf->state)) - i40e_do_reset(pf, reset_flags); - -unlock: - rtnl_unlock(); + !test_bit(__I40E_CONFIG_BUSY, &pf->state)) { + rtnl_lock(); + i40e_do_reset(pf, reset_flags, true); + rtnl_unlock(); + } } /** @@ -6873,10 +6880,12 @@ static void i40e_fdir_teardown(struct i40e_pf *pf) /** * i40e_prep_for_reset - prep for the core to reset * @pf: board private structure + * @lock_acquired: indicates whether or not the lock has been acquired + * before this function was called. * * Close up the VFs and other things in prep for PF Reset. **/ -static void i40e_prep_for_reset(struct i40e_pf *pf) +static void i40e_prep_for_reset(struct i40e_pf *pf, bool lock_acquired) { struct i40e_hw *hw = &pf->hw; i40e_status ret = 0; @@ -6891,7 +6900,12 @@ static void i40e_prep_for_reset(struct i40e_pf *pf) dev_dbg(&pf->pdev->dev, "Tearing down internal switch for reset\n"); /* quiesce the VSIs and their queues that are not already DOWN */ + /* pf_quiesce_all_vsi modifies netdev structures -rtnl_lock needed */ + if (!lock_acquired) + rtnl_lock(); i40e_pf_quiesce_all_vsi(pf); + if (!lock_acquired) + rtnl_unlock(); for (v = 0; v < pf->num_alloc_vsi; v++) { if (pf->vsi[v]) @@ -6926,29 +6940,39 @@ static void i40e_send_version(struct i40e_pf *pf) } /** - * i40e_reset_and_rebuild - reset and rebuild using a saved config + * i40e_reset - wait for core reset to finish reset, reset pf if corer not seen * @pf: board private structure - * @reinit: if the Main VSI needs to re-initialized. **/ -static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) +static int i40e_reset(struct i40e_pf *pf) { struct i40e_hw *hw = &pf->hw; - u8 set_fc_aq_fail = 0; i40e_status ret; - u32 val; - u32 v; - /* Now we wait for GRST to settle out. - * We don't have to delete the VEBs or VSIs from the hw switch - * because the reset will make them disappear. - */ ret = i40e_pf_reset(hw); if (ret) { dev_info(&pf->pdev->dev, "PF reset failed, %d\n", ret); set_bit(__I40E_RESET_FAILED, &pf->state); - goto clear_recovery; + clear_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state); + } else { + pf->pfr_count++; } - pf->pfr_count++; + return ret; +} + +/** + * i40e_rebuild - rebuild using a saved config + * @pf: board private structure + * @reinit: if the Main VSI needs to re-initialized. + * @lock_acquired: indicates whether or not the lock has been acquired + * before this function was called. + **/ +static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired) +{ + struct i40e_hw *hw = &pf->hw; + u8 set_fc_aq_fail = 0; + i40e_status ret; + u32 val; + int v; if (test_bit(__I40E_DOWN, &pf->state)) goto clear_recovery; @@ -6993,9 +7017,11 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) } #endif /* CONFIG_I40E_DCB */ /* do basic switch setup */ + if (!lock_acquired) + rtnl_lock(); ret = i40e_setup_pf_switch(pf, reinit); if (ret) - goto end_core_reset; + goto end_unlock; /* The driver only wants link up/down and module qualification * reports from firmware. Note the negative logic. @@ -7066,7 +7092,7 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) if (ret) { dev_info(&pf->pdev->dev, "rebuild of Main VSI failed: %d\n", ret); - goto end_core_reset; + goto end_unlock; } } @@ -7117,23 +7143,48 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) /* tell the firmware that we're starting */ i40e_send_version(pf); +end_unlock: +if (!lock_acquired) + rtnl_unlock(); end_core_reset: clear_bit(__I40E_RESET_FAILED, &pf->state); clear_recovery: clear_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state); } +/** + * i40e_reset_and_rebuild - reset and rebuild using a saved config + * @pf: board private structure + * @reinit: if the Main VSI needs to re-initialized. + * @lock_acquired: indicates whether or not the lock has been acquired + * before this function was called. + **/ +static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit, + bool lock_acquired) +{ + int ret; + /* Now we wait for GRST to settle out. + * We don't have to delete the VEBs or VSIs from the hw switch + * because the reset will make them disappear. + */ + ret = i40e_reset(pf); + if (!ret) + i40e_rebuild(pf, reinit, lock_acquired); +} + /** * i40e_handle_reset_warning - prep for the PF to reset, reset and rebuild * @pf: board private structure * * Close up the VFs and other things in prep for a Core Reset, * then get ready to rebuild the world. + * @lock_acquired: indicates whether or not the lock has been acquired + * before this function was called. **/ -static void i40e_handle_reset_warning(struct i40e_pf *pf) +static void i40e_handle_reset_warning(struct i40e_pf *pf, bool lock_acquired) { - i40e_prep_for_reset(pf); - i40e_reset_and_rebuild(pf, false); + i40e_prep_for_reset(pf, lock_acquired); + i40e_reset_and_rebuild(pf, false, lock_acquired); } /** @@ -8430,6 +8481,7 @@ static int i40e_pf_config_rss(struct i40e_pf *pf) * * returns 0 if rss is not enabled, if enabled returns the final rss queue * count which may be different from the requested queue count. + * Note: expects to be called while under rtnl_lock() **/ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count) { @@ -8445,11 +8497,11 @@ int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count) u16 qcount; vsi->req_queue_pairs = queue_count; - i40e_prep_for_reset(pf); + i40e_prep_for_reset(pf, true); pf->alloc_rss_size = new_rss_size; - i40e_reset_and_rebuild(pf, true); + i40e_reset_and_rebuild(pf, true, true); /* Discard the user configured hash keys and lut, if less * queues are enabled. @@ -8825,6 +8877,7 @@ static void i40e_clear_rss_lut(struct i40e_vsi *vsi) * i40e_set_features - set the netdev feature flags * @netdev: ptr to the netdev being adjusted * @features: the feature set that the stack is suggesting + * Note: expects to be called while under rtnl_lock() **/ static int i40e_set_features(struct net_device *netdev, netdev_features_t features) @@ -8848,7 +8901,7 @@ static int i40e_set_features(struct net_device *netdev, need_reset = i40e_set_ntuple(pf, features); if (need_reset) - i40e_do_reset(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED)); + i40e_do_reset(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED), true); return 0; } @@ -9043,6 +9096,8 @@ static int i40e_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], * is to change the mode then that requires a PF reset to * allow rebuild of the components with required hardware * bridge mode enabled. + * + * Note: expects to be called while under rtnl_lock() **/ static int i40e_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, @@ -9098,7 +9153,8 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev, pf->flags |= I40E_FLAG_VEB_MODE_ENABLED; else pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED; - i40e_do_reset(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED)); + i40e_do_reset(pf, BIT_ULL(__I40E_PF_RESET_REQUESTED), + true); break; } } @@ -11501,7 +11557,7 @@ static pci_ers_result_t i40e_pci_error_detected(struct pci_dev *pdev, /* shutdown all operations */ if (!test_bit(__I40E_SUSPENDED, &pf->state)) { rtnl_lock(); - i40e_prep_for_reset(pf); + i40e_prep_for_reset(pf, true); rtnl_unlock(); } @@ -11570,7 +11626,7 @@ static void i40e_pci_error_resume(struct pci_dev *pdev) return; rtnl_lock(); - i40e_handle_reset_warning(pf); + i40e_handle_reset_warning(pf, true); rtnl_unlock(); } @@ -11633,7 +11689,7 @@ static void i40e_shutdown(struct pci_dev *pdev) set_bit(__I40E_SUSPENDED, &pf->state); set_bit(__I40E_DOWN, &pf->state); rtnl_lock(); - i40e_prep_for_reset(pf); + i40e_prep_for_reset(pf, true); rtnl_unlock(); wr32(hw, I40E_PFPM_APM, (pf->wol_en ? I40E_PFPM_APM_APME_MASK : 0)); @@ -11652,7 +11708,7 @@ static void i40e_shutdown(struct pci_dev *pdev) i40e_enable_mc_magic_wake(pf); rtnl_lock(); - i40e_prep_for_reset(pf); + i40e_prep_for_reset(pf, true); rtnl_unlock(); wr32(hw, I40E_PFPM_APM, @@ -11686,7 +11742,7 @@ static int i40e_suspend(struct pci_dev *pdev, pm_message_t state) i40e_enable_mc_magic_wake(pf); rtnl_lock(); - i40e_prep_for_reset(pf); + i40e_prep_for_reset(pf, true); rtnl_unlock(); wr32(hw, I40E_PFPM_APM, (pf->wol_en ? I40E_PFPM_APM_APME_MASK : 0)); @@ -11734,7 +11790,7 @@ static int i40e_resume(struct pci_dev *pdev) if (test_and_clear_bit(__I40E_SUSPENDED, &pf->state)) { clear_bit(__I40E_DOWN, &pf->state); rtnl_lock(); - i40e_reset_and_rebuild(pf, false); + i40e_reset_and_rebuild(pf, false, true); rtnl_unlock(); } -- cgit From 17daabb5e8db2b7de742f59dd73aa12550143e0d Mon Sep 17 00:00:00 2001 From: Alan Brady Date: Wed, 5 Apr 2017 07:50:56 -0400 Subject: i40e: Simplify i40e_detect_recover_hung_queue logic This patch greatly reduces the unneeded complexity in the i40e_detect_recover_hung_queue code path. The previous implementation set a 'hung bit' which would then get cleared while polling. If the detection routine was called a second time with the bit already set, we would issue a software interrupt. This patch makes it such that if interrupts are disabled and we have pending TX descriptors, we trigger a software interrupt since in, the worst case, queues are already clean and we have an extra interrupt. Additionally this patch removes the workaround for lost interrupts as calling napi_reschedule in this context can cause software interrupts to fire on the wrong CPU. Change-ID: Iae108582a3ceb6229ed1d22e4ed6e69cf97aad8d Signed-off-by: Alan Brady Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 4 -- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 1 - drivers/net/ethernet/intel/i40e/i40e_main.c | 59 +++++--------------------- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 12 ++---- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 3 +- 5 files changed, 15 insertions(+), 64 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 686327c031fa..110ef4204306 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -617,7 +617,6 @@ struct i40e_vsi { u32 tx_busy; u64 tx_linearize; u64 tx_force_wb; - u64 tx_lost_interrupt; u32 rx_buf_failed; u32 rx_page_failed; @@ -703,9 +702,6 @@ struct i40e_q_vector { u8 num_ringpairs; /* total number of ring pairs in vector */ -#define I40E_Q_VECTOR_HUNG_DETECT 0 /* Bit Index for hung detection logic */ - unsigned long hung_detected; /* Set/Reset for hung_detection logic */ - cpumask_t affinity_mask; struct irq_affinity_notify affinity_notify; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 68c0f204f93e..10325b5a9805 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -89,7 +89,6 @@ static const struct i40e_stats i40e_gstrings_misc_stats[] = { I40E_VSI_STAT("rx_unknown_protocol", eth_stats.rx_unknown_protocol), I40E_VSI_STAT("tx_linearize", tx_linearize), I40E_VSI_STAT("tx_force_wb", tx_force_wb), - I40E_VSI_STAT("tx_lost_interrupt", tx_lost_interrupt), I40E_VSI_STAT("rx_alloc_fail", rx_buf_failed), I40E_VSI_STAT("rx_pg_alloc_fail", rx_page_failed), }; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 8181647f512e..22831a4a9099 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -737,7 +737,6 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) struct i40e_eth_stats *oes; struct i40e_eth_stats *es; /* device's eth stats */ u32 tx_restart, tx_busy; - u64 tx_lost_interrupt; struct i40e_ring *p; u32 rx_page, rx_buf; u64 bytes, packets; @@ -763,7 +762,6 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) rx_b = rx_p = 0; tx_b = tx_p = 0; tx_restart = tx_busy = tx_linearize = tx_force_wb = 0; - tx_lost_interrupt = 0; rx_page = 0; rx_buf = 0; rcu_read_lock(); @@ -782,7 +780,6 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) tx_busy += p->tx_stats.tx_busy; tx_linearize += p->tx_stats.tx_linearize; tx_force_wb += p->tx_stats.tx_force_wb; - tx_lost_interrupt += p->tx_stats.tx_lost_interrupt; /* Rx queue is part of the same block as Tx queue */ p = &p[1]; @@ -801,7 +798,6 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) vsi->tx_busy = tx_busy; vsi->tx_linearize = tx_linearize; vsi->tx_force_wb = tx_force_wb; - vsi->tx_lost_interrupt = tx_lost_interrupt; vsi->rx_page_failed = rx_page; vsi->rx_buf_failed = rx_buf; @@ -4508,16 +4504,15 @@ static int i40e_pf_wait_queues_disabled(struct i40e_pf *pf) * @vsi: Pointer to VSI struct * * This function checks specified queue for given VSI. Detects hung condition. - * Sets hung bit since it is two step process. Before next run of service task - * if napi_poll runs, it reset 'hung' bit for respective q_vector. If not, - * hung condition remain unchanged and during subsequent run, this function - * issues SW interrupt to recover from hung condition. + * We proactively detect hung TX queues by checking if interrupts are disabled + * but there are pending descriptors. If it appears hung, attempt to recover + * by triggering a SW interrupt. **/ static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi) { struct i40e_ring *tx_ring = NULL; struct i40e_pf *pf; - u32 head, val, tx_pending_hw; + u32 val, tx_pending; int i; pf = vsi->back; @@ -4543,47 +4538,15 @@ static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi) else val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0); - head = i40e_get_head(tx_ring); + tx_pending = i40e_get_tx_pending(tx_ring); - tx_pending_hw = i40e_get_tx_pending(tx_ring, false); - - /* HW is done executing descriptors, updated HEAD write back, - * but SW hasn't processed those descriptors. If interrupt is - * not generated from this point ON, it could result into - * dev_watchdog detecting timeout on those netdev_queue, - * hence proactively trigger SW interrupt. + /* Interrupts are disabled and TX pending is non-zero, + * trigger the SW interrupt (don't wait). Worst case + * there will be one extra interrupt which may result + * into not cleaning any queues because queues are cleaned. */ - if (tx_pending_hw && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))) { - /* NAPI Poll didn't run and clear since it was set */ - if (test_and_clear_bit(I40E_Q_VECTOR_HUNG_DETECT, - &tx_ring->q_vector->hung_detected)) { - netdev_info(vsi->netdev, "VSI_seid %d, Hung TX queue %d, tx_pending_hw: %d, NTC:0x%x, HWB: 0x%x, NTU: 0x%x, TAIL: 0x%x\n", - vsi->seid, q_idx, tx_pending_hw, - tx_ring->next_to_clean, head, - tx_ring->next_to_use, - readl(tx_ring->tail)); - netdev_info(vsi->netdev, "VSI_seid %d, Issuing force_wb for TX queue %d, Interrupt Reg: 0x%x\n", - vsi->seid, q_idx, val); - i40e_force_wb(vsi, tx_ring->q_vector); - } else { - /* First Chance - detected possible hung */ - set_bit(I40E_Q_VECTOR_HUNG_DETECT, - &tx_ring->q_vector->hung_detected); - } - } - - /* This is the case where we have interrupts missing, - * so the tx_pending in HW will most likely be 0, but we - * will have tx_pending in SW since the WB happened but the - * interrupt got lost. - */ - if ((!tx_pending_hw) && i40e_get_tx_pending(tx_ring, true) && - (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))) { - local_bh_disable(); - if (napi_reschedule(&tx_ring->q_vector->napi)) - tx_ring->tx_stats.tx_lost_interrupt++; - local_bh_enable(); - } + if (tx_pending && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))) + i40e_force_wb(vsi, tx_ring->q_vector); } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index a9a97dd2c0a5..e95428c7aba0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -711,19 +711,15 @@ void i40e_free_tx_resources(struct i40e_ring *tx_ring) /** * i40e_get_tx_pending - how many tx descriptors not processed * @tx_ring: the ring of descriptors - * @in_sw: is tx_pending being checked in SW or HW * * Since there is no access to the ring head register * in XL710, we need to use our local copies **/ -u32 i40e_get_tx_pending(struct i40e_ring *ring, bool in_sw) +u32 i40e_get_tx_pending(struct i40e_ring *ring) { u32 head, tail; - if (!in_sw) - head = i40e_get_head(ring); - else - head = ring->next_to_clean; + head = i40e_get_head(ring); tail = readl(ring->tail); if (head != tail) @@ -846,7 +842,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, * them to be written back in case we stay in NAPI. * In this mode on X722 we do not enable Interrupt. */ - unsigned int j = i40e_get_tx_pending(tx_ring, false); + unsigned int j = i40e_get_tx_pending(tx_ring); if (budget && ((j / WB_STRIDE) == 0) && (j > 0) && @@ -2126,8 +2122,6 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) return 0; } - /* Clear hung_detected bit */ - clear_bit(I40E_Q_VECTOR_HUNG_DETECT, &q_vector->hung_detected); /* Since the actual Tx work is minimal, we can give the Tx a larger * budget and be more aggressive about cleaning up the Tx descriptors. */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index d6609deace57..bc66ec4954d9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -275,7 +275,6 @@ struct i40e_tx_queue_stats { u64 tx_done_old; u64 tx_linearize; u64 tx_force_wb; - u64 tx_lost_interrupt; }; struct i40e_rx_queue_stats { @@ -400,7 +399,7 @@ void i40e_free_tx_resources(struct i40e_ring *tx_ring); void i40e_free_rx_resources(struct i40e_ring *rx_ring); int i40e_napi_poll(struct napi_struct *napi, int budget); void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector); -u32 i40e_get_tx_pending(struct i40e_ring *ring, bool in_sw); +u32 i40e_get_tx_pending(struct i40e_ring *ring); int __i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size); bool __i40e_chk_linearize(struct sk_buff *skb); -- cgit From 41c4c2b50d52346bdc994bd0e0c92f07bb1bb1f8 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Apr 2017 07:50:57 -0400 Subject: i40e: allow look-up of MAC address from Open Firmware or IDPROM Look up the MAC address from the eth_get_platform_mac_address() function first before checking what the firmware provides. We already handle the case of re-writing the MAC-VLAN filter, so there is no need to add extra code for this. However, update the comment where we do this to indicate that it does impact the Open Firmware MAC address case. Change-ID: I73e59fbe0b0e7e6f3ee9f5170d0bd3a4d5faf4db Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 22831a4a9099..2111f120865a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -9326,10 +9326,15 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) if (vsi->type == I40E_VSI_MAIN) { SET_NETDEV_DEV(netdev, &pf->pdev->dev); ether_addr_copy(mac_addr, hw->mac.perm_addr); - /* The following steps are necessary to properly keep track of - * MAC-VLAN filters loaded into firmware - first we remove - * filter that is automatically generated by firmware and then - * add new filter both to the driver hash table and firmware. + /* The following steps are necessary for two reasons. First, + * some older NVM configurations load a default MAC-VLAN + * filter that will accept any tagged packet, and we want to + * replace this with a normal filter. Additionally, it is + * possible our MAC address was provided by the platform using + * Open Firmware or similar. + * + * Thus, we need to remove the default filter and install one + * specific to the MAC address. */ i40e_rm_default_mac_filter(vsi, mac_addr); spin_lock_bh(&vsi->mac_filter_hash_lock); @@ -10834,20 +10839,18 @@ static void i40e_print_features(struct i40e_pf *pf) /** * i40e_get_platform_mac_addr - get platform-specific MAC address - * * @pdev: PCI device information struct * @pf: board private structure * - * Look up the MAC address in Open Firmware on systems that support it, - * and use IDPROM on SPARC if no OF address is found. On return, the - * I40E_FLAG_PF_MAC will be wset in pf->flags if a platform-specific value - * has been selected. + * Look up the MAC address for the device. First we'll try + * eth_platform_get_mac_address, which will check Open Firmware, or arch + * specific fallback. Otherwise, we'll default to the stored value in + * firmware. **/ static void i40e_get_platform_mac_addr(struct pci_dev *pdev, struct i40e_pf *pf) { - pf->flags &= ~I40E_FLAG_PF_MAC; - if (!eth_platform_get_mac_address(&pdev->dev, pf->hw.mac.addr)) - pf->flags |= I40E_FLAG_PF_MAC; + if (eth_platform_get_mac_address(&pdev->dev, pf->hw.mac.addr)) + i40e_get_mac_addr(&pf->hw, pf->hw.mac.addr); } /** @@ -11061,9 +11064,9 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) i40e_aq_stop_lldp(hw, true, NULL); } - i40e_get_mac_addr(hw, hw->mac.addr); /* allow a platform config to override the HW addr */ i40e_get_platform_mac_addr(pdev, pf); + if (!is_valid_ether_addr(hw->mac.addr)) { dev_info(&pdev->dev, "invalid MAC address %pM\n", hw->mac.addr); err = -EIO; -- cgit From af26ce2dfbf269a9608008b33a7ff978e2a7b9a9 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Apr 2017 07:50:58 -0400 Subject: i40e: remove extraneous loop in i40e_vsi_wait_queues_disabled We can simply check both Tx and Rx queues in a single loop, rather than repeating the loop twice. Change-ID: Ic06f26b0e3c2620e0e33c1a2999edda488e647ad Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2111f120865a..0e1240b704ef 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4440,7 +4440,7 @@ static void i40e_pf_unquiesce_all_vsi(struct i40e_pf *pf) * i40e_vsi_wait_queues_disabled - Wait for VSI's queues to be disabled * @vsi: the VSI being configured * - * This function waits for the given VSI's queues to be disabled. + * Wait until all queues on a given VSI have been disabled. **/ static int i40e_vsi_wait_queues_disabled(struct i40e_vsi *vsi) { @@ -4449,7 +4449,7 @@ static int i40e_vsi_wait_queues_disabled(struct i40e_vsi *vsi) pf_q = vsi->base_queue; for (i = 0; i < vsi->num_queue_pairs; i++, pf_q++) { - /* Check and wait for the disable status of the queue */ + /* Check and wait for the Tx queue */ ret = i40e_pf_txq_wait(pf, pf_q, false); if (ret) { dev_info(&pf->pdev->dev, @@ -4457,11 +4457,7 @@ static int i40e_vsi_wait_queues_disabled(struct i40e_vsi *vsi) vsi->seid, pf_q); return ret; } - } - - pf_q = vsi->base_queue; - for (i = 0; i < vsi->num_queue_pairs; i++, pf_q++) { - /* Check and wait for the disable status of the queue */ + /* Check and wait for the Tx queue */ ret = i40e_pf_rxq_wait(pf, pf_q, false); if (ret) { dev_info(&pf->pdev->dev, -- cgit From 78786d4a59a12e8d9a0b38ad300f7ebe2aeca8a2 Mon Sep 17 00:00:00 2001 From: Alice Michael Date: Fri, 7 Apr 2017 23:01:35 -0700 Subject: i40e: remove I40E_FLAG_NEED_LINK_UPDATE The I40E_FLAG_NEED_LINK_UPDATE was never used. Remove the flag definitions. Change-ID: If59d0c6b4af85ca27281f3183c54b055adb439a4 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 - drivers/net/ethernet/intel/i40e/i40e_main.c | 1 - drivers/net/ethernet/intel/i40evf/i40evf.h | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 110ef4204306..ef64dc60f857 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -389,7 +389,6 @@ struct i40e_pf { #define I40E_FLAG_MSIX_ENABLED BIT_ULL(3) #define I40E_FLAG_RSS_ENABLED BIT_ULL(6) #define I40E_FLAG_VMDQ_ENABLED BIT_ULL(7) -#define I40E_FLAG_NEED_LINK_UPDATE BIT_ULL(9) #define I40E_FLAG_IWARP_ENABLED BIT_ULL(10) #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 0e1240b704ef..3b1dab7a7cc9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -11091,7 +11091,6 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) INIT_WORK(&pf->service_task, i40e_service_task); clear_bit(__I40E_SERVICE_SCHED, &pf->state); - pf->flags |= I40E_FLAG_NEED_LINK_UPDATE; /* NVM bit on means WoL disabled for the port */ i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index d61ecf655091..35ded19e9cc2 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -205,7 +205,6 @@ struct i40evf_adapter { #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) #define I40EVF_FLAG_PF_COMMS_FAILED BIT(8) #define I40EVF_FLAG_RESET_PENDING BIT(9) #define I40EVF_FLAG_RESET_NEEDED BIT(10) -- cgit From 33512191fee4bb8a154a389ee6087272e8fd898d Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 5 Apr 2017 07:51:00 -0400 Subject: i40e: clean up historic deprecated flag definitions Since an early commit a few flags have no longer been used. Remove these definitions to reduce code clutter. Change-ID: I3589be4622574e747013cd4dc403e18b039f4965 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index ef64dc60f857..e987503f8517 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -390,11 +390,8 @@ struct i40e_pf { #define I40E_FLAG_RSS_ENABLED BIT_ULL(6) #define I40E_FLAG_VMDQ_ENABLED BIT_ULL(7) #define I40E_FLAG_IWARP_ENABLED BIT_ULL(10) -#define I40E_FLAG_CLEAN_ADMINQ BIT_ULL(14) #define I40E_FLAG_FILTER_SYNC BIT_ULL(15) #define I40E_FLAG_SERVICE_CLIENT_REQUESTED BIT_ULL(16) -#define I40E_FLAG_PROCESS_MDD_EVENT BIT_ULL(17) -#define I40E_FLAG_PROCESS_VFLR_EVENT BIT_ULL(18) #define I40E_FLAG_SRIOV_ENABLED BIT_ULL(19) #define I40E_FLAG_DCB_ENABLED BIT_ULL(20) #define I40E_FLAG_FD_SB_ENABLED BIT_ULL(21) -- cgit From 98efd69493b9d4b02353a552af8ffaaf30de8af4 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 5 Apr 2017 07:51:01 -0400 Subject: i40e/i40evf: Add support for using order 1 pages with a 3K buffer There are situations where adding padding to the front and back of an Rx buffer will require that we add additional padding. Specifically if NET_IP_ALIGN is non-zero, or the MTU size is larger than 7.5K we would need to use 2K buffers which leaves us with no room for the padding. To preemptively address these cases I am adding support for 3K buffers to the Rx path so that we can provide the additional padding needed in the event of NET_IP_ALIGN being non-zero or a cache line being greater than 64. Change-ID: I938bc1ba611285428df39a613cd66f98e60b55c7 Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 3 ++- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 27 +++++++++++++------------ drivers/net/ethernet/intel/i40e/i40e_txrx.h | 12 +++++++++++ drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 27 +++++++++++++------------ drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 12 +++++++++++ drivers/net/ethernet/intel/i40evf/i40evf_main.c | 6 ++++++ 6 files changed, 60 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 3b1dab7a7cc9..97489d69029a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3085,7 +3085,8 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi) #endif } else { vsi->max_frame = I40E_MAX_RXBUFFER; - vsi->rx_buf_len = I40E_RXBUFFER_2048; + vsi->rx_buf_len = (PAGE_SIZE < 8192) ? I40E_RXBUFFER_3072 : + I40E_RXBUFFER_2048; } /* set up individual rings */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index e95428c7aba0..bee16726c104 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1138,14 +1138,15 @@ void i40e_clean_rx_ring(struct i40e_ring *rx_ring) dma_sync_single_range_for_cpu(rx_ring->dev, rx_bi->dma, rx_bi->page_offset, - I40E_RXBUFFER_2048, + rx_ring->rx_buf_len, DMA_FROM_DEVICE); /* free resources associated with mapping */ dma_unmap_page_attrs(rx_ring->dev, rx_bi->dma, - PAGE_SIZE, + i40e_rx_pg_size(rx_ring), DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); + __page_frag_cache_drain(rx_bi->page, rx_bi->pagecnt_bias); rx_bi->page = NULL; @@ -1267,7 +1268,7 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, } /* alloc new page for storage */ - page = dev_alloc_page(); + page = dev_alloc_pages(i40e_rx_pg_order(rx_ring)); if (unlikely(!page)) { rx_ring->rx_stats.alloc_page_failed++; return false; @@ -1275,7 +1276,7 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, /* map page for use */ dma = dma_map_page_attrs(rx_ring->dev, page, 0, - PAGE_SIZE, + i40e_rx_pg_size(rx_ring), DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); @@ -1283,7 +1284,7 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, * there isn't much point in holding memory we can't use */ if (dma_mapping_error(rx_ring->dev, dma)) { - __free_pages(page, 0); + __free_pages(page, i40e_rx_pg_order(rx_ring)); rx_ring->rx_stats.alloc_page_failed++; return false; } @@ -1343,7 +1344,7 @@ bool i40e_alloc_rx_buffers(struct i40e_ring *rx_ring, u16 cleaned_count) /* sync the buffer for use by the device */ dma_sync_single_range_for_device(rx_ring->dev, bi->dma, bi->page_offset, - I40E_RXBUFFER_2048, + rx_ring->rx_buf_len, DMA_FROM_DEVICE); /* Refresh the desc even if buffer_addrs didn't change @@ -1645,9 +1646,6 @@ static inline bool i40e_page_is_reusable(struct page *page) **/ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer) { -#if (PAGE_SIZE >= 8192) - unsigned int last_offset = PAGE_SIZE - I40E_RXBUFFER_2048; -#endif unsigned int pagecnt_bias = rx_buffer->pagecnt_bias; struct page *page = rx_buffer->page; @@ -1660,7 +1658,9 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer) if (unlikely((page_count(page) - pagecnt_bias) > 1)) return false; #else - if (rx_buffer->page_offset > last_offset) +#define I40E_LAST_OFFSET \ + (SKB_WITH_OVERHEAD(PAGE_SIZE) - I40E_RXBUFFER_2048) + if (rx_buffer->page_offset > I40E_LAST_OFFSET) return false; #endif @@ -1694,7 +1694,7 @@ static void i40e_add_rx_frag(struct i40e_ring *rx_ring, unsigned int size) { #if (PAGE_SIZE < 8192) - unsigned int truesize = I40E_RXBUFFER_2048; + unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; #else unsigned int truesize = SKB_DATA_ALIGN(size); #endif @@ -1755,7 +1755,7 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, { void *va = page_address(rx_buffer->page) + rx_buffer->page_offset; #if (PAGE_SIZE < 8192) - unsigned int truesize = I40E_RXBUFFER_2048; + unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; #else unsigned int truesize = SKB_DATA_ALIGN(size); #endif @@ -1821,7 +1821,8 @@ static void i40e_put_rx_buffer(struct i40e_ring *rx_ring, rx_ring->rx_stats.page_reuse_count++; } else { /* we are not reusing the buffer so unmap it */ - dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, PAGE_SIZE, + dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, + i40e_rx_pg_size(rx_ring), DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); __page_frag_cache_drain(rx_buffer->page, rx_buffer->pagecnt_bias); diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index bc66ec4954d9..2f618539a436 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -119,6 +119,7 @@ enum i40e_dyn_idx_t { #define I40E_RXBUFFER_256 256 #define I40E_RXBUFFER_1536 1536 /* 128B aligned standard Ethernet frame */ #define I40E_RXBUFFER_2048 2048 +#define I40E_RXBUFFER_3072 3072 /* Used for large frames w/ padding */ #define I40E_MAX_RXBUFFER 9728 /* largest size for single descriptor */ /* NOTE: netdev_alloc_skb reserves up to 64 bytes, NET_IP_ALIGN means we @@ -389,6 +390,17 @@ struct i40e_ring_container { #define i40e_for_each_ring(pos, head) \ for (pos = (head).ring; pos != NULL; pos = pos->next) +static inline unsigned int i40e_rx_pg_order(struct i40e_ring *ring) +{ +#if (PAGE_SIZE < 8192) + if (ring->rx_buf_len > (PAGE_SIZE / 2)) + return 1; +#endif + return 0; +} + +#define i40e_rx_pg_size(_ring) (PAGE_SIZE << i40e_rx_pg_order(_ring)) + bool i40e_alloc_rx_buffers(struct i40e_ring *rxr, u16 cleaned_count); netdev_tx_t i40e_lan_xmit_frame(struct sk_buff *skb, struct net_device *netdev); void i40e_clean_tx_ring(struct i40e_ring *tx_ring); diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 95e383af41c4..6b60c19a794b 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -509,14 +509,15 @@ void i40evf_clean_rx_ring(struct i40e_ring *rx_ring) dma_sync_single_range_for_cpu(rx_ring->dev, rx_bi->dma, rx_bi->page_offset, - I40E_RXBUFFER_2048, + rx_ring->rx_buf_len, DMA_FROM_DEVICE); /* free resources associated with mapping */ dma_unmap_page_attrs(rx_ring->dev, rx_bi->dma, - PAGE_SIZE, + i40e_rx_pg_size(rx_ring), DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); + __page_frag_cache_drain(rx_bi->page, rx_bi->pagecnt_bias); rx_bi->page = NULL; @@ -638,7 +639,7 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, } /* alloc new page for storage */ - page = dev_alloc_page(); + page = dev_alloc_pages(i40e_rx_pg_order(rx_ring)); if (unlikely(!page)) { rx_ring->rx_stats.alloc_page_failed++; return false; @@ -646,7 +647,7 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, /* map page for use */ dma = dma_map_page_attrs(rx_ring->dev, page, 0, - PAGE_SIZE, + i40e_rx_pg_size(rx_ring), DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); @@ -654,7 +655,7 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, * there isn't much point in holding memory we can't use */ if (dma_mapping_error(rx_ring->dev, dma)) { - __free_pages(page, 0); + __free_pages(page, i40e_rx_pg_order(rx_ring)); rx_ring->rx_stats.alloc_page_failed++; return false; } @@ -714,7 +715,7 @@ bool i40evf_alloc_rx_buffers(struct i40e_ring *rx_ring, u16 cleaned_count) /* sync the buffer for use by the device */ dma_sync_single_range_for_device(rx_ring->dev, bi->dma, bi->page_offset, - I40E_RXBUFFER_2048, + rx_ring->rx_buf_len, DMA_FROM_DEVICE); /* Refresh the desc even if buffer_addrs didn't change @@ -1006,9 +1007,6 @@ static inline bool i40e_page_is_reusable(struct page *page) **/ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer) { -#if (PAGE_SIZE >= 8192) - unsigned int last_offset = PAGE_SIZE - I40E_RXBUFFER_2048; -#endif unsigned int pagecnt_bias = rx_buffer->pagecnt_bias; struct page *page = rx_buffer->page; @@ -1021,7 +1019,9 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer) if (unlikely((page_count(page) - pagecnt_bias) > 1)) return false; #else - if (rx_buffer->page_offset > last_offset) +#define I40E_LAST_OFFSET \ + (SKB_WITH_OVERHEAD(PAGE_SIZE) - I40E_RXBUFFER_2048) + if (rx_buffer->page_offset > I40E_LAST_OFFSET) return false; #endif @@ -1055,7 +1055,7 @@ static void i40e_add_rx_frag(struct i40e_ring *rx_ring, unsigned int size) { #if (PAGE_SIZE < 8192) - unsigned int truesize = I40E_RXBUFFER_2048; + unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; #else unsigned int truesize = SKB_DATA_ALIGN(size); #endif @@ -1116,7 +1116,7 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, { void *va = page_address(rx_buffer->page) + rx_buffer->page_offset; #if (PAGE_SIZE < 8192) - unsigned int truesize = I40E_RXBUFFER_2048; + unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; #else unsigned int truesize = SKB_DATA_ALIGN(size); #endif @@ -1182,7 +1182,8 @@ static void i40e_put_rx_buffer(struct i40e_ring *rx_ring, rx_ring->rx_stats.page_reuse_count++; } else { /* we are not reusing the buffer so unmap it */ - dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, PAGE_SIZE, + dma_unmap_page_attrs(rx_ring->dev, rx_buffer->dma, + i40e_rx_pg_size(rx_ring), DMA_FROM_DEVICE, I40E_RX_DMA_ATTR); __page_frag_cache_drain(rx_buffer->page, rx_buffer->pagecnt_bias); diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 3bb4d732e467..dc82f65267ec 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -106,6 +106,7 @@ enum i40e_dyn_idx_t { #define I40E_RXBUFFER_256 256 #define I40E_RXBUFFER_1536 1536 /* 128B aligned standard Ethernet frame */ #define I40E_RXBUFFER_2048 2048 +#define I40E_RXBUFFER_3072 3072 /* Used for large frames w/ padding */ #define I40E_MAX_RXBUFFER 9728 /* largest size for single descriptor */ /* NOTE: netdev_alloc_skb reserves up to 64 bytes, NET_IP_ALIGN means we @@ -376,6 +377,17 @@ struct i40e_ring_container { #define i40e_for_each_ring(pos, head) \ for (pos = (head).ring; pos != NULL; pos = pos->next) +static inline unsigned int i40e_rx_pg_order(struct i40e_ring *ring) +{ +#if (PAGE_SIZE < 8192) + if (ring->rx_buf_len > (PAGE_SIZE / 2)) + return 1; +#endif + return 0; +} + +#define i40e_rx_pg_size(_ring) (PAGE_SIZE << i40e_rx_pg_order(_ring)) + bool i40evf_alloc_rx_buffers(struct i40e_ring *rxr, u16 cleaned_count); netdev_tx_t i40evf_xmit_frame(struct sk_buff *skb, struct net_device *netdev); void i40evf_clean_tx_ring(struct i40e_ring *tx_ring); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index c690aba8e8d1..7d00abae6104 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -694,6 +694,12 @@ static void i40evf_configure_rx(struct i40evf_adapter *adapter) /* Legacy Rx will always default to a 2048 buffer size. */ #if (PAGE_SIZE < 8192) if (!(adapter->flags & I40EVF_FLAG_LEGACY_RX)) { + /* For jumbo frames on systems with 4K pages we have to use + * an order 1 page, so we might as well increase the size + * of our Rx buffer to make better use of the available space + */ + rx_buf_len = I40E_RXBUFFER_3072; + /* We use a 1536 buffer size for configurations with * standard Ethernet mtu. On x86 this gives us enough room * for shared info and 192 bytes of padding. -- cgit From ca9ec0888d631c446040a7fab9985afdeb4f73f3 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 5 Apr 2017 07:51:02 -0400 Subject: i40e/i40evf: Add support for padding start of frames This patch adds padding to the start of frames to make room for headroom for us to eventually start using build_skb. Right now we guarantee at least NET_SKB_PAD + NET_IP_ALIGN, however we allocate more space if more is available. For example on x86 the headroom should be 192 bytes. On systems that have too large of a cache line size to support storing 1.5K padding and shared info we default to using 3K buffers and reserve everything that isn't used for skb_shared_info or the data buffer for headroom. Change-ID: I33c641c9a1ea10cf7cc484c2d20985368d2d709a Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 9 +++- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 15 +++++- drivers/net/ethernet/intel/i40e/i40e_txrx.h | 70 ++++++++++++++++++++++++- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 15 +++++- drivers/net/ethernet/intel/i40evf/i40e_txrx.h | 70 ++++++++++++++++++++++++- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 8 ++- 6 files changed, 179 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 97489d69029a..b6ec9beeebff 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3038,6 +3038,12 @@ static int i40e_configure_rx_ring(struct i40e_ring *ring) return -ENOMEM; } + /* configure Rx buffer alignment */ + if (!vsi->netdev || (vsi->back->flags & I40E_FLAG_LEGACY_RX)) + clear_ring_build_skb_enabled(ring); + else + set_ring_build_skb_enabled(ring); + /* cache tail for quicker writes, and clear the reg before use */ ring->tail = hw->hw_addr + I40E_QRX_TAIL(pf_q); writel(0, ring->tail); @@ -3079,7 +3085,8 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi) vsi->max_frame = I40E_MAX_RXBUFFER; vsi->rx_buf_len = I40E_RXBUFFER_2048; #if (PAGE_SIZE < 8192) - } else if (vsi->netdev->mtu <= ETH_DATA_LEN) { + } else if (!I40E_2K_TOO_SMALL_WITH_PADDING && + (vsi->netdev->mtu <= ETH_DATA_LEN)) { vsi->max_frame = I40E_RXBUFFER_1536 - NET_IP_ALIGN; vsi->rx_buf_len = I40E_RXBUFFER_1536 - NET_IP_ALIGN; #endif diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index bee16726c104..f15e1bcf3555 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1247,6 +1247,17 @@ static inline void i40e_release_rx_desc(struct i40e_ring *rx_ring, u32 val) writel(val, rx_ring->tail); } +/** + * i40e_rx_offset - Return expected offset into page to access data + * @rx_ring: Ring we are requesting offset of + * + * Returns the offset value for ring into the data buffer. + */ +static inline unsigned int i40e_rx_offset(struct i40e_ring *rx_ring) +{ + return ring_uses_build_skb(rx_ring) ? I40E_SKB_PAD : 0; +} + /** * i40e_alloc_mapped_page - recycle or make a new page * @rx_ring: ring to use @@ -1291,7 +1302,7 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, bi->dma = dma; bi->page = page; - bi->page_offset = 0; + bi->page_offset = i40e_rx_offset(rx_ring); /* initialize pagecnt_bias to 1 representing we fully own page */ bi->pagecnt_bias = 1; @@ -1696,7 +1707,7 @@ static void i40e_add_rx_frag(struct i40e_ring *rx_ring, #if (PAGE_SIZE < 8192) unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; #else - unsigned int truesize = SKB_DATA_ALIGN(size); + unsigned int truesize = SKB_DATA_ALIGN(size + i40e_rx_offset(rx_ring)); #endif skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index 2f618539a436..f5de51124cae 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -135,6 +135,58 @@ enum i40e_dyn_idx_t { #define I40E_RX_DMA_ATTR \ (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) +/* Attempt to maximize the headroom available for incoming frames. We + * use a 2K buffer for receives and need 1536/1534 to store the data for + * the frame. This leaves us with 512 bytes of room. From that we need + * to deduct the space needed for the shared info and the padding needed + * to IP align the frame. + * + * Note: For cache line sizes 256 or larger this value is going to end + * up negative. In these cases we should fall back to the legacy + * receive path. + */ +#if (PAGE_SIZE < 8192) +#define I40E_2K_TOO_SMALL_WITH_PADDING \ +((NET_SKB_PAD + I40E_RXBUFFER_1536) > SKB_WITH_OVERHEAD(I40E_RXBUFFER_2048)) + +static inline int i40e_compute_pad(int rx_buf_len) +{ + int page_size, pad_size; + + page_size = ALIGN(rx_buf_len, PAGE_SIZE / 2); + pad_size = SKB_WITH_OVERHEAD(page_size) - rx_buf_len; + + return pad_size; +} + +static inline int i40e_skb_pad(void) +{ + int rx_buf_len; + + /* If a 2K buffer cannot handle a standard Ethernet frame then + * optimize padding for a 3K buffer instead of a 1.5K buffer. + * + * For a 3K buffer we need to add enough padding to allow for + * tailroom due to NET_IP_ALIGN possibly shifting us out of + * cache-line alignment. + */ + if (I40E_2K_TOO_SMALL_WITH_PADDING) + rx_buf_len = I40E_RXBUFFER_3072 + SKB_DATA_ALIGN(NET_IP_ALIGN); + else + rx_buf_len = I40E_RXBUFFER_1536; + + /* if needed make room for NET_IP_ALIGN */ + rx_buf_len -= NET_IP_ALIGN; + + return i40e_compute_pad(rx_buf_len); +} + +#define I40E_SKB_PAD i40e_skb_pad() +#else +#define I40E_2K_TOO_SMALL_WITH_PADDING false +#define I40E_SKB_PAD (NET_SKB_PAD + NET_IP_ALIGN) +#endif + /** * i40e_test_staterr - tests bits in Rx descriptor status and error fields * @rx_desc: pointer to receive descriptor (in le64 format) @@ -341,7 +393,8 @@ struct i40e_ring { u8 packet_stride; u16 flags; -#define I40E_TXR_FLAGS_WB_ON_ITR BIT(0) +#define I40E_TXR_FLAGS_WB_ON_ITR BIT(0) +#define I40E_RXR_FLAGS_BUILD_SKB_ENABLED BIT(1) /* stats structs */ struct i40e_queue_stats stats; @@ -369,6 +422,21 @@ struct i40e_ring { */ } ____cacheline_internodealigned_in_smp; +static inline bool ring_uses_build_skb(struct i40e_ring *ring) +{ + return !!(ring->flags & I40E_RXR_FLAGS_BUILD_SKB_ENABLED); +} + +static inline void set_ring_build_skb_enabled(struct i40e_ring *ring) +{ + ring->flags |= I40E_RXR_FLAGS_BUILD_SKB_ENABLED; +} + +static inline void clear_ring_build_skb_enabled(struct i40e_ring *ring) +{ + ring->flags &= ~I40E_RXR_FLAGS_BUILD_SKB_ENABLED; +} + enum i40e_latency_range { I40E_LOWEST_LATENCY = 0, I40E_LOW_LATENCY = 1, diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 6b60c19a794b..a71f81a9291f 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -618,6 +618,17 @@ static inline void i40e_release_rx_desc(struct i40e_ring *rx_ring, u32 val) writel(val, rx_ring->tail); } +/** + * i40e_rx_offset - Return expected offset into page to access data + * @rx_ring: Ring we are requesting offset of + * + * Returns the offset value for ring into the data buffer. + */ +static inline unsigned int i40e_rx_offset(struct i40e_ring *rx_ring) +{ + return ring_uses_build_skb(rx_ring) ? I40E_SKB_PAD : 0; +} + /** * i40e_alloc_mapped_page - recycle or make a new page * @rx_ring: ring to use @@ -662,7 +673,7 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring, bi->dma = dma; bi->page = page; - bi->page_offset = 0; + bi->page_offset = i40e_rx_offset(rx_ring); /* initialize pagecnt_bias to 1 representing we fully own page */ bi->pagecnt_bias = 1; @@ -1057,7 +1068,7 @@ static void i40e_add_rx_frag(struct i40e_ring *rx_ring, #if (PAGE_SIZE < 8192) unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; #else - unsigned int truesize = SKB_DATA_ALIGN(size); + unsigned int truesize = SKB_DATA_ALIGN(size + i40e_rx_offset(rx_ring)); #endif skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index dc82f65267ec..901282c87cf6 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -122,6 +122,58 @@ enum i40e_dyn_idx_t { #define I40E_RX_DMA_ATTR \ (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) +/* Attempt to maximize the headroom available for incoming frames. We + * use a 2K buffer for receives and need 1536/1534 to store the data for + * the frame. This leaves us with 512 bytes of room. From that we need + * to deduct the space needed for the shared info and the padding needed + * to IP align the frame. + * + * Note: For cache line sizes 256 or larger this value is going to end + * up negative. In these cases we should fall back to the legacy + * receive path. + */ +#if (PAGE_SIZE < 8192) +#define I40E_2K_TOO_SMALL_WITH_PADDING \ +((NET_SKB_PAD + I40E_RXBUFFER_1536) > SKB_WITH_OVERHEAD(I40E_RXBUFFER_2048)) + +static inline int i40e_compute_pad(int rx_buf_len) +{ + int page_size, pad_size; + + page_size = ALIGN(rx_buf_len, PAGE_SIZE / 2); + pad_size = SKB_WITH_OVERHEAD(page_size) - rx_buf_len; + + return pad_size; +} + +static inline int i40e_skb_pad(void) +{ + int rx_buf_len; + + /* If a 2K buffer cannot handle a standard Ethernet frame then + * optimize padding for a 3K buffer instead of a 1.5K buffer. + * + * For a 3K buffer we need to add enough padding to allow for + * tailroom due to NET_IP_ALIGN possibly shifting us out of + * cache-line alignment. + */ + if (I40E_2K_TOO_SMALL_WITH_PADDING) + rx_buf_len = I40E_RXBUFFER_3072 + SKB_DATA_ALIGN(NET_IP_ALIGN); + else + rx_buf_len = I40E_RXBUFFER_1536; + + /* if needed make room for NET_IP_ALIGN */ + rx_buf_len -= NET_IP_ALIGN; + + return i40e_compute_pad(rx_buf_len); +} + +#define I40E_SKB_PAD i40e_skb_pad() +#else +#define I40E_2K_TOO_SMALL_WITH_PADDING false +#define I40E_SKB_PAD (NET_SKB_PAD + NET_IP_ALIGN) +#endif + /** * i40e_test_staterr - tests bits in Rx descriptor status and error fields * @rx_desc: pointer to receive descriptor (in le64 format) @@ -328,7 +380,8 @@ struct i40e_ring { u8 packet_stride; u16 flags; -#define I40E_TXR_FLAGS_WB_ON_ITR BIT(0) +#define I40E_TXR_FLAGS_WB_ON_ITR BIT(0) +#define I40E_RXR_FLAGS_BUILD_SKB_ENABLED BIT(1) /* stats structs */ struct i40e_queue_stats stats; @@ -356,6 +409,21 @@ struct i40e_ring { */ } ____cacheline_internodealigned_in_smp; +static inline bool ring_uses_build_skb(struct i40e_ring *ring) +{ + return !!(ring->flags & I40E_RXR_FLAGS_BUILD_SKB_ENABLED); +} + +static inline void set_ring_build_skb_enabled(struct i40e_ring *ring) +{ + ring->flags |= I40E_RXR_FLAGS_BUILD_SKB_ENABLED; +} + +static inline void clear_ring_build_skb_enabled(struct i40e_ring *ring) +{ + ring->flags &= ~I40E_RXR_FLAGS_BUILD_SKB_ENABLED; +} + enum i40e_latency_range { I40E_LOWEST_LATENCY = 0, I40E_LOW_LATENCY = 1, diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 7d00abae6104..12a930e879af 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -704,7 +704,8 @@ static void i40evf_configure_rx(struct i40evf_adapter *adapter) * standard Ethernet mtu. On x86 this gives us enough room * for shared info and 192 bytes of padding. */ - if (netdev->mtu <= ETH_DATA_LEN) + if (!I40E_2K_TOO_SMALL_WITH_PADDING && + (netdev->mtu <= ETH_DATA_LEN)) rx_buf_len = I40E_RXBUFFER_1536 - NET_IP_ALIGN; } #endif @@ -712,6 +713,11 @@ static void i40evf_configure_rx(struct i40evf_adapter *adapter) for (i = 0; i < adapter->num_active_queues; i++) { adapter->rx_rings[i].tail = hw->hw_addr + I40E_QRX_TAIL1(i); adapter->rx_rings[i].rx_buf_len = rx_buf_len; + + if (adapter->flags & I40EVF_FLAG_LEGACY_RX) + clear_ring_build_skb_enabled(&adapter->rx_rings[i]); + else + set_ring_build_skb_enabled(&adapter->rx_rings[i]); } } -- cgit From f8b45b74cc6229d9f8780fd962dab84d810b6f17 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 5 Apr 2017 07:51:03 -0400 Subject: i40e/i40evf: Use build_skb to build frames This patch is meant to improve the performance of the Rx path. Specifically by using build_skb we have several distinct advantages. In the case of small frames we were previously using a copy-break approach. This means that we were allocating a page fragment to use for skb->head, and were having to copy the packet into that region. Both of those calls are now avoided since we just build the skb around the data. In the case of large frames the gains are much more significant. Specifically we were having to allocate skb->head, and copy the headers as before. However in addition we were having to parse the header using eth_get_headlen which could be quite expensive. All of this is avoided by building the frame around the data. I have seen gains as high as 30% when using VXLAN for instance due to just header pulling overhead. Finally with all this in place it also sets us up to start looking at enabling XDP. Specifically we now have a path in which the data is in the page and the frame is built around it. So if we parse it with XDP before we call build_skb we can take care of any necessary processing there. Change-ID: Id4bdd618e94473d41f892417e5d8019639e421e3 Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 47 +++++++++++++++++++++++++++ drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 47 +++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index f15e1bcf3555..20691d2bf113 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1815,6 +1815,51 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, return skb; } +/** + * i40e_build_skb - Build skb around an existing buffer + * @rx_ring: Rx descriptor ring to transact packets on + * @rx_buffer: Rx buffer to pull data from + * @size: size of buffer to add to skb + * + * This function builds an skb around an existing Rx buffer, taking care + * to set up the skb correctly and avoid any memcpy overhead. + */ +static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *rx_buffer, + unsigned int size) +{ + void *va = page_address(rx_buffer->page) + rx_buffer->page_offset; +#if (PAGE_SIZE < 8192) + unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; +#else + unsigned int truesize = SKB_DATA_ALIGN(size); +#endif + struct sk_buff *skb; + + /* prefetch first cache line of first page */ + prefetch(va); +#if L1_CACHE_BYTES < 128 + prefetch(va + L1_CACHE_BYTES); +#endif + /* build an skb around the page buffer */ + skb = build_skb(va - I40E_SKB_PAD, truesize); + if (unlikely(!skb)) + return NULL; + + /* update pointers within the skb to store the data */ + skb_reserve(skb, I40E_SKB_PAD); + __skb_put(skb, size); + + /* buffer is used by skb, update page_offset */ +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif + + return skb; +} + /** * i40e_put_rx_buffer - Clean up used buffer and either recycle or free * @rx_ring: rx descriptor ring to transact packets on @@ -1939,6 +1984,8 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) /* retrieve a buffer from the ring */ if (skb) i40e_add_rx_frag(rx_ring, rx_buffer, skb, size); + else if (ring_uses_build_skb(rx_ring)) + skb = i40e_build_skb(rx_ring, rx_buffer, size); else skb = i40e_construct_skb(rx_ring, rx_buffer, size); diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index a71f81a9291f..460171edc412 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1176,6 +1176,51 @@ static struct sk_buff *i40e_construct_skb(struct i40e_ring *rx_ring, return skb; } +/** + * i40e_build_skb - Build skb around an existing buffer + * @rx_ring: Rx descriptor ring to transact packets on + * @rx_buffer: Rx buffer to pull data from + * @size: size of buffer to add to skb + * + * This function builds an skb around an existing Rx buffer, taking care + * to set up the skb correctly and avoid any memcpy overhead. + */ +static struct sk_buff *i40e_build_skb(struct i40e_ring *rx_ring, + struct i40e_rx_buffer *rx_buffer, + unsigned int size) +{ + void *va = page_address(rx_buffer->page) + rx_buffer->page_offset; +#if (PAGE_SIZE < 8192) + unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2; +#else + unsigned int truesize = SKB_DATA_ALIGN(size); +#endif + struct sk_buff *skb; + + /* prefetch first cache line of first page */ + prefetch(va); +#if L1_CACHE_BYTES < 128 + prefetch(va + L1_CACHE_BYTES); +#endif + /* build an skb around the page buffer */ + skb = build_skb(va - I40E_SKB_PAD, truesize); + if (unlikely(!skb)) + return NULL; + + /* update pointers within the skb to store the data */ + skb_reserve(skb, I40E_SKB_PAD); + __skb_put(skb, size); + + /* buffer is used by skb, update page_offset */ +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif + + return skb; +} + /** * i40e_put_rx_buffer - Clean up used buffer and either recycle or free * @rx_ring: rx descriptor ring to transact packets on @@ -1295,6 +1340,8 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) /* retrieve a buffer from the ring */ if (skb) i40e_add_rx_frag(rx_ring, rx_buffer, skb, size); + else if (ring_uses_build_skb(rx_ring)) + skb = i40e_build_skb(rx_ring, rx_buffer, size); else skb = i40e_construct_skb(rx_ring, rx_buffer, size); -- cgit From 5daab9db7b65df87da26fd8cfa695fb9546a1ddb Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Wed, 5 Apr 2017 19:00:55 -0700 Subject: New getsockopt option to get socket cookie Introduce a new getsockopt operation to retrieve the socket cookie for a specific socket based on the socket fd. It returns a unique non-decreasing cookie for each socket. Tested: https://android-review.googlesource.com/#/c/358163/ Acked-by: Willem de Bruijn Signed-off-by: Chenbo Feng Signed-off-by: David S. Miller --- arch/alpha/include/uapi/asm/socket.h | 2 ++ arch/avr32/include/uapi/asm/socket.h | 2 ++ arch/frv/include/uapi/asm/socket.h | 2 ++ arch/ia64/include/uapi/asm/socket.h | 2 ++ arch/m32r/include/uapi/asm/socket.h | 2 ++ arch/mips/include/uapi/asm/socket.h | 2 ++ arch/mn10300/include/uapi/asm/socket.h | 2 ++ arch/parisc/include/uapi/asm/socket.h | 2 ++ arch/powerpc/include/uapi/asm/socket.h | 2 ++ arch/s390/include/uapi/asm/socket.h | 2 ++ arch/sparc/include/uapi/asm/socket.h | 2 ++ arch/xtensa/include/uapi/asm/socket.h | 2 ++ include/uapi/asm-generic/socket.h | 2 ++ net/core/sock.c | 8 ++++++++ 14 files changed, 34 insertions(+) diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h index 1bb8cac61a28..148d7a32754e 100644 --- a/arch/alpha/include/uapi/asm/socket.h +++ b/arch/alpha/include/uapi/asm/socket.h @@ -103,4 +103,6 @@ #define SO_INCOMING_NAPI_ID 56 +#define SO_COOKIE 57 + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/avr32/include/uapi/asm/socket.h b/arch/avr32/include/uapi/asm/socket.h index f824eeb0f2e4..2434d08ad8d6 100644 --- a/arch/avr32/include/uapi/asm/socket.h +++ b/arch/avr32/include/uapi/asm/socket.h @@ -96,4 +96,6 @@ #define SO_INCOMING_NAPI_ID 56 +#define SO_COOKIE 57 + #endif /* _UAPI__ASM_AVR32_SOCKET_H */ diff --git a/arch/frv/include/uapi/asm/socket.h b/arch/frv/include/uapi/asm/socket.h index a8ad9bebfc47..1ccf45657472 100644 --- a/arch/frv/include/uapi/asm/socket.h +++ b/arch/frv/include/uapi/asm/socket.h @@ -96,5 +96,7 @@ #define SO_INCOMING_NAPI_ID 56 +#define SO_COOKIE 57 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h index 6af3253e4209..2c3f4b48042a 100644 --- a/arch/ia64/include/uapi/asm/socket.h +++ b/arch/ia64/include/uapi/asm/socket.h @@ -105,4 +105,6 @@ #define SO_INCOMING_NAPI_ID 56 +#define SO_COOKIE 57 + #endif /* _ASM_IA64_SOCKET_H */ diff --git a/arch/m32r/include/uapi/asm/socket.h b/arch/m32r/include/uapi/asm/socket.h index e98b6bb897c0..ae6548d29a18 100644 --- a/arch/m32r/include/uapi/asm/socket.h +++ b/arch/m32r/include/uapi/asm/socket.h @@ -96,4 +96,6 @@ #define SO_INCOMING_NAPI_ID 56 +#define SO_COOKIE 57 + #endif /* _ASM_M32R_SOCKET_H */ diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h index ae2b62e39d4d..3418ec9c1c50 100644 --- a/arch/mips/include/uapi/asm/socket.h +++ b/arch/mips/include/uapi/asm/socket.h @@ -114,4 +114,6 @@ #define SO_INCOMING_NAPI_ID 56 +#define SO_COOKIE 57 + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/mn10300/include/uapi/asm/socket.h b/arch/mn10300/include/uapi/asm/socket.h index e4ac1843ee01..4526e92301a6 100644 --- a/arch/mn10300/include/uapi/asm/socket.h +++ b/arch/mn10300/include/uapi/asm/socket.h @@ -96,4 +96,6 @@ #define SO_INCOMING_NAPI_ID 56 +#define SO_COOKIE 57 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h index f754c793e82a..514701840bd9 100644 --- a/arch/parisc/include/uapi/asm/socket.h +++ b/arch/parisc/include/uapi/asm/socket.h @@ -95,4 +95,6 @@ #define SO_INCOMING_NAPI_ID 0x4031 +#define SO_COOKIE 0x4032 + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/powerpc/include/uapi/asm/socket.h b/arch/powerpc/include/uapi/asm/socket.h index 5f84af7dcb2e..58e2ec0310fc 100644 --- a/arch/powerpc/include/uapi/asm/socket.h +++ b/arch/powerpc/include/uapi/asm/socket.h @@ -103,4 +103,6 @@ #define SO_INCOMING_NAPI_ID 56 +#define SO_COOKIE 57 + #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h index 25ac4960e707..e8e5ecf673fd 100644 --- a/arch/s390/include/uapi/asm/socket.h +++ b/arch/s390/include/uapi/asm/socket.h @@ -102,4 +102,6 @@ #define SO_INCOMING_NAPI_ID 56 +#define SO_COOKIE 57 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h index b05513acd589..3f4ad19d9ec7 100644 --- a/arch/sparc/include/uapi/asm/socket.h +++ b/arch/sparc/include/uapi/asm/socket.h @@ -92,6 +92,8 @@ #define SO_INCOMING_NAPI_ID 0x003a +#define SO_COOKIE 0x003b + /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 #define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002 diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h index 786606c81edd..1eb6d2fe70d3 100644 --- a/arch/xtensa/include/uapi/asm/socket.h +++ b/arch/xtensa/include/uapi/asm/socket.h @@ -107,4 +107,6 @@ #define SO_INCOMING_NAPI_ID 56 +#define SO_COOKIE 57 + #endif /* _XTENSA_SOCKET_H */ diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h index c98a52fb572a..2b488565599d 100644 --- a/include/uapi/asm-generic/socket.h +++ b/include/uapi/asm-generic/socket.h @@ -98,4 +98,6 @@ #define SO_INCOMING_NAPI_ID 56 +#define SO_COOKIE 57 + #endif /* __ASM_GENERIC_SOCKET_H */ diff --git a/net/core/sock.c b/net/core/sock.c index 392f9b6f96e2..a06bb7a2a689 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1083,6 +1083,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname, union { int val; + u64 val64; struct linger ling; struct timeval tm; } v; @@ -1340,6 +1341,13 @@ int sock_getsockopt(struct socket *sock, int level, int optname, break; #endif + case SO_COOKIE: + lv = sizeof(u64); + if (len < lv) + return -EINVAL; + v.val64 = sock_gen_cookie(sk); + break; + default: /* We implement the SO_SNDLOWAT etc to not be settable * (1003.1g 7). -- cgit From 00f660eaf37808aa0a537c288c9e2dee73bd4316 Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Wed, 5 Apr 2017 19:00:56 -0700 Subject: Sample program using SO_COOKIE Added a per socket traffic monitoring option to illustrate the usage of new getsockopt SO_COOKIE. The program is based on the socket traffic monitoring program using xt_eBPF and in the new option the data entry can be directly accessed using socket cookie. The cookie retrieved allow us to lookup an element in the eBPF for a specific socket. Signed-off-by: Chenbo Feng Signed-off-by: David S. Miller --- samples/bpf/cookie_uid_helper_example.c | 146 +++++++++++++++++++++++---- samples/bpf/run_cookie_uid_helper_example.sh | 4 +- 2 files changed, 127 insertions(+), 23 deletions(-) mode change 100644 => 100755 samples/bpf/run_cookie_uid_helper_example.sh diff --git a/samples/bpf/cookie_uid_helper_example.c b/samples/bpf/cookie_uid_helper_example.c index f6e5e58931c5..ad5afedf2e70 100644 --- a/samples/bpf/cookie_uid_helper_example.c +++ b/samples/bpf/cookie_uid_helper_example.c @@ -4,10 +4,11 @@ * program into the xt_bpf match. * * TEST: - * ./run_cookie_uid_helper_example.sh - * Then generate some traffic in variate ways. ping 0 -c 10 would work - * but the cookie and uid in this case could both be 0. A sample output - * with some traffic generated by web browser is shown below: + * ./run_cookie_uid_helper_example.sh -option + * option: + * -t: do traffic monitoring test, the program will continuously + * print out network traffic happens after program started A sample + * output is shown below: * * cookie: 877, uid: 0x3e8, Pakcet Count: 20, Bytes Count: 11058 * cookie: 132, uid: 0x0, Pakcet Count: 2, Bytes Count: 286 @@ -18,6 +19,10 @@ * cookie: 0, uid: 0x0, Pakcet Count: 6, Bytes Count: 712 * cookie: 880, uid: 0xfffe, Pakcet Count: 1, Bytes Count: 70 * + * -s: do getsockopt SO_COOKIE test, the program will set up a pair of + * UDP sockets and send packets between them. And read out the traffic data + * directly from the ebpf map based on the socket cookie. + * * Clean up: if using shell script, the script file will delete the iptables * rule and unmount the bpf program when exit. Else the iptables rule need * to be deleted by hand, see run_cookie_uid_helper_example.sh for detail. @@ -34,6 +39,8 @@ #include #include #include +#include +#include #include #include #include @@ -46,6 +53,8 @@ #include #include "libbpf.h" +#define PORT 8888 + struct stats { uint32_t uid; uint64_t packets; @@ -54,6 +63,8 @@ struct stats { static int map_fd, prog_fd; +static bool test_finish; + static void maps_create(void) { map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(uint32_t), @@ -164,7 +175,7 @@ static void prog_attach_iptables(char *file) printf("file path too long: %s\n", file); exit(1); } - sprintf(rules, "iptables -A INPUT -m bpf --object-pinned %s -j ACCEPT", + sprintf(rules, "iptables -A OUTPUT -m bpf --object-pinned %s -j ACCEPT", file); ret = system(rules); if (ret < 0) { @@ -177,7 +188,8 @@ static void print_table(void) { struct stats curEntry; uint32_t curN = UINT32_MAX; - uint32_t nextN, res; + uint32_t nextN; + int res; while (bpf_map_get_next_key(map_fd, &curN, &nextN) > -1) { curN = nextN; @@ -193,25 +205,117 @@ static void print_table(void) } } -int main(int argc, char *argv[]) +static void udp_client(void) { - if (argc > 2) { - printf("Too many argument provided\n"); - return 1; - } else if (argc < 2) { - printf("Usage: %s bpfObjName\n", argv[0]); - return 1; + struct sockaddr_in si_other = {0}; + struct sockaddr_in si_me = {0}; + struct stats dataEntry; + int s_rcv, s_send, i, recv_len; + char message = 'a'; + char buf; + uint64_t cookie; + int res; + socklen_t cookie_len = sizeof(cookie); + socklen_t slen = sizeof(si_other); + + s_rcv = socket(PF_INET, SOCK_DGRAM, 0); + if (s_rcv < 0) + error(1, errno, "rcv socket creat failed!\n"); + si_other.sin_family = AF_INET; + si_other.sin_port = htons(PORT); + if (inet_aton("127.0.0.1", &si_other.sin_addr) == 0) + error(1, errno, "inet_aton\n"); + if (bind(s_rcv, (struct sockaddr *)&si_other, sizeof(si_other)) == -1) + error(1, errno, "bind\n"); + s_send = socket(PF_INET, SOCK_DGRAM, 0); + if (s_send < 0) + error(1, errno, "send socket creat failed!\n"); + res = getsockopt(s_send, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len); + if (res < 0) + printf("get cookie failed: %s\n", strerror(errno)); + res = bpf_map_lookup_elem(map_fd, &cookie, &dataEntry); + if (res != -1) + error(1, errno, "socket stat found while flow not active\n"); + for (i = 0; i < 10; i++) { + res = sendto(s_send, &message, sizeof(message), 0, + (struct sockaddr *)&si_other, slen); + if (res == -1) + error(1, errno, "send\n"); + if (res != sizeof(message)) + error(1, 0, "%uB != %luB\n", res, sizeof(message)); + recv_len = recvfrom(s_rcv, &buf, sizeof(buf), 0, + (struct sockaddr *)&si_me, &slen); + if (recv_len < 0) + error(1, errno, "revieve\n"); + res = memcmp(&(si_other.sin_addr), &(si_me.sin_addr), + sizeof(si_me.sin_addr)); + if (res != 0) + error(1, EFAULT, "sender addr error: %d\n", res); + printf("Message received: %c\n", buf); + res = bpf_map_lookup_elem(map_fd, &cookie, &dataEntry); + if (res < 0) + error(1, errno, "lookup sk stat failed, cookie: %lu\n", + cookie); + printf("cookie: %lu, uid: 0x%x, Packet Count: %lu," + " Bytes Count: %lu\n\n", cookie, dataEntry.uid, + dataEntry.packets, dataEntry.bytes); } + close(s_send); + close(s_rcv); +} - maps_create(); - prog_load(); - prog_attach_iptables(argv[1]); +static int usage(void) +{ + printf("Usage: ./run_cookie_uid_helper_example.sh" + " bpfObjName -option\n" + " -t traffic monitor test\n" + " -s getsockopt cookie test\n"); + return 1; +} - while (true) { - print_table(); - printf("\n"); - sleep(1); - }; +void finish(int ret) +{ + test_finish = true; +} + +int main(int argc, char *argv[]) +{ + int opt; + bool cfg_test_traffic = false; + bool cfg_test_cookie = false; + + if (argc != 3) + return usage(); + while ((opt = getopt(argc, argv, "ts")) != -1) { + switch (opt) { + case 't': + cfg_test_traffic = true; + break; + case 's': + cfg_test_cookie = true; + break; + default: + printf("unknown option %c\n", opt); + usage(); + return -1; + } + } + maps_create(); + prog_load(); + prog_attach_iptables(argv[2]); + if (cfg_test_traffic) { + if (signal(SIGINT, finish) == SIG_ERR) + error(1, errno, "register handler failed"); + while (!test_finish) { + print_table(); + printf("\n"); + sleep(1); + }; + } else if (cfg_test_cookie) { + udp_client(); + } + close(prog_fd); + close(map_fd); return 0; } diff --git a/samples/bpf/run_cookie_uid_helper_example.sh b/samples/bpf/run_cookie_uid_helper_example.sh old mode 100644 new mode 100755 index 40da8aa75c44..f898cfa2b1aa --- a/samples/bpf/run_cookie_uid_helper_example.sh +++ b/samples/bpf/run_cookie_uid_helper_example.sh @@ -4,11 +4,11 @@ root_dir=$local_dir/../.. mnt_dir=$(mktemp -d --tmp) on_exit() { - iptables -D INPUT -m bpf --object-pinned ${mnt_dir}/bpf_prog -j ACCEPT + iptables -D OUTPUT -m bpf --object-pinned ${mnt_dir}/bpf_prog -j ACCEPT umount ${mnt_dir} rm -r ${mnt_dir} } trap on_exit EXIT mount -t bpf bpf ${mnt_dir} -./per_socket_stats_example ${mnt_dir}/bpf_prog +./per_socket_stats_example ${mnt_dir}/bpf_prog $1 -- cgit From 1f1120a5427c9e7894be63baaab828755e45a1c0 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 6 Apr 2017 13:54:35 +0100 Subject: nfp: don't dereference a null nn->eth_port to print a warning On the case where nn->eth_port is null the warning message is printing the port by dereferencing this null pointer. Remove the deference to avoid a crash when printing the warning message. Detected by CoverityScan, CID#1426198 ("Dereference after null check") Fixes: ce22f5a2cbe3c627 ("nfp: separate high level and low level NSP headers") Signed-off-by: Colin Ian King Acked-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 3e1f97e88710..4c6863a072d3 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -495,8 +495,7 @@ static void nfp_net_refresh_netdevs(struct work_struct *work) list_for_each_entry_safe(nn, next, &pf->ports, port_list) { if (!nn->eth_port) { - nfp_warn(pf->cpp, "Warning: port %d not present after reconfig\n", - nn->eth_port->eth_index); + nfp_warn(pf->cpp, "Warning: port not present after reconfig\n"); continue; } if (!nn->eth_port->override_changed) -- cgit From 261a0a54d1ebcd04fb4a1c3f971b2c3b7ae06925 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Apr 2017 16:10:42 +0200 Subject: netlink: uapi: use hex numbers for NLM_F_* flags It's rather confusing that the netlink message flags are numbered 1, 2, 4, 8, 16, 32, , 0x100. Make that more understandable by numbering the lower ones with hex constants as well. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- include/uapi/linux/netlink.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h index f3946a27bd07..b2c9c26ea30f 100644 --- a/include/uapi/linux/netlink.h +++ b/include/uapi/linux/netlink.h @@ -50,12 +50,12 @@ struct nlmsghdr { /* Flags values */ -#define NLM_F_REQUEST 1 /* It is request message. */ -#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */ -#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */ -#define NLM_F_ECHO 8 /* Echo this request */ -#define NLM_F_DUMP_INTR 16 /* Dump was inconsistent due to sequence change */ -#define NLM_F_DUMP_FILTERED 32 /* Dump was filtered as requested */ +#define NLM_F_REQUEST 0x01 /* It is request message. */ +#define NLM_F_MULTI 0x02 /* Multipart message, terminated by NLMSG_DONE */ +#define NLM_F_ACK 0x04 /* Reply with ack, with zero or error code */ +#define NLM_F_ECHO 0x08 /* Echo this request */ +#define NLM_F_DUMP_INTR 0x10 /* Dump was inconsistent due to sequence change */ +#define NLM_F_DUMP_FILTERED 0x20 /* Dump was filtered as requested */ /* Modifiers to GET request */ #define NLM_F_ROOT 0x100 /* specify tree root */ -- cgit From 54a88e4cfc0dfaf12b2569b82cc5c5b43f5cde71 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 6 Apr 2017 12:42:16 -0700 Subject: net: dsa: mv88e6xxx: Make SMI c22/c45 read/write functions static The SMI clause 22 & 45 read/write operations are local to the global2.c file, so make them static. This eliminates the following warning: drivers/net/dsa/mv88e6xxx/global2.c:571:5: warning: no previous prototype for 'mv88e6xxx_g2_smi_phy_read_c45' [-Wmissing-prototypes] int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip, int addr, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/net/dsa/mv88e6xxx/global2.c:602:5: warning: no previous prototype for 'mv88e6xxx_g2_smi_phy_read_c22' [-Wmissing-prototypes] int mv88e6xxx_g2_smi_phy_read_c22(struct mv88e6xxx_chip *chip, int addr, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/net/dsa/mv88e6xxx/global2.c:635:5: warning: no previous prototype for 'mv88e6xxx_g2_smi_phy_write_c45' [-Wmissing-prototypes] int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip, int addr, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/net/dsa/mv88e6xxx/global2.c:664:5: warning: no previous prototype for 'mv88e6xxx_g2_smi_phy_write_c22' [-Wmissing-prototypes] int mv88e6xxx_g2_smi_phy_write_c22(struct mv88e6xxx_chip *chip, int addr, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Suggested-by: Andrew Lunn Signed-off-by: Florian Fainelli Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/global2.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 7c6bc33a9516..b3fea55071e3 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -568,8 +568,9 @@ static int mv88e6xxx_g2_smi_phy_write_addr(struct mv88e6xxx_chip *chip, return mv88e6xxx_g2_smi_phy_cmd(chip, cmd); } -int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip, int addr, - int reg_c45, u16 *val, bool external) +static int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip, + int addr, int reg_c45, u16 *val, + bool external) { int device = (reg_c45 >> 16) & 0x1f; int reg = reg_c45 & 0xffff; @@ -599,8 +600,9 @@ int mv88e6xxx_g2_smi_phy_read_c45(struct mv88e6xxx_chip *chip, int addr, return 0; } -int mv88e6xxx_g2_smi_phy_read_c22(struct mv88e6xxx_chip *chip, int addr, - int reg, u16 *val, bool external) +static int mv88e6xxx_g2_smi_phy_read_c22(struct mv88e6xxx_chip *chip, + int addr, int reg, u16 *val, + bool external) { u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_READ_DATA | (addr << 5) | reg; int err; @@ -632,8 +634,9 @@ int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip, return mv88e6xxx_g2_smi_phy_read_c22(chip, addr, reg, val, external); } -int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip, int addr, - int reg_c45, u16 val, bool external) +static int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip, + int addr, int reg_c45, u16 val, + bool external) { int device = (reg_c45 >> 16) & 0x1f; int reg = reg_c45 & 0xffff; @@ -661,8 +664,9 @@ int mv88e6xxx_g2_smi_phy_write_c45(struct mv88e6xxx_chip *chip, int addr, return 0; } -int mv88e6xxx_g2_smi_phy_write_c22(struct mv88e6xxx_chip *chip, int addr, - int reg, u16 val, bool external) +static int mv88e6xxx_g2_smi_phy_write_c22(struct mv88e6xxx_chip *chip, + int addr, int reg, u16 val, + bool external) { u16 cmd = GLOBAL2_SMI_PHY_CMD_OP_22_WRITE_DATA | (addr << 5) | reg; int err; -- cgit From bffb184247bcc783a40a0e123a9a2de3c5b28157 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Thu, 6 Apr 2017 14:59:21 -0700 Subject: netvsc: Initialize all channel related state prior to opening the channel Prior to opening the channel we should have all the state setup to handle interrupts. The current code does not do that; fix the bug. This bug can result in faults in the interrupt path. Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 27 +++++++++++++++------------ drivers/net/hyperv/rndis_filter.c | 5 +++-- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index e998e2f7a619..7ab06b338a14 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -1289,6 +1289,21 @@ int netvsc_device_add(struct hv_device *device, */ set_channel_read_mode(device->channel, HV_CALL_ISR); + /* If we're reopening the device we may have multiple queues, fill the + * chn_table with the default channel to use it before subchannels are + * opened. + * Initialize the channel state before we open; + * we can be interrupted as soon as we open the channel. + */ + + for (i = 0; i < VRSS_CHANNEL_MAX; i++) { + struct netvsc_channel *nvchan = &net_device->chan_table[i]; + + nvchan->channel = device->channel; + netif_napi_add(ndev, &nvchan->napi, + netvsc_poll, NAPI_POLL_WEIGHT); + } + /* Open the channel */ ret = vmbus_open(device->channel, ring_size * PAGE_SIZE, ring_size * PAGE_SIZE, NULL, 0, @@ -1303,18 +1318,6 @@ int netvsc_device_add(struct hv_device *device, /* Channel is opened */ netdev_dbg(ndev, "hv_netvsc channel opened successfully\n"); - /* If we're reopening the device we may have multiple queues, fill the - * chn_table with the default channel to use it before subchannels are - * opened. - */ - for (i = 0; i < VRSS_CHANNEL_MAX; i++) { - struct netvsc_channel *nvchan = &net_device->chan_table[i]; - - nvchan->channel = device->channel; - netif_napi_add(ndev, &nvchan->napi, - netvsc_poll, NAPI_POLL_WEIGHT); - } - /* Enable NAPI handler for init callbacks */ napi_enable(&net_device->chan_table[0].napi); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 983582526b37..1e9445bc4539 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1007,12 +1007,13 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) */ set_channel_read_mode(new_sc, HV_CALL_ISR); + /* Set the channel before opening.*/ + nvchan->channel = new_sc; + ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE, nvscdev->ring_size * PAGE_SIZE, NULL, 0, netvsc_channel_cb, nvchan); - if (ret == 0) - nvchan->channel = new_sc; napi_enable(&nvchan->napi); -- cgit From 0c264588b5de50353e4a1ce0c2521576426dd89d Mon Sep 17 00:00:00 2001 From: Felix Manlunas Date: Thu, 6 Apr 2017 19:22:22 -0700 Subject: liquidio: fix VF incorrectly indicating that it successfully set its VLAN For security reasons, NIC firmware does not allow VF to set its VLAN if PF set it already. Firmware allows VF to set its VLAN if PF did not set it. After the VF instructs the firmware to set the VLAN, VF always indicates (via return 0) that the operation is successful--even for the times when it isn't. Put in a mechanism for the VF's set VLAN function to receive the firmware response code, then make that function return -EPERM if the firmware forbids the operation. Make that mechanism available for other functions that may, in the future, be interested in receiving the response code from the firmware. That mechanism involves adding new fields to struct octnic_ctrl_pkt, so make all users of struct octnic_ctrl_pkt initialize the struct to zero before using it; otherwise, the mechanism might act on uninitialized garbage. Signed-off-by: Felix Manlunas Signed-off-by: Derek Chickles Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_core.c | 11 +++++++++++ drivers/net/ethernet/cavium/liquidio/lio_main.c | 4 ++++ drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 19 ++++++++++++++++++- drivers/net/ethernet/cavium/liquidio/octeon_nic.c | 10 ++++++---- drivers/net/ethernet/cavium/liquidio/octeon_nic.h | 4 ++++ 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index 08676df6cef0..796c2cbc11f6 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -127,6 +127,17 @@ void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr) struct octeon_device *oct = lio->oct_dev; u8 *mac; + if (nctrl->completion && nctrl->response_code) { + /* Signal whoever is interested that the response code from the + * firmware has arrived. + */ + WRITE_ONCE(*nctrl->response_code, nctrl->status); + complete(nctrl->completion); + } + + if (nctrl->status) + return; + switch (nctrl->ncmd.s.cmd) { case OCTNET_CMD_CHANGE_DEVFLAGS: case OCTNET_CMD_SET_MULTI_LIST: diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index fa673a1de24d..927617cbf6a9 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -3499,6 +3499,8 @@ static int liquidio_set_rxcsum_command(struct net_device *netdev, int command, struct octnic_ctrl_pkt nctrl; int ret = 0; + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + nctrl.ncmd.u64 = 0; nctrl.ncmd.s.cmd = command; nctrl.ncmd.s.param1 = rx_cmd; @@ -3532,6 +3534,8 @@ static int liquidio_vxlan_port_command(struct net_device *netdev, int command, struct octnic_ctrl_pkt nctrl; int ret = 0; + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + nctrl.ncmd.u64 = 0; nctrl.ncmd.s.cmd = command; nctrl.ncmd.s.more = vxlan_cmd_bit; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 174d748b5928..34c77821fad9 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -2484,6 +2484,8 @@ liquidio_vlan_rx_add_vid(struct net_device *netdev, struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; struct octnic_ctrl_pkt nctrl; + struct completion compl; + u16 response_code; int ret = 0; memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); @@ -2495,14 +2497,25 @@ liquidio_vlan_rx_add_vid(struct net_device *netdev, nctrl.wait_time = 100; nctrl.netpndev = (u64)netdev; nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; + init_completion(&compl); + nctrl.completion = &compl; + nctrl.response_code = &response_code; ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); if (ret < 0) { dev_err(&oct->pci_dev->dev, "Add VLAN filter failed in core (ret: 0x%x)\n", ret); + return -EIO; } - return ret; + if (!wait_for_completion_timeout(&compl, + msecs_to_jiffies(nctrl.wait_time))) + return -EPERM; + + if (READ_ONCE(response_code)) + return -EPERM; + + return 0; } static int @@ -2547,6 +2560,8 @@ static int liquidio_set_rxcsum_command(struct net_device *netdev, int command, struct octnic_ctrl_pkt nctrl; int ret = 0; + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + nctrl.ncmd.u64 = 0; nctrl.ncmd.s.cmd = command; nctrl.ncmd.s.param1 = rx_cmd; @@ -2579,6 +2594,8 @@ static int liquidio_vxlan_port_command(struct net_device *netdev, int command, struct octnic_ctrl_pkt nctrl; int ret = 0; + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + nctrl.ncmd.u64 = 0; nctrl.ncmd.s.cmd = command; nctrl.ncmd.s.more = vxlan_cmd_bit; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_nic.c b/drivers/net/ethernet/cavium/liquidio/octeon_nic.c index 0243be8dd56f..b457cf23fce6 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_nic.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_nic.c @@ -100,14 +100,16 @@ static void octnet_link_ctrl_callback(struct octeon_device *oct, nctrl = (struct octnic_ctrl_pkt *)sc->ctxptr; - /* Call the callback function if status is OK. - * Status is OK only if a response was expected and core returned - * success. + /* Call the callback function if status is zero (meaning OK) or status + * contains a firmware status code bigger than zero (meaning the + * firmware is reporting an error). * If no response was expected, status is OK if the command was posted * successfully. */ - if (!status && nctrl->cb_fn) + if ((!status || status > FIRMWARE_STATUS_CODE(0)) && nctrl->cb_fn) { + nctrl->status = status; nctrl->cb_fn(nctrl); + } octeon_free_soft_command(oct, sc); } diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_nic.h b/drivers/net/ethernet/cavium/liquidio/octeon_nic.h index 0c7a5c9b2932..6480ef863441 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_nic.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_nic.h @@ -62,6 +62,10 @@ struct octnic_ctrl_pkt { /** Callback function called when the command has been fetched */ octnic_ctrl_pkt_cb_fn_t cb_fn; + + u32 status; + u16 *response_code; + struct completion *completion; }; #define MAX_UDD_SIZE(nctrl) (sizeof((nctrl)->udd)) -- cgit From 7f564528a480084e2318cd48caba7aef4a54a77f Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Sat, 8 Apr 2017 20:36:24 +0200 Subject: skbuff: Extend gso_type to unsigned int. All available gso_type flags are currently in use, so extend gso_type from 'unsigned short' to 'unsigned int' to be able to add further flags. We reorder the struct skb_shared_info to use two bytes of the four byte hole before dataref. All fields before dataref are cleared, i.e. four bytes more than before the change. The remaining two byte hole is moved to the beginning of the structure, this protects us from immediate overwites on out of bound writes to the sk_buff head. Structure layout on x86-64 before the change: struct skb_shared_info { unsigned char nr_frags; /* 0 1 */ __u8 tx_flags; /* 1 1 */ short unsigned int gso_size; /* 2 2 */ short unsigned int gso_segs; /* 4 2 */ short unsigned int gso_type; /* 6 2 */ struct sk_buff * frag_list; /* 8 8 */ struct skb_shared_hwtstamps hwtstamps; /* 16 8 */ u32 tskey; /* 24 4 */ __be32 ip6_frag_id; /* 28 4 */ atomic_t dataref; /* 32 4 */ /* XXX 4 bytes hole, try to pack */ void * destructor_arg; /* 40 8 */ skb_frag_t frags[17]; /* 48 272 */ /* --- cacheline 5 boundary (320 bytes) --- */ /* size: 320, cachelines: 5, members: 12 */ /* sum members: 316, holes: 1, sum holes: 4 */ }; Structure layout on x86-64 after the change: struct skb_shared_info { short unsigned int _unused; /* 0 2 */ unsigned char nr_frags; /* 2 1 */ __u8 tx_flags; /* 3 1 */ short unsigned int gso_size; /* 4 2 */ short unsigned int gso_segs; /* 6 2 */ struct sk_buff * frag_list; /* 8 8 */ struct skb_shared_hwtstamps hwtstamps; /* 16 8 */ unsigned int gso_type; /* 24 4 */ u32 tskey; /* 28 4 */ __be32 ip6_frag_id; /* 32 4 */ atomic_t dataref; /* 36 4 */ void * destructor_arg; /* 40 8 */ skb_frag_t frags[17]; /* 48 272 */ /* --- cacheline 5 boundary (320 bytes) --- */ /* size: 320, cachelines: 5, members: 13 */ }; Signed-off-by: Steffen Klassert Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index c776abd86937..741d75cfc686 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -413,14 +413,15 @@ struct ubuf_info { * the end of the header data, ie. at skb->end. */ struct skb_shared_info { + unsigned short _unused; unsigned char nr_frags; __u8 tx_flags; unsigned short gso_size; /* Warning: this field is not always filled in (UFO)! */ unsigned short gso_segs; - unsigned short gso_type; struct sk_buff *frag_list; struct skb_shared_hwtstamps hwtstamps; + unsigned int gso_type; u32 tskey; __be32 ip6_frag_id; -- cgit From 1e038e3eef7d68dcdae4abfb5da7f2dff4becb63 Mon Sep 17 00:00:00 2001 From: Arushi Singhal Date: Sat, 8 Apr 2017 21:19:30 +0530 Subject: netfilter: ip6_tables: Remove unneccessary comments This comments are obsolete and should go, as there are no set of rules per CPU anymore. Signed-off-by: Arushi Singhal --- net/ipv6/netfilter/ip6_tables.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index d862e3471935..1f90644056ac 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -51,15 +51,6 @@ void *ip6t_alloc_initial_table(const struct xt_table *info) } EXPORT_SYMBOL_GPL(ip6t_alloc_initial_table); -/* - We keep a set of rules for each CPU, so we can avoid write-locking - them in the softirq when updating the counters and therefore - only need to read-lock in the softirq; doing a write_lock_bh() in user - context stops packets coming through and allows user context to read - the counters or update the rules. - - Hence the start of any table is given by get_table() below. */ - /* Returns whether matches rule or not. */ /* Performance critical - called for every packet */ static inline bool -- cgit From 9d7f9c4f78e95dd9d17199a53d903262530be62e Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sat, 8 Apr 2017 08:55:21 -0700 Subject: net: dsa: Do not check for NULL dst in tag parsers dsa_switch_rcv() already tests for dst == NULL, so there is no need to duplicate the same check within the tag receive functions. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/tag_brcm.c | 3 --- net/dsa/tag_dsa.c | 3 --- net/dsa/tag_edsa.c | 3 --- net/dsa/tag_mtk.c | 3 --- net/dsa/tag_qca.c | 3 --- net/dsa/tag_trailer.c | 2 -- 6 files changed, 17 deletions(-) diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index e2ed6cf68261..68d4feef96d4 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -100,9 +100,6 @@ static int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, int source_port; u8 *brcm_tag; - if (unlikely(dst == NULL)) - goto out_drop; - ds = dst->cpu_switch; skb = skb_unshare(skb, GFP_ATOMIC); diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index e42ba906100c..377569c0e4f7 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -77,9 +77,6 @@ static int dsa_rcv(struct sk_buff *skb, struct net_device *dev, int source_device; int source_port; - if (unlikely(dst == NULL)) - goto out_drop; - skb = skb_unshare(skb, GFP_ATOMIC); if (skb == NULL) goto out; diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c index 6a9b7a9e4e15..30520ff9c9a2 100644 --- a/net/dsa/tag_edsa.c +++ b/net/dsa/tag_edsa.c @@ -90,9 +90,6 @@ static int edsa_rcv(struct sk_buff *skb, struct net_device *dev, int source_device; int source_port; - if (unlikely(dst == NULL)) - goto out_drop; - skb = skb_unshare(skb, GFP_ATOMIC); if (skb == NULL) goto out; diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c index 44ae6353a521..836c311a3c38 100644 --- a/net/dsa/tag_mtk.c +++ b/net/dsa/tag_mtk.c @@ -55,9 +55,6 @@ static int mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev, int port; __be16 *phdr, hdr; - if (unlikely(!dst)) - goto out_drop; - skb = skb_unshare(skb, GFP_ATOMIC); if (!skb) goto out; diff --git a/net/dsa/tag_qca.c b/net/dsa/tag_qca.c index 4e0dad759d04..6579d6db1bc6 100644 --- a/net/dsa/tag_qca.c +++ b/net/dsa/tag_qca.c @@ -75,9 +75,6 @@ static int qca_tag_rcv(struct sk_buff *skb, struct net_device *dev, int port; __be16 *phdr, hdr; - if (unlikely(!dst)) - goto out_drop; - skb = skb_unshare(skb, GFP_ATOMIC); if (!skb) goto out; diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c index 74c948512550..f5c764ee2968 100644 --- a/net/dsa/tag_trailer.c +++ b/net/dsa/tag_trailer.c @@ -66,8 +66,6 @@ static int trailer_rcv(struct sk_buff *skb, struct net_device *dev, u8 *trailer; int source_port; - if (unlikely(dst == NULL)) - goto out_drop; ds = dst->cpu_switch; skb = skb_unshare(skb, GFP_ATOMIC); -- cgit From 16c5dcb13a371feae0e680e6518775b5335b37d8 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sat, 8 Apr 2017 08:55:22 -0700 Subject: net: dsa: Move skb_unshare() to dsa_switch_rcv() All DSA tag receive functions need to unshare the skb before mangling it, move this to the generic dsa_switch_rcv() function which will allow us to make the tag receive function return their mangled skb without caring about freeing a NULL skb. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa.c | 4 ++++ net/dsa/tag_brcm.c | 5 ----- net/dsa/tag_dsa.c | 5 ----- net/dsa/tag_edsa.c | 5 ----- net/dsa/tag_mtk.c | 5 ----- net/dsa/tag_qca.c | 5 ----- net/dsa/tag_trailer.c | 5 ----- 7 files changed, 4 insertions(+), 30 deletions(-) diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 6cad15da5892..d370c8bfa372 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -906,6 +906,10 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, return 0; } + skb = skb_unshare(skb, GFP_ATOMIC); + if (!skb) + return 0; + return dst->rcv(skb, dev, pt, orig_dev); } diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index 68d4feef96d4..263941769c88 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -102,10 +102,6 @@ static int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, ds = dst->cpu_switch; - skb = skb_unshare(skb, GFP_ATOMIC); - if (skb == NULL) - goto out; - if (unlikely(!pskb_may_pull(skb, BRCM_TAG_LEN))) goto out_drop; @@ -151,7 +147,6 @@ static int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, out_drop: kfree_skb(skb); -out: return 0; } diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index 377569c0e4f7..b7032699eaad 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -77,10 +77,6 @@ static int dsa_rcv(struct sk_buff *skb, struct net_device *dev, int source_device; int source_port; - skb = skb_unshare(skb, GFP_ATOMIC); - if (skb == NULL) - goto out; - if (unlikely(!pskb_may_pull(skb, DSA_HLEN))) goto out_drop; @@ -175,7 +171,6 @@ static int dsa_rcv(struct sk_buff *skb, struct net_device *dev, out_drop: kfree_skb(skb); -out: return 0; } diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c index 30520ff9c9a2..b87009672b40 100644 --- a/net/dsa/tag_edsa.c +++ b/net/dsa/tag_edsa.c @@ -90,10 +90,6 @@ static int edsa_rcv(struct sk_buff *skb, struct net_device *dev, int source_device; int source_port; - skb = skb_unshare(skb, GFP_ATOMIC); - if (skb == NULL) - goto out; - if (unlikely(!pskb_may_pull(skb, EDSA_HLEN))) goto out_drop; @@ -194,7 +190,6 @@ static int edsa_rcv(struct sk_buff *skb, struct net_device *dev, out_drop: kfree_skb(skb); -out: return 0; } diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c index 836c311a3c38..d0a477084870 100644 --- a/net/dsa/tag_mtk.c +++ b/net/dsa/tag_mtk.c @@ -55,10 +55,6 @@ static int mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev, int port; __be16 *phdr, hdr; - skb = skb_unshare(skb, GFP_ATOMIC); - if (!skb) - goto out; - if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN))) goto out_drop; @@ -105,7 +101,6 @@ static int mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev, out_drop: kfree_skb(skb); -out: return 0; } diff --git a/net/dsa/tag_qca.c b/net/dsa/tag_qca.c index 6579d6db1bc6..d1324649808c 100644 --- a/net/dsa/tag_qca.c +++ b/net/dsa/tag_qca.c @@ -75,10 +75,6 @@ static int qca_tag_rcv(struct sk_buff *skb, struct net_device *dev, int port; __be16 *phdr, hdr; - skb = skb_unshare(skb, GFP_ATOMIC); - if (!skb) - goto out; - if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) goto out_drop; @@ -126,7 +122,6 @@ static int qca_tag_rcv(struct sk_buff *skb, struct net_device *dev, out_drop: kfree_skb(skb); -out: return 0; } diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c index f5c764ee2968..1fc0b221a70f 100644 --- a/net/dsa/tag_trailer.c +++ b/net/dsa/tag_trailer.c @@ -68,10 +68,6 @@ static int trailer_rcv(struct sk_buff *skb, struct net_device *dev, ds = dst->cpu_switch; - skb = skb_unshare(skb, GFP_ATOMIC); - if (skb == NULL) - goto out; - if (skb_linearize(skb)) goto out_drop; @@ -100,7 +96,6 @@ static int trailer_rcv(struct sk_buff *skb, struct net_device *dev, out_drop: kfree_skb(skb); -out: return 0; } -- cgit From a86d8becc3f04a5e350b5a17530e6a01495c00a5 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sat, 8 Apr 2017 08:55:23 -0700 Subject: net: dsa: Factor bottom tag receive functions All DSA tag receive functions do strictly the same thing after they have located the originating source port from their tag specific protocol: - push ETH_HLEN bytes - set pkt_type to PACKET_HOST - call eth_type_trans() - bump up counters - call netif_receive_skb() Factor all of that into dsa_switch_rcv(). This also makes us return a pointer to a sk_buff, which makes us symetric with the xmit function. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 2 +- net/dsa/dsa.c | 20 +++++++++++++++++++- net/dsa/dsa_priv.h | 5 +++-- net/dsa/tag_brcm.c | 18 +++++------------- net/dsa/tag_dsa.c | 18 +++++------------- net/dsa/tag_edsa.c | 18 +++++------------- net/dsa/tag_mtk.c | 20 +++++--------------- net/dsa/tag_qca.c | 18 +++++------------- net/dsa/tag_trailer.c | 18 +++++------------- 9 files changed, 53 insertions(+), 84 deletions(-) diff --git a/include/net/dsa.h b/include/net/dsa.h index 7ba9b1fb565c..9b1c1eb4147a 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -124,7 +124,7 @@ struct dsa_switch_tree { * protocol to use. */ struct net_device *master_netdev; - int (*rcv)(struct sk_buff *skb, + struct sk_buff * (*rcv)(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev); diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index d370c8bfa372..1fb9cf7aaaf4 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "dsa_priv.h" @@ -900,6 +901,7 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { struct dsa_switch_tree *dst = dev->dsa_ptr; + struct sk_buff *nskb = NULL; if (unlikely(dst == NULL)) { kfree_skb(skb); @@ -910,7 +912,23 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, if (!skb) return 0; - return dst->rcv(skb, dev, pt, orig_dev); + nskb = dst->rcv(skb, dev, pt, orig_dev); + if (!nskb) { + kfree_skb(skb); + return 0; + } + + skb = nskb; + skb_push(skb, ETH_HLEN); + skb->pkt_type = PACKET_HOST; + skb->protocol = eth_type_trans(skb, skb->dev); + + skb->dev->stats.rx_packets++; + skb->dev->stats.rx_bytes += skb->len; + + netif_receive_skb(skb); + + return 0; } static struct packet_type dsa_pack_type __read_mostly = { diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 2a3139921811..107138a55bd8 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -17,8 +17,9 @@ struct dsa_device_ops { struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev); - int (*rcv)(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev); + struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, + struct net_device *orig_dev); }; struct dsa_slave_priv { diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index 263941769c88..2a9b52c5af86 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -92,8 +92,9 @@ out_free: return NULL; } -static int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) +static struct sk_buff *brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, + struct net_device *orig_dev) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_switch *ds; @@ -133,21 +134,12 @@ static int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, skb->data - ETH_HLEN - BRCM_TAG_LEN, 2 * ETH_ALEN); - skb_push(skb, ETH_HLEN); - skb->pkt_type = PACKET_HOST; skb->dev = ds->ports[source_port].netdev; - skb->protocol = eth_type_trans(skb, skb->dev); - skb->dev->stats.rx_packets++; - skb->dev->stats.rx_bytes += skb->len; - - netif_receive_skb(skb); - - return 0; + return skb; out_drop: - kfree_skb(skb); - return 0; + return NULL; } const struct dsa_device_ops brcm_netdev_ops = { diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index b7032699eaad..1c6633f0de01 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -68,8 +68,9 @@ out_free: return NULL; } -static int dsa_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) +static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, + struct net_device *orig_dev) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_switch *ds; @@ -158,20 +159,11 @@ static int dsa_rcv(struct sk_buff *skb, struct net_device *dev, } skb->dev = ds->ports[source_port].netdev; - skb_push(skb, ETH_HLEN); - skb->pkt_type = PACKET_HOST; - skb->protocol = eth_type_trans(skb, skb->dev); - skb->dev->stats.rx_packets++; - skb->dev->stats.rx_bytes += skb->len; - - netif_receive_skb(skb); - - return 0; + return skb; out_drop: - kfree_skb(skb); - return 0; + return NULL; } const struct dsa_device_ops dsa_netdev_ops = { diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c index b87009672b40..d9c668aa5e54 100644 --- a/net/dsa/tag_edsa.c +++ b/net/dsa/tag_edsa.c @@ -81,8 +81,9 @@ out_free: return NULL; } -static int edsa_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) +static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, + struct net_device *orig_dev) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_switch *ds; @@ -177,20 +178,11 @@ static int edsa_rcv(struct sk_buff *skb, struct net_device *dev, } skb->dev = ds->ports[source_port].netdev; - skb_push(skb, ETH_HLEN); - skb->pkt_type = PACKET_HOST; - skb->protocol = eth_type_trans(skb, skb->dev); - skb->dev->stats.rx_packets++; - skb->dev->stats.rx_bytes += skb->len; - - netif_receive_skb(skb); - - return 0; + return skb; out_drop: - kfree_skb(skb); - return 0; + return NULL; } const struct dsa_device_ops edsa_netdev_ops = { diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c index d0a477084870..837cdddb53f0 100644 --- a/net/dsa/tag_mtk.c +++ b/net/dsa/tag_mtk.c @@ -47,8 +47,9 @@ out_free: return NULL; } -static int mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) +static struct sk_buff *mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, + struct net_device *orig_dev) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_switch *ds; @@ -85,23 +86,12 @@ static int mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev, if (!ds->ports[port].netdev) goto out_drop; - /* Update skb & forward the frame accordingly */ - skb_push(skb, ETH_HLEN); - - skb->pkt_type = PACKET_HOST; skb->dev = ds->ports[port].netdev; - skb->protocol = eth_type_trans(skb, skb->dev); - - skb->dev->stats.rx_packets++; - skb->dev->stats.rx_bytes += skb->len; - - netif_receive_skb(skb); - return 0; + return skb; out_drop: - kfree_skb(skb); - return 0; + return NULL; } const struct dsa_device_ops mtk_netdev_ops = { diff --git a/net/dsa/tag_qca.c b/net/dsa/tag_qca.c index d1324649808c..3ba3f59f7a34 100644 --- a/net/dsa/tag_qca.c +++ b/net/dsa/tag_qca.c @@ -66,8 +66,9 @@ out_free: return NULL; } -static int qca_tag_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) +static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, + struct net_device *orig_dev) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_switch *ds; @@ -108,21 +109,12 @@ static int qca_tag_rcv(struct sk_buff *skb, struct net_device *dev, goto out_drop; /* Update skb & forward the frame accordingly */ - skb_push(skb, ETH_HLEN); - skb->pkt_type = PACKET_HOST; skb->dev = ds->ports[port].netdev; - skb->protocol = eth_type_trans(skb, skb->dev); - skb->dev->stats.rx_packets++; - skb->dev->stats.rx_bytes += skb->len; - - netif_receive_skb(skb); - - return 0; + return skb; out_drop: - kfree_skb(skb); - return 0; + return NULL; } const struct dsa_device_ops qca_netdev_ops = { diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c index 1fc0b221a70f..aafc2fc74c30 100644 --- a/net/dsa/tag_trailer.c +++ b/net/dsa/tag_trailer.c @@ -58,8 +58,9 @@ static struct sk_buff *trailer_xmit(struct sk_buff *skb, struct net_device *dev) return nskb; } -static int trailer_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) +static struct sk_buff *trailer_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, + struct net_device *orig_dev) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_switch *ds; @@ -83,20 +84,11 @@ static int trailer_rcv(struct sk_buff *skb, struct net_device *dev, pskb_trim_rcsum(skb, skb->len - 4); skb->dev = ds->ports[source_port].netdev; - skb_push(skb, ETH_HLEN); - skb->pkt_type = PACKET_HOST; - skb->protocol = eth_type_trans(skb, skb->dev); - skb->dev->stats.rx_packets++; - skb->dev->stats.rx_bytes += skb->len; - - netif_receive_skb(skb); - - return 0; + return skb; out_drop: - kfree_skb(skb); - return 0; + return NULL; } const struct dsa_device_ops trailer_netdev_ops = { -- cgit From 4f139972b489f8bc2c821aa25ac65018d92af3f7 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Wed, 5 Apr 2017 09:57:04 +0800 Subject: netfilter: udplite: Remove duplicated udplite4/6 declaration There are two nf_conntrack_l4proto_udp4 declarations in the head file nf_conntrack_ipv4/6.h. Now remove one which is not enbraced by the macro CONFIG_NF_CT_PROTO_UDPLITE. Signed-off-by: Gao Feng --- include/net/netfilter/ipv4/nf_conntrack_ipv4.h | 1 - include/net/netfilter/ipv6/nf_conntrack_ipv6.h | 1 - 2 files changed, 2 deletions(-) diff --git a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h index 6ff32815641b..919e4e8af327 100644 --- a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h +++ b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h @@ -14,7 +14,6 @@ extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4; -extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp; #ifdef CONFIG_NF_CT_PROTO_DCCP extern struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp4; diff --git a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h index c59b82456f89..eaea968f8657 100644 --- a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h +++ b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h @@ -5,7 +5,6 @@ extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6; -extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6; #ifdef CONFIG_NF_CT_PROTO_DCCP extern struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp6; -- cgit From bf74b20d00b13919db7ae5d1015636e76f56f6ae Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 9 Apr 2017 14:45:21 -0700 Subject: Revert "rtnl: Add support for netdev event to link messages" This reverts commit def12888c161e6fec0702e5ec9c3962846e3a21d. As per discussion between Roopa Prabhu and David Ahern, it is advisable that we instead have the code collect the setlink triggered events into a bitmask emitted in the IFLA_EVENT netlink attribute. Signed-off-by: David S. Miller --- include/linux/rtnetlink.h | 3 +- include/uapi/linux/if_link.h | 21 ---------- net/core/dev.c | 2 +- net/core/rtnetlink.c | 92 +++++--------------------------------------- 4 files changed, 11 insertions(+), 107 deletions(-) diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 0459018173cf..57e54847b0b9 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -18,8 +18,7 @@ extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change, gfp_t flags); struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, - unsigned change, unsigned long event, - gfp_t flags); + unsigned change, gfp_t flags); void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, gfp_t flags); diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 97f6d302f627..8b405afb2376 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -157,7 +157,6 @@ enum { IFLA_GSO_MAX_SIZE, IFLA_PAD, IFLA_XDP, - IFLA_EVENT, __IFLA_MAX }; @@ -900,24 +899,4 @@ enum { #define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1) -enum { - IFLA_EVENT_UNSPEC, - IFLA_EVENT_REBOOT, - IFLA_EVENT_CHANGE_MTU, - IFLA_EVENT_CHANGE_ADDR, - IFLA_EVENT_CHANGE_NAME, - IFLA_EVENT_FEAT_CHANGE, - IFLA_EVENT_BONDING_FAILOVER, - IFLA_EVENT_POST_TYPE_CHANGE, - IFLA_EVENT_NOTIFY_PEERS, - IFLA_EVENT_CHANGE_UPPER, - IFLA_EVENT_RESEND_IGMP, - IFLA_EVENT_PRE_CHANGE_MTU, - IFLA_EVENT_CHANGE_INFO_DATA, - IFLA_EVENT_PRE_CHANGE_UPPER, - IFLA_EVENT_CHANGE_LOWER_STATE, - IFLA_EVENT_UDP_TUNNEL_PUSH_INFO, - IFLA_EVENT_CHANGE_TX_QUEUE_LEN, -}; - #endif /* _UAPI_LINUX_IF_LINK_H */ diff --git a/net/core/dev.c b/net/core/dev.c index 7efb4178ffef..ef9fe60ee294 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6840,7 +6840,7 @@ static void rollback_registered_many(struct list_head *head) if (!dev->rtnl_link_ops || dev->rtnl_link_state == RTNL_LINK_INITIALIZED) - skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U, 0, + skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U, GFP_KERNEL); /* diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b2bd4c9ee860..58419da7961b 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -944,7 +944,6 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_SWITCH_ID */ + nla_total_size(IFNAMSIZ) /* IFLA_PHYS_PORT_NAME */ + rtnl_xdp_size(dev) /* IFLA_XDP */ - + nla_total_size(4) /* IFLA_EVENT */ + nla_total_size(1); /* IFLA_PROTO_DOWN */ } @@ -1277,70 +1276,9 @@ err_cancel: return err; } -static int rtnl_fill_link_event(struct sk_buff *skb, unsigned long event) -{ - u32 rtnl_event; - - switch (event) { - case NETDEV_REBOOT: - rtnl_event = IFLA_EVENT_REBOOT; - break; - case NETDEV_CHANGEMTU: - rtnl_event = IFLA_EVENT_CHANGE_MTU; - break; - case NETDEV_CHANGEADDR: - rtnl_event = IFLA_EVENT_CHANGE_ADDR; - break; - case NETDEV_CHANGENAME: - rtnl_event = IFLA_EVENT_CHANGE_NAME; - break; - case NETDEV_FEAT_CHANGE: - rtnl_event = IFLA_EVENT_FEAT_CHANGE; - break; - case NETDEV_BONDING_FAILOVER: - rtnl_event = IFLA_EVENT_BONDING_FAILOVER; - break; - case NETDEV_POST_TYPE_CHANGE: - rtnl_event = IFLA_EVENT_POST_TYPE_CHANGE; - break; - case NETDEV_NOTIFY_PEERS: - rtnl_event = IFLA_EVENT_NOTIFY_PEERS; - break; - case NETDEV_CHANGEUPPER: - rtnl_event = IFLA_EVENT_CHANGE_UPPER; - break; - case NETDEV_RESEND_IGMP: - rtnl_event = IFLA_EVENT_RESEND_IGMP; - break; - case NETDEV_PRECHANGEMTU: - rtnl_event = IFLA_EVENT_PRE_CHANGE_MTU; - break; - case NETDEV_CHANGEINFODATA: - rtnl_event = IFLA_EVENT_CHANGE_INFO_DATA; - break; - case NETDEV_PRECHANGEUPPER: - rtnl_event = IFLA_EVENT_PRE_CHANGE_UPPER; - break; - case NETDEV_CHANGELOWERSTATE: - rtnl_event = IFLA_EVENT_CHANGE_LOWER_STATE; - break; - case NETDEV_UDP_TUNNEL_PUSH_INFO: - rtnl_event = IFLA_EVENT_UDP_TUNNEL_PUSH_INFO; - break; - case NETDEV_CHANGE_TX_QUEUE_LEN: - rtnl_event = IFLA_EVENT_CHANGE_TX_QUEUE_LEN; - break; - default: - return 0; - } - - return nla_put_u32(skb, IFLA_EVENT, rtnl_event); -} - static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, int type, u32 pid, u32 seq, u32 change, - unsigned int flags, u32 ext_filter_mask, - unsigned long event) + unsigned int flags, u32 ext_filter_mask) { struct ifinfomsg *ifm; struct nlmsghdr *nlh; @@ -1389,9 +1327,6 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, nla_put_u8(skb, IFLA_PROTO_DOWN, dev->proto_down)) goto nla_put_failure; - if (rtnl_fill_link_event(skb, event)) - goto nla_put_failure; - if (rtnl_fill_link_ifmap(skb, dev)) goto nla_put_failure; @@ -1526,7 +1461,6 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_LINK_NETNSID] = { .type = NLA_S32 }, [IFLA_PROTO_DOWN] = { .type = NLA_U8 }, [IFLA_XDP] = { .type = NLA_NESTED }, - [IFLA_EVENT] = { .type = NLA_U32 }, }; static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { @@ -1685,7 +1619,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 0, flags, - ext_filter_mask, 0); + ext_filter_mask); /* If we ran out of room on the first message, * we're in trouble */ @@ -2776,7 +2710,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh) return -ENOBUFS; err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).portid, - nlh->nlmsg_seq, 0, 0, ext_filter_mask, 0); + nlh->nlmsg_seq, 0, 0, ext_filter_mask); if (err < 0) { /* -EMSGSIZE implies BUG in if_nlmsg_size */ WARN_ON(err == -EMSGSIZE); @@ -2848,8 +2782,7 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) } struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, - unsigned int change, - unsigned long event, gfp_t flags) + unsigned int change, gfp_t flags) { struct net *net = dev_net(dev); struct sk_buff *skb; @@ -2860,7 +2793,7 @@ struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, if (skb == NULL) goto errout; - err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0, 0, event); + err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0, 0); if (err < 0) { /* -EMSGSIZE implies BUG in if_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); @@ -2881,25 +2814,18 @@ void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, gfp_t flags) rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, flags); } -static void rtmsg_ifinfo_event(int type, struct net_device *dev, - unsigned int change, unsigned long event, - gfp_t flags) +void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change, + gfp_t flags) { struct sk_buff *skb; if (dev->reg_state != NETREG_REGISTERED) return; - skb = rtmsg_ifinfo_build_skb(type, dev, change, event, flags); + skb = rtmsg_ifinfo_build_skb(type, dev, change, flags); if (skb) rtmsg_ifinfo_send(skb, dev, flags); } - -void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change, - gfp_t flags) -{ - rtmsg_ifinfo_event(type, dev, change, 0, flags); -} EXPORT_SYMBOL(rtmsg_ifinfo); static int nlmsg_populate_fdb_fill(struct sk_buff *skb, @@ -4206,7 +4132,7 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_CHANGELOWERSTATE: case NETDEV_UDP_TUNNEL_PUSH_INFO: case NETDEV_CHANGE_TX_QUEUE_LEN: - rtmsg_ifinfo_event(RTM_NEWLINK, dev, 0, event, GFP_KERNEL); + rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL); break; default: break; -- cgit From f9645430ef5f53ddf0ddd481e9f70f6fce7ccff2 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 7 Apr 2017 14:41:19 -0400 Subject: netvsc: use napi_consume_skb This allows using deferred skb freeing and with NAPI. And get buffer recycling. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 7ab06b338a14..fd21d5aab580 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -601,7 +601,8 @@ static inline void netvsc_free_send_slot(struct netvsc_device *net_device, static void netvsc_send_tx_complete(struct netvsc_device *net_device, struct vmbus_channel *incoming_channel, struct hv_device *device, - const struct vmpacket_descriptor *desc) + const struct vmpacket_descriptor *desc, + int budget) { struct sk_buff *skb = (struct sk_buff *)(unsigned long)desc->trans_id; struct net_device *ndev = hv_get_drvdata(device); @@ -628,7 +629,7 @@ static void netvsc_send_tx_complete(struct netvsc_device *net_device, tx_stats->bytes += packet->total_bytes; u64_stats_update_end(&tx_stats->syncp); - dev_consume_skb_any(skb); + napi_consume_skb(skb, budget); } queue_sends = @@ -646,7 +647,8 @@ static void netvsc_send_tx_complete(struct netvsc_device *net_device, static void netvsc_send_completion(struct netvsc_device *net_device, struct vmbus_channel *incoming_channel, struct hv_device *device, - const struct vmpacket_descriptor *desc) + const struct vmpacket_descriptor *desc, + int budget) { struct nvsp_message *nvsp_packet = hv_pkt_data(desc); struct net_device *ndev = hv_get_drvdata(device); @@ -664,7 +666,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device, case NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE: netvsc_send_tx_complete(net_device, incoming_channel, - device, desc); + device, desc, budget); break; default: @@ -1174,14 +1176,16 @@ static int netvsc_process_raw_pkt(struct hv_device *device, struct vmbus_channel *channel, struct netvsc_device *net_device, struct net_device *ndev, - const struct vmpacket_descriptor *desc) + const struct vmpacket_descriptor *desc, + int budget) { struct net_device_context *net_device_ctx = netdev_priv(ndev); struct nvsp_message *nvmsg = hv_pkt_data(desc); switch (desc->type) { case VM_PKT_COMP: - netvsc_send_completion(net_device, channel, device, desc); + netvsc_send_completion(net_device, channel, device, + desc, budget); break; case VM_PKT_DATA_USING_XFER_PAGES: @@ -1230,7 +1234,7 @@ int netvsc_poll(struct napi_struct *napi, int budget) while (nvchan->desc && work_done < budget) { work_done += netvsc_process_raw_pkt(device, channel, net_device, - ndev, nvchan->desc); + ndev, nvchan->desc, budget); nvchan->desc = hv_pkt_iter_next(channel, nvchan->desc); } -- cgit From 3c60a531b9e175693a2d61f6bfd7ffacce4146cd Mon Sep 17 00:00:00 2001 From: Alexander Alemayhu Date: Sat, 8 Apr 2017 22:08:10 +0200 Subject: bpf: fix comment typo o s/bpf_bpf_get_socket_cookie/bpf_get_socket_cookie Signed-off-by: Alexander Alemayhu Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 2 +- tools/include/uapi/linux/bpf.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index a1d95386f562..1e062bb54eec 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -472,7 +472,7 @@ union bpf_attr { * > 0 length of the string including the trailing NUL on success * < 0 error * - * u64 bpf_bpf_get_socket_cookie(skb) + * u64 bpf_get_socket_cookie(skb) * Get the cookie for the socket stored inside sk_buff. * @skb: pointer to skb * Return: 8 Bytes non-decreasing number on success or 0 if the socket diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index a1d95386f562..1e062bb54eec 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -472,7 +472,7 @@ union bpf_attr { * > 0 length of the string including the trailing NUL on success * < 0 error * - * u64 bpf_bpf_get_socket_cookie(skb) + * u64 bpf_get_socket_cookie(skb) * Get the cookie for the socket stored inside sk_buff. * @skb: pointer to skb * Return: 8 Bytes non-decreasing number on success or 0 if the socket -- cgit From eb976a55c744befe8ea8f6a6132d752d2b12cb98 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sat, 8 Apr 2017 08:52:02 -0700 Subject: net: dsa: mt7530: Include gpio/consumer.h for GPIO functions Fixes build errors seen with CONFIG_GPIOLIB disabled and warnings enabled: drivers/net/dsa/mt7530.c: In function 'mt7530_setup': drivers/net/dsa/mt7530.c:948:3: error: implicit declaration of function 'gpiod_set_value_cansleep' [-Werror=implicit-function-declaration] gpiod_set_value_cansleep(priv->reset, 0); ^~~~~~~~~~~~~~~~~~~~~~~~ drivers/net/dsa/mt7530.c: In function 'mt7530_probe': drivers/net/dsa/mt7530.c:1068:17: error: implicit declaration of function 'devm_gpiod_get_optional' [-Werror=implicit-function-declaration] priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset", ^~~~~~~~~~~~~~~~~~~~~~~ drivers/net/dsa/mt7530.c:1069:13: error: 'GPIOD_OUT_LOW' undeclared (first use in this function) GPIOD_OUT_LOW); ^~~~~~~~~~~~~ drivers/net/dsa/mt7530.c:1069:13: Fixes: b8f126a8d543 ("net-next: dsa: add dsa support for Mediatek MT7530 switch") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/mt7530.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index a4f3b0b8c28e..b070c167e70f 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include -- cgit From d3ca8fb180dbec81816299273b291533fa17df71 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 10 Apr 2017 11:15:15 +1000 Subject: ftgmac100: Add a tx timeout handler We have a reset task to reset our chip, use it. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 8447987577bd..0bdc3d3258d3 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1250,6 +1250,17 @@ static int ftgmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int return phy_mii_ioctl(netdev->phydev, ifr, cmd); } +static void ftgmac100_tx_timeout(struct net_device *netdev) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + + /* Disable all interrupts */ + iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); + + /* Do the reset outside of interrupt context */ + schedule_work(&priv->reset_task); +} + static const struct net_device_ops ftgmac100_netdev_ops = { .ndo_open = ftgmac100_open, .ndo_stop = ftgmac100_stop, @@ -1257,6 +1268,7 @@ static const struct net_device_ops ftgmac100_netdev_ops = { .ndo_set_mac_address = ftgmac100_set_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = ftgmac100_do_ioctl, + .ndo_tx_timeout = ftgmac100_tx_timeout, }; static int ftgmac100_setup_mdio(struct net_device *netdev) @@ -1361,6 +1373,7 @@ static int ftgmac100_probe(struct platform_device *pdev) netdev->ethtool_ops = &ftgmac100_ethtool_ops; netdev->netdev_ops = &ftgmac100_netdev_ops; + netdev->watchdog_timeo = 5 * HZ; platform_set_drvdata(pdev, netdev); -- cgit From 58e0c348712adcecf71f52877ba8c80341662076 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 10 Apr 2017 11:15:16 +1000 Subject: ftgmac100: Move ftgmac100_hard_start_xmit() around Move it below ftgmac100_xmit() and the rest of the tx path No code change. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 58 ++++++++++++++++---------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 0bdc3d3258d3..8231d972de16 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -675,6 +675,35 @@ static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb, return NETDEV_TX_OK; } +static int ftgmac100_hard_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + dma_addr_t map; + + if (unlikely(skb->len > MAX_PKT_SIZE)) { + if (net_ratelimit()) + netdev_dbg(netdev, "tx packet too big\n"); + + netdev->stats.tx_dropped++; + kfree_skb(skb); + return NETDEV_TX_OK; + } + + map = dma_map_single(priv->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(priv->dev, map))) { + /* drop packet */ + if (net_ratelimit()) + netdev_err(netdev, "map socket buffer failed\n"); + + netdev->stats.tx_dropped++; + kfree_skb(skb); + return NETDEV_TX_OK; + } + + return ftgmac100_xmit(priv, skb, map); +} + static void ftgmac100_free_buffers(struct ftgmac100 *priv) { int i; @@ -1212,35 +1241,6 @@ static int ftgmac100_stop(struct net_device *netdev) return 0; } -static int ftgmac100_hard_start_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct ftgmac100 *priv = netdev_priv(netdev); - dma_addr_t map; - - if (unlikely(skb->len > MAX_PKT_SIZE)) { - if (net_ratelimit()) - netdev_dbg(netdev, "tx packet too big\n"); - - netdev->stats.tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; - } - - map = dma_map_single(priv->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(priv->dev, map))) { - /* drop packet */ - if (net_ratelimit()) - netdev_err(netdev, "map socket buffer failed\n"); - - netdev->stats.tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; - } - - return ftgmac100_xmit(priv, skb, map); -} - /* optional */ static int ftgmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { -- cgit From 43b25ee712f72ec3ad03285413b5515619744eeb Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 10 Apr 2017 11:15:17 +1000 Subject: ftgmac100: Merge ftgmac100_xmit() into ftgmac100_hard_start_xmit() This will make subsequent rework of the tx path simpler Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 58 ++++++++++++++------------------ 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 8231d972de16..18b021709286 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -629,12 +629,33 @@ static void ftgmac100_tx_complete(struct ftgmac100 *priv) ; } -static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb, - dma_addr_t map) +static int ftgmac100_hard_start_xmit(struct sk_buff *skb, + struct net_device *netdev) { - struct net_device *netdev = priv->netdev; - struct ftgmac100_txdes *txdes; unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; + struct ftgmac100 *priv = netdev_priv(netdev); + struct ftgmac100_txdes *txdes; + dma_addr_t map; + + if (unlikely(skb->len > MAX_PKT_SIZE)) { + if (net_ratelimit()) + netdev_dbg(netdev, "tx packet too big\n"); + + netdev->stats.tx_dropped++; + kfree_skb(skb); + return NETDEV_TX_OK; + } + + map = dma_map_single(priv->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(priv->dev, map))) { + /* drop packet */ + if (net_ratelimit()) + netdev_err(netdev, "map socket buffer failed\n"); + + netdev->stats.tx_dropped++; + kfree_skb(skb); + return NETDEV_TX_OK; + } txdes = ftgmac100_current_txdes(priv); ftgmac100_tx_pointer_advance(priv); @@ -675,35 +696,6 @@ static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb, return NETDEV_TX_OK; } -static int ftgmac100_hard_start_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct ftgmac100 *priv = netdev_priv(netdev); - dma_addr_t map; - - if (unlikely(skb->len > MAX_PKT_SIZE)) { - if (net_ratelimit()) - netdev_dbg(netdev, "tx packet too big\n"); - - netdev->stats.tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; - } - - map = dma_map_single(priv->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(priv->dev, map))) { - /* drop packet */ - if (net_ratelimit()) - netdev_err(netdev, "map socket buffer failed\n"); - - netdev->stats.tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; - } - - return ftgmac100_xmit(priv, skb, map); -} - static void ftgmac100_free_buffers(struct ftgmac100 *priv) { int i; -- cgit From 3e427a3363edbc8416c36815c2f7de4621739273 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 10 Apr 2017 11:15:18 +1000 Subject: ftgmac100: Factor tx packet dropping path Use a simple goto to a drop path at the tail of the function, it will be used in a few more cases soon Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 18b021709286..c8162d21c142 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -640,10 +640,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, if (unlikely(skb->len > MAX_PKT_SIZE)) { if (net_ratelimit()) netdev_dbg(netdev, "tx packet too big\n"); - - netdev->stats.tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; + goto drop; } map = dma_map_single(priv->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); @@ -651,10 +648,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, /* drop packet */ if (net_ratelimit()) netdev_err(netdev, "map socket buffer failed\n"); - - netdev->stats.tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; + goto drop; } txdes = ftgmac100_current_txdes(priv); @@ -693,6 +687,13 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, ftgmac100_txdma_normal_prio_start_polling(priv); + return NETDEV_TX_OK; + + drop: + /* Drop the packet */ + dev_kfree_skb_any(skb); + netdev->stats.tx_dropped++; + return NETDEV_TX_OK; } -- cgit From 9b0f7711d92bf44b727751a87373089a92031918 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 10 Apr 2017 11:15:19 +1000 Subject: ftgmac100: Pad small frames properly Rather than just transmitting garbage past the end of the small packet. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index c8162d21c142..6325e326147d 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -632,11 +632,17 @@ static void ftgmac100_tx_complete(struct ftgmac100 *priv) static int ftgmac100_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev) { - unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; struct ftgmac100 *priv = netdev_priv(netdev); struct ftgmac100_txdes *txdes; dma_addr_t map; + /* The HW doesn't pad small frames */ + if (eth_skb_pad(skb)) { + netdev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + + /* Reject oversize packets */ if (unlikely(skb->len > MAX_PKT_SIZE)) { if (net_ratelimit()) netdev_dbg(netdev, "tx packet too big\n"); @@ -657,7 +663,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, /* setup TX descriptor */ ftgmac100_txdes_set_skb(txdes, skb); ftgmac100_txdes_set_dma_addr(txdes, map); - ftgmac100_txdes_set_buffer_size(txdes, len); + ftgmac100_txdes_set_buffer_size(txdes, skb->len); ftgmac100_txdes_set_first_segment(txdes); ftgmac100_txdes_set_last_segment(txdes); -- cgit From 83617317d2136ca18265955de7782c33be27ff62 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 10 Apr 2017 11:15:20 +1000 Subject: ftgmac100: Store tx skbs in a separate array Rather than in the descriptor. The descriptor is mapped non-cachable and rather slow to access. Since to do that we need to keep track of the tx "pointer" we also have no use of all the accesors to manipulate it, just open code it, it's as clear and will help when adding fragmented sends. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 59 +++++++++----------------------- 1 file changed, 16 insertions(+), 43 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 6325e326147d..b4d84e753187 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -65,6 +65,7 @@ struct ftgmac100 { u32 rxdes0_edorr_mask; /* Tx ring */ + struct sk_buff *tx_skbs[TX_QUEUE_ENTRIES]; unsigned int tx_clean_pointer; unsigned int tx_pointer; unsigned int tx_pending; @@ -545,63 +546,29 @@ static dma_addr_t ftgmac100_txdes_get_dma_addr(struct ftgmac100_txdes *txdes) return le32_to_cpu(txdes->txdes3); } -/* - * txdes2 is not used by hardware. We use it to keep track of socket buffer. - * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). - */ -static void ftgmac100_txdes_set_skb(struct ftgmac100_txdes *txdes, - struct sk_buff *skb) -{ - txdes->txdes2 = (unsigned int)skb; -} - -static struct sk_buff *ftgmac100_txdes_get_skb(struct ftgmac100_txdes *txdes) -{ - return (struct sk_buff *)txdes->txdes2; -} - static int ftgmac100_next_tx_pointer(int pointer) { return (pointer + 1) & (TX_QUEUE_ENTRIES - 1); } -static void ftgmac100_tx_pointer_advance(struct ftgmac100 *priv) -{ - priv->tx_pointer = ftgmac100_next_tx_pointer(priv->tx_pointer); -} - -static void ftgmac100_tx_clean_pointer_advance(struct ftgmac100 *priv) -{ - priv->tx_clean_pointer = ftgmac100_next_tx_pointer(priv->tx_clean_pointer); -} - -static struct ftgmac100_txdes *ftgmac100_current_txdes(struct ftgmac100 *priv) -{ - return &priv->descs->txdes[priv->tx_pointer]; -} - -static struct ftgmac100_txdes * -ftgmac100_current_clean_txdes(struct ftgmac100 *priv) -{ - return &priv->descs->txdes[priv->tx_clean_pointer]; -} - static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) { struct net_device *netdev = priv->netdev; struct ftgmac100_txdes *txdes; + unsigned int pointer; struct sk_buff *skb; dma_addr_t map; if (priv->tx_pending == 0) return false; - txdes = ftgmac100_current_clean_txdes(priv); + pointer = priv->tx_clean_pointer; + txdes = &priv->descs->txdes[pointer]; if (ftgmac100_txdes_owned_by_dma(txdes)) return false; - skb = ftgmac100_txdes_get_skb(txdes); + skb = priv->tx_skbs[pointer]; map = ftgmac100_txdes_get_dma_addr(txdes); netdev->stats.tx_packets++; @@ -610,10 +577,11 @@ static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); dev_kfree_skb(skb); + priv->tx_skbs[pointer] = NULL; ftgmac100_txdes_reset(priv, txdes); - ftgmac100_tx_clean_pointer_advance(priv); + priv->tx_clean_pointer = ftgmac100_next_tx_pointer(pointer); spin_lock(&priv->tx_lock); priv->tx_pending--; @@ -634,6 +602,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, { struct ftgmac100 *priv = netdev_priv(netdev); struct ftgmac100_txdes *txdes; + unsigned int pointer; dma_addr_t map; /* The HW doesn't pad small frames */ @@ -657,11 +626,12 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, goto drop; } - txdes = ftgmac100_current_txdes(priv); - ftgmac100_tx_pointer_advance(priv); + /* Grab the next free tx descriptor */ + pointer = priv->tx_pointer; + txdes = &priv->descs->txdes[pointer]; /* setup TX descriptor */ - ftgmac100_txdes_set_skb(txdes, skb); + priv->tx_skbs[pointer] = skb; ftgmac100_txdes_set_dma_addr(txdes, map); ftgmac100_txdes_set_buffer_size(txdes, skb->len); @@ -682,6 +652,9 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, } } + /* Update next TX pointer */ + priv->tx_pointer = ftgmac100_next_tx_pointer(pointer); + spin_lock(&priv->tx_lock); priv->tx_pending++; if (priv->tx_pending == TX_QUEUE_ENTRIES) @@ -724,7 +697,7 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) /* Free all TX buffers */ for (i = 0; i < TX_QUEUE_ENTRIES; i++) { struct ftgmac100_txdes *txdes = &priv->descs->txdes[i]; - struct sk_buff *skb = ftgmac100_txdes_get_skb(txdes); + struct sk_buff *skb = priv->tx_skbs[i]; dma_addr_t map = ftgmac100_txdes_get_dma_addr(txdes); if (!skb) -- cgit From 6ad3d7edcbdae233a24f67a6713f461b2325e5cc Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 10 Apr 2017 11:15:21 +1000 Subject: ftgmac100: Cleanup tx queue handling We have a private lock which isn't terribly useful, and we maintain a "tx_pending" counter for information that's already available via a trivial arithmetic operation. Then we unconditionaly wake the queue even when not stopped. Finally our code in tx isn't really safe vs. a concurrent reclaim. The aspeed chips aren't SMP today but I prefer the code being right and future proof. So rip that out and replace it with more "standard" queue handling, currently with a threshold of 1 queue element, which will be increased when we implement fragmented sends. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 103 ++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index b4d84e753187..add7462bf67d 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -46,6 +46,9 @@ #define MAX_PKT_SIZE 1536 #define RX_BUF_SIZE MAX_PKT_SIZE /* must be smaller than 0x3fff */ +/* Min number of tx ring entries before stopping queue */ +#define TX_THRESHOLD (1) + struct ftgmac100_descs { struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES]; struct ftgmac100_txdes txdes[TX_QUEUE_ENTRIES]; @@ -68,9 +71,7 @@ struct ftgmac100 { struct sk_buff *tx_skbs[TX_QUEUE_ENTRIES]; unsigned int tx_clean_pointer; unsigned int tx_pointer; - unsigned int tx_pending; u32 txdes0_edotr_mask; - spinlock_t tx_lock; /* Scratch page to use when rx skb alloc fails */ void *rx_scratch; @@ -165,7 +166,6 @@ static int ftgmac100_reset_and_config_mac(struct ftgmac100 *priv) priv->rx_pointer = 0; priv->tx_clean_pointer = 0; priv->tx_pointer = 0; - priv->tx_pending = 0; /* The doc says reset twice with 10us interval */ if (ftgmac100_reset_mac(priv, maccr)) @@ -551,6 +551,23 @@ static int ftgmac100_next_tx_pointer(int pointer) return (pointer + 1) & (TX_QUEUE_ENTRIES - 1); } +static u32 ftgmac100_tx_buf_avail(struct ftgmac100 *priv) +{ + /* Returns the number of available slots in the TX queue + * + * This always leaves one free slot so we don't have to + * worry about empty vs. full, and this simplifies the + * test for ftgmac100_tx_buf_cleanable() below + */ + return (priv->tx_clean_pointer - priv->tx_pointer - 1) & + (TX_QUEUE_ENTRIES - 1); +} + +static bool ftgmac100_tx_buf_cleanable(struct ftgmac100 *priv) +{ + return priv->tx_pointer != priv->tx_clean_pointer; +} + static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) { struct net_device *netdev = priv->netdev; @@ -559,9 +576,6 @@ static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) struct sk_buff *skb; dma_addr_t map; - if (priv->tx_pending == 0) - return false; - pointer = priv->tx_clean_pointer; txdes = &priv->descs->txdes[pointer]; @@ -583,18 +597,31 @@ static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) priv->tx_clean_pointer = ftgmac100_next_tx_pointer(pointer); - spin_lock(&priv->tx_lock); - priv->tx_pending--; - spin_unlock(&priv->tx_lock); - netif_wake_queue(netdev); - return true; } static void ftgmac100_tx_complete(struct ftgmac100 *priv) { - while (ftgmac100_tx_complete_packet(priv)) + struct net_device *netdev = priv->netdev; + + /* Process all completed packets */ + while (ftgmac100_tx_buf_cleanable(priv) && + ftgmac100_tx_complete_packet(priv)) ; + + /* Restart queue if needed */ + smp_mb(); + if (unlikely(netif_queue_stopped(netdev) && + ftgmac100_tx_buf_avail(priv) >= TX_THRESHOLD)) { + struct netdev_queue *txq; + + txq = netdev_get_tx_queue(netdev, 0); + __netif_tx_lock(txq, smp_processor_id()); + if (netif_queue_stopped(netdev) && + ftgmac100_tx_buf_avail(priv) >= TX_THRESHOLD) + netif_wake_queue(netdev); + __netif_tx_unlock(txq); + } } static int ftgmac100_hard_start_xmit(struct sk_buff *skb, @@ -652,17 +679,22 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, } } + ftgmac100_txdes_set_dma_own(txdes); + /* Update next TX pointer */ priv->tx_pointer = ftgmac100_next_tx_pointer(pointer); - spin_lock(&priv->tx_lock); - priv->tx_pending++; - if (priv->tx_pending == TX_QUEUE_ENTRIES) + /* If there isn't enough room for all the fragments of a new packet + * in the TX ring, stop the queue. The sequence below is race free + * vs. a concurrent restart in ftgmac100_poll() + */ + if (unlikely(ftgmac100_tx_buf_avail(priv) < TX_THRESHOLD)) { netif_stop_queue(netdev); - - /* start transmit */ - ftgmac100_txdes_set_dma_own(txdes); - spin_unlock(&priv->tx_lock); + /* Order the queue stop with the test below */ + smp_mb(); + if (ftgmac100_tx_buf_avail(priv) >= TX_THRESHOLD) + netif_wake_queue(netdev); + } ftgmac100_txdma_normal_prio_start_polling(priv); @@ -980,17 +1012,17 @@ static bool ftgmac100_check_rx(struct ftgmac100 *priv) static int ftgmac100_poll(struct napi_struct *napi, int budget) { struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi); - bool more, completed = true; - int rx = 0; + int work_done = 0; + bool more; - ftgmac100_tx_complete(priv); + /* Handle TX completions */ + if (ftgmac100_tx_buf_cleanable(priv)) + ftgmac100_tx_complete(priv); + /* Handle RX packets */ do { - more = ftgmac100_rx_packet(priv, &rx); - } while (more && rx < budget); - - if (more && rx == budget) - completed = false; + more = ftgmac100_rx_packet(priv, &work_done); + } while (more && work_done < budget); /* The interrupt is telling us to kick the MAC back to life @@ -1004,11 +1036,13 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget) priv->base + FTGMAC100_OFFSET_IER); } - /* Keep NAPI going if we have still packets to reclaim */ - if (priv->tx_pending) - return budget; + /* As long as we are waiting for transmit packets to be + * completed we keep NAPI going + */ + if (ftgmac100_tx_buf_cleanable(priv)) + work_done = budget; - if (completed) { + if (work_done < budget) { /* We are about to re-enable all interrupts. However * the HW has been latching RX/TX packet interrupts while * they were masked. So we clear them first, then we need @@ -1016,7 +1050,8 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget) */ iowrite32(FTGMAC100_INT_RXTX, priv->base + FTGMAC100_OFFSET_ISR); - if (ftgmac100_check_rx(priv) || priv->tx_pending) + if (ftgmac100_check_rx(priv) || + ftgmac100_tx_buf_cleanable(priv)) return budget; /* deschedule NAPI */ @@ -1027,7 +1062,7 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget) priv->base + FTGMAC100_OFFSET_IER); } - return rx; + return work_done; } static int ftgmac100_init_all(struct ftgmac100 *priv, bool ignore_alloc_err) @@ -1355,8 +1390,6 @@ static int ftgmac100_probe(struct platform_device *pdev) priv->dev = &pdev->dev; INIT_WORK(&priv->reset_task, ftgmac100_reset_task); - spin_lock_init(&priv->tx_lock); - /* map io memory */ priv->res = request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev)); -- cgit From 4a2712b2f0b6895f37c657e099936679d2bc7b6e Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 10 Apr 2017 11:15:22 +1000 Subject: ftgmac100: Move the barrier out of ftgmac100_txdes_set_dma_own() We'll use variants of this accessor without barriers when building series of descriptors for fragmented sends Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index add7462bf67d..60f285e444f8 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -485,11 +485,6 @@ static bool ftgmac100_txdes_owned_by_dma(struct ftgmac100_txdes *txdes) static void ftgmac100_txdes_set_dma_own(struct ftgmac100_txdes *txdes) { - /* - * Make sure dma own bit will not be set before any other - * descriptor fields. - */ - wmb(); txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN); } @@ -679,6 +674,10 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, } } + /* Order the previous packet and descriptor udpates + * before setting the OWN bit. + */ + dma_wmb(); ftgmac100_txdes_set_dma_own(txdes); /* Update next TX pointer */ -- cgit From 42c2d19786e538dc33d365d3439eec00545651cc Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 10 Apr 2017 11:15:23 +1000 Subject: ftgmac100: Split tx packet freeing from ftgmac100_tx_complete_packet() This moves the packet freeing to a separate function which is also used by ftgmac100_free_buffers() and will be used more in the error path of fragmented sends. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 38 ++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 60f285e444f8..802f5e73006c 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -563,13 +563,29 @@ static bool ftgmac100_tx_buf_cleanable(struct ftgmac100 *priv) return priv->tx_pointer != priv->tx_clean_pointer; } +static void ftgmac100_free_tx_packet(struct ftgmac100 *priv, + unsigned int pointer, + struct sk_buff *skb, + struct ftgmac100_txdes *txdes) +{ + dma_addr_t map; + + map = ftgmac100_txdes_get_dma_addr(txdes); + + dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); + + dev_kfree_skb(skb); + priv->tx_skbs[pointer] = NULL; + + ftgmac100_txdes_reset(priv, txdes); +} + static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) { struct net_device *netdev = priv->netdev; struct ftgmac100_txdes *txdes; - unsigned int pointer; struct sk_buff *skb; - dma_addr_t map; + unsigned int pointer; pointer = priv->tx_clean_pointer; txdes = &priv->descs->txdes[pointer]; @@ -578,17 +594,9 @@ static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) return false; skb = priv->tx_skbs[pointer]; - map = ftgmac100_txdes_get_dma_addr(txdes); - netdev->stats.tx_packets++; netdev->stats.tx_bytes += skb->len; - - dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); - - dev_kfree_skb(skb); - priv->tx_skbs[pointer] = NULL; - - ftgmac100_txdes_reset(priv, txdes); + ftgmac100_free_tx_packet(priv, pointer, skb, txdes); priv->tx_clean_pointer = ftgmac100_next_tx_pointer(pointer); @@ -729,13 +737,9 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) for (i = 0; i < TX_QUEUE_ENTRIES; i++) { struct ftgmac100_txdes *txdes = &priv->descs->txdes[i]; struct sk_buff *skb = priv->tx_skbs[i]; - dma_addr_t map = ftgmac100_txdes_get_dma_addr(txdes); - - if (!skb) - continue; - dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); - kfree_skb(skb); + if (skb) + ftgmac100_free_tx_packet(priv, i, skb, txdes); } } -- cgit From e92455397237c4e680302950fe1d7970b6c063a0 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 10 Apr 2017 11:15:24 +1000 Subject: ftgmac100: Don't clear tx desc fields unnecessarily Those are non-cachable stores, let's avoid those we don't need. Remove the helper, it's not particularly helpful and since it uses "priv" I can't move it to the header file. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 802f5e73006c..d303c597e9b8 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -468,16 +468,6 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) return true; } -static void ftgmac100_txdes_reset(const struct ftgmac100 *priv, - struct ftgmac100_txdes *txdes) -{ - /* clear all except end of ring bit */ - txdes->txdes0 &= cpu_to_le32(priv->txdes0_edotr_mask); - txdes->txdes1 = 0; - txdes->txdes2 = 0; - txdes->txdes3 = 0; -} - static bool ftgmac100_txdes_owned_by_dma(struct ftgmac100_txdes *txdes) { return txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN); @@ -577,7 +567,12 @@ static void ftgmac100_free_tx_packet(struct ftgmac100 *priv, dev_kfree_skb(skb); priv->tx_skbs[pointer] = NULL; - ftgmac100_txdes_reset(priv, txdes); + /* Clear txdes0 except end of ring bit, clear txdes1 as we + * only "OR" into it, leave 2 and 3 alone as 2 is unused + * and 3 will be overwritten entirely + */ + txdes->txdes0 &= cpu_to_le32(priv->txdes0_edotr_mask); + txdes->txdes1 = 0; } static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) -- cgit From 6db7470445f0757d2e8f23f57d86611338717ebe Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 10 Apr 2017 11:15:25 +1000 Subject: ftgmac100: Add support for fragmented tx Add NETIF_F_SG and create multiple TX ring entries for skb fragments. On reclaim, the skb is only freed on the segment marked as "last". Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 119 ++++++++++++++++++++++++------- 1 file changed, 94 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index d303c597e9b8..abe2442673a0 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -47,7 +47,7 @@ #define RX_BUF_SIZE MAX_PKT_SIZE /* must be smaller than 0x3fff */ /* Min number of tx ring entries before stopping queue */ -#define TX_THRESHOLD (1) +#define TX_THRESHOLD (MAX_SKB_FRAGS + 1) struct ftgmac100_descs { struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES]; @@ -489,20 +489,30 @@ static void ftgmac100_txdes_set_first_segment(struct ftgmac100_txdes *txdes) txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_FTS); } +static inline bool ftgmac100_txdes_get_first_segment(struct ftgmac100_txdes *txdes) +{ + return (txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_FTS)) != 0; +} + static void ftgmac100_txdes_set_last_segment(struct ftgmac100_txdes *txdes) { txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_LTS); } +static inline bool ftgmac100_txdes_get_last_segment(struct ftgmac100_txdes *txdes) +{ + return (txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_LTS)) != 0; +} + static void ftgmac100_txdes_set_buffer_size(struct ftgmac100_txdes *txdes, unsigned int len) { txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXBUF_SIZE(len)); } -static void ftgmac100_txdes_set_txint(struct ftgmac100_txdes *txdes) +static inline unsigned int ftgmac100_txdes_get_buffer_size(struct ftgmac100_txdes *txdes) { - txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TXIC); + return FTGMAC100_TXDES0_TXBUF_SIZE(cpu_to_le32(txdes->txdes0)); } static void ftgmac100_txdes_set_tcpcs(struct ftgmac100_txdes *txdes) @@ -528,7 +538,7 @@ static void ftgmac100_txdes_set_dma_addr(struct ftgmac100_txdes *txdes, static dma_addr_t ftgmac100_txdes_get_dma_addr(struct ftgmac100_txdes *txdes) { - return le32_to_cpu(txdes->txdes3); + return (dma_addr_t)le32_to_cpu(txdes->txdes3); } static int ftgmac100_next_tx_pointer(int pointer) @@ -558,13 +568,19 @@ static void ftgmac100_free_tx_packet(struct ftgmac100 *priv, struct sk_buff *skb, struct ftgmac100_txdes *txdes) { - dma_addr_t map; + dma_addr_t map = ftgmac100_txdes_get_dma_addr(txdes); - map = ftgmac100_txdes_get_dma_addr(txdes); - - dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); + if (ftgmac100_txdes_get_first_segment(txdes)) { + dma_unmap_single(priv->dev, map, skb_headlen(skb), + DMA_TO_DEVICE); + } else { + dma_unmap_page(priv->dev, map, + ftgmac100_txdes_get_buffer_size(txdes), + DMA_TO_DEVICE); + } - dev_kfree_skb(skb); + if (ftgmac100_txdes_get_last_segment(txdes)) + dev_kfree_skb(skb); priv->tx_skbs[pointer] = NULL; /* Clear txdes0 except end of ring bit, clear txdes1 as we @@ -626,8 +642,8 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev) { struct ftgmac100 *priv = netdev_priv(netdev); - struct ftgmac100_txdes *txdes; - unsigned int pointer; + struct ftgmac100_txdes *txdes, *first; + unsigned int pointer, nfrags, len, i, j; dma_addr_t map; /* The HW doesn't pad small frames */ @@ -643,26 +659,33 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, goto drop; } - map = dma_map_single(priv->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(priv->dev, map))) { - /* drop packet */ + /* Do we have a limit on #fragments ? I yet have to get a reply + * from Aspeed. If there's one I haven't hit it. + */ + nfrags = skb_shinfo(skb)->nr_frags; + + /* Get header len */ + len = skb_headlen(skb); + + /* Map the packet head */ + map = dma_map_single(priv->dev, skb->data, len, DMA_TO_DEVICE); + if (dma_mapping_error(priv->dev, map)) { if (net_ratelimit()) - netdev_err(netdev, "map socket buffer failed\n"); + netdev_err(netdev, "map tx packet head failed\n"); goto drop; } /* Grab the next free tx descriptor */ pointer = priv->tx_pointer; - txdes = &priv->descs->txdes[pointer]; + txdes = first = &priv->descs->txdes[pointer]; - /* setup TX descriptor */ + /* Setup it up with the packet head. We don't set the OWN bit yet. */ priv->tx_skbs[pointer] = skb; ftgmac100_txdes_set_dma_addr(txdes, map); - ftgmac100_txdes_set_buffer_size(txdes, skb->len); - + ftgmac100_txdes_set_buffer_size(txdes, len); ftgmac100_txdes_set_first_segment(txdes); - ftgmac100_txdes_set_last_segment(txdes); - ftgmac100_txdes_set_txint(txdes); + + /* Setup HW checksumming */ if (skb->ip_summed == CHECKSUM_PARTIAL) { __be16 protocol = skb->protocol; @@ -677,14 +700,41 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, } } + /* Next descriptor */ + pointer = ftgmac100_next_tx_pointer(pointer); + + /* Add the fragments */ + for (i = 0; i < nfrags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + len = frag->size; + + /* Map it */ + map = skb_frag_dma_map(priv->dev, frag, 0, len, + DMA_TO_DEVICE); + if (dma_mapping_error(priv->dev, map)) + goto dma_err; + + /* Setup descriptor */ + priv->tx_skbs[pointer] = skb; + txdes = &priv->descs->txdes[pointer]; + ftgmac100_txdes_set_dma_addr(txdes, map); + ftgmac100_txdes_set_buffer_size(txdes, len); + ftgmac100_txdes_set_dma_own(txdes); + pointer = ftgmac100_next_tx_pointer(pointer); + } + + /* Tag last fragment */ + ftgmac100_txdes_set_last_segment(txdes); + /* Order the previous packet and descriptor udpates * before setting the OWN bit. */ dma_wmb(); - ftgmac100_txdes_set_dma_own(txdes); + ftgmac100_txdes_set_dma_own(first); /* Update next TX pointer */ - priv->tx_pointer = ftgmac100_next_tx_pointer(pointer); + priv->tx_pointer = pointer; /* If there isn't enough room for all the fragments of a new packet * in the TX ring, stop the queue. The sequence below is race free @@ -702,6 +752,25 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; + dma_err: + if (net_ratelimit()) + netdev_err(netdev, "map tx fragment failed\n"); + + /* Free head */ + pointer = priv->tx_pointer; + ftgmac100_free_tx_packet(priv, pointer, skb, first); + + /* Then all fragments */ + for (j = 0; j < i; j++) { + pointer = ftgmac100_next_tx_pointer(pointer); + txdes = &priv->descs->txdes[pointer]; + ftgmac100_free_tx_packet(priv, pointer, skb, txdes); + } + + /* This cannot be reached if we successfully mapped the + * last fragment, so we know ftgmac100_free_tx_packet() + * hasn't freed the skb yet. + */ drop: /* Drop the packet */ dev_kfree_skb_any(skb); @@ -1441,12 +1510,12 @@ static int ftgmac100_probe(struct platform_device *pdev) * when NCSI is enabled on the interface. It doesn't work * in that case. */ - netdev->features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_GRO; + netdev->features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | + NETIF_F_GRO | NETIF_F_SG; if (priv->use_ncsi && of_get_property(pdev->dev.of_node, "no-hw-checksum", NULL)) netdev->features &= ~NETIF_F_IP_CSUM; - /* register network device */ err = register_netdev(netdev); if (err) { -- cgit From 52c0cae8746513e64198c56f0cd74eacaee21be1 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 10 Apr 2017 11:15:26 +1000 Subject: ftgmac100: Remove tx descriptor accessors Directly access the fields when needed. The accessors add clutter not clarity and in some cases cause unnecessary read-modify-write type access on the slow (uncached) descriptor memory. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 175 ++++++++++++------------------- drivers/net/ethernet/faraday/ftgmac100.h | 8 +- 2 files changed, 69 insertions(+), 114 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index abe2442673a0..7156f9ff6b7e 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -468,77 +468,13 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) return true; } -static bool ftgmac100_txdes_owned_by_dma(struct ftgmac100_txdes *txdes) +static u32 ftgmac100_base_tx_ctlstat(struct ftgmac100 *priv, + unsigned int index) { - return txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN); -} - -static void ftgmac100_txdes_set_dma_own(struct ftgmac100_txdes *txdes) -{ - txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN); -} - -static void ftgmac100_txdes_set_end_of_ring(const struct ftgmac100 *priv, - struct ftgmac100_txdes *txdes) -{ - txdes->txdes0 |= cpu_to_le32(priv->txdes0_edotr_mask); -} - -static void ftgmac100_txdes_set_first_segment(struct ftgmac100_txdes *txdes) -{ - txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_FTS); -} - -static inline bool ftgmac100_txdes_get_first_segment(struct ftgmac100_txdes *txdes) -{ - return (txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_FTS)) != 0; -} - -static void ftgmac100_txdes_set_last_segment(struct ftgmac100_txdes *txdes) -{ - txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_LTS); -} - -static inline bool ftgmac100_txdes_get_last_segment(struct ftgmac100_txdes *txdes) -{ - return (txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_LTS)) != 0; -} - -static void ftgmac100_txdes_set_buffer_size(struct ftgmac100_txdes *txdes, - unsigned int len) -{ - txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXBUF_SIZE(len)); -} - -static inline unsigned int ftgmac100_txdes_get_buffer_size(struct ftgmac100_txdes *txdes) -{ - return FTGMAC100_TXDES0_TXBUF_SIZE(cpu_to_le32(txdes->txdes0)); -} - -static void ftgmac100_txdes_set_tcpcs(struct ftgmac100_txdes *txdes) -{ - txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TCP_CHKSUM); -} - -static void ftgmac100_txdes_set_udpcs(struct ftgmac100_txdes *txdes) -{ - txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_UDP_CHKSUM); -} - -static void ftgmac100_txdes_set_ipcs(struct ftgmac100_txdes *txdes) -{ - txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_IP_CHKSUM); -} - -static void ftgmac100_txdes_set_dma_addr(struct ftgmac100_txdes *txdes, - dma_addr_t addr) -{ - txdes->txdes3 = cpu_to_le32(addr); -} - -static dma_addr_t ftgmac100_txdes_get_dma_addr(struct ftgmac100_txdes *txdes) -{ - return (dma_addr_t)le32_to_cpu(txdes->txdes3); + if (index == (TX_QUEUE_ENTRIES - 1)) + return priv->txdes0_edotr_mask; + else + return 0; } static int ftgmac100_next_tx_pointer(int pointer) @@ -566,29 +502,24 @@ static bool ftgmac100_tx_buf_cleanable(struct ftgmac100 *priv) static void ftgmac100_free_tx_packet(struct ftgmac100 *priv, unsigned int pointer, struct sk_buff *skb, - struct ftgmac100_txdes *txdes) + struct ftgmac100_txdes *txdes, + u32 ctl_stat) { - dma_addr_t map = ftgmac100_txdes_get_dma_addr(txdes); + dma_addr_t map = le32_to_cpu(txdes->txdes3); + size_t len; - if (ftgmac100_txdes_get_first_segment(txdes)) { - dma_unmap_single(priv->dev, map, skb_headlen(skb), - DMA_TO_DEVICE); + if (ctl_stat & FTGMAC100_TXDES0_FTS) { + len = skb_headlen(skb); + dma_unmap_single(priv->dev, map, len, DMA_TO_DEVICE); } else { - dma_unmap_page(priv->dev, map, - ftgmac100_txdes_get_buffer_size(txdes), - DMA_TO_DEVICE); + len = FTGMAC100_TXDES0_TXBUF_SIZE(ctl_stat); + dma_unmap_page(priv->dev, map, len, DMA_TO_DEVICE); } - if (ftgmac100_txdes_get_last_segment(txdes)) + /* Free SKB on last segment */ + if (ctl_stat & FTGMAC100_TXDES0_LTS) dev_kfree_skb(skb); priv->tx_skbs[pointer] = NULL; - - /* Clear txdes0 except end of ring bit, clear txdes1 as we - * only "OR" into it, leave 2 and 3 alone as 2 is unused - * and 3 will be overwritten entirely - */ - txdes->txdes0 &= cpu_to_le32(priv->txdes0_edotr_mask); - txdes->txdes1 = 0; } static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) @@ -597,17 +528,20 @@ static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) struct ftgmac100_txdes *txdes; struct sk_buff *skb; unsigned int pointer; + u32 ctl_stat; pointer = priv->tx_clean_pointer; txdes = &priv->descs->txdes[pointer]; - if (ftgmac100_txdes_owned_by_dma(txdes)) + ctl_stat = le32_to_cpu(txdes->txdes0); + if (ctl_stat & FTGMAC100_TXDES0_TXDMA_OWN) return false; skb = priv->tx_skbs[pointer]; netdev->stats.tx_packets++; netdev->stats.tx_bytes += skb->len; - ftgmac100_free_tx_packet(priv, pointer, skb, txdes); + ftgmac100_free_tx_packet(priv, pointer, skb, txdes, ctl_stat); + txdes->txdes0 = cpu_to_le32(ctl_stat & priv->txdes0_edotr_mask); priv->tx_clean_pointer = ftgmac100_next_tx_pointer(pointer); @@ -644,6 +578,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, struct ftgmac100 *priv = netdev_priv(netdev); struct ftgmac100_txdes *txdes, *first; unsigned int pointer, nfrags, len, i, j; + u32 f_ctl_stat, ctl_stat, csum_vlan; dma_addr_t map; /* The HW doesn't pad small frames */ @@ -679,26 +614,34 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, pointer = priv->tx_pointer; txdes = first = &priv->descs->txdes[pointer]; - /* Setup it up with the packet head. We don't set the OWN bit yet. */ + /* Setup it up with the packet head. Don't write the head to the + * ring just yet + */ priv->tx_skbs[pointer] = skb; - ftgmac100_txdes_set_dma_addr(txdes, map); - ftgmac100_txdes_set_buffer_size(txdes, len); - ftgmac100_txdes_set_first_segment(txdes); + f_ctl_stat = ftgmac100_base_tx_ctlstat(priv, pointer); + f_ctl_stat |= FTGMAC100_TXDES0_TXDMA_OWN; + f_ctl_stat |= FTGMAC100_TXDES0_TXBUF_SIZE(len); + f_ctl_stat |= FTGMAC100_TXDES0_FTS; + if (nfrags == 0) + f_ctl_stat |= FTGMAC100_TXDES0_LTS; + txdes->txdes3 = cpu_to_le32(map); /* Setup HW checksumming */ + csum_vlan = 0; if (skb->ip_summed == CHECKSUM_PARTIAL) { __be16 protocol = skb->protocol; if (protocol == cpu_to_be16(ETH_P_IP)) { u8 ip_proto = ip_hdr(skb)->protocol; - ftgmac100_txdes_set_ipcs(txdes); + csum_vlan |= FTGMAC100_TXDES1_IP_CHKSUM; if (ip_proto == IPPROTO_TCP) - ftgmac100_txdes_set_tcpcs(txdes); + csum_vlan |= FTGMAC100_TXDES1_TCP_CHKSUM; else if (ip_proto == IPPROTO_UDP) - ftgmac100_txdes_set_udpcs(txdes); + csum_vlan |= FTGMAC100_TXDES1_UDP_CHKSUM; } } + txdes->txdes1 = cpu_to_le32(csum_vlan); /* Next descriptor */ pointer = ftgmac100_next_tx_pointer(pointer); @@ -718,20 +661,24 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, /* Setup descriptor */ priv->tx_skbs[pointer] = skb; txdes = &priv->descs->txdes[pointer]; - ftgmac100_txdes_set_dma_addr(txdes, map); - ftgmac100_txdes_set_buffer_size(txdes, len); - ftgmac100_txdes_set_dma_own(txdes); + ctl_stat = ftgmac100_base_tx_ctlstat(priv, pointer); + ctl_stat |= FTGMAC100_TXDES0_TXDMA_OWN; + ctl_stat |= FTGMAC100_TXDES0_TXBUF_SIZE(len); + if (i == (nfrags - 1)) + ctl_stat |= FTGMAC100_TXDES0_LTS; + txdes->txdes0 = cpu_to_le32(ctl_stat); + txdes->txdes1 = 0; + txdes->txdes3 = cpu_to_le32(map); + + /* Next one */ pointer = ftgmac100_next_tx_pointer(pointer); } - /* Tag last fragment */ - ftgmac100_txdes_set_last_segment(txdes); - /* Order the previous packet and descriptor udpates - * before setting the OWN bit. + * before setting the OWN bit on the first descriptor. */ dma_wmb(); - ftgmac100_txdes_set_dma_own(first); + first->txdes0 = cpu_to_le32(f_ctl_stat); /* Update next TX pointer */ priv->tx_pointer = pointer; @@ -758,13 +705,16 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, /* Free head */ pointer = priv->tx_pointer; - ftgmac100_free_tx_packet(priv, pointer, skb, first); + ftgmac100_free_tx_packet(priv, pointer, skb, first, f_ctl_stat); + first->txdes0 = cpu_to_le32(f_ctl_stat & priv->txdes0_edotr_mask); /* Then all fragments */ for (j = 0; j < i; j++) { pointer = ftgmac100_next_tx_pointer(pointer); txdes = &priv->descs->txdes[pointer]; - ftgmac100_free_tx_packet(priv, pointer, skb, txdes); + ctl_stat = le32_to_cpu(txdes->txdes0); + ftgmac100_free_tx_packet(priv, pointer, skb, txdes, ctl_stat); + txdes->txdes0 = cpu_to_le32(ctl_stat & priv->txdes0_edotr_mask); } /* This cannot be reached if we successfully mapped the @@ -802,8 +752,10 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) struct ftgmac100_txdes *txdes = &priv->descs->txdes[i]; struct sk_buff *skb = priv->tx_skbs[i]; - if (skb) - ftgmac100_free_tx_packet(priv, i, skb, txdes); + if (!skb) + continue; + ftgmac100_free_tx_packet(priv, i, skb, txdes, + le32_to_cpu(txdes->txdes0)); } } @@ -843,6 +795,7 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv) static void ftgmac100_init_rings(struct ftgmac100 *priv) { struct ftgmac100_rxdes *rxdes; + struct ftgmac100_txdes *txdes; int i; /* Initialize RX ring */ @@ -855,9 +808,11 @@ static void ftgmac100_init_rings(struct ftgmac100 *priv) rxdes->rxdes0 |= cpu_to_le32(priv->rxdes0_edorr_mask); /* Initialize TX ring */ - for (i = 0; i < TX_QUEUE_ENTRIES; i++) - priv->descs->txdes[i].txdes0 = 0; - ftgmac100_txdes_set_end_of_ring(priv, &priv->descs->txdes[i -1]); + for (i = 0; i < TX_QUEUE_ENTRIES; i++) { + txdes = &priv->descs->txdes[i]; + txdes->txdes0 = 0; + } + txdes->txdes0 |= cpu_to_le32(priv->txdes0_edotr_mask); } static int ftgmac100_alloc_rx_buffers(struct ftgmac100 *priv) diff --git a/drivers/net/ethernet/faraday/ftgmac100.h b/drivers/net/ethernet/faraday/ftgmac100.h index 9124785a4ab9..97912c456e80 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.h +++ b/drivers/net/ethernet/faraday/ftgmac100.h @@ -202,10 +202,10 @@ * Transmit descriptor, aligned to 16 bytes */ struct ftgmac100_txdes { - unsigned int txdes0; - unsigned int txdes1; - unsigned int txdes2; /* not used by HW */ - unsigned int txdes3; /* TXBUF_BADR */ + __le32 txdes0; /* Control & status bits */ + __le32 txdes1; /* Irq, checksum and vlan control */ + __le32 txdes2; /* Reserved */ + __le32 txdes3; /* DMA buffer address */ } __attribute__ ((aligned(16))); #define FTGMAC100_TXDES0_TXBUF_SIZE(x) ((x) & 0x3fff) -- cgit From 9415af7f306bfd5555552f059ea0a476c44c816a Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 17 Nov 2016 13:57:32 +0200 Subject: iwlwifi: mvm: support new binding API For a000 devices the binding API needs to include relevant lmac ID - support the new API. The new API should be used regardless if the device had CDB or not. If there is no actual CDB support the binding is bound to first lmac regardless of the band. There are some functionality changes in binding restrictions and quota allocations that will be handled in future patches. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/binding.c | 17 ++++++++++++++++- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 16 ++++++++++++++-- drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 14 +++++++++++--- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 14 ++++++++------ 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index d01701ee4777..9f639fdf0f6e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -344,6 +344,8 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_BT_COEX_RRC = (__force iwl_ucode_tlv_capa_t)30, IWL_UCODE_TLV_CAPA_GSCAN_SUPPORT = (__force iwl_ucode_tlv_capa_t)31, IWL_UCODE_TLV_CAPA_STA_PM_NOTIF = (__force iwl_ucode_tlv_capa_t)38, + IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT = (__force iwl_ucode_tlv_capa_t)39, + IWL_UCODE_TLV_CAPA_CDB_SUPPORT = (__force iwl_ucode_tlv_capa_t)40, IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64, IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65, IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c index 7cb68f6ed1b0..2e0ed080457f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2016 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 @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -82,6 +84,19 @@ static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action, struct iwl_mvm_phy_ctxt *phyctxt = data->phyctxt; int i, ret; u32 status; + int size; + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT)) { + size = sizeof(cmd); + if (phyctxt->channel->band == NL80211_BAND_2GHZ || + !iwl_mvm_is_cdb_supported(mvm)) + cmd.lmac_id = cpu_to_le32(IWL_LMAC_24G_INDEX); + else + cmd.lmac_id = cpu_to_le32(IWL_LMAC_5G_INDEX); + } else { + size = IWL_BINDING_CMD_SIZE_V1; + } memset(&cmd, 0, sizeof(cmd)); @@ -99,7 +114,7 @@ static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action, status = 0; ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD, - sizeof(cmd), &cmd, &status); + size, &cmd, &status); if (ret) { IWL_ERR(mvm, "Failed to send binding (action:%d): %d\n", action, ret); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index c7eb1983c4f9..b7465857b4b6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -665,6 +665,19 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_binding_cmd binding_cmd = {}; struct iwl_time_quota_cmd quota_cmd = {}; u32 status; + int size; + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT)) { + size = sizeof(binding_cmd); + if (mvmvif->phy_ctxt->channel->band == NL80211_BAND_2GHZ || + !iwl_mvm_is_cdb_supported(mvm)) + binding_cmd.lmac_id = cpu_to_le32(IWL_LMAC_24G_INDEX); + else + binding_cmd.lmac_id = cpu_to_le32(IWL_LMAC_5G_INDEX); + } else { + size = IWL_BINDING_CMD_SIZE_V1; + } /* add back the PHY */ if (WARN_ON(!mvmvif->phy_ctxt)) @@ -711,8 +724,7 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, status = 0; ret = iwl_mvm_send_cmd_pdu_status(mvm, BINDING_CONTEXT_CMD, - sizeof(binding_cmd), &binding_cmd, - &status); + size, &binding_cmd, &status); if (ret) { IWL_ERR(mvm, "Failed to add binding: %d\n", ret); return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index cf2b836f3888..248373a0990a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -673,10 +673,8 @@ struct iwl_error_resp { /* Common PHY, MAC and Bindings definitions */ - #define MAX_MACS_IN_BINDING (3) #define MAX_BINDINGS (4) -#define AUX_BINDING_INDEX (3) /* Used to extract ID and color from the context dword */ #define FW_CTXT_ID_POS (0) @@ -960,6 +958,7 @@ struct iwl_time_event_notif { * @action: action to perform, one of FW_CTXT_ACTION_* * @macs: array of MAC id and colors which belong to the binding * @phy: PHY id and color which belongs to the binding + * @lmac_id: the lmac id the binding belongs to */ struct iwl_binding_cmd { /* COMMON_INDEX_HDR_API_S_VER_1 */ @@ -968,7 +967,13 @@ struct iwl_binding_cmd { /* BINDING_DATA_API_S_VER_1 */ __le32 macs[MAX_MACS_IN_BINDING]; __le32 phy; -} __packed; /* BINDING_CMD_API_S_VER_1 */ + /* BINDING_CMD_API_S_VER_1 */ + __le32 lmac_id; +} __packed; /* BINDING_CMD_API_S_VER_2 */ + +#define IWL_BINDING_CMD_SIZE_V1 offsetof(struct iwl_binding_cmd, lmac_id) +#define IWL_LMAC_24G_INDEX 0 +#define IWL_LMAC_5G_INDEX 1 /* The maximal number of fragments in the FW's schedule session */ #define IWL_MVM_MAX_QUOTA 128 @@ -990,6 +995,9 @@ struct iwl_time_quota_data { * struct iwl_time_quota_cmd - configuration of time quota between bindings * ( TIME_QUOTA_CMD = 0x2c ) * @quotas: allocations per binding + * Note: on non-CDB the fourth one is the auxilary mac and is + * essentially zero. + * On CDB the fourth one is a regular binding. */ struct iwl_time_quota_cmd { struct iwl_time_quota_data quotas[MAX_BINDINGS]; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 73a216524af2..88bc459b1f9a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1222,13 +1222,15 @@ static inline bool iwl_mvm_is_cdb_supported(struct iwl_mvm *mvm) { /* * TODO: - * The issue of how to determine CDB support is still not well defined. - * It may be that it will be for all next HW devices and it may be per - * FW compilation and it may also differ between different devices. - * For now take a ride on the new TX API and get back to it when - * it is well defined. + * The issue of how to determine CDB APIs and usage is still not fully + * defined. + * There is a compilation for CDB and non-CDB FW, but there may + * be also runtime check. + * For now there is a TLV for checking compilation mode, but a + * runtime check will also have to be here - once defined. */ - return iwl_mvm_has_new_tx_api(mvm); + return fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CDB_SUPPORT); } static inline bool iwl_mvm_is_tt_in_fw(struct iwl_mvm *mvm) -- cgit From 6ffff630c0699ded5c3e79879cc9c4e733a46829 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 28 Dec 2016 10:46:20 +0200 Subject: iwlwifi: be more verbose about needed firmware If the supported firmware versions are not found, we currently only print "no suitable firmware found". This is not very informative for the user trying to find the correct version to use. Improve this by printing the exact firmware name(s) the driver supports and pointing to the git repository where they can be found. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index be466a074c1d..21c523a9cc7e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -211,24 +211,39 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, static int iwl_request_firmware(struct iwl_drv *drv, bool first) { - const char *name_pre = drv->trans->cfg->fw_name_pre; + const struct iwl_cfg *cfg = drv->trans->cfg; char tag[8]; if (first) { - drv->fw_index = drv->trans->cfg->ucode_api_max; + drv->fw_index = cfg->ucode_api_max; sprintf(tag, "%d", drv->fw_index); } else { drv->fw_index--; sprintf(tag, "%d", drv->fw_index); } - if (drv->fw_index < drv->trans->cfg->ucode_api_min) { + if (drv->fw_index < cfg->ucode_api_min) { IWL_ERR(drv, "no suitable firmware found!\n"); + + if (cfg->ucode_api_min == cfg->ucode_api_max) { + IWL_ERR(drv, "%s%d is required\n", cfg->fw_name_pre, + cfg->ucode_api_max); + } else { + IWL_ERR(drv, "minimum version required: %s%d\n", + cfg->fw_name_pre, + cfg->ucode_api_min); + IWL_ERR(drv, "maximum version supported: %s%d\n", + cfg->fw_name_pre, + cfg->ucode_api_max); + } + + IWL_ERR(drv, + "check git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git\n"); return -ENOENT; } snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s.ucode", - name_pre, tag); + cfg->fw_name_pre, tag); IWL_DEBUG_INFO(drv, "attempting to load firmware '%s'\n", drv->firmware_name); -- cgit From ddef2f98eee55deb7923917d388e7715310c3aa9 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 19 Dec 2016 08:41:16 +0200 Subject: iwlwifi: mvm: add DQA_ENABLE_CMD to the command list This will allow to print the name of the commands in the logs when we sent it. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 4cd72d4cdc47..f4f957fe71d7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -452,6 +452,7 @@ static const struct iwl_hcmd_names iwl_mvm_phy_names[] = { * Access is done through binary search */ static const struct iwl_hcmd_names iwl_mvm_data_path_names[] = { + HCMD_NAME(DQA_ENABLE_CMD), HCMD_NAME(UPDATE_MU_GROUPS_CMD), HCMD_NAME(TRIGGER_RX_QUEUES_NOTIF_CMD), HCMD_NAME(STA_PM_NOTIF), -- cgit From 63f231fea02afbd364bbe77717bdee5a22daedd1 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 27 Dec 2016 10:18:18 +0200 Subject: iwlwifi: remove support for deprecated RF One of the RF modules we support has been deprecated and never released publicly. Remove support for this module. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-9000.c | 19 ------------------- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 1 - drivers/net/wireless/intel/iwlwifi/iwl-csr.h | 1 - drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 17 +++++------------ 4 files changed, 5 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c index a5f0c0bf85ec..ca119bd8a32b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c @@ -74,13 +74,10 @@ #define IWL9000_FW_PRE "iwlwifi-9000-pu-a0-jf-a0-" #define IWL9260_FW_PRE "iwlwifi-9260-th-a0-jf-a0-" -#define IWL9000LC_FW_PRE "iwlwifi-9000-pu-a0-lc-a0-" #define IWL9000_MODULE_FIRMWARE(api) \ IWL9000_FW_PRE "-" __stringify(api) ".ucode" #define IWL9260_MODULE_FIRMWARE(api) \ IWL9260_FW_PRE "-" __stringify(api) ".ucode" -#define IWL9000LC_MODULE_FIRMWARE(api) \ - IWL9000LC_FW_PRE "-" __stringify(api) ".ucode" #define NVM_HW_SECTION_NUM_FAMILY_9000 10 @@ -198,21 +195,5 @@ const struct iwl_cfg iwl9560_2ac_cfg = { .integrated = true, }; -/* - * TODO the struct below is for internal testing only this should be - * removed by EO 2016~ - */ -const struct iwl_cfg iwl9000lc_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 9000", - .fw_name_pre = IWL9000LC_FW_PRE, - IWL_DEVICE_9000, - .ht_params = &iwl9000_ht_params, - .nvm_ver = IWL9000_NVM_VERSION, - .nvm_calib_ver = IWL9000_TX_POWER_VERSION, - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, - .integrated = true, -}; - MODULE_FIRMWARE(IWL9000_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL9260_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL9000LC_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 94f8a51b633e..96a2ec32f707 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -449,7 +449,6 @@ extern const struct iwl_cfg iwl4165_2ac_cfg; extern const struct iwl_cfg iwl8260_2ac_sdio_cfg; extern const struct iwl_cfg iwl8265_2ac_sdio_cfg; extern const struct iwl_cfg iwl4165_2ac_sdio_cfg; -extern const struct iwl_cfg iwl9000lc_2ac_cfg; extern const struct iwl_cfg iwl9160_2ac_cfg; extern const struct iwl_cfg iwl9260_2ac_cfg; extern const struct iwl_cfg iwl9270_2ac_cfg; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index 4ee3b621ec27..fa120fb55373 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -348,7 +348,6 @@ enum { /* RF_ID value */ #define CSR_HW_RF_ID_TYPE_JF (0x00105000) -#define CSR_HW_RF_ID_TYPE_LC (0x00101000) #define CSR_HW_RF_ID_TYPE_HR (0x00109000) /* EEPROM REG */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index ba8a81cb0e2b..d38fcc1a4768 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -667,18 +667,11 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) iwl_trans->cfg = cfg_7265d; } - if (iwl_trans->cfg->rf_id) { - if (cfg == &iwl9460_2ac_cfg && - iwl_trans->hw_rf_id == CSR_HW_RF_ID_TYPE_LC) { - cfg = &iwl9000lc_2ac_cfg; - iwl_trans->cfg = cfg; - } - - if (cfg == &iwla000_2ac_cfg_hr && - iwl_trans->hw_rf_id == CSR_HW_RF_ID_TYPE_JF) { - cfg = &iwla000_2ac_cfg_jf; - iwl_trans->cfg = cfg; - } + if (iwl_trans->cfg->rf_id && + (cfg == &iwla000_2ac_cfg_hr && + iwl_trans->hw_rf_id == CSR_HW_RF_ID_TYPE_JF)) { + cfg = &iwla000_2ac_cfg_jf; + iwl_trans->cfg = cfg; } #endif -- cgit From 2876e8c9e8a5bf075c7eddad4d309c1bdd178177 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Mon, 19 Dec 2016 12:00:32 +0200 Subject: iwlwifi: mvm: bump max API to 30 Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-7000.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/iwl-8000.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/iwl-9000.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-a000.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c index a72e58623d3a..aeefd42d23ad 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c @@ -73,8 +73,8 @@ /* Highest firmware API version supported */ #define IWL7260_UCODE_API_MAX 17 #define IWL7265_UCODE_API_MAX 17 -#define IWL7265D_UCODE_API_MAX 28 -#define IWL3168_UCODE_API_MAX 28 +#define IWL7265D_UCODE_API_MAX 30 +#define IWL3168_UCODE_API_MAX 30 /* Lowest firmware API version supported */ #define IWL7260_UCODE_API_MIN 17 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c index b7953bf55f6f..b9718c0cf174 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c @@ -70,8 +70,8 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL8000_UCODE_API_MAX 28 -#define IWL8265_UCODE_API_MAX 28 +#define IWL8000_UCODE_API_MAX 30 +#define IWL8265_UCODE_API_MAX 30 /* Lowest firmware API version supported */ #define IWL8000_UCODE_API_MIN 17 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c index ca119bd8a32b..da58ece2286a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c @@ -55,7 +55,7 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL9000_UCODE_API_MAX 28 +#define IWL9000_UCODE_API_MAX 30 /* Lowest firmware API version supported */ #define IWL9000_UCODE_API_MIN 17 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c index 15dd7f6137c8..20739e92aef6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c @@ -55,7 +55,7 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL_A000_UCODE_API_MAX 28 +#define IWL_A000_UCODE_API_MAX 30 /* Lowest firmware API version supported */ #define IWL_A000_UCODE_API_MIN 24 -- cgit From 951cb319872df74a7ec2f3a13cebc0f2422cbdba Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 29 Dec 2016 10:11:40 +0200 Subject: iwlwifi: mvm: adjust new API of compressed BA Final API has a reserved field - adjust accordingly. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h index b38cc073adcc..85744fa3bd4d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h @@ -635,6 +635,10 @@ enum iwl_mvm_ba_resp_flags { * @tx_rate: the rate the aggregation was sent at * @tfd_cnt: number of TFD-Q elements * @ra_tid_cnt: number of RATID-Q elements + * @ba_tfd: array of TFD queue status updates. See &iwl_mvm_compressed_ba_tfd + * for details. + * @ra_tid: array of RA-TID queue status updates. For debug purposes only. See + * &iwl_mvm_compressed_ba_ratid for more details. */ struct iwl_mvm_compressed_ba_notif { __le32 flags; @@ -646,6 +650,7 @@ struct iwl_mvm_compressed_ba_notif { __le16 query_frame_cnt; __le16 txed; __le16 done; + __le16 reserved; __le32 wireless_time; __le32 tx_rate; __le16 tfd_cnt; -- cgit From 5cddd05c9cbe420436799716d009bc0372ef8268 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 14 Dec 2016 13:48:04 +0100 Subject: iwlwifi: mvm: fix RX SKB header size and align it properly When receiving a frame, we currently pull in sizeof(*hdr) plus some extra (crypto/snap), which is too much, most headers aren't actually sizeof(*hdr) since that takes into account the 4-address format but doesn't take into account QoS. As a result, a typical frame will have 4 bytes of the payload in the SKB header already. Fix this by calculating the correct header length, and now that we have that, align the end of the SKB header to a multiple of 4 so that the IP header will be aligned properly when pulled in. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 20473df79c94..c1bf67f04cf6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -104,7 +104,20 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, u8 crypt_len, struct iwl_rx_cmd_buffer *rxb) { - unsigned int hdrlen, fraglen; + unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control); + unsigned int fraglen; + + /* + * The 'hdrlen' (plus the 8 bytes for the SNAP and the crypt_len, + * but those are all multiples of 4 long) all goes away, but we + * want the *end* of it, which is going to be the start of the IP + * header, to be aligned when it gets pulled in. + * The beginning of the skb->data is aligned on at least a 4-byte + * boundary after allocation. Everything here is aligned at least + * on a 2-byte boundary so we can just take hdrlen & 3 and pad by + * the result. + */ + skb_reserve(skb, hdrlen & 3); /* If frame is small enough to fit in skb->head, pull it completely. * If not, only pull ieee80211_hdr (including crypto if present, and @@ -118,8 +131,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, * If the latter changes (there are efforts in the standards group * to do so) we should revisit this and ieee80211_data_to_8023(). */ - hdrlen = (len <= skb_tailroom(skb)) ? len : - sizeof(*hdr) + crypt_len + 8; + hdrlen = (len <= skb_tailroom(skb)) ? len : hdrlen + crypt_len + 8; memcpy(skb_put(skb, hdrlen), hdr, hdrlen); fraglen = len - hdrlen; -- cgit From afb844318de2a87694380ee3824ee102f2699bd9 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 3 Jan 2017 10:04:44 +0200 Subject: iwlwifi: pcie: print less data upon firmware crash We don't need to print so much data in the kernel log. Limit the data to be printed to the queue that actually got stuck in case of a TFD queue hang, and stop dumping all the CSR and FH registers. Over the course of time, the CSR and FH values haven't proven themselves to be really useful for debugging, and they are now in the firmware dump anyway. This comes as a preparation to the addition of more data required to be printed by the firwmare team. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 3 -- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 64 ++++++++++--------------- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 3 -- 3 files changed, 24 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index de94dfdf2ec9..17806d82f3a3 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1393,9 +1393,6 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) return; } - iwl_pcie_dump_csr(trans); - iwl_dump_fh(trans, NULL); - local_bh_disable(); /* The STATUS_FW_ERROR bit is set in this function. This must happen * before we wake up the command caller, to ensure a proper cleanup. */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 7f05fc56587a..fbb0afa9de14 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -7,7 +7,7 @@ * * Copyright(c) 2007 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,7 +34,7 @@ * * Copyright(c) 2005 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -2075,48 +2075,32 @@ static void iwl_trans_pcie_block_txq_ptrs(struct iwl_trans *trans, bool block) void iwl_trans_pcie_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 scd_sram_addr; - u8 buf[16]; - int cnt; + u32 txq_id = txq->id; + u32 status; + bool active; + u8 fifo; - IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", - txq->read_ptr, txq->write_ptr); - - if (trans->cfg->use_tfh) + if (trans->cfg->use_tfh) { + IWL_ERR(trans, "Queue %d is stuck %d %d\n", txq_id, + txq->read_ptr, txq->write_ptr); /* TODO: access new SCD registers and dump them */ return; - - scd_sram_addr = trans_pcie->scd_base_addr + - SCD_TX_STTS_QUEUE_OFFSET(txq->id); - iwl_trans_read_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); - - iwl_print_hex_error(trans, buf, sizeof(buf)); - - for (cnt = 0; cnt < FH_TCSR_CHNL_NUM; cnt++) - IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", cnt, - iwl_read_direct32(trans, FH_TX_TRB_REG(cnt))); - - for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { - u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(cnt)); - u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; - bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); - u32 tbl_dw = - iwl_trans_read_mem32(trans, trans_pcie->scd_base_addr + - SCD_TRANS_TBL_OFFSET_QUEUE(cnt)); - - if (cnt & 0x1) - tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; - else - tbl_dw = tbl_dw & 0x0000FFFF; - - IWL_ERR(trans, - "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", - cnt, active ? "" : "in", fifo, tbl_dw, - iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) & - (TFD_QUEUE_SIZE_MAX - 1), - iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt))); } + + status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id)); + fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; + active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); + + IWL_ERR(trans, + "Queue %d is %sactive on fifo %d and stuck for %u ms. SW [%d, %d] HW [%d, %d] FH TRB=0x0%x\n", + txq_id, active ? "" : "in", fifo, + jiffies_to_msecs(txq->wd_timeout), + txq->read_ptr, txq->write_ptr, + iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq_id)) & + (TFD_QUEUE_SIZE_MAX - 1), + iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq_id)) & + (TFD_QUEUE_SIZE_MAX - 1), + iwl_read_direct32(trans, FH_TX_TRB_REG(fifo))); } static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 911cf9868107..03df578bed00 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -164,9 +164,6 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data) } spin_unlock(&txq->lock); - IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->id, - jiffies_to_msecs(txq->wd_timeout)); - iwl_trans_pcie_log_scd_error(trans, txq); iwl_force_nmi(trans); -- cgit From 1c17627bf5c460f94451ac5acab3ee96d3b7e1cd Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 22 Dec 2016 13:03:40 +0200 Subject: iwlwifi: mvm: cleanup pending frames in DQA mode When a station is asleep, the fw will set it as "asleep". All queues that are used only by one station will be stopped by the fw. In pre-DQA mode this was relevant for aggregation queues. However, in DQA mode a queue is owned by one station only, so all queues will be stopped. As a result, we don't expect to get filtered frames back to mac80211 and don't have to maintain the entire pending_frames state logic, the same way as we do in aggregations. The correct behavior is to align DQA behavior with the aggregation queue behaviour pre-DQA: - Don't count pending frames. - Let mac80211 know we have frames in these queues so that it can properly handle trigger frames. When a trigger frame is received, mac80211 tells the driver to send frames from the queues using release_buffered_frames. The driver will tell the fw to let frames out even if the station is asleep. This is done by iwl_mvm_sta_modify_sleep_tx_count. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 8 +++-- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 11 +++--- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 41 ++++++++++------------- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index d37b1695c64e..265d9eb8f0b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2319,7 +2319,7 @@ iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw, { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - /* Called when we need to transmit (a) frame(s) from agg queue */ + /* Called when we need to transmit (a) frame(s) from agg or dqa queue */ iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames, tids, more_data, true); @@ -2338,10 +2338,14 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; - if (tid_data->state != IWL_AGG_ON && + if (!iwl_mvm_is_dqa_supported(mvm) && + tid_data->state != IWL_AGG_ON && tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA) continue; + if (tid_data->txq_id == IEEE80211_INVAL_HW_QUEUE) + continue; + __set_bit(tid_data->txq_id, &txqs); if (iwl_mvm_tid_queued(tid_data) == 0) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index bd1dcc863d8f..b51a2853cc80 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -3135,7 +3135,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, struct ieee80211_sta *sta, enum ieee80211_frame_release_type reason, u16 cnt, u16 tids, bool more_data, - bool agg) + bool single_sta_queue) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd cmd = { @@ -3155,14 +3155,14 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]); - /* If we're releasing frames from aggregation queues then check if the - * all queues combined that we're releasing frames from have + /* If we're releasing frames from aggregation or dqa queues then check + * if all the queues that we're releasing frames from, combined, have: * - more frames than the service period, in which case more_data * needs to be set * - fewer than 'cnt' frames, in which case we need to adjust the * firmware command (but do that unconditionally) */ - if (agg) { + if (single_sta_queue) { int remaining = cnt; int sleep_tx_count; @@ -3172,7 +3172,8 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, u16 n_queued; tid_data = &mvmsta->tid_data[tid]; - if (WARN(tid_data->state != IWL_AGG_ON && + if (WARN(!iwl_mvm_is_dqa_supported(mvm) && + tid_data->state != IWL_AGG_ON && tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA, "TID %d state is %d\n", tid, tid_data->state)) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 4be34f902278..1927ce607798 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -547,7 +547,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, struct ieee80211_sta *sta, enum ieee80211_frame_release_type reason, u16 cnt, u16 tids, bool more_data, - bool agg); + bool single_sta_queue); int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, bool drain); void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index dd2b4a300819..3f37075f4cde 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -628,8 +629,10 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) * values. * Note that we don't need to make sure it isn't agg'd, since we're * TXing non-sta + * For DQA mode - we shouldn't increase it though */ - atomic_inc(&mvm->pending_frames[sta_id]); + if (!iwl_mvm_is_dqa_supported(mvm)) + atomic_inc(&mvm->pending_frames[sta_id]); return 0; } @@ -1005,11 +1008,8 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, spin_unlock(&mvmsta->lock); - /* Increase pending frames count if this isn't AMPDU */ - if ((iwl_mvm_is_dqa_supported(mvm) && - mvmsta->tid_data[tx_cmd->tid_tspec].state != IWL_AGG_ON && - mvmsta->tid_data[tx_cmd->tid_tspec].state != IWL_AGG_STARTING) || - (!iwl_mvm_is_dqa_supported(mvm) && !is_ampdu)) + /* Increase pending frames count if this isn't AMPDU or DQA queue */ + if (!iwl_mvm_is_dqa_supported(mvm) && !is_ampdu) atomic_inc(&mvm->pending_frames[mvmsta->sta_id]); return 0; @@ -1079,12 +1079,13 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, lockdep_assert_held(&mvmsta->lock); if ((tid_data->state == IWL_AGG_ON || - tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) && + tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA || + iwl_mvm_is_dqa_supported(mvm)) && iwl_mvm_tid_queued(tid_data) == 0) { /* - * Now that this aggregation queue is empty tell mac80211 so it - * knows we no longer have frames buffered for the station on - * this TID (for the TIM bitmap calculation.) + * Now that this aggregation or DQA queue is empty tell + * mac80211 so it knows we no longer have frames buffered for + * the station on this TID (for the TIM bitmap calculation.) */ ieee80211_sta_set_buffered(sta, tid, false); } @@ -1257,7 +1258,6 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, u8 skb_freed = 0; u16 next_reclaimed, seq_ctl; bool is_ndp = false; - bool txq_agg = false; /* Is this TXQ aggregated */ __skb_queue_head_init(&skbs); @@ -1283,6 +1283,10 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, info->flags |= IEEE80211_TX_STAT_ACK; break; case TX_STATUS_FAIL_DEST_PS: + /* In DQA, the FW should have stopped the queue and not + * return this status + */ + WARN_ON(iwl_mvm_is_dqa_supported(mvm)); info->flags |= IEEE80211_TX_STAT_TX_FILTERED; break; default: @@ -1387,15 +1391,6 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, bool send_eosp_ndp = false; spin_lock_bh(&mvmsta->lock); - if (iwl_mvm_is_dqa_supported(mvm)) { - enum iwl_mvm_agg_state state; - - state = mvmsta->tid_data[tid].state; - txq_agg = (state == IWL_AGG_ON || - state == IWL_EMPTYING_HW_QUEUE_DELBA); - } else { - txq_agg = txq_id >= mvm->first_agg_queue; - } if (!is_ndp) { tid_data->next_reclaimed = next_reclaimed; @@ -1452,11 +1447,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, * If the txq is not an AMPDU queue, there is no chance we freed * several skbs. Check that out... */ - if (txq_agg) + if (iwl_mvm_is_dqa_supported(mvm) || txq_id >= mvm->first_agg_queue) goto out; /* We can't free more than one frame at once on a shared queue */ - WARN_ON(!iwl_mvm_is_dqa_supported(mvm) && (skb_freed > 1)); + WARN_ON(skb_freed > 1); /* If we have still frames for this STA nothing to do here */ if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) -- cgit From bd31dd9d147aad423a9ac371b86685f87b4e1420 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 9 Jan 2017 11:28:47 +0100 Subject: iwlwifi: use upper_32_bits/lower_32_bits where appropriate That's a bit nicer than open-coding it. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-fh.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-io.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h index 33ef5372d195..361fc25e8618 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h @@ -647,7 +647,7 @@ struct iwl_rb_status { static inline u8 iwl_get_dma_hi_addr(dma_addr_t addr) { - return (sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0) & 0xF; + return (sizeof(addr) > sizeof(u32) ? upper_32_bits(addr) : 0) & 0xF; } /** * struct iwl_tfd_tb transmit buffer descriptor within transmit frame descriptor diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index a9f69fdd170b..0f893ae6e715 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -54,8 +54,8 @@ IWL_EXPORT_SYMBOL(iwl_write32); void iwl_write64(struct iwl_trans *trans, u64 ofs, u64 val) { trace_iwlwifi_dev_iowrite64(trans->dev, ofs, val); - iwl_trans_write32(trans, ofs, val & 0xffffffff); - iwl_trans_write32(trans, ofs + 4, val >> 32); + iwl_trans_write32(trans, ofs, lower_32_bits(val)); + iwl_trans_write32(trans, ofs + 4, upper_32_bits(val)); } IWL_EXPORT_SYMBOL(iwl_write64); -- cgit From 5da083d1922c23b75e921c85b687c8e112e8280e Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Sun, 8 Jan 2017 16:52:59 +0200 Subject: iwlwifi: add support for 9000 HW B-step NICs Once we remove support for A-step, we'll be able to clean the code back again. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-9000.c | 25 ++++++++++++++++--------- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 7 +++++-- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 19 +++++++++++++------ 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c index da58ece2286a..d265b279b2ca 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2015-2016 Intel Deutschland GmbH + * Copyright(c) 2015-2017 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 @@ -18,7 +18,7 @@ * * BSD LICENSE * - * Copyright(c) 2015-2016 Intel Deutschland GmbH + * Copyright(c) 2015-2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -73,11 +73,14 @@ #define IWL9000_SMEM_LEN 0x68000 #define IWL9000_FW_PRE "iwlwifi-9000-pu-a0-jf-a0-" -#define IWL9260_FW_PRE "iwlwifi-9260-th-a0-jf-a0-" +#define IWL9260A_FW_PRE "iwlwifi-9260-th-a0-jf-a0-" +#define IWL9260B_FW_PRE "iwlwifi-9260-th-b0-jf-b0-" #define IWL9000_MODULE_FIRMWARE(api) \ IWL9000_FW_PRE "-" __stringify(api) ".ucode" -#define IWL9260_MODULE_FIRMWARE(api) \ - IWL9260_FW_PRE "-" __stringify(api) ".ucode" +#define IWL9260A_MODULE_FIRMWARE(api) \ + IWL9260A_FW_PRE "-" __stringify(api) ".ucode" +#define IWL9260B_MODULE_FIRMWARE(api) \ + IWL9260B_FW_PRE "-" __stringify(api) ".ucode" #define NVM_HW_SECTION_NUM_FAMILY_9000 10 @@ -145,7 +148,8 @@ static const struct iwl_tt_params iwl9000_tt_params = { const struct iwl_cfg iwl9160_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 9160", - .fw_name_pre = IWL9260_FW_PRE, + .fw_name_pre = IWL9260A_FW_PRE, + .fw_name_pre_next_step = IWL9260B_FW_PRE, IWL_DEVICE_9000, .ht_params = &iwl9000_ht_params, .nvm_ver = IWL9000_NVM_VERSION, @@ -155,7 +159,8 @@ const struct iwl_cfg iwl9160_2ac_cfg = { const struct iwl_cfg iwl9260_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 9260", - .fw_name_pre = IWL9260_FW_PRE, + .fw_name_pre = IWL9260A_FW_PRE, + .fw_name_pre_next_step = IWL9260B_FW_PRE, IWL_DEVICE_9000, .ht_params = &iwl9000_ht_params, .nvm_ver = IWL9000_NVM_VERSION, @@ -165,7 +170,8 @@ const struct iwl_cfg iwl9260_2ac_cfg = { const struct iwl_cfg iwl9270_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 9270", - .fw_name_pre = IWL9260_FW_PRE, + .fw_name_pre = IWL9260A_FW_PRE, + .fw_name_pre_next_step = IWL9260B_FW_PRE, IWL_DEVICE_9000, .ht_params = &iwl9000_ht_params, .nvm_ver = IWL9000_NVM_VERSION, @@ -196,4 +202,5 @@ const struct iwl_cfg iwl9560_2ac_cfg = { }; MODULE_FIRMWARE(IWL9000_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL9260_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL9260A_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL9260B_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 96a2ec32f707..852963487bd9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -6,7 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * Copyright (C) 2016 Intel Deutschland GmbH + * Copyright (C) 2016 - 2017 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 @@ -32,7 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. - * Copyright (C) 2016 Intel Deutschland GmbH + * Copyright (C) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -283,6 +283,8 @@ struct iwl_pwr_tx_backoff { * @fw_name_pre: Firmware filename prefix. The api version and extension * (.ucode) will be added to filename before loading from disk. The * filename is constructed as fw_name_pre.ucode. + * @fw_name_pre_next_step: same as @fw_name_pre, only for next step + * (if supported) * @ucode_api_max: Highest version of uCode API supported by driver. * @ucode_api_min: Lowest version of uCode API supported by driver. * @max_inst_size: The maximal length of the fw inst section @@ -330,6 +332,7 @@ struct iwl_cfg { /* params specific to an individual device within a device family */ const char *name; const char *fw_name_pre; + const char *fw_name_pre_next_step; /* params not likely to change within a device family */ const struct iwl_base_params *base_params; /* params likely to change within a device family */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 21c523a9cc7e..212fb8d5c064 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -7,7 +7,7 @@ * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,7 +34,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -213,6 +213,13 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) { const struct iwl_cfg *cfg = drv->trans->cfg; char tag[8]; + const char *fw_pre_name; + + if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 && + CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_B_STEP) + fw_pre_name = cfg->fw_name_pre_next_step; + else + fw_pre_name = cfg->fw_name_pre; if (first) { drv->fw_index = cfg->ucode_api_max; @@ -226,14 +233,14 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) IWL_ERR(drv, "no suitable firmware found!\n"); if (cfg->ucode_api_min == cfg->ucode_api_max) { - IWL_ERR(drv, "%s%d is required\n", cfg->fw_name_pre, + IWL_ERR(drv, "%s%d is required\n", fw_pre_name, cfg->ucode_api_max); } else { IWL_ERR(drv, "minimum version required: %s%d\n", - cfg->fw_name_pre, + fw_pre_name, cfg->ucode_api_min); IWL_ERR(drv, "maximum version supported: %s%d\n", - cfg->fw_name_pre, + fw_pre_name, cfg->ucode_api_max); } @@ -243,7 +250,7 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) } snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s.ucode", - cfg->fw_name_pre, tag); + fw_pre_name, tag); IWL_DEBUG_INFO(drv, "attempting to load firmware '%s'\n", drv->firmware_name); -- cgit From 26d6c16bed536be526cbfcf67ceefb410d62f252 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 3 Jan 2017 12:00:19 +0200 Subject: iwlwifi: mvm: add multicast station Currently multicast queue is associated with the broadcast station. This raises quite a few issues: The multicast queue has a special treatment: - It is sent in the MAC context command - It is excluded from tfd_queue_mask In DQA mode we end up enabling two queues - the probe response queue and the multicast queue - with the same station (broadcast) and TID while in DQA mode it should be unique RA-TID. Firmware will enforce it for a000 devices, so this allocation will fail. In addition, in a000 devices the FW will set the FIFO and not the driver. So there is a need for FW to know when we enable the queue that it is multicast queue so it will be bound to the multicast FIFO. There is no such way in current design. In order to simplify driver and firmware handling of this queue create a multicast station. This solves the unique RA-TID issue in the short term and serves as preparation for the long term. In the long term we will also add a flag marking this station for the FW as the multicast station. Once we will do that the FW will know this is the multicast queue immediately when it is added and bind it to the correct FIFO. It will also enable removing the special treatment of the queue in the MAC context command. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 26 ++++- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 112 +++++++++++++++------- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 3 + 5 files changed, 106 insertions(+), 37 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 99132ea16ede..e7c8567cafff 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -472,6 +472,7 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, } mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT; + mvmvif->mcast_sta.sta_id = IWL_MVM_STATION_COUNT; mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 265d9eb8f0b0..72a056a80516 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -6,8 +6,8 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 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,7 +33,8 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1351,6 +1352,17 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, goto out_release; } + if (iwl_mvm_is_dqa_supported(mvm)) { + /* + * Only queue for this station is the mcast queue, + * which shouldn't be in TFD mask anyway + */ + ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->mcast_sta, + 0, vif->type); + if (ret) + goto out_release; + } + iwl_mvm_vif_dbgfs_register(mvm, vif); goto out_unlock; } @@ -1516,6 +1528,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mvm->noa_duration = 0; } #endif + iwl_mvm_dealloc_int_sta(mvm, &mvmvif->mcast_sta); iwl_mvm_dealloc_bcast_sta(mvm, vif); goto out_release; } @@ -2104,6 +2117,10 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, if (ret) goto out_unbind; + ret = iwl_mvm_add_mcast_sta(mvm, vif); + if (ret) + goto out_rm_bcast; + /* must be set before quota calculations */ mvmvif->ap_ibss_active = true; @@ -2131,6 +2148,8 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, out_quota_failed: iwl_mvm_power_update_mac(mvm); mvmvif->ap_ibss_active = false; + iwl_mvm_rm_mcast_sta(mvm, vif); +out_rm_bcast: iwl_mvm_send_rm_bcast_sta(mvm, vif); out_unbind: iwl_mvm_binding_remove_vif(mvm, vif); @@ -2177,6 +2196,7 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL); iwl_mvm_update_quotas(mvm, false, NULL); + iwl_mvm_rm_mcast_sta(mvm, vif); iwl_mvm_send_rm_bcast_sta(mvm, vif); iwl_mvm_binding_remove_vif(mvm, vif); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 88bc459b1f9a..70b994638273 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -407,6 +407,7 @@ struct iwl_mvm_vif { struct iwl_mvm_time_event_data hs_time_event_data; struct iwl_mvm_int_sta bcast_sta; + struct iwl_mvm_int_sta mcast_sta; /* * Assigned while mac80211 has the interface in a channel context, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index b51a2853cc80..41fd3ba574c0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1652,8 +1652,7 @@ int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, return 0; } -static void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, - struct iwl_mvm_int_sta *sta) +void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta) { RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL); memset(sta, 0, sizeof(struct iwl_mvm_int_sta)); @@ -1826,33 +1825,8 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) ret = iwl_mvm_add_int_sta_common(mvm, bsta, baddr, mvmvif->id, mvmvif->color); - if (ret) - return ret; - - /* - * In AP vif type, we also need to enable the cab_queue. However, we - * have to enable it after the ADD_STA command is sent, otherwise the - * FW will throw an assert once we send the ADD_STA command (it'll - * detect a mismatch in the tfd_queue_msk, as we can't add the - * enabled-cab_queue to the mask) - */ - if (iwl_mvm_is_dqa_supported(mvm) && - vif->type == NL80211_IFTYPE_AP) { - struct iwl_trans_txq_scd_cfg cfg = { - .fifo = IWL_MVM_TX_FIFO_MCAST, - .sta_id = mvmvif->bcast_sta.sta_id, - .tid = IWL_MAX_TID_COUNT, - .aggregate = false, - .frame_limit = IWL_FRAME_LIMIT, - }; - unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif, false, false); - - iwl_mvm_enable_txq(mvm, vif->cab_queue, vif->cab_queue, - 0, &cfg, wdg_timeout); - } - return 0; + return ret; } static void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, @@ -1862,10 +1836,6 @@ static void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); - if (vif->type == NL80211_IFTYPE_AP) - iwl_mvm_disable_txq(mvm, vif->cab_queue, vif->cab_queue, - IWL_MAX_TID_COUNT, 0); - if (mvmvif->bcast_sta.tfd_queue_msk & BIT(IWL_MVM_DQA_AP_PROBE_RESP_QUEUE)) { iwl_mvm_disable_txq(mvm, @@ -1979,6 +1949,80 @@ int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return ret; } +/* + * Allocate a new station entry for the multicast station to the given vif, + * and send it to the FW. + * Note that each AP/GO mac should have its own multicast station. + * + * @mvm: the mvm component + * @vif: the interface to which the multicast station is added + */ +int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_int_sta *msta = &mvmvif->mcast_sta; + static const u8 _maddr[] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00}; + const u8 *maddr = _maddr; + struct iwl_trans_txq_scd_cfg cfg = { + .fifo = IWL_MVM_TX_FIFO_MCAST, + .sta_id = msta->sta_id, + .tid = IWL_MAX_TID_COUNT, + .aggregate = false, + .frame_limit = IWL_FRAME_LIMIT, + }; + unsigned int timeout = iwl_mvm_get_wd_timeout(mvm, vif, false, false); + int ret; + + lockdep_assert_held(&mvm->mutex); + + if (!iwl_mvm_is_dqa_supported(mvm)) + return 0; + + if (WARN_ON(vif->type != NL80211_IFTYPE_AP)) + return -ENOTSUPP; + + ret = iwl_mvm_add_int_sta_common(mvm, msta, maddr, + mvmvif->id, mvmvif->color); + if (ret) { + iwl_mvm_dealloc_int_sta(mvm, msta); + return ret; + } + + /* + * Enable cab queue after the ADD_STA command is sent. + * This is needed for a000 firmware which won't accept SCD_QUEUE_CFG + * command with unknown station id. + */ + iwl_mvm_enable_txq(mvm, vif->cab_queue, vif->cab_queue, 0, &cfg, + timeout); + + return 0; +} + +/* + * Send the FW a request to remove the station from it's internal data + * structures, and in addition remove it from the local data structure. + */ +int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; + + lockdep_assert_held(&mvm->mutex); + + if (!iwl_mvm_is_dqa_supported(mvm)) + return 0; + + iwl_mvm_disable_txq(mvm, vif->cab_queue, vif->cab_queue, + IWL_MAX_TID_COUNT, 0); + + ret = iwl_mvm_rm_sta_common(mvm, mvmvif->mcast_sta.sta_id); + if (ret) + IWL_WARN(mvm, "Failed sending remove station\n"); + + return ret; +} + #define IWL_MAX_RX_BA_SESSIONS 16 static void iwl_mvm_sync_rxq_del_ba(struct iwl_mvm *mvm, u8 baid) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 1927ce607798..a143a8757e27 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -532,10 +532,13 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_send_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, u32 qmask, enum nl80211_iftype iftype); void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta); int iwl_mvm_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_dealloc_snif_sta(struct iwl_mvm *mvm); -- cgit From 45c458b4c0cad854bb1ae150e637dfe9c5108337 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 9 Nov 2016 15:43:26 +0200 Subject: iwlwifi: mvm: support new ADD_MODIFY_STA_KEY command The command was changed to support PN offload and TKIP offload. The FW will do TKIP calculations in D0 only for a000 devices, but API is aligned anyway. However, for all devices we can stop sending the wowlan tkip command. Firmware will fetch the keys from the station key command. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 3 + drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 4 +- .../net/wireless/intel/iwlwifi/mvm/fw-api-sta.h | 34 ++++++-- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 96 +++++++++++++++------- 4 files changed, 102 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index 9f639fdf0f6e..287e83eb30d9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -241,6 +241,8 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t; * iteration complete notification, and the timestamp reported for RX * received during scan, are reported in TSF of the mac specified in the * scan request. + * @IWL_UCODE_TLV_API_TKIP_MIC_KEYS: This ucode supports version 2 of + * ADD_MODIFY_STA_KEY_API_S_VER_2. * * @NUM_IWL_UCODE_TLV_API: number of bits used */ @@ -250,6 +252,7 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_LQ_SS_PARAMS = (__force iwl_ucode_tlv_api_t)18, IWL_UCODE_TLV_API_NEW_VERSION = (__force iwl_ucode_tlv_api_t)20, IWL_UCODE_TLV_API_SCAN_TSF_REPORT = (__force iwl_ucode_tlv_api_t)28, + IWL_UCODE_TLV_API_TKIP_MIC_KEYS = (__force iwl_ucode_tlv_api_t)29, NUM_IWL_UCODE_TLV_API #ifdef __CHECKER__ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index b7465857b4b6..d65acfa3b89b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -998,7 +998,9 @@ int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm, goto out; } - if (key_data.use_tkip) { + if (key_data.use_tkip && + !fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_TKIP_MIC_KEYS)) { ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_TKIP_PARAM, cmd_flags, sizeof(tkip_cmd), diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h index 3b5150e9975d..cd5fdf83d040 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -389,27 +389,49 @@ struct iwl_mvm_add_sta_cmd { } __packed; /* ADD_STA_CMD_API_S_VER_8 */ /** - * struct iwl_mvm_add_sta_key_cmd - add/modify sta key + * struct iwl_mvm_add_sta_key_common - add/modify sta key common part * ( REPLY_ADD_STA_KEY = 0x17 ) * @sta_id: index of station in uCode's station table * @key_offset: key offset in key storage * @key_flags: type %iwl_sta_key_flag * @key: key material data * @rx_secur_seq_cnt: RX security sequence counter for the key - * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection - * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx */ -struct iwl_mvm_add_sta_key_cmd { +struct iwl_mvm_add_sta_key_common { u8 sta_id; u8 key_offset; __le16 key_flags; u8 key[32]; u8 rx_secur_seq_cnt[16]; +} __packed; + +/** + * struct iwl_mvm_add_sta_key_cmd_v1 - add/modify sta key + * @common: see &struct iwl_mvm_add_sta_key_common + * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection + * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx + */ +struct iwl_mvm_add_sta_key_cmd_v1 { + struct iwl_mvm_add_sta_key_common common; u8 tkip_rx_tsc_byte2; u8 reserved; __le16 tkip_rx_ttak[5]; } __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */ +/** + * struct iwl_mvm_add_sta_key_cmd - add/modify sta key + * @common: see &struct iwl_mvm_add_sta_key_common + * @rx_mic_key: TKIP RX unicast or multicast key + * @tx_mic_key: TKIP TX key + * @transmit_seq_cnt: TSC, transmit packet number + */ +struct iwl_mvm_add_sta_key_cmd { + struct iwl_mvm_add_sta_key_common common; + __le64 rx_mic_key; + __le64 tx_mic_key; + __le64 transmit_seq_cnt; +} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_2 */ + /** * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command * @ADD_STA_SUCCESS: operation was executed successfully diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 41fd3ba574c0..26155e2efa84 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -2738,68 +2738,97 @@ static struct iwl_mvm_sta *iwl_mvm_get_key_sta(struct iwl_mvm *mvm, static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvm_sta, - struct ieee80211_key_conf *keyconf, bool mcast, + struct ieee80211_key_conf *key, bool mcast, u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags, u8 key_offset) { - struct iwl_mvm_add_sta_key_cmd cmd = {}; + union { + struct iwl_mvm_add_sta_key_cmd_v1 cmd_v1; + struct iwl_mvm_add_sta_key_cmd cmd; + } u = {}; __le16 key_flags; int ret; u32 status; u16 keyidx; - int i; - u8 sta_id = mvm_sta->sta_id; + u64 pn = 0; + int i, size; + bool new_api = fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_TKIP_MIC_KEYS); - keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & + keyidx = (key->keyidx << STA_KEY_FLG_KEYID_POS) & STA_KEY_FLG_KEYID_MSK; key_flags = cpu_to_le16(keyidx); key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_KEY_MAP); - switch (keyconf->cipher) { + switch (key->cipher) { case WLAN_CIPHER_SUITE_TKIP: key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP); - cmd.tkip_rx_tsc_byte2 = tkip_iv32; - for (i = 0; i < 5; i++) - cmd.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); - memcpy(cmd.key, keyconf->key, keyconf->keylen); + if (new_api) { + memcpy((void *)&u.cmd.tx_mic_key, + &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], + IWL_MIC_KEY_SIZE); + + memcpy((void *)&u.cmd.rx_mic_key, + &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], + IWL_MIC_KEY_SIZE); + pn = atomic64_read(&key->tx_pn); + + } else { + u.cmd_v1.tkip_rx_tsc_byte2 = tkip_iv32; + for (i = 0; i < 5; i++) + u.cmd_v1.tkip_rx_ttak[i] = + cpu_to_le16(tkip_p1k[i]); + } + memcpy(u.cmd.common.key, key->key, key->keylen); break; case WLAN_CIPHER_SUITE_CCMP: key_flags |= cpu_to_le16(STA_KEY_FLG_CCM); - memcpy(cmd.key, keyconf->key, keyconf->keylen); + memcpy(u.cmd.common.key, key->key, key->keylen); + if (new_api) + pn = atomic64_read(&key->tx_pn); break; case WLAN_CIPHER_SUITE_WEP104: key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_13BYTES); /* fall through */ case WLAN_CIPHER_SUITE_WEP40: key_flags |= cpu_to_le16(STA_KEY_FLG_WEP); - memcpy(cmd.key + 3, keyconf->key, keyconf->keylen); + memcpy(u.cmd.common.key + 3, key->key, key->keylen); break; case WLAN_CIPHER_SUITE_GCMP_256: key_flags |= cpu_to_le16(STA_KEY_FLG_KEY_32BYTES); /* fall through */ case WLAN_CIPHER_SUITE_GCMP: key_flags |= cpu_to_le16(STA_KEY_FLG_GCMP); - memcpy(cmd.key, keyconf->key, keyconf->keylen); + memcpy(u.cmd.common.key, key->key, key->keylen); + if (new_api) + pn = atomic64_read(&key->tx_pn); break; default: key_flags |= cpu_to_le16(STA_KEY_FLG_EXT); - memcpy(cmd.key, keyconf->key, keyconf->keylen); + memcpy(u.cmd.common.key, key->key, key->keylen); } if (mcast) key_flags |= cpu_to_le16(STA_KEY_MULTICAST); - cmd.key_offset = key_offset; - cmd.key_flags = key_flags; - cmd.sta_id = sta_id; + u.cmd.common.key_offset = key_offset; + u.cmd.common.key_flags = key_flags; + u.cmd.common.sta_id = mvm_sta->sta_id; + + if (new_api) { + u.cmd.transmit_seq_cnt = cpu_to_le64(pn); + size = sizeof(u.cmd); + } else { + size = sizeof(u.cmd_v1); + } status = ADD_STA_SUCCESS; if (cmd_flags & CMD_ASYNC) - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, CMD_ASYNC, - sizeof(cmd), &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, CMD_ASYNC, size, + &u.cmd); else - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd), - &cmd, &status); + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, size, + &u.cmd, &status); switch (status) { case ADD_STA_SUCCESS: @@ -2952,9 +2981,14 @@ static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id, struct ieee80211_key_conf *keyconf, bool mcast) { - struct iwl_mvm_add_sta_key_cmd cmd = {}; + union { + struct iwl_mvm_add_sta_key_cmd_v1 cmd_v1; + struct iwl_mvm_add_sta_key_cmd cmd; + } u = {}; + bool new_api = fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_TKIP_MIC_KEYS); __le16 key_flags; - int ret; + int ret, size; u32 status; key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & @@ -2965,13 +2999,19 @@ static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id, if (mcast) key_flags |= cpu_to_le16(STA_KEY_MULTICAST); - cmd.key_flags = key_flags; - cmd.key_offset = keyconf->hw_key_idx; - cmd.sta_id = sta_id; + /* + * The fields assigned here are in the same location at the start + * of the command, so we can do this union trick. + */ + u.cmd.common.key_flags = key_flags; + u.cmd.common.key_offset = keyconf->hw_key_idx; + u.cmd.common.sta_id = sta_id; + + size = new_api ? sizeof(u.cmd) : sizeof(u.cmd_v1); status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd), - &cmd, &status); + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, size, &u.cmd, + &status); switch (status) { case ADD_STA_SUCCESS: -- cgit From 9c36fd711c4340fff922f8a541199a67fd027e9b Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 4 Jan 2017 10:49:42 +0200 Subject: iwlwifi: mvm: optimize reorder timeout frame releasing Currently we release up to the last expired frame. However, if there are consecutive frames after it - we can optimize it further and release them as well - until the next hole. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index d79e9c2a2654..85f7c83995b9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright(c) 2015 - 2017 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 @@ -29,7 +29,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -462,6 +462,7 @@ void iwl_mvm_reorder_timer_expired(unsigned long data) int i; u16 sn = 0, index = 0; bool expired = false; + bool cont = false; spin_lock(&buf->lock); @@ -473,12 +474,21 @@ void iwl_mvm_reorder_timer_expired(unsigned long data) for (i = 0; i < buf->buf_size ; i++) { index = (buf->head_sn + i) % buf->buf_size; - if (skb_queue_empty(&buf->entries[index])) + if (skb_queue_empty(&buf->entries[index])) { + /* + * If there is a hole and the next frame didn't expire + * we want to break and not advance SN + */ + cont = false; continue; - if (!time_after(jiffies, buf->reorder_time[index] + - RX_REORDER_BUF_TIMEOUT_MQ)) + } + if (!cont && !time_after(jiffies, buf->reorder_time[index] + + RX_REORDER_BUF_TIMEOUT_MQ)) break; + expired = true; + /* continue until next hole after this expired frames */ + cont = true; sn = ieee80211_sn_add(buf->head_sn, i + 1); } -- cgit From 49f7171382b55f74f84e5c2dfceca0ed3d1ac3f2 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Mon, 9 Jan 2017 12:07:16 +0200 Subject: iwlwifi: mvm: don't assume static queue numbers In a000 devices FW will assign the queue number. Prepare for that by getting rid of static defines and store them in variables. Enlarge to u16 since we may have up to 512 queues. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 5 ++++- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 22 ++++++++-------------- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 8 ++++---- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 70b994638273..b58ac2aa9999 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -976,7 +976,10 @@ struct iwl_mvm { #endif /* Tx queues */ - u8 aux_queue; + u16 aux_queue; + u16 probe_queue; + u16 p2p_dev_queue; + u8 first_agg_queue; u8 last_agg_queue; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index f4f957fe71d7..600af8a31200 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -603,6 +603,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, } } else { mvm->aux_queue = IWL_MVM_DQA_AUX_QUEUE; + mvm->probe_queue = IWL_MVM_DQA_AP_PROBE_RESP_QUEUE; + mvm->p2p_dev_queue = IWL_MVM_DQA_P2P_DEVICE_QUEUE; mvm->first_agg_queue = IWL_MVM_DQA_MIN_DATA_QUEUE; mvm->last_agg_queue = IWL_MVM_DQA_MAX_DATA_QUEUE; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 26155e2efa84..2d68cee41306 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1806,9 +1806,9 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) int queue; if (vif->type == NL80211_IFTYPE_AP) - queue = IWL_MVM_DQA_AP_PROBE_RESP_QUEUE; + queue = mvm->probe_queue; else if (vif->type == NL80211_IFTYPE_P2P_DEVICE) - queue = IWL_MVM_DQA_P2P_DEVICE_QUEUE; + queue = mvm->p2p_dev_queue; else if (WARN(1, "Missing required TXQ for adding bcast STA\n")) return -EINVAL; @@ -1836,24 +1836,18 @@ static void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); - if (mvmvif->bcast_sta.tfd_queue_msk & - BIT(IWL_MVM_DQA_AP_PROBE_RESP_QUEUE)) { - iwl_mvm_disable_txq(mvm, - IWL_MVM_DQA_AP_PROBE_RESP_QUEUE, + if (mvmvif->bcast_sta.tfd_queue_msk & BIT(mvm->probe_queue)) { + iwl_mvm_disable_txq(mvm, mvm->probe_queue, vif->hw_queue[0], IWL_MAX_TID_COUNT, 0); - mvmvif->bcast_sta.tfd_queue_msk &= - ~BIT(IWL_MVM_DQA_AP_PROBE_RESP_QUEUE); + mvmvif->bcast_sta.tfd_queue_msk &= ~BIT(mvm->probe_queue); } - if (mvmvif->bcast_sta.tfd_queue_msk & - BIT(IWL_MVM_DQA_P2P_DEVICE_QUEUE)) { - iwl_mvm_disable_txq(mvm, - IWL_MVM_DQA_P2P_DEVICE_QUEUE, + if (mvmvif->bcast_sta.tfd_queue_msk & BIT(mvm->p2p_dev_queue)) { + iwl_mvm_disable_txq(mvm, mvm->p2p_dev_queue, vif->hw_queue[0], IWL_MAX_TID_COUNT, 0); - mvmvif->bcast_sta.tfd_queue_msk &= - ~BIT(IWL_MVM_DQA_P2P_DEVICE_QUEUE); + mvmvif->bcast_sta.tfd_queue_msk &= ~BIT(mvm->p2p_dev_queue); } } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 3f37075f4cde..568b0793e84e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -513,20 +513,20 @@ static int iwl_mvm_get_ctrl_vif_queue(struct iwl_mvm *mvm, */ if (ieee80211_is_probe_resp(fc) || ieee80211_is_auth(fc) || ieee80211_is_deauth(fc)) - return IWL_MVM_DQA_AP_PROBE_RESP_QUEUE; + return mvm->probe_queue; if (info->hw_queue == info->control.vif->cab_queue) return info->hw_queue; WARN_ONCE(1, "fc=0x%02x", le16_to_cpu(fc)); - return IWL_MVM_DQA_AP_PROBE_RESP_QUEUE; + return mvm->probe_queue; case NL80211_IFTYPE_P2P_DEVICE: if (ieee80211_is_mgmt(fc)) - return IWL_MVM_DQA_P2P_DEVICE_QUEUE; + return mvm->p2p_dev_queue; if (info->hw_queue == info->control.vif->cab_queue) return info->hw_queue; WARN_ON_ONCE(1); - return IWL_MVM_DQA_P2P_DEVICE_QUEUE; + return mvm->p2p_dev_queue; default: WARN_ONCE(1, "Not a ctrl vif, no available queue\n"); return -1; -- cgit From b9f916202997f515456c09e2c893b0dc83049d31 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 28 Sep 2016 15:56:21 +0300 Subject: Revert "iwlwifi: introduce trans API to get byte count table" This reverts commit 8aacf4b73fe8 ("iwlwifi: introduce trans API to get byte count table"). The commit is not needed as a better approach will be taken. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 11 ----------- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 1 - drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 -- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 8 -------- 4 files changed, 22 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 0296124a7f9c..b52804dd884f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -644,8 +644,6 @@ struct iwl_trans_ops { void (*txq_set_shared_mode)(struct iwl_trans *trans, u32 txq_id, bool shared); - dma_addr_t (*get_txq_byte_table)(struct iwl_trans *trans, int txq_id); - int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm); void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs, bool freeze); @@ -1072,15 +1070,6 @@ static inline void iwl_trans_txq_set_shared_mode(struct iwl_trans *trans, trans->ops->txq_set_shared_mode(trans, queue, shared_mode); } -static inline dma_addr_t iwl_trans_get_txq_byte_table(struct iwl_trans *trans, - int queue) -{ - /* we should never be called if the trans doesn't support it */ - BUG_ON(!trans->ops->get_txq_byte_table); - - return trans->ops->get_txq_byte_table(trans, queue); -} - static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, int fifo, int sta_id, int tid, int frame_limit, u16 ssn, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 10937309641a..541b969fe283 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -484,7 +484,6 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue, bool configure_scd); void iwl_trans_pcie_txq_set_shared_mode(struct iwl_trans *trans, u32 txq_id, bool shared_mode); -dma_addr_t iwl_trans_pcie_get_txq_byte_table(struct iwl_trans *trans, int txq); void iwl_trans_pcie_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq); int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index fbb0afa9de14..69f1b032260c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2897,8 +2897,6 @@ static const struct iwl_trans_ops trans_ops_pcie = { .txq_disable = iwl_trans_pcie_txq_disable, .txq_enable = iwl_trans_pcie_txq_enable, - .get_txq_byte_table = iwl_trans_pcie_get_txq_byte_table, - .txq_set_shared_mode = iwl_trans_pcie_txq_set_shared_mode, .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 03df578bed00..a313c187524b 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -1424,14 +1424,6 @@ void iwl_trans_pcie_txq_set_shared_mode(struct iwl_trans *trans, u32 txq_id, txq->ampdu = !shared_mode; } -dma_addr_t iwl_trans_pcie_get_txq_byte_table(struct iwl_trans *trans, int txq) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - return trans_pcie->scd_bc_tbls.dma + - txq * sizeof(struct iwlagn_scd_bc_tbl); -} - void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id, bool configure_scd) { -- cgit From de74c455fd991024be76a535df4d66e96af3896e Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 29 Sep 2016 14:31:24 +0300 Subject: iwlwifi: pcie: remove the active field in struct iwl_txq We already have queue_used in the transport - we can use it instead. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 2 -- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 7 +++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 541b969fe283..1ef9bb83a43f 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -237,7 +237,6 @@ struct iwl_pcie_first_tb_buf { * @stuck_timer: timer that fires if queue gets stuck * @trans_pcie: pointer back to transport (for timer) * @need_update: indicates need to update read/write index - * @active: stores if queue is active * @ampdu: true if this queue is an ampdu queue for an specific RA/TID * @wd_timeout: queue watchdog timeout (jiffies) - per queue * @frozen: tx stuck queue timer is frozen @@ -277,7 +276,6 @@ struct iwl_txq { struct iwl_trans_pcie *trans_pcie; bool need_update; bool frozen; - u8 active; bool ampdu; int block; unsigned long wd_timeout; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index a313c187524b..66bdd2df910a 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -701,7 +701,6 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); } } - txq->active = false; while (!skb_queue_empty(&txq->overflow_q)) { struct sk_buff *skb = __skb_dequeue(&txq->overflow_q); @@ -932,6 +931,8 @@ void iwl_pcie_tx_free(struct iwl_trans *trans) int txq_id; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); + /* Tx queues */ if (trans_pcie->txq) { for (txq_id = 0; @@ -1107,7 +1108,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, spin_lock_bh(&txq->lock); - if (!txq->active) { + if (!test_bit(txq_id, trans_pcie->queue_used)) { IWL_DEBUG_TX_QUEUES(trans, "Q %d inactive - ignoring idx %d\n", txq_id, ssn); goto out; @@ -1411,8 +1412,6 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, "Activate queue %d WrPtr: %d\n", txq_id, ssn & 0xff); } - - txq->active = true; } void iwl_trans_pcie_txq_set_shared_mode(struct iwl_trans *trans, u32 txq_id, -- cgit From 82ea79660c2744c6d273862a455ca634a6c4d2ee Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 28 Dec 2016 10:04:23 +0200 Subject: iwlwifi: pcie: use WFPM_GP for debugging D3 flows This register is helpful for debugging D3 issues. Driver turns all bits on, and then on exit reads the updated value there. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 1 + drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 406ef301b8ab..3a8aeee3b55a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -378,6 +378,7 @@ #define RADIO_REG_SYS_MANUAL_DFT_0 0xAD4078 #define RFIC_REG_RD 0xAD0470 #define WFPM_CTRL_REG 0xA03030 +#define WFPM_GP2 0xA030B4 enum { ENABLE_WFPM = BIT(31), WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK = 0x80000000, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 69f1b032260c..15bf5100d096 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1047,6 +1047,16 @@ static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans, if (ret) return ret; + IWL_DEBUG_POWER(trans, "Original WFPM value = 0x%08X\n", + iwl_read_prph(trans, WFPM_GP2)); + + /* + * Set default value. On resume reading the values that were + * zeored can provide debug data on the resume flow. + * This is for debugging only and has no functional impact. + */ + iwl_write_prph(trans, WFPM_GP2, 0x01010101); + /* configure the ucode to be ready to get the secured image */ /* release CPU reset */ iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT); @@ -1527,6 +1537,9 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, } } + IWL_DEBUG_POWER(trans, "WFPM value upon resume = 0x%08X\n", + iwl_read_prph(trans, WFPM_GP2)); + val = iwl_read32(trans, CSR_RESET); if (val & CSR_RESET_REG_FLAG_NEVO_RESET) *status = IWL_D3_STATUS_RESET; -- cgit From 7abf6fde970cc27dda7a8f02439fd0b8a0156459 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 9 Jan 2017 11:31:39 +0100 Subject: iwlwifi: pcie: use iwl_get_dma_hi_addr() Use iwl_get_dma_hi_addr() instead of open-coding it. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 66bdd2df910a..9c8ab1a82b72 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -380,8 +380,7 @@ static inline void iwl_pcie_tfd_set_tb(struct iwl_trans *trans, void *tfd, u16 hi_n_len = len << 4; put_unaligned_le32(addr, &tb->lo); - if (sizeof(dma_addr_t) > sizeof(u32)) - hi_n_len |= ((addr >> 16) >> 16) & 0xF; + hi_n_len |= iwl_get_dma_hi_addr(addr); tb->hi_n_len = cpu_to_le16(hi_n_len); -- cgit From bdccdb854f2fb473f2ac4a6108df3cbfcedd5a87 Mon Sep 17 00:00:00 2001 From: Golan Ben-Ami Date: Tue, 15 Nov 2016 14:45:29 +0200 Subject: iwlwifi: mvm: support MFUART dump in case of MFUART assert In case of a MFUART assert, get a notification from the fw that consists of the assert id and debug data. The notification may be divided to multiple chunks, depending on the size of the debug data sent to the driver, which would be up to 1KB. Get the notification, and if the debug info flag is enabled, print the debug data to the dmesg. Signed-off-by: Golan Ben-Ami Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 26 ++++++++++++++++++++++--- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 24 ++++++++++++++++++++++- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 9 +++++++++ 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 248373a0990a..dd2bd7c3587c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -345,9 +345,10 @@ enum iwl_regulatory_and_nvm_subcmd_ids { NVM_ACCESS_COMPLETE = 0x0, }; -enum iwl_fmac_debug_cmds { +enum iwl_debug_cmds { LMAC_RD_WR = 0x0, UMAC_RD_WR = 0x1, + MFU_ASSERT_DUMP_NTF = 0xFE, }; /* command groups */ @@ -1238,6 +1239,25 @@ struct iwl_mfuart_load_notif { __le32 image_size; } __packed; /*MFU_LOADER_NTFY_API_S_VER_2*/ +/** + * struct iwl_mfu_assert_dump_notif - mfuart dump logs + * ( MFU_ASSERT_DUMP_NTF = 0xfe ) + * @assert_id: mfuart assert id that cause the notif + * @curr_reset_num: number of asserts since uptime + * @index_num: current chunk id + * @parts_num: total number of chunks + * @data_size: number of data bytes sent + * @data: data buffer + */ +struct iwl_mfu_assert_dump_notif { + __le32 assert_id; + __le32 curr_reset_num; + __le16 index_num; + __le16 parts_num; + __le32 data_size; + __le32 data[0]; +} __packed; /*MFU_DUMP_ASSERT_API_S_VER_1*/ + /** * struct iwl_set_calib_default_cmd - set default value for calibration. * ( SET_CALIB_DEFAULT_CMD = 0x8e ) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 45cb4f476e76..286f18609729 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -271,6 +272,27 @@ static int iwl_fill_paging_mem(struct iwl_mvm *mvm, const struct fw_img *image) return 0; } +void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mfu_assert_dump_notif *mfu_dump_notif = (void *)pkt->data; + __le32 *dump_data = mfu_dump_notif->data; + int n_words = le32_to_cpu(mfu_dump_notif->data_size) / sizeof(__le32); + int i; + + if (mfu_dump_notif->index_num == 0) + IWL_INFO(mvm, "MFUART assert id 0x%x occurred\n", + le32_to_cpu(mfu_dump_notif->assert_id)); + + for (i = 0; i < n_words; i++) + IWL_DEBUG_INFO(mvm, + "MFUART assert dump, dword %u: 0x%08x\n", + le16_to_cpu(mfu_dump_notif->index_num) * + n_words + i, + le32_to_cpu(dump_data[i])); +} + static int iwl_alloc_fw_paging_mem(struct iwl_mvm *mvm, const struct fw_img *image) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index b58ac2aa9999..b3f67fe98203 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1395,6 +1395,8 @@ int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask, void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, int queue); void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); +void iwl_mvm_mfu_assert_dump_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 600af8a31200..72afccfe4282 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -302,6 +302,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER_SYNC), RX_HANDLER(TOF_NOTIFICATION, iwl_mvm_tof_resp_handler, RX_HANDLER_ASYNC_LOCKED), + RX_HANDLER_GRP(DEBUG_GROUP, MFU_ASSERT_DUMP_NTF, + iwl_mvm_mfu_assert_dump_notif, RX_HANDLER_SYNC), RX_HANDLER_GRP(PROT_OFFLOAD_GROUP, STORED_BEACON_NTF, iwl_mvm_rx_stored_beacon_notif, RX_HANDLER_SYNC), RX_HANDLER_GRP(DATA_PATH_GROUP, MU_GROUP_MGMT_NOTIF, @@ -460,6 +462,13 @@ static const struct iwl_hcmd_names iwl_mvm_data_path_names[] = { HCMD_NAME(RX_QUEUES_NOTIFICATION), }; +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mvm_debug_names[] = { + HCMD_NAME(MFU_ASSERT_DUMP_NTF), +}; + /* Please keep this array *SORTED* by hex value. * Access is done through binary search */ -- cgit From 1ea423b0e047757dea80d5d968a25aa44af51273 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 11 Apr 2017 14:46:35 +0300 Subject: iwlwifi: remove unnecessary dev_cmd_headroom parameter We don't need this parameter anymore, since we always pass 0 anyway. Remove it from the structure and from all the relevant functions. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 7 ++----- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 17 +++++------------ drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 +- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index d42cab291025..0bde26bab15d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -70,8 +70,7 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, struct device *dev, const struct iwl_cfg *cfg, - const struct iwl_trans_ops *ops, - size_t dev_cmd_headroom) + const struct iwl_trans_ops *ops) { struct iwl_trans *trans; #ifdef CONFIG_LOCKDEP @@ -90,15 +89,13 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, trans->dev = dev; 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)); trans->dev_cmd_pool = kmem_cache_create(trans->dev_cmd_pool_name, - sizeof(struct iwl_device_cmd) - + trans->dev_cmd_headroom, + sizeof(struct iwl_device_cmd), sizeof(void *), SLAB_HWCACHE_ALIGN, NULL); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index b52804dd884f..1b4b62ede2c4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -772,9 +772,6 @@ enum iwl_plat_pm_mode { * 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 - * device_cmd for Tx - for internal use only - * The user should use iwl_trans_{alloc,free}_tx_cmd. * @rx_mpdu_cmd: MPDU RX command ID, must be assigned by opmode before * starting the firmware, used for tracing * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the @@ -825,7 +822,6 @@ struct iwl_trans { /* The following fields are internal only */ struct kmem_cache *dev_cmd_pool; - size_t dev_cmd_headroom; char dev_cmd_pool_name[50]; struct dentry *dbgfs_dir; @@ -998,13 +994,13 @@ iwl_trans_dump_data(struct iwl_trans *trans, static inline struct iwl_device_cmd * iwl_trans_alloc_tx_cmd(struct iwl_trans *trans) { - u8 *dev_cmd_ptr = kmem_cache_alloc(trans->dev_cmd_pool, GFP_ATOMIC); + struct iwl_device_cmd *dev_cmd_ptr = + kmem_cache_alloc(trans->dev_cmd_pool, GFP_ATOMIC); if (unlikely(dev_cmd_ptr == NULL)) return NULL; - return (struct iwl_device_cmd *) - (dev_cmd_ptr + trans->dev_cmd_headroom); + return dev_cmd_ptr; } int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); @@ -1012,9 +1008,7 @@ int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans, struct iwl_device_cmd *dev_cmd) { - u8 *dev_cmd_ptr = (u8 *)dev_cmd - trans->dev_cmd_headroom; - - kmem_cache_free(trans->dev_cmd_pool, dev_cmd_ptr); + kmem_cache_free(trans->dev_cmd_pool, dev_cmd); } static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, @@ -1237,8 +1231,7 @@ static inline void iwl_trans_fw_error(struct iwl_trans *trans) struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, struct device *dev, const struct iwl_cfg *cfg, - const struct iwl_trans_ops *ops, - size_t dev_cmd_headroom); + const struct iwl_trans_ops *ops); void iwl_trans_free(struct iwl_trans *trans); /***************************************************** diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 15bf5100d096..b362779b66f4 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2948,7 +2948,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, return ERR_PTR(ret); trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie), - &pdev->dev, cfg, &trans_ops_pcie, 0); + &pdev->dev, cfg, &trans_ops_pcie); if (!trans) return ERR_PTR(-ENOMEM); -- cgit From 623e7766be907d9c20af334e25f98005c75b5d32 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 28 Sep 2016 15:52:21 +0300 Subject: iwlwifi: pcie: introduce split point to a000 devices a000 devices are going to have a lot of flows simplified and changed: init flow, RX, TX, and more. This, combined with the fact that code is already very complicated due to backward compatibility - introduce a split that will enable to introduce simplified version of functions. Shared ops are moved to a macro, while functions that will be updated in the next patches are defined twice for now. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-a000.c | 3 +- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 4 +- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 85 +++++++++++++++++-------- 3 files changed, 62 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c index 20739e92aef6..df4e8714d627 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c @@ -121,7 +121,8 @@ static const struct iwl_ht_params iwl_a000_ht_params = { .vht_mu_mimo_supported = true, \ .mac_addr_from_csr = true, \ .use_tfh = true, \ - .rf_id = true + .rf_id = true, \ + .gen2 = true const struct iwl_cfg iwla000_2ac_cfg_hr = { .name = "Intel(R) Dual Band Wireless AC a000", diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 852963487bd9..717e089ff036 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -323,6 +323,7 @@ struct iwl_pwr_tx_backoff { * @vht_mu_mimo_supported: VHT MU-MIMO support * @rf_id: need to read rf_id to determine the firmware image * @integrated: discrete or integrated + * @gen2: a000 and on transport operation * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -368,7 +369,8 @@ struct iwl_cfg { vht_mu_mimo_supported:1, rf_id:1, integrated:1, - use_tfh:1; + use_tfh:1, + gen2:1; u8 valid_tx_ant; u8 valid_rx_ant; u8 non_shared_ant; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index b362779b66f4..16c8b37bf6d7 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2887,20 +2887,64 @@ static void iwl_trans_pcie_resume(struct iwl_trans *trans) } #endif /* CONFIG_PM_SLEEP */ +#define IWL_TRANS_COMMON_OPS \ + .op_mode_leave = iwl_trans_pcie_op_mode_leave, \ + .write8 = iwl_trans_pcie_write8, \ + .write32 = iwl_trans_pcie_write32, \ + .read32 = iwl_trans_pcie_read32, \ + .read_prph = iwl_trans_pcie_read_prph, \ + .write_prph = iwl_trans_pcie_write_prph, \ + .read_mem = iwl_trans_pcie_read_mem, \ + .write_mem = iwl_trans_pcie_write_mem, \ + .configure = iwl_trans_pcie_configure, \ + .set_pmi = iwl_trans_pcie_set_pmi, \ + .grab_nic_access = iwl_trans_pcie_grab_nic_access, \ + .release_nic_access = iwl_trans_pcie_release_nic_access, \ + .set_bits_mask = iwl_trans_pcie_set_bits_mask, \ + .ref = iwl_trans_pcie_ref, \ + .unref = iwl_trans_pcie_unref, \ + .dump_data = iwl_trans_pcie_dump_data, \ + .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty, \ + .d3_suspend = iwl_trans_pcie_d3_suspend, \ + .d3_resume = iwl_trans_pcie_d3_resume + +#ifdef CONFIG_PM_SLEEP +#define IWL_TRANS_PM_OPS \ + .suspend = iwl_trans_pcie_suspend, \ + .resume = iwl_trans_pcie_resume, +#else +#define IWL_TRANS_PM_OPS +#endif /* CONFIG_PM_SLEEP */ + static const struct iwl_trans_ops trans_ops_pcie = { + IWL_TRANS_COMMON_OPS, + IWL_TRANS_PM_OPS .start_hw = iwl_trans_pcie_start_hw, - .op_mode_leave = iwl_trans_pcie_op_mode_leave, .fw_alive = iwl_trans_pcie_fw_alive, .start_fw = iwl_trans_pcie_start_fw, .stop_device = iwl_trans_pcie_stop_device, - .d3_suspend = iwl_trans_pcie_d3_suspend, - .d3_resume = iwl_trans_pcie_d3_resume, + .send_cmd = iwl_trans_pcie_send_hcmd, + + .tx = iwl_trans_pcie_tx, + .reclaim = iwl_trans_pcie_reclaim, -#ifdef CONFIG_PM_SLEEP - .suspend = iwl_trans_pcie_suspend, - .resume = iwl_trans_pcie_resume, -#endif /* CONFIG_PM_SLEEP */ + .txq_disable = iwl_trans_pcie_txq_disable, + .txq_enable = iwl_trans_pcie_txq_enable, + + .txq_set_shared_mode = iwl_trans_pcie_txq_set_shared_mode, + + .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, + .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs, +}; + +static const struct iwl_trans_ops trans_ops_pcie_gen2 = { + IWL_TRANS_COMMON_OPS, + IWL_TRANS_PM_OPS + .start_hw = iwl_trans_pcie_start_hw, + .fw_alive = iwl_trans_pcie_fw_alive, + .start_fw = iwl_trans_pcie_start_fw, + .stop_device = iwl_trans_pcie_stop_device, .send_cmd = iwl_trans_pcie_send_hcmd, @@ -2912,27 +2956,8 @@ static const struct iwl_trans_ops trans_ops_pcie = { .txq_set_shared_mode = iwl_trans_pcie_txq_set_shared_mode, - .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty, .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs, - - .write8 = iwl_trans_pcie_write8, - .write32 = iwl_trans_pcie_write32, - .read32 = iwl_trans_pcie_read32, - .read_prph = iwl_trans_pcie_read_prph, - .write_prph = iwl_trans_pcie_write_prph, - .read_mem = iwl_trans_pcie_read_mem, - .write_mem = iwl_trans_pcie_write_mem, - .configure = iwl_trans_pcie_configure, - .set_pmi = iwl_trans_pcie_set_pmi, - .grab_nic_access = iwl_trans_pcie_grab_nic_access, - .release_nic_access = iwl_trans_pcie_release_nic_access, - .set_bits_mask = iwl_trans_pcie_set_bits_mask, - - .ref = iwl_trans_pcie_ref, - .unref = iwl_trans_pcie_unref, - - .dump_data = iwl_trans_pcie_dump_data, }; struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, @@ -2947,8 +2972,12 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, if (ret) return ERR_PTR(ret); - trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie), - &pdev->dev, cfg, &trans_ops_pcie); + if (cfg->gen2) + trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie), + &pdev->dev, cfg, &trans_ops_pcie_gen2); + else + trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie), + &pdev->dev, cfg, &trans_ops_pcie); if (!trans) return ERR_PTR(-ENOMEM); -- cgit From eda50cde58de7368f982e3906090fc033ecb9360 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 28 Sep 2016 17:16:53 +0300 Subject: iwlwifi: pcie: add context information support Context information structure is going to be used in a000 devices for firmware self init. The self init includes firmware self loading from DRAM by ROM. This means the TFH relevant firmware loading can be cleaned up. The firmware loading includes the paging memory as well, so op mode can stop initializing the paging and sending the DRAM_BLOCK_CMD. Firmware is doing RFH, TFH and SCD configuration, while driver only fills the required configurations and addresses in the context information structure. The only remaining access to RFH is the write pointer, which is updated upon alive interrupt after FW configured the RFH. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/Makefile | 1 + .../net/wireless/intel/iwlwifi/iwl-context-info.h | 203 +++++++++++++++ drivers/net/wireless/intel/iwlwifi/iwl-fh.h | 4 + drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 1 + .../net/wireless/intel/iwlwifi/pcie/ctxt-info.c | 274 +++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 50 +++- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 45 +++- .../net/wireless/intel/iwlwifi/pcie/trans-gen2.c | 226 +++++++++++++++++ drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 60 ++--- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 78 ++++-- 10 files changed, 867 insertions(+), 75 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/iwl-context-info.h create mode 100644 drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c create mode 100644 drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index 92e611841200..b27bcac8f5db 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -7,6 +7,7 @@ iwlwifi-objs += iwl-notif-wait.o iwlwifi-objs += iwl-eeprom-read.o iwl-eeprom-parse.o iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o +iwlwifi-objs += pcie/ctxt-info.o pcie/trans-gen2.o iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o iwl-9000.o iwl-a000.o iwlwifi-objs += iwl-trans.o diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h b/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h new file mode 100644 index 000000000000..b870c0986744 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h @@ -0,0 +1,203 @@ +/****************************************************************************** + * + * 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) 2017 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. + * + * BSD LICENSE + * + * Copyright(c) 2017 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 __iwl_context_info_file_h__ +#define __iwl_context_info_file_h__ + +/* maximmum number of DRAM map entries supported by FW */ +#define IWL_MAX_DRAM_ENTRY 64 +#define CSR_CTXT_INFO_BA 0x40 + +/** + * enum iwl_context_info_flags - Context information control flags + * @IWL_CTXT_INFO_AUTO_FUNC_INIT: If set, FW will not wait before interrupting + * the init done for driver command that configures several system modes + * @IWL_CTXT_INFO_EARLY_DEBUG: enable early debug + * @IWL_CTXT_INFO_ENABLE_CDMP: enable core dump + * @IWL_CTXT_INFO_RB_SIZE_4K: Use 4K RB size (the default is 2K) + * @IWL_CTXT_INFO_RB_CB_SIZE_POS: position of the RBD Cyclic Buffer Size + * exponent, the actual size is 2**value, valid sizes are 8-2048. + * The value is four bits long. Maximum valid exponent is 12 + * @IWL_CTXT_INFO_TFD_FORMAT_LONG: use long TFD Format (the + * default is short format - not supported by the driver) + */ +enum iwl_context_info_flags { + IWL_CTXT_INFO_AUTO_FUNC_INIT = BIT(0), + IWL_CTXT_INFO_EARLY_DEBUG = BIT(1), + IWL_CTXT_INFO_ENABLE_CDMP = BIT(2), + IWL_CTXT_INFO_RB_SIZE_4K = BIT(3), + IWL_CTXT_INFO_RB_CB_SIZE_POS = 4, + IWL_CTXT_INFO_TFD_FORMAT_LONG = BIT(8), +}; + +/* + * struct iwl_context_info_version - version structure + * @mac_id: SKU and revision id + * @version: context information version id + * @size: the size of the context information in DWs + */ +struct iwl_context_info_version { + __le16 mac_id; + __le16 version; + __le16 size; + __le16 reserved; +} __packed; + +/* + * struct iwl_context_info_control - version structure + * @control_flags: context information flags see &enum iwl_context_info_flags + */ +struct iwl_context_info_control { + __le32 control_flags; + __le32 reserved; +} __packed; + +/* + * struct iwl_context_info_dram - images DRAM map + * each entry in the map represents a DRAM chunk of up to 32 KB + * @umac_img: UMAC image DRAM map + * @lmac_img: LMAC image DRAM map + * @virtual_img: paged image DRAM map + */ +struct iwl_context_info_dram { + __le64 umac_img[IWL_MAX_DRAM_ENTRY]; + __le64 lmac_img[IWL_MAX_DRAM_ENTRY]; + __le64 virtual_img[IWL_MAX_DRAM_ENTRY]; +} __packed; + +/* + * struct iwl_context_info_rbd_cfg - RBDs configuration + * @free_rbd_addr: default queue free RB CB base address + * @used_rbd_addr: default queue used RB CB base address + * @status_wr_ptr: default queue used RB status write pointer + */ +struct iwl_context_info_rbd_cfg { + __le64 free_rbd_addr; + __le64 used_rbd_addr; + __le64 status_wr_ptr; +} __packed; + +/* + * struct iwl_context_info_hcmd_cfg - command queue configuration + * @cmd_queue_addr: address of command queue + * @cmd_queue_size: number of entries + */ +struct iwl_context_info_hcmd_cfg { + __le64 cmd_queue_addr; + u8 cmd_queue_size; + u8 reserved[7]; +} __packed; + +/* + * struct iwl_context_info_dump_cfg - Core Dump configuration + * @core_dump_addr: core dump (debug DRAM address) start address + * @core_dump_size: size, in DWs + */ +struct iwl_context_info_dump_cfg { + __le64 core_dump_addr; + __le32 core_dump_size; + __le32 reserved; +} __packed; + +/* + * struct iwl_context_info_pnvm_cfg - platform NVM data configuration + * @platform_nvm_addr: Platform NVM data start address + * @platform_nvm_size: size in DWs + */ +struct iwl_context_info_pnvm_cfg { + __le64 platform_nvm_addr; + __le32 platform_nvm_size; + __le32 reserved; +} __packed; + +/* + * struct iwl_context_info_early_dbg_cfg - early debug configuration for + * dumping DRAM addresses + * @early_debug_addr: early debug start address + * @early_debug_size: size in DWs + */ +struct iwl_context_info_early_dbg_cfg { + __le64 early_debug_addr; + __le32 early_debug_size; + __le32 reserved; +} __packed; + +/* + * struct iwl_context_info - device INIT configuration + * @version: version information of context info and HW + * @control: control flags of FH configurations + * @rbd_cfg: default RX queue configuration + * @hcmd_cfg: command queue configuration + * @dump_cfg: core dump data + * @edbg_cfg: early debug configuration + * @pnvm_cfg: platform nvm configuration + * @dram: firmware image addresses in DRAM + */ +struct iwl_context_info { + struct iwl_context_info_version version; + struct iwl_context_info_control control; + __le64 reserved0; + struct iwl_context_info_rbd_cfg rbd_cfg; + struct iwl_context_info_hcmd_cfg hcmd_cfg; + __le32 reserved1[4]; + struct iwl_context_info_dump_cfg dump_cfg; + struct iwl_context_info_early_dbg_cfg edbg_cfg; + struct iwl_context_info_pnvm_cfg pnvm_cfg; + __le32 reserved2[16]; + struct iwl_context_info_dram dram; + __le32 reserved3[16]; +} __packed; + +int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, const struct fw_img *fw); +void iwl_pcie_ctxt_info_free(struct iwl_trans *trans); +void iwl_pcie_ctxt_info_free_paging(struct iwl_trans *trans); + +#endif /* __iwl_context_info_file_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h index 361fc25e8618..62f9fe926d78 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h @@ -614,6 +614,8 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(struct iwl_trans *trans, #define RX_POOL_SIZE (MQ_RX_NUM_RBDS + \ IWL_MAX_RX_HW_QUEUES * \ (RX_CLAIM_REQ_ALLOC - RX_POST_REQ_ALLOC)) +/* cb size is the exponent */ +#define RX_QUEUE_CB_SIZE(x) ilog2(x) #define RX_QUEUE_SIZE 256 #define RX_QUEUE_MASK 255 @@ -639,6 +641,8 @@ struct iwl_rb_status { #define TFD_QUEUE_SIZE_MAX (256) +/* cb size is the exponent - 3 */ +#define TFD_QUEUE_CB_SIZE(x) (ilog2(x) - 3) #define TFD_QUEUE_SIZE_BC_DUP (64) #define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP) #define IWL_TX_DMA_MASK DMA_BIT_MASK(36) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 3a8aeee3b55a..bb7e0d642981 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -309,6 +309,7 @@ * Note this address is cleared after MAC reset. */ #define UREG_UCODE_LOAD_STATUS (0xa05c40) +#define UREG_CPU_INIT_RUN (0xa05c44) #define LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR (0x1E78) #define LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR (0x1E7C) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c new file mode 100644 index 000000000000..312ee0481ec5 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c @@ -0,0 +1,274 @@ +/****************************************************************************** + * + * 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) 2017 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. + * + * BSD LICENSE + * + * Copyright(c) 2017 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. + * + *****************************************************************************/ + +#include "iwl-trans.h" +#include "iwl-fh.h" +#include "iwl-context-info.h" +#include "internal.h" +#include "iwl-prph.h" + +static int iwl_pcie_get_num_sections(const struct fw_img *fw, + int start) +{ + int i = 0; + + while (start < fw->num_sec && + fw->sec[start].offset != CPU1_CPU2_SEPARATOR_SECTION && + fw->sec[start].offset != PAGING_SEPARATOR_SECTION) { + start++; + i++; + } + + return i; +} + +static int iwl_pcie_ctxt_info_alloc_dma(struct iwl_trans *trans, + const struct fw_desc *sec, + struct iwl_dram_data *dram) +{ + dram->block = dma_alloc_coherent(trans->dev, sec->len, + &dram->physical, + GFP_KERNEL); + if (!dram->block) + return -ENOMEM; + + dram->size = sec->len; + memcpy(dram->block, sec->data, sec->len); + + return 0; +} + +static void iwl_pcie_ctxt_info_free_fw_img(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_self_init_dram *dram = &trans_pcie->init_dram; + int i; + + if (!dram->fw) { + WARN_ON(dram->fw_cnt); + return; + } + + for (i = 0; i < dram->fw_cnt; i++) + dma_free_coherent(trans->dev, dram->fw[i].size, + dram->fw[i].block, dram->fw[i].physical); + + kfree(dram->fw); + dram->fw_cnt = 0; +} + +void iwl_pcie_ctxt_info_free_paging(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_self_init_dram *dram = &trans_pcie->init_dram; + int i; + + if (!dram->paging) { + WARN_ON(dram->paging_cnt); + return; + } + + /* free paging*/ + for (i = 0; i < dram->paging_cnt; i++) + dma_free_coherent(trans->dev, dram->paging[i].size, + dram->paging[i].block, + dram->paging[i].physical); + + kfree(dram->paging); + dram->paging_cnt = 0; +} + +static int iwl_pcie_ctxt_info_init_fw_sec(struct iwl_trans *trans, + const struct fw_img *fw, + struct iwl_context_info *ctxt_info) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_self_init_dram *dram = &trans_pcie->init_dram; + struct iwl_context_info_dram *ctxt_dram = &ctxt_info->dram; + int i, ret, lmac_cnt, umac_cnt, paging_cnt; + + lmac_cnt = iwl_pcie_get_num_sections(fw, 0); + /* add 1 due to separator */ + umac_cnt = iwl_pcie_get_num_sections(fw, lmac_cnt + 1); + /* add 2 due to separators */ + paging_cnt = iwl_pcie_get_num_sections(fw, lmac_cnt + umac_cnt + 2); + + dram->fw = kcalloc(umac_cnt + lmac_cnt, sizeof(*dram->fw), GFP_KERNEL); + if (!dram->fw) + return -ENOMEM; + dram->paging = kcalloc(paging_cnt, sizeof(*dram->paging), GFP_KERNEL); + if (!dram->paging) + return -ENOMEM; + + /* initialize lmac sections */ + for (i = 0; i < lmac_cnt; i++) { + ret = iwl_pcie_ctxt_info_alloc_dma(trans, &fw->sec[i], + &dram->fw[dram->fw_cnt]); + if (ret) + return ret; + ctxt_dram->lmac_img[i] = + cpu_to_le64(dram->fw[dram->fw_cnt].physical); + dram->fw_cnt++; + } + + /* initialize umac sections */ + for (i = 0; i < umac_cnt; i++) { + /* access FW with +1 to make up for lmac separator */ + ret = iwl_pcie_ctxt_info_alloc_dma(trans, + &fw->sec[dram->fw_cnt + 1], + &dram->fw[dram->fw_cnt]); + if (ret) + return ret; + ctxt_dram->umac_img[i] = + cpu_to_le64(dram->fw[dram->fw_cnt].physical); + dram->fw_cnt++; + } + + /* + * Initialize paging. + * Paging memory isn't stored in dram->fw as the umac and lmac - it is + * stored separately. + * This is since the timing of its release is different - + * while fw memory can be released on alive, the paging memory can be + * freed only when the device goes down. + * Given that, the logic here in accessing the fw image is a bit + * different - fw_cnt isn't changing so loop counter is added to it. + */ + for (i = 0; i < paging_cnt; i++) { + /* access FW with +2 to make up for lmac & umac separators */ + int fw_idx = dram->fw_cnt + i + 2; + + ret = iwl_pcie_ctxt_info_alloc_dma(trans, &fw->sec[fw_idx], + &dram->paging[i]); + if (ret) + return ret; + + ctxt_dram->virtual_img[i] = + cpu_to_le64(dram->paging[i].physical); + dram->paging_cnt++; + } + + return 0; +} + +int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, + const struct fw_img *fw) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_context_info *ctxt_info; + struct iwl_context_info_rbd_cfg *rx_cfg; + u32 control_flags = 0; + int ret; + + ctxt_info = dma_alloc_coherent(trans->dev, sizeof(*ctxt_info), + &trans_pcie->ctxt_info_dma_addr, + GFP_KERNEL); + if (!ctxt_info) + return -ENOMEM; + + ctxt_info->version.version = 0; + ctxt_info->version.mac_id = + cpu_to_le16((u16)iwl_read32(trans, CSR_HW_REV)); + /* size is in DWs */ + ctxt_info->version.size = cpu_to_le16(sizeof(*ctxt_info) / 4); + + BUILD_BUG_ON(RX_QUEUE_CB_SIZE(MQ_RX_TABLE_SIZE) > 0xF); + control_flags = IWL_CTXT_INFO_RB_SIZE_4K | + IWL_CTXT_INFO_TFD_FORMAT_LONG | + RX_QUEUE_CB_SIZE(MQ_RX_TABLE_SIZE) << + IWL_CTXT_INFO_RB_CB_SIZE_POS; + ctxt_info->control.control_flags = cpu_to_le32(control_flags); + + /* initialize RX default queue */ + rx_cfg = &ctxt_info->rbd_cfg; + rx_cfg->free_rbd_addr = cpu_to_le64(trans_pcie->rxq->bd_dma); + rx_cfg->used_rbd_addr = cpu_to_le64(trans_pcie->rxq->used_bd_dma); + rx_cfg->status_wr_ptr = cpu_to_le64(trans_pcie->rxq->rb_stts_dma); + + /* initialize TX command queue */ + ctxt_info->hcmd_cfg.cmd_queue_addr = + cpu_to_le64(trans_pcie->txq[trans_pcie->cmd_queue].dma_addr); + ctxt_info->hcmd_cfg.cmd_queue_size = + TFD_QUEUE_CB_SIZE(TFD_QUEUE_SIZE_MAX); + + /* allocate ucode sections in dram and set addresses */ + ret = iwl_pcie_ctxt_info_init_fw_sec(trans, fw, ctxt_info); + if (ret) + return ret; + + trans_pcie->ctxt_info = ctxt_info; + + iwl_enable_interrupts(trans); + + /* kick FW self load */ + iwl_write64(trans, CSR_CTXT_INFO_BA, trans_pcie->ctxt_info_dma_addr); + iwl_write_prph(trans, UREG_CPU_INIT_RUN, 1); + + /* Context info will be released upon alive or failure to get one */ + + return 0; +} + +void iwl_pcie_ctxt_info_free(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (!trans_pcie->ctxt_info) + return; + + dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info), + trans_pcie->ctxt_info, + trans_pcie->ctxt_info_dma_addr); + trans_pcie->ctxt_info_dma_addr = 0; + trans_pcie->ctxt_info = NULL; + + iwl_pcie_ctxt_info_free_fw_img(trans); +} diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 1ef9bb83a43f..98c1308eab0d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -2,7 +2,7 @@ * * Copyright(c) 2003 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -312,12 +312,44 @@ enum iwl_shared_irq_flags { IWL_SHARED_IRQ_FIRST_RSS = BIT(1), }; +/** + * struct iwl_dram_data + * @physical: page phy pointer + * @block: pointer to the allocated block/page + * @size: size of the block/page + */ +struct iwl_dram_data { + dma_addr_t physical; + void *block; + int size; +}; + +/** + * struct iwl_self_init_dram - dram data used by self init process + * @fw: lmac and umac dram data + * @fw_cnt: total number of items in array + * @paging: paging dram data + * @paging_cnt: total number of items in array + */ +struct iwl_self_init_dram { + struct iwl_dram_data *fw; + int fw_cnt; + struct iwl_dram_data *paging; + int paging_cnt; +}; + /** * struct iwl_trans_pcie - PCIe transport specific data * @rxq: all the RX queue data * @rx_pool: initial pool of iwl_rx_mem_buffer for all the queues * @global_table: table mapping received VID from hw to rxb * @rba: allocator for RX replenishing + * @ctxt_info: context information for FW self init + * @ctxt_info_dma_addr: dma addr of context information + * @init_dram: DRAM data of firmware image (including paging). + * Context information addresses will be taken from here. + * This is driver's local copy for keeping track of size and + * count for allocating and freeing the memory. * @trans: pointer to the generic transport area * @scd_base_addr: scheduler sram base address in SRAM * @scd_bc_tbls: pointer to the byte count table of the scheduler @@ -355,6 +387,9 @@ struct iwl_trans_pcie { struct iwl_rx_mem_buffer rx_pool[RX_POOL_SIZE]; struct iwl_rx_mem_buffer *global_table[RX_POOL_SIZE]; struct iwl_rb_allocator rba; + struct iwl_context_info *ctxt_info; + dma_addr_t ctxt_info_dma_addr; + struct iwl_self_init_dram init_dram; struct iwl_trans *trans; struct net_device napi_dev; @@ -452,6 +487,7 @@ void iwl_trans_pcie_free(struct iwl_trans *trans); * RX ******************************************************/ int iwl_pcie_rx_init(struct iwl_trans *trans); +int iwl_pcie_gen2_rx_init(struct iwl_trans *trans); irqreturn_t iwl_pcie_msix_isr(int irq, void *data); irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id); irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id); @@ -472,6 +508,7 @@ void iwl_pcie_disable_ict(struct iwl_trans *trans); * TX / HCMD ******************************************************/ int iwl_pcie_tx_init(struct iwl_trans *trans); +int iwl_pcie_gen2_tx_init(struct iwl_trans *trans); void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr); int iwl_pcie_tx_stop(struct iwl_trans *trans); void iwl_pcie_tx_free(struct iwl_trans *trans); @@ -716,4 +753,15 @@ int iwl_pci_fw_enter_d0i3(struct iwl_trans *trans); void iwl_pcie_enable_rx_wake(struct iwl_trans *trans, bool enable); +/* common functions that are used by gen2 transport */ +void iwl_pcie_apm_config(struct iwl_trans *trans); +int iwl_pcie_prepare_card_hw(struct iwl_trans *trans); +void iwl_pcie_synchronize_irqs(struct iwl_trans *trans); +bool iwl_trans_check_hw_rf_kill(struct iwl_trans *trans); + +/* transport gen 2 exported functions */ +int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, + const struct fw_img *fw, bool run_in_rfkill); +void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr); + #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 17806d82f3a3..c6178d36698c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -2,7 +2,7 @@ * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -880,7 +880,7 @@ static int iwl_pcie_dummy_napi_poll(struct napi_struct *napi, int budget) return 0; } -int iwl_pcie_rx_init(struct iwl_trans *trans) +static int _iwl_pcie_rx_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rxq *def_rxq; @@ -958,20 +958,40 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL, def_rxq); + return 0; +} + +int iwl_pcie_rx_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret = _iwl_pcie_rx_init(trans); + + if (ret) + return ret; + if (trans->cfg->mq_rx_supported) iwl_pcie_rx_mq_hw_init(trans); else - iwl_pcie_rx_hw_init(trans, def_rxq); + iwl_pcie_rx_hw_init(trans, trans_pcie->rxq); - iwl_pcie_rxq_restock(trans, def_rxq); + iwl_pcie_rxq_restock(trans, trans_pcie->rxq); - spin_lock(&def_rxq->lock); - iwl_pcie_rxq_inc_wr_ptr(trans, def_rxq); - spin_unlock(&def_rxq->lock); + spin_lock(&trans_pcie->rxq->lock); + iwl_pcie_rxq_inc_wr_ptr(trans, trans_pcie->rxq); + spin_unlock(&trans_pcie->rxq->lock); return 0; } +int iwl_pcie_gen2_rx_init(struct iwl_trans *trans) +{ + /* + * We don't configure the RFH. + * Restock will be done at alive, after firmware configured the RFH. + */ + return _iwl_pcie_rx_init(trans); +} + void iwl_pcie_rx_free(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1594,6 +1614,13 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) if (inta & CSR_INT_BIT_ALIVE) { IWL_DEBUG_ISR(trans, "Alive interrupt\n"); isr_stats->alive++; + if (trans->cfg->gen2) { + /* + * We can restock, since firmware configured + * the RFH + */ + iwl_pcie_rxmq_restock(trans, trans_pcie->rxq); + } } } @@ -1930,6 +1957,10 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) if (inta_hw & MSIX_HW_INT_CAUSES_REG_ALIVE) { IWL_DEBUG_ISR(trans, "Alive interrupt\n"); isr_stats->alive++; + if (trans->cfg->gen2) { + /* We can restock, since firmware configured the RFH */ + iwl_pcie_rxmq_restock(trans, trans_pcie->rxq); + } } /* uCode wakes up after power-down sleep */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c new file mode 100644 index 000000000000..302310dfef9e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -0,0 +1,226 @@ +/****************************************************************************** + * + * 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) 2017 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. + * + * BSD LICENSE + * + * Copyright(c) 2017 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. + * + *****************************************************************************/ +#include "iwl-trans.h" +#include "iwl-context-info.h" +#include "internal.h" + +/* + * Start up NIC's basic functionality after it has been reset + * (e.g. after platform boot, or shutdown via iwl_pcie_apm_stop()) + * NOTE: This does not load uCode nor start the embedded processor + */ +static int iwl_pcie_gen2_apm_init(struct iwl_trans *trans) +{ + int ret = 0; + + IWL_DEBUG_INFO(trans, "Init card's basic functions\n"); + + /* + * Use "set_bit" below rather than "write", to preserve any hardware + * bits already set by default after reset. + */ + + /* + * Disable L0s without affecting L1; + * don't wait for ICH L0s (ICH bug W/A) + */ + iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, + CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); + + /* Set FH wait threshold to maximum (HW error during stress W/A) */ + iwl_set_bit(trans, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); + + /* + * Enable HAP INTA (interrupt from management bus) to + * wake device's PCI Express link L1a -> L0s + */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); + + iwl_pcie_apm_config(trans); + + /* + * Set "initialization complete" bit to move adapter from + * D0U* --> D0A* (powered-up active) state. + */ + iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* + * Wait for clock stabilization; once stabilized, access to + * device-internal resources is supported, e.g. iwl_write_prph() + * and accesses to uCode SRAM. + */ + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); + if (ret < 0) { + IWL_DEBUG_INFO(trans, "Failed to init the card\n"); + return ret; + } + + set_bit(STATUS_DEVICE_ENABLED, &trans->status); + + return 0; +} + +static int iwl_pcie_gen2_nic_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + /* TODO: most of the logic can be removed in A0 - but not in Z0 */ + spin_lock(&trans_pcie->irq_lock); + iwl_pcie_gen2_apm_init(trans); + spin_unlock(&trans_pcie->irq_lock); + + iwl_op_mode_nic_config(trans->op_mode); + + /* Allocate the RX queue, or reset if it is already allocated */ + if (iwl_pcie_gen2_rx_init(trans)) + return -ENOMEM; + + /* Allocate or reset and init all Tx and Command queues */ + if (iwl_pcie_gen2_tx_init(trans)) + return -ENOMEM; + + /* enable shadow regs in HW */ + iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF); + IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n"); + + return 0; +} + +void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + iwl_pcie_reset_ict(trans); + + /* make sure all queue are not stopped/used */ + memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); + memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); + + /* now that we got alive we can free the fw image & the context info. + * paging memory cannot be freed included since FW will still use it + */ + iwl_pcie_ctxt_info_free(trans); +} + +int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, + const struct fw_img *fw, bool run_in_rfkill) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + bool hw_rfkill; + int ret; + + /* This may fail if AMT took ownership of the device */ + if (iwl_pcie_prepare_card_hw(trans)) { + IWL_WARN(trans, "Exit HW not ready\n"); + ret = -EIO; + goto out; + } + + iwl_enable_rfkill_int(trans); + + iwl_write32(trans, CSR_INT, 0xFFFFFFFF); + + /* + * We enabled the RF-Kill interrupt and the handler may very + * well be running. Disable the interrupts to make sure no other + * interrupt can be fired. + */ + iwl_disable_interrupts(trans); + + /* Make sure it finished running */ + iwl_pcie_synchronize_irqs(trans); + + mutex_lock(&trans_pcie->mutex); + + /* If platform's RF_KILL switch is NOT set to KILL */ + hw_rfkill = iwl_trans_check_hw_rf_kill(trans); + if (hw_rfkill && !run_in_rfkill) { + ret = -ERFKILL; + goto out; + } + + /* Someone called stop_device, don't try to start_fw */ + if (trans_pcie->is_down) { + IWL_WARN(trans, + "Can't start_fw since the HW hasn't been started\n"); + ret = -EIO; + goto out; + } + + /* make sure rfkill handshake bits are cleared */ + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + /* clear (again), then enable host interrupts */ + iwl_write32(trans, CSR_INT, 0xFFFFFFFF); + + ret = iwl_pcie_gen2_nic_init(trans); + if (ret) { + IWL_ERR(trans, "Unable to init nic\n"); + goto out; + } + + if (iwl_pcie_ctxt_info_init(trans, fw)) + return -ENOMEM; + + /* re-check RF-Kill state since we may have missed the interrupt */ + hw_rfkill = iwl_trans_check_hw_rf_kill(trans); + if (hw_rfkill && !run_in_rfkill) + ret = -ERFKILL; + +out: + mutex_unlock(&trans_pcie->mutex); + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 16c8b37bf6d7..361f0b0a6f42 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -80,6 +80,7 @@ #include "iwl-prph.h" #include "iwl-scd.h" #include "iwl-agn-hw.h" +#include "iwl-context-info.h" #include "iwl-fw-error-dump.h" #include "internal.h" #include "iwl-fh.h" @@ -201,7 +202,7 @@ static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux) /* PCI registers */ #define PCI_CFG_RETRY_TIMEOUT 0x041 -static void iwl_pcie_apm_config(struct iwl_trans *trans) +void iwl_pcie_apm_config(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u16 lctl; @@ -567,7 +568,7 @@ static int iwl_pcie_set_hw_ready(struct iwl_trans *trans) } /* Note: returns standard 0/-ERROR code */ -static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) +int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) { int ret; int t = 0; @@ -636,29 +637,6 @@ static void iwl_pcie_load_firmware_chunk_fh(struct iwl_trans *trans, FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); } -static void iwl_pcie_load_firmware_chunk_tfh(struct iwl_trans *trans, - u32 dst_addr, dma_addr_t phy_addr, - u32 byte_cnt) -{ - /* Stop DMA channel */ - iwl_write32(trans, TFH_SRV_DMA_CHNL0_CTRL, 0); - - /* Configure SRAM address */ - iwl_write32(trans, TFH_SRV_DMA_CHNL0_SRAM_ADDR, - dst_addr); - - /* Configure DRAM address - 64 bit */ - iwl_write64(trans, TFH_SRV_DMA_CHNL0_DRAM_ADDR, phy_addr); - - /* Configure byte count to transfer */ - iwl_write32(trans, TFH_SRV_DMA_CHNL0_BC, byte_cnt); - - /* Enable the DRAM2SRAM to start */ - iwl_write32(trans, TFH_SRV_DMA_CHNL0_CTRL, TFH_SRV_DMA_SNOOP | - TFH_SRV_DMA_TO_DRIVER | - TFH_SRV_DMA_START); -} - static int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr, dma_addr_t phy_addr, u32 byte_cnt) @@ -672,12 +650,8 @@ static int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans, if (!iwl_trans_grab_nic_access(trans, &flags)) return -EIO; - if (trans->cfg->use_tfh) - iwl_pcie_load_firmware_chunk_tfh(trans, dst_addr, phy_addr, - byte_cnt); - else - iwl_pcie_load_firmware_chunk_fh(trans, dst_addr, phy_addr, - byte_cnt); + iwl_pcie_load_firmware_chunk_fh(trans, dst_addr, phy_addr, + byte_cnt); iwl_trans_release_nic_access(trans, &flags); ret = wait_event_timeout(trans_pcie->ucode_write_waitq, @@ -828,15 +802,10 @@ static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans, return ret; /* Notify ucode of loaded section number and status */ - if (trans->cfg->use_tfh) { - val = iwl_read_prph(trans, UREG_UCODE_LOAD_STATUS); - val = val | (sec_num << shift_param); - iwl_write_prph(trans, UREG_UCODE_LOAD_STATUS, val); - } else { - val = iwl_read_direct32(trans, FH_UCODE_LOAD_STATUS); - val = val | (sec_num << shift_param); - iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, val); - } + val = iwl_read_direct32(trans, FH_UCODE_LOAD_STATUS); + val = val | (sec_num << shift_param); + iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, val); + sec_num = (sec_num << 1) | 0x1; } @@ -1072,7 +1041,7 @@ static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans, &first_ucode_section); } -static bool iwl_trans_check_hw_rf_kill(struct iwl_trans *trans) +bool iwl_trans_check_hw_rf_kill(struct iwl_trans *trans) { bool hw_rfkill = iwl_is_rfkill_set(trans); @@ -1244,6 +1213,9 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) } } + iwl_pcie_ctxt_info_free_paging(trans); + iwl_pcie_ctxt_info_free(trans); + /* Make sure (redundant) we've released our request to stay awake */ iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); @@ -1309,7 +1281,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) iwl_pcie_prepare_card_hw(trans); } -static void iwl_pcie_synchronize_irqs(struct iwl_trans *trans) +void iwl_pcie_synchronize_irqs(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -2942,8 +2914,8 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = { IWL_TRANS_COMMON_OPS, IWL_TRANS_PM_OPS .start_hw = iwl_trans_pcie_start_hw, - .fw_alive = iwl_trans_pcie_fw_alive, - .start_fw = iwl_trans_pcie_start_fw, + .fw_alive = iwl_trans_pcie_gen2_fw_alive, + .start_fw = iwl_trans_pcie_gen2_start_fw, .stop_device = iwl_trans_pcie_stop_device, .send_cmd = iwl_trans_pcie_send_hcmd, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 9c8ab1a82b72..b3ac1fb51009 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -2,7 +2,7 @@ * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -612,18 +612,6 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, __skb_queue_head_init(&txq->overflow_q); - /* - * Tell nic where to find circular buffer of Tx Frame Descriptors for - * given Tx queue, and enable the DMA channel used for that queue. - * Circular buffer (TFD queue in DRAM) physical base address */ - if (trans->cfg->use_tfh) - iwl_write_direct64(trans, - FH_MEM_CBBC_QUEUE(trans, txq_id), - txq->dma_addr); - else - iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(trans, txq_id), - txq->dma_addr >> 8); - return 0; } @@ -775,9 +763,6 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); - if (trans->cfg->use_tfh) - return; - trans_pcie->scd_base_addr = iwl_read_prph(trans, SCD_SRAM_BASE_ADDR); @@ -1009,6 +994,7 @@ error: return ret; } + int iwl_pcie_tx_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1045,14 +1031,15 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); goto error; } - } - if (trans->cfg->use_tfh) { - iwl_write_direct32(trans, TFH_TRANSFER_MODE, - TFH_TRANSFER_MAX_PENDING_REQ | - TFH_CHUNK_SIZE_128 | - TFH_CHUNK_SPLIT_MODE); - return 0; + /* + * Tell nic where to find circular buffer of TFDs for a + * given Tx queue, and enable the DMA channel used for that + * queue. + * Circular buffer (TFD queue in DRAM) physical base address + */ + iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(trans, txq_id), + trans_pcie->txq[txq_id].dma_addr >> 8); } iwl_set_bits_prph(trans, SCD_GP_CTRL, SCD_GP_CTRL_AUTO_ACTIVE_MODE); @@ -1068,6 +1055,51 @@ error: return ret; } +int iwl_pcie_gen2_tx_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret; + int txq_id, slots_num; + bool alloc = false; + + if (!trans_pcie->txq) { + /* TODO: change this when moving to new TX alloc model */ + ret = iwl_pcie_tx_alloc(trans); + if (ret) + goto error; + alloc = true; + } + + spin_lock(&trans_pcie->irq_lock); + + /* Tell NIC where to find the "keep warm" buffer */ + iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, + trans_pcie->kw.dma >> 4); + + spin_unlock(&trans_pcie->irq_lock); + + /* TODO: remove this when moving to new TX alloc model */ + for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; + txq_id++) { + slots_num = (txq_id == trans_pcie->cmd_queue) ? + TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + ret = iwl_pcie_txq_init(trans, &trans_pcie->txq[txq_id], + slots_num, txq_id); + if (ret) { + IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); + goto error; + } + } + + return 0; + +error: + /* Upon error, free only if we allocated something */ + if (alloc) + iwl_pcie_tx_free(trans); + return ret; +} + static inline void iwl_pcie_txq_progress(struct iwl_txq *txq) { lockdep_assert_held(&txq->lock); -- cgit From 30d23c3cd035e92c8cc0f9f648a5acce105a2d5b Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 30 Oct 2016 09:49:09 +0200 Subject: iwlwifi: mvm: remove call for paging in new init flow Now that transport inits the paging in the context info - remove the call in mvm. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 8 -------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 ++- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 286f18609729..e481bb050c6e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -850,14 +850,6 @@ int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) goto error; } - /* TODO: remove when integrating context info */ - ret = iwl_mvm_init_paging(mvm); - if (ret) { - IWL_ERR(mvm, "Failed to init paging: %d\n", - ret); - goto error; - } - /* Read the NVM only at driver load time, no need to do this twice */ if (read_nvm) { /* Read nvm */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index b3f67fe98203..91bd5ebd5b65 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1709,7 +1709,8 @@ void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, static inline void iwl_mvm_stop_device(struct iwl_mvm *mvm) { - iwl_free_fw_paging(mvm); + if (!iwl_mvm_has_new_tx_api(mvm)) + iwl_free_fw_paging(mvm); mvm->ucode_loaded = false; iwl_trans_stop_device(mvm->trans); } -- cgit From 87d0e1af9db3bff4ec2f68fd4d032f89c23867a4 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 11 Jan 2017 21:33:38 +0200 Subject: iwlwifi: mvm: separate queue mapping from queue enablement As preparation for a000 different queue management, separate mapping of queues from actual enablement. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 34 ++++++++++++++++---------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index dedea96a8e0f..8f199ff9be87 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -7,7 +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 + * Copyright (C) 2015 - 2017 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 @@ -34,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright (C) 2015 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -644,20 +645,19 @@ int iwl_mvm_reconfig_scd(struct iwl_mvm *mvm, int queue, int fifo, int sta_id, return ret; } -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) +static bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm, int queue, + int mac80211_queue, u8 sta_id, u8 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)) { + if (mvm->queue_info[queue].tid_bitmap & BIT(tid)) { spin_unlock_bh(&mvm->queue_info_lock); IWL_ERR(mvm, "Trying to enable TXQ %d with existing TID %d\n", - queue, cfg->tid); - return; + queue, tid); + return false; } /* Update mappings and refcounts */ @@ -666,17 +666,17 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, mvm->queue_info[queue].hw_queue_to_mac80211 |= BIT(mac80211_queue); mvm->queue_info[queue].hw_queue_refcount++; - mvm->queue_info[queue].tid_bitmap |= BIT(cfg->tid); - mvm->queue_info[queue].ra_sta_id = cfg->sta_id; + mvm->queue_info[queue].tid_bitmap |= BIT(tid); + mvm->queue_info[queue].ra_sta_id = sta_id; if (enable_queue) { - if (cfg->tid != IWL_MAX_TID_COUNT) + if (tid != IWL_MAX_TID_COUNT) mvm->queue_info[queue].mac80211_ac = - tid_to_mac80211_ac[cfg->tid]; + tid_to_mac80211_ac[tid]; else mvm->queue_info[queue].mac80211_ac = IEEE80211_AC_VO; - mvm->queue_info[queue].txq_tid = cfg->tid; + mvm->queue_info[queue].txq_tid = tid; } IWL_DEBUG_TX_QUEUES(mvm, @@ -686,8 +686,16 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, spin_unlock_bh(&mvm->queue_info_lock); + return enable_queue; +} + +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) +{ /* Send the enabling command if we need to */ - if (enable_queue) { + if (iwl_mvm_update_txq_mapping(mvm, queue, mac80211_queue, + cfg->sta_id, cfg->tid)) { struct iwl_scd_txq_cfg_cmd cmd = { .scd_queue = queue, .action = SCD_CFG_ENABLE_QUEUE, -- cgit From f3297f686db084465347c83edfcd1527994e7a9f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 11 Apr 2017 13:01:21 +0200 Subject: net: alx: switch to pci_alloc_irq_vectors Remove the deprecated pci_enable_msix API in favour of its successor, and make sure to handle errors during IRQ setup properly. Signed-off-by: Christoph Hellwig Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/alx.h | 6 -- drivers/net/ethernet/atheros/alx/main.c | 128 ++++++++++++++------------------ 2 files changed, 54 insertions(+), 80 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/alx.h b/drivers/net/ethernet/atheros/alx/alx.h index d4a409139ea2..78c5de467426 100644 --- a/drivers/net/ethernet/atheros/alx/alx.h +++ b/drivers/net/ethernet/atheros/alx/alx.h @@ -102,9 +102,6 @@ struct alx_napi { #define ALX_MAX_NAPIS 8 -#define ALX_FLAG_USING_MSIX BIT(0) -#define ALX_FLAG_USING_MSI BIT(1) - struct alx_priv { struct net_device *dev; @@ -112,7 +109,6 @@ struct alx_priv { /* msi-x vectors */ int num_vec; - struct msix_entry *msix_entries; /* all descriptor memory */ struct { @@ -139,8 +135,6 @@ struct alx_priv { u16 msg_enable; - int flags; - /* protects hw.stats */ spinlock_t stats_lock; }; diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 6a27c2662675..a8c2db881b75 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -314,7 +314,7 @@ static int alx_poll(struct napi_struct *napi, int budget) napi_complete_done(&np->napi, work); /* enable interrupt */ - if (alx->flags & ALX_FLAG_USING_MSIX) { + if (alx->hw.pdev->msix_enabled) { alx_mask_msix(hw, np->vec_idx, false); } else { spin_lock_irqsave(&alx->irq_lock, flags); @@ -811,7 +811,7 @@ static void alx_config_vector_mapping(struct alx_priv *alx) u32 tbl[2] = {0, 0}; int i, vector, idx, shift; - if (alx->flags & ALX_FLAG_USING_MSIX) { + if (alx->hw.pdev->msix_enabled) { /* tx mappings */ for (i = 0, vector = 1; i < alx->num_txq; i++, vector++) { idx = txq_vec_mapping_shift[i * 2]; @@ -828,29 +828,19 @@ static void alx_config_vector_mapping(struct alx_priv *alx) alx_write_mem32(hw, ALX_MSI_ID_MAP, 0); } -static bool alx_enable_msix(struct alx_priv *alx) +static int alx_enable_msix(struct alx_priv *alx) { - int i, err, num_vec, num_txq, num_rxq; + int err, num_vec, num_txq, num_rxq; num_txq = min_t(int, num_online_cpus(), ALX_MAX_TX_QUEUES); num_rxq = 1; num_vec = max_t(int, num_txq, num_rxq) + 1; - alx->msix_entries = kcalloc(num_vec, sizeof(struct msix_entry), - GFP_KERNEL); - if (!alx->msix_entries) { - netdev_warn(alx->dev, "Allocation of msix entries failed!\n"); - return false; - } - - for (i = 0; i < num_vec; i++) - alx->msix_entries[i].entry = i; - - err = pci_enable_msix(alx->hw.pdev, alx->msix_entries, num_vec); + err = pci_alloc_irq_vectors(alx->hw.pdev, num_vec, num_vec, + PCI_IRQ_MSIX); if (err) { - kfree(alx->msix_entries); netdev_warn(alx->dev, "Enabling MSI-X interrupts failed!\n"); - return false; + return err; } alx->num_vec = num_vec; @@ -858,7 +848,7 @@ static bool alx_enable_msix(struct alx_priv *alx) alx->num_txq = num_txq; alx->num_rxq = num_rxq; - return true; + return err; } static int alx_request_msix(struct alx_priv *alx) @@ -866,7 +856,7 @@ static int alx_request_msix(struct alx_priv *alx) struct net_device *netdev = alx->dev; int i, err, vector = 0, free_vector = 0; - err = request_irq(alx->msix_entries[0].vector, alx_intr_msix_misc, + err = request_irq(pci_irq_vector(alx->hw.pdev, 0), alx_intr_msix_misc, 0, netdev->name, alx); if (err) goto out_err; @@ -889,7 +879,7 @@ static int alx_request_msix(struct alx_priv *alx) sprintf(np->irq_lbl, "%s-unused", netdev->name); np->vec_idx = vector; - err = request_irq(alx->msix_entries[vector].vector, + err = request_irq(pci_irq_vector(alx->hw.pdev, vector), alx_intr_msix_ring, 0, np->irq_lbl, np); if (err) goto out_free; @@ -897,47 +887,31 @@ static int alx_request_msix(struct alx_priv *alx) return 0; out_free: - free_irq(alx->msix_entries[free_vector++].vector, alx); + free_irq(pci_irq_vector(alx->hw.pdev, free_vector++), alx); vector--; for (i = 0; i < vector; i++) - free_irq(alx->msix_entries[free_vector++].vector, + free_irq(pci_irq_vector(alx->hw.pdev,free_vector++), alx->qnapi[i]); out_err: return err; } -static void alx_init_intr(struct alx_priv *alx, bool msix) +static int alx_init_intr(struct alx_priv *alx) { - if (msix) { - if (alx_enable_msix(alx)) - alx->flags |= ALX_FLAG_USING_MSIX; - } + int ret; - if (!(alx->flags & ALX_FLAG_USING_MSIX)) { - alx->num_vec = 1; - alx->num_napi = 1; - alx->num_txq = 1; - alx->num_rxq = 1; - - if (!pci_enable_msi(alx->hw.pdev)) - alx->flags |= ALX_FLAG_USING_MSI; - } -} - -static void alx_disable_advanced_intr(struct alx_priv *alx) -{ - if (alx->flags & ALX_FLAG_USING_MSIX) { - kfree(alx->msix_entries); - pci_disable_msix(alx->hw.pdev); - alx->flags &= ~ALX_FLAG_USING_MSIX; - } + ret = pci_alloc_irq_vectors(alx->hw.pdev, 1, 1, + PCI_IRQ_MSI | PCI_IRQ_LEGACY); + if (ret) + return ret; - if (alx->flags & ALX_FLAG_USING_MSI) { - pci_disable_msi(alx->hw.pdev); - alx->flags &= ~ALX_FLAG_USING_MSI; - } + alx->num_vec = 1; + alx->num_napi = 1; + alx->num_txq = 1; + alx->num_rxq = 1; + return 0; } static void alx_irq_enable(struct alx_priv *alx) @@ -950,10 +924,11 @@ static void alx_irq_enable(struct alx_priv *alx) alx_write_mem32(hw, ALX_IMR, alx->int_mask); alx_post_write(hw); - if (alx->flags & ALX_FLAG_USING_MSIX) + if (alx->hw.pdev->msix_enabled) { /* enable all msix irqs */ for (i = 0; i < alx->num_vec; i++) alx_mask_msix(hw, i, false); + } } static void alx_irq_disable(struct alx_priv *alx) @@ -965,13 +940,13 @@ static void alx_irq_disable(struct alx_priv *alx) alx_write_mem32(hw, ALX_IMR, 0); alx_post_write(hw); - if (alx->flags & ALX_FLAG_USING_MSIX) { + if (alx->hw.pdev->msix_enabled) { for (i = 0; i < alx->num_vec; i++) { alx_mask_msix(hw, i, true); - synchronize_irq(alx->msix_entries[i].vector); + synchronize_irq(pci_irq_vector(alx->hw.pdev, i)); } } else { - synchronize_irq(alx->hw.pdev->irq); + synchronize_irq(pci_irq_vector(alx->hw.pdev, 0)); } } @@ -981,8 +956,11 @@ static int alx_realloc_resources(struct alx_priv *alx) alx_free_rings(alx); alx_free_napis(alx); - alx_disable_advanced_intr(alx); - alx_init_intr(alx, false); + pci_free_irq_vectors(alx->hw.pdev); + + err = alx_init_intr(alx); + if (err) + return err; err = alx_alloc_napis(alx); if (err) @@ -1004,7 +982,7 @@ static int alx_request_irq(struct alx_priv *alx) msi_ctrl = (hw->imt >> 1) << ALX_MSI_RETRANS_TM_SHIFT; - if (alx->flags & ALX_FLAG_USING_MSIX) { + if (alx->hw.pdev->msix_enabled) { alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER, msi_ctrl); err = alx_request_msix(alx); if (!err) @@ -1016,20 +994,20 @@ static int alx_request_irq(struct alx_priv *alx) goto out; } - if (alx->flags & ALX_FLAG_USING_MSI) { + if (alx->hw.pdev->msi_enabled) { alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER, msi_ctrl | ALX_MSI_MASK_SEL_LINE); - err = request_irq(pdev->irq, alx_intr_msi, 0, + err = request_irq(pci_irq_vector(pdev, 0), alx_intr_msi, 0, alx->dev->name, alx); if (!err) goto out; + /* fall back to legacy interrupt */ - alx->flags &= ~ALX_FLAG_USING_MSI; - pci_disable_msi(alx->hw.pdev); + pci_free_irq_vectors(alx->hw.pdev); } alx_write_mem32(hw, ALX_MSI_RETRANS_TIMER, 0); - err = request_irq(pdev->irq, alx_intr_legacy, IRQF_SHARED, + err = request_irq(pci_irq_vector(pdev, 0), alx_intr_legacy, IRQF_SHARED, alx->dev->name, alx); out: if (!err) @@ -1042,18 +1020,15 @@ out: static void alx_free_irq(struct alx_priv *alx) { struct pci_dev *pdev = alx->hw.pdev; - int i, vector = 0; + int i; - if (alx->flags & ALX_FLAG_USING_MSIX) { - free_irq(alx->msix_entries[vector++].vector, alx); + free_irq(pci_irq_vector(pdev, 0), alx); + if (alx->hw.pdev->msix_enabled) { for (i = 0; i < alx->num_napi; i++) - free_irq(alx->msix_entries[vector++].vector, - alx->qnapi[i]); - } else { - free_irq(pdev->irq, alx); + free_irq(pci_irq_vector(pdev, i + 1), alx->qnapi[i]); } - alx_disable_advanced_intr(alx); + pci_free_irq_vectors(pdev); } static int alx_identify_hw(struct alx_priv *alx) @@ -1221,7 +1196,12 @@ static int __alx_open(struct alx_priv *alx, bool resume) { int err; - alx_init_intr(alx, true); + err = alx_enable_msix(alx); + if (err < 0) { + err = alx_init_intr(alx); + if (err) + return err; + } if (!resume) netif_carrier_off(alx->dev); @@ -1264,7 +1244,7 @@ out_free_rings: alx_free_rings(alx); alx_free_napis(alx); out_disable_adv_intr: - alx_disable_advanced_intr(alx); + pci_free_irq_vectors(alx->hw.pdev); return err; } @@ -1637,11 +1617,11 @@ static void alx_poll_controller(struct net_device *netdev) struct alx_priv *alx = netdev_priv(netdev); int i; - if (alx->flags & ALX_FLAG_USING_MSIX) { + if (alx->hw.pdev->msix_enabled) { alx_intr_msix_misc(0, alx); for (i = 0; i < alx->num_txq; i++) alx_intr_msix_ring(0, alx->qnapi[i]); - } else if (alx->flags & ALX_FLAG_USING_MSI) + } else if (alx->hw.pdev->msi_enabled) alx_intr_msi(0, alx); else alx_intr_legacy(0, alx); @@ -1783,7 +1763,7 @@ static int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->netdev_ops = &alx_netdev_ops; netdev->ethtool_ops = &alx_ethtool_ops; - netdev->irq = pdev->irq; + netdev->irq = pci_irq_vector(pdev, 0); netdev->watchdog_timeo = ALX_WATCHDOG_TIME; if (ent->driver_data & ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG) -- cgit From da6f4cf58e403628de026f06b608005beb8995ba Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 11 Apr 2017 13:01:22 +0200 Subject: net/ena: switch to pci_alloc_irq_vectors Remove the deprecated pci_enable_msix API in favour of its successor. Signed-off-by: Christoph Hellwig Signed-off-by: David S. Miller --- drivers/net/ethernet/amazon/ena/ena_netdev.c | 55 ++++++---------------------- drivers/net/ethernet/amazon/ena/ena_netdev.h | 2 - 2 files changed, 12 insertions(+), 45 deletions(-) diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index 35f19430c84a..7c1214d78855 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -133,7 +133,7 @@ static int ena_init_rx_cpu_rmap(struct ena_adapter *adapter) int irq_idx = ENA_IO_IRQ_IDX(i); rc = irq_cpu_rmap_add(adapter->netdev->rx_cpu_rmap, - adapter->msix_entries[irq_idx].vector); + pci_irq_vector(adapter->pdev, irq_idx)); if (rc) { free_irq_cpu_rmap(adapter->netdev->rx_cpu_rmap); adapter->netdev->rx_cpu_rmap = NULL; @@ -1208,13 +1208,7 @@ static irqreturn_t ena_intr_msix_io(int irq, void *data) static int ena_enable_msix(struct ena_adapter *adapter, int num_queues) { - int i, msix_vecs, rc; - - if (test_bit(ENA_FLAG_MSIX_ENABLED, &adapter->flags)) { - netif_err(adapter, probe, adapter->netdev, - "Error, MSI-X is already enabled\n"); - return -EPERM; - } + int msix_vecs, rc; /* Reserved the max msix vectors we might need */ msix_vecs = ENA_MAX_MSIX_VEC(num_queues); @@ -1222,16 +1216,9 @@ static int ena_enable_msix(struct ena_adapter *adapter, int num_queues) netif_dbg(adapter, probe, adapter->netdev, "trying to enable MSI-X, vectors %d\n", msix_vecs); - adapter->msix_entries = vzalloc(msix_vecs * sizeof(struct msix_entry)); - - if (!adapter->msix_entries) - return -ENOMEM; - - for (i = 0; i < msix_vecs; i++) - adapter->msix_entries[i].entry = i; - - rc = pci_enable_msix(adapter->pdev, adapter->msix_entries, msix_vecs); - if (rc != 0) { + rc = pci_alloc_irq_vectors(adapter->pdev, msix_vecs, msix_vecs, + PCI_IRQ_MSIX); + if (rc < 0) { netif_err(adapter, probe, adapter->netdev, "Failed to enable MSI-X, vectors %d rc %d\n", msix_vecs, rc); @@ -1248,7 +1235,6 @@ static int ena_enable_msix(struct ena_adapter *adapter, int num_queues) } adapter->msix_vecs = msix_vecs; - set_bit(ENA_FLAG_MSIX_ENABLED, &adapter->flags); return 0; } @@ -1264,7 +1250,7 @@ static void ena_setup_mgmnt_intr(struct ena_adapter *adapter) ena_intr_msix_mgmnt; adapter->irq_tbl[ENA_MGMNT_IRQ_IDX].data = adapter; adapter->irq_tbl[ENA_MGMNT_IRQ_IDX].vector = - adapter->msix_entries[ENA_MGMNT_IRQ_IDX].vector; + pci_irq_vector(adapter->pdev, ENA_MGMNT_IRQ_IDX); cpu = cpumask_first(cpu_online_mask); adapter->irq_tbl[ENA_MGMNT_IRQ_IDX].cpu = cpu; cpumask_set_cpu(cpu, @@ -1287,7 +1273,7 @@ static void ena_setup_io_intr(struct ena_adapter *adapter) adapter->irq_tbl[irq_idx].handler = ena_intr_msix_io; adapter->irq_tbl[irq_idx].data = &adapter->ena_napi[i]; adapter->irq_tbl[irq_idx].vector = - adapter->msix_entries[irq_idx].vector; + pci_irq_vector(adapter->pdev, irq_idx); adapter->irq_tbl[irq_idx].cpu = cpu; cpumask_set_cpu(cpu, @@ -1325,12 +1311,6 @@ static int ena_request_io_irq(struct ena_adapter *adapter) struct ena_irq *irq; int rc = 0, i, k; - if (!test_bit(ENA_FLAG_MSIX_ENABLED, &adapter->flags)) { - netif_err(adapter, ifup, adapter->netdev, - "Failed to request I/O IRQ: MSI-X is not enabled\n"); - return -EINVAL; - } - for (i = ENA_IO_IRQ_FIRST_IDX; i < adapter->msix_vecs; i++) { irq = &adapter->irq_tbl[i]; rc = request_irq(irq->vector, irq->handler, flags, irq->name, @@ -1389,16 +1369,6 @@ static void ena_free_io_irq(struct ena_adapter *adapter) } } -static void ena_disable_msix(struct ena_adapter *adapter) -{ - if (test_and_clear_bit(ENA_FLAG_MSIX_ENABLED, &adapter->flags)) - pci_disable_msix(adapter->pdev); - - if (adapter->msix_entries) - vfree(adapter->msix_entries); - adapter->msix_entries = NULL; -} - static void ena_disable_io_intr_sync(struct ena_adapter *adapter) { int i; @@ -2479,8 +2449,7 @@ static int ena_enable_msix_and_set_admin_interrupts(struct ena_adapter *adapter, return 0; err_disable_msix: - ena_disable_msix(adapter); - + pci_free_irq_vectors(adapter->pdev); return rc; } @@ -2518,7 +2487,7 @@ static void ena_fw_reset_device(struct work_struct *work) ena_free_mgmnt_irq(adapter); - ena_disable_msix(adapter); + pci_free_irq_vectors(adapter->pdev); ena_com_abort_admin_commands(ena_dev); @@ -2569,7 +2538,7 @@ static void ena_fw_reset_device(struct work_struct *work) return; err_disable_msix: ena_free_mgmnt_irq(adapter); - ena_disable_msix(adapter); + pci_free_irq_vectors(adapter->pdev); err_device_destroy: ena_com_admin_destroy(ena_dev); err: @@ -3103,7 +3072,7 @@ err_rss: err_free_msix: ena_com_dev_reset(ena_dev); ena_free_mgmnt_irq(adapter); - ena_disable_msix(adapter); + pci_free_irq_vectors(adapter->pdev); err_worker_destroy: ena_com_destroy_interrupt_moderation(ena_dev); del_timer(&adapter->timer_service); @@ -3188,7 +3157,7 @@ static void ena_remove(struct pci_dev *pdev) ena_free_mgmnt_irq(adapter); - ena_disable_msix(adapter); + pci_free_irq_vectors(adapter->pdev); free_netdev(netdev); diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.h b/drivers/net/ethernet/amazon/ena/ena_netdev.h index ed62d8e231a1..0e22bce6239d 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.h +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.h @@ -248,7 +248,6 @@ enum ena_flags_t { ENA_FLAG_DEVICE_RUNNING, ENA_FLAG_DEV_UP, ENA_FLAG_LINK_UP, - ENA_FLAG_MSIX_ENABLED, ENA_FLAG_TRIGGER_RESET }; @@ -267,7 +266,6 @@ struct ena_adapter { int num_queues; - struct msix_entry *msix_entries; int msix_vecs; u32 tx_usecs, rx_usecs; /* interrupt moderation */ -- cgit From ce211b172b1e3f3de30947d6604c57f3d6681406 Mon Sep 17 00:00:00 2001 From: Thanneeru Srinivasulu Date: Tue, 11 Apr 2017 13:01:23 +0200 Subject: net: thunderx: Switch to pci_alloc_irq_vectors Remove deprecated pci_enable_msix API in favour of its successor pci_alloc_irq_vectors. Signed-off-by: Thanneeru Srinivasulu Signed-off-by: Sunil Goutham Reviewed-by: Christoph Hellwig Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nic.h | 2 - drivers/net/ethernet/cavium/thunder/nic_main.c | 64 ++++++---------------- drivers/net/ethernet/cavium/thunder/nicvf_main.c | 70 ++++++++---------------- 3 files changed, 40 insertions(+), 96 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h index 2269ff562d95..6fb44218bf55 100644 --- a/drivers/net/ethernet/cavium/thunder/nic.h +++ b/drivers/net/ethernet/cavium/thunder/nic.h @@ -319,9 +319,7 @@ struct nicvf { struct bgx_stats bgx_stats; /* MSI-X */ - bool msix_enabled; u8 num_vec; - struct msix_entry msix_entries[NIC_VF_MSIX_VECTORS]; char irq_name[NIC_VF_MSIX_VECTORS][IFNAMSIZ + 15]; bool irq_allocated[NIC_VF_MSIX_VECTORS]; cpumask_var_t affinity_mask[NIC_VF_MSIX_VECTORS]; diff --git a/drivers/net/ethernet/cavium/thunder/nic_main.c b/drivers/net/ethernet/cavium/thunder/nic_main.c index 767234e2e8f9..fb770b0182d3 100644 --- a/drivers/net/ethernet/cavium/thunder/nic_main.c +++ b/drivers/net/ethernet/cavium/thunder/nic_main.c @@ -65,9 +65,7 @@ struct nicpf { bool mbx_lock[MAX_NUM_VFS_SUPPORTED]; /* MSI-X */ - bool msix_enabled; u8 num_vec; - struct msix_entry *msix_entries; bool irq_allocated[NIC_PF_MSIX_VECTORS]; char irq_name[NIC_PF_MSIX_VECTORS][20]; }; @@ -1088,7 +1086,7 @@ static irqreturn_t nic_mbx_intr_handler(int irq, void *nic_irq) u64 intr; u8 vf, vf_per_mbx_reg = 64; - if (irq == nic->msix_entries[NIC_PF_INTR_ID_MBOX0].vector) + if (irq == pci_irq_vector(nic->pdev, NIC_PF_INTR_ID_MBOX0)) mbx = 0; else mbx = 1; @@ -1107,51 +1105,13 @@ static irqreturn_t nic_mbx_intr_handler(int irq, void *nic_irq) return IRQ_HANDLED; } -static int nic_enable_msix(struct nicpf *nic) -{ - int i, ret; - - nic->num_vec = pci_msix_vec_count(nic->pdev); - - nic->msix_entries = kmalloc_array(nic->num_vec, - sizeof(struct msix_entry), - GFP_KERNEL); - if (!nic->msix_entries) - return -ENOMEM; - - for (i = 0; i < nic->num_vec; i++) - nic->msix_entries[i].entry = i; - - ret = pci_enable_msix(nic->pdev, nic->msix_entries, nic->num_vec); - if (ret) { - dev_err(&nic->pdev->dev, - "Request for #%d msix vectors failed, returned %d\n", - nic->num_vec, ret); - kfree(nic->msix_entries); - return ret; - } - - nic->msix_enabled = 1; - return 0; -} - -static void nic_disable_msix(struct nicpf *nic) -{ - if (nic->msix_enabled) { - pci_disable_msix(nic->pdev); - kfree(nic->msix_entries); - nic->msix_enabled = 0; - nic->num_vec = 0; - } -} - static void nic_free_all_interrupts(struct nicpf *nic) { int irq; for (irq = 0; irq < nic->num_vec; irq++) { if (nic->irq_allocated[irq]) - free_irq(nic->msix_entries[irq].vector, nic); + free_irq(pci_irq_vector(nic->pdev, irq), nic); nic->irq_allocated[irq] = false; } } @@ -1159,18 +1119,24 @@ static void nic_free_all_interrupts(struct nicpf *nic) static int nic_register_interrupts(struct nicpf *nic) { int i, ret; + nic->num_vec = pci_msix_vec_count(nic->pdev); /* Enable MSI-X */ - ret = nic_enable_msix(nic); - if (ret) - return ret; + ret = pci_alloc_irq_vectors(nic->pdev, nic->num_vec, nic->num_vec, + PCI_IRQ_MSIX); + if (ret < 0) { + dev_err(&nic->pdev->dev, + "Request for #%d msix vectors failed, returned %d\n", + nic->num_vec, ret); + return 1; + } /* Register mailbox interrupt handler */ for (i = NIC_PF_INTR_ID_MBOX0; i < nic->num_vec; i++) { sprintf(nic->irq_name[i], "NICPF Mbox%d", (i - NIC_PF_INTR_ID_MBOX0)); - ret = request_irq(nic->msix_entries[i].vector, + ret = request_irq(pci_irq_vector(nic->pdev, i), nic_mbx_intr_handler, 0, nic->irq_name[i], nic); if (ret) @@ -1186,14 +1152,16 @@ static int nic_register_interrupts(struct nicpf *nic) fail: dev_err(&nic->pdev->dev, "Request irq failed\n"); nic_free_all_interrupts(nic); - nic_disable_msix(nic); + pci_free_irq_vectors(nic->pdev); + nic->num_vec = 0; return ret; } static void nic_unregister_interrupts(struct nicpf *nic) { nic_free_all_interrupts(nic); - nic_disable_msix(nic); + pci_free_irq_vectors(nic->pdev); + nic->num_vec = 0; } static int nic_num_sqs_en(struct nicpf *nic, int vf_en) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 330d8a03ea11..81a2fcb3cb1b 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -882,38 +882,9 @@ static irqreturn_t nicvf_qs_err_intr_handler(int irq, void *nicvf_irq) return IRQ_HANDLED; } -static int nicvf_enable_msix(struct nicvf *nic) -{ - int ret, vec; - - nic->num_vec = NIC_VF_MSIX_VECTORS; - - for (vec = 0; vec < nic->num_vec; vec++) - nic->msix_entries[vec].entry = vec; - - ret = pci_enable_msix(nic->pdev, nic->msix_entries, nic->num_vec); - if (ret) { - netdev_err(nic->netdev, - "Req for #%d msix vectors failed\n", nic->num_vec); - return 0; - } - nic->msix_enabled = 1; - return 1; -} - -static void nicvf_disable_msix(struct nicvf *nic) -{ - if (nic->msix_enabled) { - pci_disable_msix(nic->pdev); - nic->msix_enabled = 0; - nic->num_vec = 0; - } -} - static void nicvf_set_irq_affinity(struct nicvf *nic) { int vec, cpu; - int irqnum; for (vec = 0; vec < nic->num_vec; vec++) { if (!nic->irq_allocated[vec]) @@ -930,15 +901,14 @@ static void nicvf_set_irq_affinity(struct nicvf *nic) cpumask_set_cpu(cpumask_local_spread(cpu, nic->node), nic->affinity_mask[vec]); - irqnum = nic->msix_entries[vec].vector; - irq_set_affinity_hint(irqnum, nic->affinity_mask[vec]); + irq_set_affinity_hint(pci_irq_vector(nic->pdev, vec), + nic->affinity_mask[vec]); } } static int nicvf_register_interrupts(struct nicvf *nic) { int irq, ret = 0; - int vector; for_each_cq_irq(irq) sprintf(nic->irq_name[irq], "%s-rxtx-%d", @@ -957,8 +927,8 @@ static int nicvf_register_interrupts(struct nicvf *nic) /* Register CQ interrupts */ for (irq = 0; irq < nic->qs->cq_cnt; irq++) { - vector = nic->msix_entries[irq].vector; - ret = request_irq(vector, nicvf_intr_handler, + ret = request_irq(pci_irq_vector(nic->pdev, irq), + nicvf_intr_handler, 0, nic->irq_name[irq], nic->napi[irq]); if (ret) goto err; @@ -968,8 +938,8 @@ static int nicvf_register_interrupts(struct nicvf *nic) /* Register RBDR interrupt */ for (irq = NICVF_INTR_ID_RBDR; irq < (NICVF_INTR_ID_RBDR + nic->qs->rbdr_cnt); irq++) { - vector = nic->msix_entries[irq].vector; - ret = request_irq(vector, nicvf_rbdr_intr_handler, + ret = request_irq(pci_irq_vector(nic->pdev, irq), + nicvf_rbdr_intr_handler, 0, nic->irq_name[irq], nic); if (ret) goto err; @@ -981,7 +951,7 @@ static int nicvf_register_interrupts(struct nicvf *nic) nic->pnicvf->netdev->name, nic->sqs_mode ? (nic->sqs_id + 1) : 0); irq = NICVF_INTR_ID_QS_ERR; - ret = request_irq(nic->msix_entries[irq].vector, + ret = request_irq(pci_irq_vector(nic->pdev, irq), nicvf_qs_err_intr_handler, 0, nic->irq_name[irq], nic); if (ret) @@ -1001,6 +971,7 @@ err: static void nicvf_unregister_interrupts(struct nicvf *nic) { + struct pci_dev *pdev = nic->pdev; int irq; /* Free registered interrupts */ @@ -1008,19 +979,20 @@ static void nicvf_unregister_interrupts(struct nicvf *nic) if (!nic->irq_allocated[irq]) continue; - irq_set_affinity_hint(nic->msix_entries[irq].vector, NULL); + irq_set_affinity_hint(pci_irq_vector(pdev, irq), NULL); free_cpumask_var(nic->affinity_mask[irq]); if (irq < NICVF_INTR_ID_SQ) - free_irq(nic->msix_entries[irq].vector, nic->napi[irq]); + free_irq(pci_irq_vector(pdev, irq), nic->napi[irq]); else - free_irq(nic->msix_entries[irq].vector, nic); + free_irq(pci_irq_vector(pdev, irq), nic); nic->irq_allocated[irq] = false; } /* Disable MSI-X */ - nicvf_disable_msix(nic); + pci_free_irq_vectors(pdev); + nic->num_vec = 0; } /* Initialize MSIX vectors and register MISC interrupt. @@ -1032,16 +1004,22 @@ static int nicvf_register_misc_interrupt(struct nicvf *nic) int irq = NICVF_INTR_ID_MISC; /* Return if mailbox interrupt is already registered */ - if (nic->msix_enabled) + if (nic->pdev->msix_enabled) return 0; /* Enable MSI-X */ - if (!nicvf_enable_msix(nic)) + nic->num_vec = pci_msix_vec_count(nic->pdev); + ret = pci_alloc_irq_vectors(nic->pdev, nic->num_vec, nic->num_vec, + PCI_IRQ_MSIX); + if (ret < 0) { + netdev_err(nic->netdev, + "Req for #%d msix vectors failed\n", nic->num_vec); return 1; + } sprintf(nic->irq_name[irq], "%s Mbox", "NICVF"); /* Register Misc interrupt */ - ret = request_irq(nic->msix_entries[irq].vector, + ret = request_irq(pci_irq_vector(nic->pdev, irq), nicvf_misc_intr_handler, 0, nic->irq_name[irq], nic); if (ret) @@ -1164,7 +1142,7 @@ int nicvf_stop(struct net_device *netdev) /* Wait for pending IRQ handlers to finish */ for (irq = 0; irq < nic->num_vec; irq++) - synchronize_irq(nic->msix_entries[irq].vector); + synchronize_irq(pci_irq_vector(nic->pdev, irq)); tasklet_kill(&nic->rbdr_task); tasklet_kill(&nic->qs_err_task); @@ -1365,7 +1343,7 @@ static int nicvf_set_mac_address(struct net_device *netdev, void *p) memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); - if (nic->msix_enabled) { + if (nic->pdev->msix_enabled) { if (nicvf_hw_set_mac_addr(nic, netdev)) return -EBUSY; } else { -- cgit From 4244de1c64ded7f5438717bdce3fa074efd20efb Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 11 Apr 2017 13:01:24 +0200 Subject: PCI: remove pci_enable_msix Unused now that all callers switched to pci_alloc_irq_vectors. Signed-off-by: Christoph Hellwig Acked-by: Bjorn Helgaas Signed-off-by: David S. Miller --- drivers/pci/msi.c | 21 --------------------- include/linux/pci.h | 4 ---- 2 files changed, 25 deletions(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index d571bc330686..0042c365b29b 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -973,27 +973,6 @@ static int __pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, return msix_capability_init(dev, entries, nvec, affd); } -/** - * pci_enable_msix - configure device's MSI-X capability structure - * @dev: pointer to the pci_dev data structure of MSI-X device function - * @entries: pointer to an array of MSI-X entries (optional) - * @nvec: number of MSI-X irqs requested for allocation by device driver - * - * Setup the MSI-X capability structure of device function with the number - * of requested irqs upon its software driver call to request for - * MSI-X mode enabled on its hardware device function. A return of zero - * indicates the successful configuration of MSI-X capability structure - * with new allocated MSI-X irqs. A return of < 0 indicates a failure. - * Or a return of > 0 indicates that driver request is exceeding the number - * of irqs or MSI-X vectors available. Driver should use the returned value to - * re-send its request. - **/ -int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) -{ - return __pci_enable_msix(dev, entries, nvec, NULL); -} -EXPORT_SYMBOL(pci_enable_msix); - void pci_msix_shutdown(struct pci_dev *dev) { struct msi_desc *entry; diff --git a/include/linux/pci.h b/include/linux/pci.h index eb3da1a04e6c..82dec36845e6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1300,7 +1300,6 @@ int pci_msi_vec_count(struct pci_dev *dev); void pci_msi_shutdown(struct pci_dev *dev); void pci_disable_msi(struct pci_dev *dev); int pci_msix_vec_count(struct pci_dev *dev); -int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec); void pci_msix_shutdown(struct pci_dev *dev); void pci_disable_msix(struct pci_dev *dev); void pci_restore_msi_state(struct pci_dev *dev); @@ -1330,9 +1329,6 @@ static inline int pci_msi_vec_count(struct pci_dev *dev) { return -ENOSYS; } static inline void pci_msi_shutdown(struct pci_dev *dev) { } static inline void pci_disable_msi(struct pci_dev *dev) { } static inline int pci_msix_vec_count(struct pci_dev *dev) { return -ENOSYS; } -static inline int pci_enable_msix(struct pci_dev *dev, - struct msix_entry *entries, int nvec) -{ return -ENOSYS; } static inline void pci_msix_shutdown(struct pci_dev *dev) { } static inline void pci_disable_msix(struct pci_dev *dev) { } static inline void pci_restore_msi_state(struct pci_dev *dev) { } -- cgit From 3680b1f655993e1be5a2063d00eae92532abc322 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 11 Apr 2017 13:01:25 +0200 Subject: mlxsw: convert to pci_alloc_irq_vectors Trivial conversion as only one vector is supported, but at least we lose the useless msix_entry member in the per-device structure. Signed-off-by: Christoph Hellwig Acked-by: Jiri Pirko Reviewed-by: Ido Schimmel Tested-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/pci.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/pci.c b/drivers/net/ethernet/mellanox/mlxsw/pci.c index eaa3e3bf5a2b..23f7d828cf67 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/pci.c +++ b/drivers/net/ethernet/mellanox/mlxsw/pci.c @@ -136,7 +136,6 @@ struct mlxsw_pci { u8 __iomem *hw_addr; struct mlxsw_pci_queue_type_group queues[MLXSW_PCI_QUEUE_TYPE_COUNT]; u32 doorbell_offset; - struct msix_entry msix_entry; struct mlxsw_core *core; struct { struct mlxsw_pci_mem_item *items; @@ -1409,7 +1408,7 @@ static int mlxsw_pci_init(void *bus_priv, struct mlxsw_core *mlxsw_core, if (err) goto err_aqs_init; - err = request_irq(mlxsw_pci->msix_entry.vector, + err = request_irq(pci_irq_vector(pdev, 0), mlxsw_pci_eq_irq_handler, 0, mlxsw_pci->bus_info.device_kind, mlxsw_pci); if (err) { @@ -1442,7 +1441,7 @@ static void mlxsw_pci_fini(void *bus_priv) { struct mlxsw_pci *mlxsw_pci = bus_priv; - free_irq(mlxsw_pci->msix_entry.vector, mlxsw_pci); + free_irq(pci_irq_vector(mlxsw_pci->pdev, 0), mlxsw_pci); mlxsw_pci_aqs_fini(mlxsw_pci); mlxsw_pci_fw_area_fini(mlxsw_pci); mlxsw_pci_mbox_free(mlxsw_pci, &mlxsw_pci->cmd.out_mbox); @@ -1717,8 +1716,8 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_sw_reset; } - err = pci_enable_msix_exact(pdev, &mlxsw_pci->msix_entry, 1); - if (err) { + err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX); + if (err < 0) { dev_err(&pdev->dev, "MSI-X init failed\n"); goto err_msix_init; } @@ -1737,7 +1736,7 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; err_bus_device_register: - pci_disable_msix(mlxsw_pci->pdev); + pci_free_irq_vectors(mlxsw_pci->pdev); err_msix_init: err_sw_reset: iounmap(mlxsw_pci->hw_addr); @@ -1757,7 +1756,7 @@ static void mlxsw_pci_remove(struct pci_dev *pdev) struct mlxsw_pci *mlxsw_pci = pci_get_drvdata(pdev); mlxsw_core_bus_device_unregister(mlxsw_pci->core); - pci_disable_msix(mlxsw_pci->pdev); + pci_free_irq_vectors(mlxsw_pci->pdev); iounmap(mlxsw_pci->hw_addr); pci_release_regions(mlxsw_pci->pdev); pci_disable_device(mlxsw_pci->pdev); -- cgit From af87ae465abdc070de0dc35d6c6a9e7a8cd82987 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Tue, 11 Apr 2017 13:12:13 +0200 Subject: l2tp: remove useless duplicate session detection in l2tp_netlink There's no point in checking for duplicate sessions at the beginning of l2tp_nl_cmd_session_create(); the ->session_create() callbacks already return -EEXIST when the session already exists. Furthermore, even if l2tp_session_find() returns NULL, a new session might be created right after the test. So relying on ->session_create() to avoid duplicate session is the only sane behaviour. Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller --- net/l2tp/l2tp_netlink.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index 7e3e669baac4..12cfcd0ca807 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -521,11 +521,6 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf goto out; } session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]); - session = l2tp_session_find(net, tunnel, session_id); - if (session) { - ret = -EEXIST; - goto out; - } if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) { ret = -EINVAL; -- cgit From 55a3ce3b9d98f752df9e2cfb1cba7e715522428a Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Tue, 11 Apr 2017 13:12:21 +0200 Subject: l2tp: remove l2tp_session_find() This function isn't used anymore. Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller --- net/l2tp/l2tp_core.c | 51 +-------------------------------------------------- net/l2tp/l2tp_core.h | 3 --- 2 files changed, 1 insertion(+), 53 deletions(-) diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index e37d9554da7b..154974be1eed 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -217,27 +217,6 @@ static void l2tp_tunnel_sock_put(struct sock *sk) sock_put(sk); } -/* Lookup a session by id in the global session list - */ -static struct l2tp_session *l2tp_session_find_2(struct net *net, u32 session_id) -{ - struct l2tp_net *pn = l2tp_pernet(net); - struct hlist_head *session_list = - l2tp_session_id_hash_2(pn, session_id); - struct l2tp_session *session; - - rcu_read_lock_bh(); - hlist_for_each_entry_rcu(session, session_list, global_hlist) { - if (session->session_id == session_id) { - rcu_read_unlock_bh(); - return session; - } - } - rcu_read_unlock_bh(); - - return NULL; -} - /* Session hash list. * The session_id SHOULD be random according to RFC2661, but several * L2TP implementations (Cisco and Microsoft) use incrementing @@ -250,35 +229,7 @@ l2tp_session_id_hash(struct l2tp_tunnel *tunnel, u32 session_id) return &tunnel->session_hlist[hash_32(session_id, L2TP_HASH_BITS)]; } -/* Lookup a session by id - */ -struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id) -{ - struct hlist_head *session_list; - struct l2tp_session *session; - - /* In L2TPv3, session_ids are unique over all tunnels and we - * sometimes need to look them up before we know the - * tunnel. - */ - if (tunnel == NULL) - return l2tp_session_find_2(net, session_id); - - session_list = l2tp_session_id_hash(tunnel, session_id); - read_lock_bh(&tunnel->hlist_lock); - hlist_for_each_entry(session, session_list, hlist) { - if (session->session_id == session_id) { - read_unlock_bh(&tunnel->hlist_lock); - return session; - } - } - read_unlock_bh(&tunnel->hlist_lock); - - return NULL; -} -EXPORT_SYMBOL_GPL(l2tp_session_find); - -/* Like l2tp_session_find() but takes a reference on the returned session. +/* Lookup a session. A new reference is held on the returned session. * Optionally calls session->ref() too if do_ref is true. */ struct l2tp_session *l2tp_session_get(struct net *net, diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 8ce7818c7a9d..a3248e18badb 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -233,9 +233,6 @@ out: struct l2tp_session *l2tp_session_get(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id, bool do_ref); -struct l2tp_session *l2tp_session_find(struct net *net, - struct l2tp_tunnel *tunnel, - u32 session_id); struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth, bool do_ref); struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname, -- cgit From 61e04ccbcbc4b1ba974af371e6f63906fb1d78fb Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Tue, 11 Apr 2017 19:13:03 +0800 Subject: net: fec: add return value check after calling .of_property_read_u32() Add return value check after calling .of_property_read_u32() to avoid the warning reported by coverity. Signed-off-by: Fugang Duan Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 91a16641e851..886a9c9fd628 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -3197,9 +3197,9 @@ static void fec_reset_phy(struct platform_device *pdev) if (!np) return; - of_property_read_u32(np, "phy-reset-duration", &msec); + err = of_property_read_u32(np, "phy-reset-duration", &msec); /* A sane reset duration should not be longer than 1s */ - if (msec > 1000) + if (!err && msec > 1000) msec = 1; phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); -- cgit From 145d6e295f12681ed564d32fff3dabf9292f6871 Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Tue, 11 Apr 2017 19:13:04 +0800 Subject: net: fec: avoid BD pointer type cast to 32bit In aarch64 system, the BD pointer is 64bit, and the high-order 32-bits of the address is effective, so replace usigned with (void *) type to aovid 64bit address is casted to 32bit in .fec_enet_get_nextdesc() and .fec_enet_get_prevdesc() functions. Signed-off-by: Fugang Duan Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 886a9c9fd628..172624c2d5be 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -235,14 +235,14 @@ static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp, struct bufdesc_prop *bd) { return (bdp >= bd->last) ? bd->base - : (struct bufdesc *)(((unsigned)bdp) + bd->dsize); + : (struct bufdesc *)(((void *)bdp) + bd->dsize); } static struct bufdesc *fec_enet_get_prevdesc(struct bufdesc *bdp, struct bufdesc_prop *bd) { return (bdp <= bd->base) ? bd->last - : (struct bufdesc *)(((unsigned)bdp) - bd->dsize); + : (struct bufdesc *)(((void *)bdp) - bd->dsize); } static int fec_enet_get_bd_index(struct bufdesc *bdp, -- cgit From 949201286fa4a481d8302f8b886ca565039bafcd Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Tue, 11 Apr 2017 19:13:05 +0800 Subject: net: fec: pass ->dev to dma_alloc__coherent() API In aarch64 system, it requires to trasfer ->dev to dma_alloc_coherent() API, otherwise allocate failed and print kernel warning. Signed-off-by: Fugang Duan Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 172624c2d5be..bcf86753b6e2 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2651,7 +2651,7 @@ static void fec_enet_free_queue(struct net_device *ndev) for (i = 0; i < fep->num_tx_queues; i++) if (fep->tx_queue[i] && fep->tx_queue[i]->tso_hdrs) { txq = fep->tx_queue[i]; - dma_free_coherent(NULL, + dma_free_coherent(&fep->pdev->dev, txq->bd.ring_size * TSO_HEADER_SIZE, txq->tso_hdrs, txq->tso_hdrs_dma); @@ -2685,7 +2685,7 @@ static int fec_enet_alloc_queue(struct net_device *ndev) txq->tx_wake_threshold = (txq->bd.ring_size - txq->tx_stop_threshold) / 2; - txq->tso_hdrs = dma_alloc_coherent(NULL, + txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev, txq->bd.ring_size * TSO_HEADER_SIZE, &txq->tso_hdrs_dma, GFP_KERNEL); -- cgit From 9269e5560b261eb9ee157497890dc0948db76cf8 Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Tue, 11 Apr 2017 19:13:06 +0800 Subject: net: fec: add phy-reset-gpios PROBE_DEFER check Many boards use i2c/spi expander gpio as phy-reset-gpios and these gpios maybe registered after fec port, driver should check the return value of .of_get_named_gpio(). Signed-off-by: Fugang Duan Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index bcf86753b6e2..3cec94ef8a6e 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -3187,7 +3187,7 @@ static int fec_enet_init(struct net_device *ndev) } #ifdef CONFIG_OF -static void fec_reset_phy(struct platform_device *pdev) +static int fec_reset_phy(struct platform_device *pdev) { int err, phy_reset; bool active_high = false; @@ -3195,7 +3195,7 @@ static void fec_reset_phy(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; if (!np) - return; + return 0; err = of_property_read_u32(np, "phy-reset-duration", &msec); /* A sane reset duration should not be longer than 1s */ @@ -3203,8 +3203,10 @@ static void fec_reset_phy(struct platform_device *pdev) msec = 1; phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); - if (!gpio_is_valid(phy_reset)) - return; + if (phy_reset == -EPROBE_DEFER) + return phy_reset; + else if (!gpio_is_valid(phy_reset)) + return 0; active_high = of_property_read_bool(np, "phy-reset-active-high"); @@ -3213,7 +3215,7 @@ static void fec_reset_phy(struct platform_device *pdev) "phy-reset"); if (err) { dev_err(&pdev->dev, "failed to get phy-reset-gpios: %d\n", err); - return; + return err; } if (msec > 20) @@ -3222,14 +3224,17 @@ static void fec_reset_phy(struct platform_device *pdev) usleep_range(msec * 1000, msec * 1000 + 1000); gpio_set_value_cansleep(phy_reset, !active_high); + + return 0; } #else /* CONFIG_OF */ -static void fec_reset_phy(struct platform_device *pdev) +static int fec_reset_phy(struct platform_device *pdev) { /* * In case of platform probe, the reset has been done * by machine code. */ + return 0; } #endif /* CONFIG_OF */ @@ -3400,6 +3405,7 @@ fec_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Failed to enable phy regulator: %d\n", ret); + clk_disable_unprepare(fep->clk_ipg); goto failed_regulator; } } else { @@ -3412,7 +3418,9 @@ fec_probe(struct platform_device *pdev) pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); - fec_reset_phy(pdev); + ret = fec_reset_phy(pdev); + if (ret) + goto failed_reset; if (fep->bufdesc_ex) fec_ptp_init(pdev); @@ -3473,8 +3481,10 @@ failed_init: fec_ptp_stop(pdev); if (fep->reg_phy) regulator_disable(fep->reg_phy); +failed_reset: + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); failed_regulator: - clk_disable_unprepare(fep->clk_ipg); failed_clk_ipg: fec_enet_clk_enable(ndev, false); failed_clk: -- cgit From c10bc0e7b732a05fae40dff075be6a4cc560bfd5 Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Tue, 11 Apr 2017 19:13:07 +0800 Subject: net: fec: correct the errata number comment typo Correct the errata number ERR006358 comment typo. Signed-off-by: Fugang Duan Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 3cec94ef8a6e..f3a783541eea 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1266,7 +1266,7 @@ skb_done: } } - /* ERR006538: Keep the transmitter going */ + /* ERR006358: Keep the transmitter going */ if (bdp != txq->bd.cur && readl(txq->bd.reg_desc_active) == 0) writel(0, txq->bd.reg_desc_active); -- cgit From 99492ad488a8c88e8e62283ea9bea7bec691aeab Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Tue, 11 Apr 2017 19:13:08 +0800 Subject: net: fec: add ERR007885 for i.MX6ul enet IP The errata ERR007885 HW fix don't add to i.MX6ul ENET IP version, so add sw workaroud for the chip. Signed-off-by: Fugang Duan Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index f3a783541eea..a92bf94f8e94 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -117,8 +117,9 @@ static struct platform_device_id fec_devtype[] = { .name = "imx6ul-fec", .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | - FEC_QUIRK_HAS_VLAN | FEC_QUIRK_BUG_CAPTURE | - FEC_QUIRK_HAS_RACC | FEC_QUIRK_HAS_COALESCE, + FEC_QUIRK_HAS_VLAN | FEC_QUIRK_ERR007885 | + FEC_QUIRK_BUG_CAPTURE | FEC_QUIRK_HAS_RACC | + FEC_QUIRK_HAS_COALESCE, }, { /* sentinel */ } -- cgit From be9370a7d8614d1fa54649c75de14458e79b91ec Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 11 Apr 2017 15:34:57 +0200 Subject: bpf: remove struct bpf_prog_type_list There's no need to have struct bpf_prog_type_list since it just contains a list_head, the type, and the ops pointer. Since the types are densely packed and not actually dynamically registered, it's much easier and smaller to have an array of type->ops pointer. Also initialize this array statically to remove code needed to initialize it. In order to save duplicating the list, move it to a new header file and include it in the places needing it. Signed-off-by: Johannes Berg Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf.h | 16 ++++------ include/linux/bpf_types.h | 18 ++++++++++++ kernel/bpf/syscall.c | 27 +++++++---------- kernel/trace/bpf_trace.c | 30 ++----------------- net/core/filter.c | 75 +++++------------------------------------------ 5 files changed, 44 insertions(+), 122 deletions(-) create mode 100644 include/linux/bpf_types.h diff --git a/include/linux/bpf.h b/include/linux/bpf.h index bbb513da5075..07fc02bb38e4 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -173,12 +173,6 @@ struct bpf_verifier_ops { union bpf_attr __user *uattr); }; -struct bpf_prog_type_list { - struct list_head list_node; - const struct bpf_verifier_ops *ops; - enum bpf_prog_type type; -}; - struct bpf_prog_aux { atomic_t refcnt; u32 used_map_cnt; @@ -243,7 +237,11 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, #ifdef CONFIG_BPF_SYSCALL DECLARE_PER_CPU(int, bpf_prog_active); -void bpf_register_prog_type(struct bpf_prog_type_list *tl); +#define BPF_PROG_TYPE(_id, _ops) \ + extern const struct bpf_verifier_ops _ops; +#include +#undef BPF_PROG_TYPE + void bpf_register_map_type(struct bpf_map_type_list *tl); struct bpf_prog *bpf_prog_get(u32 ufd); @@ -306,10 +304,6 @@ static inline void bpf_long_memcpy(void *dst, const void *src, u32 size) /* verify correctness of eBPF program */ int bpf_check(struct bpf_prog **fp, union bpf_attr *attr); #else -static inline void bpf_register_prog_type(struct bpf_prog_type_list *tl) -{ -} - static inline struct bpf_prog *bpf_prog_get(u32 ufd) { return ERR_PTR(-EOPNOTSUPP); diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h new file mode 100644 index 000000000000..68b0a9811216 --- /dev/null +++ b/include/linux/bpf_types.h @@ -0,0 +1,18 @@ +/* internal file - do not include directly */ + +#ifdef CONFIG_NET +BPF_PROG_TYPE(BPF_PROG_TYPE_SOCKET_FILTER, sk_filter_prog_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_CLS, tc_cls_act_prog_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_ACT, tc_cls_act_prog_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_XDP, xdp_prog_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SKB, cg_skb_prog_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCK, cg_sock_prog_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_IN, lwt_inout_prog_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_OUT, lwt_inout_prog_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_XMIT, lwt_xmit_prog_ops) +#endif +#ifdef CONFIG_BPF_EVENTS +BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe_prog_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint_prog_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event_prog_ops) +#endif diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index ab0cf4c43690..ea55691cbf5e 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -573,26 +573,21 @@ err_put: return err; } -static LIST_HEAD(bpf_prog_types); +static const struct bpf_verifier_ops * const bpf_prog_types[] = { +#define BPF_PROG_TYPE(_id, _ops) \ + [_id] = &_ops, +#include +#undef BPF_PROG_TYPE +}; static int find_prog_type(enum bpf_prog_type type, struct bpf_prog *prog) { - struct bpf_prog_type_list *tl; - - list_for_each_entry(tl, &bpf_prog_types, list_node) { - if (tl->type == type) { - prog->aux->ops = tl->ops; - prog->type = type; - return 0; - } - } - - return -EINVAL; -} + if (type >= ARRAY_SIZE(bpf_prog_types) || !bpf_prog_types[type]) + return -EINVAL; -void bpf_register_prog_type(struct bpf_prog_type_list *tl) -{ - list_add(&tl->list_node, &bpf_prog_types); + prog->aux->ops = bpf_prog_types[type]; + prog->type = type; + return 0; } /* drop refcnt on maps used by eBPF program and free auxilary data */ diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index cee9802cf3e0..8a4efac28710 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -501,16 +501,11 @@ static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type return true; } -static const struct bpf_verifier_ops kprobe_prog_ops = { +const struct bpf_verifier_ops kprobe_prog_ops = { .get_func_proto = kprobe_prog_func_proto, .is_valid_access = kprobe_prog_is_valid_access, }; -static struct bpf_prog_type_list kprobe_tl __ro_after_init = { - .ops = &kprobe_prog_ops, - .type = BPF_PROG_TYPE_KPROBE, -}; - BPF_CALL_5(bpf_perf_event_output_tp, void *, tp_buff, struct bpf_map *, map, u64, flags, void *, data, u64, size) { @@ -584,16 +579,11 @@ static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type return true; } -static const struct bpf_verifier_ops tracepoint_prog_ops = { +const struct bpf_verifier_ops tracepoint_prog_ops = { .get_func_proto = tp_prog_func_proto, .is_valid_access = tp_prog_is_valid_access, }; -static struct bpf_prog_type_list tracepoint_tl __ro_after_init = { - .ops = &tracepoint_prog_ops, - .type = BPF_PROG_TYPE_TRACEPOINT, -}; - static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type, enum bpf_reg_type *reg_type) { @@ -642,22 +632,8 @@ static u32 pe_prog_convert_ctx_access(enum bpf_access_type type, return insn - insn_buf; } -static const struct bpf_verifier_ops perf_event_prog_ops = { +const struct bpf_verifier_ops perf_event_prog_ops = { .get_func_proto = tp_prog_func_proto, .is_valid_access = pe_prog_is_valid_access, .convert_ctx_access = pe_prog_convert_ctx_access, }; - -static struct bpf_prog_type_list perf_event_tl __ro_after_init = { - .ops = &perf_event_prog_ops, - .type = BPF_PROG_TYPE_PERF_EVENT, -}; - -static int __init register_kprobe_prog_ops(void) -{ - bpf_register_prog_type(&kprobe_tl); - bpf_register_prog_type(&tracepoint_tl); - bpf_register_prog_type(&perf_event_tl); - return 0; -} -late_initcall(register_kprobe_prog_ops); diff --git a/net/core/filter.c b/net/core/filter.c index 15e9a81ffebe..bbe0cf415105 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3298,13 +3298,13 @@ static u32 xdp_convert_ctx_access(enum bpf_access_type type, return insn - insn_buf; } -static const struct bpf_verifier_ops sk_filter_ops = { +const struct bpf_verifier_ops sk_filter_prog_ops = { .get_func_proto = sk_filter_func_proto, .is_valid_access = sk_filter_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, }; -static const struct bpf_verifier_ops tc_cls_act_ops = { +const struct bpf_verifier_ops tc_cls_act_prog_ops = { .get_func_proto = tc_cls_act_func_proto, .is_valid_access = tc_cls_act_is_valid_access, .convert_ctx_access = tc_cls_act_convert_ctx_access, @@ -3312,28 +3312,28 @@ static const struct bpf_verifier_ops tc_cls_act_ops = { .test_run = bpf_prog_test_run_skb, }; -static const struct bpf_verifier_ops xdp_ops = { +const struct bpf_verifier_ops xdp_prog_ops = { .get_func_proto = xdp_func_proto, .is_valid_access = xdp_is_valid_access, .convert_ctx_access = xdp_convert_ctx_access, .test_run = bpf_prog_test_run_xdp, }; -static const struct bpf_verifier_ops cg_skb_ops = { +const struct bpf_verifier_ops cg_skb_prog_ops = { .get_func_proto = cg_skb_func_proto, .is_valid_access = sk_filter_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, .test_run = bpf_prog_test_run_skb, }; -static const struct bpf_verifier_ops lwt_inout_ops = { +const struct bpf_verifier_ops lwt_inout_prog_ops = { .get_func_proto = lwt_inout_func_proto, .is_valid_access = lwt_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, .test_run = bpf_prog_test_run_skb, }; -static const struct bpf_verifier_ops lwt_xmit_ops = { +const struct bpf_verifier_ops lwt_xmit_prog_ops = { .get_func_proto = lwt_xmit_func_proto, .is_valid_access = lwt_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, @@ -3341,73 +3341,12 @@ static const struct bpf_verifier_ops lwt_xmit_ops = { .test_run = bpf_prog_test_run_skb, }; -static const struct bpf_verifier_ops cg_sock_ops = { +const struct bpf_verifier_ops cg_sock_prog_ops = { .get_func_proto = bpf_base_func_proto, .is_valid_access = sock_filter_is_valid_access, .convert_ctx_access = sock_filter_convert_ctx_access, }; -static struct bpf_prog_type_list sk_filter_type __ro_after_init = { - .ops = &sk_filter_ops, - .type = BPF_PROG_TYPE_SOCKET_FILTER, -}; - -static struct bpf_prog_type_list sched_cls_type __ro_after_init = { - .ops = &tc_cls_act_ops, - .type = BPF_PROG_TYPE_SCHED_CLS, -}; - -static struct bpf_prog_type_list sched_act_type __ro_after_init = { - .ops = &tc_cls_act_ops, - .type = BPF_PROG_TYPE_SCHED_ACT, -}; - -static struct bpf_prog_type_list xdp_type __ro_after_init = { - .ops = &xdp_ops, - .type = BPF_PROG_TYPE_XDP, -}; - -static struct bpf_prog_type_list cg_skb_type __ro_after_init = { - .ops = &cg_skb_ops, - .type = BPF_PROG_TYPE_CGROUP_SKB, -}; - -static struct bpf_prog_type_list lwt_in_type __ro_after_init = { - .ops = &lwt_inout_ops, - .type = BPF_PROG_TYPE_LWT_IN, -}; - -static struct bpf_prog_type_list lwt_out_type __ro_after_init = { - .ops = &lwt_inout_ops, - .type = BPF_PROG_TYPE_LWT_OUT, -}; - -static struct bpf_prog_type_list lwt_xmit_type __ro_after_init = { - .ops = &lwt_xmit_ops, - .type = BPF_PROG_TYPE_LWT_XMIT, -}; - -static struct bpf_prog_type_list cg_sock_type __ro_after_init = { - .ops = &cg_sock_ops, - .type = BPF_PROG_TYPE_CGROUP_SOCK -}; - -static int __init register_sk_filter_ops(void) -{ - bpf_register_prog_type(&sk_filter_type); - bpf_register_prog_type(&sched_cls_type); - bpf_register_prog_type(&sched_act_type); - bpf_register_prog_type(&xdp_type); - bpf_register_prog_type(&cg_skb_type); - bpf_register_prog_type(&cg_sock_type); - bpf_register_prog_type(&lwt_in_type); - bpf_register_prog_type(&lwt_out_type); - bpf_register_prog_type(&lwt_xmit_type); - - return 0; -} -late_initcall(register_sk_filter_ops); - int sk_detach_filter(struct sock *sk) { int ret = -ENOENT; -- cgit From 40077e0cf62206ac3c315b6991d8dcddb3703286 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 11 Apr 2017 15:34:58 +0200 Subject: bpf: remove struct bpf_map_type_list There's no need to have struct bpf_map_type_list since it just contains a list_head, the type, and the ops pointer. Since the types are densely packed and not actually dynamically registered, it's much easier and smaller to have an array of type->ops pointer. Also initialize this array statically to remove code needed to initialize it. In order to save duplicating the list, move it to the types header file added by the previous patch and include it in the same fashion. Signed-off-by: Johannes Berg Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf.h | 11 ++----- include/linux/bpf_types.h | 18 +++++++++++ kernel/bpf/arraymap.c | 78 ++++------------------------------------------- kernel/bpf/hashtab.c | 46 +++------------------------- kernel/bpf/lpm_trie.c | 14 +-------- kernel/bpf/stackmap.c | 14 +-------- kernel/bpf/syscall.c | 37 +++++++++++----------- 7 files changed, 53 insertions(+), 165 deletions(-) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 07fc02bb38e4..6bb38d76faf4 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -53,12 +53,6 @@ struct bpf_map { struct bpf_map *inner_map_meta; }; -struct bpf_map_type_list { - struct list_head list_node; - const struct bpf_map_ops *ops; - enum bpf_map_type type; -}; - /* function argument constraints */ enum bpf_arg_type { ARG_DONTCARE = 0, /* unused argument in helper function */ @@ -239,10 +233,11 @@ DECLARE_PER_CPU(int, bpf_prog_active); #define BPF_PROG_TYPE(_id, _ops) \ extern const struct bpf_verifier_ops _ops; +#define BPF_MAP_TYPE(_id, _ops) \ + extern const struct bpf_map_ops _ops; #include #undef BPF_PROG_TYPE - -void bpf_register_map_type(struct bpf_map_type_list *tl); +#undef BPF_MAP_TYPE struct bpf_prog *bpf_prog_get(u32 ufd); struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type); diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index 68b0a9811216..03bf223f18be 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -16,3 +16,21 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe_prog_ops) BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint_prog_ops) BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event_prog_ops) #endif + +BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops) +BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops) +BPF_MAP_TYPE(BPF_MAP_TYPE_PROG_ARRAY, prog_array_map_ops) +BPF_MAP_TYPE(BPF_MAP_TYPE_PERF_EVENT_ARRAY, perf_event_array_map_ops) +#ifdef CONFIG_CGROUPS +BPF_MAP_TYPE(BPF_MAP_TYPE_CGROUP_ARRAY, cgroup_array_map_ops) +#endif +BPF_MAP_TYPE(BPF_MAP_TYPE_HASH, htab_map_ops) +BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_HASH, htab_percpu_map_ops) +BPF_MAP_TYPE(BPF_MAP_TYPE_LRU_HASH, htab_lru_map_ops) +BPF_MAP_TYPE(BPF_MAP_TYPE_LRU_PERCPU_HASH, htab_lru_percpu_map_ops) +BPF_MAP_TYPE(BPF_MAP_TYPE_LPM_TRIE, trie_map_ops) +#ifdef CONFIG_PERF_EVENTS +BPF_MAP_TYPE(BPF_MAP_TYPE_STACK_TRACE, stack_map_ops) +#endif +BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY_OF_MAPS, array_of_maps_map_ops) +BPF_MAP_TYPE(BPF_MAP_TYPE_HASH_OF_MAPS, htab_of_maps_map_ops) diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index bc9da93db403..ec621df5a97a 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -287,7 +287,7 @@ static void array_map_free(struct bpf_map *map) bpf_map_area_free(array); } -static const struct bpf_map_ops array_ops = { +const struct bpf_map_ops array_map_ops = { .map_alloc = array_map_alloc, .map_free = array_map_free, .map_get_next_key = array_map_get_next_key, @@ -297,12 +297,7 @@ static const struct bpf_map_ops array_ops = { .map_gen_lookup = array_map_gen_lookup, }; -static struct bpf_map_type_list array_type __ro_after_init = { - .ops = &array_ops, - .type = BPF_MAP_TYPE_ARRAY, -}; - -static const struct bpf_map_ops percpu_array_ops = { +const struct bpf_map_ops percpu_array_map_ops = { .map_alloc = array_map_alloc, .map_free = array_map_free, .map_get_next_key = array_map_get_next_key, @@ -311,19 +306,6 @@ static const struct bpf_map_ops percpu_array_ops = { .map_delete_elem = array_map_delete_elem, }; -static struct bpf_map_type_list percpu_array_type __ro_after_init = { - .ops = &percpu_array_ops, - .type = BPF_MAP_TYPE_PERCPU_ARRAY, -}; - -static int __init register_array_map(void) -{ - bpf_register_map_type(&array_type); - bpf_register_map_type(&percpu_array_type); - return 0; -} -late_initcall(register_array_map); - static struct bpf_map *fd_array_map_alloc(union bpf_attr *attr) { /* only file descriptors can be stored in this type of map */ @@ -427,7 +409,7 @@ void bpf_fd_array_map_clear(struct bpf_map *map) fd_array_map_delete_elem(map, &i); } -static const struct bpf_map_ops prog_array_ops = { +const struct bpf_map_ops prog_array_map_ops = { .map_alloc = fd_array_map_alloc, .map_free = fd_array_map_free, .map_get_next_key = array_map_get_next_key, @@ -437,18 +419,6 @@ static const struct bpf_map_ops prog_array_ops = { .map_fd_put_ptr = prog_fd_array_put_ptr, }; -static struct bpf_map_type_list prog_array_type __ro_after_init = { - .ops = &prog_array_ops, - .type = BPF_MAP_TYPE_PROG_ARRAY, -}; - -static int __init register_prog_array_map(void) -{ - bpf_register_map_type(&prog_array_type); - return 0; -} -late_initcall(register_prog_array_map); - static struct bpf_event_entry *bpf_event_entry_gen(struct file *perf_file, struct file *map_file) { @@ -539,7 +509,7 @@ static void perf_event_fd_array_release(struct bpf_map *map, rcu_read_unlock(); } -static const struct bpf_map_ops perf_event_array_ops = { +const struct bpf_map_ops perf_event_array_map_ops = { .map_alloc = fd_array_map_alloc, .map_free = fd_array_map_free, .map_get_next_key = array_map_get_next_key, @@ -550,18 +520,6 @@ static const struct bpf_map_ops perf_event_array_ops = { .map_release = perf_event_fd_array_release, }; -static struct bpf_map_type_list perf_event_array_type __ro_after_init = { - .ops = &perf_event_array_ops, - .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, -}; - -static int __init register_perf_event_array_map(void) -{ - bpf_register_map_type(&perf_event_array_type); - return 0; -} -late_initcall(register_perf_event_array_map); - #ifdef CONFIG_CGROUPS static void *cgroup_fd_array_get_ptr(struct bpf_map *map, struct file *map_file /* not used */, @@ -582,7 +540,7 @@ static void cgroup_fd_array_free(struct bpf_map *map) fd_array_map_free(map); } -static const struct bpf_map_ops cgroup_array_ops = { +const struct bpf_map_ops cgroup_array_map_ops = { .map_alloc = fd_array_map_alloc, .map_free = cgroup_fd_array_free, .map_get_next_key = array_map_get_next_key, @@ -591,18 +549,6 @@ static const struct bpf_map_ops cgroup_array_ops = { .map_fd_get_ptr = cgroup_fd_array_get_ptr, .map_fd_put_ptr = cgroup_fd_array_put_ptr, }; - -static struct bpf_map_type_list cgroup_array_type __ro_after_init = { - .ops = &cgroup_array_ops, - .type = BPF_MAP_TYPE_CGROUP_ARRAY, -}; - -static int __init register_cgroup_array_map(void) -{ - bpf_register_map_type(&cgroup_array_type); - return 0; -} -late_initcall(register_cgroup_array_map); #endif static struct bpf_map *array_of_map_alloc(union bpf_attr *attr) @@ -644,7 +590,7 @@ static void *array_of_map_lookup_elem(struct bpf_map *map, void *key) return READ_ONCE(*inner_map); } -static const struct bpf_map_ops array_of_map_ops = { +const struct bpf_map_ops array_of_maps_map_ops = { .map_alloc = array_of_map_alloc, .map_free = array_of_map_free, .map_get_next_key = array_map_get_next_key, @@ -653,15 +599,3 @@ static const struct bpf_map_ops array_of_map_ops = { .map_fd_get_ptr = bpf_map_fd_get_ptr, .map_fd_put_ptr = bpf_map_fd_put_ptr, }; - -static struct bpf_map_type_list array_of_map_type __ro_after_init = { - .ops = &array_of_map_ops, - .type = BPF_MAP_TYPE_ARRAY_OF_MAPS, -}; - -static int __init register_array_of_map(void) -{ - bpf_register_map_type(&array_of_map_type); - return 0; -} -late_initcall(register_array_of_map); diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index d5b0623ce87d..bc80c038e430 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -1096,7 +1096,7 @@ static void htab_map_free(struct bpf_map *map) kfree(htab); } -static const struct bpf_map_ops htab_ops = { +const struct bpf_map_ops htab_map_ops = { .map_alloc = htab_map_alloc, .map_free = htab_map_free, .map_get_next_key = htab_map_get_next_key, @@ -1106,12 +1106,7 @@ static const struct bpf_map_ops htab_ops = { .map_gen_lookup = htab_map_gen_lookup, }; -static struct bpf_map_type_list htab_type __ro_after_init = { - .ops = &htab_ops, - .type = BPF_MAP_TYPE_HASH, -}; - -static const struct bpf_map_ops htab_lru_ops = { +const struct bpf_map_ops htab_lru_map_ops = { .map_alloc = htab_map_alloc, .map_free = htab_map_free, .map_get_next_key = htab_map_get_next_key, @@ -1120,11 +1115,6 @@ static const struct bpf_map_ops htab_lru_ops = { .map_delete_elem = htab_lru_map_delete_elem, }; -static struct bpf_map_type_list htab_lru_type __ro_after_init = { - .ops = &htab_lru_ops, - .type = BPF_MAP_TYPE_LRU_HASH, -}; - /* Called from eBPF program */ static void *htab_percpu_map_lookup_elem(struct bpf_map *map, void *key) { @@ -1198,7 +1188,7 @@ int bpf_percpu_hash_update(struct bpf_map *map, void *key, void *value, return ret; } -static const struct bpf_map_ops htab_percpu_ops = { +const struct bpf_map_ops htab_percpu_map_ops = { .map_alloc = htab_map_alloc, .map_free = htab_map_free, .map_get_next_key = htab_map_get_next_key, @@ -1207,12 +1197,7 @@ static const struct bpf_map_ops htab_percpu_ops = { .map_delete_elem = htab_map_delete_elem, }; -static struct bpf_map_type_list htab_percpu_type __ro_after_init = { - .ops = &htab_percpu_ops, - .type = BPF_MAP_TYPE_PERCPU_HASH, -}; - -static const struct bpf_map_ops htab_lru_percpu_ops = { +const struct bpf_map_ops htab_lru_percpu_map_ops = { .map_alloc = htab_map_alloc, .map_free = htab_map_free, .map_get_next_key = htab_map_get_next_key, @@ -1221,11 +1206,6 @@ static const struct bpf_map_ops htab_lru_percpu_ops = { .map_delete_elem = htab_lru_map_delete_elem, }; -static struct bpf_map_type_list htab_lru_percpu_type __ro_after_init = { - .ops = &htab_lru_percpu_ops, - .type = BPF_MAP_TYPE_LRU_PERCPU_HASH, -}; - static struct bpf_map *fd_htab_map_alloc(union bpf_attr *attr) { struct bpf_map *map; @@ -1316,7 +1296,7 @@ static void htab_of_map_free(struct bpf_map *map) fd_htab_map_free(map); } -static const struct bpf_map_ops htab_of_map_ops = { +const struct bpf_map_ops htab_of_maps_map_ops = { .map_alloc = htab_of_map_alloc, .map_free = htab_of_map_free, .map_get_next_key = htab_map_get_next_key, @@ -1325,19 +1305,3 @@ static const struct bpf_map_ops htab_of_map_ops = { .map_fd_get_ptr = bpf_map_fd_get_ptr, .map_fd_put_ptr = bpf_map_fd_put_ptr, }; - -static struct bpf_map_type_list htab_of_map_type __ro_after_init = { - .ops = &htab_of_map_ops, - .type = BPF_MAP_TYPE_HASH_OF_MAPS, -}; - -static int __init register_htab_map(void) -{ - bpf_register_map_type(&htab_type); - bpf_register_map_type(&htab_percpu_type); - bpf_register_map_type(&htab_lru_type); - bpf_register_map_type(&htab_lru_percpu_type); - bpf_register_map_type(&htab_of_map_type); - return 0; -} -late_initcall(register_htab_map); diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c index b37bd9ab7f57..39cfafd895b8 100644 --- a/kernel/bpf/lpm_trie.c +++ b/kernel/bpf/lpm_trie.c @@ -505,7 +505,7 @@ static int trie_get_next_key(struct bpf_map *map, void *key, void *next_key) return -ENOTSUPP; } -static const struct bpf_map_ops trie_ops = { +const struct bpf_map_ops trie_map_ops = { .map_alloc = trie_alloc, .map_free = trie_free, .map_get_next_key = trie_get_next_key, @@ -513,15 +513,3 @@ static const struct bpf_map_ops trie_ops = { .map_update_elem = trie_update_elem, .map_delete_elem = trie_delete_elem, }; - -static struct bpf_map_type_list trie_type __ro_after_init = { - .ops = &trie_ops, - .type = BPF_MAP_TYPE_LPM_TRIE, -}; - -static int __init register_trie_map(void) -{ - bpf_register_map_type(&trie_type); - return 0; -} -late_initcall(register_trie_map); diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index 22aa45cd0324..4dfd6f2ec2f9 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -264,7 +264,7 @@ static void stack_map_free(struct bpf_map *map) put_callchain_buffers(); } -static const struct bpf_map_ops stack_map_ops = { +const struct bpf_map_ops stack_map_ops = { .map_alloc = stack_map_alloc, .map_free = stack_map_free, .map_get_next_key = stack_map_get_next_key, @@ -272,15 +272,3 @@ static const struct bpf_map_ops stack_map_ops = { .map_update_elem = stack_map_update_elem, .map_delete_elem = stack_map_delete_elem, }; - -static struct bpf_map_type_list stack_map_type __ro_after_init = { - .ops = &stack_map_ops, - .type = BPF_MAP_TYPE_STACK_TRACE, -}; - -static int __init register_stack_map(void) -{ - bpf_register_map_type(&stack_map_type); - return 0; -} -late_initcall(register_stack_map); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index ea55691cbf5e..b89288e2b589 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -27,30 +27,29 @@ DEFINE_PER_CPU(int, bpf_prog_active); int sysctl_unprivileged_bpf_disabled __read_mostly; -static LIST_HEAD(bpf_map_types); +static const struct bpf_map_ops * const bpf_map_types[] = { +#define BPF_PROG_TYPE(_id, _ops) +#define BPF_MAP_TYPE(_id, _ops) \ + [_id] = &_ops, +#include +#undef BPF_PROG_TYPE +#undef BPF_MAP_TYPE +}; static struct bpf_map *find_and_alloc_map(union bpf_attr *attr) { - struct bpf_map_type_list *tl; struct bpf_map *map; - list_for_each_entry(tl, &bpf_map_types, list_node) { - if (tl->type == attr->map_type) { - map = tl->ops->map_alloc(attr); - if (IS_ERR(map)) - return map; - map->ops = tl->ops; - map->map_type = attr->map_type; - return map; - } - } - return ERR_PTR(-EINVAL); -} + if (attr->map_type >= ARRAY_SIZE(bpf_map_types) || + !bpf_map_types[attr->map_type]) + return ERR_PTR(-EINVAL); -/* boot time registration of different map implementations */ -void bpf_register_map_type(struct bpf_map_type_list *tl) -{ - list_add(&tl->list_node, &bpf_map_types); + map = bpf_map_types[attr->map_type]->map_alloc(attr); + if (IS_ERR(map)) + return map; + map->ops = bpf_map_types[attr->map_type]; + map->map_type = attr->map_type; + return map; } void *bpf_map_area_alloc(size_t size) @@ -576,8 +575,10 @@ err_put: static const struct bpf_verifier_ops * const bpf_prog_types[] = { #define BPF_PROG_TYPE(_id, _ops) \ [_id] = &_ops, +#define BPF_MAP_TYPE(_id, _ops) #include #undef BPF_PROG_TYPE +#undef BPF_MAP_TYPE }; static int find_prog_type(enum bpf_prog_type type, struct bpf_prog *prog) -- cgit From 942d6984aa0cb3974b99d302b566eaf0b8eccf54 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Tue, 11 Apr 2017 16:11:10 +0200 Subject: s390/qeth: move common ioctl handling to core There's a number of layer-independent ioctls that we can handle in core, and reduce code duplication. For layer-specific ioctls, add a do_ioctl() discipline hook. Signed-off-by: Julian Wiedmann Acked-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 5 ++-- drivers/s390/net/qeth_core_main.c | 63 +++++++++++++++++++++++++++++++++++---- drivers/s390/net/qeth_l2_main.c | 54 ++------------------------------- drivers/s390/net/qeth_l3_main.c | 41 ++----------------------- 4 files changed, 64 insertions(+), 99 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index ed5b3582adba..ee4d43f0c149 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -716,6 +716,7 @@ struct qeth_discipline { int (*freeze)(struct ccwgroup_device *); int (*thaw) (struct ccwgroup_device *); int (*restore)(struct ccwgroup_device *); + int (*do_ioctl)(struct net_device *dev, struct ifreq *rq, int cmd); int (*control_event_handler)(struct qeth_card *card, struct qeth_ipa_cmd *cmd); }; @@ -936,9 +937,6 @@ void qeth_prepare_control_data(struct qeth_card *, int, void qeth_release_buffer(struct qeth_channel *, struct qeth_cmd_buffer *); void qeth_prepare_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *, char); struct qeth_cmd_buffer *qeth_wait_for_buffer(struct qeth_channel *); -int qeth_mdio_read(struct net_device *, int, int); -int qeth_snmp_command(struct qeth_card *, char __user *); -int qeth_query_oat_command(struct qeth_card *, char __user *); int qeth_query_switch_attributes(struct qeth_card *card, struct qeth_switch_info *sw_info); int qeth_send_control_data(struct qeth_card *, int, struct qeth_cmd_buffer *, @@ -956,6 +954,7 @@ int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *, struct sk_buff *, struct qeth_hdr *, int, int, int); int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *, struct sk_buff *, struct qeth_hdr *, int); +int qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); int qeth_core_get_sset_count(struct net_device *, int); void qeth_core_get_ethtool_stats(struct net_device *, struct ethtool_stats *, u64 *); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 6b22b05a6953..c9fdf98e5e15 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4417,7 +4417,7 @@ void qeth_tx_timeout(struct net_device *dev) } EXPORT_SYMBOL_GPL(qeth_tx_timeout); -int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum) +static int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum) { struct qeth_card *card = dev->ml_priv; int rc = 0; @@ -4480,7 +4480,6 @@ int qeth_mdio_read(struct net_device *dev, int phy_id, int regnum) } return rc; } -EXPORT_SYMBOL_GPL(qeth_mdio_read); static int qeth_send_ipa_snmp_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob, int len, @@ -4570,7 +4569,7 @@ static int qeth_snmp_command_cb(struct qeth_card *card, return 0; } -int qeth_snmp_command(struct qeth_card *card, char __user *udata) +static int qeth_snmp_command(struct qeth_card *card, char __user *udata) { struct qeth_cmd_buffer *iob; struct qeth_ipa_cmd *cmd; @@ -4630,7 +4629,6 @@ out: kfree(qinfo.udata); return rc; } -EXPORT_SYMBOL_GPL(qeth_snmp_command); static int qeth_setadpparms_query_oat_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) @@ -4662,7 +4660,7 @@ static int qeth_setadpparms_query_oat_cb(struct qeth_card *card, return 0; } -int qeth_query_oat_command(struct qeth_card *card, char __user *udata) +static int qeth_query_oat_command(struct qeth_card *card, char __user *udata) { int rc = 0; struct qeth_cmd_buffer *iob; @@ -4732,7 +4730,6 @@ out_free: out: return rc; } -EXPORT_SYMBOL_GPL(qeth_query_oat_command); static int qeth_query_card_info_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) @@ -5759,6 +5756,60 @@ static const struct attribute_group *qeth_drv_attr_groups[] = { NULL, }; +int qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct qeth_card *card = dev->ml_priv; + struct mii_ioctl_data *mii_data; + int rc = 0; + + if (!card) + return -ENODEV; + + if (!qeth_card_hw_is_reachable(card)) + return -ENODEV; + + if (card->info.type == QETH_CARD_TYPE_OSN) + return -EPERM; + + switch (cmd) { + case SIOC_QETH_ADP_SET_SNMP_CONTROL: + rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data); + break; + case SIOC_QETH_GET_CARD_TYPE: + if ((card->info.type == QETH_CARD_TYPE_OSD || + card->info.type == QETH_CARD_TYPE_OSM || + card->info.type == QETH_CARD_TYPE_OSX) && + !card->info.guestlan) + return 1; + else + return 0; + case SIOCGMIIPHY: + mii_data = if_mii(rq); + mii_data->phy_id = 0; + break; + case SIOCGMIIREG: + mii_data = if_mii(rq); + if (mii_data->phy_id != 0) + rc = -EINVAL; + else + mii_data->val_out = qeth_mdio_read(dev, + mii_data->phy_id, mii_data->reg_num); + break; + case SIOC_QETH_QUERY_OAT: + rc = qeth_query_oat_command(card, rq->ifr_ifru.ifru_data); + break; + default: + if (card->discipline->do_ioctl) + rc = card->discipline->do_ioctl(dev, rq, cmd); + else + rc = -EOPNOTSUPP; + } + if (rc) + QETH_CARD_TEXT_(card, 2, "ioce%x", rc); + return rc; +} +EXPORT_SYMBOL_GPL(qeth_do_ioctl); + static struct { const char str[ETH_GSTRING_LEN]; } qeth_ethtool_stats_keys[] = { diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index cd0ba9e0d3e5..30232076d867 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -34,56 +33,6 @@ static void qeth_bridge_state_change(struct qeth_card *card, static void qeth_bridge_host_event(struct qeth_card *card, struct qeth_ipa_cmd *cmd); -static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct qeth_card *card = dev->ml_priv; - struct mii_ioctl_data *mii_data; - int rc = 0; - - if (!card) - return -ENODEV; - - if (!qeth_card_hw_is_reachable(card)) - return -ENODEV; - - if (card->info.type == QETH_CARD_TYPE_OSN) - return -EPERM; - - switch (cmd) { - case SIOC_QETH_ADP_SET_SNMP_CONTROL: - rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data); - break; - case SIOC_QETH_GET_CARD_TYPE: - if ((card->info.type == QETH_CARD_TYPE_OSD || - card->info.type == QETH_CARD_TYPE_OSM || - card->info.type == QETH_CARD_TYPE_OSX) && - !card->info.guestlan) - return 1; - return 0; - break; - case SIOCGMIIPHY: - mii_data = if_mii(rq); - mii_data->phy_id = 0; - break; - case SIOCGMIIREG: - mii_data = if_mii(rq); - if (mii_data->phy_id != 0) - rc = -EINVAL; - else - mii_data->val_out = qeth_mdio_read(dev, - mii_data->phy_id, mii_data->reg_num); - break; - case SIOC_QETH_QUERY_OAT: - rc = qeth_query_oat_command(card, rq->ifr_ifru.ifru_data); - break; - default: - rc = -EOPNOTSUPP; - } - if (rc) - QETH_CARD_TEXT_(card, 2, "ioce%d", rc); - return rc; -} - static int qeth_l2_verify_dev(struct net_device *dev) { struct qeth_card *card; @@ -1059,7 +1008,7 @@ static const struct net_device_ops qeth_l2_netdev_ops = { .ndo_start_xmit = qeth_l2_hard_start_xmit, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = qeth_l2_set_rx_mode, - .ndo_do_ioctl = qeth_l2_do_ioctl, + .ndo_do_ioctl = qeth_do_ioctl, .ndo_set_mac_address = qeth_l2_set_mac_address, .ndo_change_mtu = qeth_change_mtu, .ndo_vlan_rx_add_vid = qeth_l2_vlan_rx_add_vid, @@ -1417,6 +1366,7 @@ struct qeth_discipline qeth_l2_discipline = { .freeze = qeth_l2_pm_suspend, .thaw = qeth_l2_pm_resume, .restore = qeth_l2_pm_resume, + .do_ioctl = NULL, .control_event_handler = qeth_l2_control_event, }; EXPORT_SYMBOL_GPL(qeth_l2_discipline); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index c29525838b7c..25b6823af1b2 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -2457,15 +2456,8 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct qeth_card *card = dev->ml_priv; struct qeth_arp_cache_entry arp_entry; - struct mii_ioctl_data *mii_data; int rc = 0; - if (!card) - return -ENODEV; - - if (!qeth_card_hw_is_reachable(card)) - return -ENODEV; - switch (cmd) { case SIOC_QETH_ARP_SET_NO_ENTRIES: if (!capable(CAP_NET_ADMIN)) { @@ -2510,37 +2502,9 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) } rc = qeth_l3_arp_flush_cache(card); break; - case SIOC_QETH_ADP_SET_SNMP_CONTROL: - rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data); - break; - case SIOC_QETH_GET_CARD_TYPE: - if ((card->info.type == QETH_CARD_TYPE_OSD || - card->info.type == QETH_CARD_TYPE_OSX) && - !card->info.guestlan) - return 1; - return 0; - break; - case SIOCGMIIPHY: - mii_data = if_mii(rq); - mii_data->phy_id = 0; - break; - case SIOCGMIIREG: - mii_data = if_mii(rq); - if (mii_data->phy_id != 0) - rc = -EINVAL; - else - mii_data->val_out = qeth_mdio_read(dev, - mii_data->phy_id, - mii_data->reg_num); - break; - case SIOC_QETH_QUERY_OAT: - rc = qeth_query_oat_command(card, rq->ifr_ifru.ifru_data); - break; default: rc = -EOPNOTSUPP; } - if (rc) - QETH_CARD_TEXT_(card, 2, "ioce%d", rc); return rc; } @@ -3056,7 +3020,7 @@ static const struct net_device_ops qeth_l3_netdev_ops = { .ndo_start_xmit = qeth_l3_hard_start_xmit, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = qeth_l3_set_multicast_list, - .ndo_do_ioctl = qeth_l3_do_ioctl, + .ndo_do_ioctl = qeth_do_ioctl, .ndo_change_mtu = qeth_change_mtu, .ndo_fix_features = qeth_fix_features, .ndo_set_features = qeth_set_features, @@ -3072,7 +3036,7 @@ static const struct net_device_ops qeth_l3_osa_netdev_ops = { .ndo_start_xmit = qeth_l3_hard_start_xmit, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = qeth_l3_set_multicast_list, - .ndo_do_ioctl = qeth_l3_do_ioctl, + .ndo_do_ioctl = qeth_do_ioctl, .ndo_change_mtu = qeth_change_mtu, .ndo_fix_features = qeth_fix_features, .ndo_set_features = qeth_set_features, @@ -3439,6 +3403,7 @@ struct qeth_discipline qeth_l3_discipline = { .freeze = qeth_l3_pm_suspend, .thaw = qeth_l3_pm_resume, .restore = qeth_l3_pm_resume, + .do_ioctl = qeth_l3_do_ioctl, .control_event_handler = qeth_l3_control_event, }; EXPORT_SYMBOL_GPL(qeth_l3_discipline); -- cgit From d73ef3249356d78de3fd676bcf52448b950cb0bb Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Tue, 11 Apr 2017 16:11:11 +0200 Subject: s390/qeth: move NAPI poll routine to core Identical code, we just need to call a layer-specific hook to process any received buffer. qeth_buffer_reclaim_work() is shuffled around to avoid a forward declaration for qeth_queue_input_buffer(). Signed-off-by: Julian Wiedmann Acked-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 5 +- drivers/s390/net/qeth_core_main.c | 105 +++++++++++++++++++++++++++++++++----- drivers/s390/net/qeth_l2_main.c | 78 +--------------------------- drivers/s390/net/qeth_l3_main.c | 78 +--------------------------- 4 files changed, 97 insertions(+), 169 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index ee4d43f0c149..196356e11077 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -705,6 +705,7 @@ struct qeth_discipline { void (*start_poll)(struct ccw_device *, int, unsigned long); qdio_handler_t *input_handler; qdio_handler_t *output_handler; + int (*process_rx_buffer)(struct qeth_card *card, int budget, int *done); int (*recover)(void *ptr); int (*setup) (struct ccwgroup_device *); void (*remove) (struct ccwgroup_device *); @@ -909,14 +910,12 @@ int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *, struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *, enum qeth_ipa_cmds, enum qeth_prot_versions); int qeth_query_setadapterparms(struct qeth_card *); -int qeth_check_qdio_errors(struct qeth_card *, struct qdio_buffer *, - unsigned int, const char *); -void qeth_queue_input_buffer(struct qeth_card *, int); struct sk_buff *qeth_core_get_next_skb(struct qeth_card *, struct qeth_qdio_buffer *, struct qdio_buffer_element **, int *, struct qeth_hdr **); void qeth_schedule_recovery(struct qeth_card *); void qeth_qdio_start_poll(struct ccw_device *, int, unsigned long); +int qeth_poll(struct napi_struct *napi, int budget); void qeth_qdio_input_handler(struct ccw_device *, unsigned int, unsigned int, int, int, unsigned long); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index c9fdf98e5e15..b9063eb17436 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3216,8 +3216,10 @@ int qeth_hw_trap(struct qeth_card *card, enum qeth_diags_trap_action action) } EXPORT_SYMBOL_GPL(qeth_hw_trap); -int qeth_check_qdio_errors(struct qeth_card *card, struct qdio_buffer *buf, - unsigned int qdio_error, const char *dbftext) +static int qeth_check_qdio_errors(struct qeth_card *card, + struct qdio_buffer *buf, + unsigned int qdio_error, + const char *dbftext) { if (qdio_error) { QETH_CARD_TEXT(card, 2, dbftext); @@ -3234,18 +3236,8 @@ int qeth_check_qdio_errors(struct qeth_card *card, struct qdio_buffer *buf, } return 0; } -EXPORT_SYMBOL_GPL(qeth_check_qdio_errors); -static void qeth_buffer_reclaim_work(struct work_struct *work) -{ - struct qeth_card *card = container_of(work, struct qeth_card, - buffer_reclaim_work.work); - - QETH_CARD_TEXT_(card, 2, "brw:%x", card->reclaim_index); - qeth_queue_input_buffer(card, card->reclaim_index); -} - -void qeth_queue_input_buffer(struct qeth_card *card, int index) +static void qeth_queue_input_buffer(struct qeth_card *card, int index) { struct qeth_qdio_q *queue = card->qdio.in_q; struct list_head *lh; @@ -3319,7 +3311,15 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index) QDIO_MAX_BUFFERS_PER_Q; } } -EXPORT_SYMBOL_GPL(qeth_queue_input_buffer); + +static void qeth_buffer_reclaim_work(struct work_struct *work) +{ + struct qeth_card *card = container_of(work, struct qeth_card, + buffer_reclaim_work.work); + + QETH_CARD_TEXT_(card, 2, "brw:%x", card->reclaim_index); + qeth_queue_input_buffer(card, card->reclaim_index); +} static void qeth_handle_send_error(struct qeth_card *card, struct qeth_qdio_out_buffer *buffer, unsigned int qdio_err) @@ -5282,6 +5282,83 @@ no_mem: } EXPORT_SYMBOL_GPL(qeth_core_get_next_skb); +int qeth_poll(struct napi_struct *napi, int budget) +{ + struct qeth_card *card = container_of(napi, struct qeth_card, napi); + int work_done = 0; + struct qeth_qdio_buffer *buffer; + int done; + int new_budget = budget; + + if (card->options.performance_stats) { + card->perf_stats.inbound_cnt++; + card->perf_stats.inbound_start_time = qeth_get_micros(); + } + + while (1) { + if (!card->rx.b_count) { + card->rx.qdio_err = 0; + card->rx.b_count = qdio_get_next_buffers( + card->data.ccwdev, 0, &card->rx.b_index, + &card->rx.qdio_err); + if (card->rx.b_count <= 0) { + card->rx.b_count = 0; + break; + } + card->rx.b_element = + &card->qdio.in_q->bufs[card->rx.b_index] + .buffer->element[0]; + card->rx.e_offset = 0; + } + + while (card->rx.b_count) { + buffer = &card->qdio.in_q->bufs[card->rx.b_index]; + if (!(card->rx.qdio_err && + qeth_check_qdio_errors(card, buffer->buffer, + card->rx.qdio_err, "qinerr"))) + work_done += + card->discipline->process_rx_buffer( + card, new_budget, &done); + else + done = 1; + + if (done) { + if (card->options.performance_stats) + card->perf_stats.bufs_rec++; + qeth_put_buffer_pool_entry(card, + buffer->pool_entry); + qeth_queue_input_buffer(card, card->rx.b_index); + card->rx.b_count--; + if (card->rx.b_count) { + card->rx.b_index = + (card->rx.b_index + 1) % + QDIO_MAX_BUFFERS_PER_Q; + card->rx.b_element = + &card->qdio.in_q + ->bufs[card->rx.b_index] + .buffer->element[0]; + card->rx.e_offset = 0; + } + } + + if (work_done >= budget) + goto out; + else + new_budget = budget - work_done; + } + } + + napi_complete(napi); + if (qdio_start_irq(card->data.ccwdev, 0)) + napi_schedule(&card->napi); +out: + if (card->options.performance_stats) + card->perf_stats.inbound_time += qeth_get_micros() - + card->perf_stats.inbound_start_time; + return work_done; +} +EXPORT_SYMBOL_GPL(qeth_poll); + int qeth_setassparms_cb(struct qeth_card *card, struct qeth_reply *reply, unsigned long data) { diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 30232076d867..572347d65c1d 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -500,81 +500,6 @@ static int qeth_l2_process_inbound_buffer(struct qeth_card *card, return work_done; } -static int qeth_l2_poll(struct napi_struct *napi, int budget) -{ - struct qeth_card *card = container_of(napi, struct qeth_card, napi); - int work_done = 0; - struct qeth_qdio_buffer *buffer; - int done; - int new_budget = budget; - - if (card->options.performance_stats) { - card->perf_stats.inbound_cnt++; - card->perf_stats.inbound_start_time = qeth_get_micros(); - } - - while (1) { - if (!card->rx.b_count) { - card->rx.qdio_err = 0; - card->rx.b_count = qdio_get_next_buffers( - card->data.ccwdev, 0, &card->rx.b_index, - &card->rx.qdio_err); - if (card->rx.b_count <= 0) { - card->rx.b_count = 0; - break; - } - card->rx.b_element = - &card->qdio.in_q->bufs[card->rx.b_index] - .buffer->element[0]; - card->rx.e_offset = 0; - } - - while (card->rx.b_count) { - buffer = &card->qdio.in_q->bufs[card->rx.b_index]; - if (!(card->rx.qdio_err && - qeth_check_qdio_errors(card, buffer->buffer, - card->rx.qdio_err, "qinerr"))) - work_done += qeth_l2_process_inbound_buffer( - card, new_budget, &done); - else - done = 1; - - if (done) { - if (card->options.performance_stats) - card->perf_stats.bufs_rec++; - qeth_put_buffer_pool_entry(card, - buffer->pool_entry); - qeth_queue_input_buffer(card, card->rx.b_index); - card->rx.b_count--; - if (card->rx.b_count) { - card->rx.b_index = - (card->rx.b_index + 1) % - QDIO_MAX_BUFFERS_PER_Q; - card->rx.b_element = - &card->qdio.in_q - ->bufs[card->rx.b_index] - .buffer->element[0]; - card->rx.e_offset = 0; - } - } - - if (work_done >= budget) - goto out; - else - new_budget = budget - work_done; - } - } - - napi_complete(napi); - if (qdio_start_irq(card->data.ccwdev, 0)) - napi_schedule(&card->napi); -out: - if (card->options.performance_stats) - card->perf_stats.inbound_time += qeth_get_micros() - - card->perf_stats.inbound_start_time; - return work_done; -} - static int qeth_l2_request_initial_mac(struct qeth_card *card) { int rc = 0; @@ -1065,7 +990,7 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) card->dev->gso_max_size = (QETH_MAX_BUFFER_ELEMENTS(card) - 1) * PAGE_SIZE; SET_NETDEV_DEV(card->dev, &card->gdev->dev); - netif_napi_add(card->dev, &card->napi, qeth_l2_poll, QETH_NAPI_WEIGHT); + netif_napi_add(card->dev, &card->napi, qeth_poll, QETH_NAPI_WEIGHT); netif_carrier_off(card->dev); return register_netdev(card->dev); } @@ -1357,6 +1282,7 @@ struct qeth_discipline qeth_l2_discipline = { .start_poll = qeth_qdio_start_poll, .input_handler = (qdio_handler_t *) qeth_qdio_input_handler, .output_handler = (qdio_handler_t *) qeth_qdio_output_handler, + .process_rx_buffer = qeth_l2_process_inbound_buffer, .recover = qeth_l2_recover, .setup = qeth_l2_probe_device, .remove = qeth_l2_remove_device, diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 25b6823af1b2..bd6ad9be9245 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1829,81 +1829,6 @@ static int qeth_l3_process_inbound_buffer(struct qeth_card *card, return work_done; } -static int qeth_l3_poll(struct napi_struct *napi, int budget) -{ - struct qeth_card *card = container_of(napi, struct qeth_card, napi); - int work_done = 0; - struct qeth_qdio_buffer *buffer; - int done; - int new_budget = budget; - - if (card->options.performance_stats) { - card->perf_stats.inbound_cnt++; - card->perf_stats.inbound_start_time = qeth_get_micros(); - } - - while (1) { - if (!card->rx.b_count) { - card->rx.qdio_err = 0; - card->rx.b_count = qdio_get_next_buffers( - card->data.ccwdev, 0, &card->rx.b_index, - &card->rx.qdio_err); - if (card->rx.b_count <= 0) { - card->rx.b_count = 0; - break; - } - card->rx.b_element = - &card->qdio.in_q->bufs[card->rx.b_index] - .buffer->element[0]; - card->rx.e_offset = 0; - } - - while (card->rx.b_count) { - buffer = &card->qdio.in_q->bufs[card->rx.b_index]; - if (!(card->rx.qdio_err && - qeth_check_qdio_errors(card, buffer->buffer, - card->rx.qdio_err, "qinerr"))) - work_done += qeth_l3_process_inbound_buffer( - card, new_budget, &done); - else - done = 1; - - if (done) { - if (card->options.performance_stats) - card->perf_stats.bufs_rec++; - qeth_put_buffer_pool_entry(card, - buffer->pool_entry); - qeth_queue_input_buffer(card, card->rx.b_index); - card->rx.b_count--; - if (card->rx.b_count) { - card->rx.b_index = - (card->rx.b_index + 1) % - QDIO_MAX_BUFFERS_PER_Q; - card->rx.b_element = - &card->qdio.in_q - ->bufs[card->rx.b_index] - .buffer->element[0]; - card->rx.e_offset = 0; - } - } - - if (work_done >= budget) - goto out; - else - new_budget = budget - work_done; - } - } - - napi_complete(napi); - if (qdio_start_irq(card->data.ccwdev, 0)) - napi_schedule(&card->napi); -out: - if (card->options.performance_stats) - card->perf_stats.inbound_time += qeth_get_micros() - - card->perf_stats.inbound_start_time; - return work_done; -} - static int qeth_l3_verify_vlan_dev(struct net_device *dev, struct qeth_card *card) { @@ -3105,7 +3030,7 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) PAGE_SIZE; SET_NETDEV_DEV(card->dev, &card->gdev->dev); - netif_napi_add(card->dev, &card->napi, qeth_l3_poll, QETH_NAPI_WEIGHT); + netif_napi_add(card->dev, &card->napi, qeth_poll, QETH_NAPI_WEIGHT); netif_carrier_off(card->dev); return register_netdev(card->dev); } @@ -3394,6 +3319,7 @@ struct qeth_discipline qeth_l3_discipline = { .start_poll = qeth_qdio_start_poll, .input_handler = (qdio_handler_t *) qeth_qdio_input_handler, .output_handler = (qdio_handler_t *) qeth_qdio_output_handler, + .process_rx_buffer = qeth_l3_process_inbound_buffer, .recover = qeth_l3_recover, .setup = qeth_l3_probe_device, .remove = qeth_l3_remove_device, -- cgit From 96d1bb53ec8b219abdee883c24799ba68ad4d460 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Tue, 11 Apr 2017 16:11:12 +0200 Subject: s390/qeth: move gdev shutdown handler to core Duplicated code. Signed-off-by: Julian Wiedmann Acked-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 1 - drivers/s390/net/qeth_core_main.c | 8 ++++++-- drivers/s390/net/qeth_l2_main.c | 12 ------------ drivers/s390/net/qeth_l3_main.c | 12 ------------ 4 files changed, 6 insertions(+), 27 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 196356e11077..cc1a4baeba55 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -711,7 +711,6 @@ struct qeth_discipline { void (*remove) (struct ccwgroup_device *); int (*set_online) (struct ccwgroup_device *); int (*set_offline) (struct ccwgroup_device *); - void (*shutdown)(struct ccwgroup_device *); int (*prepare) (struct ccwgroup_device *); void (*complete) (struct ccwgroup_device *); int (*freeze)(struct ccwgroup_device *); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index b9063eb17436..cf5493e525e8 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5749,8 +5749,12 @@ static int qeth_core_set_offline(struct ccwgroup_device *gdev) static void qeth_core_shutdown(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); - if (card->discipline && card->discipline->shutdown) - card->discipline->shutdown(gdev); + qeth_set_allowed_threads(card, 0, 1); + if ((gdev->state == CCWGROUP_ONLINE) && card->info.hwtrap) + qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); + qeth_qdio_clear_card(card, 0); + qeth_clear_qdio_buffers(card); + qdio_free(CARD_DDEV(card)); } static int qeth_core_prepare(struct ccwgroup_device *gdev) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 572347d65c1d..2ad378b57f9f 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1200,17 +1200,6 @@ static void __exit qeth_l2_exit(void) pr_info("unregister layer 2 discipline\n"); } -static void qeth_l2_shutdown(struct ccwgroup_device *gdev) -{ - struct qeth_card *card = dev_get_drvdata(&gdev->dev); - qeth_set_allowed_threads(card, 0, 1); - if ((gdev->state == CCWGROUP_ONLINE) && card->info.hwtrap) - qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); - qeth_qdio_clear_card(card, 0); - qeth_clear_qdio_buffers(card); - qdio_free(CARD_DDEV(card)); -} - static int qeth_l2_pm_suspend(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); @@ -1288,7 +1277,6 @@ struct qeth_discipline qeth_l2_discipline = { .remove = qeth_l2_remove_device, .set_online = qeth_l2_set_online, .set_offline = qeth_l2_set_offline, - .shutdown = qeth_l2_shutdown, .freeze = qeth_l2_pm_suspend, .thaw = qeth_l2_pm_resume, .restore = qeth_l2_pm_resume, diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index bd6ad9be9245..ca21a2065a5d 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -3251,17 +3251,6 @@ static int qeth_l3_recover(void *ptr) return 0; } -static void qeth_l3_shutdown(struct ccwgroup_device *gdev) -{ - struct qeth_card *card = dev_get_drvdata(&gdev->dev); - qeth_set_allowed_threads(card, 0, 1); - if ((gdev->state == CCWGROUP_ONLINE) && card->info.hwtrap) - qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); - qeth_qdio_clear_card(card, 0); - qeth_clear_qdio_buffers(card); - qdio_free(CARD_DDEV(card)); -} - static int qeth_l3_pm_suspend(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); @@ -3325,7 +3314,6 @@ struct qeth_discipline qeth_l3_discipline = { .remove = qeth_l3_remove_device, .set_online = qeth_l3_set_online, .set_offline = qeth_l3_set_offline, - .shutdown = qeth_l3_shutdown, .freeze = qeth_l3_pm_suspend, .thaw = qeth_l3_pm_resume, .restore = qeth_l3_pm_resume, -- cgit From 4e8d7e62560bba9772556ec6e11afcd20868d6a2 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Tue, 11 Apr 2017 16:11:13 +0200 Subject: s390/qeth: remove unused parameter 'elements_needed' is not used in qeth_do_send_packet_fast(), so consequently remove it. Signed-off-by: Julian Wiedmann Acked-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 2 +- drivers/s390/net/qeth_core_main.c | 3 +-- drivers/s390/net/qeth_l2_main.c | 2 +- drivers/s390/net/qeth_l3_main.c | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index cc1a4baeba55..f575c7a566c8 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -949,7 +949,7 @@ int qeth_get_elements_no(struct qeth_card *card, struct sk_buff *skb, int extra_elems, int data_offset); int qeth_get_elements_for_frags(struct sk_buff *); int qeth_do_send_packet_fast(struct qeth_card *, struct qeth_qdio_out_q *, - struct sk_buff *, struct qeth_hdr *, int, int, int); + struct sk_buff *, struct qeth_hdr *, int, int); int qeth_do_send_packet(struct qeth_card *, struct qeth_qdio_out_q *, struct sk_buff *, struct qeth_hdr *, int); int qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index cf5493e525e8..44d15c967c7d 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4024,8 +4024,7 @@ static inline int qeth_fill_buffer(struct qeth_qdio_out_q *queue, int qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue, struct sk_buff *skb, - struct qeth_hdr *hdr, int elements_needed, - int offset, int hd_len) + struct qeth_hdr *hdr, int offset, int hd_len) { struct qeth_qdio_out_buffer *buffer; int index; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 2ad378b57f9f..ab0d065a9ba2 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -783,7 +783,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) elements); } else rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr, - elements, data_offset, hd_len); + data_offset, hd_len); if (!rc) { card->stats.tx_packets++; card->stats.tx_bytes += tx_bytes; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index ca21a2065a5d..7d902b93fd5a 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2810,7 +2810,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) rc = qeth_do_send_packet(card, queue, new_skb, hdr, elements); } else rc = qeth_do_send_packet_fast(card, queue, new_skb, hdr, - elements, data_offset, 0); + data_offset, 0); if (!rc) { card->stats.tx_packets++; -- cgit From e38db6beeed0eac14e21bf3963fdba80043211e3 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Tue, 11 Apr 2017 16:11:14 +0200 Subject: s390/qeth: use correct return type for hard_start_xmit() ndo_start_xmit() expects us to return netdev_tx_t here... Signed-off-by: Julian Wiedmann Acked-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l2_main.c | 3 ++- drivers/s390/net/qeth_l3_main.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index ab0d065a9ba2..11cf4a4b36a6 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -681,7 +681,8 @@ static void qeth_l2_set_rx_mode(struct net_device *dev) qeth_promisc_to_bridge(card); } -static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) { int rc; struct qeth_hdr *hdr = NULL; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 7d902b93fd5a..bea0bae0faf7 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2655,7 +2655,8 @@ static int qeth_l3_get_elements_no_tso(struct qeth_card *card, return elements; } -static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) { int rc; __be16 *tag; -- cgit From 2aedd56af13386fa9edfd5f0178f39b85f5f9d42 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Tue, 11 Apr 2017 16:11:15 +0200 Subject: s390/qeth: use and remove some defines 1. a buffer has 16 is_header flags, because that's its # of elements 2. replace the last occurrence of QETH_HEADER_SIZE, and remove it Signed-off-by: Julian Wiedmann Acked-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 3 +-- drivers/s390/net/qeth_l2_main.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index f575c7a566c8..760f752034e4 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -240,7 +240,6 @@ static inline int qeth_is_ipa_enabled(struct qeth_ipa_info *ipa, #define QETH_TX_TIMEOUT 100 * HZ #define QETH_RCD_TIMEOUT 60 * HZ #define QETH_RECLAIM_WORK_TIME HZ -#define QETH_HEADER_SIZE 32 #define QETH_MAX_PORTNO 15 /*IPv6 address autoconfiguration stuff*/ @@ -447,7 +446,7 @@ struct qeth_qdio_out_buffer { atomic_t state; int next_element_to_fill; struct sk_buff_head skb_list; - int is_header[16]; + int is_header[QDIO_MAX_ELEMENTS_PER_BUFFER]; struct qaob *aob; struct qeth_qdio_out_q *q; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 11cf4a4b36a6..22f8eb007aa1 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -280,7 +280,7 @@ static void qeth_l2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, else hdr->hdr.l2.flags[2] |= QETH_LAYER2_FLAG_UNICAST; - hdr->hdr.l2.pkt_length = skb->len-QETH_HEADER_SIZE; + hdr->hdr.l2.pkt_length = skb->len - sizeof(struct qeth_hdr); /* VSWITCH relies on the VLAN * information to be present in * the QDIO header */ -- cgit From 774afb8e90685d6a12afa35d13a57113d7edebb4 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Tue, 11 Apr 2017 16:11:16 +0200 Subject: s390/qeth: clean up qeth_set_ecmd_adv_sup() In preparation for moving to get_link_ksettings(), clean up how we build the supported and advertised port/speed masks. Signed-off-by: Julian Wiedmann Reviewed-by: Thomas Richter Reviewed-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 57 +++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 44d15c967c7d..41fe9631e24e 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -6024,57 +6024,56 @@ void qeth_core_get_drvinfo(struct net_device *dev, } EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo); -/* Helper function to fill 'advertizing' and 'supported' which are the same. */ -/* Autoneg and full-duplex are supported and advertized uncondionally. */ -/* Always advertize and support all speeds up to specified, and only one */ +/* Helper function to fill 'advertising' and 'supported' which are the same. */ +/* Autoneg and full-duplex are supported and advertised unconditionally. */ +/* Always advertise and support all speeds up to specified, and only one */ /* specified port type. */ static void qeth_set_ecmd_adv_sup(struct ethtool_cmd *ecmd, int maxspeed, int porttype) { - int port_sup, port_adv, spd_sup, spd_adv; + u32 sup, adv; + + sup = SUPPORTED_Autoneg; + adv = ADVERTISED_Autoneg; switch (porttype) { case PORT_TP: - port_sup = SUPPORTED_TP; - port_adv = ADVERTISED_TP; + sup |= SUPPORTED_TP; + adv |= ADVERTISED_TP; break; case PORT_FIBRE: - port_sup = SUPPORTED_FIBRE; - port_adv = ADVERTISED_FIBRE; + sup |= SUPPORTED_FIBRE; + adv |= ADVERTISED_FIBRE; break; default: - port_sup = SUPPORTED_TP; - port_adv = ADVERTISED_TP; + sup |= SUPPORTED_TP; + adv |= ADVERTISED_TP; WARN_ON_ONCE(1); } - /* "Fallthrough" case'es ordered from high to low result in setting */ - /* flags cumulatively, starting from the specified speed and down to */ - /* the lowest possible. */ - spd_sup = 0; - spd_adv = 0; + /* fallthrough from high to low, to select all legal speeds: */ switch (maxspeed) { case SPEED_10000: - spd_sup |= SUPPORTED_10000baseT_Full; - spd_adv |= ADVERTISED_10000baseT_Full; + sup |= SUPPORTED_10000baseT_Full; + adv |= ADVERTISED_10000baseT_Full; case SPEED_1000: - spd_sup |= SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full; - spd_adv |= ADVERTISED_1000baseT_Half | - ADVERTISED_1000baseT_Full; + sup |= SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full; + adv |= ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full; case SPEED_100: - spd_sup |= SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; - spd_adv |= ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full; + sup |= SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; + adv |= ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full; case SPEED_10: - spd_sup |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; - spd_adv |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full; - break; + sup |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; + adv |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full; + /* end fallthrough */ + break; default: - spd_sup = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; - spd_adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full; + sup |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; + adv |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full; WARN_ON_ONCE(1); } - ecmd->advertising = ADVERTISED_Autoneg | port_adv | spd_adv; - ecmd->supported = SUPPORTED_Autoneg | port_sup | spd_sup; + ecmd->supported = sup; + ecmd->advertising = adv; } int qeth_core_ethtool_get_settings(struct net_device *netdev, -- cgit From 993e19c0aeaaf4ad21e93a09110a782f4ef0c4d5 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Tue, 11 Apr 2017 16:11:17 +0200 Subject: s390/qeth: convert to ETHTOOL_GLINKSETTINGS API get_settings() is deprecated and lacks support for higher link speeds, so implement get_link_ksettings() instead. Signed-off-by: Julian Wiedmann Reviewed-by: Thomas Richter Reviewed-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 3 +- drivers/s390/net/qeth_core_main.c | 75 +++++++++++++++++++-------------------- drivers/s390/net/qeth_l2_main.c | 2 +- drivers/s390/net/qeth_l3_main.c | 2 +- 4 files changed, 40 insertions(+), 42 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 760f752034e4..a634ec2a08f3 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -958,7 +958,8 @@ void qeth_core_get_ethtool_stats(struct net_device *, void qeth_core_get_strings(struct net_device *, u32, u8 *); void qeth_core_get_drvinfo(struct net_device *, struct ethtool_drvinfo *); void qeth_dbf_longtext(debug_info_t *id, int level, char *text, ...); -int qeth_core_ethtool_get_settings(struct net_device *, struct ethtool_cmd *); +int qeth_core_ethtool_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd); int qeth_set_access_ctrl_online(struct qeth_card *card, int fallback); int qeth_hdr_chk_and_bounce(struct sk_buff *, struct qeth_hdr **, int); int qeth_configure_cq(struct qeth_card *, enum qeth_cq); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 41fe9631e24e..4304bc2a6620 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -6028,7 +6028,7 @@ EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo); /* Autoneg and full-duplex are supported and advertised unconditionally. */ /* Always advertise and support all speeds up to specified, and only one */ /* specified port type. */ -static void qeth_set_ecmd_adv_sup(struct ethtool_cmd *ecmd, +static void qeth_set_cmd_adv_sup(struct ethtool_link_ksettings *cmd, int maxspeed, int porttype) { u32 sup, adv; @@ -6072,55 +6072,53 @@ static void qeth_set_ecmd_adv_sup(struct ethtool_cmd *ecmd, adv |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full; WARN_ON_ONCE(1); } - ecmd->supported = sup; - ecmd->advertising = adv; + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + sup); + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, + adv); } -int qeth_core_ethtool_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +int qeth_core_ethtool_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct qeth_card *card = netdev->ml_priv; enum qeth_link_types link_type; struct carrier_info carrier_info; int rc; - u32 speed; if ((card->info.type == QETH_CARD_TYPE_IQD) || (card->info.guestlan)) link_type = QETH_LINK_TYPE_10GBIT_ETH; else link_type = card->info.link_type; - ecmd->transceiver = XCVR_INTERNAL; - ecmd->duplex = DUPLEX_FULL; - ecmd->autoneg = AUTONEG_ENABLE; + cmd->base.duplex = DUPLEX_FULL; + cmd->base.autoneg = AUTONEG_ENABLE; + cmd->base.phy_address = 0; + cmd->base.mdio_support = 0; + cmd->base.eth_tp_mdix = ETH_TP_MDI_INVALID; + cmd->base.eth_tp_mdix_ctrl = ETH_TP_MDI_INVALID; switch (link_type) { case QETH_LINK_TYPE_FAST_ETH: case QETH_LINK_TYPE_LANE_ETH100: - qeth_set_ecmd_adv_sup(ecmd, SPEED_100, PORT_TP); - speed = SPEED_100; - ecmd->port = PORT_TP; + cmd->base.speed = SPEED_100; + cmd->base.port = PORT_TP; break; - case QETH_LINK_TYPE_GBIT_ETH: case QETH_LINK_TYPE_LANE_ETH1000: - qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_FIBRE); - speed = SPEED_1000; - ecmd->port = PORT_FIBRE; + cmd->base.speed = SPEED_1000; + cmd->base.port = PORT_FIBRE; break; - case QETH_LINK_TYPE_10GBIT_ETH: - qeth_set_ecmd_adv_sup(ecmd, SPEED_10000, PORT_FIBRE); - speed = SPEED_10000; - ecmd->port = PORT_FIBRE; + cmd->base.speed = SPEED_10000; + cmd->base.port = PORT_FIBRE; break; - default: - qeth_set_ecmd_adv_sup(ecmd, SPEED_10, PORT_TP); - speed = SPEED_10; - ecmd->port = PORT_TP; + cmd->base.speed = SPEED_10; + cmd->base.port = PORT_TP; } - ethtool_cmd_speed_set(ecmd, speed); + qeth_set_cmd_adv_sup(cmd, cmd->base.speed, cmd->base.port); /* Check if we can obtain more accurate information. */ /* If QUERY_CARD_INFO command is not supported or fails, */ @@ -6145,49 +6143,48 @@ int qeth_core_ethtool_get_settings(struct net_device *netdev, switch (carrier_info.card_type) { case CARD_INFO_TYPE_1G_COPPER_A: case CARD_INFO_TYPE_1G_COPPER_B: - qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_TP); - ecmd->port = PORT_TP; + cmd->base.port = PORT_TP; + qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port); break; case CARD_INFO_TYPE_1G_FIBRE_A: case CARD_INFO_TYPE_1G_FIBRE_B: - qeth_set_ecmd_adv_sup(ecmd, SPEED_1000, PORT_FIBRE); - ecmd->port = PORT_FIBRE; + cmd->base.port = PORT_FIBRE; + qeth_set_cmd_adv_sup(cmd, SPEED_1000, cmd->base.port); break; case CARD_INFO_TYPE_10G_FIBRE_A: case CARD_INFO_TYPE_10G_FIBRE_B: - qeth_set_ecmd_adv_sup(ecmd, SPEED_10000, PORT_FIBRE); - ecmd->port = PORT_FIBRE; + cmd->base.port = PORT_FIBRE; + qeth_set_cmd_adv_sup(cmd, SPEED_10000, cmd->base.port); break; } switch (carrier_info.port_mode) { case CARD_INFO_PORTM_FULLDUPLEX: - ecmd->duplex = DUPLEX_FULL; + cmd->base.duplex = DUPLEX_FULL; break; case CARD_INFO_PORTM_HALFDUPLEX: - ecmd->duplex = DUPLEX_HALF; + cmd->base.duplex = DUPLEX_HALF; break; } switch (carrier_info.port_speed) { case CARD_INFO_PORTS_10M: - speed = SPEED_10; + cmd->base.speed = SPEED_10; break; case CARD_INFO_PORTS_100M: - speed = SPEED_100; + cmd->base.speed = SPEED_100; break; case CARD_INFO_PORTS_1G: - speed = SPEED_1000; + cmd->base.speed = SPEED_1000; break; case CARD_INFO_PORTS_10G: - speed = SPEED_10000; + cmd->base.speed = SPEED_10000; break; } - ethtool_cmd_speed_set(ecmd, speed); return 0; } -EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_settings); +EXPORT_SYMBOL_GPL(qeth_core_ethtool_get_link_ksettings); /* Callback to handle checksum offload command reply from OSA card. * Verify that required features have been enabled on the card. diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index 22f8eb007aa1..1b07f382d74c 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -917,7 +917,7 @@ static const struct ethtool_ops qeth_l2_ethtool_ops = { .get_ethtool_stats = qeth_core_get_ethtool_stats, .get_sset_count = qeth_core_get_sset_count, .get_drvinfo = qeth_core_get_drvinfo, - .get_settings = qeth_core_ethtool_get_settings, + .get_link_ksettings = qeth_core_ethtool_get_link_ksettings, }; static const struct ethtool_ops qeth_l2_osn_ops = { diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index bea0bae0faf7..6e0354ef4b86 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2912,7 +2912,7 @@ static const struct ethtool_ops qeth_l3_ethtool_ops = { .get_ethtool_stats = qeth_core_get_ethtool_stats, .get_sset_count = qeth_core_get_sset_count, .get_drvinfo = qeth_core_get_drvinfo, - .get_settings = qeth_core_ethtool_get_settings, + .get_link_ksettings = qeth_core_ethtool_get_link_ksettings, }; /* -- cgit From 41fc3b6567a5b072b45f654fab767457a740c637 Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Tue, 11 Apr 2017 16:11:18 +0200 Subject: s390/qeth: use LINK_MODE_* to report the link characteristics LINK_MODE_* replaces the u32-limited SUPPORTED_* / ENABLED_* definitions. Signed-off-by: Julian Wiedmann Reviewed-by: Thomas Richter Reviewed-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core_main.c | 71 ++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 4304bc2a6620..88f4c6cff9a4 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -6031,52 +6031,75 @@ EXPORT_SYMBOL_GPL(qeth_core_get_drvinfo); static void qeth_set_cmd_adv_sup(struct ethtool_link_ksettings *cmd, int maxspeed, int porttype) { - u32 sup, adv; + ethtool_link_ksettings_zero_link_mode(cmd, supported); + ethtool_link_ksettings_zero_link_mode(cmd, advertising); + ethtool_link_ksettings_zero_link_mode(cmd, lp_advertising); - sup = SUPPORTED_Autoneg; - adv = ADVERTISED_Autoneg; + ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg); + ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg); switch (porttype) { case PORT_TP: - sup |= SUPPORTED_TP; - adv |= ADVERTISED_TP; + ethtool_link_ksettings_add_link_mode(cmd, supported, TP); + ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); break; case PORT_FIBRE: - sup |= SUPPORTED_FIBRE; - adv |= ADVERTISED_FIBRE; + ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE); + ethtool_link_ksettings_add_link_mode(cmd, advertising, FIBRE); break; default: - sup |= SUPPORTED_TP; - adv |= ADVERTISED_TP; + ethtool_link_ksettings_add_link_mode(cmd, supported, TP); + ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); WARN_ON_ONCE(1); } /* fallthrough from high to low, to select all legal speeds: */ switch (maxspeed) { case SPEED_10000: - sup |= SUPPORTED_10000baseT_Full; - adv |= ADVERTISED_10000baseT_Full; + ethtool_link_ksettings_add_link_mode(cmd, supported, + 10000baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 10000baseT_Full); case SPEED_1000: - sup |= SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full; - adv |= ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full; + ethtool_link_ksettings_add_link_mode(cmd, supported, + 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, supported, + 1000baseT_Half); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 1000baseT_Half); case SPEED_100: - sup |= SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; - adv |= ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full; + ethtool_link_ksettings_add_link_mode(cmd, supported, + 100baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 100baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, supported, + 100baseT_Half); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 100baseT_Half); case SPEED_10: - sup |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; - adv |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full; + ethtool_link_ksettings_add_link_mode(cmd, supported, + 10baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 10baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, supported, + 10baseT_Half); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 10baseT_Half); /* end fallthrough */ break; default: - sup |= SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full; - adv |= ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full; + ethtool_link_ksettings_add_link_mode(cmd, supported, + 10baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 10baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, supported, + 10baseT_Half); + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 10baseT_Half); WARN_ON_ONCE(1); } - - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, - sup); - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, - adv); } int qeth_core_ethtool_get_link_ksettings(struct net_device *netdev, -- cgit From 6ffa4d1bbe461b7b88e02feca98282a46cb6c33b Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Tue, 11 Apr 2017 16:11:19 +0200 Subject: s390/qeth: remove unimplemented gdev routines prepare() and complete() are not implemented by any discipline, so just drop all the indirection. Signed-off-by: Julian Wiedmann Reviewed-by: Hans Wippel Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- drivers/s390/net/qeth_core.h | 2 -- drivers/s390/net/qeth_core_main.c | 19 ++----------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index a634ec2a08f3..f6aa21176d89 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -710,8 +710,6 @@ struct qeth_discipline { void (*remove) (struct ccwgroup_device *); int (*set_online) (struct ccwgroup_device *); int (*set_offline) (struct ccwgroup_device *); - int (*prepare) (struct ccwgroup_device *); - void (*complete) (struct ccwgroup_device *); int (*freeze)(struct ccwgroup_device *); int (*thaw) (struct ccwgroup_device *); int (*restore)(struct ccwgroup_device *); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 88f4c6cff9a4..38114a8d56e0 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5756,21 +5756,6 @@ static void qeth_core_shutdown(struct ccwgroup_device *gdev) qdio_free(CARD_DDEV(card)); } -static int qeth_core_prepare(struct ccwgroup_device *gdev) -{ - struct qeth_card *card = dev_get_drvdata(&gdev->dev); - if (card->discipline && card->discipline->prepare) - return card->discipline->prepare(gdev); - return 0; -} - -static void qeth_core_complete(struct ccwgroup_device *gdev) -{ - struct qeth_card *card = dev_get_drvdata(&gdev->dev); - if (card->discipline && card->discipline->complete) - card->discipline->complete(gdev); -} - static int qeth_core_freeze(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); @@ -5805,8 +5790,8 @@ static struct ccwgroup_driver qeth_core_ccwgroup_driver = { .set_online = qeth_core_set_online, .set_offline = qeth_core_set_offline, .shutdown = qeth_core_shutdown, - .prepare = qeth_core_prepare, - .complete = qeth_core_complete, + .prepare = NULL, + .complete = NULL, .freeze = qeth_core_freeze, .thaw = qeth_core_thaw, .restore = qeth_core_restore, -- cgit From cb6bf9cfdb5e26dfb7eb9b955b2f24d82703e27d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 11 Apr 2017 16:02:02 +0000 Subject: devlink: fix return value check in devlink_dpipe_header_put() Fix the return value check which testing the wrong variable in devlink_dpipe_header_put(). Fixes: 1555d204e743 ("devlink: Support for pipeline debug (dpipe)") Signed-off-by: Wei Yongjun Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- net/core/devlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/devlink.c b/net/core/devlink.c index 24b766003a61..0afac5800b57 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -2031,7 +2031,7 @@ static int devlink_dpipe_header_put(struct sk_buff *skb, int err; header_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_HEADER); - if (!header) + if (!header_attr) return -EMSGSIZE; if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_HEADER_NAME, header->name) || -- cgit From 8f917bba0042f1e3b7693743fbe9782709e936e7 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 11 Apr 2017 14:08:08 -0400 Subject: bpf: pass sk to helper functions BPF helper functions access socket fields through skb->sk. This is not set in ingress cgroup and socket filters. The association is only made in skb_set_owner_r once the filter has accepted the packet. Sk is available as socket lookup has taken place. Temporarily set skb->sk to sk in these cases. Signed-off-by: Willem de Bruijn Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/cgroup.c | 5 ++++- net/core/filter.c | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index da0f53690295..ea6033cba947 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -154,7 +154,7 @@ int __cgroup_bpf_update(struct cgroup *cgrp, struct cgroup *parent, /** * __cgroup_bpf_run_filter_skb() - Run a program for packet filtering - * @sk: The socken sending or receiving traffic + * @sk: The socket sending or receiving traffic * @skb: The skb that is being sent or received * @type: The type of program to be exectuted * @@ -189,10 +189,13 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk, prog = rcu_dereference(cgrp->bpf.effective[type]); if (prog) { unsigned int offset = skb->data - skb_network_header(skb); + struct sock *save_sk = skb->sk; + skb->sk = sk; __skb_push(skb, offset); ret = bpf_prog_run_save_cb(prog, skb) == 1 ? 0 : -EPERM; __skb_pull(skb, offset); + skb->sk = save_sk; } rcu_read_unlock(); diff --git a/net/core/filter.c b/net/core/filter.c index bbe0cf415105..ce2a19da8aa4 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -92,8 +92,13 @@ int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap) rcu_read_lock(); filter = rcu_dereference(sk->sk_filter); if (filter) { - unsigned int pkt_len = bpf_prog_run_save_cb(filter->prog, skb); + struct sock *save_sk = skb->sk; + unsigned int pkt_len; + + skb->sk = sk; + pkt_len = bpf_prog_run_save_cb(filter->prog, skb); err = pkt_len ? pskb_trim(skb, max(cap, pkt_len)) : -EPERM; + skb->sk = save_sk; } rcu_read_unlock(); -- cgit From c02b7a9145512e9995e875faf8c9df4147870bbf Mon Sep 17 00:00:00 2001 From: Joao Pinto Date: Mon, 10 Apr 2017 11:32:14 +0100 Subject: net: stmmac: use netif_set_real_num_{rx,tx}_queues In the submission of the lastest multiple buffer patch set, this fix was lost. I am sending this patch to put it right again. The fix was originally proposed by Arnd Bergmann. Signed-off-by: Arnd Bergmann Signed-off-by: Joao Pinto Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index a89f76b27ea2..85f315e01c1d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -4103,8 +4103,8 @@ int stmmac_dvr_probe(struct device *device, goto error_hw_init; /* Configure real RX and TX queues */ - ndev->real_num_rx_queues = priv->plat->rx_queues_to_use; - ndev->real_num_tx_queues = priv->plat->tx_queues_to_use; + netif_set_real_num_rx_queues(ndev, priv->plat->rx_queues_to_use); + netif_set_real_num_tx_queues(ndev, priv->plat->tx_queues_to_use); ndev->netdev_ops = &stmmac_netdev_ops; -- cgit From 3c22e8f3200227d64fc4b1795b12b2cdc9099cd6 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Mon, 10 Apr 2017 14:57:56 +0200 Subject: net/smc: get rid of old comment This patch removes an outdated comment. Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- net/smc/smc_cdc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c index 5a339493872e..4c9c2f6cd021 100644 --- a/net/smc/smc_cdc.c +++ b/net/smc/smc_cdc.c @@ -228,8 +228,6 @@ static void smc_cdc_msg_recv_action(struct smc_sock *smc, smc_close_wake_tx_prepared(smc); } - /* subsequent patch: trigger socket release if connection closed */ - /* socket connected but not accepted */ if (!smc->sk.sk_socket) return; -- cgit From 249633a443fd12485d6f352d6cbe41efc92db233 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Mon, 10 Apr 2017 14:57:57 +0200 Subject: net/smc: remove useless smc_ib_devices_list check The global event handler is created only, if the ib_device has already been used by at least one link group. It is guaranteed that there exists the corresponding entry in the smc_ib_devices list. Get rid of this superfluous check. Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- net/smc/smc_ib.c | 2 -- net/smc/smc_pnet.c | 2 +- net/smc/smc_pnet.h | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/net/smc/smc_ib.c b/net/smc/smc_ib.c index e6743c008ac5..b76257798ba5 100644 --- a/net/smc/smc_ib.c +++ b/net/smc/smc_ib.c @@ -179,8 +179,6 @@ static void smc_ib_global_event_handler(struct ib_event_handler *handler, u8 port_idx; smcibdev = container_of(handler, struct smc_ib_device, event_handler); - if (!smc_pnet_find_ib(smcibdev->ibdev->name)) - return; switch (ibevent->event) { case IB_EVENT_PORT_ERR: diff --git a/net/smc/smc_pnet.c b/net/smc/smc_pnet.c index 9d3e7fb8348d..f1cff0159980 100644 --- a/net/smc/smc_pnet.c +++ b/net/smc/smc_pnet.c @@ -219,7 +219,7 @@ static bool smc_pnetid_valid(const char *pnet_name, char *pnetid) } /* Find an infiniband device by a given name. The device might not exist. */ -struct smc_ib_device *smc_pnet_find_ib(char *ib_name) +static struct smc_ib_device *smc_pnet_find_ib(char *ib_name) { struct smc_ib_device *ibdev; diff --git a/net/smc/smc_pnet.h b/net/smc/smc_pnet.h index 32ab3df928ca..c4f1bccd4358 100644 --- a/net/smc/smc_pnet.h +++ b/net/smc/smc_pnet.h @@ -16,7 +16,6 @@ struct smc_ib_device; int smc_pnet_init(void) __init; void smc_pnet_exit(void); int smc_pnet_remove_by_ibdev(struct smc_ib_device *ibdev); -struct smc_ib_device *smc_pnet_find_ib(char *ib_name); void smc_pnet_find_roce_resource(struct sock *sk, struct smc_ib_device **smcibdev, u8 *ibport); -- cgit From 5da7e4d35507992da2af6c89434c33a69802de9c Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Mon, 10 Apr 2017 14:57:58 +0200 Subject: net/smc: return active RoCE port only SMC requires an active ib port on the RoCE device. smc_pnet_find_roce_resource() determines the matching RoCE device port according to the configured PNET table. Do not return the found RoCE device port, if it is not flagged active. Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- net/smc/smc_pnet.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/smc/smc_pnet.c b/net/smc/smc_pnet.c index f1cff0159980..78f7af28ae4f 100644 --- a/net/smc/smc_pnet.c +++ b/net/smc/smc_pnet.c @@ -523,8 +523,11 @@ void smc_pnet_find_roce_resource(struct sock *sk, read_lock(&smc_pnettable.lock); list_for_each_entry(pnetelem, &smc_pnettable.pnetlist, list) { if (dst->dev == pnetelem->ndev) { - *smcibdev = pnetelem->smcibdev; - *ibport = pnetelem->ib_port; + if (smc_ib_port_active(pnetelem->smcibdev, + pnetelem->ib_port)) { + *smcibdev = pnetelem->smcibdev; + *ibport = pnetelem->ib_port; + } break; } } -- cgit From 90cacb2ea60c37b9a413a2d7dcb244c3d397c060 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Mon, 10 Apr 2017 14:57:59 +0200 Subject: net/smc: guarantee reset of write_blocked for heavy workload If peer indicates write_blocked, the cursor state of the received data should be send to the peer immediately (in smc_tx_consumer_update()). Afterwards the write_blocked indicator is cleared. If there is no free slot for another write request, sending is postponed to worker smc_tx_work, and the write_blocked indicator is not cleared. Therefore another clearing check is needed in smc_tx_work(). Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- net/smc/smc_tx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/smc/smc_tx.c b/net/smc/smc_tx.c index 69a0013dd25c..21ec1832ab51 100644 --- a/net/smc/smc_tx.c +++ b/net/smc/smc_tx.c @@ -431,9 +431,13 @@ static void smc_tx_work(struct work_struct *work) struct smc_connection, tx_work); struct smc_sock *smc = container_of(conn, struct smc_sock, conn); + int rc; lock_sock(&smc->sk); - smc_tx_sndbuf_nonempty(conn); + rc = smc_tx_sndbuf_nonempty(conn); + if (!rc && conn->local_rx_ctrl.prod_flags.write_blocked && + !atomic_read(&conn->bytes_to_rcv)) + conn->local_rx_ctrl.prod_flags.write_blocked = 0; release_sock(&smc->sk); } -- cgit From 90e9517ed9615f9fb23fbef7d279f81e6c5c08b2 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Mon, 10 Apr 2017 14:58:00 +0200 Subject: net/smc: always call the POLL_IN part of sk_wake_async Wake up reading file descriptors for a closing socket as well, otherwise some socket applications may stall. Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- net/smc/smc_rx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c index c4ef9a4ec569..f0c8b089f770 100644 --- a/net/smc/smc_rx.c +++ b/net/smc/smc_rx.c @@ -36,11 +36,10 @@ static void smc_rx_data_ready(struct sock *sk) if (skwq_has_sleeper(wq)) wake_up_interruptible_sync_poll(&wq->wait, POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND); + sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN); if ((sk->sk_shutdown == SHUTDOWN_MASK) || (sk->sk_state == SMC_CLOSED)) sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_HUP); - else - sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN); rcu_read_unlock(); } -- cgit From 46c28dbd4c23c3f7fa37f5ea48772af79c9cc40e Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Mon, 10 Apr 2017 14:58:01 +0200 Subject: net/smc: no socket state changes in tasklet context Several state changes occur during SMC socket closing. Currently state changes triggered locally occur in process context with lock_sock() taken while state changes triggered by peer occur in tasklet context with bh_lock_sock() taken. bh_lock_sock() does not wait till a lock_sock(() task in process context is finished. This may lead to races in socket state transitions resulting in dangling SMC-sockets, or it may lead to duplicate SMC socket freeing. This patch introduces a closing worker to run all state changes under lock_sock(). Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Reported-by: Dave Jones Signed-off-by: David S. Miller --- net/smc/af_smc.c | 8 ++++++-- net/smc/smc.h | 1 + net/smc/smc_cdc.c | 9 +++++++-- net/smc/smc_close.c | 39 +++++++++++++++++++++++++-------------- net/smc/smc_close.h | 2 +- net/smc/smc_core.c | 2 +- 6 files changed, 41 insertions(+), 20 deletions(-) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 093803786eac..3b7eda6c27de 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -451,6 +451,9 @@ static int smc_connect_rdma(struct smc_sock *smc) goto decline_rdma_unlock; } + smc_close_init(smc); + smc_rx_init(smc); + if (local_contact == SMC_FIRST_CONTACT) { rc = smc_ib_ready_link(link); if (rc) { @@ -477,7 +480,6 @@ static int smc_connect_rdma(struct smc_sock *smc) mutex_unlock(&smc_create_lgr_pending); smc_tx_init(smc); - smc_rx_init(smc); out_connected: smc_copy_sock_settings_to_clc(smc); @@ -800,6 +802,9 @@ static void smc_listen_work(struct work_struct *work) goto decline_rdma; } + smc_close_init(new_smc); + smc_rx_init(new_smc); + rc = smc_clc_send_accept(new_smc, local_contact); if (rc) goto out_err; @@ -839,7 +844,6 @@ static void smc_listen_work(struct work_struct *work) } smc_tx_init(new_smc); - smc_rx_init(new_smc); out_connected: sk_refcnt_debug_inc(newsmcsk); diff --git a/net/smc/smc.h b/net/smc/smc.h index ee5fbea24549..6e44313e4467 100644 --- a/net/smc/smc.h +++ b/net/smc/smc.h @@ -164,6 +164,7 @@ struct smc_connection { #ifndef KERNEL_HAS_ATOMIC64 spinlock_t acurs_lock; /* protect cursors */ #endif + struct work_struct close_work; /* peer sent some closing */ }; struct smc_sock { /* smc sock container */ diff --git a/net/smc/smc_cdc.c b/net/smc/smc_cdc.c index 4c9c2f6cd021..a7294edbc221 100644 --- a/net/smc/smc_cdc.c +++ b/net/smc/smc_cdc.c @@ -217,8 +217,13 @@ static void smc_cdc_msg_recv_action(struct smc_sock *smc, smc->sk.sk_err = ECONNRESET; conn->local_tx_ctrl.conn_state_flags.peer_conn_abort = 1; } - if (smc_cdc_rxed_any_close_or_senddone(conn)) - smc_close_passive_received(smc); + if (smc_cdc_rxed_any_close_or_senddone(conn)) { + smc->sk.sk_shutdown |= RCV_SHUTDOWN; + if (smc->clcsock && smc->clcsock->sk) + smc->clcsock->sk->sk_shutdown |= RCV_SHUTDOWN; + sock_set_flag(&smc->sk, SOCK_DONE); + schedule_work(&conn->close_work); + } /* piggy backed tx info */ /* trigger sndbuf consumer: RDMA write into peer RMBE and CDC */ diff --git a/net/smc/smc_close.c b/net/smc/smc_close.c index 67a71d170bed..388503b78228 100644 --- a/net/smc/smc_close.c +++ b/net/smc/smc_close.c @@ -117,7 +117,6 @@ void smc_close_active_abort(struct smc_sock *smc) struct smc_cdc_conn_state_flags *txflags = &smc->conn.local_tx_ctrl.conn_state_flags; - bh_lock_sock(&smc->sk); smc->sk.sk_err = ECONNABORTED; if (smc->clcsock && smc->clcsock->sk) { smc->clcsock->sk->sk_err = ECONNABORTED; @@ -125,6 +124,7 @@ void smc_close_active_abort(struct smc_sock *smc) } switch (smc->sk.sk_state) { case SMC_INIT: + case SMC_ACTIVE: smc->sk.sk_state = SMC_PEERABORTWAIT; break; case SMC_APPCLOSEWAIT1: @@ -161,7 +161,6 @@ void smc_close_active_abort(struct smc_sock *smc) } sock_set_flag(&smc->sk, SOCK_DEAD); - bh_unlock_sock(&smc->sk); smc->sk.sk_state_change(&smc->sk); } @@ -185,7 +184,7 @@ again: case SMC_INIT: sk->sk_state = SMC_CLOSED; if (smc->smc_listen_work.func) - flush_work(&smc->smc_listen_work); + cancel_work_sync(&smc->smc_listen_work); sock_put(sk); break; case SMC_LISTEN: @@ -198,7 +197,7 @@ again: } release_sock(sk); smc_close_cleanup_listen(sk); - flush_work(&smc->tcp_listen_work); + cancel_work_sync(&smc->smc_listen_work); lock_sock(sk); break; case SMC_ACTIVE: @@ -306,22 +305,27 @@ static void smc_close_passive_abort_received(struct smc_sock *smc) /* Some kind of closing has been received: peer_conn_closed, peer_conn_abort, * or peer_done_writing. - * Called under tasklet context. */ -void smc_close_passive_received(struct smc_sock *smc) +static void smc_close_passive_work(struct work_struct *work) { - struct smc_cdc_conn_state_flags *rxflags = - &smc->conn.local_rx_ctrl.conn_state_flags; + struct smc_connection *conn = container_of(work, + struct smc_connection, + close_work); + struct smc_sock *smc = container_of(conn, struct smc_sock, conn); + struct smc_cdc_conn_state_flags *rxflags; struct sock *sk = &smc->sk; int old_state; - sk->sk_shutdown |= RCV_SHUTDOWN; - if (smc->clcsock && smc->clcsock->sk) - smc->clcsock->sk->sk_shutdown |= RCV_SHUTDOWN; - sock_set_flag(&smc->sk, SOCK_DONE); - + lock_sock(&smc->sk); old_state = sk->sk_state; + if (!conn->alert_token_local) { + /* abnormal termination */ + smc_close_active_abort(smc); + goto wakeup; + } + + rxflags = &smc->conn.local_rx_ctrl.conn_state_flags; if (rxflags->peer_conn_abort) { smc_close_passive_abort_received(smc); goto wakeup; @@ -373,11 +377,12 @@ wakeup: sk->sk_write_space(sk); /* wakeup blocked sndbuf producers */ if ((sk->sk_state == SMC_CLOSED) && - (sock_flag(sk, SOCK_DEAD) || (old_state == SMC_INIT))) { + (sock_flag(sk, SOCK_DEAD) || !sk->sk_socket)) { smc_conn_free(&smc->conn); schedule_delayed_work(&smc->sock_put_work, SMC_CLOSE_SOCK_PUT_DELAY); } + release_sock(&smc->sk); } void smc_close_sock_put_work(struct work_struct *work) @@ -442,3 +447,9 @@ again: sk->sk_state_change(&smc->sk); return rc; } + +/* Initialize close properties on connection establishment. */ +void smc_close_init(struct smc_sock *smc) +{ + INIT_WORK(&smc->conn.close_work, smc_close_passive_work); +} diff --git a/net/smc/smc_close.h b/net/smc/smc_close.h index bc9a2df3633c..4a3d99a8d7cb 100644 --- a/net/smc/smc_close.h +++ b/net/smc/smc_close.h @@ -21,8 +21,8 @@ void smc_close_wake_tx_prepared(struct smc_sock *smc); void smc_close_active_abort(struct smc_sock *smc); int smc_close_active(struct smc_sock *smc); -void smc_close_passive_received(struct smc_sock *smc); void smc_close_sock_put_work(struct work_struct *work); int smc_close_shutdown_write(struct smc_sock *smc); +void smc_close_init(struct smc_sock *smc); #endif /* SMC_CLOSE_H */ diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index 0eac633fb354..65020e93ff21 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -316,7 +316,7 @@ void smc_lgr_terminate(struct smc_link_group *lgr) smc = container_of(conn, struct smc_sock, conn); sock_hold(&smc->sk); __smc_lgr_unregister_conn(conn); - smc_close_active_abort(smc); + schedule_work(&conn->close_work); sock_put(&smc->sk); node = rb_first(&lgr->conns_all); } -- cgit From a98bf8c0bce7bd3371adbe99d1e2b31c32308fb9 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Mon, 10 Apr 2017 14:58:02 +0200 Subject: net/smc: guarantee ConnClosed send after shutdown SHUT_WR State SMC_CLOSED should be reached only, if ConnClosed has been sent to the peer. If ConnClosed is received from the peer, a socket with shutdown SHUT_WR done, switches errorneously to state SMC_CLOSED, which means the peer socket is dangling. The local SMC socket is supposed to switch to state APPFINCLOSEWAIT to make sure smc_close_final() is called during socket close. Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- net/smc/smc_close.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/net/smc/smc_close.c b/net/smc/smc_close.c index 388503b78228..9070720b6d1a 100644 --- a/net/smc/smc_close.c +++ b/net/smc/smc_close.c @@ -164,6 +164,12 @@ void smc_close_active_abort(struct smc_sock *smc) smc->sk.sk_state_change(&smc->sk); } +static inline bool smc_close_sent_any_close(struct smc_connection *conn) +{ + return conn->local_tx_ctrl.conn_state_flags.peer_conn_abort || + conn->local_tx_ctrl.conn_state_flags.peer_conn_closed; +} + int smc_close_active(struct smc_sock *smc) { struct smc_cdc_conn_state_flags *txflags = @@ -217,7 +223,7 @@ again: case SMC_APPFINCLOSEWAIT: /* socket already shutdown wr or both (active close) */ if (txflags->peer_done_writing && - !txflags->peer_conn_closed) { + !smc_close_sent_any_close(conn)) { /* just shutdown wr done, send close request */ rc = smc_close_final(conn); } @@ -247,6 +253,13 @@ again: break; case SMC_PEERCLOSEWAIT1: case SMC_PEERCLOSEWAIT2: + if (txflags->peer_done_writing && + !smc_close_sent_any_close(conn)) { + /* just shutdown wr done, send close request */ + rc = smc_close_final(conn); + } + /* peer sending PeerConnectionClosed will cause transition */ + break; case SMC_PEERFINCLOSEWAIT: /* peer sending PeerConnectionClosed will cause transition */ break; @@ -284,7 +297,7 @@ static void smc_close_passive_abort_received(struct smc_sock *smc) case SMC_PEERCLOSEWAIT1: case SMC_PEERCLOSEWAIT2: if (txflags->peer_done_writing && - !txflags->peer_conn_closed) { + !smc_close_sent_any_close(&smc->conn)) { /* just shutdown, but not yet closed locally */ smc_close_abort(&smc->conn); sk->sk_state = SMC_PROCESSABORT; @@ -335,7 +348,7 @@ static void smc_close_passive_work(struct work_struct *work) case SMC_INIT: if (atomic_read(&smc->conn.bytes_to_rcv) || (rxflags->peer_done_writing && - !rxflags->peer_conn_closed)) + !smc_cdc_rxed_any_close(conn))) sk->sk_state = SMC_APPCLOSEWAIT1; else sk->sk_state = SMC_CLOSED; @@ -352,7 +365,7 @@ static void smc_close_passive_work(struct work_struct *work) if (!smc_cdc_rxed_any_close(&smc->conn)) break; if (sock_flag(sk, SOCK_DEAD) && - (sk->sk_shutdown == SHUTDOWN_MASK)) { + smc_close_sent_any_close(conn)) { /* smc_release has already been called locally */ sk->sk_state = SMC_CLOSED; } else { @@ -371,16 +384,17 @@ static void smc_close_passive_work(struct work_struct *work) } wakeup: - if (old_state != sk->sk_state) - sk->sk_state_change(sk); sk->sk_data_ready(sk); /* wakeup blocked rcvbuf consumers */ sk->sk_write_space(sk); /* wakeup blocked sndbuf producers */ - if ((sk->sk_state == SMC_CLOSED) && - (sock_flag(sk, SOCK_DEAD) || !sk->sk_socket)) { - smc_conn_free(&smc->conn); - schedule_delayed_work(&smc->sock_put_work, - SMC_CLOSE_SOCK_PUT_DELAY); + if (old_state != sk->sk_state) { + sk->sk_state_change(sk); + if ((sk->sk_state == SMC_CLOSED) && + (sock_flag(sk, SOCK_DEAD) || !sk->sk_socket)) { + smc_conn_free(&smc->conn); + schedule_delayed_work(&smc->sock_put_work, + SMC_CLOSE_SOCK_PUT_DELAY); + } } release_sock(&smc->sk); } -- cgit From f5227cd9f1d752ad45f08597db8cac4a9436c48d Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Mon, 10 Apr 2017 14:58:03 +0200 Subject: net/smc: remove duplicate unhash unhash is already called in sock_put_work. Remove the second call. Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- net/smc/af_smc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 3b7eda6c27de..491a8b2b8b89 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -147,7 +147,6 @@ static int smc_release(struct socket *sock) schedule_delayed_work(&smc->sock_put_work, SMC_CLOSE_SOCK_PUT_DELAY); } - sk->sk_prot->unhash(sk); release_sock(sk); sock_put(sk); -- cgit From 288c83902a15acf91863949aec467a20a1e32fe1 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Mon, 10 Apr 2017 14:58:04 +0200 Subject: net/smc: destruct non-accepted sockets Make sure sockets never accepted are removed cleanly. Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- net/smc/af_smc.c | 14 +++++++++----- net/smc/smc_close.c | 1 - 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 491a8b2b8b89..5b6ee21368a6 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -638,7 +638,8 @@ struct sock *smc_accept_dequeue(struct sock *parent, smc_accept_unlink(new_sk); if (new_sk->sk_state == SMC_CLOSED) { - /* tbd in follow-on patch: close this sock */ + new_sk->sk_prot->unhash(new_sk); + sock_put(new_sk); continue; } if (new_sock) @@ -658,8 +659,13 @@ void smc_close_non_accepted(struct sock *sk) if (!sk->sk_lingertime) /* wait for peer closing */ sk->sk_lingertime = SMC_MAX_STREAM_WAIT_TIMEOUT; - if (!smc->use_fallback) + if (smc->use_fallback) { + sk->sk_state = SMC_CLOSED; + } else { smc_close_active(smc); + sock_set_flag(sk, SOCK_DEAD); + sk->sk_shutdown |= SHUTDOWN_MASK; + } if (smc->clcsock) { struct socket *tcp; @@ -667,11 +673,9 @@ void smc_close_non_accepted(struct sock *sk) smc->clcsock = NULL; sock_release(tcp); } - sock_set_flag(sk, SOCK_DEAD); - sk->sk_shutdown |= SHUTDOWN_MASK; if (smc->use_fallback) { schedule_delayed_work(&smc->sock_put_work, TCP_TIMEWAIT_LEN); - } else { + } else if (sk->sk_state == SMC_CLOSED) { smc_conn_free(&smc->conn); schedule_delayed_work(&smc->sock_put_work, SMC_CLOSE_SOCK_PUT_DELAY); diff --git a/net/smc/smc_close.c b/net/smc/smc_close.c index 9070720b6d1a..3c2e166b5d22 100644 --- a/net/smc/smc_close.c +++ b/net/smc/smc_close.c @@ -191,7 +191,6 @@ again: sk->sk_state = SMC_CLOSED; if (smc->smc_listen_work.func) cancel_work_sync(&smc->smc_listen_work); - sock_put(sk); break; case SMC_LISTEN: sk->sk_state = SMC_CLOSED; -- cgit From 2c9c16825ea08b94e013e6902486b53457555b8d Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Mon, 10 Apr 2017 14:58:05 +0200 Subject: net/smc: do not use IB_SEND_INLINE together with mapped data smc specifies IB_SEND_INLINE for IB_WR_SEND ib_post_send calls, but provides a mapped buffer to be sent. This is inconsistent, since IB_SEND_INLINE works without mapped buffer. Problem has not been detected in the past, because tests had been limited to Connect X3 cards from Mellanox, whose mlx4 driver just ignored the IB_SEND_INLINE flag. For now, the IB_SEND_INLINE flag is removed. Signed-off-by: Ursula Braun Reviewed-by: Thomas Richter Signed-off-by: David S. Miller --- net/smc/smc_ib.c | 1 - net/smc/smc_wr.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/net/smc/smc_ib.c b/net/smc/smc_ib.c index b76257798ba5..16b7c801f8b6 100644 --- a/net/smc/smc_ib.c +++ b/net/smc/smc_ib.c @@ -257,7 +257,6 @@ int smc_ib_create_queue_pair(struct smc_link *lnk) .max_recv_wr = SMC_WR_BUF_CNT * 3, .max_send_sge = SMC_IB_MAX_SEND_SGE, .max_recv_sge = 1, - .max_inline_data = SMC_WR_TX_SIZE, }, .sq_sig_type = IB_SIGNAL_REQ_WR, .qp_type = IB_QPT_RC, diff --git a/net/smc/smc_wr.c b/net/smc/smc_wr.c index eadf157418dc..874ee9f9d796 100644 --- a/net/smc/smc_wr.c +++ b/net/smc/smc_wr.c @@ -447,7 +447,7 @@ static void smc_wr_init_sge(struct smc_link *lnk) lnk->wr_tx_ibs[i].num_sge = 1; lnk->wr_tx_ibs[i].opcode = IB_WR_SEND; lnk->wr_tx_ibs[i].send_flags = - IB_SEND_SIGNALED | IB_SEND_SOLICITED | IB_SEND_INLINE; + IB_SEND_SIGNALED | IB_SEND_SOLICITED; } for (i = 0; i < lnk->wr_rx_cnt; i++) { lnk->wr_rx_sges[i].addr = -- cgit From 05690d633f309d7369176754f7362e979c5710a6 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 12 Apr 2017 13:27:01 +1000 Subject: ftgmac100: Upgrade to NETIF_F_HW_CSUM The documentation describes NETIF_F_IP_CSUM as deprecated so let's switch to NETIF_F_HW_CSUM and use the helper to handle unhandled protocols. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 40 ++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 7156f9ff6b7e..3994a8307c08 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -572,6 +572,26 @@ static void ftgmac100_tx_complete(struct ftgmac100 *priv) } } +static bool ftgmac100_prep_tx_csum(struct sk_buff *skb, u32 *csum_vlan) +{ + if (skb->protocol == cpu_to_be16(ETH_P_IP)) { + u8 ip_proto = ip_hdr(skb)->protocol; + + *csum_vlan |= FTGMAC100_TXDES1_IP_CHKSUM; + switch(ip_proto) { + case IPPROTO_TCP: + *csum_vlan |= FTGMAC100_TXDES1_TCP_CHKSUM; + return true; + case IPPROTO_UDP: + *csum_vlan |= FTGMAC100_TXDES1_UDP_CHKSUM; + return true; + case IPPROTO_IP: + return true; + } + } + return skb_checksum_help(skb) == 0; +} + static int ftgmac100_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev) { @@ -628,19 +648,9 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, /* Setup HW checksumming */ csum_vlan = 0; - if (skb->ip_summed == CHECKSUM_PARTIAL) { - __be16 protocol = skb->protocol; - - if (protocol == cpu_to_be16(ETH_P_IP)) { - u8 ip_proto = ip_hdr(skb)->protocol; - - csum_vlan |= FTGMAC100_TXDES1_IP_CHKSUM; - if (ip_proto == IPPROTO_TCP) - csum_vlan |= FTGMAC100_TXDES1_TCP_CHKSUM; - else if (ip_proto == IPPROTO_UDP) - csum_vlan |= FTGMAC100_TXDES1_UDP_CHKSUM; - } - } + if (skb->ip_summed == CHECKSUM_PARTIAL && + !ftgmac100_prep_tx_csum(skb, &csum_vlan)) + goto drop; txdes->txdes1 = cpu_to_le32(csum_vlan); /* Next descriptor */ @@ -1465,11 +1475,11 @@ static int ftgmac100_probe(struct platform_device *pdev) * when NCSI is enabled on the interface. It doesn't work * in that case. */ - netdev->features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | + netdev->features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM | NETIF_F_GRO | NETIF_F_SG; if (priv->use_ncsi && of_get_property(pdev->dev.of_node, "no-hw-checksum", NULL)) - netdev->features &= ~NETIF_F_IP_CSUM; + netdev->features &= ~NETIF_F_HW_CSUM; /* register network device */ err = register_netdev(netdev); -- cgit From 78d28543a6093fad8f1b00a5debbde02f392ebb4 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 12 Apr 2017 13:27:02 +1000 Subject: ftgmac100: Use device "compatible" property, not machine. We test for aspeed chips to handle a couple of special cases, but we do that by checking the machine type which isn't right. Instead check the actual device compatible property. This also updates the dtsi files for the aspeed SoC to match. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- arch/arm/boot/dts/aspeed-g4.dtsi | 4 ++-- arch/arm/boot/dts/aspeed-g5.dtsi | 4 ++-- drivers/net/ethernet/faraday/ftgmac100.c | 16 +++++++++------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi index 0b4932cc02a8..6068e79fb651 100644 --- a/arch/arm/boot/dts/aspeed-g4.dtsi +++ b/arch/arm/boot/dts/aspeed-g4.dtsi @@ -42,7 +42,7 @@ }; mac0: ethernet@1e660000 { - compatible = "faraday,ftgmac100"; + compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; reg = <0x1e660000 0x180>; interrupts = <2>; no-hw-checksum; @@ -50,7 +50,7 @@ }; mac1: ethernet@1e680000 { - compatible = "faraday,ftgmac100"; + compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; reg = <0x1e680000 0x180>; interrupts = <3>; no-hw-checksum; diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi index b664fe380936..4dbe91a02792 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -33,7 +33,7 @@ }; mac0: ethernet@1e660000 { - compatible = "faraday,ftgmac100"; + compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; reg = <0x1e660000 0x180>; interrupts = <2>; no-hw-checksum; @@ -41,7 +41,7 @@ }; mac1: ethernet@1e680000 { - compatible = "faraday,ftgmac100"; + compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; reg = <0x1e680000 0x180>; interrupts = <3>; no-hw-checksum; diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 3994a8307c08..e793d353882f 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -92,6 +92,7 @@ struct ftgmac100 { /* Misc */ bool need_mac_restart; + bool is_aspeed; }; static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr) @@ -1322,8 +1323,7 @@ static int ftgmac100_setup_mdio(struct net_device *netdev) if (!priv->mii_bus) return -EIO; - if (of_machine_is_compatible("aspeed,ast2400") || - of_machine_is_compatible("aspeed,ast2500")) { + if (priv->is_aspeed) { /* This driver supports the old MDIO interface */ reg = ioread32(priv->base + FTGMAC100_OFFSET_REVR); reg &= ~FTGMAC100_REVR_NEW_MDIO_INTERFACE; @@ -1388,6 +1388,7 @@ static int ftgmac100_probe(struct platform_device *pdev) int irq; struct net_device *netdev; struct ftgmac100 *priv; + struct device_node *np; int err = 0; if (!pdev) @@ -1443,17 +1444,18 @@ static int ftgmac100_probe(struct platform_device *pdev) /* MAC address from chip or random one */ ftgmac100_setup_mac(priv); - if (of_machine_is_compatible("aspeed,ast2400") || - of_machine_is_compatible("aspeed,ast2500")) { + np = pdev->dev.of_node; + if (np && (of_device_is_compatible(np, "aspeed,ast2400-mac") || + of_device_is_compatible(np, "aspeed,ast2500-mac"))) { priv->rxdes0_edorr_mask = BIT(30); priv->txdes0_edotr_mask = BIT(30); + priv->is_aspeed = true; } else { priv->rxdes0_edorr_mask = BIT(15); priv->txdes0_edotr_mask = BIT(15); } - if (pdev->dev.of_node && - of_get_property(pdev->dev.of_node, "use-ncsi", NULL)) { + if (np && of_get_property(np, "use-ncsi", NULL)) { if (!IS_ENABLED(CONFIG_NET_NCSI)) { dev_err(&pdev->dev, "NCSI stack not enabled\n"); goto err_ncsi_dev; @@ -1478,7 +1480,7 @@ static int ftgmac100_probe(struct platform_device *pdev) netdev->features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM | NETIF_F_GRO | NETIF_F_SG; if (priv->use_ncsi && - of_get_property(pdev->dev.of_node, "no-hw-checksum", NULL)) + of_get_property(np, "no-hw-checksum", NULL)) netdev->features &= ~NETIF_F_HW_CSUM; /* register network device */ -- cgit From 6aff0bf641cf69e487d7b46fc8be773d161f814d Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 12 Apr 2017 13:27:03 +1000 Subject: ftgmac100: Disable HW checksum generation on AST2400, enable on others We found out that HW checksum generation only works from AST2500 onward. This disables it on AST2400 and removes the "no-hw-checksum" properties in the device-trees. The problem we had wasn't related to NC-SI. Also rework the logic testing for that property so it can be used to disable HW checksum generation and checking regardless of whether NC-SI is used or not in case other variants out there need this. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- arch/arm/boot/dts/aspeed-g4.dtsi | 2 -- arch/arm/boot/dts/aspeed-g5.dtsi | 2 -- drivers/net/ethernet/faraday/ftgmac100.c | 12 ++++++------ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi index 6068e79fb651..c79c937b0a8a 100644 --- a/arch/arm/boot/dts/aspeed-g4.dtsi +++ b/arch/arm/boot/dts/aspeed-g4.dtsi @@ -45,7 +45,6 @@ compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; reg = <0x1e660000 0x180>; interrupts = <2>; - no-hw-checksum; status = "disabled"; }; @@ -53,7 +52,6 @@ compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; reg = <0x1e680000 0x180>; interrupts = <3>; - no-hw-checksum; status = "disabled"; }; diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi index 4dbe91a02792..b6596633036c 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -36,7 +36,6 @@ compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; reg = <0x1e660000 0x180>; interrupts = <2>; - no-hw-checksum; status = "disabled"; }; @@ -44,7 +43,6 @@ compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; reg = <0x1e680000 0x180>; interrupts = <3>; - no-hw-checksum; status = "disabled"; }; diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index e793d353882f..099309920d6a 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1473,15 +1473,15 @@ static int ftgmac100_probe(struct platform_device *pdev) goto err_setup_mdio; } - /* We have to disable on-chip IP checksum functionality - * when NCSI is enabled on the interface. It doesn't work - * in that case. - */ + /* Base feature set */ netdev->features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM | NETIF_F_GRO | NETIF_F_SG; - if (priv->use_ncsi && - of_get_property(np, "no-hw-checksum", NULL)) + + /* AST2400 doesn't have working HW checksum generation */ + if (np && (of_device_is_compatible(np, "aspeed,ast2400-mac"))) netdev->features &= ~NETIF_F_HW_CSUM; + if (np && of_get_property(np, "no-hw-checksum", NULL)) + netdev->features &= ~(NETIF_F_HW_CSUM | NETIF_F_RXCSUM); /* register network device */ err = register_netdev(netdev); -- cgit From 8c3ed1315e129e97e47d267dea55d4cbc3b0e21a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 12 Apr 2017 13:27:04 +1000 Subject: ftgmac100: Set netdev->hw_features So features can be turned on/off via ethtool Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 099309920d6a..a08226a9b0df 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1474,14 +1474,15 @@ static int ftgmac100_probe(struct platform_device *pdev) } /* Base feature set */ - netdev->features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM | + netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM | NETIF_F_GRO | NETIF_F_SG; /* AST2400 doesn't have working HW checksum generation */ if (np && (of_device_is_compatible(np, "aspeed,ast2400-mac"))) - netdev->features &= ~NETIF_F_HW_CSUM; + netdev->hw_features &= ~NETIF_F_HW_CSUM; if (np && of_get_property(np, "no-hw-checksum", NULL)) - netdev->features &= ~(NETIF_F_HW_CSUM | NETIF_F_RXCSUM); + netdev->hw_features &= ~(NETIF_F_HW_CSUM | NETIF_F_RXCSUM); + netdev->features |= netdev->hw_features; /* register network device */ err = register_netdev(netdev); -- cgit From f39c71b069ccb6c1e921d504f6598b0989ac9ecf Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 12 Apr 2017 13:27:05 +1000 Subject: ftgmac100: Rename ftgmac100_set_mac to ftgmac100_write_mac_addr To avoid confusion with the ndo callback and generally be clearer about the purpose of that function Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index a08226a9b0df..bc0aab02bb33 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -175,7 +175,7 @@ static int ftgmac100_reset_and_config_mac(struct ftgmac100 *priv) return ftgmac100_reset_mac(priv, maccr); } -static void ftgmac100_set_mac(struct ftgmac100 *priv, const unsigned char *mac) +static void ftgmac100_write_mac_addr(struct ftgmac100 *priv, const u8 *mac) { unsigned int maddr = mac[0] << 8 | mac[1]; unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; @@ -228,7 +228,7 @@ static int ftgmac100_set_mac_addr(struct net_device *dev, void *p) return ret; eth_commit_mac_addr_change(dev, p); - ftgmac100_set_mac(netdev_priv(dev), dev->dev_addr); + ftgmac100_write_mac_addr(netdev_priv(dev), dev->dev_addr); return 0; } @@ -247,7 +247,7 @@ static void ftgmac100_init_hw(struct ftgmac100 *priv) iowrite32(FTGMAC100_APTC_RXPOLL_CNT(1), priv->base + FTGMAC100_OFFSET_APTC); - ftgmac100_set_mac(priv, priv->netdev->dev_addr); + ftgmac100_write_mac_addr(priv, priv->netdev->dev_addr); } static void ftgmac100_start_hw(struct ftgmac100 *priv) -- cgit From ba1b1234d6a3ecb864404014fcdfeb51d1f62233 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 12 Apr 2017 13:27:06 +1000 Subject: ftgmac100: Rename ftgmac100_setup_mac to ftgmac100_initial_mac To remove more confusion. This function is about obtaining the initial MAC address at driver probe time. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index bc0aab02bb33..d9c5ede53606 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -184,7 +184,7 @@ static void ftgmac100_write_mac_addr(struct ftgmac100 *priv, const u8 *mac) iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR); } -static void ftgmac100_setup_mac(struct ftgmac100 *priv) +static void ftgmac100_initial_mac(struct ftgmac100 *priv) { u8 mac[ETH_ALEN]; unsigned int m; @@ -1442,7 +1442,7 @@ static int ftgmac100_probe(struct platform_device *pdev) netdev->irq = irq; /* MAC address from chip or random one */ - ftgmac100_setup_mac(priv); + ftgmac100_initial_mac(priv); np = pdev->dev.of_node; if (np && (of_device_is_compatible(np, "aspeed,ast2400-mac") || -- cgit From 8eecf7caad8687eae2812fc6467f156a8909eb12 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 12 Apr 2017 13:27:07 +1000 Subject: ftgmac100: Open code remaining register writes The helpers just take space but don't provide much value. Simple one line comments are more explanatory. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 53 ++++++++++++-------------------- 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index d9c5ede53606..f14700f67d67 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -95,29 +95,6 @@ struct ftgmac100 { bool is_aspeed; }; -static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr) -{ - iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR); -} - -static void ftgmac100_set_rx_buffer_size(struct ftgmac100 *priv, - unsigned int size) -{ - size = FTGMAC100_RBSR_SIZE(size); - iowrite32(size, priv->base + FTGMAC100_OFFSET_RBSR); -} - -static void ftgmac100_set_normal_prio_tx_ring_base(struct ftgmac100 *priv, - dma_addr_t addr) -{ - iowrite32(addr, priv->base + FTGMAC100_OFFSET_NPTXR_BADR); -} - -static void ftgmac100_txdma_normal_prio_start_polling(struct ftgmac100 *priv) -{ - iowrite32(1, priv->base + FTGMAC100_OFFSET_NPTXPD); -} - static int ftgmac100_reset_mac(struct ftgmac100 *priv, u32 maccr) { struct net_device *netdev = priv->netdev; @@ -235,18 +212,27 @@ static int ftgmac100_set_mac_addr(struct net_device *dev, void *p) static void ftgmac100_init_hw(struct ftgmac100 *priv) { - /* setup ring buffer base registers */ - ftgmac100_set_rx_ring_base(priv, - priv->descs_dma_addr + - offsetof(struct ftgmac100_descs, rxdes)); - ftgmac100_set_normal_prio_tx_ring_base(priv, - priv->descs_dma_addr + - offsetof(struct ftgmac100_descs, txdes)); - ftgmac100_set_rx_buffer_size(priv, RX_BUF_SIZE); - iowrite32(FTGMAC100_APTC_RXPOLL_CNT(1), priv->base + FTGMAC100_OFFSET_APTC); + /* Setup RX ring buffer base */ + iowrite32(priv->descs_dma_addr + + offsetof(struct ftgmac100_descs, rxdes), + priv->base + FTGMAC100_OFFSET_RXR_BADR); + /* Setup TX ring buffer base */ + iowrite32(priv->descs_dma_addr + + offsetof(struct ftgmac100_descs, txdes), + priv->base + FTGMAC100_OFFSET_NPTXR_BADR); + + /* Configure RX buffer size */ + iowrite32(FTGMAC100_RBSR_SIZE(RX_BUF_SIZE), + priv->base + FTGMAC100_OFFSET_RBSR); + + /* Set RX descriptor autopoll */ + iowrite32(FTGMAC100_APTC_RXPOLL_CNT(1), + priv->base + FTGMAC100_OFFSET_APTC); + + /* Write MAC address */ ftgmac100_write_mac_addr(priv, priv->netdev->dev_addr); } @@ -706,7 +692,8 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, netif_wake_queue(netdev); } - ftgmac100_txdma_normal_prio_start_polling(priv); + /* Poke transmitter to read the updated TX descriptors */ + iowrite32(1, priv->base + FTGMAC100_OFFSET_NPTXPD); return NETDEV_TX_OK; -- cgit From 3833dc6c1888cfd872b9ce2e3c70a4f07f1a6f9a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 12 Apr 2017 13:27:08 +1000 Subject: ftgmac100: Add more register inits in ftgmac100_init_hw() Clear stale interrupts on entry, configure FIFO sizes, set FIFO thresholds, configure interrupt mitigation. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index f14700f67d67..49c19b786122 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -212,7 +212,11 @@ static int ftgmac100_set_mac_addr(struct net_device *dev, void *p) static void ftgmac100_init_hw(struct ftgmac100 *priv) { + u32 reg, rfifo_sz, tfifo_sz; + /* Clear stale interrupts */ + reg = ioread32(priv->base + FTGMAC100_OFFSET_ISR); + iowrite32(reg, priv->base + FTGMAC100_OFFSET_ISR); /* Setup RX ring buffer base */ iowrite32(priv->descs_dma_addr + @@ -234,6 +238,38 @@ static void ftgmac100_init_hw(struct ftgmac100 *priv) /* Write MAC address */ ftgmac100_write_mac_addr(priv, priv->netdev->dev_addr); + + /* Configure descriptor sizes and increase burst sizes according + * to values in Aspeed SDK. The FIFO arbitration is enabled and + * the thresholds set based on the recommended values in the + * AST2400 specification. + */ + iowrite32(FTGMAC100_DBLAC_RXDES_SIZE(2) | /* 2*8 bytes RX descs */ + FTGMAC100_DBLAC_TXDES_SIZE(2) | /* 2*8 bytes TX descs */ + FTGMAC100_DBLAC_RXBURST_SIZE(3) | /* 512 bytes max RX bursts */ + FTGMAC100_DBLAC_TXBURST_SIZE(3) | /* 512 bytes max TX bursts */ + FTGMAC100_DBLAC_RX_THR_EN | /* Enable fifo threshold arb */ + FTGMAC100_DBLAC_RXFIFO_HTHR(6) | /* 6/8 of FIFO high threshold */ + FTGMAC100_DBLAC_RXFIFO_LTHR(2), /* 2/8 of FIFO low threshold */ + priv->base + FTGMAC100_OFFSET_DBLAC); + + /* Interrupt mitigation configured for 1 interrupt/packet. HW interrupt + * mitigation doesn't seem to provide any benefit with NAPI so leave + * it at that. + */ + iowrite32(FTGMAC100_ITC_RXINT_THR(1) | + FTGMAC100_ITC_TXINT_THR(1), + priv->base + FTGMAC100_OFFSET_ITC); + + /* Configure FIFO sizes in the TPAFCR register */ + reg = ioread32(priv->base + FTGMAC100_OFFSET_FEAR); + rfifo_sz = reg & 0x00000007; + tfifo_sz = (reg >> 3) & 0x00000007; + reg = ioread32(priv->base + FTGMAC100_OFFSET_TPAFCR); + reg &= ~0x3f000000; + reg |= (tfifo_sz << 27); + reg |= (rfifo_sz << 24); + iowrite32(reg, priv->base + FTGMAC100_OFFSET_TPAFCR); } static void ftgmac100_start_hw(struct ftgmac100 *priv) -- cgit From 52d9138fb31ac2dadfbf8e51abf04130ee25f4ee Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 12 Apr 2017 13:27:09 +1000 Subject: ftgmac100: Make ring sizes configurable via ethtool We set an arbitrary max at 1024 since we pre-allocate the actual descriptor arrays and skb arrays to the full size to keep the code a bit simpler and avoid allocation failures in the reset task. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 204 ++++++++++++++++++++++--------- 1 file changed, 148 insertions(+), 56 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 49c19b786122..259790b7126e 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -40,8 +40,15 @@ #define DRV_NAME "ftgmac100" #define DRV_VERSION "0.7" -#define RX_QUEUE_ENTRIES 256 /* must be power of 2 */ -#define TX_QUEUE_ENTRIES 512 /* must be power of 2 */ +/* Arbitrary values, I am not sure the HW has limits */ +#define MAX_RX_QUEUE_ENTRIES 1024 +#define MAX_TX_QUEUE_ENTRIES 1024 +#define MIN_RX_QUEUE_ENTRIES 32 +#define MIN_TX_QUEUE_ENTRIES 32 + +/* Defaults */ +#define DEF_RX_QUEUE_ENTRIES 256 +#define DEF_TX_QUEUE_ENTRIES 512 #define MAX_PKT_SIZE 1536 #define RX_BUF_SIZE MAX_PKT_SIZE /* must be smaller than 0x3fff */ @@ -49,30 +56,32 @@ /* Min number of tx ring entries before stopping queue */ #define TX_THRESHOLD (MAX_SKB_FRAGS + 1) -struct ftgmac100_descs { - struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES]; - struct ftgmac100_txdes txdes[TX_QUEUE_ENTRIES]; -}; - struct ftgmac100 { /* Registers */ struct resource *res; void __iomem *base; - struct ftgmac100_descs *descs; - dma_addr_t descs_dma_addr; - /* Rx ring */ - struct sk_buff *rx_skbs[RX_QUEUE_ENTRIES]; + unsigned int rx_q_entries; + struct ftgmac100_rxdes *rxdes; + dma_addr_t rxdes_dma; + struct sk_buff **rx_skbs; unsigned int rx_pointer; u32 rxdes0_edorr_mask; /* Tx ring */ - struct sk_buff *tx_skbs[TX_QUEUE_ENTRIES]; + unsigned int tx_q_entries; + struct ftgmac100_txdes *txdes; + dma_addr_t txdes_dma; + struct sk_buff **tx_skbs; unsigned int tx_clean_pointer; unsigned int tx_pointer; u32 txdes0_edotr_mask; + /* Used to signal the reset task of ring change request */ + unsigned int new_rx_q_entries; + unsigned int new_tx_q_entries; + /* Scratch page to use when rx skb alloc fails */ void *rx_scratch; dma_addr_t rx_scratch_dma; @@ -219,14 +228,10 @@ static void ftgmac100_init_hw(struct ftgmac100 *priv) iowrite32(reg, priv->base + FTGMAC100_OFFSET_ISR); /* Setup RX ring buffer base */ - iowrite32(priv->descs_dma_addr + - offsetof(struct ftgmac100_descs, rxdes), - priv->base + FTGMAC100_OFFSET_RXR_BADR); + iowrite32(priv->rxdes_dma, priv->base + FTGMAC100_OFFSET_RXR_BADR); /* Setup TX ring buffer base */ - iowrite32(priv->descs_dma_addr + - offsetof(struct ftgmac100_descs, txdes), - priv->base + FTGMAC100_OFFSET_NPTXR_BADR); + iowrite32(priv->txdes_dma, priv->base + FTGMAC100_OFFSET_NPTXR_BADR); /* Configure RX buffer size */ iowrite32(FTGMAC100_RBSR_SIZE(RX_BUF_SIZE), @@ -339,7 +344,7 @@ static int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv, unsigned int entry, dma_wmb(); /* Clean status (which resets own bit) */ - if (entry == (RX_QUEUE_ENTRIES - 1)) + if (entry == (priv->rx_q_entries - 1)) rxdes->rxdes0 = cpu_to_le32(priv->rxdes0_edorr_mask); else rxdes->rxdes0 = 0; @@ -347,9 +352,10 @@ static int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv, unsigned int entry, return 0; } -static int ftgmac100_next_rx_pointer(int pointer) +static unsigned int ftgmac100_next_rx_pointer(struct ftgmac100 *priv, + unsigned int pointer) { - return (pointer + 1) & (RX_QUEUE_ENTRIES - 1); + return (pointer + 1) & (priv->rx_q_entries - 1); } static void ftgmac100_rx_packet_error(struct ftgmac100 *priv, u32 status) @@ -379,7 +385,7 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) /* Grab next RX descriptor */ pointer = priv->rx_pointer; - rxdes = &priv->descs->rxdes[pointer]; + rxdes = &priv->rxdes[pointer]; /* Grab descriptor status */ status = le32_to_cpu(rxdes->rxdes0); @@ -467,7 +473,7 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) /* Resplenish rx ring */ ftgmac100_alloc_rx_buf(priv, pointer, rxdes, GFP_ATOMIC); - priv->rx_pointer = ftgmac100_next_rx_pointer(pointer); + priv->rx_pointer = ftgmac100_next_rx_pointer(priv, pointer); skb->protocol = eth_type_trans(skb, netdev); @@ -486,7 +492,7 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) drop: /* Clean rxdes0 (which resets own bit) */ rxdes->rxdes0 = cpu_to_le32(status & priv->rxdes0_edorr_mask); - priv->rx_pointer = ftgmac100_next_rx_pointer(pointer); + priv->rx_pointer = ftgmac100_next_rx_pointer(priv, pointer); netdev->stats.rx_dropped++; return true; } @@ -494,15 +500,16 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) static u32 ftgmac100_base_tx_ctlstat(struct ftgmac100 *priv, unsigned int index) { - if (index == (TX_QUEUE_ENTRIES - 1)) + if (index == (priv->tx_q_entries - 1)) return priv->txdes0_edotr_mask; else return 0; } -static int ftgmac100_next_tx_pointer(int pointer) +static unsigned int ftgmac100_next_tx_pointer(struct ftgmac100 *priv, + unsigned int pointer) { - return (pointer + 1) & (TX_QUEUE_ENTRIES - 1); + return (pointer + 1) & (priv->tx_q_entries - 1); } static u32 ftgmac100_tx_buf_avail(struct ftgmac100 *priv) @@ -514,7 +521,7 @@ static u32 ftgmac100_tx_buf_avail(struct ftgmac100 *priv) * test for ftgmac100_tx_buf_cleanable() below */ return (priv->tx_clean_pointer - priv->tx_pointer - 1) & - (TX_QUEUE_ENTRIES - 1); + (priv->tx_q_entries - 1); } static bool ftgmac100_tx_buf_cleanable(struct ftgmac100 *priv) @@ -554,7 +561,7 @@ static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) u32 ctl_stat; pointer = priv->tx_clean_pointer; - txdes = &priv->descs->txdes[pointer]; + txdes = &priv->txdes[pointer]; ctl_stat = le32_to_cpu(txdes->txdes0); if (ctl_stat & FTGMAC100_TXDES0_TXDMA_OWN) @@ -566,7 +573,7 @@ static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) ftgmac100_free_tx_packet(priv, pointer, skb, txdes, ctl_stat); txdes->txdes0 = cpu_to_le32(ctl_stat & priv->txdes0_edotr_mask); - priv->tx_clean_pointer = ftgmac100_next_tx_pointer(pointer); + priv->tx_clean_pointer = ftgmac100_next_tx_pointer(priv, pointer); return true; } @@ -655,7 +662,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, /* Grab the next free tx descriptor */ pointer = priv->tx_pointer; - txdes = first = &priv->descs->txdes[pointer]; + txdes = first = &priv->txdes[pointer]; /* Setup it up with the packet head. Don't write the head to the * ring just yet @@ -677,7 +684,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, txdes->txdes1 = cpu_to_le32(csum_vlan); /* Next descriptor */ - pointer = ftgmac100_next_tx_pointer(pointer); + pointer = ftgmac100_next_tx_pointer(priv, pointer); /* Add the fragments */ for (i = 0; i < nfrags; i++) { @@ -693,7 +700,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, /* Setup descriptor */ priv->tx_skbs[pointer] = skb; - txdes = &priv->descs->txdes[pointer]; + txdes = &priv->txdes[pointer]; ctl_stat = ftgmac100_base_tx_ctlstat(priv, pointer); ctl_stat |= FTGMAC100_TXDES0_TXDMA_OWN; ctl_stat |= FTGMAC100_TXDES0_TXBUF_SIZE(len); @@ -704,7 +711,7 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, txdes->txdes3 = cpu_to_le32(map); /* Next one */ - pointer = ftgmac100_next_tx_pointer(pointer); + pointer = ftgmac100_next_tx_pointer(priv, pointer); } /* Order the previous packet and descriptor udpates @@ -744,8 +751,8 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, /* Then all fragments */ for (j = 0; j < i; j++) { - pointer = ftgmac100_next_tx_pointer(pointer); - txdes = &priv->descs->txdes[pointer]; + pointer = ftgmac100_next_tx_pointer(priv, pointer); + txdes = &priv->txdes[pointer]; ctl_stat = le32_to_cpu(txdes->txdes0); ftgmac100_free_tx_packet(priv, pointer, skb, txdes, ctl_stat); txdes->txdes0 = cpu_to_le32(ctl_stat & priv->txdes0_edotr_mask); @@ -768,8 +775,8 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) int i; /* Free all RX buffers */ - for (i = 0; i < RX_QUEUE_ENTRIES; i++) { - struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; + for (i = 0; i < priv->rx_q_entries; i++) { + struct ftgmac100_rxdes *rxdes = &priv->rxdes[i]; struct sk_buff *skb = priv->rx_skbs[i]; dma_addr_t map = le32_to_cpu(rxdes->rxdes3); @@ -782,8 +789,8 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) } /* Free all TX buffers */ - for (i = 0; i < TX_QUEUE_ENTRIES; i++) { - struct ftgmac100_txdes *txdes = &priv->descs->txdes[i]; + for (i = 0; i < priv->tx_q_entries; i++) { + struct ftgmac100_txdes *txdes = &priv->txdes[i]; struct sk_buff *skb = priv->tx_skbs[i]; if (!skb) @@ -795,10 +802,22 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) static void ftgmac100_free_rings(struct ftgmac100 *priv) { + /* Free skb arrays */ + kfree(priv->rx_skbs); + kfree(priv->tx_skbs); + /* Free descriptors */ - if (priv->descs) - dma_free_coherent(priv->dev, sizeof(struct ftgmac100_descs), - priv->descs, priv->descs_dma_addr); + if (priv->rxdes) + dma_free_coherent(priv->dev, MAX_RX_QUEUE_ENTRIES * + sizeof(struct ftgmac100_rxdes), + priv->rxdes, priv->rxdes_dma); + priv->rxdes = NULL; + + if (priv->txdes) + dma_free_coherent(priv->dev, MAX_TX_QUEUE_ENTRIES * + sizeof(struct ftgmac100_txdes), + priv->txdes, priv->txdes_dma); + priv->txdes = NULL; /* Free scratch packet buffer */ if (priv->rx_scratch) @@ -808,11 +827,28 @@ static void ftgmac100_free_rings(struct ftgmac100 *priv) static int ftgmac100_alloc_rings(struct ftgmac100 *priv) { + /* Allocate skb arrays */ + priv->rx_skbs = kcalloc(MAX_RX_QUEUE_ENTRIES, sizeof(void *), + GFP_KERNEL); + if (!priv->rx_skbs) + return -ENOMEM; + priv->tx_skbs = kcalloc(MAX_TX_QUEUE_ENTRIES, sizeof(void *), + GFP_KERNEL); + if (!priv->tx_skbs) + return -ENOMEM; + /* Allocate descriptors */ - priv->descs = dma_zalloc_coherent(priv->dev, - sizeof(struct ftgmac100_descs), - &priv->descs_dma_addr, GFP_KERNEL); - if (!priv->descs) + priv->rxdes = dma_zalloc_coherent(priv->dev, + MAX_RX_QUEUE_ENTRIES * + sizeof(struct ftgmac100_rxdes), + &priv->rxdes_dma, GFP_KERNEL); + if (!priv->rxdes) + return -ENOMEM; + priv->txdes = dma_zalloc_coherent(priv->dev, + MAX_TX_QUEUE_ENTRIES * + sizeof(struct ftgmac100_txdes), + &priv->txdes_dma, GFP_KERNEL); + if (!priv->txdes) return -ENOMEM; /* Allocate scratch packet buffer */ @@ -828,22 +864,32 @@ static int ftgmac100_alloc_rings(struct ftgmac100 *priv) static void ftgmac100_init_rings(struct ftgmac100 *priv) { - struct ftgmac100_rxdes *rxdes; - struct ftgmac100_txdes *txdes; + struct ftgmac100_rxdes *rxdes = NULL; + struct ftgmac100_txdes *txdes = NULL; int i; + /* Update entries counts */ + priv->rx_q_entries = priv->new_rx_q_entries; + priv->tx_q_entries = priv->new_tx_q_entries; + + if (WARN_ON(priv->rx_q_entries < MIN_RX_QUEUE_ENTRIES)) + return; + /* Initialize RX ring */ - for (i = 0; i < RX_QUEUE_ENTRIES; i++) { - rxdes = &priv->descs->rxdes[i]; + for (i = 0; i < priv->rx_q_entries; i++) { + rxdes = &priv->rxdes[i]; rxdes->rxdes0 = 0; rxdes->rxdes3 = cpu_to_le32(priv->rx_scratch_dma); } /* Mark the end of the ring */ rxdes->rxdes0 |= cpu_to_le32(priv->rxdes0_edorr_mask); + if (WARN_ON(priv->tx_q_entries < MIN_RX_QUEUE_ENTRIES)) + return; + /* Initialize TX ring */ - for (i = 0; i < TX_QUEUE_ENTRIES; i++) { - txdes = &priv->descs->txdes[i]; + for (i = 0; i < priv->tx_q_entries; i++) { + txdes = &priv->txdes[i]; txdes->txdes0 = 0; } txdes->txdes0 |= cpu_to_le32(priv->txdes0_edotr_mask); @@ -853,8 +899,8 @@ static int ftgmac100_alloc_rx_buffers(struct ftgmac100 *priv) { int i; - for (i = 0; i < RX_QUEUE_ENTRIES; i++) { - struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; + for (i = 0; i < priv->rx_q_entries; i++) { + struct ftgmac100_rxdes *rxdes = &priv->rxdes[i]; if (ftgmac100_alloc_rx_buf(priv, i, rxdes, GFP_KERNEL)) return -ENOMEM; @@ -999,11 +1045,53 @@ static void ftgmac100_get_drvinfo(struct net_device *netdev, strlcpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info)); } +static int ftgmac100_nway_reset(struct net_device *ndev) +{ + if (!ndev->phydev) + return -ENXIO; + return phy_start_aneg(ndev->phydev); +} + +static void ftgmac100_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ering) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + + memset(ering, 0, sizeof(*ering)); + ering->rx_max_pending = MAX_RX_QUEUE_ENTRIES; + ering->tx_max_pending = MAX_TX_QUEUE_ENTRIES; + ering->rx_pending = priv->rx_q_entries; + ering->tx_pending = priv->tx_q_entries; +} + +static int ftgmac100_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ering) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + + if (ering->rx_pending > MAX_RX_QUEUE_ENTRIES || + ering->tx_pending > MAX_TX_QUEUE_ENTRIES || + ering->rx_pending < MIN_RX_QUEUE_ENTRIES || + ering->tx_pending < MIN_TX_QUEUE_ENTRIES || + !is_power_of_2(ering->rx_pending) || + !is_power_of_2(ering->tx_pending)) + return -EINVAL; + + priv->new_rx_q_entries = ering->rx_pending; + priv->new_tx_q_entries = ering->tx_pending; + if (netif_running(netdev)) + schedule_work(&priv->reset_task); + + return 0; +} + static const struct ethtool_ops ftgmac100_ethtool_ops = { .get_drvinfo = ftgmac100_get_drvinfo, .get_link = ethtool_op_get_link, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_ringparam = ftgmac100_get_ringparam, + .set_ringparam = ftgmac100_set_ringparam, }; static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) @@ -1059,7 +1147,7 @@ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) static bool ftgmac100_check_rx(struct ftgmac100 *priv) { - struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[priv->rx_pointer]; + struct ftgmac100_rxdes *rxdes = &priv->rxdes[priv->rx_pointer]; /* Do we have a packet ? */ return !!(rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY)); @@ -1496,6 +1584,10 @@ static int ftgmac100_probe(struct platform_device *pdev) goto err_setup_mdio; } + /* Default ring sizes */ + priv->rx_q_entries = priv->new_rx_q_entries = DEF_RX_QUEUE_ENTRIES; + priv->tx_q_entries = priv->new_tx_q_entries = DEF_TX_QUEUE_ENTRIES; + /* Base feature set */ netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM | NETIF_F_GRO | NETIF_F_SG; -- cgit From bd3e4fde2f596e2b5393d154d2766e39b217cd63 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 12 Apr 2017 13:27:10 +1000 Subject: ftgmac100: Set default ring sizes to 128 entries I haven't seen any improvement above that size on the machines I've tested with. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 259790b7126e..2153c5bbdd12 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -47,8 +47,8 @@ #define MIN_TX_QUEUE_ENTRIES 32 /* Defaults */ -#define DEF_RX_QUEUE_ENTRIES 256 -#define DEF_TX_QUEUE_ENTRIES 512 +#define DEF_RX_QUEUE_ENTRIES 128 +#define DEF_TX_QUEUE_ENTRIES 128 #define MAX_PKT_SIZE 1536 #define RX_BUF_SIZE MAX_PKT_SIZE /* must be smaller than 0x3fff */ -- cgit From 9aaef50c44f132e040dcd7686c8e78a3390037c5 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Wed, 12 Apr 2017 10:05:29 +0200 Subject: l2tp: define parameters of l2tp_session_get*() as "const" Make l2tp_pernet()'s parameter constant, so that l2tp_session_get*() can declare their "net" variable as "const". Also constify "ifname" in l2tp_session_get_by_ifname(). Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller --- net/l2tp/l2tp_core.c | 7 ++++--- net/l2tp/l2tp_core.h | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 154974be1eed..4828111c9ea7 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -120,7 +120,7 @@ static inline struct l2tp_tunnel *l2tp_tunnel(struct sock *sk) return sk->sk_user_data; } -static inline struct l2tp_net *l2tp_pernet(struct net *net) +static inline struct l2tp_net *l2tp_pernet(const struct net *net) { BUG_ON(!net); @@ -232,7 +232,7 @@ l2tp_session_id_hash(struct l2tp_tunnel *tunnel, u32 session_id) /* Lookup a session. A new reference is held on the returned session. * Optionally calls session->ref() too if do_ref is true. */ -struct l2tp_session *l2tp_session_get(struct net *net, +struct l2tp_session *l2tp_session_get(const struct net *net, struct l2tp_tunnel *tunnel, u32 session_id, bool do_ref) { @@ -307,7 +307,8 @@ EXPORT_SYMBOL_GPL(l2tp_session_get_nth); /* Lookup a session by interface name. * This is very inefficient but is only used by management interfaces. */ -struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname, +struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net, + const char *ifname, bool do_ref) { struct l2tp_net *pn = l2tp_pernet(net); diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index a3248e18badb..1a46bc44632f 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -230,12 +230,13 @@ out: return tunnel; } -struct l2tp_session *l2tp_session_get(struct net *net, +struct l2tp_session *l2tp_session_get(const struct net *net, struct l2tp_tunnel *tunnel, u32 session_id, bool do_ref); struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth, bool do_ref); -struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname, +struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net, + const char *ifname, bool do_ref); struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id); struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth); -- cgit From 2f858b928bf5a8174911aaec76b8b72a9ca0533d Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Wed, 12 Apr 2017 10:05:30 +0200 Subject: l2tp: define parameters of l2tp_tunnel_find*() as "const" l2tp_tunnel_find() and l2tp_tunnel_find_nth() don't modify "net". Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller --- net/l2tp/l2tp_core.c | 4 ++-- net/l2tp/l2tp_core.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 4828111c9ea7..fa0342574b89 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -379,7 +379,7 @@ exist: /* Lookup a tunnel by id */ -struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id) +struct l2tp_tunnel *l2tp_tunnel_find(const struct net *net, u32 tunnel_id) { struct l2tp_tunnel *tunnel; struct l2tp_net *pn = l2tp_pernet(net); @@ -397,7 +397,7 @@ struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id) } EXPORT_SYMBOL_GPL(l2tp_tunnel_find); -struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth) +struct l2tp_tunnel *l2tp_tunnel_find_nth(const struct net *net, int nth) { struct l2tp_net *pn = l2tp_pernet(net); struct l2tp_tunnel *tunnel; diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 1a46bc44632f..eec5ad2ebb93 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -238,8 +238,8 @@ struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth, struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net, const char *ifname, bool do_ref); -struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id); -struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth); +struct l2tp_tunnel *l2tp_tunnel_find(const struct net *net, u32 tunnel_id); +struct l2tp_tunnel *l2tp_tunnel_find_nth(const struct net *net, int nth); int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, -- cgit From eaffadbbb3f2711fc76e7b0ddf37c452ee11b805 Mon Sep 17 00:00:00 2001 From: Ilan Tayari Date: Sat, 8 Apr 2017 02:07:08 +0300 Subject: gso: Support frag_list splitting with head_frag A driver may use build_skb() for received packets. These SKBs then have a head_frag. Since commit d7e8883cfcf4 ("net: make GRO aware of skb->head_frag"), GRO may build frag_list SKBs out of head_frag received SKBs. In such a case, the chained SKBs end up with a head_frag. Commit 07b26c9454a2 ("gso: Support partial splitting at the frag_list pointer") adds partial segmentation of frag_list SKB chains into individual SKBs. However, this is not done if the chained SKBs have any linear part, because the device may not be able to DMA the private linear buffer. A chained frag_list SKB with head_frag is wrongfully detected in this case as having a private linear part and thus falls back to software GSO, while in fact the linear part is backed by a DMA page just like any other frag. This causes low performance when forwarding those packets that were built with build_skb() Allow partial segmentation at the frag_list pointer for chained SKBs with head_frag. Note that such SKBs can only be created by GRO, when applied to received packets with head_frag. Also note that this change only affects the data path that performs the partial segmentation at frag_list pointer, and not any of the other more common data paths. Signed-off-by: Ilan Tayari Signed-off-by: David S. Miller --- net/core/skbuff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 9f781092fda9..5d9a11eafbf5 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3093,7 +3093,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, * containing the same amount of data. */ skb_walk_frags(head_skb, iter) { - if (skb_headlen(iter)) + if (skb_headlen(iter) && !iter->head_frag) goto normal; len -= iter->len; -- cgit From 5b3dc2f37d7daf76a679cd204492ec5dff06bb8a Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Mon, 10 Apr 2017 11:11:17 +0300 Subject: net: neigh: make ->hh_len 32-bit Using 16-bit ->hh_len doesn't save any memory, save some .text instead: add/remove: 0/0 grow/shrink: 1/6 up/down: 2/-19 (-17) function old new delta neigh_update 2312 2314 +2 fwnet_header_cache 199 197 -2 eth_header_cache 101 99 -2 ip6_finish_output2 2371 2368 -3 vrf_finish_output6 1522 1518 -4 vrf_finish_output 1413 1409 -4 ip_finish_output2 1627 1623 -4 Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +-- include/net/neighbour.h | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index cc07c3be2705..8ea8a8b70755 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -237,8 +237,7 @@ struct netdev_hw_addr_list { netdev_hw_addr_list_for_each(ha, &(dev)->mc) struct hh_cache { - u16 hh_len; - u16 __pad; + unsigned int hh_len; seqlock_t hh_lock; /* cached hardware header; allow for machine alignment needs. */ diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 9496179c7b4e..e4dd3a214034 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -450,7 +450,7 @@ static inline int neigh_hh_bridge(struct hh_cache *hh, struct sk_buff *skb) static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb) { unsigned int seq; - int hh_len; + unsigned int hh_len; do { seq = read_seqbegin(&hh->hh_lock); @@ -459,7 +459,7 @@ static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb /* this is inlined by gcc */ memcpy(skb->data - HH_DATA_MOD, hh->hh_data, HH_DATA_MOD); } else { - int hh_alen = HH_DATA_ALIGN(hh_len); + unsigned int hh_alen = HH_DATA_ALIGN(hh_len); memcpy(skb->data - hh_alen, hh->hh_data, hh_alen); } -- cgit From d92be7a41ef15463eb816a4a2d42bf094b56dfce Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Mon, 10 Apr 2017 11:25:26 +0300 Subject: net: make struct net_device::min_header_len 8-bit This field is never big enough to warrant 16-bitness. 8-bit accesses enjoy shorted encoding on i386/x86_64 than 16-bit accesses: add/remove: 0/0 grow/shrink: 0/2 up/down: 0/-10 (-10) function old new delta loopback_setup 169 164 -5 ether_setup 148 143 -5 Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 8ea8a8b70755..b0aa089ce67f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1715,7 +1715,7 @@ struct net_device { unsigned int max_mtu; unsigned short type; unsigned short hard_header_len; - unsigned short min_header_len; + unsigned char min_header_len; unsigned short needed_headroom; unsigned short needed_tailroom; -- cgit From 9232259913022c1ab96f7bf2b87598b89b942f1f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 21 Mar 2017 12:20:28 +0000 Subject: Bluetooth: btmrvl: fix spelling mistake: "unregester" -> "unregister" trivial fix to spelling mistake in debug message Signed-off-by: Colin Ian King Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmrvl_sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 08e01f002bad..7d052ce2052b 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -1574,7 +1574,7 @@ static void btmrvl_sdio_remove(struct sdio_func *func) MODULE_SHUTDOWN_REQ); btmrvl_sdio_disable_host_int(card); } - BT_DBG("unregester dev"); + BT_DBG("unregister dev"); card->priv->surprise_removed = true; btmrvl_sdio_unregister_dev(card); btmrvl_remove_card(card->priv); -- cgit From dab6b5daeeb3e70bfb2ed2d6d6f02dd387e3b338 Mon Sep 17 00:00:00 2001 From: Elena Reshetova Date: Fri, 17 Mar 2017 13:12:46 +0200 Subject: Bluetooth: convert rfcomm_dlc.refcnt from atomic_t to refcount_t refcount_t type and corresponding API should be used instead of atomic_t when the variable is used as a reference counter. This allows to avoid accidental refcounter overflows that might lead to use-after-free situations. Signed-off-by: Elena Reshetova Signed-off-by: Hans Liljestrand Signed-off-by: Kees Cook Signed-off-by: David Windsor Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 8 +++++--- net/bluetooth/rfcomm/core.c | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 4190af53a46a..da4acefe39c8 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -21,6 +21,8 @@ SOFTWARE IS DISCLAIMED. */ +#include + #ifndef __RFCOMM_H #define __RFCOMM_H @@ -174,7 +176,7 @@ struct rfcomm_dlc { struct mutex lock; unsigned long state; unsigned long flags; - atomic_t refcnt; + refcount_t refcnt; u8 dlci; u8 addr; u8 priority; @@ -247,12 +249,12 @@ struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel); static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d) { - atomic_inc(&d->refcnt); + refcount_inc(&d->refcnt); } static inline void rfcomm_dlc_put(struct rfcomm_dlc *d) { - if (atomic_dec_and_test(&d->refcnt)) + if (refcount_dec_and_test(&d->refcnt)) rfcomm_dlc_free(d); } diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index f7eb02f09b54..8ebca9033d60 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -311,7 +311,7 @@ struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio) skb_queue_head_init(&d->tx_queue); mutex_init(&d->lock); - atomic_set(&d->refcnt, 1); + refcount_set(&d->refcnt, 1); rfcomm_dlc_clear_state(d); @@ -342,7 +342,7 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) { struct rfcomm_session *s = d->session; - BT_DBG("dlc %p refcnt %d session %p", d, atomic_read(&d->refcnt), s); + BT_DBG("dlc %p refcnt %d session %p", d, refcount_read(&d->refcnt), s); list_del(&d->list); d->session = NULL; -- cgit From 730ce397cd38e1cce257a44493b6c90edf102824 Mon Sep 17 00:00:00 2001 From: John Keeping Date: Wed, 15 Mar 2017 12:20:05 +0000 Subject: Bluetooth: hci_bcm: Fix clock (un)prepare The hci_bcm driver currently does not prepare/unprepare the clock and goes directly to enable, but as the documentation for clk_enable says, clk_prepare must be called before clk_enable. Signed-off-by: John Keeping Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_bcm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 5262a2077d7a..c7d3c6842bcf 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -146,13 +146,13 @@ static bool bcm_device_exists(struct bcm_device *device) static int bcm_gpio_set_power(struct bcm_device *dev, bool powered) { if (powered && !IS_ERR(dev->clk) && !dev->clk_enabled) - clk_enable(dev->clk); + clk_prepare_enable(dev->clk); gpiod_set_value(dev->shutdown, powered); gpiod_set_value(dev->device_wakeup, powered); if (!powered && !IS_ERR(dev->clk) && dev->clk_enabled) - clk_disable(dev->clk); + clk_disable_unprepare(dev->clk); dev->clk_enabled = powered; -- cgit From c259d1413b195c57e708f324eed41668a37ba6ec Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Sun, 12 Mar 2017 10:19:33 +0200 Subject: bluetooth: Set 6 byte device addresses Set BTLE MAC addresses that are 6 bytes long and not 8 bytes that are used in other places with 6lowpan. Signed-off-by: Patrik Flykt Signed-off-by: Luiz Augusto von Dentz Reviewed-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- net/bluetooth/6lowpan.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index d491529332f4..bad0e9d1ea20 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -80,6 +80,8 @@ struct lowpan_btle_dev { struct delayed_work notify_peers; }; +static void set_addr(u8 *eui, u8 *addr, u8 addr_type); + static inline struct lowpan_btle_dev * lowpan_btle_dev(const struct net_device *netdev) { @@ -272,9 +274,10 @@ static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev) static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev, struct l2cap_chan *chan) { - const u8 *saddr, *daddr; + const u8 *saddr; struct lowpan_btle_dev *dev; struct lowpan_peer *peer; + unsigned char eui64_daddr[EUI64_ADDR_LEN]; dev = lowpan_btle_dev(netdev); @@ -285,9 +288,9 @@ static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev, return -EINVAL; saddr = peer->eui64_addr; - daddr = dev->netdev->dev_addr; + set_addr(&eui64_daddr[0], chan->src.b, chan->src_type); - return lowpan_header_decompress(skb, netdev, daddr, saddr); + return lowpan_header_decompress(skb, netdev, &eui64_daddr, saddr); } static int recv_pkt(struct sk_buff *skb, struct net_device *dev, @@ -681,13 +684,6 @@ static void set_addr(u8 *eui, u8 *addr, u8 addr_type) BT_DBG("type %d addr %*phC", addr_type, 8, eui); } -static void set_dev_addr(struct net_device *netdev, bdaddr_t *addr, - u8 addr_type) -{ - netdev->addr_assign_type = NET_ADDR_PERM; - set_addr(netdev->dev_addr, addr->b, addr_type); -} - static void ifup(struct net_device *netdev) { int err; @@ -803,7 +799,8 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_btle_dev **dev) if (!netdev) return -ENOMEM; - set_dev_addr(netdev, &chan->src, chan->src_type); + netdev->addr_assign_type = NET_ADDR_PERM; + baswap((void *)netdev->dev_addr, &chan->src); netdev->netdev_ops = &netdev_ops; SET_NETDEV_DEV(netdev, &chan->conn->hcon->hdev->dev); -- cgit From be054fc830ff70aced3533fffc6b851fa604d241 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Sun, 12 Mar 2017 10:19:34 +0200 Subject: 6lowpan: Set MAC address length according to LOWPAN_LLTYPE Set MAC address length according to the 6LoWPAN link layer in use. Bluetooth Low Energy uses 48 bit addressing while IEEE802.15.4 uses 64 bits. Signed-off-by: Patrik Flykt Reviewed-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- net/6lowpan/core.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c index 5945f7e19c67..5f9909a2b58c 100644 --- a/net/6lowpan/core.c +++ b/net/6lowpan/core.c @@ -23,7 +23,16 @@ int lowpan_register_netdevice(struct net_device *dev, { int i, ret; - dev->addr_len = EUI64_ADDR_LEN; + switch (lltype) { + case LOWPAN_LLTYPE_IEEE802154: + dev->addr_len = EUI64_ADDR_LEN; + break; + + case LOWPAN_LLTYPE_BTLE: + dev->addr_len = ETH_ALEN; + break; + } + dev->type = ARPHRD_6LOWPAN; dev->mtu = IPV6_MIN_MTU; dev->priv_flags |= IFF_NO_QUEUE; -- cgit From 94e4a68039cc0c9d6e5cf708819cb8c7e770ebbf Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Sun, 12 Mar 2017 10:19:35 +0200 Subject: 6lowpan: iphc: override l2 packet information The skb->pkt_type need to be set by L2, but on 6LoWPAN there exists L2 e.g. BTLE which doesn't has multicast addressing. If it's a multicast or not is detected by IPHC headers multicast bit. The IPv6 layer will evaluate this pkt_type, so we force set this type while uncompressing. Should be okay for 802.15.4 as well. Signed-off-by: Alexander Aring Reviewed-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- net/6lowpan/iphc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c index 79f1fa22509a..fb5f6fa8f1df 100644 --- a/net/6lowpan/iphc.c +++ b/net/6lowpan/iphc.c @@ -666,6 +666,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev, switch (iphc1 & (LOWPAN_IPHC_M | LOWPAN_IPHC_DAC)) { case LOWPAN_IPHC_M | LOWPAN_IPHC_DAC: + skb->pkt_type = PACKET_BROADCAST; + spin_lock_bh(&lowpan_dev(dev)->ctx.lock); ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid)); if (!ci) { @@ -681,11 +683,15 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev, spin_unlock_bh(&lowpan_dev(dev)->ctx.lock); break; case LOWPAN_IPHC_M: + skb->pkt_type = PACKET_BROADCAST; + /* multicast */ err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr, iphc1 & LOWPAN_IPHC_DAM_MASK); break; case LOWPAN_IPHC_DAC: + skb->pkt_type = PACKET_HOST; + spin_lock_bh(&lowpan_dev(dev)->ctx.lock); ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid)); if (!ci) { @@ -701,6 +707,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev, spin_unlock_bh(&lowpan_dev(dev)->ctx.lock); break; default: + skb->pkt_type = PACKET_HOST; + err = lowpan_iphc_uncompress_addr(skb, dev, &hdr.daddr, iphc1 & LOWPAN_IPHC_DAM_MASK, daddr); -- cgit From 8a7a4b476719df1e06f8eae837b13bdf79908843 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Sun, 12 Mar 2017 10:19:36 +0200 Subject: ipv6: addrconf: fix 48 bit 6lowpan autoconfiguration This patch adds support for 48 bit 6LoWPAN address length autoconfiguration which is the case for BTLE 6LoWPAN. Signed-off-by: Alexander Aring Signed-off-by: Luiz Augusto von Dentz Reviewed-by: Stefan Schmidt Acked-by: David S. Miller Signed-off-by: Marcel Holtmann --- net/ipv6/addrconf.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 67ec87ea5fb6..b22796e707d3 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2073,12 +2073,19 @@ static void addrconf_leave_anycast(struct inet6_ifaddr *ifp) __ipv6_dev_ac_dec(ifp->idev, &addr); } -static int addrconf_ifid_eui64(u8 *eui, struct net_device *dev) +static int addrconf_ifid_6lowpan(u8 *eui, struct net_device *dev) { - if (dev->addr_len != EUI64_ADDR_LEN) + switch (dev->addr_len) { + case ETH_ALEN: + return addrconf_ifid_eui48(eui, dev); + case EUI64_ADDR_LEN: + memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN); + eui[0] ^= 2; + break; + default: return -1; - memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN); - eui[0] ^= 2; + } + return 0; } @@ -2170,7 +2177,7 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev) case ARPHRD_TUNNEL: return addrconf_ifid_gre(eui, dev); case ARPHRD_6LOWPAN: - return addrconf_ifid_eui64(eui, dev); + return addrconf_ifid_6lowpan(eui, dev); case ARPHRD_IEEE1394: return addrconf_ifid_ieee1394(eui, dev); case ARPHRD_TUNNEL6: -- cgit From fa09ae661fb5ab6f9826545d5128f2b7393bcf4a Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Sun, 12 Mar 2017 10:19:37 +0200 Subject: 6lowpan: Use netdev addr_len to determine lladdr len This allow technologies such as Bluetooth to use its native lladdr which is eui48 instead of eui64 which was expected by functions like lowpan_header_decompress and lowpan_header_compress. Signed-off-by: Luiz Augusto von Dentz Reviewed-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- include/net/6lowpan.h | 19 +++++++++++++++++++ net/6lowpan/iphc.c | 49 ++++++++++++++++++++++++++++++++++++++----------- net/bluetooth/6lowpan.c | 42 ++++++------------------------------------ 3 files changed, 63 insertions(+), 47 deletions(-) diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index 5ab4c9901ccc..c5792cb6c3eb 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -198,6 +198,25 @@ static inline void lowpan_iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr, ipaddr->s6_addr[8] ^= 0x02; } +static inline void lowpan_iphc_uncompress_eui48_lladdr(struct in6_addr *ipaddr, + const void *lladdr) +{ + /* fe:80::XXXX:XXff:feXX:XXXX + * \_________________/ + * hwaddr + */ + ipaddr->s6_addr[0] = 0xFE; + ipaddr->s6_addr[1] = 0x80; + memcpy(&ipaddr->s6_addr[8], lladdr, 3); + ipaddr->s6_addr[11] = 0xFF; + ipaddr->s6_addr[12] = 0xFE; + memcpy(&ipaddr->s6_addr[13], lladdr + 3, 3); + /* second bit-flip (Universe/Local) + * is done according RFC2464 + */ + ipaddr->s6_addr[8] ^= 0x02; +} + #ifdef DEBUG /* print data in line */ static inline void raw_dump_inline(const char *caller, char *msg, diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c index fb5f6fa8f1df..6b1042e21656 100644 --- a/net/6lowpan/iphc.c +++ b/net/6lowpan/iphc.c @@ -278,6 +278,23 @@ lowpan_iphc_ctx_get_by_mcast_addr(const struct net_device *dev, return ret; } +static void lowpan_iphc_uncompress_lladdr(const struct net_device *dev, + struct in6_addr *ipaddr, + const void *lladdr) +{ + switch (dev->addr_len) { + case ETH_ALEN: + lowpan_iphc_uncompress_eui48_lladdr(ipaddr, lladdr); + break; + case EUI64_ADDR_LEN: + lowpan_iphc_uncompress_eui64_lladdr(ipaddr, lladdr); + break; + default: + WARN_ON_ONCE(1); + break; + } +} + /* Uncompress address function for source and * destination address(non-multicast). * @@ -320,7 +337,7 @@ static int lowpan_iphc_uncompress_addr(struct sk_buff *skb, lowpan_iphc_uncompress_802154_lladdr(ipaddr, lladdr); break; default: - lowpan_iphc_uncompress_eui64_lladdr(ipaddr, lladdr); + lowpan_iphc_uncompress_lladdr(dev, ipaddr, lladdr); break; } break; @@ -381,7 +398,7 @@ static int lowpan_iphc_uncompress_ctx_addr(struct sk_buff *skb, lowpan_iphc_uncompress_802154_lladdr(ipaddr, lladdr); break; default: - lowpan_iphc_uncompress_eui64_lladdr(ipaddr, lladdr); + lowpan_iphc_uncompress_lladdr(dev, ipaddr, lladdr); break; } ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen); @@ -810,6 +827,21 @@ lowpan_iphc_compress_ctx_802154_lladdr(const struct in6_addr *ipaddr, return lladdr_compress; } +static bool lowpan_iphc_addr_equal(const struct net_device *dev, + const struct lowpan_iphc_ctx *ctx, + const struct in6_addr *ipaddr, + const void *lladdr) +{ + struct in6_addr tmp = {}; + + lowpan_iphc_uncompress_lladdr(dev, &tmp, lladdr); + + if (ctx) + ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen); + + return ipv6_addr_equal(&tmp, ipaddr); +} + static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct net_device *dev, const struct in6_addr *ipaddr, const struct lowpan_iphc_ctx *ctx, @@ -827,13 +859,7 @@ static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct net_device *dev, } break; default: - /* check for SAM/DAM = 11 */ - memcpy(&tmp.s6_addr[8], lladdr, EUI64_ADDR_LEN); - /* second bit-flip (Universe/Local) is done according RFC2464 */ - tmp.s6_addr[8] ^= 0x02; - /* context information are always used */ - ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen); - if (ipv6_addr_equal(&tmp, ipaddr)) { + if (lowpan_iphc_addr_equal(dev, ctx, ipaddr, lladdr)) { dam = LOWPAN_IPHC_DAM_11; goto out; } @@ -929,11 +955,12 @@ static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct net_device *dev, } break; default: - if (is_addr_mac_addr_based(ipaddr, lladdr)) { - dam = LOWPAN_IPHC_DAM_11; /* 0-bits */ + if (lowpan_iphc_addr_equal(dev, NULL, ipaddr, lladdr)) { + dam = LOWPAN_IPHC_DAM_11; pr_debug("address compression 0 bits\n"); goto out; } + break; } diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index bad0e9d1ea20..54bf4d765a7d 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -64,7 +64,7 @@ struct lowpan_peer { struct l2cap_chan *chan; /* peer addresses in various formats */ - unsigned char eui64_addr[EUI64_ADDR_LEN]; + unsigned char lladdr[ETH_ALEN]; struct in6_addr peer_addr; }; @@ -80,8 +80,6 @@ struct lowpan_btle_dev { struct delayed_work notify_peers; }; -static void set_addr(u8 *eui, u8 *addr, u8 addr_type); - static inline struct lowpan_btle_dev * lowpan_btle_dev(const struct net_device *netdev) { @@ -277,7 +275,6 @@ static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev, const u8 *saddr; struct lowpan_btle_dev *dev; struct lowpan_peer *peer; - unsigned char eui64_daddr[EUI64_ADDR_LEN]; dev = lowpan_btle_dev(netdev); @@ -287,10 +284,9 @@ static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev, if (!peer) return -EINVAL; - saddr = peer->eui64_addr; - set_addr(&eui64_daddr[0], chan->src.b, chan->src_type); + saddr = peer->lladdr; - return lowpan_header_decompress(skb, netdev, &eui64_daddr, saddr); + return lowpan_header_decompress(skb, netdev, netdev->dev_addr, saddr); } static int recv_pkt(struct sk_buff *skb, struct net_device *dev, @@ -477,7 +473,7 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev, } } - daddr = peer->eui64_addr; + daddr = peer->lladdr; *peer_addr = addr; *peer_addr_type = addr_type; lowpan_cb(skb)->chan = peer->chan; @@ -663,27 +659,6 @@ static struct device_type bt_type = { .name = "bluetooth", }; -static void set_addr(u8 *eui, u8 *addr, u8 addr_type) -{ - /* addr is the BT address in little-endian format */ - eui[0] = addr[5]; - eui[1] = addr[4]; - eui[2] = addr[3]; - eui[3] = 0xFF; - eui[4] = 0xFE; - eui[5] = addr[2]; - eui[6] = addr[1]; - eui[7] = addr[0]; - - /* Universal/local bit set, BT 6lowpan draft ch. 3.2.1 */ - if (addr_type == BDADDR_LE_PUBLIC) - eui[0] &= ~0x02; - else - eui[0] |= 0x02; - - BT_DBG("type %d addr %*phC", addr_type, 8, eui); -} - static void ifup(struct net_device *netdev) { int err; @@ -762,14 +737,9 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, peer->chan = chan; memset(&peer->peer_addr, 0, sizeof(struct in6_addr)); - /* RFC 2464 ch. 5 */ - peer->peer_addr.s6_addr[0] = 0xFE; - peer->peer_addr.s6_addr[1] = 0x80; - set_addr((u8 *)&peer->peer_addr.s6_addr + 8, chan->dst.b, - chan->dst_type); + baswap((void *)peer->lladdr, &chan->dst); - memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8, - EUI64_ADDR_LEN); + lowpan_iphc_uncompress_eui48_lladdr(&peer->peer_addr, peer->lladdr); /* IPv6 address needs to have the U/L bit set properly so toggle * it back here. -- cgit From 9dae2e030319811e9cdaa260faaa151cf0866186 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Sun, 12 Mar 2017 10:19:38 +0200 Subject: 6lowpan: Fix IID format for Bluetooth According to RFC 7668 U/L bit shall not be used: https://wiki.tools.ietf.org/html/rfc7668#section-3.2.2 [Page 10]: In the figure, letter 'b' represents a bit from the Bluetooth device address, copied as is without any changes on any bit. This means that no bit in the IID indicates whether the underlying Bluetooth device address is public or random. |0 1|1 3|3 4|4 6| |0 5|6 1|2 7|8 3| +----------------+----------------+----------------+----------------+ |bbbbbbbbbbbbbbbb|bbbbbbbb11111111|11111110bbbbbbbb|bbbbbbbbbbbbbbbb| +----------------+----------------+----------------+----------------+ Because of this the code cannot figure out the address type from the IP address anymore thus it makes no sense to use peer_lookup_ba as it needs the peer address type. Signed-off-by: Luiz Augusto von Dentz Reviewed-by: Stefan Schmidt Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- include/net/6lowpan.h | 4 --- net/bluetooth/6lowpan.c | 79 ++++++++----------------------------------------- net/ipv6/addrconf.c | 6 +++- 3 files changed, 17 insertions(+), 72 deletions(-) diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index c5792cb6c3eb..a71378007e61 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -211,10 +211,6 @@ static inline void lowpan_iphc_uncompress_eui48_lladdr(struct in6_addr *ipaddr, ipaddr->s6_addr[11] = 0xFF; ipaddr->s6_addr[12] = 0xFE; memcpy(&ipaddr->s6_addr[13], lladdr + 3, 3); - /* second bit-flip (Universe/Local) - * is done according RFC2464 - */ - ipaddr->s6_addr[8] ^= 0x02; } #ifdef DEBUG diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 54bf4d765a7d..24348c8579dd 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -398,37 +398,6 @@ static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) return err; } -static u8 get_addr_type_from_eui64(u8 byte) -{ - /* Is universal(0) or local(1) bit */ - return ((byte & 0x02) ? BDADDR_LE_RANDOM : BDADDR_LE_PUBLIC); -} - -static void copy_to_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr) -{ - u8 *eui64 = ip6_daddr->s6_addr + 8; - - addr->b[0] = eui64[7]; - addr->b[1] = eui64[6]; - addr->b[2] = eui64[5]; - addr->b[3] = eui64[2]; - addr->b[4] = eui64[1]; - addr->b[5] = eui64[0]; -} - -static void convert_dest_bdaddr(struct in6_addr *ip6_daddr, - bdaddr_t *addr, u8 *addr_type) -{ - copy_to_bdaddr(ip6_daddr, addr); - - /* We need to toggle the U/L bit that we got from IPv6 address - * so that we get the proper address and type of the BD address. - */ - addr->b[5] ^= 0x02; - - *addr_type = get_addr_type_from_eui64(addr->b[5]); -} - static int setup_header(struct sk_buff *skb, struct net_device *netdev, bdaddr_t *peer_addr, u8 *peer_addr_type) { @@ -436,8 +405,7 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev, struct ipv6hdr *hdr; struct lowpan_btle_dev *dev; struct lowpan_peer *peer; - bdaddr_t addr, *any = BDADDR_ANY; - u8 *daddr = any->b; + u8 *daddr; int err, status = 0; hdr = ipv6_hdr(skb); @@ -448,34 +416,24 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev, if (ipv6_addr_is_multicast(&ipv6_daddr)) { lowpan_cb(skb)->chan = NULL; + daddr = NULL; } else { - u8 addr_type; + BT_DBG("dest IP %pI6c", &ipv6_daddr); - /* Get destination BT device from skb. - * If there is no such peer then discard the packet. + /* The packet might be sent to 6lowpan interface + * because of routing (either via default route + * or user set route) so get peer according to + * the destination address. */ - convert_dest_bdaddr(&ipv6_daddr, &addr, &addr_type); - - BT_DBG("dest addr %pMR type %d IP %pI6c", &addr, - addr_type, &ipv6_daddr); - - peer = peer_lookup_ba(dev, &addr, addr_type); + peer = peer_lookup_dst(dev, &ipv6_daddr, skb); if (!peer) { - /* The packet might be sent to 6lowpan interface - * because of routing (either via default route - * or user set route) so get peer according to - * the destination address. - */ - peer = peer_lookup_dst(dev, &ipv6_daddr, skb); - if (!peer) { - BT_DBG("no such peer %pMR found", &addr); - return -ENOENT; - } + BT_DBG("no such peer"); + return -ENOENT; } daddr = peer->lladdr; - *peer_addr = addr; - *peer_addr_type = addr_type; + peer_addr = &peer->chan->dst; + *peer_addr_type = peer->chan->dst_type; lowpan_cb(skb)->chan = peer->chan; status = 1; @@ -717,14 +675,6 @@ static struct l2cap_chan *chan_create(void) return chan; } -static void set_ip_addr_bits(u8 addr_type, u8 *addr) -{ - if (addr_type == BDADDR_LE_PUBLIC) - *addr |= 0x02; - else - *addr &= ~0x02; -} - static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, struct lowpan_btle_dev *dev) { @@ -741,11 +691,6 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, lowpan_iphc_uncompress_eui48_lladdr(&peer->peer_addr, peer->lladdr); - /* IPv6 address needs to have the U/L bit set properly so toggle - * it back here. - */ - set_ip_addr_bits(chan->dst_type, (u8 *)&peer->peer_addr.s6_addr + 8); - spin_lock(&devices_lock); INIT_LIST_HEAD(&peer->list); peer_add(dev, peer); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index b22796e707d3..3650f6e829e8 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2077,7 +2077,11 @@ static int addrconf_ifid_6lowpan(u8 *eui, struct net_device *dev) { switch (dev->addr_len) { case ETH_ALEN: - return addrconf_ifid_eui48(eui, dev); + memcpy(eui, dev->dev_addr, 3); + eui[3] = 0xFF; + eui[4] = 0xFE; + memcpy(eui + 5, dev->dev_addr + 3, 3); + break; case EUI64_ADDR_LEN: memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN); eui[0] ^= 2; -- cgit From 459848564f5186bf033a5c1cc33c2cb3b284066e Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sat, 11 Mar 2017 08:46:58 +0800 Subject: Bluetooth: bluecard: use setup_timer Use setup_timer() instead of init_timer() to simplify the code. Signed-off-by: Geliang Tang Signed-off-by: Marcel Holtmann --- drivers/bluetooth/bluecard_cs.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index c0b3b5576992..007c0a45f31b 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -695,9 +695,8 @@ static int bluecard_open(struct bluecard_info *info) spin_lock_init(&(info->lock)); - init_timer(&(info->timer)); - info->timer.function = &bluecard_activity_led_timeout; - info->timer.data = (u_long)info; + setup_timer(&(info->timer), &bluecard_activity_led_timeout, + (u_long)info); skb_queue_head_init(&(info->txq)); -- cgit From e163376220169170f3945703a600083f1792aaf8 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Fri, 10 Mar 2017 11:34:45 +0000 Subject: Bluetooth: Handle bt_accept_enqueue() socket atomically There is a small risk that bt_accept_unlink() runs concurrently with bt_accept_enqueue() on the same socket. This scenario could potentially lead to a NULL pointer dereference of the socket's parent member because the socket can be on the list but the socket's parent member is not yet updated by bt_accept_enqueue(). Therefore, add socket locking inside bt_accept_enqueue() so that the socket is added to the list AND the parent's socket address is set in the socket's parent member. The socket locking ensures that the socket is on the list with a valid non-NULL parent member. Signed-off-by: Dean Jenkins Signed-off-by: Marcel Holtmann --- net/bluetooth/af_bluetooth.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 69e1f7d362a8..a374fd27b5e1 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -159,8 +159,10 @@ void bt_accept_enqueue(struct sock *parent, struct sock *sk) BT_DBG("parent %p, sk %p", parent, sk); sock_hold(sk); + lock_sock(sk); list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q); bt_sk(sk)->parent = parent; + release_sock(sk); parent->sk_ack_backlog++; } EXPORT_SYMBOL(bt_accept_enqueue); -- cgit From 27bfbc21a0c0f711fa5382de026c7c0700c9ea28 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Fri, 10 Mar 2017 11:34:46 +0000 Subject: Bluetooth: Avoid bt_accept_unlink() double unlinking There is a race condition between a thread calling bt_accept_dequeue() and a different thread calling bt_accept_unlink(). Protection against concurrency is implemented using sk locking. However, sk locking causes serialisation of the bt_accept_dequeue() and bt_accept_unlink() threads. This serialisation can cause bt_accept_dequeue() to obtain the sk from the parent list but becomes blocked waiting for the sk lock held by the bt_accept_unlink() thread. bt_accept_unlink() unlinks sk and this thread releases the sk lock unblocking bt_accept_dequeue() which potentially runs bt_accept_unlink() again on the same sk causing a crash. The attempt to double unlink the same sk from the parent list can cause a NULL pointer dereference crash due to bt_sk(sk)->parent becoming NULL on the first unlink, followed by the second unlink trying to execute bt_sk(sk)->parent->sk_ack_backlog-- in bt_accept_unlink() which crashes. When sk is in the parent list, bt_sk(sk)->parent will be not be NULL. When sk is removed from the parent list, bt_sk(sk)->parent is set to NULL. Therefore, add a defensive check for bt_sk(sk)->parent not being NULL to ensure that sk is still in the parent list after the sk lock has been taken in bt_accept_dequeue(). If bt_sk(sk)->parent is detected as being NULL then restart the loop so that the loop variables are refreshed to use the latest values. This is necessary as list_for_each_entry_safe() is not thread safe so causing a risk of an infinite loop occurring as sk could point to itself. In addition, in bt_accept_dequeue() increase the sk reference count to protect against early freeing of sk. Early freeing can be possible if the bt_accept_unlink() thread calls l2cap_sock_kill() or rfcomm_sock_kill() functions before bt_accept_dequeue() gets the sk lock. For test purposes, the probability of failure can be increased by putting a msleep of 1 second in bt_accept_dequeue() between getting the sk and waiting for the sk lock. This exposes the fact that the loop list_for_each_entry_safe(p, n, &bt_sk(parent)->accept_q) is not safe from threads that unlink sk from the list in parallel with the loop which can cause sk to become stale within the loop. Signed-off-by: Dean Jenkins Signed-off-by: Marcel Holtmann --- net/bluetooth/af_bluetooth.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index a374fd27b5e1..42d0997e2fbb 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -167,6 +167,9 @@ void bt_accept_enqueue(struct sock *parent, struct sock *sk) } EXPORT_SYMBOL(bt_accept_enqueue); +/* Calling function must hold the sk lock. + * bt_sk(sk)->parent must be non-NULL meaning sk is in the parent list. + */ void bt_accept_unlink(struct sock *sk) { BT_DBG("sk %p state %d", sk, sk->sk_state); @@ -185,11 +188,32 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) BT_DBG("parent %p", parent); +restart: list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) { sk = (struct sock *)s; + /* Prevent early freeing of sk due to unlink and sock_kill */ + sock_hold(sk); lock_sock(sk); + /* Check sk has not already been unlinked via + * bt_accept_unlink() due to serialisation caused by sk locking + */ + if (!bt_sk(sk)->parent) { + BT_DBG("sk %p, already unlinked", sk); + release_sock(sk); + sock_put(sk); + + /* Restart the loop as sk is no longer in the list + * and also avoid a potential infinite loop because + * list_for_each_entry_safe() is not thread safe. + */ + goto restart; + } + + /* sk is safely in the parent list so reduce reference count */ + sock_put(sk); + /* FIXME: Is this check still needed */ if (sk->sk_state == BT_CLOSED) { bt_accept_unlink(sk); -- cgit From d8edd9ed156a1a840f1b1c2dbbf458684d6eea6e Mon Sep 17 00:00:00 2001 From: Marcin Kraglak Date: Wed, 8 Mar 2017 14:09:41 +0100 Subject: Bluetooth: L2CAP: Fix L2CAP_CR_SCID_IN_USE value Fix issue found during L2CAP qualification test TP/LE/CFC/BV-20-C. Signed-off-by: Marcin Kraglak Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 5ee3c689c863..0697fd413087 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -282,7 +282,7 @@ struct l2cap_conn_rsp { #define L2CAP_CR_BAD_KEY_SIZE 0x0007 #define L2CAP_CR_ENCRYPTION 0x0008 #define L2CAP_CR_INVALID_SCID 0x0009 -#define L2CAP_CR_SCID_IN_USE 0x0010 +#define L2CAP_CR_SCID_IN_USE 0x000A /* connect/create channel status */ #define L2CAP_CS_NO_INFO 0x0000 -- cgit From 9268834b60c0b08101c7a8522b6901cf4cd57a14 Mon Sep 17 00:00:00 2001 From: Tedd Ho-Jeong An Date: Mon, 6 Mar 2017 15:38:26 -0800 Subject: Bluetooth: Use switch statement for Intel hardware variants Multiple new hardware variants are planned and the simple if statement would get really complicated and unreadable. So instead replace it with a simple switch statement. The change is applied to both USB and UART. Based-on-patch-by: Marcel Holtmann Signed-off-by: Tedd Ho-Jeong An Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 10 +++++++--- drivers/bluetooth/hci_intel.c | 17 +++++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 1c8094ef3f22..b599be805eb3 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -2024,13 +2024,17 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) return -EINVAL; } - /* At the moment the iBT 3.0 hardware variants 0x0b (LnP/SfP) - * and 0x0c (WsP) are supported by this firmware loading method. + /* Check for supported iBT hardware variants of this firmware + * loading method. * * This check has been put in place to ensure correct forward * compatibility options when newer hardware variants come along. */ - if (ver.hw_variant != 0x0b && ver.hw_variant != 0x0c) { + switch (ver.hw_variant) { + case 0x0b: /* SfP */ + case 0x0c: /* WsP */ + break; + default: BT_ERR("%s: Unsupported Intel hardware variant (%u)", hdev->name, ver.hw_variant); return -EINVAL; diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index 9e271286c5e5..bfd718c58d99 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -601,12 +601,17 @@ static int intel_setup(struct hci_uart *hu) return -EINVAL; } - /* At the moment only the hardware variant iBT 3.0 (LnP/SfP) is - * supported by this firmware loading method. This check has been - * put in place to ensure correct forward compatibility options - * when newer hardware variants come along. - */ - if (ver.hw_variant != 0x0b) { + /* Check for supported iBT hardware variants of this firmware + * loading method. + * + * This check has been put in place to ensure correct forward + * compatibility options when newer hardware variants come along. + */ + switch (ver.hw_variant) { + case 0x0b: /* LnP */ + case 0x0c: /* WsP */ + break; + default: bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)", ver.hw_variant); return -EINVAL; -- cgit From de766142e3a5fcb67096e24ca4233a7f60999468 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 6 Mar 2017 15:38:28 -0800 Subject: Bluetooth: btusb: Add support for Intel Bluetooth devices 9160/9260 [8087:0025] The new Bluetooth devices 9160/9260 (also known as ThunderPeak) devices from Intel use the same firmware loading mechanism as previous generation. So include the new USB product identifier and whitelist the hardware variant. T: Bus=02 Lev=02 Prnt=03 Port=00 Cnt=01 Dev#= 8 Spd=12 MxCh= 0 D: Ver= 2.01 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=8087 ProdID=0025 Rev= 0.02 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms I: If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb E: Ad=03(O) Atr=01(Isoc) MxPS= 63 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 63 Ivl=1ms Bluetooth: hci0: Bootloader revision 0.1 build 42 week 52 2015 Bluetooth: hci0: Device revision is 2 Bluetooth: hci0: Secure boot is enabled Bluetooth: hci0: OTP lock is disabled Bluetooth: hci0: API lock is disabled Bluetooth: hci0: Debug lock is disabled Bluetooth: hci0: Minimum firmware build 1 week 10 2014 < HCI Command: Read Local Version Information (0x04|0x0001) plen 0 > HCI Event: Command Complete (0x0e) plen 12 Read Local Version Information (0x04|0x0001) ncmd 1 Status: Success (0x00) HCI version: Bluetooth 5.0 (0x09) - Revision 256 (0x0100) LMP version: Bluetooth 5.0 (0x09) - Subversion 256 (0x0100) Manufacturer: Intel Corp. (2) Based on original patch from Jaya Praveen G Signed-off-by: Marcel Holtmann Tested-by: Tedd Ho-Jeong An Signed-off-by: Tedd Ho-Jeong An Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index b599be805eb3..90219dc1b90b 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -328,6 +328,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x1286, 0x204e), .driver_info = BTUSB_MARVELL }, /* Intel Bluetooth devices */ + { USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_NEW }, { USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR }, { USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL }, { USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL }, @@ -2033,6 +2034,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) switch (ver.hw_variant) { case 0x0b: /* SfP */ case 0x0c: /* WsP */ + case 0x12: /* ThP */ break; default: BT_ERR("%s: Unsupported Intel hardware variant (%u)", -- cgit From b7da6a69defd195da66bfd6b35efeb376a252557 Mon Sep 17 00:00:00 2001 From: Tedd Ho-Jeong An Date: Mon, 6 Mar 2017 15:38:32 -0800 Subject: Bluetooth: hci_intel: Fix firmware file name to use hw_variant The format of Intel Bluetooth firmware for bootloader product is ibt--.sfi and .ddc. This patch uses a hw_variant value read from the device during runtime to form the firmware filenames instead of using a constant value, so it can support multiple prouducts. Signed-off-by: Tedd Ho-Jeong An Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_intel.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index bfd718c58d99..71d0850fab10 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -704,11 +704,14 @@ static int intel_setup(struct hci_uart *hu) /* With this Intel bootloader only the hardware variant and device * revision information are used to select the right firmware. * - * Currently this bootloader support is limited to hardware variant - * iBT 3.0 (LnP/SfP) which is identified by the value 11 (0x0b). + * The firmware filename is ibt--.sfi. + * + * Currently the supported hardware variants are: + * 11 (0x0b) for iBT 3.0 (LnP/SfP) */ - snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.sfi", - le16_to_cpu(params->dev_revid)); + snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u.sfi", + le16_to_cpu(ver.hw_variant), + le16_to_cpu(params->dev_revid)); err = request_firmware(&fw, fwname, &hdev->dev); if (err < 0) { @@ -721,8 +724,9 @@ static int intel_setup(struct hci_uart *hu) bt_dev_info(hdev, "Found device firmware: %s", fwname); /* Save the DDC file name for later */ - snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.ddc", - le16_to_cpu(params->dev_revid)); + snprintf(fwname, sizeof(fwname), "intel/ibt-%u-%u.ddc", + le16_to_cpu(ver.hw_variant), + le16_to_cpu(params->dev_revid)); kfree_skb(skb); -- cgit From 6c7bb7ebb58c0e17b11aebf928ffc5c295959a01 Mon Sep 17 00:00:00 2001 From: Tedd Ho-Jeong An Date: Mon, 6 Mar 2017 15:38:35 -0800 Subject: Bluetooth: hci_intel: Add support Intel Bluetooth device 9160/9260 for UART This patch adds support for Intel Bluetooth device 9160/9260 also known as ThunderPeak(ThP) for UART. Signed-off-by: Tedd Ho-Jeong An Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_intel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index 71d0850fab10..d915e7eee233 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -610,6 +610,7 @@ static int intel_setup(struct hci_uart *hu) switch (ver.hw_variant) { case 0x0b: /* LnP */ case 0x0c: /* WsP */ + case 0x12: /* ThP */ break; default: bt_dev_err(hdev, "Unsupported Intel hardware variant (%u)", -- cgit From cd50361c21b788a29f4661a6c49260eac2b13435 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 1 Mar 2017 15:10:53 +0000 Subject: Bluetooth: fix assignments on error variable err Variable err is being initialized to zero and then later being set to the error return from the call to hci_req_run_skb; hence we can remove the redundant initialization to zero. Also on two occassions err is not being set from the error return from the call to hci_req_run_skb, so add these missing assignments. Signed-off-by: Colin Ian King Signed-off-by: Marcel Holtmann --- net/bluetooth/amp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 02a4ccc04e1e..ebcab5bbadd7 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -263,7 +263,7 @@ void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle) struct hci_cp_read_local_amp_assoc cp; struct amp_assoc *loc_assoc = &hdev->loc_assoc; struct hci_request req; - int err = 0; + int err; BT_DBG("%s handle %d", hdev->name, phy_handle); @@ -282,7 +282,7 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) { struct hci_cp_read_local_amp_assoc cp; struct hci_request req; - int err = 0; + int err; memset(&hdev->loc_assoc, 0, sizeof(struct amp_assoc)); memset(&cp, 0, sizeof(cp)); @@ -292,7 +292,7 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) set_bit(READ_LOC_AMP_ASSOC, &mgr->state); hci_req_init(&req, hdev); hci_req_add(&req, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); - hci_req_run_skb(&req, read_local_amp_assoc_complete); + err = hci_req_run_skb(&req, read_local_amp_assoc_complete); if (err < 0) a2mp_send_getampassoc_rsp(hdev, A2MP_STATUS_INVALID_CTRL_ID); } @@ -303,7 +303,7 @@ void amp_read_loc_assoc_final_data(struct hci_dev *hdev, struct hci_cp_read_local_amp_assoc cp; struct amp_mgr *mgr = hcon->amp_mgr; struct hci_request req; - int err = 0; + int err; cp.phy_handle = hcon->handle; cp.len_so_far = cpu_to_le16(0); @@ -314,7 +314,7 @@ void amp_read_loc_assoc_final_data(struct hci_dev *hdev, /* Read Local AMP Assoc final link information data */ hci_req_init(&req, hdev); hci_req_add(&req, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); - hci_req_run_skb(&req, read_local_amp_assoc_complete); + err = hci_req_run_skb(&req, read_local_amp_assoc_complete); if (err < 0) a2mp_send_getampassoc_rsp(hdev, A2MP_STATUS_INVALID_CTRL_ID); } -- cgit From b48c3b59a32932d80e3032e2e97eb2751208390a Mon Sep 17 00:00:00 2001 From: Jonas Holmberg Date: Thu, 23 Feb 2017 15:17:02 +0100 Subject: Bluetooth: Change initial min and max interval Use the initial connection interval recommended in Bluetooth Specification v4.2 (30ms - 50ms). Signed-off-by: Jonas Holmberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3ac89e9ace71..05686776a5fb 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2950,8 +2950,8 @@ struct hci_dev *hci_alloc_dev(void) hdev->le_adv_max_interval = 0x0800; hdev->le_scan_interval = 0x0060; hdev->le_scan_window = 0x0030; - hdev->le_conn_min_interval = 0x0028; - hdev->le_conn_max_interval = 0x0038; + hdev->le_conn_min_interval = 0x0018; + hdev->le_conn_max_interval = 0x0028; hdev->le_conn_latency = 0x0000; hdev->le_supv_timeout = 0x002a; hdev->le_def_tx_len = 0x001b; -- cgit From 1eef1c35006e69755330e721bf0789bbe0b1f7d3 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Mon, 20 Feb 2017 12:32:22 -0500 Subject: Bluetooth: Added support for Rivet Networks Killer 1535 Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 90219dc1b90b..eb78d235a34a 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -262,6 +262,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0cf3, 0xe007), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x0cf3, 0xe009), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME }, + { USB_DEVICE(0x0cf3, 0xe301), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x0489, 0xe092), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x04ca, 0x3011), .driver_info = BTUSB_QCA_ROME }, -- cgit From abed84a0d53bac543771849af07a9a6254ce93b7 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 19 Feb 2017 20:04:57 -0600 Subject: Bluetooth: btrtl: Change message for missing config file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The message concerning missing config files for 8723b, 8821a, and 8761a should have been issued with BT_INFO() rather than BT_ERR() as this condition is not fatal. After looking at that code, I have reworked the logic to log such messages only if the device needs such a config file. At the moment, only the 8822b fits that description. Signed-off-by: Larry Finger Acked-by: 陆朱伟 Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btrtl.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index fc9b25703c67..8279094dd713 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -275,11 +275,8 @@ static int rtl_load_config(struct hci_dev *hdev, const char *name, u8 **buff) BT_INFO("%s: rtl: loading %s", hdev->name, name); ret = request_firmware(&fw, name, &hdev->dev); - if (ret < 0) { - BT_ERR("%s: Failed to load %s", hdev->name, name); + if (ret < 0) return ret; - } - ret = fw->size; *buff = kmemdup(fw->data, ret, GFP_KERNEL); @@ -331,6 +328,7 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver, u8 *cfg_buff = NULL; u8 *tbuff; char *cfg_name = NULL; + bool config_needed = false; switch (lmp_subver) { case RTL_ROM_LMP_8723B: @@ -344,6 +342,7 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver, break; case RTL_ROM_LMP_8822B: cfg_name = "rtl_bt/rtl8822b_config.bin"; + config_needed = true; break; default: BT_ERR("%s: rtl: no config according to lmp_subver %04x", @@ -353,8 +352,12 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver, if (cfg_name) { cfg_sz = rtl_load_config(hdev, cfg_name, &cfg_buff); - if (cfg_sz < 0) + if (cfg_sz < 0) { cfg_sz = 0; + if (config_needed) + BT_ERR("Necessary config file %s not found\n", + cfg_name); + } } else cfg_sz = 0; -- cgit From 017789f37b9b5ade5850c9d81c33a5b2b91ef087 Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Fri, 24 Feb 2017 14:24:29 +0800 Subject: Bluetooth: btusb: wake system up when receives a wake irq Currrently we are disabling this wake irq after receiving it. If this happens before we finish suspend and the pm event check is disabled, the system will continue suspending, and this irq would not work again. We may need to abort system suspend to avoid that. Signed-off-by: Jeffy Chen Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index eb78d235a34a..7fa373b428f8 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -2799,6 +2800,7 @@ static irqreturn_t btusb_oob_wake_handler(int irq, void *priv) struct btusb_data *data = priv; pm_wakeup_event(&data->udev->dev, 0); + pm_system_wakeup(); /* Disable only if not already disabled (keep it balanced) */ if (test_and_clear_bit(BTUSB_OOB_WAKE_ENABLED, &data->flags)) { -- cgit From 17e41ea6db92226df1acbe18e48b36bf59780d32 Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Fri, 24 Feb 2017 14:24:30 +0800 Subject: Bluetooth: btmrvl: wake system up when receives a wake irq Currrently we are disabling this wake irq after receiving it. If this happens before we finish suspend and the pm event check is disabled, the system will continue suspending, and this irq would not work again. We may need to abort system suspend to avoid that. Signed-off-by: Jeffy Chen Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmrvl_sdio.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 7d052ce2052b..e65ca19970ea 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -60,7 +61,8 @@ static const struct of_device_id btmrvl_sdio_of_match_table[] = { static irqreturn_t btmrvl_wake_irq_bt(int irq, void *priv) { - struct btmrvl_plt_wake_cfg *cfg = priv; + struct btmrvl_sdio_card *card = priv; + struct btmrvl_plt_wake_cfg *cfg = card->plt_wake_cfg; if (cfg->irq_bt >= 0) { pr_info("%s: wake by bt", __func__); @@ -68,6 +70,9 @@ static irqreturn_t btmrvl_wake_irq_bt(int irq, void *priv) disable_irq_nosync(irq); } + pm_wakeup_event(&card->func->dev, 0); + pm_system_wakeup(); + return IRQ_HANDLED; } @@ -101,7 +106,7 @@ static int btmrvl_sdio_probe_of(struct device *dev, } else { ret = devm_request_irq(dev, cfg->irq_bt, btmrvl_wake_irq_bt, - 0, "bt_wake", cfg); + 0, "bt_wake", card); if (ret) { dev_err(dev, "Failed to request irq_bt %d (%d)\n", -- cgit From 212d71833315c65644efc46223db61dee7b3c68e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 10 Mar 2017 14:28:20 +0200 Subject: Bluetooth: hci_bcm: Support platform enumeration Until now the driver supports only ACPI enumeration. Nevertheless Intel Edison SoM has Broadcom Wi-Fi + BT chip and neither ACPI nor DT enumeration mechanism. Enable pure platform driver in order to support Intel Edison SoM. Cc: Jarkko Nikula Signed-off-by: Andy Shevchenko Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_bcm.c | 50 ++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index c7d3c6842bcf..04fe5535a153 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -697,28 +697,14 @@ static int bcm_resource(struct acpi_resource *ares, void *data) /* Always tell the ACPI core to skip this resource */ return 1; } +#endif /* CONFIG_ACPI */ -static int bcm_acpi_probe(struct bcm_device *dev) +static int bcm_platform_probe(struct bcm_device *dev) { struct platform_device *pdev = dev->pdev; - LIST_HEAD(resources); - const struct dmi_system_id *dmi_id; - const struct acpi_gpio_mapping *gpio_mapping = acpi_bcm_int_last_gpios; - const struct acpi_device_id *id; - int ret; dev->name = dev_name(&pdev->dev); - /* Retrieve GPIO data */ - id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); - if (id) - gpio_mapping = (const struct acpi_gpio_mapping *) id->driver_data; - - ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev), - gpio_mapping); - if (ret) - return ret; - dev->clk = devm_clk_get(&pdev->dev, NULL); dev->device_wakeup = devm_gpiod_get_optional(&pdev->dev, @@ -755,6 +741,33 @@ static int bcm_acpi_probe(struct bcm_device *dev) return -EINVAL; } + return 0; +} + +#ifdef CONFIG_ACPI +static int bcm_acpi_probe(struct bcm_device *dev) +{ + struct platform_device *pdev = dev->pdev; + LIST_HEAD(resources); + const struct dmi_system_id *dmi_id; + const struct acpi_gpio_mapping *gpio_mapping = acpi_bcm_int_last_gpios; + const struct acpi_device_id *id; + int ret; + + /* Retrieve GPIO data */ + id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); + if (id) + gpio_mapping = (const struct acpi_gpio_mapping *) id->driver_data; + + ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev), + gpio_mapping); + if (ret) + return ret; + + ret = bcm_platform_probe(dev); + if (ret) + return ret; + /* Retrieve UART ACPI info */ ret = acpi_dev_get_resources(ACPI_COMPANION(&dev->pdev->dev), &resources, bcm_resource, dev); @@ -789,7 +802,10 @@ static int bcm_probe(struct platform_device *pdev) dev->pdev = pdev; - ret = bcm_acpi_probe(dev); + if (has_acpi_companion(&pdev->dev)) + ret = bcm_acpi_probe(dev); + else + ret = bcm_platform_probe(dev); if (ret) return ret; -- cgit From ded845a781a578dfb0b5b2c138e5a067aa3b1242 Mon Sep 17 00:00:00 2001 From: Harry Morris Date: Tue, 28 Mar 2017 13:08:58 +0100 Subject: ieee802154: Add CA8210 IEEE 802.15.4 device driver Add driver source and config for softMAC implementation of Cascoda's CA8210 IEEE 802.15.4 transceiver device. The driver mimics a common PHY-only implementation despite the CA8210 being a hardMAC device which exposes a SAP interface to the fully integrated MAC. The chip is a modem-only device with an integrated processor which runs the 802.15.4 MAC. The chip communicates via full-duplex SPI with additional pins for NIRQ and NRESET. The chip can also output its 16MHz clock to a GPIO with a configurable divider. The driver can be configured to implement a debugfs node that provides access to the SAP-based API to drive mechanisms not currently supported by the standard kernel interface. Signed-off-by: Harry Morris Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/Kconfig | 22 + drivers/net/ieee802154/Makefile | 1 + drivers/net/ieee802154/ca8210.c | 3240 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 3263 insertions(+) create mode 100644 drivers/net/ieee802154/ca8210.c diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig index 3057a8df4ce9..ce4864dc3c6e 100644 --- a/drivers/net/ieee802154/Kconfig +++ b/drivers/net/ieee802154/Kconfig @@ -82,3 +82,25 @@ config IEEE802154_ADF7242 This driver can also be built as a module. To do so, say M here. the module will be called 'adf7242'. + +config IEEE802154_CA8210 + tristate "Cascoda CA8210 transceiver driver" + depends on IEEE802154_DRIVERS && MAC802154 + depends on SPI + select COMMON_CLK + ---help--- + Say Y here to enable the CA8210 SPI 802.15.4 wireless + controller. + + This driver can also be built as a module. To do so, say M here. + the module will be called 'ca8210'. + +config IEEE802154_CA8210_DEBUGFS + bool "CA8210 debugfs interface" + depends on IEEE802154_CA8210 + depends on DEBUG_FS + ---help--- + This option compiles debugfs code for the ca8210 driver. This + exposes a debugfs node for each CA8210 instance which allows + direct use of the Cascoda API, exposing the 802.15.4 MAC + management entities. diff --git a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile index 3a923d339497..8374bb44a145 100644 --- a/drivers/net/ieee802154/Makefile +++ b/drivers/net/ieee802154/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_IEEE802154_MRF24J40) += mrf24j40.o obj-$(CONFIG_IEEE802154_CC2520) += cc2520.o obj-$(CONFIG_IEEE802154_ATUSB) += atusb.o obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o +obj-$(CONFIG_IEEE802154_CA8210) += ca8210.o diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c new file mode 100644 index 000000000000..53fa87bfede0 --- /dev/null +++ b/drivers/net/ieee802154/ca8210.c @@ -0,0 +1,3240 @@ +/* + * http://www.cascoda.com/products/ca-821x/ + * Copyright (c) 2016, Cascoda, Ltd. + * All rights reserved. + * + * This code is dual-licensed under both GPLv2 and 3-clause BSD. What follows is + * the license notice for both respectively. + * + ******************************************************************************* + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + ******************************************************************************* + * + * 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 name of the copyright holder 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 HOLDERS 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 +#include +#include + +#include +#include + +#define DRIVER_NAME "ca8210" + +/* external clock frequencies */ +#define ONE_MHZ 1000000 +#define TWO_MHZ (2 * ONE_MHZ) +#define FOUR_MHZ (4 * ONE_MHZ) +#define EIGHT_MHZ (8 * ONE_MHZ) +#define SIXTEEN_MHZ (16 * ONE_MHZ) + +/* spi constants */ +#define CA8210_SPI_BUF_SIZE 256 +#define CA8210_SYNC_TIMEOUT 1000 /* Timeout for synchronous commands [ms] */ + +/* test interface constants */ +#define CA8210_TEST_INT_FILE_NAME "ca8210_test" +#define CA8210_TEST_INT_FIFO_SIZE 256 + +/* MAC status enumerations */ +#define MAC_SUCCESS (0x00) +#define MAC_ERROR (0x01) +#define MAC_CANCELLED (0x02) +#define MAC_READY_FOR_POLL (0x03) +#define MAC_COUNTER_ERROR (0xDB) +#define MAC_IMPROPER_KEY_TYPE (0xDC) +#define MAC_IMPROPER_SECURITY_LEVEL (0xDD) +#define MAC_UNSUPPORTED_LEGACY (0xDE) +#define MAC_UNSUPPORTED_SECURITY (0xDF) +#define MAC_BEACON_LOST (0xE0) +#define MAC_CHANNEL_ACCESS_FAILURE (0xE1) +#define MAC_DENIED (0xE2) +#define MAC_DISABLE_TRX_FAILURE (0xE3) +#define MAC_SECURITY_ERROR (0xE4) +#define MAC_FRAME_TOO_LONG (0xE5) +#define MAC_INVALID_GTS (0xE6) +#define MAC_INVALID_HANDLE (0xE7) +#define MAC_INVALID_PARAMETER (0xE8) +#define MAC_NO_ACK (0xE9) +#define MAC_NO_BEACON (0xEA) +#define MAC_NO_DATA (0xEB) +#define MAC_NO_SHORT_ADDRESS (0xEC) +#define MAC_OUT_OF_CAP (0xED) +#define MAC_PAN_ID_CONFLICT (0xEE) +#define MAC_REALIGNMENT (0xEF) +#define MAC_TRANSACTION_EXPIRED (0xF0) +#define MAC_TRANSACTION_OVERFLOW (0xF1) +#define MAC_TX_ACTIVE (0xF2) +#define MAC_UNAVAILABLE_KEY (0xF3) +#define MAC_UNSUPPORTED_ATTRIBUTE (0xF4) +#define MAC_INVALID_ADDRESS (0xF5) +#define MAC_ON_TIME_TOO_LONG (0xF6) +#define MAC_PAST_TIME (0xF7) +#define MAC_TRACKING_OFF (0xF8) +#define MAC_INVALID_INDEX (0xF9) +#define MAC_LIMIT_REACHED (0xFA) +#define MAC_READ_ONLY (0xFB) +#define MAC_SCAN_IN_PROGRESS (0xFC) +#define MAC_SUPERFRAME_OVERLAP (0xFD) +#define MAC_SYSTEM_ERROR (0xFF) + +/* HWME attribute IDs */ +#define HWME_EDTHRESHOLD (0x04) +#define HWME_EDVALUE (0x06) +#define HWME_SYSCLKOUT (0x0F) +#define HWME_LQILIMIT (0x11) + +/* TDME attribute IDs */ +#define TDME_CHANNEL (0x00) +#define TDME_ATM_CONFIG (0x06) + +#define MAX_HWME_ATTRIBUTE_SIZE 16 +#define MAX_TDME_ATTRIBUTE_SIZE 2 + +/* PHY/MAC PIB Attribute Enumerations */ +#define PHY_CURRENT_CHANNEL (0x00) +#define PHY_TRANSMIT_POWER (0x02) +#define PHY_CCA_MODE (0x03) +#define MAC_ASSOCIATION_PERMIT (0x41) +#define MAC_AUTO_REQUEST (0x42) +#define MAC_BATT_LIFE_EXT (0x43) +#define MAC_BATT_LIFE_EXT_PERIODS (0x44) +#define MAC_BEACON_PAYLOAD (0x45) +#define MAC_BEACON_PAYLOAD_LENGTH (0x46) +#define MAC_BEACON_ORDER (0x47) +#define MAC_GTS_PERMIT (0x4d) +#define MAC_MAX_CSMA_BACKOFFS (0x4e) +#define MAC_MIN_BE (0x4f) +#define MAC_PAN_ID (0x50) +#define MAC_PROMISCUOUS_MODE (0x51) +#define MAC_RX_ON_WHEN_IDLE (0x52) +#define MAC_SHORT_ADDRESS (0x53) +#define MAC_SUPERFRAME_ORDER (0x54) +#define MAC_ASSOCIATED_PAN_COORD (0x56) +#define MAC_MAX_BE (0x57) +#define MAC_MAX_FRAME_RETRIES (0x59) +#define MAC_RESPONSE_WAIT_TIME (0x5A) +#define MAC_SECURITY_ENABLED (0x5D) + +#define MAC_AUTO_REQUEST_SECURITY_LEVEL (0x78) +#define MAC_AUTO_REQUEST_KEY_ID_MODE (0x79) + +#define NS_IEEE_ADDRESS (0xFF) /* Non-standard IEEE address */ + +/* MAC Address Mode Definitions */ +#define MAC_MODE_NO_ADDR (0x00) +#define MAC_MODE_SHORT_ADDR (0x02) +#define MAC_MODE_LONG_ADDR (0x03) + +/* MAC constants */ +#define MAX_BEACON_OVERHEAD (75) +#define MAX_BEACON_PAYLOAD_LENGTH (IEEE802154_MTU - MAX_BEACON_OVERHEAD) + +#define MAX_ATTRIBUTE_SIZE (122) +#define MAX_DATA_SIZE (114) + +#define CA8210_VALID_CHANNELS (0x07FFF800) + +/* MAC workarounds for V1.1 and MPW silicon (V0.x) */ +#define CA8210_MAC_WORKAROUNDS (0) +#define CA8210_MAC_MPW (0) + +/* memory manipulation macros */ +#define LS_BYTE(x) ((u8)((x) & 0xFF)) +#define MS_BYTE(x) ((u8)(((x) >> 8) & 0xFF)) + +/* message ID codes in SPI commands */ +/* downstream */ +#define MCPS_DATA_REQUEST (0x00) +#define MLME_ASSOCIATE_REQUEST (0x02) +#define MLME_ASSOCIATE_RESPONSE (0x03) +#define MLME_DISASSOCIATE_REQUEST (0x04) +#define MLME_GET_REQUEST (0x05) +#define MLME_ORPHAN_RESPONSE (0x06) +#define MLME_RESET_REQUEST (0x07) +#define MLME_RX_ENABLE_REQUEST (0x08) +#define MLME_SCAN_REQUEST (0x09) +#define MLME_SET_REQUEST (0x0A) +#define MLME_START_REQUEST (0x0B) +#define MLME_POLL_REQUEST (0x0D) +#define HWME_SET_REQUEST (0x0E) +#define HWME_GET_REQUEST (0x0F) +#define TDME_SETSFR_REQUEST (0x11) +#define TDME_GETSFR_REQUEST (0x12) +#define TDME_SET_REQUEST (0x14) +/* upstream */ +#define MCPS_DATA_INDICATION (0x00) +#define MCPS_DATA_CONFIRM (0x01) +#define MLME_RESET_CONFIRM (0x0A) +#define MLME_SET_CONFIRM (0x0E) +#define MLME_START_CONFIRM (0x0F) +#define HWME_SET_CONFIRM (0x12) +#define HWME_GET_CONFIRM (0x13) +#define HWME_WAKEUP_INDICATION (0x15) +#define TDME_SETSFR_CONFIRM (0x17) + +/* SPI command IDs */ +/* bit indicating a confirm or indication from slave to master */ +#define SPI_S2M (0x20) +/* bit indicating a synchronous message */ +#define SPI_SYN (0x40) + +/* SPI command definitions */ +#define SPI_IDLE (0xFF) +#define SPI_NACK (0xF0) + +#define SPI_MCPS_DATA_REQUEST (MCPS_DATA_REQUEST) +#define SPI_MCPS_DATA_INDICATION (MCPS_DATA_INDICATION + SPI_S2M) +#define SPI_MCPS_DATA_CONFIRM (MCPS_DATA_CONFIRM + SPI_S2M) + +#define SPI_MLME_ASSOCIATE_REQUEST (MLME_ASSOCIATE_REQUEST) +#define SPI_MLME_RESET_REQUEST (MLME_RESET_REQUEST + SPI_SYN) +#define SPI_MLME_SET_REQUEST (MLME_SET_REQUEST + SPI_SYN) +#define SPI_MLME_START_REQUEST (MLME_START_REQUEST + SPI_SYN) +#define SPI_MLME_RESET_CONFIRM (MLME_RESET_CONFIRM + SPI_S2M + SPI_SYN) +#define SPI_MLME_SET_CONFIRM (MLME_SET_CONFIRM + SPI_S2M + SPI_SYN) +#define SPI_MLME_START_CONFIRM (MLME_START_CONFIRM + SPI_S2M + SPI_SYN) + +#define SPI_HWME_SET_REQUEST (HWME_SET_REQUEST + SPI_SYN) +#define SPI_HWME_GET_REQUEST (HWME_GET_REQUEST + SPI_SYN) +#define SPI_HWME_SET_CONFIRM (HWME_SET_CONFIRM + SPI_S2M + SPI_SYN) +#define SPI_HWME_GET_CONFIRM (HWME_GET_CONFIRM + SPI_S2M + SPI_SYN) +#define SPI_HWME_WAKEUP_INDICATION (HWME_WAKEUP_INDICATION + SPI_S2M) + +#define SPI_TDME_SETSFR_REQUEST (TDME_SETSFR_REQUEST + SPI_SYN) +#define SPI_TDME_SET_REQUEST (TDME_SET_REQUEST + SPI_SYN) +#define SPI_TDME_SETSFR_CONFIRM (TDME_SETSFR_CONFIRM + SPI_S2M + SPI_SYN) + +/* TDME SFR addresses */ +/* Page 0 */ +#define CA8210_SFR_PACFG (0xB1) +#define CA8210_SFR_MACCON (0xD8) +#define CA8210_SFR_PACFGIB (0xFE) +/* Page 1 */ +#define CA8210_SFR_LOTXCAL (0xBF) +#define CA8210_SFR_PTHRH (0xD1) +#define CA8210_SFR_PRECFG (0xD3) +#define CA8210_SFR_LNAGX40 (0xE1) +#define CA8210_SFR_LNAGX41 (0xE2) +#define CA8210_SFR_LNAGX42 (0xE3) +#define CA8210_SFR_LNAGX43 (0xE4) +#define CA8210_SFR_LNAGX44 (0xE5) +#define CA8210_SFR_LNAGX45 (0xE6) +#define CA8210_SFR_LNAGX46 (0xE7) +#define CA8210_SFR_LNAGX47 (0xE9) + +#define PACFGIB_DEFAULT_CURRENT (0x3F) +#define PTHRH_DEFAULT_THRESHOLD (0x5A) +#define LNAGX40_DEFAULT_GAIN (0x29) /* 10dB */ +#define LNAGX41_DEFAULT_GAIN (0x54) /* 21dB */ +#define LNAGX42_DEFAULT_GAIN (0x6C) /* 27dB */ +#define LNAGX43_DEFAULT_GAIN (0x7A) /* 30dB */ +#define LNAGX44_DEFAULT_GAIN (0x84) /* 33dB */ +#define LNAGX45_DEFAULT_GAIN (0x8B) /* 34dB */ +#define LNAGX46_DEFAULT_GAIN (0x92) /* 36dB */ +#define LNAGX47_DEFAULT_GAIN (0x96) /* 37dB */ + +#define CA8210_IOCTL_HARD_RESET (0x00) + +/* Structs/Enums */ + +/** + * struct cas_control - spi transfer structure + * @msg: spi_message for each exchange + * @transfer: spi_transfer for each exchange + * @tx_buf: source array for transmission + * @tx_in_buf: array storing bytes received during transmission + * @priv: pointer to private data + * + * This structure stores all the necessary data passed around during a single + * spi exchange. + */ +struct cas_control { + struct spi_message msg; + struct spi_transfer transfer; + + u8 tx_buf[CA8210_SPI_BUF_SIZE]; + u8 tx_in_buf[CA8210_SPI_BUF_SIZE]; + + struct ca8210_priv *priv; +}; + +/** + * struct ca8210_test - ca8210 test interface structure + * @ca8210_dfs_spi_int: pointer to the entry in the debug fs for this device + * @up_fifo: fifo for upstream messages + * + * This structure stores all the data pertaining to the debug interface + */ +struct ca8210_test { + struct dentry *ca8210_dfs_spi_int; + struct kfifo up_fifo; + wait_queue_head_t readq; +}; + +/** + * struct ca8210_priv - ca8210 private data structure + * @spi: pointer to the ca8210 spi device object + * @hw: pointer to the ca8210 ieee802154_hw object + * @hw_registered: true if hw has been registered with ieee802154 + * @lock: spinlock protecting the private data area + * @mlme_workqueue: workqueue for triggering MLME Reset + * @irq_workqueue: workqueue for irq processing + * @tx_skb: current socket buffer to transmit + * @nextmsduhandle: msdu handle to pass to the 15.4 MAC layer for the + * next transmission + * @clk: external clock provided by the ca8210 + * @last_dsn: sequence number of last data packet received, for + * resend detection + * @test: test interface data section for this instance + * @async_tx_pending: true if an asynchronous transmission was started and + * is not complete + * @sync_command_response: pointer to buffer to fill with sync response + * @ca8210_is_awake: nonzero if ca8210 is initialised, ready for comms + * @sync_down: counts number of downstream synchronous commands + * @sync_up: counts number of upstream synchronous commands + * @spi_transfer_complete completion object for a single spi_transfer + * @sync_exchange_complete completion object for a complete synchronous API + * exchange + * @promiscuous whether the ca8210 is in promiscuous mode or not + * @retries: records how many times the current pending spi + * transfer has been retried + */ +struct ca8210_priv { + struct spi_device *spi; + struct ieee802154_hw *hw; + bool hw_registered; + spinlock_t lock; + struct workqueue_struct *mlme_workqueue; + struct workqueue_struct *irq_workqueue; + struct sk_buff *tx_skb; + u8 nextmsduhandle; + struct clk *clk; + int last_dsn; + struct ca8210_test test; + bool async_tx_pending; + u8 *sync_command_response; + struct completion ca8210_is_awake; + int sync_down, sync_up; + struct completion spi_transfer_complete, sync_exchange_complete; + bool promiscuous; + int retries; +}; + +/** + * struct work_priv_container - link between a work object and the relevant + * device's private data + * @work: work object being executed + * @priv: device's private data section + * + */ +struct work_priv_container { + struct work_struct work; + struct ca8210_priv *priv; +}; + +/** + * struct ca8210_platform_data - ca8210 platform data structure + * @extclockenable: true if the external clock is to be enabled + * @extclockfreq: frequency of the external clock + * @extclockgpio: ca8210 output gpio of the external clock + * @gpio_reset: gpio number of ca8210 reset line + * @gpio_irq: gpio number of ca8210 interrupt line + * @irq_id: identifier for the ca8210 irq + * + */ +struct ca8210_platform_data { + bool extclockenable; + unsigned int extclockfreq; + unsigned int extclockgpio; + int gpio_reset; + int gpio_irq; + int irq_id; +}; + +/** + * struct fulladdr - full MAC addressing information structure + * @mode: address mode (none, short, extended) + * @pan_id: 16-bit LE pan id + * @address: LE address, variable length as specified by mode + * + */ +struct fulladdr { + u8 mode; + u8 pan_id[2]; + u8 address[8]; +}; + +/** + * union macaddr: generic MAC address container + * @short_addr: 16-bit short address + * @ieee_address: 64-bit extended address as LE byte array + * + */ +union macaddr { + u16 short_address; + u8 ieee_address[8]; +}; + +/** + * struct secspec: security specification for SAP commands + * @security_level: 0-7, controls level of authentication & encryption + * @key_id_mode: 0-3, specifies how to obtain key + * @key_source: extended key retrieval data + * @key_index: single-byte key identifier + * + */ +struct secspec { + u8 security_level; + u8 key_id_mode; + u8 key_source[8]; + u8 key_index; +}; + +/* downlink functions parameter set definitions */ +struct mcps_data_request_pset { + u8 src_addr_mode; + struct fulladdr dst; + u8 msdu_length; + u8 msdu_handle; + u8 tx_options; + u8 msdu[MAX_DATA_SIZE]; +}; + +struct mlme_set_request_pset { + u8 pib_attribute; + u8 pib_attribute_index; + u8 pib_attribute_length; + u8 pib_attribute_value[MAX_ATTRIBUTE_SIZE]; +}; + +struct hwme_set_request_pset { + u8 hw_attribute; + u8 hw_attribute_length; + u8 hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE]; +}; + +struct hwme_get_request_pset { + u8 hw_attribute; +}; + +struct tdme_setsfr_request_pset { + u8 sfr_page; + u8 sfr_address; + u8 sfr_value; +}; + +/* uplink functions parameter set definitions */ +struct hwme_set_confirm_pset { + u8 status; + u8 hw_attribute; +}; + +struct hwme_get_confirm_pset { + u8 status; + u8 hw_attribute; + u8 hw_attribute_length; + u8 hw_attribute_value[MAX_HWME_ATTRIBUTE_SIZE]; +}; + +struct tdme_setsfr_confirm_pset { + u8 status; + u8 sfr_page; + u8 sfr_address; +}; + +struct mac_message { + u8 command_id; + u8 length; + union { + struct mcps_data_request_pset data_req; + struct mlme_set_request_pset set_req; + struct hwme_set_request_pset hwme_set_req; + struct hwme_get_request_pset hwme_get_req; + struct tdme_setsfr_request_pset tdme_set_sfr_req; + struct hwme_set_confirm_pset hwme_set_cnf; + struct hwme_get_confirm_pset hwme_get_cnf; + struct tdme_setsfr_confirm_pset tdme_set_sfr_cnf; + u8 u8param; + u8 status; + u8 payload[148]; + } pdata; +}; + +union pa_cfg_sfr { + struct { + u8 bias_current_trim : 3; + u8 /* reserved */ : 1; + u8 buffer_capacitor_trim : 3; + u8 boost : 1; + }; + u8 paib; +}; + +struct preamble_cfg_sfr { + u8 timeout_symbols : 3; + u8 acquisition_symbols : 3; + u8 search_symbols : 2; +}; + +static int (*cascoda_api_upstream)( + const u8 *buf, + size_t len, + void *device_ref +); + +/** + * link_to_linux_err() - Translates an 802.15.4 return code into the closest + * linux error + * @link_status: 802.15.4 status code + * + * Return: 0 or Linux error code + */ +static int link_to_linux_err(int link_status) +{ + if (link_status < 0) { + /* status is already a Linux code */ + return link_status; + } + switch (link_status) { + case MAC_SUCCESS: + case MAC_REALIGNMENT: + return 0; + case MAC_IMPROPER_KEY_TYPE: + return -EKEYREJECTED; + case MAC_IMPROPER_SECURITY_LEVEL: + case MAC_UNSUPPORTED_LEGACY: + case MAC_DENIED: + return -EACCES; + case MAC_BEACON_LOST: + case MAC_NO_ACK: + case MAC_NO_BEACON: + return -ENETUNREACH; + case MAC_CHANNEL_ACCESS_FAILURE: + case MAC_TX_ACTIVE: + case MAC_SCAN_IN_PROGRESS: + return -EBUSY; + case MAC_DISABLE_TRX_FAILURE: + case MAC_OUT_OF_CAP: + return -EAGAIN; + case MAC_FRAME_TOO_LONG: + return -EMSGSIZE; + case MAC_INVALID_GTS: + case MAC_PAST_TIME: + return -EBADSLT; + case MAC_INVALID_HANDLE: + return -EBADMSG; + case MAC_INVALID_PARAMETER: + case MAC_UNSUPPORTED_ATTRIBUTE: + case MAC_ON_TIME_TOO_LONG: + case MAC_INVALID_INDEX: + return -EINVAL; + case MAC_NO_DATA: + return -ENODATA; + case MAC_NO_SHORT_ADDRESS: + return -EFAULT; + case MAC_PAN_ID_CONFLICT: + return -EADDRINUSE; + case MAC_TRANSACTION_EXPIRED: + return -ETIME; + case MAC_TRANSACTION_OVERFLOW: + return -ENOBUFS; + case MAC_UNAVAILABLE_KEY: + return -ENOKEY; + case MAC_INVALID_ADDRESS: + return -ENXIO; + case MAC_TRACKING_OFF: + case MAC_SUPERFRAME_OVERLAP: + return -EREMOTEIO; + case MAC_LIMIT_REACHED: + return -EDQUOT; + case MAC_READ_ONLY: + return -EROFS; + default: + return -EPROTO; + } +} + +/** + * ca8210_test_int_driver_write() - Writes a message to the test interface to be + * read by the userspace + * @buf: Buffer containing upstream message + * @len: length of message to write + * @spi: SPI device of message originator + * + * Return: 0 or linux error code + */ +static int ca8210_test_int_driver_write( + const u8 *buf, + size_t len, + void *spi +) +{ + struct ca8210_priv *priv = spi_get_drvdata(spi); + struct ca8210_test *test = &priv->test; + char *fifo_buffer; + int i; + + dev_dbg( + &priv->spi->dev, + "test_interface: Buffering upstream message:\n" + ); + for (i = 0; i < len; i++) + dev_dbg(&priv->spi->dev, "%#03x\n", buf[i]); + + fifo_buffer = kmalloc(len, GFP_KERNEL); + memcpy(fifo_buffer, buf, len); + kfifo_in(&test->up_fifo, &fifo_buffer, 4); + wake_up_interruptible(&priv->test.readq); + + return 0; +} + +/* SPI Operation */ + +static int ca8210_net_rx( + struct ieee802154_hw *hw, + u8 *command, + size_t len +); +static u8 mlme_reset_request_sync( + u8 set_default_pib, + void *device_ref +); +static int ca8210_spi_transfer( + struct spi_device *spi, + const u8 *buf, + size_t len +); + +/** + * ca8210_reset_send() - Hard resets the ca8210 for a given time + * @spi: Pointer to target ca8210 spi device + * @ms: Milliseconds to hold the reset line low for + */ +static void ca8210_reset_send(struct spi_device *spi, unsigned int ms) +{ + struct ca8210_platform_data *pdata = spi->dev.platform_data; + struct ca8210_priv *priv = spi_get_drvdata(spi); + long status; + + gpio_set_value(pdata->gpio_reset, 0); + reinit_completion(&priv->ca8210_is_awake); + msleep(ms); + gpio_set_value(pdata->gpio_reset, 1); + priv->promiscuous = false; + + /* Wait until wakeup indication seen */ + status = wait_for_completion_interruptible_timeout( + &priv->ca8210_is_awake, + msecs_to_jiffies(CA8210_SYNC_TIMEOUT) + ); + if (status == 0) { + dev_crit( + &spi->dev, + "Fatal: No wakeup from ca8210 after reset!\n" + ); + } + + dev_dbg(&spi->dev, "Reset the device\n"); +} + +/** + * ca8210_mlme_reset_worker() - Resets the MLME, Called when the MAC OVERFLOW + * condition happens. + * @work: Pointer to work being executed + */ +static void ca8210_mlme_reset_worker(struct work_struct *work) +{ + struct work_priv_container *wpc = container_of( + work, + struct work_priv_container, + work + ); + struct ca8210_priv *priv = wpc->priv; + + mlme_reset_request_sync(0, priv->spi); + kfree(wpc); +} + +/** + * ca8210_rx_done() - Calls various message dispatches responding to a received + * command + * @arg: Pointer to the cas_control object for the relevant spi transfer + * + * Presents a received SAP command from the ca8210 to the Cascoda EVBME, test + * interface and network driver. + */ +static void ca8210_rx_done(struct cas_control *cas_ctl) +{ + u8 *buf; + u8 len; + struct work_priv_container *mlme_reset_wpc; + struct ca8210_priv *priv = cas_ctl->priv; + + buf = cas_ctl->tx_in_buf; + len = buf[1] + 2; + if (len > CA8210_SPI_BUF_SIZE) { + dev_crit( + &priv->spi->dev, + "Received packet len (%d) erroneously long\n", + len + ); + goto finish; + } + + if (buf[0] & SPI_SYN) { + if (priv->sync_command_response) { + memcpy(priv->sync_command_response, buf, len); + complete(&priv->sync_exchange_complete); + } else { + if (cascoda_api_upstream) + cascoda_api_upstream(buf, len, priv->spi); + priv->sync_up++; + } + } else { + if (cascoda_api_upstream) + cascoda_api_upstream(buf, len, priv->spi); + } + + ca8210_net_rx(priv->hw, buf, len); + if (buf[0] == SPI_MCPS_DATA_CONFIRM) { + if (buf[3] == MAC_TRANSACTION_OVERFLOW) { + dev_info( + &priv->spi->dev, + "Waiting for transaction overflow to stabilise...\n"); + msleep(2000); + dev_info( + &priv->spi->dev, + "Resetting MAC...\n"); + + mlme_reset_wpc = kmalloc( + sizeof(struct work_priv_container), + GFP_KERNEL + ); + INIT_WORK( + &mlme_reset_wpc->work, + ca8210_mlme_reset_worker + ); + mlme_reset_wpc->priv = priv; + queue_work(priv->mlme_workqueue, &mlme_reset_wpc->work); + } + } else if (buf[0] == SPI_HWME_WAKEUP_INDICATION) { + dev_notice( + &priv->spi->dev, + "Wakeup indication received, reason:\n" + ); + switch (buf[2]) { + case 0: + dev_notice( + &priv->spi->dev, + "Transceiver woken up from Power Up / System Reset\n" + ); + break; + case 1: + dev_notice( + &priv->spi->dev, + "Watchdog Timer Time-Out\n" + ); + break; + case 2: + dev_notice( + &priv->spi->dev, + "Transceiver woken up from Power-Off by Sleep Timer Time-Out\n"); + break; + case 3: + dev_notice( + &priv->spi->dev, + "Transceiver woken up from Power-Off by GPIO Activity\n" + ); + break; + case 4: + dev_notice( + &priv->spi->dev, + "Transceiver woken up from Standby by Sleep Timer Time-Out\n" + ); + break; + case 5: + dev_notice( + &priv->spi->dev, + "Transceiver woken up from Standby by GPIO Activity\n" + ); + break; + case 6: + dev_notice( + &priv->spi->dev, + "Sleep-Timer Time-Out in Active Mode\n" + ); + break; + default: + dev_warn(&priv->spi->dev, "Wakeup reason unknown\n"); + break; + } + complete(&priv->ca8210_is_awake); + } + +finish:; +} + +static int ca8210_remove(struct spi_device *spi_device); + +/** + * ca8210_spi_transfer_complete() - Called when a single spi transfer has + * completed + * @context: Pointer to the cas_control object for the finished transfer + */ +static void ca8210_spi_transfer_complete(void *context) +{ + struct cas_control *cas_ctl = context; + struct ca8210_priv *priv = cas_ctl->priv; + bool duplex_rx = false; + int i; + u8 retry_buffer[CA8210_SPI_BUF_SIZE]; + + if ( + cas_ctl->tx_in_buf[0] == SPI_NACK || + (cas_ctl->tx_in_buf[0] == SPI_IDLE && + cas_ctl->tx_in_buf[1] == SPI_NACK) + ) { + /* ca8210 is busy */ + dev_info(&priv->spi->dev, "ca8210 was busy during attempted write\n"); + if (cas_ctl->tx_buf[0] == SPI_IDLE) { + dev_warn( + &priv->spi->dev, + "IRQ servicing NACKd, dropping transfer\n" + ); + kfree(cas_ctl); + return; + } + if (priv->retries > 3) { + dev_err(&priv->spi->dev, "too many retries!\n"); + kfree(cas_ctl); + ca8210_remove(priv->spi); + return; + } + memcpy(retry_buffer, cas_ctl->tx_buf, CA8210_SPI_BUF_SIZE); + kfree(cas_ctl); + ca8210_spi_transfer( + priv->spi, + retry_buffer, + CA8210_SPI_BUF_SIZE + ); + priv->retries++; + dev_info(&priv->spi->dev, "retried spi write\n"); + return; + } else if ( + cas_ctl->tx_in_buf[0] != SPI_IDLE && + cas_ctl->tx_in_buf[0] != SPI_NACK + ) { + duplex_rx = true; + } + + if (duplex_rx) { + dev_dbg(&priv->spi->dev, "READ CMD DURING TX\n"); + for (i = 0; i < cas_ctl->tx_in_buf[1] + 2; i++) + dev_dbg( + &priv->spi->dev, + "%#03x\n", + cas_ctl->tx_in_buf[i] + ); + ca8210_rx_done(cas_ctl); + } + complete(&priv->spi_transfer_complete); + kfree(cas_ctl); + priv->retries = 0; +} + +/** + * ca8210_spi_transfer() - Initiate duplex spi transfer with ca8210 + * @spi: Pointer to spi device for transfer + * @buf: Octet array to send + * @len: length of the buffer being sent + * + * Return: 0 or linux error code + */ +static int ca8210_spi_transfer( + struct spi_device *spi, + const u8 *buf, + size_t len +) +{ + int i, status = 0; + struct ca8210_priv *priv = spi_get_drvdata(spi); + struct cas_control *cas_ctl; + + if (!spi) { + dev_crit( + &spi->dev, + "NULL spi device passed to ca8210_spi_transfer\n" + ); + return -ENODEV; + } + + reinit_completion(&priv->spi_transfer_complete); + + dev_dbg(&spi->dev, "ca8210_spi_transfer called\n"); + + cas_ctl = kmalloc( + sizeof(struct cas_control), + GFP_ATOMIC + ); + cas_ctl->priv = priv; + memset(cas_ctl->tx_buf, SPI_IDLE, CA8210_SPI_BUF_SIZE); + memset(cas_ctl->tx_in_buf, SPI_IDLE, CA8210_SPI_BUF_SIZE); + memcpy(cas_ctl->tx_buf, buf, len); + + for (i = 0; i < len; i++) + dev_dbg(&spi->dev, "%#03x\n", cas_ctl->tx_buf[i]); + + spi_message_init(&cas_ctl->msg); + + cas_ctl->transfer.tx_nbits = 1; /* 1 MOSI line */ + cas_ctl->transfer.rx_nbits = 1; /* 1 MISO line */ + cas_ctl->transfer.speed_hz = 0; /* Use device setting */ + cas_ctl->transfer.bits_per_word = 0; /* Use device setting */ + cas_ctl->transfer.tx_buf = cas_ctl->tx_buf; + cas_ctl->transfer.rx_buf = cas_ctl->tx_in_buf; + cas_ctl->transfer.delay_usecs = 0; + cas_ctl->transfer.cs_change = 0; + cas_ctl->transfer.len = sizeof(struct mac_message); + cas_ctl->msg.complete = ca8210_spi_transfer_complete; + cas_ctl->msg.context = cas_ctl; + + spi_message_add_tail( + &cas_ctl->transfer, + &cas_ctl->msg + ); + + status = spi_async(spi, &cas_ctl->msg); + if (status < 0) { + dev_crit( + &spi->dev, + "status %d from spi_sync in write\n", + status + ); + } + + return status; +} + +/** + * ca8210_spi_exchange() - Exchange API/SAP commands with the radio + * @buf: Octet array of command being sent downstream + * @len: length of buf + * @response: buffer for storing synchronous response + * @device_ref: spi_device pointer for ca8210 + * + * Effectively calls ca8210_spi_transfer to write buf[] to the spi, then for + * synchronous commands waits for the corresponding response to be read from + * the spi before returning. The response is written to the response parameter. + * + * Return: 0 or linux error code + */ +static int ca8210_spi_exchange( + const u8 *buf, + size_t len, + u8 *response, + void *device_ref +) +{ + int status = 0; + struct spi_device *spi = device_ref; + struct ca8210_priv *priv = spi->dev.driver_data; + long wait_remaining; + + if ((buf[0] & SPI_SYN) && response) { /* if sync wait for confirm */ + reinit_completion(&priv->sync_exchange_complete); + priv->sync_command_response = response; + } + + do { + reinit_completion(&priv->spi_transfer_complete); + status = ca8210_spi_transfer(priv->spi, buf, len); + if (status) { + dev_warn( + &spi->dev, + "spi write failed, returned %d\n", + status + ); + if (status == -EBUSY) + continue; + if (((buf[0] & SPI_SYN) && response)) + complete(&priv->sync_exchange_complete); + goto cleanup; + } + + wait_remaining = wait_for_completion_interruptible_timeout( + &priv->spi_transfer_complete, + msecs_to_jiffies(1000) + ); + if (wait_remaining == -ERESTARTSYS) { + status = -ERESTARTSYS; + } else if (wait_remaining == 0) { + dev_err( + &spi->dev, + "SPI downstream transfer timed out!\n" + ); + status = -ETIME; + goto cleanup; + } + } while (status < 0); + + if (!((buf[0] & SPI_SYN) && response)) + goto cleanup; + + wait_remaining = wait_for_completion_interruptible_timeout( + &priv->sync_exchange_complete, + msecs_to_jiffies(CA8210_SYNC_TIMEOUT) + ); + if (wait_remaining == -ERESTARTSYS) { + status = -ERESTARTSYS; + } else if (wait_remaining == 0) { + dev_err( + &spi->dev, + "Synchronous confirm timeout\n" + ); + status = -ETIME; + } + +cleanup: + priv->sync_command_response = NULL; + return status; +} + +/** + * ca8210_interrupt_handler() - Called when an irq is received from the ca8210 + * @irq: Id of the irq being handled + * @dev_id: Pointer passed by the system, pointing to the ca8210's private data + * + * This function is called when the irq line from the ca8210 is asserted, + * signifying that the ca8210 has a message to send upstream to us. Starts the + * asynchronous spi read. + * + * Return: irq return code + */ +static irqreturn_t ca8210_interrupt_handler(int irq, void *dev_id) +{ + struct ca8210_priv *priv = dev_id; + int status; + + dev_dbg(&priv->spi->dev, "irq: Interrupt occurred\n"); + do { + status = ca8210_spi_transfer(priv->spi, NULL, 0); + if (status && (status != -EBUSY)) { + dev_warn( + &priv->spi->dev, + "spi read failed, returned %d\n", + status + ); + } + } while (status == -EBUSY); + return IRQ_HANDLED; +} + +static int (*cascoda_api_downstream)( + const u8 *buf, + size_t len, + u8 *response, + void *device_ref +) = ca8210_spi_exchange; + +/* Cascoda API / 15.4 SAP Primitives */ + +/** + * tdme_setsfr_request_sync() - TDME_SETSFR_request/confirm according to API + * @sfr_page: SFR Page + * @sfr_address: SFR Address + * @sfr_value: SFR Value + * @device_ref: Nondescript pointer to target device + * + * Return: 802.15.4 status code of TDME-SETSFR.confirm + */ +static u8 tdme_setsfr_request_sync( + u8 sfr_page, + u8 sfr_address, + u8 sfr_value, + void *device_ref +) +{ + int ret; + struct mac_message command, response; + struct spi_device *spi = device_ref; + + command.command_id = SPI_TDME_SETSFR_REQUEST; + command.length = 3; + command.pdata.tdme_set_sfr_req.sfr_page = sfr_page; + command.pdata.tdme_set_sfr_req.sfr_address = sfr_address; + command.pdata.tdme_set_sfr_req.sfr_value = sfr_value; + response.command_id = SPI_IDLE; + ret = cascoda_api_downstream( + &command.command_id, + command.length + 2, + &response.command_id, + device_ref + ); + if (ret) { + dev_crit(&spi->dev, "cascoda_api_downstream returned %d", ret); + return MAC_SYSTEM_ERROR; + } + + if (response.command_id != SPI_TDME_SETSFR_CONFIRM) { + dev_crit( + &spi->dev, + "sync response to SPI_TDME_SETSFR_REQUEST was not SPI_TDME_SETSFR_CONFIRM, it was %d\n", + response.command_id + ); + return MAC_SYSTEM_ERROR; + } + + return response.pdata.tdme_set_sfr_cnf.status; +} + +/** + * tdme_chipinit() - TDME Chip Register Default Initialisation Macro + * @device_ref: Nondescript pointer to target device + * + * Return: 802.15.4 status code of API calls + */ +static u8 tdme_chipinit(void *device_ref) +{ + u8 status = MAC_SUCCESS; + u8 sfr_address; + struct spi_device *spi = device_ref; + struct preamble_cfg_sfr pre_cfg_value = { + .timeout_symbols = 3, + .acquisition_symbols = 3, + .search_symbols = 1, + }; + /* LNA Gain Settings */ + status = tdme_setsfr_request_sync( + 1, (sfr_address = CA8210_SFR_LNAGX40), + LNAGX40_DEFAULT_GAIN, device_ref); + if (status) + goto finish; + status = tdme_setsfr_request_sync( + 1, (sfr_address = CA8210_SFR_LNAGX41), + LNAGX41_DEFAULT_GAIN, device_ref); + if (status) + goto finish; + status = tdme_setsfr_request_sync( + 1, (sfr_address = CA8210_SFR_LNAGX42), + LNAGX42_DEFAULT_GAIN, device_ref); + if (status) + goto finish; + status = tdme_setsfr_request_sync( + 1, (sfr_address = CA8210_SFR_LNAGX43), + LNAGX43_DEFAULT_GAIN, device_ref); + if (status) + goto finish; + status = tdme_setsfr_request_sync( + 1, (sfr_address = CA8210_SFR_LNAGX44), + LNAGX44_DEFAULT_GAIN, device_ref); + if (status) + goto finish; + status = tdme_setsfr_request_sync( + 1, (sfr_address = CA8210_SFR_LNAGX45), + LNAGX45_DEFAULT_GAIN, device_ref); + if (status) + goto finish; + status = tdme_setsfr_request_sync( + 1, (sfr_address = CA8210_SFR_LNAGX46), + LNAGX46_DEFAULT_GAIN, device_ref); + if (status) + goto finish; + status = tdme_setsfr_request_sync( + 1, (sfr_address = CA8210_SFR_LNAGX47), + LNAGX47_DEFAULT_GAIN, device_ref); + if (status) + goto finish; + /* Preamble Timing Config */ + status = tdme_setsfr_request_sync( + 1, (sfr_address = CA8210_SFR_PRECFG), + *((u8 *)&pre_cfg_value), device_ref); + if (status) + goto finish; + /* Preamble Threshold High */ + status = tdme_setsfr_request_sync( + 1, (sfr_address = CA8210_SFR_PTHRH), + PTHRH_DEFAULT_THRESHOLD, device_ref); + if (status) + goto finish; + /* Tx Output Power 8 dBm */ + status = tdme_setsfr_request_sync( + 0, (sfr_address = CA8210_SFR_PACFGIB), + PACFGIB_DEFAULT_CURRENT, device_ref); + if (status) + goto finish; + +finish: + if (status != MAC_SUCCESS) { + dev_err( + &spi->dev, + "failed to set sfr at %#03x, status = %#03x\n", + sfr_address, + status + ); + } + return status; +} + +/** + * tdme_channelinit() - TDME Channel Register Default Initialisation Macro (Tx) + * @channel: 802.15.4 channel to initialise chip for + * @device_ref: Nondescript pointer to target device + * + * Return: 802.15.4 status code of API calls + */ +static u8 tdme_channelinit(u8 channel, void *device_ref) +{ + /* Transceiver front-end local oscillator tx two-point calibration + * value. Tuned for the hardware. + */ + u8 txcalval; + + if (channel >= 25) + txcalval = 0xA7; + else if (channel >= 23) + txcalval = 0xA8; + else if (channel >= 22) + txcalval = 0xA9; + else if (channel >= 20) + txcalval = 0xAA; + else if (channel >= 17) + txcalval = 0xAB; + else if (channel >= 16) + txcalval = 0xAC; + else if (channel >= 14) + txcalval = 0xAD; + else if (channel >= 12) + txcalval = 0xAE; + else + txcalval = 0xAF; + + return tdme_setsfr_request_sync( + 1, + CA8210_SFR_LOTXCAL, + txcalval, + device_ref + ); /* LO Tx Cal */ +} + +/** + * tdme_checkpibattribute() - Checks Attribute Values that are not checked in + * MAC + * @pib_attribute: Attribute Number + * @pib_attribute_length: Attribute length + * @pib_attribute_value: Pointer to Attribute Value + * @device_ref: Nondescript pointer to target device + * + * Return: 802.15.4 status code of checks + */ +static u8 tdme_checkpibattribute( + u8 pib_attribute, + u8 pib_attribute_length, + const void *pib_attribute_value +) +{ + u8 status = MAC_SUCCESS; + u8 value; + + value = *((u8 *)pib_attribute_value); + + switch (pib_attribute) { + /* PHY */ + case PHY_TRANSMIT_POWER: + if (value > 0x3F) + status = MAC_INVALID_PARAMETER; + break; + case PHY_CCA_MODE: + if (value > 0x03) + status = MAC_INVALID_PARAMETER; + break; + /* MAC */ + case MAC_BATT_LIFE_EXT_PERIODS: + if ((value < 6) || (value > 41)) + status = MAC_INVALID_PARAMETER; + break; + case MAC_BEACON_PAYLOAD: + if (pib_attribute_length > MAX_BEACON_PAYLOAD_LENGTH) + status = MAC_INVALID_PARAMETER; + break; + case MAC_BEACON_PAYLOAD_LENGTH: + if (value > MAX_BEACON_PAYLOAD_LENGTH) + status = MAC_INVALID_PARAMETER; + break; + case MAC_BEACON_ORDER: + if (value > 15) + status = MAC_INVALID_PARAMETER; + break; + case MAC_MAX_BE: + if ((value < 3) || (value > 8)) + status = MAC_INVALID_PARAMETER; + break; + case MAC_MAX_CSMA_BACKOFFS: + if (value > 5) + status = MAC_INVALID_PARAMETER; + break; + case MAC_MAX_FRAME_RETRIES: + if (value > 7) + status = MAC_INVALID_PARAMETER; + break; + case MAC_MIN_BE: + if (value > 8) + status = MAC_INVALID_PARAMETER; + break; + case MAC_RESPONSE_WAIT_TIME: + if ((value < 2) || (value > 64)) + status = MAC_INVALID_PARAMETER; + break; + case MAC_SUPERFRAME_ORDER: + if (value > 15) + status = MAC_INVALID_PARAMETER; + break; + /* boolean */ + case MAC_ASSOCIATED_PAN_COORD: + case MAC_ASSOCIATION_PERMIT: + case MAC_AUTO_REQUEST: + case MAC_BATT_LIFE_EXT: + case MAC_GTS_PERMIT: + case MAC_PROMISCUOUS_MODE: + case MAC_RX_ON_WHEN_IDLE: + case MAC_SECURITY_ENABLED: + if (value > 1) + status = MAC_INVALID_PARAMETER; + break; + /* MAC SEC */ + case MAC_AUTO_REQUEST_SECURITY_LEVEL: + if (value > 7) + status = MAC_INVALID_PARAMETER; + break; + case MAC_AUTO_REQUEST_KEY_ID_MODE: + if (value > 3) + status = MAC_INVALID_PARAMETER; + break; + default: + break; + } + + return status; +} + +/** + * tdme_settxpower() - Sets the tx power for MLME_SET phyTransmitPower + * @txp: Transmit Power + * @device_ref: Nondescript pointer to target device + * + * Normalised to 802.15.4 Definition (6-bit, signed): + * Bit 7-6: not used + * Bit 5-0: tx power (-32 - +31 dB) + * + * Return: 802.15.4 status code of api calls + */ +static u8 tdme_settxpower(u8 txp, void *device_ref) +{ + u8 status; + s8 txp_val; + u8 txp_ext; + union pa_cfg_sfr pa_cfg_val; + + /* extend from 6 to 8 bit */ + txp_ext = 0x3F & txp; + if (txp_ext & 0x20) + txp_ext += 0xC0; + txp_val = (s8)txp_ext; + + if (CA8210_MAC_MPW) { + if (txp_val > 0) { + /* 8 dBm: ptrim = 5, itrim = +3 => +4 dBm */ + pa_cfg_val.bias_current_trim = 3; + pa_cfg_val.buffer_capacitor_trim = 5; + pa_cfg_val.boost = 1; + } else { + /* 0 dBm: ptrim = 7, itrim = +3 => -6 dBm */ + pa_cfg_val.bias_current_trim = 3; + pa_cfg_val.buffer_capacitor_trim = 7; + pa_cfg_val.boost = 0; + } + /* write PACFG */ + status = tdme_setsfr_request_sync( + 0, + CA8210_SFR_PACFG, + pa_cfg_val.paib, + device_ref + ); + } else { + /* Look-Up Table for Setting Current and Frequency Trim values + * for desired Output Power + */ + if (txp_val > 8) { + pa_cfg_val.paib = 0x3F; + } else if (txp_val == 8) { + pa_cfg_val.paib = 0x32; + } else if (txp_val == 7) { + pa_cfg_val.paib = 0x22; + } else if (txp_val == 6) { + pa_cfg_val.paib = 0x18; + } else if (txp_val == 5) { + pa_cfg_val.paib = 0x10; + } else if (txp_val == 4) { + pa_cfg_val.paib = 0x0C; + } else if (txp_val == 3) { + pa_cfg_val.paib = 0x08; + } else if (txp_val == 2) { + pa_cfg_val.paib = 0x05; + } else if (txp_val == 1) { + pa_cfg_val.paib = 0x03; + } else if (txp_val == 0) { + pa_cfg_val.paib = 0x01; + } else { /* < 0 */ + pa_cfg_val.paib = 0x00; + } + /* write PACFGIB */ + status = tdme_setsfr_request_sync( + 0, + CA8210_SFR_PACFGIB, + pa_cfg_val.paib, + device_ref + ); + } + + return status; +} + +/** + * mcps_data_request() - mcps_data_request (Send Data) according to API Spec + * @src_addr_mode: Source Addressing Mode + * @dst_address_mode: Destination Addressing Mode + * @dst_pan_id: Destination PAN ID + * @dst_addr: Pointer to Destination Address + * @msdu_length: length of Data + * @msdu: Pointer to Data + * @msdu_handle: Handle of Data + * @tx_options: Tx Options Bit Field + * @security: Pointer to Security Structure or NULL + * @device_ref: Nondescript pointer to target device + * + * Return: 802.15.4 status code of action + */ +static u8 mcps_data_request( + u8 src_addr_mode, + u8 dst_address_mode, + u16 dst_pan_id, + union macaddr *dst_addr, + u8 msdu_length, + u8 *msdu, + u8 msdu_handle, + u8 tx_options, + struct secspec *security, + void *device_ref +) +{ + struct secspec *psec; + struct mac_message command; + + command.command_id = SPI_MCPS_DATA_REQUEST; + command.pdata.data_req.src_addr_mode = src_addr_mode; + command.pdata.data_req.dst.mode = dst_address_mode; + if (dst_address_mode != MAC_MODE_NO_ADDR) { + command.pdata.data_req.dst.pan_id[0] = LS_BYTE(dst_pan_id); + command.pdata.data_req.dst.pan_id[1] = MS_BYTE(dst_pan_id); + if (dst_address_mode == MAC_MODE_SHORT_ADDR) { + command.pdata.data_req.dst.address[0] = LS_BYTE( + dst_addr->short_address + ); + command.pdata.data_req.dst.address[1] = MS_BYTE( + dst_addr->short_address + ); + } else { /* MAC_MODE_LONG_ADDR*/ + memcpy( + command.pdata.data_req.dst.address, + dst_addr->ieee_address, + 8 + ); + } + } + command.pdata.data_req.msdu_length = msdu_length; + command.pdata.data_req.msdu_handle = msdu_handle; + command.pdata.data_req.tx_options = tx_options; + memcpy(command.pdata.data_req.msdu, msdu, msdu_length); + psec = (struct secspec *)(command.pdata.data_req.msdu + msdu_length); + command.length = sizeof(struct mcps_data_request_pset) - + MAX_DATA_SIZE + msdu_length; + if (!security || (security->security_level == 0)) { + psec->security_level = 0; + command.length += 1; + } else { + *psec = *security; + command.length += sizeof(struct secspec); + } + + if (ca8210_spi_transfer(device_ref, &command.command_id, + command.length + 2)) + return MAC_SYSTEM_ERROR; + + return MAC_SUCCESS; +} + +/** + * mlme_reset_request_sync() - MLME_RESET_request/confirm according to API Spec + * @set_default_pib: Set defaults in PIB + * @device_ref: Nondescript pointer to target device + * + * Return: 802.15.4 status code of MLME-RESET.confirm + */ +static u8 mlme_reset_request_sync( + u8 set_default_pib, + void *device_ref +) +{ + u8 status; + struct mac_message command, response; + struct spi_device *spi = device_ref; + + command.command_id = SPI_MLME_RESET_REQUEST; + command.length = 1; + command.pdata.u8param = set_default_pib; + + if (cascoda_api_downstream( + &command.command_id, + command.length + 2, + &response.command_id, + device_ref)) { + dev_err(&spi->dev, "cascoda_api_downstream failed\n"); + return MAC_SYSTEM_ERROR; + } + + if (response.command_id != SPI_MLME_RESET_CONFIRM) + return MAC_SYSTEM_ERROR; + + status = response.pdata.status; + + /* reset COORD Bit for Channel Filtering as Coordinator */ + if (CA8210_MAC_WORKAROUNDS && set_default_pib && (!status)) { + status = tdme_setsfr_request_sync( + 0, + CA8210_SFR_MACCON, + 0, + device_ref + ); + } + + return status; +} + +/** + * mlme_set_request_sync() - MLME_SET_request/confirm according to API Spec + * @pib_attribute: Attribute Number + * @pib_attribute_index: Index within Attribute if an Array + * @pib_attribute_length: Attribute length + * @pib_attribute_value: Pointer to Attribute Value + * @device_ref: Nondescript pointer to target device + * + * Return: 802.15.4 status code of MLME-SET.confirm + */ +static u8 mlme_set_request_sync( + u8 pib_attribute, + u8 pib_attribute_index, + u8 pib_attribute_length, + const void *pib_attribute_value, + void *device_ref +) +{ + u8 status; + struct mac_message command, response; + + /* pre-check the validity of pib_attribute values that are not checked + * in MAC + */ + if (tdme_checkpibattribute( + pib_attribute, pib_attribute_length, pib_attribute_value)) { + return MAC_INVALID_PARAMETER; + } + + if (pib_attribute == PHY_CURRENT_CHANNEL) { + status = tdme_channelinit( + *((u8 *)pib_attribute_value), + device_ref + ); + if (status) + return status; + } + + if (pib_attribute == PHY_TRANSMIT_POWER) { + return tdme_settxpower( + *((u8 *)pib_attribute_value), + device_ref + ); + } + + command.command_id = SPI_MLME_SET_REQUEST; + command.length = sizeof(struct mlme_set_request_pset) - + MAX_ATTRIBUTE_SIZE + pib_attribute_length; + command.pdata.set_req.pib_attribute = pib_attribute; + command.pdata.set_req.pib_attribute_index = pib_attribute_index; + command.pdata.set_req.pib_attribute_length = pib_attribute_length; + memcpy( + command.pdata.set_req.pib_attribute_value, + pib_attribute_value, + pib_attribute_length + ); + + if (cascoda_api_downstream( + &command.command_id, + command.length + 2, + &response.command_id, + device_ref)) { + return MAC_SYSTEM_ERROR; + } + + if (response.command_id != SPI_MLME_SET_CONFIRM) + return MAC_SYSTEM_ERROR; + + return response.pdata.status; +} + +/** + * hwme_set_request_sync() - HWME_SET_request/confirm according to API Spec + * @hw_attribute: Attribute Number + * @hw_attribute_length: Attribute length + * @hw_attribute_value: Pointer to Attribute Value + * @device_ref: Nondescript pointer to target device + * + * Return: 802.15.4 status code of HWME-SET.confirm + */ +static u8 hwme_set_request_sync( + u8 hw_attribute, + u8 hw_attribute_length, + u8 *hw_attribute_value, + void *device_ref +) +{ + struct mac_message command, response; + + command.command_id = SPI_HWME_SET_REQUEST; + command.length = 2 + hw_attribute_length; + command.pdata.hwme_set_req.hw_attribute = hw_attribute; + command.pdata.hwme_set_req.hw_attribute_length = hw_attribute_length; + memcpy( + command.pdata.hwme_set_req.hw_attribute_value, + hw_attribute_value, + hw_attribute_length + ); + + if (cascoda_api_downstream( + &command.command_id, + command.length + 2, + &response.command_id, + device_ref)) { + return MAC_SYSTEM_ERROR; + } + + if (response.command_id != SPI_HWME_SET_CONFIRM) + return MAC_SYSTEM_ERROR; + + return response.pdata.hwme_set_cnf.status; +} + +/** + * hwme_get_request_sync() - HWME_GET_request/confirm according to API Spec + * @hw_attribute: Attribute Number + * @hw_attribute_length: Attribute length + * @hw_attribute_value: Pointer to Attribute Value + * @device_ref: Nondescript pointer to target device + * + * Return: 802.15.4 status code of HWME-GET.confirm + */ +static u8 hwme_get_request_sync( + u8 hw_attribute, + u8 *hw_attribute_length, + u8 *hw_attribute_value, + void *device_ref +) +{ + struct mac_message command, response; + + command.command_id = SPI_HWME_GET_REQUEST; + command.length = 1; + command.pdata.hwme_get_req.hw_attribute = hw_attribute; + + if (cascoda_api_downstream( + &command.command_id, + command.length + 2, + &response.command_id, + device_ref)) { + return MAC_SYSTEM_ERROR; + } + + if (response.command_id != SPI_HWME_GET_CONFIRM) + return MAC_SYSTEM_ERROR; + + if (response.pdata.hwme_get_cnf.status == MAC_SUCCESS) { + *hw_attribute_length = + response.pdata.hwme_get_cnf.hw_attribute_length; + memcpy( + hw_attribute_value, + response.pdata.hwme_get_cnf.hw_attribute_value, + *hw_attribute_length + ); + } + + return response.pdata.hwme_get_cnf.status; +} + +/* Network driver operation */ + +/** + * ca8210_async_xmit_complete() - Called to announce that an asynchronous + * transmission has finished + * @hw: ieee802154_hw of ca8210 that has finished exchange + * @msduhandle: Identifier of transmission that has completed + * @status: Returned 802.15.4 status code of the transmission + * + * Return: 0 or linux error code + */ +static int ca8210_async_xmit_complete( + struct ieee802154_hw *hw, + u8 msduhandle, + u8 status) +{ + struct ca8210_priv *priv = hw->priv; + + if (priv->nextmsduhandle != msduhandle) { + dev_err( + &priv->spi->dev, + "Unexpected msdu_handle on data confirm, Expected %d, got %d\n", + priv->nextmsduhandle, + msduhandle + ); + return -EIO; + } + + priv->async_tx_pending = false; + priv->nextmsduhandle++; + + if (status) { + dev_err( + &priv->spi->dev, + "Link transmission unsuccessful, status = %d\n", + status + ); + if (status != MAC_TRANSACTION_OVERFLOW) { + ieee802154_wake_queue(priv->hw); + return 0; + } + } + ieee802154_xmit_complete(priv->hw, priv->tx_skb, true); + + return 0; +} + +/** + * ca8210_skb_rx() - Contructs a properly framed socket buffer from a received + * MCPS_DATA_indication + * @hw: ieee802154_hw that MCPS_DATA_indication was received by + * @len: length of MCPS_DATA_indication + * @data_ind: Octet array of MCPS_DATA_indication + * + * Called by the spi driver whenever a SAP command is received, this function + * will ascertain whether the command is of interest to the network driver and + * take necessary action. + * + * Return: 0 or linux error code + */ +static int ca8210_skb_rx( + struct ieee802154_hw *hw, + size_t len, + u8 *data_ind +) +{ + struct ieee802154_hdr hdr; + int msdulen; + int hlen; + u8 mpdulinkquality = data_ind[23]; + struct sk_buff *skb; + struct ca8210_priv *priv = hw->priv; + + /* Allocate mtu size buffer for every rx packet */ + skb = dev_alloc_skb(IEEE802154_MTU + sizeof(hdr)); + if (!skb) { + dev_crit(&priv->spi->dev, "dev_alloc_skb failed\n"); + return -ENOMEM; + } + skb_reserve(skb, sizeof(hdr)); + + msdulen = data_ind[22]; /* msdu_length */ + if (msdulen > IEEE802154_MTU) { + dev_err( + &priv->spi->dev, + "received erroneously large msdu length!\n" + ); + kfree_skb(skb); + return -EMSGSIZE; + } + dev_dbg(&priv->spi->dev, "skb buffer length = %d\n", msdulen); + + if (priv->promiscuous) + goto copy_payload; + + /* Populate hdr */ + hdr.sec.level = data_ind[29 + msdulen]; + dev_dbg(&priv->spi->dev, "security level: %#03x\n", hdr.sec.level); + if (hdr.sec.level > 0) { + hdr.sec.key_id_mode = data_ind[30 + msdulen]; + memcpy(&hdr.sec.extended_src, &data_ind[31 + msdulen], 8); + hdr.sec.key_id = data_ind[39 + msdulen]; + } + hdr.source.mode = data_ind[0]; + dev_dbg(&priv->spi->dev, "srcAddrMode: %#03x\n", hdr.source.mode); + hdr.source.pan_id = *(u16 *)&data_ind[1]; + dev_dbg(&priv->spi->dev, "srcPanId: %#06x\n", hdr.source.pan_id); + memcpy(&hdr.source.extended_addr, &data_ind[3], 8); + hdr.dest.mode = data_ind[11]; + dev_dbg(&priv->spi->dev, "dstAddrMode: %#03x\n", hdr.dest.mode); + hdr.dest.pan_id = *(u16 *)&data_ind[12]; + dev_dbg(&priv->spi->dev, "dstPanId: %#06x\n", hdr.dest.pan_id); + memcpy(&hdr.dest.extended_addr, &data_ind[14], 8); + + /* Fill in FC implicitly */ + hdr.fc.type = 1; /* Data frame */ + if (hdr.sec.level) + hdr.fc.security_enabled = 1; + else + hdr.fc.security_enabled = 0; + if (data_ind[1] != data_ind[12] || data_ind[2] != data_ind[13]) + hdr.fc.intra_pan = 1; + else + hdr.fc.intra_pan = 0; + hdr.fc.dest_addr_mode = hdr.dest.mode; + hdr.fc.source_addr_mode = hdr.source.mode; + + /* Add hdr to front of buffer */ + hlen = ieee802154_hdr_push(skb, &hdr); + + if (hlen < 0) { + dev_crit(&priv->spi->dev, "failed to push mac hdr onto skb!\n"); + kfree_skb(skb); + return hlen; + } + + skb_reset_mac_header(skb); + skb->mac_len = hlen; + +copy_payload: + /* Add bytes of space to the back of the buffer */ + /* Copy msdu to skb */ + memcpy(skb_put(skb, msdulen), &data_ind[29], msdulen); + + ieee802154_rx_irqsafe(hw, skb, mpdulinkquality); + return 0; +} + +/** + * ca8210_net_rx() - Acts upon received SAP commands relevant to the network + * driver + * @hw: ieee802154_hw that command was received by + * @command: Octet array of received command + * @len: length of the received command + * + * Called by the spi driver whenever a SAP command is received, this function + * will ascertain whether the command is of interest to the network driver and + * take necessary action. + * + * Return: 0 or linux error code + */ +static int ca8210_net_rx(struct ieee802154_hw *hw, u8 *command, size_t len) +{ + struct ca8210_priv *priv = hw->priv; + unsigned long flags; + u8 status; + + dev_dbg(&priv->spi->dev, "ca8210_net_rx(), CmdID = %d\n", command[0]); + + if (command[0] == SPI_MCPS_DATA_INDICATION) { + /* Received data */ + spin_lock_irqsave(&priv->lock, flags); + if (command[26] == priv->last_dsn) { + dev_dbg( + &priv->spi->dev, + "DSN %d resend received, ignoring...\n", + command[26] + ); + spin_unlock_irqrestore(&priv->lock, flags); + return 0; + } + priv->last_dsn = command[26]; + spin_unlock_irqrestore(&priv->lock, flags); + return ca8210_skb_rx(hw, len - 2, command + 2); + } else if (command[0] == SPI_MCPS_DATA_CONFIRM) { + status = command[3]; + if (priv->async_tx_pending) { + return ca8210_async_xmit_complete( + hw, + command[2], + status + ); + } + } + + return 0; +} + +/** + * ca8210_skb_tx() - Transmits a given socket buffer using the ca8210 + * @skb: Socket buffer to transmit + * @msduhandle: Data identifier to pass to the 802.15.4 MAC + * @priv: Pointer to private data section of target ca8210 + * + * Return: 0 or linux error code + */ +static int ca8210_skb_tx( + struct sk_buff *skb, + u8 msduhandle, + struct ca8210_priv *priv +) +{ + int status; + struct ieee802154_hdr header = { 0 }; + struct secspec secspec; + unsigned int mac_len; + + dev_dbg(&priv->spi->dev, "ca8210_skb_tx() called\n"); + + /* Get addressing info from skb - ieee802154 layer creates a full + * packet + */ + mac_len = ieee802154_hdr_peek_addrs(skb, &header); + + secspec.security_level = header.sec.level; + secspec.key_id_mode = header.sec.key_id_mode; + if (secspec.key_id_mode == 2) + memcpy(secspec.key_source, &header.sec.short_src, 4); + else if (secspec.key_id_mode == 3) + memcpy(secspec.key_source, &header.sec.extended_src, 8); + secspec.key_index = header.sec.key_id; + + /* Pass to Cascoda API */ + status = mcps_data_request( + header.source.mode, + header.dest.mode, + header.dest.pan_id, + (union macaddr *)&header.dest.extended_addr, + skb->len - mac_len, + &skb->data[mac_len], + msduhandle, + header.fc.ack_request, + &secspec, + priv->spi + ); + return link_to_linux_err(status); +} + +/** + * ca8210_start() - Starts the network driver + * @hw: ieee802154_hw of ca8210 being started + * + * Return: 0 or linux error code + */ +static int ca8210_start(struct ieee802154_hw *hw) +{ + int status; + u8 rx_on_when_idle; + u8 lqi_threshold = 0; + struct ca8210_priv *priv = hw->priv; + + priv->last_dsn = -1; + /* Turn receiver on when idle for now just to test rx */ + rx_on_when_idle = 1; + status = mlme_set_request_sync( + MAC_RX_ON_WHEN_IDLE, + 0, + 1, + &rx_on_when_idle, + priv->spi + ); + if (status) { + dev_crit( + &priv->spi->dev, + "Setting rx_on_when_idle failed, status = %d\n", + status + ); + return link_to_linux_err(status); + } + status = hwme_set_request_sync( + HWME_LQILIMIT, + 1, + &lqi_threshold, + priv->spi + ); + if (status) { + dev_crit( + &priv->spi->dev, + "Setting lqilimit failed, status = %d\n", + status + ); + return link_to_linux_err(status); + } + + return 0; +} + +/** + * ca8210_stop() - Stops the network driver + * @hw: ieee802154_hw of ca8210 being stopped + * + * Return: 0 or linux error code + */ +static void ca8210_stop(struct ieee802154_hw *hw) +{ +} + +/** + * ca8210_xmit_async() - Asynchronously transmits a given socket buffer using + * the ca8210 + * @hw: ieee802154_hw of ca8210 to transmit from + * @skb: Socket buffer to transmit + * + * Return: 0 or linux error code + */ +static int ca8210_xmit_async(struct ieee802154_hw *hw, struct sk_buff *skb) +{ + struct ca8210_priv *priv = hw->priv; + int status; + + dev_dbg(&priv->spi->dev, "calling ca8210_xmit_async()\n"); + + priv->tx_skb = skb; + priv->async_tx_pending = true; + status = ca8210_skb_tx(skb, priv->nextmsduhandle, priv); + return status; +} + +/** + * ca8210_get_ed() - Returns the measured energy on the current channel at this + * instant in time + * @hw: ieee802154_hw of target ca8210 + * @level: Measured Energy Detect level + * + * Return: 0 or linux error code + */ +static int ca8210_get_ed(struct ieee802154_hw *hw, u8 *level) +{ + u8 lenvar; + struct ca8210_priv *priv = hw->priv; + + return link_to_linux_err( + hwme_get_request_sync(HWME_EDVALUE, &lenvar, level, priv->spi) + ); +} + +/** + * ca8210_set_channel() - Sets the current operating 802.15.4 channel of the + * ca8210 + * @hw: ieee802154_hw of target ca8210 + * @page: Channel page to set + * @channel: Channel number to set + * + * Return: 0 or linux error code + */ +static int ca8210_set_channel( + struct ieee802154_hw *hw, + u8 page, + u8 channel +) +{ + u8 status; + struct ca8210_priv *priv = hw->priv; + + status = mlme_set_request_sync( + PHY_CURRENT_CHANNEL, + 0, + 1, + &channel, + priv->spi + ); + if (status) { + dev_err( + &priv->spi->dev, + "error setting channel, MLME-SET.confirm status = %d\n", + status + ); + } + return link_to_linux_err(status); +} + +/** + * ca8210_set_hw_addr_filt() - Sets the address filtering parameters of the + * ca8210 + * @hw: ieee802154_hw of target ca8210 + * @filt: Filtering parameters + * @changed: Bitmap representing which parameters to change + * + * Effectively just sets the actual addressing information identifying this node + * as all filtering is performed by the ca8210 as detailed in the IEEE 802.15.4 + * 2006 specification. + * + * Return: 0 or linux error code + */ +static int ca8210_set_hw_addr_filt( + struct ieee802154_hw *hw, + struct ieee802154_hw_addr_filt *filt, + unsigned long changed +) +{ + u8 status = 0; + struct ca8210_priv *priv = hw->priv; + + if (changed & IEEE802154_AFILT_PANID_CHANGED) { + status = mlme_set_request_sync( + MAC_PAN_ID, + 0, + 2, + &filt->pan_id, priv->spi + ); + if (status) { + dev_err( + &priv->spi->dev, + "error setting pan id, MLME-SET.confirm status = %d", + status + ); + return link_to_linux_err(status); + } + } + if (changed & IEEE802154_AFILT_SADDR_CHANGED) { + status = mlme_set_request_sync( + MAC_SHORT_ADDRESS, + 0, + 2, + &filt->short_addr, priv->spi + ); + if (status) { + dev_err( + &priv->spi->dev, + "error setting short address, MLME-SET.confirm status = %d", + status + ); + return link_to_linux_err(status); + } + } + if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) { + status = mlme_set_request_sync( + NS_IEEE_ADDRESS, + 0, + 8, + &filt->ieee_addr, + priv->spi + ); + if (status) { + dev_err( + &priv->spi->dev, + "error setting ieee address, MLME-SET.confirm status = %d", + status + ); + return link_to_linux_err(status); + } + } + /* TODO: Should use MLME_START to set coord bit? */ + return 0; +} + +/** + * ca8210_set_tx_power() - Sets the transmit power of the ca8210 + * @hw: ieee802154_hw of target ca8210 + * @mbm: Transmit power in mBm (dBm*100) + * + * Return: 0 or linux error code + */ +static int ca8210_set_tx_power(struct ieee802154_hw *hw, s32 mbm) +{ + struct ca8210_priv *priv = hw->priv; + + mbm /= 100; + return link_to_linux_err( + mlme_set_request_sync(PHY_TRANSMIT_POWER, 0, 1, &mbm, priv->spi) + ); +} + +/** + * ca8210_set_cca_mode() - Sets the clear channel assessment mode of the ca8210 + * @hw: ieee802154_hw of target ca8210 + * @cca: CCA mode to set + * + * Return: 0 or linux error code + */ +static int ca8210_set_cca_mode( + struct ieee802154_hw *hw, + const struct wpan_phy_cca *cca +) +{ + u8 status; + u8 cca_mode; + struct ca8210_priv *priv = hw->priv; + + cca_mode = cca->mode & 3; + if (cca_mode == 3 && cca->opt == NL802154_CCA_OPT_ENERGY_CARRIER_OR) { + /* cca_mode 0 == CS OR ED, 3 == CS AND ED */ + cca_mode = 0; + } + status = mlme_set_request_sync( + PHY_CCA_MODE, + 0, + 1, + &cca_mode, + priv->spi + ); + if (status) { + dev_err( + &priv->spi->dev, + "error setting cca mode, MLME-SET.confirm status = %d", + status + ); + } + return link_to_linux_err(status); +} + +/** + * ca8210_set_cca_ed_level() - Sets the CCA ED level of the ca8210 + * @hw: ieee802154_hw of target ca8210 + * @level: ED level to set (in mbm) + * + * Sets the minimum threshold of measured energy above which the ca8210 will + * back off and retry a transmission. + * + * Return: 0 or linux error code + */ +static int ca8210_set_cca_ed_level(struct ieee802154_hw *hw, s32 level) +{ + u8 status; + u8 ed_threshold = (level / 100) * 2 + 256; + struct ca8210_priv *priv = hw->priv; + + status = hwme_set_request_sync( + HWME_EDTHRESHOLD, + 1, + &ed_threshold, + priv->spi + ); + if (status) { + dev_err( + &priv->spi->dev, + "error setting ed threshold, HWME-SET.confirm status = %d", + status + ); + } + return link_to_linux_err(status); +} + +/** + * ca8210_set_csma_params() - Sets the CSMA parameters of the ca8210 + * @hw: ieee802154_hw of target ca8210 + * @min_be: Minimum backoff exponent when backing off a transmission + * @max_be: Maximum backoff exponent when backing off a transmission + * @retries: Number of times to retry after backing off + * + * Return: 0 or linux error code + */ +static int ca8210_set_csma_params( + struct ieee802154_hw *hw, + u8 min_be, + u8 max_be, + u8 retries +) +{ + u8 status; + struct ca8210_priv *priv = hw->priv; + + status = mlme_set_request_sync(MAC_MIN_BE, 0, 1, &min_be, priv->spi); + if (status) { + dev_err( + &priv->spi->dev, + "error setting min be, MLME-SET.confirm status = %d", + status + ); + return link_to_linux_err(status); + } + status = mlme_set_request_sync(MAC_MAX_BE, 0, 1, &max_be, priv->spi); + if (status) { + dev_err( + &priv->spi->dev, + "error setting max be, MLME-SET.confirm status = %d", + status + ); + return link_to_linux_err(status); + } + status = mlme_set_request_sync( + MAC_MAX_CSMA_BACKOFFS, + 0, + 1, + &retries, + priv->spi + ); + if (status) { + dev_err( + &priv->spi->dev, + "error setting max csma backoffs, MLME-SET.confirm status = %d", + status + ); + } + return link_to_linux_err(status); +} + +/** + * ca8210_set_frame_retries() - Sets the maximum frame retries of the ca8210 + * @hw: ieee802154_hw of target ca8210 + * @retries: Number of retries + * + * Sets the number of times to retry a transmission if no acknowledgment was + * was received from the other end when one was requested. + * + * Return: 0 or linux error code + */ +static int ca8210_set_frame_retries(struct ieee802154_hw *hw, s8 retries) +{ + u8 status; + struct ca8210_priv *priv = hw->priv; + + status = mlme_set_request_sync( + MAC_MAX_FRAME_RETRIES, + 0, + 1, + &retries, + priv->spi + ); + if (status) { + dev_err( + &priv->spi->dev, + "error setting frame retries, MLME-SET.confirm status = %d", + status + ); + } + return link_to_linux_err(status); +} + +static int ca8210_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on) +{ + u8 status; + struct ca8210_priv *priv = hw->priv; + + status = mlme_set_request_sync( + MAC_PROMISCUOUS_MODE, + 0, + 1, + (const void*)&on, + priv->spi + ); + if (status) { + dev_err( + &priv->spi->dev, + "error setting promiscuous mode, MLME-SET.confirm status = %d", + status + ); + } else { + priv->promiscuous = on; + } + return link_to_linux_err(status); +} + +static const struct ieee802154_ops ca8210_phy_ops = { + .start = ca8210_start, + .stop = ca8210_stop, + .xmit_async = ca8210_xmit_async, + .ed = ca8210_get_ed, + .set_channel = ca8210_set_channel, + .set_hw_addr_filt = ca8210_set_hw_addr_filt, + .set_txpower = ca8210_set_tx_power, + .set_cca_mode = ca8210_set_cca_mode, + .set_cca_ed_level = ca8210_set_cca_ed_level, + .set_csma_params = ca8210_set_csma_params, + .set_frame_retries = ca8210_set_frame_retries, + .set_promiscuous_mode = ca8210_set_promiscuous_mode +}; + +/* Test/EVBME Interface */ + +/** + * ca8210_test_int_open() - Opens the test interface to the userspace + * @inodp: inode representation of file interface + * @filp: file interface + * + * Return: 0 or linux error code + */ +static int ca8210_test_int_open(struct inode *inodp, struct file *filp) +{ + struct ca8210_priv *priv = inodp->i_private; + + filp->private_data = priv; + return 0; +} + +/** + * ca8210_test_check_upstream() - Checks a command received from the upstream + * testing interface for required action + * @buf: Buffer containing command to check + * @device_ref: Nondescript pointer to target device + * + * Return: 0 or linux error code + */ +static int ca8210_test_check_upstream(u8 *buf, void *device_ref) +{ + int ret; + u8 response[CA8210_SPI_BUF_SIZE]; + + if (buf[0] == SPI_MLME_SET_REQUEST) { + ret = tdme_checkpibattribute(buf[2], buf[4], buf + 5); + if (ret) { + response[0] = SPI_MLME_SET_CONFIRM; + response[1] = 3; + response[2] = MAC_INVALID_PARAMETER; + response[3] = buf[2]; + response[4] = buf[3]; + if (cascoda_api_upstream) + cascoda_api_upstream(response, 5, device_ref); + return ret; + } + } + if (buf[0] == SPI_MLME_ASSOCIATE_REQUEST) { + return tdme_channelinit(buf[2], device_ref); + } else if (buf[0] == SPI_MLME_START_REQUEST) { + return tdme_channelinit(buf[4], device_ref); + } else if ( + (buf[0] == SPI_MLME_SET_REQUEST) && + (buf[2] == PHY_CURRENT_CHANNEL) + ) { + return tdme_channelinit(buf[5], device_ref); + } else if ( + (buf[0] == SPI_TDME_SET_REQUEST) && + (buf[2] == TDME_CHANNEL) + ) { + return tdme_channelinit(buf[4], device_ref); + } else if ( + (CA8210_MAC_WORKAROUNDS) && + (buf[0] == SPI_MLME_RESET_REQUEST) && + (buf[2] == 1) + ) { + /* reset COORD Bit for Channel Filtering as Coordinator */ + return tdme_setsfr_request_sync( + 0, + CA8210_SFR_MACCON, + 0, + device_ref + ); + } + return 0; +} /* End of EVBMECheckSerialCommand() */ + +/** + * ca8210_test_int_user_write() - Called by a process in userspace to send a + * message to the ca8210 drivers + * @filp: file interface + * @in_buf: Buffer containing message to write + * @len: length of message + * @off: file offset + * + * Return: 0 or linux error code + */ +static ssize_t ca8210_test_int_user_write( + struct file *filp, + const char __user *in_buf, + size_t len, + loff_t *off +) +{ + int ret; + struct ca8210_priv *priv = filp->private_data; + u8 command[CA8210_SPI_BUF_SIZE]; + + if (len > CA8210_SPI_BUF_SIZE) { + dev_warn( + &priv->spi->dev, + "userspace requested erroneously long write (%zu)\n", + len + ); + return -EMSGSIZE; + } + + ret = copy_from_user(command, in_buf, len); + if (ret) { + dev_err( + &priv->spi->dev, + "%d bytes could not be copied from userspace\n", + ret + ); + return -EIO; + } + + ret = ca8210_test_check_upstream(command, priv->spi); + if (ret == 0) { + ret = ca8210_spi_exchange( + command, + command[1] + 2, + NULL, + priv->spi + ); + if (ret < 0) { + /* effectively 0 bytes were written successfully */ + dev_err( + &priv->spi->dev, + "spi exchange failed\n" + ); + return ret; + } + if (command[0] & SPI_SYN) + priv->sync_down++; + } + + return len; +} + +/** + * ca8210_test_int_user_read() - Called by a process in userspace to read a + * message from the ca8210 drivers + * @filp: file interface + * @buf: Buffer to write message to + * @len: length of message to read (ignored) + * @offp: file offset + * + * If the O_NONBLOCK flag was set when opening the file then this function will + * not block, i.e. it will return if the fifo is empty. Otherwise the function + * will block, i.e. wait until new data arrives. + * + * Return: number of bytes read + */ +static ssize_t ca8210_test_int_user_read( + struct file *filp, + char __user *buf, + size_t len, + loff_t *offp +) +{ + int i, cmdlen; + struct ca8210_priv *priv = filp->private_data; + unsigned char *fifo_buffer; + unsigned long bytes_not_copied; + + if (filp->f_flags & O_NONBLOCK) { + /* Non-blocking mode */ + if (kfifo_is_empty(&priv->test.up_fifo)) + return 0; + } else { + /* Blocking mode */ + wait_event_interruptible( + priv->test.readq, + !kfifo_is_empty(&priv->test.up_fifo) + ); + } + + if (kfifo_out(&priv->test.up_fifo, &fifo_buffer, 4) != 4) { + dev_err( + &priv->spi->dev, + "test_interface: Wrong number of elements popped from upstream fifo\n" + ); + return 0; + } + cmdlen = fifo_buffer[1]; + bytes_not_copied = cmdlen + 2; + + bytes_not_copied = copy_to_user(buf, fifo_buffer, bytes_not_copied); + if (bytes_not_copied > 0) { + dev_err( + &priv->spi->dev, + "%lu bytes could not be copied to user space!\n", + bytes_not_copied + ); + } + + dev_dbg(&priv->spi->dev, "test_interface: Cmd len = %d\n", cmdlen); + + dev_dbg(&priv->spi->dev, "test_interface: Read\n"); + for (i = 0; i < cmdlen + 2; i++) + dev_dbg(&priv->spi->dev, "%#03x\n", fifo_buffer[i]); + + kfree(fifo_buffer); + + return cmdlen + 2; +} + +/** + * ca8210_test_int_ioctl() - Called by a process in userspace to enact an + * arbitrary action + * @filp: file interface + * @ioctl_num: which action to enact + * @ioctl_param: arbitrary parameter for the action + * + * Return: status + */ +static long ca8210_test_int_ioctl( + struct file *filp, + unsigned int ioctl_num, + unsigned long ioctl_param +) +{ + struct ca8210_priv *priv = filp->private_data; + + switch (ioctl_num) { + case CA8210_IOCTL_HARD_RESET: + ca8210_reset_send(priv->spi, ioctl_param); + break; + default: + break; + } + return 0; +} + +/** + * ca8210_test_int_poll() - Called by a process in userspace to determine which + * actions are currently possible for the file + * @filp: file interface + * @ptable: poll table + * + * Return: set of poll return flags + */ +static unsigned int ca8210_test_int_poll( + struct file *filp, + struct poll_table_struct *ptable +) +{ + unsigned int return_flags = 0; + struct ca8210_priv *priv = filp->private_data; + + poll_wait(filp, &priv->test.readq, ptable); + if (!kfifo_is_empty(&priv->test.up_fifo)) + return_flags |= (POLLIN | POLLRDNORM); + if (wait_event_interruptible( + priv->test.readq, + !kfifo_is_empty(&priv->test.up_fifo))) { + return POLLERR; + } + return return_flags; +} + +static const struct file_operations test_int_fops = { + .read = ca8210_test_int_user_read, + .write = ca8210_test_int_user_write, + .open = ca8210_test_int_open, + .release = NULL, + .unlocked_ioctl = ca8210_test_int_ioctl, + .poll = ca8210_test_int_poll +}; + +/* Init/Deinit */ + +/** + * ca8210_get_platform_data() - Populate a ca8210_platform_data object + * @spi_device: Pointer to ca8210 spi device object to get data for + * @pdata: Pointer to ca8210_platform_data object to populate + * + * Return: 0 or linux error code + */ +static int ca8210_get_platform_data( + struct spi_device *spi_device, + struct ca8210_platform_data *pdata +) +{ + int ret = 0; + + if (!spi_device->dev.of_node) + return -EINVAL; + + pdata->extclockenable = of_property_read_bool( + spi_device->dev.of_node, + "extclock-enable" + ); + if (pdata->extclockenable) { + ret = of_property_read_u32( + spi_device->dev.of_node, + "extclock-freq", + &pdata->extclockfreq + ); + if (ret < 0) + return ret; + + ret = of_property_read_u32( + spi_device->dev.of_node, + "extclock-gpio", + &pdata->extclockgpio + ); + } + + return ret; +} + +/** + * ca8210_config_extern_clk() - Configure the external clock provided by the + * ca8210 + * @pdata: Pointer to ca8210_platform_data containing clock parameters + * @spi: Pointer to target ca8210 spi device + * @on: True to turn the clock on, false to turn off + * + * The external clock is configured with a frequency and output pin taken from + * the platform data. + * + * Return: 0 or linux error code + */ +static int ca8210_config_extern_clk( + struct ca8210_platform_data *pdata, + struct spi_device *spi, + bool on +) +{ + u8 clkparam[2]; + + if (on) { + dev_info(&spi->dev, "Switching external clock on\n"); + switch (pdata->extclockfreq) { + case SIXTEEN_MHZ: + clkparam[0] = 1; + break; + case EIGHT_MHZ: + clkparam[0] = 2; + break; + case FOUR_MHZ: + clkparam[0] = 3; + break; + case TWO_MHZ: + clkparam[0] = 4; + break; + case ONE_MHZ: + clkparam[0] = 5; + break; + default: + dev_crit(&spi->dev, "Invalid extclock-freq\n"); + return -EINVAL; + } + clkparam[1] = pdata->extclockgpio; + } else { + dev_info(&spi->dev, "Switching external clock off\n"); + clkparam[0] = 0; /* off */ + clkparam[1] = 0; + } + return link_to_linux_err( + hwme_set_request_sync(HWME_SYSCLKOUT, 2, clkparam, spi) + ); +} + +/** + * ca8210_register_ext_clock() - Register ca8210's external clock with kernel + * @spi: Pointer to target ca8210 spi device + * + * Return: 0 or linux error code + */ +static int ca8210_register_ext_clock(struct spi_device *spi) +{ + struct device_node *np = spi->dev.of_node; + struct ca8210_priv *priv = spi_get_drvdata(spi); + struct ca8210_platform_data *pdata = spi->dev.platform_data; + int ret = 0; + + if (!np) + return -EFAULT; + + priv->clk = clk_register_fixed_rate( + &spi->dev, + np->name, + NULL, + 0, + pdata->extclockfreq + ); + + if (IS_ERR(priv->clk)) { + dev_crit(&spi->dev, "Failed to register external clk\n"); + return PTR_ERR(priv->clk); + } + ret = of_clk_add_provider(np, of_clk_src_simple_get, priv->clk); + if (ret) { + clk_unregister(priv->clk); + dev_crit( + &spi->dev, + "Failed to register external clock as clock provider\n" + ); + } else { + dev_info(&spi->dev, "External clock set as clock provider\n"); + } + + return ret; +} + +/** + * ca8210_unregister_ext_clock() - Unregister ca8210's external clock with + * kernel + * @spi: Pointer to target ca8210 spi device + */ +static void ca8210_unregister_ext_clock(struct spi_device *spi) +{ + struct ca8210_priv *priv = spi_get_drvdata(spi); + + if (!priv->clk) + return + + of_clk_del_provider(spi->dev.of_node); + clk_unregister(priv->clk); + dev_info(&spi->dev, "External clock unregistered\n"); +} + +/** + * ca8210_reset_init() - Initialise the reset input to the ca8210 + * @spi: Pointer to target ca8210 spi device + * + * Return: 0 or linux error code + */ +static int ca8210_reset_init(struct spi_device *spi) +{ + int ret; + struct ca8210_platform_data *pdata = spi->dev.platform_data; + + pdata->gpio_reset = of_get_named_gpio( + spi->dev.of_node, + "reset-gpio", + 0 + ); + + ret = gpio_direction_output(pdata->gpio_reset, 1); + if (ret < 0) { + dev_crit( + &spi->dev, + "Reset GPIO %d did not set to output mode\n", + pdata->gpio_reset + ); + } + + return ret; +} + +/** + * ca8210_interrupt_init() - Initialise the irq output from the ca8210 + * @spi: Pointer to target ca8210 spi device + * + * Return: 0 or linux error code + */ +static int ca8210_interrupt_init(struct spi_device *spi) +{ + int ret; + struct ca8210_platform_data *pdata = spi->dev.platform_data; + + pdata->gpio_irq = of_get_named_gpio( + spi->dev.of_node, + "irq-gpio", + 0 + ); + + pdata->irq_id = gpio_to_irq(pdata->gpio_irq); + if (pdata->irq_id < 0) { + dev_crit( + &spi->dev, + "Could not get irq for gpio pin %d\n", + pdata->gpio_irq + ); + gpio_free(pdata->gpio_irq); + return pdata->irq_id; + } + + ret = request_irq( + pdata->irq_id, + ca8210_interrupt_handler, + IRQF_TRIGGER_FALLING, + "ca8210-irq", + spi_get_drvdata(spi) + ); + if (ret) { + dev_crit(&spi->dev, "request_irq %d failed\n", pdata->irq_id); + gpio_unexport(pdata->gpio_irq); + gpio_free(pdata->gpio_irq); + } + + return ret; +} + +/** + * ca8210_dev_com_init() - Initialise the spi communication component + * @priv: Pointer to private data structure + * + * Return: 0 or linux error code + */ +static int ca8210_dev_com_init(struct ca8210_priv *priv) +{ + priv->mlme_workqueue = alloc_ordered_workqueue( + "MLME work queue", + WQ_UNBOUND + ); + if (!priv->mlme_workqueue) { + dev_crit(&priv->spi->dev, "alloc of mlme_workqueue failed!\n"); + return -ENOMEM; + } + + priv->irq_workqueue = alloc_ordered_workqueue( + "ca8210 irq worker", + WQ_UNBOUND + ); + if (!priv->irq_workqueue) { + dev_crit(&priv->spi->dev, "alloc of irq_workqueue failed!\n"); + return -ENOMEM; + } + + return 0; +} + +/** + * ca8210_dev_com_clear() - Deinitialise the spi communication component + * @priv: Pointer to private data structure + */ +static void ca8210_dev_com_clear(struct ca8210_priv *priv) +{ + flush_workqueue(priv->mlme_workqueue); + destroy_workqueue(priv->mlme_workqueue); + flush_workqueue(priv->irq_workqueue); + destroy_workqueue(priv->irq_workqueue); +} + +#define CA8210_MAX_TX_POWERS (9) +static const s32 ca8210_tx_powers[CA8210_MAX_TX_POWERS] = { + 800, 700, 600, 500, 400, 300, 200, 100, 0 +}; + +#define CA8210_MAX_ED_LEVELS (21) +static const s32 ca8210_ed_levels[CA8210_MAX_ED_LEVELS] = { + -10300, -10250, -10200, -10150, -10100, -10050, -10000, -9950, -9900, + -9850, -9800, -9750, -9700, -9650, -9600, -9550, -9500, -9450, -9400, + -9350, -9300 +}; + +/** + * ca8210_hw_setup() - Populate the ieee802154_hw phy attributes with the + * ca8210's defaults + * @ca8210_hw: Pointer to ieee802154_hw to populate + */ +static void ca8210_hw_setup(struct ieee802154_hw *ca8210_hw) +{ + /* Support channels 11-26 */ + ca8210_hw->phy->supported.channels[0] = CA8210_VALID_CHANNELS; + ca8210_hw->phy->supported.tx_powers_size = CA8210_MAX_TX_POWERS; + ca8210_hw->phy->supported.tx_powers = ca8210_tx_powers; + ca8210_hw->phy->supported.cca_ed_levels_size = CA8210_MAX_ED_LEVELS; + ca8210_hw->phy->supported.cca_ed_levels = ca8210_ed_levels; + ca8210_hw->phy->current_channel = 18; + ca8210_hw->phy->current_page = 0; + ca8210_hw->phy->transmit_power = 800; + ca8210_hw->phy->cca.mode = NL802154_CCA_ENERGY_CARRIER; + ca8210_hw->phy->cca.opt = NL802154_CCA_OPT_ENERGY_CARRIER_AND; + ca8210_hw->phy->cca_ed_level = -9800; + ca8210_hw->phy->symbol_duration = 16; + ca8210_hw->phy->lifs_period = 40; + ca8210_hw->phy->sifs_period = 12; + ca8210_hw->flags = + IEEE802154_HW_AFILT | + IEEE802154_HW_OMIT_CKSUM | + IEEE802154_HW_FRAME_RETRIES | + IEEE802154_HW_PROMISCUOUS | + IEEE802154_HW_CSMA_PARAMS; + ca8210_hw->phy->flags = + WPAN_PHY_FLAG_TXPOWER | + WPAN_PHY_FLAG_CCA_ED_LEVEL | + WPAN_PHY_FLAG_CCA_MODE; +} + +/** + * ca8210_test_interface_init() - Initialise the test file interface + * @priv: Pointer to private data structure + * + * Provided as an alternative to the standard linux network interface, the test + * interface exposes a file in the filesystem (ca8210_test) that allows + * 802.15.4 SAP Commands and Cascoda EVBME commands to be sent directly to + * the stack. + * + * Return: 0 or linux error code + */ +static int ca8210_test_interface_init(struct ca8210_priv *priv) +{ + struct ca8210_test *test = &priv->test; + char node_name[32]; + + snprintf( + node_name, + sizeof(node_name), + "ca8210@%d_%d", + priv->spi->master->bus_num, + priv->spi->chip_select + ); + + test->ca8210_dfs_spi_int = debugfs_create_file( + node_name, + 0600, /* S_IRUSR | S_IWUSR */ + NULL, + priv, + &test_int_fops + ); + if (IS_ERR(test->ca8210_dfs_spi_int)) { + dev_err( + &priv->spi->dev, + "Error %ld when creating debugfs node\n", + PTR_ERR(test->ca8210_dfs_spi_int) + ); + return PTR_ERR(test->ca8210_dfs_spi_int); + } + debugfs_create_symlink("ca8210", NULL, node_name); + init_waitqueue_head(&test->readq); + return kfifo_alloc( + &test->up_fifo, + CA8210_TEST_INT_FIFO_SIZE, + GFP_KERNEL + ); +} + +/** + * ca8210_test_interface_clear() - Deinitialise the test file interface + * @priv: Pointer to private data structure + */ +static void ca8210_test_interface_clear(struct ca8210_priv *priv) +{ + struct ca8210_test *test = &priv->test; + + if (!IS_ERR(test->ca8210_dfs_spi_int)) + debugfs_remove(test->ca8210_dfs_spi_int); + kfifo_free(&test->up_fifo); + dev_info(&priv->spi->dev, "Test interface removed\n"); +} + +/** + * ca8210_remove() - Shut down a ca8210 upon being disconnected + * @priv: Pointer to private data structure + * + * Return: 0 or linux error code + */ +static int ca8210_remove(struct spi_device *spi_device) +{ + struct ca8210_priv *priv; + struct ca8210_platform_data *pdata; + + dev_info(&spi_device->dev, "Removing ca8210\n"); + + pdata = spi_device->dev.platform_data; + if (pdata) { + if (pdata->extclockenable) { + ca8210_unregister_ext_clock(spi_device); + ca8210_config_extern_clk(pdata, spi_device, 0); + } + free_irq(pdata->irq_id, spi_device->dev.driver_data); + kfree(pdata); + spi_device->dev.platform_data = NULL; + } + /* get spi_device private data */ + priv = spi_get_drvdata(spi_device); + if (priv) { + dev_info( + &spi_device->dev, + "sync_down = %d, sync_up = %d\n", + priv->sync_down, + priv->sync_up + ); + ca8210_dev_com_clear(spi_device->dev.driver_data); + if (priv->hw) { + if (priv->hw_registered) + ieee802154_unregister_hw(priv->hw); + ieee802154_free_hw(priv->hw); + priv->hw = NULL; + dev_info( + &spi_device->dev, + "Unregistered & freed ieee802154_hw.\n" + ); + } + if (IS_ENABLED(CONFIG_IEEE802154_CA8210_DEBUGFS)) + ca8210_test_interface_clear(priv); + } + + return 0; +} + +/** + * ca8210_probe() - Set up a connected ca8210 upon being detected by the system + * @priv: Pointer to private data structure + * + * Return: 0 or linux error code + */ +static int ca8210_probe(struct spi_device *spi_device) +{ + struct ca8210_priv *priv; + struct ieee802154_hw *hw; + struct ca8210_platform_data *pdata; + int ret; + + dev_info(&spi_device->dev, "Inserting ca8210\n"); + + /* allocate ieee802154_hw and private data */ + hw = ieee802154_alloc_hw(sizeof(struct ca8210_priv), &ca8210_phy_ops); + if (!hw) { + dev_crit(&spi_device->dev, "ieee802154_alloc_hw failed\n"); + ret = -ENOMEM; + goto error; + } + + priv = hw->priv; + priv->hw = hw; + priv->spi = spi_device; + hw->parent = &spi_device->dev; + spin_lock_init(&priv->lock); + priv->async_tx_pending = false; + priv->hw_registered = false; + priv->sync_up = 0; + priv->sync_down = 0; + priv->promiscuous = false; + priv->retries = 0; + init_completion(&priv->ca8210_is_awake); + init_completion(&priv->spi_transfer_complete); + init_completion(&priv->sync_exchange_complete); + spi_set_drvdata(priv->spi, priv); + if (IS_ENABLED(CONFIG_IEEE802154_CA8210_DEBUGFS)) { + cascoda_api_upstream = ca8210_test_int_driver_write; + ca8210_test_interface_init(priv); + } else { + cascoda_api_upstream = NULL; + } + ca8210_hw_setup(hw); + ieee802154_random_extended_addr(&hw->phy->perm_extended_addr); + + pdata = kmalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_crit( + &spi_device->dev, + "Could not allocate platform data\n" + ); + ret = -ENOMEM; + goto error; + } + + ret = ca8210_get_platform_data(priv->spi, pdata); + if (ret) { + dev_crit(&spi_device->dev, "ca8210_get_platform_data failed\n"); + goto error; + } + priv->spi->dev.platform_data = pdata; + + ret = ca8210_dev_com_init(priv); + if (ret) { + dev_crit(&spi_device->dev, "ca8210_dev_com_init failed\n"); + goto error; + } + ret = ca8210_reset_init(priv->spi); + if (ret) { + dev_crit(&spi_device->dev, "ca8210_reset_init failed\n"); + goto error; + } + + ret = ca8210_interrupt_init(priv->spi); + if (ret) { + dev_crit(&spi_device->dev, "ca8210_interrupt_init failed\n"); + goto error; + } + + msleep(100); + + ca8210_reset_send(priv->spi, 1); + + ret = tdme_chipinit(priv->spi); + if (ret) { + dev_crit(&spi_device->dev, "tdme_chipinit failed\n"); + goto error; + } + + if (pdata->extclockenable) { + ret = ca8210_config_extern_clk(pdata, priv->spi, 1); + if (ret) { + dev_crit( + &spi_device->dev, + "ca8210_config_extern_clk failed\n" + ); + goto error; + } + ret = ca8210_register_ext_clock(priv->spi); + if (ret) { + dev_crit( + &spi_device->dev, + "ca8210_register_ext_clock failed\n" + ); + goto error; + } + } + + ret = ieee802154_register_hw(hw); + if (ret) { + dev_crit(&spi_device->dev, "ieee802154_register_hw failed\n"); + goto error; + } + priv->hw_registered = true; + + return 0; +error: + msleep(100); /* wait for pending spi transfers to complete */ + ca8210_remove(spi_device); + return link_to_linux_err(ret); +} + +static const struct of_device_id ca8210_of_ids[] = { + {.compatible = "cascoda,ca8210", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ca8210_of_ids); + +static struct spi_driver ca8210_spi_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ca8210_of_ids), + }, + .probe = ca8210_probe, + .remove = ca8210_remove +}; + +module_spi_driver(ca8210_spi_driver); + +MODULE_AUTHOR("Harry Morris "); +MODULE_DESCRIPTION("CA-8210 SoftMAC driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION("1.0"); -- cgit From ab3916f7fe9dd52026566fb534f5834683f0d578 Mon Sep 17 00:00:00 2001 From: Harry Morris Date: Tue, 28 Mar 2017 13:08:59 +0100 Subject: ieee802154: Add device tree documentation for CA8210 Signed-off-by: Harry Morris Acked-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- .../devicetree/bindings/net/ieee802154/ca8210.txt | 28 ++++++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.txt | 1 + 2 files changed, 29 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/ieee802154/ca8210.txt diff --git a/Documentation/devicetree/bindings/net/ieee802154/ca8210.txt b/Documentation/devicetree/bindings/net/ieee802154/ca8210.txt new file mode 100644 index 000000000000..a1046e636fa1 --- /dev/null +++ b/Documentation/devicetree/bindings/net/ieee802154/ca8210.txt @@ -0,0 +1,28 @@ +* CA8210 IEEE 802.15.4 * + +Required properties: + - compatible: Should be "cascoda,ca8210" + - reg: Controlling chip select + - spi-max-frequency: Maximum clock speed, should be *less than* + 4000000 + - spi-cpol: Requires inverted clock polarity + - reset-gpio: GPIO attached to reset + - irq-gpio: GPIO attached to IRQ +Optional properties: + - extclock-enable: Include for the ca8210 to route its 16MHz clock + to an output + - extclock-freq: Frequency in Hz of the external clock + - extclock-gpio: GPIO of the ca8210 to output the clock on + +Example: + ca8210@0 { + compatible = "cascoda,ca8210"; + reg = <0>; + spi-max-frequency = <3000000>; + spi-cpol; + reset-gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>; + irq-gpio = <&gpio1 2 GPIO_ACTIVE_HIGH>; + extclock-enable; + extclock-freq = 16000000; + extclock-gpio = 2; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index ec0bfb9bbebd..36b4d8a3fe6c 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -51,6 +51,7 @@ brcm Broadcom Corporation buffalo Buffalo, Inc. calxeda Calxeda capella Capella Microsystems, Inc +cascoda Cascoda, Ltd. cavium Cavium, Inc. cdns Cadence Design Systems Inc. ceva Ceva, Inc. -- cgit From 8b1cfcbd282498f7db452175222b68b96421e409 Mon Sep 17 00:00:00 2001 From: Harry Morris Date: Tue, 28 Mar 2017 13:09:00 +0100 Subject: ieee802154: Add entry in MAINTAINTERS for CA8210 driver Signed-off-by: Harry Morris Acked-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5397f54af5fc..055c7de08db8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2944,6 +2944,15 @@ W: http://www.linux-c6x.org/wiki/index.php/Main_Page S: Maintained F: arch/c6x/ +CA8210 IEEE-802.15.4 RADIO DRIVER +M: Harry Morris +M: linuxdev@cascoda.com +L: linux-wpan@vger.kernel.org +W: https://github.com/Cascoda/ca8210-linux.git +S: Maintained +F: drivers/net/ieee802154/ca8210.c +F: Documentation/devicetree/bindings/net/ieee802154/ca8210.txt + CACHEFILES: FS-CACHE BACKEND FOR CACHING ON MOUNTED FILESYSTEMS M: David Howells L: linux-cachefs@redhat.com (moderated for non-subscribers) -- cgit From fa0eaf840adc3df8df914ee211502bc3e5d57508 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 28 Mar 2017 13:11:29 +0100 Subject: 6lowpan: fix assignment of peer_addr The data from peer->chan->dst is not being copied to peer_addr, the current code just updates the pointer and not the contents of what it points to. Fix this with the intended assignment. Detected by CoverityScan, CID#1422111 ("Parse warning (PW.PARAM_SET_BUT_NOT_USED)") Fixes: fb6f2f606ce8 ("6lowpan: Fix IID format for Bluetooth") Signed-off-by: Colin Ian King Reviewed-by: Luiz Augusto von Dentz Signed-off-by: Marcel Holtmann --- net/bluetooth/6lowpan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 24348c8579dd..ba28c7b1557f 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -432,7 +432,7 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev, } daddr = peer->lladdr; - peer_addr = &peer->chan->dst; + *peer_addr = peer->chan->dst; *peer_addr_type = peer->chan->dst_type; lowpan_cb(skb)->chan = peer->chan; -- cgit From f1554b7b2c27cedee6c772dab954629987690110 Mon Sep 17 00:00:00 2001 From: prasanna karthik Date: Tue, 28 Mar 2017 19:44:00 +0000 Subject: Bluetooth: btmrvl: cleanup code in return from btmrvl_sdio_suspend() Else is not generally useful after a break or return Signed-off-by: Prasanna Karthik Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmrvl_sdio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index e65ca19970ea..2424ea2685a7 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -1642,10 +1642,10 @@ static int btmrvl_sdio_suspend(struct device *dev) if (priv->adapter->hs_state == HS_ACTIVATED) { BT_DBG("suspend with MMC_PM_KEEP_POWER"); return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); - } else { - BT_DBG("suspend without MMC_PM_KEEP_POWER"); - return 0; } + + BT_DBG("suspend without MMC_PM_KEEP_POWER"); + return 0; } static int btmrvl_sdio_resume(struct device *dev) -- cgit From 941825e11424f00f9a76c944c16f677aadfe3515 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 29 Mar 2017 18:05:40 +0100 Subject: ieee802154: ca8210: Add checks for kmalloc allocation failures Ensure we don't end up with a null pointer dereferences by checking for for allocation failures. Allocate by sizeof(*ptr) rather than the type to fix checkpack warnings. Also merge multiple lines into one line for the kmalloc call. Detected by CoverityScan, CID#1422435 ("Dereference null return value") Signed-off-by: Colin Ian King Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/ca8210.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/net/ieee802154/ca8210.c b/drivers/net/ieee802154/ca8210.c index 53fa87bfede0..25fd3b04b3c0 100644 --- a/drivers/net/ieee802154/ca8210.c +++ b/drivers/net/ieee802154/ca8210.c @@ -634,6 +634,8 @@ static int ca8210_test_int_driver_write( dev_dbg(&priv->spi->dev, "%#03x\n", buf[i]); fifo_buffer = kmalloc(len, GFP_KERNEL); + if (!fifo_buffer) + return -ENOMEM; memcpy(fifo_buffer, buf, len); kfifo_in(&test->up_fifo, &fifo_buffer, 4); wake_up_interruptible(&priv->test.readq); @@ -759,10 +761,10 @@ static void ca8210_rx_done(struct cas_control *cas_ctl) &priv->spi->dev, "Resetting MAC...\n"); - mlme_reset_wpc = kmalloc( - sizeof(struct work_priv_container), - GFP_KERNEL - ); + mlme_reset_wpc = kmalloc(sizeof(*mlme_reset_wpc), + GFP_KERNEL); + if (!mlme_reset_wpc) + goto finish; INIT_WORK( &mlme_reset_wpc->work, ca8210_mlme_reset_worker @@ -925,10 +927,10 @@ static int ca8210_spi_transfer( dev_dbg(&spi->dev, "ca8210_spi_transfer called\n"); - cas_ctl = kmalloc( - sizeof(struct cas_control), - GFP_ATOMIC - ); + cas_ctl = kmalloc(sizeof(*cas_ctl), GFP_ATOMIC); + if (!cas_ctl) + return -ENOMEM; + cas_ctl->priv = priv; memset(cas_ctl->tx_buf, SPI_IDLE, CA8210_SPI_BUF_SIZE); memset(cas_ctl->tx_in_buf, SPI_IDLE, CA8210_SPI_BUF_SIZE); -- cgit From 95065a61e9bf25fb85295127fba893200c2bbbd8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Mar 2017 18:15:27 +0200 Subject: Bluetooth: hci_bcm: add missing tty-device sanity check Make sure to check the tty-device pointer before looking up the sibling platform device to avoid dereferencing a NULL-pointer when the tty is one end of a Unix98 pty. Fixes: 0395ffc1ee05 ("Bluetooth: hci_bcm: Add PM for BCM devices") Cc: stable # 4.3 Cc: Frederic Danis Signed-off-by: Johan Hovold Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_bcm.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 04fe5535a153..f87bfdfee4ff 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -287,6 +287,9 @@ static int bcm_open(struct hci_uart *hu) hu->priv = bcm; + if (!hu->tty->dev) + goto out; + mutex_lock(&bcm_device_lock); list_for_each(p, &bcm_device_list) { struct bcm_device *dev = list_entry(p, struct bcm_device, list); @@ -307,7 +310,7 @@ static int bcm_open(struct hci_uart *hu) } mutex_unlock(&bcm_device_lock); - +out: return 0; } -- cgit From dcb9cfaa5ea9aa0ec08aeb92582ccfe3e4c719a9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 29 Mar 2017 18:15:28 +0200 Subject: Bluetooth: hci_intel: add missing tty-device sanity check Make sure to check the tty-device pointer before looking up the sibling platform device to avoid dereferencing a NULL-pointer when the tty is one end of a Unix98 pty. Fixes: 74cdad37cd24 ("Bluetooth: hci_intel: Add runtime PM support") Fixes: 1ab1f239bf17 ("Bluetooth: hci_intel: Add support for platform driver") Cc: stable # 4.3 Cc: Loic Poulain Signed-off-by: Johan Hovold Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_intel.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index d915e7eee233..fa5099986f1b 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -307,6 +307,9 @@ static int intel_set_power(struct hci_uart *hu, bool powered) struct list_head *p; int err = -ENODEV; + if (!hu->tty->dev) + return err; + mutex_lock(&intel_device_list_lock); list_for_each(p, &intel_device_list) { @@ -379,6 +382,9 @@ static void intel_busy_work(struct work_struct *work) struct intel_data *intel = container_of(work, struct intel_data, busy_work); + if (!intel->hu->tty->dev) + return; + /* Link is busy, delay the suspend */ mutex_lock(&intel_device_list_lock); list_for_each(p, &intel_device_list) { @@ -899,6 +905,8 @@ done: list_for_each(p, &intel_device_list) { struct intel_device *dev = list_entry(p, struct intel_device, list); + if (!hu->tty->dev) + break; if (hu->tty->dev->parent == dev->pdev->dev.parent) { if (device_may_wakeup(&dev->pdev->dev)) { set_bit(STATE_LPM_ENABLED, &intel->flags); @@ -1066,6 +1074,9 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb) BT_DBG("hu %p skb %p", hu, skb); + if (!hu->tty->dev) + goto out_enqueue; + /* Be sure our controller is resumed and potential LPM transaction * completed before enqueuing any packet. */ @@ -1082,7 +1093,7 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb) } } mutex_unlock(&intel_device_list_lock); - +out_enqueue: skb_queue_tail(&intel->txq, skb); return 0; -- cgit From d2891c4d071d807f01cc911dc42a68f4568d65cf Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Tue, 28 Mar 2017 23:10:54 -0700 Subject: Bluetooth: 6lowpan: fix delay work init in add_peer_chan() When adding 6lowpan devices very rapidly we sometimes see a crash: [23122.306615] CPU: 2 PID: 0 Comm: swapper/2 Not tainted 4.9.0-43-arm64 #1 Debian 4.9.9.linaro.43-1 [23122.315400] Hardware name: HiKey Development Board (DT) [23122.320623] task: ffff800075443080 task.stack: ffff800075484000 [23122.326551] PC is at expire_timers+0x70/0x150 [23122.330907] LR is at run_timer_softirq+0xa0/0x1a0 [23122.335616] pc : [] lr : [] pstate: 600001c5 This was due to add_peer_chan() unconditionally initializing the lowpan_btle_dev->notify_peers delayed work structure, even if the lowpan_btle_dev passed into add_peer_chan() had previously been initialized. Normally, this would go unnoticed as the delayed work timer is set for 100 msec, however when calling add_peer_chan() faster than 100 msec it clears out a previously queued delay work causing the crash above. To fix this, let add_peer_chan() know when a new lowpan_btle_dev is passed in so that it only performs the delay work initialization when needed. Signed-off-by: Michael Scott Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/bluetooth/6lowpan.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index ba28c7b1557f..6ae51e840af1 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -676,7 +676,8 @@ static struct l2cap_chan *chan_create(void) } static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, - struct lowpan_btle_dev *dev) + struct lowpan_btle_dev *dev, + bool new_netdev) { struct lowpan_peer *peer; @@ -697,7 +698,8 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, spin_unlock(&devices_lock); /* Notifying peers about us needs to be done without locks held */ - INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers); + if (new_netdev) + INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers); schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100)); return peer->chan; @@ -755,6 +757,7 @@ out: static inline void chan_ready_cb(struct l2cap_chan *chan) { struct lowpan_btle_dev *dev; + bool new_netdev = false; dev = lookup_dev(chan->conn); @@ -765,12 +768,13 @@ static inline void chan_ready_cb(struct l2cap_chan *chan) l2cap_chan_del(chan, -ENOENT); return; } + new_netdev = true; } if (!try_module_get(THIS_MODULE)) return; - add_peer_chan(chan, dev); + add_peer_chan(chan, dev, new_netdev); ifup(dev->netdev); } -- cgit From 6dea44f5acc3d63fbaa992146e92252329d9a2be Mon Sep 17 00:00:00 2001 From: Michael Scott Date: Tue, 28 Mar 2017 23:10:18 -0700 Subject: Bluetooth: 6lowpan: fix use after free in chan_suspend/resume A status field in the skb_cb struct was storing a channel status based on channel suspend/resume events. This stored status was then used to return EAGAIN if there were packet sending issues in snd_pkt(). The issue is that the skb has been freed by the time the callback to 6lowpan's suspend/resume was called. So, this generates a "use after free" issue that was noticed while running kernel tests with KASAN debug enabled. Let's eliminate the status field entirely as we can use the channel tx_credits to indicate whether we should return EAGAIN when handling packets. Signed-off-by: Michael Scott Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/bluetooth/6lowpan.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 6ae51e840af1..b39da8d36aa9 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -38,7 +38,6 @@ struct skb_cb { struct in6_addr addr; struct in6_addr gw; struct l2cap_chan *chan; - int status; }; #define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb)) @@ -485,7 +484,7 @@ static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb, } if (!err) - err = lowpan_cb(skb)->status; + err = (!chan->tx_credits ? -EAGAIN : 0); if (err < 0) { if (err == -EAGAIN) @@ -880,26 +879,12 @@ static struct sk_buff *chan_alloc_skb_cb(struct l2cap_chan *chan, static void chan_suspend_cb(struct l2cap_chan *chan) { - struct sk_buff *skb = chan->data; - - BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb); - - if (!skb) - return; - - lowpan_cb(skb)->status = -EAGAIN; + BT_DBG("chan %p suspend", chan); } static void chan_resume_cb(struct l2cap_chan *chan) { - struct sk_buff *skb = chan->data; - - BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb); - - if (!skb) - return; - - lowpan_cb(skb)->status = 0; + BT_DBG("chan %p resume", chan); } static long chan_get_sndtimeo_cb(struct l2cap_chan *chan) -- cgit From 6eb7bd66830c1e1919aa4006d7eb288873b0f806 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Fri, 31 Mar 2017 14:32:31 +0800 Subject: Bluetooth: btmrvl: disable platform wakeup interrupt in suspend failure path Host sleep handshake with device might been fail, disable platform wakeup interrupt in this case. Reported-by: Guenter Roeck Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmrvl_sdio.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 2424ea2685a7..95e40ec27c0e 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -1630,6 +1630,13 @@ static int btmrvl_sdio_suspend(struct device *dev) if (priv->adapter->hs_state != HS_ACTIVATED) { if (btmrvl_enable_hs(priv)) { BT_ERR("HS not activated, suspend failed!"); + /* Disable platform specific wakeup interrupt */ + if (card->plt_wake_cfg && + card->plt_wake_cfg->irq_bt >= 0) { + disable_irq_wake(card->plt_wake_cfg->irq_bt); + disable_irq(card->plt_wake_cfg->irq_bt); + } + priv->adapter->is_suspending = false; return -EBUSY; } -- cgit From c8ba804437d6311f61e4ca77a1cf43ce6a17bd21 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Fri, 31 Mar 2017 14:32:32 +0800 Subject: Bluetooth: btmrvl: remove unnecessary wakeup interrupt number sanity check Sanity check of interrupt number in interrupt handler is unnecessary and confusion, remove it. Reported-by: Guenter Roeck Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmrvl_sdio.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 95e40ec27c0e..eb794f08b238 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -64,11 +64,9 @@ static irqreturn_t btmrvl_wake_irq_bt(int irq, void *priv) struct btmrvl_sdio_card *card = priv; struct btmrvl_plt_wake_cfg *cfg = card->plt_wake_cfg; - if (cfg->irq_bt >= 0) { - pr_info("%s: wake by bt", __func__); - cfg->wake_by_bt = true; - disable_irq_nosync(irq); - } + pr_info("%s: wake by bt", __func__); + cfg->wake_by_bt = true; + disable_irq_nosync(irq); pm_wakeup_event(&card->func->dev, 0); pm_system_wakeup(); -- cgit From 27ce68a37b0bd86123571e1e76b9bdb362d109e0 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 3 Apr 2017 17:48:55 +0300 Subject: Bluetooth: 6lowpan: Remove unnecessary peer lookup During chan_recv_cb there is already a peer lookup which can be passed to recv_pkt directly instead of the channel. Signed-off-by: Luiz Augusto von Dentz Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/bluetooth/6lowpan.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index b39da8d36aa9..2063e96bc006 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -269,27 +269,20 @@ static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev) } static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev, - struct l2cap_chan *chan) + struct lowpan_peer *peer) { const u8 *saddr; struct lowpan_btle_dev *dev; - struct lowpan_peer *peer; dev = lowpan_btle_dev(netdev); - rcu_read_lock(); - peer = __peer_lookup_chan(dev, chan); - rcu_read_unlock(); - if (!peer) - return -EINVAL; - saddr = peer->lladdr; return lowpan_header_decompress(skb, netdev, netdev->dev_addr, saddr); } static int recv_pkt(struct sk_buff *skb, struct net_device *dev, - struct l2cap_chan *chan) + struct lowpan_peer *peer) { struct sk_buff *local_skb; int ret; @@ -342,7 +335,7 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, local_skb->dev = dev; - ret = iphc_decompress(local_skb, dev, chan); + ret = iphc_decompress(local_skb, dev, peer); if (ret < 0) { kfree_skb(local_skb); goto drop; @@ -388,7 +381,7 @@ static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) if (!dev || !dev->netdev) return -ENOENT; - err = recv_pkt(skb, dev->netdev, chan); + err = recv_pkt(skb, dev->netdev, peer); if (err) { BT_DBG("recv pkt %d", err); err = -EAGAIN; -- cgit From da75fdc6bdccaf99220757cd1ac33cec72cfebb1 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 3 Apr 2017 17:48:56 +0300 Subject: Bluetooth: 6lowpan: Print errors during recv_pkt This makes should make it more clear why a packet is being dropped. Signed-off-by: Luiz Augusto von Dentz Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/bluetooth/6lowpan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 2063e96bc006..5b91e85cf1c6 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -337,6 +337,7 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, ret = iphc_decompress(local_skb, dev, peer); if (ret < 0) { + BT_DBG("iphc_decompress failed: %d", ret); kfree_skb(local_skb); goto drop; } @@ -356,6 +357,7 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, consume_skb(local_skb); consume_skb(skb); } else { + BT_DBG("unknown packet type"); goto drop; } -- cgit From 03732141bf23378b31e266a1da5a8f51f9e35cbe Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 3 Apr 2017 17:48:57 +0300 Subject: Bluetooth: L2CAP: Don't return -EAGAIN if out of credits Just keep queueing them into TX queue since the caller might just have to do the same and there is no impact in adding another packet to the TX queue even if there aren't any credits to transmit them. Signed-off-by: Luiz Augusto von Dentz Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap_core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fc7f321a3823..3a202b09d4ac 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2458,9 +2458,6 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) if (len > chan->omtu) return -EMSGSIZE; - if (!chan->tx_credits) - return -EAGAIN; - __skb_queue_head_init(&seg_queue); err = l2cap_segment_le_sdu(chan, &seg_queue, msg, len); -- cgit From 24dcbf66220564cdb23129aa3854b49532b1a9d8 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 11 Apr 2017 22:20:58 +0300 Subject: 6lowpan: Don't set IFF_NO_QUEUE There is no point in setting IFF_NO_QUEUE should already have taken care of setting it if tx_queue_len is not set, in fact this may actually disable queue for interfaces that require it and do set tx_queue_len. Signed-off-by: Luiz Augusto von Dentz Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/6lowpan/core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/6lowpan/core.c b/net/6lowpan/core.c index 5f9909a2b58c..40d3d72beb53 100644 --- a/net/6lowpan/core.c +++ b/net/6lowpan/core.c @@ -35,7 +35,6 @@ int lowpan_register_netdevice(struct net_device *dev, dev->type = ARPHRD_6LOWPAN; dev->mtu = IPV6_MIN_MTU; - dev->priv_flags |= IFF_NO_QUEUE; lowpan_dev(dev)->lltype = lltype; -- cgit From e1008f95e1e7762fade7ccbc973247a3b32edddb Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 11 Apr 2017 22:20:59 +0300 Subject: Bluetooth: 6lowpan: Don't drop packets when run out of credits Since l2cap_chan_send will now queue the packets there is no point in checking the credits anymore. Signed-off-by: Luiz Augusto von Dentz Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/bluetooth/6lowpan.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 5b91e85cf1c6..22bd936ed2ce 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -478,15 +478,8 @@ static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb, return 0; } - if (!err) - err = (!chan->tx_credits ? -EAGAIN : 0); - - if (err < 0) { - if (err == -EAGAIN) - netdev->stats.tx_dropped++; - else - netdev->stats.tx_errors++; - } + if (err < 0) + netdev->stats.tx_errors++; return err; } -- cgit From f183e52b8ef3d62c795b0fe2228400ac48f6cae6 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 11 Apr 2017 22:21:00 +0300 Subject: Bluetooth: 6lowpan: Use netif APIs to flow control Rely on netif_wake_queue and netif_stop_queue to flow control when transmit resources are unavailable. Signed-off-by: Luiz Augusto von Dentz Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/bluetooth/6lowpan.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 22bd936ed2ce..dc7fda307b15 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -867,12 +867,28 @@ static struct sk_buff *chan_alloc_skb_cb(struct l2cap_chan *chan, static void chan_suspend_cb(struct l2cap_chan *chan) { + struct lowpan_btle_dev *dev; + BT_DBG("chan %p suspend", chan); + + dev = lookup_dev(chan->conn); + if (!dev || !dev->netdev) + return; + + netif_stop_queue(dev->netdev); } static void chan_resume_cb(struct l2cap_chan *chan) { + struct lowpan_btle_dev *dev; + BT_DBG("chan %p resume", chan); + + dev = lookup_dev(chan->conn); + if (!dev || !dev->netdev) + return; + + netif_wake_queue(dev->netdev); } static long chan_get_sndtimeo_cb(struct l2cap_chan *chan) -- cgit From 8a505b7f39f7fb2d3a2b25bfaa2f18d2ce010eef Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 11 Apr 2017 22:21:01 +0300 Subject: Bluetooth: L2CAP: Add l2cap_le_flowctl_send Consolidate code sending data to LE CoC channels and adds proper accounting of packets sent, the remaining credits and how many packets are queued. Signed-off-by: Luiz Augusto von Dentz Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap_core.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 3a202b09d4ac..f88ac99528ce 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2425,6 +2425,22 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan, return 0; } +static void l2cap_le_flowctl_send(struct l2cap_chan *chan) +{ + int sent = 0; + + BT_DBG("chan %p", chan); + + while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) { + l2cap_do_send(chan, skb_dequeue(&chan->tx_q)); + chan->tx_credits--; + sent++; + } + + BT_DBG("Sent %d credits %u queued %u", sent, chan->tx_credits, + skb_queue_len(&chan->tx_q)); +} + int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { struct sk_buff *skb; @@ -2472,10 +2488,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) skb_queue_splice_tail_init(&seg_queue, &chan->tx_q); - while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) { - l2cap_do_send(chan, skb_dequeue(&chan->tx_q)); - chan->tx_credits--; - } + l2cap_le_flowctl_send(chan); if (!chan->tx_credits) chan->ops->suspend(chan); @@ -5567,10 +5580,8 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn, chan->tx_credits += credits; - while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) { - l2cap_do_send(chan, skb_dequeue(&chan->tx_q)); - chan->tx_credits--; - } + /* Resume sending */ + l2cap_le_flowctl_send(chan); if (chan->tx_credits) chan->ops->resume(chan); -- cgit From 814f1b243d2c63928f6aa72d66bec0e5fae2f4a9 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 11 Apr 2017 22:21:02 +0300 Subject: Bluetooth: 6lowpan: Set tx_queue_len to DEFAULT_TX_QUEUE_LEN Make netdev queue packets if we run out of credits. Signed-off-by: Luiz Augusto von Dentz Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/bluetooth/6lowpan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index dc7fda307b15..a4deba6548fe 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -594,6 +595,7 @@ static void netdev_setup(struct net_device *dev) dev->flags = IFF_RUNNING | IFF_POINTOPOINT | IFF_MULTICAST; dev->watchdog_timeo = 0; + dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; dev->netdev_ops = &netdev_ops; dev->header_ops = &header_ops; -- cgit From 258695222bdb89071aebe60554f5246927817a89 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Tue, 11 Apr 2017 22:21:03 +0300 Subject: bluetooth: Do not set IFF_POINTOPOINT The IPv6 stack needs to send and receive Neighbor Discovery messages. Remove the IFF_POINTOPOINT flag. Signed-off-by: Patrik Flykt Acked-by: Jukka Rissanen Reviewed-by: Luiz Augusto von Dentz Signed-off-by: Marcel Holtmann --- net/bluetooth/6lowpan.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index a4deba6548fe..608959989f8e 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -592,8 +592,7 @@ static void netdev_setup(struct net_device *dev) { dev->hard_header_len = 0; dev->needed_tailroom = 0; - dev->flags = IFF_RUNNING | IFF_POINTOPOINT | - IFF_MULTICAST; + dev->flags = IFF_RUNNING | IFF_MULTICAST; dev->watchdog_timeo = 0; dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; -- cgit From 2f09276769a1f9c515e7767284fb188198ddfed7 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 28 Mar 2017 17:59:30 +0200 Subject: tty: serial: omap: add UPF_BOOT_AUTOCONF flag for DT init The UPF_BOOT_AUTOCONF flag is needed for proper flow control support. Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel Acked-by: Greg Kroah-Hartman Signed-off-by: Marcel Holtmann --- drivers/tty/serial/omap-serial.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 6c6f82ad8d5c..a4734649a0f0 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1597,6 +1597,9 @@ static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) of_property_read_u32(dev->of_node, "clock-frequency", &omap_up_info->uartclk); + + omap_up_info->flags = UPF_BOOT_AUTOCONF; + return omap_up_info; } -- cgit From b3f80c8f75efb2e6a817a0e48bf36cd30685a138 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 28 Mar 2017 17:59:31 +0200 Subject: serdev: add serdev_device_wait_until_sent Add method, which waits until the transmission buffer has been sent. Note, that the change in ttyport_write_wakeup is related, since tty_wait_until_sent will hang without that change. Acked-by: Rob Herring Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel Acked-by: Greg Kroah-Hartman Signed-off-by: Marcel Holtmann --- drivers/tty/serdev/core.c | 11 +++++++++++ drivers/tty/serdev/serdev-ttyport.c | 18 ++++++++++++++---- include/linux/serdev.h | 3 +++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index f4c6c90add78..a63b74031e22 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -173,6 +173,17 @@ void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable) } EXPORT_SYMBOL_GPL(serdev_device_set_flow_control); +void serdev_device_wait_until_sent(struct serdev_device *serdev, long timeout) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->wait_until_sent) + return; + + ctrl->ops->wait_until_sent(ctrl, timeout); +} +EXPORT_SYMBOL_GPL(serdev_device_wait_until_sent); + static int serdev_drv_probe(struct device *dev) { const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index d05393594f15..50dc75c4d204 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -14,6 +14,7 @@ #include #include #include +#include #define SERPORT_ACTIVE 1 @@ -46,11 +47,11 @@ static void ttyport_write_wakeup(struct tty_port *port) struct serdev_controller *ctrl = port->client_data; struct serport *serport = serdev_controller_get_drvdata(ctrl); - if (!test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags)) - return; - - if (test_bit(SERPORT_ACTIVE, &serport->flags)) + if (test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags) && + test_bit(SERPORT_ACTIVE, &serport->flags)) serdev_controller_write_wakeup(ctrl); + + wake_up_interruptible_poll(&port->tty->write_wait, POLLOUT); } static const struct tty_port_client_operations client_ops = { @@ -167,6 +168,14 @@ static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable tty_set_termios(tty, &ktermios); } +static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + tty_wait_until_sent(tty, timeout); +} + static const struct serdev_controller_ops ctrl_ops = { .write_buf = ttyport_write_buf, .write_flush = ttyport_write_flush, @@ -175,6 +184,7 @@ static const struct serdev_controller_ops ctrl_ops = { .close = ttyport_close, .set_flow_control = ttyport_set_flow_control, .set_baudrate = ttyport_set_baudrate, + .wait_until_sent = ttyport_wait_until_sent, }; struct device *serdev_tty_port_register(struct tty_port *port, diff --git a/include/linux/serdev.h b/include/linux/serdev.h index 9519da6253a8..a308b206d204 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -81,6 +81,7 @@ struct serdev_controller_ops { void (*close)(struct serdev_controller *); void (*set_flow_control)(struct serdev_controller *, bool); unsigned int (*set_baudrate)(struct serdev_controller *, unsigned int); + void (*wait_until_sent)(struct serdev_controller *, long); }; /** @@ -186,6 +187,7 @@ int serdev_device_open(struct serdev_device *); void serdev_device_close(struct serdev_device *); unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int); void serdev_device_set_flow_control(struct serdev_device *, bool); +void serdev_device_wait_until_sent(struct serdev_device *, long); int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t); void serdev_device_write_flush(struct serdev_device *); int serdev_device_write_room(struct serdev_device *); @@ -223,6 +225,7 @@ static inline unsigned int serdev_device_set_baudrate(struct serdev_device *sdev return 0; } static inline void serdev_device_set_flow_control(struct serdev_device *sdev, bool enable) {} +static inline void serdev_device_wait_until_sent(struct serdev_device *sdev, long timeout) {} static inline int serdev_device_write_buf(struct serdev_device *sdev, const unsigned char *buf, size_t count) { return -ENODEV; -- cgit From 5659dab26f09a60db8bd1600e1ce89802fab1c7f Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 28 Mar 2017 17:59:32 +0200 Subject: serdev: implement get/set tiocm Add method for getting and setting tiocm. Acked-by: Pavel Machek Acked-by: Rob Herring Signed-off-by: Sebastian Reichel Acked-by: Greg Kroah-Hartman Signed-off-by: Marcel Holtmann --- drivers/tty/serdev/core.c | 22 ++++++++++++++++++++++ drivers/tty/serdev/serdev-ttyport.c | 24 ++++++++++++++++++++++++ include/linux/serdev.h | 13 +++++++++++++ 3 files changed, 59 insertions(+) diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index a63b74031e22..1e1cbae3a0ea 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -184,6 +184,28 @@ void serdev_device_wait_until_sent(struct serdev_device *serdev, long timeout) } EXPORT_SYMBOL_GPL(serdev_device_wait_until_sent); +int serdev_device_get_tiocm(struct serdev_device *serdev) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->get_tiocm) + return -ENOTSUPP; + + return ctrl->ops->get_tiocm(ctrl); +} +EXPORT_SYMBOL_GPL(serdev_device_get_tiocm); + +int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear) +{ + struct serdev_controller *ctrl = serdev->ctrl; + + if (!ctrl || !ctrl->ops->set_tiocm) + return -ENOTSUPP; + + return ctrl->ops->set_tiocm(ctrl, set, clear); +} +EXPORT_SYMBOL_GPL(serdev_device_set_tiocm); + static int serdev_drv_probe(struct device *dev) { const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index 50dc75c4d204..487c88f6aa0e 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -176,6 +176,28 @@ static void ttyport_wait_until_sent(struct serdev_controller *ctrl, long timeout tty_wait_until_sent(tty, timeout); } +static int ttyport_get_tiocm(struct serdev_controller *ctrl) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + if (!tty->ops->tiocmget) + return -ENOTSUPP; + + return tty->driver->ops->tiocmget(tty); +} + +static int ttyport_set_tiocm(struct serdev_controller *ctrl, unsigned int set, unsigned int clear) +{ + struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct tty_struct *tty = serport->tty; + + if (!tty->ops->tiocmset) + return -ENOTSUPP; + + return tty->driver->ops->tiocmset(tty, set, clear); +} + static const struct serdev_controller_ops ctrl_ops = { .write_buf = ttyport_write_buf, .write_flush = ttyport_write_flush, @@ -185,6 +207,8 @@ static const struct serdev_controller_ops ctrl_ops = { .set_flow_control = ttyport_set_flow_control, .set_baudrate = ttyport_set_baudrate, .wait_until_sent = ttyport_wait_until_sent, + .get_tiocm = ttyport_get_tiocm, + .set_tiocm = ttyport_set_tiocm, }; struct device *serdev_tty_port_register(struct tty_port *port, diff --git a/include/linux/serdev.h b/include/linux/serdev.h index a308b206d204..e29a270f603c 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -15,6 +15,7 @@ #include #include +#include struct serdev_controller; struct serdev_device; @@ -82,6 +83,8 @@ struct serdev_controller_ops { void (*set_flow_control)(struct serdev_controller *, bool); unsigned int (*set_baudrate)(struct serdev_controller *, unsigned int); void (*wait_until_sent)(struct serdev_controller *, long); + int (*get_tiocm)(struct serdev_controller *); + int (*set_tiocm)(struct serdev_controller *, unsigned int, unsigned int); }; /** @@ -188,6 +191,8 @@ void serdev_device_close(struct serdev_device *); unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int); void serdev_device_set_flow_control(struct serdev_device *, bool); void serdev_device_wait_until_sent(struct serdev_device *, long); +int serdev_device_get_tiocm(struct serdev_device *); +int serdev_device_set_tiocm(struct serdev_device *, int, int); int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t); void serdev_device_write_flush(struct serdev_device *); int serdev_device_write_room(struct serdev_device *); @@ -226,6 +231,14 @@ static inline unsigned int serdev_device_set_baudrate(struct serdev_device *sdev } static inline void serdev_device_set_flow_control(struct serdev_device *sdev, bool enable) {} static inline void serdev_device_wait_until_sent(struct serdev_device *sdev, long timeout) {} +static inline int serdev_device_get_tiocm(struct serdev_device *serdev) +{ + return -ENOTSUPP; +} +static inline int serdev_device_set_tiocm(struct serdev_device *serdev, int set, int clear) +{ + return -ENOTSUPP; +} static inline int serdev_device_write_buf(struct serdev_device *sdev, const unsigned char *buf, size_t count) { return -ENODEV; -- cgit From 756db778748949f6403b727fc6251674dbfcb1a2 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 28 Mar 2017 17:59:33 +0200 Subject: serdev: add helpers for cts and rts handling Add serdev helper functions for handling of cts and rts lines using the serdev's tiocm functions. Acked-by: Rob Herring Signed-off-by: Sebastian Reichel Acked-by: Greg Kroah-Hartman Signed-off-by: Marcel Holtmann --- include/linux/serdev.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/include/linux/serdev.h b/include/linux/serdev.h index e29a270f603c..37395b8eb8f1 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -16,6 +16,7 @@ #include #include #include +#include struct serdev_controller; struct serdev_device; @@ -254,6 +255,36 @@ static inline int serdev_device_write_room(struct serdev_device *sdev) #endif /* CONFIG_SERIAL_DEV_BUS */ +static inline bool serdev_device_get_cts(struct serdev_device *serdev) +{ + int status = serdev_device_get_tiocm(serdev); + return !!(status & TIOCM_CTS); +} + +static inline int serdev_device_wait_for_cts(struct serdev_device *serdev, bool state, int timeout_ms) +{ + unsigned long timeout; + bool signal; + + timeout = jiffies + msecs_to_jiffies(timeout_ms); + while (time_is_after_jiffies(timeout)) { + signal = serdev_device_get_cts(serdev); + if (signal == state) + return 0; + usleep_range(1000, 2000); + } + + return -ETIMEDOUT; +} + +static inline int serdev_device_set_rts(struct serdev_device *serdev, bool enable) +{ + if (enable) + return serdev_device_set_tiocm(serdev, TIOCM_RTS, 0); + else + return serdev_device_set_tiocm(serdev, 0, TIOCM_RTS); +} + /* * serdev hooks into TTY core */ -- cgit From aeac30140694824f26d14655271e1dcf3e32fd49 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 28 Mar 2017 17:59:34 +0200 Subject: Bluetooth: hci_uart: add support for word alignment This will be used by Nokia's H4+ protocol, which uses 2-byte aligned packets. Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_h4.c | 17 +++++++++++++++++ drivers/bluetooth/hci_ldisc.c | 4 ++++ drivers/bluetooth/hci_uart.h | 3 +++ 3 files changed, 24 insertions(+) diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 635597b6e168..82e5a32b87a4 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -171,9 +171,20 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, const unsigned char *buffer, int count, const struct h4_recv_pkt *pkts, int pkts_count) { + struct hci_uart *hu = hci_get_drvdata(hdev); + u8 alignment = hu->alignment; + while (count) { int i, len; + /* remove padding bytes from buffer */ + for (; hu->padding && count > 0; hu->padding--) { + count--; + buffer++; + } + if (!count) + break; + if (!skb) { for (i = 0; i < pkts_count; i++) { if (buffer[0] != (&pkts[i])->type) @@ -253,11 +264,17 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, } if (!dlen) { + hu->padding = (skb->len - 1) % alignment; + hu->padding = (alignment - hu->padding) % alignment; + /* No more data, complete frame */ (&pkts[i])->recv(hdev, skb); skb = NULL; } } else { + hu->padding = (skb->len - 1) % alignment; + hu->padding = (alignment - hu->padding) % alignment; + /* Complete frame */ (&pkts[i])->recv(hdev, skb); skb = NULL; diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 9497c469efd2..0ec8a94bd712 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -459,6 +459,10 @@ static int hci_uart_tty_open(struct tty_struct *tty) hu->tty = tty; tty->receive_room = 65536; + /* disable alignment support by default */ + hu->alignment = 1; + hu->padding = 0; + INIT_WORK(&hu->init_ready, hci_uart_init_work); INIT_WORK(&hu->write_work, hci_uart_write_work); diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 070139513e65..4aff50960cac 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -92,6 +92,9 @@ struct hci_uart { unsigned int init_speed; unsigned int oper_speed; + + u8 alignment; + u8 padding; }; /* HCI_UART proto flag bits */ -- cgit From 82f5169bf3d3b5d8f7f7c437d2d83435173cb638 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 28 Mar 2017 17:59:35 +0200 Subject: Bluetooth: hci_uart: add serdev driver support library This adds library functions for serdev based BT drivers. This is largely copied from hci_ldisc.c and modified to use serdev calls. There's a little bit of duplication, but I avoided intermixing this as the ldisc code should eventually go away. Signed-off-by: Rob Herring Cc: Marcel Holtmann Cc: Gustavo Padovan Cc: Johan Hedberg Cc: linux-bluetooth@vger.kernel.org Acked-by: Pavel Machek [Fix style issues reported by Pavel] Signed-off-by: Sebastian Reichel Signed-off-by: Marcel Holtmann --- drivers/bluetooth/Makefile | 1 + drivers/bluetooth/hci_serdev.c | 361 +++++++++++++++++++++++++++++++++++++++++ drivers/bluetooth/hci_uart.h | 4 + 3 files changed, 366 insertions(+) create mode 100644 drivers/bluetooth/hci_serdev.c diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 80627187c8b6..fd571689eed6 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -29,6 +29,7 @@ btmrvl-y := btmrvl_main.o btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o hci_uart-y := hci_ldisc.o +hci_uart-$(CONFIG_SERIAL_DEV_BUS) += hci_serdev.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c new file mode 100644 index 000000000000..f5ccb2c7ef92 --- /dev/null +++ b/drivers/bluetooth/hci_serdev.c @@ -0,0 +1,361 @@ +/* + * Bluetooth HCI serdev driver lib + * + * Copyright (C) 2017 Linaro, Ltd., Rob Herring + * + * Based on hci_ldisc.c: + * + * Copyright (C) 2000-2001 Qualcomm Incorporated + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2004-2005 Marcel Holtmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include + +#include +#include + +#include "hci_uart.h" + +struct serdev_device_ops hci_serdev_client_ops; + +static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type) +{ + struct hci_dev *hdev = hu->hdev; + + /* Update HCI stat counters */ + switch (pkt_type) { + case HCI_COMMAND_PKT: + hdev->stat.cmd_tx++; + break; + + case HCI_ACLDATA_PKT: + hdev->stat.acl_tx++; + break; + + case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + break; + } +} + +static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu) +{ + struct sk_buff *skb = hu->tx_skb; + + if (!skb) + skb = hu->proto->dequeue(hu); + else + hu->tx_skb = NULL; + + return skb; +} + +static void hci_uart_write_work(struct work_struct *work) +{ + struct hci_uart *hu = container_of(work, struct hci_uart, write_work); + struct serdev_device *serdev = hu->serdev; + struct hci_dev *hdev = hu->hdev; + struct sk_buff *skb; + + /* REVISIT: + * should we cope with bad skbs or ->write() returning an error value? + */ + do { + clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); + + while ((skb = hci_uart_dequeue(hu))) { + int len; + + len = serdev_device_write_buf(serdev, + skb->data, skb->len); + hdev->stat.byte_tx += len; + + skb_pull(skb, len); + if (skb->len) { + hu->tx_skb = skb; + break; + } + + hci_uart_tx_complete(hu, hci_skb_pkt_type(skb)); + kfree_skb(skb); + } + } while(test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)); + + clear_bit(HCI_UART_SENDING, &hu->tx_state); +} + +/* ------- Interface to HCI layer ------ */ + +/* Initialize device */ +static int hci_uart_open(struct hci_dev *hdev) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + + BT_DBG("%s %p", hdev->name, hdev); + + serdev_device_set_client_ops(hu->serdev, &hci_serdev_client_ops); + + return serdev_device_open(hu->serdev); +} + +/* Reset device */ +static int hci_uart_flush(struct hci_dev *hdev) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + + BT_DBG("hdev %p serdev %p", hdev, hu->serdev); + + if (hu->tx_skb) { + kfree_skb(hu->tx_skb); hu->tx_skb = NULL; + } + + /* Flush any pending characters in the driver and discipline. */ + serdev_device_write_flush(hu->serdev); + + if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) + hu->proto->flush(hu); + + return 0; +} + +/* Close device */ +static int hci_uart_close(struct hci_dev *hdev) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + + BT_DBG("hdev %p", hdev); + + hci_uart_flush(hdev); + hdev->flush = NULL; + + serdev_device_close(hu->serdev); + + return 0; +} + +/* Send frames from HCI layer */ +static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + + BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb), + skb->len); + + hu->proto->enqueue(hu, skb); + + hci_uart_tx_wakeup(hu); + + return 0; +} + +static int hci_uart_setup(struct hci_dev *hdev) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct hci_rp_read_local_version *ver; + struct sk_buff *skb; + unsigned int speed; + int err; + + /* Init speed if any */ + if (hu->init_speed) + speed = hu->init_speed; + else if (hu->proto->init_speed) + speed = hu->proto->init_speed; + else + speed = 0; + + if (speed) + serdev_device_set_baudrate(hu->serdev, speed); + + /* Operational speed if any */ + if (hu->oper_speed) + speed = hu->oper_speed; + else if (hu->proto->oper_speed) + speed = hu->proto->oper_speed; + else + speed = 0; + + if (hu->proto->set_baudrate && speed) { + err = hu->proto->set_baudrate(hu, speed); + if (err) + BT_ERR("%s: failed to set baudrate", hdev->name); + else + serdev_device_set_baudrate(hu->serdev, speed); + } + + if (hu->proto->setup) + return hu->proto->setup(hu); + + if (!test_bit(HCI_UART_VND_DETECT, &hu->hdev_flags)) + return 0; + + skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + BT_ERR("%s: Reading local version information failed (%ld)", + hdev->name, PTR_ERR(skb)); + return 0; + } + + if (skb->len != sizeof(*ver)) { + BT_ERR("%s: Event length mismatch for version information", + hdev->name); + } + + kfree_skb(skb); + return 0; +} + +/** hci_uart_write_wakeup - transmit buffer wakeup + * @serdev: serial device + * + * This function is called by the serdev framework when it accepts + * more data being sent. + */ +static void hci_uart_write_wakeup(struct serdev_device *serdev) +{ + struct hci_uart *hu = serdev_device_get_drvdata(serdev); + + BT_DBG(""); + + if (!hu || serdev != hu->serdev) { + WARN_ON(1); + return; + } + + if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) + hci_uart_tx_wakeup(hu); +} + +/** hci_uart_receive_buf - receive buffer wakeup + * @serdev: serial device + * @data: pointer to received data + * @count: count of received data in bytes + * + * This function is called by the serdev framework when it received data + * in the RX buffer. + * + * Return: number of processed bytes + */ +static int hci_uart_receive_buf(struct serdev_device *serdev, const u8 *data, + size_t count) +{ + struct hci_uart *hu = serdev_device_get_drvdata(serdev); + + if (!hu || serdev != hu->serdev) { + WARN_ON(1); + return 0; + } + + if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) + return 0; + + /* 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; + + return count; +} + +struct serdev_device_ops hci_serdev_client_ops = { + .receive_buf = hci_uart_receive_buf, + .write_wakeup = hci_uart_write_wakeup, +}; + +int hci_uart_register_device(struct hci_uart *hu, + const struct hci_uart_proto *p) +{ + int err; + struct hci_dev *hdev; + + BT_DBG(""); + + err = p->open(hu); + if (err) + return err; + + hu->proto = p; + set_bit(HCI_UART_PROTO_READY, &hu->flags); + + /* Initialize and register HCI device */ + hdev = hci_alloc_dev(); + if (!hdev) { + BT_ERR("Can't allocate HCI device"); + err = -ENOMEM; + goto err_alloc; + } + + hu->hdev = hdev; + + hdev->bus = HCI_UART; + hci_set_drvdata(hdev, hu); + + INIT_WORK(&hu->write_work, hci_uart_write_work); + + /* Only when vendor specific setup callback is provided, consider + * the manufacturer information valid. This avoids filling in the + * value for Ericsson when nothing is specified. + */ + if (hu->proto->setup) + hdev->manufacturer = hu->proto->manufacturer; + + hdev->open = hci_uart_open; + hdev->close = hci_uart_close; + hdev->flush = hci_uart_flush; + hdev->send = hci_uart_send_frame; + hdev->setup = hci_uart_setup; + SET_HCIDEV_DEV(hdev, &hu->serdev->dev); + + if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags)) + set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); + + if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags)) + set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks); + + if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags)) + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); + + if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags)) + hdev->dev_type = HCI_AMP; + else + hdev->dev_type = HCI_PRIMARY; + + if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags)) + return 0; + + if (hci_register_dev(hdev) < 0) { + BT_ERR("Can't register HCI device"); + err = -ENODEV; + goto err_register; + } + + set_bit(HCI_UART_REGISTERED, &hu->flags); + + return 0; + +err_register: + hci_free_dev(hdev); +err_alloc: + clear_bit(HCI_UART_PROTO_READY, &hu->flags); + p->close(hu); + return err; +} diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 4aff50960cac..1b41c661bbb8 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -58,6 +58,7 @@ #define HCI_UART_VND_DETECT 5 struct hci_uart; +struct serdev_device; struct hci_uart_proto { unsigned int id; @@ -77,6 +78,7 @@ struct hci_uart_proto { struct hci_uart { struct tty_struct *tty; + struct serdev_device *serdev; struct hci_dev *hdev; unsigned long flags; unsigned long hdev_flags; @@ -108,6 +110,8 @@ struct hci_uart { int hci_uart_register_proto(const struct hci_uart_proto *p); int hci_uart_unregister_proto(const struct hci_uart_proto *p); +int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p); + int hci_uart_tx_wakeup(struct hci_uart *hu); int hci_uart_init_ready(struct hci_uart *hu); void hci_uart_init_tty(struct hci_uart *hu); -- cgit From 52b318e6196169e5ff8a52371833cd5a9dba05d1 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 28 Mar 2017 17:59:36 +0200 Subject: Bluetooth: hci_serdev: do not open device in hci open The device driver may need to communicate with the UART device while the Bluetooth device is closed (e.g. due to interrupts). Acked-by: Pavel Machek Signed-off-by: Sebastian Reichel Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_serdev.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c index f5ccb2c7ef92..3b8ac0ece3fb 100644 --- a/drivers/bluetooth/hci_serdev.c +++ b/drivers/bluetooth/hci_serdev.c @@ -104,13 +104,9 @@ static void hci_uart_write_work(struct work_struct *work) /* Initialize device */ static int hci_uart_open(struct hci_dev *hdev) { - struct hci_uart *hu = hci_get_drvdata(hdev); - BT_DBG("%s %p", hdev->name, hdev); - serdev_device_set_client_ops(hu->serdev, &hci_serdev_client_ops); - - return serdev_device_open(hu->serdev); + return 0; } /* Reset device */ @@ -136,15 +132,11 @@ static int hci_uart_flush(struct hci_dev *hdev) /* Close device */ static int hci_uart_close(struct hci_dev *hdev) { - struct hci_uart *hu = hci_get_drvdata(hdev); - BT_DBG("hdev %p", hdev); hci_uart_flush(hdev); hdev->flush = NULL; - serdev_device_close(hu->serdev); - return 0; } @@ -289,6 +281,8 @@ int hci_uart_register_device(struct hci_uart *hu, BT_DBG(""); + serdev_device_set_client_ops(hu->serdev, &hci_serdev_client_ops); + err = p->open(hu); if (err) return err; -- cgit From 081f36a8c2e0ae56f33ba6b12389b290a49b6508 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Tue, 28 Mar 2017 17:59:37 +0200 Subject: Bluetooth: hci_serdev: allow modular drivers For bluetooth protocol driver only supporting serdev it makes sense to follow common practice and built them into their own module. Such modules need access to hci_uart_register_device and hci_uart_tx_wakeup for using the common protocol helpers. Signed-off-by: Sebastian Reichel Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ldisc.c | 1 + drivers/bluetooth/hci_serdev.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 0ec8a94bd712..17bcbc13623f 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -134,6 +134,7 @@ int hci_uart_tx_wakeup(struct hci_uart *hu) return 0; } +EXPORT_SYMBOL_GPL(hci_uart_tx_wakeup); static void hci_uart_write_work(struct work_struct *work) { diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c index 3b8ac0ece3fb..7de0edc0ff8c 100644 --- a/drivers/bluetooth/hci_serdev.c +++ b/drivers/bluetooth/hci_serdev.c @@ -353,3 +353,4 @@ err_alloc: p->close(hu); return err; } +EXPORT_SYMBOL_GPL(hci_uart_register_device); -- cgit From 93f56de259376d7e4fff2b2d104082e1fa66e237 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Thu, 6 Apr 2017 16:31:41 -0700 Subject: mac80211: Fix clang warning about constant operand in logical operation When clang detects a non-boolean constant in a logical operation it generates a 'constant-logical-operand' warning. In ieee80211_try_rate_control_ops_get() the result of strlen() is used in a logical operation, clang resolves the expression to an (integer) constant at compile time when clang's builtin strlen function is used. Change the condition to check for strlen() > 0 to make the constant operand boolean and thus avoid the warning. Signed-off-by: Matthias Kaehlcke Signed-off-by: Johannes Berg --- net/mac80211/rate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 3bddd9bbb76f..9d7a1cd949fb 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -174,9 +174,11 @@ ieee80211_rate_control_ops_get(const char *name) /* try default if specific alg requested but not found */ ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo); - /* try built-in one if specific alg requested but not found */ - if (!ops && strlen(CONFIG_MAC80211_RC_DEFAULT)) + /* Note: check for > 0 is intentional to avoid clang warning */ + if (!ops && (strlen(CONFIG_MAC80211_RC_DEFAULT) > 0)) + /* try built-in one if specific alg requested but not found */ ops = ieee80211_try_rate_control_ops_get(CONFIG_MAC80211_RC_DEFAULT); + kernel_param_unlock(THIS_MODULE); return ops; -- cgit From 1286be270e9a9edaf534856b1143a06f0dd1f30d Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Thu, 13 Apr 2017 02:26:58 +0200 Subject: dt-bindings: net: bluetooth: Add nokia-bluetooth Add binding document for serial bluetooth chips using Nokia H4+ protocol. Acked-by: Rob Herring Signed-off-by: Sebastian Reichel Signed-off-by: Marcel Holtmann --- .../devicetree/bindings/net/nokia-bluetooth.txt | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/nokia-bluetooth.txt diff --git a/Documentation/devicetree/bindings/net/nokia-bluetooth.txt b/Documentation/devicetree/bindings/net/nokia-bluetooth.txt new file mode 100644 index 000000000000..42be7dc9a70b --- /dev/null +++ b/Documentation/devicetree/bindings/net/nokia-bluetooth.txt @@ -0,0 +1,51 @@ +Nokia Bluetooth Chips +--------------------- + +Nokia phones often come with UART connected bluetooth chips from different +vendors and modified device API. Those devices speak a protocol named H4+ +(also known as h4p) by Nokia, which is similar to the H4 protocol from the +Bluetooth standard. In addition to the H4 protocol it specifies two more +UART status lines for wakeup of UART transceivers to improve power management +and a few new packet types used to negotiate uart speed. + +Required properties: + + - compatible: should contain "nokia,h4p-bluetooth" as well as one of the following: + * "brcm,bcm2048-nokia" + * "ti,wl1271-bluetooth-nokia" + - reset-gpios: GPIO specifier, used to reset the BT module (active low) + - bluetooth-wakeup-gpios: GPIO specifier, used to wakeup the BT module (active high) + - host-wakeup-gpios: GPIO specifier, used to wakeup the host processor (active high) + - clock-names: should be "sysclk" + - clocks: should contain a clock specifier for every name in clock-names + +Optional properties: + + - None + +Example: + +/ { + /* controlled (enabled/disabled) directly by BT module */ + bluetooth_clk: vctcxo { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <38400000>; + }; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + + bluetooth { + compatible = "ti,wl1271-bluetooth-nokia", "nokia,h4p-bluetooth"; + + reset-gpios = <&gpio1 26 GPIO_ACTIVE_LOW>; /* gpio26 */ + host-wakeup-gpios = <&gpio4 5 GPIO_ACTIVE_HIGH>; /* gpio101 */ + bluetooth-wakeup-gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>; /* gpio37 */ + + clocks = <&bluetooth_clk>; + clock-names = "sysclk"; + }; +}; -- cgit From 7bb318680e868cd049922f6761170b42ff89687d Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Thu, 13 Apr 2017 02:26:59 +0200 Subject: Bluetooth: add nokia driver This adds a driver for the Nokia H4+ protocol, which is used at least on the Nokia N9, N900 & N950. Signed-off-by: Sebastian Reichel Signed-off-by: Marcel Holtmann --- drivers/bluetooth/Kconfig | 12 + drivers/bluetooth/Makefile | 2 + drivers/bluetooth/hci_nokia.c | 820 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 834 insertions(+) create mode 100644 drivers/bluetooth/hci_nokia.c diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index a6a9dd4d0eef..8aafbed9e160 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -86,6 +86,18 @@ config BT_HCIUART_H4 Say Y here to compile support for HCI UART (H4) protocol. +config BT_HCIUART_NOKIA + tristate "UART Nokia H4+ protocol support" + depends on BT_HCIUART + depends on SERIAL_DEV_BUS + depends on PM + help + Nokia H4+ is serial protocol for communication between Bluetooth + device and host. This protocol is required for Bluetooth devices + with UART interface in Nokia devices. + + Say Y here to compile support for Nokia's H4+ protocol. + config BT_HCIUART_BCSP bool "BCSP protocol support" depends on BT_HCIUART diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index fd571689eed6..a7f237320f4b 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -25,6 +25,8 @@ obj-$(CONFIG_BT_BCM) += btbcm.o obj-$(CONFIG_BT_RTL) += btrtl.o obj-$(CONFIG_BT_QCA) += btqca.o +obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o + btmrvl-y := btmrvl_main.o btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o diff --git a/drivers/bluetooth/hci_nokia.c b/drivers/bluetooth/hci_nokia.c new file mode 100644 index 000000000000..4038daf78d24 --- /dev/null +++ b/drivers/bluetooth/hci_nokia.c @@ -0,0 +1,820 @@ +/* + * Bluetooth HCI UART H4 driver with Nokia Extensions AKA Nokia H4+ + * + * Copyright (C) 2015 Marcel Holtmann + * Copyright (C) 2015-2017 Sebastian Reichel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hci_uart.h" +#include "btbcm.h" + +#define NOKIA_ID_BCM2048 0x04 +#define NOKIA_ID_TI1271 0x31 + +#define FIRMWARE_BCM2048 "nokia/bcmfw.bin" +#define FIRMWARE_TI1271 "nokia/ti1273.bin" + +#define HCI_NOKIA_NEG_PKT 0x06 +#define HCI_NOKIA_ALIVE_PKT 0x07 +#define HCI_NOKIA_RADIO_PKT 0x08 + +#define HCI_NOKIA_NEG_HDR_SIZE 1 +#define HCI_NOKIA_MAX_NEG_SIZE 255 +#define HCI_NOKIA_ALIVE_HDR_SIZE 1 +#define HCI_NOKIA_MAX_ALIVE_SIZE 255 +#define HCI_NOKIA_RADIO_HDR_SIZE 2 +#define HCI_NOKIA_MAX_RADIO_SIZE 255 + +#define NOKIA_PROTO_PKT 0x44 +#define NOKIA_PROTO_BYTE 0x4c + +#define NOKIA_NEG_REQ 0x00 +#define NOKIA_NEG_ACK 0x20 +#define NOKIA_NEG_NAK 0x40 + +#define H4_TYPE_SIZE 1 + +#define NOKIA_RECV_ALIVE \ + .type = HCI_NOKIA_ALIVE_PKT, \ + .hlen = HCI_NOKIA_ALIVE_HDR_SIZE, \ + .loff = 0, \ + .lsize = 1, \ + .maxlen = HCI_NOKIA_MAX_ALIVE_SIZE \ + +#define NOKIA_RECV_NEG \ + .type = HCI_NOKIA_NEG_PKT, \ + .hlen = HCI_NOKIA_NEG_HDR_SIZE, \ + .loff = 0, \ + .lsize = 1, \ + .maxlen = HCI_NOKIA_MAX_NEG_SIZE \ + +#define NOKIA_RECV_RADIO \ + .type = HCI_NOKIA_RADIO_PKT, \ + .hlen = HCI_NOKIA_RADIO_HDR_SIZE, \ + .loff = 1, \ + .lsize = 1, \ + .maxlen = HCI_NOKIA_MAX_RADIO_SIZE \ + +struct hci_nokia_neg_hdr { + u8 dlen; +} __packed; + +struct hci_nokia_neg_cmd { + u8 ack; + u16 baud; + u16 unused1; + u8 proto; + u16 sys_clk; + u16 unused2; +} __packed; + +#define NOKIA_ALIVE_REQ 0x55 +#define NOKIA_ALIVE_RESP 0xcc + +struct hci_nokia_alive_hdr { + u8 dlen; +} __packed; + +struct hci_nokia_alive_pkt { + u8 mid; + u8 unused; +} __packed; + +struct hci_nokia_neg_evt { + u8 ack; + u16 baud; + u16 unused1; + u8 proto; + u16 sys_clk; + u16 unused2; + u8 man_id; + u8 ver_id; +} __packed; + +#define MAX_BAUD_RATE 3692300 +#define SETUP_BAUD_RATE 921600 +#define INIT_BAUD_RATE 120000 + +struct hci_nokia_radio_hdr { + u8 evt; + u8 dlen; +} __packed; + +struct nokia_bt_dev { + struct hci_uart hu; + struct serdev_device *serdev; + + struct gpio_desc *reset; + struct gpio_desc *wakeup_host; + struct gpio_desc *wakeup_bt; + unsigned long sysclk_speed; + + int wake_irq; + struct sk_buff *rx_skb; + struct sk_buff_head txq; + bdaddr_t bdaddr; + + int init_error; + struct completion init_completion; + + u8 man_id; + u8 ver_id; + + bool initialized; + bool tx_enabled; + bool rx_enabled; +}; + +static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb); + +static void nokia_flow_control(struct serdev_device *serdev, bool enable) +{ + if (enable) { + serdev_device_set_rts(serdev, true); + serdev_device_set_flow_control(serdev, true); + } else { + serdev_device_set_flow_control(serdev, false); + serdev_device_set_rts(serdev, false); + } +} + +static irqreturn_t wakeup_handler(int irq, void *data) +{ + struct nokia_bt_dev *btdev = data; + struct device *dev = &btdev->serdev->dev; + int wake_state = gpiod_get_value(btdev->wakeup_host); + + if (btdev->rx_enabled == wake_state) + return IRQ_HANDLED; + + if (wake_state) + pm_runtime_get(dev); + else + pm_runtime_put(dev); + + btdev->rx_enabled = wake_state; + + return IRQ_HANDLED; +} + +static int nokia_reset(struct hci_uart *hu) +{ + struct nokia_bt_dev *btdev = hu->priv; + struct device *dev = &btdev->serdev->dev; + int err; + + /* reset routine */ + gpiod_set_value_cansleep(btdev->reset, 1); + gpiod_set_value_cansleep(btdev->wakeup_bt, 1); + + msleep(100); + + /* safety check */ + err = gpiod_get_value_cansleep(btdev->wakeup_host); + if (err == 1) { + dev_err(dev, "reset: host wakeup not low!"); + return -EPROTO; + } + + /* flush queue */ + serdev_device_write_flush(btdev->serdev); + + /* init uart */ + nokia_flow_control(btdev->serdev, false); + serdev_device_set_baudrate(btdev->serdev, INIT_BAUD_RATE); + + gpiod_set_value_cansleep(btdev->reset, 0); + + /* wait for cts */ + err = serdev_device_wait_for_cts(btdev->serdev, true, 200); + if (err < 0) { + dev_err(dev, "CTS not received: %d", err); + return err; + } + + nokia_flow_control(btdev->serdev, true); + + return 0; +} + +static int nokia_send_alive_packet(struct hci_uart *hu) +{ + struct nokia_bt_dev *btdev = hu->priv; + struct device *dev = &btdev->serdev->dev; + struct hci_nokia_alive_hdr *hdr; + struct hci_nokia_alive_pkt *pkt; + struct sk_buff *skb; + int len; + + init_completion(&btdev->init_completion); + + len = H4_TYPE_SIZE + sizeof(*hdr) + sizeof(*pkt); + skb = bt_skb_alloc(len, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + hci_skb_pkt_type(skb) = HCI_NOKIA_ALIVE_PKT; + memset(skb->data, 0x00, len); + + hdr = (struct hci_nokia_alive_hdr *)skb_put(skb, sizeof(*hdr)); + hdr->dlen = sizeof(*pkt); + pkt = (struct hci_nokia_alive_pkt *)skb_put(skb, sizeof(*pkt)); + pkt->mid = NOKIA_ALIVE_REQ; + + nokia_enqueue(hu, skb); + hci_uart_tx_wakeup(hu); + + dev_dbg(dev, "Alive sent"); + + if (!wait_for_completion_interruptible_timeout(&btdev->init_completion, + msecs_to_jiffies(1000))) { + return -ETIMEDOUT; + } + + if (btdev->init_error < 0) + return btdev->init_error; + + return 0; +} + +static int nokia_send_negotiation(struct hci_uart *hu) +{ + struct nokia_bt_dev *btdev = hu->priv; + struct device *dev = &btdev->serdev->dev; + struct hci_nokia_neg_cmd *neg_cmd; + struct hci_nokia_neg_hdr *neg_hdr; + struct sk_buff *skb; + int len, err; + u16 baud = DIV_ROUND_CLOSEST(btdev->sysclk_speed * 10, SETUP_BAUD_RATE); + int sysclk = btdev->sysclk_speed / 1000; + + len = H4_TYPE_SIZE + sizeof(*neg_hdr) + sizeof(*neg_cmd); + skb = bt_skb_alloc(len, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + hci_skb_pkt_type(skb) = HCI_NOKIA_NEG_PKT; + + neg_hdr = (struct hci_nokia_neg_hdr *)skb_put(skb, sizeof(*neg_hdr)); + neg_hdr->dlen = sizeof(*neg_cmd); + + neg_cmd = (struct hci_nokia_neg_cmd *)skb_put(skb, sizeof(*neg_cmd)); + neg_cmd->ack = NOKIA_NEG_REQ; + neg_cmd->baud = cpu_to_le16(baud); + neg_cmd->unused1 = 0x0000; + neg_cmd->proto = NOKIA_PROTO_BYTE; + neg_cmd->sys_clk = cpu_to_le16(sysclk); + neg_cmd->unused2 = 0x0000; + + btdev->init_error = 0; + init_completion(&btdev->init_completion); + + nokia_enqueue(hu, skb); + hci_uart_tx_wakeup(hu); + + dev_dbg(dev, "Negotiation sent"); + + if (!wait_for_completion_interruptible_timeout(&btdev->init_completion, + msecs_to_jiffies(10000))) { + return -ETIMEDOUT; + } + + if (btdev->init_error < 0) + return btdev->init_error; + + /* Change to previously negotiated speed. Flow Control + * is disabled until bluetooth adapter is ready to avoid + * broken bytes being received. + */ + nokia_flow_control(btdev->serdev, false); + serdev_device_set_baudrate(btdev->serdev, SETUP_BAUD_RATE); + err = serdev_device_wait_for_cts(btdev->serdev, true, 200); + if (err < 0) { + dev_err(dev, "CTS not received: %d", err); + return err; + } + nokia_flow_control(btdev->serdev, true); + + dev_dbg(dev, "Negotiation successful"); + + return 0; +} + +static int nokia_setup_fw(struct hci_uart *hu) +{ + struct nokia_bt_dev *btdev = hu->priv; + struct device *dev = &btdev->serdev->dev; + const char *fwname; + const struct firmware *fw; + const u8 *fw_ptr; + size_t fw_size; + int err; + + dev_dbg(dev, "setup firmware"); + + if (btdev->man_id == NOKIA_ID_BCM2048) { + fwname = FIRMWARE_BCM2048; + } else if (btdev->man_id == NOKIA_ID_TI1271) { + fwname = FIRMWARE_TI1271; + } else { + dev_err(dev, "Unsupported bluetooth device!"); + return -ENODEV; + } + + err = request_firmware(&fw, fwname, dev); + if (err < 0) { + dev_err(dev, "%s: Failed to load Nokia firmware file (%d)", + hu->hdev->name, err); + return err; + } + + fw_ptr = fw->data; + fw_size = fw->size; + + while (fw_size >= 4) { + u16 pkt_size = get_unaligned_le16(fw_ptr); + u8 pkt_type = fw_ptr[2]; + const struct hci_command_hdr *cmd; + u16 opcode; + struct sk_buff *skb; + + switch (pkt_type) { + case HCI_COMMAND_PKT: + cmd = (struct hci_command_hdr *)(fw_ptr + 3); + opcode = le16_to_cpu(cmd->opcode); + + skb = __hci_cmd_sync(hu->hdev, opcode, cmd->plen, + fw_ptr + 3 + HCI_COMMAND_HDR_SIZE, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + dev_err(dev, "%s: FW command %04x failed (%d)", + hu->hdev->name, opcode, err); + goto done; + } + kfree_skb(skb); + break; + case HCI_NOKIA_RADIO_PKT: + case HCI_NOKIA_NEG_PKT: + case HCI_NOKIA_ALIVE_PKT: + break; + } + + fw_ptr += pkt_size + 2; + fw_size -= pkt_size + 2; + } + +done: + release_firmware(fw); + return err; +} + +static int nokia_setup(struct hci_uart *hu) +{ + struct nokia_bt_dev *btdev = hu->priv; + struct device *dev = &btdev->serdev->dev; + int err; + + btdev->initialized = false; + + nokia_flow_control(btdev->serdev, false); + + pm_runtime_get_sync(dev); + + if (btdev->tx_enabled) { + gpiod_set_value_cansleep(btdev->wakeup_bt, 0); + pm_runtime_put(&btdev->serdev->dev); + btdev->tx_enabled = false; + } + + dev_dbg(dev, "protocol setup"); + + /* 0. reset connection */ + err = nokia_reset(hu); + if (err < 0) { + dev_err(dev, "Reset failed: %d", err); + goto out; + } + + /* 1. negotiate speed etc */ + err = nokia_send_negotiation(hu); + if (err < 0) { + dev_err(dev, "Negotiation failed: %d", err); + goto out; + } + + /* 2. verify correct setup using alive packet */ + err = nokia_send_alive_packet(hu); + if (err < 0) { + dev_err(dev, "Alive check failed: %d", err); + goto out; + } + + /* 3. send firmware */ + err = nokia_setup_fw(hu); + if (err < 0) { + dev_err(dev, "Could not setup FW: %d", err); + goto out; + } + + nokia_flow_control(btdev->serdev, false); + serdev_device_set_baudrate(btdev->serdev, MAX_BAUD_RATE); + nokia_flow_control(btdev->serdev, true); + + if (btdev->man_id == NOKIA_ID_BCM2048) { + hu->hdev->set_bdaddr = btbcm_set_bdaddr; + set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks); + dev_dbg(dev, "bcm2048 has invalid bluetooth address!"); + } + + dev_dbg(dev, "protocol setup done!"); + + gpiod_set_value_cansleep(btdev->wakeup_bt, 0); + pm_runtime_put(dev); + btdev->tx_enabled = false; + btdev->initialized = true; + + return 0; +out: + pm_runtime_put(dev); + + return err; +} + +static int nokia_open(struct hci_uart *hu) +{ + struct device *dev = &hu->serdev->dev; + + dev_dbg(dev, "protocol open"); + + serdev_device_open(hu->serdev); + + pm_runtime_enable(dev); + + return 0; +} + +static int nokia_flush(struct hci_uart *hu) +{ + struct nokia_bt_dev *btdev = hu->priv; + + dev_dbg(&btdev->serdev->dev, "flush device"); + + skb_queue_purge(&btdev->txq); + + return 0; +} + +static int nokia_close(struct hci_uart *hu) +{ + struct nokia_bt_dev *btdev = hu->priv; + struct device *dev = &btdev->serdev->dev; + + dev_dbg(dev, "close device"); + + btdev->initialized = false; + + skb_queue_purge(&btdev->txq); + + kfree_skb(btdev->rx_skb); + + /* disable module */ + gpiod_set_value(btdev->reset, 1); + gpiod_set_value(btdev->wakeup_bt, 0); + + pm_runtime_disable(&btdev->serdev->dev); + serdev_device_close(btdev->serdev); + + return 0; +} + +/* Enqueue frame for transmittion (padding, crc, etc) */ +static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb) +{ + struct nokia_bt_dev *btdev = hu->priv; + int err; + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + + /* Packets must be word aligned */ + if (skb->len % 2) { + err = skb_pad(skb, 1); + if (err) + return err; + *skb_put(skb, 1) = 0x00; + } + + skb_queue_tail(&btdev->txq, skb); + + return 0; +} + +static int nokia_recv_negotiation_packet(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct nokia_bt_dev *btdev = hu->priv; + struct device *dev = &btdev->serdev->dev; + struct hci_nokia_neg_hdr *hdr; + struct hci_nokia_neg_evt *evt; + int ret = 0; + + hdr = (struct hci_nokia_neg_hdr *)skb->data; + if (hdr->dlen != sizeof(*evt)) { + btdev->init_error = -EIO; + ret = -EIO; + goto finish_neg; + } + + evt = (struct hci_nokia_neg_evt *)skb_pull(skb, sizeof(*hdr)); + + if (evt->ack != NOKIA_NEG_ACK) { + dev_err(dev, "Negotiation received: wrong reply"); + btdev->init_error = -EINVAL; + ret = -EINVAL; + goto finish_neg; + } + + btdev->man_id = evt->man_id; + btdev->ver_id = evt->ver_id; + + dev_dbg(dev, "Negotiation received: baud=%u:clk=%u:manu=%u:vers=%u", + evt->baud, evt->sys_clk, evt->man_id, evt->ver_id); + +finish_neg: + complete(&btdev->init_completion); + kfree_skb(skb); + return ret; +} + +static int nokia_recv_alive_packet(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct nokia_bt_dev *btdev = hu->priv; + struct device *dev = &btdev->serdev->dev; + struct hci_nokia_alive_hdr *hdr; + struct hci_nokia_alive_pkt *pkt; + int ret = 0; + + hdr = (struct hci_nokia_alive_hdr *)skb->data; + if (hdr->dlen != sizeof(*pkt)) { + dev_err(dev, "Corrupted alive message"); + btdev->init_error = -EIO; + ret = -EIO; + goto finish_alive; + } + + pkt = (struct hci_nokia_alive_pkt *)skb_pull(skb, sizeof(*hdr)); + + if (pkt->mid != NOKIA_ALIVE_RESP) { + dev_err(dev, "Alive received: invalid response: 0x%02x!", + pkt->mid); + btdev->init_error = -EINVAL; + ret = -EINVAL; + goto finish_alive; + } + + dev_dbg(dev, "Alive received"); + +finish_alive: + complete(&btdev->init_completion); + kfree_skb(skb); + return ret; +} + +static int nokia_recv_radio(struct hci_dev *hdev, struct sk_buff *skb) +{ + /* Packets received on the dedicated radio channel are + * HCI events and so feed them back into the core. + */ + hci_skb_pkt_type(skb) = HCI_EVENT_PKT; + return hci_recv_frame(hdev, skb); +} + +/* Recv data */ +static const struct h4_recv_pkt nokia_recv_pkts[] = { + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = hci_recv_frame }, + { NOKIA_RECV_ALIVE, .recv = nokia_recv_alive_packet }, + { NOKIA_RECV_NEG, .recv = nokia_recv_negotiation_packet }, + { NOKIA_RECV_RADIO, .recv = nokia_recv_radio }, +}; + +static int nokia_recv(struct hci_uart *hu, const void *data, int count) +{ + struct nokia_bt_dev *btdev = hu->priv; + struct device *dev = &btdev->serdev->dev; + int err; + + if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) + return -EUNATCH; + + btdev->rx_skb = h4_recv_buf(hu->hdev, btdev->rx_skb, data, count, + nokia_recv_pkts, ARRAY_SIZE(nokia_recv_pkts)); + if (IS_ERR(btdev->rx_skb)) { + err = PTR_ERR(btdev->rx_skb); + dev_err(dev, "Frame reassembly failed (%d)", err); + btdev->rx_skb = NULL; + return err; + } + + return count; +} + +static struct sk_buff *nokia_dequeue(struct hci_uart *hu) +{ + struct nokia_bt_dev *btdev = hu->priv; + struct device *dev = &btdev->serdev->dev; + struct sk_buff *result = skb_dequeue(&btdev->txq); + + if (!btdev->initialized) + return result; + + if (btdev->tx_enabled == !!result) + return result; + + if (result) { + pm_runtime_get_sync(dev); + gpiod_set_value_cansleep(btdev->wakeup_bt, 1); + } else { + serdev_device_wait_until_sent(btdev->serdev, 0); + gpiod_set_value_cansleep(btdev->wakeup_bt, 0); + pm_runtime_put(dev); + } + + btdev->tx_enabled = !!result; + + return result; +} + +static const struct hci_uart_proto nokia_proto = { + .id = HCI_UART_NOKIA, + .name = "Nokia", + .open = nokia_open, + .close = nokia_close, + .recv = nokia_recv, + .enqueue = nokia_enqueue, + .dequeue = nokia_dequeue, + .flush = nokia_flush, + .setup = nokia_setup, + .manufacturer = 1, +}; + +static int nokia_bluetooth_serdev_probe(struct serdev_device *serdev) +{ + struct device *dev = &serdev->dev; + struct nokia_bt_dev *btdev; + struct clk *sysclk; + int err = 0; + + btdev = devm_kzalloc(dev, sizeof(*btdev), GFP_KERNEL); + if (!btdev) + return -ENOMEM; + + btdev->hu.serdev = btdev->serdev = serdev; + serdev_device_set_drvdata(serdev, btdev); + + btdev->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(btdev->reset)) { + err = PTR_ERR(btdev->reset); + dev_err(dev, "could not get reset gpio: %d", err); + return err; + } + + btdev->wakeup_host = devm_gpiod_get(dev, "host-wakeup", GPIOD_IN); + if (IS_ERR(btdev->wakeup_host)) { + err = PTR_ERR(btdev->wakeup_host); + dev_err(dev, "could not get host wakeup gpio: %d", err); + return err; + } + + btdev->wake_irq = gpiod_to_irq(btdev->wakeup_host); + + err = devm_request_threaded_irq(dev, btdev->wake_irq, NULL, + wakeup_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "wakeup", btdev); + if (err) { + dev_err(dev, "could request wakeup irq: %d", err); + return err; + } + + btdev->wakeup_bt = devm_gpiod_get(dev, "bluetooth-wakeup", + GPIOD_OUT_LOW); + if (IS_ERR(btdev->wakeup_bt)) { + err = PTR_ERR(btdev->wakeup_bt); + dev_err(dev, "could not get BT wakeup gpio: %d", err); + return err; + } + + sysclk = devm_clk_get(dev, "sysclk"); + if (IS_ERR(sysclk)) { + err = PTR_ERR(sysclk); + dev_err(dev, "could not get sysclk: %d", err); + return err; + } + + clk_prepare_enable(sysclk); + btdev->sysclk_speed = clk_get_rate(sysclk); + clk_disable_unprepare(sysclk); + + skb_queue_head_init(&btdev->txq); + + btdev->hu.priv = btdev; + btdev->hu.alignment = 2; /* Nokia H4+ is word aligned */ + + err = hci_uart_register_device(&btdev->hu, &nokia_proto); + if (err) { + dev_err(dev, "could not register bluetooth uart: %d", err); + return err; + } + + return 0; +} + +static void nokia_bluetooth_serdev_remove(struct serdev_device *serdev) +{ + struct nokia_bt_dev *btdev = serdev_device_get_drvdata(serdev); + struct hci_uart *hu = &btdev->hu; + struct hci_dev *hdev = hu->hdev; + + cancel_work_sync(&hu->write_work); + + hci_unregister_dev(hdev); + hci_free_dev(hdev); + hu->proto->close(hu); + + pm_runtime_disable(&btdev->serdev->dev); +} + +static int nokia_bluetooth_runtime_suspend(struct device *dev) +{ + struct serdev_device *serdev = to_serdev_device(dev); + + nokia_flow_control(serdev, false); + return 0; +} + +static int nokia_bluetooth_runtime_resume(struct device *dev) +{ + struct serdev_device *serdev = to_serdev_device(dev); + + nokia_flow_control(serdev, true); + return 0; +} + +static const struct dev_pm_ops nokia_bluetooth_pm_ops = { + SET_RUNTIME_PM_OPS(nokia_bluetooth_runtime_suspend, + nokia_bluetooth_runtime_resume, + NULL) +}; + +#ifdef CONFIG_OF +static const struct of_device_id nokia_bluetooth_of_match[] = { + { .compatible = "nokia,h4p-bluetooth", }, + {}, +}; +MODULE_DEVICE_TABLE(of, nokia_bluetooth_of_match); +#endif + +static struct serdev_device_driver nokia_bluetooth_serdev_driver = { + .probe = nokia_bluetooth_serdev_probe, + .remove = nokia_bluetooth_serdev_remove, + .driver = { + .name = "nokia-bluetooth", + .pm = &nokia_bluetooth_pm_ops, + .of_match_table = of_match_ptr(nokia_bluetooth_of_match), + }, +}; + +module_serdev_device_driver(nokia_bluetooth_serdev_driver); -- cgit From 30841f5cdeccd24c4a68b9df681b3ef11b0dda53 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 11 Apr 2017 15:38:56 +0200 Subject: mac80211: drop frames too short for FCS earlier Instead of dropping such frames only when removing the monitor info, drop them earlier (keeping the warning) and simplify removing monitor info. While at it, make that function return void. Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index e48724a6725e..e35c42ebb7a5 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -95,24 +95,13 @@ static u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, * This function cleans up the SKB, i.e. it removes all the stuff * only useful for monitoring. */ -static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, - struct sk_buff *skb, - unsigned int rtap_vendor_space) +static void remove_monitor_info(struct sk_buff *skb, + unsigned int present_fcs_len, + unsigned int rtap_vendor_space) { - if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) { - if (likely(skb->len > FCS_LEN)) - __pskb_trim(skb, skb->len - FCS_LEN); - else { - /* driver bug */ - WARN_ON(1); - dev_kfree_skb(skb); - return NULL; - } - } - + if (present_fcs_len) + __pskb_trim(skb, skb->len - present_fcs_len); __pskb_pull(skb, rtap_vendor_space); - - return skb; } static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len, @@ -534,8 +523,15 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, * the SKB because it has a bad FCS/PLCP checksum. */ - if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) + if (ieee80211_hw_check(&local->hw, RX_INCLUDES_FCS)) { + if (unlikely(origskb->len <= FCS_LEN)) { + /* driver bug */ + WARN_ON(1); + dev_kfree_skb(origskb); + return NULL; + } present_fcs_len = FCS_LEN; + } /* ensure hdr->frame_control and vendor radiotap data are in skb head */ if (!pskb_may_pull(origskb, 2 + rtap_vendor_space)) { @@ -550,7 +546,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, return NULL; } - return remove_monitor_info(local, origskb, rtap_vendor_space); + remove_monitor_info(origskb, present_fcs_len, + rtap_vendor_space); + return origskb; } /* room for the radiotap header based on driver features */ @@ -580,9 +578,8 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, * and FCS from the original. */ skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC); - - origskb = remove_monitor_info(local, origskb, - rtap_vendor_space); + remove_monitor_info(origskb, present_fcs_len, + rtap_vendor_space); if (!skb) return origskb; -- cgit From b0265024b8b5fb35d1e1a1da6be65399e33e122e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 10:43:49 +0200 Subject: cfg80211: allow leaving MU-MIMO monitor configuration unchanged When changing monitor parameters, not setting the MU-MIMO attributes should mean that they're not changed - it's documented that to turn the feature off it's necessary to set all-zero group membership and an invalid follow-address. This isn't implemented. Fix this by making the parameters pointers, stop reusing the macaddr struct member, and documenting that NULL pointers mean unchanged. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 9 ++++++--- net/mac80211/cfg.c | 8 ++++---- net/wireless/nl80211.c | 8 +++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 273b1dca0861..ba9348ee5327 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -370,13 +370,16 @@ static inline void wiphy_read_of_freq_limits(struct wiphy *wiphy) * This feature is only fully supported by drivers that enable the * %NL80211_FEATURE_MAC_ON_CREATE flag. Others may support creating ** only p2p devices with specified MAC. - * @vht_mumimo_groups: MU-MIMO groupID. used for monitoring only - * packets belonging to that MU-MIMO groupID. + * @vht_mumimo_groups: MU-MIMO groupID, used for monitoring MU-MIMO packets + * belonging to that MU-MIMO groupID; %NULL if not changed + * @vht_mumimo_follow_addr: MU-MIMO follow address, used for monitoring + * MU-MIMO packets going to the specified station; %NULL if not changed */ struct vif_params { int use_4addr; u8 macaddr[ETH_ALEN]; - u8 vht_mumimo_groups[VHT_MUMIMO_GROUPS_DATA_LEN]; + const u8 *vht_mumimo_groups; + const u8 *vht_mumimo_follow_addr; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8bc3d3669348..ef7de9eb94b1 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -80,8 +80,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy, u32 mu_mntr_cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; monitor_sdata = rtnl_dereference(local->monitor_sdata); - if (monitor_sdata && - wiphy_ext_feature_isset(wiphy, mu_mntr_cap_flag)) { + if (monitor_sdata && params->vht_mumimo_groups) { memcpy(monitor_sdata->vif.bss_conf.mu_group.membership, params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN); memcpy(monitor_sdata->vif.bss_conf.mu_group.position, @@ -90,10 +89,11 @@ static int ieee80211_change_iface(struct wiphy *wiphy, monitor_sdata->vif.mu_mimo_owner = true; ieee80211_bss_info_change_notify(monitor_sdata, BSS_CHANGED_MU_GROUPS); + } + if (monitor_sdata && params->vht_mumimo_follow_addr) ether_addr_copy(monitor_sdata->u.mntr.mu_follow_addr, - params->macaddr); - } + params->vht_mumimo_follow_addr); if (!flags) return 0; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9910aae08f1a..5cdb0f9b0168 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2832,8 +2832,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) (mumimo_groups[VHT_MUMIMO_GROUPS_DATA_LEN - 1] & BIT(0))) return -EINVAL; - memcpy(params.vht_mumimo_groups, mumimo_groups, - VHT_MUMIMO_GROUPS_DATA_LEN); + params.vht_mumimo_groups = mumimo_groups; change = true; } @@ -2843,9 +2842,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) if (!wiphy_ext_feature_isset(&rdev->wiphy, cap_flag)) return -EOPNOTSUPP; - nla_memcpy(params.macaddr, - info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR], - ETH_ALEN); + params.vht_mumimo_follow_addr = + nla_data(info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]); change = true; } -- cgit From 8c5e68894450d3bb7471e426e2eec9a8472bb660 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 10:46:13 +0200 Subject: mac80211: correct MU-MIMO monitor follow functionality The MU-MIMO monitor follow functionality is broken because it doesn't clear the MU-MIMO owner even if both follow features are disabled. Fix that, and while at it move the code into a new helper function. Call this also when creating a new monitor interface to prepare for an upcoming cfg80211 change allowing that. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 78 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 20 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index ef7de9eb94b1..e276b8cb24a4 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -22,6 +22,49 @@ #include "mesh.h" #include "wme.h" +static int ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata, + struct vif_params *params) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *monitor_sdata; + bool mu_mimo_groups = false; + bool mu_mimo_follow = false; + + monitor_sdata = rtnl_dereference(local->monitor_sdata); + + if (!monitor_sdata) + return -EOPNOTSUPP; + + if (params->vht_mumimo_groups) { + u64 membership; + + BUILD_BUG_ON(sizeof(membership) != WLAN_MEMBERSHIP_LEN); + + memcpy(monitor_sdata->vif.bss_conf.mu_group.membership, + params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN); + memcpy(monitor_sdata->vif.bss_conf.mu_group.position, + params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN, + WLAN_USER_POSITION_LEN); + ieee80211_bss_info_change_notify(monitor_sdata, + BSS_CHANGED_MU_GROUPS); + /* don't care about endianness - just check for 0 */ + memcpy(&membership, params->vht_mumimo_groups, + WLAN_MEMBERSHIP_LEN); + mu_mimo_groups = membership != 0; + } + + if (params->vht_mumimo_follow_addr) { + mu_mimo_follow = + is_valid_ether_addr(params->vht_mumimo_follow_addr); + ether_addr_copy(monitor_sdata->u.mntr.mu_follow_addr, + params->vht_mumimo_follow_addr); + } + + monitor_sdata->vif.mu_mimo_owner = mu_mimo_groups || mu_mimo_follow; + + return 0; +} + static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, @@ -38,9 +81,17 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, if (err) return ERR_PTR(err); - if (type == NL80211_IFTYPE_MONITOR && flags) { - sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); - sdata->u.mntr.flags = *flags; + sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + + if (type == NL80211_IFTYPE_MONITOR) { + err = ieee80211_set_mu_mimo_follow(sdata, params); + if (err) { + ieee80211_if_remove(sdata); + return NULL; + } + + if (flags) + sdata->u.mntr.flags = *flags; } return wdev; @@ -76,24 +127,11 @@ static int ieee80211_change_iface(struct wiphy *wiphy, if (sdata->vif.type == NL80211_IFTYPE_MONITOR) { struct ieee80211_local *local = sdata->local; - struct ieee80211_sub_if_data *monitor_sdata; - u32 mu_mntr_cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; - - monitor_sdata = rtnl_dereference(local->monitor_sdata); - if (monitor_sdata && params->vht_mumimo_groups) { - memcpy(monitor_sdata->vif.bss_conf.mu_group.membership, - params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN); - memcpy(monitor_sdata->vif.bss_conf.mu_group.position, - params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN, - WLAN_USER_POSITION_LEN); - monitor_sdata->vif.mu_mimo_owner = true; - ieee80211_bss_info_change_notify(monitor_sdata, - BSS_CHANGED_MU_GROUPS); - } + int err; - if (monitor_sdata && params->vht_mumimo_follow_addr) - ether_addr_copy(monitor_sdata->u.mntr.mu_follow_addr, - params->vht_mumimo_follow_addr); + err = ieee80211_set_mu_mimo_follow(sdata, params); + if (err) + return err; if (!flags) return 0; -- cgit From 818a986e4ebacea2020622e48c8bc04b7f500d89 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 11:23:28 +0200 Subject: cfg80211: move add/change interface monitor flags into params Instead passing both flags, which can be NULL, and vif_params, which are never NULL, move the flags into the vif_params and use BIT(0), which is invalid from userspace, to indicate that the flags were changed. While updating all drivers, fix a small bug in wil6210 where it was setting the flags to 0 instead of leaving them unchanged. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 3 +- drivers/net/wireless/ath/wil6210/cfg80211.c | 11 +++---- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 12 ++++---- .../net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 3 +- .../net/wireless/broadcom/brcm80211/brcmfmac/p2p.h | 2 +- drivers/net/wireless/intersil/orinoco/cfg.c | 2 +- drivers/net/wireless/marvell/libertas/cfg.c | 2 +- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 27 ++++++++--------- drivers/net/wireless/marvell/mwifiex/main.c | 7 ++--- drivers/net/wireless/marvell/mwifiex/main.h | 1 - drivers/net/wireless/rndis_wlan.c | 4 +-- drivers/staging/wilc1000/wilc_wfi_cfgoperations.c | 3 +- drivers/staging/wlan-ng/cfg80211.c | 2 +- include/net/cfg80211.h | 8 +++-- net/mac80211/cfg.c | 15 +++++----- net/wireless/core.h | 2 +- net/wireless/nl80211.c | 34 ++++++++++++---------- net/wireless/rdev-ops.h | 9 +++--- net/wireless/util.c | 4 +-- net/wireless/wext-compat.c | 2 +- 20 files changed, 73 insertions(+), 80 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 363b30a549c2..48dc66aac67d 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1505,7 +1505,6 @@ static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, - u32 *flags, struct vif_params *params) { struct ath6kl *ar = wiphy_priv(wiphy); @@ -1552,7 +1551,7 @@ static int ath6kl_cfg80211_del_iface(struct wiphy *wiphy, static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct ath6kl_vif *vif = netdev_priv(ndev); diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 79d107018eac..17fc152e33d9 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -255,7 +255,7 @@ static struct wireless_dev * wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, - u32 *flags, struct vif_params *params) + struct vif_params *params) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); struct net_device *ndev = wil_to_ndev(wil); @@ -306,7 +306,7 @@ static int wil_cfg80211_del_iface(struct wiphy *wiphy, static int wil_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); @@ -333,11 +333,8 @@ static int wil_cfg80211_change_iface(struct wiphy *wiphy, case NL80211_IFTYPE_P2P_GO: break; case NL80211_IFTYPE_MONITOR: - if (flags) - wil->monitor_flags = *flags; - else - wil->monitor_flags = 0; - + if (params->flags) + wil->monitor_flags = params->flags; break; default: return -EOPNOTSUPP; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 944b83cfc519..bd245a6c70d9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -575,12 +575,11 @@ static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) * * @wiphy: wiphy device of new interface. * @name: name of the new interface. - * @flags: not used. * @params: contains mac address for AP device. */ static struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, - u32 *flags, struct vif_params *params) + struct vif_params *params) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); @@ -653,7 +652,6 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, - u32 *flags, struct vif_params *params) { struct wireless_dev *wdev; @@ -674,12 +672,12 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, case NL80211_IFTYPE_MESH_POINT: return ERR_PTR(-EOPNOTSUPP); case NL80211_IFTYPE_AP: - wdev = brcmf_ap_add_vif(wiphy, name, flags, params); + wdev = brcmf_ap_add_vif(wiphy, name, params); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: - wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params); + wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, params); break; case NL80211_IFTYPE_UNSPECIFIED: default: @@ -858,7 +856,7 @@ int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) static s32 brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); @@ -6549,7 +6547,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) if (err) goto default_conf_out; err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, - NULL, NULL); + NULL); if (err) goto default_conf_out; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index de19c7c92bc6..208ae3535367 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -2141,12 +2141,11 @@ fail: * @name: name of the new interface. * @name_assign_type: origin of the interface name * @type: nl80211 interface type. - * @flags: not used. * @params: contains mac address for P2P device. */ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h index 8ce9447533ef..0e8b34d2d85c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h @@ -150,7 +150,7 @@ 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, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params); int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev); int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg, diff --git a/drivers/net/wireless/intersil/orinoco/cfg.c b/drivers/net/wireless/intersil/orinoco/cfg.c index 7aa47069af0a..b2d5ec8634b5 100644 --- a/drivers/net/wireless/intersil/orinoco/cfg.c +++ b/drivers/net/wireless/intersil/orinoco/cfg.c @@ -97,7 +97,7 @@ int orinoco_wiphy_register(struct wiphy *wiphy) } static int orinoco_change_vif(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct orinoco_private *priv = wiphy_priv(wiphy); diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c index 3f97acb57e66..a0463fef79b0 100644 --- a/drivers/net/wireless/marvell/libertas/cfg.c +++ b/drivers/net/wireless/marvell/libertas/cfg.c @@ -1657,7 +1657,7 @@ static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, */ static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct lbs_private *priv = wiphy_priv(wiphy); diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 1e3bd435a694..322adad0a65a 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -916,7 +916,7 @@ mwifiex_init_new_priv_params(struct mwifiex_private *priv, static int mwifiex_change_vif_to_p2p(struct net_device *dev, enum nl80211_iftype curr_iftype, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct mwifiex_private *priv; @@ -988,7 +988,7 @@ mwifiex_change_vif_to_p2p(struct net_device *dev, static int mwifiex_change_vif_to_sta_adhoc(struct net_device *dev, enum nl80211_iftype curr_iftype, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct mwifiex_private *priv; @@ -1047,7 +1047,7 @@ mwifiex_change_vif_to_sta_adhoc(struct net_device *dev, static int mwifiex_change_vif_to_ap(struct net_device *dev, enum nl80211_iftype curr_iftype, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct mwifiex_private *priv; @@ -1103,7 +1103,7 @@ mwifiex_change_vif_to_ap(struct net_device *dev, static int mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); @@ -1124,10 +1124,10 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: return mwifiex_change_vif_to_p2p(dev, curr_iftype, - type, flags, params); + type, params); case NL80211_IFTYPE_AP: return mwifiex_change_vif_to_ap(dev, curr_iftype, type, - flags, params); + params); case NL80211_IFTYPE_UNSPECIFIED: mwifiex_dbg(priv->adapter, INFO, "%s: kept type as IBSS\n", dev->name); @@ -1154,10 +1154,10 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: return mwifiex_change_vif_to_p2p(dev, curr_iftype, - type, flags, params); + type, params); case NL80211_IFTYPE_AP: return mwifiex_change_vif_to_ap(dev, curr_iftype, type, - flags, params); + params); case NL80211_IFTYPE_UNSPECIFIED: mwifiex_dbg(priv->adapter, INFO, "%s: kept type as STA\n", dev->name); @@ -1175,13 +1175,12 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_STATION: return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, - type, flags, - params); + type, params); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: return mwifiex_change_vif_to_p2p(dev, curr_iftype, - type, flags, params); + type, params); case NL80211_IFTYPE_UNSPECIFIED: mwifiex_dbg(priv->adapter, INFO, "%s: kept type as AP\n", dev->name); @@ -1214,14 +1213,13 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, if (mwifiex_cfg80211_deinit_p2p(priv)) return -EFAULT; return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, - type, flags, - params); + type, params); break; case NL80211_IFTYPE_AP: if (mwifiex_cfg80211_deinit_p2p(priv)) return -EFAULT; return mwifiex_change_vif_to_ap(dev, curr_iftype, type, - flags, params); + params); case NL80211_IFTYPE_UNSPECIFIED: mwifiex_dbg(priv->adapter, INFO, "%s: kept type as P2P\n", dev->name); @@ -2822,7 +2820,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, - u32 *flags, struct vif_params *params) { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 5ebca1d0cfc7..96b5fc5e5398 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -593,7 +593,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) rtnl_lock(); /* Create station interface by default */ wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM, - NL80211_IFTYPE_STATION, NULL, NULL); + NL80211_IFTYPE_STATION, NULL); if (IS_ERR(wdev)) { mwifiex_dbg(adapter, ERROR, "cannot create default STA interface\n"); @@ -603,7 +603,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) { wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM, - NL80211_IFTYPE_AP, NULL, NULL); + NL80211_IFTYPE_AP, NULL); if (IS_ERR(wdev)) { mwifiex_dbg(adapter, ERROR, "cannot create AP interface\n"); @@ -614,8 +614,7 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) { wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM, - NL80211_IFTYPE_P2P_CLIENT, NULL, - NULL); + NL80211_IFTYPE_P2P_CLIENT, NULL); if (IS_ERR(wdev)) { mwifiex_dbg(adapter, ERROR, "cannot create p2p client interface\n"); diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 5c8297207f33..bc19b0a3571f 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1529,7 +1529,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, - u32 *flags, struct vif_params *params); int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev); diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 785334f7a538..3495386482be 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -479,7 +479,7 @@ struct rndis_wlan_private { */ static int rndis_change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params); static int rndis_scan(struct wiphy *wiphy, @@ -1857,7 +1857,7 @@ error: */ static int rndis_change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); diff --git a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c index 7961d1c56847..2b4536318ca6 100644 --- a/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c +++ b/drivers/staging/wilc1000/wilc_wfi_cfgoperations.c @@ -1837,7 +1837,7 @@ static int set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, } static int change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, struct vif_params *params) + enum nl80211_iftype type, struct vif_params *params) { struct wilc_priv *priv; struct wilc_vif *vif; @@ -2099,7 +2099,6 @@ static struct wireless_dev *add_virtual_intf(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, enum nl80211_iftype type, - u32 *flags, struct vif_params *params) { struct wilc_vif *vif; diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index 11870cb3f254..cbb3388a9756 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -100,7 +100,7 @@ static int prism2_domibset_pstr32(struct wlandevice *wlandev, /* The interface functions, called by the cfg80211 layer */ static int prism2_change_virtual_intf(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct wlandevice *wlandev = dev->ml_priv; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ba9348ee5327..89fa4995ddca 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -363,6 +363,8 @@ static inline void wiphy_read_of_freq_limits(struct wiphy *wiphy) /** * struct vif_params - describes virtual interface parameters + * @flags: monitor interface flags, unchanged if 0, otherwise + * %MONITOR_FLAG_CHANGED will be set * @use_4addr: use 4-address frames * @macaddr: address to use for this virtual interface. * If this parameter is set to zero address the driver may @@ -376,6 +378,7 @@ static inline void wiphy_read_of_freq_limits(struct wiphy *wiphy) * MU-MIMO packets going to the specified station; %NULL if not changed */ struct vif_params { + u32 flags; int use_4addr; u8 macaddr[ETH_ALEN]; const u8 *vht_mumimo_groups; @@ -1214,6 +1217,7 @@ static inline int cfg80211_get_station(struct net_device *dev, * Monitor interface configuration flags. Note that these must be the bits * according to the nl80211 flags. * + * @MONITOR_FLAG_CHANGED: set if the flags were changed * @MONITOR_FLAG_FCSFAIL: pass frames with bad FCS * @MONITOR_FLAG_PLCPFAIL: pass frames with bad PLCP * @MONITOR_FLAG_CONTROL: pass control frames @@ -1222,6 +1226,7 @@ static inline int cfg80211_get_station(struct net_device *dev, * @MONITOR_FLAG_ACTIVE: active monitor, ACKs frames on its MAC address */ enum monitor_flags { + MONITOR_FLAG_CHANGED = 1<<__NL80211_MNTR_FLAG_INVALID, MONITOR_FLAG_FCSFAIL = 1<u.mntr.flags = *flags; + sdata->u.mntr.flags = params->flags; } return wdev; @@ -106,7 +104,7 @@ static int ieee80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) static int ieee80211_change_iface(struct wiphy *wiphy, struct net_device *dev, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -133,7 +131,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy, if (err) return err; - if (!flags) + if (!params->flags) return 0; if (ieee80211_sdata_running(sdata)) { @@ -149,11 +147,12 @@ static int ieee80211_change_iface(struct wiphy *wiphy, * cooked_mntrs, monitor and all fif_* counters * reconfigure hardware */ - if ((*flags & mask) != (sdata->u.mntr.flags & mask)) + if ((params->flags & mask) != + (sdata->u.mntr.flags & mask)) return -EBUSY; ieee80211_adjust_monitor_flags(sdata, -1); - sdata->u.mntr.flags = *flags; + sdata->u.mntr.flags = params->flags; ieee80211_adjust_monitor_flags(sdata, 1); ieee80211_configure_filter(local); @@ -163,7 +162,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy, * and ieee80211_do_open take care of "everything" * mentioned in the comment above. */ - sdata->u.mntr.flags = *flags; + sdata->u.mntr.flags = params->flags; } } diff --git a/net/wireless/core.h b/net/wireless/core.h index d614efb41726..5d27eca57d3b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -430,7 +430,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, void cfg80211_upload_connect_keys(struct wireless_dev *wdev); int cfg80211_change_iface(struct cfg80211_registered_device *rdev, struct net_device *dev, enum nl80211_iftype ntype, - u32 *flags, struct vif_params *params); + struct vif_params *params); void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); void cfg80211_process_wdev_events(struct wireless_dev *wdev); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5cdb0f9b0168..a07a55eda55b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2726,6 +2726,8 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) if (flags[flag]) *mntrflags |= (1<user_ptr[1]; - u32 _flags, *flags = NULL; bool change = false; memset(¶ms, 0, sizeof(params)); @@ -2809,14 +2810,17 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) if (ntype != NL80211_IFTYPE_MONITOR) return -EINVAL; err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], - &_flags); + ¶ms.flags); if (err) return err; - flags = &_flags; change = true; } + if (params.flags & MONITOR_FLAG_ACTIVE && + !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) + return -EOPNOTSUPP; + if (info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]) { const u8 *mumimo_groups; u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; @@ -2847,12 +2851,8 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) change = true; } - if (flags && (*flags & MONITOR_FLAG_ACTIVE) && - !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) - return -EOPNOTSUPP; - if (change) - err = cfg80211_change_iface(rdev, dev, ntype, flags, ¶ms); + err = cfg80211_change_iface(rdev, dev, ntype, ¶ms); else err = 0; @@ -2870,7 +2870,6 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) struct sk_buff *msg; int err; enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; - u32 flags; /* to avoid failing a new interface creation due to pending removal */ cfg80211_destroy_ifaces(rdev); @@ -2906,11 +2905,17 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) return err; } - err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? - info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, - &flags); + if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { + if (type != NL80211_IFTYPE_MONITOR) + return -EINVAL; + + err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], + ¶ms.flags); + if (err) + return err; + } - if (!err && (flags & MONITOR_FLAG_ACTIVE) && + if (params.flags & MONITOR_FLAG_ACTIVE && !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) return -EOPNOTSUPP; @@ -2920,8 +2925,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) wdev = rdev_add_virtual_intf(rdev, nla_data(info->attrs[NL80211_ATTR_IFNAME]), - NET_NAME_USER, type, err ? NULL : &flags, - ¶ms); + NET_NAME_USER, type, ¶ms); if (WARN_ON(!wdev)) { nlmsg_free(msg); return -EPROTO; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index f2baf5921091..e4a99989dd06 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -36,13 +36,13 @@ static inline void rdev_set_wakeup(struct cfg80211_registered_device *rdev, static inline struct wireless_dev *rdev_add_virtual_intf(struct cfg80211_registered_device *rdev, char *name, unsigned char name_assign_type, - enum nl80211_iftype type, u32 *flags, + enum nl80211_iftype type, struct vif_params *params) { struct wireless_dev *ret; trace_rdev_add_virtual_intf(&rdev->wiphy, name, type); ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, name_assign_type, - type, flags, params); + type, params); trace_rdev_return_wdev(&rdev->wiphy, ret); return ret; } @@ -61,12 +61,11 @@ rdev_del_virtual_intf(struct cfg80211_registered_device *rdev, static inline int rdev_change_virtual_intf(struct cfg80211_registered_device *rdev, struct net_device *dev, enum nl80211_iftype type, - u32 *flags, struct vif_params *params) + struct vif_params *params) { int ret; trace_rdev_change_virtual_intf(&rdev->wiphy, dev, type); - ret = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, type, flags, - params); + ret = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, type, params); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } diff --git a/net/wireless/util.c b/net/wireless/util.c index 8d6a0a7b1ca1..88f3a11dbcd3 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -985,7 +985,7 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev) int cfg80211_change_iface(struct cfg80211_registered_device *rdev, struct net_device *dev, enum nl80211_iftype ntype, - u32 *flags, struct vif_params *params) + struct vif_params *params) { int err; enum nl80211_iftype otype = dev->ieee80211_ptr->iftype; @@ -1043,7 +1043,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, cfg80211_process_rdev_events(rdev); } - err = rdev_change_virtual_intf(rdev, dev, ntype, flags, params); + err = rdev_change_virtual_intf(rdev, dev, ntype, params); WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index a220156cf217..5d4a02c7979b 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -62,7 +62,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, memset(&vifparams, 0, sizeof(vifparams)); - return cfg80211_change_iface(rdev, dev, type, NULL, &vifparams); + return cfg80211_change_iface(rdev, dev, type, &vifparams); } EXPORT_WEXT_HANDLER(cfg80211_wext_siwmode); -- cgit From 1db77596e4c6b653f3cd001073a14dd09b8deb4a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 11:36:31 +0200 Subject: cfg80211: refactor nl80211 monitor option parsing Refactor the parsing of monitor flags and the MU-MIMO options. This will allow adding more things cleanly in the future and also allows setting the latter already when creating a monitor interface. Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 126 +++++++++++++++++++++++++++---------------------- 1 file changed, 70 insertions(+), 56 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a07a55eda55b..671b635c0625 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2731,6 +2731,69 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) return 0; } +static int nl80211_parse_mon_options(struct cfg80211_registered_device *rdev, + enum nl80211_iftype type, + struct genl_info *info, + struct vif_params *params) +{ + bool change = false; + int err; + + if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { + if (type != NL80211_IFTYPE_MONITOR) + return -EINVAL; + + err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], + ¶ms->flags); + if (err) + return err; + + change = true; + } + + if (params->flags & MONITOR_FLAG_ACTIVE && + !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) + return -EOPNOTSUPP; + + if (info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]) { + const u8 *mumimo_groups; + u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; + + if (type != NL80211_IFTYPE_MONITOR) + return -EINVAL; + + if (!wiphy_ext_feature_isset(&rdev->wiphy, cap_flag)) + return -EOPNOTSUPP; + + mumimo_groups = + nla_data(info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]); + + /* bits 0 and 63 are reserved and must be zero */ + if ((mumimo_groups[0] & BIT(7)) || + (mumimo_groups[VHT_MUMIMO_GROUPS_DATA_LEN - 1] & BIT(0))) + return -EINVAL; + + params->vht_mumimo_groups = mumimo_groups; + change = true; + } + + if (info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]) { + u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; + + if (type != NL80211_IFTYPE_MONITOR) + return -EINVAL; + + if (!wiphy_ext_feature_isset(&rdev->wiphy, cap_flag)) + return -EOPNOTSUPP; + + params->vht_mumimo_follow_addr = + nla_data(info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]); + change = true; + } + + return change ? 1 : 0; +} + static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev, struct net_device *netdev, u8 use_4addr, enum nl80211_iftype iftype) @@ -2806,50 +2869,11 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) params.use_4addr = -1; } - if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { - if (ntype != NL80211_IFTYPE_MONITOR) - return -EINVAL; - err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], - ¶ms.flags); - if (err) - return err; - - change = true; - } - - if (params.flags & MONITOR_FLAG_ACTIVE && - !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) - return -EOPNOTSUPP; - - if (info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]) { - const u8 *mumimo_groups; - u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; - - if (!wiphy_ext_feature_isset(&rdev->wiphy, cap_flag)) - return -EOPNOTSUPP; - - mumimo_groups = - nla_data(info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]); - - /* bits 0 and 63 are reserved and must be zero */ - if ((mumimo_groups[0] & BIT(7)) || - (mumimo_groups[VHT_MUMIMO_GROUPS_DATA_LEN - 1] & BIT(0))) - return -EINVAL; - - params.vht_mumimo_groups = mumimo_groups; - change = true; - } - - if (info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]) { - u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; - - if (!wiphy_ext_feature_isset(&rdev->wiphy, cap_flag)) - return -EOPNOTSUPP; - - params.vht_mumimo_follow_addr = - nla_data(info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]); + err = nl80211_parse_mon_options(rdev, ntype, info, ¶ms); + if (err < 0) + return err; + if (err > 0) change = true; - } if (change) err = cfg80211_change_iface(rdev, dev, ntype, ¶ms); @@ -2905,19 +2929,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) return err; } - if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { - if (type != NL80211_IFTYPE_MONITOR) - return -EINVAL; - - err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], - ¶ms.flags); - if (err) - return err; - } - - if (params.flags & MONITOR_FLAG_ACTIVE && - !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) - return -EOPNOTSUPP; + err = nl80211_parse_mon_options(rdev, type, info, ¶ms); + if (err < 0) + return err; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) -- cgit From 65f1d6007e999f3a3dda1ba5f264447529247697 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 12:36:31 +0200 Subject: mac80211: use common code for monitor options in add/change Refactor the code to have common code for changing monitor options when adding and changing virtual interfaces. This will make it easier to add BPF filters to both paths. Note that this code carefully checks the error conditions first and only then applies the changes, to guarantee atomicity. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 122 ++++++++++++++++++++++++++++------------------------- 1 file changed, 64 insertions(+), 58 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 5c16d23e28dd..d041f78ecee6 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -22,31 +22,23 @@ #include "mesh.h" #include "wme.h" -static int ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata, - struct vif_params *params) +static void ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata, + struct vif_params *params) { - struct ieee80211_local *local = sdata->local; - struct ieee80211_sub_if_data *monitor_sdata; bool mu_mimo_groups = false; bool mu_mimo_follow = false; - monitor_sdata = rtnl_dereference(local->monitor_sdata); - - if (!monitor_sdata) - return -EOPNOTSUPP; - if (params->vht_mumimo_groups) { u64 membership; BUILD_BUG_ON(sizeof(membership) != WLAN_MEMBERSHIP_LEN); - memcpy(monitor_sdata->vif.bss_conf.mu_group.membership, + memcpy(sdata->vif.bss_conf.mu_group.membership, params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN); - memcpy(monitor_sdata->vif.bss_conf.mu_group.position, + memcpy(sdata->vif.bss_conf.mu_group.position, params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN, WLAN_USER_POSITION_LEN); - ieee80211_bss_info_change_notify(monitor_sdata, - BSS_CHANGED_MU_GROUPS); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_MU_GROUPS); /* don't care about endianness - just check for 0 */ memcpy(&membership, params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN); @@ -56,11 +48,64 @@ static int ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata, if (params->vht_mumimo_follow_addr) { mu_mimo_follow = is_valid_ether_addr(params->vht_mumimo_follow_addr); - ether_addr_copy(monitor_sdata->u.mntr.mu_follow_addr, + ether_addr_copy(sdata->u.mntr.mu_follow_addr, params->vht_mumimo_follow_addr); } - monitor_sdata->vif.mu_mimo_owner = mu_mimo_groups || mu_mimo_follow; + sdata->vif.mu_mimo_owner = mu_mimo_groups || mu_mimo_follow; +} + +static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata, + struct vif_params *params) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *monitor_sdata; + + /* check flags first */ + if (params->flags && ieee80211_sdata_running(sdata)) { + u32 mask = MONITOR_FLAG_COOK_FRAMES | MONITOR_FLAG_ACTIVE; + + /* + * Prohibit MONITOR_FLAG_COOK_FRAMES and + * MONITOR_FLAG_ACTIVE to be changed while the + * interface is up. + * Else we would need to add a lot of cruft + * to update everything: + * cooked_mntrs, monitor and all fif_* counters + * reconfigure hardware + */ + if ((params->flags & mask) != (sdata->u.mntr.flags & mask)) + return -EBUSY; + } + + /* also validate MU-MIMO change */ + monitor_sdata = rtnl_dereference(local->monitor_sdata); + + if (!monitor_sdata && + (params->vht_mumimo_groups || params->vht_mumimo_follow_addr)) + return -EOPNOTSUPP; + + /* apply all changes now - no failures allowed */ + + if (monitor_sdata) + ieee80211_set_mu_mimo_follow(monitor_sdata, params); + + if (params->flags) { + if (ieee80211_sdata_running(sdata)) { + ieee80211_adjust_monitor_flags(sdata, -1); + sdata->u.mntr.flags = params->flags; + ieee80211_adjust_monitor_flags(sdata, 1); + + ieee80211_configure_filter(local); + } else { + /* + * Because the interface is down, ieee80211_do_stop + * and ieee80211_do_open take care of "everything" + * mentioned in the comment above. + */ + sdata->u.mntr.flags = params->flags; + } + } return 0; } @@ -83,13 +128,11 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); if (type == NL80211_IFTYPE_MONITOR) { - err = ieee80211_set_mu_mimo_follow(sdata, params); + err = ieee80211_set_mon_options(sdata, params); if (err) { ieee80211_if_remove(sdata); return NULL; } - - sdata->u.mntr.flags = params->flags; } return wdev; @@ -124,46 +167,9 @@ static int ieee80211_change_iface(struct wiphy *wiphy, } if (sdata->vif.type == NL80211_IFTYPE_MONITOR) { - struct ieee80211_local *local = sdata->local; - int err; - - err = ieee80211_set_mu_mimo_follow(sdata, params); - if (err) - return err; - - if (!params->flags) - return 0; - - if (ieee80211_sdata_running(sdata)) { - u32 mask = MONITOR_FLAG_COOK_FRAMES | - MONITOR_FLAG_ACTIVE; - - /* - * Prohibit MONITOR_FLAG_COOK_FRAMES and - * MONITOR_FLAG_ACTIVE to be changed while the - * interface is up. - * Else we would need to add a lot of cruft - * to update everything: - * cooked_mntrs, monitor and all fif_* counters - * reconfigure hardware - */ - if ((params->flags & mask) != - (sdata->u.mntr.flags & mask)) - return -EBUSY; - - ieee80211_adjust_monitor_flags(sdata, -1); - sdata->u.mntr.flags = params->flags; - ieee80211_adjust_monitor_flags(sdata, 1); - - ieee80211_configure_filter(local); - } else { - /* - * Because the interface is down, ieee80211_do_stop - * and ieee80211_do_open take care of "everything" - * mentioned in the comment above. - */ - sdata->u.mntr.flags = params->flags; - } + ret = ieee80211_set_mon_options(sdata, params); + if (ret) + return ret; } return 0; -- cgit From 1d5e9f80ab021e3e1f9436627a4ad07a143ccb2c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Apr 2017 10:31:16 +0200 Subject: mac80211_hwsim: use per-interface power level When channel contexts are used, there's no global power level (the power_level is always 0). Use the per-interface TX power in mac80211_hwsim to have a proper setting for both cases. This fixes the bgscan_simple and bgscan_learn test cases when the number of channels advertised by hwsim is >1 by default. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 4e58513d24e8..7ac45af44144 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -558,8 +558,6 @@ struct mac80211_hwsim_data { /* wmediumd portid responsible for netgroup of this radio */ u32 wmediumd; - int power_level; - /* difference between this hw's clock and the real clock, in usecs */ s64 tsf_offset; s64 bcn_delta; @@ -1207,7 +1205,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, if (info->control.rates[0].flags & IEEE80211_TX_RC_SHORT_GI) rx_status.flag |= RX_FLAG_SHORT_GI; /* TODO: simulate real signal strength (and optional packet loss) */ - rx_status.signal = data->power_level - 50; + rx_status.signal = -50; + if (info->control.vif) + rx_status.signal += info->control.vif->bss_conf.txpower; if (data->ps != PS_DISABLED) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); @@ -1633,7 +1633,6 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) } mutex_unlock(&data->mutex); - data->power_level = conf->power_level; if (!data->started || !data->beacon_int) tasklet_hrtimer_cancel(&data->beacon_timer); else if (!hrtimer_is_queued(&data->beacon_timer.timer)) { @@ -2253,7 +2252,6 @@ static const char mac80211_hwsim_gstrings_stats[][ETH_GSTRING_LEN] = { "d_tx_failed", "d_ps_mode", "d_group", - "d_tx_power", }; #define MAC80211_HWSIM_SSTATS_LEN ARRAY_SIZE(mac80211_hwsim_gstrings_stats) @@ -2290,7 +2288,6 @@ static void mac80211_hwsim_get_et_stats(struct ieee80211_hw *hw, data[i++] = ar->tx_failed; data[i++] = ar->ps; data[i++] = ar->group; - data[i++] = ar->power_level; WARN_ON(i != MAC80211_HWSIM_SSTATS_LEN); } -- cgit From 169345d40d0fa05c4fdcec67835b008d24cfcf26 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 30 Mar 2017 15:57:23 -0700 Subject: ath6kl: add __printf verification to ath6kl_dbg Fix fallout too. Signed-off-by: Joe Perches Reviewed-by: Steve deRosier Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/debug.h | 2 ++ drivers/net/wireless/ath/ath6kl/htc_pipe.c | 2 +- drivers/net/wireless/ath/ath6kl/wmi.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 0614393dd7ae..94297572914f 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -63,6 +63,7 @@ int ath6kl_read_tgt_stats(struct ath6kl *ar, struct ath6kl_vif *vif); #ifdef CONFIG_ATH6KL_DEBUG +__printf(2, 3) void ath6kl_dbg(enum ATH6K_DEBUG_MASK mask, const char *fmt, ...); void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask, const char *msg, const char *prefix, @@ -83,6 +84,7 @@ int ath6kl_debug_init_fs(struct ath6kl *ar); void ath6kl_debug_cleanup(struct ath6kl *ar); #else +__printf(2, 3) static inline void ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask, const char *fmt, ...) { diff --git a/drivers/net/wireless/ath/ath6kl/htc_pipe.c b/drivers/net/wireless/ath/ath6kl/htc_pipe.c index ca1a18c86c0d..d127a08d60df 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_pipe.c +++ b/drivers/net/wireless/ath/ath6kl/htc_pipe.c @@ -995,7 +995,7 @@ static int ath6kl_htc_pipe_rx_complete(struct ath6kl *ar, struct sk_buff *skb, if (netlen < (payload_len + HTC_HDR_LENGTH)) { ath6kl_dbg(ATH6KL_DBG_HTC, - "HTC Rx: insufficient length, got:%d expected =%u\n", + "HTC Rx: insufficient length, got:%d expected =%zu\n", netlen, payload_len + HTC_HDR_LENGTH); status = -EINVAL; goto free_skb; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 84a6d12c3f8a..a082de81ec4c 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1596,7 +1596,7 @@ static int ath6kl_wmi_txe_notify_event_rx(struct wmi *wmi, u8 *datap, int len, rate = le32_to_cpu(ev->rate); pkts = le32_to_cpu(ev->pkts); - ath6kl_dbg(ATH6KL_DBG_WMI, "TXE notify event: peer %pM rate %d% pkts %d intvl %ds\n", + ath6kl_dbg(ATH6KL_DBG_WMI, "TXE notify event: peer %pM rate %d%% pkts %d intvl %ds\n", vif->bssid, rate, pkts, vif->txe_intvl); cfg80211_cqm_txe_notify(vif->ndev, vif->bssid, pkts, -- cgit From 62ca0690cd495bb7c1414cdf0cf790c2922a1d79 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Tue, 4 Apr 2017 22:22:56 +0530 Subject: ath10k: fix compile time sanity check for CE4 buffer size In 'ath10k_ce_alloc_pipe' the compile time sanity check to ensure that there is sufficient buffers in CE4 for HTT Tx MSDU descriptors, but this did not take into account of the case with 'peer flow control' enabled, fix this. Cc: Michal Kazior Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ce.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 9ac0a73a3a9f..ee1090ca2eac 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -1051,7 +1051,7 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, */ BUILD_BUG_ON(2 * TARGET_NUM_MSDU_DESC > (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); - BUILD_BUG_ON(2 * TARGET_10X_NUM_MSDU_DESC > + BUILD_BUG_ON(2 * TARGET_10_4_NUM_MSDU_DESC_PFC > (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); BUILD_BUG_ON(2 * TARGET_TLV_NUM_MSDU_DESC > (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); -- cgit From 82e9f646555442d63e47155171592ee790275ea3 Mon Sep 17 00:00:00 2001 From: Hamad Kadmany Date: Wed, 5 Apr 2017 14:58:04 +0300 Subject: wil6210: fix sequence for scan-abort during reset Communication with FW must be done before wil->status is initialized in order to properly handle cases where communication with FW halts during reset sequence. Signed-off-by: Hamad Kadmany Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index c33cc4ad44c4..7e72096d738a 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -948,15 +948,15 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) /* Disable device led before reset*/ wmi_led_cfg(wil, false); + mutex_lock(&wil->p2p_wdev_mutex); + wil_abort_scan(wil, false); + mutex_unlock(&wil->p2p_wdev_mutex); + /* prevent NAPI from being scheduled and prevent wmi commands */ mutex_lock(&wil->wmi_mutex); bitmap_zero(wil->status, wil_status_last); mutex_unlock(&wil->wmi_mutex); - mutex_lock(&wil->p2p_wdev_mutex); - wil_abort_scan(wil, false); - mutex_unlock(&wil->p2p_wdev_mutex); - wil_mask_irq(wil); wmi_event_flush(wil); -- cgit From 8b068c032a5cbe6f80168e0ecf2c9625c8d14d90 Mon Sep 17 00:00:00 2001 From: Lazar Alexei Date: Wed, 5 Apr 2017 14:58:05 +0300 Subject: wil6210: restore power save state after internal FW reset The power save profile is set to default state in case of FW reset, regardless of the state before the reset took place. Fix this by saving the current power save profile and restore it in case of FW reset. Signed-off-by: Lazar Alexei Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/cfg80211.c | 12 +----------- drivers/net/wireless/ath/wil6210/main.c | 24 ++++++++++++++++++++++++ drivers/net/wireless/ath/wil6210/wil6210.h | 4 ++++ 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 1981ec2e0186..474ab8070c00 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -1563,12 +1563,6 @@ static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy, { struct wil6210_priv *wil = wiphy_to_wil(wiphy); enum wmi_ps_profile_type ps_profile; - int rc; - - if (!test_bit(WMI_FW_CAPABILITY_PS_CONFIG, wil->fw_capabilities)) { - wil_err(wil, "set_power_mgmt not supported\n"); - return -EOPNOTSUPP; - } wil_dbg_misc(wil, "enabled=%d, timeout=%d\n", enabled, timeout); @@ -1578,11 +1572,7 @@ static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy, else ps_profile = WMI_PS_PROFILE_TYPE_PS_DISABLED; - rc = wmi_ps_dev_profile_cfg(wil, ps_profile); - if (rc) - wil_err(wil, "wmi_ps_dev_profile_cfg failed (%d)\n", rc); - - return rc; + return wil_ps_update(wil, ps_profile); } static const struct cfg80211_ops wil_cfg80211_ops = { diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 7e72096d738a..9aa81ce895b3 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -576,6 +576,9 @@ int wil_priv_init(struct wil6210_priv *wil) if (rx_ring_overflow_thrsh == WIL6210_RX_HIGH_TRSH_INIT) rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_DEFAULT; + + wil->ps_profile = WMI_PS_PROFILE_TYPE_DEFAULT; + return 0; out_wmi_wq: @@ -903,6 +906,24 @@ void wil_abort_scan(struct wil6210_priv *wil, bool sync) } } +int wil_ps_update(struct wil6210_priv *wil, enum wmi_ps_profile_type ps_profile) +{ + int rc; + + if (!test_bit(WMI_FW_CAPABILITY_PS_CONFIG, wil->fw_capabilities)) { + wil_err(wil, "set_power_mgmt not supported\n"); + return -EOPNOTSUPP; + } + + rc = wmi_ps_dev_profile_cfg(wil, ps_profile); + if (rc) + wil_err(wil, "wmi_ps_dev_profile_cfg failed (%d)\n", rc); + else + wil->ps_profile = ps_profile; + + return rc; +} + /* * We reset all the structures, and we reset the UMAC. * After calling this routine, you're expected to reload @@ -1033,6 +1054,9 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) return rc; } + if (wil->ps_profile != WMI_PS_PROFILE_TYPE_DEFAULT) + wil_ps_update(wil, wil->ps_profile); + wil_collect_fw_info(wil); if (wil->platform_ops.notify) { diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index fee18916b713..9cedc2d642ba 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -691,6 +691,8 @@ struct wil6210_priv { /* High Access Latency Policy voting */ struct wil_halp halp; + enum wmi_ps_profile_type ps_profile; + #ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP struct notifier_block pm_notify; @@ -812,6 +814,8 @@ int wil_if_add(struct wil6210_priv *wil); void wil_if_remove(struct wil6210_priv *wil); int wil_priv_init(struct wil6210_priv *wil); void wil_priv_deinit(struct wil6210_priv *wil); +int wil_ps_update(struct wil6210_priv *wil, + enum wmi_ps_profile_type ps_profile); int wil_reset(struct wil6210_priv *wil, bool no_fw); void wil_fw_error_recovery(struct wil6210_priv *wil); void wil_set_recovery_state(struct wil6210_priv *wil, int state); -- cgit From 52a457020a89158f4eb886544165dc162ca9a35e Mon Sep 17 00:00:00 2001 From: Lior David Date: Wed, 5 Apr 2017 14:58:06 +0300 Subject: wil6210: support 8KB RX buffers The 11ad spec requires 11ad devices to be able to receive 8KB packets over the air. Currently this is only possible by loading the driver with mtu_max=7912 but this also forces a smaller block ACK window size which reduces performance for stations which transmit normal sized packets (<2KB). Fix this problem as follows: 1. Add a module parameter rx_large_buf that when set, will allocate 8KB RX buffers regardless of mtu_max setting. 2. When receiving block ACK request agree to any window size not above our maximum, regardless of the mtu_max setting. This means if the other side transmits small packets (2KB) it can still set up block ACK with a large window size, and get better performance. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/rx_reorder.c | 12 ++++++++++-- drivers/net/wireless/ath/wil6210/txrx.c | 24 ++++++++++++++++++++++-- drivers/net/wireless/ath/wil6210/wil6210.h | 2 ++ drivers/net/wireless/ath/wil6210/wmi.c | 3 ++- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index 7404b6f39c6a..a43cffcf1bbf 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -343,8 +343,16 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) wil_err(wil, "BACK requested unsupported ba_policy == 1\n"); status = WLAN_STATUS_INVALID_QOS_PARAM; } - if (status == WLAN_STATUS_SUCCESS) - agg_wsize = wil_agg_size(wil, req_agg_wsize); + if (status == WLAN_STATUS_SUCCESS) { + if (req_agg_wsize == 0) { + wil_dbg_misc(wil, "Suggest BACK wsize %d\n", + WIL_MAX_AGG_WSIZE); + agg_wsize = WIL_MAX_AGG_WSIZE; + } else { + agg_wsize = min_t(u16, + WIL_MAX_AGG_WSIZE, req_agg_wsize); + } + } rc = wmi_addba_rx_resp(wil, cid, tid, dialog_token, status, agg_amsdu, agg_wsize, agg_timeout); diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 67f50ae17cd3..edab4c0a900f 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -37,6 +37,10 @@ bool rx_align_2; module_param(rx_align_2, bool, 0444); MODULE_PARM_DESC(rx_align_2, " align Rx buffers on 4*n+2, default - no"); +bool rx_large_buf; +module_param(rx_large_buf, bool, 0444); +MODULE_PARM_DESC(rx_large_buf, " allocate 8KB RX buffers, default - no"); + static inline uint wil_rx_snaplen(void) { return rx_align_2 ? 6 : 0; @@ -255,7 +259,7 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, u32 i, int headroom) { struct device *dev = wil_to_dev(wil); - unsigned int sz = mtu_max + ETH_HLEN + wil_rx_snaplen(); + unsigned int sz = wil->rx_buf_len + ETH_HLEN + wil_rx_snaplen(); struct vring_rx_desc dd, *d = ⅆ volatile struct vring_rx_desc *_d = &vring->va[i].rx; dma_addr_t pa; @@ -419,7 +423,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, struct sk_buff *skb; dma_addr_t pa; unsigned int snaplen = wil_rx_snaplen(); - unsigned int sz = mtu_max + ETH_HLEN + snaplen; + unsigned int sz = wil->rx_buf_len + ETH_HLEN + snaplen; u16 dmalen; u8 ftype; int cid; @@ -780,6 +784,20 @@ void wil_rx_handle(struct wil6210_priv *wil, int *quota) wil_rx_refill(wil, v->size); } +static void wil_rx_buf_len_init(struct wil6210_priv *wil) +{ + wil->rx_buf_len = rx_large_buf ? + WIL_MAX_ETH_MTU : TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD; + if (mtu_max > wil->rx_buf_len) { + /* do not allow RX buffers to be smaller than mtu_max, for + * backward compatibility (mtu_max parameter was also used + * to support receiving large packets) + */ + wil_info(wil, "Override RX buffer to mtu_max(%d)\n", mtu_max); + wil->rx_buf_len = mtu_max; + } +} + int wil_rx_init(struct wil6210_priv *wil, u16 size) { struct vring *vring = &wil->vring_rx; @@ -792,6 +810,8 @@ int wil_rx_init(struct wil6210_priv *wil, u16 size) return -EINVAL; } + wil_rx_buf_len_init(wil); + vring->size = size; rc = wil_vring_alloc(wil, vring); if (rc) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 9cedc2d642ba..2af4a643bd03 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -32,6 +32,7 @@ extern unsigned short rx_ring_overflow_thrsh; extern int agg_wsize; extern u32 vring_idle_trsh; extern bool rx_align_2; +extern bool rx_large_buf; extern bool debug_fw; extern bool disable_ap_sme; @@ -656,6 +657,7 @@ struct wil6210_priv { struct work_struct probe_client_worker; /* DMA related */ struct vring vring_rx; + unsigned int rx_buf_len; struct vring vring_tx[WIL6210_MAX_TX_RINGS]; struct vring_tx_data vring_tx_data[WIL6210_MAX_TX_RINGS]; u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */ diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 9255c47af15a..e6c249d47487 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -1398,7 +1398,8 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) struct wmi_cfg_rx_chain_cmd cmd = { .action = WMI_RX_CHAIN_ADD, .rx_sw_ring = { - .max_mpdu_size = cpu_to_le16(wil_mtu2macbuf(mtu_max)), + .max_mpdu_size = cpu_to_le16( + wil_mtu2macbuf(wil->rx_buf_len)), .ring_mem_base = cpu_to_le64(vring->pa), .ring_size = cpu_to_le16(vring->size), }, -- cgit From 90ffabb08b7d0627239c7dd53c3c7065a158508c Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 5 Apr 2017 14:58:07 +0300 Subject: wil6210: align to latest auto generated wmi.h Align to latest version of the auto generated wmi file describing the interface with FW Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wmi.h | 73 ++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 7c9fee57aa91..f7f5f4f801e3 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -58,6 +58,7 @@ enum wmi_fw_capability { WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT = 3, WMI_FW_CAPABILITY_DISABLE_AP_SME = 4, WMI_FW_CAPABILITY_WMI_ONLY = 5, + WMI_FW_CAPABILITY_THERMAL_THROTTLING = 7, WMI_FW_CAPABILITY_MAX, }; @@ -142,8 +143,6 @@ enum wmi_command_id { WMI_MAINTAIN_RESUME_CMDID = 0x851, WMI_RS_MGMT_CMDID = 0x852, WMI_RF_MGMT_CMDID = 0x853, - WMI_THERMAL_THROTTLING_CTRL_CMDID = 0x854, - WMI_THERMAL_THROTTLING_GET_STATUS_CMDID = 0x855, WMI_OTP_READ_CMDID = 0x856, WMI_OTP_WRITE_CMDID = 0x857, WMI_LED_CFG_CMDID = 0x858, @@ -192,6 +191,8 @@ enum wmi_command_id { WMI_GET_MGMT_RETRY_LIMIT_CMDID = 0x931, WMI_NEW_STA_CMDID = 0x935, WMI_DEL_STA_CMDID = 0x936, + WMI_SET_THERMAL_THROTTLING_CFG_CMDID = 0x940, + WMI_GET_THERMAL_THROTTLING_CFG_CMDID = 0x941, WMI_TOF_SESSION_START_CMDID = 0x991, WMI_TOF_GET_CAPABILITIES_CMDID = 0x992, WMI_TOF_SET_LCR_CMDID = 0x993, @@ -438,16 +439,6 @@ struct wmi_rf_mgmt_cmd { __le32 rf_mgmt_type; } __packed; -/* WMI_THERMAL_THROTTLING_CTRL_CMDID */ -#define THERMAL_THROTTLING_USE_DEFAULT_MAX_TXOP_LENGTH (0xFFFFFFFF) - -/* WMI_THERMAL_THROTTLING_CTRL_CMDID */ -struct wmi_thermal_throttling_ctrl_cmd { - __le32 time_on_usec; - __le32 time_off_usec; - __le32 max_txop_length_usec; -} __packed; - /* WMI_RF_RX_TEST_CMDID */ struct wmi_rf_rx_test_cmd { __le32 sector; @@ -549,7 +540,7 @@ struct wmi_pcp_start_cmd { u8 hidden_ssid; u8 is_go; u8 reserved0[5]; - /* abft_len override if non-0 */ + /* A-BFT length override if non-0 */ u8 abft_len; u8 disable_ap_sme; u8 network_type; @@ -910,6 +901,39 @@ struct wmi_set_mgmt_retry_limit_cmd { u8 reserved[3]; } __packed; +/* Zones: HIGH, MAX, CRITICAL */ +#define WMI_NUM_OF_TT_ZONES (3) + +struct wmi_tt_zone_limits { + /* Above this temperature this zone is active */ + u8 temperature_high; + /* Below this temperature the adjacent lower zone is active */ + u8 temperature_low; + u8 reserved[2]; +} __packed; + +/* Struct used for both configuration and status commands of thermal + * throttling + */ +struct wmi_tt_data { + /* Enable/Disable TT algorithm for baseband */ + u8 bb_enabled; + u8 reserved0[3]; + /* Define zones for baseband */ + struct wmi_tt_zone_limits bb_zones[WMI_NUM_OF_TT_ZONES]; + /* Enable/Disable TT algorithm for radio */ + u8 rf_enabled; + u8 reserved1[3]; + /* Define zones for all radio chips */ + struct wmi_tt_zone_limits rf_zones[WMI_NUM_OF_TT_ZONES]; +} __packed; + +/* WMI_SET_THERMAL_THROTTLING_CFG_CMDID */ +struct wmi_set_thermal_throttling_cfg_cmd { + /* Command data */ + struct wmi_tt_data tt_data; +} __packed; + /* WMI_NEW_STA_CMDID */ struct wmi_new_sta_cmd { u8 dst_mac[WMI_MAC_LEN]; @@ -1040,7 +1064,6 @@ enum wmi_event_id { WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839, WMI_RS_MGMT_DONE_EVENTID = 0x1852, WMI_RF_MGMT_STATUS_EVENTID = 0x1853, - WMI_THERMAL_THROTTLING_STATUS_EVENTID = 0x1855, WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838, WMI_RX_MGMT_PACKET_EVENTID = 0x1840, WMI_TX_MGMT_PACKET_EVENTID = 0x1841, @@ -1090,6 +1113,8 @@ enum wmi_event_id { WMI_BRP_SET_ANT_LIMIT_EVENTID = 0x1924, WMI_SET_MGMT_RETRY_LIMIT_EVENTID = 0x1930, WMI_GET_MGMT_RETRY_LIMIT_EVENTID = 0x1931, + WMI_SET_THERMAL_THROTTLING_CFG_EVENTID = 0x1940, + WMI_GET_THERMAL_THROTTLING_CFG_EVENTID = 0x1941, WMI_TOF_SESSION_END_EVENTID = 0x1991, WMI_TOF_GET_CAPABILITIES_EVENTID = 0x1992, WMI_TOF_SET_LCR_EVENTID = 0x1993, @@ -1133,13 +1158,6 @@ struct wmi_rf_mgmt_status_event { __le32 rf_status; } __packed; -/* WMI_THERMAL_THROTTLING_STATUS_EVENTID */ -struct wmi_thermal_throttling_status_event { - __le32 time_on_usec; - __le32 time_off_usec; - __le32 max_txop_length_usec; -} __packed; - /* WMI_GET_STATUS_DONE_EVENTID */ struct wmi_get_status_done_event { __le32 is_associated; @@ -2206,6 +2224,19 @@ struct wmi_tof_get_capabilities_event { __le32 aoa_supported_types; } __packed; +/* WMI_SET_THERMAL_THROTTLING_CFG_EVENTID */ +struct wmi_set_thermal_throttling_cfg_event { + /* wmi_fw_status */ + u8 status; + u8 reserved[3]; +} __packed; + +/* WMI_GET_THERMAL_THROTTLING_CFG_EVENTID */ +struct wmi_get_thermal_throttling_cfg_event { + /* Status data */ + struct wmi_tt_data tt_data; +} __packed; + enum wmi_tof_session_end_status { WMI_TOF_SESSION_END_NO_ERROR = 0x00, WMI_TOF_SESSION_END_FAIL = 0x01, -- cgit From b819447dfc4bd120c9d6cd8521252d544fce8fe7 Mon Sep 17 00:00:00 2001 From: Hamad Kadmany Date: Wed, 5 Apr 2017 14:58:08 +0300 Subject: wil6210: fix protection against connections during reset Existing code that ignores connection events during reset flow will never take effect since it locks the same mutex taken by the reset flow. In addition, in case of unsolicited disconnect events ignore those as well since device is about to get reset. Signed-off-by: Hamad Kadmany Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/wmi.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index e6c249d47487..814c35645b73 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -518,16 +518,16 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) assoc_resp_ielen = 0; } - mutex_lock(&wil->mutex); if (test_bit(wil_status_resetting, wil->status) || !test_bit(wil_status_fwready, wil->status)) { wil_err(wil, "status_resetting, cancel connect event, CID %d\n", evt->cid); - mutex_unlock(&wil->mutex); /* no need for cleanup, wil_reset will do that */ return; } + mutex_lock(&wil->mutex); + if ((wdev->iftype == NL80211_IFTYPE_STATION) || (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { if (!test_bit(wil_status_fwconnecting, wil->status)) { @@ -631,6 +631,13 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, wil->sinfo_gen++; + if (test_bit(wil_status_resetting, wil->status) || + !test_bit(wil_status_fwready, wil->status)) { + wil_err(wil, "status_resetting, cancel disconnect event\n"); + /* no need for cleanup, wil_reset will do that */ + return; + } + mutex_lock(&wil->mutex); wil6210_disconnect(wil, evt->bssid, reason_code, true); mutex_unlock(&wil->mutex); -- cgit From a3839fbcf0351e8192429f1cc1c499799465737f Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Wed, 5 Apr 2017 14:58:09 +0300 Subject: wil6210: protect against sporadic interrupt during suspend flow During the suspend flow, wil6210 HW can send sporadic interrupts, while PCIe bus is not operational. To prevent that, keep the interrupts disabled during the suspend flow and re-enable them only after PCIe enablement in resume. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/pm.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index a0acb2d0cb79..7260bef314a4 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -80,12 +80,20 @@ int wil_suspend(struct wil6210_priv *wil, bool is_runtime) } } - if (wil->platform_ops.suspend) + /* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */ + wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n"); + wil_disable_irq(wil); + + if (wil->platform_ops.suspend) { rc = wil->platform_ops.suspend(wil->platform_handle); + if (rc) + wil_enable_irq(wil); + } out: wil_dbg_pm(wil, "suspend: %s => %d\n", is_runtime ? "runtime" : "system", rc); + return rc; } @@ -104,6 +112,9 @@ int wil_resume(struct wil6210_priv *wil, bool is_runtime) } } + wil_dbg_pm(wil, "Enabling PCIe IRQ\n"); + wil_enable_irq(wil); + /* if netif up, bring hardware up * During open(), IFF_UP set after actual device method * invocation. This prevent recursive call to wil_up() -- cgit From 4d4f8132f72b78d1260ec9afa94c3b44deb12adf Mon Sep 17 00:00:00 2001 From: Hamad Kadmany Date: Wed, 5 Apr 2017 14:58:10 +0300 Subject: wil6210: fix check for sparrow D0 FW file Driver fails to load FW for sparrow D0 devices in some cases. Fix this by returning correct value from wil_fw_verify_file_exists when D0 FW file is not detected for any reason. Signed-off-by: Hamad Kadmany Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/fw_inc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c index f4901587c005..e01acac88825 100644 --- a/drivers/net/wireless/ath/wil6210/fw_inc.c +++ b/drivers/net/wireless/ath/wil6210/fw_inc.c @@ -554,5 +554,7 @@ bool wil_fw_verify_file_exists(struct wil6210_priv *wil, const char *name) rc = request_firmware(&fw, name, wil_to_dev(wil)); if (!rc) release_firmware(fw); - return rc != -ENOENT; + else + wil_dbg_fw(wil, "<%s> not available: %d\n", name, rc); + return !rc; } -- cgit From 0f6edfe2bbbb59d161580cb4870fcc46f5490f85 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 5 Apr 2017 14:58:11 +0300 Subject: wil6210: fix memory access violation in wil_memcpy_from/toio_32 In case count is not multiple of 4, there is a read access in wil_memcpy_toio_32() from outside src buffer boundary. In wil_memcpy_fromio_32(), in case count is not multiple of 4, there is a write access to outside dst io memory boundary. Fix these issues with proper handling of the last 1 to 4 copied bytes. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 9aa81ce895b3..439d27c3aeae 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -130,9 +130,15 @@ void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, u32 *d = dst; const volatile u32 __iomem *s = src; - /* size_t is unsigned, if (count%4 != 0) it will wrap */ - for (count += 4; count > 4; count -= 4) + for (; count >= 4; count -= 4) *d++ = __raw_readl(s++); + + if (unlikely(count)) { + /* count can be 1..3 */ + u32 tmp = __raw_readl(s); + + memcpy(d, &tmp, count); + } } void wil_memcpy_fromio_halp_vote(struct wil6210_priv *wil, void *dst, @@ -149,8 +155,16 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, volatile u32 __iomem *d = dst; const u32 *s = src; - for (count += 4; count > 4; count -= 4) + for (; count >= 4; count -= 4) __raw_writel(*s++, d++); + + if (unlikely(count)) { + /* count can be 1..3 */ + u32 tmp = 0; + + memcpy(&tmp, s, count); + __raw_writel(tmp, d); + } } void wil_memcpy_toio_halp_vote(struct wil6210_priv *wil, -- cgit From bd50e2688a7812754e964231c850e8414751df1d Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Wed, 5 Apr 2017 14:58:12 +0300 Subject: wil6210: remove HALP voting in debugfs ioblob debugfs ioblob function is called by the FW logs scripts to copy the FW logs via PCIe. As the FW logs collection is done in parallel to the operational 11AD actions, the HALP voting can take place during 11AD reset flow and other sensitive scenarios. To prevent that, remove HALP voting from the ioblob function. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/debugfs.c | 5 ++--- drivers/net/wireless/ath/wil6210/main.c | 17 ----------------- drivers/net/wireless/ath/wil6210/wil6210.h | 6 ------ 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 3e8cdf12feda..5648ebbd0e16 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -524,9 +524,8 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, if (!buf) return -ENOMEM; - wil_memcpy_fromio_halp_vote(wil_blob->wil, buf, - (const volatile void __iomem *) - wil_blob->blob.data + pos, count); + wil_memcpy_fromio_32(buf, (const void __iomem *) + wil_blob->blob.data + pos, count); ret = copy_to_user(user_buf, buf, count); kfree(buf); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 439d27c3aeae..32086792dfc3 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -141,14 +141,6 @@ void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, } } -void wil_memcpy_fromio_halp_vote(struct wil6210_priv *wil, void *dst, - const volatile void __iomem *src, size_t count) -{ - wil_halp_vote(wil); - wil_memcpy_fromio_32(dst, src, count); - wil_halp_unvote(wil); -} - void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, size_t count) { @@ -167,15 +159,6 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, } } -void wil_memcpy_toio_halp_vote(struct wil6210_priv *wil, - volatile void __iomem *dst, - const void *src, size_t count) -{ - wil_halp_vote(wil); - wil_memcpy_toio_32(dst, src, count); - wil_halp_unvote(wil); -} - static void wil_disconnect_cid(struct wil6210_priv *wil, int cid, u16 reason_code, bool from_event) __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 2af4a643bd03..ec646d7df522 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -803,12 +803,6 @@ void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, size_t count); void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, size_t count); -void wil_memcpy_fromio_halp_vote(struct wil6210_priv *wil, void *dst, - const volatile void __iomem *src, - size_t count); -void wil_memcpy_toio_halp_vote(struct wil6210_priv *wil, - volatile void __iomem *dst, - const void *src, size_t count); void *wil_if_alloc(struct device *dev); void wil_if_free(struct wil6210_priv *wil); -- cgit From 98a830a98dab6d474190bec72c4a4ad0f7f9bf75 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 5 Apr 2017 14:58:13 +0300 Subject: wil6210: fix array out of bounds access in pmc Array index 'i' is used before limits check. Fix this by doing limits check first. Signed-off-by: Dedy Lansky Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/pmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/pmc.c b/drivers/net/wireless/ath/wil6210/pmc.c index b067fdf086d4..2e301b6b32a9 100644 --- a/drivers/net/wireless/ath/wil6210/pmc.c +++ b/drivers/net/wireless/ath/wil6210/pmc.c @@ -200,7 +200,7 @@ void wil_pmc_alloc(struct wil6210_priv *wil, release_pmc_skbs: wil_err(wil, "exit on error: Releasing skbs...\n"); - for (i = 0; pmc->descriptors[i].va && i < num_descriptors; i++) { + for (i = 0; i < num_descriptors && pmc->descriptors[i].va; i++) { dma_free_coherent(dev, descriptor_size, pmc->descriptors[i].va, @@ -283,7 +283,7 @@ void wil_pmc_free(struct wil6210_priv *wil, int send_pmc_cmd) int i; for (i = 0; - pmc->descriptors[i].va && i < pmc->num_descriptors; i++) { + i < pmc->num_descriptors && pmc->descriptors[i].va; i++) { dma_free_coherent(dev, pmc->descriptor_size, pmc->descriptors[i].va, -- cgit From 3161adddf309e4c5f362d77f92206dd340fea67d Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Wed, 5 Apr 2017 14:58:14 +0300 Subject: wil6210: prevent access to 11AD device if resume fails In case wil6210 resume fails, wil6210 suspend function will try to access the suspended device in the next kernel suspend. To prevent that, add wil_status_suspended flag to indicate if the device is already suspended and clear it only if the resume succeeds. Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/pm.c | 16 ++++++++++++++-- drivers/net/wireless/ath/wil6210/wil6210.h | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index 7260bef314a4..2ae4fe85cc8c 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -71,6 +71,11 @@ int wil_suspend(struct wil6210_priv *wil, bool is_runtime) wil_dbg_pm(wil, "suspend: %s\n", is_runtime ? "runtime" : "system"); + if (test_bit(wil_status_suspended, wil->status)) { + wil_dbg_pm(wil, "trying to suspend while suspended\n"); + return 0; + } + /* if netif up, hardware is alive, shut it down */ if (ndev->flags & IFF_UP) { rc = wil_down(wil); @@ -86,10 +91,14 @@ int wil_suspend(struct wil6210_priv *wil, bool is_runtime) if (wil->platform_ops.suspend) { rc = wil->platform_ops.suspend(wil->platform_handle); - if (rc) + if (rc) { wil_enable_irq(wil); + goto out; + } } + set_bit(wil_status_suspended, wil->status); + out: wil_dbg_pm(wil, "suspend: %s => %d\n", is_runtime ? "runtime" : "system", rc); @@ -117,10 +126,13 @@ int wil_resume(struct wil6210_priv *wil, bool is_runtime) /* if netif up, bring hardware up * During open(), IFF_UP set after actual device method - * invocation. This prevent recursive call to wil_up() + * invocation. This prevent recursive call to wil_up(). + * wil_status_suspended will be cleared in wil_reset */ if (ndev->flags & IFF_UP) rc = wil_up(wil); + else + clear_bit(wil_status_suspended, wil->status); out: wil_dbg_pm(wil, "resume: %s => %d\n", diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index ec646d7df522..b00c803a1e83 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -412,6 +412,7 @@ enum { /* for wil6210_priv.status */ wil_status_irqen, /* FIXME: interrupts enabled - for debug */ wil_status_napi_en, /* NAPI enabled protected by wil->mutex */ wil_status_resetting, /* reset in progress */ + wil_status_suspended, /* suspend completed, device is suspended */ wil_status_last /* keep last */ }; -- cgit From ecd7eb7c2bcf99f6c23d68ad56ce15949da848a1 Mon Sep 17 00:00:00 2001 From: Ganapathi Bhat Date: Tue, 4 Apr 2017 10:16:28 +0530 Subject: mwifiex: Fix invalid port issue We have to use start port, for TX/RX of single packet, instead of current aggregating port. This will fix SDIO CMD53(TX/RX) returning -ETIMEDOUT and halting the data path. Fixes: 0cb52aac4d19 ("mwifiex: do not set multiport flag for tx/rx single packet") Signed-off-by: Ganapathi Bhat Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 424532b81c2d..0af1c6733c92 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -1380,7 +1380,7 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, } if (card->mpa_rx.pkt_cnt == 1) - mport = adapter->ioport + port; + mport = adapter->ioport + card->mpa_rx.start_port; if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, card->mpa_rx.buf_len, mport, 1)) @@ -1813,7 +1813,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, } if (card->mpa_tx.pkt_cnt == 1) - mport = adapter->ioport + port; + mport = adapter->ioport + card->mpa_tx.start_port; ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, card->mpa_tx.buf_len, mport); -- cgit From 6e84ab604bdedaa16239bd1c6e5fcb5660309f02 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 5 Apr 2017 20:33:26 +0200 Subject: brcmfmac: properly align buffers on certain platforms with 64 bit DMA Systems with 64 bit DMA at least partially require buffers to be used for DMA to be 8-byte-aligned. One example is Amlogic Meson GX. Switching the MMC/SDIO driver for this platform to SG DMA mode resulted in problems due to unaligned buffers. Fortunately the brcmfmac driver has a global define for the alignment. Changing it to 8 fixed the issues with Meson GX. Suggested-by: Helmut Klein Tested-by: Helmut Klein Signed-off-by: Heiner Kallweit Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index a999f95062c7..fc64b8913aa6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -540,7 +540,11 @@ static int qcount[NUMPRIO]; /* Limit on rounding up frames */ static const uint max_roundup = 512; +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT +#define ALIGNMENT 8 +#else #define ALIGNMENT 4 +#endif enum brcmf_sdio_frmtype { BRCMF_SDIO_FT_NORMAL, -- cgit From ffb9f18ad8f0e5e1d88c56478bf24e6464c96eee Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Thu, 6 Apr 2017 13:14:37 +0100 Subject: brcmfmac: rename brcmf_fws_{de,}init to brcmf_fws{at,de}tach This is a non-functional change to align the fwsignal module to the naming pattern used throughout the driver. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c | 4 ++-- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c | 6 +++--- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index 24da6276d29d..9628084b7aa0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -420,7 +420,7 @@ brcmf_proto_bcdc_reset_if(struct brcmf_if *ifp) static int brcmf_proto_bcdc_init_done(struct brcmf_pub *drvr) { - return brcmf_fws_init(drvr); + return brcmf_fws_attach(drvr); } int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) @@ -464,7 +464,7 @@ fail: void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) { - brcmf_fws_deinit(drvr); + brcmf_fws_detach(drvr); kfree(drvr->proto->pd); drvr->proto->pd = NULL; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c index 5f1a5929cb30..c27a2254f416 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c @@ -2308,7 +2308,7 @@ static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) } #endif -int brcmf_fws_init(struct brcmf_pub *drvr) +int brcmf_fws_attach(struct brcmf_pub *drvr) { struct brcmf_fws_info *fws; struct brcmf_if *ifp; @@ -2408,11 +2408,11 @@ int brcmf_fws_init(struct brcmf_pub *drvr) return 0; fail: - brcmf_fws_deinit(drvr); + brcmf_fws_detach(drvr); return rc; } -void brcmf_fws_deinit(struct brcmf_pub *drvr) +void brcmf_fws_detach(struct brcmf_pub *drvr) { struct brcmf_fws_info *fws = drvr->fws; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h index 96df66073b2a..d72515867aa0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h @@ -18,8 +18,8 @@ #ifndef FWSIGNAL_H_ #define FWSIGNAL_H_ -int brcmf_fws_init(struct brcmf_pub *drvr); -void brcmf_fws_deinit(struct brcmf_pub *drvr); +int brcmf_fws_attach(struct brcmf_pub *drvr); +void brcmf_fws_detach(struct brcmf_pub *drvr); bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws); bool brcmf_fws_fc_active(struct brcmf_fws_info *fws); void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb); -- cgit From fc0471e3e884a13d293afae53917ef8ff33b8ae5 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Thu, 6 Apr 2017 13:14:38 +0100 Subject: brcmfmac: ignore interfaces when fwsignal is disabled When brcmf_fws_add_interface() is called the struct brcmf_if::fws_desc field is initialized regardless the state of the fwsignal functionality, ie. the fcmode. This is not needed when fcmode is NONE, which is the default mode. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c index c27a2254f416..23b2b3fa1c2c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c @@ -2145,7 +2145,7 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp) struct brcmf_fws_info *fws = ifp->drvr->fws; struct brcmf_fws_mac_descriptor *entry; - if (!ifp->ndev) + if (!ifp->ndev || fws->fcmode == BRCMF_FWS_FCMODE_NONE) return; entry = &fws->desc.iface[ifp->ifidx]; -- cgit From acf8ac41dd733508b9e77483f96e53610c87fa64 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Thu, 6 Apr 2017 13:14:39 +0100 Subject: brcmfmac: remove reference to fwsignal data from struct brcmf_pub The fwsignal module is part of the bcdc protocol and as such does its instance data is not needed in core structure. Moving it into struct brcmf_bcdc instead. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/bcdc.c | 35 +++++++++++---- .../wireless/broadcom/brcm80211/brcmfmac/bcdc.h | 1 + .../wireless/broadcom/brcm80211/brcmfmac/core.h | 2 - .../broadcom/brcm80211/brcmfmac/fwsignal.c | 51 ++++++++++------------ .../broadcom/brcm80211/brcmfmac/fwsignal.h | 4 +- 5 files changed, 54 insertions(+), 39 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index 9628084b7aa0..9f2d0b0cf6e5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -103,9 +103,17 @@ struct brcmf_bcdc { u8 bus_header[BUS_HEADER_LEN]; struct brcmf_proto_bcdc_dcmd msg; unsigned char buf[BRCMF_DCMD_MAXLEN]; + struct brcmf_fws_info *fws; }; +struct brcmf_fws_info *drvr_to_fws(struct brcmf_pub *drvr) +{ + struct brcmf_bcdc *bcdc = drvr->proto->pd; + + return bcdc->fws; +} + static int brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len, bool set) @@ -330,8 +338,9 @@ static int brcmf_proto_bcdc_tx_queue_data(struct brcmf_pub *drvr, int ifidx, struct sk_buff *skb) { struct brcmf_if *ifp = brcmf_get_ifp(drvr, ifidx); + struct brcmf_bcdc *bcdc = drvr->proto->pd; - if (!brcmf_fws_queue_skbs(drvr->fws)) + if (!brcmf_fws_queue_skbs(bcdc->fws)) return brcmf_proto_txdata(drvr, ifidx, 0, skb); return brcmf_fws_process_skb(ifp, skb); @@ -360,15 +369,15 @@ brcmf_proto_bcdc_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; + struct brcmf_bcdc *bcdc = bus_if->drvr->proto->pd; struct brcmf_if *ifp; /* await txstatus signal for firmware if active */ - if (brcmf_fws_fc_active(drvr->fws)) { + if (brcmf_fws_fc_active(bcdc->fws)) { if (!success) - brcmf_fws_bustxfail(drvr->fws, txp); + brcmf_fws_bustxfail(bcdc->fws, txp); } else { - if (brcmf_proto_bcdc_hdrpull(drvr, false, txp, &ifp)) + if (brcmf_proto_bcdc_hdrpull(bus_if->drvr, false, txp, &ifp)) brcmu_pkt_buf_free_skb(txp); else brcmf_txfinalize(ifp, txp, success); @@ -420,7 +429,15 @@ brcmf_proto_bcdc_reset_if(struct brcmf_if *ifp) static int brcmf_proto_bcdc_init_done(struct brcmf_pub *drvr) { - return brcmf_fws_attach(drvr); + struct brcmf_bcdc *bcdc = drvr->proto->pd; + struct brcmf_fws_info *fws; + + fws = brcmf_fws_attach(drvr); + if (IS_ERR(fws)) + return PTR_ERR(fws); + + bcdc->fws = fws; + return 0; } int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) @@ -464,7 +481,9 @@ fail: void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) { - brcmf_fws_detach(drvr); - kfree(drvr->proto->pd); + struct brcmf_bcdc *bcdc = drvr->proto->pd; + drvr->proto->pd = NULL; + brcmf_fws_detach(bcdc->fws); + kfree(bcdc); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h index b6fa7a836cda..3b0e9eff21b5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h @@ -22,6 +22,7 @@ void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr); void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state); void brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp, bool success); +struct brcmf_fws_info *drvr_to_fws(struct brcmf_pub *drvr); #else static inline int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { return 0; } static inline void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) {} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 6aecd8dfd824..a4dd313140f3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -127,8 +127,6 @@ struct brcmf_pub { struct brcmf_fweh_info fweh; - struct brcmf_fws_info *fws; - struct brcmf_ampdu_rx_reorder *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS]; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c index 23b2b3fa1c2c..72373e59308e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c @@ -36,6 +36,7 @@ #include "p2p.h" #include "cfg80211.h" #include "proto.h" +#include "bcdc.h" #include "common.h" /** @@ -1586,7 +1587,7 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data) { - struct brcmf_fws_info *fws = ifp->drvr->fws; + struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr); int i; u8 *credits = data; @@ -1617,7 +1618,7 @@ static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data) { - struct brcmf_fws_info *fws = ifp->drvr->fws; + struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr); if (fws) { brcmf_fws_lock(fws); @@ -1826,7 +1827,7 @@ netif_rx: 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 = ifp->drvr->fws; + struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr); u8 *signal_data; s16 data_len; u8 type; @@ -2091,8 +2092,7 @@ static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p, int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) { - struct brcmf_pub *drvr = ifp->drvr; - struct brcmf_fws_info *fws = drvr->fws; + struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr); struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); struct ethhdr *eh = (struct ethhdr *)(skb->data); int fifo = BRCMF_FWS_FIFO_BCMC; @@ -2142,7 +2142,7 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp) void brcmf_fws_add_interface(struct brcmf_if *ifp) { - struct brcmf_fws_info *fws = ifp->drvr->fws; + struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr); struct brcmf_fws_mac_descriptor *entry; if (!ifp->ndev || fws->fcmode == BRCMF_FWS_FCMODE_NONE) @@ -2160,16 +2160,17 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp) void brcmf_fws_del_interface(struct brcmf_if *ifp) { struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; + struct brcmf_fws_info *fws = drvr_to_fws(ifp->drvr); if (!entry) return; - brcmf_fws_lock(ifp->drvr->fws); + brcmf_fws_lock(fws); ifp->fws_desc = NULL; brcmf_dbg(TRACE, "deleting %s\n", entry->name); brcmf_fws_macdesc_deinit(entry); - brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx); - brcmf_fws_unlock(ifp->drvr->fws); + brcmf_fws_cleanup(fws, ifp->ifidx); + brcmf_fws_unlock(fws); } static void brcmf_fws_dequeue_worker(struct work_struct *worker) @@ -2243,7 +2244,7 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) { struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); - struct brcmf_fws_stats *fwstats = &bus_if->drvr->fws->stats; + struct brcmf_fws_stats *fwstats = &(drvr_to_fws(bus_if->drvr)->stats); seq_printf(seq, "header_pulls: %u\n" @@ -2308,7 +2309,7 @@ static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) } #endif -int brcmf_fws_attach(struct brcmf_pub *drvr) +struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr) { struct brcmf_fws_info *fws; struct brcmf_if *ifp; @@ -2316,17 +2317,15 @@ int brcmf_fws_attach(struct brcmf_pub *drvr) int rc; u32 mode; - drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL); - if (!drvr->fws) { + fws = kzalloc(sizeof(*fws), GFP_KERNEL); + if (!fws) { rc = -ENOMEM; goto fail; } - fws = drvr->fws; - spin_lock_init(&fws->spinlock); - /* set linkage back */ + /* store drvr reference */ fws->drvr = drvr; fws->fcmode = drvr->settings->fcmode; @@ -2334,7 +2333,7 @@ int brcmf_fws_attach(struct brcmf_pub *drvr) (fws->fcmode == BRCMF_FWS_FCMODE_NONE)) { fws->avoid_queueing = true; brcmf_dbg(INFO, "FWS queueing will be avoided\n"); - return 0; + return fws; } fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq"); @@ -2396,6 +2395,7 @@ int brcmf_fws_attach(struct brcmf_pub *drvr) brcmf_fws_hanger_init(&fws->hanger); brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0); brcmf_fws_macdesc_set_name(fws, &fws->desc.other); + brcmf_dbg(INFO, "added %s\n", fws->desc.other.name); brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT, BRCMF_FWS_PSQ_LEN); @@ -2405,27 +2405,24 @@ int brcmf_fws_attach(struct brcmf_pub *drvr) brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n", fws->fw_signals ? "enabled" : "disabled", tlv); - return 0; + return fws; fail: - brcmf_fws_detach(drvr); - return rc; + brcmf_fws_detach(fws); + return ERR_PTR(rc); } -void brcmf_fws_detach(struct brcmf_pub *drvr) +void brcmf_fws_detach(struct brcmf_fws_info *fws) { - struct brcmf_fws_info *fws = drvr->fws; - if (!fws) return; - if (drvr->fws->fws_wq) - destroy_workqueue(drvr->fws->fws_wq); + if (fws->fws_wq) + destroy_workqueue(fws->fws_wq); /* cleanup */ brcmf_fws_lock(fws); brcmf_fws_cleanup(fws, -1); - drvr->fws = NULL; brcmf_fws_unlock(fws); /* free top structure */ @@ -2461,7 +2458,7 @@ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked) { - struct brcmf_fws_info *fws = drvr->fws; + struct brcmf_fws_info *fws = drvr_to_fws(drvr); struct brcmf_if *ifp; int i; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h index d72515867aa0..ba07bd972002 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h @@ -18,8 +18,8 @@ #ifndef FWSIGNAL_H_ #define FWSIGNAL_H_ -int brcmf_fws_attach(struct brcmf_pub *drvr); -void brcmf_fws_detach(struct brcmf_pub *drvr); +struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr); +void brcmf_fws_detach(struct brcmf_fws_info *fws); bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws); bool brcmf_fws_fc_active(struct brcmf_fws_info *fws); void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb); -- cgit From 4835f37e3bafc138f8bfa3cbed2920dd56fed283 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Thu, 6 Apr 2017 13:14:40 +0100 Subject: brcmfmac: add length checks in scheduled scan result handler Assure the event data buffer is long enough to hold the array of netinfo items and that SSID length does not exceed the maximum of 32 characters as per 802.11 spec. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 89ac12437c92..760781f4abd2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3300,6 +3300,7 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, struct brcmf_pno_scanresults_le *pfn_result; u32 result_count; u32 status; + u32 datalen; brcmf_dbg(SCAN, "Enter\n"); @@ -3326,6 +3327,14 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, brcmf_err("FALSE PNO Event. (pfn_count == 0)\n"); goto out_err; } + + netinfo_start = brcmf_get_netinfo_array(pfn_result); + datalen = e->datalen - ((void *)netinfo_start - (void *)pfn_result); + if (datalen < result_count * sizeof(*netinfo)) { + brcmf_err("insufficient event data\n"); + goto out_err; + } + request = brcmf_alloc_internal_escan_request(wiphy, result_count); if (!request) { @@ -3333,8 +3342,6 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, goto out_err; } - netinfo_start = brcmf_get_netinfo_array(pfn_result); - for (i = 0; i < result_count; i++) { netinfo = &netinfo_start[i]; if (!netinfo) { @@ -3344,6 +3351,8 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, goto out_err; } + if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN) + netinfo->SSID_len = IEEE80211_MAX_SSID_LEN; brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n", netinfo->SSID, netinfo->channel); err = brcmf_internal_escan_add_info(request, -- cgit From 6594e1e8343645fe849a2ad42fcab94e2cf5b2c0 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Thu, 6 Apr 2017 13:14:41 +0100 Subject: brcmfmac: remove bogus check in scheduled scan result handler Checking whether the address of an array element is null is bogus so removing it. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 760781f4abd2..b687533e5c28 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3344,12 +3344,6 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, for (i = 0; i < result_count; i++) { netinfo = &netinfo_start[i]; - if (!netinfo) { - brcmf_err("Invalid netinfo ptr. index: %d\n", - i); - err = -EINVAL; - goto out_err; - } if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN) netinfo->SSID_len = IEEE80211_MAX_SSID_LEN; -- cgit From 6ea51fc708aedcf411f355de65a704ecda501bc4 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Thu, 6 Apr 2017 13:14:42 +0100 Subject: brcmfmac: only add channels and ssids once in scan request When receiving pno results there may be duplicate channels and/or ssids. Assure each is added only once when preparing the internal escan request. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index b687533e5c28..0a067e59f90b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3216,7 +3216,7 @@ static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req, { struct ieee80211_channel *chan; enum nl80211_band band; - int freq; + int freq, i; if (channel <= CH_MAX_2G_CHANNEL) band = NL80211_BAND_2GHZ; @@ -3231,10 +3231,22 @@ static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req, if (!chan) return -EINVAL; - req->channels[req->n_channels++] = chan; - memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len); - req->ssids[req->n_ssids++].ssid_len = ssid_len; + for (i = 0; i < req->n_channels; i++) { + if (req->channels[i] == chan) + break; + } + if (i == req->n_channels) + req->channels[req->n_channels++] = chan; + for (i = 0; i < req->n_ssids; i++) { + if (req->ssids[i].ssid_len == ssid_len && + !memcmp(req->ssids[i].ssid, ssid, ssid_len)) + break; + } + if (i == req->n_ssids) { + memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len); + req->ssids[req->n_ssids++].ssid_len = ssid_len; + } return 0; } -- cgit From b01127b268339fe12d3e1cbda4ceef1e89e0a086 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Tue, 4 Apr 2017 11:49:54 -0500 Subject: rtlwifi: btcoex: 23b 2ant: check PS state before setting tdma duration For time division multiple access, the wifi and bt take turns to transmit, but we need to let AP know that wifi is under standby mode by sending null data to "pretend" entering power saving state using lps rpwm. But, the fw does not know if it is the actual power saving mode or just a fake one to cheat to the AP. Hence, before fw setting the tdma duration, the fw needs the driver to check the power saving state first. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 95 ++++++++++++++++++++++ .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.h | 6 ++ 2 files changed, 101 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 988f276531dd..dda14bd0ac45 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -978,6 +978,33 @@ static void btc8723b2ant_set_fw_ignore_wlan_act(struct btc_coexist *btcoexist, btcoexist->btc_fill_h2c(btcoexist, 0x63, 1, h2c_parameter); } +static void btc8723b2ant_set_lps_rpwm(struct btc_coexist *btcoexist, + u8 lps_val, u8 rpwm_val) +{ + u8 lps = lps_val; + u8 rpwm = rpwm_val; + + btcoexist->btc_set(btcoexist, BTC_SET_U1_LPS_VAL, &lps); + btcoexist->btc_set(btcoexist, BTC_SET_U1_RPWM_VAL, &rpwm); +} + +static void btc8723b2ant_lps_rpwm(struct btc_coexist *btcoexist, + bool force_exec, u8 lps_val, u8 rpwm_val) +{ + coex_dm->cur_lps = lps_val; + coex_dm->cur_rpwm = rpwm_val; + + if (!force_exec) { + if ((coex_dm->pre_lps == coex_dm->cur_lps) && + (coex_dm->pre_rpwm == coex_dm->cur_rpwm)) + return; + } + btc8723b2ant_set_lps_rpwm(btcoexist, lps_val, rpwm_val); + + coex_dm->pre_lps = coex_dm->cur_lps; + coex_dm->pre_rpwm = coex_dm->cur_rpwm; +} + static void btc8723b2ant_ignore_wlan_act(struct btc_coexist *btcoexist, bool force_exec, bool enable) { @@ -1341,9 +1368,74 @@ static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec, coex_dm->pre_ps_tdma = coex_dm->cur_ps_tdma; } +static void btc8723b2ant_ps_tdma_check_for_power_save_state( + struct btc_coexist *btcoexist, bool new_ps_state) +{ + u8 lps_mode = 0x0; + + btcoexist->btc_get(btcoexist, BTC_GET_U1_LPS_MODE, &lps_mode); + + if (lps_mode) { + /* already under LPS state */ + if (new_ps_state) { + /* keep state under LPS, do nothing. */ + } else { + /* will leave LPS state, turn off psTdma first */ + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + } + } else { + /* NO PS state */ + if (new_ps_state) { + /* will enter LPS state, turn off psTdma first */ + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + } else { + /* keep state under NO PS state, do nothing. */ + } + } +} + +static void btc8723b2ant_power_save_state(struct btc_coexist *btcoexist, + u8 ps_type, u8 lps_val, u8 rpwm_val) +{ + bool low_pwr_disable = false; + + switch (ps_type) { + case BTC_PS_WIFI_NATIVE: + /* recover to original 32k low power setting */ + low_pwr_disable = false; + btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, + &low_pwr_disable); + btcoexist->btc_set(btcoexist, BTC_SET_ACT_NORMAL_LPS, NULL); + coex_sta->force_lps_on = false; + break; + case BTC_PS_LPS_ON: + btc8723b2ant_ps_tdma_check_for_power_save_state(btcoexist, + true); + btc8723b2ant_lps_rpwm(btcoexist, NORMAL_EXEC, lps_val, + rpwm_val); + /* when coex force to enter LPS, do not enter 32k low power */ + low_pwr_disable = true; + btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, + &low_pwr_disable); + /* power save must executed before psTdma */ + btcoexist->btc_set(btcoexist, BTC_SET_ACT_ENTER_LPS, NULL); + coex_sta->force_lps_on = true; + break; + case BTC_PS_LPS_OFF: + btc8723b2ant_ps_tdma_check_for_power_save_state(btcoexist, + false); + btcoexist->btc_set(btcoexist, BTC_SET_ACT_LEAVE_LPS, NULL); + coex_sta->force_lps_on = false; + break; + default: + break; + } +} + static void btc8723b2ant_coex_alloff(struct btc_coexist *btcoexist) { /* fw all off */ + btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); @@ -1359,6 +1451,7 @@ static void btc8723b2ant_coex_alloff(struct btc_coexist *btcoexist) static void btc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist) { /* force to reset coex mechanism*/ + btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); btc8723b2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1); btc8723b2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6); @@ -1801,6 +1894,8 @@ static void btc8723b2ant_action_hid(struct btc_coexist *btcoexist) /* for HID quality & wifi performance balance at 11n mode */ btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 9); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h index 746930d0d244..5bcaeceada9c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h @@ -122,6 +122,11 @@ struct coex_dm_8723b_2ant { u8 bt_status; u8 wifi_chnl_info[3]; + u8 pre_lps; + u8 cur_lps; + u8 pre_rpwm; + u8 cur_rpwm; + bool need_recover_0x948; u16 backup_0x948; }; @@ -160,6 +165,7 @@ struct coex_sta_8723b_2ant { u32 crc_err_11g; u32 crc_err_11n; u32 crc_err_11n_agg; + bool force_lps_on; u8 a2dp_bit_pool; }; -- cgit From b91ed731393bc3fe9da3a0d909d6a3fdf7685232 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Tue, 4 Apr 2017 11:49:55 -0500 Subject: rtlwifi: btcoex: 23b 2ant: rename tdma_adj_type to ps_tdma_du_adj_type The variable is for PS tdma duration adjust, so rename it to clearly to specify its usage. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 30 +++++++++++----------- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.h | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index dda14bd0ac45..30b2b93e92ca 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1632,34 +1632,34 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 13); - coex_dm->tdma_adj_type = 13; + coex_dm->ps_tdma_du_adj_type = 13; } else if (max_interval == 2) { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); - coex_dm->tdma_adj_type = 14; + coex_dm->ps_tdma_du_adj_type = 14; } else { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 15); - coex_dm->tdma_adj_type = 15; + coex_dm->ps_tdma_du_adj_type = 15; } } else { if (max_interval == 1) { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); - coex_dm->tdma_adj_type = 9; + coex_dm->ps_tdma_du_adj_type = 9; } else if (max_interval == 2) { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10); - coex_dm->tdma_adj_type = 10; + coex_dm->ps_tdma_du_adj_type = 10; } else { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); - coex_dm->tdma_adj_type = 11; + coex_dm->ps_tdma_du_adj_type = 11; } } } else { @@ -1668,34 +1668,34 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); - coex_dm->tdma_adj_type = 5; + coex_dm->ps_tdma_du_adj_type = 5; } else if (max_interval == 2) { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 6); - coex_dm->tdma_adj_type = 6; + coex_dm->ps_tdma_du_adj_type = 6; } else { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 7); - coex_dm->tdma_adj_type = 7; + coex_dm->ps_tdma_du_adj_type = 7; } } else { if (max_interval == 1) { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 1); - coex_dm->tdma_adj_type = 1; + coex_dm->ps_tdma_du_adj_type = 1; } else if (max_interval == 2) { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 2); - coex_dm->tdma_adj_type = 2; + coex_dm->ps_tdma_du_adj_type = 2; } else { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); - coex_dm->tdma_adj_type = 3; + coex_dm->ps_tdma_du_adj_type = 3; } } } @@ -1804,11 +1804,11 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, /* if current PsTdma not match with the recorded one (scan, dhcp, ...), * then we have to adjust it back to the previous recorded one. */ - if (coex_dm->cur_ps_tdma != coex_dm->tdma_adj_type) { + if (coex_dm->cur_ps_tdma != coex_dm->ps_tdma_du_adj_type) { bool scan = false, link = false, roam = false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], PsTdma type dismatch!!!, curPsTdma=%d, recordPsTdma=%d\n", - coex_dm->cur_ps_tdma, coex_dm->tdma_adj_type); + coex_dm->cur_ps_tdma, coex_dm->ps_tdma_du_adj_type); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link); @@ -1816,7 +1816,7 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, if (!scan && !link && !roam) btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, - coex_dm->tdma_adj_type); + coex_dm->ps_tdma_du_adj_type); else RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], roaming/link/scan is under progress, will adjust next time!!!\n"); diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h index 5bcaeceada9c..9f41206b20ed 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h @@ -84,7 +84,7 @@ struct coex_dm_8723b_2ant { u8 pre_ps_tdma; u8 cur_ps_tdma; u8 ps_tdma_para[5]; - u8 tdma_adj_type; + u8 ps_tdma_du_adj_type; bool reset_tdma_adjust; bool auto_tdma_adjust; bool pre_ps_tdma_on; -- cgit From 5a347a48467b3c94ad66ead8addb8a29f117dc59 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Tue, 4 Apr 2017 11:49:56 -0500 Subject: rtlwifi: btcoex: 23b 2ant: detect ap num and set GNT_BT properly If ap num < 10, let wifi and bt transmit simultaneously, otherwise set wifi into standy when bt is transmitting Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 30b2b93e92ca..eaf909295a69 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -2831,6 +2831,7 @@ void ex_btc8723b2ant_media_status_notify(struct btc_coexist *btcoexist, u8 h2c_parameter[3] = {0}; u32 wifi_bw; u8 wifi_central_chnl; + u8 ap_num = 0; if (BTC_MEDIA_CONNECT == type) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -2848,10 +2849,16 @@ void ex_btc8723b2ant_media_status_notify(struct btc_coexist *btcoexist, h2c_parameter[1] = wifi_central_chnl; btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - if (BTC_WIFI_BW_HT40 == wifi_bw) + if (wifi_bw == BTC_WIFI_BW_HT40) { h2c_parameter[2] = 0x30; - else - h2c_parameter[2] = 0x20; + } else { + btcoexist->btc_get(btcoexist, BTC_GET_U1_AP_NUM, + &ap_num); + if (ap_num < 10) + h2c_parameter[2] = 0x30; + else + h2c_parameter[2] = 0x20; + } } coex_dm->wifi_chnl_info[0] = h2c_parameter[0]; -- cgit From 1712952beabb4f8f5071336b4cc744365efc9b2b Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Tue, 4 Apr 2017 11:49:57 -0500 Subject: rtlwifi: btcoex: 23b 2ant: more cases for adjusting tdma duration These cases are for the btcoex to tune the performance Signed-off-by: Yan-Hsuan Chuang Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 766 +++++++++++++++++++++ 1 file changed, 766 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index eaf909295a69..dadcddb53557 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1638,6 +1638,11 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, NORMAL_EXEC, true, 14); coex_dm->ps_tdma_du_adj_type = 14; + } else if (max_interval == 3) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = 15; } else { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, @@ -1655,6 +1660,11 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, NORMAL_EXEC, true, 10); coex_dm->ps_tdma_du_adj_type = 10; + } else if (max_interval == 3) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = 11; } else { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, @@ -1674,6 +1684,11 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, NORMAL_EXEC, true, 6); coex_dm->ps_tdma_du_adj_type = 6; + } else if (max_interval == 3) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = 7; } else { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, @@ -1691,6 +1706,11 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, NORMAL_EXEC, true, 2); coex_dm->ps_tdma_du_adj_type = 2; + } else if (max_interval == 3) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = 3; } else { btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, @@ -1796,6 +1816,752 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], max Interval = %d\n", max_interval); + if (max_interval == 1) { + if (tx_pause) { + if (coex_dm->cur_ps_tdma == 71) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 5); + coex_dm->ps_tdma_du_adj_type = 5; + } else if (coex_dm->cur_ps_tdma == 1) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 5); + coex_dm->ps_tdma_du_adj_type = 5; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 6); + coex_dm->ps_tdma_du_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 4) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 8); + coex_dm->ps_tdma_du_adj_type = 8; + } + if (coex_dm->cur_ps_tdma == 9) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 13); + coex_dm->ps_tdma_du_adj_type = 13; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 14); + coex_dm->ps_tdma_du_adj_type = 14; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 16); + coex_dm->ps_tdma_du_adj_type = 16; + } + + if (result == -1) { + if (coex_dm->cur_ps_tdma == 5) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->ps_tdma_du_adj_type = + 6; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 8); + coex_dm->ps_tdma_du_adj_type = + 8; + } else if (coex_dm->cur_ps_tdma == 13) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->ps_tdma_du_adj_type = + 14; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 16); + coex_dm->ps_tdma_du_adj_type = + 16; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 8) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->ps_tdma_du_adj_type = + 6; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 5); + coex_dm->ps_tdma_du_adj_type = + 5; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->ps_tdma_du_adj_type = + 14; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 13); + coex_dm->ps_tdma_du_adj_type = + 13; + } + } + } else { + if (coex_dm->cur_ps_tdma == 5) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 71); + coex_dm->ps_tdma_du_adj_type = 71; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 2); + coex_dm->ps_tdma_du_adj_type = 2; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 8) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 4); + coex_dm->ps_tdma_du_adj_type = 4; + } + if (coex_dm->cur_ps_tdma == 13) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 9); + coex_dm->ps_tdma_du_adj_type = 9; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 10); + coex_dm->ps_tdma_du_adj_type = 10; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 12); + coex_dm->ps_tdma_du_adj_type = 12; + } + + if (result == -1) { + if (coex_dm->cur_ps_tdma == 71) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 1); + coex_dm->ps_tdma_du_adj_type = + 1; + } else if (coex_dm->cur_ps_tdma == 1) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->ps_tdma_du_adj_type = + 2; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 4); + coex_dm->ps_tdma_du_adj_type = + 4; + } else if (coex_dm->cur_ps_tdma == 9) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->ps_tdma_du_adj_type = + 10; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 12); + coex_dm->ps_tdma_du_adj_type = + 12; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 4) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->ps_tdma_du_adj_type = + 2; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 1); + coex_dm->ps_tdma_du_adj_type = + 1; + } else if (coex_dm->cur_ps_tdma == 1) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 71); + coex_dm->ps_tdma_du_adj_type = + 71; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->ps_tdma_du_adj_type = + 10; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 9); + coex_dm->ps_tdma_du_adj_type = + 9; + } + } + } + } else if (max_interval == 2) { + if (tx_pause) { + if (coex_dm->cur_ps_tdma == 1) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 6); + coex_dm->ps_tdma_du_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 6); + coex_dm->ps_tdma_du_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 4) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 8); + coex_dm->ps_tdma_du_adj_type = 8; + } + if (coex_dm->cur_ps_tdma == 9) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 14); + coex_dm->ps_tdma_du_adj_type = 14; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 14); + coex_dm->ps_tdma_du_adj_type = 14; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 16); + coex_dm->ps_tdma_du_adj_type = 16; + } + if (result == -1) { + if (coex_dm->cur_ps_tdma == 5) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->ps_tdma_du_adj_type = + 6; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 8); + coex_dm->ps_tdma_du_adj_type = + 8; + } else if (coex_dm->cur_ps_tdma == 13) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->ps_tdma_du_adj_type = + 14; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 16); + coex_dm->ps_tdma_du_adj_type = + 16; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 8) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->ps_tdma_du_adj_type = + 6; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->ps_tdma_du_adj_type = + 6; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->ps_tdma_du_adj_type = + 14; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->ps_tdma_du_adj_type = + 14; + } + } + } else { + if (coex_dm->cur_ps_tdma == 5) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 2); + coex_dm->ps_tdma_du_adj_type = 2; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 2); + coex_dm->ps_tdma_du_adj_type = 2; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 8) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 4); + coex_dm->ps_tdma_du_adj_type = 4; + } + if (coex_dm->cur_ps_tdma == 13) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 10); + coex_dm->ps_tdma_du_adj_type = 10; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 10); + coex_dm->ps_tdma_du_adj_type = 10; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 12); + coex_dm->ps_tdma_du_adj_type = 12; + } + if (result == -1) { + if (coex_dm->cur_ps_tdma == 1) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->ps_tdma_du_adj_type = + 2; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 4); + coex_dm->ps_tdma_du_adj_type = + 4; + } else if (coex_dm->cur_ps_tdma == 9) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->ps_tdma_du_adj_type = + 10; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 12); + coex_dm->ps_tdma_du_adj_type = + 12; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 4) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->ps_tdma_du_adj_type = + 2; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->ps_tdma_du_adj_type = + 2; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->ps_tdma_du_adj_type = + 10; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->ps_tdma_du_adj_type = + 10; + } + } + } + } else if (max_interval == 3) { + if (tx_pause) { + if (coex_dm->cur_ps_tdma == 1) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 4) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 8); + coex_dm->ps_tdma_du_adj_type = 8; + } + if (coex_dm->cur_ps_tdma == 9) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 16); + coex_dm->ps_tdma_du_adj_type = 16; + } + if (result == -1) { + if (coex_dm->cur_ps_tdma == 5) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 8); + coex_dm->ps_tdma_du_adj_type = + 8; + } else if (coex_dm->cur_ps_tdma == 13) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 16); + coex_dm->ps_tdma_du_adj_type = + 16; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 8) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } + } + } else { + if (coex_dm->cur_ps_tdma == 5) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 8) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 4); + coex_dm->ps_tdma_du_adj_type = 4; + } + if (coex_dm->cur_ps_tdma == 13) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 12); + coex_dm->ps_tdma_du_adj_type = 12; + } + if (result == -1) { + if (coex_dm->cur_ps_tdma == 1) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 4); + coex_dm->ps_tdma_du_adj_type = + 4; + } else if (coex_dm->cur_ps_tdma == 9) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 12); + coex_dm->ps_tdma_du_adj_type = + 12; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 4) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } + } + } + } } RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, -- cgit From b59f02fb7f44cd9c7b159f87c84c1e53e00e03a2 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Tue, 4 Apr 2017 11:49:58 -0500 Subject: rtlwifi: btcoex: 23b 2ant: fix PTA unstable problem when hw init In the hardware initialisation stage, the PTA circuits may be unstable, so we reset it after 6 secs to fix the problem. Signed-off-by: Yan-Hsuan Chuang Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 41 ++++++++-------------- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.h | 2 ++ 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index dadcddb53557..e0b6dfa938fa 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -3282,12 +3282,14 @@ void ex_btc8723b2ant_init_hwconfig(struct btc_coexist *btcoexist) /* Antenna config */ btc8723b2ant_set_ant_path(btcoexist, BTC_ANT_WIFI_AT_MAIN, true, false); + coex_sta->dis_ver_info_cnt = 0; + /* PTA parameter */ btc8723b2ant_coex_table_with_type(btcoexist, FORCE_EXEC, 0); /* Enable counter statistics */ /* 0x76e[3] = 1, WLAN_ACT controlled by PTA */ - btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc); + btcoexist->btc_write_1byte(btcoexist, 0x76e, 0x4); btcoexist->btc_write_1byte(btcoexist, 0x778, 0x3); btcoexist->btc_write_1byte_bitmask(btcoexist, 0x40, 0x20, 0x1); } @@ -3827,36 +3829,21 @@ void ex_btc8723b2ant_halt_notify(struct btc_coexist *btcoexist) void ex_btc8723b2ant_periodical(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; - struct btc_board_info *board_info = &btcoexist->board_info; - struct btc_stack_info *stack_info = &btcoexist->stack_info; - static u8 dis_ver_info_cnt; - u32 fw_ver = 0, bt_patch_ver = 0; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], ==========================Periodical===========================\n"); - if (dis_ver_info_cnt <= 5) { - dis_ver_info_cnt += 1; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], ****************************************************************\n"); - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Ant PG Num/ Ant Mech/ Ant Pos = %d/ %d/ %d\n", - board_info->pg_ant_num, - board_info->btdm_ant_num, - board_info->btdm_ant_pos); - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], BT stack/ hci ext ver = %s / %d\n", - stack_info->profile_notified ? "Yes" : "No", - stack_info->hci_version); - btcoexist->btc_get(btcoexist, BTC_GET_U4_BT_PATCH_VER, - &bt_patch_ver); - btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_FW_VER, &fw_ver); - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], CoexVer/ fw_ver/ PatchVer = %d_%x/ 0x%x/ 0x%x(%d)\n", - glcoex_ver_date_8723b_2ant, glcoex_ver_8723b_2ant, - fw_ver, bt_patch_ver, bt_patch_ver); - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], ****************************************************************\n"); + if (coex_sta->dis_ver_info_cnt <= 5) { + coex_sta->dis_ver_info_cnt += 1; + if (coex_sta->dis_ver_info_cnt == 3) { + /* Antenna config to set 0x765 = 0x0 (GNT_BT control by + * PTA) after initial + */ + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], Set GNT_BT control by PTA\n"); + btc8723b2ant_set_ant_path( + btcoexist, BTC_ANT_WIFI_AT_MAIN, false, false); + } } #if (BT_AUTO_REPORT_ONLY_8723B_2ANT == 0) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h index 9f41206b20ed..57eac223bcbd 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h @@ -167,6 +167,8 @@ struct coex_sta_8723b_2ant { u32 crc_err_11n_agg; bool force_lps_on; + u8 dis_ver_info_cnt; + u8 a2dp_bit_pool; }; -- cgit From 609d59acb68ab5175768b818438ea2c95ab9a980 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Tue, 4 Apr 2017 11:49:59 -0500 Subject: rtlwifi: btcoex: 23b 2ant: add pnp notidy to avoid LPS/IPS mismatch When driver is going to sleep, it does not leave LPS/IPS, thus the BTCoex may have mismatch when driver wakes up. To avoid that, BTCoex needs to clear the IPS/LPS state when it receives a pnp notify, then it can properly set up the hw when driver wakes up. Signed-off-by: Yan-Hsuan Chuang Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index e0b6dfa938fa..d599e659930c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -3826,6 +3826,33 @@ void ex_btc8723b2ant_halt_notify(struct btc_coexist *btcoexist) ex_btc8723b2ant_media_status_notify(btcoexist, BTC_MEDIA_DISCONNECT); } +void ex_btc8723b2ant_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Pnp notify\n"); + + if (pnp_state == BTC_WIFI_PNP_SLEEP) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], Pnp notify to SLEEP\n"); + + /* Driver do not leave IPS/LPS when driver is going to sleep, so + * BTCoexistence think wifi is still under IPS/LPS + * + * BT should clear UnderIPS/UnderLPS state to avoid mismatch + * state after wakeup. + */ + coex_sta->under_ips = false; + coex_sta->under_lps = false; + } else if (pnp_state == BTC_WIFI_PNP_WAKE_UP) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], Pnp notify to WAKE UP\n"); + ex_btc8723b2ant_init_hwconfig(btcoexist); + btc8723b2ant_init_coex_dm(btcoexist); + btc8723b2ant_query_bt_info(btcoexist); + } +} + void ex_btc8723b2ant_periodical(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; -- cgit From 684df42cd59711d9077ca4a1980c0b9b084c0b4c Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Tue, 4 Apr 2017 11:50:00 -0500 Subject: rtlwifi: btcoex: 23b 2ant: check more cases when bt is queuing If bt is queing, we need to set the packet priority properly. Originally we only consider if wifi was connected or not, but now we also consider if bt is under abnormal scan or wifi is scanning, roaming or linking, and set the coex table. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 24 +++++++++++++++++++--- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.h | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index d599e659930c..31bfc4bb2bd5 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1464,17 +1464,35 @@ static void btc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist) static void btc8723b2ant_action_bt_inquiry(struct btc_coexist *btcoexist) { + struct rtl_priv *rtlpriv = btcoexist->adapter; bool wifi_connected = false; bool low_pwr_disable = true; + bool scan = false, link = false, roam = false; btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); - if (wifi_connected) { - btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_ROAM, &roam); + + btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); + + if (coex_sta->bt_abnormal_scan) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 23); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 3); + } else if (scan || link || roam) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], Wifi link process + BT Inq/Page!!\n"); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 15); + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); + } else if (wifi_connected) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], Wifi connected + BT Inq/Page!!\n"); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 15); + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); } else { btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h index 57eac223bcbd..f5e091ff4b59 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h @@ -137,6 +137,7 @@ struct coex_sta_8723b_2ant { bool a2dp_exist; bool hid_exist; bool pan_exist; + bool bt_abnormal_scan; bool under_lps; bool under_ips; -- cgit From a0f430b3eaefc180b9b04f3579ed90a354765f39 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Tue, 4 Apr 2017 11:50:01 -0500 Subject: rtlwifi: btcoex: 23b 2ant: workaround for bt a2dp and hid For a2dp and hid, we need extra process to avoid voice degradation Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 31bfc4bb2bd5..55dabe050f87 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1035,6 +1035,8 @@ static void btc8723b2ant_set_fw_ps_tdma(struct btc_coexist *btcoexist, u8 byte1, { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[5]; + if ((coex_sta->a2dp_exist) && (coex_sta->hid_exist)) + byte5 = byte5 | 0x1; h2c_parameter[0] = byte1; h2c_parameter[1] = byte2; -- cgit From 3b4fa04d8efdf24f331e1d590090b8cfe9007182 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Tue, 4 Apr 2017 11:50:02 -0500 Subject: rtlwifi: btcoex: 23b 2ant: tell fw if external or internal switch is used Some chips use an external antenna switch, but fw may not realize it. Here we tell fw exactly that which type of switch we are using. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 55dabe050f87..ec95f0097dfc 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1106,14 +1106,19 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) { /* tell firmware "no antenna inverse" */ h2c_parameter[0] = 0; - h2c_parameter[1] = 1; /* ext switch type */ - btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, - h2c_parameter); - btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); } else { /* tell firmware "antenna inverse" */ h2c_parameter[0] = 1; } + + if (use_ext_switch) { + /* ext switch type */ + h2c_parameter[1] = 1; + } else { + /* int switch type */ + h2c_parameter[1] = 0; + } + btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); } else { if (fw_ver >= 0x180000) { /* Use H2C to set GNT_BT to "Control by PTA"*/ -- cgit From bcd37f4a0831fb4f3f7e1c9c7baccdf3d97b7064 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Tue, 4 Apr 2017 11:50:03 -0500 Subject: rtlwifi: btcoex: 23b 2ant: let bt transmit when hw initialisation done During hw initialisation, wifi may be ready after bt has already been ready, which causes bt to act abnormally. To avoid this, set GNT_BT to high during hw init. Signed-off-by: Yan-Hsuan Chuang Cc: Stable Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 45 +++++++++++++++------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index ec95f0097dfc..16a4a5c4f024 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1083,12 +1083,7 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, use_ext_switch = true; if (init_hwcfg) { - /* 0x4c[23] = 0, 0x4c[24] = 1 Antenna control by WL/BT */ - u32tmp = btcoexist->btc_read_4byte(btcoexist, 0x4c); - u32tmp &= ~BIT23; - u32tmp |= BIT24; - btcoexist->btc_write_4byte(btcoexist, 0x4c, u32tmp); - + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x39, 0x8, 0x1); btcoexist->btc_write_1byte(btcoexist, 0x974, 0xff); btcoexist->btc_write_1byte_bitmask(btcoexist, 0x944, 0x3, 0x3); btcoexist->btc_write_1byte(btcoexist, 0x930, 0x77); @@ -1103,6 +1098,12 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, btcoexist->btc_write_1byte(btcoexist, 0x765, 0x18); } + btcoexist->btc_write_4byte(btcoexist, 0x948, 0x0); + + /* WiFi TRx Mask off */ + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, + 0x1, 0xfffff, 0x0); + if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) { /* tell firmware "no antenna inverse" */ h2c_parameter[0] = 0; @@ -1125,17 +1126,23 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, h2c_parameter[0] = 0; btcoexist->btc_fill_h2c(btcoexist, 0x6E, 1, h2c_parameter); - btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280); + } else { + btcoexist->btc_write_1byte(btcoexist, 0x765, 0x0); } } /* ext switch setting */ if (use_ext_switch) { + if (init_hwcfg) { + /* 0x4c[23] = 0, 0x4c[24] = 1 Ant controlled by WL/BT */ + u32tmp = btcoexist->btc_read_4byte(btcoexist, 0x4c); + u32tmp &= ~BIT23; + u32tmp |= BIT24; + btcoexist->btc_write_4byte(btcoexist, 0x4c, u32tmp); + } + /* fixed internal switch S1->WiFi, S0->BT */ - if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) - btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); - else - btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280); + btcoexist->btc_write_4byte(btcoexist, 0x948, 0x0); switch (antpos_type) { case BTC_ANT_WIFI_AT_MAIN: @@ -1149,9 +1156,18 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, 0x92c, 0x3, 0x2); break; } - } else { /* internal switch */ - /* fixed ext switch */ - btcoexist->btc_write_1byte_bitmask(btcoexist, 0x92c, 0x3, 0x1); + } else { + /* internal switch */ + if (init_hwcfg) { + /* 0x4c[23] = 0, 0x4c[24] = 1 Ant controlled by WL/BT */ + u32tmp = btcoexist->btc_read_4byte(btcoexist, 0x4c); + u32tmp |= BIT23; + u32tmp &= ~BIT24; + btcoexist->btc_write_4byte(btcoexist, 0x4c, u32tmp); + } + + /* fixed ext switch, S1->Main, S0->Aux */ + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, 0x1, 0x0); switch (antpos_type) { case BTC_ANT_WIFI_AT_MAIN: /* fixed internal switch S1->WiFi, S0->BT */ @@ -1458,6 +1474,7 @@ static void btc8723b2ant_coex_alloff(struct btc_coexist *btcoexist) static void btc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist) { /* force to reset coex mechanism*/ + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); btc8723b2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1); -- cgit From a8570896b962dc2b8e7787c59a45521e64b187e2 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Tue, 4 Apr 2017 11:50:04 -0500 Subject: rtlwifi: btcoex: 23b 2ant: turn off ps and tdma mechanism when in concurrent mode When wifi is in concurrent mode, we can not distinguish if it is the real PS or just a fake one by sending null data, so we turn it off in case of miracast scenario. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 46 ++++++++++++++++++++++ .../realtek/rtlwifi/btcoexist/halbtcoutsrc.h | 1 + 2 files changed, 47 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 16a4a5c4f024..654886598482 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -3168,10 +3168,29 @@ static void btc8723b2ant_action_hid_a2dp(struct btc_coexist *btcoexist) } } +static void btc8723b2ant_action_wifi_multi_port(struct btc_coexist *btcoexist) +{ + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + + /* sw all off */ + btc8723b2ant_sw_mechanism(btcoexist, false, false, false, false); + + /* hw all off */ + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); + + btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); +} + static void btc8723b2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 algorithm = 0; + u32 num_of_wifi_link = 0; + u32 wifi_link_status = 0; + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; + bool miracast_plus_bt = false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], RunCoexistMechanism()===>\n"); @@ -3203,6 +3222,33 @@ static void btc8723b2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) } } + /* for P2P */ + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_LINK_STATUS, + &wifi_link_status); + num_of_wifi_link = wifi_link_status >> 16; + + if ((num_of_wifi_link >= 2) || + (wifi_link_status & WIFI_P2P_GO_CONNECTED)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "############# [BTCoex], Multi-Port num_of_wifi_link = %d, wifi_link_status = 0x%x\n", + num_of_wifi_link, wifi_link_status); + + if (bt_link_info->bt_link_exist) + miracast_plus_bt = true; + else + miracast_plus_bt = false; + + btcoexist->btc_set(btcoexist, BTC_SET_BL_MIRACAST_PLUS_BT, + &miracast_plus_bt); + btc8723b2ant_action_wifi_multi_port(btcoexist); + + return; + } + + miracast_plus_bt = false; + btcoexist->btc_set(btcoexist, BTC_SET_BL_MIRACAST_PLUS_BT, + &miracast_plus_bt); + coex_dm->cur_algorithm = algorithm; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Algorithm = %d\n", diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h index d7ba6ad1e66f..022658cafbca 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h @@ -249,6 +249,7 @@ enum btc_set_type { BTC_SET_BL_TO_REJ_AP_AGG_PKT, BTC_SET_BL_BT_CTRL_AGG_SIZE, BTC_SET_BL_INC_SCAN_DEV_NUM, + BTC_SET_BL_MIRACAST_PLUS_BT, /* type u1Byte */ BTC_SET_U1_RSSI_ADJ_VAL_FOR_AGC_TABLE_ON, -- cgit From c5e2113613bcf2693a9d7e54e1f54b27194a935c Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Tue, 4 Apr 2017 11:50:05 -0500 Subject: rtlwifi: btcoex: 23b 2ant: turn off antenna when rssi is too high/low For 2-antenna combo card, the signal of the neighbor antenna could be over noise level and cause be severe interference. So we monitor the rssi and turn off one of the antennas when the other is transmitting and the rssi level is beyond a threshold. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 332 +++++++++++++++++---- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.h | 7 + 2 files changed, 282 insertions(+), 57 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 654886598482..e722c7cd1673 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -369,6 +369,8 @@ static bool btc8723b2ant_is_wifi_status_changed(struct btc_coexist *btcoexist) static bool pre_bt_hs_on; bool wifi_busy = false, under_4way = false, bt_hs_on = false; bool wifi_connected = false; + u8 wifi_rssi_state = BTC_RSSI_STATE_HIGH; + u8 tmp; btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); @@ -392,6 +394,15 @@ static bool btc8723b2ant_is_wifi_status_changed(struct btc_coexist *btcoexist) pre_bt_hs_on = bt_hs_on; return true; } + + tmp = BT_8723B_2ANT_WIFI_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; + wifi_rssi_state = + btc8723b2ant_wifi_rssi_state(btcoexist, 0, 2, tmp, 0); + + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_LOW)) + return true; } return false; @@ -1186,8 +1197,16 @@ static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec, { struct rtl_priv *rtlpriv = btcoexist->adapter; struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; + u8 wifi_rssi_state, bt_rssi_state; s8 wifi_duration_adjust = 0x0; u8 tdma_byte4_modify = 0x0; + u8 tmp = BT_8723B_2ANT_WIFI_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; + + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, 0, 2, tmp, 0); + tmp = BT_8723B_2ANT_BT_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; + bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, tmp, 0); RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], %s turn %s PS TDMA, type=%d\n", @@ -1196,6 +1215,15 @@ static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec, coex_dm->cur_ps_tdma_on = turn_on; coex_dm->cur_ps_tdma = type; + if (!(BTC_RSSI_HIGH(wifi_rssi_state) && + BTC_RSSI_HIGH(bt_rssi_state)) && turn_on) { + /* for WiFi RSSI low or BT RSSI low */ + type = type + 100; + coex_dm->is_switch_to_1dot5_ant = true; + } else { + coex_dm->is_switch_to_1dot5_ant = false; + } + if (!force_exec) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], bPrePsTdmaOn = %d, bCurPsTdmaOn = %d!!\n", @@ -1346,6 +1374,12 @@ static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec, btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x15, 0x03, 0x70, 0x90); break; + + case 23: + case 123: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x35, + 0x03, 0x71, 0x10); + break; case 71: btc8723b2ant_set_fw_ps_tdma( btcoexist, 0xe3, 0x3c + wifi_duration_adjust, @@ -1367,6 +1401,37 @@ static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec, btcoexist, 0xd3, 0x2d + wifi_duration_adjust, 0x03, 0x70, 0x50 | tdma_byte4_modify); break; + case 103: + case 107: + case 111: + case 115: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xd3, 0x1c, + 0x03, 0x70, + 0x50 | tdma_byte4_modify); + break; + case 104: + case 108: + case 112: + case 116: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xd3, 0x10, + 0x03, 0x70, + 0x50 | tdma_byte4_modify); + break; + case 109: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x3c, + 0x03, 0xf1, + 0x90 | tdma_byte4_modify); + break; + case 121: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x15, + 0x03, 0x70, + 0x90 | tdma_byte4_modify); + break; + case 22: + case 122: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x35, + 0x03, 0x71, 0x11); + break; } } else { /* disable PS tdma */ @@ -2634,17 +2699,25 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, /* SCO only or SCO+PAN(HS) */ static void btc8723b2ant_action_sco(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state; + u8 wifi_rssi_state, bt_rssi_state; u32 wifi_bw; - wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, - 0, 2, 15, 0); + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8723b2ant_bt_rssi_state( + btcoexist, 2, BT_8723B_2ANT_BT_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset, + 0); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 4); + if (BTC_RSSI_HIGH(bt_rssi_state)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); if (BTC_WIFI_BW_LEGACY == wifi_bw) @@ -2683,16 +2756,22 @@ static void btc8723b2ant_action_hid(struct btc_coexist *btcoexist) { u8 wifi_rssi_state, bt_rssi_state; u32 wifi_bw; + u8 tmp = BT_8723B_2ANT_BT_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; - wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, - 0, 2, 15, 0); - bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, 29, 0); + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, tmp, 0); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); - btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, true, 0x5); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + if (BTC_RSSI_HIGH(bt_rssi_state)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); if (wifi_bw == BTC_WIFI_BW_LEGACY) @@ -2738,12 +2817,12 @@ static void btc8723b2ant_action_a2dp(struct btc_coexist *btcoexist) u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; u32 wifi_bw; u8 ap_num = 0; + u8 tmp = BT_8723B_2ANT_BT_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; - wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, - 0, 2, 15, 0); - wifi_rssi_state1 = btc8723b2ant_wifi_rssi_state(btcoexist, - 1, 2, 40, 0); - bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, 29, 0); + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state1 = btc8723b2ant_wifi_rssi_state(btcoexist, 1, 2, 40, 0); + bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, tmp, 0); btcoexist->btc_get(btcoexist, BTC_GET_U1_AP_NUM, &ap_num); @@ -2774,7 +2853,20 @@ static void btc8723b2ant_action_a2dp(struct btc_coexist *btcoexist) btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); + if (BTC_RSSI_HIGH(bt_rssi_state)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + + if (BTC_RSSI_HIGH(wifi_rssi_state1) && BTC_RSSI_HIGH(bt_rssi_state)) { + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + } else { + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 13); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_LPS_ON, 0x50, + 0x4); + } if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) @@ -2808,18 +2900,37 @@ static void btc8723b2ant_action_a2dp(struct btc_coexist *btcoexist) static void btc8723b2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; u32 wifi_bw; + u8 tmp = BT_8723B_2ANT_WIFI_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; - wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, - 0, 2, 15, 0); + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state1 = btc8723b2ant_wifi_rssi_state(btcoexist, 1, 2, + tmp, 0); + tmp = BT_8723B_2ANT_BT_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; + bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, tmp, 0); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); + if (BTC_RSSI_HIGH(bt_rssi_state)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + + if (BTC_RSSI_HIGH(wifi_rssi_state1) && BTC_RSSI_HIGH(bt_rssi_state)) { + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + } else { + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 13); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_LPS_ON, 0x50, + 0x4); + } btc8723b2ant_tdma_duration_adjust(btcoexist, false, true, 2); @@ -2848,12 +2959,17 @@ static void btc8723b2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) static void btc8723b2ant_action_pan_edr(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; u32 wifi_bw; + u8 tmp = BT_8723B_2ANT_WIFI_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; - wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, - 0, 2, 15, 0); - bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, 29, 0); + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state1 = btc8723b2ant_wifi_rssi_state(btcoexist, 1, 2, + tmp, 0); + tmp = BT_8723B_2ANT_BT_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; + bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, tmp, 0); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); @@ -2865,7 +2981,15 @@ static void btc8723b2ant_action_pan_edr(struct btc_coexist *btcoexist) else btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); - btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 10); + if (BTC_RSSI_HIGH(wifi_rssi_state1) && BTC_RSSI_HIGH(bt_rssi_state)) { + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 10); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + } else { + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 13); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_LPS_ON, 0x50, + 0x4); + } if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) @@ -2899,20 +3023,25 @@ static void btc8723b2ant_action_pan_edr(struct btc_coexist *btcoexist) /* PAN(HS) only */ static void btc8723b2ant_action_pan_hs(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; u32 wifi_bw; + u8 tmp = BT_8723B_2ANT_WIFI_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; - wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, - 0, 2, 15, 0); + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state1 = btc8723b2ant_wifi_rssi_state(btcoexist, 1, 2, + tmp, 0); + tmp = BT_8723B_2ANT_BT_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; + bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, tmp, 0); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || - (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) - btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + if (BTC_RSSI_HIGH(bt_rssi_state)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); else btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); @@ -2944,12 +3073,17 @@ static void btc8723b2ant_action_pan_hs(struct btc_coexist *btcoexist) /* PAN(EDR) + A2DP */ static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; u32 wifi_bw; + u8 tmp = BT_8723B_2ANT_WIFI_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; - wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, - 0, 2, 15, 0); - bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, 29, 0); + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state1 = btc8723b2ant_wifi_rssi_state(btcoexist, 1, 2, + tmp, 0); + tmp = BT_8723B_2ANT_BT_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; + bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, tmp, 0); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); @@ -2961,6 +3095,12 @@ static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) else btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + if (BTC_RSSI_HIGH(wifi_rssi_state1) && BTC_RSSI_HIGH(bt_rssi_state)) + btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + else + btc8723b2ant_power_save_state(btcoexist, BTC_PS_LPS_ON, 0x50, + 0x4); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || @@ -3001,12 +3141,17 @@ static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) static void btc8723b2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; u32 wifi_bw; - - wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, - 0, 2, 15, 0); - bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, 29, 0); + u8 tmp = BT_8723B_2ANT_WIFI_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; + + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state1 = btc8723b2ant_wifi_rssi_state(btcoexist, 1, 2, + tmp, 0); + tmp = BT_8723B_2ANT_BT_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; + bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, tmp, 0); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); @@ -3016,27 +3161,32 @@ static void btc8723b2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) else btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + if (BTC_RSSI_HIGH(wifi_rssi_state1) && BTC_RSSI_HIGH(bt_rssi_state)) { + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + } else { + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 14); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_LPS_ON, 0x50, + 0x4); + } + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { if (BTC_WIFI_BW_HT40 == wifi_bw) { btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 3); - btc8723b2ant_coex_table_with_type(btcoexist, - NORMAL_EXEC, 11); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x780); } else { btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8723b2ant_coex_table_with_type(btcoexist, - NORMAL_EXEC, 7); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); } btc8723b2ant_tdma_duration_adjust(btcoexist, true, false, 2); } else { btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 11); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); btc8723b2ant_tdma_duration_adjust(btcoexist, true, true, 2); @@ -3067,12 +3217,17 @@ static void btc8723b2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) /* HID + A2DP + PAN(EDR) */ static void btc8723b2ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; u32 wifi_bw; + u8 tmp = BT_8723B_2ANT_WIFI_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; - wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, - 0, 2, 15, 0); - bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, 29, 0); + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state1 = btc8723b2ant_wifi_rssi_state(btcoexist, 1, 2, + tmp, 0); + tmp = BT_8723B_2ANT_BT_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; + bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, tmp, 0); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); @@ -3084,9 +3239,18 @@ static void btc8723b2ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) else btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + if (BTC_RSSI_HIGH(wifi_rssi_state1) && BTC_RSSI_HIGH(bt_rssi_state)) { + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + } else { + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 14); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_LPS_ON, 0x50, + 0x4); + } + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { @@ -3124,12 +3288,18 @@ static void btc8723b2ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) static void btc8723b2ant_action_hid_a2dp(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; u32 wifi_bw; + u8 ap_num = 0; + u8 tmp = BT_8723B_2ANT_WIFI_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; - wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, - 0, 2, 15, 0); - bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, 29, 0); + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state1 = btc8723b2ant_wifi_rssi_state(btcoexist, 1, 2, + tmp, 0); + tmp = BT_8723B_2ANT_BT_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset; + bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 3, tmp, 37); btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); @@ -3138,13 +3308,61 @@ static void btc8723b2ant_action_hid_a2dp(struct btc_coexist *btcoexist) btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); + if (wifi_bw == BTC_WIFI_BW_LEGACY) { + if (BTC_RSSI_HIGH(bt_rssi_state)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + else if (BTC_RSSI_MEDIUM(bt_rssi_state)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + } else { + /* only 802.11N mode we have to dec bt power to 4 degree */ + if (BTC_RSSI_HIGH(bt_rssi_state)) { + /* need to check ap Number of Not */ + if (ap_num < 10) + btc8723b2ant_dec_bt_pwr(btcoexist, + NORMAL_EXEC, 4); + else + btc8723b2ant_dec_bt_pwr(btcoexist, + NORMAL_EXEC, 2); + } else if (BTC_RSSI_MEDIUM(bt_rssi_state)) { + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + } else { + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + } + } - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) - btc8723b2ant_tdma_duration_adjust(btcoexist, true, false, 2); - else - btc8723b2ant_tdma_duration_adjust(btcoexist, true, true, 2); + if (BTC_RSSI_HIGH(wifi_rssi_state1) && BTC_RSSI_HIGH(bt_rssi_state)) { + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + } else { + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 14); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_LPS_ON, 0x50, + 0x4); + } + + if (BTC_RSSI_HIGH(bt_rssi_state)) { + if (ap_num < 10) + btc8723b2ant_tdma_duration_adjust(btcoexist, true, + false, 1); + else + btc8723b2ant_tdma_duration_adjust(btcoexist, true, + false, 3); + } else { + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 18); + btcoexist->btc_write_1byte(btcoexist, 0x456, 0x38); + btcoexist->btc_write_2byte(btcoexist, 0x42a, 0x0808); + btcoexist->btc_write_4byte(btcoexist, 0x430, 0x0); + btcoexist->btc_write_4byte(btcoexist, 0x434, 0x01010000); + + if (ap_num < 10) + btc8723b2ant_tdma_duration_adjust(btcoexist, true, + true, 1); + else + btc8723b2ant_tdma_duration_adjust(btcoexist, true, + true, 3); + } /* sw mechanism */ if (BTC_WIFI_BW_HT40 == wifi_bw) { diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h index f5e091ff4b59..2c2113482481 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h @@ -41,6 +41,11 @@ #define BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT 2 +/* WiFi RSSI Threshold for 2-Ant TDMA/1-Ant PS-TDMA translation */ +#define BT_8723B_2ANT_WIFI_RSSI_COEXSWITCH_THRES 42 +/* BT RSSI Threshold for 2-Ant TDMA/1-Ant PS-TDMA translation */ +#define BT_8723B_2ANT_BT_RSSI_COEXSWITCH_THRES 46 + enum BT_INFO_SRC_8723B_2ANT { BT_INFO_SRC_8723B_2ANT_WIFI_FW = 0x0, BT_INFO_SRC_8723B_2ANT_BT_RSP = 0x1, @@ -127,6 +132,8 @@ struct coex_dm_8723b_2ant { u8 pre_rpwm; u8 cur_rpwm; + bool is_switch_to_1dot5_ant; + u8 switch_thres_offset; bool need_recover_0x948; u16 backup_0x948; }; -- cgit From 83cded7a1ef075ecebc08d5d92f2891288dbbe30 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Tue, 4 Apr 2017 11:50:06 -0500 Subject: rtlwifi: btcoex: 23b 2ant: set coex table when wifi is linking When wifi is under linking process, those packets are important. Mark them as high priority to protect the linking process Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index e722c7cd1673..c9f04454b19b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1592,6 +1592,27 @@ static void btc8723b2ant_action_bt_inquiry(struct btc_coexist *btcoexist) btc8723b2ant_sw_mechanism(btcoexist, false, false, false, false); } +static void btc8723b2ant_action_wifi_link_process(struct btc_coexist + *btcoexist) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + u32 u32tmp; + u8 u8tmpa, u8tmpb; + + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 15); + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); + + btc8723b2ant_sw_mechanism(btcoexist, false, false, false, false); + + u32tmp = btcoexist->btc_read_4byte(btcoexist, 0x948); + u8tmpa = btcoexist->btc_read_1byte(btcoexist, 0x765); + u8tmpb = btcoexist->btc_read_1byte(btcoexist, 0x76e); + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], 0x948 = 0x%x, 0x765 = 0x%x, 0x76e = 0x%x\n", + u32tmp, u8tmpa, u8tmpb); +} + static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -3409,6 +3430,7 @@ static void btc8723b2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) u32 wifi_link_status = 0; struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; bool miracast_plus_bt = false; + bool scan = false, link = false, roam = false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], RunCoexistMechanism()===>\n"); @@ -3440,6 +3462,17 @@ static void btc8723b2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) } } + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_ROAM, &roam); + + if (scan || link || roam) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], WiFi is under Link Process !!\n"); + btc8723b2ant_action_wifi_link_process(btcoexist); + return; + } + /* for P2P */ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_LINK_STATUS, &wifi_link_status); -- cgit From 470eec1a3d0ab2087d42b1b0a9dd1d4c04b1824f Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 6 Apr 2017 15:19:48 -0500 Subject: rtlwifi: btcoex: 23b 2ant: set coex table when wifi is idle When wifi is idle, the bt should have more resource to transmit. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 61 ++++++++++++++++------ 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index c9f04454b19b..327609e3a309 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1613,6 +1613,47 @@ static void btc8723b2ant_action_wifi_link_process(struct btc_coexist u32tmp, u8tmpa, u8tmpb); } +static bool btc8723b2ant_action_wifi_idle_process(struct btc_coexist *btcoexist) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; + u8 ap_num = 0; + u8 tmp = BT_8723B_2ANT_WIFI_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset - coex_dm->switch_thres_offset; + + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state1 = btc8723b2ant_wifi_rssi_state(btcoexist, 1, 2, + tmp, 0); + tmp = BT_8723B_2ANT_BT_RSSI_COEXSWITCH_THRES - + coex_dm->switch_thres_offset - coex_dm->switch_thres_offset; + bt_rssi_state = btc8723b2ant_bt_rssi_state(btcoexist, 2, tmp, 0); + + btcoexist->btc_get(btcoexist, BTC_GET_U1_AP_NUM, &ap_num); + + /* office environment */ + if (BTC_RSSI_HIGH(wifi_rssi_state1) && (coex_sta->hid_exist) && + (coex_sta->a2dp_exist)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], Wifi idle process for BT HID+A2DP exist!!\n"); + + btc8723b2ant_dac_swing(btcoexist, NORMAL_EXEC, true, 0x6); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + + /* sw all off */ + btc8723b2ant_sw_mechanism(btcoexist, false, false, false, + false); + btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); + btc8723b2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + + return true; + } + + btc8723b2ant_dac_swing(btcoexist, NORMAL_EXEC, true, 0x18); + return false; +} + static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -1710,26 +1751,12 @@ static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) "[BTCoex], Wifi Connected-Busy + BT Busy!!\n"); common = false; } else { - if (bt_hs_on) - return false; - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi Connected-Idle + BT Busy!!\n"); - btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, - 0x1, 0xfffff, 0x0); - btc8723b2ant_coex_table_with_type(btcoexist, - NORMAL_EXEC, - 7); - btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 21); - btc8723b2ant_fw_dac_swing_lvl(btcoexist, - NORMAL_EXEC, - 0xb); - btc8723b2ant_sw_mechanism(btcoexist, false, - false, false, - false); - common = true; + common = + btc8723b2ant_action_wifi_idle_process( + btcoexist); } } } -- cgit From 2037b83c12d1535cab7e73181c34e353ffb94700 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 6 Apr 2017 15:19:49 -0500 Subject: rtlwifi: btcoex: 23b 2ant: treat too many low prio packets as retry Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 327609e3a309..82a5e5357cb7 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1884,6 +1884,11 @@ static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, } else { /*accquire the BT TRx retry count from BT_Info byte2*/ retry_count = coex_sta->bt_retry_cnt; + + if ((coex_sta->low_priority_tx) > 1050 || + (coex_sta->low_priority_rx) > 1250) + retry_count++; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], retry_count = %d\n", retry_count); RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, -- cgit From acb9c779ecc56c27a15f7ef0400d0509802357b6 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 6 Apr 2017 15:19:50 -0500 Subject: rtlwifi: btcoex: 23b 2ant: remove debugging code for 0x948 Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 6 ------ drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h | 2 -- 2 files changed, 8 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 82a5e5357cb7..9c169d094b51 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -3486,12 +3486,6 @@ static void btc8723b2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) "[BTCoex], BT is under inquiry/page scan !!\n"); btc8723b2ant_action_bt_inquiry(btcoexist); return; - } else { - if (coex_dm->need_recover_0x948) { - coex_dm->need_recover_0x948 = false; - btcoexist->btc_write_2byte(btcoexist, 0x948, - coex_dm->backup_0x948); - } } btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan); diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h index 2c2113482481..9e29f85304cc 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h @@ -134,8 +134,6 @@ struct coex_dm_8723b_2ant { bool is_switch_to_1dot5_ant; u8 switch_thres_offset; - bool need_recover_0x948; - u16 backup_0x948; }; struct coex_sta_8723b_2ant { -- cgit From ae889ebc76c95892dc7b10f15bf7a5cb9e1c6684 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 6 Apr 2017 15:19:51 -0500 Subject: rtlwifi: btcoex: 23b 2ant: need those information when scan For scan notify, we need to supervise some registers to make sure that coexistence is operating as we expected. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 9c169d094b51..93dbbc507ba1 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -3934,6 +3934,12 @@ void ex_btc8723b2ant_lps_notify(struct btc_coexist *btcoexist, u8 type) void ex_btc8723b2ant_scan_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; + u32 u32tmp; + u8 u8tmpa, u8tmpb; + + u32tmp = btcoexist->btc_read_4byte(btcoexist, 0x948); + u8tmpa = btcoexist->btc_read_1byte(btcoexist, 0x765); + u8tmpb = btcoexist->btc_read_1byte(btcoexist, 0x76e); if (BTC_SCAN_START == type) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -3943,6 +3949,10 @@ void ex_btc8723b2ant_scan_notify(struct btc_coexist *btcoexist, u8 type) "[BTCoex], SCAN FINISH notify\n"); btcoexist->btc_get(btcoexist, BTC_GET_U1_AP_NUM, &coex_sta->scan_ap_num); + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "############# [BTCoex], 0x948=0x%x, 0x765=0x%x, 0x76e=0x%x\n", + u32tmp, u8tmpa, u8tmpb); } void ex_btc8723b2ant_connect_notify(struct btc_coexist *btcoexist, u8 type) -- cgit From 7558668d05366b5747cd5223e87913083450b8bf Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 6 Apr 2017 15:19:52 -0500 Subject: rtlwifi: btcoex: 23b 2ant: wifi is not actually off in mp mode In mp mode, the wifi will not turn off and still has control of the PTA, so the driver needs to distinguish whether it is mp mode or not Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 10 ++++++++++ drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h | 1 + 2 files changed, 11 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 93dbbc507ba1..70f21059ddc3 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -3606,6 +3606,7 @@ static void btc8723b2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) static void btc8723b2ant_wifioff_hwcfg(struct btc_coexist *btcoexist) { + bool is_in_mp_mode = false; u8 h2c_parameter[2] = {0}; u32 fw_ver = 0; @@ -3623,6 +3624,15 @@ static void btc8723b2ant_wifioff_hwcfg(struct btc_coexist *btcoexist) } else { btcoexist->btc_write_1byte(btcoexist, 0x765, 0x18); } + + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_IS_IN_MP_MODE, + &is_in_mp_mode); + if (!is_in_mp_mode) + /* BT select s0/s1 is controlled by BT */ + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x67, 0x20, 0x0); + else + /* BT select s0/s1 is controlled by WiFi */ + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x67, 0x20, 0x1); } /********************************************************************* diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h index 022658cafbca..0b90e7f0b722 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h @@ -205,6 +205,7 @@ enum btc_get_type { BTC_GET_BL_WIFI_ENABLE_ENCRYPTION, BTC_GET_BL_WIFI_UNDER_B_MODE, BTC_GET_BL_EXT_SWITCH, + BTC_GET_BL_WIFI_IS_IN_MP_MODE, /* type s4Byte */ BTC_GET_S4_WIFI_RSSI, -- cgit From 4e6becc0448b936c24bccacf39a94f8a1cf81e21 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 6 Apr 2017 15:19:53 -0500 Subject: rtlwifi: btcoex: 23b 2ant: power on settings for coex When power on, the wifi could be initiating, force the antenna to transmit bt packets to avoid bt unstable problems Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 31 ++++++++++++++++++++++ .../realtek/rtlwifi/btcoexist/halbtcoutsrc.h | 1 + 2 files changed, 32 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 70f21059ddc3..0f3d04529ff2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -3669,6 +3669,37 @@ void ex_btc8723b2ant_init_hwconfig(struct btc_coexist *btcoexist) btcoexist->btc_write_1byte_bitmask(btcoexist, 0x40, 0x20, 0x1); } +void ex_btc8723b2ant_power_on_setting(struct btc_coexist *btcoexist) +{ + struct btc_board_info *board_info = &btcoexist->board_info; + u16 u16tmp = 0x0; + u32 value = 0; + + btcoexist->btc_write_1byte(btcoexist, 0x67, 0x20); + + /* enable BB, REG_SYS_FUNC_EN such that we can write 0x948 correctly */ + u16tmp = btcoexist->btc_read_2byte(btcoexist, 0x2); + btcoexist->btc_write_2byte(btcoexist, 0x2, u16tmp | BIT0 | BIT1); + + btcoexist->btc_write_4byte(btcoexist, 0x948, 0x0); + + if (btcoexist->chip_interface == BTC_INTF_USB) { + /* fixed at S0 for USB interface */ + board_info->btdm_ant_pos = BTC_ANTENNA_AT_AUX_PORT; + } else { + /* for PCIE and SDIO interface, we check efuse 0xc3[6] */ + if (board_info->single_ant_path == 0) { + /* set to S1 */ + board_info->btdm_ant_pos = BTC_ANTENNA_AT_MAIN_PORT; + } else if (board_info->single_ant_path == 1) { + /* set to S0 */ + board_info->btdm_ant_pos = BTC_ANTENNA_AT_AUX_PORT; + } + btcoexist->btc_set(btcoexist, BTC_SET_ACT_ANTPOSREGRISTRY_CTRL, + &value); + } +} + void ex_btc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h index 0b90e7f0b722..5a34cd895e44 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h @@ -139,6 +139,7 @@ struct btc_board_info { u8 pg_ant_num; /* pg ant number */ u8 btdm_ant_num; /* ant number for btdm */ u8 btdm_ant_pos; + u8 single_ant_path; /* current used for 8723b only, 1=>s0, 0=>s1 */ bool bt_exist; }; -- cgit From 6dcf041eabd1136abc46574092985acc86be99c0 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 6 Apr 2017 15:19:54 -0500 Subject: rtlwifi: btcoex: 23b 2ant: before firmware ready settings Before firmware is ready, set GNT_BT to high to let bt transmit Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 38 ++++++++++++++++++++++ .../realtek/rtlwifi/btcoexist/halbtcoutsrc.h | 2 ++ 2 files changed, 40 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 0f3d04529ff2..06f70944bdac 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -3700,6 +3700,44 @@ void ex_btc8723b2ant_power_on_setting(struct btc_coexist *btcoexist) } } +void ex_btc8723b2ant_pre_load_firmware(struct btc_coexist *btcoexist) +{ + struct btc_board_info *board_info = &btcoexist->board_info; + u8 u8tmp = 0x4; /* Set BIT2 by default since it's 2ant case */ + + /** + * S0 or S1 setting and Local register setting(By this fw can get + * ant number, S0/S1, ... info) + * + * Local setting bit define + * BIT0: "0" : no antenna inverse; "1" : antenna inverse + * BIT1: "0" : internal switch; "1" : external switch + * BIT2: "0" : one antenna; "1" : two antennas + * + * NOTE: here default all internal switch and 1-antenna ==> BIT1=0 and + * BIT2 = 0 + */ + if (btcoexist->chip_interface == BTC_INTF_USB) { + /* fixed at S0 for USB interface */ + u8tmp |= 0x1; /* antenna inverse */ + btcoexist->btc_write_local_reg_1byte(btcoexist, 0xfe08, u8tmp); + } else { + /* for PCIE and SDIO interface, we check efuse 0xc3[6] */ + if (board_info->single_ant_path == 0) { + } else if (board_info->single_ant_path == 1) { + /* set to S0 */ + u8tmp |= 0x1; /* antenna inverse */ + } + + if (btcoexist->chip_interface == BTC_INTF_PCI) + btcoexist->btc_write_local_reg_1byte(btcoexist, 0x384, + u8tmp); + else if (btcoexist->chip_interface == BTC_INTF_SDIO) + btcoexist->btc_write_local_reg_1byte(btcoexist, 0x60, + u8tmp); + } +} + void ex_btc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h index 5a34cd895e44..d31e6df5b1bf 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h @@ -369,6 +369,7 @@ typedef void (*bfp_btc_w2)(void *btc_context, u32 reg_addr, u16 data); typedef void (*bfp_btc_w4)(void *btc_context, u32 reg_addr, u32 data); +typedef void (*bfp_btc_local_reg_w1)(void *btc_context, u32 reg_addr, u8 data); typedef void (*bfp_btc_wr_1byte_bit_mask)(void *btc_context, u32 reg_addr, u8 bit_mask, u8 data); @@ -496,6 +497,7 @@ struct btc_coexist { bfp_btc_w2 btc_write_2byte; bfp_btc_r4 btc_read_4byte; bfp_btc_w4 btc_write_4byte; + bfp_btc_local_reg_w1 btc_write_local_reg_1byte; bfp_btc_set_bb_reg btc_set_bb_reg; bfp_btc_get_bb_reg btc_get_bb_reg; -- cgit From 2e6689824a4c6d4275662f989c65a529c6f7a9f2 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 6 Apr 2017 15:19:55 -0500 Subject: rtlwifi: btcoex: 23b 2ant: fine tune for bt pan_edr_a2dp If we don't limit the rx aggregation size, and set tdma instead, the bt profile can get more stable. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 06f70944bdac..a76c8d20e093 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -3140,7 +3140,6 @@ static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); - btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) @@ -3167,7 +3166,7 @@ static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) false, 3); } else { btc8723b2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); - btc8723b2ant_tdma_duration_adjust(btcoexist, false, true, 3); + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); } /* sw mechanism */ -- cgit From 110944027627071cab79c968dbb8dfec3b337b54 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 6 Apr 2017 15:19:56 -0500 Subject: rtlwifi: btcoex: 23b 2ant: fine tune for bt hid_a2dp Let bt control the aggregation size to improve stability. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index a76c8d20e093..9092de2d2c7c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -3355,7 +3355,7 @@ static void btc8723b2ant_action_hid_a2dp(struct btc_coexist *btcoexist) btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); - btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + btc8723b2ant_limited_rx(btcoexist, NORMAL_EXEC, false, true, 0x5); btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); -- cgit From 588a290b4642de9996a83d95474528f08ffe882a Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 6 Apr 2017 15:19:57 -0500 Subject: rtlwifi: btcoex: 23b 2ant: notify more bt information These bt information are displayed in display coex Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 20 ++++++++++++++++++++ .../realtek/rtlwifi/btcoexist/halbtc8723b2ant.h | 2 ++ .../realtek/rtlwifi/btcoexist/halbtcoutsrc.h | 15 +++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 9092de2d2c7c..1c823a71ce9f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -4145,9 +4145,19 @@ void ex_btc8723b2ant_bt_info_notify(struct btc_coexist *btcoexist, coex_sta->bt_retry_cnt = coex_sta->bt_info_c2h[rsp_source][2] & 0xf; + if (coex_sta->bt_retry_cnt >= 1) + coex_sta->pop_event_cnt++; + coex_sta->bt_rssi = coex_sta->bt_info_c2h[rsp_source][3] * 2 + 10; + coex_sta->bt_info_ext = coex_sta->bt_info_c2h[rsp_source][4]; + + if (coex_sta->bt_info_c2h[rsp_source][2] & 0x20) + coex_sta->c2h_bt_remote_name_req = true; + else + coex_sta->c2h_bt_remote_name_req = false; + if (coex_sta->bt_info_c2h[rsp_source][1] == 0x49) coex_sta->a2dp_bit_pool = coex_sta->bt_info_c2h[rsp_source][6]; @@ -4222,6 +4232,16 @@ void ex_btc8723b2ant_bt_info_notify(struct btc_coexist *btcoexist, coex_sta->sco_exist = true; else coex_sta->sco_exist = false; + + if ((!coex_sta->hid_exist) && + (!coex_sta->c2h_bt_inquiry_page) && + (!coex_sta->sco_exist)) { + if (coex_sta->high_priority_tx + + coex_sta->high_priority_rx >= 160) { + coex_sta->hid_exist = true; + bt_info = bt_info | 0x28; + } + } } btc8723b2ant_update_bt_link_info(btcoexist); diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h index 9e29f85304cc..18a35c7faba9 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.h @@ -151,12 +151,14 @@ struct coex_sta_8723b_2ant { u32 low_priority_tx; u32 low_priority_rx; u8 bt_rssi; + bool bt_tx_rx_mask; u8 pre_bt_rssi_state; u8 pre_wifi_rssi_state[4]; bool c2h_bt_info_req_sent; u8 bt_info_c2h[BT_INFO_SRC_8723B_2ANT_MAX][10]; u32 bt_info_c2h_cnt[BT_INFO_SRC_8723B_2ANT_MAX]; bool c2h_bt_inquiry_page; + bool c2h_bt_remote_name_req; u8 bt_retry_cnt; u8 bt_info_ext; u32 pop_event_cnt; diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h index d31e6df5b1bf..c8271135aaaa 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h @@ -66,6 +66,15 @@ #define BTC_ANT_WIFI_AT_CPL_MAIN 0 #define BTC_ANT_WIFI_AT_CPL_AUX 1 +enum btc_bt_reg_type { + BTC_BT_REG_RF = 0, + BTC_BT_REG_MODEM = 1, + BTC_BT_REG_BLUEWIZE = 2, + BTC_BT_REG_VENDOR = 3, + BTC_BT_REG_LE = 4, + BTC_BT_REG_MAX +}; + enum btc_chip_interface { BTC_INTF_UNKNOWN = 0, BTC_INTF_PCI = 1, @@ -251,6 +260,7 @@ enum btc_set_type { BTC_SET_BL_TO_REJ_AP_AGG_PKT, BTC_SET_BL_BT_CTRL_AGG_SIZE, BTC_SET_BL_INC_SCAN_DEV_NUM, + BTC_SET_BL_BT_TX_RX_MASK, BTC_SET_BL_MIRACAST_PLUS_BT, /* type u1Byte */ @@ -392,6 +402,9 @@ typedef bool (*bfp_btc_get)(void *btcoexist, u8 get_type, void *out_buf); typedef bool (*bfp_btc_set)(void *btcoexist, u8 set_type, void *in_buf); +typedef void (*bfp_btc_set_bt_reg)(void *btc_context, u8 reg_type, u32 offset, + u32 value); + typedef void (*bfp_btc_disp_dbg_msg)(void *btcoexist, u8 disp_type); struct btc_bt_info { @@ -511,6 +524,8 @@ struct btc_coexist { bfp_btc_get btc_get; bfp_btc_set btc_set; + + bfp_btc_set_bt_reg btc_set_bt_reg; }; bool halbtc_is_wifi_uplink(struct rtl_priv *adapter); -- cgit From f4a23e194ef370baddf3b3ea74b418fa63f08869 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 6 Apr 2017 15:19:58 -0500 Subject: rtlwifi: btcoex: 23b 2ant: some hi-prio pkt will cause hid_exist Clear the hid_exist flag by monitoring the packet counter. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index 1c823a71ce9f..2f3946be4ce2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -4329,6 +4329,7 @@ void ex_btc8723b2ant_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state) void ex_btc8723b2ant_periodical(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], ==========================Periodical===========================\n"); @@ -4352,6 +4353,13 @@ void ex_btc8723b2ant_periodical(struct btc_coexist *btcoexist) btc8723b2ant_monitor_bt_ctr(btcoexist); btc8723b2ant_monitor_wifi_ctr(btcoexist); + /* for some BT speakers that High-Priority pkts appear before + * playing, this will cause HID exist + */ + if ((coex_sta->high_priority_tx + coex_sta->high_priority_rx < 50) && + (bt_link_info->hid_exist)) + bt_link_info->hid_exist = false; + if (btc8723b2ant_is_wifi_status_changed(btcoexist) || coex_dm->auto_tdma_adjust) btc8723b2ant_run_coexist_mechanism(btcoexist); -- cgit From 1bdd83392f7f7719e99bf8d2c0ead60639ce6f3b Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:22:57 -0500 Subject: rtlwifi: btcoex: 21a 1ant: fw settings for softap mode For ap mode, adjust fw settings to operate properly. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 25 ++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index 6f6ab0738fbb..5b8f4ed5ce62 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -843,18 +843,35 @@ static void btc8821a1ant_set_fw_ps_tdma(struct btc_coexist *btcoexist, u8 byte1, { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[5] = {0}; + u8 real_byte1 = byte1, real_byte5 = byte5; + bool ap_enable = false; - h2c_parameter[0] = byte1; + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_AP_MODE_ENABLE, + &ap_enable); + + if (ap_enable) { + if (byte1 & BIT4 && !(byte1 & BIT5)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], FW for 1Ant AP mode\n"); + real_byte1 &= ~BIT4; + real_byte1 |= BIT5; + + real_byte5 |= BIT5; + real_byte5 &= ~BIT6; + } + } + + h2c_parameter[0] = real_byte1; h2c_parameter[1] = byte2; h2c_parameter[2] = byte3; h2c_parameter[3] = byte4; - h2c_parameter[4] = byte5; + h2c_parameter[4] = real_byte5; - coex_dm->ps_tdma_para[0] = byte1; + coex_dm->ps_tdma_para[0] = real_byte1; coex_dm->ps_tdma_para[1] = byte2; coex_dm->ps_tdma_para[2] = byte3; coex_dm->ps_tdma_para[3] = byte4; - coex_dm->ps_tdma_para[4] = byte5; + coex_dm->ps_tdma_para[4] = real_byte5; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], PS-TDMA H2C cmd =0x%x%08x\n", -- cgit From 19baccc4561fc7dc18ff79e14bdfa86bd0b34abc Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:22:58 -0500 Subject: rtlwifi: btcoex: 21a 1ant: add function to check wifi status This function checks if wifi has changed its statusi. It will be needed in the future. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index 5b8f4ed5ce62..65c455bfd8fb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -424,6 +424,39 @@ static void btc8821a1ant_query_bt_info(struct btc_coexist *btcoexist) btcoexist->btc_fill_h2c(btcoexist, 0x61, 1, h2c_parameter); } +bool btc8821a1ant_is_wifi_status_changed(struct btc_coexist *btcoexist) +{ + static bool pre_wifi_busy = true; + static bool pre_under_4way = true; + static bool pre_bt_hs_on = true; + bool wifi_busy = false, under_4way = false, bt_hs_on = false; + bool wifi_connected = false; + + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, + &wifi_connected); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy); + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_4_WAY_PROGRESS, + &under_4way); + + if (wifi_connected) { + if (wifi_busy != pre_wifi_busy) { + pre_wifi_busy = wifi_busy; + return true; + } + if (under_4way != pre_under_4way) { + pre_under_4way = under_4way; + return true; + } + if (bt_hs_on != pre_bt_hs_on) { + pre_bt_hs_on = bt_hs_on; + return true; + } + } + + return false; +} + static void btc8821a1ant_update_bt_link_info(struct btc_coexist *btcoexist) { struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; -- cgit From 3121b4ddd1817b7177c0747835e652ef9b733cd9 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:22:59 -0500 Subject: rtlwifi: btcoex: 21a 1ant: coex table setting for new fw For newer fw, the coex table setting needs to be modified to operate correctly. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index 65c455bfd8fb..3a117eaf1b21 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -806,24 +806,24 @@ static void btc8821a1ant_coex_table_with_type(struct btc_coexist *btcoexist, 0x5a5a5a5a, 0xffffff, 0x3); break; case 3: - btc8821a1ant_coex_table(btcoexist, force_exec, 0x55555555, + btc8821a1ant_coex_table(btcoexist, force_exec, 0x5a5a5a5a, 0xaaaaaaaa, 0xffffff, 0x3); break; case 4: - btc8821a1ant_coex_table(btcoexist, force_exec, 0xffffffff, - 0xffffffff, 0xffffff, 0x3); + btc8821a1ant_coex_table(btcoexist, force_exec, 0x55555555, + 0x5a5a5a5a, 0xffffff, 0x3); break; case 5: - btc8821a1ant_coex_table(btcoexist, force_exec, 0x5fff5fff, - 0x5fff5fff, 0xffffff, 0x3); + btc8821a1ant_coex_table(btcoexist, force_exec, 0x5a5a5a5a, + 0xaaaa5a5a, 0xffffff, 0x3); break; case 6: - btc8821a1ant_coex_table(btcoexist, force_exec, 0x55ff55ff, - 0x5a5a5a5a, 0xffffff, 0x3); + btc8821a1ant_coex_table(btcoexist, force_exec, 0x55555555, + 0xaaaa5a5a, 0xffffff, 0x3); break; case 7: - btc8821a1ant_coex_table(btcoexist, force_exec, 0x5afa5afa, - 0x5afa5afa, 0xffffff, 0x3); + btc8821a1ant_coex_table(btcoexist, force_exec, 0xaaaaaaaa, + 0xaaaaaaaa, 0xffffff, 0x3); break; default: break; -- cgit From f0c40cf09dd12e8ebd1a05695d41eacef007076e Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:23:00 -0500 Subject: rtlwifi: btcoex: 21a 1ant: mask profile bit for connect-ilde Mask out the connect-idle bit. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index 3a117eaf1b21..88bf70f27669 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -2606,7 +2606,12 @@ void ex_btc8821a1ant_bt_info_notify(struct btc_coexist *btcoexist, btc8821a1ant_update_bt_link_info(btcoexist); - if (!(bt_info&BT_INFO_8821A_1ANT_B_CONNECTION)) { + /* mask profile bit for connect-ilde identification + * (for CSR case: A2DP idle --> 0x41) + */ + bt_info = bt_info & 0x1f; + + if (!(bt_info & BT_INFO_8821A_1ANT_B_CONNECTION)) { coex_dm->bt_status = BT_8821A_1ANT_BT_STATUS_NON_CONNECTED_IDLE; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], BtInfoNotify(), BT Non-Connected idle!!!\n"); -- cgit From e605103c443eaafdb760aecf00d512f8b6d988f0 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:23:01 -0500 Subject: rtlwifi: btcoex: 21a 1ant: remove setting for 2 antennas The antenna position setting is useless for 1 antenna chip. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index 88bf70f27669..b84c1a5be40a 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -994,9 +994,6 @@ static void btc8821a1ant_set_ant_path(struct btc_coexist *btcoexist, h2c_parameter[1] = 1; btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); - /* Main Ant to BT for IPS case 0x4c[23] = 1 */ - btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, - 0x1, 0x1); } else { /* tell firmware "no antenna inverse" * WRONG firmware antenna control code, need fw to fix @@ -1005,9 +1002,6 @@ static void btc8821a1ant_set_ant_path(struct btc_coexist *btcoexist, h2c_parameter[1] = 1; btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); - /* Aux Ant to BT for IPS case 0x4c[23] = 1 */ - btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, - 0x1, 0x0); } } else if (wifi_off) { /* 0x4c[24:23] = 00, Set Antenna control -- cgit From 4f78287e5ed9ca9326b0df7a7dcf048a7d32be67 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:23:02 -0500 Subject: rtlwifi: btcoex: 21a 1ant: set antenna control path for PTA Set antenna control path if PTA is in control of the packet path of wifi and bt. If wifi is turned off, tell the PTA about it. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index b84c1a5be40a..5b374c7728ba 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -1011,11 +1011,18 @@ static void btc8821a1ant_set_ant_path(struct btc_coexist *btcoexist, u4_tmp &= ~BIT23; u4_tmp &= ~BIT24; btcoexist->btc_write_4byte(btcoexist, 0x4c, u4_tmp); + + /* 0x765 = 0x18 */ + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x765, 0x18, 0x3); + } else { + /* 0x765 = 0x0 */ + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x765, 0x18, 0x0); } /* ext switch setting */ switch (ant_pos_type) { case BTC_ANT_PATH_WIFI: + btcoexist->btc_write_1byte(btcoexist, 0xcb4, 0x77); if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) btcoexist->btc_write_1byte_bitmask(btcoexist, 0xcb7, 0x30, 0x1); @@ -1024,6 +1031,7 @@ static void btc8821a1ant_set_ant_path(struct btc_coexist *btcoexist, 0x30, 0x2); break; case BTC_ANT_PATH_BT: + btcoexist->btc_write_1byte(btcoexist, 0xcb4, 0x77); if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) btcoexist->btc_write_1byte_bitmask(btcoexist, 0xcb7, 0x30, 0x2); @@ -1033,6 +1041,7 @@ static void btc8821a1ant_set_ant_path(struct btc_coexist *btcoexist, break; default: case BTC_ANT_PATH_PTA: + btcoexist->btc_write_1byte(btcoexist, 0xcb4, 0x66); if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) btcoexist->btc_write_1byte_bitmask(btcoexist, 0xcb7, 0x30, 0x1); -- cgit From 06a75324d5436b99c3a727d32b964732835e86a1 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:23:03 -0500 Subject: rtlwifi: btcoex: 21a 1ant: add multi port action for miracast and P2P To support miracast and P2P, the chip may operate under concurrent mode, In this situation, do not aggregate tx packet and properly set the rx aggregation size. We detect it by monitoring the number of link established. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 67 ++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index 5b374c7728ba..cadc0f91af41 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -1478,6 +1478,14 @@ static void btc8821a1ant_action_hid_a2dp(struct btc_coexist *btcoexist) * Non-Software Coex Mechanism start * ***********************************************/ +static +void btc8821a1ant_action_wifi_multi_port(struct btc_coexist *btcoexist) +{ + btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); +} + static void btc8821a1ant_action_hs(struct btc_coexist *btcoexist) { @@ -1820,6 +1828,8 @@ static void btc8821a1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) bool bt_ctrl_agg_buf_size = false; u8 agg_buf_size = 5; u8 wifi_rssi_state = BTC_RSSI_STATE_HIGH; + u32 wifi_link_status = 0; + u32 num_of_wifi_link = 0; bool wifi_under_5g = false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -1862,6 +1872,18 @@ static void btc8821a1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_LINK_STATUS, + &wifi_link_status); + num_of_wifi_link = wifi_link_status >> 16; + if ((num_of_wifi_link >= 2) || + (wifi_link_status & WIFI_P2P_GO_CONNECTED)) { + btc8821a1ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); + btc8821a1ant_limited_rx(btcoexist, NORMAL_EXEC, false, + bt_ctrl_agg_buf_size, agg_buf_size); + btc8821a1ant_action_wifi_multi_port(btcoexist); + return; + } + if (!bt_link_info->sco_exist && !bt_link_info->hid_exist) { btc8821a1ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); } else { @@ -2337,6 +2359,10 @@ void ex_btc8821a1ant_scan_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; bool wifi_connected = false, bt_hs_on = false; + bool bt_ctrl_agg_buf_size = false; + u32 wifi_link_status = 0; + u32 num_of_wifi_link = 0; + u8 agg_buf_size = 5; if (btcoexist->manual_control || btcoexist->stop_coex_dm || @@ -2350,6 +2376,17 @@ void ex_btc8821a1ant_scan_notify(struct btc_coexist *btcoexist, u8 type) btc8821a1ant_query_bt_info(btcoexist); + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_LINK_STATUS, + &wifi_link_status); + num_of_wifi_link = wifi_link_status >> 16; + if (num_of_wifi_link >= 2) { + btc8821a1ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); + btc8821a1ant_limited_rx(btcoexist, NORMAL_EXEC, false, + bt_ctrl_agg_buf_size, agg_buf_size); + btc8821a1ant_action_wifi_multi_port(btcoexist); + return; + } + if (coex_sta->c2h_bt_inquiry_page) { btc8821a1ant_action_bt_inquiry(btcoexist); return; @@ -2384,12 +2421,27 @@ void ex_btc8821a1ant_connect_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; bool wifi_connected = false, bt_hs_on = false; + u32 wifi_link_status = 0; + u32 num_of_wifi_link = 0; + bool bt_ctrl_agg_buf_size = false; + u8 agg_buf_size = 5; if (btcoexist->manual_control || btcoexist->stop_coex_dm || btcoexist->bt_info.bt_disabled) return; + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_LINK_STATUS, + &wifi_link_status); + num_of_wifi_link = wifi_link_status >> 16; + if (num_of_wifi_link >= 2) { + btc8821a1ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); + btc8821a1ant_limited_rx(btcoexist, NORMAL_EXEC, false, + bt_ctrl_agg_buf_size, agg_buf_size); + btc8821a1ant_action_wifi_multi_port(btcoexist); + return; + } + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); if (coex_sta->c2h_bt_inquiry_page) { btc8821a1ant_action_bt_inquiry(btcoexist); @@ -2472,6 +2524,10 @@ void ex_btc8821a1ant_special_packet_notify(struct btc_coexist *btcoexist, { struct rtl_priv *rtlpriv = btcoexist->adapter; bool bt_hs_on = false; + bool bt_ctrl_agg_buf_size = false; + u32 wifi_link_status = 0; + u32 num_of_wifi_link = 0; + u8 agg_buf_size = 5; if (btcoexist->manual_control || btcoexist->stop_coex_dm || @@ -2480,6 +2536,17 @@ void ex_btc8821a1ant_special_packet_notify(struct btc_coexist *btcoexist, coex_sta->special_pkt_period_cnt = 0; + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_LINK_STATUS, + &wifi_link_status); + num_of_wifi_link = wifi_link_status >> 16; + if (num_of_wifi_link >= 2) { + btc8821a1ant_limited_tx(btcoexist, NORMAL_EXEC, 0, 0, 0, 0); + btc8821a1ant_limited_rx(btcoexist, NORMAL_EXEC, false, + bt_ctrl_agg_buf_size, agg_buf_size); + btc8821a1ant_action_wifi_multi_port(btcoexist); + return; + } + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); if (coex_sta->c2h_bt_inquiry_page) { btc8821a1ant_action_bt_inquiry(btcoexist); -- cgit From edf8fa7b66f0e7b74806439581b2b5ae72a8b8d3 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:23:04 -0500 Subject: rtlwifi: btcoex: 21a 1ant: action when associating/authenticating When wifi is associating or authenticating, set the coex table for wifi to establish link. These packets should have higher priority. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 33 ++++++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index cadc0f91af41..05e33d38d49c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -1486,6 +1486,28 @@ void btc8821a1ant_action_wifi_multi_port(struct btc_coexist *btcoexist) btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); } +static +void btc8821a1ant_action_wifi_not_connected_asso_auth( + struct btc_coexist *btcoexist) +{ + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; + + btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, + 0x0); + + /* tdma and coex table */ + if ((bt_link_info->sco_exist) || (bt_link_info->hid_exist)) { + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + } else if ((bt_link_info->a2dp_exist) || (bt_link_info->pan_exist)) { + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 20); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 4); + } else { + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); + } +} + static void btc8821a1ant_action_hs(struct btc_coexist *btcoexist) { @@ -1941,10 +1963,15 @@ static void btc8821a1ant_run_coexist_mechanism(struct btc_coexist *btcoexist) btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_ROAM, &roam); - if (scan || link || roam) - btc8821a1ant_act_wifi_not_conn_scan(btcoexist); - else + if (scan || link || roam) { + if (scan) + btc8821a1ant_act_wifi_not_conn_scan(btcoexist); + else + btc8821a1ant_action_wifi_not_connected_asso_auth( + btcoexist); + } else { btc8821a1ant_action_wifi_not_connected(btcoexist); + } } else { /* wifi LPS/Busy */ btc8821a1ant_action_wifi_connected(btcoexist); -- cgit From bcdffd050c80a72465a8736aacdd614f583a5fb3 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:23:05 -0500 Subject: rtlwifi: btcoex: 21a 1ant: If wifi only, do not initiate coex mechanism If the device has wifi mode only, there is no need to initiate the hardware for wifi and bt coexistence, so just return to avoid it. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index 05e33d38d49c..86eab6a4ada2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -1990,7 +1990,7 @@ static void btc8821a1ant_init_coex_dm(struct btc_coexist *btcoexist) } static void btc8821a1ant_init_hw_config(struct btc_coexist *btcoexist, - bool back_up) + bool back_up, bool wifi_only) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 u1_tmp = 0; @@ -1999,6 +1999,9 @@ static void btc8821a1ant_init_hw_config(struct btc_coexist *btcoexist, RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], 1Ant Init HW Config!!\n"); + if (wifi_only) + return; + if (back_up) { coex_dm->backup_arfr_cnt1 = btcoexist->btc_read_4byte(btcoexist, 0x430); @@ -2039,9 +2042,9 @@ static void btc8821a1ant_init_hw_config(struct btc_coexist *btcoexist, /************************************************************** * extern function start with ex_btc8821a1ant_ **************************************************************/ -void ex_btc8821a1ant_init_hwconfig(struct btc_coexist *btcoexist) +void ex_btc8821a1ant_init_hwconfig(struct btc_coexist *btcoexist, bool wifionly) { - btc8821a1ant_init_hw_config(btcoexist, true); + btc8821a1ant_init_hw_config(btcoexist, true, wifionly); } void ex_btc8821a1ant_init_coex_dm(struct btc_coexist *btcoexist) @@ -2783,7 +2786,7 @@ void ex_btc8821a1ant_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Pnp notify to WAKE UP\n"); btcoexist->stop_coex_dm = false; - btc8821a1ant_init_hw_config(btcoexist, false); + btc8821a1ant_init_hw_config(btcoexist, false, false); btc8821a1ant_init_coex_dm(btcoexist); btc8821a1ant_query_bt_info(btcoexist); } -- cgit From 19afb92222af081597747a2fc51638e36968bf4d Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:23:06 -0500 Subject: rtlwifi: btcoex: 21a 1ant: move bt_disabled to global struct Move the bt disable flag to a global structure to indicate that bt is turned off. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 24 ++++++++++------------ .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.h | 1 + 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index 86eab6a4ada2..c6114b32c0ca 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -2168,7 +2168,7 @@ void ex_btc8821a1ant_display_coex_info(struct btc_coexist *btcoexist) "uplink" : "downlink"))); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = [%s/ %d/ %d] ", "BT [status/ rssi/ retryCnt]", - ((btcoexist->bt_info.bt_disabled) ? ("disabled") : + ((coex_sta->bt_disabled) ? ("disabled") : ((coex_sta->c2h_bt_inquiry_page) ? ("inquiry/page scan") : ((BT_8821A_1ANT_BT_STATUS_NON_CONNECTED_IDLE == coex_dm->bt_status) ? @@ -2394,9 +2394,10 @@ void ex_btc8821a1ant_scan_notify(struct btc_coexist *btcoexist, u8 type) u32 num_of_wifi_link = 0; u8 agg_buf_size = 5; - if (btcoexist->manual_control || - btcoexist->stop_coex_dm || - btcoexist->bt_info.bt_disabled) + if (btcoexist->manual_control || btcoexist->stop_coex_dm) + return; + + if (coex_sta->bt_disabled) return; btcoexist->btc_get(btcoexist, @@ -2456,9 +2457,8 @@ void ex_btc8821a1ant_connect_notify(struct btc_coexist *btcoexist, u8 type) bool bt_ctrl_agg_buf_size = false; u8 agg_buf_size = 5; - if (btcoexist->manual_control || - btcoexist->stop_coex_dm || - btcoexist->bt_info.bt_disabled) + if (btcoexist->manual_control || btcoexist->stop_coex_dm || + coex_sta->bt_disabled) return; btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_LINK_STATUS, @@ -2508,9 +2508,8 @@ void ex_btc8821a1ant_media_status_notify(struct btc_coexist *btcoexist, u32 wifi_bw; u8 wifi_central_chnl; - if (btcoexist->manual_control || - btcoexist->stop_coex_dm || - btcoexist->bt_info.bt_disabled) + if (btcoexist->manual_control || btcoexist->stop_coex_dm || + coex_sta->bt_disabled) return; if (BTC_MEDIA_CONNECT == type) { @@ -2559,9 +2558,8 @@ void ex_btc8821a1ant_special_packet_notify(struct btc_coexist *btcoexist, u32 num_of_wifi_link = 0; u8 agg_buf_size = 5; - if (btcoexist->manual_control || - btcoexist->stop_coex_dm || - btcoexist->bt_info.bt_disabled) + if (btcoexist->manual_control || btcoexist->stop_coex_dm || + coex_sta->bt_disabled) return; coex_sta->special_pkt_period_cnt = 0; diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.h index 9f50a1427388..2dec38ab7746 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.h @@ -140,6 +140,7 @@ struct coex_dm_8821a_1ant { }; struct coex_sta_8821a_1ant { + bool bt_disabled; bool bt_link_exist; bool sco_exist; bool a2dp_exist; -- cgit From ee828085176ed679d1f8ec4c7ca386b73ab2ee51 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:23:07 -0500 Subject: rtlwifi: btcoex: 21a 1ant: consider more cases when bt inquiry With bt inquiry, the wifi may start as a softap or the wifi and bt are busy, we take these scenarios into consider to avoid bt inquiry to degrade the performance of the network Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 75 +++++++++++++++++----- .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.h | 1 + 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index c6114b32c0ca..6b08051614eb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -1139,8 +1139,8 @@ static void btc8821a1ant_ps_tdma(struct btc_coexist *btcoexist, 0x18, 0x0, 0x10); break; case 14: - btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x51, 0x21, - 0x3, 0x10, 0x10); + btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x51, 0x1e, + 0x3, 0x10, 0x14); break; case 15: btc8821a1ant_set_fw_ps_tdma(btcoexist, 0x13, 0xa, @@ -1519,27 +1519,46 @@ static void btc8821a1ant_action_bt_inquiry(struct btc_coexist *btcoexist) { struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; bool wifi_connected = false; + bool ap_enable = false; + bool wifi_busy = false, bt_busy = false; - btcoexist->btc_get(btcoexist, - BTC_GET_BL_WIFI_CONNECTED, &wifi_connected); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, + &wifi_connected); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_AP_MODE_ENABLE, + &ap_enable); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy); + btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_TRAFFIC_BUSY, &bt_busy); - if (!wifi_connected) { + if (!wifi_connected && !coex_sta->wifi_is_high_pri_task) { btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); - btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); - btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); - } else if ((bt_link_info->sco_exist) || + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); + } else if ((bt_link_info->sco_exist) || (bt_link_info->a2dp_exist) || (bt_link_info->hid_only)) { /* SCO/HID-only busy */ btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 32); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 4); + } else if ((bt_link_info->a2dp_exist) && (bt_link_info->hid_exist)) { + /* A2DP+HID busy */ + btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + } else if ((bt_link_info->pan_exist) || (wifi_busy)) { + btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 20); + + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 4); } else { - btc8821a1ant_power_save_state(btcoexist, BTC_PS_LPS_ON, - 0x50, 0x4); - btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 30); - btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); } } @@ -1629,11 +1648,35 @@ void btc8821a1ant_action_wifi_not_connected(struct btc_coexist *btcoexist) static void btc8821a1ant_act_wifi_not_conn_scan(struct btc_coexist *btcoexist) { - btc8821a1ant_power_save_state(btcoexist, - BTC_PS_WIFI_NATIVE, 0x0, 0x0); + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; + + btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); - btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); - btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 1); + /* tdma and coex table */ + if (coex_dm->bt_status == BT_8821A_1ANT_BT_STATUS_ACL_BUSY) { + if (bt_link_info->a2dp_exist) { + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); + btc8821a1ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 1); + } else if (bt_link_info->a2dp_exist && + bt_link_info->pan_exist) { + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); + btc8821a1ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 4); + } else { + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 20); + btc8821a1ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 4); + } + } else if ((coex_dm->bt_status == BT_8821A_1ANT_BT_STATUS_SCO_BUSY) || + (BT_8821A_1ANT_BT_STATUS_ACL_SCO_BUSY == + coex_dm->bt_status)) { + btc8821a1ant_act_bt_sco_hid_only_busy(btcoexist, + BT_8821A_1ANT_WIFI_STATUS_CONNECTED_SCAN); + } else { + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); + } } static diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.h index 2dec38ab7746..1bd1ebe3364e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.h @@ -161,6 +161,7 @@ struct coex_sta_8821a_1ant { u8 bt_info_c2h[BT_INFO_SRC_8821A_1ANT_MAX][10]; u32 bt_info_c2h_cnt[BT_INFO_SRC_8821A_1ANT_MAX]; bool c2h_bt_inquiry_page; + bool wifi_is_high_pri_task; u8 bt_retry_cnt; u8 bt_info_ext; }; -- cgit From cb52b118594703db75f6caace7e318aa18a118ba Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:23:08 -0500 Subject: rtlwifi: btcoex: 21a 1ant: monitor bt profiling when scan When wifi is scanning and not connected, set the tdma and coex table properly to control the priority of the packets to make the wifi bt coexistence operate smoothly Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 29 ++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index 6b08051614eb..eab04c2bdd3d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -1481,9 +1481,34 @@ static void btc8821a1ant_action_hid_a2dp(struct btc_coexist *btcoexist) static void btc8821a1ant_action_wifi_multi_port(struct btc_coexist *btcoexist) { + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; + btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); - btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); - btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); + /* tdma and coex table */ + if (coex_dm->bt_status == BT_8821A_1ANT_BT_STATUS_ACL_BUSY) { + if (bt_link_info->a2dp_exist) { + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); + btc8821a1ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 1); + } else if (bt_link_info->a2dp_exist && + bt_link_info->pan_exist) { + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); + btc8821a1ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 4); + } else { + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 20); + btc8821a1ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 4); + } + } else if ((coex_dm->bt_status == BT_8821A_1ANT_BT_STATUS_SCO_BUSY) || + (BT_8821A_1ANT_BT_STATUS_ACL_SCO_BUSY == + coex_dm->bt_status)) { + btc8821a1ant_act_bt_sco_hid_only_busy(btcoexist, + BT_8821A_1ANT_WIFI_STATUS_CONNECTED_SCAN); + } else { + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 8); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); + } } static -- cgit From da0fd9ccb4325bcc95532d15e553bdb355a56e5f Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:23:09 -0500 Subject: rtlwifi: btcoex: 21a 1ant: do not switch antenna when wifi is under 5G channel When wifi is on a 5G channel, the 5G signal will not interfere bt 2.4G signal, and they can transmit simultaneously, hence there is no need to switch antenna between wifi and bt. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 64 +++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index eab04c2bdd3d..0509ffab9e6f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -44,6 +44,8 @@ static struct coex_dm_8821a_1ant glcoex_dm_8821a_1ant; static struct coex_dm_8821a_1ant *coex_dm = &glcoex_dm_8821a_1ant; static struct coex_sta_8821a_1ant glcoex_sta_8821a_1ant; static struct coex_sta_8821a_1ant *coex_sta = &glcoex_sta_8821a_1ant; +static void btc8821a1ant_act_bt_sco_hid_only_busy(struct btc_coexist *btcoexist, + u8 wifi_status); static const char *const glbt_info_src_8821a_1ant[] = { "BT Info[wifi fw]", @@ -1588,7 +1590,8 @@ static void btc8821a1ant_action_bt_inquiry(struct btc_coexist *btcoexist) } static void btc8821a1ant_act_bt_sco_hid_only_busy(struct btc_coexist *btcoexist, - u8 wifi_status) { + u8 wifi_status) +{ /* tdma and coex table */ btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); @@ -2412,9 +2415,17 @@ void ex_btc8821a1ant_display_coex_info(struct btc_coexist *btcoexist) void ex_btc8821a1ant_ips_notify(struct btc_coexist *btcoexist, u8 type) { struct rtl_priv *rtlpriv = btcoexist->adapter; + bool wifi_under_5g = false; if (btcoexist->manual_control || btcoexist->stop_coex_dm) return; + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g); + if (wifi_under_5g) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], RunCoexistMechanism(), return for 5G <===\n"); + btc8821a1ant_coex_under_5g(btcoexist); + return; + } if (BTC_IPS_ENTER == type) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -2458,12 +2469,20 @@ void ex_btc8821a1ant_scan_notify(struct btc_coexist *btcoexist, u8 type) struct rtl_priv *rtlpriv = btcoexist->adapter; bool wifi_connected = false, bt_hs_on = false; bool bt_ctrl_agg_buf_size = false; + bool wifi_under_5g = false; u32 wifi_link_status = 0; u32 num_of_wifi_link = 0; u8 agg_buf_size = 5; if (btcoexist->manual_control || btcoexist->stop_coex_dm) return; + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g); + if (wifi_under_5g) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], RunCoexistMechanism(), return for 5G <===\n"); + btc8821a1ant_coex_under_5g(btcoexist); + return; + } if (coex_sta->bt_disabled) return; @@ -2523,11 +2542,19 @@ void ex_btc8821a1ant_connect_notify(struct btc_coexist *btcoexist, u8 type) u32 wifi_link_status = 0; u32 num_of_wifi_link = 0; bool bt_ctrl_agg_buf_size = false; + bool wifi_under_5g = false; u8 agg_buf_size = 5; if (btcoexist->manual_control || btcoexist->stop_coex_dm || coex_sta->bt_disabled) return; + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g); + if (wifi_under_5g) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], RunCoexistMechanism(), return for 5G <===\n"); + btc8821a1ant_coex_under_5g(btcoexist); + return; + } btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_LINK_STATUS, &wifi_link_status); @@ -2575,10 +2602,18 @@ void ex_btc8821a1ant_media_status_notify(struct btc_coexist *btcoexist, u8 h2c_parameter[3] = {0}; u32 wifi_bw; u8 wifi_central_chnl; + bool wifi_under_5g = false; if (btcoexist->manual_control || btcoexist->stop_coex_dm || coex_sta->bt_disabled) return; + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g); + if (wifi_under_5g) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], RunCoexistMechanism(), return for 5G <===\n"); + btc8821a1ant_coex_under_5g(btcoexist); + return; + } if (BTC_MEDIA_CONNECT == type) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -2622,6 +2657,7 @@ void ex_btc8821a1ant_special_packet_notify(struct btc_coexist *btcoexist, struct rtl_priv *rtlpriv = btcoexist->adapter; bool bt_hs_on = false; bool bt_ctrl_agg_buf_size = false; + bool wifi_under_5g = false; u32 wifi_link_status = 0; u32 num_of_wifi_link = 0; u8 agg_buf_size = 5; @@ -2630,6 +2666,14 @@ void ex_btc8821a1ant_special_packet_notify(struct btc_coexist *btcoexist, coex_sta->bt_disabled) return; + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g); + if (wifi_under_5g) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], RunCoexistMechanism(), return for 5G <===\n"); + btc8821a1ant_coex_under_5g(btcoexist); + return; + } + coex_sta->special_pkt_period_cnt = 0; btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_LINK_STATUS, @@ -2818,9 +2862,18 @@ void ex_btc8821a1ant_bt_info_notify(struct btc_coexist *btcoexist, void ex_btc8821a1ant_halt_notify(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; + bool wifi_under_5g = false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Halt notify\n"); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g); + if (wifi_under_5g) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], RunCoexistMechanism(), return for 5G <===\n"); + btc8821a1ant_coex_under_5g(btcoexist); + return; + } + btcoexist->stop_coex_dm = true; @@ -2836,6 +2889,15 @@ void ex_btc8821a1ant_halt_notify(struct btc_coexist *btcoexist) void ex_btc8821a1ant_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state) { struct rtl_priv *rtlpriv = btcoexist->adapter; + bool wifi_under_5g = false; + + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g); + if (wifi_under_5g) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], RunCoexistMechanism(), return for 5G <===\n"); + btc8821a1ant_coex_under_5g(btcoexist); + return; + } RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Pnp notify\n"); -- cgit From 4da5e7ea135283ca57be96991d5bca96438c99ad Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Mon, 10 Apr 2017 11:23:10 -0500 Subject: rtlwifi: btcoex: 21a 1ant: avoid LPS/IPS mismatch for pnp notify When driver is going to sleep, it does not leave LPS/IPS, thus the BTCoex may have mismatch when driver wakes up. To avoid that, BTCoex needs to clear the IPS/LPS state when it receives a pnp notify, then it can properly set up the hw when driver wakes up. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c index 0509ffab9e6f..5e9f3b0f7a25 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a1ant.c @@ -2905,11 +2905,18 @@ void ex_btc8821a1ant_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state) if (BTC_WIFI_PNP_SLEEP == pnp_state) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Pnp notify to SLEEP\n"); + /* BT should clear UnderIPS/UnderLPS state to avoid mismatch + * state after wakeup. + */ + coex_sta->under_ips = false; + coex_sta->under_lps = false; btcoexist->stop_coex_dm = true; - btc8821a1ant_ignore_wlan_act(btcoexist, FORCE_EXEC, true); btc8821a1ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); - btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 9); + btc8821a1ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); + btc8821a1ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); + btc8821a1ant_set_ant_path(btcoexist, BTC_ANT_PATH_BT, false, + true); } else if (BTC_WIFI_PNP_WAKE_UP == pnp_state) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Pnp notify to WAKE UP\n"); -- cgit From 1f242a3de702d5a19c479685d35b050837122724 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 10 Apr 2017 15:29:45 +0200 Subject: rt2x00: reverse external PA capability flag logic Consequently refer to external PA instead of inverting the logic and use an internal PA capability flag which is a bit confusing. Currently this is used for Rt3352 only, but MT7620A also allows for an external PA which will be supported by a follow up patch. Signed-off-by: Daniel Golle Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 40 +++++++++++++------------- drivers/net/wireless/ralink/rt2x00/rt2x00.h | 4 +-- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 201b12ed90c6..ba06ac2d876d 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -7014,9 +7014,9 @@ static void rt2800_init_rfcsr_3290(struct rt2x00_dev *rt2x00dev) static void rt2800_init_rfcsr_3352(struct rt2x00_dev *rt2x00dev) { - int tx0_int_pa = test_bit(CAPABILITY_INTERNAL_PA_TX0, + int tx0_ext_pa = test_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags); - int tx1_int_pa = test_bit(CAPABILITY_INTERNAL_PA_TX1, + int tx1_ext_pa = test_bit(CAPABILITY_EXTERNAL_PA_TX1, &rt2x00dev->cap_flags); u8 rfcsr; @@ -7056,9 +7056,9 @@ static void rt2800_init_rfcsr_3352(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 32, 0x80); rt2800_rfcsr_write(rt2x00dev, 33, 0x00); rfcsr = 0x01; - if (!tx0_int_pa) + if (tx0_ext_pa) rt2x00_set_field8(&rfcsr, RFCSR34_TX0_EXT_PA, 1); - if (!tx1_int_pa) + if (tx1_ext_pa) rt2x00_set_field8(&rfcsr, RFCSR34_TX1_EXT_PA, 1); rt2800_rfcsr_write(rt2x00dev, 34, rfcsr); rt2800_rfcsr_write(rt2x00dev, 35, 0x03); @@ -7068,13 +7068,13 @@ static void rt2800_init_rfcsr_3352(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 39, 0xc5); rt2800_rfcsr_write(rt2x00dev, 40, 0x33); rfcsr = 0x52; - if (tx0_int_pa) { + if (!tx0_ext_pa) { rt2x00_set_field8(&rfcsr, RFCSR41_BIT1, 1); rt2x00_set_field8(&rfcsr, RFCSR41_BIT4, 1); } rt2800_rfcsr_write(rt2x00dev, 41, rfcsr); rfcsr = 0x52; - if (tx1_int_pa) { + if (!tx1_ext_pa) { rt2x00_set_field8(&rfcsr, RFCSR42_BIT1, 1); rt2x00_set_field8(&rfcsr, RFCSR42_BIT4, 1); } @@ -7087,19 +7087,19 @@ static void rt2800_init_rfcsr_3352(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 48, 0x14); rt2800_rfcsr_write(rt2x00dev, 49, 0x00); rfcsr = 0x2d; - if (!tx0_int_pa) + if (tx0_ext_pa) rt2x00_set_field8(&rfcsr, RFCSR50_TX0_EXT_PA, 1); - if (!tx1_int_pa) + if (tx1_ext_pa) rt2x00_set_field8(&rfcsr, RFCSR50_TX1_EXT_PA, 1); rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); - rt2800_rfcsr_write(rt2x00dev, 51, (tx0_int_pa ? 0x7f : 0x52)); - rt2800_rfcsr_write(rt2x00dev, 52, (tx0_int_pa ? 0x00 : 0xc0)); - rt2800_rfcsr_write(rt2x00dev, 53, (tx0_int_pa ? 0x52 : 0xd2)); - rt2800_rfcsr_write(rt2x00dev, 54, (tx0_int_pa ? 0x1b : 0xc0)); - rt2800_rfcsr_write(rt2x00dev, 55, (tx1_int_pa ? 0x7f : 0x52)); - rt2800_rfcsr_write(rt2x00dev, 56, (tx1_int_pa ? 0x00 : 0xc0)); - rt2800_rfcsr_write(rt2x00dev, 57, (tx0_int_pa ? 0x52 : 0x49)); - rt2800_rfcsr_write(rt2x00dev, 58, (tx1_int_pa ? 0x1b : 0xc0)); + rt2800_rfcsr_write(rt2x00dev, 51, (tx0_ext_pa ? 0x52 : 0x7f)); + rt2800_rfcsr_write(rt2x00dev, 52, (tx0_ext_pa ? 0xc0 : 0x00)); + rt2800_rfcsr_write(rt2x00dev, 53, (tx0_ext_pa ? 0xd2 : 0x52)); + rt2800_rfcsr_write(rt2x00dev, 54, (tx0_ext_pa ? 0xc0 : 0x1b)); + rt2800_rfcsr_write(rt2x00dev, 55, (tx1_ext_pa ? 0x52 : 0x7f)); + rt2800_rfcsr_write(rt2x00dev, 56, (tx1_ext_pa ? 0xc0 : 0x00)); + rt2800_rfcsr_write(rt2x00dev, 57, (tx0_ext_pa ? 0x49 : 0x52)); + rt2800_rfcsr_write(rt2x00dev, 58, (tx1_ext_pa ? 0xc0 : 0x1b)); rt2800_rfcsr_write(rt2x00dev, 59, 0x00); rt2800_rfcsr_write(rt2x00dev, 60, 0x00); rt2800_rfcsr_write(rt2x00dev, 61, 0x00); @@ -8782,13 +8782,13 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); if (rt2x00_rt(rt2x00dev, RT3352)) { - if (!rt2x00_get_field16(eeprom, + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_TX0_PA_3352)) - __set_bit(CAPABILITY_INTERNAL_PA_TX0, + __set_bit(CAPABILITY_EXTERNAL_PA_TX0, &rt2x00dev->cap_flags); - if (!rt2x00_get_field16(eeprom, + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_TX1_PA_3352)) - __set_bit(CAPABILITY_INTERNAL_PA_TX1, + __set_bit(CAPABILITY_EXTERNAL_PA_TX1, &rt2x00dev->cap_flags); } diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h index 8fdd2f9726ee..1bc353eafe37 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -719,8 +719,8 @@ enum rt2x00_capability_flags { CAPABILITY_DOUBLE_ANTENNA, CAPABILITY_BT_COEXIST, CAPABILITY_VCO_RECALIBRATION, - CAPABILITY_INTERNAL_PA_TX0, - CAPABILITY_INTERNAL_PA_TX1, + CAPABILITY_EXTERNAL_PA_TX0, + CAPABILITY_EXTERNAL_PA_TX1, }; /* -- cgit From 37282485dd4c95c4a637cfa263a0753216f1ec24 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 10 Apr 2017 16:28:04 +0100 Subject: net: mvmdio: disable interrupts in driver failure path When the mvmdio driver has an interrupt, it enables the "done" interrupt after requesting its interrupt handler. However, probe failure results in the interrupt being left enabled. Disable it on the failure path. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvmdio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index a0d1b084ecec..7aea0beca56e 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -251,6 +251,8 @@ static int orion_mdio_probe(struct platform_device *pdev) return 0; out_mdio: + if (dev->err_interrupt > 0) + writel(0, dev->regs + MVMDIO_ERR_INT_MASK); if (!IS_ERR(dev->clk)) clk_disable_unprepare(dev->clk); return ret; -- cgit From 7093a9702e00274fde609d634010a5833b45b229 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 10 Apr 2017 16:28:09 +0100 Subject: net: mvmdio: fix interrupt disable in remove path The pre-existing write to disable interrupts on the remove path happens whether we have an interrupt or not. While this may seem to be a good idea, this driver is re-used in many different implementations, some where the binding only specifies four bytes of register space. This access causes us to access registers outside of the binding. Make it conditional on the interrupt being present, which is the same condition used when enabling the interrupt in the first place. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvmdio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index 7aea0beca56e..6ea5caddca62 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -263,7 +263,8 @@ static int orion_mdio_remove(struct platform_device *pdev) struct mii_bus *bus = platform_get_drvdata(pdev); struct orion_mdio_dev *dev = bus->priv; - writel(0, dev->regs + MVMDIO_ERR_INT_MASK); + if (dev->err_interrupt > 0) + writel(0, dev->regs + MVMDIO_ERR_INT_MASK); mdiobus_unregister(bus); if (!IS_ERR(dev->clk)) clk_disable_unprepare(dev->clk); -- cgit From 2c26122e2c8d9b76b98995a7bd9f13a79dacfabe Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 10 Apr 2017 16:28:15 +0100 Subject: dt-bindings: correct marvell orion MDIO binding document Correct the Marvell Orion MDIO binding document to properly reflect the cases where an interrupt is present. Augment the examples to show this. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- .../devicetree/bindings/net/marvell-orion-mdio.txt | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt index 9417e54c26c0..ca733ff68ab9 100644 --- a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt +++ b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt @@ -7,7 +7,10 @@ interface. Required properties: - compatible: "marvell,orion-mdio" -- reg: address and length of the SMI register +- reg: address and length of the MDIO registers. When an interrupt is + not present, the length is the size of the SMI register (4 bytes) + otherwise it must be 0x84 bytes to cover the interrupt control + registers. Optional properties: - interrupts: interrupt line number for the SMI error/done interrupt @@ -17,7 +20,7 @@ The child nodes of the MDIO driver are the individual PHY devices connected to this MDIO bus. They must have a "reg" property given the PHY address on the MDIO bus. -Example at the SoC level: +Example at the SoC level without an interrupt property: mdio { #address-cells = <1>; @@ -26,6 +29,16 @@ mdio { reg = <0xd0072004 0x4>; }; +Example with an interrupt property: + +mdio { + #address-cells = <1>; + #size-cells = <0>; + compatible = "marvell,orion-mdio"; + reg = <0xd0072004 0x84>; + interrupts = <30>; +}; + And at the board level: mdio { -- cgit From a51e2c9da44acad7494ac3f57c48f296890cbe2a Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 10 Apr 2017 16:28:20 +0100 Subject: net: mvmdio: disable interrupt if resource size is too small Disable the MDIO interrupt, falling back to polled mode, if the resource size does not allow us to access the interrupt registers. All current DT bindings use a size of 0x84, which allows access, but verifying it is good practice. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvmdio.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index 6ea5caddca62..614dfde657fe 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -221,6 +221,12 @@ static int orion_mdio_probe(struct platform_device *pdev) clk_prepare_enable(dev->clk); dev->err_interrupt = platform_get_irq(pdev, 0); + if (dev->err_interrupt > 0 && + resource_size(r) < MVMDIO_ERR_INT_MASK + 4) { + dev_err(&pdev->dev, + "disabling interrupt, resource size is too small\n"); + dev->err_interrupt = 0; + } if (dev->err_interrupt > 0) { ret = devm_request_irq(&pdev->dev, dev->err_interrupt, orion_mdio_err_irq, -- cgit From 6d6a331f44a18accbff5f693d03f2842b0e8c581 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 10 Apr 2017 16:28:25 +0100 Subject: dt-bindings: allow up to three clocks for orion-mdio Armada 8040 needs three clocks to be enabled for MDIO accesses to work. Update the binding to allow the extra clocks to be specified. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/marvell-orion-mdio.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt index ca733ff68ab9..ccdabdcc8618 100644 --- a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt +++ b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt @@ -14,7 +14,7 @@ Required properties: Optional properties: - interrupts: interrupt line number for the SMI error/done interrupt -- clocks: Phandle to the clock control device and gate bit +- clocks: phandle for up to three required clocks for the MDIO instance The child nodes of the MDIO driver are the individual PHY devices connected to this MDIO bus. They must have a "reg" property given the -- cgit From 96cb4342382290c935d933a08feb57d6d0183071 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 10 Apr 2017 16:28:31 +0100 Subject: net: mvmdio: allow up to three clocks to be specified for orion-mdio Allow up to three clocks to be specified and enabled for the orion-mdio interface, which are required for this interface to be accessible on Armada 8k platforms. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvmdio.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index 614dfde657fe..90a60b98c28e 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -53,7 +53,7 @@ struct orion_mdio_dev { struct mutex lock; void __iomem *regs; - struct clk *clk; + struct clk *clk[3]; /* * If we have access to the error interrupt pin (which is * somewhat misnamed as it not only reflects internal errors @@ -187,7 +187,7 @@ static int orion_mdio_probe(struct platform_device *pdev) struct resource *r; struct mii_bus *bus; struct orion_mdio_dev *dev; - int ret; + int i, ret; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { @@ -216,9 +216,12 @@ static int orion_mdio_probe(struct platform_device *pdev) init_waitqueue_head(&dev->smi_busy_wait); - dev->clk = devm_clk_get(&pdev->dev, NULL); - if (!IS_ERR(dev->clk)) - clk_prepare_enable(dev->clk); + for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { + dev->clk[i] = of_clk_get(pdev->dev.of_node, i); + if (IS_ERR(dev->clk[i])) + break; + clk_prepare_enable(dev->clk[i]); + } dev->err_interrupt = platform_get_irq(pdev, 0); if (dev->err_interrupt > 0 && @@ -259,8 +262,14 @@ static int orion_mdio_probe(struct platform_device *pdev) out_mdio: if (dev->err_interrupt > 0) writel(0, dev->regs + MVMDIO_ERR_INT_MASK); - if (!IS_ERR(dev->clk)) - clk_disable_unprepare(dev->clk); + + for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { + if (IS_ERR(dev->clk[i])) + break; + clk_disable_unprepare(dev->clk[i]); + clk_put(dev->clk[i]); + } + return ret; } @@ -268,12 +277,18 @@ static int orion_mdio_remove(struct platform_device *pdev) { struct mii_bus *bus = platform_get_drvdata(pdev); struct orion_mdio_dev *dev = bus->priv; + int i; if (dev->err_interrupt > 0) writel(0, dev->regs + MVMDIO_ERR_INT_MASK); mdiobus_unregister(bus); - if (!IS_ERR(dev->clk)) - clk_disable_unprepare(dev->clk); + + for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { + if (IS_ERR(dev->clk[i])) + break; + clk_disable_unprepare(dev->clk[i]); + clk_put(dev->clk[i]); + } return 0; } -- cgit From 6b254afd2ef384b21aeaf166ddc957fe1083a7e2 Mon Sep 17 00:00:00 2001 From: Ganesh Goudar Date: Mon, 10 Apr 2017 21:26:18 +0530 Subject: cxgb4: save tid while creating server filter Save the filter tid while creating the server filter, which is used later to retrieve the corresponding filter instance while handling the filter reply. Signed-off-by: Ganesh Goudar Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index afb0967d2ce6..aa7101953e64 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2338,6 +2338,10 @@ int cxgb4_create_server_filter(const struct net_device *dev, unsigned int stid, f->locked = 1; f->fs.rpttid = 1; + /* Save the actual tid. We need this to get the corresponding + * filter entry structure in filter_rpl. + */ + f->tid = stid + adap->tids.ftid_base; ret = set_filter_wr(adap, stid); if (ret) { clear_filter(adap, f); -- cgit From fe6af0e1229e2d22a798fb7375ff0e58670073d3 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Mon, 10 Apr 2017 20:33:29 +0200 Subject: net: stmmac: set total length of the packet to be transmitted in TDES3 Field FL/TPL in register TDES3 is not correctly set on GMAC4. TX appears to be functional on GMAC 4.10a even if this field is not set, however, to avoid relying on undefined behavior, set the length in TDES3. The field has a different meaning depending on if the TSE bit in TDES3 is set or not (TSO). However, regardless of the TSE bit, the field is not optional. The field is already set correctly when the TSE bit is set. Since there is no limit for the number of descriptors that can be used for a single packet, the field should be set to the sum of the buffers contained in: [ ... ... ], which should be equal to skb->len. Signed-off-by: Niklas Cassel Acked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/chain_mode.c | 6 +++--- drivers/net/ethernet/stmicro/stmmac/common.h | 2 +- drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c | 3 ++- drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 2 +- drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 2 +- drivers/net/ethernet/stmicro/stmmac/ring_mode.c | 9 ++++++--- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 5 +++-- 7 files changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c index 37881f81319e..e93c40b4631e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c @@ -52,7 +52,7 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) tx_q->tx_skbuff_dma[entry].len = bmax; /* do not close the descriptor and do not set own bit */ priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE, - 0, false); + 0, false, skb->len); while (len != 0) { tx_q->tx_skbuff[entry] = NULL; @@ -70,7 +70,7 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) tx_q->tx_skbuff_dma[entry].len = bmax; priv->hw->desc->prepare_tx_desc(desc, 0, bmax, csum, STMMAC_CHAIN_MODE, 1, - false); + false, skb->len); len -= bmax; i++; } else { @@ -85,7 +85,7 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) /* last descriptor can be set now */ priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, STMMAC_CHAIN_MODE, 1, - true); + true, skb->len); len = 0; } } diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 90d28bcad880..b7ce3fbb5375 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -373,7 +373,7 @@ struct stmmac_desc_ops { /* Invoked by the xmit function to prepare the tx descriptor */ void (*prepare_tx_desc) (struct dma_desc *p, int is_fs, int len, bool csum_flag, int mode, bool tx_own, - bool ls); + bool ls, unsigned int tot_pkt_len); void (*prepare_tso_tx_desc)(struct dma_desc *p, int is_fs, int len1, int len2, bool tx_own, bool ls, unsigned int tcphdrlen, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c index 843ec69222ea..aa6476439aee 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c @@ -304,12 +304,13 @@ static void dwmac4_rd_init_tx_desc(struct dma_desc *p, int mode, int end) static void dwmac4_rd_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, bool csum_flag, int mode, bool tx_own, - bool ls) + bool ls, unsigned int tot_pkt_len) { unsigned int tdes3 = le32_to_cpu(p->des3); p->des2 |= cpu_to_le32(len & TDES2_BUFFER1_SIZE_MASK); + tdes3 |= tot_pkt_len & TDES3_PACKET_SIZE_MASK; if (is_fs) tdes3 |= TDES3_FIRST_DESCRIPTOR; else diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 323b59ec74a3..7546b3664113 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -315,7 +315,7 @@ static void enh_desc_release_tx_desc(struct dma_desc *p, int mode) static void enh_desc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, bool csum_flag, int mode, bool tx_own, - bool ls) + bool ls, unsigned int tot_pkt_len) { unsigned int tdes0 = le32_to_cpu(p->des0); diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index efb818ebd55e..f817f8f36569 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -191,7 +191,7 @@ static void ndesc_release_tx_desc(struct dma_desc *p, int mode) static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, bool csum_flag, int mode, bool tx_own, - bool ls) + bool ls, unsigned int tot_pkt_len) { unsigned int tdes1 = le32_to_cpu(p->des1); diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c index 31213e64513d..28e4b5d50ce6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c +++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c @@ -59,7 +59,8 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, - STMMAC_RING_MODE, 0, false); + STMMAC_RING_MODE, 0, + false, skb->len); tx_q->tx_skbuff[entry] = NULL; entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); @@ -79,7 +80,8 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, - STMMAC_RING_MODE, 1, true); + STMMAC_RING_MODE, 1, + true, skb->len); } else { des2 = dma_map_single(priv->device, skb->data, nopaged_len, DMA_TO_DEVICE); @@ -91,7 +93,8 @@ static int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum) tx_q->tx_skbuff_dma[entry].is_jumbo = true; desc->des3 = cpu_to_le32(des2 + BUF_SIZE_4KiB); priv->hw->desc->prepare_tx_desc(desc, 1, nopaged_len, csum, - STMMAC_RING_MODE, 0, true); + STMMAC_RING_MODE, 0, + true, skb->len); } tx_q->cur_tx = entry; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 85f315e01c1d..cd8c60132390 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3033,7 +3033,8 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) /* Prepare the descriptor and set the own bit too */ priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion, - priv->mode, 1, last_segment); + priv->mode, 1, last_segment, + skb->len); } entry = STMMAC_GET_ENTRY(entry, DMA_TX_SIZE); @@ -3116,7 +3117,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) /* Prepare the first descriptor setting the OWN bit too */ priv->hw->desc->prepare_tx_desc(first, 1, nopaged_len, csum_insertion, priv->mode, 1, - last_segment); + last_segment, skb->len); /* The own bit must be the latest setting done when prepare the * descriptor and then barrier is needed to make sure that -- cgit From b6518e6a0086fc152f9a35ac5062930788f8b4bc Mon Sep 17 00:00:00 2001 From: David Daney Date: Tue, 11 Apr 2017 14:30:52 -0700 Subject: tools: bpf_jit_disasm: Add option to dump JIT image to a file. When debugging the JIT on an embedded platform or cross build environment, libbfd may not be available, making it impossible to run bpf_jit_disasm natively. Add an option to emit a binary image of the JIT code to a file. This file can then be disassembled off line. Typical usage in this case might be (pasting mips64 dmesg output to cat command): $ cat > jit.raw $ bpf_jit_disasm -f jit.raw -O jit.bin $ mips64-linux-gnu-objdump -D -b binary -m mips:isa64r2 -EB jit.bin Signed-off-by: David Daney Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/net/bpf_jit_disasm.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/tools/net/bpf_jit_disasm.c b/tools/net/bpf_jit_disasm.c index 544b05a53b70..ad572e6cdbd0 100644 --- a/tools/net/bpf_jit_disasm.c +++ b/tools/net/bpf_jit_disasm.c @@ -229,6 +229,7 @@ static void usage(void) { printf("Usage: bpf_jit_disasm [...]\n"); printf(" -o Also display related opcodes (default: off).\n"); + printf(" -O Write binary image of code to file, don't disassemble to stdout.\n"); printf(" -f Read last image dump from file or stdin (default: klog).\n"); printf(" -h Display this help.\n"); } @@ -238,12 +239,19 @@ int main(int argc, char **argv) unsigned int len, klen, opt, opcodes = 0; static uint8_t image[32768]; char *kbuff, *file = NULL; + char *ofile = NULL; + int ofd; + ssize_t nr; + uint8_t *pos; - while ((opt = getopt(argc, argv, "of:")) != -1) { + while ((opt = getopt(argc, argv, "of:O:")) != -1) { switch (opt) { case 'o': opcodes = 1; break; + case 'O': + ofile = optarg; + break; case 'f': file = optarg; break; @@ -263,11 +271,35 @@ int main(int argc, char **argv) } len = get_last_jit_image(kbuff, klen, image, sizeof(image)); - if (len > 0) - get_asm_insns(image, len, opcodes); - else + if (len <= 0) { fprintf(stderr, "No JIT image found!\n"); + goto done; + } + if (!ofile) { + get_asm_insns(image, len, opcodes); + goto done; + } + + ofd = open(ofile, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE); + if (ofd < 0) { + fprintf(stderr, "Could not open file %s for writing: ", ofile); + perror(NULL); + goto done; + } + pos = image; + do { + nr = write(ofd, pos, len); + if (nr < 0) { + fprintf(stderr, "Could not write data to %s: ", ofile); + perror(NULL); + goto done; + } + len -= nr; + pos += nr; + } while (len); + close(ofd); +done: put_log_buff(kbuff); return 0; } -- cgit From 085e1a65f04fb05941de0a071c8d70246cd03178 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 11 Apr 2017 17:02:40 -0700 Subject: rtnetlink: Do not generate notifications for MTU events Changing MTU on a link currently causes 3 messages to be sent to userspace: [LINK]11: dummy1: mtu 1490 qdisc noqueue state UNKNOWN group default link/ether f2:52:5c:6d:21:f3 brd ff:ff:ff:ff:ff:ff [LINK]11: dummy1: mtu 1500 qdisc noqueue state UNKNOWN group default link/ether f2:52:5c:6d:21:f3 brd ff:ff:ff:ff:ff:ff [LINK]11: dummy1: mtu 1500 qdisc noqueue state UNKNOWN group default link/ether f2:52:5c:6d:21:f3 brd ff:ff:ff:ff:ff:ff Remove the messages sent for PRE_CHANGE_MTU and CHANGE_MTU netdev events. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 58419da7961b..79ab43796b08 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4117,7 +4117,6 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi switch (event) { case NETDEV_REBOOT: - case NETDEV_CHANGEMTU: case NETDEV_CHANGEADDR: case NETDEV_CHANGENAME: case NETDEV_FEAT_CHANGE: @@ -4126,7 +4125,6 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_NOTIFY_PEERS: case NETDEV_CHANGEUPPER: case NETDEV_RESEND_IGMP: - case NETDEV_PRECHANGEMTU: case NETDEV_CHANGEINFODATA: case NETDEV_PRECHANGEUPPER: case NETDEV_CHANGELOWERSTATE: -- cgit From 46ede612c7a3e1558c98345b611baf636d3ea65f Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 11 Apr 2017 17:02:41 -0700 Subject: rtnetlink: Do not generate notification for UDP_TUNNEL_PUSH_INFO NETDEV_UDP_TUNNEL_PUSH_INFO is an internal notifier; nothing userspace can do so don't generate a netlink notification. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 79ab43796b08..58722bf10d50 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4128,7 +4128,6 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_CHANGEINFODATA: case NETDEV_PRECHANGEUPPER: case NETDEV_CHANGELOWERSTATE: - case NETDEV_UDP_TUNNEL_PUSH_INFO: case NETDEV_CHANGE_TX_QUEUE_LEN: rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL); break; -- cgit From cd8966e75ed3c6b41a37047a904617bc44fa481f Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 11 Apr 2017 17:02:42 -0700 Subject: rtnetlink: Do not generate notifications for CHANGEADDR event Changing hardware address generates redundant messages: [LINK]11: dummy1: mtu 1500 qdisc noqueue state UNKNOWN group default link/ether 02:02:02:02:02:02 brd ff:ff:ff:ff:ff:ff [LINK]11: dummy1: mtu 1500 qdisc noqueue state UNKNOWN group default link/ether 02:02:02:02:02:02 brd ff:ff:ff:ff:ff:ff Do not send a notification for the CHANGEADDR notifier. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 58722bf10d50..574f9b79919a 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4117,7 +4117,6 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi switch (event) { case NETDEV_REBOOT: - case NETDEV_CHANGEADDR: case NETDEV_CHANGENAME: case NETDEV_FEAT_CHANGE: case NETDEV_BONDING_FAILOVER: -- cgit From aef091ae58aab340afb6f4d6a835037e66fa56a1 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 11 Apr 2017 17:02:43 -0700 Subject: rtnetlink: Do not generate notifications for POST_TYPE_CHANGE event Changing the master device for a link generates many messages; the one generated for POST_TYPE_CHANGE is redundant: [LINK]11: dummy1: mtu 1500 qdisc noqueue master br1 state UNKNOWN group default link/ether 02:02:02:02:02:03 brd ff:ff:ff:ff:ff:ff [LINK]11: dummy1: mtu 1500 qdisc noqueue master br1 state UNKNOWN group default link/ether 02:02:02:02:02:03 brd ff:ff:ff:ff:ff:ff Remove POST_TYPE_CHANGE from the list of notifiers that generate notifications. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 574f9b79919a..8cfb9e3c1f6e 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4120,7 +4120,6 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_CHANGENAME: case NETDEV_FEAT_CHANGE: case NETDEV_BONDING_FAILOVER: - case NETDEV_POST_TYPE_CHANGE: case NETDEV_NOTIFY_PEERS: case NETDEV_CHANGEUPPER: case NETDEV_RESEND_IGMP: -- cgit From bf2c2984d3f4d22b8617df1b0103bab9b7970902 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 11 Apr 2017 17:02:44 -0700 Subject: rtnetlink: Do not generate notifications for PRECHANGEUPPER event PRECHANGEUPPER is an internal event; do not generate userspace notifications. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 8cfb9e3c1f6e..10df445bb818 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4124,7 +4124,6 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_CHANGEUPPER: case NETDEV_RESEND_IGMP: case NETDEV_CHANGEINFODATA: - case NETDEV_PRECHANGEUPPER: case NETDEV_CHANGELOWERSTATE: case NETDEV_CHANGE_TX_QUEUE_LEN: rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL); -- cgit From aed073590970137c5937f10c1dff5379d30083a3 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 11 Apr 2017 17:02:45 -0700 Subject: rtnetlink: Do not generate notifications for CHANGELOWERSTATE event CHANGELOWERSTATE is an internal event; do not generate userspace notifications. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 10df445bb818..b70e915be66d 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4124,7 +4124,6 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_CHANGEUPPER: case NETDEV_RESEND_IGMP: case NETDEV_CHANGEINFODATA: - case NETDEV_CHANGELOWERSTATE: case NETDEV_CHANGE_TX_QUEUE_LEN: rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL); break; -- cgit From b6b36eb23a46bd927ffc9d9a3c911965c1be7c36 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 11 Apr 2017 17:02:46 -0700 Subject: rtnetlink: Do not generate notifications for NETDEV_CHANGEUPPER event NETDEV_CHANGEUPPER is an internal event; do not generate userspace notifications. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b70e915be66d..ef93f6c983f3 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4121,7 +4121,6 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_FEAT_CHANGE: case NETDEV_BONDING_FAILOVER: case NETDEV_NOTIFY_PEERS: - case NETDEV_CHANGEUPPER: case NETDEV_RESEND_IGMP: case NETDEV_CHANGEINFODATA: case NETDEV_CHANGE_TX_QUEUE_LEN: -- cgit From 27b3b551d8a7af98423b4a2b0d033013c08d812c Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 11 Apr 2017 17:02:47 -0700 Subject: rtnetlink: Do not generate notifications for NETDEV_CHANGE_TX_QUEUE_LEN event Changing tx queue length generates identical messages: [LINK]22: dummy1: mtu 1500 qdisc noqueue state UNKNOWN group default link/ether 02:04:f4:b7:5c:d2 brd ff:ff:ff:ff:ff:ff promiscuity 0 dummy numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 [LINK]22: dummy1: mtu 1500 qdisc noqueue state UNKNOWN group default link/ether 02:04:f4:b7:5c:d2 brd ff:ff:ff:ff:ff:ff promiscuity 0 dummy numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 Remove NETDEV_CHANGE_TX_QUEUE_LEN from the list of notifiers that generate notifications. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index ef93f6c983f3..c138b6b75e59 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4123,7 +4123,6 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_NOTIFY_PEERS: case NETDEV_RESEND_IGMP: case NETDEV_CHANGEINFODATA: - case NETDEV_CHANGE_TX_QUEUE_LEN: rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL); break; default: -- cgit From 7ed14d973f7c6742f4827fbbf6730cbe20888b8b Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Wed, 12 Apr 2017 12:34:03 +0800 Subject: net: ipv4: Refine the ipv4_default_advmss 1. Don't get the metric RTAX_ADVMSS of dst. There are two reasons. 1) Its caller dst_metric_advmss has already invoke dst_metric_advmss before invoke default_advmss. 2) The ipv4_default_advmss is used to get the default mss, it should not try to get the metric like ip6_default_advmss. 2. Use sizeof(tcphdr)+sizeof(iphdr) instead of literal 40. 3. Define one new macro IPV4_MAX_PMTU instead of 65535 according to RFC 2675, section 5.1. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- include/net/ip.h | 2 ++ net/ipv4/route.c | 12 ++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/include/net/ip.h b/include/net/ip.h index bf264a8db1ce..821cedcc8e73 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -33,6 +33,8 @@ #include #include +#define IPV4_MAX_PMTU 65535U /* RFC 2675, Section 5.1 */ + struct sock; struct inet_skb_parm { diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 5e1e60546fce..0fcc2d5192bd 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1250,15 +1250,11 @@ static void set_class_tag(struct rtable *rt, u32 tag) static unsigned int ipv4_default_advmss(const struct dst_entry *dst) { - unsigned int advmss = dst_metric_raw(dst, RTAX_ADVMSS); + unsigned int header_size = sizeof(struct tcphdr) + sizeof(struct iphdr); + unsigned int advmss = max_t(unsigned int, dst->dev->mtu - header_size, + ip_rt_min_advmss); - if (advmss == 0) { - advmss = max_t(unsigned int, dst->dev->mtu - 40, - ip_rt_min_advmss); - if (advmss > 65535 - 40) - advmss = 65535 - 40; - } - return advmss; + return min(advmss, IPV4_MAX_PMTU - header_size); } static unsigned int ipv4_mtu(const struct dst_entry *dst) -- cgit From f0c660e04f6167e8f229055d3e872d5ca0edeb9f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 13 Apr 2017 10:03:50 -0500 Subject: dt-bindings: net: Add TI WiLink shared transport binding Add serial slave device binding for the TI WiLink series of Bluetooth/FM/GPS devices. Signed-off-by: Rob Herring Cc: Mark Rutland Cc: netdev@vger.kernel.org Cc: devicetree@vger.kernel.org Signed-off-by: Marcel Holtmann --- .../devicetree/bindings/net/ti,wilink-st.txt | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/ti,wilink-st.txt diff --git a/Documentation/devicetree/bindings/net/ti,wilink-st.txt b/Documentation/devicetree/bindings/net/ti,wilink-st.txt new file mode 100644 index 000000000000..cbad73a84ac4 --- /dev/null +++ b/Documentation/devicetree/bindings/net/ti,wilink-st.txt @@ -0,0 +1,35 @@ +TI WiLink 7/8 (wl12xx/wl18xx) Shared Transport BT/FM/GPS devices + +TI WiLink devices have a UART interface for providing Bluetooth, FM radio, +and GPS over what's called "shared transport". The shared transport is +standard BT HCI protocol with additional channels for the other functions. + +These devices also have a separate WiFi interface as described in +wireless/ti,wlcore.txt. + +This bindings follows the UART slave device binding in +../serial/slave-device.txt. + +Required properties: + - compatible: should be one of the following: + "ti,wl1271-st" + "ti,wl1273-st" + "ti,wl1831-st" + "ti,wl1835-st" + "ti,wl1837-st" + +Optional properties: + - enable-gpios : GPIO signal controlling enabling of BT. Active high. + - vio-supply : Vio input supply (1.8V) + - vbat-supply : Vbat input supply (2.9-4.8V) + +Example: + +&serial0 { + compatible = "ns16550a"; + ... + bluetooth { + compatible = "ti,wl1835-st"; + enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; + }; +}; -- cgit From 31927e5a52847761c1f2797a7ea53b6a75bdc518 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 13 Apr 2017 10:03:51 -0500 Subject: bluetooth: hci_uart: remove unused hci_uart_init_tty There are no users of hci_uart_init_tty, so remove it. Signed-off-by: Rob Herring Cc: Marcel Holtmann Cc: Gustavo Padovan Cc: Johan Hedberg Cc: linux-bluetooth@vger.kernel.org Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ldisc.c | 19 ------------------- drivers/bluetooth/hci_uart.h | 1 - 2 files changed, 20 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 17bcbc13623f..cec4438ede01 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -319,25 +319,6 @@ void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed, hu->oper_speed = oper_speed; } -void hci_uart_init_tty(struct hci_uart *hu) -{ - struct tty_struct *tty = hu->tty; - struct ktermios ktermios; - - /* Bring the UART into a known 8 bits no parity hw fc state */ - ktermios = tty->termios; - ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | - INLCR | IGNCR | ICRNL | IXON); - ktermios.c_oflag &= ~OPOST; - ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - ktermios.c_cflag &= ~(CSIZE | PARENB); - ktermios.c_cflag |= CS8; - ktermios.c_cflag |= CRTSCTS; - - /* tty_set_termios() return not checked as it is always 0 */ - tty_set_termios(tty, &ktermios); -} - void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed) { struct tty_struct *tty = hu->tty; diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 1b41c661bbb8..2b05e557fad0 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -114,7 +114,6 @@ int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p int hci_uart_tx_wakeup(struct hci_uart *hu); int hci_uart_init_ready(struct hci_uart *hu); -void hci_uart_init_tty(struct hci_uart *hu); void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed); void hci_uart_set_flow_control(struct hci_uart *hu, bool enable); void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed, -- cgit From 371805522f870986144fcd88727a47858e364a2c Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 13 Apr 2017 10:03:52 -0500 Subject: bluetooth: hci_uart: add LL protocol serdev driver support Turns out that the LL protocol and the TI-ST are the same thing AFAICT. The TI-ST adds firmware loading, GPIO control, and shared access for NFC, FM radio, etc. For now, we're only implementing what is needed for BT. This mirrors other drivers like BCM and Intel, but uses the new serdev bus. The firmware loading is greatly simplified by using existing infrastructure to send commands. It may be a bit slower than the original code using synchronous functions, but the real bottleneck is likely doing firmware load at 115.2kbps. Signed-off-by: Rob Herring Cc: Marcel Holtmann Cc: Gustavo Padovan Cc: Johan Hedberg Cc: linux-bluetooth@vger.kernel.org Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ll.c | 262 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 261 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index 02692fe30279..485e8eb04542 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -34,20 +34,24 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include #include +#include +#include #include +#include #include #include +#include #include "hci_uart.h" @@ -76,6 +80,12 @@ struct hcill_cmd { u8 cmd; } __packed; +struct ll_device { + struct hci_uart hu; + struct serdev_device *serdev; + struct gpio_desc *enable_gpio; +}; + struct ll_struct { unsigned long rx_state; unsigned long rx_count; @@ -136,6 +146,9 @@ static int ll_open(struct hci_uart *hu) hu->priv = ll; + if (hu->serdev) + serdev_device_open(hu->serdev); + return 0; } @@ -164,6 +177,13 @@ static int ll_close(struct hci_uart *hu) kfree_skb(ll->rx_skb); + if (hu->serdev) { + struct ll_device *lldev = serdev_device_get_drvdata(hu->serdev); + gpiod_set_value_cansleep(lldev->enable_gpio, 0); + + serdev_device_close(hu->serdev); + } + hu->priv = NULL; kfree(ll); @@ -505,9 +525,245 @@ static struct sk_buff *ll_dequeue(struct hci_uart *hu) return skb_dequeue(&ll->txq); } +#if IS_ENABLED(CONFIG_SERIAL_DEV_BUS) +static int read_local_version(struct hci_dev *hdev) +{ + int err = 0; + unsigned short version = 0; + struct sk_buff *skb; + struct hci_rp_read_local_version *ver; + + skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Reading TI version information failed (%ld)", + PTR_ERR(skb)); + err = PTR_ERR(skb); + goto out; + } + if (skb->len != sizeof(*ver)) { + err = -EILSEQ; + goto out; + } + + ver = (struct hci_rp_read_local_version *)skb->data; + if (le16_to_cpu(ver->manufacturer) != 13) { + err = -ENODEV; + goto out; + } + + version = le16_to_cpu(ver->lmp_subver); + +out: + if (err) bt_dev_err(hdev, "Failed to read TI version info: %d", err); + kfree_skb(skb); + return err ? err : version; +} + +/** + * download_firmware - + * internal function which parses through the .bts firmware + * script file intreprets SEND, DELAY actions only as of now + */ +static int download_firmware(struct ll_device *lldev) +{ + unsigned short chip, min_ver, maj_ver; + int version, err, len; + unsigned char *ptr, *action_ptr; + unsigned char bts_scr_name[40]; /* 40 char long bts scr name? */ + const struct firmware *fw; + struct sk_buff *skb; + struct hci_command *cmd; + + version = read_local_version(lldev->hu.hdev); + if (version < 0) + return version; + + chip = (version & 0x7C00) >> 10; + min_ver = (version & 0x007F); + maj_ver = (version & 0x0380) >> 7; + if (version & 0x8000) + maj_ver |= 0x0008; + + snprintf(bts_scr_name, sizeof(bts_scr_name), + "ti-connectivity/TIInit_%d.%d.%d.bts", + chip, maj_ver, min_ver); + + err = request_firmware(&fw, bts_scr_name, &lldev->serdev->dev); + if (err || !fw->data || !fw->size) { + bt_dev_err(lldev->hu.hdev, "request_firmware failed(errno %d) for %s", + err, bts_scr_name); + return -EINVAL; + } + ptr = (void *)fw->data; + len = fw->size; + /* bts_header to remove out magic number and + * version + */ + ptr += sizeof(struct bts_header); + len -= sizeof(struct bts_header); + + while (len > 0 && ptr) { + bt_dev_dbg(lldev->hu.hdev, " action size %d, type %d ", + ((struct bts_action *)ptr)->size, + ((struct bts_action *)ptr)->type); + + action_ptr = &(((struct bts_action *)ptr)->data[0]); + + switch (((struct bts_action *)ptr)->type) { + case ACTION_SEND_COMMAND: /* action send */ + bt_dev_dbg(lldev->hu.hdev, "S"); + cmd = (struct hci_command *)action_ptr; + if (cmd->opcode == 0xff36) { + /* ignore remote change + * baud rate HCI VS command */ + bt_dev_warn(lldev->hu.hdev, "change remote baud rate command in firmware"); + break; + } + if (cmd->prefix != 1) + bt_dev_dbg(lldev->hu.hdev, "command type %d\n", cmd->prefix); + + skb = __hci_cmd_sync(lldev->hu.hdev, cmd->opcode, cmd->plen, &cmd->speed, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(lldev->hu.hdev, "send command failed\n"); + goto out_rel_fw; + } + kfree_skb(skb); + break; + case ACTION_WAIT_EVENT: /* wait */ + /* no need to wait as command was synchronous */ + bt_dev_dbg(lldev->hu.hdev, "W"); + break; + case ACTION_DELAY: /* sleep */ + bt_dev_info(lldev->hu.hdev, "sleep command in scr"); + mdelay(((struct bts_action_delay *)action_ptr)->msec); + break; + } + len -= (sizeof(struct bts_action) + + ((struct bts_action *)ptr)->size); + ptr += sizeof(struct bts_action) + + ((struct bts_action *)ptr)->size; + } + +out_rel_fw: + /* fw download complete */ + release_firmware(fw); + return err; +} + +static int ll_setup(struct hci_uart *hu) +{ + int err, retry = 3; + struct ll_device *lldev; + struct serdev_device *serdev = hu->serdev; + u32 speed; + + if (!serdev) + return 0; + + lldev = serdev_device_get_drvdata(serdev); + + serdev_device_set_flow_control(serdev, true); + + do { + /* Configure BT_EN to HIGH state */ + gpiod_set_value_cansleep(lldev->enable_gpio, 0); + msleep(5); + gpiod_set_value_cansleep(lldev->enable_gpio, 1); + msleep(100); + + err = download_firmware(lldev); + if (!err) + break; + + /* Toggle BT_EN and retry */ + bt_dev_err(hu->hdev, "download firmware failed, retrying..."); + } while (retry--); + + if (err) + return err; + + /* Operational speed if any */ + if (hu->oper_speed) + speed = hu->oper_speed; + else if (hu->proto->oper_speed) + speed = hu->proto->oper_speed; + else + speed = 0; + + if (speed) { + struct sk_buff *skb = __hci_cmd_sync(hu->hdev, 0xff36, sizeof(speed), &speed, HCI_INIT_TIMEOUT); + if (!IS_ERR(skb)) { + kfree_skb(skb); + serdev_device_set_baudrate(serdev, speed); + } + } + + return 0; +} + +static const struct hci_uart_proto llp; + +static int hci_ti_probe(struct serdev_device *serdev) +{ + struct hci_uart *hu; + struct ll_device *lldev; + u32 max_speed = 3000000; + + lldev = devm_kzalloc(&serdev->dev, sizeof(struct ll_device), GFP_KERNEL); + if (!lldev) + return -ENOMEM; + hu = &lldev->hu; + + serdev_device_set_drvdata(serdev, lldev); + lldev->serdev = hu->serdev = serdev; + + lldev->enable_gpio = devm_gpiod_get_optional(&serdev->dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(lldev->enable_gpio)) + return PTR_ERR(lldev->enable_gpio); + + of_property_read_u32(serdev->dev.of_node, "max-speed", &max_speed); + hci_uart_set_speeds(hu, 115200, max_speed); + + return hci_uart_register_device(hu, &llp); +} + +static void hci_ti_remove(struct serdev_device *serdev) +{ + struct ll_device *lldev = serdev_device_get_drvdata(serdev); + struct hci_uart *hu = &lldev->hu; + struct hci_dev *hdev = hu->hdev; + + cancel_work_sync(&hu->write_work); + + hci_unregister_dev(hdev); + hci_free_dev(hdev); + hu->proto->close(hu); +} + +static const struct of_device_id hci_ti_of_match[] = { + { .compatible = "ti,wl1831-st" }, + { .compatible = "ti,wl1835-st" }, + { .compatible = "ti,wl1837-st" }, + {}, +}; +MODULE_DEVICE_TABLE(of, hci_ti_of_match); + +static struct serdev_device_driver hci_ti_drv = { + .driver = { + .name = "hci-ti", + .of_match_table = of_match_ptr(hci_ti_of_match), + }, + .probe = hci_ti_probe, + .remove = hci_ti_remove, +}; +#else +#define ll_setup NULL +#endif + static const struct hci_uart_proto llp = { .id = HCI_UART_LL, .name = "LL", + .setup = ll_setup, .open = ll_open, .close = ll_close, .recv = ll_recv, @@ -518,10 +774,14 @@ static const struct hci_uart_proto llp = { int __init ll_init(void) { + serdev_device_driver_register(&hci_ti_drv); + return hci_uart_register_proto(&llp); } int __exit ll_deinit(void) { + serdev_device_driver_unregister(&hci_ti_drv); + return hci_uart_unregister_proto(&llp); } -- cgit From 019aa56b7df8a796b2c01a56269a370ad3442ec7 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 13 Apr 2017 10:03:53 -0500 Subject: arm64: dts: hikey: add WL1835 Bluetooth device node This adds the serial slave device for the WL1835 Bluetooth interface. Signed-off-by: Rob Herring Cc: Wei Xu Cc: Mark Rutland Signed-off-by: Marcel Holtmann --- arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts b/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts index dba3c131c62c..9b4ba7169210 100644 --- a/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts +++ b/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts @@ -98,6 +98,11 @@ assigned-clocks = <&sys_ctrl HI6220_UART1_SRC>; assigned-clock-rates = <150000000>; status = "ok"; + + bluetooth { + compatible = "ti,wl1835-st"; + enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; + }; }; uart2: uart@f7112000 { -- cgit From d4d49bc145e830fdcfdcfba9ef4b7db5d0b1f8a7 Mon Sep 17 00:00:00 2001 From: Jie Deng Date: Wed, 12 Apr 2017 13:10:06 +0800 Subject: net: dwc-xlgmac: add the initial ethtool support It is necessary to provide ethtool support for displaying and modifying parameters of dwc-xlgmac. Signed-off-by: Jie Deng Signed-off-by: David S. Miller --- drivers/net/ethernet/synopsys/Makefile | 3 +- drivers/net/ethernet/synopsys/dwc-xlgmac-common.c | 1 + drivers/net/ethernet/synopsys/dwc-xlgmac-ethtool.c | 275 +++++++++++++++++++++ drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c | 4 +- drivers/net/ethernet/synopsys/dwc-xlgmac-net.c | 22 +- drivers/net/ethernet/synopsys/dwc-xlgmac.h | 11 + 6 files changed, 312 insertions(+), 4 deletions(-) create mode 100644 drivers/net/ethernet/synopsys/dwc-xlgmac-ethtool.c diff --git a/drivers/net/ethernet/synopsys/Makefile b/drivers/net/ethernet/synopsys/Makefile index c06e2eb3be90..0ad01916f11e 100644 --- a/drivers/net/ethernet/synopsys/Makefile +++ b/drivers/net/ethernet/synopsys/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_DWC_XLGMAC) += dwc-xlgmac.o dwc-xlgmac-objs := dwc-xlgmac-net.o dwc-xlgmac-desc.o \ - dwc-xlgmac-hw.o dwc-xlgmac-common.o + dwc-xlgmac-hw.o dwc-xlgmac-common.o \ + dwc-xlgmac-ethtool.o dwc-xlgmac-$(CONFIG_DWC_XLGMAC_PCI) += dwc-xlgmac-pci.o diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c index 07def2beabfa..d655a4261e98 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-common.c @@ -175,6 +175,7 @@ static int xlgmac_init(struct xlgmac_pdata *pdata) /* Set device operations */ netdev->netdev_ops = xlgmac_get_netdev_ops(); + netdev->ethtool_ops = xlgmac_get_ethtool_ops(); /* Set device features */ if (pdata->hw_feat.tso) { diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-ethtool.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-ethtool.c new file mode 100644 index 000000000000..fde722136869 --- /dev/null +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-ethtool.c @@ -0,0 +1,275 @@ +/* Synopsys DesignWare Core Enterprise Ethernet (XLGMAC) Driver + * + * Copyright (c) 2017 Synopsys, Inc. (www.synopsys.com) + * + * This program is dual-licensed; you may select either version 2 of + * the GNU General Public License ("GPL") or BSD license ("BSD"). + * + * This Synopsys DWC XLGMAC software driver and associated documentation + * (hereinafter the "Software") is an unsupported proprietary work of + * Synopsys, Inc. unless otherwise expressly agreed to in writing between + * Synopsys and you. The Software IS NOT an item of Licensed Software or a + * Licensed Product under any End User Software License Agreement or + * Agreement for Licensed Products with Synopsys or any supplement thereto. + * Synopsys is a registered trademark of Synopsys, Inc. Other names included + * in the SOFTWARE may be the trademarks of their respective owners. + */ + +#include +#include +#include + +#include "dwc-xlgmac.h" +#include "dwc-xlgmac-reg.h" + +struct xlgmac_stats_desc { + char stat_string[ETH_GSTRING_LEN]; + int stat_offset; +}; + +#define XLGMAC_STAT(str, var) \ + { \ + str, \ + offsetof(struct xlgmac_pdata, stats.var), \ + } + +static const struct xlgmac_stats_desc xlgmac_gstring_stats[] = { + /* MMC TX counters */ + XLGMAC_STAT("tx_bytes", txoctetcount_gb), + XLGMAC_STAT("tx_bytes_good", txoctetcount_g), + XLGMAC_STAT("tx_packets", txframecount_gb), + XLGMAC_STAT("tx_packets_good", txframecount_g), + XLGMAC_STAT("tx_unicast_packets", txunicastframes_gb), + XLGMAC_STAT("tx_broadcast_packets", txbroadcastframes_gb), + XLGMAC_STAT("tx_broadcast_packets_good", txbroadcastframes_g), + XLGMAC_STAT("tx_multicast_packets", txmulticastframes_gb), + XLGMAC_STAT("tx_multicast_packets_good", txmulticastframes_g), + XLGMAC_STAT("tx_vlan_packets_good", txvlanframes_g), + XLGMAC_STAT("tx_64_byte_packets", tx64octets_gb), + XLGMAC_STAT("tx_65_to_127_byte_packets", tx65to127octets_gb), + XLGMAC_STAT("tx_128_to_255_byte_packets", tx128to255octets_gb), + XLGMAC_STAT("tx_256_to_511_byte_packets", tx256to511octets_gb), + XLGMAC_STAT("tx_512_to_1023_byte_packets", tx512to1023octets_gb), + XLGMAC_STAT("tx_1024_to_max_byte_packets", tx1024tomaxoctets_gb), + XLGMAC_STAT("tx_underflow_errors", txunderflowerror), + XLGMAC_STAT("tx_pause_frames", txpauseframes), + + /* MMC RX counters */ + XLGMAC_STAT("rx_bytes", rxoctetcount_gb), + XLGMAC_STAT("rx_bytes_good", rxoctetcount_g), + XLGMAC_STAT("rx_packets", rxframecount_gb), + XLGMAC_STAT("rx_unicast_packets_good", rxunicastframes_g), + XLGMAC_STAT("rx_broadcast_packets_good", rxbroadcastframes_g), + XLGMAC_STAT("rx_multicast_packets_good", rxmulticastframes_g), + XLGMAC_STAT("rx_vlan_packets", rxvlanframes_gb), + XLGMAC_STAT("rx_64_byte_packets", rx64octets_gb), + XLGMAC_STAT("rx_65_to_127_byte_packets", rx65to127octets_gb), + XLGMAC_STAT("rx_128_to_255_byte_packets", rx128to255octets_gb), + XLGMAC_STAT("rx_256_to_511_byte_packets", rx256to511octets_gb), + XLGMAC_STAT("rx_512_to_1023_byte_packets", rx512to1023octets_gb), + XLGMAC_STAT("rx_1024_to_max_byte_packets", rx1024tomaxoctets_gb), + XLGMAC_STAT("rx_undersize_packets_good", rxundersize_g), + XLGMAC_STAT("rx_oversize_packets_good", rxoversize_g), + XLGMAC_STAT("rx_crc_errors", rxcrcerror), + XLGMAC_STAT("rx_crc_errors_small_packets", rxrunterror), + XLGMAC_STAT("rx_crc_errors_giant_packets", rxjabbererror), + XLGMAC_STAT("rx_length_errors", rxlengtherror), + XLGMAC_STAT("rx_out_of_range_errors", rxoutofrangetype), + XLGMAC_STAT("rx_fifo_overflow_errors", rxfifooverflow), + XLGMAC_STAT("rx_watchdog_errors", rxwatchdogerror), + XLGMAC_STAT("rx_pause_frames", rxpauseframes), + + /* Extra counters */ + XLGMAC_STAT("tx_tso_packets", tx_tso_packets), + XLGMAC_STAT("rx_split_header_packets", rx_split_header_packets), + XLGMAC_STAT("tx_process_stopped", tx_process_stopped), + XLGMAC_STAT("rx_process_stopped", rx_process_stopped), + XLGMAC_STAT("tx_buffer_unavailable", tx_buffer_unavailable), + XLGMAC_STAT("rx_buffer_unavailable", rx_buffer_unavailable), + XLGMAC_STAT("fatal_bus_error", fatal_bus_error), + XLGMAC_STAT("tx_vlan_packets", tx_vlan_packets), + XLGMAC_STAT("rx_vlan_packets", rx_vlan_packets), + XLGMAC_STAT("napi_poll_isr", napi_poll_isr), + XLGMAC_STAT("napi_poll_txtimer", napi_poll_txtimer), +}; + +#define XLGMAC_STATS_COUNT ARRAY_SIZE(xlgmac_gstring_stats) + +static void xlgmac_ethtool_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + u32 ver = pdata->hw_feat.version; + u32 snpsver, devid, userver; + + strlcpy(drvinfo->driver, pdata->drv_name, sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, pdata->drv_ver, sizeof(drvinfo->version)); + strlcpy(drvinfo->bus_info, dev_name(pdata->dev), + sizeof(drvinfo->bus_info)); + /* S|SNPSVER: Synopsys-defined Version + * D|DEVID: Indicates the Device family + * U|USERVER: User-defined Version + */ + snpsver = XLGMAC_GET_REG_BITS(ver, MAC_VR_SNPSVER_POS, + MAC_VR_SNPSVER_LEN); + devid = XLGMAC_GET_REG_BITS(ver, MAC_VR_DEVID_POS, + MAC_VR_DEVID_LEN); + userver = XLGMAC_GET_REG_BITS(ver, MAC_VR_USERVER_POS, + MAC_VR_USERVER_LEN); + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "S.D.U: %x.%x.%x", snpsver, devid, userver); +} + +static u32 xlgmac_ethtool_get_msglevel(struct net_device *netdev) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + + return pdata->msg_enable; +} + +static void xlgmac_ethtool_set_msglevel(struct net_device *netdev, + u32 msglevel) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + + pdata->msg_enable = msglevel; +} + +static void xlgmac_ethtool_get_channels(struct net_device *netdev, + struct ethtool_channels *channel) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + + channel->max_rx = XLGMAC_MAX_DMA_CHANNELS; + channel->max_tx = XLGMAC_MAX_DMA_CHANNELS; + channel->rx_count = pdata->rx_q_count; + channel->tx_count = pdata->tx_q_count; +} + +static int xlgmac_ethtool_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + + memset(ec, 0, sizeof(struct ethtool_coalesce)); + ec->rx_coalesce_usecs = pdata->rx_usecs; + ec->rx_max_coalesced_frames = pdata->rx_frames; + ec->tx_max_coalesced_frames = pdata->tx_frames; + + return 0; +} + +static int xlgmac_ethtool_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; + unsigned int rx_frames, rx_riwt, rx_usecs; + unsigned int tx_frames; + + /* Check for not supported parameters */ + if ((ec->rx_coalesce_usecs_irq) || (ec->rx_max_coalesced_frames_irq) || + (ec->tx_coalesce_usecs) || (ec->tx_coalesce_usecs_high) || + (ec->tx_max_coalesced_frames_irq) || (ec->tx_coalesce_usecs_irq) || + (ec->stats_block_coalesce_usecs) || (ec->pkt_rate_low) || + (ec->use_adaptive_rx_coalesce) || (ec->use_adaptive_tx_coalesce) || + (ec->rx_max_coalesced_frames_low) || (ec->rx_coalesce_usecs_low) || + (ec->tx_coalesce_usecs_low) || (ec->tx_max_coalesced_frames_low) || + (ec->pkt_rate_high) || (ec->rx_coalesce_usecs_high) || + (ec->rx_max_coalesced_frames_high) || + (ec->tx_max_coalesced_frames_high) || + (ec->rate_sample_interval)) + return -EOPNOTSUPP; + + rx_usecs = ec->rx_coalesce_usecs; + rx_riwt = hw_ops->usec_to_riwt(pdata, rx_usecs); + rx_frames = ec->rx_max_coalesced_frames; + tx_frames = ec->tx_max_coalesced_frames; + + if ((rx_riwt > XLGMAC_MAX_DMA_RIWT) || + (rx_riwt < XLGMAC_MIN_DMA_RIWT) || + (rx_frames > pdata->rx_desc_count)) + return -EINVAL; + + if (tx_frames > pdata->tx_desc_count) + return -EINVAL; + + pdata->rx_riwt = rx_riwt; + pdata->rx_usecs = rx_usecs; + pdata->rx_frames = rx_frames; + hw_ops->config_rx_coalesce(pdata); + + pdata->tx_frames = tx_frames; + hw_ops->config_tx_coalesce(pdata); + + return 0; +} + +static void xlgmac_ethtool_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < XLGMAC_STATS_COUNT; i++) { + memcpy(data, xlgmac_gstring_stats[i].stat_string, + ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + break; + default: + WARN_ON(1); + break; + } +} + +static int xlgmac_ethtool_get_sset_count(struct net_device *netdev, + int stringset) +{ + int ret; + + switch (stringset) { + case ETH_SS_STATS: + ret = XLGMAC_STATS_COUNT; + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static void xlgmac_ethtool_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, + u64 *data) +{ + struct xlgmac_pdata *pdata = netdev_priv(netdev); + u8 *stat; + int i; + + pdata->hw_ops.read_mmc_stats(pdata); + for (i = 0; i < XLGMAC_STATS_COUNT; i++) { + stat = (u8 *)pdata + xlgmac_gstring_stats[i].stat_offset; + *data++ = *(u64 *)stat; + } +} + +static const struct ethtool_ops xlgmac_ethtool_ops = { + .get_drvinfo = xlgmac_ethtool_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_msglevel = xlgmac_ethtool_get_msglevel, + .set_msglevel = xlgmac_ethtool_set_msglevel, + .get_channels = xlgmac_ethtool_get_channels, + .get_coalesce = xlgmac_ethtool_get_coalesce, + .set_coalesce = xlgmac_ethtool_set_coalesce, + .get_strings = xlgmac_ethtool_get_strings, + .get_sset_count = xlgmac_ethtool_get_sset_count, + .get_ethtool_stats = xlgmac_ethtool_get_ethtool_stats, +}; + +const struct ethtool_ops *xlgmac_get_ethtool_ops(void) +{ + return &xlgmac_ethtool_ops; +} diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c index 0dec1dcf8457..458a7844260a 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-hw.c @@ -835,12 +835,14 @@ static void xlgmac_dev_xmit(struct xlgmac_channel *channel) desc_data->skb_dma_len); /* VLAN tag insertion check */ - if (vlan) + if (vlan) { dma_desc->desc2 = XLGMAC_SET_REG_BITS_LE( dma_desc->desc2, TX_NORMAL_DESC2_VTIR_POS, TX_NORMAL_DESC2_VTIR_LEN, TX_NORMAL_DESC2_VLAN_INSERT); + pdata->stats.tx_vlan_packets++; + } /* Timestamp enablement check */ if (XLGMAC_GET_REG_BITS(pkt_info->attributes, diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c index 6acf86ced61d..3b91257683bc 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c @@ -290,19 +290,34 @@ static irqreturn_t xlgmac_isr(int irq, void *data) /* Disable Tx and Rx interrupts */ xlgmac_disable_rx_tx_ints(pdata); + pdata->stats.napi_poll_isr++; /* Turn on polling */ __napi_schedule_irqoff(&pdata->napi); } } + if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TPS_POS, + DMA_CH_SR_TPS_LEN)) + pdata->stats.tx_process_stopped++; + + if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RPS_POS, + DMA_CH_SR_RPS_LEN)) + pdata->stats.rx_process_stopped++; + + if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TBU_POS, + DMA_CH_SR_TBU_LEN)) + pdata->stats.tx_buffer_unavailable++; + if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RBU_POS, DMA_CH_SR_RBU_LEN)) pdata->stats.rx_buffer_unavailable++; /* Restart the device on a Fatal Bus Error */ if (XLGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_FBE_POS, - DMA_CH_SR_FBE_LEN)) + DMA_CH_SR_FBE_LEN)) { + pdata->stats.fatal_bus_error++; schedule_work(&pdata->restart_work); + } /* Clear all interrupt signals */ writel(dma_ch_isr, XLGMAC_DMA_REG(channel, DMA_CH_SR)); @@ -357,6 +372,7 @@ static void xlgmac_tx_timer(unsigned long data) else xlgmac_disable_rx_tx_ints(pdata); + pdata->stats.napi_poll_txtimer++; /* Turn on polling */ __napi_schedule(napi); } @@ -1225,9 +1241,11 @@ read_again: if (XLGMAC_GET_REG_BITS(pkt_info->attributes, RX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, - RX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN)) + RX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN)) { __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), pkt_info->vlan_ctag); + pdata->stats.rx_vlan_packets++; + } if (XLGMAC_GET_REG_BITS(pkt_info->attributes, RX_PACKET_ATTRIBUTES_RSS_HASH_POS, diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac.h b/drivers/net/ethernet/synopsys/dwc-xlgmac.h index 676b2fb8dfcc..cab3e40a86b9 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac.h +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac.h @@ -67,6 +67,8 @@ #define XLGMAC_INIT_DMA_TX_FRAMES 25 #define XLGMAC_INIT_DMA_RX_USECS 30 #define XLGMAC_INIT_DMA_RX_FRAMES 25 +#define XLGMAC_MAX_DMA_RIWT 0xff +#define XLGMAC_MIN_DMA_RIWT 0x01 /* Flow control queue count */ #define XLGMAC_MAX_FLOW_CONTROL_QUEUES 8 @@ -190,7 +192,15 @@ struct xlgmac_stats { /* Extra counters */ u64 tx_tso_packets; u64 rx_split_header_packets; + u64 tx_process_stopped; + u64 rx_process_stopped; + u64 tx_buffer_unavailable; u64 rx_buffer_unavailable; + u64 fatal_bus_error; + u64 tx_vlan_packets; + u64 rx_vlan_packets; + u64 napi_poll_isr; + u64 napi_poll_txtimer; }; struct xlgmac_ring_buf { @@ -622,6 +632,7 @@ struct xlgmac_pdata { void xlgmac_init_desc_ops(struct xlgmac_desc_ops *desc_ops); void xlgmac_init_hw_ops(struct xlgmac_hw_ops *hw_ops); const struct net_device_ops *xlgmac_get_netdev_ops(void); +const struct ethtool_ops *xlgmac_get_ethtool_ops(void); void xlgmac_dump_tx_desc(struct xlgmac_pdata *pdata, struct xlgmac_ring *ring, unsigned int idx, -- cgit From fb9eb899a6dc663e4a2deed9af2ac28f507d0ffb Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Tue, 11 Apr 2017 22:36:00 -0700 Subject: bonding: handle link transition from FAIL to UP correctly When link transitions from LINK_FAIL to LINK_UP, the commit phase is not called. This leads to an erroneous state causing slave-link state to get stuck in "going down" state while its speed and duplex are perfectly fine. This issue is a side-effect of splitting link-set into propose and commit phases introduced by de77ecd4ef02 ("bonding: improve link-status update in mii-monitoring") This patch fixes these issues by calling commit phase whenever link state change is proposed. Fixes: de77ecd4ef02 ("bonding: improve link-status update in mii-monitoring") Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index aba7352906a5..01e4a69af421 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2064,6 +2064,7 @@ static int bond_miimon_inspect(struct bonding *bond) (bond->params.downdelay - slave->delay) * bond->params.miimon, slave->dev->name); + commit++; continue; } @@ -2098,7 +2099,7 @@ static int bond_miimon_inspect(struct bonding *bond) (bond->params.updelay - slave->delay) * bond->params.miimon, slave->dev->name); - + commit++; continue; } -- cgit From 2d4bc93368f5a0ddb57c8c885cdad9c9b7a10ed5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 14:34:04 +0200 Subject: netlink: extended ACK reporting Add the base infrastructure and UAPI for netlink extended ACK reporting. All "manual" calls to netlink_ack() pass NULL for now and thus don't get extended ACK reporting. Big thanks goes to Pablo Neira Ayuso for not only bringing up the whole topic at netconf (again) but also coming up with the nlattr passing trick and various other ideas. Signed-off-by: Johannes Berg Reviewed-by: David Ahern Signed-off-by: David S. Miller --- crypto/crypto_user.c | 3 +- drivers/infiniband/core/netlink.c | 5 +-- drivers/scsi/scsi_netlink.c | 2 +- include/linux/netlink.h | 26 +++++++++++++- include/net/netlink.h | 3 +- include/uapi/linux/netlink.h | 32 ++++++++++++++++++ kernel/audit.c | 2 +- net/core/rtnetlink.c | 3 +- net/core/sock_diag.c | 3 +- net/decnet/netfilter/dn_rtmsg.c | 2 +- net/hsr/hsr_netlink.c | 4 +-- net/netfilter/ipset/ip_set_core.c | 2 +- net/netfilter/nfnetlink.c | 22 ++++++------ net/netlink/af_netlink.c | 71 ++++++++++++++++++++++++++++++++++----- net/netlink/af_netlink.h | 1 + net/netlink/genetlink.c | 3 +- net/xfrm/xfrm_user.c | 3 +- 17 files changed, 153 insertions(+), 34 deletions(-) diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index a90404a0c5ff..4a44830741c1 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -483,7 +483,8 @@ static const struct crypto_link { [CRYPTO_MSG_DELRNG - CRYPTO_MSG_BASE] = { .doit = crypto_del_rng }, }; -static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct nlattr *attrs[CRYPTOCFGA_MAX+1]; const struct crypto_link *link; diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c index 10469b0088b5..b784055423c8 100644 --- a/drivers/infiniband/core/netlink.c +++ b/drivers/infiniband/core/netlink.c @@ -146,7 +146,8 @@ nla_put_failure: } EXPORT_SYMBOL(ibnl_put_attr); -static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct ibnl_client *client; int type = nlh->nlmsg_type; @@ -209,7 +210,7 @@ static void ibnl_rcv_reply_skb(struct sk_buff *skb) if (nlh->nlmsg_flags & NLM_F_REQUEST) return; - ibnl_rcv_msg(skb, nlh); + ibnl_rcv_msg(skb, nlh, NULL); msglen = NLMSG_ALIGN(nlh->nlmsg_len); if (msglen > skb->len) diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c index 109802f776ed..50e624fb8307 100644 --- a/drivers/scsi/scsi_netlink.c +++ b/drivers/scsi/scsi_netlink.c @@ -111,7 +111,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb) next_msg: if ((err) || (nlh->nlmsg_flags & NLM_F_ACK)) - netlink_ack(skb, nlh, err); + netlink_ack(skb, nlh, err, NULL); skb_pull(skb, rlen); } diff --git a/include/linux/netlink.h b/include/linux/netlink.h index da14ab61f363..60e7137f840d 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -62,11 +62,35 @@ netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg) return __netlink_kernel_create(net, unit, THIS_MODULE, cfg); } +/** + * struct netlink_ext_ack - netlink extended ACK report struct + * @_msg: message string to report - don't access directly, use + * %NL_SET_ERR_MSG + * @bad_attr: attribute with error + */ +struct netlink_ext_ack { + const char *_msg; + const struct nlattr *bad_attr; +}; + +/* Always use this macro, this allows later putting the + * message into a separate section or such for things + * like translation or listing all possible messages. + * Currently string formatting is not supported (due + * to the lack of an output buffer.) + */ +#define NL_SET_ERR_MSG(extack, msg) do { \ + static const char _msg[] = (msg); \ + \ + (extack)->_msg = _msg; \ +} while (0) + extern void netlink_kernel_release(struct sock *sk); extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups); extern int netlink_change_ngroups(struct sock *sk, unsigned int groups); extern void __netlink_clear_multicast_users(struct sock *sk, unsigned int group); -extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err); +extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err, + const struct netlink_ext_ack *extack); extern int netlink_has_listeners(struct sock *sk, unsigned int group); extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock); diff --git a/include/net/netlink.h b/include/net/netlink.h index b239fcd33d80..a064ec3e2ee1 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -233,7 +233,8 @@ struct nl_info { }; int netlink_rcv_skb(struct sk_buff *skb, - int (*cb)(struct sk_buff *, struct nlmsghdr *)); + int (*cb)(struct sk_buff *, struct nlmsghdr *, + struct netlink_ext_ack *)); int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 portid, unsigned int group, int report, gfp_t flags); diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h index b2c9c26ea30f..7df88770e029 100644 --- a/include/uapi/linux/netlink.h +++ b/include/uapi/linux/netlink.h @@ -69,6 +69,10 @@ struct nlmsghdr { #define NLM_F_CREATE 0x400 /* Create, if it does not exist */ #define NLM_F_APPEND 0x800 /* Add to end of list */ +/* Flags for ACK message */ +#define NLM_F_CAPPED 0x100 /* request was capped */ +#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */ + /* 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL 4.4BSD CHANGE NLM_F_REPLACE @@ -101,6 +105,33 @@ struct nlmsghdr { struct nlmsgerr { int error; struct nlmsghdr msg; + /* + * followed by the message contents unless NETLINK_CAP_ACK was set + * or the ACK indicates success (error == 0) + * message length is aligned with NLMSG_ALIGN() + */ + /* + * followed by TLVs defined in enum nlmsgerr_attrs + * if NETLINK_EXT_ACK was set + */ +}; + +/** + * enum nlmsgerr_attrs - nlmsgerr attributes + * @NLMSGERR_ATTR_UNUSED: unused + * @NLMSGERR_ATTR_MSG: error message string (string) + * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original + * message, counting from the beginning of the header (u32) + * @__NLMSGERR_ATTR_MAX: number of attributes + * @NLMSGERR_ATTR_MAX: highest attribute number + */ +enum nlmsgerr_attrs { + NLMSGERR_ATTR_UNUSED, + NLMSGERR_ATTR_MSG, + NLMSGERR_ATTR_OFFS, + + __NLMSGERR_ATTR_MAX, + NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1 }; #define NETLINK_ADD_MEMBERSHIP 1 @@ -115,6 +146,7 @@ struct nlmsgerr { #define NETLINK_LISTEN_ALL_NSID 8 #define NETLINK_LIST_MEMBERSHIPS 9 #define NETLINK_CAP_ACK 10 +#define NETLINK_EXT_ACK 11 struct nl_pktinfo { __u32 group; diff --git a/kernel/audit.c b/kernel/audit.c index 2f4964cfde0b..d54bf5932374 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1402,7 +1402,7 @@ static void audit_receive_skb(struct sk_buff *skb) err = audit_receive_msg(skb, nlh); /* if err or if this message says it wants a response */ if (err || (nlh->nlmsg_flags & NLM_F_ACK)) - netlink_ack(skb, nlh, err); + netlink_ack(skb, nlh, err, NULL); nlh = nlmsg_next(nlh, &len); } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index c138b6b75e59..3cc4a627a537 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4046,7 +4046,8 @@ out: /* Process one rtnetlink message. */ -static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); rtnl_doit_func doit; diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index fb9d0e2fd148..217f4e3b82f6 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -238,7 +238,8 @@ static int __sock_diag_cmd(struct sk_buff *skb, struct nlmsghdr *nlh) return err; } -static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { int ret; diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c index 85f2fdc360c2..c8bf5136a72b 100644 --- a/net/decnet/netfilter/dn_rtmsg.c +++ b/net/decnet/netfilter/dn_rtmsg.c @@ -96,7 +96,7 @@ static unsigned int dnrmg_hook(void *priv, } -#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) +#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err), NULL); return; } while (0) static inline void dnrmg_receive_user_skb(struct sk_buff *skb) { diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c index 1ab30e7d3f99..81dac16933fc 100644 --- a/net/hsr/hsr_netlink.c +++ b/net/hsr/hsr_netlink.c @@ -350,7 +350,7 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) return 0; invalid: - netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL); + netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL); return 0; nla_put_failure: @@ -432,7 +432,7 @@ static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info) return 0; invalid: - netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL); + netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL); return 0; nla_put_failure: diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index c296f9b606d4..26356bf8cebf 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1305,7 +1305,7 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) * manually :-( */ if (nlh->nlmsg_flags & NLM_F_ACK) - netlink_ack(cb->skb, nlh, ret); + netlink_ack(cb->skb, nlh, ret, NULL); return ret; } } diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 68eda920160e..181d3bb800e6 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -148,7 +148,8 @@ int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid, EXPORT_SYMBOL_GPL(nfnetlink_unicast); /* Process one complete nfnetlink message. */ -static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); const struct nfnl_callback *nc; @@ -261,7 +262,7 @@ static void nfnl_err_deliver(struct list_head *err_list, struct sk_buff *skb) struct nfnl_err *nfnl_err, *next; list_for_each_entry_safe(nfnl_err, next, err_list, head) { - netlink_ack(skb, nfnl_err->nlh, nfnl_err->err); + netlink_ack(skb, nfnl_err->nlh, nfnl_err->err, NULL); nfnl_err_del(nfnl_err); } } @@ -284,13 +285,13 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, int err; if (subsys_id >= NFNL_SUBSYS_COUNT) - return netlink_ack(skb, nlh, -EINVAL); + return netlink_ack(skb, nlh, -EINVAL, NULL); replay: status = 0; skb = netlink_skb_clone(oskb, GFP_KERNEL); if (!skb) - return netlink_ack(oskb, nlh, -ENOMEM); + return netlink_ack(oskb, nlh, -ENOMEM, NULL); nfnl_lock(subsys_id); ss = nfnl_dereference_protected(subsys_id); @@ -304,20 +305,20 @@ replay: #endif { nfnl_unlock(subsys_id); - netlink_ack(oskb, nlh, -EOPNOTSUPP); + netlink_ack(oskb, nlh, -EOPNOTSUPP, NULL); return kfree_skb(skb); } } if (!ss->commit || !ss->abort) { nfnl_unlock(subsys_id); - netlink_ack(oskb, nlh, -EOPNOTSUPP); + netlink_ack(oskb, nlh, -EOPNOTSUPP, NULL); return kfree_skb(skb); } if (genid && ss->valid_genid && !ss->valid_genid(net, genid)) { nfnl_unlock(subsys_id); - netlink_ack(oskb, nlh, -ERESTART); + netlink_ack(oskb, nlh, -ERESTART, NULL); return kfree_skb(skb); } @@ -407,7 +408,8 @@ ack: * pointing to the batch header. */ nfnl_err_reset(&err_list); - netlink_ack(oskb, nlmsg_hdr(oskb), -ENOMEM); + netlink_ack(oskb, nlmsg_hdr(oskb), -ENOMEM, + NULL); status |= NFNL_BATCH_FAILURE; goto done; } @@ -467,7 +469,7 @@ static void nfnetlink_rcv_skb_batch(struct sk_buff *skb, struct nlmsghdr *nlh) err = nla_parse(cda, NFNL_BATCH_MAX, attr, attrlen, nfnl_batch_policy); if (err < 0) { - netlink_ack(skb, nlh, err); + netlink_ack(skb, nlh, err, NULL); return; } if (cda[NFNL_BATCH_GENID]) @@ -493,7 +495,7 @@ static void nfnetlink_rcv(struct sk_buff *skb) return; if (!netlink_net_capable(skb, CAP_NET_ADMIN)) { - netlink_ack(skb, nlh, -EPERM); + netlink_ack(skb, nlh, -EPERM, NULL); return; } diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index fc232441cf23..c1564768000e 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1652,6 +1652,13 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, nlk->flags &= ~NETLINK_F_CAP_ACK; err = 0; break; + case NETLINK_EXT_ACK: + if (val) + nlk->flags |= NETLINK_F_EXT_ACK; + else + nlk->flags &= ~NETLINK_F_EXT_ACK; + err = 0; + break; default: err = -ENOPROTOOPT; } @@ -1736,6 +1743,15 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname, return -EFAULT; err = 0; break; + case NETLINK_EXT_ACK: + if (len < sizeof(int)) + return -EINVAL; + len = sizeof(int); + val = nlk->flags & NETLINK_F_EXT_ACK ? 1 : 0; + if (put_user(len, optlen) || put_user(val, optval)) + return -EFAULT; + err = 0; + break; default: err = -ENOPROTOOPT; } @@ -2267,21 +2283,40 @@ error_free: } EXPORT_SYMBOL(__netlink_dump_start); -void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) +void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err, + const struct netlink_ext_ack *extack) { struct sk_buff *skb; struct nlmsghdr *rep; struct nlmsgerr *errmsg; size_t payload = sizeof(*errmsg); + size_t tlvlen = 0; struct netlink_sock *nlk = nlk_sk(NETLINK_CB(in_skb).sk); + unsigned int flags = 0; /* Error messages get the original request appened, unless the user - * requests to cap the error message. + * requests to cap the error message, and get extra error data if + * requested. */ - if (!(nlk->flags & NETLINK_F_CAP_ACK) && err) - payload += nlmsg_len(nlh); + if (err) { + if (!(nlk->flags & NETLINK_F_CAP_ACK)) + payload += nlmsg_len(nlh); + else + flags |= NLM_F_CAPPED; + if (nlk->flags & NETLINK_F_EXT_ACK && extack) { + if (extack->_msg) + tlvlen += nla_total_size(strlen(extack->_msg) + 1); + if (extack->bad_attr) + tlvlen += nla_total_size(sizeof(u32)); + } + } else { + flags |= NLM_F_CAPPED; + } - skb = nlmsg_new(payload, GFP_KERNEL); + if (tlvlen) + flags |= NLM_F_ACK_TLVS; + + skb = nlmsg_new(payload + tlvlen, GFP_KERNEL); if (!skb) { struct sock *sk; @@ -2297,17 +2332,35 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) } rep = __nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, - NLMSG_ERROR, payload, 0); + NLMSG_ERROR, payload, flags); errmsg = nlmsg_data(rep); errmsg->error = err; memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh)); + + if (err && nlk->flags & NETLINK_F_EXT_ACK && extack) { + if (extack->_msg) + WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG, + extack->_msg)); + if (extack->bad_attr && + !WARN_ON((u8 *)extack->bad_attr < in_skb->data || + (u8 *)extack->bad_attr >= in_skb->data + + in_skb->len)) + WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS, + (u8 *)extack->bad_attr - + in_skb->data)); + } + + nlmsg_end(skb, rep); + netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid, MSG_DONTWAIT); } EXPORT_SYMBOL(netlink_ack); int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, - struct nlmsghdr *)) + struct nlmsghdr *, + struct netlink_ext_ack *)) { + struct netlink_ext_ack extack = {}; struct nlmsghdr *nlh; int err; @@ -2328,13 +2381,13 @@ int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, if (nlh->nlmsg_type < NLMSG_MIN_TYPE) goto ack; - err = cb(skb, nlh); + err = cb(skb, nlh, &extack); if (err == -EINTR) goto skip; ack: if (nlh->nlmsg_flags & NLM_F_ACK || err) - netlink_ack(skb, nlh, err); + netlink_ack(skb, nlh, err, &extack); skip: msglen = NLMSG_ALIGN(nlh->nlmsg_len); diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h index f792f8d7f982..3490f2430532 100644 --- a/net/netlink/af_netlink.h +++ b/net/netlink/af_netlink.h @@ -13,6 +13,7 @@ #define NETLINK_F_RECV_NO_ENOBUFS 0x8 #define NETLINK_F_LISTEN_ALL_NSID 0x10 #define NETLINK_F_CAP_ACK 0x20 +#define NETLINK_F_EXT_ACK 0x40 #define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8) #define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long)) diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 92e0981f7404..57b2e3648bc0 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -605,7 +605,8 @@ out: return err; } -static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { const struct genl_family *family; int err; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 4f7e62ddc17e..e93d5c0471b2 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2448,7 +2448,8 @@ static const struct xfrm_link { [XFRM_MSG_GETSPDINFO - XFRM_MSG_BASE] = { .doit = xfrm_get_spdinfo }, }; -static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct nlattr *attrs[XFRMA_MAX+1]; -- cgit From 7ab606d1609dd6dfeae9c8ad0a8a4e051d831e46 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 14:34:05 +0200 Subject: genetlink: pass extended ACK report down Pass the extended ACK reporting struct down from generic netlink to the families, using the existing struct genl_info for simplicity. Also add support to set the extended ACK information from generic netlink users. Signed-off-by: Johannes Berg Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/genetlink.h | 12 ++++++++++++ net/netlink/genetlink.c | 6 ++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/include/net/genetlink.h b/include/net/genetlink.h index a34275be3600..f18db6570f52 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -84,6 +84,7 @@ struct nlattr **genl_family_attrbuf(const struct genl_family *family); * @attrs: netlink attributes * @_net: network namespace * @user_ptr: user pointers + * @extack: extended ACK report struct */ struct genl_info { u32 snd_seq; @@ -94,6 +95,7 @@ struct genl_info { struct nlattr ** attrs; possible_net_t _net; void * user_ptr[2]; + struct netlink_ext_ack *extack; }; static inline struct net *genl_info_net(struct genl_info *info) @@ -106,6 +108,16 @@ static inline void genl_info_net_set(struct genl_info *info, struct net *net) write_pnet(&info->_net, net); } +#define GENL_SET_ERR_MSG(info, msg) NL_SET_ERR_MSG((info)->extack, msg) + +static inline int genl_err_attr(struct genl_info *info, int err, + struct nlattr *attr) +{ + info->extack->bad_attr = attr; + + return err; +} + /** * struct genl_ops - generic netlink operations * @cmd: command identifier diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 57b2e3648bc0..4b598a5999a2 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -497,7 +497,8 @@ static int genl_lock_done(struct netlink_callback *cb) static int genl_family_rcv_msg(const struct genl_family *family, struct sk_buff *skb, - struct nlmsghdr *nlh) + struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { const struct genl_ops *ops; struct net *net = sock_net(skb->sk); @@ -584,6 +585,7 @@ static int genl_family_rcv_msg(const struct genl_family *family, info.genlhdr = nlmsg_data(nlh); info.userhdr = nlmsg_data(nlh) + GENL_HDRLEN; info.attrs = attrbuf; + info.extack = extack; genl_info_net_set(&info, net); memset(&info.user_ptr, 0, sizeof(info.user_ptr)); @@ -618,7 +620,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, if (!family->parallel_ops) genl_lock(); - err = genl_family_rcv_msg(family, skb, nlh); + err = genl_family_rcv_msg(family, skb, nlh, extack); if (!family->parallel_ops) genl_unlock(); -- cgit From ba0dc5f6e0ba5a5d2f575bcdb35e5d1960cf7c04 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 14:34:06 +0200 Subject: netlink: allow sending extended ACK with cookie on success Now that we have extended error reporting and a new message format for netlink ACK messages, also extend this to be able to return arbitrary cookie data on success. This will allow, for example, nl80211 to not send an extra message for cookies identifying newly created objects, but return those directly in the ACK message. The cookie data size is currently limited to 20 bytes (since Jamal talked about using SHA1 for identifiers.) Thanks to Jamal Hadi Salim for bringing up this idea during the discussions. Signed-off-by: Johannes Berg Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/netlink.h | 7 +++++++ include/uapi/linux/netlink.h | 4 ++++ net/netlink/af_netlink.c | 33 ++++++++++++++++++++++----------- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 60e7137f840d..8d2a8924705c 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -62,15 +62,22 @@ netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg) return __netlink_kernel_create(net, unit, THIS_MODULE, cfg); } +/* this can be increased when necessary - don't expose to userland */ +#define NETLINK_MAX_COOKIE_LEN 20 + /** * struct netlink_ext_ack - netlink extended ACK report struct * @_msg: message string to report - don't access directly, use * %NL_SET_ERR_MSG * @bad_attr: attribute with error + * @cookie: cookie data to return to userspace (for success) + * @cookie_len: actual cookie data length */ struct netlink_ext_ack { const char *_msg; const struct nlattr *bad_attr; + u8 cookie[NETLINK_MAX_COOKIE_LEN]; + u8 cookie_len; }; /* Always use this macro, this allows later putting the diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h index 7df88770e029..f86127a46cfc 100644 --- a/include/uapi/linux/netlink.h +++ b/include/uapi/linux/netlink.h @@ -122,6 +122,9 @@ struct nlmsgerr { * @NLMSGERR_ATTR_MSG: error message string (string) * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original * message, counting from the beginning of the header (u32) + * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to + * be used - in the success case - to identify a created + * object or operation or similar (binary) * @__NLMSGERR_ATTR_MAX: number of attributes * @NLMSGERR_ATTR_MAX: highest attribute number */ @@ -129,6 +132,7 @@ enum nlmsgerr_attrs { NLMSGERR_ATTR_UNUSED, NLMSGERR_ATTR_MSG, NLMSGERR_ATTR_OFFS, + NLMSGERR_ATTR_COOKIE, __NLMSGERR_ATTR_MAX, NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1 diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index c1564768000e..ee841f00a6ec 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2311,6 +2311,10 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err, } } else { flags |= NLM_F_CAPPED; + + if (nlk->flags & NETLINK_F_EXT_ACK && + extack && extack->cookie_len) + tlvlen += nla_total_size(extack->cookie_len); } if (tlvlen) @@ -2337,17 +2341,24 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err, errmsg->error = err; memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh)); - if (err && nlk->flags & NETLINK_F_EXT_ACK && extack) { - if (extack->_msg) - WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG, - extack->_msg)); - if (extack->bad_attr && - !WARN_ON((u8 *)extack->bad_attr < in_skb->data || - (u8 *)extack->bad_attr >= in_skb->data + - in_skb->len)) - WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS, - (u8 *)extack->bad_attr - - in_skb->data)); + if (nlk->flags & NETLINK_F_EXT_ACK && extack) { + if (err) { + if (extack->_msg) + WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG, + extack->_msg)); + if (extack->bad_attr && + !WARN_ON((u8 *)extack->bad_attr < in_skb->data || + (u8 *)extack->bad_attr >= in_skb->data + + in_skb->len)) + WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS, + (u8 *)extack->bad_attr - + in_skb->data)); + } else { + if (extack->cookie_len) + WARN_ON(nla_put(skb, NLMSGERR_ATTR_COOKIE, + extack->cookie_len, + extack->cookie)); + } } nlmsg_end(skb, rep); -- cgit From fceb6435e85298f747fee938415057af837f5a8a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 14:34:07 +0200 Subject: netlink: pass extended ACK struct to parsing functions Pass the new extended ACK reporting struct to all of the generic netlink parsing functions. For now, pass NULL in almost all callers (except for some in the core.) Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- crypto/crypto_user.c | 2 +- drivers/block/drbd/drbd_nla.c | 2 +- drivers/infiniband/core/addr.c | 2 +- drivers/infiniband/core/iwpm_util.c | 6 +- drivers/infiniband/core/sa_query.c | 4 +- drivers/net/macsec.c | 10 +-- drivers/net/team/team.c | 2 +- drivers/net/veth.c | 3 +- drivers/net/wireless/ath/ath10k/testmode.c | 4 +- drivers/net/wireless/ath/ath6kl/testmode.c | 4 +- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 +- drivers/net/wireless/mac80211_hwsim.c | 4 +- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 4 +- drivers/net/wireless/ti/wlcore/testmode.c | 3 +- drivers/net/wireless/ti/wlcore/vendor_cmd.c | 4 +- include/net/genetlink.h | 8 ++- include/net/netlink.h | 33 +++++++--- include/net/rtnetlink.h | 3 +- lib/nlattr.c | 28 +++++--- net/8021q/vlan_netlink.c | 3 +- net/bridge/br_mdb.c | 3 +- net/bridge/br_netlink.c | 4 +- net/bridge/br_netlink_tunnel.c | 4 +- net/can/gw.c | 2 +- net/core/fib_rules.c | 4 +- net/core/lwt_bpf.c | 5 +- net/core/neighbour.c | 8 +-- net/core/net_namespace.c | 4 +- net/core/rtnetlink.c | 47 ++++++++------ net/dcb/dcbnl.c | 57 ++++++++--------- net/decnet/dn_dev.c | 4 +- net/decnet/dn_fib.c | 6 +- net/decnet/dn_route.c | 2 +- net/ieee802154/nl802154.c | 29 ++++----- net/ipv4/devinet.c | 12 ++-- net/ipv4/fib_frontend.c | 3 +- net/ipv4/ip_tunnel_core.c | 5 +- net/ipv4/ipmr.c | 3 +- net/ipv4/route.c | 3 +- net/ipv6/addrconf.c | 16 +++-- net/ipv6/addrlabel.c | 4 +- net/ipv6/ila/ila_lwt.c | 3 +- net/ipv6/route.c | 6 +- net/ipv6/seg6_iptunnel.c | 2 +- net/mpls/af_mpls.c | 5 +- net/mpls/mpls_iptunnel.c | 2 +- net/netfilter/ipset/ip_set_core.c | 27 ++++---- net/netfilter/ipvs/ip_vs_ctl.c | 12 ++-- net/netfilter/nf_conntrack_netlink.c | 27 ++++---- net/netfilter/nf_conntrack_proto_dccp.c | 2 +- net/netfilter/nf_conntrack_proto_sctp.c | 6 +- net/netfilter/nf_conntrack_proto_tcp.c | 3 +- net/netfilter/nf_nat_core.c | 5 +- net/netfilter/nf_tables_api.c | 27 ++++---- net/netfilter/nfnetlink.c | 11 ++-- net/netfilter/nfnetlink_acct.c | 3 +- net/netfilter/nfnetlink_cthelper.c | 12 ++-- net/netfilter/nfnetlink_cttimeout.c | 3 +- net/netfilter/nfnetlink_queue.c | 2 +- net/netfilter/nft_compat.c | 2 +- net/netlabel/netlabel_cipso_v4.c | 19 +++--- net/netlink/genetlink.c | 2 +- net/nfc/netlink.c | 5 +- net/openvswitch/datapath.c | 2 +- net/openvswitch/flow_netlink.c | 4 +- net/openvswitch/vport-vxlan.c | 3 +- net/phonet/pn_netlink.c | 6 +- net/qrtr/qrtr.c | 2 +- net/sched/act_api.c | 20 +++--- net/sched/act_bpf.c | 2 +- net/sched/act_connmark.c | 3 +- net/sched/act_csum.c | 2 +- net/sched/act_gact.c | 2 +- net/sched/act_ife.c | 4 +- net/sched/act_ipt.c | 2 +- net/sched/act_mirred.c | 2 +- net/sched/act_nat.c | 2 +- net/sched/act_pedit.c | 4 +- net/sched/act_police.c | 2 +- net/sched/act_sample.c | 2 +- net/sched/act_simple.c | 2 +- net/sched/act_skbedit.c | 2 +- net/sched/act_skbmod.c | 2 +- net/sched/act_tunnel_key.c | 3 +- net/sched/act_vlan.c | 2 +- net/sched/cls_api.c | 2 +- net/sched/cls_basic.c | 2 +- net/sched/cls_bpf.c | 3 +- net/sched/cls_cgroup.c | 2 +- net/sched/cls_flow.c | 2 +- net/sched/cls_flower.c | 3 +- net/sched/cls_fw.c | 2 +- net/sched/cls_matchall.c | 4 +- net/sched/cls_route.c | 2 +- net/sched/cls_rsvp.h | 2 +- net/sched/cls_tcindex.c | 2 +- net/sched/cls_u32.c | 2 +- net/sched/em_meta.c | 2 +- net/sched/ematch.c | 2 +- net/sched/sch_api.c | 10 +-- net/sched/sch_atm.c | 2 +- net/sched/sch_cbq.c | 4 +- net/sched/sch_choke.c | 2 +- net/sched/sch_codel.c | 2 +- net/sched/sch_drr.c | 2 +- net/sched/sch_dsmark.c | 4 +- net/sched/sch_fq.c | 2 +- net/sched/sch_fq_codel.c | 3 +- net/sched/sch_gred.c | 4 +- net/sched/sch_hfsc.c | 2 +- net/sched/sch_hhf.c | 2 +- net/sched/sch_htb.c | 4 +- net/sched/sch_netem.c | 2 +- net/sched/sch_pie.c | 2 +- net/sched/sch_qfq.c | 3 +- net/sched/sch_red.c | 2 +- net/sched/sch_sfb.c | 2 +- net/sched/sch_tbf.c | 2 +- net/switchdev/switchdev.c | 2 +- net/tipc/bearer.c | 14 ++-- net/tipc/link.c | 2 +- net/tipc/net.c | 4 +- net/tipc/netlink.c | 3 +- net/tipc/netlink_compat.c | 32 +++++----- net/tipc/node.c | 12 ++-- net/tipc/socket.c | 2 +- net/tipc/udp_media.c | 7 +- net/wireless/nl80211.c | 78 ++++++++++++----------- net/xfrm/xfrm_user.c | 6 +- 129 files changed, 477 insertions(+), 391 deletions(-) diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index 4a44830741c1..fc79906c1fe7 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -523,7 +523,7 @@ static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, } err = nlmsg_parse(nlh, crypto_msg_min[type], attrs, CRYPTOCFGA_MAX, - crypto_policy); + crypto_policy, NULL); if (err < 0) return err; diff --git a/drivers/block/drbd/drbd_nla.c b/drivers/block/drbd/drbd_nla.c index b2d4791498a6..6bf806df60dc 100644 --- a/drivers/block/drbd/drbd_nla.c +++ b/drivers/block/drbd/drbd_nla.c @@ -34,7 +34,7 @@ int drbd_nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, err = drbd_nla_check_mandatory(maxtype, nla); if (!err) - err = nla_parse_nested(tb, maxtype, nla, policy); + err = nla_parse_nested(tb, maxtype, nla, policy, NULL); return err; } diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 0f58f46dbad7..329d08c884f6 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -88,7 +88,7 @@ static inline bool ib_nl_is_good_ip_resp(const struct nlmsghdr *nlh) return false; ret = nla_parse(tb, LS_NLA_TYPE_MAX - 1, nlmsg_data(nlh), - nlmsg_len(nlh), ib_nl_addr_policy); + nlmsg_len(nlh), ib_nl_addr_policy, NULL); if (ret) return false; diff --git a/drivers/infiniband/core/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c index 3ef51a96bbf1..f13870e69ccd 100644 --- a/drivers/infiniband/core/iwpm_util.c +++ b/drivers/infiniband/core/iwpm_util.c @@ -472,12 +472,14 @@ int iwpm_parse_nlmsg(struct netlink_callback *cb, int policy_max, int ret; const char *err_str = ""; - ret = nlmsg_validate(cb->nlh, nlh_len, policy_max-1, nlmsg_policy); + ret = nlmsg_validate(cb->nlh, nlh_len, policy_max - 1, nlmsg_policy, + NULL); if (ret) { err_str = "Invalid attribute"; goto parse_nlmsg_error; } - ret = nlmsg_parse(cb->nlh, nlh_len, nltb, policy_max-1, nlmsg_policy); + ret = nlmsg_parse(cb->nlh, nlh_len, nltb, policy_max - 1, + nlmsg_policy, NULL); if (ret) { err_str = "Unable to parse the nlmsg"; goto parse_nlmsg_error; diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index 81b742ca1639..ceae153997d0 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -808,7 +808,7 @@ int ib_nl_handle_set_timeout(struct sk_buff *skb, return -EPERM; ret = nla_parse(tb, LS_NLA_TYPE_MAX - 1, nlmsg_data(nlh), - nlmsg_len(nlh), ib_nl_policy); + nlmsg_len(nlh), ib_nl_policy, NULL); attr = (const struct nlattr *)tb[LS_NLA_TYPE_TIMEOUT]; if (ret || !attr) goto settimeout_out; @@ -860,7 +860,7 @@ static inline int ib_nl_is_good_resolve_resp(const struct nlmsghdr *nlh) return 0; ret = nla_parse(tb, LS_NLA_TYPE_MAX - 1, nlmsg_data(nlh), - nlmsg_len(nlh), ib_nl_policy); + nlmsg_len(nlh), ib_nl_policy, NULL); if (ret) return 0; diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index ff0a5ed3ca80..9eb7a69be92a 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -1590,8 +1590,9 @@ static int parse_sa_config(struct nlattr **attrs, struct nlattr **tb_sa) if (!attrs[MACSEC_ATTR_SA_CONFIG]) return -EINVAL; - if (nla_parse_nested(tb_sa, MACSEC_SA_ATTR_MAX, attrs[MACSEC_ATTR_SA_CONFIG], - macsec_genl_sa_policy)) + if (nla_parse_nested(tb_sa, MACSEC_SA_ATTR_MAX, + attrs[MACSEC_ATTR_SA_CONFIG], + macsec_genl_sa_policy, NULL)) return -EINVAL; return 0; @@ -1602,8 +1603,9 @@ static int parse_rxsc_config(struct nlattr **attrs, struct nlattr **tb_rxsc) if (!attrs[MACSEC_ATTR_RXSC_CONFIG]) return -EINVAL; - if (nla_parse_nested(tb_rxsc, MACSEC_RXSC_ATTR_MAX, attrs[MACSEC_ATTR_RXSC_CONFIG], - macsec_genl_rxsc_policy)) + if (nla_parse_nested(tb_rxsc, MACSEC_RXSC_ATTR_MAX, + attrs[MACSEC_ATTR_RXSC_CONFIG], + macsec_genl_rxsc_policy, NULL)) return -EINVAL; return 0; diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 1b52520715ae..86f227124ba1 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -2471,7 +2471,7 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) goto team_put; } err = nla_parse_nested(opt_attrs, TEAM_ATTR_OPTION_MAX, - nl_option, team_nl_option_policy); + nl_option, team_nl_option_policy, NULL); if (err) goto team_put; if (!opt_attrs[TEAM_ATTR_OPTION_NAME] || diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 317103680675..38f0f03a29c8 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -368,7 +368,8 @@ static int veth_newlink(struct net *src_net, struct net_device *dev, ifmp = nla_data(nla_peer); err = rtnl_nla_parse_ifla(peer_tb, nla_data(nla_peer) + sizeof(struct ifinfomsg), - nla_len(nla_peer) - sizeof(struct ifinfomsg)); + nla_len(nla_peer) - sizeof(struct ifinfomsg), + NULL); if (err < 0) return err; diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c index 8bb36c18a749..d8564624415c 100644 --- a/drivers/net/wireless/ath/ath10k/testmode.c +++ b/drivers/net/wireless/ath/ath10k/testmode.c @@ -420,8 +420,8 @@ int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct nlattr *tb[ATH10K_TM_ATTR_MAX + 1]; int ret; - ret = nla_parse(tb, ATH10K_TM_ATTR_MAX, data, len, - ath10k_tm_policy); + ret = nla_parse(tb, ATH10K_TM_ATTR_MAX, data, len, ath10k_tm_policy, + NULL); if (ret) return ret; diff --git a/drivers/net/wireless/ath/ath6kl/testmode.c b/drivers/net/wireless/ath/ath6kl/testmode.c index d67170ea1038..d8dcacda9add 100644 --- a/drivers/net/wireless/ath/ath6kl/testmode.c +++ b/drivers/net/wireless/ath/ath6kl/testmode.c @@ -74,8 +74,8 @@ int ath6kl_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, int err, buf_len; void *buf; - err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len, - ath6kl_tm_policy); + err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len, ath6kl_tm_policy, + NULL); if (err) return err; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 486dcceed17a..841bfdff8750 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3711,7 +3711,8 @@ static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm, int err; u32 noa_duration; - err = nla_parse(tb, IWL_MVM_TM_ATTR_MAX, data, len, iwl_mvm_tm_policy); + err = nla_parse(tb, IWL_MVM_TM_ATTR_MAX, data, len, iwl_mvm_tm_policy, + NULL); if (err) return err; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 50c219fb1a52..84a0e242ffdb 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -389,7 +389,7 @@ static int mac80211_hwsim_vendor_cmd_test(struct wiphy *wiphy, u32 val; err = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len, - hwsim_vendor_test_policy); + hwsim_vendor_test_policy, NULL); if (err) return err; if (!tb[QCA_WLAN_VENDOR_ATTR_TEST]) @@ -1852,7 +1852,7 @@ static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, int err, ps; err = nla_parse(tb, HWSIM_TM_ATTR_MAX, data, len, - hwsim_testmode_policy); + hwsim_testmode_policy, NULL); if (err) return err; diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 44d06177859e..252e802df8fe 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -4016,8 +4016,8 @@ static int mwifiex_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, if (!priv) return -EINVAL; - err = nla_parse(tb, MWIFIEX_TM_ATTR_MAX, data, len, - mwifiex_tm_policy); + err = nla_parse(tb, MWIFIEX_TM_ATTR_MAX, data, len, mwifiex_tm_policy, + NULL); if (err) return err; diff --git a/drivers/net/wireless/ti/wlcore/testmode.c b/drivers/net/wireless/ti/wlcore/testmode.c index ddad58f614da..009ec07c4cec 100644 --- a/drivers/net/wireless/ti/wlcore/testmode.c +++ b/drivers/net/wireless/ti/wlcore/testmode.c @@ -366,7 +366,8 @@ int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 nla_cmd; int err; - err = nla_parse(tb, WL1271_TM_ATTR_MAX, data, len, wl1271_tm_policy); + err = nla_parse(tb, WL1271_TM_ATTR_MAX, data, len, wl1271_tm_policy, + NULL); if (err) return err; diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.c b/drivers/net/wireless/ti/wlcore/vendor_cmd.c index fd4e9ba176c9..5c0bcb1fe1a1 100644 --- a/drivers/net/wireless/ti/wlcore/vendor_cmd.c +++ b/drivers/net/wireless/ti/wlcore/vendor_cmd.c @@ -41,7 +41,7 @@ wlcore_vendor_cmd_smart_config_start(struct wiphy *wiphy, return -EINVAL; ret = nla_parse(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len, - wlcore_vendor_attr_policy); + wlcore_vendor_attr_policy, NULL); if (ret) return ret; @@ -116,7 +116,7 @@ wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy *wiphy, return -EINVAL; ret = nla_parse(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len, - wlcore_vendor_attr_policy); + wlcore_vendor_attr_policy, NULL); if (ret) return ret; diff --git a/include/net/genetlink.h b/include/net/genetlink.h index f18db6570f52..68b88192b00c 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -174,14 +174,16 @@ genlmsg_nlhdr(void *user_hdr, const struct genl_family *family) * @tb: destination array with maxtype+1 elements * @maxtype: maximum attribute type to be expected * @policy: validation policy - * */ + * @extack: extended ACK report struct + */ static inline int genlmsg_parse(const struct nlmsghdr *nlh, const struct genl_family *family, struct nlattr *tb[], int maxtype, - const struct nla_policy *policy) + const struct nla_policy *policy, + struct netlink_ext_ack *extack) { return nlmsg_parse(nlh, family->hdrsize + GENL_HDRLEN, tb, maxtype, - policy); + policy, extack); } /** diff --git a/include/net/netlink.h b/include/net/netlink.h index a064ec3e2ee1..01709172b3d3 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -239,9 +239,11 @@ int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 portid, unsigned int group, int report, gfp_t flags); int nla_validate(const struct nlattr *head, int len, int maxtype, - const struct nla_policy *policy); + const struct nla_policy *policy, + struct netlink_ext_ack *extack); int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head, - int len, const struct nla_policy *policy); + int len, const struct nla_policy *policy, + struct netlink_ext_ack *extack); int nla_policy_len(const struct nla_policy *, int); struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype); size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize); @@ -375,18 +377,20 @@ nlmsg_next(const struct nlmsghdr *nlh, int *remaining) * @tb: destination array with maxtype+1 elements * @maxtype: maximum attribute type to be expected * @policy: validation policy + * @extack: extended ACK report struct * * See nla_parse() */ static inline int nlmsg_parse(const struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, - const struct nla_policy *policy) + const struct nla_policy *policy, + struct netlink_ext_ack *extack) { if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) return -EINVAL; return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen), - nlmsg_attrlen(nlh, hdrlen), policy); + nlmsg_attrlen(nlh, hdrlen), policy, extack); } /** @@ -410,16 +414,19 @@ static inline struct nlattr *nlmsg_find_attr(const struct nlmsghdr *nlh, * @hdrlen: length of familiy specific header * @maxtype: maximum attribute type to be expected * @policy: validation policy + * @extack: extended ACK report struct */ static inline int nlmsg_validate(const struct nlmsghdr *nlh, int hdrlen, int maxtype, - const struct nla_policy *policy) + const struct nla_policy *policy, + struct netlink_ext_ack *extack) { if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) return -EINVAL; return nla_validate(nlmsg_attrdata(nlh, hdrlen), - nlmsg_attrlen(nlh, hdrlen), maxtype, policy); + nlmsg_attrlen(nlh, hdrlen), maxtype, policy, + extack); } /** @@ -740,14 +747,17 @@ nla_find_nested(const struct nlattr *nla, int attrtype) * @maxtype: maximum attribute type to be expected * @nla: attribute containing the nested attributes * @policy: validation policy + * @extack: extended ACK report struct * * See nla_parse() */ static inline int nla_parse_nested(struct nlattr *tb[], int maxtype, const struct nlattr *nla, - const struct nla_policy *policy) + const struct nla_policy *policy, + struct netlink_ext_ack *extack) { - return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); + return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy, + extack); } /** @@ -1253,6 +1263,7 @@ static inline void nla_nest_cancel(struct sk_buff *skb, struct nlattr *start) * @start: container attribute * @maxtype: maximum attribute type to be expected * @policy: validation policy + * @extack: extended ACK report struct * * Validates all attributes in the nested attribute stream against the * specified policy. Attributes with a type exceeding maxtype will be @@ -1261,9 +1272,11 @@ static inline void nla_nest_cancel(struct sk_buff *skb, struct nlattr *start) * Returns 0 on success or a negative error code. */ static inline int nla_validate_nested(const struct nlattr *start, int maxtype, - const struct nla_policy *policy) + const struct nla_policy *policy, + struct netlink_ext_ack *extack) { - return nla_validate(nla_data(start), nla_len(start), maxtype, policy); + return nla_validate(nla_data(start), nla_len(start), maxtype, policy, + extack); } /** diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index 106de5f7bf06..c07b941fce89 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -158,7 +158,8 @@ struct net_device *rtnl_create_link(struct net *net, const char *ifname, int rtnl_delete_link(struct net_device *dev); int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm); -int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len); +int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len, + struct netlink_ext_ack *exterr); #define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind) diff --git a/lib/nlattr.c b/lib/nlattr.c index b42b8577fc23..a7e0b16078df 100644 --- a/lib/nlattr.c +++ b/lib/nlattr.c @@ -112,6 +112,7 @@ static int validate_nla(const struct nlattr *nla, int maxtype, * @len: length of attribute stream * @maxtype: maximum attribute type to be expected * @policy: validation policy + * @extack: extended ACK report struct * * Validates all attributes in the specified attribute stream against the * specified policy. Attributes with a type exceeding maxtype will be @@ -120,20 +121,23 @@ static int validate_nla(const struct nlattr *nla, int maxtype, * Returns 0 on success or a negative error code. */ int nla_validate(const struct nlattr *head, int len, int maxtype, - const struct nla_policy *policy) + const struct nla_policy *policy, + struct netlink_ext_ack *extack) { const struct nlattr *nla; - int rem, err; + int rem; nla_for_each_attr(nla, head, len, rem) { - err = validate_nla(nla, maxtype, policy); - if (err < 0) - goto errout; + int err = validate_nla(nla, maxtype, policy); + + if (err < 0) { + if (extack) + extack->bad_attr = nla; + return err; + } } - err = 0; -errout: - return err; + return 0; } EXPORT_SYMBOL(nla_validate); @@ -180,7 +184,8 @@ EXPORT_SYMBOL(nla_policy_len); * Returns 0 on success or a negative error code. */ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head, - int len, const struct nla_policy *policy) + int len, const struct nla_policy *policy, + struct netlink_ext_ack *extack) { const struct nlattr *nla; int rem, err; @@ -193,8 +198,11 @@ int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head, if (type > 0 && type <= maxtype) { if (policy) { err = validate_nla(nla, maxtype, policy); - if (err < 0) + if (err < 0) { + if (extack) + extack->bad_attr = nla; goto errout; + } } tb[type] = (struct nlattr *)nla; diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index 1270207f3d7c..9c94aad153b3 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c @@ -35,7 +35,8 @@ static inline int vlan_validate_qos_map(struct nlattr *attr) { if (!attr) return 0; - return nla_validate_nested(attr, IFLA_VLAN_QOS_MAX, vlan_map_policy); + return nla_validate_nested(attr, IFLA_VLAN_QOS_MAX, vlan_map_policy, + NULL); } static int vlan_validate(struct nlattr *tb[], struct nlattr *data[]) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 056e6ac49d8f..993626a7fc3b 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -464,7 +464,8 @@ static int br_mdb_parse(struct sk_buff *skb, struct nlmsghdr *nlh, struct net_device *dev; int err; - err = nlmsg_parse(nlh, sizeof(*bpm), tb, MDBA_SET_ENTRY_MAX, NULL); + err = nlmsg_parse(nlh, sizeof(*bpm), tb, MDBA_SET_ENTRY_MAX, NULL, + NULL); if (err < 0) return err; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index a8f6acd23e30..e6dea5cd6bd6 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -748,8 +748,8 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags) if (p && protinfo) { if (protinfo->nla_type & NLA_F_NESTED) { - err = nla_parse_nested(tb, IFLA_BRPORT_MAX, - protinfo, br_port_policy); + err = nla_parse_nested(tb, IFLA_BRPORT_MAX, protinfo, + br_port_policy, NULL); if (err) return err; diff --git a/net/bridge/br_netlink_tunnel.c b/net/bridge/br_netlink_tunnel.c index c913491495ab..3712c7f0e00c 100644 --- a/net/bridge/br_netlink_tunnel.c +++ b/net/bridge/br_netlink_tunnel.c @@ -227,8 +227,8 @@ int br_parse_vlan_tunnel_info(struct nlattr *attr, memset(tinfo, 0, sizeof(*tinfo)); - err = nla_parse_nested(tb, IFLA_BRIDGE_VLAN_TUNNEL_MAX, - attr, vlan_tunnel_policy); + err = nla_parse_nested(tb, IFLA_BRIDGE_VLAN_TUNNEL_MAX, attr, + vlan_tunnel_policy, NULL); if (err < 0) return err; diff --git a/net/can/gw.c b/net/can/gw.c index 3c117a33e15f..3b84fb7d98aa 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -641,7 +641,7 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, memset(mod, 0, sizeof(*mod)); err = nlmsg_parse(nlh, sizeof(struct rtcanmsg), tb, CGW_MAX, - cgw_policy); + cgw_policy, NULL); if (err < 0) return err; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 816e3ccb0ec9..df03110ca3c8 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -386,7 +386,7 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh) goto errout; } - err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy); + err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy, NULL); if (err < 0) goto errout; @@ -580,7 +580,7 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh) goto errout; } - err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy); + err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy, NULL); if (err < 0) goto errout; diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c index 0cfe7b0216c3..b3bc0a31af9f 100644 --- a/net/core/lwt_bpf.c +++ b/net/core/lwt_bpf.c @@ -209,7 +209,8 @@ static int bpf_parse_prog(struct nlattr *attr, struct bpf_lwt_prog *prog, int ret; u32 fd; - ret = nla_parse_nested(tb, LWT_BPF_PROG_MAX, attr, bpf_prog_policy); + ret = nla_parse_nested(tb, LWT_BPF_PROG_MAX, attr, bpf_prog_policy, + NULL); if (ret < 0) return ret; @@ -249,7 +250,7 @@ static int bpf_build_state(struct nlattr *nla, if (family != AF_INET && family != AF_INET6) return -EAFNOSUPPORT; - ret = nla_parse_nested(tb, LWT_BPF_MAX, nla, bpf_nl_policy); + ret = nla_parse_nested(tb, LWT_BPF_MAX, nla, bpf_nl_policy, NULL); if (ret < 0) return ret; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 8ae87c591c8e..31f37b264710 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1661,7 +1661,7 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh) int err; ASSERT_RTNL(); - err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); + err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, NULL); if (err < 0) goto out; @@ -1946,7 +1946,7 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh) int err, tidx; err = nlmsg_parse(nlh, sizeof(*ndtmsg), tb, NDTA_MAX, - nl_neightbl_policy); + nl_neightbl_policy, NULL); if (err < 0) goto errout; @@ -1984,7 +1984,7 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh) int i, ifindex = 0; err = nla_parse_nested(tbp, NDTPA_MAX, tb[NDTA_PARMS], - nl_ntbl_parm_policy); + nl_ntbl_parm_policy, NULL); if (err < 0) goto errout_tbl_lock; @@ -2275,7 +2275,7 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, unsigned int flags = NLM_F_MULTI; int err; - err = nlmsg_parse(nlh, sizeof(struct ndmsg), tb, NDA_MAX, NULL); + err = nlmsg_parse(nlh, sizeof(struct ndmsg), tb, NDA_MAX, NULL, NULL); if (!err) { if (tb[NDA_IFINDEX]) filter_idx = nla_get_u32(tb[NDA_IFINDEX]); diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 652468ff65b7..ec18cbc756d2 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -579,7 +579,7 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh) int nsid, err; err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, - rtnl_net_policy); + rtnl_net_policy, NULL); if (err < 0) return err; if (!tb[NETNSA_NSID]) @@ -653,7 +653,7 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh) int err, id; err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, - rtnl_net_policy); + rtnl_net_policy, NULL); if (err < 0) return err; if (tb[NETNSA_PID]) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 3cc4a627a537..0ee5479528b5 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1515,7 +1515,8 @@ static const struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla const struct rtnl_link_ops *ops = NULL; struct nlattr *linfo[IFLA_INFO_MAX + 1]; - if (nla_parse_nested(linfo, IFLA_INFO_MAX, nla, ifla_info_policy) < 0) + if (nla_parse_nested(linfo, IFLA_INFO_MAX, nla, + ifla_info_policy, NULL) < 0) return NULL; if (linfo[IFLA_INFO_KIND]) { @@ -1592,8 +1593,8 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) hdrlen = nlmsg_len(cb->nlh) < sizeof(struct ifinfomsg) ? sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg); - if (nlmsg_parse(cb->nlh, hdrlen, tb, IFLA_MAX, ifla_policy) >= 0) { - + if (nlmsg_parse(cb->nlh, hdrlen, tb, IFLA_MAX, + ifla_policy, NULL) >= 0) { if (tb[IFLA_EXT_MASK]) ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); @@ -1640,9 +1641,10 @@ out: return skb->len; } -int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len) +int rtnl_nla_parse_ifla(struct nlattr **tb, const struct nlattr *head, int len, + struct netlink_ext_ack *exterr) { - return nla_parse(tb, IFLA_MAX, head, len, ifla_policy); + return nla_parse(tb, IFLA_MAX, head, len, ifla_policy, exterr); } EXPORT_SYMBOL(rtnl_nla_parse_ifla); @@ -2078,7 +2080,7 @@ static int do_setlink(const struct sk_buff *skb, goto errout; } err = nla_parse_nested(vfinfo, IFLA_VF_MAX, attr, - ifla_vf_policy); + ifla_vf_policy, NULL); if (err < 0) goto errout; err = do_setvfinfo(dev, vfinfo); @@ -2106,7 +2108,7 @@ static int do_setlink(const struct sk_buff *skb, goto errout; } err = nla_parse_nested(port, IFLA_PORT_MAX, attr, - ifla_port_policy); + ifla_port_policy, NULL); if (err < 0) goto errout; if (!port[IFLA_PORT_VF]) { @@ -2126,7 +2128,8 @@ static int do_setlink(const struct sk_buff *skb, struct nlattr *port[IFLA_PORT_MAX+1]; err = nla_parse_nested(port, IFLA_PORT_MAX, - tb[IFLA_PORT_SELF], ifla_port_policy); + tb[IFLA_PORT_SELF], ifla_port_policy, + NULL); if (err < 0) goto errout; @@ -2170,7 +2173,7 @@ static int do_setlink(const struct sk_buff *skb, u32 xdp_flags = 0; err = nla_parse_nested(xdp, IFLA_XDP_MAX, tb[IFLA_XDP], - ifla_xdp_policy); + ifla_xdp_policy, NULL); if (err < 0) goto errout; @@ -2219,7 +2222,7 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh) struct nlattr *tb[IFLA_MAX+1]; char ifname[IFNAMSIZ]; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, NULL); if (err < 0) goto errout; @@ -2312,7 +2315,7 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh) struct nlattr *tb[IFLA_MAX+1]; int err; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, NULL); if (err < 0) return err; @@ -2441,7 +2444,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh) #ifdef CONFIG_MODULES replay: #endif - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, NULL); if (err < 0) return err; @@ -2472,7 +2475,8 @@ replay: if (tb[IFLA_LINKINFO]) { err = nla_parse_nested(linkinfo, IFLA_INFO_MAX, - tb[IFLA_LINKINFO], ifla_info_policy); + tb[IFLA_LINKINFO], ifla_info_policy, + NULL); if (err < 0) return err; } else @@ -2497,7 +2501,7 @@ replay: if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) { err = nla_parse_nested(attr, ops->maxtype, linkinfo[IFLA_INFO_DATA], - ops->policy); + ops->policy, NULL); if (err < 0) return err; data = attr; @@ -2515,7 +2519,8 @@ replay: err = nla_parse_nested(slave_attr, m_ops->slave_maxtype, linkinfo[IFLA_INFO_SLAVE_DATA], - m_ops->slave_policy); + m_ops->slave_policy, + NULL); if (err < 0) return err; slave_data = slave_attr; @@ -2684,7 +2689,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh) int err; u32 ext_filter_mask = 0; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, NULL); if (err < 0) return err; @@ -2734,7 +2739,7 @@ static u16 rtnl_calcit(struct sk_buff *skb, struct nlmsghdr *nlh) hdrlen = nlmsg_len(nlh) < sizeof(struct ifinfomsg) ? sizeof(struct rtgenmsg) : sizeof(struct ifinfomsg); - if (nlmsg_parse(nlh, hdrlen, tb, IFLA_MAX, ifla_policy) >= 0) { + if (nlmsg_parse(nlh, hdrlen, tb, IFLA_MAX, ifla_policy, NULL) >= 0) { if (tb[IFLA_EXT_MASK]) ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); } @@ -2965,7 +2970,7 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh) u16 vid; int err; - err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); + err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, NULL); if (err < 0) return err; @@ -3068,7 +3073,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh) if (!netlink_capable(skb, CAP_NET_ADMIN)) return -EPERM; - err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); + err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, NULL); if (err < 0) return err; @@ -3203,8 +3208,8 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) int err = 0; int fidx = 0; - if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, IFLA_MAX, - ifla_policy) == 0) { + if (nlmsg_parse(cb->nlh, sizeof(struct ifinfomsg), tb, + IFLA_MAX, ifla_policy, NULL) == 0) { if (tb[IFLA_MASTER]) br_idx = nla_get_u32(tb[IFLA_MASTER]); } diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 3202d75329b5..3f5a5f710576 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -245,8 +245,7 @@ static int dcbnl_getpfccfg(struct net_device *netdev, struct nlmsghdr *nlh, return -EOPNOTSUPP; ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX, - tb[DCB_ATTR_PFC_CFG], - dcbnl_pfc_up_nest); + tb[DCB_ATTR_PFC_CFG], dcbnl_pfc_up_nest, NULL); if (ret) return ret; @@ -304,7 +303,7 @@ static int dcbnl_getcap(struct net_device *netdev, struct nlmsghdr *nlh, return -EOPNOTSUPP; ret = nla_parse_nested(data, DCB_CAP_ATTR_MAX, tb[DCB_ATTR_CAP], - dcbnl_cap_nest); + dcbnl_cap_nest, NULL); if (ret) return ret; @@ -348,7 +347,7 @@ static int dcbnl_getnumtcs(struct net_device *netdev, struct nlmsghdr *nlh, return -EOPNOTSUPP; ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS], - dcbnl_numtcs_nest); + dcbnl_numtcs_nest, NULL); if (ret) return ret; @@ -393,7 +392,7 @@ static int dcbnl_setnumtcs(struct net_device *netdev, struct nlmsghdr *nlh, return -EOPNOTSUPP; ret = nla_parse_nested(data, DCB_NUMTCS_ATTR_MAX, tb[DCB_ATTR_NUMTCS], - dcbnl_numtcs_nest); + dcbnl_numtcs_nest, NULL); if (ret) return ret; @@ -452,7 +451,7 @@ static int dcbnl_getapp(struct net_device *netdev, struct nlmsghdr *nlh, return -EINVAL; ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP], - dcbnl_app_nest); + dcbnl_app_nest, NULL); if (ret) return ret; @@ -520,7 +519,7 @@ static int dcbnl_setapp(struct net_device *netdev, struct nlmsghdr *nlh, return -EINVAL; ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP], - dcbnl_app_nest); + dcbnl_app_nest, NULL); if (ret) return ret; @@ -577,8 +576,8 @@ static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlmsghdr *nlh, !netdev->dcbnl_ops->getpgbwgcfgrx) return -EOPNOTSUPP; - ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX, - tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest); + ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX, tb[DCB_ATTR_PG_CFG], + dcbnl_pg_nest, NULL); if (ret) return ret; @@ -597,8 +596,8 @@ static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlmsghdr *nlh, data = pg_tb[DCB_PG_ATTR_TC_ALL]; else data = pg_tb[i]; - ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX, - data, dcbnl_tc_param_nest); + ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX, data, + dcbnl_tc_param_nest, NULL); if (ret) goto err_pg; @@ -735,8 +734,7 @@ static int dcbnl_setpfccfg(struct net_device *netdev, struct nlmsghdr *nlh, return -EOPNOTSUPP; ret = nla_parse_nested(data, DCB_PFC_UP_ATTR_MAX, - tb[DCB_ATTR_PFC_CFG], - dcbnl_pfc_up_nest); + tb[DCB_ATTR_PFC_CFG], dcbnl_pfc_up_nest, NULL); if (ret) return ret; @@ -791,8 +789,8 @@ static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlmsghdr *nlh, !netdev->dcbnl_ops->setpgbwgcfgrx) return -EOPNOTSUPP; - ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX, - tb[DCB_ATTR_PG_CFG], dcbnl_pg_nest); + ret = nla_parse_nested(pg_tb, DCB_PG_ATTR_MAX, tb[DCB_ATTR_PG_CFG], + dcbnl_pg_nest, NULL); if (ret) return ret; @@ -801,7 +799,7 @@ static int __dcbnl_pg_setcfg(struct net_device *netdev, struct nlmsghdr *nlh, continue; ret = nla_parse_nested(param_tb, DCB_TC_ATTR_PARAM_MAX, - pg_tb[i], dcbnl_tc_param_nest); + pg_tb[i], dcbnl_tc_param_nest, NULL); if (ret) return ret; @@ -889,8 +887,8 @@ static int dcbnl_bcn_getcfg(struct net_device *netdev, struct nlmsghdr *nlh, !netdev->dcbnl_ops->getbcncfg) return -EOPNOTSUPP; - ret = nla_parse_nested(bcn_tb, DCB_BCN_ATTR_MAX, - tb[DCB_ATTR_BCN], dcbnl_bcn_nest); + ret = nla_parse_nested(bcn_tb, DCB_BCN_ATTR_MAX, tb[DCB_ATTR_BCN], + dcbnl_bcn_nest, NULL); if (ret) return ret; @@ -948,9 +946,8 @@ static int dcbnl_bcn_setcfg(struct net_device *netdev, struct nlmsghdr *nlh, !netdev->dcbnl_ops->setbcnrp) return -EOPNOTSUPP; - ret = nla_parse_nested(data, DCB_BCN_ATTR_MAX, - tb[DCB_ATTR_BCN], - dcbnl_pfc_up_nest); + ret = nla_parse_nested(data, DCB_BCN_ATTR_MAX, tb[DCB_ATTR_BCN], + dcbnl_pfc_up_nest, NULL); if (ret) return ret; @@ -1424,8 +1421,8 @@ static int dcbnl_ieee_set(struct net_device *netdev, struct nlmsghdr *nlh, if (!tb[DCB_ATTR_IEEE]) return -EINVAL; - err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX, - tb[DCB_ATTR_IEEE], dcbnl_ieee_policy); + err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX, tb[DCB_ATTR_IEEE], + dcbnl_ieee_policy, NULL); if (err) return err; @@ -1508,8 +1505,8 @@ static int dcbnl_ieee_del(struct net_device *netdev, struct nlmsghdr *nlh, if (!tb[DCB_ATTR_IEEE]) return -EINVAL; - err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX, - tb[DCB_ATTR_IEEE], dcbnl_ieee_policy); + err = nla_parse_nested(ieee, DCB_ATTR_IEEE_MAX, tb[DCB_ATTR_IEEE], + dcbnl_ieee_policy, NULL); if (err) return err; @@ -1581,8 +1578,8 @@ static int dcbnl_getfeatcfg(struct net_device *netdev, struct nlmsghdr *nlh, if (!tb[DCB_ATTR_FEATCFG]) return -EINVAL; - ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, tb[DCB_ATTR_FEATCFG], - dcbnl_featcfg_nest); + ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, + tb[DCB_ATTR_FEATCFG], dcbnl_featcfg_nest, NULL); if (ret) return ret; @@ -1625,8 +1622,8 @@ static int dcbnl_setfeatcfg(struct net_device *netdev, struct nlmsghdr *nlh, if (!tb[DCB_ATTR_FEATCFG]) return -EINVAL; - ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, tb[DCB_ATTR_FEATCFG], - dcbnl_featcfg_nest); + ret = nla_parse_nested(data, DCB_FEATCFG_ATTR_MAX, + tb[DCB_ATTR_FEATCFG], dcbnl_featcfg_nest, NULL); if (ret) goto err; @@ -1715,7 +1712,7 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh) return -EPERM; ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX, - dcbnl_rtnl_policy); + dcbnl_rtnl_policy, NULL); if (ret < 0) return ret; diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index 8fdd9f492b0e..e65f1be44e8e 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -581,7 +581,7 @@ static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) if (!net_eq(net, &init_net)) goto errout; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy, NULL); if (err < 0) goto errout; @@ -625,7 +625,7 @@ static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) if (!net_eq(net, &init_net)) return -EINVAL; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy, NULL); if (err < 0) return err; diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index 7af0ba6157a1..34663bf8aa6d 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -515,7 +515,8 @@ static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) if (!net_eq(net, &init_net)) return -EINVAL; - err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy); + err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy, + NULL); if (err < 0) return err; @@ -540,7 +541,8 @@ static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) if (!net_eq(net, &init_net)) return -EINVAL; - err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy); + err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy, + NULL); if (err < 0) return err; diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index b1dc096d22f8..2d7097bbc666 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1654,7 +1654,7 @@ static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) if (!net_eq(net, &init_net)) return -EINVAL; - err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_dn_policy); + err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_dn_policy, NULL); if (err < 0) return err; diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index fc60cd061f39..d6b1a1b21909 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -249,8 +249,7 @@ nl802154_prepare_wpan_dev_dump(struct sk_buff *skb, if (!cb->args[0]) { err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize, genl_family_attrbuf(&nl802154_fam), - nl802154_fam.maxattr, - nl802154_policy); + nl802154_fam.maxattr, nl802154_policy, NULL); if (err) goto out_unlock; @@ -562,8 +561,8 @@ static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb, struct nl802154_dump_wpan_phy_state *state) { struct nlattr **tb = genl_family_attrbuf(&nl802154_fam); - int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize, - tb, nl802154_fam.maxattr, nl802154_policy); + int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize, tb, + nl802154_fam.maxattr, nl802154_policy, NULL); /* TODO check if we can handle error here, * we have no backward compatibility @@ -1308,7 +1307,7 @@ ieee802154_llsec_parse_dev_addr(struct nlattr *nla, struct nlattr *attrs[NL802154_DEV_ADDR_ATTR_MAX + 1]; if (!nla || nla_parse_nested(attrs, NL802154_DEV_ADDR_ATTR_MAX, nla, - nl802154_dev_addr_policy)) + nl802154_dev_addr_policy, NULL)) return -EINVAL; if (!attrs[NL802154_DEV_ADDR_ATTR_PAN_ID] || @@ -1348,7 +1347,7 @@ ieee802154_llsec_parse_key_id(struct nlattr *nla, struct nlattr *attrs[NL802154_KEY_ID_ATTR_MAX + 1]; if (!nla || nla_parse_nested(attrs, NL802154_KEY_ID_ATTR_MAX, nla, - nl802154_key_id_policy)) + nl802154_key_id_policy, NULL)) return -EINVAL; if (!attrs[NL802154_KEY_ID_ATTR_MODE]) @@ -1565,7 +1564,7 @@ static int nl802154_add_llsec_key(struct sk_buff *skb, struct genl_info *info) if (nla_parse_nested(attrs, NL802154_KEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_KEY], - nl802154_key_policy)) + nl802154_key_policy, NULL)) return -EINVAL; if (!attrs[NL802154_KEY_ATTR_USAGE_FRAMES] || @@ -1615,7 +1614,7 @@ static int nl802154_del_llsec_key(struct sk_buff *skb, struct genl_info *info) if (nla_parse_nested(attrs, NL802154_KEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_KEY], - nl802154_key_policy)) + nl802154_key_policy, NULL)) return -EINVAL; if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0) @@ -1729,8 +1728,8 @@ ieee802154_llsec_parse_device(struct nlattr *nla, { struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1]; - if (!nla || nla_parse_nested(attrs, NL802154_DEV_ATTR_MAX, nla, - nl802154_dev_policy)) + if (!nla || nla_parse_nested(attrs, NL802154_DEV_ATTR_MAX, + nla, nl802154_dev_policy, NULL)) return -EINVAL; memset(dev, 0, sizeof(*dev)); @@ -1783,7 +1782,7 @@ static int nl802154_del_llsec_dev(struct sk_buff *skb, struct genl_info *info) if (nla_parse_nested(attrs, NL802154_DEV_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVICE], - nl802154_dev_policy)) + nl802154_dev_policy, NULL)) return -EINVAL; if (!attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]) @@ -1911,7 +1910,7 @@ static int nl802154_add_llsec_devkey(struct sk_buff *skb, struct genl_info *info 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) + nl802154_devkey_policy, NULL) < 0) return -EINVAL; if (!attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER] || @@ -1943,7 +1942,7 @@ static int nl802154_del_llsec_devkey(struct sk_buff *skb, struct genl_info *info if (nla_parse_nested(attrs, NL802154_DEVKEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVKEY], - nl802154_devkey_policy)) + nl802154_devkey_policy, NULL)) return -EINVAL; if (!attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]) @@ -2063,8 +2062,8 @@ 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)) + if (!nla || nla_parse_nested(attrs, NL802154_SECLEVEL_ATTR_MAX, + nla, nl802154_seclevel_policy, NULL)) return -EINVAL; memset(sl, 0, sizeof(*sl)); diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 6d3602ec640c..f33f53791f50 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -582,7 +582,8 @@ static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) ASSERT_RTNL(); - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy, + NULL); if (err < 0) goto errout; @@ -752,7 +753,8 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, struct in_device *in_dev; int err; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy, + NULL); if (err < 0) goto errout; @@ -1717,7 +1719,7 @@ static int inet_validate_link_af(const struct net_device *dev, if (dev && !__in_dev_get_rtnl(dev)) return -EAFNOSUPPORT; - err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy); + err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy, NULL); if (err < 0) return err; @@ -1745,7 +1747,7 @@ static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) if (!in_dev) return -EAFNOSUPPORT; - if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0) + if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0) BUG(); if (tb[IFLA_INET_CONF]) { @@ -1882,7 +1884,7 @@ static int inet_netconf_get_devconf(struct sk_buff *in_skb, int err; err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, - devconf_ipv4_policy); + devconf_ipv4_policy, NULL); if (err < 0) goto errout; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 8f2133ffc2ff..434dd2538716 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -632,7 +632,8 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb, int err, remaining; struct rtmsg *rtm; - err = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipv4_policy); + err = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipv4_policy, + NULL); if (err < 0) goto errout; diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index a31f47ccaad9..baf196eaf1d8 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -235,7 +235,7 @@ static int ip_tun_build_state(struct nlattr *attr, struct nlattr *tb[LWTUNNEL_IP_MAX + 1]; int err; - err = nla_parse_nested(tb, LWTUNNEL_IP_MAX, attr, ip_tun_policy); + err = nla_parse_nested(tb, LWTUNNEL_IP_MAX, attr, ip_tun_policy, NULL); if (err < 0) return err; @@ -332,7 +332,8 @@ static int ip6_tun_build_state(struct nlattr *attr, struct nlattr *tb[LWTUNNEL_IP6_MAX + 1]; int err; - err = nla_parse_nested(tb, LWTUNNEL_IP6_MAX, attr, ip6_tun_policy); + err = nla_parse_nested(tb, LWTUNNEL_IP6_MAX, attr, ip6_tun_policy, + NULL); if (err < 0) return err; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 5bca64fc71b7..d7be21f2174a 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -2439,7 +2439,8 @@ static int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh, struct rtmsg *rtm; int ret, rem; - ret = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipmr_policy); + ret = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipmr_policy, + NULL); if (ret < 0) goto out; rtm = nlmsg_data(nlh); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 0fcc2d5192bd..7a4f2c38c3c4 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2645,7 +2645,8 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) u32 table_id = RT_TABLE_MAIN; kuid_t uid; - err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv4_policy); + err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv4_policy, + NULL); if (err < 0) goto errout; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 67ec87ea5fb6..b330c2abcb24 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -624,7 +624,7 @@ static int inet6_netconf_get_devconf(struct sk_buff *in_skb, int err; err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, - devconf_ipv6_policy); + devconf_ipv6_policy, NULL); if (err < 0) goto errout; @@ -4408,7 +4408,8 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) u32 ifa_flags; int err; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy, + NULL); if (err < 0) return err; @@ -4520,7 +4521,8 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) u32 ifa_flags; int err; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy, + NULL); if (err < 0) return err; @@ -4881,7 +4883,8 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh) struct sk_buff *skb; int err; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy, + NULL); if (err < 0) goto errout; @@ -5251,7 +5254,8 @@ static int inet6_validate_link_af(const struct net_device *dev, if (dev && !__in6_dev_get(dev)) return -EAFNOSUPPORT; - return nla_parse_nested(tb, IFLA_INET6_MAX, nla, inet6_af_policy); + return nla_parse_nested(tb, IFLA_INET6_MAX, nla, inet6_af_policy, + NULL); } static int check_addr_gen_mode(int mode) @@ -5283,7 +5287,7 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla) if (!idev) return -EAFNOSUPPORT; - if (nla_parse_nested(tb, IFLA_INET6_MAX, nla, NULL) < 0) + if (nla_parse_nested(tb, IFLA_INET6_MAX, nla, NULL, NULL) < 0) BUG(); if (tb[IFLA_INET6_TOKEN]) { diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c index a8f6986dcbe5..6cb4ed91722a 100644 --- a/net/ipv6/addrlabel.c +++ b/net/ipv6/addrlabel.c @@ -413,7 +413,7 @@ static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh) u32 label; int err = 0; - err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy); + err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy, NULL); if (err < 0) return err; @@ -532,7 +532,7 @@ static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh) struct ip6addrlbl_entry *p; struct sk_buff *skb; - err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy); + err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy, NULL); if (err < 0) return err; diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c index ce1aae4a7fc8..b3df03e3faa0 100644 --- a/net/ipv6/ila/ila_lwt.c +++ b/net/ipv6/ila/ila_lwt.c @@ -146,8 +146,7 @@ static int ila_build_state(struct nlattr *nla, return -EINVAL; } - ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, - ila_nl_policy); + ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, ila_nl_policy, NULL); if (ret < 0) return ret; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 9db1418993f2..ccde23eba702 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2906,7 +2906,8 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, unsigned int pref; int err; - err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); + err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy, + NULL); if (err < 0) goto errout; @@ -3574,7 +3575,8 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) struct flowi6 fl6; int err, iif = 0, oif = 0; - err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); + err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy, + NULL); if (err < 0) goto errout; diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index a644aaecdfd3..7436a4a62f3e 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -328,7 +328,7 @@ static int seg6_build_state(struct nlattr *nla, int err; err = nla_parse_nested(tb, SEG6_IPTUNNEL_MAX, nla, - seg6_iptunnel_policy); + seg6_iptunnel_policy, NULL); if (err < 0) return err; diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 5928d22ba9c8..07181d2273e1 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1122,7 +1122,7 @@ static int mpls_netconf_get_devconf(struct sk_buff *in_skb, int err; err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, - devconf_mpls_policy); + devconf_mpls_policy, NULL); if (err < 0) goto errout; @@ -1643,7 +1643,8 @@ static int rtm_to_route_config(struct sk_buff *skb, struct nlmsghdr *nlh, int index; int err; - err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_mpls_policy); + err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_mpls_policy, + NULL); if (err < 0) goto errout; diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c index fe00e98667cf..369c7a23c86c 100644 --- a/net/mpls/mpls_iptunnel.c +++ b/net/mpls/mpls_iptunnel.c @@ -168,7 +168,7 @@ static int mpls_build_state(struct nlattr *nla, int ret; ret = nla_parse_nested(tb, MPLS_IPTUNNEL_MAX, nla, - mpls_iptunnel_policy); + mpls_iptunnel_policy, NULL); if (ret < 0) return ret; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 26356bf8cebf..9bd5b6636181 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -295,7 +295,8 @@ ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr) if (unlikely(!flag_nested(nla))) return -IPSET_ERR_PROTOCOL; - if (nla_parse_nested(tb, IPSET_ATTR_IPADDR_MAX, nla, ipaddr_policy)) + if (nla_parse_nested(tb, IPSET_ATTR_IPADDR_MAX, nla, + ipaddr_policy, NULL)) return -IPSET_ERR_PROTOCOL; if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_IPADDR_IPV4))) return -IPSET_ERR_PROTOCOL; @@ -313,7 +314,8 @@ ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr) if (unlikely(!flag_nested(nla))) return -IPSET_ERR_PROTOCOL; - if (nla_parse_nested(tb, IPSET_ATTR_IPADDR_MAX, nla, ipaddr_policy)) + if (nla_parse_nested(tb, IPSET_ATTR_IPADDR_MAX, nla, + ipaddr_policy, NULL)) return -IPSET_ERR_PROTOCOL; if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_IPADDR_IPV6))) return -IPSET_ERR_PROTOCOL; @@ -906,7 +908,7 @@ static int ip_set_create(struct net *net, struct sock *ctnl, /* Without holding any locks, create private part. */ if (attr[IPSET_ATTR_DATA] && nla_parse_nested(tb, IPSET_ATTR_CREATE_MAX, attr[IPSET_ATTR_DATA], - set->type->create_policy)) { + set->type->create_policy, NULL)) { ret = -IPSET_ERR_PROTOCOL; goto put_out; } @@ -1257,8 +1259,8 @@ dump_init(struct netlink_callback *cb, struct ip_set_net *inst) ip_set_id_t index; /* Second pass, so parser can't fail */ - nla_parse(cda, IPSET_ATTR_CMD_MAX, - attr, nlh->nlmsg_len - min_len, ip_set_setname_policy); + nla_parse(cda, IPSET_ATTR_CMD_MAX, attr, nlh->nlmsg_len - min_len, + ip_set_setname_policy, NULL); if (cda[IPSET_ATTR_SETNAME]) { struct ip_set *set; @@ -1501,9 +1503,8 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set, memcpy(&errmsg->msg, nlh, nlh->nlmsg_len); cmdattr = (void *)&errmsg->msg + min_len; - nla_parse(cda, IPSET_ATTR_CMD_MAX, - cmdattr, nlh->nlmsg_len - min_len, - ip_set_adt_policy); + nla_parse(cda, IPSET_ATTR_CMD_MAX, cmdattr, + nlh->nlmsg_len - min_len, ip_set_adt_policy, NULL); errline = nla_data(cda[IPSET_ATTR_LINENO]); @@ -1549,7 +1550,7 @@ static int ip_set_uadd(struct net *net, struct sock *ctnl, struct sk_buff *skb, if (attr[IPSET_ATTR_DATA]) { if (nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, attr[IPSET_ATTR_DATA], - set->type->adt_policy)) + set->type->adt_policy, NULL)) return -IPSET_ERR_PROTOCOL; ret = call_ad(ctnl, skb, set, tb, IPSET_ADD, flags, use_lineno); @@ -1561,7 +1562,7 @@ static int ip_set_uadd(struct net *net, struct sock *ctnl, struct sk_buff *skb, if (nla_type(nla) != IPSET_ATTR_DATA || !flag_nested(nla) || nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, nla, - set->type->adt_policy)) + set->type->adt_policy, NULL)) return -IPSET_ERR_PROTOCOL; ret = call_ad(ctnl, skb, set, tb, IPSET_ADD, flags, use_lineno); @@ -1603,7 +1604,7 @@ static int ip_set_udel(struct net *net, struct sock *ctnl, struct sk_buff *skb, if (attr[IPSET_ATTR_DATA]) { if (nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, attr[IPSET_ATTR_DATA], - set->type->adt_policy)) + set->type->adt_policy, NULL)) return -IPSET_ERR_PROTOCOL; ret = call_ad(ctnl, skb, set, tb, IPSET_DEL, flags, use_lineno); @@ -1615,7 +1616,7 @@ static int ip_set_udel(struct net *net, struct sock *ctnl, struct sk_buff *skb, if (nla_type(nla) != IPSET_ATTR_DATA || !flag_nested(nla) || nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, nla, - set->type->adt_policy)) + set->type->adt_policy, NULL)) return -IPSET_ERR_PROTOCOL; ret = call_ad(ctnl, skb, set, tb, IPSET_DEL, flags, use_lineno); @@ -1646,7 +1647,7 @@ static int ip_set_utest(struct net *net, struct sock *ctnl, struct sk_buff *skb, return -ENOENT; if (nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, attr[IPSET_ATTR_DATA], - set->type->adt_policy)) + set->type->adt_policy, NULL)) return -IPSET_ERR_PROTOCOL; rcu_read_lock_bh(); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 541aa7694775..adb7ee142c5f 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -3089,7 +3089,8 @@ static int ip_vs_genl_parse_service(struct netns_ipvs *ipvs, /* Parse mandatory identifying service fields first */ if (nla == NULL || - nla_parse_nested(attrs, IPVS_SVC_ATTR_MAX, nla, ip_vs_svc_policy)) + nla_parse_nested(attrs, IPVS_SVC_ATTR_MAX, nla, + ip_vs_svc_policy, NULL)) return -EINVAL; nla_af = attrs[IPVS_SVC_ATTR_AF]; @@ -3251,8 +3252,8 @@ static int ip_vs_genl_dump_dests(struct sk_buff *skb, mutex_lock(&__ip_vs_mutex); /* Try to find the service for which to dump destinations */ - if (nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs, - IPVS_CMD_ATTR_MAX, ip_vs_cmd_policy)) + if (nlmsg_parse(cb->nlh, GENL_HDRLEN, attrs, IPVS_CMD_ATTR_MAX, + ip_vs_cmd_policy, NULL)) goto out_err; @@ -3288,7 +3289,8 @@ static int ip_vs_genl_parse_dest(struct ip_vs_dest_user_kern *udest, /* Parse mandatory identifying destination fields first */ if (nla == NULL || - nla_parse_nested(attrs, IPVS_DEST_ATTR_MAX, nla, ip_vs_dest_policy)) + nla_parse_nested(attrs, IPVS_DEST_ATTR_MAX, nla, + ip_vs_dest_policy, NULL)) return -EINVAL; nla_addr = attrs[IPVS_DEST_ATTR_ADDR]; @@ -3530,7 +3532,7 @@ static int ip_vs_genl_set_daemon(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[IPVS_CMD_ATTR_DAEMON] || nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX, info->attrs[IPVS_CMD_ATTR_DAEMON], - ip_vs_daemon_policy)) + ip_vs_daemon_policy, NULL)) goto out; if (cmd == IPVS_CMD_NEW_DAEMON) diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index ecdc324c7785..ace824ab2e03 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -908,7 +908,7 @@ static int ctnetlink_parse_tuple_ip(struct nlattr *attr, struct nf_conntrack_l3proto *l3proto; int ret = 0; - ret = nla_parse_nested(tb, CTA_IP_MAX, attr, NULL); + ret = nla_parse_nested(tb, CTA_IP_MAX, attr, NULL, NULL); if (ret < 0) return ret; @@ -917,7 +917,7 @@ static int ctnetlink_parse_tuple_ip(struct nlattr *attr, if (likely(l3proto->nlattr_to_tuple)) { ret = nla_validate_nested(attr, CTA_IP_MAX, - l3proto->nla_policy); + l3proto->nla_policy, NULL); if (ret == 0) ret = l3proto->nlattr_to_tuple(tb, tuple); } @@ -938,7 +938,8 @@ static int ctnetlink_parse_tuple_proto(struct nlattr *attr, struct nf_conntrack_l4proto *l4proto; int ret = 0; - ret = nla_parse_nested(tb, CTA_PROTO_MAX, attr, proto_nla_policy); + ret = nla_parse_nested(tb, CTA_PROTO_MAX, attr, proto_nla_policy, + NULL); if (ret < 0) return ret; @@ -951,7 +952,7 @@ static int ctnetlink_parse_tuple_proto(struct nlattr *attr, if (likely(l4proto->nlattr_to_tuple)) { ret = nla_validate_nested(attr, CTA_PROTO_MAX, - l4proto->nla_policy); + l4proto->nla_policy, NULL); if (ret == 0) ret = l4proto->nlattr_to_tuple(tb, tuple); } @@ -1015,7 +1016,8 @@ ctnetlink_parse_tuple(const struct nlattr * const cda[], memset(tuple, 0, sizeof(*tuple)); - err = nla_parse_nested(tb, CTA_TUPLE_MAX, cda[type], tuple_nla_policy); + err = nla_parse_nested(tb, CTA_TUPLE_MAX, cda[type], tuple_nla_policy, + NULL); if (err < 0) return err; @@ -1065,7 +1067,7 @@ static int ctnetlink_parse_help(const struct nlattr *attr, char **helper_name, int err; struct nlattr *tb[CTA_HELP_MAX+1]; - err = nla_parse_nested(tb, CTA_HELP_MAX, attr, help_nla_policy); + err = nla_parse_nested(tb, CTA_HELP_MAX, attr, help_nla_policy, NULL); if (err < 0) return err; @@ -1566,7 +1568,8 @@ static int ctnetlink_change_protoinfo(struct nf_conn *ct, struct nf_conntrack_l4proto *l4proto; int err = 0; - err = nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, protoinfo_policy); + err = nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, protoinfo_policy, + NULL); if (err < 0) return err; @@ -1591,7 +1594,7 @@ static int change_seq_adj(struct nf_ct_seqadj *seq, int err; struct nlattr *cda[CTA_SEQADJ_MAX+1]; - err = nla_parse_nested(cda, CTA_SEQADJ_MAX, attr, seqadj_policy); + err = nla_parse_nested(cda, CTA_SEQADJ_MAX, attr, seqadj_policy, NULL); if (err < 0) return err; @@ -2348,7 +2351,7 @@ ctnetlink_glue_parse(const struct nlattr *attr, struct nf_conn *ct) struct nlattr *cda[CTA_MAX+1]; int ret; - ret = nla_parse_nested(cda, CTA_MAX, attr, ct_nla_policy); + ret = nla_parse_nested(cda, CTA_MAX, attr, ct_nla_policy, NULL); if (ret < 0) return ret; @@ -2385,7 +2388,8 @@ ctnetlink_glue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, struct nf_conntrack_expect *exp; int err; - err = nla_parse_nested(cda, CTA_EXPECT_MAX, attr, exp_nla_policy); + err = nla_parse_nested(cda, CTA_EXPECT_MAX, attr, exp_nla_policy, + NULL); if (err < 0) return err; @@ -3004,7 +3008,8 @@ ctnetlink_parse_expect_nat(const struct nlattr *attr, struct nf_conntrack_tuple nat_tuple = {}; int err; - err = nla_parse_nested(tb, CTA_EXPECT_NAT_MAX, attr, exp_nat_nla_policy); + err = nla_parse_nested(tb, CTA_EXPECT_NAT_MAX, attr, + exp_nat_nla_policy, NULL); if (err < 0) return err; diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index 93dd1c5b7bff..b2e02dfe7fa8 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -665,7 +665,7 @@ static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct) return 0; err = nla_parse_nested(tb, CTA_PROTOINFO_DCCP_MAX, attr, - dccp_nla_policy); + dccp_nla_policy, NULL); if (err < 0) return err; diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 33279aab583d..2a7300587c87 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -584,10 +584,8 @@ static int nlattr_to_sctp(struct nlattr *cda[], struct nf_conn *ct) if (!attr) return 0; - err = nla_parse_nested(tb, - CTA_PROTOINFO_SCTP_MAX, - attr, - sctp_nla_policy); + err = nla_parse_nested(tb, CTA_PROTOINFO_SCTP_MAX, attr, + sctp_nla_policy, NULL); if (err < 0) return err; diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index b122e9dacfed..85bde77ad967 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -1234,7 +1234,8 @@ static int nlattr_to_tcp(struct nlattr *cda[], struct nf_conn *ct) if (!pattr) return 0; - err = nla_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, pattr, tcp_nla_policy); + err = nla_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, pattr, + tcp_nla_policy, NULL); if (err < 0) return err; diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 82802e4a6640..908ba5abbc0b 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -751,7 +751,8 @@ static int nfnetlink_parse_nat_proto(struct nlattr *attr, const struct nf_nat_l4proto *l4proto; int err; - err = nla_parse_nested(tb, CTA_PROTONAT_MAX, attr, protonat_nla_policy); + err = nla_parse_nested(tb, CTA_PROTONAT_MAX, attr, + protonat_nla_policy, NULL); if (err < 0) return err; @@ -780,7 +781,7 @@ nfnetlink_parse_nat(const struct nlattr *nat, memset(range, 0, sizeof(*range)); - err = nla_parse_nested(tb, CTA_NAT_MAX, nat, nat_nla_policy); + err = nla_parse_nested(tb, CTA_NAT_MAX, nat, nat_nla_policy, NULL); if (err < 0) return err; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 2d822d2fd830..907431318637 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1182,7 +1182,8 @@ static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr) struct nft_stats *stats; int err; - err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy); + err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy, + NULL); if (err < 0) return ERR_PTR(err); @@ -1257,7 +1258,7 @@ static int nft_chain_parse_hook(struct net *net, int err; err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK], - nft_hook_policy); + nft_hook_policy, NULL); if (err < 0) return err; @@ -1724,7 +1725,7 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx, struct nlattr *tb[NFTA_EXPR_MAX + 1]; int err; - err = nla_parse_nested(tb, NFTA_EXPR_MAX, nla, nft_expr_policy); + err = nla_parse_nested(tb, NFTA_EXPR_MAX, nla, nft_expr_policy, NULL); if (err < 0) return err; @@ -1734,7 +1735,7 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx, if (tb[NFTA_EXPR_DATA]) { err = nla_parse_nested(info->tb, type->maxattr, - tb[NFTA_EXPR_DATA], type->policy); + tb[NFTA_EXPR_DATA], type->policy, NULL); if (err < 0) goto err1; } else @@ -2879,7 +2880,8 @@ static int nf_tables_set_desc_parse(const struct nft_ctx *ctx, struct nlattr *da[NFTA_SET_DESC_MAX + 1]; int err; - err = nla_parse_nested(da, NFTA_SET_DESC_MAX, nla, nft_set_desc_policy); + err = nla_parse_nested(da, NFTA_SET_DESC_MAX, nla, + nft_set_desc_policy, NULL); if (err < 0) return err; @@ -3381,7 +3383,8 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) int event, err; err = nlmsg_parse(cb->nlh, sizeof(struct nfgenmsg), nla, - NFTA_SET_ELEM_LIST_MAX, nft_set_elem_list_policy); + NFTA_SET_ELEM_LIST_MAX, nft_set_elem_list_policy, + NULL); if (err < 0) return err; @@ -3640,7 +3643,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, int err; err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, - nft_set_elem_policy); + nft_set_elem_policy, NULL); if (err < 0) return err; @@ -3870,7 +3873,7 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set, int err; err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, - nft_set_elem_policy); + nft_set_elem_policy, NULL); if (err < 0) goto err1; @@ -4101,7 +4104,8 @@ static struct nft_object *nft_obj_init(const struct nft_ctx *ctx, int err; if (attr) { - err = nla_parse_nested(tb, type->maxattr, attr, type->policy); + err = nla_parse_nested(tb, type->maxattr, attr, type->policy, + NULL); if (err < 0) goto err1; } else { @@ -5314,7 +5318,8 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, struct nft_chain *chain; int err; - err = nla_parse_nested(tb, NFTA_VERDICT_MAX, nla, nft_verdict_policy); + err = nla_parse_nested(tb, NFTA_VERDICT_MAX, nla, nft_verdict_policy, + NULL); if (err < 0) return err; @@ -5444,7 +5449,7 @@ int nft_data_init(const struct nft_ctx *ctx, struct nlattr *tb[NFTA_DATA_MAX + 1]; int err; - err = nla_parse_nested(tb, NFTA_DATA_MAX, nla, nft_data_policy); + err = nla_parse_nested(tb, NFTA_DATA_MAX, nla, nft_data_policy, NULL); if (err < 0) return err; diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 181d3bb800e6..792def00a07d 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -192,8 +192,8 @@ replay: int attrlen = nlh->nlmsg_len - min_len; __u8 subsys_id = NFNL_SUBSYS_ID(type); - err = nla_parse(cda, ss->cb[cb_id].attr_count, - attr, attrlen, ss->cb[cb_id].policy); + err = nla_parse(cda, ss->cb[cb_id].attr_count, attr, attrlen, + ss->cb[cb_id].policy, NULL); if (err < 0) { rcu_read_unlock(); return err; @@ -377,8 +377,8 @@ replay: struct nlattr *attr = (void *)nlh + min_len; int attrlen = nlh->nlmsg_len - min_len; - err = nla_parse(cda, ss->cb[cb_id].attr_count, - attr, attrlen, ss->cb[cb_id].policy); + err = nla_parse(cda, ss->cb[cb_id].attr_count, attr, + attrlen, ss->cb[cb_id].policy, NULL); if (err < 0) goto ack; @@ -467,7 +467,8 @@ static void nfnetlink_rcv_skb_batch(struct sk_buff *skb, struct nlmsghdr *nlh) skb->len < NLMSG_HDRLEN + sizeof(struct nfgenmsg)) return; - err = nla_parse(cda, NFNL_BATCH_MAX, attr, attrlen, nfnl_batch_policy); + err = nla_parse(cda, NFNL_BATCH_MAX, attr, attrlen, nfnl_batch_policy, + NULL); if (err < 0) { netlink_ack(skb, nlh, err, NULL); return; diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c index c86da174a5fc..2837d5fb98bd 100644 --- a/net/netfilter/nfnetlink_acct.c +++ b/net/netfilter/nfnetlink_acct.c @@ -244,7 +244,8 @@ nfacct_filter_alloc(const struct nlattr * const attr) struct nlattr *tb[NFACCT_FILTER_MAX + 1]; int err; - err = nla_parse_nested(tb, NFACCT_FILTER_MAX, attr, filter_policy); + err = nla_parse_nested(tb, NFACCT_FILTER_MAX, attr, filter_policy, + NULL); if (err < 0) return ERR_PTR(err); diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index d45558178da5..5b6c68311566 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -77,7 +77,8 @@ nfnl_cthelper_parse_tuple(struct nf_conntrack_tuple *tuple, int err; struct nlattr *tb[NFCTH_TUPLE_MAX+1]; - err = nla_parse_nested(tb, NFCTH_TUPLE_MAX, attr, nfnl_cthelper_tuple_pol); + err = nla_parse_nested(tb, NFCTH_TUPLE_MAX, attr, + nfnl_cthelper_tuple_pol, NULL); if (err < 0) return err; @@ -137,7 +138,8 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy, int err; struct nlattr *tb[NFCTH_POLICY_MAX+1]; - err = nla_parse_nested(tb, NFCTH_POLICY_MAX, attr, nfnl_cthelper_expect_pol); + err = nla_parse_nested(tb, NFCTH_POLICY_MAX, attr, + nfnl_cthelper_expect_pol, NULL); if (err < 0) return err; @@ -171,7 +173,7 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper, unsigned int class_max; ret = nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr, - nfnl_cthelper_expect_policy_set); + nfnl_cthelper_expect_policy_set, NULL); if (ret < 0) return ret; @@ -276,7 +278,7 @@ nfnl_cthelper_update_policy_one(const struct nf_conntrack_expect_policy *policy, int err; err = nla_parse_nested(tb, NFCTH_POLICY_MAX, attr, - nfnl_cthelper_expect_pol); + nfnl_cthelper_expect_pol, NULL); if (err < 0) return err; @@ -336,7 +338,7 @@ static int nfnl_cthelper_update_policy(struct nf_conntrack_helper *helper, int err; err = nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr, - nfnl_cthelper_expect_policy_set); + nfnl_cthelper_expect_policy_set, NULL); if (err < 0) return err; diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index 57c2cdf7b691..0a3510e7e396 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -56,7 +56,8 @@ ctnl_timeout_parse_policy(void *timeouts, struct nf_conntrack_l4proto *l4proto, struct nlattr *tb[l4proto->ctnl_timeout.nlattr_max+1]; ret = nla_parse_nested(tb, l4proto->ctnl_timeout.nlattr_max, - attr, l4proto->ctnl_timeout.nla_policy); + attr, l4proto->ctnl_timeout.nla_policy, + NULL); if (ret < 0) return ret; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 933509ebf3d3..3be6fef30581 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1109,7 +1109,7 @@ static int nfqa_parse_bridge(struct nf_queue_entry *entry, int err; err = nla_parse_nested(tb, NFQA_VLAN_MAX, nfqa[NFQA_VLAN], - nfqa_vlan_policy); + nfqa_vlan_policy, NULL); if (err < 0) return err; diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index fab6bf3f955e..d76d0f36799f 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -200,7 +200,7 @@ static int nft_parse_compat(const struct nlattr *attr, u16 *proto, bool *inv) int err; err = nla_parse_nested(tb, NFTA_RULE_COMPAT_MAX, attr, - nft_rule_compat_policy); + nft_rule_compat_policy, NULL); if (err < 0) return err; diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c index 4149d3e63589..9aacf2da3d98 100644 --- a/net/netlabel/netlabel_cipso_v4.c +++ b/net/netlabel/netlabel_cipso_v4.c @@ -101,7 +101,7 @@ static int netlbl_cipsov4_add_common(struct genl_info *info, if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_TAGLST], NLBL_CIPSOV4_A_MAX, - netlbl_cipsov4_genl_policy) != 0) + netlbl_cipsov4_genl_policy, NULL) != 0) return -EINVAL; nla_for_each_nested(nla, info->attrs[NLBL_CIPSOV4_A_TAGLST], nla_rem) @@ -148,7 +148,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info, if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSLVLLST], NLBL_CIPSOV4_A_MAX, - netlbl_cipsov4_genl_policy) != 0) + netlbl_cipsov4_genl_policy, NULL) != 0) return -EINVAL; doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL); @@ -170,10 +170,10 @@ static int netlbl_cipsov4_add_std(struct genl_info *info, info->attrs[NLBL_CIPSOV4_A_MLSLVLLST], nla_a_rem) if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSLVL) { - if (nla_validate_nested(nla_a, - NLBL_CIPSOV4_A_MAX, - netlbl_cipsov4_genl_policy) != 0) - goto add_std_failure; + if (nla_validate_nested(nla_a, NLBL_CIPSOV4_A_MAX, + netlbl_cipsov4_genl_policy, + NULL) != 0) + goto add_std_failure; nla_for_each_nested(nla_b, nla_a, nla_b_rem) switch (nla_type(nla_b)) { case NLBL_CIPSOV4_A_MLSLVLLOC: @@ -236,7 +236,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info, if (info->attrs[NLBL_CIPSOV4_A_MLSCATLST]) { if (nla_validate_nested(info->attrs[NLBL_CIPSOV4_A_MLSCATLST], NLBL_CIPSOV4_A_MAX, - netlbl_cipsov4_genl_policy) != 0) + netlbl_cipsov4_genl_policy, NULL) != 0) goto add_std_failure; nla_for_each_nested(nla_a, @@ -244,8 +244,9 @@ static int netlbl_cipsov4_add_std(struct genl_info *info, nla_a_rem) if (nla_type(nla_a) == NLBL_CIPSOV4_A_MLSCAT) { if (nla_validate_nested(nla_a, - NLBL_CIPSOV4_A_MAX, - netlbl_cipsov4_genl_policy) != 0) + NLBL_CIPSOV4_A_MAX, + netlbl_cipsov4_genl_policy, + NULL) != 0) goto add_std_failure; nla_for_each_nested(nla_b, nla_a, nla_b_rem) switch (nla_type(nla_b)) { diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 4b598a5999a2..ff6db5e66eb5 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -574,7 +574,7 @@ static int genl_family_rcv_msg(const struct genl_family *family, if (attrbuf) { err = nlmsg_parse(nlh, hdrlen, attrbuf, family->maxattr, - ops->policy); + ops->policy, NULL); if (err < 0) goto out; } diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 03f3d5c7beb8..aca903c12671 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -119,7 +119,8 @@ static struct nfc_dev *__get_device_from_cb(struct netlink_callback *cb) u32 idx; rc = nlmsg_parse(cb->nlh, GENL_HDRLEN + nfc_genl_family.hdrsize, - attrbuf, nfc_genl_family.maxattr, nfc_genl_policy); + attrbuf, nfc_genl_family.maxattr, nfc_genl_policy, + NULL); if (rc < 0) return ERR_PTR(rc); @@ -1161,7 +1162,7 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(attr, info->attrs[NFC_ATTR_LLC_SDP], rem) { rc = nla_parse_nested(sdp_attrs, NFC_SDP_ATTR_MAX, attr, - nfc_sdp_genl_policy); + nfc_sdp_genl_policy, NULL); if (rc != 0) { rc = -EINVAL; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 9c62b6325f7a..7b17da9a94a0 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -1353,7 +1353,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) int err; err = genlmsg_parse(cb->nlh, &dp_flow_genl_family, a, - OVS_FLOW_ATTR_MAX, flow_policy); + OVS_FLOW_ATTR_MAX, flow_policy, NULL); if (err) return err; ufid_flags = ovs_nla_get_ufid_flags(a[OVS_FLOW_ATTR_UFID_FLAGS]); diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index df82b81a9b35..7e1d8a2afa63 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -2427,8 +2427,8 @@ static int validate_userspace(const struct nlattr *attr) struct nlattr *a[OVS_USERSPACE_ATTR_MAX + 1]; int error; - error = nla_parse_nested(a, OVS_USERSPACE_ATTR_MAX, - attr, userspace_policy); + error = nla_parse_nested(a, OVS_USERSPACE_ATTR_MAX, attr, + userspace_policy, NULL); if (error) return error; diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c index 7eb955e453e6..869acb3b3d3f 100644 --- a/net/openvswitch/vport-vxlan.c +++ b/net/openvswitch/vport-vxlan.c @@ -70,7 +70,8 @@ static int vxlan_configure_exts(struct vport *vport, struct nlattr *attr, if (nla_len(attr) < sizeof(struct nlattr)) return -EINVAL; - err = nla_parse_nested(exts, OVS_VXLAN_EXT_MAX, attr, exts_policy); + err = nla_parse_nested(exts, OVS_VXLAN_EXT_MAX, attr, exts_policy, + NULL); if (err < 0) return err; diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c index bc5ee5fbe6ae..363799bf97f6 100644 --- a/net/phonet/pn_netlink.c +++ b/net/phonet/pn_netlink.c @@ -78,7 +78,8 @@ static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh) ASSERT_RTNL(); - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_phonet_policy); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_phonet_policy, + NULL); if (err < 0) return err; @@ -243,7 +244,8 @@ static int route_doit(struct sk_buff *skb, struct nlmsghdr *nlh) ASSERT_RTNL(); - err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_phonet_policy); + err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_phonet_policy, + NULL); if (err < 0) return err; diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c index ae5ac175b2be..7fdbb34002f5 100644 --- a/net/qrtr/qrtr.c +++ b/net/qrtr/qrtr.c @@ -957,7 +957,7 @@ static int qrtr_addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh) ASSERT_RTNL(); - rc = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, qrtr_policy); + rc = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, qrtr_policy, NULL); if (rc < 0) return rc; diff --git a/net/sched/act_api.c b/net/sched/act_api.c index b70aa57319ea..79d875c6e8a0 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -557,7 +557,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, int err; if (name == NULL) { - err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL); + err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL, NULL); if (err < 0) goto err_out; err = -EINVAL; @@ -654,7 +654,7 @@ int tcf_action_init(struct net *net, struct nlattr *nla, struct nlattr *est, int err; int i; - err = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL); + err = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL, NULL); if (err < 0) return err; @@ -786,7 +786,7 @@ static struct tc_action *tcf_action_get_1(struct net *net, struct nlattr *nla, int index; int err; - err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL); + err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL, NULL); if (err < 0) goto err_out; @@ -835,7 +835,7 @@ static int tca_action_flush(struct net *net, struct nlattr *nla, b = skb_tail_pointer(skb); - err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL); + err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL, NULL); if (err < 0) goto err_out; @@ -921,7 +921,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, struct tc_action *act; LIST_HEAD(actions); - ret = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL); + ret = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL, NULL); if (ret < 0) return ret; @@ -1004,7 +1004,8 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n) !netlink_capable(skb, CAP_NET_ADMIN)) return -EPERM; - ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL); + ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL, + NULL); if (ret < 0) return ret; @@ -1051,19 +1052,20 @@ static struct nlattr *find_dump_kind(const struct nlmsghdr *n) struct nlattr *nla[TCAA_MAX + 1]; struct nlattr *kind; - if (nlmsg_parse(n, sizeof(struct tcamsg), nla, TCAA_MAX, NULL) < 0) + if (nlmsg_parse(n, sizeof(struct tcamsg), nla, TCAA_MAX, + NULL, NULL) < 0) return NULL; tb1 = nla[TCA_ACT_TAB]; if (tb1 == NULL) return NULL; if (nla_parse(tb, TCA_ACT_MAX_PRIO, nla_data(tb1), - NLMSG_ALIGN(nla_len(tb1)), NULL) < 0) + NLMSG_ALIGN(nla_len(tb1)), NULL, NULL) < 0) return NULL; if (tb[1] == NULL) return NULL; - if (nla_parse_nested(tb2, TCA_ACT_MAX, tb[1], NULL) < 0) + if (nla_parse_nested(tb2, TCA_ACT_MAX, tb[1], NULL, NULL) < 0) return NULL; kind = tb2[TCA_ACT_KIND]; diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index 520baa41cba3..d33947d6e9d0 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -283,7 +283,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, if (!nla) return -EINVAL; - ret = nla_parse_nested(tb, TCA_ACT_BPF_MAX, nla, act_bpf_policy); + ret = nla_parse_nested(tb, TCA_ACT_BPF_MAX, nla, act_bpf_policy, NULL); if (ret < 0) return ret; diff --git a/net/sched/act_connmark.c b/net/sched/act_connmark.c index f9bb43c25697..2155bc6c6a1e 100644 --- a/net/sched/act_connmark.c +++ b/net/sched/act_connmark.c @@ -109,7 +109,8 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla, if (!nla) return -EINVAL; - ret = nla_parse_nested(tb, TCA_CONNMARK_MAX, nla, connmark_policy); + ret = nla_parse_nested(tb, TCA_CONNMARK_MAX, nla, connmark_policy, + NULL); if (ret < 0) return ret; diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index 6c319a40c1cc..ab6fdbd34db7 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -59,7 +59,7 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla, if (nla == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_CSUM_MAX, nla, csum_policy); + err = nla_parse_nested(tb, TCA_CSUM_MAX, nla, csum_policy, NULL); if (err < 0) return err; diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index e6c874a2b283..99afe8b1f1fb 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -73,7 +73,7 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, if (nla == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_GACT_MAX, nla, gact_policy); + err = nla_parse_nested(tb, TCA_GACT_MAX, nla, gact_policy, NULL); if (err < 0) return err; diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c index c75ea5c9102c..c5dec308b8b1 100644 --- a/net/sched/act_ife.c +++ b/net/sched/act_ife.c @@ -443,7 +443,7 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, int ret = 0; int err; - err = nla_parse_nested(tb, TCA_IFE_MAX, nla, ife_policy); + err = nla_parse_nested(tb, TCA_IFE_MAX, nla, ife_policy, NULL); if (err < 0) return err; @@ -514,7 +514,7 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, if (tb[TCA_IFE_METALST]) { err = nla_parse_nested(tb2, IFE_META_MAX, tb[TCA_IFE_METALST], - NULL); + NULL, NULL); if (err) { metadata_parse_err: if (exists) diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 992ef8d624f1..36f0ced9e60c 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -107,7 +107,7 @@ static int __tcf_ipt_init(struct tc_action_net *tn, struct nlattr *nla, if (nla == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_IPT_MAX, nla, ipt_policy); + err = nla_parse_nested(tb, TCA_IPT_MAX, nla, ipt_policy, NULL); if (err < 0) return err; diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index af49c7dca860..1b5549ababd4 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -87,7 +87,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, if (nla == NULL) return -EINVAL; - ret = nla_parse_nested(tb, TCA_MIRRED_MAX, nla, mirred_policy); + ret = nla_parse_nested(tb, TCA_MIRRED_MAX, nla, mirred_policy, NULL); if (ret < 0) return ret; if (tb[TCA_MIRRED_PARMS] == NULL) diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c index 9b6aec665495..9016ab8a0649 100644 --- a/net/sched/act_nat.c +++ b/net/sched/act_nat.c @@ -50,7 +50,7 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, if (nla == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_NAT_MAX, nla, nat_policy); + err = nla_parse_nested(tb, TCA_NAT_MAX, nla, nat_policy, NULL); if (err < 0) return err; diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index c1310472f620..164b5ac094be 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -72,7 +72,7 @@ static struct tcf_pedit_key_ex *tcf_pedit_keys_ex_parse(struct nlattr *nla, } err = nla_parse_nested(tb, TCA_PEDIT_KEY_EX_MAX, ka, - pedit_key_ex_policy); + pedit_key_ex_policy, NULL); if (err) goto err_out; @@ -147,7 +147,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, if (nla == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_PEDIT_MAX, nla, pedit_policy); + err = nla_parse_nested(tb, TCA_PEDIT_MAX, nla, pedit_policy, NULL); if (err < 0) return err; diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 0ba91d1ce994..f42008b29311 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -90,7 +90,7 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla, if (nla == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_POLICE_MAX, nla, police_policy); + err = nla_parse_nested(tb, TCA_POLICE_MAX, nla, police_policy, NULL); if (err < 0) return err; diff --git a/net/sched/act_sample.c b/net/sched/act_sample.c index 0b8217b4763f..59d6645a4007 100644 --- a/net/sched/act_sample.c +++ b/net/sched/act_sample.c @@ -50,7 +50,7 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla, if (!nla) return -EINVAL; - ret = nla_parse_nested(tb, TCA_SAMPLE_MAX, nla, sample_policy); + ret = nla_parse_nested(tb, TCA_SAMPLE_MAX, nla, sample_policy, NULL); if (ret < 0) return ret; if (!tb[TCA_SAMPLE_PARMS] || !tb[TCA_SAMPLE_RATE] || diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index 823a73ad0c60..43605e7ce051 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -94,7 +94,7 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla, if (nla == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_DEF_MAX, nla, simple_policy); + err = nla_parse_nested(tb, TCA_DEF_MAX, nla, simple_policy, NULL); if (err < 0) return err; diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index 06ccae3c12ee..6b3e65d7de0c 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -82,7 +82,7 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, if (nla == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_SKBEDIT_MAX, nla, skbedit_policy); + err = nla_parse_nested(tb, TCA_SKBEDIT_MAX, nla, skbedit_policy, NULL); if (err < 0) return err; diff --git a/net/sched/act_skbmod.c b/net/sched/act_skbmod.c index c736627f8f4a..a73c4bbcada2 100644 --- a/net/sched/act_skbmod.c +++ b/net/sched/act_skbmod.c @@ -103,7 +103,7 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla, if (!nla) return -EINVAL; - err = nla_parse_nested(tb, TCA_SKBMOD_MAX, nla, skbmod_policy); + err = nla_parse_nested(tb, TCA_SKBMOD_MAX, nla, skbmod_policy, NULL); if (err < 0) return err; diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c index e3a58e021198..b9a2f241a5b3 100644 --- a/net/sched/act_tunnel_key.c +++ b/net/sched/act_tunnel_key.c @@ -89,7 +89,8 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, if (!nla) return -EINVAL; - err = nla_parse_nested(tb, TCA_TUNNEL_KEY_MAX, nla, tunnel_key_policy); + err = nla_parse_nested(tb, TCA_TUNNEL_KEY_MAX, nla, tunnel_key_policy, + NULL); if (err < 0) return err; diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c index 19e0dba305ce..13ba3a89f675 100644 --- a/net/sched/act_vlan.c +++ b/net/sched/act_vlan.c @@ -121,7 +121,7 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, if (!nla) return -EINVAL; - err = nla_parse_nested(tb, TCA_VLAN_MAX, nla, vlan_policy); + err = nla_parse_nested(tb, TCA_VLAN_MAX, nla, vlan_policy, NULL); if (err < 0) return err; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 732f7cae459d..e2c68c30f97d 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -229,7 +229,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n) replay: tp_created = 0; - err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL); + err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL, NULL); if (err < 0) return err; diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index 5877f6061b57..422414f16b38 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -174,7 +174,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, return -EINVAL; err = nla_parse_nested(tb, TCA_BASIC_MAX, tca[TCA_OPTIONS], - basic_policy); + basic_policy, NULL); if (err < 0) return err; diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 80f688436dd7..7ddd08efaa0f 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -478,7 +478,8 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, if (tca[TCA_OPTIONS] == NULL) return -EINVAL; - ret = nla_parse_nested(tb, TCA_BPF_MAX, tca[TCA_OPTIONS], bpf_policy); + ret = nla_parse_nested(tb, TCA_BPF_MAX, tca[TCA_OPTIONS], bpf_policy, + NULL); if (ret < 0) return ret; diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index c1f20077837f..b5e7c1bee6c3 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -99,7 +99,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, new->handle = handle; new->tp = tp; err = nla_parse_nested(tb, TCA_CGROUP_MAX, tca[TCA_OPTIONS], - cgroup_policy); + cgroup_policy, NULL); if (err < 0) goto errout; diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index ca193af8634a..008ba7e63b7a 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -400,7 +400,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, if (opt == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_FLOW_MAX, opt, flow_policy); + err = nla_parse_nested(tb, TCA_FLOW_MAX, opt, flow_policy, NULL); if (err < 0) return err; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 9d0c99d2e9fb..3e7bd7801aa8 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -848,7 +848,8 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, if (!tb) return -ENOBUFS; - err = nla_parse_nested(tb, TCA_FLOWER_MAX, tca[TCA_OPTIONS], fl_policy); + err = nla_parse_nested(tb, TCA_FLOWER_MAX, tca[TCA_OPTIONS], + fl_policy, NULL); if (err < 0) goto errout_tb; diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 9dc63d54e167..996209083c6b 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -250,7 +250,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, if (!opt) return handle ? -EINVAL : 0; /* Succeed if it is old method. */ - err = nla_parse_nested(tb, TCA_FW_MAX, opt, fw_policy); + err = nla_parse_nested(tb, TCA_FW_MAX, opt, fw_policy, NULL); if (err < 0) return err; diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index 224eb2c14346..0dbcca62aa6a 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -161,8 +161,8 @@ static int mall_change(struct net *net, struct sk_buff *in_skb, if (head) return -EEXIST; - err = nla_parse_nested(tb, TCA_MATCHALL_MAX, - tca[TCA_OPTIONS], mall_policy); + err = nla_parse_nested(tb, TCA_MATCHALL_MAX, tca[TCA_OPTIONS], + mall_policy, NULL); if (err < 0) return err; diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 455fc8f83d0a..a371075c1d7a 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -489,7 +489,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, if (opt == NULL) return handle ? -EINVAL : 0; - err = nla_parse_nested(tb, TCA_ROUTE4_MAX, opt, route4_policy); + err = nla_parse_nested(tb, TCA_ROUTE4_MAX, opt, route4_policy, NULL); if (err < 0) return err; diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 322438fb3ffc..d7f2923e6ebd 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -484,7 +484,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb, if (opt == NULL) return handle ? -EINVAL : 0; - err = nla_parse_nested(tb, TCA_RSVP_MAX, opt, rsvp_policy); + err = nla_parse_nested(tb, TCA_RSVP_MAX, opt, rsvp_policy, NULL); if (err < 0) return err; diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index 0751245a6ace..2ab001361457 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -482,7 +482,7 @@ tcindex_change(struct net *net, struct sk_buff *in_skb, if (!opt) return 0; - err = nla_parse_nested(tb, TCA_TCINDEX_MAX, opt, tcindex_policy); + err = nla_parse_nested(tb, TCA_TCINDEX_MAX, opt, tcindex_policy, NULL); if (err < 0) return err; diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 4dbe0c680fe6..9e2f330ac80f 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -860,7 +860,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, if (opt == NULL) return handle ? -EINVAL : 0; - err = nla_parse_nested(tb, TCA_U32_MAX, opt, u32_policy); + err = nla_parse_nested(tb, TCA_U32_MAX, opt, u32_policy, NULL); if (err < 0) return err; diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c index ae7e4f5b348b..eb0e9bab54c1 100644 --- a/net/sched/em_meta.c +++ b/net/sched/em_meta.c @@ -912,7 +912,7 @@ static int em_meta_change(struct net *net, void *data, int len, struct tcf_meta_hdr *hdr; struct meta_match *meta = NULL; - err = nla_parse(tb, TCA_EM_META_MAX, data, len, meta_policy); + err = nla_parse(tb, TCA_EM_META_MAX, data, len, meta_policy, NULL); if (err < 0) goto errout; diff --git a/net/sched/ematch.c b/net/sched/ematch.c index fbb7ebfc58c6..03b677bc0700 100644 --- a/net/sched/ematch.c +++ b/net/sched/ematch.c @@ -314,7 +314,7 @@ int tcf_em_tree_validate(struct tcf_proto *tp, struct nlattr *nla, if (!nla) return 0; - err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, nla, em_policy); + err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, nla, em_policy, NULL); if (err < 0) goto errout; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 62567bfe52c7..fcb5ae581c04 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -457,7 +457,7 @@ static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt) u16 *tab = NULL; int err; - err = nla_parse_nested(tb, TCA_STAB_MAX, opt, stab_policy); + err = nla_parse_nested(tb, TCA_STAB_MAX, opt, stab_policy, NULL); if (err < 0) return ERR_PTR(err); if (!tb[TCA_STAB_BASE]) @@ -1131,7 +1131,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n) !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) return -EPERM; - err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL); + err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL, NULL); if (err < 0) return err; @@ -1200,7 +1200,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n) replay: /* Reinit, just in case something touches this. */ - err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL); + err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL, NULL); if (err < 0) return err; @@ -1515,7 +1515,7 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) idx = 0; ASSERT_RTNL(); - err = nlmsg_parse(nlh, sizeof(*tcm), tca, TCA_MAX, NULL); + err = nlmsg_parse(nlh, sizeof(*tcm), tca, TCA_MAX, NULL, NULL); if (err < 0) return err; @@ -1577,7 +1577,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n) !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) return -EPERM; - err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL); + err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL, NULL); if (err < 0) return err; diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 2209c2ddacbf..40cbceed4de8 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -214,7 +214,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, if (opt == NULL) return -EINVAL; - error = nla_parse_nested(tb, TCA_ATM_MAX, opt, atm_policy); + error = nla_parse_nested(tb, TCA_ATM_MAX, opt, atm_policy, NULL); if (error < 0) return error; diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index cf93e5ff3d63..7415859fd4c3 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1137,7 +1137,7 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt) struct tc_ratespec *r; int err; - err = nla_parse_nested(tb, TCA_CBQ_MAX, opt, cbq_policy); + err = nla_parse_nested(tb, TCA_CBQ_MAX, opt, cbq_policy, NULL); if (err < 0) return err; @@ -1474,7 +1474,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t if (opt == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_CBQ_MAX, opt, cbq_policy); + err = nla_parse_nested(tb, TCA_CBQ_MAX, opt, cbq_policy, NULL); if (err < 0) return err; diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c index 593183a5b5b5..d00f4c7c2f3a 100644 --- a/net/sched/sch_choke.c +++ b/net/sched/sch_choke.c @@ -357,7 +357,7 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt) if (opt == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_CHOKE_MAX, opt, choke_policy); + err = nla_parse_nested(tb, TCA_CHOKE_MAX, opt, choke_policy, NULL); if (err < 0) return err; diff --git a/net/sched/sch_codel.c b/net/sched/sch_codel.c index 5bfa79ee657c..c518a1efcb9d 100644 --- a/net/sched/sch_codel.c +++ b/net/sched/sch_codel.c @@ -140,7 +140,7 @@ static int codel_change(struct Qdisc *sch, struct nlattr *opt) if (!opt) return -EINVAL; - err = nla_parse_nested(tb, TCA_CODEL_MAX, opt, codel_policy); + err = nla_parse_nested(tb, TCA_CODEL_MAX, opt, codel_policy, NULL); if (err < 0) return err; diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index 9fe67e257dfa..58a8c32eab23 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -76,7 +76,7 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid, if (!opt) return -EINVAL; - err = nla_parse_nested(tb, TCA_DRR_MAX, opt, drr_policy); + err = nla_parse_nested(tb, TCA_DRR_MAX, opt, drr_policy, NULL); if (err < 0) return err; diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index cfa1f2cdbaf7..1c0f877f673a 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -129,7 +129,7 @@ static int dsmark_change(struct Qdisc *sch, u32 classid, u32 parent, if (!opt) goto errout; - err = nla_parse_nested(tb, TCA_DSMARK_MAX, opt, dsmark_policy); + err = nla_parse_nested(tb, TCA_DSMARK_MAX, opt, dsmark_policy, NULL); if (err < 0) goto errout; @@ -342,7 +342,7 @@ static int dsmark_init(struct Qdisc *sch, struct nlattr *opt) if (!opt) goto errout; - err = nla_parse_nested(tb, TCA_DSMARK_MAX, opt, dsmark_policy); + err = nla_parse_nested(tb, TCA_DSMARK_MAX, opt, dsmark_policy, NULL); if (err < 0) goto errout; diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c index a4f738ac7728..da4f67bda0ee 100644 --- a/net/sched/sch_fq.c +++ b/net/sched/sch_fq.c @@ -698,7 +698,7 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt) if (!opt) return -EINVAL; - err = nla_parse_nested(tb, TCA_FQ_MAX, opt, fq_policy); + err = nla_parse_nested(tb, TCA_FQ_MAX, opt, fq_policy, NULL); if (err < 0) return err; diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index 097bbe9857a5..18bbb5476c83 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -383,7 +383,8 @@ static int fq_codel_change(struct Qdisc *sch, struct nlattr *opt) if (!opt) return -EINVAL; - err = nla_parse_nested(tb, TCA_FQ_CODEL_MAX, opt, fq_codel_policy); + err = nla_parse_nested(tb, TCA_FQ_CODEL_MAX, opt, fq_codel_policy, + NULL); if (err < 0) return err; if (tb[TCA_FQ_CODEL_FLOWS]) { diff --git a/net/sched/sch_gred.c b/net/sched/sch_gred.c index c78a093c551a..17c7130454bd 100644 --- a/net/sched/sch_gred.c +++ b/net/sched/sch_gred.c @@ -401,7 +401,7 @@ static int gred_change(struct Qdisc *sch, struct nlattr *opt) if (opt == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_GRED_MAX, opt, gred_policy); + err = nla_parse_nested(tb, TCA_GRED_MAX, opt, gred_policy, NULL); if (err < 0) return err; @@ -470,7 +470,7 @@ static int gred_init(struct Qdisc *sch, struct nlattr *opt) if (opt == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_GRED_MAX, opt, gred_policy); + err = nla_parse_nested(tb, TCA_GRED_MAX, opt, gred_policy, NULL); if (err < 0) return err; diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 0198c6cdda49..5cb82f6c1b06 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -957,7 +957,7 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid, if (opt == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_HFSC_MAX, opt, hfsc_policy); + err = nla_parse_nested(tb, TCA_HFSC_MAX, opt, hfsc_policy, NULL); if (err < 0) return err; diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c index 2fae8b5f1b80..c19d346e6c5a 100644 --- a/net/sched/sch_hhf.c +++ b/net/sched/sch_hhf.c @@ -529,7 +529,7 @@ static int hhf_change(struct Qdisc *sch, struct nlattr *opt) if (!opt) return -EINVAL; - err = nla_parse_nested(tb, TCA_HHF_MAX, opt, hhf_policy); + err = nla_parse_nested(tb, TCA_HHF_MAX, opt, hhf_policy, NULL); if (err < 0) return err; diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 95867033542e..570ef3b0c09b 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1017,7 +1017,7 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt) if (!opt) return -EINVAL; - err = nla_parse_nested(tb, TCA_HTB_MAX, opt, htb_policy); + err = nla_parse_nested(tb, TCA_HTB_MAX, opt, htb_policy, NULL); if (err < 0) return err; @@ -1342,7 +1342,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, if (!opt) goto failure; - err = nla_parse_nested(tb, TCA_HTB_MAX, opt, htb_policy); + err = nla_parse_nested(tb, TCA_HTB_MAX, opt, htb_policy, NULL); if (err < 0) goto failure; diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 94b4928ad413..f0ce4780f395 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -843,7 +843,7 @@ static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla, if (nested_len >= nla_attr_size(0)) return nla_parse(tb, maxtype, nla_data(nla) + NLA_ALIGN(len), - nested_len, policy); + nested_len, policy, NULL); memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); return 0; diff --git a/net/sched/sch_pie.c b/net/sched/sch_pie.c index 5c3a99d6aa82..6c2791d6102d 100644 --- a/net/sched/sch_pie.c +++ b/net/sched/sch_pie.c @@ -190,7 +190,7 @@ static int pie_change(struct Qdisc *sch, struct nlattr *opt) if (!opt) return -EINVAL; - err = nla_parse_nested(tb, TCA_PIE_MAX, opt, pie_policy); + err = nla_parse_nested(tb, TCA_PIE_MAX, opt, pie_policy, NULL); if (err < 0) return err; diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index 6c85f3e9239b..041eba3006cc 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -418,7 +418,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, return -EINVAL; } - err = nla_parse_nested(tb, TCA_QFQ_MAX, tca[TCA_OPTIONS], qfq_policy); + err = nla_parse_nested(tb, TCA_QFQ_MAX, tca[TCA_OPTIONS], qfq_policy, + NULL); if (err < 0) return err; diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index 799ea6dd69b2..11292adce412 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -173,7 +173,7 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt) if (opt == NULL) return -EINVAL; - err = nla_parse_nested(tb, TCA_RED_MAX, opt, red_policy); + err = nla_parse_nested(tb, TCA_RED_MAX, opt, red_policy, NULL); if (err < 0) return err; diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index ae862f172c94..0f777273ba29 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -495,7 +495,7 @@ static int sfb_change(struct Qdisc *sch, struct nlattr *opt) int err; if (opt) { - err = nla_parse_nested(tb, TCA_SFB_MAX, opt, sfb_policy); + err = nla_parse_nested(tb, TCA_SFB_MAX, opt, sfb_policy, NULL); if (err < 0) return -EINVAL; diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 9850126129a3..b2e4b6ad241a 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -315,7 +315,7 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt) s64 buffer, mtu; u64 rate64 = 0, prate64 = 0; - err = nla_parse_nested(tb, TCA_TBF_MAX, opt, tbf_policy); + err = nla_parse_nested(tb, TCA_TBF_MAX, opt, tbf_policy, NULL); if (err < 0) return err; diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 017801f9dbaa..8d40a7d31c99 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -826,7 +826,7 @@ static int switchdev_port_br_setlink_protinfo(struct net_device *dev, int err; err = nla_validate_nested(protinfo, IFLA_BRPORT_MAX, - switchdev_port_bridge_policy); + switchdev_port_bridge_policy, NULL); if (err) return err; diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 33a5bdfbef76..9b5c45f48f60 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -802,7 +802,7 @@ int tipc_nl_bearer_get(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, info->attrs[TIPC_NLA_BEARER], - tipc_nl_bearer_policy); + tipc_nl_bearer_policy, NULL); if (err) return err; @@ -851,7 +851,7 @@ int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, info->attrs[TIPC_NLA_BEARER], - tipc_nl_bearer_policy); + tipc_nl_bearer_policy, NULL); if (err) return err; @@ -891,7 +891,7 @@ int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, info->attrs[TIPC_NLA_BEARER], - tipc_nl_bearer_policy); + tipc_nl_bearer_policy, NULL); if (err) return err; @@ -939,7 +939,7 @@ int tipc_nl_bearer_add(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, info->attrs[TIPC_NLA_BEARER], - tipc_nl_bearer_policy); + tipc_nl_bearer_policy, NULL); if (err) return err; @@ -982,7 +982,7 @@ int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, info->attrs[TIPC_NLA_BEARER], - tipc_nl_bearer_policy); + tipc_nl_bearer_policy, NULL); if (err) return err; @@ -1104,7 +1104,7 @@ int tipc_nl_media_get(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_MEDIA_MAX, info->attrs[TIPC_NLA_MEDIA], - tipc_nl_media_policy); + tipc_nl_media_policy, NULL); if (err) return err; @@ -1152,7 +1152,7 @@ int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_MEDIA_MAX, info->attrs[TIPC_NLA_MEDIA], - tipc_nl_media_policy); + tipc_nl_media_policy, NULL); if (!attrs[TIPC_NLA_MEDIA_NAME]) return -EINVAL; diff --git a/net/tipc/link.c b/net/tipc/link.c index ddd2dd6f77aa..60820dc35a08 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -1827,7 +1827,7 @@ int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[]) int err; err = nla_parse_nested(props, TIPC_NLA_PROP_MAX, prop, - tipc_nl_prop_policy); + tipc_nl_prop_policy, NULL); if (err) return err; diff --git a/net/tipc/net.c b/net/tipc/net.c index ab8a2d5d1e32..836da66cd852 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -211,8 +211,8 @@ int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info) return -EINVAL; err = nla_parse_nested(attrs, TIPC_NLA_NET_MAX, - info->attrs[TIPC_NLA_NET], - tipc_nl_net_policy); + info->attrs[TIPC_NLA_NET], tipc_nl_net_policy, + NULL); if (err) return err; diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 26ca8dd64ded..b76f13f6fea1 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -268,7 +268,8 @@ int tipc_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr ***attr) if (!*attr) return -EOPNOTSUPP; - return nlmsg_parse(nlh, GENL_HDRLEN, *attr, maxattr, tipc_nl_policy); + return nlmsg_parse(nlh, GENL_HDRLEN, *attr, maxattr, tipc_nl_policy, + NULL); } int __init tipc_netlink_start(void) diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c index e1ae8a8a2b8e..9bfe886ab330 100644 --- a/net/tipc/netlink_compat.c +++ b/net/tipc/netlink_compat.c @@ -296,7 +296,7 @@ static int __tipc_nl_compat_doit(struct tipc_nl_compat_cmd_doit *cmd, err = nla_parse(attrbuf, tipc_genl_family.maxattr, (const struct nlattr *)trans_buf->data, - trans_buf->len, NULL); + trans_buf->len, NULL, NULL); if (err) goto parse_out; @@ -352,7 +352,7 @@ static int tipc_nl_compat_bearer_dump(struct tipc_nl_compat_msg *msg, return -EINVAL; err = nla_parse_nested(bearer, TIPC_NLA_BEARER_MAX, - attrs[TIPC_NLA_BEARER], NULL); + attrs[TIPC_NLA_BEARER], NULL, NULL); if (err) return err; @@ -472,7 +472,7 @@ static int tipc_nl_compat_link_stat_dump(struct tipc_nl_compat_msg *msg, return -EINVAL; err = nla_parse_nested(link, TIPC_NLA_LINK_MAX, attrs[TIPC_NLA_LINK], - NULL); + NULL, NULL); if (err) return err; @@ -480,7 +480,7 @@ static int tipc_nl_compat_link_stat_dump(struct tipc_nl_compat_msg *msg, return -EINVAL; err = nla_parse_nested(prop, TIPC_NLA_PROP_MAX, - link[TIPC_NLA_LINK_PROP], NULL); + link[TIPC_NLA_LINK_PROP], NULL, NULL); if (err) return err; @@ -488,7 +488,7 @@ static int tipc_nl_compat_link_stat_dump(struct tipc_nl_compat_msg *msg, return -EINVAL; err = nla_parse_nested(stats, TIPC_NLA_STATS_MAX, - link[TIPC_NLA_LINK_STATS], NULL); + link[TIPC_NLA_LINK_STATS], NULL, NULL); if (err) return err; @@ -598,7 +598,7 @@ static int tipc_nl_compat_link_dump(struct tipc_nl_compat_msg *msg, return -EINVAL; err = nla_parse_nested(link, TIPC_NLA_LINK_MAX, attrs[TIPC_NLA_LINK], - NULL); + NULL, NULL); if (err) return err; @@ -795,7 +795,7 @@ static int tipc_nl_compat_name_table_dump(struct tipc_nl_compat_msg *msg, return -EINVAL; err = nla_parse_nested(nt, TIPC_NLA_NAME_TABLE_MAX, - attrs[TIPC_NLA_NAME_TABLE], NULL); + attrs[TIPC_NLA_NAME_TABLE], NULL, NULL); if (err) return err; @@ -803,7 +803,7 @@ static int tipc_nl_compat_name_table_dump(struct tipc_nl_compat_msg *msg, return -EINVAL; err = nla_parse_nested(publ, TIPC_NLA_PUBL_MAX, - nt[TIPC_NLA_NAME_TABLE_PUBL], NULL); + nt[TIPC_NLA_NAME_TABLE_PUBL], NULL, NULL); if (err) return err; @@ -863,7 +863,7 @@ static int __tipc_nl_compat_publ_dump(struct tipc_nl_compat_msg *msg, return -EINVAL; err = nla_parse_nested(publ, TIPC_NLA_PUBL_MAX, attrs[TIPC_NLA_PUBL], - NULL); + NULL, NULL); if (err) return err; @@ -929,7 +929,7 @@ static int tipc_nl_compat_sk_dump(struct tipc_nl_compat_msg *msg, return -EINVAL; err = nla_parse_nested(sock, TIPC_NLA_SOCK_MAX, attrs[TIPC_NLA_SOCK], - NULL); + NULL, NULL); if (err) return err; @@ -940,8 +940,8 @@ static int tipc_nl_compat_sk_dump(struct tipc_nl_compat_msg *msg, u32 node; struct nlattr *con[TIPC_NLA_CON_MAX + 1]; - nla_parse_nested(con, TIPC_NLA_CON_MAX, sock[TIPC_NLA_SOCK_CON], - NULL); + nla_parse_nested(con, TIPC_NLA_CON_MAX, + sock[TIPC_NLA_SOCK_CON], NULL, NULL); node = nla_get_u32(con[TIPC_NLA_CON_NODE]); tipc_tlv_sprintf(msg->rep, " connected to <%u.%u.%u:%u>", @@ -977,8 +977,8 @@ static int tipc_nl_compat_media_dump(struct tipc_nl_compat_msg *msg, if (!attrs[TIPC_NLA_MEDIA]) return -EINVAL; - err = nla_parse_nested(media, TIPC_NLA_MEDIA_MAX, attrs[TIPC_NLA_MEDIA], - NULL); + err = nla_parse_nested(media, TIPC_NLA_MEDIA_MAX, + attrs[TIPC_NLA_MEDIA], NULL, NULL); if (err) return err; @@ -998,7 +998,7 @@ static int tipc_nl_compat_node_dump(struct tipc_nl_compat_msg *msg, return -EINVAL; err = nla_parse_nested(node, TIPC_NLA_NODE_MAX, attrs[TIPC_NLA_NODE], - NULL); + NULL, NULL); if (err) return err; @@ -1045,7 +1045,7 @@ static int tipc_nl_compat_net_dump(struct tipc_nl_compat_msg *msg, return -EINVAL; err = nla_parse_nested(net, TIPC_NLA_NET_MAX, attrs[TIPC_NLA_NET], - NULL); + NULL, NULL); if (err) return err; diff --git a/net/tipc/node.c b/net/tipc/node.c index 4512e83652b1..1dcde24c7053 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -1607,8 +1607,8 @@ int tipc_nl_peer_rm(struct sk_buff *skb, struct genl_info *info) return -EINVAL; err = nla_parse_nested(attrs, TIPC_NLA_NET_MAX, - info->attrs[TIPC_NLA_NET], - tipc_nl_net_policy); + info->attrs[TIPC_NLA_NET], tipc_nl_net_policy, + NULL); if (err) return err; @@ -1774,7 +1774,7 @@ int tipc_nl_node_set_link(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX, info->attrs[TIPC_NLA_LINK], - tipc_nl_link_policy); + tipc_nl_link_policy, NULL); if (err) return err; @@ -1902,7 +1902,7 @@ int tipc_nl_node_reset_link_stats(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX, info->attrs[TIPC_NLA_LINK], - tipc_nl_link_policy); + tipc_nl_link_policy, NULL); if (err) return err; @@ -2042,7 +2042,7 @@ int tipc_nl_node_set_monitor(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_MON_MAX, info->attrs[TIPC_NLA_MON], - tipc_nl_monitor_policy); + tipc_nl_monitor_policy, NULL); if (err) return err; @@ -2163,7 +2163,7 @@ int tipc_nl_node_dump_monitor_peer(struct sk_buff *skb, err = nla_parse_nested(mon, TIPC_NLA_MON_MAX, attrs[TIPC_NLA_MON], - tipc_nl_monitor_policy); + tipc_nl_monitor_policy, NULL); if (err) return err; diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 15f6ce7bf868..740100abeec3 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -2866,7 +2866,7 @@ int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb) err = nla_parse_nested(sock, TIPC_NLA_SOCK_MAX, attrs[TIPC_NLA_SOCK], - tipc_nl_sock_policy); + tipc_nl_sock_policy, NULL); if (err) return err; diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c index 46061cf48cd1..ecca64fc6a6f 100644 --- a/net/tipc/udp_media.c +++ b/net/tipc/udp_media.c @@ -457,7 +457,7 @@ int tipc_udp_nl_dump_remoteip(struct sk_buff *skb, struct netlink_callback *cb) err = nla_parse_nested(battrs, TIPC_NLA_BEARER_MAX, attrs[TIPC_NLA_BEARER], - tipc_nl_bearer_policy); + tipc_nl_bearer_policy, NULL); if (err) return err; @@ -609,7 +609,8 @@ int tipc_udp_nl_bearer_add(struct tipc_bearer *b, struct nlattr *attr) struct nlattr *opts[TIPC_NLA_UDP_MAX + 1]; struct udp_media_addr *dst; - if (nla_parse_nested(opts, TIPC_NLA_UDP_MAX, attr, tipc_nl_udp_policy)) + if (nla_parse_nested(opts, TIPC_NLA_UDP_MAX, attr, + tipc_nl_udp_policy, NULL)) return -EINVAL; if (!opts[TIPC_NLA_UDP_REMOTE]) @@ -662,7 +663,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, if (nla_parse_nested(opts, TIPC_NLA_UDP_MAX, attrs[TIPC_NLA_BEARER_UDP_OPTS], - tipc_nl_udp_policy)) + tipc_nl_udp_policy, NULL)) goto err; if (!opts[TIPC_NLA_UDP_LOCAL] || !opts[TIPC_NLA_UDP_REMOTE]) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2312dc2ffdb9..e7594a3a0ab6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -548,7 +548,7 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb, if (!cb->args[0]) { err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, genl_family_attrbuf(&nl80211_fam), - nl80211_fam.maxattr, nl80211_policy); + nl80211_fam.maxattr, nl80211_policy, NULL); if (err) return err; @@ -719,7 +719,7 @@ static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k) { struct nlattr *tb[NL80211_KEY_MAX + 1]; int err = nla_parse_nested(tb, NL80211_KEY_MAX, key, - nl80211_key_policy); + nl80211_key_policy, NULL); if (err) return err; @@ -760,7 +760,7 @@ static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k) err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1, tb[NL80211_KEY_DEFAULT_TYPES], - nl80211_key_default_policy); + nl80211_key_default_policy, NULL); if (err) return err; @@ -807,10 +807,10 @@ static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k) if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) { struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; - int err = nla_parse_nested( - kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1, - info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES], - nl80211_key_default_policy); + int err = nla_parse_nested(kdt, + NUM_NL80211_KEY_DEFAULT_TYPES - 1, + info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES], + nl80211_key_default_policy, NULL); if (err) return err; @@ -1892,8 +1892,8 @@ static int nl80211_dump_wiphy_parse(struct sk_buff *skb, struct nl80211_dump_wiphy_state *state) { struct nlattr **tb = genl_family_attrbuf(&nl80211_fam); - int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, - tb, nl80211_fam.maxattr, nl80211_policy); + int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, tb, + nl80211_fam.maxattr, nl80211_policy, NULL); /* ignore parse errors for backward compatibility */ if (ret) return 0; @@ -2308,7 +2308,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) rem_txq_params) { result = nla_parse_nested(tb, NL80211_TXQ_ATTR_MAX, nl_txq_params, - txq_params_policy); + txq_params_policy, NULL); if (result) return result; result = parse_txq_params(tb, &txq_params); @@ -2695,8 +2695,8 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) if (!nla) return -EINVAL; - if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX, - nla, mntr_flags_policy)) + if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX, nla, + mntr_flags_policy, NULL)) return -EINVAL; for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++) @@ -3561,7 +3561,7 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, if (sband == NULL) return -EINVAL; err = nla_parse_nested(tb, NL80211_TXRATE_MAX, tx_rates, - nl80211_txattr_policy); + nl80211_txattr_policy, NULL); if (err) return err; if (tb[NL80211_TXRATE_LEGACY]) { @@ -4100,8 +4100,8 @@ static int parse_station_flags(struct genl_info *info, if (!nla) return 0; - if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX, - nla, sta_flags_policy)) + if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX, nla, + sta_flags_policy, NULL)) return -EINVAL; /* @@ -4728,7 +4728,7 @@ static int nl80211_parse_sta_wme(struct genl_info *info, nla = info->attrs[NL80211_ATTR_STA_WME]; err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla, - nl80211_sta_wme_policy); + nl80211_sta_wme_policy, NULL); if (err) return err; @@ -5853,7 +5853,7 @@ do { \ return -EINVAL; if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX, info->attrs[NL80211_ATTR_MESH_CONFIG], - nl80211_meshconf_params_policy)) + nl80211_meshconf_params_policy, NULL)) return -EINVAL; /* This makes sure that there aren't more than 32 mesh config @@ -6002,7 +6002,7 @@ static int nl80211_parse_mesh_setup(struct genl_info *info, return -EINVAL; if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX, info->attrs[NL80211_ATTR_MESH_SETUP], - nl80211_mesh_setup_params_policy)) + nl80211_mesh_setup_params_policy, NULL)) return -EINVAL; if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC]) @@ -6393,7 +6393,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], rem_reg_rules) { r = nla_parse_nested(tb, NL80211_REG_RULE_ATTR_MAX, - nl_reg_rule, reg_rule_policy); + nl_reg_rule, reg_rule_policy, NULL); if (r) goto bad_reg; r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); @@ -6461,7 +6461,7 @@ static int parse_bss_select(struct nlattr *nla, struct wiphy *wiphy, return -EINVAL; err = nla_parse_nested(attr, NL80211_BSS_SELECT_ATTR_MAX, nest, - nl80211_bss_select_policy); + nl80211_bss_select_policy, NULL); if (err) return err; @@ -6862,7 +6862,7 @@ nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans, return -EINVAL; err = nla_parse_nested(plan, NL80211_SCHED_SCAN_PLAN_MAX, - attr, nl80211_plan_policy); + attr, nl80211_plan_policy, NULL); if (err) return err; @@ -6953,7 +6953,8 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, err = nla_parse_nested(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, - attr, nl80211_match_policy); + attr, nl80211_match_policy, + NULL); if (err) return ERR_PTR(err); /* add other standalone attributes here */ @@ -7132,7 +7133,8 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, err = nla_parse_nested(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, - attr, nl80211_match_policy); + attr, nl80211_match_policy, + NULL); if (err) goto out_free; ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]; @@ -7433,7 +7435,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(csa_attrs, NL80211_ATTR_MAX, info->attrs[NL80211_ATTR_CSA_IES], - nl80211_policy); + nl80211_policy, NULL); if (err) return err; @@ -8639,7 +8641,8 @@ static int nl80211_testmode_dump(struct sk_buff *skb, struct nlattr **attrbuf = genl_family_attrbuf(&nl80211_fam); err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, - attrbuf, nl80211_fam.maxattr, nl80211_policy); + attrbuf, nl80211_fam.maxattr, + nl80211_policy, NULL); if (err) goto out_err; @@ -9530,7 +9533,7 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) return -EINVAL; err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm, - nl80211_attr_cqm_policy); + nl80211_attr_cqm_policy, NULL); if (err) return err; @@ -9940,7 +9943,7 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev, return -EINVAL; err = nla_parse_nested(tb, MAX_NL80211_WOWLAN_TCP, attr, - nl80211_wowlan_tcp_policy); + nl80211_wowlan_tcp_policy, NULL); if (err) return err; @@ -10085,7 +10088,8 @@ static int nl80211_parse_wowlan_nd(struct cfg80211_registered_device *rdev, goto out; } - err = nla_parse_nested(tb, NL80211_ATTR_MAX, attr, nl80211_policy); + err = nla_parse_nested(tb, NL80211_ATTR_MAX, attr, nl80211_policy, + NULL); if (err) goto out; @@ -10122,7 +10126,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(tb, MAX_NL80211_WOWLAN_TRIG, info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS], - nl80211_wowlan_policy); + nl80211_wowlan_policy, NULL); if (err) return err; @@ -10205,7 +10209,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) u8 *mask_pat; nla_parse_nested(pat_tb, MAX_NL80211_PKTPAT, pat, - NULL); + NULL, NULL); err = -EINVAL; if (!pat_tb[NL80211_PKTPAT_MASK] || !pat_tb[NL80211_PKTPAT_PATTERN]) @@ -10416,7 +10420,7 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; err = nla_parse_nested(tb, NL80211_ATTR_COALESCE_RULE_MAX, rule, - nl80211_coalesce_policy); + nl80211_coalesce_policy, NULL); if (err) return err; @@ -10454,7 +10458,7 @@ static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, rem) { u8 *mask_pat; - nla_parse_nested(pat_tb, MAX_NL80211_PKTPAT, pat, NULL); + nla_parse_nested(pat_tb, MAX_NL80211_PKTPAT, pat, NULL, NULL); if (!pat_tb[NL80211_PKTPAT_MASK] || !pat_tb[NL80211_PKTPAT_PATTERN]) return -EINVAL; @@ -10575,7 +10579,7 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(tb, MAX_NL80211_REKEY_DATA, info->attrs[NL80211_ATTR_REKEY_DATA], - nl80211_rekey_policy); + nl80211_rekey_policy, NULL); if (err) return err; @@ -10892,7 +10896,7 @@ static int nl80211_nan_add_func(struct sk_buff *skb, err = nla_parse_nested(tb, NL80211_NAN_FUNC_ATTR_MAX, info->attrs[NL80211_ATTR_NAN_FUNC], - nl80211_nan_func_policy); + nl80211_nan_func_policy, NULL); if (err) return err; @@ -10989,7 +10993,7 @@ static int nl80211_nan_add_func(struct sk_buff *skb, err = nla_parse_nested(srf_tb, NL80211_NAN_SRF_ATTR_MAX, tb[NL80211_NAN_FUNC_SRF], - nl80211_nan_srf_policy); + nl80211_nan_srf_policy, NULL); if (err) goto out; @@ -11524,8 +11528,8 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb, return 0; } - err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, - attrbuf, nl80211_fam.maxattr, nl80211_policy); + err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, attrbuf, + nl80211_fam.maxattr, nl80211_policy, NULL); if (err) return err; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index e93d5c0471b2..804e99a3227c 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -932,8 +932,8 @@ static int xfrm_dump_sa(struct sk_buff *skb, struct netlink_callback *cb) u8 proto = 0; int err; - err = nlmsg_parse(cb->nlh, 0, attrs, XFRMA_MAX, - xfrma_policy); + err = nlmsg_parse(cb->nlh, 0, attrs, XFRMA_MAX, xfrma_policy, + NULL); if (err < 0) return err; @@ -2489,7 +2489,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, link->nla_max ? : XFRMA_MAX, - link->nla_pol ? : xfrma_policy); + link->nla_pol ? : xfrma_policy, NULL); if (err < 0) return err; -- cgit From fe52145f91fe81b994e4622f6b9c3a0f22643363 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 12 Apr 2017 14:34:08 +0200 Subject: netlink: pass extended ACK struct where available This is an add-on to the previous patch that passes the extended ACK structure where it's already available by existing genl_info or extack function arguments. This was done with this spatch (with some manual adjustment of indentation): @@ expression A, B, C, D, E; identifier fn, info; @@ fn(..., struct genl_info *info, ...) { ... -nlmsg_parse(A, B, C, D, E, NULL) +nlmsg_parse(A, B, C, D, E, info->extack) ... } @@ expression A, B, C, D, E; identifier fn, info; @@ fn(..., struct genl_info *info, ...) { <... -nla_parse_nested(A, B, C, D, NULL) +nla_parse_nested(A, B, C, D, info->extack) ...> } @@ expression A, B, C, D, E; identifier fn, extack; @@ fn(..., struct netlink_ext_ack *extack, ...) { <... -nlmsg_parse(A, B, C, D, E, NULL) +nlmsg_parse(A, B, C, D, E, extack) ...> } @@ expression A, B, C, D, E; identifier fn, extack; @@ fn(..., struct netlink_ext_ack *extack, ...) { <... -nla_parse(A, B, C, D, E, NULL) +nla_parse(A, B, C, D, E, extack) ...> } @@ expression A, B, C, D, E; identifier fn, extack; @@ fn(..., struct netlink_ext_ack *extack, ...) { ... -nlmsg_parse(A, B, C, D, E, NULL) +nlmsg_parse(A, B, C, D, E, extack) ... } @@ expression A, B, C, D; identifier fn, extack; @@ fn(..., struct netlink_ext_ack *extack, ...) { <... -nla_parse_nested(A, B, C, D, NULL) +nla_parse_nested(A, B, C, D, extack) ...> } @@ expression A, B, C, D; identifier fn, extack; @@ fn(..., struct netlink_ext_ack *extack, ...) { <... -nlmsg_validate(A, B, C, D, NULL) +nlmsg_validate(A, B, C, D, extack) ...> } @@ expression A, B, C, D; identifier fn, extack; @@ fn(..., struct netlink_ext_ack *extack, ...) { <... -nla_validate(A, B, C, D, NULL) +nla_validate(A, B, C, D, extack) ...> } @@ expression A, B, C; identifier fn, extack; @@ fn(..., struct netlink_ext_ack *extack, ...) { <... -nla_validate_nested(A, B, C, NULL) +nla_validate_nested(A, B, C, extack) ...> } Signed-off-by: Johannes Berg Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- crypto/crypto_user.c | 2 +- drivers/net/team/team.c | 3 ++- net/ieee802154/nl802154.c | 10 +++++----- net/netfilter/ipvs/ip_vs_ctl.c | 2 +- net/netfilter/nfnetlink.c | 2 +- net/netlink/genetlink.c | 2 +- net/nfc/netlink.c | 2 +- net/tipc/bearer.c | 14 +++++++------- net/tipc/net.c | 2 +- net/tipc/node.c | 8 ++++---- net/wireless/nl80211.c | 33 ++++++++++++++++++--------------- net/xfrm/xfrm_user.c | 2 +- 12 files changed, 43 insertions(+), 39 deletions(-) diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index fc79906c1fe7..b5758768920b 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -523,7 +523,7 @@ static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, } err = nlmsg_parse(nlh, crypto_msg_min[type], attrs, CRYPTOCFGA_MAX, - crypto_policy, NULL); + crypto_policy, extack); if (err < 0) return err; diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 86f227124ba1..65c056e2f705 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -2471,7 +2471,8 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) goto team_put; } err = nla_parse_nested(opt_attrs, TEAM_ATTR_OPTION_MAX, - nl_option, team_nl_option_policy, NULL); + nl_option, team_nl_option_policy, + info->extack); if (err) goto team_put; if (!opt_attrs[TEAM_ATTR_OPTION_NAME] || diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c index d6b1a1b21909..99f6c254ea77 100644 --- a/net/ieee802154/nl802154.c +++ b/net/ieee802154/nl802154.c @@ -1564,7 +1564,7 @@ static int nl802154_add_llsec_key(struct sk_buff *skb, struct genl_info *info) if (nla_parse_nested(attrs, NL802154_KEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_KEY], - nl802154_key_policy, NULL)) + nl802154_key_policy, info->extack)) return -EINVAL; if (!attrs[NL802154_KEY_ATTR_USAGE_FRAMES] || @@ -1614,7 +1614,7 @@ static int nl802154_del_llsec_key(struct sk_buff *skb, struct genl_info *info) if (nla_parse_nested(attrs, NL802154_KEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_KEY], - nl802154_key_policy, NULL)) + nl802154_key_policy, info->extack)) return -EINVAL; if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0) @@ -1782,7 +1782,7 @@ static int nl802154_del_llsec_dev(struct sk_buff *skb, struct genl_info *info) if (nla_parse_nested(attrs, NL802154_DEV_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVICE], - nl802154_dev_policy, NULL)) + nl802154_dev_policy, info->extack)) return -EINVAL; if (!attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]) @@ -1910,7 +1910,7 @@ static int nl802154_add_llsec_devkey(struct sk_buff *skb, struct genl_info *info if (!info->attrs[NL802154_ATTR_SEC_DEVKEY] || nla_parse_nested(attrs, NL802154_DEVKEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVKEY], - nl802154_devkey_policy, NULL) < 0) + nl802154_devkey_policy, info->extack) < 0) return -EINVAL; if (!attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER] || @@ -1942,7 +1942,7 @@ static int nl802154_del_llsec_devkey(struct sk_buff *skb, struct genl_info *info if (nla_parse_nested(attrs, NL802154_DEVKEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVKEY], - nl802154_devkey_policy, NULL)) + nl802154_devkey_policy, info->extack)) return -EINVAL; if (!attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]) diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index adb7ee142c5f..892da70866d6 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -3532,7 +3532,7 @@ static int ip_vs_genl_set_daemon(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[IPVS_CMD_ATTR_DAEMON] || nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX, info->attrs[IPVS_CMD_ATTR_DAEMON], - ip_vs_daemon_policy, NULL)) + ip_vs_daemon_policy, info->extack)) goto out; if (cmd == IPVS_CMD_NEW_DAEMON) diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 792def00a07d..e42f858b91d2 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -193,7 +193,7 @@ replay: __u8 subsys_id = NFNL_SUBSYS_ID(type); err = nla_parse(cda, ss->cb[cb_id].attr_count, attr, attrlen, - ss->cb[cb_id].policy, NULL); + ss->cb[cb_id].policy, extack); if (err < 0) { rcu_read_unlock(); return err; diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index ff6db5e66eb5..10f8b4cff40a 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -574,7 +574,7 @@ static int genl_family_rcv_msg(const struct genl_family *family, if (attrbuf) { err = nlmsg_parse(nlh, hdrlen, attrbuf, family->maxattr, - ops->policy, NULL); + ops->policy, extack); if (err < 0) goto out; } diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index aca903c12671..529443acd3bc 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -1162,7 +1162,7 @@ static int nfc_genl_llc_sdreq(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(attr, info->attrs[NFC_ATTR_LLC_SDP], rem) { rc = nla_parse_nested(sdp_attrs, NFC_SDP_ATTR_MAX, attr, - nfc_sdp_genl_policy, NULL); + nfc_sdp_genl_policy, info->extack); if (rc != 0) { rc = -EINVAL; diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 9b5c45f48f60..d174ee3254ee 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -802,7 +802,7 @@ int tipc_nl_bearer_get(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, info->attrs[TIPC_NLA_BEARER], - tipc_nl_bearer_policy, NULL); + tipc_nl_bearer_policy, info->extack); if (err) return err; @@ -851,7 +851,7 @@ int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, info->attrs[TIPC_NLA_BEARER], - tipc_nl_bearer_policy, NULL); + tipc_nl_bearer_policy, info->extack); if (err) return err; @@ -891,7 +891,7 @@ int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, info->attrs[TIPC_NLA_BEARER], - tipc_nl_bearer_policy, NULL); + tipc_nl_bearer_policy, info->extack); if (err) return err; @@ -939,7 +939,7 @@ int tipc_nl_bearer_add(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, info->attrs[TIPC_NLA_BEARER], - tipc_nl_bearer_policy, NULL); + tipc_nl_bearer_policy, info->extack); if (err) return err; @@ -982,7 +982,7 @@ int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX, info->attrs[TIPC_NLA_BEARER], - tipc_nl_bearer_policy, NULL); + tipc_nl_bearer_policy, info->extack); if (err) return err; @@ -1104,7 +1104,7 @@ int tipc_nl_media_get(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_MEDIA_MAX, info->attrs[TIPC_NLA_MEDIA], - tipc_nl_media_policy, NULL); + tipc_nl_media_policy, info->extack); if (err) return err; @@ -1152,7 +1152,7 @@ int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_MEDIA_MAX, info->attrs[TIPC_NLA_MEDIA], - tipc_nl_media_policy, NULL); + tipc_nl_media_policy, info->extack); if (!attrs[TIPC_NLA_MEDIA_NAME]) return -EINVAL; diff --git a/net/tipc/net.c b/net/tipc/net.c index 836da66cd852..719c5924b638 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -212,7 +212,7 @@ int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_NET_MAX, info->attrs[TIPC_NLA_NET], tipc_nl_net_policy, - NULL); + info->extack); if (err) return err; diff --git a/net/tipc/node.c b/net/tipc/node.c index 1dcde24c7053..01b1f077603e 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -1608,7 +1608,7 @@ int tipc_nl_peer_rm(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_NET_MAX, info->attrs[TIPC_NLA_NET], tipc_nl_net_policy, - NULL); + info->extack); if (err) return err; @@ -1774,7 +1774,7 @@ int tipc_nl_node_set_link(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX, info->attrs[TIPC_NLA_LINK], - tipc_nl_link_policy, NULL); + tipc_nl_link_policy, info->extack); if (err) return err; @@ -1902,7 +1902,7 @@ int tipc_nl_node_reset_link_stats(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX, info->attrs[TIPC_NLA_LINK], - tipc_nl_link_policy, NULL); + tipc_nl_link_policy, info->extack); if (err) return err; @@ -2042,7 +2042,7 @@ int tipc_nl_node_set_monitor(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(attrs, TIPC_NLA_MON_MAX, info->attrs[TIPC_NLA_MON], - tipc_nl_monitor_policy, NULL); + tipc_nl_monitor_policy, info->extack); if (err) return err; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e7594a3a0ab6..f280357552b2 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -810,7 +810,8 @@ static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k) int err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1, info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES], - nl80211_key_default_policy, NULL); + nl80211_key_default_policy, + info->extack); if (err) return err; @@ -2308,7 +2309,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) rem_txq_params) { result = nla_parse_nested(tb, NL80211_TXQ_ATTR_MAX, nl_txq_params, - txq_params_policy, NULL); + txq_params_policy, + info->extack); if (result) return result; result = parse_txq_params(tb, &txq_params); @@ -3561,7 +3563,7 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, if (sband == NULL) return -EINVAL; err = nla_parse_nested(tb, NL80211_TXRATE_MAX, tx_rates, - nl80211_txattr_policy, NULL); + nl80211_txattr_policy, info->extack); if (err) return err; if (tb[NL80211_TXRATE_LEGACY]) { @@ -4101,7 +4103,7 @@ static int parse_station_flags(struct genl_info *info, return 0; if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX, nla, - sta_flags_policy, NULL)) + sta_flags_policy, info->extack)) return -EINVAL; /* @@ -4728,7 +4730,7 @@ static int nl80211_parse_sta_wme(struct genl_info *info, nla = info->attrs[NL80211_ATTR_STA_WME]; err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla, - nl80211_sta_wme_policy, NULL); + nl80211_sta_wme_policy, info->extack); if (err) return err; @@ -5853,7 +5855,7 @@ do { \ return -EINVAL; if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX, info->attrs[NL80211_ATTR_MESH_CONFIG], - nl80211_meshconf_params_policy, NULL)) + nl80211_meshconf_params_policy, info->extack)) return -EINVAL; /* This makes sure that there aren't more than 32 mesh config @@ -6002,7 +6004,7 @@ static int nl80211_parse_mesh_setup(struct genl_info *info, return -EINVAL; if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX, info->attrs[NL80211_ATTR_MESH_SETUP], - nl80211_mesh_setup_params_policy, NULL)) + nl80211_mesh_setup_params_policy, info->extack)) return -EINVAL; if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC]) @@ -6393,7 +6395,8 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], rem_reg_rules) { r = nla_parse_nested(tb, NL80211_REG_RULE_ATTR_MAX, - nl_reg_rule, reg_rule_policy, NULL); + nl_reg_rule, reg_rule_policy, + info->extack); if (r) goto bad_reg; r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); @@ -7435,7 +7438,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(csa_attrs, NL80211_ATTR_MAX, info->attrs[NL80211_ATTR_CSA_IES], - nl80211_policy, NULL); + nl80211_policy, info->extack); if (err) return err; @@ -9533,7 +9536,7 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) return -EINVAL; err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm, - nl80211_attr_cqm_policy, NULL); + nl80211_attr_cqm_policy, info->extack); if (err) return err; @@ -10126,7 +10129,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(tb, MAX_NL80211_WOWLAN_TRIG, info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS], - nl80211_wowlan_policy, NULL); + nl80211_wowlan_policy, info->extack); if (err) return err; @@ -10209,7 +10212,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) u8 *mask_pat; nla_parse_nested(pat_tb, MAX_NL80211_PKTPAT, pat, - NULL, NULL); + NULL, info->extack); err = -EINVAL; if (!pat_tb[NL80211_PKTPAT_MASK] || !pat_tb[NL80211_PKTPAT_PATTERN]) @@ -10579,7 +10582,7 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) err = nla_parse_nested(tb, MAX_NL80211_REKEY_DATA, info->attrs[NL80211_ATTR_REKEY_DATA], - nl80211_rekey_policy, NULL); + nl80211_rekey_policy, info->extack); if (err) return err; @@ -10896,7 +10899,7 @@ static int nl80211_nan_add_func(struct sk_buff *skb, err = nla_parse_nested(tb, NL80211_NAN_FUNC_ATTR_MAX, info->attrs[NL80211_ATTR_NAN_FUNC], - nl80211_nan_func_policy, NULL); + nl80211_nan_func_policy, info->extack); if (err) return err; @@ -10993,7 +10996,7 @@ static int nl80211_nan_add_func(struct sk_buff *skb, err = nla_parse_nested(srf_tb, NL80211_NAN_SRF_ATTR_MAX, tb[NL80211_NAN_FUNC_SRF], - nl80211_nan_srf_policy, NULL); + nl80211_nan_srf_policy, info->extack); if (err) goto out; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 804e99a3227c..5f691fd53a6c 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2489,7 +2489,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, err = nlmsg_parse(nlh, xfrm_msg_min[type], attrs, link->nla_max ? : XFRMA_MAX, - link->nla_pol ? : xfrma_policy, NULL); + link->nla_pol ? : xfrma_policy, extack); if (err < 0) return err; -- cgit From 5389023421b8f719fe4f1e6abd3f0bd0e571866e Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Tue, 28 Mar 2017 00:28:50 +0900 Subject: netfilter: nat: remove rcu_read_lock in __nf_nat_decode_session. __nf_nat_decode_session is called from nf_nat_decode_session as decodefn. before calling decodefn, it already set rcu_read_lock. so rcu_read_lock in __nf_nat_decode_session can be removed. Signed-off-by: Taehee Yoo Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_nat_core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 376c1b36f222..fb0e65411785 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -71,11 +71,10 @@ static void __nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl) if (ct == NULL) return; - family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; - rcu_read_lock(); + family = nf_ct_l3num(ct); l3proto = __nf_nat_l3proto_find(family); if (l3proto == NULL) - goto out; + return; dir = CTINFO2DIR(ctinfo); if (dir == IP_CT_DIR_ORIGINAL) @@ -84,8 +83,6 @@ static void __nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl) statusbit = IPS_SRC_NAT; l3proto->decode_session(skb, ct, dir, statusbit, fl); -out: - rcu_read_unlock(); } int nf_xfrm_me_harder(struct net *net, struct sk_buff *skb, unsigned int family) -- cgit From 79250568276ff648f3fc0afc6cc47b677d108e16 Mon Sep 17 00:00:00 2001 From: Aaron Conole Date: Wed, 12 Apr 2017 16:12:28 -0400 Subject: netfilter: nf_tables: remove double return statement Signed-off-by: Aaron Conole Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 22e191ad4468..91e9191a43d8 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4433,8 +4433,6 @@ static int nf_tables_getobj(struct net *net, struct sock *nlsk, err: kfree_skb(skb2); return err; - - return 0; } static void nft_obj_destroy(struct nft_object *obj) -- cgit From 809c2d9a3b81f0ad9732d8ded0e91b3a97a81685 Mon Sep 17 00:00:00 2001 From: Aaron Conole Date: Wed, 12 Apr 2017 16:32:54 -0400 Subject: netfilter: nf_conntrack: remove double assignment The protonet pointer will unconditionally be rewritten, so just do the needed assignment first. Signed-off-by: Aaron Conole Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_proto.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 1329e090fd5e..2de6c1fe3261 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -441,9 +441,8 @@ EXPORT_SYMBOL_GPL(nf_ct_l4proto_unregister_one); void nf_ct_l4proto_pernet_unregister_one(struct net *net, struct nf_conntrack_l4proto *l4proto) { - struct nf_proto_net *pn = NULL; + struct nf_proto_net *pn = nf_ct_l4proto_net(net, l4proto); - pn = nf_ct_l4proto_net(net, l4proto); if (pn == NULL) return; -- cgit From c7ef8f0c020ac43c8a692bf989017c06ab1fdf0f Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Apr 2017 10:05:36 +0200 Subject: net: Add ESP offload features This patch adds netdev features to configure IPsec offloads. Signed-off-by: Steffen Klassert --- include/linux/netdev_features.h | 8 +++++++- include/linux/netdevice.h | 1 + include/linux/skbuff.h | 2 ++ net/core/ethtool.c | 3 +++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index 9a0419594e84..1d4737cffc71 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -54,8 +54,9 @@ enum { */ NETIF_F_GSO_TUNNEL_REMCSUM_BIT, /* ... TUNNEL with TSO & REMCSUM */ NETIF_F_GSO_SCTP_BIT, /* ... SCTP fragmentation */ + NETIF_F_GSO_ESP_BIT, /* ... ESP with TSO */ /**/NETIF_F_GSO_LAST = /* last bit, see GSO_MASK */ - NETIF_F_GSO_SCTP_BIT, + NETIF_F_GSO_ESP_BIT, NETIF_F_FCOE_CRC_BIT, /* FCoE CRC32 */ NETIF_F_SCTP_CRC_BIT, /* SCTP checksum offload */ @@ -73,6 +74,8 @@ enum { NETIF_F_HW_L2FW_DOFFLOAD_BIT, /* Allow L2 Forwarding in Hardware */ NETIF_F_HW_TC_BIT, /* Offload TC infrastructure */ + NETIF_F_HW_ESP_BIT, /* Hardware ESP transformation offload */ + NETIF_F_HW_ESP_TX_CSUM_BIT, /* ESP with TX checksum offload */ /* * Add your fresh new feature above and remember to update @@ -129,11 +132,14 @@ enum { #define NETIF_F_GSO_PARTIAL __NETIF_F(GSO_PARTIAL) #define NETIF_F_GSO_TUNNEL_REMCSUM __NETIF_F(GSO_TUNNEL_REMCSUM) #define NETIF_F_GSO_SCTP __NETIF_F(GSO_SCTP) +#define NETIF_F_GSO_ESP __NETIF_F(GSO_ESP) #define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER) #define NETIF_F_HW_VLAN_STAG_RX __NETIF_F(HW_VLAN_STAG_RX) #define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX) #define NETIF_F_HW_L2FW_DOFFLOAD __NETIF_F(HW_L2FW_DOFFLOAD) #define NETIF_F_HW_TC __NETIF_F(HW_TC) +#define NETIF_F_HW_ESP __NETIF_F(HW_ESP) +#define NETIF_F_HW_ESP_TX_CSUM __NETIF_F(HW_ESP_TX_CSUM) #define for_each_netdev_feature(mask_addr, bit) \ for_each_set_bit(bit, (unsigned long *)mask_addr, NETDEV_FEATURE_COUNT) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index cc07c3be2705..5bb03d181848 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -4070,6 +4070,7 @@ static inline bool net_gso_ok(netdev_features_t features, int gso_type) BUILD_BUG_ON(SKB_GSO_PARTIAL != (NETIF_F_GSO_PARTIAL >> NETIF_F_GSO_SHIFT)); BUILD_BUG_ON(SKB_GSO_TUNNEL_REMCSUM != (NETIF_F_GSO_TUNNEL_REMCSUM >> NETIF_F_GSO_SHIFT)); BUILD_BUG_ON(SKB_GSO_SCTP != (NETIF_F_GSO_SCTP >> NETIF_F_GSO_SHIFT)); + BUILD_BUG_ON(SKB_GSO_ESP != (NETIF_F_GSO_ESP >> NETIF_F_GSO_SHIFT)); return (features & feature) == feature; } diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 741d75cfc686..81ef53f06534 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -492,6 +492,8 @@ enum { SKB_GSO_TUNNEL_REMCSUM = 1 << 14, SKB_GSO_SCTP = 1 << 15, + + SKB_GSO_ESP = 1 << 16, }; #if BITS_PER_LONG > 32 diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 905a88ad28e0..03111a2d6653 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -90,6 +90,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_GSO_UDP_TUNNEL_CSUM_BIT] = "tx-udp_tnl-csum-segmentation", [NETIF_F_GSO_PARTIAL_BIT] = "tx-gso-partial", [NETIF_F_GSO_SCTP_BIT] = "tx-sctp-segmentation", + [NETIF_F_GSO_ESP_BIT] = "tx-esp-segmentation", [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc", [NETIF_F_SCTP_CRC_BIT] = "tx-checksum-sctp", @@ -103,6 +104,8 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_RXALL_BIT] = "rx-all", [NETIF_F_HW_L2FW_DOFFLOAD_BIT] = "l2-fwd-offload", [NETIF_F_HW_TC_BIT] = "hw-tc-offload", + [NETIF_F_HW_ESP_BIT] = "esp-hw-offload", + [NETIF_F_HW_ESP_TX_CSUM_BIT] = "esp-tx-csum-hw-offload", }; static const char -- cgit From 9d389d7f84bbb3a294eb05f7dfe2076e291fc150 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Apr 2017 10:05:44 +0200 Subject: xfrm: Add a xfrm type offload. We add a struct xfrm_type_offload so that we have the offloaded codepath separated to the non offloaded codepath. With this the non offloade and the offloaded codepath can coexist. Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 28 +++++++++++++++----- net/xfrm/xfrm_state.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 6 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 9e3dc7b81a4d..159342f3e72b 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -222,6 +222,8 @@ struct xfrm_state { struct xfrm_mode *inner_mode_iaf; struct xfrm_mode *outer_mode; + const struct xfrm_type_offload *type_offload; + /* Security context */ struct xfrm_sec_ctx *security; @@ -314,12 +316,14 @@ void km_state_expired(struct xfrm_state *x, int hard, u32 portid); int __xfrm_state_delete(struct xfrm_state *x); struct xfrm_state_afinfo { - unsigned int family; - unsigned int proto; - __be16 eth_proto; - struct module *owner; - const struct xfrm_type *type_map[IPPROTO_MAX]; - struct xfrm_mode *mode_map[XFRM_MODE_MAX]; + unsigned int family; + unsigned int proto; + __be16 eth_proto; + struct module *owner; + const struct xfrm_type *type_map[IPPROTO_MAX]; + const struct xfrm_type_offload *type_offload_map[IPPROTO_MAX]; + struct xfrm_mode *mode_map[XFRM_MODE_MAX]; + int (*init_flags)(struct xfrm_state *x); void (*init_tempsel)(struct xfrm_selector *sel, const struct flowi *fl); @@ -380,6 +384,18 @@ struct xfrm_type { int xfrm_register_type(const struct xfrm_type *type, unsigned short family); int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family); +struct xfrm_type_offload { + char *description; + struct module *owner; + u8 proto; + void (*encap)(struct xfrm_state *, struct sk_buff *pskb); + int (*input_tail)(struct xfrm_state *x, struct sk_buff *skb); + int (*xmit)(struct xfrm_state *, struct sk_buff *pskb, netdev_features_t features); +}; + +int xfrm_register_type_offload(const struct xfrm_type_offload *type, unsigned short family); +int xfrm_unregister_type_offload(const struct xfrm_type_offload *type, unsigned short family); + struct xfrm_mode { /* * Remove encapsulation header. diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 5a597dbbe564..47fefe97d1e3 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -251,6 +251,75 @@ static void xfrm_put_type(const struct xfrm_type *type) module_put(type->owner); } +static DEFINE_SPINLOCK(xfrm_type_offload_lock); +int xfrm_register_type_offload(const struct xfrm_type_offload *type, + unsigned short family) +{ + struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); + const struct xfrm_type_offload **typemap; + int err = 0; + + if (unlikely(afinfo == NULL)) + return -EAFNOSUPPORT; + typemap = afinfo->type_offload_map; + spin_lock_bh(&xfrm_type_offload_lock); + + if (likely(typemap[type->proto] == NULL)) + typemap[type->proto] = type; + else + err = -EEXIST; + spin_unlock_bh(&xfrm_type_offload_lock); + rcu_read_unlock(); + return err; +} +EXPORT_SYMBOL(xfrm_register_type_offload); + +int xfrm_unregister_type_offload(const struct xfrm_type_offload *type, + unsigned short family) +{ + struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family); + const struct xfrm_type_offload **typemap; + int err = 0; + + if (unlikely(afinfo == NULL)) + return -EAFNOSUPPORT; + typemap = afinfo->type_offload_map; + spin_lock_bh(&xfrm_type_offload_lock); + + if (unlikely(typemap[type->proto] != type)) + err = -ENOENT; + else + typemap[type->proto] = NULL; + spin_unlock_bh(&xfrm_type_offload_lock); + rcu_read_unlock(); + return err; +} +EXPORT_SYMBOL(xfrm_unregister_type_offload); + +static const struct xfrm_type_offload *xfrm_get_type_offload(u8 proto, unsigned short family) +{ + struct xfrm_state_afinfo *afinfo; + const struct xfrm_type_offload **typemap; + const struct xfrm_type_offload *type; + + afinfo = xfrm_state_get_afinfo(family); + if (unlikely(afinfo == NULL)) + return NULL; + typemap = afinfo->type_offload_map; + + type = typemap[proto]; + if ((type && !try_module_get(type->owner))) + type = NULL; + + rcu_read_unlock(); + return type; +} + +static void xfrm_put_type_offload(const struct xfrm_type_offload *type) +{ + module_put(type->owner); +} + static DEFINE_SPINLOCK(xfrm_mode_lock); int xfrm_register_mode(struct xfrm_mode *mode, int family) { @@ -365,6 +434,8 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x) xfrm_put_mode(x->inner_mode_iaf); if (x->outer_mode) xfrm_put_mode(x->outer_mode); + if (x->type_offload) + xfrm_put_type_offload(x->type_offload); if (x->type) { x->type->destructor(x); xfrm_put_type(x->type); @@ -2077,6 +2148,8 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay) if (x->type == NULL) goto error; + x->type_offload = xfrm_get_type_offload(x->id.proto, family); + err = x->type->init_state(x); if (err) goto error; -- cgit From 21f42cc95f07c1d7827b339c04442e147411e44b Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Apr 2017 10:05:53 +0200 Subject: xfrm: Move device notifications to a sepatate file This is needed for the upcomming IPsec device offloading. Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 1 + net/xfrm/Makefile | 2 +- net/xfrm/xfrm_device.c | 43 +++++++++++++++++++++++++++++++++++++++++++ net/xfrm/xfrm_policy.c | 17 +---------------- 4 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 net/xfrm/xfrm_device.c diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 159342f3e72b..ac984da27879 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1443,6 +1443,7 @@ struct xfrm6_tunnel { void xfrm_init(void); void xfrm4_init(void); int xfrm_state_init(struct net *net); +void xfrm_dev_init(void); void xfrm_state_fini(struct net *net); void xfrm4_state_init(void); void xfrm4_protocol_init(void); diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile index c0e961983f17..55b2ac300995 100644 --- a/net/xfrm/Makefile +++ b/net/xfrm/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \ xfrm_input.o xfrm_output.o \ - xfrm_sysctl.o xfrm_replay.o + xfrm_sysctl.o xfrm_replay.o xfrm_device.o obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o obj-$(CONFIG_XFRM_ALGO) += xfrm_algo.o obj-$(CONFIG_XFRM_USER) += xfrm_user.o diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c new file mode 100644 index 000000000000..34a260a61be9 --- /dev/null +++ b/net/xfrm/xfrm_device.c @@ -0,0 +1,43 @@ +/* + * xfrm_device.c - IPsec device offloading code. + * + * Copyright (c) 2015 secunet Security Networks AG + * + * Author: + * Steffen Klassert + * + * 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 + +static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + + switch (event) { + case NETDEV_DOWN: + xfrm_garbage_collect(dev_net(dev)); + } + return NOTIFY_DONE; +} + +static struct notifier_block xfrm_dev_notifier = { + .notifier_call = xfrm_dev_event, +}; + +void __net_init xfrm_dev_init(void) +{ + register_netdevice_notifier(&xfrm_dev_notifier); +} diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 236cbbc0ab9c..7befca2a0773 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2929,21 +2929,6 @@ void xfrm_policy_unregister_afinfo(const struct xfrm_policy_afinfo *afinfo) } EXPORT_SYMBOL(xfrm_policy_unregister_afinfo); -static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - switch (event) { - case NETDEV_DOWN: - xfrm_garbage_collect(dev_net(dev)); - } - return NOTIFY_DONE; -} - -static struct notifier_block xfrm_dev_notifier = { - .notifier_call = xfrm_dev_event, -}; - #ifdef CONFIG_XFRM_STATISTICS static int __net_init xfrm_statistics_init(struct net *net) { @@ -3020,7 +3005,7 @@ static int __net_init xfrm_policy_init(struct net *net) INIT_WORK(&net->xfrm.policy_hash_work, xfrm_hash_resize); INIT_WORK(&net->xfrm.policy_hthresh.work, xfrm_hash_rebuild); if (net_eq(net, &init_net)) - register_netdevice_notifier(&xfrm_dev_notifier); + xfrm_dev_init(); return 0; out_bydst: -- cgit From c35fe4106b928d0a5909cfdac53c1db559b24299 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Apr 2017 10:06:01 +0200 Subject: xfrm: Add mode handlers for IPsec on layer 2 This patch adds a gso_segment and xmit callback for the xfrm_mode and implement these functions for tunnel and transport mode. Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 10 ++++++++++ net/ipv4/xfrm4_mode_transport.c | 32 ++++++++++++++++++++++++++++++++ net/ipv4/xfrm4_mode_tunnel.c | 25 +++++++++++++++++++++++++ net/ipv6/xfrm6_mode_transport.c | 33 +++++++++++++++++++++++++++++++++ net/ipv6/xfrm6_mode_tunnel.c | 24 ++++++++++++++++++++++++ 5 files changed, 124 insertions(+) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index ac984da27879..54515d989365 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -444,6 +444,16 @@ struct xfrm_mode { */ int (*output)(struct xfrm_state *x, struct sk_buff *skb); + /* + * Adjust pointers into the packet and do GSO segmentation. + */ + struct sk_buff *(*gso_segment)(struct xfrm_state *x, struct sk_buff *skb, netdev_features_t features); + + /* + * Adjust pointers into the packet when IPsec is done at layer2. + */ + void (*xmit)(struct xfrm_state *x, struct sk_buff *skb); + struct xfrm_state_afinfo *afinfo; struct module *owner; unsigned int encap; diff --git a/net/ipv4/xfrm4_mode_transport.c b/net/ipv4/xfrm4_mode_transport.c index 4acc0508c5eb..6c2411d09386 100644 --- a/net/ipv4/xfrm4_mode_transport.c +++ b/net/ipv4/xfrm4_mode_transport.c @@ -12,6 +12,7 @@ #include #include #include +#include /* Add encapsulation header. * @@ -56,9 +57,40 @@ static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb) return 0; } +static struct sk_buff *xfrm4_transport_gso_segment(struct xfrm_state *x, + struct sk_buff *skb, + netdev_features_t features) +{ + const struct net_offload *ops; + struct sk_buff *segs = ERR_PTR(-EINVAL); + struct xfrm_offload *xo = xfrm_offload(skb); + + skb->transport_header += x->props.header_len; + ops = rcu_dereference(inet_offloads[xo->proto]); + if (likely(ops && ops->callbacks.gso_segment)) + segs = ops->callbacks.gso_segment(skb, features); + + return segs; +} + +static void xfrm4_transport_xmit(struct xfrm_state *x, struct sk_buff *skb) +{ + struct xfrm_offload *xo = xfrm_offload(skb); + + skb_reset_mac_len(skb); + pskb_pull(skb, skb->mac_len + sizeof(struct iphdr) + x->props.header_len); + + if (xo->flags & XFRM_GSO_SEGMENT) { + skb_reset_transport_header(skb); + skb->transport_header -= x->props.header_len; + } +} + static struct xfrm_mode xfrm4_transport_mode = { .input = xfrm4_transport_input, .output = xfrm4_transport_output, + .gso_segment = xfrm4_transport_gso_segment, + .xmit = xfrm4_transport_xmit, .owner = THIS_MODULE, .encap = XFRM_MODE_TRANSPORT, }; diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c index 35feda676464..d3f2434fa0b8 100644 --- a/net/ipv4/xfrm4_mode_tunnel.c +++ b/net/ipv4/xfrm4_mode_tunnel.c @@ -96,11 +96,36 @@ out: return err; } +static struct sk_buff *xfrm4_mode_tunnel_gso_segment(struct xfrm_state *x, + struct sk_buff *skb, + netdev_features_t features) +{ + __skb_push(skb, skb->mac_len); + return skb_mac_gso_segment(skb, features); + +} + +static void xfrm4_mode_tunnel_xmit(struct xfrm_state *x, struct sk_buff *skb) +{ + struct xfrm_offload *xo = xfrm_offload(skb); + + if (xo->flags & XFRM_GSO_SEGMENT) { + skb->network_header = skb->network_header - x->props.header_len; + skb->transport_header = skb->network_header + + sizeof(struct iphdr); + } + + skb_reset_mac_len(skb); + pskb_pull(skb, skb->mac_len + x->props.header_len); +} + static struct xfrm_mode xfrm4_tunnel_mode = { .input2 = xfrm4_mode_tunnel_input, .input = xfrm_prepare_input, .output2 = xfrm4_mode_tunnel_output, .output = xfrm4_prepare_output, + .gso_segment = xfrm4_mode_tunnel_gso_segment, + .xmit = xfrm4_mode_tunnel_xmit, .owner = THIS_MODULE, .encap = XFRM_MODE_TUNNEL, .flags = XFRM_MODE_FLAG_TUNNEL, diff --git a/net/ipv6/xfrm6_mode_transport.c b/net/ipv6/xfrm6_mode_transport.c index 4439ee44c8b0..eb9b36b06c1d 100644 --- a/net/ipv6/xfrm6_mode_transport.c +++ b/net/ipv6/xfrm6_mode_transport.c @@ -13,6 +13,7 @@ #include #include #include +#include /* Add encapsulation header. * @@ -61,9 +62,41 @@ static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb) return 0; } +static struct sk_buff *xfrm4_transport_gso_segment(struct xfrm_state *x, + struct sk_buff *skb, + netdev_features_t features) +{ + const struct net_offload *ops; + struct sk_buff *segs = ERR_PTR(-EINVAL); + struct xfrm_offload *xo = xfrm_offload(skb); + + skb->transport_header += x->props.header_len; + ops = rcu_dereference(inet6_offloads[xo->proto]); + if (likely(ops && ops->callbacks.gso_segment)) + segs = ops->callbacks.gso_segment(skb, features); + + return segs; +} + +static void xfrm6_transport_xmit(struct xfrm_state *x, struct sk_buff *skb) +{ + struct xfrm_offload *xo = xfrm_offload(skb); + + skb_reset_mac_len(skb); + pskb_pull(skb, skb->mac_len + sizeof(struct ipv6hdr) + x->props.header_len); + + if (xo->flags & XFRM_GSO_SEGMENT) { + skb_reset_transport_header(skb); + skb->transport_header -= x->props.header_len; + } +} + + static struct xfrm_mode xfrm6_transport_mode = { .input = xfrm6_transport_input, .output = xfrm6_transport_output, + .gso_segment = xfrm4_transport_gso_segment, + .xmit = xfrm6_transport_xmit, .owner = THIS_MODULE, .encap = XFRM_MODE_TRANSPORT, }; diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c index 372855eeaf42..19a60fc4c29b 100644 --- a/net/ipv6/xfrm6_mode_tunnel.c +++ b/net/ipv6/xfrm6_mode_tunnel.c @@ -96,11 +96,35 @@ out: return err; } +static struct sk_buff *xfrm6_mode_tunnel_gso_segment(struct xfrm_state *x, + struct sk_buff *skb, + netdev_features_t features) +{ + __skb_push(skb, skb->mac_len); + return skb_mac_gso_segment(skb, features); + +} + +static void xfrm6_mode_tunnel_xmit(struct xfrm_state *x, struct sk_buff *skb) +{ + struct xfrm_offload *xo = xfrm_offload(skb); + + if (xo->flags & XFRM_GSO_SEGMENT) { + skb->network_header = skb->network_header - x->props.header_len; + skb->transport_header = skb->network_header + sizeof(struct ipv6hdr); + } + + skb_reset_mac_len(skb); + pskb_pull(skb, skb->mac_len + x->props.header_len); +} + static struct xfrm_mode xfrm6_tunnel_mode = { .input2 = xfrm6_mode_tunnel_input, .input = xfrm_prepare_input, .output2 = xfrm6_mode_tunnel_output, .output = xfrm6_prepare_output, + .gso_segment = xfrm6_mode_tunnel_gso_segment, + .xmit = xfrm6_mode_tunnel_xmit, .owner = THIS_MODULE, .encap = XFRM_MODE_TUNNEL, .flags = XFRM_MODE_FLAG_TUNNEL, -- cgit From d77e38e612a017480157fe6d2c1422f42cb5b7e3 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Apr 2017 10:06:10 +0200 Subject: xfrm: Add an IPsec hardware offloading API This patch adds all the bits that are needed to do IPsec hardware offload for IPsec states and ESP packets. We add xfrmdev_ops to the net_device. xfrmdev_ops has function pointers that are needed to manage the xfrm states in the hardware and to do a per packet offloading decision. Joint work with: Ilan Tayari Guy Shapiro Yossi Kuperman Signed-off-by: Guy Shapiro Signed-off-by: Ilan Tayari Signed-off-by: Yossi Kuperman Signed-off-by: Steffen Klassert --- include/linux/netdevice.h | 14 +++++ include/net/xfrm.h | 65 +++++++++++++++++++++- include/uapi/linux/xfrm.h | 8 +++ net/ipv4/esp4.c | 7 +-- net/ipv4/xfrm4_output.c | 3 +- net/ipv6/esp6.c | 4 +- net/ipv6/xfrm6_output.c | 9 ++- net/xfrm/Makefile | 3 +- net/xfrm/xfrm_device.c | 138 +++++++++++++++++++++++++++++++++++++++++++++- net/xfrm/xfrm_input.c | 41 +++++++++++++- net/xfrm/xfrm_output.c | 44 +++++++++++++-- net/xfrm/xfrm_policy.c | 10 ++-- net/xfrm/xfrm_state.c | 74 +++++++++++++++++++++++++ net/xfrm/xfrm_user.c | 28 ++++++++++ 14 files changed, 424 insertions(+), 24 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5bb03d181848..b3eb83db0223 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -824,6 +824,16 @@ struct netdev_xdp { }; }; +#ifdef CONFIG_XFRM_OFFLOAD +struct xfrmdev_ops { + int (*xdo_dev_state_add) (struct xfrm_state *x); + void (*xdo_dev_state_delete) (struct xfrm_state *x); + void (*xdo_dev_state_free) (struct xfrm_state *x); + bool (*xdo_dev_offload_ok) (struct sk_buff *skb, + struct xfrm_state *x); +}; +#endif + /* * This structure defines the management hooks for network devices. * The following hooks can be defined; unless noted otherwise, they are @@ -1697,6 +1707,10 @@ struct net_device { const struct ndisc_ops *ndisc_ops; #endif +#ifdef CONFIG_XFRM + const struct xfrmdev_ops *xfrmdev_ops; +#endif + const struct header_ops *header_ops; unsigned int flags; diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 54515d989365..17603bf190c1 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -120,6 +120,13 @@ struct xfrm_state_walk { struct xfrm_address_filter *filter; }; +struct xfrm_state_offload { + struct net_device *dev; + unsigned long offload_handle; + unsigned int num_exthdrs; + u8 flags; +}; + /* Full description of state of transformer. */ struct xfrm_state { possible_net_t xs_net; @@ -207,6 +214,8 @@ struct xfrm_state { struct xfrm_lifetime_cur curlft; struct tasklet_hrtimer mtimer; + struct xfrm_state_offload xso; + /* used to fix curlft->add_time when changing date */ long saved_tmo; @@ -1453,7 +1462,6 @@ struct xfrm6_tunnel { void xfrm_init(void); void xfrm4_init(void); int xfrm_state_init(struct net *net); -void xfrm_dev_init(void); void xfrm_state_fini(struct net *net); void xfrm4_state_init(void); void xfrm4_protocol_init(void); @@ -1559,6 +1567,7 @@ struct xfrmk_spdinfo { struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq); int xfrm_state_delete(struct xfrm_state *x); int xfrm_state_flush(struct net *net, u8 proto, bool task_valid); +int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid); void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); @@ -1641,6 +1650,11 @@ static inline int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) } #endif +struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif, + const xfrm_address_t *saddr, + const xfrm_address_t *daddr, + int family); + struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp); void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type); @@ -1846,6 +1860,55 @@ static inline struct xfrm_offload *xfrm_offload(struct sk_buff *skb) } #endif +#ifdef CONFIG_XFRM_OFFLOAD +void __net_init xfrm_dev_init(void); +int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, + struct xfrm_user_offload *xuo); +bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x); + +static inline void xfrm_dev_state_delete(struct xfrm_state *x) +{ + struct xfrm_state_offload *xso = &x->xso; + + if (xso->dev) + xso->dev->xfrmdev_ops->xdo_dev_state_delete(x); +} + +static inline void xfrm_dev_state_free(struct xfrm_state *x) +{ + struct xfrm_state_offload *xso = &x->xso; + struct net_device *dev = xso->dev; + + if (dev && dev->xfrmdev_ops) { + dev->xfrmdev_ops->xdo_dev_state_free(x); + xso->dev = NULL; + dev_put(dev); + } +} +#else +static inline void __net_init xfrm_dev_init(void) +{ +} + +static inline int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo) +{ + return 0; +} + +static inline void xfrm_dev_state_delete(struct xfrm_state *x) +{ +} + +static inline void xfrm_dev_state_free(struct xfrm_state *x) +{ +} + +static inline bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x) +{ + return false; +} +#endif + static inline int xfrm_mark_get(struct nlattr **attrs, struct xfrm_mark *m) { if (attrs[XFRMA_MARK]) diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h index 1fc62b239f1b..2b384ff09fa0 100644 --- a/include/uapi/linux/xfrm.h +++ b/include/uapi/linux/xfrm.h @@ -303,6 +303,7 @@ enum xfrm_attr_type_t { XFRMA_PROTO, /* __u8 */ XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */ XFRMA_PAD, + XFRMA_OFFLOAD_DEV, /* struct xfrm_state_offload */ __XFRMA_MAX #define XFRMA_MAX (__XFRMA_MAX - 1) @@ -494,6 +495,13 @@ struct xfrm_address_filter { __u8 dplen; }; +struct xfrm_user_offload { + int ifindex; + __u8 flags; +}; +#define XFRM_OFFLOAD_IPV6 1 +#define XFRM_OFFLOAD_INBOUND 2 + #ifndef __KERNEL__ /* backwards compatibility for userspace */ #define XFRMGRP_ACQUIRE 1 diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index b1e24446e297..c6aba234b6e9 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -435,9 +435,6 @@ skip_cow2: aead_request_set_crypt(req, sg, dsg, ivlen + clen, iv); aead_request_set_ad(req, assoclen); - seqno = cpu_to_be64(XFRM_SKB_CB(skb)->seq.output.low + - ((u64)XFRM_SKB_CB(skb)->seq.output.hi << 32)); - memset(iv, 0, ivlen); memcpy(iv + ivlen - min(ivlen, 8), (u8 *)&seqno + 8 - min(ivlen, 8), min(ivlen, 8)); @@ -470,6 +467,7 @@ static int esp_input_done2(struct sk_buff *skb, int err) { const struct iphdr *iph; struct xfrm_state *x = xfrm_input_state(skb); + struct xfrm_offload *xo = xfrm_offload(skb); struct crypto_aead *aead = x->data; int alen = crypto_aead_authsize(aead); int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); @@ -478,7 +476,8 @@ static int esp_input_done2(struct sk_buff *skb, int err) u8 nexthdr[2]; int padlen; - kfree(ESP_SKB_CB(skb)->tmp); + if (!xo || (xo && !(xo->flags & CRYPTO_DONE))) + kfree(ESP_SKB_CB(skb)->tmp); if (unlikely(err)) goto out; diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 7ee6518afa86..94b8702603bc 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c @@ -29,7 +29,8 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb) goto out; mtu = dst_mtu(skb_dst(skb)); - if (skb->len > mtu) { + if ((!skb_is_gso(skb) && skb->len > mtu) || + (skb_is_gso(skb) && skb_gso_network_seglen(skb) > ip_skb_dst_mtu(skb->sk, skb))) { skb->protocol = htons(ETH_P_IP); if (skb->sk) diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index ff54faa75631..3d3757d20d0a 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -450,6 +450,7 @@ error: static int esp_input_done2(struct sk_buff *skb, int err) { struct xfrm_state *x = xfrm_input_state(skb); + struct xfrm_offload *xo = xfrm_offload(skb); struct crypto_aead *aead = x->data; int alen = crypto_aead_authsize(aead); int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); @@ -458,7 +459,8 @@ static int esp_input_done2(struct sk_buff *skb, int err) int padlen; u8 nexthdr[2]; - kfree(ESP_SKB_CB(skb)->tmp); + if (!xo || (xo && !(xo->flags & CRYPTO_DONE))) + kfree(ESP_SKB_CB(skb)->tmp); if (unlikely(err)) goto out; diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 4d09ce6fa90e..8ae87d4ec5ff 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -73,11 +73,16 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb) int mtu, ret = 0; struct dst_entry *dst = skb_dst(skb); + if (skb->ignore_df) + goto out; + mtu = dst_mtu(dst); if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU; - if (!skb->ignore_df && skb->len > mtu) { + if ((!skb_is_gso(skb) && skb->len > mtu) || + (skb_is_gso(skb) && + skb_gso_network_seglen(skb) > ip6_skb_dst_mtu(skb))) { skb->dev = dst->dev; skb->protocol = htons(ETH_P_IPV6); @@ -89,7 +94,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb) icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); ret = -EMSGSIZE; } - +out: return ret; } diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile index 55b2ac300995..abf81b329dc1 100644 --- a/net/xfrm/Makefile +++ b/net/xfrm/Makefile @@ -4,7 +4,8 @@ obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \ xfrm_input.o xfrm_output.o \ - xfrm_sysctl.o xfrm_replay.o xfrm_device.o + xfrm_sysctl.o xfrm_replay.o +obj-$(CONFIG_XFRM_OFFLOAD) += xfrm_device.o obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o obj-$(CONFIG_XFRM_ALGO) += xfrm_algo.o obj-$(CONFIG_XFRM_USER) += xfrm_user.o diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 34a260a61be9..9bac2ba9052c 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -22,13 +22,149 @@ #include #include +int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, + struct xfrm_user_offload *xuo) +{ + int err; + struct dst_entry *dst; + struct net_device *dev; + struct xfrm_state_offload *xso = &x->xso; + xfrm_address_t *saddr; + xfrm_address_t *daddr; + + if (!x->type_offload) + return 0; + + /* We don't yet support UDP encapsulation, TFC padding and ESN. */ + if (x->encap || x->tfcpad || (x->props.flags & XFRM_STATE_ESN)) + return 0; + + dev = dev_get_by_index(net, xuo->ifindex); + if (!dev) { + if (!(xuo->flags & XFRM_OFFLOAD_INBOUND)) { + saddr = &x->props.saddr; + daddr = &x->id.daddr; + } else { + saddr = &x->id.daddr; + daddr = &x->props.saddr; + } + + dst = __xfrm_dst_lookup(net, 0, 0, saddr, daddr, x->props.family); + if (IS_ERR(dst)) + return 0; + + dev = dst->dev; + + dev_hold(dev); + dst_release(dst); + } + + if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_state_add) { + dev_put(dev); + return 0; + } + + xso->dev = dev; + xso->num_exthdrs = 1; + xso->flags = xuo->flags; + + err = dev->xfrmdev_ops->xdo_dev_state_add(x); + if (err) { + dev_put(dev); + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(xfrm_dev_state_add); + +bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x) +{ + int mtu; + struct dst_entry *dst = skb_dst(skb); + struct xfrm_dst *xdst = (struct xfrm_dst *)dst; + struct net_device *dev = x->xso.dev; + + if (!x->type_offload || x->encap) + return false; + + if ((x->xso.offload_handle && (dev == dst->path->dev)) && + !dst->child->xfrm && x->type->get_mtu) { + mtu = x->type->get_mtu(x, xdst->child_mtu_cached); + + if (skb->len <= mtu) + goto ok; + + if (skb_is_gso(skb) && skb_gso_validate_mtu(skb, mtu)) + goto ok; + } + + return false; + +ok: + if (dev && dev->xfrmdev_ops && dev->xfrmdev_ops->xdo_dev_offload_ok) + return x->xso.dev->xfrmdev_ops->xdo_dev_offload_ok(skb, x); + + return true; +} +EXPORT_SYMBOL_GPL(xfrm_dev_offload_ok); + +int xfrm_dev_register(struct net_device *dev) +{ + if ((dev->features & NETIF_F_HW_ESP) && !dev->xfrmdev_ops) + return NOTIFY_BAD; + if ((dev->features & NETIF_F_HW_ESP_TX_CSUM) && + !(dev->features & NETIF_F_HW_ESP)) + return NOTIFY_BAD; + + return NOTIFY_DONE; +} + +static int xfrm_dev_unregister(struct net_device *dev) +{ + return NOTIFY_DONE; +} + +static int xfrm_dev_feat_change(struct net_device *dev) +{ + if ((dev->features & NETIF_F_HW_ESP) && !dev->xfrmdev_ops) + return NOTIFY_BAD; + else if (!(dev->features & NETIF_F_HW_ESP)) + dev->xfrmdev_ops = NULL; + + if ((dev->features & NETIF_F_HW_ESP_TX_CSUM) && + !(dev->features & NETIF_F_HW_ESP)) + return NOTIFY_BAD; + + return NOTIFY_DONE; +} + +static int xfrm_dev_down(struct net_device *dev) +{ + if (dev->hw_features & NETIF_F_HW_ESP) + xfrm_dev_state_flush(dev_net(dev), dev, true); + + xfrm_garbage_collect(dev_net(dev)); + + return NOTIFY_DONE; +} + static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *dev = netdev_notifier_info_to_dev(ptr); switch (event) { + case NETDEV_REGISTER: + return xfrm_dev_register(dev); + + case NETDEV_UNREGISTER: + return xfrm_dev_unregister(dev); + + case NETDEV_FEAT_CHANGE: + return xfrm_dev_feat_change(dev); + case NETDEV_DOWN: - xfrm_garbage_collect(dev_net(dev)); + return xfrm_dev_down(dev); } return NOTIFY_DONE; } diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 46bdb4fbed0b..362d655eac27 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -107,6 +107,8 @@ struct sec_path *secpath_dup(struct sec_path *src) sp->len = 0; sp->olen = 0; + memset(sp->ovec, 0, sizeof(sp->ovec[XFRM_MAX_OFFLOAD_DEPTH])); + if (src) { int i; @@ -207,8 +209,9 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) unsigned int family; int decaps = 0; int async = 0; - struct xfrm_offload *xo; bool xfrm_gro = false; + bool crypto_done = false; + struct xfrm_offload *xo = xfrm_offload(skb); if (encap_type < 0) { x = xfrm_input_state(skb); @@ -226,6 +229,37 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) goto lock; } + if (xo && (xo->flags & CRYPTO_DONE)) { + crypto_done = true; + x = xfrm_input_state(skb); + family = XFRM_SPI_SKB_CB(skb)->family; + + if (!(xo->status & CRYPTO_SUCCESS)) { + if (xo->status & + (CRYPTO_TRANSPORT_AH_AUTH_FAILED | + CRYPTO_TRANSPORT_ESP_AUTH_FAILED | + CRYPTO_TUNNEL_AH_AUTH_FAILED | + CRYPTO_TUNNEL_ESP_AUTH_FAILED)) { + + xfrm_audit_state_icvfail(x, skb, + x->type->proto); + x->stats.integrity_failed++; + XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR); + goto drop; + } + + XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR); + goto drop; + } + + if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) { + XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); + goto drop; + } + + goto lock; + } + daddr = (xfrm_address_t *)(skb_network_header(skb) + XFRM_SPI_SKB_CB(skb)->daddroff); family = XFRM_SPI_SKB_CB(skb)->family; @@ -311,7 +345,10 @@ lock: skb_dst_force(skb); dev_hold(skb->dev); - nexthdr = x->type->input(x, skb); + if (crypto_done) + nexthdr = x->type_offload->input_tail(x, skb); + else + nexthdr = x->type->input(x, skb); if (nexthdr == -EINPROGRESS) return 0; diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 8ba29fe58352..a15088613a6c 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -99,12 +99,13 @@ static int xfrm_output_one(struct sk_buff *skb, int err) skb_dst_force(skb); - /* Inner headers are invalid now. */ - skb->encapsulation = 0; - - err = x->type->output(x, skb); - if (err == -EINPROGRESS) - goto out; + if (xfrm_offload(skb)) { + x->type_offload->encap(x, skb); + } else { + err = x->type->output(x, skb); + if (err == -EINPROGRESS) + goto out; + } resume: if (err) { @@ -200,8 +201,38 @@ static int xfrm_output_gso(struct net *net, struct sock *sk, struct sk_buff *skb int xfrm_output(struct sock *sk, struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); + struct xfrm_state *x = skb_dst(skb)->xfrm; int err; + secpath_reset(skb); + + if (xfrm_dev_offload_ok(skb, x)) { + struct sec_path *sp; + + sp = secpath_dup(skb->sp); + if (!sp) { + XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR); + kfree_skb(skb); + return -ENOMEM; + } + if (skb->sp) + secpath_put(skb->sp); + skb->sp = sp; + + sp->olen++; + sp->xvec[skb->sp->len++] = x; + xfrm_state_hold(x); + + if (skb_is_gso(skb)) { + skb_shinfo(skb)->gso_type |= SKB_GSO_ESP; + + return xfrm_output2(net, sk, skb); + } + + if (x->xso.dev && x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM) + goto out; + } + if (skb_is_gso(skb)) return xfrm_output_gso(net, sk, skb); @@ -214,6 +245,7 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb) } } +out: return xfrm_output2(net, sk, skb); } EXPORT_SYMBOL_GPL(xfrm_output); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 7befca2a0773..dd44ddc1aea5 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -116,11 +116,10 @@ static const struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short fa return afinfo; } -static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, - int tos, int oif, - const xfrm_address_t *saddr, - const xfrm_address_t *daddr, - int family) +struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif, + const xfrm_address_t *saddr, + const xfrm_address_t *daddr, + int family) { const struct xfrm_policy_afinfo *afinfo; struct dst_entry *dst; @@ -135,6 +134,7 @@ static inline struct dst_entry *__xfrm_dst_lookup(struct net *net, return dst; } +EXPORT_SYMBOL(__xfrm_dst_lookup); static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, int oif, diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 47fefe97d1e3..fc3c5aa38754 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -440,6 +440,7 @@ static void xfrm_state_gc_destroy(struct xfrm_state *x) x->type->destructor(x); xfrm_put_type(x->type); } + xfrm_dev_state_free(x); security_xfrm_state_free(x); kfree(x); } @@ -609,6 +610,8 @@ int __xfrm_state_delete(struct xfrm_state *x) net->xfrm.state_num--; spin_unlock(&net->xfrm.xfrm_state_lock); + xfrm_dev_state_delete(x); + /* All xfrm_state objects are created by xfrm_state_alloc. * The xfrm_state_alloc call gives a reference, and that * is what we are dropping here. @@ -653,12 +656,41 @@ xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid) return err; } + +static inline int +xfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool task_valid) +{ + int i, err = 0; + + for (i = 0; i <= net->xfrm.state_hmask; i++) { + struct xfrm_state *x; + struct xfrm_state_offload *xso; + + hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) { + xso = &x->xso; + + if (xso->dev == dev && + (err = security_xfrm_state_delete(x)) != 0) { + xfrm_audit_state_delete(x, 0, task_valid); + return err; + } + } + } + + return err; +} #else static inline int xfrm_state_flush_secctx_check(struct net *net, u8 proto, bool task_valid) { return 0; } + +static inline int +xfrm_dev_state_flush_secctx_check(struct net *net, struct net_device *dev, bool task_valid) +{ + return 0; +} #endif int xfrm_state_flush(struct net *net, u8 proto, bool task_valid) @@ -701,6 +733,48 @@ out: } EXPORT_SYMBOL(xfrm_state_flush); +int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid) +{ + int i, err = 0, cnt = 0; + + spin_lock_bh(&net->xfrm.xfrm_state_lock); + err = xfrm_dev_state_flush_secctx_check(net, dev, task_valid); + if (err) + goto out; + + err = -ESRCH; + for (i = 0; i <= net->xfrm.state_hmask; i++) { + struct xfrm_state *x; + struct xfrm_state_offload *xso; +restart: + hlist_for_each_entry(x, net->xfrm.state_bydst+i, bydst) { + xso = &x->xso; + + if (!xfrm_state_kern(x) && xso->dev == dev) { + xfrm_state_hold(x); + spin_unlock_bh(&net->xfrm.xfrm_state_lock); + + err = xfrm_state_delete(x); + xfrm_audit_state_delete(x, err ? 0 : 1, + task_valid); + xfrm_state_put(x); + if (!err) + cnt++; + + spin_lock_bh(&net->xfrm.xfrm_state_lock); + goto restart; + } + } + } + if (cnt) + err = 0; + +out: + spin_unlock_bh(&net->xfrm.xfrm_state_lock); + return err; +} +EXPORT_SYMBOL(xfrm_dev_state_flush); + void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si) { spin_lock_bh(&net->xfrm.xfrm_state_lock); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 4f7e62ddc17e..de3332e3f9e2 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -595,6 +595,10 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, goto error; } + if (attrs[XFRMA_OFFLOAD_DEV] && + xfrm_dev_state_add(net, x, nla_data(attrs[XFRMA_OFFLOAD_DEV]))) + goto error; + if ((err = xfrm_alloc_replay_state_esn(&x->replay_esn, &x->preplay_esn, attrs[XFRMA_REPLAY_ESN_VAL]))) goto error; @@ -779,6 +783,23 @@ static int copy_sec_ctx(struct xfrm_sec_ctx *s, struct sk_buff *skb) return 0; } +static int copy_user_offload(struct xfrm_state_offload *xso, struct sk_buff *skb) +{ + struct xfrm_user_offload *xuo; + struct nlattr *attr; + + attr = nla_reserve(skb, XFRMA_OFFLOAD_DEV, sizeof(*xuo)); + if (attr == NULL) + return -EMSGSIZE; + + xuo = nla_data(attr); + + xuo->ifindex = xso->dev->ifindex; + xuo->flags = xso->flags; + + return 0; +} + static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb) { struct xfrm_algo *algo; @@ -869,6 +890,10 @@ static int copy_to_user_state_extra(struct xfrm_state *x, &x->replay); if (ret) goto out; + if(x->xso.dev) + ret = copy_user_offload(&x->xso, skb); + if (ret) + goto out; if (x->security) ret = copy_sec_ctx(x->security, skb); out: @@ -2406,6 +2431,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { [XFRMA_SA_EXTRA_FLAGS] = { .type = NLA_U32 }, [XFRMA_PROTO] = { .type = NLA_U8 }, [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, + [XFRMA_OFFLOAD_DEV] = { .len = sizeof(struct xfrm_user_offload) }, }; static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = { @@ -2622,6 +2648,8 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x) l += nla_total_size(sizeof(*x->coaddr)); if (x->props.extra_flags) l += nla_total_size(sizeof(x->props.extra_flags)); + if (x->xso.dev) + l += nla_total_size(sizeof(x->xso)); /* Must count x->lastused as it may become non-zero behind our back. */ l += nla_total_size_64bit(sizeof(u64)); -- cgit From f1fbed0e89303e6100085ea1dfb25d8413ca06ec Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Apr 2017 10:06:21 +0200 Subject: esp6: Remame esp_input_done2 We are going to export the ipv4 and the ipv6 version of esp_input_done2. They are not static anymore and can't have the same name. So rename the ipv6 version to esp6_input_done2. Signed-off-by: Steffen Klassert --- net/ipv6/esp6.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 3d3757d20d0a..5bd1dcc8d16b 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -447,7 +447,7 @@ error: return err; } -static int esp_input_done2(struct sk_buff *skb, int err) +static int esp6_input_done2(struct sk_buff *skb, int err) { struct xfrm_state *x = xfrm_input_state(skb); struct xfrm_offload *xo = xfrm_offload(skb); @@ -499,7 +499,7 @@ static void esp_input_done(struct crypto_async_request *base, int err) { struct sk_buff *skb = base->data; - xfrm_input_resume(skb, esp_input_done2(skb, err)); + xfrm_input_resume(skb, esp6_input_done2(skb, err)); } static void esp_input_restore_header(struct sk_buff *skb) @@ -621,7 +621,7 @@ skip_cow: if ((x->props.flags & XFRM_STATE_ESN)) esp_input_restore_header(skb); - ret = esp_input_done2(skb, ret); + ret = esp6_input_done2(skb, ret); out: return ret; -- cgit From fca11ebde3f0d1c637550a9b231caa385ce35749 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Apr 2017 10:06:33 +0200 Subject: esp4: Reorganize esp_output We need a fallback for ESP at layer 2, so split esp_output into generic functions that can be used at layer 3 and layer 2 and use them in esp_output. We also add esp_xmit which is used for the layer 2 fallback. Signed-off-by: Steffen Klassert --- include/net/esp.h | 16 +++ net/ipv4/esp4.c | 341 ++++++++++++++++++++++++++---------------------- net/ipv4/esp4_offload.c | 102 +++++++++++++++ 3 files changed, 301 insertions(+), 158 deletions(-) diff --git a/include/net/esp.h b/include/net/esp.h index a43be85aedc4..411a49915f11 100644 --- a/include/net/esp.h +++ b/include/net/esp.h @@ -10,4 +10,20 @@ static inline struct ip_esp_hdr *ip_esp_hdr(const struct sk_buff *skb) return (struct ip_esp_hdr *)skb_transport_header(skb); } +struct esp_info { + struct ip_esp_hdr *esph; + __be64 seqno; + int tfclen; + int tailen; + int plen; + int clen; + int len; + int nfrags; + __u8 proto; + bool inplace; +}; + +int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp); +int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp); +int esp_input_done2(struct sk_buff *skb, int err); #endif diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index c6aba234b6e9..91e6a402e22e 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -152,11 +152,10 @@ static void esp_output_restore_header(struct sk_buff *skb) } static struct ip_esp_hdr *esp_output_set_extra(struct sk_buff *skb, + struct xfrm_state *x, struct ip_esp_hdr *esph, struct esp_output_extra *extra) { - struct xfrm_state *x = skb_dst(skb)->xfrm; - /* For ESN we move the header forward by 4 bytes to * accomodate the high bits. We will move it back after * encryption. @@ -198,98 +197,56 @@ static void esp_output_fill_trailer(u8 *tail, int tfclen, int plen, __u8 proto) tail[plen - 1] = proto; } -static int esp_output(struct xfrm_state *x, struct sk_buff *skb) +static void esp_output_udp_encap(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp) { - struct esp_output_extra *extra; - int err = -ENOMEM; - struct ip_esp_hdr *esph; - struct crypto_aead *aead; - struct aead_request *req; - struct scatterlist *sg, *dsg; - struct sk_buff *trailer; - struct page *page; - void *tmp; - u8 *iv; - u8 *tail; - u8 *vaddr; - int blksize; - int clen; - int alen; - int plen; - int ivlen; - int tfclen; - int nfrags; - int assoclen; - int extralen; - int tailen; - __be64 seqno; - __u8 proto = *skb_mac_header(skb); - - /* skb is pure payload to encrypt */ - - aead = x->data; - alen = crypto_aead_authsize(aead); - ivlen = crypto_aead_ivsize(aead); - - tfclen = 0; - if (x->tfcpad) { - struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); - u32 padto; - - padto = min(x->tfcpad, esp4_get_mtu(x, dst->child_mtu_cached)); - if (skb->len < padto) - tfclen = padto - skb->len; + int encap_type; + struct udphdr *uh; + __be32 *udpdata32; + __be16 sport, dport; + struct xfrm_encap_tmpl *encap = x->encap; + struct ip_esp_hdr *esph = esp->esph; + + spin_lock_bh(&x->lock); + sport = encap->encap_sport; + dport = encap->encap_dport; + encap_type = encap->encap_type; + spin_unlock_bh(&x->lock); + + uh = (struct udphdr *)esph; + uh->source = sport; + uh->dest = dport; + uh->len = htons(skb->len + esp->tailen + - skb_transport_offset(skb)); + uh->check = 0; + + switch (encap_type) { + default: + case UDP_ENCAP_ESPINUDP: + esph = (struct ip_esp_hdr *)(uh + 1); + break; + case UDP_ENCAP_ESPINUDP_NON_IKE: + udpdata32 = (__be32 *)(uh + 1); + udpdata32[0] = udpdata32[1] = 0; + esph = (struct ip_esp_hdr *)(udpdata32 + 2); + break; } - blksize = ALIGN(crypto_aead_blocksize(aead), 4); - clen = ALIGN(skb->len + 2 + tfclen, blksize); - plen = clen - skb->len - tfclen; - tailen = tfclen + plen + alen; - assoclen = sizeof(*esph); - extralen = 0; - if (x->props.flags & XFRM_STATE_ESN) { - extralen += sizeof(*extra); - assoclen += sizeof(__be32); - } + *skb_mac_header(skb) = IPPROTO_UDP; + esp->esph = esph; +} - *skb_mac_header(skb) = IPPROTO_ESP; - esph = ip_esp_hdr(skb); +int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp) +{ + u8 *tail; + u8 *vaddr; + int nfrags; + struct page *page; + struct sk_buff *trailer; + int tailen = esp->tailen; /* this is non-NULL only with UDP Encapsulation */ - if (x->encap) { - struct xfrm_encap_tmpl *encap = x->encap; - struct udphdr *uh; - __be32 *udpdata32; - __be16 sport, dport; - int encap_type; - - spin_lock_bh(&x->lock); - sport = encap->encap_sport; - dport = encap->encap_dport; - encap_type = encap->encap_type; - spin_unlock_bh(&x->lock); - - uh = (struct udphdr *)esph; - uh->source = sport; - uh->dest = dport; - uh->len = htons(skb->len + tailen - - skb_transport_offset(skb)); - uh->check = 0; - - switch (encap_type) { - default: - case UDP_ENCAP_ESPINUDP: - esph = (struct ip_esp_hdr *)(uh + 1); - break; - case UDP_ENCAP_ESPINUDP_NON_IKE: - udpdata32 = (__be32 *)(uh + 1); - udpdata32[0] = udpdata32[1] = 0; - esph = (struct ip_esp_hdr *)(udpdata32 + 2); - break; - } - - *skb_mac_header(skb) = IPPROTO_UDP; - } + if (x->encap) + esp_output_udp_encap(x, skb, esp); if (!skb_cloned(skb)) { if (tailen <= skb_availroom(skb)) { @@ -304,6 +261,8 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) struct sock *sk = skb->sk; struct page_frag *pfrag = &x->xfrag; + esp->inplace = false; + allocsize = ALIGN(tailen, L1_CACHE_BYTES); spin_lock_bh(&x->lock); @@ -320,10 +279,12 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) tail = vaddr + pfrag->offset; - esp_output_fill_trailer(tail, tfclen, plen, proto); + esp_output_fill_trailer(tail, esp->tfclen, esp->plen, esp->proto); kunmap_atomic(vaddr); + spin_unlock_bh(&x->lock); + nfrags = skb_shinfo(skb)->nr_frags; __skb_fill_page_desc(skb, nfrags, page, pfrag->offset, @@ -339,76 +300,56 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) if (sk) atomic_add(tailen, &sk->sk_wmem_alloc); - skb_push(skb, -skb_network_offset(skb)); - - esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); - esph->spi = x->id.spi; - - tmp = esp_alloc_tmp(aead, nfrags + 2, extralen); - if (!tmp) { - spin_unlock_bh(&x->lock); - err = -ENOMEM; - goto error; - } - - extra = esp_tmp_extra(tmp); - iv = esp_tmp_iv(aead, tmp, extralen); - req = esp_tmp_req(aead, iv); - sg = esp_req_sg(aead, req); - dsg = &sg[nfrags]; - - esph = esp_output_set_extra(skb, esph, extra); - - sg_init_table(sg, nfrags); - skb_to_sgvec(skb, sg, - (unsigned char *)esph - skb->data, - assoclen + ivlen + clen + alen); - - allocsize = ALIGN(skb->data_len, L1_CACHE_BYTES); - - if (unlikely(!skb_page_frag_refill(allocsize, pfrag, GFP_ATOMIC))) { - spin_unlock_bh(&x->lock); - err = -ENOMEM; - goto error; - } - - skb_shinfo(skb)->nr_frags = 1; - - page = pfrag->page; - get_page(page); - /* replace page frags in skb with new page */ - __skb_fill_page_desc(skb, 0, page, pfrag->offset, skb->data_len); - pfrag->offset = pfrag->offset + allocsize; - - sg_init_table(dsg, skb_shinfo(skb)->nr_frags + 1); - skb_to_sgvec(skb, dsg, - (unsigned char *)esph - skb->data, - assoclen + ivlen + clen + alen); - - spin_unlock_bh(&x->lock); - - goto skip_cow2; + goto out; } } cow: - err = skb_cow_data(skb, tailen, &trailer); - if (err < 0) - goto error; - nfrags = err; + nfrags = skb_cow_data(skb, tailen, &trailer); + if (nfrags < 0) + goto out; tail = skb_tail_pointer(trailer); - esph = ip_esp_hdr(skb); skip_cow: - esp_output_fill_trailer(tail, tfclen, plen, proto); + esp_output_fill_trailer(tail, esp->tfclen, esp->plen, esp->proto); + pskb_put(skb, trailer, tailen); - pskb_put(skb, trailer, clen - skb->len + alen); - skb_push(skb, -skb_network_offset(skb)); - esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); - esph->spi = x->id.spi; +out: + return nfrags; +} +EXPORT_SYMBOL_GPL(esp_output_head); + +int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp) +{ + u8 *iv; + int alen; + void *tmp; + int ivlen; + int assoclen; + int extralen; + struct page *page; + struct ip_esp_hdr *esph; + struct crypto_aead *aead; + struct aead_request *req; + struct scatterlist *sg, *dsg; + struct esp_output_extra *extra; + int err = -ENOMEM; + + assoclen = sizeof(struct ip_esp_hdr); + extralen = 0; + + if (x->props.flags & XFRM_STATE_ESN) { + extralen += sizeof(*extra); + assoclen += sizeof(__be32); + } - tmp = esp_alloc_tmp(aead, nfrags, extralen); + aead = x->data; + alen = crypto_aead_authsize(aead); + ivlen = crypto_aead_ivsize(aead); + + tmp = esp_alloc_tmp(aead, esp->nfrags + 2, extralen); if (!tmp) { + spin_unlock_bh(&x->lock); err = -ENOMEM; goto error; } @@ -417,26 +358,58 @@ skip_cow: iv = esp_tmp_iv(aead, tmp, extralen); req = esp_tmp_req(aead, iv); sg = esp_req_sg(aead, req); - dsg = sg; - esph = esp_output_set_extra(skb, esph, extra); + if (esp->inplace) + dsg = sg; + else + dsg = &sg[esp->nfrags]; - sg_init_table(sg, nfrags); + esph = esp_output_set_extra(skb, x, esp->esph, extra); + esp->esph = esph; + + sg_init_table(sg, esp->nfrags); skb_to_sgvec(skb, sg, (unsigned char *)esph - skb->data, - assoclen + ivlen + clen + alen); + assoclen + ivlen + esp->clen + alen); + + if (!esp->inplace) { + int allocsize; + struct page_frag *pfrag = &x->xfrag; + + allocsize = ALIGN(skb->data_len, L1_CACHE_BYTES); + + spin_lock_bh(&x->lock); + if (unlikely(!skb_page_frag_refill(allocsize, pfrag, GFP_ATOMIC))) { + spin_unlock_bh(&x->lock); + err = -ENOMEM; + goto error; + } + + skb_shinfo(skb)->nr_frags = 1; + + page = pfrag->page; + get_page(page); + /* replace page frags in skb with new page */ + __skb_fill_page_desc(skb, 0, page, pfrag->offset, skb->data_len); + pfrag->offset = pfrag->offset + allocsize; + spin_unlock_bh(&x->lock); + + sg_init_table(dsg, skb_shinfo(skb)->nr_frags + 1); + skb_to_sgvec(skb, dsg, + (unsigned char *)esph - skb->data, + assoclen + ivlen + esp->clen + alen); + } -skip_cow2: if ((x->props.flags & XFRM_STATE_ESN)) aead_request_set_callback(req, 0, esp_output_done_esn, skb); else aead_request_set_callback(req, 0, esp_output_done, skb); - aead_request_set_crypt(req, sg, dsg, ivlen + clen, iv); + aead_request_set_crypt(req, sg, dsg, ivlen + esp->clen, iv); aead_request_set_ad(req, assoclen); memset(iv, 0, ivlen); - memcpy(iv + ivlen - min(ivlen, 8), (u8 *)&seqno + 8 - min(ivlen, 8), + memcpy(iv + ivlen - min(ivlen, 8), (u8 *)&esp->seqno + 8 - min(ivlen, 8), min(ivlen, 8)); ESP_SKB_CB(skb)->tmp = tmp; @@ -462,8 +435,59 @@ skip_cow2: error: return err; } +EXPORT_SYMBOL_GPL(esp_output_tail); + +static int esp_output(struct xfrm_state *x, struct sk_buff *skb) +{ + int alen; + int blksize; + struct ip_esp_hdr *esph; + struct crypto_aead *aead; + struct esp_info esp; + + esp.inplace = true; + + esp.proto = *skb_mac_header(skb); + *skb_mac_header(skb) = IPPROTO_ESP; + + /* skb is pure payload to encrypt */ + + aead = x->data; + alen = crypto_aead_authsize(aead); + + esp.tfclen = 0; + if (x->tfcpad) { + struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); + u32 padto; + + padto = min(x->tfcpad, esp4_get_mtu(x, dst->child_mtu_cached)); + if (skb->len < padto) + esp.tfclen = padto - skb->len; + } + blksize = ALIGN(crypto_aead_blocksize(aead), 4); + esp.clen = ALIGN(skb->len + 2 + esp.tfclen, blksize); + esp.plen = esp.clen - skb->len - esp.tfclen; + esp.tailen = esp.tfclen + esp.plen + alen; + + esp.esph = ip_esp_hdr(skb); + + esp.nfrags = esp_output_head(x, skb, &esp); + if (esp.nfrags < 0) + return esp.nfrags; + + esph = esp.esph; + esph->spi = x->id.spi; + + esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); + esp.seqno = cpu_to_be64(XFRM_SKB_CB(skb)->seq.output.low + + ((u64)XFRM_SKB_CB(skb)->seq.output.hi << 32)); + + skb_push(skb, -skb_network_offset(skb)); + + return esp_output_tail(x, skb, &esp); +} -static int esp_input_done2(struct sk_buff *skb, int err) +int esp_input_done2(struct sk_buff *skb, int err) { const struct iphdr *iph; struct xfrm_state *x = xfrm_input_state(skb); @@ -548,6 +572,7 @@ static int esp_input_done2(struct sk_buff *skb, int err) out: return err; } +EXPORT_SYMBOL_GPL(esp_input_done2); static void esp_input_done(struct crypto_async_request *base, int err) { @@ -930,7 +955,7 @@ static const struct xfrm_type esp_type = .destructor = esp_destroy, .get_mtu = esp4_get_mtu, .input = esp_input, - .output = esp_output + .output = esp_output, }; static struct xfrm4_protocol esp4_protocol = { diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c index 1de442632406..efaaa44e1073 100644 --- a/net/ipv4/esp4_offload.c +++ b/net/ipv4/esp4_offload.c @@ -84,19 +84,121 @@ out: return NULL; } +static int esp_input_tail(struct xfrm_state *x, struct sk_buff *skb) +{ + struct crypto_aead *aead = x->data; + + if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead))) + return -EINVAL; + + skb->ip_summed = CHECKSUM_NONE; + + return esp_input_done2(skb, 0); +} + +static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_t features) +{ + int err; + int alen; + int blksize; + struct xfrm_offload *xo; + struct ip_esp_hdr *esph; + struct crypto_aead *aead; + struct esp_info esp; + bool hw_offload = true; + + esp.inplace = true; + + xo = xfrm_offload(skb); + + if (!xo) + return -EINVAL; + + if (!(features & NETIF_F_HW_ESP) || + (x->xso.offload_handle && x->xso.dev != skb->dev)) { + xo->flags |= CRYPTO_FALLBACK; + hw_offload = false; + } + + esp.proto = xo->proto; + + /* skb is pure payload to encrypt */ + + aead = x->data; + alen = crypto_aead_authsize(aead); + + esp.tfclen = 0; + /* XXX: Add support for tfc padding here. */ + + blksize = ALIGN(crypto_aead_blocksize(aead), 4); + esp.clen = ALIGN(skb->len + 2 + esp.tfclen, blksize); + esp.plen = esp.clen - skb->len - esp.tfclen; + esp.tailen = esp.tfclen + esp.plen + alen; + + esp.esph = ip_esp_hdr(skb); + + + if (!hw_offload || (hw_offload && !skb_is_gso(skb))) { + esp.nfrags = esp_output_head(x, skb, &esp); + if (esp.nfrags < 0) + return esp.nfrags; + } + + esph = esp.esph; + esph->spi = x->id.spi; + + skb_push(skb, -skb_network_offset(skb)); + + if (xo->flags & XFRM_GSO_SEGMENT) { + esph->seq_no = htonl(xo->seq.low); + } else { + ip_hdr(skb)->tot_len = htons(skb->len); + ip_send_check(ip_hdr(skb)); + } + + if (hw_offload) + return 0; + + esp.seqno = cpu_to_be64(xo->seq.low + ((u64)xo->seq.hi << 32)); + + err = esp_output_tail(x, skb, &esp); + if (err < 0) + return err; + + secpath_reset(skb); + + return 0; +} + static const struct net_offload esp4_offload = { .callbacks = { .gro_receive = esp4_gro_receive, }, }; +static const struct xfrm_type_offload esp_type_offload = { + .description = "ESP4 OFFLOAD", + .owner = THIS_MODULE, + .proto = IPPROTO_ESP, + .input_tail = esp_input_tail, + .xmit = esp_xmit, +}; + static int __init esp4_offload_init(void) { + if (xfrm_register_type_offload(&esp_type_offload, AF_INET) < 0) { + pr_info("%s: can't add xfrm type offload\n", __func__); + return -EAGAIN; + } + return inet_add_offload(&esp4_offload, IPPROTO_ESP); } static void __exit esp4_offload_exit(void) { + if (xfrm_unregister_type_offload(&esp_type_offload, AF_INET) < 0) + pr_info("%s: can't remove xfrm type offload\n", __func__); + inet_del_offload(&esp4_offload, IPPROTO_ESP); } -- cgit From 383d0350f2cc5e5b3a2003c46e15c0b98432037b Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Apr 2017 10:06:42 +0200 Subject: esp6: Reorganize esp_output We need a fallback for ESP at layer 2, so split esp6_output into generic functions that can be used at layer 3 and layer 2 and use them in esp_output. We also add esp6_xmit which is used for the layer 2 fallback. Signed-off-by: Steffen Klassert --- include/net/esp.h | 3 + net/ipv6/esp6.c | 264 +++++++++++++++++++++++++----------------------- net/ipv6/esp6_offload.c | 103 +++++++++++++++++++ 3 files changed, 246 insertions(+), 124 deletions(-) diff --git a/include/net/esp.h b/include/net/esp.h index 411a49915f11..c41994d1bfef 100644 --- a/include/net/esp.h +++ b/include/net/esp.h @@ -26,4 +26,7 @@ struct esp_info { int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp); int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp); int esp_input_done2(struct sk_buff *skb, int err); +int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp); +int esp6_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp); +int esp6_input_done2(struct sk_buff *skb, int err); #endif diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 5bd1dcc8d16b..cc654a7afd91 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -170,11 +170,10 @@ static void esp_output_restore_header(struct sk_buff *skb) } static struct ip_esp_hdr *esp_output_set_esn(struct sk_buff *skb, + struct xfrm_state *x, struct ip_esp_hdr *esph, __be32 *seqhi) { - struct xfrm_state *x = skb_dst(skb)->xfrm; - /* For ESN we move the header forward by 4 bytes to * accomodate the high bits. We will move it back after * encryption. @@ -214,59 +213,15 @@ static void esp_output_fill_trailer(u8 *tail, int tfclen, int plen, __u8 proto) tail[plen - 1] = proto; } -static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) +int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp) { - int err; - struct ip_esp_hdr *esph; - struct crypto_aead *aead; - struct aead_request *req; - struct scatterlist *sg, *dsg; - struct sk_buff *trailer; - struct page *page; - void *tmp; - int blksize; - int clen; - int alen; - int plen; - int ivlen; - int tfclen; - int nfrags; - int assoclen; - int seqhilen; - int tailen; - u8 *iv; u8 *tail; u8 *vaddr; - __be32 *seqhi; - __be64 seqno; - __u8 proto = *skb_mac_header(skb); - - /* skb is pure payload to encrypt */ - aead = x->data; - alen = crypto_aead_authsize(aead); - ivlen = crypto_aead_ivsize(aead); - - tfclen = 0; - if (x->tfcpad) { - struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); - u32 padto; - - padto = min(x->tfcpad, esp6_get_mtu(x, dst->child_mtu_cached)); - if (skb->len < padto) - tfclen = padto - skb->len; - } - blksize = ALIGN(crypto_aead_blocksize(aead), 4); - clen = ALIGN(skb->len + 2 + tfclen, blksize); - plen = clen - skb->len - tfclen; - tailen = tfclen + plen + alen; - - assoclen = sizeof(*esph); - seqhilen = 0; - - if (x->props.flags & XFRM_STATE_ESN) { - seqhilen += sizeof(__be32); - assoclen += seqhilen; - } + int nfrags; + struct page *page; + struct ip_esp_hdr *esph; + struct sk_buff *trailer; + int tailen = esp->tailen; *skb_mac_header(skb) = IPPROTO_ESP; esph = ip_esp_hdr(skb); @@ -284,6 +239,8 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) struct sock *sk = skb->sk; struct page_frag *pfrag = &x->xfrag; + esp->inplace = false; + allocsize = ALIGN(tailen, L1_CACHE_BYTES); spin_lock_bh(&x->lock); @@ -300,10 +257,12 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) tail = vaddr + pfrag->offset; - esp_output_fill_trailer(tail, tfclen, plen, proto); + esp_output_fill_trailer(tail, esp->tfclen, esp->plen, esp->proto); kunmap_atomic(vaddr); + spin_unlock_bh(&x->lock); + nfrags = skb_shinfo(skb)->nr_frags; __skb_fill_page_desc(skb, nfrags, page, pfrag->offset, @@ -319,77 +278,56 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) if (sk) atomic_add(tailen, &sk->sk_wmem_alloc); - skb_push(skb, -skb_network_offset(skb)); - - esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); - esph->spi = x->id.spi; - - tmp = esp_alloc_tmp(aead, nfrags + 2, seqhilen); - if (!tmp) { - spin_unlock_bh(&x->lock); - err = -ENOMEM; - goto error; - } - seqhi = esp_tmp_seqhi(tmp); - iv = esp_tmp_iv(aead, tmp, seqhilen); - req = esp_tmp_req(aead, iv); - sg = esp_req_sg(aead, req); - dsg = &sg[nfrags]; - - esph = esp_output_set_esn(skb, esph, seqhi); - - sg_init_table(sg, nfrags); - skb_to_sgvec(skb, sg, - (unsigned char *)esph - skb->data, - assoclen + ivlen + clen + alen); - - allocsize = ALIGN(skb->data_len, L1_CACHE_BYTES); - - if (unlikely(!skb_page_frag_refill(allocsize, pfrag, GFP_ATOMIC))) { - spin_unlock_bh(&x->lock); - err = -ENOMEM; - goto error; - } - - skb_shinfo(skb)->nr_frags = 1; - - page = pfrag->page; - get_page(page); - /* replace page frags in skb with new page */ - __skb_fill_page_desc(skb, 0, page, pfrag->offset, skb->data_len); - pfrag->offset = pfrag->offset + allocsize; - - sg_init_table(dsg, skb_shinfo(skb)->nr_frags + 1); - skb_to_sgvec(skb, dsg, - (unsigned char *)esph - skb->data, - assoclen + ivlen + clen + alen); - - spin_unlock_bh(&x->lock); - - goto skip_cow2; + goto out; } } cow: - err = skb_cow_data(skb, tailen, &trailer); - if (err < 0) - goto error; - nfrags = err; - + nfrags = skb_cow_data(skb, tailen, &trailer); + if (nfrags < 0) + goto out; tail = skb_tail_pointer(trailer); - esph = ip_esp_hdr(skb); skip_cow: - esp_output_fill_trailer(tail, tfclen, plen, proto); + esp_output_fill_trailer(tail, esp->tfclen, esp->plen, esp->proto); + pskb_put(skb, trailer, tailen); - pskb_put(skb, trailer, clen - skb->len + alen); - skb_push(skb, -skb_network_offset(skb)); +out: + return nfrags; +} +EXPORT_SYMBOL_GPL(esp6_output_head); - esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); - esph->spi = x->id.spi; +int esp6_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp) +{ + u8 *iv; + int alen; + void *tmp; + int ivlen; + int assoclen; + int seqhilen; + __be32 *seqhi; + struct page *page; + struct ip_esp_hdr *esph; + struct aead_request *req; + struct crypto_aead *aead; + struct scatterlist *sg, *dsg; + int err = -ENOMEM; - tmp = esp_alloc_tmp(aead, nfrags, seqhilen); + assoclen = sizeof(struct ip_esp_hdr); + seqhilen = 0; + + if (x->props.flags & XFRM_STATE_ESN) { + seqhilen += sizeof(__be32); + assoclen += sizeof(__be32); + } + + aead = x->data; + alen = crypto_aead_authsize(aead); + ivlen = crypto_aead_ivsize(aead); + + tmp = esp_alloc_tmp(aead, esp->nfrags + 2, seqhilen); if (!tmp) { + spin_unlock_bh(&x->lock); err = -ENOMEM; goto error; } @@ -398,29 +336,57 @@ skip_cow: iv = esp_tmp_iv(aead, tmp, seqhilen); req = esp_tmp_req(aead, iv); sg = esp_req_sg(aead, req); - dsg = sg; - esph = esp_output_set_esn(skb, esph, seqhi); + if (esp->inplace) + dsg = sg; + else + dsg = &sg[esp->nfrags]; - sg_init_table(sg, nfrags); + esph = esp_output_set_esn(skb, x, ip_esp_hdr(skb), seqhi); + + sg_init_table(sg, esp->nfrags); skb_to_sgvec(skb, sg, (unsigned char *)esph - skb->data, - assoclen + ivlen + clen + alen); + assoclen + ivlen + esp->clen + alen); + + if (!esp->inplace) { + int allocsize; + struct page_frag *pfrag = &x->xfrag; + + allocsize = ALIGN(skb->data_len, L1_CACHE_BYTES); + + spin_lock_bh(&x->lock); + if (unlikely(!skb_page_frag_refill(allocsize, pfrag, GFP_ATOMIC))) { + spin_unlock_bh(&x->lock); + err = -ENOMEM; + goto error; + } + + skb_shinfo(skb)->nr_frags = 1; + + page = pfrag->page; + get_page(page); + /* replace page frags in skb with new page */ + __skb_fill_page_desc(skb, 0, page, pfrag->offset, skb->data_len); + pfrag->offset = pfrag->offset + allocsize; + spin_unlock_bh(&x->lock); + + sg_init_table(dsg, skb_shinfo(skb)->nr_frags + 1); + skb_to_sgvec(skb, dsg, + (unsigned char *)esph - skb->data, + assoclen + ivlen + esp->clen + alen); + } -skip_cow2: if ((x->props.flags & XFRM_STATE_ESN)) aead_request_set_callback(req, 0, esp_output_done_esn, skb); else aead_request_set_callback(req, 0, esp_output_done, skb); - aead_request_set_crypt(req, sg, dsg, ivlen + clen, iv); + aead_request_set_crypt(req, sg, dsg, ivlen + esp->clen, iv); aead_request_set_ad(req, assoclen); - seqno = cpu_to_be64(XFRM_SKB_CB(skb)->seq.output.low + - ((u64)XFRM_SKB_CB(skb)->seq.output.hi << 32)); - memset(iv, 0, ivlen); - memcpy(iv + ivlen - min(ivlen, 8), (u8 *)&seqno + 8 - min(ivlen, 8), + memcpy(iv + ivlen - min(ivlen, 8), (u8 *)&esp->seqno + 8 - min(ivlen, 8), min(ivlen, 8)); ESP_SKB_CB(skb)->tmp = tmp; @@ -446,8 +412,57 @@ skip_cow2: error: return err; } +EXPORT_SYMBOL_GPL(esp6_output_tail); + +static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) +{ + int alen; + int blksize; + struct ip_esp_hdr *esph; + struct crypto_aead *aead; + struct esp_info esp; + + esp.inplace = true; + + esp.proto = *skb_mac_header(skb); + *skb_mac_header(skb) = IPPROTO_ESP; + + /* skb is pure payload to encrypt */ + + aead = x->data; + alen = crypto_aead_authsize(aead); + + esp.tfclen = 0; + if (x->tfcpad) { + struct xfrm_dst *dst = (struct xfrm_dst *)skb_dst(skb); + u32 padto; + + padto = min(x->tfcpad, esp6_get_mtu(x, dst->child_mtu_cached)); + if (skb->len < padto) + esp.tfclen = padto - skb->len; + } + blksize = ALIGN(crypto_aead_blocksize(aead), 4); + esp.clen = ALIGN(skb->len + 2 + esp.tfclen, blksize); + esp.plen = esp.clen - skb->len - esp.tfclen; + esp.tailen = esp.tfclen + esp.plen + alen; + + esp.nfrags = esp6_output_head(x, skb, &esp); + if (esp.nfrags < 0) + return esp.nfrags; + + esph = ip_esp_hdr(skb); + esph->spi = x->id.spi; + + esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); + esp.seqno = cpu_to_be64(XFRM_SKB_CB(skb)->seq.output.low + + ((u64)XFRM_SKB_CB(skb)->seq.output.hi << 32)); + + skb_push(skb, -skb_network_offset(skb)); + + return esp6_output_tail(x, skb, &esp); +} -static int esp6_input_done2(struct sk_buff *skb, int err) +int esp6_input_done2(struct sk_buff *skb, int err) { struct xfrm_state *x = xfrm_input_state(skb); struct xfrm_offload *xo = xfrm_offload(skb); @@ -494,6 +509,7 @@ static int esp6_input_done2(struct sk_buff *skb, int err) out: return err; } +EXPORT_SYMBOL_GPL(esp6_input_done2); static void esp_input_done(struct crypto_async_request *base, int err) { diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c index d914eb93204a..5dd20c0de956 100644 --- a/net/ipv6/esp6_offload.c +++ b/net/ipv6/esp6_offload.c @@ -86,19 +86,122 @@ out: return NULL; } +static int esp6_input_tail(struct xfrm_state *x, struct sk_buff *skb) +{ + struct crypto_aead *aead = x->data; + + if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead))) + return -EINVAL; + + skb->ip_summed = CHECKSUM_NONE; + + return esp6_input_done2(skb, 0); +} + +static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_t features) +{ + int err; + int alen; + int blksize; + struct xfrm_offload *xo; + struct ip_esp_hdr *esph; + struct crypto_aead *aead; + struct esp_info esp; + bool hw_offload = true; + + esp.inplace = true; + + xo = xfrm_offload(skb); + + if (!xo) + return -EINVAL; + + if (!(features & NETIF_F_HW_ESP) || + (x->xso.offload_handle && x->xso.dev != skb->dev)) { + xo->flags |= CRYPTO_FALLBACK; + } + + esp.proto = xo->proto; + + /* skb is pure payload to encrypt */ + + aead = x->data; + alen = crypto_aead_authsize(aead); + + esp.tfclen = 0; + /* XXX: Add support for tfc padding here. */ + + blksize = ALIGN(crypto_aead_blocksize(aead), 4); + esp.clen = ALIGN(skb->len + 2 + esp.tfclen, blksize); + esp.plen = esp.clen - skb->len - esp.tfclen; + esp.tailen = esp.tfclen + esp.plen + alen; + + if (!hw_offload || (hw_offload && !skb_is_gso(skb))) { + esp.nfrags = esp6_output_head(x, skb, &esp); + if (esp.nfrags < 0) + return esp.nfrags; + } + + esph = ip_esp_hdr(skb); + esph->spi = x->id.spi; + + skb_push(skb, -skb_network_offset(skb)); + + if (xo->flags & XFRM_GSO_SEGMENT) { + esph->seq_no = htonl(xo->seq.low); + } else { + int len; + + len = skb->len - sizeof(struct ipv6hdr); + if (len > IPV6_MAXPLEN) + len = 0; + + ipv6_hdr(skb)->payload_len = htons(len); + } + + if (x->xso.offload_handle && !(xo->flags & CRYPTO_FALLBACK)) + return 0; + + esp.seqno = cpu_to_be64(xo->seq.low + ((u64)xo->seq.hi << 32)); + + err = esp6_output_tail(x, skb, &esp); + if (err < 0) + return err; + + secpath_reset(skb); + + return 0; +} + static const struct net_offload esp6_offload = { .callbacks = { .gro_receive = esp6_gro_receive, }, }; +static const struct xfrm_type_offload esp6_type_offload = { + .description = "ESP6 OFFLOAD", + .owner = THIS_MODULE, + .proto = IPPROTO_ESP, + .input_tail = esp6_input_tail, + .xmit = esp6_xmit, +}; + static int __init esp6_offload_init(void) { + if (xfrm_register_type_offload(&esp6_type_offload, AF_INET6) < 0) { + pr_info("%s: can't add xfrm type offload\n", __func__); + return -EAGAIN; + } + return inet6_add_offload(&esp6_offload, IPPROTO_ESP); } static void __exit esp6_offload_exit(void) { + if (xfrm_unregister_type_offload(&esp6_type_offload, AF_INET6) < 0) + pr_info("%s: can't remove xfrm type offload\n", __func__); + inet6_del_offload(&esp6_offload, IPPROTO_ESP); } -- cgit From 7862b4058b9f10c9177f347e7d981511bac87213 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Apr 2017 10:06:50 +0200 Subject: esp: Add gso handlers for esp4 and esp6 This patch extends the xfrm_type by an encap function pointer and implements esp4_gso_encap and esp6_gso_encap. These functions doing the basic esp encapsulation for a GSO packet. In case the GSO packet needs to be segmented in software, we add gso_segment functions. This codepath is going to be used on esp hardware offloads. Signed-off-by: Steffen Klassert --- net/ipv4/esp4.c | 10 +++++- net/ipv4/esp4_offload.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++ net/ipv6/esp6.c | 8 +++-- net/ipv6/esp6_offload.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++ net/xfrm/xfrm_replay.c | 3 +- 5 files changed, 203 insertions(+), 4 deletions(-) diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 91e6a402e22e..4382f306935b 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -161,11 +161,19 @@ static struct ip_esp_hdr *esp_output_set_extra(struct sk_buff *skb, * encryption. */ if ((x->props.flags & XFRM_STATE_ESN)) { + __u32 seqhi; + struct xfrm_offload *xo = xfrm_offload(skb); + + if (xo) + seqhi = xo->seq.hi; + else + seqhi = XFRM_SKB_CB(skb)->seq.output.hi; + extra->esphoff = (unsigned char *)esph - skb_transport_header(skb); esph = (struct ip_esp_hdr *)((unsigned char *)esph - 4); extra->seqhi = esph->spi; - esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.hi); + esph->seq_no = htonl(seqhi); } esph->spi = x->id.spi; diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c index efaaa44e1073..1e39564cb8b4 100644 --- a/net/ipv4/esp4_offload.c +++ b/net/ipv4/esp4_offload.c @@ -84,6 +84,97 @@ out: return NULL; } +static void esp4_gso_encap(struct xfrm_state *x, struct sk_buff *skb) +{ + struct ip_esp_hdr *esph; + struct iphdr *iph = ip_hdr(skb); + struct xfrm_offload *xo = xfrm_offload(skb); + int proto = iph->protocol; + + skb_push(skb, -skb_network_offset(skb)); + esph = ip_esp_hdr(skb); + *skb_mac_header(skb) = IPPROTO_ESP; + + esph->spi = x->id.spi; + esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); + + xo->proto = proto; +} + +static struct sk_buff *esp4_gso_segment(struct sk_buff *skb, + netdev_features_t features) +{ + __u32 seq; + int err = 0; + struct sk_buff *skb2; + struct xfrm_state *x; + struct ip_esp_hdr *esph; + struct crypto_aead *aead; + struct sk_buff *segs = ERR_PTR(-EINVAL); + netdev_features_t esp_features = features; + struct xfrm_offload *xo = xfrm_offload(skb); + + if (!xo) + goto out; + + seq = xo->seq.low; + + x = skb->sp->xvec[skb->sp->len - 1]; + aead = x->data; + esph = ip_esp_hdr(skb); + + if (esph->spi != x->id.spi) + goto out; + + if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead))) + goto out; + + __skb_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead)); + + skb->encap_hdr_csum = 1; + + if (!(features & NETIF_F_HW_ESP)) + esp_features = features & ~(NETIF_F_SG | NETIF_F_CSUM_MASK); + + segs = x->outer_mode->gso_segment(x, skb, esp_features); + if (IS_ERR_OR_NULL(segs)) + goto out; + + __skb_pull(skb, skb->data - skb_mac_header(skb)); + + skb2 = segs; + do { + struct sk_buff *nskb = skb2->next; + + xo = xfrm_offload(skb2); + xo->flags |= XFRM_GSO_SEGMENT; + xo->seq.low = seq; + xo->seq.hi = xfrm_replay_seqhi(x, seq); + + if(!(features & NETIF_F_HW_ESP)) + xo->flags |= CRYPTO_FALLBACK; + + x->outer_mode->xmit(x, skb2); + + err = x->type_offload->xmit(x, skb2, esp_features); + if (err) { + kfree_skb_list(segs); + return ERR_PTR(err); + } + + if (!skb_is_gso(skb2)) + seq++; + else + seq += skb_shinfo(skb2)->gso_segs; + + skb_push(skb2, skb2->mac_len); + skb2 = nskb; + } while (skb2); + +out: + return segs; +} + static int esp_input_tail(struct xfrm_state *x, struct sk_buff *skb) { struct crypto_aead *aead = x->data; @@ -173,6 +264,7 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_ static const struct net_offload esp4_offload = { .callbacks = { .gro_receive = esp4_gro_receive, + .gso_segment = esp4_gso_segment, }, }; @@ -182,6 +274,7 @@ static const struct xfrm_type_offload esp_type_offload = { .proto = IPPROTO_ESP, .input_tail = esp_input_tail, .xmit = esp_xmit, + .encap = esp4_gso_encap, }; static int __init esp4_offload_init(void) diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index cc654a7afd91..82d3da81293e 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -179,9 +179,14 @@ static struct ip_esp_hdr *esp_output_set_esn(struct sk_buff *skb, * encryption. */ if ((x->props.flags & XFRM_STATE_ESN)) { + struct xfrm_offload *xo = xfrm_offload(skb); + esph = (void *)(skb_transport_header(skb) - sizeof(__be32)); *seqhi = esph->spi; - esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.hi); + if (xo) + esph->seq_no = htonl(xo->seq.hi); + else + esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.hi); } esph->spi = x->id.spi; @@ -223,7 +228,6 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info struct sk_buff *trailer; int tailen = esp->tailen; - *skb_mac_header(skb) = IPPROTO_ESP; esph = ip_esp_hdr(skb); if (!skb_cloned(skb)) { diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c index 5dd20c0de956..06e972135ab0 100644 --- a/net/ipv6/esp6_offload.c +++ b/net/ipv6/esp6_offload.c @@ -86,6 +86,97 @@ out: return NULL; } +static void esp6_gso_encap(struct xfrm_state *x, struct sk_buff *skb) +{ + struct ip_esp_hdr *esph; + struct ipv6hdr *iph = ipv6_hdr(skb); + struct xfrm_offload *xo = xfrm_offload(skb); + int proto = iph->nexthdr; + + skb_push(skb, -skb_network_offset(skb)); + esph = ip_esp_hdr(skb); + *skb_mac_header(skb) = IPPROTO_ESP; + + esph->spi = x->id.spi; + esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); + + xo->proto = proto; +} + +static struct sk_buff *esp6_gso_segment(struct sk_buff *skb, + netdev_features_t features) +{ + __u32 seq; + int err = 0; + struct sk_buff *skb2; + struct xfrm_state *x; + struct ip_esp_hdr *esph; + struct crypto_aead *aead; + struct sk_buff *segs = ERR_PTR(-EINVAL); + netdev_features_t esp_features = features; + struct xfrm_offload *xo = xfrm_offload(skb); + + if (xo) + goto out; + + seq = xo->seq.low; + + x = skb->sp->xvec[skb->sp->len - 1]; + aead = x->data; + esph = ip_esp_hdr(skb); + + if (esph->spi != x->id.spi) + goto out; + + if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead))) + goto out; + + __skb_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead)); + + skb->encap_hdr_csum = 1; + + if (!(features & NETIF_F_HW_ESP)) + esp_features = features & ~(NETIF_F_SG | NETIF_F_CSUM_MASK); + + segs = x->outer_mode->gso_segment(x, skb, esp_features); + if (IS_ERR_OR_NULL(segs)) + goto out; + + __skb_pull(skb, skb->data - skb_mac_header(skb)); + + skb2 = segs; + do { + struct sk_buff *nskb = skb2->next; + + xo = xfrm_offload(skb2); + xo->flags |= XFRM_GSO_SEGMENT; + xo->seq.low = seq; + xo->seq.hi = xfrm_replay_seqhi(x, seq); + + if(!(features & NETIF_F_HW_ESP)) + xo->flags |= CRYPTO_FALLBACK; + + x->outer_mode->xmit(x, skb2); + + err = x->type_offload->xmit(x, skb2, esp_features); + if (err) { + kfree_skb_list(segs); + return ERR_PTR(err); + } + + if (!skb_is_gso(skb2)) + seq++; + else + seq += skb_shinfo(skb2)->gso_segs; + + skb_push(skb2, skb2->mac_len); + skb2 = nskb; + } while (skb2); + +out: + return segs; +} + static int esp6_input_tail(struct xfrm_state *x, struct sk_buff *skb) { struct crypto_aead *aead = x->data; @@ -176,6 +267,7 @@ static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features static const struct net_offload esp6_offload = { .callbacks = { .gro_receive = esp6_gro_receive, + .gso_segment = esp6_gso_segment, }, }; @@ -185,6 +277,7 @@ static const struct xfrm_type_offload esp6_type_offload = { .proto = IPPROTO_ESP, .input_tail = esp6_input_tail, .xmit = esp6_xmit, + .encap = esp6_gso_encap, }; static int __init esp6_offload_init(void) diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c index cdc2e2e71bff..20e68a3cad9a 100644 --- a/net/xfrm/xfrm_replay.c +++ b/net/xfrm/xfrm_replay.c @@ -45,7 +45,8 @@ u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq) return seq_hi; } - +EXPORT_SYMBOL(xfrm_replay_seqhi); +; static void xfrm_replay_notify(struct xfrm_state *x, int event) { struct km_event c; -- cgit From d7dbefc45cf5517a0cf9a0391316fab0abe71bd2 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Apr 2017 10:07:01 +0200 Subject: xfrm: Add xfrm_replay_overflow functions for offloading This patch adds functions that handles IPsec sequence numbers for GSO segments and TSO offloading. We need to calculate and update the sequence numbers based on the segments that GSO/TSO will generate. We need this to keep software and hardware sequence number counter in sync. Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_replay.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 2 deletions(-) diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c index 20e68a3cad9a..8b23c5bcf8e8 100644 --- a/net/xfrm/xfrm_replay.c +++ b/net/xfrm/xfrm_replay.c @@ -559,6 +559,158 @@ static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq) x->repl->notify(x, XFRM_REPLAY_UPDATE); } +#ifdef CONFIG_XFRM_OFFLOAD +static int xfrm_replay_overflow_offload(struct xfrm_state *x, struct sk_buff *skb) +{ + int err = 0; + struct net *net = xs_net(x); + struct xfrm_offload *xo = xfrm_offload(skb); + __u32 oseq = x->replay.oseq; + + if (!xo) + return xfrm_replay_overflow(x, skb); + + if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { + if (!skb_is_gso(skb)) { + XFRM_SKB_CB(skb)->seq.output.low = ++oseq; + xo->seq.low = oseq; + } else { + XFRM_SKB_CB(skb)->seq.output.low = oseq + 1; + xo->seq.low = oseq + 1; + oseq += skb_shinfo(skb)->gso_segs; + } + + XFRM_SKB_CB(skb)->seq.output.hi = 0; + xo->seq.hi = 0; + if (unlikely(oseq < x->replay.oseq)) { + xfrm_audit_state_replay_overflow(x, skb); + err = -EOVERFLOW; + + return err; + } + + x->replay.oseq = oseq; + + if (xfrm_aevent_is_on(net)) + x->repl->notify(x, XFRM_REPLAY_UPDATE); + } + + return err; +} + +static int xfrm_replay_overflow_offload_bmp(struct xfrm_state *x, struct sk_buff *skb) +{ + int err = 0; + struct xfrm_offload *xo = xfrm_offload(skb); + struct xfrm_replay_state_esn *replay_esn = x->replay_esn; + struct net *net = xs_net(x); + __u32 oseq = replay_esn->oseq; + + if (!xo) + return xfrm_replay_overflow_bmp(x, skb); + + if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { + if (!skb_is_gso(skb)) { + XFRM_SKB_CB(skb)->seq.output.low = ++oseq; + xo->seq.low = oseq; + } else { + XFRM_SKB_CB(skb)->seq.output.low = oseq + 1; + xo->seq.low = oseq + 1; + oseq += skb_shinfo(skb)->gso_segs; + } + + XFRM_SKB_CB(skb)->seq.output.hi = 0; + xo->seq.hi = 0; + if (unlikely(oseq < replay_esn->oseq)) { + xfrm_audit_state_replay_overflow(x, skb); + err = -EOVERFLOW; + + return err; + } else { + replay_esn->oseq = oseq; + } + + if (xfrm_aevent_is_on(net)) + x->repl->notify(x, XFRM_REPLAY_UPDATE); + } + + return err; +} + +static int xfrm_replay_overflow_offload_esn(struct xfrm_state *x, struct sk_buff *skb) +{ + int err = 0; + struct xfrm_offload *xo = xfrm_offload(skb); + struct xfrm_replay_state_esn *replay_esn = x->replay_esn; + struct net *net = xs_net(x); + __u32 oseq = replay_esn->oseq; + __u32 oseq_hi = replay_esn->oseq_hi; + + if (!xo) + return xfrm_replay_overflow_esn(x, skb); + + if (x->type->flags & XFRM_TYPE_REPLAY_PROT) { + if (!skb_is_gso(skb)) { + XFRM_SKB_CB(skb)->seq.output.low = ++oseq; + XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi; + xo->seq.low = oseq; + xo->seq.hi = oseq_hi; + } else { + XFRM_SKB_CB(skb)->seq.output.low = oseq + 1; + XFRM_SKB_CB(skb)->seq.output.hi = oseq_hi; + xo->seq.low = oseq = oseq + 1; + xo->seq.hi = oseq_hi; + oseq += skb_shinfo(skb)->gso_segs; + } + + if (unlikely(oseq < replay_esn->oseq)) { + XFRM_SKB_CB(skb)->seq.output.hi = ++oseq_hi; + xo->seq.hi = oseq_hi; + + if (replay_esn->oseq_hi == 0) { + replay_esn->oseq--; + replay_esn->oseq_hi--; + xfrm_audit_state_replay_overflow(x, skb); + err = -EOVERFLOW; + + return err; + } + } + + replay_esn->oseq = oseq; + replay_esn->oseq_hi = oseq_hi; + + if (xfrm_aevent_is_on(net)) + x->repl->notify(x, XFRM_REPLAY_UPDATE); + } + + return err; +} + +static const struct xfrm_replay xfrm_replay_legacy = { + .advance = xfrm_replay_advance, + .check = xfrm_replay_check, + .recheck = xfrm_replay_check, + .notify = xfrm_replay_notify, + .overflow = xfrm_replay_overflow_offload, +}; + +static const struct xfrm_replay xfrm_replay_bmp = { + .advance = xfrm_replay_advance_bmp, + .check = xfrm_replay_check_bmp, + .recheck = xfrm_replay_check_bmp, + .notify = xfrm_replay_notify_bmp, + .overflow = xfrm_replay_overflow_offload_bmp, +}; + +static const struct xfrm_replay xfrm_replay_esn = { + .advance = xfrm_replay_advance_esn, + .check = xfrm_replay_check_esn, + .recheck = xfrm_replay_recheck_esn, + .notify = xfrm_replay_notify_esn, + .overflow = xfrm_replay_overflow_offload_esn, +}; +#else static const struct xfrm_replay xfrm_replay_legacy = { .advance = xfrm_replay_advance, .check = xfrm_replay_check, @@ -582,6 +734,7 @@ static const struct xfrm_replay xfrm_replay_esn = { .notify = xfrm_replay_notify_esn, .overflow = xfrm_replay_overflow_esn, }; +#endif int xfrm_init_replay(struct xfrm_state *x) { @@ -596,10 +749,12 @@ int xfrm_init_replay(struct xfrm_state *x) if (replay_esn->replay_window == 0) return -EINVAL; x->repl = &xfrm_replay_esn; - } else + } else { x->repl = &xfrm_replay_bmp; - } else + } + } else { x->repl = &xfrm_replay_legacy; + } return 0; } -- cgit From b3859c8ebffe26ddf7aec0b23a83389d6f2419f9 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Apr 2017 10:07:19 +0200 Subject: esp: Use a synchronous crypto algorithm on offloading. We need a fallback algorithm for crypto offloading to a NIC. This is because packets can be rerouted to other NICs that don't support crypto offloading. The fallback is going to be implemented at layer2 where we know the final output device but can't handle asynchronous returns fron the crypto layer. Signed-off-by: Steffen Klassert --- net/ipv4/esp4.c | 12 ++++++++++-- net/ipv6/esp6.c | 12 ++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 4382f306935b..7e501adb5042 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -783,13 +783,17 @@ static int esp_init_aead(struct xfrm_state *x) char aead_name[CRYPTO_MAX_ALG_NAME]; struct crypto_aead *aead; int err; + u32 mask = 0; err = -ENAMETOOLONG; if (snprintf(aead_name, CRYPTO_MAX_ALG_NAME, "%s(%s)", x->geniv, x->aead->alg_name) >= CRYPTO_MAX_ALG_NAME) goto error; - aead = crypto_alloc_aead(aead_name, 0, 0); + if (x->xso.offload_handle) + mask |= CRYPTO_ALG_ASYNC; + + aead = crypto_alloc_aead(aead_name, 0, mask); err = PTR_ERR(aead); if (IS_ERR(aead)) goto error; @@ -819,6 +823,7 @@ static int esp_init_authenc(struct xfrm_state *x) char authenc_name[CRYPTO_MAX_ALG_NAME]; unsigned int keylen; int err; + u32 mask = 0; err = -EINVAL; if (!x->ealg) @@ -844,7 +849,10 @@ static int esp_init_authenc(struct xfrm_state *x) goto error; } - aead = crypto_alloc_aead(authenc_name, 0, 0); + if (x->xso.offload_handle) + mask |= CRYPTO_ALG_ASYNC; + + aead = crypto_alloc_aead(authenc_name, 0, mask); err = PTR_ERR(aead); if (IS_ERR(aead)) goto error; diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 82d3da81293e..8b55abf1c45b 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -704,13 +704,17 @@ static int esp_init_aead(struct xfrm_state *x) char aead_name[CRYPTO_MAX_ALG_NAME]; struct crypto_aead *aead; int err; + u32 mask = 0; err = -ENAMETOOLONG; if (snprintf(aead_name, CRYPTO_MAX_ALG_NAME, "%s(%s)", x->geniv, x->aead->alg_name) >= CRYPTO_MAX_ALG_NAME) goto error; - aead = crypto_alloc_aead(aead_name, 0, 0); + if (x->xso.offload_handle) + mask |= CRYPTO_ALG_ASYNC; + + aead = crypto_alloc_aead(aead_name, 0, mask); err = PTR_ERR(aead); if (IS_ERR(aead)) goto error; @@ -740,6 +744,7 @@ static int esp_init_authenc(struct xfrm_state *x) char authenc_name[CRYPTO_MAX_ALG_NAME]; unsigned int keylen; int err; + u32 mask = 0; err = -EINVAL; if (!x->ealg) @@ -765,7 +770,10 @@ static int esp_init_authenc(struct xfrm_state *x) goto error; } - aead = crypto_alloc_aead(authenc_name, 0, 0); + if (x->xso.offload_handle) + mask |= CRYPTO_ALG_ASYNC; + + aead = crypto_alloc_aead(authenc_name, 0, mask); err = PTR_ERR(aead); if (IS_ERR(aead)) goto error; -- cgit From f6e27114a60a0afdec40db1bf7f6da37b565745a Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Apr 2017 10:07:28 +0200 Subject: net: Add a xfrm validate function to validate_xmit_skb When we do IPsec offloading, we need a fallback for packets that were targeted to be IPsec offloaded but rerouted to a device that does not support IPsec offload. For that we add a function that checks the offloading features of the sending device and and flags the requirement of a fallback before it calls the IPsec output function. The IPsec output function adds the IPsec trailer and does encryption if needed. Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 6 ++++++ net/core/dev.c | 3 +++ net/xfrm/xfrm_device.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 17603bf190c1..6793a30c66b1 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1862,6 +1862,7 @@ static inline struct xfrm_offload *xfrm_offload(struct sk_buff *skb) #ifdef CONFIG_XFRM_OFFLOAD void __net_init xfrm_dev_init(void); +int validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features); int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo); bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x); @@ -1890,6 +1891,11 @@ static inline void __net_init xfrm_dev_init(void) { } +static inline int validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features) +{ + return 0; +} + static inline int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo) { return 0; diff --git a/net/core/dev.c b/net/core/dev.c index ef9fe60ee294..5f0a864623e8 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2972,6 +2972,9 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device __skb_linearize(skb)) goto out_kfree_skb; + if (validate_xmit_xfrm(skb, features)) + goto out_kfree_skb; + /* If packet is not checksummed and device does not * support checksumming for this protocol, complete * checksumming here. diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 9bac2ba9052c..8ec8a3fcf8d4 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -22,6 +22,35 @@ #include #include +int validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features) +{ + int err; + struct xfrm_state *x; + struct xfrm_offload *xo = xfrm_offload(skb); + + if (skb_is_gso(skb)) + return 0; + + if (xo) { + x = skb->sp->xvec[skb->sp->len - 1]; + if (xo->flags & XFRM_GRO || x->xso.flags & XFRM_OFFLOAD_INBOUND) + return 0; + + x->outer_mode->xmit(x, skb); + + err = x->type_offload->xmit(x, skb, features); + if (err) { + XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR); + return err; + } + + skb_push(skb, skb->data - skb_mac_header(skb)); + } + + return 0; +} +EXPORT_SYMBOL_GPL(validate_xmit_xfrm); + int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo) { -- cgit From f1bd7d659ef0ba0f18c6f6afe7bbbd2410acffa0 Mon Sep 17 00:00:00 2001 From: Ilan Tayari Date: Fri, 14 Apr 2017 10:07:39 +0200 Subject: xfrm: Add encapsulation header offsets while SKB is not encrypted Both esp4 and esp6 used to assume that the SKB payload is encrypted and therefore the inner_network and inner_transport offsets are not relevant. When doing crypto offload in the NIC, this is no longer the case and the NIC driver needs these offsets so it can do TX TCP checksum offloading. This patch sets the inner_network and inner_transport members of the SKB, as well as encapsulation, to reflect the actual positions of these headers, and removes them only once encryption is done on the payload. Signed-off-by: Ilan Tayari Signed-off-by: Steffen Klassert --- net/ipv4/xfrm4_mode_transport.c | 2 ++ net/ipv4/xfrm4_mode_tunnel.c | 3 +++ net/ipv6/xfrm6_mode_transport.c | 1 + net/ipv6/xfrm6_mode_tunnel.c | 3 +++ net/xfrm/xfrm_output.c | 2 ++ 5 files changed, 11 insertions(+) diff --git a/net/ipv4/xfrm4_mode_transport.c b/net/ipv4/xfrm4_mode_transport.c index 6c2411d09386..3d36644890bb 100644 --- a/net/ipv4/xfrm4_mode_transport.c +++ b/net/ipv4/xfrm4_mode_transport.c @@ -24,6 +24,8 @@ static int xfrm4_transport_output(struct xfrm_state *x, struct sk_buff *skb) struct iphdr *iph = ip_hdr(skb); int ihl = iph->ihl * 4; + skb_set_inner_transport_header(skb, skb_transport_offset(skb)); + skb_set_network_header(skb, -x->props.header_len); skb->mac_header = skb->network_header + offsetof(struct iphdr, protocol); diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c index d3f2434fa0b8..e6265e2c274e 100644 --- a/net/ipv4/xfrm4_mode_tunnel.c +++ b/net/ipv4/xfrm4_mode_tunnel.c @@ -33,6 +33,9 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) struct iphdr *top_iph; int flags; + skb_set_inner_network_header(skb, skb_network_offset(skb)); + skb_set_inner_transport_header(skb, skb_transport_offset(skb)); + skb_set_network_header(skb, -x->props.header_len); skb->mac_header = skb->network_header + offsetof(struct iphdr, protocol); diff --git a/net/ipv6/xfrm6_mode_transport.c b/net/ipv6/xfrm6_mode_transport.c index eb9b36b06c1d..7a92c0f31912 100644 --- a/net/ipv6/xfrm6_mode_transport.c +++ b/net/ipv6/xfrm6_mode_transport.c @@ -27,6 +27,7 @@ static int xfrm6_transport_output(struct xfrm_state *x, struct sk_buff *skb) int hdr_len; iph = ipv6_hdr(skb); + skb_set_inner_transport_header(skb, skb_transport_offset(skb)); hdr_len = x->type->hdr_offset(x, skb, &prevhdr); skb_set_mac_header(skb, (prevhdr - x->props.header_len) - skb->data); diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c index 19a60fc4c29b..02556e356f87 100644 --- a/net/ipv6/xfrm6_mode_tunnel.c +++ b/net/ipv6/xfrm6_mode_tunnel.c @@ -36,6 +36,9 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) struct ipv6hdr *top_iph; int dsfield; + skb_set_inner_network_header(skb, skb_network_offset(skb)); + skb_set_inner_transport_header(skb, skb_transport_offset(skb)); + skb_set_network_header(skb, -x->props.header_len); skb->mac_header = skb->network_header + offsetof(struct ipv6hdr, nexthdr); diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index a15088613a6c..8c0b6722aaa8 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -205,6 +205,7 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb) int err; secpath_reset(skb); + skb->encapsulation = 0; if (xfrm_dev_offload_ok(skb, x)) { struct sec_path *sp; @@ -218,6 +219,7 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb) if (skb->sp) secpath_put(skb->sp); skb->sp = sp; + skb->encapsulation = 1; sp->olen++; sp->xvec[skb->sp->len++] = x; -- cgit From bcd1f8a45e7d5804e4f7bd78a91348cfce3cb74a Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Fri, 14 Apr 2017 10:07:49 +0200 Subject: xfrm: Prepare the GRO codepath for hardware offloading. On IPsec hardware offloading, we already get a secpath with valid state attached when the packet enters the GRO handlers. So check for hardware offload and skip the state lookup in this case. Signed-off-by: Steffen Klassert --- net/ipv4/esp4_offload.c | 42 ++++++++++++++++++++++------------------- net/ipv6/esp6_offload.c | 42 ++++++++++++++++++++++------------------- net/xfrm/xfrm_input.c | 50 ++++++++++++++++++++++++------------------------- 3 files changed, 71 insertions(+), 63 deletions(-) diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c index 1e39564cb8b4..f3e33c26dc33 100644 --- a/net/ipv4/esp4_offload.c +++ b/net/ipv4/esp4_offload.c @@ -43,27 +43,31 @@ static struct sk_buff **esp4_gro_receive(struct sk_buff **head, if ((err = xfrm_parse_spi(skb, IPPROTO_ESP, &spi, &seq)) != 0) goto out; - err = secpath_set(skb); - if (err) - goto out; - - if (skb->sp->len == XFRM_MAX_DEPTH) - goto out; - - x = xfrm_state_lookup(dev_net(skb->dev), skb->mark, - (xfrm_address_t *)&ip_hdr(skb)->daddr, - spi, IPPROTO_ESP, AF_INET); - if (!x) - goto out; - - skb->sp->xvec[skb->sp->len++] = x; - skb->sp->olen++; - xo = xfrm_offload(skb); - if (!xo) { - xfrm_state_put(x); - goto out; + if (!xo || !(xo->flags & CRYPTO_DONE)) { + err = secpath_set(skb); + if (err) + goto out; + + if (skb->sp->len == XFRM_MAX_DEPTH) + goto out; + + x = xfrm_state_lookup(dev_net(skb->dev), skb->mark, + (xfrm_address_t *)&ip_hdr(skb)->daddr, + spi, IPPROTO_ESP, AF_INET); + if (!x) + goto out; + + skb->sp->xvec[skb->sp->len++] = x; + skb->sp->olen++; + + xo = xfrm_offload(skb); + if (!xo) { + xfrm_state_put(x); + goto out; + } } + xo->flags |= XFRM_GRO; XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL; diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c index 06e972135ab0..1cceeee7cc33 100644 --- a/net/ipv6/esp6_offload.c +++ b/net/ipv6/esp6_offload.c @@ -45,27 +45,31 @@ static struct sk_buff **esp6_gro_receive(struct sk_buff **head, if ((err = xfrm_parse_spi(skb, IPPROTO_ESP, &spi, &seq)) != 0) goto out; - err = secpath_set(skb); - if (err) - goto out; - - if (skb->sp->len == XFRM_MAX_DEPTH) - goto out; - - x = xfrm_state_lookup(dev_net(skb->dev), skb->mark, - (xfrm_address_t *)&ipv6_hdr(skb)->daddr, - spi, IPPROTO_ESP, AF_INET6); - if (!x) - goto out; - - skb->sp->xvec[skb->sp->len++] = x; - skb->sp->olen++; - xo = xfrm_offload(skb); - if (!xo) { - xfrm_state_put(x); - goto out; + if (!xo || !(xo->flags & CRYPTO_DONE)) { + err = secpath_set(skb); + if (err) + goto out; + + if (skb->sp->len == XFRM_MAX_DEPTH) + goto out; + + x = xfrm_state_lookup(dev_net(skb->dev), skb->mark, + (xfrm_address_t *)&ipv6_hdr(skb)->daddr, + spi, IPPROTO_ESP, AF_INET6); + if (!x) + goto out; + + skb->sp->xvec[skb->sp->len++] = x; + skb->sp->olen++; + + xo = xfrm_offload(skb); + if (!xo) { + xfrm_state_put(x); + goto out; + } } + xo->flags |= XFRM_GRO; XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL; diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 362d655eac27..21c6cc965402 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -223,38 +223,38 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) seq = XFRM_SKB_CB(skb)->seq.input.low; goto resume; } + /* encap_type < -1 indicates a GRO call. */ encap_type = 0; seq = XFRM_SPI_SKB_CB(skb)->seq; - goto lock; - } - - if (xo && (xo->flags & CRYPTO_DONE)) { - crypto_done = true; - x = xfrm_input_state(skb); - family = XFRM_SPI_SKB_CB(skb)->family; - if (!(xo->status & CRYPTO_SUCCESS)) { - if (xo->status & - (CRYPTO_TRANSPORT_AH_AUTH_FAILED | - CRYPTO_TRANSPORT_ESP_AUTH_FAILED | - CRYPTO_TUNNEL_AH_AUTH_FAILED | - CRYPTO_TUNNEL_ESP_AUTH_FAILED)) { - - xfrm_audit_state_icvfail(x, skb, - x->type->proto); - x->stats.integrity_failed++; - XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR); + if (xo && (xo->flags & CRYPTO_DONE)) { + crypto_done = true; + x = xfrm_input_state(skb); + family = XFRM_SPI_SKB_CB(skb)->family; + + if (!(xo->status & CRYPTO_SUCCESS)) { + if (xo->status & + (CRYPTO_TRANSPORT_AH_AUTH_FAILED | + CRYPTO_TRANSPORT_ESP_AUTH_FAILED | + CRYPTO_TUNNEL_AH_AUTH_FAILED | + CRYPTO_TUNNEL_ESP_AUTH_FAILED)) { + + xfrm_audit_state_icvfail(x, skb, + x->type->proto); + x->stats.integrity_failed++; + XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR); + goto drop; + } + + XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR); goto drop; } - XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR); - goto drop; - } - - if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) { - XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); - goto drop; + if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) { + XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); + goto drop; + } } goto lock; -- cgit From db268d4dfdb9025eaf98ec88c99b38fbba172c43 Mon Sep 17 00:00:00 2001 From: Aaron Conole Date: Mon, 10 Apr 2017 15:52:37 -0400 Subject: ipset: remove unused function __ip_set_get_netlink There are no in-tree callers. Signed-off-by: Aaron Conole Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_core.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index cb120c3c040e..2b87d9fd3f72 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -500,14 +500,6 @@ __ip_set_put(struct ip_set *set) /* set->ref can be swapped out by ip_set_swap, netlink events (like dump) need * a separate reference counter */ -static inline void -__ip_set_get_netlink(struct ip_set *set) -{ - write_lock_bh(&ip_set_ref_lock); - set->ref_netlink++; - write_unlock_bh(&ip_set_ref_lock); -} - static inline void __ip_set_put_netlink(struct ip_set *set) { -- cgit From 7025bac47fba90cb39a53c011ee78f7f66d72793 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Wed, 12 Apr 2017 18:33:03 +0800 Subject: netfilter: nf_nat: Fix return NF_DROP in nfnetlink_parse_nat_setup The __nf_nat_alloc_null_binding invokes nf_nat_setup_info which may return NF_DROP when memory is exhausted, so convert NF_DROP to -ENOMEM to make ctnetlink happy. Or ctnetlink_setup_nat treats it as a success when one error NF_DROP happens actully. Signed-off-by: Gao Feng Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_nat_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index fb0e65411785..5e35643da650 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -806,7 +806,7 @@ nfnetlink_parse_nat_setup(struct nf_conn *ct, /* No NAT information has been passed, allocate the null-binding */ if (attr == NULL) - return __nf_nat_alloc_null_binding(ct, manip); + return __nf_nat_alloc_null_binding(ct, manip) == NF_DROP ? -ENOMEM : 0; err = nfnetlink_parse_nat(attr, ct, &range, l3proto); if (err < 0) -- cgit From 6e354a5e565110c3a0eb7da3788340d4809a42b6 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Thu, 13 Apr 2017 09:35:49 +0800 Subject: netfilter: ecache: Refine the nf_ct_deliver_cached_events 1. Remove single !events condition check to deliver the missed event even though there is no new event happened. Consider this case: 1) nf_ct_deliver_cached_events is invoked at the first time, the event is failed to deliver, then the missed is set. 2) nf_ct_deliver_cached_events is invoked again, but there is no any new event happened. The missed event is lost really. It would try to send the missed event again after remove this check. And it is ok if there is no missed event because the latter check !((events | missed) & e->ctmask) could avoid it. 2. Correct the return value check of notify->fcn. When send the event successfully, it returns 0, not postive value. Signed-off-by: Gao Feng Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_ecache.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 22fc32143e9c..6161e92d2980 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -195,7 +195,7 @@ void nf_ct_deliver_cached_events(struct nf_conn *ct) events = xchg(&e->cache, 0); - if (!nf_ct_is_confirmed(ct) || nf_ct_is_dying(ct) || !events) + if (!nf_ct_is_confirmed(ct) || nf_ct_is_dying(ct)) goto out_unlock; /* We make a copy of the missed event cache without taking @@ -212,7 +212,7 @@ void nf_ct_deliver_cached_events(struct nf_conn *ct) ret = notify->fcn(events | missed, &item); - if (likely(ret >= 0 && !missed)) + if (likely(ret == 0 && !missed)) goto out_unlock; spin_lock_bh(&ct->lock); -- cgit From cc41c84b7e7f2d7f6698bccc84890943fd021265 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 14 Apr 2017 20:31:08 +0200 Subject: netfilter: kill the fake untracked conntrack objects resurrect an old patch from Pablo Neira to remove the untracked objects. Currently, there are four possible states of an skb wrt. conntrack. 1. No conntrack attached, ct is NULL. 2. Normal (kmem cache allocated) ct attached. 3. a template (kmalloc'd), not in any hash tables at any point in time 4. the 'untracked' conntrack, a percpu nf_conn object, tagged via IPS_UNTRACKED_BIT in ct->status. Untracked is supposed to be identical to case 1. It exists only so users can check -m conntrack --ctstate UNTRACKED vs. -m conntrack --ctstate INVALID e.g. attempts to set connmark on INVALID or UNTRACKED conntracks is supposed to be a no-op. Thus currently we need to check ct == NULL || nf_ct_is_untracked(ct) in a lot of places in order to avoid altering untracked objects. The other consequence of the percpu untracked object is that all -j NOTRACK (and, later, kfree_skb of such skbs) result in an atomic op (inc/dec the untracked conntracks refcount). This adds a new kernel-private ctinfo state, IP_CT_UNTRACKED, to make the distinction instead. The (few) places that care about packet invalid (ct is NULL) vs. packet untracked now need to test ct == NULL vs. ctinfo == IP_CT_UNTRACKED, but all other places can omit the nf_ct_is_untracked() check. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 6 +-- include/net/netfilter/nf_conntrack.h | 10 +---- include/uapi/linux/netfilter/nf_conntrack_common.h | 6 ++- net/ipv4/netfilter/nf_dup_ipv4.c | 3 +- net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 3 +- net/ipv6/netfilter/nf_dup_ipv6.c | 3 +- net/netfilter/nf_conntrack_core.c | 48 +++------------------- net/netfilter/nf_nat_core.c | 3 -- net/netfilter/nft_ct.c | 14 +++---- net/netfilter/xt_CT.c | 16 ++++---- net/netfilter/xt_conntrack.c | 11 +++-- net/netfilter/xt_state.c | 13 +++--- 12 files changed, 39 insertions(+), 97 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 8a4a57b887fb..9a75d9933e63 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1556,12 +1556,8 @@ static inline void ip_vs_notrack(struct sk_buff *skb) struct nf_conn *ct = nf_ct_get(skb, &ctinfo); if (!ct || !nf_ct_is_untracked(ct)) { - struct nf_conn *untracked; - nf_conntrack_put(&ct->ct_general); - untracked = nf_ct_untracked_get(); - nf_conntrack_get(&untracked->ct_general); - nf_ct_set(skb, untracked, IP_CT_NEW); + nf_ct_set(skb, NULL, IP_CT_UNTRACKED); } #endif } diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 19605878da47..012b99f563e5 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -243,14 +243,6 @@ extern s32 (*nf_ct_nat_offset)(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq); -/* Fake conntrack entry for untracked connections */ -DECLARE_PER_CPU_ALIGNED(struct nf_conn, nf_conntrack_untracked); -static inline struct nf_conn *nf_ct_untracked_get(void) -{ - return raw_cpu_ptr(&nf_conntrack_untracked); -} -void nf_ct_untracked_status_or(unsigned long bits); - /* Iterate over all conntracks: if iter returns true, it's deleted. */ void nf_ct_iterate_cleanup(struct net *net, int (*iter)(struct nf_conn *i, void *data), @@ -283,7 +275,7 @@ static inline int nf_ct_is_dying(const struct nf_conn *ct) static inline int nf_ct_is_untracked(const struct nf_conn *ct) { - return test_bit(IPS_UNTRACKED_BIT, &ct->status); + return false; } /* Packet is received from loopback */ diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h index 6a8e33dd4ecb..b4a0a1940118 100644 --- a/include/uapi/linux/netfilter/nf_conntrack_common.h +++ b/include/uapi/linux/netfilter/nf_conntrack_common.h @@ -28,12 +28,14 @@ enum ip_conntrack_info { /* only for userspace compatibility */ #ifndef __KERNEL__ IP_CT_NEW_REPLY = IP_CT_NUMBER, +#else + IP_CT_UNTRACKED = 7, #endif }; #define NF_CT_STATE_INVALID_BIT (1 << 0) #define NF_CT_STATE_BIT(ctinfo) (1 << ((ctinfo) % IP_CT_IS_REPLY + 1)) -#define NF_CT_STATE_UNTRACKED_BIT (1 << (IP_CT_NUMBER + 1)) +#define NF_CT_STATE_UNTRACKED_BIT (1 << (IP_CT_UNTRACKED + 1)) /* Bitset representing status of connection. */ enum ip_conntrack_status { @@ -94,7 +96,7 @@ enum ip_conntrack_status { IPS_TEMPLATE_BIT = 11, IPS_TEMPLATE = (1 << IPS_TEMPLATE_BIT), - /* Conntrack is a fake untracked entry */ + /* Conntrack is a fake untracked entry. Obsolete and not used anymore */ IPS_UNTRACKED_BIT = 12, IPS_UNTRACKED = (1 << IPS_UNTRACKED_BIT), diff --git a/net/ipv4/netfilter/nf_dup_ipv4.c b/net/ipv4/netfilter/nf_dup_ipv4.c index f0dbff05fc28..39895b9ddeb9 100644 --- a/net/ipv4/netfilter/nf_dup_ipv4.c +++ b/net/ipv4/netfilter/nf_dup_ipv4.c @@ -69,8 +69,7 @@ void nf_dup_ipv4(struct net *net, struct sk_buff *skb, unsigned int hooknum, #if IS_ENABLED(CONFIG_NF_CONNTRACK) /* Avoid counting cloned packets towards the original connection. */ nf_reset(skb); - nf_ct_set(skb, nf_ct_untracked_get(), IP_CT_NEW); - nf_conntrack_get(skb_nfct(skb)); + nf_ct_set(skb, NULL, IP_CT_UNTRACKED); #endif /* * If we are in PREROUTING/INPUT, decrease the TTL to mitigate potential diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index d2c2ccbfbe72..d5f028e33f65 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -221,8 +221,7 @@ icmpv6_error(struct net *net, struct nf_conn *tmpl, type = icmp6h->icmp6_type - 130; if (type >= 0 && type < sizeof(noct_valid_new) && noct_valid_new[type]) { - nf_ct_set(skb, nf_ct_untracked_get(), IP_CT_NEW); - nf_conntrack_get(skb_nfct(skb)); + nf_ct_set(skb, NULL, IP_CT_UNTRACKED); return NF_ACCEPT; } diff --git a/net/ipv6/netfilter/nf_dup_ipv6.c b/net/ipv6/netfilter/nf_dup_ipv6.c index 888ecd106e5f..4a7ddeddbaab 100644 --- a/net/ipv6/netfilter/nf_dup_ipv6.c +++ b/net/ipv6/netfilter/nf_dup_ipv6.c @@ -58,8 +58,7 @@ void nf_dup_ipv6(struct net *net, struct sk_buff *skb, unsigned int hooknum, #if IS_ENABLED(CONFIG_NF_CONNTRACK) nf_reset(skb); - nf_ct_set(skb, nf_ct_untracked_get(), IP_CT_NEW); - nf_conntrack_get(skb_nfct(skb)); + nf_ct_set(skb, NULL, IP_CT_UNTRACKED); #endif if (hooknum == NF_INET_PRE_ROUTING || hooknum == NF_INET_LOCAL_IN) { diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index bcf1d2a6539e..03150f60714d 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -180,14 +180,6 @@ EXPORT_SYMBOL_GPL(nf_conntrack_htable_size); unsigned int nf_conntrack_max __read_mostly; seqcount_t nf_conntrack_generation __read_mostly; - -/* nf_conn must be 8 bytes aligned, as the 3 LSB bits are used - * for the nfctinfo. We cheat by (ab)using the PER CPU cache line - * alignment to enforce this. - */ -DEFINE_PER_CPU_ALIGNED(struct nf_conn, nf_conntrack_untracked); -EXPORT_PER_CPU_SYMBOL(nf_conntrack_untracked); - static unsigned int nf_conntrack_hash_rnd __read_mostly; static u32 hash_conntrack_raw(const struct nf_conntrack_tuple *tuple, @@ -1314,9 +1306,10 @@ nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, int ret; tmpl = nf_ct_get(skb, &ctinfo); - if (tmpl) { + if (tmpl || ctinfo == IP_CT_UNTRACKED) { /* Previously seen (loopback or untracked)? Ignore. */ - if (!nf_ct_is_template(tmpl)) { + if ((tmpl && !nf_ct_is_template(tmpl)) || + ctinfo == IP_CT_UNTRACKED) { NF_CT_STAT_INC_ATOMIC(net, ignore); return NF_ACCEPT; } @@ -1629,18 +1622,6 @@ void nf_ct_free_hashtable(void *hash, unsigned int size) } EXPORT_SYMBOL_GPL(nf_ct_free_hashtable); -static int untrack_refs(void) -{ - int cnt = 0, cpu; - - for_each_possible_cpu(cpu) { - struct nf_conn *ct = &per_cpu(nf_conntrack_untracked, cpu); - - cnt += atomic_read(&ct->ct_general.use) - 1; - } - return cnt; -} - void nf_conntrack_cleanup_start(void) { conntrack_gc_work.exiting = true; @@ -1650,8 +1631,6 @@ void nf_conntrack_cleanup_start(void) void nf_conntrack_cleanup_end(void) { RCU_INIT_POINTER(nf_ct_destroy, NULL); - while (untrack_refs() > 0) - schedule(); cancel_delayed_work_sync(&conntrack_gc_work.dwork); nf_ct_free_hashtable(nf_conntrack_hash, nf_conntrack_htable_size); @@ -1825,20 +1804,11 @@ EXPORT_SYMBOL_GPL(nf_conntrack_set_hashsize); module_param_call(hashsize, nf_conntrack_set_hashsize, param_get_uint, &nf_conntrack_htable_size, 0600); -void nf_ct_untracked_status_or(unsigned long bits) -{ - int cpu; - - for_each_possible_cpu(cpu) - per_cpu(nf_conntrack_untracked, cpu).status |= bits; -} -EXPORT_SYMBOL_GPL(nf_ct_untracked_status_or); - int nf_conntrack_init_start(void) { int max_factor = 8; int ret = -ENOMEM; - int i, cpu; + int i; seqcount_init(&nf_conntrack_generation); @@ -1921,15 +1891,6 @@ int nf_conntrack_init_start(void) if (ret < 0) goto err_proto; - /* Set up fake conntrack: to never be deleted, not in any hashes */ - for_each_possible_cpu(cpu) { - struct nf_conn *ct = &per_cpu(nf_conntrack_untracked, cpu); - write_pnet(&ct->ct_net, &init_net); - atomic_set(&ct->ct_general.use, 1); - } - /* - and look it like as a confirmed connection */ - nf_ct_untracked_status_or(IPS_CONFIRMED | IPS_UNTRACKED); - conntrack_gc_work_init(&conntrack_gc_work); queue_delayed_work(system_long_wq, &conntrack_gc_work.dwork, HZ); @@ -1977,6 +1938,7 @@ int nf_conntrack_init_net(struct net *net) int ret = -ENOMEM; int cpu; + BUILD_BUG_ON(IP_CT_UNTRACKED == IP_CT_NUMBER); atomic_set(&net->ct.count, 0); net->ct.pcpu_lists = alloc_percpu(struct ct_pcpu); diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 5e35643da650..9cbf49f9c1b7 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -861,9 +861,6 @@ static int __init nf_nat_init(void) nf_ct_helper_expectfn_register(&follow_master_nat); - /* Initialize fake conntrack so that NAT will skip it */ - nf_ct_untracked_status_or(IPS_NAT_DONE_MASK); - BUG_ON(nfnetlink_parse_nat_setup_hook != NULL); RCU_INIT_POINTER(nfnetlink_parse_nat_setup_hook, nfnetlink_parse_nat_setup); diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 6e23dbbedd7f..6c6fd48b024c 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -72,12 +72,12 @@ static void nft_ct_get_eval(const struct nft_expr *expr, switch (priv->key) { case NFT_CT_STATE: - if (ct == NULL) - state = NF_CT_STATE_INVALID_BIT; - else if (nf_ct_is_untracked(ct)) + if (ct) + state = NF_CT_STATE_BIT(ctinfo); + else if (ctinfo == IP_CT_UNTRACKED) state = NF_CT_STATE_UNTRACKED_BIT; else - state = NF_CT_STATE_BIT(ctinfo); + state = NF_CT_STATE_INVALID_BIT; *dest = state; return; default: @@ -718,12 +718,10 @@ static void nft_notrack_eval(const struct nft_expr *expr, ct = nf_ct_get(pkt->skb, &ctinfo); /* Previously seen (loopback or untracked)? Ignore. */ - if (ct) + if (ct || ctinfo == IP_CT_UNTRACKED) return; - ct = nf_ct_untracked_get(); - atomic_inc(&ct->ct_general.use); - nf_ct_set(skb, ct, IP_CT_NEW); + nf_ct_set(skb, ct, IP_CT_UNTRACKED); } static struct nft_expr_type nft_notrack_type; diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index b008db0184b8..3cbe1bcf6a74 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -26,11 +26,12 @@ static inline int xt_ct_target(struct sk_buff *skb, struct nf_conn *ct) if (skb->_nfct != 0) return XT_CONTINUE; - /* special case the untracked ct : we want the percpu object */ - if (!ct) - ct = nf_ct_untracked_get(); - atomic_inc(&ct->ct_general.use); - nf_ct_set(skb, ct, IP_CT_NEW); + if (ct) { + atomic_inc(&ct->ct_general.use); + nf_ct_set(skb, ct, IP_CT_NEW); + } else { + nf_ct_set(skb, ct, IP_CT_UNTRACKED); + } return XT_CONTINUE; } @@ -335,7 +336,7 @@ static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par, struct nf_conn *ct = info->ct; struct nf_conn_help *help; - if (ct && !nf_ct_is_untracked(ct)) { + if (ct) { help = nfct_help(ct); if (help) module_put(help->helper->me); @@ -412,8 +413,7 @@ notrack_tg(struct sk_buff *skb, const struct xt_action_param *par) if (skb->_nfct != 0) return XT_CONTINUE; - nf_ct_set(skb, nf_ct_untracked_get(), IP_CT_NEW); - nf_conntrack_get(skb_nfct(skb)); + nf_ct_set(skb, NULL, IP_CT_UNTRACKED); return XT_CONTINUE; } diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index c0fb217bc649..39cf1d019240 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -172,12 +172,11 @@ conntrack_mt(const struct sk_buff *skb, struct xt_action_param *par, ct = nf_ct_get(skb, &ctinfo); - if (ct) { - if (nf_ct_is_untracked(ct)) - statebit = XT_CONNTRACK_STATE_UNTRACKED; - else - statebit = XT_CONNTRACK_STATE_BIT(ctinfo); - } else + if (ct) + statebit = XT_CONNTRACK_STATE_BIT(ctinfo); + else if (ctinfo == IP_CT_UNTRACKED) + statebit = XT_CONNTRACK_STATE_UNTRACKED; + else statebit = XT_CONNTRACK_STATE_INVALID; if (info->match_flags & XT_CONNTRACK_STATE) { diff --git a/net/netfilter/xt_state.c b/net/netfilter/xt_state.c index 5746a33789a5..5fbd79194d21 100644 --- a/net/netfilter/xt_state.c +++ b/net/netfilter/xt_state.c @@ -28,14 +28,13 @@ state_mt(const struct sk_buff *skb, struct xt_action_param *par) unsigned int statebit; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - if (!ct) + if (ct) + statebit = XT_STATE_BIT(ctinfo); + else if (ctinfo == IP_CT_UNTRACKED) + statebit = XT_STATE_UNTRACKED; + else statebit = XT_STATE_INVALID; - else { - if (nf_ct_is_untracked(ct)) - statebit = XT_STATE_UNTRACKED; - else - statebit = XT_STATE_BIT(ctinfo); - } + return (sinfo->statemask & statebit); } -- cgit From ab8bc7ed864b9c4f1fcb00a22bbe4e0f66ce8003 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 14 Apr 2017 20:31:09 +0200 Subject: netfilter: remove nf_ct_is_untracked This function is now obsolete and always returns false. This change has no effect on generated code. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 4 ++-- include/net/netfilter/nf_conntrack.h | 5 ----- include/net/netfilter/nf_conntrack_core.h | 2 +- net/ipv4/netfilter/nf_nat_l3proto_ipv4.c | 4 ---- net/ipv4/netfilter/nf_socket_ipv4.c | 2 +- net/ipv6/netfilter/nf_nat_l3proto_ipv6.c | 4 ---- net/netfilter/ipvs/ip_vs_ftp.c | 3 +-- net/netfilter/ipvs/ip_vs_nfct.c | 4 ++-- net/netfilter/ipvs/ip_vs_xmit.c | 8 ++++---- net/netfilter/nf_conntrack_netlink.c | 12 +----------- net/netfilter/xt_HMARK.c | 2 +- net/netfilter/xt_cluster.c | 3 --- net/netfilter/xt_connlabel.c | 2 +- net/netfilter/xt_connmark.c | 4 ++-- net/netfilter/xt_ipvs.c | 2 +- net/openvswitch/conntrack.c | 5 ----- 16 files changed, 17 insertions(+), 49 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 9a75d9933e63..632082300e77 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1555,7 +1555,7 @@ static inline void ip_vs_notrack(struct sk_buff *skb) enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - if (!ct || !nf_ct_is_untracked(ct)) { + if (ct) { nf_conntrack_put(&ct->ct_general); nf_ct_set(skb, NULL, IP_CT_UNTRACKED); } @@ -1616,7 +1616,7 @@ static inline bool ip_vs_conn_uses_conntrack(struct ip_vs_conn *cp, if (!(cp->flags & IP_VS_CONN_F_NFCT)) return false; ct = nf_ct_get(skb, &ctinfo); - if (ct && !nf_ct_is_untracked(ct)) + if (ct) return true; #endif return false; diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 012b99f563e5..4978a82b75fa 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -273,11 +273,6 @@ static inline int nf_ct_is_dying(const struct nf_conn *ct) return test_bit(IPS_DYING_BIT, &ct->status); } -static inline int nf_ct_is_untracked(const struct nf_conn *ct) -{ - return false; -} - /* Packet is received from loopback */ static inline bool nf_is_loopback_packet(const struct sk_buff *skb) { diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h index 84ec7ca5f195..81d7f8a30945 100644 --- a/include/net/netfilter/nf_conntrack_core.h +++ b/include/net/netfilter/nf_conntrack_core.h @@ -65,7 +65,7 @@ static inline int nf_conntrack_confirm(struct sk_buff *skb) struct nf_conn *ct = (struct nf_conn *)skb_nfct(skb); int ret = NF_ACCEPT; - if (ct && !nf_ct_is_untracked(ct)) { + if (ct) { if (!nf_ct_is_confirmed(ct)) ret = __nf_conntrack_confirm(skb); if (likely(ret == NF_ACCEPT)) diff --git a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c index 6f5e8d01b876..e3bfa6a169f0 100644 --- a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c @@ -264,10 +264,6 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb, if (!ct) return NF_ACCEPT; - /* Don't try to NAT if this packet is not conntracked */ - if (nf_ct_is_untracked(ct)) - return NF_ACCEPT; - nat = nf_ct_nat_ext_add(ct); if (nat == NULL) return NF_ACCEPT; diff --git a/net/ipv4/netfilter/nf_socket_ipv4.c b/net/ipv4/netfilter/nf_socket_ipv4.c index a83d558e1aae..e9293bdebba0 100644 --- a/net/ipv4/netfilter/nf_socket_ipv4.c +++ b/net/ipv4/netfilter/nf_socket_ipv4.c @@ -139,7 +139,7 @@ struct sock *nf_sk_lookup_slow_v4(struct net *net, const struct sk_buff *skb, * SNAT-ted connection. */ ct = nf_ct_get(skb, &ctinfo); - if (ct && !nf_ct_is_untracked(ct) && + if (ct && ((iph->protocol != IPPROTO_ICMP && ctinfo == IP_CT_ESTABLISHED_REPLY) || (iph->protocol == IPPROTO_ICMP && diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c index e0be97e636a4..922b5aef273c 100644 --- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c @@ -273,10 +273,6 @@ nf_nat_ipv6_fn(void *priv, struct sk_buff *skb, if (!ct) return NF_ACCEPT; - /* Don't try to NAT if this packet is not conntracked */ - if (nf_ct_is_untracked(ct)) - return NF_ACCEPT; - nat = nf_ct_nat_ext_add(ct); if (nat == NULL) return NF_ACCEPT; diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index 1e589f8644ca..af3a9bbdf2ae 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -260,9 +260,8 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, buf_len = strlen(buf); ct = nf_ct_get(skb, &ctinfo); - if (ct && !nf_ct_is_untracked(ct) && (ct->status & IPS_NAT_MASK)) { + if (ct && nfct_nat(ct)) { bool mangled; - /* If mangling fails this function will return 0 * which will cause the packet to be dropped. * Mangling can only fail under memory pressure, diff --git a/net/netfilter/ipvs/ip_vs_nfct.c b/net/netfilter/ipvs/ip_vs_nfct.c index fc230d99aa3b..6cf3fd81a5ec 100644 --- a/net/netfilter/ipvs/ip_vs_nfct.c +++ b/net/netfilter/ipvs/ip_vs_nfct.c @@ -85,7 +85,7 @@ ip_vs_update_conntrack(struct sk_buff *skb, struct ip_vs_conn *cp, int outin) struct nf_conn *ct = nf_ct_get(skb, &ctinfo); struct nf_conntrack_tuple new_tuple; - if (ct == NULL || nf_ct_is_confirmed(ct) || nf_ct_is_untracked(ct) || + if (ct == NULL || nf_ct_is_confirmed(ct) || nf_ct_is_dying(ct)) return; @@ -232,7 +232,7 @@ void ip_vs_nfct_expect_related(struct sk_buff *skb, struct nf_conn *ct, { struct nf_conntrack_expect *exp; - if (ct == NULL || nf_ct_is_untracked(ct)) + if (ct == NULL) return; exp = nf_ct_expect_alloc(ct); diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 4e1a98fcc8c3..2eab1e0400f4 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -775,7 +775,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - if (ct && !nf_ct_is_untracked(ct)) { + if (ct) { IP_VS_DBG_RL_PKT(10, AF_INET, pp, skb, ipvsh->off, "ip_vs_nat_xmit(): " "stopping DNAT to local address"); @@ -866,7 +866,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - if (ct && !nf_ct_is_untracked(ct)) { + if (ct) { IP_VS_DBG_RL_PKT(10, AF_INET6, pp, skb, ipvsh->off, "ip_vs_nat_xmit_v6(): " "stopping DNAT to local address"); @@ -1338,7 +1338,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - if (ct && !nf_ct_is_untracked(ct)) { + if (ct) { IP_VS_DBG(10, "%s(): " "stopping DNAT to local address %pI4\n", __func__, &cp->daddr.ip); @@ -1429,7 +1429,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - if (ct && !nf_ct_is_untracked(ct)) { + if (ct) { IP_VS_DBG(10, "%s(): " "stopping DNAT to local address %pI6\n", __func__, &cp->daddr.in6); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 773d2187a5ea..83a1190504b4 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -627,10 +627,6 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) unsigned int flags = 0, group; int err; - /* ignore our fake conntrack entry */ - if (nf_ct_is_untracked(ct)) - return 0; - if (events & (1 << IPCT_DESTROY)) { type = IPCTNL_MSG_CT_DELETE; group = NFNLGRP_CONNTRACK_DESTROY; @@ -2173,13 +2169,7 @@ ctnetlink_glue_build_size(const struct nf_conn *ct) static struct nf_conn *ctnetlink_glue_get_ct(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo) { - struct nf_conn *ct; - - ct = nf_ct_get(skb, ctinfo); - if (ct && nf_ct_is_untracked(ct)) - ct = NULL; - - return ct; + return nf_ct_get(skb, ctinfo); } static int __ctnetlink_glue_build(struct sk_buff *skb, struct nf_conn *ct) diff --git a/net/netfilter/xt_HMARK.c b/net/netfilter/xt_HMARK.c index 02afaf48a729..60e6dbe12460 100644 --- a/net/netfilter/xt_HMARK.c +++ b/net/netfilter/xt_HMARK.c @@ -84,7 +84,7 @@ hmark_ct_set_htuple(const struct sk_buff *skb, struct hmark_tuple *t, struct nf_conntrack_tuple *otuple; struct nf_conntrack_tuple *rtuple; - if (ct == NULL || nf_ct_is_untracked(ct)) + if (ct == NULL) return -1; otuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; diff --git a/net/netfilter/xt_cluster.c b/net/netfilter/xt_cluster.c index 9a9884a39c0e..57ef175dfbfa 100644 --- a/net/netfilter/xt_cluster.c +++ b/net/netfilter/xt_cluster.c @@ -121,9 +121,6 @@ xt_cluster_mt(const struct sk_buff *skb, struct xt_action_param *par) if (ct == NULL) return false; - if (nf_ct_is_untracked(ct)) - return false; - if (ct->master) hash = xt_cluster_hash(ct->master, info); else diff --git a/net/netfilter/xt_connlabel.c b/net/netfilter/xt_connlabel.c index 7827128d5a95..23372879e6e3 100644 --- a/net/netfilter/xt_connlabel.c +++ b/net/netfilter/xt_connlabel.c @@ -29,7 +29,7 @@ connlabel_mt(const struct sk_buff *skb, struct xt_action_param *par) bool invert = info->options & XT_CONNLABEL_OP_INVERT; ct = nf_ct_get(skb, &ctinfo); - if (ct == NULL || nf_ct_is_untracked(ct)) + if (ct == NULL) return invert; labels = nf_ct_labels_find(ct); diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index 9935d5029b0e..ec377cc6a369 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -44,7 +44,7 @@ connmark_tg(struct sk_buff *skb, const struct xt_action_param *par) u_int32_t newmark; ct = nf_ct_get(skb, &ctinfo); - if (ct == NULL || nf_ct_is_untracked(ct)) + if (ct == NULL) return XT_CONTINUE; switch (info->mode) { @@ -97,7 +97,7 @@ connmark_mt(const struct sk_buff *skb, struct xt_action_param *par) const struct nf_conn *ct; ct = nf_ct_get(skb, &ctinfo); - if (ct == NULL || nf_ct_is_untracked(ct)) + if (ct == NULL) return false; return ((ct->mark & info->mask) == info->mark) ^ info->invert; diff --git a/net/netfilter/xt_ipvs.c b/net/netfilter/xt_ipvs.c index 0fdc89064488..42540d26c2b8 100644 --- a/net/netfilter/xt_ipvs.c +++ b/net/netfilter/xt_ipvs.c @@ -116,7 +116,7 @@ ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par) enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); - if (ct == NULL || nf_ct_is_untracked(ct)) { + if (ct == NULL) { match = false; goto out_put_cp; } diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index 7b2c2fce408a..57c68664d09c 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -795,11 +795,6 @@ static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, enum nf_nat_manip_type maniptype; int err; - if (nf_ct_is_untracked(ct)) { - /* A NAT action may only be performed on tracked packets. */ - return NF_ACCEPT; - } - /* Add NAT extension if not confirmed yet. */ if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct)) return NF_ACCEPT; /* Can't NAT. */ -- cgit From 8b55d7581fc5dccc658b9a169879c46dda807d5e Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Thu, 6 Apr 2017 05:49:02 +0200 Subject: NFC: pn533: use constant off-stack buffer for sending acks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix for WARN: usb 3-2.4.1: NFC: Exchanging data failed (error 0x13) llcp: nfc_llcp_recv: err -5 llcp: nfc_llcp_symm_timer: SYMM timeout ------------[ cut here ]------------ WARNING: CPU: 1 PID: 26397 at .../drivers/usb/core/hcd.c:1584 usb_hcd_map_urb_for_dma+0x370/0x550 transfer buffer not dma capable [...] Workqueue: events nfc_llcp_timeout_work [nfc] Call Trace: ? dump_stack+0x46/0x5a ? __warn+0xb9/0xe0 ? warn_slowpath_fmt+0x5a/0x80 ? usb_hcd_map_urb_for_dma+0x370/0x550 ? usb_hcd_submit_urb+0x2fb/0xa60 ? dequeue_entity+0x3f2/0xc30 ? pn533_usb_send_ack+0x5d/0x80 [pn533_usb] ? pn533_usb_abort_cmd+0x13/0x20 [pn533_usb] ? pn533_dep_link_down+0x32/0x70 [pn533] ? nfc_dep_link_down+0x87/0xd0 [nfc] [...] usb 3-2.4.1: NFC: Exchanging data failed (error 0x13) llcp: nfc_llcp_recv: err -5 llcp: nfc_llcp_symm_timer: SYMM timeout Signed-off-by: Michał Mirosław Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533/i2c.c | 2 +- drivers/nfc/pn533/usb.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/nfc/pn533/i2c.c b/drivers/nfc/pn533/i2c.c index 2c2fb9cfe10a..8f60ce039b0d 100644 --- a/drivers/nfc/pn533/i2c.c +++ b/drivers/nfc/pn533/i2c.c @@ -51,7 +51,7 @@ static int pn533_i2c_send_ack(struct pn533 *dev, gfp_t flags) { struct pn533_i2c_phy *phy = dev->phy; struct i2c_client *client = phy->i2c_dev; - u8 ack[6] = {0x00, 0x00, 0xff, 0x00, 0xff, 0x00}; + static const u8 ack[6] = {0x00, 0x00, 0xff, 0x00, 0xff, 0x00}; /* spec 6.2.1.3: Preamble, SoPC (2), ACK Code (2), Postamble */ int rc; diff --git a/drivers/nfc/pn533/usb.c b/drivers/nfc/pn533/usb.c index 000159ea9c5f..8ed203ea21ea 100644 --- a/drivers/nfc/pn533/usb.c +++ b/drivers/nfc/pn533/usb.c @@ -148,11 +148,11 @@ static int pn533_submit_urb_for_ack(struct pn533_usb_phy *phy, gfp_t flags) static int pn533_usb_send_ack(struct pn533 *dev, gfp_t flags) { struct pn533_usb_phy *phy = dev->phy; - u8 ack[6] = {0x00, 0x00, 0xff, 0x00, 0xff, 0x00}; + static const u8 ack[6] = {0x00, 0x00, 0xff, 0x00, 0xff, 0x00}; /* spec 7.1.1.3: Preamble, SoPC (2), ACK Code (2), Postamble */ int rc; - phy->out_urb->transfer_buffer = ack; + phy->out_urb->transfer_buffer = (u8 *)ack; phy->out_urb->transfer_buffer_length = sizeof(ack); rc = usb_submit_urb(phy->out_urb, flags); -- cgit From 4ea206395d3aede32bab94a75ec573530486fa44 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 17 Apr 2017 00:42:22 +0200 Subject: nfc: fix get_unaligned_...() misuses * if a local variable of type uint16_t is unaligned, your compiler is FUBAR * the whole point of get_unaligned_... is to avoid memcpy + ..._to_cpu(). Using it *after* memcpy() (into aligned object, no less) is pointless. Signed-off-by: Al Viro Signed-off-by: Samuel Ortiz --- drivers/nfc/nfcmrvl/fw_dnld.c | 5 ++--- drivers/nfc/nxp-nci/i2c.c | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/nfc/nfcmrvl/fw_dnld.c b/drivers/nfc/nfcmrvl/fw_dnld.c index 441c1b0ec7b5..c38bdd6a5a82 100644 --- a/drivers/nfc/nfcmrvl/fw_dnld.c +++ b/drivers/nfc/nfcmrvl/fw_dnld.c @@ -281,12 +281,11 @@ static int process_state_fw_dnld(struct nfcmrvl_private *priv, return -EINVAL; } skb_pull(skb, 1); - memcpy(&len, skb->data, 2); + len = get_unaligned_le16(skb->data); skb_pull(skb, 2); + comp_len = get_unaligned_le16(skb->data); memcpy(&comp_len, skb->data, 2); skb_pull(skb, 2); - len = get_unaligned_le16(&len); - comp_len = get_unaligned_le16(&comp_len); if (((~len) & 0xFFFF) != comp_len) { nfc_err(priv->dev, "bad len complement: %x %x %x", len, comp_len, (~len & 0xFFFF)); diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index c6a04a950225..ff22d761183c 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -126,7 +126,7 @@ static int nxp_nci_i2c_fw_read(struct nxp_nci_i2c_phy *phy, goto fw_read_exit; } - frame_len = (get_unaligned_be16(&header) & NXP_NCI_FW_FRAME_LEN_MASK) + + frame_len = (be16_to_cpu(header) & NXP_NCI_FW_FRAME_LEN_MASK) + NXP_NCI_FW_CRC_LEN; *skb = alloc_skb(NXP_NCI_FW_HDR_LEN + frame_len, GFP_KERNEL); -- cgit From a6a71f19fe5e05a90e0bd2487b87aba60a7bfbe0 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Wed, 12 Apr 2017 12:45:03 -0400 Subject: net: dsa: isolate legacy code This patch moves as is the legacy DSA code from dsa.c to legacy.c, except the few shared symbols which remain in dsa.c. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 4 +- net/dsa/Makefile | 2 +- net/dsa/dsa.c | 768 +------------------------------------------------ net/dsa/dsa_priv.h | 4 + net/dsa/legacy.c | 818 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 828 insertions(+), 768 deletions(-) create mode 100644 net/dsa/legacy.c diff --git a/include/net/dsa.h b/include/net/dsa.h index 9b1c1eb4147a..04c3fe93f803 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -302,7 +302,7 @@ struct dsa_notifier_bridge_info { struct dsa_switch_ops { /* - * Probing and setup. + * Legacy probing. */ const char *(*probe)(struct device *dsa_dev, struct device *host_dev, int sw_addr, @@ -472,9 +472,11 @@ struct dsa_switch_driver { const struct dsa_switch_ops *ops; }; +/* Legacy driver registration */ void register_switch_driver(struct dsa_switch_driver *type); void unregister_switch_driver(struct dsa_switch_driver *type); struct mii_bus *dsa_host_dev_to_mii_bus(struct device *dev); + struct net_device *dsa_dev_to_net_device(struct device *dev); static inline bool dsa_uses_tagged_protocol(struct dsa_switch_tree *dst) diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 9b1d478f3713..11a082d7e103 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -1,6 +1,6 @@ # the core obj-$(CONFIG_NET_DSA) += dsa_core.o -dsa_core-y += dsa.o slave.o dsa2.o switch.o +dsa_core-y += dsa.o slave.o dsa2.o switch.o legacy.o # tagging formats dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 1fb9cf7aaaf4..e117047174fc 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -61,59 +61,6 @@ const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = { [DSA_TAG_PROTO_NONE] = &none_ops, }; -/* switch driver registration ***********************************************/ -static DEFINE_MUTEX(dsa_switch_drivers_mutex); -static LIST_HEAD(dsa_switch_drivers); - -void register_switch_driver(struct dsa_switch_driver *drv) -{ - mutex_lock(&dsa_switch_drivers_mutex); - list_add_tail(&drv->list, &dsa_switch_drivers); - mutex_unlock(&dsa_switch_drivers_mutex); -} -EXPORT_SYMBOL_GPL(register_switch_driver); - -void unregister_switch_driver(struct dsa_switch_driver *drv) -{ - mutex_lock(&dsa_switch_drivers_mutex); - list_del_init(&drv->list); - mutex_unlock(&dsa_switch_drivers_mutex); -} -EXPORT_SYMBOL_GPL(unregister_switch_driver); - -static const struct dsa_switch_ops * -dsa_switch_probe(struct device *parent, struct device *host_dev, int sw_addr, - const char **_name, void **priv) -{ - const struct dsa_switch_ops *ret; - struct list_head *list; - const char *name; - - ret = NULL; - name = NULL; - - mutex_lock(&dsa_switch_drivers_mutex); - list_for_each(list, &dsa_switch_drivers) { - const struct dsa_switch_ops *ops; - struct dsa_switch_driver *drv; - - drv = list_entry(list, struct dsa_switch_driver, list); - ops = drv->ops; - - name = ops->probe(parent, host_dev, sw_addr, priv); - if (name != NULL) { - ret = ops; - break; - } - } - mutex_unlock(&dsa_switch_drivers_mutex); - - *_name = name; - - return ret; -} - -/* basic switch operations **************************************************/ int dsa_cpu_dsa_setup(struct dsa_switch *ds, struct device *dev, struct dsa_port *dport, int port) { @@ -145,23 +92,6 @@ int dsa_cpu_dsa_setup(struct dsa_switch *ds, struct device *dev, return 0; } -static int dsa_cpu_dsa_setups(struct dsa_switch *ds, struct device *dev) -{ - struct dsa_port *dport; - int ret, port; - - for (port = 0; port < ds->num_ports; port++) { - if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) - continue; - - dport = &ds->ports[port]; - ret = dsa_cpu_dsa_setup(ds, dev, dport, port); - if (ret) - return ret; - } - return 0; -} - const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol) { const struct dsa_device_ops *ops; @@ -211,168 +141,6 @@ void dsa_cpu_port_ethtool_restore(struct dsa_switch *ds) master->ethtool_ops = ds->dst->master_orig_ethtool_ops; } -static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) -{ - const struct dsa_switch_ops *ops = ds->ops; - struct dsa_switch_tree *dst = ds->dst; - struct dsa_chip_data *cd = ds->cd; - bool valid_name_found = false; - int index = ds->index; - int i, ret; - - /* - * Validate supplied switch configuration. - */ - for (i = 0; i < ds->num_ports; i++) { - char *name; - - name = cd->port_names[i]; - if (name == NULL) - continue; - - if (!strcmp(name, "cpu")) { - if (dst->cpu_switch) { - netdev_err(dst->master_netdev, - "multiple cpu ports?!\n"); - return -EINVAL; - } - dst->cpu_switch = ds; - dst->cpu_port = i; - ds->cpu_port_mask |= 1 << i; - } else if (!strcmp(name, "dsa")) { - ds->dsa_port_mask |= 1 << i; - } else { - ds->enabled_port_mask |= 1 << i; - } - valid_name_found = true; - } - - if (!valid_name_found && i == ds->num_ports) - return -EINVAL; - - /* Make the built-in MII bus mask match the number of ports, - * switch drivers can override this later - */ - ds->phys_mii_mask = ds->enabled_port_mask; - - /* - * If the CPU connects to this switch, set the switch tree - * tagging protocol to the preferred tagging format of this - * switch. - */ - if (dst->cpu_switch == ds) { - enum dsa_tag_protocol tag_protocol; - - tag_protocol = ops->get_tag_protocol(ds); - dst->tag_ops = dsa_resolve_tag_protocol(tag_protocol); - if (IS_ERR(dst->tag_ops)) - return PTR_ERR(dst->tag_ops); - - dst->rcv = dst->tag_ops->rcv; - } - - memcpy(ds->rtable, cd->rtable, sizeof(ds->rtable)); - - /* - * Do basic register setup. - */ - ret = ops->setup(ds); - if (ret < 0) - return ret; - - ret = dsa_switch_register_notifier(ds); - if (ret) - return ret; - - if (ops->set_addr) { - ret = ops->set_addr(ds, dst->master_netdev->dev_addr); - if (ret < 0) - return ret; - } - - if (!ds->slave_mii_bus && ops->phy_read) { - ds->slave_mii_bus = devm_mdiobus_alloc(parent); - if (!ds->slave_mii_bus) - return -ENOMEM; - dsa_slave_mii_bus_init(ds); - - ret = mdiobus_register(ds->slave_mii_bus); - if (ret < 0) - return ret; - } - - /* - * Create network devices for physical switch ports. - */ - for (i = 0; i < ds->num_ports; i++) { - ds->ports[i].dn = cd->port_dn[i]; - - if (!(ds->enabled_port_mask & (1 << i))) - continue; - - ret = dsa_slave_create(ds, parent, i, cd->port_names[i]); - if (ret < 0) - netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s): %d\n", - index, i, cd->port_names[i], ret); - } - - /* Perform configuration of the CPU and DSA ports */ - ret = dsa_cpu_dsa_setups(ds, parent); - if (ret < 0) - netdev_err(dst->master_netdev, "[%d] : can't configure CPU and DSA ports\n", - index); - - ret = dsa_cpu_port_ethtool_setup(ds); - if (ret) - return ret; - - return 0; -} - -static struct dsa_switch * -dsa_switch_setup(struct dsa_switch_tree *dst, int index, - struct device *parent, struct device *host_dev) -{ - struct dsa_chip_data *cd = dst->pd->chip + index; - const struct dsa_switch_ops *ops; - struct dsa_switch *ds; - int ret; - const char *name; - void *priv; - - /* - * Probe for switch model. - */ - ops = dsa_switch_probe(parent, host_dev, cd->sw_addr, &name, &priv); - if (!ops) { - netdev_err(dst->master_netdev, "[%d]: could not detect attached switch\n", - index); - return ERR_PTR(-EINVAL); - } - netdev_info(dst->master_netdev, "[%d]: detected a %s switch\n", - index, name); - - - /* - * Allocate and initialise switch state. - */ - ds = dsa_switch_alloc(parent, DSA_MAX_PORTS); - if (!ds) - return ERR_PTR(-ENOMEM); - - ds->dst = dst; - ds->index = index; - ds->cd = cd; - ds->ops = ops; - ds->priv = priv; - - ret = dsa_switch_setup_one(ds, parent); - if (ret) - return ERR_PTR(ret); - - return ds; -} - void dsa_cpu_dsa_destroy(struct dsa_port *port) { struct device_node *port_dn = port->dn; @@ -381,86 +149,6 @@ void dsa_cpu_dsa_destroy(struct dsa_port *port) of_phy_deregister_fixed_link(port_dn); } -static void dsa_switch_destroy(struct dsa_switch *ds) -{ - int port; - - /* Destroy network devices for physical switch ports. */ - for (port = 0; port < ds->num_ports; port++) { - if (!(ds->enabled_port_mask & (1 << port))) - continue; - - if (!ds->ports[port].netdev) - continue; - - dsa_slave_destroy(ds->ports[port].netdev); - } - - /* Disable configuration of the CPU and DSA ports */ - for (port = 0; port < ds->num_ports; port++) { - if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) - continue; - dsa_cpu_dsa_destroy(&ds->ports[port]); - - /* Clearing a bit which is not set does no harm */ - ds->cpu_port_mask |= ~(1 << port); - ds->dsa_port_mask |= ~(1 << port); - } - - if (ds->slave_mii_bus && ds->ops->phy_read) - mdiobus_unregister(ds->slave_mii_bus); - - dsa_switch_unregister_notifier(ds); -} - -#ifdef CONFIG_PM_SLEEP -int dsa_switch_suspend(struct dsa_switch *ds) -{ - int i, ret = 0; - - /* Suspend slave network devices */ - for (i = 0; i < ds->num_ports; i++) { - if (!dsa_is_port_initialized(ds, i)) - continue; - - ret = dsa_slave_suspend(ds->ports[i].netdev); - if (ret) - return ret; - } - - if (ds->ops->suspend) - ret = ds->ops->suspend(ds); - - return ret; -} -EXPORT_SYMBOL_GPL(dsa_switch_suspend); - -int dsa_switch_resume(struct dsa_switch *ds) -{ - int i, ret = 0; - - if (ds->ops->resume) - ret = ds->ops->resume(ds); - - if (ret) - return ret; - - /* Resume slave network devices */ - for (i = 0; i < ds->num_ports; i++) { - if (!dsa_is_port_initialized(ds, i)) - continue; - - ret = dsa_slave_resume(ds->ports[i].netdev); - if (ret) - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(dsa_switch_resume); -#endif - -/* platform driver init and cleanup *****************************************/ static int dev_is_class(struct device *dev, void *class) { if (dev->class != NULL && !strcmp(dev->class->name, class)) @@ -479,24 +167,6 @@ static struct device *dev_find_class(struct device *parent, char *class) return device_find_child(parent, class, dev_is_class); } -struct mii_bus *dsa_host_dev_to_mii_bus(struct device *dev) -{ - struct device *d; - - d = dev_find_class(dev, "mdio_bus"); - if (d != NULL) { - struct mii_bus *bus; - - bus = to_mii_bus(d); - put_device(d); - - return bus; - } - - return NULL; -} -EXPORT_SYMBOL_GPL(dsa_host_dev_to_mii_bus); - struct net_device *dsa_dev_to_net_device(struct device *dev) { struct device *d; @@ -516,387 +186,6 @@ struct net_device *dsa_dev_to_net_device(struct device *dev) } EXPORT_SYMBOL_GPL(dsa_dev_to_net_device); -#ifdef CONFIG_OF -static int dsa_of_setup_routing_table(struct dsa_platform_data *pd, - struct dsa_chip_data *cd, - int chip_index, int port_index, - struct device_node *link) -{ - const __be32 *reg; - int link_sw_addr; - struct device_node *parent_sw; - int len; - - parent_sw = of_get_parent(link); - if (!parent_sw) - return -EINVAL; - - reg = of_get_property(parent_sw, "reg", &len); - if (!reg || (len != sizeof(*reg) * 2)) - return -EINVAL; - - /* - * Get the destination switch number from the second field of its 'reg' - * property, i.e. for "reg = <0x19 1>" sw_addr is '1'. - */ - link_sw_addr = be32_to_cpup(reg + 1); - - if (link_sw_addr >= pd->nr_chips) - return -EINVAL; - - cd->rtable[link_sw_addr] = port_index; - - return 0; -} - -static int dsa_of_probe_links(struct dsa_platform_data *pd, - struct dsa_chip_data *cd, - int chip_index, int port_index, - struct device_node *port, - const char *port_name) -{ - struct device_node *link; - int link_index; - int ret; - - for (link_index = 0;; link_index++) { - link = of_parse_phandle(port, "link", link_index); - if (!link) - break; - - if (!strcmp(port_name, "dsa") && pd->nr_chips > 1) { - ret = dsa_of_setup_routing_table(pd, cd, chip_index, - port_index, link); - if (ret) - return ret; - } - } - return 0; -} - -static void dsa_of_free_platform_data(struct dsa_platform_data *pd) -{ - int i; - int port_index; - - for (i = 0; i < pd->nr_chips; i++) { - port_index = 0; - while (port_index < DSA_MAX_PORTS) { - kfree(pd->chip[i].port_names[port_index]); - port_index++; - } - - /* Drop our reference to the MDIO bus device */ - if (pd->chip[i].host_dev) - put_device(pd->chip[i].host_dev); - } - kfree(pd->chip); -} - -static int dsa_of_probe(struct device *dev) -{ - struct device_node *np = dev->of_node; - struct device_node *child, *mdio, *ethernet, *port; - struct mii_bus *mdio_bus, *mdio_bus_switch; - struct net_device *ethernet_dev; - struct dsa_platform_data *pd; - struct dsa_chip_data *cd; - const char *port_name; - int chip_index, port_index; - const unsigned int *sw_addr, *port_reg; - u32 eeprom_len; - int ret; - - mdio = of_parse_phandle(np, "dsa,mii-bus", 0); - if (!mdio) - return -EINVAL; - - mdio_bus = of_mdio_find_bus(mdio); - if (!mdio_bus) - return -EPROBE_DEFER; - - ethernet = of_parse_phandle(np, "dsa,ethernet", 0); - if (!ethernet) { - ret = -EINVAL; - goto out_put_mdio; - } - - ethernet_dev = of_find_net_device_by_node(ethernet); - if (!ethernet_dev) { - ret = -EPROBE_DEFER; - goto out_put_mdio; - } - - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) { - ret = -ENOMEM; - goto out_put_ethernet; - } - - dev->platform_data = pd; - pd->of_netdev = ethernet_dev; - pd->nr_chips = of_get_available_child_count(np); - if (pd->nr_chips > DSA_MAX_SWITCHES) - pd->nr_chips = DSA_MAX_SWITCHES; - - pd->chip = kcalloc(pd->nr_chips, sizeof(struct dsa_chip_data), - GFP_KERNEL); - if (!pd->chip) { - ret = -ENOMEM; - goto out_free; - } - - chip_index = -1; - for_each_available_child_of_node(np, child) { - int i; - - chip_index++; - cd = &pd->chip[chip_index]; - - cd->of_node = child; - - /* Initialize the routing table */ - for (i = 0; i < DSA_MAX_SWITCHES; ++i) - cd->rtable[i] = DSA_RTABLE_NONE; - - /* When assigning the host device, increment its refcount */ - cd->host_dev = get_device(&mdio_bus->dev); - - sw_addr = of_get_property(child, "reg", NULL); - if (!sw_addr) - continue; - - cd->sw_addr = be32_to_cpup(sw_addr); - if (cd->sw_addr >= PHY_MAX_ADDR) - continue; - - if (!of_property_read_u32(child, "eeprom-length", &eeprom_len)) - cd->eeprom_len = eeprom_len; - - mdio = of_parse_phandle(child, "mii-bus", 0); - if (mdio) { - mdio_bus_switch = of_mdio_find_bus(mdio); - if (!mdio_bus_switch) { - ret = -EPROBE_DEFER; - goto out_free_chip; - } - - /* Drop the mdio_bus device ref, replacing the host - * device with the mdio_bus_switch device, keeping - * the refcount from of_mdio_find_bus() above. - */ - put_device(cd->host_dev); - cd->host_dev = &mdio_bus_switch->dev; - } - - for_each_available_child_of_node(child, port) { - port_reg = of_get_property(port, "reg", NULL); - if (!port_reg) - continue; - - port_index = be32_to_cpup(port_reg); - if (port_index >= DSA_MAX_PORTS) - break; - - port_name = of_get_property(port, "label", NULL); - if (!port_name) - continue; - - cd->port_dn[port_index] = port; - - cd->port_names[port_index] = kstrdup(port_name, - GFP_KERNEL); - if (!cd->port_names[port_index]) { - ret = -ENOMEM; - goto out_free_chip; - } - - ret = dsa_of_probe_links(pd, cd, chip_index, - port_index, port, port_name); - if (ret) - goto out_free_chip; - - } - } - - /* The individual chips hold their own refcount on the mdio bus, - * so drop ours */ - put_device(&mdio_bus->dev); - - return 0; - -out_free_chip: - dsa_of_free_platform_data(pd); -out_free: - kfree(pd); - dev->platform_data = NULL; -out_put_ethernet: - put_device(ðernet_dev->dev); -out_put_mdio: - put_device(&mdio_bus->dev); - return ret; -} - -static void dsa_of_remove(struct device *dev) -{ - struct dsa_platform_data *pd = dev->platform_data; - - if (!dev->of_node) - return; - - dsa_of_free_platform_data(pd); - put_device(&pd->of_netdev->dev); - kfree(pd); -} -#else -static inline int dsa_of_probe(struct device *dev) -{ - return 0; -} - -static inline void dsa_of_remove(struct device *dev) -{ -} -#endif - -static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, - struct device *parent, struct dsa_platform_data *pd) -{ - int i; - unsigned configured = 0; - - dst->pd = pd; - dst->master_netdev = dev; - dst->cpu_port = -1; - - for (i = 0; i < pd->nr_chips; i++) { - struct dsa_switch *ds; - - ds = dsa_switch_setup(dst, i, parent, pd->chip[i].host_dev); - if (IS_ERR(ds)) { - netdev_err(dev, "[%d]: couldn't create dsa switch instance (error %ld)\n", - i, PTR_ERR(ds)); - continue; - } - - dst->ds[i] = ds; - - ++configured; - } - - /* - * If no switch was found, exit cleanly - */ - if (!configured) - return -EPROBE_DEFER; - - /* - * If we use a tagging format that doesn't have an ethertype - * field, make sure that all packets from this point on get - * sent to the tag format's receive function. - */ - wmb(); - dev->dsa_ptr = (void *)dst; - - return 0; -} - -static int dsa_probe(struct platform_device *pdev) -{ - struct dsa_platform_data *pd = pdev->dev.platform_data; - struct net_device *dev; - struct dsa_switch_tree *dst; - int ret; - - if (pdev->dev.of_node) { - ret = dsa_of_probe(&pdev->dev); - if (ret) - return ret; - - pd = pdev->dev.platform_data; - } - - if (pd == NULL || (pd->netdev == NULL && pd->of_netdev == NULL)) - return -EINVAL; - - if (pd->of_netdev) { - dev = pd->of_netdev; - dev_hold(dev); - } else { - dev = dsa_dev_to_net_device(pd->netdev); - } - if (dev == NULL) { - ret = -EPROBE_DEFER; - goto out; - } - - if (dev->dsa_ptr != NULL) { - dev_put(dev); - ret = -EEXIST; - goto out; - } - - dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL); - if (dst == NULL) { - dev_put(dev); - ret = -ENOMEM; - goto out; - } - - platform_set_drvdata(pdev, dst); - - ret = dsa_setup_dst(dst, dev, &pdev->dev, pd); - if (ret) { - dev_put(dev); - goto out; - } - - return 0; - -out: - dsa_of_remove(&pdev->dev); - - return ret; -} - -static void dsa_remove_dst(struct dsa_switch_tree *dst) -{ - int i; - - dst->master_netdev->dsa_ptr = NULL; - - /* If we used a tagging format that doesn't have an ethertype - * field, make sure that all packets from this point get sent - * without the tag and go through the regular receive path. - */ - wmb(); - - for (i = 0; i < dst->pd->nr_chips; i++) { - struct dsa_switch *ds = dst->ds[i]; - - if (ds) - dsa_switch_destroy(ds); - } - - dsa_cpu_port_ethtool_restore(dst->cpu_switch); - - dev_put(dst->master_netdev); -} - -static int dsa_remove(struct platform_device *pdev) -{ - struct dsa_switch_tree *dst = platform_get_drvdata(pdev); - - dsa_remove_dst(dst); - dsa_of_remove(&pdev->dev); - - return 0; -} - -static void dsa_shutdown(struct platform_device *pdev) -{ -} - static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { @@ -936,59 +225,6 @@ static struct packet_type dsa_pack_type __read_mostly = { .func = dsa_switch_rcv, }; -#ifdef CONFIG_PM_SLEEP -static int dsa_suspend(struct device *d) -{ - struct platform_device *pdev = to_platform_device(d); - struct dsa_switch_tree *dst = platform_get_drvdata(pdev); - int i, ret = 0; - - for (i = 0; i < dst->pd->nr_chips; i++) { - struct dsa_switch *ds = dst->ds[i]; - - if (ds != NULL) - ret = dsa_switch_suspend(ds); - } - - return ret; -} - -static int dsa_resume(struct device *d) -{ - struct platform_device *pdev = to_platform_device(d); - struct dsa_switch_tree *dst = platform_get_drvdata(pdev); - int i, ret = 0; - - for (i = 0; i < dst->pd->nr_chips; i++) { - struct dsa_switch *ds = dst->ds[i]; - - if (ds != NULL) - ret = dsa_switch_resume(ds); - } - - return ret; -} -#endif - -static SIMPLE_DEV_PM_OPS(dsa_pm_ops, dsa_suspend, dsa_resume); - -static const struct of_device_id dsa_of_match_table[] = { - { .compatible = "marvell,dsa", }, - {} -}; -MODULE_DEVICE_TABLE(of, dsa_of_match_table); - -static struct platform_driver dsa_driver = { - .probe = dsa_probe, - .remove = dsa_remove, - .shutdown = dsa_shutdown, - .driver = { - .name = "dsa", - .of_match_table = dsa_of_match_table, - .pm = &dsa_pm_ops, - }, -}; - static int __init dsa_init_module(void) { int rc; @@ -997,7 +233,7 @@ static int __init dsa_init_module(void) if (rc) return rc; - rc = platform_driver_register(&dsa_driver); + rc = dsa_legacy_register(); if (rc) return rc; @@ -1011,7 +247,7 @@ static void __exit dsa_cleanup_module(void) { dsa_slave_unregister_notifier(); dev_remove_pack(&dsa_pack_type); - platform_driver_unregister(&dsa_driver); + dsa_legacy_unregister(); } module_exit(dsa_cleanup_module); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 107138a55bd8..ab397c07880f 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -55,6 +55,10 @@ const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol); int dsa_cpu_port_ethtool_setup(struct dsa_switch *ds); void dsa_cpu_port_ethtool_restore(struct dsa_switch *ds); +/* legacy.c */ +int dsa_legacy_register(void); +void dsa_legacy_unregister(void); + /* slave.c */ extern const struct dsa_device_ops notag_netdev_ops; void dsa_slave_mii_bus_init(struct dsa_switch *ds); diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c new file mode 100644 index 000000000000..ad345c8b0b06 --- /dev/null +++ b/net/dsa/legacy.c @@ -0,0 +1,818 @@ +/* + * net/dsa/legacy.c - Hardware switch handling + * Copyright (c) 2008-2009 Marvell Semiconductor + * Copyright (c) 2013 Florian Fainelli + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dsa_priv.h" + +/* switch driver registration ***********************************************/ +static DEFINE_MUTEX(dsa_switch_drivers_mutex); +static LIST_HEAD(dsa_switch_drivers); + +void register_switch_driver(struct dsa_switch_driver *drv) +{ + mutex_lock(&dsa_switch_drivers_mutex); + list_add_tail(&drv->list, &dsa_switch_drivers); + mutex_unlock(&dsa_switch_drivers_mutex); +} +EXPORT_SYMBOL_GPL(register_switch_driver); + +void unregister_switch_driver(struct dsa_switch_driver *drv) +{ + mutex_lock(&dsa_switch_drivers_mutex); + list_del_init(&drv->list); + mutex_unlock(&dsa_switch_drivers_mutex); +} +EXPORT_SYMBOL_GPL(unregister_switch_driver); + +static const struct dsa_switch_ops * +dsa_switch_probe(struct device *parent, struct device *host_dev, int sw_addr, + const char **_name, void **priv) +{ + const struct dsa_switch_ops *ret; + struct list_head *list; + const char *name; + + ret = NULL; + name = NULL; + + mutex_lock(&dsa_switch_drivers_mutex); + list_for_each(list, &dsa_switch_drivers) { + const struct dsa_switch_ops *ops; + struct dsa_switch_driver *drv; + + drv = list_entry(list, struct dsa_switch_driver, list); + ops = drv->ops; + + name = ops->probe(parent, host_dev, sw_addr, priv); + if (name != NULL) { + ret = ops; + break; + } + } + mutex_unlock(&dsa_switch_drivers_mutex); + + *_name = name; + + return ret; +} + +/* basic switch operations **************************************************/ +static int dsa_cpu_dsa_setups(struct dsa_switch *ds, struct device *dev) +{ + struct dsa_port *dport; + int ret, port; + + for (port = 0; port < ds->num_ports; port++) { + if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) + continue; + + dport = &ds->ports[port]; + ret = dsa_cpu_dsa_setup(ds, dev, dport, port); + if (ret) + return ret; + } + return 0; +} + +static int dsa_switch_setup_one(struct dsa_switch *ds, struct device *parent) +{ + const struct dsa_switch_ops *ops = ds->ops; + struct dsa_switch_tree *dst = ds->dst; + struct dsa_chip_data *cd = ds->cd; + bool valid_name_found = false; + int index = ds->index; + int i, ret; + + /* + * Validate supplied switch configuration. + */ + for (i = 0; i < ds->num_ports; i++) { + char *name; + + name = cd->port_names[i]; + if (name == NULL) + continue; + + if (!strcmp(name, "cpu")) { + if (dst->cpu_switch) { + netdev_err(dst->master_netdev, + "multiple cpu ports?!\n"); + return -EINVAL; + } + dst->cpu_switch = ds; + dst->cpu_port = i; + ds->cpu_port_mask |= 1 << i; + } else if (!strcmp(name, "dsa")) { + ds->dsa_port_mask |= 1 << i; + } else { + ds->enabled_port_mask |= 1 << i; + } + valid_name_found = true; + } + + if (!valid_name_found && i == ds->num_ports) + return -EINVAL; + + /* Make the built-in MII bus mask match the number of ports, + * switch drivers can override this later + */ + ds->phys_mii_mask = ds->enabled_port_mask; + + /* + * If the CPU connects to this switch, set the switch tree + * tagging protocol to the preferred tagging format of this + * switch. + */ + if (dst->cpu_switch == ds) { + enum dsa_tag_protocol tag_protocol; + + tag_protocol = ops->get_tag_protocol(ds); + dst->tag_ops = dsa_resolve_tag_protocol(tag_protocol); + if (IS_ERR(dst->tag_ops)) + return PTR_ERR(dst->tag_ops); + + dst->rcv = dst->tag_ops->rcv; + } + + memcpy(ds->rtable, cd->rtable, sizeof(ds->rtable)); + + /* + * Do basic register setup. + */ + ret = ops->setup(ds); + if (ret < 0) + return ret; + + ret = dsa_switch_register_notifier(ds); + if (ret) + return ret; + + if (ops->set_addr) { + ret = ops->set_addr(ds, dst->master_netdev->dev_addr); + if (ret < 0) + return ret; + } + + if (!ds->slave_mii_bus && ops->phy_read) { + ds->slave_mii_bus = devm_mdiobus_alloc(parent); + if (!ds->slave_mii_bus) + return -ENOMEM; + dsa_slave_mii_bus_init(ds); + + ret = mdiobus_register(ds->slave_mii_bus); + if (ret < 0) + return ret; + } + + /* + * Create network devices for physical switch ports. + */ + for (i = 0; i < ds->num_ports; i++) { + ds->ports[i].dn = cd->port_dn[i]; + + if (!(ds->enabled_port_mask & (1 << i))) + continue; + + ret = dsa_slave_create(ds, parent, i, cd->port_names[i]); + if (ret < 0) + netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s): %d\n", + index, i, cd->port_names[i], ret); + } + + /* Perform configuration of the CPU and DSA ports */ + ret = dsa_cpu_dsa_setups(ds, parent); + if (ret < 0) + netdev_err(dst->master_netdev, "[%d] : can't configure CPU and DSA ports\n", + index); + + ret = dsa_cpu_port_ethtool_setup(ds); + if (ret) + return ret; + + return 0; +} + +static struct dsa_switch * +dsa_switch_setup(struct dsa_switch_tree *dst, int index, + struct device *parent, struct device *host_dev) +{ + struct dsa_chip_data *cd = dst->pd->chip + index; + const struct dsa_switch_ops *ops; + struct dsa_switch *ds; + int ret; + const char *name; + void *priv; + + /* + * Probe for switch model. + */ + ops = dsa_switch_probe(parent, host_dev, cd->sw_addr, &name, &priv); + if (!ops) { + netdev_err(dst->master_netdev, "[%d]: could not detect attached switch\n", + index); + return ERR_PTR(-EINVAL); + } + netdev_info(dst->master_netdev, "[%d]: detected a %s switch\n", + index, name); + + + /* + * Allocate and initialise switch state. + */ + ds = dsa_switch_alloc(parent, DSA_MAX_PORTS); + if (!ds) + return ERR_PTR(-ENOMEM); + + ds->dst = dst; + ds->index = index; + ds->cd = cd; + ds->ops = ops; + ds->priv = priv; + + ret = dsa_switch_setup_one(ds, parent); + if (ret) + return ERR_PTR(ret); + + return ds; +} + +static void dsa_switch_destroy(struct dsa_switch *ds) +{ + int port; + + /* Destroy network devices for physical switch ports. */ + for (port = 0; port < ds->num_ports; port++) { + if (!(ds->enabled_port_mask & (1 << port))) + continue; + + if (!ds->ports[port].netdev) + continue; + + dsa_slave_destroy(ds->ports[port].netdev); + } + + /* Disable configuration of the CPU and DSA ports */ + for (port = 0; port < ds->num_ports; port++) { + if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) + continue; + dsa_cpu_dsa_destroy(&ds->ports[port]); + + /* Clearing a bit which is not set does no harm */ + ds->cpu_port_mask |= ~(1 << port); + ds->dsa_port_mask |= ~(1 << port); + } + + if (ds->slave_mii_bus && ds->ops->phy_read) + mdiobus_unregister(ds->slave_mii_bus); + + dsa_switch_unregister_notifier(ds); +} + +#ifdef CONFIG_PM_SLEEP +int dsa_switch_suspend(struct dsa_switch *ds) +{ + int i, ret = 0; + + /* Suspend slave network devices */ + for (i = 0; i < ds->num_ports; i++) { + if (!dsa_is_port_initialized(ds, i)) + continue; + + ret = dsa_slave_suspend(ds->ports[i].netdev); + if (ret) + return ret; + } + + if (ds->ops->suspend) + ret = ds->ops->suspend(ds); + + return ret; +} +EXPORT_SYMBOL_GPL(dsa_switch_suspend); + +int dsa_switch_resume(struct dsa_switch *ds) +{ + int i, ret = 0; + + if (ds->ops->resume) + ret = ds->ops->resume(ds); + + if (ret) + return ret; + + /* Resume slave network devices */ + for (i = 0; i < ds->num_ports; i++) { + if (!dsa_is_port_initialized(ds, i)) + continue; + + ret = dsa_slave_resume(ds->ports[i].netdev); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(dsa_switch_resume); +#endif + +/* platform driver init and cleanup *****************************************/ +static int dev_is_class(struct device *dev, void *class) +{ + if (dev->class != NULL && !strcmp(dev->class->name, class)) + return 1; + + return 0; +} + +static struct device *dev_find_class(struct device *parent, char *class) +{ + if (dev_is_class(parent, class)) { + get_device(parent); + return parent; + } + + return device_find_child(parent, class, dev_is_class); +} + +struct mii_bus *dsa_host_dev_to_mii_bus(struct device *dev) +{ + struct device *d; + + d = dev_find_class(dev, "mdio_bus"); + if (d != NULL) { + struct mii_bus *bus; + + bus = to_mii_bus(d); + put_device(d); + + return bus; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(dsa_host_dev_to_mii_bus); + +#ifdef CONFIG_OF +static int dsa_of_setup_routing_table(struct dsa_platform_data *pd, + struct dsa_chip_data *cd, + int chip_index, int port_index, + struct device_node *link) +{ + const __be32 *reg; + int link_sw_addr; + struct device_node *parent_sw; + int len; + + parent_sw = of_get_parent(link); + if (!parent_sw) + return -EINVAL; + + reg = of_get_property(parent_sw, "reg", &len); + if (!reg || (len != sizeof(*reg) * 2)) + return -EINVAL; + + /* + * Get the destination switch number from the second field of its 'reg' + * property, i.e. for "reg = <0x19 1>" sw_addr is '1'. + */ + link_sw_addr = be32_to_cpup(reg + 1); + + if (link_sw_addr >= pd->nr_chips) + return -EINVAL; + + cd->rtable[link_sw_addr] = port_index; + + return 0; +} + +static int dsa_of_probe_links(struct dsa_platform_data *pd, + struct dsa_chip_data *cd, + int chip_index, int port_index, + struct device_node *port, + const char *port_name) +{ + struct device_node *link; + int link_index; + int ret; + + for (link_index = 0;; link_index++) { + link = of_parse_phandle(port, "link", link_index); + if (!link) + break; + + if (!strcmp(port_name, "dsa") && pd->nr_chips > 1) { + ret = dsa_of_setup_routing_table(pd, cd, chip_index, + port_index, link); + if (ret) + return ret; + } + } + return 0; +} + +static void dsa_of_free_platform_data(struct dsa_platform_data *pd) +{ + int i; + int port_index; + + for (i = 0; i < pd->nr_chips; i++) { + port_index = 0; + while (port_index < DSA_MAX_PORTS) { + kfree(pd->chip[i].port_names[port_index]); + port_index++; + } + + /* Drop our reference to the MDIO bus device */ + if (pd->chip[i].host_dev) + put_device(pd->chip[i].host_dev); + } + kfree(pd->chip); +} + +static int dsa_of_probe(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct device_node *child, *mdio, *ethernet, *port; + struct mii_bus *mdio_bus, *mdio_bus_switch; + struct net_device *ethernet_dev; + struct dsa_platform_data *pd; + struct dsa_chip_data *cd; + const char *port_name; + int chip_index, port_index; + const unsigned int *sw_addr, *port_reg; + u32 eeprom_len; + int ret; + + mdio = of_parse_phandle(np, "dsa,mii-bus", 0); + if (!mdio) + return -EINVAL; + + mdio_bus = of_mdio_find_bus(mdio); + if (!mdio_bus) + return -EPROBE_DEFER; + + ethernet = of_parse_phandle(np, "dsa,ethernet", 0); + if (!ethernet) { + ret = -EINVAL; + goto out_put_mdio; + } + + ethernet_dev = of_find_net_device_by_node(ethernet); + if (!ethernet_dev) { + ret = -EPROBE_DEFER; + goto out_put_mdio; + } + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) { + ret = -ENOMEM; + goto out_put_ethernet; + } + + dev->platform_data = pd; + pd->of_netdev = ethernet_dev; + pd->nr_chips = of_get_available_child_count(np); + if (pd->nr_chips > DSA_MAX_SWITCHES) + pd->nr_chips = DSA_MAX_SWITCHES; + + pd->chip = kcalloc(pd->nr_chips, sizeof(struct dsa_chip_data), + GFP_KERNEL); + if (!pd->chip) { + ret = -ENOMEM; + goto out_free; + } + + chip_index = -1; + for_each_available_child_of_node(np, child) { + int i; + + chip_index++; + cd = &pd->chip[chip_index]; + + cd->of_node = child; + + /* Initialize the routing table */ + for (i = 0; i < DSA_MAX_SWITCHES; ++i) + cd->rtable[i] = DSA_RTABLE_NONE; + + /* When assigning the host device, increment its refcount */ + cd->host_dev = get_device(&mdio_bus->dev); + + sw_addr = of_get_property(child, "reg", NULL); + if (!sw_addr) + continue; + + cd->sw_addr = be32_to_cpup(sw_addr); + if (cd->sw_addr >= PHY_MAX_ADDR) + continue; + + if (!of_property_read_u32(child, "eeprom-length", &eeprom_len)) + cd->eeprom_len = eeprom_len; + + mdio = of_parse_phandle(child, "mii-bus", 0); + if (mdio) { + mdio_bus_switch = of_mdio_find_bus(mdio); + if (!mdio_bus_switch) { + ret = -EPROBE_DEFER; + goto out_free_chip; + } + + /* Drop the mdio_bus device ref, replacing the host + * device with the mdio_bus_switch device, keeping + * the refcount from of_mdio_find_bus() above. + */ + put_device(cd->host_dev); + cd->host_dev = &mdio_bus_switch->dev; + } + + for_each_available_child_of_node(child, port) { + port_reg = of_get_property(port, "reg", NULL); + if (!port_reg) + continue; + + port_index = be32_to_cpup(port_reg); + if (port_index >= DSA_MAX_PORTS) + break; + + port_name = of_get_property(port, "label", NULL); + if (!port_name) + continue; + + cd->port_dn[port_index] = port; + + cd->port_names[port_index] = kstrdup(port_name, + GFP_KERNEL); + if (!cd->port_names[port_index]) { + ret = -ENOMEM; + goto out_free_chip; + } + + ret = dsa_of_probe_links(pd, cd, chip_index, + port_index, port, port_name); + if (ret) + goto out_free_chip; + + } + } + + /* The individual chips hold their own refcount on the mdio bus, + * so drop ours */ + put_device(&mdio_bus->dev); + + return 0; + +out_free_chip: + dsa_of_free_platform_data(pd); +out_free: + kfree(pd); + dev->platform_data = NULL; +out_put_ethernet: + put_device(ðernet_dev->dev); +out_put_mdio: + put_device(&mdio_bus->dev); + return ret; +} + +static void dsa_of_remove(struct device *dev) +{ + struct dsa_platform_data *pd = dev->platform_data; + + if (!dev->of_node) + return; + + dsa_of_free_platform_data(pd); + put_device(&pd->of_netdev->dev); + kfree(pd); +} +#else +static inline int dsa_of_probe(struct device *dev) +{ + return 0; +} + +static inline void dsa_of_remove(struct device *dev) +{ +} +#endif + +static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, + struct device *parent, struct dsa_platform_data *pd) +{ + int i; + unsigned configured = 0; + + dst->pd = pd; + dst->master_netdev = dev; + dst->cpu_port = -1; + + for (i = 0; i < pd->nr_chips; i++) { + struct dsa_switch *ds; + + ds = dsa_switch_setup(dst, i, parent, pd->chip[i].host_dev); + if (IS_ERR(ds)) { + netdev_err(dev, "[%d]: couldn't create dsa switch instance (error %ld)\n", + i, PTR_ERR(ds)); + continue; + } + + dst->ds[i] = ds; + + ++configured; + } + + /* + * If no switch was found, exit cleanly + */ + if (!configured) + return -EPROBE_DEFER; + + /* + * If we use a tagging format that doesn't have an ethertype + * field, make sure that all packets from this point on get + * sent to the tag format's receive function. + */ + wmb(); + dev->dsa_ptr = (void *)dst; + + return 0; +} + +static int dsa_probe(struct platform_device *pdev) +{ + struct dsa_platform_data *pd = pdev->dev.platform_data; + struct net_device *dev; + struct dsa_switch_tree *dst; + int ret; + + if (pdev->dev.of_node) { + ret = dsa_of_probe(&pdev->dev); + if (ret) + return ret; + + pd = pdev->dev.platform_data; + } + + if (pd == NULL || (pd->netdev == NULL && pd->of_netdev == NULL)) + return -EINVAL; + + if (pd->of_netdev) { + dev = pd->of_netdev; + dev_hold(dev); + } else { + dev = dsa_dev_to_net_device(pd->netdev); + } + if (dev == NULL) { + ret = -EPROBE_DEFER; + goto out; + } + + if (dev->dsa_ptr != NULL) { + dev_put(dev); + ret = -EEXIST; + goto out; + } + + dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL); + if (dst == NULL) { + dev_put(dev); + ret = -ENOMEM; + goto out; + } + + platform_set_drvdata(pdev, dst); + + ret = dsa_setup_dst(dst, dev, &pdev->dev, pd); + if (ret) { + dev_put(dev); + goto out; + } + + return 0; + +out: + dsa_of_remove(&pdev->dev); + + return ret; +} + +static void dsa_remove_dst(struct dsa_switch_tree *dst) +{ + int i; + + dst->master_netdev->dsa_ptr = NULL; + + /* If we used a tagging format that doesn't have an ethertype + * field, make sure that all packets from this point get sent + * without the tag and go through the regular receive path. + */ + wmb(); + + for (i = 0; i < dst->pd->nr_chips; i++) { + struct dsa_switch *ds = dst->ds[i]; + + if (ds) + dsa_switch_destroy(ds); + } + + dsa_cpu_port_ethtool_restore(dst->cpu_switch); + + dev_put(dst->master_netdev); +} + +static int dsa_remove(struct platform_device *pdev) +{ + struct dsa_switch_tree *dst = platform_get_drvdata(pdev); + + dsa_remove_dst(dst); + dsa_of_remove(&pdev->dev); + + return 0; +} + +static void dsa_shutdown(struct platform_device *pdev) +{ +} + +#ifdef CONFIG_PM_SLEEP +static int dsa_suspend(struct device *d) +{ + struct platform_device *pdev = to_platform_device(d); + struct dsa_switch_tree *dst = platform_get_drvdata(pdev); + int i, ret = 0; + + for (i = 0; i < dst->pd->nr_chips; i++) { + struct dsa_switch *ds = dst->ds[i]; + + if (ds != NULL) + ret = dsa_switch_suspend(ds); + } + + return ret; +} + +static int dsa_resume(struct device *d) +{ + struct platform_device *pdev = to_platform_device(d); + struct dsa_switch_tree *dst = platform_get_drvdata(pdev); + int i, ret = 0; + + for (i = 0; i < dst->pd->nr_chips; i++) { + struct dsa_switch *ds = dst->ds[i]; + + if (ds != NULL) + ret = dsa_switch_resume(ds); + } + + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(dsa_pm_ops, dsa_suspend, dsa_resume); + +static const struct of_device_id dsa_of_match_table[] = { + { .compatible = "marvell,dsa", }, + {} +}; +MODULE_DEVICE_TABLE(of, dsa_of_match_table); + +static struct platform_driver dsa_driver = { + .probe = dsa_probe, + .remove = dsa_remove, + .shutdown = dsa_shutdown, + .driver = { + .name = "dsa", + .of_match_table = dsa_of_match_table, + .pm = &dsa_pm_ops, + }, +}; + +int dsa_legacy_register(void) +{ + return platform_driver_register(&dsa_driver); +} + +void dsa_legacy_unregister(void) +{ + platform_driver_unregister(&dsa_driver); +} -- cgit From 8db91f6a9b2ff2bb5355ad11c668fe63eb8ae0c3 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Wed, 12 Apr 2017 11:35:05 -0700 Subject: hv_netvsc: Fix the queue index computation in forwarding case If the outgoing skb has a RX queue mapping available, we use the queue number directly, other than put it through Send Indirection Table. Signed-off-by: Haiyang Zhang Reviewed-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/hyperv_net.h | 2 +- drivers/net/hyperv/netvsc_drv.c | 44 ++++++++++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 4747ad48b3cc..768b3ae6ae8f 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -633,7 +633,7 @@ struct nvsp_message { #define NETVSC_PACKET_SIZE 4096 -#define VRSS_SEND_TAB_SIZE 16 +#define VRSS_SEND_TAB_SIZE 16 /* must be power of 2 */ #define VRSS_CHANNEL_MAX 64 #define VRSS_CHANNEL_DEFAULT 8 diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index f24c2891dd0c..e813eaf69256 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -191,6 +191,24 @@ static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size, return ppi; } +static inline int netvsc_get_tx_queue(struct net_device *ndev, + struct sk_buff *skb, int old_idx) +{ + const struct net_device_context *ndc = netdev_priv(ndev); + struct sock *sk = skb->sk; + int q_idx; + + q_idx = ndc->tx_send_table[skb_get_hash(skb) & + (VRSS_SEND_TAB_SIZE - 1)]; + + /* If queue index changed record the new value */ + if (q_idx != old_idx && + sk && sk_fullsock(sk) && rcu_access_pointer(sk->sk_dst_cache)) + sk_tx_queue_set(sk, q_idx); + + return q_idx; +} + /* * Select queue for transmit. * @@ -205,24 +223,22 @@ static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size, static u16 netvsc_select_queue(struct net_device *ndev, struct sk_buff *skb, void *accel_priv, select_queue_fallback_t fallback) { - struct net_device_context *net_device_ctx = netdev_priv(ndev); unsigned int num_tx_queues = ndev->real_num_tx_queues; - struct sock *sk = skb->sk; - int q_idx = sk_tx_queue_get(sk); - - if (q_idx < 0 || skb->ooo_okay || q_idx >= num_tx_queues) { - u16 hash = __skb_tx_hash(ndev, skb, VRSS_SEND_TAB_SIZE); - int new_idx; - - new_idx = net_device_ctx->tx_send_table[hash] % num_tx_queues; + int q_idx = sk_tx_queue_get(skb->sk); - if (q_idx != new_idx && sk && - sk_fullsock(sk) && rcu_access_pointer(sk->sk_dst_cache)) - sk_tx_queue_set(sk, new_idx); - - q_idx = new_idx; + if (q_idx < 0 || skb->ooo_okay) { + /* If forwarding a packet, we use the recorded queue when + * available for better cache locality. + */ + if (skb_rx_queue_recorded(skb)) + q_idx = skb_get_rx_queue(skb); + else + q_idx = netvsc_get_tx_queue(ndev, skb, q_idx); } + while (unlikely(q_idx >= num_tx_queues)) + q_idx -= num_tx_queues; + return q_idx; } -- cgit From f72860afa2e32cdc674cbdd7f354f8fb62e908a6 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Wed, 12 Apr 2017 11:45:18 -0700 Subject: hv_netvsc: Exclude non-TCP port numbers from vRSS hashing Azure hosts are not supporting non-TCP port numbers in vRSS hashing for now. For example, UDP packet loss rate will be high if port numbers are also included in vRSS hash. So, we created this patch to use only IP numbers for hashing in non-TCP traffic. Signed-off-by: Haiyang Zhang Reviewed-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index e813eaf69256..51fa90365348 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -191,6 +191,36 @@ static void *init_ppi_data(struct rndis_message *msg, u32 ppi_size, return ppi; } +/* Azure hosts don't support non-TCP port numbers in hashing yet. We compute + * hash for non-TCP traffic with only IP numbers. + */ +static inline u32 netvsc_get_hash(struct sk_buff *skb, struct sock *sk) +{ + struct flow_keys flow; + u32 hash; + static u32 hashrnd __read_mostly; + + net_get_random_once(&hashrnd, sizeof(hashrnd)); + + if (!skb_flow_dissect_flow_keys(skb, &flow, 0)) + return 0; + + if (flow.basic.ip_proto == IPPROTO_TCP) { + return skb_get_hash(skb); + } else { + if (flow.basic.n_proto == htons(ETH_P_IP)) + hash = jhash2((u32 *)&flow.addrs.v4addrs, 2, hashrnd); + else if (flow.basic.n_proto == htons(ETH_P_IPV6)) + hash = jhash2((u32 *)&flow.addrs.v6addrs, 8, hashrnd); + else + hash = 0; + + skb_set_hash(skb, hash, PKT_HASH_TYPE_L3); + } + + return hash; +} + static inline int netvsc_get_tx_queue(struct net_device *ndev, struct sk_buff *skb, int old_idx) { @@ -198,7 +228,7 @@ static inline int netvsc_get_tx_queue(struct net_device *ndev, struct sock *sk = skb->sk; int q_idx; - q_idx = ndc->tx_send_table[skb_get_hash(skb) & + q_idx = ndc->tx_send_table[netvsc_get_hash(skb, sk) & (VRSS_SEND_TAB_SIZE - 1)]; /* If queue index changed record the new value */ -- cgit From 500a3d0ded5ee41072d0f084bff938747ee0c125 Mon Sep 17 00:00:00 2001 From: Erez Shitrit Date: Thu, 13 Apr 2017 06:36:51 +0300 Subject: net/mlx5: Add IPoIB enhanced offloads bits to mlx5_ifc New capability bit: ipoib_enhanced_offloads, indicates new ability for UD QP to do RSS and enhanced IPoIB offloads and acceleration. Add underlay_qpn to the TIS and flow_table objects In order to support SET_ROOT command, to connect between IPoIB QPs and flow steering tables. Signed-off-by: Erez Shitrit Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- include/linux/mlx5/mlx5_ifc.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 1993adbd2c82..7c50bd39b297 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -872,7 +872,8 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 compact_address_vector[0x1]; u8 striding_rq[0x1]; - u8 reserved_at_202[0x2]; + u8 reserved_at_202[0x1]; + u8 ipoib_enhanced_offloads[0x1]; u8 ipoib_basic_offloads[0x1]; u8 reserved_at_205[0xa]; u8 drain_sigerr[0x1]; @@ -2293,7 +2294,9 @@ struct mlx5_ifc_tisc_bits { u8 reserved_at_120[0x8]; u8 transport_domain[0x18]; - u8 reserved_at_140[0x3c0]; + u8 reserved_at_140[0x8]; + u8 underlay_qpn[0x18]; + u8 reserved_at_160[0x3a0]; }; enum { @@ -8218,7 +8221,9 @@ struct mlx5_ifc_set_flow_table_root_in_bits { u8 reserved_at_a0[0x8]; u8 table_id[0x18]; - u8 reserved_at_c0[0x140]; + u8 reserved_at_c0[0x8]; + u8 underlay_qpn[0x18]; + u8 reserved_at_e0[0x120]; }; enum { -- cgit From b3ba51498bddd72a526d9067b8b0ecf4932ce57e Mon Sep 17 00:00:00 2001 From: Erez Shitrit Date: Thu, 13 Apr 2017 06:36:52 +0300 Subject: net/mlx5: Refactor create flow table method to accept underlay QP IB flow tables need the underlay qp to perform flow steering. Here we change the API of the flow tables creation to accept the underlay QP number as a parameter in order to support IB (IPoIB) flow steering. Signed-off-by: Erez Shitrit Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c | 10 ++- drivers/net/ethernet/mellanox/mlx5/core/en_fs.c | 25 +++++-- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 5 +- .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 16 +++-- drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c | 8 +++ drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 84 +++++++++++++--------- drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 1 + include/linux/mlx5/fs.h | 14 ++-- 8 files changed, 113 insertions(+), 50 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c index c4e9cc79f5c7..c8a005326e30 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c @@ -321,10 +321,16 @@ static int arfs_create_table(struct mlx5e_priv *priv, { struct mlx5e_arfs_tables *arfs = &priv->fs.arfs; struct mlx5e_flow_table *ft = &arfs->arfs_tables[type].ft; + struct mlx5_flow_table_attr ft_attr = {}; int err; - ft->t = mlx5_create_flow_table(priv->fs.ns, MLX5E_NIC_PRIO, - MLX5E_ARFS_TABLE_SIZE, MLX5E_ARFS_FT_LEVEL, 0); + ft->num_groups = 0; + + ft_attr.max_fte = MLX5E_ARFS_TABLE_SIZE; + ft_attr.level = MLX5E_ARFS_FT_LEVEL; + ft_attr.prio = MLX5E_NIC_PRIO; + + ft->t = mlx5_create_flow_table(priv->fs.ns, &ft_attr); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); ft->t = NULL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index 5376d69a6b1a..729904c43801 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -803,11 +803,15 @@ static void mlx5e_destroy_ttc_table(struct mlx5e_priv *priv) static int mlx5e_create_ttc_table(struct mlx5e_priv *priv) { struct mlx5e_ttc_table *ttc = &priv->fs.ttc; + struct mlx5_flow_table_attr ft_attr = {}; struct mlx5e_flow_table *ft = &ttc->ft; int err; - ft->t = mlx5_create_flow_table(priv->fs.ns, MLX5E_NIC_PRIO, - MLX5E_TTC_TABLE_SIZE, MLX5E_TTC_FT_LEVEL, 0); + ft_attr.max_fte = MLX5E_TTC_TABLE_SIZE; + ft_attr.level = MLX5E_TTC_FT_LEVEL; + ft_attr.prio = MLX5E_NIC_PRIO; + + ft->t = mlx5_create_flow_table(priv->fs.ns, &ft_attr); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); ft->t = NULL; @@ -973,12 +977,16 @@ static int mlx5e_create_l2_table(struct mlx5e_priv *priv) { struct mlx5e_l2_table *l2_table = &priv->fs.l2; struct mlx5e_flow_table *ft = &l2_table->ft; + struct mlx5_flow_table_attr ft_attr = {}; int err; ft->num_groups = 0; - ft->t = mlx5_create_flow_table(priv->fs.ns, MLX5E_NIC_PRIO, - MLX5E_L2_TABLE_SIZE, MLX5E_L2_FT_LEVEL, 0); + ft_attr.max_fte = MLX5E_L2_TABLE_SIZE; + ft_attr.level = MLX5E_L2_FT_LEVEL; + ft_attr.prio = MLX5E_NIC_PRIO; + + ft->t = mlx5_create_flow_table(priv->fs.ns, &ft_attr); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); ft->t = NULL; @@ -1076,11 +1084,16 @@ static int mlx5e_create_vlan_table_groups(struct mlx5e_flow_table *ft) static int mlx5e_create_vlan_table(struct mlx5e_priv *priv) { struct mlx5e_flow_table *ft = &priv->fs.vlan.ft; + struct mlx5_flow_table_attr ft_attr = {}; int err; ft->num_groups = 0; - ft->t = mlx5_create_flow_table(priv->fs.ns, MLX5E_NIC_PRIO, - MLX5E_VLAN_TABLE_SIZE, MLX5E_VLAN_FT_LEVEL, 0); + + ft_attr.max_fte = MLX5E_VLAN_TABLE_SIZE; + ft_attr.level = MLX5E_VLAN_FT_LEVEL; + ft_attr.prio = MLX5E_NIC_PRIO; + + ft->t = mlx5_create_flow_table(priv->fs.ns, &ft_attr); if (IS_ERR(ft->t)) { err = PTR_ERR(ft->t); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index fcd5bc7e31db..b3281d1118b3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -337,6 +337,7 @@ esw_fdb_set_vport_promisc_rule(struct mlx5_eswitch *esw, u32 vport) static int esw_create_legacy_fdb_table(struct mlx5_eswitch *esw, int nvports) { int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_table_attr ft_attr = {}; struct mlx5_core_dev *dev = esw->dev; struct mlx5_flow_namespace *root_ns; struct mlx5_flow_table *fdb; @@ -362,7 +363,9 @@ static int esw_create_legacy_fdb_table(struct mlx5_eswitch *esw, int nvports) memset(flow_group_in, 0, inlen); table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size)); - fdb = mlx5_create_flow_table(root_ns, 0, table_size, 0, 0); + + ft_attr.max_fte = table_size; + fdb = mlx5_create_flow_table(root_ns, &ft_attr); if (IS_ERR(fdb)) { err = PTR_ERR(fdb); esw_warn(dev, "Failed to create FDB Table err %d\n", err); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index fff962dac8e3..992b380d36be 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -432,6 +432,7 @@ out: static int esw_create_offloads_fdb_table(struct mlx5_eswitch *esw, int nvports) { int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_table_attr ft_attr = {}; int table_size, ix, esw_size, err = 0; struct mlx5_core_dev *dev = esw->dev; struct mlx5_flow_namespace *root_ns; @@ -475,7 +476,11 @@ static int esw_create_offloads_fdb_table(struct mlx5_eswitch *esw, int nvports) esw->fdb_table.fdb = fdb; table_size = nvports + MAX_PF_SQ + 1; - fdb = mlx5_create_flow_table(root_ns, FDB_SLOW_PATH, table_size, 0, 0); + + ft_attr.max_fte = table_size; + ft_attr.prio = FDB_SLOW_PATH; + + fdb = mlx5_create_flow_table(root_ns, &ft_attr); if (IS_ERR(fdb)) { err = PTR_ERR(fdb); esw_warn(dev, "Failed to create slow path FDB Table err %d\n", err); @@ -556,9 +561,10 @@ static void esw_destroy_offloads_fdb_table(struct mlx5_eswitch *esw) static int esw_create_offloads_table(struct mlx5_eswitch *esw) { - struct mlx5_flow_namespace *ns; - struct mlx5_flow_table *ft_offloads; + struct mlx5_flow_table_attr ft_attr = {}; struct mlx5_core_dev *dev = esw->dev; + struct mlx5_flow_table *ft_offloads; + struct mlx5_flow_namespace *ns; int err = 0; ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_OFFLOADS); @@ -567,7 +573,9 @@ static int esw_create_offloads_table(struct mlx5_eswitch *esw) return -EOPNOTSUPP; } - ft_offloads = mlx5_create_flow_table(ns, 0, dev->priv.sriov.num_vfs + 2, 0, 0); + ft_attr.max_fte = dev->priv.sriov.num_vfs + 2; + + ft_offloads = mlx5_create_flow_table(ns, &ft_attr); if (IS_ERR(ft_offloads)) { err = PTR_ERR(ft_offloads); esw_warn(esw->dev, "Failed to create offloads table, err %d\n", err); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index c6178ea1a461..19e3d2fc2099 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -45,6 +45,10 @@ int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev, u32 in[MLX5_ST_SZ_DW(set_flow_table_root_in)] = {0}; u32 out[MLX5_ST_SZ_DW(set_flow_table_root_out)] = {0}; + if ((MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_IB) && + ft->underlay_qpn == 0) + return 0; + MLX5_SET(set_flow_table_root_in, in, opcode, MLX5_CMD_OP_SET_FLOW_TABLE_ROOT); MLX5_SET(set_flow_table_root_in, in, table_type, ft->type); @@ -54,6 +58,10 @@ int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev, MLX5_SET(set_flow_table_root_in, in, other_vport, 1); } + if ((MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_IB) && + ft->underlay_qpn != 0) + MLX5_SET(set_flow_table_root_in, in, underlay_qpn, ft->underlay_qpn); + return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 27ff815600f7..55182d0b06e8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -778,18 +778,16 @@ static void list_add_flow_table(struct mlx5_flow_table *ft, } static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespace *ns, + struct mlx5_flow_table_attr *ft_attr, enum fs_flow_table_op_mod op_mod, - u16 vport, int prio, - int max_fte, u32 level, - u32 flags) + u16 vport) { + struct mlx5_flow_root_namespace *root = find_root(&ns->node); struct mlx5_flow_table *next_ft = NULL; + struct fs_prio *fs_prio = NULL; struct mlx5_flow_table *ft; - int err; int log_table_sz; - struct mlx5_flow_root_namespace *root = - find_root(&ns->node); - struct fs_prio *fs_prio = NULL; + int err; if (!root) { pr_err("mlx5: flow steering failed to find root of namespace\n"); @@ -797,29 +795,31 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa } mutex_lock(&root->chain_lock); - fs_prio = find_prio(ns, prio); + fs_prio = find_prio(ns, ft_attr->prio); if (!fs_prio) { err = -EINVAL; goto unlock_root; } - if (level >= fs_prio->num_levels) { + if (ft_attr->level >= fs_prio->num_levels) { err = -ENOSPC; goto unlock_root; } /* The level is related to the * priority level range. */ - level += fs_prio->start_level; - ft = alloc_flow_table(level, + ft_attr->level += fs_prio->start_level; + ft = alloc_flow_table(ft_attr->level, vport, - max_fte ? roundup_pow_of_two(max_fte) : 0, + ft_attr->max_fte ? roundup_pow_of_two(ft_attr->max_fte) : 0, root->table_type, - op_mod, flags); + op_mod, ft_attr->flags); if (!ft) { err = -ENOMEM; goto unlock_root; } + ft->underlay_qpn = ft_attr->underlay_qpn; + tree_init_node(&ft->node, 1, del_flow_table); log_table_sz = ft->max_fte ? ilog2(ft->max_fte) : 0; next_ft = find_next_chained_ft(fs_prio); @@ -849,44 +849,56 @@ unlock_root: } struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, - int prio, int max_fte, - u32 level, - u32 flags) + struct mlx5_flow_table_attr *ft_attr) { - return __mlx5_create_flow_table(ns, FS_FT_OP_MOD_NORMAL, 0, prio, - max_fte, level, flags); + return __mlx5_create_flow_table(ns, ft_attr, FS_FT_OP_MOD_NORMAL, 0); } struct mlx5_flow_table *mlx5_create_vport_flow_table(struct mlx5_flow_namespace *ns, int prio, int max_fte, u32 level, u16 vport) { - return __mlx5_create_flow_table(ns, FS_FT_OP_MOD_NORMAL, vport, prio, - max_fte, level, 0); + struct mlx5_flow_table_attr ft_attr = {}; + + ft_attr.max_fte = max_fte; + ft_attr.level = level; + ft_attr.prio = prio; + + return __mlx5_create_flow_table(ns, &ft_attr, FS_FT_OP_MOD_NORMAL, 0); } -struct mlx5_flow_table *mlx5_create_lag_demux_flow_table( - struct mlx5_flow_namespace *ns, - int prio, u32 level) +struct mlx5_flow_table* +mlx5_create_lag_demux_flow_table(struct mlx5_flow_namespace *ns, + int prio, u32 level) { - return __mlx5_create_flow_table(ns, FS_FT_OP_MOD_LAG_DEMUX, 0, prio, 0, - level, 0); + struct mlx5_flow_table_attr ft_attr = {}; + + ft_attr.level = level; + ft_attr.prio = prio; + return __mlx5_create_flow_table(ns, &ft_attr, FS_FT_OP_MOD_LAG_DEMUX, 0); } EXPORT_SYMBOL(mlx5_create_lag_demux_flow_table); -struct mlx5_flow_table *mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns, - int prio, - int num_flow_table_entries, - int max_num_groups, - u32 level, - u32 flags) +struct mlx5_flow_table* +mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns, + int prio, + int num_flow_table_entries, + int max_num_groups, + u32 level, + u32 flags) { + struct mlx5_flow_table_attr ft_attr = {}; struct mlx5_flow_table *ft; if (max_num_groups > num_flow_table_entries) return ERR_PTR(-EINVAL); - ft = mlx5_create_flow_table(ns, prio, num_flow_table_entries, level, flags); + ft_attr.max_fte = num_flow_table_entries; + ft_attr.prio = prio; + ft_attr.level = level; + ft_attr.flags = flags; + + ft = mlx5_create_flow_table(ns, &ft_attr); if (IS_ERR(ft)) return ft; @@ -1828,12 +1840,18 @@ static void set_prio_attrs(struct mlx5_flow_root_namespace *root_ns) static int create_anchor_flow_table(struct mlx5_flow_steering *steering) { struct mlx5_flow_namespace *ns = NULL; + struct mlx5_flow_table_attr ft_attr = {}; struct mlx5_flow_table *ft; ns = mlx5_get_flow_namespace(steering->dev, MLX5_FLOW_NAMESPACE_ANCHOR); if (WARN_ON(!ns)) return -EINVAL; - ft = mlx5_create_flow_table(ns, ANCHOR_PRIO, ANCHOR_SIZE, ANCHOR_LEVEL, 0); + + ft_attr.max_fte = ANCHOR_SIZE; + ft_attr.level = ANCHOR_LEVEL; + ft_attr.prio = ANCHOR_PRIO; + + ft = mlx5_create_flow_table(ns, &ft_attr); if (IS_ERR(ft)) { mlx5_core_err(steering->dev, "Failed to create last anchor flow table"); return PTR_ERR(ft); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 03af2e7989f3..577d056bf3df 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -118,6 +118,7 @@ struct mlx5_flow_table { /* FWD rules that point on this flow table */ struct list_head fwd_rules; u32 flags; + u32 underlay_qpn; }; struct mlx5_fc_cache { diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index ae91a4bda1a3..1b166d2e19c5 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -104,12 +104,18 @@ mlx5_create_auto_grouped_flow_table(struct mlx5_flow_namespace *ns, u32 level, u32 flags); +struct mlx5_flow_table_attr { + int prio; + int max_fte; + u32 level; + u32 flags; + u32 underlay_qpn; +}; + struct mlx5_flow_table * mlx5_create_flow_table(struct mlx5_flow_namespace *ns, - int prio, - int num_flow_table_entries, - u32 level, - u32 flags); + struct mlx5_flow_table_attr *ft_attr); + struct mlx5_flow_table * mlx5_create_vport_flow_table(struct mlx5_flow_namespace *ns, int prio, -- cgit From ffdb8827ec31f63f5db242937c3233bedfeb8702 Mon Sep 17 00:00:00 2001 From: Erez Shitrit Date: Thu, 13 Apr 2017 06:36:53 +0300 Subject: net/mlx5: Enable flow-steering for IB link Get the relevant capabilities if supports ipoib_enhanced_offloads and init the flow steering table accordingly. Signed-off-by: Erez Shitrit Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 11 ++++------- drivers/net/ethernet/mellanox/mlx5/core/fw.c | 3 ++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 55182d0b06e8..b8a176503d38 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -1905,9 +1905,6 @@ void mlx5_cleanup_fs(struct mlx5_core_dev *dev) { struct mlx5_flow_steering *steering = dev->priv.steering; - if (MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) - return; - cleanup_root_ns(steering->root_ns); cleanup_root_ns(steering->esw_egress_root_ns); cleanup_root_ns(steering->esw_ingress_root_ns); @@ -2010,9 +2007,6 @@ int mlx5_init_fs(struct mlx5_core_dev *dev) struct mlx5_flow_steering *steering; int err = 0; - if (MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) - return 0; - err = mlx5_init_fc_stats(dev); if (err) return err; @@ -2023,7 +2017,10 @@ int mlx5_init_fs(struct mlx5_core_dev *dev) steering->dev = dev; dev->priv.steering = steering; - if (MLX5_CAP_GEN(dev, nic_flow_table) && + if ((((MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_ETH) && + (MLX5_CAP_GEN(dev, nic_flow_table))) || + ((MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_IB) && + MLX5_CAP_GEN(dev, ipoib_enhanced_offloads))) && MLX5_CAP_FLOWTABLE_NIC_RX(dev, ft_support)) { err = init_root_ns(steering); if (err) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index d0bbefa08af7..1bc14d0fded8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -137,7 +137,8 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev) return err; } - if (MLX5_CAP_GEN(dev, nic_flow_table)) { + if (MLX5_CAP_GEN(dev, nic_flow_table) || + MLX5_CAP_GEN(dev, ipoib_enhanced_offloads)) { err = mlx5_core_get_caps(dev, MLX5_CAP_FLOW_TABLE); if (err) return err; -- cgit From 2c3b5beec46ab0d77c94828eb15170b333ae769a Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 13 Apr 2017 06:36:54 +0300 Subject: net/mlx5e: More generic netdev management API In preparation for mlx5e RDMA net_device support, here we generalize mlx5e_attach/detach in a way that those functions will be agnostic to link type. For that we move ethernet specific NIC net device logic out of those functions into {nic,rep}_{enable/disable} mlx5e NIC and representor profiles callbacks. Also some of the logic was moved only to NIC profile since it is not right to have this logic for representor net device (e.g. set port MTU). Signed-off-by: Saeed Mahameed Reviewed-by: Erez Shitrit Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 15 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 160 +++++++++++----------- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 12 +- 3 files changed, 96 insertions(+), 91 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index b7feecfbb5a5..ced31906b8fd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -999,12 +999,6 @@ void mlx5e_cleanup_nic_tx(struct mlx5e_priv *priv); int mlx5e_close(struct net_device *netdev); int mlx5e_open(struct net_device *netdev); void mlx5e_update_stats_work(struct work_struct *work); -struct net_device *mlx5e_create_netdev(struct mlx5_core_dev *mdev, - const struct mlx5e_profile *profile, - void *ppriv); -void mlx5e_destroy_netdev(struct mlx5_core_dev *mdev, struct mlx5e_priv *priv); -int mlx5e_attach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev); -void mlx5e_detach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev); u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout); int mlx5e_get_offload_stats(int attr_id, const struct net_device *dev, @@ -1013,4 +1007,13 @@ bool mlx5e_has_offload_stats(const struct net_device *dev, int attr_id); bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv); bool mlx5e_is_vf_vport_rep(struct mlx5e_priv *priv); + +/* mlx5e generic netdev management API */ +struct net_device* +mlx5e_create_netdev(struct mlx5_core_dev *mdev, const struct mlx5e_profile *profile, + void *ppriv); +int mlx5e_attach_netdev(struct mlx5e_priv *priv); +void mlx5e_detach_netdev(struct mlx5e_priv *priv); +void mlx5e_destroy_netdev(struct mlx5e_priv *priv); + #endif /* __MLX5_EN_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 8b7b7e604ea0..cdc34ba354c8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -4121,12 +4121,57 @@ static int mlx5e_init_nic_tx(struct mlx5e_priv *priv) return 0; } +static void mlx5e_register_vport_rep(struct mlx5_core_dev *mdev) +{ + struct mlx5_eswitch *esw = mdev->priv.eswitch; + int total_vfs = MLX5_TOTAL_VPORTS(mdev); + int vport; + u8 mac[ETH_ALEN]; + + if (!MLX5_CAP_GEN(mdev, vport_group_manager)) + return; + + mlx5_query_nic_vport_mac_address(mdev, 0, mac); + + for (vport = 1; vport < total_vfs; vport++) { + struct mlx5_eswitch_rep rep; + + rep.load = mlx5e_vport_rep_load; + rep.unload = mlx5e_vport_rep_unload; + rep.vport = vport; + ether_addr_copy(rep.hw_id, mac); + mlx5_eswitch_register_vport_rep(esw, vport, &rep); + } +} + +static void mlx5e_unregister_vport_rep(struct mlx5_core_dev *mdev) +{ + struct mlx5_eswitch *esw = mdev->priv.eswitch; + int total_vfs = MLX5_TOTAL_VPORTS(mdev); + int vport; + + if (!MLX5_CAP_GEN(mdev, vport_group_manager)) + return; + + for (vport = 1; vport < total_vfs; vport++) + mlx5_eswitch_unregister_vport_rep(esw, vport); +} + static void mlx5e_nic_enable(struct mlx5e_priv *priv) { struct net_device *netdev = priv->netdev; struct mlx5_core_dev *mdev = priv->mdev; struct mlx5_eswitch *esw = mdev->priv.eswitch; struct mlx5_eswitch_rep rep; + u16 max_mtu; + + mlx5e_init_l2_addr(priv); + + /* MTU range: 68 - hw-specific max */ + netdev->min_mtu = ETH_MIN_MTU; + mlx5_query_port_max_mtu(priv->mdev, &max_mtu, 1); + netdev->max_mtu = MLX5E_HW2SW_MTU(max_mtu); + mlx5e_set_dev_port_mtu(priv); mlx5_lag_add(mdev, netdev); @@ -4141,6 +4186,8 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) mlx5_eswitch_register_vport_rep(esw, 0, &rep); } + mlx5e_register_vport_rep(mdev); + if (netdev->reg_state != NETREG_REGISTERED) return; @@ -4152,6 +4199,12 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) } queue_work(priv->wq, &priv->set_rx_mode_work); + + rtnl_lock(); + if (netif_running(netdev)) + mlx5e_open(netdev); + netif_device_attach(netdev); + rtnl_unlock(); } static void mlx5e_nic_disable(struct mlx5e_priv *priv) @@ -4159,7 +4212,14 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv) struct mlx5_core_dev *mdev = priv->mdev; struct mlx5_eswitch *esw = mdev->priv.eswitch; + rtnl_lock(); + if (netif_running(priv->netdev)) + mlx5e_close(priv->netdev); + netif_device_detach(priv->netdev); + rtnl_unlock(); + queue_work(priv->wq, &priv->set_rx_mode_work); + mlx5e_unregister_vport_rep(mdev); if (MLX5_CAP_GEN(mdev, vport_group_manager)) mlx5_eswitch_unregister_vport_rep(esw, 0); mlx5e_disable_async_events(priv); @@ -4180,6 +4240,8 @@ static const struct mlx5e_profile mlx5e_nic_profile = { .max_tc = MLX5E_MAX_NUM_TC, }; +/* mlx5e generic netdev management API (move to en_common.c) */ + struct net_device *mlx5e_create_netdev(struct mlx5_core_dev *mdev, const struct mlx5e_profile *profile, void *ppriv) @@ -4219,14 +4281,12 @@ err_cleanup_nic: return NULL; } -int mlx5e_attach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev) +int mlx5e_attach_netdev(struct mlx5e_priv *priv) { + struct mlx5_core_dev *mdev = priv->mdev; const struct mlx5e_profile *profile; - struct mlx5e_priv *priv; - u16 max_mtu; int err; - priv = netdev_priv(netdev); profile = priv->profile; clear_bit(MLX5E_STATE_DESTROYING, &priv->state); @@ -4246,24 +4306,9 @@ int mlx5e_attach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev) mlx5e_create_q_counter(priv); - mlx5e_init_l2_addr(priv); - - /* MTU range: 68 - hw-specific max */ - netdev->min_mtu = ETH_MIN_MTU; - mlx5_query_port_max_mtu(priv->mdev, &max_mtu, 1); - netdev->max_mtu = MLX5E_HW2SW_MTU(max_mtu); - - mlx5e_set_dev_port_mtu(priv); - if (profile->enable) profile->enable(priv); - rtnl_lock(); - if (netif_running(netdev)) - mlx5e_open(netdev); - netif_device_attach(netdev); - rtnl_unlock(); - return 0; err_close_drop_rq: @@ -4276,55 +4321,12 @@ out: return err; } -static void mlx5e_register_vport_rep(struct mlx5_core_dev *mdev) -{ - struct mlx5_eswitch *esw = mdev->priv.eswitch; - int total_vfs = MLX5_TOTAL_VPORTS(mdev); - int vport; - u8 mac[ETH_ALEN]; - - if (!MLX5_CAP_GEN(mdev, vport_group_manager)) - return; - - mlx5_query_nic_vport_mac_address(mdev, 0, mac); - - for (vport = 1; vport < total_vfs; vport++) { - struct mlx5_eswitch_rep rep; - - rep.load = mlx5e_vport_rep_load; - rep.unload = mlx5e_vport_rep_unload; - rep.vport = vport; - ether_addr_copy(rep.hw_id, mac); - mlx5_eswitch_register_vport_rep(esw, vport, &rep); - } -} - -static void mlx5e_unregister_vport_rep(struct mlx5_core_dev *mdev) -{ - struct mlx5_eswitch *esw = mdev->priv.eswitch; - int total_vfs = MLX5_TOTAL_VPORTS(mdev); - int vport; - - if (!MLX5_CAP_GEN(mdev, vport_group_manager)) - return; - - for (vport = 1; vport < total_vfs; vport++) - mlx5_eswitch_unregister_vport_rep(esw, vport); -} - -void mlx5e_detach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev) +void mlx5e_detach_netdev(struct mlx5e_priv *priv) { - struct mlx5e_priv *priv = netdev_priv(netdev); const struct mlx5e_profile *profile = priv->profile; set_bit(MLX5E_STATE_DESTROYING, &priv->state); - rtnl_lock(); - if (netif_running(netdev)) - mlx5e_close(netdev); - netif_device_detach(netdev); - rtnl_unlock(); - if (profile->disable) profile->disable(priv); flush_workqueue(priv->wq); @@ -4336,6 +4338,17 @@ void mlx5e_detach_netdev(struct mlx5_core_dev *mdev, struct net_device *netdev) cancel_delayed_work_sync(&priv->update_stats_work); } +void mlx5e_destroy_netdev(struct mlx5e_priv *priv) +{ + const struct mlx5e_profile *profile = priv->profile; + struct net_device *netdev = priv->netdev; + + destroy_workqueue(priv->wq); + if (profile->cleanup) + profile->cleanup(priv); + free_netdev(netdev); +} + /* mlx5e_attach and mlx5e_detach scope should be only creating/destroying * hardware contexts and to connect it to the current netdev. */ @@ -4352,13 +4365,12 @@ static int mlx5e_attach(struct mlx5_core_dev *mdev, void *vpriv) if (err) return err; - err = mlx5e_attach_netdev(mdev, netdev); + err = mlx5e_attach_netdev(priv); if (err) { mlx5e_destroy_mdev_resources(mdev); return err; } - mlx5e_register_vport_rep(mdev); return 0; } @@ -4370,8 +4382,7 @@ static void mlx5e_detach(struct mlx5_core_dev *mdev, void *vpriv) if (!netif_device_present(netdev)) return; - mlx5e_unregister_vport_rep(mdev); - mlx5e_detach_netdev(mdev, netdev); + mlx5e_detach_netdev(priv); mlx5e_destroy_mdev_resources(mdev); } @@ -4418,7 +4429,7 @@ err_detach: mlx5e_detach(mdev, priv); err_destroy_netdev: - mlx5e_destroy_netdev(mdev, priv); + mlx5e_destroy_netdev(priv); err_unregister_reps: for (vport = 1; vport < total_vfs; vport++) @@ -4427,24 +4438,13 @@ err_unregister_reps: return NULL; } -void mlx5e_destroy_netdev(struct mlx5_core_dev *mdev, struct mlx5e_priv *priv) -{ - const struct mlx5e_profile *profile = priv->profile; - struct net_device *netdev = priv->netdev; - - destroy_workqueue(priv->wq); - if (profile->cleanup) - profile->cleanup(priv); - free_netdev(netdev); -} - static void mlx5e_remove(struct mlx5_core_dev *mdev, void *vpriv) { struct mlx5e_priv *priv = vpriv; unregister_netdev(priv->netdev); mlx5e_detach(mdev, vpriv); - mlx5e_destroy_netdev(mdev, priv); + mlx5e_destroy_netdev(priv); } static void *mlx5e_get_netdev(void *vpriv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 53db5ec2c122..d7664ff5c736 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -470,6 +470,8 @@ static int mlx5e_init_rep_rx(struct mlx5e_priv *priv) int err; int i; + mlx5e_init_l2_addr(priv); + err = mlx5e_create_direct_rqts(priv); if (err) { mlx5_core_warn(mdev, "create direct rqts failed, %d\n", err); @@ -563,7 +565,7 @@ int mlx5e_vport_rep_load(struct mlx5_eswitch *esw, rep->netdev = netdev; - err = mlx5e_attach_netdev(esw->dev, netdev); + err = mlx5e_attach_netdev(netdev_priv(netdev)); if (err) { pr_warn("Failed to attach representor netdev for vport %d\n", rep->vport); @@ -580,10 +582,10 @@ int mlx5e_vport_rep_load(struct mlx5_eswitch *esw, return 0; err_detach_netdev: - mlx5e_detach_netdev(esw->dev, netdev); + mlx5e_detach_netdev(netdev_priv(netdev)); err_destroy_netdev: - mlx5e_destroy_netdev(esw->dev, netdev_priv(netdev)); + mlx5e_destroy_netdev(netdev_priv(netdev)); return err; @@ -595,6 +597,6 @@ void mlx5e_vport_rep_unload(struct mlx5_eswitch *esw, struct net_device *netdev = rep->netdev; unregister_netdev(netdev); - mlx5e_detach_netdev(esw->dev, netdev); - mlx5e_destroy_netdev(esw->dev, netdev_priv(netdev)); + mlx5e_detach_netdev(netdev_priv(netdev)); + mlx5e_destroy_netdev(netdev_priv(netdev)); } -- cgit From 48935bbb7ae8bd4157a84fe12da48cd8d337c40a Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 13 Apr 2017 06:36:55 +0300 Subject: net/mlx5e: IPoIB, Add netdevice profile skeleton Create mlx5e IPoIB netdevice profile skeleton in the new ipoib.c file with empty implementation. Downstream patches will provide the full mlx5 rdma netdevice acceleration support for IPoIB into this new file, by using the mlx5e netdevice profile and new mlx5_channels APIs and infrastructures. Same as already done in mlx5e NIC netdevice and switchdev mode VF representors. Signed-off-by: Saeed Mahameed Reviewed-by: Erez Shitrit Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/Kconfig | 7 + drivers/net/ethernet/mellanox/mlx5/core/Makefile | 2 + drivers/net/ethernet/mellanox/mlx5/core/en.h | 9 ++ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 9 -- drivers/net/ethernet/mellanox/mlx5/core/ipoib.c | 181 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/ipoib.h | 50 ++++++ 6 files changed, 249 insertions(+), 9 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/ipoib.c create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/ipoib.h diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig index 117170014e88..a84b652f9b54 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig +++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig @@ -31,3 +31,10 @@ config MLX5_CORE_EN_DCB This flag is depended on the kernel's DCB support. If unsure, set to Y + +config MLX5_CORE_IPOIB + bool "Mellanox Technologies ConnectX-4 IPoIB offloads support" + depends on MLX5_CORE_EN + default y + ---help--- + MLX5 IPoIB offloads & acceleration support. diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index 9f43beb86250..9e644615f07a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -11,3 +11,5 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += wq.o eswitch.o eswitch_offloads.o \ en_tc.o en_arfs.o en_rep.o en_fs_ethtool.o en_selftest.o mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o + +mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib.o diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index ced31906b8fd..02aa3cc59dc3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -153,6 +154,14 @@ static inline int mlx5_max_log_rq_size(int wq_type) } } +static inline int mlx5e_get_max_num_channels(struct mlx5_core_dev *mdev) +{ + return is_kdump_kernel() ? + MLX5E_MIN_NUM_CHANNELS : + min_t(int, mdev->priv.eq_table.num_comp_vectors, + MLX5E_MAX_NUM_CHANNELS); +} + struct mlx5e_tx_wqe { struct mlx5_wqe_ctrl_seg ctrl; struct mlx5_wqe_eth_seg eth; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index cdc34ba354c8..14c7452a6348 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -31,7 +31,6 @@ */ #include -#include #include #include #include @@ -1710,14 +1709,6 @@ static int mlx5e_set_tx_maxrate(struct net_device *dev, int index, u32 rate) return err; } -static inline int mlx5e_get_max_num_channels(struct mlx5_core_dev *mdev) -{ - return is_kdump_kernel() ? - MLX5E_MIN_NUM_CHANNELS : - min_t(int, mdev->priv.eq_table.num_comp_vectors, - MLX5E_MAX_NUM_CHANNELS); -} - static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, struct mlx5e_params *params, struct mlx5e_channel_param *cparam, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c new file mode 100644 index 000000000000..2f65927a8d03 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2017, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "en.h" +#include "ipoib.h" + +/* IPoIB mlx5 netdev profile */ + +/* Called directly after IPoIB netdevice was created to initialize SW structs */ +static void mlx5i_init(struct mlx5_core_dev *mdev, + struct net_device *netdev, + const struct mlx5e_profile *profile, + void *ppriv) +{ + struct mlx5e_priv *priv = mlx5i_epriv(netdev); + + priv->ppriv = ppriv; + /* TODO: init netdev and mlx5e_params here */ +} + +/* Called directly before IPoIB netdevice is destroyed to cleanup SW structs */ +static void mlx5i_cleanup(struct mlx5e_priv *priv) +{ + /* Do nothing .. */ +} + +static int mlx5i_init_tx(struct mlx5e_priv *priv) +{ + /* TODO: Create IPoIB underlay QP */ + /* TODO: create IPoIB TX HW TIS */ + return 0; +} + +static void mlx5i_cleanup_tx(struct mlx5e_priv *priv) +{ +} + +static int mlx5i_init_rx(struct mlx5e_priv *priv) +{ + /* TODO: create IPoIB RX HW steering contexts */ + return 0; +} + +static void mlx5i_cleanup_rx(struct mlx5e_priv *priv) +{ +} + +static const struct mlx5e_profile mlx5i_nic_profile = { + .init = mlx5i_init, + .cleanup = mlx5i_cleanup, + .init_tx = mlx5i_init_tx, + .cleanup_tx = mlx5i_cleanup_tx, + .init_rx = mlx5i_init_rx, + .cleanup_rx = mlx5i_cleanup_rx, + .enable = NULL, /* mlx5i_enable */ + .disable = NULL, /* mlx5i_disable */ + .update_stats = NULL, /* mlx5i_update_stats */ + .max_nch = mlx5e_get_max_num_channels, + .max_tc = MLX5I_MAX_NUM_TC, +}; + +/* IPoIB RDMA netdev callbacks */ + +static int mlx5i_check_required_hca_cap(struct mlx5_core_dev *mdev) +{ + if (MLX5_CAP_GEN(mdev, port_type) != MLX5_CAP_PORT_TYPE_IB) + return -EOPNOTSUPP; + + if (!MLX5_CAP_GEN(mdev, ipoib_enhanced_offloads)) { + mlx5_core_warn(mdev, "IPoIB enhanced offloads are not supported\n"); + return -ENOTSUPP; + } + + return 0; +} + +struct net_device *mlx5_rdma_netdev_alloc(struct mlx5_core_dev *mdev, + struct ib_device *ibdev, + const char *name, + void (*setup)(struct net_device *)) +{ + const struct mlx5e_profile *profile = &mlx5i_nic_profile; + int nch = profile->max_nch(mdev); + struct net_device *netdev; + struct mlx5i_priv *ipriv; + struct mlx5e_priv *epriv; + int err; + + if (mlx5i_check_required_hca_cap(mdev)) { + mlx5_core_warn(mdev, "Accelerated mode is not supported\n"); + return ERR_PTR(-EOPNOTSUPP); + } + + /* This function should only be called once per mdev */ + err = mlx5e_create_mdev_resources(mdev); + if (err) + return NULL; + + netdev = alloc_netdev_mqs(sizeof(struct mlx5i_priv) + sizeof(struct mlx5e_priv), + name, NET_NAME_UNKNOWN, + setup, + nch * MLX5E_MAX_NUM_TC, + nch); + if (!netdev) { + mlx5_core_warn(mdev, "alloc_netdev_mqs failed\n"); + goto free_mdev_resources; + } + + ipriv = netdev_priv(netdev); + epriv = mlx5i_epriv(netdev); + + epriv->wq = create_singlethread_workqueue("mlx5i"); + if (!epriv->wq) + goto err_free_netdev; + + profile->init(mdev, netdev, profile, ipriv); + + mlx5e_attach_netdev(epriv); + netif_carrier_off(netdev); + + /* TODO: set rdma_netdev func pointers + * rn = &ipriv->rn; + * rn->hca = ibdev; + * rn->send = mlx5i_xmit; + * rn->attach_mcast = mlx5i_attach_mcast; + * rn->detach_mcast = mlx5i_detach_mcast; + */ + return netdev; + +free_mdev_resources: + mlx5e_destroy_mdev_resources(mdev); +err_free_netdev: + free_netdev(netdev); + return NULL; +} +EXPORT_SYMBOL(mlx5_rdma_netdev_alloc); + +void mlx5_rdma_netdev_free(struct net_device *netdev) +{ + struct mlx5e_priv *priv = mlx5i_epriv(netdev); + const struct mlx5e_profile *profile = priv->profile; + + mlx5e_detach_netdev(priv); + profile->cleanup(priv); + destroy_workqueue(priv->wq); + free_netdev(netdev); + + mlx5e_destroy_mdev_resources(priv->mdev); +} +EXPORT_SYMBOL(mlx5_rdma_netdev_free); + diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.h b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.h new file mode 100644 index 000000000000..25b8b8a33e24 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __MLX5E_IPOB_H__ +#define __MLX5E_IPOB_H__ + +#include +#include "en.h" + +#define MLX5I_MAX_NUM_TC 1 + +/* ipoib rdma netdev's private data structure */ +struct mlx5i_priv { + struct mlx5_core_qp qp; + char *mlx5e_priv[0]; +}; + +/* Extract mlx5e_priv from IPoIB netdev */ +#define mlx5i_epriv(netdev) ((void *)(((struct mlx5i_priv *)netdev_priv(netdev))->mlx5e_priv)) + +#endif /* __MLX5E_IPOB_H__ */ -- cgit From 8f493ffd88eac1b45685f0f21005b65352320b8a Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 13 Apr 2017 06:36:56 +0300 Subject: net/mlx5e: IPoIB, RX steering RSS RQTs and TIRs Implement IPoIB RX RSS (RQTs and TIRs) HW objects creation, All we do here is simply reuse the mlx5e implementation to create direct and indirect (RSS) steering HW objects. For that we just expose mlx5e_{create,destroy}_{direct,indirect}_{rqt,tir} functions into en.h and call them from ipoib.c in init/cleanup_rx IPoIB netdevice profile callbacks. Signed-off-by: Saeed Mahameed Reviewed-by: Erez Shitrit Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 12 ++++- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 56 ++++++++++++----------- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 17 ++----- drivers/net/ethernet/mellanox/mlx5/core/ipoib.c | 42 +++++++++++++++-- 4 files changed, 83 insertions(+), 44 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 02aa3cc59dc3..e5518536d56f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -999,10 +999,17 @@ int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr); void mlx5e_handle_rx_cqe_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe); void mlx5e_update_hw_rep_counters(struct mlx5e_priv *priv); +int mlx5e_create_indirect_rqt(struct mlx5e_priv *priv); + +int mlx5e_create_indirect_tirs(struct mlx5e_priv *priv); +void mlx5e_destroy_indirect_tirs(struct mlx5e_priv *priv); + int mlx5e_create_direct_rqts(struct mlx5e_priv *priv); -void mlx5e_destroy_rqt(struct mlx5e_priv *priv, struct mlx5e_rqt *rqt); +void mlx5e_destroy_direct_rqts(struct mlx5e_priv *priv); int mlx5e_create_direct_tirs(struct mlx5e_priv *priv); void mlx5e_destroy_direct_tirs(struct mlx5e_priv *priv); +void mlx5e_destroy_rqt(struct mlx5e_priv *priv, struct mlx5e_rqt *rqt); + int mlx5e_create_tises(struct mlx5e_priv *priv); void mlx5e_cleanup_nic_tx(struct mlx5e_priv *priv); int mlx5e_close(struct net_device *netdev); @@ -1024,5 +1031,8 @@ mlx5e_create_netdev(struct mlx5_core_dev *mdev, const struct mlx5e_profile *prof int mlx5e_attach_netdev(struct mlx5e_priv *priv); void mlx5e_detach_netdev(struct mlx5e_priv *priv); void mlx5e_destroy_netdev(struct mlx5e_priv *priv); +void mlx5e_build_nic_params(struct mlx5_core_dev *mdev, + struct mlx5e_params *params, + u16 max_channels); #endif /* __MLX5_EN_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 14c7452a6348..08b67aa24644 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2115,11 +2115,15 @@ void mlx5e_destroy_rqt(struct mlx5e_priv *priv, struct mlx5e_rqt *rqt) mlx5_core_destroy_rqt(priv->mdev, rqt->rqtn); } -static int mlx5e_create_indirect_rqts(struct mlx5e_priv *priv) +int mlx5e_create_indirect_rqt(struct mlx5e_priv *priv) { struct mlx5e_rqt *rqt = &priv->indir_rqt; + int err; - return mlx5e_create_rqt(priv, MLX5E_INDIR_RQT_SIZE, rqt); + err = mlx5e_create_rqt(priv, MLX5E_INDIR_RQT_SIZE, rqt); + if (err) + mlx5_core_warn(priv->mdev, "create indirect rqts failed, %d\n", err); + return err; } int mlx5e_create_direct_rqts(struct mlx5e_priv *priv) @@ -2138,12 +2142,21 @@ int mlx5e_create_direct_rqts(struct mlx5e_priv *priv) return 0; err_destroy_rqts: + mlx5_core_warn(priv->mdev, "create direct rqts failed, %d\n", err); for (ix--; ix >= 0; ix--) mlx5e_destroy_rqt(priv, &priv->direct_tir[ix].rqt); return err; } +void mlx5e_destroy_direct_rqts(struct mlx5e_priv *priv) +{ + int i; + + for (i = 0; i < priv->profile->max_nch(priv->mdev); i++) + mlx5e_destroy_rqt(priv, &priv->direct_tir[i].rqt); +} + static int mlx5e_rx_hash_fn(int hfunc) { return (hfunc == ETH_RSS_HASH_TOP) ? @@ -2818,7 +2831,7 @@ static void mlx5e_build_direct_tir_ctx(struct mlx5e_priv *priv, u32 rqtn, u32 *t MLX5_SET(tirc, tirc, rx_hash_fn, MLX5_RX_HASH_FN_INVERTED_XOR8); } -static int mlx5e_create_indirect_tirs(struct mlx5e_priv *priv) +int mlx5e_create_indirect_tirs(struct mlx5e_priv *priv) { struct mlx5e_tir *tir; void *tirc; @@ -2847,6 +2860,7 @@ static int mlx5e_create_indirect_tirs(struct mlx5e_priv *priv) return 0; err_destroy_tirs: + mlx5_core_warn(priv->mdev, "create indirect tirs failed, %d\n", err); for (tt--; tt >= 0; tt--) mlx5e_destroy_tir(priv->mdev, &priv->indir_tir[tt]); @@ -2885,6 +2899,7 @@ int mlx5e_create_direct_tirs(struct mlx5e_priv *priv) return 0; err_destroy_ch_tirs: + mlx5_core_warn(priv->mdev, "create direct tirs failed, %d\n", err); for (ix--; ix >= 0; ix--) mlx5e_destroy_tir(priv->mdev, &priv->direct_tir[ix]); @@ -2893,7 +2908,7 @@ err_destroy_ch_tirs: return err; } -static void mlx5e_destroy_indirect_tirs(struct mlx5e_priv *priv) +void mlx5e_destroy_indirect_tirs(struct mlx5e_priv *priv) { int i; @@ -3794,9 +3809,9 @@ u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout) return MLX5_CAP_ETH(mdev, lro_timer_supported_periods[i]); } -static void mlx5e_build_nic_params(struct mlx5_core_dev *mdev, - struct mlx5e_params *params, - u16 max_channels) +void mlx5e_build_nic_params(struct mlx5_core_dev *mdev, + struct mlx5e_params *params, + u16 max_channels) { u8 cq_period_mode = 0; u32 link_speed = 0; @@ -4031,31 +4046,22 @@ static int mlx5e_init_nic_rx(struct mlx5e_priv *priv) { struct mlx5_core_dev *mdev = priv->mdev; int err; - int i; - err = mlx5e_create_indirect_rqts(priv); - if (err) { - mlx5_core_warn(mdev, "create indirect rqts failed, %d\n", err); + err = mlx5e_create_indirect_rqt(priv); + if (err) return err; - } err = mlx5e_create_direct_rqts(priv); - if (err) { - mlx5_core_warn(mdev, "create direct rqts failed, %d\n", err); + if (err) goto err_destroy_indirect_rqts; - } err = mlx5e_create_indirect_tirs(priv); - if (err) { - mlx5_core_warn(mdev, "create indirect tirs failed, %d\n", err); + if (err) goto err_destroy_direct_rqts; - } err = mlx5e_create_direct_tirs(priv); - if (err) { - mlx5_core_warn(mdev, "create direct tirs failed, %d\n", err); + if (err) goto err_destroy_indirect_tirs; - } err = mlx5e_create_flow_steering(priv); if (err) { @@ -4076,8 +4082,7 @@ err_destroy_direct_tirs: err_destroy_indirect_tirs: mlx5e_destroy_indirect_tirs(priv); err_destroy_direct_rqts: - for (i = 0; i < priv->profile->max_nch(mdev); i++) - mlx5e_destroy_rqt(priv, &priv->direct_tir[i].rqt); + mlx5e_destroy_direct_rqts(priv); err_destroy_indirect_rqts: mlx5e_destroy_rqt(priv, &priv->indir_rqt); return err; @@ -4085,14 +4090,11 @@ err_destroy_indirect_rqts: static void mlx5e_cleanup_nic_rx(struct mlx5e_priv *priv) { - int i; - mlx5e_tc_cleanup(priv); mlx5e_destroy_flow_steering(priv); mlx5e_destroy_direct_tirs(priv); mlx5e_destroy_indirect_tirs(priv); - for (i = 0; i < priv->profile->max_nch(priv->mdev); i++) - mlx5e_destroy_rqt(priv, &priv->direct_tir[i].rqt); + mlx5e_destroy_direct_rqts(priv); mlx5e_destroy_rqt(priv, &priv->indir_rqt); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index d7664ff5c736..da85b0ad3e92 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -465,24 +465,18 @@ static int mlx5e_init_rep_rx(struct mlx5e_priv *priv) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5_eswitch_rep *rep = priv->ppriv; - struct mlx5_core_dev *mdev = priv->mdev; struct mlx5_flow_handle *flow_rule; int err; - int i; mlx5e_init_l2_addr(priv); err = mlx5e_create_direct_rqts(priv); - if (err) { - mlx5_core_warn(mdev, "create direct rqts failed, %d\n", err); + if (err) return err; - } err = mlx5e_create_direct_tirs(priv); - if (err) { - mlx5_core_warn(mdev, "create direct tirs failed, %d\n", err); + if (err) goto err_destroy_direct_rqts; - } flow_rule = mlx5_eswitch_create_vport_rx_rule(esw, rep->vport, @@ -504,21 +498,18 @@ err_del_flow_rule: err_destroy_direct_tirs: mlx5e_destroy_direct_tirs(priv); err_destroy_direct_rqts: - for (i = 0; i < priv->channels.params.num_channels; i++) - mlx5e_destroy_rqt(priv, &priv->direct_tir[i].rqt); + mlx5e_destroy_direct_rqts(priv); return err; } static void mlx5e_cleanup_rep_rx(struct mlx5e_priv *priv) { struct mlx5_eswitch_rep *rep = priv->ppriv; - int i; mlx5e_tc_cleanup(priv); mlx5_del_flow_rules(rep->vport_rx_rule); mlx5e_destroy_direct_tirs(priv); - for (i = 0; i < priv->channels.params.num_channels; i++) - mlx5e_destroy_rqt(priv, &priv->direct_tir[i].rqt); + mlx5e_destroy_direct_rqts(priv); } static int mlx5e_init_rep_tx(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c index 2f65927a8d03..f0318920844e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c @@ -44,8 +44,15 @@ static void mlx5i_init(struct mlx5_core_dev *mdev, { struct mlx5e_priv *priv = mlx5i_epriv(netdev); - priv->ppriv = ppriv; - /* TODO: init netdev and mlx5e_params here */ + priv->mdev = mdev; + priv->netdev = netdev; + priv->profile = profile; + priv->ppriv = ppriv; + + mlx5e_build_nic_params(mdev, &priv->channels.params, profile->max_nch(mdev)); + + mutex_init(&priv->state_lock); + /* TODO : init netdev features here */ } /* Called directly before IPoIB netdevice is destroyed to cleanup SW structs */ @@ -67,12 +74,41 @@ static void mlx5i_cleanup_tx(struct mlx5e_priv *priv) static int mlx5i_init_rx(struct mlx5e_priv *priv) { - /* TODO: create IPoIB RX HW steering contexts */ + int err; + + err = mlx5e_create_indirect_rqt(priv); + if (err) + return err; + + err = mlx5e_create_direct_rqts(priv); + if (err) + goto err_destroy_indirect_rqts; + + err = mlx5e_create_indirect_tirs(priv); + if (err) + goto err_destroy_direct_rqts; + + err = mlx5e_create_direct_tirs(priv); + if (err) + goto err_destroy_indirect_tirs; + return 0; + +err_destroy_indirect_tirs: + mlx5e_destroy_indirect_tirs(priv); +err_destroy_direct_rqts: + mlx5e_destroy_direct_rqts(priv); +err_destroy_indirect_rqts: + mlx5e_destroy_rqt(priv, &priv->indir_rqt); + return err; } static void mlx5i_cleanup_rx(struct mlx5e_priv *priv) { + mlx5e_destroy_direct_tirs(priv); + mlx5e_destroy_indirect_tirs(priv); + mlx5e_destroy_direct_rqts(priv); + mlx5e_destroy_rqt(priv, &priv->indir_rqt); } static const struct mlx5e_profile mlx5i_nic_profile = { -- cgit From bc81b9d3267c4d23d829e9af88b21eb46d9d388b Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 13 Apr 2017 06:36:57 +0300 Subject: net/mlx5e: IPoIB, RSS flow steering tables Like the mlx5e ethernet mode, on IPoIB mode we need to create RX steering tables, but IPoIB do not require MAC and VLAN steering tables so the only tables we create in here are: 1. TTC Table (Traffic Type Classifier table for RSS steering) 2. ARFS Table (for accelerated RFS support) Creation of those tables is identical to mlx5e ethernet mode, hence the use of mlx5e_create_ttc_table and mlx5e_arfs_create_tables. Signed-off-by: Saeed Mahameed Reviewed-by: Erez Shitrit Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 4 +++ drivers/net/ethernet/mellanox/mlx5/core/en_fs.c | 7 ++-- drivers/net/ethernet/mellanox/mlx5/core/ipoib.c | 46 +++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index e5518536d56f..c813eab5d764 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -999,6 +999,7 @@ int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr); void mlx5e_handle_rx_cqe_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe); void mlx5e_update_hw_rep_counters(struct mlx5e_priv *priv); +/* common netdev helpers */ int mlx5e_create_indirect_rqt(struct mlx5e_priv *priv); int mlx5e_create_indirect_tirs(struct mlx5e_priv *priv); @@ -1010,6 +1011,9 @@ int mlx5e_create_direct_tirs(struct mlx5e_priv *priv); void mlx5e_destroy_direct_tirs(struct mlx5e_priv *priv); void mlx5e_destroy_rqt(struct mlx5e_priv *priv, struct mlx5e_rqt *rqt); +int mlx5e_create_ttc_table(struct mlx5e_priv *priv, u32 underlay_qpn); +void mlx5e_destroy_ttc_table(struct mlx5e_priv *priv); + int mlx5e_create_tises(struct mlx5e_priv *priv); void mlx5e_cleanup_nic_tx(struct mlx5e_priv *priv); int mlx5e_close(struct net_device *netdev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index 729904c43801..576d6787b484 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -792,7 +792,7 @@ err: return err; } -static void mlx5e_destroy_ttc_table(struct mlx5e_priv *priv) +void mlx5e_destroy_ttc_table(struct mlx5e_priv *priv) { struct mlx5e_ttc_table *ttc = &priv->fs.ttc; @@ -800,7 +800,7 @@ static void mlx5e_destroy_ttc_table(struct mlx5e_priv *priv) mlx5e_destroy_flow_table(&ttc->ft); } -static int mlx5e_create_ttc_table(struct mlx5e_priv *priv) +int mlx5e_create_ttc_table(struct mlx5e_priv *priv, u32 underlay_qpn) { struct mlx5e_ttc_table *ttc = &priv->fs.ttc; struct mlx5_flow_table_attr ft_attr = {}; @@ -810,6 +810,7 @@ static int mlx5e_create_ttc_table(struct mlx5e_priv *priv) ft_attr.max_fte = MLX5E_TTC_TABLE_SIZE; ft_attr.level = MLX5E_TTC_FT_LEVEL; ft_attr.prio = MLX5E_NIC_PRIO; + ft_attr.underlay_qpn = underlay_qpn; ft->t = mlx5_create_flow_table(priv->fs.ns, &ft_attr); if (IS_ERR(ft->t)) { @@ -1146,7 +1147,7 @@ int mlx5e_create_flow_steering(struct mlx5e_priv *priv) priv->netdev->hw_features &= ~NETIF_F_NTUPLE; } - err = mlx5e_create_ttc_table(priv); + err = mlx5e_create_ttc_table(priv, 0); if (err) { netdev_err(priv->netdev, "Failed to create ttc table, err=%d\n", err); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c index f0318920844e..e16e1c7b246e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c @@ -72,6 +72,45 @@ static void mlx5i_cleanup_tx(struct mlx5e_priv *priv) { } +static int mlx5i_create_flow_steering(struct mlx5e_priv *priv) +{ + struct mlx5i_priv *ipriv = priv->ppriv; + int err; + + priv->fs.ns = mlx5_get_flow_namespace(priv->mdev, + MLX5_FLOW_NAMESPACE_KERNEL); + + if (!priv->fs.ns) + return -EINVAL; + + err = mlx5e_arfs_create_tables(priv); + if (err) { + netdev_err(priv->netdev, "Failed to create arfs tables, err=%d\n", + err); + priv->netdev->hw_features &= ~NETIF_F_NTUPLE; + } + + err = mlx5e_create_ttc_table(priv, ipriv->qp.qpn); + if (err) { + netdev_err(priv->netdev, "Failed to create ttc table, err=%d\n", + err); + goto err_destroy_arfs_tables; + } + + return 0; + +err_destroy_arfs_tables: + mlx5e_arfs_destroy_tables(priv); + + return err; +} + +static void mlx5i_destroy_flow_steering(struct mlx5e_priv *priv) +{ + mlx5e_destroy_ttc_table(priv); + mlx5e_arfs_destroy_tables(priv); +} + static int mlx5i_init_rx(struct mlx5e_priv *priv) { int err; @@ -92,8 +131,14 @@ static int mlx5i_init_rx(struct mlx5e_priv *priv) if (err) goto err_destroy_indirect_tirs; + err = mlx5i_create_flow_steering(priv); + if (err) + goto err_destroy_direct_tirs; + return 0; +err_destroy_direct_tirs: + mlx5e_destroy_direct_tirs(priv); err_destroy_indirect_tirs: mlx5e_destroy_indirect_tirs(priv); err_destroy_direct_rqts: @@ -105,6 +150,7 @@ err_destroy_indirect_rqts: static void mlx5i_cleanup_rx(struct mlx5e_priv *priv) { + mlx5i_destroy_flow_steering(priv); mlx5e_destroy_direct_tirs(priv); mlx5e_destroy_indirect_tirs(priv); mlx5e_destroy_direct_rqts(priv); -- cgit From 5426a0b2746a629eed8608397a385f34081f3f66 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 13 Apr 2017 06:36:58 +0300 Subject: net/mlx5e: IPoIB, TX TIS creation Modify mlx5e tis creation function to accept underlay qp number, which will be needed by IPoIB. Implement mlx5i (IPoIB) tx init/cleanup netdevice profile flows to create one TIS with the IPoIB underlay qp, for IPoIB TX SQs. Signed-off-by: Saeed Mahameed Reviewed-by: Erez Shitrit Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 4 ++++ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 18 ++++++++++-------- drivers/net/ethernet/mellanox/mlx5/core/ipoib.c | 14 ++++++++++++-- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index c813eab5d764..5345d875b695 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -1014,6 +1014,10 @@ void mlx5e_destroy_rqt(struct mlx5e_priv *priv, struct mlx5e_rqt *rqt); int mlx5e_create_ttc_table(struct mlx5e_priv *priv, u32 underlay_qpn); void mlx5e_destroy_ttc_table(struct mlx5e_priv *priv); +int mlx5e_create_tis(struct mlx5_core_dev *mdev, int tc, + u32 underlay_qpn, u32 *tisn); +void mlx5e_destroy_tis(struct mlx5_core_dev *mdev, u32 tisn); + int mlx5e_create_tises(struct mlx5e_priv *priv); void mlx5e_cleanup_nic_tx(struct mlx5e_priv *priv); int mlx5e_close(struct net_device *netdev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 08b67aa24644..1fde4e2301a4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2759,24 +2759,25 @@ static void mlx5e_close_drop_rq(struct mlx5e_rq *drop_rq) mlx5e_free_cq(&drop_rq->cq); } -static int mlx5e_create_tis(struct mlx5e_priv *priv, int tc) +int mlx5e_create_tis(struct mlx5_core_dev *mdev, int tc, + u32 underlay_qpn, u32 *tisn) { - struct mlx5_core_dev *mdev = priv->mdev; u32 in[MLX5_ST_SZ_DW(create_tis_in)] = {0}; void *tisc = MLX5_ADDR_OF(create_tis_in, in, ctx); MLX5_SET(tisc, tisc, prio, tc << 1); + MLX5_SET(tisc, tisc, underlay_qpn, underlay_qpn); MLX5_SET(tisc, tisc, transport_domain, mdev->mlx5e_res.td.tdn); if (mlx5_lag_is_lacp_owner(mdev)) MLX5_SET(tisc, tisc, strict_lag_tx_port_affinity, 1); - return mlx5_core_create_tis(mdev, in, sizeof(in), &priv->tisn[tc]); + return mlx5_core_create_tis(mdev, in, sizeof(in), tisn); } -static void mlx5e_destroy_tis(struct mlx5e_priv *priv, int tc) +void mlx5e_destroy_tis(struct mlx5_core_dev *mdev, u32 tisn) { - mlx5_core_destroy_tis(priv->mdev, priv->tisn[tc]); + mlx5_core_destroy_tis(mdev, tisn); } int mlx5e_create_tises(struct mlx5e_priv *priv) @@ -2785,7 +2786,7 @@ int mlx5e_create_tises(struct mlx5e_priv *priv) int tc; for (tc = 0; tc < priv->profile->max_tc; tc++) { - err = mlx5e_create_tis(priv, tc); + err = mlx5e_create_tis(priv->mdev, tc, 0, &priv->tisn[tc]); if (err) goto err_close_tises; } @@ -2794,7 +2795,7 @@ int mlx5e_create_tises(struct mlx5e_priv *priv) err_close_tises: for (tc--; tc >= 0; tc--) - mlx5e_destroy_tis(priv, tc); + mlx5e_destroy_tis(priv->mdev, priv->tisn[tc]); return err; } @@ -2804,7 +2805,7 @@ void mlx5e_cleanup_nic_tx(struct mlx5e_priv *priv) int tc; for (tc = 0; tc < priv->profile->max_tc; tc++) - mlx5e_destroy_tis(priv, tc); + mlx5e_destroy_tis(priv->mdev, priv->tisn[tc]); } static void mlx5e_build_indir_tir_ctx(struct mlx5e_priv *priv, @@ -3841,6 +3842,7 @@ void mlx5e_build_nic_params(struct mlx5_core_dev *mdev, mlx5e_set_rq_params(mdev, params); /* HW LRO */ + /* TODO: && MLX5_CAP_ETH(mdev, lro_cap) */ if (params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) params->lro_en = true; params->lro_timeout = mlx5e_choose_lro_timeout(mdev, MLX5E_DEFAULT_LRO_TIMEOUT); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c index e16e1c7b246e..d7d705c840ae 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c @@ -63,13 +63,23 @@ static void mlx5i_cleanup(struct mlx5e_priv *priv) static int mlx5i_init_tx(struct mlx5e_priv *priv) { + struct mlx5i_priv *ipriv = priv->ppriv; + int err; + /* TODO: Create IPoIB underlay QP */ - /* TODO: create IPoIB TX HW TIS */ + + err = mlx5e_create_tis(priv->mdev, 0 /* tc */, ipriv->qp.qpn, &priv->tisn[0]); + if (err) { + mlx5_core_warn(priv->mdev, "create tis failed, %d\n", err); + return err; + } + return 0; } -static void mlx5i_cleanup_tx(struct mlx5e_priv *priv) +void mlx5i_cleanup_tx(struct mlx5e_priv *priv) { + mlx5e_destroy_tis(priv->mdev, priv->tisn[0]); } static int mlx5i_create_flow_steering(struct mlx5e_priv *priv) -- cgit From 603f4a45214d602f27749ad8b3bb6bd767dac2d5 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 13 Apr 2017 06:36:59 +0300 Subject: net/mlx5e: IPoIB, Basic netdev ndos open/close Implement open/close of IPoIB netdevice ndos using mlx5e's channels API to manage data path resources (RQs/SQs/CQs). Set IPoIB netdev address on dev_init ndo. Signed-off-by: Saeed Mahameed Reviewed-by: Erez Shitrit Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 + drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 +- drivers/net/ethernet/mellanox/mlx5/core/ipoib.c | 90 ++++++++++++++++++++++- 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 5345d875b695..23b92ec54e12 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -883,6 +883,8 @@ typedef int (*mlx5e_fp_hw_modify)(struct mlx5e_priv *priv); void mlx5e_switch_priv_channels(struct mlx5e_priv *priv, struct mlx5e_channels *new_chs, mlx5e_fp_hw_modify hw_modify); +void mlx5e_activate_priv_channels(struct mlx5e_priv *priv); +void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv); void mlx5e_build_default_indir_rqt(struct mlx5_core_dev *mdev, u32 *indirection_rqt, int len, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 1fde4e2301a4..eb657987e9b5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2547,7 +2547,7 @@ static void mlx5e_build_channels_tx_maps(struct mlx5e_priv *priv) } } -static void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) +void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) { int num_txqs = priv->channels.num * priv->channels.params.num_tc; struct net_device *netdev = priv->netdev; @@ -2567,7 +2567,7 @@ static void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) mlx5e_redirect_rqts_to_channels(priv, &priv->channels); } -static void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv) +void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv) { mlx5e_redirect_rqts_to_drop(priv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c index d7d705c840ae..e188d067bc97 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c @@ -34,6 +34,18 @@ #include "en.h" #include "ipoib.h" +static int mlx5i_open(struct net_device *netdev); +static int mlx5i_close(struct net_device *netdev); +static int mlx5i_dev_init(struct net_device *dev); +static void mlx5i_dev_cleanup(struct net_device *dev); + +static const struct net_device_ops mlx5i_netdev_ops = { + .ndo_open = mlx5i_open, + .ndo_stop = mlx5i_close, + .ndo_init = mlx5i_dev_init, + .ndo_uninit = mlx5i_dev_cleanup, +}; + /* IPoIB mlx5 netdev profile */ /* Called directly after IPoIB netdevice was created to initialize SW structs */ @@ -52,7 +64,17 @@ static void mlx5i_init(struct mlx5_core_dev *mdev, mlx5e_build_nic_params(mdev, &priv->channels.params, profile->max_nch(mdev)); mutex_init(&priv->state_lock); - /* TODO : init netdev features here */ + + netdev->hw_features |= NETIF_F_SG; + netdev->hw_features |= NETIF_F_IP_CSUM; + netdev->hw_features |= NETIF_F_IPV6_CSUM; + netdev->hw_features |= NETIF_F_GRO; + netdev->hw_features |= NETIF_F_TSO; + netdev->hw_features |= NETIF_F_TSO6; + netdev->hw_features |= NETIF_F_RXCSUM; + netdev->hw_features |= NETIF_F_RXHASH; + + netdev->netdev_ops = &mlx5i_netdev_ops; } /* Called directly before IPoIB netdevice is destroyed to cleanup SW structs */ @@ -181,6 +203,72 @@ static const struct mlx5e_profile mlx5i_nic_profile = { .max_tc = MLX5I_MAX_NUM_TC, }; +/* mlx5i netdev NDos */ + +static int mlx5i_dev_init(struct net_device *dev) +{ + struct mlx5e_priv *priv = mlx5i_epriv(dev); + struct mlx5i_priv *ipriv = priv->ppriv; + + /* Set dev address using underlay QP */ + dev->dev_addr[1] = (ipriv->qp.qpn >> 16) & 0xff; + dev->dev_addr[2] = (ipriv->qp.qpn >> 8) & 0xff; + dev->dev_addr[3] = (ipriv->qp.qpn) & 0xff; + + return 0; +} + +static void mlx5i_dev_cleanup(struct net_device *dev) +{ + /* TODO: detach underlay qp from flow-steering by reset it */ +} + +static int mlx5i_open(struct net_device *netdev) +{ + struct mlx5e_priv *priv = mlx5i_epriv(netdev); + int err; + + mutex_lock(&priv->state_lock); + + set_bit(MLX5E_STATE_OPENED, &priv->state); + + err = mlx5e_open_channels(priv, &priv->channels); + if (err) + goto err_clear_state_opened_flag; + + mlx5e_refresh_tirs(priv, false); + mlx5e_activate_priv_channels(priv); + mutex_unlock(&priv->state_lock); + return 0; + +err_clear_state_opened_flag: + clear_bit(MLX5E_STATE_OPENED, &priv->state); + mutex_unlock(&priv->state_lock); + return err; +} + +static int mlx5i_close(struct net_device *netdev) +{ + struct mlx5e_priv *priv = mlx5i_epriv(netdev); + + /* May already be CLOSED in case a previous configuration operation + * (e.g RX/TX queue size change) that involves close&open failed. + */ + mutex_lock(&priv->state_lock); + + if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) + goto unlock; + + clear_bit(MLX5E_STATE_OPENED, &priv->state); + + netif_carrier_off(priv->netdev); + mlx5e_deactivate_priv_channels(priv); + mlx5e_close_channels(&priv->channels); +unlock: + mutex_unlock(&priv->state_lock); + return 0; +} + /* IPoIB RDMA netdev callbacks */ static int mlx5i_check_required_hca_cap(struct mlx5_core_dev *mdev) -- cgit From ec8fd927b7a9006425f34dc51880e9fed582c4a7 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 13 Apr 2017 06:37:00 +0300 Subject: net/mlx5e: IPoIB, Underlay QP Create IPoIB underlay QP needed by the IPoIB netdevice profile for RSS and TX HW context to perform on IPoIB traffic. Reset the underlay QP on dev_uninit ndo to stop IPoIB traffic going through this QP when the ULP IPoIB decides to cleanup. Implement attach/detach mcast RDMA netdev callbacks for later RDMA netdev use. Signed-off-by: Saeed Mahameed Reviewed-by: Erez Shitrit Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/ipoib.c | 126 +++++++++++++++++++++++- 1 file changed, 124 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c index e188d067bc97..bd56f36066b3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c @@ -34,6 +34,8 @@ #include "en.h" #include "ipoib.h" +#define IB_DEFAULT_Q_KEY 0xb1b + static int mlx5i_open(struct net_device *netdev); static int mlx5i_close(struct net_device *netdev); static int mlx5i_dev_init(struct net_device *dev); @@ -83,12 +85,89 @@ static void mlx5i_cleanup(struct mlx5e_priv *priv) /* Do nothing .. */ } +#define MLX5_QP_ENHANCED_ULP_STATELESS_MODE 2 + +static int mlx5i_create_underlay_qp(struct mlx5_core_dev *mdev, struct mlx5_core_qp *qp) +{ + struct mlx5_qp_context *context = NULL; + u32 *in = NULL; + void *addr_path; + int ret = 0; + int inlen; + void *qpc; + + inlen = MLX5_ST_SZ_BYTES(create_qp_in); + in = mlx5_vzalloc(inlen); + if (!in) + return -ENOMEM; + + qpc = MLX5_ADDR_OF(create_qp_in, in, qpc); + MLX5_SET(qpc, qpc, st, MLX5_QP_ST_UD); + MLX5_SET(qpc, qpc, pm_state, MLX5_QP_PM_MIGRATED); + MLX5_SET(qpc, qpc, ulp_stateless_offload_mode, + MLX5_QP_ENHANCED_ULP_STATELESS_MODE); + + addr_path = MLX5_ADDR_OF(qpc, qpc, primary_address_path); + MLX5_SET(ads, addr_path, port, 1); + MLX5_SET(ads, addr_path, grh, 1); + + ret = mlx5_core_create_qp(mdev, qp, in, inlen); + if (ret) { + mlx5_core_err(mdev, "Failed creating IPoIB QP err : %d\n", ret); + goto out; + } + + /* QP states */ + context = kzalloc(sizeof(*context), GFP_KERNEL); + if (!context) { + ret = -ENOMEM; + goto out; + } + + context->flags = cpu_to_be32(MLX5_QP_PM_MIGRATED << 11); + context->pri_path.port = 1; + context->qkey = cpu_to_be32(IB_DEFAULT_Q_KEY); + + ret = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_RST2INIT_QP, 0, context, qp); + if (ret) { + mlx5_core_err(mdev, "Failed to modify qp RST2INIT, err: %d\n", ret); + goto out; + } + memset(context, 0, sizeof(*context)); + + ret = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_INIT2RTR_QP, 0, context, qp); + if (ret) { + mlx5_core_err(mdev, "Failed to modify qp INIT2RTR, err: %d\n", ret); + goto out; + } + + ret = mlx5_core_qp_modify(mdev, MLX5_CMD_OP_RTR2RTS_QP, 0, context, qp); + if (ret) { + mlx5_core_err(mdev, "Failed to modify qp RTR2RTS, err: %d\n", ret); + goto out; + } + +out: + kfree(context); + kvfree(in); + return ret; +} + +static void mlx5i_destroy_underlay_qp(struct mlx5_core_dev *mdev, struct mlx5_core_qp *qp) +{ + mlx5_core_destroy_qp(mdev, qp); +} + static int mlx5i_init_tx(struct mlx5e_priv *priv) { struct mlx5i_priv *ipriv = priv->ppriv; int err; - /* TODO: Create IPoIB underlay QP */ + err = mlx5i_create_underlay_qp(priv->mdev, &ipriv->qp); + if (err) { + mlx5_core_warn(priv->mdev, "create underlay QP failed, %d\n", err); + return err; + } err = mlx5e_create_tis(priv->mdev, 0 /* tc */, ipriv->qp.qpn, &priv->tisn[0]); if (err) { @@ -101,7 +180,10 @@ static int mlx5i_init_tx(struct mlx5e_priv *priv) void mlx5i_cleanup_tx(struct mlx5e_priv *priv) { + struct mlx5i_priv *ipriv = priv->ppriv; + mlx5e_destroy_tis(priv->mdev, priv->tisn[0]); + mlx5i_destroy_underlay_qp(priv->mdev, &ipriv->qp); } static int mlx5i_create_flow_steering(struct mlx5e_priv *priv) @@ -220,7 +302,13 @@ static int mlx5i_dev_init(struct net_device *dev) static void mlx5i_dev_cleanup(struct net_device *dev) { - /* TODO: detach underlay qp from flow-steering by reset it */ + struct mlx5e_priv *priv = mlx5i_epriv(dev); + struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5i_priv *ipriv = priv->ppriv; + struct mlx5_qp_context context; + + /* detach qp from flow-steering by reset it */ + mlx5_core_qp_modify(mdev, MLX5_CMD_OP_2RST_QP, 0, &context, &ipriv->qp); } static int mlx5i_open(struct net_device *netdev) @@ -270,6 +358,40 @@ unlock: } /* IPoIB RDMA netdev callbacks */ +int mlx5i_attach_mcast(struct net_device *netdev, struct ib_device *hca, + union ib_gid *gid, u16 lid, int set_qkey) +{ + struct mlx5e_priv *epriv = mlx5i_epriv(netdev); + struct mlx5_core_dev *mdev = epriv->mdev; + struct mlx5i_priv *ipriv = epriv->ppriv; + int err; + + mlx5_core_dbg(mdev, "attaching QPN 0x%x, MGID %pI6\n", ipriv->qp.qpn, gid->raw); + err = mlx5_core_attach_mcg(mdev, gid, ipriv->qp.qpn); + if (err) + mlx5_core_warn(mdev, "failed attaching QPN 0x%x, MGID %pI6\n", + ipriv->qp.qpn, gid->raw); + + return err; +} + +int mlx5i_detach_mcast(struct net_device *netdev, struct ib_device *hca, + union ib_gid *gid, u16 lid) +{ + struct mlx5e_priv *epriv = mlx5i_epriv(netdev); + struct mlx5_core_dev *mdev = epriv->mdev; + struct mlx5i_priv *ipriv = epriv->ppriv; + int err; + + mlx5_core_dbg(mdev, "detaching QPN 0x%x, MGID %pI6\n", ipriv->qp.qpn, gid->raw); + + err = mlx5_core_detach_mcg(mdev, gid, ipriv->qp.qpn); + if (err) + mlx5_core_dbg(mdev, "failed dettaching QPN 0x%x, MGID %pI6\n", + ipriv->qp.qpn, gid->raw); + + return err; +} static int mlx5i_check_required_hca_cap(struct mlx5_core_dev *mdev) { -- cgit From 77bdf8950b3cd17c780b4e5a2803806a9f573f51 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 13 Apr 2017 06:37:01 +0300 Subject: net/mlx5e: Xmit flow break down Break current mlx5e xmit flow into smaller blocks (helper functions) in order to reuse them for IPoIB SKB transmission. Signed-off-by: Saeed Mahameed Reviewed-by: Erez Shitrit Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 7 +- drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 199 +++++++++++++--------- 3 files changed, 119 insertions(+), 89 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 23b92ec54e12..25185f8c3562 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -304,6 +304,7 @@ struct mlx5e_cq { } ____cacheline_aligned_in_smp; struct mlx5e_tx_wqe_info { + struct sk_buff *skb; u32 num_bytes; u8 num_wqebbs; u8 num_dma; @@ -345,7 +346,6 @@ struct mlx5e_txqsq { /* write@xmit, read@completion */ struct { - struct sk_buff **skb; struct mlx5e_sq_dma *dma_fifo; struct mlx5e_tx_wqe_info *wqe_info; } db; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index eb657987e9b5..2201b7ea05f4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1042,7 +1042,6 @@ static void mlx5e_free_txqsq_db(struct mlx5e_txqsq *sq) { kfree(sq->db.wqe_info); kfree(sq->db.dma_fifo); - kfree(sq->db.skb); } static int mlx5e_alloc_txqsq_db(struct mlx5e_txqsq *sq, int numa) @@ -1050,13 +1049,11 @@ static int mlx5e_alloc_txqsq_db(struct mlx5e_txqsq *sq, int numa) int wq_sz = mlx5_wq_cyc_get_size(&sq->wq); int df_sz = wq_sz * MLX5_SEND_WQEBB_NUM_DS; - sq->db.skb = kzalloc_node(wq_sz * sizeof(*sq->db.skb), - GFP_KERNEL, numa); sq->db.dma_fifo = kzalloc_node(df_sz * sizeof(*sq->db.dma_fifo), GFP_KERNEL, numa); sq->db.wqe_info = kzalloc_node(wq_sz * sizeof(*sq->db.wqe_info), GFP_KERNEL, numa); - if (!sq->db.skb || !sq->db.dma_fifo || !sq->db.wqe_info) { + if (!sq->db.dma_fifo || !sq->db.wqe_info) { mlx5e_free_txqsq_db(sq); return -ENOMEM; } @@ -1295,7 +1292,7 @@ static void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq) if (mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, 1)) { struct mlx5e_tx_wqe *nop; - sq->db.skb[(sq->pc & sq->wq.sz_m1)] = NULL; + sq->db.wqe_info[(sq->pc & sq->wq.sz_m1)].skb = NULL; nop = mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &nop->ctrl); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 5bbc313e70c5..ba664a1126cf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -177,30 +177,9 @@ static inline void mlx5e_insert_vlan(void *start, struct sk_buff *skb, u16 ihs, mlx5e_tx_skb_pull_inline(skb_data, skb_len, cpy2_sz); } -static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb) +static inline void +mlx5e_txwqe_build_eseg_csum(struct mlx5e_txqsq *sq, struct sk_buff *skb, struct mlx5_wqe_eth_seg *eseg) { - struct mlx5_wq_cyc *wq = &sq->wq; - - u16 pi = sq->pc & wq->sz_m1; - struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); - struct mlx5e_tx_wqe_info *wi = &sq->db.wqe_info[pi]; - - struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; - struct mlx5_wqe_eth_seg *eseg = &wqe->eth; - struct mlx5_wqe_data_seg *dseg; - - unsigned char *skb_data = skb->data; - unsigned int skb_len = skb->len; - u8 opcode = MLX5_OPCODE_SEND; - dma_addr_t dma_addr = 0; - unsigned int num_bytes; - u16 headlen; - u16 ds_cnt; - u16 ihs; - int i; - - memset(wqe, 0, sizeof(*wqe)); - if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { eseg->cs_flags = MLX5_ETH_WQE_L3_CSUM; if (skb->encapsulation) { @@ -212,66 +191,51 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb) } } else sq->stats.csum_none++; +} - if (skb_is_gso(skb)) { - eseg->mss = cpu_to_be16(skb_shinfo(skb)->gso_size); - opcode = MLX5_OPCODE_LSO; +static inline u16 +mlx5e_txwqe_build_eseg_gso(struct mlx5e_txqsq *sq, struct sk_buff *skb, + struct mlx5_wqe_eth_seg *eseg, unsigned int *num_bytes) +{ + u16 ihs; - if (skb->encapsulation) { - ihs = skb_inner_transport_offset(skb) + inner_tcp_hdrlen(skb); - sq->stats.tso_inner_packets++; - sq->stats.tso_inner_bytes += skb->len - ihs; - } else { - ihs = skb_transport_offset(skb) + tcp_hdrlen(skb); - sq->stats.tso_packets++; - sq->stats.tso_bytes += skb->len - ihs; - } + eseg->mss = cpu_to_be16(skb_shinfo(skb)->gso_size); - sq->stats.packets += skb_shinfo(skb)->gso_segs; - num_bytes = skb->len + (skb_shinfo(skb)->gso_segs - 1) * ihs; + if (skb->encapsulation) { + ihs = skb_inner_transport_offset(skb) + inner_tcp_hdrlen(skb); + sq->stats.tso_inner_packets++; + sq->stats.tso_inner_bytes += skb->len - ihs; } else { - ihs = mlx5e_calc_min_inline(sq->min_inline_mode, skb); - sq->stats.packets++; - num_bytes = max_t(unsigned int, skb->len, ETH_ZLEN); + ihs = skb_transport_offset(skb) + tcp_hdrlen(skb); + sq->stats.tso_packets++; + sq->stats.tso_bytes += skb->len - ihs; } - sq->stats.bytes += num_bytes; - wi->num_bytes = num_bytes; - - ds_cnt = sizeof(*wqe) / MLX5_SEND_WQE_DS; - if (ihs) { - if (skb_vlan_tag_present(skb)) { - mlx5e_insert_vlan(eseg->inline_hdr.start, skb, ihs, &skb_data, &skb_len); - ihs += VLAN_HLEN; - } else { - memcpy(eseg->inline_hdr.start, skb_data, ihs); - mlx5e_tx_skb_pull_inline(&skb_data, &skb_len, ihs); - } - eseg->inline_hdr.sz = cpu_to_be16(ihs); - ds_cnt += DIV_ROUND_UP(ihs - sizeof(eseg->inline_hdr.start), MLX5_SEND_WQE_DS); - } else if (skb_vlan_tag_present(skb)) { - eseg->insert.type = cpu_to_be16(MLX5_ETH_WQE_INSERT_VLAN); - eseg->insert.vlan_tci = cpu_to_be16(skb_vlan_tag_get(skb)); - } - - dseg = (struct mlx5_wqe_data_seg *)cseg + ds_cnt; + *num_bytes = skb->len + (skb_shinfo(skb)->gso_segs - 1) * ihs; + return ihs; +} - wi->num_dma = 0; +static inline int +mlx5e_txwqe_build_dsegs(struct mlx5e_txqsq *sq, struct sk_buff *skb, + unsigned char *skb_data, u16 headlen, + struct mlx5_wqe_data_seg *dseg) +{ + dma_addr_t dma_addr = 0; + u8 num_dma = 0; + int i; - headlen = skb_len - skb->data_len; if (headlen) { dma_addr = dma_map_single(sq->pdev, skb_data, headlen, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(sq->pdev, dma_addr))) - goto dma_unmap_wqe_err; + return -ENOMEM; dseg->addr = cpu_to_be64(dma_addr); dseg->lkey = sq->mkey_be; dseg->byte_count = cpu_to_be32(headlen); mlx5e_dma_push(sq, dma_addr, headlen, MLX5E_DMA_MAP_SINGLE); - wi->num_dma++; - + num_dma++; dseg++; } @@ -280,51 +244,120 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb) int fsz = skb_frag_size(frag); dma_addr = skb_frag_dma_map(sq->pdev, frag, 0, fsz, - DMA_TO_DEVICE); + DMA_TO_DEVICE); if (unlikely(dma_mapping_error(sq->pdev, dma_addr))) - goto dma_unmap_wqe_err; + return -ENOMEM; dseg->addr = cpu_to_be64(dma_addr); dseg->lkey = sq->mkey_be; dseg->byte_count = cpu_to_be32(fsz); mlx5e_dma_push(sq, dma_addr, fsz, MLX5E_DMA_MAP_PAGE); - wi->num_dma++; - + num_dma++; dseg++; } - ds_cnt += wi->num_dma; - - cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | opcode); - cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt); + return num_dma; +} - sq->db.skb[pi] = skb; +static inline void +mlx5e_txwqe_complete(struct mlx5e_txqsq *sq, struct sk_buff *skb, + u8 opcode, u16 ds_cnt, u32 num_bytes, u8 num_dma, + struct mlx5e_tx_wqe_info *wi, struct mlx5_wqe_ctrl_seg *cseg) +{ + struct mlx5_wq_cyc *wq = &sq->wq; + u16 pi; + wi->num_bytes = num_bytes; + wi->num_dma = num_dma; wi->num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS); - sq->pc += wi->num_wqebbs; + wi->skb = skb; - netdev_tx_sent_queue(sq->txq, wi->num_bytes); + cseg->opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | opcode); + cseg->qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt); + + netdev_tx_sent_queue(sq->txq, num_bytes); if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; - if (unlikely(!mlx5e_wqc_has_room_for(&sq->wq, sq->cc, sq->pc, - MLX5E_SQ_STOP_ROOM))) { + sq->pc += wi->num_wqebbs; + if (unlikely(!mlx5e_wqc_has_room_for(wq, sq->cc, sq->pc, MLX5E_SQ_STOP_ROOM))) { netif_tx_stop_queue(sq->txq); sq->stats.stopped++; } - sq->stats.xmit_more += skb->xmit_more; if (!skb->xmit_more || netif_xmit_stopped(sq->txq)) mlx5e_notify_hw(wq, sq->pc, sq->uar_map, cseg); /* fill sq edge with nops to avoid wqe wrap around */ while ((pi = (sq->pc & wq->sz_m1)) > sq->edge) { - sq->db.skb[pi] = NULL; - mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); + sq->db.wqe_info[pi].skb = NULL; + mlx5e_post_nop(wq, sq->sqn, &sq->pc); sq->stats.nop++; } +} + +static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb) +{ + struct mlx5_wq_cyc *wq = &sq->wq; + + u16 pi = sq->pc & wq->sz_m1; + struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); + struct mlx5e_tx_wqe_info *wi = &sq->db.wqe_info[pi]; + + struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; + struct mlx5_wqe_eth_seg *eseg = &wqe->eth; + + unsigned char *skb_data = skb->data; + unsigned int skb_len = skb->len; + u8 opcode = MLX5_OPCODE_SEND; + unsigned int num_bytes; + int num_dma; + u16 headlen; + u16 ds_cnt; + u16 ihs; + + memset(wqe, 0, sizeof(*wqe)); + + mlx5e_txwqe_build_eseg_csum(sq, skb, eseg); + + if (skb_is_gso(skb)) { + opcode = MLX5_OPCODE_LSO; + ihs = mlx5e_txwqe_build_eseg_gso(sq, skb, eseg, &num_bytes); + sq->stats.packets += skb_shinfo(skb)->gso_segs; + } else { + ihs = mlx5e_calc_min_inline(sq->min_inline_mode, skb); + num_bytes = max_t(unsigned int, skb->len, ETH_ZLEN); + sq->stats.packets++; + } + sq->stats.bytes += num_bytes; + sq->stats.xmit_more += skb->xmit_more; + + ds_cnt = sizeof(*wqe) / MLX5_SEND_WQE_DS; + if (ihs) { + if (skb_vlan_tag_present(skb)) { + mlx5e_insert_vlan(eseg->inline_hdr.start, skb, ihs, &skb_data, &skb_len); + ihs += VLAN_HLEN; + } else { + memcpy(eseg->inline_hdr.start, skb_data, ihs); + mlx5e_tx_skb_pull_inline(&skb_data, &skb_len, ihs); + } + eseg->inline_hdr.sz = cpu_to_be16(ihs); + ds_cnt += DIV_ROUND_UP(ihs - sizeof(eseg->inline_hdr.start), MLX5_SEND_WQE_DS); + } else if (skb_vlan_tag_present(skb)) { + eseg->insert.type = cpu_to_be16(MLX5_ETH_WQE_INSERT_VLAN); + eseg->insert.vlan_tci = cpu_to_be16(skb_vlan_tag_get(skb)); + } + + headlen = skb_len - skb->data_len; + num_dma = mlx5e_txwqe_build_dsegs(sq, skb, skb_data, headlen, + (struct mlx5_wqe_data_seg *)cseg + ds_cnt); + if (unlikely(num_dma < 0)) + goto dma_unmap_wqe_err; + + mlx5e_txwqe_complete(sq, skb, opcode, ds_cnt + num_dma, + num_bytes, num_dma, wi, cseg); return NETDEV_TX_OK; @@ -392,8 +425,8 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) last_wqe = (sqcc == wqe_counter); ci = sqcc & sq->wq.sz_m1; - skb = sq->db.skb[ci]; wi = &sq->db.wqe_info[ci]; + skb = wi->skb; if (unlikely(!skb)) { /* nop */ sqcc++; @@ -451,8 +484,8 @@ void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq) while (sq->cc != sq->pc) { ci = sq->cc & sq->wq.sz_m1; - skb = sq->db.skb[ci]; wi = &sq->db.wqe_info[ci]; + skb = wi->skb; if (!skb) { /* nop */ sq->cc++; -- cgit From 258545449b7b410727b516b782256f8a3bde8bf2 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 13 Apr 2017 06:37:02 +0300 Subject: net/mlx5e: IPoIB, Xmit flow Implement mlx5e's IPoIB SKB transmit using the helper functions provided by mlx5e ethernet tx flow, the only difference in the code between mlx5e_xmit and mlx5i_xmit is that IPoIB has some extra fields to fill (UD datagram segment) in the TX descriptor (WQE) and it doesn't need to have any vlan handling. Signed-off-by: Saeed Mahameed Reviewed-by: Erez Shitrit Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx5/mlx5_ib.h | 10 --- drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 87 +++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/ipoib.c | 10 +++ drivers/net/ethernet/mellanox/mlx5/core/ipoib.h | 3 + include/linux/mlx5/qp.h | 10 +++ 5 files changed, 110 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 3cd064b5f0bf..ce8ba617d46e 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -729,16 +729,6 @@ static inline struct mlx5_ib_mw *to_mmw(struct ib_mw *ibmw) return container_of(ibmw, struct mlx5_ib_mw, ibmw); } -struct mlx5_ib_ah { - struct ib_ah ibah; - struct mlx5_av av; -}; - -static inline struct mlx5_ib_ah *to_mah(struct ib_ah *ibah) -{ - return container_of(ibah, struct mlx5_ib_ah, ibah); -} - int mlx5_ib_db_map_user(struct mlx5_ib_ucontext *context, unsigned long virt, struct mlx5_db *db); void mlx5_ib_db_unmap_user(struct mlx5_ib_ucontext *context, struct mlx5_db *db); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index ba664a1126cf..dda7db503043 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -503,3 +503,90 @@ void mlx5e_free_txqsq_descs(struct mlx5e_txqsq *sq) sq->cc += wi->num_wqebbs; } } + +#ifdef CONFIG_MLX5_CORE_IPOIB + +struct mlx5_wqe_eth_pad { + u8 rsvd0[16]; +}; + +struct mlx5i_tx_wqe { + struct mlx5_wqe_ctrl_seg ctrl; + struct mlx5_wqe_datagram_seg datagram; + struct mlx5_wqe_eth_pad pad; + struct mlx5_wqe_eth_seg eth; +}; + +static inline void +mlx5i_txwqe_build_datagram(struct mlx5_av *av, u32 dqpn, u32 dqkey, + struct mlx5_wqe_datagram_seg *dseg) +{ + memcpy(&dseg->av, av, sizeof(struct mlx5_av)); + dseg->av.dqp_dct = cpu_to_be32(dqpn | MLX5_EXTENDED_UD_AV); + dseg->av.key.qkey.qkey = cpu_to_be32(dqkey); +} + +netdev_tx_t mlx5i_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb, + struct mlx5_av *av, u32 dqpn, u32 dqkey) +{ + struct mlx5_wq_cyc *wq = &sq->wq; + u16 pi = sq->pc & wq->sz_m1; + struct mlx5i_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(wq, pi); + struct mlx5e_tx_wqe_info *wi = &sq->db.wqe_info[pi]; + + struct mlx5_wqe_ctrl_seg *cseg = &wqe->ctrl; + struct mlx5_wqe_datagram_seg *datagram = &wqe->datagram; + struct mlx5_wqe_eth_seg *eseg = &wqe->eth; + + unsigned char *skb_data = skb->data; + unsigned int skb_len = skb->len; + u8 opcode = MLX5_OPCODE_SEND; + unsigned int num_bytes; + int num_dma; + u16 headlen; + u16 ds_cnt; + u16 ihs; + + memset(wqe, 0, sizeof(*wqe)); + + mlx5i_txwqe_build_datagram(av, dqpn, dqkey, datagram); + + mlx5e_txwqe_build_eseg_csum(sq, skb, eseg); + + if (skb_is_gso(skb)) { + opcode = MLX5_OPCODE_LSO; + ihs = mlx5e_txwqe_build_eseg_gso(sq, skb, eseg, &num_bytes); + } else { + ihs = mlx5e_calc_min_inline(sq->min_inline_mode, skb); + num_bytes = max_t(unsigned int, skb->len, ETH_ZLEN); + } + + ds_cnt = sizeof(*wqe) / MLX5_SEND_WQE_DS; + if (ihs) { + memcpy(eseg->inline_hdr.start, skb_data, ihs); + mlx5e_tx_skb_pull_inline(&skb_data, &skb_len, ihs); + eseg->inline_hdr.sz = cpu_to_be16(ihs); + ds_cnt += DIV_ROUND_UP(ihs - sizeof(eseg->inline_hdr.start), MLX5_SEND_WQE_DS); + } + + headlen = skb_len - skb->data_len; + num_dma = mlx5e_txwqe_build_dsegs(sq, skb, skb_data, headlen, + (struct mlx5_wqe_data_seg *)cseg + ds_cnt); + if (unlikely(num_dma < 0)) + goto dma_unmap_wqe_err; + + mlx5e_txwqe_complete(sq, skb, opcode, ds_cnt + num_dma, + num_bytes, num_dma, wi, cseg); + + return NETDEV_TX_OK; + +dma_unmap_wqe_err: + sq->stats.dropped++; + mlx5e_dma_unmap_wqe_err(sq, wi->num_dma); + + dev_kfree_skb_any(skb); + + return NETDEV_TX_OK; +} + +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c index bd56f36066b3..c468aaedf0a6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c @@ -393,6 +393,16 @@ int mlx5i_detach_mcast(struct net_device *netdev, struct ib_device *hca, return err; } +int mlx5i_xmit(struct net_device *dev, struct sk_buff *skb, + struct ib_ah *address, u32 dqpn, u32 dqkey) +{ + struct mlx5e_priv *epriv = mlx5i_epriv(dev); + struct mlx5e_txqsq *sq = epriv->txq2sq[skb_get_queue_mapping(skb)]; + struct mlx5_ib_ah *mah = to_mah(address); + + return mlx5i_sq_xmit(sq, skb, &mah->av, dqpn, dqkey); +} + static int mlx5i_check_required_hca_cap(struct mlx5_core_dev *mdev) { if (MLX5_CAP_GEN(mdev, port_type) != MLX5_CAP_PORT_TYPE_IB) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.h b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.h index 25b8b8a33e24..89bca182464c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.h @@ -47,4 +47,7 @@ struct mlx5i_priv { /* Extract mlx5e_priv from IPoIB netdev */ #define mlx5i_epriv(netdev) ((void *)(((struct mlx5i_priv *)netdev_priv(netdev))->mlx5e_priv)) +netdev_tx_t mlx5i_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb, + struct mlx5_av *av, u32 dqpn, u32 dqkey); + #endif /* __MLX5E_IPOB_H__ */ diff --git a/include/linux/mlx5/qp.h b/include/linux/mlx5/qp.h index 3096370fe831..bef80d0a0e30 100644 --- a/include/linux/mlx5/qp.h +++ b/include/linux/mlx5/qp.h @@ -295,6 +295,16 @@ struct mlx5_av { u8 rgid[16]; }; +struct mlx5_ib_ah { + struct ib_ah ibah; + struct mlx5_av av; +}; + +static inline struct mlx5_ib_ah *to_mah(struct ib_ah *ibah) +{ + return container_of(ibah, struct mlx5_ib_ah, ibah); +} + struct mlx5_wqe_datagram_seg { struct mlx5_av av; }; -- cgit From 20fd0c193ff6384717a6d500aa0e737a81c6a1fa Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 13 Apr 2017 06:37:03 +0300 Subject: net/mlx5e: RX handlers per netdev profile In order to have different RX handler per profile, fix and refactor the current code to take the rx handler directly from the netdevice profile rather than computing it on runtime as it was done with the switchdev mode representor rx handler. This will also remove the current wrong assumption in mlx5e_alloc_rq code that mlx5e_priv->ppriv is of the type vport_rep. Signed-off-by: Saeed Mahameed Reviewed-by: Erez Shitrit Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 5 +++- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 28 ++++++++++++++--------- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 4 +++- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 25185f8c3562..0881325fba04 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -779,6 +779,10 @@ struct mlx5e_profile { void (*disable)(struct mlx5e_priv *priv); void (*update_stats)(struct mlx5e_priv *priv); int (*max_nch)(struct mlx5_core_dev *mdev); + struct { + mlx5e_fp_handle_rx_cqe handle_rx_cqe; + mlx5e_fp_handle_rx_cqe handle_rx_cqe_mpwqe; + } rx_handlers; int max_tc; }; @@ -1032,7 +1036,6 @@ int mlx5e_get_offload_stats(int attr_id, const struct net_device *dev, bool mlx5e_has_offload_stats(const struct net_device *dev, int attr_id); bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv); -bool mlx5e_is_vf_vport_rep(struct mlx5e_priv *priv); /* mlx5e generic netdev management API */ struct net_device* diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 2201b7ea05f4..6a164aff404c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -585,15 +585,17 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, switch (rq->wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: - if (mlx5e_is_vf_vport_rep(c->priv)) { - err = -EINVAL; - goto err_rq_wq_destroy; - } - rq->handle_rx_cqe = mlx5e_handle_rx_cqe_mpwrq; rq->alloc_wqe = mlx5e_alloc_rx_mpwqe; rq->dealloc_wqe = mlx5e_dealloc_rx_mpwqe; + rq->handle_rx_cqe = c->priv->profile->rx_handlers.handle_rx_cqe_mpwqe; + if (!rq->handle_rx_cqe) { + err = -EINVAL; + netdev_err(c->netdev, "RX handler of MPWQE RQ is not set, err %d\n", err); + goto err_rq_wq_destroy; + } + rq->mpwqe_stride_sz = BIT(params->mpwqe_log_stride_sz); rq->mpwqe_num_strides = BIT(params->mpwqe_log_num_strides); @@ -616,15 +618,17 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, err = -ENOMEM; goto err_rq_wq_destroy; } - - if (mlx5e_is_vf_vport_rep(c->priv)) - rq->handle_rx_cqe = mlx5e_handle_rx_cqe_rep; - else - rq->handle_rx_cqe = mlx5e_handle_rx_cqe; - rq->alloc_wqe = mlx5e_alloc_rx_wqe; rq->dealloc_wqe = mlx5e_dealloc_rx_wqe; + rq->handle_rx_cqe = c->priv->profile->rx_handlers.handle_rx_cqe; + if (!rq->handle_rx_cqe) { + kfree(rq->dma_info); + err = -EINVAL; + netdev_err(c->netdev, "RX handler of RQ is not set, err %d\n", err); + goto err_rq_wq_destroy; + } + rq->buff.wqe_sz = params->lro_en ? params->lro_wqe_sz : MLX5E_SW2HW_MTU(c->netdev->mtu); @@ -4229,6 +4233,8 @@ static const struct mlx5e_profile mlx5e_nic_profile = { .disable = mlx5e_nic_disable, .update_stats = mlx5e_update_stats, .max_nch = mlx5e_get_max_num_channels, + .rx_handlers.handle_rx_cqe = mlx5e_handle_rx_cqe, + .rx_handlers.handle_rx_cqe_mpwqe = mlx5e_handle_rx_cqe_mpwrq, .max_tc = MLX5E_MAX_NUM_TC, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index da85b0ad3e92..16b683e8226d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -329,7 +329,7 @@ bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv) return false; } -bool mlx5e_is_vf_vport_rep(struct mlx5e_priv *priv) +static bool mlx5e_is_vf_vport_rep(struct mlx5e_priv *priv) { struct mlx5_eswitch_rep *rep = (struct mlx5_eswitch_rep *)priv->ppriv; @@ -538,6 +538,8 @@ static struct mlx5e_profile mlx5e_rep_profile = { .cleanup_tx = mlx5e_cleanup_nic_tx, .update_stats = mlx5e_rep_update_stats, .max_nch = mlx5e_get_rep_max_num_channels, + .rx_handlers.handle_rx_cqe = mlx5e_handle_rx_cqe_rep, + .rx_handlers.handle_rx_cqe_mpwqe = NULL /* Not supported */, .max_tc = 1, }; -- cgit From 9d6bd752c63c17419855bce1992e7b75af7370eb Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 13 Apr 2017 06:37:04 +0300 Subject: net/mlx5e: IPoIB, RX handler Implement IPoIB RX SKB handler. Signed-off-by: Saeed Mahameed Reviewed-by: Erez Shitrit Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 78 +++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/ipoib.c | 2 + drivers/net/ethernet/mellanox/mlx5/core/ipoib.h | 1 + 3 files changed, 81 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 1a9532b31635..43308243f519 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -1031,3 +1031,81 @@ void mlx5e_free_xdpsq_descs(struct mlx5e_xdpsq *sq) mlx5e_page_release(rq, di, false); } } + +#ifdef CONFIG_MLX5_CORE_IPOIB + +#define MLX5_IB_GRH_DGID_OFFSET 24 +#define MLX5_IB_GRH_BYTES 40 +#define MLX5_IPOIB_ENCAP_LEN 4 +#define MLX5_GID_SIZE 16 + +static inline void mlx5i_complete_rx_cqe(struct mlx5e_rq *rq, + struct mlx5_cqe64 *cqe, + u32 cqe_bcnt, + struct sk_buff *skb) +{ + struct net_device *netdev = rq->netdev; + u8 *dgid; + u8 g; + + g = (be32_to_cpu(cqe->flags_rqpn) >> 28) & 3; + dgid = skb->data + MLX5_IB_GRH_DGID_OFFSET; + if ((!g) || dgid[0] != 0xff) + skb->pkt_type = PACKET_HOST; + else if (memcmp(dgid, netdev->broadcast + 4, MLX5_GID_SIZE) == 0) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + + /* TODO: IB/ipoib: Allow mcast packets from other VFs + * 68996a6e760e5c74654723eeb57bf65628ae87f4 + */ + + skb_pull(skb, MLX5_IB_GRH_BYTES); + + skb->protocol = *((__be16 *)(skb->data)); + + skb->ip_summed = CHECKSUM_COMPLETE; + skb->csum = csum_unfold((__force __sum16)cqe->check_sum); + + skb_record_rx_queue(skb, rq->ix); + + if (likely(netdev->features & NETIF_F_RXHASH)) + mlx5e_skb_set_hash(cqe, skb); + + skb_reset_mac_header(skb); + skb_pull(skb, MLX5_IPOIB_ENCAP_LEN); + + skb->dev = netdev; + + rq->stats.csum_complete++; + rq->stats.packets++; + rq->stats.bytes += cqe_bcnt; +} + +void mlx5i_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe) +{ + struct mlx5e_rx_wqe *wqe; + __be16 wqe_counter_be; + struct sk_buff *skb; + u16 wqe_counter; + u32 cqe_bcnt; + + wqe_counter_be = cqe->wqe_counter; + wqe_counter = be16_to_cpu(wqe_counter_be); + wqe = mlx5_wq_ll_get_wqe(&rq->wq, wqe_counter); + cqe_bcnt = be32_to_cpu(cqe->byte_cnt); + + skb = skb_from_cqe(rq, cqe, wqe_counter, cqe_bcnt); + if (!skb) + goto wq_ll_pop; + + mlx5i_complete_rx_cqe(rq, cqe, cqe_bcnt, skb); + napi_gro_receive(rq->cq.napi, skb); + +wq_ll_pop: + mlx5_wq_ll_pop(&rq->wq, wqe_counter_be, + &wqe->next.next_wqe_index); +} + +#endif /* CONFIG_MLX5_CORE_IPOIB */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c index c468aaedf0a6..001d2953cb6d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c @@ -282,6 +282,8 @@ static const struct mlx5e_profile mlx5i_nic_profile = { .disable = NULL, /* mlx5i_disable */ .update_stats = NULL, /* mlx5i_update_stats */ .max_nch = mlx5e_get_max_num_channels, + .rx_handlers.handle_rx_cqe = mlx5i_handle_rx_cqe, + .rx_handlers.handle_rx_cqe_mpwqe = NULL, /* Not supported */ .max_tc = MLX5I_MAX_NUM_TC, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.h b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.h index 89bca182464c..bae0a5cbc8ad 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.h @@ -49,5 +49,6 @@ struct mlx5i_priv { netdev_tx_t mlx5i_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb, struct mlx5_av *av, u32 dqpn, u32 dqkey); +void mlx5i_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe); #endif /* __MLX5E_IPOB_H__ */ -- cgit From 955bc48081805c053aa53943b2904cb68569a364 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Thu, 13 Apr 2017 06:37:05 +0300 Subject: net/mlx5e: E-switch vport manager is valid for ethernet only Currently the driver support only ethernet eswitch, and we want to protect downstream IPoIB netdev from trying to access it in IB link. Signed-off-by: Saeed Mahameed Reviewed-by: Erez Shitrit Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 6a164aff404c..061b20c73071 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -2548,6 +2548,12 @@ static void mlx5e_build_channels_tx_maps(struct mlx5e_priv *priv) } } +static bool mlx5e_is_eswitch_vport_mngr(struct mlx5_core_dev *mdev) +{ + return (MLX5_CAP_GEN(mdev, vport_group_manager) && + MLX5_CAP_GEN(mdev, port_type) == MLX5_CAP_PORT_TYPE_ETH); +} + void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) { int num_txqs = priv->channels.num * priv->channels.params.num_tc; @@ -2561,7 +2567,7 @@ void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) mlx5e_activate_channels(&priv->channels); netif_tx_start_all_queues(priv->netdev); - if (MLX5_CAP_GEN(priv->mdev, vport_group_manager)) + if (mlx5e_is_eswitch_vport_mngr(priv->mdev)) mlx5e_add_sqs_fwd_rules(priv); mlx5e_wait_channels_min_rx_wqes(&priv->channels); @@ -2572,7 +2578,7 @@ void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv) { mlx5e_redirect_rqts_to_drop(priv); - if (MLX5_CAP_GEN(priv->mdev, vport_group_manager)) + if (mlx5e_is_eswitch_vport_mngr(priv->mdev)) mlx5e_remove_sqs_fwd_rules(priv); /* FIXME: This is a W/A only for tx timeout watch dog false alarm when -- cgit From 93d576af3cc8a20f94c96448ef6cf4c21d552fa0 Mon Sep 17 00:00:00 2001 From: Erez Shitrit Date: Thu, 13 Apr 2017 06:37:06 +0300 Subject: hw/mlx5: Add New bit to check over QP creation Add check for bit IB_QP_CREATE_NETIF_QP while creating QP. Signed-off-by: Erez Shitrit Signed-off-by: Saeed Mahameed Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx5/qp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index ad8a2638e339..ed6320186f89 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -897,6 +897,7 @@ static int create_kernel_qp(struct mlx5_ib_dev *dev, if (init_attr->create_flags & ~(IB_QP_CREATE_SIGNATURE_EN | IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK | IB_QP_CREATE_IPOIB_UD_LSO | + IB_QP_CREATE_NETIF_QP | mlx5_ib_create_qp_sqpn_qp1())) return -EINVAL; -- cgit From 4a6e3c5def13c91adf2acc613837001f09af3baa Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 12 Apr 2017 11:49:04 -0700 Subject: net: ipv6: send unsolicited NA on admin up ndisc_notify is the ipv6 equivalent to arp_notify. When arp_notify is set to 1, gratuitous arp requests are sent when the device is brought up. The same is expected when ndisc_notify is set to 1 (per ndisc_notify in Documentation/networking/ip-sysctl.txt). The NA is not sent on NETDEV_UP event; add it. Fixes: 5cb04436eef6 ("ipv6: add knob to send unsolicited ND on link-layer address change") Signed-off-by: David Ahern Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index b5812b3f7539..b23822e64228 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1748,6 +1748,8 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, case NETDEV_CHANGEADDR: neigh_changeaddr(&nd_tbl, dev); fib6_run_gc(0, net, false); + /* fallthrough */ + case NETDEV_UP: idev = in6_dev_get(dev); if (!idev) break; -- cgit From 57240d007816486131bee88cd474c2a71f0fe224 Mon Sep 17 00:00:00 2001 From: "R. Parameswaran" Date: Wed, 12 Apr 2017 18:31:04 -0700 Subject: l2tp: device MTU setup, tunnel socket needs a lock The MTU overhead calculation in L2TP device set-up merged via commit b784e7ebfce8cfb16c6f95e14e8532d0768ab7ff needs to be adjusted to lock the tunnel socket while referencing the sub-data structures to derive the socket's IP overhead. Reported-by: Guillaume Nault Tested-by: Guillaume Nault Signed-off-by: R. Parameswaran Signed-off-by: David S. Miller --- include/linux/net.h | 2 +- net/l2tp/l2tp_eth.c | 2 ++ net/socket.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/linux/net.h b/include/linux/net.h index a42fab24c8af..abcfa46a2bd9 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -298,7 +298,7 @@ int kernel_sendpage(struct socket *sock, struct page *page, int offset, int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how); -/* Following routine returns the IP overhead imposed by a socket. */ +/* Routine returns the IP overhead imposed by a (caller-protected) socket. */ u32 kernel_sock_ip_overhead(struct sock *sk); #define MODULE_ALIAS_NETPROTO(proto) \ diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index 138566a63123..b722d559c544 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -225,7 +225,9 @@ static void l2tp_eth_adjust_mtu(struct l2tp_tunnel *tunnel, dev->needed_headroom += session->hdr_len; return; } + lock_sock(tunnel->sock); l3_overhead = kernel_sock_ip_overhead(tunnel->sock); + release_sock(tunnel->sock); if (l3_overhead == 0) { /* L3 Overhead couldn't be identified, this could be * because tunnel->sock was NULL or the socket's diff --git a/net/socket.c b/net/socket.c index eea997036ada..c2564eb25c6b 100644 --- a/net/socket.c +++ b/net/socket.c @@ -3360,7 +3360,7 @@ EXPORT_SYMBOL(kernel_sock_shutdown); /* This routine returns the IP overhead imposed by a socket i.e. * the length of the underlying IP header, depending on whether * this is an IPv4 or IPv6 socket and the length from IP options turned - * on at the socket. + * on at the socket. Assumes that the caller has a lock on the socket. */ u32 kernel_sock_ip_overhead(struct sock *sk) { -- cgit From 53a759c89b7924b2cd218b40fa4b51f9220f20b1 Mon Sep 17 00:00:00 2001 From: Martin Wetterwald Date: Thu, 13 Apr 2017 10:08:44 +0200 Subject: smsc95xx: Add comments to the registers definition This chip is used by a lot of embedded devices and also by the Raspberry Pi 1, 2 & 3 which were created to promote the study of computer sciences. Students wanting to learn kernel / network device driver programming through those devices can only rely on the Linux kernel driver source to make their own. This commit adds a lot of comments to the registers definition to expand the register names. Cc: Steve Glendinning Cc: Microchip Linux Driver Support CC: David Miller Signed-off-by: Martin Wetterwald Reviewed-by: Andrew Lunn Acked-by: Steve Glendinning Acked-by: Woojung Huh Signed-off-by: David S. Miller --- drivers/net/usb/smsc95xx.c | 4 +- drivers/net/usb/smsc95xx.h | 490 +++++++++++++++++++++++++-------------------- 2 files changed, 280 insertions(+), 214 deletions(-) diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index c2f67cecdf5b..4db2b6e0ba62 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -33,7 +33,7 @@ #include "smsc95xx.h" #define SMSC_CHIPNAME "smsc95xx" -#define SMSC_DRIVER_VERSION "1.0.5" +#define SMSC_DRIVER_VERSION "1.0.6" #define HS_USB_PKT_SIZE (512) #define FS_USB_PKT_SIZE (64) #define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE) @@ -1499,7 +1499,7 @@ static int smsc95xx_enter_suspend3(struct usbnet *dev) if (ret < 0) return ret; - if (val & 0xFFFF) { + if (val & RX_FIFO_INF_USED_) { netdev_info(dev->net, "rx fifo not empty in autosuspend\n"); return -EBUSY; } diff --git a/drivers/net/usb/smsc95xx.h b/drivers/net/usb/smsc95xx.h index 29a4d9efce7c..cfc704f3a460 100644 --- a/drivers/net/usb/smsc95xx.h +++ b/drivers/net/usb/smsc95xx.h @@ -21,218 +21,280 @@ #define _SMSC95XX_H /* Tx command words */ -#define TX_CMD_A_DATA_OFFSET_ (0x001F0000) -#define TX_CMD_A_FIRST_SEG_ (0x00002000) -#define TX_CMD_A_LAST_SEG_ (0x00001000) -#define TX_CMD_A_BUF_SIZE_ (0x000007FF) +#define TX_CMD_A_DATA_OFFSET_ (0x001F0000) /* Data Start Offset */ +#define TX_CMD_A_FIRST_SEG_ (0x00002000) /* First Segment */ +#define TX_CMD_A_LAST_SEG_ (0x00001000) /* Last Segment */ +#define TX_CMD_A_BUF_SIZE_ (0x000007FF) /* Buffer Size */ -#define TX_CMD_B_CSUM_ENABLE (0x00004000) -#define TX_CMD_B_ADD_CRC_DISABLE_ (0x00002000) -#define TX_CMD_B_DISABLE_PADDING_ (0x00001000) -#define TX_CMD_B_PKT_BYTE_LENGTH_ (0x000007FF) +#define TX_CMD_B_CSUM_ENABLE (0x00004000) /* TX Checksum Enable */ +#define TX_CMD_B_ADD_CRC_DIS_ (0x00002000) /* Add CRC Disable */ +#define TX_CMD_B_DIS_PADDING_ (0x00001000) /* Disable Frame Padding */ +#define TX_CMD_B_FRAME_LENGTH_ (0x000007FF) /* Frame Length (bytes) */ /* Rx status word */ -#define RX_STS_FF_ (0x40000000) /* Filter Fail */ -#define RX_STS_FL_ (0x3FFF0000) /* Frame Length */ -#define RX_STS_ES_ (0x00008000) /* Error Summary */ -#define RX_STS_BF_ (0x00002000) /* Broadcast Frame */ -#define RX_STS_LE_ (0x00001000) /* Length Error */ -#define RX_STS_RF_ (0x00000800) /* Runt Frame */ -#define RX_STS_MF_ (0x00000400) /* Multicast Frame */ -#define RX_STS_TL_ (0x00000080) /* Frame too long */ -#define RX_STS_CS_ (0x00000040) /* Collision Seen */ -#define RX_STS_FT_ (0x00000020) /* Frame Type */ -#define RX_STS_RW_ (0x00000010) /* Receive Watchdog */ -#define RX_STS_ME_ (0x00000008) /* Mii Error */ -#define RX_STS_DB_ (0x00000004) /* Dribbling */ -#define RX_STS_CRC_ (0x00000002) /* CRC Error */ - -/* SCSRs */ -#define ID_REV (0x00) -#define ID_REV_CHIP_ID_MASK_ (0xFFFF0000) -#define ID_REV_CHIP_REV_MASK_ (0x0000FFFF) -#define ID_REV_CHIP_ID_9500_ (0x9500) -#define ID_REV_CHIP_ID_9500A_ (0x9E00) -#define ID_REV_CHIP_ID_9512_ (0xEC00) -#define ID_REV_CHIP_ID_9530_ (0x9530) -#define ID_REV_CHIP_ID_89530_ (0x9E08) -#define ID_REV_CHIP_ID_9730_ (0x9730) - -#define INT_STS (0x08) -#define INT_STS_TX_STOP_ (0x00020000) -#define INT_STS_RX_STOP_ (0x00010000) -#define INT_STS_PHY_INT_ (0x00008000) -#define INT_STS_TXE_ (0x00004000) -#define INT_STS_TDFU_ (0x00002000) -#define INT_STS_TDFO_ (0x00001000) -#define INT_STS_RXDF_ (0x00000800) -#define INT_STS_GPIOS_ (0x000007FF) -#define INT_STS_CLEAR_ALL_ (0xFFFFFFFF) - -#define RX_CFG (0x0C) -#define RX_FIFO_FLUSH_ (0x00000001) - -#define TX_CFG (0x10) -#define TX_CFG_ON_ (0x00000004) -#define TX_CFG_STOP_ (0x00000002) -#define TX_CFG_FIFO_FLUSH_ (0x00000001) - -#define HW_CFG (0x14) -#define HW_CFG_BIR_ (0x00001000) -#define HW_CFG_LEDB_ (0x00000800) -#define HW_CFG_RXDOFF_ (0x00000600) -#define HW_CFG_DRP_ (0x00000040) -#define HW_CFG_MEF_ (0x00000020) -#define HW_CFG_LRST_ (0x00000008) -#define HW_CFG_PSEL_ (0x00000004) -#define HW_CFG_BCE_ (0x00000002) -#define HW_CFG_SRST_ (0x00000001) - -#define RX_FIFO_INF (0x18) - -#define PM_CTRL (0x20) -#define PM_CTL_RES_CLR_WKP_STS (0x00000200) -#define PM_CTL_DEV_RDY_ (0x00000080) -#define PM_CTL_SUS_MODE_ (0x00000060) -#define PM_CTL_SUS_MODE_0 (0x00000000) -#define PM_CTL_SUS_MODE_1 (0x00000020) -#define PM_CTL_SUS_MODE_2 (0x00000040) -#define PM_CTL_SUS_MODE_3 (0x00000060) -#define PM_CTL_PHY_RST_ (0x00000010) -#define PM_CTL_WOL_EN_ (0x00000008) -#define PM_CTL_ED_EN_ (0x00000004) -#define PM_CTL_WUPS_ (0x00000003) -#define PM_CTL_WUPS_NO_ (0x00000000) -#define PM_CTL_WUPS_ED_ (0x00000001) -#define PM_CTL_WUPS_WOL_ (0x00000002) -#define PM_CTL_WUPS_MULTI_ (0x00000003) - -#define LED_GPIO_CFG (0x24) -#define LED_GPIO_CFG_SPD_LED (0x01000000) -#define LED_GPIO_CFG_LNK_LED (0x00100000) -#define LED_GPIO_CFG_FDX_LED (0x00010000) - -#define GPIO_CFG (0x28) - -#define AFC_CFG (0x2C) - +#define RX_STS_FF_ (0x40000000) /* Filter Fail */ +#define RX_STS_FL_ (0x3FFF0000) /* Frame Length */ +#define RX_STS_ES_ (0x00008000) /* Error Summary */ +#define RX_STS_BF_ (0x00002000) /* Broadcast Frame */ +#define RX_STS_LE_ (0x00001000) /* Length Error */ +#define RX_STS_RF_ (0x00000800) /* Runt Frame */ +#define RX_STS_MF_ (0x00000400) /* Multicast Frame */ +#define RX_STS_TL_ (0x00000080) /* Frame too long */ +#define RX_STS_CS_ (0x00000040) /* Collision Seen */ +#define RX_STS_FT_ (0x00000020) /* Frame Type */ +#define RX_STS_RW_ (0x00000010) /* Receive Watchdog */ +#define RX_STS_ME_ (0x00000008) /* MII Error */ +#define RX_STS_DB_ (0x00000004) /* Dribbling */ +#define RX_STS_CRC_ (0x00000002) /* CRC Error */ + +/* SCSRs - System Control and Status Registers */ +/* Device ID and Revision Register */ +#define ID_REV (0x00) +#define ID_REV_CHIP_ID_MASK_ (0xFFFF0000) +#define ID_REV_CHIP_REV_MASK_ (0x0000FFFF) +#define ID_REV_CHIP_ID_9500_ (0x9500) +#define ID_REV_CHIP_ID_9500A_ (0x9E00) +#define ID_REV_CHIP_ID_9512_ (0xEC00) +#define ID_REV_CHIP_ID_9530_ (0x9530) +#define ID_REV_CHIP_ID_89530_ (0x9E08) +#define ID_REV_CHIP_ID_9730_ (0x9730) + +/* Interrupt Status Register */ +#define INT_STS (0x08) +#define INT_STS_MAC_RTO_ (0x00040000) /* MAC Reset Time Out */ +#define INT_STS_TX_STOP_ (0x00020000) /* TX Stopped */ +#define INT_STS_RX_STOP_ (0x00010000) /* RX Stopped */ +#define INT_STS_PHY_INT_ (0x00008000) /* PHY Interrupt */ +#define INT_STS_TXE_ (0x00004000) /* Transmitter Error */ +#define INT_STS_TDFU_ (0x00002000) /* TX Data FIFO Underrun */ +#define INT_STS_TDFO_ (0x00001000) /* TX Data FIFO Overrun */ +#define INT_STS_RXDF_ (0x00000800) /* RX Dropped Frame */ +#define INT_STS_GPIOS_ (0x000007FF) /* GPIOs Interrupts */ +#define INT_STS_CLEAR_ALL_ (0xFFFFFFFF) + +/* Receive Configuration Register */ +#define RX_CFG (0x0C) +#define RX_FIFO_FLUSH_ (0x00000001) /* Receive FIFO Flush */ + +/* Transmit Configuration Register */ +#define TX_CFG (0x10) +#define TX_CFG_ON_ (0x00000004) /* Transmitter Enable */ +#define TX_CFG_STOP_ (0x00000002) /* Stop Transmitter */ +#define TX_CFG_FIFO_FLUSH_ (0x00000001) /* Transmit FIFO Flush */ + +/* Hardware Configuration Register */ +#define HW_CFG (0x14) +#define HW_CFG_BIR_ (0x00001000) /* Bulk In Empty Response */ +#define HW_CFG_LEDB_ (0x00000800) /* Activity LED 80ms Bypass */ +#define HW_CFG_RXDOFF_ (0x00000600) /* RX Data Offset */ +#define HW_CFG_SBP_ (0x00000100) /* Stall Bulk Out Pipe Dis. */ +#define HW_CFG_IME_ (0x00000080) /* Internal MII Visi. Enable */ +#define HW_CFG_DRP_ (0x00000040) /* Discard Errored RX Frame */ +#define HW_CFG_MEF_ (0x00000020) /* Mult. ETH Frames/USB pkt */ +#define HW_CFG_ETC_ (0x00000010) /* EEPROM Timeout Control */ +#define HW_CFG_LRST_ (0x00000008) /* Soft Lite Reset */ +#define HW_CFG_PSEL_ (0x00000004) /* External PHY Select */ +#define HW_CFG_BCE_ (0x00000002) /* Burst Cap Enable */ +#define HW_CFG_SRST_ (0x00000001) /* Soft Reset */ + +/* Receive FIFO Information Register */ +#define RX_FIFO_INF (0x18) +#define RX_FIFO_INF_USED_ (0x0000FFFF) /* RX Data FIFO Used Space */ + +/* Transmit FIFO Information Register */ +#define TX_FIFO_INF (0x1C) +#define TX_FIFO_INF_FREE_ (0x0000FFFF) /* TX Data FIFO Free Space */ + +/* Power Management Control Register */ +#define PM_CTRL (0x20) +#define PM_CTL_RES_CLR_WKP_STS (0x00000200) /* Resume Clears Wakeup STS */ +#define PM_CTL_RES_CLR_WKP_EN (0x00000100) /* Resume Clears Wkp Enables */ +#define PM_CTL_DEV_RDY_ (0x00000080) /* Device Ready */ +#define PM_CTL_SUS_MODE_ (0x00000060) /* Suspend Mode */ +#define PM_CTL_SUS_MODE_0 (0x00000000) +#define PM_CTL_SUS_MODE_1 (0x00000020) +#define PM_CTL_SUS_MODE_2 (0x00000040) +#define PM_CTL_SUS_MODE_3 (0x00000060) +#define PM_CTL_PHY_RST_ (0x00000010) /* PHY Reset */ +#define PM_CTL_WOL_EN_ (0x00000008) /* Wake On Lan Enable */ +#define PM_CTL_ED_EN_ (0x00000004) /* Energy Detect Enable */ +#define PM_CTL_WUPS_ (0x00000003) /* Wake Up Status */ +#define PM_CTL_WUPS_NO_ (0x00000000) /* No Wake Up Event Detected */ +#define PM_CTL_WUPS_ED_ (0x00000001) /* Energy Detect */ +#define PM_CTL_WUPS_WOL_ (0x00000002) /* Wake On Lan */ +#define PM_CTL_WUPS_MULTI_ (0x00000003) /* Multiple Events Occurred */ + +/* LED General Purpose IO Configuration Register */ +#define LED_GPIO_CFG (0x24) +#define LED_GPIO_CFG_SPD_LED (0x01000000) /* GPIOz as Speed LED */ +#define LED_GPIO_CFG_LNK_LED (0x00100000) /* GPIOy as Link LED */ +#define LED_GPIO_CFG_FDX_LED (0x00010000) /* GPIOx as Full Duplex LED */ + +/* General Purpose IO Configuration Register */ +#define GPIO_CFG (0x28) + +/* Automatic Flow Control Configuration Register */ +#define AFC_CFG (0x2C) +#define AFC_CFG_HI_ (0x00FF0000) /* Auto Flow Ctrl High Level */ +#define AFC_CFG_LO_ (0x0000FF00) /* Auto Flow Ctrl Low Level */ +#define AFC_CFG_BACK_DUR_ (0x000000F0) /* Back Pressure Duration */ +#define AFC_CFG_FC_MULT_ (0x00000008) /* Flow Ctrl on Mcast Frame */ +#define AFC_CFG_FC_BRD_ (0x00000004) /* Flow Ctrl on Bcast Frame */ +#define AFC_CFG_FC_ADD_ (0x00000002) /* Flow Ctrl on Addr. Decode */ +#define AFC_CFG_FC_ANY_ (0x00000001) /* Flow Ctrl on Any Frame */ /* Hi watermark = 15.5Kb (~10 mtu pkts) */ /* low watermark = 3k (~2 mtu pkts) */ /* backpressure duration = ~ 350us */ /* Apply FC on any frame. */ -#define AFC_CFG_DEFAULT (0x00F830A1) - -#define E2P_CMD (0x30) -#define E2P_CMD_BUSY_ (0x80000000) -#define E2P_CMD_MASK_ (0x70000000) -#define E2P_CMD_READ_ (0x00000000) -#define E2P_CMD_EWDS_ (0x10000000) -#define E2P_CMD_EWEN_ (0x20000000) -#define E2P_CMD_WRITE_ (0x30000000) -#define E2P_CMD_WRAL_ (0x40000000) -#define E2P_CMD_ERASE_ (0x50000000) -#define E2P_CMD_ERAL_ (0x60000000) -#define E2P_CMD_RELOAD_ (0x70000000) -#define E2P_CMD_TIMEOUT_ (0x00000400) -#define E2P_CMD_LOADED_ (0x00000200) -#define E2P_CMD_ADDR_ (0x000001FF) - -#define MAX_EEPROM_SIZE (512) - -#define E2P_DATA (0x34) -#define E2P_DATA_MASK_ (0x000000FF) - -#define BURST_CAP (0x38) - +#define AFC_CFG_DEFAULT (0x00F830A1) + +/* EEPROM Command Register */ +#define E2P_CMD (0x30) +#define E2P_CMD_BUSY_ (0x80000000) /* E2P Controller Busy */ +#define E2P_CMD_MASK_ (0x70000000) /* Command Mask (see below) */ +#define E2P_CMD_READ_ (0x00000000) /* Read Location */ +#define E2P_CMD_EWDS_ (0x10000000) /* Erase/Write Disable */ +#define E2P_CMD_EWEN_ (0x20000000) /* Erase/Write Enable */ +#define E2P_CMD_WRITE_ (0x30000000) /* Write Location */ +#define E2P_CMD_WRAL_ (0x40000000) /* Write All */ +#define E2P_CMD_ERASE_ (0x50000000) /* Erase Location */ +#define E2P_CMD_ERAL_ (0x60000000) /* Erase All */ +#define E2P_CMD_RELOAD_ (0x70000000) /* Data Reload */ +#define E2P_CMD_TIMEOUT_ (0x00000400) /* Set if no resp within 30ms */ +#define E2P_CMD_LOADED_ (0x00000200) /* Valid EEPROM found */ +#define E2P_CMD_ADDR_ (0x000001FF) /* Byte aligned address */ + +#define MAX_EEPROM_SIZE (512) + +/* EEPROM Data Register */ +#define E2P_DATA (0x34) +#define E2P_DATA_MASK_ (0x000000FF) /* EEPROM Data Mask */ + +/* Burst Cap Register */ +#define BURST_CAP (0x38) +#define BURST_CAP_MASK_ (0x000000FF) /* Max burst sent by the UTX */ + +/* Configuration Straps Status Register */ #define STRAP_STATUS (0x3C) -#define STRAP_STATUS_PWR_SEL_ (0x00000020) -#define STRAP_STATUS_AMDIX_EN_ (0x00000010) -#define STRAP_STATUS_PORT_SWAP_ (0x00000008) -#define STRAP_STATUS_EEP_SIZE_ (0x00000004) -#define STRAP_STATUS_RMT_WKP_ (0x00000002) -#define STRAP_STATUS_EEP_DISABLE_ (0x00000001) - -#define GPIO_WAKE (0x64) - -#define INT_EP_CTL (0x68) -#define INT_EP_CTL_INTEP_ (0x80000000) -#define INT_EP_CTL_MACRTO_ (0x00080000) -#define INT_EP_CTL_TX_STOP_ (0x00020000) -#define INT_EP_CTL_RX_STOP_ (0x00010000) -#define INT_EP_CTL_PHY_INT_ (0x00008000) -#define INT_EP_CTL_TXE_ (0x00004000) -#define INT_EP_CTL_TDFU_ (0x00002000) -#define INT_EP_CTL_TDFO_ (0x00001000) -#define INT_EP_CTL_RXDF_ (0x00000800) -#define INT_EP_CTL_GPIOS_ (0x000007FF) - -#define BULK_IN_DLY (0x6C) - -/* MAC CSRs */ -#define MAC_CR (0x100) -#define MAC_CR_RXALL_ (0x80000000) -#define MAC_CR_RCVOWN_ (0x00800000) -#define MAC_CR_LOOPBK_ (0x00200000) -#define MAC_CR_FDPX_ (0x00100000) -#define MAC_CR_MCPAS_ (0x00080000) -#define MAC_CR_PRMS_ (0x00040000) -#define MAC_CR_INVFILT_ (0x00020000) -#define MAC_CR_PASSBAD_ (0x00010000) -#define MAC_CR_HFILT_ (0x00008000) -#define MAC_CR_HPFILT_ (0x00002000) -#define MAC_CR_LCOLL_ (0x00001000) -#define MAC_CR_BCAST_ (0x00000800) -#define MAC_CR_DISRTY_ (0x00000400) -#define MAC_CR_PADSTR_ (0x00000100) -#define MAC_CR_BOLMT_MASK (0x000000C0) -#define MAC_CR_DFCHK_ (0x00000020) -#define MAC_CR_TXEN_ (0x00000008) -#define MAC_CR_RXEN_ (0x00000004) - -#define ADDRH (0x104) - -#define ADDRL (0x108) - -#define HASHH (0x10C) - -#define HASHL (0x110) - -#define MII_ADDR (0x114) -#define MII_WRITE_ (0x02) -#define MII_BUSY_ (0x01) -#define MII_READ_ (0x00) /* ~of MII Write bit */ - -#define MII_DATA (0x118) - -#define FLOW (0x11C) -#define FLOW_FCPT_ (0xFFFF0000) -#define FLOW_FCPASS_ (0x00000004) -#define FLOW_FCEN_ (0x00000002) -#define FLOW_FCBSY_ (0x00000001) - -#define VLAN1 (0x120) - -#define VLAN2 (0x124) - -#define WUFF (0x128) -#define LAN9500_WUFF_NUM (4) -#define LAN9500A_WUFF_NUM (8) - -#define WUCSR (0x12C) -#define WUCSR_WFF_PTR_RST_ (0x80000000) -#define WUCSR_GUE_ (0x00000200) -#define WUCSR_WUFR_ (0x00000040) -#define WUCSR_MPR_ (0x00000020) -#define WUCSR_WAKE_EN_ (0x00000004) -#define WUCSR_MPEN_ (0x00000002) - -#define COE_CR (0x130) -#define Tx_COE_EN_ (0x00010000) -#define Rx_COE_MODE_ (0x00000002) -#define Rx_COE_EN_ (0x00000001) - -/* Vendor-specific PHY Definitions */ - +#define STRAP_STATUS_PWR_SEL_ (0x00000020) /* Device self-powered */ +#define STRAP_STATUS_AMDIX_EN_ (0x00000010) /* Auto-MDIX Enabled */ +#define STRAP_STATUS_PORT_SWAP_ (0x00000008) /* USBD+/USBD- Swapped */ +#define STRAP_STATUS_EEP_SIZE_ (0x00000004) /* EEPROM Size */ +#define STRAP_STATUS_RMT_WKP_ (0x00000002) /* Remote Wkp supported */ +#define STRAP_STATUS_EEP_DISABLE_ (0x00000001) /* EEPROM Disabled */ + +/* Data Port Select Register */ +#define DP_SEL (0x40) + +/* Data Port Command Register */ +#define DP_CMD (0x44) + +/* Data Port Address Register */ +#define DP_ADDR (0x48) + +/* Data Port Data 0 Register */ +#define DP_DATA0 (0x4C) + +/* Data Port Data 1 Register */ +#define DP_DATA1 (0x50) + +/* General Purpose IO Wake Enable and Polarity Register */ +#define GPIO_WAKE (0x64) + +/* Interrupt Endpoint Control Register */ +#define INT_EP_CTL (0x68) +#define INT_EP_CTL_INTEP_ (0x80000000) /* Always TX Interrupt PKT */ +#define INT_EP_CTL_MAC_RTO_ (0x00080000) /* MAC Reset Time Out */ +#define INT_EP_CTL_RX_FIFO_ (0x00040000) /* RX FIFO Has Frame */ +#define INT_EP_CTL_TX_STOP_ (0x00020000) /* TX Stopped */ +#define INT_EP_CTL_RX_STOP_ (0x00010000) /* RX Stopped */ +#define INT_EP_CTL_PHY_INT_ (0x00008000) /* PHY Interrupt */ +#define INT_EP_CTL_TXE_ (0x00004000) /* TX Error */ +#define INT_EP_CTL_TDFU_ (0x00002000) /* TX Data FIFO Underrun */ +#define INT_EP_CTL_TDFO_ (0x00001000) /* TX Data FIFO Overrun */ +#define INT_EP_CTL_RXDF_ (0x00000800) /* RX Dropped Frame */ +#define INT_EP_CTL_GPIOS_ (0x000007FF) /* GPIOs Interrupt Enable */ + +/* Bulk In Delay Register (units of 16.667ns, until ~1092µs) */ +#define BULK_IN_DLY (0x6C) + +/* MAC CSRs - MAC Control and Status Registers */ +/* MAC Control Register */ +#define MAC_CR (0x100) +#define MAC_CR_RXALL_ (0x80000000) /* Receive All Mode */ +#define MAC_CR_RCVOWN_ (0x00800000) /* Disable Receive Own */ +#define MAC_CR_LOOPBK_ (0x00200000) /* Loopback Operation Mode */ +#define MAC_CR_FDPX_ (0x00100000) /* Full Duplex Mode */ +#define MAC_CR_MCPAS_ (0x00080000) /* Pass All Multicast */ +#define MAC_CR_PRMS_ (0x00040000) /* Promiscuous Mode */ +#define MAC_CR_INVFILT_ (0x00020000) /* Inverse Filtering */ +#define MAC_CR_PASSBAD_ (0x00010000) /* Pass Bad Frames */ +#define MAC_CR_HFILT_ (0x00008000) /* Hash Only Filtering Mode */ +#define MAC_CR_HPFILT_ (0x00002000) /* Hash/Perfect Filt. Mode */ +#define MAC_CR_LCOLL_ (0x00001000) /* Late Collision Control */ +#define MAC_CR_BCAST_ (0x00000800) /* Disable Broadcast Frames */ +#define MAC_CR_DISRTY_ (0x00000400) /* Disable Retry */ +#define MAC_CR_PADSTR_ (0x00000100) /* Automatic Pad Stripping */ +#define MAC_CR_BOLMT_MASK (0x000000C0) /* BackOff Limit */ +#define MAC_CR_DFCHK_ (0x00000020) /* Deferral Check */ +#define MAC_CR_TXEN_ (0x00000008) /* Transmitter Enable */ +#define MAC_CR_RXEN_ (0x00000004) /* Receiver Enable */ + +/* MAC Address High Register */ +#define ADDRH (0x104) + +/* MAC Address Low Register */ +#define ADDRL (0x108) + +/* Multicast Hash Table High Register */ +#define HASHH (0x10C) + +/* Multicast Hash Table Low Register */ +#define HASHL (0x110) + +/* MII Access Register */ +#define MII_ADDR (0x114) +#define MII_WRITE_ (0x02) +#define MII_BUSY_ (0x01) +#define MII_READ_ (0x00) /* ~of MII Write bit */ + +/* MII Data Register */ +#define MII_DATA (0x118) + +/* Flow Control Register */ +#define FLOW (0x11C) +#define FLOW_FCPT_ (0xFFFF0000) /* Pause Time */ +#define FLOW_FCPASS_ (0x00000004) /* Pass Control Frames */ +#define FLOW_FCEN_ (0x00000002) /* Flow Control Enable */ +#define FLOW_FCBSY_ (0x00000001) /* Flow Control Busy */ + +/* VLAN1 Tag Register */ +#define VLAN1 (0x120) + +/* VLAN2 Tag Register */ +#define VLAN2 (0x124) + +/* Wake Up Frame Filter Register */ +#define WUFF (0x128) +#define LAN9500_WUFF_NUM (4) +#define LAN9500A_WUFF_NUM (8) + +/* Wake Up Control and Status Register */ +#define WUCSR (0x12C) +#define WUCSR_WFF_PTR_RST_ (0x80000000) /* WFrame Filter Pointer Rst */ +#define WUCSR_GUE_ (0x00000200) /* Global Unicast Enable */ +#define WUCSR_WUFR_ (0x00000040) /* Wakeup Frame Received */ +#define WUCSR_MPR_ (0x00000020) /* Magic Packet Received */ +#define WUCSR_WAKE_EN_ (0x00000004) /* Wakeup Frame Enable */ +#define WUCSR_MPEN_ (0x00000002) /* Magic Packet Enable */ + +/* Checksum Offload Engine Control Register */ +#define COE_CR (0x130) +#define Tx_COE_EN_ (0x00010000) /* TX Csum Offload Enable */ +#define Rx_COE_MODE_ (0x00000002) /* RX Csum Offload Mode */ +#define Rx_COE_EN_ (0x00000001) /* RX Csum Offload Enable */ + +/* Vendor-specific PHY Definitions (via MII access) */ /* EDPD NLP / crossover time configuration (LAN9500A only) */ #define PHY_EDPD_CONFIG (16) #define PHY_EDPD_CONFIG_TX_NLP_EN_ ((u16)0x8000) @@ -255,17 +317,20 @@ #define MODE_CTRL_STS_EDPWRDOWN_ ((u16)0x2000) #define MODE_CTRL_STS_ENERGYON_ ((u16)0x0002) +/* Control/Status Indication Register */ #define SPECIAL_CTRL_STS (27) #define SPECIAL_CTRL_STS_OVRRD_AMDIX_ ((u16)0x8000) #define SPECIAL_CTRL_STS_AMDIX_ENABLE_ ((u16)0x4000) #define SPECIAL_CTRL_STS_AMDIX_STATE_ ((u16)0x2000) +/* Interrupt Source Register */ #define PHY_INT_SRC (29) #define PHY_INT_SRC_ENERGY_ON_ ((u16)0x0080) #define PHY_INT_SRC_ANEG_COMP_ ((u16)0x0040) #define PHY_INT_SRC_REMOTE_FAULT_ ((u16)0x0020) #define PHY_INT_SRC_LINK_DOWN_ ((u16)0x0010) +/* Interrupt Mask Register */ #define PHY_INT_MASK (30) #define PHY_INT_MASK_ENERGY_ON_ ((u16)0x0080) #define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040) @@ -273,7 +338,7 @@ #define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010) #define PHY_INT_MASK_DEFAULT_ (PHY_INT_MASK_ANEG_COMP_ | \ PHY_INT_MASK_LINK_DOWN_) - +/* PHY Special Control/Status Register */ #define PHY_SPECIAL (31) #define PHY_SPECIAL_SPD_ ((u16)0x001C) #define PHY_SPECIAL_SPD_10HALF_ ((u16)0x0004) @@ -287,12 +352,13 @@ #define USB_VENDOR_REQUEST_GET_STATS 0xA2 /* Interrupt Endpoint status word bitfields */ -#define INT_ENP_TX_STOP_ ((u32)BIT(17)) -#define INT_ENP_RX_STOP_ ((u32)BIT(16)) -#define INT_ENP_PHY_INT_ ((u32)BIT(15)) -#define INT_ENP_TXE_ ((u32)BIT(14)) -#define INT_ENP_TDFU_ ((u32)BIT(13)) -#define INT_ENP_TDFO_ ((u32)BIT(12)) -#define INT_ENP_RXDF_ ((u32)BIT(11)) +#define INT_ENP_MAC_RTO_ ((u32)BIT(18)) /* MAC Reset Time Out */ +#define INT_ENP_TX_STOP_ ((u32)BIT(17)) /* TX Stopped */ +#define INT_ENP_RX_STOP_ ((u32)BIT(16)) /* RX Stopped */ +#define INT_ENP_PHY_INT_ ((u32)BIT(15)) /* PHY Interrupt */ +#define INT_ENP_TXE_ ((u32)BIT(14)) /* TX Error */ +#define INT_ENP_TDFU_ ((u32)BIT(13)) /* TX FIFO Underrun */ +#define INT_ENP_TDFO_ ((u32)BIT(12)) /* TX FIFO Overrun */ +#define INT_ENP_RXDF_ ((u32)BIT(11)) /* RX Dropped Frame */ #endif /* _SMSC95XX_H */ -- cgit From d51e4af5c2092c48a06ceaf2323b13a39a2df4ee Mon Sep 17 00:00:00 2001 From: "Chopra, Manish" Date: Thu, 13 Apr 2017 04:54:44 -0700 Subject: qed: aRFS infrastructure support This patch adds necessary APIs to interface with qede aRFS support in successive patch. It also reserves separate PTT entry for aRFS, [as being in fastpath flow] for hardware access instead of trying to acquire it at run time from the ptt pool. Signed-off-by: Manish Chopra Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 2 + drivers/net/ethernet/qlogic/qed/qed_cxt.c | 13 +- drivers/net/ethernet/qlogic/qed/qed_hsi.h | 187 +++++++++++++++++++++ .../net/ethernet/qlogic/qed/qed_init_fw_funcs.c | 129 ++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_l2.c | 133 +++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_l2.h | 8 + drivers/net/ethernet/qlogic/qed/qed_main.c | 26 +++ drivers/net/ethernet/qlogic/qed/qed_reg_addr.h | 8 + drivers/net/ethernet/qlogic/qed/qed_sp.h | 1 + include/linux/qed/qed_eth_if.h | 8 + include/linux/qed/qed_if.h | 7 + 11 files changed, 519 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 4896ee0cc458..c539ba138db9 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -504,6 +504,8 @@ struct qed_hwfn { u8 dcbx_no_edpm; u8 db_bar_no_edpm; + struct qed_ptt *p_arfs_ptt; + /* p_ptp_ptt is valid for leading HWFN only */ struct qed_ptt *p_ptp_ptt; struct qed_simd_fp_handler simd_proto_handler[64]; diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c index 15ef6ebed6bb..b3aaa985956e 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c @@ -219,9 +219,6 @@ struct qed_cxt_mngr { */ u32 vf_count; - /* total number of SRQ's for this hwfn */ - u32 srq_count; - /* Acquired CIDs */ struct qed_cid_acquired_map acquired[MAX_CONN_TYPES]; @@ -237,6 +234,12 @@ struct qed_cxt_mngr { u32 t2_num_pages; u64 first_free; u64 last_free; + + /* total number of SRQ's for this hwfn */ + u32 srq_count; + + /* Maximal number of L2 steering filters */ + u32 arfs_count; }; static bool src_proto(enum protocol_type type) { @@ -291,6 +294,9 @@ static void qed_cxt_src_iids(struct qed_cxt_mngr *p_mngr, iids->pf_cids += p_mngr->conn_cfg[i].cid_count; iids->per_vf_cids += p_mngr->conn_cfg[i].cids_per_vf; } + + /* Add L2 filtering filters in addition */ + iids->pf_cids += p_mngr->arfs_count; } /* counts the iids for the Timers block configuration */ @@ -2007,6 +2013,7 @@ int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn, u32 rdma_tasks) qed_cxt_set_proto_cid_count(p_hwfn, PROTOCOLID_ETH, p_params->num_cons, 1); + p_hwfn->p_cxt_mngr->arfs_count = p_params->num_arfs_filters; break; } case QED_PCI_FCOE: diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index 815c4ec5b458..858a57a73589 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -3473,6 +3473,11 @@ void qed_set_geneve_dest_port(struct qed_hwfn *p_hwfn, void qed_set_geneve_enable(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, bool eth_geneve_enable, bool ip_geneve_enable); +void qed_set_rfs_mode_disable(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u16 pf_id); +void qed_set_rfs_mode_enable(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, + u16 pf_id, bool tcp, bool udp, + bool ipv4, bool ipv6); #define YSTORM_FLOW_CONTROL_MODE_OFFSET (IRO[0].base) #define YSTORM_FLOW_CONTROL_MODE_SIZE (IRO[0].size) @@ -4862,6 +4867,18 @@ struct eth_vport_tx_mode { __le16 reserved2[3]; }; +enum gft_filter_update_action { + GFT_ADD_FILTER, + GFT_DELETE_FILTER, + MAX_GFT_FILTER_UPDATE_ACTION +}; + +enum gft_logic_filter_type { + GFT_FILTER_TYPE, + RFS_FILTER_TYPE, + MAX_GFT_LOGIC_FILTER_TYPE +}; + /* Ramrod data for rx queue start ramrod */ struct rx_queue_start_ramrod_data { __le16 rx_queue_id; @@ -4932,6 +4949,16 @@ struct rx_udp_filter_data { __le32 tenant_id; }; +struct rx_update_gft_filter_data { + struct regpair pkt_hdr_addr; + __le16 pkt_hdr_length; + __le16 rx_qid_or_action_icid; + u8 vport_id; + u8 filter_type; + u8 filter_action; + u8 reserved; +}; + /* Ramrod data for rx queue start ramrod */ struct tx_queue_start_ramrod_data { __le16 sb_id; @@ -5075,6 +5102,166 @@ struct vport_update_ramrod_data { struct eth_vport_rss_config rss_config; }; +struct gft_cam_line { + __le32 camline; +#define GFT_CAM_LINE_VALID_MASK 0x1 +#define GFT_CAM_LINE_VALID_SHIFT 0 +#define GFT_CAM_LINE_DATA_MASK 0x3FFF +#define GFT_CAM_LINE_DATA_SHIFT 1 +#define GFT_CAM_LINE_MASK_BITS_MASK 0x3FFF +#define GFT_CAM_LINE_MASK_BITS_SHIFT 15 +#define GFT_CAM_LINE_RESERVED1_MASK 0x7 +#define GFT_CAM_LINE_RESERVED1_SHIFT 29 +}; + +struct gft_cam_line_mapped { + __le32 camline; +#define GFT_CAM_LINE_MAPPED_VALID_MASK 0x1 +#define GFT_CAM_LINE_MAPPED_VALID_SHIFT 0 +#define GFT_CAM_LINE_MAPPED_IP_VERSION_MASK 0x1 +#define GFT_CAM_LINE_MAPPED_IP_VERSION_SHIFT 1 +#define GFT_CAM_LINE_MAPPED_TUNNEL_IP_VERSION_MASK 0x1 +#define GFT_CAM_LINE_MAPPED_TUNNEL_IP_VERSION_SHIFT 2 +#define GFT_CAM_LINE_MAPPED_UPPER_PROTOCOL_TYPE_MASK 0xF +#define GFT_CAM_LINE_MAPPED_UPPER_PROTOCOL_TYPE_SHIFT 3 +#define GFT_CAM_LINE_MAPPED_TUNNEL_TYPE_MASK 0xF +#define GFT_CAM_LINE_MAPPED_TUNNEL_TYPE_SHIFT 7 +#define GFT_CAM_LINE_MAPPED_PF_ID_MASK 0xF +#define GFT_CAM_LINE_MAPPED_PF_ID_SHIFT 11 +#define GFT_CAM_LINE_MAPPED_IP_VERSION_MASK_MASK 0x1 +#define GFT_CAM_LINE_MAPPED_IP_VERSION_MASK_SHIFT 15 +#define GFT_CAM_LINE_MAPPED_TUNNEL_IP_VERSION_MASK_MASK 0x1 +#define GFT_CAM_LINE_MAPPED_TUNNEL_IP_VERSION_MASK_SHIFT 16 +#define GFT_CAM_LINE_MAPPED_UPPER_PROTOCOL_TYPE_MASK_MASK 0xF +#define GFT_CAM_LINE_MAPPED_UPPER_PROTOCOL_TYPE_MASK_SHIFT 17 +#define GFT_CAM_LINE_MAPPED_TUNNEL_TYPE_MASK_MASK 0xF +#define GFT_CAM_LINE_MAPPED_TUNNEL_TYPE_MASK_SHIFT 21 +#define GFT_CAM_LINE_MAPPED_PF_ID_MASK_MASK 0xF +#define GFT_CAM_LINE_MAPPED_PF_ID_MASK_SHIFT 25 +#define GFT_CAM_LINE_MAPPED_RESERVED1_MASK 0x7 +#define GFT_CAM_LINE_MAPPED_RESERVED1_SHIFT 29 +}; + +union gft_cam_line_union { + struct gft_cam_line cam_line; + struct gft_cam_line_mapped cam_line_mapped; +}; + +enum gft_profile_ip_version { + GFT_PROFILE_IPV4 = 0, + GFT_PROFILE_IPV6 = 1, + MAX_GFT_PROFILE_IP_VERSION +}; + +enum gft_profile_upper_protocol_type { + GFT_PROFILE_ROCE_PROTOCOL = 0, + GFT_PROFILE_RROCE_PROTOCOL = 1, + GFT_PROFILE_FCOE_PROTOCOL = 2, + GFT_PROFILE_ICMP_PROTOCOL = 3, + GFT_PROFILE_ARP_PROTOCOL = 4, + GFT_PROFILE_USER_TCP_SRC_PORT_1_INNER = 5, + GFT_PROFILE_USER_TCP_DST_PORT_1_INNER = 6, + GFT_PROFILE_TCP_PROTOCOL = 7, + GFT_PROFILE_USER_UDP_DST_PORT_1_INNER = 8, + GFT_PROFILE_USER_UDP_DST_PORT_2_OUTER = 9, + GFT_PROFILE_UDP_PROTOCOL = 10, + GFT_PROFILE_USER_IP_1_INNER = 11, + GFT_PROFILE_USER_IP_2_OUTER = 12, + GFT_PROFILE_USER_ETH_1_INNER = 13, + GFT_PROFILE_USER_ETH_2_OUTER = 14, + GFT_PROFILE_RAW = 15, + MAX_GFT_PROFILE_UPPER_PROTOCOL_TYPE +}; + +struct gft_ram_line { + __le32 low32bits; +#define GFT_RAM_LINE_VLAN_SELECT_MASK 0x3 +#define GFT_RAM_LINE_VLAN_SELECT_SHIFT 0 +#define GFT_RAM_LINE_TUNNEL_ENTROPHY_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_ENTROPHY_SHIFT 2 +#define GFT_RAM_LINE_TUNNEL_TTL_EQUAL_ONE_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_TTL_EQUAL_ONE_SHIFT 3 +#define GFT_RAM_LINE_TUNNEL_TTL_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_TTL_SHIFT 4 +#define GFT_RAM_LINE_TUNNEL_ETHERTYPE_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_ETHERTYPE_SHIFT 5 +#define GFT_RAM_LINE_TUNNEL_DST_PORT_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_DST_PORT_SHIFT 6 +#define GFT_RAM_LINE_TUNNEL_SRC_PORT_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_SRC_PORT_SHIFT 7 +#define GFT_RAM_LINE_TUNNEL_DSCP_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_DSCP_SHIFT 8 +#define GFT_RAM_LINE_TUNNEL_OVER_IP_PROTOCOL_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_OVER_IP_PROTOCOL_SHIFT 9 +#define GFT_RAM_LINE_TUNNEL_DST_IP_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_DST_IP_SHIFT 10 +#define GFT_RAM_LINE_TUNNEL_SRC_IP_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_SRC_IP_SHIFT 11 +#define GFT_RAM_LINE_TUNNEL_PRIORITY_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_PRIORITY_SHIFT 12 +#define GFT_RAM_LINE_TUNNEL_PROVIDER_VLAN_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_PROVIDER_VLAN_SHIFT 13 +#define GFT_RAM_LINE_TUNNEL_VLAN_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_VLAN_SHIFT 14 +#define GFT_RAM_LINE_TUNNEL_DST_MAC_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_DST_MAC_SHIFT 15 +#define GFT_RAM_LINE_TUNNEL_SRC_MAC_MASK 0x1 +#define GFT_RAM_LINE_TUNNEL_SRC_MAC_SHIFT 16 +#define GFT_RAM_LINE_TTL_EQUAL_ONE_MASK 0x1 +#define GFT_RAM_LINE_TTL_EQUAL_ONE_SHIFT 17 +#define GFT_RAM_LINE_TTL_MASK 0x1 +#define GFT_RAM_LINE_TTL_SHIFT 18 +#define GFT_RAM_LINE_ETHERTYPE_MASK 0x1 +#define GFT_RAM_LINE_ETHERTYPE_SHIFT 19 +#define GFT_RAM_LINE_RESERVED0_MASK 0x1 +#define GFT_RAM_LINE_RESERVED0_SHIFT 20 +#define GFT_RAM_LINE_TCP_FLAG_FIN_MASK 0x1 +#define GFT_RAM_LINE_TCP_FLAG_FIN_SHIFT 21 +#define GFT_RAM_LINE_TCP_FLAG_SYN_MASK 0x1 +#define GFT_RAM_LINE_TCP_FLAG_SYN_SHIFT 22 +#define GFT_RAM_LINE_TCP_FLAG_RST_MASK 0x1 +#define GFT_RAM_LINE_TCP_FLAG_RST_SHIFT 23 +#define GFT_RAM_LINE_TCP_FLAG_PSH_MASK 0x1 +#define GFT_RAM_LINE_TCP_FLAG_PSH_SHIFT 24 +#define GFT_RAM_LINE_TCP_FLAG_ACK_MASK 0x1 +#define GFT_RAM_LINE_TCP_FLAG_ACK_SHIFT 25 +#define GFT_RAM_LINE_TCP_FLAG_URG_MASK 0x1 +#define GFT_RAM_LINE_TCP_FLAG_URG_SHIFT 26 +#define GFT_RAM_LINE_TCP_FLAG_ECE_MASK 0x1 +#define GFT_RAM_LINE_TCP_FLAG_ECE_SHIFT 27 +#define GFT_RAM_LINE_TCP_FLAG_CWR_MASK 0x1 +#define GFT_RAM_LINE_TCP_FLAG_CWR_SHIFT 28 +#define GFT_RAM_LINE_TCP_FLAG_NS_MASK 0x1 +#define GFT_RAM_LINE_TCP_FLAG_NS_SHIFT 29 +#define GFT_RAM_LINE_DST_PORT_MASK 0x1 +#define GFT_RAM_LINE_DST_PORT_SHIFT 30 +#define GFT_RAM_LINE_SRC_PORT_MASK 0x1 +#define GFT_RAM_LINE_SRC_PORT_SHIFT 31 + __le32 high32bits; +#define GFT_RAM_LINE_DSCP_MASK 0x1 +#define GFT_RAM_LINE_DSCP_SHIFT 0 +#define GFT_RAM_LINE_OVER_IP_PROTOCOL_MASK 0x1 +#define GFT_RAM_LINE_OVER_IP_PROTOCOL_SHIFT 1 +#define GFT_RAM_LINE_DST_IP_MASK 0x1 +#define GFT_RAM_LINE_DST_IP_SHIFT 2 +#define GFT_RAM_LINE_SRC_IP_MASK 0x1 +#define GFT_RAM_LINE_SRC_IP_SHIFT 3 +#define GFT_RAM_LINE_PRIORITY_MASK 0x1 +#define GFT_RAM_LINE_PRIORITY_SHIFT 4 +#define GFT_RAM_LINE_PROVIDER_VLAN_MASK 0x1 +#define GFT_RAM_LINE_PROVIDER_VLAN_SHIFT 5 +#define GFT_RAM_LINE_VLAN_MASK 0x1 +#define GFT_RAM_LINE_VLAN_SHIFT 6 +#define GFT_RAM_LINE_DST_MAC_MASK 0x1 +#define GFT_RAM_LINE_DST_MAC_SHIFT 7 +#define GFT_RAM_LINE_SRC_MAC_MASK 0x1 +#define GFT_RAM_LINE_SRC_MAC_SHIFT 8 +#define GFT_RAM_LINE_TENANT_ID_MASK 0x1 +#define GFT_RAM_LINE_TENANT_ID_SHIFT 9 +#define GFT_RAM_LINE_RESERVED1_MASK 0x3FFFFF +#define GFT_RAM_LINE_RESERVED1_SHIFT 10 +}; + struct mstorm_eth_conn_ag_ctx { u8 byte0; u8 byte1; diff --git a/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c b/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c index 2a50e2b7568f..67200c5498ab 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c +++ b/drivers/net/ethernet/qlogic/qed/qed_init_fw_funcs.c @@ -961,3 +961,132 @@ void qed_set_geneve_enable(struct qed_hwfn *p_hwfn, qed_wr(p_hwfn, p_ptt, DORQ_REG_L2_EDPM_TUNNEL_NGE_IP_EN, ip_geneve_enable ? 1 : 0); } + +#define T_ETH_PACKET_MATCH_RFS_EVENTID 25 +#define PARSER_ETH_CONN_CM_HDR (0x0) +#define CAM_LINE_SIZE sizeof(u32) +#define RAM_LINE_SIZE sizeof(u64) +#define REG_SIZE sizeof(u32) + +void qed_set_rfs_mode_disable(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, u16 pf_id) +{ + union gft_cam_line_union camline; + struct gft_ram_line ramline; + u32 *p_ramline, i; + + p_ramline = (u32 *)&ramline; + + /*stop using gft logic */ + qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_GFT, 0); + qed_wr(p_hwfn, p_ptt, PRS_REG_CM_HDR_GFT, 0x0); + memset(&camline, 0, sizeof(union gft_cam_line_union)); + qed_wr(p_hwfn, p_ptt, PRS_REG_GFT_CAM + CAM_LINE_SIZE * pf_id, + camline.cam_line_mapped.camline); + memset(&ramline, 0, sizeof(union gft_cam_line_union)); + + for (i = 0; i < RAM_LINE_SIZE / REG_SIZE; i++) { + u32 hw_addr = PRS_REG_GFT_PROFILE_MASK_RAM; + + hw_addr += (RAM_LINE_SIZE * pf_id + i * REG_SIZE); + + qed_wr(p_hwfn, p_ptt, hw_addr, *(p_ramline + i)); + } +} + +void qed_set_rfs_mode_enable(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, + u16 pf_id, bool tcp, bool udp, + bool ipv4, bool ipv6) +{ + u32 rfs_cm_hdr_event_id, *p_ramline; + union gft_cam_line_union camline; + struct gft_ram_line ramline; + int i; + + rfs_cm_hdr_event_id = qed_rd(p_hwfn, p_ptt, PRS_REG_CM_HDR_GFT); + p_ramline = (u32 *)&ramline; + + if (!ipv6 && !ipv4) + DP_NOTICE(p_hwfn, + "set_rfs_mode_enable: must accept at least on of - ipv4 or ipv6"); + if (!tcp && !udp) + DP_NOTICE(p_hwfn, + "set_rfs_mode_enable: must accept at least on of - udp or tcp"); + + rfs_cm_hdr_event_id |= T_ETH_PACKET_MATCH_RFS_EVENTID << + PRS_REG_CM_HDR_GFT_EVENT_ID_SHIFT; + rfs_cm_hdr_event_id |= PARSER_ETH_CONN_CM_HDR << + PRS_REG_CM_HDR_GFT_CM_HDR_SHIFT; + qed_wr(p_hwfn, p_ptt, PRS_REG_CM_HDR_GFT, rfs_cm_hdr_event_id); + + /* Configure Registers for RFS mode */ + qed_wr(p_hwfn, p_ptt, PRS_REG_SEARCH_GFT, 1); + qed_wr(p_hwfn, p_ptt, PRS_REG_LOAD_L2_FILTER, 0); + camline.cam_line_mapped.camline = 0; + + /* cam line is now valid!! */ + SET_FIELD(camline.cam_line_mapped.camline, + GFT_CAM_LINE_MAPPED_VALID, 1); + + /* filters are per PF!! */ + SET_FIELD(camline.cam_line_mapped.camline, + GFT_CAM_LINE_MAPPED_PF_ID_MASK, 1); + SET_FIELD(camline.cam_line_mapped.camline, + GFT_CAM_LINE_MAPPED_PF_ID, pf_id); + if (!(tcp && udp)) { + SET_FIELD(camline.cam_line_mapped.camline, + GFT_CAM_LINE_MAPPED_UPPER_PROTOCOL_TYPE_MASK, 1); + if (tcp) + SET_FIELD(camline.cam_line_mapped.camline, + GFT_CAM_LINE_MAPPED_UPPER_PROTOCOL_TYPE, + GFT_PROFILE_TCP_PROTOCOL); + else + SET_FIELD(camline.cam_line_mapped.camline, + GFT_CAM_LINE_MAPPED_UPPER_PROTOCOL_TYPE, + GFT_PROFILE_UDP_PROTOCOL); + } + + if (!(ipv4 && ipv6)) { + SET_FIELD(camline.cam_line_mapped.camline, + GFT_CAM_LINE_MAPPED_IP_VERSION_MASK, 1); + if (ipv4) + SET_FIELD(camline.cam_line_mapped.camline, + GFT_CAM_LINE_MAPPED_IP_VERSION, + GFT_PROFILE_IPV4); + else + SET_FIELD(camline.cam_line_mapped.camline, + GFT_CAM_LINE_MAPPED_IP_VERSION, + GFT_PROFILE_IPV6); + } + + /* write characteristics to cam */ + qed_wr(p_hwfn, p_ptt, PRS_REG_GFT_CAM + CAM_LINE_SIZE * pf_id, + camline.cam_line_mapped.camline); + camline.cam_line_mapped.camline = qed_rd(p_hwfn, p_ptt, + PRS_REG_GFT_CAM + + CAM_LINE_SIZE * pf_id); + + /* write line to RAM - compare to filter 4 tuple */ + ramline.low32bits = 0; + ramline.high32bits = 0; + SET_FIELD(ramline.high32bits, GFT_RAM_LINE_DST_IP, 1); + SET_FIELD(ramline.high32bits, GFT_RAM_LINE_SRC_IP, 1); + SET_FIELD(ramline.low32bits, GFT_RAM_LINE_SRC_PORT, 1); + SET_FIELD(ramline.low32bits, GFT_RAM_LINE_DST_PORT, 1); + + /* each iteration write to reg */ + for (i = 0; i < RAM_LINE_SIZE / REG_SIZE; i++) + qed_wr(p_hwfn, p_ptt, + PRS_REG_GFT_PROFILE_MASK_RAM + RAM_LINE_SIZE * pf_id + + i * REG_SIZE, *(p_ramline + i)); + + /* set default profile so that no filter match will happen */ + ramline.low32bits = 0xffff; + ramline.high32bits = 0xffff; + + for (i = 0; i < RAM_LINE_SIZE / REG_SIZE; i++) + qed_wr(p_hwfn, p_ptt, + PRS_REG_GFT_PROFILE_MASK_RAM + RAM_LINE_SIZE * + PRS_GFT_CAM_LINES_NO_MATCH + i * REG_SIZE, + *(p_ramline + i)); +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index d56441da87c5..eb5e280eb104 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -1799,6 +1799,84 @@ void qed_reset_vport_stats(struct qed_dev *cdev) _qed_get_vport_stats(cdev, cdev->reset_stats); } +static void +qed_arfs_mode_configure(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, + struct qed_arfs_config_params *p_cfg_params) +{ + if (p_cfg_params->arfs_enable) { + qed_set_rfs_mode_enable(p_hwfn, p_ptt, p_hwfn->rel_pf_id, + p_cfg_params->tcp, p_cfg_params->udp, + p_cfg_params->ipv4, p_cfg_params->ipv6); + DP_VERBOSE(p_hwfn, QED_MSG_SP, + "tcp = %s, udp = %s, ipv4 = %s, ipv6 =%s\n", + p_cfg_params->tcp ? "Enable" : "Disable", + p_cfg_params->udp ? "Enable" : "Disable", + p_cfg_params->ipv4 ? "Enable" : "Disable", + p_cfg_params->ipv6 ? "Enable" : "Disable"); + } else { + qed_set_rfs_mode_disable(p_hwfn, p_ptt, p_hwfn->rel_pf_id); + } + + DP_VERBOSE(p_hwfn, QED_MSG_SP, "Configured ARFS mode : %s\n", + p_cfg_params->arfs_enable ? "Enable" : "Disable"); +} + +static int +qed_configure_rfs_ntuple_filter(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, + struct qed_spq_comp_cb *p_cb, + dma_addr_t p_addr, u16 length, u16 qid, + u8 vport_id, bool b_is_add) +{ + struct rx_update_gft_filter_data *p_ramrod = NULL; + struct qed_spq_entry *p_ent = NULL; + struct qed_sp_init_data init_data; + u16 abs_rx_q_id = 0; + u8 abs_vport_id = 0; + int rc = -EINVAL; + + rc = qed_fw_vport(p_hwfn, vport_id, &abs_vport_id); + if (rc) + return rc; + + rc = qed_fw_l2_queue(p_hwfn, qid, &abs_rx_q_id); + if (rc) + return rc; + + /* Get SPQ entry */ + memset(&init_data, 0, sizeof(init_data)); + init_data.cid = qed_spq_get_cid(p_hwfn); + + init_data.opaque_fid = p_hwfn->hw_info.opaque_fid; + + if (p_cb) { + init_data.comp_mode = QED_SPQ_MODE_CB; + init_data.p_comp_data = p_cb; + } else { + init_data.comp_mode = QED_SPQ_MODE_EBLOCK; + } + + rc = qed_sp_init_request(p_hwfn, &p_ent, + ETH_RAMROD_GFT_UPDATE_FILTER, + PROTOCOLID_ETH, &init_data); + if (rc) + return rc; + + p_ramrod = &p_ent->ramrod.rx_update_gft; + DMA_REGPAIR_LE(p_ramrod->pkt_hdr_addr, p_addr); + p_ramrod->pkt_hdr_length = cpu_to_le16(length); + p_ramrod->rx_qid_or_action_icid = cpu_to_le16(abs_rx_q_id); + p_ramrod->vport_id = abs_vport_id; + p_ramrod->filter_type = RFS_FILTER_TYPE; + p_ramrod->filter_action = b_is_add ? GFT_ADD_FILTER : GFT_DELETE_FILTER; + + DP_VERBOSE(p_hwfn, QED_MSG_SP, + "V[%0x], Q[%04x] - %s filter from 0x%llx [length %04xb]\n", + abs_vport_id, abs_rx_q_id, + b_is_add ? "Adding" : "Removing", (u64)p_addr, length); + + return qed_spq_post(p_hwfn, p_ent, NULL); +} + static int qed_fill_eth_dev_info(struct qed_dev *cdev, struct qed_dev_eth_info *info) { @@ -2356,6 +2434,59 @@ static int qed_configure_filter(struct qed_dev *cdev, } } +static int qed_configure_arfs_searcher(struct qed_dev *cdev, bool en_searcher) +{ + struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); + struct qed_arfs_config_params arfs_config_params; + + memset(&arfs_config_params, 0, sizeof(arfs_config_params)); + arfs_config_params.tcp = true; + arfs_config_params.udp = true; + arfs_config_params.ipv4 = true; + arfs_config_params.ipv6 = true; + arfs_config_params.arfs_enable = en_searcher; + + qed_arfs_mode_configure(p_hwfn, p_hwfn->p_arfs_ptt, + &arfs_config_params); + return 0; +} + +static void +qed_arfs_sp_response_handler(struct qed_hwfn *p_hwfn, + void *cookie, union event_ring_data *data, + u8 fw_return_code) +{ + struct qed_common_cb_ops *op = p_hwfn->cdev->protocol_ops.common; + void *dev = p_hwfn->cdev->ops_cookie; + + op->arfs_filter_op(dev, cookie, fw_return_code); +} + +static int qed_ntuple_arfs_filter_config(struct qed_dev *cdev, void *cookie, + dma_addr_t mapping, u16 length, + u16 vport_id, u16 rx_queue_id, + bool add_filter) +{ + struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); + struct qed_spq_comp_cb cb; + int rc = -EINVAL; + + cb.function = qed_arfs_sp_response_handler; + cb.cookie = cookie; + + rc = qed_configure_rfs_ntuple_filter(p_hwfn, p_hwfn->p_arfs_ptt, + &cb, mapping, length, rx_queue_id, + vport_id, add_filter); + if (rc) + DP_NOTICE(p_hwfn, + "Failed to issue a-RFS filter configuration\n"); + else + DP_VERBOSE(p_hwfn, NETIF_MSG_DRV, + "Successfully issued a-RFS filter configuration\n"); + + return rc; +} + static int qed_fp_cqe_completion(struct qed_dev *dev, u8 rss_id, struct eth_slow_path_rx_cqe *cqe) { @@ -2397,6 +2528,8 @@ static const struct qed_eth_ops qed_eth_ops_pass = { .eth_cqe_completion = &qed_fp_cqe_completion, .get_vport_stats = &qed_get_vport_stats, .tunn_config = &qed_tunn_configure, + .ntuple_filter_config = &qed_ntuple_arfs_filter_config, + .configure_arfs_searcher = &qed_configure_arfs_searcher, }; const struct qed_eth_ops *qed_get_eth_ops(void) diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.h b/drivers/net/ethernet/qlogic/qed/qed_l2.h index e763abd334f6..6f44229899eb 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.h +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.h @@ -185,6 +185,14 @@ struct qed_filter_accept_flags { #define QED_ACCEPT_BCAST 0x20 }; +struct qed_arfs_config_params { + bool tcp; + bool udp; + bool ipv4; + bool ipv6; + bool arfs_enable; +}; + struct qed_sp_vport_update_params { u16 opaque_fid; u8 vport_id; diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 029f431e89ec..da562cf8a965 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -883,6 +883,9 @@ static void qed_update_pf_params(struct qed_dev *cdev, params->rdma_pf_params.gl_pi = QED_ROCE_PROTOCOL_INDEX; } + if (cdev->num_hwfns > 1 || IS_VF(cdev)) + params->eth_pf_params.num_arfs_filters = 0; + /* In case we might support RDMA, don't allow qede to be greedy * with the L2 contexts. Allow for 64 queues [rx, tx, xdp] per hwfn. */ @@ -926,6 +929,18 @@ static int qed_slowpath_start(struct qed_dev *cdev, goto err; } +#ifdef CONFIG_RFS_ACCEL + if (cdev->num_hwfns == 1) { + p_ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev)); + if (p_ptt) { + QED_LEADING_HWFN(cdev)->p_arfs_ptt = p_ptt; + } else { + DP_NOTICE(cdev, + "Failed to acquire PTT for aRFS\n"); + goto err; + } + } +#endif p_ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev)); if (p_ptt) { QED_LEADING_HWFN(cdev)->p_ptp_ptt = p_ptt; @@ -1032,6 +1047,12 @@ err: if (IS_PF(cdev)) release_firmware(cdev->firmware); +#ifdef CONFIG_RFS_ACCEL + if (IS_PF(cdev) && (cdev->num_hwfns == 1) && + QED_LEADING_HWFN(cdev)->p_arfs_ptt) + qed_ptt_release(QED_LEADING_HWFN(cdev), + QED_LEADING_HWFN(cdev)->p_arfs_ptt); +#endif if (IS_PF(cdev) && QED_LEADING_HWFN(cdev)->p_ptp_ptt) qed_ptt_release(QED_LEADING_HWFN(cdev), QED_LEADING_HWFN(cdev)->p_ptp_ptt); @@ -1049,6 +1070,11 @@ static int qed_slowpath_stop(struct qed_dev *cdev) qed_ll2_dealloc_if(cdev); if (IS_PF(cdev)) { +#ifdef CONFIG_RFS_ACCEL + if (cdev->num_hwfns == 1) + qed_ptt_release(QED_LEADING_HWFN(cdev), + QED_LEADING_HWFN(cdev)->p_arfs_ptt); +#endif qed_ptt_release(QED_LEADING_HWFN(cdev), QED_LEADING_HWFN(cdev)->p_ptp_ptt); qed_free_stream_mem(cdev); diff --git a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h index e65397360ab4..1ae73b2d6d1e 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h +++ b/drivers/net/ethernet/qlogic/qed/qed_reg_addr.h @@ -1560,4 +1560,12 @@ #define NIG_REG_TSGEN_FREECNT_UPDATE_K2 0x509008UL #define CNIG_REG_NIG_PORT0_CONF_K2 0x218200UL +#define PRS_REG_SEARCH_GFT 0x1f11bcUL +#define PRS_REG_CM_HDR_GFT 0x1f11c8UL +#define PRS_REG_GFT_CAM 0x1f1100UL +#define PRS_REG_GFT_PROFILE_MASK_RAM 0x1f1000UL +#define PRS_REG_CM_HDR_GFT_EVENT_ID_SHIFT 0 +#define PRS_REG_CM_HDR_GFT_CM_HDR_SHIFT 8 +#define PRS_REG_LOAD_L2_FILTER 0x1f0198UL + #endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_sp.h b/drivers/net/ethernet/qlogic/qed/qed_sp.h index 30393ffaa8e5..583c8d38c8d7 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_sp.h @@ -84,6 +84,7 @@ union ramrod_data { struct tx_queue_stop_ramrod_data tx_queue_stop; struct vport_start_ramrod_data vport_start; struct vport_stop_ramrod_data vport_stop; + struct rx_update_gft_filter_data rx_update_gft; struct vport_update_ramrod_data vport_update; struct core_rx_start_ramrod_data core_rx_queue_start; struct core_rx_stop_ramrod_data core_rx_queue_stop; diff --git a/include/linux/qed/qed_eth_if.h b/include/linux/qed/qed_eth_if.h index 4cd1f0ccfa36..1eba803cb7f1 100644 --- a/include/linux/qed/qed_eth_if.h +++ b/include/linux/qed/qed_eth_if.h @@ -301,6 +301,14 @@ struct qed_eth_ops { int (*tunn_config)(struct qed_dev *cdev, struct qed_tunn_params *params); + + int (*ntuple_filter_config)(struct qed_dev *cdev, void *cookie, + dma_addr_t mapping, u16 length, + u16 vport_id, u16 rx_queue_id, + bool add_filter); + + int (*configure_arfs_searcher)(struct qed_dev *cdev, + bool en_searcher); }; const struct qed_eth_ops *qed_get_eth_ops(void); diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index 625f80f08f91..d44933a058ee 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -178,6 +178,12 @@ struct qed_eth_pf_params { * to update_pf_params routine invoked before slowpath start */ u16 num_cons; + + /* To enable arfs, previous to HW-init a positive number needs to be + * set [as filters require allocated searcher ILT memory]. + * This will set the maximal number of configured steering-filters. + */ + u32 num_arfs_filters; }; struct qed_fcoe_pf_params { @@ -427,6 +433,7 @@ struct qed_int_info { }; struct qed_common_cb_ops { + void (*arfs_filter_op)(void *dev, void *fltr, u8 fw_rc); void (*link_update)(void *dev, struct qed_link_output *link); void (*dcbx_aen)(void *dev, struct qed_dcbx_get *get, u32 mib_type); -- cgit From e4917d46a6537c5bf05acc2e42df3b67d87775e4 Mon Sep 17 00:00:00 2001 From: "Chopra, Manish" Date: Thu, 13 Apr 2017 04:54:45 -0700 Subject: qede: Add aRFS support This patch adds support for aRFS for TCP and UDP protocols with IPv4/IPv6. Signed-off-by: Manish Chopra Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 22 +- drivers/net/ethernet/qlogic/qede/qede_filter.c | 441 +++++++++++++++++++++++++ drivers/net/ethernet/qlogic/qede/qede_main.c | 50 ++- 3 files changed, 508 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index 5e7ad25db8ad..7e18ae6dec51 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -41,6 +41,9 @@ #include #include #include +#ifdef CONFIG_RFS_ACCEL +#include +#endif #include #include #include @@ -237,7 +240,10 @@ struct qede_dev { u16 vxlan_dst_port; u16 geneve_dst_port; - bool wol_enabled; +#ifdef CONFIG_RFS_ACCEL + struct qede_arfs *arfs; +#endif + bool wol_enabled; struct qede_rdma_dev rdma_info; @@ -439,6 +445,20 @@ struct qede_fastpath { #define QEDE_SP_VXLAN_PORT_CONFIG 2 #define QEDE_SP_GENEVE_PORT_CONFIG 3 +#ifdef CONFIG_RFS_ACCEL +int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, + u16 rxq_index, u32 flow_id); +void qede_process_arfs_filters(struct qede_dev *edev, bool free_fltr); +void qede_poll_for_freeing_arfs_filters(struct qede_dev *edev); +void qede_arfs_filter_op(void *dev, void *filter, u8 fw_rc); +void qede_free_arfs(struct qede_dev *edev); +int qede_alloc_arfs(struct qede_dev *edev); + +#define QEDE_SP_ARFS_CONFIG 4 +#define QEDE_SP_TASK_POLL_DELAY (5 * HZ) +#define QEDE_RFS_MAX_FLTR 256 +#endif + struct qede_reload_args { void (*func)(struct qede_dev *edev, struct qede_reload_args *args); union { diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index b00a4fce44b7..8c594a3ca63b 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -38,6 +38,447 @@ #include #include "qede.h" +#ifdef CONFIG_RFS_ACCEL +struct qede_arfs_tuple { + union { + __be32 src_ipv4; + struct in6_addr src_ipv6; + }; + union { + __be32 dst_ipv4; + struct in6_addr dst_ipv6; + }; + __be16 src_port; + __be16 dst_port; + __be16 eth_proto; + u8 ip_proto; +}; + +struct qede_arfs_fltr_node { +#define QEDE_FLTR_VALID 0 + unsigned long state; + + /* pointer to aRFS packet buffer */ + void *data; + + /* dma map address of aRFS packet buffer */ + dma_addr_t mapping; + + /* length of aRFS packet buffer */ + int buf_len; + + /* tuples to hold from aRFS packet buffer */ + struct qede_arfs_tuple tuple; + + u32 flow_id; + u16 sw_id; + u16 rxq_id; + u16 next_rxq_id; + bool filter_op; + bool used; + struct hlist_node node; +}; + +struct qede_arfs { +#define QEDE_ARFS_POLL_COUNT 100 +#define QEDE_RFS_FLW_BITSHIFT (4) +#define QEDE_RFS_FLW_MASK ((1 << QEDE_RFS_FLW_BITSHIFT) - 1) + struct hlist_head arfs_hl_head[1 << QEDE_RFS_FLW_BITSHIFT]; + + /* lock for filter list access */ + spinlock_t arfs_list_lock; + unsigned long *arfs_fltr_bmap; + int filter_count; + bool enable; +}; + +static void qede_configure_arfs_fltr(struct qede_dev *edev, + struct qede_arfs_fltr_node *n, + u16 rxq_id, bool add_fltr) +{ + const struct qed_eth_ops *op = edev->ops; + + if (n->used) + return; + + DP_VERBOSE(edev, NETIF_MSG_RX_STATUS, + "%s arfs filter flow_id=%d, sw_id=%d, src_port=%d, dst_port=%d, rxq=%d\n", + add_fltr ? "Adding" : "Deleting", + n->flow_id, n->sw_id, ntohs(n->tuple.src_port), + ntohs(n->tuple.dst_port), rxq_id); + + n->used = true; + n->filter_op = add_fltr; + op->ntuple_filter_config(edev->cdev, n, n->mapping, n->buf_len, 0, + rxq_id, add_fltr); +} + +static void +qede_free_arfs_filter(struct qede_dev *edev, struct qede_arfs_fltr_node *fltr) +{ + kfree(fltr->data); + clear_bit(fltr->sw_id, edev->arfs->arfs_fltr_bmap); + kfree(fltr); +} + +void qede_arfs_filter_op(void *dev, void *filter, u8 fw_rc) +{ + struct qede_arfs_fltr_node *fltr = filter; + struct qede_dev *edev = dev; + + if (fw_rc) { + DP_NOTICE(edev, + "Failed arfs filter configuration fw_rc=%d, flow_id=%d, sw_id=%d, src_port=%d, dst_port=%d, rxq=%d\n", + fw_rc, fltr->flow_id, fltr->sw_id, + ntohs(fltr->tuple.src_port), + ntohs(fltr->tuple.dst_port), fltr->rxq_id); + + spin_lock_bh(&edev->arfs->arfs_list_lock); + + fltr->used = false; + clear_bit(QEDE_FLTR_VALID, &fltr->state); + + spin_unlock_bh(&edev->arfs->arfs_list_lock); + return; + } + + spin_lock_bh(&edev->arfs->arfs_list_lock); + + fltr->used = false; + + if (fltr->filter_op) { + set_bit(QEDE_FLTR_VALID, &fltr->state); + if (fltr->rxq_id != fltr->next_rxq_id) + qede_configure_arfs_fltr(edev, fltr, fltr->rxq_id, + false); + } else { + clear_bit(QEDE_FLTR_VALID, &fltr->state); + if (fltr->rxq_id != fltr->next_rxq_id) { + fltr->rxq_id = fltr->next_rxq_id; + qede_configure_arfs_fltr(edev, fltr, + fltr->rxq_id, true); + } + } + + spin_unlock_bh(&edev->arfs->arfs_list_lock); +} + +/* Should be called while qede_lock is held */ +void qede_process_arfs_filters(struct qede_dev *edev, bool free_fltr) +{ + int i; + + for (i = 0; i <= QEDE_RFS_FLW_MASK; i++) { + struct hlist_node *temp; + struct hlist_head *head; + struct qede_arfs_fltr_node *fltr; + + head = &edev->arfs->arfs_hl_head[i]; + + hlist_for_each_entry_safe(fltr, temp, head, node) { + bool del = false; + + if (edev->state != QEDE_STATE_OPEN) + del = true; + + spin_lock_bh(&edev->arfs->arfs_list_lock); + + if ((!test_bit(QEDE_FLTR_VALID, &fltr->state) && + !fltr->used) || free_fltr) { + hlist_del(&fltr->node); + dma_unmap_single(&edev->pdev->dev, + fltr->mapping, + fltr->buf_len, DMA_TO_DEVICE); + qede_free_arfs_filter(edev, fltr); + edev->arfs->filter_count--; + } else { + if ((rps_may_expire_flow(edev->ndev, + fltr->rxq_id, + fltr->flow_id, + fltr->sw_id) || del) && + !free_fltr) + qede_configure_arfs_fltr(edev, fltr, + fltr->rxq_id, + false); + } + + spin_unlock_bh(&edev->arfs->arfs_list_lock); + } + } + + spin_lock_bh(&edev->arfs->arfs_list_lock); + + if (!edev->arfs->filter_count) { + if (edev->arfs->enable) { + edev->arfs->enable = false; + edev->ops->configure_arfs_searcher(edev->cdev, false); + } + } else { + set_bit(QEDE_SP_ARFS_CONFIG, &edev->sp_flags); + schedule_delayed_work(&edev->sp_task, + QEDE_SP_TASK_POLL_DELAY); + } + + spin_unlock_bh(&edev->arfs->arfs_list_lock); +} + +/* This function waits until all aRFS filters get deleted and freed. + * On timeout it frees all filters forcefully. + */ +void qede_poll_for_freeing_arfs_filters(struct qede_dev *edev) +{ + int count = QEDE_ARFS_POLL_COUNT; + + while (count) { + qede_process_arfs_filters(edev, false); + + if (!edev->arfs->filter_count) + break; + + msleep(100); + count--; + } + + if (!count) { + DP_NOTICE(edev, "Timeout in polling for arfs filter free\n"); + + /* Something is terribly wrong, free forcefully */ + qede_process_arfs_filters(edev, true); + } +} + +int qede_alloc_arfs(struct qede_dev *edev) +{ + int i; + + edev->arfs = vzalloc(sizeof(*edev->arfs)); + if (!edev->arfs) + return -ENOMEM; + + spin_lock_init(&edev->arfs->arfs_list_lock); + + for (i = 0; i <= QEDE_RFS_FLW_MASK; i++) + INIT_HLIST_HEAD(&edev->arfs->arfs_hl_head[i]); + + edev->ndev->rx_cpu_rmap = alloc_irq_cpu_rmap(QEDE_RSS_COUNT(edev)); + if (!edev->ndev->rx_cpu_rmap) { + vfree(edev->arfs); + edev->arfs = NULL; + return -ENOMEM; + } + + edev->arfs->arfs_fltr_bmap = vzalloc(BITS_TO_LONGS(QEDE_RFS_MAX_FLTR)); + if (!edev->arfs->arfs_fltr_bmap) { + free_irq_cpu_rmap(edev->ndev->rx_cpu_rmap); + edev->ndev->rx_cpu_rmap = NULL; + vfree(edev->arfs); + edev->arfs = NULL; + return -ENOMEM; + } + + return 0; +} + +void qede_free_arfs(struct qede_dev *edev) +{ + if (!edev->arfs) + return; + + if (edev->ndev->rx_cpu_rmap) + free_irq_cpu_rmap(edev->ndev->rx_cpu_rmap); + + edev->ndev->rx_cpu_rmap = NULL; + vfree(edev->arfs->arfs_fltr_bmap); + edev->arfs->arfs_fltr_bmap = NULL; + vfree(edev->arfs); + edev->arfs = NULL; +} + +static bool qede_compare_ip_addr(struct qede_arfs_fltr_node *tpos, + const struct sk_buff *skb) +{ + if (skb->protocol == htons(ETH_P_IP)) { + if (tpos->tuple.src_ipv4 == ip_hdr(skb)->saddr && + tpos->tuple.dst_ipv4 == ip_hdr(skb)->daddr) + return true; + else + return false; + } else { + struct in6_addr *src = &tpos->tuple.src_ipv6; + u8 size = sizeof(struct in6_addr); + + if (!memcmp(src, &ipv6_hdr(skb)->saddr, size) && + !memcmp(&tpos->tuple.dst_ipv6, &ipv6_hdr(skb)->daddr, size)) + return true; + else + return false; + } +} + +static struct qede_arfs_fltr_node * +qede_arfs_htbl_key_search(struct hlist_head *h, const struct sk_buff *skb, + __be16 src_port, __be16 dst_port, u8 ip_proto) +{ + struct qede_arfs_fltr_node *tpos; + + hlist_for_each_entry(tpos, h, node) + if (tpos->tuple.ip_proto == ip_proto && + tpos->tuple.eth_proto == skb->protocol && + qede_compare_ip_addr(tpos, skb) && + tpos->tuple.src_port == src_port && + tpos->tuple.dst_port == dst_port) + return tpos; + + return NULL; +} + +static struct qede_arfs_fltr_node * +qede_alloc_filter(struct qede_dev *edev, int min_hlen) +{ + struct qede_arfs_fltr_node *n; + int bit_id; + + bit_id = find_first_zero_bit(edev->arfs->arfs_fltr_bmap, + QEDE_RFS_MAX_FLTR); + + if (bit_id >= QEDE_RFS_MAX_FLTR) + return NULL; + + n = kzalloc(sizeof(*n), GFP_ATOMIC); + if (!n) + return NULL; + + n->data = kzalloc(min_hlen, GFP_ATOMIC); + if (!n->data) { + kfree(n); + return NULL; + } + + n->sw_id = (u16)bit_id; + set_bit(bit_id, edev->arfs->arfs_fltr_bmap); + return n; +} + +int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, + u16 rxq_index, u32 flow_id) +{ + struct qede_dev *edev = netdev_priv(dev); + struct qede_arfs_fltr_node *n; + int min_hlen, rc, tp_offset; + struct ethhdr *eth; + __be16 *ports; + u16 tbl_idx; + u8 ip_proto; + + if (skb->encapsulation) + return -EPROTONOSUPPORT; + + if (skb->protocol != htons(ETH_P_IP) && + skb->protocol != htons(ETH_P_IPV6)) + return -EPROTONOSUPPORT; + + if (skb->protocol == htons(ETH_P_IP)) { + ip_proto = ip_hdr(skb)->protocol; + tp_offset = sizeof(struct iphdr); + } else { + ip_proto = ipv6_hdr(skb)->nexthdr; + tp_offset = sizeof(struct ipv6hdr); + } + + if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) + return -EPROTONOSUPPORT; + + ports = (__be16 *)(skb->data + tp_offset); + tbl_idx = skb_get_hash_raw(skb) & QEDE_RFS_FLW_MASK; + + spin_lock_bh(&edev->arfs->arfs_list_lock); + + n = qede_arfs_htbl_key_search(&edev->arfs->arfs_hl_head[tbl_idx], + skb, ports[0], ports[1], ip_proto); + + if (n) { + /* Filter match */ + n->next_rxq_id = rxq_index; + + if (test_bit(QEDE_FLTR_VALID, &n->state)) { + if (n->rxq_id != rxq_index) + qede_configure_arfs_fltr(edev, n, n->rxq_id, + false); + } else { + if (!n->used) { + n->rxq_id = rxq_index; + qede_configure_arfs_fltr(edev, n, n->rxq_id, + true); + } + } + + rc = n->sw_id; + goto ret_unlock; + } + + min_hlen = ETH_HLEN + skb_headlen(skb); + + n = qede_alloc_filter(edev, min_hlen); + if (!n) { + rc = -ENOMEM; + goto ret_unlock; + } + + n->buf_len = min_hlen; + n->rxq_id = rxq_index; + n->next_rxq_id = rxq_index; + n->tuple.src_port = ports[0]; + n->tuple.dst_port = ports[1]; + n->flow_id = flow_id; + + if (skb->protocol == htons(ETH_P_IP)) { + n->tuple.src_ipv4 = ip_hdr(skb)->saddr; + n->tuple.dst_ipv4 = ip_hdr(skb)->daddr; + } else { + memcpy(&n->tuple.src_ipv6, &ipv6_hdr(skb)->saddr, + sizeof(struct in6_addr)); + memcpy(&n->tuple.dst_ipv6, &ipv6_hdr(skb)->daddr, + sizeof(struct in6_addr)); + } + + eth = (struct ethhdr *)n->data; + eth->h_proto = skb->protocol; + n->tuple.eth_proto = skb->protocol; + n->tuple.ip_proto = ip_proto; + memcpy(n->data + ETH_HLEN, skb->data, skb_headlen(skb)); + + n->mapping = dma_map_single(&edev->pdev->dev, n->data, + n->buf_len, DMA_TO_DEVICE); + if (dma_mapping_error(&edev->pdev->dev, n->mapping)) { + DP_NOTICE(edev, "Failed to map DMA memory for arfs\n"); + qede_free_arfs_filter(edev, n); + rc = -ENOMEM; + goto ret_unlock; + } + + INIT_HLIST_NODE(&n->node); + hlist_add_head(&n->node, &edev->arfs->arfs_hl_head[tbl_idx]); + edev->arfs->filter_count++; + + if (edev->arfs->filter_count == 1 && !edev->arfs->enable) { + edev->ops->configure_arfs_searcher(edev->cdev, true); + edev->arfs->enable = true; + } + + qede_configure_arfs_fltr(edev, n, n->rxq_id, true); + + spin_unlock_bh(&edev->arfs->arfs_list_lock); + + set_bit(QEDE_SP_ARFS_CONFIG, &edev->sp_flags); + schedule_delayed_work(&edev->sp_task, 0); + return n->sw_id; + +ret_unlock: + spin_unlock_bh(&edev->arfs->arfs_list_lock); + return rc; +} +#endif + void qede_force_mac(void *dev, u8 *mac, bool forced) { struct qede_dev *edev = dev; diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 8c2baf8b2a08..02b305c19f38 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -225,6 +225,9 @@ static struct pci_driver qede_pci_driver = { static struct qed_eth_cb_ops qede_ll_ops = { { +#ifdef CONFIG_RFS_ACCEL + .arfs_filter_op = qede_arfs_filter_op, +#endif .link_update = qede_link_update, }, .force_mac = qede_force_mac, @@ -554,6 +557,9 @@ static const struct net_device_ops qede_netdev_ops = { .ndo_udp_tunnel_del = qede_udp_tunnel_del, .ndo_features_check = qede_features_check, .ndo_xdp = qede_xdp, +#ifdef CONFIG_RFS_ACCEL + .ndo_rx_flow_steer = qede_rx_flow_steer, +#endif }; /* ------------------------------------------------------------------------- @@ -603,7 +609,7 @@ static void qede_init_ndev(struct qede_dev *edev) { struct net_device *ndev = edev->ndev; struct pci_dev *pdev = edev->pdev; - u32 hw_features; + netdev_features_t hw_features; pci_set_drvdata(pdev, ndev); @@ -629,6 +635,10 @@ static void qede_init_ndev(struct qede_dev *edev) hw_features |= NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL | NETIF_F_TSO_ECN | NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_GRE_CSUM; + + if (!IS_VF(edev) && edev->dev_info.common.num_hwfns == 1) + hw_features |= NETIF_F_NTUPLE; + ndev->hw_enc_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 | NETIF_F_GSO_GRE | @@ -798,6 +808,12 @@ static void qede_sp_task(struct work_struct *work) qed_ops->tunn_config(cdev, &tunn_params); } +#ifdef CONFIG_RFS_ACCEL + if (test_and_clear_bit(QEDE_SP_ARFS_CONFIG, &edev->sp_flags)) { + if (edev->state == QEDE_STATE_OPEN) + qede_process_arfs_filters(edev, false); + } +#endif __qede_unlock(edev); } @@ -808,6 +824,9 @@ static void qede_update_pf_params(struct qed_dev *cdev) /* 64 rx + 64 tx + 64 XDP */ memset(&pf_params, 0, sizeof(struct qed_pf_params)); pf_params.eth_pf_params.num_cons = (MAX_SB_PER_PF_MIMD - 1) * 3; +#ifdef CONFIG_RFS_ACCEL + pf_params.eth_pf_params.num_arfs_filters = QEDE_RFS_MAX_FLTR; +#endif qed_ops->common->update_pf_params(cdev, &pf_params); } @@ -962,9 +981,8 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode) DP_INFO(edev, "Starting qede_remove\n"); - cancel_delayed_work_sync(&edev->sp_task); - unregister_netdev(ndev); + cancel_delayed_work_sync(&edev->sp_task); qede_ptp_remove(edev); @@ -1490,6 +1508,18 @@ static int qede_req_msix_irqs(struct qede_dev *edev) } for (i = 0; i < QEDE_QUEUE_CNT(edev); i++) { +#ifdef CONFIG_RFS_ACCEL + struct qede_fastpath *fp = &edev->fp_array[i]; + + if (edev->ndev->rx_cpu_rmap && (fp->type & QEDE_FASTPATH_RX)) { + rc = irq_cpu_rmap_add(edev->ndev->rx_cpu_rmap, + edev->int_info.msix[i].vector); + if (rc) { + DP_ERR(edev, "Failed to add CPU rmap\n"); + qede_free_arfs(edev); + } + } +#endif rc = request_irq(edev->int_info.msix[i].vector, qede_msix_fp_int, 0, edev->fp_array[i].name, &edev->fp_array[i]); @@ -1871,7 +1901,12 @@ static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode, qede_vlan_mark_nonconfigured(edev); edev->ops->fastpath_stop(edev->cdev); - +#ifdef CONFIG_RFS_ACCEL + if (!IS_VF(edev) && edev->dev_info.common.num_hwfns == 1) { + qede_poll_for_freeing_arfs_filters(edev); + qede_free_arfs(edev); + } +#endif /* Release the interrupts */ qede_sync_free_irqs(edev); edev->ops->common->set_fp_int(edev->cdev, 0); @@ -1923,6 +1958,13 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode, if (rc) goto err2; +#ifdef CONFIG_RFS_ACCEL + if (!IS_VF(edev) && edev->dev_info.common.num_hwfns == 1) { + rc = qede_alloc_arfs(edev); + if (rc) + DP_NOTICE(edev, "aRFS memory allocation failed\n"); + } +#endif qede_napi_add_enable(edev); DP_INFO(edev, "Napi added and enabled\n"); -- cgit From 8ea3e439115a50b1927a4d035b2f84c46fc61c42 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Thu, 13 Apr 2017 08:40:53 -0700 Subject: Subject: net: allow configuring default qdisc Since 3.12 it has been possible to configure the default queuing discipline via sysctl. This patch adds ability to configure the default queue discipline in kernel configuration. This is useful for environments where configuring the value from userspace is difficult to manage. The default is still the same as before (pfifo_fast) and it is possible to change after kernel init with sysctl. This is similar to how TCP congestion control works. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/sched/Kconfig | 45 +++++++++++++++++++++++++++++++++++++++++++++ net/sched/sch_api.c | 9 +++++++++ 2 files changed, 54 insertions(+) diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 403790cce7d2..9fb84f0de6af 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -352,6 +352,51 @@ config NET_SCH_PLUG To compile this code as a module, choose M here: the module will be called sch_plug. +menuconfig NET_SCH_DEFAULT + bool "Allow override default queue discipline" + ---help--- + Support for selection of default queuing discipline. + + Nearly all users can safely say no here, and the default + of pfifo_fast will be used. Many distributions already set + the default value via /proc/sys/net/core/default_qdisc. + + If unsure, say N. + +if NET_SCH_DEFAULT + +choice + prompt "Default queuing discipline" + default DEFAULT_PFIFO_FAST + help + Select the queueing discipline that will be used by default + for all network devices. + + config DEFAULT_FQ + bool "Fair Queue" if NET_SCH_FQ + + config DEFAULT_CODEL + bool "Controlled Delay" if NET_SCH_CODEL + + config DEFAULT_FQ_CODEL + bool "Fair Queue Controlled Delay" if NET_SCH_FQ_CODEL + + config DEFAULT_SFQ + bool "Stochastic Fair Queue" if NET_SCH_SFQ + + config DEFAULT_PFIFO_FAST + bool "Priority FIFO Fast" +endchoice + +config DEFAULT_NET_SCH + string + default "pfifo_fast" if DEFAULT_PFIFO_FAST + default "fq" if DEFAULT_FQ + default "fq_codel" if DEFAULT_FQ_CODEL + default "sfq" if DEFAULT_SFQ + default "pfifo_fast" +endif + comment "Classification" config NET_CLS diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index fcb5ae581c04..9b09ef9f944d 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -251,6 +251,15 @@ int qdisc_set_default(const char *name) return ops ? 0 : -ENOENT; } +#ifdef CONFIG_NET_SCH_DEFAULT +/* Set default value from kernel config */ +static int __init sch_default_qdisc(void) +{ + return qdisc_set_default(CONFIG_DEFAULT_NET_SCH); +} +late_initcall(sch_default_qdisc); +#endif + /* We know handle. Find qdisc among all qdisc's attached to device * (root qdisc, all its children, children of children etc.) * Note: caller either uses rtnl or rcu_read_lock() -- cgit From d06130377c4826624681505c0bb45bfd6eb7cd4f Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 13 Apr 2017 16:49:15 +0100 Subject: net: phy: improve phylib correctness for non-autoneg settings phylib has some undesirable behaviour when forcing a link mode through ethtool. phylib uses this code: idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex), features); to find an index in the settings table. phy_find_setting() starts at index 0, and scans upwards looking for an exact speed and duplex match. When it doesn't find it, it returns MAX_NUM_SETTINGS - 1, which is 10baseT-Half duplex. phy_find_valid() then scans from the point (and effectively only checks one entry) before bailing out, returning MAX_NUM_SETTINGS - 1. phy_sanitize_settings() then sets ->speed to SPEED_10 and ->duplex to DUPLEX_HALF whether or not 10baseT-Half is supported or not. This goes against all the comments against these functions, and 10baseT-Half may not even be supported by the hardware. Rework these functions, introducing a new method of scanning the table. There are two modes of lookup that phylib wants: exact, and inexact. - in exact mode, we return either an exact match or failure - in inexact mode, we return an exact match if it exists, a match at the highest speed that is not greater than the requested speed (ignoring duplex), or failing that, the lowest supported speed, or failure. The biggest difference is that we always check whether the entry is supported before further consideration, so all unsupported entries are not considered as candidates. This results in arguably saner behaviour, better matches the comments, and is probably what users would expect. This becomes important as ethernet speeds increase, PHYs exist which do not support the 10Mbit speeds, and half-duplex is likely to become obsolete - it's already not even an option on 10Gbit and faster links. Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 109 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 66 insertions(+), 43 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index bf7d614ff18f..00280d4eeb56 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -176,7 +176,9 @@ struct phy_setting { u32 setting; }; -/* A mapping of all SUPPORTED settings to speed/duplex */ +/* A mapping of all SUPPORTED settings to speed/duplex. This table + * must be grouped by speed and sorted in descending match priority + * - iow, descending speed. */ static const struct phy_setting settings[] = { { .speed = SPEED_10000, @@ -235,45 +237,70 @@ static const struct phy_setting settings[] = { }, }; -#define MAX_NUM_SETTINGS ARRAY_SIZE(settings) - /** - * phy_find_setting - find a PHY settings array entry that matches speed & duplex + * phy_lookup_setting - lookup a PHY setting * @speed: speed to match * @duplex: duplex to match + * @feature: allowed link modes + * @exact: an exact match is required + * + * Search the settings array for a setting that matches the speed and + * duplex, and which is supported. + * + * If @exact is unset, either an exact match or %NULL for no match will + * be returned. * - * Description: Searches the settings array for the setting which - * matches the desired speed and duplex, and returns the index - * of that setting. Returns the index of the last setting if - * none of the others match. + * If @exact is set, an exact match, the fastest supported setting at + * or below the specified speed, the slowest supported setting, or if + * they all fail, %NULL will be returned. */ -static inline unsigned int phy_find_setting(int speed, int duplex) +static const struct phy_setting * +phy_lookup_setting(int speed, int duplex, u32 features, bool exact) { - unsigned int idx = 0; + const struct phy_setting *p, *match = NULL, *last = NULL; + int i; + + for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) { + if (p->setting & features) { + last = p; + if (p->speed == speed && p->duplex == duplex) { + /* Exact match for speed and duplex */ + match = p; + break; + } else if (!exact) { + if (!match && p->speed <= speed) + /* Candidate */ + match = p; + + if (p->speed < speed) + break; + } + } + } - while (idx < ARRAY_SIZE(settings) && - (settings[idx].speed != speed || settings[idx].duplex != duplex)) - idx++; + if (!match && !exact) + match = last; - return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; + return match; } /** - * phy_find_valid - find a PHY setting that matches the requested features mask - * @idx: The first index in settings[] to search - * @features: A mask of the valid settings + * phy_find_valid - find a PHY setting that matches the requested parameters + * @speed: desired speed + * @duplex: desired duplex + * @supported: mask of supported link modes * - * Description: Returns the index of the first valid setting less - * than or equal to the one pointed to by idx, as determined by - * the mask in features. Returns the index of the last setting - * if nothing else matches. + * Locate a supported phy setting that is, in priority order: + * - an exact match for the specified speed and duplex mode + * - a match for the specified speed, or slower speed + * - the slowest supported speed + * Returns the matched phy_setting entry, or %NULL if no supported phy + * settings were found. */ -static inline unsigned int phy_find_valid(unsigned int idx, u32 features) +static const struct phy_setting * +phy_find_valid(int speed, int duplex, u32 supported) { - while (idx < MAX_NUM_SETTINGS && !(settings[idx].setting & features)) - idx++; - - return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; + return phy_lookup_setting(speed, duplex, supported, false); } /** @@ -293,11 +320,9 @@ unsigned int phy_supported_speeds(struct phy_device *phy, unsigned int count = 0; unsigned int idx = 0; - while (idx < MAX_NUM_SETTINGS && count < size) { - idx = phy_find_valid(idx, phy->supported); - + for (idx = 0; idx < ARRAY_SIZE(settings) && count < size; idx++) { if (!(settings[idx].setting & phy->supported)) - break; + continue; /* Assumes settings are grouped by speed */ if ((count == 0) || @@ -305,7 +330,6 @@ unsigned int phy_supported_speeds(struct phy_device *phy, speeds[count] = settings[idx].speed; count++; } - idx++; } return count; @@ -322,12 +346,7 @@ unsigned int phy_supported_speeds(struct phy_device *phy, */ static inline bool phy_check_valid(int speed, int duplex, u32 features) { - unsigned int idx; - - idx = phy_find_valid(phy_find_setting(speed, duplex), features); - - return settings[idx].speed == speed && settings[idx].duplex == duplex && - (settings[idx].setting & features); + return !!phy_lookup_setting(speed, duplex, features, true); } /** @@ -340,18 +359,22 @@ static inline bool phy_check_valid(int speed, int duplex, u32 features) */ static void phy_sanitize_settings(struct phy_device *phydev) { + const struct phy_setting *setting; u32 features = phydev->supported; - unsigned int idx; /* Sanitize settings based on PHY capabilities */ if ((features & SUPPORTED_Autoneg) == 0) phydev->autoneg = AUTONEG_DISABLE; - idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex), - features); - - phydev->speed = settings[idx].speed; - phydev->duplex = settings[idx].duplex; + setting = phy_find_valid(phydev->speed, phydev->duplex, features); + if (setting) { + phydev->speed = setting->speed; + phydev->duplex = setting->duplex; + } else { + /* We failed to find anything (no supported speeds?) */ + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + } } /** -- cgit From 786df9c2a4216e64e5b7c321405d706036a76ba3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 13 Apr 2017 16:49:20 +0100 Subject: net: phy: simplify phy_supported_speeds() Simplify the loop in phy_supported_speeds(). Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 00280d4eeb56..6afbd973a779 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -320,17 +320,11 @@ unsigned int phy_supported_speeds(struct phy_device *phy, unsigned int count = 0; unsigned int idx = 0; - for (idx = 0; idx < ARRAY_SIZE(settings) && count < size; idx++) { - if (!(settings[idx].setting & phy->supported)) - continue; - + for (idx = 0; idx < ARRAY_SIZE(settings) && count < size; idx++) /* Assumes settings are grouped by speed */ - if ((count == 0) || - (speeds[count - 1] != settings[idx].speed)) { - speeds[count] = settings[idx].speed; - count++; - } - } + if ((settings[idx].setting & phy->supported) && + (count == 0 || speeds[count - 1] != settings[idx].speed)) + speeds[count++] = settings[idx].speed; return count; } -- cgit From 6b2af241f0766a9d2c01dfa4e5aece433b51ff70 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 13 Apr 2017 18:13:51 +0200 Subject: MAINTAINERS: rename TC entry and add couple of header files The section is not specific only to "TC classifiers", but applies to the whole TC subsystem. Also, add couple of forgotten headers. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- MAINTAINERS | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 8bc85dc8a71a..197387863297 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12187,12 +12187,17 @@ F: Documentation/accounting/taskstats* F: include/linux/taskstats* F: kernel/taskstats.c -TC CLASSIFIER +TC subsystem M: Jamal Hadi Salim L: netdev@vger.kernel.org S: Maintained F: include/net/pkt_cls.h +F: include/net/pkt_sched.h +F: include/net/tc_act/ F: include/uapi/linux/pkt_cls.h +F: include/uapi/linux/pkt_sched.h +F: include/uapi/linux/tc_act/ +F: include/uapi/linux/tc_ematch/ F: net/sched/ TCP LOW PRIORITY MODULE -- cgit From f5001ceab8996d76880004381c188b3b96c8c7af Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 13 Apr 2017 11:38:02 -0700 Subject: kcm: remove a useless copy_from_user() struct kcm_clone only contains fd, and kcm_clone() only writes this struct, so there is no need to copy it from user. Cc: Tom Herbert Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/kcm/kcmsock.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index 31762f76cdb5..deca20fb2ce2 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -1707,11 +1707,7 @@ static int kcm_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) struct kcm_clone info; struct socket *newsock = NULL; - if (copy_from_user(&info, (void __user *)arg, sizeof(info))) - return -EFAULT; - err = kcm_clone(sock, &info, &newsock); - if (!err) { if (copy_to_user((void __user *)arg, &info, sizeof(info))) { -- cgit From cab93af0ed6a12b63ebf0ebca37e6d8efd399114 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Fri, 14 Apr 2017 13:49:34 +0300 Subject: net: bridge: notify on hw fdb takeover Recently we added support for SW fdbs to take over HW ones, but that results in changing a user-visible fdb flag thus we need to send a notification, also it's consistent with how HW takes over SW entries. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- net/bridge/br_fdb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 5a40a87c4f4f..de7988b0349e 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -595,8 +595,10 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, if (unlikely(added_by_user)) fdb->added_by_user = 1; /* Take over HW learned entry */ - if (unlikely(fdb->added_by_external_learn)) + if (unlikely(fdb->added_by_external_learn)) { fdb->added_by_external_learn = 0; + fdb_modified = true; + } if (unlikely(fdb_modified)) fdb_notify(br, fdb, RTM_NEWNEIGH); } -- cgit From 82960fff09bc394e2a33d5369969410699c04861 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 14 Apr 2017 19:07:32 +0800 Subject: net: mvneta: fix failed to suspend if WOL is enabled Recently, suspend/resume and WOL support are added into mvneta driver. If we enable WOL, then we get some error as below on Marvell BG4CT platforms during suspend: [ 184.149723] dpm_run_callback(): mdio_bus_suspend+0x0/0x50 returns -16 [ 184.149727] PM: Device f7b62004.mdio-mi:00 failed to suspend: error -16 -16 means -EBUSY, phy_suspend() will return -EBUSY if it finds the device has WOL enabled. We fix this issue by properly setting the netdev's power.can_wakeup and power.wakeup, i.e 1. in mvneta_mdio_probe(), call device_set_wakeup_capable() to set power.can_wakeup if the phy support WOL. 2. in mvneta_ethtool_set_wol(), call device_set_wakeup_enable() to set power.wakeup if WOL has been successfully enabled in phy. Signed-off-by: Jisheng Zhang Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 34a3686d2ce6..0992db47070f 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3318,6 +3318,7 @@ static void mvneta_adjust_link(struct net_device *ndev) static int mvneta_mdio_probe(struct mvneta_port *pp) { struct phy_device *phy_dev; + struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; phy_dev = of_phy_connect(pp->dev, pp->phy_node, mvneta_adjust_link, 0, pp->phy_interface); @@ -3326,6 +3327,9 @@ static int mvneta_mdio_probe(struct mvneta_port *pp) return -ENODEV; } + phy_ethtool_get_wol(phy_dev, &wol); + device_set_wakeup_capable(&pp->dev->dev, !!wol.supported); + phy_dev->supported &= PHY_GBIT_FEATURES; phy_dev->advertising = phy_dev->supported; @@ -3942,10 +3946,16 @@ static void mvneta_ethtool_get_wol(struct net_device *dev, static int mvneta_ethtool_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { + int ret; + if (!dev->phydev) return -EOPNOTSUPP; - return phy_ethtool_set_wol(dev->phydev, wol); + ret = phy_ethtool_set_wol(dev->phydev, wol); + if (!ret) + device_set_wakeup_enable(&dev->dev, !!wol->wolopts); + + return ret; } static const struct net_device_ops mvneta_netdev_ops = { -- cgit From 9746f85686849df107c899f34188f977e78154b0 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Fri, 14 Apr 2017 10:30:25 -0700 Subject: bpf: lru: Add test_lru_sanity6 for BPF_F_NO_COMMON_LRU test_lru_sanity3 is not applicable to BPF_F_NO_COMMON_LRU. It just happens to work when PERCPU_FREE_TARGET == 16. This patch: 1) Disable test_lru_sanity3 for BPF_F_NO_COMMON_LRU 2) Add test_lru_sanity6 to test list rotation for the BPF_F_NO_COMMON_LRU map. Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/test_lru_map.c | 70 +++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/tools/testing/selftests/bpf/test_lru_map.c b/tools/testing/selftests/bpf/test_lru_map.c index 00b0aff56e2e..04b2242937cc 100644 --- a/tools/testing/selftests/bpf/test_lru_map.c +++ b/tools/testing/selftests/bpf/test_lru_map.c @@ -387,6 +387,10 @@ static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free) unsigned int map_size; int next_cpu = 0; + if (map_flags & BPF_F_NO_COMMON_LRU) + /* This test is only applicable to common LRU list */ + return; + printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type, map_flags); @@ -396,11 +400,7 @@ static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free) assert(batch_size * 2 == tgt_free); map_size = tgt_free * 2; - if (map_flags & BPF_F_NO_COMMON_LRU) - lru_map_fd = create_map(map_type, map_flags, - map_size * nr_cpus); - else - lru_map_fd = create_map(map_type, map_flags, map_size); + lru_map_fd = create_map(map_type, map_flags, map_size); assert(lru_map_fd != -1); expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size); @@ -566,6 +566,65 @@ static void test_lru_sanity5(int map_type, int map_flags) printf("Pass\n"); } +/* Test list rotation for BPF_F_NO_COMMON_LRU map */ +static void test_lru_sanity6(int map_type, int map_flags, int tgt_free) +{ + int lru_map_fd, expected_map_fd; + unsigned long long key, value[nr_cpus]; + unsigned int map_size = tgt_free * 2; + int next_cpu = 0; + + if (!(map_flags & BPF_F_NO_COMMON_LRU)) + return; + + printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type, + map_flags); + + assert(sched_next_online(0, &next_cpu) != -1); + + expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size); + assert(expected_map_fd != -1); + + lru_map_fd = create_map(map_type, map_flags, map_size * nr_cpus); + assert(lru_map_fd != -1); + + value[0] = 1234; + + for (key = 1; key <= tgt_free; key++) { + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); + assert(!bpf_map_update_elem(expected_map_fd, &key, value, + BPF_NOEXIST)); + } + + for (; key <= tgt_free * 2; key++) { + unsigned long long stable_key; + + /* Make ref bit sticky for key: [1, tgt_free] */ + for (stable_key = 1; stable_key <= tgt_free; stable_key++) { + /* Mark the ref bit */ + assert(!bpf_map_lookup_elem(lru_map_fd, &stable_key, + value)); + } + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); + } + + for (; key <= tgt_free * 3; key++) { + assert(!bpf_map_update_elem(lru_map_fd, &key, value, + BPF_NOEXIST)); + assert(!bpf_map_update_elem(expected_map_fd, &key, value, + BPF_NOEXIST)); + } + + assert(map_equal(lru_map_fd, expected_map_fd)); + + close(expected_map_fd); + close(lru_map_fd); + + printf("Pass\n"); +} + int main(int argc, char **argv) { struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; @@ -593,6 +652,7 @@ int main(int argc, char **argv) test_lru_sanity3(map_types[t], map_flags[f], tgt_free); test_lru_sanity4(map_types[t], map_flags[f], tgt_free); test_lru_sanity5(map_types[t], map_flags[f]); + test_lru_sanity6(map_types[t], map_flags[f], tgt_free); printf("\n"); } -- cgit From 6467acbc70f011d5852f60f6e04825a268c8e8b0 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Fri, 14 Apr 2017 10:30:26 -0700 Subject: bpf: lru: Cleanup test_lru_map.c This patch does the following cleanup on test_lru_map.c 1) Fix indentation (Replace spaces by tabs) 2) Remove redundant BPF_F_NO_COMMON_LRU test 3) Simplify some comments Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/test_lru_map.c | 32 +++++++++--------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/bpf/test_lru_map.c b/tools/testing/selftests/bpf/test_lru_map.c index 04b2242937cc..87c05e5bdfdd 100644 --- a/tools/testing/selftests/bpf/test_lru_map.c +++ b/tools/testing/selftests/bpf/test_lru_map.c @@ -191,12 +191,7 @@ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) int next_cpu = 0; if (map_flags & BPF_F_NO_COMMON_LRU) - /* Ther percpu lru list (i.e each cpu has its own LRU - * list) does not have a local free list. Hence, - * it will only free old nodes till there is no free - * from the LRU list. Hence, this test does not apply - * to BPF_F_NO_COMMON_LRU - */ + /* This test is only applicable to common LRU list */ return; printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type, @@ -227,7 +222,7 @@ static void test_lru_sanity1(int map_type, int map_flags, unsigned int tgt_free) for (key = 1; key < end_key; key++) { assert(!bpf_map_lookup_elem(lru_map_fd, &key, value)); assert(!bpf_map_update_elem(expected_map_fd, &key, value, - BPF_NOEXIST)); + BPF_NOEXIST)); } /* Insert 1+tgt_free to 2*tgt_free @@ -273,12 +268,7 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) int next_cpu = 0; if (map_flags & BPF_F_NO_COMMON_LRU) - /* Ther percpu lru list (i.e each cpu has its own LRU - * list) does not have a local free list. Hence, - * it will only free old nodes till there is no free - * from the LRU list. Hence, this test does not apply - * to BPF_F_NO_COMMON_LRU - */ + /* This test is only applicable to common LRU list */ return; printf("%s (map_type:%d map_flags:0x%X): ", __func__, map_type, @@ -290,11 +280,7 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) assert(batch_size * 2 == tgt_free); map_size = tgt_free + batch_size; - if (map_flags & BPF_F_NO_COMMON_LRU) - lru_map_fd = create_map(map_type, map_flags, - map_size * nr_cpus); - else - lru_map_fd = create_map(map_type, map_flags, map_size); + lru_map_fd = create_map(map_type, map_flags, map_size); assert(lru_map_fd != -1); expected_map_fd = create_map(BPF_MAP_TYPE_HASH, 0, map_size); @@ -341,7 +327,7 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) assert(!bpf_map_lookup_elem(lru_map_fd, &key, value)); assert(value[0] == 4321); assert(!bpf_map_update_elem(expected_map_fd, &key, value, - BPF_NOEXIST)); + BPF_NOEXIST)); } value[0] = 1234; @@ -361,7 +347,7 @@ static void test_lru_sanity2(int map_type, int map_flags, unsigned int tgt_free) assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); assert(!bpf_map_update_elem(expected_map_fd, &key, value, - BPF_NOEXIST)); + BPF_NOEXIST)); } assert(map_equal(lru_map_fd, expected_map_fd)); @@ -419,7 +405,7 @@ static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free) for (key = 1; key < end_key; key++) { assert(!bpf_map_lookup_elem(lru_map_fd, &key, value)); assert(!bpf_map_update_elem(expected_map_fd, &key, value, - BPF_NOEXIST)); + BPF_NOEXIST)); } /* Add 1+2*tgt_free to tgt_free*5/2 @@ -431,7 +417,7 @@ static void test_lru_sanity3(int map_type, int map_flags, unsigned int tgt_free) assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); assert(!bpf_map_update_elem(expected_map_fd, &key, value, - BPF_NOEXIST)); + BPF_NOEXIST)); } assert(map_equal(lru_map_fd, expected_map_fd)); @@ -491,7 +477,7 @@ static void test_lru_sanity4(int map_type, int map_flags, unsigned int tgt_free) assert(!bpf_map_update_elem(lru_map_fd, &key, value, BPF_NOEXIST)); assert(!bpf_map_update_elem(expected_map_fd, &key, value, - BPF_NOEXIST)); + BPF_NOEXIST)); } assert(map_equal(lru_map_fd, expected_map_fd)); -- cgit From bf8db5d243a103ccd3f6d82a110e2302608e248c Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Fri, 14 Apr 2017 10:30:27 -0700 Subject: bpf: lru: Refactor LRU map tests in map_perf_test One more LRU test will be added later in this patch series. In this patch, we first move all existing LRU map tests into a single syscall (connect) first so that the future new LRU test can be added without hunting another syscall. One of the map name is also changed from percpu_lru_hash_map to nocommon_lru_hash_map to avoid the confusion with percpu_hash_map. Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- samples/bpf/map_perf_test_kern.c | 43 +++++++++++++++++++++++--------- samples/bpf/map_perf_test_user.c | 53 +++++++++++++++++++++++++++------------- 2 files changed, 67 insertions(+), 29 deletions(-) diff --git a/samples/bpf/map_perf_test_kern.c b/samples/bpf/map_perf_test_kern.c index 9da2a3441b0a..404ed53b8a53 100644 --- a/samples/bpf/map_perf_test_kern.c +++ b/samples/bpf/map_perf_test_kern.c @@ -26,7 +26,7 @@ struct bpf_map_def SEC("maps") lru_hash_map = { .max_entries = 10000, }; -struct bpf_map_def SEC("maps") percpu_lru_hash_map = { +struct bpf_map_def SEC("maps") nocommon_lru_hash_map = { .type = BPF_MAP_TYPE_LRU_HASH, .key_size = sizeof(u32), .value_size = sizeof(long), @@ -100,6 +100,7 @@ int stress_percpu_hmap(struct pt_regs *ctx) bpf_map_delete_elem(&percpu_hash_map, &key); return 0; } + SEC("kprobe/sys_getgid") int stress_hmap_alloc(struct pt_regs *ctx) { @@ -128,24 +129,42 @@ int stress_percpu_hmap_alloc(struct pt_regs *ctx) return 0; } -SEC("kprobe/sys_getpid") +SEC("kprobe/sys_connect") int stress_lru_hmap_alloc(struct pt_regs *ctx) { - u32 key = bpf_get_prandom_u32(); + struct sockaddr_in6 *in6; + u16 test_case, dst6[8]; + int addrlen, ret; + char fmt[] = "Failed at stress_lru_hmap_alloc. ret:%d\n"; long val = 1; + u32 key = bpf_get_prandom_u32(); - bpf_map_update_elem(&lru_hash_map, &key, &val, BPF_ANY); + in6 = (struct sockaddr_in6 *)PT_REGS_PARM2(ctx); + addrlen = (int)PT_REGS_PARM3(ctx); - return 0; -} + if (addrlen != sizeof(*in6)) + return 0; -SEC("kprobe/sys_getppid") -int stress_percpu_lru_hmap_alloc(struct pt_regs *ctx) -{ - u32 key = bpf_get_prandom_u32(); - long val = 1; + ret = bpf_probe_read(dst6, sizeof(dst6), &in6->sin6_addr); + if (ret) + goto done; + + if (dst6[0] != 0xdead || dst6[1] != 0xbeef) + return 0; + + test_case = dst6[7]; + + if (test_case == 0) + ret = bpf_map_update_elem(&lru_hash_map, &key, &val, BPF_ANY); + else if (test_case == 1) + ret = bpf_map_update_elem(&nocommon_lru_hash_map, &key, &val, + BPF_ANY); + else + ret = -EINVAL; - bpf_map_update_elem(&percpu_lru_hash_map, &key, &val, BPF_ANY); +done: + if (ret) + bpf_trace_printk(fmt, sizeof(fmt), ret); return 0; } diff --git a/samples/bpf/map_perf_test_user.c b/samples/bpf/map_perf_test_user.c index e29ff318a793..51cb8f238aa2 100644 --- a/samples/bpf/map_perf_test_user.c +++ b/samples/bpf/map_perf_test_user.c @@ -18,6 +18,9 @@ #include #include #include +#include +#include + #include "libbpf.h" #include "bpf_load.h" @@ -36,7 +39,7 @@ static __u64 time_get_ns(void) #define HASH_KMALLOC (1 << 2) #define PERCPU_HASH_KMALLOC (1 << 3) #define LRU_HASH_PREALLOC (1 << 4) -#define PERCPU_LRU_HASH_PREALLOC (1 << 5) +#define NOCOMMON_LRU_HASH_PREALLOC (1 << 5) #define LPM_KMALLOC (1 << 6) #define HASH_LOOKUP (1 << 7) #define ARRAY_LOOKUP (1 << 8) @@ -55,28 +58,44 @@ static void test_hash_prealloc(int cpu) cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); } -static void test_lru_hash_prealloc(int cpu) +static void do_test_lru(int lru_test_flag, int cpu) { + struct sockaddr_in6 in6 = { .sin6_family = AF_INET6 }; + const char *test_name; __u64 start_time; - int i; + int i, ret; + + in6.sin6_addr.s6_addr16[0] = 0xdead; + in6.sin6_addr.s6_addr16[1] = 0xbeef; + + if (lru_test_flag & LRU_HASH_PREALLOC) { + test_name = "lru_hash_map_perf"; + in6.sin6_addr.s6_addr16[7] = 0; + } else if (lru_test_flag & NOCOMMON_LRU_HASH_PREALLOC) { + test_name = "nocommon_lru_hash_map_perf"; + in6.sin6_addr.s6_addr16[7] = 1; + } else { + assert(0); + } start_time = time_get_ns(); - for (i = 0; i < MAX_CNT; i++) - syscall(__NR_getpid); - printf("%d:lru_hash_map_perf pre-alloc %lld events per sec\n", - cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); + for (i = 0; i < MAX_CNT; i++) { + ret = connect(-1, (const struct sockaddr *)&in6, sizeof(in6)); + assert(ret == -1 && errno == EBADF); + } + printf("%d:%s pre-alloc %lld events per sec\n", + cpu, test_name, + MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); } -static void test_percpu_lru_hash_prealloc(int cpu) +static void test_lru_hash_prealloc(int cpu) { - __u64 start_time; - int i; + do_test_lru(LRU_HASH_PREALLOC, cpu); +} - start_time = time_get_ns(); - for (i = 0; i < MAX_CNT; i++) - syscall(__NR_getppid); - printf("%d:lru_hash_map_perf pre-alloc %lld events per sec\n", - cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); +static void test_nocommon_lru_hash_prealloc(int cpu) +{ + do_test_lru(NOCOMMON_LRU_HASH_PREALLOC, cpu); } static void test_percpu_hash_prealloc(int cpu) @@ -174,8 +193,8 @@ static void loop(int cpu) if (test_flags & LRU_HASH_PREALLOC) test_lru_hash_prealloc(cpu); - if (test_flags & PERCPU_LRU_HASH_PREALLOC) - test_percpu_lru_hash_prealloc(cpu); + if (test_flags & NOCOMMON_LRU_HASH_PREALLOC) + test_nocommon_lru_hash_prealloc(cpu); if (test_flags & LPM_KMALLOC) test_lpm_kmalloc(cpu); -- cgit From 9fd63d05f3e8476282cd8c484eb34d3f6be54f40 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Fri, 14 Apr 2017 10:30:28 -0700 Subject: bpf: Allow bpf sample programs (*_user.c) to change bpf_map_def The current bpf_map_def is statically defined during compile time. This patch allows the *_user.c program to change it during runtime. It is done by adding load_bpf_file_fixup_map() which takes a callback. The callback will be called before creating each map so that it has a chance to modify the bpf_map_def. The current usecase is to change max_entries in map_perf_test. It is interesting to test with a much bigger map size in some cases (e.g. the following patch on bpf_lru_map.c). However, it is hard to find one size to fit all testing environment. Hence, it is handy to take the max_entries as a cmdline arg and then configure the bpf_map_def during runtime. This patch adds two cmdline args. One is to configure the map's max_entries. Another is to configure the max_cnt which controls how many times a syscall is called. Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- samples/bpf/bpf_load.c | 114 +++++++++++++++++++++++++----- samples/bpf/bpf_load.h | 13 ++++ samples/bpf/map_perf_test_user.c | 148 ++++++++++++++++++++++++--------------- 3 files changed, 201 insertions(+), 74 deletions(-) diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c index dcdce1270d38..0d449d8032d1 100644 --- a/samples/bpf/bpf_load.c +++ b/samples/bpf/bpf_load.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "libbpf.h" #include "bpf_load.h" #include "perf-sys.h" @@ -37,15 +38,6 @@ int event_fd[MAX_PROGS]; int prog_cnt; int prog_array_fd = -1; -struct bpf_map_def { - unsigned int type; - unsigned int key_size; - unsigned int value_size; - unsigned int max_entries; - unsigned int map_flags; - unsigned int inner_map_idx; -}; - static int populate_prog_array(const char *event, int prog_fd) { int ind = atoi(event), err; @@ -193,11 +185,14 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) return 0; } -static int load_maps(struct bpf_map_def *maps, int len) +static int load_maps(struct bpf_map_def *maps, int len, + const char **map_names, fixup_map_cb fixup_map) { int i; for (i = 0; i < len / sizeof(struct bpf_map_def); i++) { + if (fixup_map) + fixup_map(&maps[i], map_names[i], i); if (maps[i].type == BPF_MAP_TYPE_ARRAY_OF_MAPS || maps[i].type == BPF_MAP_TYPE_HASH_OF_MAPS) { @@ -280,14 +275,64 @@ static int parse_relo_and_apply(Elf_Data *data, Elf_Data *symbols, return 0; } -int load_bpf_file(char *path) +static int cmp_symbols(const void *l, const void *r) +{ + const GElf_Sym *lsym = (const GElf_Sym *)l; + const GElf_Sym *rsym = (const GElf_Sym *)r; + + if (lsym->st_value < rsym->st_value) + return -1; + else if (lsym->st_value > rsym->st_value) + return 1; + else + return 0; +} + +static int get_sorted_map_names(Elf *elf, Elf_Data *symbols, int maps_shndx, + int strtabidx, char **map_names) +{ + GElf_Sym map_symbols[MAX_MAPS]; + int i, nr_maps = 0; + + for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) { + assert(nr_maps < MAX_MAPS); + if (!gelf_getsym(symbols, i, &map_symbols[nr_maps])) + continue; + if (map_symbols[nr_maps].st_shndx != maps_shndx) + continue; + nr_maps++; + } + + qsort(map_symbols, nr_maps, sizeof(GElf_Sym), cmp_symbols); + + for (i = 0; i < nr_maps; i++) { + char *map_name; + + map_name = elf_strptr(elf, strtabidx, map_symbols[i].st_name); + if (!map_name) { + printf("cannot get map symbol\n"); + return 1; + } + + map_names[i] = strdup(map_name); + if (!map_names[i]) { + printf("strdup(%s): %s(%d)\n", map_name, + strerror(errno), errno); + return 1; + } + } + + return 0; +} + +static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map) { - int fd, i; + int fd, i, ret, maps_shndx = -1, strtabidx = -1; Elf *elf; GElf_Ehdr ehdr; GElf_Shdr shdr, shdr_prog; - Elf_Data *data, *data_prog, *symbols = NULL; - char *shname, *shname_prog; + Elf_Data *data, *data_prog, *data_maps = NULL, *symbols = NULL; + char *shname, *shname_prog, *map_names[MAX_MAPS] = { NULL }; /* reset global variables */ kern_version = 0; @@ -335,14 +380,33 @@ int load_bpf_file(char *path) } memcpy(&kern_version, data->d_buf, sizeof(int)); } else if (strcmp(shname, "maps") == 0) { - processed_sec[i] = true; - if (load_maps(data->d_buf, data->d_size)) - return 1; + maps_shndx = i; + data_maps = data; } else if (shdr.sh_type == SHT_SYMTAB) { + strtabidx = shdr.sh_link; symbols = data; } } + ret = 1; + + if (!symbols) { + printf("missing SHT_SYMTAB section\n"); + goto done; + } + + if (data_maps) { + if (get_sorted_map_names(elf, symbols, maps_shndx, strtabidx, + map_names)) + goto done; + + if (load_maps(data_maps->d_buf, data_maps->d_size, + (const char **)map_names, fixup_map)) + goto done; + + processed_sec[maps_shndx] = true; + } + /* load programs that need map fixup (relocations) */ for (i = 1; i < ehdr.e_shnum; i++) { if (processed_sec[i]) @@ -399,8 +463,22 @@ int load_bpf_file(char *path) load_and_attach(shname, data->d_buf, data->d_size); } + ret = 0; +done: + for (i = 0; i < MAX_MAPS; i++) + free(map_names[i]); close(fd); - return 0; + return ret; +} + +int load_bpf_file(char *path) +{ + return do_load_bpf_file(path, NULL); +} + +int load_bpf_file_fixup_map(const char *path, fixup_map_cb fixup_map) +{ + return do_load_bpf_file(path, fixup_map); } void read_trace_pipe(void) diff --git a/samples/bpf/bpf_load.h b/samples/bpf/bpf_load.h index c827827299b3..68f6b2d22507 100644 --- a/samples/bpf/bpf_load.h +++ b/samples/bpf/bpf_load.h @@ -6,6 +6,18 @@ #define MAX_MAPS 32 #define MAX_PROGS 32 +struct bpf_map_def { + unsigned int type; + unsigned int key_size; + unsigned int value_size; + unsigned int max_entries; + unsigned int map_flags; + unsigned int inner_map_idx; +}; + +typedef void (*fixup_map_cb)(struct bpf_map_def *map, const char *map_name, + int idx); + extern int map_fd[MAX_MAPS]; extern int prog_fd[MAX_PROGS]; extern int event_fd[MAX_PROGS]; @@ -25,6 +37,7 @@ extern int prog_cnt; * returns zero on success */ int load_bpf_file(char *path); +int load_bpf_file_fixup_map(const char *path, fixup_map_cb fixup_map); void read_trace_pipe(void); struct ksym { diff --git a/samples/bpf/map_perf_test_user.c b/samples/bpf/map_perf_test_user.c index 51cb8f238aa2..2a12f48b5c6d 100644 --- a/samples/bpf/map_perf_test_user.c +++ b/samples/bpf/map_perf_test_user.c @@ -24,7 +24,7 @@ #include "libbpf.h" #include "bpf_load.h" -#define MAX_CNT 1000000 +#define TEST_BIT(t) (1U << (t)) static __u64 time_get_ns(void) { @@ -34,17 +34,39 @@ static __u64 time_get_ns(void) return ts.tv_sec * 1000000000ull + ts.tv_nsec; } -#define HASH_PREALLOC (1 << 0) -#define PERCPU_HASH_PREALLOC (1 << 1) -#define HASH_KMALLOC (1 << 2) -#define PERCPU_HASH_KMALLOC (1 << 3) -#define LRU_HASH_PREALLOC (1 << 4) -#define NOCOMMON_LRU_HASH_PREALLOC (1 << 5) -#define LPM_KMALLOC (1 << 6) -#define HASH_LOOKUP (1 << 7) -#define ARRAY_LOOKUP (1 << 8) +enum test_type { + HASH_PREALLOC, + PERCPU_HASH_PREALLOC, + HASH_KMALLOC, + PERCPU_HASH_KMALLOC, + LRU_HASH_PREALLOC, + NOCOMMON_LRU_HASH_PREALLOC, + LPM_KMALLOC, + HASH_LOOKUP, + ARRAY_LOOKUP, + NR_TESTS, +}; + +const char *test_map_names[NR_TESTS] = { + [HASH_PREALLOC] = "hash_map", + [PERCPU_HASH_PREALLOC] = "percpu_hash_map", + [HASH_KMALLOC] = "hash_map_alloc", + [PERCPU_HASH_KMALLOC] = "percpu_hash_map_alloc", + [LRU_HASH_PREALLOC] = "lru_hash_map", + [NOCOMMON_LRU_HASH_PREALLOC] = "nocommon_lru_hash_map", + [LPM_KMALLOC] = "lpm_trie_map_alloc", + [HASH_LOOKUP] = "hash_map", + [ARRAY_LOOKUP] = "array_map", +}; static int test_flags = ~0; +static uint32_t num_map_entries; +static uint32_t max_cnt = 1000000; + +static int check_test_flags(enum test_type t) +{ + return test_flags & TEST_BIT(t); +} static void test_hash_prealloc(int cpu) { @@ -52,13 +74,13 @@ static void test_hash_prealloc(int cpu) int i; start_time = time_get_ns(); - for (i = 0; i < MAX_CNT; i++) + for (i = 0; i < max_cnt; i++) syscall(__NR_getuid); printf("%d:hash_map_perf pre-alloc %lld events per sec\n", - cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); + cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time)); } -static void do_test_lru(int lru_test_flag, int cpu) +static void do_test_lru(enum test_type test, int cpu) { struct sockaddr_in6 in6 = { .sin6_family = AF_INET6 }; const char *test_name; @@ -68,10 +90,10 @@ static void do_test_lru(int lru_test_flag, int cpu) in6.sin6_addr.s6_addr16[0] = 0xdead; in6.sin6_addr.s6_addr16[1] = 0xbeef; - if (lru_test_flag & LRU_HASH_PREALLOC) { + if (test == LRU_HASH_PREALLOC) { test_name = "lru_hash_map_perf"; in6.sin6_addr.s6_addr16[7] = 0; - } else if (lru_test_flag & NOCOMMON_LRU_HASH_PREALLOC) { + } else if (test == NOCOMMON_LRU_HASH_PREALLOC) { test_name = "nocommon_lru_hash_map_perf"; in6.sin6_addr.s6_addr16[7] = 1; } else { @@ -79,13 +101,13 @@ static void do_test_lru(int lru_test_flag, int cpu) } start_time = time_get_ns(); - for (i = 0; i < MAX_CNT; i++) { + for (i = 0; i < max_cnt; i++) { ret = connect(-1, (const struct sockaddr *)&in6, sizeof(in6)); assert(ret == -1 && errno == EBADF); } printf("%d:%s pre-alloc %lld events per sec\n", cpu, test_name, - MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); + max_cnt * 1000000000ll / (time_get_ns() - start_time)); } static void test_lru_hash_prealloc(int cpu) @@ -104,10 +126,10 @@ static void test_percpu_hash_prealloc(int cpu) int i; start_time = time_get_ns(); - for (i = 0; i < MAX_CNT; i++) + for (i = 0; i < max_cnt; i++) syscall(__NR_geteuid); printf("%d:percpu_hash_map_perf pre-alloc %lld events per sec\n", - cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); + cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time)); } static void test_hash_kmalloc(int cpu) @@ -116,10 +138,10 @@ static void test_hash_kmalloc(int cpu) int i; start_time = time_get_ns(); - for (i = 0; i < MAX_CNT; i++) + for (i = 0; i < max_cnt; i++) syscall(__NR_getgid); printf("%d:hash_map_perf kmalloc %lld events per sec\n", - cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); + cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time)); } static void test_percpu_hash_kmalloc(int cpu) @@ -128,10 +150,10 @@ static void test_percpu_hash_kmalloc(int cpu) int i; start_time = time_get_ns(); - for (i = 0; i < MAX_CNT; i++) + for (i = 0; i < max_cnt; i++) syscall(__NR_getegid); printf("%d:percpu_hash_map_perf kmalloc %lld events per sec\n", - cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); + cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time)); } static void test_lpm_kmalloc(int cpu) @@ -140,10 +162,10 @@ static void test_lpm_kmalloc(int cpu) int i; start_time = time_get_ns(); - for (i = 0; i < MAX_CNT; i++) + for (i = 0; i < max_cnt; i++) syscall(__NR_gettid); printf("%d:lpm_perf kmalloc %lld events per sec\n", - cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); + cpu, max_cnt * 1000000000ll / (time_get_ns() - start_time)); } static void test_hash_lookup(int cpu) @@ -152,10 +174,10 @@ static void test_hash_lookup(int cpu) int i; start_time = time_get_ns(); - for (i = 0; i < MAX_CNT; i++) + for (i = 0; i < max_cnt; i++) syscall(__NR_getpgid, 0); printf("%d:hash_lookup %lld lookups per sec\n", - cpu, MAX_CNT * 1000000000ll * 64 / (time_get_ns() - start_time)); + cpu, max_cnt * 1000000000ll * 64 / (time_get_ns() - start_time)); } static void test_array_lookup(int cpu) @@ -164,46 +186,38 @@ static void test_array_lookup(int cpu) int i; start_time = time_get_ns(); - for (i = 0; i < MAX_CNT; i++) + for (i = 0; i < max_cnt; i++) syscall(__NR_getpgrp, 0); printf("%d:array_lookup %lld lookups per sec\n", - cpu, MAX_CNT * 1000000000ll * 64 / (time_get_ns() - start_time)); + cpu, max_cnt * 1000000000ll * 64 / (time_get_ns() - start_time)); } +typedef void (*test_func)(int cpu); +const test_func test_funcs[] = { + [HASH_PREALLOC] = test_hash_prealloc, + [PERCPU_HASH_PREALLOC] = test_percpu_hash_prealloc, + [HASH_KMALLOC] = test_hash_kmalloc, + [PERCPU_HASH_KMALLOC] = test_percpu_hash_kmalloc, + [LRU_HASH_PREALLOC] = test_lru_hash_prealloc, + [NOCOMMON_LRU_HASH_PREALLOC] = test_nocommon_lru_hash_prealloc, + [LPM_KMALLOC] = test_lpm_kmalloc, + [HASH_LOOKUP] = test_hash_lookup, + [ARRAY_LOOKUP] = test_array_lookup, +}; + static void loop(int cpu) { cpu_set_t cpuset; + int i; CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); sched_setaffinity(0, sizeof(cpuset), &cpuset); - if (test_flags & HASH_PREALLOC) - test_hash_prealloc(cpu); - - if (test_flags & PERCPU_HASH_PREALLOC) - test_percpu_hash_prealloc(cpu); - - if (test_flags & HASH_KMALLOC) - test_hash_kmalloc(cpu); - - if (test_flags & PERCPU_HASH_KMALLOC) - test_percpu_hash_kmalloc(cpu); - - if (test_flags & LRU_HASH_PREALLOC) - test_lru_hash_prealloc(cpu); - - if (test_flags & NOCOMMON_LRU_HASH_PREALLOC) - test_nocommon_lru_hash_prealloc(cpu); - - if (test_flags & LPM_KMALLOC) - test_lpm_kmalloc(cpu); - - if (test_flags & HASH_LOOKUP) - test_hash_lookup(cpu); - - if (test_flags & ARRAY_LOOKUP) - test_array_lookup(cpu); + for (i = 0; i < NR_TESTS; i++) { + if (check_test_flags(i)) + test_funcs[i](cpu); + } } static void run_perf_test(int tasks) @@ -260,6 +274,22 @@ static void fill_lpm_trie(void) assert(!r); } +static void fixup_map(struct bpf_map_def *map, const char *name, int idx) +{ + int i; + + if (num_map_entries <= 0) + return; + + /* Only change the max_entries for the enabled test(s) */ + for (i = 0; i < NR_TESTS; i++) { + if (!strcmp(test_map_names[i], name) && + (check_test_flags(i))) { + map->max_entries = num_map_entries; + } + } +} + int main(int argc, char **argv) { struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; @@ -275,7 +305,13 @@ int main(int argc, char **argv) if (argc > 2) num_cpu = atoi(argv[2]) ? : num_cpu; - if (load_bpf_file(filename)) { + if (argc > 3) + num_map_entries = atoi(argv[3]); + + if (argc > 4) + max_cnt = atoi(argv[4]); + + if (load_bpf_file_fixup_map(filename, fixup_map)) { printf("%s", bpf_log_buf); return 1; } -- cgit From 695ba2651a2ecbb336145f9cc033c85b9c6a5cee Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Fri, 14 Apr 2017 10:30:29 -0700 Subject: bpf: lru: Lower the PERCPU_NR_SCANS from 16 to 4 After doing map_perf_test with a much bigger BPF_F_NO_COMMON_LRU map, the perf report shows a lot of time spent in rotating the inactive list (i.e. __bpf_lru_list_rotate_inactive): > map_perf_test 32 8 10000 1000000 | awk '{sum += $3}END{print sum}' 19644783 (19M/s) > map_perf_test 32 8 10000000 10000000 | awk '{sum += $3}END{print sum}' 6283930 (6.28M/s) By inactive, it usually means the element is not in cache. Hence, there is a need to tune the PERCPU_NR_SCANS value. This patch finds a better number of elements to scan during each list rotation. The PERCPU_NR_SCANS (which is defined the same as PERCPU_FREE_TARGET) decreases from 16 elements to 4 elements. This change only affects the BPF_F_NO_COMMON_LRU map. The test_lru_dist does not show meaningful difference between 16 and 4. Our production L4 load balancer which uses the LRU map for conntrack-ing also shows little change in cache hit rate. Since both benchmark and production data show no cache-hit difference, PERCPU_NR_SCANS is lowered from 16 to 4. We can consider making it configurable if we find a usecase later that shows another value works better and/or use a different rotation strategy. After this change: > map_perf_test 32 8 10000000 10000000 | awk '{sum += $3}END{print sum}' 9240324 (9.2M/s) i.e. 6.28M/s -> 9.2M/s The test_lru_dist has not shown meaningful difference: > test_lru_dist zipf.100k.a1_01.out 4000 1: nr_misses: 31575 (Before) vs 31566 (After) > test_lru_dist zipf.100k.a0_01.out 40000 1 nr_misses: 67036 (Before) vs 67031 (After) Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- kernel/bpf/bpf_lru_list.c | 2 +- tools/testing/selftests/bpf/test_lru_map.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/bpf_lru_list.c b/kernel/bpf/bpf_lru_list.c index f62d1d56f41d..e6ef4401a138 100644 --- a/kernel/bpf/bpf_lru_list.c +++ b/kernel/bpf/bpf_lru_list.c @@ -13,7 +13,7 @@ #define LOCAL_FREE_TARGET (128) #define LOCAL_NR_SCANS LOCAL_FREE_TARGET -#define PERCPU_FREE_TARGET (16) +#define PERCPU_FREE_TARGET (4) #define PERCPU_NR_SCANS PERCPU_FREE_TARGET /* Helpers to get the local list index */ diff --git a/tools/testing/selftests/bpf/test_lru_map.c b/tools/testing/selftests/bpf/test_lru_map.c index 87c05e5bdfdd..8c10c9180c1a 100644 --- a/tools/testing/selftests/bpf/test_lru_map.c +++ b/tools/testing/selftests/bpf/test_lru_map.c @@ -22,7 +22,7 @@ #include "bpf_util.h" #define LOCAL_FREE_TARGET (128) -#define PERCPU_FREE_TARGET (16) +#define PERCPU_FREE_TARGET (4) static int nr_cpus; -- cgit From 3a5795b83d578cc542a92c94399946258cf1a2af Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Fri, 14 Apr 2017 10:30:30 -0700 Subject: bpf: lru: Add map-in-map LRU example This patch adds a map-in-map LRU example. If we know only a subset of cores will use the LRU, we can allocate a common LRU list per targeting core and store it into an array-of-hashs. It allows using the common LRU map with map-update performance comparable to the BPF_F_NO_COMMON_LRU map but without wasting memory on the unused cores that we know they will never access the LRU map. BPF_F_NO_COMMON_LRU: > map_perf_test 32 8 10000000 10000000 | awk '{sum += $3}END{print sum}' 9234314 (9.23M/s) map-in-map LRU: > map_perf_test 512 8 1260000 80000000 | awk '{sum += $3}END{print sum}' 9962743 (9.96M/s) Notes that the max_entries for the map-in-map LRU test is 1260000 which is the max_entries for each inner LRU map. 8 processes have been started, so 8 * 1260000 = 10080000 (~10M) which is close to what is used in the BPF_F_NO_COMMON_LRU test. Signed-off-by: Martin KaFai Lau Signed-off-by: David S. Miller --- samples/bpf/map_perf_test_kern.c | 34 ++++++++++++++++++++-- samples/bpf/map_perf_test_user.c | 62 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 3 deletions(-) diff --git a/samples/bpf/map_perf_test_kern.c b/samples/bpf/map_perf_test_kern.c index 404ed53b8a53..245165817fbe 100644 --- a/samples/bpf/map_perf_test_kern.c +++ b/samples/bpf/map_perf_test_kern.c @@ -11,6 +11,7 @@ #include "bpf_helpers.h" #define MAX_ENTRIES 1000 +#define MAX_NR_CPUS 1024 struct bpf_map_def SEC("maps") hash_map = { .type = BPF_MAP_TYPE_HASH, @@ -34,6 +35,19 @@ struct bpf_map_def SEC("maps") nocommon_lru_hash_map = { .map_flags = BPF_F_NO_COMMON_LRU, }; +struct bpf_map_def SEC("maps") inner_lru_hash_map = { + .type = BPF_MAP_TYPE_LRU_HASH, + .key_size = sizeof(u32), + .value_size = sizeof(long), + .max_entries = MAX_ENTRIES, +}; + +struct bpf_map_def SEC("maps") array_of_lru_hashs = { + .type = BPF_MAP_TYPE_ARRAY_OF_MAPS, + .key_size = sizeof(u32), + .max_entries = MAX_NR_CPUS, +}; + struct bpf_map_def SEC("maps") percpu_hash_map = { .type = BPF_MAP_TYPE_PERCPU_HASH, .key_size = sizeof(u32), @@ -154,13 +168,27 @@ int stress_lru_hmap_alloc(struct pt_regs *ctx) test_case = dst6[7]; - if (test_case == 0) + if (test_case == 0) { ret = bpf_map_update_elem(&lru_hash_map, &key, &val, BPF_ANY); - else if (test_case == 1) + } else if (test_case == 1) { ret = bpf_map_update_elem(&nocommon_lru_hash_map, &key, &val, BPF_ANY); - else + } else if (test_case == 2) { + void *nolocal_lru_map; + int cpu = bpf_get_smp_processor_id(); + + nolocal_lru_map = bpf_map_lookup_elem(&array_of_lru_hashs, + &cpu); + if (!nolocal_lru_map) { + ret = -ENOENT; + goto done; + } + + ret = bpf_map_update_elem(nolocal_lru_map, &key, &val, + BPF_ANY); + } else { ret = -EINVAL; + } done: if (ret) diff --git a/samples/bpf/map_perf_test_user.c b/samples/bpf/map_perf_test_user.c index 2a12f48b5c6d..6ac778153315 100644 --- a/samples/bpf/map_perf_test_user.c +++ b/samples/bpf/map_perf_test_user.c @@ -25,6 +25,7 @@ #include "bpf_load.h" #define TEST_BIT(t) (1U << (t)) +#define MAX_NR_CPUS 1024 static __u64 time_get_ns(void) { @@ -44,6 +45,7 @@ enum test_type { LPM_KMALLOC, HASH_LOOKUP, ARRAY_LOOKUP, + INNER_LRU_HASH_PREALLOC, NR_TESTS, }; @@ -57,10 +59,14 @@ const char *test_map_names[NR_TESTS] = { [LPM_KMALLOC] = "lpm_trie_map_alloc", [HASH_LOOKUP] = "hash_map", [ARRAY_LOOKUP] = "array_map", + [INNER_LRU_HASH_PREALLOC] = "inner_lru_hash_map", }; static int test_flags = ~0; static uint32_t num_map_entries; +static uint32_t inner_lru_hash_size; +static int inner_lru_hash_idx = -1; +static int array_of_lru_hashs_idx = -1; static uint32_t max_cnt = 1000000; static int check_test_flags(enum test_type t) @@ -82,11 +88,42 @@ static void test_hash_prealloc(int cpu) static void do_test_lru(enum test_type test, int cpu) { + static int inner_lru_map_fds[MAX_NR_CPUS]; + struct sockaddr_in6 in6 = { .sin6_family = AF_INET6 }; const char *test_name; __u64 start_time; int i, ret; + if (test == INNER_LRU_HASH_PREALLOC) { + int outer_fd = map_fd[array_of_lru_hashs_idx]; + + assert(cpu < MAX_NR_CPUS); + + if (cpu) { + inner_lru_map_fds[cpu] = + bpf_create_map(BPF_MAP_TYPE_LRU_HASH, + sizeof(uint32_t), sizeof(long), + inner_lru_hash_size, 0); + if (inner_lru_map_fds[cpu] == -1) { + printf("cannot create BPF_MAP_TYPE_LRU_HASH %s(%d)\n", + strerror(errno), errno); + exit(1); + } + } else { + inner_lru_map_fds[cpu] = map_fd[inner_lru_hash_idx]; + } + + ret = bpf_map_update_elem(outer_fd, &cpu, + &inner_lru_map_fds[cpu], + BPF_ANY); + if (ret) { + printf("cannot update ARRAY_OF_LRU_HASHS with key:%u. %s(%d)\n", + cpu, strerror(errno), errno); + exit(1); + } + } + in6.sin6_addr.s6_addr16[0] = 0xdead; in6.sin6_addr.s6_addr16[1] = 0xbeef; @@ -96,6 +133,9 @@ static void do_test_lru(enum test_type test, int cpu) } else if (test == NOCOMMON_LRU_HASH_PREALLOC) { test_name = "nocommon_lru_hash_map_perf"; in6.sin6_addr.s6_addr16[7] = 1; + } else if (test == INNER_LRU_HASH_PREALLOC) { + test_name = "inner_lru_hash_map_perf"; + in6.sin6_addr.s6_addr16[7] = 2; } else { assert(0); } @@ -120,6 +160,11 @@ static void test_nocommon_lru_hash_prealloc(int cpu) do_test_lru(NOCOMMON_LRU_HASH_PREALLOC, cpu); } +static void test_inner_lru_hash_prealloc(int cpu) +{ + do_test_lru(INNER_LRU_HASH_PREALLOC, cpu); +} + static void test_percpu_hash_prealloc(int cpu) { __u64 start_time; @@ -203,6 +248,7 @@ const test_func test_funcs[] = { [LPM_KMALLOC] = test_lpm_kmalloc, [HASH_LOOKUP] = test_hash_lookup, [ARRAY_LOOKUP] = test_array_lookup, + [INNER_LRU_HASH_PREALLOC] = test_inner_lru_hash_prealloc, }; static void loop(int cpu) @@ -278,9 +324,25 @@ static void fixup_map(struct bpf_map_def *map, const char *name, int idx) { int i; + if (!strcmp("inner_lru_hash_map", name)) { + inner_lru_hash_idx = idx; + inner_lru_hash_size = map->max_entries; + } + + if (!strcmp("array_of_lru_hashs", name)) { + if (inner_lru_hash_idx == -1) { + printf("inner_lru_hash_map must be defined before array_of_lru_hashs\n"); + exit(1); + } + map->inner_map_idx = inner_lru_hash_idx; + array_of_lru_hashs_idx = idx; + } + if (num_map_entries <= 0) return; + inner_lru_hash_size = num_map_entries; + /* Only change the max_entries for the enabled test(s) */ for (i = 0; i < NR_TESTS; i++) { if (!strcmp(test_map_names[i], name) && -- cgit From 1dbba4cb8df73844ea0d7c1fd11e1ea35f1670b7 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 14 Apr 2017 22:10:41 +0300 Subject: net: phy: test the right variable in phy_write_mmd() This is a copy and paste buglet. We meant to test for ->write_mmd but we test for ->read_mmd. Fixes: 1ee6b9bc6206 ("net: phy: make phy_(read|write)_mmd() generic MMD accessors") Signed-off-by: Dan Carpenter Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phy-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index 357a4d0d7641..6739b738bbaf 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -76,7 +76,7 @@ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) if (regnum > (u16)~0 || devad > 32) return -EINVAL; - if (phydev->drv->read_mmd) { + if (phydev->drv->write_mmd) { ret = phydev->drv->write_mmd(phydev, devad, regnum, val); } else if (phydev->is_c45) { u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); -- cgit From 776e726bfb3493f71bb11759f7a60f1b0f6dd2d2 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 14 Apr 2017 14:42:57 -0700 Subject: netvsc: fix RCU warning in get_stats The statistics functionis called with RTNL held during probe but with RCU held during access from /proc and elsewhere. This is safe so update the lockdep annotation. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 51fa90365348..97d0b5875cd5 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -943,7 +943,7 @@ static void netvsc_get_stats64(struct net_device *net, struct rtnl_link_stats64 *t) { struct net_device_context *ndev_ctx = netdev_priv(net); - struct netvsc_device *nvdev = rcu_dereference(ndev_ctx->nvdev); + struct netvsc_device *nvdev = rcu_dereference_rtnl(ndev_ctx->nvdev); int i; if (!nvdev) -- cgit From f3c9d40ee12926f330a1dfebce0bebadd1406ba6 Mon Sep 17 00:00:00 2001 From: Simon Xiao Date: Fri, 14 Apr 2017 14:42:58 -0700 Subject: hv_netvsc: change netvsc device default duplex to FULL The netvsc device supports full duplex by default. This warnings in log from bonding device which did not like seeing UNKNOWN duplex. Signed-off-by: Simon Xiao Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 97d0b5875cd5..4421a6d00375 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -861,7 +861,7 @@ static void netvsc_init_settings(struct net_device *dev) struct net_device_context *ndc = netdev_priv(dev); ndc->speed = SPEED_UNKNOWN; - ndc->duplex = DUPLEX_UNKNOWN; + ndc->duplex = DUPLEX_FULL; } static int netvsc_get_link_ksettings(struct net_device *dev, -- cgit From 9fd0f315631f3d3677cbd44966749cb63e55058a Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Fri, 14 Apr 2017 18:25:26 -0700 Subject: Add uid and cookie bpf helper to cg_skb_func_proto BPF helper functions get_socket_cookie and get_socket_uid can be used for network traffic classifications, among others. Expose them also to programs of type BPF_PROG_TYPE_CGROUP_SKB. As of commit 8f917bba0042 ("bpf: pass sk to helper functions") the required skb->sk function is available at both cgroup bpf ingress and egress hooks. With these two new helper, cg_skb_func_proto is effectively the same as sk_filter_func_proto. Change since V1: Instead of add the helper to cg_skb_func_proto, redirect the cg_skb_func_proto to sk_filter_func_proto since all helper function in sk_filter_func_proto are applicable to cg_skb_func_proto now. Signed-off-by: Chenbo Feng Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- net/core/filter.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index ce2a19da8aa4..19be954f8ce7 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2766,12 +2766,7 @@ xdp_func_proto(enum bpf_func_id func_id) static const struct bpf_func_proto * cg_skb_func_proto(enum bpf_func_id func_id) { - switch (func_id) { - case BPF_FUNC_skb_load_bytes: - return &bpf_skb_load_bytes_proto; - default: - return bpf_base_func_proto(func_id); - } + return sk_filter_func_proto(func_id); } static const struct bpf_func_proto * -- cgit From edb12f2d72d735acfb3aa53bbda64b1827042209 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 15 Apr 2017 21:56:57 +0800 Subject: sctp: get list_of_streams of strreset outreq earlier Now when processing strreset out responses, it gets outreq->list_of_streams only when result is performed. But if result is not performed, str_p will be NULL. It will cause panic in sctp_ulpevent_make_stream_reset_event if nums is not 0. This patch is to fix it by getting outreq->list_of_streams earlier, and also to improve some codes for the strreset inreq process. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- net/sctp/stream.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/sctp/stream.c b/net/sctp/stream.c index eff6008a32ba..4ec3679a1cef 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -732,14 +732,14 @@ struct sctp_chunk *sctp_process_strreset_resp( if (req->type == SCTP_PARAM_RESET_OUT_REQUEST) { struct sctp_strreset_outreq *outreq; - __u16 *str_p = NULL; + __u16 *str_p; outreq = (struct sctp_strreset_outreq *)req; + str_p = outreq->list_of_streams; nums = (ntohs(outreq->param_hdr.length) - sizeof(*outreq)) / 2; if (result == SCTP_STRRESET_PERFORMED) { if (nums) { - str_p = outreq->list_of_streams; for (i = 0; i < nums; i++) stream->out[ntohs(str_p[i])].ssn = 0; } else { @@ -757,16 +757,16 @@ struct sctp_chunk *sctp_process_strreset_resp( nums, str_p, GFP_ATOMIC); } else if (req->type == SCTP_PARAM_RESET_IN_REQUEST) { struct sctp_strreset_inreq *inreq; - __u16 *str_p = NULL; + __u16 *str_p; /* if the result is performed, it's impossible for inreq */ if (result == SCTP_STRRESET_PERFORMED) return NULL; inreq = (struct sctp_strreset_inreq *)req; + str_p = inreq->list_of_streams; nums = (ntohs(inreq->param_hdr.length) - sizeof(*inreq)) / 2; - str_p = inreq->list_of_streams; *evp = sctp_ulpevent_make_stream_reset_event(asoc, flags, nums, str_p, GFP_ATOMIC); } else if (req->type == SCTP_PARAM_RESET_TSN_REQUEST) { -- cgit From 7a7a9bd7aceea77dc2397bc58a1e88197ed759cb Mon Sep 17 00:00:00 2001 From: Ilan Tayari Date: Sun, 16 Apr 2017 11:00:07 +0300 Subject: gso: Validate assumption of frag_list segementation Commit 07b26c9454a2 ("gso: Support partial splitting at the frag_list pointer") assumes that all SKBs in a frag_list (except maybe the last one) contain the same amount of GSO payload. This assumption is not always correct, resulting in the following warning message in the log: skb_segment: too many frags For example, mlx5 driver in Striding RQ mode creates some RX SKBs with one frag, and some with 2 frags. After GRO, the frag_list SKBs end up having different amounts of payload. If this frag_list SKB is then forwarded, the aforementioned assumption is violated. Validate the assumption, and fall back to software GSO if it not true. Fixes: 07b26c9454a2 ("gso: Support partial splitting at the frag_list pointer") Signed-off-by: Ilan Tayari Signed-off-by: Ilya Lesokhin Signed-off-by: David S. Miller --- net/core/skbuff.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 5d9a11eafbf5..ad2af563756a 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3082,22 +3082,32 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, if (sg && csum && (mss != GSO_BY_FRAGS)) { if (!(features & NETIF_F_GSO_PARTIAL)) { struct sk_buff *iter; + unsigned int frag_len; if (!list_skb || !net_gso_ok(features, skb_shinfo(head_skb)->gso_type)) goto normal; - /* Split the buffer at the frag_list pointer. - * This is based on the assumption that all - * buffers in the chain excluding the last - * containing the same amount of data. + /* If we get here then all the required + * GSO features except frag_list are supported. + * Try to split the SKB to multiple GSO SKBs + * with no frag_list. + * Currently we can do that only when the buffers don't + * have a linear part and all the buffers except + * the last are of the same length. */ + frag_len = list_skb->len; skb_walk_frags(head_skb, iter) { + if (frag_len != iter->len && iter->next) + goto normal; if (skb_headlen(iter) && !iter->head_frag) goto normal; len -= iter->len; } + + if (len != frag_len) + goto normal; } /* GSO partial only requires that we trim off any excess that -- cgit From af3b5158b89d3bab9be881113417558c71b71ca4 Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Sun, 16 Apr 2017 12:27:14 +0200 Subject: ipv6: sr: fix BUG due to headroom too small after SRH push When a locally generated packet receives an SRH with two or more segments, the remaining headroom is too small to push an ethernet header. This patch ensures that the headroom is large enough after SRH push. The BUG generated the following trace. [ 192.950285] skbuff: skb_under_panic: text:ffffffff81809675 len:198 put:14 head:ffff88006f306400 data:ffff88006f3063fa tail:0xc0 end:0x2c0 dev:A-1 [ 192.952456] ------------[ cut here ]------------ [ 192.953218] kernel BUG at net/core/skbuff.c:105! [ 192.953411] invalid opcode: 0000 [#1] PREEMPT SMP [ 192.953411] Modules linked in: [ 192.953411] CPU: 5 PID: 3433 Comm: ping6 Not tainted 4.11.0-rc3+ #237 [ 192.953411] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.10.1-0-g8891697-prebuilt.qemu-project.org 04/01/2014 [ 192.953411] task: ffff88007c2d42c0 task.stack: ffffc90000ef4000 [ 192.953411] RIP: 0010:skb_panic+0x61/0x70 [ 192.953411] RSP: 0018:ffffc90000ef7900 EFLAGS: 00010286 [ 192.953411] RAX: 0000000000000085 RBX: 00000000000086dd RCX: 0000000000000201 [ 192.953411] RDX: 0000000080000201 RSI: ffffffff81d104c5 RDI: 00000000ffffffff [ 192.953411] RBP: ffffc90000ef7920 R08: 0000000000000001 R09: 0000000000000000 [ 192.953411] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 [ 192.953411] R13: ffff88007c5a4000 R14: ffff88007b363d80 R15: 00000000000000b8 [ 192.953411] FS: 00007f94b558b700(0000) GS:ffff88007fd40000(0000) knlGS:0000000000000000 [ 192.953411] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 192.953411] CR2: 00007fff5ecd5080 CR3: 0000000074141000 CR4: 00000000001406e0 [ 192.953411] Call Trace: [ 192.953411] skb_push+0x3b/0x40 [ 192.953411] eth_header+0x25/0xc0 [ 192.953411] neigh_resolve_output+0x168/0x230 [ 192.953411] ? ip6_finish_output2+0x242/0x8f0 [ 192.953411] ip6_finish_output2+0x242/0x8f0 [ 192.953411] ? ip6_finish_output2+0x76/0x8f0 [ 192.953411] ip6_finish_output+0xa8/0x1d0 [ 192.953411] ip6_output+0x64/0x2d0 [ 192.953411] ? ip6_output+0x73/0x2d0 [ 192.953411] ? ip6_dst_check+0xb5/0xc0 [ 192.953411] ? dst_cache_per_cpu_get.isra.2+0x40/0x80 [ 192.953411] seg6_output+0xb0/0x220 [ 192.953411] lwtunnel_output+0xcf/0x210 [ 192.953411] ? lwtunnel_output+0x59/0x210 [ 192.953411] ip6_local_out+0x38/0x70 [ 192.953411] ip6_send_skb+0x2a/0xb0 [ 192.953411] ip6_push_pending_frames+0x48/0x50 [ 192.953411] rawv6_sendmsg+0xa39/0xf10 [ 192.953411] ? __lock_acquire+0x489/0x890 [ 192.953411] ? __mutex_lock+0x1fc/0x970 [ 192.953411] ? __lock_acquire+0x489/0x890 [ 192.953411] ? __mutex_lock+0x1fc/0x970 [ 192.953411] ? tty_ioctl+0x283/0xec0 [ 192.953411] inet_sendmsg+0x45/0x1d0 [ 192.953411] ? _copy_from_user+0x54/0x80 [ 192.953411] sock_sendmsg+0x33/0x40 [ 192.953411] SYSC_sendto+0xef/0x170 [ 192.953411] ? entry_SYSCALL_64_fastpath+0x5/0xc2 [ 192.953411] ? trace_hardirqs_on_caller+0x12b/0x1b0 [ 192.953411] ? trace_hardirqs_on_thunk+0x1a/0x1c [ 192.953411] SyS_sendto+0x9/0x10 [ 192.953411] entry_SYSCALL_64_fastpath+0x1f/0xc2 [ 192.953411] RIP: 0033:0x7f94b453db33 [ 192.953411] RSP: 002b:00007fff5ecd0578 EFLAGS: 00000246 ORIG_RAX: 000000000000002c [ 192.953411] RAX: ffffffffffffffda RBX: 00007fff5ecd16e0 RCX: 00007f94b453db33 [ 192.953411] RDX: 0000000000000040 RSI: 000055a78352e9c0 RDI: 0000000000000003 [ 192.953411] RBP: 00007fff5ecd1690 R08: 000055a78352c940 R09: 000000000000001c [ 192.953411] R10: 0000000000000000 R11: 0000000000000246 R12: 000055a783321e10 [ 192.953411] R13: 000055a7839890c0 R14: 0000000000000004 R15: 0000000000000000 [ 192.953411] Code: 00 00 48 89 44 24 10 8b 87 c4 00 00 00 48 89 44 24 08 48 8b 87 d8 00 00 00 48 c7 c7 90 58 d2 81 48 89 04 24 31 c0 e8 4f 70 9a ff <0f> 0b 0f 1f 00 66 2e 0f 1f 84 00 00 00 00 00 48 8b 97 d8 00 00 [ 192.953411] RIP: skb_panic+0x61/0x70 RSP: ffffc90000ef7900 [ 193.000186] ---[ end trace bd0b89fabdf2f92c ]--- [ 193.000951] Kernel panic - not syncing: Fatal exception in interrupt [ 193.001137] Kernel Offset: disabled [ 193.001169] ---[ end Kernel panic - not syncing: Fatal exception in interrupt Fixes: 19d5a26f5ef8de5dcb78799feaf404d717b1aac3 ("ipv6: sr: expand skb head only if necessary") Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/seg6_iptunnel.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 7436a4a62f3e..6a495490d43e 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -265,6 +265,10 @@ static int seg6_input(struct sk_buff *skb) skb_dst_set(skb, dst); } + err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); + if (unlikely(err)) + return err; + return dst_input(skb); } @@ -310,6 +314,10 @@ static int seg6_output(struct net *net, struct sock *sk, struct sk_buff *skb) skb_dst_drop(skb); skb_dst_set(skb, dst); + err = skb_cow_head(skb, LL_RESERVED_SPACE(dst->dev)); + if (unlikely(err)) + goto drop; + return dst_output(net, sk, skb); drop: kfree_skb(skb); -- cgit From c21ef3e343ae916ad3cfd4dc6ef6791c1f80a010 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sun, 16 Apr 2017 09:48:24 -0700 Subject: net: rtnetlink: plumb extended ack to doit function Add netlink_ext_ack arg to rtnl_doit_func. Pass extack arg to nlmsg_parse for doit functions that call it directly. This is the first step to using extended error reporting in rtnetlink. >From here individual subsystems can be updated to set netlink_ext_ack as needed. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/vrf.c | 4 ++-- include/net/fib_rules.h | 6 ++++-- include/net/rtnetlink.h | 3 ++- net/bridge/br_mdb.c | 6 ++++-- net/can/gw.c | 6 ++++-- net/core/fib_rules.c | 10 ++++++---- net/core/neighbour.c | 15 +++++++++------ net/core/net_namespace.c | 10 ++++++---- net/core/rtnetlink.c | 42 ++++++++++++++++++++++++++---------------- net/dcb/dcbnl.c | 5 +++-- net/decnet/dn_dev.c | 12 ++++++++---- net/decnet/dn_fib.c | 10 ++++++---- net/decnet/dn_route.c | 6 ++++-- net/ipv4/devinet.c | 13 ++++++++----- net/ipv4/fib_frontend.c | 6 ++++-- net/ipv4/ipmr.c | 10 ++++++---- net/ipv4/route.c | 5 +++-- net/ipv6/addrconf.c | 20 ++++++++++++-------- net/ipv6/addrlabel.c | 12 ++++++++---- net/ipv6/route.c | 11 +++++++---- net/mpls/af_mpls.c | 9 ++++++--- net/phonet/pn_netlink.c | 10 ++++++---- net/qrtr/qrtr.c | 5 +++-- net/sched/act_api.c | 5 +++-- net/sched/cls_api.c | 5 +++-- net/sched/sch_api.c | 15 +++++++++------ 26 files changed, 162 insertions(+), 99 deletions(-) diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index eb5493e83556..a84dcad2ee91 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -1282,11 +1282,11 @@ static int vrf_fib_rule(const struct net_device *dev, __u8 family, bool add_it) /* fib_nl_{new,del}rule handling looks for net from skb->sk */ skb->sk = dev_net(dev)->rtnl; if (add_it) { - err = fib_nl_newrule(skb, nlh); + err = fib_nl_newrule(skb, nlh, NULL); if (err == -EEXIST) err = 0; } else { - err = fib_nl_delrule(skb, nlh); + err = fib_nl_delrule(skb, nlh, NULL); if (err == -ENOENT) err = 0; } diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 1243b9c7694e..76c7300626d6 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -143,6 +143,8 @@ int fib_default_rule_add(struct fib_rules_ops *, u32 pref, u32 table, u32 flags); bool fib_rule_matchall(const struct fib_rule *rule); -int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh); -int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh); +int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack); +int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack); #endif diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index c07b941fce89..78fa5fe32947 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -4,7 +4,8 @@ #include #include -typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *); +typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *, + struct netlink_ext_ack *); typedef int (*rtnl_dumpit_func)(struct sk_buff *, struct netlink_callback *); typedef u16 (*rtnl_calcit_func)(struct sk_buff *, struct nlmsghdr *); diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 993626a7fc3b..b0845480a3ae 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -569,7 +569,8 @@ static int __br_mdb_add(struct net *net, struct net_bridge *br, return ret; } -static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh) +static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct net_bridge_vlan_group *vg; @@ -663,7 +664,8 @@ unlock: return err; } -static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh) +static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct net_bridge_vlan_group *vg; diff --git a/net/can/gw.c b/net/can/gw.c index 3b84fb7d98aa..ad5bf5d508d3 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -809,7 +809,8 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, return 0; } -static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh) +static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct rtcanmsg *r; struct cgw_job *gwj; @@ -921,7 +922,8 @@ static void cgw_remove_all_jobs(void) } } -static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh) +static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct cgw_job *gwj = NULL; struct hlist_node *nx; diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index df03110ca3c8..c58c1df6f92b 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -368,7 +368,8 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh, return 0; } -int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh) +int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct fib_rule_hdr *frh = nlmsg_data(nlh); @@ -386,7 +387,7 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh) goto errout; } - err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy, NULL); + err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy, extack); if (err < 0) goto errout; @@ -561,7 +562,8 @@ errout: } EXPORT_SYMBOL_GPL(fib_nl_newrule); -int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh) +int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct fib_rule_hdr *frh = nlmsg_data(nlh); @@ -580,7 +582,7 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh) goto errout; } - err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy, NULL); + err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy, extack); if (err < 0) goto errout; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 31f37b264710..58b0bcc125b5 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1590,7 +1590,8 @@ static struct neigh_table *neigh_find_table(int family) return tbl; } -static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh) +static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct ndmsg *ndm; @@ -1648,7 +1649,8 @@ out: return err; } -static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh) +static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE; struct net *net = sock_net(skb->sk); @@ -1661,7 +1663,7 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh) int err; ASSERT_RTNL(); - err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, NULL); + err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, extack); if (err < 0) goto out; @@ -1936,7 +1938,8 @@ static const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = { [NDTPA_LOCKTIME] = { .type = NLA_U64 }, }; -static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh) +static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct neigh_table *tbl; @@ -1946,7 +1949,7 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh) int err, tidx; err = nlmsg_parse(nlh, sizeof(*ndtmsg), tb, NDTA_MAX, - nl_neightbl_policy, NULL); + nl_neightbl_policy, extack); if (err < 0) goto errout; @@ -1984,7 +1987,7 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh) int i, ifindex = 0; err = nla_parse_nested(tbp, NDTPA_MAX, tb[NDTA_PARMS], - nl_ntbl_parm_policy, NULL); + nl_ntbl_parm_policy, extack); if (err < 0) goto errout_tbl_lock; diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index ec18cbc756d2..c1d8aed8e5a8 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -571,7 +571,8 @@ static const struct nla_policy rtnl_net_policy[NETNSA_MAX + 1] = { [NETNSA_FD] = { .type = NLA_U32 }, }; -static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh) +static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct nlattr *tb[NETNSA_MAX + 1]; @@ -579,7 +580,7 @@ static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh) int nsid, err; err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, - rtnl_net_policy, NULL); + rtnl_net_policy, extack); if (err < 0) return err; if (!tb[NETNSA_NSID]) @@ -644,7 +645,8 @@ nla_put_failure: return -EMSGSIZE; } -static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh) +static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct nlattr *tb[NETNSA_MAX + 1]; @@ -653,7 +655,7 @@ static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh) int err, id; err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, - rtnl_net_policy, NULL); + rtnl_net_policy, extack); if (err < 0) return err; if (tb[NETNSA_PID]) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 0ee5479528b5..088f9c8b4196 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2213,7 +2213,8 @@ errout: return err; } -static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh) +static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct ifinfomsg *ifm; @@ -2222,7 +2223,8 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh) struct nlattr *tb[IFLA_MAX+1]; char ifname[IFNAMSIZ]; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, NULL); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, + extack); if (err < 0) goto errout; @@ -2306,7 +2308,8 @@ int rtnl_delete_link(struct net_device *dev) } EXPORT_SYMBOL_GPL(rtnl_delete_link); -static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh) +static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct net_device *dev; @@ -2315,7 +2318,7 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh) struct nlattr *tb[IFLA_MAX+1]; int err; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, NULL); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, extack); if (err < 0) return err; @@ -2426,7 +2429,8 @@ static int rtnl_group_changelink(const struct sk_buff *skb, return 0; } -static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh) +static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); const struct rtnl_link_ops *ops; @@ -2444,7 +2448,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh) #ifdef CONFIG_MODULES replay: #endif - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, NULL); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, extack); if (err < 0) return err; @@ -2678,7 +2682,8 @@ out_unregister: } } -static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh) +static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct ifinfomsg *ifm; @@ -2689,7 +2694,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr* nlh) int err; u32 ext_filter_mask = 0; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, NULL); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFLA_MAX, ifla_policy, extack); if (err < 0) return err; @@ -2960,7 +2965,8 @@ static int fdb_vid_parse(struct nlattr *vlan_attr, u16 *p_vid) return 0; } -static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh) +static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct ndmsg *ndm; @@ -2970,7 +2976,7 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh) u16 vid; int err; - err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, NULL); + err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, extack); if (err < 0) return err; @@ -3060,7 +3066,8 @@ int ndo_dflt_fdb_del(struct ndmsg *ndm, } EXPORT_SYMBOL(ndo_dflt_fdb_del); -static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh) +static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct ndmsg *ndm; @@ -3073,7 +3080,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh) if (!netlink_capable(skb, CAP_NET_ADMIN)) return -EPERM; - err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, NULL); + err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, extack); if (err < 0) return err; @@ -3503,7 +3510,8 @@ errout: return err; } -static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh) +static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct ifinfomsg *ifm; @@ -3577,7 +3585,8 @@ out: return err; } -static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh) +static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct ifinfomsg *ifm; @@ -3945,7 +3954,8 @@ static size_t if_nlmsg_stats_size(const struct net_device *dev, return size; } -static int rtnl_stats_get(struct sk_buff *skb, struct nlmsghdr *nlh) +static int rtnl_stats_get(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct net_device *dev = NULL; @@ -4107,7 +4117,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, if (doit == NULL) return -EOPNOTSUPP; - return doit(skb, nlh); + return doit(skb, nlh, extack); } static void rtnetlink_rcv(struct sk_buff *skb) diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 3f5a5f710576..93106120f987 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1696,7 +1696,8 @@ static const struct reply_func reply_funcs[DCB_CMD_MAX+1] = { [DCB_CMD_CEE_GET] = { RTM_GETDCB, dcbnl_cee_get }, }; -static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh) +static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct net_device *netdev; @@ -1712,7 +1713,7 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh) return -EPERM; ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX, - dcbnl_rtnl_policy, NULL); + dcbnl_rtnl_policy, extack); if (ret < 0) return ret; diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index e65f1be44e8e..9017a9a73ab5 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -565,7 +565,8 @@ static const struct nla_policy dn_ifa_policy[IFA_MAX+1] = { [IFA_FLAGS] = { .type = NLA_U32 }, }; -static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) +static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct nlattr *tb[IFA_MAX+1]; @@ -581,7 +582,8 @@ static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) if (!net_eq(net, &init_net)) goto errout; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy, NULL); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy, + extack); if (err < 0) goto errout; @@ -609,7 +611,8 @@ errout: return err; } -static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) +static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct nlattr *tb[IFA_MAX+1]; @@ -625,7 +628,8 @@ static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) if (!net_eq(net, &init_net)) return -EINVAL; - err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy, NULL); + err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, dn_ifa_policy, + extack); if (err < 0) return err; diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index 34663bf8aa6d..f9058ebeb635 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -501,7 +501,8 @@ static inline u32 rtm_get_table(struct nlattr *attrs[], u8 table) return table; } -static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) +static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct dn_fib_table *tb; @@ -516,7 +517,7 @@ static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) return -EINVAL; err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy, - NULL); + extack); if (err < 0) return err; @@ -527,7 +528,8 @@ static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) return tb->delete(tb, r, attrs, nlh, &NETLINK_CB(skb)); } -static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) +static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct dn_fib_table *tb; @@ -542,7 +544,7 @@ static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) return -EINVAL; err = nlmsg_parse(nlh, sizeof(*r), attrs, RTA_MAX, rtm_dn_policy, - NULL); + extack); if (err < 0) return err; diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 2d7097bbc666..4b9518a0d248 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1640,7 +1640,8 @@ const struct nla_policy rtm_dn_policy[RTA_MAX + 1] = { /* * This is called by both endnodes and routers now. */ -static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) +static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(in_skb->sk); struct rtmsg *rtm = nlmsg_data(nlh); @@ -1654,7 +1655,8 @@ static int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) if (!net_eq(net, &init_net)) return -EINVAL; - err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_dn_policy, NULL); + err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_dn_policy, + extack); if (err < 0) return err; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index f33f53791f50..df14815a3b8c 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -571,7 +571,8 @@ static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa) return ret; } -static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) +static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct nlattr *tb[IFA_MAX+1]; @@ -583,7 +584,7 @@ static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) ASSERT_RTNL(); err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy, - NULL); + extack); if (err < 0) goto errout; @@ -845,7 +846,8 @@ static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa) return NULL; } -static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) +static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct in_ifaddr *ifa; @@ -1871,7 +1873,8 @@ static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { }; static int inet_netconf_get_devconf(struct sk_buff *in_skb, - struct nlmsghdr *nlh) + struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(in_skb->sk); struct nlattr *tb[NETCONFA_MAX+1]; @@ -1884,7 +1887,7 @@ static int inet_netconf_get_devconf(struct sk_buff *in_skb, int err; err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, - devconf_ipv4_policy, NULL); + devconf_ipv4_policy, extack); if (err < 0) goto errout; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 434dd2538716..5a0e456b5d58 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -710,7 +710,8 @@ errout: return err; } -static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) +static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct fib_config cfg; @@ -732,7 +733,8 @@ errout: return err; } -static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) +static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct fib_config cfg; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index d7be21f2174a..95ea3585a223 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -2430,7 +2430,8 @@ static int ipmr_nla_get_ttls(const struct nlattr *nla, struct mfcctl *mfcc) /* returns < 0 on error, 0 for ADD_MFC and 1 for ADD_MFC_PROXY */ static int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh, struct mfcctl *mfcc, int *mrtsock, - struct mr_table **mrtret) + struct mr_table **mrtret, + struct netlink_ext_ack *extack) { struct net_device *dev = NULL; u32 tblid = RT_TABLE_DEFAULT; @@ -2440,7 +2441,7 @@ static int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh, int ret, rem; ret = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipmr_policy, - NULL); + extack); if (ret < 0) goto out; rtm = nlmsg_data(nlh); @@ -2499,7 +2500,8 @@ out: } /* takes care of both newroute and delroute */ -static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh) +static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); int ret, mrtsock, parent; @@ -2508,7 +2510,7 @@ static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh) mrtsock = 0; tbl = NULL; - ret = rtm_to_ipmr_mfcc(net, nlh, &mfcc, &mrtsock, &tbl); + ret = rtm_to_ipmr_mfcc(net, nlh, &mfcc, &mrtsock, &tbl, extack); if (ret < 0) return ret; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 7a4f2c38c3c4..a4443748cc1e 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2629,7 +2629,8 @@ nla_put_failure: return -EMSGSIZE; } -static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) +static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(in_skb->sk); struct rtmsg *rtm; @@ -2646,7 +2647,7 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) kuid_t uid; err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv4_policy, - NULL); + extack); if (err < 0) goto errout; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 8a6756ac7519..08f9e8ea7a81 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -611,7 +611,8 @@ static const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = { }; static int inet6_netconf_get_devconf(struct sk_buff *in_skb, - struct nlmsghdr *nlh) + struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(in_skb->sk); struct nlattr *tb[NETCONFA_MAX+1]; @@ -624,7 +625,7 @@ static int inet6_netconf_get_devconf(struct sk_buff *in_skb, int err; err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, - devconf_ipv6_policy, NULL); + devconf_ipv6_policy, extack); if (err < 0) goto errout; @@ -4413,7 +4414,8 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = { }; static int -inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) +inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct ifaddrmsg *ifm; @@ -4423,7 +4425,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) int err; err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy, - NULL); + extack); if (err < 0) return err; @@ -4523,7 +4525,8 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags, } static int -inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) +inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct ifaddrmsg *ifm; @@ -4536,7 +4539,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) int err; err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy, - NULL); + extack); if (err < 0) return err; @@ -4886,7 +4889,8 @@ static int inet6_dump_ifacaddr(struct sk_buff *skb, struct netlink_callback *cb) return inet6_dump_addr(skb, cb, type); } -static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh) +static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(in_skb->sk); struct ifaddrmsg *ifm; @@ -4898,7 +4902,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh) int err; err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy, - NULL); + extack); if (err < 0) goto errout; diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c index 6cb4ed91722a..07cd7d248bb6 100644 --- a/net/ipv6/addrlabel.c +++ b/net/ipv6/addrlabel.c @@ -404,7 +404,8 @@ static const struct nla_policy ifal_policy[IFAL_MAX+1] = { [IFAL_LABEL] = { .len = sizeof(u32), }, }; -static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh) +static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct ifaddrlblmsg *ifal; @@ -413,7 +414,8 @@ static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh) u32 label; int err = 0; - err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy, NULL); + err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy, + extack); if (err < 0) return err; @@ -521,7 +523,8 @@ static inline int ip6addrlbl_msgsize(void) + nla_total_size(4); /* IFAL_LABEL */ } -static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh) +static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(in_skb->sk); struct ifaddrlblmsg *ifal; @@ -532,7 +535,8 @@ static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh) struct ip6addrlbl_entry *p; struct sk_buff *skb; - err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy, NULL); + err = nlmsg_parse(nlh, sizeof(*ifal), tb, IFAL_MAX, ifal_policy, + extack); if (err < 0) return err; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index ccde23eba702..4ba7c49872ff 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3260,7 +3260,8 @@ static int ip6_route_multipath_del(struct fib6_config *cfg) return last_err; } -static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) +static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct fib6_config cfg; int err; @@ -3277,7 +3278,8 @@ static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) } } -static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) +static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct fib6_config cfg; int err; @@ -3565,7 +3567,8 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg) NLM_F_MULTI); } -static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) +static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(in_skb->sk); struct nlattr *tb[RTA_MAX+1]; @@ -3576,7 +3579,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh) int err, iif = 0, oif = 0; err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy, - NULL); + extack); if (err < 0) goto errout; diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index 07181d2273e1..088e2b459d0f 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -1110,7 +1110,8 @@ static const struct nla_policy devconf_mpls_policy[NETCONFA_MAX + 1] = { }; static int mpls_netconf_get_devconf(struct sk_buff *in_skb, - struct nlmsghdr *nlh) + struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(in_skb->sk); struct nlattr *tb[NETCONFA_MAX + 1]; @@ -1746,7 +1747,8 @@ errout: return err; } -static int mpls_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) +static int mpls_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct mpls_route_config *cfg; int err; @@ -1767,7 +1769,8 @@ out: } -static int mpls_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) +static int mpls_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct mpls_route_config *cfg; int err; diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c index 363799bf97f6..45b3af3080d8 100644 --- a/net/phonet/pn_netlink.c +++ b/net/phonet/pn_netlink.c @@ -61,7 +61,8 @@ static const struct nla_policy ifa_phonet_policy[IFA_MAX+1] = { [IFA_LOCAL] = { .type = NLA_U8 }, }; -static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh) +static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct nlattr *tb[IFA_MAX+1]; @@ -79,7 +80,7 @@ static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh) ASSERT_RTNL(); err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_phonet_policy, - NULL); + extack); if (err < 0) return err; @@ -227,7 +228,8 @@ static const struct nla_policy rtm_phonet_policy[RTA_MAX+1] = { [RTA_OIF] = { .type = NLA_U32 }, }; -static int route_doit(struct sk_buff *skb, struct nlmsghdr *nlh) +static int route_doit(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct nlattr *tb[RTA_MAX+1]; @@ -245,7 +247,7 @@ static int route_doit(struct sk_buff *skb, struct nlmsghdr *nlh) ASSERT_RTNL(); err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_phonet_policy, - NULL); + extack); if (err < 0) return err; diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c index 7fdbb34002f5..c36b0ec364a4 100644 --- a/net/qrtr/qrtr.c +++ b/net/qrtr/qrtr.c @@ -943,7 +943,8 @@ static const struct nla_policy qrtr_policy[IFA_MAX + 1] = { [IFA_LOCAL] = { .type = NLA_U32 }, }; -static int qrtr_addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh) +static int qrtr_addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) { struct nlattr *tb[IFA_MAX + 1]; struct ifaddrmsg *ifm; @@ -957,7 +958,7 @@ static int qrtr_addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh) ASSERT_RTNL(); - rc = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, qrtr_policy, NULL); + rc = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, qrtr_policy, extack); if (rc < 0) return rc; diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 79d875c6e8a0..82b1d48d91cc 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -993,7 +993,8 @@ static int tcf_action_add(struct net *net, struct nlattr *nla, return tcf_add_notify(net, n, &actions, portid); } -static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n) +static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct nlattr *tca[TCA_ACT_MAX + 1]; @@ -1005,7 +1006,7 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n) return -EPERM; ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL, - NULL); + extack); if (ret < 0) return ret; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index e2c68c30f97d..a8da383b681a 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -201,7 +201,8 @@ EXPORT_SYMBOL(tcf_destroy_chain); /* Add/change/delete/get a filter node */ -static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n) +static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct nlattr *tca[TCA_MAX + 1]; @@ -229,7 +230,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n) replay: tp_created = 0; - err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL, NULL); + err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL, extack); if (err < 0) return err; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 9b09ef9f944d..bbe57d57b67f 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1125,7 +1125,8 @@ check_loop_fn(struct Qdisc *q, unsigned long cl, struct qdisc_walker *w) * Delete/get qdisc. */ -static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n) +static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct tcmsg *tcm = nlmsg_data(n); @@ -1140,7 +1141,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n) !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) return -EPERM; - err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL, NULL); + err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL, extack); if (err < 0) return err; @@ -1194,7 +1195,8 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n) * Create/change qdisc. */ -static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n) +static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct tcmsg *tcm; @@ -1209,7 +1211,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n) replay: /* Reinit, just in case something touches this. */ - err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL, NULL); + err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL, extack); if (err < 0) return err; @@ -1567,7 +1569,8 @@ done: -static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n) +static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, + struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); struct tcmsg *tcm = nlmsg_data(n); @@ -1586,7 +1589,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n) !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) return -EPERM; - err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL, NULL); + err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL, extack); if (err < 0) return err; -- cgit From b89f04c61efe3b7756434d693b9203cc0cce002e Mon Sep 17 00:00:00 2001 From: Chonggang Li Date: Sun, 16 Apr 2017 12:02:18 -0700 Subject: bonding: deliver link-local packets with skb->dev set to link that packets arrived on MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bonding driver changes the skb->dev to the bonding-master before passing the packet to stack for further processing. This, however does not make sense for the link-local packets and it loses "the link info" once its skb->dev is changed to bonding-master. This patch changes this behavior for link-local packets by not changing the skb->dev to the bonding-master and maintaining it as it is, i.e. the link on which the packet arrived. Signed-off-by: Chonggang Li Signed-off-by: Mahesh Bandewar Signed-off-by: Maciej Żenczykowski Signed-off-by: Jay Vosburgh Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 01e4a69af421..6bd3b50faf48 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1176,6 +1176,9 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) } } + /* don't change skb->dev for link-local packets */ + if (is_link_local_ether_addr(eth_hdr(skb)->h_dest)) + return RX_HANDLER_PASS; if (bond_should_deliver_exact_match(skb, slave, bond)) return RX_HANDLER_EXACT; -- cgit From 96b08fd6080efdfa8f6125cffc6742a2235d92f1 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Thu, 13 Apr 2017 13:06:27 +0100 Subject: nl80211: add request id in scheduled scan event messages For multi-scheduled scan support in subsequent patch a request id will be added. This patch add this request id to the scheduled scan event messages. For now the request id will always be zero. With multi-scheduled scan its value will inform user-space to which scan the event relates. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ net/wireless/nl80211.c | 23 +++++++++++------------ net/wireless/nl80211.h | 3 +-- net/wireless/scan.c | 5 ++--- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 89fa4995ddca..2a200b964b7a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1649,6 +1649,7 @@ struct cfg80211_bss_select_adjust { /** * struct cfg80211_sched_scan_request - scheduled scan request description * + * @reqid: identifies this request. * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans) * @n_ssids: number of SSIDs * @n_channels: total number of channels to scan @@ -1693,6 +1694,7 @@ struct cfg80211_bss_select_adjust { * comparisions. */ struct cfg80211_sched_scan_request { + u64 reqid; struct cfg80211_ssid *ssids; int n_ssids; u32 n_channels; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 671b635c0625..a047992e5df1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7371,8 +7371,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req); - nl80211_send_sched_scan(rdev, dev, - NL80211_CMD_START_SCHED_SCAN); + nl80211_send_sched_scan(sched_scan_req, NL80211_CMD_START_SCHED_SCAN); return 0; out_free: @@ -13219,18 +13218,19 @@ static int nl80211_prep_scan_msg(struct sk_buff *msg, static int nl80211_prep_sched_scan_msg(struct sk_buff *msg, - struct cfg80211_registered_device *rdev, - struct net_device *netdev, - u32 portid, u32 seq, int flags, u32 cmd) + struct cfg80211_sched_scan_request *req, u32 cmd) { void *hdr; - hdr = nl80211hdr_put(msg, portid, seq, flags, cmd); + hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); if (!hdr) return -1; - if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex)) + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, + wiphy_to_rdev(req->wiphy)->wiphy_idx) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, req->dev->ifindex) || + nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->reqid, + NL80211_ATTR_PAD)) goto nla_put_failure; genlmsg_end(msg, hdr); @@ -13290,8 +13290,7 @@ void nl80211_send_scan_msg(struct cfg80211_registered_device *rdev, NL80211_MCGRP_SCAN, GFP_KERNEL); } -void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 cmd) +void nl80211_send_sched_scan(struct cfg80211_sched_scan_request *req, u32 cmd) { struct sk_buff *msg; @@ -13299,12 +13298,12 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, if (!msg) return; - if (nl80211_prep_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) { + if (nl80211_prep_sched_scan_msg(msg, req, cmd) < 0) { nlmsg_free(msg); return; } - genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, + genlmsg_multicast_netns(&nl80211_fam, wiphy_net(req->wiphy), msg, 0, NL80211_MCGRP_SCAN, GFP_KERNEL); } diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 3cb17cd9577f..d5f6860e62ab 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -16,8 +16,7 @@ struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, bool aborted); void nl80211_send_scan_msg(struct cfg80211_registered_device *rdev, struct sk_buff *msg); -void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 cmd); +void nl80211_send_sched_scan(struct cfg80211_sched_scan_request *req, u32 cmd); void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, struct regulatory_request *request); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 21be56b3128e..6f4996c0f4df 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -321,8 +321,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk) spin_unlock_bh(&rdev->bss_lock); request->scan_start = jiffies; } - nl80211_send_sched_scan(rdev, request->dev, - NL80211_CMD_SCHED_SCAN_RESULTS); + nl80211_send_sched_scan(request, NL80211_CMD_SCHED_SCAN_RESULTS); } rtnl_unlock(); @@ -379,7 +378,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, return err; } - nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); + nl80211_send_sched_scan(sched_scan_req, NL80211_CMD_SCHED_SCAN_STOPPED); RCU_INIT_POINTER(rdev->sched_scan_req, NULL); kfree_rcu(sched_scan_req, rcu_head); -- cgit From f64331d58045b05e5af581284884d5df9b26c031 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Apr 2017 13:28:18 +0200 Subject: mac80211: keep a separate list of monitor interfaces that are up In addition to keeping monitor interfaces on the regular list of interfaces, keep those that are up and not in cooked mode on a separate list. This saves having to iterate all interfaces when delivering to monitor interfaces. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 3 +++ net/mac80211/iface.c | 19 +++++++++++++++++-- net/mac80211/main.c | 1 + net/mac80211/rx.c | 11 +---------- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0e718437d080..cf6d5abb65a3 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -839,6 +839,8 @@ struct txq_info { struct ieee80211_if_mntr { u32 flags; u8 mu_follow_addr[ETH_ALEN] __aligned(2); + + struct list_head list; }; /** @@ -1259,6 +1261,7 @@ struct ieee80211_local { /* see iface.c */ struct list_head interfaces; + struct list_head mon_list; /* only that are IFF_UP && !cooked */ struct mutex iflist_mtx; /* diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 40813dd3301c..02d4d6a29b75 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -676,7 +676,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) set_bit(SDATA_STATE_RUNNING, &sdata->state); - if (sdata->vif.type == NL80211_IFTYPE_WDS) { + switch (sdata->vif.type) { + case NL80211_IFTYPE_WDS: /* Create STA entry for the WDS peer */ sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr, GFP_KERNEL); @@ -697,8 +698,17 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) rate_control_rate_init(sta); netif_carrier_on(dev); - } else if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE) { + break; + case NL80211_IFTYPE_P2P_DEVICE: rcu_assign_pointer(local->p2p_sdata, sdata); + break; + case NL80211_IFTYPE_MONITOR: + if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) + break; + list_add_tail_rcu(&sdata->u.mntr.list, &local->mon_list); + break; + default: + break; } /* @@ -816,6 +826,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP: cancel_work_sync(&sdata->u.ap.request_smps_work); break; + case NL80211_IFTYPE_MONITOR: + if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) + break; + list_del_rcu(&sdata->u.mntr.list); + break; default: break; } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 56fb47953b72..ae408a96c407 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -603,6 +603,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, ARRAY_SIZE(local->ext_capa); INIT_LIST_HEAD(&local->interfaces); + INIT_LIST_HEAD(&local->mon_list); __hw_addr_init(&local->mc_list); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index e35c42ebb7a5..638dc63a51bf 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -593,16 +593,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - if (sdata->vif.type != NL80211_IFTYPE_MONITOR) - continue; - - if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) - continue; - - if (!ieee80211_sdata_running(sdata)) - continue; - + list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) { if (prev_dev) { skb2 = skb_clone(skb, GFP_ATOMIC); if (skb2) { -- cgit From aa1702dd162f420bf85ecef0c77686ef0dbc1496 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Thu, 13 Apr 2017 10:05:04 -0700 Subject: cfg80211: Fix array-bounds warning in fragment copy __ieee80211_amsdu_copy_frag intentionally initializes a pointer to array[-1] to increment it later to valid values. clang rightfully generates an array-bounds warning on the initialization statement. Initialize the pointer to array[0] and change the algorithm from increment before to increment after consume. Signed-off-by: Matthias Kaehlcke Signed-off-by: Johannes Berg --- net/wireless/util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/wireless/util.c b/net/wireless/util.c index 88f3a11dbcd3..a46bc42d0910 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -659,7 +659,7 @@ __ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame, int offset, int len) { struct skb_shared_info *sh = skb_shinfo(skb); - const skb_frag_t *frag = &sh->frags[-1]; + const skb_frag_t *frag = &sh->frags[0]; struct page *frag_page; void *frag_ptr; int frag_len, frag_size; @@ -672,10 +672,10 @@ __ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame, while (offset >= frag_size) { offset -= frag_size; - frag++; frag_page = skb_frag_page(frag); frag_ptr = skb_frag_address(frag); frag_size = skb_frag_size(frag); + frag++; } frag_ptr += offset; @@ -687,12 +687,12 @@ __ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame, len -= cur_len; while (len > 0) { - frag++; frag_len = skb_frag_size(frag); cur_len = min(len, frag_len); __frame_add_frag(frame, skb_frag_page(frag), skb_frag_address(frag), cur_len, frag_len); len -= cur_len; + frag++; } } -- cgit From a4ac6f2e53e568a77a2eb3710efd99ca08634c0a Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Mon, 17 Apr 2017 13:59:53 -0700 Subject: mac80211: ibss: Fix channel type enum in ieee80211_sta_join_ibss() cfg80211_chandef_create() expects an 'enum nl80211_channel_type' as channel type however in ieee80211_sta_join_ibss() NL80211_CHAN_WIDTH_20_NOHT is passed in two occasions, which is of the enum type 'nl80211_chan_width'. Change the value to NL80211_CHAN_NO_HT (20 MHz, non-HT channel) of the channel type enum. Signed-off-by: Matthias Kaehlcke Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 98999d3d5262..e957351976a2 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -425,7 +425,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, case NL80211_CHAN_WIDTH_5: case NL80211_CHAN_WIDTH_10: cfg80211_chandef_create(&chandef, cbss->channel, - NL80211_CHAN_WIDTH_20_NOHT); + NL80211_CHAN_NO_HT); chandef.width = sdata->u.ibss.chandef.width; break; case NL80211_CHAN_WIDTH_80: @@ -437,7 +437,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, default: /* fall back to 20 MHz for unsupported modes */ cfg80211_chandef_create(&chandef, cbss->channel, - NL80211_CHAN_WIDTH_20_NOHT); + NL80211_CHAN_NO_HT); break; } -- cgit From bbf67e450a5dc2a595e1e7a67b4869f1a7f5a338 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Mon, 17 Apr 2017 15:59:52 -0700 Subject: nl80211: Fix enum type of variable in nl80211_put_sta_rate() rate_flg is of type 'enum nl80211_attrs', however it is assigned with 'enum nl80211_rate_info' values. Change the type of rate_flg accordingly. Signed-off-by: Matthias Kaehlcke Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a047992e5df1..04c1c7896cc1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4203,7 +4203,7 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, struct nlattr *rate; u32 bitrate; u16 bitrate_compat; - enum nl80211_attrs rate_flg; + enum nl80211_rate_info rate_flg; rate = nla_nest_start(msg, attr); if (!rate) -- cgit From e4dc99c7c21ba456fd72a70ada5d8d5f3850bcf5 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 15 Apr 2017 22:00:27 +0800 Subject: sctp: process duplicated strreset out and addstrm out requests correctly Now sctp stream reconf will process a request again even if it's seqno is less than asoc->strreset_inseq. If one request has been done successfully and some data chunks have been accepted and then a duplicated strreset out request comes, the streamin's ssn will be cleared. It will cause that stream will never receive chunks any more because of unsynchronized ssn. It allows a replay attack. A similar issue also exists when processing addstrm out requests. It will cause more extra streams being added. This patch is to fix it by saving the last 2 results into asoc. When a duplicated strreset out or addstrm out request is received, reply it with bad seqno if it's seqno < asoc->strreset_inseq - 2, and reply it with the result saved in asoc if it's seqno >= asoc->strreset_inseq - 2. Note that it saves last 2 results instead of only last 1 result, because two requests can be sent together in one chunk. And note that when receiving a duplicated request, the receiver side will still reply it even if the peer has received the response. It's safe, As the response will be dropped by the peer. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 1 + net/sctp/stream.c | 39 +++++++++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index b751399aa6b7..a8b38e123f97 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1889,6 +1889,7 @@ struct sctp_association { __u32 strreset_outseq; /* Update after receiving response */ __u32 strreset_inseq; /* Update after receiving request */ + __u32 strreset_result[2]; /* save the results of last 2 responses */ struct sctp_chunk *strreset_chunk; /* save request chunk */ diff --git a/net/sctp/stream.c b/net/sctp/stream.c index 4ec3679a1cef..6cab7c3721a3 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -344,6 +344,13 @@ static sctp_paramhdr_t *sctp_chunk_lookup_strreset_param( return NULL; } +static void sctp_update_strreset_result(struct sctp_association *asoc, + __u32 result) +{ + asoc->strreset_result[1] = asoc->strreset_result[0]; + asoc->strreset_result[0] = result; +} + struct sctp_chunk *sctp_process_strreset_outreq( struct sctp_association *asoc, union sctp_params param, @@ -360,15 +367,19 @@ struct sctp_chunk *sctp_process_strreset_outreq( if (ntohl(outreq->send_reset_at_tsn) > sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map)) { result = SCTP_STRRESET_IN_PROGRESS; - goto out; + goto err; } - if (request_seq > asoc->strreset_inseq) { + if (TSN_lt(asoc->strreset_inseq, request_seq) || + TSN_lt(request_seq, asoc->strreset_inseq - 2)) { result = SCTP_STRRESET_ERR_BAD_SEQNO; - goto out; - } else if (request_seq == asoc->strreset_inseq) { - asoc->strreset_inseq++; + goto err; + } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { + i = asoc->strreset_inseq - request_seq - 1; + result = asoc->strreset_result[i]; + goto err; } + asoc->strreset_inseq++; /* Check strreset_enable after inseq inc, as sender cannot tell * the peer doesn't enable strreset after receiving response with @@ -427,6 +438,8 @@ struct sctp_chunk *sctp_process_strreset_outreq( GFP_ATOMIC); out: + sctp_update_strreset_result(asoc, result); +err: return sctp_make_strreset_resp(asoc, result, request_seq); } @@ -582,15 +595,19 @@ struct sctp_chunk *sctp_process_strreset_addstrm_out( __u32 result = SCTP_STRRESET_DENIED; struct sctp_stream_in *streamin; __u32 request_seq, incnt; - __u16 in; + __u16 in, i; request_seq = ntohl(addstrm->request_seq); - if (request_seq > asoc->strreset_inseq) { + if (TSN_lt(asoc->strreset_inseq, request_seq) || + TSN_lt(request_seq, asoc->strreset_inseq - 2)) { result = SCTP_STRRESET_ERR_BAD_SEQNO; - goto out; - } else if (request_seq == asoc->strreset_inseq) { - asoc->strreset_inseq++; + goto err; + } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { + i = asoc->strreset_inseq - request_seq - 1; + result = asoc->strreset_result[i]; + goto err; } + asoc->strreset_inseq++; if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) goto out; @@ -638,6 +655,8 @@ struct sctp_chunk *sctp_process_strreset_addstrm_out( 0, ntohs(addstrm->number_of_streams), 0, GFP_ATOMIC); out: + sctp_update_strreset_result(asoc, result); +err: return sctp_make_strreset_resp(asoc, result, request_seq); } -- cgit From d0f025e611581169c81e3c0fc44b5133d12748dd Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 15 Apr 2017 22:00:28 +0800 Subject: sctp: process duplicated strreset in and addstrm in requests correctly This patch is to fix the replay attack issue for strreset and addstrm in requests. When a duplicated strreset in or addstrm in request is received, reply it with bad seqno if it's seqno < asoc->strreset_inseq - 2, and reply it with the result saved in asoc if it's seqno >= asoc->strreset_inseq - 2. For strreset in or addstrm in request, if the receiver side processes it successfully, a strreset out or addstrm out request(as a response for that request) will be sent back to peer. reconf_time will retransmit the out request even if it's lost. So when receiving a duplicated strreset in or addstrm in request and it's result was performed, it shouldn't reply this request, but drop it instead. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- net/sctp/stream.c | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/net/sctp/stream.c b/net/sctp/stream.c index 6cab7c3721a3..c91d97e5d590 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -456,12 +456,18 @@ struct sctp_chunk *sctp_process_strreset_inreq( __u32 request_seq; request_seq = ntohl(inreq->request_seq); - if (request_seq > asoc->strreset_inseq) { + if (TSN_lt(asoc->strreset_inseq, request_seq) || + TSN_lt(request_seq, asoc->strreset_inseq - 2)) { result = SCTP_STRRESET_ERR_BAD_SEQNO; - goto out; - } else if (request_seq == asoc->strreset_inseq) { - asoc->strreset_inseq++; + goto err; + } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { + i = asoc->strreset_inseq - request_seq - 1; + result = asoc->strreset_result[i]; + if (result == SCTP_STRRESET_PERFORMED) + return NULL; + goto err; } + asoc->strreset_inseq++; if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_STREAM_REQ)) goto out; @@ -496,10 +502,14 @@ struct sctp_chunk *sctp_process_strreset_inreq( asoc->strreset_outstanding = 1; sctp_chunk_hold(asoc->strreset_chunk); + result = SCTP_STRRESET_PERFORMED; + *evp = sctp_ulpevent_make_stream_reset_event(asoc, SCTP_STREAM_RESET_INCOMING_SSN, nums, str_p, GFP_ATOMIC); out: + sctp_update_strreset_result(asoc, result); +err: if (!chunk) chunk = sctp_make_strreset_resp(asoc, result, request_seq); @@ -671,15 +681,21 @@ struct sctp_chunk *sctp_process_strreset_addstrm_in( struct sctp_stream_out *streamout; struct sctp_chunk *chunk = NULL; __u32 request_seq, outcnt; - __u16 out; + __u16 out, i; request_seq = ntohl(addstrm->request_seq); - if (request_seq > asoc->strreset_inseq) { + if (TSN_lt(asoc->strreset_inseq, request_seq) || + TSN_lt(request_seq, asoc->strreset_inseq - 2)) { result = SCTP_STRRESET_ERR_BAD_SEQNO; - goto out; - } else if (request_seq == asoc->strreset_inseq) { - asoc->strreset_inseq++; + goto err; + } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { + i = asoc->strreset_inseq - request_seq - 1; + result = asoc->strreset_result[i]; + if (result == SCTP_STRRESET_PERFORMED) + return NULL; + goto err; } + asoc->strreset_inseq++; if (!(asoc->strreset_enable & SCTP_ENABLE_CHANGE_ASSOC_REQ)) goto out; @@ -712,10 +728,14 @@ struct sctp_chunk *sctp_process_strreset_addstrm_in( stream->outcnt = outcnt; + result = SCTP_STRRESET_PERFORMED; + *evp = sctp_ulpevent_make_stream_change_event(asoc, 0, 0, ntohs(addstrm->number_of_streams), GFP_ATOMIC); out: + sctp_update_strreset_result(asoc, result); +err: if (!chunk) chunk = sctp_make_strreset_resp(asoc, result, request_seq); -- cgit From 6c80138773efff75ee9598b4ebcd7aa0e3a5a2a3 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 15 Apr 2017 22:00:29 +0800 Subject: sctp: process duplicated strreset asoc request correctly This patch is to fix the replay attack issue for strreset asoc requests. When a duplicated strreset asoc request is received, reply it with bad seqno if it's seqno < asoc->strreset_inseq - 2, and reply it with the result saved in asoc if it's seqno >= asoc->strreset_inseq - 2. But note that if the result saved in asoc is performed, the sender's next tsn and receiver's next tsn for the response chunk should be set. It's safe to get them from asoc. Because if it's changed, which means the peer has received the response already, the new response with wrong tsn won't be accepted by peer. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- net/sctp/stream.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/net/sctp/stream.c b/net/sctp/stream.c index c91d97e5d590..dda53a293986 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -529,12 +529,21 @@ struct sctp_chunk *sctp_process_strreset_tsnreq( __u16 i; request_seq = ntohl(tsnreq->request_seq); - if (request_seq > asoc->strreset_inseq) { + if (TSN_lt(asoc->strreset_inseq, request_seq) || + TSN_lt(request_seq, asoc->strreset_inseq - 2)) { result = SCTP_STRRESET_ERR_BAD_SEQNO; - goto out; - } else if (request_seq == asoc->strreset_inseq) { - asoc->strreset_inseq++; + goto err; + } else if (TSN_lt(request_seq, asoc->strreset_inseq)) { + i = asoc->strreset_inseq - request_seq - 1; + result = asoc->strreset_result[i]; + if (result == SCTP_STRRESET_PERFORMED) { + next_tsn = asoc->next_tsn; + init_tsn = + sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map) + 1; + } + goto err; } + asoc->strreset_inseq++; if (!(asoc->strreset_enable & SCTP_ENABLE_RESET_ASSOC_REQ)) goto out; @@ -591,6 +600,8 @@ struct sctp_chunk *sctp_process_strreset_tsnreq( next_tsn, GFP_ATOMIC); out: + sctp_update_strreset_result(asoc, result); +err: return sctp_make_strreset_tsnresp(asoc, result, request_seq, next_tsn, init_tsn); } -- cgit From 5f8ddeab10ce45d3d3de8ae7ea8811512845c497 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 16 Apr 2017 02:55:09 +0200 Subject: rhashtable: remove insecure_elasticity commit 83e7e4ce9e93c3 ("mac80211: Use rhltable instead of rhashtable") removed the last user that made use of 'insecure_elasticity' parameter, i.e. the default of 16 is used everywhere. Replace it with a constant. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 21 ++++++++++++++++----- lib/rhashtable.c | 17 +---------------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index e507290cd2c7..ae87dcdf52d2 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -49,6 +49,21 @@ /* Base bits plus 1 bit for nulls marker */ #define RHT_HASH_RESERVED_SPACE (RHT_BASE_BITS + 1) +/* Maximum chain length before rehash + * + * The maximum (not average) chain length grows with the size of the hash + * table, at a rate of (log N)/(log log N). + * + * The value of 16 is selected so that even if the hash table grew to + * 2^32 you would not expect the maximum chain length to exceed it + * unless we are under attack (or extremely unlucky). + * + * As this limit is only to detect attacks, we don't need to set it to a + * lower value as you'd need the chain length to vastly exceed 16 to have + * any real effect on the system. + */ +#define RHT_ELASTICITY 16u + struct rhash_head { struct rhash_head __rcu *next; }; @@ -114,7 +129,6 @@ struct rhashtable; * @max_size: Maximum size while expanding * @min_size: Minimum size while shrinking * @nulls_base: Base value to generate nulls marker - * @insecure_elasticity: Set to true to disable chain length checks * @automatic_shrinking: Enable automatic shrinking of tables * @locks_mul: Number of bucket locks to allocate per cpu (default: 128) * @hashfn: Hash function (default: jhash2 if !(key_len % 4), or jhash) @@ -130,7 +144,6 @@ struct rhashtable_params { unsigned int max_size; unsigned int min_size; u32 nulls_base; - bool insecure_elasticity; bool automatic_shrinking; size_t locks_mul; rht_hashfn_t hashfn; @@ -143,7 +156,6 @@ struct rhashtable_params { * @tbl: Bucket table * @nelems: Number of elements in table * @key_len: Key length for hashfn - * @elasticity: Maximum chain length before rehash * @p: Configuration parameters * @rhlist: True if this is an rhltable * @run_work: Deferred worker to expand/shrink asynchronously @@ -154,7 +166,6 @@ struct rhashtable { struct bucket_table __rcu *tbl; atomic_t nelems; unsigned int key_len; - unsigned int elasticity; struct rhashtable_params p; bool rhlist; struct work_struct run_work; @@ -726,7 +737,7 @@ slow_path: return rhashtable_insert_slow(ht, key, obj); } - elasticity = ht->elasticity; + elasticity = RHT_ELASTICITY; pprev = rht_bucket_insert(ht, tbl, hash); data = ERR_PTR(-ENOMEM); if (!pprev) diff --git a/lib/rhashtable.c b/lib/rhashtable.c index f8635fd57442..d22a5ef109fb 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -535,7 +535,7 @@ static void *rhashtable_lookup_one(struct rhashtable *ht, struct rhash_head *head; int elasticity; - elasticity = ht->elasticity; + elasticity = RHT_ELASTICITY; pprev = rht_bucket_var(tbl, hash); rht_for_each_continue(head, *pprev, tbl, hash) { struct rhlist_head *list; @@ -972,21 +972,6 @@ int rhashtable_init(struct rhashtable *ht, if (params->nelem_hint) size = rounded_hashtable_size(&ht->p); - /* The maximum (not average) chain length grows with the - * size of the hash table, at a rate of (log N)/(log log N). - * The value of 16 is selected so that even if the hash - * table grew to 2^32 you would not expect the maximum - * chain length to exceed it unless we are under attack - * (or extremely unlucky). - * - * As this limit is only to detect attacks, we don't need - * to set it to a lower value as you'd need the chain - * length to vastly exceed 16 to have any real effect - * on the system. - */ - if (!params->insecure_elasticity) - ht->elasticity = 16; - if (params->locks_mul) ht->p.locks_mul = roundup_pow_of_two(params->locks_mul); else -- cgit From 2911063011fc7adcb43c93e9c3e9dc7798f459f5 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 16 Apr 2017 21:23:19 +0200 Subject: net: mvneta: Use devm_kmalloc_array() in mvneta_init() * A multiplication for the size determination of a memory allocation indicated that an array data structure should be processed. Thus use the corresponding function "devm_kmalloc_array". This issue was detected by using the Coccinelle software. * Replace the specification of a data type by a pointer dereference to make the corresponding size determination a bit safer according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 0992db47070f..652e7ab4b708 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -4027,9 +4027,11 @@ static int mvneta_init(struct device *dev, struct mvneta_port *pp) rxq->size = pp->rx_ring_size; rxq->pkts_coal = MVNETA_RX_COAL_PKTS; rxq->time_coal = MVNETA_RX_COAL_USEC; - rxq->buf_virt_addr = devm_kmalloc(pp->dev->dev.parent, - rxq->size * sizeof(void *), - GFP_KERNEL); + rxq->buf_virt_addr + = devm_kmalloc_array(pp->dev->dev.parent, + rxq->size, + sizeof(*rxq->buf_virt_addr), + GFP_KERNEL); if (!rxq->buf_virt_addr) return -ENOMEM; } -- cgit From 5d6312ed57a909c86bb9472b2bbc012539392e7d Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 16 Apr 2017 21:45:38 +0200 Subject: net: mvneta: Improve two size determinations in mvneta_init() Replace the specification of two data structures by pointer dereferences as the parameter for the operator "sizeof" to make the corresponding size determination a bit safer according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 652e7ab4b708..3c4c05b7cc92 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -4002,8 +4002,7 @@ static int mvneta_init(struct device *dev, struct mvneta_port *pp) /* Set port default values */ mvneta_defaults_set(pp); - pp->txqs = devm_kcalloc(dev, txq_number, sizeof(struct mvneta_tx_queue), - GFP_KERNEL); + pp->txqs = devm_kcalloc(dev, txq_number, sizeof(*pp->txqs), GFP_KERNEL); if (!pp->txqs) return -ENOMEM; @@ -4015,8 +4014,7 @@ static int mvneta_init(struct device *dev, struct mvneta_port *pp) txq->done_pkts_coal = MVNETA_TXDONE_COAL_PKTS; } - pp->rxqs = devm_kcalloc(dev, rxq_number, sizeof(struct mvneta_rx_queue), - GFP_KERNEL); + pp->rxqs = devm_kcalloc(dev, rxq_number, sizeof(*pp->rxqs), GFP_KERNEL); if (!pp->rxqs) return -ENOMEM; -- cgit From d441b688a1bce8e2e1b43d8090738c306dd09131 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 16 Apr 2017 22:11:22 +0200 Subject: net: mvneta: Use kmalloc_array() in mvneta_txq_init() A multiplication for the size determination of a memory allocation indicated that an array data structure should be processed. Thus use the corresponding function "kmalloc_array". This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 3c4c05b7cc92..8d2fcf77f6a3 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -2933,7 +2933,8 @@ static int mvneta_txq_init(struct mvneta_port *pp, mvreg_write(pp, MVNETA_TXQ_BASE_ADDR_REG(txq->id), txq->descs_phys); mvreg_write(pp, MVNETA_TXQ_SIZE_REG(txq->id), txq->size); - txq->tx_skb = kmalloc(txq->size * sizeof(*txq->tx_skb), GFP_KERNEL); + txq->tx_skb = kmalloc_array(txq->size, sizeof(*txq->tx_skb), + GFP_KERNEL); if (txq->tx_skb == NULL) { dma_free_coherent(pp->dev->dev.parent, txq->size * MVNETA_DESC_ALIGNED_SIZE, -- cgit From f95936cca6a8410ebdaf164bc5d3ade9e1de5bdb Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 16 Apr 2017 22:45:33 +0200 Subject: net: mvneta: Adjust six checks for null pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The script “checkpatch.pl” pointed information out like the following. Comparison to NULL could be written … Thus fix the affected source code places. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 8d2fcf77f6a3..d297011b535d 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -1107,7 +1107,7 @@ static void mvneta_port_up(struct mvneta_port *pp) q_map = 0; for (queue = 0; queue < txq_number; queue++) { struct mvneta_tx_queue *txq = &pp->txqs[queue]; - if (txq->descs != NULL) + if (txq->descs) q_map |= (1 << queue); } mvreg_write(pp, MVNETA_TXQ_CMD, q_map); @@ -1116,7 +1116,7 @@ static void mvneta_port_up(struct mvneta_port *pp) for (queue = 0; queue < rxq_number; queue++) { struct mvneta_rx_queue *rxq = &pp->rxqs[queue]; - if (rxq->descs != NULL) + if (rxq->descs) q_map |= (1 << queue); } mvreg_write(pp, MVNETA_RXQ_CMD, q_map); @@ -2850,7 +2850,7 @@ static int mvneta_rxq_init(struct mvneta_port *pp, rxq->descs = dma_alloc_coherent(pp->dev->dev.parent, rxq->size * MVNETA_DESC_ALIGNED_SIZE, &rxq->descs_phys, GFP_KERNEL); - if (rxq->descs == NULL) + if (!rxq->descs) return -ENOMEM; rxq->last_desc = rxq->size - 1; @@ -2920,7 +2920,7 @@ static int mvneta_txq_init(struct mvneta_port *pp, txq->descs = dma_alloc_coherent(pp->dev->dev.parent, txq->size * MVNETA_DESC_ALIGNED_SIZE, &txq->descs_phys, GFP_KERNEL); - if (txq->descs == NULL) + if (!txq->descs) return -ENOMEM; txq->last_desc = txq->size - 1; @@ -2935,7 +2935,7 @@ static int mvneta_txq_init(struct mvneta_port *pp, txq->tx_skb = kmalloc_array(txq->size, sizeof(*txq->tx_skb), GFP_KERNEL); - if (txq->tx_skb == NULL) { + if (!txq->tx_skb) { dma_free_coherent(pp->dev->dev.parent, txq->size * MVNETA_DESC_ALIGNED_SIZE, txq->descs, txq->descs_phys); @@ -2946,7 +2946,7 @@ static int mvneta_txq_init(struct mvneta_port *pp, txq->tso_hdrs = dma_alloc_coherent(pp->dev->dev.parent, txq->size * TSO_HEADER_SIZE, &txq->tso_hdrs_phys, GFP_KERNEL); - if (txq->tso_hdrs == NULL) { + if (!txq->tso_hdrs) { kfree(txq->tx_skb); dma_free_coherent(pp->dev->dev.parent, txq->size * MVNETA_DESC_ALIGNED_SIZE, -- cgit From 02c91ece4ee644fbe87933a870c660c0e8413d41 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 08:09:07 +0200 Subject: net: mvpp2: Use kmalloc_array() in mvpp2_txq_init() * A multiplication for the size determination of a memory allocation indicated that an array data structure should be processed. Thus use the corresponding function "kmalloc_array". This issue was detected by using the Coccinelle software. * Replace the specification of a data structure by a pointer dereference to make the corresponding size determination a bit safer according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index af5bfa13d976..a86d9111c476 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -5083,9 +5083,9 @@ static int mvpp2_txq_init(struct mvpp2_port *port, for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); txq_pcpu->size = txq->size; - txq_pcpu->buffs = kmalloc(txq_pcpu->size * - sizeof(struct mvpp2_txq_pcpu_buf), - GFP_KERNEL); + txq_pcpu->buffs = kmalloc_array(txq_pcpu->size, + sizeof(*txq_pcpu->buffs), + GFP_KERNEL); if (!txq_pcpu->buffs) goto error; -- cgit From 0b92e5945bdfed7051e03f2d03f3c5d3555416f3 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 08:38:32 +0200 Subject: net: mvpp2: Improve two size determinations in mvpp2_probe() Replace the specification of two data structures by pointer dereferences as the parameter for the operator "sizeof" to make the corresponding size determination a bit safer according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index a86d9111c476..04aba2307b60 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -6873,7 +6873,7 @@ static int mvpp2_probe(struct platform_device *pdev) int port_count, cpu; int err; - priv = devm_kzalloc(&pdev->dev, sizeof(struct mvpp2), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -6970,8 +6970,8 @@ static int mvpp2_probe(struct platform_device *pdev) } priv->port_list = devm_kcalloc(&pdev->dev, port_count, - sizeof(struct mvpp2_port *), - GFP_KERNEL); + sizeof(*priv->port_list), + GFP_KERNEL); if (!priv->port_list) { err = -ENOMEM; goto err_mg_clk; -- cgit From d7ce3cec279da510fb06c46534e5b2668cd71b84 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 08:48:23 +0200 Subject: net: mvpp2: Improve another size determination in mvpp2_init() Replace the specification of a data structure by a pointer dereference as the parameter for the operator "sizeof" to make the corresponding size determination a bit safer according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 04aba2307b60..e35c4d0b112a 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -6806,7 +6806,7 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) /* Allocate and initialize aggregated TXQs */ priv->aggr_txqs = devm_kcalloc(&pdev->dev, num_present_cpus(), - sizeof(struct mvpp2_tx_queue), + sizeof(*priv->aggr_txqs), GFP_KERNEL); if (!priv->aggr_txqs) return -ENOMEM; -- cgit From 03bfffdef421fd8ff795aa1cf16d1a0d78cb5f0e Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 08:55:42 +0200 Subject: net: mvpp2: Improve another size determination in mvpp2_port_probe() Replace the specification of a data structure by a pointer dereference as the parameter for the operator "sizeof" to make the corresponding size determination a bit safer according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index e35c4d0b112a..8f1fec2986f9 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -6487,8 +6487,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, int phy_mode; int err, i, cpu; - dev = alloc_etherdev_mqs(sizeof(struct mvpp2_port), txq_number, - rxq_number); + dev = alloc_etherdev_mqs(sizeof(*port), txq_number, rxq_number); if (!dev) return -ENOMEM; -- cgit From 81f915eb3ff2908d0f6dc6b0d15e589577214516 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 09:06:33 +0200 Subject: net: mvpp2: Improve another size determination in mvpp2_bm_init() Replace the specification of a data structure by a pointer dereference as the parameter for the operator "sizeof" to make the corresponding size determination a bit safer according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 8f1fec2986f9..8fb039b2073e 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -3833,7 +3833,7 @@ static int mvpp2_bm_init(struct platform_device *pdev, struct mvpp2 *priv) /* Allocate and initialize BM pools */ priv->bm_pools = devm_kcalloc(&pdev->dev, MVPP2_BM_POOLS_NUM, - sizeof(struct mvpp2_bm_pool), GFP_KERNEL); + sizeof(*priv->bm_pools), GFP_KERNEL); if (!priv->bm_pools) return -ENOMEM; -- cgit From 37df25e83b18f20c4152d08777430222a8166125 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 09:12:34 +0200 Subject: net: mvpp2: Improve another size determination in mvpp2_prs_default_init() Replace the specification of a data structure by a pointer dereference as the parameter for the operator "sizeof" to make the corresponding size determination a bit safer according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 8fb039b2073e..abf084b43af7 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -3205,7 +3205,7 @@ static int mvpp2_prs_default_init(struct platform_device *pdev, mvpp2_prs_hw_inv(priv, index); priv->prs_shadow = devm_kcalloc(&pdev->dev, MVPP2_PRS_TCAM_SRAM_SIZE, - sizeof(struct mvpp2_prs_shadow), + sizeof(*priv->prs_shadow), GFP_KERNEL); if (!priv->prs_shadow) return -ENOMEM; -- cgit From c5b2ce24f3d27366135d542a98b596c056cb75f3 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 10:30:29 +0200 Subject: net: mvpp2: Improve 27 size determinations Replace the specification of data structures by references to a local variable as the parameter for the operator "sizeof" to make the corresponding size determination a bit safer. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 54 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index abf084b43af7..61fd87d0a4ec 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -1689,7 +1689,7 @@ static void mvpp2_prs_mac_drop_all_set(struct mvpp2 *priv, int port, bool add) mvpp2_prs_hw_read(priv, &pe); } else { /* Entry doesn't exist - create new */ - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); pe.index = MVPP2_PE_DROP_ALL; @@ -1726,7 +1726,7 @@ static void mvpp2_prs_mac_promisc_set(struct mvpp2 *priv, int port, bool add) mvpp2_prs_hw_read(priv, &pe); } else { /* Entry doesn't exist - create new */ - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); pe.index = MVPP2_PE_MAC_PROMISCUOUS; @@ -1772,7 +1772,7 @@ static void mvpp2_prs_mac_multi_set(struct mvpp2 *priv, int port, int index, mvpp2_prs_hw_read(priv, &pe); } else { /* Entry doesn't exist - create new */ - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); pe.index = index; @@ -1824,7 +1824,7 @@ static void mvpp2_prs_dsa_tag_set(struct mvpp2 *priv, int port, bool add, mvpp2_prs_hw_read(priv, &pe); } else { /* Entry doesn't exist - create new */ - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_DSA); pe.index = tid; @@ -1887,7 +1887,7 @@ static void mvpp2_prs_dsa_tag_ethertype_set(struct mvpp2 *priv, int port, mvpp2_prs_hw_read(priv, &pe); } else { /* Entry doesn't exist - create new */ - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_DSA); pe.index = tid; @@ -2212,7 +2212,7 @@ static int mvpp2_prs_ip4_proto(struct mvpp2 *priv, unsigned short proto, if (tid < 0) return tid; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); pe.index = tid; @@ -2270,7 +2270,7 @@ static int mvpp2_prs_ip4_cast(struct mvpp2 *priv, unsigned short l3_cast) if (tid < 0) return tid; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); pe.index = tid; @@ -2326,7 +2326,7 @@ static int mvpp2_prs_ip6_proto(struct mvpp2 *priv, unsigned short proto, if (tid < 0) return tid; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); pe.index = tid; @@ -2365,7 +2365,7 @@ static int mvpp2_prs_ip6_cast(struct mvpp2 *priv, unsigned short l3_cast) if (tid < 0) return tid; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); pe.index = tid; @@ -2425,7 +2425,7 @@ static void mvpp2_prs_def_flow_init(struct mvpp2 *priv) int port; for (port = 0; port < MVPP2_MAX_PORTS; port++) { - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_FLOWS); pe.index = MVPP2_PE_FIRST_DEFAULT_FLOW - port; @@ -2447,7 +2447,7 @@ static void mvpp2_prs_mh_init(struct mvpp2 *priv) { struct mvpp2_prs_entry pe; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); pe.index = MVPP2_PE_MH_DEFAULT; mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MH); @@ -2470,7 +2470,7 @@ static void mvpp2_prs_mac_init(struct mvpp2 *priv) { struct mvpp2_prs_entry pe; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); /* Non-promiscuous mode for all ports - DROP unknown packets */ pe.index = MVPP2_PE_MAC_NON_PROMISCUOUS; @@ -2531,7 +2531,7 @@ static void mvpp2_prs_dsa_init(struct mvpp2 *priv) MVPP2_PRS_TAGGED, MVPP2_PRS_DSA); /* Set default entry, in case DSA or EDSA tag not found */ - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_DSA); pe.index = MVPP2_PE_DSA_DEFAULT; mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_VLAN); @@ -2561,7 +2561,7 @@ static int mvpp2_prs_etype_init(struct mvpp2 *priv) if (tid < 0) return tid; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); pe.index = tid; @@ -2587,7 +2587,7 @@ static int mvpp2_prs_etype_init(struct mvpp2 *priv) if (tid < 0) return tid; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); pe.index = tid; @@ -2617,7 +2617,7 @@ static int mvpp2_prs_etype_init(struct mvpp2 *priv) if (tid < 0) return tid; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); pe.index = tid; @@ -2651,7 +2651,7 @@ static int mvpp2_prs_etype_init(struct mvpp2 *priv) if (tid < 0) return tid; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); pe.index = tid; @@ -2716,7 +2716,7 @@ static int mvpp2_prs_etype_init(struct mvpp2 *priv) if (tid < 0) return tid; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); pe.index = tid; @@ -2813,7 +2813,7 @@ static int mvpp2_prs_vlan_init(struct platform_device *pdev, struct mvpp2 *priv) return err; /* Set default double vlan entry */ - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VLAN); pe.index = MVPP2_PE_VLAN_DBL; @@ -2833,7 +2833,7 @@ static int mvpp2_prs_vlan_init(struct platform_device *pdev, struct mvpp2 *priv) mvpp2_prs_hw_write(priv, &pe); /* Set default vlan none entry */ - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_VLAN); pe.index = MVPP2_PE_VLAN_NONE; @@ -2863,7 +2863,7 @@ static int mvpp2_prs_pppoe_init(struct mvpp2 *priv) if (tid < 0) return tid; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); pe.index = tid; @@ -2913,7 +2913,7 @@ static int mvpp2_prs_pppoe_init(struct mvpp2 *priv) if (tid < 0) return tid; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); pe.index = tid; @@ -2940,7 +2940,7 @@ static int mvpp2_prs_pppoe_init(struct mvpp2 *priv) if (tid < 0) return tid; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_PPPOE); pe.index = tid; @@ -2998,7 +2998,7 @@ static int mvpp2_prs_ip4_init(struct mvpp2 *priv) return err; /* Default IPv4 entry for unknown protocols */ - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); pe.index = MVPP2_PE_IP4_PROTO_UN; @@ -3023,7 +3023,7 @@ static int mvpp2_prs_ip4_init(struct mvpp2 *priv) mvpp2_prs_hw_write(priv, &pe); /* Default IPv4 entry for unicast address */ - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4); pe.index = MVPP2_PE_IP4_ADDR_UN; @@ -3091,7 +3091,7 @@ static int mvpp2_prs_ip6_init(struct mvpp2 *priv) if (tid < 0) return tid; - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); pe.index = tid; @@ -3112,7 +3112,7 @@ static int mvpp2_prs_ip6_init(struct mvpp2 *priv) mvpp2_prs_hw_write(priv, &pe); /* Default IPv6 entry for unknown protocols */ - memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(&pe, 0, sizeof(pe)); mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP6); pe.index = MVPP2_PE_IP6_PROTO_UN; -- cgit From bd6aaf5535a6782b04e84ea575d65e95d2e7acbc Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 10:40:32 +0200 Subject: net: mvpp2: Improve a size determination in two functions Replace the specification of two data structures by pointer dereferences as the parameter for the operator "sizeof" to make the corresponding size determination a bit safer according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 61fd87d0a4ec..3bddb8fcd595 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -2024,7 +2024,7 @@ static int mvpp2_prs_vlan_add(struct mvpp2 *priv, unsigned short tpid, int ai, goto error; } - memset(pe, 0 , sizeof(struct mvpp2_prs_entry)); + memset(pe, 0, sizeof(*pe)); mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); pe->index = tid; @@ -2165,7 +2165,7 @@ static int mvpp2_prs_double_vlan_add(struct mvpp2 *priv, unsigned short tpid1, goto error; } - memset(pe, 0, sizeof(struct mvpp2_prs_entry)); + memset(pe, 0, sizeof(*pe)); mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN); pe->index = tid; -- cgit From 8a52488bc0b9c33f3520882e1986bce6b9b3af00 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 10:52:02 +0200 Subject: net: mvpp2: Fix a jump label position in mvpp2_rx() The script "checkpatch.pl" pointed out that labels should not be indented. Thus delete two horizontal tabs before the jump label "err_drop_frame" in the function "mvpp2_rx". Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 3bddb8fcd595..4f4b659d7297 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -5515,7 +5515,7 @@ static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, * comprised by the RX descriptor. */ if (rx_status & MVPP2_RXD_ERR_SUMMARY) { - err_drop_frame: +err_drop_frame: dev->stats.rx_errors++; mvpp2_rx_error(port, rx_desc); /* Return the buffer to the pool */ -- cgit From c117554712ce68dfa62f070bdd70b5776d2de52d Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 11:10:47 +0200 Subject: net: mvpp2: Rename a jump label in two functions Adjust jump labels according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 4f4b659d7297..89237abf386d 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -6065,7 +6065,7 @@ static int mvpp2_set_mac_address(struct net_device *dev, void *p) if (!is_valid_ether_addr(addr->sa_data)) { err = -EADDRNOTAVAIL; - goto error; + goto log_error; } if (!netif_running(dev)) { @@ -6075,7 +6075,7 @@ static int mvpp2_set_mac_address(struct net_device *dev, void *p) /* Reconfigure parser to accept the original MAC address */ err = mvpp2_prs_update_mac_da(dev, dev->dev_addr); if (err) - goto error; + goto log_error; } mvpp2_stop_dev(port); @@ -6087,14 +6087,13 @@ static int mvpp2_set_mac_address(struct net_device *dev, void *p) /* Reconfigure parser accept the original MAC address */ err = mvpp2_prs_update_mac_da(dev, dev->dev_addr); if (err) - goto error; + goto log_error; out_start: mvpp2_start_dev(port); mvpp2_egress_enable(port); mvpp2_ingress_enable(port); return 0; - -error: +log_error: netdev_err(dev, "fail to change MAC address\n"); return err; } @@ -6120,7 +6119,7 @@ static int mvpp2_change_mtu(struct net_device *dev, int mtu) /* Reconfigure BM to the original MTU */ err = mvpp2_bm_update_mtu(dev, dev->mtu); if (err) - goto error; + goto log_error; } mvpp2_stop_dev(port); @@ -6134,7 +6133,7 @@ static int mvpp2_change_mtu(struct net_device *dev, int mtu) /* Reconfigure BM to the original MTU */ err = mvpp2_bm_update_mtu(dev, dev->mtu); if (err) - goto error; + goto log_error; out_start: mvpp2_start_dev(port); @@ -6142,8 +6141,7 @@ out_start: mvpp2_ingress_enable(port); return 0; - -error: +log_error: netdev_err(dev, "fail to change MTU\n"); return err; } -- cgit From dfd4240a52e6f0a925f4b9e0c41effba4ad83d1f Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 11:20:41 +0200 Subject: net: mvpp2: Adjust three error messages Use the word "failed" in the string for three function calls. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 89237abf386d..717d79dcb6aa 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -6094,7 +6094,7 @@ out_start: mvpp2_ingress_enable(port); return 0; log_error: - netdev_err(dev, "fail to change MAC address\n"); + netdev_err(dev, "failed to change MAC address\n"); return err; } @@ -6142,7 +6142,7 @@ out_start: return 0; log_error: - netdev_err(dev, "fail to change MTU\n"); + netdev_err(dev, "failed to change MTU\n"); return err; } @@ -6311,7 +6311,7 @@ static int mvpp2_ethtool_set_ringparam(struct net_device *dev, err_clean_rxqs: mvpp2_cleanup_rxqs(port); err_out: - netdev_err(dev, "fail to change ring parameters"); + netdev_err(dev, "failed to change ring parameters"); return err; } -- cgit From 32bae631141be2c4a16e6e8a6412234e6f21b3fc Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 11:36:34 +0200 Subject: net: mvpp2: Rename a jump label in mvpp2_tx_frag_process() Adjust jump labels according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 717d79dcb6aa..8f4348316d28 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -5606,7 +5606,7 @@ static int mvpp2_tx_frag_process(struct mvpp2_port *port, struct sk_buff *skb, DMA_TO_DEVICE); if (dma_mapping_error(port->dev->dev.parent, buf_dma_addr)) { mvpp2_txq_desc_put(txq); - goto error; + goto cleanup; } mvpp2_txdesc_offset_set(port, tx_desc, @@ -5627,8 +5627,7 @@ static int mvpp2_tx_frag_process(struct mvpp2_port *port, struct sk_buff *skb, } return 0; - -error: +cleanup: /* Release all descriptors that were used to map fragments of * this packet, as well as the corresponding DMA mappings */ -- cgit From 20b1e16ef08642245d15b22386a2a02dca9a7384 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 12:58:33 +0200 Subject: net: mvpp2: Rename a jump label in mvpp2_txq_init() Adjust jump labels according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 8f4348316d28..a5cd1036012a 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -5087,7 +5087,7 @@ static int mvpp2_txq_init(struct mvpp2_port *port, sizeof(*txq_pcpu->buffs), GFP_KERNEL); if (!txq_pcpu->buffs) - goto error; + goto cleanup; txq_pcpu->count = 0; txq_pcpu->reserved_num = 0; @@ -5096,8 +5096,7 @@ static int mvpp2_txq_init(struct mvpp2_port *port, } return 0; - -error: +cleanup: for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); kfree(txq_pcpu->buffs); -- cgit From c9a7e1206a36bdf0e9a64cdbb133bbc8b3d66cdb Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 13:03:49 +0200 Subject: net: mvpp2: Rename a jump label in mvpp2_prs_double_vlan_add() Adjust jump labels according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index a5cd1036012a..0c190bd003b1 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -2139,7 +2139,7 @@ static int mvpp2_prs_double_vlan_add(struct mvpp2 *priv, unsigned short tpid1, ai = mvpp2_prs_double_vlan_ai_free_get(priv); if (ai < 0) { ret = ai; - goto error; + goto free_pe; } /* Get first single/triple vlan tid */ @@ -2162,7 +2162,7 @@ static int mvpp2_prs_double_vlan_add(struct mvpp2 *priv, unsigned short tpid1, if (tid >= tid_aux) { ret = -ERANGE; - goto error; + goto free_pe; } memset(pe, 0, sizeof(*pe)); @@ -2189,8 +2189,7 @@ static int mvpp2_prs_double_vlan_add(struct mvpp2 *priv, unsigned short tpid1, /* Update ports' mask */ mvpp2_prs_tcam_port_map_set(pe, port_map); mvpp2_prs_hw_write(priv, pe); - -error: +free_pe: kfree(pe); return ret; } -- cgit From f9fd0e342383033efc8a19deb068c4e30e10bf77 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 13:50:35 +0200 Subject: net: mvpp2: Rename a jump label in mvpp2_prs_vlan_add() Adjust jump labels according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 0c190bd003b1..3bdd3f1fe34e 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -2021,7 +2021,7 @@ static int mvpp2_prs_vlan_add(struct mvpp2 *priv, unsigned short tpid, int ai, if (tid <= tid_aux) { ret = -EINVAL; - goto error; + goto free_pe; } memset(pe, 0, sizeof(*pe)); @@ -2053,8 +2053,7 @@ static int mvpp2_prs_vlan_add(struct mvpp2 *priv, unsigned short tpid, int ai, mvpp2_prs_tcam_port_map_set(pe, port_map); mvpp2_prs_hw_write(priv, pe); - -error: +free_pe: kfree(pe); return ret; -- cgit From dbbb2f034b5a5c1da368e4ac7e85a4ee4922f57b Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 14:07:52 +0200 Subject: net: mvpp2: Adjust a null pointer check in mvpp2_egress_enable() The script "checkpatch.pl" pointed information out like the following. Comparison to NULL could be written "txq->descs". Thus fix the affected source code place. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 3bdd3f1fe34e..9b875d776b29 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -4415,7 +4415,7 @@ static void mvpp2_egress_enable(struct mvpp2_port *port) for (queue = 0; queue < txq_number; queue++) { struct mvpp2_tx_queue *txq = port->txqs[queue]; - if (txq->descs != NULL) + if (txq->descs) qmap |= (1 << queue); } -- cgit From 91acebedbd7a6c76fdc0911366f0ada523fee645 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 14:32:14 +0200 Subject: net: pxa168_eth: Use kcalloc() in two functions Multiplications for the size determination of memory allocations indicated that array data structures should be processed. Thus use the corresponding function "kcalloc". This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/pxa168_eth.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 28cb36d9e50a..2404eac51c63 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -1036,8 +1036,7 @@ static int rxq_init(struct net_device *dev) int rx_desc_num = pep->rx_ring_size; /* Allocate RX skb rings */ - pep->rx_skb = kzalloc(sizeof(*pep->rx_skb) * pep->rx_ring_size, - GFP_KERNEL); + pep->rx_skb = kcalloc(rx_desc_num, sizeof(*pep->rx_skb), GFP_KERNEL); if (!pep->rx_skb) return -ENOMEM; @@ -1096,8 +1095,7 @@ static int txq_init(struct net_device *dev) int size = 0, i = 0; int tx_desc_num = pep->tx_ring_size; - pep->tx_skb = kzalloc(sizeof(*pep->tx_skb) * pep->tx_ring_size, - GFP_KERNEL); + pep->tx_skb = kcalloc(tx_desc_num, sizeof(*pep->tx_skb), GFP_KERNEL); if (!pep->tx_skb) return -ENOMEM; -- cgit From 8273f0a34cb87f0131101bab37655ee8fc05b54c Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 15:23:45 +0200 Subject: net: pxa168_eth: Adjust four checks for null pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The script “checkpatch.pl” pointed information out like the following. Comparison to NULL could be written … Thus fix the affected source code places. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/pxa168_eth.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 2404eac51c63..993724959a7c 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -556,11 +556,11 @@ static int init_hash_table(struct pxa168_eth_private *pep) * function.Driver can dynamically switch to them if the 1/2kB hash * table is full. */ - if (pep->htpr == NULL) { + if (!pep->htpr) { pep->htpr = dma_zalloc_coherent(pep->dev->dev.parent, HASH_ADDR_TABLE_SIZE, &pep->htpr_dma, GFP_KERNEL); - if (pep->htpr == NULL) + if (!pep->htpr) return -ENOMEM; } else { memset(pep->htpr, 0, HASH_ADDR_TABLE_SIZE); @@ -1356,7 +1356,7 @@ static int pxa168_smi_write(struct mii_bus *bus, int phy_addr, int regnum, static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - if (dev->phydev != NULL) + if (dev->phydev) return phy_mii_ioctl(dev->phydev, ifr, cmd); return -EOPNOTSUPP; @@ -1501,7 +1501,7 @@ static int pxa168_eth_probe(struct platform_device *pdev) pep->timeout.data = (unsigned long)pep; pep->smi_bus = mdiobus_alloc(); - if (pep->smi_bus == NULL) { + if (!pep->smi_bus) { err = -ENOMEM; goto err_netdev; } -- cgit From d77566247d62660b74fbd233a00b8aee76f3bfdb Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 15:43:08 +0200 Subject: skge: Use seq_puts() in skge_debug_show() A string which did not contain a data format specification should be put into a sequence. Thus use the corresponding function "seq_puts". This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/skge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index edb95271a4f2..90bfdbcfd910 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -3718,7 +3718,7 @@ static int skge_debug_show(struct seq_file *seq, void *v) t->csum_offs, t->csum_write, t->csum_start); } - seq_printf(seq, "\nRx Ring:\n"); + seq_puts(seq, "\nRx Ring:\n"); for (e = skge->rx_ring.to_clean; ; e = e->next) { const struct skge_rx_desc *r = e->desc; -- cgit From ca735bd83f2d076a0a9ad8aeeb054737e3e0063f Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 16:08:39 +0200 Subject: skge: Adjust a null pointer check in skge_down() The script "checkpatch.pl" pointed information out like the following. Comparison to NULL could be written "!skge->mem". Thus fix the affected source code place. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/skge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index 90bfdbcfd910..5d7d94de4e00 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -2657,7 +2657,7 @@ static int skge_down(struct net_device *dev) struct skge_hw *hw = skge->hw; int port = skge->port; - if (skge->mem == NULL) + if (!skge->mem) return 0; netif_info(skge, ifdown, skge->netdev, "disabling interface\n"); -- cgit From a0c51cf10720348001bc5fbcd1b521e8622efda1 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 17 Apr 2017 16:15:12 +0200 Subject: sky2: Use seq_puts() in sky2_debug_show() A string which did not contain a data format specification should be put into a sequence. Thus use the corresponding function "seq_puts". This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/sky2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index 2b2cc3f3ca10..1145cde2274a 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -4544,7 +4544,7 @@ static int sky2_debug_show(struct seq_file *seq, void *v) sky2_read32(hw, B0_Y2_SP_ICR)); if (!netif_running(dev)) { - seq_printf(seq, "network not running\n"); + seq_puts(seq, "network not running\n"); return 0; } -- cgit From e98233a6192d75d695b0972dc955d6561e46964f Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 18 Apr 2017 08:36:58 +1000 Subject: ftgmac100: Add ethtool n-way reset call A non-wired up implementation accidentally made its way in a previous patch (Make ring sizes configurable via ethtool). This removes it and wires up the generic phy_ethtool_nway_reset instead. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 2153c5bbdd12..4cdd25a5742d 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1045,13 +1045,6 @@ static void ftgmac100_get_drvinfo(struct net_device *netdev, strlcpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info)); } -static int ftgmac100_nway_reset(struct net_device *ndev) -{ - if (!ndev->phydev) - return -ENXIO; - return phy_start_aneg(ndev->phydev); -} - static void ftgmac100_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ering) { @@ -1090,6 +1083,7 @@ static const struct ethtool_ops ftgmac100_ethtool_ops = { .get_link = ethtool_op_get_link, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, + .nway_reset = phy_ethtool_nway_reset, .get_ringparam = ftgmac100_get_ringparam, .set_ringparam = ftgmac100_set_ringparam, }; -- cgit From 7c8e5141ca633ae6dc7489dc85cabcfed2144a2d Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 18 Apr 2017 08:36:59 +1000 Subject: ftgmac100: Add pause frames configuration and support Hopefully my understanding of how the hardware works is correct, as the documentation isn't completely clear. So far I have seen no obvious issue. Pause seem to also work with NC-SI. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 96 +++++++++++++++++++++++++++++++- drivers/net/ethernet/faraday/ftgmac100.h | 7 +++ 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 4cdd25a5742d..949b48c0eae6 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -99,6 +99,11 @@ struct ftgmac100 { int cur_duplex; bool use_ncsi; + /* Flow control settings */ + bool tx_pause; + bool rx_pause; + bool aneg_pause; + /* Misc */ bool need_mac_restart; bool is_aspeed; @@ -219,6 +224,23 @@ static int ftgmac100_set_mac_addr(struct net_device *dev, void *p) return 0; } +static void ftgmac100_config_pause(struct ftgmac100 *priv) +{ + u32 fcr = FTGMAC100_FCR_PAUSE_TIME(16); + + /* Throttle tx queue when receiving pause frames */ + if (priv->rx_pause) + fcr |= FTGMAC100_FCR_FC_EN; + + /* Enables sending pause frames when the RX queue is past a + * certain threshold. + */ + if (priv->tx_pause) + fcr |= FTGMAC100_FCR_FCTHR_EN; + + iowrite32(fcr, priv->base + FTGMAC100_OFFSET_FCR); +} + static void ftgmac100_init_hw(struct ftgmac100 *priv) { u32 reg, rfifo_sz, tfifo_sz; @@ -912,6 +934,7 @@ static void ftgmac100_adjust_link(struct net_device *netdev) { struct ftgmac100 *priv = netdev_priv(netdev); struct phy_device *phydev = netdev->phydev; + bool tx_pause, rx_pause; int new_speed; /* We store "no link" as speed 0 */ @@ -920,8 +943,21 @@ static void ftgmac100_adjust_link(struct net_device *netdev) else new_speed = phydev->speed; + /* Grab pause settings from PHY if configured to do so */ + if (priv->aneg_pause) { + rx_pause = tx_pause = phydev->pause; + if (phydev->asym_pause) + tx_pause = !rx_pause; + } else { + rx_pause = priv->rx_pause; + tx_pause = priv->tx_pause; + } + + /* Link hasn't changed, do nothing */ if (phydev->speed == priv->cur_speed && - phydev->duplex == priv->cur_duplex) + phydev->duplex == priv->cur_duplex && + rx_pause == priv->rx_pause && + tx_pause == priv->tx_pause) return; /* Print status if we have a link or we had one and just lost it, @@ -932,6 +968,8 @@ static void ftgmac100_adjust_link(struct net_device *netdev) priv->cur_speed = new_speed; priv->cur_duplex = phydev->duplex; + priv->rx_pause = rx_pause; + priv->tx_pause = tx_pause; /* Link is down, do nothing else */ if (!new_speed) @@ -963,6 +1001,12 @@ static int ftgmac100_mii_probe(struct ftgmac100 *priv) return PTR_ERR(phydev); } + /* Indicate that we support PAUSE frames (see comment in + * Documentation/networking/phy.txt) + */ + phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + phydev->advertising = phydev->supported; + return 0; } @@ -1078,6 +1122,48 @@ static int ftgmac100_set_ringparam(struct net_device *netdev, return 0; } +static void ftgmac100_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + + pause->autoneg = priv->aneg_pause; + pause->tx_pause = priv->tx_pause; + pause->rx_pause = priv->rx_pause; +} + +static int ftgmac100_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + struct phy_device *phydev = netdev->phydev; + + priv->aneg_pause = pause->autoneg; + priv->tx_pause = pause->tx_pause; + priv->rx_pause = pause->rx_pause; + + if (phydev) { + phydev->advertising &= ~ADVERTISED_Pause; + phydev->advertising &= ~ADVERTISED_Asym_Pause; + + if (pause->rx_pause) { + phydev->advertising |= ADVERTISED_Pause; + phydev->advertising |= ADVERTISED_Asym_Pause; + } + + if (pause->tx_pause) + phydev->advertising ^= ADVERTISED_Asym_Pause; + } + if (netif_running(netdev)) { + if (phydev && priv->aneg_pause) + phy_start_aneg(phydev); + else + ftgmac100_config_pause(priv); + } + + return 0; +} + static const struct ethtool_ops ftgmac100_ethtool_ops = { .get_drvinfo = ftgmac100_get_drvinfo, .get_link = ethtool_op_get_link, @@ -1086,6 +1172,8 @@ static const struct ethtool_ops ftgmac100_ethtool_ops = { .nway_reset = phy_ethtool_nway_reset, .get_ringparam = ftgmac100_get_ringparam, .set_ringparam = ftgmac100_set_ringparam, + .get_pauseparam = ftgmac100_get_pauseparam, + .set_pauseparam = ftgmac100_set_pauseparam, }; static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) @@ -1217,6 +1305,7 @@ static int ftgmac100_init_all(struct ftgmac100 *priv, bool ignore_alloc_err) /* Reinit and restart HW */ ftgmac100_init_hw(priv); + ftgmac100_config_pause(priv); ftgmac100_start_hw(priv); /* Re-enable the device */ @@ -1546,6 +1635,11 @@ static int ftgmac100_probe(struct platform_device *pdev) netdev->irq = irq; + /* Enable pause */ + priv->tx_pause = true; + priv->rx_pause = true; + priv->aneg_pause = true; + /* MAC address from chip or random one */ ftgmac100_initial_mac(priv); diff --git a/drivers/net/ethernet/faraday/ftgmac100.h b/drivers/net/ethernet/faraday/ftgmac100.h index 97912c456e80..0653d8176e6a 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.h +++ b/drivers/net/ethernet/faraday/ftgmac100.h @@ -198,6 +198,13 @@ #define FTGMAC100_PHYDATA_MIIWDATA(x) ((x) & 0xffff) #define FTGMAC100_PHYDATA_MIIRDATA(phydata) (((phydata) >> 16) & 0xffff) +/* + * Flow control register + */ +#define FTGMAC100_FCR_FC_EN (1 << 0) +#define FTGMAC100_FCR_FCTHR_EN (1 << 2) +#define FTGMAC100_FCR_PAUSE_TIME(x) (((x) & 0xffff) << 16) + /* * Transmit descriptor, aligned to 16 bytes */ -- cgit From f48b3c0d5b6ab4d88db75e45e1ea38074e2765c9 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 18 Apr 2017 08:37:00 +1000 Subject: ftgmac100: Add ndo_set_rx_mode() and support for multicast & promisc This adds the ndo_set_rx_mode() callback to configure the multicast filters, promisc and allmulti options. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 52 ++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 949b48c0eae6..f4db6e21f5df 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -99,6 +100,10 @@ struct ftgmac100 { int cur_duplex; bool use_ncsi; + /* Multicast filter settings */ + u32 maht0; + u32 maht1; + /* Flow control settings */ bool tx_pause; bool rx_pause; @@ -266,6 +271,10 @@ static void ftgmac100_init_hw(struct ftgmac100 *priv) /* Write MAC address */ ftgmac100_write_mac_addr(priv, priv->netdev->dev_addr); + /* Write multicast filter */ + iowrite32(priv->maht0, priv->base + FTGMAC100_OFFSET_MAHT0); + iowrite32(priv->maht1, priv->base + FTGMAC100_OFFSET_MAHT1); + /* Configure descriptor sizes and increase burst sizes according * to values in Aspeed SDK. The FIFO arbitration is enabled and * the thresholds set based on the recommended values in the @@ -319,6 +328,12 @@ static void ftgmac100_start_hw(struct ftgmac100 *priv) /* Add other bits as needed */ if (priv->cur_duplex == DUPLEX_FULL) maccr |= FTGMAC100_MACCR_FULLDUP; + if (priv->netdev->flags & IFF_PROMISC) + maccr |= FTGMAC100_MACCR_RX_ALL; + if (priv->netdev->flags & IFF_ALLMULTI) + maccr |= FTGMAC100_MACCR_RX_MULTIPKT; + else if (netdev_mc_count(priv->netdev)) + maccr |= FTGMAC100_MACCR_HT_MULTI_EN; /* Hit the HW */ iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR); @@ -329,6 +344,42 @@ static void ftgmac100_stop_hw(struct ftgmac100 *priv) iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR); } +static void ftgmac100_calc_mc_hash(struct ftgmac100 *priv) +{ + struct netdev_hw_addr *ha; + + priv->maht1 = 0; + priv->maht0 = 0; + netdev_for_each_mc_addr(ha, priv->netdev) { + u32 crc_val = ether_crc_le(ETH_ALEN, ha->addr); + + crc_val = (~(crc_val >> 2)) & 0x3f; + if (crc_val >= 32) + priv->maht1 |= 1ul << (crc_val - 32); + else + priv->maht0 |= 1ul << (crc_val); + } +} + +static void ftgmac100_set_rx_mode(struct net_device *netdev) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + + /* Setup the hash filter */ + ftgmac100_calc_mc_hash(priv); + + /* Interface down ? that's all there is to do */ + if (!netif_running(netdev)) + return; + + /* Update the HW */ + iowrite32(priv->maht0, priv->base + FTGMAC100_OFFSET_MAHT0); + iowrite32(priv->maht1, priv->base + FTGMAC100_OFFSET_MAHT1); + + /* Reconfigure MACCR */ + ftgmac100_start_hw(priv); +} + static int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv, unsigned int entry, struct ftgmac100_rxdes *rxdes, gfp_t gfp) { @@ -1503,6 +1554,7 @@ static const struct net_device_ops ftgmac100_netdev_ops = { .ndo_validate_addr = eth_validate_addr, .ndo_do_ioctl = ftgmac100_do_ioctl, .ndo_tx_timeout = ftgmac100_tx_timeout, + .ndo_set_rx_mode = ftgmac100_set_rx_mode, }; static int ftgmac100_setup_mdio(struct net_device *netdev) -- cgit From 0fb9968876c386697170bc468633f4a9e2041a91 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 18 Apr 2017 08:37:01 +1000 Subject: ftgmac100: Add vlan HW offload The chip supports HW vlan tag insertion and extraction. Add support for it. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 46 +++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index f4db6e21f5df..40a03d53432c 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -335,6 +336,10 @@ static void ftgmac100_start_hw(struct ftgmac100 *priv) else if (netdev_mc_count(priv->netdev)) maccr |= FTGMAC100_MACCR_HT_MULTI_EN; + /* Vlan filtering enabled */ + if (priv->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) + maccr |= FTGMAC100_MACCR_RM_VLAN; + /* Hit the HW */ iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR); } @@ -530,6 +535,12 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) /* Transfer received size to skb */ skb_put(skb, size); + /* Extract vlan tag */ + if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && + (csum_vlan & FTGMAC100_RXDES1_VLANTAG_AVAIL)) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + csum_vlan & 0xffff); + /* Tear down DMA mapping, do necessary cache management */ map = le32_to_cpu(rxdes->rxdes3); @@ -754,6 +765,13 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb, if (skb->ip_summed == CHECKSUM_PARTIAL && !ftgmac100_prep_tx_csum(skb, &csum_vlan)) goto drop; + + /* Add VLAN tag */ + if (skb_vlan_tag_present(skb)) { + csum_vlan |= FTGMAC100_TXDES1_INS_VLANTAG; + csum_vlan |= skb_vlan_tag_get(skb) & 0xffff; + } + txdes->txdes1 = cpu_to_le32(csum_vlan); /* Next descriptor */ @@ -1546,6 +1564,30 @@ static void ftgmac100_tx_timeout(struct net_device *netdev) schedule_work(&priv->reset_task); } +static int ftgmac100_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + netdev_features_t changed = netdev->features ^ features; + + if (!netif_running(netdev)) + return 0; + + /* Update the vlan filtering bit */ + if (changed & NETIF_F_HW_VLAN_CTAG_RX) { + u32 maccr; + + maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR); + if (priv->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) + maccr |= FTGMAC100_MACCR_RM_VLAN; + else + maccr &= ~FTGMAC100_MACCR_RM_VLAN; + iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR); + } + + return 0; +} + static const struct net_device_ops ftgmac100_netdev_ops = { .ndo_open = ftgmac100_open, .ndo_stop = ftgmac100_stop, @@ -1555,6 +1597,7 @@ static const struct net_device_ops ftgmac100_netdev_ops = { .ndo_do_ioctl = ftgmac100_do_ioctl, .ndo_tx_timeout = ftgmac100_tx_timeout, .ndo_set_rx_mode = ftgmac100_set_rx_mode, + .ndo_set_features = ftgmac100_set_features, }; static int ftgmac100_setup_mdio(struct net_device *netdev) @@ -1730,7 +1773,8 @@ static int ftgmac100_probe(struct platform_device *pdev) /* Base feature set */ netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM | - NETIF_F_GRO | NETIF_F_SG; + NETIF_F_GRO | NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_HW_VLAN_CTAG_TX; /* AST2400 doesn't have working HW checksum generation */ if (np && (of_device_is_compatible(np, "aspeed,ast2400-mac"))) -- cgit From 030d9828703ec7ecc09d09d39fb68b72f45b05e7 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 18 Apr 2017 08:37:02 +1000 Subject: ftgmac100: Add netpoll support Just call the interrupt handler with interrupts locally disabled Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 40a03d53432c..f76765e0b243 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1588,6 +1588,17 @@ static int ftgmac100_set_features(struct net_device *netdev, return 0; } +#ifdef CONFIG_NET_POLL_CONTROLLER +static void ftgmac100_poll_controller(struct net_device *netdev) +{ + unsigned long flags; + + local_irq_save(flags); + ftgmac100_interrupt(netdev->irq, netdev); + local_irq_restore(flags); +} +#endif + static const struct net_device_ops ftgmac100_netdev_ops = { .ndo_open = ftgmac100_open, .ndo_stop = ftgmac100_stop, @@ -1598,6 +1609,9 @@ static const struct net_device_ops ftgmac100_netdev_ops = { .ndo_tx_timeout = ftgmac100_tx_timeout, .ndo_set_rx_mode = ftgmac100_set_rx_mode, .ndo_set_features = ftgmac100_set_features, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = ftgmac100_poll_controller, +#endif }; static int ftgmac100_setup_mdio(struct net_device *netdev) -- cgit From abcc3eb00e10af3a0c5df90ce515ebde5fc12319 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 18 Apr 2017 08:37:03 +1000 Subject: ftgmac100: Allow configuration of phy interface via device-tree This uses the standard phy-mode property Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 42 +++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index f76765e0b243..7721c2ab24ef 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -1051,7 +1052,7 @@ static void ftgmac100_adjust_link(struct net_device *netdev) schedule_work(&priv->reset_task); } -static int ftgmac100_mii_probe(struct ftgmac100 *priv) +static int ftgmac100_mii_probe(struct ftgmac100 *priv, phy_interface_t intf) { struct net_device *netdev = priv->netdev; struct phy_device *phydev; @@ -1063,7 +1064,7 @@ static int ftgmac100_mii_probe(struct ftgmac100 *priv) } phydev = phy_connect(netdev, phydev_name(phydev), - &ftgmac100_adjust_link, PHY_INTERFACE_MODE_GMII); + &ftgmac100_adjust_link, intf); if (IS_ERR(phydev)) { netdev_err(netdev, "%s: Could not attach to PHY\n", netdev->name); @@ -1618,6 +1619,8 @@ static int ftgmac100_setup_mdio(struct net_device *netdev) { struct ftgmac100 *priv = netdev_priv(netdev); struct platform_device *pdev = to_platform_device(priv->dev); + int phy_intf = PHY_INTERFACE_MODE_RGMII; + struct device_node *np = pdev->dev.of_node; int i, err = 0; u32 reg; @@ -1633,6 +1636,39 @@ static int ftgmac100_setup_mdio(struct net_device *netdev) iowrite32(reg, priv->base + FTGMAC100_OFFSET_REVR); }; + /* Get PHY mode from device-tree */ + if (np) { + /* Default to RGMII. It's a gigabit part after all */ + phy_intf = of_get_phy_mode(np); + if (phy_intf < 0) + phy_intf = PHY_INTERFACE_MODE_RGMII; + + /* Aspeed only supports these. I don't know about other IP + * block vendors so I'm going to just let them through for + * now. Note that this is only a warning if for some obscure + * reason the DT really means to lie about it or it's a newer + * part we don't know about. + * + * On the Aspeed SoC there are additionally straps and SCU + * control bits that could tell us what the interface is + * (or allow us to configure it while the IP block is held + * in reset). For now I chose to keep this driver away from + * those SoC specific bits and assume the device-tree is + * right and the SCU has been configured properly by pinmux + * or the firmware. + */ + if (priv->is_aspeed && + phy_intf != PHY_INTERFACE_MODE_RMII && + phy_intf != PHY_INTERFACE_MODE_RGMII && + phy_intf != PHY_INTERFACE_MODE_RGMII_ID && + phy_intf != PHY_INTERFACE_MODE_RGMII_RXID && + phy_intf != PHY_INTERFACE_MODE_RGMII_TXID) { + netdev_warn(netdev, + "Unsupported PHY mode %s !\n", + phy_modes(phy_intf)); + } + } + priv->mii_bus->name = "ftgmac100_mdio"; snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id); @@ -1649,7 +1685,7 @@ static int ftgmac100_setup_mdio(struct net_device *netdev) goto err_register_mdiobus; } - err = ftgmac100_mii_probe(priv); + err = ftgmac100_mii_probe(priv, phy_intf); if (err) { dev_err(priv->dev, "MII Probe failed!\n"); goto err_mii_probe; -- cgit From 33de693248b45640c0d894c2454a9909f2e81ffd Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 18 Apr 2017 08:37:04 +1000 Subject: ftgmac100: Display the discovered PHY device info Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 7721c2ab24ef..45b8267b81b7 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1077,6 +1077,9 @@ static int ftgmac100_mii_probe(struct ftgmac100 *priv, phy_interface_t intf) phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; phydev->advertising = phydev->supported; + /* Display what we found */ + phy_attached_info(phydev); + return 0; } -- cgit From ccaf725a1fd7904ed3e771ed178c2aa1ccb21509 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 18 Apr 2017 08:37:05 +1000 Subject: ftgmac100: Fix potential ordering issue in NAPI poll We need to ensure the loads from the descriptor are done after the MMIO store clearing the interrupts has completed, otherwise we might still miss work. A read back from the MMIO register will "push" the posted store and ioread32 has a barrier on weakly aordered architectures that will order subsequent accesses. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/faraday/ftgmac100.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 45b8267b81b7..95bf5e89cfd1 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1349,6 +1349,13 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget) */ iowrite32(FTGMAC100_INT_RXTX, priv->base + FTGMAC100_OFFSET_ISR); + + /* Push the above (and provides a barrier vs. subsequent + * reads of the descriptor). + */ + ioread32(priv->base + FTGMAC100_OFFSET_ISR); + + /* Check RX and TX descriptors for more work to do */ if (ftgmac100_check_rx(priv) || ftgmac100_tx_buf_cleanable(priv)) return budget; -- cgit From 88a286f7e9df643e211debe55a7aa206d2332763 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 18 Apr 2017 08:37:06 +1000 Subject: ftgmac100: Document device-tree binding Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- .../devicetree/bindings/net/ftgmac100.txt | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/ftgmac100.txt diff --git a/Documentation/devicetree/bindings/net/ftgmac100.txt b/Documentation/devicetree/bindings/net/ftgmac100.txt new file mode 100644 index 000000000000..c1ce1680246f --- /dev/null +++ b/Documentation/devicetree/bindings/net/ftgmac100.txt @@ -0,0 +1,35 @@ +* Faraday Technology FTGMAC100 gigabit ethernet controller + +Required properties: +- compatible: "faraday,ftgmac100" + + Must also contain one of these if used as part of an Aspeed AST2400 + or 2500 family SoC as they have some subtle tweaks to the + implementation: + + - "aspeed,ast2400-mac" + - "aspeed,ast2500-mac" + +- reg: Address and length of the register set for the device +- interrupts: Should contain ethernet controller interrupt + +Optional properties: +- phy-mode: See ethernet.txt file in the same directory. If the property is + absent, "rgmii" is assumed. Supported values are "rgmii*" and "rmii" for + aspeed parts. Other (unknown) parts will accept any value. +- use-ncsi: Use the NC-SI stack instead of an MDIO PHY. Currently assumes + rmii (100bT) but kept as a separate property in case NC-SI grows support + for a gigabit link. +- no-hw-checksum: Used to disable HW checksum support. Here for backward + compatibility as the driver now should have correct defaults based on + the SoC. + +Example: + + mac0: ethernet@1e660000 { + compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; + reg = <0x1e660000 0x180>; + interrupts = <2>; + status = "okay"; + use-ncsi; + }; -- cgit From b5d8acbb8781269cd4e2b986c9b0b884c0ed836a Mon Sep 17 00:00:00 2001 From: Usha Ketineni Date: Fri, 23 Dec 2016 10:08:14 -0800 Subject: ixgbe: Avoid Tx hang by not allowing more than the number of VFs supported. When DCB is enabled, add checks to ensure creation of number of VF's is valid based on the traffic classes configured by the device. Signed-off-by: Usha Ketineni Tested-by: Ronald Bynoe Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 26 +++++++++++++++++++++++--- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h | 3 +++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 044cb44747cf..39e109da9bd9 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -298,6 +298,7 @@ static int ixgbe_pci_sriov_enable(struct pci_dev *dev, int num_vfs) #ifdef CONFIG_PCI_IOV struct ixgbe_adapter *adapter = pci_get_drvdata(dev); int err = 0; + u8 num_tc; int i; int pre_existing_vfs = pci_num_vf(dev); @@ -310,16 +311,35 @@ static int ixgbe_pci_sriov_enable(struct pci_dev *dev, int num_vfs) return err; /* While the SR-IOV capability structure reports total VFs to be 64, - * we have to limit the actual number allocated based on two factors. + * we limit the actual number allocated as below based on two factors. + * Num_TCs MAX_VFs + * 1 63 + * <=4 31 + * >4 15 * First, we reserve some transmit/receive resources for the PF. * Second, VMDQ also uses the same pools that SR-IOV does. We need to * account for this, so that we don't accidentally allocate more VFs * than we have available pools. The PCI bus driver already checks for * other values out of range. */ - if ((num_vfs + adapter->num_rx_pools) > IXGBE_MAX_VF_FUNCTIONS) - return -EPERM; + num_tc = netdev_get_num_tc(adapter->netdev); + if (num_tc > 4) { + if ((num_vfs + adapter->num_rx_pools) > IXGBE_MAX_VFS_8TC) { + e_dev_err("Currently the device is configured with %d TCs, Creating more than %d VFs is not allowed\n", num_tc, IXGBE_MAX_VFS_8TC); + return -EPERM; + } + } else if ((num_tc > 1) && (num_tc <= 4)) { + if ((num_vfs + adapter->num_rx_pools) > IXGBE_MAX_VFS_4TC) { + e_dev_err("Currently the device is configured with %d TCs, Creating more than %d VFs is not allowed\n", num_tc, IXGBE_MAX_VFS_4TC); + return -EPERM; + } + } else { + if ((num_vfs + adapter->num_rx_pools) > IXGBE_MAX_VFS_1TC) { + e_dev_err("Currently the device is configured with %d TCs, Creating more than %d VFs is not allowed\n", num_tc, IXGBE_MAX_VFS_1TC); + return -EPERM; + } + } adapter->num_vfs = num_vfs; err = __ixgbe_enable_sriov(adapter); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h index 0c7977d27b71..3166fd164e51 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h @@ -33,6 +33,9 @@ * 63 (IXGBE_MAX_VF_FUNCTIONS - 1) */ #define IXGBE_MAX_VFS_DRV_LIMIT (IXGBE_MAX_VF_FUNCTIONS - 1) +#define IXGBE_MAX_VFS_1TC IXGBE_MAX_VF_FUNCTIONS +#define IXGBE_MAX_VFS_4TC 32 +#define IXGBE_MAX_VFS_8TC 16 #ifdef CONFIG_PCI_IOV void ixgbe_restore_vf_multicasts(struct ixgbe_adapter *adapter); -- cgit From e05ddafd896fccb4568db01fe4de812ca1e6f353 Mon Sep 17 00:00:00 2001 From: Iyappan Subramanian Date: Mon, 17 Apr 2017 16:47:55 -0700 Subject: drivers: net: xgene-v2: Extend ethtool statistics This patch adds extended statistics reporting to ethtool. In summary, this patch, - adds ethtool.h with the statistics register definitions - adds 'struct xge_gstrings_extd_stats' to gather extended stats - modifies xge_get_strings(), get_sset_count() and get_ethtool_stats() accordingly - moves 'struct xge_gstrings_stats' to ethtool.h Signed-off-by: Iyappan Subramanian Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene-v2/ethtool.c | 78 ++++++++++++++++++++++++++--- drivers/net/ethernet/apm/xgene-v2/ethtool.h | 78 +++++++++++++++++++++++++++++ drivers/net/ethernet/apm/xgene-v2/mac.h | 3 -- drivers/net/ethernet/apm/xgene-v2/main.h | 2 +- 4 files changed, 151 insertions(+), 10 deletions(-) create mode 100644 drivers/net/ethernet/apm/xgene-v2/ethtool.h diff --git a/drivers/net/ethernet/apm/xgene-v2/ethtool.c b/drivers/net/ethernet/apm/xgene-v2/ethtool.c index 0c426f55ffdb..b6666e418e79 100644 --- a/drivers/net/ethernet/apm/xgene-v2/ethtool.c +++ b/drivers/net/ethernet/apm/xgene-v2/ethtool.c @@ -21,12 +21,13 @@ #include "main.h" -struct xge_gstrings_stats { - char name[ETH_GSTRING_LEN]; - int offset; -}; - #define XGE_STAT(m) { #m, offsetof(struct xge_pdata, stats.m) } +#define XGE_EXTD_STAT(m, n) \ + { \ + #m, \ + n, \ + 0 \ + } static const struct xge_gstrings_stats gstrings_stats[] = { XGE_STAT(rx_packets), @@ -36,7 +37,62 @@ static const struct xge_gstrings_stats gstrings_stats[] = { XGE_STAT(rx_errors) }; +static struct xge_gstrings_extd_stats gstrings_extd_stats[] = { + XGE_EXTD_STAT(tx_rx_64b_frame_cntr, TR64), + XGE_EXTD_STAT(tx_rx_127b_frame_cntr, TR127), + XGE_EXTD_STAT(tx_rx_255b_frame_cntr, TR255), + XGE_EXTD_STAT(tx_rx_511b_frame_cntr, TR511), + XGE_EXTD_STAT(tx_rx_1023b_frame_cntr, TR1K), + XGE_EXTD_STAT(tx_rx_1518b_frame_cntr, TRMAX), + XGE_EXTD_STAT(tx_rx_1522b_frame_cntr, TRMGV), + XGE_EXTD_STAT(rx_fcs_error_cntr, RFCS), + XGE_EXTD_STAT(rx_multicast_pkt_cntr, RMCA), + XGE_EXTD_STAT(rx_broadcast_pkt_cntr, RBCA), + XGE_EXTD_STAT(rx_ctrl_frame_pkt_cntr, RXCF), + XGE_EXTD_STAT(rx_pause_frame_pkt_cntr, RXPF), + XGE_EXTD_STAT(rx_unk_opcode_cntr, RXUO), + XGE_EXTD_STAT(rx_align_err_cntr, RALN), + XGE_EXTD_STAT(rx_frame_len_err_cntr, RFLR), + XGE_EXTD_STAT(rx_code_err_cntr, RCDE), + XGE_EXTD_STAT(rx_carrier_sense_err_cntr, RCSE), + XGE_EXTD_STAT(rx_undersize_pkt_cntr, RUND), + XGE_EXTD_STAT(rx_oversize_pkt_cntr, ROVR), + XGE_EXTD_STAT(rx_fragments_cntr, RFRG), + XGE_EXTD_STAT(rx_jabber_cntr, RJBR), + XGE_EXTD_STAT(rx_dropped_pkt_cntr, RDRP), + XGE_EXTD_STAT(tx_multicast_pkt_cntr, TMCA), + XGE_EXTD_STAT(tx_broadcast_pkt_cntr, TBCA), + XGE_EXTD_STAT(tx_pause_ctrl_frame_cntr, TXPF), + XGE_EXTD_STAT(tx_defer_pkt_cntr, TDFR), + XGE_EXTD_STAT(tx_excv_defer_pkt_cntr, TEDF), + XGE_EXTD_STAT(tx_single_col_pkt_cntr, TSCL), + XGE_EXTD_STAT(tx_multi_col_pkt_cntr, TMCL), + XGE_EXTD_STAT(tx_late_col_pkt_cntr, TLCL), + XGE_EXTD_STAT(tx_excv_col_pkt_cntr, TXCL), + XGE_EXTD_STAT(tx_total_col_cntr, TNCL), + XGE_EXTD_STAT(tx_pause_frames_hnrd_cntr, TPFH), + XGE_EXTD_STAT(tx_drop_frame_cntr, TDRP), + XGE_EXTD_STAT(tx_jabber_frame_cntr, TJBR), + XGE_EXTD_STAT(tx_fcs_error_cntr, TFCS), + XGE_EXTD_STAT(tx_ctrl_frame_cntr, TXCF), + XGE_EXTD_STAT(tx_oversize_frame_cntr, TOVR), + XGE_EXTD_STAT(tx_undersize_frame_cntr, TUND), + XGE_EXTD_STAT(tx_fragments_cntr, TFRG) +}; + #define XGE_STATS_LEN ARRAY_SIZE(gstrings_stats) +#define XGE_EXTD_STATS_LEN ARRAY_SIZE(gstrings_extd_stats) + +static void xge_mac_get_extd_stats(struct xge_pdata *pdata) +{ + u32 data; + int i; + + for (i = 0; i < XGE_EXTD_STATS_LEN; i++) { + data = xge_rd_csr(pdata, gstrings_extd_stats[i].addr); + gstrings_extd_stats[i].value += data; + } +} static void xge_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) @@ -62,6 +118,11 @@ static void xge_get_strings(struct net_device *ndev, u32 stringset, u8 *data) memcpy(p, gstrings_stats[i].name, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; } + + for (i = 0; i < XGE_EXTD_STATS_LEN; i++) { + memcpy(p, gstrings_extd_stats[i].name, ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } } static int xge_get_sset_count(struct net_device *ndev, int sset) @@ -69,7 +130,7 @@ static int xge_get_sset_count(struct net_device *ndev, int sset) if (sset != ETH_SS_STATS) return -EINVAL; - return XGE_STATS_LEN; + return XGE_STATS_LEN + XGE_EXTD_STATS_LEN; } static void xge_get_ethtool_stats(struct net_device *ndev, @@ -81,6 +142,11 @@ static void xge_get_ethtool_stats(struct net_device *ndev, for (i = 0; i < XGE_STATS_LEN; i++) *data++ = *(u64 *)(pdata + gstrings_stats[i].offset); + + xge_mac_get_extd_stats(pdata); + + for (i = 0; i < XGE_EXTD_STATS_LEN; i++) + *data++ = gstrings_extd_stats[i].value; } static int xge_get_link_ksettings(struct net_device *ndev, diff --git a/drivers/net/ethernet/apm/xgene-v2/ethtool.h b/drivers/net/ethernet/apm/xgene-v2/ethtool.h new file mode 100644 index 000000000000..54b48d5561b8 --- /dev/null +++ b/drivers/net/ethernet/apm/xgene-v2/ethtool.h @@ -0,0 +1,78 @@ +/* + * Applied Micro X-Gene SoC Ethernet v2 Driver + * + * Copyright (c) 2017, Applied Micro Circuits Corporation + * Author(s): Iyappan Subramanian + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __XGENE_ENET_V2_ETHTOOL_H__ +#define __XGENE_ENET_V2_ETHTOOL_H__ + +struct xge_gstrings_stats { + char name[ETH_GSTRING_LEN]; + int offset; +}; + +struct xge_gstrings_extd_stats { + char name[ETH_GSTRING_LEN]; + u32 addr; + u32 value; +}; + +#define TR64 0xa080 +#define TR127 0xa084 +#define TR255 0xa088 +#define TR511 0xa08c +#define TR1K 0xa090 +#define TRMAX 0xa094 +#define TRMGV 0xa098 +#define RFCS 0xa0a4 +#define RMCA 0xa0a8 +#define RBCA 0xa0ac +#define RXCF 0xa0b0 +#define RXPF 0xa0b4 +#define RXUO 0xa0b8 +#define RALN 0xa0bc +#define RFLR 0xa0c0 +#define RCDE 0xa0c4 +#define RCSE 0xa0c8 +#define RUND 0xa0cc +#define ROVR 0xa0d0 +#define RFRG 0xa0d4 +#define RJBR 0xa0d8 +#define RDRP 0xa0dc +#define TMCA 0xa0e8 +#define TBCA 0xa0ec +#define TXPF 0xa0f0 +#define TDFR 0xa0f4 +#define TEDF 0xa0f8 +#define TSCL 0xa0fc +#define TMCL 0xa100 +#define TLCL 0xa104 +#define TXCL 0xa108 +#define TNCL 0xa10c +#define TPFH 0xa110 +#define TDRP 0xa114 +#define TJBR 0xa118 +#define TFCS 0xa11c +#define TXCF 0xa120 +#define TOVR 0xa124 +#define TUND 0xa128 +#define TFRG 0xa12c + +void xge_set_ethtool_ops(struct net_device *ndev); + +#endif /* __XGENE_ENET_V2_ETHTOOL_H__ */ diff --git a/drivers/net/ethernet/apm/xgene-v2/mac.h b/drivers/net/ethernet/apm/xgene-v2/mac.h index 18a9c9d8a5e5..3c83fa617356 100644 --- a/drivers/net/ethernet/apm/xgene-v2/mac.h +++ b/drivers/net/ethernet/apm/xgene-v2/mac.h @@ -34,9 +34,6 @@ #define INTERFACE_CONTROL 0xa038 #define STATION_ADDR0 0xa040 #define STATION_ADDR1 0xa044 -#define RBYT 0xa09c -#define RPKT 0xa0a0 -#define RFCS 0xa0a4 #define RGMII_REG_0 0x27e0 #define ICM_CONFIG0_REG_0 0x2c00 diff --git a/drivers/net/ethernet/apm/xgene-v2/main.h b/drivers/net/ethernet/apm/xgene-v2/main.h index db1178e82a0a..969b258cb7de 100644 --- a/drivers/net/ethernet/apm/xgene-v2/main.h +++ b/drivers/net/ethernet/apm/xgene-v2/main.h @@ -38,6 +38,7 @@ #include "mac.h" #include "enet.h" #include "ring.h" +#include "ethtool.h" #define XGENE_ENET_V2_VERSION "v1.0" #define XGENE_ENET_STD_MTU 1536 @@ -75,6 +76,5 @@ struct xge_pdata { int xge_mdio_config(struct net_device *ndev); void xge_mdio_remove(struct net_device *ndev); -void xge_set_ethtool_ops(struct net_device *ndev); #endif /* __XGENE_ENET_V2_MAIN_H__ */ -- cgit From bf8d9dfb9beeeef48f0444eb02198a09d5a52954 Mon Sep 17 00:00:00 2001 From: Sriharsha Basavapatna Date: Mon, 17 Apr 2017 21:33:13 +0530 Subject: be2net: VxLAN offload should be re-enabled when only 1 UDP port is left We disable VxLAN offload when more than 1 UDP port is added to the driver, since Skyhawk doesn't support offload with multiple ports. The existing driver design expects the user to delete all port configurations and create a configuration with a single UDP port for VxLAN offload to be re-enabled. Remove this restriction by tracking the ports added and re-enabling offload when ports get deleted and only 1 port is left. Signed-off-by: Sriharsha Basavapatna Reviewed-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be.h | 12 ++- drivers/net/ethernet/emulex/benet/be_main.c | 134 +++++++++++++++++++--------- 2 files changed, 102 insertions(+), 44 deletions(-) diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index d49528ad7821..50566243e6fa 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -567,6 +567,12 @@ struct be_error_recovery { /* Ethtool priv_flags */ #define BE_DISABLE_TPE_RECOVERY 0x1 +struct be_vxlan_port { + struct list_head list; + __be16 port; /* VxLAN UDP dst port */ + int port_aliases; /* alias count */ +}; + struct be_adapter { struct pci_dev *pdev; struct net_device *netdev; @@ -671,9 +677,9 @@ struct be_adapter { u32 sli_family; u8 hba_port_num; u16 pvid; - __be16 vxlan_port; - int vxlan_port_count; - int vxlan_port_aliases; + __be16 vxlan_port; /* offloaded vxlan port num */ + int vxlan_port_count; /* active vxlan port count */ + struct list_head vxlan_port_list; /* vxlan port list */ struct phy_info phy; u8 wol_cap; bool wol_en; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 6be3b9aba8ed..8702661b99c0 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -3857,6 +3857,44 @@ static void be_cancel_err_detection(struct be_adapter *adapter) } } +static int be_enable_vxlan_offloads(struct be_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct device *dev = &adapter->pdev->dev; + struct be_vxlan_port *vxlan_port; + __be16 port; + int status; + + vxlan_port = list_first_entry(&adapter->vxlan_port_list, + struct be_vxlan_port, list); + port = vxlan_port->port; + + status = be_cmd_manage_iface(adapter, adapter->if_handle, + OP_CONVERT_NORMAL_TO_TUNNEL); + if (status) { + dev_warn(dev, "Failed to convert normal interface to tunnel\n"); + return status; + } + adapter->flags |= BE_FLAGS_VXLAN_OFFLOADS; + + status = be_cmd_set_vxlan_port(adapter, port); + if (status) { + dev_warn(dev, "Failed to add VxLAN port\n"); + return status; + } + adapter->vxlan_port = port; + + netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_TSO | NETIF_F_TSO6 | + NETIF_F_GSO_UDP_TUNNEL; + netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL; + netdev->features |= NETIF_F_GSO_UDP_TUNNEL; + + dev_info(dev, "Enabled VxLAN offloads for UDP port %d\n", + be16_to_cpu(port)); + return 0; +} + static void be_disable_vxlan_offloads(struct be_adapter *adapter) { struct net_device *netdev = adapter->netdev; @@ -4903,63 +4941,59 @@ static struct be_cmd_work *be_alloc_work(struct be_adapter *adapter, * those other tunnels are unexported on the fly through ndo_features_check(). * * Skyhawk supports VxLAN offloads only for one UDP dport. So, if the stack - * adds more than one port, disable offloads and don't re-enable them again - * until after all the tunnels are removed. + * adds more than one port, disable offloads and re-enable them again when + * there's only one port left. We maintain a list of ports for this purpose. */ static void be_work_add_vxlan_port(struct work_struct *work) { struct be_cmd_work *cmd_work = container_of(work, struct be_cmd_work, work); struct be_adapter *adapter = cmd_work->adapter; - struct net_device *netdev = adapter->netdev; struct device *dev = &adapter->pdev->dev; __be16 port = cmd_work->info.vxlan_port; + struct be_vxlan_port *vxlan_port; int status; - if (adapter->vxlan_port == port && adapter->vxlan_port_count) { - adapter->vxlan_port_aliases++; - goto done; + /* Bump up the alias count if it is an existing port */ + list_for_each_entry(vxlan_port, &adapter->vxlan_port_list, list) { + if (vxlan_port->port == port) { + vxlan_port->port_aliases++; + goto done; + } } + /* Add a new port to our list. We don't need a lock here since port + * add/delete are done only in the context of a single-threaded work + * queue (be_wq). + */ + vxlan_port = kzalloc(sizeof(*vxlan_port), GFP_KERNEL); + if (!vxlan_port) + goto done; + + vxlan_port->port = port; + INIT_LIST_HEAD(&vxlan_port->list); + list_add_tail(&vxlan_port->list, &adapter->vxlan_port_list); + adapter->vxlan_port_count++; + if (adapter->flags & BE_FLAGS_VXLAN_OFFLOADS) { dev_info(dev, "Only one UDP port supported for VxLAN offloads\n"); dev_info(dev, "Disabling VxLAN offloads\n"); - adapter->vxlan_port_count++; goto err; } - if (adapter->vxlan_port_count++ >= 1) + if (adapter->vxlan_port_count > 1) goto done; - status = be_cmd_manage_iface(adapter, adapter->if_handle, - OP_CONVERT_NORMAL_TO_TUNNEL); - if (status) { - dev_warn(dev, "Failed to convert normal interface to tunnel\n"); - goto err; - } - - status = be_cmd_set_vxlan_port(adapter, port); - if (status) { - dev_warn(dev, "Failed to add VxLAN port\n"); - goto err; - } - adapter->flags |= BE_FLAGS_VXLAN_OFFLOADS; - adapter->vxlan_port = port; - - netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_TSO | NETIF_F_TSO6 | - NETIF_F_GSO_UDP_TUNNEL; - netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL; - netdev->features |= NETIF_F_GSO_UDP_TUNNEL; + status = be_enable_vxlan_offloads(adapter); + if (!status) + goto done; - dev_info(dev, "Enabled VxLAN offloads for UDP port %d\n", - be16_to_cpu(port)); - goto done; err: be_disable_vxlan_offloads(adapter); done: kfree(cmd_work); + return; } static void be_work_del_vxlan_port(struct work_struct *work) @@ -4968,23 +5002,40 @@ static void be_work_del_vxlan_port(struct work_struct *work) container_of(work, struct be_cmd_work, work); struct be_adapter *adapter = cmd_work->adapter; __be16 port = cmd_work->info.vxlan_port; + struct be_vxlan_port *vxlan_port; - if (adapter->vxlan_port != port) - goto done; + /* Nothing to be done if a port alias is being deleted */ + list_for_each_entry(vxlan_port, &adapter->vxlan_port_list, list) { + if (vxlan_port->port == port) { + if (vxlan_port->port_aliases) { + vxlan_port->port_aliases--; + goto done; + } + break; + } + } + + /* No port aliases left; delete the port from the list */ + list_del(&vxlan_port->list); + adapter->vxlan_port_count--; - if (adapter->vxlan_port_aliases) { - adapter->vxlan_port_aliases--; + /* Disable VxLAN offload if this is the offloaded port */ + if (adapter->vxlan_port == vxlan_port->port) { + WARN_ON(adapter->vxlan_port_count); + be_disable_vxlan_offloads(adapter); + dev_info(&adapter->pdev->dev, + "Disabled VxLAN offloads for UDP port %d\n", + be16_to_cpu(port)); goto out; } - be_disable_vxlan_offloads(adapter); + /* If only 1 port is left, re-enable VxLAN offload */ + if (adapter->vxlan_port_count == 1) + be_enable_vxlan_offloads(adapter); - dev_info(&adapter->pdev->dev, - "Disabled VxLAN offloads for UDP port %d\n", - be16_to_cpu(port)); -done: - adapter->vxlan_port_count--; out: + kfree(vxlan_port); +done: kfree(cmd_work); } @@ -5626,6 +5677,7 @@ static int be_drv_init(struct be_adapter *adapter) /* Must be a power of 2 or else MODULO will BUG_ON */ adapter->be_get_temp_freq = 64; + INIT_LIST_HEAD(&adapter->vxlan_port_list); return 0; free_rx_filter: -- cgit From 332f235836082fe7d3d890409ed6a20e0ea0d923 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 3 Jan 2017 07:28:11 -0800 Subject: ixgbe: Remove pr_cont uses As pr_cont output can be interleaved by other processes, using pr_cont should be avoided where possible. Miscellanea: - Use a temporary pointer to hold the next descriptions and consolidate the pr_cont uses - Use the temporary buffer to hold the 8 u32 register values and emit those in a single go - Coalesce formats and logging neatening around those changes - Fix a defective output for the rx ring entry description when also emitting rx_buffer_info data This reduces overall object size a tiny bit too. $ size drivers/net/ethernet/intel/ixgbe/*.o* text data bss dec hex filename 62167 728 12 62907 f5bb drivers/net/ethernet/intel/ixgbe/ixgbe_main.o.new 62273 728 12 63013 f625 drivers/net/ethernet/intel/ixgbe/ixgbe_main.o.old Signed-off-by: Joe Perches Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 84 +++++++++++++++------------ 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 852a2e7e25ed..848dea922b2a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -508,7 +508,7 @@ static const struct ixgbe_reg_info ixgbe_reg_info_tbl[] = { */ static void ixgbe_regdump(struct ixgbe_hw *hw, struct ixgbe_reg_info *reginfo) { - int i = 0, j = 0; + int i; char rname[16]; u32 regs[64]; @@ -570,17 +570,21 @@ static void ixgbe_regdump(struct ixgbe_hw *hw, struct ixgbe_reg_info *reginfo) regs[i] = IXGBE_READ_REG(hw, IXGBE_TXDCTL(i)); break; default: - pr_info("%-15s %08x\n", reginfo->name, - IXGBE_READ_REG(hw, reginfo->ofs)); + pr_info("%-15s %08x\n", + reginfo->name, IXGBE_READ_REG(hw, reginfo->ofs)); return; } - for (i = 0; i < 8; i++) { - snprintf(rname, 16, "%s[%d-%d]", reginfo->name, i*8, i*8+7); - pr_err("%-15s", rname); + i = 0; + while (i < 64) { + int j; + char buf[9 * 8 + 1]; + char *p = buf; + + snprintf(rname, 16, "%s[%d-%d]", reginfo->name, i, i + 7); for (j = 0; j < 8; j++) - pr_cont(" %08x", regs[i*8+j]); - pr_cont("\n"); + p += sprintf(p, " %08x", regs[i++]); + pr_err("%-15s%s\n", rname, buf); } } @@ -701,7 +705,18 @@ static void ixgbe_dump(struct ixgbe_adapter *adapter) tx_buffer = &tx_ring->tx_buffer_info[i]; u0 = (struct my_u0 *)tx_desc; if (dma_unmap_len(tx_buffer, len) > 0) { - pr_info("T [0x%03X] %016llX %016llX %016llX %08X %p %016llX %p", + const char *ring_desc; + + if (i == tx_ring->next_to_use && + i == tx_ring->next_to_clean) + ring_desc = " NTC/U"; + else if (i == tx_ring->next_to_use) + ring_desc = " NTU"; + else if (i == tx_ring->next_to_clean) + ring_desc = " NTC"; + else + ring_desc = ""; + pr_info("T [0x%03X] %016llX %016llX %016llX %08X %p %016llX %p%s", i, le64_to_cpu(u0->a), le64_to_cpu(u0->b), @@ -709,16 +724,8 @@ static void ixgbe_dump(struct ixgbe_adapter *adapter) dma_unmap_len(tx_buffer, len), tx_buffer->next_to_watch, (u64)tx_buffer->time_stamp, - tx_buffer->skb); - if (i == tx_ring->next_to_use && - i == tx_ring->next_to_clean) - pr_cont(" NTC/U\n"); - else if (i == tx_ring->next_to_use) - pr_cont(" NTU\n"); - else if (i == tx_ring->next_to_clean) - pr_cont(" NTC\n"); - else - pr_cont("\n"); + tx_buffer->skb, + ring_desc); if (netif_msg_pktdata(adapter) && tx_buffer->skb) @@ -797,34 +804,45 @@ rx_ring_summary: pr_info("------------------------------------\n"); pr_info("RX QUEUE INDEX = %d\n", rx_ring->queue_index); pr_info("------------------------------------\n"); - pr_info("%s%s%s", + pr_info("%s%s%s\n", "R [desc] [ PktBuf A0] ", "[ HeadBuf DD] [bi->dma ] [bi->skb ] ", - "<-- Adv Rx Read format\n"); - pr_info("%s%s%s", + "<-- Adv Rx Read format"); + pr_info("%s%s%s\n", "RWB[desc] [PcsmIpSHl PtRs] ", "[vl er S cks ln] ---------------- [bi->skb ] ", - "<-- Adv Rx Write-Back format\n"); + "<-- Adv Rx Write-Back format"); for (i = 0; i < rx_ring->count; i++) { + const char *ring_desc; + + if (i == rx_ring->next_to_use) + ring_desc = " NTU"; + else if (i == rx_ring->next_to_clean) + ring_desc = " NTC"; + else + ring_desc = ""; + rx_buffer_info = &rx_ring->rx_buffer_info[i]; rx_desc = IXGBE_RX_DESC(rx_ring, i); u0 = (struct my_u0 *)rx_desc; staterr = le32_to_cpu(rx_desc->wb.upper.status_error); if (staterr & IXGBE_RXD_STAT_DD) { /* Descriptor Done */ - pr_info("RWB[0x%03X] %016llX " - "%016llX ---------------- %p", i, + pr_info("RWB[0x%03X] %016llX %016llX ---------------- %p%s\n", + i, le64_to_cpu(u0->a), le64_to_cpu(u0->b), - rx_buffer_info->skb); + rx_buffer_info->skb, + ring_desc); } else { - pr_info("R [0x%03X] %016llX " - "%016llX %016llX %p", i, + pr_info("R [0x%03X] %016llX %016llX %016llX %p%s\n", + i, le64_to_cpu(u0->a), le64_to_cpu(u0->b), (u64)rx_buffer_info->dma, - rx_buffer_info->skb); + rx_buffer_info->skb, + ring_desc); if (netif_msg_pktdata(adapter) && rx_buffer_info->dma) { @@ -835,14 +853,6 @@ rx_ring_summary: ixgbe_rx_bufsz(rx_ring), true); } } - - if (i == rx_ring->next_to_use) - pr_cont(" NTU\n"); - else if (i == rx_ring->next_to_clean) - pr_cont(" NTC\n"); - else - pr_cont("\n"); - } } } -- cgit From 9868879f293c599ce13b584c5bd8800312970781 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 17 Apr 2017 13:54:34 -0700 Subject: net: cx89x0: move attribute declaration before struct keyword The attribute declaration is typically before the definition. Move the __maybe_unused attribute declaration before the struct keyword. Signed-off-by: Stefan Agner Signed-off-by: David S. Miller --- drivers/net/ethernet/cirrus/cs89x0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c index 3647b28e8de0..47384f7323ac 100644 --- a/drivers/net/ethernet/cirrus/cs89x0.c +++ b/drivers/net/ethernet/cirrus/cs89x0.c @@ -1896,7 +1896,7 @@ static int cs89x0_platform_remove(struct platform_device *pdev) return 0; } -static const struct __maybe_unused of_device_id cs89x0_match[] = { +static const struct of_device_id __maybe_unused cs89x0_match[] = { { .compatible = "cirrus,cs8900", }, { .compatible = "cirrus,cs8920", }, { }, -- cgit From 5fbf5addad987028d31d69863c7ec8ed776562cb Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Tue, 1 Nov 2016 13:58:27 -0700 Subject: ixgbe: Remove driver config for KX4 PHY The KX4 PHY is configured by the NVM. Currently, the driver is overwriting the config; remove the code associated with KX4 configuration. Signed-off-by: Tony Nguyen Tested-by: Krishneil Singh --- drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c | 3 +- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 12 -------- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 40 +-------------------------- 3 files changed, 3 insertions(+), 52 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index e55b2602f371..654a402f0e9e 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -792,7 +792,8 @@ s32 ixgbe_setup_phy_link_speed_generic(struct ixgbe_hw *hw, hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10_FULL; /* Setup link based on the new speed settings */ - hw->phy.ops.setup_link(hw); + if (hw->phy.ops.setup_link) + hw->phy.ops.setup_link(hw); return 0; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 1d07f2ead914..c0f6b9503439 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -3754,15 +3754,6 @@ struct ixgbe_info { #define IXGBE_KRM_TX_COEFF_CTRL_1_CZERO_EN BIT(3) #define IXGBE_KRM_TX_COEFF_CTRL_1_OVRRD_EN BIT(31) -#define IXGBE_KX4_LINK_CNTL_1 0x4C -#define IXGBE_KX4_LINK_CNTL_1_TETH_AN_CAP_KX BIT(16) -#define IXGBE_KX4_LINK_CNTL_1_TETH_AN_CAP_KX4 BIT(17) -#define IXGBE_KX4_LINK_CNTL_1_TETH_EEE_CAP_KX BIT(24) -#define IXGBE_KX4_LINK_CNTL_1_TETH_EEE_CAP_KX4 BIT(25) -#define IXGBE_KX4_LINK_CNTL_1_TETH_AN_ENABLE BIT(29) -#define IXGBE_KX4_LINK_CNTL_1_TETH_FORCE_LINK_UP BIT(30) -#define IXGBE_KX4_LINK_CNTL_1_TETH_AN_RESTART BIT(31) - #define IXGBE_SB_IOSF_INDIRECT_CTRL 0x00011144 #define IXGBE_SB_IOSF_INDIRECT_DATA 0x00011148 @@ -3779,9 +3770,6 @@ struct ixgbe_info { #define IXGBE_SB_IOSF_CTRL_BUSY_SHIFT 31 #define IXGBE_SB_IOSF_CTRL_BUSY BIT(IXGBE_SB_IOSF_CTRL_BUSY_SHIFT) #define IXGBE_SB_IOSF_TARGET_KR_PHY 0 -#define IXGBE_SB_IOSF_TARGET_KX4_UNIPHY 1 -#define IXGBE_SB_IOSF_TARGET_KX4_PCS0 2 -#define IXGBE_SB_IOSF_TARGET_KX4_PCS1 3 #define IXGBE_NW_MNG_IF_SEL 0x00011178 #define IXGBE_NW_MNG_IF_SEL_MDIO_ACT BIT(1) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 200f847fd8f3..0a2eacff7327 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -2473,44 +2473,6 @@ static s32 ixgbe_setup_kr_speed_x550em(struct ixgbe_hw *hw, return ixgbe_restart_an_internal_phy_x550em(hw); } -/** ixgbe_setup_kx4_x550em - Configure the KX4 PHY. - * @hw: pointer to hardware structure - * - * Configures the integrated KX4 PHY. - **/ -static s32 ixgbe_setup_kx4_x550em(struct ixgbe_hw *hw) -{ - s32 status; - u32 reg_val; - - status = hw->mac.ops.read_iosf_sb_reg(hw, IXGBE_KX4_LINK_CNTL_1, - IXGBE_SB_IOSF_TARGET_KX4_PCS0 + - hw->bus.lan_id, ®_val); - if (status) - return status; - - reg_val &= ~(IXGBE_KX4_LINK_CNTL_1_TETH_AN_CAP_KX4 | - IXGBE_KX4_LINK_CNTL_1_TETH_AN_CAP_KX); - - reg_val |= IXGBE_KX4_LINK_CNTL_1_TETH_AN_ENABLE; - - /* Advertise 10G support. */ - if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL) - reg_val |= IXGBE_KX4_LINK_CNTL_1_TETH_AN_CAP_KX4; - - /* Advertise 1G support. */ - if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) - reg_val |= IXGBE_KX4_LINK_CNTL_1_TETH_AN_CAP_KX; - - /* Restart auto-negotiation. */ - reg_val |= IXGBE_KX4_LINK_CNTL_1_TETH_AN_RESTART; - status = hw->mac.ops.write_iosf_sb_reg(hw, IXGBE_KX4_LINK_CNTL_1, - IXGBE_SB_IOSF_TARGET_KX4_PCS0 + - hw->bus.lan_id, reg_val); - - return status; -} - /** * ixgbe_setup_kr_x550em - Configure the KR PHY * @hw: pointer to hardware structure @@ -3134,7 +3096,7 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) /* Set functions pointers based on phy type */ switch (hw->phy.type) { case ixgbe_phy_x550em_kx4: - phy->ops.setup_link = ixgbe_setup_kx4_x550em; + phy->ops.setup_link = NULL; phy->ops.read_reg = ixgbe_read_phy_reg_x550em; phy->ops.write_reg = ixgbe_write_phy_reg_x550em; break; -- cgit From 18bda0d93b633a238cc4df257e39c36ea8b1dccf Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Fri, 30 Dec 2016 21:07:14 -0500 Subject: ixgbe: Complete support for X553 sgmii The initial patches supporting X553 sgmii forgot some details. This patch should cover those missing spots. Signed-off-by: Don Skidmore Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 29 +++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 0a2eacff7327..633079eaf8fb 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -334,6 +334,16 @@ static s32 ixgbe_identify_phy_x550em(struct ixgbe_hw *hw) case IXGBE_DEV_ID_X550EM_X_1G_T: case IXGBE_DEV_ID_X550EM_X_10G_T: return ixgbe_identify_phy_generic(hw); + case IXGBE_DEV_ID_X550EM_A_1G_T: + case IXGBE_DEV_ID_X550EM_A_1G_T_L: + hw->phy.type = ixgbe_phy_fw; + hw->phy.ops.read_reg = NULL; + hw->phy.ops.write_reg = NULL; + if (hw->bus.lan_id) + hw->phy.phy_semaphore_mask |= IXGBE_GSSR_PHY1_SM; + else + hw->phy.phy_semaphore_mask |= IXGBE_GSSR_PHY0_SM; + break; default: break; } @@ -2215,8 +2225,20 @@ static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw, else *speed = IXGBE_LINK_SPEED_10GB_FULL; } else { - *speed = IXGBE_LINK_SPEED_10GB_FULL | - IXGBE_LINK_SPEED_1GB_FULL; + switch (hw->phy.type) { + case ixgbe_phy_x550em_kx4: + *speed = IXGBE_LINK_SPEED_1GB_FULL | + IXGBE_LINK_SPEED_2_5GB_FULL | + IXGBE_LINK_SPEED_10GB_FULL; + break; + case ixgbe_phy_sgmii: + *speed = IXGBE_LINK_SPEED_1GB_FULL; + break; + default: + *speed = IXGBE_LINK_SPEED_10GB_FULL | + IXGBE_LINK_SPEED_1GB_FULL; + break; + } *autoneg = true; } return 0; @@ -3126,6 +3148,9 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) phy->ops.handle_lasi = ixgbe_handle_lasi_ext_t_x550em; phy->ops.reset = ixgbe_reset_phy_t_X550em; break; + case ixgbe_phy_sgmii: + phy->ops.setup_link = NULL; + break; case ixgbe_phy_fw: phy->ops.setup_link = ixgbe_setup_fw_link; phy->ops.reset = ixgbe_reset_phy_fw; -- cgit From 18e01ee75f4533cddd774b8618e20d26d7d0d958 Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Fri, 30 Dec 2016 21:07:58 -0500 Subject: ixgbe: Add X552 XFI backplane support This patch add support for X552 XFI backplane interface. The XFI backplane requires a custom tuned link. HW/FW owns the link config for XF backplane and SW must not interfere with it. Signed-off-by: Don Skidmore Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 2 ++ drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 14 ++++++++++++++ 4 files changed, 18 insertions(+) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 0da0752fedef..59730ede4746 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -179,6 +179,7 @@ static u32 ixgbe_get_supported_10gtypes(struct ixgbe_hw *hw) case IXGBE_DEV_ID_82598_BX: case IXGBE_DEV_ID_82599_KR: case IXGBE_DEV_ID_X550EM_X_KR: + case IXGBE_DEV_ID_X550EM_X_XFI: return SUPPORTED_10000baseKR_Full; default: return SUPPORTED_10000baseKX4_Full | diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 848dea922b2a..e250c1d54a11 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -131,6 +131,7 @@ static const struct pci_device_id ixgbe_pci_tbl[] = { {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550T), board_X550}, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550T1), board_X550}, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_KX4), board_X550EM_x}, + {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_XFI), board_X550EM_x}, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_KR), board_X550EM_x}, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_10G_T), board_X550EM_x}, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_SFP), board_X550EM_x}, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index c0f6b9503439..22869aaec3d4 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -85,6 +85,7 @@ #define IXGBE_DEV_ID_X550EM_X_SFP 0x15AC #define IXGBE_DEV_ID_X550EM_X_10G_T 0x15AD #define IXGBE_DEV_ID_X550EM_X_1G_T 0x15AE +#define IXGBE_DEV_ID_X550EM_X_XFI 0x15B0 #define IXGBE_DEV_ID_X550EM_A_KR 0x15C2 #define IXGBE_DEV_ID_X550EM_A_KR_L 0x15C3 #define IXGBE_DEV_ID_X550EM_A_SFP_N 0x15C4 @@ -3128,6 +3129,7 @@ enum ixgbe_phy_type { ixgbe_phy_aq, ixgbe_phy_x550em_kr, ixgbe_phy_x550em_kx4, + ixgbe_phy_x550em_xfi, ixgbe_phy_x550em_ext_t, ixgbe_phy_cu_unknown, ixgbe_phy_qt, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 633079eaf8fb..5127e5e759f2 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -320,6 +320,9 @@ static s32 ixgbe_identify_phy_x550em(struct ixgbe_hw *hw) case IXGBE_DEV_ID_X550EM_X_KX4: hw->phy.type = ixgbe_phy_x550em_kx4; break; + case IXGBE_DEV_ID_X550EM_X_XFI: + hw->phy.type = ixgbe_phy_x550em_xfi; + break; case IXGBE_DEV_ID_X550EM_X_KR: case IXGBE_DEV_ID_X550EM_A_KR: case IXGBE_DEV_ID_X550EM_A_KR_L: @@ -2231,6 +2234,10 @@ static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw, IXGBE_LINK_SPEED_2_5GB_FULL | IXGBE_LINK_SPEED_10GB_FULL; break; + case ixgbe_phy_x550em_xfi: + *speed = IXGBE_LINK_SPEED_1GB_FULL | + IXGBE_LINK_SPEED_10GB_FULL; + break; case ixgbe_phy_sgmii: *speed = IXGBE_LINK_SPEED_1GB_FULL; break; @@ -3127,6 +3134,12 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) phy->ops.read_reg = ixgbe_read_phy_reg_x550em; phy->ops.write_reg = ixgbe_write_phy_reg_x550em; break; + case ixgbe_phy_x550em_xfi: + /* link is managed by HW */ + phy->ops.setup_link = NULL; + phy->ops.read_reg = ixgbe_read_phy_reg_x550em; + phy->ops.write_reg = ixgbe_write_phy_reg_x550em; + break; case ixgbe_phy_x550em_ext_t: /* Save NW management interface connected on board. This is used * to determine internal PHY mode @@ -3180,6 +3193,7 @@ static enum ixgbe_media_type ixgbe_get_media_type_X550em(struct ixgbe_hw *hw) /* Fallthrough */ case IXGBE_DEV_ID_X550EM_X_KR: case IXGBE_DEV_ID_X550EM_X_KX4: + case IXGBE_DEV_ID_X550EM_X_XFI: case IXGBE_DEV_ID_X550EM_A_KR: case IXGBE_DEV_ID_X550EM_A_KR_L: media_type = ixgbe_media_type_backplane; -- cgit From 8e5c9c534f7da526529124d43b3a87b245587f92 Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Fri, 30 Dec 2016 21:13:18 -0500 Subject: ixgbe: list X553 backplane speeds correctly We forgot to indicate some of the supported speed on the X553 backplane. This patch attempts to correct for that. Signed-off-by: Don Skidmore Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 5 +++++ drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 22869aaec3d4..f5fdd9b17de2 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -3775,6 +3775,11 @@ struct ixgbe_info { #define IXGBE_NW_MNG_IF_SEL 0x00011178 #define IXGBE_NW_MNG_IF_SEL_MDIO_ACT BIT(1) +#define IXGBE_NW_MNG_IF_SEL_PHY_SPEED_10M BIT(17) +#define IXGBE_NW_MNG_IF_SEL_PHY_SPEED_100M BIT(18) +#define IXGBE_NW_MNG_IF_SEL_PHY_SPEED_1G BIT(19) +#define IXGBE_NW_MNG_IF_SEL_PHY_SPEED_2_5G BIT(20) +#define IXGBE_NW_MNG_IF_SEL_PHY_SPEED_10G BIT(21) #define IXGBE_NW_MNG_IF_SEL_ENABLE_10_100M BIT(23) #define IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE BIT(24) #define IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD_SHIFT 3 diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 5127e5e759f2..af579c21e57f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -2241,6 +2241,20 @@ static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw, case ixgbe_phy_sgmii: *speed = IXGBE_LINK_SPEED_1GB_FULL; break; + case ixgbe_phy_x550em_kr: + if (hw->mac.type == ixgbe_mac_x550em_a) { + /* check different backplane modes */ + if (hw->phy.nw_mng_if_sel & + IXGBE_NW_MNG_IF_SEL_PHY_SPEED_2_5G) { + *speed = IXGBE_LINK_SPEED_2_5GB_FULL; + break; + } else if (hw->device_id == + IXGBE_DEV_ID_X550EM_A_KR_L) { + *speed = IXGBE_LINK_SPEED_1GB_FULL; + break; + } + } + /* fall through */ default: *speed = IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL; -- cgit From 5b9d3cfb6b362a407d0c1ced588b962c5c934f24 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Thu, 19 Jan 2017 15:55:12 -0800 Subject: ixgbe: add default setup_link for x550em_a MAC type Add default setting for mac->ops.setup_link on x550em_a MAC types. This fixes a link issue on KR parts. Signed-off-by: Emil Tantilov Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index af579c21e57f..04d6c744b936 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -3795,7 +3795,7 @@ static struct ixgbe_mac_operations mac_ops_x550em_a = { .get_media_type = ixgbe_get_media_type_X550em, .get_san_mac_addr = NULL, .get_wwn_prefix = NULL, - .setup_link = NULL, /* defined later */ + .setup_link = &ixgbe_setup_mac_link_X540, .get_link_capabilities = ixgbe_get_link_capabilities_X550em, .get_bus_info = ixgbe_get_bus_info_X550em, .setup_sfp = ixgbe_setup_sfp_modules_X550em, -- cgit From 2bc0972988c770dce093584ffd641856e3b18c5c Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Fri, 20 Jan 2017 14:11:45 -0800 Subject: ixgbe: move num_vfs_macvlans allocation into separate function Move the code allocating memory for list of MAC addresses that the VFs can use for MACVLAN into its own function. Signed-off-by: Emil Tantilov Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 48 ++++++++++++++++---------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 39e109da9bd9..d10b25fa0be4 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -46,42 +46,50 @@ #include "ixgbe_sriov.h" #ifdef CONFIG_PCI_IOV -static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter) +static inline void ixgbe_alloc_vf_macvlans(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; - int num_vf_macvlans, i; struct vf_macvlans *mv_list; - - adapter->flags |= IXGBE_FLAG_SRIOV_ENABLED; - e_info(probe, "SR-IOV enabled with %d VFs\n", adapter->num_vfs); - - /* Enable VMDq flag so device will be set in VM mode */ - adapter->flags |= IXGBE_FLAG_VMDQ_ENABLED; - if (!adapter->ring_feature[RING_F_VMDQ].limit) - adapter->ring_feature[RING_F_VMDQ].limit = 1; - adapter->ring_feature[RING_F_VMDQ].offset = adapter->num_vfs; + int num_vf_macvlans, i; num_vf_macvlans = hw->mac.num_rar_entries - - (IXGBE_MAX_PF_MACVLANS + 1 + adapter->num_vfs); + (IXGBE_MAX_PF_MACVLANS + 1 + adapter->num_vfs); + if (!num_vf_macvlans) + return; - adapter->mv_list = mv_list = kcalloc(num_vf_macvlans, - sizeof(struct vf_macvlans), - GFP_KERNEL); + mv_list = kcalloc(num_vf_macvlans, sizeof(struct vf_macvlans), + GFP_KERNEL); if (mv_list) { /* Initialize list of VF macvlans */ INIT_LIST_HEAD(&adapter->vf_mvs.l); for (i = 0; i < num_vf_macvlans; i++) { - mv_list->vf = -1; - mv_list->free = true; - list_add(&mv_list->l, &adapter->vf_mvs.l); - mv_list++; + mv_list[i].vf = -1; + mv_list[i].free = true; + list_add(&mv_list[i].l, &adapter->vf_mvs.l); } + adapter->mv_list = mv_list; } +} + +static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter) +{ + struct ixgbe_hw *hw = &adapter->hw; + + adapter->flags |= IXGBE_FLAG_SRIOV_ENABLED; + e_info(probe, "SR-IOV enabled with %d VFs\n", adapter->num_vfs); + + /* Enable VMDq flag so device will be set in VM mode */ + adapter->flags |= IXGBE_FLAG_VMDQ_ENABLED; + if (!adapter->ring_feature[RING_F_VMDQ].limit) + adapter->ring_feature[RING_F_VMDQ].limit = 1; + adapter->ring_feature[RING_F_VMDQ].offset = adapter->num_vfs; /* Initialize default switching mode VEB */ IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, IXGBE_PFDTXGSWC_VT_LBEN); adapter->bridge_mode = BRIDGE_MODE_VEB; + ixgbe_alloc_vf_macvlans(adapter); + /* If call to enable VFs succeeded then allocate memory * for per VF control structures. */ @@ -89,6 +97,8 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter) kcalloc(adapter->num_vfs, sizeof(struct vf_data_storage), GFP_KERNEL); if (adapter->vfinfo) { + int i; + /* limit trafffic classes based on VFs enabled */ if ((adapter->hw.mac.type == ixgbe_mac_82599EB) && (adapter->num_vfs < 16)) { -- cgit From da614d042ac236e5db52c56c7d7d8accd325dd40 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Fri, 20 Jan 2017 14:11:50 -0800 Subject: ixgbe: return early instead of wrap block in if statement Since we exit at the end of the block, we can save a level of indentation by performing an early return, and make the next several sections of code more legible, with fewer 80 character line breaks. Also moved allocating vfinfo at the beginning and the notification for enabling SRIOV at the end of the function when we know that it will succeed. Signed-off-by: Emil Tantilov Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 85 +++++++++++++------------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index d10b25fa0be4..16952d33730e 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -74,9 +74,9 @@ static inline void ixgbe_alloc_vf_macvlans(struct ixgbe_adapter *adapter) static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; + int i; adapter->flags |= IXGBE_FLAG_SRIOV_ENABLED; - e_info(probe, "SR-IOV enabled with %d VFs\n", adapter->num_vfs); /* Enable VMDq flag so device will be set in VM mode */ adapter->flags |= IXGBE_FLAG_VMDQ_ENABLED; @@ -84,60 +84,57 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter) adapter->ring_feature[RING_F_VMDQ].limit = 1; adapter->ring_feature[RING_F_VMDQ].offset = adapter->num_vfs; - /* Initialize default switching mode VEB */ - IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, IXGBE_PFDTXGSWC_VT_LBEN); - adapter->bridge_mode = BRIDGE_MODE_VEB; - - ixgbe_alloc_vf_macvlans(adapter); - /* If call to enable VFs succeeded then allocate memory * for per VF control structures. */ - adapter->vfinfo = - kcalloc(adapter->num_vfs, - sizeof(struct vf_data_storage), GFP_KERNEL); - if (adapter->vfinfo) { - int i; - - /* limit trafffic classes based on VFs enabled */ - if ((adapter->hw.mac.type == ixgbe_mac_82599EB) && - (adapter->num_vfs < 16)) { - adapter->dcb_cfg.num_tcs.pg_tcs = MAX_TRAFFIC_CLASS; - adapter->dcb_cfg.num_tcs.pfc_tcs = MAX_TRAFFIC_CLASS; - } else if (adapter->num_vfs < 32) { - adapter->dcb_cfg.num_tcs.pg_tcs = 4; - adapter->dcb_cfg.num_tcs.pfc_tcs = 4; - } else { - adapter->dcb_cfg.num_tcs.pg_tcs = 1; - adapter->dcb_cfg.num_tcs.pfc_tcs = 1; - } + adapter->vfinfo = kcalloc(adapter->num_vfs, + sizeof(struct vf_data_storage), GFP_KERNEL); + if (!adapter->vfinfo) + return -ENOMEM; + + ixgbe_alloc_vf_macvlans(adapter); + + /* Initialize default switching mode VEB */ + IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, IXGBE_PFDTXGSWC_VT_LBEN); + adapter->bridge_mode = BRIDGE_MODE_VEB; - /* Disable RSC when in SR-IOV mode */ - adapter->flags2 &= ~(IXGBE_FLAG2_RSC_CAPABLE | - IXGBE_FLAG2_RSC_ENABLED); + /* limit trafffic classes based on VFs enabled */ + if ((adapter->hw.mac.type == ixgbe_mac_82599EB) && + (adapter->num_vfs < 16)) { + adapter->dcb_cfg.num_tcs.pg_tcs = MAX_TRAFFIC_CLASS; + adapter->dcb_cfg.num_tcs.pfc_tcs = MAX_TRAFFIC_CLASS; + } else if (adapter->num_vfs < 32) { + adapter->dcb_cfg.num_tcs.pg_tcs = 4; + adapter->dcb_cfg.num_tcs.pfc_tcs = 4; + } else { + adapter->dcb_cfg.num_tcs.pg_tcs = 1; + adapter->dcb_cfg.num_tcs.pfc_tcs = 1; + } - for (i = 0; i < adapter->num_vfs; i++) { - /* enable spoof checking for all VFs */ - adapter->vfinfo[i].spoofchk_enabled = true; + /* Disable RSC when in SR-IOV mode */ + adapter->flags2 &= ~(IXGBE_FLAG2_RSC_CAPABLE | + IXGBE_FLAG2_RSC_ENABLED); - /* We support VF RSS querying only for 82599 and x540 - * devices at the moment. These devices share RSS - * indirection table and RSS hash key with PF therefore - * we want to disable the querying by default. - */ - adapter->vfinfo[i].rss_query_enabled = 0; + for (i = 0; i < adapter->num_vfs; i++) { + /* enable spoof checking for all VFs */ + adapter->vfinfo[i].spoofchk_enabled = true; - /* Untrust all VFs */ - adapter->vfinfo[i].trusted = false; + /* We support VF RSS querying only for 82599 and x540 + * devices at the moment. These devices share RSS + * indirection table and RSS hash key with PF therefore + * we want to disable the querying by default. + */ + adapter->vfinfo[i].rss_query_enabled = 0; - /* set the default xcast mode */ - adapter->vfinfo[i].xcast_mode = IXGBEVF_XCAST_MODE_NONE; - } + /* Untrust all VFs */ + adapter->vfinfo[i].trusted = false; - return 0; + /* set the default xcast mode */ + adapter->vfinfo[i].xcast_mode = IXGBEVF_XCAST_MODE_NONE; } - return -ENOMEM; + e_info(probe, "SR-IOV enabled with %d VFs\n", adapter->num_vfs); + return 0; } /** -- cgit From 5c11f00ddac2c030827cdecf9c2d3678cbd3137b Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Fri, 20 Jan 2017 14:11:56 -0800 Subject: ixgbe: do not use adapter->num_vfs when setting VFs via module parameter Avoid setting adapter->num_vfs early in the init code path when using the max_vfs module parameter by passing it to ixgbe_enable_sriov() as a function parameter. This fixes an issue where if we failed to allocate vfinfo in __ixgbe_enable_sriov() the driver will crash with NULL pointer in ixgbe_disable_sriov() when attempting to free the vfinfo struct based on adapter->num_vfs. Also it cleans up the assignment of adapter->num_vfs since now it will only be set in __ixgbe_enable_sriov() and cleared in ixgbe_disable_sriov(). Signed-off-by: Emil Tantilov Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 6 ++-- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 50 +++++++++++++------------- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h | 2 +- 3 files changed, 28 insertions(+), 30 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index e250c1d54a11..dcc8a5c26276 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -5955,10 +5955,8 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter, /* assign number of SR-IOV VFs */ if (hw->mac.type != ixgbe_mac_82598EB) { if (max_vfs > IXGBE_MAX_VFS_DRV_LIMIT) { - adapter->num_vfs = 0; + max_vfs = 0; e_dev_warn("max_vfs parameter out of range. Not assigning any SR-IOV VFs\n"); - } else { - adapter->num_vfs = max_vfs; } } #endif /* CONFIG_PCI_IOV */ @@ -9821,7 +9819,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ixgbe_init_mbx_params_pf(hw); hw->mbx.ops = ii->mbx_ops; pci_sriov_set_totalvfs(pdev, IXGBE_MAX_VFS_DRV_LIMIT); - ixgbe_enable_sriov(adapter); + ixgbe_enable_sriov(adapter, max_vfs); skip_sriov: #endif diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 16952d33730e..102ca937ddb4 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -46,14 +46,15 @@ #include "ixgbe_sriov.h" #ifdef CONFIG_PCI_IOV -static inline void ixgbe_alloc_vf_macvlans(struct ixgbe_adapter *adapter) +static inline void ixgbe_alloc_vf_macvlans(struct ixgbe_adapter *adapter, + unsigned int num_vfs) { struct ixgbe_hw *hw = &adapter->hw; struct vf_macvlans *mv_list; int num_vf_macvlans, i; num_vf_macvlans = hw->mac.num_rar_entries - - (IXGBE_MAX_PF_MACVLANS + 1 + adapter->num_vfs); + (IXGBE_MAX_PF_MACVLANS + 1 + num_vfs); if (!num_vf_macvlans) return; @@ -71,7 +72,8 @@ static inline void ixgbe_alloc_vf_macvlans(struct ixgbe_adapter *adapter) } } -static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter) +static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter, + unsigned int num_vfs) { struct ixgbe_hw *hw = &adapter->hw; int i; @@ -82,28 +84,27 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter) adapter->flags |= IXGBE_FLAG_VMDQ_ENABLED; if (!adapter->ring_feature[RING_F_VMDQ].limit) adapter->ring_feature[RING_F_VMDQ].limit = 1; - adapter->ring_feature[RING_F_VMDQ].offset = adapter->num_vfs; - /* If call to enable VFs succeeded then allocate memory - * for per VF control structures. - */ - adapter->vfinfo = kcalloc(adapter->num_vfs, - sizeof(struct vf_data_storage), GFP_KERNEL); + /* Allocate memory for per VF control structures */ + adapter->vfinfo = kcalloc(num_vfs, sizeof(struct vf_data_storage), + GFP_KERNEL); if (!adapter->vfinfo) return -ENOMEM; - ixgbe_alloc_vf_macvlans(adapter); + adapter->num_vfs = num_vfs; + + ixgbe_alloc_vf_macvlans(adapter, num_vfs); + adapter->ring_feature[RING_F_VMDQ].offset = num_vfs; /* Initialize default switching mode VEB */ IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, IXGBE_PFDTXGSWC_VT_LBEN); adapter->bridge_mode = BRIDGE_MODE_VEB; /* limit trafffic classes based on VFs enabled */ - if ((adapter->hw.mac.type == ixgbe_mac_82599EB) && - (adapter->num_vfs < 16)) { + if ((adapter->hw.mac.type == ixgbe_mac_82599EB) && (num_vfs < 16)) { adapter->dcb_cfg.num_tcs.pg_tcs = MAX_TRAFFIC_CLASS; adapter->dcb_cfg.num_tcs.pfc_tcs = MAX_TRAFFIC_CLASS; - } else if (adapter->num_vfs < 32) { + } else if (num_vfs < 32) { adapter->dcb_cfg.num_tcs.pg_tcs = 4; adapter->dcb_cfg.num_tcs.pfc_tcs = 4; } else { @@ -115,7 +116,7 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter) adapter->flags2 &= ~(IXGBE_FLAG2_RSC_CAPABLE | IXGBE_FLAG2_RSC_ENABLED); - for (i = 0; i < adapter->num_vfs; i++) { + for (i = 0; i < num_vfs; i++) { /* enable spoof checking for all VFs */ adapter->vfinfo[i].spoofchk_enabled = true; @@ -133,7 +134,7 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter) adapter->vfinfo[i].xcast_mode = IXGBEVF_XCAST_MODE_NONE; } - e_info(probe, "SR-IOV enabled with %d VFs\n", adapter->num_vfs); + e_info(probe, "SR-IOV enabled with %d VFs\n", num_vfs); return 0; } @@ -172,12 +173,13 @@ static void ixgbe_get_vfs(struct ixgbe_adapter *adapter) /* Note this function is called when the user wants to enable SR-IOV * VFs using the now deprecated module parameter */ -void ixgbe_enable_sriov(struct ixgbe_adapter *adapter) +void ixgbe_enable_sriov(struct ixgbe_adapter *adapter, unsigned int max_vfs) { int pre_existing_vfs = 0; + unsigned int num_vfs; pre_existing_vfs = pci_num_vf(adapter->pdev); - if (!pre_existing_vfs && !adapter->num_vfs) + if (!pre_existing_vfs && !max_vfs) return; /* If there are pre-existing VFs then we have to force @@ -187,7 +189,7 @@ void ixgbe_enable_sriov(struct ixgbe_adapter *adapter) * have been created via the new PCI SR-IOV sysfs interface. */ if (pre_existing_vfs) { - adapter->num_vfs = pre_existing_vfs; + num_vfs = pre_existing_vfs; dev_warn(&adapter->pdev->dev, "Virtual Functions already enabled for this device - Please reload all VF drivers to avoid spoofed packet errors\n"); } else { @@ -199,17 +201,16 @@ void ixgbe_enable_sriov(struct ixgbe_adapter *adapter) * physical function. If the user requests greater than * 63 VFs then it is an error - reset to default of zero. */ - adapter->num_vfs = min_t(unsigned int, adapter->num_vfs, IXGBE_MAX_VFS_DRV_LIMIT); + num_vfs = min_t(unsigned int, max_vfs, IXGBE_MAX_VFS_DRV_LIMIT); - err = pci_enable_sriov(adapter->pdev, adapter->num_vfs); + err = pci_enable_sriov(adapter->pdev, num_vfs); if (err) { e_err(probe, "Failed to enable PCI sriov: %d\n", err); - adapter->num_vfs = 0; return; } } - if (!__ixgbe_enable_sriov(adapter)) { + if (!__ixgbe_enable_sriov(adapter, num_vfs)) { ixgbe_get_vfs(adapter); return; } @@ -347,13 +348,12 @@ static int ixgbe_pci_sriov_enable(struct pci_dev *dev, int num_vfs) return -EPERM; } } - adapter->num_vfs = num_vfs; - err = __ixgbe_enable_sriov(adapter); + err = __ixgbe_enable_sriov(adapter, num_vfs); if (err) return err; - for (i = 0; i < adapter->num_vfs; i++) + for (i = 0; i < num_vfs; i++) ixgbe_vf_configuration(dev, (i | 0x10000000)); /* reset before enabling SRIOV to avoid mailbox issues */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h index 3166fd164e51..cf67b9b18ed7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.h @@ -59,7 +59,7 @@ int ixgbe_ndo_get_vf_config(struct net_device *netdev, void ixgbe_check_vf_rate_limit(struct ixgbe_adapter *adapter); int ixgbe_disable_sriov(struct ixgbe_adapter *adapter); #ifdef CONFIG_PCI_IOV -void ixgbe_enable_sriov(struct ixgbe_adapter *adapter); +void ixgbe_enable_sriov(struct ixgbe_adapter *adapter, unsigned int max_vfs); #endif int ixgbe_pci_sriov_configure(struct pci_dev *dev, int num_vfs); -- cgit From 7ee814d7a6c449e2e96f76f1acb2b7d47dab108c Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Thu, 2 Feb 2017 14:38:46 -0500 Subject: ixgbe: Remove unused define Remove the Marvell 1145 PHY define as we have never had a device that supports it and have no plan to in the future. The existence of this define has caused confusing on whether or not this PHY was supported by ixgbe. Signed-off-by: Don Skidmore Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index f5fdd9b17de2..2f06e4d9208d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -1388,9 +1388,6 @@ struct ixgbe_thermal_sensor_data { #define ATH_PHY_ID 0x03429050 #define AQ_FW_REV 0x20 -/* PHY Types */ -#define IXGBE_M88E1145_E_PHY_ID 0x01410CD0 - /* Special PHY Init Routine */ #define IXGBE_PHY_INIT_OFFSET_NL 0x002B #define IXGBE_PHY_INIT_END_NL 0xFFFF -- cgit From 9668c93616b2eb9f615adac024c111fb47455554 Mon Sep 17 00:00:00 2001 From: Philippe Reynes Date: Tue, 7 Feb 2017 16:56:33 +0100 Subject: ixgbevf: use new api ethtool_{get|set}_link_ksettings The ethtool api {get|set}_settings is deprecated. We move this driver to new api {get|set}_link_ksettings. As I don't have the hardware, I'd be very pleased if someone may test this patch. Signed-off-by: Philippe Reynes Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/ethtool.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c index 1f6c0ecd50bb..6bf740945260 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -91,18 +91,18 @@ static const char ixgbe_gstrings_test[][ETH_GSTRING_LEN] = { #define IXGBEVF_TEST_LEN (sizeof(ixgbe_gstrings_test) / ETH_GSTRING_LEN) -static int ixgbevf_get_settings(struct net_device *netdev, - struct ethtool_cmd *ecmd) +static int ixgbevf_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) { struct ixgbevf_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; u32 link_speed = 0; bool link_up; - ecmd->supported = SUPPORTED_10000baseT_Full; - ecmd->autoneg = AUTONEG_DISABLE; - ecmd->transceiver = XCVR_DUMMY1; - ecmd->port = -1; + ethtool_link_ksettings_zero_link_mode(cmd, supported); + ethtool_link_ksettings_add_link_mode(cmd, supported, 10000baseT_Full); + cmd->base.autoneg = AUTONEG_DISABLE; + cmd->base.port = -1; hw->mac.get_link_status = 1; hw->mac.ops.check_link(hw, &link_speed, &link_up, false); @@ -122,11 +122,11 @@ static int ixgbevf_get_settings(struct net_device *netdev, break; } - ethtool_cmd_speed_set(ecmd, speed); - ecmd->duplex = DUPLEX_FULL; + cmd->base.speed = speed; + cmd->base.duplex = DUPLEX_FULL; } else { - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; } return 0; @@ -885,7 +885,6 @@ static int ixgbevf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, } static const struct ethtool_ops ixgbevf_ethtool_ops = { - .get_settings = ixgbevf_get_settings, .get_drvinfo = ixgbevf_get_drvinfo, .get_regs_len = ixgbevf_get_regs_len, .get_regs = ixgbevf_get_regs, @@ -905,6 +904,7 @@ static const struct ethtool_ops ixgbevf_ethtool_ops = { .get_rxfh_indir_size = ixgbevf_get_rxfh_indir_size, .get_rxfh_key_size = ixgbevf_get_rxfh_key_size, .get_rxfh = ixgbevf_get_rxfh, + .get_link_ksettings = ixgbevf_get_link_ksettings, }; void ixgbevf_set_ethtool_ops(struct net_device *netdev) -- cgit From f4a6374ba46132896154397ce3c559ccb0d15e60 Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Wed, 1 Mar 2017 11:52:09 -0800 Subject: ixgbe: add check for VETO bit when configuring link for KR We did not have a check in place for MMNGC.MNG_VETO when setting up link on X550EM_X KR devices which resulted in link loss for the BMC when loading the driver. This patch adds a check for ixgbe_check_reset_blocked() in setup_link() since in that case there is no PHY reset function. Signed-off-by: Tony Nguyen Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 04d6c744b936..2658394599e4 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -2526,6 +2526,9 @@ static s32 ixgbe_setup_kr_x550em(struct ixgbe_hw *hw) if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_2_5GB_FULL) return 0; + if (ixgbe_check_reset_blocked(hw)) + return 0; + return ixgbe_setup_kr_speed_x550em(hw, hw->phy.autoneg_advertised); } -- cgit From 541ea69a909739229962ef2c762074550a56fc0c Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 2 Mar 2017 15:01:05 -0800 Subject: ixgbe: Add support for maximum headroom when using build_skb This patch increases the headroom allocated when using build_skb on a system with 4K pages. Specifically the breakdown of headroom versus cache size is as follows: L1 Cache Size Headroom 64 192 64, NET_IP_ALIGN == 2 194 128 128 128, NET_IP_ALIGN == 2 130 256 512 256, NET_IP_ALIGN == 2 258 I stopped at supporting only a cache line size of 256 as that was the largest cache size I could find supported in the kernel. With this we are guaranteeing at least 128 bytes of headroom to spare in the frame. This should be enough for us to insert a couple of IPv6 headers if needed which is likely enough room for anything XDP should need. I'm leaving the padding for systems with pages larger than 4K unmodified for now. XDP currently isn't really setup to work on those types of systems so we can cross that bridge when we get there. Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 55 ++++++++++++++++++++++++--- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 6 +-- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index b1ecc2627a5a..656ca8f69768 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -86,17 +86,62 @@ /* Supported Rx Buffer Sizes */ #define IXGBE_RXBUFFER_256 256 /* Used for skb receive header */ +#define IXGBE_RXBUFFER_1536 1536 #define IXGBE_RXBUFFER_2K 2048 #define IXGBE_RXBUFFER_3K 3072 #define IXGBE_RXBUFFER_4K 4096 #define IXGBE_MAX_RXBUFFER 16384 /* largest size for a single descriptor */ -#define IXGBE_SKB_PAD (NET_SKB_PAD + NET_IP_ALIGN) +/* Attempt to maximize the headroom available for incoming frames. We + * use a 2K buffer for receives and need 1536/1534 to store the data for + * the frame. This leaves us with 512 bytes of room. From that we need + * to deduct the space needed for the shared info and the padding needed + * to IP align the frame. + * + * Note: For cache line sizes 256 or larger this value is going to end + * up negative. In these cases we should fall back to the 3K + * buffers. + */ #if (PAGE_SIZE < 8192) -#define IXGBE_MAX_FRAME_BUILD_SKB \ - (SKB_WITH_OVERHEAD(IXGBE_RXBUFFER_2K) - IXGBE_SKB_PAD) +#define IXGBE_MAX_2K_FRAME_BUILD_SKB (IXGBE_RXBUFFER_1536 - NET_IP_ALIGN) +#define IXGBE_2K_TOO_SMALL_WITH_PADDING \ +((NET_SKB_PAD + IXGBE_RXBUFFER_1536) > SKB_WITH_OVERHEAD(IXGBE_RXBUFFER_2K)) + +static inline int ixgbe_compute_pad(int rx_buf_len) +{ + int page_size, pad_size; + + page_size = ALIGN(rx_buf_len, PAGE_SIZE / 2); + pad_size = SKB_WITH_OVERHEAD(page_size) - rx_buf_len; + + return pad_size; +} + +static inline int ixgbe_skb_pad(void) +{ + int rx_buf_len; + + /* If a 2K buffer cannot handle a standard Ethernet frame then + * optimize padding for a 3K buffer instead of a 1.5K buffer. + * + * For a 3K buffer we need to add enough padding to allow for + * tailroom due to NET_IP_ALIGN possibly shifting us out of + * cache-line alignment. + */ + if (IXGBE_2K_TOO_SMALL_WITH_PADDING) + rx_buf_len = IXGBE_RXBUFFER_3K + SKB_DATA_ALIGN(NET_IP_ALIGN); + else + rx_buf_len = IXGBE_RXBUFFER_1536; + + /* if needed make room for NET_IP_ALIGN */ + rx_buf_len -= NET_IP_ALIGN; + + return ixgbe_compute_pad(rx_buf_len); +} + +#define IXGBE_SKB_PAD ixgbe_skb_pad() #else -#define IXGBE_MAX_FRAME_BUILD_SKB IXGBE_RXBUFFER_2K +#define IXGBE_SKB_PAD (NET_SKB_PAD + NET_IP_ALIGN) #endif /* @@ -361,7 +406,7 @@ static inline unsigned int ixgbe_rx_bufsz(struct ixgbe_ring *ring) return IXGBE_RXBUFFER_3K; #if (PAGE_SIZE < 8192) if (ring_uses_build_skb(ring)) - return IXGBE_MAX_FRAME_BUILD_SKB; + return IXGBE_MAX_2K_FRAME_BUILD_SKB; #endif return IXGBE_RXBUFFER_2K; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index dcc8a5c26276..536dd9b1ad97 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3813,7 +3813,7 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, /* Limit the maximum frame size so we don't overrun the skb */ if (ring_uses_build_skb(ring) && !test_bit(__IXGBE_RX_3K_BUFFER, &ring->state)) - rxdctl |= IXGBE_MAX_FRAME_BUILD_SKB | + rxdctl |= IXGBE_MAX_2K_FRAME_BUILD_SKB | IXGBE_RXDCTL_RLPML_EN; #endif } @@ -3983,8 +3983,8 @@ static void ixgbe_set_rx_buffer_len(struct ixgbe_adapter *adapter) if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) set_bit(__IXGBE_RX_3K_BUFFER, &rx_ring->state); - if ((max_frame > (ETH_FRAME_LEN + ETH_FCS_LEN)) || - (max_frame > IXGBE_MAX_FRAME_BUILD_SKB)) + if (IXGBE_2K_TOO_SMALL_WITH_PADDING || + (max_frame > (ETH_FRAME_LEN + ETH_FCS_LEN))) set_bit(__IXGBE_RX_3K_BUFFER, &rx_ring->state); #endif } -- cgit From 18a8cc9815746b8f0ae6f78733877d3846058d1c Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 2 Mar 2017 15:01:36 -0800 Subject: ixgbe: Fix output from ixgbe_dump I just found that when we had changed the Rx path to check for length instead of the DD bit we introduced an issue in ixgbe_dump since we were no longer clearing the status bits. To correct this I am updating ixgbe_dump to look for the length bits in the descriptor since that is what we are using in the Rx path. Fixes: c3630cc40b4f ("ixgbe: Use length to determine if descriptor is done") Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 536dd9b1ad97..afff2ca7f8c0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -606,7 +606,6 @@ static void ixgbe_dump(struct ixgbe_adapter *adapter) struct ixgbe_ring *rx_ring; union ixgbe_adv_rx_desc *rx_desc; struct ixgbe_rx_buffer *rx_buffer_info; - u32 staterr; int i = 0; if (!netif_msg_hw(adapter)) @@ -827,8 +826,7 @@ rx_ring_summary: rx_buffer_info = &rx_ring->rx_buffer_info[i]; rx_desc = IXGBE_RX_DESC(rx_ring, i); u0 = (struct my_u0 *)rx_desc; - staterr = le32_to_cpu(rx_desc->wb.upper.status_error); - if (staterr & IXGBE_RXD_STAT_DD) { + if (rx_desc->wb.upper.length) { /* Descriptor Done */ pr_info("RWB[0x%03X] %016llX %016llX ---------------- %p%s\n", i, -- cgit From ffa6f571e4e20bbe5b3d8b5e112e66b3b6c29632 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 18 Apr 2017 15:06:53 +0100 Subject: esp6: fix incorrect null pointer check on xo The check for xo being null is incorrect, currently it is checking for non-null, it should be checking for null. Detected with CoverityScan, CID#1429349 ("Dereference after null check") Fixes: 7862b4058b9f ("esp: Add gso handlers for esp4 and esp6") Signed-off-by: Colin Ian King Signed-off-by: Steffen Klassert --- net/ipv6/esp6_offload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c index 1cceeee7cc33..95f10728abaa 100644 --- a/net/ipv6/esp6_offload.c +++ b/net/ipv6/esp6_offload.c @@ -120,7 +120,7 @@ static struct sk_buff *esp6_gso_segment(struct sk_buff *skb, netdev_features_t esp_features = features; struct xfrm_offload *xo = xfrm_offload(skb); - if (xo) + if (!xo) goto out; seq = xo->seq.low; -- cgit From 8f92e03ecca390beed3d5ccc81023d050f0369fd Mon Sep 17 00:00:00 2001 From: Ilan Tayari Date: Wed, 19 Apr 2017 08:41:01 +0300 Subject: esp4/6: Fix GSO path for non-GSO SW-crypto packets If esp*_offload module is loaded, outbound packets take the GSO code path, being encapsulated at layer 3, but encrypted in layer 2. validate_xmit_xfrm calls esp*_xmit for that. esp*_xmit was wrongfully detecting these packets as going through hardware crypto offload, while in fact they should be encrypted in software, causing plaintext leakage to the network, and also dropping at the receiver side. Perform the encryption in esp*_xmit, if the SA doesn't have a hardware offload_handle. Also, align esp6 code to esp4 logic. Fixes: fca11ebde3f0 ("esp4: Reorganize esp_output") Fixes: 383d0350f2cc ("esp6: Reorganize esp_output") Signed-off-by: Ilan Tayari Signed-off-by: Steffen Klassert --- net/ipv4/esp4_offload.c | 4 ++-- net/ipv6/esp6_offload.c | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c index f3e33c26dc33..e0666016a764 100644 --- a/net/ipv4/esp4_offload.c +++ b/net/ipv4/esp4_offload.c @@ -209,8 +209,8 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_ if (!xo) return -EINVAL; - if (!(features & NETIF_F_HW_ESP) || - (x->xso.offload_handle && x->xso.dev != skb->dev)) { + if (!(features & NETIF_F_HW_ESP) || !x->xso.offload_handle || + (x->xso.dev != skb->dev)) { xo->flags |= CRYPTO_FALLBACK; hw_offload = false; } diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c index 95f10728abaa..d950d43ba255 100644 --- a/net/ipv6/esp6_offload.c +++ b/net/ipv6/esp6_offload.c @@ -211,9 +211,10 @@ static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features if (!xo) return -EINVAL; - if (!(features & NETIF_F_HW_ESP) || - (x->xso.offload_handle && x->xso.dev != skb->dev)) { + if (!(features & NETIF_F_HW_ESP) || !x->xso.offload_handle || + (x->xso.dev != skb->dev)) { xo->flags |= CRYPTO_FALLBACK; + hw_offload = false; } esp.proto = xo->proto; @@ -254,7 +255,7 @@ static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features ipv6_hdr(skb)->payload_len = htons(len); } - if (x->xso.offload_handle && !(xo->flags & CRYPTO_FALLBACK)) + if (hw_offload) return 0; esp.seqno = cpu_to_be64(xo->seq.low + ((u64)xo->seq.hi << 32)); -- cgit From 26ecfe01790381c4caa65ec9cce484c623f092c4 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Fri, 14 Apr 2017 22:27:37 +0100 Subject: brcmfmac: only build fwsignal module for CONFIG_BRCMFMAC_PROTO_BCDC The fwsignal module is only referenced by the bcdc module and part of the bcdc protocol. So only build it when CONFIG_BRCMFMAC_PROTO_BCDC is selected. Fixes: acf8ac41dd73 ("brcmfmac: remove reference to fwsignal data from struct brcmf_pub") Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile index 0383ba559edc..1f5a9b948abf 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile @@ -25,7 +25,6 @@ brcmfmac-objs += \ chip.o \ fwil.o \ fweh.o \ - fwsignal.o \ p2p.o \ proto.o \ common.o \ @@ -36,7 +35,8 @@ brcmfmac-objs += \ vendor.o \ pno.o brcmfmac-$(CONFIG_BRCMFMAC_PROTO_BCDC) += \ - bcdc.o + bcdc.o \ + fwsignal.o brcmfmac-$(CONFIG_BRCMFMAC_PROTO_MSGBUF) += \ commonring.o \ flowring.o \ -- cgit From b7dcf68f383a05567bd16a390907b67022a62d3d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 6 Apr 2017 08:12:20 +0300 Subject: ath9k: off by one in ath9k_hw_nvram_read_array() The > should be >= or we read one space beyond the end of the array. Fixes: ab5c4f71d8c7 ("ath9k: allow to load EEPROM content via firmware API") Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/eeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index fb80ec86e53d..6ccf24814514 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -112,7 +112,7 @@ void ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data, static bool ath9k_hw_nvram_read_array(u16 *blob, size_t blob_size, off_t offset, u16 *data) { - if (offset > blob_size) + if (offset >= blob_size) return false; *data = blob[offset]; -- cgit From 050fd820dc177a77f6f74a152db22fff25a35032 Mon Sep 17 00:00:00 2001 From: Damien Thébault Date: Thu, 6 Apr 2017 13:51:10 +0200 Subject: ath9k: Add Dell Wireless 1601 with wowlan capability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the Dell Wireless 1601 card as an AR9462 in the ath9k pci list. Note that the wowlan feature is supported and has been tested successfully. Signed-off-by: Damien Thébault Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index aff473dfa10d..7b7627f85d3a 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -383,6 +383,11 @@ static const struct pci_device_id ath_pci_id_table[] = { 0x10CF, /* Fujitsu */ 0x1783), .driver_data = ATH9K_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_DELL, + 0x020B), + .driver_data = ATH9K_PCI_WOW }, /* Killer Wireless (2x2) */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, -- cgit From 627871b71c89a6ec12fbed75063f238e0c7127b2 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Thu, 6 Apr 2017 14:21:35 -0700 Subject: ath9k: Add cast to u8 to FREQ2FBIN macro The macro results are assigned to u8 variables/fields. Adding the cast fixes plenty of clang warnings about "implicit conversion from 'int' to 'u8'". Signed-off-by: Matthias Kaehlcke Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/eeprom.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h index 30bf722e33ed..31390af6c33e 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.h +++ b/drivers/net/wireless/ath/ath9k/eeprom.h @@ -106,7 +106,7 @@ #define AR9285_RDEXT_DEFAULT 0x1F #define ATH9K_POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) -#define FREQ2FBIN(x, y) ((y) ? ((x) - 2300) : (((x) - 4800) / 5)) +#define FREQ2FBIN(x, y) (u8)((y) ? ((x) - 2300) : (((x) - 4800) / 5)) #define FBIN2FREQ(x, y) ((y) ? (2300 + x) : (4800 + 5 * x)) #define ath9k_hw_use_flash(_ah) (!(_ah->ah_flags & AH_USE_EEPROM)) -- cgit From a4aab099cc9e7c4c94b8a0973b2a483c69b541e3 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Mon, 10 Apr 2017 21:08:17 +0530 Subject: ath10k: fix spectral scan for QCA99X0 family of chipsets spectral_bin length (number of bins per fft sample) is usually a value where (2^n = value), n is an integer. All of the QCA99X0 family of chipsets seems to report a spectral_bin length of 2^n + 'm' bytes, where m = 4, 12 based on the chipset. This 'm' bytes seems to carry some radar related info which is currently discarded only for 'bin_len = 68' bytes. Extend this discarding of irrelevant 'bin_len' for QCA9984, QCA9888, IPQ4019 as well by introducing a hardware parameter 'spectral_bin_discard'. Also for QCA988X based family of chipsets which doesn't seem to have this issue and also for some of the hardware which I have not tested like QCA6174/QCA9377 the existing behaviour is retained as it is. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 12 ++++++++++++ drivers/net/wireless/ath/ath10k/hw.h | 3 +++ drivers/net/wireless/ath/ath10k/spectral.c | 26 +++++++++++++++++--------- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 1b4c08b5eaa9..5a0638915874 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -71,6 +71,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { }, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, + .spectral_bin_discard = 0, }, { .id = QCA9887_HW_1_0_VERSION, @@ -91,6 +92,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { }, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, + .spectral_bin_discard = 0, }, { .id = QCA6174_HW_2_1_VERSION, @@ -110,6 +112,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { }, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, + .spectral_bin_discard = 0, }, { .id = QCA6174_HW_2_1_VERSION, @@ -129,6 +132,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { }, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, + .spectral_bin_discard = 0, }, { .id = QCA6174_HW_3_0_VERSION, @@ -148,6 +152,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { }, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, + .spectral_bin_discard = 0, }, { .id = QCA6174_HW_3_2_VERSION, @@ -170,6 +175,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .hw_clk = qca6174_clk, .target_cpu_freq = 176000000, .decap_align_bytes = 4, + .spectral_bin_discard = 0, }, { .id = QCA99X0_HW_2_0_DEV_VERSION, @@ -195,6 +201,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .sw_decrypt_mcast_mgmt = true, .hw_ops = &qca99x0_ops, .decap_align_bytes = 1, + .spectral_bin_discard = 4, }, { .id = QCA9984_HW_1_0_DEV_VERSION, @@ -221,6 +228,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .sw_decrypt_mcast_mgmt = true, .hw_ops = &qca99x0_ops, .decap_align_bytes = 1, + .spectral_bin_discard = 12, }, { .id = QCA9888_HW_2_0_DEV_VERSION, @@ -246,6 +254,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .sw_decrypt_mcast_mgmt = true, .hw_ops = &qca99x0_ops, .decap_align_bytes = 1, + .spectral_bin_discard = 12, }, { .id = QCA9377_HW_1_0_DEV_VERSION, @@ -265,6 +274,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { }, .hw_ops = &qca988x_ops, .decap_align_bytes = 4, + .spectral_bin_discard = 0, }, { .id = QCA9377_HW_1_1_DEV_VERSION, @@ -286,6 +296,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .hw_clk = qca6174_clk, .target_cpu_freq = 176000000, .decap_align_bytes = 4, + .spectral_bin_discard = 0, }, { .id = QCA4019_HW_1_0_DEV_VERSION, @@ -312,6 +323,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .sw_decrypt_mcast_mgmt = true, .hw_ops = &qca99x0_ops, .decap_align_bytes = 1, + .spectral_bin_discard = 4, }, }; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 8aca62bca348..fd432b60dca7 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -448,6 +448,9 @@ struct ath10k_hw_params { /* hw specific clock control parameters */ const struct ath10k_hw_clk_params *hw_clk; int target_cpu_freq; + + /* Number of bytes to be discarded for each FFT sample */ + int spectral_bin_discard; }; struct htt_rx_desc; diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c index 46471ed30275..dd9cc0939ea8 100644 --- a/drivers/net/wireless/ath/ath10k/spectral.c +++ b/drivers/net/wireless/ath/ath10k/spectral.c @@ -56,6 +56,21 @@ static uint8_t get_max_exp(s8 max_index, u16 max_magnitude, size_t bin_len, return max_exp; } +static inline size_t ath10k_spectral_fix_bin_size(struct ath10k *ar, + size_t bin_len) +{ + /* some chipsets reports bin size as 2^n bytes + 'm' bytes in + * report mode 2. First 2^n bytes carries inband tones and last + * 'm' bytes carries band edge detection data mainly used in + * radar detection purpose. Strip last 'm' bytes to make bin size + * as a valid one. 'm' can take possible values of 4, 12. + */ + if (!is_power_of_2(bin_len)) + bin_len -= ar->hw_params.spectral_bin_discard; + + return bin_len; +} + int ath10k_spectral_process_fft(struct ath10k *ar, struct wmi_phyerr_ev_arg *phyerr, const struct phyerr_fft_report *fftr, @@ -70,18 +85,11 @@ int ath10k_spectral_process_fft(struct ath10k *ar, fft_sample = (struct fft_sample_ath10k *)&buf; + bin_len = ath10k_spectral_fix_bin_size(ar, bin_len); + if (bin_len < 64 || bin_len > SPECTRAL_ATH10K_MAX_NUM_BINS) return -EINVAL; - /* qca99x0 reports bin size as 68 bytes (64 bytes + 4 bytes) in - * report mode 2. First 64 bytes carries inband tones (-32 to +31) - * and last 4 byte carries band edge detection data (+32) mainly - * used in radar detection purpose. Strip last 4 byte to make bin - * size is valid one. - */ - if (bin_len == 68) - bin_len -= 4; - reg0 = __le32_to_cpu(fftr->reg0); reg1 = __le32_to_cpu(fftr->reg1); -- cgit From aad1fd7f7677d05013b5fe247a5a6e1464c69a0f Mon Sep 17 00:00:00 2001 From: Ryan Hsu Date: Tue, 11 Apr 2017 14:04:48 -0700 Subject: ath10k: bump up FW API to 6 For QCA6174 hw3.0, since WLAN.RM.4.4-00022-QCARMSWPZ-2, it starts to support the board ID information from otp, with some devices released on the market that didn't calibrated with OTP, will have 0 for board ID information, which cause the backward compatibility issue and was fixed in commit 'd2e202c06ca4 ("ath10k: ignore configuring the incorrect board_id")' So bump the fw api version to differentiate the latest firmware support. Signed-off-by: Ryan Hsu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/hw.h | 5 ++++- drivers/net/wireless/ath/ath10k/pci.c | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index fd432b60dca7..5b1e90bb2a4d 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -129,7 +129,7 @@ enum qca9377_chip_id_rev { #define QCA4019_HW_1_0_PATCH_LOAD_ADDR 0x1234 #define ATH10K_FW_FILE_BASE "firmware" -#define ATH10K_FW_API_MAX 5 +#define ATH10K_FW_API_MAX 6 #define ATH10K_FW_API_MIN 2 #define ATH10K_FW_API2_FILE "firmware-2.bin" @@ -141,6 +141,9 @@ enum qca9377_chip_id_rev { /* HTT id conflict fix for management frames over HTT */ #define ATH10K_FW_API5_FILE "firmware-5.bin" +/* the firmware-6.bin blob */ +#define ATH10K_FW_API6_FILE "firmware-6.bin" + #define ATH10K_FW_UTF_FILE "utf.bin" #define ATH10K_FW_UTF_API2_FILE "utf-2.bin" diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index b20b66d9d7bc..1e9806f57ee4 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -3428,6 +3428,7 @@ MODULE_FIRMWARE(QCA6174_HW_2_1_FW_DIR "/" ATH10K_BOARD_API2_FILE); /* QCA6174 3.1 firmware files */ MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_FW_API4_FILE); MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_FW_API5_FILE); +MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_FW_API6_FILE); MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" QCA6174_HW_3_0_BOARD_DATA_FILE); MODULE_FIRMWARE(QCA6174_HW_3_0_FW_DIR "/" ATH10K_BOARD_API2_FILE); -- cgit From b90189759a7ff92aa47e8878f6b5a9f868e19895 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Thu, 23 Mar 2017 14:30:48 +0100 Subject: ath9k: add noise floor override option Introduce a debugfs option to manually override the noise floor, ignoring the automatically tuned noise floor of the driver/hw. In my tests with a AR9580 based module and a tx99 5 MHz interferer, I could tune the noisefloor to -95 dBm or above to allow communication again. The automatic noise floor calibration sometimes could adapt to the situation as well, but not reliably and permanently. I would consider this "feature" experimental and interesting for people debugging the noise floor calibration or other effects of the hardware. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/calib.c | 5 ++- drivers/net/wireless/ath/ath9k/debug.c | 62 ++++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/hw.h | 1 + 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 0f71146b781d..13ab6bc46775 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -254,7 +254,9 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) continue; - if (h) + if (ah->nf_override) + nfval = ah->nf_override; + else if (h) nfval = h[i].privNF; else nfval = default_nf; @@ -348,6 +350,7 @@ int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) return 0; } +EXPORT_SYMBOL(ath9k_hw_loadnf); static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf) diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 43930c336987..2e64977a8ab6 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1191,6 +1191,65 @@ static const struct file_operations fops_tpc = { .llseek = default_llseek, }; +static ssize_t read_file_nf_override(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + char buf[32]; + unsigned int len; + + if (ah->nf_override == 0) + len = sprintf(buf, "off\n"); + else + len = sprintf(buf, "%d\n", ah->nf_override); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_nf_override(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (strncmp("off", buf, 3) == 0) + val = 0; + else if (kstrtol(buf, 0, &val)) + return -EINVAL; + + if (val > 0) + return -EINVAL; + + if (val < -120) + return -EINVAL; + + ah->nf_override = val; + + if (ah->curchan) + ath9k_hw_loadnf(ah, ah->curchan); + + return count; +} + +static const struct file_operations fops_nf_override = { + .read = read_file_nf_override, + .write = write_file_nf_override, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + /* Ethtool support for get-stats */ #define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" @@ -1402,5 +1461,8 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_u16("airtime_flags", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->airtime_flags); + debugfs_create_file("nf_override", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, &fops_nf_override); + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 9cbca1229bac..4ac70827d142 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -803,6 +803,7 @@ struct ath_hw { u32 rfkill_gpio; u32 rfkill_polarity; u32 ah_flags; + s16 nf_override; bool reset_power_on; bool htc_reset_init; -- cgit From c0c345d4cacc6a1f39d4856f37dcf6e34f51a5e4 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 12 Apr 2017 23:19:37 +0530 Subject: ath: Fix updating radar flags for coutry code India As per latest regulatory update for India, channel 52, 56, 60, 64 is no longer restricted to DFS. Enabling DFS/no infra flags in driver results in applying all DFS related restrictions (like doing CAC etc before this channel moves to 'available state') for these channels even though the country code is programmed as 'India' in he hardware, fix this by relaxing the frequency range while applying RADAR flags only if the country code is programmed to India. If the frequency range needs to modified based on different country code, ath_is_radar_freq can be extended/modified dynamically. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/regd.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 43afa83a9f0c..e25bfdf78c2e 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -254,8 +254,12 @@ bool ath_is_49ghz_allowed(u16 regdomain) EXPORT_SYMBOL(ath_is_49ghz_allowed); /* Frequency is one where radar detection is required */ -static bool ath_is_radar_freq(u16 center_freq) +static bool ath_is_radar_freq(u16 center_freq, + struct ath_regulatory *reg) + { + if (reg->country_code == CTRY_INDIA) + return (center_freq >= 5500 && center_freq <= 5700); return (center_freq >= 5260 && center_freq <= 5700); } @@ -306,7 +310,7 @@ __ath_reg_apply_beaconing_flags(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, struct ieee80211_channel *ch) { - if (ath_is_radar_freq(ch->center_freq) || + if (ath_is_radar_freq(ch->center_freq, reg) || (ch->flags & IEEE80211_CHAN_RADAR)) return; @@ -395,8 +399,9 @@ ath_reg_apply_ir_flags(struct wiphy *wiphy, } } -/* Always apply Radar/DFS rules on freq range 5260 MHz - 5700 MHz */ -static void ath_reg_apply_radar_flags(struct wiphy *wiphy) +/* Always apply Radar/DFS rules on freq range 5500 MHz - 5700 MHz */ +static void ath_reg_apply_radar_flags(struct wiphy *wiphy, + struct ath_regulatory *reg) { struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; @@ -409,7 +414,7 @@ static void ath_reg_apply_radar_flags(struct wiphy *wiphy) for (i = 0; i < sband->n_channels; i++) { ch = &sband->channels[i]; - if (!ath_is_radar_freq(ch->center_freq)) + if (!ath_is_radar_freq(ch->center_freq, reg)) continue; /* We always enable radar detection/DFS on this * frequency range. Additionally we also apply on @@ -506,7 +511,7 @@ void ath_reg_notifier_apply(struct wiphy *wiphy, struct ath_common *common = container_of(reg, struct ath_common, regulatory); /* We always apply this */ - ath_reg_apply_radar_flags(wiphy); + ath_reg_apply_radar_flags(wiphy, reg); /* * This would happen when we have sent a custom regulatory request @@ -654,7 +659,7 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, } wiphy_apply_custom_regulatory(wiphy, regd); - ath_reg_apply_radar_flags(wiphy); + ath_reg_apply_radar_flags(wiphy, reg); ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg); return 0; } -- cgit From 694a0055f039bc1d73aba10606ea74e798d2d759 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 15 Apr 2017 19:26:10 +0200 Subject: netfilter: nft_ct: allow to set ctnetlink event types of a connection By default the kernel emits all ctnetlink events for a connection. This allows to select the types of events to generate. This can be used to e.g. only send DESTROY events but no NEW/UPDATE ones and will work even if sysctl net.netfilter.nf_conntrack_events is set to 0. This was already possible via iptables' CT target, but the nft version has the advantage that it can also be used with already-established conntracks. The added nf_ct_is_template() check isn't a bug fix as we only support mark and labels (and unlike ecache the conntrack core doesn't copy those). Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 2 ++ net/netfilter/nft_ct.c | 25 ++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 8f3842690d17..683f6f88fcac 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -901,6 +901,7 @@ enum nft_rt_attributes { * @NFT_CT_BYTES: conntrack bytes * @NFT_CT_AVGPKT: conntrack average bytes per packet * @NFT_CT_ZONE: conntrack zone + * @NFT_CT_EVENTMASK: ctnetlink events to be generated for this conntrack */ enum nft_ct_keys { NFT_CT_STATE, @@ -921,6 +922,7 @@ enum nft_ct_keys { NFT_CT_BYTES, NFT_CT_AVGPKT, NFT_CT_ZONE, + NFT_CT_EVENTMASK, }; /** diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 6c6fd48b024c..a34ceb38fc55 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -264,7 +264,7 @@ static void nft_ct_set_eval(const struct nft_expr *expr, struct nf_conn *ct; ct = nf_ct_get(skb, &ctinfo); - if (ct == NULL) + if (ct == NULL || nf_ct_is_template(ct)) return; switch (priv->key) { @@ -283,6 +283,22 @@ static void nft_ct_set_eval(const struct nft_expr *expr, ®s->data[priv->sreg], NF_CT_LABELS_MAX_SIZE / sizeof(u32)); break; +#endif +#ifdef CONFIG_NF_CONNTRACK_EVENTS + case NFT_CT_EVENTMASK: { + struct nf_conntrack_ecache *e = nf_ct_ecache_find(ct); + u32 ctmask = regs->data[priv->sreg]; + + if (e) { + if (e->ctmask != ctmask) + e->ctmask = ctmask; + break; + } + + if (ctmask && !nf_ct_is_confirmed(ct)) + nf_ct_ecache_ext_add(ct, ctmask, 0, GFP_ATOMIC); + break; + } #endif default: break; @@ -538,6 +554,13 @@ static int nft_ct_set_init(const struct nft_ctx *ctx, nft_ct_pcpu_template_refcnt++; len = sizeof(u16); break; +#endif +#ifdef CONFIG_NF_CONNTRACK_EVENTS + case NFT_CT_EVENTMASK: + if (tb[NFTA_CT_DIRECTION]) + return -EINVAL; + len = sizeof(u32); + break; #endif default: return -EOPNOTSUPP; -- cgit From 906535b04671c84ec8731030d4968570d8d2a16c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 16 Apr 2017 01:29:14 +0200 Subject: netfilter: conntrack: move helper struct to nf_conntrack_helper.h its definition is not needed in nf_conntrack.h. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack.h | 19 ------------------- include/net/netfilter/nf_conntrack_helper.h | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 4978a82b75fa..8ece3612d0cd 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -50,25 +50,6 @@ union nf_conntrack_expect_proto { #define NF_CT_ASSERT(x) #endif -struct nf_conntrack_helper; - -/* Must be kept in sync with the classes defined by helpers */ -#define NF_CT_MAX_EXPECT_CLASSES 4 - -/* nf_conn feature for connections that have a helper */ -struct nf_conn_help { - /* Helper. if any */ - struct nf_conntrack_helper __rcu *helper; - - struct hlist_head expectations; - - /* Current number of expected connections */ - u8 expecting[NF_CT_MAX_EXPECT_CLASSES]; - - /* private helper information. */ - char data[]; -}; - #include #include diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 1eaac1f4cd6a..15d746558665 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -52,6 +52,23 @@ struct nf_conntrack_helper { unsigned int queue_num; /* For user-space helpers. */ }; +/* Must be kept in sync with the classes defined by helpers */ +#define NF_CT_MAX_EXPECT_CLASSES 4 + +/* nf_conn feature for connections that have a helper */ +struct nf_conn_help { + /* Helper. if any */ + struct nf_conntrack_helper __rcu *helper; + + struct hlist_head expectations; + + /* Current number of expected connections */ + u8 expecting[NF_CT_MAX_EXPECT_CLASSES]; + + /* private helper information. */ + char data[]; +}; + struct nf_conntrack_helper *__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum); -- cgit From dcf67740f22d31be685d7172f05b289c8243ea12 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 16 Apr 2017 01:29:15 +0200 Subject: netfilter: helper: add build-time asserts for helper data size add a 32 byte scratch area in the helper struct instead of relying on variable sized helpers plus compile-time asserts to let us know if 32 bytes aren't enough anymore. Not having variable sized helpers will later allow to add BUILD_BUG_ON for the total size of conntrack extensions -- the helper extension is the only one that doesn't have a fixed size. The (useless!) NF_CT_HELPER_BUILD_BUG_ON(0); are added so that in case someone adds a new helper and copy-pastes from one that doesn't store private data at least some indication that this macro should be used somehow is there... Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_helper.h | 5 ++++- net/netfilter/nf_conntrack_amanda.c | 2 ++ net/netfilter/nf_conntrack_ftp.c | 2 ++ net/netfilter/nf_conntrack_h323_main.c | 2 ++ net/netfilter/nf_conntrack_netbios_ns.c | 2 ++ net/netfilter/nf_conntrack_pptp.c | 2 ++ net/netfilter/nf_conntrack_sane.c | 2 ++ net/netfilter/nf_conntrack_sip.c | 2 ++ net/netfilter/nf_conntrack_tftp.c | 2 ++ 9 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 15d746558665..29539ed1008f 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -66,9 +66,12 @@ struct nf_conn_help { u8 expecting[NF_CT_MAX_EXPECT_CLASSES]; /* private helper information. */ - char data[]; + char data[32] __aligned(8); }; +#define NF_CT_HELPER_BUILD_BUG_ON(structsize) \ + BUILD_BUG_ON((structsize) > FIELD_SIZEOF(struct nf_conn_help, data)) + struct nf_conntrack_helper *__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum); diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c index 57a26cc90c9f..03d2ccffa9fa 100644 --- a/net/netfilter/nf_conntrack_amanda.c +++ b/net/netfilter/nf_conntrack_amanda.c @@ -207,6 +207,8 @@ static int __init nf_conntrack_amanda_init(void) { int ret, i; + NF_CT_HELPER_BUILD_BUG_ON(0); + for (i = 0; i < ARRAY_SIZE(search); i++) { search[i].ts = textsearch_prepare(ts_algo, search[i].string, search[i].len, diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index 4aecef4a89fb..58e1256cd05d 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -577,6 +577,8 @@ static int __init nf_conntrack_ftp_init(void) { int i, ret = 0; + NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_ftp_master)); + ftp_buffer = kmalloc(65536, GFP_KERNEL); if (!ftp_buffer) return -ENOMEM; diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index f65d93639d12..e98204349efe 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -1836,6 +1836,8 @@ static int __init nf_conntrack_h323_init(void) { int ret; + NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_h323_master)); + h323_buffer = kmalloc(65536, GFP_KERNEL); if (!h323_buffer) return -ENOMEM; diff --git a/net/netfilter/nf_conntrack_netbios_ns.c b/net/netfilter/nf_conntrack_netbios_ns.c index 4c8f30a3d6d2..496ce173f0c1 100644 --- a/net/netfilter/nf_conntrack_netbios_ns.c +++ b/net/netfilter/nf_conntrack_netbios_ns.c @@ -58,6 +58,8 @@ static struct nf_conntrack_helper helper __read_mostly = { static int __init nf_conntrack_netbios_ns_init(void) { + NF_CT_HELPER_BUILD_BUG_ON(0); + exp_policy.timeout = timeout; return nf_conntrack_helper_register(&helper); } diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index f60a4755d71e..34fac4c52c4c 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -607,6 +607,8 @@ static struct nf_conntrack_helper pptp __read_mostly = { static int __init nf_conntrack_pptp_init(void) { + NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_pptp_master)); + return nf_conntrack_helper_register(&pptp); } diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c index 9dcb9ee9b97d..1121db08d048 100644 --- a/net/netfilter/nf_conntrack_sane.c +++ b/net/netfilter/nf_conntrack_sane.c @@ -184,6 +184,8 @@ static int __init nf_conntrack_sane_init(void) { int i, ret = 0; + NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_sane_master)); + sane_buffer = kmalloc(65536, GFP_KERNEL); if (!sane_buffer) return -ENOMEM; diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 91a9c97b7e9a..79bbcc4d52ee 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1622,6 +1622,8 @@ static int __init nf_conntrack_sip_init(void) { int i, ret; + NF_CT_HELPER_BUILD_BUG_ON(sizeof(struct nf_ct_sip_master)); + if (ports_c == 0) ports[ports_c++] = SIP_PORT; diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c index b1227dc6f75e..27e2f6f904dc 100644 --- a/net/netfilter/nf_conntrack_tftp.c +++ b/net/netfilter/nf_conntrack_tftp.c @@ -113,6 +113,8 @@ static int __init nf_conntrack_tftp_init(void) { int i, ret; + NF_CT_HELPER_BUILD_BUG_ON(0); + if (ports_c == 0) ports[ports_c++] = TFTP_PORT; -- cgit From 157ffffeb5dc1b5bbeeef4d4349ab65ba5f42f4e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 16 Apr 2017 01:29:16 +0200 Subject: netfilter: nfnetlink_cthelper: reject too large userspace allocation requests Userspace should not abuse the kernel to store large amounts of data, reject requests larger than the private area can accommodate. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nfnetlink_cthelper.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 9a50bf93dd16..eef7120e1f74 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -104,7 +104,7 @@ nfnl_cthelper_from_nlattr(struct nlattr *attr, struct nf_conn *ct) if (help->helper->data_len == 0) return -EINVAL; - memcpy(help->data, nla_data(attr), help->helper->data_len); + nla_memcpy(help->data, nla_data(attr), sizeof(help->data)); return 0; } @@ -216,6 +216,7 @@ nfnl_cthelper_create(const struct nlattr * const tb[], { struct nf_conntrack_helper *helper; struct nfnl_cthelper *nfcth; + unsigned int size; int ret; if (!tb[NFCTH_TUPLE] || !tb[NFCTH_POLICY] || !tb[NFCTH_PRIV_DATA_LEN]) @@ -231,7 +232,12 @@ nfnl_cthelper_create(const struct nlattr * const tb[], goto err1; strncpy(helper->name, nla_data(tb[NFCTH_NAME]), NF_CT_HELPER_NAME_LEN); - helper->data_len = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN])); + size = ntohl(nla_get_be32(tb[NFCTH_PRIV_DATA_LEN])); + if (size > FIELD_SIZEOF(struct nf_conn_help, data)) { + ret = -ENOMEM; + goto err2; + } + helper->flags |= NF_CT_HELPER_F_USERSPACE; memcpy(&helper->tuple, tuple, sizeof(struct nf_conntrack_tuple)); -- cgit From 9f0f3ebeda47a5518817f33c40f6d3ea9c0275b8 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 16 Apr 2017 01:29:17 +0200 Subject: netfilter: helpers: remove data_len usage for inkernel helpers No need to track this for inkernel helpers anymore as NF_CT_HELPER_BUILD_BUG_ON checks do this now. All inkernel helpers know what kind of structure they stored in helper->data. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_helper.h | 11 ++++++----- net/netfilter/nf_conntrack_ftp.c | 6 ++---- net/netfilter/nf_conntrack_h323_main.c | 4 ---- net/netfilter/nf_conntrack_helper.c | 6 ++---- net/netfilter/nf_conntrack_irc.c | 2 +- net/netfilter/nf_conntrack_pptp.c | 1 - net/netfilter/nf_conntrack_sane.c | 6 ++---- net/netfilter/nf_conntrack_sip.c | 12 ++++-------- net/netfilter/nf_conntrack_tftp.c | 4 ++-- 9 files changed, 19 insertions(+), 33 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 29539ed1008f..e04fa7691e5d 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -29,9 +29,6 @@ struct nf_conntrack_helper { struct module *me; /* pointer to self */ const struct nf_conntrack_expect_policy *expect_policy; - /* length of internal data, ie. sizeof(struct nf_ct_*_master) */ - size_t data_len; - /* Tuple of things we will help (compared against server response) */ struct nf_conntrack_tuple tuple; @@ -49,7 +46,11 @@ struct nf_conntrack_helper { unsigned int expect_class_max; unsigned int flags; - unsigned int queue_num; /* For user-space helpers. */ + + /* For user-space helpers: */ + unsigned int queue_num; + /* length of userspace private data stored in nf_conn_help->data */ + u16 data_len; }; /* Must be kept in sync with the classes defined by helpers */ @@ -82,7 +83,7 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, u16 l3num, u16 protonum, const char *name, u16 default_port, u16 spec_port, u32 id, const struct nf_conntrack_expect_policy *exp_pol, - u32 expect_class_max, u32 data_len, + u32 expect_class_max, int (*help)(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo), diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index 58e1256cd05d..f0e9a7511e1a 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -591,12 +591,10 @@ static int __init nf_conntrack_ftp_init(void) for (i = 0; i < ports_c; i++) { nf_ct_helper_init(&ftp[2 * i], AF_INET, IPPROTO_TCP, "ftp", FTP_PORT, ports[i], ports[i], &ftp_exp_policy, - 0, sizeof(struct nf_ct_ftp_master), help, - nf_ct_ftp_from_nlattr, THIS_MODULE); + 0, help, nf_ct_ftp_from_nlattr, THIS_MODULE); nf_ct_helper_init(&ftp[2 * i + 1], AF_INET6, IPPROTO_TCP, "ftp", FTP_PORT, ports[i], ports[i], &ftp_exp_policy, - 0, sizeof(struct nf_ct_ftp_master), help, - nf_ct_ftp_from_nlattr, THIS_MODULE); + 0, help, nf_ct_ftp_from_nlattr, THIS_MODULE); } ret = nf_conntrack_helpers_register(ftp, ports_c * 2); diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index e98204349efe..3bcdc718484e 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -637,7 +637,6 @@ static const struct nf_conntrack_expect_policy h245_exp_policy = { static struct nf_conntrack_helper nf_conntrack_helper_h245 __read_mostly = { .name = "H.245", .me = THIS_MODULE, - .data_len = sizeof(struct nf_ct_h323_master), .tuple.src.l3num = AF_UNSPEC, .tuple.dst.protonum = IPPROTO_UDP, .help = h245_help, @@ -1215,7 +1214,6 @@ static struct nf_conntrack_helper nf_conntrack_helper_q931[] __read_mostly = { { .name = "Q.931", .me = THIS_MODULE, - .data_len = sizeof(struct nf_ct_h323_master), .tuple.src.l3num = AF_INET, .tuple.src.u.tcp.port = cpu_to_be16(Q931_PORT), .tuple.dst.protonum = IPPROTO_TCP, @@ -1800,7 +1798,6 @@ static struct nf_conntrack_helper nf_conntrack_helper_ras[] __read_mostly = { { .name = "RAS", .me = THIS_MODULE, - .data_len = sizeof(struct nf_ct_h323_master), .tuple.src.l3num = AF_INET, .tuple.src.u.udp.port = cpu_to_be16(RAS_PORT), .tuple.dst.protonum = IPPROTO_UDP, @@ -1810,7 +1807,6 @@ static struct nf_conntrack_helper nf_conntrack_helper_ras[] __read_mostly = { { .name = "RAS", .me = THIS_MODULE, - .data_len = sizeof(struct nf_ct_h323_master), .tuple.src.l3num = AF_INET6, .tuple.src.u.udp.port = cpu_to_be16(RAS_PORT), .tuple.dst.protonum = IPPROTO_UDP, diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 33ebb78649f8..8239b4406f56 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -178,8 +178,7 @@ nf_ct_helper_ext_add(struct nf_conn *ct, { struct nf_conn_help *help; - help = nf_ct_ext_add_length(ct, NF_CT_EXT_HELPER, - helper->data_len, gfp); + help = nf_ct_ext_add(ct, NF_CT_EXT_HELPER, gfp); if (help) INIT_HLIST_HEAD(&help->expectations); else @@ -484,7 +483,7 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, u16 l3num, u16 protonum, const char *name, u16 default_port, u16 spec_port, u32 id, const struct nf_conntrack_expect_policy *exp_pol, - u32 expect_class_max, u32 data_len, + u32 expect_class_max, int (*help)(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo), @@ -497,7 +496,6 @@ void nf_ct_helper_init(struct nf_conntrack_helper *helper, helper->tuple.src.u.all = htons(spec_port); helper->expect_policy = exp_pol; helper->expect_class_max = expect_class_max; - helper->data_len = data_len; helper->help = help; helper->from_nlattr = from_nlattr; helper->me = module; diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index 1a5af4d4af2d..5523acce9d69 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -263,7 +263,7 @@ static int __init nf_conntrack_irc_init(void) for (i = 0; i < ports_c; i++) { nf_ct_helper_init(&irc[i], AF_INET, IPPROTO_TCP, "irc", IRC_PORT, ports[i], i, &irc_exp_policy, - 0, 0, help, NULL, THIS_MODULE); + 0, help, NULL, THIS_MODULE); } ret = nf_conntrack_helpers_register(&irc[0], ports_c); diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index 34fac4c52c4c..126031909fc7 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -596,7 +596,6 @@ static const struct nf_conntrack_expect_policy pptp_exp_policy = { static struct nf_conntrack_helper pptp __read_mostly = { .name = "pptp", .me = THIS_MODULE, - .data_len = sizeof(struct nf_ct_pptp_master), .tuple.src.l3num = AF_INET, .tuple.src.u.tcp.port = cpu_to_be16(PPTP_CONTROL_PORT), .tuple.dst.protonum = IPPROTO_TCP, diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c index 1121db08d048..ae457f39d5ce 100644 --- a/net/netfilter/nf_conntrack_sane.c +++ b/net/netfilter/nf_conntrack_sane.c @@ -198,13 +198,11 @@ static int __init nf_conntrack_sane_init(void) for (i = 0; i < ports_c; i++) { nf_ct_helper_init(&sane[2 * i], AF_INET, IPPROTO_TCP, "sane", SANE_PORT, ports[i], ports[i], - &sane_exp_policy, 0, - sizeof(struct nf_ct_sane_master), help, NULL, + &sane_exp_policy, 0, help, NULL, THIS_MODULE); nf_ct_helper_init(&sane[2 * i + 1], AF_INET6, IPPROTO_TCP, "sane", SANE_PORT, ports[i], ports[i], - &sane_exp_policy, 0, - sizeof(struct nf_ct_sane_master), help, NULL, + &sane_exp_policy, 0, help, NULL, THIS_MODULE); } diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 79bbcc4d52ee..d38af4274335 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1630,23 +1630,19 @@ static int __init nf_conntrack_sip_init(void) for (i = 0; i < ports_c; i++) { nf_ct_helper_init(&sip[4 * i], AF_INET, IPPROTO_UDP, "sip", SIP_PORT, ports[i], i, sip_exp_policy, - SIP_EXPECT_MAX, - sizeof(struct nf_ct_sip_master), sip_help_udp, + SIP_EXPECT_MAX, sip_help_udp, NULL, THIS_MODULE); nf_ct_helper_init(&sip[4 * i + 1], AF_INET, IPPROTO_TCP, "sip", SIP_PORT, ports[i], i, sip_exp_policy, - SIP_EXPECT_MAX, - sizeof(struct nf_ct_sip_master), sip_help_tcp, + SIP_EXPECT_MAX, sip_help_tcp, NULL, THIS_MODULE); nf_ct_helper_init(&sip[4 * i + 2], AF_INET6, IPPROTO_UDP, "sip", SIP_PORT, ports[i], i, sip_exp_policy, - SIP_EXPECT_MAX, - sizeof(struct nf_ct_sip_master), sip_help_udp, + SIP_EXPECT_MAX, sip_help_udp, NULL, THIS_MODULE); nf_ct_helper_init(&sip[4 * i + 3], AF_INET6, IPPROTO_TCP, "sip", SIP_PORT, ports[i], i, sip_exp_policy, - SIP_EXPECT_MAX, - sizeof(struct nf_ct_sip_master), sip_help_tcp, + SIP_EXPECT_MAX, sip_help_tcp, NULL, THIS_MODULE); } diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c index 27e2f6f904dc..0ec6779fd5d9 100644 --- a/net/netfilter/nf_conntrack_tftp.c +++ b/net/netfilter/nf_conntrack_tftp.c @@ -121,10 +121,10 @@ static int __init nf_conntrack_tftp_init(void) for (i = 0; i < ports_c; i++) { nf_ct_helper_init(&tftp[2 * i], AF_INET, IPPROTO_UDP, "tftp", TFTP_PORT, ports[i], i, &tftp_exp_policy, - 0, 0, tftp_help, NULL, THIS_MODULE); + 0, tftp_help, NULL, THIS_MODULE); nf_ct_helper_init(&tftp[2 * i + 1], AF_INET6, IPPROTO_UDP, "tftp", TFTP_PORT, ports[i], i, &tftp_exp_policy, - 0, 0, tftp_help, NULL, THIS_MODULE); + 0, tftp_help, NULL, THIS_MODULE); } ret = nf_conntrack_helpers_register(tftp, ports_c * 2); -- cgit From faec865db9a79e7452d7fc01a4a409b06d02b479 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 16 Apr 2017 01:29:18 +0200 Subject: netfilter: remove last traces of variable-sized extensions get rid of the (now unused) nf_ct_ext_add_length define and also rename the function to plain nf_ct_ext_add(). Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_extend.h | 8 +------- net/netfilter/nf_conntrack_extend.c | 16 +++++++--------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 1c3035dda31f..4ec645c8b647 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -86,13 +86,7 @@ static inline void nf_ct_ext_free(struct nf_conn *ct) } /* Add this type, returns pointer to data or NULL. */ -void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id, - size_t var_alloc_len, gfp_t gfp); - -#define nf_ct_ext_add(ct, id, gfp) \ - ((id##_TYPE *)__nf_ct_ext_add_length((ct), (id), 0, (gfp))) -#define nf_ct_ext_add_length(ct, id, len, gfp) \ - ((id##_TYPE *)__nf_ct_ext_add_length((ct), (id), (len), (gfp))) +void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp); #define NF_CT_EXT_F_PREALLOC 0x0001 diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 008299b7f78f..b5879a9c748d 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -44,8 +44,7 @@ void __nf_ct_ext_destroy(struct nf_conn *ct) EXPORT_SYMBOL(__nf_ct_ext_destroy); static void * -nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, - size_t var_alloc_len, gfp_t gfp) +nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp) { unsigned int off, len; struct nf_ct_ext_type *t; @@ -59,8 +58,8 @@ nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, } off = ALIGN(sizeof(struct nf_ct_ext), t->align); - len = off + t->len + var_alloc_len; - alloc_size = t->alloc_size + var_alloc_len; + len = off + t->len; + alloc_size = t->alloc_size; rcu_read_unlock(); *ext = kzalloc(alloc_size, gfp); @@ -73,8 +72,7 @@ nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, return (void *)(*ext) + off; } -void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id, - size_t var_alloc_len, gfp_t gfp) +void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) { struct nf_ct_ext *old, *new; int newlen, newoff; @@ -85,7 +83,7 @@ void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id, old = ct->ext; if (!old) - return nf_ct_ext_create(&ct->ext, id, var_alloc_len, gfp); + return nf_ct_ext_create(&ct->ext, id, gfp); if (__nf_ct_ext_exist(old, id)) return NULL; @@ -98,7 +96,7 @@ void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id, } newoff = ALIGN(old->len, t->align); - newlen = newoff + t->len + var_alloc_len; + newlen = newoff + t->len; rcu_read_unlock(); new = __krealloc(old, newlen, gfp); @@ -115,7 +113,7 @@ void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id, memset((void *)new + newoff, 0, newlen - newoff); return (void *)new + newoff; } -EXPORT_SYMBOL(__nf_ct_ext_add_length); +EXPORT_SYMBOL(nf_ct_ext_add); static void update_alloc_size(struct nf_ct_ext_type *type) { -- cgit From b3a5db109e0670d6d168e9cd9de4d272a68f7c35 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 16 Apr 2017 01:29:19 +0200 Subject: netfilter: conntrack: use u8 for extension sizes again commit 223b02d923ecd7c84cf9780bb3686f455d279279 ("netfilter: nf_conntrack: reserve two bytes for nf_ct_ext->len") had to increase size of the extension offsets because total size of the extensions had increased to a point where u8 did overflow. 3 years later we've managed to diet extensions a bit and we no longer need u16. Furthermore we can now add a compile-time assertion for this problem. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_extend.h | 4 ++-- net/netfilter/nf_conntrack_core.c | 33 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 4ec645c8b647..5fc908dc9f32 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -43,8 +43,8 @@ enum nf_ct_ext_id { /* Extensions: optional stuff which isn't permanently in struct. */ struct nf_ct_ext { struct rcu_head rcu; - u16 offset[NF_CT_EXT_NUM]; - u16 len; + u8 offset[NF_CT_EXT_NUM]; + u8 len; char data[0]; }; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 03150f60714d..62368b05cef5 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1804,12 +1804,45 @@ EXPORT_SYMBOL_GPL(nf_conntrack_set_hashsize); module_param_call(hashsize, nf_conntrack_set_hashsize, param_get_uint, &nf_conntrack_htable_size, 0600); +static unsigned int total_extension_size(void) +{ + /* remember to add new extensions below */ + BUILD_BUG_ON(NF_CT_EXT_NUM > 9); + + return sizeof(struct nf_ct_ext) + + sizeof(struct nf_conn_help) +#if IS_ENABLED(CONFIG_NF_NAT) + + sizeof(struct nf_conn_nat) +#endif + + sizeof(struct nf_conn_seqadj) + + sizeof(struct nf_conn_acct) +#ifdef CONFIG_NF_CONNTRACK_EVENTS + + sizeof(struct nf_conntrack_ecache) +#endif +#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP + + sizeof(struct nf_conn_tstamp) +#endif +#ifdef CONFIG_NF_CONNTRACK_TIMEOUT + + sizeof(struct nf_conn_timeout) +#endif +#ifdef CONFIG_NF_CONNTRACK_LABELS + + sizeof(struct nf_conn_labels) +#endif +#if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY) + + sizeof(struct nf_conn_synproxy) +#endif + ; +}; + int nf_conntrack_init_start(void) { int max_factor = 8; int ret = -ENOMEM; int i; + /* struct nf_ct_ext uses u8 to store offsets/size */ + BUILD_BUG_ON(total_extension_size() > 255u); + seqcount_init(&nf_conntrack_generation); for (i = 0; i < CONNTRACK_LOCKS; i++) -- cgit From c6dd940b1f747bee62865e348d360f602057196e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 16 Apr 2017 22:08:53 +0200 Subject: netfilter: allow early drop of assured conntracks If insertion of a new conntrack fails because the table is full, the kernel searches the next buckets of the hash slot where the new connection was supposed to be inserted at for an entry that hasn't seen traffic in reply direction (non-assured), if it finds one, that entry is is dropped and the new connection entry is allocated. Allow the conntrack gc worker to also remove *assured* conntracks if resources are low. Do this by querying the l4 tracker, e.g. tcp connections are now dropped if they are no longer established (e.g. in finwait). This could be refined further, e.g. by adding 'soft' established timeout (i.e., a timeout that is only used once we get close to resource exhaustion). Cc: Jozsef Kadlecsik Signed-off-by: Florian Westphal Acked-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 3 ++ net/netfilter/nf_conntrack_core.c | 49 ++++++++++++++++++++++++++++ net/netfilter/nf_conntrack_proto_dccp.c | 16 +++++++++ net/netfilter/nf_conntrack_proto_sctp.c | 16 +++++++++ net/netfilter/nf_conntrack_proto_tcp.c | 18 ++++++++++ 5 files changed, 102 insertions(+) diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 85e993e278d5..7032e044bbe2 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -58,6 +58,9 @@ struct nf_conntrack_l4proto { unsigned int dataoff, u_int8_t pf, unsigned int hooknum); + /* called by gc worker if table is full */ + bool (*can_early_drop)(const struct nf_conn *ct); + /* Print out the per-protocol part of the tuple. Return like seq_* */ void (*print_tuple)(struct seq_file *s, const struct nf_conntrack_tuple *); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 62368b05cef5..f9245dbfe435 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -76,6 +76,7 @@ struct conntrack_gc_work { struct delayed_work dwork; u32 last_bucket; bool exiting; + bool early_drop; long next_gc_run; }; @@ -951,10 +952,30 @@ static noinline int early_drop(struct net *net, unsigned int _hash) return false; } +static bool gc_worker_skip_ct(const struct nf_conn *ct) +{ + return !nf_ct_is_confirmed(ct) || nf_ct_is_dying(ct); +} + +static bool gc_worker_can_early_drop(const struct nf_conn *ct) +{ + const struct nf_conntrack_l4proto *l4proto; + + if (!test_bit(IPS_ASSURED_BIT, &ct->status)) + return true; + + l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); + if (l4proto->can_early_drop && l4proto->can_early_drop(ct)) + return true; + + return false; +} + static void gc_worker(struct work_struct *work) { unsigned int min_interval = max(HZ / GC_MAX_BUCKETS_DIV, 1u); unsigned int i, goal, buckets = 0, expired_count = 0; + unsigned int nf_conntrack_max95 = 0; struct conntrack_gc_work *gc_work; unsigned int ratio, scanned = 0; unsigned long next_run; @@ -963,6 +984,8 @@ static void gc_worker(struct work_struct *work) goal = nf_conntrack_htable_size / GC_MAX_BUCKETS_DIV; i = gc_work->last_bucket; + if (gc_work->early_drop) + nf_conntrack_max95 = nf_conntrack_max / 100u * 95u; do { struct nf_conntrack_tuple_hash *h; @@ -979,6 +1002,8 @@ static void gc_worker(struct work_struct *work) i = 0; hlist_nulls_for_each_entry_rcu(h, n, &ct_hash[i], hnnode) { + struct net *net; + tmp = nf_ct_tuplehash_to_ctrack(h); scanned++; @@ -987,6 +1012,27 @@ static void gc_worker(struct work_struct *work) expired_count++; continue; } + + if (nf_conntrack_max95 == 0 || gc_worker_skip_ct(tmp)) + continue; + + net = nf_ct_net(tmp); + if (atomic_read(&net->ct.count) < nf_conntrack_max95) + continue; + + /* need to take reference to avoid possible races */ + if (!atomic_inc_not_zero(&tmp->ct_general.use)) + continue; + + if (gc_worker_skip_ct(tmp)) { + nf_ct_put(tmp); + continue; + } + + if (gc_worker_can_early_drop(tmp)) + nf_ct_kill(tmp); + + nf_ct_put(tmp); } /* could check get_nulls_value() here and restart if ct @@ -1032,6 +1078,7 @@ static void gc_worker(struct work_struct *work) next_run = gc_work->next_gc_run; gc_work->last_bucket = i; + gc_work->early_drop = false; queue_delayed_work(system_long_wq, &gc_work->dwork, next_run); } @@ -1057,6 +1104,8 @@ __nf_conntrack_alloc(struct net *net, if (nf_conntrack_max && unlikely(atomic_read(&net->ct.count) > nf_conntrack_max)) { if (!early_drop(net, hash)) { + if (!conntrack_gc_work.early_drop) + conntrack_gc_work.early_drop = true; atomic_dec(&net->ct.count); net_warn_ratelimited("nf_conntrack: table full, dropping packet\n"); return ERR_PTR(-ENOMEM); diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index 93dd1c5b7bff..4b3b6e1cadc9 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -609,6 +609,20 @@ out_invalid: return -NF_ACCEPT; } +static bool dccp_can_early_drop(const struct nf_conn *ct) +{ + switch (ct->proto.dccp.state) { + case CT_DCCP_CLOSEREQ: + case CT_DCCP_CLOSING: + case CT_DCCP_TIMEWAIT: + return true; + default: + break; + } + + return false; +} + static void dccp_print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple) { @@ -868,6 +882,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp4 __read_mostly = { .packet = dccp_packet, .get_timeouts = dccp_get_timeouts, .error = dccp_error, + .can_early_drop = dccp_can_early_drop, .print_tuple = dccp_print_tuple, .print_conntrack = dccp_print_conntrack, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) @@ -902,6 +917,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp6 __read_mostly = { .packet = dccp_packet, .get_timeouts = dccp_get_timeouts, .error = dccp_error, + .can_early_drop = dccp_can_early_drop, .print_tuple = dccp_print_tuple, .print_conntrack = dccp_print_conntrack, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 33279aab583d..b34b49c59a1c 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -535,6 +535,20 @@ out_invalid: return -NF_ACCEPT; } +static bool sctp_can_early_drop(const struct nf_conn *ct) +{ + switch (ct->proto.sctp.state) { + case SCTP_CONNTRACK_SHUTDOWN_SENT: + case SCTP_CONNTRACK_SHUTDOWN_RECD: + case SCTP_CONNTRACK_SHUTDOWN_ACK_SENT: + return true; + default: + break; + } + + return false; +} + #if IS_ENABLED(CONFIG_NF_CT_NETLINK) #include @@ -783,6 +797,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = { .get_timeouts = sctp_get_timeouts, .new = sctp_new, .error = sctp_error, + .can_early_drop = sctp_can_early_drop, .me = THIS_MODULE, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) .to_nlattr = sctp_to_nlattr, @@ -818,6 +833,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = { .get_timeouts = sctp_get_timeouts, .new = sctp_new, .error = sctp_error, + .can_early_drop = sctp_can_early_drop, .me = THIS_MODULE, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) .to_nlattr = sctp_to_nlattr, diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index b122e9dacfed..d0c0a31dfe74 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -1172,6 +1172,22 @@ static bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb, return true; } +static bool tcp_can_early_drop(const struct nf_conn *ct) +{ + switch (ct->proto.tcp.state) { + case TCP_CONNTRACK_FIN_WAIT: + case TCP_CONNTRACK_LAST_ACK: + case TCP_CONNTRACK_TIME_WAIT: + case TCP_CONNTRACK_CLOSE: + case TCP_CONNTRACK_CLOSE_WAIT: + return true; + default: + break; + } + + return false; +} + #if IS_ENABLED(CONFIG_NF_CT_NETLINK) #include @@ -1549,6 +1565,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly = .get_timeouts = tcp_get_timeouts, .new = tcp_new, .error = tcp_error, + .can_early_drop = tcp_can_early_drop, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) .to_nlattr = tcp_to_nlattr, .nlattr_size = tcp_nlattr_size, @@ -1586,6 +1603,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 __read_mostly = .get_timeouts = tcp_get_timeouts, .new = tcp_new, .error = tcp_error, + .can_early_drop = tcp_can_early_drop, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) .to_nlattr = tcp_to_nlattr, .nlattr_size = tcp_nlattr_size, -- cgit From 01026edef9062b7d26ace74a5b4a5a33a2399501 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 18 Apr 2017 17:27:32 +0200 Subject: nefilter: eache: reduce struct size from 32 to 24 byte Only "cache" needs to use ulong (its used with set_bit()), missed can use u16. Also add build-time assertion to ensure event bits fit. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_ecache.h | 4 ++-- include/uapi/linux/netfilter/nf_conntrack_common.h | 3 +++ net/netfilter/nf_conntrack_ecache.c | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h index 12d967b58726..2a10c6570fcc 100644 --- a/include/net/netfilter/nf_conntrack_ecache.h +++ b/include/net/netfilter/nf_conntrack_ecache.h @@ -20,11 +20,11 @@ enum nf_ct_ecache_state { struct nf_conntrack_ecache { unsigned long cache; /* bitops want long */ - unsigned long missed; /* missed events */ + u16 missed; /* missed events */ u16 ctmask; /* bitmask of ct events to be delivered */ u16 expmask; /* bitmask of expect events to be delivered */ + enum nf_ct_ecache_state state:8;/* ecache state */ u32 portid; /* netlink portid of destroyer */ - enum nf_ct_ecache_state state; /* ecache state */ }; static inline struct nf_conntrack_ecache * diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h index b4a0a1940118..a8072cc7fa0b 100644 --- a/include/uapi/linux/netfilter/nf_conntrack_common.h +++ b/include/uapi/linux/netfilter/nf_conntrack_common.h @@ -119,6 +119,9 @@ enum ip_conntrack_events { IPCT_NATSEQADJ = IPCT_SEQADJ, IPCT_SECMARK, /* new security mark has been set */ IPCT_LABEL, /* new connlabel has been set */ +#ifdef __KERNEL__ + __IPCT_MAX +#endif }; enum ip_conntrack_expect_events { diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 6161e92d2980..515212948125 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -420,6 +420,9 @@ int nf_conntrack_ecache_init(void) int ret = nf_ct_extend_register(&event_extend); if (ret < 0) pr_err("nf_ct_event: Unable to register event extension.\n"); + + BUILD_BUG_ON(__IPCT_MAX >= 16); /* ctmask, missed use u16 */ + return ret; } -- cgit From be7be6e161a218e92c4e46b97ba59c1e40cfea9c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 18 Apr 2017 17:49:56 +0200 Subject: netfilter: ipvs: fix incorrect conflict resolution The commit ab8bc7ed864b9c4f1fcb00a22bbe4e0f66ce8003 ("netfilter: remove nf_ct_is_untracked") changed the line if (ct && !nf_ct_is_untracked(ct) && nfct_nat(ct)) { to if (ct && nfct_nat(ct)) { meanwhile, the commit 41390895e50bc4f28abe384c6b35ac27464a20ec ("netfilter: ipvs: don't check for presence of nat extension") from ipvs-next had changed the same line to if (ct && !nf_ct_is_untracked(ct) && (ct->status & IPS_NAT_MASK)) { When ipvs-next got merged into nf-next, the merge resolution took the first version, dropping the conversion of nfct_nat(). While this doesn't cause a problem at the moment, it will once we stop adding the nat extension by default. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_ftp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index af3a9bbdf2ae..fb780be76d15 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -260,8 +260,9 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, buf_len = strlen(buf); ct = nf_ct_get(skb, &ctinfo); - if (ct && nfct_nat(ct)) { + if (ct && (ct->status & IPS_NAT_MASK)) { bool mangled; + /* If mangling fails this function will return 0 * which will cause the packet to be dropped. * Mangling can only fail under memory pressure, -- cgit From 122868b378094853b376f3e2ac833bcee078eb3c Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Wed, 19 Apr 2017 09:23:42 +0800 Subject: netfilter: tcp: Use TCP_MAX_WSCALE instead of literal 14 The window scale may be enlarged from 14 to 15 according to the itef draft https://tools.ietf.org/html/draft-nishida-tcpm-maxwin-03. Use the macro TCP_MAX_WSCALE to support it easily with TCP stack in the future. Signed-off-by: Gao Feng Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_proto_tcp.c | 7 +++---- net/netfilter/nf_synproxy_core.c | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index d0c0a31dfe74..d61a68759dea 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -419,10 +419,9 @@ static void tcp_options(const struct sk_buff *skb, && opsize == TCPOLEN_WINDOW) { state->td_scale = *(u_int8_t *)ptr; - if (state->td_scale > 14) { - /* See RFC1323 */ - state->td_scale = 14; - } + if (state->td_scale > TCP_MAX_WSCALE) + state->td_scale = TCP_MAX_WSCALE; + state->flags |= IP_CT_TCP_FLAG_WINDOW_SCALE; } diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index abe03e869f7b..a504e87c6ddf 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -66,8 +66,8 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, case TCPOPT_WINDOW: if (opsize == TCPOLEN_WINDOW) { opts->wscale = *ptr; - if (opts->wscale > 14) - opts->wscale = 14; + if (opts->wscale > TCP_MAX_WSCALE) + opts->wscale = TCP_MAX_WSCALE; opts->options |= XT_SYNPROXY_OPT_WSCALE; } break; -- cgit From d6be9c1d0b2e9b1d4722de2d6e5dabeff56c88a5 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 12 Jan 2017 11:31:25 +0200 Subject: iwlwifi: mvm: read new secure boot registers Addresses were changed for a000 devices. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index bb7e0d642981..e1aa1f7836fc 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -400,6 +400,8 @@ enum aux_misc_master1_en { #define PREG_AUX_BUS_WPROT_0 0xA04CC0 #define SB_CPU_1_STATUS 0xA01E30 #define SB_CPU_2_STATUS 0xA01E34 +#define UMAG_SB_CPU_1_STATUS 0xA038C0 +#define UMAG_SB_CPU_2_STATUS 0xA038C4 /* FW chicken bits */ #define LMPM_CHICK 0xA01FF8 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index e481bb050c6e..6adbd4bcb40a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -639,11 +639,18 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait, MVM_UCODE_ALIVE_TIMEOUT); if (ret) { - if (mvm->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + struct iwl_trans *trans = mvm->trans; + + if (trans->cfg->gen2) + IWL_ERR(mvm, + "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", + iwl_read_prph(trans, UMAG_SB_CPU_1_STATUS), + iwl_read_prph(trans, UMAG_SB_CPU_2_STATUS)); + else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) IWL_ERR(mvm, "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", - iwl_read_prph(mvm->trans, SB_CPU_1_STATUS), - iwl_read_prph(mvm->trans, SB_CPU_2_STATUS)); + iwl_read_prph(trans, SB_CPU_1_STATUS), + iwl_read_prph(trans, SB_CPU_2_STATUS)); mvm->cur_ucode = old_type; return ret; } -- cgit From c5a719ee575ccace15eb9ad49c13c2750946c303 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 15 Nov 2016 10:20:48 +0200 Subject: iwlwifi: mvm: add queues after adding station Currently aux & broadcast queues are added before calling add station, which results with a SCD_QUEUE_CFG command sent with a station id unknown yet to fw. While this works for pre-a000 firmware, the a000 fw requires the order to be reversed. The reason the change is only for a000 devices and not for previous devices is that we cannot reverse the order since the tfd_queue_mask containing the aux queue will cause FW to assert on adding a queue mask with a queue that is not enabled. This is not a problem in a000 fw since the tfd_queue_mask was removed from the add sta API. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 104 +++++++++++++-------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index a552955f72f7..566d57ec86ce 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1700,25 +1700,11 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, return ret; } -int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) +static void iwl_mvm_enable_aux_queue(struct iwl_mvm *mvm) { unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ? mvm->cfg->base_params->wd_timeout : IWL_WATCHDOG_DISABLED; - int ret; - - lockdep_assert_held(&mvm->mutex); - - /* Map Aux queue to fifo - needs to happen before adding Aux station */ - if (!iwl_mvm_is_dqa_supported(mvm)) - iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, mvm->aux_queue, - 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), - NL80211_IFTYPE_UNSPECIFIED); - if (ret) - return ret; if (iwl_mvm_is_dqa_supported(mvm)) { struct iwl_trans_txq_scd_cfg cfg = { @@ -1731,14 +1717,43 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) iwl_mvm_enable_txq(mvm, mvm->aux_queue, mvm->aux_queue, 0, &cfg, wdg_timeout); + } else { + iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, mvm->aux_queue, + IWL_MVM_TX_FIFO_MCAST, 0, wdg_timeout); } +} - ret = iwl_mvm_add_int_sta_common(mvm, &mvm->aux_sta, NULL, - MAC_INDEX_AUX, 0); +int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) +{ + int ret; + + lockdep_assert_held(&mvm->mutex); + /* Allocate aux station and assign to it the aux queue */ + ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue), + NL80211_IFTYPE_UNSPECIFIED); if (ret) + return ret; + + /* Map Aux queue to fifo - needs to happen before adding Aux station */ + if (!iwl_mvm_has_new_tx_api(mvm)) + iwl_mvm_enable_aux_queue(mvm); + + ret = iwl_mvm_add_int_sta_common(mvm, &mvm->aux_sta, NULL, + MAC_INDEX_AUX, 0); + if (ret) { iwl_mvm_dealloc_int_sta(mvm, &mvm->aux_sta); - return ret; + return ret; + } + + /* + * For a000 firmware and on we cannot add queue to a station unknown + * to firmware so enable queue here - after the station was added + */ + if (iwl_mvm_has_new_tx_api(mvm)) + iwl_mvm_enable_aux_queue(mvm); + + return 0; } int iwl_mvm_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) @@ -1789,22 +1804,21 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; const u8 *baddr = _baddr; + int queue = 0; int ret; + unsigned int wdg_timeout = + iwl_mvm_get_wd_timeout(mvm, vif, false, false); + struct iwl_trans_txq_scd_cfg cfg = { + .fifo = IWL_MVM_TX_FIFO_VO, + .sta_id = mvmvif->bcast_sta.sta_id, + .tid = IWL_MAX_TID_COUNT, + .aggregate = false, + .frame_limit = IWL_FRAME_LIMIT, + }; lockdep_assert_held(&mvm->mutex); if (iwl_mvm_is_dqa_supported(mvm)) { - struct iwl_trans_txq_scd_cfg cfg = { - .fifo = IWL_MVM_TX_FIFO_VO, - .sta_id = mvmvif->bcast_sta.sta_id, - .tid = IWL_MAX_TID_COUNT, - .aggregate = false, - .frame_limit = IWL_FRAME_LIMIT, - }; - unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif, false, false); - int queue; - if (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_ADHOC) queue = mvm->probe_queue; @@ -1813,9 +1827,11 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) else if (WARN(1, "Missing required TXQ for adding bcast STA\n")) return -EINVAL; - iwl_mvm_enable_txq(mvm, queue, vif->hw_queue[0], 0, &cfg, - wdg_timeout); bsta->tfd_queue_msk |= BIT(queue); + + if (!iwl_mvm_has_new_tx_api(mvm)) + iwl_mvm_enable_txq(mvm, queue, vif->hw_queue[0], 0, + &cfg, wdg_timeout); } if (vif->type == NL80211_IFTYPE_ADHOC) @@ -1830,28 +1846,12 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return ret; /* - * In AP vif type, we also need to enable the cab_queue. However, we - * have to enable it after the ADD_STA command is sent, otherwise the - * FW will throw an assert once we send the ADD_STA command (it'll - * detect a mismatch in the tfd_queue_msk, as we can't add the - * enabled-cab_queue to the mask) + * For a000 firmware and on we cannot add queue to a station unknown + * to firmware so enable queue here - after the station was added */ - if (iwl_mvm_is_dqa_supported(mvm) && - (vif->type == NL80211_IFTYPE_AP || - vif->type == NL80211_IFTYPE_ADHOC)) { - struct iwl_trans_txq_scd_cfg cfg = { - .fifo = IWL_MVM_TX_FIFO_MCAST, - .sta_id = mvmvif->bcast_sta.sta_id, - .tid = IWL_MAX_TID_COUNT, - .aggregate = false, - .frame_limit = IWL_FRAME_LIMIT, - }; - unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif, false, false); - - iwl_mvm_enable_txq(mvm, vif->cab_queue, vif->cab_queue, - 0, &cfg, wdg_timeout); - } + if (iwl_mvm_has_new_tx_api(mvm)) + iwl_mvm_enable_txq(mvm, queue, vif->hw_queue[0], 0, &cfg, + wdg_timeout); return 0; } -- cgit From dd48847763a79f0382e0383c85bbe01f3cd57a10 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 15 Jan 2017 17:08:40 +0200 Subject: iwlwifi: cleanup unused function iwl_has_secure_boot() isn't getting called anywhere. Clean it up. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 717e089ff036..06034a71a190 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -90,16 +90,6 @@ enum iwl_device_family { IWL_DEVICE_FAMILY_8000, }; -static inline bool iwl_has_secure_boot(u32 hw_rev, - enum iwl_device_family family) -{ - /* return 1 only for family 8000 B0 */ - if ((family == IWL_DEVICE_FAMILY_8000) && (hw_rev & 0xC)) - return true; - - return false; -} - /* * LED mode * IWL_LED_DEFAULT: use device default -- cgit From 0ae988125d777ce5b564cf73e6d83c471e32aff9 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 4 Jan 2017 14:53:58 +0200 Subject: iwlwifi: mvm: prepare for station count change In a000 devices we will support up to 32 stations. The max station define is used also for invalid station marking which makes finding usages of actual maximum station pretty hard to sort through - change it to be a different define in order to make future changes easier. Use also ARRAY_SIZE intead of define when possible. Do not move yet to 32 stations until firmware do it though. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 2 +- .../net/wireless/intel/iwlwifi/mvm/debugfs-vif.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c | 2 +- .../net/wireless/intel/iwlwifi/mvm/fw-api-mac.h | 4 ++- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 6 ++--- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 6 ++--- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 16 ++++++------ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 12 ++++----- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 4 +-- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/sf.c | 6 ++--- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 30 +++++++++++----------- drivers/net/wireless/intel/iwlwifi/mvm/tdls.c | 22 ++++++++-------- drivers/net/wireless/intel/iwlwifi/mvm/tof.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 6 ++--- drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 2 +- 18 files changed, 66 insertions(+), 62 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 5bdb6c2c8390..49b4418e6c35 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -756,7 +756,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * Rssi update while not associated - can happen since the statistics * are handled asynchronously */ - if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) + if (mvmvif->ap_sta_id == IWL_MVM_INVALID_STA) return; /* No BT - reports should be disabled */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index d65acfa3b89b..d74ae15b2d85 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1208,7 +1208,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, mvmvif = iwl_mvm_vif_from_mac80211(vif); - if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) { + if (mvmvif->ap_sta_id == IWL_MVM_INVALID_STA) { /* if we're not associated, this must be netdetect */ if (!wowlan->nd_config) { ret = 1; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c index f4d75ffe3d8a..5d475b4850ae 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c @@ -280,7 +280,7 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file, mvmvif->queue_params[i].uapsd); if (vif->type == NL80211_IFTYPE_STATION && - ap_sta_id != IWL_MVM_STATION_COUNT) { + ap_sta_id != IWL_MVM_INVALID_STA) { struct iwl_mvm_sta *mvm_sta; mvm_sta = iwl_mvm_sta_from_staid_protected(mvm, ap_sta_id); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 077bfd8f4c0c..402846650cbe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -330,7 +330,7 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, mutex_lock(&mvm->mutex); - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) { pos += scnprintf(buf + pos, bufsz - pos, "%.2d: ", i); sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], lockdep_is_held(&mvm->mutex)); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h index 480a54af4534..d3cdd889c85c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h @@ -73,7 +73,9 @@ #define NUM_MAC_INDEX (NUM_MAC_INDEX_DRIVER + 1) #define NUM_MAC_INDEX_CDB (NUM_MAC_INDEX_DRIVER + 2) -#define IWL_MVM_STATION_COUNT 16 +#define IWL_MVM_STATION_COUNT 16 +#define IWL_MVM_INVALID_STA 0xFF + #define IWL_MVM_TDLS_STA_COUNT 4 enum iwl_ac { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 6adbd4bcb40a..61cafb541554 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1250,10 +1250,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm) } /* init the fw <-> mac80211 STA mapping */ - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) + for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); - mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT; + mvm->tdls_cs.peer.sta_id = IWL_MVM_INVALID_STA; /* reset quota debouncing buffer - 0xff will yield invalid data */ memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd)); @@ -1383,7 +1383,7 @@ int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm) goto error; /* init the fw <-> mac80211 STA mapping */ - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) + for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); /* Add auxiliary station for scanning */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 626864f3701c..ae1406fe9988 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -472,9 +472,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; } - mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT; - mvmvif->mcast_sta.sta_id = IWL_MVM_STATION_COUNT; - mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + mvmvif->bcast_sta.sta_id = IWL_MVM_INVALID_STA; + mvmvif->mcast_sta.sta_id = IWL_MVM_INVALID_STA; + mvmvif->ap_sta_id = IWL_MVM_INVALID_STA; for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) mvmvif->smps_requests[i] = IEEE80211_SMPS_AUTOMATIC; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index afe0e0f0744f..26cf3dfcc108 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -767,7 +767,7 @@ static bool iwl_mvm_defer_tx(struct iwl_mvm *mvm, goto out; mvmsta = iwl_mvm_sta_from_mac80211(sta); - if (mvmsta->sta_id == IWL_MVM_STATION_COUNT || + if (mvmsta->sta_id == IWL_MVM_INVALID_STA || mvmsta->sta_id != mvm->d0i3_ap_sta_id) goto out; @@ -1011,7 +1011,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); mvmvif->uploaded = false; - mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + mvmvif->ap_sta_id = IWL_MVM_INVALID_STA; spin_lock_bh(&mvm->time_event_lock); iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data); @@ -1054,7 +1054,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) ieee80211_iterate_interfaces(mvm->hw, 0, iwl_mvm_cleanup_iterator, mvm); mvm->p2p_device_vif = NULL; - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; + mvm->d0i3_ap_sta_id = IWL_MVM_INVALID_STA; iwl_mvm_reset_phy_ctxts(mvm); memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); @@ -1965,7 +1965,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, IWL_MVM_SMPS_REQ_PROT, IEEE80211_SMPS_DYNAMIC); } - } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { + } else if (mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) { /* * If update fails - SF might be running in associated * mode while disassociated - which is forbidden. @@ -1979,8 +1979,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, IWL_ERR(mvm, "failed to remove AP station\n"); if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; - mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + mvm->d0i3_ap_sta_id = IWL_MVM_INVALID_STA; + mvmvif->ap_sta_id = IWL_MVM_INVALID_STA; /* remove quota for this interface */ ret = iwl_mvm_update_quotas(mvm, false, NULL); if (ret) @@ -2391,7 +2391,7 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, */ break; case STA_NOTIFY_AWAKE: - if (WARN_ON(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) + if (WARN_ON(mvmsta->sta_id == IWL_MVM_INVALID_STA)) break; if (txqs) @@ -3961,7 +3961,7 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, mvmvif = iwl_mvm_vif_from_mac80211(vif); /* flush the AP-station and all TDLS peers */ - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) { sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], lockdep_is_held(&mvm->mutex)); if (IS_ERR_OR_NULL(sta)) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 72afccfe4282..d313941ffc9d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1268,7 +1268,7 @@ static bool iwl_mvm_disallow_offloading(struct iwl_mvm *mvm, u8 tid; if (WARN_ON(vif->type != NL80211_IFTYPE_STATION || - mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)) + mvmvif->ap_sta_id == IWL_MVM_INVALID_STA)) return false; mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id); @@ -1356,7 +1356,7 @@ static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm, struct ieee80211_sta *ap_sta; struct iwl_mvm_sta *mvm_ap_sta; - if (iter_data->ap_sta_id == IWL_MVM_STATION_COUNT) + if (iter_data->ap_sta_id == IWL_MVM_INVALID_STA) return; rcu_read_lock(); @@ -1426,7 +1426,7 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) mvm->d0i3_offloading = !d0i3_iter_data.disable_offloading; } else { WARN_ON_ONCE(d0i3_iter_data.vif_count > 1); - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; + mvm->d0i3_ap_sta_id = IWL_MVM_INVALID_STA; mvm->d0i3_offloading = false; } @@ -1439,7 +1439,7 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) return ret; /* configure wowlan configuration only if needed */ - if (mvm->d0i3_ap_sta_id != IWL_MVM_STATION_COUNT) { + if (mvm->d0i3_ap_sta_id != IWL_MVM_INVALID_STA) { /* wake on beacons only if beacon storing isn't supported */ if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEACON_STORING)) @@ -1516,7 +1516,7 @@ void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq) spin_lock_bh(&mvm->d0i3_tx_lock); - if (mvm->d0i3_ap_sta_id == IWL_MVM_STATION_COUNT) + if (mvm->d0i3_ap_sta_id == IWL_MVM_INVALID_STA) goto out; IWL_DEBUG_RPM(mvm, "re-enqueue packets\n"); @@ -1554,7 +1554,7 @@ out: } clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); wake_up(&mvm->d0i3_exit_waitq); - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; + mvm->d0i3_ap_sta_id = IWL_MVM_INVALID_STA; if (wake_queues) ieee80211_wake_queues(mvm->hw); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index c1bf67f04cf6..861d54641d1e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -351,7 +351,7 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, id >>= RX_MDPU_RES_STATUS_STA_ID_SHIFT; - if (!WARN_ON_ONCE(id >= IWL_MVM_STATION_COUNT)) { + if (!WARN_ON_ONCE(id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))) { sta = rcu_dereference(mvm->fw_id_to_mac_id[id]); if (IS_ERR(sta)) sta = NULL; @@ -693,7 +693,7 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, } rcu_read_lock(); - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) { struct iwl_mvm_sta *sta; if (!energy[i]) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 85f7c83995b9..29c9c56ed3fa 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -841,7 +841,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, if (le16_to_cpu(desc->status) & IWL_RX_MPDU_STATUS_SRC_STA_FOUND) { u8 id = desc->sta_id_flags & IWL_RX_MPDU_SIF_STA_ID_MASK; - if (!WARN_ON_ONCE(id >= IWL_MVM_STATION_COUNT)) { + if (!WARN_ON_ONCE(id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))) { sta = rcu_dereference(mvm->fw_id_to_mac_id[id]); if (IS_ERR(sta)) sta = NULL; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c index 101fb04a8573..539b06bf0803 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sf.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sf.c @@ -235,7 +235,7 @@ static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id, iwl_mvm_fill_sf_command(mvm, &sf_cmd, NULL); break; case SF_FULL_ON: - if (sta_id == IWL_MVM_STATION_COUNT) { + if (sta_id == IWL_MVM_INVALID_STA) { IWL_ERR(mvm, "No station: Cannot switch SF to FULL_ON\n"); return -EINVAL; @@ -276,12 +276,12 @@ int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif, bool remove_vif) { enum iwl_sf_state new_state; - u8 sta_id = IWL_MVM_STATION_COUNT; + u8 sta_id = IWL_MVM_INVALID_STA; struct iwl_mvm_vif *mvmvif = NULL; struct iwl_mvm_active_iface_iterator_data data = { .ignore_vif = changed_vif, .sta_vif_state = SF_UNINIT, - .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT, + .sta_vif_ap_sta_id = IWL_MVM_INVALID_STA, }; /* diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 566d57ec86ce..fb031197266e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -98,7 +98,7 @@ static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, reserved_ids = BIT(0); /* Don't take rcu_read_lock() since we are protected by mvm->mutex */ - for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) { + for (sta_id = 0; sta_id < ARRAY_SIZE(mvm->fw_id_to_mac_id); sta_id++) { if (BIT(sta_id) & reserved_ids) continue; @@ -106,7 +106,7 @@ static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, lockdep_is_held(&mvm->mutex))) return sta_id; } - return IWL_MVM_STATION_COUNT; + return IWL_MVM_INVALID_STA; } /* send station add/update command to firmware */ @@ -1235,7 +1235,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, else sta_id = mvm_sta->sta_id; - if (sta_id == IWL_MVM_STATION_COUNT) + if (sta_id == IWL_MVM_INVALID_STA) return -ENOSPC; spin_lock_init(&mvm_sta->lock); @@ -1317,10 +1317,10 @@ update_fw: if (vif->type == NL80211_IFTYPE_STATION) { if (!sta->tdls) { - WARN_ON(mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT); + WARN_ON(mvmvif->ap_sta_id != IWL_MVM_INVALID_STA); mvmvif->ap_sta_id = sta_id; } else { - WARN_ON(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT); + WARN_ON(mvmvif->ap_sta_id == IWL_MVM_INVALID_STA); } } @@ -1571,11 +1571,11 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, return ret; /* unassoc - go ahead - remove the AP STA now */ - mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + mvmvif->ap_sta_id = IWL_MVM_INVALID_STA; /* clear d0i3_ap_sta_id if no longer relevant */ if (mvm->d0i3_ap_sta_id == sta_id) - mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; + mvm->d0i3_ap_sta_id = IWL_MVM_INVALID_STA; } } @@ -1584,7 +1584,7 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, * before the STA is removed. */ if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == sta_id)) { - mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT; + mvm->tdls_cs.peer.sta_id = IWL_MVM_INVALID_STA; cancel_delayed_work(&mvm->tdls_cs.dwork); } @@ -1641,7 +1641,7 @@ int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, { if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype); - if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT)) + if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_INVALID_STA)) return -ENOSPC; } @@ -1656,7 +1656,7 @@ void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta) { RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL); memset(sta, 0, sizeof(struct iwl_mvm_int_sta)); - sta->sta_id = IWL_MVM_STATION_COUNT; + sta->sta_id = IWL_MVM_INVALID_STA; } static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, @@ -1837,7 +1837,7 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (vif->type == NL80211_IFTYPE_ADHOC) baddr = vif->bss_conf.bssid; - if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_STATION_COUNT)) + if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_INVALID_STA)) return -ENOSPC; ret = iwl_mvm_add_int_sta_common(mvm, bsta, baddr, @@ -2742,7 +2742,7 @@ static struct iwl_mvm_sta *iwl_mvm_get_key_sta(struct iwl_mvm *mvm, * station ID, then use AP's station ID. */ if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { + mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) { u8 sta_id = mvmvif->ap_sta_id; sta = rcu_dereference_check(mvm->fw_id_to_mac_id[sta_id], @@ -2954,7 +2954,7 @@ static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm, return sta->addr; if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { + mvmvif->ap_sta_id != IWL_MVM_INVALID_STA) { u8 sta_id = mvmvif->ap_sta_id; sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], lockdep_is_held(&mvm->mutex)); @@ -3151,7 +3151,7 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, { bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); struct iwl_mvm_sta *mvm_sta; - u8 sta_id = IWL_MVM_STATION_COUNT; + u8 sta_id = IWL_MVM_INVALID_STA; int ret, i; lockdep_assert_held(&mvm->mutex); @@ -3408,7 +3408,7 @@ void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); /* Block/unblock all the stations of the given mvmvif */ - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) { sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], lockdep_is_held(&mvm->mutex)); if (IS_ERR_OR_NULL(sta)) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c index 9f160fc58cd0..df7cd87199ea 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tdls.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2014 Intel Mobile Communications GmbH + * Copyright(c) 2017 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 @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2014 Intel Mobile Communications GmbH + * Copyright(c) 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -78,7 +80,7 @@ void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm) lockdep_assert_held(&mvm->mutex); - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) { sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], lockdep_is_held(&mvm->mutex)); if (!sta || IS_ERR(sta) || !sta->tdls) @@ -101,7 +103,7 @@ int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif) lockdep_assert_held(&mvm->mutex); - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) { sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], lockdep_is_held(&mvm->mutex)); if (!sta || IS_ERR(sta) || !sta->tdls) @@ -145,7 +147,7 @@ static void iwl_mvm_tdls_config(struct iwl_mvm *mvm, struct ieee80211_vif *vif) /* populate TDLS peer data */ cnt = 0; - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) { sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], lockdep_is_held(&mvm->mutex)); if (IS_ERR_OR_NULL(sta) || !sta->tdls) @@ -251,7 +253,7 @@ static void iwl_mvm_tdls_update_cs_state(struct iwl_mvm *mvm, iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG); if (state == IWL_MVM_TDLS_SW_IDLE) - mvm->tdls_cs.cur_sta_id = IWL_MVM_STATION_COUNT; + mvm->tdls_cs.cur_sta_id = IWL_MVM_INVALID_STA; } void iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) @@ -305,7 +307,7 @@ iwl_mvm_tdls_check_action(struct iwl_mvm *mvm, /* get the existing peer if it's there */ if (mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE && - mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) { + mvm->tdls_cs.cur_sta_id != IWL_MVM_INVALID_STA) { struct ieee80211_sta *sta = rcu_dereference_protected( mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id], lockdep_is_held(&mvm->mutex)); @@ -523,7 +525,7 @@ void iwl_mvm_tdls_ch_switch_work(struct work_struct *work) iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE); /* station might be gone, in that case do nothing */ - if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT) + if (mvm->tdls_cs.peer.sta_id == IWL_MVM_INVALID_STA) goto out; sta = rcu_dereference_protected( @@ -573,7 +575,7 @@ iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw, sta->addr, chandef->chan->center_freq, chandef->width); /* we only support a single peer for channel switching */ - if (mvm->tdls_cs.peer.sta_id != IWL_MVM_STATION_COUNT) { + if (mvm->tdls_cs.peer.sta_id != IWL_MVM_INVALID_STA) { IWL_DEBUG_TDLS(mvm, "Existing peer. Can't start switch with %pM\n", sta->addr); @@ -633,7 +635,7 @@ void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw, IWL_DEBUG_TDLS(mvm, "TDLS cancel channel switch with %pM\n", sta->addr); /* we only support a single peer for channel switching */ - if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT) { + if (mvm->tdls_cs.peer.sta_id == IWL_MVM_INVALID_STA) { IWL_DEBUG_TDLS(mvm, "No ch switch peer - %pM\n", sta->addr); goto out; } @@ -654,7 +656,7 @@ void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw, mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE) wait_for_phy = true; - mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT; + mvm->tdls_cs.peer.sta_id = IWL_MVM_INVALID_STA; dev_kfree_skb(mvm->tdls_cs.peer.skb); mvm->tdls_cs.peer.skb = NULL; @@ -697,7 +699,7 @@ iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw, if (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE && params->status != 0 && mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT && - mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) { + mvm->tdls_cs.cur_sta_id != IWL_MVM_INVALID_STA) { struct ieee80211_sta *cur_sta; /* make sure it's the same peer */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tof.c b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c index a1947d6f3a2c..16ce8a56b5b9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tof.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tof.c @@ -80,7 +80,7 @@ void iwl_mvm_tof_init(struct iwl_mvm *mvm) if (IWL_MVM_TOF_IS_RESPONDER) { tof_data->responder_cfg.sub_grp_cmd_id = cpu_to_le32(TOF_RESPONDER_CONFIG_CMD); - tof_data->responder_cfg.sta_id = IWL_MVM_STATION_COUNT; + tof_data->responder_cfg.sta_id = IWL_MVM_INVALID_STA; } #endif diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index bec7d9c46087..f9cbd197246f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -356,7 +356,7 @@ static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) struct iwl_mvm_sta *mvmsta; int i, err; - for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) { mvmsta = iwl_mvm_sta_from_staid_protected(mvm, i); if (!mvmsta) continue; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index ffbbe7228f5d..00b6eaca0892 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -598,7 +598,7 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) is_multicast_ether_addr(hdr->addr1)) { u8 ap_sta_id = ACCESS_ONCE(mvmvif->ap_sta_id); - if (ap_sta_id != IWL_MVM_STATION_COUNT) + if (ap_sta_id != IWL_MVM_INVALID_STA) sta_id = ap_sta_id; } else if (iwl_mvm_is_dqa_supported(mvm) && info.control.vif->type == NL80211_IFTYPE_STATION && @@ -896,7 +896,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, if (WARN_ON_ONCE(!mvmsta)) return -1; - if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) + if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_INVALID_STA)) return -1; dev_cmd = iwl_mvm_set_tx_params(mvm, skb, info, hdrlen, @@ -1036,7 +1036,7 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, if (WARN_ON_ONCE(!mvmsta)) return -1; - if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_STATION_COUNT)) + if (WARN_ON_ONCE(mvmsta->sta_id == IWL_MVM_INVALID_STA)) return -1; memcpy(&info, skb->cb, sizeof(info)); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 8f199ff9be87..3054945b949a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -824,7 +824,7 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init) .data = { lq, }, }; - if (WARN_ON(lq->sta_id == IWL_MVM_STATION_COUNT)) + if (WARN_ON(lq->sta_id == IWL_MVM_INVALID_STA)) return -EINVAL; return iwl_mvm_send_cmd(mvm, &cmd); -- cgit From a0b4828c20ce9ab34e6ae8be0148eb55e6a89193 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 12 Jan 2017 16:01:11 +0200 Subject: iwlwifi: mvm: use same scan API for all a000 devices API will be the same regardless of FW compilation. CDB related values will be filled in only for CDB. Cahneg code and names accordingly. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/mvm/fw-api-scan.h | 14 ++-- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 80 ++++++++++++---------- 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h index c78a0c499459..3178eb96e395 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h @@ -516,7 +516,7 @@ struct iwl_scan_dwell { * scan_config_channel_flag * @channel_array: default supported channels */ -struct iwl_scan_config { +struct iwl_scan_config_v1 { __le32 flags; __le32 tx_chains; __le32 rx_chains; @@ -532,7 +532,7 @@ struct iwl_scan_config { #define SCAN_TWO_LMACS 2 -struct iwl_scan_config_cdb { +struct iwl_scan_config { __le32 flags; __le32 tx_chains; __le32 rx_chains; @@ -669,7 +669,7 @@ struct iwl_scan_req_umac { u8 n_channels; __le16 reserved; u8 data[]; - } no_cdb; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */ + } v1; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */ struct { __le32 max_out_time[SCAN_TWO_LMACS]; __le32 suspend_time[SCAN_TWO_LMACS]; @@ -679,13 +679,13 @@ struct iwl_scan_req_umac { u8 n_channels; __le16 reserved; u8 data[]; - } cdb; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_5 */ + } v6; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_6 */ }; } __packed; -#define IWL_SCAN_REQ_UMAC_SIZE_CDB sizeof(struct iwl_scan_req_umac) -#define IWL_SCAN_REQ_UMAC_SIZE (sizeof(struct iwl_scan_req_umac) - \ - 2 * sizeof(__le32)) +#define IWL_SCAN_REQ_UMAC_SIZE sizeof(struct iwl_scan_req_umac) +#define IWL_SCAN_REQ_UMAC_SIZE_V1 (sizeof(struct iwl_scan_req_umac) - \ + 2 * sizeof(__le32)) /** * struct iwl_umac_scan_abort diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 0a64efa844b7..ce6a2b3b021e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -966,11 +966,11 @@ static void iwl_mvm_fill_channels(struct iwl_mvm *mvm, u8 *channels) channels[j] = band->channels[i].hw_value; } -static void iwl_mvm_fill_scan_config(struct iwl_mvm *mvm, void *config, - u32 flags, u8 channel_flags) +static void iwl_mvm_fill_scan_config_v1(struct iwl_mvm *mvm, void *config, + u32 flags, u8 channel_flags) { enum iwl_mvm_scan_type type = iwl_mvm_get_scan_type(mvm, false); - struct iwl_scan_config *cfg = config; + struct iwl_scan_config_v1 *cfg = config; cfg->flags = cpu_to_le32(flags); cfg->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); @@ -989,11 +989,11 @@ static void iwl_mvm_fill_scan_config(struct iwl_mvm *mvm, void *config, iwl_mvm_fill_channels(mvm, cfg->channel_array); } -static void iwl_mvm_fill_scan_config_cdb(struct iwl_mvm *mvm, void *config, - u32 flags, u8 channel_flags) +static void iwl_mvm_fill_scan_config(struct iwl_mvm *mvm, void *config, + u32 flags, u8 channel_flags) { enum iwl_mvm_scan_type type = iwl_mvm_get_scan_type(mvm, false); - struct iwl_scan_config_cdb *cfg = config; + struct iwl_scan_config *cfg = config; cfg->flags = cpu_to_le32(flags); cfg->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); @@ -1001,10 +1001,14 @@ static void iwl_mvm_fill_scan_config_cdb(struct iwl_mvm *mvm, void *config, cfg->legacy_rates = iwl_mvm_scan_config_rates(mvm); cfg->out_of_channel_time[0] = cpu_to_le32(scan_timing[type].max_out_time); - cfg->out_of_channel_time[1] = - cpu_to_le32(scan_timing[type].max_out_time); cfg->suspend_time[0] = cpu_to_le32(scan_timing[type].suspend_time); - cfg->suspend_time[1] = cpu_to_le32(scan_timing[type].suspend_time); + + if (iwl_mvm_is_cdb_supported(mvm)) { + cfg->suspend_time[1] = + cpu_to_le32(scan_timing[type].suspend_time); + cfg->out_of_channel_time[1] = + cpu_to_le32(scan_timing[type].max_out_time); + } iwl_mvm_fill_scan_dwell(mvm, &cfg->dwell, &scan_timing[type]); @@ -1039,10 +1043,10 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) return 0; } - if (iwl_mvm_is_cdb_supported(mvm)) - cmd_size = sizeof(struct iwl_scan_config_cdb); - else + if (iwl_mvm_has_new_tx_api(mvm)) cmd_size = sizeof(struct iwl_scan_config); + else + cmd_size = sizeof(struct iwl_scan_config_v1); cmd_size += mvm->fw->ucode_capa.n_scan_channels; cfg = kzalloc(cmd_size, GFP_KERNEL); @@ -1068,13 +1072,13 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) IWL_CHANNEL_FLAG_EBS_ADD | IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE; - if (iwl_mvm_is_cdb_supported(mvm)) { + if (iwl_mvm_has_new_tx_api(mvm)) { flags |= (type == IWL_SCAN_TYPE_FRAGMENTED) ? SCAN_CONFIG_FLAG_SET_LMAC2_FRAGMENTED : SCAN_CONFIG_FLAG_CLEAR_LMAC2_FRAGMENTED; - iwl_mvm_fill_scan_config_cdb(mvm, cfg, flags, channel_flags); - } else { iwl_mvm_fill_scan_config(mvm, cfg, flags, channel_flags); + } else { + iwl_mvm_fill_scan_config_v1(mvm, cfg, flags, channel_flags); } cmd.data[0] = cfg; @@ -1119,16 +1123,20 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, } cmd->fragmented_dwell = timing->dwell_fragmented; - if (iwl_mvm_is_cdb_supported(mvm)) { - cmd->cdb.max_out_time[0] = cpu_to_le32(timing->max_out_time); - cmd->cdb.suspend_time[0] = cpu_to_le32(timing->suspend_time); - cmd->cdb.max_out_time[1] = cpu_to_le32(timing->max_out_time); - cmd->cdb.suspend_time[1] = cpu_to_le32(timing->suspend_time); - cmd->cdb.scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); + if (iwl_mvm_has_new_tx_api(mvm)) { + cmd->v6.scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); + cmd->v6.max_out_time[0] = cpu_to_le32(timing->max_out_time); + cmd->v6.suspend_time[0] = cpu_to_le32(timing->suspend_time); + if (iwl_mvm_is_cdb_supported(mvm)) { + cmd->v6.max_out_time[1] = + cpu_to_le32(timing->max_out_time); + cmd->v6.suspend_time[1] = + cpu_to_le32(timing->suspend_time); + } } else { - cmd->no_cdb.max_out_time = cpu_to_le32(timing->max_out_time); - cmd->no_cdb.suspend_time = cpu_to_le32(timing->suspend_time); - cmd->no_cdb.scan_priority = + cmd->v1.max_out_time = cpu_to_le32(timing->max_out_time); + cmd->v1.suspend_time = cpu_to_le32(timing->suspend_time); + cmd->v1.scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); } @@ -1207,8 +1215,8 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int type) { struct iwl_scan_req_umac *cmd = mvm->scan_cmd; - void *cmd_data = iwl_mvm_is_cdb_supported(mvm) ? - (void *)&cmd->cdb.data : (void *)&cmd->no_cdb.data; + void *cmd_data = iwl_mvm_has_new_tx_api(mvm) ? + (void *)&cmd->v6.data : (void *)&cmd->v1.data; struct iwl_scan_req_umac_tail *sec_part = cmd_data + sizeof(struct iwl_scan_channel_cfg_umac) * mvm->fw->ucode_capa.n_scan_channels; @@ -1245,12 +1253,12 @@ static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; - if (iwl_mvm_is_cdb_supported(mvm)) { - cmd->cdb.channel_flags = channel_flags; - cmd->cdb.n_channels = params->n_channels; + if (iwl_mvm_has_new_tx_api(mvm)) { + cmd->v6.channel_flags = channel_flags; + cmd->v6.n_channels = params->n_channels; } else { - cmd->no_cdb.channel_flags = channel_flags; - cmd->no_cdb.n_channels = params->n_channels; + cmd->v1.channel_flags = channel_flags; + cmd->v1.n_channels = params->n_channels; } iwl_scan_build_ssids(params, sec_part->direct_scan, &ssid_bitmap); @@ -1692,10 +1700,10 @@ static int iwl_mvm_scan_stop_wait(struct iwl_mvm *mvm, int type) int iwl_mvm_scan_size(struct iwl_mvm *mvm) { - int base_size = IWL_SCAN_REQ_UMAC_SIZE; + int base_size = IWL_SCAN_REQ_UMAC_SIZE_V1; - if (iwl_mvm_is_cdb_supported(mvm)) - base_size = IWL_SCAN_REQ_UMAC_SIZE_CDB; + if (iwl_mvm_has_new_tx_api(mvm)) + base_size = IWL_SCAN_REQ_UMAC_SIZE; if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) return base_size + -- cgit From 09a2e25d10cd85d981fb19fdcf1bc460f1706e22 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Mon, 19 Dec 2016 14:10:35 +0200 Subject: iwlwifi: mvm: disable multi-queue for a000 devices Firmware isn't configuring multi RX queue hardware yet in the self init mode. Disable it for now until we have an API that enables it. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 61cafb541554..4c5088643b2b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1240,7 +1240,8 @@ int iwl_mvm_up(struct iwl_mvm *mvm) } /* Init RSS configuration */ - if (iwl_mvm_has_new_rx_api(mvm)) { + /* TODO - remove a000 disablement when we have RXQ config API */ + if (iwl_mvm_has_new_rx_api(mvm) && !iwl_mvm_has_new_tx_api(mvm)) { ret = iwl_send_rss_cfg_cmd(mvm); if (ret) { IWL_ERR(mvm, "Failed to configure RSS queues: %d\n", -- cgit From c386dacb4ed681f26f6965b3e0f0448eeabfbb13 Mon Sep 17 00:00:00 2001 From: Haim Dreyfuss Date: Wed, 28 Dec 2016 15:58:22 +0200 Subject: iwlwifi: mvm: refactor SAR init to prepare for dynamic SAR We are adding support for dynamic TX power tables for SAR (specific absorption rate) compliance. Currently, we only support a single (static) TX power table, which is read from ACPI, and use it statically. To prepare for more tables that can be switched dynamically, refactor the SAR init flow to allow reusage and add the current static table as a single entry in an array of tables. Signed-off-by: Haim Dreyfuss Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 155 ++++++++++++++++----------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 17 ++- 2 files changed, 107 insertions(+), 65 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 4c5088643b2b..1679104e19e9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -990,85 +990,87 @@ static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) sizeof(cmd), &cmd); } -#define ACPI_WRDS_METHOD "WRDS" -#define ACPI_WRDS_WIFI (0x07) -#define ACPI_WRDS_TABLE_SIZE 10 +#ifdef CONFIG_ACPI +#define ACPI_WRDS_METHOD "WRDS" +#define ACPI_WRDS_WIFI (0x07) +#define ACPI_WRDS_WIFI_DATA_SIZE (IWL_MVM_SAR_TABLE_SIZE + 2) + +static int iwl_mvm_sar_set_profile(struct iwl_mvm *mvm, + union acpi_object *table, + struct iwl_mvm_sar_profile *profile, + bool enabled) +{ + int i; -struct iwl_mvm_sar_table { - bool enabled; - u8 values[ACPI_WRDS_TABLE_SIZE]; -}; + profile->enabled = enabled; -#ifdef CONFIG_ACPI -static int iwl_mvm_sar_get_wrds(struct iwl_mvm *mvm, union acpi_object *wrds, - struct iwl_mvm_sar_table *sar_table) + for (i = 0; i < IWL_MVM_SAR_TABLE_SIZE; i++) { + if ((table[i].type != ACPI_TYPE_INTEGER) || + (table[i].integer.value > U8_MAX)) + return -EINVAL; + + profile->table[i] = table[i].integer.value; + } + + return 0; +} + +static union acpi_object *iwl_mvm_sar_find_wifi_pkg(struct iwl_mvm *mvm, + union acpi_object *data, + int data_size) { - union acpi_object *data_pkg; - u32 i; + int i; + union acpi_object *wifi_pkg; - /* We need at least two packages, one for the revision and one + /* + * We need at least two packages, one for the revision and one * for the data itself. Also check that the revision is valid * (i.e. it is an integer set to 0). - */ - if (wrds->type != ACPI_TYPE_PACKAGE || - wrds->package.count < 2 || - wrds->package.elements[0].type != ACPI_TYPE_INTEGER || - wrds->package.elements[0].integer.value != 0) { - IWL_DEBUG_RADIO(mvm, "Unsupported wrds structure\n"); - return -EINVAL; + */ + if (data->type != ACPI_TYPE_PACKAGE || + data->package.count < 2 || + data->package.elements[0].type != ACPI_TYPE_INTEGER || + data->package.elements[0].integer.value != 0) { + IWL_DEBUG_RADIO(mvm, "Unsupported packages structure\n"); + return ERR_PTR(-EINVAL); } /* loop through all the packages to find the one for WiFi */ - for (i = 1; i < wrds->package.count; i++) { + for (i = 1; i < data->package.count; i++) { union acpi_object *domain; - data_pkg = &wrds->package.elements[i]; + wifi_pkg = &data->package.elements[i]; /* Skip anything that is not a package with the right * amount of elements (i.e. domain_type, - * enabled/disabled plus the sar table size. + * enabled/disabled plus the actual data size. */ - if (data_pkg->type != ACPI_TYPE_PACKAGE || - data_pkg->package.count != ACPI_WRDS_TABLE_SIZE + 2) + if (wifi_pkg->type != ACPI_TYPE_PACKAGE || + wifi_pkg->package.count != data_size) continue; - domain = &data_pkg->package.elements[0]; + domain = &wifi_pkg->package.elements[0]; if (domain->type == ACPI_TYPE_INTEGER && domain->integer.value == ACPI_WRDS_WIFI) break; - data_pkg = NULL; + wifi_pkg = NULL; } - if (!data_pkg) - return -ENOENT; - - if (data_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) - return -EINVAL; - - sar_table->enabled = !!(data_pkg->package.elements[1].integer.value); - - for (i = 0; i < ACPI_WRDS_TABLE_SIZE; i++) { - union acpi_object *entry; - - entry = &data_pkg->package.elements[i + 2]; - if ((entry->type != ACPI_TYPE_INTEGER) || - (entry->integer.value > U8_MAX)) - return -EINVAL; - - sar_table->values[i] = entry->integer.value; - } + if (!wifi_pkg) + return ERR_PTR(-ENOENT); - return 0; + return wifi_pkg; } -static int iwl_mvm_sar_get_table(struct iwl_mvm *mvm, - struct iwl_mvm_sar_table *sar_table) +static int iwl_mvm_sar_get_wrds_table(struct iwl_mvm *mvm) { + union acpi_object *wifi_pkg, *table; acpi_handle root_handle; acpi_handle handle; struct acpi_buffer wrds = {ACPI_ALLOCATE_BUFFER, NULL}; acpi_status status; + bool enabled; int ret; root_handle = ACPI_HANDLE(mvm->dev); @@ -1093,22 +1095,36 @@ static int iwl_mvm_sar_get_table(struct iwl_mvm *mvm, return -ENOENT; } - ret = iwl_mvm_sar_get_wrds(mvm, wrds.pointer, sar_table); - kfree(wrds.pointer); + wifi_pkg = iwl_mvm_sar_find_wifi_pkg(mvm, wrds.pointer, + ACPI_WRDS_WIFI_DATA_SIZE); + if (IS_ERR(wifi_pkg)) { + ret = PTR_ERR(wifi_pkg); + goto out_free; + } + + if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) { + ret = -EINVAL; + goto out_free; + } + + enabled = !!(wifi_pkg->package.elements[1].integer.value); + /* position of the actual table */ + table = &wifi_pkg->package.elements[2]; + + /* The profile from WRDS is officially profile 1, but goes + * into sar_profiles[0] (because we don't have a profile 0). + */ + ret = iwl_mvm_sar_set_profile(mvm, table, &mvm->sar_profiles[0], + enabled); + +out_free: + kfree(wrds.pointer); return ret; } -#else /* CONFIG_ACPI */ -static int iwl_mvm_sar_get_table(struct iwl_mvm *mvm, - struct iwl_mvm_sar_table *sar_table) -{ - return -ENOENT; -} -#endif /* CONFIG_ACPI */ static int iwl_mvm_sar_init(struct iwl_mvm *mvm) { - struct iwl_mvm_sar_table sar_table; struct iwl_dev_tx_power_cmd cmd = { .v3.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), }; @@ -1118,7 +1134,7 @@ static int iwl_mvm_sar_init(struct iwl_mvm *mvm) if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TX_POWER_ACK)) len = sizeof(cmd.v3); - ret = iwl_mvm_sar_get_table(mvm, &sar_table); + ret = iwl_mvm_sar_get_wrds_table(mvm); if (ret < 0) { IWL_DEBUG_RADIO(mvm, "SAR BIOS table invalid or unavailable. (%d)\n", @@ -1127,22 +1143,23 @@ static int iwl_mvm_sar_init(struct iwl_mvm *mvm) return 0; } - if (!sar_table.enabled) + /* if profile 0 is disabled, there's nothing else to do here */ + if (!mvm->sar_profiles[0].enabled) return 0; IWL_DEBUG_RADIO(mvm, "Sending REDUCE_TX_POWER_CMD per chain\n"); BUILD_BUG_ON(IWL_NUM_CHAIN_LIMITS * IWL_NUM_SUB_BANDS != - ACPI_WRDS_TABLE_SIZE); + IWL_MVM_SAR_TABLE_SIZE); for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { IWL_DEBUG_RADIO(mvm, " Chain[%d]:\n", i); for (j = 0; j < IWL_NUM_SUB_BANDS; j++) { idx = (i * IWL_NUM_SUB_BANDS) + j; cmd.v3.per_chain_restriction[i][j] = - cpu_to_le16(sar_table.values[idx]); + cpu_to_le16(mvm->sar_profiles[0].table[idx]); IWL_DEBUG_RADIO(mvm, " Band[%d] = %d * .125dBm\n", - j, sar_table.values[idx]); + j, mvm->sar_profiles[0].table[idx]); } } @@ -1153,6 +1170,18 @@ static int iwl_mvm_sar_init(struct iwl_mvm *mvm) return ret; } +#else /* CONFIG_ACPI */ +static inline int iwl_mvm_sar_get_wrds_table(struct iwl_mvm *mvm) +{ + return -ENOENT; +} + +static inline int iwl_mvm_sar_init(struct iwl_mvm *mvm) +{ + return 0; +} +#endif /* CONFIG_ACPI */ + static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm) { int ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 91bd5ebd5b65..0c35d9ebd4ac 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -710,6 +710,16 @@ enum iwl_mvm_queue_status { #define IWL_MVM_DQA_QUEUE_TIMEOUT (5 * HZ) #define IWL_MVM_NUM_CIPHERS 10 +#ifdef CONFIG_ACPI +#define IWL_MVM_SAR_TABLE_SIZE 10 +#define IWL_MVM_SAR_PROFILE_NUM 1 + +struct iwl_mvm_sar_profile { + bool enabled; + u8 table[IWL_MVM_SAR_TABLE_SIZE]; +}; +#endif + struct iwl_mvm { /* for logger access */ struct device *dev; @@ -1039,6 +1049,9 @@ struct iwl_mvm { bool drop_bcn_ap_mode; struct delayed_work cs_tx_unblock_dwork; +#ifdef CONFIG_ACPI + struct iwl_mvm_sar_profile sar_profiles[IWL_MVM_SAR_PROFILE_NUM]; +#endif }; /* Extract MVM priv from op_mode and _hw */ -- cgit From 42ce76d615e7cb2b57be90ce0f357604a7903253 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 11 Jan 2017 23:36:30 +0200 Subject: iwlwifi: mvm: spin off SAR profile selection function For dynamic SAR, we will need to select the current profile from different places. In preparation for that, spin the profile selection code out of iwl_mvm_sar_init(). Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 4 ++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 73 +++++++++++++++++++--------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 + 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index d74ae15b2d85..119a3bd92c50 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -2116,6 +2116,10 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) */ iwl_mvm_update_changed_regdom(mvm); + if (!unified_image) + /* Re-configure default SAR profile */ + iwl_mvm_sar_select_profile(mvm, 1, 1); + if (mvm->net_detect) { /* If this is a non-unified image, we restart the FW, * so no need to stop the netdetect scan. If that diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 1679104e19e9..6360361c576d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1123,49 +1123,78 @@ out_free: return ret; } -static int iwl_mvm_sar_init(struct iwl_mvm *mvm) +int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) { struct iwl_dev_tx_power_cmd cmd = { .v3.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), }; - int ret, i, j, idx; + int i, j, idx; + int profs[IWL_NUM_CHAIN_LIMITS] = { prof_a, prof_b }; int len = sizeof(cmd); + BUILD_BUG_ON(IWL_NUM_CHAIN_LIMITS < 2); + BUILD_BUG_ON(IWL_NUM_CHAIN_LIMITS * IWL_NUM_SUB_BANDS != + IWL_MVM_SAR_TABLE_SIZE); + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TX_POWER_ACK)) len = sizeof(cmd.v3); - ret = iwl_mvm_sar_get_wrds_table(mvm); - if (ret < 0) { - IWL_DEBUG_RADIO(mvm, - "SAR BIOS table invalid or unavailable. (%d)\n", - ret); - /* we don't fail if the table is not available */ - return 0; - } + for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { + struct iwl_mvm_sar_profile *prof; - /* if profile 0 is disabled, there's nothing else to do here */ - if (!mvm->sar_profiles[0].enabled) - return 0; + /* don't allow SAR to be disabled (profile 0 means disable) */ + if (profs[i] == 0) + return -EPERM; - IWL_DEBUG_RADIO(mvm, "Sending REDUCE_TX_POWER_CMD per chain\n"); + /* we are off by one, so allow up to IWL_MVM_SAR_PROFILE_NUM */ + if (profs[i] > IWL_MVM_SAR_PROFILE_NUM) + return -EINVAL; - BUILD_BUG_ON(IWL_NUM_CHAIN_LIMITS * IWL_NUM_SUB_BANDS != - IWL_MVM_SAR_TABLE_SIZE); + /* profiles go from 1 to 4, so decrement to access the array */ + prof = &mvm->sar_profiles[profs[i] - 1]; + + /* if the profile is disabled, do nothing */ + if (!prof->enabled) { + IWL_DEBUG_RADIO(mvm, "SAR profile %d is disabled.\n", + profs[i]); + /* if one of the profiles is disabled, we fail all */ + return -ENOENT; + } - for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { IWL_DEBUG_RADIO(mvm, " Chain[%d]:\n", i); for (j = 0; j < IWL_NUM_SUB_BANDS; j++) { idx = (i * IWL_NUM_SUB_BANDS) + j; cmd.v3.per_chain_restriction[i][j] = - cpu_to_le16(mvm->sar_profiles[0].table[idx]); + cpu_to_le16(prof->table[idx]); IWL_DEBUG_RADIO(mvm, " Band[%d] = %d * .125dBm\n", - j, mvm->sar_profiles[0].table[idx]); + j, prof->table[idx]); } } - ret = iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); - if (ret) - IWL_ERR(mvm, "failed to set per-chain TX power: %d\n", ret); + IWL_DEBUG_RADIO(mvm, "Sending REDUCE_TX_POWER_CMD per chain\n"); + + return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); +} + +static int iwl_mvm_sar_init(struct iwl_mvm *mvm) +{ + int ret; + + ret = iwl_mvm_sar_get_wrds_table(mvm); + if (ret < 0) { + IWL_DEBUG_RADIO(mvm, + "SAR BIOS table invalid or unavailable. (%d)\n", + ret); + /* we don't fail if the table is not available */ + return 0; + } + + /* choose profile 1 (WRDS) as default for both chains */ + ret = iwl_mvm_sar_select_profile(mvm, 1, 1); + + /* if we don't have profile 0 from BIOS, just skip it */ + if (ret == -ENOENT) + return 0; return ret; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 0c35d9ebd4ac..f4e8fa3765fa 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1818,5 +1818,6 @@ int iwl_mvm_send_lqm_cmd(struct ieee80211_vif *vif, enum iwl_lqm_cmd_operatrions operation, u32 duration, u32 timeout); bool iwl_mvm_lqm_active(struct iwl_mvm *mvm); +int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b); #endif /* __IWL_MVM_H__ */ -- cgit From 6996490501ed8011964e1b1403ae2d104337843c Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Thu, 12 Jan 2017 12:43:12 +0200 Subject: iwlwifi: mvm: add support for EWRD (Dynamic SAR) ACPI table Dynamic SAR allows changing TX power limits at runtime to comply with SAR regulations on multiple form factors (e.g. tablet vs. clamshell mode). To support this, a new table was added to ACPI, which is called Extended Wireless Regulatory Descriptor (EWRD). This table allows OEMs to define different TX power profiles for each form-factor or usage mode. Read this new table and store it in our SAR profiles table, in preparation for Dynamic SAR support. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 114 +++++++++++++++++++++++---- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 11 ++- 2 files changed, 108 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 6360361c576d..2a4c952ef01a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -992,8 +992,11 @@ static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) #ifdef CONFIG_ACPI #define ACPI_WRDS_METHOD "WRDS" -#define ACPI_WRDS_WIFI (0x07) +#define ACPI_EWRD_METHOD "EWRD" +#define ACPI_WIFI_DOMAIN (0x07) #define ACPI_WRDS_WIFI_DATA_SIZE (IWL_MVM_SAR_TABLE_SIZE + 2) +#define ACPI_EWRD_WIFI_DATA_SIZE ((IWL_MVM_SAR_PROFILE_NUM - 1) * \ + IWL_MVM_SAR_TABLE_SIZE + 3) static int iwl_mvm_sar_set_profile(struct iwl_mvm *mvm, union acpi_object *table, @@ -1051,7 +1054,7 @@ static union acpi_object *iwl_mvm_sar_find_wifi_pkg(struct iwl_mvm *mvm, domain = &wifi_pkg->package.elements[0]; if (domain->type == ACPI_TYPE_INTEGER && - domain->integer.value == ACPI_WRDS_WIFI) + domain->integer.value == ACPI_WIFI_DOMAIN) break; wifi_pkg = NULL; @@ -1123,6 +1126,78 @@ out_free: return ret; } +static int iwl_mvm_sar_get_ewrd_table(struct iwl_mvm *mvm) +{ + union acpi_object *wifi_pkg; + acpi_handle root_handle; + acpi_handle handle; + struct acpi_buffer ewrd = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + bool enabled; + int i, n_profiles, ret; + + root_handle = ACPI_HANDLE(mvm->dev); + if (!root_handle) { + IWL_DEBUG_RADIO(mvm, + "Could not retrieve root port ACPI handle\n"); + return -ENOENT; + } + + /* Get the method's handle */ + status = acpi_get_handle(root_handle, (acpi_string)ACPI_EWRD_METHOD, + &handle); + if (ACPI_FAILURE(status)) { + IWL_DEBUG_RADIO(mvm, "EWRD method not found\n"); + return -ENOENT; + } + + /* Call EWRD with no arguments */ + status = acpi_evaluate_object(handle, NULL, NULL, &ewrd); + if (ACPI_FAILURE(status)) { + IWL_DEBUG_RADIO(mvm, "EWRD invocation failed (0x%x)\n", status); + return -ENOENT; + } + + wifi_pkg = iwl_mvm_sar_find_wifi_pkg(mvm, ewrd.pointer, + ACPI_EWRD_WIFI_DATA_SIZE); + if (IS_ERR(wifi_pkg)) { + ret = PTR_ERR(wifi_pkg); + goto out_free; + } + + if ((wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) || + (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER)) { + ret = -EINVAL; + goto out_free; + } + + enabled = !!(wifi_pkg->package.elements[1].integer.value); + n_profiles = wifi_pkg->package.elements[2].integer.value; + + for (i = 0; i < n_profiles; i++) { + /* the tables start at element 3 */ + static int pos = 3; + + /* The EWRD profiles officially go from 2 to 4, but we + * save them in sar_profiles[1-3] (because we don't + * have profile 0). So in the array we start from 1. + */ + ret = iwl_mvm_sar_set_profile(mvm, + &wifi_pkg->package.elements[pos], + &mvm->sar_profiles[i + 1], + enabled); + if (ret < 0) + break; + + /* go to the next table */ + pos += IWL_MVM_SAR_TABLE_SIZE; + } + +out_free: + kfree(ewrd.pointer); + return ret; +} + int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) { struct iwl_dev_tx_power_cmd cmd = { @@ -1176,6 +1251,18 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); } +#else /* CONFIG_ACPI */ +static int iwl_mvm_sar_get_wrds_table(struct iwl_mvm *mvm) +{ + return -ENOENT; +} + +static int iwl_mvm_sar_get_ewrd_table(struct iwl_mvm *mvm) +{ + return -ENOENT; +} +#endif /* CONFIG_ACPI */ + static int iwl_mvm_sar_init(struct iwl_mvm *mvm) { int ret; @@ -1183,12 +1270,19 @@ static int iwl_mvm_sar_init(struct iwl_mvm *mvm) ret = iwl_mvm_sar_get_wrds_table(mvm); if (ret < 0) { IWL_DEBUG_RADIO(mvm, - "SAR BIOS table invalid or unavailable. (%d)\n", + "WRDS SAR BIOS table invalid or unavailable. (%d)\n", ret); - /* we don't fail if the table is not available */ + /* if not available, don't fail and don't bother with EWRD */ return 0; } + ret = iwl_mvm_sar_get_ewrd_table(mvm); + /* if EWRD is not available, we can still use WRDS, so don't fail */ + if (ret < 0) + IWL_DEBUG_RADIO(mvm, + "EWRD SAR BIOS table invalid or unavailable. (%d)\n", + ret); + /* choose profile 1 (WRDS) as default for both chains */ ret = iwl_mvm_sar_select_profile(mvm, 1, 1); @@ -1199,18 +1293,6 @@ static int iwl_mvm_sar_init(struct iwl_mvm *mvm) return ret; } -#else /* CONFIG_ACPI */ -static inline int iwl_mvm_sar_get_wrds_table(struct iwl_mvm *mvm) -{ - return -ENOENT; -} - -static inline int iwl_mvm_sar_init(struct iwl_mvm *mvm) -{ - return 0; -} -#endif /* CONFIG_ACPI */ - static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm) { int ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index f4e8fa3765fa..2205c9f8bb58 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -712,7 +712,7 @@ enum iwl_mvm_queue_status { #ifdef CONFIG_ACPI #define IWL_MVM_SAR_TABLE_SIZE 10 -#define IWL_MVM_SAR_PROFILE_NUM 1 +#define IWL_MVM_SAR_PROFILE_NUM 4 struct iwl_mvm_sar_profile { bool enabled; @@ -1818,6 +1818,15 @@ int iwl_mvm_send_lqm_cmd(struct ieee80211_vif *vif, enum iwl_lqm_cmd_operatrions operation, u32 duration, u32 timeout); bool iwl_mvm_lqm_active(struct iwl_mvm *mvm); + +#ifdef CONFIG_ACPI int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b); +#else +static inline +int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) +{ + return -ENOENT; +} +#endif /* CONFIG_ACPI */ #endif /* __IWL_MVM_H__ */ -- cgit From c47de665751027781fe07fba1959911ab194add4 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 29 Sep 2016 17:28:33 +0300 Subject: iwlwifi: mvm: support new TX API Support the new TX command API for a000 devices. Command is a very slim version of current TX command. Generalize iwl_mvm_tx_mpdu to get rid of TX command dependencies. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h | 42 +++++++++++++- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 66 ++++++++++++++++------ 2 files changed, 91 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h index 85744fa3bd4d..f83ee6e760d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h @@ -6,7 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -32,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -123,6 +124,20 @@ enum iwl_tx_flags { TX_CMD_FLG_HCCA_CHUNK = BIT(31) }; /* TX_FLAGS_BITS_API_S_VER_1 */ +/** + * enum iwl_tx_cmd_flags - bitmasks for tx_flags in TX command for a000 + * @IWL_TX_FLAGS_CMD_RATE: use rate from the TX command + * @IWL_TX_FLAGS_ENCRYPT_DIS: frame should not be encrypted, even if it belongs + * to a secured STA + * @IWL_TX_FLAGS_HIGH_PRI: high priority frame (like EAPOL) - can affect rate + * selection, retry limits and BT kill + */ +enum iwl_tx_cmd_flags { + IWL_TX_FLAGS_CMD_RATE = BIT(0), + IWL_TX_FLAGS_ENCRYPT_DIS = BIT(1), + IWL_TX_FLAGS_HIGH_PRI = BIT(2), +}; /* TX_FLAGS_BITS_API_S_VER_3 */ + /** * enum iwl_tx_pm_timeouts - pm timeout values in TX command * @PM_FRAME_NONE: no need to suspend sleep mode @@ -301,6 +316,31 @@ struct iwl_tx_cmd { struct ieee80211_hdr hdr[0]; } __packed; /* TX_CMD_API_S_VER_6 */ +struct iwl_dram_sec_info { + __le32 pn_low; + __le16 pn_high; + __le16 aux_info; +} __packed; /* DRAM_SEC_INFO_API_S_VER_1 */ + +/** + * struct iwl_tx_cmd_gen2 - TX command struct to FW for a000 devices + * ( TX_CMD = 0x1c ) + * @len: in bytes of the payload, see below for details + * @offload_assist: TX offload configuration + * @tx_flags: combination of &iwl_tx_cmd_flags + * @dram_info: FW internal DRAM storage + * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is + * cleared. Combination of RATE_MCS_* + */ +struct iwl_tx_cmd_gen2 { + __le16 len; + __le16 offload_assist; + __le32 flags; + struct iwl_dram_sec_info dram_info; + __le32 rate_n_flags; + struct ieee80211_hdr hdr[0]; +} __packed; /* TX_CMD_API_S_VER_7 */ + /* * TX response related data */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 00b6eaca0892..878367d9aa85 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -475,6 +475,39 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, memset(dev_cmd, 0, sizeof(*dev_cmd)); dev_cmd->hdr.cmd = TX_CMD; + + if (iwl_mvm_has_new_tx_api(mvm)) { + struct iwl_tx_cmd_gen2 *cmd = (void *)dev_cmd->payload; + u16 offload_assist = iwl_mvm_tx_csum(mvm, skb, hdr, info); + + /* padding is inserted later in transport */ + /* FIXME - check for AMSDU may need to be removed */ + if (ieee80211_hdrlen(hdr->frame_control) % 4 && + !(offload_assist & BIT(TX_CMD_OFFLD_AMSDU))) + offload_assist |= BIT(TX_CMD_OFFLD_PAD); + + cmd->offload_assist |= cpu_to_le16(offload_assist); + + /* Total # bytes to be transmitted */ + cmd->len = cpu_to_le16((u16)skb->len); + + /* Copy MAC header from skb into command buffer */ + memcpy(cmd->hdr, hdr, hdrlen); + + if (!info->control.hw_key) + cmd->flags |= cpu_to_le32(IWL_TX_FLAGS_ENCRYPT_DIS); + + /* For data packets rate info comes from the fw */ + if (ieee80211_is_data(hdr->frame_control) && sta) + goto out; + + cmd->flags |= cpu_to_le32(IWL_TX_FLAGS_CMD_RATE); + cmd->rate_n_flags = + cpu_to_le32(iwl_mvm_get_tx_rate(mvm, info, sta)); + + goto out; + } + tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; if (info->control.hw_key) @@ -484,6 +517,10 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, iwl_mvm_set_tx_cmd_rate(mvm, tx_cmd, info, sta, hdr->frame_control); + /* Copy MAC header from skb into command buffer */ + memcpy(tx_cmd->hdr, hdr, hdrlen); + +out: return dev_cmd; } @@ -541,7 +578,6 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) struct ieee80211_tx_info *skb_info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_info info; struct iwl_device_cmd *dev_cmd; - struct iwl_tx_cmd *tx_cmd; u8 sta_id; int hdrlen = ieee80211_hdrlen(hdr->frame_control); int queue; @@ -616,11 +652,6 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) /* From now on, we cannot access info->control */ iwl_mvm_skb_prepare_status(skb, dev_cmd); - tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; - - /* Copy MAC header from skb into command buffer */ - memcpy(tx_cmd->hdr, hdr, hdrlen); - if (iwl_trans_tx(mvm->trans, skb, dev_cmd, queue)) { iwl_trans_free_tx_cmd(mvm->trans, dev_cmd); return -1; @@ -881,7 +912,6 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct iwl_mvm_sta *mvmsta; struct iwl_device_cmd *dev_cmd; - struct iwl_tx_cmd *tx_cmd; __le16 fc; u16 seq_number = 0; u8 tid = IWL_MAX_TID_COUNT; @@ -904,8 +934,6 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, if (!dev_cmd) goto drop; - tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; - /* * we handle that entirely ourselves -- for uAPSD the firmware * will always send a notification, and for PS-Poll responses @@ -926,18 +954,27 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) goto drop_unlock_sta; - seq_number = mvmsta->tid_data[tid].seq_number; - seq_number &= IEEE80211_SCTL_SEQ; - hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - hdr->seq_ctrl |= cpu_to_le16(seq_number); is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU; if (WARN_ON_ONCE(is_ampdu && mvmsta->tid_data[tid].state != IWL_AGG_ON)) goto drop_unlock_sta; + + seq_number = mvmsta->tid_data[tid].seq_number; + seq_number &= IEEE80211_SCTL_SEQ; + + if (!iwl_mvm_has_new_tx_api(mvm)) { + struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload; + + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(seq_number); + /* update the tx_cmd hdr as it was already copied */ + tx_cmd->hdr->seq_ctrl = hdr->seq_ctrl; + } } if (iwl_mvm_is_dqa_supported(mvm) || is_ampdu) txq_id = mvmsta->tid_data[tid].txq_id; + if (sta->tdls && !iwl_mvm_is_dqa_supported(mvm)) { /* default to TID 0 for non-QoS packets */ u8 tdls_tid = tid == IWL_MAX_TID_COUNT ? 0 : tid; @@ -945,9 +982,6 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, txq_id = mvmsta->hw_queue[tid_to_mac80211_ac[tdls_tid]]; } - /* Copy MAC header from skb into command buffer */ - memcpy(tx_cmd->hdr, hdr, hdrlen); - WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM); /* Check if TXQ needs to be allocated or re-activated */ -- cgit From 6b35ff91572f7fc42ec0026f512a8d274071163b Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 29 Sep 2016 14:36:19 +0300 Subject: iwlwifi: pcie: introduce a000 TX queues management In a000 devices the TX handling is different in a few ways: * Queues are allocated dynamically * DQA is enabled by default * Driver shouldn't access TFH registers - ucode configures it all in SCD_QUEUE_CFG command Support all this in a new API with op mode, where op mode sends the command, transport will allocate the queue dynamically, fill in DMA properties, send the command to FW and get the ID back. Current implementation only sets the new transport API and fills the DMA properties. Future patches will complete the other parts. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/Makefile | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 70 ++++++++- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 6 + drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 6 +- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 162 +++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 8 +- 6 files changed, 240 insertions(+), 14 deletions(-) create mode 100644 drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index b27bcac8f5db..411cb91c102f 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -7,7 +7,7 @@ iwlwifi-objs += iwl-notif-wait.o iwlwifi-objs += iwl-eeprom-read.o iwl-eeprom-parse.o iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o -iwlwifi-objs += pcie/ctxt-info.o pcie/trans-gen2.o +iwlwifi-objs += pcie/ctxt-info.o pcie/trans-gen2.o pcie/tx-gen2.o iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o iwl-9000.o iwl-a000.o iwlwifi-objs += iwl-trans.o diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 1b4b62ede2c4..723402ee7be2 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -7,7 +7,7 @@ * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,7 +34,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -530,6 +530,38 @@ struct iwl_trans_txq_scd_cfg { int frame_limit; }; +/** + * struct iwl_tx_queue_cfg_cmd - txq hw scheduler config command + * @token: token of the command + * @sta_id: station id + * @tid: tid of the queue + * @scd_queue: scheduler queue to config + * @action: 1 queue enable, 0 queue disable + * @aggregate: 1 aggregated queue, 0 otherwise + * @tx_fifo: TX fifo + * @window: BA window size + * @ssn: SSN for the BA agreement + * @cb_size: size of TFD cyclic buffer. Value is exponent - 3. + * Minimum value 0 (8 TFDs), maximum value 5 (256 TFDs) + * @byte_cnt_addr: address of byte count table + * @tfdq_addr: address of TFD circular buffer + */ +struct iwl_tx_queue_cfg_cmd { + u8 token; + u8 sta_id; + u8 tid; + u8 scd_queue; + u8 action; + u8 aggregate; + u8 tx_fifo; + u8 window; + __le16 ssn; + __le16 reserved; + __le32 cb_size; + __le64 byte_cnt_addr; + __le64 tfdq_addr; +} __packed; /* TX_QUEUE_CFG_CMD_API_S_VER_1 */ + /** * struct iwl_trans_ops - transport specific operations * @@ -640,6 +672,12 @@ struct iwl_trans_ops { unsigned int queue_wdg_timeout); void (*txq_disable)(struct iwl_trans *trans, int queue, bool configure_scd); + /* a000 functions */ + int (*txq_alloc)(struct iwl_trans *trans, + struct iwl_tx_queue_cfg_cmd *cmd, + int cmd_id, + unsigned int queue_wdg_timeout); + void (*txq_free)(struct iwl_trans *trans, int queue); void (*txq_set_shared_mode)(struct iwl_trans *trans, u32 txq_id, bool shared); @@ -1057,6 +1095,34 @@ iwl_trans_txq_enable_cfg(struct iwl_trans *trans, int queue, u16 ssn, trans->ops->txq_enable(trans, queue, ssn, cfg, queue_wdg_timeout); } +static inline void +iwl_trans_txq_free(struct iwl_trans *trans, int queue) +{ + if (WARN_ON_ONCE(!trans->ops->txq_free)) + return; + + trans->ops->txq_free(trans, queue); +} + +static inline int +iwl_trans_txq_alloc(struct iwl_trans *trans, + struct iwl_tx_queue_cfg_cmd *cmd, + int cmd_id, + unsigned int queue_wdg_timeout) +{ + might_sleep(); + + if (WARN_ON_ONCE(!trans->ops->txq_alloc)) + return -ENOTSUPP; + + if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) { + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); + return -EIO; + } + + return trans->ops->txq_alloc(trans, cmd, cmd_id, queue_wdg_timeout); +} + static inline void iwl_trans_txq_set_shared_mode(struct iwl_trans *trans, int queue, bool shared_mode) { diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 98c1308eab0d..5e191579b9a9 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -758,10 +758,16 @@ void iwl_pcie_apm_config(struct iwl_trans *trans); int iwl_pcie_prepare_card_hw(struct iwl_trans *trans); void iwl_pcie_synchronize_irqs(struct iwl_trans *trans); bool iwl_trans_check_hw_rf_kill(struct iwl_trans *trans); +void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq); /* transport gen 2 exported functions */ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, const struct fw_img *fw, bool run_in_rfkill); void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr); +int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, + struct iwl_tx_queue_cfg_cmd *cmd, + int cmd_id, + unsigned int timeout); +void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue); #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 361f0b0a6f42..9cd9c1f5a3dc 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2923,10 +2923,8 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = { .tx = iwl_trans_pcie_tx, .reclaim = iwl_trans_pcie_reclaim, - .txq_disable = iwl_trans_pcie_txq_disable, - .txq_enable = iwl_trans_pcie_txq_enable, - - .txq_set_shared_mode = iwl_trans_pcie_txq_set_shared_mode, + .txq_alloc = iwl_trans_pcie_dyn_txq_alloc, + .txq_free = iwl_trans_pcie_dyn_txq_free, .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c new file mode 100644 index 000000000000..7c93f266871e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -0,0 +1,162 @@ +/****************************************************************************** + * + * 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) 2017 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. + * + * BSD LICENSE + * + * Copyright(c) 2017 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. + * + *****************************************************************************/ + +#include "iwl-debug.h" +#include "iwl-csr.h" +#include "iwl-io.h" +#include "internal.h" + +/* + * iwl_pcie_gen2_txq_unmap - Unmap any remaining DMA mappings and free skb's + */ +void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + + spin_lock_bh(&txq->lock); + while (txq->write_ptr != txq->read_ptr) { + IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", + txq_id, txq->read_ptr); + + iwl_pcie_txq_free_tfd(trans, txq); + txq->read_ptr = iwl_queue_inc_wrap(txq->read_ptr); + + if (txq->read_ptr == txq->write_ptr) { + unsigned long flags; + + spin_lock_irqsave(&trans_pcie->reg_lock, flags); + if (txq_id != trans_pcie->cmd_queue) { + IWL_DEBUG_RPM(trans, "Q %d - last tx freed\n", + txq->id); + iwl_trans_unref(trans); + } else if (trans_pcie->ref_cmd_in_flight) { + trans_pcie->ref_cmd_in_flight = false; + IWL_DEBUG_RPM(trans, + "clear ref_cmd_in_flight\n"); + iwl_trans_unref(trans); + } + spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); + } + } + spin_unlock_bh(&txq->lock); + + /* just in case - this queue may have been stopped */ + iwl_wake_queue(trans, txq); +} + +int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, + struct iwl_tx_queue_cfg_cmd *cmd, + int cmd_id, + unsigned int timeout) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[cmd->scd_queue]; + struct iwl_host_cmd hcmd = { + .id = cmd_id, + .len = { sizeof(*cmd) }, + .data = { cmd, }, + .flags = 0, + }; + u16 ssn = le16_to_cpu(cmd->ssn); + + if (test_and_set_bit(cmd->scd_queue, trans_pcie->queue_used)) { + WARN_ONCE(1, "queue %d already used", cmd->scd_queue); + return -EINVAL; + } + + txq->wd_timeout = msecs_to_jiffies(timeout); + + /* + * Place first TFD at index corresponding to start sequence number. + * Assumes that ssn_idx is valid (!= 0xFFF) + */ + txq->read_ptr = (ssn & 0xff); + txq->write_ptr = (ssn & 0xff); + iwl_write_direct32(trans, HBUS_TARG_WRPTR, + (ssn & 0xff) | (cmd->scd_queue << 8)); + + IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d WrPtr: %d\n", + cmd->scd_queue, ssn & 0xff); + + cmd->tfdq_addr = cpu_to_le64(txq->dma_addr); + cmd->byte_cnt_addr = cpu_to_le64(trans_pcie->scd_bc_tbls.dma + + cmd->scd_queue * + sizeof(struct iwlagn_scd_bc_tbl)); + cmd->cb_size = cpu_to_le64(TFD_QUEUE_CB_SIZE(TFD_QUEUE_SIZE_MAX)); + + return iwl_trans_send_cmd(trans, &hcmd); +} + +void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + trans_pcie->txq[queue].frozen_expiry_remainder = 0; + trans_pcie->txq[queue].frozen = false; + + /* + * Upon HW Rfkill - we stop the device, and then stop the queues + * in the op_mode. Just for the sake of the simplicity of the op_mode, + * allow the op_mode to call txq_disable after it already called + * stop_device. + */ + if (!test_and_clear_bit(queue, trans_pcie->queue_used)) { + WARN_ONCE(test_bit(STATUS_DEVICE_ENABLED, &trans->status), + "queue %d not used", queue); + return; + } + + iwl_pcie_gen2_txq_unmap(trans, queue); + + IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", queue); +} + diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index b3ac1fb51009..9c903270645c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -456,7 +456,7 @@ static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, * Does NOT advance any TFD circular buffer read/write indexes * Does NOT free the TFD itself (which is within circular buffer) */ -static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) +void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) { /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and * idx is bounded by n_window @@ -1359,9 +1359,6 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, if (test_and_set_bit(txq_id, trans_pcie->queue_used)) WARN_ONCE(1, "queue %d already used - expect issues", txq_id); - if (cfg && trans->cfg->use_tfh) - WARN_ONCE(1, "Expected no calls to SCD configuration"); - txq->wd_timeout = msecs_to_jiffies(wdg_timeout); if (cfg) { @@ -1477,9 +1474,6 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id, return; } - if (configure_scd && trans->cfg->use_tfh) - WARN_ONCE(1, "Expected no calls to SCD configuration"); - if (configure_scd) { iwl_scd_txq_set_inactive(trans, txq_id); -- cgit From bb49701b41deff0a8edd297357b23ffe833099eb Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 29 Sep 2016 14:52:40 +0300 Subject: iwlwifi: mvm: support a000 SCD queue configuration a000 devices queue management is going to change significantly. We will have 512 queues. Those queues will be assigned number by the firmware and not by the driver. In addition, due to SN offload having TX queue shared between TIDs is impossible Also, the ADD_STA command no longer updates queues status. The only point of changing queue in the SCD queue config API. From driver perspective we have here a new design: Queue sharing and inactivity checks are disabled. Once this is done, the only paths that call scd_queue_cfg command are paths that alloc and release TX queues - which will make future accommodation to queue number assignment by FW easier. Since allocating 512 queues statically is not advisable, transport will allocate the queue on demand, fill the command with DRAM data and send it. This is reflected in the new transport API. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/mvm/fw-api-sta.h | 5 +- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 62 ++++++++++++++++++++-- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 3 ++ drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 39 +++++++++++--- 4 files changed, 96 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h index cd5fdf83d040..4f8648f53879 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h @@ -351,7 +351,8 @@ struct iwl_mvm_add_sta_cmd_v7 { * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP * mac-addr. * @beamform_flags: beam forming controls - * @tfd_queue_msk: tfd queues used by this station + * @tfd_queue_msk: tfd queues used by this station. + * Obselete for new TX API (9 and above). * @rx_ba_window: aggregation window size * @scd_queue_bank: queue bank in used. Each bank contains 32 queues. 0 means * that the queues used by this station are in the first 32. @@ -386,7 +387,7 @@ struct iwl_mvm_add_sta_cmd { __le16 rx_ba_window; u8 scd_queue_bank; u8 uapsd_trigger_acs; -} __packed; /* ADD_STA_CMD_API_S_VER_8 */ +} __packed; /* ADD_STA_CMD_API_S_VER_9 */ /** * struct iwl_mvm_add_sta_key_common - add/modify sta key common part diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index fb031197266e..e51b74cd5b9f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -127,11 +127,17 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u32 agg_size = 0, mpdu_dens = 0; if (!update || (flags & STA_MODIFY_QUEUES)) { - add_sta_cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk); memcpy(&add_sta_cmd.addr, sta->addr, ETH_ALEN); - if (flags & STA_MODIFY_QUEUES) - add_sta_cmd.modify_mask |= STA_MODIFY_QUEUES; + if (!iwl_mvm_has_new_tx_api(mvm)) { + add_sta_cmd.tfd_queue_msk = + cpu_to_le32(mvm_sta->tfd_queue_msk); + + if (flags & STA_MODIFY_QUEUES) + add_sta_cmd.modify_mask |= STA_MODIFY_QUEUES; + } else { + WARN_ON(flags & STA_MODIFY_QUEUES); + } } switch (sta->bandwidth) { @@ -337,6 +343,9 @@ static int iwl_mvm_invalidate_sta_queue(struct iwl_mvm *mvm, int queue, u8 sta_id; int ret; + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return -EINVAL; + spin_lock_bh(&mvm->queue_info_lock); sta_id = mvm->queue_info[queue].ra_sta_id; spin_unlock_bh(&mvm->queue_info_lock); @@ -387,6 +396,9 @@ static int iwl_mvm_get_queue_agg_tids(struct iwl_mvm *mvm, int queue) lockdep_assert_held(&mvm->mutex); + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return -EINVAL; + spin_lock_bh(&mvm->queue_info_lock); sta_id = mvm->queue_info[queue].ra_sta_id; tid_bitmap = mvm->queue_info[queue].tid_bitmap; @@ -426,6 +438,9 @@ static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue) lockdep_assert_held(&mvm->mutex); + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return -EINVAL; + spin_lock_bh(&mvm->queue_info_lock); sta_id = mvm->queue_info[queue].ra_sta_id; tid_bitmap = mvm->queue_info[queue].tid_bitmap; @@ -468,6 +483,9 @@ static int iwl_mvm_free_inactive_queue(struct iwl_mvm *mvm, int queue, lockdep_assert_held(&mvm->mutex); + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return -EINVAL; + spin_lock_bh(&mvm->queue_info_lock); txq_curr_ac = mvm->queue_info[queue].mac80211_ac; sta_id = mvm->queue_info[queue].ra_sta_id; @@ -512,6 +530,8 @@ static int iwl_mvm_get_shared_queue(struct iwl_mvm *mvm, int i; lockdep_assert_held(&mvm->queue_info_lock); + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return -EINVAL; memset(&ac_to_queue, IEEE80211_INVAL_HW_QUEUE, sizeof(ac_to_queue)); @@ -596,6 +616,9 @@ int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid, unsigned long mq; int ret; + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return -EINVAL; + /* * If the AC is lower than current one - FIFO needs to be redirected to * the lowest one of the streams in the queue. Check if this is needed @@ -757,6 +780,15 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, /* No free queue - we'll have to share */ if (queue <= 0) { + /* This shouldn't happen in new HW - we have 512 queues */ + if (WARN(iwl_mvm_has_new_tx_api(mvm), + "No available queues for tid %d on sta_id %d\n", + tid, cfg.sta_id)) { + spin_unlock_bh(&mvm->queue_info_lock); + + return queue; + } + queue = iwl_mvm_get_shared_queue(mvm, tfd_queue_mask, ac); if (queue > 0) { shared_queue = true; @@ -841,6 +873,9 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, mvmsta->reserved_queue = IEEE80211_INVAL_HW_QUEUE; spin_unlock_bh(&mvmsta->lock); + if (iwl_mvm_has_new_tx_api(mvm)) + return 0; + if (!shared_queue) { ret = iwl_mvm_sta_send_to_fw(mvm, sta, true, STA_MODIFY_QUEUES); if (ret) @@ -880,6 +915,9 @@ static void iwl_mvm_change_queue_owner(struct iwl_mvm *mvm, int queue) lockdep_assert_held(&mvm->mutex); + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return; + spin_lock_bh(&mvm->queue_info_lock); tid_bitmap = mvm->queue_info[queue].tid_bitmap; spin_unlock_bh(&mvm->queue_info_lock); @@ -917,6 +955,10 @@ static void iwl_mvm_unshare_queue(struct iwl_mvm *mvm, int queue) int ssn; int ret = true; + /* queue sharing is disabled on new TX path */ + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return; + lockdep_assert_held(&mvm->mutex); spin_lock_bh(&mvm->queue_info_lock); @@ -1675,7 +1717,8 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id, color)); - cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk); + if (!iwl_mvm_has_new_tx_api(mvm)) + cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk); cmd.tid_disable_tx = cpu_to_le16(0xffff); if (addr) @@ -2293,7 +2336,9 @@ int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); cmd.sta_id = mvm_sta->sta_id; cmd.add_modify = STA_MODE_MODIFY; - cmd.modify_mask = STA_MODIFY_QUEUES | STA_MODIFY_TID_DISABLE_TX; + if (!iwl_mvm_has_new_tx_api(mvm)) + cmd.modify_mask = STA_MODIFY_QUEUES; + cmd.modify_mask |= STA_MODIFY_TID_DISABLE_TX; cmd.tfd_queue_msk = cpu_to_le32(mvm_sta->tfd_queue_msk); cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg); @@ -2493,6 +2538,13 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * changed from current (become smaller) */ if (!alloc_queue && buf_size < mvmsta->max_agg_bufsize) { + /* + * On new TX API rs and BA manager are offloaded. + * For now though, just don't support being reconfigured + */ + if (iwl_mvm_has_new_tx_api(mvm)) + return -ENOTSUPP; + /* * If reconfiguring an existing queue, it first must be * drained diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 878367d9aa85..4d6683141c03 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -893,6 +893,9 @@ static bool iwl_mvm_txq_should_update(struct iwl_mvm *mvm, int txq_id) unsigned long now = jiffies; int tid; + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return false; + for_each_set_bit(tid, &queue_tid_bitmap, IWL_MAX_TID_COUNT + 1) { if (time_before(mvm->queue_info[txq_id].last_frame_time[tid] + IWL_MVM_DQA_QUEUE_TIMEOUT, now)) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 3054945b949a..74f62851f653 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -598,6 +598,9 @@ int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id, u8 minq, u8 maxq) mvm->queue_info[i].status == IWL_MVM_QUEUE_FREE) return i; + if (iwl_mvm_has_new_tx_api(mvm)) + return -ENOSPC; + /* * If no free queue found - settle for an inactive one to reconfigure * Make sure that the inactive queue either already belongs to this STA, @@ -628,6 +631,9 @@ int iwl_mvm_reconfig_scd(struct iwl_mvm *mvm, int queue, int fifo, int sta_id, }; int ret; + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return -EINVAL; + spin_lock_bh(&mvm->queue_info_lock); if (WARN(mvm->queue_info[queue].hw_queue_refcount == 0, "Trying to reconfig unallocated queue %d\n", queue)) { @@ -696,7 +702,7 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, /* Send the enabling command if we need to */ if (iwl_mvm_update_txq_mapping(mvm, queue, mac80211_queue, cfg->sta_id, cfg->tid)) { - struct iwl_scd_txq_cfg_cmd cmd = { + struct iwl_tx_queue_cfg_cmd cmd = { .scd_queue = queue, .action = SCD_CFG_ENABLE_QUEUE, .window = cfg->frame_limit, @@ -707,9 +713,16 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, .tid = cfg->tid, }; + if (iwl_mvm_has_new_tx_api(mvm)) { + iwl_trans_txq_alloc(mvm->trans, (void *)&cmd, + SCD_QUEUE_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), + WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, + sizeof(struct iwl_scd_txq_cfg_cmd), &cmd), "Failed to configure queue %d on FIFO %d\n", queue, cfg->fifo); @@ -719,7 +732,7 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, u8 tid, u8 flags) { - struct iwl_scd_txq_cfg_cmd cmd = { + struct iwl_tx_queue_cfg_cmd cmd = { .scd_queue = queue, .action = SCD_CFG_DISABLE_QUEUE, }; @@ -795,9 +808,17 @@ int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, 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); + if (iwl_mvm_has_new_tx_api(mvm)) { + iwl_trans_txq_free(mvm->trans, queue); + ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, + sizeof(cmd), &cmd); + } else { + iwl_trans_txq_disable(mvm->trans, queue, false); + ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, + sizeof(struct iwl_scd_txq_cfg_cmd), + &cmd); + } + if (ret) IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n", queue, ret); @@ -1096,6 +1117,9 @@ static void iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm, lockdep_assert_held(&mvmsta->lock); lockdep_assert_held(&mvm->queue_info_lock); + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return; + /* Go over all non-active TIDs, incl. IWL_MAX_TID_COUNT (for mgmt) */ for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { /* If some TFDs are still queued - don't mark TID as inactive */ @@ -1162,6 +1186,9 @@ void iwl_mvm_inactivity_check(struct iwl_mvm *mvm) unsigned long now = jiffies; int i; + if (iwl_mvm_has_new_tx_api(mvm)) + return; + spin_lock_bh(&mvm->queue_info_lock); for (i = 0; i < IWL_MAX_HW_QUEUES; i++) if (mvm->queue_info[i].hw_queue_refcount > 0) -- cgit From c65f4e03fc46c646f81cc659e501969ca48c3220 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 13 Dec 2016 16:10:28 +0200 Subject: iwlwifi: mvm: support moving to mgmt tid For a000 FW moved to 15 as management TID. The change for us is fairly local - translate old TID to 15 when enabling and disabling a queue, and make sure to cover it also on TX responses. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 9 ++++++--- drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 4 ++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 723402ee7be2..c87a58ee012a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -397,6 +397,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_MGMT_TID 15 #define IWL_FRAME_LIMIT 64 #define IWL_MAX_RX_HW_QUEUES 16 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 4d6683141c03..4ba7ff44420d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1425,7 +1425,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, if (!IS_ERR(sta)) { mvmsta = iwl_mvm_sta_from_mac80211(sta); - if (tid != IWL_TID_NON_QOS) { + if (tid != IWL_TID_NON_QOS && tid != IWL_MGMT_TID) { struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; bool send_eosp_ndp = false; @@ -1767,8 +1767,11 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) * This will go together with SN and AddBA offload and cannot * be handled properly for now. */ - WARN_ON(le16_to_cpu(ba_res->tfd_cnt) != 1); - iwl_mvm_tx_reclaim(mvm, sta_id, ba_res->ra_tid[0].tid, + WARN_ON(le16_to_cpu(ba_res->ra_tid_cnt) != 1); + tid = ba_res->ra_tid[0].tid; + if (tid == IWL_MGMT_TID) + tid = IWL_MAX_TID_COUNT; + iwl_mvm_tx_reclaim(mvm, sta_id, tid, (int)ba_res->tfd[0].q_num, le16_to_cpu(ba_res->tfd[0].tfd_index), &ba_info, le32_to_cpu(ba_res->tx_rate)); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 74f62851f653..70ec048ac152 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -714,6 +714,8 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, }; if (iwl_mvm_has_new_tx_api(mvm)) { + if (cmd.tid == IWL_MAX_TID_COUNT) + cmd.tid = IWL_MGMT_TID; iwl_trans_txq_alloc(mvm->trans, (void *)&cmd, SCD_QUEUE_CFG, wdg_timeout); return; @@ -810,6 +812,8 @@ int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, if (iwl_mvm_has_new_tx_api(mvm)) { iwl_trans_txq_free(mvm->trans, queue); + if (cmd.tid == IWL_MAX_TID_COUNT) + cmd.tid = IWL_MGMT_TID; ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, sizeof(cmd), &cmd); } else { -- cgit From ab6c644539e98be3d73702c84f8f9efc789391f0 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 1 Nov 2016 12:37:49 +0200 Subject: iwlwifi: pcie: copy TX functions to new transport This is just a copy-paste in order to make changes tracking easier. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 13 + drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 519 +++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 38 +- 4 files changed, 540 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 5e191579b9a9..b9afffae8a4d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -650,6 +650,12 @@ static inline void iwl_enable_fw_load_int(struct iwl_trans *trans) } } +static inline void *iwl_pcie_get_tfd(struct iwl_trans_pcie *trans_pcie, + struct iwl_txq *txq, int idx) +{ + return txq->tfds + trans_pcie->tfd_size * idx; +} + static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -759,6 +765,11 @@ int iwl_pcie_prepare_card_hw(struct iwl_trans *trans); void iwl_pcie_synchronize_irqs(struct iwl_trans *trans); bool iwl_trans_check_hw_rf_kill(struct iwl_trans *trans); void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq); +int iwl_queue_space(const struct iwl_txq *q); +int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_txq *txq, u8 hdr_len, + struct iwl_cmd_meta *out_meta, + struct iwl_device_cmd *dev_cmd, u16 tb1_len); /* transport gen 2 exported functions */ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, @@ -769,5 +780,7 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, int cmd_id, unsigned int timeout); void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue); +int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_device_cmd *dev_cmd, int txq_id); #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 9cd9c1f5a3dc..e1610241be07 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2920,7 +2920,7 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = { .send_cmd = iwl_trans_pcie_send_hcmd, - .tx = iwl_trans_pcie_tx, + .tx = iwl_trans_pcie_gen2_tx, .reclaim = iwl_trans_pcie_reclaim, .txq_alloc = iwl_trans_pcie_dyn_txq_alloc, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 7c93f266871e..2ddaf7a1a1b5 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -53,6 +53,525 @@ #include "iwl-csr.h" #include "iwl-io.h" #include "internal.h" +#include "mvm/fw-api.h" + +/* + * iwl_pcie_txq_update_byte_tbl - Set up entry in Tx byte-count array + */ +static void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans *trans, + struct iwl_txq *txq, u16 byte_cnt, + int num_tbs) +{ + struct iwlagn_scd_bc_tbl *scd_bc_tbl; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int write_ptr = txq->write_ptr; + u8 filled_tfd_size, num_fetch_chunks; + u16 len = byte_cnt; + __le16 bc_ent; + + scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; + + len = DIV_ROUND_UP(len, 4); + + if (WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX)) + return; + + filled_tfd_size = offsetof(struct iwl_tfh_tfd, tbs) + + num_tbs * sizeof(struct iwl_tfh_tb); + /* + * filled_tfd_size contains the number of filled bytes in the TFD. + * Dividing it by 64 will give the number of chunks to fetch + * to SRAM- 0 for one chunk, 1 for 2 and so on. + * If, for example, TFD contains only 3 TBs then 32 bytes + * of the TFD are used, and only one chunk of 64 bytes should + * be fetched + */ + num_fetch_chunks = DIV_ROUND_UP(filled_tfd_size, 64) - 1; + + bc_ent = cpu_to_le16(len | (num_fetch_chunks << 12)); + scd_bc_tbl[txq->id].tfd_offset[write_ptr] = bc_ent; +} + +/* + * iwl_pcie_gen2_txq_inc_wr_ptr - Send new write index to hardware + */ +static void iwl_pcie_gen2_txq_inc_wr_ptr(struct iwl_trans *trans, + struct iwl_txq *txq) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 reg = 0; + int txq_id = txq->id; + + lockdep_assert_held(&txq->lock); + + /* + * explicitly wake up the NIC if: + * 1. shadow registers aren't enabled + * 2. NIC is woken up for CMD regardless of shadow outside this function + * 3. there is a chance that the NIC is asleep + */ + if (!trans->cfg->base_params->shadow_reg_enable && + txq_id != trans_pcie->cmd_queue && + test_bit(STATUS_TPOWER_PMI, &trans->status)) { + /* + * wake up nic if it's powered down ... + * uCode will wake up, and interrupt us again, so next + * time we'll skip this part. + */ + reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); + + if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + IWL_DEBUG_INFO(trans, + "Tx queue %d requesting wakeup, GP1 = 0x%x\n", + txq_id, reg); + iwl_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + txq->need_update = true; + return; + } + } + + /* + * if not in power-save mode, uCode will never sleep when we're + * trying to tx (during RFKILL, we're not trying to tx). + */ + IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, txq->write_ptr); + if (!txq->block) + iwl_write32(trans, HBUS_TARG_WRPTR, + txq->write_ptr | (txq_id << 8)); +} + +static inline u8 iwl_pcie_gen2_get_num_tbs(struct iwl_trans *trans, void *_tfd) +{ + if (trans->cfg->use_tfh) { + struct iwl_tfh_tfd *tfd = _tfd; + + return le16_to_cpu(tfd->num_tbs) & 0x1f; + } else { + struct iwl_tfd *tfd = _tfd; + + return tfd->num_tbs & 0x1f; + } +} + +static inline dma_addr_t iwl_pcie_gen2_tb_get_addr(struct iwl_trans *trans, + void *_tfd, u8 idx) +{ + if (trans->cfg->use_tfh) { + struct iwl_tfh_tfd *tfd = _tfd; + struct iwl_tfh_tb *tb = &tfd->tbs[idx]; + + return (dma_addr_t)(le64_to_cpu(tb->addr)); + } else { + struct iwl_tfd *tfd = _tfd; + struct iwl_tfd_tb *tb = &tfd->tbs[idx]; + dma_addr_t addr = get_unaligned_le32(&tb->lo); + dma_addr_t hi_len; + + if (sizeof(dma_addr_t) <= sizeof(u32)) + return addr; + + hi_len = le16_to_cpu(tb->hi_n_len) & 0xF; + + /* + * shift by 16 twice to avoid warnings on 32-bit + * (where this code never runs anyway due to the + * if statement above) + */ + return addr | ((hi_len << 16) << 16); + } +} + +static void iwl_pcie_gen2_tfd_unmap(struct iwl_trans *trans, + struct iwl_cmd_meta *meta, + struct iwl_txq *txq, int index) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int i, num_tbs; + void *tfd = iwl_pcie_get_tfd(trans_pcie, txq, index); + + /* Sanity check on number of chunks */ + num_tbs = iwl_pcie_gen2_get_num_tbs(trans, tfd); + + if (num_tbs >= trans_pcie->max_tbs) { + IWL_ERR(trans, "Too many chunks: %i\n", num_tbs); + /* @todo issue fatal error, it is quite serious situation */ + return; + } + + /* first TB is never freed - it's the bidirectional DMA data */ + + for (i = 1; i < num_tbs; i++) { + if (meta->tbs & BIT(i)) + dma_unmap_page(trans->dev, + iwl_pcie_gen2_tb_get_addr(trans, tfd, + i), + iwl_pcie_gen2_tb_get_addr(trans, tfd, + i), + DMA_TO_DEVICE); + else + dma_unmap_single(trans->dev, + iwl_pcie_gen2_tb_get_addr(trans, tfd, + i), + iwl_pcie_gen2_tb_get_addr(trans, tfd, + i), + DMA_TO_DEVICE); + } + + if (trans->cfg->use_tfh) { + struct iwl_tfh_tfd *tfd_fh = (void *)tfd; + + tfd_fh->num_tbs = 0; + } else { + struct iwl_tfd *tfd_fh = (void *)tfd; + + tfd_fh->num_tbs = 0; + } +} + +static void iwl_pcie_gen2_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) +{ + /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and + * idx is bounded by n_window + */ + int rd_ptr = txq->read_ptr; + int idx = get_cmd_index(txq, rd_ptr); + + lockdep_assert_held(&txq->lock); + + /* We have only q->n_window txq->entries, but we use + * TFD_QUEUE_SIZE_MAX tfds + */ + iwl_pcie_gen2_tfd_unmap(trans, &txq->entries[idx].meta, txq, rd_ptr); + + /* free SKB */ + if (txq->entries) { + struct sk_buff *skb; + + skb = txq->entries[idx].skb; + + /* Can be called from irqs-disabled context + * If skb is not NULL, it means that the whole queue is being + * freed and that the queue is not empty - free the skb + */ + if (skb) { + iwl_op_mode_free_skb(trans->op_mode, skb); + txq->entries[idx].skb = NULL; + } + } +} + +static inline void iwl_pcie_gen2_set_tb(struct iwl_trans *trans, void *tfd, + u8 idx, dma_addr_t addr, u16 len) +{ + if (trans->cfg->use_tfh) { + struct iwl_tfh_tfd *tfd_fh = (void *)tfd; + struct iwl_tfh_tb *tb = &tfd_fh->tbs[idx]; + + put_unaligned_le64(addr, &tb->addr); + tb->tb_len = cpu_to_le16(len); + + tfd_fh->num_tbs = cpu_to_le16(idx + 1); + } else { + struct iwl_tfd *tfd_fh = (void *)tfd; + struct iwl_tfd_tb *tb = &tfd_fh->tbs[idx]; + + u16 hi_n_len = len << 4; + + put_unaligned_le32(addr, &tb->lo); + hi_n_len |= iwl_get_dma_hi_addr(addr); + + tb->hi_n_len = cpu_to_le16(hi_n_len); + + tfd_fh->num_tbs = idx + 1; + } +} + +int iwl_pcie_gen2_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, + dma_addr_t addr, u16 len, bool reset) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + void *tfd; + u32 num_tbs; + + tfd = txq->tfds + trans_pcie->tfd_size * txq->write_ptr; + + if (reset) + memset(tfd, 0, trans_pcie->tfd_size); + + num_tbs = iwl_pcie_gen2_get_num_tbs(trans, tfd); + + /* Each TFD can point to a maximum max_tbs Tx buffers */ + if (num_tbs >= trans_pcie->max_tbs) { + IWL_ERR(trans, "Error can not send more than %d chunks\n", + trans_pcie->max_tbs); + return -EINVAL; + } + + if (WARN(addr & ~IWL_TX_DMA_MASK, + "Unaligned address = %llx\n", (unsigned long long)addr)) + return -EINVAL; + + iwl_pcie_gen2_set_tb(trans, tfd, num_tbs, addr, len); + + return num_tbs; +} + +static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_txq *txq, u8 hdr_len, + struct iwl_cmd_meta *out_meta, + struct iwl_device_cmd *dev_cmd, u16 tb1_len) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u16 tb2_len; + int i; + + /* + * Set up TFD's third entry to point directly to remainder + * of skb's head, if any + */ + tb2_len = skb_headlen(skb) - hdr_len; + + if (tb2_len > 0) { + dma_addr_t tb2_phys = dma_map_single(trans->dev, + skb->data + hdr_len, + tb2_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, tb2_phys))) { + iwl_pcie_gen2_tfd_unmap(trans, out_meta, txq, + txq->write_ptr); + return -EINVAL; + } + iwl_pcie_gen2_build_tfd(trans, txq, tb2_phys, tb2_len, false); + } + + /* set up the remaining entries to point to the data */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + dma_addr_t tb_phys; + int tb_idx; + + if (!skb_frag_size(frag)) + continue; + + tb_phys = skb_frag_dma_map(trans->dev, frag, 0, + skb_frag_size(frag), DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(trans->dev, tb_phys))) { + iwl_pcie_gen2_tfd_unmap(trans, out_meta, txq, + txq->write_ptr); + return -EINVAL; + } + tb_idx = iwl_pcie_gen2_build_tfd(trans, txq, tb_phys, + skb_frag_size(frag), false); + + out_meta->tbs |= BIT(tb_idx); + } + + trace_iwlwifi_dev_tx(trans->dev, skb, + iwl_pcie_get_tfd(trans_pcie, txq, txq->write_ptr), + trans_pcie->tfd_size, + &dev_cmd->hdr, IWL_FIRST_TB_SIZE + tb1_len, + skb->data + hdr_len, tb2_len); + trace_iwlwifi_dev_tx_data(trans->dev, skb, + hdr_len, skb->len - hdr_len); + return 0; +} + +#define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(TX_CMD_FLG_MH_PAD) + +int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_device_cmd *dev_cmd, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct ieee80211_hdr *hdr; + struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; + struct iwl_cmd_meta *out_meta; + struct iwl_txq *txq; + dma_addr_t tb0_phys, tb1_phys, scratch_phys; + void *tb1_addr; + void *tfd; + u16 len, tb1_len; + bool wait_write_ptr; + __le16 fc; + u8 hdr_len; + u16 wifi_seq; + bool amsdu; + + txq = &trans_pcie->txq[txq_id]; + + if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used), + "TX on unused queue %d\n", txq_id)) + return -EINVAL; + + if (unlikely(trans_pcie->sw_csum_tx && + skb->ip_summed == CHECKSUM_PARTIAL)) { + int offs = skb_checksum_start_offset(skb); + int csum_offs = offs + skb->csum_offset; + __wsum csum; + + if (skb_ensure_writable(skb, csum_offs + sizeof(__sum16))) + return -1; + + csum = skb_checksum(skb, offs, skb->len - offs, 0); + *(__sum16 *)(skb->data + csum_offs) = csum_fold(csum); + + skb->ip_summed = CHECKSUM_UNNECESSARY; + } + + if (skb_is_nonlinear(skb) && + skb_shinfo(skb)->nr_frags > IWL_PCIE_MAX_FRAGS(trans_pcie) && + __skb_linearize(skb)) + return -ENOMEM; + + /* mac80211 always puts the full header into the SKB's head, + * so there's no need to check if it's readable there + */ + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + hdr_len = ieee80211_hdrlen(fc); + + spin_lock(&txq->lock); + + if (iwl_queue_space(txq) < txq->high_mark) { + iwl_stop_queue(trans, txq); + + /* don't put the packet on the ring, if there is no room */ + if (unlikely(iwl_queue_space(txq) < 3)) { + struct iwl_device_cmd **dev_cmd_ptr; + + dev_cmd_ptr = (void *)((u8 *)skb->cb + + trans_pcie->dev_cmd_offs); + + *dev_cmd_ptr = dev_cmd; + __skb_queue_tail(&txq->overflow_q, skb); + + spin_unlock(&txq->lock); + return 0; + } + } + + /* In AGG mode, the index in the ring must correspond to the WiFi + * sequence number. This is a HW requirements to help the SCD to parse + * the BA. + * Check here that the packets are in the right place on the ring. + */ + wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); + WARN_ONCE(txq->ampdu && + (wifi_seq & 0xff) != txq->write_ptr, + "Q: %d WiFi Seq %d tfdNum %d", + txq_id, wifi_seq, txq->write_ptr); + + /* Set up driver data for this TFD */ + txq->entries[txq->write_ptr].skb = skb; + txq->entries[txq->write_ptr].cmd = dev_cmd; + + dev_cmd->hdr.sequence = + cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | + INDEX_TO_SEQ(txq->write_ptr))); + + tb0_phys = iwl_pcie_get_first_tb_dma(txq, txq->write_ptr); + scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) + + offsetof(struct iwl_tx_cmd, scratch); + + tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); + tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); + + /* Set up first empty entry in queue's array of Tx/cmd buffers */ + out_meta = &txq->entries[txq->write_ptr].meta; + out_meta->flags = 0; + + /* + * The second TB (tb1) points to the remainder of the TX command + * and the 802.11 header - dword aligned size + * (This calculation modifies the TX command, so do it before the + * setup of the first TB) + */ + len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + + hdr_len - IWL_FIRST_TB_SIZE; + /* do not align A-MSDU to dword as the subframe header aligns it */ + amsdu = ieee80211_is_data_qos(fc) && + (*ieee80211_get_qos_ctl(hdr) & + IEEE80211_QOS_CTL_A_MSDU_PRESENT); + if (trans_pcie->sw_csum_tx || !amsdu) { + tb1_len = ALIGN(len, 4); + /* Tell NIC about any 2-byte padding after MAC header */ + if (tb1_len != len) + tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; + } else { + tb1_len = len; + } + + /* + * The first TB points to bi-directional DMA data, we'll + * memcpy the data into it later. + */ + iwl_pcie_gen2_build_tfd(trans, txq, tb0_phys, IWL_FIRST_TB_SIZE, true); + + /* there must be data left over for TB1 or this code must be changed */ + BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_FIRST_TB_SIZE); + + /* map the data for TB1 */ + tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_FIRST_TB_SIZE; + tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, tb1_phys))) + goto out_err; + iwl_pcie_gen2_build_tfd(trans, txq, tb1_phys, tb1_len, false); + + if (amsdu) { + if (unlikely(iwl_fill_data_tbs_amsdu(trans, skb, txq, hdr_len, + out_meta, dev_cmd, + tb1_len))) + goto out_err; + } else if (unlikely(iwl_fill_data_tbs(trans, skb, txq, hdr_len, + out_meta, dev_cmd, tb1_len))) { + goto out_err; + } + + /* building the A-MSDU might have changed this data, so memcpy it now */ + memcpy(&txq->first_tb_bufs[txq->write_ptr], &dev_cmd->hdr, + IWL_FIRST_TB_SIZE); + + tfd = iwl_pcie_get_tfd(trans_pcie, txq, txq->write_ptr); + /* Set up entry for this TFD in Tx byte-count array */ + iwl_pcie_gen2_update_byte_tbl(trans, txq, le16_to_cpu(tx_cmd->len), + iwl_pcie_gen2_get_num_tbs(trans, tfd)); + + wait_write_ptr = ieee80211_has_morefrags(fc); + + /* start timer if queue currently empty */ + if (txq->read_ptr == txq->write_ptr) { + if (txq->wd_timeout) { + /* + * If the TXQ is active, then set the timer, if not, + * set the timer in remainder so that the timer will + * be armed with the right value when the station will + * wake up. + */ + if (!txq->frozen) + mod_timer(&txq->stuck_timer, + jiffies + txq->wd_timeout); + else + txq->frozen_expiry_remainder = txq->wd_timeout; + } + IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", txq->id); + iwl_trans_ref(trans); + } + + /* Tell device the write index *just past* this latest filled TFD */ + txq->write_ptr = iwl_queue_inc_wrap(txq->write_ptr); + if (!wait_write_ptr) + iwl_pcie_gen2_txq_inc_wr_ptr(trans, txq); + + /* + * At this point the frame is "transmitted" successfully + * and we will get a TX status notification eventually. + */ + spin_unlock(&txq->lock); + return 0; +out_err: + spin_unlock(&txq->lock); + return -1; +} /* * iwl_pcie_gen2_txq_unmap - Unmap any remaining DMA mappings and free skb's diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 9c903270645c..9572330e22c0 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -71,7 +71,7 @@ * ***************************************************/ -static int iwl_queue_space(const struct iwl_txq *q) +int iwl_queue_space(const struct iwl_txq *q) { unsigned int max; unsigned int used; @@ -185,6 +185,7 @@ static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans, __le16 bc_ent; struct iwl_tx_cmd *tx_cmd = (void *)txq->entries[txq->write_ptr].cmd->payload; + u8 sta_id = tx_cmd->sta_id; scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; @@ -207,26 +208,7 @@ static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans, if (WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX)) return; - if (trans->cfg->use_tfh) { - u8 filled_tfd_size = offsetof(struct iwl_tfh_tfd, tbs) + - num_tbs * sizeof(struct iwl_tfh_tb); - /* - * filled_tfd_size contains the number of filled bytes in the - * TFD. - * Dividing it by 64 will give the number of chunks to fetch - * to SRAM- 0 for one chunk, 1 for 2 and so on. - * If, for example, TFD contains only 3 TBs then 32 bytes - * of the TFD are used, and only one chunk of 64 bytes should - * be fetched - */ - u8 num_fetch_chunks = DIV_ROUND_UP(filled_tfd_size, 64) - 1; - - bc_ent = cpu_to_le16(len | (num_fetch_chunks << 12)); - } else { - u8 sta_id = tx_cmd->sta_id; - - bc_ent = cpu_to_le16(len | (sta_id << 12)); - } + bc_ent = cpu_to_le16(len | (sta_id << 12)); scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; @@ -327,12 +309,6 @@ void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans) } } -static inline void *iwl_pcie_get_tfd(struct iwl_trans_pcie *trans_pcie, - struct iwl_txq *txq, int idx) -{ - return txq->tfds + trans_pcie->tfd_size * idx; -} - static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_trans *trans, void *_tfd, u8 idx) { @@ -2104,10 +2080,10 @@ static void iwl_compute_pseudo_hdr_csum(void *iph, struct tcphdr *tcph, } } -static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, - struct iwl_txq *txq, u8 hdr_len, - struct iwl_cmd_meta *out_meta, - struct iwl_device_cmd *dev_cmd, u16 tb1_len) +int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_txq *txq, u8 hdr_len, + struct iwl_cmd_meta *out_meta, + struct iwl_device_cmd *dev_cmd, u16 tb1_len) { struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload; struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; -- cgit From fd2103570125be186cdb0115abc2fecb6e582aa4 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 24 Jan 2017 09:40:43 +0200 Subject: iwlwifi: mvm: remove unnecessary debugging from UMAC scan There are several occasions where a scan of the same type is requested concurrently, so logging every time this happens is just noisy and unnecessary. Remove the logging for these cases. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index ce6a2b3b021e..9668f945b4e6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -1037,11 +1037,8 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels)) return -ENOBUFS; - if (type == mvm->scan_type) { - IWL_DEBUG_SCAN(mvm, - "Ignoring UMAC scan config of the same type\n"); + if (type == mvm->scan_type) return 0; - } if (iwl_mvm_has_new_tx_api(mvm)) cmd_size = sizeof(struct iwl_scan_config); -- cgit From c80eb570f8ce188e92a1693774a24d0f227985b5 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 12 Jan 2017 15:43:57 +0200 Subject: iwlwifi: mvm: tell the firmware about the U-APSD parameters Newer firmware versions will be able to handle all the WMM-PS flows internally when we act as a GO. The firwmare relies on the fact that the drivers puts frames for different peers in different queues (DQA) to achieve this. The driver will not be aware of the power state of the peers anymore. Tell the firmware about the WMM-PS parameters of earch peer that connects to us so that it can know what are the trigger-enabled ACs, the delivery-enableds ACs and the Service Period length. This API change is backward compatible since older firmware versions will simply ignore the newly added values. Since we don't support ieee80211 TSPECs for now, just copy the trigger-enabled ACs to the delivery enabled ones. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h | 11 ++++++----- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 10 ++++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h index 4f8648f53879..e79df1c53d68 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h @@ -179,7 +179,7 @@ enum iwl_sta_key_flag { * enum iwl_sta_modify_flag - indicate to the fw what flag are being changed * @STA_MODIFY_QUEUE_REMOVAL: this command removes a queue * @STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx - * @STA_MODIFY_UAPSD_ACS: this command modifies %uapsd_trigger_acs + * @STA_MODIFY_UAPSD_ACS: this command modifies %uapsd_acs * @STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid * @STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid * @STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count @@ -354,8 +354,9 @@ struct iwl_mvm_add_sta_cmd_v7 { * @tfd_queue_msk: tfd queues used by this station. * Obselete for new TX API (9 and above). * @rx_ba_window: aggregation window size - * @scd_queue_bank: queue bank in used. Each bank contains 32 queues. 0 means - * that the queues used by this station are in the first 32. + * @sp_length: the size of the SP as it appears in the WME IE + * @uapsd_acs: 4 LS bits are trigger enabled ACs, 4 MS bits are the deliver + * enabled ACs. * * The device contains an internal table of per-station information, with info * on security keys, aggregation parameters, and Tx rates for initial Tx @@ -385,8 +386,8 @@ struct iwl_mvm_add_sta_cmd { __le16 beamform_flags; __le32 tfd_queue_msk; __le16 rx_ba_window; - u8 scd_queue_bank; - u8 uapsd_trigger_acs; + u8 sp_length; + u8 uapsd_acs; } __packed; /* ADD_STA_CMD_API_S_VER_9 */ /** diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index e51b74cd5b9f..36ae228c1612 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -215,13 +215,15 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, add_sta_cmd.modify_mask |= STA_MODIFY_UAPSD_ACS; if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) - add_sta_cmd.uapsd_trigger_acs |= BIT(AC_BK); + add_sta_cmd.uapsd_acs |= BIT(AC_BK); if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) - add_sta_cmd.uapsd_trigger_acs |= BIT(AC_BE); + add_sta_cmd.uapsd_acs |= BIT(AC_BE); if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) - add_sta_cmd.uapsd_trigger_acs |= BIT(AC_VI); + add_sta_cmd.uapsd_acs |= BIT(AC_VI); if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) - add_sta_cmd.uapsd_trigger_acs |= BIT(AC_VO); + add_sta_cmd.uapsd_acs |= BIT(AC_VO); + add_sta_cmd.uapsd_acs |= add_sta_cmd.uapsd_acs << 4; + add_sta_cmd.sp_length = sta->max_sp; } status = ADD_STA_SUCCESS; -- cgit From cfbeb5982467f38bdde2d522b3d457d5fc94ab0c Mon Sep 17 00:00:00 2001 From: "Goodstein, Mordechay" Date: Mon, 21 Nov 2016 10:26:36 +0200 Subject: iwlwifi: mvm: move new API code to the end By moving all the code that depends on the new API we avoid unnecessary indentation in the code. Signed-off-by: Mordechai Goodstein Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 69 +++++++++++++++-------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 861d54641d1e..655bd1384158 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -649,6 +649,9 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, .mvm = mvm, }; int expected_size; + int i; + u8 *energy; + __le32 *bytes, *air_time; if (iwl_mvm_is_cdb_supported(mvm)) expected_size = sizeof(*stats); @@ -674,38 +677,6 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, le64_to_cpu(stats->general.common.on_time_scan); data.general = &stats->general; - if (iwl_mvm_has_new_rx_api(mvm)) { - int i; - u8 *energy; - __le32 *bytes, *air_time; - - if (!iwl_mvm_is_cdb_supported(mvm)) { - struct iwl_notif_statistics_v11 *v11 = - (void *)&pkt->data; - - energy = (void *)&v11->load_stats.avg_energy; - bytes = (void *)&v11->load_stats.byte_count; - air_time = (void *)&v11->load_stats.air_time; - } else { - energy = (void *)&stats->load_stats.avg_energy; - bytes = (void *)&stats->load_stats.byte_count; - air_time = (void *)&stats->load_stats.air_time; - } - - rcu_read_lock(); - for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) { - struct iwl_mvm_sta *sta; - - if (!energy[i]) - continue; - - sta = iwl_mvm_sta_from_staid_rcu(mvm, i); - if (!sta) - continue; - sta->avg_energy = energy[i]; - } - rcu_read_unlock(); - } iwl_mvm_rx_stats_check_trigger(mvm, pkt); @@ -713,7 +684,39 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_stat_iterator, &data); + + if (!iwl_mvm_has_new_rx_api(mvm)) + return; + + if (!iwl_mvm_is_cdb_supported(mvm)) { + struct iwl_notif_statistics_v11 *v11 = + (void *)&pkt->data; + + energy = (void *)&v11->load_stats.avg_energy; + bytes = (void *)&v11->load_stats.byte_count; + air_time = (void *)&v11->load_stats.air_time; + } else { + energy = (void *)&stats->load_stats.avg_energy; + bytes = (void *)&stats->load_stats.byte_count; + air_time = (void *)&stats->load_stats.air_time; + } + + rcu_read_lock(); + for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) { + struct iwl_mvm_sta *sta; + + if (!energy[i]) + continue; + + sta = iwl_mvm_sta_from_staid_rcu(mvm, i); + if (!sta) + continue; + sta->avg_energy = energy[i]; + } + rcu_read_unlock(); + return; + invalid: IWL_ERR(mvm, "received invalid statistics size (%d)!\n", iwl_rx_packet_payload_len(pkt)); -- cgit From 066fd29a2fa366fd87c3d1da4cd25f4dfdece1bc Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 24 Jan 2017 14:53:11 +0200 Subject: iwlwifi: pcie: cleanup old transport code from gen2 Cleanup code that is irrelevant for a000 devices. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 4 - drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 195 +++------------------ drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 8 +- 3 files changed, 30 insertions(+), 177 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index b9afffae8a4d..bd358895caee 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -766,10 +766,6 @@ void iwl_pcie_synchronize_irqs(struct iwl_trans *trans); bool iwl_trans_check_hw_rf_kill(struct iwl_trans *trans); void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq); int iwl_queue_space(const struct iwl_txq *q); -int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, - struct iwl_txq *txq, u8 hdr_len, - struct iwl_cmd_meta *out_meta, - struct iwl_device_cmd *dev_cmd, u16 tb1_len); /* transport gen 2 exported functions */ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 2ddaf7a1a1b5..3a36bda5fd27 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -98,88 +98,23 @@ static void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans *trans, static void iwl_pcie_gen2_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 reg = 0; - int txq_id = txq->id; - lockdep_assert_held(&txq->lock); - /* - * explicitly wake up the NIC if: - * 1. shadow registers aren't enabled - * 2. NIC is woken up for CMD regardless of shadow outside this function - * 3. there is a chance that the NIC is asleep - */ - if (!trans->cfg->base_params->shadow_reg_enable && - txq_id != trans_pcie->cmd_queue && - test_bit(STATUS_TPOWER_PMI, &trans->status)) { - /* - * wake up nic if it's powered down ... - * uCode will wake up, and interrupt us again, so next - * time we'll skip this part. - */ - reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); - - if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { - IWL_DEBUG_INFO(trans, - "Tx queue %d requesting wakeup, GP1 = 0x%x\n", - txq_id, reg); - iwl_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - txq->need_update = true; - return; - } - } + IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq->id, txq->write_ptr); /* * if not in power-save mode, uCode will never sleep when we're * trying to tx (during RFKILL, we're not trying to tx). */ - IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, txq->write_ptr); if (!txq->block) iwl_write32(trans, HBUS_TARG_WRPTR, - txq->write_ptr | (txq_id << 8)); + txq->write_ptr | (txq->id << 8)); } -static inline u8 iwl_pcie_gen2_get_num_tbs(struct iwl_trans *trans, void *_tfd) +static u8 iwl_pcie_gen2_get_num_tbs(struct iwl_trans *trans, + struct iwl_tfh_tfd *tfd) { - if (trans->cfg->use_tfh) { - struct iwl_tfh_tfd *tfd = _tfd; - - return le16_to_cpu(tfd->num_tbs) & 0x1f; - } else { - struct iwl_tfd *tfd = _tfd; - - return tfd->num_tbs & 0x1f; - } -} - -static inline dma_addr_t iwl_pcie_gen2_tb_get_addr(struct iwl_trans *trans, - void *_tfd, u8 idx) -{ - if (trans->cfg->use_tfh) { - struct iwl_tfh_tfd *tfd = _tfd; - struct iwl_tfh_tb *tb = &tfd->tbs[idx]; - - return (dma_addr_t)(le64_to_cpu(tb->addr)); - } else { - struct iwl_tfd *tfd = _tfd; - struct iwl_tfd_tb *tb = &tfd->tbs[idx]; - dma_addr_t addr = get_unaligned_le32(&tb->lo); - dma_addr_t hi_len; - - if (sizeof(dma_addr_t) <= sizeof(u32)) - return addr; - - hi_len = le16_to_cpu(tb->hi_n_len) & 0xF; - - /* - * shift by 16 twice to avoid warnings on 32-bit - * (where this code never runs anyway due to the - * if statement above) - */ - return addr | ((hi_len << 16) << 16); - } + return le16_to_cpu(tfd->num_tbs) & 0x1f; } static void iwl_pcie_gen2_tfd_unmap(struct iwl_trans *trans, @@ -188,45 +123,31 @@ static void iwl_pcie_gen2_tfd_unmap(struct iwl_trans *trans, { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int i, num_tbs; - void *tfd = iwl_pcie_get_tfd(trans_pcie, txq, index); + struct iwl_tfh_tfd *tfd = iwl_pcie_get_tfd(trans_pcie, txq, index); /* Sanity check on number of chunks */ num_tbs = iwl_pcie_gen2_get_num_tbs(trans, tfd); if (num_tbs >= trans_pcie->max_tbs) { IWL_ERR(trans, "Too many chunks: %i\n", num_tbs); - /* @todo issue fatal error, it is quite serious situation */ return; } /* first TB is never freed - it's the bidirectional DMA data */ - for (i = 1; i < num_tbs; i++) { if (meta->tbs & BIT(i)) dma_unmap_page(trans->dev, - iwl_pcie_gen2_tb_get_addr(trans, tfd, - i), - iwl_pcie_gen2_tb_get_addr(trans, tfd, - i), + le64_to_cpu(tfd->tbs[i].addr), + le16_to_cpu(tfd->tbs[i].tb_len), DMA_TO_DEVICE); else dma_unmap_single(trans->dev, - iwl_pcie_gen2_tb_get_addr(trans, tfd, - i), - iwl_pcie_gen2_tb_get_addr(trans, tfd, - i), + le64_to_cpu(tfd->tbs[i].addr), + le16_to_cpu(tfd->tbs[i].tb_len), DMA_TO_DEVICE); } - if (trans->cfg->use_tfh) { - struct iwl_tfh_tfd *tfd_fh = (void *)tfd; - - tfd_fh->num_tbs = 0; - } else { - struct iwl_tfd *tfd_fh = (void *)tfd; - - tfd_fh->num_tbs = 0; - } + tfd->num_tbs = 0; } static void iwl_pcie_gen2_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) @@ -264,27 +185,13 @@ static void iwl_pcie_gen2_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) static inline void iwl_pcie_gen2_set_tb(struct iwl_trans *trans, void *tfd, u8 idx, dma_addr_t addr, u16 len) { - if (trans->cfg->use_tfh) { - struct iwl_tfh_tfd *tfd_fh = (void *)tfd; - struct iwl_tfh_tb *tb = &tfd_fh->tbs[idx]; - - put_unaligned_le64(addr, &tb->addr); - tb->tb_len = cpu_to_le16(len); - - tfd_fh->num_tbs = cpu_to_le16(idx + 1); - } else { - struct iwl_tfd *tfd_fh = (void *)tfd; - struct iwl_tfd_tb *tb = &tfd_fh->tbs[idx]; + struct iwl_tfh_tfd *tfd_fh = (void *)tfd; + struct iwl_tfh_tb *tb = &tfd_fh->tbs[idx]; - u16 hi_n_len = len << 4; + put_unaligned_le64(addr, &tb->addr); + tb->tb_len = cpu_to_le16(len); - put_unaligned_le32(addr, &tb->lo); - hi_n_len |= iwl_get_dma_hi_addr(addr); - - tb->hi_n_len = cpu_to_le16(hi_n_len); - - tfd_fh->num_tbs = idx + 1; - } + tfd_fh->num_tbs = cpu_to_le16(idx + 1); } int iwl_pcie_gen2_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, @@ -391,11 +298,9 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, void *tb1_addr; void *tfd; u16 len, tb1_len; - bool wait_write_ptr; __le16 fc; u8 hdr_len; u16 wifi_seq; - bool amsdu; txq = &trans_pcie->txq[txq_id]; @@ -403,21 +308,6 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, "TX on unused queue %d\n", txq_id)) return -EINVAL; - if (unlikely(trans_pcie->sw_csum_tx && - skb->ip_summed == CHECKSUM_PARTIAL)) { - int offs = skb_checksum_start_offset(skb); - int csum_offs = offs + skb->csum_offset; - __wsum csum; - - if (skb_ensure_writable(skb, csum_offs + sizeof(__sum16))) - return -1; - - csum = skb_checksum(skb, offs, skb->len - offs, 0); - *(__sum16 *)(skb->data + csum_offs) = csum_fold(csum); - - skb->ip_summed = CHECKSUM_UNNECESSARY; - } - if (skb_is_nonlinear(skb) && skb_shinfo(skb)->nr_frags > IWL_PCIE_MAX_FRAGS(trans_pcie) && __skb_linearize(skb)) @@ -432,24 +322,6 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, spin_lock(&txq->lock); - if (iwl_queue_space(txq) < txq->high_mark) { - iwl_stop_queue(trans, txq); - - /* don't put the packet on the ring, if there is no room */ - if (unlikely(iwl_queue_space(txq) < 3)) { - struct iwl_device_cmd **dev_cmd_ptr; - - dev_cmd_ptr = (void *)((u8 *)skb->cb + - trans_pcie->dev_cmd_offs); - - *dev_cmd_ptr = dev_cmd; - __skb_queue_tail(&txq->overflow_q, skb); - - spin_unlock(&txq->lock); - return 0; - } - } - /* In AGG mode, the index in the ring must correspond to the WiFi * sequence number. This is a HW requirements to help the SCD to parse * the BA. @@ -488,18 +360,10 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, */ len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + hdr_len - IWL_FIRST_TB_SIZE; - /* do not align A-MSDU to dword as the subframe header aligns it */ - amsdu = ieee80211_is_data_qos(fc) && - (*ieee80211_get_qos_ctl(hdr) & - IEEE80211_QOS_CTL_A_MSDU_PRESENT); - if (trans_pcie->sw_csum_tx || !amsdu) { - tb1_len = ALIGN(len, 4); - /* Tell NIC about any 2-byte padding after MAC header */ - if (tb1_len != len) - tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; - } else { - tb1_len = len; - } + tb1_len = ALIGN(len, 4); + /* Tell NIC about any 2-byte padding after MAC header */ + if (tb1_len != len) + tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; /* * The first TB points to bi-directional DMA data, we'll @@ -517,15 +381,9 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, goto out_err; iwl_pcie_gen2_build_tfd(trans, txq, tb1_phys, tb1_len, false); - if (amsdu) { - if (unlikely(iwl_fill_data_tbs_amsdu(trans, skb, txq, hdr_len, - out_meta, dev_cmd, - tb1_len))) - goto out_err; - } else if (unlikely(iwl_fill_data_tbs(trans, skb, txq, hdr_len, - out_meta, dev_cmd, tb1_len))) { + if (unlikely(iwl_fill_data_tbs(trans, skb, txq, hdr_len, + out_meta, dev_cmd, tb1_len))) goto out_err; - } /* building the A-MSDU might have changed this data, so memcpy it now */ memcpy(&txq->first_tb_bufs[txq->write_ptr], &dev_cmd->hdr, @@ -536,8 +394,6 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, iwl_pcie_gen2_update_byte_tbl(trans, txq, le16_to_cpu(tx_cmd->len), iwl_pcie_gen2_get_num_tbs(trans, tfd)); - wait_write_ptr = ieee80211_has_morefrags(fc); - /* start timer if queue currently empty */ if (txq->read_ptr == txq->write_ptr) { if (txq->wd_timeout) { @@ -559,8 +415,9 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, /* Tell device the write index *just past* this latest filled TFD */ txq->write_ptr = iwl_queue_inc_wrap(txq->write_ptr); - if (!wait_write_ptr) - iwl_pcie_gen2_txq_inc_wr_ptr(trans, txq); + iwl_pcie_gen2_txq_inc_wr_ptr(trans, txq); + if (iwl_queue_space(txq) < txq->high_mark) + iwl_stop_queue(trans, txq); /* * At this point the frame is "transmitted" successfully @@ -586,7 +443,7 @@ void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id) IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", txq_id, txq->read_ptr); - iwl_pcie_txq_free_tfd(trans, txq); + iwl_pcie_gen2_free_tfd(trans, txq); txq->read_ptr = iwl_queue_inc_wrap(txq->read_ptr); if (txq->read_ptr == txq->write_ptr) { diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 9572330e22c0..d57310eaaef7 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -2080,10 +2080,10 @@ static void iwl_compute_pseudo_hdr_csum(void *iph, struct tcphdr *tcph, } } -int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, - struct iwl_txq *txq, u8 hdr_len, - struct iwl_cmd_meta *out_meta, - struct iwl_device_cmd *dev_cmd, u16 tb1_len) +static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_txq *txq, u8 hdr_len, + struct iwl_cmd_meta *out_meta, + struct iwl_device_cmd *dev_cmd, u16 tb1_len) { struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload; struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; -- cgit From b97277ccc61d11d354b6a7d027c6316cadc76676 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 24 Jan 2017 15:29:57 +0200 Subject: iwlwifi: pcie: support new TX command Move to use the correct structure. Remove code referring to old command. Update DMA locations. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 10 +--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index bd358895caee..912d475ed292 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -205,11 +205,11 @@ struct iwl_cmd_meta { * into the buffer regardless of whether it should be mapped or not. * This indicates how big the first TB must be to include the scratch buffer * and the assigned PN. - * Since PN location is 16 bytes at offset 24, it's 40 now. + * Since PN location is 8 bytes at offset 12, it's 20 now. * If we make it bigger then allocations will be bigger and copy slower, so * that's probably not useful. */ -#define IWL_FIRST_TB_SIZE 40 +#define IWL_FIRST_TB_SIZE 20 #define IWL_FIRST_TB_SIZE_ALIGN ALIGN(IWL_FIRST_TB_SIZE, 64) struct iwl_pcie_txq_entry { diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 3a36bda5fd27..67af73ef7d0d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -284,14 +284,12 @@ static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb, return 0; } -#define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(TX_CMD_FLG_MH_PAD) - int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct ieee80211_hdr *hdr; - struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; + struct iwl_tx_cmd_gen2 *tx_cmd = (void *)dev_cmd->payload; struct iwl_cmd_meta *out_meta; struct iwl_txq *txq; dma_addr_t tb0_phys, tb1_phys, scratch_phys; @@ -345,9 +343,6 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) + offsetof(struct iwl_tx_cmd, scratch); - tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); - tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); - /* Set up first empty entry in queue's array of Tx/cmd buffers */ out_meta = &txq->entries[txq->write_ptr].meta; out_meta->flags = 0; @@ -361,9 +356,6 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + hdr_len - IWL_FIRST_TB_SIZE; tb1_len = ALIGN(len, 4); - /* Tell NIC about any 2-byte padding after MAC header */ - if (tb1_len != len) - tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; /* * The first TB points to bi-directional DMA data, we'll -- cgit From cefe13af25275eabc6d2ad4f6b67ee7b06f94c5f Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 24 Jan 2017 15:50:35 +0200 Subject: iwlwifi: pcie: rewrite TFD creation Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 215 +++++++++------------- 1 file changed, 82 insertions(+), 133 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 67af73ef7d0d..4717440bc7f9 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -119,11 +119,10 @@ static u8 iwl_pcie_gen2_get_num_tbs(struct iwl_trans *trans, static void iwl_pcie_gen2_tfd_unmap(struct iwl_trans *trans, struct iwl_cmd_meta *meta, - struct iwl_txq *txq, int index) + struct iwl_tfh_tfd *tfd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int i, num_tbs; - struct iwl_tfh_tfd *tfd = iwl_pcie_get_tfd(trans_pcie, txq, index); /* Sanity check on number of chunks */ num_tbs = iwl_pcie_gen2_get_num_tbs(trans, tfd); @@ -152,6 +151,8 @@ static void iwl_pcie_gen2_tfd_unmap(struct iwl_trans *trans, static void iwl_pcie_gen2_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and * idx is bounded by n_window */ @@ -163,7 +164,8 @@ static void iwl_pcie_gen2_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) /* We have only q->n_window txq->entries, but we use * TFD_QUEUE_SIZE_MAX tfds */ - iwl_pcie_gen2_tfd_unmap(trans, &txq->entries[idx].meta, txq, rd_ptr); + iwl_pcie_gen2_tfd_unmap(trans, &txq->entries[idx].meta, + iwl_pcie_get_tfd(trans_pcie, txq, rd_ptr)); /* free SKB */ if (txq->entries) { @@ -182,79 +184,89 @@ static void iwl_pcie_gen2_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) } } -static inline void iwl_pcie_gen2_set_tb(struct iwl_trans *trans, void *tfd, - u8 idx, dma_addr_t addr, u16 len) -{ - struct iwl_tfh_tfd *tfd_fh = (void *)tfd; - struct iwl_tfh_tb *tb = &tfd_fh->tbs[idx]; - - put_unaligned_le64(addr, &tb->addr); - tb->tb_len = cpu_to_le16(len); - - tfd_fh->num_tbs = cpu_to_le16(idx + 1); -} - -int iwl_pcie_gen2_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, - dma_addr_t addr, u16 len, bool reset) +static int iwl_pcie_gen2_set_tb(struct iwl_trans *trans, + struct iwl_tfh_tfd *tfd, dma_addr_t addr, + u16 len) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - void *tfd; - u32 num_tbs; - - tfd = txq->tfds + trans_pcie->tfd_size * txq->write_ptr; - - if (reset) - memset(tfd, 0, trans_pcie->tfd_size); - - num_tbs = iwl_pcie_gen2_get_num_tbs(trans, tfd); + int idx = iwl_pcie_gen2_get_num_tbs(trans, tfd); + struct iwl_tfh_tb *tb = &tfd->tbs[idx]; /* Each TFD can point to a maximum max_tbs Tx buffers */ - if (num_tbs >= trans_pcie->max_tbs) { + if (tfd->num_tbs >= trans_pcie->max_tbs) { IWL_ERR(trans, "Error can not send more than %d chunks\n", trans_pcie->max_tbs); return -EINVAL; } - if (WARN(addr & ~IWL_TX_DMA_MASK, - "Unaligned address = %llx\n", (unsigned long long)addr)) - return -EINVAL; + put_unaligned_le64(addr, &tb->addr); + tb->tb_len = cpu_to_le16(len); - iwl_pcie_gen2_set_tb(trans, tfd, num_tbs, addr, len); + tfd->num_tbs = cpu_to_le16(idx + 1); - return num_tbs; + return idx; } -static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb, - struct iwl_txq *txq, u8 hdr_len, - struct iwl_cmd_meta *out_meta, - struct iwl_device_cmd *dev_cmd, u16 tb1_len) +static +struct iwl_tfh_tfd *iwl_pcie_gen2_build_tfd(struct iwl_trans *trans, + struct iwl_txq *txq, + struct iwl_device_cmd *dev_cmd, + struct sk_buff *skb, + struct iwl_cmd_meta *out_meta) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u16 tb2_len; - int i; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct iwl_tfh_tfd *tfd = + iwl_pcie_get_tfd(trans_pcie, txq, txq->write_ptr); + dma_addr_t tb_phys; + int i, len, tb1_len, tb2_len, hdr_len; + void *tb1_addr; + + memset(tfd, 0, sizeof(*tfd)); + + tb_phys = iwl_pcie_get_first_tb_dma(txq, txq->write_ptr); + /* The first TB points to bi-directional DMA data */ + memcpy(&txq->first_tb_bufs[txq->write_ptr], &dev_cmd->hdr, + IWL_FIRST_TB_SIZE); + + iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, IWL_FIRST_TB_SIZE); + + /* there must be data left over for TB1 or this code must be changed */ + BUILD_BUG_ON(sizeof(struct iwl_tx_cmd_gen2) < IWL_FIRST_TB_SIZE); /* - * Set up TFD's third entry to point directly to remainder - * of skb's head, if any + * The second TB (tb1) points to the remainder of the TX command + * and the 802.11 header - dword aligned size + * (This calculation modifies the TX command, so do it before the + * setup of the first TB) */ + len = sizeof(struct iwl_tx_cmd_gen2) + sizeof(struct iwl_cmd_header) + + ieee80211_hdrlen(hdr->frame_control) - IWL_FIRST_TB_SIZE; + + tb1_len = ALIGN(len, 4); + + /* map the data for TB1 */ + tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_FIRST_TB_SIZE; + tb_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, tb_phys))) + goto out_err; + iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, tb1_len); + + /* set up TFD's third entry to point to remainder of skb's head */ + hdr_len = ieee80211_hdrlen(hdr->frame_control); tb2_len = skb_headlen(skb) - hdr_len; if (tb2_len > 0) { - dma_addr_t tb2_phys = dma_map_single(trans->dev, - skb->data + hdr_len, - tb2_len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(trans->dev, tb2_phys))) { - iwl_pcie_gen2_tfd_unmap(trans, out_meta, txq, - txq->write_ptr); - return -EINVAL; - } - iwl_pcie_gen2_build_tfd(trans, txq, tb2_phys, tb2_len, false); + tb_phys = dma_map_single(trans->dev, skb->data + hdr_len, + tb2_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, tb_phys))) + goto out_err; + iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, tb2_len); } /* set up the remaining entries to point to the data */ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - dma_addr_t tb_phys; int tb_idx; if (!skb_frag_size(frag)) @@ -263,44 +275,35 @@ static int iwl_fill_data_tbs(struct iwl_trans *trans, struct sk_buff *skb, tb_phys = skb_frag_dma_map(trans->dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(trans->dev, tb_phys))) { - iwl_pcie_gen2_tfd_unmap(trans, out_meta, txq, - txq->write_ptr); - return -EINVAL; - } - tb_idx = iwl_pcie_gen2_build_tfd(trans, txq, tb_phys, - skb_frag_size(frag), false); + if (unlikely(dma_mapping_error(trans->dev, tb_phys))) + goto out_err; + tb_idx = iwl_pcie_gen2_set_tb(trans, tfd, tb_phys, + skb_frag_size(frag)); out_meta->tbs |= BIT(tb_idx); } - trace_iwlwifi_dev_tx(trans->dev, skb, - iwl_pcie_get_tfd(trans_pcie, txq, txq->write_ptr), - trans_pcie->tfd_size, - &dev_cmd->hdr, IWL_FIRST_TB_SIZE + tb1_len, + trace_iwlwifi_dev_tx(trans->dev, skb, tfd, sizeof(*tfd), &dev_cmd->hdr, + IWL_FIRST_TB_SIZE + tb1_len, skb->data + hdr_len, tb2_len); - trace_iwlwifi_dev_tx_data(trans->dev, skb, - hdr_len, skb->len - hdr_len); - return 0; + trace_iwlwifi_dev_tx_data(trans->dev, skb, hdr_len, + skb->len - hdr_len); + + return tfd; + +out_err: + iwl_pcie_gen2_tfd_unmap(trans, out_meta, tfd); + return NULL; } int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct ieee80211_hdr *hdr; struct iwl_tx_cmd_gen2 *tx_cmd = (void *)dev_cmd->payload; struct iwl_cmd_meta *out_meta; - struct iwl_txq *txq; - dma_addr_t tb0_phys, tb1_phys, scratch_phys; - void *tb1_addr; + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; void *tfd; - u16 len, tb1_len; - __le16 fc; - u8 hdr_len; - u16 wifi_seq; - - txq = &trans_pcie->txq[txq_id]; if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used), "TX on unused queue %d\n", txq_id)) @@ -311,26 +314,8 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, __skb_linearize(skb)) return -ENOMEM; - /* mac80211 always puts the full header into the SKB's head, - * so there's no need to check if it's readable there - */ - hdr = (struct ieee80211_hdr *)skb->data; - fc = hdr->frame_control; - hdr_len = ieee80211_hdrlen(fc); - spin_lock(&txq->lock); - /* In AGG mode, the index in the ring must correspond to the WiFi - * sequence number. This is a HW requirements to help the SCD to parse - * the BA. - * Check here that the packets are in the right place on the ring. - */ - wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); - WARN_ONCE(txq->ampdu && - (wifi_seq & 0xff) != txq->write_ptr, - "Q: %d WiFi Seq %d tfdNum %d", - txq_id, wifi_seq, txq->write_ptr); - /* Set up driver data for this TFD */ txq->entries[txq->write_ptr].skb = skb; txq->entries[txq->write_ptr].cmd = dev_cmd; @@ -339,49 +324,16 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | INDEX_TO_SEQ(txq->write_ptr))); - tb0_phys = iwl_pcie_get_first_tb_dma(txq, txq->write_ptr); - scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) + - offsetof(struct iwl_tx_cmd, scratch); - /* Set up first empty entry in queue's array of Tx/cmd buffers */ out_meta = &txq->entries[txq->write_ptr].meta; out_meta->flags = 0; - /* - * The second TB (tb1) points to the remainder of the TX command - * and the 802.11 header - dword aligned size - * (This calculation modifies the TX command, so do it before the - * setup of the first TB) - */ - len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + - hdr_len - IWL_FIRST_TB_SIZE; - tb1_len = ALIGN(len, 4); - - /* - * The first TB points to bi-directional DMA data, we'll - * memcpy the data into it later. - */ - iwl_pcie_gen2_build_tfd(trans, txq, tb0_phys, IWL_FIRST_TB_SIZE, true); - - /* there must be data left over for TB1 or this code must be changed */ - BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_FIRST_TB_SIZE); - - /* map the data for TB1 */ - tb1_addr = ((u8 *)&dev_cmd->hdr) + IWL_FIRST_TB_SIZE; - tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(trans->dev, tb1_phys))) - goto out_err; - iwl_pcie_gen2_build_tfd(trans, txq, tb1_phys, tb1_len, false); - - if (unlikely(iwl_fill_data_tbs(trans, skb, txq, hdr_len, - out_meta, dev_cmd, tb1_len))) - goto out_err; - - /* building the A-MSDU might have changed this data, so memcpy it now */ - memcpy(&txq->first_tb_bufs[txq->write_ptr], &dev_cmd->hdr, - IWL_FIRST_TB_SIZE); + tfd = iwl_pcie_gen2_build_tfd(trans, txq, dev_cmd, skb, out_meta); + if (!tfd) { + spin_unlock(&txq->lock); + return -1; + } - tfd = iwl_pcie_get_tfd(trans_pcie, txq, txq->write_ptr); /* Set up entry for this TFD in Tx byte-count array */ iwl_pcie_gen2_update_byte_tbl(trans, txq, le16_to_cpu(tx_cmd->len), iwl_pcie_gen2_get_num_tbs(trans, tfd)); @@ -417,9 +369,6 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, */ spin_unlock(&txq->lock); return 0; -out_err: - spin_unlock(&txq->lock); - return -1; } /* -- cgit From ca60da2eb41641183c9a1b85a7de26f1327ba356 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 8 Dec 2016 13:22:55 +0200 Subject: iwlwifi: pcie: support host commands in new transport Code is basically the same, with a cleanups of old narrow host command, ampg workarounds, some cosmetic stuff, and usage of TFH functions when accessing TFD queues. This enables also the cleanup of iwl_pcie_tfd_set_tb() since now it won't be called anywhere in the a000 data path Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 2 + drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 386 +++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 24 +- 4 files changed, 396 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 912d475ed292..2266f3aa67aa 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -778,5 +778,7 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue); int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int txq_id); +int iwl_trans_pcie_gen2_send_hcmd(struct iwl_trans *trans, + struct iwl_host_cmd *cmd); #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index e1610241be07..16a057398de2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2918,7 +2918,7 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = { .start_fw = iwl_trans_pcie_gen2_start_fw, .stop_device = iwl_trans_pcie_stop_device, - .send_cmd = iwl_trans_pcie_send_hcmd, + .send_cmd = iwl_trans_pcie_gen2_send_hcmd, .tx = iwl_trans_pcie_gen2_tx, .reclaim = iwl_trans_pcie_reclaim, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 4717440bc7f9..af5b97c82c5e 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -48,6 +48,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ +#include #include "iwl-debug.h" #include "iwl-csr.h" @@ -371,6 +372,391 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, return 0; } +/*************** HOST COMMAND QUEUE FUNCTIONS *****/ + +/* + * iwl_pcie_gen2_enqueue_hcmd - enqueue a uCode command + * @priv: device private data point + * @cmd: a pointer to the ucode command structure + * + * The function returns < 0 values to indicate the operation + * failed. On success, it returns the index (>= 0) of command in the + * command queue. + */ +static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, + struct iwl_host_cmd *cmd) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_device_cmd *out_cmd; + struct iwl_cmd_meta *out_meta; + unsigned long flags; + void *dup_buf = NULL; + dma_addr_t phys_addr; + int idx, i, cmd_pos; + u16 copy_size, cmd_size, tb0_size; + bool had_nocopy = false; + u8 group_id = iwl_cmd_groupid(cmd->id); + const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD]; + u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD]; + struct iwl_tfh_tfd *tfd = + iwl_pcie_get_tfd(trans_pcie, txq, txq->write_ptr); + + memset(tfd, 0, sizeof(*tfd)); + + copy_size = sizeof(struct iwl_cmd_header_wide); + cmd_size = sizeof(struct iwl_cmd_header_wide); + + for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { + cmddata[i] = cmd->data[i]; + cmdlen[i] = cmd->len[i]; + + if (!cmd->len[i]) + continue; + + /* need at least IWL_FIRST_TB_SIZE copied */ + if (copy_size < IWL_FIRST_TB_SIZE) { + int copy = IWL_FIRST_TB_SIZE - copy_size; + + if (copy > cmdlen[i]) + copy = cmdlen[i]; + cmdlen[i] -= copy; + cmddata[i] += copy; + copy_size += copy; + } + + if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { + had_nocopy = true; + if (WARN_ON(cmd->dataflags[i] & IWL_HCMD_DFL_DUP)) { + idx = -EINVAL; + goto free_dup_buf; + } + } else if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) { + /* + * This is also a chunk that isn't copied + * to the static buffer so set had_nocopy. + */ + had_nocopy = true; + + /* only allowed once */ + if (WARN_ON(dup_buf)) { + idx = -EINVAL; + goto free_dup_buf; + } + + dup_buf = kmemdup(cmddata[i], cmdlen[i], + GFP_ATOMIC); + if (!dup_buf) + return -ENOMEM; + } else { + /* NOCOPY must not be followed by normal! */ + if (WARN_ON(had_nocopy)) { + idx = -EINVAL; + goto free_dup_buf; + } + copy_size += cmdlen[i]; + } + cmd_size += cmd->len[i]; + } + + /* + * If any of the command structures end up being larger than the + * TFD_MAX_PAYLOAD_SIZE and they aren't dynamically allocated into + * separate TFDs, then we will need to increase the size of the buffers + */ + if (WARN(copy_size > TFD_MAX_PAYLOAD_SIZE, + "Command %s (%#x) is too large (%d bytes)\n", + iwl_get_cmd_string(trans, cmd->id), cmd->id, copy_size)) { + idx = -EINVAL; + goto free_dup_buf; + } + + spin_lock_bh(&txq->lock); + + if (iwl_queue_space(txq) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { + spin_unlock_bh(&txq->lock); + + IWL_ERR(trans, "No space in command queue\n"); + iwl_op_mode_cmd_queue_full(trans->op_mode); + idx = -ENOSPC; + goto free_dup_buf; + } + + idx = get_cmd_index(txq, txq->write_ptr); + out_cmd = txq->entries[idx].cmd; + out_meta = &txq->entries[idx].meta; + + /* re-initialize to NULL */ + memset(out_meta, 0, sizeof(*out_meta)); + if (cmd->flags & CMD_WANT_SKB) + out_meta->source = cmd; + + /* set up the header */ + out_cmd->hdr_wide.cmd = iwl_cmd_opcode(cmd->id); + out_cmd->hdr_wide.group_id = group_id; + out_cmd->hdr_wide.version = iwl_cmd_version(cmd->id); + out_cmd->hdr_wide.length = + cpu_to_le16(cmd_size - sizeof(struct iwl_cmd_header_wide)); + out_cmd->hdr_wide.reserved = 0; + out_cmd->hdr_wide.sequence = + cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | + INDEX_TO_SEQ(txq->write_ptr)); + + cmd_pos = sizeof(struct iwl_cmd_header_wide); + copy_size = sizeof(struct iwl_cmd_header_wide); + + /* and copy the data that needs to be copied */ + for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { + int copy; + + if (!cmd->len[i]) + continue; + + /* copy everything if not nocopy/dup */ + if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | + IWL_HCMD_DFL_DUP))) { + copy = cmd->len[i]; + + memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); + cmd_pos += copy; + copy_size += copy; + continue; + } + + /* + * Otherwise we need at least IWL_FIRST_TB_SIZE copied + * in total (for bi-directional DMA), but copy up to what + * we can fit into the payload for debug dump purposes. + */ + copy = min_t(int, TFD_MAX_PAYLOAD_SIZE - cmd_pos, cmd->len[i]); + + memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); + cmd_pos += copy; + + /* However, treat copy_size the proper way, we need it below */ + if (copy_size < IWL_FIRST_TB_SIZE) { + copy = IWL_FIRST_TB_SIZE - copy_size; + + if (copy > cmd->len[i]) + copy = cmd->len[i]; + copy_size += copy; + } + } + + IWL_DEBUG_HC(trans, + "Sending command %s (%.2x.%.2x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", + iwl_get_cmd_string(trans, cmd->id), group_id, + out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), + cmd_size, txq->write_ptr, idx, trans_pcie->cmd_queue); + + /* start the TFD with the minimum copy bytes */ + tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE); + memcpy(&txq->first_tb_bufs[idx], &out_cmd->hdr, tb0_size); + iwl_pcie_gen2_set_tb(trans, tfd, iwl_pcie_get_first_tb_dma(txq, idx), + tb0_size); + + /* map first command fragment, if any remains */ + if (copy_size > tb0_size) { + phys_addr = dma_map_single(trans->dev, + ((u8 *)&out_cmd->hdr) + tb0_size, + copy_size - tb0_size, + DMA_TO_DEVICE); + if (dma_mapping_error(trans->dev, phys_addr)) { + idx = -ENOMEM; + iwl_pcie_gen2_tfd_unmap(trans, out_meta, tfd); + goto out; + } + iwl_pcie_gen2_set_tb(trans, tfd, phys_addr, + copy_size - tb0_size); + } + + /* map the remaining (adjusted) nocopy/dup fragments */ + for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { + const void *data = cmddata[i]; + + if (!cmdlen[i]) + continue; + if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | + IWL_HCMD_DFL_DUP))) + continue; + if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) + data = dup_buf; + phys_addr = dma_map_single(trans->dev, (void *)data, + cmdlen[i], DMA_TO_DEVICE); + if (dma_mapping_error(trans->dev, phys_addr)) { + idx = -ENOMEM; + iwl_pcie_gen2_tfd_unmap(trans, out_meta, tfd); + goto out; + } + iwl_pcie_gen2_set_tb(trans, tfd, phys_addr, cmdlen[i]); + } + + BUILD_BUG_ON(IWL_TFH_NUM_TBS > sizeof(out_meta->tbs) * BITS_PER_BYTE); + out_meta->flags = cmd->flags; + if (WARN_ON_ONCE(txq->entries[idx].free_buf)) + kzfree(txq->entries[idx].free_buf); + txq->entries[idx].free_buf = dup_buf; + + trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr_wide); + + /* start timer if queue currently empty */ + if (txq->read_ptr == txq->write_ptr && txq->wd_timeout) + mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout); + + spin_lock_irqsave(&trans_pcie->reg_lock, flags); + if (!(cmd->flags & CMD_SEND_IN_IDLE) && + !trans_pcie->ref_cmd_in_flight) { + trans_pcie->ref_cmd_in_flight = true; + IWL_DEBUG_RPM(trans, "set ref_cmd_in_flight - ref\n"); + iwl_trans_ref(trans); + } + /* Increment and update queue's write index */ + txq->write_ptr = iwl_queue_inc_wrap(txq->write_ptr); + iwl_pcie_gen2_txq_inc_wr_ptr(trans, txq); + spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); + +out: + spin_unlock_bh(&txq->lock); +free_dup_buf: + if (idx < 0) + kfree(dup_buf); + return idx; +} + +#define HOST_COMPLETE_TIMEOUT (2 * HZ) + +static int iwl_pcie_gen2_send_hcmd_sync(struct iwl_trans *trans, + struct iwl_host_cmd *cmd) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + const char *cmd_str = iwl_get_cmd_string(trans, cmd->id); + int cmd_idx; + int ret; + + IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", cmd_str); + + if (WARN(test_and_set_bit(STATUS_SYNC_HCMD_ACTIVE, + &trans->status), + "Command %s: a command is already active!\n", cmd_str)) + return -EIO; + + IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", cmd_str); + + if (pm_runtime_suspended(&trans_pcie->pci_dev->dev)) { + ret = wait_event_timeout(trans_pcie->d0i3_waitq, + pm_runtime_active(&trans_pcie->pci_dev->dev), + msecs_to_jiffies(IWL_TRANS_IDLE_TIMEOUT)); + if (!ret) { + IWL_ERR(trans, "Timeout exiting D0i3 before hcmd\n"); + return -ETIMEDOUT; + } + } + + cmd_idx = iwl_pcie_gen2_enqueue_hcmd(trans, cmd); + if (cmd_idx < 0) { + ret = cmd_idx; + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", + cmd_str, ret); + return ret; + } + + ret = wait_event_timeout(trans_pcie->wait_command_queue, + !test_bit(STATUS_SYNC_HCMD_ACTIVE, + &trans->status), + HOST_COMPLETE_TIMEOUT); + if (!ret) { + struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + + IWL_ERR(trans, "Error sending %s: time out after %dms.\n", + cmd_str, jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); + + IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n", + txq->read_ptr, txq->write_ptr); + + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", + cmd_str); + ret = -ETIMEDOUT; + + iwl_force_nmi(trans); + iwl_trans_fw_error(trans); + + goto cancel; + } + + if (test_bit(STATUS_FW_ERROR, &trans->status)) { + IWL_ERR(trans, "FW error in SYNC CMD %s\n", cmd_str); + dump_stack(); + ret = -EIO; + goto cancel; + } + + if (!(cmd->flags & CMD_SEND_IN_RFKILL) && + test_bit(STATUS_RFKILL, &trans->status)) { + IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n"); + ret = -ERFKILL; + goto cancel; + } + + if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) { + IWL_ERR(trans, "Error: Response NULL in '%s'\n", cmd_str); + ret = -EIO; + goto cancel; + } + + return 0; + +cancel: + if (cmd->flags & CMD_WANT_SKB) { + /* + * Cancel the CMD_WANT_SKB flag for the cmd in the + * TX cmd queue. Otherwise in case the cmd comes + * in later, it will possibly set an invalid + * address (cmd->meta.source). + */ + trans_pcie->txq[trans_pcie->cmd_queue]. + entries[cmd_idx].meta.flags &= ~CMD_WANT_SKB; + } + + if (cmd->resp_pkt) { + iwl_free_resp(cmd); + cmd->resp_pkt = NULL; + } + + return ret; +} + +int iwl_trans_pcie_gen2_send_hcmd(struct iwl_trans *trans, + struct iwl_host_cmd *cmd) +{ + if (!(cmd->flags & CMD_SEND_IN_RFKILL) && + test_bit(STATUS_RFKILL, &trans->status)) { + IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n", + cmd->id); + return -ERFKILL; + } + + if (cmd->flags & CMD_ASYNC) { + int ret; + + /* An asynchronous command can not expect an SKB to be set. */ + if (WARN_ON(cmd->flags & CMD_WANT_SKB)) + return -EINVAL; + + ret = iwl_pcie_gen2_enqueue_hcmd(trans, cmd); + if (ret < 0) { + IWL_ERR(trans, + "Error sending %s: enqueue_hcmd failed: %d\n", + iwl_get_cmd_string(trans, cmd->id), ret); + return ret; + } + return 0; + } + + return iwl_pcie_gen2_send_hcmd_sync(trans, cmd); +} + /* * iwl_pcie_gen2_txq_unmap - Unmap any remaining DMA mappings and free skb's */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index d57310eaaef7..5af6eb98eb43 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -341,27 +341,17 @@ static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_trans *trans, static inline void iwl_pcie_tfd_set_tb(struct iwl_trans *trans, void *tfd, u8 idx, dma_addr_t addr, u16 len) { - if (trans->cfg->use_tfh) { - struct iwl_tfh_tfd *tfd_fh = (void *)tfd; - struct iwl_tfh_tb *tb = &tfd_fh->tbs[idx]; + struct iwl_tfd *tfd_fh = (void *)tfd; + struct iwl_tfd_tb *tb = &tfd_fh->tbs[idx]; - put_unaligned_le64(addr, &tb->addr); - tb->tb_len = cpu_to_le16(len); + u16 hi_n_len = len << 4; - tfd_fh->num_tbs = cpu_to_le16(idx + 1); - } else { - struct iwl_tfd *tfd_fh = (void *)tfd; - struct iwl_tfd_tb *tb = &tfd_fh->tbs[idx]; + put_unaligned_le32(addr, &tb->lo); + hi_n_len |= iwl_get_dma_hi_addr(addr); - u16 hi_n_len = len << 4; + tb->hi_n_len = cpu_to_le16(hi_n_len); - put_unaligned_le32(addr, &tb->lo); - hi_n_len |= iwl_get_dma_hi_addr(addr); - - tb->hi_n_len = cpu_to_le16(hi_n_len); - - tfd_fh->num_tbs = idx + 1; - } + tfd_fh->num_tbs = idx + 1; } static inline u8 iwl_pcie_tfd_get_num_tbs(struct iwl_trans *trans, void *_tfd) -- cgit From 4822929388df78c57f85bc4f8aca781e268da44f Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 8 Dec 2016 12:05:58 +0200 Subject: iwlwifi: pcie: support new write pointer width In a000 devices we have 16 bytes for the TFD index and 16 for the queue, in order to support 512 queues. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index af5b97c82c5e..e8eb11282795 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -109,7 +109,7 @@ static void iwl_pcie_gen2_txq_inc_wr_ptr(struct iwl_trans *trans, */ if (!txq->block) iwl_write32(trans, HBUS_TARG_WRPTR, - txq->write_ptr | (txq->id << 8)); + txq->write_ptr | (txq->id << 16)); } static u8 iwl_pcie_gen2_get_num_tbs(struct iwl_trans *trans, @@ -825,7 +825,7 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, txq->read_ptr = (ssn & 0xff); txq->write_ptr = (ssn & 0xff); iwl_write_direct32(trans, HBUS_TARG_WRPTR, - (ssn & 0xff) | (cmd->scd_queue << 8)); + (ssn & 0xff) | (cmd->scd_queue << 16)); IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d WrPtr: %d\n", cmd->scd_queue, ssn & 0xff); -- cgit From 43e9cdc268cbc462d88d6b48dbc36b34429b782b Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 29 Nov 2016 13:19:25 +0200 Subject: iwlwifi: pcie: remove block and freeze operations from new transport New transport will be used only by op modes that supports buffer station offload - hence those will never be called. Clean it up. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 3 --- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 22 +++------------------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 16a057398de2..d39ca944f484 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2925,9 +2925,6 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = { .txq_alloc = iwl_trans_pcie_dyn_txq_alloc, .txq_free = iwl_trans_pcie_dyn_txq_free, - - .freeze_txq_timer = iwl_trans_pcie_freeze_txq_timer, - .block_txq_ptrs = iwl_trans_pcie_block_txq_ptrs, }; struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index e8eb11282795..2019ccda31c4 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -107,9 +107,7 @@ static void iwl_pcie_gen2_txq_inc_wr_ptr(struct iwl_trans *trans, * if not in power-save mode, uCode will never sleep when we're * trying to tx (during RFKILL, we're not trying to tx). */ - if (!txq->block) - iwl_write32(trans, HBUS_TARG_WRPTR, - txq->write_ptr | (txq->id << 16)); + iwl_write32(trans, HBUS_TARG_WRPTR, txq->write_ptr | (txq->id << 16)); } static u8 iwl_pcie_gen2_get_num_tbs(struct iwl_trans *trans, @@ -341,19 +339,8 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, /* start timer if queue currently empty */ if (txq->read_ptr == txq->write_ptr) { - if (txq->wd_timeout) { - /* - * If the TXQ is active, then set the timer, if not, - * set the timer in remainder so that the timer will - * be armed with the right value when the station will - * wake up. - */ - if (!txq->frozen) - mod_timer(&txq->stuck_timer, - jiffies + txq->wd_timeout); - else - txq->frozen_expiry_remainder = txq->wd_timeout; - } + if (txq->wd_timeout) + mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout); IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", txq->id); iwl_trans_ref(trans); } @@ -843,9 +830,6 @@ void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - trans_pcie->txq[queue].frozen_expiry_remainder = 0; - trans_pcie->txq[queue].frozen = false; - /* * Upon HW Rfkill - we stop the device, and then stop the queues * in the op_mode. Just for the sake of the simplicity of the op_mode, -- cgit From b2a3b1c1044cb686d3b975fa36e42f477b2aa386 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 11 Dec 2016 11:36:38 +0200 Subject: iwlwifi: pcie: prepare for dynamic queue allocation In a000 transport we will allocate queues dynamically. Right now queue are allocated as one big chunk of memory and accessed as such. The dynamic allocation of the queues will require accessing the queues as pointers. In order to keep simplicity of pre-a000 tx queues handling, keep allocating and freeing the memory in the same style, but move to access the queues in the various functions as individual pointers. Dynamic allocation for the a000 devices will be in a separate patch. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/pcie/ctxt-info.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 3 +- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 4 +- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 12 ++-- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 14 ++-- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 81 +++++++++++----------- 6 files changed, 58 insertions(+), 58 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c index 312ee0481ec5..854d61888f4d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c @@ -235,7 +235,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, /* initialize TX command queue */ ctxt_info->hcmd_cfg.cmd_queue_addr = - cpu_to_le64(trans_pcie->txq[trans_pcie->cmd_queue].dma_addr); + cpu_to_le64(trans_pcie->txq[trans_pcie->cmd_queue]->dma_addr); ctxt_info->hcmd_cfg.cmd_queue_size = TFD_QUEUE_CB_SIZE(TFD_QUEUE_SIZE_MAX); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 2266f3aa67aa..cdc2b0a938a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -411,7 +411,8 @@ struct iwl_trans_pcie { struct iwl_dma_ptr scd_bc_tbls; struct iwl_dma_ptr kw; - struct iwl_txq *txq; + struct iwl_txq *txq_memory; + struct iwl_txq *txq[IWL_MAX_HW_QUEUES]; unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index c6178d36698c..0338c5f41ce6 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1094,7 +1094,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, bool emergency) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_txq *txq = trans_pcie->txq[trans_pcie->cmd_queue]; bool page_stolen = false; int max_len = PAGE_SIZE << trans_pcie->rx_page_order; u32 offset = 0; @@ -1420,7 +1420,7 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) local_bh_enable(); for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) - del_timer(&trans_pcie->txq[i].stuck_timer); + del_timer(&trans_pcie->txq[i]->stuck_timer); clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); wake_up(&trans_pcie->wait_command_queue); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index d39ca944f484..ccc9280d845a 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1983,7 +1983,7 @@ static void iwl_trans_pcie_freeze_txq_timer(struct iwl_trans *trans, int queue; for_each_set_bit(queue, &txqs, BITS_PER_LONG) { - struct iwl_txq *txq = &trans_pcie->txq[queue]; + struct iwl_txq *txq = trans_pcie->txq[queue]; unsigned long now; spin_lock_bh(&txq->lock); @@ -2035,7 +2035,7 @@ static void iwl_trans_pcie_block_txq_ptrs(struct iwl_trans *trans, bool block) int i; for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { - struct iwl_txq *txq = &trans_pcie->txq[i]; + struct iwl_txq *txq = trans_pcie->txq[i]; if (i == trans_pcie->cmd_queue) continue; @@ -2108,7 +2108,7 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) continue; IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", cnt); - txq = &trans_pcie->txq[cnt]; + txq = trans_pcie->txq[cnt]; wr_ptr = ACCESS_ONCE(txq->write_ptr); while (txq->read_ptr != ACCESS_ONCE(txq->write_ptr) && @@ -2299,7 +2299,7 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, bufsz = sizeof(char) * 75 * trans->cfg->base_params->num_of_queues; - if (!trans_pcie->txq) + if (!trans_pcie->txq_memory) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); @@ -2307,7 +2307,7 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, return -ENOMEM; for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { - txq = &trans_pcie->txq[cnt]; + txq = trans_pcie->txq[cnt]; pos += scnprintf(buf + pos, bufsz - pos, "hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d frozen=%d%s\n", cnt, txq->read_ptr, txq->write_ptr, @@ -2724,7 +2724,7 @@ static struct iwl_trans_dump_data { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_fw_error_dump_data *data; - struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_txq *cmdq = trans_pcie->txq[trans_pcie->cmd_queue]; struct iwl_fw_error_dump_txcmd *txcmd; struct iwl_trans_dump_data *dump_data; u32 len, num_rbs; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 2019ccda31c4..b938a6a3b169 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -301,7 +301,7 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_cmd_gen2 *tx_cmd = (void *)dev_cmd->payload; struct iwl_cmd_meta *out_meta; - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct iwl_txq *txq = trans_pcie->txq[txq_id]; void *tfd; if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used), @@ -374,7 +374,7 @@ static int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_txq *txq = trans_pcie->txq[trans_pcie->cmd_queue]; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; unsigned long flags; @@ -617,6 +617,7 @@ static int iwl_pcie_gen2_send_hcmd_sync(struct iwl_trans *trans, { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); const char *cmd_str = iwl_get_cmd_string(trans, cmd->id); + struct iwl_txq *txq = trans_pcie->txq[trans_pcie->cmd_queue]; int cmd_idx; int ret; @@ -653,8 +654,6 @@ static int iwl_pcie_gen2_send_hcmd_sync(struct iwl_trans *trans, &trans->status), HOST_COMPLETE_TIMEOUT); if (!ret) { - struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; - IWL_ERR(trans, "Error sending %s: time out after %dms.\n", cmd_str, jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); @@ -702,8 +701,7 @@ cancel: * in later, it will possibly set an invalid * address (cmd->meta.source). */ - trans_pcie->txq[trans_pcie->cmd_queue]. - entries[cmd_idx].meta.flags &= ~CMD_WANT_SKB; + txq->entries[cmd_idx].meta.flags &= ~CMD_WANT_SKB; } if (cmd->resp_pkt) { @@ -750,7 +748,7 @@ int iwl_trans_pcie_gen2_send_hcmd(struct iwl_trans *trans, void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct iwl_txq *txq = trans_pcie->txq[txq_id]; spin_lock_bh(&txq->lock); while (txq->write_ptr != txq->read_ptr) { @@ -789,7 +787,7 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, unsigned int timeout) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[cmd->scd_queue]; + struct iwl_txq *txq = trans_pcie->txq[cmd->scd_queue]; struct iwl_host_cmd hcmd = { .id = cmd_id, .len = { sizeof(*cmd) }, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 5af6eb98eb43..9ea92ac3920e 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -298,12 +298,12 @@ void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans) int i; for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { - struct iwl_txq *txq = &trans_pcie->txq[i]; + struct iwl_txq *txq = trans_pcie->txq[i]; spin_lock_bh(&txq->lock); - if (trans_pcie->txq[i].need_update) { + if (txq->need_update) { iwl_pcie_txq_inc_wr_ptr(trans, txq); - trans_pcie->txq[i].need_update = false; + txq->need_update = false; } spin_unlock_bh(&txq->lock); } @@ -622,7 +622,7 @@ static void iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans) static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct iwl_txq *txq = trans_pcie->txq[txq_id]; spin_lock_bh(&txq->lock); while (txq->write_ptr != txq->read_ptr) { @@ -678,7 +678,7 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct iwl_txq *txq = trans_pcie->txq[txq_id]; struct device *dev = trans->dev; int i; @@ -780,7 +780,7 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans) for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; txq_id++) { - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct iwl_txq *txq = trans_pcie->txq[txq_id]; if (trans->cfg->use_tfh) iwl_write_direct64(trans, FH_MEM_CBBC_QUEUE(trans, txq_id), @@ -860,7 +860,7 @@ int iwl_pcie_tx_stop(struct iwl_trans *trans) memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); /* This can happen: start_hw, stop_device */ - if (!trans_pcie->txq) + if (!trans_pcie->txq_memory) return 0; /* Unmap DMA from host system and free skb's */ @@ -884,14 +884,17 @@ void iwl_pcie_tx_free(struct iwl_trans *trans) memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); /* Tx queues */ - if (trans_pcie->txq) { + if (trans_pcie->txq_memory) { for (txq_id = 0; - txq_id < trans->cfg->base_params->num_of_queues; txq_id++) + txq_id < trans->cfg->base_params->num_of_queues; + txq_id++) { iwl_pcie_txq_free(trans, txq_id); + trans_pcie->txq[txq_id] = NULL; + } } - kfree(trans_pcie->txq); - trans_pcie->txq = NULL; + kfree(trans_pcie->txq_memory); + trans_pcie->txq_memory = NULL; iwl_pcie_free_dma_ptr(trans, &trans_pcie->kw); @@ -913,7 +916,7 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans) /*It is not allowed to alloc twice, so warn when this happens. * We cannot rely on the previous allocation, so free and fail */ - if (WARN_ON(trans_pcie->txq)) { + if (WARN_ON(trans_pcie->txq_memory)) { ret = -EINVAL; goto error; } @@ -932,9 +935,9 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans) goto error; } - trans_pcie->txq = kcalloc(trans->cfg->base_params->num_of_queues, - sizeof(struct iwl_txq), GFP_KERNEL); - if (!trans_pcie->txq) { + trans_pcie->txq_memory = kcalloc(trans->cfg->base_params->num_of_queues, + sizeof(struct iwl_txq), GFP_KERNEL); + if (!trans_pcie->txq_memory) { IWL_ERR(trans, "Not enough memory for txq\n"); ret = -ENOMEM; goto error; @@ -945,8 +948,9 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans) txq_id++) { slots_num = (txq_id == trans_pcie->cmd_queue) ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; - ret = iwl_pcie_txq_alloc(trans, &trans_pcie->txq[txq_id], - slots_num, txq_id); + trans_pcie->txq[txq_id] = &trans_pcie->txq_memory[txq_id]; + ret = iwl_pcie_txq_alloc(trans, trans_pcie->txq[txq_id], + slots_num, txq_id); if (ret) { IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); goto error; @@ -968,7 +972,7 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) int txq_id, slots_num; bool alloc = false; - if (!trans_pcie->txq) { + if (!trans_pcie->txq_memory) { ret = iwl_pcie_tx_alloc(trans); if (ret) goto error; @@ -991,8 +995,8 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) txq_id++) { slots_num = (txq_id == trans_pcie->cmd_queue) ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; - ret = iwl_pcie_txq_init(trans, &trans_pcie->txq[txq_id], - slots_num, txq_id); + ret = iwl_pcie_txq_init(trans, trans_pcie->txq[txq_id], + slots_num, txq_id); if (ret) { IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); goto error; @@ -1005,7 +1009,7 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) * Circular buffer (TFD queue in DRAM) physical base address */ iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(trans, txq_id), - trans_pcie->txq[txq_id].dma_addr >> 8); + trans_pcie->txq[txq_id]->dma_addr >> 8); } iwl_set_bits_prph(trans, SCD_GP_CTRL, SCD_GP_CTRL_AUTO_ACTIVE_MODE); @@ -1028,7 +1032,7 @@ int iwl_pcie_gen2_tx_init(struct iwl_trans *trans) int txq_id, slots_num; bool alloc = false; - if (!trans_pcie->txq) { + if (!trans_pcie->txq_memory) { /* TODO: change this when moving to new TX alloc model */ ret = iwl_pcie_tx_alloc(trans); if (ret) @@ -1049,7 +1053,7 @@ int iwl_pcie_gen2_tx_init(struct iwl_trans *trans) txq_id++) { slots_num = (txq_id == trans_pcie->cmd_queue) ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; - ret = iwl_pcie_txq_init(trans, &trans_pcie->txq[txq_id], + ret = iwl_pcie_txq_init(trans, trans_pcie->txq[txq_id], slots_num, txq_id); if (ret) { IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); @@ -1095,7 +1099,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, struct sk_buff_head *skbs) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct iwl_txq *txq = trans_pcie->txq[txq_id]; int tfd_num = ssn & (TFD_QUEUE_SIZE_MAX - 1); int last_to_free; @@ -1252,7 +1256,7 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans, static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct iwl_txq *txq = trans_pcie->txq[txq_id]; unsigned long flags; int nfreed = 0; @@ -1319,7 +1323,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, unsigned int wdg_timeout) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct iwl_txq *txq = trans_pcie->txq[txq_id]; int fifo = -1; if (test_and_set_bit(txq_id, trans_pcie->queue_used)) @@ -1412,7 +1416,7 @@ void iwl_trans_pcie_txq_set_shared_mode(struct iwl_trans *trans, u32 txq_id, bool shared_mode) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct iwl_txq *txq = trans_pcie->txq[txq_id]; txq->ampdu = !shared_mode; } @@ -1425,8 +1429,8 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id, SCD_TX_STTS_QUEUE_OFFSET(txq_id); static const u32 zero_val[4] = {}; - trans_pcie->txq[txq_id].frozen_expiry_remainder = 0; - trans_pcie->txq[txq_id].frozen = false; + trans_pcie->txq[txq_id]->frozen_expiry_remainder = 0; + trans_pcie->txq[txq_id]->frozen = false; /* * Upon HW Rfkill - we stop the device, and then stop the queues @@ -1448,7 +1452,7 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id, } iwl_pcie_txq_unmap(trans, txq_id); - trans_pcie->txq[txq_id].ampdu = false; + trans_pcie->txq[txq_id]->ampdu = false; IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id); } @@ -1468,7 +1472,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_txq *txq = trans_pcie->txq[trans_pcie->cmd_queue]; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; unsigned long flags; @@ -1753,16 +1757,15 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, struct iwl_device_cmd *cmd; struct iwl_cmd_meta *meta; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_txq *txq = trans_pcie->txq[trans_pcie->cmd_queue]; /* If a Tx command is being handled and it isn't in the actual * command queue then there a command routing bug has been introduced * in the queue management code. */ if (WARN(txq_id != trans_pcie->cmd_queue, "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", - txq_id, trans_pcie->cmd_queue, sequence, - trans_pcie->txq[trans_pcie->cmd_queue].read_ptr, - trans_pcie->txq[trans_pcie->cmd_queue].write_ptr)) { + txq_id, trans_pcie->cmd_queue, sequence, txq->read_ptr, + txq->write_ptr)) { iwl_print_hex_error(trans, pkt, 32); return; } @@ -1846,6 +1849,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = trans_pcie->txq[trans_pcie->cmd_queue]; int cmd_idx; int ret; @@ -1886,8 +1890,6 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, &trans->status), HOST_COMPLETE_TIMEOUT); if (!ret) { - struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; - IWL_ERR(trans, "Error sending %s: time out after %dms.\n", iwl_get_cmd_string(trans, cmd->id), jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); @@ -1938,8 +1940,7 @@ cancel: * in later, it will possibly set an invalid * address (cmd->meta.source). */ - trans_pcie->txq[trans_pcie->cmd_queue]. - entries[cmd_idx].meta.flags &= ~CMD_WANT_SKB; + txq->entries[cmd_idx].meta.flags &= ~CMD_WANT_SKB; } if (cmd->resp_pkt) { @@ -2293,7 +2294,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, u16 wifi_seq; bool amsdu; - txq = &trans_pcie->txq[txq_id]; + txq = trans_pcie->txq[txq_id]; if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used), "TX on unused queue %d\n", txq_id)) -- cgit From 77c09bc87278d8ccf224c4177e7168022cdd92a9 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Mon, 12 Dec 2016 12:48:48 +0200 Subject: iwlwifi: pcie: introduce new stop_device This function is basically the same as gen1, except for clean ups of old devices configuration that are never used in a000 configuration. It will also help with refactoring rf_kill later on. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 5 + .../net/wireless/intel/iwlwifi/pcie/trans-gen2.c | 147 +++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 18 +-- 3 files changed, 161 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index cdc2b0a938a1..bb7b25976e90 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -767,6 +767,8 @@ void iwl_pcie_synchronize_irqs(struct iwl_trans *trans); bool iwl_trans_check_hw_rf_kill(struct iwl_trans *trans); void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq); int iwl_queue_space(const struct iwl_txq *q); +int iwl_pcie_apm_stop_master(struct iwl_trans *trans); +void iwl_pcie_conf_msix_hw(struct iwl_trans_pcie *trans_pcie); /* transport gen 2 exported functions */ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, @@ -781,5 +783,8 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int txq_id); int iwl_trans_pcie_gen2_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); +void iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, + bool low_power); +void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power); #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index 302310dfef9e..4d5d35501fdc 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -111,6 +111,153 @@ static int iwl_pcie_gen2_apm_init(struct iwl_trans *trans) return 0; } +static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave) +{ + IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); + + if (op_mode_leave) { + if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) + iwl_pcie_gen2_apm_init(trans); + + /* inform ME that we are leaving */ + iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PREPARE | + CSR_HW_IF_CONFIG_REG_ENABLE_PME); + mdelay(1); + iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); + mdelay(5); + } + + clear_bit(STATUS_DEVICE_ENABLED, &trans->status); + + /* Stop device's DMA activity */ + iwl_pcie_apm_stop_master(trans); + + /* Reset the entire device */ + iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + usleep_range(1000, 2000); + + /* + * Clear "initialization complete" bit to move adapter from + * D0A* (powered-up Active) --> D0U* (Uninitialized) state. + */ + iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); +} + +void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + bool hw_rfkill, was_hw_rfkill; + + lockdep_assert_held(&trans_pcie->mutex); + + if (trans_pcie->is_down) + return; + + trans_pcie->is_down = true; + + was_hw_rfkill = iwl_is_rfkill_set(trans); + + /* tell the device to stop sending interrupts */ + iwl_disable_interrupts(trans); + + /* device going down, Stop using ICT table */ + iwl_pcie_disable_ict(trans); + + /* + * If a HW restart happens during firmware loading, + * then the firmware loading might call this function + * and later it might be called again due to the + * restart. So don't process again if the device is + * already dead. + */ + if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) { + IWL_DEBUG_INFO(trans, + "DEVICE_ENABLED bit was set and is now cleared\n"); + iwl_pcie_tx_stop(trans); + iwl_pcie_rx_stop(trans); + } + + iwl_pcie_ctxt_info_free_paging(trans); + iwl_pcie_ctxt_info_free(trans); + + /* Make sure (redundant) we've released our request to stay awake */ + iwl_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + + /* Stop the device, and put it in low power state */ + iwl_pcie_gen2_apm_stop(trans, false); + + /* stop and reset the on-board processor */ + iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + usleep_range(1000, 2000); + + /* + * Upon stop, the IVAR table gets erased, so msi-x won't + * work. This causes a bug in RF-KILL flows, since the interrupt + * that enables radio won't fire on the correct irq, and the + * driver won't be able to handle the interrupt. + * Configure the IVAR table again after reset. + */ + iwl_pcie_conf_msix_hw(trans_pcie); + + /* + * Upon stop, the APM issues an interrupt if HW RF kill is set. + * This is a bug in certain verions of the hardware. + * Certain devices also keep sending HW RF kill interrupt all + * the time, unless the interrupt is ACKed even if the interrupt + * should be masked. Re-ACK all the interrupts here. + */ + iwl_disable_interrupts(trans); + + /* clear all status bits */ + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + clear_bit(STATUS_INT_ENABLED, &trans->status); + clear_bit(STATUS_TPOWER_PMI, &trans->status); + clear_bit(STATUS_RFKILL, &trans->status); + + /* + * Even if we stop the HW, we still want the RF kill + * interrupt + */ + iwl_enable_rfkill_int(trans); + + /* + * Check again since the RF kill state may have changed while + * all the interrupts were disabled, in this case we couldn't + * receive the RF kill interrupt and update the state in the + * op_mode. + * Don't call the op_mode if the rkfill state hasn't changed. + * This allows the op_mode to call stop_device from the rfkill + * notification without endless recursion. Under very rare + * circumstances, we might have a small recursion if the rfkill + * state changed exactly now while we were called from stop_device. + * This is very unlikely but can happen and is supported. + */ + hw_rfkill = iwl_is_rfkill_set(trans); + if (hw_rfkill) + set_bit(STATUS_RFKILL, &trans->status); + else + clear_bit(STATUS_RFKILL, &trans->status); + if (hw_rfkill != was_hw_rfkill) + iwl_trans_pcie_rf_kill(trans, hw_rfkill); + + /* re-take ownership to prevent other users from stealing the device */ + iwl_pcie_prepare_card_hw(trans); +} + +void iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + mutex_lock(&trans_pcie->mutex); + _iwl_trans_pcie_gen2_stop_device(trans, low_power); + mutex_unlock(&trans_pcie->mutex); +} + static int iwl_pcie_gen2_nic_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index ccc9280d845a..6a565d35f071 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -80,7 +80,6 @@ #include "iwl-prph.h" #include "iwl-scd.h" #include "iwl-agn-hw.h" -#include "iwl-context-info.h" #include "iwl-fw-error-dump.h" #include "internal.h" #include "iwl-fh.h" @@ -449,7 +448,7 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans) ~SHR_APMG_XTAL_CFG_XTAL_ON_REQ); } -static int iwl_pcie_apm_stop_master(struct iwl_trans *trans) +int iwl_pcie_apm_stop_master(struct iwl_trans *trans) { int ret = 0; @@ -1126,7 +1125,7 @@ static void iwl_pcie_map_rx_causes(struct iwl_trans *trans) iwl_write8(trans, CSR_MSIX_RX_IVAR(1), val); } -static void iwl_pcie_conf_msix_hw(struct iwl_trans_pcie *trans_pcie) +void iwl_pcie_conf_msix_hw(struct iwl_trans_pcie *trans_pcie) { struct iwl_trans *trans = trans_pcie->trans; @@ -1213,9 +1212,6 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool low_power) } } - iwl_pcie_ctxt_info_free_paging(trans); - iwl_pcie_ctxt_info_free(trans); - /* Make sure (redundant) we've released our request to stay awake */ iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); @@ -1405,8 +1401,12 @@ void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state) lockdep_assert_held(&trans_pcie->mutex); - if (iwl_op_mode_hw_rf_kill(trans->op_mode, state)) - _iwl_trans_pcie_stop_device(trans, true); + if (iwl_op_mode_hw_rf_kill(trans->op_mode, state)) { + if (trans->cfg->gen2) + _iwl_trans_pcie_gen2_stop_device(trans, true); + else + _iwl_trans_pcie_stop_device(trans, true); + } } static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test, @@ -2916,7 +2916,7 @@ static const struct iwl_trans_ops trans_ops_pcie_gen2 = { .start_hw = iwl_trans_pcie_start_hw, .fw_alive = iwl_trans_pcie_gen2_fw_alive, .start_fw = iwl_trans_pcie_gen2_start_fw, - .stop_device = iwl_trans_pcie_stop_device, + .stop_device = iwl_trans_pcie_gen2_stop_device, .send_cmd = iwl_trans_pcie_gen2_send_hcmd, -- cgit From 13a3a39052aa42ed7e8ca2540366169584ae321f Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 29 Nov 2016 13:49:59 +0200 Subject: iwlwifi: pcie: alloc queues dynamically Change queue allocation to be dynamic. On transport init only the command queue is being allocated. Other queues are allocated on demand. This is due to the huge amount of queues we will soon enable (512) and as a preparation for TX Virtual Queue Manager feature (TVQM), where firmware will assign the actual queue number on demand. This includes also allocation of the byte count table per queue and not as a contiguous chunk of memory. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 13 +- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 5 +- .../net/wireless/intel/iwlwifi/pcie/trans-gen2.c | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 5 +- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 179 +++++++++++++++++++-- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 68 ++------ 6 files changed, 199 insertions(+), 73 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index bb7b25976e90..405ae3799f5e 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -241,6 +241,7 @@ struct iwl_pcie_first_tb_buf { * @wd_timeout: queue watchdog timeout (jiffies) - per queue * @frozen: tx stuck queue timer is frozen * @frozen_expiry_remainder: remember how long until the timer fires + * @bc_tbl: byte count table of the queue (relevant only for gen2 transport) * @write_ptr: 1-st empty entry (index) host_w * @read_ptr: last used entry (index) host_r * @dma_addr: physical addr for BD's @@ -280,6 +281,7 @@ struct iwl_txq { int block; unsigned long wd_timeout; struct sk_buff_head overflow_q; + struct iwl_dma_ptr bc_tbl; int write_ptr; int read_ptr; @@ -769,6 +771,13 @@ void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq); int iwl_queue_space(const struct iwl_txq *q); int iwl_pcie_apm_stop_master(struct iwl_trans *trans); void iwl_pcie_conf_msix_hw(struct iwl_trans_pcie *trans_pcie); +int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, + int slots_num, u32 txq_id); +int iwl_pcie_txq_alloc(struct iwl_trans *trans, + struct iwl_txq *txq, int slots_num, u32 txq_id); +int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, + struct iwl_dma_ptr *ptr, size_t size); +void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr); /* transport gen 2 exported functions */ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, @@ -786,5 +795,7 @@ int iwl_trans_pcie_gen2_send_hcmd(struct iwl_trans *trans, void iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power); void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power); - +void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id); +void iwl_pcie_gen2_tx_free(struct iwl_trans *trans); +void iwl_pcie_gen2_tx_stop(struct iwl_trans *trans); #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 0338c5f41ce6..f98f2d2b8a1b 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1419,8 +1419,11 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) iwl_trans_fw_error(trans); local_bh_enable(); - for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) + for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { + if (!trans_pcie->txq[i]) + continue; del_timer(&trans_pcie->txq[i]->stuck_timer); + } clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); wake_up(&trans_pcie->wait_command_queue); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index 4d5d35501fdc..ef8f563a48d9 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -177,7 +177,7 @@ void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans, bool low_power) if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) { IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n"); - iwl_pcie_tx_stop(trans); + iwl_pcie_gen2_tx_stop(trans); iwl_pcie_rx_stop(trans); } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 6a565d35f071..ed1034e58cb4 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1813,7 +1813,10 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) iwl_pcie_synchronize_irqs(trans); - iwl_pcie_tx_free(trans); + if (trans->cfg->gen2) + iwl_pcie_gen2_tx_free(trans); + else + iwl_pcie_tx_free(trans); iwl_pcie_rx_free(trans); if (trans_pcie->msix_enabled) { diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index b938a6a3b169..446e837613a5 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -56,22 +56,42 @@ #include "internal.h" #include "mvm/fw-api.h" + /* + * iwl_pcie_gen2_tx_stop - Stop all Tx DMA channels + */ +void iwl_pcie_gen2_tx_stop(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int txq_id; + + /* + * This function can be called before the op_mode disabled the + * queues. This happens when we have an rfkill interrupt. + * Since we stop Tx altogether - mark the queues as stopped. + */ + memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); + memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); + + /* Unmap DMA from host system and free skb's */ + for (txq_id = 0; txq_id < ARRAY_SIZE(trans_pcie->txq); txq_id++) { + if (!trans_pcie->txq[txq_id]) + continue; + iwl_pcie_gen2_txq_unmap(trans, txq_id); + } +} + /* * iwl_pcie_txq_update_byte_tbl - Set up entry in Tx byte-count array */ -static void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans *trans, - struct iwl_txq *txq, u16 byte_cnt, - int num_tbs) +static void iwl_pcie_gen2_update_byte_tbl(struct iwl_txq *txq, u16 byte_cnt, + int num_tbs) { - struct iwlagn_scd_bc_tbl *scd_bc_tbl; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwlagn_scd_bc_tbl *scd_bc_tbl = txq->bc_tbl.addr; int write_ptr = txq->write_ptr; u8 filled_tfd_size, num_fetch_chunks; u16 len = byte_cnt; __le16 bc_ent; - scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; - len = DIV_ROUND_UP(len, 4); if (WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX)) @@ -90,7 +110,7 @@ static void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans *trans, num_fetch_chunks = DIV_ROUND_UP(filled_tfd_size, 64) - 1; bc_ent = cpu_to_le16(len | (num_fetch_chunks << 12)); - scd_bc_tbl[txq->id].tfd_offset[write_ptr] = bc_ent; + scd_bc_tbl->tfd_offset[write_ptr] = bc_ent; } /* @@ -192,7 +212,7 @@ static int iwl_pcie_gen2_set_tb(struct iwl_trans *trans, struct iwl_tfh_tb *tb = &tfd->tbs[idx]; /* Each TFD can point to a maximum max_tbs Tx buffers */ - if (tfd->num_tbs >= trans_pcie->max_tbs) { + if (le16_to_cpu(tfd->num_tbs) >= trans_pcie->max_tbs) { IWL_ERR(trans, "Error can not send more than %d chunks\n", trans_pcie->max_tbs); return -EINVAL; @@ -334,7 +354,7 @@ int iwl_trans_pcie_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, } /* Set up entry for this TFD in Tx byte-count array */ - iwl_pcie_gen2_update_byte_tbl(trans, txq, le16_to_cpu(tx_cmd->len), + iwl_pcie_gen2_update_byte_tbl(txq, le16_to_cpu(tx_cmd->len), iwl_pcie_gen2_get_num_tbs(trans, tfd)); /* start timer if queue currently empty */ @@ -781,26 +801,99 @@ void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id) iwl_wake_queue(trans, txq); } +/* + * iwl_pcie_txq_free - Deallocate DMA queue. + * @txq: Transmit queue to deallocate. + * + * Empty queue by removing and destroying all BD's. + * Free all buffers. + * 0-fill, but do not free "txq" descriptor structure. + */ +static void iwl_pcie_gen2_txq_free(struct iwl_trans *trans, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = trans_pcie->txq[txq_id]; + struct device *dev = trans->dev; + int i; + + if (WARN_ON(!txq)) + return; + + iwl_pcie_gen2_txq_unmap(trans, txq_id); + + /* De-alloc array of command/tx buffers */ + if (txq_id == trans_pcie->cmd_queue) + for (i = 0; i < txq->n_window; i++) { + kzfree(txq->entries[i].cmd); + kzfree(txq->entries[i].free_buf); + } + + /* De-alloc circular buffer of TFDs */ + if (txq->tfds) { + dma_free_coherent(dev, + trans_pcie->tfd_size * TFD_QUEUE_SIZE_MAX, + txq->tfds, txq->dma_addr); + dma_free_coherent(dev, + sizeof(*txq->first_tb_bufs) * txq->n_window, + txq->first_tb_bufs, txq->first_tb_dma); + } + + kfree(txq->entries); + + del_timer_sync(&txq->stuck_timer); + + iwl_pcie_free_dma_ptr(trans, &txq->bc_tbl); + kfree(txq); + trans_pcie->txq[txq_id] = NULL; + + clear_bit(txq_id, trans_pcie->queue_used); +} + int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, struct iwl_tx_queue_cfg_cmd *cmd, int cmd_id, unsigned int timeout) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = trans_pcie->txq[cmd->scd_queue]; + struct iwl_txq *txq; struct iwl_host_cmd hcmd = { .id = cmd_id, .len = { sizeof(*cmd) }, .data = { cmd, }, .flags = 0, }; + int ret, qid = cmd->scd_queue; u16 ssn = le16_to_cpu(cmd->ssn); + txq = kzalloc(sizeof(*txq), GFP_KERNEL); + if (!txq) + return -ENOMEM; + ret = iwl_pcie_alloc_dma_ptr(trans, &txq->bc_tbl, + sizeof(struct iwlagn_scd_bc_tbl)); + if (ret) { + IWL_ERR(trans, "Scheduler BC Table allocation failed\n"); + kfree(txq); + return -ENOMEM; + } + if (test_and_set_bit(cmd->scd_queue, trans_pcie->queue_used)) { WARN_ONCE(1, "queue %d already used", cmd->scd_queue); return -EINVAL; } + trans_pcie->txq[qid] = txq; + + ret = iwl_pcie_txq_alloc(trans, txq, TFD_TX_CMD_SLOTS, qid); + if (ret) { + IWL_ERR(trans, "Tx %d queue init failed\n", qid); + goto error; + } + ret = iwl_pcie_txq_init(trans, txq, TFD_TX_CMD_SLOTS, qid); + if (ret) { + IWL_ERR(trans, "Tx %d queue alloc failed\n", qid); + goto error; + } + txq->wd_timeout = msecs_to_jiffies(timeout); /* @@ -816,12 +909,14 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, cmd->scd_queue, ssn & 0xff); cmd->tfdq_addr = cpu_to_le64(txq->dma_addr); - cmd->byte_cnt_addr = cpu_to_le64(trans_pcie->scd_bc_tbls.dma + - cmd->scd_queue * - sizeof(struct iwlagn_scd_bc_tbl)); - cmd->cb_size = cpu_to_le64(TFD_QUEUE_CB_SIZE(TFD_QUEUE_SIZE_MAX)); + cmd->byte_cnt_addr = cpu_to_le64(txq->bc_tbl.dma); + cmd->cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(TFD_QUEUE_SIZE_MAX)); return iwl_trans_send_cmd(trans, &hcmd); + +error: + iwl_pcie_gen2_txq_free(trans, cmd->scd_queue); + return -ENOMEM; } void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue) @@ -845,3 +940,57 @@ void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue) IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", queue); } +void iwl_pcie_gen2_tx_free(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int i; + + memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); + + /* Free all TX queues */ + for (i = 0; i < ARRAY_SIZE(trans_pcie->txq); i++) { + if (!trans_pcie->txq[i]) + continue; + + iwl_pcie_gen2_txq_free(trans, i); + } +} + +int iwl_pcie_gen2_tx_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *cmd_queue; + int txq_id = trans_pcie->cmd_queue, ret; + + /* alloc and init the command queue */ + if (!trans_pcie->txq[txq_id]) { + cmd_queue = kzalloc(sizeof(*cmd_queue), GFP_KERNEL); + if (!cmd_queue) { + IWL_ERR(trans, "Not enough memory for command queue\n"); + return -ENOMEM; + } + trans_pcie->txq[txq_id] = cmd_queue; + ret = iwl_pcie_txq_alloc(trans, cmd_queue, TFD_CMD_SLOTS, + txq_id); + if (ret) { + IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); + goto error; + } + } else { + cmd_queue = trans_pcie->txq[txq_id]; + } + + ret = iwl_pcie_txq_init(trans, cmd_queue, TFD_CMD_SLOTS, txq_id); + if (ret) { + IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); + goto error; + } + set_bit(txq_id, trans_pcie->queue_used); + + return 0; + +error: + iwl_pcie_gen2_tx_free(trans); + return ret; +} + diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 9ea92ac3920e..df31e14bc33d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -126,8 +126,8 @@ static int iwl_queue_init(struct iwl_txq *q, int slots_num, u32 id) return 0; } -static int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, - struct iwl_dma_ptr *ptr, size_t size) +int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, + struct iwl_dma_ptr *ptr, size_t size) { if (WARN_ON(ptr->addr)) return -EINVAL; @@ -140,8 +140,7 @@ static int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, return 0; } -static void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, - struct iwl_dma_ptr *ptr) +void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr) { if (unlikely(!ptr->addr)) return; @@ -484,9 +483,8 @@ static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, return num_tbs; } -static int iwl_pcie_txq_alloc(struct iwl_trans *trans, - struct iwl_txq *txq, int slots_num, - u32 txq_id) +int iwl_pcie_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, + int slots_num, u32 txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); size_t tfd_sz = trans_pcie->tfd_size * TFD_QUEUE_SIZE_MAX; @@ -551,8 +549,8 @@ error: } -static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, - int slots_num, u32 txq_id) +int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, + int slots_num, u32 txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; @@ -778,6 +776,13 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int txq_id; + /* + * we should never get here in gen2 trans mode return early to avoid + * having invalid accesses + */ + if (WARN_ON_ONCE(trans->cfg->gen2)) + return; + for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; txq_id++) { struct iwl_txq *txq = trans_pcie->txq[txq_id]; @@ -1025,51 +1030,6 @@ error: return ret; } -int iwl_pcie_gen2_tx_init(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ret; - int txq_id, slots_num; - bool alloc = false; - - if (!trans_pcie->txq_memory) { - /* TODO: change this when moving to new TX alloc model */ - ret = iwl_pcie_tx_alloc(trans); - if (ret) - goto error; - alloc = true; - } - - spin_lock(&trans_pcie->irq_lock); - - /* Tell NIC where to find the "keep warm" buffer */ - iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, - trans_pcie->kw.dma >> 4); - - spin_unlock(&trans_pcie->irq_lock); - - /* TODO: remove this when moving to new TX alloc model */ - for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; - txq_id++) { - slots_num = (txq_id == trans_pcie->cmd_queue) ? - TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; - ret = iwl_pcie_txq_init(trans, trans_pcie->txq[txq_id], - slots_num, txq_id); - if (ret) { - IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); - goto error; - } - } - - return 0; - -error: - /* Upon error, free only if we allocated something */ - if (alloc) - iwl_pcie_tx_free(trans); - return ret; -} - static inline void iwl_pcie_txq_progress(struct iwl_txq *txq) { lockdep_assert_held(&txq->lock); -- cgit From 6a90f85a696429ebe89777c6c895fd98fb36dc9b Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 25 Jan 2017 18:57:26 +0200 Subject: iwlwifi: mvm: remove unnecessary label in iwl_mvm_handle_rx_statistics() The "invalid" label was a bit ugly and unnecessary. Remove it. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 655bd1384158..eab6e2ad62e1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -660,8 +660,11 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, else expected_size = sizeof(struct iwl_notif_statistics_v10); - if (iwl_rx_packet_payload_len(pkt) != expected_size) - goto invalid; + if (iwl_rx_packet_payload_len(pkt) != expected_size) { + IWL_ERR(mvm, "received invalid statistics size (%d)!\n", + iwl_rx_packet_payload_len(pkt)); + return; + } data.mac_id = stats->rx.general.mac_id; data.beacon_filter_average_energy = @@ -714,12 +717,6 @@ void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm, sta->avg_energy = energy[i]; } rcu_read_unlock(); - - return; - - invalid: - IWL_ERR(mvm, "received invalid statistics size (%d)!\n", - iwl_rx_packet_payload_len(pkt)); } void iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -- cgit From b8e8d7cee32291a0cfdf813e45317b9d4d1b9574 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 17 Jan 2017 14:14:29 +0200 Subject: iwlwifi: pcie: get rid of txq id assignment In TVQM mode the queue ID is assigned after enablement. Get rid of assuming pre-defined TX queue ID in functions that will be used by TVQM allocation path. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 4 +- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 50 +++++++++++++--------- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 33 +++++++------- 3 files changed, 47 insertions(+), 40 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 405ae3799f5e..b9e9e10c32fa 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -772,9 +772,9 @@ int iwl_queue_space(const struct iwl_txq *q); int iwl_pcie_apm_stop_master(struct iwl_trans *trans); void iwl_pcie_conf_msix_hw(struct iwl_trans_pcie *trans_pcie); int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, - int slots_num, u32 txq_id); + int slots_num, bool cmd_queue); int iwl_pcie_txq_alloc(struct iwl_trans *trans, - struct iwl_txq *txq, int slots_num, u32 txq_id); + struct iwl_txq *txq, int slots_num, bool cmd_queue); int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr, size_t size); void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 446e837613a5..0bc9522cf364 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -801,6 +801,27 @@ void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id) iwl_wake_queue(trans, txq); } +static void iwl_pcie_gen2_txq_free_memory(struct iwl_trans *trans, + struct iwl_txq *txq) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct device *dev = trans->dev; + + /* De-alloc circular buffer of TFDs */ + if (txq->tfds) { + dma_free_coherent(dev, + trans_pcie->tfd_size * TFD_QUEUE_SIZE_MAX, + txq->tfds, txq->dma_addr); + dma_free_coherent(dev, + sizeof(*txq->first_tb_bufs) * txq->n_window, + txq->first_tb_bufs, txq->first_tb_dma); + } + + kfree(txq->entries); + iwl_pcie_free_dma_ptr(trans, &txq->bc_tbl); + kfree(txq); +} + /* * iwl_pcie_txq_free - Deallocate DMA queue. * @txq: Transmit queue to deallocate. @@ -813,7 +834,6 @@ static void iwl_pcie_gen2_txq_free(struct iwl_trans *trans, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_txq *txq = trans_pcie->txq[txq_id]; - struct device *dev = trans->dev; int i; if (WARN_ON(!txq)) @@ -827,23 +847,10 @@ static void iwl_pcie_gen2_txq_free(struct iwl_trans *trans, int txq_id) kzfree(txq->entries[i].cmd); kzfree(txq->entries[i].free_buf); } - - /* De-alloc circular buffer of TFDs */ - if (txq->tfds) { - dma_free_coherent(dev, - trans_pcie->tfd_size * TFD_QUEUE_SIZE_MAX, - txq->tfds, txq->dma_addr); - dma_free_coherent(dev, - sizeof(*txq->first_tb_bufs) * txq->n_window, - txq->first_tb_bufs, txq->first_tb_dma); - } - - kfree(txq->entries); - del_timer_sync(&txq->stuck_timer); - iwl_pcie_free_dma_ptr(trans, &txq->bc_tbl); - kfree(txq); + iwl_pcie_gen2_txq_free_memory(trans, txq); + trans_pcie->txq[txq_id] = NULL; clear_bit(txq_id, trans_pcie->queue_used); @@ -882,13 +889,14 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, } trans_pcie->txq[qid] = txq; + trans_pcie->txq[qid]->id = qid; - ret = iwl_pcie_txq_alloc(trans, txq, TFD_TX_CMD_SLOTS, qid); + ret = iwl_pcie_txq_alloc(trans, txq, TFD_TX_CMD_SLOTS, false); if (ret) { IWL_ERR(trans, "Tx %d queue init failed\n", qid); goto error; } - ret = iwl_pcie_txq_init(trans, txq, TFD_TX_CMD_SLOTS, qid); + ret = iwl_pcie_txq_init(trans, txq, TFD_TX_CMD_SLOTS, false); if (ret) { IWL_ERR(trans, "Tx %d queue alloc failed\n", qid); goto error; @@ -970,8 +978,7 @@ int iwl_pcie_gen2_tx_init(struct iwl_trans *trans) return -ENOMEM; } trans_pcie->txq[txq_id] = cmd_queue; - ret = iwl_pcie_txq_alloc(trans, cmd_queue, TFD_CMD_SLOTS, - txq_id); + ret = iwl_pcie_txq_alloc(trans, cmd_queue, TFD_CMD_SLOTS, true); if (ret) { IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); goto error; @@ -980,11 +987,12 @@ int iwl_pcie_gen2_tx_init(struct iwl_trans *trans) cmd_queue = trans_pcie->txq[txq_id]; } - ret = iwl_pcie_txq_init(trans, cmd_queue, TFD_CMD_SLOTS, txq_id); + ret = iwl_pcie_txq_init(trans, cmd_queue, TFD_CMD_SLOTS, true); if (ret) { IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); goto error; } + trans_pcie->txq[txq_id]->id = txq_id; set_bit(txq_id, trans_pcie->queue_used); return 0; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index df31e14bc33d..386950a2d616 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -102,10 +102,9 @@ int iwl_queue_space(const struct iwl_txq *q) /* * iwl_queue_init - Initialize queue's high/low-water and read/write indexes */ -static int iwl_queue_init(struct iwl_txq *q, int slots_num, u32 id) +static int iwl_queue_init(struct iwl_txq *q, int slots_num) { q->n_window = slots_num; - q->id = id; /* slots_num must be power-of-two size, otherwise * get_cmd_index is broken. */ @@ -484,7 +483,7 @@ static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, } int iwl_pcie_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, - int slots_num, u32 txq_id) + int slots_num, bool cmd_queue) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); size_t tfd_sz = trans_pcie->tfd_size * TFD_QUEUE_SIZE_MAX; @@ -507,7 +506,7 @@ int iwl_pcie_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, if (!txq->entries) goto error; - if (txq_id == trans_pcie->cmd_queue) + if (cmd_queue) for (i = 0; i < slots_num; i++) { txq->entries[i].cmd = kmalloc(sizeof(struct iwl_device_cmd), @@ -533,13 +532,11 @@ int iwl_pcie_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, if (!txq->first_tb_bufs) goto err_free_tfds; - txq->id = txq_id; - return 0; err_free_tfds: dma_free_coherent(trans->dev, tfd_sz, txq->tfds, txq->dma_addr); error: - if (txq->entries && txq_id == trans_pcie->cmd_queue) + if (txq->entries && cmd_queue) for (i = 0; i < slots_num; i++) kfree(txq->entries[i].cmd); kfree(txq->entries); @@ -550,9 +547,8 @@ error: } int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, - int slots_num, u32 txq_id) + int slots_num, bool cmd_queue) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; txq->need_update = false; @@ -562,13 +558,13 @@ int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); /* Initialize queue's high/low-water marks, and head/tail indexes */ - ret = iwl_queue_init(txq, slots_num, txq_id); + ret = iwl_queue_init(txq, slots_num); if (ret) return ret; spin_lock_init(&txq->lock); - if (txq_id == trans_pcie->cmd_queue) { + if (cmd_queue) { static struct lock_class_key iwl_pcie_cmd_queue_lock_class; lockdep_set_class(&txq->lock, &iwl_pcie_cmd_queue_lock_class); @@ -951,15 +947,17 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans) /* Alloc and init all Tx queues, including the command queue (#4/#9) */ for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; txq_id++) { - slots_num = (txq_id == trans_pcie->cmd_queue) ? - TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + bool cmd_queue = (txq_id == trans_pcie->cmd_queue); + + slots_num = cmd_queue ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; trans_pcie->txq[txq_id] = &trans_pcie->txq_memory[txq_id]; ret = iwl_pcie_txq_alloc(trans, trans_pcie->txq[txq_id], - slots_num, txq_id); + slots_num, cmd_queue); if (ret) { IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); goto error; } + trans_pcie->txq[txq_id]->id = txq_id; } return 0; @@ -998,10 +996,11 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) /* Alloc and init all Tx queues, including the command queue (#4/#9) */ for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; txq_id++) { - slots_num = (txq_id == trans_pcie->cmd_queue) ? - TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + bool cmd_queue = (txq_id == trans_pcie->cmd_queue); + + slots_num = cmd_queue ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; ret = iwl_pcie_txq_init(trans, trans_pcie->txq[txq_id], - slots_num, txq_id); + slots_num, cmd_queue); if (ret) { IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); goto error; -- cgit From 12db294c7872eeb0e3ff595cd985e369325dde0e Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 17 Jan 2017 14:28:21 +0200 Subject: iwlwifi: mvm: support new TX response for TVQM In TVQM mode the TX responses were changed to include queue number since legacy TX queue number retrieval cannot be scaled up to 512 queues. Support this change. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h | 42 ++++++++++------------ drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 4 ++- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 10 ++++++ drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 34 +++++++++++++++--- 4 files changed, 61 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h index f83ee6e760d0..e9f055706a6f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h @@ -548,9 +548,11 @@ struct agg_tx_status { * @tlc_info: TLC rate info * @ra_tid: bits [3:0] = ra, bits [7:4] = tid * @frame_ctrl: frame control + * @tx_queue: TX queue for this response * @status: for non-agg: frame status TX_STATUS_* * for agg: status of 1st frame, AGG_TX_STATE_*; other frame status fields * follow this one, up to frame_count. + * For version 6 TX response isn't received for aggregation at all. * * After the array of statuses comes the SSN of the SCD. Look at * %iwl_mvm_get_scd_ssn for more details. @@ -577,9 +579,17 @@ struct iwl_mvm_tx_resp { u8 tlc_info; u8 ra_tid; __le16 frame_ctrl; - - struct agg_tx_status status; -} __packed; /* TX_RSP_API_S_VER_3 */ + union { + struct { + struct agg_tx_status status; + } v3;/* TX_RSP_API_S_VER_3 */ + struct { + __le16 tx_queue; + __le16 reserved2; + struct agg_tx_status status; + } v6; + }; +} __packed; /* TX_RSP_API_S_VER_6 */ /** * struct iwl_mvm_ba_notif - notifies about reception of BA @@ -619,11 +629,14 @@ struct iwl_mvm_ba_notif { * struct iwl_mvm_compressed_ba_tfd - progress of a TFD queue * @q_num: TFD queue number * @tfd_index: Index of first un-acked frame in the TFD queue + * @scd_queue: For debug only - the physical queue the TFD queue is bound to */ struct iwl_mvm_compressed_ba_tfd { - u8 q_num; - u8 reserved; + __le16 q_num; __le16 tfd_index; + u8 scd_queue; + u8 reserved; + __le16 reserved2; } __packed; /* COMPRESSED_BA_TFD_API_S_VER_1 */ /** @@ -799,25 +812,6 @@ struct iwl_tx_path_flush_cmd { __le16 reserved; } __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_1 */ -/** - * iwl_mvm_get_scd_ssn - returns the SSN of the SCD - * @tx_resp: the Tx response from the fw (agg or non-agg) - * - * When the fw sends an AMPDU, it fetches the MPDUs one after the other. Since - * it can't know that everything will go well until the end of the AMPDU, it - * can't know in advance the number of MPDUs that will be sent in the current - * batch. This is why it writes the agg Tx response while it fetches the MPDUs. - * Hence, it can't know in advance what the SSN of the SCD will be at the end - * of the batch. This is why the SSN of the SCD is written at the end of the - * whole struct at a variable offset. This function knows how to cope with the - * variable offset and returns the SSN of the SCD. - */ -static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm_tx_resp *tx_resp) -{ - return le32_to_cpup((__le32 *)&tx_resp->status + - tx_resp->frame_count) & 0xfff; -} - /* Available options for the SCD_QUEUE_CFG HCMD */ enum iwl_scd_cfg_actions { SCD_CFG_DISABLE_QUEUE = 0x0, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index ae1406fe9988..9e69b9d2012c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1443,6 +1443,7 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, struct iwl_mvm_tx_resp *beacon_notify_hdr; struct ieee80211_vif *csa_vif; struct ieee80211_vif *tx_blocked_vif; + struct agg_tx_status *agg_status; u16 status; lockdep_assert_held(&mvm->mutex); @@ -1450,7 +1451,8 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, beacon_notify_hdr = &beacon->beacon_notify_hdr; mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2); - status = le16_to_cpu(beacon_notify_hdr->status.status) & TX_STATUS_MSK; + agg_status = iwl_mvm_get_agg_status(mvm, beacon_notify_hdr); + status = le16_to_cpu(agg_status->status) & TX_STATUS_MSK; IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n", status, beacon_notify_hdr->failure_frame, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 2205c9f8bb58..70abc6cd3b47 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1250,6 +1250,16 @@ static inline bool iwl_mvm_is_cdb_supported(struct iwl_mvm *mvm) IWL_UCODE_TLV_CAPA_CDB_SUPPORT); } +static inline struct agg_tx_status* +iwl_mvm_get_agg_status(struct iwl_mvm *mvm, + struct iwl_mvm_tx_resp *tx_resp) +{ + if (iwl_mvm_has_new_tx_api(mvm)) + return &tx_resp->v6.status; + else + return &tx_resp->v3.status; +} + static inline bool iwl_mvm_is_tt_in_fw(struct iwl_mvm *mvm) { #ifdef CONFIG_THERMAL diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 4ba7ff44420d..90c33ee69f21 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1282,6 +1282,26 @@ static void iwl_mvm_tx_status_check_trigger(struct iwl_mvm *mvm, } } +/** + * iwl_mvm_get_scd_ssn - returns the SSN of the SCD + * @tx_resp: the Tx response from the fw (agg or non-agg) + * + * When the fw sends an AMPDU, it fetches the MPDUs one after the other. Since + * it can't know that everything will go well until the end of the AMPDU, it + * can't know in advance the number of MPDUs that will be sent in the current + * batch. This is why it writes the agg Tx response while it fetches the MPDUs. + * Hence, it can't know in advance what the SSN of the SCD will be at the end + * of the batch. This is why the SSN of the SCD is written at the end of the + * whole struct at a variable offset. This function knows how to cope with the + * variable offset and returns the SSN of the SCD. + */ +static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm *mvm, + struct iwl_mvm_tx_resp *tx_resp) +{ + return le32_to_cpup((__le32 *)iwl_mvm_get_agg_status(mvm, tx_resp) + + tx_resp->frame_count) & 0xfff; +} + static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { @@ -1291,8 +1311,10 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid); int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid); - u32 status = le16_to_cpu(tx_resp->status.status); - u16 ssn = iwl_mvm_get_scd_ssn(tx_resp); + struct agg_tx_status *agg_status = + iwl_mvm_get_agg_status(mvm, tx_resp); + u32 status = le16_to_cpu(agg_status->status); + u16 ssn = iwl_mvm_get_scd_ssn(mvm, tx_resp); struct iwl_mvm_sta *mvmsta; struct sk_buff_head skbs; u8 skb_freed = 0; @@ -1301,6 +1323,9 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, __skb_queue_head_init(&skbs); + if (iwl_mvm_has_new_tx_api(mvm)) + txq_id = le16_to_cpu(tx_resp->v6.tx_queue); + seq_ctl = le16_to_cpu(tx_resp->seq_ctl); /* we can free until ssn % q.n_bd not inclusive */ @@ -1557,7 +1582,8 @@ static void iwl_mvm_rx_tx_cmd_agg_dbg(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; - struct agg_tx_status *frame_status = &tx_resp->status; + struct agg_tx_status *frame_status = + iwl_mvm_get_agg_status(mvm, tx_resp); int i; for (i = 0; i < tx_resp->frame_count; i++) { @@ -1772,7 +1798,7 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) if (tid == IWL_MGMT_TID) tid = IWL_MAX_TID_COUNT; iwl_mvm_tx_reclaim(mvm, sta_id, tid, - (int)ba_res->tfd[0].q_num, + (int)(le16_to_cpu(ba_res->tfd[0].q_num)), le16_to_cpu(ba_res->tfd[0].tfd_index), &ba_info, le32_to_cpu(ba_res->tx_rate)); -- cgit From 310181ec34e20134dc1b1c8b5e62605af83b1777 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 17 Jan 2017 14:27:48 +0200 Subject: iwlwifi: move to TVQM mode In TVQM firmware returns the value of the queue ID and code should accept it. The TX queue config API was changed. Move to new API. This has to be done in parallel in mvm and pcie. Do not move yet to 512 queues since there are some opens with enabling it. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 38 ++++--- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 + drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 124 ++++++++++++++++------ drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 63 +++++++---- drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 70 +++++++----- 5 files changed, 202 insertions(+), 96 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index c87a58ee012a..626e2703a57f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -531,37 +531,43 @@ struct iwl_trans_txq_scd_cfg { int frame_limit; }; +/* Available options for &struct iwl_tx_queue_cfg_cmd */ +enum iwl_tx_queue_cfg_actions { + TX_QUEUE_CFG_ENABLE_QUEUE = BIT(0), + TX_QUEUE_CFG_TFD_SHORT_FORMAT = BIT(1), +}; + /** * struct iwl_tx_queue_cfg_cmd - txq hw scheduler config command - * @token: token of the command * @sta_id: station id * @tid: tid of the queue - * @scd_queue: scheduler queue to config - * @action: 1 queue enable, 0 queue disable - * @aggregate: 1 aggregated queue, 0 otherwise - * @tx_fifo: TX fifo - * @window: BA window size - * @ssn: SSN for the BA agreement + * @flags: Bit 0 - on enable, off - disable, Bit 1 - short TFD format * @cb_size: size of TFD cyclic buffer. Value is exponent - 3. * Minimum value 0 (8 TFDs), maximum value 5 (256 TFDs) * @byte_cnt_addr: address of byte count table * @tfdq_addr: address of TFD circular buffer */ struct iwl_tx_queue_cfg_cmd { - u8 token; u8 sta_id; u8 tid; - u8 scd_queue; - u8 action; - u8 aggregate; - u8 tx_fifo; - u8 window; - __le16 ssn; - __le16 reserved; + __le16 flags; __le32 cb_size; __le64 byte_cnt_addr; __le64 tfdq_addr; -} __packed; /* TX_QUEUE_CFG_CMD_API_S_VER_1 */ +} __packed; /* TX_QUEUE_CFG_CMD_API_S_VER_2 */ + +/** + * struct iwl_tx_queue_cfg_rsp - response to txq hw scheduler config + * @queue_number: queue number assigned to this RA -TID + * @flags: set on failure + * @write_pointer: initial value for write pointer + */ +struct iwl_tx_queue_cfg_rsp { + __le16 queue_number; + __le16 flags; + __le16 write_pointer; + __le16 reserved; +} __packed; /* TX_QUEUE_CFG_RSP_API_S_VER_2 */ /** * struct iwl_trans_ops - transport specific operations diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 70abc6cd3b47..a22fe45eecc4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1699,6 +1699,9 @@ static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif) 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); +int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, int mac80211_queue, + u8 sta_id, u8 tid, unsigned int timeout); + /* * Disable a TXQ. * Note that in non-DQA mode the %mac80211_queue and %tid params are ignored. diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 36ae228c1612..ea35fcbd3b26 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -702,6 +702,41 @@ out: return ret; } +static int iwl_mvm_sta_alloc_queue_tvqm(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, u8 ac, + int tid) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + unsigned int wdg_timeout = + iwl_mvm_get_wd_timeout(mvm, mvmsta->vif, false, false); + u8 mac_queue = mvmsta->vif->hw_queue[ac]; + int queue = -1; + + lockdep_assert_held(&mvm->mutex); + + IWL_DEBUG_TX_QUEUES(mvm, + "Allocating queue for sta %d on tid %d\n", + mvmsta->sta_id, tid); + queue = iwl_mvm_tvqm_enable_txq(mvm, mac_queue, mvmsta->sta_id, tid, + wdg_timeout); + if (queue < 0) + return queue; + + IWL_DEBUG_TX_QUEUES(mvm, "Allocated queue is %d\n", queue); + + spin_lock_bh(&mvmsta->lock); + mvmsta->tid_data[tid].txq_id = queue; + mvmsta->tid_data[tid].is_tid_active = true; + mvmsta->tfd_queue_msk |= BIT(queue); + spin_unlock_bh(&mvmsta->lock); + + spin_lock_bh(&mvm->queue_info_lock); + mvm->queue_info[queue].status = IWL_MVM_QUEUE_READY; + spin_unlock_bh(&mvm->queue_info_lock); + + return 0; +} + static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u8 ac, int tid, struct ieee80211_hdr *hdr) @@ -727,6 +762,9 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); + if (iwl_mvm_has_new_tx_api(mvm)) + return iwl_mvm_sta_alloc_queue_tvqm(mvm, sta, ac, tid); + spin_lock_bh(&mvmsta->lock); tfd_queue_mask = mvmsta->tfd_queue_msk; spin_unlock_bh(&mvmsta->lock); @@ -782,15 +820,6 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, /* No free queue - we'll have to share */ if (queue <= 0) { - /* This shouldn't happen in new HW - we have 512 queues */ - if (WARN(iwl_mvm_has_new_tx_api(mvm), - "No available queues for tid %d on sta_id %d\n", - tid, cfg.sta_id)) { - spin_unlock_bh(&mvm->queue_info_lock); - - return queue; - } - queue = iwl_mvm_get_shared_queue(mvm, tfd_queue_mask, ac); if (queue > 0) { shared_queue = true; @@ -875,9 +904,6 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, mvmsta->reserved_queue = IEEE80211_INVAL_HW_QUEUE; spin_unlock_bh(&mvmsta->lock); - if (iwl_mvm_has_new_tx_api(mvm)) - return 0; - if (!shared_queue) { ret = iwl_mvm_sta_send_to_fw(mvm, sta, true, STA_MODIFY_QUEUES); if (ret) @@ -1243,18 +1269,30 @@ static void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm, ac = tid_to_mac80211_ac[i]; mac_queue = mvm_sta->vif->hw_queue[ac]; - cfg.tid = i; - cfg.fifo = iwl_mvm_ac_to_tx_fifo[ac]; - cfg.aggregate = (txq_id >= IWL_MVM_DQA_MIN_DATA_QUEUE || - txq_id == IWL_MVM_DQA_BSS_CLIENT_QUEUE); + if (iwl_mvm_has_new_tx_api(mvm)) { + IWL_DEBUG_TX_QUEUES(mvm, + "Re-mapping sta %d tid %d\n", + mvm_sta->sta_id, i); + txq_id = iwl_mvm_tvqm_enable_txq(mvm, mac_queue, + mvm_sta->sta_id, + i, wdg_timeout); + tid_data->txq_id = txq_id; + } else { + u16 seq = IEEE80211_SEQ_TO_SN(tid_data->seq_number); - IWL_DEBUG_TX_QUEUES(mvm, - "Re-mapping sta %d tid %d to queue %d\n", - mvm_sta->sta_id, i, txq_id); + cfg.tid = i; + cfg.fifo = iwl_mvm_ac_to_tx_fifo[ac]; + cfg.aggregate = (txq_id >= IWL_MVM_DQA_MIN_DATA_QUEUE || + txq_id == + IWL_MVM_DQA_BSS_CLIENT_QUEUE); - iwl_mvm_enable_txq(mvm, txq_id, mac_queue, - IEEE80211_SEQ_TO_SN(tid_data->seq_number), - &cfg, wdg_timeout); + IWL_DEBUG_TX_QUEUES(mvm, + "Re-mapping sta %d tid %d to queue %d\n", + mvm_sta->sta_id, i, txq_id); + + iwl_mvm_enable_txq(mvm, txq_id, mac_queue, seq, &cfg, + wdg_timeout); + } mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_READY; } @@ -1751,7 +1789,13 @@ static void iwl_mvm_enable_aux_queue(struct iwl_mvm *mvm) mvm->cfg->base_params->wd_timeout : IWL_WATCHDOG_DISABLED; - if (iwl_mvm_is_dqa_supported(mvm)) { + if (iwl_mvm_has_new_tx_api(mvm)) { + int queue = iwl_mvm_tvqm_enable_txq(mvm, mvm->aux_queue, + mvm->aux_sta.sta_id, + IWL_MAX_TID_COUNT, + wdg_timeout); + mvm->aux_queue = queue; + } else if (iwl_mvm_is_dqa_supported(mvm)) { struct iwl_trans_txq_scd_cfg cfg = { .fifo = IWL_MVM_TX_FIFO_MCAST, .sta_id = mvm->aux_sta.sta_id, @@ -1863,7 +1907,7 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) lockdep_assert_held(&mvm->mutex); - if (iwl_mvm_is_dqa_supported(mvm)) { + if (iwl_mvm_is_dqa_supported(mvm) && !iwl_mvm_has_new_tx_api(mvm)) { if (vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_ADHOC) queue = mvm->probe_queue; @@ -1874,9 +1918,8 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) bsta->tfd_queue_msk |= BIT(queue); - if (!iwl_mvm_has_new_tx_api(mvm)) - iwl_mvm_enable_txq(mvm, queue, vif->hw_queue[0], 0, - &cfg, wdg_timeout); + iwl_mvm_enable_txq(mvm, queue, vif->hw_queue[0], 0, + &cfg, wdg_timeout); } if (vif->type == NL80211_IFTYPE_ADHOC) @@ -1894,9 +1937,18 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) * For a000 firmware and on we cannot add queue to a station unknown * to firmware so enable queue here - after the station was added */ - if (iwl_mvm_has_new_tx_api(mvm)) - iwl_mvm_enable_txq(mvm, queue, vif->hw_queue[0], 0, &cfg, - wdg_timeout); + if (iwl_mvm_has_new_tx_api(mvm)) { + int queue = iwl_mvm_tvqm_enable_txq(mvm, vif->hw_queue[0], + bsta->sta_id, + IWL_MAX_TID_COUNT, + wdg_timeout); + if (vif->type == NL80211_IFTYPE_AP) + mvm->probe_queue = queue; + else if (vif->type == NL80211_IFTYPE_P2P_DEVICE) + mvm->p2p_dev_queue = queue; + + bsta->tfd_queue_msk |= BIT(queue); + } return 0; } @@ -2064,8 +2116,16 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) * This is needed for a000 firmware which won't accept SCD_QUEUE_CFG * command with unknown station id. */ - iwl_mvm_enable_txq(mvm, vif->cab_queue, vif->cab_queue, 0, &cfg, - timeout); + if (iwl_mvm_has_new_tx_api(mvm)) { + int queue = iwl_mvm_tvqm_enable_txq(mvm, vif->cab_queue, + msta->sta_id, + IWL_MAX_TID_COUNT, + timeout); + vif->cab_queue = queue; + } else { + iwl_mvm_enable_txq(mvm, vif->cab_queue, vif->cab_queue, 0, + &cfg, timeout); + } return 0; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 70ec048ac152..1dde05697c29 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -695,14 +695,47 @@ static bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm, int queue, return enable_queue; } +int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, int mac80211_queue, + u8 sta_id, u8 tid, unsigned int timeout) +{ + struct iwl_tx_queue_cfg_cmd cmd = { + .flags = cpu_to_le16(TX_QUEUE_CFG_ENABLE_QUEUE), + .sta_id = sta_id, + .tid = tid, + }; + int queue; + + if (cmd.tid == IWL_MAX_TID_COUNT) + cmd.tid = IWL_MGMT_TID; + queue = iwl_trans_txq_alloc(mvm->trans, (void *)&cmd, + SCD_QUEUE_CFG, timeout); + + if (queue < 0) { + IWL_DEBUG_TX_QUEUES(mvm, + "Failed allocating TXQ for sta %d tid %d, ret: %d\n", + sta_id, tid, queue); + return queue; + } + + IWL_DEBUG_TX_QUEUES(mvm, "Enabling TXQ #%d for sta %d tid %d\n", + queue, sta_id, tid); + + iwl_mvm_update_txq_mapping(mvm, queue, mac80211_queue, sta_id, tid); + + return queue; +} + 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) { + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return; + /* Send the enabling command if we need to */ if (iwl_mvm_update_txq_mapping(mvm, queue, mac80211_queue, cfg->sta_id, cfg->tid)) { - struct iwl_tx_queue_cfg_cmd cmd = { + struct iwl_scd_txq_cfg_cmd cmd = { .scd_queue = queue, .action = SCD_CFG_ENABLE_QUEUE, .window = cfg->frame_limit, @@ -713,14 +746,6 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, .tid = cfg->tid, }; - if (iwl_mvm_has_new_tx_api(mvm)) { - if (cmd.tid == IWL_MAX_TID_COUNT) - cmd.tid = IWL_MGMT_TID; - iwl_trans_txq_alloc(mvm->trans, (void *)&cmd, - SCD_QUEUE_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, @@ -734,12 +759,11 @@ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, u8 tid, u8 flags) { - struct iwl_tx_queue_cfg_cmd cmd = { + struct iwl_scd_txq_cfg_cmd cmd = { .scd_queue = queue, .action = SCD_CFG_DISABLE_QUEUE, }; bool remove_mac_queue = true; - int ret; spin_lock_bh(&mvm->queue_info_lock); @@ -812,22 +836,21 @@ int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, if (iwl_mvm_has_new_tx_api(mvm)) { iwl_trans_txq_free(mvm->trans, queue); - if (cmd.tid == IWL_MAX_TID_COUNT) - cmd.tid = IWL_MGMT_TID; - ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, - sizeof(cmd), &cmd); } else { + int ret; + iwl_trans_txq_disable(mvm->trans, queue, false); ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, sizeof(struct iwl_scd_txq_cfg_cmd), &cmd); - } - if (ret) - IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n", - queue, ret); + if (ret) + IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n", + queue, ret); + return ret; + } - return ret; + return 0; } /** diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 0bc9522cf364..9fb46a6f47cf 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -862,15 +862,15 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, unsigned int timeout) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue_cfg_rsp *rsp; struct iwl_txq *txq; struct iwl_host_cmd hcmd = { .id = cmd_id, .len = { sizeof(*cmd) }, .data = { cmd, }, - .flags = 0, + .flags = CMD_WANT_SKB, }; - int ret, qid = cmd->scd_queue; - u16 ssn = le16_to_cpu(cmd->ssn); + int ret, qid; txq = kzalloc(sizeof(*txq), GFP_KERNEL); if (!txq) @@ -883,48 +883,62 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans, return -ENOMEM; } - if (test_and_set_bit(cmd->scd_queue, trans_pcie->queue_used)) { - WARN_ONCE(1, "queue %d already used", cmd->scd_queue); - return -EINVAL; - } - - trans_pcie->txq[qid] = txq; - trans_pcie->txq[qid]->id = qid; - ret = iwl_pcie_txq_alloc(trans, txq, TFD_TX_CMD_SLOTS, false); if (ret) { - IWL_ERR(trans, "Tx %d queue init failed\n", qid); + IWL_ERR(trans, "Tx queue alloc failed\n"); goto error; } ret = iwl_pcie_txq_init(trans, txq, TFD_TX_CMD_SLOTS, false); if (ret) { - IWL_ERR(trans, "Tx %d queue alloc failed\n", qid); + IWL_ERR(trans, "Tx queue init failed\n"); goto error; } txq->wd_timeout = msecs_to_jiffies(timeout); - /* - * Place first TFD at index corresponding to start sequence number. - * Assumes that ssn_idx is valid (!= 0xFFF) - */ - txq->read_ptr = (ssn & 0xff); - txq->write_ptr = (ssn & 0xff); - iwl_write_direct32(trans, HBUS_TARG_WRPTR, - (ssn & 0xff) | (cmd->scd_queue << 16)); - - IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d WrPtr: %d\n", - cmd->scd_queue, ssn & 0xff); - cmd->tfdq_addr = cpu_to_le64(txq->dma_addr); cmd->byte_cnt_addr = cpu_to_le64(txq->bc_tbl.dma); cmd->cb_size = cpu_to_le32(TFD_QUEUE_CB_SIZE(TFD_QUEUE_SIZE_MAX)); - return iwl_trans_send_cmd(trans, &hcmd); + ret = iwl_trans_send_cmd(trans, &hcmd); + if (ret) + goto error; + + if (WARN_ON(iwl_rx_packet_payload_len(hcmd.resp_pkt) != sizeof(*rsp))) { + ret = -EINVAL; + goto error; + } + + rsp = (void *)hcmd.resp_pkt->data; + qid = le16_to_cpu(rsp->queue_number); + + if (qid > ARRAY_SIZE(trans_pcie->txq)) { + WARN_ONCE(1, "queue index %d unsupported", qid); + ret = -EIO; + goto error; + } + + if (test_and_set_bit(qid, trans_pcie->queue_used)) { + WARN_ONCE(1, "queue %d already used", qid); + ret = -EIO; + goto error; + } + + txq->id = qid; + trans_pcie->txq[qid] = txq; + + /* Place first TFD at index corresponding to start sequence number */ + txq->read_ptr = le16_to_cpu(rsp->write_pointer); + txq->write_ptr = le16_to_cpu(rsp->write_pointer); + iwl_write_direct32(trans, HBUS_TARG_WRPTR, + (txq->write_ptr) | (qid << 16)); + IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d\n", qid); + + return qid; error: - iwl_pcie_gen2_txq_free(trans, cmd->scd_queue); - return -ENOMEM; + iwl_pcie_gen2_txq_free_memory(trans, txq); + return ret; } void iwl_trans_pcie_dyn_txq_free(struct iwl_trans *trans, int queue) -- cgit From 5a4aa895521bb0f7148e34de69059799f9cb1690 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Wed, 1 Feb 2017 16:21:32 +0200 Subject: iwlwifi: mvm: remove unneeded reg write in iwl_mvm_up() Not only that this write is not needed (as FW does this itself), on newer HW this register is write protected so trying to write there will cause problems. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 3 --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 4 ---- 2 files changed, 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index e1aa1f7836fc..4bceaccee62f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -294,9 +294,6 @@ /*********************** END TX SCHEDULER *************************************/ -/* tcp checksum offload */ -#define RX_EN_CSUM (0x00a00d88) - /* Oscillator clock */ #define OSC_CLK (0xa04068) #define OSC_CLK_FORCE_CONTROL (0x8) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 2a4c952ef01a..bfccbadd1a6e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1475,10 +1475,6 @@ int iwl_mvm_up(struct iwl_mvm *mvm) goto error; } - if (iwl_mvm_is_csum_supported(mvm) && - mvm->cfg->features & NETIF_F_RXCSUM) - iwl_trans_write_prph(mvm->trans, RX_EN_CSUM, 0x3); - /* allow FW/transport low power modes if not during restart */ if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); -- cgit From bbf049d92a718ddba70d72388dcdea5304a3f723 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 19 May 2016 16:19:22 +0300 Subject: iwlwifi: mvm: do not turn on RX_FLAG_AMSDU_MORE This flag is used for mac80211 reordering. As we do reordering ourselves, turning it on is misleading and pointless. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 29c9c56ed3fa..44072c3c4b5f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -917,9 +917,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, u8 *qc = ieee80211_get_qos_ctl(hdr); *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; - if (!(desc->amsdu_info & - IWL_RX_MPDU_AMSDU_LAST_SUBFRAME)) - rx_status->flag |= RX_FLAG_AMSDU_MORE; } if (baid != IWL_RX_REORDER_DATA_INVALID_BAID) iwl_mvm_agg_rx_received(mvm, baid); -- cgit From a56cb4f0d826359d98adf12cb5e0a2de35e8aa59 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 31 Jan 2017 14:36:10 +0200 Subject: iwlwifi: mvm: work around HW issue with AMSDU de-aggregation Seems like HW is reversing addr3 in the MAC header of de-aggregated AMSDU. Reverse it back. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 44072c3c4b5f..c99775039f59 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -911,12 +911,19 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, * Our hardware de-aggregates AMSDUs but copies the mac header * as it to the de-aggregated MPDUs. We need to turn off the * AMSDU bit in the QoS control ourselves. + * In addition, HW reverses addr3 - reverse it back. */ if ((desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU) && !WARN_ON(!ieee80211_is_data_qos(hdr->frame_control))) { + int i; u8 *qc = ieee80211_get_qos_ctl(hdr); + u8 mac_addr[ETH_ALEN]; *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; + + for (i = 0; i < ETH_ALEN; i++) + mac_addr[i] = hdr->addr3[ETH_ALEN - i - 1]; + ether_addr_copy(hdr->addr3, mac_addr); } if (baid != IWL_RX_REORDER_DATA_INVALID_BAID) iwl_mvm_agg_rx_received(mvm, baid); -- cgit From 0f17e1bb8447f1cd2228c363a86c36340b87fa08 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Wed, 4 Jan 2017 09:30:24 +0200 Subject: iwlwifi: mvm: change TX_CMD_SEC_KEY_FROM_TABLE value Change the value of TX_CMD_SEC_KEY_FROM_TABLE flag in TX_CMD security flags to accommodate a FW API change. Bump min API for 9000 series devices to 30 to keep the driver aligned aligned the FW. Signed-off-by: David Spinadel Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-9000.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c index d265b279b2ca..110ceefccc15 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c @@ -58,7 +58,7 @@ #define IWL9000_UCODE_API_MAX 30 /* Lowest firmware API version supported */ -#define IWL9000_UCODE_API_MIN 17 +#define IWL9000_UCODE_API_MIN 30 /* NVM versions */ #define IWL9000_NVM_VERSION 0x0a1d diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h index e9f055706a6f..81b98915b1a4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h @@ -174,7 +174,7 @@ enum iwl_tx_cmd_sec_ctrl { TX_CMD_SEC_EXT = 0x04, TX_CMD_SEC_GCMP = 0x05, TX_CMD_SEC_KEY128 = 0x08, - TX_CMD_SEC_KEY_FROM_TABLE = 0x08, + TX_CMD_SEC_KEY_FROM_TABLE = 0x10, }; /* TODO: how does these values are OK with only 16 bit variable??? */ -- cgit From 5d43eab6632df3a5b5bfc79025860dc4583ed0f4 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 2 Feb 2017 12:51:39 +0200 Subject: iwlwifi: mvm: ignore BAID for SN smaller than SSN When we get SN that is smaller than SSN of the aggregation, we shouldn't apply any reordering on them. Further more, HW NSSN will be zeroed, which can cause us to make some invalid decisions. Detect the situation and invalidate the BAID. Fixes: b915c10174fb ("iwlwifi: mvm: add reorder buffer per queue") Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 30 +++++++++++++++++++++------ drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 1 + 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index a22fe45eecc4..32e62175cbd5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -626,6 +626,7 @@ struct iwl_mvm_shared_mem_cfg { * @reorder_timer: timer for frames are in the reorder buffer. For AMSDU * it is the time of last received sub-frame * @removed: prevent timer re-arming + * @valid: reordering is valid for this queue * @lock: protect reorder buffer internal state * @mvm: mvm pointer, needed for frame timer context */ @@ -641,6 +642,7 @@ struct iwl_mvm_reorder_buffer { unsigned long reorder_time[IEEE80211_MAX_AMPDU_BUF]; struct timer_list reorder_timer; bool removed; + bool valid; spinlock_t lock; struct iwl_mvm *mvm; } ____cacheline_aligned_in_smp; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index c99775039f59..8601d25407b3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -636,9 +636,13 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, return false; baid_data = rcu_dereference(mvm->baid_map[baid]); - if (WARN(!baid_data, - "Received baid %d, but no data exists for this BAID\n", baid)) + if (!baid_data) { + WARN(!(reorder & IWL_RX_MPDU_REORDER_BA_OLD_SN), + "Received baid %d, but no data exists for this BAID\n", + baid); return false; + } + if (WARN(tid != baid_data->tid || mvm_sta->sta_id != baid_data->sta_id, "baid 0x%x is mapped to sta:%d tid:%d, but was received for sta:%d tid:%d\n", baid, baid_data->sta_id, baid_data->tid, mvm_sta->sta_id, @@ -653,6 +657,14 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, spin_lock_bh(&buffer->lock); + if (!buffer->valid) { + if (reorder & IWL_RX_MPDU_REORDER_BA_OLD_SN) { + spin_unlock_bh(&buffer->lock); + return false; + } + buffer->valid = true; + } + if (ieee80211_is_back_req(hdr->frame_control)) { iwl_mvm_release_frames(mvm, sta, napi, buffer, nssn); goto drop; @@ -737,7 +749,8 @@ drop: return true; } -static void iwl_mvm_agg_rx_received(struct iwl_mvm *mvm, u8 baid) +static void iwl_mvm_agg_rx_received(struct iwl_mvm *mvm, + u32 reorder_data, u8 baid) { unsigned long now = jiffies; unsigned long timeout; @@ -746,8 +759,10 @@ static void iwl_mvm_agg_rx_received(struct iwl_mvm *mvm, u8 baid) rcu_read_lock(); data = rcu_dereference(mvm->baid_map[baid]); - if (WARN_ON(!data)) + if (!data) { + WARN_ON(!(reorder_data & IWL_RX_MPDU_REORDER_BA_OLD_SN)); goto out; + } if (!data->timeout) goto out; @@ -925,8 +940,11 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, mac_addr[i] = hdr->addr3[ETH_ALEN - i - 1]; ether_addr_copy(hdr->addr3, mac_addr); } - if (baid != IWL_RX_REORDER_DATA_INVALID_BAID) - iwl_mvm_agg_rx_received(mvm, baid); + if (baid != IWL_RX_REORDER_DATA_INVALID_BAID) { + u32 reorder_data = le32_to_cpu(desc->reorder_data); + + iwl_mvm_agg_rx_received(mvm, reorder_data, baid); + } } /* Set up the HT phy flags */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index ea35fcbd3b26..cc9c92b8e2ba 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -2231,6 +2231,7 @@ static void iwl_mvm_init_reorder_buffer(struct iwl_mvm *mvm, reorder_buf->mvm = mvm; reorder_buf->queue = i; reorder_buf->sta_id = sta_id; + reorder_buf->valid = false; for (j = 0; j < reorder_buf->buf_size; j++) __skb_queue_head_init(&reorder_buf->entries[j]); } -- cgit From e71ca5ea7466a7a38bb6cd83388623e47f62467b Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 8 Feb 2017 14:53:32 +0200 Subject: iwlwifi: mvm: provide the actual number of frames for the SP len In the end, the firmware doesn't want the SP len as present in the WMM IE, but rather the actual number of frames. Fixes: bd3c6cf901a8 ("iwlwifi: mvm: tell the firmware about the U-APSD parameters") Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index cc9c92b8e2ba..a2a1fa06b781 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -223,7 +223,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) add_sta_cmd.uapsd_acs |= BIT(AC_VO); add_sta_cmd.uapsd_acs |= add_sta_cmd.uapsd_acs << 4; - add_sta_cmd.sp_length = sta->max_sp; + add_sta_cmd.sp_length = sta->max_sp ? sta->max_sp * 2 : 128; } status = ADD_STA_SUCCESS; -- cgit From f27588938a585d38f2ed9e7398b24d0a2b421abf Mon Sep 17 00:00:00 2001 From: Tzipi Peres Date: Sun, 12 Feb 2017 11:08:08 +0200 Subject: iwlwifi: add four new 8265 and 8275 series PCI IDs Add one new PCI ID for the 8265 series. Add three new PCI ID for the 8275 series. Signed-off-by: Tzipi Peres Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index d38fcc1a4768..52e1d0c9428d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -501,6 +501,10 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x24FD, 0x0930, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x0950, iwl8265_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x0850, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1014, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x3E02, iwl8275_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x3E01, iwl8275_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1012, iwl8275_2ac_cfg)}, {IWL_PCI_DEVICE(0x24FD, 0x0012, iwl8275_2ac_cfg)}, /* 9000 Series */ -- cgit From fa1f2b617a94ff734fe2d5136bbda4e4f2d22635 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 26 Jan 2017 12:40:25 +0200 Subject: iwlwifi: mvm: support change to a000 smem API API was changed once more to support 2 LMACs. Adapt to change while preserving current functionality. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 2 + drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 41 +++- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 287 ++++++++++++++---------- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 53 +++-- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 11 +- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 2 +- 6 files changed, 241 insertions(+), 155 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 4bceaccee62f..f832e58e0ef9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -314,6 +314,8 @@ #define LMPM_SECURE_CPU1_HDR_MEM_SPACE (0x420000) #define LMPM_SECURE_CPU2_HDR_MEM_SPACE (0x420400) +#define LMAC2_PRPH_OFFSET (0x100000) + /* Rx FIFO */ #define RXF_SIZE_ADDR (0xa00c88) #define RXF_RD_D_SPACE (0xa00c40) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index dd2bd7c3587c..56f50d8a7b69 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -2026,19 +2026,48 @@ struct iwl_shared_mem_cfg_v1 { __le32 internal_txfifo_size[TX_FIFO_INTERNAL_MAX_NUM]; } __packed; /* SHARED_MEM_ALLOC_API_S_VER_2 */ +/** + * struct iwl_shared_mem_lmac_cfg - LMAC shared memory configuration + * + * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB) + * @txfifo_size: size of TX FIFOs + * @rxfifo1_addr: RXF1 addr + * @rxfifo1_size: RXF1 size + */ +struct iwl_shared_mem_lmac_cfg { + __le32 txfifo_addr; + __le32 txfifo_size[TX_FIFO_MAX_NUM]; + __le32 rxfifo1_addr; + __le32 rxfifo1_size; + +} __packed; /* SHARED_MEM_ALLOC_LMAC_API_S_VER_1 */ + +/** + * Shared memory configuration information from the FW + * + * @shared_mem_addr: shared memory address + * @shared_mem_size: shared memory size + * @sample_buff_addr: internal sample (mon/adc) buff addr + * @sample_buff_size: internal sample buff size + * @rxfifo2_addr: start addr of RXF2 + * @rxfifo2_size: size of RXF2 + * @page_buff_addr: used by UMAC and performance debug (page miss analysis), + * when paging is not supported this should be 0 + * @page_buff_size: size of %page_buff_addr + * @lmac_num: number of LMACs (1 or 2) + * @lmac_smem: per - LMAC smem data + */ struct iwl_shared_mem_cfg { __le32 shared_mem_addr; __le32 shared_mem_size; __le32 sample_buff_addr; __le32 sample_buff_size; - __le32 txfifo_addr; - __le32 txfifo_size[TX_FIFO_MAX_NUM]; - __le32 rxfifo_size[RX_FIFO_MAX_NUM]; + __le32 rxfifo2_addr; + __le32 rxfifo2_size; __le32 page_buff_addr; __le32 page_buff_size; - __le32 rxfifo_addr; - __le32 internal_txfifo_addr; - __le32 internal_txfifo_size[TX_FIFO_INTERNAL_MAX_NUM]; + __le32 lmac_num; + struct iwl_shared_mem_lmac_cfg lmac_smem[2]; } __packed; /* SHARED_MEM_ALLOC_API_S_VER_3 */ /** diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index a027b11bbdb3..7b86a4f1b574 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -7,7 +7,7 @@ * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright(c) 2015 - 2017 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 @@ -32,7 +32,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -99,10 +99,120 @@ static void iwl_mvm_read_radio_reg(struct iwl_mvm *mvm, iwl_trans_release_nic_access(mvm->trans, &flags); } +static void iwl_mvm_dump_rxf(struct iwl_mvm *mvm, + struct iwl_fw_error_dump_data **dump_data, + int size, u32 offset, int fifo_num) +{ + struct iwl_fw_error_dump_fifo *fifo_hdr; + u32 *fifo_data; + u32 fifo_len; + int i; + + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = size; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + return; + + /* Add a TLV for the RXF */ + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); + (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(fifo_num); + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_D_SPACE + offset)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_WR_PTR + offset)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_RD_PTR + offset)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_FENCE_PTR + offset)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_SET_FENCE_MODE + offset)); + + /* Lock fence */ + iwl_trans_write_prph(mvm->trans, RXF_SET_FENCE_MODE + offset, 0x1); + /* Set fence pointer to the same place like WR pointer */ + iwl_trans_write_prph(mvm->trans, RXF_LD_WR2FENCE + offset, 0x1); + /* Set fence offset */ + iwl_trans_write_prph(mvm->trans, + RXF_LD_FENCE_OFFSET_ADDR + offset, 0x0); + + /* Read FIFO */ + fifo_len /= sizeof(u32); /* Size in DWORDS */ + for (i = 0; i < fifo_len; i++) + fifo_data[i] = iwl_trans_read_prph(mvm->trans, + RXF_FIFO_RD_FENCE_INC + + offset); + *dump_data = iwl_fw_error_next_data(*dump_data); +} + +static void iwl_mvm_dump_txf(struct iwl_mvm *mvm, + struct iwl_fw_error_dump_data **dump_data, + int size, u32 offset, int fifo_num) +{ + struct iwl_fw_error_dump_fifo *fifo_hdr; + u32 *fifo_data; + u32 fifo_len; + int i; + + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = size; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + return; + + /* Add a TLV for the FIFO */ + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); + (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(fifo_num); + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_FIFO_ITEM_CNT + offset)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_WR_PTR + offset)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_RD_PTR + offset)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_FENCE_PTR + offset)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_LOCK_FENCE + offset)); + + /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ + iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR + offset, + TXF_WR_PTR + offset); + + /* Dummy-read to advance the read pointer to the head */ + iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA + offset); + + /* Read FIFO */ + fifo_len /= sizeof(u32); /* Size in DWORDS */ + for (i = 0; i < fifo_len; i++) + fifo_data[i] = iwl_trans_read_prph(mvm->trans, + TXF_READ_MODIFY_DATA + + offset); + *dump_data = iwl_fw_error_next_data(*dump_data); +} + static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, struct iwl_fw_error_dump_data **dump_data) { struct iwl_fw_error_dump_fifo *fifo_hdr; + struct iwl_mvm_shared_mem_cfg *cfg = &mvm->smem_cfg; u32 *fifo_data; u32 fifo_len; unsigned long flags; @@ -111,126 +221,47 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, if (!iwl_trans_grab_nic_access(mvm->trans, &flags)) return; - /* Pull RXF data from all RXFs */ - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) { - /* - * Keep aside the additional offset that might be needed for - * next RXF - */ - u32 offset_diff = RXF_DIFF_FROM_PREV * i; - - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = mvm->shared_mem_cfg.rxfifo_size[i]; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - continue; - - /* Add a TLV for the RXF */ - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); - (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(i); - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_D_SPACE + - offset_diff)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_WR_PTR + - offset_diff)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_RD_PTR + - offset_diff)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_RD_FENCE_PTR + - offset_diff)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - RXF_SET_FENCE_MODE + - offset_diff)); - - /* Lock fence */ - iwl_trans_write_prph(mvm->trans, - RXF_SET_FENCE_MODE + offset_diff, 0x1); - /* Set fence pointer to the same place like WR pointer */ - iwl_trans_write_prph(mvm->trans, - RXF_LD_WR2FENCE + offset_diff, 0x1); - /* Set fence offset */ - iwl_trans_write_prph(mvm->trans, - RXF_LD_FENCE_OFFSET_ADDR + offset_diff, - 0x0); - - /* Read FIFO */ - fifo_len /= sizeof(u32); /* Size in DWORDS */ - for (j = 0; j < fifo_len; j++) - fifo_data[j] = iwl_trans_read_prph(mvm->trans, - RXF_FIFO_RD_FENCE_INC + - offset_diff); - *dump_data = iwl_fw_error_next_data(*dump_data); - } - - /* Pull TXF data from all TXFs */ - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) { + /* Pull RXF1 */ + iwl_mvm_dump_rxf(mvm, dump_data, cfg->lmac[0].rxfifo1_size, 0, 0); + /* Pull RXF2 */ + iwl_mvm_dump_rxf(mvm, dump_data, cfg->rxfifo2_size, + RXF_DIFF_FROM_PREV, 1); + /* Pull LMAC2 RXF1 */ + if (mvm->smem_cfg.num_lmacs > 1) + iwl_mvm_dump_rxf(mvm, dump_data, cfg->lmac[1].rxfifo1_size, + LMAC2_PRPH_OFFSET, 2); + + /* Pull TXF data from LMAC1 */ + for (i = 0; i < mvm->smem_cfg.num_txfifo_entries; i++) { /* Mark the number of TXF we're pulling now */ iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i); + iwl_mvm_dump_txf(mvm, dump_data, cfg->lmac[0].txfifo_size[i], + 0, i); + } - fifo_hdr = (void *)(*dump_data)->data; - fifo_data = (void *)fifo_hdr->data; - fifo_len = mvm->shared_mem_cfg.txfifo_size[i]; - - /* No need to try to read the data if the length is 0 */ - if (fifo_len == 0) - continue; - - /* Add a TLV for the FIFO */ - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); - (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); - - fifo_hdr->fifo_num = cpu_to_le32(i); - fifo_hdr->available_bytes = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_FIFO_ITEM_CNT)); - fifo_hdr->wr_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_WR_PTR)); - fifo_hdr->rd_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_RD_PTR)); - fifo_hdr->fence_ptr = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_FENCE_PTR)); - fifo_hdr->fence_mode = - cpu_to_le32(iwl_trans_read_prph(mvm->trans, - TXF_LOCK_FENCE)); - - /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ - iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR, - TXF_WR_PTR); - - /* Dummy-read to advance the read pointer to the head */ - iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA); - - /* Read FIFO */ - fifo_len /= sizeof(u32); /* Size in DWORDS */ - for (j = 0; j < fifo_len; j++) - fifo_data[j] = iwl_trans_read_prph(mvm->trans, - TXF_READ_MODIFY_DATA); - *dump_data = iwl_fw_error_next_data(*dump_data); + /* Pull TXF data from LMAC2 */ + if (mvm->smem_cfg.num_lmacs > 1) { + for (i = 0; i < mvm->smem_cfg.num_txfifo_entries; i++) { + /* Mark the number of TXF we're pulling now */ + iwl_trans_write_prph(mvm->trans, + TXF_LARC_NUM + LMAC2_PRPH_OFFSET, + i); + iwl_mvm_dump_txf(mvm, dump_data, + cfg->lmac[1].txfifo_size[i], + LMAC2_PRPH_OFFSET, + i + cfg->num_txfifo_entries); + } } if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { /* Pull UMAC internal TXF data from all TXFs */ for (i = 0; - i < ARRAY_SIZE(mvm->shared_mem_cfg.internal_txfifo_size); + i < ARRAY_SIZE(mvm->smem_cfg.internal_txfifo_size); i++) { fifo_hdr = (void *)(*dump_data)->data; fifo_data = (void *)fifo_hdr->data; - fifo_len = mvm->shared_mem_cfg.internal_txfifo_size[i]; + fifo_len = mvm->smem_cfg.internal_txfifo_size[i]; /* No need to try to read the data if the length is 0 */ if (fifo_len == 0) @@ -246,7 +277,7 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, /* Mark the number of TXF we're pulling now */ iwl_trans_write_prph(mvm->trans, TXF_CPU2_NUM, i + - ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size)); + mvm->smem_cfg.num_txfifo_entries); fifo_hdr->available_bytes = cpu_to_le32(iwl_trans_read_prph(mvm->trans, @@ -553,31 +584,45 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) /* reading RXF/TXF sizes */ if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { - struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg; + struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->smem_cfg; fifo_data_len = 0; - /* Count RXF size */ - for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) { - if (!mem_cfg->rxfifo_size[i]) - continue; - + /* Count RXF2 size */ + if (mem_cfg->rxfifo2_size) { /* Add header info */ - fifo_data_len += mem_cfg->rxfifo_size[i] + + fifo_data_len += mem_cfg->rxfifo2_size + sizeof(*dump_data) + sizeof(struct iwl_fw_error_dump_fifo); } - for (i = 0; i < mem_cfg->num_txfifo_entries; i++) { - if (!mem_cfg->txfifo_size[i]) + /* Count RXF1 sizes */ + for (i = 0; i < mem_cfg->num_lmacs; i++) { + if (!mem_cfg->lmac[i].rxfifo1_size) continue; /* Add header info */ - fifo_data_len += mem_cfg->txfifo_size[i] + + fifo_data_len += mem_cfg->lmac[i].rxfifo1_size + sizeof(*dump_data) + sizeof(struct iwl_fw_error_dump_fifo); } + /* Count TXF sizes */ + for (i = 0; i < mem_cfg->num_lmacs; i++) { + int j; + + for (j = 0; j < mem_cfg->num_txfifo_entries; j++) { + if (!mem_cfg->lmac[i].txfifo_size[j]) + continue; + + /* Add header info */ + fifo_data_len += + mem_cfg->lmac[i].txfifo_size[j] + + sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_fifo); + } + } + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { for (i = 0; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index bfccbadd1a6e..a845233d14a3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -897,24 +897,27 @@ static void iwl_mvm_parse_shared_mem_a000(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { struct iwl_shared_mem_cfg *mem_cfg = (void *)pkt->data; - int i; + int i, lmac; + int lmac_num = le32_to_cpu(mem_cfg->lmac_num); - mvm->shared_mem_cfg.num_txfifo_entries = - ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); - for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) - mvm->shared_mem_cfg.txfifo_size[i] = - le32_to_cpu(mem_cfg->txfifo_size[i]); - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) - mvm->shared_mem_cfg.rxfifo_size[i] = - le32_to_cpu(mem_cfg->rxfifo_size[i]); + if (WARN_ON(lmac_num > ARRAY_SIZE(mem_cfg->lmac_smem))) + return; - BUILD_BUG_ON(sizeof(mvm->shared_mem_cfg.internal_txfifo_size) != - sizeof(mem_cfg->internal_txfifo_size)); + mvm->smem_cfg.num_lmacs = lmac_num; + mvm->smem_cfg.num_txfifo_entries = + ARRAY_SIZE(mem_cfg->lmac_smem[0].txfifo_size); + mvm->smem_cfg.rxfifo2_size = le32_to_cpu(mem_cfg->rxfifo2_size); - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.internal_txfifo_size); - i++) - mvm->shared_mem_cfg.internal_txfifo_size[i] = - le32_to_cpu(mem_cfg->internal_txfifo_size[i]); + for (lmac = 0; lmac < lmac_num; lmac++) { + struct iwl_shared_mem_lmac_cfg *lmac_cfg = + &mem_cfg->lmac_smem[lmac]; + + for (i = 0; i < ARRAY_SIZE(lmac_cfg->txfifo_size); i++) + mvm->smem_cfg.lmac[lmac].txfifo_size[i] = + le32_to_cpu(lmac_cfg->txfifo_size[i]); + mvm->smem_cfg.lmac[lmac].rxfifo1_size = + le32_to_cpu(lmac_cfg->rxfifo1_size); + } } static void iwl_mvm_parse_shared_mem(struct iwl_mvm *mvm, @@ -923,25 +926,27 @@ static void iwl_mvm_parse_shared_mem(struct iwl_mvm *mvm, struct iwl_shared_mem_cfg_v1 *mem_cfg = (void *)pkt->data; int i; - mvm->shared_mem_cfg.num_txfifo_entries = - ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); + mvm->smem_cfg.num_lmacs = 1; + + mvm->smem_cfg.num_txfifo_entries = ARRAY_SIZE(mem_cfg->txfifo_size); for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) - mvm->shared_mem_cfg.txfifo_size[i] = + mvm->smem_cfg.lmac[0].txfifo_size[i] = le32_to_cpu(mem_cfg->txfifo_size[i]); - for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) - mvm->shared_mem_cfg.rxfifo_size[i] = - le32_to_cpu(mem_cfg->rxfifo_size[i]); + + mvm->smem_cfg.lmac[0].rxfifo1_size = + le32_to_cpu(mem_cfg->rxfifo_size[0]); + mvm->smem_cfg.rxfifo2_size = le32_to_cpu(mem_cfg->rxfifo_size[1]); /* new API has more data, from rxfifo_addr field and on */ if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { - BUILD_BUG_ON(sizeof(mvm->shared_mem_cfg.internal_txfifo_size) != + BUILD_BUG_ON(sizeof(mvm->smem_cfg.internal_txfifo_size) != sizeof(mem_cfg->internal_txfifo_size)); for (i = 0; - i < ARRAY_SIZE(mvm->shared_mem_cfg.internal_txfifo_size); + i < ARRAY_SIZE(mvm->smem_cfg.internal_txfifo_size); i++) - mvm->shared_mem_cfg.internal_txfifo_size[i] = + mvm->smem_cfg.internal_txfifo_size[i] = le32_to_cpu(mem_cfg->internal_txfifo_size[i]); } } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 32e62175cbd5..14b8c2eba7fc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -604,10 +604,15 @@ enum iwl_mvm_tdls_cs_state { IWL_MVM_TDLS_SW_ACTIVE, }; +#define MAX_NUM_LMAC 2 struct iwl_mvm_shared_mem_cfg { + int num_lmacs; int num_txfifo_entries; - u32 txfifo_size[TX_FIFO_MAX_NUM]; - u32 rxfifo_size[RX_FIFO_MAX_NUM]; + struct { + u32 txfifo_size[TX_FIFO_MAX_NUM]; + u32 rxfifo1_size; + } lmac[MAX_NUM_LMAC]; + u32 rxfifo2_size; u32 internal_txfifo_addr; u32 internal_txfifo_size[TX_FIFO_INTERNAL_MAX_NUM]; }; @@ -1034,7 +1039,7 @@ struct iwl_mvm { } peer; } tdls_cs; - struct iwl_mvm_shared_mem_cfg shared_mem_cfg; + struct iwl_mvm_shared_mem_cfg smem_cfg; u32 ciphers[IWL_MVM_NUM_CIPHERS]; struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 90c33ee69f21..ed7ee5004645 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -744,7 +744,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, * fifo to be able to send bursts. */ max_amsdu_len = min_t(unsigned int, max_amsdu_len, - mvm->shared_mem_cfg.txfifo_size[txf] - 256); + mvm->smem_cfg.lmac[0].txfifo_size[txf] - 256); if (unlikely(dbg_max_amsdu_len)) max_amsdu_len = min_t(unsigned int, max_amsdu_len, -- cgit From 386f49361a356f3f5c6afe076b9a61b54ac16a3f Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 22 Jan 2017 17:17:19 +0200 Subject: iwlwifi: support a000 CDB product Identify and load FW for a000 CDB product. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-a000.c | 20 ++++++++++++++++---- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 5 ++++- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 7 ++++--- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c index df4e8714d627..1c731b88d08c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2015-2016 Intel Deutschland GmbH + * Copyright(c) 2015-2017 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 @@ -18,7 +18,7 @@ * * BSD LICENSE * - * Copyright(c) 2015-2016 Intel Deutschland GmbH + * Copyright(c) 2015-2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -72,8 +72,9 @@ #define IWL_A000_SMEM_OFFSET 0x400000 #define IWL_A000_SMEM_LEN 0x68000 -#define IWL_A000_JF_FW_PRE "iwlwifi-Qu-a0-jf-b0-" -#define IWL_A000_HR_FW_PRE "iwlwifi-Qu-a0-hr-a0-" +#define IWL_A000_JF_FW_PRE "iwlwifi-Qu-a0-jf-b0-" +#define IWL_A000_HR_FW_PRE "iwlwifi-Qu-a0-hr-a0-" +#define IWL_A000_HR_CDB_FW_PRE "iwlwifi-QuIcp-a0-hrcdb-a0-" #define IWL_A000_HR_MODULE_FIRMWARE(api) \ IWL_A000_HR_FW_PRE "-" __stringify(api) ".ucode" @@ -134,6 +135,17 @@ const struct iwl_cfg iwla000_2ac_cfg_hr = { .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, }; +const struct iwl_cfg iwla000_2ac_cfg_hr_cdb = { + .name = "Intel(R) Dual Band Wireless AC a000", + .fw_name_pre = IWL_A000_HR_CDB_FW_PRE, + IWL_DEVICE_A000, + .ht_params = &iwl_a000_ht_params, + .nvm_ver = IWL_A000_NVM_VERSION, + .nvm_calib_ver = IWL_A000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, + .cdb = true, +}; + const struct iwl_cfg iwla000_2ac_cfg_jf = { .name = "Intel(R) Dual Band Wireless AC a000", .fw_name_pre = IWL_A000_JF_FW_PRE, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 06034a71a190..4af1267181a9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -314,6 +314,7 @@ struct iwl_pwr_tx_backoff { * @rf_id: need to read rf_id to determine the firmware image * @integrated: discrete or integrated * @gen2: a000 and on transport operation + * @cdb: CDB support * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -360,7 +361,8 @@ struct iwl_cfg { rf_id:1, integrated:1, use_tfh:1, - gen2:1; + gen2:1, + cdb:1; u8 valid_tx_ant; u8 valid_rx_ant; u8 non_shared_ant; @@ -450,6 +452,7 @@ extern const struct iwl_cfg iwl9270_2ac_cfg; extern const struct iwl_cfg iwl9460_2ac_cfg; extern const struct iwl_cfg iwl9560_2ac_cfg; extern const struct iwl_cfg iwla000_2ac_cfg_hr; +extern const struct iwl_cfg iwla000_2ac_cfg_hr_cdb; extern const struct iwl_cfg iwla000_2ac_cfg_jf; #endif /* CONFIG_IWLMVM */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index 52e1d0c9428d..e51760e752d4 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -537,7 +537,8 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0xA370, 0x1030, iwl9560_2ac_cfg)}, /* a000 Series */ - {IWL_PCI_DEVICE(0x2720, 0x0A10, iwla000_2ac_cfg_hr)}, + {IWL_PCI_DEVICE(0x2720, 0x0A10, iwla000_2ac_cfg_hr_cdb)}, + {IWL_PCI_DEVICE(0x2722, 0x0A10, iwla000_2ac_cfg_hr)}, #endif /* CONFIG_IWLMVM */ {0} @@ -672,8 +673,8 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } if (iwl_trans->cfg->rf_id && - (cfg == &iwla000_2ac_cfg_hr && - iwl_trans->hw_rf_id == CSR_HW_RF_ID_TYPE_JF)) { + (cfg == &iwla000_2ac_cfg_hr || cfg == &iwla000_2ac_cfg_hr_cdb) && + iwl_trans->hw_rf_id == CSR_HW_RF_ID_TYPE_JF) { cfg = &iwla000_2ac_cfg_jf; iwl_trans->cfg = cfg; } -- cgit From 528709b081dbf2bc86de072280a11634918c1e53 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Mon, 13 Feb 2017 09:48:17 +0200 Subject: iwlwifi: pcie: remove RSA race workaround This workaround is not needed anymore. Remove it. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 46 ------------------------- 1 file changed, 46 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index ed1034e58cb4..91f6030529b3 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -720,47 +720,6 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, return ret; } -/* - * Driver Takes the ownership on secure machine before FW load - * and prevent race with the BT load. - * W/A for ROM bug. (should be remove in the next Si step) - */ -static int iwl_pcie_rsa_race_bug_wa(struct iwl_trans *trans) -{ - u32 val, loop = 1000; - - /* - * Check the RSA semaphore is accessible. - * If the HW isn't locked and the rsa semaphore isn't accessible, - * we are in trouble. - */ - val = iwl_read_prph(trans, PREG_AUX_BUS_WPROT_0); - if (val & (BIT(1) | BIT(17))) { - IWL_DEBUG_INFO(trans, - "can't access the RSA semaphore it is write protected\n"); - return 0; - } - - /* take ownership on the AUX IF */ - iwl_write_prph(trans, WFPM_CTRL_REG, WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK); - iwl_write_prph(trans, AUX_MISC_MASTER1_EN, AUX_MISC_MASTER1_EN_SBE_MSK); - - do { - iwl_write_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS, 0x1); - val = iwl_read_prph(trans, AUX_MISC_MASTER1_SMPHR_STATUS); - if (val == 0x1) { - iwl_write_prph(trans, RSA_ENABLE, 0); - return 0; - } - - udelay(10); - loop--; - } while (loop > 0); - - IWL_ERR(trans, "Failed to take ownership on secure machine\n"); - return -EIO; -} - static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans, const struct fw_img *image, int cpu, @@ -1010,11 +969,6 @@ static int iwl_pcie_load_given_ucode_8000(struct iwl_trans *trans, if (trans->dbg_dest_tlv) iwl_pcie_apply_destination(trans); - /* TODO: remove in the next Si step */ - ret = iwl_pcie_rsa_race_bug_wa(trans); - if (ret) - return ret; - IWL_DEBUG_POWER(trans, "Original WFPM value = 0x%08X\n", iwl_read_prph(trans, WFPM_GP2)); -- cgit From a6bff3cb19b7d57e297a11d844ffb71be9fd9d80 Mon Sep 17 00:00:00 2001 From: Haim Dreyfuss Date: Thu, 19 Jan 2017 12:00:46 +0200 Subject: iwlwifi: mvm: add GEO_TX_POWER_LIMIT cmd for geographic tx power table To utilize the maximum allowed tx power, an additional table was added to the BIOS. The table consists of up to seven different regions (currently only three are in use). Each region contains per band: 1. Maximum allowed tx power on the band. 2. Tx power offset for chain A. 3. Tx power offset for chain B. On init flow driver reads this table by means of ACPI and passes it to the firmware with GEO_TX_POWER_LIMIT cmd. The firmware will use this table to enhance tx power with the offset in the relevant table as well as verifying it does not violate the maximum allowed tx power. Signed-off-by: Haim Dreyfuss Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/mvm/fw-api-power.h | 43 +++++++- drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 112 +++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 5 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + 5 files changed, 160 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h index 3fa43d1348a2..750510aff70b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-power.h @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright(c) 2015 - 2017 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 @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -351,6 +351,45 @@ struct iwl_dev_tx_power_cmd { u8 reserved[3]; } __packed; /* TX_REDUCED_POWER_API_S_VER_4 */ +#define IWL_NUM_GEO_PROFILES 3 + +/** + * enum iwl_geo_per_chain_offset_operation - type of operation + * @IWL_PER_CHAIN_OFFSET_SET_TABLES: send the tables from the host to the FW. + * @IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE: retrieve the last configured table. + */ +enum iwl_geo_per_chain_offset_operation { + IWL_PER_CHAIN_OFFSET_SET_TABLES, + IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE, +}; /* GEO_TX_POWER_LIMIT FLAGS TYPE */ + +/** + * struct iwl_per_chain_offset - embedded struct for GEO_TX_POWER_LIMIT. + * @max_tx_power: maximum allowed tx power. + * @chain_a: tx power offset for chain a. + * @chain_b: tx power offset for chain b. + */ +struct iwl_per_chain_offset { + __le16 max_tx_power; + u8 chain_a; + u8 chain_b; +} __packed; /* PER_CHAIN_LIMIT_OFFSET_PER_CHAIN_S_VER_1 */ + +struct iwl_per_chain_offset_group { + struct iwl_per_chain_offset lb; + struct iwl_per_chain_offset hb; +} __packed; /* PER_CHAIN_LIMIT_OFFSET_GROUP_S_VER_1 */ + +/** + * struct iwl_geo_tx_power_profile_cmd - struct for GEO_TX_POWER_LIMIT cmd. + * @ops: operations, value from &enum iwl_geo_per_chain_offset_operation + * @table: offset profile per band. + */ +struct iwl_geo_tx_power_profiles_cmd { + __le32 ops; + struct iwl_per_chain_offset_group table[IWL_NUM_GEO_PROFILES]; +} __packed; /* GEO_TX_POWER_LIMIT */ + /** * struct iwl_beacon_filter_cmd * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 56f50d8a7b69..5086dc00346c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -320,6 +320,7 @@ enum iwl_phy_ops_subcmd_ids { CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0, CTDP_CONFIG_CMD = 0x03, TEMP_REPORTING_THRESHOLDS_CMD = 0x04, + GEO_TX_POWER_LIMIT = 0x05, CT_KILL_NOTIFICATION = 0xFE, DTS_MEASUREMENT_NOTIF_WIDE = 0xFF, }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index a845233d14a3..bce3cf5fab67 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -998,10 +998,14 @@ static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) #ifdef CONFIG_ACPI #define ACPI_WRDS_METHOD "WRDS" #define ACPI_EWRD_METHOD "EWRD" +#define ACPI_WGDS_METHOD "WGDS" #define ACPI_WIFI_DOMAIN (0x07) #define ACPI_WRDS_WIFI_DATA_SIZE (IWL_MVM_SAR_TABLE_SIZE + 2) #define ACPI_EWRD_WIFI_DATA_SIZE ((IWL_MVM_SAR_PROFILE_NUM - 1) * \ IWL_MVM_SAR_TABLE_SIZE + 3) +#define ACPI_WGDS_WIFI_DATA_SIZE 18 +#define ACPI_WGDS_NUM_BANDS 2 +#define ACPI_WGDS_TABLE_SIZE 3 static int iwl_mvm_sar_set_profile(struct iwl_mvm *mvm, union acpi_object *table, @@ -1203,6 +1207,61 @@ out_free: return ret; } +static int iwl_mvm_sar_get_wgds_table(struct iwl_mvm *mvm, + struct iwl_mvm_geo_table *geo_table) +{ + union acpi_object *wifi_pkg; + acpi_handle root_handle; + acpi_handle handle; + struct acpi_buffer wgds = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + int i, ret; + + root_handle = ACPI_HANDLE(mvm->dev); + if (!root_handle) { + IWL_DEBUG_RADIO(mvm, + "Could not retrieve root port ACPI handle\n"); + return -ENOENT; + } + + /* Get the method's handle */ + status = acpi_get_handle(root_handle, (acpi_string)ACPI_WGDS_METHOD, + &handle); + if (ACPI_FAILURE(status)) { + IWL_DEBUG_RADIO(mvm, "WGDS method not found\n"); + return -ENOENT; + } + + /* Call WGDS with no arguments */ + status = acpi_evaluate_object(handle, NULL, NULL, &wgds); + if (ACPI_FAILURE(status)) { + IWL_DEBUG_RADIO(mvm, "WGDS invocation failed (0x%x)\n", status); + return -ENOENT; + } + + wifi_pkg = iwl_mvm_sar_find_wifi_pkg(mvm, wgds.pointer, + ACPI_WGDS_WIFI_DATA_SIZE); + if (IS_ERR(wifi_pkg)) { + ret = PTR_ERR(wifi_pkg); + goto out_free; + } + + for (i = 0; i < ACPI_WGDS_WIFI_DATA_SIZE; i++) { + union acpi_object *entry; + + entry = &wifi_pkg->package.elements[i + 1]; + if ((entry->type != ACPI_TYPE_INTEGER) || + (entry->integer.value > U8_MAX)) + return -EINVAL; + + geo_table->values[i] = entry->integer.value; + } + ret = 0; +out_free: + kfree(wgds.pointer); + return ret; +} + int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) { struct iwl_dev_tx_power_cmd cmd = { @@ -1256,6 +1315,50 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); } +static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) +{ + struct iwl_mvm_geo_table geo_table; + struct iwl_geo_tx_power_profiles_cmd cmd = { + .ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES), + }; + int ret, i, j, idx; + u16 cmd_wide_id = WIDE_ID(PHY_OPS_GROUP, GEO_TX_POWER_LIMIT); + + ret = iwl_mvm_sar_get_wgds_table(mvm, &geo_table); + if (ret < 0) { + IWL_DEBUG_RADIO(mvm, + "Geo SAR BIOS table invalid or unavailable. (%d)\n", + ret); + /* we don't fail if the table is not available */ + return 0; + } + + IWL_DEBUG_RADIO(mvm, "Sending GEO_TX_POWER_LIMIT\n"); + + BUILD_BUG_ON(IWL_NUM_GEO_PROFILES * ACPI_WGDS_NUM_BANDS * + ACPI_WGDS_TABLE_SIZE != ACPI_WGDS_WIFI_DATA_SIZE); + + for (i = 0; i < IWL_NUM_GEO_PROFILES; i++) { + struct iwl_per_chain_offset *chain = + (struct iwl_per_chain_offset *)&cmd.table[i]; + + for (j = 0; j < ACPI_WGDS_NUM_BANDS; j++) { + u8 *value; + + idx = i * ACPI_WGDS_NUM_BANDS * ACPI_WGDS_TABLE_SIZE + + j * ACPI_WGDS_TABLE_SIZE; + value = &geo_table.values[idx]; + chain[j].max_tx_power = cpu_to_le16(value[0]); + chain[j].chain_a = value[1]; + chain[j].chain_b = value[2]; + IWL_DEBUG_RADIO(mvm, + "SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n", + i, j, value[1], value[2], value[0]); + } + } + return iwl_mvm_send_cmd_pdu(mvm, cmd_wide_id, 0, sizeof(cmd), &cmd); +} + #else /* CONFIG_ACPI */ static int iwl_mvm_sar_get_wrds_table(struct iwl_mvm *mvm) { @@ -1266,6 +1369,11 @@ static int iwl_mvm_sar_get_ewrd_table(struct iwl_mvm *mvm) { return -ENOENT; } + +static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) +{ + return 0; +} #endif /* CONFIG_ACPI */ static int iwl_mvm_sar_init(struct iwl_mvm *mvm) @@ -1488,6 +1596,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) goto error; + ret = iwl_mvm_sar_geo_init(mvm); + if (ret) + goto error; + IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; error: diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 14b8c2eba7fc..1938dfb44152 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -720,11 +720,16 @@ enum iwl_mvm_queue_status { #ifdef CONFIG_ACPI #define IWL_MVM_SAR_TABLE_SIZE 10 #define IWL_MVM_SAR_PROFILE_NUM 4 +#define IWL_MVM_GEO_TABLE_SIZE 18 struct iwl_mvm_sar_profile { bool enabled; u8 table[IWL_MVM_SAR_TABLE_SIZE]; }; + +struct iwl_mvm_geo_table { + u8 values[IWL_MVM_GEO_TABLE_SIZE]; +}; #endif struct iwl_mvm { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index d313941ffc9d..488d3c948fe8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -446,6 +446,7 @@ static const struct iwl_hcmd_names iwl_mvm_phy_names[] = { HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER_WIDE), HCMD_NAME(CTDP_CONFIG_CMD), HCMD_NAME(TEMP_REPORTING_THRESHOLDS_CMD), + HCMD_NAME(GEO_TX_POWER_LIMIT), HCMD_NAME(CT_KILL_NOTIFICATION), HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE), }; -- cgit From 4399caaa70bb30f538fc0c3b08a7a8f128ca5f12 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 11 Dec 2016 10:32:42 +0200 Subject: iwlwifi: mvm: support init extended command When we load firmware in extended mode (as we do by default for now) driver should send a command what kind of commands ucode should stop and wait for before proceeding with phy calibrations. Support this command. Currently we only do NVM access - so mark this bit only. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 23 +++++++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 15 +++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + 3 files changed, 39 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 5086dc00346c..8302cf03ac28 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -327,6 +327,7 @@ enum iwl_phy_ops_subcmd_ids { enum iwl_system_subcmd_ids { SHARED_MEM_CFG_CMD = 0x0, + INIT_EXTENDED_CFG_CMD = 0x03, }; enum iwl_data_path_subcmd_ids { @@ -2236,4 +2237,26 @@ struct iwl_nvm_access_complete_cmd { __le32 reserved; } __packed; /* NVM_ACCESS_COMPLETE_CMD_API_S_VER_1 */ +/** + * enum iwl_extended_cfg_flag - commands driver may send before + * finishing init flow + * @IWL_INIT_DEBUG_CFG: driver is going to send debug config command + * @IWL_INIT_NVM: driver is going to send NVM_ACCESS commands + * @IWL_INIT_PHY: driver is going to send PHY_DB commands + */ +enum iwl_extended_cfg_flags { + IWL_INIT_DEBUG_CFG, + IWL_INIT_NVM, + IWL_INIT_PHY, +}; + +/** + * struct iwl_extended_cfg_cmd - mark what commands ucode should wait for + * before finishing init flows + * @init_flags: values from iwl_extended_cfg_flags + */ +struct iwl_init_extended_cfg_cmd { + __le32 init_flags; +} __packed; /* INIT_EXTENDED_CFG_CMD_API_S_VER_1 */ + #endif /* __fw_api_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index bce3cf5fab67..900f1e25b9da 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -836,6 +836,9 @@ int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) { struct iwl_notification_wait init_wait; struct iwl_nvm_access_complete_cmd nvm_complete = {}; + struct iwl_init_extended_cfg_cmd init_cfg = { + .init_flags = cpu_to_le32(BIT(IWL_INIT_NVM)), + }; static const u16 init_complete[] = { INIT_COMPLETE_NOTIF, }; @@ -857,6 +860,18 @@ int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) goto error; } + /* Send init config command to mark that we are sending NVM access + * commands + */ + ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(SYSTEM_GROUP, + INIT_EXTENDED_CFG_CMD), 0, + sizeof(init_cfg), &init_cfg); + if (ret) { + IWL_ERR(mvm, "Failed to run init config command: %d\n", + ret); + goto error; + } + /* Read the NVM only at driver load time, no need to do this twice */ if (read_nvm) { /* Read nvm */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 488d3c948fe8..888053323c92 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -428,6 +428,7 @@ static const struct iwl_hcmd_names iwl_mvm_legacy_names[] = { */ static const struct iwl_hcmd_names iwl_mvm_system_names[] = { HCMD_NAME(SHARED_MEM_CFG_CMD), + HCMD_NAME(INIT_EXTENDED_CFG_CMD), }; /* Please keep this array *SORTED* by hex value. -- cgit From bc0294696456365f1e80fa8a0a6e13d076316b30 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Mon, 13 Feb 2017 13:17:11 +0200 Subject: iwlwifi: mvm: disable RX queue notification for a000 devices For a000 devices, we don't really have multi RX queue for now, until we have the RX queue configuration API. Disable RX queue notification for now. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 26cf3dfcc108..f35f295d0c81 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -4218,7 +4218,8 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); - if (!iwl_mvm_has_new_rx_api(mvm)) + /* TODO - remove a000 disablement when we have RXQ config API */ + if (!iwl_mvm_has_new_rx_api(mvm) || iwl_mvm_has_new_tx_api(mvm)) return; notif->cookie = mvm->queue_sync_cookie; -- cgit From cb2de6bb4f129683673da907b71344d59a38c55a Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Mon, 13 Feb 2017 13:36:31 +0200 Subject: iwlwifi: mvm: dump frames early on invalid rate Currently when rate isn't found (invalid rate or CCK rate in high band) driver is assigning rate -1, which causes mac80211 to dump it later with the cryptic rate value of 0xFF. Instead, warn early and dump the frame in mvm. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 13 ++++++++++--- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 18 +++++++++++++----- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index eab6e2ad62e1..d4c0ca7ccb34 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -460,9 +460,16 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, if (rate_n_flags & RATE_MCS_BF_MSK) rx_status->vht_flag |= RX_VHT_FLAG_BF; } else { - rx_status->rate_idx = - iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, - rx_status->band); + int rate = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, + rx_status->band); + + if (WARN(rate < 0 || rate > 0xFF, + "Invalid rate flags 0x%x, band %d,\n", + rate_n_flags, rx_status->band)) { + kfree_skb(skb); + return; + } + rx_status->rate_idx = rate; } #ifdef CONFIG_IWLWIFI_DEBUGFS diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 8601d25407b3..0f74a200e812 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -918,8 +918,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, if (iwl_mvm_is_nonagg_dup(sta, queue, rx_status, hdr, desc)) { kfree_skb(skb); - rcu_read_unlock(); - return; + goto out; } /* @@ -985,9 +984,17 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, if (rate_n_flags & RATE_MCS_BF_MSK) rx_status->vht_flag |= RX_VHT_FLAG_BF; } else { - rx_status->rate_idx = - iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, - rx_status->band); + int rate = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, + rx_status->band); + + if (WARN(rate < 0 || rate > 0xFF, + "Invalid rate flags 0x%x, band %d,\n", + rate_n_flags, rx_status->band)) { + kfree_skb(skb); + goto out; + } + rx_status->rate_idx = rate; + } /* management stuff on default queue */ @@ -1006,6 +1013,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, iwl_mvm_create_skb(skb, hdr, len, crypt_len, rxb); if (!iwl_mvm_reorder(mvm, napi, queue, sta, skb, desc)) iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta); +out: rcu_read_unlock(); } -- cgit From 2220fb2960b72915e7fd9da640a4695dceff238c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 13 Feb 2017 11:29:16 +0200 Subject: iwlwifi: split the handler and the wake parts of the notification infra The notification infrastructure (iwl_notification_wait_* functions) allows to wait until a list of notifications will come up from the firmware and to run a special handler (notif_wait handler) when those are received. The operation mode notifies the notification infrastructure about any Rx being received by the mean of iwl_notification_wait_notify() which will do two things: 1) call the notif_wait handler 2) wakeup the thread that was waiting for the notification Typically, only after those two steps happened, the operation mode will run its own handler for the notification that was received from the firmware. This means that the thread that was waiting for that notification can be running before the operation mode's handler was called. When the operation mode's handler is ASYNC, things get even worse since the thread that was waiting for the notification isn't even guaranteed that the ASYNC callback was added to async_handlers_list before it starts to run. This means that even calling iwl_mvm_wait_for_async_handlers() can't guarantee that absolutely everything related to that notification has run. The following can happen: Thread sending the command Operation mode's Rx path -------------------------- ------------------------ iwl_init_notification_wait() iwl_mvm_send_cmd() iwl_mvm_rx_common() iwl_notification_wait_notify() iwl_mvm_wait_for_async_handlers() // Possibly free some data // structure list_add_tail(async_handlers_list); schedule_work(async_handlers_wk); // Access the freed structure Split the 'run notif_wait's handler' and the 'wake up the thread' parts to fix this. This allows the operation mode to do the following: Thread sending the command Operation mode's Rx path -------------------------- ------------------------ iwl_init_notification_wait() iwl_mvm_send_cmd() iwl_mvm_rx_common() iwl_notification_wait() // Will run the notif_wait's handler list_add_tail(async_handlers_list); schedule_work(async_handlers_wk); iwl_notification_notify() iwl_mvm_wait_for_async_handlers() This way, the waiter is guaranteed that all the handlers have been run (if SYNC), or at least enqueued (if ASYNC) by the time it wakes up. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/iwl-notif-wait.c | 10 ++++----- .../net/wireless/intel/iwlwifi/iwl-notif-wait.h | 25 +++++++++++++++++----- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c index 88f260db3744..68412ff2112e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.c @@ -76,8 +76,8 @@ void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait) } IWL_EXPORT_SYMBOL(iwl_notification_wait_init); -void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt) +bool iwl_notification_wait(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt) { bool triggered = false; @@ -118,13 +118,11 @@ void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait, } } spin_unlock(¬if_wait->notif_wait_lock); - } - if (triggered) - wake_up_all(¬if_wait->notif_waitq); + return triggered; } -IWL_EXPORT_SYMBOL(iwl_notification_wait_notify); +IWL_EXPORT_SYMBOL(iwl_notification_wait); void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait) { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h index 0f9995ed71cd..368884be4e7c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-notif-wait.h @@ -6,7 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2015 - 2017 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 @@ -32,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2015 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -89,10 +90,10 @@ struct iwl_notif_wait_data { * * This structure is not used directly, to wait for a * notification declare it on the stack, and call - * iwlagn_init_notification_wait() with appropriate + * iwl_init_notification_wait() with appropriate * parameters. Then do whatever will cause the ucode * to notify the driver, and to wait for that then - * call iwlagn_wait_notification(). + * call iwl_wait_notification(). * * Each notification is one-shot. If at some point we * need to support multi-shot notifications (which @@ -114,10 +115,24 @@ struct iwl_notification_wait { /* caller functions */ void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_data); -void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_data, - struct iwl_rx_packet *pkt); +bool iwl_notification_wait(struct iwl_notif_wait_data *notif_data, + struct iwl_rx_packet *pkt); void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_data); +static inline void +iwl_notification_notify(struct iwl_notif_wait_data *notif_data) +{ + wake_up_all(¬if_data->notif_waitq); +} + +static inline void +iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_data, + struct iwl_rx_packet *pkt) +{ + if (iwl_notification_wait(notif_data, pkt)) + iwl_notification_notify(notif_data); +} + /* user functions */ void __acquires(wait_entry) iwl_init_notification_wait(struct iwl_notif_wait_data *notif_data, -- cgit From 9dfa21517a75d6c06d9e9a92814101eaebba168b Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 14 Feb 2017 14:58:21 +0200 Subject: iwlwifi: mvm: flip address 4 of AMSDU frames Address 4 is reversed as well. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 0f74a200e812..24c4fbe139a3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -925,7 +925,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, * Our hardware de-aggregates AMSDUs but copies the mac header * as it to the de-aggregated MPDUs. We need to turn off the * AMSDU bit in the QoS control ourselves. - * In addition, HW reverses addr3 - reverse it back. + * In addition, HW reverses addr3 and addr4 - reverse it back. */ if ((desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU) && !WARN_ON(!ieee80211_is_data_qos(hdr->frame_control))) { @@ -938,6 +938,13 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, for (i = 0; i < ETH_ALEN; i++) mac_addr[i] = hdr->addr3[ETH_ALEN - i - 1]; ether_addr_copy(hdr->addr3, mac_addr); + + if (ieee80211_has_a4(hdr->frame_control)) { + for (i = 0; i < ETH_ALEN; i++) + mac_addr[i] = + hdr->addr4[ETH_ALEN - i - 1]; + ether_addr_copy(hdr->addr4, mac_addr); + } } if (baid != IWL_RX_REORDER_DATA_INVALID_BAID) { u32 reorder_data = le32_to_cpu(desc->reorder_data); -- cgit From 730a18912bcbde0b94ae7f1b554a9908b3424a22 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Tue, 7 Feb 2017 18:37:40 +0200 Subject: iwlwifi: mvm: support changing band for phy context In a000 CDB firmware, we cannot update phy context to a different band - we must first remove it and add it again. Support this flow for all a000 devices since we may have various combinations that cause us to fail regardless if CDB is active. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 8302cf03ac28..f545c5f9e4e3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -690,7 +690,7 @@ struct iwl_error_resp { (_color << FW_CTXT_COLOR_POS)) /* Possible actions on PHYs, MACs and Bindings */ -enum { +enum iwl_phy_ctxt_action { FW_CTXT_ACTION_STUB = 0, FW_CTXT_ACTION_ADD, FW_CTXT_ACTION_MODIFY, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c index 95138830b9f8..d59efe804356 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-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) 2017 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 @@ -250,12 +251,30 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic) { + enum iwl_phy_ctxt_action action = FW_CTXT_ACTION_MODIFY; + lockdep_assert_held(&mvm->mutex); + /* In CDB mode we cannot modify PHY context between bands so... */ + if (iwl_mvm_has_new_tx_api(mvm) && + ctxt->channel->band != chandef->chan->band) { + int ret; + + /* ... remove it here ...*/ + ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, + chains_static, chains_dynamic, + FW_CTXT_ACTION_REMOVE, 0); + if (ret) + return ret; + + /* ... and proceed to add it again */ + action = FW_CTXT_ACTION_ADD; + } + ctxt->channel = chandef->chan; return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, chains_static, chains_dynamic, - FW_CTXT_ACTION_MODIFY, 0); + action, 0); } void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) -- cgit From f4d1047914ea05e0f8393944da18f6ee5dad24c4 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Sun, 19 Feb 2017 10:42:40 +0200 Subject: iwlwifi: a000: fix memory offsets and lengths Memory offsets and lengths for A000 HW is different than currently specified. Fixes: e34d975e40ff ("iwlwifi: Add a000 HW family support") Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-a000.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c index 1c731b88d08c..097cb45c8ad9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c @@ -65,12 +65,12 @@ #define IWL_A000_TX_POWER_VERSION 0xffff /* meaningless */ /* Memory offsets and lengths */ -#define IWL_A000_DCCM_OFFSET 0x800000 -#define IWL_A000_DCCM_LEN 0x18000 +#define IWL_A000_DCCM_OFFSET 0x800000 /* LMAC1 */ +#define IWL_A000_DCCM_LEN 0x10000 /* LMAC1 */ #define IWL_A000_DCCM2_OFFSET 0x880000 #define IWL_A000_DCCM2_LEN 0x8000 #define IWL_A000_SMEM_OFFSET 0x400000 -#define IWL_A000_SMEM_LEN 0x68000 +#define IWL_A000_SMEM_LEN 0xD0000 #define IWL_A000_JF_FW_PRE "iwlwifi-Qu-a0-jf-b0-" #define IWL_A000_HR_FW_PRE "iwlwifi-Qu-a0-hr-a0-" -- cgit From 4a0a3abfd951943f770f5306d32f8640f55568c4 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Thu, 6 Apr 2017 08:46:28 +0200 Subject: i40evf: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct i40evf_adapter, use stats from struct net_device. Also remove the now unnecessary .ndo_get_stats function. Signed-off-by: Tobias Klauser Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf.h | 1 - drivers/net/ethernet/intel/i40evf/i40evf_main.c | 16 ---------------- .../net/ethernet/intel/i40evf/i40evf_virtchnl.c | 22 +++++++++++----------- 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 35ded19e9cc2..7bbd11abed08 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -252,7 +252,6 @@ struct i40evf_adapter { /* OS defined structs */ struct net_device *netdev; struct pci_dev *pdev; - struct net_device_stats net_stats; struct i40e_hw hw; /* defined in i40e_type.h */ diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 12a930e879af..671913c74bf8 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -2242,21 +2242,6 @@ static int i40evf_close(struct net_device *netdev) return 0; } -/** - * i40evf_get_stats - Get System Network Statistics - * @netdev: network interface device structure - * - * Returns the address of the device statistics structure. - * The statistics are actually updated from the timer callback. - **/ -static struct net_device_stats *i40evf_get_stats(struct net_device *netdev) -{ - struct i40evf_adapter *adapter = netdev_priv(netdev); - - /* only return the current stats */ - return &adapter->net_stats; -} - /** * i40evf_change_mtu - Change the Maximum Transfer Unit * @netdev: network interface device structure @@ -2363,7 +2348,6 @@ static const struct net_device_ops i40evf_netdev_ops = { .ndo_open = i40evf_open, .ndo_stop = i40evf_close, .ndo_start_xmit = i40evf_xmit_frame, - .ndo_get_stats = i40evf_get_stats, .ndo_set_rx_mode = i40evf_set_rx_mode, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = i40evf_set_mac, diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 3bccfbb1db14..deb2cb8dac6b 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -960,17 +960,17 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, case I40E_VIRTCHNL_OP_GET_STATS: { struct i40e_eth_stats *stats = (struct i40e_eth_stats *)msg; - adapter->net_stats.rx_packets = stats->rx_unicast + - stats->rx_multicast + - stats->rx_broadcast; - adapter->net_stats.tx_packets = stats->tx_unicast + - stats->tx_multicast + - stats->tx_broadcast; - adapter->net_stats.rx_bytes = stats->rx_bytes; - adapter->net_stats.tx_bytes = stats->tx_bytes; - adapter->net_stats.tx_errors = stats->tx_errors; - adapter->net_stats.rx_dropped = stats->rx_discards; - adapter->net_stats.tx_dropped = stats->tx_discards; + netdev->stats.rx_packets = stats->rx_unicast + + stats->rx_multicast + + stats->rx_broadcast; + netdev->stats.tx_packets = stats->tx_unicast + + stats->tx_multicast + + stats->tx_broadcast; + netdev->stats.rx_bytes = stats->rx_bytes; + netdev->stats.tx_bytes = stats->tx_bytes; + netdev->stats.tx_errors = stats->tx_errors; + netdev->stats.rx_dropped = stats->rx_discards; + netdev->stats.tx_dropped = stats->tx_discards; adapter->current_stats = *stats; } break; -- cgit From 53240e99dbbfe69a6b3ca808a6d15eea744be169 Mon Sep 17 00:00:00 2001 From: alice michael Date: Thu, 6 Apr 2017 05:59:34 -0400 Subject: i40e/i40evf: Remove VF Rx csum offload for tunneled packets Rx checksum offload for tunneled packets was never being negotiated or requested by VF. This capability was assumed by default and enabled in current hardware for VF. Going forward, this feature needs to be disabled or advanced ptypes should be negotiated with PF in the future. Change-ID: I9e54cfa8a90e03ab6956db4412f1e337ccd2c2e0 Signed-off-by: Preethi Banala Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 460171edc412..fc4b14af370d 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -831,13 +831,6 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT)) return; - /* If there is an outer header present that might contain a checksum - * we need to bump the checksum level by 1 to reflect the fact that - * we are indicating we validated the inner checksum. - */ - if (decoded.tunnel_type >= I40E_RX_PTYPE_TUNNEL_IP_GRENAT) - skb->csum_level = 1; - /* Only report checksum unnecessary for TCP, UDP, or SCTP */ switch (decoded.inner_prot) { case I40E_RX_PTYPE_INNER_PROT_TCP: -- cgit From 0e626ff7ccbfc43c6cc4aeea611c40b899682382 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 10 Apr 2017 05:18:43 -0400 Subject: i40e: Fix support for flow director programming status This patch fixes an issue I introduced when I converted the code over to using the length field to determine if a descriptor was done or not. It turns out that we are also processing programming descriptors in the Rx path and need to have these processed even though the length field will be 0 on these packets. What will happen with a programming descriptor is that we will receive a descriptor that has the SPH bit set, and the header length and packet length fields cleared. To account for this we should be checking for the bit for split header being set even though we aren't actually using header split. This bit is set in the length field to indicate if a programming descriptor response is contained in the descriptor. Since we don't support header split we don't need to perform the extra checks of using a fixed value for the entire length field. In addition I am moving the function for checking if a filter is a programming status filter into the i40e_txrx.c file since there is no longer support for FCoE it doesn't make sense to keep this file in i40e.h. Change-ID: I12c359c3dc70adb9d6b92b27324bb2c7f04c1a06 Signed-off-by: Alexander Duyck Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 15 -------- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 50 ++++++++++++++++++++------- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 9 ++--- 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index e987503f8517..b629f3adcecb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -762,21 +762,6 @@ static inline void i40e_vsi_setup_irqhandler(struct i40e_vsi *vsi, vsi->irq_handler = irq_handler; } -/** - * i40e_rx_is_programming_status - check for programming status descriptor - * @qw: the first quad word of the program status descriptor - * - * The value of in the descriptor length field indicate if this - * is a programming status descriptor for flow director or FCoE - * by the value of I40E_RX_PROG_STATUS_DESC_LENGTH, otherwise - * it is a packet descriptor. - **/ -static inline bool i40e_rx_is_programming_status(u64 qw) -{ - return I40E_RX_PROG_STATUS_DESC_LENGTH == - (qw >> I40E_RX_PROG_STATUS_DESC_LENGTH_SHIFT); -} - /** * i40e_get_fd_cnt_all - get the total FD filter space available * @pf: pointer to the PF struct diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 20691d2bf113..843d7ffe1784 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1037,10 +1037,30 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) return false; } +/** + * i40e_rx_is_programming_status - check for programming status descriptor + * @qw: qword representing status_error_len in CPU ordering + * + * The value of in the descriptor length field indicate if this + * is a programming status descriptor for flow director or FCoE + * by the value of I40E_RX_PROG_STATUS_DESC_LENGTH, otherwise + * it is a packet descriptor. + **/ +static inline bool i40e_rx_is_programming_status(u64 qw) +{ + /* The Rx filter programming status and SPH bit occupy the same + * spot in the descriptor. Since we don't support packet split we + * can just reuse the bit as an indication that this is a + * programming status descriptor. + */ + return qw & I40E_RXD_QW1_LENGTH_SPH_MASK; +} + /** * i40e_clean_programming_status - clean the programming status descriptor * @rx_ring: the rx ring that has this descriptor * @rx_desc: the rx descriptor written back by HW + * @qw: qword representing status_error_len in CPU ordering * * Flow director should handle FD_FILTER_STATUS to check its filter programming * status being successful or not and take actions accordingly. FCoE should @@ -1048,12 +1068,18 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) * **/ static void i40e_clean_programming_status(struct i40e_ring *rx_ring, - union i40e_rx_desc *rx_desc) + union i40e_rx_desc *rx_desc, + u64 qw) { - u64 qw; + u32 ntc = rx_ring->next_to_clean + 1; u8 id; - qw = le64_to_cpu(rx_desc->wb.qword1.status_error_len); + /* fetch, update, and store next to clean */ + ntc = (ntc < rx_ring->count) ? ntc : 0; + rx_ring->next_to_clean = ntc; + + prefetch(I40E_RX_DESC(rx_ring, ntc)); + id = (qw & I40E_RX_PROG_STATUS_DESC_QW1_PROGID_MASK) >> I40E_RX_PROG_STATUS_DESC_QW1_PROGID_SHIFT; @@ -1911,11 +1937,6 @@ static bool i40e_is_non_eop(struct i40e_ring *rx_ring, prefetch(I40E_RX_DESC(rx_ring, ntc)); -#define staterrlen rx_desc->wb.qword1.status_error_len - if (unlikely(i40e_rx_is_programming_status(le64_to_cpu(staterrlen)))) { - i40e_clean_programming_status(rx_ring, rx_desc); - return true; - } /* if we are the last buffer then there is nothing else to do */ #define I40E_RXD_EOF BIT(I40E_RX_DESC_STATUS_EOF_SHIFT) if (likely(i40e_test_staterr(rx_desc, I40E_RXD_EOF))) @@ -1968,10 +1989,6 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) * hardware wrote DD then the length will be non-zero */ qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); - size = (qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> - I40E_RXD_QW1_LENGTH_PBUF_SHIFT; - if (!size) - break; /* This memory barrier is needed to keep us from reading * any other fields out of the rx_desc until we have @@ -1979,6 +1996,15 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) */ dma_rmb(); + if (unlikely(i40e_rx_is_programming_status(qword))) { + i40e_clean_programming_status(rx_ring, rx_desc, qword); + continue; + } + size = (qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> + I40E_RXD_QW1_LENGTH_PBUF_SHIFT; + if (!size) + break; + rx_buffer = i40e_get_rx_buffer(rx_ring, size); /* retrieve a buffer from the ring */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index fc4b14af370d..80931e3f9445 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -1317,10 +1317,6 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) * hardware wrote DD then the length will be non-zero */ qword = le64_to_cpu(rx_desc->wb.qword1.status_error_len); - size = (qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> - I40E_RXD_QW1_LENGTH_PBUF_SHIFT; - if (!size) - break; /* This memory barrier is needed to keep us from reading * any other fields out of the rx_desc until we have @@ -1328,6 +1324,11 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) */ dma_rmb(); + size = (qword & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> + I40E_RXD_QW1_LENGTH_PBUF_SHIFT; + if (!size) + break; + rx_buffer = i40e_get_rx_buffer(rx_ring, size); /* retrieve a buffer from the ring */ -- cgit From 3118025a070f3346a3f23cbb8e9039ff567a6c46 Mon Sep 17 00:00:00 2001 From: Mitch Williams Date: Wed, 12 Apr 2017 07:16:52 -0400 Subject: i40e: dump VF information in debugfs Dump some internal state about VFs through debugfs. This provides information not available with 'ip link show'. To use, write "dump vf " to the command file, or just "dump vf" to dump information on all of the VFs. Change-ID: Ibe32b7f4ae55d4358c0b903217475f708ada1ecd Signed-off-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_debugfs.c | 51 ++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index c5f68cc1edcd..a3d7ec62b76c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -384,6 +384,8 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) " base_queue = %d, num_queue_pairs = %d, num_desc = %d\n", vsi->base_queue, vsi->num_queue_pairs, vsi->num_desc); dev_info(&pf->pdev->dev, " type = %i\n", vsi->type); + if (vsi->type == I40E_VSI_SRIOV) + dev_info(&pf->pdev->dev, " VF ID = %i\n", vsi->vf_id); dev_info(&pf->pdev->dev, " info: valid_sections = 0x%04x, switch_id = 0x%04x\n", vsi->info.valid_sections, vsi->info.switch_id); @@ -694,6 +696,47 @@ static void i40e_dbg_dump_veb_all(struct i40e_pf *pf) } } +/** + * i40e_dbg_dump_vf - dump VF info + * @pf: the i40e_pf created in command write + * @vf_id: the vf_id from the user + **/ +static void i40e_dbg_dump_vf(struct i40e_pf *pf, int vf_id) +{ + struct i40e_vf *vf; + struct i40e_vsi *vsi; + + if (!pf->num_alloc_vfs) { + dev_info(&pf->pdev->dev, "no VFs allocated\n"); + } else if ((vf_id >= 0) && (vf_id < pf->num_alloc_vfs)) { + vf = &pf->vf[vf_id]; + vsi = pf->vsi[vf->lan_vsi_idx]; + dev_info(&pf->pdev->dev, "vf %2d: VSI id=%d, seid=%d, qps=%d\n", + vf_id, vf->lan_vsi_id, vsi->seid, vf->num_queue_pairs); + dev_info(&pf->pdev->dev, " num MDD=%lld, invalid msg=%lld, valid msg=%lld\n", + vf->num_mdd_events, + vf->num_invalid_msgs, + vf->num_valid_msgs); + } else { + dev_info(&pf->pdev->dev, "invalid VF id %d\n", vf_id); + } +} + +/** + * i40e_dbg_dump_vf_all - dump VF info for all VFs + * @pf: the i40e_pf created in command write + **/ +static void i40e_dbg_dump_vf_all(struct i40e_pf *pf) +{ + int i; + + if (!pf->num_alloc_vfs) + dev_info(&pf->pdev->dev, "no VFs enabled!\n"); + else + for (i = 0; i < pf->num_alloc_vfs; i++) + i40e_dbg_dump_vf(pf, i); +} + #define I40E_MAX_DEBUG_OUT_BUFFER (4096*4) /** * i40e_dbg_command_write - write into command datum @@ -712,6 +755,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, struct i40e_vsi *vsi; int vsi_seid; int veb_seid; + int vf_id; int cnt; /* don't allow partial writes */ @@ -914,6 +958,12 @@ static ssize_t i40e_dbg_command_write(struct file *filp, i40e_dbg_dump_veb_seid(pf, vsi_seid); else i40e_dbg_dump_veb_all(pf); + } else if (strncmp(&cmd_buf[5], "vf", 2) == 0) { + cnt = sscanf(&cmd_buf[7], "%i", &vf_id); + if (cnt > 0) + i40e_dbg_dump_vf(pf, vf_id); + else + i40e_dbg_dump_vf_all(pf); } else if (strncmp(&cmd_buf[5], "desc", 4) == 0) { int ring_id, desc_n; if (strncmp(&cmd_buf[10], "rx", 2) == 0) { @@ -1109,6 +1159,7 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, "dump vsi [seid]\n"); dev_info(&pf->pdev->dev, "dump reset stats\n"); dev_info(&pf->pdev->dev, "dump port\n"); + dev_info(&pf->pdev->dev, "dump vf [vf_id]\n"); dev_info(&pf->pdev->dev, "dump debug fwdata \n"); } -- cgit From ed0980c4401a21148d2fb9f4f6dd6132a4cc7599 Mon Sep 17 00:00:00 2001 From: Scott Peterson Date: Thu, 13 Apr 2017 04:45:44 -0400 Subject: i40e/i40evf: Add tracepoints This patch adds tracepoints to the i40e and i40evf drivers to which BPF programs can be attached for feature testing and verification. It's expected that an attached BPF program will identify and count or log some interesting subset of traffic. The bcc-tools package is helpful there for containing all the BPF arcana in a handy Python wrapper. Though you can make these tracepoints log trace messages, the messages themselves probably won't be very useful (other to verify the tracepoint is being called while you're debugging your BPF program). The idea here is that tracepoints have such low performance cost when disabled that we can leave these in the upstream drivers. This may eventually enable the instrumentation of unmodified customer systems should the need arise to verify a NIC feature is working as expected. In general this enables one set of feature verification tools to be used on these drivers whether they're built with the kernel or separately. Users are advised against using these tracepoints for anything other than a diagnostic tool. They have a performance impact when enabled, and their exact placement and form may change as we see how well they work in practice for the purposes above. Change-ID: Id6014a7322c0e6d08068114dd20bd156f2f6435e Signed-off-by: Scott Peterson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/Makefile | 3 + drivers/net/ethernet/intel/i40e/i40e_main.c | 6 + drivers/net/ethernet/intel/i40e/i40e_trace.h | 229 ++++++++++++++++++++++++ drivers/net/ethernet/intel/i40e/i40e_txrx.c | 9 + drivers/net/ethernet/intel/i40evf/Makefile | 3 + drivers/net/ethernet/intel/i40evf/i40e_trace.h | 229 ++++++++++++++++++++++++ drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 9 + drivers/net/ethernet/intel/i40evf/i40evf_main.c | 7 + 8 files changed, 495 insertions(+) create mode 100644 drivers/net/ethernet/intel/i40e/i40e_trace.h create mode 100644 drivers/net/ethernet/intel/i40evf/i40e_trace.h diff --git a/drivers/net/ethernet/intel/i40e/Makefile b/drivers/net/ethernet/intel/i40e/Makefile index 4f454d364d0d..3da482c3d68d 100644 --- a/drivers/net/ethernet/intel/i40e/Makefile +++ b/drivers/net/ethernet/intel/i40e/Makefile @@ -28,6 +28,9 @@ # Makefile for the Intel(R) Ethernet Connection XL710 (i40e.ko) driver # +ccflags-y += -I$(src) +subdir-ccflags-y += -I$(src) + obj-$(CONFIG_I40E) += i40e.o i40e-objs := i40e_main.o \ diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index b6ec9beeebff..5e625e0c73ac 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -32,6 +32,12 @@ #include "i40e.h" #include "i40e_diag.h" #include +/* All i40e tracepoints are defined by the include below, which + * must be included exactly once across the whole kernel with + * CREATE_TRACE_POINTS defined + */ +#define CREATE_TRACE_POINTS +#include "i40e_trace.h" const char i40e_driver_name[] = "i40e"; static const char i40e_driver_string[] = diff --git a/drivers/net/ethernet/intel/i40e/i40e_trace.h b/drivers/net/ethernet/intel/i40e/i40e_trace.h new file mode 100644 index 000000000000..d3e55f54a05e --- /dev/null +++ b/drivers/net/ethernet/intel/i40e/i40e_trace.h @@ -0,0 +1,229 @@ +/******************************************************************************* + * + * Intel(R) 40-10 Gigabit Ethernet Connection Network Driver + * Copyright(c) 2013 - 2017 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. + * + * 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 + * + ******************************************************************************/ + +/* Modeled on trace-events-sample.h */ + +/* The trace subsystem name for i40e will be "i40e". + * + * This file is named i40e_trace.h. + * + * Since this include file's name is different from the trace + * subsystem name, we'll have to define TRACE_INCLUDE_FILE at the end + * of this file. + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM i40e + +/* See trace-events-sample.h for a detailed description of why this + * guard clause is different from most normal include files. + */ +#if !defined(_I40E_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _I40E_TRACE_H_ + +#include + +/** + * i40e_trace() macro enables shared code to refer to trace points + * like: + * + * trace_i40e{,vf}_example(args...) + * + * ... as: + * + * i40e_trace(example, args...) + * + * ... to resolve to the PF or VF version of the tracepoint without + * ifdefs, and to allow tracepoints to be disabled entirely at build + * time. + * + * Trace point should always be referred to in the driver via this + * macro. + * + * Similarly, i40e_trace_enabled(trace_name) wraps references to + * trace_i40e{,vf}__enabled() functions. + */ +#define _I40E_TRACE_NAME(trace_name) (trace_ ## i40e ## _ ## trace_name) +#define I40E_TRACE_NAME(trace_name) _I40E_TRACE_NAME(trace_name) + +#define i40e_trace(trace_name, args...) I40E_TRACE_NAME(trace_name)(args) + +#define i40e_trace_enabled(trace_name) I40E_TRACE_NAME(trace_name##_enabled)() + +/* Events common to PF and VF. Corresponding versions will be defined + * for both, named trace_i40e_* and trace_i40evf_*. The i40e_trace() + * macro above will select the right trace point name for the driver + * being built from shared code. + */ + +/* Events related to a vsi & ring */ +DECLARE_EVENT_CLASS( + i40e_tx_template, + + TP_PROTO(struct i40e_ring *ring, + struct i40e_tx_desc *desc, + struct i40e_tx_buffer *buf), + + TP_ARGS(ring, desc, buf), + + /* The convention here is to make the first fields in the + * TP_STRUCT match the TP_PROTO exactly. This enables the use + * of the args struct generated by the tplist tool (from the + * bcc-tools package) to be used for those fields. To access + * fields other than the tracepoint args will require the + * tplist output to be adjusted. + */ + TP_STRUCT__entry( + __field(void*, ring) + __field(void*, desc) + __field(void*, buf) + __string(devname, ring->netdev->name) + ), + + TP_fast_assign( + __entry->ring = ring; + __entry->desc = desc; + __entry->buf = buf; + __assign_str(devname, ring->netdev->name); + ), + + TP_printk( + "netdev: %s ring: %p desc: %p buf %p", + __get_str(devname), __entry->ring, + __entry->desc, __entry->buf) +); + +DEFINE_EVENT( + i40e_tx_template, i40e_clean_tx_irq, + TP_PROTO(struct i40e_ring *ring, + struct i40e_tx_desc *desc, + struct i40e_tx_buffer *buf), + + TP_ARGS(ring, desc, buf)); + +DEFINE_EVENT( + i40e_tx_template, i40e_clean_tx_irq_unmap, + TP_PROTO(struct i40e_ring *ring, + struct i40e_tx_desc *desc, + struct i40e_tx_buffer *buf), + + TP_ARGS(ring, desc, buf)); + +DECLARE_EVENT_CLASS( + i40e_rx_template, + + TP_PROTO(struct i40e_ring *ring, + union i40e_32byte_rx_desc *desc, + struct sk_buff *skb), + + TP_ARGS(ring, desc, skb), + + TP_STRUCT__entry( + __field(void*, ring) + __field(void*, desc) + __field(void*, skb) + __string(devname, ring->netdev->name) + ), + + TP_fast_assign( + __entry->ring = ring; + __entry->desc = desc; + __entry->skb = skb; + __assign_str(devname, ring->netdev->name); + ), + + TP_printk( + "netdev: %s ring: %p desc: %p skb %p", + __get_str(devname), __entry->ring, + __entry->desc, __entry->skb) +); + +DEFINE_EVENT( + i40e_rx_template, i40e_clean_rx_irq, + TP_PROTO(struct i40e_ring *ring, + union i40e_32byte_rx_desc *desc, + struct sk_buff *skb), + + TP_ARGS(ring, desc, skb)); + +DEFINE_EVENT( + i40e_rx_template, i40e_clean_rx_irq_rx, + TP_PROTO(struct i40e_ring *ring, + union i40e_32byte_rx_desc *desc, + struct sk_buff *skb), + + TP_ARGS(ring, desc, skb)); + +DECLARE_EVENT_CLASS( + i40e_xmit_template, + + TP_PROTO(struct sk_buff *skb, + struct i40e_ring *ring), + + TP_ARGS(skb, ring), + + TP_STRUCT__entry( + __field(void*, skb) + __field(void*, ring) + __string(devname, ring->netdev->name) + ), + + TP_fast_assign( + __entry->skb = skb; + __entry->ring = ring; + __assign_str(devname, ring->netdev->name); + ), + + TP_printk( + "netdev: %s skb: %p ring: %p", + __get_str(devname), __entry->skb, + __entry->ring) +); + +DEFINE_EVENT( + i40e_xmit_template, i40e_xmit_frame_ring, + TP_PROTO(struct sk_buff *skb, + struct i40e_ring *ring), + + TP_ARGS(skb, ring)); + +DEFINE_EVENT( + i40e_xmit_template, i40e_xmit_frame_ring_drop, + TP_PROTO(struct sk_buff *skb, + struct i40e_ring *ring), + + TP_ARGS(skb, ring)); + +/* Events unique to the PF. */ + +#endif /* _I40E_TRACE_H_ */ +/* This must be outside ifdef _I40E_TRACE_H */ + +/* This trace include file is not located in the .../include/trace + * with the kernel tracepoint definitions, because we're a loadable + * module. + */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE i40e_trace +#include diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 843d7ffe1784..1531a0f9fcc6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -27,6 +27,7 @@ #include #include #include "i40e.h" +#include "i40e_trace.h" #include "i40e_prototype.h" static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, @@ -765,6 +766,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, /* prevent any other reads prior to eop_desc */ read_barrier_depends(); + i40e_trace(clean_tx_irq, tx_ring, tx_desc, tx_buf); /* we have caught up to head, no work left to do */ if (tx_head == tx_desc) break; @@ -791,6 +793,8 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, /* unmap remaining buffers */ while (tx_desc != eop_desc) { + i40e_trace(clean_tx_irq_unmap, + tx_ring, tx_desc, tx_buf); tx_buf++; tx_desc++; @@ -2005,6 +2009,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) if (!size) break; + i40e_trace(clean_rx_irq, rx_ring, rx_desc, skb); rx_buffer = i40e_get_rx_buffer(rx_ring, size); /* retrieve a buffer from the ring */ @@ -2057,6 +2062,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) vlan_tag = (qword & BIT(I40E_RX_DESC_STATUS_L2TAG1P_SHIFT)) ? le16_to_cpu(rx_desc->wb.qword0.lo_dword.l2tag1) : 0; + i40e_trace(clean_rx_irq_rx, rx_ring, rx_desc, skb); i40e_receive_skb(rx_ring, skb, vlan_tag); skb = NULL; @@ -3138,6 +3144,8 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, /* prefetch the data, we'll need it later */ prefetch(skb->data); + i40e_trace(xmit_frame_ring, skb, tx_ring); + count = i40e_xmit_descriptor_count(skb); if (i40e_chk_linearize(skb, count)) { if (__skb_linearize(skb)) { @@ -3216,6 +3224,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, return NETDEV_TX_OK; out_drop: + i40e_trace(xmit_frame_ring_drop, first->skb, tx_ring); dev_kfree_skb_any(first->skb); first->skb = NULL; return NETDEV_TX_OK; diff --git a/drivers/net/ethernet/intel/i40evf/Makefile b/drivers/net/ethernet/intel/i40evf/Makefile index 827c7a6ed0ba..a393f4a07f06 100644 --- a/drivers/net/ethernet/intel/i40evf/Makefile +++ b/drivers/net/ethernet/intel/i40evf/Makefile @@ -29,6 +29,9 @@ # # +ccflags-y += -I$(src) +subdir-ccflags-y += -I$(src) + obj-$(CONFIG_I40EVF) += i40evf.o i40evf-objs := i40evf_main.o i40evf_ethtool.o i40evf_virtchnl.o \ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_trace.h b/drivers/net/ethernet/intel/i40evf/i40e_trace.h new file mode 100644 index 000000000000..9a5100b2b7c7 --- /dev/null +++ b/drivers/net/ethernet/intel/i40evf/i40e_trace.h @@ -0,0 +1,229 @@ +/******************************************************************************* + * + * Intel(R) 40-10 Gigabit Ethernet Virtual Function Driver + * Copyright(c) 2013 - 2017 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. + * + * 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 + * + ******************************************************************************/ + +/* Modeled on trace-events-sample.h */ + +/* The trace subsystem name for i40evf will be "i40evf". + * + * This file is named i40e_trace.h. + * + * Since this include file's name is different from the trace + * subsystem name, we'll have to define TRACE_INCLUDE_FILE at the end + * of this file. + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM i40evf + +/* See trace-events-sample.h for a detailed description of why this + * guard clause is different from most normal include files. + */ +#if !defined(_I40E_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _I40E_TRACE_H_ + +#include + +/** + * i40e_trace() macro enables shared code to refer to trace points + * like: + * + * trace_i40e{,vf}_example(args...) + * + * ... as: + * + * i40e_trace(example, args...) + * + * ... to resolve to the PF or VF version of the tracepoint without + * ifdefs, and to allow tracepoints to be disabled entirely at build + * time. + * + * Trace point should always be referred to in the driver via this + * macro. + * + * Similarly, i40e_trace_enabled(trace_name) wraps references to + * trace_i40e{,vf}__enabled() functions. + */ +#define _I40E_TRACE_NAME(trace_name) (trace_ ## i40evf ## _ ## trace_name) +#define I40E_TRACE_NAME(trace_name) _I40E_TRACE_NAME(trace_name) + +#define i40e_trace(trace_name, args...) I40E_TRACE_NAME(trace_name)(args) + +#define i40e_trace_enabled(trace_name) I40E_TRACE_NAME(trace_name##_enabled)() + +/* Events common to PF and VF. Corresponding versions will be defined + * for both, named trace_i40e_* and trace_i40evf_*. The i40e_trace() + * macro above will select the right trace point name for the driver + * being built from shared code. + */ + +/* Events related to a vsi & ring */ +DECLARE_EVENT_CLASS( + i40evf_tx_template, + + TP_PROTO(struct i40e_ring *ring, + struct i40e_tx_desc *desc, + struct i40e_tx_buffer *buf), + + TP_ARGS(ring, desc, buf), + + /* The convention here is to make the first fields in the + * TP_STRUCT match the TP_PROTO exactly. This enables the use + * of the args struct generated by the tplist tool (from the + * bcc-tools package) to be used for those fields. To access + * fields other than the tracepoint args will require the + * tplist output to be adjusted. + */ + TP_STRUCT__entry( + __field(void*, ring) + __field(void*, desc) + __field(void*, buf) + __string(devname, ring->netdev->name) + ), + + TP_fast_assign( + __entry->ring = ring; + __entry->desc = desc; + __entry->buf = buf; + __assign_str(devname, ring->netdev->name); + ), + + TP_printk( + "netdev: %s ring: %p desc: %p buf %p", + __get_str(devname), __entry->ring, + __entry->desc, __entry->buf) +); + +DEFINE_EVENT( + i40evf_tx_template, i40evf_clean_tx_irq, + TP_PROTO(struct i40e_ring *ring, + struct i40e_tx_desc *desc, + struct i40e_tx_buffer *buf), + + TP_ARGS(ring, desc, buf)); + +DEFINE_EVENT( + i40evf_tx_template, i40evf_clean_tx_irq_unmap, + TP_PROTO(struct i40e_ring *ring, + struct i40e_tx_desc *desc, + struct i40e_tx_buffer *buf), + + TP_ARGS(ring, desc, buf)); + +DECLARE_EVENT_CLASS( + i40evf_rx_template, + + TP_PROTO(struct i40e_ring *ring, + union i40e_32byte_rx_desc *desc, + struct sk_buff *skb), + + TP_ARGS(ring, desc, skb), + + TP_STRUCT__entry( + __field(void*, ring) + __field(void*, desc) + __field(void*, skb) + __string(devname, ring->netdev->name) + ), + + TP_fast_assign( + __entry->ring = ring; + __entry->desc = desc; + __entry->skb = skb; + __assign_str(devname, ring->netdev->name); + ), + + TP_printk( + "netdev: %s ring: %p desc: %p skb %p", + __get_str(devname), __entry->ring, + __entry->desc, __entry->skb) +); + +DEFINE_EVENT( + i40evf_rx_template, i40evf_clean_rx_irq, + TP_PROTO(struct i40e_ring *ring, + union i40e_32byte_rx_desc *desc, + struct sk_buff *skb), + + TP_ARGS(ring, desc, skb)); + +DEFINE_EVENT( + i40evf_rx_template, i40evf_clean_rx_irq_rx, + TP_PROTO(struct i40e_ring *ring, + union i40e_32byte_rx_desc *desc, + struct sk_buff *skb), + + TP_ARGS(ring, desc, skb)); + +DECLARE_EVENT_CLASS( + i40evf_xmit_template, + + TP_PROTO(struct sk_buff *skb, + struct i40e_ring *ring), + + TP_ARGS(skb, ring), + + TP_STRUCT__entry( + __field(void*, skb) + __field(void*, ring) + __string(devname, ring->netdev->name) + ), + + TP_fast_assign( + __entry->skb = skb; + __entry->ring = ring; + __assign_str(devname, ring->netdev->name); + ), + + TP_printk( + "netdev: %s skb: %p ring: %p", + __get_str(devname), __entry->skb, + __entry->ring) +); + +DEFINE_EVENT( + i40evf_xmit_template, i40evf_xmit_frame_ring, + TP_PROTO(struct sk_buff *skb, + struct i40e_ring *ring), + + TP_ARGS(skb, ring)); + +DEFINE_EVENT( + i40evf_xmit_template, i40evf_xmit_frame_ring_drop, + TP_PROTO(struct sk_buff *skb, + struct i40e_ring *ring), + + TP_ARGS(skb, ring)); + +/* Events unique to the VF. */ + +#endif /* _I40E_TRACE_H_ */ +/* This must be outside ifdef _I40E_TRACE_H */ + +/* This trace include file is not located in the .../include/trace + * with the kernel tracepoint definitions, because we're a loadable + * module. + */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE i40e_trace +#include diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 80931e3f9445..34e96d98251a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -28,6 +28,7 @@ #include #include "i40evf.h" +#include "i40e_trace.h" #include "i40e_prototype.h" static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, @@ -180,6 +181,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, /* prevent any other reads prior to eop_desc */ read_barrier_depends(); + i40e_trace(clean_tx_irq, tx_ring, tx_desc, tx_buf); /* if the descriptor isn't done, no work yet to do */ if (!(eop_desc->cmd_type_offset_bsz & cpu_to_le64(I40E_TX_DESC_DTYPE_DESC_DONE))) @@ -207,6 +209,8 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, /* unmap remaining buffers */ while (tx_desc != eop_desc) { + i40e_trace(clean_tx_irq_unmap, + tx_ring, tx_desc, tx_buf); tx_buf++; tx_desc++; @@ -1329,6 +1333,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) if (!size) break; + i40e_trace(clean_rx_irq, rx_ring, rx_desc, skb); rx_buffer = i40e_get_rx_buffer(rx_ring, size); /* retrieve a buffer from the ring */ @@ -1382,6 +1387,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) vlan_tag = (qword & BIT(I40E_RX_DESC_STATUS_L2TAG1P_SHIFT)) ? le16_to_cpu(rx_desc->wb.qword0.lo_dword.l2tag1) : 0; + i40e_trace(clean_rx_irq_rx, rx_ring, rx_desc, skb); i40e_receive_skb(rx_ring, skb, vlan_tag); skb = NULL; @@ -2223,6 +2229,8 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, /* prefetch the data, we'll need it later */ prefetch(skb->data); + i40e_trace(xmit_frame_ring, skb, tx_ring); + count = i40e_xmit_descriptor_count(skb); if (i40e_chk_linearize(skb, count)) { if (__skb_linearize(skb)) { @@ -2290,6 +2298,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, return NETDEV_TX_OK; out_drop: + i40e_trace(xmit_frame_ring_drop, first->skb, tx_ring); dev_kfree_skb_any(first->skb); first->skb = NULL; return NETDEV_TX_OK; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 671913c74bf8..5915273c372f 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -27,6 +27,13 @@ #include "i40evf.h" #include "i40e_prototype.h" #include "i40evf_client.h" +/* All i40evf tracepoints are defined by the include below, which must + * be included exactly once across the whole kernel with + * CREATE_TRACE_POINTS defined + */ +#define CREATE_TRACE_POINTS +#include "i40e_trace.h" + static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter); static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter); static int i40evf_close(struct net_device *netdev); -- cgit From 1d5c960c5ef565bc799a28d1fc4873e124adad6a Mon Sep 17 00:00:00 2001 From: Jingjing Wu Date: Thu, 13 Apr 2017 04:45:45 -0400 Subject: i40e: new AQ commands Add admin queue functions for Pipeline Personalization Profile AQ commands: - Write Recipe Command buffer (Opcode: 0x0270) - Get Applied Profiles list (Opcode: 0x0271) Change-ID: I558b4145364140f624013af48d4bbf79d21ebb0d Signed-off-by: Jingjing Wu Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h | 34 ++++ drivers/net/ethernet/intel/i40e/i40e_common.c | 212 +++++++++++++++++++++ drivers/net/ethernet/intel/i40e/i40e_prototype.h | 17 ++ drivers/net/ethernet/intel/i40e/i40e_type.h | 80 ++++++++ .../net/ethernet/intel/i40evf/i40e_adminq_cmd.h | 34 ++++ drivers/net/ethernet/intel/i40evf/i40e_common.c | 212 +++++++++++++++++++++ drivers/net/ethernet/intel/i40evf/i40e_prototype.h | 17 ++ drivers/net/ethernet/intel/i40evf/i40e_type.h | 80 ++++++++ 8 files changed, 686 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 251074c677c4..5eb04114e13f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -190,6 +190,10 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_add_mirror_rule = 0x0260, i40e_aqc_opc_delete_mirror_rule = 0x0261, + /* Pipeline Personalization Profile */ + i40e_aqc_opc_write_personalization_profile = 0x0270, + i40e_aqc_opc_get_personalization_profile_list = 0x0271, + /* DCB commands */ i40e_aqc_opc_dcb_ignore_pfc = 0x0301, i40e_aqc_opc_dcb_updated = 0x0302, @@ -1431,6 +1435,36 @@ struct i40e_aqc_add_delete_mirror_rule_completion { I40E_CHECK_CMD_LENGTH(i40e_aqc_add_delete_mirror_rule_completion); +/* Pipeline Personalization Profile */ +struct i40e_aqc_write_personalization_profile { + u8 flags; + u8 reserved[3]; + __le32 profile_track_id; + __le32 addr_high; + __le32 addr_low; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_write_personalization_profile); + +struct i40e_aqc_write_ppp_resp { + __le32 error_offset; + __le32 error_info; + __le32 addr_high; + __le32 addr_low; +}; + +struct i40e_aqc_get_applied_profiles { + u8 flags; +#define I40E_AQC_GET_PPP_GET_CONF 0x1 +#define I40E_AQC_GET_PPP_GET_RDPU_CONF 0x2 + u8 rsv[3]; + __le32 reserved; + __le32 addr_high; + __le32 addr_low; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_get_applied_profiles); + /* DCB 0x03xx*/ /* PFC Ignore (direct 0x0301) diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index f9db95aa3a20..24f020655291 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -5042,3 +5042,215 @@ do_retry: if (status || use_register) wr32(hw, reg_addr, reg_val); } + +/** + * i40e_aq_write_ppp - Write pipeline personalization profile (ppp) + * @hw: pointer to the hw struct + * @buff: command buffer (size in bytes = buff_size) + * @buff_size: buffer size in bytes + * @track_id: package tracking id + * @error_offset: returns error offset + * @error_info: returns error information + * @cmd_details: pointer to command details structure or NULL + **/ +enum +i40e_status_code i40e_aq_write_ppp(struct i40e_hw *hw, void *buff, + u16 buff_size, u32 track_id, + u32 *error_offset, u32 *error_info, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_write_personalization_profile *cmd = + (struct i40e_aqc_write_personalization_profile *) + &desc.params.raw; + struct i40e_aqc_write_ppp_resp *resp; + i40e_status status; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_write_personalization_profile); + + desc.flags |= cpu_to_le16(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD); + if (buff_size > I40E_AQ_LARGE_BUF) + desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + + desc.datalen = cpu_to_le16(buff_size); + + cmd->profile_track_id = cpu_to_le32(track_id); + + status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details); + if (!status) { + resp = (struct i40e_aqc_write_ppp_resp *)&desc.params.raw; + if (error_offset) + *error_offset = le32_to_cpu(resp->error_offset); + if (error_info) + *error_info = le32_to_cpu(resp->error_info); + } + + return status; +} + +/** + * i40e_aq_get_ppp_list - Read pipeline personalization profile (ppp) + * @hw: pointer to the hw struct + * @buff: command buffer (size in bytes = buff_size) + * @buff_size: buffer size in bytes + * @cmd_details: pointer to command details structure or NULL + **/ +enum +i40e_status_code i40e_aq_get_ppp_list(struct i40e_hw *hw, void *buff, + u16 buff_size, u8 flags, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_get_applied_profiles *cmd = + (struct i40e_aqc_get_applied_profiles *)&desc.params.raw; + i40e_status status; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_get_personalization_profile_list); + + desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); + if (buff_size > I40E_AQ_LARGE_BUF) + desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.datalen = cpu_to_le16(buff_size); + + cmd->flags = flags; + + status = i40e_asq_send_command(hw, &desc, buff, buff_size, cmd_details); + + return status; +} + +/** + * i40e_find_segment_in_package + * @segment_type: the segment type to search for (i.e., SEGMENT_TYPE_I40E) + * @pkg_hdr: pointer to the package header to be searched + * + * This function searches a package file for a particular segment type. On + * success it returns a pointer to the segment header, otherwise it will + * return NULL. + **/ +struct i40e_generic_seg_header * +i40e_find_segment_in_package(u32 segment_type, + struct i40e_package_header *pkg_hdr) +{ + struct i40e_generic_seg_header *segment; + u32 i; + + /* Search all package segments for the requested segment type */ + for (i = 0; i < pkg_hdr->segment_count; i++) { + segment = + (struct i40e_generic_seg_header *)((u8 *)pkg_hdr + + pkg_hdr->segment_offset[i]); + + if (segment->type == segment_type) + return segment; + } + + return NULL; +} + +/** + * i40e_write_profile + * @hw: pointer to the hardware structure + * @profile: pointer to the profile segment of the package to be downloaded + * @track_id: package tracking id + * + * Handles the download of a complete package. + */ +enum i40e_status_code +i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile, + u32 track_id) +{ + i40e_status status = 0; + struct i40e_section_table *sec_tbl; + struct i40e_profile_section_header *sec = NULL; + u32 dev_cnt; + u32 vendor_dev_id; + u32 *nvm; + u32 section_size = 0; + u32 offset = 0, info = 0; + u32 i; + + if (!track_id) { + i40e_debug(hw, I40E_DEBUG_PACKAGE, "Track_id can't be 0."); + return I40E_NOT_SUPPORTED; + } + + dev_cnt = profile->device_table_count; + + for (i = 0; i < dev_cnt; i++) { + vendor_dev_id = profile->device_table[i].vendor_dev_id; + if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL) + if (hw->device_id == (vendor_dev_id & 0xFFFF)) + break; + } + if (i == dev_cnt) { + i40e_debug(hw, I40E_DEBUG_PACKAGE, "Device doesn't support PPP"); + return I40E_ERR_DEVICE_NOT_SUPPORTED; + } + + nvm = (u32 *)&profile->device_table[dev_cnt]; + sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1]; + + for (i = 0; i < sec_tbl->section_count; i++) { + sec = (struct i40e_profile_section_header *)((u8 *)profile + + sec_tbl->section_offset[i]); + + /* Skip 'AQ', 'note' and 'name' sections */ + if (sec->section.type != SECTION_TYPE_MMIO) + continue; + + section_size = sec->section.size + + sizeof(struct i40e_profile_section_header); + + /* Write profile */ + status = i40e_aq_write_ppp(hw, (void *)sec, (u16)section_size, + track_id, &offset, &info, NULL); + if (status) { + i40e_debug(hw, I40E_DEBUG_PACKAGE, + "Failed to write profile: offset %d, info %d", + offset, info); + break; + } + } + return status; +} + +/** + * i40e_add_pinfo_to_list + * @hw: pointer to the hardware structure + * @profile: pointer to the profile segment of the package + * @profile_info_sec: buffer for information section + * @track_id: package tracking id + * + * Register a profile to the list of loaded profiles. + */ +enum i40e_status_code +i40e_add_pinfo_to_list(struct i40e_hw *hw, + struct i40e_profile_segment *profile, + u8 *profile_info_sec, u32 track_id) +{ + i40e_status status = 0; + struct i40e_profile_section_header *sec = NULL; + struct i40e_profile_info *pinfo; + u32 offset = 0, info = 0; + + sec = (struct i40e_profile_section_header *)profile_info_sec; + sec->tbl_size = 1; + sec->data_end = sizeof(struct i40e_profile_section_header) + + sizeof(struct i40e_profile_info); + sec->section.type = SECTION_TYPE_INFO; + sec->section.offset = sizeof(struct i40e_profile_section_header); + sec->section.size = sizeof(struct i40e_profile_info); + pinfo = (struct i40e_profile_info *)(profile_info_sec + + sec->section.offset); + pinfo->track_id = track_id; + pinfo->version = profile->version; + pinfo->op = I40E_PPP_ADD_TRACKID; + memcpy(pinfo->name, profile->name, I40E_PPP_NAME_SIZE); + + status = i40e_aq_write_ppp(hw, (void *)sec, sec->data_end, + track_id, &offset, &info, NULL); + return status; +} diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index dfc5e5901be5..c56d976cf85a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -377,4 +377,21 @@ i40e_status i40e_write_phy_register(struct i40e_hw *hw, u8 page, u16 reg, u8 i40e_get_phy_address(struct i40e_hw *hw, u8 dev_num); i40e_status i40e_blink_phy_link_led(struct i40e_hw *hw, u32 time, u32 interval); +i40e_status i40e_aq_write_ppp(struct i40e_hw *hw, void *buff, + u16 buff_size, u32 track_id, + u32 *error_offset, u32 *error_info, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_get_ppp_list(struct i40e_hw *hw, void *buff, + u16 buff_size, u8 flags, + struct i40e_asq_cmd_details *cmd_details); +struct i40e_generic_seg_header * +i40e_find_segment_in_package(u32 segment_type, + struct i40e_package_header *pkg_header); +enum i40e_status_code +i40e_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *i40e_seg, + u32 track_id); +enum i40e_status_code +i40e_add_pinfo_to_list(struct i40e_hw *hw, + struct i40e_profile_segment *profile, + u8 *profile_info_sec, u32 track_id); #endif /* _I40E_PROTOTYPE_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 9200f2d9c752..3a18ed13edc4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -78,6 +78,7 @@ enum i40e_debug_mask { I40E_DEBUG_DCB = 0x00000400, I40E_DEBUG_DIAG = 0x00000800, I40E_DEBUG_FD = 0x00001000, + I40E_DEBUG_PACKAGE = 0x00002000, I40E_DEBUG_IWARP = 0x00F00000, I40E_DEBUG_AQ_MESSAGE = 0x01000000, I40E_DEBUG_AQ_DESCRIPTOR = 0x02000000, @@ -1462,4 +1463,83 @@ struct i40e_lldp_variables { #define I40E_FLEX_56_MASK (0x1ULL << I40E_FLEX_56_SHIFT) #define I40E_FLEX_57_SHIFT 6 #define I40E_FLEX_57_MASK (0x1ULL << I40E_FLEX_57_SHIFT) + +/* Version format for PPP */ +struct i40e_ppp_version { + u8 major; + u8 minor; + u8 update; + u8 draft; +}; + +#define I40E_PPP_NAME_SIZE 32 + +/* Package header */ +struct i40e_package_header { + struct i40e_ppp_version version; + u32 segment_count; + u32 segment_offset[1]; +}; + +/* Generic segment header */ +struct i40e_generic_seg_header { +#define SEGMENT_TYPE_METADATA 0x00000001 +#define SEGMENT_TYPE_NOTES 0x00000002 +#define SEGMENT_TYPE_I40E 0x00000011 +#define SEGMENT_TYPE_X722 0x00000012 + u32 type; + struct i40e_ppp_version version; + u32 size; + char name[I40E_PPP_NAME_SIZE]; +}; + +struct i40e_metadata_segment { + struct i40e_generic_seg_header header; + struct i40e_ppp_version version; + u32 track_id; + char name[I40E_PPP_NAME_SIZE]; +}; + +struct i40e_device_id_entry { + u32 vendor_dev_id; + u32 sub_vendor_dev_id; +}; + +struct i40e_profile_segment { + struct i40e_generic_seg_header header; + struct i40e_ppp_version version; + char name[I40E_PPP_NAME_SIZE]; + u32 device_table_count; + struct i40e_device_id_entry device_table[1]; +}; + +struct i40e_section_table { + u32 section_count; + u32 section_offset[1]; +}; + +struct i40e_profile_section_header { + u16 tbl_size; + u16 data_end; + struct { +#define SECTION_TYPE_INFO 0x00000010 +#define SECTION_TYPE_MMIO 0x00000800 +#define SECTION_TYPE_AQ 0x00000801 +#define SECTION_TYPE_NOTE 0x80000000 +#define SECTION_TYPE_NAME 0x80000001 + u32 type; + u32 offset; + u32 size; + } section; +}; + +struct i40e_profile_info { + u32 track_id; + struct i40e_ppp_version version; + u8 op; +#define I40E_PPP_ADD_TRACKID 0x01 +#define I40E_PPP_REMOVE_TRACKID 0x02 + u8 reserved[7]; + u8 name[I40E_PPP_NAME_SIZE]; +}; #endif /* _I40E_TYPE_H_ */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h index c28cb8f27243..91d8786d386d 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h @@ -190,6 +190,10 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_add_mirror_rule = 0x0260, i40e_aqc_opc_delete_mirror_rule = 0x0261, + /* Pipeline Personalization Profile */ + i40e_aqc_opc_write_personalization_profile = 0x0270, + i40e_aqc_opc_get_personalization_profile_list = 0x0271, + /* DCB commands */ i40e_aqc_opc_dcb_ignore_pfc = 0x0301, i40e_aqc_opc_dcb_updated = 0x0302, @@ -1426,6 +1430,36 @@ struct i40e_aqc_add_delete_mirror_rule_completion { I40E_CHECK_CMD_LENGTH(i40e_aqc_add_delete_mirror_rule_completion); +/* Pipeline Personalization Profile */ +struct i40e_aqc_write_personalization_profile { + u8 flags; + u8 reserved[3]; + __le32 profile_track_id; + __le32 addr_high; + __le32 addr_low; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_write_personalization_profile); + +struct i40e_aqc_write_ppp_resp { + __le32 error_offset; + __le32 error_info; + __le32 addr_high; + __le32 addr_low; +}; + +struct i40e_aqc_get_applied_profiles { + u8 flags; +#define I40E_AQC_GET_PPP_GET_CONF 0x1 +#define I40E_AQC_GET_PPP_GET_RDPU_CONF 0x2 + u8 rsv[3]; + __le32 reserved; + __le32 addr_high; + __le32 addr_low; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_get_applied_profiles); + /* DCB 0x03xx*/ /* PFC Ignore (direct 0x0301) diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c index 626fbf1ead4d..43f10761f4ba 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c @@ -1131,3 +1131,215 @@ i40e_status i40e_vf_reset(struct i40e_hw *hw) return i40e_aq_send_msg_to_pf(hw, I40E_VIRTCHNL_OP_RESET_VF, 0, NULL, 0, NULL); } + +/** + * i40evf_aq_write_ppp - Write pipeline personalization profile (ppp) + * @hw: pointer to the hw struct + * @buff: command buffer (size in bytes = buff_size) + * @buff_size: buffer size in bytes + * @track_id: package tracking id + * @error_offset: returns error offset + * @error_info: returns error information + * @cmd_details: pointer to command details structure or NULL + **/ +enum +i40e_status_code i40evf_aq_write_ppp(struct i40e_hw *hw, void *buff, + u16 buff_size, u32 track_id, + u32 *error_offset, u32 *error_info, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_write_personalization_profile *cmd = + (struct i40e_aqc_write_personalization_profile *) + &desc.params.raw; + struct i40e_aqc_write_ppp_resp *resp; + i40e_status status; + + i40evf_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_write_personalization_profile); + + desc.flags |= cpu_to_le16(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD); + if (buff_size > I40E_AQ_LARGE_BUF) + desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + + desc.datalen = cpu_to_le16(buff_size); + + cmd->profile_track_id = cpu_to_le32(track_id); + + status = i40evf_asq_send_command(hw, &desc, buff, buff_size, cmd_details); + if (!status) { + resp = (struct i40e_aqc_write_ppp_resp *)&desc.params.raw; + if (error_offset) + *error_offset = le32_to_cpu(resp->error_offset); + if (error_info) + *error_info = le32_to_cpu(resp->error_info); + } + + return status; +} + +/** + * i40evf_aq_get_ppp_list - Read pipeline personalization profile (ppp) + * @hw: pointer to the hw struct + * @buff: command buffer (size in bytes = buff_size) + * @buff_size: buffer size in bytes + * @cmd_details: pointer to command details structure or NULL + **/ +enum +i40e_status_code i40evf_aq_get_ppp_list(struct i40e_hw *hw, void *buff, + u16 buff_size, u8 flags, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_get_applied_profiles *cmd = + (struct i40e_aqc_get_applied_profiles *)&desc.params.raw; + i40e_status status; + + i40evf_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_get_personalization_profile_list); + + desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_BUF); + if (buff_size > I40E_AQ_LARGE_BUF) + desc.flags |= cpu_to_le16((u16)I40E_AQ_FLAG_LB); + desc.datalen = cpu_to_le16(buff_size); + + cmd->flags = flags; + + status = i40evf_asq_send_command(hw, &desc, buff, buff_size, cmd_details); + + return status; +} + +/** + * i40evf_find_segment_in_package + * @segment_type: the segment type to search for (i.e., SEGMENT_TYPE_I40E) + * @pkg_hdr: pointer to the package header to be searched + * + * This function searches a package file for a particular segment type. On + * success it returns a pointer to the segment header, otherwise it will + * return NULL. + **/ +struct i40e_generic_seg_header * +i40evf_find_segment_in_package(u32 segment_type, + struct i40e_package_header *pkg_hdr) +{ + struct i40e_generic_seg_header *segment; + u32 i; + + /* Search all package segments for the requested segment type */ + for (i = 0; i < pkg_hdr->segment_count; i++) { + segment = + (struct i40e_generic_seg_header *)((u8 *)pkg_hdr + + pkg_hdr->segment_offset[i]); + + if (segment->type == segment_type) + return segment; + } + + return NULL; +} + +/** + * i40evf_write_profile + * @hw: pointer to the hardware structure + * @profile: pointer to the profile segment of the package to be downloaded + * @track_id: package tracking id + * + * Handles the download of a complete package. + */ +enum i40e_status_code +i40evf_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *profile, + u32 track_id) +{ + i40e_status status = 0; + struct i40e_section_table *sec_tbl; + struct i40e_profile_section_header *sec = NULL; + u32 dev_cnt; + u32 vendor_dev_id; + u32 *nvm; + u32 section_size = 0; + u32 offset = 0, info = 0; + u32 i; + + if (!track_id) { + i40e_debug(hw, I40E_DEBUG_PACKAGE, "Track_id can't be 0."); + return I40E_NOT_SUPPORTED; + } + + dev_cnt = profile->device_table_count; + + for (i = 0; i < dev_cnt; i++) { + vendor_dev_id = profile->device_table[i].vendor_dev_id; + if ((vendor_dev_id >> 16) == PCI_VENDOR_ID_INTEL) + if (hw->device_id == (vendor_dev_id & 0xFFFF)) + break; + } + if (i == dev_cnt) { + i40e_debug(hw, I40E_DEBUG_PACKAGE, "Device doesn't support PPP"); + return I40E_ERR_DEVICE_NOT_SUPPORTED; + } + + nvm = (u32 *)&profile->device_table[dev_cnt]; + sec_tbl = (struct i40e_section_table *)&nvm[nvm[0] + 1]; + + for (i = 0; i < sec_tbl->section_count; i++) { + sec = (struct i40e_profile_section_header *)((u8 *)profile + + sec_tbl->section_offset[i]); + + /* Skip 'AQ', 'note' and 'name' sections */ + if (sec->section.type != SECTION_TYPE_MMIO) + continue; + + section_size = sec->section.size + + sizeof(struct i40e_profile_section_header); + + /* Write profile */ + status = i40evf_aq_write_ppp(hw, (void *)sec, (u16)section_size, + track_id, &offset, &info, NULL); + if (status) { + i40e_debug(hw, I40E_DEBUG_PACKAGE, + "Failed to write profile: offset %d, info %d", + offset, info); + break; + } + } + return status; +} + +/** + * i40evf_add_pinfo_to_list + * @hw: pointer to the hardware structure + * @profile: pointer to the profile segment of the package + * @profile_info_sec: buffer for information section + * @track_id: package tracking id + * + * Register a profile to the list of loaded profiles. + */ +enum i40e_status_code +i40evf_add_pinfo_to_list(struct i40e_hw *hw, + struct i40e_profile_segment *profile, + u8 *profile_info_sec, u32 track_id) +{ + i40e_status status = 0; + struct i40e_profile_section_header *sec = NULL; + struct i40e_profile_info *pinfo; + u32 offset = 0, info = 0; + + sec = (struct i40e_profile_section_header *)profile_info_sec; + sec->tbl_size = 1; + sec->data_end = sizeof(struct i40e_profile_section_header) + + sizeof(struct i40e_profile_info); + sec->section.type = SECTION_TYPE_INFO; + sec->section.offset = sizeof(struct i40e_profile_section_header); + sec->section.size = sizeof(struct i40e_profile_info); + pinfo = (struct i40e_profile_info *)(profile_info_sec + + sec->section.offset); + pinfo->track_id = track_id; + pinfo->version = profile->version; + pinfo->op = I40E_PPP_ADD_TRACKID; + memcpy(pinfo->name, profile->name, I40E_PPP_NAME_SIZE); + + status = i40evf_aq_write_ppp(hw, (void *)sec, sec->data_end, + track_id, &offset, &info, NULL); + return status; +} diff --git a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h index ba6c6bda0e22..741223d5d809 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_prototype.h @@ -122,4 +122,21 @@ i40e_status i40e_write_phy_register(struct i40e_hw *hw, u8 page, u16 reg, u8 i40e_get_phy_address(struct i40e_hw *hw, u8 dev_num); i40e_status i40e_blink_phy_link_led(struct i40e_hw *hw, u32 time, u32 interval); +i40e_status i40evf_aq_write_ppp(struct i40e_hw *hw, void *buff, + u16 buff_size, u32 track_id, + u32 *error_offset, u32 *error_info, + struct i40e_asq_cmd_details *cmd_details); +i40e_status i40evf_aq_get_ppp_list(struct i40e_hw *hw, void *buff, + u16 buff_size, u8 flags, + struct i40e_asq_cmd_details *cmd_details); +struct i40e_generic_seg_header * +i40evf_find_segment_in_package(u32 segment_type, + struct i40e_package_header *pkg_header); +enum i40e_status_code +i40evf_write_profile(struct i40e_hw *hw, struct i40e_profile_segment *i40e_seg, + u32 track_id); +enum i40e_status_code +i40evf_add_pinfo_to_list(struct i40e_hw *hw, + struct i40e_profile_segment *profile, + u8 *profile_info_sec, u32 track_id); #endif /* _I40E_PROTOTYPE_H_ */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index 16bb88084bb9..bde7f24af1c6 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -78,6 +78,7 @@ enum i40e_debug_mask { I40E_DEBUG_DCB = 0x00000400, I40E_DEBUG_DIAG = 0x00000800, I40E_DEBUG_FD = 0x00001000, + I40E_DEBUG_PACKAGE = 0x00002000, I40E_DEBUG_AQ_MESSAGE = 0x01000000, I40E_DEBUG_AQ_DESCRIPTOR = 0x02000000, @@ -1396,4 +1397,83 @@ enum i40e_reset_type { #define I40E_FD_INSET_FLEX_WORD57_SHIFT 10 #define I40E_FD_INSET_FLEX_WORD57_MASK (0x1ULL << \ I40E_FD_INSET_FLEX_WORD57_SHIFT) + +/* Version format for PPP */ +struct i40e_ppp_version { + u8 major; + u8 minor; + u8 update; + u8 draft; +}; + +#define I40E_PPP_NAME_SIZE 32 + +/* Package header */ +struct i40e_package_header { + struct i40e_ppp_version version; + u32 segment_count; + u32 segment_offset[1]; +}; + +/* Generic segment header */ +struct i40e_generic_seg_header { +#define SEGMENT_TYPE_METADATA 0x00000001 +#define SEGMENT_TYPE_NOTES 0x00000002 +#define SEGMENT_TYPE_I40E 0x00000011 +#define SEGMENT_TYPE_X722 0x00000012 + u32 type; + struct i40e_ppp_version version; + u32 size; + char name[I40E_PPP_NAME_SIZE]; +}; + +struct i40e_metadata_segment { + struct i40e_generic_seg_header header; + struct i40e_ppp_version version; + u32 track_id; + char name[I40E_PPP_NAME_SIZE]; +}; + +struct i40e_device_id_entry { + u32 vendor_dev_id; + u32 sub_vendor_dev_id; +}; + +struct i40e_profile_segment { + struct i40e_generic_seg_header header; + struct i40e_ppp_version version; + char name[I40E_PPP_NAME_SIZE]; + u32 device_table_count; + struct i40e_device_id_entry device_table[1]; +}; + +struct i40e_section_table { + u32 section_count; + u32 section_offset[1]; +}; + +struct i40e_profile_section_header { + u16 tbl_size; + u16 data_end; + struct { +#define SECTION_TYPE_INFO 0x00000010 +#define SECTION_TYPE_MMIO 0x00000800 +#define SECTION_TYPE_AQ 0x00000801 +#define SECTION_TYPE_NOTE 0x80000000 +#define SECTION_TYPE_NAME 0x80000001 + u32 type; + u32 offset; + u32 size; + } section; +}; + +struct i40e_profile_info { + u32 track_id; + struct i40e_ppp_version version; + u8 op; +#define I40E_PPP_ADD_TRACKID 0x01 +#define I40E_PPP_REMOVE_TRACKID 0x02 + u8 reserved[7]; + u8 name[I40E_PPP_NAME_SIZE]; +}; #endif /* _I40E_TYPE_H_ */ -- cgit From 024b05f4246e281ef50e019eff0fc53aedf069ac Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 13 Apr 2017 04:45:46 -0400 Subject: i40e: don't hold RTNL lock while waiting for VF reset to finish We made some effort to reduce the RTNL lock scope when resetting and rebuilding the PF. Unfortunately we still held the RTNL lock during the VF reset operation, which meant that multiple PFs could not reset in parallel due to the global lock. For now, further reduce the scope by not holding the RTNL lock while resetting VFs. This allows multiple PFs to reset in a timely manner. Change-ID: I2fbf823a0063f24dff67676cad09f0bbf83ee4ce Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 5e625e0c73ac..5c2ceb247959 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7108,6 +7108,10 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired) /* restart the VSIs that were rebuilt and running before the reset */ i40e_pf_unquiesce_all_vsi(pf); + /* Release the RTNL lock before we start resetting VFs */ + if (!lock_acquired) + rtnl_unlock(); + if (pf->num_alloc_vfs) { for (v = 0; v < pf->num_alloc_vfs; v++) i40e_reset_vf(&pf->vf[v], true); @@ -7116,9 +7120,12 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired) /* tell the firmware that we're starting */ i40e_send_version(pf); + /* We've already released the lock, so don't do it again */ + goto end_core_reset; + end_unlock: -if (!lock_acquired) - rtnl_unlock(); + if (!lock_acquired) + rtnl_unlock(); end_core_reset: clear_bit(__I40E_RESET_FAILED, &pf->state); clear_recovery: -- cgit From c768e490640dbb928d1c8a5f7b437a334d0cde44 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 13 Apr 2017 04:45:47 -0400 Subject: i40e: factor out queue control from i40e_vsi_control_(tx|rx) A future patch will need to be able to handle controlling queues without waiting until all VSIs are handled. Factor out the direct queue modification so that we can easily re-use this code. The result is also a bit easier to read since we don't embed multiple single-letter loop counters. Change-ID: Id923cbfa43127b1c24d8ed4f809b1012c736d9ac Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 139 ++++++++++++++++++---------- 1 file changed, 89 insertions(+), 50 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 5c2ceb247959..2bf5bb1b4627 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -3919,6 +3919,8 @@ static void i40e_netpoll(struct net_device *netdev) } #endif +#define I40E_QTX_ENA_WAIT_COUNT 50 + /** * i40e_pf_txq_wait - Wait for a PF's Tx queue to be enabled or disabled * @pf: the PF being configured @@ -3948,6 +3950,50 @@ static int i40e_pf_txq_wait(struct i40e_pf *pf, int pf_q, bool enable) return 0; } +/** + * i40e_control_tx_q - Start or stop a particular Tx queue + * @pf: the PF structure + * @pf_q: the PF queue to configure + * @enable: start or stop the queue + * + * This function enables or disables a single queue. Note that any delay + * required after the operation is expected to be handled by the caller of + * this function. + **/ +static void i40e_control_tx_q(struct i40e_pf *pf, int pf_q, bool enable) +{ + struct i40e_hw *hw = &pf->hw; + u32 tx_reg; + int i; + + /* warn the TX unit of coming changes */ + i40e_pre_tx_queue_cfg(&pf->hw, pf_q, enable); + if (!enable) + usleep_range(10, 20); + + for (i = 0; i < I40E_QTX_ENA_WAIT_COUNT; i++) { + tx_reg = rd32(hw, I40E_QTX_ENA(pf_q)); + if (((tx_reg >> I40E_QTX_ENA_QENA_REQ_SHIFT) & 1) == + ((tx_reg >> I40E_QTX_ENA_QENA_STAT_SHIFT) & 1)) + break; + usleep_range(1000, 2000); + } + + /* Skip if the queue is already in the requested state */ + if (enable == !!(tx_reg & I40E_QTX_ENA_QENA_STAT_MASK)) + return; + + /* turn on/off the queue */ + if (enable) { + wr32(hw, I40E_QTX_HEAD(pf_q), 0); + tx_reg |= I40E_QTX_ENA_QENA_REQ_MASK; + } else { + tx_reg &= ~I40E_QTX_ENA_QENA_REQ_MASK; + } + + wr32(hw, I40E_QTX_ENA(pf_q), tx_reg); +} + /** * i40e_vsi_control_tx - Start or stop a VSI's rings * @vsi: the VSI being configured @@ -3956,39 +4002,13 @@ static int i40e_pf_txq_wait(struct i40e_pf *pf, int pf_q, bool enable) static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable) { struct i40e_pf *pf = vsi->back; - struct i40e_hw *hw = &pf->hw; - int i, j, pf_q, ret = 0; - u32 tx_reg; + int i, pf_q, ret = 0; pf_q = vsi->base_queue; for (i = 0; i < vsi->num_queue_pairs; i++, pf_q++) { + i40e_control_tx_q(pf, pf_q, enable); - /* warn the TX unit of coming changes */ - i40e_pre_tx_queue_cfg(&pf->hw, pf_q, enable); - if (!enable) - usleep_range(10, 20); - - for (j = 0; j < 50; j++) { - tx_reg = rd32(hw, I40E_QTX_ENA(pf_q)); - if (((tx_reg >> I40E_QTX_ENA_QENA_REQ_SHIFT) & 1) == - ((tx_reg >> I40E_QTX_ENA_QENA_STAT_SHIFT) & 1)) - break; - usleep_range(1000, 2000); - } - /* Skip if the queue is already in the requested state */ - if (enable == !!(tx_reg & I40E_QTX_ENA_QENA_STAT_MASK)) - continue; - - /* turn on/off the queue */ - if (enable) { - wr32(hw, I40E_QTX_HEAD(pf_q), 0); - tx_reg |= I40E_QTX_ENA_QENA_REQ_MASK; - } else { - tx_reg &= ~I40E_QTX_ENA_QENA_REQ_MASK; - } - - wr32(hw, I40E_QTX_ENA(pf_q), tx_reg); - /* No waiting for the Tx queue to disable */ + /* Don't wait to disable when port Tx is suspended */ if (!enable && test_bit(__I40E_PORT_TX_SUSPENDED, &pf->state)) continue; @@ -4034,6 +4054,43 @@ static int i40e_pf_rxq_wait(struct i40e_pf *pf, int pf_q, bool enable) return 0; } +/** + * i40e_control_rx_q - Start or stop a particular Rx queue + * @pf: the PF structure + * @pf_q: the PF queue to configure + * @enable: start or stop the queue + * + * This function enables or disables a single queue. Note that any delay + * required after the operation is expected to be handled by the caller of + * this function. + **/ +static void i40e_control_rx_q(struct i40e_pf *pf, int pf_q, bool enable) +{ + struct i40e_hw *hw = &pf->hw; + u32 rx_reg; + int i; + + for (i = 0; i < I40E_QTX_ENA_WAIT_COUNT; i++) { + rx_reg = rd32(hw, I40E_QRX_ENA(pf_q)); + if (((rx_reg >> I40E_QRX_ENA_QENA_REQ_SHIFT) & 1) == + ((rx_reg >> I40E_QRX_ENA_QENA_STAT_SHIFT) & 1)) + break; + usleep_range(1000, 2000); + } + + /* Skip if the queue is already in the requested state */ + if (enable == !!(rx_reg & I40E_QRX_ENA_QENA_STAT_MASK)) + return; + + /* turn on/off the queue */ + if (enable) + rx_reg |= I40E_QRX_ENA_QENA_REQ_MASK; + else + rx_reg &= ~I40E_QRX_ENA_QENA_REQ_MASK; + + wr32(hw, I40E_QRX_ENA(pf_q), rx_reg); +} + /** * i40e_vsi_control_rx - Start or stop a VSI's rings * @vsi: the VSI being configured @@ -4042,31 +4099,13 @@ static int i40e_pf_rxq_wait(struct i40e_pf *pf, int pf_q, bool enable) static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable) { struct i40e_pf *pf = vsi->back; - struct i40e_hw *hw = &pf->hw; - int i, j, pf_q, ret = 0; - u32 rx_reg; + int i, pf_q, ret = 0; pf_q = vsi->base_queue; for (i = 0; i < vsi->num_queue_pairs; i++, pf_q++) { - for (j = 0; j < 50; j++) { - rx_reg = rd32(hw, I40E_QRX_ENA(pf_q)); - if (((rx_reg >> I40E_QRX_ENA_QENA_REQ_SHIFT) & 1) == - ((rx_reg >> I40E_QRX_ENA_QENA_STAT_SHIFT) & 1)) - break; - usleep_range(1000, 2000); - } + i40e_control_rx_q(pf, pf_q, enable); - /* Skip if the queue is already in the requested state */ - if (enable == !!(rx_reg & I40E_QRX_ENA_QENA_STAT_MASK)) - continue; - - /* turn on/off the queue */ - if (enable) - rx_reg |= I40E_QRX_ENA_QENA_REQ_MASK; - else - rx_reg &= ~I40E_QRX_ENA_QENA_REQ_MASK; - wr32(hw, I40E_QRX_ENA(pf_q), rx_reg); - /* No waiting for the Tx queue to disable */ + /* Don't wait to disable when port Tx is suspended */ if (!enable && test_bit(__I40E_PORT_TX_SUSPENDED, &pf->state)) continue; -- cgit From e8d2f4c674571b2b2d8a58405196d4a390996e33 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 13 Apr 2017 04:45:48 -0400 Subject: i40e: fix CONFIG_BUSY checks in i40e_set_settings function The check for I40E_CONFIG_BUSY state bit in the i40e_set_link_ksettings function is fishy. First we can notice a few things about the check here. First a similar check was introduced by commit 'c7d05ca89f8e ("i40e: driver ethtool core")' Later a commit introducing the link settings was added by commit 'bf9c71417f72 ("i40e: Implement set_settings for ethtool")' However, this second check was against vsi->state instead of pf->state, and also failed to set the bit, it only checks. That indicates the locking was not quite correct. The only other place that the state bit in vsi->state gets used is to protect the filter list. Since this code does not care about the mac filter list, and seems clear the original code should have set the pf->state bit. Fix these issues by using pf->state correctly, and by actually setting the bit so that we properly lock as expected. Since these checks occur while holding the rtnl_lock(), lets also add a timeout so that we don't potentially softlock the system. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 38 ++++++++++++++++++++------ 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 10325b5a9805..08035c4389cd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -698,6 +698,7 @@ static int i40e_set_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings copy_cmd; i40e_status status = 0; bool change = false; + int timeout = 50; int err = 0; u32 autoneg; u32 advertise; @@ -756,14 +757,20 @@ static int i40e_set_link_ksettings(struct net_device *netdev, if (memcmp(©_cmd, &safe_cmd, sizeof(struct ethtool_link_ksettings))) return -EOPNOTSUPP; - while (test_bit(__I40E_CONFIG_BUSY, &vsi->state)) + while (test_and_set_bit(__I40E_CONFIG_BUSY, &pf->state)) { + timeout--; + if (!timeout) + return -EBUSY; usleep_range(1000, 2000); + } /* Get the current phy config */ status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities, NULL); - if (status) - return -EAGAIN; + if (status) { + err = -EAGAIN; + goto done; + } /* Copy abilities to config in case autoneg is not * set below @@ -779,7 +786,8 @@ static int i40e_set_link_ksettings(struct net_device *netdev, if (!ethtool_link_ksettings_test_link_mode( &safe_cmd, supported, Autoneg)) { netdev_info(netdev, "Autoneg not supported on this phy\n"); - return -EINVAL; + err = -EINVAL; + goto done; } /* Autoneg is allowed to change */ config.abilities = abilities.abilities | @@ -797,7 +805,8 @@ static int i40e_set_link_ksettings(struct net_device *netdev, hw->phy.link_info.phy_type != I40E_PHY_TYPE_10GBASE_T) { netdev_info(netdev, "Autoneg cannot be disabled on this phy\n"); - return -EINVAL; + err = -EINVAL; + goto done; } /* Autoneg is allowed to change */ config.abilities = abilities.abilities & @@ -808,8 +817,10 @@ static int i40e_set_link_ksettings(struct net_device *netdev, ethtool_convert_link_mode_to_legacy_u32(&tmp, safe_cmd.link_modes.supported); - if (advertise & ~tmp) - return -EINVAL; + if (advertise & ~tmp) { + err = -EINVAL; + goto done; + } if (advertise & ADVERTISED_100baseT_Full) config.link_speed |= I40E_LINK_SPEED_100MB; @@ -865,7 +876,8 @@ static int i40e_set_link_ksettings(struct net_device *netdev, netdev_info(netdev, "Set phy config failed, err %s aq_err %s\n", i40e_stat_str(hw, status), i40e_aq_str(hw, hw->aq.asq_last_status)); - return -EAGAIN; + err = -EAGAIN; + goto done; } status = i40e_update_link_info(hw); @@ -878,6 +890,9 @@ static int i40e_set_link_ksettings(struct net_device *netdev, netdev_info(netdev, "Nothing changed, exiting without setting anything.\n"); } +done: + clear_bit(__I40E_CONFIG_BUSY, &pf->state); + return err; } @@ -1292,6 +1307,7 @@ static int i40e_set_ringparam(struct net_device *netdev, struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; u32 new_rx_count, new_tx_count; + int timeout = 50; int i, err = 0; if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) @@ -1316,8 +1332,12 @@ static int i40e_set_ringparam(struct net_device *netdev, (new_rx_count == vsi->rx_rings[0]->count)) return 0; - while (test_and_set_bit(__I40E_CONFIG_BUSY, &pf->state)) + while (test_and_set_bit(__I40E_CONFIG_BUSY, &pf->state)) { + timeout--; + if (!timeout) + return -EBUSY; usleep_range(1000, 2000); + } if (!netif_running(vsi->netdev)) { /* simple case - set for the next time the netdev is started */ -- cgit From 9e3f23f44f3294f794802e3fee2ba03214451a95 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 13 Apr 2017 04:45:49 -0400 Subject: i40e: reduce wait time for adminq command completion When sending an adminq command, we wait for the command to complete in a loop. This loop waits for an entire millisecond, when in practice the adminq command is processed often much faster. Change the loop to use i40e_usec_delay instead, and wait for 50 usecs each time instead. This appears to be about the minimum time required, based on some manual observation and testing. The primary benefit of this change is reducing latency of various operations in the PF driver, especially when related to having a large number of VFs enabled. For example, on Linux, when instantiating 128 VFs, the time to finish the operation dropped from about 9 seconds down to under 6 seconds. Additionally, the time it takes to finish a PF reset with 128 VFs dropped from 5.1 seconds down to 0.7 seconds. As the examples above show, a significant portion of the delay is wasted waiting for admiqn operations which have already finished. This patch shouldn't cause impact to functionality, as we still check and keep waiting until the command does get processed. The only expected change is an increase in CPU utilization as we now check for completion far more times. However, in practice the commands appear to generally be complete within the first delay window anyways. Change-ID: If8af8388e100da0a14eaf9e1af3afadf73a958cf Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_adminq.c | 4 ++-- drivers/net/ethernet/intel/i40e/i40e_adminq.h | 2 +- drivers/net/ethernet/intel/i40evf/i40e_adminq.c | 4 ++-- drivers/net/ethernet/intel/i40evf/i40e_adminq.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.c b/drivers/net/ethernet/intel/i40e/i40e_adminq.c index 56fb27298936..ba04988e0598 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.c @@ -850,8 +850,8 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw, */ if (i40e_asq_done(hw)) break; - usleep_range(1000, 2000); - total_delay++; + udelay(50); + total_delay += 50; } while (total_delay < hw->aq.asq_cmd_timeout); } diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.h b/drivers/net/ethernet/intel/i40e/i40e_adminq.h index d92aad38afdc..2349fbe04bd2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.h @@ -151,7 +151,7 @@ static inline int i40e_aq_rc_to_posix(int aq_ret, int aq_rc) /* general information */ #define I40E_AQ_LARGE_BUF 512 -#define I40E_ASQ_CMD_TIMEOUT 250 /* msecs */ +#define I40E_ASQ_CMD_TIMEOUT 250000 /* usecs */ void i40e_fill_default_direct_cmd_desc(struct i40e_aq_desc *desc, u16 opcode); diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c index 96385156b824..8b0d4b255dea 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.c @@ -797,8 +797,8 @@ i40e_status i40evf_asq_send_command(struct i40e_hw *hw, */ if (i40evf_asq_done(hw)) break; - usleep_range(1000, 2000); - total_delay++; + udelay(50); + total_delay += 50; } while (total_delay < hw->aq.asq_cmd_timeout); } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h index 1f9b3b5d946d..e0bfaa3d4a21 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h @@ -151,7 +151,7 @@ static inline int i40e_aq_rc_to_posix(int aq_ret, int aq_rc) /* general information */ #define I40E_AQ_LARGE_BUF 512 -#define I40E_ASQ_CMD_TIMEOUT 250 /* msecs */ +#define I40E_ASQ_CMD_TIMEOUT 250000 /* usecs */ void i40evf_fill_default_direct_cmd_desc(struct i40e_aq_desc *desc, u16 opcode); -- cgit From 1de81c2d07abeed32e9cbe54bf19a79c6fc8e3ff Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 13 Apr 2017 04:45:50 -0400 Subject: i40e: remove I40E_FLAG_IN_NETPOLL entirely This flag was originally intended to be used to let some driver code know when we were running from netpoll. Ultimately this was not necessary and we never used it. Let's remove it Change-ID: I43b72483d91c1638071d2a7f389ab171ec5b796a Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 7bbd11abed08..40f56e2335df 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -202,7 +202,6 @@ struct i40evf_adapter { u32 flags; #define I40EVF_FLAG_RX_CSUM_ENABLED BIT(0) -#define I40EVF_FLAG_IN_NETPOLL BIT(4) #define I40EVF_FLAG_IMIR_ENABLED BIT(5) #define I40EVF_FLAG_MQ_CAPABLE BIT(6) #define I40EVF_FLAG_PF_COMMS_FAILED BIT(8) @@ -221,7 +220,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 From 9dc2e417383815bc6b8239ae2714d145c167b5c8 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 13 Apr 2017 04:45:51 -0400 Subject: i40e: split some code in i40e_reset_vf into helpers A future patch is going to want to re-use some of the code in i40e_reset_vf, so lets break up the beginning and ending parts into their own helper functions. The first function will be used to initialize the reset on a VF, while the second function will be used to finalize the reset and restore functionality. Change-ID: I48df808b8bf09de3c2ed8c521f57b3f0ab9e5907 Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 109 ++++++++++++++------- 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 65c95ffc15ec..9a75ce3f1564 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -923,22 +923,19 @@ static int i40e_quiesce_vf_pci(struct i40e_vf *vf) } /** - * i40e_reset_vf + * i40e_trigger_vf_reset * @vf: pointer to the VF structure * @flr: VFLR was issued or not * - * reset the VF + * Trigger hardware to start a reset for a particular VF. Expects the caller + * to wait the proper amount of time to allow hardware to reset the VF before + * it cleans up and restores VF functionality. **/ -void i40e_reset_vf(struct i40e_vf *vf, bool flr) +static void i40e_trigger_vf_reset(struct i40e_vf *vf, bool flr) { struct i40e_pf *pf = vf->pf; struct i40e_hw *hw = &pf->hw; u32 reg, reg_idx, bit_idx; - bool rsd = false; - int i; - - if (test_and_set_bit(__I40E_VF_DISABLE, &pf->state)) - return; /* warn the VF */ clear_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states); @@ -970,37 +967,22 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) if (i40e_quiesce_vf_pci(vf)) dev_err(&pf->pdev->dev, "VF %d PCI transactions stuck\n", vf->vf_id); +} - /* poll VPGEN_VFRSTAT reg to make sure - * that reset is complete - */ - for (i = 0; i < 10; i++) { - /* VF reset requires driver to first reset the VF and then - * poll the status register to make sure that the reset - * completed successfully. Due to internal HW FIFO flushes, - * we must wait 10ms before the register will be valid. - */ - usleep_range(10000, 20000); - reg = rd32(hw, I40E_VPGEN_VFRSTAT(vf->vf_id)); - if (reg & I40E_VPGEN_VFRSTAT_VFRD_MASK) { - rsd = true; - break; - } - } - - if (flr) - usleep_range(10000, 20000); - - if (!rsd) - dev_err(&pf->pdev->dev, "VF reset check timeout on VF %d\n", - vf->vf_id); - - /* On initial reset, we won't have any queues */ - if (vf->lan_vsi_idx == 0) - goto complete_reset; +/** + * i40e_cleanup_reset_vf + * @vf: pointer to the VF structure + * + * Cleanup a VF after the hardware reset is finished. Expects the caller to + * have verified whether the reset is finished properly, and ensure the + * minimum amount of wait time has passed. + **/ +static void i40e_cleanup_reset_vf(struct i40e_vf *vf) +{ + struct i40e_pf *pf = vf->pf; + struct i40e_hw *hw = &pf->hw; + u32 reg; - i40e_vsi_stop_rings(pf->vsi[vf->lan_vsi_idx]); -complete_reset: /* free VF resources to begin resetting the VSI state */ i40e_free_vf_res(vf); @@ -1035,6 +1017,59 @@ complete_reset: * request resources immediately after setting this flag. */ wr32(hw, I40E_VFGEN_RSTAT1(vf->vf_id), I40E_VFR_VFACTIVE); +} + +/** + * i40e_reset_vf + * @vf: pointer to the VF structure + * @flr: VFLR was issued or not + * + * reset the VF + **/ +void i40e_reset_vf(struct i40e_vf *vf, bool flr) +{ + struct i40e_pf *pf = vf->pf; + struct i40e_hw *hw = &pf->hw; + bool rsd = false; + u32 reg; + int i; + + /* If VFs have been disabled, there is no need to reset */ + if (test_and_set_bit(__I40E_VF_DISABLE, &pf->state)) + return; + + i40e_trigger_vf_reset(vf, flr); + + /* poll VPGEN_VFRSTAT reg to make sure + * that reset is complete + */ + for (i = 0; i < 10; i++) { + /* VF reset requires driver to first reset the VF and then + * poll the status register to make sure that the reset + * completed successfully. Due to internal HW FIFO flushes, + * we must wait 10ms before the register will be valid. + */ + usleep_range(10000, 20000); + reg = rd32(hw, I40E_VPGEN_VFRSTAT(vf->vf_id)); + if (reg & I40E_VPGEN_VFRSTAT_VFRD_MASK) { + rsd = true; + break; + } + } + + if (flr) + usleep_range(10000, 20000); + + if (!rsd) + dev_err(&pf->pdev->dev, "VF reset check timeout on VF %d\n", + vf->vf_id); + usleep_range(10000, 20000); + + /* On initial reset, we don't have any queues to disable */ + if (vf->lan_vsi_idx != 0) + i40e_vsi_stop_rings(pf->vsi[vf->lan_vsi_idx]); + + i40e_cleanup_reset_vf(vf); i40e_flush(hw); clear_bit(__I40E_VF_DISABLE, &pf->state); -- cgit From e4b433f4a74196476ccf226e450c4582428641c1 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 13 Apr 2017 04:45:52 -0400 Subject: i40e: reset all VFs in parallel when rebuilding PF When there are a lot of active VFs, it can take multiple seconds to finish resetting all of them during certain flows., which can cause some VFs to fail to wait long enough for the reset to occur. The user might see messages like "Never saw reset" or "Reset never finished" and the VF driver will stop functioning properly. The naive solution would be to simply increase the wait timer. We can get much more clever. Notice that i40e_reset_vf is run in a serialized fashion, and includes lots of delays. There are two prominent delays which take most of the time. First, when we begin resetting VFs, we have multiple 10ms delays which accrue because we reset each VF in a serial fashion. These delays accumulate to almost 4 seconds when handling the maximum number of VFs (128). Secondly, there is a massive 50ms delay for each time we disable queues on a VSI. This delay is necessary to allow HW to finish disabling queues before we restore functionality. However, just like with the first case, we are paying the cost for each VF, rather than disabling all VFs and waiting once. Both of these can be fixed, but required some previous refactoring to handle the special case. First, we will need the i40e_vsi_wait_queues_disabled function which was previously DCB specific. Second, we will need to implement our own i40e_vsi_stop_rings_no_wait function which will handle the stopping of rings without the delays. Finally, implement an i40e_reset_all_vfs function, which will first start the reset of all VFs, and pay the wait cost all at once, rather than serially waiting for each VF before we start processing then next one. After the VF has been reset, we'll disable all the VF queues, and then wait for them to disable. Again, we'll organize the flow such that we pay the wait cost only once. Finally, after we've disabled queues we'll go ahead and begin restoring VF functionality. The result is reducing the wait time by a large factor and ensuring that VFs do not timeout when waiting in the VF driver. Change-ID: Ia6e8cf8d98131b78aec89db78afb8d905c9b12be Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 2 + drivers/net/ethernet/intel/i40e/i40e_main.c | 32 +++++-- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 100 +++++++++++++++++++++ drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h | 1 + 4 files changed, 129 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index b629f3adcecb..dfab7b030b67 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -868,6 +868,8 @@ void i40e_notify_client_of_vf_msg(struct i40e_vsi *vsi, u32 vf_id, int i40e_vsi_start_rings(struct i40e_vsi *vsi); void i40e_vsi_stop_rings(struct i40e_vsi *vsi); +void i40e_vsi_stop_rings_no_wait(struct i40e_vsi *vsi); +int i40e_vsi_wait_queues_disabled(struct i40e_vsi *vsi); int i40e_reconfig_rss_queues(struct i40e_pf *pf, int queue_count); struct i40e_veb *i40e_veb_setup(struct i40e_pf *pf, u16 flags, u16 uplink_seid, u16 downlink_seid, u8 enabled_tc); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2bf5bb1b4627..2fe1fbdaafae 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4158,6 +4158,29 @@ void i40e_vsi_stop_rings(struct i40e_vsi *vsi) i40e_vsi_control_rx(vsi, false); } +/** + * i40e_vsi_stop_rings_no_wait - Stop a VSI's rings and do not delay + * @vsi: the VSI being shutdown + * + * This function stops all the rings for a VSI but does not delay to verify + * that rings have been disabled. It is expected that the caller is shutting + * down multiple VSIs at once and will delay together for all the VSIs after + * initiating the shutdown. This is particularly useful for shutting down lots + * of VFs together. Otherwise, a large delay can be incurred while configuring + * each VSI in serial. + **/ +void i40e_vsi_stop_rings_no_wait(struct i40e_vsi *vsi) +{ + struct i40e_pf *pf = vsi->back; + int i, pf_q; + + pf_q = vsi->base_queue; + for (i = 0; i < vsi->num_queue_pairs; i++, pf_q++) { + i40e_control_tx_q(pf, pf_q, false); + i40e_control_rx_q(pf, pf_q, false); + } +} + /** * i40e_vsi_free_irq - Free the irq association with the OS * @vsi: the VSI being configured @@ -4488,14 +4511,13 @@ static void i40e_pf_unquiesce_all_vsi(struct i40e_pf *pf) } } -#ifdef CONFIG_I40E_DCB /** * i40e_vsi_wait_queues_disabled - Wait for VSI's queues to be disabled * @vsi: the VSI being configured * * Wait until all queues on a given VSI have been disabled. **/ -static int i40e_vsi_wait_queues_disabled(struct i40e_vsi *vsi) +int i40e_vsi_wait_queues_disabled(struct i40e_vsi *vsi) { struct i40e_pf *pf = vsi->back; int i, pf_q, ret; @@ -4523,6 +4545,7 @@ static int i40e_vsi_wait_queues_disabled(struct i40e_vsi *vsi) return 0; } +#ifdef CONFIG_I40E_DCB /** * i40e_pf_wait_queues_disabled - Wait for all queues of PF VSIs to be disabled * @pf: the PF @@ -7151,10 +7174,7 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired) if (!lock_acquired) rtnl_unlock(); - if (pf->num_alloc_vfs) { - for (v = 0; v < pf->num_alloc_vfs; v++) - i40e_reset_vf(&pf->vf[v], true); - } + i40e_reset_all_vfs(pf, true); /* tell the firmware that we're starting */ i40e_send_version(pf); diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 9a75ce3f1564..350cba70490c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1075,6 +1075,106 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) clear_bit(__I40E_VF_DISABLE, &pf->state); } +/** + * i40e_reset_all_vfs + * @pf: pointer to the PF structure + * @flr: VFLR was issued or not + * + * Reset all allocated VFs in one go. First, tell the hardware to reset each + * VF, then do all the waiting in one chunk, and finally finish restoring each + * VF after the wait. This is useful during PF routines which need to reset + * all VFs, as otherwise it must perform these resets in a serialized fashion. + **/ +void i40e_reset_all_vfs(struct i40e_pf *pf, bool flr) +{ + struct i40e_hw *hw = &pf->hw; + struct i40e_vf *vf; + int i, v; + u32 reg; + + /* If we don't have any VFs, then there is nothing to reset */ + if (!pf->num_alloc_vfs) + return; + + /* If VFs have been disabled, there is no need to reset */ + if (test_and_set_bit(__I40E_VF_DISABLE, &pf->state)) + return; + + /* Begin reset on all VFs at once */ + for (v = 0; v < pf->num_alloc_vfs; v++) + i40e_trigger_vf_reset(&pf->vf[v], flr); + + /* HW requires some time to make sure it can flush the FIFO for a VF + * when it resets it. Poll the VPGEN_VFRSTAT register for each VF in + * sequence to make sure that it has completed. We'll keep track of + * the VFs using a simple iterator that increments once that VF has + * finished resetting. + */ + for (i = 0, v = 0; i < 10 && v < pf->num_alloc_vfs; i++) { + usleep_range(10000, 20000); + + /* Check each VF in sequence, beginning with the VF to fail + * the previous check. + */ + while (v < pf->num_alloc_vfs) { + vf = &pf->vf[v]; + reg = rd32(hw, I40E_VPGEN_VFRSTAT(vf->vf_id)); + if (!(reg & I40E_VPGEN_VFRSTAT_VFRD_MASK)) + break; + + /* If the current VF has finished resetting, move on + * to the next VF in sequence. + */ + v++; + } + } + + if (flr) + usleep_range(10000, 20000); + + /* Display a warning if at least one VF didn't manage to reset in + * time, but continue on with the operation. + */ + if (v < pf->num_alloc_vfs) + dev_err(&pf->pdev->dev, "VF reset check timeout on VF %d\n", + pf->vf[v].vf_id); + usleep_range(10000, 20000); + + /* Begin disabling all the rings associated with VFs, but do not wait + * between each VF. + */ + for (v = 0; v < pf->num_alloc_vfs; v++) { + /* On initial reset, we don't have any queues to disable */ + if (pf->vf[v].lan_vsi_idx == 0) + continue; + + i40e_vsi_stop_rings_no_wait(pf->vsi[pf->vf[v].lan_vsi_idx]); + } + + /* Now that we've notified HW to disable all of the VF rings, wait + * until they finish. + */ + for (v = 0; v < pf->num_alloc_vfs; v++) { + /* On initial reset, we don't have any queues to disable */ + if (pf->vf[v].lan_vsi_idx == 0) + continue; + + i40e_vsi_wait_queues_disabled(pf->vsi[pf->vf[v].lan_vsi_idx]); + } + + /* Hw may need up to 50ms to finish disabling the RX queues. We + * minimize the wait by delaying only once for all VFs. + */ + mdelay(50); + + /* Finish the reset on each VF */ + for (v = 0; v < pf->num_alloc_vfs; v++) + i40e_cleanup_reset_vf(&pf->vf[v]); + + i40e_flush(hw); + clear_bit(__I40E_VF_DISABLE, &pf->state); +} + /** * i40e_free_vfs * @pf: pointer to the PF structure diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h index 37af437daa5d..9495f1422122 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h @@ -124,6 +124,7 @@ int i40e_vc_process_vf_msg(struct i40e_pf *pf, s16 vf_id, u32 v_opcode, u32 v_retval, u8 *msg, u16 msglen); int i40e_vc_process_vflr_event(struct i40e_pf *pf); void i40e_reset_vf(struct i40e_vf *vf, bool flr); +void i40e_reset_all_vfs(struct i40e_pf *pf, bool flr); void i40e_vc_notify_vf_reset(struct i40e_vf *vf); /* VF configuration related iplink handlers */ -- cgit From 3480756f2cb93c9245e831a4f46ff6ed19c41031 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 13 Apr 2017 04:45:53 -0400 Subject: i40e: use i40e_stop_rings_no_wait to implement PORT_SUSPENDED state This state bit was added as a way for DCB to avoid having to wait for the queues to disable when handling LLDP events. The logic for this was burried deep within stop Tx and stop Rx queue code. First, let's rename it so that it does not appear to only affect Tx when infact it modifies both Tx and Rx flow. Second we can move it up into the i40e_stop_rings() function, and we can simply re-use the i40e_stop_rings_no_wait() so that we don't have to bury the implementation as deep into the call stack. An alternative might be to remove the state bit and instead attempt to shut down everything directly in DCP flow. This, however, is not ideal because it creates yet another separate shutdown routine that we'd have to maintain. In the current implementation any changes will be made to both flows. Change-ID: I68e1ccb901af320862bca395e9c9746f08e8b17c Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 2 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index dfab7b030b67..70f9458f7a01 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -145,7 +145,7 @@ enum i40e_state_t { __I40E_DOWN_REQUESTED, __I40E_FD_FLUSH_REQUESTED, __I40E_RESET_FAILED, - __I40E_PORT_TX_SUSPENDED, + __I40E_PORT_SUSPENDED, __I40E_VF_DISABLE, }; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2fe1fbdaafae..c001562f19b2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -4008,10 +4008,6 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable) for (i = 0; i < vsi->num_queue_pairs; i++, pf_q++) { i40e_control_tx_q(pf, pf_q, enable); - /* Don't wait to disable when port Tx is suspended */ - if (!enable && test_bit(__I40E_PORT_TX_SUSPENDED, &pf->state)) - continue; - /* wait for the change to finish */ ret = i40e_pf_txq_wait(pf, pf_q, enable); if (ret) { @@ -4105,10 +4101,6 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable) for (i = 0; i < vsi->num_queue_pairs; i++, pf_q++) { i40e_control_rx_q(pf, pf_q, enable); - /* Don't wait to disable when port Tx is suspended */ - if (!enable && test_bit(__I40E_PORT_TX_SUSPENDED, &pf->state)) - continue; - /* wait for the change to finish */ ret = i40e_pf_rxq_wait(pf, pf_q, enable); if (ret) { @@ -4151,6 +4143,10 @@ int i40e_vsi_start_rings(struct i40e_vsi *vsi) **/ void i40e_vsi_stop_rings(struct i40e_vsi *vsi) { + /* When port TX is suspended, don't wait */ + if (test_bit(__I40E_PORT_SUSPENDED, &vsi->back->state)) + return i40e_vsi_stop_rings_no_wait(vsi); + /* do rx first for enable and last for disable * Ignore return value, we need to shutdown whatever we can */ @@ -5948,7 +5944,7 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf, else pf->flags &= ~I40E_FLAG_DCB_ENABLED; - set_bit(__I40E_PORT_TX_SUSPENDED, &pf->state); + set_bit(__I40E_PORT_SUSPENDED, &pf->state); /* Reconfiguration needed quiesce all VSIs */ i40e_pf_quiesce_all_vsi(pf); @@ -5957,7 +5953,7 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf, ret = i40e_resume_port_tx(pf); - clear_bit(__I40E_PORT_TX_SUSPENDED, &pf->state); + clear_bit(__I40E_PORT_SUSPENDED, &pf->state); /* In case of error no point in resuming VSIs */ if (ret) goto exit; -- cgit From 9b1ea16763d37080892e4425831ca9c6298f4f5c Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 19 Feb 2017 17:00:58 +0200 Subject: iwlwifi: mvm: allow block ack response without data When FW fails to get block ack, it will send the notification with 0 items in the TFD queue elements. Allow this and handle accordingly. Fixes: c46e7724bfe9 ("iwlwifi: mvm: support new BA notification response") Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index ed7ee5004645..8f737f6cdd80 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1785,6 +1785,9 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) ba_info.status.status_driver_data[0] = (void *)(uintptr_t)ba_res->reduced_txp; + if (!le16_to_cpu(ba_res->tfd_cnt)) + goto out; + /* * TODO: * When supporting multi TID aggregations - we need to move @@ -1802,6 +1805,7 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) le16_to_cpu(ba_res->tfd[0].tfd_index), &ba_info, le32_to_cpu(ba_res->tx_rate)); +out: IWL_DEBUG_TX_REPLY(mvm, "BA_NOTIFICATION Received from sta_id = %d, flags %x, sent:%d, acked:%d\n", sta_id, le32_to_cpu(ba_res->flags), -- cgit From 97b00d877bb8c2e499b9d0c07b65bd92b4e91ddd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 19 Apr 2017 10:26:02 +0200 Subject: iwlwifi: pcie: fix mutex leak in gen2 start If the context info fails to be allocated, the mutex isn't unlocked properly, fix that. Fixes: eda50cde58de ("iwlwifi: pcie: add context information support") Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index ef8f563a48d9..ac60a282d6de 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -359,8 +359,9 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, goto out; } - if (iwl_pcie_ctxt_info_init(trans, fw)) - return -ENOMEM; + ret = iwl_pcie_ctxt_info_init(trans, fw); + if (ret) + goto out; /* re-check RF-Kill state since we may have missed the interrupt */ hw_rfkill = iwl_trans_check_hw_rf_kill(trans); -- cgit From 718ceb22a0ccd0b3a02a4bd401d944a1eb16f3ce Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 19 Apr 2017 10:30:47 +0200 Subject: iwlwifi: pcie: free context info in case of failures If iwl_pcie_ctxt_info_init_fw_sec() fails, the previous allocated DMA memory needs to be freed (it even goes out of scope immediately.) Do that to prevent the leak. Fixes: eda50cde58de ("iwlwifi: pcie: add context information support") Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c index 854d61888f4d..1d95512361b2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c @@ -241,8 +241,11 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, /* allocate ucode sections in dram and set addresses */ ret = iwl_pcie_ctxt_info_init_fw_sec(trans, fw, ctxt_info); - if (ret) + if (ret) { + dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info), + ctxt_info, trans_pcie->ctxt_info_dma_addr); return ret; + } trans_pcie->ctxt_info = ctxt_info; -- cgit From 7e2f18f06408ff56d7f75e68de8064777137b319 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 5 Apr 2017 15:26:40 -0700 Subject: mwifiex: MAC randomization should not be persistent nl80211 provides the NL80211_SCAN_FLAG_RANDOM_ADDR for every scan request that should be randomized; the absence of such a flag means we should not randomize. However, mwifiex was stashing the latest randomization request and *always* using it for future scans, even those that didn't set the flag. Let's zero out the randomization info whenever we get a scan request without NL80211_SCAN_FLAG_RANDOM_ADDR. I'd prefer to remove priv->random_mac entirely (and plumb the randomization MAC properly through the call sequence), but the spaghetti is a little difficult to unravel here for me. Fixes: c2a8f0ff9c6c ("mwifiex: support random MAC address for scanning") Cc: # 4.9+ Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 44d06177859e..410766313065 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -2547,9 +2547,11 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, priv->random_mac[i] |= get_random_int() & ~(request->mac_addr_mask[i]); } + ether_addr_copy(user_scan_cfg->random_mac, priv->random_mac); + } else { + eth_zero_addr(priv->random_mac); } - ether_addr_copy(user_scan_cfg->random_mac, priv->random_mac); user_scan_cfg->num_ssids = request->n_ssids; user_scan_cfg->ssid_list = request->ssids; -- cgit From 625b4dba57b256943a1714c7783e3cfbb52ce5c3 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Thu, 13 Apr 2017 06:48:19 +0000 Subject: mwifiex: remove unnecessary wakeup interrupt number sanity check If wakeup interrupt handler is called, we know that the wakeup interrupt number is valid, there is no need to check it. Signed-off-by: Xinming Hu Signed-off-by: Cathy Luo Reviewed-by: Dmitry Torokhov Reviewed-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 912b687f4671..3d59d74c7f99 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -1514,11 +1514,9 @@ static irqreturn_t mwifiex_irq_wakeup_handler(int irq, void *priv) { struct mwifiex_adapter *adapter = priv; - if (adapter->irq_wakeup >= 0) { - dev_dbg(adapter->dev, "%s: wake by wifi", __func__); - adapter->wake_by_wifi = true; - disable_irq_nosync(irq); - } + dev_dbg(adapter->dev, "%s: wake by wifi", __func__); + adapter->wake_by_wifi = true; + disable_irq_nosync(irq); /* Notify PM core we are wakeup source */ pm_wakeup_event(adapter->dev, 0); -- cgit From ef6c7d3cb731e3aa6a4ce8d501b97df73ea40b4e Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Thu, 13 Apr 2017 06:48:20 +0000 Subject: mwifiex: fall back mwifiex_dbg to pr_info when adapter->dev not set mwifiex_dbg will do nothing before adapter->dev get assigned. several logs lost in this case. it can be avoided by fall back to pr_info. Signed-off-by: Xinming Hu Reviewed-by: Brian Norris Reviewed-by: Dmitry Torokhov Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 3d59d74c7f99..cd21bbf4412f 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -1753,7 +1753,7 @@ void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, struct va_format vaf; va_list args; - if (!adapter->dev || !(adapter->debug_mask & mask)) + if (!(adapter->debug_mask & mask)) return; va_start(args, fmt); @@ -1761,7 +1761,10 @@ void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, vaf.fmt = fmt; vaf.va = &args; - dev_info(adapter->dev, "%pV", &vaf); + if (adapter->dev) + dev_info(adapter->dev, "%pV", &vaf); + else + pr_info("%pV", &vaf); va_end(args); } -- cgit From 127ee1db09707856dc4915afc60886561644585d Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Thu, 13 Apr 2017 06:48:21 +0000 Subject: mwifiex: pcie: correct scratch register name This patch correct pcie scratch register name, to keep the same with chipset side definition. Signed-off-by: Xinming Hu Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 4 ++-- drivers/net/wireless/marvell/mwifiex/pcie.h | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 92ffb1ba17e6..061223149bed 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -2494,8 +2494,8 @@ mwifiex_pcie_reg_dump(struct mwifiex_adapter *adapter, char *drv_buf) struct pcie_service_card *card = adapter->card; const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; int pcie_scratch_reg[] = {PCIE_SCRATCH_12_REG, - PCIE_SCRATCH_13_REG, - PCIE_SCRATCH_14_REG}; + PCIE_SCRATCH_14_REG, + PCIE_SCRATCH_15_REG}; if (!p) return 0; diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h index 00e8ee5ad4a8..7e2450ce79d3 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie.h @@ -77,8 +77,9 @@ #define PCIE_SCRATCH_10_REG 0xCE8 #define PCIE_SCRATCH_11_REG 0xCEC #define PCIE_SCRATCH_12_REG 0xCF0 -#define PCIE_SCRATCH_13_REG 0xCF8 -#define PCIE_SCRATCH_14_REG 0xCFC +#define PCIE_SCRATCH_13_REG 0xCF4 +#define PCIE_SCRATCH_14_REG 0xCF8 +#define PCIE_SCRATCH_15_REG 0xCFC #define PCIE_RD_DATA_PTR_Q0_Q1 0xC08C #define PCIE_WR_DATA_PTR_Q0_Q1 0xC05C @@ -217,8 +218,8 @@ static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = { .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, .pfu_enabled = 1, .sleep_cookie = 0, - .fw_dump_ctrl = 0xcf4, - .fw_dump_start = 0xcf8, + .fw_dump_ctrl = PCIE_SCRATCH_13_REG, + .fw_dump_start = PCIE_SCRATCH_14_REG, .fw_dump_end = 0xcff, .fw_dump_host_ready = 0xee, .fw_dump_read_done = 0xfe, @@ -254,8 +255,8 @@ static const struct mwifiex_pcie_card_reg mwifiex_reg_8997 = { .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, .pfu_enabled = 1, .sleep_cookie = 0, - .fw_dump_ctrl = 0xcf4, - .fw_dump_start = 0xcf8, + .fw_dump_ctrl = PCIE_SCRATCH_13_REG, + .fw_dump_start = PCIE_SCRATCH_14_REG, .fw_dump_end = 0xcff, .fw_dump_host_ready = 0xcc, .fw_dump_read_done = 0xdd, -- cgit From efde6648a618025a8fe1bc550d7ba569e44dc2fe Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Thu, 13 Apr 2017 13:10:33 -0700 Subject: mwifiex: pcie: extract wifi part from combo firmware during function level reset A separate wifi-only firmware was download during pcie function level reset. It is in fact the tail part of wifi/bt combo firmware. Per Brian's and Dmitry's suggestion, this patch extract the wifi part from combo firmware. After that, the mrvl/pcie8997_wlan_v4.bin image in linux-firmware repo is redundant (though I guess we keep it around to support older kernels). Signed-off-by: Xinming Hu Signed-off-by: Ganapathi Bhat Signed-off-by: Cathy Luo Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/fw.h | 18 +++++ drivers/net/wireless/marvell/mwifiex/pcie.c | 114 ++++++++++++++++++++++++++-- drivers/net/wireless/marvell/mwifiex/pcie.h | 3 +- 3 files changed, 127 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index 0b683742e30c..6cf9ab9133ea 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -43,6 +43,24 @@ struct tx_packet_hdr { struct rfc_1042_hdr rfc1042_hdr; } __packed; +struct mwifiex_fw_header { + __le32 dnld_cmd; + __le32 base_addr; + __le32 data_length; + __le32 crc; +} __packed; + +struct mwifiex_fw_data { + struct mwifiex_fw_header header; + __le32 seq_num; + u8 data[1]; +} __packed; + +#define MWIFIEX_FW_DNLD_CMD_1 0x1 +#define MWIFIEX_FW_DNLD_CMD_5 0x5 +#define MWIFIEX_FW_DNLD_CMD_6 0x6 +#define MWIFIEX_FW_DNLD_CMD_7 0x7 + #define B_SUPPORTED_RATES 5 #define G_SUPPORTED_RATES 9 #define BG_SUPPORTED_RATES 13 diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 061223149bed..63102efb388e 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -1956,6 +1956,94 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, return ret; } +/* Combo firmware image is a combination of + * (1) combo crc heaer, start with CMD5 + * (2) bluetooth image, start with CMD7, end with CMD6, data wrapped in CMD1. + * (3) wifi image. + * + * This function bypass the header and bluetooth part, return + * the offset of tail wifi-only part. + */ + +static int mwifiex_extract_wifi_fw(struct mwifiex_adapter *adapter, + const void *firmware, u32 firmware_len) { + const struct mwifiex_fw_data *fwdata; + u32 offset = 0, data_len, dnld_cmd; + int ret = 0; + bool cmd7_before = false; + + while (1) { + /* Check for integer and buffer overflow */ + if (offset + sizeof(fwdata->header) < sizeof(fwdata->header) || + offset + sizeof(fwdata->header) >= firmware_len) { + mwifiex_dbg(adapter, ERROR, + "extract wifi-only fw failure!\n"); + ret = -1; + goto done; + } + + fwdata = firmware + offset; + dnld_cmd = le32_to_cpu(fwdata->header.dnld_cmd); + data_len = le32_to_cpu(fwdata->header.data_length); + + /* Skip past header */ + offset += sizeof(fwdata->header); + + switch (dnld_cmd) { + case MWIFIEX_FW_DNLD_CMD_1: + if (!cmd7_before) { + mwifiex_dbg(adapter, ERROR, + "no cmd7 before cmd1!\n"); + ret = -1; + goto done; + } + if (offset + data_len < data_len) { + mwifiex_dbg(adapter, ERROR, "bad FW parse\n"); + ret = -1; + goto done; + } + offset += data_len; + break; + case MWIFIEX_FW_DNLD_CMD_5: + /* Check for integer overflow */ + if (offset + data_len < data_len) { + mwifiex_dbg(adapter, ERROR, "bad FW parse\n"); + ret = -1; + goto done; + } + offset += data_len; + break; + case MWIFIEX_FW_DNLD_CMD_6: + /* Check for integer overflow */ + if (offset + data_len < data_len) { + mwifiex_dbg(adapter, ERROR, "bad FW parse\n"); + ret = -1; + goto done; + } + offset += data_len; + if (offset >= firmware_len) { + mwifiex_dbg(adapter, ERROR, + "extract wifi-only fw failure!\n"); + ret = -1; + } else { + ret = offset; + } + goto done; + case MWIFIEX_FW_DNLD_CMD_7: + cmd7_before = true; + break; + default: + mwifiex_dbg(adapter, ERROR, "unknown dnld_cmd %d\n", + dnld_cmd); + ret = -1; + goto done; + } + } + +done: + return ret; +} + /* * This function downloads the firmware to the card. * @@ -1971,7 +2059,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, u32 firmware_len = fw->fw_len; u32 offset = 0; struct sk_buff *skb; - u32 txlen, tx_blocks = 0, tries, len; + u32 txlen, tx_blocks = 0, tries, len, val; u32 block_retry_cnt = 0; struct pcie_service_card *card = adapter->card; const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; @@ -1998,6 +2086,24 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, goto done; } + ret = mwifiex_read_reg(adapter, PCIE_SCRATCH_13_REG, &val); + if (ret) { + mwifiex_dbg(adapter, FATAL, "Failed to read scratch register 13\n"); + goto done; + } + + /* PCIE FLR case: extract wifi part from combo firmware*/ + if (val == MWIFIEX_PCIE_FLR_HAPPENS) { + ret = mwifiex_extract_wifi_fw(adapter, firmware, firmware_len); + if (ret < 0) { + mwifiex_dbg(adapter, ERROR, "Failed to extract wifi fw\n"); + goto done; + } + offset = ret; + mwifiex_dbg(adapter, MSG, + "info: dnld wifi firmware from %d bytes\n", offset); + } + /* Perform firmware data transfer */ do { u32 ireg_intr = 0; @@ -3070,12 +3176,6 @@ static void mwifiex_pcie_up_dev(struct mwifiex_adapter *adapter) struct pci_dev *pdev = card->dev; const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - /* Bluetooth is not on pcie interface. Download Wifi only firmware - * during pcie FLR, so that bluetooth part of firmware which is - * already running doesn't get affected. - */ - strcpy(adapter->fw_name, PCIE8997_DEFAULT_WIFIFW_NAME); - /* tx_buf_size might be changed to 3584 by firmware during * data transfer, we should reset it to default size. */ diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h index 7e2450ce79d3..f7ce9b6db6b4 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie.h @@ -35,7 +35,6 @@ #define PCIE8897_B0_FW_NAME "mrvl/pcie8897_uapsta.bin" #define PCIEUART8997_FW_NAME_V4 "mrvl/pcieuart8997_combo_v4.bin" #define PCIEUSB8997_FW_NAME_V4 "mrvl/pcieusb8997_combo_v4.bin" -#define PCIE8997_DEFAULT_WIFIFW_NAME "mrvl/pcie8997_wlan_v4.bin" #define PCIE_VENDOR_ID_MARVELL (0x11ab) #define PCIE_VENDOR_ID_V2_MARVELL (0x1b4b) @@ -120,6 +119,8 @@ #define MWIFIEX_SLEEP_COOKIE_SIZE 4 #define MWIFIEX_MAX_DELAY_COUNT 100 +#define MWIFIEX_PCIE_FLR_HAPPENS 0xFEDCBABA + struct mwifiex_pcie_card_reg { u16 cmd_addr_lo; u16 cmd_addr_hi; -- cgit From 3c8cb9ad032d737b874e402c59eb51e3c991a144 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 14 Apr 2017 14:51:17 -0700 Subject: mwifiex: pcie: fix cmd_buf use-after-free in remove/reset Command buffers (skb's) are allocated by the main driver, and freed upon the last use. That last use is often in mwifiex_free_cmd_buffer(). In the meantime, if the command buffer gets used by the PCI driver, we map it as DMA-able, and store the mapping information in the 'cb' memory. However, if a command was in-flight when resetting the device (and therefore was still mapped), we don't get a chance to unmap this memory until after the core has cleaned up its command handling. Let's keep a refcount within the PCI driver, so we ensure the memory only gets freed after we've finished unmapping it. Noticed by KASAN when forcing a reset via: echo 1 > /sys/bus/pci/.../reset The same code path can presumably be exercised in remove() and shutdown(). [ 205.390377] mwifiex_pcie 0000:01:00.0: info: shutdown mwifiex... [ 205.400393] ================================================================== [ 205.407719] BUG: KASAN: use-after-free in mwifiex_unmap_pci_memory.isra.14+0x4c/0x100 [mwifiex_pcie] at addr ffffffc0ad471b28 [ 205.419040] Read of size 16 by task bash/1913 [ 205.423421] ============================================================================= [ 205.431625] BUG skbuff_head_cache (Tainted: G B ): kasan: bad access detected [ 205.439815] ----------------------------------------------------------------------------- [ 205.439815] [ 205.449534] INFO: Allocated in __build_skb+0x48/0x114 age=1311 cpu=4 pid=1913 [ 205.456709] alloc_debug_processing+0x124/0x178 [ 205.461282] ___slab_alloc.constprop.58+0x528/0x608 [ 205.466196] __slab_alloc.isra.54.constprop.57+0x44/0x54 [ 205.471542] kmem_cache_alloc+0xcc/0x278 [ 205.475497] __build_skb+0x48/0x114 [ 205.479019] __netdev_alloc_skb+0xe0/0x170 [ 205.483244] mwifiex_alloc_cmd_buffer+0x68/0xdc [mwifiex] [ 205.488759] mwifiex_init_fw+0x40/0x6cc [mwifiex] [ 205.493584] _mwifiex_fw_dpc+0x158/0x520 [mwifiex] [ 205.498491] mwifiex_reinit_sw+0x2c4/0x398 [mwifiex] [ 205.503510] mwifiex_pcie_reset_notify+0x114/0x15c [mwifiex_pcie] [ 205.509643] pci_reset_notify+0x5c/0x6c [ 205.513519] pci_reset_function+0x6c/0x7c [ 205.517567] reset_store+0x68/0x98 [ 205.521003] dev_attr_store+0x54/0x60 [ 205.524705] sysfs_kf_write+0x9c/0xb0 [ 205.528413] INFO: Freed in __kfree_skb+0xb0/0xbc age=131 cpu=4 pid=1913 [ 205.535064] free_debug_processing+0x264/0x370 [ 205.539550] __slab_free+0x84/0x40c [ 205.543075] kmem_cache_free+0x1c8/0x2a0 [ 205.547030] __kfree_skb+0xb0/0xbc [ 205.550465] consume_skb+0x164/0x178 [ 205.554079] __dev_kfree_skb_any+0x58/0x64 [ 205.558304] mwifiex_free_cmd_buffer+0xa0/0x158 [mwifiex] [ 205.563817] mwifiex_shutdown_drv+0x578/0x5c4 [mwifiex] [ 205.569164] mwifiex_shutdown_sw+0x178/0x310 [mwifiex] [ 205.574353] mwifiex_pcie_reset_notify+0xd4/0x15c [mwifiex_pcie] [ 205.580398] pci_reset_notify+0x5c/0x6c [ 205.584274] pci_dev_save_and_disable+0x24/0x6c [ 205.588837] pci_reset_function+0x30/0x7c [ 205.592885] reset_store+0x68/0x98 [ 205.596324] dev_attr_store+0x54/0x60 [ 205.600017] sysfs_kf_write+0x9c/0xb0 ... [ 205.800488] Call trace: [ 205.802980] [] dump_backtrace+0x0/0x190 [ 205.808415] [] show_stack+0x20/0x28 [ 205.813506] [] dump_stack+0xa4/0xcc [ 205.818598] [] print_trailer+0x158/0x168 [ 205.824120] [] object_err+0x4c/0x5c [ 205.829210] [] kasan_report+0x334/0x500 [ 205.834641] [] check_memory_region+0x20/0x14c [ 205.840593] [] __asan_loadN+0x14/0x1c [ 205.845879] [] mwifiex_unmap_pci_memory.isra.14+0x4c/0x100 [mwifiex_pcie] [ 205.854282] [] mwifiex_pcie_delete_cmdrsp_buf+0x94/0xa8 [mwifiex_pcie] [ 205.862421] [] mwifiex_pcie_free_buffers+0x11c/0x158 [mwifiex_pcie] [ 205.870302] [] mwifiex_pcie_down_dev+0x70/0x80 [mwifiex_pcie] [ 205.877736] [] mwifiex_shutdown_sw+0x190/0x310 [mwifiex] [ 205.884658] [] mwifiex_pcie_reset_notify+0xd4/0x15c [mwifiex_pcie] [ 205.892446] [] pci_reset_notify+0x5c/0x6c [ 205.898048] [] pci_dev_save_and_disable+0x24/0x6c [ 205.904350] [] pci_reset_function+0x30/0x7c [ 205.910134] [] reset_store+0x68/0x98 [ 205.915312] [] dev_attr_store+0x54/0x60 [ 205.920750] [] sysfs_kf_write+0x9c/0xb0 [ 205.926182] [] kernfs_fop_write+0x184/0x1f8 [ 205.931963] [] __vfs_write+0x6c/0x17c [ 205.937221] [] vfs_write+0xf0/0x1c4 [ 205.942310] [] SyS_write+0x78/0xd8 [ 205.947312] [] el0_svc_naked+0x24/0x28 ... [ 205.998268] ================================================================== This bug has been around in different forms for a while. It was sort of noticed in commit 955ab095c51a ("mwifiex: Do not kfree cmd buf while unregistering PCIe"), but it just fixed the double-free, without acknowledging the potential for use-after-free. Fixes: fc3314609047 ("mwifiex: use pci_alloc/free_consistent APIs for PCIe") Cc: Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 63102efb388e..d44bd3faed0e 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -1035,6 +1035,7 @@ static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) if (card && card->cmd_buf) { mwifiex_unmap_pci_memory(adapter, card->cmd_buf, PCI_DMA_TODEVICE); + dev_kfree_skb_any(card->cmd_buf); } return 0; } @@ -1601,6 +1602,11 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) return -1; card->cmd_buf = skb; + /* + * Need to keep a reference, since core driver might free up this + * buffer before we've unmapped it. + */ + skb_get(skb); /* To send a command, the driver will: 1. Write the 64bit physical address of the data buffer to @@ -1703,6 +1709,7 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) if (card->cmd_buf) { mwifiex_unmap_pci_memory(adapter, card->cmd_buf, PCI_DMA_TODEVICE); + dev_kfree_skb_any(card->cmd_buf); card->cmd_buf = NULL; } -- cgit From 9ae3fbd109d9d89c442a3412e41fc532a9829ea8 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 14 Apr 2017 14:51:18 -0700 Subject: mwifiex: reset timeout flag when resetting device If we reset because of a command timeout, we should reset this flag. Otherwise, we might erroneously think the next command after reset is timing out, and trigger another reset. The above behavior effectively neuters the automatic card_reset() behavior, as it means we will never recover from a command timeout properly (and in fact, we might enter an infinite loop: timeout -> reset -> (fake) timeout -> reset -> ... This fixes a bug introduced with introduction of PCIe function level reset support, but it was carried into the SDIO driver when it was converted to use the same codepaths. And this is currently mostly a problem only in the SDIO driver, because it's the only one with automatic card_reset() support (e.g., on command timeout). But it will be a problem for PCIe too, as I'm working on supporting automatic card_reset() for PCIe. Fixes: c742e623e941 ("mwifiex: sdio card reset enhancement") Fixes: 4c5dae59d2e9 ("mwifiex: add PCIe function level reset support") Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index cd21bbf4412f..fdeb0bbb884c 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -1441,6 +1441,7 @@ mwifiex_reinit_sw(struct mwifiex_adapter *adapter) init_waitqueue_head(&adapter->init_wait_q); adapter->is_suspended = false; adapter->hs_activated = false; + adapter->is_cmd_timedout = 0; init_waitqueue_head(&adapter->hs_activate_wait_q); init_waitqueue_head(&adapter->cmd_wait_q.wait); adapter->cmd_wait_q.status = 0; -- cgit From 35e67d3d58b955fe35d78ce439b4fa996a9eadcd Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 14 Apr 2017 14:51:19 -0700 Subject: mwifiex: pcie: clear outstanding work when resetting When we shut down the device (i.e., during 'reset'), we cancel any outstanding work, but we don't clear any work-related flags. This can cause problems if, e.g., we begin to queue a new firmware dump or card reset while the other one is in progress. That might leave work_flags with a stale value, and we might begin one of these *after* we've completely reset the device. That doesn't make sense, because all firmware context will have been lost by then. This fixes some forms of cascading failures, where I: (a) force a firmware dump (cat /sys/kernel/debug/mwifiex/mlan0/device_dump) (b) run a Wifi scan in parallel (iw mlan0 scan) (c) the scan times out due to (a) hogging the interface (d) the command timeout triggers another firmware dump and a reset [*] (e) the 2nd firmware dump flag persists across the reset (f) as soon as the interface comes back up, we trigger the pending firmware dump (g) subsequent commands time out again, while we are processing the firmware dump; return to (d) [*] Note that automatic card_reset() support is not yet implemented for the mwifiex PCIe driver, so we won't hit *exactly* this behavior yet. But we can see similarly-confusing behaviors today. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index d44bd3faed0e..ac62bce50e96 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -371,6 +371,8 @@ static void mwifiex_pcie_reset_notify(struct pci_dev *pdev, bool prepare) */ mwifiex_shutdown_sw(adapter); adapter->surprise_removed = true; + clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &card->work_flags); + clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags); } else { /* Kernel stores and restores PCIe function context before and * after performing FLR respectively. Reconfigure the software -- cgit From fb9e67bee3ab7111513130c516ffe378d885c0d0 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 14 Apr 2017 14:51:20 -0700 Subject: mwifiex: don't leak 'chan_stats' on reset 'chan_stats' is (re)allocated in _mwifiex_fw_dpc() -> mwifiex_init_channel_scan_gap(), which is called whenever the device is initialized -- at probe or at reset. But we only free it in we completely unregister the adapter, meaning we leak a copy of it during every reset. Let's free it in the shutdown / removal paths instead (and in the error-handling path), to avoid the leak. Ideally, we can eventually unify much of mwifiex_shutdown_sw() and mwifiex_remove_card() (way too much copy-and-paste) to reduce the burden on bugfixes like this. But that's work for tomorrow. Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index fdeb0bbb884c..739d654bc9a6 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -149,7 +149,6 @@ static int mwifiex_unregister(struct mwifiex_adapter *adapter) kfree(adapter->regd); - vfree(adapter->chan_stats); kfree(adapter); return 0; } @@ -633,6 +632,7 @@ static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context) goto done; err_add_intf: + vfree(adapter->chan_stats); wiphy_unregister(adapter->wiphy); wiphy_free(adapter->wiphy); err_init_fw: @@ -1417,6 +1417,7 @@ mwifiex_shutdown_sw(struct mwifiex_adapter *adapter) mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev); rtnl_unlock(); } + vfree(adapter->chan_stats); mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); exit_return: @@ -1726,6 +1727,7 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter) mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev); rtnl_unlock(); } + vfree(adapter->chan_stats); wiphy_unregister(adapter->wiphy); wiphy_free(adapter->wiphy); -- cgit From bf30171b22c56567d44117592ec376aef1e47a6f Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 19 Apr 2017 10:12:35 -0700 Subject: MAINTAINERS: update Amitkumar's email address His email is bouncing, and he'd like to use this new one. Cc: Amitkumar Karwar Cc: Nishant Sarmukadam Cc: Ganapathi Bhat Cc: Xinming Hu Signed-off-by: Brian Norris Signed-off-by: Kalle Valo --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5397f54af5fc..2efb7df5f879 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7857,7 +7857,7 @@ S: Maintained F: drivers/net/ethernet/marvell/mvneta.* MARVELL MWIFIEX WIRELESS DRIVER -M: Amitkumar Karwar +M: Amitkumar Karwar M: Nishant Sarmukadam M: Ganapathi Bhat M: Xinming Hu -- cgit From 930d2bf242d6abdfe8226a3b585007127fbb0f99 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:06:52 -0500 Subject: rtlwifi: btcoex: 21a 2ant: limit rx aggregation size to avoid bt interrupt Larger packets have higher opportunity to be interrupt by bt signal. In order to shorten the transmission time, control the packet aggregation size. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index f36dab9291b1..3e671d409e61 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -244,6 +244,27 @@ static u8 btc8821a2ant_wifi_rssi_state(struct btc_coexist *btcoexist, return wifi_rssi_state; } +static +void btc8821a2ant_limited_rx(struct btc_coexist *btcoexist, bool force_exec, + bool rej_ap_agg_pkt, bool bt_ctrl_agg_buf_size, + u8 agg_buf_size) +{ + bool reject_rx_agg = rej_ap_agg_pkt; + bool bt_ctrl_rx_agg_size = bt_ctrl_agg_buf_size; + u8 rx_agg_size = agg_buf_size; + + /* Rx Aggregation related setting */ + btcoexist->btc_set(btcoexist, BTC_SET_BL_TO_REJ_AP_AGG_PKT, + &reject_rx_agg); + /* decide BT control aggregation buf size or not */ + btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_CTRL_AGG_SIZE, + &bt_ctrl_rx_agg_size); + /* aggregation buf size, works when BT control Rx aggregation size */ + btcoexist->btc_set(btcoexist, BTC_SET_U1_AGG_BUF_SIZE, &rx_agg_size); + /* real update aggregation setting */ + btcoexist->btc_set(btcoexist, BTC_SET_ACT_AGGREGATE_CTRL, NULL); +} + static void btc8821a2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -1141,6 +1162,8 @@ static bool btc8821a2ant_is_common_action(struct btc_coexist *btcoexist) low_pwr_disable = true; btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, + 0x8); RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi IPS + BT LPS!!\n"); @@ -1447,6 +1470,7 @@ static void btc8821a2ant_action_sco(struct btc_coexist *btcoexist) 15, 0); bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 4); if (BTC_RSSI_HIGH(bt_rssi_state)) @@ -1526,6 +1550,7 @@ static void btc8821a2ant_action_hid(struct btc_coexist *btcoexist) wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) @@ -1816,6 +1841,7 @@ static void btc8821a2ant_action_pan_hs(struct btc_coexist *btcoexist) wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); @@ -1959,6 +1985,7 @@ static void btc8821a2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) @@ -2038,6 +2065,7 @@ static void btc8821a2ant_act_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) -- cgit From 4776d349071d6cf85886e85721f71599b832bf72 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:06:53 -0500 Subject: rtlwifi: btcoex: 21a 2ant: monitor if bt is slave or not We monitor the packet counter to guess if the bt is slave or not, and when bt is slave, it may receive packet at any time, so we will have to take care about it Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 3e671d409e61..7f685a02d865 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -268,6 +268,7 @@ void btc8821a2ant_limited_rx(struct btc_coexist *btcoexist, bool force_exec, static void btc8821a2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; u32 reg_hp_txrx, reg_lp_txrx, u4tmp; u32 reg_hp_tx = 0, reg_hp_rx = 0, reg_lp_tx = 0, reg_lp_rx = 0; @@ -287,6 +288,13 @@ static void btc8821a2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) coex_sta->low_priority_tx = reg_lp_tx; coex_sta->low_priority_rx = reg_lp_rx; + if ((coex_sta->low_priority_rx >= 950) && + (coex_sta->low_priority_rx >= coex_sta->low_priority_tx) && + (!coex_sta->under_ips)) + bt_link_info->slave_role = true; + else + bt_link_info->slave_role = false; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], High Priority Tx/Rx (reg 0x%x) = 0x%x(%d)/0x%x(%d)\n", reg_hp_txrx, reg_hp_tx, reg_hp_tx, reg_hp_rx, reg_hp_rx); -- cgit From eebc58782eff0253934b834934ab5ba385e5390e Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:06:54 -0500 Subject: rtlwifi: btcoex: 21a 2ant: monitor wifi counter to check network status If there are a lot of low-rate packets, then the connection of wifi is unstable. If so, we should switch resource to bt to have a higher transmission quality, or wifi resource will be wasted Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 38 ++++++++++++++++++++++ .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.h | 10 ++++++ 2 files changed, 48 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 7f685a02d865..e23670a23e4d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -306,6 +306,43 @@ static void btc8821a2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc); } +static void btc8821a2ant_monitor_wifi_ctr(struct btc_coexist *btcoexist) +{ + if (coex_sta->under_ips) { + coex_sta->crc_ok_cck = 0; + coex_sta->crc_ok_11g = 0; + coex_sta->crc_ok_11n = 0; + coex_sta->crc_ok_11n_agg = 0; + + coex_sta->crc_err_cck = 0; + coex_sta->crc_err_11g = 0; + coex_sta->crc_err_11n = 0; + coex_sta->crc_err_11n_agg = 0; + } else { + coex_sta->crc_ok_cck = + btcoexist->btc_read_4byte(btcoexist, 0xf88); + coex_sta->crc_ok_11g = + btcoexist->btc_read_2byte(btcoexist, 0xf94); + coex_sta->crc_ok_11n = + btcoexist->btc_read_2byte(btcoexist, 0xf90); + coex_sta->crc_ok_11n_agg = + btcoexist->btc_read_2byte(btcoexist, 0xfb8); + + coex_sta->crc_err_cck = + btcoexist->btc_read_4byte(btcoexist, 0xf84); + coex_sta->crc_err_11g = + btcoexist->btc_read_2byte(btcoexist, 0xf96); + coex_sta->crc_err_11n = + btcoexist->btc_read_2byte(btcoexist, 0xf92); + coex_sta->crc_err_11n_agg = + btcoexist->btc_read_2byte(btcoexist, 0xfba); + } + + /* reset counter */ + btcoexist->btc_write_1byte_bitmask(btcoexist, 0xf16, 0x1, 0x1); + btcoexist->btc_write_1byte_bitmask(btcoexist, 0xf16, 0x1, 0x0); +} + static void btc8821a2ant_query_bt_info(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2888,4 +2925,5 @@ void ex_btc8821a2ant_periodical(struct btc_coexist *btcoexist) btc8821a2ant_query_bt_info(btcoexist); btc8821a2ant_monitor_bt_ctr(btcoexist); + btc8821a2ant_monitor_wifi_ctr(btcoexist); } diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h index b4cf1f53d510..ce8e0d719e78 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h @@ -141,6 +141,16 @@ struct coex_sta_8821a_2ant { bool c2h_bt_inquiry_page; u8 bt_retry_cnt; u8 bt_info_ext; + + u32 crc_ok_cck; + u32 crc_ok_11g; + u32 crc_ok_11n; + u32 crc_ok_11n_agg; + + u32 crc_err_cck; + u32 crc_err_11g; + u32 crc_err_11n; + u32 crc_err_11n_agg; }; /*=========================================== -- cgit From 4b1f6eaac344ed0895a9ed5dc71b4f6945e255ca Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:06:55 -0500 Subject: rtlwifi: btcoex: 21a 2ant: update bt profiling information This function updates the information of bt profiling to help us decide the network status and dispatch the resource properly. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 51 +++++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index e23670a23e4d..a9ce79d7f4b4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -359,6 +359,54 @@ static void btc8821a2ant_query_bt_info(struct btc_coexist *btcoexist) btcoexist->btc_fill_h2c(btcoexist, 0x61, 1, h2c_parameter); } +static void btc8821a2ant_update_bt_link_info(struct btc_coexist *btcoexist) +{ + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; + bool bt_hs_on = false; + + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); + + bt_link_info->bt_link_exist = coex_sta->bt_link_exist; + bt_link_info->sco_exist = coex_sta->sco_exist; + bt_link_info->a2dp_exist = coex_sta->a2dp_exist; + bt_link_info->pan_exist = coex_sta->pan_exist; + bt_link_info->hid_exist = coex_sta->hid_exist; + + /* work around for HS mode. */ + if (bt_hs_on) { + bt_link_info->pan_exist = true; + bt_link_info->bt_link_exist = true; + } + + /* check if Sco only */ + if (bt_link_info->sco_exist && !bt_link_info->a2dp_exist && + !bt_link_info->pan_exist && !bt_link_info->hid_exist) + bt_link_info->sco_only = true; + else + bt_link_info->sco_only = false; + + /* check if A2dp only */ + if (!bt_link_info->sco_exist && bt_link_info->a2dp_exist && + !bt_link_info->pan_exist && !bt_link_info->hid_exist) + bt_link_info->a2dp_only = true; + else + bt_link_info->a2dp_only = false; + + /* check if Pan only */ + if (!bt_link_info->sco_exist && !bt_link_info->a2dp_exist && + bt_link_info->pan_exist && !bt_link_info->hid_exist) + bt_link_info->pan_only = true; + else + bt_link_info->pan_only = false; + + /* check if Hid only */ + if (!bt_link_info->sco_exist && !bt_link_info->a2dp_exist && + !bt_link_info->pan_exist && bt_link_info->hid_exist) + bt_link_info->hid_only = true; + else + bt_link_info->hid_only = false; +} + static u8 btc8821a2ant_action_algorithm(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; @@ -2856,8 +2904,7 @@ void ex_btc8821a2ant_bt_info_notify(struct btc_coexist *btcoexist, coex_dm->bt_status = BT_8821A_2ANT_BT_STATUS_IDLE; } - if (bt_hs_on) - coex_dm->bt_status = BT_8821A_2ANT_BT_STATUS_NON_IDLE; + btc8821a2ant_update_bt_link_info(btcoexist); } if (BT_8821A_2ANT_BT_STATUS_NON_IDLE == coex_dm->bt_status) -- cgit From aed6b11e4fc2134ccd4db4d1a41b2cc8fed980f3 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:06:56 -0500 Subject: rtlwifi: btcoex: 21a 2ant: finer adjustment of bt power Originally we only increase/decrease bt power in a fixed power gap, this patch makes us be able to modify bt power for multiple power gaps and we can precisely adjust the bt power. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 51 ++++++++++------------ .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.h | 4 +- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index a9ce79d7f4b4..a074d480166d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -617,45 +617,42 @@ static void btc8821a2ant_set_fw_dac_swing_lvl(struct btc_coexist *btcoexist, } static void btc8821a2ant_set_fw_dec_bt_pwr(struct btc_coexist *btcoexist, - bool dec_bt_pwr) + u8 dec_bt_pwr_lvl) { struct rtl_priv *rtlpriv = btcoexist->adapter; u8 h2c_parameter[1] = {0}; - h2c_parameter[0] = 0; - - if (dec_bt_pwr) - h2c_parameter[0] |= BIT1; + h2c_parameter[0] = dec_bt_pwr_lvl; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], decrease Bt Power : %s, FW write 0x62 = 0x%x\n", - (dec_bt_pwr ? "Yes!!" : "No!!"), h2c_parameter[0]); + "[BTCoex], decrease Bt Power Level : %u, FW write 0x62 = 0x%x\n", + dec_bt_pwr_lvl, h2c_parameter[0]); btcoexist->btc_fill_h2c(btcoexist, 0x62, 1, h2c_parameter); } static void btc8821a2ant_dec_bt_pwr(struct btc_coexist *btcoexist, - bool force_exec, bool dec_bt_pwr) + bool force_exec, u8 dec_bt_pwr_lvl) { struct rtl_priv *rtlpriv = btcoexist->adapter; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], %s Dec BT power = %s\n", - (force_exec ? "force to" : ""), - ((dec_bt_pwr) ? "ON" : "OFF")); - coex_dm->cur_dec_bt_pwr = dec_bt_pwr; + "[BTCoex], %s Dec BT power level = %u\n", + (force_exec ? "force to" : ""), dec_bt_pwr_lvl); + coex_dm->cur_dec_bt_pwr_lvl = dec_bt_pwr_lvl; if (!force_exec) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], pre_dec_bt_pwr = %d, cur_dec_bt_pwr = %d\n", - coex_dm->pre_dec_bt_pwr, coex_dm->cur_dec_bt_pwr); + "[BTCoex], pre_dec_bt_pwr_lvl = %d, cur_dec_bt_pwr_lvl = %d\n", + coex_dm->pre_dec_bt_pwr_lvl, + coex_dm->cur_dec_bt_pwr_lvl); - if (coex_dm->pre_dec_bt_pwr == coex_dm->cur_dec_bt_pwr) + if (coex_dm->pre_dec_bt_pwr_lvl == coex_dm->cur_dec_bt_pwr_lvl) return; } - btc8821a2ant_set_fw_dec_bt_pwr(btcoexist, coex_dm->cur_dec_bt_pwr); + btc8821a2ant_set_fw_dec_bt_pwr(btcoexist, coex_dm->cur_dec_bt_pwr_lvl); - coex_dm->pre_dec_bt_pwr = coex_dm->cur_dec_bt_pwr; + coex_dm->pre_dec_bt_pwr_lvl = coex_dm->cur_dec_bt_pwr_lvl; } static void btc8821a2ant_fw_dac_swing_lvl(struct btc_coexist *btcoexist, @@ -1149,7 +1146,7 @@ static void btc8821a2ant_coex_all_off(struct btc_coexist *btcoexist) /* fw all off */ btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); /* sw all off */ btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, false); @@ -1173,7 +1170,7 @@ static void btc8821a2ant_init_coex_dm(struct btc_coexist *btcoexist) btc8821a2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1); btc8821a2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6); - btc8821a2ant_dec_bt_pwr(btcoexist, FORCE_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, FORCE_EXEC, 0); btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, false); btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); @@ -1263,7 +1260,7 @@ static bool btc8821a2ant_is_common_action(struct btc_coexist *btcoexist) btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, false); @@ -1792,9 +1789,9 @@ static void btc8821a2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); if (BTC_RSSI_HIGH(bt_rssi_state)) - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); else - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); @@ -2007,9 +2004,9 @@ static void btc8821a2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); else - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); @@ -2162,9 +2159,9 @@ static void btc8821a2ant_act_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); else - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); @@ -2608,7 +2605,7 @@ void ex_btc8821a2ant_display_coex_info(struct btc_coexist *btcoexist) RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "\r\n %-35s = %d/ %d ", "DecBtPwr/ IgnWlanAct", - coex_dm->cur_dec_bt_pwr, + coex_dm->cur_dec_bt_pwr_lvl, coex_dm->cur_ignore_wlan_act); } diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h index ce8e0d719e78..ee5aa188af55 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h @@ -69,8 +69,8 @@ enum _BT_8821A_2ANT_COEX_ALGO { struct coex_dm_8821a_2ant { /* fw mechanism */ - bool pre_dec_bt_pwr; - bool cur_dec_bt_pwr; + bool pre_dec_bt_pwr_lvl; + bool cur_dec_bt_pwr_lvl; bool pre_bt_lna_constrain; bool cur_bt_lna_constrain; u8 pre_bt_psd_mode; -- cgit From 7cc7f1a18bccea1e278045b133d91265baa3eb11 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:06:57 -0500 Subject: rtlwifi: btcoex: 21a 2ant: move from bt_stack_info to bt_link_info Gather variables to the 8821a2ant coex structure. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 112 ++++++++++----------- 1 file changed, 55 insertions(+), 57 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index a074d480166d..4abfddcf2d11 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -410,50 +410,43 @@ static void btc8821a2ant_update_bt_link_info(struct btc_coexist *btcoexist) static u8 btc8821a2ant_action_algorithm(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; - struct btc_stack_info *stack_info = &btcoexist->stack_info; + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; bool bt_hs_on = false; u8 algorithm = BT_8821A_2ANT_COEX_ALGO_UNDEFINED; u8 num_of_diff_profile = 0; btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); - /* sync BTInfo with BT firmware and stack */ - if (!stack_info->hid_exist) - stack_info->hid_exist = coex_sta->hid_exist; - /* when stack HID report error, here we use the info from bt fw. */ - if (!stack_info->bt_link_exist) - stack_info->bt_link_exist = coex_sta->bt_link_exist; - - if (!coex_sta->bt_link_exist) { + if (!bt_link_info->bt_link_exist) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], No profile exists!!!\n"); + "[BTCoex], No BT link exists!!!\n"); return algorithm; } - if (coex_sta->sco_exist) + if (bt_link_info->sco_exist) num_of_diff_profile++; - if (coex_sta->hid_exist) + if (bt_link_info->hid_exist) num_of_diff_profile++; - if (coex_sta->pan_exist) + if (bt_link_info->pan_exist) num_of_diff_profile++; - if (coex_sta->a2dp_exist) + if (bt_link_info->a2dp_exist) num_of_diff_profile++; if (num_of_diff_profile == 1) { - if (coex_sta->sco_exist) { + if (bt_link_info->sco_exist) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCO only\n"); algorithm = BT_8821A_2ANT_COEX_ALGO_SCO; } else { - if (coex_sta->hid_exist) { + if (bt_link_info->hid_exist) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], HID only\n"); algorithm = BT_8821A_2ANT_COEX_ALGO_HID; - } else if (coex_sta->a2dp_exist) { + } else if (bt_link_info->a2dp_exist) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], A2DP only\n"); algorithm = BT_8821A_2ANT_COEX_ALGO_A2DP; - } else if (coex_sta->pan_exist) { + } else if (bt_link_info->pan_exist) { if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -468,16 +461,16 @@ static u8 btc8821a2ant_action_algorithm(struct btc_coexist *btcoexist) } } } else if (num_of_diff_profile == 2) { - if (coex_sta->sco_exist) { - if (coex_sta->hid_exist) { + if (bt_link_info->sco_exist) { + if (bt_link_info->hid_exist) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCO + HID\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_PANEDR_HID; - } else if (coex_sta->a2dp_exist) { + algorithm = BT_8821A_2ANT_COEX_ALGO_SCO; + } else if (bt_link_info->a2dp_exist) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCO + A2DP ==> SCO\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_PANEDR_HID; - } else if (coex_sta->pan_exist) { + algorithm = BT_8821A_2ANT_COEX_ALGO_SCO; + } else if (bt_link_info->pan_exist) { if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -487,99 +480,104 @@ static u8 btc8821a2ant_action_algorithm(struct btc_coexist *btcoexist) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCO + PAN(EDR)\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_PANEDR_HID; + algorithm = BT_8821A_2ANT_COEX_ALGO_SCO; } } } else { - if (coex_sta->hid_exist && - coex_sta->a2dp_exist) { + if (bt_link_info->hid_exist && + bt_link_info->a2dp_exist) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], HID + A2DP\n"); algorithm = BT_8821A_2ANT_COEX_ALGO_HID_A2DP; - } else if (coex_sta->hid_exist && - coex_sta->pan_exist) { + } else if (bt_link_info->hid_exist && + bt_link_info->pan_exist) { if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], HID + PAN(HS)\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_HID; + algorithm = BT_8821A_2ANT_COEX_ALGO_HID; } else { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], HID + PAN(EDR)\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_PANEDR_HID; + algorithm = + BT_8821A_2ANT_COEX_ALGO_PANEDR_HID; } - } else if (coex_sta->pan_exist && - coex_sta->a2dp_exist) { + } else if (bt_link_info->pan_exist && + bt_link_info->a2dp_exist) { if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], A2DP + PAN(HS)\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_A2DP_PANHS; + algorithm = + BT_8821A_2ANT_COEX_ALGO_A2DP_PANHS; } else { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], A2DP + PAN(EDR)\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_PANEDR_A2DP; + algorithm = + BT_8821A_2ANT_COEX_ALGO_PANEDR_A2DP; } } } } else if (num_of_diff_profile == 3) { - if (coex_sta->sco_exist) { - if (coex_sta->hid_exist && - coex_sta->a2dp_exist) { + if (bt_link_info->sco_exist) { + if (bt_link_info->hid_exist && + bt_link_info->a2dp_exist) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCO + HID + A2DP ==> HID\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_PANEDR_HID; - } else if (coex_sta->hid_exist && - coex_sta->pan_exist) { + algorithm = BT_8821A_2ANT_COEX_ALGO_SCO; + } else if (bt_link_info->hid_exist && + bt_link_info->pan_exist) { if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCO + HID + PAN(HS)\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_PANEDR_HID; + algorithm = BT_8821A_2ANT_COEX_ALGO_SCO; } else { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCO + HID + PAN(EDR)\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_PANEDR_HID; + algorithm = BT_8821A_2ANT_COEX_ALGO_SCO; } - } else if (coex_sta->pan_exist && - coex_sta->a2dp_exist) { + } else if (bt_link_info->pan_exist && + bt_link_info->a2dp_exist) { if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCO + A2DP + PAN(HS)\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_PANEDR_HID; + algorithm = BT_8821A_2ANT_COEX_ALGO_SCO; } else { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCO + A2DP + PAN(EDR) ==> HID\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_PANEDR_HID; + algorithm = BT_8821A_2ANT_COEX_ALGO_SCO; } } } else { - if (coex_sta->hid_exist && - coex_sta->pan_exist && - coex_sta->a2dp_exist) { + if (bt_link_info->hid_exist && + bt_link_info->pan_exist && + bt_link_info->a2dp_exist) { if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], HID + A2DP + PAN(HS)\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_HID_A2DP; + algorithm = + BT_8821A_2ANT_COEX_ALGO_HID_A2DP; } else { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], HID + A2DP + PAN(EDR)\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_HID_A2DP_PANEDR; + algorithm = + BT_8821A_2ANT_COEX_ALGO_HID_A2DP_PANEDR; } } } } else if (num_of_diff_profile >= 3) { - if (coex_sta->sco_exist) { - if (coex_sta->hid_exist && - coex_sta->pan_exist && - coex_sta->a2dp_exist) { + if (bt_link_info->sco_exist) { + if (bt_link_info->hid_exist && + bt_link_info->pan_exist && + bt_link_info->a2dp_exist) { if (bt_hs_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -589,7 +587,7 @@ static u8 btc8821a2ant_action_algorithm(struct btc_coexist *btcoexist) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], SCO + HID + A2DP + PAN(EDR)==>PAN(EDR)+HID\n"); - algorithm = BT_8821A_2ANT_COEX_ALGO_PANEDR_HID; + algorithm = BT_8821A_2ANT_COEX_ALGO_SCO; } } } -- cgit From 5a81969c1cc3a19a65ebbd9f31f844b0a5060d25 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:06:58 -0500 Subject: rtlwifi: btcoex: 21a 2ant: suffer less tx penalty from retry Change h2c parameter to decrease tx penalty. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 4abfddcf2d11..1a1e4136d48e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -693,11 +693,11 @@ static void btc8821a2ant_set_sw_penalty_tx_rate_adaptive( /* normal rate except MCS7/6/5, OFDM54/48/36 */ h2c_parameter[2] = 0x00; /* MCS7 or OFDM54 */ - h2c_parameter[3] = 0xf7; + h2c_parameter[3] = 0xf5; /* MCS6 or OFDM48 */ - h2c_parameter[4] = 0xf8; + h2c_parameter[4] = 0xa0; /* MCS5 or OFDM36 */ - h2c_parameter[5] = 0xf9; + h2c_parameter[5] = 0xa0; } RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, -- cgit From d9158ea1d1bde5f01c03fa92f3a88b7fb39153e4 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:06:59 -0500 Subject: rtlwifi: btcoex: 21a 2ant: check power save state before pstdma The power_save_state function checks the state of power saving. For tdma settings, the wifi sends nullfunc to pretend enter power saving and then bt can transmit. Hence the coex needs to check the power status before set the pstdma function. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 119 +++++++++++++++++---- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.h | 5 + 2 files changed, 104 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 1a1e4136d48e..36e722a33f0f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -872,6 +872,33 @@ static void btc8821a2ant_set_fw_ignore_wlan_act(struct btc_coexist *btcoex, btcoex->btc_fill_h2c(btcoex, 0x63, 1, h2c_parameter); } +static void btc8821a2ant_set_lps_rpwm(struct btc_coexist *btcoexist, u8 lps_val, + u8 rpwm_val) +{ + u8 lps = lps_val; + u8 rpwm = rpwm_val; + + btcoexist->btc_set(btcoexist, BTC_SET_U1_LPS_VAL, &lps); + btcoexist->btc_set(btcoexist, BTC_SET_U1_RPWM_VAL, &rpwm); +} + +static void btc8821a2ant_lps_rpwm(struct btc_coexist *btcoexist, + bool force_exec, u8 lps_val, u8 rpwm_val) +{ + coex_dm->cur_lps = lps_val; + coex_dm->cur_rpwm = rpwm_val; + + if (!force_exec) { + if ((coex_dm->pre_lps == coex_dm->cur_lps) && + (coex_dm->pre_rpwm == coex_dm->cur_rpwm)) + return; + } + btc8821a2ant_set_lps_rpwm(btcoexist, lps_val, rpwm_val); + + coex_dm->pre_lps = coex_dm->cur_lps; + coex_dm->pre_rpwm = coex_dm->cur_rpwm; +} + static void btc8821a2ant_ignore_wlan_act(struct btc_coexist *btcoexist, bool force_exec, bool enable) { @@ -1139,9 +1166,75 @@ static void btc8821a2ant_ps_tdma(struct btc_coexist *btcoexist, coex_dm->pre_ps_tdma = coex_dm->cur_ps_tdma; } +static void +btc8821a2ant_ps_tdma_check_for_power_save_state(struct btc_coexist *btcoexist, + bool new_ps_state) +{ + u8 lps_mode = 0x0; + + btcoexist->btc_get(btcoexist, BTC_GET_U1_LPS_MODE, &lps_mode); + + if (lps_mode) { + /* already under LPS state */ + if (new_ps_state) { + /* keep state under LPS, do nothing */ + } else { + /* will leave LPS state, turn off psTdma first */ + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + } + } else { + /* NO PS state */ + if (new_ps_state) { + /* will enter LPS state, turn off psTdma first */ + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + } else { + /* keep state under NO PS state, do nothing */ + } + } +} + +static void btc8821a2ant_power_save_state(struct btc_coexist *btcoexist, + u8 ps_type, u8 lps_val, u8 rpwm_val) +{ + bool low_pwr_disable = false; + + switch (ps_type) { + case BTC_PS_WIFI_NATIVE: + /* recover to original 32k low power setting */ + low_pwr_disable = false; + btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, + &low_pwr_disable); + btcoexist->btc_set(btcoexist, BTC_SET_ACT_NORMAL_LPS, NULL); + coex_sta->force_lps_on = false; + break; + case BTC_PS_LPS_ON: + btc8821a2ant_ps_tdma_check_for_power_save_state(btcoexist, + true); + btc8821a2ant_lps_rpwm(btcoexist, NORMAL_EXEC, lps_val, + rpwm_val); + /* when coex force to enter LPS, do not enter 32k low power */ + low_pwr_disable = true; + btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, + &low_pwr_disable); + /* power save must executed before psTdma */ + btcoexist->btc_set(btcoexist, BTC_SET_ACT_ENTER_LPS, NULL); + coex_sta->force_lps_on = true; + break; + case BTC_PS_LPS_OFF: + btc8821a2ant_ps_tdma_check_for_power_save_state(btcoexist, + false); + btcoexist->btc_set(btcoexist, BTC_SET_ACT_LEAVE_LPS, NULL); + coex_sta->force_lps_on = false; + break; + default: + break; + } +} + static void btc8821a2ant_coex_all_off(struct btc_coexist *btcoexist) { /* fw all off */ + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); @@ -1166,6 +1259,7 @@ static void btc8821a2ant_init_coex_dm(struct btc_coexist *btcoexist) btc8821a2ant_coex_table(btcoexist, FORCE_EXEC, 0x55555555, 0x55555555, 0xffff, 0x3); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); btc8821a2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1); btc8821a2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6); btc8821a2ant_dec_bt_pwr(btcoexist, FORCE_EXEC, 0); @@ -1300,6 +1394,8 @@ static bool btc8821a2ant_is_common_action(struct btc_coexist *btcoexist) RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Wifi IPS + BT Busy!!\n"); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); @@ -1658,17 +1754,10 @@ static void btc8821a2ant_action_hid(struct btc_coexist *btcoexist) 0x5aea5aea, 0xffff, 0x3); } - if (wifi_bw == BTC_WIFI_BW_HT40) { - /* fw mechanism */ - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - } else { - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 13); - } + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 24); + if (wifi_bw == BTC_WIFI_BW_HT40) { /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { @@ -1683,16 +1772,6 @@ static void btc8821a2ant_action_hid(struct btc_coexist *btcoexist) false, 0x18); } } else { - /* fw mechanism */ - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 9); - } else { - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 13); - } - /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h index ee5aa188af55..804b5b95cab0 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h @@ -118,6 +118,10 @@ struct coex_dm_8821a_2ant { u8 cur_algorithm; u8 bt_status; u8 wifi_chnl_info[3]; + u8 pre_lps; + u8 cur_lps; + u8 pre_rpwm; + u8 cur_rpwm; }; struct coex_sta_8821a_2ant { @@ -151,6 +155,7 @@ struct coex_sta_8821a_2ant { u32 crc_err_11g; u32 crc_err_11n; u32 crc_err_11n_agg; + bool force_lps_on; }; /*=========================================== -- cgit From d09199eb20382a2dd5e61afecd647784cc9697eb Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:07:00 -0500 Subject: rtlwifi: btcoex: 21a 2ant: do not check wifi bandwidth Remove workaround for HT40 issues for RF low pass filter. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 36e722a33f0f..77916ae3d043 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -958,16 +958,6 @@ static void btc8821a2ant_sw_mechanism1(struct btc_coexist *btcoexist, bool shrink_rx_lpf, bool low_penalty_ra, bool limited_dig, bool bt_lna_constrain) { - u32 wifi_bw; - - btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - - if (BTC_WIFI_BW_HT40 != wifi_bw) { - /*only shrink RF Rx LPF for HT40*/ - if (shrink_rx_lpf) - shrink_rx_lpf = false; - } - btc8821a2ant_low_penalty_ra(btcoexist, NORMAL_EXEC, low_penalty_ra); } -- cgit From d4acd81ed268fed257e8a8b4a40511bebc817244 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:07:01 -0500 Subject: rtlwifi: btcoex: 21a 2ant: centralized control of coex table Gather multiple coex table settings into a function coex_table_with_type() and control the coex table according the type value as put in switch-case expression. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 116 +++++++++++++++++---- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.h | 2 + 2 files changed, 99 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 77916ae3d043..4d69955fbebc 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -856,6 +856,89 @@ static void btc8821a2ant_coex_table(struct btc_coexist *btcoexist, coex_dm->pre_val0x6cc = coex_dm->cur_val0x6cc; } +static void btc8821a2ant_coex_table_with_type(struct btc_coexist *btcoexist, + bool force_exec, u8 type) +{ + coex_sta->coex_table_type = type; + + switch (type) { + case 0: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x55555555, + 0x55555555, 0xffffff, 0x3); + break; + case 1: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x55555555, + 0x5afa5afa, 0xffffff, 0x3); + break; + case 2: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x5ada5ada, + 0x5ada5ada, 0xffffff, 0x3); + break; + case 3: + btc8821a2ant_coex_table(btcoexist, force_exec, 0xaaaaaaaa, + 0xaaaaaaaa, 0xffffff, 0x3); + break; + case 4: + btc8821a2ant_coex_table(btcoexist, force_exec, 0xffffffff, + 0xffffffff, 0xffffff, 0x3); + break; + case 5: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x5fff5fff, + 0x5fff5fff, 0xffffff, 0x3); + break; + case 6: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x55ff55ff, + 0x5a5a5a5a, 0xffffff, 0x3); + break; + case 7: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0x5ada5ada, 0xffffff, 0x3); + break; + case 8: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0x5ada5ada, 0xffffff, 0x3); + break; + case 9: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0x5ada5ada, 0xffffff, 0x3); + break; + case 10: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0x5ada5ada, 0xffffff, 0x3); + break; + case 11: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0x5ada5ada, 0xffffff, 0x3); + break; + case 12: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0x5ada5ada, 0xffffff, 0x3); + break; + case 13: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x5fff5fff, + 0xaaaaaaaa, 0xffffff, 0x3); + break; + case 14: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x5fff5fff, + 0x5ada5ada, 0xffffff, 0x3); + break; + case 15: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x55dd55dd, + 0xaaaaaaaa, 0xffffff, 0x3); + break; + case 16: + btc8821a2ant_coex_table(btcoexist, force_exec, 0x5fdf5fdf, + 0x5fdb5fdb, 0xffffff, 0x3); + break; + case 17: + btc8821a2ant_coex_table(btcoexist, force_exec, 0xfafafafa, + 0xfafafafa, 0xffffff, 0x3); + break; + default: + break; + } +} + static void btc8821a2ant_set_fw_ignore_wlan_act(struct btc_coexist *btcoex, bool enable) { @@ -1234,8 +1317,7 @@ static void btc8821a2ant_coex_all_off(struct btc_coexist *btcoexist) btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); /* hw all off */ - btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, - 0x55555555, 0x55555555, 0xffff, 0x3); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); } static void btc8821a2ant_coex_under_5g(struct btc_coexist *btcoexist) @@ -1246,8 +1328,7 @@ static void btc8821a2ant_coex_under_5g(struct btc_coexist *btcoexist) static void btc8821a2ant_init_coex_dm(struct btc_coexist *btcoexist) { /* force to reset coex mechanism */ - btc8821a2ant_coex_table(btcoexist, FORCE_EXEC, 0x55555555, - 0x55555555, 0xffff, 0x3); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); btc8821a2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1); @@ -1736,12 +1817,10 @@ static void btc8821a2ant_action_hid(struct btc_coexist *btcoexist) if (wifi_bw == BTC_WIFI_BW_LEGACY) { /* for HID at 11b/g mode */ - btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5a5a5a5a, 0xffff, 0x3); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); } else { /* for HID quality & wifi performance balance at 11n mode */ - btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5aea5aea, 0xffff, 0x3); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); } btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); @@ -1933,9 +2012,9 @@ static void btc8821a2ant_action_pan_edr(struct btc_coexist *btcoexist) btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, 0x5aff5aff, 0xffff, 0x3); } else { - /* for HID quality & wifi performance balance at 11n mode */ - btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5aff5aff, 0xffff, 0x3); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 13); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_LPS_ON, 0x50, + 0x4); } if (BTC_WIFI_BW_HT40 == wifi_bw) { @@ -2317,13 +2396,13 @@ static void btc8821a2ant_action_hid_a2dp(struct btc_coexist *btcoexist) btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); if (wifi_bw == BTC_WIFI_BW_LEGACY) { - /* for HID at 11b/g mode */ - btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5f5b5f5b, 0xffffff, 0x3); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); } else { - /* for HID quality & wifi performance balance at 11n mode */ - btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5f5b5f5b, 0xffffff, 0x3); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 14); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_LPS_ON, 0x50, + 0x4); } if (BTC_WIFI_BW_HT40 == wifi_bw) { @@ -2496,8 +2575,7 @@ void ex_btc8821a2ant_init_hwconfig(struct btc_coexist *btcoexist) btc8821a2ant_set_ant_path(btcoexist, BTC_ANT_WIFI_AT_MAIN, true, false); /* PTA parameter */ - btc8821a2ant_coex_table(btcoexist, FORCE_EXEC, 0x55555555, 0x55555555, - 0xffff, 0x3); + btc8821a2ant_coex_table_with_type(btcoexist, FORCE_EXEC, 0); /* Enable counter statistics */ /*0x76e[3] = 1, WLAN_Act control by PTA*/ diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h index 804b5b95cab0..6db96cf44034 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h @@ -155,6 +155,8 @@ struct coex_sta_8821a_2ant { u32 crc_err_11g; u32 crc_err_11n; u32 crc_err_11n_agg; + + u8 coex_table_type; bool force_lps_on; }; -- cgit From 3fd7ba4c764c0b6b13e6d764d7761ea3cec12745 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:07:02 -0500 Subject: rtlwifi: btcoex: 21a 2ant: check if wifi status changed Monitor wifi status and check if it is changed. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 41 ++++++++++++++++++++++ .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.h | 5 +++ 2 files changed, 46 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 4d69955fbebc..4030b6429c1d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -359,6 +359,47 @@ static void btc8821a2ant_query_bt_info(struct btc_coexist *btcoexist) btcoexist->btc_fill_h2c(btcoexist, 0x61, 1, h2c_parameter); } +bool btc8821a2ant_is_wifi_status_changed(struct btc_coexist *btcoexist) +{ + static bool pre_wifi_busy = true; + static bool pre_under_4way = true; + static bool pre_bt_hs_on = true; + bool wifi_busy = false, under_4way = false, bt_hs_on = false; + bool wifi_connected = false; + u8 wifi_rssi_state = BTC_RSSI_STATE_HIGH; + + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, + &wifi_connected); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy); + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_4_WAY_PROGRESS, + &under_4way); + + if (wifi_connected) { + if (wifi_busy != pre_wifi_busy) { + pre_wifi_busy = wifi_busy; + return true; + } + if (under_4way != pre_under_4way) { + pre_under_4way = under_4way; + return true; + } + if (bt_hs_on != pre_bt_hs_on) { + pre_bt_hs_on = bt_hs_on; + return true; + } + + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 3, 2, + BT_8821A_2ANT_WIFI_RSSI_COEXSWITCH_THRES, 0); + + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_LOW)) + return true; + } + + return false; +} + static void btc8821a2ant_update_bt_link_info(struct btc_coexist *btcoexist) { struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h index 6db96cf44034..603e446b5953 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h @@ -38,6 +38,11 @@ #define BTC_RSSI_COEX_THRESH_TOL_8821A_2ANT 2 +/* WiFi RSSI Threshold for 2-Ant TDMA/1-Ant PS-TDMA translation */ +#define BT_8821A_2ANT_WIFI_RSSI_COEXSWITCH_THRES 42 +/* BT RSSI Threshold for 2-Ant TDMA/1-Ant PS-TDMA translation */ +#define BT_8821A_2ANT_BT_RSSI_COEXSWITCH_THRES 46 + enum _BT_INFO_SRC_8821A_2ANT { BT_INFO_SRC_8821A_2ANT_WIFI_FW = 0x0, BT_INFO_SRC_8821A_2ANT_BT_RSP = 0x1, -- cgit From 9153c11cefa3905a3299d2d7b83d9330cf90f5d2 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:07:03 -0500 Subject: rtlwifi: btcoex: 21a 2ant: ignore wifi if it is at 5G band When wifi is at 5G band, it does not intefere with 2.4G bt signal, hence we can just ignore it and transmit normally as nothing happened. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 4030b6429c1d..bb609f078a01 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -1364,6 +1364,7 @@ static void btc8821a2ant_coex_all_off(struct btc_coexist *btcoexist) static void btc8821a2ant_coex_under_5g(struct btc_coexist *btcoexist) { btc8821a2ant_coex_all_off(btcoexist); + btc8821a2ant_ignore_wlan_act(btcoexist, NORMAL_EXEC, true); } static void btc8821a2ant_init_coex_dm(struct btc_coexist *btcoexist) -- cgit From aae1e8f5a0f05e0965b56a3cf6bfd2686577e817 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:07:04 -0500 Subject: rtlwifi: btcoex: 21a 2ant: set coex table and tdma when bt inquiry Instead of just setting the coex table directly, we check if the wifi is under some important activity (scanning|roaming|linking) and mark the packets as high priority in that case. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 51 +++++++++++++++++++--- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index bb609f078a01..7b2fb62947da 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -1381,16 +1381,54 @@ static void btc8821a2ant_init_coex_dm(struct btc_coexist *btcoexist) btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); } -static void btc8821a2ant_bt_inquiry_page(struct btc_coexist *btcoexist) +static void btc8821a2ant_action_bt_inquiry(struct btc_coexist *btcoexist) { + struct rtl_priv *rtlpriv = btcoexist->adapter; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; + bool wifi_connected = false; bool low_pwr_disable = true; + bool scan = false, link = false, roam = false; + + wifi_rssi_state = + btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state1 = btc8821a2ant_wifi_rssi_state(btcoexist, 1, 2, + BT_8821A_2ANT_WIFI_RSSI_COEXSWITCH_THRES, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, + 2, BT_8821A_2ANT_BT_RSSI_COEXSWITCH_THRES, 0); btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, + &wifi_connected); - btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5afa5afa, 0xffff, 0x3); - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_ROAM, &roam); + + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); + + if (scan || link || roam) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], Wifi link process + BT Inq/Page!!\n"); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 15); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); + } else if (wifi_connected) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], Wifi connected + BT Inq/Page!!\n"); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 15); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], Wifi no-link + BT Inq/Page!!\n"); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + } + + btc8821a2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + + btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); } static bool btc8821a2ant_is_common_action(struct btc_coexist *btcoexist) @@ -1779,8 +1817,7 @@ static void btc8821a2ant_action_sco(struct btc_coexist *btcoexist) if (wifi_bw == BTC_WIFI_BW_LEGACY) { /* for SCO quality at 11b/g mode */ - btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, - 0x5a5a5a5a, 0x5a5a5a5a, 0xffff, 0x3); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); } else { /* for SCO quality & wifi performance balance at 11n mode */ btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, @@ -2511,7 +2548,7 @@ static void btc8821a2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) (BT_8821A_2ANT_COEX_ALGO_PANHS != algorithm)) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], BT is under inquiry/page scan !!\n"); - btc8821a2ant_bt_inquiry_page(btcoexist); + btc8821a2ant_action_bt_inquiry(btcoexist); return; } -- cgit From 8c670a1b1daeb5c7d2628e0d74fc3594af443a36 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:07:05 -0500 Subject: rtlwifi: btcoex: 21a 2ant: let PTA circuit control the switch Register 0xcb4 determines if the PTA circuit can control the swtich Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 7b2fb62947da..6faa53ee821d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -1109,7 +1109,6 @@ static void btc8821a2ant_set_ant_path(struct btc_coexist *btcoexist, btcoexist->btc_write_4byte(btcoexist, 0x4c, u4tmp); btcoexist->btc_write_4byte(btcoexist, 0x974, 0x3ff); - btcoexist->btc_write_1byte(btcoexist, 0xcb4, 0x77); if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) { /* tell firmware "antenna inverse" ==> WRONG firmware -- cgit From 8189d81890c150a6268a4660d24cabd603a3a540 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:07:06 -0500 Subject: rtlwifi: btcoex: 21a 2ant: slot time fine tune Tune the wifi/bt slot time to get better performance. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 52 +++++++++++----------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 6faa53ee821d..5991a2cd8678 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -1168,12 +1168,12 @@ static void btc8821a2ant_ps_tdma(struct btc_coexist *btcoexist, switch (type) { case 1: default: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x3c, + 0x03, 0xf1, 0x90); break; case 2: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, - 0x12, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x2d, + 0x03, 0xf1, 0x90); break; case 3: btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, @@ -1184,12 +1184,12 @@ static void btc8821a2ant_ps_tdma(struct btc_coexist *btcoexist, 0x03, 0xf1, 0x90); break; case 5: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x3c, + 0x3, 0x70, 0x90); break; case 6: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, - 0x12, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x2d, + 0x3, 0x70, 0x90); break; case 7: btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, @@ -1200,44 +1200,44 @@ static void btc8821a2ant_ps_tdma(struct btc_coexist *btcoexist, 0x3, 0x70, 0x90); break; case 9: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x3c, + 0x03, 0xf1, 0x90); break; case 10: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, - 0x12, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x2d, + 0x03, 0xf1, 0x90); break; case 11: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0xa, - 0xa, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, + 0x3, 0xf1, 0x90); break; case 12: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, - 0x5, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x10, + 0x3, 0xf1, 0x90); break; case 13: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x3c, + 0x3, 0x70, 0x90); break; case 14: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, - 0x12, 0x12, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x2d, + 0x3, 0x70, 0x90); break; case 15: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0xa, - 0xa, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, + 0x3, 0x70, 0x90); break; case 16: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, - 0x5, 0x60, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x10, + 0x3, 0x70, 0x90); break; case 17: btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xa3, 0x2f, 0x2f, 0x60, 0x90); break; case 18: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, - 0x5, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, 0x5, + 0xe1, 0x90); break; case 19: btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x25, -- cgit From 4b764919268783ac0b31bcd16699704310f76b5a Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:07:07 -0500 Subject: rtlwifi: btcoex: 21a 2ant: tdma cases for low wifi/bt rssi If the wifi or bt has low rssi, they need extra parameter settings for the tdma. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 91 +++++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 5991a2cd8678..c0537d0268e6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -1145,6 +1145,20 @@ static void btc8821a2ant_ps_tdma(struct btc_coexist *btcoexist, { struct rtl_priv *rtlpriv = btcoexist->adapter; + u8 wifi_rssi_state, bt_rssi_state; + + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 1, 2, + BT_8821A_2ANT_WIFI_RSSI_COEXSWITCH_THRES, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, + BT_8821A_2ANT_BT_RSSI_COEXSWITCH_THRES, 0); + + if (!(BTC_RSSI_HIGH(wifi_rssi_state) && + BTC_RSSI_HIGH(bt_rssi_state)) && + turn_on) { + /* for WiFi RSSI low or BT RSSI low */ + type = type + 100; + } + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], %s turn %s PS TDMA, type = %d\n", (force_exec ? "force to" : ""), (turn_on ? "ON" : "OFF"), @@ -1251,9 +1265,82 @@ static void btc8821a2ant_ps_tdma(struct btc_coexist *btcoexist, btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x15, 0x03, 0x70, 0x90); break; + case 23: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1e, + 0x03, 0xf0, 0x14); + break; + case 24: + case 124: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xd3, 0x3c, + 0x03, 0x70, 0x50); + break; + case 25: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x14, + 0x03, 0xf1, 0x90); + break; + case 26: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x30, + 0x03, 0xf1, 0x90); + break; case 71: - btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, - 0x1a, 0xe1, 0x90); + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x3c, + 0x03, 0xf1, 0x90); + break; + case 101: + case 105: + case 171: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xd3, 0x3a, + 0x03, 0x70, 0x50); + break; + case 102: + case 106: + case 110: + case 114: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xd3, 0x2d, + 0x03, 0x70, 0x50); + break; + case 103: + case 107: + case 111: + case 115: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xd3, 0x1c, + 0x03, 0x70, 0x50); + break; + case 104: + case 108: + case 112: + case 116: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xd3, 0x10, + 0x03, 0x70, 0x50); + break; + case 109: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x3c, + 0x03, 0xf1, 0x90); + break; + case 113: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x3c, + 0x03, 0x70, 0x90); + break; + case 121: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x15, + 0x03, 0x70, 0x90); + break; + case 22: + case 122: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x35, + 0x03, 0x71, 0x11); + break; + case 123: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xd3, 0x1c, + 0x03, 0x70, 0x54); + break; + case 125: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xd3, 0x14, + 0x03, 0x70, 0x50); + break; + case 126: + btc8821a2ant_set_fw_ps_tdma(btcoexist, 0xd3, 0x30, + 0x03, 0x70, 0x50); break; } } else { -- cgit From 3506bc286abe0f3ba2344c8df8a4f688eef6221f Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:07:08 -0500 Subject: rtlwifi: btcoex: 21a 2ant: action for wifi is idle/linking/common Depending on the state of wifi, we need to set different tdma and coex table parameters to make wfi and bt coexist smoothly. Otherwise the bt may have low sound quality or mouse lag, which mean bad user experience. The same problem may occur on wifi also, if could disconnect or lose some important packets. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 240 +++++++++++++-------- 1 file changed, 145 insertions(+), 95 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index c0537d0268e6..f640f6079f9a 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -1517,151 +1517,201 @@ static void btc8821a2ant_action_bt_inquiry(struct btc_coexist *btcoexist) btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); } -static bool btc8821a2ant_is_common_action(struct btc_coexist *btcoexist) +void btc8821a2ant_action_wifi_link_process(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; - bool common = false, wifi_connected = false, wifi_busy = false; - bool low_pwr_disable = false; + u8 u8tmpa, u8tmpb; - btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, - &wifi_connected); - btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 15); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 22); - btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5afa5afa, 0xffff, 0x3); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); - if (!wifi_connected && - BT_8821A_2ANT_BT_STATUS_IDLE == coex_dm->bt_status) { - low_pwr_disable = false; - btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, - &low_pwr_disable); + u8tmpa = btcoexist->btc_read_1byte(btcoexist, 0x765); + u8tmpb = btcoexist->btc_read_1byte(btcoexist, 0x76e); - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi IPS + BT IPS!!\n"); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], 0x765=0x%x, 0x76e=0x%x\n", u8tmpa, u8tmpb); +} - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); - btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); +static bool btc8821a2ant_action_wifi_idle_process(struct btc_coexist *btcoexist) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; + u8 ap_num = 0; - btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, - false); - btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, - 0x18); + wifi_rssi_state = + btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state1 = btc8821a2ant_wifi_rssi_state(btcoexist, 1, 2, + BT_8821A_2ANT_WIFI_RSSI_COEXSWITCH_THRES - 20, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, + 2, BT_8821A_2ANT_BT_RSSI_COEXSWITCH_THRES, 0); - common = true; - } else if (wifi_connected && - (BT_8821A_2ANT_BT_STATUS_IDLE == coex_dm->bt_status)) { - low_pwr_disable = false; - btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, - &low_pwr_disable); + btcoexist->btc_get(btcoexist, BTC_GET_U1_AP_NUM, &ap_num); - if (wifi_busy) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi Busy + BT IPS!!\n"); - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 1); - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi LPS + BT IPS!!\n"); - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 1); - } + /* define the office environment */ + if (BTC_RSSI_HIGH(wifi_rssi_state1) && (coex_sta->hid_exist) && + (coex_sta->a2dp_exist)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], Wifi idle process for BT HID+A2DP exist!!\n"); - btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dac_swing(btcoexist, NORMAL_EXEC, true, 0x6); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + /* sw all off */ btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, false); btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); - common = true; - } else if (!wifi_connected && - (BT_8821A_2ANT_BT_STATUS_CON_IDLE == coex_dm->bt_status)) { - low_pwr_disable = true; - btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, - &low_pwr_disable); - btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, - 0x8); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + return true; + } else if (coex_sta->pan_exist) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi IPS + BT LPS!!\n"); + "[BTCoex], Wifi idle process for BT PAN exist!!\n"); - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); - btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_dac_swing(btcoexist, NORMAL_EXEC, true, 0x6); btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + /* sw all off */ btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, false); btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); - common = true; - } else if (wifi_connected && - (BT_8821A_2ANT_BT_STATUS_CON_IDLE == coex_dm->bt_status)) { - low_pwr_disable = true; - btcoexist->btc_set(btcoexist, - BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); - if (wifi_busy) { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi Busy + BT LPS!!\n"); - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 1); - } else { - RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi LPS + BT LPS!!\n"); - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 1); - } + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); - btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + return true; + } + btc8821a2ant_dac_swing(btcoexist, NORMAL_EXEC, true, 0x18); + return false; +} - btc8821a2ant_sw_mechanism1(btcoexist, true, true, true, true); - btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, - 0x18); +static bool btc8821a2ant_is_common_action(struct btc_coexist *btcoexist) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + bool common = false, wifi_connected = false, wifi_busy = false; + bool low_pwr_disable = false; + bool bt_hs_on = false; - common = true; - } else if (!wifi_connected && - (coex_dm->bt_status == BT_8821A_2ANT_BT_STATUS_NON_IDLE)) { + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, + &wifi_connected); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy); + + if (!wifi_connected) { low_pwr_disable = false; btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, &low_pwr_disable); + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, + 0x8); RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi IPS + BT Busy!!\n"); + "[BTCoex], Wifi non-connected idle!!\n"); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, + 0x0); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); - btc8821a2ant_sw_mechanism1(btcoexist, false, false, - false, false); - btc8821a2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, + false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, + 0x18); common = true; } else { - low_pwr_disable = true; - btcoexist->btc_set(btcoexist, - BTC_SET_ACT_DISABLE_LOW_POWER, - &low_pwr_disable); + if (BT_8821A_2ANT_BT_STATUS_IDLE == + coex_dm->bt_status) { + low_pwr_disable = false; + btcoexist->btc_set(btcoexist, + BTC_SET_ACT_DISABLE_LOW_POWER, + &low_pwr_disable); + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, + false, false, 0x8); - if (wifi_busy) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi Busy + BT Busy!!\n"); - common = false; - } else { + "[BTCoex], Wifi connected + BT non connected-idle!!\n"); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, + 0xfffff, 0x0); + btc8821a2ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 0); + + btc8821a2ant_power_save_state( + btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, + 0xb); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + + btc8821a2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + + common = true; + } else if (BT_8821A_2ANT_BT_STATUS_CON_IDLE == + coex_dm->bt_status) { + low_pwr_disable = true; + btcoexist->btc_set(btcoexist, + BTC_SET_ACT_DISABLE_LOW_POWER, + &low_pwr_disable); + + if (bt_hs_on) + return false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, - "[BTCoex], Wifi LPS + BT Busy!!\n"); - btc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, true, 21); + "[BTCoex], Wifi connected + BT connected-idle!!\n"); + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, + false, false, 0x8); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, + 0xfffff, 0x0); + btc8821a2ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 0); + + btc8821a2ant_power_save_state( + btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, + 0xb); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + btc8821a2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); common = true; + } else { + low_pwr_disable = true; + btcoexist->btc_set(btcoexist, + BTC_SET_ACT_DISABLE_LOW_POWER, + &low_pwr_disable); + + if (wifi_busy) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], Wifi Connected-Busy + BT Busy!!\n"); + common = false; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], Wifi Connected-Idle + BT Busy!!\n"); + common = + btc8821a2ant_action_wifi_idle_process( + btcoexist); + } } - btc8821a2ant_sw_mechanism1(btcoexist, true, true, true, true); } return common; } -- cgit From 20ec48e51d252daa5c829061b7af596faf75209b Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:07:09 -0500 Subject: rtlwifi: btcoex: 21a 2ant: fix invalid argument passed The dac swing level should be an unsigned 32-bit value rather than boolean. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index f640f6079f9a..3bdf691c9d08 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -1090,7 +1090,7 @@ static void btc8821a2ant_sw_mechanism2(struct btc_coexist *btcoexist, bool sw_dac_swing, u32 dac_swing_lvl) { btc8821a2ant_dac_swing(btcoexist, NORMAL_EXEC, sw_dac_swing, - sw_dac_swing); + dac_swing_lvl); } static void btc8821a2ant_set_ant_path(struct btc_coexist *btcoexist, -- cgit From 3f775d5cee7966e41354a45377e34b9c01cf1f79 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Thu, 13 Apr 2017 13:07:10 -0500 Subject: rtlwifi: btcoex: 21a 2ant: refine tdma duration adjust function 1. Add more cases to adjust the wifi duration and add a case with the max interval of 3 for some future uses. 2. rename tdma_adj -> ps_tdma_du_adj to indicate that this member is for power saving tdma duration adjustment Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 968 +++++++++++++++++++-- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.h | 3 +- 2 files changed, 893 insertions(+), 78 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 3bdf691c9d08..0ef83727fdf4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -1716,9 +1716,9 @@ static bool btc8821a2ant_is_common_action(struct btc_coexist *btcoexist) return common; } -static void btc8821a2ant_tdma_dur_adj(struct btc_coexist *btcoexist, - bool sco_hid, bool tx_pause, - u8 max_interval) +static void btc8821a2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, + bool sco_hid, bool tx_pause, + u8 max_interval) { struct rtl_priv *rtlpriv = btcoexist->adapter; static long up, dn, m, n, wait_count; @@ -1732,80 +1732,84 @@ static void btc8821a2ant_tdma_dur_adj(struct btc_coexist *btcoexist, RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], TdmaDurationAdjust()\n"); - if (coex_dm->reset_tdma_adjust) { - coex_dm->reset_tdma_adjust = false; + if (coex_dm->auto_tdma_adjust) { + coex_dm->auto_tdma_adjust = false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], first run TdmaDurationAdjust()!!\n"); if (sco_hid) { if (tx_pause) { if (max_interval == 1) { btc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 13); - coex_dm->tdma_adj_type = 13; + NORMAL_EXEC, true, 13); + coex_dm->ps_tdma_du_adj_type = 13; } else if (max_interval == 2) { btc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 14); - coex_dm->tdma_adj_type = 14; + NORMAL_EXEC, true, 14); + coex_dm->ps_tdma_du_adj_type = 14; + } else if (max_interval == 3) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 15); + coex_dm->ps_tdma_du_adj_type = 15; } else { btc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 15); - coex_dm->tdma_adj_type = 15; + NORMAL_EXEC, true, 15); + coex_dm->ps_tdma_du_adj_type = 15; } } else { if (max_interval == 1) { btc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 9); - coex_dm->tdma_adj_type = 9; + NORMAL_EXEC, true, 9); + coex_dm->ps_tdma_du_adj_type = 9; } else if (max_interval == 2) { btc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 10); - coex_dm->tdma_adj_type = 10; + NORMAL_EXEC, true, 10); + coex_dm->ps_tdma_du_adj_type = 10; + } else if (max_interval == 3) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 11); + coex_dm->ps_tdma_du_adj_type = 11; } else { btc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 11); - coex_dm->tdma_adj_type = 11; + NORMAL_EXEC, true, 11); + coex_dm->ps_tdma_du_adj_type = 11; } } } else { if (tx_pause) { if (max_interval == 1) { btc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 5); - coex_dm->tdma_adj_type = 5; + NORMAL_EXEC, true, 5); + coex_dm->ps_tdma_du_adj_type = 5; } else if (max_interval == 2) { btc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 6); - coex_dm->tdma_adj_type = 6; + NORMAL_EXEC, true, 6); + coex_dm->ps_tdma_du_adj_type = 6; + } else if (max_interval == 3) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 7); + coex_dm->ps_tdma_du_adj_type = 7; } else { btc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 7); - coex_dm->tdma_adj_type = 7; + NORMAL_EXEC, true, 7); + coex_dm->ps_tdma_du_adj_type = 7; } } else { if (max_interval == 1) { btc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 1); - coex_dm->tdma_adj_type = 1; + NORMAL_EXEC, true, 1); + coex_dm->ps_tdma_du_adj_type = 1; } else if (max_interval == 2) { btc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 2); - coex_dm->tdma_adj_type = 2; + NORMAL_EXEC, true, 2); + coex_dm->ps_tdma_du_adj_type = 2; + } else if (max_interval == 3) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 3); + coex_dm->ps_tdma_du_adj_type = 3; } else { btc8821a2ant_ps_tdma(btcoexist, - NORMAL_EXEC, - true, 3); - coex_dm->tdma_adj_type = 3; + NORMAL_EXEC, true, 3); + coex_dm->ps_tdma_du_adj_type = 3; } } } @@ -1902,18 +1906,716 @@ static void btc8821a2ant_tdma_dur_adj(struct btc_coexist *btcoexist, RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], max Interval = %d\n", max_interval); + + if (max_interval == 1) { + if (tx_pause) { + if (coex_dm->cur_ps_tdma == 71) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 5); + coex_dm->ps_tdma_du_adj_type = 5; + } else if (coex_dm->cur_ps_tdma == 1) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 5); + coex_dm->ps_tdma_du_adj_type = 5; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 6); + coex_dm->ps_tdma_du_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 7); + coex_dm->ps_tdma_du_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 4) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 8); + coex_dm->ps_tdma_du_adj_type = 8; + } + if (coex_dm->cur_ps_tdma == 9) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 13); + coex_dm->ps_tdma_du_adj_type = 13; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 14); + coex_dm->ps_tdma_du_adj_type = 14; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 15); + coex_dm->ps_tdma_du_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 16); + coex_dm->ps_tdma_du_adj_type = 16; + } + + if (result == -1) { + if (coex_dm->cur_ps_tdma == 5) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->ps_tdma_du_adj_type = + 6; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 8); + coex_dm->ps_tdma_du_adj_type = + 8; + } else if (coex_dm->cur_ps_tdma == 13) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->ps_tdma_du_adj_type = + 14; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 16); + coex_dm->ps_tdma_du_adj_type = + 16; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 8) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->ps_tdma_du_adj_type = + 6; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 5); + coex_dm->ps_tdma_du_adj_type = + 5; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->ps_tdma_du_adj_type = + 14; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 13); + coex_dm->ps_tdma_du_adj_type = + 13; + } + } + } else { + if (coex_dm->cur_ps_tdma == 5) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 71); + coex_dm->ps_tdma_du_adj_type = 71; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 2); + coex_dm->ps_tdma_du_adj_type = 2; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 3); + coex_dm->ps_tdma_du_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 8) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 4); + coex_dm->ps_tdma_du_adj_type = 4; + } + if (coex_dm->cur_ps_tdma == 13) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 9); + coex_dm->ps_tdma_du_adj_type = 9; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 10); + coex_dm->ps_tdma_du_adj_type = 10; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 11); + coex_dm->ps_tdma_du_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 12); + coex_dm->ps_tdma_du_adj_type = 12; + } + + if (result == -1) { + if (coex_dm->cur_ps_tdma == 71) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 1); + coex_dm->ps_tdma_du_adj_type = + 1; + } else if (coex_dm->cur_ps_tdma == 1) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->ps_tdma_du_adj_type = + 2; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 4); + coex_dm->ps_tdma_du_adj_type = + 4; + } else if (coex_dm->cur_ps_tdma == 9) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->ps_tdma_du_adj_type = + 10; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 12); + coex_dm->ps_tdma_du_adj_type = + 12; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 4) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->ps_tdma_du_adj_type = + 2; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 1); + coex_dm->ps_tdma_du_adj_type = + 1; + } else if (coex_dm->cur_ps_tdma == 1) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 71); + coex_dm->ps_tdma_du_adj_type = + 71; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->ps_tdma_du_adj_type = + 10; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 9); + coex_dm->ps_tdma_du_adj_type = + 9; + } + } + } + } else if (max_interval == 2) { + if (tx_pause) { + if (coex_dm->cur_ps_tdma == 1) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 6); + coex_dm->ps_tdma_du_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 6); + coex_dm->ps_tdma_du_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 7); + coex_dm->ps_tdma_du_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 4) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 8); + coex_dm->ps_tdma_du_adj_type = 8; + } + if (coex_dm->cur_ps_tdma == 9) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 14); + coex_dm->ps_tdma_du_adj_type = 14; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 14); + coex_dm->ps_tdma_du_adj_type = 14; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 15); + coex_dm->ps_tdma_du_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 16); + coex_dm->ps_tdma_du_adj_type = 16; + } + if (result == -1) { + if (coex_dm->cur_ps_tdma == 5) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->ps_tdma_du_adj_type = + 6; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 8); + coex_dm->ps_tdma_du_adj_type = + 8; + } else if (coex_dm->cur_ps_tdma == 13) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->ps_tdma_du_adj_type = + 14; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 16); + coex_dm->ps_tdma_du_adj_type = + 16; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 8) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->ps_tdma_du_adj_type = + 6; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->ps_tdma_du_adj_type = + 6; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->ps_tdma_du_adj_type = + 14; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->ps_tdma_du_adj_type = + 14; + } + } + } else { + if (coex_dm->cur_ps_tdma == 5) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 2); + coex_dm->ps_tdma_du_adj_type = 2; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 2); + coex_dm->ps_tdma_du_adj_type = 2; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 3); + coex_dm->ps_tdma_du_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 8) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 4); + coex_dm->ps_tdma_du_adj_type = 4; + } + if (coex_dm->cur_ps_tdma == 13) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 10); + coex_dm->ps_tdma_du_adj_type = 10; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 10); + coex_dm->ps_tdma_du_adj_type = 10; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 11); + coex_dm->ps_tdma_du_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 12); + coex_dm->ps_tdma_du_adj_type = 12; + } + if (result == -1) { + if (coex_dm->cur_ps_tdma == 1) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->ps_tdma_du_adj_type = + 2; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 4); + coex_dm->ps_tdma_du_adj_type = + 4; + } else if (coex_dm->cur_ps_tdma == 9) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->ps_tdma_du_adj_type = + 10; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 12); + coex_dm->ps_tdma_du_adj_type = + 12; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 4) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->ps_tdma_du_adj_type = + 2; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->ps_tdma_du_adj_type = + 2; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->ps_tdma_du_adj_type = + 10; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->ps_tdma_du_adj_type = + 10; + } + } + } + } else if (max_interval == 3) { + if (tx_pause) { + if (coex_dm->cur_ps_tdma == 1) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 7); + coex_dm->ps_tdma_du_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 7); + coex_dm->ps_tdma_du_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 7); + coex_dm->ps_tdma_du_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 4) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 8); + coex_dm->ps_tdma_du_adj_type = 8; + } + if (coex_dm->cur_ps_tdma == 9) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 15); + coex_dm->ps_tdma_du_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 15); + coex_dm->ps_tdma_du_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 15); + coex_dm->ps_tdma_du_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 16); + coex_dm->ps_tdma_du_adj_type = 16; + } + if (result == -1) { + if (coex_dm->cur_ps_tdma == 5) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 8); + coex_dm->ps_tdma_du_adj_type = + 8; + } else if (coex_dm->cur_ps_tdma == 13) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 16); + coex_dm->ps_tdma_du_adj_type = + 16; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 8) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->ps_tdma_du_adj_type = + 7; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->ps_tdma_du_adj_type = + 15; + } + } + } else { + if (coex_dm->cur_ps_tdma == 5) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 3); + coex_dm->ps_tdma_du_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 3); + coex_dm->ps_tdma_du_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 3); + coex_dm->ps_tdma_du_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 8) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 4); + coex_dm->ps_tdma_du_adj_type = 4; + } + if (coex_dm->cur_ps_tdma == 13) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 11); + coex_dm->ps_tdma_du_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 11); + coex_dm->ps_tdma_du_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 11); + coex_dm->ps_tdma_du_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8821a2ant_ps_tdma(btcoexist, + NORMAL_EXEC, true, 12); + coex_dm->ps_tdma_du_adj_type = 12; + } + if (result == -1) { + if (coex_dm->cur_ps_tdma == 1) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 4); + coex_dm->ps_tdma_du_adj_type = + 4; + } else if (coex_dm->cur_ps_tdma == 9) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 12); + coex_dm->ps_tdma_du_adj_type = + 12; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 4) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->ps_tdma_du_adj_type = + 3; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8821a2ant_ps_tdma( + btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->ps_tdma_du_adj_type = + 11; + } + } + } + } } /* if current PsTdma not match with the recorded one * (when scan, dhcp...), then we have to adjust it back to * the previous recorded one. */ - if (coex_dm->cur_ps_tdma != coex_dm->tdma_adj_type) { - bool scan = false, link = false, roam = false; + if (coex_dm->cur_ps_tdma != coex_dm->ps_tdma_du_adj_type) { + bool scan = false, link = false, roam = false; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], PsTdma type dismatch!!!, cur_ps_tdma = %d, recordPsTdma = %d\n", - coex_dm->cur_ps_tdma, coex_dm->tdma_adj_type); + coex_dm->cur_ps_tdma, coex_dm->ps_tdma_du_adj_type); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan); btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link); @@ -1921,14 +2623,12 @@ static void btc8821a2ant_tdma_dur_adj(struct btc_coexist *btcoexist, if (!scan && !link && !roam) { btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, - coex_dm->tdma_adj_type); + coex_dm->ps_tdma_du_adj_type); } else { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], roaming/link/scan is under progress, will adjust next time!!!\n"); } } - - btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 0x6); } /* SCO only or SCO+PAN(HS)*/ @@ -2082,7 +2782,7 @@ static void btc8821a2ant_action_a2dp(struct btc_coexist *btcoexist) 15, 0); bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); - /* fw dac swing is called in btc8821a2ant_tdma_dur_adj() + /* fw dac swing is called in btc8821a2ant_tdma_duration_adjust() * btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); */ @@ -2097,9 +2797,11 @@ static void btc8821a2ant_action_a2dp(struct btc_coexist *btcoexist) /* fw mechanism */ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_tdma_dur_adj(btcoexist, false, false, 1); + btc8821a2ant_tdma_duration_adjust(btcoexist, false, + false, 1); } else { - btc8821a2ant_tdma_dur_adj(btcoexist, false, true, 1); + btc8821a2ant_tdma_duration_adjust(btcoexist, false, + true, 1); } /* sw mechanism */ @@ -2119,9 +2821,11 @@ static void btc8821a2ant_action_a2dp(struct btc_coexist *btcoexist) /* fw mechanism */ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_tdma_dur_adj(btcoexist, false, false, 1); + btc8821a2ant_tdma_duration_adjust(btcoexist, false, + false, 1); } else { - btc8821a2ant_tdma_dur_adj(btcoexist, false, true, 1); + btc8821a2ant_tdma_duration_adjust(btcoexist, false, + true, 1); } /* sw mechanism */ @@ -2160,10 +2864,12 @@ static void btc8821a2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) /* fw mechanism */ if (bt_info_ext&BIT0) { /* a2dp basic rate */ - btc8821a2ant_tdma_dur_adj(btcoexist, false, true, 2); + btc8821a2ant_tdma_duration_adjust(btcoexist, false, + true, 2); } else { /* a2dp edr rate */ - btc8821a2ant_tdma_dur_adj(btcoexist, false, true, 1); + btc8821a2ant_tdma_duration_adjust(btcoexist, false, + true, 1); } /* sw mechanism */ @@ -2183,10 +2889,12 @@ static void btc8821a2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) /* fw mechanism */ if (bt_info_ext&BIT0) { /* a2dp basic rate */ - btc8821a2ant_tdma_dur_adj(btcoexist, false, true, 2); + btc8821a2ant_tdma_duration_adjust(btcoexist, false, + true, 2); } else { /* a2dp edr rate */ - btc8821a2ant_tdma_dur_adj(btcoexist, false, true, 1); + btc8821a2ant_tdma_duration_adjust(btcoexist, false, + true, 1); } /* sw mechanism */ @@ -2384,12 +3092,27 @@ static void btc8821a2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) if (BTC_WIFI_BW_HT40 == wifi_bw) { /* fw mechanism */ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) - btc8821a2ant_tdma_dur_adj(btcoexist, false, - false, 3); - else - btc8821a2ant_tdma_dur_adj(btcoexist, false, - true, 3); + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + if (bt_info_ext&BIT0) { + /* a2dp basic rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + false, false, 3); + } else { + /* a2dp edr rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + false, false, 3); + } + } else { + if (bt_info_ext&BIT0) { + /* a2dp basic rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + false, true, 3); + } else { + /* a2dp edr rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + false, true, 3); + } + } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || @@ -2407,10 +3130,27 @@ static void btc8821a2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) } else { /* fw mechanism */ if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) - btc8821a2ant_tdma_dur_adj(btcoexist, false, false, 3); - else - btc8821a2ant_tdma_dur_adj(btcoexist, false, true, 3); + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + if (bt_info_ext&BIT0) { + /* a2dp basic rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + false, false, 3); + } else { + /* a2dp edr rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + false, false, 3); + } + } else { + if (bt_info_ext&BIT0) { + /* a2dp basic rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + false, true, 3); + } else { + /* a2dp edr rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + false, true, 3); + } + } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || @@ -2538,7 +3278,28 @@ static void btc8821a2ant_act_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) if (BTC_WIFI_BW_HT40 == wifi_bw) { /* fw mechanism */ - btc8821a2ant_tdma_dur_adj(btcoexist, true, true, 3); + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + if (bt_info_ext&BIT0) { + /* a2dp basic rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, 3); + } else { + /* a2dp edr rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, 3); + } + } else { + if (bt_info_ext&BIT0) { + /* a2dp basic rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, 3); + } else { + /* a2dp edr rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, 3); + } + } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || @@ -2559,22 +3320,24 @@ static void btc8821a2ant_act_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { if (bt_info_ext&BIT0) { /* a2dp basic rate */ - btc8821a2ant_tdma_dur_adj(btcoexist, true, - false, 3); + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, false, 3); } else { /* a2dp edr rate */ - btc8821a2ant_tdma_dur_adj(btcoexist, true, - false, 3); + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, false, 3); } } else { if (bt_info_ext&BIT0) { /* a2dp basic rate */ - btc8821a2ant_tdma_dur_adj(btcoexist, true, - true, 3); + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, + 3); } else { /* a2dp edr rate */ - btc8821a2ant_tdma_dur_adj(btcoexist, true, - true, 3); + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, + 3); } } @@ -2622,7 +3385,32 @@ static void btc8821a2ant_action_hid_a2dp(struct btc_coexist *btcoexist) if (BTC_WIFI_BW_HT40 == wifi_bw) { /* fw mechanism */ - btc8821a2ant_tdma_dur_adj(btcoexist, true, true, 2); + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + if (bt_info_ext & BIT0) { + /* a2dp basic rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, + 2); + } else { + /* a2dp edr rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, + 2); + } + } else { + if (bt_info_ext & BIT0) { + /* a2dp basic rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, + 2); + } else { + /* a2dp edr rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, + 2); + } + } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || @@ -2639,7 +3427,33 @@ static void btc8821a2ant_action_hid_a2dp(struct btc_coexist *btcoexist) } } else { /* fw mechanism */ - btc8821a2ant_tdma_dur_adj(btcoexist, true, true, 2); + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + if (bt_info_ext & BIT0) { + /* a2dp basic rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, + 2); + + } else { + /* a2dp edr rate */ + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, + 2); + } + } else { + if (bt_info_ext & BIT0) { + /*a2dp basic rate*/ + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, + 2); + } else { + /*a2dp edr rate*/ + btc8821a2ant_tdma_duration_adjust(btcoexist, + true, true, + 2); + } + } /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h index 603e446b5953..535ca10e910b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.h @@ -87,8 +87,9 @@ struct coex_dm_8821a_2ant { u8 pre_ps_tdma; u8 cur_ps_tdma; u8 ps_tdma_para[5]; - u8 tdma_adj_type; + u8 ps_tdma_du_adj_type; bool reset_tdma_adjust; + bool auto_tdma_adjust; bool pre_ps_tdma_on; bool cur_ps_tdma_on; bool pre_bt_auto_report; -- cgit From 1a2534930e9ec51fe459dc5dd592e496ecd99750 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:31:47 -0500 Subject: rtlwifi: btcoex: 21a 2ant: turn on sw dac swing and check if is sco_only Use software dac swing and double check if it is sco_only to set the tdma for voice quality. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 55 ++++++++++------------ 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 0ef83727fdf4..7b5dbc62906e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -2634,13 +2634,15 @@ static void btc8821a2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, /* SCO only or SCO+PAN(HS)*/ static void btc8821a2ant_action_sco(struct btc_coexist *btcoexist) { + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; u8 wifi_rssi_state, bt_rssi_state; u32 wifi_bw; - wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, - 15, 0); + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 4); @@ -2656,58 +2658,49 @@ static void btc8821a2ant_action_sco(struct btc_coexist *btcoexist) btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 2); } else { /* for SCO quality & wifi performance balance at 11n mode */ - btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, - 0x5aea5aea, 0x5aea5aea, 0xffff, 0x3); - } - - if (wifi_bw == BTC_WIFI_BW_HT40) { - - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - /* for voice quality */ - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 0); + if (wifi_bw == BTC_WIFI_BW_HT40) { + btc8821a2ant_coex_table_with_type(btcoexist, + NORMAL_EXEC, 8); } else { - /* for voice quality */ - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - false, 0); + if (bt_link_info->sco_only) + btc8821a2ant_coex_table_with_type( + btcoexist, NORMAL_EXEC, 17); + else + btc8821a2ant_coex_table_with_type( + btcoexist, NORMAL_EXEC, 12); } + } - /* sw mechanism */ + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); + /* for voice quality */ + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); + + /* sw mechanism */ + if (wifi_bw == BTC_WIFI_BW_HT40) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8821a2ant_sw_mechanism1(btcoexist, true, true, false, false); btc8821a2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); + true, 0x18); } else { btc8821a2ant_sw_mechanism1(btcoexist, true, true, false, false); btc8821a2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); + true, 0x18); } } else { - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - /* for voice quality */ - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); - } else { - /* for voice quality */ - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); - } - - /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8821a2ant_sw_mechanism1(btcoexist, false, true, false, false); btc8821a2ant_sw_mechanism2(btcoexist, true, false, - false, 0x18); + true, 0x18); } else { btc8821a2ant_sw_mechanism1(btcoexist, false, true, false, false); btc8821a2ant_sw_mechanism2(btcoexist, false, false, - false, 0x18); + true, 0x18); } } } -- cgit From 124e50ff6582a45668c18984e6371f3ca5a30ba7 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:31:48 -0500 Subject: rtlwifi: btcoex: 21a 2ant: add threshold to examine bt rssi The threshold is used to adjust the base line for the rssi state. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 7b5dbc62906e..b880bc2eaf85 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -2711,7 +2711,8 @@ static void btc8821a2ant_action_hid(struct btc_coexist *btcoexist) u32 wifi_bw; wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); - bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, + 2, BT_8821A_2ANT_BT_RSSI_COEXSWITCH_THRES, 0); btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); -- cgit From 10c2e1cc15e4e2202413eae601dd229682cb9bea Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:31:49 -0500 Subject: rtlwifi: btcoex: 21a 2ant: force wifi to use RF path A Let the wifi use main antenna to have higher power. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index b880bc2eaf85..8437f1b6c108 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -2714,6 +2714,8 @@ static void btc8821a2ant_action_hid(struct btc_coexist *btcoexist) bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, BT_8821A_2ANT_BT_RSSI_COEXSWITCH_THRES, 0); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); -- cgit From 70a8adef7108f3b8773c35b80fbd46d9d7df92b3 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:31:50 -0500 Subject: rtlwifi: btcoex: 21a 2ant: more combinations of wifi/bt rssi state For bt a2dp, we need to check more rssi state combinations to have better voice quality. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 97 +++++++++++++++------- 1 file changed, 65 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 8437f1b6c108..62c3556e83b5 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -2771,36 +2771,80 @@ static void btc8821a2ant_action_hid(struct btc_coexist *btcoexist) /* A2DP only / PAN(EDR) only/ A2DP+PAN(HS) */ static void btc8821a2ant_action_a2dp(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; + u8 ap_num = 0; u32 wifi_bw; - wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, - 15, 0); - bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); + wifi_rssi_state1 = btc8821a2ant_wifi_rssi_state(btcoexist, 1, 2, + BT_8821A_2ANT_WIFI_RSSI_COEXSWITCH_THRES, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, + 2, BT_8821A_2ANT_BT_RSSI_COEXSWITCH_THRES, 0); - /* fw dac swing is called in btc8821a2ant_tdma_duration_adjust() - * btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - */ + if ((ap_num >= 10) && BTC_RSSI_HIGH(wifi_rssi_state1) && + BTC_RSSI_HIGH(bt_rssi_state)) { + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); - if (BTC_RSSI_HIGH(bt_rssi_state)) - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); - else - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, + 0x0); + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, + 0x8); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); - btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); - if (BTC_WIFI_BW_HT40 == wifi_bw) { - /* fw mechanism */ - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_tdma_duration_adjust(btcoexist, false, - false, 1); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 23); + + /* sw mechanism */ + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + if (wifi_bw == BTC_WIFI_BW_HT40) { + btc8821a2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + true, 0x6); } else { - btc8821a2ant_tdma_duration_adjust(btcoexist, false, - true, 1); + btc8821a2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8821a2ant_sw_mechanism2(btcoexist, true, false, + true, 0x6); } + return; + } - /* sw mechanism */ + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + + if (BTC_RSSI_HIGH(bt_rssi_state)) + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + else + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + + if (BTC_RSSI_HIGH(wifi_rssi_state1) && BTC_RSSI_HIGH(bt_rssi_state)) { + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + } else { + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 13); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_LPS_ON, 0x50, + 0x4); + } + + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 23); + } else { + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 23); + } + + /* sw mechanism */ + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + if (wifi_bw == BTC_WIFI_BW_HT40) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8821a2ant_sw_mechanism1(btcoexist, true, false, @@ -2814,17 +2858,6 @@ static void btc8821a2ant_action_a2dp(struct btc_coexist *btcoexist) false, 0x18); } } else { - /* fw mechanism */ - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_tdma_duration_adjust(btcoexist, false, - false, 1); - } else { - btc8821a2ant_tdma_duration_adjust(btcoexist, false, - true, 1); - } - - /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8821a2ant_sw_mechanism1(btcoexist, false, false, -- cgit From f0557cf062c7d7f220aa89fbdc037d208e4ad11b Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:31:51 -0500 Subject: rtlwifi: btcoex: 21a 2ant: fix some coding style issues Fix some ident and naming for linux coding style. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 62c3556e83b5..bbce0cbe2e9a 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -3512,8 +3512,7 @@ static void btc8821a2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) return; } - btcoexist->btc_get(btcoexist, - BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g); if (wifi_under_5g) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -3629,15 +3628,15 @@ void ex_btc8821a2ant_init_hwconfig(struct btc_coexist *btcoexist) u1tmp |= 0x5; btcoexist->btc_write_1byte(btcoexist, 0x790, u1tmp); - /*Antenna config */ + /* Antenna config */ btc8821a2ant_set_ant_path(btcoexist, BTC_ANT_WIFI_AT_MAIN, true, false); /* PTA parameter */ btc8821a2ant_coex_table_with_type(btcoexist, FORCE_EXEC, 0); /* Enable counter statistics */ - /*0x76e[3] = 1, WLAN_Act control by PTA*/ - btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc); + /* 0x76e[3] = 1, WLAN_Act control by PTA */ + btcoexist->btc_write_1byte(btcoexist, 0x76e, 0x4); btcoexist->btc_write_1byte(btcoexist, 0x778, 0x3); btcoexist->btc_write_1byte_bitmask(btcoexist, 0x40, 0x20, 0x1); } -- cgit From 137cc90f094a53496a8b497e54e2d699291eaa7a Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:31:52 -0500 Subject: rtlwifi: btcoex: 21a 2ant: set tdma based on rssi state amd limit rx agg size Monitor the rssi state to set the tdma and limit rx aggregation size to fit the bt profiling. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 51 ++++++++++------------ 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index bbce0cbe2e9a..9df5b4c195ff 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -2875,33 +2875,40 @@ static void btc8821a2ant_action_a2dp(struct btc_coexist *btcoexist) static void btc8821a2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state, bt_info_ext; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; u32 wifi_bw; - bt_info_ext = coex_sta->bt_info_ext; wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); - bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state1 = btc8821a2ant_wifi_rssi_state(btcoexist, 1, 2, + BT_8821A_2ANT_WIFI_RSSI_COEXSWITCH_THRES, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, + 2, BT_8821A_2ANT_BT_RSSI_COEXSWITCH_THRES, 0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); else btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); - btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + if (BTC_RSSI_HIGH(wifi_rssi_state1) && BTC_RSSI_HIGH(bt_rssi_state)) { + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + } else { + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 13); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_LPS_ON, 0x50, + 0x4); + } - if (wifi_bw == BTC_WIFI_BW_HT40) { - /* fw mechanism */ - if (bt_info_ext&BIT0) { - /* a2dp basic rate */ - btc8821a2ant_tdma_duration_adjust(btcoexist, false, - true, 2); - } else { - /* a2dp edr rate */ - btc8821a2ant_tdma_duration_adjust(btcoexist, false, - true, 1); - } + btc8821a2ant_tdma_duration_adjust(btcoexist, false, true, 2); - /* sw mechanism */ + /* sw mechanism */ + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + if (wifi_bw == BTC_WIFI_BW_HT40) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8821a2ant_sw_mechanism1(btcoexist, true, false, @@ -2915,18 +2922,6 @@ static void btc8821a2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) false, 0x18); } } else { - /* fw mechanism */ - if (bt_info_ext&BIT0) { - /* a2dp basic rate */ - btc8821a2ant_tdma_duration_adjust(btcoexist, false, - true, 2); - } else { - /* a2dp edr rate */ - btc8821a2ant_tdma_duration_adjust(btcoexist, false, - true, 1); - } - - /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8821a2ant_sw_mechanism1(btcoexist, false, false, -- cgit From 0fcad45bdbf7b013dabc3a09026ad47f01ea5fd2 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:31:53 -0500 Subject: rtlwifi: btcoex: 21a 2ant: add multiport action for p2p/miracast For p2p/miracast, the wifi may have multiple ports for different roles. Under this, we need extra settings for turning off the tdma and proper coex table parameters. We monnitor the number of links on a port to determine if it is for p2p/miracast or not. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 9df5b4c195ff..4471bd66a2e5 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -3495,11 +3495,31 @@ static void btc8821a2ant_action_hid_a2dp(struct btc_coexist *btcoexist) } } +static void btc8821a2ant_action_wifi_multi_port(struct btc_coexist *btcoexist) +{ + btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + + /* sw all off */ + btc8821a2ant_sw_mechanism1(btcoexist, false, false, false, false); + btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); + + /* hw all off */ + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 0); + + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); +} + static void btc8821a2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) { struct rtl_priv *rtlpriv = btcoexist->adapter; + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; bool wifi_under_5g = false; u8 algorithm = 0; + u32 num_of_wifi_link = 0; + u32 wifi_link_status = 0; + bool miracast_plus_bt = false; if (btcoexist->manual_control) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -3525,6 +3545,33 @@ static void btc8821a2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) return; } + /* for P2P */ + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_LINK_STATUS, + &wifi_link_status); + num_of_wifi_link = wifi_link_status >> 16; + + if ((num_of_wifi_link >= 2) || + (wifi_link_status & WIFI_P2P_GO_CONNECTED)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "############# [BTCoex], Multi-Port num_of_wifi_link = %d, wifi_link_status = 0x%x\n", + num_of_wifi_link, wifi_link_status); + + if (bt_link_info->bt_link_exist) + miracast_plus_bt = true; + else + miracast_plus_bt = false; + + btcoexist->btc_set(btcoexist, BTC_SET_BL_MIRACAST_PLUS_BT, + &miracast_plus_bt); + btc8821a2ant_action_wifi_multi_port(btcoexist); + + return; + } + + miracast_plus_bt = false; + btcoexist->btc_set(btcoexist, BTC_SET_BL_MIRACAST_PLUS_BT, + &miracast_plus_bt); + coex_dm->cur_algorithm = algorithm; RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, "[BTCoex], Algorithm = %d\n", coex_dm->cur_algorithm); -- cgit From a4162ea71b39667e0393480080819be095fc7a33 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:31:54 -0500 Subject: rtlwifi: btcoex: 21a 2ant: monitor extra wifi rssi to examine network status Here we monitor one more wifi rssi to check the status of the network and set the coex table instead of the legacy way. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 51 +++++++++------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 4471bd66a2e5..b93a4f7ce17a 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -2939,11 +2939,18 @@ static void btc8821a2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) static void btc8821a2ant_action_pan_edr(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; u32 wifi_bw; wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); - bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state1 = btc8821a2ant_wifi_rssi_state(btcoexist, 1, 2, + BT_8821A_2ANT_WIFI_RSSI_COEXSWITCH_THRES, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, + 2, BT_8821A_2ANT_BT_RSSI_COEXSWITCH_THRES, 0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); @@ -2952,30 +2959,25 @@ static void btc8821a2ant_action_pan_edr(struct btc_coexist *btcoexist) else btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - - if (BTC_WIFI_BW_LEGACY == wifi_bw) { - /* for HID at 11b/g mode */ - btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5aff5aff, 0xffff, 0x3); + if (BTC_RSSI_HIGH(wifi_rssi_state1) && BTC_RSSI_HIGH(bt_rssi_state)) { + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 10); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); } else { btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 13); btc8821a2ant_power_save_state(btcoexist, BTC_PS_LPS_ON, 0x50, 0x4); } - if (BTC_WIFI_BW_HT40 == wifi_bw) { - /* fw mechanism */ - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 1); - } else { - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); - } + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 26); + else + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 26); - /* sw mechanism */ + /* sw mechanism */ + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + if (wifi_bw == BTC_WIFI_BW_HT40) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8821a2ant_sw_mechanism1(btcoexist, true, false, @@ -2989,17 +2991,6 @@ static void btc8821a2ant_action_pan_edr(struct btc_coexist *btcoexist) false, 0x18); } } else { - /* fw mechanism */ - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 1); - } else { - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, - true, 5); - } - - /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8821a2ant_sw_mechanism1(btcoexist, false, false, -- cgit From f76184d024571c9f39d66a38bedfab33e6322039 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:31:55 -0500 Subject: rtlwifi: btcoex: 21a 2ant: notify fw the number of APs Use h2c to tell the firmware if the number of AP is more than 10 or not. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index b93a4f7ce17a..9e152117730c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -3982,6 +3982,7 @@ void ex_btc8821a2ant_media_status_notify(struct btc_coexist *btcoexist, u8 h2c_parameter[3] = {0}; u32 wifi_bw; u8 wifi_central_chnl; + u8 ap_num = 0; if (BTC_MEDIA_CONNECT == type) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -3999,10 +4000,15 @@ void ex_btc8821a2ant_media_status_notify(struct btc_coexist *btcoexist, h2c_parameter[0] = 0x1; h2c_parameter[1] = wifi_central_chnl; btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - if (BTC_WIFI_BW_HT40 == wifi_bw) + if (wifi_bw == BTC_WIFI_BW_HT40) { h2c_parameter[2] = 0x30; - else + } else { h2c_parameter[2] = 0x20; + if (ap_num < 10) + h2c_parameter[2] = 0x30; + else + h2c_parameter[2] = 0x20; + } } coex_dm->wifi_chnl_info[0] = h2c_parameter[0]; -- cgit From c129bc843b629623580c248e9f7f91c1f503dc9d Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:31:56 -0500 Subject: rtlwifi: btcoex: 21a 2ant: dec bt power according to bt rssi and set tdma Check the bt rssi first and decrease it if the bt rssi is too high. Then set the tdma and coex table. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Signed-off-by: Kalle Valo --- .../realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 140 +++++++-------------- 1 file changed, 48 insertions(+), 92 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 9e152117730c..626b73fd97a5 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -3009,28 +3009,31 @@ static void btc8821a2ant_action_pan_edr(struct btc_coexist *btcoexist) /* PAN(HS) only */ static void btc8821a2ant_action_pan_hs(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; u32 wifi_bw; wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); - bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state1 = btc8821a2ant_wifi_rssi_state(btcoexist, 1, 2, + BT_8821A_2ANT_WIFI_RSSI_COEXSWITCH_THRES, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, + 2, BT_8821A_2ANT_BT_RSSI_COEXSWITCH_THRES, 0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); - btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + if (BTC_RSSI_HIGH(bt_rssi_state)) + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 2); + else + btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); - if (BTC_WIFI_BW_HT40 == wifi_bw) { - /* fw mechanism */ - if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || - (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); - } else { - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - } - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 7); + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, 0x0, 0x0); + btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); - /* sw mechanism */ + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + if (wifi_bw == BTC_WIFI_BW_HT40) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8821a2ant_sw_mechanism1(btcoexist, true, false, @@ -3044,22 +3047,6 @@ static void btc8821a2ant_action_pan_hs(struct btc_coexist *btcoexist) false, 0x18); } } else { - /* fw mechanism */ - if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || - (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); - } else { - btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); - } - - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); - } else { - btc8821a2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); - } - - /* sw mechanism */ if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8821a2ant_sw_mechanism1(btcoexist, false, false, @@ -3078,12 +3065,18 @@ static void btc8821a2ant_action_pan_hs(struct btc_coexist *btcoexist) /* PAN(EDR)+A2DP */ static void btc8821a2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) { - u8 wifi_rssi_state, bt_rssi_state, bt_info_ext; - u32 wifi_bw; + u8 wifi_rssi_state, wifi_rssi_state1, bt_rssi_state; + u32 wifi_bw; - bt_info_ext = coex_sta->bt_info_ext; wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); - bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + wifi_rssi_state1 = btc8821a2ant_wifi_rssi_state(btcoexist, 1, 2, + BT_8821A_2ANT_WIFI_RSSI_COEXSWITCH_THRES, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, + 2, BT_8821A_2ANT_BT_RSSI_COEXSWITCH_THRES, 0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + + btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); @@ -3092,44 +3085,32 @@ static void btc8821a2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) else btc8821a2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, 0); + if (BTC_RSSI_HIGH(wifi_rssi_state1) && BTC_RSSI_HIGH(bt_rssi_state)) + btc8821a2ant_power_save_state(btcoexist, BTC_PS_WIFI_NATIVE, + 0x0, 0x0); + else + btc8821a2ant_power_save_state(btcoexist, BTC_PS_LPS_ON, 0x50, + 0x4); + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); - if (wifi_bw == BTC_WIFI_BW_LEGACY) { - /* for HID at 11b/g mode */ - btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5afa5afa, 0xffff, 0x3); + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 12); + + if (wifi_bw == BTC_WIFI_BW_HT40) + btc8821a2ant_tdma_duration_adjust(btcoexist, false, + true, 3); + else + btc8821a2ant_tdma_duration_adjust(btcoexist, false, + false, 3); } else { - /* for HID quality & wifi performance balance at 11n mode */ - btc8821a2ant_coex_table(btcoexist, NORMAL_EXEC, 0x55ff55ff, - 0x5afa5afa, 0xffff, 0x3); + btc8821a2ant_coex_table_with_type(btcoexist, NORMAL_EXEC, 13); + btc8821a2ant_tdma_duration_adjust(btcoexist, false, true, 3); } - if (BTC_WIFI_BW_HT40 == wifi_bw) { - /* fw mechanism */ - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - if (bt_info_ext&BIT0) { - /* a2dp basic rate */ - btc8821a2ant_tdma_duration_adjust(btcoexist, - false, false, 3); - } else { - /* a2dp edr rate */ - btc8821a2ant_tdma_duration_adjust(btcoexist, - false, false, 3); - } - } else { - if (bt_info_ext&BIT0) { - /* a2dp basic rate */ - btc8821a2ant_tdma_duration_adjust(btcoexist, - false, true, 3); - } else { - /* a2dp edr rate */ - btc8821a2ant_tdma_duration_adjust(btcoexist, - false, true, 3); - } - } - - /* sw mechanism */ + /* sw mechanism */ + if (wifi_bw == BTC_WIFI_BW_HT40) { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8821a2ant_sw_mechanism1(btcoexist, true, false, @@ -3141,33 +3122,8 @@ static void btc8821a2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) false, false); btc8821a2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); - }; - } else { - /* fw mechanism */ - if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || - (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { - if (bt_info_ext&BIT0) { - /* a2dp basic rate */ - btc8821a2ant_tdma_duration_adjust(btcoexist, - false, false, 3); - } else { - /* a2dp edr rate */ - btc8821a2ant_tdma_duration_adjust(btcoexist, - false, false, 3); - } - } else { - if (bt_info_ext&BIT0) { - /* a2dp basic rate */ - btc8821a2ant_tdma_duration_adjust(btcoexist, - false, true, 3); - } else { - /* a2dp edr rate */ - btc8821a2ant_tdma_duration_adjust(btcoexist, - false, true, 3); - } } - - /* sw mechanism */ + } else { if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { btc8821a2ant_sw_mechanism1(btcoexist, false, false, -- cgit From 97632f8c0a934b5f01383db0cc8a1c2ac6cf4e2c Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:31:57 -0500 Subject: rtlwifi: btcoex: 21a 2ant: macro for bt rssi threshold Using macro to control the bt threshold. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 626b73fd97a5..5a8e4b80006b 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -3145,7 +3145,8 @@ static void btc8821a2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) u32 wifi_bw; wifi_rssi_state = btc8821a2ant_wifi_rssi_state(btcoexist, 0, 2, 15, 0); - bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, 35, 0); + bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, + 2, BT_8821A_2ANT_BT_RSSI_COEXSWITCH_THRES, 0); btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); -- cgit From 63a7e8109b2cd0bd73dac3ee503430e3030eee10 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:31:58 -0500 Subject: rtlwifi: btcoex: 21a 2ant: do not limit rx agg size For bt profiling, we do not need to limit the rx agg size. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 5a8e4b80006b..22cb8aa8208d 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -3148,7 +3148,6 @@ static void btc8821a2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) bt_rssi_state = btc8821a2ant_bt_rssi_state(btcoexist, 2, BT_8821A_2ANT_BT_RSSI_COEXSWITCH_THRES, 0); - btc8821a2ant_limited_rx(btcoexist, NORMAL_EXEC, false, false, 0x8); btc8821a2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); if (BTC_RSSI_HIGH(bt_rssi_state)) -- cgit From 67cbe62a27dcc100484fde60c9ac1d89eaac911f Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:31:59 -0500 Subject: rtlwifi: btcoex: 21a 2ant: just return when wifi is under ips If wifi is in power saving mode, do nothing. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 6 ++++++ 1 file changed, 6 insertions(+) Signed-off-by: Kalle Valo --- drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 22cb8aa8208d..0aa36247e402 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -3483,6 +3483,12 @@ static void btc8821a2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) return; } + if (coex_sta->under_ips) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], wifi is under IPS !!!\n"); + return; + } + algorithm = btc8821a2ant_action_algorithm(btcoexist); if (coex_sta->c2h_bt_inquiry_page && (BT_8821A_2ANT_COEX_ALGO_PANHS != algorithm)) { -- cgit From f9558f5fc86b614f905e4f86aed750b433056688 Mon Sep 17 00:00:00 2001 From: Yan-Hsuan Chuang Date: Sat, 15 Apr 2017 15:32:00 -0500 Subject: rtlwifi: btcoex: 21a 2ant: wifi is linking action When wifi is under scanning/linking/roaming, do not run the reset of the coex mechanism because these activities are important for wifi, just run the linking process and return. Signed-off-by: Yan-Hsuan Chuang Signed-off-by: Larry Finger Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c index 0aa36247e402..841b4a83ab70 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8821a2ant.c @@ -3467,6 +3467,7 @@ static void btc8821a2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) u32 num_of_wifi_link = 0; u32 wifi_link_status = 0; bool miracast_plus_bt = false; + bool scan = false, link = false, roam = false; if (btcoexist->manual_control) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, @@ -3498,6 +3499,17 @@ static void btc8821a2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) return; } + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_ROAM, &roam); + + if (scan || link || roam) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[BTCoex], WiFi is under Link Process !!\n"); + btc8821a2ant_action_wifi_link_process(btcoexist); + return; + } + /* for P2P */ btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_LINK_STATUS, &wifi_link_status); -- cgit From 225a644aae03ffe2ec186cd994cd5ab241130035 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 17 Apr 2017 21:30:12 +0200 Subject: rt2800: fix LNA gain assignment for MT7620 The base value used for MT7620 differs from Rt5392 which resulted in quite bad RX signal quality. Fix this by using the correct base value as well as the LNA calibration values for HT20. Reported-by: Tom Psyborg Signed-off-by: Daniel Golle Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index ba06ac2d876d..7135519a638c 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -3806,11 +3806,25 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, } if (rt2x00_rt(rt2x00dev, RT5592) || rt2x00_rt(rt2x00dev, RT6352)) { + reg = 0x10; + if (!conf_is_ht40(conf)) { + if (rt2x00_rt(rt2x00dev, RT6352) && + rt2x00_has_cap_external_lna_bg(rt2x00dev)) { + reg |= 0x5; + } else { + reg |= 0xa; + } + } rt2800_bbp_write(rt2x00dev, 195, 141); - rt2800_bbp_write(rt2x00dev, 196, conf_is_ht40(conf) ? 0x10 : 0x1a); + rt2800_bbp_write(rt2x00dev, 196, reg); /* AGC init */ - reg = (rf->channel <= 14 ? 0x1c : 0x24) + 2 * rt2x00dev->lna_gain; + if (rt2x00_rt(rt2x00dev, RT6352)) + reg = 0x04; + else + reg = rf->channel <= 14 ? 0x1c : 0x24; + + reg += 2 * rt2x00dev->lna_gain; rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); rt2800_iq_calibrate(rt2x00dev, rf->channel); -- cgit From 2031badabf269067f9bc581fea946a27229e4030 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 17 Apr 2017 21:32:12 +0200 Subject: rt2800: do VCO calibration after programming ALC Somehow AP doesn't come up and the first scan fails if we don't do VCO calibration every time. The vendor driver duplicates the VCO calibration function into the channel switching logic, we can do the same with less duplication. Signed-off-by: Daniel Golle Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 7135519a638c..870bf315f98b 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -3407,6 +3407,8 @@ static void rt2800_config_alc(struct rt2x00_dev *rt2x00dev, rt2800_rfcsr_write(rt2x00dev, 42, 0x5b); } rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, mac_sys_ctrl); + + rt2800_vco_calibration(rt2x00dev); } static void rt2800_bbp_write_with_rx_chain(struct rt2x00_dev *rt2x00dev, -- cgit From 4bd96b5d3379b00164f454084bbe0ea7d9f752a7 Mon Sep 17 00:00:00 2001 From: Tomislav Požega Date: Tue, 18 Apr 2017 11:32:35 +0200 Subject: rt2800: fix mt7620 vco calibration registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use register values from init LNA function instead of the ones from restore LNA function. Apply register values based on rx path configuration. Signed-off-by: Tomislav Požega Signed-off-by: Daniel Golle Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 870bf315f98b..86cffee6876a 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -4932,7 +4932,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); if (rt2x00_rt(rt2x00dev, RT6352)) { - if (rt2x00dev->default_ant.tx_chain_num == 1) { + if (rt2x00dev->default_ant.rx_chain_num == 1) { rt2800_bbp_write(rt2x00dev, 91, 0x07); rt2800_bbp_write(rt2x00dev, 95, 0x1A); rt2800_bbp_write(rt2x00dev, 195, 128); @@ -4953,8 +4953,8 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) } if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { - rt2800_bbp_write(rt2x00dev, 75, 0x60); - rt2800_bbp_write(rt2x00dev, 76, 0x44); + rt2800_bbp_write(rt2x00dev, 75, 0x68); + rt2800_bbp_write(rt2x00dev, 76, 0x4C); rt2800_bbp_write(rt2x00dev, 79, 0x1C); rt2800_bbp_write(rt2x00dev, 80, 0x0C); rt2800_bbp_write(rt2x00dev, 82, 0xB6); -- cgit From a0597834dc0e1af68f48f92b31617b360a14dfc8 Mon Sep 17 00:00:00 2001 From: Tomislav Požega Date: Tue, 18 Apr 2017 11:52:07 +0200 Subject: rt2800: fix mt7620 E2 channel registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit update RF register 47 and 54 values according to vendor driver Signed-off-by: Tomislav Požega Signed-off-by: Daniel Golle Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 86cffee6876a..8585cdc3de53 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -8145,9 +8145,11 @@ static void rt2800_init_rfcsr_6352(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write_chanreg(rt2x00dev, 44, 0xB3); rt2800_rfcsr_write_chanreg(rt2x00dev, 45, 0xD5); rt2800_rfcsr_write_chanreg(rt2x00dev, 46, 0x27); - rt2800_rfcsr_write_chanreg(rt2x00dev, 47, 0x69); + rt2800_rfcsr_write_bank(rt2x00dev, 4, 47, 0x67); + rt2800_rfcsr_write_bank(rt2x00dev, 6, 47, 0x69); rt2800_rfcsr_write_chanreg(rt2x00dev, 48, 0xFF); - rt2800_rfcsr_write_chanreg(rt2x00dev, 54, 0x20); + rt2800_rfcsr_write_bank(rt2x00dev, 4, 54, 0x27); + rt2800_rfcsr_write_bank(rt2x00dev, 6, 54, 0x20); rt2800_rfcsr_write_chanreg(rt2x00dev, 55, 0x66); rt2800_rfcsr_write_chanreg(rt2x00dev, 56, 0xFF); rt2800_rfcsr_write_chanreg(rt2x00dev, 57, 0x1C); -- cgit From 8dc7d11f1dbd3659bfba371aeb72b539db12a2d1 Mon Sep 17 00:00:00 2001 From: Greg Thelen Date: Mon, 17 Apr 2017 23:21:35 -0700 Subject: net/mlx4: suppress 'may be used uninitialized' warning gcc 4.8.4 complains that mlx4_SW2HW_MPT_wrapper() uses an uninitialized 'mpt' variable: drivers/net/ethernet/mellanox/mlx4/resource_tracker.c: In function 'mlx4_SW2HW_MPT_wrapper': drivers/net/ethernet/mellanox/mlx4/resource_tracker.c:2802:12: warning: 'mpt' may be used uninitialized in this function [-Wmaybe-uninitialized] mpt->mtt = mtt; I think this warning is a false complaint. mpt is only used when mr_res_start_move_to() return zero, and in all such cases it initializes mpt. But apparently gcc cannot see that. Initialize mpt to avoid the warning. Signed-off-by: Greg Thelen Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/resource_tracker.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index d8d5d161b8c7..4aa29ee93013 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -2749,7 +2749,7 @@ int mlx4_SW2HW_MPT_wrapper(struct mlx4_dev *dev, int slave, int err; int index = vhcr->in_modifier; struct res_mtt *mtt; - struct res_mpt *mpt; + struct res_mpt *mpt = NULL; int mtt_base = mr_get_mtt_addr(inbox->buf) / dev->caps.mtt_entry_sz; int phys; int id; -- cgit From e8fe177a62feaf576cd9869d9bfedf9c9574bb12 Mon Sep 17 00:00:00 2001 From: Juergen Beisert Date: Tue, 18 Apr 2017 10:48:24 +0200 Subject: net: dsa: add support for the SMSC-LAN9303 tagging format To define the outgoing port and to discover the incoming port a regular VLAN tag is used by the LAN9303. But its VID meaning is 'special'. This tag handler/filter depends on some hardware features which must be enabled in the device to provide and make use of this special VLAN tag to control the destination and the source of an ethernet packet. Signed-off-by: Juergen Borleis Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 1 + net/dsa/Kconfig | 4 ++ net/dsa/Makefile | 1 + net/dsa/dsa.c | 3 ++ net/dsa/dsa_priv.h | 3 ++ net/dsa/tag_lan9303.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 153 insertions(+) create mode 100644 net/dsa/tag_lan9303.c diff --git a/include/net/dsa.h b/include/net/dsa.h index 04c3fe93f803..8e24677b1c62 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -33,6 +33,7 @@ enum dsa_tag_protocol { DSA_TAG_PROTO_BRCM, DSA_TAG_PROTO_QCA, DSA_TAG_PROTO_MTK, + DSA_TAG_PROTO_LAN9303, DSA_TAG_LAST, /* MUST BE LAST */ }; diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index aa21f49f1215..81a0868edb1d 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -33,4 +33,8 @@ config NET_DSA_TAG_QCA config NET_DSA_TAG_MTK bool + +config NET_DSA_TAG_LAN9303 + bool + endif diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 11a082d7e103..0b747d75e65a 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile @@ -9,3 +9,4 @@ dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o dsa_core-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o dsa_core-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o dsa_core-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o +dsa_core-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index e117047174fc..26130ae438da 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -57,6 +57,9 @@ const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = { #endif #ifdef CONFIG_NET_DSA_TAG_MTK [DSA_TAG_PROTO_MTK] = &mtk_netdev_ops, +#endif +#ifdef CONFIG_NET_DSA_TAG_LAN9303 + [DSA_TAG_PROTO_LAN9303] = &lan9303_netdev_ops, #endif [DSA_TAG_PROTO_NONE] = &none_ops, }; diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index ab397c07880f..f4a88e485213 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -93,4 +93,7 @@ extern const struct dsa_device_ops qca_netdev_ops; /* tag_mtk.c */ extern const struct dsa_device_ops mtk_netdev_ops; +/* tag_lan9303.c */ +extern const struct dsa_device_ops lan9303_netdev_ops; + #endif diff --git a/net/dsa/tag_lan9303.c b/net/dsa/tag_lan9303.c new file mode 100644 index 000000000000..563b6c8fe445 --- /dev/null +++ b/net/dsa/tag_lan9303.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2017 Pengutronix, Juergen Borleis + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include "dsa_priv.h" + +/* To define the outgoing port and to discover the incoming port a regular + * VLAN tag is used by the LAN9303. But its VID meaning is 'special': + * + * Dest MAC Src MAC TAG Type + * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 | 1 2 |... + * |<------->| + * TAG: + * |<------------->| + * | 1 2 | 3 4 | + * TPID VID + * 0x8100 + * + * VID bit 3 indicates a request for an ALR lookup. + * + * If VID bit 3 is zero, then bits 0 and 1 specify the destination port + * (0, 1, 2) or broadcast (3) or the source port (1, 2). + * + * VID bit 4 is used to specify if the STP port state should be overridden. + * Required when no forwarding between the external ports should happen. + */ + +#define LAN9303_TAG_LEN 4 +#define LAN9303_MAX_PORTS 3 + +static struct sk_buff *lan9303_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + u16 *lan9303_tag; + + /* insert a special VLAN tag between the MAC addresses + * and the current ethertype field. + */ + if (skb_cow_head(skb, LAN9303_TAG_LEN) < 0) { + dev_dbg(&dev->dev, + "Cannot make room for the special tag. Dropping packet\n"); + goto out_free; + } + + /* provide 'LAN9303_TAG_LEN' bytes additional space */ + skb_push(skb, LAN9303_TAG_LEN); + + /* make room between MACs and Ether-Type */ + memmove(skb->data, skb->data + LAN9303_TAG_LEN, 2 * ETH_ALEN); + + lan9303_tag = (u16 *)(skb->data + 2 * ETH_ALEN); + lan9303_tag[0] = htons(ETH_P_8021Q); + lan9303_tag[1] = htons(p->dp->index | BIT(4)); + + return skb; +out_free: + kfree_skb(skb); + return NULL; +} + +static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + u16 *lan9303_tag; + struct dsa_switch_tree *dst = dev->dsa_ptr; + struct dsa_switch *ds; + unsigned int source_port; + + if (unlikely(!dst)) { + dev_warn_ratelimited(&dev->dev, "Dropping packet, due to missing switch tree device\n"); + return NULL; + } + + ds = dst->ds[0]; + + if (unlikely(!ds)) { + dev_warn_ratelimited(&dev->dev, "Dropping packet, due to missing DSA switch device\n"); + return NULL; + } + + if (unlikely(!pskb_may_pull(skb, LAN9303_TAG_LEN))) { + dev_warn_ratelimited(&dev->dev, + "Dropping packet, cannot pull\n"); + return NULL; + } + + /* '->data' points into the middle of our special VLAN tag information: + * + * ~ MAC src | 0x81 | 0x00 | 0xyy | 0xzz | ether type + * ^ + * ->data + */ + lan9303_tag = (u16 *)(skb->data - 2); + + if (lan9303_tag[0] != htons(ETH_P_8021Q)) { + dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid VLAN marker\n"); + return NULL; + } + + source_port = ntohs(lan9303_tag[1]) & 0x3; + + if (source_port >= LAN9303_MAX_PORTS) { + dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid source port\n"); + return NULL; + } + + if (!ds->ports[source_port].netdev) { + dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid netdev or device\n"); + return NULL; + } + + /* remove the special VLAN tag between the MAC addresses + * and the current ethertype field. + */ + skb_pull_rcsum(skb, 2 + 2); + memmove(skb->data - ETH_HLEN, skb->data - (ETH_HLEN + LAN9303_TAG_LEN), + 2 * ETH_ALEN); + + /* forward the packet to the dedicated interface */ + skb->dev = ds->ports[source_port].netdev; + + return skb; +} + +const struct dsa_device_ops lan9303_netdev_ops = { + .xmit = lan9303_xmit, + .rcv = lan9303_rcv, +}; -- cgit From a1292595e006075792d6961fcb7de5e862a77aa8 Mon Sep 17 00:00:00 2001 From: Juergen Beisert Date: Tue, 18 Apr 2017 10:48:25 +0200 Subject: net: dsa: add new DSA switch driver for the SMSC-LAN9303 The SMSC/Microchip LAN9303 is an ethernet switch device with one CPU port and two external ethernet ports with built-in phys. This driver uses the DSA framework, but is currently only capable of separating the two external ports. There is no offload support yet. Signed-off-by: Juergen Borleis Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303-core.c | 879 +++++++++++++++++++++++++++++++++++++++++ drivers/net/dsa/lan9303.h | 19 + 2 files changed, 898 insertions(+) create mode 100644 drivers/net/dsa/lan9303-core.c create mode 100644 drivers/net/dsa/lan9303.h diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c new file mode 100644 index 000000000000..c8b2423c8ef7 --- /dev/null +++ b/drivers/net/dsa/lan9303-core.c @@ -0,0 +1,879 @@ +/* + * Copyright (C) 2017 Pengutronix, Juergen Borleis + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include + +#include "lan9303.h" + +#define LAN9303_CHIP_REV 0x14 +# define LAN9303_CHIP_ID 0x9303 +#define LAN9303_IRQ_CFG 0x15 +# define LAN9303_IRQ_CFG_IRQ_ENABLE BIT(8) +# define LAN9303_IRQ_CFG_IRQ_POL BIT(4) +# define LAN9303_IRQ_CFG_IRQ_TYPE BIT(0) +#define LAN9303_INT_STS 0x16 +# define LAN9303_INT_STS_PHY_INT2 BIT(27) +# define LAN9303_INT_STS_PHY_INT1 BIT(26) +#define LAN9303_INT_EN 0x17 +# define LAN9303_INT_EN_PHY_INT2_EN BIT(27) +# define LAN9303_INT_EN_PHY_INT1_EN BIT(26) +#define LAN9303_HW_CFG 0x1D +# define LAN9303_HW_CFG_READY BIT(27) +# define LAN9303_HW_CFG_AMDX_EN_PORT2 BIT(26) +# define LAN9303_HW_CFG_AMDX_EN_PORT1 BIT(25) +#define LAN9303_PMI_DATA 0x29 +#define LAN9303_PMI_ACCESS 0x2A +# define LAN9303_PMI_ACCESS_PHY_ADDR(x) (((x) & 0x1f) << 11) +# define LAN9303_PMI_ACCESS_MIIRINDA(x) (((x) & 0x1f) << 6) +# define LAN9303_PMI_ACCESS_MII_BUSY BIT(0) +# define LAN9303_PMI_ACCESS_MII_WRITE BIT(1) +#define LAN9303_MANUAL_FC_1 0x68 +#define LAN9303_MANUAL_FC_2 0x69 +#define LAN9303_MANUAL_FC_0 0x6a +#define LAN9303_SWITCH_CSR_DATA 0x6b +#define LAN9303_SWITCH_CSR_CMD 0x6c +#define LAN9303_SWITCH_CSR_CMD_BUSY BIT(31) +#define LAN9303_SWITCH_CSR_CMD_RW BIT(30) +#define LAN9303_SWITCH_CSR_CMD_LANES (BIT(19) | BIT(18) | BIT(17) | BIT(16)) +#define LAN9303_VIRT_PHY_BASE 0x70 +#define LAN9303_VIRT_SPECIAL_CTRL 0x77 + +#define LAN9303_SW_DEV_ID 0x0000 +#define LAN9303_SW_RESET 0x0001 +#define LAN9303_SW_RESET_RESET BIT(0) +#define LAN9303_SW_IMR 0x0004 +#define LAN9303_SW_IPR 0x0005 +#define LAN9303_MAC_VER_ID_0 0x0400 +#define LAN9303_MAC_RX_CFG_0 0x0401 +# define LAN9303_MAC_RX_CFG_X_REJECT_MAC_TYPES BIT(1) +# define LAN9303_MAC_RX_CFG_X_RX_ENABLE BIT(0) +#define LAN9303_MAC_RX_UNDSZE_CNT_0 0x0410 +#define LAN9303_MAC_RX_64_CNT_0 0x0411 +#define LAN9303_MAC_RX_127_CNT_0 0x0412 +#define LAN9303_MAC_RX_255_CNT_0 0x413 +#define LAN9303_MAC_RX_511_CNT_0 0x0414 +#define LAN9303_MAC_RX_1023_CNT_0 0x0415 +#define LAN9303_MAC_RX_MAX_CNT_0 0x0416 +#define LAN9303_MAC_RX_OVRSZE_CNT_0 0x0417 +#define LAN9303_MAC_RX_PKTOK_CNT_0 0x0418 +#define LAN9303_MAC_RX_CRCERR_CNT_0 0x0419 +#define LAN9303_MAC_RX_MULCST_CNT_0 0x041a +#define LAN9303_MAC_RX_BRDCST_CNT_0 0x041b +#define LAN9303_MAC_RX_PAUSE_CNT_0 0x041c +#define LAN9303_MAC_RX_FRAG_CNT_0 0x041d +#define LAN9303_MAC_RX_JABB_CNT_0 0x041e +#define LAN9303_MAC_RX_ALIGN_CNT_0 0x041f +#define LAN9303_MAC_RX_PKTLEN_CNT_0 0x0420 +#define LAN9303_MAC_RX_GOODPKTLEN_CNT_0 0x0421 +#define LAN9303_MAC_RX_SYMBL_CNT_0 0x0422 +#define LAN9303_MAC_RX_CTLFRM_CNT_0 0x0423 + +#define LAN9303_MAC_TX_CFG_0 0x0440 +# define LAN9303_MAC_TX_CFG_X_TX_IFG_CONFIG_DEFAULT (21 << 2) +# define LAN9303_MAC_TX_CFG_X_TX_PAD_ENABLE BIT(1) +# define LAN9303_MAC_TX_CFG_X_TX_ENABLE BIT(0) +#define LAN9303_MAC_TX_DEFER_CNT_0 0x0451 +#define LAN9303_MAC_TX_PAUSE_CNT_0 0x0452 +#define LAN9303_MAC_TX_PKTOK_CNT_0 0x0453 +#define LAN9303_MAC_TX_64_CNT_0 0x0454 +#define LAN9303_MAC_TX_127_CNT_0 0x0455 +#define LAN9303_MAC_TX_255_CNT_0 0x0456 +#define LAN9303_MAC_TX_511_CNT_0 0x0457 +#define LAN9303_MAC_TX_1023_CNT_0 0x0458 +#define LAN9303_MAC_TX_MAX_CNT_0 0x0459 +#define LAN9303_MAC_TX_UNDSZE_CNT_0 0x045a +#define LAN9303_MAC_TX_PKTLEN_CNT_0 0x045c +#define LAN9303_MAC_TX_BRDCST_CNT_0 0x045d +#define LAN9303_MAC_TX_MULCST_CNT_0 0x045e +#define LAN9303_MAC_TX_LATECOL_0 0x045f +#define LAN9303_MAC_TX_EXCOL_CNT_0 0x0460 +#define LAN9303_MAC_TX_SNGLECOL_CNT_0 0x0461 +#define LAN9303_MAC_TX_MULTICOL_CNT_0 0x0462 +#define LAN9303_MAC_TX_TOTALCOL_CNT_0 0x0463 + +#define LAN9303_MAC_VER_ID_1 0x0800 +#define LAN9303_MAC_RX_CFG_1 0x0801 +#define LAN9303_MAC_TX_CFG_1 0x0840 +#define LAN9303_MAC_VER_ID_2 0x0c00 +#define LAN9303_MAC_RX_CFG_2 0x0c01 +#define LAN9303_MAC_TX_CFG_2 0x0c40 +#define LAN9303_SWE_ALR_CMD 0x1800 +#define LAN9303_SWE_VLAN_CMD 0x180b +# define LAN9303_SWE_VLAN_CMD_RNW BIT(5) +# define LAN9303_SWE_VLAN_CMD_PVIDNVLAN BIT(4) +#define LAN9303_SWE_VLAN_WR_DATA 0x180c +#define LAN9303_SWE_VLAN_RD_DATA 0x180e +# define LAN9303_SWE_VLAN_MEMBER_PORT2 BIT(17) +# define LAN9303_SWE_VLAN_UNTAG_PORT2 BIT(16) +# define LAN9303_SWE_VLAN_MEMBER_PORT1 BIT(15) +# define LAN9303_SWE_VLAN_UNTAG_PORT1 BIT(14) +# define LAN9303_SWE_VLAN_MEMBER_PORT0 BIT(13) +# define LAN9303_SWE_VLAN_UNTAG_PORT0 BIT(12) +#define LAN9303_SWE_VLAN_CMD_STS 0x1810 +#define LAN9303_SWE_GLB_INGRESS_CFG 0x1840 +#define LAN9303_SWE_PORT_STATE 0x1843 +# define LAN9303_SWE_PORT_STATE_FORWARDING_PORT2 (0) +# define LAN9303_SWE_PORT_STATE_LEARNING_PORT2 BIT(5) +# define LAN9303_SWE_PORT_STATE_BLOCKING_PORT2 BIT(4) +# define LAN9303_SWE_PORT_STATE_FORWARDING_PORT1 (0) +# define LAN9303_SWE_PORT_STATE_LEARNING_PORT1 BIT(3) +# define LAN9303_SWE_PORT_STATE_BLOCKING_PORT1 BIT(2) +# define LAN9303_SWE_PORT_STATE_FORWARDING_PORT0 (0) +# define LAN9303_SWE_PORT_STATE_LEARNING_PORT0 BIT(1) +# define LAN9303_SWE_PORT_STATE_BLOCKING_PORT0 BIT(0) +#define LAN9303_SWE_PORT_MIRROR 0x1846 +# define LAN9303_SWE_PORT_MIRROR_SNIFF_ALL BIT(8) +# define LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT2 BIT(7) +# define LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT1 BIT(6) +# define LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT0 BIT(5) +# define LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT2 BIT(4) +# define LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT1 BIT(3) +# define LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT0 BIT(2) +# define LAN9303_SWE_PORT_MIRROR_ENABLE_RX_MIRRORING BIT(1) +# define LAN9303_SWE_PORT_MIRROR_ENABLE_TX_MIRRORING BIT(0) +#define LAN9303_SWE_INGRESS_PORT_TYPE 0x1847 +#define LAN9303_BM_CFG 0x1c00 +#define LAN9303_BM_EGRSS_PORT_TYPE 0x1c0c +# define LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT2 (BIT(17) | BIT(16)) +# define LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT1 (BIT(9) | BIT(8)) +# define LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT0 (BIT(1) | BIT(0)) + +#define LAN9303_PORT_0_OFFSET 0x400 +#define LAN9303_PORT_1_OFFSET 0x800 +#define LAN9303_PORT_2_OFFSET 0xc00 + +/* the built-in PHYs are of type LAN911X */ +#define MII_LAN911X_SPECIAL_MODES 0x12 +#define MII_LAN911X_SPECIAL_CONTROL_STATUS 0x1f + +static const struct regmap_range lan9303_valid_regs[] = { + regmap_reg_range(0x14, 0x17), /* misc, interrupt */ + regmap_reg_range(0x19, 0x19), /* endian test */ + regmap_reg_range(0x1d, 0x1d), /* hardware config */ + regmap_reg_range(0x23, 0x24), /* general purpose timer */ + regmap_reg_range(0x27, 0x27), /* counter */ + regmap_reg_range(0x29, 0x2a), /* PMI index regs */ + regmap_reg_range(0x68, 0x6a), /* flow control */ + regmap_reg_range(0x6b, 0x6c), /* switch fabric indirect regs */ + regmap_reg_range(0x6d, 0x6f), /* misc */ + regmap_reg_range(0x70, 0x77), /* virtual phy */ + regmap_reg_range(0x78, 0x7a), /* GPIO */ + regmap_reg_range(0x7c, 0x7e), /* MAC & reset */ + regmap_reg_range(0x80, 0xb7), /* switch fabric direct regs (wr only) */ +}; + +static const struct regmap_range lan9303_reserved_ranges[] = { + regmap_reg_range(0x00, 0x13), + regmap_reg_range(0x18, 0x18), + regmap_reg_range(0x1a, 0x1c), + regmap_reg_range(0x1e, 0x22), + regmap_reg_range(0x25, 0x26), + regmap_reg_range(0x28, 0x28), + regmap_reg_range(0x2b, 0x67), + regmap_reg_range(0x7b, 0x7b), + regmap_reg_range(0x7f, 0x7f), + regmap_reg_range(0xb8, 0xff), +}; + +const struct regmap_access_table lan9303_register_set = { + .yes_ranges = lan9303_valid_regs, + .n_yes_ranges = ARRAY_SIZE(lan9303_valid_regs), + .no_ranges = lan9303_reserved_ranges, + .n_no_ranges = ARRAY_SIZE(lan9303_reserved_ranges), +}; +EXPORT_SYMBOL(lan9303_register_set); + +static int lan9303_read(struct regmap *regmap, unsigned int offset, u32 *reg) +{ + int ret, i; + + /* we can lose arbitration for the I2C case, because the device + * tries to detect and read an external EEPROM after reset and acts as + * a master on the shared I2C bus itself. This conflicts with our + * attempts to access the device as a slave at the same moment. + */ + for (i = 0; i < 5; i++) { + ret = regmap_read(regmap, offset, reg); + if (!ret) + return 0; + if (ret != -EAGAIN) + break; + msleep(500); + } + + return -EIO; +} + +static int lan9303_virt_phy_reg_read(struct lan9303 *chip, int regnum) +{ + int ret; + u32 val; + + if (regnum > MII_EXPANSION) + return -EINVAL; + + ret = lan9303_read(chip->regmap, LAN9303_VIRT_PHY_BASE + regnum, &val); + if (ret) + return ret; + + return val & 0xffff; +} + +static int lan9303_virt_phy_reg_write(struct lan9303 *chip, int regnum, u16 val) +{ + if (regnum > MII_EXPANSION) + return -EINVAL; + + return regmap_write(chip->regmap, LAN9303_VIRT_PHY_BASE + regnum, val); +} + +static int lan9303_port_phy_reg_wait_for_completion(struct lan9303 *chip) +{ + int ret, i; + u32 reg; + + for (i = 0; i < 25; i++) { + ret = lan9303_read(chip->regmap, LAN9303_PMI_ACCESS, ®); + if (ret) { + dev_err(chip->dev, + "Failed to read pmi access status: %d\n", ret); + return ret; + } + if (!(reg & LAN9303_PMI_ACCESS_MII_BUSY)) + return 0; + msleep(1); + } + + return -EIO; +} + +static int lan9303_port_phy_reg_read(struct lan9303 *chip, int addr, int regnum) +{ + int ret; + u32 val; + + val = LAN9303_PMI_ACCESS_PHY_ADDR(addr); + val |= LAN9303_PMI_ACCESS_MIIRINDA(regnum); + + mutex_lock(&chip->indirect_mutex); + + ret = lan9303_port_phy_reg_wait_for_completion(chip); + if (ret) + goto on_error; + + /* start the MII read cycle */ + ret = regmap_write(chip->regmap, LAN9303_PMI_ACCESS, val); + if (ret) + goto on_error; + + ret = lan9303_port_phy_reg_wait_for_completion(chip); + if (ret) + goto on_error; + + /* read the result of this operation */ + ret = lan9303_read(chip->regmap, LAN9303_PMI_DATA, &val); + if (ret) + goto on_error; + + mutex_unlock(&chip->indirect_mutex); + + return val & 0xffff; + +on_error: + mutex_unlock(&chip->indirect_mutex); + return ret; +} + +static int lan9303_phy_reg_write(struct lan9303 *chip, int addr, int regnum, + unsigned int val) +{ + int ret; + u32 reg; + + reg = LAN9303_PMI_ACCESS_PHY_ADDR(addr); + reg |= LAN9303_PMI_ACCESS_MIIRINDA(regnum); + reg |= LAN9303_PMI_ACCESS_MII_WRITE; + + mutex_lock(&chip->indirect_mutex); + + ret = lan9303_port_phy_reg_wait_for_completion(chip); + if (ret) + goto on_error; + + /* write the data first... */ + ret = regmap_write(chip->regmap, LAN9303_PMI_DATA, val); + if (ret) + goto on_error; + + /* ...then start the MII write cycle */ + ret = regmap_write(chip->regmap, LAN9303_PMI_ACCESS, reg); + +on_error: + mutex_unlock(&chip->indirect_mutex); + return ret; +} + +static int lan9303_switch_wait_for_completion(struct lan9303 *chip) +{ + int ret, i; + u32 reg; + + for (i = 0; i < 25; i++) { + ret = lan9303_read(chip->regmap, LAN9303_SWITCH_CSR_CMD, ®); + if (ret) { + dev_err(chip->dev, + "Failed to read csr command status: %d\n", ret); + return ret; + } + if (!(reg & LAN9303_SWITCH_CSR_CMD_BUSY)) + return 0; + msleep(1); + } + + return -EIO; +} + +static int lan9303_write_switch_reg(struct lan9303 *chip, u16 regnum, u32 val) +{ + u32 reg; + int ret; + + reg = regnum; + reg |= LAN9303_SWITCH_CSR_CMD_LANES; + reg |= LAN9303_SWITCH_CSR_CMD_BUSY; + + mutex_lock(&chip->indirect_mutex); + + ret = lan9303_switch_wait_for_completion(chip); + if (ret) + goto on_error; + + ret = regmap_write(chip->regmap, LAN9303_SWITCH_CSR_DATA, val); + if (ret) { + dev_err(chip->dev, "Failed to write csr data reg: %d\n", ret); + goto on_error; + } + + /* trigger write */ + ret = regmap_write(chip->regmap, LAN9303_SWITCH_CSR_CMD, reg); + if (ret) + dev_err(chip->dev, "Failed to write csr command reg: %d\n", + ret); + +on_error: + mutex_unlock(&chip->indirect_mutex); + return ret; +} + +static int lan9303_read_switch_reg(struct lan9303 *chip, u16 regnum, u32 *val) +{ + u32 reg; + int ret; + + reg = regnum; + reg |= LAN9303_SWITCH_CSR_CMD_LANES; + reg |= LAN9303_SWITCH_CSR_CMD_RW; + reg |= LAN9303_SWITCH_CSR_CMD_BUSY; + + mutex_lock(&chip->indirect_mutex); + + ret = lan9303_switch_wait_for_completion(chip); + if (ret) + goto on_error; + + /* trigger read */ + ret = regmap_write(chip->regmap, LAN9303_SWITCH_CSR_CMD, reg); + if (ret) { + dev_err(chip->dev, "Failed to write csr command reg: %d\n", + ret); + goto on_error; + } + + ret = lan9303_switch_wait_for_completion(chip); + if (ret) + goto on_error; + + ret = lan9303_read(chip->regmap, LAN9303_SWITCH_CSR_DATA, val); + if (ret) + dev_err(chip->dev, "Failed to read csr data reg: %d\n", ret); +on_error: + mutex_unlock(&chip->indirect_mutex); + return ret; +} + +static int lan9303_detect_phy_setup(struct lan9303 *chip) +{ + int reg; + + /* depending on the 'phy_addr_sel_strap' setting, the three phys are + * using IDs 0-1-2 or IDs 1-2-3. We cannot read back the + * 'phy_addr_sel_strap' setting directly, so we need a test, which + * configuration is active: + * Special reg 18 of phy 3 reads as 0x0000, if 'phy_addr_sel_strap' is 0 + * and the IDs are 0-1-2, else it contains something different from + * 0x0000, which means 'phy_addr_sel_strap' is 1 and the IDs are 1-2-3. + */ + reg = lan9303_port_phy_reg_read(chip, 3, MII_LAN911X_SPECIAL_MODES); + if (reg < 0) { + dev_err(chip->dev, "Failed to detect phy config: %d\n", reg); + return reg; + } + + if (reg != 0) + chip->phy_addr_sel_strap = 1; + else + chip->phy_addr_sel_strap = 0; + + dev_dbg(chip->dev, "Phy setup '%s' detected\n", + chip->phy_addr_sel_strap ? "1-2-3" : "0-1-2"); + + return 0; +} + +#define LAN9303_MAC_RX_CFG_OFFS (LAN9303_MAC_RX_CFG_0 - LAN9303_PORT_0_OFFSET) +#define LAN9303_MAC_TX_CFG_OFFS (LAN9303_MAC_TX_CFG_0 - LAN9303_PORT_0_OFFSET) + +static int lan9303_disable_packet_processing(struct lan9303 *chip, + unsigned int port) +{ + int ret; + + /* disable RX, but keep register reset default values else */ + ret = lan9303_write_switch_reg(chip, LAN9303_MAC_RX_CFG_OFFS + port, + LAN9303_MAC_RX_CFG_X_REJECT_MAC_TYPES); + if (ret) + return ret; + + /* disable TX, but keep register reset default values else */ + return lan9303_write_switch_reg(chip, LAN9303_MAC_TX_CFG_OFFS + port, + LAN9303_MAC_TX_CFG_X_TX_IFG_CONFIG_DEFAULT | + LAN9303_MAC_TX_CFG_X_TX_PAD_ENABLE); +} + +static int lan9303_enable_packet_processing(struct lan9303 *chip, + unsigned int port) +{ + int ret; + + /* enable RX and keep register reset default values else */ + ret = lan9303_write_switch_reg(chip, LAN9303_MAC_RX_CFG_OFFS + port, + LAN9303_MAC_RX_CFG_X_REJECT_MAC_TYPES | + LAN9303_MAC_RX_CFG_X_RX_ENABLE); + if (ret) + return ret; + + /* enable TX and keep register reset default values else */ + return lan9303_write_switch_reg(chip, LAN9303_MAC_TX_CFG_OFFS + port, + LAN9303_MAC_TX_CFG_X_TX_IFG_CONFIG_DEFAULT | + LAN9303_MAC_TX_CFG_X_TX_PAD_ENABLE | + LAN9303_MAC_TX_CFG_X_TX_ENABLE); +} + +/* We want a special working switch: + * - do not forward packets between port 1 and 2 + * - forward everything from port 1 to port 0 + * - forward everything from port 2 to port 0 + * - forward special tagged packets from port 0 to port 1 *or* port 2 + */ +static int lan9303_separate_ports(struct lan9303 *chip) +{ + int ret; + + ret = lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_MIRROR, + LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT0 | + LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT1 | + LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT2 | + LAN9303_SWE_PORT_MIRROR_ENABLE_RX_MIRRORING | + LAN9303_SWE_PORT_MIRROR_SNIFF_ALL); + if (ret) + return ret; + + /* enable defining the destination port via special VLAN tagging + * for port 0 + */ + ret = lan9303_write_switch_reg(chip, LAN9303_SWE_INGRESS_PORT_TYPE, + 0x03); + if (ret) + return ret; + + /* tag incoming packets at port 1 and 2 on their way to port 0 to be + * able to discover their source port + */ + ret = lan9303_write_switch_reg(chip, LAN9303_BM_EGRSS_PORT_TYPE, + LAN9303_BM_EGRSS_PORT_TYPE_SPECIAL_TAG_PORT0); + if (ret) + return ret; + + /* prevent port 1 and 2 from forwarding packets by their own */ + return lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_STATE, + LAN9303_SWE_PORT_STATE_FORWARDING_PORT0 | + LAN9303_SWE_PORT_STATE_BLOCKING_PORT1 | + LAN9303_SWE_PORT_STATE_BLOCKING_PORT2); +} + +static int lan9303_handle_reset(struct lan9303 *chip) +{ + if (!chip->reset_gpio) + return 0; + + if (chip->reset_duration != 0) + msleep(chip->reset_duration); + + /* release (deassert) reset and activate the device */ + gpiod_set_value_cansleep(chip->reset_gpio, 0); + + return 0; +} + +/* stop processing packets for all ports */ +static int lan9303_disable_processing(struct lan9303 *chip) +{ + int ret; + + ret = lan9303_disable_packet_processing(chip, LAN9303_PORT_0_OFFSET); + if (ret) + return ret; + ret = lan9303_disable_packet_processing(chip, LAN9303_PORT_1_OFFSET); + if (ret) + return ret; + return lan9303_disable_packet_processing(chip, LAN9303_PORT_2_OFFSET); +} + +static int lan9303_check_device(struct lan9303 *chip) +{ + int ret; + u32 reg; + + ret = lan9303_read(chip->regmap, LAN9303_CHIP_REV, ®); + if (ret) { + dev_err(chip->dev, "failed to read chip revision register: %d\n", + ret); + if (!chip->reset_gpio) { + dev_dbg(chip->dev, + "hint: maybe failed due to missing reset GPIO\n"); + } + return ret; + } + + if ((reg >> 16) != LAN9303_CHIP_ID) { + dev_err(chip->dev, "expecting LAN9303 chip, but found: %X\n", + reg >> 16); + return ret; + } + + /* The default state of the LAN9303 device is to forward packets between + * all ports (if not configured differently by an external EEPROM). + * The initial state of a DSA device must be forwarding packets only + * between the external and the internal ports and no forwarding + * between the external ports. In preparation we stop packet handling + * at all for now until the LAN9303 device is re-programmed accordingly. + */ + ret = lan9303_disable_processing(chip); + if (ret) + dev_warn(chip->dev, "failed to disable switching %d\n", ret); + + dev_info(chip->dev, "Found LAN9303 rev. %u\n", reg & 0xffff); + + ret = lan9303_detect_phy_setup(chip); + if (ret) { + dev_err(chip->dev, + "failed to discover phy bootstrap setup: %d\n", ret); + return ret; + } + + return 0; +} + +/* ---------------------------- DSA -----------------------------------*/ + +static enum dsa_tag_protocol lan9303_get_tag_protocol(struct dsa_switch *ds) +{ + return DSA_TAG_PROTO_LAN9303; +} + +static int lan9303_setup(struct dsa_switch *ds) +{ + struct lan9303 *chip = ds->priv; + int ret; + + /* Make sure that port 0 is the cpu port */ + if (!dsa_is_cpu_port(ds, 0)) { + dev_err(chip->dev, "port 0 is not the CPU port\n"); + return -EINVAL; + } + + ret = lan9303_separate_ports(chip); + if (ret) + dev_err(chip->dev, "failed to separate ports %d\n", ret); + + ret = lan9303_enable_packet_processing(chip, LAN9303_PORT_0_OFFSET); + if (ret) + dev_err(chip->dev, "failed to re-enable switching %d\n", ret); + + return 0; +} + +struct lan9303_mib_desc { + unsigned int offset; /* offset of first MAC */ + const char *name; +}; + +static const struct lan9303_mib_desc lan9303_mib[] = { + { .offset = LAN9303_MAC_RX_BRDCST_CNT_0, .name = "RxBroad", }, + { .offset = LAN9303_MAC_RX_PAUSE_CNT_0, .name = "RxPause", }, + { .offset = LAN9303_MAC_RX_MULCST_CNT_0, .name = "RxMulti", }, + { .offset = LAN9303_MAC_RX_PKTOK_CNT_0, .name = "RxOk", }, + { .offset = LAN9303_MAC_RX_CRCERR_CNT_0, .name = "RxCrcErr", }, + { .offset = LAN9303_MAC_RX_ALIGN_CNT_0, .name = "RxAlignErr", }, + { .offset = LAN9303_MAC_RX_JABB_CNT_0, .name = "RxJabber", }, + { .offset = LAN9303_MAC_RX_FRAG_CNT_0, .name = "RxFragment", }, + { .offset = LAN9303_MAC_RX_64_CNT_0, .name = "Rx64Byte", }, + { .offset = LAN9303_MAC_RX_127_CNT_0, .name = "Rx128Byte", }, + { .offset = LAN9303_MAC_RX_255_CNT_0, .name = "Rx256Byte", }, + { .offset = LAN9303_MAC_RX_511_CNT_0, .name = "Rx512Byte", }, + { .offset = LAN9303_MAC_RX_1023_CNT_0, .name = "Rx1024Byte", }, + { .offset = LAN9303_MAC_RX_MAX_CNT_0, .name = "RxMaxByte", }, + { .offset = LAN9303_MAC_RX_PKTLEN_CNT_0, .name = "RxByteCnt", }, + { .offset = LAN9303_MAC_RX_SYMBL_CNT_0, .name = "RxSymbolCnt", }, + { .offset = LAN9303_MAC_RX_CTLFRM_CNT_0, .name = "RxCfs", }, + { .offset = LAN9303_MAC_RX_OVRSZE_CNT_0, .name = "RxOverFlow", }, + { .offset = LAN9303_MAC_TX_UNDSZE_CNT_0, .name = "TxShort", }, + { .offset = LAN9303_MAC_TX_BRDCST_CNT_0, .name = "TxBroad", }, + { .offset = LAN9303_MAC_TX_PAUSE_CNT_0, .name = "TxPause", }, + { .offset = LAN9303_MAC_TX_MULCST_CNT_0, .name = "TxMulti", }, + { .offset = LAN9303_MAC_RX_UNDSZE_CNT_0, .name = "TxUnderRun", }, + { .offset = LAN9303_MAC_TX_64_CNT_0, .name = "Tx64Byte", }, + { .offset = LAN9303_MAC_TX_127_CNT_0, .name = "Tx128Byte", }, + { .offset = LAN9303_MAC_TX_255_CNT_0, .name = "Tx256Byte", }, + { .offset = LAN9303_MAC_TX_511_CNT_0, .name = "Tx512Byte", }, + { .offset = LAN9303_MAC_TX_1023_CNT_0, .name = "Tx1024Byte", }, + { .offset = LAN9303_MAC_TX_MAX_CNT_0, .name = "TxMaxByte", }, + { .offset = LAN9303_MAC_TX_PKTLEN_CNT_0, .name = "TxByteCnt", }, + { .offset = LAN9303_MAC_TX_PKTOK_CNT_0, .name = "TxOk", }, + { .offset = LAN9303_MAC_TX_TOTALCOL_CNT_0, .name = "TxCollision", }, + { .offset = LAN9303_MAC_TX_MULTICOL_CNT_0, .name = "TxMultiCol", }, + { .offset = LAN9303_MAC_TX_SNGLECOL_CNT_0, .name = "TxSingleCol", }, + { .offset = LAN9303_MAC_TX_EXCOL_CNT_0, .name = "TxExcCol", }, + { .offset = LAN9303_MAC_TX_DEFER_CNT_0, .name = "TxDefer", }, + { .offset = LAN9303_MAC_TX_LATECOL_0, .name = "TxLateCol", }, +}; + +static void lan9303_get_strings(struct dsa_switch *ds, int port, uint8_t *data) +{ + unsigned int u; + + for (u = 0; u < ARRAY_SIZE(lan9303_mib); u++) { + strncpy(data + u * ETH_GSTRING_LEN, lan9303_mib[u].name, + ETH_GSTRING_LEN); + } +} + +static void lan9303_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *data) +{ + struct lan9303 *chip = ds->priv; + u32 reg; + unsigned int u, poff; + int ret; + + poff = port * 0x400; + + for (u = 0; u < ARRAY_SIZE(lan9303_mib); u++) { + ret = lan9303_read_switch_reg(chip, + lan9303_mib[u].offset + poff, + ®); + if (ret) + dev_warn(chip->dev, "Reading status reg %u failed\n", + lan9303_mib[u].offset + poff); + data[u] = reg; + } +} + +static int lan9303_get_sset_count(struct dsa_switch *ds) +{ + return ARRAY_SIZE(lan9303_mib); +} + +static int lan9303_phy_read(struct dsa_switch *ds, int phy, int regnum) +{ + struct lan9303 *chip = ds->priv; + int phy_base = chip->phy_addr_sel_strap; + + if (phy == phy_base) + return lan9303_virt_phy_reg_read(chip, regnum); + if (phy > phy_base + 2) + return -ENODEV; + + return lan9303_port_phy_reg_read(chip, phy, regnum); +} + +static int lan9303_phy_write(struct dsa_switch *ds, int phy, int regnum, + u16 val) +{ + struct lan9303 *chip = ds->priv; + int phy_base = chip->phy_addr_sel_strap; + + if (phy == phy_base) + return lan9303_virt_phy_reg_write(chip, regnum, val); + if (phy > phy_base + 2) + return -ENODEV; + + return lan9303_phy_reg_write(chip, phy, regnum, val); +} + +static int lan9303_port_enable(struct dsa_switch *ds, int port, + struct phy_device *phy) +{ + struct lan9303 *chip = ds->priv; + + /* enable internal packet processing */ + switch (port) { + case 1: + return lan9303_enable_packet_processing(chip, + LAN9303_PORT_1_OFFSET); + case 2: + return lan9303_enable_packet_processing(chip, + LAN9303_PORT_2_OFFSET); + default: + dev_dbg(chip->dev, + "Error: request to power up invalid port %d\n", port); + } + + return -ENODEV; +} + +static void lan9303_port_disable(struct dsa_switch *ds, int port, + struct phy_device *phy) +{ + struct lan9303 *chip = ds->priv; + + /* disable internal packet processing */ + switch (port) { + case 1: + lan9303_disable_packet_processing(chip, LAN9303_PORT_1_OFFSET); + lan9303_phy_reg_write(chip, chip->phy_addr_sel_strap + 1, + MII_BMCR, BMCR_PDOWN); + break; + case 2: + lan9303_disable_packet_processing(chip, LAN9303_PORT_2_OFFSET); + lan9303_phy_reg_write(chip, chip->phy_addr_sel_strap + 2, + MII_BMCR, BMCR_PDOWN); + break; + default: + dev_dbg(chip->dev, + "Error: request to power down invalid port %d\n", port); + } +} + +static struct dsa_switch_ops lan9303_switch_ops = { + .get_tag_protocol = lan9303_get_tag_protocol, + .setup = lan9303_setup, + .get_strings = lan9303_get_strings, + .phy_read = lan9303_phy_read, + .phy_write = lan9303_phy_write, + .get_ethtool_stats = lan9303_get_ethtool_stats, + .get_sset_count = lan9303_get_sset_count, + .port_enable = lan9303_port_enable, + .port_disable = lan9303_port_disable, +}; + +static int lan9303_register_switch(struct lan9303 *chip) +{ + chip->ds = dsa_switch_alloc(chip->dev, DSA_MAX_PORTS); + if (!chip->ds) + return -ENOMEM; + + chip->ds->priv = chip; + chip->ds->ops = &lan9303_switch_ops; + chip->ds->phys_mii_mask = chip->phy_addr_sel_strap ? 0xe : 0x7; + + return dsa_register_switch(chip->ds, chip->dev); +} + +static void lan9303_probe_reset_gpio(struct lan9303 *chip, + struct device_node *np) +{ + chip->reset_gpio = devm_gpiod_get_optional(chip->dev, "reset", + GPIOD_OUT_LOW); + + if (!chip->reset_gpio) { + dev_dbg(chip->dev, "No reset GPIO defined\n"); + return; + } + + chip->reset_duration = 200; + + if (np) { + of_property_read_u32(np, "reset-duration", + &chip->reset_duration); + } else { + dev_dbg(chip->dev, "reset duration defaults to 200 ms\n"); + } + + /* A sane reset duration should not be longer than 1s */ + if (chip->reset_duration > 1000) + chip->reset_duration = 1000; +} + +int lan9303_probe(struct lan9303 *chip, struct device_node *np) +{ + int ret; + + mutex_init(&chip->indirect_mutex); + + lan9303_probe_reset_gpio(chip, np); + + ret = lan9303_handle_reset(chip); + if (ret) + return ret; + + ret = lan9303_check_device(chip); + if (ret) + return ret; + + ret = lan9303_register_switch(chip); + if (ret) { + dev_dbg(chip->dev, "Failed to register switch: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(lan9303_probe); + +int lan9303_remove(struct lan9303 *chip) +{ + int rc; + + rc = lan9303_disable_processing(chip); + if (rc != 0) + dev_warn(chip->dev, "shutting down failed\n"); + + dsa_unregister_switch(chip->ds); + + /* assert reset to the whole device to prevent it from doing anything */ + gpiod_set_value_cansleep(chip->reset_gpio, 1); + gpiod_unexport(chip->reset_gpio); + + return 0; +} +EXPORT_SYMBOL(lan9303_remove); + +MODULE_AUTHOR("Juergen Borleis "); +MODULE_DESCRIPTION("Core driver for SMSC/Microchip LAN9303 three port ethernet switch"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/lan9303.h b/drivers/net/dsa/lan9303.h new file mode 100644 index 000000000000..d1512dad2d90 --- /dev/null +++ b/drivers/net/dsa/lan9303.h @@ -0,0 +1,19 @@ +#include +#include +#include + +struct lan9303 { + struct device *dev; + struct regmap *regmap; + struct regmap_irq_chip_data *irq_data; + struct gpio_desc *reset_gpio; + u32 reset_duration; /* in [ms] */ + bool phy_addr_sel_strap; + struct dsa_switch *ds; + struct mutex indirect_mutex; /* protect indexed register access */ +}; + +extern const struct regmap_access_table lan9303_register_set; + +int lan9303_probe(struct lan9303 *chip, struct device_node *np); +int lan9303_remove(struct lan9303 *chip); -- cgit From be4e119f991451a3f3385b4d167c016c6eb49e78 Mon Sep 17 00:00:00 2001 From: Juergen Beisert Date: Tue, 18 Apr 2017 10:48:26 +0200 Subject: net: dsa: LAN9303: add I2C managed mode support In this mode the switch device and the internal phys will be managed via I2C interface. The MDIO interface is still supported, but for the (emulated) CPU port only. Signed-off-by: Juergen Borleis CC: devicetree@vger.kernel.org CC: robh+dt@kernel.org CC: mark.rutland@arm.com Acked-by: Rob Herring Signed-off-by: David S. Miller --- .../devicetree/bindings/net/dsa/lan9303.txt | 62 +++++++++++ drivers/net/dsa/Kconfig | 16 +++ drivers/net/dsa/Makefile | 2 + drivers/net/dsa/lan9303_i2c.c | 113 +++++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/dsa/lan9303.txt create mode 100644 drivers/net/dsa/lan9303_i2c.c diff --git a/Documentation/devicetree/bindings/net/dsa/lan9303.txt b/Documentation/devicetree/bindings/net/dsa/lan9303.txt new file mode 100644 index 000000000000..2edc2561467a --- /dev/null +++ b/Documentation/devicetree/bindings/net/dsa/lan9303.txt @@ -0,0 +1,62 @@ +SMSC/MicroChip LAN9303 three port ethernet switch +------------------------------------------------- + +Required properties: + +- compatible: should be "smsc,lan9303-i2c" + +Optional properties: + +- reset-gpios: GPIO to be used to reset the whole device +- reset-duration: reset duration in milliseconds, defaults to 200 ms + +Subnodes: + +The integrated switch subnode should be specified according to the binding +described in dsa/dsa.txt. The CPU port of this switch is always port 0. + +Note: always use 'reg = <0/1/2>;' for the three DSA ports, even if the device is +configured to use 1/2/3 instead. This hardware configuration will be +auto-detected and mapped accordingly. + +Example: + +I2C managed mode: + + master: masterdevice@X { + status = "okay"; + + fixed-link { /* RMII fixed link to LAN9303 */ + speed = <100>; + full-duplex; + }; + }; + + switch: switch@a { + compatible = "smsc,lan9303-i2c"; + reg = <0xa>; + status = "okay"; + reset-gpios = <&gpio7 6 GPIO_ACTIVE_LOW>; + reset-duration = <200>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { /* RMII fixed link to master */ + reg = <0>; + label = "cpu"; + ethernet = <&master>; + }; + + port@1 { /* external port 1 */ + reg = <1>; + label = "lan1; + }; + + port@2 { /* external port 2 */ + reg = <2>; + label = "lan2"; + }; + }; + }; diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 31a2b229106d..c56533bffc8c 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -50,4 +50,20 @@ config NET_DSA_MT7530 This enables support for the Mediatek MT7530 Ethernet switch chip. +config NET_DSA_SMSC_LAN9303 + tristate + select NET_DSA_TAG_LAN9303 + ---help--- + This enables support for the SMSC/Microchip LAN9303 3 port ethernet + switch chips. + +config NET_DSA_SMSC_LAN9303_I2C + tristate "SMSC/Microchip LAN9303 3-ports 10/100 ethernet switch in I2C managed mode" + depends on NET_DSA + select NET_DSA_SMSC_LAN9303 + select REGMAP_I2C + ---help--- + Enable access functions if the SMSC/Microchip LAN9303 is configured + for I2C managed mode. + endmenu diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index 2ae07f4fbf63..c1981ba18963 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -3,6 +3,8 @@ obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm-sf2.o bcm-sf2-objs := bcm_sf2.o bcm_sf2_cfp.o obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o +obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o +obj-$(CONFIG_NET_DSA_SMSC_LAN9303_I2C) += lan9303_i2c.o obj-y += b53/ obj-y += mv88e6xxx/ obj-$(CONFIG_NET_DSA_LOOP) += dsa_loop.o dsa_loop_bdinfo.o diff --git a/drivers/net/dsa/lan9303_i2c.c b/drivers/net/dsa/lan9303_i2c.c new file mode 100644 index 000000000000..ab3ce0da5071 --- /dev/null +++ b/drivers/net/dsa/lan9303_i2c.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 Pengutronix, Juergen Borleis + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include + +#include "lan9303.h" + +struct lan9303_i2c { + struct i2c_client *device; + struct lan9303 chip; +}; + +static const struct regmap_config lan9303_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 1, + .can_multi_write = true, + .max_register = 0x0ff, /* address bits 0..1 are not used */ + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + + .volatile_table = &lan9303_register_set, + .wr_table = &lan9303_register_set, + .rd_table = &lan9303_register_set, + + .cache_type = REGCACHE_NONE, +}; + +static int lan9303_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lan9303_i2c *sw_dev; + int ret; + + sw_dev = devm_kzalloc(&client->dev, sizeof(struct lan9303_i2c), + GFP_KERNEL); + if (!sw_dev) + return -ENOMEM; + + sw_dev->chip.regmap = devm_regmap_init_i2c(client, + &lan9303_i2c_regmap_config); + if (IS_ERR(sw_dev->chip.regmap)) { + ret = PTR_ERR(sw_dev->chip.regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + /* link forward and backward */ + sw_dev->device = client; + i2c_set_clientdata(client, sw_dev); + sw_dev->chip.dev = &client->dev; + + ret = lan9303_probe(&sw_dev->chip, client->dev.of_node); + if (ret != 0) + return ret; + + dev_info(&client->dev, "LAN9303 I2C driver loaded successfully\n"); + + return 0; +} + +static int lan9303_i2c_remove(struct i2c_client *client) +{ + struct lan9303_i2c *sw_dev; + + sw_dev = i2c_get_clientdata(client); + if (!sw_dev) + return -ENODEV; + + return lan9303_remove(&sw_dev->chip); +} + +/*-------------------------------------------------------------------------*/ + +static const struct i2c_device_id lan9303_i2c_id[] = { + { "lan9303", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, lan9303_i2c_id); + +static const struct of_device_id lan9303_i2c_of_match[] = { + { .compatible = "smsc,lan9303-i2c", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, lan9303_i2c_of_match); + +static struct i2c_driver lan9303_i2c_driver = { + .driver = { + .name = "LAN9303_I2C", + .of_match_table = of_match_ptr(lan9303_i2c_of_match), + }, + .probe = lan9303_i2c_probe, + .remove = lan9303_i2c_remove, + .id_table = lan9303_i2c_id, +}; +module_i2c_driver(lan9303_i2c_driver); + +MODULE_AUTHOR("Juergen Borleis "); +MODULE_DESCRIPTION("Driver for SMSC/Microchip LAN9303 three port ethernet switch in I2C managed mode"); +MODULE_LICENSE("GPL v2"); -- cgit From dc7005831523d674be11a98f3256e0cc871157d6 Mon Sep 17 00:00:00 2001 From: Juergen Beisert Date: Tue, 18 Apr 2017 10:48:27 +0200 Subject: net: dsa: LAN9303: add MDIO managed mode support When the LAN9303 device is in MDIO manged mode, all register accesses must be done via MDIO. Please note: this code is compile time tested only due to the absence of such configured hardware. It is based on a patch from Stefan Roese from 2014. Signed-off-by: Juergen Borleis CC: devicetree@vger.kernel.org CC: robh+dt@kernel.org CC: mark.rutland@arm.com CC: sr@denx.de Acked-by: Rob Herring Signed-off-by: David S. Miller --- .../devicetree/bindings/net/dsa/lan9303.txt | 45 ++++++- drivers/net/dsa/Kconfig | 8 ++ drivers/net/dsa/Makefile | 1 + drivers/net/dsa/lan9303_mdio.c | 148 +++++++++++++++++++++ 4 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 drivers/net/dsa/lan9303_mdio.c diff --git a/Documentation/devicetree/bindings/net/dsa/lan9303.txt b/Documentation/devicetree/bindings/net/dsa/lan9303.txt index 2edc2561467a..04f2965a4467 100644 --- a/Documentation/devicetree/bindings/net/dsa/lan9303.txt +++ b/Documentation/devicetree/bindings/net/dsa/lan9303.txt @@ -3,7 +3,10 @@ SMSC/MicroChip LAN9303 three port ethernet switch Required properties: -- compatible: should be "smsc,lan9303-i2c" +- compatible: should be + - "smsc,lan9303-i2c" for I2C managed mode + or + - "smsc,lan9303-mdio" for mdio managed mode Optional properties: @@ -60,3 +63,43 @@ I2C managed mode: }; }; }; + +MDIO managed mode: + + master: masterdevice@X { + status = "okay"; + phy-handle = <&switch>; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + switch: switch-phy@0 { + compatible = "smsc,lan9303-mdio"; + reg = <0>; + reset-gpios = <&gpio7 6 GPIO_ACTIVE_LOW>; + reset-duration = <100>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "cpu"; + ethernet = <&master>; + }; + + port@1 { /* external port 1 */ + reg = <1>; + label = "lan1; + }; + + port@2 { /* external port 2 */ + reg = <2>; + label = "lan2"; + }; + }; + }; + }; + }; diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index c56533bffc8c..131a5b1cbfc8 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -66,4 +66,12 @@ config NET_DSA_SMSC_LAN9303_I2C Enable access functions if the SMSC/Microchip LAN9303 is configured for I2C managed mode. +config NET_DSA_SMSC_LAN9303_MDIO + tristate "SMSC/Microchip LAN9303 3-ports 10/100 ethernet switch in MDIO managed mode" + depends on NET_DSA + select NET_DSA_SMSC_LAN9303 + ---help--- + Enable access functions if the SMSC/Microchip LAN9303 is configured + for MDIO managed mode. + endmenu diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index c1981ba18963..edd630361736 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_NET_DSA_QCA8K) += qca8k.o obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o obj-$(CONFIG_NET_DSA_SMSC_LAN9303_I2C) += lan9303_i2c.o +obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o obj-y += b53/ obj-y += mv88e6xxx/ obj-$(CONFIG_NET_DSA_LOOP) += dsa_loop.o dsa_loop_bdinfo.o diff --git a/drivers/net/dsa/lan9303_mdio.c b/drivers/net/dsa/lan9303_mdio.c new file mode 100644 index 000000000000..93c36c0541cf --- /dev/null +++ b/drivers/net/dsa/lan9303_mdio.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2017 Pengutronix, Juergen Borleis + * + * Partially based on a patch from + * Copyright (c) 2014 Stefan Roese + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include + +#include "lan9303.h" + +/* Generate phy-addr and -reg from the input address */ +#define PHY_ADDR(x) ((((x) >> 6) + 0x10) & 0x1f) +#define PHY_REG(x) (((x) >> 1) & 0x1f) + +struct lan9303_mdio { + struct mdio_device *device; + struct lan9303 chip; +}; + +static void lan9303_mdio_real_write(struct mdio_device *mdio, int reg, u16 val) +{ + mdio->bus->write(mdio->bus, PHY_ADDR(reg), PHY_REG(reg), val); +} + +static int lan9303_mdio_write(void *ctx, uint32_t reg, uint32_t val) +{ + struct lan9303_mdio *sw_dev = (struct lan9303_mdio *)ctx; + + mutex_lock(&sw_dev->device->bus->mdio_lock); + lan9303_mdio_real_write(sw_dev->device, reg, val & 0xffff); + lan9303_mdio_real_write(sw_dev->device, reg + 2, (val >> 16) & 0xffff); + mutex_unlock(&sw_dev->device->bus->mdio_lock); + + return 0; +} + +static u16 lan9303_mdio_real_read(struct mdio_device *mdio, int reg) +{ + return mdio->bus->read(mdio->bus, PHY_ADDR(reg), PHY_REG(reg)); +} + +static int lan9303_mdio_read(void *ctx, uint32_t reg, uint32_t *val) +{ + struct lan9303_mdio *sw_dev = (struct lan9303_mdio *)ctx; + + mutex_lock(&sw_dev->device->bus->mdio_lock); + *val = lan9303_mdio_real_read(sw_dev->device, reg); + *val |= (lan9303_mdio_real_read(sw_dev->device, reg + 2) << 16); + mutex_unlock(&sw_dev->device->bus->mdio_lock); + + return 0; +} + +static const struct regmap_config lan9303_mdio_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 1, + .can_multi_write = true, + .max_register = 0x0ff, /* address bits 0..1 are not used */ + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + + .volatile_table = &lan9303_register_set, + .wr_table = &lan9303_register_set, + .rd_table = &lan9303_register_set, + + .reg_read = lan9303_mdio_read, + .reg_write = lan9303_mdio_write, + + .cache_type = REGCACHE_NONE, +}; + +static int lan9303_mdio_probe(struct mdio_device *mdiodev) +{ + struct lan9303_mdio *sw_dev; + int ret; + + sw_dev = devm_kzalloc(&mdiodev->dev, sizeof(struct lan9303_mdio), + GFP_KERNEL); + if (!sw_dev) + return -ENOMEM; + + sw_dev->chip.regmap = devm_regmap_init(&mdiodev->dev, NULL, sw_dev, + &lan9303_mdio_regmap_config); + if (IS_ERR(sw_dev->chip.regmap)) { + ret = PTR_ERR(sw_dev->chip.regmap); + dev_err(&mdiodev->dev, "regmap init failed: %d\n", ret); + return ret; + } + + /* link forward and backward */ + sw_dev->device = mdiodev; + dev_set_drvdata(&mdiodev->dev, sw_dev); + sw_dev->chip.dev = &mdiodev->dev; + + ret = lan9303_probe(&sw_dev->chip, mdiodev->dev.of_node); + if (ret != 0) + return ret; + + dev_info(&mdiodev->dev, "LAN9303 MDIO driver loaded successfully\n"); + + return 0; +} + +static void lan9303_mdio_remove(struct mdio_device *mdiodev) +{ + struct lan9303_mdio *sw_dev = dev_get_drvdata(&mdiodev->dev); + + if (!sw_dev) + return; + + lan9303_remove(&sw_dev->chip); +} + +/*-------------------------------------------------------------------------*/ + +static const struct of_device_id lan9303_mdio_of_match[] = { + { .compatible = "smsc,lan9303-mdio" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, lan9303_mdio_of_match); + +static struct mdio_driver lan9303_mdio_driver = { + .mdiodrv.driver = { + .name = "LAN9303_MDIO", + .of_match_table = of_match_ptr(lan9303_mdio_of_match), + }, + .probe = lan9303_mdio_probe, + .remove = lan9303_mdio_remove, +}; +mdio_module_driver(lan9303_mdio_driver); + +MODULE_AUTHOR("Stefan Roese , Juergen Borleis "); +MODULE_DESCRIPTION("Driver for SMSC/Microchip LAN9303 three port ethernet switch in MDIO managed mode"); +MODULE_LICENSE("GPL v2"); -- cgit From 1f504ec9896ba682270c2ffc119f93eb8ac064ee Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 18 Apr 2017 11:27:00 +0200 Subject: bpf: remove reference to sock_filter_ext from kerneldoc comment struct sock_filter_ext didn't make it into the tree and is now called struct bpf_insn. Reword the kerneldoc comment for bpf_convert_filter() accordingly. Signed-off-by: Tobias Klauser Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- net/core/filter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/core/filter.c b/net/core/filter.c index 19be954f8ce7..085925834727 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -354,7 +354,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, * @new_prog: buffer where converted program will be stored * @new_len: pointer to store length of converted program * - * Remap 'sock_filter' style BPF instruction set to 'sock_filter_ext' style. + * Remap 'sock_filter' style classic BPF (cBPF) instruction set to 'bpf_insn' + * style extended BPF (eBPF). * Conversion workflow: * * 1) First pass for calculating the new program length: -- cgit From fada43ccc84e225fe8d3804878a4028def7f85a5 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 18 Apr 2017 14:39:53 +0200 Subject: bindings: net: stmmac: add missing note about LPI interrupt The hardware has a LPI interrupt. There is already code in the stmmac driver to parse and handle the interrupt. However, this information was missing from the DT binding. At the same time, improve the description of the existing interrupts. Signed-off-by: Niklas Cassel Acked-By: Joao Pinto Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index f652b0c384ce..c3a7be6615c5 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -7,9 +7,12 @@ Required properties: - interrupt-parent: Should be the phandle for the interrupt controller that services interrupts for this device - interrupts: Should contain the STMMAC interrupts -- interrupt-names: Should contain the interrupt names "macirq" - "eth_wake_irq" if this interrupt is supported in the "interrupts" - property +- interrupt-names: Should contain a list of interrupt names corresponding to + the interrupts in the interrupts property, if available. + Valid interrupt names are: + - "macirq" (combined signal for various interrupt events) + - "eth_wake_irq" (the interrupt to manage the remote wake-up packet detection) + - "eth_lpi" (the interrupt that occurs when Tx or Rx enters/exits LPI state) - phy-mode: See ethernet.txt file in the same directory. - snps,reset-gpio gpio number for phy reset. - snps,reset-active-low boolean flag to indicate if phy reset is active low. @@ -152,8 +155,8 @@ Examples: compatible = "st,spear600-gmac"; reg = <0xe0800000 0x8000>; interrupt-parent = <&vic1>; - interrupts = <24 23>; - interrupt-names = "macirq", "eth_wake_irq"; + interrupts = <24 23 22>; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; mac-address = [000000000000]; /* Filled in by U-Boot */ max-frame-size = <3800>; phy-mode = "gmii"; -- cgit From b51df799f6a804297c7cb4f0d19570c4ae3d19b3 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 18 Apr 2017 16:55:31 +0200 Subject: mlxsw: spectrum: Fix indent in mlxsw_sp_netdevice_port_upper_event Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index b031f09bf4e6..9b9861aae7a2 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4038,8 +4038,8 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, err = mlxsw_sp_port_vlan_link(mlxsw_sp_port, upper_dev); else - mlxsw_sp_port_vlan_unlink(mlxsw_sp_port, - upper_dev); + mlxsw_sp_port_vlan_unlink(mlxsw_sp_port, + upper_dev); } else if (netif_is_bridge_master(upper_dev)) { if (info->linking) err = mlxsw_sp_port_bridge_join(mlxsw_sp_port, -- cgit From ac44dd43d8c99f33552ddb0c96a4624b143f505d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 18 Apr 2017 16:55:32 +0200 Subject: mlxsw: spectrum: Implement action to set FID Implement part of multipurpose Virtual Router and Forwarding Domain Action that takes care of setting up FID. We need to use it to be able to forward packets using ACL action when no FID is associated on RX. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- .../mellanox/mlxsw/core_acl_flex_actions.c | 44 ++++++++++++++++++++++ .../mellanox/mlxsw/core_acl_flex_actions.h | 1 + drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 3 ++ drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c | 7 ++++ 4 files changed, 55 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c index a984c361926c..46304ffb9449 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.c @@ -811,3 +811,47 @@ int mlxsw_afa_block_append_counter(struct mlxsw_afa_block *block, return 0; } EXPORT_SYMBOL(mlxsw_afa_block_append_counter); + +/* Virtual Router and Forwarding Domain Action + * ------------------------------------------- + * Virtual Switch action is used for manipulate the Virtual Router (VR), + * MPLS label space and the Forwarding Identifier (FID). + */ + +#define MLXSW_AFA_VIRFWD_CODE 0x0E +#define MLXSW_AFA_VIRFWD_SIZE 1 + +enum mlxsw_afa_virfwd_fid_cmd { + /* Do nothing */ + MLXSW_AFA_VIRFWD_FID_CMD_NOOP, + /* Set the Forwarding Identifier (FID) to fid */ + MLXSW_AFA_VIRFWD_FID_CMD_SET, +}; + +/* afa_virfwd_fid_cmd */ +MLXSW_ITEM32(afa, virfwd, fid_cmd, 0x08, 29, 3); + +/* afa_virfwd_fid + * The FID value. + */ +MLXSW_ITEM32(afa, virfwd, fid, 0x08, 0, 16); + +static inline void mlxsw_afa_virfwd_pack(char *payload, + enum mlxsw_afa_virfwd_fid_cmd fid_cmd, + u16 fid) +{ + mlxsw_afa_virfwd_fid_cmd_set(payload, fid_cmd); + mlxsw_afa_virfwd_fid_set(payload, fid); +} + +int mlxsw_afa_block_append_fid_set(struct mlxsw_afa_block *block, u16 fid) +{ + char *act = mlxsw_afa_block_append_action(block, + MLXSW_AFA_VIRFWD_CODE, + MLXSW_AFA_VIRFWD_SIZE); + if (!act) + return -ENOBUFS; + mlxsw_afa_virfwd_pack(act, MLXSW_AFA_VIRFWD_FID_CMD_SET, fid); + return 0; +} +EXPORT_SYMBOL(mlxsw_afa_block_append_fid_set); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h index a03362c1ef32..bd8b91d02880 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_actions.h @@ -66,5 +66,6 @@ int mlxsw_afa_block_append_vlan_modify(struct mlxsw_afa_block *block, u16 vid, u8 pcp, u8 et); int mlxsw_afa_block_append_counter(struct mlxsw_afa_block *block, u32 counter_index); +int mlxsw_afa_block_append_fid_set(struct mlxsw_afa_block *block, u16 fid); #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index c245e4c3d9ad..3d32b158c07e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -661,6 +661,9 @@ int mlxsw_sp_acl_rulei_act_vlan(struct mlxsw_sp *mlxsw_sp, u32 action, u16 vid, u16 proto, u8 prio); int mlxsw_sp_acl_rulei_act_count(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_rule_info *rulei); +int mlxsw_sp_acl_rulei_act_fid_set(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + u16 fid); struct mlxsw_sp_acl_rule; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index d3b791f69f5b..317f7b14627f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -403,6 +403,13 @@ int mlxsw_sp_acl_rulei_act_count(struct mlxsw_sp *mlxsw_sp, rulei->counter_index); } +int mlxsw_sp_acl_rulei_act_fid_set(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + u16 fid) +{ + return mlxsw_afa_block_append_fid_set(rulei->act_block, fid); +} + struct mlxsw_sp_acl_rule * mlxsw_sp_acl_rule_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset, -- cgit From 202d6f423c8a9600d645becf8a0b6ba908411c3f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 18 Apr 2017 16:55:33 +0200 Subject: mlxsw: spectrum: Add dummy FID initialization For forwarding using ACL action, HW needs a valid FID to be setup. It does not actually use it, so it can be any valid FID. So create a dummy FID only for this purpose. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 21 +++++++++++++++++++++ drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 4 +++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 9b9861aae7a2..6e113c325f92 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3268,6 +3268,18 @@ static int mlxsw_sp_basic_trap_groups_set(struct mlxsw_core *mlxsw_core) return mlxsw_reg_write(mlxsw_core, MLXSW_REG(htgt), htgt_pl); } +static int mlxsw_sp_vfid_op(struct mlxsw_sp *mlxsw_sp, u16 fid, bool create); + +static int mlxsw_sp_dummy_fid_init(struct mlxsw_sp *mlxsw_sp) +{ + return mlxsw_sp_vfid_op(mlxsw_sp, MLXSW_SP_DUMMY_FID, true); +} + +static void mlxsw_sp_dummy_fid_fini(struct mlxsw_sp *mlxsw_sp) +{ + mlxsw_sp_vfid_op(mlxsw_sp, MLXSW_SP_DUMMY_FID, false); +} + static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, const struct mlxsw_bus_info *mlxsw_bus_info) { @@ -3346,6 +3358,12 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, goto err_dpipe_init; } + err = mlxsw_sp_dummy_fid_init(mlxsw_sp); + if (err) { + dev_err(mlxsw_sp->bus_info->dev, "Failed to init dummy FID\n"); + goto err_dummy_fid_init; + } + err = mlxsw_sp_ports_create(mlxsw_sp); if (err) { dev_err(mlxsw_sp->bus_info->dev, "Failed to create ports\n"); @@ -3355,6 +3373,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core, return 0; err_ports_create: + mlxsw_sp_dummy_fid_fini(mlxsw_sp); +err_dummy_fid_init: mlxsw_sp_dpipe_fini(mlxsw_sp); err_dpipe_init: mlxsw_sp_counter_pool_fini(mlxsw_sp); @@ -3381,6 +3401,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core) struct mlxsw_sp *mlxsw_sp = mlxsw_core_driver_priv(mlxsw_core); mlxsw_sp_ports_remove(mlxsw_sp); + mlxsw_sp_dummy_fid_fini(mlxsw_sp); mlxsw_sp_dpipe_fini(mlxsw_sp); mlxsw_sp_counter_pool_fini(mlxsw_sp); mlxsw_sp_acl_fini(mlxsw_sp); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 3d32b158c07e..0af6e1abe0a7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -57,6 +57,8 @@ #define MLXSW_SP_VFID_BASE VLAN_N_VID #define MLXSW_SP_VFID_MAX 1024 /* Bridged VLAN interfaces */ +#define MLXSW_SP_DUMMY_FID 15359 + #define MLXSW_SP_RFID_BASE 15360 #define MLXSW_SP_MID_MAX 7000 @@ -105,7 +107,7 @@ static inline u16 mlxsw_sp_fid_to_vfid(u16 fid) static inline bool mlxsw_sp_fid_is_vfid(u16 fid) { - return fid >= MLXSW_SP_VFID_BASE && fid < MLXSW_SP_RFID_BASE; + return fid >= MLXSW_SP_VFID_BASE && fid < MLXSW_SP_DUMMY_FID; } struct mlxsw_sp_sb_pr { -- cgit From cedbb8b25948764689385075fce793e63fab9973 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 18 Apr 2017 16:55:34 +0200 Subject: mlxsw: spectrum_flower: Set dummy FID before forward action HW requires the FID to be valid in order for the forward action to work. So regardless of the current FID validity, just set the dummy FID which would do the trick. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 3e7a0bcbba72..7d87e23578a3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -71,6 +71,11 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, int ifindex = tcf_mirred_ifindex(a); struct net_device *out_dev; + err = mlxsw_sp_acl_rulei_act_fid_set(mlxsw_sp, rulei, + MLXSW_SP_DUMMY_FID); + if (err) + return err; + out_dev = __dev_get_by_index(dev_net(dev), ifindex); if (out_dev == dev) out_dev = NULL; -- cgit From 93cd081305092929a8be1ebed9eb5bcbe54e4b7d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 18 Apr 2017 16:55:35 +0200 Subject: mlxsw: spectrum: Teach mlxsw_sp_port_vlan_set to accept any vlan range So far, mlxsw_sp_port_vlan_set range is limited by MLXSW_REG_SPVM_REC_MAX_COUNT. In spectrum_switchdev code this is wrapped up by a helper function which actually does multiple calls to FW for bigger ranges. Move the code into mlxsw_sp_port_vlan_set and use it always. That allows caller not to care about the range. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 25 ++++++++++++++-- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 33 ++++------------------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 6e113c325f92..77b5bb38eb01 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1061,8 +1061,9 @@ mlxsw_sp_port_get_stats64(struct net_device *dev, memcpy(stats, mlxsw_sp_port->hw_stats.cache, sizeof(*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) +static 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; @@ -1079,6 +1080,26 @@ int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin, return err; } +int mlxsw_sp_port_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin, + u16 vid_end, bool is_member, bool untagged) +{ + u16 vid, vid_e; + int 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, + is_member, untagged); + if (err) + return err; + } + + return 0; +} + 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; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 05eaa15ad9d5..0d8411f1f954 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -745,27 +745,6 @@ err_port_allow_untagged_set: return err; } -static int __mlxsw_sp_port_vlans_set(struct mlxsw_sp_port *mlxsw_sp_port, - u16 vid_begin, u16 vid_end, bool is_member, - bool untagged) -{ - u16 vid, vid_e; - int 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, - is_member, untagged); - if (err) - return err; - } - - return 0; -} - static int mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid_begin, u16 vid_end, bool learn_enable) @@ -804,8 +783,8 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, return err; } - err = __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end, - true, flag_untagged); + err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid_begin, vid_end, + true, flag_untagged); if (err) { netdev_err(dev, "Unable to add VIDs %d-%d\n", vid_begin, vid_end); @@ -863,8 +842,8 @@ err_port_vid_learning_set: if (old_pvid != mlxsw_sp_port->pvid) mlxsw_sp_port_pvid_set(mlxsw_sp_port, old_pvid); err_port_pvid_set: - __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end, false, - false); + mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid_begin, vid_end, + false, false); err_port_vlans_set: mlxsw_sp_port_fid_leave(mlxsw_sp_port, vid_begin, vid_end); return err; @@ -1171,8 +1150,8 @@ static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, if (pvid >= vid_begin && pvid <= vid_end) mlxsw_sp_port_pvid_set(mlxsw_sp_port, 0); - __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end, false, - false); + mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid_begin, vid_end, + false, false); mlxsw_sp_port_fid_leave(mlxsw_sp_port, vid_begin, vid_end); -- cgit From 5be661412762bbef45a55eaf1e6847258d69b3a4 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 18 Apr 2017 16:55:36 +0200 Subject: net: add netif_is_ovs_port helper To find out if a netdev is an OVS port. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- include/linux/netdevice.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b0aa089ce67f..0f3c38ce5417 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -4171,6 +4171,11 @@ static inline bool netif_is_ovs_master(const struct net_device *dev) return dev->priv_flags & IFF_OPENVSWITCH; } +static inline bool netif_is_ovs_port(const struct net_device *dev) +{ + return dev->priv_flags & IFF_OVS_DATAPATH; +} + static inline bool netif_is_team_master(const struct net_device *dev) { return dev->priv_flags & IFF_TEAM; -- cgit From 2b94e58df58c93e5d74714d7cb5ac924e2089118 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 18 Apr 2017 16:55:37 +0200 Subject: mlxsw: spectrum: Allow ports to work under OVS master >From now on, a port can become a slave of OVS master. All vlans are enabled, STP state is set to "forwarding". It is up to the OVS userspace daemon to setup the flows either in kernel or in HW using TC flower offload. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 62 +++++++++++++++++++++- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 4 +- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 77b5bb38eb01..f1c1b01a45a9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4036,6 +4036,56 @@ static void mlxsw_sp_port_vlan_unlink(struct mlxsw_sp_port *mlxsw_sp_port, mlxsw_sp_vport->dev = mlxsw_sp_port->dev; } +static int mlxsw_sp_port_stp_set(struct mlxsw_sp_port *mlxsw_sp_port, + bool enable) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + enum mlxsw_reg_spms_state spms_state; + char *spms_pl; + u16 vid; + int err; + + spms_state = enable ? MLXSW_REG_SPMS_STATE_FORWARDING : + MLXSW_REG_SPMS_STATE_DISCARDING; + + 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 (vid = 0; vid < VLAN_N_VID; 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_ovs_join(struct mlxsw_sp_port *mlxsw_sp_port) +{ + int err; + + err = mlxsw_sp_port_stp_set(mlxsw_sp_port, true); + if (err) + return err; + err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, 2, VLAN_N_VID - 1, + true, false); + if (err) + goto err_port_vlan_set; + return 0; + +err_port_vlan_set: + mlxsw_sp_port_stp_set(mlxsw_sp_port, false); + return err; +} + +static void mlxsw_sp_port_ovs_leave(struct mlxsw_sp_port *mlxsw_sp_port) +{ + mlxsw_sp_port_vlan_set(mlxsw_sp_port, 2, VLAN_N_VID - 1, + false, false); + mlxsw_sp_port_stp_set(mlxsw_sp_port, false); +} + static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, unsigned long event, void *ptr) { @@ -4055,7 +4105,8 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, if (!is_vlan_dev(upper_dev) && !netif_is_lag_master(upper_dev) && !netif_is_bridge_master(upper_dev) && - !netif_is_l3_master(upper_dev)) + !netif_is_l3_master(upper_dev) && + !netif_is_ovs_master(upper_dev)) return -EINVAL; if (!info->linking) break; @@ -4072,6 +4123,10 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, if (netif_is_lag_port(dev) && is_vlan_dev(upper_dev) && !netif_is_lag_master(vlan_dev_real_dev(upper_dev))) return -EINVAL; + if (netif_is_ovs_master(upper_dev) && vlan_uses_dev(dev)) + return -EINVAL; + if (netif_is_ovs_port(dev) && is_vlan_dev(upper_dev)) + return -EINVAL; break; case NETDEV_CHANGEUPPER: upper_dev = info->upper_dev; @@ -4100,6 +4155,11 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, err = mlxsw_sp_port_vrf_join(mlxsw_sp_port); else mlxsw_sp_port_vrf_leave(mlxsw_sp_port); + } else if (netif_is_ovs_master(upper_dev)) { + if (info->linking) + err = mlxsw_sp_port_ovs_join(mlxsw_sp_port); + else + mlxsw_sp_port_ovs_leave(mlxsw_sp_port); } else { err = -EINVAL; WARN_ON(1); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index c70c59181014..146f8c7d1120 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3098,7 +3098,9 @@ static int mlxsw_sp_inetaddr_vport_event(struct net_device *l3_dev, static int mlxsw_sp_inetaddr_port_event(struct net_device *port_dev, unsigned long event) { - if (netif_is_bridge_port(port_dev) || netif_is_lag_port(port_dev)) + if (netif_is_bridge_port(port_dev) || + netif_is_lag_port(port_dev) || + netif_is_ovs_port(port_dev)) return 0; return mlxsw_sp_inetaddr_vport_event(port_dev, port_dev, event, 1); -- cgit From 9d41accc1e917d071497d749e9686ba6db91517e Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 18 Apr 2017 16:55:38 +0200 Subject: mlxsw: spectrum: Add FID miss trap When there is no FID set for a specific packet, the HW will drop it. However, by default these packets are useful to be delivered to CPU as it can inspect them and program HW accordingly. So add this trap. This would only ever happen when port is enslaved to an OVS master. Otherwise, packets would be dropped during VLAN / STP filtering, before FID classification. Signed-off-by: Jiri Pirko Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 1 + drivers/net/ethernet/mellanox/mlxsw/trap.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index f1c1b01a45a9..20c1b6c2dba0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3008,6 +3008,7 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { MLXSW_SP_RXL_NO_MARK(IGMP_V3_REPORT, TRAP_TO_CPU, IGMP, false), MLXSW_SP_RXL_MARK(ARPBC, MIRROR_TO_CPU, ARP, false), MLXSW_SP_RXL_MARK(ARPUC, MIRROR_TO_CPU, ARP, false), + MLXSW_SP_RXL_NO_MARK(FID_MISS, TRAP_TO_CPU, IP2ME, false), /* L3 traps */ MLXSW_SP_RXL_NO_MARK(MTUERROR, TRAP_TO_CPU, ROUTER_EXP, false), MLXSW_SP_RXL_NO_MARK(TTLERROR, TRAP_TO_CPU, ROUTER_EXP, false), diff --git a/drivers/net/ethernet/mellanox/mlxsw/trap.h b/drivers/net/ethernet/mellanox/mlxsw/trap.h index 02ea48b15eb5..e008fdbed20f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/trap.h +++ b/drivers/net/ethernet/mellanox/mlxsw/trap.h @@ -55,6 +55,7 @@ enum { MLXSW_TRAP_ID_IGMP_V2_LEAVE = 0x33, MLXSW_TRAP_ID_IGMP_V3_REPORT = 0x34, MLXSW_TRAP_ID_PKT_SAMPLE = 0x38, + MLXSW_TRAP_ID_FID_MISS = 0x3D, MLXSW_TRAP_ID_ARPBC = 0x50, MLXSW_TRAP_ID_ARPUC = 0x51, MLXSW_TRAP_ID_MTUERROR = 0x52, -- cgit From 3d4762639dd36a5f0f433f0c9d82e9743dc21a33 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 18 Apr 2017 09:45:51 -0700 Subject: tcp: remove poll() flakes when receiving RST When a RST packet is processed, we send two wakeup events to interested polling users. First one by a sk->sk_error_report(sk) from tcp_reset(), followed by a sk->sk_state_change(sk) from tcp_done(). Depending on machine load and luck, poll() can either return POLLERR, or POLLIN|POLLOUT|POLLERR|POLLHUP (this happens on 99 % of the cases) This is probably fine, but we can avoid the confusion by reordering things so that we have more TCP fields updated before the first wakeup. This might even allow us to remove some barriers we added in the past. Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a5838858c362..37e2aa925f62 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4008,10 +4008,10 @@ void tcp_reset(struct sock *sk) /* This barrier is coupled with smp_rmb() in tcp_poll() */ smp_wmb(); + tcp_done(sk); + if (!sock_flag(sk, SOCK_DEAD)) sk->sk_error_report(sk); - - tcp_done(sk); } /* -- cgit From 0f9fa831aecfc297b7b45d4f046759bcefcf87f0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 18 Apr 2017 09:45:52 -0700 Subject: tcp: remove poll() flakes with FastOpen When using TCP FastOpen for an active session, we send one wakeup event from tcp_finish_connect(), right before the data eventually contained in the received SYNACK is queued to sk->sk_receive_queue. This means that depending on machine load or luck, poll() users might receive POLLOUT events instead of POLLIN|POLLOUT To fix this, we need to move the call to sk->sk_state_change() after the (optional) call to tcp_rcv_fastopen_synack() Signed-off-by: Eric Dumazet Acked-by: Yuchung Cheng Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 37e2aa925f62..341f021f02a2 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5580,10 +5580,6 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) else tp->pred_flags = 0; - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT); - } } static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, @@ -5652,6 +5648,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, struct tcp_sock *tp = tcp_sk(sk); struct tcp_fastopen_cookie foc = { .len = -1 }; int saved_clamp = tp->rx_opt.mss_clamp; + bool fastopen_fail; tcp_parse_options(skb, &tp->rx_opt, 0, &foc); if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) @@ -5755,10 +5752,15 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, tcp_finish_connect(sk, skb); - if ((tp->syn_fastopen || tp->syn_data) && - tcp_rcv_fastopen_synack(sk, skb, &foc)) - return -1; + fastopen_fail = (tp->syn_fastopen || tp->syn_data) && + tcp_rcv_fastopen_synack(sk, skb, &foc); + if (!sock_flag(sk, SOCK_DEAD)) { + sk->sk_state_change(sk); + sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT); + } + if (fastopen_fail) + return -1; if (sk->sk_write_pending || icsk->icsk_accept_queue.rskq_defer_accept || icsk->icsk_ack.pingpong) { -- cgit From 0bd84065b19bca12f07f288c8ea470e2c1b2de7a Mon Sep 17 00:00:00 2001 From: "subashab@codeaurora.org" Date: Tue, 18 Apr 2017 11:39:41 -0600 Subject: net: ipv6: Fix UDP early demux lookup with udp_l3mdev_accept=0 David Ahern reported that 5425077d73e0c ("net: ipv6: Add early demux handler for UDP unicast") breaks udp_l3mdev_accept=0 since early demux for IPv6 UDP was doing a generic socket lookup which does not require an exact match. Fix this by making UDPv6 early demux match connected sockets only. v1->v2: Take reference to socket after match as suggested by Eric v2->v3: Add comment before break Fixes: 5425077d73e0c ("net: ipv6: Add early demux handler for UDP unicast") Reported-by: David Ahern Signed-off-by: Subash Abhinov Kasiviswanathan Cc: Eric Dumazet Acked-by: David Ahern Tested-by: David Ahern Signed-off-by: David S. Miller --- net/ipv6/udp.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index fd4b1c98a472..04862abfe4ec 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -864,21 +865,26 @@ discard: return 0; } + static struct sock *__udp6_lib_demux_lookup(struct net *net, __be16 loc_port, const struct in6_addr *loc_addr, __be16 rmt_port, const struct in6_addr *rmt_addr, int dif) { + unsigned short hnum = ntohs(loc_port); + unsigned int hash2 = udp6_portaddr_hash(net, loc_addr, hnum); + unsigned int slot2 = hash2 & udp_table.mask; + struct udp_hslot *hslot2 = &udp_table.hash2[slot2]; + const __portpair ports = INET_COMBINED_PORTS(rmt_port, hnum); struct sock *sk; - rcu_read_lock(); - sk = __udp6_lib_lookup(net, rmt_addr, rmt_port, loc_addr, loc_port, - dif, &udp_table, NULL); - if (sk && !atomic_inc_not_zero(&sk->sk_refcnt)) - sk = NULL; - rcu_read_unlock(); - - return sk; + udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { + if (INET6_MATCH(sk, net, rmt_addr, loc_addr, ports, dif)) + return sk; + /* Only check first socket in chain */ + break; + } + return NULL; } static void udp_v6_early_demux(struct sk_buff *skb) @@ -903,7 +909,7 @@ static void udp_v6_early_demux(struct sk_buff *skb) else return; - if (!sk) + if (!sk || !atomic_inc_not_zero_hint(&sk->sk_refcnt, 2)) return; skb->sk = sk; -- cgit From 7ab273be23e6cadedfd680556758e77bf522807b Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Tue, 18 Apr 2017 21:23:59 -0400 Subject: Add Cong Wang as TC subsystem co-maintainer Signed-off-by: Jamal Hadi Salim Acked-by: Cong Wang Signed-off-by: David S. Miller --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 7b4b82865179..a72ab454a9f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12198,6 +12198,7 @@ F: kernel/taskstats.c TC subsystem M: Jamal Hadi Salim +M: Cong Wang L: netdev@vger.kernel.org S: Maintained F: include/net/pkt_cls.h -- cgit From b603aa4d3287e910fb79a511953ac150a8d3dde4 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Tue, 18 Apr 2017 21:24:00 -0400 Subject: Add Jiri Pirko as TC subsystem co-maintainer Signed-off-by: Jamal Hadi Salim Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index a72ab454a9f8..2aa84164cad8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12199,6 +12199,7 @@ F: kernel/taskstats.c TC subsystem M: Jamal Hadi Salim M: Cong Wang +M: Jiri Pirko L: netdev@vger.kernel.org S: Maintained F: include/net/pkt_cls.h -- cgit From d6ecf32805502cf76c61084c83a08ac42f9be4e5 Mon Sep 17 00:00:00 2001 From: Chema Gonzalez Date: Tue, 18 Apr 2017 19:22:23 -0700 Subject: tcp_cubic: fix typo in module param description Signed-off-by: Chema Gonzalez Signed-off-by: David S. Miller --- net/ipv4/tcp_cubic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index c99230efcd52..0683ba447d77 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -72,7 +72,7 @@ MODULE_PARM_DESC(tcp_friendliness, "turn on/off tcp friendliness"); module_param(hystart, int, 0644); MODULE_PARM_DESC(hystart, "turn on/off hybrid slow start algorithm"); module_param(hystart_detect, int, 0644); -MODULE_PARM_DESC(hystart_detect, "hyrbrid slow start detection mechanisms" +MODULE_PARM_DESC(hystart_detect, "hybrid slow start detection mechanisms" " 1: packet-train 2: delay 3: both packet-train and delay"); module_param(hystart_low_window, int, 0644); MODULE_PARM_DESC(hystart_low_window, "lower bound cwnd for hybrid slow start"); -- cgit From f6ca26f26837f90727a1450f010a1638834e34e1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 19 Apr 2017 12:54:33 +0300 Subject: qede: allocate enough data for ->arfs_fltr_bmap We've got the number of longs, yes, but we should multiply by sizeof(long) to get the number of bytes needed. Fixes: e4917d46a653 ("qede: Add aRFS support") Signed-off-by: Dan Carpenter Acked-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_filter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index 8c594a3ca63b..34473fbac798 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -267,7 +267,8 @@ int qede_alloc_arfs(struct qede_dev *edev) return -ENOMEM; } - edev->arfs->arfs_fltr_bmap = vzalloc(BITS_TO_LONGS(QEDE_RFS_MAX_FLTR)); + edev->arfs->arfs_fltr_bmap = vzalloc(BITS_TO_LONGS(QEDE_RFS_MAX_FLTR) * + sizeof(long)); if (!edev->arfs->arfs_fltr_bmap) { free_irq_cpu_rmap(edev->ndev->rx_cpu_rmap); edev->ndev->rx_cpu_rmap = NULL; -- cgit From 6905e5a5c8d552ace1d65cacb43499a0eb1e0b89 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 19 Apr 2017 12:59:15 +0300 Subject: net/mlx5e: IPoIB, Fix error handling in mlx5_rdma_netdev_alloc() The labels were out of order, so it either could result in an Oops or a leak. Fixes: 48935bbb7ae8 ("net/mlx5e: IPoIB, Add netdevice profile skeleton") Signed-off-by: Dan Carpenter Acked-by: Leon Romanovsky Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/ipoib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c index 001d2953cb6d..ec78e637840f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c @@ -471,10 +471,11 @@ struct net_device *mlx5_rdma_netdev_alloc(struct mlx5_core_dev *mdev, */ return netdev; -free_mdev_resources: - mlx5e_destroy_mdev_resources(mdev); err_free_netdev: free_netdev(netdev); +free_mdev_resources: + mlx5e_destroy_mdev_resources(mdev); + return NULL; } EXPORT_SYMBOL(mlx5_rdma_netdev_alloc); -- cgit From 14cf2ddf089c897ed660f5b4fbfd00872f0fc619 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 15 Feb 2017 12:08:11 +0100 Subject: e1000: Omit private ndo_get_stats function e1000_get_stats() just returns dev->stats so we can leave it out altogether and let dev_get_stats() do the job. Suggested-by: Joe Perches Signed-off-by: Tobias Klauser Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000/e1000_main.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 93fc6c67306b..bd8b05fe8258 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -131,7 +131,6 @@ static void e1000_watchdog(struct work_struct *work); static void e1000_82547_tx_fifo_stall_task(struct work_struct *work); static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev); -static struct net_device_stats *e1000_get_stats(struct net_device *netdev); static int e1000_change_mtu(struct net_device *netdev, int new_mtu); static int e1000_set_mac(struct net_device *netdev, void *p); static irqreturn_t e1000_intr(int irq, void *data); @@ -846,7 +845,6 @@ static const struct net_device_ops e1000_netdev_ops = { .ndo_open = e1000_open, .ndo_stop = e1000_close, .ndo_start_xmit = e1000_xmit_frame, - .ndo_get_stats = e1000_get_stats, .ndo_set_rx_mode = e1000_set_rx_mode, .ndo_set_mac_address = e1000_set_mac, .ndo_tx_timeout = e1000_tx_timeout, @@ -3529,19 +3527,6 @@ static void e1000_reset_task(struct work_struct *work) e1000_reinit_locked(adapter); } -/** - * e1000_get_stats - Get System Network Statistics - * @netdev: network interface device structure - * - * Returns the address of the device statistics structure. - * The statistics are actually updated from the watchdog. - **/ -static struct net_device_stats *e1000_get_stats(struct net_device *netdev) -{ - /* only return the current stats */ - return &netdev->stats; -} - /** * e1000_change_mtu - Change the Maximum Transfer Unit * @netdev: network interface device structure -- cgit From 541c1662ba2076b5f28b32294c9718b56f27b645 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 15 Feb 2017 12:08:20 +0100 Subject: ixgb: Omit private ndo_get_stats function ixgb_get_stats() just returns dev->stats so we can leave it out altogether and let dev_get_stats() do the job. Suggested-by: Joe Perches Signed-off-by: Tobias Klauser Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgb/ixgb_main.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_main.c b/drivers/net/ethernet/intel/ixgb/ixgb_main.c index fbd220d137b3..5a713199653c 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_main.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_main.c @@ -86,7 +86,6 @@ static void ixgb_set_multi(struct net_device *netdev); static void ixgb_watchdog(unsigned long data); static netdev_tx_t ixgb_xmit_frame(struct sk_buff *skb, struct net_device *netdev); -static struct net_device_stats *ixgb_get_stats(struct net_device *netdev); static int ixgb_change_mtu(struct net_device *netdev, int new_mtu); static int ixgb_set_mac(struct net_device *netdev, void *p); static irqreturn_t ixgb_intr(int irq, void *data); @@ -367,7 +366,6 @@ static const struct net_device_ops ixgb_netdev_ops = { .ndo_open = ixgb_open, .ndo_stop = ixgb_close, .ndo_start_xmit = ixgb_xmit_frame, - .ndo_get_stats = ixgb_get_stats, .ndo_set_rx_mode = ixgb_set_multi, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = ixgb_set_mac, @@ -1596,20 +1594,6 @@ ixgb_tx_timeout_task(struct work_struct *work) ixgb_up(adapter); } -/** - * ixgb_get_stats - Get System Network Statistics - * @netdev: network interface device structure - * - * Returns the address of the device statistics structure. - * The statistics are actually updated from the timer callback. - **/ - -static struct net_device_stats * -ixgb_get_stats(struct net_device *netdev) -{ - return &netdev->stats; -} - /** * ixgb_change_mtu - Change the Maximum Transfer Unit * @netdev: network interface device structure -- cgit From 5313eeccd2d7f486be4e5c7560e3e2be239ec8f7 Mon Sep 17 00:00:00 2001 From: Bernd Faust Date: Thu, 16 Feb 2017 19:42:07 +0100 Subject: e1000e: fix timing for 82579 Gigabit Ethernet controller After an upgrade to Linux kernel v4.x the hardware timestamps of the 82579 Gigabit Ethernet Controller are different than expected. The values that are being read are almost four times as big as before the kernel upgrade. The difference is that after the upgrade the driver sets the clock frequency to 25MHz, where before the upgrade it was set to 96MHz. Intel confirmed that the correct frequency for this network adapter is 96MHz. Signed-off-by: Bernd Faust Acked-by: Sasha Neftin Acked-by: Jacob Keller Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/netdev.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index e9af89ad039c..667fc45ce906 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -3511,6 +3511,12 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca) switch (hw->mac.type) { case e1000_pch2lan: + /* Stable 96MHz frequency */ + incperiod = INCPERIOD_96MHz; + incvalue = INCVALUE_96MHz; + shift = INCVALUE_SHIFT_96MHz; + adapter->cc.shift = shift + INCPERIOD_SHIFT_96MHz; + break; case e1000_pch_lpt: if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) { /* Stable 96MHz frequency */ -- cgit From 83c21335c87622215f3daa59e670cf6467baa869 Mon Sep 17 00:00:00 2001 From: Yury Kylulin Date: Tue, 7 Mar 2017 11:20:25 +0300 Subject: igb: improve MAC filter handling Using the work which was done for ixgbe driver by Jacob Keller commit 5d7daa35b9eb ("ixgbe: improve mac filter handling") and Alexander Duyck commit 0f079d22834a ("ixgbe: Use __dev_uc_sync and __dev_uc_unsync for unicast addresses") and out-of-tree igb driver add functionality to manage (add and delete) MAC filters. Signed-off-by: Yury Kylulin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 11 ++ drivers/net/ethernet/intel/igb/igb_main.c | 246 ++++++++++++++++++++++-------- 2 files changed, 192 insertions(+), 65 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index dc6e2980718f..97f348aa6ba4 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -449,6 +449,15 @@ struct igb_nfc_filter { u16 action; }; +struct igb_mac_addr { + u8 addr[ETH_ALEN]; + u8 queue; + u8 state; /* bitmask */ +}; + +#define IGB_MAC_STATE_DEFAULT 0x1 +#define IGB_MAC_STATE_IN_USE 0x2 + /* board specific private data structure */ struct igb_adapter { unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; @@ -575,6 +584,8 @@ struct igb_adapter { /* lock for RX network flow classification filter */ spinlock_t nfc_lock; bool etype_bitmap[MAX_ETYPE_FILTER]; + + struct igb_mac_addr *mac_table; }; /* flags controlling PTP/1588 function */ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 26a821fcd220..bb5d5ac22f04 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -161,11 +161,16 @@ static void igb_vlan_mode(struct net_device *netdev, static int igb_vlan_rx_add_vid(struct net_device *, __be16, u16); static int igb_vlan_rx_kill_vid(struct net_device *, __be16, u16); static void igb_restore_vlan(struct igb_adapter *); -static void igb_rar_set_qsel(struct igb_adapter *, u8 *, u32 , u8); +static void igb_rar_set_index(struct igb_adapter *, u32); static void igb_ping_all_vfs(struct igb_adapter *); static void igb_msg_task(struct igb_adapter *); static void igb_vmm_control(struct igb_adapter *); static int igb_set_vf_mac(struct igb_adapter *, int, unsigned char *); +static void igb_flush_mac_table(struct igb_adapter *); +static int igb_available_rars(struct igb_adapter *, u8); +static void igb_set_default_mac_filter(struct igb_adapter *); +static int igb_uc_sync(struct net_device *, const unsigned char *); +static int igb_uc_unsync(struct net_device *, const unsigned char *); static void igb_restore_vf_multicasts(struct igb_adapter *adapter); static int igb_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac); static int igb_ndo_set_vf_vlan(struct net_device *netdev, @@ -1987,6 +1992,13 @@ void igb_reset(struct igb_adapter *adapter) if (hw->mac.ops.init_hw(hw)) dev_err(&pdev->dev, "Hardware Error\n"); + /* RAR registers were cleared during init_hw, clear mac table */ + igb_flush_mac_table(adapter); + __dev_uc_unsync(adapter->netdev, NULL); + + /* Recover default RAR entry */ + igb_set_default_mac_filter(adapter); + /* Flow control settings reset on hardware reset, so guarantee flow * control is off when forcing speed. */ @@ -2095,11 +2107,9 @@ static int igb_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], /* guarantee we can provide a unique filter for the unicast address */ if (is_unicast_ether_addr(addr) || is_link_local_ether_addr(addr)) { struct igb_adapter *adapter = netdev_priv(dev); - struct e1000_hw *hw = &adapter->hw; int vfn = adapter->vfs_allocated_count; - int rar_entries = hw->mac.rar_entry_count - (vfn + 1); - if (netdev_uc_count(dev) >= rar_entries) + if (netdev_uc_count(dev) >= igb_available_rars(adapter, vfn)) return -ENOMEM; } @@ -2517,6 +2527,8 @@ static int igb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_eeprom; } + igb_set_default_mac_filter(adapter); + /* get firmware version for ethtool -i */ igb_set_fw_version(adapter); @@ -2761,6 +2773,7 @@ err_eeprom: if (hw->flash_address) iounmap(hw->flash_address); err_sw_init: + kfree(adapter->mac_table); kfree(adapter->shadow_vfta); igb_clear_interrupt_scheme(adapter); #ifdef CONFIG_PCI_IOV @@ -2937,6 +2950,7 @@ static void igb_remove(struct pci_dev *pdev) iounmap(hw->flash_address); pci_release_mem_regions(pdev); + kfree(adapter->mac_table); kfree(adapter->shadow_vfta); free_netdev(netdev); @@ -3099,6 +3113,11 @@ static int igb_sw_init(struct igb_adapter *adapter) /* Assume MSI-X interrupts, will be checked during IRQ allocation */ adapter->flags |= IGB_FLAG_HAS_MSIX; + adapter->mac_table = kzalloc(sizeof(struct igb_mac_addr) * + hw->mac.rar_entry_count, GFP_ATOMIC); + if (!adapter->mac_table) + return -ENOMEM; + igb_probe_vfs(adapter); igb_init_queue_configuration(adapter); @@ -3810,8 +3829,7 @@ static void igb_configure_rx(struct igb_adapter *adapter) int i; /* set the correct pool for the PF default MAC address in entry 0 */ - igb_rar_set_qsel(adapter, adapter->hw.mac.addr, 0, - adapter->vfs_allocated_count); + igb_set_default_mac_filter(adapter); /* Setup the HW Rx Head and Tail Descriptor Pointers and * the Base and Length of the Rx Descriptor Ring @@ -4051,8 +4069,7 @@ static int igb_set_mac(struct net_device *netdev, void *p) memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len); /* set the correct pool for the new PF MAC address in entry 0 */ - igb_rar_set_qsel(adapter, hw->mac.addr, 0, - adapter->vfs_allocated_count); + igb_set_default_mac_filter(adapter); return 0; } @@ -4096,49 +4113,6 @@ static int igb_write_mc_addr_list(struct net_device *netdev) return netdev_mc_count(netdev); } -/** - * igb_write_uc_addr_list - write unicast addresses to RAR table - * @netdev: network interface device structure - * - * Writes unicast address list to the RAR table. - * Returns: -ENOMEM on failure/insufficient address space - * 0 on no addresses written - * X on writing X addresses to the RAR table - **/ -static int igb_write_uc_addr_list(struct net_device *netdev) -{ - struct igb_adapter *adapter = netdev_priv(netdev); - struct e1000_hw *hw = &adapter->hw; - unsigned int vfn = adapter->vfs_allocated_count; - unsigned int rar_entries = hw->mac.rar_entry_count - (vfn + 1); - int count = 0; - - /* return ENOMEM indicating insufficient memory for addresses */ - if (netdev_uc_count(netdev) > rar_entries) - return -ENOMEM; - - if (!netdev_uc_empty(netdev) && rar_entries) { - struct netdev_hw_addr *ha; - - netdev_for_each_uc_addr(ha, netdev) { - if (!rar_entries) - break; - igb_rar_set_qsel(adapter, ha->addr, - rar_entries--, - vfn); - count++; - } - } - /* write the addresses in reverse order to avoid write combining */ - for (; rar_entries > 0 ; rar_entries--) { - wr32(E1000_RAH(rar_entries), 0); - wr32(E1000_RAL(rar_entries), 0); - } - wrfl(); - - return count; -} - static int igb_vlan_promisc_enable(struct igb_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; @@ -4311,8 +4285,7 @@ static void igb_set_rx_mode(struct net_device *netdev) * sufficient space to store all the addresses then enable * unicast promiscuous mode */ - count = igb_write_uc_addr_list(netdev); - if (count < 0) { + if (__dev_uc_sync(netdev, igb_uc_sync, igb_uc_unsync)) { rctl |= E1000_RCTL_UPE; vmolr |= E1000_VMOLR_ROPE; } @@ -6369,7 +6342,6 @@ static void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf) { struct e1000_hw *hw = &adapter->hw; unsigned char *vf_mac = adapter->vf_data[vf].vf_mac_addresses; - int rar_entry = hw->mac.rar_entry_count - (vf + 1); u32 reg, msgbuf[3]; u8 *addr = (u8 *)(&msgbuf[1]); @@ -6377,7 +6349,7 @@ static void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf) igb_vf_reset(adapter, vf); /* set vf mac address */ - igb_rar_set_qsel(adapter, vf_mac, rar_entry, vf); + igb_set_vf_mac(adapter, vf, vf_mac); /* enable transmit and receive for vf */ reg = rd32(E1000_VFTE); @@ -6397,6 +6369,138 @@ static void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf) igb_write_mbx(hw, msgbuf, 3, vf); } +static void igb_flush_mac_table(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + int i; + + for (i = 0; i < hw->mac.rar_entry_count; i++) { + adapter->mac_table[i].state &= ~IGB_MAC_STATE_IN_USE; + memset(adapter->mac_table[i].addr, 0, ETH_ALEN); + adapter->mac_table[i].queue = 0; + igb_rar_set_index(adapter, i); + } +} + +static int igb_available_rars(struct igb_adapter *adapter, u8 queue) +{ + struct e1000_hw *hw = &adapter->hw; + /* do not count rar entries reserved for VFs MAC addresses */ + int rar_entries = hw->mac.rar_entry_count - + adapter->vfs_allocated_count; + int i, count = 0; + + for (i = 0; i < rar_entries; i++) { + /* do not count default entries */ + if (adapter->mac_table[i].state & IGB_MAC_STATE_DEFAULT) + continue; + + /* do not count "in use" entries for different queues */ + if ((adapter->mac_table[i].state & IGB_MAC_STATE_IN_USE) && + (adapter->mac_table[i].queue != queue)) + continue; + + count++; + } + + return count; +} + +/* Set default MAC address for the PF in the first RAR entry */ +static void igb_set_default_mac_filter(struct igb_adapter *adapter) +{ + struct igb_mac_addr *mac_table = &adapter->mac_table[0]; + + ether_addr_copy(mac_table->addr, adapter->hw.mac.addr); + mac_table->queue = adapter->vfs_allocated_count; + mac_table->state = IGB_MAC_STATE_DEFAULT | IGB_MAC_STATE_IN_USE; + + igb_rar_set_index(adapter, 0); +} + +int igb_add_mac_filter(struct igb_adapter *adapter, const u8 *addr, + const u8 queue) +{ + struct e1000_hw *hw = &adapter->hw; + int rar_entries = hw->mac.rar_entry_count - + adapter->vfs_allocated_count; + int i; + + if (is_zero_ether_addr(addr)) + return -EINVAL; + + /* Search for the first empty entry in the MAC table. + * Do not touch entries at the end of the table reserved for the VF MAC + * addresses. + */ + for (i = 0; i < rar_entries; i++) { + if (adapter->mac_table[i].state & IGB_MAC_STATE_IN_USE) + continue; + + ether_addr_copy(adapter->mac_table[i].addr, addr); + adapter->mac_table[i].queue = queue; + adapter->mac_table[i].state |= IGB_MAC_STATE_IN_USE; + + igb_rar_set_index(adapter, i); + return i; + } + + return -ENOSPC; +} + +int igb_del_mac_filter(struct igb_adapter *adapter, const u8 *addr, + const u8 queue) +{ + struct e1000_hw *hw = &adapter->hw; + int rar_entries = hw->mac.rar_entry_count - + adapter->vfs_allocated_count; + int i; + + if (is_zero_ether_addr(addr)) + return -EINVAL; + + /* Search for matching entry in the MAC table based on given address + * and queue. Do not touch entries at the end of the table reserved + * for the VF MAC addresses. + */ + for (i = 0; i < rar_entries; i++) { + if (!(adapter->mac_table[i].state & IGB_MAC_STATE_IN_USE)) + continue; + if (adapter->mac_table[i].queue != queue) + continue; + if (!ether_addr_equal(adapter->mac_table[i].addr, addr)) + continue; + + adapter->mac_table[i].state &= ~IGB_MAC_STATE_IN_USE; + memset(adapter->mac_table[i].addr, 0, ETH_ALEN); + adapter->mac_table[i].queue = 0; + + igb_rar_set_index(adapter, i); + return 0; + } + + return -ENOENT; +} + +static int igb_uc_sync(struct net_device *netdev, const unsigned char *addr) +{ + struct igb_adapter *adapter = netdev_priv(netdev); + int ret; + + ret = igb_add_mac_filter(adapter, addr, adapter->vfs_allocated_count); + + return min_t(int, ret, 0); +} + +static int igb_uc_unsync(struct net_device *netdev, const unsigned char *addr) +{ + struct igb_adapter *adapter = netdev_priv(netdev); + + igb_del_mac_filter(adapter, addr, adapter->vfs_allocated_count); + + return 0; +} + static int igb_set_vf_mac_addr(struct igb_adapter *adapter, u32 *msg, int vf) { /* The VF MAC Address is stored in a packed array of bytes @@ -8078,11 +8182,16 @@ static void igb_io_resume(struct pci_dev *pdev) igb_get_hw_control(adapter); } -static void igb_rar_set_qsel(struct igb_adapter *adapter, u8 *addr, u32 index, - u8 qsel) +/** + * igb_rar_set_index - Sync RAL[index] and RAH[index] registers with MAC table + * @adapter: Pointer to adapter structure + * @index: Index of the RAR entry which need to be synced with MAC table + **/ +static void igb_rar_set_index(struct igb_adapter *adapter, u32 index) { struct e1000_hw *hw = &adapter->hw; u32 rar_low, rar_high; + u8 *addr = adapter->mac_table[index].addr; /* HW expects these to be in network order when they are plugged * into the registers which are little endian. In order to guarantee @@ -8093,12 +8202,16 @@ static void igb_rar_set_qsel(struct igb_adapter *adapter, u8 *addr, u32 index, rar_high = le16_to_cpup((__le16 *)(addr + 4)); /* Indicate to hardware the Address is Valid. */ - rar_high |= E1000_RAH_AV; + if (adapter->mac_table[index].state & IGB_MAC_STATE_IN_USE) { + rar_high |= E1000_RAH_AV; - if (hw->mac.type == e1000_82575) - rar_high |= E1000_RAH_POOL_1 * qsel; - else - rar_high |= E1000_RAH_POOL_1 << qsel; + if (hw->mac.type == e1000_82575) + rar_high |= E1000_RAH_POOL_1 * + adapter->mac_table[index].queue; + else + rar_high |= E1000_RAH_POOL_1 << + adapter->mac_table[index].queue; + } wr32(E1000_RAL(index), rar_low); wrfl(); @@ -8114,10 +8227,13 @@ static int igb_set_vf_mac(struct igb_adapter *adapter, * towards the first, as a result a collision should not be possible */ int rar_entry = hw->mac.rar_entry_count - (vf + 1); + unsigned char *vf_mac_addr = adapter->vf_data[vf].vf_mac_addresses; - memcpy(adapter->vf_data[vf].vf_mac_addresses, mac_addr, ETH_ALEN); - - igb_rar_set_qsel(adapter, mac_addr, rar_entry, vf); + ether_addr_copy(vf_mac_addr, mac_addr); + ether_addr_copy(adapter->mac_table[rar_entry].addr, mac_addr); + adapter->mac_table[rar_entry].queue = vf; + adapter->mac_table[rar_entry].state |= IGB_MAC_STATE_IN_USE; + igb_rar_set_index(adapter, rar_entry); return 0; } -- cgit From 4827cc37796a02ece7097e01dad8e08f537ac815 Mon Sep 17 00:00:00 2001 From: Yury Kylulin Date: Tue, 7 Mar 2017 11:20:26 +0300 Subject: igb/igbvf: Add VF MAC filter request capabilities Add functionality for the VF to request up to 3 additional MAC filters. This is done using existing E1000_VF_SET_MAC_ADDR message, but with additional message info - E1000_VF_MAC_FILTER_CLR to clear all unicast MAC filters previously set for this VF and E1000_VF_MAC_FILTER_ADD to add MAC filter. Additional filters can be added only in case if administrator did not set VF MAC explicitly and allowed to change default MAC to the VF. Due to the limited number of RAR entries reserve at least 3 MAC filters for the PF. If SRIOV is supported by the NIC after this change RAR entries starting from 1 to (RAR MAX ENTRIES - NUM SRIOV VFS) will be used for PF and VF MAC filters. Signed-off-by: Yury Kylulin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_mbx.h | 4 + drivers/net/ethernet/intel/igb/igb.h | 12 +++ drivers/net/ethernet/intel/igb/igb_main.c | 147 ++++++++++++++++++++++++++--- drivers/net/ethernet/intel/igbvf/igbvf.h | 2 + drivers/net/ethernet/intel/igbvf/mbx.h | 4 + drivers/net/ethernet/intel/igbvf/netdev.c | 44 ++++++++- drivers/net/ethernet/intel/igbvf/vf.c | 41 ++++++++ drivers/net/ethernet/intel/igbvf/vf.h | 1 + 8 files changed, 240 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.h b/drivers/net/ethernet/intel/igb/e1000_mbx.h index d20af6b2f581..3e7fed73df15 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mbx.h +++ b/drivers/net/ethernet/intel/igb/e1000_mbx.h @@ -55,6 +55,10 @@ #define E1000_VF_RESET 0x01 /* VF requests reset */ #define E1000_VF_SET_MAC_ADDR 0x02 /* VF requests to set MAC addr */ +/* VF requests to clear all unicast MAC filters */ +#define E1000_VF_MAC_FILTER_CLR (0x01 << E1000_VT_MSGINFO_SHIFT) +/* VF requests to add unicast MAC filter */ +#define E1000_VF_MAC_FILTER_ADD (0x02 << E1000_VT_MSGINFO_SHIFT) #define E1000_VF_SET_MULTICAST 0x03 /* VF requests to set MC addr */ #define E1000_VF_SET_VLAN 0x04 /* VF requests to set VLAN */ #define E1000_VF_SET_LPE 0x05 /* VF requests to set VMOLR.LPE */ diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 97f348aa6ba4..bf9bf9056d0c 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -111,6 +111,16 @@ struct vf_data_storage { bool spoofchk_enabled; }; +/* Number of unicast MAC filters reserved for the PF in the RAR registers */ +#define IGB_PF_MAC_FILTERS_RESERVED 3 + +struct vf_mac_filter { + struct list_head l; + int vf; + bool free; + u8 vf_mac[ETH_ALEN]; +}; + #define IGB_VF_FLAG_CTS 0x00000001 /* VF is clear to send data */ #define IGB_VF_FLAG_UNI_PROMISC 0x00000002 /* VF has unicast promisc */ #define IGB_VF_FLAG_MULTI_PROMISC 0x00000004 /* VF has multicast promisc */ @@ -586,6 +596,8 @@ struct igb_adapter { bool etype_bitmap[MAX_ETYPE_FILTER]; struct igb_mac_addr *mac_table; + struct vf_mac_filter vf_macs; + struct vf_mac_filter *vf_mac_list; }; /* flags controlling PTP/1588 function */ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index bb5d5ac22f04..53e66c87abaf 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1158,6 +1158,8 @@ msi_only: pci_disable_sriov(adapter->pdev); msleep(500); + kfree(adapter->vf_mac_list); + adapter->vf_mac_list = NULL; kfree(adapter->vf_data); adapter->vf_data = NULL; wr32(E1000_IOVCTL, E1000_IOVCTL_REUSE_VFQ); @@ -2809,6 +2811,8 @@ static int igb_disable_sriov(struct pci_dev *pdev) msleep(500); } + kfree(adapter->vf_mac_list); + adapter->vf_mac_list = NULL; kfree(adapter->vf_data); adapter->vf_data = NULL; adapter->vfs_allocated_count = 0; @@ -2829,8 +2833,9 @@ static int igb_enable_sriov(struct pci_dev *pdev, int num_vfs) struct net_device *netdev = pci_get_drvdata(pdev); struct igb_adapter *adapter = netdev_priv(netdev); int old_vfs = pci_num_vf(pdev); + struct vf_mac_filter *mac_list; int err = 0; - int i; + int num_vf_mac_filters, i; if (!(adapter->flags & IGB_FLAG_HAS_MSIX) || num_vfs > 7) { err = -EPERM; @@ -2858,6 +2863,38 @@ static int igb_enable_sriov(struct pci_dev *pdev, int num_vfs) goto out; } + /* Due to the limited number of RAR entries calculate potential + * number of MAC filters available for the VFs. Reserve entries + * for PF default MAC, PF MAC filters and at least one RAR entry + * for each VF for VF MAC. + */ + num_vf_mac_filters = adapter->hw.mac.rar_entry_count - + (1 + IGB_PF_MAC_FILTERS_RESERVED + + adapter->vfs_allocated_count); + + adapter->vf_mac_list = kcalloc(num_vf_mac_filters, + sizeof(struct vf_mac_filter), + GFP_KERNEL); + + mac_list = adapter->vf_mac_list; + INIT_LIST_HEAD(&adapter->vf_macs.l); + + if (adapter->vf_mac_list) { + /* Initialize list of VF MAC filters */ + for (i = 0; i < num_vf_mac_filters; i++) { + mac_list->vf = -1; + mac_list->free = true; + list_add(&mac_list->l, &adapter->vf_macs.l); + mac_list++; + } + } else { + /* If we could not allocate memory for the VF MAC filters + * we can continue without this feature but warn user. + */ + dev_err(&pdev->dev, + "Unable to allocate memory for VF MAC filter list\n"); + } + /* only call pci_enable_sriov() if no VFs are allocated already */ if (!old_vfs) { err = pci_enable_sriov(pdev, adapter->vfs_allocated_count); @@ -2874,6 +2911,8 @@ static int igb_enable_sriov(struct pci_dev *pdev, int num_vfs) goto out; err_out: + kfree(adapter->vf_mac_list); + adapter->vf_mac_list = NULL; kfree(adapter->vf_data); adapter->vf_data = NULL; adapter->vfs_allocated_count = 0; @@ -6501,18 +6540,106 @@ static int igb_uc_unsync(struct net_device *netdev, const unsigned char *addr) return 0; } +int igb_set_vf_mac_filter(struct igb_adapter *adapter, const int vf, + const u32 info, const u8 *addr) +{ + struct pci_dev *pdev = adapter->pdev; + struct vf_data_storage *vf_data = &adapter->vf_data[vf]; + struct list_head *pos; + struct vf_mac_filter *entry = NULL; + int ret = 0; + + switch (info) { + case E1000_VF_MAC_FILTER_CLR: + /* remove all unicast MAC filters related to the current VF */ + list_for_each(pos, &adapter->vf_macs.l) { + entry = list_entry(pos, struct vf_mac_filter, l); + if (entry->vf == vf) { + entry->vf = -1; + entry->free = true; + igb_del_mac_filter(adapter, entry->vf_mac, vf); + } + } + break; + case E1000_VF_MAC_FILTER_ADD: + if (vf_data->flags & IGB_VF_FLAG_PF_SET_MAC) { + dev_warn(&pdev->dev, + "VF %d requested MAC filter but is administratively denied\n", + vf); + return -EINVAL; + } + + if (!is_valid_ether_addr(addr)) { + dev_warn(&pdev->dev, + "VF %d attempted to set invalid MAC filter\n", + vf); + return -EINVAL; + } + + /* try to find empty slot in the list */ + list_for_each(pos, &adapter->vf_macs.l) { + entry = list_entry(pos, struct vf_mac_filter, l); + if (entry->free) + break; + } + + if (entry && entry->free) { + entry->free = false; + entry->vf = vf; + ether_addr_copy(entry->vf_mac, addr); + + ret = igb_add_mac_filter(adapter, addr, vf); + ret = min_t(int, ret, 0); + } else { + ret = -ENOSPC; + } + + if (ret == -ENOSPC) + dev_warn(&pdev->dev, + "VF %d has requested MAC filter but there is no space for it\n", + vf); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + static int igb_set_vf_mac_addr(struct igb_adapter *adapter, u32 *msg, int vf) { + struct pci_dev *pdev = adapter->pdev; + struct vf_data_storage *vf_data = &adapter->vf_data[vf]; + u32 info = msg[0] & E1000_VT_MSGINFO_MASK; + /* The VF MAC Address is stored in a packed array of bytes * starting at the second 32 bit word of the msg array */ - unsigned char *addr = (char *)&msg[1]; - int err = -1; + unsigned char *addr = (unsigned char *)&msg[1]; + int ret = 0; - if (is_valid_ether_addr(addr)) - err = igb_set_vf_mac(adapter, vf, addr); + if (!info) { + if (vf_data->flags & IGB_VF_FLAG_PF_SET_MAC) { + dev_warn(&pdev->dev, + "VF %d attempted to override administratively set MAC address\nReload the VF driver to resume operations\n", + vf); + return -EINVAL; + } - return err; + if (!is_valid_ether_addr(addr)) { + dev_warn(&pdev->dev, + "VF %d attempted to set invalid MAC\n", + vf); + return -EINVAL; + } + + ret = igb_set_vf_mac(adapter, vf, addr); + } else { + ret = igb_set_vf_mac_filter(adapter, vf, info, addr); + } + + return ret; } static void igb_rcv_ack_from_vf(struct igb_adapter *adapter, u32 vf) @@ -6569,13 +6696,7 @@ static void igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf) switch ((msgbuf[0] & 0xFFFF)) { case E1000_VF_SET_MAC_ADDR: - retval = -EINVAL; - if (!(vf_data->flags & IGB_VF_FLAG_PF_SET_MAC)) - retval = igb_set_vf_mac_addr(adapter, msgbuf, vf); - else - dev_warn(&pdev->dev, - "VF %d attempted to override administratively set MAC address\nReload the VF driver to resume operations\n", - vf); + retval = igb_set_vf_mac_addr(adapter, msgbuf, vf); break; case E1000_VF_SET_PROMISC: retval = igb_set_vf_promisc(adapter, msgbuf, vf); diff --git a/drivers/net/ethernet/intel/igbvf/igbvf.h b/drivers/net/ethernet/intel/igbvf/igbvf.h index 6f4290d6dc9f..2a53ed84d9fc 100644 --- a/drivers/net/ethernet/intel/igbvf/igbvf.h +++ b/drivers/net/ethernet/intel/igbvf/igbvf.h @@ -101,6 +101,8 @@ enum latency_range { #define IGBVF_MNG_VLAN_NONE (-1) +#define IGBVF_MAX_MAC_FILTERS 3 + /* Number of packet split data buffers (not including the header buffer) */ #define PS_PAGE_BUFFERS (MAX_PS_BUFFERS - 1) diff --git a/drivers/net/ethernet/intel/igbvf/mbx.h b/drivers/net/ethernet/intel/igbvf/mbx.h index f800bf8eedae..30d58c4a444e 100644 --- a/drivers/net/ethernet/intel/igbvf/mbx.h +++ b/drivers/net/ethernet/intel/igbvf/mbx.h @@ -62,6 +62,10 @@ #define E1000_VF_RESET 0x01 /* VF requests reset */ #define E1000_VF_SET_MAC_ADDR 0x02 /* VF requests PF to set MAC addr */ +/* VF requests PF to clear all unicast MAC filters */ +#define E1000_VF_MAC_FILTER_CLR (0x01 << E1000_VT_MSGINFO_SHIFT) +/* VF requests PF to add unicast MAC filter */ +#define E1000_VF_MAC_FILTER_ADD (0x02 << E1000_VT_MSGINFO_SHIFT) #define E1000_VF_SET_MULTICAST 0x03 /* VF requests PF to set MC addr */ #define E1000_VF_SET_VLAN 0x04 /* VF requests PF to set VLAN */ #define E1000_VF_SET_LPE 0x05 /* VF requests PF to set VMOLR.LPE */ diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 839ba110f7fb..6029854b6a70 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -1432,13 +1432,53 @@ static void igbvf_set_multi(struct net_device *netdev) kfree(mta_list); } +/** + * igbvf_set_uni - Configure unicast MAC filters + * @netdev: network interface device structure + * + * This routine is responsible for configuring the hardware for proper + * unicast filters. + **/ +static int igbvf_set_uni(struct net_device *netdev) +{ + struct igbvf_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + + if (netdev_uc_count(netdev) > IGBVF_MAX_MAC_FILTERS) { + pr_err("Too many unicast filters - No Space\n"); + return -ENOSPC; + } + + /* Clear all unicast MAC filters */ + hw->mac.ops.set_uc_addr(hw, E1000_VF_MAC_FILTER_CLR, NULL); + + if (!netdev_uc_empty(netdev)) { + struct netdev_hw_addr *ha; + + /* Add MAC filters one by one */ + netdev_for_each_uc_addr(ha, netdev) { + hw->mac.ops.set_uc_addr(hw, E1000_VF_MAC_FILTER_ADD, + ha->addr); + udelay(200); + } + } + + return 0; +} + +static void igbvf_set_rx_mode(struct net_device *netdev) +{ + igbvf_set_multi(netdev); + igbvf_set_uni(netdev); +} + /** * igbvf_configure - configure the hardware for Rx and Tx * @adapter: private board structure **/ static void igbvf_configure(struct igbvf_adapter *adapter) { - igbvf_set_multi(adapter->netdev); + igbvf_set_rx_mode(adapter->netdev); igbvf_restore_vlan(adapter); @@ -2636,7 +2676,7 @@ static const struct net_device_ops igbvf_netdev_ops = { .ndo_stop = igbvf_close, .ndo_start_xmit = igbvf_xmit_frame, .ndo_get_stats = igbvf_get_stats, - .ndo_set_rx_mode = igbvf_set_multi, + .ndo_set_rx_mode = igbvf_set_rx_mode, .ndo_set_mac_address = igbvf_set_mac, .ndo_change_mtu = igbvf_change_mtu, .ndo_do_ioctl = igbvf_ioctl, diff --git a/drivers/net/ethernet/intel/igbvf/vf.c b/drivers/net/ethernet/intel/igbvf/vf.c index 335ba6642145..528be116184e 100644 --- a/drivers/net/ethernet/intel/igbvf/vf.c +++ b/drivers/net/ethernet/intel/igbvf/vf.c @@ -36,6 +36,7 @@ static void e1000_update_mc_addr_list_vf(struct e1000_hw *hw, u8 *, u32, u32, u32); static void e1000_rar_set_vf(struct e1000_hw *, u8 *, u32); static s32 e1000_read_mac_addr_vf(struct e1000_hw *); +static s32 e1000_set_uc_addr_vf(struct e1000_hw *hw, u32 subcmd, u8 *addr); static s32 e1000_set_vfta_vf(struct e1000_hw *, u16, bool); /** @@ -66,6 +67,8 @@ static s32 e1000_init_mac_params_vf(struct e1000_hw *hw) mac->ops.rar_set = e1000_rar_set_vf; /* read mac address */ mac->ops.read_mac_addr = e1000_read_mac_addr_vf; + /* set mac filter */ + mac->ops.set_uc_addr = e1000_set_uc_addr_vf; /* set vlan filter table array */ mac->ops.set_vfta = e1000_set_vfta_vf; @@ -337,6 +340,44 @@ static s32 e1000_read_mac_addr_vf(struct e1000_hw *hw) return E1000_SUCCESS; } +/** + * e1000_set_uc_addr_vf - Set or clear unicast filters + * @hw: pointer to the HW structure + * @sub_cmd: add or clear filters + * @addr: pointer to the filter MAC address + **/ +static s32 e1000_set_uc_addr_vf(struct e1000_hw *hw, u32 sub_cmd, u8 *addr) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + u32 msgbuf[3], msgbuf_chk; + u8 *msg_addr = (u8 *)(&msgbuf[1]); + s32 ret_val; + + memset(msgbuf, 0, sizeof(msgbuf)); + msgbuf[0] |= sub_cmd; + msgbuf[0] |= E1000_VF_SET_MAC_ADDR; + msgbuf_chk = msgbuf[0]; + + if (addr) + memcpy(msg_addr, addr, ETH_ALEN); + + ret_val = mbx->ops.write_posted(hw, msgbuf, 3); + + if (!ret_val) + ret_val = mbx->ops.read_posted(hw, msgbuf, 3); + + msgbuf[0] &= ~E1000_VT_MSGTYPE_CTS; + + if (!ret_val) { + msgbuf[0] &= ~E1000_VT_MSGTYPE_CTS; + + if (msgbuf[0] == (msgbuf_chk | E1000_VT_MSGTYPE_NACK)) + return -ENOSPC; + } + + return ret_val; +} + /** * e1000_check_for_link_vf - Check for link for a virtual interface * @hw: pointer to the HW structure diff --git a/drivers/net/ethernet/intel/igbvf/vf.h b/drivers/net/ethernet/intel/igbvf/vf.h index f00a41d9a1ca..4cf78b0dec50 100644 --- a/drivers/net/ethernet/intel/igbvf/vf.h +++ b/drivers/net/ethernet/intel/igbvf/vf.h @@ -179,6 +179,7 @@ struct e1000_mac_operations { s32 (*get_bus_info)(struct e1000_hw *); s32 (*get_link_up_info)(struct e1000_hw *, u16 *, u16 *); void (*update_mc_addr_list)(struct e1000_hw *, u8 *, u32, u32, u32); + s32 (*set_uc_addr)(struct e1000_hw *, u32, u8 *); s32 (*reset_hw)(struct e1000_hw *); s32 (*init_hw)(struct e1000_hw *); s32 (*setup_link)(struct e1000_hw *); -- cgit From b90fa8763560aab7c75697fb324ffc73fb813ecc Mon Sep 17 00:00:00 2001 From: Kim Tatt Chuah Date: Mon, 27 Mar 2017 08:44:35 +0800 Subject: igb: Enable reading of wake up packet Currently, in igb_resume(), igb driver ignores the Wake Up Status (WUS) and Wake Up Packet Memory (WUPM) registers. This patch enables the igb driver to read the WUPM if the controller was woken by a wake up packet that is not more than 128 bytes long (maximum WUPM size), then pass it up the kernel network stack. Signed-off-by: Kim Tatt Chuah Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_defines.h | 21 +++++++++++++++ drivers/net/ethernet/intel/igb/igb_main.c | 36 +++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index 8aee314332a8..d8517779439b 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -39,6 +39,27 @@ #define E1000_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */ #define E1000_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ +/* Wake Up Status */ +#define E1000_WUS_EX 0x00000004 /* Directed Exact */ +#define E1000_WUS_ARPD 0x00000020 /* Directed ARP Request */ +#define E1000_WUS_IPV4 0x00000040 /* Directed IPv4 */ +#define E1000_WUS_IPV6 0x00000080 /* Directed IPv6 */ +#define E1000_WUS_NSD 0x00000400 /* Directed IPv6 Neighbor Solicitation */ + +/* Packet types that are enabled for wake packet delivery */ +#define WAKE_PKT_WUS ( \ + E1000_WUS_EX | \ + E1000_WUS_ARPD | \ + E1000_WUS_IPV4 | \ + E1000_WUS_IPV6 | \ + E1000_WUS_NSD) + +/* Wake Up Packet Length */ +#define E1000_WUPL_MASK 0x00000FFF + +/* Wake Up Packet Memory stores the first 128 bytes of the wake up packet */ +#define E1000_WUPM_BYTES 128 + /* Extended Device Control */ #define E1000_CTRL_EXT_SDP2_DATA 0x00000040 /* Value of SW Defineable Pin 2 */ #define E1000_CTRL_EXT_SDP3_DATA 0x00000080 /* Value of SW Defineable Pin 3 */ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 53e66c87abaf..1cf74aa4ebd9 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -7985,6 +7985,36 @@ static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake, return 0; } +static void igb_deliver_wake_packet(struct net_device *netdev) +{ + struct igb_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + struct sk_buff *skb; + u32 wupl; + + wupl = rd32(E1000_WUPL) & E1000_WUPL_MASK; + + /* WUPM stores only the first 128 bytes of the wake packet. + * Read the packet only if we have the whole thing. + */ + if ((wupl == 0) || (wupl > E1000_WUPM_BYTES)) + return; + + skb = netdev_alloc_skb_ip_align(netdev, E1000_WUPM_BYTES); + if (!skb) + return; + + skb_put(skb, wupl); + + /* Ensure reads are 32-bit aligned */ + wupl = roundup(wupl, 4); + + memcpy_fromio(skb->data, hw->hw_addr + E1000_WUPM_REG(0), wupl); + + skb->protocol = eth_type_trans(skb, netdev); + netif_rx(skb); +} + #ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP static int igb_suspend(struct device *dev) @@ -8014,7 +8044,7 @@ static int igb_resume(struct device *dev) struct net_device *netdev = pci_get_drvdata(pdev); struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - u32 err; + u32 err, val; pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); @@ -8045,6 +8075,10 @@ static int igb_resume(struct device *dev) */ igb_get_hw_control(adapter); + val = rd32(E1000_WUS); + if (val & WAKE_PKT_WUS) + igb_deliver_wake_packet(netdev); + wr32(E1000_WUS, ~0); rtnl_lock(); -- cgit From 55c05dd0295d8334a987c82cc57abe60a5d49f37 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Thu, 6 Apr 2017 08:45:22 +0200 Subject: igbvf: Use net_device_stats from struct net_device Instead of using a private copy of struct net_device_stats in struct igbvf_adapter, use stats from struct net_device. Also remove the now unnecessary .ndo_get_stats function. Signed-off-by: Tobias Klauser Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igbvf/igbvf.h | 1 - drivers/net/ethernet/intel/igbvf/netdev.c | 26 +++++--------------------- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/intel/igbvf/igbvf.h b/drivers/net/ethernet/intel/igbvf/igbvf.h index 2a53ed84d9fc..bf69f01f8467 100644 --- a/drivers/net/ethernet/intel/igbvf/igbvf.h +++ b/drivers/net/ethernet/intel/igbvf/igbvf.h @@ -243,7 +243,6 @@ struct igbvf_adapter { /* OS defined structs */ struct net_device *netdev; struct pci_dev *pdev; - struct net_device_stats net_stats; spinlock_t stats_lock; /* prevent concurrent stats updates */ /* structs defined in e1000_hw.h */ diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 6029854b6a70..1b9cbbe88f6f 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -400,8 +400,8 @@ next_desc: adapter->total_rx_packets += total_packets; adapter->total_rx_bytes += total_bytes; - adapter->net_stats.rx_bytes += total_bytes; - adapter->net_stats.rx_packets += total_packets; + netdev->stats.rx_bytes += total_bytes; + netdev->stats.rx_packets += total_packets; return cleaned; } @@ -864,8 +864,8 @@ static bool igbvf_clean_tx_irq(struct igbvf_ring *tx_ring) } } - adapter->net_stats.tx_bytes += total_bytes; - adapter->net_stats.tx_packets += total_packets; + netdev->stats.tx_bytes += total_bytes; + netdev->stats.tx_packets += total_packets; return count < tx_ring->count; } @@ -1838,7 +1838,7 @@ void igbvf_update_stats(struct igbvf_adapter *adapter) UPDATE_VF_COUNTER(VFGPRLBC, gprlbc); /* Fill out the OS statistics structure */ - adapter->net_stats.multicast = adapter->stats.mprc; + adapter->netdev->stats.multicast = adapter->stats.mprc; } static void igbvf_print_link_info(struct igbvf_adapter *adapter) @@ -2373,21 +2373,6 @@ static void igbvf_reset_task(struct work_struct *work) igbvf_reinit_locked(adapter); } -/** - * igbvf_get_stats - Get System Network Statistics - * @netdev: network interface device structure - * - * Returns the address of the device statistics structure. - * The statistics are actually updated from the timer callback. - **/ -static struct net_device_stats *igbvf_get_stats(struct net_device *netdev) -{ - struct igbvf_adapter *adapter = netdev_priv(netdev); - - /* only return the current stats */ - return &adapter->net_stats; -} - /** * igbvf_change_mtu - Change the Maximum Transfer Unit * @netdev: network interface device structure @@ -2675,7 +2660,6 @@ static const struct net_device_ops igbvf_netdev_ops = { .ndo_open = igbvf_open, .ndo_stop = igbvf_close, .ndo_start_xmit = igbvf_xmit_frame, - .ndo_get_stats = igbvf_get_stats, .ndo_set_rx_mode = igbvf_set_rx_mode, .ndo_set_mac_address = igbvf_set_mac, .ndo_change_mtu = igbvf_change_mtu, -- cgit From 8e6c1812e632ae3b54a1a9da759cad762f633e11 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 20 Apr 2017 15:47:22 -0700 Subject: net: dsa: Remove redundant NULL dst check tag_lan9303.c does check for a NULL dst but that's already checked by dsa_switch_rcv() one layer above. Signed-off-by: Florian Fainelli Acked-by: Juergen Borleis Signed-off-by: David S. Miller --- net/dsa/tag_lan9303.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/dsa/tag_lan9303.c b/net/dsa/tag_lan9303.c index 563b6c8fe445..70130ed5c21a 100644 --- a/net/dsa/tag_lan9303.c +++ b/net/dsa/tag_lan9303.c @@ -79,11 +79,6 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev, struct dsa_switch *ds; unsigned int source_port; - if (unlikely(!dst)) { - dev_warn_ratelimited(&dev->dev, "Dropping packet, due to missing switch tree device\n"); - return NULL; - } - ds = dst->ds[0]; if (unlikely(!ds)) { -- cgit From 0a473b82cb23e7a35c4be6e9765c8487a65e8f55 Mon Sep 17 00:00:00 2001 From: Craig Gallek Date: Wed, 19 Apr 2017 12:30:53 -0400 Subject: ip6_tunnel: Allow policy-based routing through tunnels This feature allows the administrator to set an fwmark for packets traversing a tunnel. This allows the use of independent routing tables for tunneled packets without the use of iptables. Signed-off-by: Craig Gallek Signed-off-by: David S. Miller --- include/net/ip6_tunnel.h | 2 ++ include/uapi/linux/if_tunnel.h | 3 +++ net/ipv6/ip6_gre.c | 14 +++++++++++++- net/ipv6/ip6_tunnel.c | 15 ++++++++++++++- net/ipv6/ip6_vti.c | 10 +++++++++- 5 files changed, 41 insertions(+), 3 deletions(-) diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index 1b1cf33cbfb0..08fbc7f7d8d7 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -33,6 +33,8 @@ struct __ip6_tnl_parm { __be16 o_flags; __be32 i_key; __be32 o_key; + + __u32 fwmark; }; /* IPv6 tunnel */ diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h index 92f3c8677523..6792d1967d31 100644 --- a/include/uapi/linux/if_tunnel.h +++ b/include/uapi/linux/if_tunnel.h @@ -75,6 +75,7 @@ enum { IFLA_IPTUN_ENCAP_SPORT, IFLA_IPTUN_ENCAP_DPORT, IFLA_IPTUN_COLLECT_METADATA, + IFLA_IPTUN_FWMARK, __IFLA_IPTUN_MAX, }; #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) @@ -132,6 +133,7 @@ enum { IFLA_GRE_ENCAP_DPORT, IFLA_GRE_COLLECT_METADATA, IFLA_GRE_IGNORE_DF, + IFLA_GRE_FWMARK, __IFLA_GRE_MAX, }; @@ -147,6 +149,7 @@ enum { IFLA_VTI_OKEY, IFLA_VTI_LOCAL, IFLA_VTI_REMOTE, + IFLA_VTI_FWMARK, __IFLA_VTI_MAX, }; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 6fcb7cb49bb2..8d128ba79b66 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -544,6 +544,8 @@ static inline int ip6gre_xmit_ipv4(struct sk_buff *skb, struct net_device *dev) & IPV6_TCLASS_MASK; if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) fl6.flowi6_mark = skb->mark; + else + fl6.flowi6_mark = t->parms.fwmark; fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); @@ -603,6 +605,8 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev) fl6.flowlabel |= ip6_flowlabel(ipv6h); if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) fl6.flowi6_mark = skb->mark; + else + fl6.flowi6_mark = t->parms.fwmark; fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); @@ -780,6 +784,7 @@ static int ip6gre_tnl_change(struct ip6_tnl *t, t->parms.o_key = p->o_key; t->parms.i_flags = p->i_flags; t->parms.o_flags = p->o_flags; + t->parms.fwmark = p->fwmark; dst_cache_reset(&t->dst_cache); ip6gre_tnl_link_config(t, set_mtu); return 0; @@ -1249,6 +1254,9 @@ static void ip6gre_netlink_parms(struct nlattr *data[], if (data[IFLA_GRE_FLAGS]) parms->flags = nla_get_u32(data[IFLA_GRE_FLAGS]); + + if (data[IFLA_GRE_FWMARK]) + parms->fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]); } static int ip6gre_tap_init(struct net_device *dev) @@ -1470,6 +1478,8 @@ static size_t ip6gre_get_size(const struct net_device *dev) nla_total_size(2) + /* IFLA_GRE_ENCAP_DPORT */ nla_total_size(2) + + /* IFLA_GRE_FWMARK */ + nla_total_size(4) + 0; } @@ -1490,7 +1500,8 @@ static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u8(skb, IFLA_GRE_TTL, p->hop_limit) || nla_put_u8(skb, IFLA_GRE_ENCAP_LIMIT, p->encap_limit) || nla_put_be32(skb, IFLA_GRE_FLOWINFO, p->flowinfo) || - nla_put_u32(skb, IFLA_GRE_FLAGS, p->flags)) + nla_put_u32(skb, IFLA_GRE_FLAGS, p->flags) || + nla_put_u32(skb, IFLA_GRE_FWMARK, p->fwmark)) goto nla_put_failure; if (nla_put_u16(skb, IFLA_GRE_ENCAP_TYPE, @@ -1525,6 +1536,7 @@ static const struct nla_policy ip6gre_policy[IFLA_GRE_MAX + 1] = { [IFLA_GRE_ENCAP_FLAGS] = { .type = NLA_U16 }, [IFLA_GRE_ENCAP_SPORT] = { .type = NLA_U16 }, [IFLA_GRE_ENCAP_DPORT] = { .type = NLA_U16 }, + [IFLA_GRE_FWMARK] = { .type = NLA_U32 }, }; static struct rtnl_link_ops ip6gre_link_ops __read_mostly = { diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 75fac933c209..ad15d38b41e8 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1256,6 +1256,8 @@ ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) & IPV6_TCLASS_MASK; if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) fl6.flowi6_mark = skb->mark; + else + fl6.flowi6_mark = t->parms.fwmark; } fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); @@ -1338,6 +1340,8 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) fl6.flowlabel |= ip6_flowlabel(ipv6h); if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK) fl6.flowi6_mark = skb->mark; + else + fl6.flowi6_mark = t->parms.fwmark; } fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); @@ -1467,6 +1471,7 @@ ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p) t->parms.flowinfo = p->flowinfo; t->parms.link = p->link; t->parms.proto = p->proto; + t->parms.fwmark = p->fwmark; dst_cache_reset(&t->dst_cache); ip6_tnl_link_config(t); return 0; @@ -1918,6 +1923,9 @@ static void ip6_tnl_netlink_parms(struct nlattr *data[], if (data[IFLA_IPTUN_COLLECT_METADATA]) parms->collect_md = true; + + if (data[IFLA_IPTUN_FWMARK]) + parms->fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]); } static bool ip6_tnl_netlink_encap_parms(struct nlattr *data[], @@ -2054,6 +2062,8 @@ static size_t ip6_tnl_get_size(const struct net_device *dev) nla_total_size(2) + /* IFLA_IPTUN_COLLECT_METADATA */ nla_total_size(0) + + /* IFLA_IPTUN_FWMARK */ + nla_total_size(4) + 0; } @@ -2069,7 +2079,8 @@ static int ip6_tnl_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u8(skb, IFLA_IPTUN_ENCAP_LIMIT, parm->encap_limit) || nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) || nla_put_u32(skb, IFLA_IPTUN_FLAGS, parm->flags) || - nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto)) + nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto) || + nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark)) goto nla_put_failure; if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE, tunnel->encap.type) || @@ -2081,6 +2092,7 @@ static int ip6_tnl_fill_info(struct sk_buff *skb, const struct net_device *dev) if (parm->collect_md) if (nla_put_flag(skb, IFLA_IPTUN_COLLECT_METADATA)) goto nla_put_failure; + return 0; nla_put_failure: @@ -2109,6 +2121,7 @@ static const struct nla_policy ip6_tnl_policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_ENCAP_SPORT] = { .type = NLA_U16 }, [IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 }, [IFLA_IPTUN_COLLECT_METADATA] = { .type = NLA_FLAG }, + [IFLA_IPTUN_FWMARK] = { .type = NLA_U32 }, }; static struct rtnl_link_ops ip6_link_ops __read_mostly = { diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 3d8a3b63b4fd..d67ef56454b2 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -657,6 +657,7 @@ vti6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p) t->parms.i_key = p->i_key; t->parms.o_key = p->o_key; t->parms.proto = p->proto; + t->parms.fwmark = p->fwmark; dst_cache_reset(&t->dst_cache); vti6_link_config(t); return 0; @@ -933,6 +934,9 @@ static void vti6_netlink_parms(struct nlattr *data[], if (data[IFLA_VTI_OKEY]) parms->o_key = nla_get_be32(data[IFLA_VTI_OKEY]); + + if (data[IFLA_VTI_FWMARK]) + parms->fwmark = nla_get_u32(data[IFLA_VTI_FWMARK]); } static int vti6_newlink(struct net *src_net, struct net_device *dev, @@ -998,6 +1002,8 @@ static size_t vti6_get_size(const struct net_device *dev) nla_total_size(4) + /* IFLA_VTI_OKEY */ nla_total_size(4) + + /* IFLA_VTI_FWMARK */ + nla_total_size(4) + 0; } @@ -1010,7 +1016,8 @@ static int vti6_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_in6_addr(skb, IFLA_VTI_LOCAL, &parm->laddr) || nla_put_in6_addr(skb, IFLA_VTI_REMOTE, &parm->raddr) || nla_put_be32(skb, IFLA_VTI_IKEY, parm->i_key) || - nla_put_be32(skb, IFLA_VTI_OKEY, parm->o_key)) + nla_put_be32(skb, IFLA_VTI_OKEY, parm->o_key) || + nla_put_u32(skb, IFLA_VTI_FWMARK, parm->fwmark)) goto nla_put_failure; return 0; @@ -1024,6 +1031,7 @@ static const struct nla_policy vti6_policy[IFLA_VTI_MAX + 1] = { [IFLA_VTI_REMOTE] = { .len = sizeof(struct in6_addr) }, [IFLA_VTI_IKEY] = { .type = NLA_U32 }, [IFLA_VTI_OKEY] = { .type = NLA_U32 }, + [IFLA_VTI_FWMARK] = { .type = NLA_U32 }, }; static struct rtnl_link_ops vti6_link_ops __read_mostly = { -- cgit From 9830ad4c6a7f8db18d3b0933875937e36470987d Mon Sep 17 00:00:00 2001 From: Craig Gallek Date: Wed, 19 Apr 2017 12:30:54 -0400 Subject: ip_tunnel: Allow policy-based routing through tunnels This feature allows the administrator to set an fwmark for packets traversing a tunnel. This allows the use of independent routing tables for tunneled packets without the use of iptables. There is no concept of per-packet routing decisions through IPv4 tunnels, so this implementation does not need to work with per-packet route lookups as the v6 implementation may (with IP6_TNL_F_USE_ORIG_FWMARK). Further, since the v4 tunnel ioctls share datastructures (which can not be trivially modified) with the kernel's internal tunnel configuration structures, the mark attribute must be stored in the tunnel structure itself and passed as a parameter when creating or changing tunnel attributes. Signed-off-by: Craig Gallek Signed-off-by: David S. Miller --- include/net/ip_tunnels.h | 5 +++-- net/ipv4/ip_gre.c | 24 +++++++++++++++++------- net/ipv4/ip_tunnel.c | 27 +++++++++++++++++---------- net/ipv4/ip_vti.c | 20 +++++++++++++++----- net/ipv4/ipip.c | 24 +++++++++++++++++------- net/ipv6/sit.c | 37 ++++++++++++++++++++++++------------- 6 files changed, 93 insertions(+), 44 deletions(-) diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 95056796657c..520809912f03 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -132,6 +132,7 @@ struct ip_tunnel { unsigned int prl_count; /* # of entries in PRL */ unsigned int ip_tnl_net_id; struct gro_cells gro_cells; + __u32 fwmark; bool collect_md; bool ignore_df; }; @@ -273,9 +274,9 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb, const struct tnl_ptk_info *tpi, struct metadata_dst *tun_dst, bool log_ecn_error); int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[], - struct ip_tunnel_parm *p); + struct ip_tunnel_parm *p, __u32 fwmark); int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[], - struct ip_tunnel_parm *p); + struct ip_tunnel_parm *p, __u32 fwmark); void ip_tunnel_setup(struct net_device *dev, unsigned int net_id); struct ip_tunnel_encap_ops { diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index c9c1cb635d9a..e90c80a548ad 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -829,7 +829,8 @@ out: static int ipgre_netlink_parms(struct net_device *dev, struct nlattr *data[], struct nlattr *tb[], - struct ip_tunnel_parm *parms) + struct ip_tunnel_parm *parms, + __u32 *fwmark) { struct ip_tunnel *t = netdev_priv(dev); @@ -886,6 +887,9 @@ static int ipgre_netlink_parms(struct net_device *dev, t->ignore_df = !!nla_get_u8(data[IFLA_GRE_IGNORE_DF]); } + if (data[IFLA_GRE_FWMARK]) + *fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]); + return 0; } @@ -957,6 +961,7 @@ static int ipgre_newlink(struct net *src_net, struct net_device *dev, { struct ip_tunnel_parm p; struct ip_tunnel_encap ipencap; + __u32 fwmark = 0; int err; if (ipgre_netlink_encap_parms(data, &ipencap)) { @@ -967,31 +972,32 @@ static int ipgre_newlink(struct net *src_net, struct net_device *dev, return err; } - err = ipgre_netlink_parms(dev, data, tb, &p); + err = ipgre_netlink_parms(dev, data, tb, &p, &fwmark); if (err < 0) return err; - return ip_tunnel_newlink(dev, tb, &p); + return ip_tunnel_newlink(dev, tb, &p, fwmark); } static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { + struct ip_tunnel *t = netdev_priv(dev); struct ip_tunnel_parm p; struct ip_tunnel_encap ipencap; + __u32 fwmark = t->fwmark; int err; if (ipgre_netlink_encap_parms(data, &ipencap)) { - struct ip_tunnel *t = netdev_priv(dev); err = ip_tunnel_encap_setup(t, &ipencap); if (err < 0) return err; } - err = ipgre_netlink_parms(dev, data, tb, &p); + err = ipgre_netlink_parms(dev, data, tb, &p, &fwmark); if (err < 0) return err; - return ip_tunnel_changelink(dev, tb, &p); + return ip_tunnel_changelink(dev, tb, &p, fwmark); } static size_t ipgre_get_size(const struct net_device *dev) @@ -1029,6 +1035,8 @@ static size_t ipgre_get_size(const struct net_device *dev) nla_total_size(0) + /* IFLA_GRE_IGNORE_DF */ nla_total_size(1) + + /* IFLA_GRE_FWMARK */ + nla_total_size(4) + 0; } @@ -1049,7 +1057,8 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u8(skb, IFLA_GRE_TTL, p->iph.ttl) || nla_put_u8(skb, IFLA_GRE_TOS, p->iph.tos) || nla_put_u8(skb, IFLA_GRE_PMTUDISC, - !!(p->iph.frag_off & htons(IP_DF)))) + !!(p->iph.frag_off & htons(IP_DF))) || + nla_put_u32(skb, IFLA_GRE_FWMARK, t->fwmark)) goto nla_put_failure; if (nla_put_u16(skb, IFLA_GRE_ENCAP_TYPE, @@ -1093,6 +1102,7 @@ static const struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = { [IFLA_GRE_ENCAP_DPORT] = { .type = NLA_U16 }, [IFLA_GRE_COLLECT_METADATA] = { .type = NLA_FLAG }, [IFLA_GRE_IGNORE_DF] = { .type = NLA_U8 }, + [IFLA_GRE_FWMARK] = { .type = NLA_U32 }, }; static struct rtnl_link_ops ipgre_link_ops __read_mostly = { diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 823abaef006b..b878ecbc0608 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -293,7 +293,8 @@ failed: static inline void init_tunnel_flow(struct flowi4 *fl4, int proto, __be32 daddr, __be32 saddr, - __be32 key, __u8 tos, int oif) + __be32 key, __u8 tos, int oif, + __u32 mark) { memset(fl4, 0, sizeof(*fl4)); fl4->flowi4_oif = oif; @@ -302,6 +303,7 @@ static inline void init_tunnel_flow(struct flowi4 *fl4, fl4->flowi4_tos = tos; fl4->flowi4_proto = proto; fl4->fl4_gre_key = key; + fl4->flowi4_mark = mark; } static int ip_tunnel_bind_dev(struct net_device *dev) @@ -322,7 +324,8 @@ static int ip_tunnel_bind_dev(struct net_device *dev) init_tunnel_flow(&fl4, iph->protocol, iph->daddr, iph->saddr, tunnel->parms.o_key, - RT_TOS(iph->tos), tunnel->parms.link); + RT_TOS(iph->tos), tunnel->parms.link, + tunnel->fwmark); rt = ip_route_output_key(tunnel->net, &fl4); if (!IS_ERR(rt)) { @@ -578,7 +581,7 @@ void ip_md_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, u8 proto) tos = ipv6_get_dsfield((const struct ipv6hdr *)inner_iph); } init_tunnel_flow(&fl4, proto, key->u.ipv4.dst, key->u.ipv4.src, 0, - RT_TOS(tos), tunnel->parms.link); + RT_TOS(tos), tunnel->parms.link, tunnel->fwmark); if (tunnel->encap.type != TUNNEL_ENCAP_NONE) goto tx_error; rt = ip_route_output_key(tunnel->net, &fl4); @@ -707,7 +710,8 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, } init_tunnel_flow(&fl4, protocol, dst, tnl_params->saddr, - tunnel->parms.o_key, RT_TOS(tos), tunnel->parms.link); + tunnel->parms.o_key, RT_TOS(tos), tunnel->parms.link, + tunnel->fwmark); if (ip_tunnel_encap(skb, tunnel, &protocol, &fl4) < 0) goto tx_error; @@ -795,7 +799,8 @@ static void ip_tunnel_update(struct ip_tunnel_net *itn, struct ip_tunnel *t, struct net_device *dev, struct ip_tunnel_parm *p, - bool set_mtu) + bool set_mtu, + __u32 fwmark) { ip_tunnel_del(itn, t); t->parms.iph.saddr = p->iph.saddr; @@ -812,10 +817,11 @@ static void ip_tunnel_update(struct ip_tunnel_net *itn, t->parms.iph.tos = p->iph.tos; t->parms.iph.frag_off = p->iph.frag_off; - if (t->parms.link != p->link) { + if (t->parms.link != p->link || t->fwmark != fwmark) { int mtu; t->parms.link = p->link; + t->fwmark = fwmark; mtu = ip_tunnel_bind_dev(dev); if (set_mtu) dev->mtu = mtu; @@ -893,7 +899,7 @@ int ip_tunnel_ioctl(struct net_device *dev, struct ip_tunnel_parm *p, int cmd) if (t) { err = 0; - ip_tunnel_update(itn, t, dev, p, true); + ip_tunnel_update(itn, t, dev, p, true, 0); } else { err = -ENOENT; } @@ -1066,7 +1072,7 @@ void ip_tunnel_delete_net(struct ip_tunnel_net *itn, struct rtnl_link_ops *ops) EXPORT_SYMBOL_GPL(ip_tunnel_delete_net); int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[], - struct ip_tunnel_parm *p) + struct ip_tunnel_parm *p, __u32 fwmark) { struct ip_tunnel *nt; struct net *net = dev_net(dev); @@ -1087,6 +1093,7 @@ int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[], nt->net = net; nt->parms = *p; + nt->fwmark = fwmark; err = register_netdevice(dev); if (err) goto out; @@ -1105,7 +1112,7 @@ out: EXPORT_SYMBOL_GPL(ip_tunnel_newlink); int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[], - struct ip_tunnel_parm *p) + struct ip_tunnel_parm *p, __u32 fwmark) { struct ip_tunnel *t; struct ip_tunnel *tunnel = netdev_priv(dev); @@ -1137,7 +1144,7 @@ int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[], } } - ip_tunnel_update(itn, t, dev, p, !tb[IFLA_MTU]); + ip_tunnel_update(itn, t, dev, p, !tb[IFLA_MTU], fwmark); return 0; } EXPORT_SYMBOL_GPL(ip_tunnel_changelink); diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 8b14f1404c8f..40977413fd48 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -471,7 +471,8 @@ static int vti_tunnel_validate(struct nlattr *tb[], struct nlattr *data[]) } static void vti_netlink_parms(struct nlattr *data[], - struct ip_tunnel_parm *parms) + struct ip_tunnel_parm *parms, + __u32 *fwmark) { memset(parms, 0, sizeof(*parms)); @@ -497,24 +498,29 @@ static void vti_netlink_parms(struct nlattr *data[], if (data[IFLA_VTI_REMOTE]) parms->iph.daddr = nla_get_in_addr(data[IFLA_VTI_REMOTE]); + if (data[IFLA_VTI_FWMARK]) + *fwmark = nla_get_u32(data[IFLA_VTI_FWMARK]); } static int vti_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct ip_tunnel_parm parms; + __u32 fwmark = 0; - vti_netlink_parms(data, &parms); - return ip_tunnel_newlink(dev, tb, &parms); + vti_netlink_parms(data, &parms, &fwmark); + return ip_tunnel_newlink(dev, tb, &parms, fwmark); } static int vti_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { + struct ip_tunnel *t = netdev_priv(dev); + __u32 fwmark = t->fwmark; struct ip_tunnel_parm p; - vti_netlink_parms(data, &p); - return ip_tunnel_changelink(dev, tb, &p); + vti_netlink_parms(data, &p, &fwmark); + return ip_tunnel_changelink(dev, tb, &p, fwmark); } static size_t vti_get_size(const struct net_device *dev) @@ -530,6 +536,8 @@ static size_t vti_get_size(const struct net_device *dev) nla_total_size(4) + /* IFLA_VTI_REMOTE */ nla_total_size(4) + + /* IFLA_VTI_FWMARK */ + nla_total_size(4) + 0; } @@ -543,6 +551,7 @@ static int vti_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_be32(skb, IFLA_VTI_OKEY, p->o_key); nla_put_in_addr(skb, IFLA_VTI_LOCAL, p->iph.saddr); nla_put_in_addr(skb, IFLA_VTI_REMOTE, p->iph.daddr); + nla_put_u32(skb, IFLA_VTI_FWMARK, t->fwmark); return 0; } @@ -553,6 +562,7 @@ static const struct nla_policy vti_policy[IFLA_VTI_MAX + 1] = { [IFLA_VTI_OKEY] = { .type = NLA_U32 }, [IFLA_VTI_LOCAL] = { .len = FIELD_SIZEOF(struct iphdr, saddr) }, [IFLA_VTI_REMOTE] = { .len = FIELD_SIZEOF(struct iphdr, daddr) }, + [IFLA_VTI_FWMARK] = { .type = NLA_U32 }, }; static struct rtnl_link_ops vti_link_ops __read_mostly = { diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 00d4229b6954..1e441c6f2160 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -390,7 +390,8 @@ static int ipip_tunnel_validate(struct nlattr *tb[], struct nlattr *data[]) } static void ipip_netlink_parms(struct nlattr *data[], - struct ip_tunnel_parm *parms, bool *collect_md) + struct ip_tunnel_parm *parms, bool *collect_md, + __u32 *fwmark) { memset(parms, 0, sizeof(*parms)); @@ -428,6 +429,9 @@ static void ipip_netlink_parms(struct nlattr *data[], if (data[IFLA_IPTUN_COLLECT_METADATA]) *collect_md = true; + + if (data[IFLA_IPTUN_FWMARK]) + *fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]); } /* This function returns true when ENCAP attributes are present in the nl msg */ @@ -470,6 +474,7 @@ static int ipip_newlink(struct net *src_net, struct net_device *dev, struct ip_tunnel *t = netdev_priv(dev); struct ip_tunnel_parm p; struct ip_tunnel_encap ipencap; + __u32 fwmark = 0; if (ipip_netlink_encap_parms(data, &ipencap)) { int err = ip_tunnel_encap_setup(t, &ipencap); @@ -478,26 +483,27 @@ static int ipip_newlink(struct net *src_net, struct net_device *dev, return err; } - ipip_netlink_parms(data, &p, &t->collect_md); - return ip_tunnel_newlink(dev, tb, &p); + ipip_netlink_parms(data, &p, &t->collect_md, &fwmark); + return ip_tunnel_newlink(dev, tb, &p, fwmark); } static int ipip_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { + struct ip_tunnel *t = netdev_priv(dev); struct ip_tunnel_parm p; struct ip_tunnel_encap ipencap; bool collect_md; + __u32 fwmark = t->fwmark; if (ipip_netlink_encap_parms(data, &ipencap)) { - struct ip_tunnel *t = netdev_priv(dev); int err = ip_tunnel_encap_setup(t, &ipencap); if (err < 0) return err; } - ipip_netlink_parms(data, &p, &collect_md); + ipip_netlink_parms(data, &p, &collect_md, &fwmark); if (collect_md) return -EINVAL; @@ -505,7 +511,7 @@ static int ipip_changelink(struct net_device *dev, struct nlattr *tb[], (!(dev->flags & IFF_POINTOPOINT) && p.iph.daddr)) return -EINVAL; - return ip_tunnel_changelink(dev, tb, &p); + return ip_tunnel_changelink(dev, tb, &p, fwmark); } static size_t ipip_get_size(const struct net_device *dev) @@ -535,6 +541,8 @@ static size_t ipip_get_size(const struct net_device *dev) nla_total_size(2) + /* IFLA_IPTUN_COLLECT_METADATA */ nla_total_size(0) + + /* IFLA_IPTUN_FWMARK */ + nla_total_size(4) + 0; } @@ -550,7 +558,8 @@ static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u8(skb, IFLA_IPTUN_TOS, parm->iph.tos) || nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->iph.protocol) || nla_put_u8(skb, IFLA_IPTUN_PMTUDISC, - !!(parm->iph.frag_off & htons(IP_DF)))) + !!(parm->iph.frag_off & htons(IP_DF))) || + nla_put_u32(skb, IFLA_IPTUN_FWMARK, tunnel->fwmark)) goto nla_put_failure; if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE, @@ -585,6 +594,7 @@ static const struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_ENCAP_SPORT] = { .type = NLA_U16 }, [IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 }, [IFLA_IPTUN_COLLECT_METADATA] = { .type = NLA_FLAG }, + [IFLA_IPTUN_FWMARK] = { .type = NLA_U32 }, }; static struct rtnl_link_ops ipip_link_ops __read_mostly = { diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 99853c6e33a8..61e5902f0687 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -881,11 +881,12 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, goto tx_error; } - rt = ip_route_output_ports(tunnel->net, &fl4, NULL, - dst, tiph->saddr, - 0, 0, - IPPROTO_IPV6, RT_TOS(tos), - tunnel->parms.link); + flowi4_init_output(&fl4, tunnel->parms.link, tunnel->fwmark, + RT_TOS(tos), RT_SCOPE_UNIVERSE, IPPROTO_IPV6, + 0, dst, tiph->saddr, 0, 0, + sock_net_uid(tunnel->net, NULL)); + rt = ip_route_output_flow(tunnel->net, &fl4, NULL); + if (IS_ERR(rt)) { dev->stats.tx_carrier_errors++; goto tx_error_icmp; @@ -1071,7 +1072,8 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev) } } -static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p) +static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p, + __u32 fwmark) { struct net *net = t->net; struct sit_net *sitn = net_generic(net, sit_net_id); @@ -1085,8 +1087,9 @@ static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p) ipip6_tunnel_link(sitn, t); t->parms.iph.ttl = p->iph.ttl; t->parms.iph.tos = p->iph.tos; - if (t->parms.link != p->link) { + if (t->parms.link != p->link || t->fwmark != fwmark) { t->parms.link = p->link; + t->fwmark = fwmark; ipip6_tunnel_bind_dev(t->dev); } dst_cache_reset(&t->dst_cache); @@ -1220,7 +1223,7 @@ ipip6_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) t = netdev_priv(dev); } - ipip6_tunnel_update(t, &p); + ipip6_tunnel_update(t, &p, t->fwmark); } if (t) { @@ -1418,7 +1421,8 @@ static int ipip6_validate(struct nlattr *tb[], struct nlattr *data[]) } static void ipip6_netlink_parms(struct nlattr *data[], - struct ip_tunnel_parm *parms) + struct ip_tunnel_parm *parms, + __u32 *fwmark) { memset(parms, 0, sizeof(*parms)); @@ -1457,6 +1461,8 @@ static void ipip6_netlink_parms(struct nlattr *data[], if (data[IFLA_IPTUN_PROTO]) parms->iph.protocol = nla_get_u8(data[IFLA_IPTUN_PROTO]); + if (data[IFLA_IPTUN_FWMARK]) + *fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]); } /* This function returns true when ENCAP attributes are present in the nl msg */ @@ -1549,7 +1555,7 @@ static int ipip6_newlink(struct net *src_net, struct net_device *dev, return err; } - ipip6_netlink_parms(data, &nt->parms); + ipip6_netlink_parms(data, &nt->parms, &nt->fwmark); if (ipip6_tunnel_locate(net, &nt->parms, 0)) return -EEXIST; @@ -1577,6 +1583,7 @@ static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[], #ifdef CONFIG_IPV6_SIT_6RD struct ip_tunnel_6rd ip6rd; #endif + __u32 fwmark = t->fwmark; int err; if (dev == sitn->fb_tunnel_dev) @@ -1588,7 +1595,7 @@ static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[], return err; } - ipip6_netlink_parms(data, &p); + ipip6_netlink_parms(data, &p, &fwmark); if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) || (!(dev->flags & IFF_POINTOPOINT) && p.iph.daddr)) @@ -1602,7 +1609,7 @@ static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[], } else t = netdev_priv(dev); - ipip6_tunnel_update(t, &p); + ipip6_tunnel_update(t, &p, fwmark); #ifdef CONFIG_IPV6_SIT_6RD if (ipip6_netlink_6rd_parms(data, &ip6rd)) @@ -1649,6 +1656,8 @@ static size_t ipip6_get_size(const struct net_device *dev) nla_total_size(2) + /* IFLA_IPTUN_ENCAP_DPORT */ nla_total_size(2) + + /* IFLA_IPTUN_FWMARK */ + nla_total_size(4) + 0; } @@ -1665,7 +1674,8 @@ static int ipip6_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u8(skb, IFLA_IPTUN_PMTUDISC, !!(parm->iph.frag_off & htons(IP_DF))) || nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->iph.protocol) || - nla_put_be16(skb, IFLA_IPTUN_FLAGS, parm->i_flags)) + nla_put_be16(skb, IFLA_IPTUN_FLAGS, parm->i_flags) || + nla_put_u32(skb, IFLA_IPTUN_FWMARK, tunnel->fwmark)) goto nla_put_failure; #ifdef CONFIG_IPV6_SIT_6RD @@ -1715,6 +1725,7 @@ static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_ENCAP_FLAGS] = { .type = NLA_U16 }, [IFLA_IPTUN_ENCAP_SPORT] = { .type = NLA_U16 }, [IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 }, + [IFLA_IPTUN_FWMARK] = { .type = NLA_U32 }, }; static void ipip6_dellink(struct net_device *dev, struct list_head *head) -- cgit From 7acf8a1e8a28b3d7407a8d8061a7d0766cfac2f4 Mon Sep 17 00:00:00 2001 From: Matthew Whitehead Date: Wed, 19 Apr 2017 12:37:10 -0400 Subject: Replace 2 jiffies with sysctl netdev_budget_usecs to enable softirq tuning Constants used for tuning are generally a bad idea, especially as hardware changes over time. Replace the constant 2 jiffies with sysctl variable netdev_budget_usecs to enable sysadmins to tune the softirq processing. Also document the variable. For example, a very fast machine might tune this to 1000 microseconds, while my regression testing 486DX-25 needs it to be 4000 microseconds on a nearly idle network to prevent time_squeeze from being incremented. Version 2: changed jiffies to microseconds for predictable units. Signed-off-by: Matthew Whitehead Signed-off-by: David S. Miller --- Documentation/sysctl/net.txt | 11 ++++++++++- include/linux/netdevice.h | 1 + include/uapi/linux/sysctl.h | 1 + kernel/sysctl_binary.c | 1 + net/core/dev.c | 4 +++- net/core/sysctl_net_core.c | 8 ++++++++ 6 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Documentation/sysctl/net.txt b/Documentation/sysctl/net.txt index 2ebabc93014a..14db18c970b1 100644 --- a/Documentation/sysctl/net.txt +++ b/Documentation/sysctl/net.txt @@ -188,7 +188,16 @@ netdev_budget Maximum number of packets taken from all interfaces in one polling cycle (NAPI poll). In one polling cycle interfaces which are registered to polling are -probed in a round-robin manner. +probed in a round-robin manner. Also, a polling cycle may not exceed +netdev_budget_usecs microseconds, even if netdev_budget has not been +exhausted. + +netdev_budget_usecs +--------------------- + +Maximum number of microseconds in one NAPI polling cycle. Polling +will exit when either netdev_budget_usecs have elapsed during the +poll cycle or the number of packets processed reaches netdev_budget. netdev_max_backlog ------------------ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0f3c38ce5417..c49cf21f2b31 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3296,6 +3296,7 @@ static __always_inline int ____dev_forward_skb(struct net_device *dev, void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev); extern int netdev_budget; +extern unsigned int netdev_budget_usecs; /* Called by rtnetlink.c:rtnl_unlock() */ void netdev_run_todo(void); diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h index e13d48058b8d..177f5f139b36 100644 --- a/include/uapi/linux/sysctl.h +++ b/include/uapi/linux/sysctl.h @@ -274,6 +274,7 @@ enum NET_CORE_AEVENT_ETIME=20, NET_CORE_AEVENT_RSEQTH=21, NET_CORE_WARNINGS=22, + NET_CORE_BUDGET_USECS=23, }; /* /proc/sys/net/ethernet */ diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c index ece4b177052b..4ee3e49530d2 100644 --- a/kernel/sysctl_binary.c +++ b/kernel/sysctl_binary.c @@ -197,6 +197,7 @@ static const struct bin_table bin_net_core_table[] = { { CTL_INT, NET_CORE_AEVENT_ETIME, "xfrm_aevent_etime" }, { CTL_INT, NET_CORE_AEVENT_RSEQTH, "xfrm_aevent_rseqth" }, { CTL_INT, NET_CORE_WARNINGS, "warnings" }, + { CTL_INT, NET_CORE_BUDGET_USECS, "netdev_budget_usecs" }, {}, }; diff --git a/net/core/dev.c b/net/core/dev.c index 5d33e2baab2b..1c53c055b197 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3441,6 +3441,7 @@ EXPORT_SYMBOL(netdev_max_backlog); int netdev_tstamp_prequeue __read_mostly = 1; int netdev_budget __read_mostly = 300; +unsigned int __read_mostly netdev_budget_usecs = 2000; int weight_p __read_mostly = 64; /* old backlog weight */ int dev_weight_rx_bias __read_mostly = 1; /* bias for backlog weight */ int dev_weight_tx_bias __read_mostly = 1; /* bias for output_queue quota */ @@ -5307,7 +5308,8 @@ out_unlock: static __latent_entropy void net_rx_action(struct softirq_action *h) { struct softnet_data *sd = this_cpu_ptr(&softnet_data); - unsigned long time_limit = jiffies + 2; + unsigned long time_limit = jiffies + + usecs_to_jiffies(netdev_budget_usecs); int budget = netdev_budget; LIST_HEAD(list); LIST_HEAD(repoll); diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 7f9cc400eca0..ea23254b2457 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -452,6 +452,14 @@ static struct ctl_table net_core_table[] = { .extra1 = &one, .extra2 = &max_skb_frags, }, + { + .procname = "netdev_budget_usecs", + .data = &netdev_budget_usecs, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + }, { } }; -- cgit From ca1cb28da00448fdbddc99ee6d410f461bb619b5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 19 Apr 2017 19:30:59 +0200 Subject: liquidio: remove unnecessary variable assignment gcc points out an useless assignment that was added during code refactoring: drivers/net/ethernet/cavium/liquidio/lio_ethtool.c: In function 'octnet_intrmod_callback': drivers/net/ethernet/cavium/liquidio/lio_ethtool.c:1315:59: error: parameter 'oct_dev' set but not used [-Werror=unused-but-set-parameter] This is harmless but can clearly be remove to avoid the warning. Fixes: 50c0add534d2 ("liquidio: refactor interrupt moderation code") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index dab10c7e4443..579dc7336f58 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -1323,8 +1323,6 @@ static void octnet_intrmod_callback(struct octeon_device *oct_dev, ctx->status = status; - oct_dev = lio_get_device(ctx->octeon_id); - WRITE_ONCE(ctx->cond, 1); /* This barrier is required to be sure that the response has been -- cgit From ffa738555b917c5b78f2dc1aa4f29078c605bb94 Mon Sep 17 00:00:00 2001 From: Thomas Falcon Date: Wed, 19 Apr 2017 13:44:29 -0400 Subject: ibmvnic: Report errors when failing to release sub-crqs Add reporting of errors when releasing sub-crqs fails. Signed-off-by: Thomas Falcon Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 7ba43cfadf3a..7bf35071a9a3 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1309,6 +1309,12 @@ static void release_sub_crq_queue(struct ibmvnic_adapter *adapter, scrq->crq_num); } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); + if (rc) { + netdev_err(adapter->netdev, + "Failed to release sub-CRQ %16lx, rc = %ld\n", + scrq->crq_num, rc); + } + dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE, DMA_BIDIRECTIONAL); free_pages((unsigned long)scrq->msgs, 2); -- cgit From 993a82b0ff03b356c0001561ca7035b02c5e7bae Mon Sep 17 00:00:00 2001 From: Murilo Fossa Vicentini Date: Wed, 19 Apr 2017 13:44:35 -0400 Subject: ibmvnic: Fix ibmvnic_change_mac_addr struct format The ibmvnic_change_mac_addr struct alignment was not matching the defined format in PAPR+, it had the reserved and return code fields swapped. As a consequence, the CHANGE_MAC_ADDR_RSP commands were being improperly handled and executed even when the operation wasn't successfully completed by the system firmware. Also changing the endianness of the debug message to make it easier to parse the CRQ content. Signed-off-by: Murilo Fossa Vicentini Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 5 +++-- drivers/net/ethernet/ibm/ibmvnic.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 7bf35071a9a3..625896de25f7 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -2890,11 +2890,12 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq, struct ibmvnic_generic_crq *gen_crq = &crq->generic; struct net_device *netdev = adapter->netdev; struct device *dev = &adapter->vdev->dev; + u64 *u64_crq = (u64 *)crq; long rc; netdev_dbg(netdev, "Handling CRQ: %016lx %016lx\n", - ((unsigned long int *)crq)[0], - ((unsigned long int *)crq)[1]); + (unsigned long int)cpu_to_be64(u64_crq[0]), + (unsigned long int)cpu_to_be64(u64_crq[1])); switch (gen_crq->first) { case IBMVNIC_CRQ_INIT_RSP: switch (gen_crq->cmd) { diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index b0d0b890d033..1b404cae3bc1 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -518,8 +518,8 @@ struct ibmvnic_change_mac_addr { u8 first; u8 cmd; u8 mac_addr[6]; - struct ibmvnic_rc rc; u8 reserved[4]; + struct ibmvnic_rc rc; } __packed __aligned(8); struct ibmvnic_multicast_ctrl { -- cgit From 59af56c25bc34d4e98820c694d48fd5145fb6e4f Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 19 Apr 2017 13:44:41 -0400 Subject: ibmvnic: Unmap longer term buffer before free Make sure we unregister long term buffers from the adapter prior to DMA unmapping it and freeing the buffer. Failure to do so could result in a DMA to a now invalid address. Signed-off-by: Brian King Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 625896de25f7..c10bae7823e0 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -193,9 +193,9 @@ static void free_long_term_buff(struct ibmvnic_adapter *adapter, if (!ltb->buff) return; - dma_free_coherent(dev, ltb->size, ltb->buff, ltb->addr); if (!adapter->failover) send_request_unmap(adapter, ltb->map_id); + dma_free_coherent(dev, ltb->size, ltb->buff, ltb->addr); } static void replenish_rx_pool(struct ibmvnic_adapter *adapter, -- cgit From 58c8c0c096611f6a1d43ed24e320494bf894a66e Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 19 Apr 2017 13:44:47 -0400 Subject: ibmvnic: Fixup atomic API usage Replace a couple of modifications of an atomic followed by a read of the atomic, which is no longer atomic, to use atomic_XX_return variants to avoid race conditions. Signed-off-by: Brian King Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index c10bae7823e0..b5871dfff1b7 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -962,9 +962,8 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) goto out; } - atomic_inc(&tx_scrq->used); - - if (atomic_read(&tx_scrq->used) >= adapter->req_tx_entries_per_subcrq) { + if (atomic_inc_return(&tx_scrq->used) + >= adapter->req_tx_entries_per_subcrq) { netdev_info(netdev, "Stopping queue %d\n", queue_num); netif_stop_subqueue(netdev, queue_num); } @@ -1499,9 +1498,8 @@ restart_loop: } if (txbuff->last_frag) { - atomic_dec(&scrq->used); - - if (atomic_read(&scrq->used) <= + if (atomic_sub_return(next->tx_comp.num_comps, + &scrq->used) <= (adapter->req_tx_entries_per_subcrq / 2) && netif_subqueue_stopped(adapter->netdev, txbuff->skb)) { -- cgit From ed7ecbf700616d83ac5eea060638a2471c37e465 Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 19 Apr 2017 13:44:53 -0400 Subject: ibmvnic: Do not disable IRQ after scheduling tasklet Since the primary CRQ is only used for service functions and not in the performance path, simplify the code a bit and avoid disabling the IRQ. Signed-off-by: Brian King Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index b5871dfff1b7..27d7d2767204 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -3027,12 +3027,8 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq, static irqreturn_t ibmvnic_interrupt(int irq, void *instance) { struct ibmvnic_adapter *adapter = instance; - unsigned long flags; - spin_lock_irqsave(&adapter->crq.lock, flags); - vio_disable_interrupts(adapter->vdev); tasklet_schedule(&adapter->tasklet); - spin_unlock_irqrestore(&adapter->crq.lock, flags); return IRQ_HANDLED; } @@ -3040,32 +3036,23 @@ static void ibmvnic_tasklet(void *data) { struct ibmvnic_adapter *adapter = data; struct ibmvnic_crq_queue *queue = &adapter->crq; - struct vio_dev *vdev = adapter->vdev; union ibmvnic_crq *crq; unsigned long flags; bool done = false; spin_lock_irqsave(&queue->lock, flags); - vio_disable_interrupts(vdev); while (!done) { /* Pull all the valid messages off the CRQ */ while ((crq = ibmvnic_next_crq(adapter)) != NULL) { ibmvnic_handle_crq(crq, adapter); crq->generic.first = 0; } - vio_enable_interrupts(vdev); - crq = ibmvnic_next_crq(adapter); - if (crq) { - vio_disable_interrupts(vdev); - ibmvnic_handle_crq(crq, adapter); - crq->generic.first = 0; - } else { - /* remain in tasklet until all - * capabilities responses are received - */ - if (!adapter->wait_capability) - done = true; - } + + /* remain in tasklet until all + * capabilities responses are received + */ + if (!adapter->wait_capability) + done = true; } /* if capabilities CRQ's were sent in this tasklet, the following * tasklet must wait until all responses are received -- cgit From 661a26227621b9a602a816fa29451e53c5ba006b Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Wed, 19 Apr 2017 13:44:58 -0400 Subject: ibmvnic: Remove inflight list The inflight list used to track memory that is allocated for crq that are inflight is not needed. The one piece of the inflight list that does need to be cleaned at module exit is the error buffer list which is already attached to the adapter struct. This patch removes the inflight list and moves checking the error buffer list to ibmvnic_remove. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 98 ++++++++------------------------------ drivers/net/ethernet/ibm/ibmvnic.h | 9 ---- 2 files changed, 19 insertions(+), 88 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 27d7d2767204..18673e2fe91b 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -546,6 +546,23 @@ static int init_bounce_buffer(struct net_device *netdev) return 0; } +static void release_error_buffers(struct ibmvnic_adapter *adapter) +{ + struct device *dev = &adapter->vdev->dev; + struct ibmvnic_error_buff *error_buff, *tmp; + unsigned long flags; + + spin_lock_irqsave(&adapter->error_list_lock, flags); + list_for_each_entry_safe(error_buff, tmp, &adapter->errors, list) { + list_del(&error_buff->list); + dma_unmap_single(dev, error_buff->dma, error_buff->len, + DMA_FROM_DEVICE); + kfree(error_buff->buff); + kfree(error_buff); + } + spin_unlock_irqrestore(&adapter->error_list_lock, flags); +} + static int ibmvnic_login(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); @@ -588,6 +605,7 @@ static void release_resources(struct ibmvnic_adapter *adapter) release_crq_queue(adapter); release_stats_token(adapter); + release_error_buffers(adapter); } static int ibmvnic_open(struct net_device *netdev) @@ -1957,13 +1975,11 @@ static void send_login(struct ibmvnic_adapter *adapter) { struct ibmvnic_login_rsp_buffer *login_rsp_buffer; struct ibmvnic_login_buffer *login_buffer; - struct ibmvnic_inflight_cmd *inflight_cmd; struct device *dev = &adapter->vdev->dev; dma_addr_t rsp_buffer_token; dma_addr_t buffer_token; size_t rsp_buffer_size; union ibmvnic_crq crq; - unsigned long flags; size_t buffer_size; __be64 *tx_list_p; __be64 *rx_list_p; @@ -2000,11 +2016,7 @@ static void send_login(struct ibmvnic_adapter *adapter) dev_err(dev, "Couldn't map login rsp buffer\n"); goto buf_rsp_map_failed; } - inflight_cmd = kmalloc(sizeof(*inflight_cmd), GFP_ATOMIC); - if (!inflight_cmd) { - dev_err(dev, "Couldn't allocate inflight_cmd\n"); - goto inflight_alloc_failed; - } + adapter->login_buf = login_buffer; adapter->login_buf_token = buffer_token; adapter->login_buf_sz = buffer_size; @@ -2055,20 +2067,10 @@ static void send_login(struct ibmvnic_adapter *adapter) crq.login.cmd = LOGIN; crq.login.ioba = cpu_to_be32(buffer_token); crq.login.len = cpu_to_be32(buffer_size); - - memcpy(&inflight_cmd->crq, &crq, sizeof(crq)); - - spin_lock_irqsave(&adapter->inflight_lock, flags); - list_add_tail(&inflight_cmd->list, &adapter->inflight); - spin_unlock_irqrestore(&adapter->inflight_lock, flags); - ibmvnic_send_crq(adapter, &crq); return; -inflight_alloc_failed: - dma_unmap_single(dev, rsp_buffer_token, rsp_buffer_size, - DMA_FROM_DEVICE); buf_rsp_map_failed: kfree(login_rsp_buffer); buf_rsp_alloc_failed: @@ -2374,7 +2376,6 @@ static void handle_error_indication(union ibmvnic_crq *crq, struct ibmvnic_adapter *adapter) { int detail_len = be32_to_cpu(crq->error_indication.detail_error_sz); - struct ibmvnic_inflight_cmd *inflight_cmd; struct device *dev = &adapter->vdev->dev; struct ibmvnic_error_buff *error_buff; union ibmvnic_crq new_crq; @@ -2406,15 +2407,6 @@ static void handle_error_indication(union ibmvnic_crq *crq, return; } - inflight_cmd = kmalloc(sizeof(*inflight_cmd), GFP_ATOMIC); - if (!inflight_cmd) { - dma_unmap_single(dev, error_buff->dma, detail_len, - DMA_FROM_DEVICE); - kfree(error_buff->buff); - kfree(error_buff); - return; - } - error_buff->len = detail_len; error_buff->error_id = crq->error_indication.error_id; @@ -2428,13 +2420,6 @@ static void handle_error_indication(union ibmvnic_crq *crq, new_crq.request_error_info.ioba = cpu_to_be32(error_buff->dma); new_crq.request_error_info.len = cpu_to_be32(detail_len); new_crq.request_error_info.error_id = crq->error_indication.error_id; - - memcpy(&inflight_cmd->crq, &crq, sizeof(crq)); - - spin_lock_irqsave(&adapter->inflight_lock, flags); - list_add_tail(&inflight_cmd->list, &adapter->inflight); - spin_unlock_irqrestore(&adapter->inflight_lock, flags); - ibmvnic_send_crq(adapter, &new_crq); } @@ -2819,48 +2804,6 @@ out: } } -static void ibmvnic_free_inflight(struct ibmvnic_adapter *adapter) -{ - struct ibmvnic_inflight_cmd *inflight_cmd, *tmp1; - struct device *dev = &adapter->vdev->dev; - struct ibmvnic_error_buff *error_buff, *tmp2; - unsigned long flags; - unsigned long flags2; - - spin_lock_irqsave(&adapter->inflight_lock, flags); - list_for_each_entry_safe(inflight_cmd, tmp1, &adapter->inflight, list) { - switch (inflight_cmd->crq.generic.cmd) { - case LOGIN: - dma_unmap_single(dev, adapter->login_buf_token, - adapter->login_buf_sz, - DMA_BIDIRECTIONAL); - dma_unmap_single(dev, adapter->login_rsp_buf_token, - adapter->login_rsp_buf_sz, - DMA_BIDIRECTIONAL); - kfree(adapter->login_rsp_buf); - kfree(adapter->login_buf); - break; - case REQUEST_ERROR_INFO: - spin_lock_irqsave(&adapter->error_list_lock, flags2); - list_for_each_entry_safe(error_buff, tmp2, - &adapter->errors, list) { - dma_unmap_single(dev, error_buff->dma, - error_buff->len, - DMA_FROM_DEVICE); - kfree(error_buff->buff); - list_del(&error_buff->list); - kfree(error_buff); - } - spin_unlock_irqrestore(&adapter->error_list_lock, - flags2); - break; - } - list_del(&inflight_cmd->list); - kfree(inflight_cmd); - } - spin_unlock_irqrestore(&adapter->inflight_lock, flags); -} - static void ibmvnic_xport_event(struct work_struct *work) { struct ibmvnic_adapter *adapter = container_of(work, @@ -2869,7 +2812,6 @@ static void ibmvnic_xport_event(struct work_struct *work) struct device *dev = &adapter->vdev->dev; long rc; - ibmvnic_free_inflight(adapter); release_sub_crqs(adapter); if (adapter->migrated) { rc = ibmvnic_reenable_crq_queue(adapter); @@ -3333,9 +3275,7 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) spin_lock_init(&adapter->stats_lock); INIT_LIST_HEAD(&adapter->errors); - INIT_LIST_HEAD(&adapter->inflight); spin_lock_init(&adapter->error_list_lock); - spin_lock_init(&adapter->inflight_lock); rc = ibmvnic_init(adapter); if (rc) { diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 1b404cae3bc1..8fbe05d2710a 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -913,11 +913,6 @@ struct ibmvnic_error_buff { __be32 error_id; }; -struct ibmvnic_inflight_cmd { - union ibmvnic_crq crq; - struct list_head list; -}; - struct ibmvnic_adapter { struct vio_dev *vdev; struct net_device *netdev; @@ -978,10 +973,6 @@ struct ibmvnic_adapter { struct completion fw_done; - /* in-flight commands that allocate and/or map memory*/ - struct list_head inflight; - spinlock_t inflight_lock; - /* partner capabilities */ u64 min_tx_queues; u64 min_rx_queues; -- cgit From 3748905599cbbb37bf90f818109151809de5241c Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Wed, 19 Apr 2017 13:45:04 -0400 Subject: ibmvnic: Correct crq and resource releasing We should not be releasing the crq's when calling close for the adapter, these need to remain open to facilitate operations such as updating the mac address. The crq's should be released in the adpaters remove routine. Additionally, we need to call release_reources from remove. This corrects the scenario of trying to remove an adapter that has only been probed. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 18673e2fe91b..a8b3c5741258 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -601,9 +601,6 @@ static void release_resources(struct ibmvnic_adapter *adapter) release_tx_pools(adapter); release_rx_pools(adapter); - release_sub_crqs(adapter); - release_crq_queue(adapter); - release_stats_token(adapter); release_error_buffers(adapter); } @@ -3300,8 +3297,14 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) static int ibmvnic_remove(struct vio_dev *dev) { struct net_device *netdev = dev_get_drvdata(&dev->dev); + struct ibmvnic_adapter *adapter = netdev_priv(netdev); unregister_netdev(netdev); + + release_resources(adapter); + release_sub_crqs(adapter); + release_crq_queue(adapter); + free_netdev(netdev); dev_set_drvdata(&dev->dev, NULL); -- cgit From dd9c20fa07ba5cfb5a0ab3181d68530506610605 Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 19 Apr 2017 13:45:10 -0400 Subject: ibmvnic: Disable irq prior to close Add some code to call disable_irq on all the vnic interface's irqs. This fixes a crash observed when closing an active interface, as seen in the oops below when we try to access a buffer in the interrupt handler which we've already freed. Unable to handle kernel paging request for data at address 0x00000001 Faulting instruction address: 0xd000000003886824 Oops: Kernel access of bad area, sig: 11 [#1] SMP NR_CPUS=2048 NUMA pSeries Modules linked in: ibmvnic(OEN) rpadlpar_io(X) rpaphp(X) tcp_diag udp_diag inet_diag unix_diag af_packet_diag netlink_diag rpcsec_ Supported: No, Unsupported modules are loaded CPU: 8 PID: 0 Comm: swapper/8 Tainted: G OE NX 4.4.49-92.11-default #1 task: c00000007f990110 ti: c0000000fffa0000 task.ti: c00000007f9b8000 NIP: d000000003886824 LR: d000000003886824 CTR: c0000000007eff60 REGS: c0000000fffa3a70 TRAP: 0300 Tainted: G OE NX (4.4.49-92.11-default) MSR: 8000000000009033 CR: 22008042 XER: 20000008 CFAR: c000000000008468 DAR: 0000000000000001 DSISR: 40000000 SOFTE: 0 GPR00: d000000003886824 c0000000fffa3cf0 d000000003894118 0000000000000000 GPR04: 0000000000000000 0000000000000000 c000000001249da0 0000000000000000 GPR08: 000000000000000e 0000000000000000 c0000000ccb00000 d000000003889180 GPR12: c0000000007eff60 c000000007af4c00 0000000000000001 c0000000010def30 GPR16: c00000007f9b8000 c000000000b98c30 c00000007f9b8080 c000000000bab858 GPR20: 0000000000000005 0000000000000000 c0000000ff5d7e80 c0000000f809f648 GPR24: c0000000ff5d7ec8 0000000000000000 0000000000000000 c0000000ccb001a0 GPR28: 000000000000000a c0000000f809f600 c0000000fd4cd900 c0000000f9cd5b00 NIP [d000000003886824] ibmvnic_interrupt_tx+0x114/0x380 [ibmvnic] LR [d000000003886824] ibmvnic_interrupt_tx+0x114/0x380 [ibmvnic] Call Trace: [c0000000fffa3cf0] [d000000003886824] ibmvnic_interrupt_tx+0x114/0x380 [ibmvnic] (unreliable) [c0000000fffa3dd0] [c000000000132940] __handle_irq_event_percpu+0x90/0x2e0 [c0000000fffa3e90] [c000000000132bcc] handle_irq_event_percpu+0x3c/0x90 [c0000000fffa3ed0] [c000000000132c88] handle_irq_event+0x68/0xc0 [c0000000fffa3f00] [c000000000137edc] handle_fasteoi_irq+0xec/0x250 [c0000000fffa3f30] [c000000000131b04] generic_handle_irq+0x54/0x80 [c0000000fffa3f60] [c000000000011190] __do_irq+0x80/0x1d0 [c0000000fffa3f90] [c0000000000248d8] call_do_irq+0x14/0x24 [c00000007f9bb9e0] [c000000000011380] do_IRQ+0xa0/0x120 [c00000007f9bba40] [c000000000002594] hardware_interrupt_common+0x114/0x180 Signed-off-by: Brian King Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index a8b3c5741258..ce8b14748832 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -689,6 +689,23 @@ ibmvnic_open_fail: return -ENOMEM; } +static void disable_sub_crqs(struct ibmvnic_adapter *adapter) +{ + int i; + + if (adapter->tx_scrq) { + for (i = 0; i < adapter->req_tx_queues; i++) + if (adapter->tx_scrq[i]) + disable_irq(adapter->tx_scrq[i]->irq); + } + + if (adapter->rx_scrq) { + for (i = 0; i < adapter->req_rx_queues; i++) + if (adapter->rx_scrq[i]) + disable_irq(adapter->rx_scrq[i]->irq); + } +} + static int ibmvnic_close(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); @@ -696,6 +713,7 @@ static int ibmvnic_close(struct net_device *netdev) int i; adapter->closing = true; + disable_sub_crqs(adapter); for (i = 0; i < adapter->req_rx_queues; i++) napi_disable(&adapter->napi[i]); -- cgit From 7f7adc5060a787b15587062274d2dec4912f3588 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Wed, 19 Apr 2017 13:45:16 -0400 Subject: ibmvnic: Allocate zero-filled memory for sub crqs Update the allocation of memory for the sub crq structs and their associated pages to allocate zero-filled memory. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index ce8b14748832..221d65286329 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1360,12 +1360,12 @@ static struct ibmvnic_sub_crq_queue *init_sub_crq_queue(struct ibmvnic_adapter struct ibmvnic_sub_crq_queue *scrq; int rc; - scrq = kmalloc(sizeof(*scrq), GFP_ATOMIC); + scrq = kzalloc(sizeof(*scrq), GFP_ATOMIC); if (!scrq) return NULL; - scrq->msgs = (union sub_crq *)__get_free_pages(GFP_ATOMIC, 2); - memset(scrq->msgs, 0, 4 * PAGE_SIZE); + scrq->msgs = + (union sub_crq *)__get_free_pages(GFP_ATOMIC | __GFP_ZERO, 2); if (!scrq->msgs) { dev_warn(dev, "Couldn't allocate crq queue messages page\n"); goto zero_page_failed; @@ -1393,9 +1393,6 @@ static struct ibmvnic_sub_crq_queue *init_sub_crq_queue(struct ibmvnic_adapter scrq->adapter = adapter; scrq->size = 4 * PAGE_SIZE / sizeof(*scrq->msgs); - scrq->cur = 0; - atomic_set(&scrq->used, 0); - scrq->rx_skb_top = NULL; spin_lock_init(&scrq->lock); netdev_dbg(adapter->netdev, -- cgit From d76e0fec7e03ab29f0dd68a89364225c98bd5568 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Wed, 19 Apr 2017 13:45:22 -0400 Subject: ibmvnic: Remove unused bouce buffer The bounce buffer is not used in the ibmvnic driver, just get rid of it. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 53 -------------------------------------- drivers/net/ethernet/ibm/ibmvnic.h | 4 --- 2 files changed, 57 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 221d65286329..e8c72abfd7ac 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -502,50 +502,6 @@ static int init_tx_pools(struct net_device *netdev) return 0; } -static void release_bounce_buffer(struct ibmvnic_adapter *adapter) -{ - struct device *dev = &adapter->vdev->dev; - - if (!adapter->bounce_buffer) - return; - - if (!dma_mapping_error(dev, adapter->bounce_buffer_dma)) { - dma_unmap_single(dev, adapter->bounce_buffer_dma, - adapter->bounce_buffer_size, - DMA_BIDIRECTIONAL); - adapter->bounce_buffer_dma = DMA_ERROR_CODE; - } - - kfree(adapter->bounce_buffer); - adapter->bounce_buffer = NULL; -} - -static int init_bounce_buffer(struct net_device *netdev) -{ - struct ibmvnic_adapter *adapter = netdev_priv(netdev); - struct device *dev = &adapter->vdev->dev; - char *buf; - int buf_sz; - dma_addr_t map_addr; - - buf_sz = (netdev->mtu + ETH_HLEN - 1) / PAGE_SIZE + 1; - buf = kmalloc(adapter->bounce_buffer_size, GFP_KERNEL); - if (!buf) - return -1; - - map_addr = dma_map_single(dev, buf, buf_sz, DMA_TO_DEVICE); - if (dma_mapping_error(dev, map_addr)) { - dev_err(dev, "Couldn't map bounce buffer\n"); - kfree(buf); - return -1; - } - - adapter->bounce_buffer = buf; - adapter->bounce_buffer_size = buf_sz; - adapter->bounce_buffer_dma = map_addr; - return 0; -} - static void release_error_buffers(struct ibmvnic_adapter *adapter) { struct device *dev = &adapter->vdev->dev; @@ -597,7 +553,6 @@ static int ibmvnic_login(struct net_device *netdev) static void release_resources(struct ibmvnic_adapter *adapter) { - release_bounce_buffer(adapter); release_tx_pools(adapter); release_rx_pools(adapter); @@ -656,10 +611,6 @@ static int ibmvnic_open(struct net_device *netdev) if (rc) goto ibmvnic_open_fail; - rc = init_bounce_buffer(netdev); - if (rc) - goto ibmvnic_open_fail; - replenish_pools(adapter); /* We're ready to receive frames, enable the sub-crq interrupts and @@ -880,7 +831,6 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) unsigned int tx_bytes = 0; dma_addr_t data_dma_addr; struct netdev_queue *txq; - bool used_bounce = false; unsigned long lpar_rc; union sub_crq tx_crq; unsigned int offset; @@ -921,7 +871,6 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) tx_buff->index = index; tx_buff->pool_index = queue_num; tx_buff->last_frag = true; - tx_buff->used_bounce = used_bounce; memset(&tx_crq, 0, sizeof(tx_crq)); tx_crq.v1.first = IBMVNIC_CRQ_CMD; @@ -1517,7 +1466,6 @@ restart_loop: continue; txbuff->data_dma[j] = 0; - txbuff->used_bounce = false; } /* if sub_crq was sent indirectly */ first = txbuff->indir_arr[0].generic.first; @@ -3343,7 +3291,6 @@ static unsigned long ibmvnic_get_desired_dma(struct vio_dev *vdev) adapter = netdev_priv(netdev); ret += PAGE_SIZE; /* the crq message queue */ - ret += adapter->bounce_buffer_size; ret += IOMMU_PAGE_ALIGN(sizeof(struct ibmvnic_statistics), tbl); for (i = 0; i < adapter->req_tx_queues + adapter->req_rx_queues; i++) diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 8fbe05d2710a..355225cf6d30 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -868,7 +868,6 @@ struct ibmvnic_tx_buff { int index; int pool_index; bool last_frag; - bool used_bounce; union sub_crq indir_arr[6]; u8 hdr_data[140]; dma_addr_t indir_dma; @@ -924,9 +923,6 @@ struct ibmvnic_adapter { dma_addr_t ip_offload_ctrl_tok; bool migrated; u32 msg_enable; - void *bounce_buffer; - int bounce_buffer_size; - dma_addr_t bounce_buffer_dma; /* Statistics */ struct ibmvnic_statistics stats; -- cgit From 73e64fa4f417b22d8d5521999a631ced8e2d442e Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Wed, 19 Apr 2017 13:53:49 -0700 Subject: netvsc: Deal with rescinded channels correctly We will not be able to send packets over a channel that has been rescinded. Make necessary adjustments so we can properly cleanup even when the channel is rescinded. This issue can be trigerred in the NIC hot-remove path. Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index fd21d5aab580..967843ba03fa 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -135,6 +135,13 @@ static void netvsc_destroy_buf(struct hv_device *device) sizeof(struct nvsp_message), (unsigned long)revoke_packet, VM_PKT_DATA_INBAND, 0); + /* If the failure is because the channel is rescinded; + * ignore the failure since we cannot send on a rescinded + * channel. This would allow us to properly cleanup + * even when the channel is rescinded. + */ + if (device->channel->rescind) + ret = 0; /* * If we failed here, we might as well return and * have a leak rather than continue and a bugchk @@ -195,6 +202,15 @@ static void netvsc_destroy_buf(struct hv_device *device) sizeof(struct nvsp_message), (unsigned long)revoke_packet, VM_PKT_DATA_INBAND, 0); + + /* If the failure is because the channel is rescinded; + * ignore the failure since we cannot send on a rescinded + * channel. This would allow us to properly cleanup + * even when the channel is rescinded. + */ + if (device->channel->rescind) + ret = 0; + /* If we failed here, we might as well return and * have a leak rather than continue and a bugchk */ -- cgit From b1d9fc41aab11f9520b2e0d57ae872e2ec5d6f32 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 19 Apr 2017 23:01:17 +0200 Subject: bpf: add napi_id read access to __sk_buff Add napi_id access to __sk_buff for socket filter program types, tc program types and other bpf_convert_ctx_access() users. Having access to skb->napi_id is useful for per RX queue listener siloing, f.e. in combination with SO_ATTACH_REUSEPORT_EBPF and when busy polling is used, meaning SO_REUSEPORT enabled listeners can then select the corresponding socket at SYN time already [1]. The skb is marked via skb_mark_napi_id() early in the receive path (e.g., napi_gro_receive()). Currently, sockets can only use SO_INCOMING_NAPI_ID from 6d4339028b35 ("net: Introduce SO_INCOMING_NAPI_ID") as a socket option to look up the NAPI ID associated with the queue for steering, which requires a prior sk_mark_napi_id() after the socket was looked up. Semantics for the __sk_buff napi_id access are similar, meaning if skb->napi_id is < MIN_NAPI_ID (e.g. outgoing packets using sender_cpu), then an invalid napi_id of 0 is returned to the program, otherwise a valid non-zero napi_id. [1] http://netdevconf.org/2.1/slides/apr6/dumazet-BUSY-POLLING-Netdev-2.1.pdf Suggested-by: Eric Dumazet Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 1 + net/core/filter.c | 14 ++++++++++++++ tools/include/uapi/linux/bpf.h | 1 + tools/testing/selftests/bpf/test_verifier.c | 3 +++ 4 files changed, 19 insertions(+) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 1e062bb54eec..e553529929f6 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -603,6 +603,7 @@ struct __sk_buff { __u32 tc_classid; __u32 data; __u32 data_end; + __u32 napi_id; }; struct bpf_tunnel_key { diff --git a/net/core/filter.c b/net/core/filter.c index 085925834727..9a37860a80fc 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -53,6 +53,7 @@ #include #include #include +#include /** * sk_filter_trim_cap - run a packet through a socket filter @@ -3201,6 +3202,19 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, *insn++ = BPF_MOV64_REG(si->dst_reg, si->dst_reg); else *insn++ = BPF_MOV64_IMM(si->dst_reg, 0); +#endif + break; + + case offsetof(struct __sk_buff, napi_id): +#if defined(CONFIG_NET_RX_BUSY_POLL) + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, napi_id) != 4); + + *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg, + offsetof(struct sk_buff, napi_id)); + *insn++ = BPF_JMP_IMM(BPF_JGE, si->dst_reg, MIN_NAPI_ID, 1); + *insn++ = BPF_MOV64_IMM(si->dst_reg, 0); +#else + *insn++ = BPF_MOV64_IMM(si->dst_reg, 0); #endif break; } diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 1e062bb54eec..e553529929f6 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -603,6 +603,7 @@ struct __sk_buff { __u32 tc_classid; __u32 data; __u32 data_end; + __u32 napi_id; }; struct bpf_tunnel_key { diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 6178b65fee59..95a8d5f3ab80 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -772,6 +772,9 @@ static struct bpf_test tests[] = { BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, offsetof(struct __sk_buff, vlan_tci)), BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, napi_id)), + BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0), BPF_EXIT_INSN(), }, .result = ACCEPT, -- cgit From 763dbf6328e41de7a55851baf5ee49e367552531 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Wed, 19 Apr 2017 14:21:21 -0700 Subject: net_sched: move the empty tp check from ->destroy() to ->delete() We could have a race condition where in ->classify() path we dereference tp->root and meanwhile a parallel ->destroy() makes it a NULL. Daniel cured this bug in commit d936377414fa ("net, sched: respect rcu grace period on cls destruction"). This happens when ->destroy() is called for deleting a filter to check if we are the last one in tp, this tp is still linked and visible at that time. The root cause of this problem is the semantic of ->destroy(), it does two things (for non-force case): 1) check if tp is empty 2) if tp is empty we could really destroy it and its caller, if cares, needs to check its return value to see if it is really destroyed. Therefore we can't unlink tp unless we know it is empty. As suggested by Daniel, we could actually move the test logic to ->delete() so that we can safely unlink tp after ->delete() tells us the last one is just deleted and before ->destroy(). Fixes: 1e052be69d04 ("net_sched: destroy proto tp when all filters are gone") Cc: Roi Dayan Cc: Daniel Borkmann Cc: John Fastabend Cc: Jamal Hadi Salim Signed-off-by: Cong Wang Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/net/sch_generic.h | 4 +-- net/sched/cls_api.c | 27 +++++++++--------- net/sched/cls_basic.c | 10 +++---- net/sched/cls_bpf.c | 11 ++++---- net/sched/cls_cgroup.c | 8 ++---- net/sched/cls_flow.c | 10 +++---- net/sched/cls_flower.c | 10 ++----- net/sched/cls_fw.c | 29 +++++++++++-------- net/sched/cls_matchall.c | 7 ++--- net/sched/cls_route.c | 29 +++++++++---------- net/sched/cls_rsvp.h | 32 +++++++++++---------- net/sched/cls_tcindex.c | 14 +++++----- net/sched/cls_u32.c | 71 +++++++++++++++++++++++++++-------------------- 13 files changed, 134 insertions(+), 128 deletions(-) diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 65d502610314..22e52093bfda 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -204,14 +204,14 @@ struct tcf_proto_ops { const struct tcf_proto *, struct tcf_result *); int (*init)(struct tcf_proto*); - bool (*destroy)(struct tcf_proto*, bool); + void (*destroy)(struct tcf_proto*); unsigned long (*get)(struct tcf_proto*, u32 handle); int (*change)(struct net *net, struct sk_buff *, struct tcf_proto*, unsigned long, u32 handle, struct nlattr **, unsigned long *, bool); - int (*delete)(struct tcf_proto*, unsigned long); + int (*delete)(struct tcf_proto*, unsigned long, bool*); void (*walk)(struct tcf_proto*, struct tcf_walker *arg); /* rtnetlink specific */ diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index a8da383b681a..22f88b35a546 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -178,14 +178,11 @@ errout: return ERR_PTR(err); } -static bool tcf_proto_destroy(struct tcf_proto *tp, bool force) +static void tcf_proto_destroy(struct tcf_proto *tp) { - if (tp->ops->destroy(tp, force)) { - module_put(tp->ops->owner); - kfree_rcu(tp, rcu); - return true; - } - return false; + tp->ops->destroy(tp); + module_put(tp->ops->owner); + kfree_rcu(tp, rcu); } void tcf_destroy_chain(struct tcf_proto __rcu **fl) @@ -194,7 +191,7 @@ void tcf_destroy_chain(struct tcf_proto __rcu **fl) while ((tp = rtnl_dereference(*fl)) != NULL) { RCU_INIT_POINTER(*fl, tp->next); - tcf_proto_destroy(tp, true); + tcf_proto_destroy(tp); } } EXPORT_SYMBOL(tcf_destroy_chain); @@ -361,7 +358,7 @@ replay: RCU_INIT_POINTER(*back, next); tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER, false); - tcf_proto_destroy(tp, true); + tcf_proto_destroy(tp); err = 0; goto errout; } @@ -372,24 +369,28 @@ replay: goto errout; } } else { + bool last; + switch (n->nlmsg_type) { case RTM_NEWTFILTER: if (n->nlmsg_flags & NLM_F_EXCL) { if (tp_created) - tcf_proto_destroy(tp, true); + tcf_proto_destroy(tp); err = -EEXIST; goto errout; } break; case RTM_DELTFILTER: - err = tp->ops->delete(tp, fh); + err = tp->ops->delete(tp, fh, &last); if (err) goto errout; next = rtnl_dereference(tp->next); tfilter_notify(net, skb, n, tp, t->tcm_handle, RTM_DELTFILTER, false); - if (tcf_proto_destroy(tp, false)) + if (last) { RCU_INIT_POINTER(*back, next); + tcf_proto_destroy(tp); + } goto errout; case RTM_GETTFILTER: err = tfilter_notify(net, skb, n, tp, fh, @@ -411,7 +412,7 @@ replay: tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER, false); } else { if (tp_created) - tcf_proto_destroy(tp, true); + tcf_proto_destroy(tp); } errout: diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index 422414f16b38..c4fd63a068f9 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -93,30 +93,28 @@ static void basic_delete_filter(struct rcu_head *head) kfree(f); } -static bool basic_destroy(struct tcf_proto *tp, bool force) +static void basic_destroy(struct tcf_proto *tp) { struct basic_head *head = rtnl_dereference(tp->root); struct basic_filter *f, *n; - if (!force && !list_empty(&head->flist)) - return false; - list_for_each_entry_safe(f, n, &head->flist, link) { list_del_rcu(&f->link); tcf_unbind_filter(tp, &f->res); call_rcu(&f->rcu, basic_delete_filter); } kfree_rcu(head, rcu); - return true; } -static int basic_delete(struct tcf_proto *tp, unsigned long arg) +static int basic_delete(struct tcf_proto *tp, unsigned long arg, bool *last) { + struct basic_head *head = rtnl_dereference(tp->root); struct basic_filter *f = (struct basic_filter *) arg; list_del_rcu(&f->link); tcf_unbind_filter(tp, &f->res); call_rcu(&f->rcu, basic_delete_filter); + *last = list_empty(&head->flist); return 0; } diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 7ddd08efaa0f..5ebeae996e63 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -274,25 +274,24 @@ static void __cls_bpf_delete(struct tcf_proto *tp, struct cls_bpf_prog *prog) call_rcu(&prog->rcu, cls_bpf_delete_prog_rcu); } -static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg) +static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg, bool *last) { + struct cls_bpf_head *head = rtnl_dereference(tp->root); + __cls_bpf_delete(tp, (struct cls_bpf_prog *) arg); + *last = list_empty(&head->plist); return 0; } -static bool cls_bpf_destroy(struct tcf_proto *tp, bool force) +static void cls_bpf_destroy(struct tcf_proto *tp) { struct cls_bpf_head *head = rtnl_dereference(tp->root); struct cls_bpf_prog *prog, *tmp; - if (!force && !list_empty(&head->plist)) - return false; - list_for_each_entry_safe(prog, tmp, &head->plist, link) __cls_bpf_delete(tp, prog); kfree_rcu(head, rcu); - return true; } static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle) diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index b5e7c1bee6c3..12ce547eea04 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -131,20 +131,16 @@ errout: return err; } -static bool cls_cgroup_destroy(struct tcf_proto *tp, bool force) +static void cls_cgroup_destroy(struct tcf_proto *tp) { struct cls_cgroup_head *head = rtnl_dereference(tp->root); - if (!force) - return false; /* Head can still be NULL due to cls_cgroup_init(). */ if (head) call_rcu(&head->rcu, cls_cgroup_destroy_rcu); - - return true; } -static int cls_cgroup_delete(struct tcf_proto *tp, unsigned long arg) +static int cls_cgroup_delete(struct tcf_proto *tp, unsigned long arg, bool *last) { return -EOPNOTSUPP; } diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 008ba7e63b7a..3065752b9cda 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -562,12 +562,14 @@ err1: return err; } -static int flow_delete(struct tcf_proto *tp, unsigned long arg) +static int flow_delete(struct tcf_proto *tp, unsigned long arg, bool *last) { + struct flow_head *head = rtnl_dereference(tp->root); struct flow_filter *f = (struct flow_filter *)arg; list_del_rcu(&f->list); call_rcu(&f->rcu, flow_destroy_filter); + *last = list_empty(&head->filters); return 0; } @@ -583,20 +585,16 @@ static int flow_init(struct tcf_proto *tp) return 0; } -static bool flow_destroy(struct tcf_proto *tp, bool force) +static void flow_destroy(struct tcf_proto *tp) { struct flow_head *head = rtnl_dereference(tp->root); struct flow_filter *f, *next; - if (!force && !list_empty(&head->filters)) - return false; - list_for_each_entry_safe(f, next, &head->filters, list) { list_del_rcu(&f->list); call_rcu(&f->rcu, flow_destroy_filter); } kfree_rcu(head, rcu); - return true; } static unsigned long flow_get(struct tcf_proto *tp, u32 handle) diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 3e7bd7801aa8..31ee3404aeb4 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -328,21 +328,16 @@ static void fl_destroy_rcu(struct rcu_head *rcu) schedule_work(&head->work); } -static bool fl_destroy(struct tcf_proto *tp, bool force) +static void fl_destroy(struct tcf_proto *tp) { struct cls_fl_head *head = rtnl_dereference(tp->root); struct cls_fl_filter *f, *next; - if (!force && !list_empty(&head->filters)) - return false; - list_for_each_entry_safe(f, next, &head->filters, list) __fl_delete(tp, f); __module_get(THIS_MODULE); call_rcu(&head->rcu, fl_destroy_rcu); - - return true; } static unsigned long fl_get(struct tcf_proto *tp, u32 handle) @@ -947,7 +942,7 @@ errout_tb: return err; } -static int fl_delete(struct tcf_proto *tp, unsigned long arg) +static int fl_delete(struct tcf_proto *tp, unsigned long arg, bool *last) { struct cls_fl_head *head = rtnl_dereference(tp->root); struct cls_fl_filter *f = (struct cls_fl_filter *) arg; @@ -956,6 +951,7 @@ static int fl_delete(struct tcf_proto *tp, unsigned long arg) rhashtable_remove_fast(&head->ht, &f->ht_node, head->ht_params); __fl_delete(tp, f); + *last = list_empty(&head->filters); return 0; } diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 996209083c6b..78ccb4a85a4d 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -127,20 +127,14 @@ static void fw_delete_filter(struct rcu_head *head) kfree(f); } -static bool fw_destroy(struct tcf_proto *tp, bool force) +static void fw_destroy(struct tcf_proto *tp) { struct fw_head *head = rtnl_dereference(tp->root); struct fw_filter *f; int h; if (head == NULL) - return true; - - if (!force) { - for (h = 0; h < HTSIZE; h++) - if (rcu_access_pointer(head->ht[h])) - return false; - } + return; for (h = 0; h < HTSIZE; h++) { while ((f = rtnl_dereference(head->ht[h])) != NULL) { @@ -152,15 +146,16 @@ static bool fw_destroy(struct tcf_proto *tp, bool force) } RCU_INIT_POINTER(tp->root, NULL); kfree_rcu(head, rcu); - return true; } -static int fw_delete(struct tcf_proto *tp, unsigned long arg) +static int fw_delete(struct tcf_proto *tp, unsigned long arg, bool *last) { struct fw_head *head = rtnl_dereference(tp->root); struct fw_filter *f = (struct fw_filter *)arg; struct fw_filter __rcu **fp; struct fw_filter *pfp; + int ret = -EINVAL; + int h; if (head == NULL || f == NULL) goto out; @@ -173,11 +168,21 @@ static int fw_delete(struct tcf_proto *tp, unsigned long arg) RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); tcf_unbind_filter(tp, &f->res); call_rcu(&f->rcu, fw_delete_filter); - return 0; + ret = 0; + break; } } + + *last = true; + for (h = 0; h < HTSIZE; h++) { + if (rcu_access_pointer(head->ht[h])) { + *last = false; + break; + } + } + out: - return -EINVAL; + return ret; } static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = { diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index 0dbcca62aa6a..2efb36c08f2a 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -90,19 +90,18 @@ static void mall_destroy_hw_filter(struct tcf_proto *tp, &offload); } -static bool mall_destroy(struct tcf_proto *tp, bool force) +static void mall_destroy(struct tcf_proto *tp) { struct cls_mall_head *head = rtnl_dereference(tp->root); struct net_device *dev = tp->q->dev_queue->dev; if (!head) - return true; + return; if (tc_should_offload(dev, tp, head->flags)) mall_destroy_hw_filter(tp, head, (unsigned long) head); call_rcu(&head->rcu, mall_destroy_rcu); - return true; } static unsigned long mall_get(struct tcf_proto *tp, u32 handle) @@ -216,7 +215,7 @@ err_exts_init: return err; } -static int mall_delete(struct tcf_proto *tp, unsigned long arg) +static int mall_delete(struct tcf_proto *tp, unsigned long arg, bool *last) { return -EOPNOTSUPP; } diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index a371075c1d7a..f4d687e04240 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -276,20 +276,13 @@ static void route4_delete_filter(struct rcu_head *head) kfree(f); } -static bool route4_destroy(struct tcf_proto *tp, bool force) +static void route4_destroy(struct tcf_proto *tp) { struct route4_head *head = rtnl_dereference(tp->root); int h1, h2; if (head == NULL) - return true; - - if (!force) { - for (h1 = 0; h1 <= 256; h1++) { - if (rcu_access_pointer(head->table[h1])) - return false; - } - } + return; for (h1 = 0; h1 <= 256; h1++) { struct route4_bucket *b; @@ -314,10 +307,9 @@ static bool route4_destroy(struct tcf_proto *tp, bool force) } RCU_INIT_POINTER(tp->root, NULL); kfree_rcu(head, rcu); - return true; } -static int route4_delete(struct tcf_proto *tp, unsigned long arg) +static int route4_delete(struct tcf_proto *tp, unsigned long arg, bool *last) { struct route4_head *head = rtnl_dereference(tp->root); struct route4_filter *f = (struct route4_filter *)arg; @@ -325,7 +317,7 @@ static int route4_delete(struct tcf_proto *tp, unsigned long arg) struct route4_filter *nf; struct route4_bucket *b; unsigned int h = 0; - int i; + int i, h1; if (!head || !f) return -EINVAL; @@ -356,16 +348,25 @@ static int route4_delete(struct tcf_proto *tp, unsigned long arg) rt = rtnl_dereference(b->ht[i]); if (rt) - return 0; + goto out; } /* OK, session has no flows */ RCU_INIT_POINTER(head->table[to_hash(h)], NULL); kfree_rcu(b, rcu); + break; + } + } - return 0; +out: + *last = true; + for (h1 = 0; h1 <= 256; h1++) { + if (rcu_access_pointer(head->table[h1])) { + *last = false; + break; } } + return 0; } diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index d7f2923e6ebd..18a947016178 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -302,20 +302,13 @@ static void rsvp_delete_filter(struct tcf_proto *tp, struct rsvp_filter *f) call_rcu(&f->rcu, rsvp_delete_filter_rcu); } -static bool rsvp_destroy(struct tcf_proto *tp, bool force) +static void rsvp_destroy(struct tcf_proto *tp) { struct rsvp_head *data = rtnl_dereference(tp->root); int h1, h2; if (data == NULL) - return true; - - if (!force) { - for (h1 = 0; h1 < 256; h1++) { - if (rcu_access_pointer(data->ht[h1])) - return false; - } - } + return; RCU_INIT_POINTER(tp->root, NULL); @@ -337,10 +330,9 @@ static bool rsvp_destroy(struct tcf_proto *tp, bool force) } } kfree_rcu(data, rcu); - return true; } -static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) +static int rsvp_delete(struct tcf_proto *tp, unsigned long arg, bool *last) { struct rsvp_head *head = rtnl_dereference(tp->root); struct rsvp_filter *nfp, *f = (struct rsvp_filter *)arg; @@ -348,7 +340,7 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) unsigned int h = f->handle; struct rsvp_session __rcu **sp; struct rsvp_session *nsp, *s = f->sess; - int i; + int i, h1; fp = &s->ht[(h >> 8) & 0xFF]; for (nfp = rtnl_dereference(*fp); nfp; @@ -361,7 +353,7 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) for (i = 0; i <= 16; i++) if (s->ht[i]) - return 0; + goto out; /* OK, session has no flows */ sp = &head->ht[h & 0xFF]; @@ -370,13 +362,23 @@ static int rsvp_delete(struct tcf_proto *tp, unsigned long arg) if (nsp == s) { RCU_INIT_POINTER(*sp, s->next); kfree_rcu(s, rcu); - return 0; + goto out; } } - return 0; + break; } } + +out: + *last = true; + for (h1 = 0; h1 < 256; h1++) { + if (rcu_access_pointer(head->ht[h1])) { + *last = false; + break; + } + } + return 0; } diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index 2ab001361457..8a8a58357c39 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -150,7 +150,7 @@ static void tcindex_destroy_fexts(struct rcu_head *head) kfree(f); } -static int tcindex_delete(struct tcf_proto *tp, unsigned long arg) +static int tcindex_delete(struct tcf_proto *tp, unsigned long arg, bool *last) { struct tcindex_data *p = rtnl_dereference(tp->root); struct tcindex_filter_result *r = (struct tcindex_filter_result *) arg; @@ -186,6 +186,8 @@ found: call_rcu(&f->rcu, tcindex_destroy_fexts); else call_rcu(&r->rcu, tcindex_destroy_rexts); + + *last = false; return 0; } @@ -193,7 +195,9 @@ static int tcindex_destroy_element(struct tcf_proto *tp, unsigned long arg, struct tcf_walker *walker) { - return tcindex_delete(tp, arg); + bool last; + + return tcindex_delete(tp, arg, &last); } static void __tcindex_destroy(struct rcu_head *head) @@ -529,14 +533,11 @@ static void tcindex_walk(struct tcf_proto *tp, struct tcf_walker *walker) } } -static bool tcindex_destroy(struct tcf_proto *tp, bool force) +static void tcindex_destroy(struct tcf_proto *tp) { struct tcindex_data *p = rtnl_dereference(tp->root); struct tcf_walker walker; - if (!force) - return false; - pr_debug("tcindex_destroy(tp %p),p %p\n", tp, p); walker.count = 0; walker.skip = 0; @@ -544,7 +545,6 @@ static bool tcindex_destroy(struct tcf_proto *tp, bool force) tcindex_walk(tp, &walker); call_rcu(&p->rcu, __tcindex_destroy); - return true; } diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 9e2f330ac80f..d20e72a095d5 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -585,37 +585,13 @@ static bool ht_empty(struct tc_u_hnode *ht) return true; } -static bool u32_destroy(struct tcf_proto *tp, bool force) +static void u32_destroy(struct tcf_proto *tp) { struct tc_u_common *tp_c = tp->data; struct tc_u_hnode *root_ht = rtnl_dereference(tp->root); WARN_ON(root_ht == NULL); - if (!force) { - if (root_ht) { - if (root_ht->refcnt > 1) - return false; - if (root_ht->refcnt == 1) { - if (!ht_empty(root_ht)) - return false; - } - } - - if (tp_c->refcnt > 1) - return false; - - if (tp_c->refcnt == 1) { - struct tc_u_hnode *ht; - - for (ht = rtnl_dereference(tp_c->hlist); - ht; - ht = rtnl_dereference(ht->next)) - if (!ht_empty(ht)) - return false; - } - } - if (root_ht && --root_ht->refcnt == 0) u32_destroy_hnode(tp, root_ht); @@ -640,20 +616,22 @@ static bool u32_destroy(struct tcf_proto *tp, bool force) } tp->data = NULL; - return true; } -static int u32_delete(struct tcf_proto *tp, unsigned long arg) +static int u32_delete(struct tcf_proto *tp, unsigned long arg, bool *last) { struct tc_u_hnode *ht = (struct tc_u_hnode *)arg; struct tc_u_hnode *root_ht = rtnl_dereference(tp->root); + struct tc_u_common *tp_c = tp->data; + int ret = 0; if (ht == NULL) - return 0; + goto out; if (TC_U32_KEY(ht->handle)) { u32_remove_hw_knode(tp, ht->handle); - return u32_delete_key(tp, (struct tc_u_knode *)ht); + ret = u32_delete_key(tp, (struct tc_u_knode *)ht); + goto out; } if (root_ht == ht) @@ -666,7 +644,40 @@ static int u32_delete(struct tcf_proto *tp, unsigned long arg) return -EBUSY; } - return 0; +out: + *last = true; + if (root_ht) { + if (root_ht->refcnt > 1) { + *last = false; + goto ret; + } + if (root_ht->refcnt == 1) { + if (!ht_empty(root_ht)) { + *last = false; + goto ret; + } + } + } + + if (tp_c->refcnt > 1) { + *last = false; + goto ret; + } + + if (tp_c->refcnt == 1) { + struct tc_u_hnode *ht; + + for (ht = rtnl_dereference(tp_c->hlist); + ht; + ht = rtnl_dereference(ht->next)) + if (!ht_empty(ht)) { + *last = false; + break; + } + } + +ret: + return ret; } #define NR_U32_NODE (1<<12) -- cgit From 4392053879717edb0c4756a3878c0274267e237b Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Wed, 19 Apr 2017 14:21:22 -0700 Subject: net_sched: remove useless NULL to tp->root There is no need to NULL tp->root in ->destroy(), since tp is going to be freed very soon, and existing readers are still safe to read them. For cls_route, we always init its tp->root, so it can't be NULL, we can drop more useless code. Cc: Daniel Borkmann Cc: John Fastabend Cc: Jamal Hadi Salim Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/sched/cls_fw.c | 1 - net/sched/cls_route.c | 15 --------------- net/sched/cls_rsvp.h | 4 ---- 3 files changed, 20 deletions(-) diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 78ccb4a85a4d..d3885362e017 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -144,7 +144,6 @@ static void fw_destroy(struct tcf_proto *tp) call_rcu(&f->rcu, fw_delete_filter); } } - RCU_INIT_POINTER(tp->root, NULL); kfree_rcu(head, rcu); } diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index f4d687e04240..d63d5502ee02 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -140,8 +140,6 @@ static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp, goto failure; id = dst->tclassid; - if (head == NULL) - goto old_method; iif = inet_iif(skb); @@ -194,15 +192,6 @@ restart: route4_set_fastmap(head, id, iif, ROUTE4_FAILURE); failure: return -1; - -old_method: - if (id && (TC_H_MAJ(id) == 0 || - !(TC_H_MAJ(id^tp->q->handle)))) { - res->classid = id; - res->class = 0; - return 0; - } - return -1; } static inline u32 to_hash(u32 id) @@ -234,9 +223,6 @@ static unsigned long route4_get(struct tcf_proto *tp, u32 handle) struct route4_filter *f; unsigned int h1, h2; - if (!head) - return 0; - h1 = to_hash(handle); if (h1 > 256) return 0; @@ -305,7 +291,6 @@ static void route4_destroy(struct tcf_proto *tp) kfree_rcu(b, rcu); } } - RCU_INIT_POINTER(tp->root, NULL); kfree_rcu(head, rcu); } diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 18a947016178..0d9d07798699 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -152,8 +152,6 @@ static int rsvp_classify(struct sk_buff *skb, const struct tcf_proto *tp, return -1; nhptr = ip_hdr(skb); #endif - if (unlikely(!head)) - return -1; restart: #if RSVP_DST_LEN == 4 @@ -310,8 +308,6 @@ static void rsvp_destroy(struct tcf_proto *tp) if (data == NULL) return; - RCU_INIT_POINTER(tp->root, NULL); - for (h1 = 0; h1 < 256; h1++) { struct rsvp_session *s; -- cgit From 76bb5db5c749dfe19d779aac076133e821b859dd Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Wed, 19 Apr 2017 15:22:02 -0700 Subject: netvsc: fix use after free on module removal The NAPI data structure is embedded in the netvsc_device structure and is freed when device is closed. There is still a reference (in NAPI list) to this which causes a crash in netif_napi_del when device is removed. Fix by managing NAPI instances correctly. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc.c | 9 +++++---- drivers/net/hyperv/rndis_filter.c | 9 ++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 967843ba03fa..f99651c03e0a 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -584,8 +584,9 @@ void netvsc_device_remove(struct hv_device *device) /* Now, we can close the channel safely */ vmbus_close(device->channel); + /* And dissassociate NAPI context from device */ for (i = 0; i < net_device->num_chn; i++) - napi_disable(&net_device->chan_table[i].napi); + netif_napi_del(&net_device->chan_table[i].napi); /* Release all resources */ free_netvsc_device_rcu(net_device); @@ -1320,8 +1321,6 @@ int netvsc_device_add(struct hv_device *device, struct netvsc_channel *nvchan = &net_device->chan_table[i]; nvchan->channel = device->channel; - netif_napi_add(ndev, &nvchan->napi, - netvsc_poll, NAPI_POLL_WEIGHT); } /* Open the channel */ @@ -1339,6 +1338,8 @@ int netvsc_device_add(struct hv_device *device, netdev_dbg(ndev, "hv_netvsc channel opened successfully\n"); /* Enable NAPI handler for init callbacks */ + netif_napi_add(ndev, &net_device->chan_table[0].napi, + netvsc_poll, NAPI_POLL_WEIGHT); napi_enable(&net_device->chan_table[0].napi); /* Writing nvdev pointer unlocks netvsc_send(), make sure chn_table is @@ -1357,7 +1358,7 @@ int netvsc_device_add(struct hv_device *device, return ret; close: - napi_disable(&net_device->chan_table[0].napi); + netif_napi_del(&net_device->chan_table[0].napi); /* Now, we can close the channel safely */ vmbus_close(device->channel); diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 1e9445bc4539..ab92c3c95951 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1009,13 +1009,16 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) /* Set the channel before opening.*/ nvchan->channel = new_sc; + netif_napi_add(ndev, &nvchan->napi, + netvsc_poll, NAPI_POLL_WEIGHT); ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE, nvscdev->ring_size * PAGE_SIZE, NULL, 0, netvsc_channel_cb, nvchan); - - - napi_enable(&nvchan->napi); + if (ret == 0) + napi_enable(&nvchan->napi); + else + netdev_err(ndev, "sub channel open failed (%d)\n", ret); if (refcount_dec_and_test(&nvscdev->sc_offered)) complete(&nvscdev->channel_init_wait); -- cgit From 77999328b5fbccca674b3d71a346289e17b4c7cf Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Thu, 20 Apr 2017 10:36:32 +0200 Subject: MAINTAINERS: Add new IPsec offloading files. This adds two new files to IPsec maintenance scope: net/ipv4/esp4_offload.c net/ipv6/ip6_offload.c Signed-off-by: Steffen Klassert Signed-off-by: David S. Miller --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 2aa84164cad8..39aa09100a48 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8817,12 +8817,12 @@ F: net/core/flow.c F: net/xfrm/ F: net/key/ F: net/ipv4/xfrm* -F: net/ipv4/esp4.c +F: net/ipv4/esp4* F: net/ipv4/ah4.c F: net/ipv4/ipcomp.c F: net/ipv4/ip_vti.c F: net/ipv6/xfrm* -F: net/ipv6/esp6.c +F: net/ipv6/esp6* F: net/ipv6/ah6.c F: net/ipv6/ipcomp6.c F: net/ipv6/ip6_vti.c -- cgit From b18b745397e0154795b2d5504def5ccd090b893c Mon Sep 17 00:00:00 2001 From: Alexander Kochetkov Date: Thu, 20 Apr 2017 16:29:34 +0300 Subject: net: arc_emac: switch to phy_start()/phy_stop() Currently driver use phy_start_aneg() in arc_emac_open() to bring up PHY. But phy_start() function is more appropriate for this purposes. Besides that it call phy_start_aneg() as part of PHY startup sequence it also can correctly bring up PHY from error and suspended states. So the patch replace phy_start_aneg() to phy_start(). Also the patch add call to phy_stop() to arc_emac_stop() to allow the PHY device to be fully suspended when the interface is unused. Signed-off-by: Alexander Kochetkov Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/ethernet/arc/emac_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c index 23873395f100..68de2f2652f2 100644 --- a/drivers/net/ethernet/arc/emac_main.c +++ b/drivers/net/ethernet/arc/emac_main.c @@ -434,7 +434,7 @@ static int arc_emac_open(struct net_device *ndev) /* Enable EMAC */ arc_reg_or(priv, R_CTRL, EN_MASK); - phy_start_aneg(ndev->phydev); + phy_start(ndev->phydev); netif_start_queue(ndev); @@ -556,6 +556,8 @@ static int arc_emac_stop(struct net_device *ndev) napi_disable(&priv->napi); netif_stop_queue(ndev); + phy_stop(ndev->phydev); + /* Disable interrupts */ arc_reg_clr(priv, R_ENABLE, RXINT_MASK | TXINT_MASK | ERR_MASK); -- cgit From ea8ffc0818d8a47ffda423f61f1d8ad1caca8986 Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Thu, 20 Apr 2017 12:49:24 -0700 Subject: bonding: fix wq initialization for links created via netlink Earlier patch 4493b81bea ("bonding: initialize work-queues during creation of bond") moved the work-queue initialization from bond_open() to bond_create(). However this caused the link those are created using netlink 'create bond option' (ip link add bondX type bond); create the new trunk without initializing work-queues. Prior to the above mentioned change, ndo_open was in both paths and things worked correctly. The consequence is visible in the report shared by Joe Stringer - I've noticed that this patch breaks bonding within namespaces if you're not careful to perform device cleanup correctly. Here's my repro script, you can run on any net-next with this patch and you'll start seeing some weird behaviour: ip netns add foo ip li add veth0 type veth peer name veth0+ netns foo ip li add veth1 type veth peer name veth1+ netns foo ip netns exec foo ip li add bond0 type bond ip netns exec foo ip li set dev veth0+ master bond0 ip netns exec foo ip li set dev veth1+ master bond0 ip netns exec foo ip addr add dev bond0 192.168.0.1/24 ip netns exec foo ip li set dev bond0 up ip li del dev veth0 ip li del dev veth1 The second to last command segfaults, last command hangs. rtnl is now permanently locked. It's not a problem if you take bond0 down before deleting veths, or delete bond0 before deleting veths. If you delete either end of the veth pair as per above, either inside or outside the namespace, it hits this problem. Here's some kernel logs: [ 1221.801610] bond0: Enslaving veth0+ as an active interface with an up link [ 1224.449581] bond0: Enslaving veth1+ as an active interface with an up link [ 1281.193863] bond0: Releasing backup interface veth0+ [ 1281.193866] bond0: the permanent HWaddr of veth0+ - 16:bf:fb:e0:b8:43 - is still in use by bond0 - set the HWaddr of veth0+ to a different address to avoid conflicts [ 1281.193867] ------------[ cut here ]------------ [ 1281.193873] WARNING: CPU: 0 PID: 2024 at kernel/workqueue.c:1511 __queue_delayed_work+0x13f/0x150 [ 1281.193873] Modules linked in: bonding veth openvswitch nf_nat_ipv6 nf_nat_ipv4 nf_nat autofs4 nfsd auth_rpcgss nfs_acl binfmt_misc nfs lockd grace sunrpc fscache ppdev vmw_balloon coretemp psmouse serio_raw vmwgfx ttm drm_kms_helper vmw_vmci netconsole parport_pc configfs drm i2c_piix4 fb_sys_fops syscopyarea sysfillrect sysimgblt shpchp mac_hid nf_conntrack_ipv6 nf_defrag_ipv6 nf_conntrack_ipv4 nf_defrag_ipv4 nf_conntrack libcrc32c lp parport hid_generic usbhid hid mptspi mptscsih e1000 mptbase ahci libahci [ 1281.193905] CPU: 0 PID: 2024 Comm: ip Tainted: G W 4.10.0-bisect-bond-v0.14 #37 [ 1281.193906] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 09/30/2014 [ 1281.193906] Call Trace: [ 1281.193912] dump_stack+0x63/0x89 [ 1281.193915] __warn+0xd1/0xf0 [ 1281.193917] warn_slowpath_null+0x1d/0x20 [ 1281.193918] __queue_delayed_work+0x13f/0x150 [ 1281.193920] queue_delayed_work_on+0x27/0x40 [ 1281.193929] bond_change_active_slave+0x25b/0x670 [bonding] [ 1281.193932] ? synchronize_rcu_expedited+0x27/0x30 [ 1281.193935] __bond_release_one+0x489/0x510 [bonding] [ 1281.193939] ? addrconf_notify+0x1b7/0xab0 [ 1281.193942] bond_netdev_event+0x2c5/0x2e0 [bonding] [ 1281.193944] ? netconsole_netdev_event+0x124/0x190 [netconsole] [ 1281.193947] notifier_call_chain+0x49/0x70 [ 1281.193948] raw_notifier_call_chain+0x16/0x20 [ 1281.193950] call_netdevice_notifiers_info+0x35/0x60 [ 1281.193951] rollback_registered_many+0x23b/0x3e0 [ 1281.193953] unregister_netdevice_many+0x24/0xd0 [ 1281.193955] rtnl_delete_link+0x3c/0x50 [ 1281.193956] rtnl_dellink+0x8d/0x1b0 [ 1281.193960] rtnetlink_rcv_msg+0x95/0x220 [ 1281.193962] ? __kmalloc_node_track_caller+0x35/0x280 [ 1281.193964] ? __netlink_lookup+0xf1/0x110 [ 1281.193966] ? rtnl_newlink+0x830/0x830 [ 1281.193967] netlink_rcv_skb+0xa7/0xc0 [ 1281.193969] rtnetlink_rcv+0x28/0x30 [ 1281.193970] netlink_unicast+0x15b/0x210 [ 1281.193971] netlink_sendmsg+0x319/0x390 [ 1281.193974] sock_sendmsg+0x38/0x50 [ 1281.193975] ___sys_sendmsg+0x25c/0x270 [ 1281.193978] ? mem_cgroup_commit_charge+0x76/0xf0 [ 1281.193981] ? page_add_new_anon_rmap+0x89/0xc0 [ 1281.193984] ? lru_cache_add_active_or_unevictable+0x35/0xb0 [ 1281.193985] ? __handle_mm_fault+0x4e9/0x1170 [ 1281.193987] __sys_sendmsg+0x45/0x80 [ 1281.193989] SyS_sendmsg+0x12/0x20 [ 1281.193991] do_syscall_64+0x6e/0x180 [ 1281.193993] entry_SYSCALL64_slow_path+0x25/0x25 [ 1281.193995] RIP: 0033:0x7f6ec122f5a0 [ 1281.193995] RSP: 002b:00007ffe69e89c48 EFLAGS: 00000246 ORIG_RAX: 000000000000002e [ 1281.193997] RAX: ffffffffffffffda RBX: 00007ffe69e8dd60 RCX: 00007f6ec122f5a0 [ 1281.193997] RDX: 0000000000000000 RSI: 00007ffe69e89c90 RDI: 0000000000000003 [ 1281.193998] RBP: 00007ffe69e89c90 R08: 0000000000000000 R09: 0000000000000003 [ 1281.193999] R10: 00007ffe69e89a10 R11: 0000000000000246 R12: 0000000058f14b9f [ 1281.193999] R13: 0000000000000000 R14: 00000000006473a0 R15: 00007ffe69e8e450 [ 1281.194001] ---[ end trace 713a77486cbfbfa3 ]--- Fixes: 4493b81bea ("bonding: initialize work-queues during creation of bond") Reported-by: Joe Stringer Tested-by: Joe Stringer Signed-off-by: Mahesh Bandewar Acked-by: Andy Gospodarek Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 2 +- drivers/net/bonding/bond_netlink.c | 5 +++++ include/net/bonding.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 6bd3b50faf48..e549bf6f5cac 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3243,7 +3243,7 @@ u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb) /*-------------------------- Device entry points ----------------------------*/ -static void bond_work_init_all(struct bonding *bond) +void bond_work_init_all(struct bonding *bond) { INIT_DELAYED_WORK(&bond->mcast_work, bond_resend_igmp_join_requests_delayed); diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index b8df0f5e8c25..c502c139d3bc 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -449,6 +449,11 @@ static int bond_newlink(struct net *src_net, struct net_device *bond_dev, err = register_netdevice(bond_dev); netif_carrier_off(bond_dev); + if (!err) { + struct bonding *bond = netdev_priv(bond_dev); + + bond_work_init_all(bond); + } return err; } diff --git a/include/net/bonding.h b/include/net/bonding.h index 04a21e8048be..b00508d22e0a 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -614,6 +614,7 @@ struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev, int level); int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave); void bond_slave_arr_work_rearm(struct bonding *bond, unsigned long delay); +void bond_work_init_all(struct bonding *bond); #ifdef CONFIG_PROC_FS void bond_create_proc_entry(struct bonding *bond); -- cgit From 239c599a5f553d65ddc933d06bbbed95aaad9dd0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 21 Apr 2017 18:22:40 +0200 Subject: net: dsa: LAN9303: add I2C dependency With CONFIG_I2C=m and NET_DSA_SMSC_LAN9303=y, we run into a link error: drivers/base/regmap/regmap-i2c.o: In function `regmap_smbus_byte_reg_read': regmap-i2c.c:(.text.regmap_smbus_byte_reg_read+0x18): undefined reference to `i2c_smbus_read_byte_data' drivers/base/regmap/regmap-i2c.o: In function `regmap_smbus_byte_reg_write': regmap-i2c.c:(.text.regmap_smbus_byte_reg_write+0x18): undefined reference to `i2c_smbus_write_byte_data' drivers/base/regmap/regmap-i2c.o: In function `regmap_smbus_word_reg_read': regmap-i2c.c:(.text.regmap_smbus_word_reg_read+0x18): undefined reference to `i2c_smbus_read_word_data' drivers/base/regmap/regmap-i2c.o: In function `regmap_smbus_word_read_swapped': regmap-i2c.c:(.text.regmap_smbus_word_read_swapped+0x18): undefined reference to `i2c_smbus_read_word_data' drivers/base/regmap/regmap-i2c.o: In function `regmap_smbus_word_write_swapped': This adds a Kconfig dependency to avoid the broken configuration. Fixes: be4e119f9914 ("net: dsa: LAN9303: add I2C managed mode support") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/dsa/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 131a5b1cbfc8..862ee22303c2 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -59,7 +59,7 @@ config NET_DSA_SMSC_LAN9303 config NET_DSA_SMSC_LAN9303_I2C tristate "SMSC/Microchip LAN9303 3-ports 10/100 ethernet switch in I2C managed mode" - depends on NET_DSA + depends on NET_DSA && I2C select NET_DSA_SMSC_LAN9303 select REGMAP_I2C ---help--- -- cgit From 1f4407e2548827e3e6e7b943640a2da90c611306 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 21 Apr 2017 15:59:52 -0400 Subject: net: Remove NET_CORE_BUDGET_USECS from sysctl binary interface. We are not supposed to add new entries to this thing any more. Thanks to Eric Dumazet for noticing this. Signed-off-by: David S. Miller --- include/uapi/linux/sysctl.h | 1 - kernel/sysctl_binary.c | 1 - 2 files changed, 2 deletions(-) diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h index 177f5f139b36..e13d48058b8d 100644 --- a/include/uapi/linux/sysctl.h +++ b/include/uapi/linux/sysctl.h @@ -274,7 +274,6 @@ enum NET_CORE_AEVENT_ETIME=20, NET_CORE_AEVENT_RSEQTH=21, NET_CORE_WARNINGS=22, - NET_CORE_BUDGET_USECS=23, }; /* /proc/sys/net/ethernet */ diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c index 4ee3e49530d2..ece4b177052b 100644 --- a/kernel/sysctl_binary.c +++ b/kernel/sysctl_binary.c @@ -197,7 +197,6 @@ static const struct bin_table bin_net_core_table[] = { { CTL_INT, NET_CORE_AEVENT_ETIME, "xfrm_aevent_etime" }, { CTL_INT, NET_CORE_AEVENT_RSEQTH, "xfrm_aevent_rseqth" }, { CTL_INT, NET_CORE_WARNINGS, "warnings" }, - { CTL_INT, NET_CORE_BUDGET_USECS, "netdev_budget_usecs" }, {}, }; -- cgit From f2edd9f67b8bdbe8cc3bc5c0ba4992e511147642 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 15 Apr 2017 23:54:13 +0200 Subject: Bluetooth: hci_ll: Fix NULL pointer deref on FW upload failure Avoid NULL pointer dereference occurring due to freeing skb containing an error pointer. It can easily be triggered by using the driver with broken uart (i.e. due to misconfigured pinmuxing). Fixes: 371805522f87 ("bluetooth: hci_uart: add LL protocol serdev driver support") Signed-off-by: Sebastian Reichel Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ll.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index 485e8eb04542..adc444f309a3 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -537,8 +537,7 @@ static int read_local_version(struct hci_dev *hdev) if (IS_ERR(skb)) { bt_dev_err(hdev, "Reading TI version information failed (%ld)", PTR_ERR(skb)); - err = PTR_ERR(skb); - goto out; + return PTR_ERR(skb); } if (skb->len != sizeof(*ver)) { err = -EILSEQ; -- cgit From 1fb78fb6c6ad3751cce18d37e773d07858c1ced9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 19 Apr 2017 19:50:18 +0200 Subject: Bluetooth: try to improve CONFIG_SERIAL_DEV_BUS dependency With CONFIG_SERIAL_DEV_BUS=m, the hci_serdev.o file does not actually get built into hci_uart.o as the Makefile doesn't pick it up, leading to a link error with anything referring to it: ERROR: "hci_uart_register_device" [drivers/bluetooth/hci_nokia.ko] undefined! scripts/Makefile.modpost:91: recipe for target '__modpost' failed Changing this in the Makefile would cause another problem when hci_uart itself is built-in and cannot reference symbols from the serdev module. This tries to address both problems by introducing a new hidden Kconfig symbol that controls both the compilation of hci_serdev.o and whether the Nokia driver can be selected. This seems to address the problem for me, though there might be a better way to do it. Fixes: 7bb318680e86 ("Bluetooth: add nokia driver") Signed-off-by: Arnd Bergmann Signed-off-by: Marcel Holtmann --- drivers/bluetooth/Kconfig | 8 +++++++- drivers/bluetooth/Makefile | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 8aafbed9e160..737d93ef27c5 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -76,6 +76,12 @@ config BT_HCIUART Say Y here to compile support for Bluetooth UART devices into the kernel or say M to compile it as module (hci_uart). +config BT_HCIUART_SERDEV + bool + depends on SERIAL_DEV_BUS && BT_HCIUART + depends on SERIAL_DEV_BUS=y || SERIAL_DEV_BUS=BT_HCIUART + default y + config BT_HCIUART_H4 bool "UART (H4) protocol support" depends on BT_HCIUART @@ -89,7 +95,7 @@ config BT_HCIUART_H4 config BT_HCIUART_NOKIA tristate "UART Nokia H4+ protocol support" depends on BT_HCIUART - depends on SERIAL_DEV_BUS + depends on BT_HCIUART_SERDEV depends on PM help Nokia H4+ is serial protocol for communication between Bluetooth diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index a7f237320f4b..e693ca6eeed9 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -31,7 +31,7 @@ btmrvl-y := btmrvl_main.o btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o hci_uart-y := hci_ldisc.o -hci_uart-$(CONFIG_SERIAL_DEV_BUS) += hci_serdev.o +hci_uart-$(CONFIG_BT_HCIUART_SERDEV) += hci_serdev.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o -- cgit From eee6044f6694ae21f6f4b8e5ce4f13dbda0c112b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 19 Apr 2017 19:32:25 +0200 Subject: ieee802154: don't select COMMON_CLK A device driver must not select the COMMON_CLK subsystem, as that conflicts with platforms that provide a legacy implementation of the clk API: drivers/clk/clk.o: In function `clk_enable': clk.c:(.text.clk_enable+0x0): multiple definition of `clk_enable' arch/arm/mach-sa1100/clock.o:clock.c:(.text.clk_enable+0x0): first defined here drivers/clk/clk.o: In function `clk_round_rate': clk.c:(.text.clk_round_rate+0x0): multiple definition of `clk_round_rate' arch/arm/mach-sa1100/clock.o:clock.c:(.text.clk_round_rate+0x0): first defined here drivers/clk/clk.o: In function `clk_get_parent': clk.c:(.text.clk_get_parent+0x0): multiple definition of `clk_get_parent' arch/arm/mach-sa1100/clock.o:clock.c:(.text.clk_get_parent+0x0): first defined here drivers/clk/clk.o: In function `clk_get_rate': clk.c:(.text.clk_get_rate+0x0): multiple definition of `clk_get_rate' This changes the 'select' into 'depends on', as all other similar drivers do. Fixes: d931acd575d6 ("ieee802154: Add CA8210 IEEE 802.15.4 device driver") Signed-off-by: Arnd Bergmann Signed-off-by: Marcel Holtmann --- drivers/net/ieee802154/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig index ce4864dc3c6e..303ba4133920 100644 --- a/drivers/net/ieee802154/Kconfig +++ b/drivers/net/ieee802154/Kconfig @@ -86,8 +86,8 @@ config IEEE802154_ADF7242 config IEEE802154_CA8210 tristate "Cascoda CA8210 transceiver driver" depends on IEEE802154_DRIVERS && MAC802154 + depends on COMMON_CLK depends on SPI - select COMMON_CLK ---help--- Say Y here to enable the CA8210 SPI 802.15.4 wireless controller. -- cgit From cb926520e18e6aecc63614b8aa2e40d431aa29cd Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Thu, 20 Apr 2017 18:06:39 +0100 Subject: Bluetooth: hci_ldisc: Add missing return in hci_uart_init_work() If hci_register_dev() returns an error in hci_uart_init_work() then the HCI_UART_REGISTERED bit gets erroneously set due to a missing return statement. Therefore, add the missing return statement. The consequence of the missing return is that the HCI UART is not registered but HCI_UART_REGISTERED is set which allows the code to think that hu->hdev is safe to access but hu->hdev has been freed so could lead to a crash. Signed-off-by: Dean Jenkins Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ldisc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index cec4438ede01..1166e3f5682d 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -187,6 +187,7 @@ static void hci_uart_init_work(struct work_struct *work) hci_free_dev(hu->hdev); hu->hdev = NULL; hu->proto->close(hu); + return; } set_bit(HCI_UART_REGISTERED, &hu->flags); -- cgit From a225b8c70af9368cfcb52a3608bc862bc88a7801 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Thu, 20 Apr 2017 18:06:40 +0100 Subject: Bluetooth: hci_ldisc: Ensure hu->hdev set to NULL before freeing hdev When hci_register_dev() fails, hu->hdev should be set to NULL before freeing hdev. This avoids potential use of hu->hdev after it has been freed. This commit sets hu->hdev to NULL before calling hci_free_dev() in error handling scenarios in hci_uart_init_work() and hci_uart_register_dev(). Signed-off-by: Dean Jenkins Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ldisc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 1166e3f5682d..b1096d1ab30e 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -177,6 +177,7 @@ static void hci_uart_init_work(struct work_struct *work) { struct hci_uart *hu = container_of(work, struct hci_uart, init_ready); int err; + struct hci_dev *hdev; if (!test_and_clear_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags)) return; @@ -184,8 +185,9 @@ static void hci_uart_init_work(struct work_struct *work) err = hci_register_dev(hu->hdev); if (err < 0) { BT_ERR("Can't register HCI device"); - hci_free_dev(hu->hdev); + hdev = hu->hdev; hu->hdev = NULL; + hci_free_dev(hdev); hu->proto->close(hu); return; } @@ -603,6 +605,7 @@ static int hci_uart_register_dev(struct hci_uart *hu) if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); + hu->hdev = NULL; hci_free_dev(hdev); return -ENODEV; } -- cgit From d160b74da85a4ec072b076db056e27ba7246eba0 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Thu, 20 Apr 2017 18:06:41 +0100 Subject: Bluetooth: hci_ldisc: Add missing clear HCI_UART_PROTO_READY Ensure that HCI_UART_PROTO_READY is cleared before close(hu) is called which closes the Data Link protocol layer. Therefore, add the missing bit clear of HCI_UART_PROTO_READY to hci_uart_init_work() so that the flag is cleared when hci_register_dev fails. Without the fix, the functions of the Data Link protocol layer could potentially be accessed after that layer has been closed. This could lead to a crash as memory would have been freed in that layer. Signed-off-by: Dean Jenkins Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ldisc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index b1096d1ab30e..c53513cb7654 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -188,6 +188,7 @@ static void hci_uart_init_work(struct work_struct *work) hdev = hu->hdev; hu->hdev = NULL; hci_free_dev(hdev); + clear_bit(HCI_UART_PROTO_READY, &hu->flags); hu->proto->close(hu); return; } -- cgit From f43e9b069aeaf0f3d51fa30ddc9c0003e86623b8 Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Sun, 25 Sep 2016 13:52:44 +0300 Subject: net/devlink: Add E-Switch encapsulation control This is an e-switch global knob to enable HW support for applying encapsulation/decapsulation to VF traffic as part of SRIOV e-switch offloading. The actual encap/decap is carried out (along with the matching and other actions) per offloaded e-switch rules, e.g as done when offloading the TC tunnel key action. Signed-off-by: Roi Dayan Reviewed-by: Or Gerlitz Acked-by: Jiri Pirko Signed-off-by: Saeed Mahameed --- include/net/devlink.h | 2 ++ include/uapi/linux/devlink.h | 7 +++++++ net/core/devlink.c | 26 +++++++++++++++++++++++--- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/include/net/devlink.h b/include/net/devlink.h index 24de13f8c94f..ed7687bbf5d0 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -268,6 +268,8 @@ struct devlink_ops { int (*eswitch_mode_set)(struct devlink *devlink, u16 mode); int (*eswitch_inline_mode_get)(struct devlink *devlink, u8 *p_inline_mode); int (*eswitch_inline_mode_set)(struct devlink *devlink, u8 inline_mode); + int (*eswitch_encap_mode_get)(struct devlink *devlink, u8 *p_encap_mode); + int (*eswitch_encap_mode_set)(struct devlink *devlink, u8 encap_mode); }; static inline void *devlink_priv(struct devlink *devlink) diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index b47bee277347..b0e807ac53bb 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -119,6 +119,11 @@ enum devlink_eswitch_inline_mode { DEVLINK_ESWITCH_INLINE_MODE_TRANSPORT, }; +enum devlink_eswitch_encap_mode { + DEVLINK_ESWITCH_ENCAP_MODE_NONE, + DEVLINK_ESWITCH_ENCAP_MODE_BASIC, +}; + enum devlink_attr { /* don't change the order or add anything between, this is ABI! */ DEVLINK_ATTR_UNSPEC, @@ -195,6 +200,8 @@ enum devlink_attr { DEVLINK_ATTR_PAD, + DEVLINK_ATTR_ESWITCH_ENCAP_MODE, /* u8 */ + /* add new attributes above here, update the policy in devlink.c */ __DEVLINK_ATTR_MAX, diff --git a/net/core/devlink.c b/net/core/devlink.c index 0afac5800b57..b0b87a292e7c 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1397,10 +1397,10 @@ static int devlink_nl_eswitch_fill(struct sk_buff *msg, struct devlink *devlink, u32 seq, int flags) { const struct devlink_ops *ops = devlink->ops; + u8 inline_mode, encap_mode; void *hdr; int err = 0; u16 mode; - u8 inline_mode; hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd); if (!hdr) @@ -1429,6 +1429,15 @@ static int devlink_nl_eswitch_fill(struct sk_buff *msg, struct devlink *devlink, goto nla_put_failure; } + if (ops->eswitch_encap_mode_get) { + err = ops->eswitch_encap_mode_get(devlink, &encap_mode); + if (err) + goto nla_put_failure; + err = nla_put_u8(msg, DEVLINK_ATTR_ESWITCH_ENCAP_MODE, encap_mode); + if (err) + goto nla_put_failure; + } + genlmsg_end(msg, hdr); return 0; @@ -1468,9 +1477,9 @@ static int devlink_nl_cmd_eswitch_set_doit(struct sk_buff *skb, { struct devlink *devlink = info->user_ptr[0]; const struct devlink_ops *ops = devlink->ops; - u16 mode; - u8 inline_mode; + u8 inline_mode, encap_mode; int err = 0; + u16 mode; if (!ops) return -EOPNOTSUPP; @@ -1493,6 +1502,16 @@ static int devlink_nl_cmd_eswitch_set_doit(struct sk_buff *skb, if (err) return err; } + + if (info->attrs[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]) { + if (!ops->eswitch_encap_mode_set) + return -EOPNOTSUPP; + encap_mode = nla_get_u8(info->attrs[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]); + err = ops->eswitch_encap_mode_set(devlink, encap_mode); + if (err) + return err; + } + return 0; } @@ -2190,6 +2209,7 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = { [DEVLINK_ATTR_SB_TC_INDEX] = { .type = NLA_U16 }, [DEVLINK_ATTR_ESWITCH_MODE] = { .type = NLA_U16 }, [DEVLINK_ATTR_ESWITCH_INLINE_MODE] = { .type = NLA_U8 }, + [DEVLINK_ATTR_ESWITCH_ENCAP_MODE] = { .type = NLA_U8 }, [DEVLINK_ATTR_DPIPE_TABLE_NAME] = { .type = NLA_NUL_STRING }, [DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED] = { .type = NLA_U8 }, }; -- cgit From 1967ce6ea5c8a03e5f963b639fe33ece60b2dc91 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Wed, 15 Feb 2017 12:13:02 +0200 Subject: net/mlx5: E-Switch, Refactor fast path FDB table creation in switchdev mode Refactor the creation of the fast path FDB table that holds the offloaded rules in SRIOV switchdev mode into it's own function. This will be used in the next patch to be able and re-create the table under different settings without going through legacy mode. This patch doesn't change any functionality. Signed-off-by: Or Gerlitz Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 69 +++++++++++++++------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 992b380d36be..ce3a2c040706 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -426,31 +426,21 @@ out: return err; } -#define MAX_PF_SQ 256 #define ESW_OFFLOADS_NUM_GROUPS 4 -static int esw_create_offloads_fdb_table(struct mlx5_eswitch *esw, int nvports) +static int esw_create_offloads_fast_fdb_table(struct mlx5_eswitch *esw) { - int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); - struct mlx5_flow_table_attr ft_attr = {}; - int table_size, ix, esw_size, err = 0; struct mlx5_core_dev *dev = esw->dev; struct mlx5_flow_namespace *root_ns; struct mlx5_flow_table *fdb = NULL; - struct mlx5_flow_group *g; - u32 *flow_group_in; - void *match_criteria; + int esw_size, err = 0; u32 flags = 0; - flow_group_in = mlx5_vzalloc(inlen); - if (!flow_group_in) - return -ENOMEM; - root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB); if (!root_ns) { esw_warn(dev, "Failed to get FDB flow namespace\n"); err = -EOPNOTSUPP; - goto ns_err; + goto out; } esw_debug(dev, "Create offloads FDB table, min (max esw size(2^%d), max counters(%d)*groups(%d))\n", @@ -471,10 +461,49 @@ static int esw_create_offloads_fdb_table(struct mlx5_eswitch *esw, int nvports) if (IS_ERR(fdb)) { err = PTR_ERR(fdb); esw_warn(dev, "Failed to create Fast path FDB Table err %d\n", err); - goto fast_fdb_err; + goto out; } esw->fdb_table.fdb = fdb; +out: + return err; +} + +static void esw_destroy_offloads_fast_fdb_table(struct mlx5_eswitch *esw) +{ + mlx5_destroy_flow_table(esw->fdb_table.fdb); +} + +#define MAX_PF_SQ 256 + +static int esw_create_offloads_fdb_tables(struct mlx5_eswitch *esw, int nvports) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_table_attr ft_attr = {}; + struct mlx5_core_dev *dev = esw->dev; + struct mlx5_flow_namespace *root_ns; + struct mlx5_flow_table *fdb = NULL; + int table_size, ix, err = 0; + struct mlx5_flow_group *g; + void *match_criteria; + u32 *flow_group_in; + + esw_debug(esw->dev, "Create offloads FDB Tables\n"); + flow_group_in = mlx5_vzalloc(inlen); + if (!flow_group_in) + return -ENOMEM; + + root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB); + if (!root_ns) { + esw_warn(dev, "Failed to get FDB flow namespace\n"); + err = -EOPNOTSUPP; + goto ns_err; + } + + err = esw_create_offloads_fast_fdb_table(esw); + if (err) + goto fast_fdb_err; + table_size = nvports + MAX_PF_SQ + 1; ft_attr.max_fte = table_size; @@ -545,18 +574,18 @@ ns_err: return err; } -static void esw_destroy_offloads_fdb_table(struct mlx5_eswitch *esw) +static void esw_destroy_offloads_fdb_tables(struct mlx5_eswitch *esw) { if (!esw->fdb_table.fdb) return; - esw_debug(esw->dev, "Destroy offloads FDB Table\n"); + esw_debug(esw->dev, "Destroy offloads FDB Tables\n"); mlx5_del_flow_rules(esw->fdb_table.offloads.miss_rule); mlx5_destroy_flow_group(esw->fdb_table.offloads.send_to_vport_grp); mlx5_destroy_flow_group(esw->fdb_table.offloads.miss_grp); mlx5_destroy_flow_table(esw->fdb_table.offloads.fdb); - mlx5_destroy_flow_table(esw->fdb_table.fdb); + esw_destroy_offloads_fast_fdb_table(esw); } static int esw_create_offloads_table(struct mlx5_eswitch *esw) @@ -716,7 +745,7 @@ int esw_offloads_init(struct mlx5_eswitch *esw, int nvports) mlx5_remove_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB); mlx5_dev_list_unlock(); - err = esw_create_offloads_fdb_table(esw, nvports); + err = esw_create_offloads_fdb_tables(esw, nvports); if (err) goto create_fdb_err; @@ -753,7 +782,7 @@ create_fg_err: esw_destroy_offloads_table(esw); create_ft_err: - esw_destroy_offloads_fdb_table(esw); + esw_destroy_offloads_fdb_tables(esw); create_fdb_err: /* enable back PF RoCE */ @@ -799,7 +828,7 @@ void esw_offloads_cleanup(struct mlx5_eswitch *esw, int nvports) esw_destroy_vport_rx_group(esw); esw_destroy_offloads_table(esw); - esw_destroy_offloads_fdb_table(esw); + esw_destroy_offloads_fdb_tables(esw); } static int esw_mode_from_devlink(u16 mode, u16 *mlx5_mode) -- cgit From 7768d1971de676b0e12f57450ba7a6b38ff4cfb2 Mon Sep 17 00:00:00 2001 From: Roi Dayan Date: Sun, 25 Sep 2016 14:27:17 +0300 Subject: net/mlx5: E-Switch, Add control for encapsulation Implement the devlink e-switch encapsulation control set and get callbacks. Apply the value set by the user on the switchdev offloads mode when creating the fast FDB table where offloaded rules will be set. Signed-off-by: Roi Dayan Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 5 ++ drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 3 ++ .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 63 +++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx5/core/main.c | 2 + 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index b3281d1118b3..21bed3c3334d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1806,6 +1806,11 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) esw->enabled_vports = 0; esw->mode = SRIOV_NONE; esw->offloads.inline_mode = MLX5_INLINE_MODE_NONE; + if (MLX5_CAP_ESW_FLOWTABLE_FDB(dev, encap) && + MLX5_CAP_ESW_FLOWTABLE_FDB(dev, decap)) + esw->offloads.encap = DEVLINK_ESWITCH_ENCAP_MODE_BASIC; + else + esw->offloads.encap = DEVLINK_ESWITCH_ENCAP_MODE_NONE; dev->priv.eswitch = esw; return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 1f56ed9f5a6f..1e7f21be1233 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -210,6 +210,7 @@ struct mlx5_esw_offload { DECLARE_HASHTABLE(encap_tbl, 8); u8 inline_mode; u64 num_flows; + u8 encap; }; struct mlx5_eswitch { @@ -322,6 +323,8 @@ int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode); int mlx5_devlink_eswitch_inline_mode_set(struct devlink *devlink, u8 mode); int mlx5_devlink_eswitch_inline_mode_get(struct devlink *devlink, u8 *mode); int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, int nvfs, u8 *mode); +int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, u8 encap); +int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink, u8 *encap); void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw, int vport_index, struct mlx5_eswitch_rep *rep); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index ce3a2c040706..189d24dbd3e3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -450,8 +450,7 @@ static int esw_create_offloads_fast_fdb_table(struct mlx5_eswitch *esw) esw_size = min_t(int, MLX5_CAP_GEN(dev, max_flow_counter) * ESW_OFFLOADS_NUM_GROUPS, 1 << MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size)); - if (MLX5_CAP_ESW_FLOWTABLE_FDB(dev, encap) && - MLX5_CAP_ESW_FLOWTABLE_FDB(dev, decap)) + if (esw->offloads.encap != DEVLINK_ESWITCH_ENCAP_MODE_NONE) flags |= MLX5_FLOW_TABLE_TUNNEL_EN; fdb = mlx5_create_auto_grouped_flow_table(root_ns, FDB_FAST_PATH, @@ -1045,6 +1044,66 @@ int mlx5_eswitch_inline_mode_get(struct mlx5_eswitch *esw, int nvfs, u8 *mode) return 0; } +int mlx5_devlink_eswitch_encap_mode_set(struct devlink *devlink, u8 encap) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + struct mlx5_eswitch *esw = dev->priv.eswitch; + int err; + + if (!MLX5_CAP_GEN(dev, vport_group_manager)) + return -EOPNOTSUPP; + + if (esw->mode == SRIOV_NONE) + return -EOPNOTSUPP; + + if (encap != DEVLINK_ESWITCH_ENCAP_MODE_NONE && + (!MLX5_CAP_ESW_FLOWTABLE_FDB(dev, encap) || + !MLX5_CAP_ESW_FLOWTABLE_FDB(dev, decap))) + return -EOPNOTSUPP; + + if (encap && encap != DEVLINK_ESWITCH_ENCAP_MODE_BASIC) + return -EOPNOTSUPP; + + if (esw->mode == SRIOV_LEGACY) { + esw->offloads.encap = encap; + return 0; + } + + if (esw->offloads.encap == encap) + return 0; + + if (esw->offloads.num_flows > 0) { + esw_warn(dev, "Can't set encapsulation when flows are configured\n"); + return -EOPNOTSUPP; + } + + esw_destroy_offloads_fast_fdb_table(esw); + + esw->offloads.encap = encap; + err = esw_create_offloads_fast_fdb_table(esw); + if (err) { + esw_warn(esw->dev, "Failed re-creating fast FDB table, err %d\n", err); + esw->offloads.encap = !encap; + (void) esw_create_offloads_fast_fdb_table(esw); + } + return err; +} + +int mlx5_devlink_eswitch_encap_mode_get(struct devlink *devlink, u8 *encap) +{ + struct mlx5_core_dev *dev = devlink_priv(devlink); + struct mlx5_eswitch *esw = dev->priv.eswitch; + + if (!MLX5_CAP_GEN(dev, vport_group_manager)) + return -EOPNOTSUPP; + + if (esw->mode == SRIOV_NONE) + return -EOPNOTSUPP; + + *encap = esw->offloads.encap; + return 0; +} + void mlx5_eswitch_register_vport_rep(struct mlx5_eswitch *esw, int vport_index, struct mlx5_eswitch_rep *__rep) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 9c2bec732af9..bde91a8bec73 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1280,6 +1280,8 @@ static const struct devlink_ops mlx5_devlink_ops = { .eswitch_mode_get = mlx5_devlink_eswitch_mode_get, .eswitch_inline_mode_set = mlx5_devlink_eswitch_inline_mode_set, .eswitch_inline_mode_get = mlx5_devlink_eswitch_inline_mode_get, + .eswitch_encap_mode_set = mlx5_devlink_eswitch_encap_mode_set, + .eswitch_encap_mode_get = mlx5_devlink_eswitch_encap_mode_get, #endif }; -- cgit From a7082ef066f022c9bdeaae8d23ec2c119fecf7d9 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 21 Apr 2017 11:15:56 -0700 Subject: mlx5: hide unused functions Fix sparse warnings in recent ipoib support. The RDMA functions are not used yet, hide behind #ifdef. Based on comment, they will eventually be local so make static. Signed-off-by: Stephen Hemminger Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/ipoib.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c index ec78e637840f..3c84e36af018 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib.c @@ -178,7 +178,7 @@ static int mlx5i_init_tx(struct mlx5e_priv *priv) return 0; } -void mlx5i_cleanup_tx(struct mlx5e_priv *priv) +static void mlx5i_cleanup_tx(struct mlx5e_priv *priv) { struct mlx5i_priv *ipriv = priv->ppriv; @@ -359,9 +359,10 @@ unlock: return 0; } +#ifdef notusedyet /* IPoIB RDMA netdev callbacks */ -int mlx5i_attach_mcast(struct net_device *netdev, struct ib_device *hca, - union ib_gid *gid, u16 lid, int set_qkey) +static int mlx5i_attach_mcast(struct net_device *netdev, struct ib_device *hca, + union ib_gid *gid, u16 lid, int set_qkey) { struct mlx5e_priv *epriv = mlx5i_epriv(netdev); struct mlx5_core_dev *mdev = epriv->mdev; @@ -377,8 +378,8 @@ int mlx5i_attach_mcast(struct net_device *netdev, struct ib_device *hca, return err; } -int mlx5i_detach_mcast(struct net_device *netdev, struct ib_device *hca, - union ib_gid *gid, u16 lid) +static int mlx5i_detach_mcast(struct net_device *netdev, struct ib_device *hca, + union ib_gid *gid, u16 lid) { struct mlx5e_priv *epriv = mlx5i_epriv(netdev); struct mlx5_core_dev *mdev = epriv->mdev; @@ -395,7 +396,7 @@ int mlx5i_detach_mcast(struct net_device *netdev, struct ib_device *hca, return err; } -int mlx5i_xmit(struct net_device *dev, struct sk_buff *skb, +static int mlx5i_xmit(struct net_device *dev, struct sk_buff *skb, struct ib_ah *address, u32 dqpn, u32 dqkey) { struct mlx5e_priv *epriv = mlx5i_epriv(dev); @@ -404,6 +405,7 @@ int mlx5i_xmit(struct net_device *dev, struct sk_buff *skb, return mlx5i_sq_xmit(sq, skb, &mah->av, dqpn, dqkey); } +#endif static int mlx5i_check_required_hca_cap(struct mlx5_core_dev *mdev) { @@ -418,10 +420,10 @@ static int mlx5i_check_required_hca_cap(struct mlx5_core_dev *mdev) return 0; } -struct net_device *mlx5_rdma_netdev_alloc(struct mlx5_core_dev *mdev, - struct ib_device *ibdev, - const char *name, - void (*setup)(struct net_device *)) +static struct net_device *mlx5_rdma_netdev_alloc(struct mlx5_core_dev *mdev, + struct ib_device *ibdev, + const char *name, + void (*setup)(struct net_device *)) { const struct mlx5e_profile *profile = &mlx5i_nic_profile; int nch = profile->max_nch(mdev); @@ -480,7 +482,7 @@ free_mdev_resources: } EXPORT_SYMBOL(mlx5_rdma_netdev_alloc); -void mlx5_rdma_netdev_free(struct net_device *netdev) +static void mlx5_rdma_netdev_free(struct net_device *netdev) { struct mlx5e_priv *priv = mlx5i_epriv(netdev); const struct mlx5e_profile *profile = priv->profile; -- cgit From 8bf3198a5e394ed6815aeb8fedaf49722986bbd3 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 21 Apr 2017 11:15:57 -0700 Subject: mlx5: fix warning about missing prototype Fix sparse warning about missing prototypes. The rx/tx code path defines functions with prototypes in ipoib.h. Signed-off-by: Stephen Hemminger Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 43308243f519..ae66fad98244 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -39,6 +39,7 @@ #include "en.h" #include "en_tc.h" #include "eswitch.h" +#include "ipoib.h" static inline bool mlx5e_rx_hw_stamp(struct mlx5e_tstamp *tstamp) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index dda7db503043..ab3bb026ff9e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -33,6 +33,7 @@ #include #include #include "en.h" +#include "ipoib.h" #define MLX5E_SQ_NOPS_ROOM MLX5_SEND_WQE_MAX_WQEBBS #define MLX5E_SQ_STOP_ROOM (MLX5_SEND_WQE_MAX_WQEBBS +\ -- cgit From 6b3d4eec7f34c21df80191bfd72657404dad0f0a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 17 Apr 2017 18:25:07 -0700 Subject: sparc: Split BPF JIT into 32-bit and 64-bit. This is in preparation for adding the 64-bit eBPF JIT. Signed-off-by: David S. Miller --- arch/sparc/net/Makefile | 2 +- arch/sparc/net/bpf_jit.h | 68 ---- arch/sparc/net/bpf_jit_32.h | 68 ++++ arch/sparc/net/bpf_jit_asm.S | 208 ---------- arch/sparc/net/bpf_jit_asm_32.S | 208 ++++++++++ arch/sparc/net/bpf_jit_asm_64.S | 1 + arch/sparc/net/bpf_jit_comp.c | 815 --------------------------------------- arch/sparc/net/bpf_jit_comp_32.c | 815 +++++++++++++++++++++++++++++++++++++++ arch/sparc/net/bpf_jit_comp_64.c | 1 + 9 files changed, 1094 insertions(+), 1092 deletions(-) delete mode 100644 arch/sparc/net/bpf_jit.h create mode 100644 arch/sparc/net/bpf_jit_32.h delete mode 100644 arch/sparc/net/bpf_jit_asm.S create mode 100644 arch/sparc/net/bpf_jit_asm_32.S create mode 100644 arch/sparc/net/bpf_jit_asm_64.S delete mode 100644 arch/sparc/net/bpf_jit_comp.c create mode 100644 arch/sparc/net/bpf_jit_comp_32.c create mode 100644 arch/sparc/net/bpf_jit_comp_64.c diff --git a/arch/sparc/net/Makefile b/arch/sparc/net/Makefile index 1306a58ac541..76fa8e95b721 100644 --- a/arch/sparc/net/Makefile +++ b/arch/sparc/net/Makefile @@ -1,4 +1,4 @@ # # Arch-specific network modules # -obj-$(CONFIG_BPF_JIT) += bpf_jit_asm.o bpf_jit_comp.o +obj-$(CONFIG_BPF_JIT) += bpf_jit_asm_$(BITS).o bpf_jit_comp_$(BITS).o diff --git a/arch/sparc/net/bpf_jit.h b/arch/sparc/net/bpf_jit.h deleted file mode 100644 index 33d6b375ff12..000000000000 --- a/arch/sparc/net/bpf_jit.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef _BPF_JIT_H -#define _BPF_JIT_H - -/* Conventions: - * %g1 : temporary - * %g2 : Secondary temporary used by SKB data helper stubs. - * %g3 : packet offset passed into SKB data helper stubs. - * %o0 : pointer to skb (first argument given to JIT function) - * %o1 : BPF A accumulator - * %o2 : BPF X accumulator - * %o3 : Holds saved %o7 so we can call helper functions without needing - * to allocate a register window. - * %o4 : skb->len - skb->data_len - * %o5 : skb->data - */ - -#ifndef __ASSEMBLER__ -#define G0 0x00 -#define G1 0x01 -#define G3 0x03 -#define G6 0x06 -#define O0 0x08 -#define O1 0x09 -#define O2 0x0a -#define O3 0x0b -#define O4 0x0c -#define O5 0x0d -#define SP 0x0e -#define O7 0x0f -#define FP 0x1e - -#define r_SKB O0 -#define r_A O1 -#define r_X O2 -#define r_saved_O7 O3 -#define r_HEADLEN O4 -#define r_SKB_DATA O5 -#define r_TMP G1 -#define r_TMP2 G2 -#define r_OFF G3 - -/* assembly code in arch/sparc/net/bpf_jit_asm.S */ -extern u32 bpf_jit_load_word[]; -extern u32 bpf_jit_load_half[]; -extern u32 bpf_jit_load_byte[]; -extern u32 bpf_jit_load_byte_msh[]; -extern u32 bpf_jit_load_word_positive_offset[]; -extern u32 bpf_jit_load_half_positive_offset[]; -extern u32 bpf_jit_load_byte_positive_offset[]; -extern u32 bpf_jit_load_byte_msh_positive_offset[]; -extern u32 bpf_jit_load_word_negative_offset[]; -extern u32 bpf_jit_load_half_negative_offset[]; -extern u32 bpf_jit_load_byte_negative_offset[]; -extern u32 bpf_jit_load_byte_msh_negative_offset[]; - -#else -#define r_SKB %o0 -#define r_A %o1 -#define r_X %o2 -#define r_saved_O7 %o3 -#define r_HEADLEN %o4 -#define r_SKB_DATA %o5 -#define r_TMP %g1 -#define r_TMP2 %g2 -#define r_OFF %g3 -#endif - -#endif /* _BPF_JIT_H */ diff --git a/arch/sparc/net/bpf_jit_32.h b/arch/sparc/net/bpf_jit_32.h new file mode 100644 index 000000000000..33d6b375ff12 --- /dev/null +++ b/arch/sparc/net/bpf_jit_32.h @@ -0,0 +1,68 @@ +#ifndef _BPF_JIT_H +#define _BPF_JIT_H + +/* Conventions: + * %g1 : temporary + * %g2 : Secondary temporary used by SKB data helper stubs. + * %g3 : packet offset passed into SKB data helper stubs. + * %o0 : pointer to skb (first argument given to JIT function) + * %o1 : BPF A accumulator + * %o2 : BPF X accumulator + * %o3 : Holds saved %o7 so we can call helper functions without needing + * to allocate a register window. + * %o4 : skb->len - skb->data_len + * %o5 : skb->data + */ + +#ifndef __ASSEMBLER__ +#define G0 0x00 +#define G1 0x01 +#define G3 0x03 +#define G6 0x06 +#define O0 0x08 +#define O1 0x09 +#define O2 0x0a +#define O3 0x0b +#define O4 0x0c +#define O5 0x0d +#define SP 0x0e +#define O7 0x0f +#define FP 0x1e + +#define r_SKB O0 +#define r_A O1 +#define r_X O2 +#define r_saved_O7 O3 +#define r_HEADLEN O4 +#define r_SKB_DATA O5 +#define r_TMP G1 +#define r_TMP2 G2 +#define r_OFF G3 + +/* assembly code in arch/sparc/net/bpf_jit_asm.S */ +extern u32 bpf_jit_load_word[]; +extern u32 bpf_jit_load_half[]; +extern u32 bpf_jit_load_byte[]; +extern u32 bpf_jit_load_byte_msh[]; +extern u32 bpf_jit_load_word_positive_offset[]; +extern u32 bpf_jit_load_half_positive_offset[]; +extern u32 bpf_jit_load_byte_positive_offset[]; +extern u32 bpf_jit_load_byte_msh_positive_offset[]; +extern u32 bpf_jit_load_word_negative_offset[]; +extern u32 bpf_jit_load_half_negative_offset[]; +extern u32 bpf_jit_load_byte_negative_offset[]; +extern u32 bpf_jit_load_byte_msh_negative_offset[]; + +#else +#define r_SKB %o0 +#define r_A %o1 +#define r_X %o2 +#define r_saved_O7 %o3 +#define r_HEADLEN %o4 +#define r_SKB_DATA %o5 +#define r_TMP %g1 +#define r_TMP2 %g2 +#define r_OFF %g3 +#endif + +#endif /* _BPF_JIT_H */ diff --git a/arch/sparc/net/bpf_jit_asm.S b/arch/sparc/net/bpf_jit_asm.S deleted file mode 100644 index 8c83f4b8eb15..000000000000 --- a/arch/sparc/net/bpf_jit_asm.S +++ /dev/null @@ -1,208 +0,0 @@ -#include - -#include "bpf_jit.h" - -#ifdef CONFIG_SPARC64 -#define SAVE_SZ 176 -#define SCRATCH_OFF STACK_BIAS + 128 -#define BE_PTR(label) be,pn %xcc, label -#define SIGN_EXTEND(reg) sra reg, 0, reg -#else -#define SAVE_SZ 96 -#define SCRATCH_OFF 72 -#define BE_PTR(label) be label -#define SIGN_EXTEND(reg) -#endif - -#define SKF_MAX_NEG_OFF (-0x200000) /* SKF_LL_OFF from filter.h */ - - .text - .globl bpf_jit_load_word -bpf_jit_load_word: - cmp r_OFF, 0 - bl bpf_slow_path_word_neg - nop - .globl bpf_jit_load_word_positive_offset -bpf_jit_load_word_positive_offset: - sub r_HEADLEN, r_OFF, r_TMP - cmp r_TMP, 3 - ble bpf_slow_path_word - add r_SKB_DATA, r_OFF, r_TMP - andcc r_TMP, 3, %g0 - bne load_word_unaligned - nop - retl - ld [r_TMP], r_A -load_word_unaligned: - ldub [r_TMP + 0x0], r_OFF - ldub [r_TMP + 0x1], r_TMP2 - sll r_OFF, 8, r_OFF - or r_OFF, r_TMP2, r_OFF - ldub [r_TMP + 0x2], r_TMP2 - sll r_OFF, 8, r_OFF - or r_OFF, r_TMP2, r_OFF - ldub [r_TMP + 0x3], r_TMP2 - sll r_OFF, 8, r_OFF - retl - or r_OFF, r_TMP2, r_A - - .globl bpf_jit_load_half -bpf_jit_load_half: - cmp r_OFF, 0 - bl bpf_slow_path_half_neg - nop - .globl bpf_jit_load_half_positive_offset -bpf_jit_load_half_positive_offset: - sub r_HEADLEN, r_OFF, r_TMP - cmp r_TMP, 1 - ble bpf_slow_path_half - add r_SKB_DATA, r_OFF, r_TMP - andcc r_TMP, 1, %g0 - bne load_half_unaligned - nop - retl - lduh [r_TMP], r_A -load_half_unaligned: - ldub [r_TMP + 0x0], r_OFF - ldub [r_TMP + 0x1], r_TMP2 - sll r_OFF, 8, r_OFF - retl - or r_OFF, r_TMP2, r_A - - .globl bpf_jit_load_byte -bpf_jit_load_byte: - cmp r_OFF, 0 - bl bpf_slow_path_byte_neg - nop - .globl bpf_jit_load_byte_positive_offset -bpf_jit_load_byte_positive_offset: - cmp r_OFF, r_HEADLEN - bge bpf_slow_path_byte - nop - retl - ldub [r_SKB_DATA + r_OFF], r_A - - .globl bpf_jit_load_byte_msh -bpf_jit_load_byte_msh: - cmp r_OFF, 0 - bl bpf_slow_path_byte_msh_neg - nop - .globl bpf_jit_load_byte_msh_positive_offset -bpf_jit_load_byte_msh_positive_offset: - cmp r_OFF, r_HEADLEN - bge bpf_slow_path_byte_msh - nop - ldub [r_SKB_DATA + r_OFF], r_OFF - and r_OFF, 0xf, r_OFF - retl - sll r_OFF, 2, r_X - -#define bpf_slow_path_common(LEN) \ - save %sp, -SAVE_SZ, %sp; \ - mov %i0, %o0; \ - mov r_OFF, %o1; \ - add %fp, SCRATCH_OFF, %o2; \ - call skb_copy_bits; \ - mov (LEN), %o3; \ - cmp %o0, 0; \ - restore; - -bpf_slow_path_word: - bpf_slow_path_common(4) - bl bpf_error - ld [%sp + SCRATCH_OFF], r_A - retl - nop -bpf_slow_path_half: - bpf_slow_path_common(2) - bl bpf_error - lduh [%sp + SCRATCH_OFF], r_A - retl - nop -bpf_slow_path_byte: - bpf_slow_path_common(1) - bl bpf_error - ldub [%sp + SCRATCH_OFF], r_A - retl - nop -bpf_slow_path_byte_msh: - bpf_slow_path_common(1) - bl bpf_error - ldub [%sp + SCRATCH_OFF], r_A - and r_OFF, 0xf, r_OFF - retl - sll r_OFF, 2, r_X - -#define bpf_negative_common(LEN) \ - save %sp, -SAVE_SZ, %sp; \ - mov %i0, %o0; \ - mov r_OFF, %o1; \ - SIGN_EXTEND(%o1); \ - call bpf_internal_load_pointer_neg_helper; \ - mov (LEN), %o2; \ - mov %o0, r_TMP; \ - cmp %o0, 0; \ - BE_PTR(bpf_error); \ - restore; - -bpf_slow_path_word_neg: - sethi %hi(SKF_MAX_NEG_OFF), r_TMP - cmp r_OFF, r_TMP - bl bpf_error - nop - .globl bpf_jit_load_word_negative_offset -bpf_jit_load_word_negative_offset: - bpf_negative_common(4) - andcc r_TMP, 3, %g0 - bne load_word_unaligned - nop - retl - ld [r_TMP], r_A - -bpf_slow_path_half_neg: - sethi %hi(SKF_MAX_NEG_OFF), r_TMP - cmp r_OFF, r_TMP - bl bpf_error - nop - .globl bpf_jit_load_half_negative_offset -bpf_jit_load_half_negative_offset: - bpf_negative_common(2) - andcc r_TMP, 1, %g0 - bne load_half_unaligned - nop - retl - lduh [r_TMP], r_A - -bpf_slow_path_byte_neg: - sethi %hi(SKF_MAX_NEG_OFF), r_TMP - cmp r_OFF, r_TMP - bl bpf_error - nop - .globl bpf_jit_load_byte_negative_offset -bpf_jit_load_byte_negative_offset: - bpf_negative_common(1) - retl - ldub [r_TMP], r_A - -bpf_slow_path_byte_msh_neg: - sethi %hi(SKF_MAX_NEG_OFF), r_TMP - cmp r_OFF, r_TMP - bl bpf_error - nop - .globl bpf_jit_load_byte_msh_negative_offset -bpf_jit_load_byte_msh_negative_offset: - bpf_negative_common(1) - ldub [r_TMP], r_OFF - and r_OFF, 0xf, r_OFF - retl - sll r_OFF, 2, r_X - -bpf_error: - /* Make the JIT program return zero. The JIT epilogue - * stores away the original %o7 into r_saved_O7. The - * normal leaf function return is to use "retl" which - * would evalute to "jmpl %o7 + 8, %g0" but we want to - * use the saved value thus the sequence you see here. - */ - jmpl r_saved_O7 + 8, %g0 - clr %o0 diff --git a/arch/sparc/net/bpf_jit_asm_32.S b/arch/sparc/net/bpf_jit_asm_32.S new file mode 100644 index 000000000000..5632cdc922b1 --- /dev/null +++ b/arch/sparc/net/bpf_jit_asm_32.S @@ -0,0 +1,208 @@ +#include + +#include "bpf_jit_32.h" + +#ifdef CONFIG_SPARC64 +#define SAVE_SZ 176 +#define SCRATCH_OFF STACK_BIAS + 128 +#define BE_PTR(label) be,pn %xcc, label +#define SIGN_EXTEND(reg) sra reg, 0, reg +#else +#define SAVE_SZ 96 +#define SCRATCH_OFF 72 +#define BE_PTR(label) be label +#define SIGN_EXTEND(reg) +#endif + +#define SKF_MAX_NEG_OFF (-0x200000) /* SKF_LL_OFF from filter.h */ + + .text + .globl bpf_jit_load_word +bpf_jit_load_word: + cmp r_OFF, 0 + bl bpf_slow_path_word_neg + nop + .globl bpf_jit_load_word_positive_offset +bpf_jit_load_word_positive_offset: + sub r_HEADLEN, r_OFF, r_TMP + cmp r_TMP, 3 + ble bpf_slow_path_word + add r_SKB_DATA, r_OFF, r_TMP + andcc r_TMP, 3, %g0 + bne load_word_unaligned + nop + retl + ld [r_TMP], r_A +load_word_unaligned: + ldub [r_TMP + 0x0], r_OFF + ldub [r_TMP + 0x1], r_TMP2 + sll r_OFF, 8, r_OFF + or r_OFF, r_TMP2, r_OFF + ldub [r_TMP + 0x2], r_TMP2 + sll r_OFF, 8, r_OFF + or r_OFF, r_TMP2, r_OFF + ldub [r_TMP + 0x3], r_TMP2 + sll r_OFF, 8, r_OFF + retl + or r_OFF, r_TMP2, r_A + + .globl bpf_jit_load_half +bpf_jit_load_half: + cmp r_OFF, 0 + bl bpf_slow_path_half_neg + nop + .globl bpf_jit_load_half_positive_offset +bpf_jit_load_half_positive_offset: + sub r_HEADLEN, r_OFF, r_TMP + cmp r_TMP, 1 + ble bpf_slow_path_half + add r_SKB_DATA, r_OFF, r_TMP + andcc r_TMP, 1, %g0 + bne load_half_unaligned + nop + retl + lduh [r_TMP], r_A +load_half_unaligned: + ldub [r_TMP + 0x0], r_OFF + ldub [r_TMP + 0x1], r_TMP2 + sll r_OFF, 8, r_OFF + retl + or r_OFF, r_TMP2, r_A + + .globl bpf_jit_load_byte +bpf_jit_load_byte: + cmp r_OFF, 0 + bl bpf_slow_path_byte_neg + nop + .globl bpf_jit_load_byte_positive_offset +bpf_jit_load_byte_positive_offset: + cmp r_OFF, r_HEADLEN + bge bpf_slow_path_byte + nop + retl + ldub [r_SKB_DATA + r_OFF], r_A + + .globl bpf_jit_load_byte_msh +bpf_jit_load_byte_msh: + cmp r_OFF, 0 + bl bpf_slow_path_byte_msh_neg + nop + .globl bpf_jit_load_byte_msh_positive_offset +bpf_jit_load_byte_msh_positive_offset: + cmp r_OFF, r_HEADLEN + bge bpf_slow_path_byte_msh + nop + ldub [r_SKB_DATA + r_OFF], r_OFF + and r_OFF, 0xf, r_OFF + retl + sll r_OFF, 2, r_X + +#define bpf_slow_path_common(LEN) \ + save %sp, -SAVE_SZ, %sp; \ + mov %i0, %o0; \ + mov r_OFF, %o1; \ + add %fp, SCRATCH_OFF, %o2; \ + call skb_copy_bits; \ + mov (LEN), %o3; \ + cmp %o0, 0; \ + restore; + +bpf_slow_path_word: + bpf_slow_path_common(4) + bl bpf_error + ld [%sp + SCRATCH_OFF], r_A + retl + nop +bpf_slow_path_half: + bpf_slow_path_common(2) + bl bpf_error + lduh [%sp + SCRATCH_OFF], r_A + retl + nop +bpf_slow_path_byte: + bpf_slow_path_common(1) + bl bpf_error + ldub [%sp + SCRATCH_OFF], r_A + retl + nop +bpf_slow_path_byte_msh: + bpf_slow_path_common(1) + bl bpf_error + ldub [%sp + SCRATCH_OFF], r_A + and r_OFF, 0xf, r_OFF + retl + sll r_OFF, 2, r_X + +#define bpf_negative_common(LEN) \ + save %sp, -SAVE_SZ, %sp; \ + mov %i0, %o0; \ + mov r_OFF, %o1; \ + SIGN_EXTEND(%o1); \ + call bpf_internal_load_pointer_neg_helper; \ + mov (LEN), %o2; \ + mov %o0, r_TMP; \ + cmp %o0, 0; \ + BE_PTR(bpf_error); \ + restore; + +bpf_slow_path_word_neg: + sethi %hi(SKF_MAX_NEG_OFF), r_TMP + cmp r_OFF, r_TMP + bl bpf_error + nop + .globl bpf_jit_load_word_negative_offset +bpf_jit_load_word_negative_offset: + bpf_negative_common(4) + andcc r_TMP, 3, %g0 + bne load_word_unaligned + nop + retl + ld [r_TMP], r_A + +bpf_slow_path_half_neg: + sethi %hi(SKF_MAX_NEG_OFF), r_TMP + cmp r_OFF, r_TMP + bl bpf_error + nop + .globl bpf_jit_load_half_negative_offset +bpf_jit_load_half_negative_offset: + bpf_negative_common(2) + andcc r_TMP, 1, %g0 + bne load_half_unaligned + nop + retl + lduh [r_TMP], r_A + +bpf_slow_path_byte_neg: + sethi %hi(SKF_MAX_NEG_OFF), r_TMP + cmp r_OFF, r_TMP + bl bpf_error + nop + .globl bpf_jit_load_byte_negative_offset +bpf_jit_load_byte_negative_offset: + bpf_negative_common(1) + retl + ldub [r_TMP], r_A + +bpf_slow_path_byte_msh_neg: + sethi %hi(SKF_MAX_NEG_OFF), r_TMP + cmp r_OFF, r_TMP + bl bpf_error + nop + .globl bpf_jit_load_byte_msh_negative_offset +bpf_jit_load_byte_msh_negative_offset: + bpf_negative_common(1) + ldub [r_TMP], r_OFF + and r_OFF, 0xf, r_OFF + retl + sll r_OFF, 2, r_X + +bpf_error: + /* Make the JIT program return zero. The JIT epilogue + * stores away the original %o7 into r_saved_O7. The + * normal leaf function return is to use "retl" which + * would evalute to "jmpl %o7 + 8, %g0" but we want to + * use the saved value thus the sequence you see here. + */ + jmpl r_saved_O7 + 8, %g0 + clr %o0 diff --git a/arch/sparc/net/bpf_jit_asm_64.S b/arch/sparc/net/bpf_jit_asm_64.S new file mode 100644 index 000000000000..6fb023f9cd99 --- /dev/null +++ b/arch/sparc/net/bpf_jit_asm_64.S @@ -0,0 +1 @@ +#include "bpf_jit_asm_32.S" diff --git a/arch/sparc/net/bpf_jit_comp.c b/arch/sparc/net/bpf_jit_comp.c deleted file mode 100644 index a6d9204a6a0b..000000000000 --- a/arch/sparc/net/bpf_jit_comp.c +++ /dev/null @@ -1,815 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "bpf_jit.h" - -int bpf_jit_enable __read_mostly; - -static inline bool is_simm13(unsigned int value) -{ - return value + 0x1000 < 0x2000; -} - -static void bpf_flush_icache(void *start_, void *end_) -{ -#ifdef CONFIG_SPARC64 - /* Cheetah's I-cache is fully coherent. */ - if (tlb_type == spitfire) { - unsigned long start = (unsigned long) start_; - unsigned long end = (unsigned long) end_; - - start &= ~7UL; - end = (end + 7UL) & ~7UL; - while (start < end) { - flushi(start); - start += 32; - } - } -#endif -} - -#define SEEN_DATAREF 1 /* might call external helpers */ -#define SEEN_XREG 2 /* ebx is used */ -#define SEEN_MEM 4 /* use mem[] for temporary storage */ - -#define S13(X) ((X) & 0x1fff) -#define IMMED 0x00002000 -#define RD(X) ((X) << 25) -#define RS1(X) ((X) << 14) -#define RS2(X) ((X)) -#define OP(X) ((X) << 30) -#define OP2(X) ((X) << 22) -#define OP3(X) ((X) << 19) -#define COND(X) ((X) << 25) -#define F1(X) OP(X) -#define F2(X, Y) (OP(X) | OP2(Y)) -#define F3(X, Y) (OP(X) | OP3(Y)) - -#define CONDN COND(0x0) -#define CONDE COND(0x1) -#define CONDLE COND(0x2) -#define CONDL COND(0x3) -#define CONDLEU COND(0x4) -#define CONDCS COND(0x5) -#define CONDNEG COND(0x6) -#define CONDVC COND(0x7) -#define CONDA COND(0x8) -#define CONDNE COND(0x9) -#define CONDG COND(0xa) -#define CONDGE COND(0xb) -#define CONDGU COND(0xc) -#define CONDCC COND(0xd) -#define CONDPOS COND(0xe) -#define CONDVS COND(0xf) - -#define CONDGEU CONDCC -#define CONDLU CONDCS - -#define WDISP22(X) (((X) >> 2) & 0x3fffff) - -#define BA (F2(0, 2) | CONDA) -#define BGU (F2(0, 2) | CONDGU) -#define BLEU (F2(0, 2) | CONDLEU) -#define BGEU (F2(0, 2) | CONDGEU) -#define BLU (F2(0, 2) | CONDLU) -#define BE (F2(0, 2) | CONDE) -#define BNE (F2(0, 2) | CONDNE) - -#ifdef CONFIG_SPARC64 -#define BE_PTR (F2(0, 1) | CONDE | (2 << 20)) -#else -#define BE_PTR BE -#endif - -#define SETHI(K, REG) \ - (F2(0, 0x4) | RD(REG) | (((K) >> 10) & 0x3fffff)) -#define OR_LO(K, REG) \ - (F3(2, 0x02) | IMMED | RS1(REG) | ((K) & 0x3ff) | RD(REG)) - -#define ADD F3(2, 0x00) -#define AND F3(2, 0x01) -#define ANDCC F3(2, 0x11) -#define OR F3(2, 0x02) -#define XOR F3(2, 0x03) -#define SUB F3(2, 0x04) -#define SUBCC F3(2, 0x14) -#define MUL F3(2, 0x0a) /* umul */ -#define DIV F3(2, 0x0e) /* udiv */ -#define SLL F3(2, 0x25) -#define SRL F3(2, 0x26) -#define JMPL F3(2, 0x38) -#define CALL F1(1) -#define BR F2(0, 0x01) -#define RD_Y F3(2, 0x28) -#define WR_Y F3(2, 0x30) - -#define LD32 F3(3, 0x00) -#define LD8 F3(3, 0x01) -#define LD16 F3(3, 0x02) -#define LD64 F3(3, 0x0b) -#define ST32 F3(3, 0x04) - -#ifdef CONFIG_SPARC64 -#define LDPTR LD64 -#define BASE_STACKFRAME 176 -#else -#define LDPTR LD32 -#define BASE_STACKFRAME 96 -#endif - -#define LD32I (LD32 | IMMED) -#define LD8I (LD8 | IMMED) -#define LD16I (LD16 | IMMED) -#define LD64I (LD64 | IMMED) -#define LDPTRI (LDPTR | IMMED) -#define ST32I (ST32 | IMMED) - -#define emit_nop() \ -do { \ - *prog++ = SETHI(0, G0); \ -} while (0) - -#define emit_neg() \ -do { /* sub %g0, r_A, r_A */ \ - *prog++ = SUB | RS1(G0) | RS2(r_A) | RD(r_A); \ -} while (0) - -#define emit_reg_move(FROM, TO) \ -do { /* or %g0, FROM, TO */ \ - *prog++ = OR | RS1(G0) | RS2(FROM) | RD(TO); \ -} while (0) - -#define emit_clear(REG) \ -do { /* or %g0, %g0, REG */ \ - *prog++ = OR | RS1(G0) | RS2(G0) | RD(REG); \ -} while (0) - -#define emit_set_const(K, REG) \ -do { /* sethi %hi(K), REG */ \ - *prog++ = SETHI(K, REG); \ - /* or REG, %lo(K), REG */ \ - *prog++ = OR_LO(K, REG); \ -} while (0) - - /* Emit - * - * OP r_A, r_X, r_A - */ -#define emit_alu_X(OPCODE) \ -do { \ - seen |= SEEN_XREG; \ - *prog++ = OPCODE | RS1(r_A) | RS2(r_X) | RD(r_A); \ -} while (0) - - /* Emit either: - * - * OP r_A, K, r_A - * - * or - * - * sethi %hi(K), r_TMP - * or r_TMP, %lo(K), r_TMP - * OP r_A, r_TMP, r_A - * - * depending upon whether K fits in a signed 13-bit - * immediate instruction field. Emit nothing if K - * is zero. - */ -#define emit_alu_K(OPCODE, K) \ -do { \ - if (K || OPCODE == AND || OPCODE == MUL) { \ - unsigned int _insn = OPCODE; \ - _insn |= RS1(r_A) | RD(r_A); \ - if (is_simm13(K)) { \ - *prog++ = _insn | IMMED | S13(K); \ - } else { \ - emit_set_const(K, r_TMP); \ - *prog++ = _insn | RS2(r_TMP); \ - } \ - } \ -} while (0) - -#define emit_loadimm(K, DEST) \ -do { \ - if (is_simm13(K)) { \ - /* or %g0, K, DEST */ \ - *prog++ = OR | IMMED | RS1(G0) | S13(K) | RD(DEST); \ - } else { \ - emit_set_const(K, DEST); \ - } \ -} while (0) - -#define emit_loadptr(BASE, STRUCT, FIELD, DEST) \ -do { unsigned int _off = offsetof(STRUCT, FIELD); \ - BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(void *)); \ - *prog++ = LDPTRI | RS1(BASE) | S13(_off) | RD(DEST); \ -} while (0) - -#define emit_load32(BASE, STRUCT, FIELD, DEST) \ -do { unsigned int _off = offsetof(STRUCT, FIELD); \ - BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u32)); \ - *prog++ = LD32I | RS1(BASE) | S13(_off) | RD(DEST); \ -} while (0) - -#define emit_load16(BASE, STRUCT, FIELD, DEST) \ -do { unsigned int _off = offsetof(STRUCT, FIELD); \ - BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u16)); \ - *prog++ = LD16I | RS1(BASE) | S13(_off) | RD(DEST); \ -} while (0) - -#define __emit_load8(BASE, STRUCT, FIELD, DEST) \ -do { unsigned int _off = offsetof(STRUCT, FIELD); \ - *prog++ = LD8I | RS1(BASE) | S13(_off) | RD(DEST); \ -} while (0) - -#define emit_load8(BASE, STRUCT, FIELD, DEST) \ -do { BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u8)); \ - __emit_load8(BASE, STRUCT, FIELD, DEST); \ -} while (0) - -#ifdef CONFIG_SPARC64 -#define BIAS (STACK_BIAS - 4) -#else -#define BIAS (-4) -#endif - -#define emit_ldmem(OFF, DEST) \ -do { *prog++ = LD32I | RS1(SP) | S13(BIAS - (OFF)) | RD(DEST); \ -} while (0) - -#define emit_stmem(OFF, SRC) \ -do { *prog++ = ST32I | RS1(SP) | S13(BIAS - (OFF)) | RD(SRC); \ -} while (0) - -#ifdef CONFIG_SMP -#ifdef CONFIG_SPARC64 -#define emit_load_cpu(REG) \ - emit_load16(G6, struct thread_info, cpu, REG) -#else -#define emit_load_cpu(REG) \ - emit_load32(G6, struct thread_info, cpu, REG) -#endif -#else -#define emit_load_cpu(REG) emit_clear(REG) -#endif - -#define emit_skb_loadptr(FIELD, DEST) \ - emit_loadptr(r_SKB, struct sk_buff, FIELD, DEST) -#define emit_skb_load32(FIELD, DEST) \ - emit_load32(r_SKB, struct sk_buff, FIELD, DEST) -#define emit_skb_load16(FIELD, DEST) \ - emit_load16(r_SKB, struct sk_buff, FIELD, DEST) -#define __emit_skb_load8(FIELD, DEST) \ - __emit_load8(r_SKB, struct sk_buff, FIELD, DEST) -#define emit_skb_load8(FIELD, DEST) \ - emit_load8(r_SKB, struct sk_buff, FIELD, DEST) - -#define emit_jmpl(BASE, IMM_OFF, LREG) \ - *prog++ = (JMPL | IMMED | RS1(BASE) | S13(IMM_OFF) | RD(LREG)) - -#define emit_call(FUNC) \ -do { void *_here = image + addrs[i] - 8; \ - unsigned int _off = (void *)(FUNC) - _here; \ - *prog++ = CALL | (((_off) >> 2) & 0x3fffffff); \ - emit_nop(); \ -} while (0) - -#define emit_branch(BR_OPC, DEST) \ -do { unsigned int _here = addrs[i] - 8; \ - *prog++ = BR_OPC | WDISP22((DEST) - _here); \ -} while (0) - -#define emit_branch_off(BR_OPC, OFF) \ -do { *prog++ = BR_OPC | WDISP22(OFF); \ -} while (0) - -#define emit_jump(DEST) emit_branch(BA, DEST) - -#define emit_read_y(REG) *prog++ = RD_Y | RD(REG) -#define emit_write_y(REG) *prog++ = WR_Y | IMMED | RS1(REG) | S13(0) - -#define emit_cmp(R1, R2) \ - *prog++ = (SUBCC | RS1(R1) | RS2(R2) | RD(G0)) - -#define emit_cmpi(R1, IMM) \ - *prog++ = (SUBCC | IMMED | RS1(R1) | S13(IMM) | RD(G0)); - -#define emit_btst(R1, R2) \ - *prog++ = (ANDCC | RS1(R1) | RS2(R2) | RD(G0)) - -#define emit_btsti(R1, IMM) \ - *prog++ = (ANDCC | IMMED | RS1(R1) | S13(IMM) | RD(G0)); - -#define emit_sub(R1, R2, R3) \ - *prog++ = (SUB | RS1(R1) | RS2(R2) | RD(R3)) - -#define emit_subi(R1, IMM, R3) \ - *prog++ = (SUB | IMMED | RS1(R1) | S13(IMM) | RD(R3)) - -#define emit_add(R1, R2, R3) \ - *prog++ = (ADD | RS1(R1) | RS2(R2) | RD(R3)) - -#define emit_addi(R1, IMM, R3) \ - *prog++ = (ADD | IMMED | RS1(R1) | S13(IMM) | RD(R3)) - -#define emit_and(R1, R2, R3) \ - *prog++ = (AND | RS1(R1) | RS2(R2) | RD(R3)) - -#define emit_andi(R1, IMM, R3) \ - *prog++ = (AND | IMMED | RS1(R1) | S13(IMM) | RD(R3)) - -#define emit_alloc_stack(SZ) \ - *prog++ = (SUB | IMMED | RS1(SP) | S13(SZ) | RD(SP)) - -#define emit_release_stack(SZ) \ - *prog++ = (ADD | IMMED | RS1(SP) | S13(SZ) | RD(SP)) - -/* A note about branch offset calculations. The addrs[] array, - * indexed by BPF instruction, records the address after all the - * sparc instructions emitted for that BPF instruction. - * - * The most common case is to emit a branch at the end of such - * a code sequence. So this would be two instructions, the - * branch and it's delay slot. - * - * Therefore by default the branch emitters calculate the branch - * offset field as: - * - * destination - (addrs[i] - 8) - * - * This "addrs[i] - 8" is the address of the branch itself or - * what "." would be in assembler notation. The "8" part is - * how we take into consideration the branch and it's delay - * slot mentioned above. - * - * Sometimes we need to emit a branch earlier in the code - * sequence. And in these situations we adjust "destination" - * to accommodate this difference. For example, if we needed - * to emit a branch (and it's delay slot) right before the - * final instruction emitted for a BPF opcode, we'd use - * "destination + 4" instead of just plain "destination" above. - * - * This is why you see all of these funny emit_branch() and - * emit_jump() calls with adjusted offsets. - */ - -void bpf_jit_compile(struct bpf_prog *fp) -{ - unsigned int cleanup_addr, proglen, oldproglen = 0; - u32 temp[8], *prog, *func, seen = 0, pass; - const struct sock_filter *filter = fp->insns; - int i, flen = fp->len, pc_ret0 = -1; - unsigned int *addrs; - void *image; - - if (!bpf_jit_enable) - return; - - addrs = kmalloc(flen * sizeof(*addrs), GFP_KERNEL); - if (addrs == NULL) - return; - - /* Before first pass, make a rough estimation of addrs[] - * each bpf instruction is translated to less than 64 bytes - */ - for (proglen = 0, i = 0; i < flen; i++) { - proglen += 64; - addrs[i] = proglen; - } - cleanup_addr = proglen; /* epilogue address */ - image = NULL; - for (pass = 0; pass < 10; pass++) { - u8 seen_or_pass0 = (pass == 0) ? (SEEN_XREG | SEEN_DATAREF | SEEN_MEM) : seen; - - /* no prologue/epilogue for trivial filters (RET something) */ - proglen = 0; - prog = temp; - - /* Prologue */ - if (seen_or_pass0) { - if (seen_or_pass0 & SEEN_MEM) { - unsigned int sz = BASE_STACKFRAME; - sz += BPF_MEMWORDS * sizeof(u32); - emit_alloc_stack(sz); - } - - /* Make sure we dont leek kernel memory. */ - if (seen_or_pass0 & SEEN_XREG) - emit_clear(r_X); - - /* If this filter needs to access skb data, - * load %o4 and %o5 with: - * %o4 = skb->len - skb->data_len - * %o5 = skb->data - * And also back up %o7 into r_saved_O7 so we can - * invoke the stubs using 'call'. - */ - if (seen_or_pass0 & SEEN_DATAREF) { - emit_load32(r_SKB, struct sk_buff, len, r_HEADLEN); - emit_load32(r_SKB, struct sk_buff, data_len, r_TMP); - emit_sub(r_HEADLEN, r_TMP, r_HEADLEN); - emit_loadptr(r_SKB, struct sk_buff, data, r_SKB_DATA); - } - } - emit_reg_move(O7, r_saved_O7); - - /* Make sure we dont leak kernel information to the user. */ - if (bpf_needs_clear_a(&filter[0])) - emit_clear(r_A); /* A = 0 */ - - for (i = 0; i < flen; i++) { - unsigned int K = filter[i].k; - unsigned int t_offset; - unsigned int f_offset; - u32 t_op, f_op; - u16 code = bpf_anc_helper(&filter[i]); - int ilen; - - switch (code) { - case BPF_ALU | BPF_ADD | BPF_X: /* A += X; */ - emit_alu_X(ADD); - break; - case BPF_ALU | BPF_ADD | BPF_K: /* A += K; */ - emit_alu_K(ADD, K); - break; - case BPF_ALU | BPF_SUB | BPF_X: /* A -= X; */ - emit_alu_X(SUB); - break; - case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */ - emit_alu_K(SUB, K); - break; - case BPF_ALU | BPF_AND | BPF_X: /* A &= X */ - emit_alu_X(AND); - break; - case BPF_ALU | BPF_AND | BPF_K: /* A &= K */ - emit_alu_K(AND, K); - break; - case BPF_ALU | BPF_OR | BPF_X: /* A |= X */ - emit_alu_X(OR); - break; - case BPF_ALU | BPF_OR | BPF_K: /* A |= K */ - emit_alu_K(OR, K); - break; - case BPF_ANC | SKF_AD_ALU_XOR_X: /* A ^= X; */ - case BPF_ALU | BPF_XOR | BPF_X: - emit_alu_X(XOR); - break; - case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K */ - emit_alu_K(XOR, K); - break; - case BPF_ALU | BPF_LSH | BPF_X: /* A <<= X */ - emit_alu_X(SLL); - break; - case BPF_ALU | BPF_LSH | BPF_K: /* A <<= K */ - emit_alu_K(SLL, K); - break; - case BPF_ALU | BPF_RSH | BPF_X: /* A >>= X */ - emit_alu_X(SRL); - break; - case BPF_ALU | BPF_RSH | BPF_K: /* A >>= K */ - emit_alu_K(SRL, K); - break; - case BPF_ALU | BPF_MUL | BPF_X: /* A *= X; */ - emit_alu_X(MUL); - break; - case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */ - emit_alu_K(MUL, K); - break; - case BPF_ALU | BPF_DIV | BPF_K: /* A /= K with K != 0*/ - if (K == 1) - break; - emit_write_y(G0); -#ifdef CONFIG_SPARC32 - /* The Sparc v8 architecture requires - * three instructions between a %y - * register write and the first use. - */ - emit_nop(); - emit_nop(); - emit_nop(); -#endif - emit_alu_K(DIV, K); - break; - case BPF_ALU | BPF_DIV | BPF_X: /* A /= X; */ - emit_cmpi(r_X, 0); - if (pc_ret0 > 0) { - t_offset = addrs[pc_ret0 - 1]; -#ifdef CONFIG_SPARC32 - emit_branch(BE, t_offset + 20); -#else - emit_branch(BE, t_offset + 8); -#endif - emit_nop(); /* delay slot */ - } else { - emit_branch_off(BNE, 16); - emit_nop(); -#ifdef CONFIG_SPARC32 - emit_jump(cleanup_addr + 20); -#else - emit_jump(cleanup_addr + 8); -#endif - emit_clear(r_A); - } - emit_write_y(G0); -#ifdef CONFIG_SPARC32 - /* The Sparc v8 architecture requires - * three instructions between a %y - * register write and the first use. - */ - emit_nop(); - emit_nop(); - emit_nop(); -#endif - emit_alu_X(DIV); - break; - case BPF_ALU | BPF_NEG: - emit_neg(); - break; - case BPF_RET | BPF_K: - if (!K) { - if (pc_ret0 == -1) - pc_ret0 = i; - emit_clear(r_A); - } else { - emit_loadimm(K, r_A); - } - /* Fallthrough */ - case BPF_RET | BPF_A: - if (seen_or_pass0) { - if (i != flen - 1) { - emit_jump(cleanup_addr); - emit_nop(); - break; - } - if (seen_or_pass0 & SEEN_MEM) { - unsigned int sz = BASE_STACKFRAME; - sz += BPF_MEMWORDS * sizeof(u32); - emit_release_stack(sz); - } - } - /* jmpl %r_saved_O7 + 8, %g0 */ - emit_jmpl(r_saved_O7, 8, G0); - emit_reg_move(r_A, O0); /* delay slot */ - break; - case BPF_MISC | BPF_TAX: - seen |= SEEN_XREG; - emit_reg_move(r_A, r_X); - break; - case BPF_MISC | BPF_TXA: - seen |= SEEN_XREG; - emit_reg_move(r_X, r_A); - break; - case BPF_ANC | SKF_AD_CPU: - emit_load_cpu(r_A); - break; - case BPF_ANC | SKF_AD_PROTOCOL: - emit_skb_load16(protocol, r_A); - break; - case BPF_ANC | SKF_AD_PKTTYPE: - __emit_skb_load8(__pkt_type_offset, r_A); - emit_andi(r_A, PKT_TYPE_MAX, r_A); - emit_alu_K(SRL, 5); - break; - case BPF_ANC | SKF_AD_IFINDEX: - emit_skb_loadptr(dev, r_A); - emit_cmpi(r_A, 0); - emit_branch(BE_PTR, cleanup_addr + 4); - emit_nop(); - emit_load32(r_A, struct net_device, ifindex, r_A); - break; - case BPF_ANC | SKF_AD_MARK: - emit_skb_load32(mark, r_A); - break; - case BPF_ANC | SKF_AD_QUEUE: - emit_skb_load16(queue_mapping, r_A); - break; - case BPF_ANC | SKF_AD_HATYPE: - emit_skb_loadptr(dev, r_A); - emit_cmpi(r_A, 0); - emit_branch(BE_PTR, cleanup_addr + 4); - emit_nop(); - emit_load16(r_A, struct net_device, type, r_A); - break; - case BPF_ANC | SKF_AD_RXHASH: - emit_skb_load32(hash, r_A); - break; - case BPF_ANC | SKF_AD_VLAN_TAG: - case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT: - emit_skb_load16(vlan_tci, r_A); - if (code != (BPF_ANC | SKF_AD_VLAN_TAG)) { - emit_alu_K(SRL, 12); - emit_andi(r_A, 1, r_A); - } else { - emit_loadimm(~VLAN_TAG_PRESENT, r_TMP); - emit_and(r_A, r_TMP, r_A); - } - break; - case BPF_LD | BPF_W | BPF_LEN: - emit_skb_load32(len, r_A); - break; - case BPF_LDX | BPF_W | BPF_LEN: - emit_skb_load32(len, r_X); - break; - case BPF_LD | BPF_IMM: - emit_loadimm(K, r_A); - break; - case BPF_LDX | BPF_IMM: - emit_loadimm(K, r_X); - break; - case BPF_LD | BPF_MEM: - seen |= SEEN_MEM; - emit_ldmem(K * 4, r_A); - break; - case BPF_LDX | BPF_MEM: - seen |= SEEN_MEM | SEEN_XREG; - emit_ldmem(K * 4, r_X); - break; - case BPF_ST: - seen |= SEEN_MEM; - emit_stmem(K * 4, r_A); - break; - case BPF_STX: - seen |= SEEN_MEM | SEEN_XREG; - emit_stmem(K * 4, r_X); - break; - -#define CHOOSE_LOAD_FUNC(K, func) \ - ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset) - - case BPF_LD | BPF_W | BPF_ABS: - func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_word); -common_load: seen |= SEEN_DATAREF; - emit_loadimm(K, r_OFF); - emit_call(func); - break; - case BPF_LD | BPF_H | BPF_ABS: - func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_half); - goto common_load; - case BPF_LD | BPF_B | BPF_ABS: - func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_byte); - goto common_load; - case BPF_LDX | BPF_B | BPF_MSH: - func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_byte_msh); - goto common_load; - case BPF_LD | BPF_W | BPF_IND: - func = bpf_jit_load_word; -common_load_ind: seen |= SEEN_DATAREF | SEEN_XREG; - if (K) { - if (is_simm13(K)) { - emit_addi(r_X, K, r_OFF); - } else { - emit_loadimm(K, r_TMP); - emit_add(r_X, r_TMP, r_OFF); - } - } else { - emit_reg_move(r_X, r_OFF); - } - emit_call(func); - break; - case BPF_LD | BPF_H | BPF_IND: - func = bpf_jit_load_half; - goto common_load_ind; - case BPF_LD | BPF_B | BPF_IND: - func = bpf_jit_load_byte; - goto common_load_ind; - case BPF_JMP | BPF_JA: - emit_jump(addrs[i + K]); - emit_nop(); - break; - -#define COND_SEL(CODE, TOP, FOP) \ - case CODE: \ - t_op = TOP; \ - f_op = FOP; \ - goto cond_branch - - COND_SEL(BPF_JMP | BPF_JGT | BPF_K, BGU, BLEU); - COND_SEL(BPF_JMP | BPF_JGE | BPF_K, BGEU, BLU); - COND_SEL(BPF_JMP | BPF_JEQ | BPF_K, BE, BNE); - COND_SEL(BPF_JMP | BPF_JSET | BPF_K, BNE, BE); - COND_SEL(BPF_JMP | BPF_JGT | BPF_X, BGU, BLEU); - COND_SEL(BPF_JMP | BPF_JGE | BPF_X, BGEU, BLU); - COND_SEL(BPF_JMP | BPF_JEQ | BPF_X, BE, BNE); - COND_SEL(BPF_JMP | BPF_JSET | BPF_X, BNE, BE); - -cond_branch: f_offset = addrs[i + filter[i].jf]; - t_offset = addrs[i + filter[i].jt]; - - /* same targets, can avoid doing the test :) */ - if (filter[i].jt == filter[i].jf) { - emit_jump(t_offset); - emit_nop(); - break; - } - - switch (code) { - case BPF_JMP | BPF_JGT | BPF_X: - case BPF_JMP | BPF_JGE | BPF_X: - case BPF_JMP | BPF_JEQ | BPF_X: - seen |= SEEN_XREG; - emit_cmp(r_A, r_X); - break; - case BPF_JMP | BPF_JSET | BPF_X: - seen |= SEEN_XREG; - emit_btst(r_A, r_X); - break; - case BPF_JMP | BPF_JEQ | BPF_K: - case BPF_JMP | BPF_JGT | BPF_K: - case BPF_JMP | BPF_JGE | BPF_K: - if (is_simm13(K)) { - emit_cmpi(r_A, K); - } else { - emit_loadimm(K, r_TMP); - emit_cmp(r_A, r_TMP); - } - break; - case BPF_JMP | BPF_JSET | BPF_K: - if (is_simm13(K)) { - emit_btsti(r_A, K); - } else { - emit_loadimm(K, r_TMP); - emit_btst(r_A, r_TMP); - } - break; - } - if (filter[i].jt != 0) { - if (filter[i].jf) - t_offset += 8; - emit_branch(t_op, t_offset); - emit_nop(); /* delay slot */ - if (filter[i].jf) { - emit_jump(f_offset); - emit_nop(); - } - break; - } - emit_branch(f_op, f_offset); - emit_nop(); /* delay slot */ - break; - - default: - /* hmm, too complex filter, give up with jit compiler */ - goto out; - } - ilen = (void *) prog - (void *) temp; - if (image) { - if (unlikely(proglen + ilen > oldproglen)) { - pr_err("bpb_jit_compile fatal error\n"); - kfree(addrs); - module_memfree(image); - return; - } - memcpy(image + proglen, temp, ilen); - } - proglen += ilen; - addrs[i] = proglen; - prog = temp; - } - /* last bpf instruction is always a RET : - * use it to give the cleanup instruction(s) addr - */ - cleanup_addr = proglen - 8; /* jmpl; mov r_A,%o0; */ - if (seen_or_pass0 & SEEN_MEM) - cleanup_addr -= 4; /* add %sp, X, %sp; */ - - if (image) { - if (proglen != oldproglen) - pr_err("bpb_jit_compile proglen=%u != oldproglen=%u\n", - proglen, oldproglen); - break; - } - if (proglen == oldproglen) { - image = module_alloc(proglen); - if (!image) - goto out; - } - oldproglen = proglen; - } - - if (bpf_jit_enable > 1) - bpf_jit_dump(flen, proglen, pass + 1, image); - - if (image) { - bpf_flush_icache(image, image + proglen); - fp->bpf_func = (void *)image; - fp->jited = 1; - } -out: - kfree(addrs); - return; -} - -void bpf_jit_free(struct bpf_prog *fp) -{ - if (fp->jited) - module_memfree(fp->bpf_func); - - bpf_prog_unlock_free(fp); -} diff --git a/arch/sparc/net/bpf_jit_comp_32.c b/arch/sparc/net/bpf_jit_comp_32.c new file mode 100644 index 000000000000..83fc41df9943 --- /dev/null +++ b/arch/sparc/net/bpf_jit_comp_32.c @@ -0,0 +1,815 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "bpf_jit_32.h" + +int bpf_jit_enable __read_mostly; + +static inline bool is_simm13(unsigned int value) +{ + return value + 0x1000 < 0x2000; +} + +static void bpf_flush_icache(void *start_, void *end_) +{ +#ifdef CONFIG_SPARC64 + /* Cheetah's I-cache is fully coherent. */ + if (tlb_type == spitfire) { + unsigned long start = (unsigned long) start_; + unsigned long end = (unsigned long) end_; + + start &= ~7UL; + end = (end + 7UL) & ~7UL; + while (start < end) { + flushi(start); + start += 32; + } + } +#endif +} + +#define SEEN_DATAREF 1 /* might call external helpers */ +#define SEEN_XREG 2 /* ebx is used */ +#define SEEN_MEM 4 /* use mem[] for temporary storage */ + +#define S13(X) ((X) & 0x1fff) +#define IMMED 0x00002000 +#define RD(X) ((X) << 25) +#define RS1(X) ((X) << 14) +#define RS2(X) ((X)) +#define OP(X) ((X) << 30) +#define OP2(X) ((X) << 22) +#define OP3(X) ((X) << 19) +#define COND(X) ((X) << 25) +#define F1(X) OP(X) +#define F2(X, Y) (OP(X) | OP2(Y)) +#define F3(X, Y) (OP(X) | OP3(Y)) + +#define CONDN COND(0x0) +#define CONDE COND(0x1) +#define CONDLE COND(0x2) +#define CONDL COND(0x3) +#define CONDLEU COND(0x4) +#define CONDCS COND(0x5) +#define CONDNEG COND(0x6) +#define CONDVC COND(0x7) +#define CONDA COND(0x8) +#define CONDNE COND(0x9) +#define CONDG COND(0xa) +#define CONDGE COND(0xb) +#define CONDGU COND(0xc) +#define CONDCC COND(0xd) +#define CONDPOS COND(0xe) +#define CONDVS COND(0xf) + +#define CONDGEU CONDCC +#define CONDLU CONDCS + +#define WDISP22(X) (((X) >> 2) & 0x3fffff) + +#define BA (F2(0, 2) | CONDA) +#define BGU (F2(0, 2) | CONDGU) +#define BLEU (F2(0, 2) | CONDLEU) +#define BGEU (F2(0, 2) | CONDGEU) +#define BLU (F2(0, 2) | CONDLU) +#define BE (F2(0, 2) | CONDE) +#define BNE (F2(0, 2) | CONDNE) + +#ifdef CONFIG_SPARC64 +#define BE_PTR (F2(0, 1) | CONDE | (2 << 20)) +#else +#define BE_PTR BE +#endif + +#define SETHI(K, REG) \ + (F2(0, 0x4) | RD(REG) | (((K) >> 10) & 0x3fffff)) +#define OR_LO(K, REG) \ + (F3(2, 0x02) | IMMED | RS1(REG) | ((K) & 0x3ff) | RD(REG)) + +#define ADD F3(2, 0x00) +#define AND F3(2, 0x01) +#define ANDCC F3(2, 0x11) +#define OR F3(2, 0x02) +#define XOR F3(2, 0x03) +#define SUB F3(2, 0x04) +#define SUBCC F3(2, 0x14) +#define MUL F3(2, 0x0a) /* umul */ +#define DIV F3(2, 0x0e) /* udiv */ +#define SLL F3(2, 0x25) +#define SRL F3(2, 0x26) +#define JMPL F3(2, 0x38) +#define CALL F1(1) +#define BR F2(0, 0x01) +#define RD_Y F3(2, 0x28) +#define WR_Y F3(2, 0x30) + +#define LD32 F3(3, 0x00) +#define LD8 F3(3, 0x01) +#define LD16 F3(3, 0x02) +#define LD64 F3(3, 0x0b) +#define ST32 F3(3, 0x04) + +#ifdef CONFIG_SPARC64 +#define LDPTR LD64 +#define BASE_STACKFRAME 176 +#else +#define LDPTR LD32 +#define BASE_STACKFRAME 96 +#endif + +#define LD32I (LD32 | IMMED) +#define LD8I (LD8 | IMMED) +#define LD16I (LD16 | IMMED) +#define LD64I (LD64 | IMMED) +#define LDPTRI (LDPTR | IMMED) +#define ST32I (ST32 | IMMED) + +#define emit_nop() \ +do { \ + *prog++ = SETHI(0, G0); \ +} while (0) + +#define emit_neg() \ +do { /* sub %g0, r_A, r_A */ \ + *prog++ = SUB | RS1(G0) | RS2(r_A) | RD(r_A); \ +} while (0) + +#define emit_reg_move(FROM, TO) \ +do { /* or %g0, FROM, TO */ \ + *prog++ = OR | RS1(G0) | RS2(FROM) | RD(TO); \ +} while (0) + +#define emit_clear(REG) \ +do { /* or %g0, %g0, REG */ \ + *prog++ = OR | RS1(G0) | RS2(G0) | RD(REG); \ +} while (0) + +#define emit_set_const(K, REG) \ +do { /* sethi %hi(K), REG */ \ + *prog++ = SETHI(K, REG); \ + /* or REG, %lo(K), REG */ \ + *prog++ = OR_LO(K, REG); \ +} while (0) + + /* Emit + * + * OP r_A, r_X, r_A + */ +#define emit_alu_X(OPCODE) \ +do { \ + seen |= SEEN_XREG; \ + *prog++ = OPCODE | RS1(r_A) | RS2(r_X) | RD(r_A); \ +} while (0) + + /* Emit either: + * + * OP r_A, K, r_A + * + * or + * + * sethi %hi(K), r_TMP + * or r_TMP, %lo(K), r_TMP + * OP r_A, r_TMP, r_A + * + * depending upon whether K fits in a signed 13-bit + * immediate instruction field. Emit nothing if K + * is zero. + */ +#define emit_alu_K(OPCODE, K) \ +do { \ + if (K || OPCODE == AND || OPCODE == MUL) { \ + unsigned int _insn = OPCODE; \ + _insn |= RS1(r_A) | RD(r_A); \ + if (is_simm13(K)) { \ + *prog++ = _insn | IMMED | S13(K); \ + } else { \ + emit_set_const(K, r_TMP); \ + *prog++ = _insn | RS2(r_TMP); \ + } \ + } \ +} while (0) + +#define emit_loadimm(K, DEST) \ +do { \ + if (is_simm13(K)) { \ + /* or %g0, K, DEST */ \ + *prog++ = OR | IMMED | RS1(G0) | S13(K) | RD(DEST); \ + } else { \ + emit_set_const(K, DEST); \ + } \ +} while (0) + +#define emit_loadptr(BASE, STRUCT, FIELD, DEST) \ +do { unsigned int _off = offsetof(STRUCT, FIELD); \ + BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(void *)); \ + *prog++ = LDPTRI | RS1(BASE) | S13(_off) | RD(DEST); \ +} while (0) + +#define emit_load32(BASE, STRUCT, FIELD, DEST) \ +do { unsigned int _off = offsetof(STRUCT, FIELD); \ + BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u32)); \ + *prog++ = LD32I | RS1(BASE) | S13(_off) | RD(DEST); \ +} while (0) + +#define emit_load16(BASE, STRUCT, FIELD, DEST) \ +do { unsigned int _off = offsetof(STRUCT, FIELD); \ + BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u16)); \ + *prog++ = LD16I | RS1(BASE) | S13(_off) | RD(DEST); \ +} while (0) + +#define __emit_load8(BASE, STRUCT, FIELD, DEST) \ +do { unsigned int _off = offsetof(STRUCT, FIELD); \ + *prog++ = LD8I | RS1(BASE) | S13(_off) | RD(DEST); \ +} while (0) + +#define emit_load8(BASE, STRUCT, FIELD, DEST) \ +do { BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u8)); \ + __emit_load8(BASE, STRUCT, FIELD, DEST); \ +} while (0) + +#ifdef CONFIG_SPARC64 +#define BIAS (STACK_BIAS - 4) +#else +#define BIAS (-4) +#endif + +#define emit_ldmem(OFF, DEST) \ +do { *prog++ = LD32I | RS1(SP) | S13(BIAS - (OFF)) | RD(DEST); \ +} while (0) + +#define emit_stmem(OFF, SRC) \ +do { *prog++ = ST32I | RS1(SP) | S13(BIAS - (OFF)) | RD(SRC); \ +} while (0) + +#ifdef CONFIG_SMP +#ifdef CONFIG_SPARC64 +#define emit_load_cpu(REG) \ + emit_load16(G6, struct thread_info, cpu, REG) +#else +#define emit_load_cpu(REG) \ + emit_load32(G6, struct thread_info, cpu, REG) +#endif +#else +#define emit_load_cpu(REG) emit_clear(REG) +#endif + +#define emit_skb_loadptr(FIELD, DEST) \ + emit_loadptr(r_SKB, struct sk_buff, FIELD, DEST) +#define emit_skb_load32(FIELD, DEST) \ + emit_load32(r_SKB, struct sk_buff, FIELD, DEST) +#define emit_skb_load16(FIELD, DEST) \ + emit_load16(r_SKB, struct sk_buff, FIELD, DEST) +#define __emit_skb_load8(FIELD, DEST) \ + __emit_load8(r_SKB, struct sk_buff, FIELD, DEST) +#define emit_skb_load8(FIELD, DEST) \ + emit_load8(r_SKB, struct sk_buff, FIELD, DEST) + +#define emit_jmpl(BASE, IMM_OFF, LREG) \ + *prog++ = (JMPL | IMMED | RS1(BASE) | S13(IMM_OFF) | RD(LREG)) + +#define emit_call(FUNC) \ +do { void *_here = image + addrs[i] - 8; \ + unsigned int _off = (void *)(FUNC) - _here; \ + *prog++ = CALL | (((_off) >> 2) & 0x3fffffff); \ + emit_nop(); \ +} while (0) + +#define emit_branch(BR_OPC, DEST) \ +do { unsigned int _here = addrs[i] - 8; \ + *prog++ = BR_OPC | WDISP22((DEST) - _here); \ +} while (0) + +#define emit_branch_off(BR_OPC, OFF) \ +do { *prog++ = BR_OPC | WDISP22(OFF); \ +} while (0) + +#define emit_jump(DEST) emit_branch(BA, DEST) + +#define emit_read_y(REG) *prog++ = RD_Y | RD(REG) +#define emit_write_y(REG) *prog++ = WR_Y | IMMED | RS1(REG) | S13(0) + +#define emit_cmp(R1, R2) \ + *prog++ = (SUBCC | RS1(R1) | RS2(R2) | RD(G0)) + +#define emit_cmpi(R1, IMM) \ + *prog++ = (SUBCC | IMMED | RS1(R1) | S13(IMM) | RD(G0)); + +#define emit_btst(R1, R2) \ + *prog++ = (ANDCC | RS1(R1) | RS2(R2) | RD(G0)) + +#define emit_btsti(R1, IMM) \ + *prog++ = (ANDCC | IMMED | RS1(R1) | S13(IMM) | RD(G0)); + +#define emit_sub(R1, R2, R3) \ + *prog++ = (SUB | RS1(R1) | RS2(R2) | RD(R3)) + +#define emit_subi(R1, IMM, R3) \ + *prog++ = (SUB | IMMED | RS1(R1) | S13(IMM) | RD(R3)) + +#define emit_add(R1, R2, R3) \ + *prog++ = (ADD | RS1(R1) | RS2(R2) | RD(R3)) + +#define emit_addi(R1, IMM, R3) \ + *prog++ = (ADD | IMMED | RS1(R1) | S13(IMM) | RD(R3)) + +#define emit_and(R1, R2, R3) \ + *prog++ = (AND | RS1(R1) | RS2(R2) | RD(R3)) + +#define emit_andi(R1, IMM, R3) \ + *prog++ = (AND | IMMED | RS1(R1) | S13(IMM) | RD(R3)) + +#define emit_alloc_stack(SZ) \ + *prog++ = (SUB | IMMED | RS1(SP) | S13(SZ) | RD(SP)) + +#define emit_release_stack(SZ) \ + *prog++ = (ADD | IMMED | RS1(SP) | S13(SZ) | RD(SP)) + +/* A note about branch offset calculations. The addrs[] array, + * indexed by BPF instruction, records the address after all the + * sparc instructions emitted for that BPF instruction. + * + * The most common case is to emit a branch at the end of such + * a code sequence. So this would be two instructions, the + * branch and it's delay slot. + * + * Therefore by default the branch emitters calculate the branch + * offset field as: + * + * destination - (addrs[i] - 8) + * + * This "addrs[i] - 8" is the address of the branch itself or + * what "." would be in assembler notation. The "8" part is + * how we take into consideration the branch and it's delay + * slot mentioned above. + * + * Sometimes we need to emit a branch earlier in the code + * sequence. And in these situations we adjust "destination" + * to accommodate this difference. For example, if we needed + * to emit a branch (and it's delay slot) right before the + * final instruction emitted for a BPF opcode, we'd use + * "destination + 4" instead of just plain "destination" above. + * + * This is why you see all of these funny emit_branch() and + * emit_jump() calls with adjusted offsets. + */ + +void bpf_jit_compile(struct bpf_prog *fp) +{ + unsigned int cleanup_addr, proglen, oldproglen = 0; + u32 temp[8], *prog, *func, seen = 0, pass; + const struct sock_filter *filter = fp->insns; + int i, flen = fp->len, pc_ret0 = -1; + unsigned int *addrs; + void *image; + + if (!bpf_jit_enable) + return; + + addrs = kmalloc(flen * sizeof(*addrs), GFP_KERNEL); + if (addrs == NULL) + return; + + /* Before first pass, make a rough estimation of addrs[] + * each bpf instruction is translated to less than 64 bytes + */ + for (proglen = 0, i = 0; i < flen; i++) { + proglen += 64; + addrs[i] = proglen; + } + cleanup_addr = proglen; /* epilogue address */ + image = NULL; + for (pass = 0; pass < 10; pass++) { + u8 seen_or_pass0 = (pass == 0) ? (SEEN_XREG | SEEN_DATAREF | SEEN_MEM) : seen; + + /* no prologue/epilogue for trivial filters (RET something) */ + proglen = 0; + prog = temp; + + /* Prologue */ + if (seen_or_pass0) { + if (seen_or_pass0 & SEEN_MEM) { + unsigned int sz = BASE_STACKFRAME; + sz += BPF_MEMWORDS * sizeof(u32); + emit_alloc_stack(sz); + } + + /* Make sure we dont leek kernel memory. */ + if (seen_or_pass0 & SEEN_XREG) + emit_clear(r_X); + + /* If this filter needs to access skb data, + * load %o4 and %o5 with: + * %o4 = skb->len - skb->data_len + * %o5 = skb->data + * And also back up %o7 into r_saved_O7 so we can + * invoke the stubs using 'call'. + */ + if (seen_or_pass0 & SEEN_DATAREF) { + emit_load32(r_SKB, struct sk_buff, len, r_HEADLEN); + emit_load32(r_SKB, struct sk_buff, data_len, r_TMP); + emit_sub(r_HEADLEN, r_TMP, r_HEADLEN); + emit_loadptr(r_SKB, struct sk_buff, data, r_SKB_DATA); + } + } + emit_reg_move(O7, r_saved_O7); + + /* Make sure we dont leak kernel information to the user. */ + if (bpf_needs_clear_a(&filter[0])) + emit_clear(r_A); /* A = 0 */ + + for (i = 0; i < flen; i++) { + unsigned int K = filter[i].k; + unsigned int t_offset; + unsigned int f_offset; + u32 t_op, f_op; + u16 code = bpf_anc_helper(&filter[i]); + int ilen; + + switch (code) { + case BPF_ALU | BPF_ADD | BPF_X: /* A += X; */ + emit_alu_X(ADD); + break; + case BPF_ALU | BPF_ADD | BPF_K: /* A += K; */ + emit_alu_K(ADD, K); + break; + case BPF_ALU | BPF_SUB | BPF_X: /* A -= X; */ + emit_alu_X(SUB); + break; + case BPF_ALU | BPF_SUB | BPF_K: /* A -= K */ + emit_alu_K(SUB, K); + break; + case BPF_ALU | BPF_AND | BPF_X: /* A &= X */ + emit_alu_X(AND); + break; + case BPF_ALU | BPF_AND | BPF_K: /* A &= K */ + emit_alu_K(AND, K); + break; + case BPF_ALU | BPF_OR | BPF_X: /* A |= X */ + emit_alu_X(OR); + break; + case BPF_ALU | BPF_OR | BPF_K: /* A |= K */ + emit_alu_K(OR, K); + break; + case BPF_ANC | SKF_AD_ALU_XOR_X: /* A ^= X; */ + case BPF_ALU | BPF_XOR | BPF_X: + emit_alu_X(XOR); + break; + case BPF_ALU | BPF_XOR | BPF_K: /* A ^= K */ + emit_alu_K(XOR, K); + break; + case BPF_ALU | BPF_LSH | BPF_X: /* A <<= X */ + emit_alu_X(SLL); + break; + case BPF_ALU | BPF_LSH | BPF_K: /* A <<= K */ + emit_alu_K(SLL, K); + break; + case BPF_ALU | BPF_RSH | BPF_X: /* A >>= X */ + emit_alu_X(SRL); + break; + case BPF_ALU | BPF_RSH | BPF_K: /* A >>= K */ + emit_alu_K(SRL, K); + break; + case BPF_ALU | BPF_MUL | BPF_X: /* A *= X; */ + emit_alu_X(MUL); + break; + case BPF_ALU | BPF_MUL | BPF_K: /* A *= K */ + emit_alu_K(MUL, K); + break; + case BPF_ALU | BPF_DIV | BPF_K: /* A /= K with K != 0*/ + if (K == 1) + break; + emit_write_y(G0); +#ifdef CONFIG_SPARC32 + /* The Sparc v8 architecture requires + * three instructions between a %y + * register write and the first use. + */ + emit_nop(); + emit_nop(); + emit_nop(); +#endif + emit_alu_K(DIV, K); + break; + case BPF_ALU | BPF_DIV | BPF_X: /* A /= X; */ + emit_cmpi(r_X, 0); + if (pc_ret0 > 0) { + t_offset = addrs[pc_ret0 - 1]; +#ifdef CONFIG_SPARC32 + emit_branch(BE, t_offset + 20); +#else + emit_branch(BE, t_offset + 8); +#endif + emit_nop(); /* delay slot */ + } else { + emit_branch_off(BNE, 16); + emit_nop(); +#ifdef CONFIG_SPARC32 + emit_jump(cleanup_addr + 20); +#else + emit_jump(cleanup_addr + 8); +#endif + emit_clear(r_A); + } + emit_write_y(G0); +#ifdef CONFIG_SPARC32 + /* The Sparc v8 architecture requires + * three instructions between a %y + * register write and the first use. + */ + emit_nop(); + emit_nop(); + emit_nop(); +#endif + emit_alu_X(DIV); + break; + case BPF_ALU | BPF_NEG: + emit_neg(); + break; + case BPF_RET | BPF_K: + if (!K) { + if (pc_ret0 == -1) + pc_ret0 = i; + emit_clear(r_A); + } else { + emit_loadimm(K, r_A); + } + /* Fallthrough */ + case BPF_RET | BPF_A: + if (seen_or_pass0) { + if (i != flen - 1) { + emit_jump(cleanup_addr); + emit_nop(); + break; + } + if (seen_or_pass0 & SEEN_MEM) { + unsigned int sz = BASE_STACKFRAME; + sz += BPF_MEMWORDS * sizeof(u32); + emit_release_stack(sz); + } + } + /* jmpl %r_saved_O7 + 8, %g0 */ + emit_jmpl(r_saved_O7, 8, G0); + emit_reg_move(r_A, O0); /* delay slot */ + break; + case BPF_MISC | BPF_TAX: + seen |= SEEN_XREG; + emit_reg_move(r_A, r_X); + break; + case BPF_MISC | BPF_TXA: + seen |= SEEN_XREG; + emit_reg_move(r_X, r_A); + break; + case BPF_ANC | SKF_AD_CPU: + emit_load_cpu(r_A); + break; + case BPF_ANC | SKF_AD_PROTOCOL: + emit_skb_load16(protocol, r_A); + break; + case BPF_ANC | SKF_AD_PKTTYPE: + __emit_skb_load8(__pkt_type_offset, r_A); + emit_andi(r_A, PKT_TYPE_MAX, r_A); + emit_alu_K(SRL, 5); + break; + case BPF_ANC | SKF_AD_IFINDEX: + emit_skb_loadptr(dev, r_A); + emit_cmpi(r_A, 0); + emit_branch(BE_PTR, cleanup_addr + 4); + emit_nop(); + emit_load32(r_A, struct net_device, ifindex, r_A); + break; + case BPF_ANC | SKF_AD_MARK: + emit_skb_load32(mark, r_A); + break; + case BPF_ANC | SKF_AD_QUEUE: + emit_skb_load16(queue_mapping, r_A); + break; + case BPF_ANC | SKF_AD_HATYPE: + emit_skb_loadptr(dev, r_A); + emit_cmpi(r_A, 0); + emit_branch(BE_PTR, cleanup_addr + 4); + emit_nop(); + emit_load16(r_A, struct net_device, type, r_A); + break; + case BPF_ANC | SKF_AD_RXHASH: + emit_skb_load32(hash, r_A); + break; + case BPF_ANC | SKF_AD_VLAN_TAG: + case BPF_ANC | SKF_AD_VLAN_TAG_PRESENT: + emit_skb_load16(vlan_tci, r_A); + if (code != (BPF_ANC | SKF_AD_VLAN_TAG)) { + emit_alu_K(SRL, 12); + emit_andi(r_A, 1, r_A); + } else { + emit_loadimm(~VLAN_TAG_PRESENT, r_TMP); + emit_and(r_A, r_TMP, r_A); + } + break; + case BPF_LD | BPF_W | BPF_LEN: + emit_skb_load32(len, r_A); + break; + case BPF_LDX | BPF_W | BPF_LEN: + emit_skb_load32(len, r_X); + break; + case BPF_LD | BPF_IMM: + emit_loadimm(K, r_A); + break; + case BPF_LDX | BPF_IMM: + emit_loadimm(K, r_X); + break; + case BPF_LD | BPF_MEM: + seen |= SEEN_MEM; + emit_ldmem(K * 4, r_A); + break; + case BPF_LDX | BPF_MEM: + seen |= SEEN_MEM | SEEN_XREG; + emit_ldmem(K * 4, r_X); + break; + case BPF_ST: + seen |= SEEN_MEM; + emit_stmem(K * 4, r_A); + break; + case BPF_STX: + seen |= SEEN_MEM | SEEN_XREG; + emit_stmem(K * 4, r_X); + break; + +#define CHOOSE_LOAD_FUNC(K, func) \ + ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset) + + case BPF_LD | BPF_W | BPF_ABS: + func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_word); +common_load: seen |= SEEN_DATAREF; + emit_loadimm(K, r_OFF); + emit_call(func); + break; + case BPF_LD | BPF_H | BPF_ABS: + func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_half); + goto common_load; + case BPF_LD | BPF_B | BPF_ABS: + func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_byte); + goto common_load; + case BPF_LDX | BPF_B | BPF_MSH: + func = CHOOSE_LOAD_FUNC(K, bpf_jit_load_byte_msh); + goto common_load; + case BPF_LD | BPF_W | BPF_IND: + func = bpf_jit_load_word; +common_load_ind: seen |= SEEN_DATAREF | SEEN_XREG; + if (K) { + if (is_simm13(K)) { + emit_addi(r_X, K, r_OFF); + } else { + emit_loadimm(K, r_TMP); + emit_add(r_X, r_TMP, r_OFF); + } + } else { + emit_reg_move(r_X, r_OFF); + } + emit_call(func); + break; + case BPF_LD | BPF_H | BPF_IND: + func = bpf_jit_load_half; + goto common_load_ind; + case BPF_LD | BPF_B | BPF_IND: + func = bpf_jit_load_byte; + goto common_load_ind; + case BPF_JMP | BPF_JA: + emit_jump(addrs[i + K]); + emit_nop(); + break; + +#define COND_SEL(CODE, TOP, FOP) \ + case CODE: \ + t_op = TOP; \ + f_op = FOP; \ + goto cond_branch + + COND_SEL(BPF_JMP | BPF_JGT | BPF_K, BGU, BLEU); + COND_SEL(BPF_JMP | BPF_JGE | BPF_K, BGEU, BLU); + COND_SEL(BPF_JMP | BPF_JEQ | BPF_K, BE, BNE); + COND_SEL(BPF_JMP | BPF_JSET | BPF_K, BNE, BE); + COND_SEL(BPF_JMP | BPF_JGT | BPF_X, BGU, BLEU); + COND_SEL(BPF_JMP | BPF_JGE | BPF_X, BGEU, BLU); + COND_SEL(BPF_JMP | BPF_JEQ | BPF_X, BE, BNE); + COND_SEL(BPF_JMP | BPF_JSET | BPF_X, BNE, BE); + +cond_branch: f_offset = addrs[i + filter[i].jf]; + t_offset = addrs[i + filter[i].jt]; + + /* same targets, can avoid doing the test :) */ + if (filter[i].jt == filter[i].jf) { + emit_jump(t_offset); + emit_nop(); + break; + } + + switch (code) { + case BPF_JMP | BPF_JGT | BPF_X: + case BPF_JMP | BPF_JGE | BPF_X: + case BPF_JMP | BPF_JEQ | BPF_X: + seen |= SEEN_XREG; + emit_cmp(r_A, r_X); + break; + case BPF_JMP | BPF_JSET | BPF_X: + seen |= SEEN_XREG; + emit_btst(r_A, r_X); + break; + case BPF_JMP | BPF_JEQ | BPF_K: + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JGE | BPF_K: + if (is_simm13(K)) { + emit_cmpi(r_A, K); + } else { + emit_loadimm(K, r_TMP); + emit_cmp(r_A, r_TMP); + } + break; + case BPF_JMP | BPF_JSET | BPF_K: + if (is_simm13(K)) { + emit_btsti(r_A, K); + } else { + emit_loadimm(K, r_TMP); + emit_btst(r_A, r_TMP); + } + break; + } + if (filter[i].jt != 0) { + if (filter[i].jf) + t_offset += 8; + emit_branch(t_op, t_offset); + emit_nop(); /* delay slot */ + if (filter[i].jf) { + emit_jump(f_offset); + emit_nop(); + } + break; + } + emit_branch(f_op, f_offset); + emit_nop(); /* delay slot */ + break; + + default: + /* hmm, too complex filter, give up with jit compiler */ + goto out; + } + ilen = (void *) prog - (void *) temp; + if (image) { + if (unlikely(proglen + ilen > oldproglen)) { + pr_err("bpb_jit_compile fatal error\n"); + kfree(addrs); + module_memfree(image); + return; + } + memcpy(image + proglen, temp, ilen); + } + proglen += ilen; + addrs[i] = proglen; + prog = temp; + } + /* last bpf instruction is always a RET : + * use it to give the cleanup instruction(s) addr + */ + cleanup_addr = proglen - 8; /* jmpl; mov r_A,%o0; */ + if (seen_or_pass0 & SEEN_MEM) + cleanup_addr -= 4; /* add %sp, X, %sp; */ + + if (image) { + if (proglen != oldproglen) + pr_err("bpb_jit_compile proglen=%u != oldproglen=%u\n", + proglen, oldproglen); + break; + } + if (proglen == oldproglen) { + image = module_alloc(proglen); + if (!image) + goto out; + } + oldproglen = proglen; + } + + if (bpf_jit_enable > 1) + bpf_jit_dump(flen, proglen, pass + 1, image); + + if (image) { + bpf_flush_icache(image, image + proglen); + fp->bpf_func = (void *)image; + fp->jited = 1; + } +out: + kfree(addrs); + return; +} + +void bpf_jit_free(struct bpf_prog *fp) +{ + if (fp->jited) + module_memfree(fp->bpf_func); + + bpf_prog_unlock_free(fp); +} diff --git a/arch/sparc/net/bpf_jit_comp_64.c b/arch/sparc/net/bpf_jit_comp_64.c new file mode 100644 index 000000000000..49b5f65f84ac --- /dev/null +++ b/arch/sparc/net/bpf_jit_comp_64.c @@ -0,0 +1 @@ +#include "bpf_jit_comp_32.c" -- cgit From 7a12b5031c6b947cc13918237ae652b536243b76 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 17 Apr 2017 18:44:36 -0700 Subject: sparc64: Add eBPF JIT. This is an eBPF JIT for sparc64. All major features are supported. All tests under tools/testing/selftests/bpf/ pass. Signed-off-by: David S. Miller --- arch/sparc/Kconfig | 3 +- arch/sparc/net/bpf_jit_32.h | 2 +- arch/sparc/net/bpf_jit_64.h | 66 +++ arch/sparc/net/bpf_jit_asm_32.S | 7 - arch/sparc/net/bpf_jit_asm_64.S | 162 +++++- arch/sparc/net/bpf_jit_comp_32.c | 49 -- arch/sparc/net/bpf_jit_comp_64.c | 1195 +++++++++++++++++++++++++++++++++++++- 7 files changed, 1424 insertions(+), 60 deletions(-) create mode 100644 arch/sparc/net/bpf_jit_64.h diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index a59deaef21e5..7b16251b7476 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -31,7 +31,8 @@ config SPARC select ARCH_WANT_IPC_PARSE_VERSION select GENERIC_PCI_IOMAP select HAVE_NMI_WATCHDOG if SPARC64 - select HAVE_CBPF_JIT + select HAVE_CBPF_JIT if SPARC32 + select HAVE_EBPF_JIT if SPARC64 select HAVE_DEBUG_BUGVERBOSE select GENERIC_SMP_IDLE_THREAD select GENERIC_CLOCKEVENTS diff --git a/arch/sparc/net/bpf_jit_32.h b/arch/sparc/net/bpf_jit_32.h index 33d6b375ff12..d5c069bff5f9 100644 --- a/arch/sparc/net/bpf_jit_32.h +++ b/arch/sparc/net/bpf_jit_32.h @@ -39,7 +39,7 @@ #define r_TMP2 G2 #define r_OFF G3 -/* assembly code in arch/sparc/net/bpf_jit_asm.S */ +/* assembly code in arch/sparc/net/bpf_jit_asm_32.S */ extern u32 bpf_jit_load_word[]; extern u32 bpf_jit_load_half[]; extern u32 bpf_jit_load_byte[]; diff --git a/arch/sparc/net/bpf_jit_64.h b/arch/sparc/net/bpf_jit_64.h new file mode 100644 index 000000000000..74abd45796ea --- /dev/null +++ b/arch/sparc/net/bpf_jit_64.h @@ -0,0 +1,66 @@ +#ifndef _BPF_JIT_H +#define _BPF_JIT_H + +#ifndef __ASSEMBLER__ +#define G0 0x00 +#define G1 0x01 +#define G2 0x02 +#define G3 0x03 +#define G6 0x06 +#define G7 0x07 +#define O0 0x08 +#define O1 0x09 +#define O2 0x0a +#define O3 0x0b +#define O4 0x0c +#define O5 0x0d +#define SP 0x0e +#define O7 0x0f +#define L0 0x10 +#define L1 0x11 +#define L2 0x12 +#define L3 0x13 +#define L4 0x14 +#define L5 0x15 +#define L6 0x16 +#define L7 0x17 +#define I0 0x18 +#define I1 0x19 +#define I2 0x1a +#define I3 0x1b +#define I4 0x1c +#define I5 0x1d +#define FP 0x1e +#define I7 0x1f + +#define r_SKB L0 +#define r_HEADLEN L4 +#define r_SKB_DATA L5 +#define r_TMP G1 +#define r_TMP2 G3 + +/* assembly code in arch/sparc/net/bpf_jit_asm_64.S */ +extern u32 bpf_jit_load_word[]; +extern u32 bpf_jit_load_half[]; +extern u32 bpf_jit_load_byte[]; +extern u32 bpf_jit_load_byte_msh[]; +extern u32 bpf_jit_load_word_positive_offset[]; +extern u32 bpf_jit_load_half_positive_offset[]; +extern u32 bpf_jit_load_byte_positive_offset[]; +extern u32 bpf_jit_load_byte_msh_positive_offset[]; +extern u32 bpf_jit_load_word_negative_offset[]; +extern u32 bpf_jit_load_half_negative_offset[]; +extern u32 bpf_jit_load_byte_negative_offset[]; +extern u32 bpf_jit_load_byte_msh_negative_offset[]; + +#else +#define r_RESULT %o0 +#define r_SKB %o0 +#define r_OFF %o1 +#define r_HEADLEN %l4 +#define r_SKB_DATA %l5 +#define r_TMP %g1 +#define r_TMP2 %g3 +#endif + +#endif /* _BPF_JIT_H */ diff --git a/arch/sparc/net/bpf_jit_asm_32.S b/arch/sparc/net/bpf_jit_asm_32.S index 5632cdc922b1..dcc402f5738a 100644 --- a/arch/sparc/net/bpf_jit_asm_32.S +++ b/arch/sparc/net/bpf_jit_asm_32.S @@ -2,17 +2,10 @@ #include "bpf_jit_32.h" -#ifdef CONFIG_SPARC64 -#define SAVE_SZ 176 -#define SCRATCH_OFF STACK_BIAS + 128 -#define BE_PTR(label) be,pn %xcc, label -#define SIGN_EXTEND(reg) sra reg, 0, reg -#else #define SAVE_SZ 96 #define SCRATCH_OFF 72 #define BE_PTR(label) be label #define SIGN_EXTEND(reg) -#endif #define SKF_MAX_NEG_OFF (-0x200000) /* SKF_LL_OFF from filter.h */ diff --git a/arch/sparc/net/bpf_jit_asm_64.S b/arch/sparc/net/bpf_jit_asm_64.S index 6fb023f9cd99..3b3f14655f81 100644 --- a/arch/sparc/net/bpf_jit_asm_64.S +++ b/arch/sparc/net/bpf_jit_asm_64.S @@ -1 +1,161 @@ -#include "bpf_jit_asm_32.S" +#include + +#include "bpf_jit_64.h" + +#define SAVE_SZ 176 +#define SCRATCH_OFF STACK_BIAS + 128 +#define BE_PTR(label) be,pn %xcc, label +#define SIGN_EXTEND(reg) sra reg, 0, reg + +#define SKF_MAX_NEG_OFF (-0x200000) /* SKF_LL_OFF from filter.h */ + + .text + .globl bpf_jit_load_word +bpf_jit_load_word: + cmp r_OFF, 0 + bl bpf_slow_path_word_neg + nop + .globl bpf_jit_load_word_positive_offset +bpf_jit_load_word_positive_offset: + sub r_HEADLEN, r_OFF, r_TMP + cmp r_TMP, 3 + ble bpf_slow_path_word + add r_SKB_DATA, r_OFF, r_TMP + andcc r_TMP, 3, %g0 + bne load_word_unaligned + nop + retl + ld [r_TMP], r_RESULT +load_word_unaligned: + ldub [r_TMP + 0x0], r_OFF + ldub [r_TMP + 0x1], r_TMP2 + sll r_OFF, 8, r_OFF + or r_OFF, r_TMP2, r_OFF + ldub [r_TMP + 0x2], r_TMP2 + sll r_OFF, 8, r_OFF + or r_OFF, r_TMP2, r_OFF + ldub [r_TMP + 0x3], r_TMP2 + sll r_OFF, 8, r_OFF + retl + or r_OFF, r_TMP2, r_RESULT + + .globl bpf_jit_load_half +bpf_jit_load_half: + cmp r_OFF, 0 + bl bpf_slow_path_half_neg + nop + .globl bpf_jit_load_half_positive_offset +bpf_jit_load_half_positive_offset: + sub r_HEADLEN, r_OFF, r_TMP + cmp r_TMP, 1 + ble bpf_slow_path_half + add r_SKB_DATA, r_OFF, r_TMP + andcc r_TMP, 1, %g0 + bne load_half_unaligned + nop + retl + lduh [r_TMP], r_RESULT +load_half_unaligned: + ldub [r_TMP + 0x0], r_OFF + ldub [r_TMP + 0x1], r_TMP2 + sll r_OFF, 8, r_OFF + retl + or r_OFF, r_TMP2, r_RESULT + + .globl bpf_jit_load_byte +bpf_jit_load_byte: + cmp r_OFF, 0 + bl bpf_slow_path_byte_neg + nop + .globl bpf_jit_load_byte_positive_offset +bpf_jit_load_byte_positive_offset: + cmp r_OFF, r_HEADLEN + bge bpf_slow_path_byte + nop + retl + ldub [r_SKB_DATA + r_OFF], r_RESULT + +#define bpf_slow_path_common(LEN) \ + save %sp, -SAVE_SZ, %sp; \ + mov %i0, %o0; \ + mov %i1, %o1; \ + add %fp, SCRATCH_OFF, %o2; \ + call skb_copy_bits; \ + mov (LEN), %o3; \ + cmp %o0, 0; \ + restore; + +bpf_slow_path_word: + bpf_slow_path_common(4) + bl bpf_error + ld [%sp + SCRATCH_OFF], r_RESULT + retl + nop +bpf_slow_path_half: + bpf_slow_path_common(2) + bl bpf_error + lduh [%sp + SCRATCH_OFF], r_RESULT + retl + nop +bpf_slow_path_byte: + bpf_slow_path_common(1) + bl bpf_error + ldub [%sp + SCRATCH_OFF], r_RESULT + retl + nop + +#define bpf_negative_common(LEN) \ + save %sp, -SAVE_SZ, %sp; \ + mov %i0, %o0; \ + mov %i1, %o1; \ + SIGN_EXTEND(%o1); \ + call bpf_internal_load_pointer_neg_helper; \ + mov (LEN), %o2; \ + mov %o0, r_TMP; \ + cmp %o0, 0; \ + BE_PTR(bpf_error); \ + restore; + +bpf_slow_path_word_neg: + sethi %hi(SKF_MAX_NEG_OFF), r_TMP + cmp r_OFF, r_TMP + bl bpf_error + nop + .globl bpf_jit_load_word_negative_offset +bpf_jit_load_word_negative_offset: + bpf_negative_common(4) + andcc r_TMP, 3, %g0 + bne load_word_unaligned + nop + retl + ld [r_TMP], r_RESULT + +bpf_slow_path_half_neg: + sethi %hi(SKF_MAX_NEG_OFF), r_TMP + cmp r_OFF, r_TMP + bl bpf_error + nop + .globl bpf_jit_load_half_negative_offset +bpf_jit_load_half_negative_offset: + bpf_negative_common(2) + andcc r_TMP, 1, %g0 + bne load_half_unaligned + nop + retl + lduh [r_TMP], r_RESULT + +bpf_slow_path_byte_neg: + sethi %hi(SKF_MAX_NEG_OFF), r_TMP + cmp r_OFF, r_TMP + bl bpf_error + nop + .globl bpf_jit_load_byte_negative_offset +bpf_jit_load_byte_negative_offset: + bpf_negative_common(1) + retl + ldub [r_TMP], r_RESULT + +bpf_error: + /* Make the JIT program itself return zero. */ + ret + restore %g0, %g0, %o0 diff --git a/arch/sparc/net/bpf_jit_comp_32.c b/arch/sparc/net/bpf_jit_comp_32.c index 83fc41df9943..d193748548e2 100644 --- a/arch/sparc/net/bpf_jit_comp_32.c +++ b/arch/sparc/net/bpf_jit_comp_32.c @@ -17,24 +17,6 @@ static inline bool is_simm13(unsigned int value) return value + 0x1000 < 0x2000; } -static void bpf_flush_icache(void *start_, void *end_) -{ -#ifdef CONFIG_SPARC64 - /* Cheetah's I-cache is fully coherent. */ - if (tlb_type == spitfire) { - unsigned long start = (unsigned long) start_; - unsigned long end = (unsigned long) end_; - - start &= ~7UL; - end = (end + 7UL) & ~7UL; - while (start < end) { - flushi(start); - start += 32; - } - } -#endif -} - #define SEEN_DATAREF 1 /* might call external helpers */ #define SEEN_XREG 2 /* ebx is used */ #define SEEN_MEM 4 /* use mem[] for temporary storage */ @@ -82,11 +64,7 @@ static void bpf_flush_icache(void *start_, void *end_) #define BE (F2(0, 2) | CONDE) #define BNE (F2(0, 2) | CONDNE) -#ifdef CONFIG_SPARC64 -#define BE_PTR (F2(0, 1) | CONDE | (2 << 20)) -#else #define BE_PTR BE -#endif #define SETHI(K, REG) \ (F2(0, 0x4) | RD(REG) | (((K) >> 10) & 0x3fffff)) @@ -116,13 +94,8 @@ static void bpf_flush_icache(void *start_, void *end_) #define LD64 F3(3, 0x0b) #define ST32 F3(3, 0x04) -#ifdef CONFIG_SPARC64 -#define LDPTR LD64 -#define BASE_STACKFRAME 176 -#else #define LDPTR LD32 #define BASE_STACKFRAME 96 -#endif #define LD32I (LD32 | IMMED) #define LD8I (LD8 | IMMED) @@ -234,11 +207,7 @@ do { BUILD_BUG_ON(FIELD_SIZEOF(STRUCT, FIELD) != sizeof(u8)); \ __emit_load8(BASE, STRUCT, FIELD, DEST); \ } while (0) -#ifdef CONFIG_SPARC64 -#define BIAS (STACK_BIAS - 4) -#else #define BIAS (-4) -#endif #define emit_ldmem(OFF, DEST) \ do { *prog++ = LD32I | RS1(SP) | S13(BIAS - (OFF)) | RD(DEST); \ @@ -249,13 +218,8 @@ do { *prog++ = ST32I | RS1(SP) | S13(BIAS - (OFF)) | RD(SRC); \ } while (0) #ifdef CONFIG_SMP -#ifdef CONFIG_SPARC64 -#define emit_load_cpu(REG) \ - emit_load16(G6, struct thread_info, cpu, REG) -#else #define emit_load_cpu(REG) \ emit_load32(G6, struct thread_info, cpu, REG) -#endif #else #define emit_load_cpu(REG) emit_clear(REG) #endif @@ -486,7 +450,6 @@ void bpf_jit_compile(struct bpf_prog *fp) if (K == 1) break; emit_write_y(G0); -#ifdef CONFIG_SPARC32 /* The Sparc v8 architecture requires * three instructions between a %y * register write and the first use. @@ -494,31 +457,21 @@ void bpf_jit_compile(struct bpf_prog *fp) emit_nop(); emit_nop(); emit_nop(); -#endif emit_alu_K(DIV, K); break; case BPF_ALU | BPF_DIV | BPF_X: /* A /= X; */ emit_cmpi(r_X, 0); if (pc_ret0 > 0) { t_offset = addrs[pc_ret0 - 1]; -#ifdef CONFIG_SPARC32 emit_branch(BE, t_offset + 20); -#else - emit_branch(BE, t_offset + 8); -#endif emit_nop(); /* delay slot */ } else { emit_branch_off(BNE, 16); emit_nop(); -#ifdef CONFIG_SPARC32 emit_jump(cleanup_addr + 20); -#else - emit_jump(cleanup_addr + 8); -#endif emit_clear(r_A); } emit_write_y(G0); -#ifdef CONFIG_SPARC32 /* The Sparc v8 architecture requires * three instructions between a %y * register write and the first use. @@ -526,7 +479,6 @@ void bpf_jit_compile(struct bpf_prog *fp) emit_nop(); emit_nop(); emit_nop(); -#endif emit_alu_X(DIV); break; case BPF_ALU | BPF_NEG: @@ -797,7 +749,6 @@ cond_branch: f_offset = addrs[i + filter[i].jf]; bpf_jit_dump(flen, proglen, pass + 1, image); if (image) { - bpf_flush_icache(image, image + proglen); fp->bpf_func = (void *)image; fp->jited = 1; } diff --git a/arch/sparc/net/bpf_jit_comp_64.c b/arch/sparc/net/bpf_jit_comp_64.c index 49b5f65f84ac..43bef1ceebbf 100644 --- a/arch/sparc/net/bpf_jit_comp_64.c +++ b/arch/sparc/net/bpf_jit_comp_64.c @@ -1 +1,1194 @@ -#include "bpf_jit_comp_32.c" +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "bpf_jit_64.h" + +int bpf_jit_enable __read_mostly; + +static inline bool is_simm13(unsigned int value) +{ + return value + 0x1000 < 0x2000; +} + +static void bpf_flush_icache(void *start_, void *end_) +{ + /* Cheetah's I-cache is fully coherent. */ + if (tlb_type == spitfire) { + unsigned long start = (unsigned long) start_; + unsigned long end = (unsigned long) end_; + + start &= ~7UL; + end = (end + 7UL) & ~7UL; + while (start < end) { + flushi(start); + start += 32; + } + } +} + +#define SEEN_DATAREF 1 /* might call external helpers */ +#define SEEN_XREG 2 /* ebx is used */ +#define SEEN_MEM 4 /* use mem[] for temporary storage */ + +#define S13(X) ((X) & 0x1fff) +#define IMMED 0x00002000 +#define RD(X) ((X) << 25) +#define RS1(X) ((X) << 14) +#define RS2(X) ((X)) +#define OP(X) ((X) << 30) +#define OP2(X) ((X) << 22) +#define OP3(X) ((X) << 19) +#define COND(X) ((X) << 25) +#define F1(X) OP(X) +#define F2(X, Y) (OP(X) | OP2(Y)) +#define F3(X, Y) (OP(X) | OP3(Y)) +#define ASI(X) (((X) & 0xff) << 5) + +#define CONDN COND(0x0) +#define CONDE COND(0x1) +#define CONDLE COND(0x2) +#define CONDL COND(0x3) +#define CONDLEU COND(0x4) +#define CONDCS COND(0x5) +#define CONDNEG COND(0x6) +#define CONDVC COND(0x7) +#define CONDA COND(0x8) +#define CONDNE COND(0x9) +#define CONDG COND(0xa) +#define CONDGE COND(0xb) +#define CONDGU COND(0xc) +#define CONDCC COND(0xd) +#define CONDPOS COND(0xe) +#define CONDVS COND(0xf) + +#define CONDGEU CONDCC +#define CONDLU CONDCS + +#define WDISP22(X) (((X) >> 2) & 0x3fffff) +#define WDISP19(X) (((X) >> 2) & 0x7ffff) + +#define ANNUL (1 << 29) +#define XCC (1 << 21) + +#define BRANCH (F2(0, 1) | XCC) + +#define BA (BRANCH | CONDA) +#define BG (BRANCH | CONDG) +#define BGU (BRANCH | CONDGU) +#define BLEU (BRANCH | CONDLEU) +#define BGE (BRANCH | CONDGE) +#define BGEU (BRANCH | CONDGEU) +#define BLU (BRANCH | CONDLU) +#define BE (BRANCH | CONDE) +#define BNE (BRANCH | CONDNE) + +#define SETHI(K, REG) \ + (F2(0, 0x4) | RD(REG) | (((K) >> 10) & 0x3fffff)) +#define OR_LO(K, REG) \ + (F3(2, 0x02) | IMMED | RS1(REG) | ((K) & 0x3ff) | RD(REG)) + +#define ADD F3(2, 0x00) +#define AND F3(2, 0x01) +#define ANDCC F3(2, 0x11) +#define OR F3(2, 0x02) +#define XOR F3(2, 0x03) +#define SUB F3(2, 0x04) +#define SUBCC F3(2, 0x14) +#define MUL F3(2, 0x0a) +#define MULX F3(2, 0x09) +#define UDIVX F3(2, 0x0d) +#define DIV F3(2, 0x0e) +#define SLL F3(2, 0x25) +#define SLLX (F3(2, 0x25)|(1<<12)) +#define SRA F3(2, 0x27) +#define SRAX (F3(2, 0x27)|(1<<12)) +#define SRL F3(2, 0x26) +#define SRLX (F3(2, 0x26)|(1<<12)) +#define JMPL F3(2, 0x38) +#define SAVE F3(2, 0x3c) +#define RESTORE F3(2, 0x3d) +#define CALL F1(1) +#define BR F2(0, 0x01) +#define RD_Y F3(2, 0x28) +#define WR_Y F3(2, 0x30) + +#define LD32 F3(3, 0x00) +#define LD8 F3(3, 0x01) +#define LD16 F3(3, 0x02) +#define LD64 F3(3, 0x0b) +#define LD64A F3(3, 0x1b) +#define ST8 F3(3, 0x05) +#define ST16 F3(3, 0x06) +#define ST32 F3(3, 0x04) +#define ST64 F3(3, 0x0e) + +#define CAS F3(3, 0x3c) +#define CASX F3(3, 0x3e) + +#define LDPTR LD64 +#define BASE_STACKFRAME 176 + +#define LD32I (LD32 | IMMED) +#define LD8I (LD8 | IMMED) +#define LD16I (LD16 | IMMED) +#define LD64I (LD64 | IMMED) +#define LDPTRI (LDPTR | IMMED) +#define ST32I (ST32 | IMMED) + +struct jit_ctx { + struct bpf_prog *prog; + unsigned int *offset; + int idx; + int epilogue_offset; + bool tmp_1_used; + bool tmp_2_used; + bool tmp_3_used; + bool saw_ld_abs_ind; + bool saw_frame_pointer; + bool saw_call; + bool saw_tail_call; + u32 *image; +}; + +#define TMP_REG_1 (MAX_BPF_JIT_REG + 0) +#define TMP_REG_2 (MAX_BPF_JIT_REG + 1) +#define SKB_HLEN_REG (MAX_BPF_JIT_REG + 2) +#define SKB_DATA_REG (MAX_BPF_JIT_REG + 3) +#define TMP_REG_3 (MAX_BPF_JIT_REG + 4) + +/* Map BPF registers to SPARC registers */ +static const int bpf2sparc[] = { + /* return value from in-kernel function, and exit value from eBPF */ + [BPF_REG_0] = O5, + + /* arguments from eBPF program to in-kernel function */ + [BPF_REG_1] = O0, + [BPF_REG_2] = O1, + [BPF_REG_3] = O2, + [BPF_REG_4] = O3, + [BPF_REG_5] = O4, + + /* callee saved registers that in-kernel function will preserve */ + [BPF_REG_6] = L0, + [BPF_REG_7] = L1, + [BPF_REG_8] = L2, + [BPF_REG_9] = L3, + + /* read-only frame pointer to access stack */ + [BPF_REG_FP] = L6, + + [BPF_REG_AX] = G7, + + /* temporary register for internal BPF JIT */ + [TMP_REG_1] = G1, + [TMP_REG_2] = G2, + [TMP_REG_3] = G3, + + [SKB_HLEN_REG] = L4, + [SKB_DATA_REG] = L5, +}; + +static void emit(const u32 insn, struct jit_ctx *ctx) +{ + if (ctx->image != NULL) + ctx->image[ctx->idx] = insn; + + ctx->idx++; +} + +static void emit_call(u32 *func, struct jit_ctx *ctx) +{ + if (ctx->image != NULL) { + void *here = &ctx->image[ctx->idx]; + unsigned int off; + + off = (void *)func - here; + ctx->image[ctx->idx] = CALL | ((off >> 2) & 0x3fffffff); + } + ctx->idx++; +} + +static void emit_nop(struct jit_ctx *ctx) +{ + emit(SETHI(0, G0), ctx); +} + +static void emit_reg_move(u32 from, u32 to, struct jit_ctx *ctx) +{ + emit(OR | RS1(G0) | RS2(from) | RD(to), ctx); +} + +/* Emit 32-bit constant, zero extended. */ +static void emit_set_const(s32 K, u32 reg, struct jit_ctx *ctx) +{ + emit(SETHI(K, reg), ctx); + emit(OR_LO(K, reg), ctx); +} + +/* Emit 32-bit constant, sign extended. */ +static void emit_set_const_sext(s32 K, u32 reg, struct jit_ctx *ctx) +{ + if (K >= 0) { + emit(SETHI(K, reg), ctx); + emit(OR_LO(K, reg), ctx); + } else { + u32 hbits = ~(u32) K; + u32 lbits = -0x400 | (u32) K; + + emit(SETHI(hbits, reg), ctx); + emit(XOR | IMMED | RS1(reg) | S13(lbits) | RD(reg), ctx); + } +} + +static void emit_alu(u32 opcode, u32 src, u32 dst, struct jit_ctx *ctx) +{ + emit(opcode | RS1(dst) | RS2(src) | RD(dst), ctx); +} + +static void emit_alu3(u32 opcode, u32 a, u32 b, u32 c, struct jit_ctx *ctx) +{ + emit(opcode | RS1(a) | RS2(b) | RD(c), ctx); +} + +static void emit_alu_K(unsigned int opcode, unsigned int dst, unsigned int imm, + struct jit_ctx *ctx) +{ + bool small_immed = is_simm13(imm); + unsigned int insn = opcode; + + insn |= RS1(dst) | RD(dst); + if (small_immed) { + emit(insn | IMMED | S13(imm), ctx); + } else { + unsigned int tmp = bpf2sparc[TMP_REG_1]; + + ctx->tmp_1_used = true; + + emit_set_const_sext(imm, tmp, ctx); + emit(insn | RS2(tmp), ctx); + } +} + +static void emit_alu3_K(unsigned int opcode, unsigned int src, unsigned int imm, + unsigned int dst, struct jit_ctx *ctx) +{ + bool small_immed = is_simm13(imm); + unsigned int insn = opcode; + + insn |= RS1(src) | RD(dst); + if (small_immed) { + emit(insn | IMMED | S13(imm), ctx); + } else { + unsigned int tmp = bpf2sparc[TMP_REG_1]; + + ctx->tmp_1_used = true; + + emit_set_const_sext(imm, tmp, ctx); + emit(insn | RS2(tmp), ctx); + } +} + +static void emit_loadimm32(s32 K, unsigned int dest, struct jit_ctx *ctx) +{ + if (K >= 0 && is_simm13(K)) { + /* or %g0, K, DEST */ + emit(OR | IMMED | RS1(G0) | S13(K) | RD(dest), ctx); + } else { + emit_set_const(K, dest, ctx); + } +} + +static void emit_loadimm(s32 K, unsigned int dest, struct jit_ctx *ctx) +{ + if (is_simm13(K)) { + /* or %g0, K, DEST */ + emit(OR | IMMED | RS1(G0) | S13(K) | RD(dest), ctx); + } else { + emit_set_const(K, dest, ctx); + } +} + +static void emit_loadimm_sext(s32 K, unsigned int dest, struct jit_ctx *ctx) +{ + if (is_simm13(K)) { + /* or %g0, K, DEST */ + emit(OR | IMMED | RS1(G0) | S13(K) | RD(dest), ctx); + } else { + emit_set_const_sext(K, dest, ctx); + } +} + +static void emit_loadimm64(u64 K, unsigned int dest, struct jit_ctx *ctx) +{ + unsigned int tmp = bpf2sparc[TMP_REG_1]; + u32 high_part = (K >> 32); + u32 low_part = (K & 0xffffffff); + + ctx->tmp_1_used = true; + + emit_set_const(high_part, tmp, ctx); + emit_set_const(low_part, dest, ctx); + emit_alu_K(SLLX, tmp, 32, ctx); + emit(OR | RS1(dest) | RS2(tmp) | RD(dest), ctx); +} + +static void emit_branch(unsigned int br_opc, unsigned int from_idx, unsigned int to_idx, + struct jit_ctx *ctx) +{ + unsigned int off = to_idx - from_idx; + + if (br_opc & XCC) + emit(br_opc | WDISP19(off << 2), ctx); + else + emit(br_opc | WDISP22(off << 2), ctx); +} + +#define emit_read_y(REG, CTX) emit(RD_Y | RD(REG), CTX) +#define emit_write_y(REG, CTX) emit(WR_Y | IMMED | RS1(REG) | S13(0), CTX) + +#define emit_cmp(R1, R2, CTX) \ + emit(SUBCC | RS1(R1) | RS2(R2) | RD(G0), CTX) + +#define emit_cmpi(R1, IMM, CTX) \ + emit(SUBCC | IMMED | RS1(R1) | S13(IMM) | RD(G0), CTX); + +#define emit_btst(R1, R2, CTX) \ + emit(ANDCC | RS1(R1) | RS2(R2) | RD(G0), CTX) + +#define emit_btsti(R1, IMM, CTX) \ + emit(ANDCC | IMMED | RS1(R1) | S13(IMM) | RD(G0), CTX) + +static void load_skb_regs(struct jit_ctx *ctx, u8 r_skb) +{ + const u8 r_headlen = bpf2sparc[SKB_HLEN_REG]; + const u8 r_data = bpf2sparc[SKB_DATA_REG]; + const u8 r_tmp = bpf2sparc[TMP_REG_1]; + unsigned int off; + + off = offsetof(struct sk_buff, len); + emit(LD32I | RS1(r_skb) | S13(off) | RD(r_headlen), ctx); + + off = offsetof(struct sk_buff, data_len); + emit(LD32I | RS1(r_skb) | S13(off) | RD(r_tmp), ctx); + + emit(SUB | RS1(r_headlen) | RS2(r_tmp) | RD(r_headlen), ctx); + + off = offsetof(struct sk_buff, data); + emit(LDPTRI | RS1(r_skb) | S13(off) | RD(r_data), ctx); +} + +/* Just skip the save instruction and the ctx register move. */ +#define BPF_TAILCALL_PROLOGUE_SKIP 16 +#define BPF_TAILCALL_CNT_SP_OFF (STACK_BIAS + 128) + +static void build_prologue(struct jit_ctx *ctx) +{ + s32 stack_needed = BASE_STACKFRAME; + + if (ctx->saw_frame_pointer || ctx->saw_tail_call) + stack_needed += MAX_BPF_STACK; + + if (ctx->saw_tail_call) + stack_needed += 8; + + /* save %sp, -176, %sp */ + emit(SAVE | IMMED | RS1(SP) | S13(-stack_needed) | RD(SP), ctx); + + /* tail_call_cnt = 0 */ + if (ctx->saw_tail_call) { + u32 off = BPF_TAILCALL_CNT_SP_OFF; + + emit(ST32 | IMMED | RS1(SP) | S13(off) | RD(G0), ctx); + } else { + emit_nop(ctx); + } + if (ctx->saw_frame_pointer) { + const u8 vfp = bpf2sparc[BPF_REG_FP]; + + emit(ADD | IMMED | RS1(FP) | S13(STACK_BIAS) | RD(vfp), ctx); + } + + emit_reg_move(I0, O0, ctx); + /* If you add anything here, adjust BPF_TAILCALL_PROLOGUE_SKIP above. */ + + if (ctx->saw_ld_abs_ind) + load_skb_regs(ctx, bpf2sparc[BPF_REG_1]); +} + +static void build_epilogue(struct jit_ctx *ctx) +{ + ctx->epilogue_offset = ctx->idx; + + /* ret (jmpl %i7 + 8, %g0) */ + emit(JMPL | IMMED | RS1(I7) | S13(8) | RD(G0), ctx); + + /* restore %i5, %g0, %o0 */ + emit(RESTORE | RS1(bpf2sparc[BPF_REG_0]) | RS2(G0) | RD(O0), ctx); +} + +static void emit_tail_call(struct jit_ctx *ctx) +{ + const u8 bpf_array = bpf2sparc[BPF_REG_2]; + const u8 bpf_index = bpf2sparc[BPF_REG_3]; + const u8 tmp = bpf2sparc[TMP_REG_1]; + u32 off; + + ctx->saw_tail_call = true; + + off = offsetof(struct bpf_array, map.max_entries); + emit(LD32 | IMMED | RS1(bpf_array) | S13(off) | RD(tmp), ctx); + emit_cmp(bpf_index, tmp, ctx); +#define OFFSET1 17 + emit_branch(BGEU, ctx->idx, ctx->idx + OFFSET1, ctx); + emit_nop(ctx); + + off = BPF_TAILCALL_CNT_SP_OFF; + emit(LD32 | IMMED | RS1(SP) | S13(off) | RD(tmp), ctx); + emit_cmpi(tmp, MAX_TAIL_CALL_CNT, ctx); +#define OFFSET2 13 + emit_branch(BGU, ctx->idx, ctx->idx + OFFSET2, ctx); + emit_nop(ctx); + + emit_alu_K(ADD, tmp, 1, ctx); + off = BPF_TAILCALL_CNT_SP_OFF; + emit(ST32 | IMMED | RS1(SP) | S13(off) | RD(tmp), ctx); + + emit_alu3_K(SLL, bpf_index, 3, tmp, ctx); + emit_alu(ADD, bpf_array, tmp, ctx); + off = offsetof(struct bpf_array, ptrs); + emit(LD64 | IMMED | RS1(tmp) | S13(off) | RD(tmp), ctx); + + emit_cmpi(tmp, 0, ctx); +#define OFFSET3 5 + emit_branch(BE, ctx->idx, ctx->idx + OFFSET3, ctx); + emit_nop(ctx); + + off = offsetof(struct bpf_prog, bpf_func); + emit(LD64 | IMMED | RS1(tmp) | S13(off) | RD(tmp), ctx); + + off = BPF_TAILCALL_PROLOGUE_SKIP; + emit(JMPL | IMMED | RS1(tmp) | S13(off) | RD(G0), ctx); + emit_nop(ctx); +} + +static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx) +{ + const u8 code = insn->code; + const u8 dst = bpf2sparc[insn->dst_reg]; + const u8 src = bpf2sparc[insn->src_reg]; + const int i = insn - ctx->prog->insnsi; + const s16 off = insn->off; + const s32 imm = insn->imm; + u32 *func; + + if (insn->src_reg == BPF_REG_FP) + ctx->saw_frame_pointer = true; + + switch (code) { + /* dst = src */ + case BPF_ALU | BPF_MOV | BPF_X: + emit_alu3_K(SRL, src, 0, dst, ctx); + break; + case BPF_ALU64 | BPF_MOV | BPF_X: + emit_reg_move(src, dst, ctx); + break; + /* dst = dst OP src */ + case BPF_ALU | BPF_ADD | BPF_X: + case BPF_ALU64 | BPF_ADD | BPF_X: + emit_alu(ADD, src, dst, ctx); + goto do_alu32_trunc; + case BPF_ALU | BPF_SUB | BPF_X: + case BPF_ALU64 | BPF_SUB | BPF_X: + emit_alu(SUB, src, dst, ctx); + goto do_alu32_trunc; + case BPF_ALU | BPF_AND | BPF_X: + case BPF_ALU64 | BPF_AND | BPF_X: + emit_alu(AND, src, dst, ctx); + goto do_alu32_trunc; + case BPF_ALU | BPF_OR | BPF_X: + case BPF_ALU64 | BPF_OR | BPF_X: + emit_alu(OR, src, dst, ctx); + goto do_alu32_trunc; + case BPF_ALU | BPF_XOR | BPF_X: + case BPF_ALU64 | BPF_XOR | BPF_X: + emit_alu(XOR, src, dst, ctx); + goto do_alu32_trunc; + case BPF_ALU | BPF_MUL | BPF_X: + emit_alu(MUL, src, dst, ctx); + goto do_alu32_trunc; + case BPF_ALU64 | BPF_MUL | BPF_X: + emit_alu(MULX, src, dst, ctx); + break; + case BPF_ALU | BPF_DIV | BPF_X: + emit_cmp(src, G0, ctx); + emit_branch(BE|ANNUL, ctx->idx, ctx->epilogue_offset, ctx); + emit_loadimm(0, bpf2sparc[BPF_REG_0], ctx); + + emit_write_y(G0, ctx); + emit_alu(DIV, src, dst, ctx); + break; + + case BPF_ALU64 | BPF_DIV | BPF_X: + emit_cmp(src, G0, ctx); + emit_branch(BE|ANNUL, ctx->idx, ctx->epilogue_offset, ctx); + emit_loadimm(0, bpf2sparc[BPF_REG_0], ctx); + + emit_alu(UDIVX, src, dst, ctx); + break; + + case BPF_ALU | BPF_MOD | BPF_X: { + const u8 tmp = bpf2sparc[TMP_REG_1]; + + ctx->tmp_1_used = true; + + emit_cmp(src, G0, ctx); + emit_branch(BE|ANNUL, ctx->idx, ctx->epilogue_offset, ctx); + emit_loadimm(0, bpf2sparc[BPF_REG_0], ctx); + + emit_write_y(G0, ctx); + emit_alu3(DIV, dst, src, tmp, ctx); + emit_alu3(MULX, tmp, src, tmp, ctx); + emit_alu3(SUB, dst, tmp, dst, ctx); + goto do_alu32_trunc; + } + case BPF_ALU64 | BPF_MOD | BPF_X: { + const u8 tmp = bpf2sparc[TMP_REG_1]; + + ctx->tmp_1_used = true; + + emit_cmp(src, G0, ctx); + emit_branch(BE|ANNUL, ctx->idx, ctx->epilogue_offset, ctx); + emit_loadimm(0, bpf2sparc[BPF_REG_0], ctx); + + emit_alu3(UDIVX, dst, src, tmp, ctx); + emit_alu3(MULX, tmp, src, tmp, ctx); + emit_alu3(SUB, dst, tmp, dst, ctx); + break; + } + case BPF_ALU | BPF_LSH | BPF_X: + emit_alu(SLL, src, dst, ctx); + goto do_alu32_trunc; + case BPF_ALU64 | BPF_LSH | BPF_X: + emit_alu(SLLX, src, dst, ctx); + break; + case BPF_ALU | BPF_RSH | BPF_X: + emit_alu(SRL, src, dst, ctx); + break; + case BPF_ALU64 | BPF_RSH | BPF_X: + emit_alu(SRLX, src, dst, ctx); + break; + case BPF_ALU | BPF_ARSH | BPF_X: + emit_alu(SRA, src, dst, ctx); + goto do_alu32_trunc; + case BPF_ALU64 | BPF_ARSH | BPF_X: + emit_alu(SRAX, src, dst, ctx); + break; + + /* dst = -dst */ + case BPF_ALU | BPF_NEG: + case BPF_ALU64 | BPF_NEG: + emit(SUB | RS1(0) | RS2(dst) | RD(dst), ctx); + goto do_alu32_trunc; + + case BPF_ALU | BPF_END | BPF_FROM_BE: + switch (imm) { + case 16: + emit_alu_K(SLL, dst, 16, ctx); + emit_alu_K(SRL, dst, 16, ctx); + break; + case 32: + emit_alu_K(SRL, dst, 0, ctx); + break; + case 64: + /* nop */ + break; + + } + break; + + /* dst = BSWAP##imm(dst) */ + case BPF_ALU | BPF_END | BPF_FROM_LE: { + const u8 tmp = bpf2sparc[TMP_REG_1]; + const u8 tmp2 = bpf2sparc[TMP_REG_2]; + + ctx->tmp_1_used = true; + switch (imm) { + case 16: + emit_alu3_K(AND, dst, 0xff, tmp, ctx); + emit_alu3_K(SRL, dst, 8, dst, ctx); + emit_alu3_K(AND, dst, 0xff, dst, ctx); + emit_alu3_K(SLL, tmp, 8, tmp, ctx); + emit_alu(OR, tmp, dst, ctx); + break; + + case 32: + ctx->tmp_2_used = true; + emit_alu3_K(SRL, dst, 24, tmp, ctx); /* tmp = dst >> 24 */ + emit_alu3_K(SRL, dst, 16, tmp2, ctx); /* tmp2 = dst >> 16 */ + emit_alu3_K(AND, tmp2, 0xff, tmp2, ctx);/* tmp2 = tmp2 & 0xff */ + emit_alu3_K(SLL, tmp2, 8, tmp2, ctx); /* tmp2 = tmp2 << 8 */ + emit_alu(OR, tmp2, tmp, ctx); /* tmp = tmp | tmp2 */ + emit_alu3_K(SRL, dst, 8, tmp2, ctx); /* tmp2 = dst >> 8 */ + emit_alu3_K(AND, tmp2, 0xff, tmp2, ctx);/* tmp2 = tmp2 & 0xff */ + emit_alu3_K(SLL, tmp2, 16, tmp2, ctx); /* tmp2 = tmp2 << 16 */ + emit_alu(OR, tmp2, tmp, ctx); /* tmp = tmp | tmp2 */ + emit_alu3_K(AND, dst, 0xff, dst, ctx); /* dst = dst & 0xff */ + emit_alu3_K(SLL, dst, 24, dst, ctx); /* dst = dst << 24 */ + emit_alu(OR, tmp, dst, ctx); /* dst = dst | tmp */ + break; + + case 64: + emit_alu3_K(ADD, SP, STACK_BIAS + 128, tmp, ctx); + emit(ST64 | RS1(tmp) | RS2(G0) | RD(dst), ctx); + emit(LD64A | ASI(ASI_PL) | RS1(tmp) | RS2(G0) | RD(dst), ctx); + break; + } + break; + } + /* dst = imm */ + case BPF_ALU | BPF_MOV | BPF_K: + emit_loadimm32(imm, dst, ctx); + break; + case BPF_ALU64 | BPF_MOV | BPF_K: + emit_loadimm_sext(imm, dst, ctx); + break; + /* dst = dst OP imm */ + case BPF_ALU | BPF_ADD | BPF_K: + case BPF_ALU64 | BPF_ADD | BPF_K: + emit_alu_K(ADD, dst, imm, ctx); + goto do_alu32_trunc; + case BPF_ALU | BPF_SUB | BPF_K: + case BPF_ALU64 | BPF_SUB | BPF_K: + emit_alu_K(SUB, dst, imm, ctx); + goto do_alu32_trunc; + case BPF_ALU | BPF_AND | BPF_K: + case BPF_ALU64 | BPF_AND | BPF_K: + emit_alu_K(AND, dst, imm, ctx); + goto do_alu32_trunc; + case BPF_ALU | BPF_OR | BPF_K: + case BPF_ALU64 | BPF_OR | BPF_K: + emit_alu_K(OR, dst, imm, ctx); + goto do_alu32_trunc; + case BPF_ALU | BPF_XOR | BPF_K: + case BPF_ALU64 | BPF_XOR | BPF_K: + emit_alu_K(XOR, dst, imm, ctx); + goto do_alu32_trunc; + case BPF_ALU | BPF_MUL | BPF_K: + emit_alu_K(MUL, dst, imm, ctx); + goto do_alu32_trunc; + case BPF_ALU64 | BPF_MUL | BPF_K: + emit_alu_K(MULX, dst, imm, ctx); + break; + case BPF_ALU | BPF_DIV | BPF_K: + if (imm == 0) + return -EINVAL; + + emit_write_y(G0, ctx); + emit_alu_K(DIV, dst, imm, ctx); + goto do_alu32_trunc; + case BPF_ALU64 | BPF_DIV | BPF_K: + if (imm == 0) + return -EINVAL; + + emit_alu_K(UDIVX, dst, imm, ctx); + break; + case BPF_ALU64 | BPF_MOD | BPF_K: + case BPF_ALU | BPF_MOD | BPF_K: { + const u8 tmp = bpf2sparc[TMP_REG_2]; + unsigned int div; + + if (imm == 0) + return -EINVAL; + + div = (BPF_CLASS(code) == BPF_ALU64) ? UDIVX : DIV; + + ctx->tmp_2_used = true; + + if (BPF_CLASS(code) != BPF_ALU64) + emit_write_y(G0, ctx); + if (is_simm13(imm)) { + emit(div | IMMED | RS1(dst) | S13(imm) | RD(tmp), ctx); + emit(MULX | IMMED | RS1(tmp) | S13(imm) | RD(tmp), ctx); + emit(SUB | RS1(dst) | RS2(tmp) | RD(dst), ctx); + } else { + const u8 tmp1 = bpf2sparc[TMP_REG_1]; + + ctx->tmp_1_used = true; + + emit_set_const_sext(imm, tmp1, ctx); + emit(div | RS1(dst) | RS2(tmp1) | RD(tmp), ctx); + emit(MULX | RS1(tmp) | RS2(tmp1) | RD(tmp), ctx); + emit(SUB | RS1(dst) | RS2(tmp) | RD(dst), ctx); + } + goto do_alu32_trunc; + } + case BPF_ALU | BPF_LSH | BPF_K: + emit_alu_K(SLL, dst, imm, ctx); + goto do_alu32_trunc; + case BPF_ALU64 | BPF_LSH | BPF_K: + emit_alu_K(SLLX, dst, imm, ctx); + break; + case BPF_ALU | BPF_RSH | BPF_K: + emit_alu_K(SRL, dst, imm, ctx); + break; + case BPF_ALU64 | BPF_RSH | BPF_K: + emit_alu_K(SRLX, dst, imm, ctx); + break; + case BPF_ALU | BPF_ARSH | BPF_K: + emit_alu_K(SRA, dst, imm, ctx); + goto do_alu32_trunc; + case BPF_ALU64 | BPF_ARSH | BPF_K: + emit_alu_K(SRAX, dst, imm, ctx); + break; + + do_alu32_trunc: + if (BPF_CLASS(code) == BPF_ALU) + emit_alu_K(SRL, dst, 0, ctx); + break; + + /* JUMP off */ + case BPF_JMP | BPF_JA: + emit_branch(BA, ctx->idx, ctx->offset[i + off], ctx); + emit_nop(ctx); + break; + /* IF (dst COND src) JUMP off */ + case BPF_JMP | BPF_JEQ | BPF_X: + case BPF_JMP | BPF_JGT | BPF_X: + case BPF_JMP | BPF_JGE | BPF_X: + case BPF_JMP | BPF_JNE | BPF_X: + case BPF_JMP | BPF_JSGT | BPF_X: + case BPF_JMP | BPF_JSGE | BPF_X: { + u32 br_opcode; + + emit_cmp(dst, src, ctx); +emit_cond_jmp: + switch (BPF_OP(code)) { + case BPF_JEQ: + br_opcode = BE; + break; + case BPF_JGT: + br_opcode = BGU; + break; + case BPF_JGE: + br_opcode = BGEU; + break; + case BPF_JSET: + case BPF_JNE: + br_opcode = BNE; + break; + case BPF_JSGT: + br_opcode = BG; + break; + case BPF_JSGE: + br_opcode = BGE; + break; + default: + /* Make sure we dont leak kernel information to the + * user. + */ + return -EFAULT; + } + emit_branch(br_opcode, ctx->idx, ctx->offset[i + off], ctx); + emit_nop(ctx); + break; + } + case BPF_JMP | BPF_JSET | BPF_X: + emit_btst(dst, src, ctx); + goto emit_cond_jmp; + /* IF (dst COND imm) JUMP off */ + case BPF_JMP | BPF_JEQ | BPF_K: + case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JGE | BPF_K: + case BPF_JMP | BPF_JNE | BPF_K: + case BPF_JMP | BPF_JSGT | BPF_K: + case BPF_JMP | BPF_JSGE | BPF_K: + if (is_simm13(imm)) { + emit_cmpi(dst, imm, ctx); + } else { + ctx->tmp_1_used = true; + emit_loadimm_sext(imm, bpf2sparc[TMP_REG_1], ctx); + emit_cmp(dst, bpf2sparc[TMP_REG_1], ctx); + } + goto emit_cond_jmp; + case BPF_JMP | BPF_JSET | BPF_K: + if (is_simm13(imm)) { + emit_btsti(dst, imm, ctx); + } else { + ctx->tmp_1_used = true; + emit_loadimm_sext(imm, bpf2sparc[TMP_REG_1], ctx); + emit_btst(dst, bpf2sparc[TMP_REG_1], ctx); + } + goto emit_cond_jmp; + + /* function call */ + case BPF_JMP | BPF_CALL: + { + u8 *func = ((u8 *)__bpf_call_base) + imm; + + ctx->saw_call = true; + + emit_call((u32 *)func, ctx); + emit_nop(ctx); + + emit_reg_move(O0, bpf2sparc[BPF_REG_0], ctx); + + if (bpf_helper_changes_pkt_data(func) && ctx->saw_ld_abs_ind) + load_skb_regs(ctx, bpf2sparc[BPF_REG_6]); + break; + } + + /* tail call */ + case BPF_JMP | BPF_CALL |BPF_X: + emit_tail_call(ctx); + break; + + /* function return */ + case BPF_JMP | BPF_EXIT: + /* Optimization: when last instruction is EXIT, + simply fallthrough to epilogue. */ + if (i == ctx->prog->len - 1) + break; + emit_branch(BA, ctx->idx, ctx->epilogue_offset, ctx); + emit_nop(ctx); + break; + + /* dst = imm64 */ + case BPF_LD | BPF_IMM | BPF_DW: + { + const struct bpf_insn insn1 = insn[1]; + u64 imm64; + + imm64 = (u64)insn1.imm << 32 | (u32)imm; + emit_loadimm64(imm64, dst, ctx); + + return 1; + } + + /* LDX: dst = *(size *)(src + off) */ + case BPF_LDX | BPF_MEM | BPF_W: + case BPF_LDX | BPF_MEM | BPF_H: + case BPF_LDX | BPF_MEM | BPF_B: + case BPF_LDX | BPF_MEM | BPF_DW: { + const u8 tmp = bpf2sparc[TMP_REG_1]; + u32 opcode = 0, rs2; + + ctx->tmp_1_used = true; + switch (BPF_SIZE(code)) { + case BPF_W: + opcode = LD32; + break; + case BPF_H: + opcode = LD16; + break; + case BPF_B: + opcode = LD8; + break; + case BPF_DW: + opcode = LD64; + break; + } + + if (is_simm13(off)) { + opcode |= IMMED; + rs2 = S13(off); + } else { + emit_loadimm(off, tmp, ctx); + rs2 = RS2(tmp); + } + emit(opcode | RS1(src) | rs2 | RD(dst), ctx); + break; + } + /* ST: *(size *)(dst + off) = imm */ + case BPF_ST | BPF_MEM | BPF_W: + case BPF_ST | BPF_MEM | BPF_H: + case BPF_ST | BPF_MEM | BPF_B: + case BPF_ST | BPF_MEM | BPF_DW: { + const u8 tmp = bpf2sparc[TMP_REG_1]; + const u8 tmp2 = bpf2sparc[TMP_REG_2]; + u32 opcode = 0, rs2; + + ctx->tmp_2_used = true; + emit_loadimm(imm, tmp2, ctx); + + switch (BPF_SIZE(code)) { + case BPF_W: + opcode = ST32; + break; + case BPF_H: + opcode = ST16; + break; + case BPF_B: + opcode = ST8; + break; + case BPF_DW: + opcode = ST64; + break; + } + + if (is_simm13(off)) { + opcode |= IMMED; + rs2 = S13(off); + } else { + ctx->tmp_1_used = true; + emit_loadimm(off, tmp, ctx); + rs2 = RS2(tmp); + } + emit(opcode | RS1(dst) | rs2 | RD(tmp2), ctx); + break; + } + + /* STX: *(size *)(dst + off) = src */ + case BPF_STX | BPF_MEM | BPF_W: + case BPF_STX | BPF_MEM | BPF_H: + case BPF_STX | BPF_MEM | BPF_B: + case BPF_STX | BPF_MEM | BPF_DW: { + const u8 tmp = bpf2sparc[TMP_REG_1]; + u32 opcode = 0, rs2; + + switch (BPF_SIZE(code)) { + case BPF_W: + opcode = ST32; + break; + case BPF_H: + opcode = ST16; + break; + case BPF_B: + opcode = ST8; + break; + case BPF_DW: + opcode = ST64; + break; + } + if (is_simm13(off)) { + opcode |= IMMED; + rs2 = S13(off); + } else { + ctx->tmp_1_used = true; + emit_loadimm(off, tmp, ctx); + rs2 = RS2(tmp); + } + emit(opcode | RS1(dst) | rs2 | RD(src), ctx); + break; + } + + /* STX XADD: lock *(u32 *)(dst + off) += src */ + case BPF_STX | BPF_XADD | BPF_W: { + const u8 tmp = bpf2sparc[TMP_REG_1]; + const u8 tmp2 = bpf2sparc[TMP_REG_2]; + const u8 tmp3 = bpf2sparc[TMP_REG_3]; + + ctx->tmp_1_used = true; + ctx->tmp_2_used = true; + ctx->tmp_3_used = true; + emit_loadimm(off, tmp, ctx); + emit_alu3(ADD, dst, tmp, tmp, ctx); + + emit(LD32 | RS1(tmp) | RS2(G0) | RD(tmp2), ctx); + emit_alu3(ADD, tmp2, src, tmp3, ctx); + emit(CAS | ASI(ASI_P) | RS1(tmp) | RS2(tmp2) | RD(tmp3), ctx); + emit_cmp(tmp2, tmp3, ctx); + emit_branch(BNE, 4, 0, ctx); + emit_nop(ctx); + break; + } + /* STX XADD: lock *(u64 *)(dst + off) += src */ + case BPF_STX | BPF_XADD | BPF_DW: { + const u8 tmp = bpf2sparc[TMP_REG_1]; + const u8 tmp2 = bpf2sparc[TMP_REG_2]; + const u8 tmp3 = bpf2sparc[TMP_REG_3]; + + ctx->tmp_1_used = true; + ctx->tmp_2_used = true; + ctx->tmp_3_used = true; + emit_loadimm(off, tmp, ctx); + emit_alu3(ADD, dst, tmp, tmp, ctx); + + emit(LD64 | RS1(tmp) | RS2(G0) | RD(tmp2), ctx); + emit_alu3(ADD, tmp2, src, tmp3, ctx); + emit(CASX | ASI(ASI_P) | RS1(tmp) | RS2(tmp2) | RD(tmp3), ctx); + emit_cmp(tmp2, tmp3, ctx); + emit_branch(BNE, 4, 0, ctx); + emit_nop(ctx); + break; + } +#define CHOOSE_LOAD_FUNC(K, func) \ + ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative_offset : func) : func##_positive_offset) + + /* R0 = ntohx(*(size *)(((struct sk_buff *)R6)->data + imm)) */ + case BPF_LD | BPF_ABS | BPF_W: + func = CHOOSE_LOAD_FUNC(imm, bpf_jit_load_word); + goto common_load; + case BPF_LD | BPF_ABS | BPF_H: + func = CHOOSE_LOAD_FUNC(imm, bpf_jit_load_half); + goto common_load; + case BPF_LD | BPF_ABS | BPF_B: + func = CHOOSE_LOAD_FUNC(imm, bpf_jit_load_byte); + goto common_load; + /* R0 = ntohx(*(size *)(((struct sk_buff *)R6)->data + src + imm)) */ + case BPF_LD | BPF_IND | BPF_W: + func = bpf_jit_load_word; + goto common_load; + case BPF_LD | BPF_IND | BPF_H: + func = bpf_jit_load_half; + goto common_load; + + case BPF_LD | BPF_IND | BPF_B: + func = bpf_jit_load_byte; + common_load: + ctx->saw_ld_abs_ind = true; + + emit_reg_move(bpf2sparc[BPF_REG_6], O0, ctx); + emit_loadimm(imm, O1, ctx); + + if (BPF_MODE(code) == BPF_IND) + emit_alu(ADD, src, O1, ctx); + + emit_call(func, ctx); + emit_alu_K(SRA, O1, 0, ctx); + + emit_reg_move(O0, bpf2sparc[BPF_REG_0], ctx); + break; + + default: + pr_err_once("unknown opcode %02x\n", code); + return -EINVAL; + } + + return 0; +} + +static int build_body(struct jit_ctx *ctx) +{ + const struct bpf_prog *prog = ctx->prog; + int i; + + for (i = 0; i < prog->len; i++) { + const struct bpf_insn *insn = &prog->insnsi[i]; + int ret; + + ret = build_insn(insn, ctx); + ctx->offset[i] = ctx->idx; + + if (ret > 0) { + i++; + continue; + } + if (ret) + return ret; + } + return 0; +} + +static void jit_fill_hole(void *area, unsigned int size) +{ + u32 *ptr; + /* We are guaranteed to have aligned memory. */ + for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) + *ptr++ = 0x91d02005; /* ta 5 */ +} + +struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) +{ + struct bpf_prog *tmp, *orig_prog = prog; + struct bpf_binary_header *header; + bool tmp_blinded = false; + struct jit_ctx ctx; + u32 image_size; + u8 *image_ptr; + int pass; + + if (!bpf_jit_enable) + return orig_prog; + + tmp = bpf_jit_blind_constants(prog); + /* If blinding was requested and we failed during blinding, + * we must fall back to the interpreter. + */ + if (IS_ERR(tmp)) + return orig_prog; + if (tmp != prog) { + tmp_blinded = true; + prog = tmp; + } + + memset(&ctx, 0, sizeof(ctx)); + ctx.prog = prog; + + ctx.offset = kcalloc(prog->len, sizeof(unsigned int), GFP_KERNEL); + if (ctx.offset == NULL) { + prog = orig_prog; + goto out; + } + + /* Fake pass to detect features used, and get an accurate assessment + * of what the final image size will be. + */ + if (build_body(&ctx)) { + prog = orig_prog; + goto out_off; + } + build_prologue(&ctx); + build_epilogue(&ctx); + + /* Now we know the actual image size. */ + image_size = sizeof(u32) * ctx.idx; + header = bpf_jit_binary_alloc(image_size, &image_ptr, + sizeof(u32), jit_fill_hole); + if (header == NULL) { + prog = orig_prog; + goto out_off; + } + + ctx.image = (u32 *)image_ptr; + + for (pass = 1; pass < 3; pass++) { + ctx.idx = 0; + + build_prologue(&ctx); + + if (build_body(&ctx)) { + bpf_jit_binary_free(header); + prog = orig_prog; + goto out_off; + } + + build_epilogue(&ctx); + + if (bpf_jit_enable > 1) + pr_info("Pass %d: shrink = %d, seen = [%c%c%c%c%c%c%c]\n", pass, + image_size - (ctx.idx * 4), + ctx.tmp_1_used ? '1' : ' ', + ctx.tmp_2_used ? '2' : ' ', + ctx.tmp_3_used ? '3' : ' ', + ctx.saw_ld_abs_ind ? 'L' : ' ', + ctx.saw_frame_pointer ? 'F' : ' ', + ctx.saw_call ? 'C' : ' ', + ctx.saw_tail_call ? 'T' : ' '); + } + + if (bpf_jit_enable > 1) + bpf_jit_dump(prog->len, image_size, pass, ctx.image); + + bpf_flush_icache(header, (u8 *)header + (header->pages * PAGE_SIZE)); + + bpf_jit_binary_lock_ro(header); + + prog->bpf_func = (void *)ctx.image; + prog->jited = 1; + +out_off: + kfree(ctx.offset); +out: + if (tmp_blinded) + bpf_jit_prog_release_other(prog, prog == orig_prog ? + tmp : orig_prog); + return prog; +} -- cgit From b0c47807d31deccdfb78a22a0d04dd9785bdb9d3 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 22 Apr 2017 12:31:05 -0700 Subject: bpf: Add sparc support to tools and samples. Signed-off-by: David S. Miller Acked-by: Daniel Borkmann --- samples/bpf/bpf_helpers.h | 19 +++++++++++++++++++ tools/build/feature/test-bpf.c | 3 +++ tools/lib/bpf/bpf.c | 2 ++ 3 files changed, 24 insertions(+) diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h index 52de9d88c021..9a9c95f2c9fb 100644 --- a/samples/bpf/bpf_helpers.h +++ b/samples/bpf/bpf_helpers.h @@ -146,11 +146,30 @@ static int (*bpf_skb_change_head)(void *, int len, int flags) = #define PT_REGS_SP(x) ((x)->sp) #define PT_REGS_IP(x) ((x)->nip) +#elif defined(__sparc__) + +#define PT_REGS_PARM1(x) ((x)->u_regs[UREG_I0]) +#define PT_REGS_PARM2(x) ((x)->u_regs[UREG_I1]) +#define PT_REGS_PARM3(x) ((x)->u_regs[UREG_I2]) +#define PT_REGS_PARM4(x) ((x)->u_regs[UREG_I3]) +#define PT_REGS_PARM5(x) ((x)->u_regs[UREG_I4]) +#define PT_REGS_RET(x) ((x)->u_regs[UREG_I7]) +#define PT_REGS_RC(x) ((x)->u_regs[UREG_I0]) +#define PT_REGS_SP(x) ((x)->u_regs[UREG_FP]) +#if defined(__arch64__) +#define PT_REGS_IP(x) ((x)->tpc) +#else +#define PT_REGS_IP(x) ((x)->pc) +#endif + #endif #ifdef __powerpc__ #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; }) #define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP +#elif defined(__sparc__) +#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); }) +#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP #else #define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ \ bpf_probe_read(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); }) diff --git a/tools/build/feature/test-bpf.c b/tools/build/feature/test-bpf.c index e04ab89a1013..ebc6dceddb58 100644 --- a/tools/build/feature/test-bpf.c +++ b/tools/build/feature/test-bpf.c @@ -9,6 +9,9 @@ # define __NR_bpf 321 # elif defined(__aarch64__) # define __NR_bpf 280 +# elif defined(__sparc__) +# define __NR_bpf 349 +# else # error __NR_bpf not defined. libbpf does not support your arch. # endif #endif diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index f84c398c11f4..4fe444b8092e 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -37,6 +37,8 @@ # define __NR_bpf 321 # elif defined(__aarch64__) # define __NR_bpf 280 +# elif defined(__sparc__) +# define __NR_bpf 349 # else # error __NR_bpf not defined. libbpf does not support your arch. # endif -- cgit From e2989ee9746b3f2e78d1a39bbc402d884e8b8bf1 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 23 Apr 2017 09:01:00 -0700 Subject: bpf, doc: update list of architectures that do eBPF JIT update the list and remove 'in the future' statement, since all still alive 64-bit architectures now do eBPF JIT. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- Documentation/networking/filter.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Documentation/networking/filter.txt b/Documentation/networking/filter.txt index 683ada5ad81d..b69b205501de 100644 --- a/Documentation/networking/filter.txt +++ b/Documentation/networking/filter.txt @@ -595,10 +595,9 @@ got from bpf_prog_create(), and 'ctx' the given context (e.g. skb pointer). All constraints and restrictions from bpf_check_classic() apply before a conversion to the new layout is being done behind the scenes! -Currently, the classic BPF format is being used for JITing on most of the -architectures. x86-64, aarch64 and s390x perform JIT compilation from eBPF -instruction set, however, future work will migrate other JIT compilers as well, -so that they will profit from the very same benefits. +Currently, the classic BPF format is being used for JITing on most 32-bit +architectures, whereas x86-64, aarch64, s390x, powerpc64, sparc64 perform JIT +compilation from eBPF instruction set. Some core changes of the new internal format: -- cgit From e892d2d40445a14a19530a2be8c489b87bcd7c19 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Mon, 24 Apr 2017 07:33:56 +0200 Subject: esp: Fix misplaced spin_unlock_bh. A recent commit moved esp_alloc_tmp() out of a lock protected region, but forgot to remove the unlock from the error path. This patch removes the forgotten unlock. While at it, remove some unneeded error assignments too. Fixes: fca11ebde3f0 ("esp4: Reorganize esp_output") Fixes: 383d0350f2cc ("esp6: Reorganize esp_output") Reported-by: Dan Carpenter Signed-off-by: Steffen Klassert --- net/ipv4/esp4.c | 6 +----- net/ipv6/esp6.c | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 7e501adb5042..7f2caf71212b 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -356,11 +356,8 @@ int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * ivlen = crypto_aead_ivsize(aead); tmp = esp_alloc_tmp(aead, esp->nfrags + 2, extralen); - if (!tmp) { - spin_unlock_bh(&x->lock); - err = -ENOMEM; + if (!tmp) goto error; - } extra = esp_tmp_extra(tmp); iv = esp_tmp_iv(aead, tmp, extralen); @@ -389,7 +386,6 @@ int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * spin_lock_bh(&x->lock); if (unlikely(!skb_page_frag_refill(allocsize, pfrag, GFP_ATOMIC))) { spin_unlock_bh(&x->lock); - err = -ENOMEM; goto error; } diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 8b55abf1c45b..1fe99ba8066c 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -330,11 +330,8 @@ int esp6_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info ivlen = crypto_aead_ivsize(aead); tmp = esp_alloc_tmp(aead, esp->nfrags + 2, seqhilen); - if (!tmp) { - spin_unlock_bh(&x->lock); - err = -ENOMEM; + if (!tmp) goto error; - } seqhi = esp_tmp_seqhi(tmp); iv = esp_tmp_iv(aead, tmp, seqhilen); @@ -362,7 +359,6 @@ int esp6_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info spin_lock_bh(&x->lock); if (unlikely(!skb_page_frag_refill(allocsize, pfrag, GFP_ATOMIC))) { spin_unlock_bh(&x->lock); - err = -ENOMEM; goto error; } -- cgit From 270837b399f7ab7602f473fa514fe1138b308ec0 Mon Sep 17 00:00:00 2001 From: "sudarsana.kalluru@cavium.com" Date: Thu, 20 Apr 2017 22:31:16 -0700 Subject: qed: Cleanup DCBx unnecessary parameters. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dcbx.c | 38 ++++++++++++------------------ drivers/net/ethernet/qlogic/qed/qed_dcbx.h | 2 +- drivers/net/ethernet/qlogic/qed/qed_dev.c | 2 +- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c index e575cfe70454..3d53d1146343 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c @@ -271,8 +271,8 @@ qed_dcbx_process_tlv(struct qed_hwfn *p_hwfn, struct dcbx_app_priority_entry *p_tbl, u32 pri_tc_tbl, int count, u8 dcbx_version) { - u8 tc, priority_map; enum dcbx_protocol_type type; + u8 tc, priority_map; bool enable, ieee; u16 protocol_id; int priority; @@ -620,8 +620,7 @@ qed_dcbx_get_common_params(struct qed_hwfn *p_hwfn, } static void -qed_dcbx_get_local_params(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, struct qed_dcbx_get *params) +qed_dcbx_get_local_params(struct qed_hwfn *p_hwfn, struct qed_dcbx_get *params) { struct dcbx_features *p_feat; @@ -633,8 +632,7 @@ qed_dcbx_get_local_params(struct qed_hwfn *p_hwfn, } static void -qed_dcbx_get_remote_params(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, struct qed_dcbx_get *params) +qed_dcbx_get_remote_params(struct qed_hwfn *p_hwfn, struct qed_dcbx_get *params) { struct dcbx_features *p_feat; @@ -647,7 +645,6 @@ qed_dcbx_get_remote_params(struct qed_hwfn *p_hwfn, static void qed_dcbx_get_operational_params(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, struct qed_dcbx_get *params) { struct qed_dcbx_operational_params *p_operational; @@ -697,7 +694,6 @@ qed_dcbx_get_operational_params(struct qed_hwfn *p_hwfn, static void qed_dcbx_get_local_lldp_params(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, struct qed_dcbx_get *params) { struct lldp_config_params_s *p_local; @@ -712,7 +708,6 @@ qed_dcbx_get_local_lldp_params(struct qed_hwfn *p_hwfn, static void qed_dcbx_get_remote_lldp_params(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, struct qed_dcbx_get *params) { struct lldp_status_params_s *p_remote; @@ -726,25 +721,24 @@ qed_dcbx_get_remote_lldp_params(struct qed_hwfn *p_hwfn, } static int -qed_dcbx_get_params(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - struct qed_dcbx_get *p_params, +qed_dcbx_get_params(struct qed_hwfn *p_hwfn, struct qed_dcbx_get *p_params, enum qed_mib_read_type type) { switch (type) { case QED_DCBX_REMOTE_MIB: - qed_dcbx_get_remote_params(p_hwfn, p_ptt, p_params); + qed_dcbx_get_remote_params(p_hwfn, p_params); break; case QED_DCBX_LOCAL_MIB: - qed_dcbx_get_local_params(p_hwfn, p_ptt, p_params); + qed_dcbx_get_local_params(p_hwfn, p_params); break; case QED_DCBX_OPERATIONAL_MIB: - qed_dcbx_get_operational_params(p_hwfn, p_ptt, p_params); + qed_dcbx_get_operational_params(p_hwfn, p_params); break; case QED_DCBX_REMOTE_LLDP_MIB: - qed_dcbx_get_remote_lldp_params(p_hwfn, p_ptt, p_params); + qed_dcbx_get_remote_lldp_params(p_hwfn, p_params); break; case QED_DCBX_LOCAL_LLDP_MIB: - qed_dcbx_get_local_lldp_params(p_hwfn, p_ptt, p_params); + qed_dcbx_get_local_lldp_params(p_hwfn, p_params); break; default: DP_ERR(p_hwfn, "MIB read err, unknown mib type %d\n", type); @@ -902,7 +896,8 @@ qed_dcbx_mib_update_event(struct qed_hwfn *p_hwfn, qed_sp_pf_update(p_hwfn); } } - qed_dcbx_get_params(p_hwfn, p_ptt, &p_hwfn->p_dcbx_info->get, type); + + qed_dcbx_get_params(p_hwfn, &p_hwfn->p_dcbx_info->get, type); qed_dcbx_aen(p_hwfn, type); return rc; @@ -910,17 +905,14 @@ qed_dcbx_mib_update_event(struct qed_hwfn *p_hwfn, int qed_dcbx_info_alloc(struct qed_hwfn *p_hwfn) { - int rc = 0; - p_hwfn->p_dcbx_info = kzalloc(sizeof(*p_hwfn->p_dcbx_info), GFP_KERNEL); if (!p_hwfn->p_dcbx_info) - rc = -ENOMEM; + return -ENOMEM; - return rc; + return 0; } -void qed_dcbx_info_free(struct qed_hwfn *p_hwfn, - struct qed_dcbx_info *p_dcbx_info) +void qed_dcbx_info_free(struct qed_hwfn *p_hwfn) { kfree(p_hwfn->p_dcbx_info); } @@ -992,7 +984,7 @@ static int qed_dcbx_query_params(struct qed_hwfn *p_hwfn, if (rc) goto out; - rc = qed_dcbx_get_params(p_hwfn, p_ptt, p_get, type); + rc = qed_dcbx_get_params(p_hwfn, p_get, type); out: qed_ptt_release(p_hwfn, p_ptt); diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.h b/drivers/net/ethernet/qlogic/qed/qed_dcbx.h index 2eb988fe1298..414e26268f3a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.h +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.h @@ -119,7 +119,7 @@ qed_dcbx_mib_update_event(struct qed_hwfn *, struct qed_ptt *, enum qed_mib_read_type); int qed_dcbx_info_alloc(struct qed_hwfn *p_hwfn); -void qed_dcbx_info_free(struct qed_hwfn *, struct qed_dcbx_info *); +void qed_dcbx_info_free(struct qed_hwfn *p_hwfn); void qed_dcbx_set_pf_update_params(struct qed_dcbx_results *p_src, struct pf_update_ramrod_data *p_dest); diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index fad73195010d..6d2430896c5a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -183,7 +183,7 @@ void qed_resc_free(struct qed_dev *cdev) } qed_iov_free(p_hwfn); qed_dmae_info_free(p_hwfn); - qed_dcbx_info_free(p_hwfn, p_hwfn->p_dcbx_info); + qed_dcbx_info_free(p_hwfn); } } -- cgit From 449ad505e9d2f420b7bf590a708c101ff587593e Mon Sep 17 00:00:00 2001 From: "sudarsana.kalluru@cavium.com" Date: Thu, 20 Apr 2017 22:31:17 -0700 Subject: qed: Separate RoCE DCBx support for V2. In the older firmware there was no distinction between RoCE and RoCEv2 whereas the newer firmware (8.15.3.0) allows us to configure each independently. Driver need to populate the RoCEv2 data in its specific structure. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dcbx.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c index 3d53d1146343..ea16850b8ec5 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c @@ -951,14 +951,9 @@ void qed_dcbx_set_pf_update_params(struct qed_dcbx_results *p_src, p_dcb_data = &p_dest->fcoe_dcb_data; qed_dcbx_update_protocol_data(p_dcb_data, p_src, DCBX_PROTOCOL_FCOE); p_dcb_data = &p_dest->roce_dcb_data; - - if (p_src->arr[DCBX_PROTOCOL_ROCE].update) - qed_dcbx_update_protocol_data(p_dcb_data, p_src, - DCBX_PROTOCOL_ROCE); - if (p_src->arr[DCBX_PROTOCOL_ROCE_V2].update) - qed_dcbx_update_protocol_data(p_dcb_data, p_src, - DCBX_PROTOCOL_ROCE_V2); - + qed_dcbx_update_protocol_data(p_dcb_data, p_src, DCBX_PROTOCOL_ROCE); + p_dcb_data = &p_dest->rroce_dcb_data; + qed_dcbx_update_protocol_data(p_dcb_data, p_src, DCBX_PROTOCOL_ROCE_V2); p_dcb_data = &p_dest->iscsi_dcb_data; qed_dcbx_update_protocol_data(p_dcb_data, p_src, DCBX_PROTOCOL_ISCSI); p_dcb_data = &p_dest->eth_dcb_data; -- cgit From dfbeb85f2fe6093ac7deb5dd40f4fd6c75b9fc78 Mon Sep 17 00:00:00 2001 From: "sudarsana.kalluru@cavium.com" Date: Thu, 20 Apr 2017 22:31:18 -0700 Subject: qed: Add additional DCBx debug messages. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dcbx.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c index ea16850b8ec5..46865a582eb4 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c @@ -556,8 +556,9 @@ qed_dcbx_get_pfc_data(struct qed_hwfn *p_hwfn, p_params->pfc.prio[7] = !!(pfc_map & DCBX_PFC_PRI_EN_BITMAP_PRI_7); DP_VERBOSE(p_hwfn, QED_MSG_DCB, - "PFC params: willing %d, pfc_bitmap %d\n", - p_params->pfc.willing, pfc_map); + "PFC params: willing %d, pfc_bitmap %u max_tc = %u enabled = %d\n", + p_params->pfc.willing, pfc_map, p_params->pfc.max_tc, + p_params->pfc.enabled); } static void @@ -576,10 +577,10 @@ qed_dcbx_get_ets_data(struct qed_hwfn *p_hwfn, p_params->max_ets_tc = QED_MFW_GET_FIELD(p_ets->flags, DCBX_ETS_MAX_TCS); DP_VERBOSE(p_hwfn, QED_MSG_DCB, - "ETS params: willing %d, ets_cbs %d pri_tc_tbl_0 %x max_ets_tc %d\n", - p_params->ets_willing, - p_params->ets_cbs, - p_ets->pri_tc_tbl[0], p_params->max_ets_tc); + "ETS params: willing %d, enabled = %d ets_cbs %d pri_tc_tbl_0 %x max_ets_tc %d\n", + p_params->ets_willing, p_params->ets_enabled, + p_params->ets_cbs, p_ets->pri_tc_tbl[0], + p_params->max_ets_tc); if (p_params->ets_enabled && !p_params->max_ets_tc) { p_params->max_ets_tc = QED_MAX_PFC_PRIORITIES; @@ -665,6 +666,7 @@ qed_dcbx_get_operational_params(struct qed_hwfn *p_hwfn, if (!enabled) { p_operational->enabled = enabled; p_operational->valid = false; + DP_VERBOSE(p_hwfn, QED_MSG_DCB, "Dcbx is disabled\n"); return; } @@ -1154,6 +1156,9 @@ qed_dcbx_set_local_params(struct qed_hwfn *p_hwfn, local_admin->config = DCBX_CONFIG_VERSION_DISABLED; } + DP_VERBOSE(p_hwfn, QED_MSG_DCB, "Dcbx version = %d\n", + local_admin->config); + if (params->override_flags & QED_DCBX_OVERRIDE_PFC_CFG) qed_dcbx_set_pfc_data(p_hwfn, &local_admin->features.pfc, ¶ms->config.params); -- cgit From 05a79f925d7494a7a9a72d25943a3541b1c54066 Mon Sep 17 00:00:00 2001 From: "sudarsana.kalluru@cavium.com" Date: Thu, 20 Apr 2017 22:31:19 -0700 Subject: qed: Support dcbnl IEEE selector field. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dcbx.c | 53 ++++++++++++++++++++++----- drivers/net/ethernet/qlogic/qede/qede_dcbnl.c | 5 +++ 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c index 46865a582eb4..40a4975443e4 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c @@ -2192,15 +2192,46 @@ qed_dcbnl_ieee_peer_getpfc(struct qed_dev *cdev, struct ieee_pfc *pfc) return qed_dcbnl_get_ieee_pfc(cdev, pfc, true); } +static int qed_get_sf_ieee_value(u8 selector, u8 *sf_ieee) +{ + switch (selector) { + case IEEE_8021QAZ_APP_SEL_ETHERTYPE: + *sf_ieee = QED_DCBX_SF_IEEE_ETHTYPE; + break; + case IEEE_8021QAZ_APP_SEL_STREAM: + *sf_ieee = QED_DCBX_SF_IEEE_TCP_PORT; + break; + case IEEE_8021QAZ_APP_SEL_DGRAM: + *sf_ieee = QED_DCBX_SF_IEEE_UDP_PORT; + break; + case IEEE_8021QAZ_APP_SEL_ANY: + *sf_ieee = QED_DCBX_SF_IEEE_TCP_UDP_PORT; + break; + default: + return -EINVAL; + } + + return 0; +} + static int qed_dcbnl_ieee_getapp(struct qed_dev *cdev, struct dcb_app *app) { struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev); struct qed_dcbx_get *dcbx_info; struct qed_app_entry *entry; - bool ethtype; u8 prio = 0; + u8 sf_ieee; int i; + DP_VERBOSE(hwfn, QED_MSG_DCB, "selector = %d protocol = %d\n", + app->selector, app->protocol); + + if (qed_get_sf_ieee_value(app->selector, &sf_ieee)) { + DP_INFO(cdev, "Invalid selector field value %d\n", + app->selector); + return -EINVAL; + } + dcbx_info = qed_dcbnl_get_dcbx(hwfn, QED_DCBX_OPERATIONAL_MIB); if (!dcbx_info) return -EINVAL; @@ -2211,11 +2242,9 @@ static int qed_dcbnl_ieee_getapp(struct qed_dev *cdev, struct dcb_app *app) return -EINVAL; } - /* ieee defines the selector field value for ethertype to be 1 */ - ethtype = !!((app->selector - 1) == DCB_APP_IDTYPE_ETHTYPE); for (i = 0; i < QED_DCBX_MAX_APP_PROTOCOL; i++) { entry = &dcbx_info->operational.params.app_entry[i]; - if ((entry->ethtype == ethtype) && + if ((entry->sf_ieee == sf_ieee) && (entry->proto_id == app->protocol)) { prio = entry->prio; break; @@ -2243,14 +2272,22 @@ static int qed_dcbnl_ieee_setapp(struct qed_dev *cdev, struct dcb_app *app) struct qed_dcbx_set dcbx_set; struct qed_app_entry *entry; struct qed_ptt *ptt; - bool ethtype; + u8 sf_ieee; int rc, i; + DP_VERBOSE(hwfn, QED_MSG_DCB, "selector = %d protocol = %d pri = %d\n", + app->selector, app->protocol, app->priority); if (app->priority < 0 || app->priority >= QED_MAX_PFC_PRIORITIES) { DP_INFO(hwfn, "Invalid priority %d\n", app->priority); return -EINVAL; } + if (qed_get_sf_ieee_value(app->selector, &sf_ieee)) { + DP_INFO(cdev, "Invalid selector field value %d\n", + app->selector); + return -EINVAL; + } + dcbx_info = qed_dcbnl_get_dcbx(hwfn, QED_DCBX_OPERATIONAL_MIB); if (!dcbx_info) return -EINVAL; @@ -2268,11 +2305,9 @@ static int qed_dcbnl_ieee_setapp(struct qed_dev *cdev, struct dcb_app *app) if (rc) return -EINVAL; - /* ieee defines the selector field value for ethertype to be 1 */ - ethtype = !!((app->selector - 1) == DCB_APP_IDTYPE_ETHTYPE); for (i = 0; i < QED_DCBX_MAX_APP_PROTOCOL; i++) { entry = &dcbx_set.config.params.app_entry[i]; - if ((entry->ethtype == ethtype) && + if ((entry->sf_ieee == sf_ieee) && (entry->proto_id == app->protocol)) break; /* First empty slot */ @@ -2288,7 +2323,7 @@ static int qed_dcbnl_ieee_setapp(struct qed_dev *cdev, struct dcb_app *app) } dcbx_set.override_flags |= QED_DCBX_OVERRIDE_APP_CFG; - dcbx_set.config.params.app_entry[i].ethtype = ethtype; + dcbx_set.config.params.app_entry[i].sf_ieee = sf_ieee; dcbx_set.config.params.app_entry[i].proto_id = app->protocol; dcbx_set.config.params.app_entry[i].prio = BIT(app->priority); diff --git a/drivers/net/ethernet/qlogic/qede/qede_dcbnl.c b/drivers/net/ethernet/qlogic/qede/qede_dcbnl.c index 03e8c0212433..a9e7379313db 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_dcbnl.c +++ b/drivers/net/ethernet/qlogic/qede/qede_dcbnl.c @@ -281,6 +281,11 @@ static int qede_dcbnl_ieee_setapp(struct net_device *netdev, struct dcb_app *app) { struct qede_dev *edev = netdev_priv(netdev); + int err; + + err = dcb_ieee_setapp(netdev, app); + if (err) + return err; return edev->ops->dcb->ieee_setapp(edev->cdev, app); } -- cgit From 49632b5822ea2af0e9531f8d20dcd5fb786093a9 Mon Sep 17 00:00:00 2001 From: "sudarsana.kalluru@cavium.com" Date: Thu, 20 Apr 2017 22:31:20 -0700 Subject: qed: Add support for static dcbx. The patch adds driver support for static/local dcbx mode. In this mode adapter brings up the dcbx link with locally configured parameters instead of performing the dcbx negotiation with the peer. The feature is useful when peer device/switch doesn't support dcbx. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dcbx.c | 24 +++++++++++++++++++----- include/linux/qed/qed_if.h | 1 + 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c index 40a4975443e4..233ca9727388 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c @@ -680,8 +680,14 @@ qed_dcbx_get_operational_params(struct qed_hwfn *p_hwfn, DCBX_CONFIG_VERSION_CEE); p_operational->cee = val; - DP_VERBOSE(p_hwfn, QED_MSG_DCB, "Version support: ieee %d, cee %d\n", - p_operational->ieee, p_operational->cee); + val = !!(QED_MFW_GET_FIELD(flags, DCBX_CONFIG_VERSION) == + DCBX_CONFIG_VERSION_STATIC); + p_operational->local = val; + + DP_VERBOSE(p_hwfn, QED_MSG_DCB, + "Version support: ieee %d, cee %d, static %d\n", + p_operational->ieee, p_operational->cee, + p_operational->local); qed_dcbx_get_common_params(p_hwfn, &p_feat->app, p_feat->app.app_pri_tbl, &p_feat->ets, @@ -1235,6 +1241,8 @@ int qed_dcbx_get_config_params(struct qed_hwfn *p_hwfn, p_hwfn->p_dcbx_info->set.ver_num |= DCBX_CONFIG_VERSION_CEE; if (dcbx_info->operational.ieee) p_hwfn->p_dcbx_info->set.ver_num |= DCBX_CONFIG_VERSION_IEEE; + if (dcbx_info->operational.local) + p_hwfn->p_dcbx_info->set.ver_num |= DCBX_CONFIG_VERSION_STATIC; p_hwfn->p_dcbx_info->set.enabled = dcbx_info->operational.enabled; memcpy(&p_hwfn->p_dcbx_info->set.config.params, @@ -1784,8 +1792,9 @@ static u8 qed_dcbnl_setdcbx(struct qed_dev *cdev, u8 mode) DP_VERBOSE(hwfn, QED_MSG_DCB, "new mode = %x\n", mode); - if (!(mode & DCB_CAP_DCBX_VER_IEEE) && !(mode & DCB_CAP_DCBX_VER_CEE)) { - DP_INFO(hwfn, "Allowed mode is cee, ieee or both\n"); + if (!(mode & DCB_CAP_DCBX_VER_IEEE) && + !(mode & DCB_CAP_DCBX_VER_CEE) && !(mode & DCB_CAP_DCBX_STATIC)) { + DP_INFO(hwfn, "Allowed modes are cee, ieee or static\n"); return 1; } @@ -1805,6 +1814,11 @@ static u8 qed_dcbnl_setdcbx(struct qed_dev *cdev, u8 mode) dcbx_set.enabled = true; } + if (mode & DCB_CAP_DCBX_STATIC) { + dcbx_set.ver_num |= DCBX_CONFIG_VERSION_STATIC; + dcbx_set.enabled = true; + } + ptt = qed_ptt_acquire(hwfn); if (!ptt) return 1; @@ -1813,7 +1827,7 @@ static u8 qed_dcbnl_setdcbx(struct qed_dev *cdev, u8 mode) qed_ptt_release(hwfn, ptt); - return 0; + return rc; } static u8 qed_dcbnl_getfeatcfg(struct qed_dev *cdev, int featid, u8 *flags) diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index d44933a058ee..9f966be89510 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -144,6 +144,7 @@ struct qed_dcbx_operational_params { bool enabled; bool ieee; bool cee; + bool local; u32 err; }; -- cgit From 531b374834c891ae2abf800693074df35a7d1a36 Mon Sep 17 00:00:00 2001 From: Gerard Garcia Date: Fri, 21 Apr 2017 10:10:44 +0100 Subject: VSOCK: Add vsockmon tap functions Add tap functions that can be used by the vsock transports to deliver packets to vsockmon virtual network devices. Signed-off-by: Gerard Garcia Signed-off-by: Stefan Hajnoczi Reviewed-by: Jorgen Hansen Signed-off-by: David S. Miller --- MAINTAINERS | 1 + include/net/af_vsock.h | 13 +++++ include/uapi/linux/if_arp.h | 1 + net/vmw_vsock/Makefile | 2 +- net/vmw_vsock/af_vsock_tap.c | 114 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 net/vmw_vsock/af_vsock_tap.c diff --git a/MAINTAINERS b/MAINTAINERS index b283d5ef7b68..fdab4f9e8ac9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13317,6 +13317,7 @@ L: netdev@vger.kernel.org S: Maintained F: include/linux/virtio_vsock.h F: include/uapi/linux/virtio_vsock.h +F: net/vmw_vsock/af_vsock_tap.c F: net/vmw_vsock/virtio_transport_common.c F: net/vmw_vsock/virtio_transport.c F: drivers/vhost/vsock.c diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h index f32ed9ac181a..f9fb566e75cf 100644 --- a/include/net/af_vsock.h +++ b/include/net/af_vsock.h @@ -188,4 +188,17 @@ struct sock *vsock_find_connected_socket(struct sockaddr_vm *src, void vsock_remove_sock(struct vsock_sock *vsk); void vsock_for_each_connected_socket(void (*fn)(struct sock *sk)); +/**** TAP ****/ + +struct vsock_tap { + struct net_device *dev; + struct module *module; + struct list_head list; +}; + +int vsock_init_tap(void); +int vsock_add_tap(struct vsock_tap *vt); +int vsock_remove_tap(struct vsock_tap *vt); +void vsock_deliver_tap(struct sk_buff *build_skb(void *opaque), void *opaque); + #endif /* __AF_VSOCK_H__ */ diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h index 4d024d75d64b..cf73510b9238 100644 --- a/include/uapi/linux/if_arp.h +++ b/include/uapi/linux/if_arp.h @@ -95,6 +95,7 @@ #define ARPHRD_IP6GRE 823 /* GRE over IPv6 */ #define ARPHRD_NETLINK 824 /* Netlink header */ #define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */ +#define ARPHRD_VSOCKMON 826 /* Vsock monitor header */ #define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */ #define ARPHRD_NONE 0xFFFE /* zero header length */ diff --git a/net/vmw_vsock/Makefile b/net/vmw_vsock/Makefile index bc27c70e0e59..09fc2eb29dc8 100644 --- a/net/vmw_vsock/Makefile +++ b/net/vmw_vsock/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_VMWARE_VMCI_VSOCKETS) += vmw_vsock_vmci_transport.o obj-$(CONFIG_VIRTIO_VSOCKETS) += vmw_vsock_virtio_transport.o obj-$(CONFIG_VIRTIO_VSOCKETS_COMMON) += vmw_vsock_virtio_transport_common.o -vsock-y += af_vsock.o vsock_addr.o +vsock-y += af_vsock.o af_vsock_tap.o vsock_addr.o vmw_vsock_vmci_transport-y += vmci_transport.o vmci_transport_notify.o \ vmci_transport_notify_qstate.o diff --git a/net/vmw_vsock/af_vsock_tap.c b/net/vmw_vsock/af_vsock_tap.c new file mode 100644 index 000000000000..98f09b539366 --- /dev/null +++ b/net/vmw_vsock/af_vsock_tap.c @@ -0,0 +1,114 @@ +/* + * Tap functions for AF_VSOCK sockets. + * + * Code based on net/netlink/af_netlink.c tap functions. + * + * 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 + +static DEFINE_SPINLOCK(vsock_tap_lock); +static struct list_head vsock_tap_all __read_mostly = + LIST_HEAD_INIT(vsock_tap_all); + +int vsock_add_tap(struct vsock_tap *vt) +{ + if (unlikely(vt->dev->type != ARPHRD_VSOCKMON)) + return -EINVAL; + + __module_get(vt->module); + + spin_lock(&vsock_tap_lock); + list_add_rcu(&vt->list, &vsock_tap_all); + spin_unlock(&vsock_tap_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(vsock_add_tap); + +int vsock_remove_tap(struct vsock_tap *vt) +{ + struct vsock_tap *tmp; + bool found = false; + + spin_lock(&vsock_tap_lock); + + list_for_each_entry(tmp, &vsock_tap_all, list) { + if (vt == tmp) { + list_del_rcu(&vt->list); + found = true; + goto out; + } + } + + pr_warn("vsock_remove_tap: %p not found\n", vt); +out: + spin_unlock(&vsock_tap_lock); + + synchronize_net(); + + if (found) + module_put(vt->module); + + return found ? 0 : -ENODEV; +} +EXPORT_SYMBOL_GPL(vsock_remove_tap); + +static int __vsock_deliver_tap_skb(struct sk_buff *skb, + struct net_device *dev) +{ + int ret = 0; + struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC); + + if (nskb) { + dev_hold(dev); + + nskb->dev = dev; + ret = dev_queue_xmit(nskb); + if (unlikely(ret > 0)) + ret = net_xmit_errno(ret); + + dev_put(dev); + } + + return ret; +} + +static void __vsock_deliver_tap(struct sk_buff *skb) +{ + int ret; + struct vsock_tap *tmp; + + list_for_each_entry_rcu(tmp, &vsock_tap_all, list) { + ret = __vsock_deliver_tap_skb(skb, tmp->dev); + if (unlikely(ret)) + break; + } +} + +void vsock_deliver_tap(struct sk_buff *build_skb(void *opaque), void *opaque) +{ + struct sk_buff *skb; + + rcu_read_lock(); + + if (likely(list_empty(&vsock_tap_all))) + goto out; + + skb = build_skb(opaque); + if (skb) { + __vsock_deliver_tap(skb); + consume_skb(skb); + } + +out: + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(vsock_deliver_tap); -- cgit From 0b2e66448ba20eb30ea62345d6beb9ee2a1ce06b Mon Sep 17 00:00:00 2001 From: Gerard Garcia Date: Fri, 21 Apr 2017 10:10:45 +0100 Subject: VSOCK: Add vsockmon device Add vsockmon virtual network device that receives packets from the vsock transports and exposes them to user space. Based on the nlmon device. Signed-off-by: Gerard Garcia Signed-off-by: Stefan Hajnoczi Signed-off-by: David S. Miller --- MAINTAINERS | 2 + drivers/net/Kconfig | 8 ++ drivers/net/Makefile | 1 + drivers/net/vsockmon.c | 170 ++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/Kbuild | 1 + include/uapi/linux/vsockmon.h | 60 +++++++++++++++ 6 files changed, 242 insertions(+) create mode 100644 drivers/net/vsockmon.c create mode 100644 include/uapi/linux/vsockmon.h diff --git a/MAINTAINERS b/MAINTAINERS index fdab4f9e8ac9..28ea78b12d0c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13317,9 +13317,11 @@ L: netdev@vger.kernel.org S: Maintained F: include/linux/virtio_vsock.h F: include/uapi/linux/virtio_vsock.h +F: include/uapi/linux/vsockmon.h F: net/vmw_vsock/af_vsock_tap.c F: net/vmw_vsock/virtio_transport_common.c F: net/vmw_vsock/virtio_transport.c +F: drivers/net/vsockmon.c F: drivers/vhost/vsock.c F: drivers/vhost/vsock.h diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 100fbdc9b95c..83a1616903f8 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -355,6 +355,14 @@ config NET_VRF This option enables the support for mapping interfaces into VRF's. The support enables VRF devices. +config VSOCKMON + tristate "Virtual vsock monitoring device" + depends on VHOST_VSOCK + ---help--- + This option enables a monitoring net device for vsock sockets. It is + mostly intended for developers or support to debug vsock issues. If + unsure, say N. + endif # NET_CORE config SUNGEM_PHY diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 57fc47ad5ab3..b2f6556d8848 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_GENEVE) += geneve.o obj-$(CONFIG_GTP) += gtp.o obj-$(CONFIG_NLMON) += nlmon.o obj-$(CONFIG_NET_VRF) += vrf.o +obj-$(CONFIG_VSOCKMON) += vsockmon.o # # Networking Drivers diff --git a/drivers/net/vsockmon.c b/drivers/net/vsockmon.c new file mode 100644 index 000000000000..7f0136f2dd9d --- /dev/null +++ b/drivers/net/vsockmon.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Virtio transport max packet size plus header */ +#define DEFAULT_MTU (VIRTIO_VSOCK_MAX_PKT_BUF_SIZE + \ + sizeof(struct af_vsockmon_hdr)) + +struct pcpu_lstats { + u64 rx_packets; + u64 rx_bytes; + struct u64_stats_sync syncp; +}; + +static int vsockmon_dev_init(struct net_device *dev) +{ + dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats); + if (!dev->lstats) + return -ENOMEM; + return 0; +} + +static void vsockmon_dev_uninit(struct net_device *dev) +{ + free_percpu(dev->lstats); +} + +struct vsockmon { + struct vsock_tap vt; +}; + +static int vsockmon_open(struct net_device *dev) +{ + struct vsockmon *vsockmon = netdev_priv(dev); + + vsockmon->vt.dev = dev; + vsockmon->vt.module = THIS_MODULE; + return vsock_add_tap(&vsockmon->vt); +} + +static int vsockmon_close(struct net_device *dev) +{ + struct vsockmon *vsockmon = netdev_priv(dev); + + return vsock_remove_tap(&vsockmon->vt); +} + +static netdev_tx_t vsockmon_xmit(struct sk_buff *skb, struct net_device *dev) +{ + int len = skb->len; + struct pcpu_lstats *stats = this_cpu_ptr(dev->lstats); + + u64_stats_update_begin(&stats->syncp); + stats->rx_bytes += len; + stats->rx_packets++; + u64_stats_update_end(&stats->syncp); + + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +static void +vsockmon_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) +{ + int i; + u64 bytes = 0, packets = 0; + + for_each_possible_cpu(i) { + const struct pcpu_lstats *vstats; + u64 tbytes, tpackets; + unsigned int start; + + vstats = per_cpu_ptr(dev->lstats, i); + + do { + start = u64_stats_fetch_begin_irq(&vstats->syncp); + tbytes = vstats->rx_bytes; + tpackets = vstats->rx_packets; + } while (u64_stats_fetch_retry_irq(&vstats->syncp, start)); + + packets += tpackets; + bytes += tbytes; + } + + stats->rx_packets = packets; + stats->tx_packets = 0; + + stats->rx_bytes = bytes; + stats->tx_bytes = 0; +} + +static int vsockmon_is_valid_mtu(int new_mtu) +{ + return new_mtu >= (int)sizeof(struct af_vsockmon_hdr); +} + +static int vsockmon_change_mtu(struct net_device *dev, int new_mtu) +{ + if (!vsockmon_is_valid_mtu(new_mtu)) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + +static const struct net_device_ops vsockmon_ops = { + .ndo_init = vsockmon_dev_init, + .ndo_uninit = vsockmon_dev_uninit, + .ndo_open = vsockmon_open, + .ndo_stop = vsockmon_close, + .ndo_start_xmit = vsockmon_xmit, + .ndo_get_stats64 = vsockmon_get_stats64, + .ndo_change_mtu = vsockmon_change_mtu, +}; + +static u32 always_on(struct net_device *dev) +{ + return 1; +} + +static const struct ethtool_ops vsockmon_ethtool_ops = { + .get_link = always_on, +}; + +static void vsockmon_setup(struct net_device *dev) +{ + dev->type = ARPHRD_VSOCKMON; + dev->priv_flags |= IFF_NO_QUEUE; + + dev->netdev_ops = &vsockmon_ops; + dev->ethtool_ops = &vsockmon_ethtool_ops; + dev->destructor = free_netdev; + + dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | + NETIF_F_HIGHDMA | NETIF_F_LLTX; + + dev->flags = IFF_NOARP; + + dev->mtu = DEFAULT_MTU; +} + +static struct rtnl_link_ops vsockmon_link_ops __read_mostly = { + .kind = "vsockmon", + .priv_size = sizeof(struct vsockmon), + .setup = vsockmon_setup, +}; + +static __init int vsockmon_register(void) +{ + return rtnl_link_register(&vsockmon_link_ops); +} + +static __exit void vsockmon_unregister(void) +{ + rtnl_link_unregister(&vsockmon_link_ops); +} + +module_init(vsockmon_register); +module_exit(vsockmon_unregister); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Gerard Garcia "); +MODULE_DESCRIPTION("Vsock monitoring device. Based on nlmon device."); +MODULE_ALIAS_RTNL_LINK("vsockmon"); diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index f8d9fed17ba9..6b0e2758585f 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -477,6 +477,7 @@ header-y += virtio_types.h header-y += virtio_vsock.h header-y += virtio_crypto.h header-y += vm_sockets.h +header-y += vsockmon.h header-y += vt.h header-y += vtpm_proxy.h header-y += wait.h diff --git a/include/uapi/linux/vsockmon.h b/include/uapi/linux/vsockmon.h new file mode 100644 index 000000000000..a08b522ef597 --- /dev/null +++ b/include/uapi/linux/vsockmon.h @@ -0,0 +1,60 @@ +#ifndef _UAPI_VSOCKMON_H +#define _UAPI_VSOCKMON_H + +#include + +/* + * vsockmon is the AF_VSOCK packet capture device. Packets captured have the + * following layout: + * + * +-----------------------------------+ + * | vsockmon header | + * | (struct af_vsockmon_hdr) | + * +-----------------------------------+ + * | transport header | + * | (af_vsockmon_hdr->len bytes long) | + * +-----------------------------------+ + * | payload | + * | (until end of packet) | + * +-----------------------------------+ + * + * The vsockmon header is a transport-independent description of the packet. + * It duplicates some of the information from the transport header so that + * no transport-specific knowledge is necessary to process packets. + * + * The transport header is useful for low-level transport-specific packet + * analysis. Transport type is given in af_vsockmon_hdr->transport and + * transport header length is given in af_vsockmon_hdr->len. + * + * If af_vsockmon_hdr->op is AF_VSOCK_OP_PAYLOAD then the payload follows the + * transport header. Other ops do not have a payload. + */ + +struct af_vsockmon_hdr { + __le64 src_cid; + __le64 dst_cid; + __le32 src_port; + __le32 dst_port; + __le16 op; /* enum af_vsockmon_op */ + __le16 transport; /* enum af_vsockmon_transport */ + __le16 len; /* Transport header length */ + __u8 reserved[2]; +}; + +enum af_vsockmon_op { + AF_VSOCK_OP_UNKNOWN = 0, + AF_VSOCK_OP_CONNECT = 1, + AF_VSOCK_OP_DISCONNECT = 2, + AF_VSOCK_OP_CONTROL = 3, + AF_VSOCK_OP_PAYLOAD = 4, +}; + +enum af_vsockmon_transport { + AF_VSOCK_TRANSPORT_UNKNOWN = 0, + AF_VSOCK_TRANSPORT_NO_INFO = 1, /* No transport information */ + + /* Transport header type: struct virtio_vsock_hdr */ + AF_VSOCK_TRANSPORT_VIRTIO = 2, +}; + +#endif -- cgit From 82dfb540aeb277d945bf646ff780493b8a520d8a Mon Sep 17 00:00:00 2001 From: Gerard Garcia Date: Fri, 21 Apr 2017 10:10:46 +0100 Subject: VSOCK: Add virtio vsock vsockmon hooks The virtio drivers deal with struct virtio_vsock_pkt. Add virtio_transport_deliver_tap_pkt(pkt) for handing packets to the vsockmon device. We call virtio_transport_deliver_tap_pkt(pkt) from net/vmw_vsock/virtio_transport.c and drivers/vhost/vsock.c instead of common code. This is because the drivers may drop packets before handing them to common code - we still want to capture them. Signed-off-by: Gerard Garcia Signed-off-by: Stefan Hajnoczi Reviewed-by: Jorgen Hansen Signed-off-by: David S. Miller --- drivers/vhost/vsock.c | 8 +++++ include/linux/virtio_vsock.h | 1 + net/vmw_vsock/virtio_transport.c | 3 ++ net/vmw_vsock/virtio_transport_common.c | 64 +++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+) diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c index 44eed8eb0725..d939ac1a4997 100644 --- a/drivers/vhost/vsock.c +++ b/drivers/vhost/vsock.c @@ -176,6 +176,11 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock, restart_tx = true; } + /* Deliver to monitoring devices all correctly transmitted + * packets. + */ + virtio_transport_deliver_tap_pkt(pkt); + virtio_transport_free_pkt(pkt); } if (added) @@ -383,6 +388,9 @@ static void vhost_vsock_handle_tx_kick(struct vhost_work *work) len = pkt->len; + /* Deliver to monitoring devices all received packets */ + virtio_transport_deliver_tap_pkt(pkt); + /* Only accept correctly addressed packets */ if (le64_to_cpu(pkt->hdr.src_cid) == vsock->guest_cid) virtio_transport_recv_pkt(pkt); diff --git a/include/linux/virtio_vsock.h b/include/linux/virtio_vsock.h index 584f9a647ad4..ab13f0743da8 100644 --- a/include/linux/virtio_vsock.h +++ b/include/linux/virtio_vsock.h @@ -153,5 +153,6 @@ void virtio_transport_free_pkt(struct virtio_vsock_pkt *pkt); void virtio_transport_inc_tx_pkt(struct virtio_vsock_sock *vvs, struct virtio_vsock_pkt *pkt); u32 virtio_transport_get_credit(struct virtio_vsock_sock *vvs, u32 wanted); void virtio_transport_put_credit(struct virtio_vsock_sock *vvs, u32 credit); +void virtio_transport_deliver_tap_pkt(struct virtio_vsock_pkt *pkt); #endif /* _LINUX_VIRTIO_VSOCK_H */ diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c index 68675a151f22..9dffe0282ad4 100644 --- a/net/vmw_vsock/virtio_transport.c +++ b/net/vmw_vsock/virtio_transport.c @@ -144,6 +144,8 @@ virtio_transport_send_pkt_work(struct work_struct *work) list_del_init(&pkt->list); spin_unlock_bh(&vsock->send_pkt_list_lock); + virtio_transport_deliver_tap_pkt(pkt); + reply = pkt->reply; sg_init_one(&hdr, &pkt->hdr, sizeof(pkt->hdr)); @@ -370,6 +372,7 @@ static void virtio_transport_rx_work(struct work_struct *work) } pkt->len = len - sizeof(pkt->hdr); + virtio_transport_deliver_tap_pkt(pkt); virtio_transport_recv_pkt(pkt); } } while (!virtqueue_enable_cb(vq)); diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index af087b44ceea..18e24793659f 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -85,6 +86,69 @@ out_pkt: return NULL; } +/* Packet capture */ +static struct sk_buff *virtio_transport_build_skb(void *opaque) +{ + struct virtio_vsock_pkt *pkt = opaque; + unsigned char *t_hdr, *payload; + struct af_vsockmon_hdr *hdr; + struct sk_buff *skb; + + skb = alloc_skb(sizeof(*hdr) + sizeof(pkt->hdr) + pkt->len, + GFP_ATOMIC); + if (!skb) + return NULL; + + hdr = (struct af_vsockmon_hdr *)skb_put(skb, sizeof(*hdr)); + + /* pkt->hdr is little-endian so no need to byteswap here */ + hdr->src_cid = pkt->hdr.src_cid; + hdr->src_port = pkt->hdr.src_port; + hdr->dst_cid = pkt->hdr.dst_cid; + hdr->dst_port = pkt->hdr.dst_port; + + hdr->transport = cpu_to_le16(AF_VSOCK_TRANSPORT_VIRTIO); + hdr->len = cpu_to_le16(sizeof(pkt->hdr)); + memset(hdr->reserved, 0, sizeof(hdr->reserved)); + + switch (le16_to_cpu(pkt->hdr.op)) { + case VIRTIO_VSOCK_OP_REQUEST: + case VIRTIO_VSOCK_OP_RESPONSE: + hdr->op = cpu_to_le16(AF_VSOCK_OP_CONNECT); + break; + case VIRTIO_VSOCK_OP_RST: + case VIRTIO_VSOCK_OP_SHUTDOWN: + hdr->op = cpu_to_le16(AF_VSOCK_OP_DISCONNECT); + break; + case VIRTIO_VSOCK_OP_RW: + hdr->op = cpu_to_le16(AF_VSOCK_OP_PAYLOAD); + break; + case VIRTIO_VSOCK_OP_CREDIT_UPDATE: + case VIRTIO_VSOCK_OP_CREDIT_REQUEST: + hdr->op = cpu_to_le16(AF_VSOCK_OP_CONTROL); + break; + default: + hdr->op = cpu_to_le16(AF_VSOCK_OP_UNKNOWN); + break; + } + + t_hdr = skb_put(skb, sizeof(pkt->hdr)); + memcpy(t_hdr, &pkt->hdr, sizeof(pkt->hdr)); + + if (pkt->len) { + payload = skb_put(skb, pkt->len); + memcpy(payload, pkt->buf, pkt->len); + } + + return skb; +} + +void virtio_transport_deliver_tap_pkt(struct virtio_vsock_pkt *pkt) +{ + vsock_deliver_tap(virtio_transport_build_skb, pkt); +} +EXPORT_SYMBOL_GPL(virtio_transport_deliver_tap_pkt); + static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, struct virtio_vsock_pkt_info *info) { -- cgit From 69226896ad636b94f6d2e55d75ff21a29c4de83b Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 21 Apr 2017 16:15:38 +0300 Subject: mdio_bus: Issue GPIO RESET to PHYs. Some boards [1] leave the PHYs at an invalid state during system power-up or reset thus causing unreliability issues with the PHY which manifests as PHY not being detected or link not functional. To fix this, these PHYs need to be RESET via a GPIO connected to the PHY's RESET pin. Some boards have a single GPIO controlling the PHY RESET pin of all PHYs on the bus whereas some others have separate GPIOs controlling individual PHY RESETs. In both cases, the RESET de-assertion cannot be done in the PHY driver as the PHY will not probe till its reset is de-asserted. So do the RESET de-assertion in the MDIO bus driver. [1] - am572x-idk, am571x-idk, a437x-idk Signed-off-by: Roger Quadros Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/mdio.txt | 33 ++++++++++++++++++ drivers/net/phy/mdio_bus.c | 47 ++++++++++++++++++++++++++ drivers/of/of_mdio.c | 7 ++++ include/linux/phy.h | 7 ++++ 4 files changed, 94 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/mdio.txt diff --git a/Documentation/devicetree/bindings/net/mdio.txt b/Documentation/devicetree/bindings/net/mdio.txt new file mode 100644 index 000000000000..4ffbbacebda1 --- /dev/null +++ b/Documentation/devicetree/bindings/net/mdio.txt @@ -0,0 +1,33 @@ +Common MDIO bus properties. + +These are generic properties that can apply to any MDIO bus. + +Optional properties: +- reset-gpios: List of one or more GPIOs that control the RESET lines + of the PHYs on that MDIO bus. +- reset-delay-us: RESET pulse width in microseconds as per PHY datasheet. + +A list of child nodes, one per device on the bus is expected. These +should follow the generic phy.txt, or a device specific binding document. + +Example : +This example shows these optional properties, plus other properties +required for the TI Davinci MDIO driver. + + davinci_mdio: ethernet@0x5c030000 { + compatible = "ti,davinci_mdio"; + reg = <0x5c030000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + + reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; + reset-delay-us = <2>; /* PHY datasheet states 1us min */ + + ethphy0: ethernet-phy@1 { + reg = <1>; + }; + + ethphy1: ethernet-phy@3 { + reg = <3>; + }; + }; diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 5a214f3b8671..a898e5c4ef1b 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -22,8 +22,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -337,6 +340,7 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner) { struct mdio_device *mdiodev; int i, err; + struct gpio_desc *gpiod; if (NULL == bus || NULL == bus->name || NULL == bus->read || NULL == bus->write) @@ -363,6 +367,35 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner) if (bus->reset) bus->reset(bus); + /* de-assert bus level PHY GPIO resets */ + if (bus->num_reset_gpios > 0) { + bus->reset_gpiod = devm_kcalloc(&bus->dev, + bus->num_reset_gpios, + sizeof(struct gpio_desc *), + GFP_KERNEL); + if (!bus->reset_gpiod) + return -ENOMEM; + } + + for (i = 0; i < bus->num_reset_gpios; i++) { + gpiod = devm_gpiod_get_index(&bus->dev, "reset", i, + GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) { + err = PTR_ERR(gpiod); + if (err != -ENOENT) { + dev_err(&bus->dev, + "mii_bus %s couldn't get reset GPIO\n", + bus->id); + return err; + } + } else { + bus->reset_gpiod[i] = gpiod; + gpiod_set_value_cansleep(gpiod, 1); + udelay(bus->reset_delay_us); + gpiod_set_value_cansleep(gpiod, 0); + } + } + for (i = 0; i < PHY_MAX_ADDR; i++) { if ((bus->phy_mask & (1 << i)) == 0) { struct phy_device *phydev; @@ -390,6 +423,13 @@ error: mdiodev->device_remove(mdiodev); mdiodev->device_free(mdiodev); } + + /* Put PHYs in RESET to save power */ + for (i = 0; i < bus->num_reset_gpios; i++) { + if (bus->reset_gpiod[i]) + gpiod_set_value_cansleep(bus->reset_gpiod[i], 1); + } + device_del(&bus->dev); return err; } @@ -411,6 +451,13 @@ void mdiobus_unregister(struct mii_bus *bus) mdiodev->device_remove(mdiodev); mdiodev->device_free(mdiodev); } + + /* Put PHYs in RESET to save power */ + for (i = 0; i < bus->num_reset_gpios; i++) { + if (bus->reset_gpiod[i]) + gpiod_set_value_cansleep(bus->reset_gpiod[i], 1); + } + device_del(&bus->dev); } EXPORT_SYMBOL(mdiobus_unregister); diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 0b2979816dbf..7e4c80f9b6cd 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -22,6 +22,8 @@ #include #include +#define DEFAULT_GPIO_RESET_DELAY 10 /* in microseconds */ + MODULE_AUTHOR("Grant Likely "); MODULE_LICENSE("GPL"); @@ -221,6 +223,11 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) mdio->dev.of_node = np; + /* Get bus level PHY reset GPIO details */ + mdio->reset_delay_us = DEFAULT_GPIO_RESET_DELAY; + of_property_read_u32(np, "reset-delay-us", &mdio->reset_delay_us); + mdio->num_reset_gpios = of_gpio_named_count(np, "reset-gpios"); + /* Register the MDIO bus */ rc = mdiobus_register(mdio); if (rc) diff --git a/include/linux/phy.h b/include/linux/phy.h index 624cecf69c28..37ca77d86983 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -217,6 +217,13 @@ struct mii_bus { * matching its address */ int irq[PHY_MAX_ADDR]; + + /* GPIO reset pulse width in microseconds */ + int reset_delay_us; + /* Number of reset GPIOs */ + int num_reset_gpios; + /* Array of RESET GPIO descriptors */ + struct gpio_desc **reset_gpiod; }; #define to_mii_bus(d) container_of(d, struct mii_bus, dev) -- cgit From 2e7a721714b9cdca539da78a0eb1f59dbe4020ac Mon Sep 17 00:00:00 2001 From: Mike Maloney Date: Fri, 21 Apr 2017 10:56:10 -0400 Subject: selftests/net: cleanup unused parameter in psock_fanout sock_fanout_open no longer sets the size of packet_socket ring, so stop passing the parameter. Signed-off-by: Mike Maloney Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- tools/testing/selftests/net/psock_fanout.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c index e62bb354820c..27c4027fab03 100644 --- a/tools/testing/selftests/net/psock_fanout.c +++ b/tools/testing/selftests/net/psock_fanout.c @@ -71,7 +71,7 @@ /* Open a socket in a given fanout mode. * @return -1 if mode is bad, a valid socket otherwise */ -static int sock_fanout_open(uint16_t typeflags, int num_packets) +static int sock_fanout_open(uint16_t typeflags) { int fd, val; @@ -228,7 +228,7 @@ static void test_control_single(void) fprintf(stderr, "test: control single socket\n"); if (sock_fanout_open(PACKET_FANOUT_ROLLOVER | - PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) { + PACKET_FANOUT_FLAG_ROLLOVER) != -1) { fprintf(stderr, "ERROR: opened socket with dual rollover\n"); exit(1); } @@ -241,26 +241,26 @@ static void test_control_group(void) fprintf(stderr, "test: control multiple sockets\n"); - fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + fds[0] = sock_fanout_open(PACKET_FANOUT_HASH); if (fds[0] == -1) { fprintf(stderr, "ERROR: failed to open HASH socket\n"); exit(1); } if (sock_fanout_open(PACKET_FANOUT_HASH | - PACKET_FANOUT_FLAG_DEFRAG, 10) != -1) { + PACKET_FANOUT_FLAG_DEFRAG) != -1) { fprintf(stderr, "ERROR: joined group with wrong flag defrag\n"); exit(1); } if (sock_fanout_open(PACKET_FANOUT_HASH | - PACKET_FANOUT_FLAG_ROLLOVER, 10) != -1) { + PACKET_FANOUT_FLAG_ROLLOVER) != -1) { fprintf(stderr, "ERROR: joined group with wrong flag ro\n"); exit(1); } - if (sock_fanout_open(PACKET_FANOUT_CPU, 10) != -1) { + if (sock_fanout_open(PACKET_FANOUT_CPU) != -1) { fprintf(stderr, "ERROR: joined group with wrong mode\n"); exit(1); } - fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 20); + fds[1] = sock_fanout_open(PACKET_FANOUT_HASH); if (fds[1] == -1) { fprintf(stderr, "ERROR: failed to join group\n"); exit(1); @@ -281,8 +281,8 @@ static int test_datapath(uint16_t typeflags, int port_off, fprintf(stderr, "test: datapath 0x%hx\n", typeflags); - fds[0] = sock_fanout_open(typeflags, 20); - fds[1] = sock_fanout_open(typeflags, 20); + fds[0] = sock_fanout_open(typeflags); + fds[1] = sock_fanout_open(typeflags); if (fds[0] == -1 || fds[1] == -1) { fprintf(stderr, "ERROR: failed open\n"); exit(1); -- cgit From 4a69a864209e9ab436d4a58e8028ac96cc873d15 Mon Sep 17 00:00:00 2001 From: Mike Maloney Date: Fri, 21 Apr 2017 10:56:11 -0400 Subject: packet: add PACKET_FANOUT_FLAG_UNIQUEID to assign new fanout group id. Fanout uses a per net global namespace. A process that intends to create a new fanout group can accidentally join an existing group. It is not possible to detect this. Add socket option PACKET_FANOUT_FLAG_UNIQUEID. When specified the supplied fanout group id must be set to 0, and the kernel chooses an id that is not already in use. This is an ephemeral flag so that other sockets can be added to this group using setsockopt, but NOT specifying this flag. The current getsockopt(..., PACKET_FANOUT, ...) can be used to retrieve the new group id. We assume that there are not a lot of fanout groups and that this is not a high frequency call. The method assigns ids starting at zero and increases until it finds an unused id. It keeps track of the last assigned id, and uses it as a starting point to find new ids. Signed-off-by: Mike Maloney Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/uapi/linux/if_packet.h | 1 + net/packet/af_packet.c | 44 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/include/uapi/linux/if_packet.h b/include/uapi/linux/if_packet.h index 9e7edfd8141e..4df96a7dd4fa 100644 --- a/include/uapi/linux/if_packet.h +++ b/include/uapi/linux/if_packet.h @@ -66,6 +66,7 @@ struct sockaddr_ll { #define PACKET_FANOUT_CBPF 6 #define PACKET_FANOUT_EBPF 7 #define PACKET_FANOUT_FLAG_ROLLOVER 0x1000 +#define PACKET_FANOUT_FLAG_UNIQUEID 0x2000 #define PACKET_FANOUT_FLAG_DEFRAG 0x8000 struct tpacket_stats { diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8489beff5c25..94052f42058b 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1496,6 +1496,7 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev, DEFINE_MUTEX(fanout_mutex); EXPORT_SYMBOL_GPL(fanout_mutex); static LIST_HEAD(fanout_list); +static u16 fanout_next_id; static void __fanout_link(struct sock *sk, struct packet_sock *po) { @@ -1629,6 +1630,36 @@ static void fanout_release_data(struct packet_fanout *f) }; } +static bool __fanout_id_is_free(struct sock *sk, u16 candidate_id) +{ + struct packet_fanout *f; + + list_for_each_entry(f, &fanout_list, list) { + if (f->id == candidate_id && + read_pnet(&f->net) == sock_net(sk)) { + return false; + } + } + return true; +} + +static bool fanout_find_new_id(struct sock *sk, u16 *new_id) +{ + u16 id = fanout_next_id; + + do { + if (__fanout_id_is_free(sk, id)) { + *new_id = id; + fanout_next_id = id + 1; + return true; + } + + id++; + } while (id != fanout_next_id); + + return false; +} + static int fanout_add(struct sock *sk, u16 id, u16 type_flags) { struct packet_rollover *rollover = NULL; @@ -1676,6 +1707,19 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) po->rollover = rollover; } + if (type_flags & PACKET_FANOUT_FLAG_UNIQUEID) { + if (id != 0) { + err = -EINVAL; + goto out; + } + if (!fanout_find_new_id(sk, &id)) { + err = -ENOMEM; + goto out; + } + /* ephemeral flag for the first socket in the group: drop it */ + flags &= ~(PACKET_FANOUT_FLAG_UNIQUEID >> 8); + } + match = NULL; list_for_each_entry(f, &fanout_list, list) { if (f->id == id && -- cgit From 28be04f5c1c95bdf5614af19f7faf7d4fb781fa6 Mon Sep 17 00:00:00 2001 From: Mike Maloney Date: Fri, 21 Apr 2017 10:56:12 -0400 Subject: selftests/net: add tests for PACKET_FANOUT_FLAG_UNIQUEID Create two groups with PACKET_FANOUT_FLAG_UNIQUEID, add a socket to one. Ensure that the groups can only be joined if all options are consistent with the original except for this flag. Signed-off-by: Mike Maloney Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- tools/testing/selftests/net/psock_fanout.c | 95 ++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 11 deletions(-) diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c index 27c4027fab03..b4b1d91fcea5 100644 --- a/tools/testing/selftests/net/psock_fanout.c +++ b/tools/testing/selftests/net/psock_fanout.c @@ -71,7 +71,7 @@ /* Open a socket in a given fanout mode. * @return -1 if mode is bad, a valid socket otherwise */ -static int sock_fanout_open(uint16_t typeflags) +static int sock_fanout_open(uint16_t typeflags, uint16_t group_id) { int fd, val; @@ -81,8 +81,7 @@ static int sock_fanout_open(uint16_t typeflags) exit(1); } - /* fanout group ID is always 0: tests whether old groups are deleted */ - val = ((int) typeflags) << 16; + val = (((int) typeflags) << 16) | group_id; if (setsockopt(fd, SOL_PACKET, PACKET_FANOUT, &val, sizeof(val))) { if (close(fd)) { perror("close packet"); @@ -113,6 +112,20 @@ static void sock_fanout_set_cbpf(int fd) } } +static void sock_fanout_getopts(int fd, uint16_t *typeflags, uint16_t *group_id) +{ + int sockopt; + socklen_t sockopt_len = sizeof(sockopt); + + if (getsockopt(fd, SOL_PACKET, PACKET_FANOUT, + &sockopt, &sockopt_len)) { + perror("failed to getsockopt"); + exit(1); + } + *typeflags = sockopt >> 16; + *group_id = sockopt & 0xfffff; +} + static void sock_fanout_set_ebpf(int fd) { const int len_off = __builtin_offsetof(struct __sk_buff, len); @@ -228,7 +241,7 @@ static void test_control_single(void) fprintf(stderr, "test: control single socket\n"); if (sock_fanout_open(PACKET_FANOUT_ROLLOVER | - PACKET_FANOUT_FLAG_ROLLOVER) != -1) { + PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) { fprintf(stderr, "ERROR: opened socket with dual rollover\n"); exit(1); } @@ -241,26 +254,26 @@ static void test_control_group(void) fprintf(stderr, "test: control multiple sockets\n"); - fds[0] = sock_fanout_open(PACKET_FANOUT_HASH); + fds[0] = sock_fanout_open(PACKET_FANOUT_HASH, 0); if (fds[0] == -1) { fprintf(stderr, "ERROR: failed to open HASH socket\n"); exit(1); } if (sock_fanout_open(PACKET_FANOUT_HASH | - PACKET_FANOUT_FLAG_DEFRAG) != -1) { + PACKET_FANOUT_FLAG_DEFRAG, 0) != -1) { fprintf(stderr, "ERROR: joined group with wrong flag defrag\n"); exit(1); } if (sock_fanout_open(PACKET_FANOUT_HASH | - PACKET_FANOUT_FLAG_ROLLOVER) != -1) { + PACKET_FANOUT_FLAG_ROLLOVER, 0) != -1) { fprintf(stderr, "ERROR: joined group with wrong flag ro\n"); exit(1); } - if (sock_fanout_open(PACKET_FANOUT_CPU) != -1) { + if (sock_fanout_open(PACKET_FANOUT_CPU, 0) != -1) { fprintf(stderr, "ERROR: joined group with wrong mode\n"); exit(1); } - fds[1] = sock_fanout_open(PACKET_FANOUT_HASH); + fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, 0); if (fds[1] == -1) { fprintf(stderr, "ERROR: failed to join group\n"); exit(1); @@ -271,6 +284,61 @@ static void test_control_group(void) } } +/* Test creating a unique fanout group ids */ +static void test_unique_fanout_group_ids(void) +{ + int fds[3]; + uint16_t typeflags, first_group_id, second_group_id; + + fprintf(stderr, "test: unique ids\n"); + + fds[0] = sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_UNIQUEID, 0); + if (fds[0] == -1) { + fprintf(stderr, "ERROR: failed to create a unique id group.\n"); + exit(1); + } + + sock_fanout_getopts(fds[0], &typeflags, &first_group_id); + if (typeflags != PACKET_FANOUT_HASH) { + fprintf(stderr, "ERROR: unexpected typeflags %x\n", typeflags); + exit(1); + } + + if (sock_fanout_open(PACKET_FANOUT_CPU, first_group_id)) { + fprintf(stderr, "ERROR: joined group with wrong type.\n"); + exit(1); + } + + fds[1] = sock_fanout_open(PACKET_FANOUT_HASH, first_group_id); + if (fds[1] == -1) { + fprintf(stderr, + "ERROR: failed to join previously created group.\n"); + exit(1); + } + + fds[2] = sock_fanout_open(PACKET_FANOUT_HASH | + PACKET_FANOUT_FLAG_UNIQUEID, 0); + if (fds[2] == -1) { + fprintf(stderr, + "ERROR: failed to create a second unique id group.\n"); + exit(1); + } + + sock_fanout_getopts(fds[2], &typeflags, &second_group_id); + if (sock_fanout_open(PACKET_FANOUT_HASH | PACKET_FANOUT_FLAG_UNIQUEID, + second_group_id) != -1) { + fprintf(stderr, + "ERROR: specified a group id when requesting unique id\n"); + exit(1); + } + + if (close(fds[0]) || close(fds[1]) || close(fds[2])) { + fprintf(stderr, "ERROR: closing sockets\n"); + exit(1); + } +} + static int test_datapath(uint16_t typeflags, int port_off, const int expect1[], const int expect2[]) { @@ -281,8 +349,8 @@ static int test_datapath(uint16_t typeflags, int port_off, fprintf(stderr, "test: datapath 0x%hx\n", typeflags); - fds[0] = sock_fanout_open(typeflags); - fds[1] = sock_fanout_open(typeflags); + fds[0] = sock_fanout_open(typeflags, 0); + fds[1] = sock_fanout_open(typeflags, 0); if (fds[0] == -1 || fds[1] == -1) { fprintf(stderr, "ERROR: failed open\n"); exit(1); @@ -349,10 +417,12 @@ int main(int argc, char **argv) const int expect_cpu0[2][2] = { { 20, 0 }, { 20, 0 } }; const int expect_cpu1[2][2] = { { 0, 20 }, { 0, 20 } }; const int expect_bpf[2][2] = { { 15, 5 }, { 15, 20 } }; + const int expect_uniqueid[2][2] = { { 20, 20}, { 20, 20 } }; int port_off = 2, tries = 5, ret; test_control_single(); test_control_group(); + test_unique_fanout_group_ids(); /* find a set of ports that do not collide onto the same socket */ ret = test_datapath(PACKET_FANOUT_HASH, port_off, @@ -383,6 +453,9 @@ int main(int argc, char **argv) ret |= test_datapath(PACKET_FANOUT_CPU, port_off, expect_cpu1[0], expect_cpu1[1]); + ret |= test_datapath(PACKET_FANOUT_FLAG_UNIQUEID, port_off, + expect_uniqueid[0], expect_uniqueid[1]); + if (ret) return 1; -- cgit From 7f3c6e6b905018d07228fe0f43f0ef1359e7196d Mon Sep 17 00:00:00 2001 From: Thomas Falcon Date: Fri, 21 Apr 2017 15:38:40 -0400 Subject: ibmvnic: Set real number of rx queues Along with 5 TX queues, 5 RX queues are allocated at the beginning of device probe. However, only the real number of TX queues is set. Configure the real number of RX queues as well. Signed-off-by: Thomas Falcon Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index e8c72abfd7ac..7f4cecbb4b5a 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -560,6 +560,24 @@ static void release_resources(struct ibmvnic_adapter *adapter) release_error_buffers(adapter); } +static int set_real_num_queues(struct net_device *netdev) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + int rc; + + rc = netif_set_real_num_tx_queues(netdev, adapter->req_tx_queues); + if (rc) { + netdev_err(netdev, "failed to set the number of tx queues\n"); + return rc; + } + + rc = netif_set_real_num_rx_queues(netdev, adapter->req_rx_queues); + if (rc) + netdev_err(netdev, "failed to set the number of rx queues\n"); + + return rc; +} + static int ibmvnic_open(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); @@ -578,11 +596,9 @@ static int ibmvnic_open(struct net_device *netdev) if (rc) return rc; - rc = netif_set_real_num_tx_queues(netdev, adapter->req_tx_queues); - if (rc) { - dev_err(dev, "failed to set the number of tx queues\n"); - return -1; - } + rc = set_real_num_queues(netdev); + if (rc) + return rc; rc = init_sub_crq_irqs(adapter); if (rc) { -- cgit From 6052d5e2a1961b59dd942b52d3bf2b395d023644 Mon Sep 17 00:00:00 2001 From: Murilo Fossa Vicentini Date: Fri, 21 Apr 2017 15:38:46 -0400 Subject: ibmvnic: Insert header on VLAN tagged received frame This patch addresses a modification in the PAPR+ specification which now defines a previously reserved value for vNIC capabilities. It indicates whether the system firmware performs a VLAN header stripping on all VLAN tagged received frames, in case it does, the behavior expected is for the ibmvnic driver to be responsible for inserting the VLAN header. Reported-by: Manvanthara B. Puttashankar Signed-off-by: Murilo Fossa Vicentini Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 21 ++++++++++++++++++++- drivers/net/ethernet/ibm/ibmvnic.h | 2 ++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 7f4cecbb4b5a..0f3595439293 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -74,6 +74,7 @@ #include #include #include +#include #include "ibmvnic.h" @@ -1105,7 +1106,15 @@ restart_poll: skb = rx_buff->skb; skb_copy_to_linear_data(skb, rx_buff->data + offset, length); - skb->vlan_tci = be16_to_cpu(next->rx_comp.vlan_tci); + + /* VLAN Header has been stripped by the system firmware and + * needs to be inserted by the driver + */ + if (adapter->rx_vlan_header_insertion && + (flags & IBMVNIC_VLAN_STRIPPED)) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + ntohs(next->rx_comp.vlan_tci)); + /* free the entry */ next->rx_comp.first = 0; remove_buff_from_pool(adapter, rx_buff); @@ -2170,6 +2179,10 @@ static void send_cap_queries(struct ibmvnic_adapter *adapter) atomic_inc(&adapter->running_cap_crqs); ibmvnic_send_crq(adapter, &crq); + crq.query_capability.capability = cpu_to_be16(RX_VLAN_HEADER_INSERTION); + atomic_inc(&adapter->running_cap_crqs); + ibmvnic_send_crq(adapter, &crq); + crq.query_capability.capability = cpu_to_be16(MAX_TX_SG_ENTRIES); atomic_inc(&adapter->running_cap_crqs); ibmvnic_send_crq(adapter, &crq); @@ -2719,6 +2732,12 @@ static void handle_query_cap_rsp(union ibmvnic_crq *crq, netdev_dbg(netdev, "vlan_header_insertion = %lld\n", adapter->vlan_header_insertion); break; + case RX_VLAN_HEADER_INSERTION: + adapter->rx_vlan_header_insertion = + be64_to_cpu(crq->query_capability.number); + netdev_dbg(netdev, "rx_vlan_header_insertion = %lld\n", + adapter->rx_vlan_header_insertion); + break; case MAX_TX_SG_ENTRIES: adapter->max_tx_sg_entries = be64_to_cpu(crq->query_capability.number); diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 355225cf6d30..387c84303b7f 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -733,6 +733,7 @@ enum ibmvnic_capabilities { REQ_MTU = 21, MAX_MULTICAST_FILTERS = 22, VLAN_HEADER_INSERTION = 23, + RX_VLAN_HEADER_INSERTION = 24, MAX_TX_SG_ENTRIES = 25, RX_SG_SUPPORTED = 26, RX_SG_REQUESTED = 27, @@ -993,6 +994,7 @@ struct ibmvnic_adapter { u64 req_mtu; u64 max_multicast_filters; u64 vlan_header_insertion; + u64 rx_vlan_header_insertion; u64 max_tx_sg_entries; u64 rx_sg_supported; u64 rx_sg_requested; -- cgit From 2f9de9bac625ae08e9ea132e8dc08cfbe9fb67d2 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Fri, 21 Apr 2017 15:38:52 -0400 Subject: ibmvnic: Only retrieve error info if present When handling a fatal error in the driver, there can be additional error information provided by the vios. This information is not always present, so only retrieve the additional error information when present. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 71 +++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 0f3595439293..cc34bf9be405 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -2361,25 +2361,22 @@ static void handle_error_info_rsp(union ibmvnic_crq *crq, kfree(error_buff); } -static void handle_error_indication(union ibmvnic_crq *crq, - struct ibmvnic_adapter *adapter) +static void request_error_information(struct ibmvnic_adapter *adapter, + union ibmvnic_crq *err_crq) { - int detail_len = be32_to_cpu(crq->error_indication.detail_error_sz); struct device *dev = &adapter->vdev->dev; + struct net_device *netdev = adapter->netdev; struct ibmvnic_error_buff *error_buff; - union ibmvnic_crq new_crq; + unsigned long timeout = msecs_to_jiffies(30000); + union ibmvnic_crq crq; unsigned long flags; - - dev_err(dev, "Firmware reports %serror id %x, cause %d\n", - crq->error_indication. - flags & IBMVNIC_FATAL_ERROR ? "FATAL " : "", - be32_to_cpu(crq->error_indication.error_id), - be16_to_cpu(crq->error_indication.error_cause)); + int rc, detail_len; error_buff = kmalloc(sizeof(*error_buff), GFP_ATOMIC); if (!error_buff) return; + detail_len = be32_to_cpu(err_crq->error_indication.detail_error_sz); error_buff->buff = kmalloc(detail_len, GFP_ATOMIC); if (!error_buff->buff) { kfree(error_buff); @@ -2389,27 +2386,61 @@ static void handle_error_indication(union ibmvnic_crq *crq, error_buff->dma = dma_map_single(dev, error_buff->buff, detail_len, DMA_FROM_DEVICE); if (dma_mapping_error(dev, error_buff->dma)) { - if (!firmware_has_feature(FW_FEATURE_CMO)) - dev_err(dev, "Couldn't map error buffer\n"); + netdev_err(netdev, "Couldn't map error buffer\n"); kfree(error_buff->buff); kfree(error_buff); return; } error_buff->len = detail_len; - error_buff->error_id = crq->error_indication.error_id; + error_buff->error_id = err_crq->error_indication.error_id; spin_lock_irqsave(&adapter->error_list_lock, flags); list_add_tail(&error_buff->list, &adapter->errors); spin_unlock_irqrestore(&adapter->error_list_lock, flags); - memset(&new_crq, 0, sizeof(new_crq)); - new_crq.request_error_info.first = IBMVNIC_CRQ_CMD; - new_crq.request_error_info.cmd = REQUEST_ERROR_INFO; - new_crq.request_error_info.ioba = cpu_to_be32(error_buff->dma); - new_crq.request_error_info.len = cpu_to_be32(detail_len); - new_crq.request_error_info.error_id = crq->error_indication.error_id; - ibmvnic_send_crq(adapter, &new_crq); + memset(&crq, 0, sizeof(crq)); + crq.request_error_info.first = IBMVNIC_CRQ_CMD; + crq.request_error_info.cmd = REQUEST_ERROR_INFO; + crq.request_error_info.ioba = cpu_to_be32(error_buff->dma); + crq.request_error_info.len = cpu_to_be32(detail_len); + crq.request_error_info.error_id = err_crq->error_indication.error_id; + + rc = ibmvnic_send_crq(adapter, &crq); + if (rc) { + netdev_err(netdev, "failed to request error information\n"); + goto err_info_fail; + } + + if (!wait_for_completion_timeout(&adapter->init_done, timeout)) { + netdev_err(netdev, "timeout waiting for error information\n"); + goto err_info_fail; + } + + return; + +err_info_fail: + spin_lock_irqsave(&adapter->error_list_lock, flags); + list_del(&error_buff->list); + spin_unlock_irqrestore(&adapter->error_list_lock, flags); + + kfree(error_buff->buff); + kfree(error_buff); +} + +static void handle_error_indication(union ibmvnic_crq *crq, + struct ibmvnic_adapter *adapter) +{ + struct device *dev = &adapter->vdev->dev; + + dev_err(dev, "Firmware reports %serror id %x, cause %d\n", + crq->error_indication.flags + & IBMVNIC_FATAL_ERROR ? "FATAL " : "", + be32_to_cpu(crq->error_indication.error_id), + be16_to_cpu(crq->error_indication.error_cause)); + + if (be32_to_cpu(crq->error_indication.error_id)) + request_error_information(adapter, crq); } static void handle_change_mac_rsp(union ibmvnic_crq *crq, -- cgit From 5d5e84eb7276d30a21257515a83b34d1ef77f99b Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Fri, 21 Apr 2017 15:38:58 -0400 Subject: ibmvnic: Move initialization of the stats token to ibmvnic_open We should be initializing the stats token in the same place we initialize the other resources for the driver. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index cc34bf9be405..199cccbb577a 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -607,6 +607,10 @@ static int ibmvnic_open(struct net_device *netdev) return -1; } + rc = init_stats_token(adapter); + if (rc) + return rc; + adapter->map_id = 1; adapter->napi = kcalloc(adapter->req_rx_queues, sizeof(struct napi_struct), GFP_KERNEL); @@ -3241,12 +3245,6 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter) return rc; } - rc = init_stats_token(adapter); - if (rc) { - release_crq_queue(adapter); - return rc; - } - init_completion(&adapter->init_done); ibmvnic_send_crq_init(adapter); if (!wait_for_completion_timeout(&adapter->init_done, timeout)) { -- cgit From 53da09e92910f675ebb93921007428a3c2a024fb Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Fri, 21 Apr 2017 15:39:04 -0400 Subject: ibmvnic: Add set_link_state routine for setting adapter link state Create a common routine for setting the link state for the vnic adapter. This update moves the sending of the crq and waiting for the link state response to a common place. The new routine also adds handling of resending the crq in cases of getting a partial success response. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 71 ++++++++++++++++++++++++++++++-------- drivers/net/ethernet/ibm/ibmvnic.h | 1 + 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 199cccbb577a..115f21694994 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -561,6 +561,51 @@ static void release_resources(struct ibmvnic_adapter *adapter) release_error_buffers(adapter); } +static int set_link_state(struct ibmvnic_adapter *adapter, u8 link_state) +{ + struct net_device *netdev = adapter->netdev; + unsigned long timeout = msecs_to_jiffies(30000); + union ibmvnic_crq crq; + bool resend; + int rc; + + if (adapter->logical_link_state == link_state) { + netdev_dbg(netdev, "Link state already %d\n", link_state); + return 0; + } + + netdev_err(netdev, "setting link state %d\n", link_state); + memset(&crq, 0, sizeof(crq)); + crq.logical_link_state.first = IBMVNIC_CRQ_CMD; + crq.logical_link_state.cmd = LOGICAL_LINK_STATE; + crq.logical_link_state.link_state = link_state; + + do { + resend = false; + + reinit_completion(&adapter->init_done); + rc = ibmvnic_send_crq(adapter, &crq); + if (rc) { + netdev_err(netdev, "Failed to set link state\n"); + return rc; + } + + if (!wait_for_completion_timeout(&adapter->init_done, + timeout)) { + netdev_err(netdev, "timeout setting link state\n"); + return -1; + } + + if (adapter->init_done_rc == 1) { + /* Partuial success, delay and re-send */ + mdelay(1000); + resend = true; + } + } while (resend); + + return 0; +} + static int set_real_num_queues(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); @@ -583,7 +628,6 @@ static int ibmvnic_open(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); struct device *dev = &adapter->vdev->dev; - union ibmvnic_crq crq; int rc = 0; int i; @@ -643,11 +687,9 @@ static int ibmvnic_open(struct net_device *netdev) for (i = 0; i < adapter->req_tx_queues; i++) enable_scrq_irq(adapter, adapter->tx_scrq[i]); - memset(&crq, 0, sizeof(crq)); - crq.logical_link_state.first = IBMVNIC_CRQ_CMD; - crq.logical_link_state.cmd = LOGICAL_LINK_STATE; - crq.logical_link_state.link_state = IBMVNIC_LOGICAL_LNK_UP; - ibmvnic_send_crq(adapter, &crq); + rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_UP); + if (rc) + goto ibmvnic_open_fail; netif_tx_start_all_queues(netdev); adapter->is_closed = false; @@ -681,7 +723,7 @@ static void disable_sub_crqs(struct ibmvnic_adapter *adapter) static int ibmvnic_close(struct net_device *netdev) { struct ibmvnic_adapter *adapter = netdev_priv(netdev); - union ibmvnic_crq crq; + int rc = 0; int i; adapter->closing = true; @@ -693,17 +735,13 @@ static int ibmvnic_close(struct net_device *netdev) if (!adapter->failover) netif_tx_stop_all_queues(netdev); - memset(&crq, 0, sizeof(crq)); - crq.logical_link_state.first = IBMVNIC_CRQ_CMD; - crq.logical_link_state.cmd = LOGICAL_LINK_STATE; - crq.logical_link_state.link_state = IBMVNIC_LOGICAL_LNK_DN; - ibmvnic_send_crq(adapter, &crq); + rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN); release_resources(adapter); adapter->is_closed = true; adapter->closing = false; - return 0; + return rc; } /** @@ -2945,9 +2983,14 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq, handle_login_rsp(crq, adapter); break; case LOGICAL_LINK_STATE_RSP: - netdev_dbg(netdev, "Got Logical Link State Response\n"); + netdev_dbg(netdev, + "Got Logical Link State Response, state: %d rc: %d\n", + crq->logical_link_state_rsp.link_state, + crq->logical_link_state_rsp.rc.code); adapter->logical_link_state = crq->logical_link_state_rsp.link_state; + adapter->init_done_rc = crq->logical_link_state_rsp.rc.code; + complete(&adapter->init_done); break; case LINK_STATE_INDICATION: netdev_dbg(netdev, "Got Logical Link State Indication\n"); diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 387c84303b7f..a69979f6f19d 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -964,6 +964,7 @@ struct ibmvnic_adapter { struct ibmvnic_tx_pool *tx_pool; bool closing; struct completion init_done; + int init_done_rc; struct list_head errors; spinlock_t error_list_lock; -- cgit From 3ca1993264503957f81f3846598b16601b1e9457 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Fri, 21 Apr 2017 15:39:10 -0400 Subject: ibmvnic: Validate napi exist before disabling them Validate that the napi structs exist before trying to disable them at driver close. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 115f21694994..5a916a2f91be 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -729,8 +729,10 @@ static int ibmvnic_close(struct net_device *netdev) adapter->closing = true; disable_sub_crqs(adapter); - for (i = 0; i < adapter->req_rx_queues; i++) - napi_disable(&adapter->napi[i]); + if (adapter->napi) { + for (i = 0; i < adapter->req_rx_queues; i++) + napi_disable(&adapter->napi[i]); + } if (!adapter->failover) netif_tx_stop_all_queues(netdev); -- cgit From 7f5b030830fecc4a2a235804a15b395720b48a24 Mon Sep 17 00:00:00 2001 From: Thomas Falcon Date: Fri, 21 Apr 2017 15:39:16 -0400 Subject: ibmvnic: Free skb's in cases of failure in transmit When an error is encountered during transmit we need to free the skb instead of returning TX_BUSY. Signed-off-by: Thomas Falcon Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 5a916a2f91be..51bf337bea7a 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -908,9 +908,13 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) be32_to_cpu(adapter->login_rsp_buf-> off_txsubm_subcrqs)); if (adapter->migrated) { + if (!netif_subqueue_stopped(netdev, skb)) + netif_stop_subqueue(netdev, queue_num); + dev_kfree_skb_any(skb); + tx_send_failed++; tx_dropped++; - ret = NETDEV_TX_BUSY; + ret = NETDEV_TX_OK; goto out; } @@ -976,11 +980,13 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) sizeof(tx_buff->indir_arr), DMA_TO_DEVICE); if (dma_mapping_error(dev, tx_buff->indir_dma)) { + dev_kfree_skb_any(skb); + tx_buff->skb = NULL; if (!firmware_has_feature(FW_FEATURE_CMO)) dev_err(dev, "tx: unable to map descriptor array\n"); tx_map_failed++; tx_dropped++; - ret = NETDEV_TX_BUSY; + ret = NETDEV_TX_OK; goto out; } lpar_rc = send_subcrq_indirect(adapter, handle_array[queue_num], @@ -999,9 +1005,15 @@ static int ibmvnic_xmit(struct sk_buff *skb, struct net_device *netdev) else tx_pool->consumer_index--; + dev_kfree_skb_any(skb); + tx_buff->skb = NULL; + + if (lpar_rc == H_CLOSED) + netif_stop_subqueue(netdev, queue_num); + tx_send_failed++; tx_dropped++; - ret = NETDEV_TX_BUSY; + ret = NETDEV_TX_OK; goto out; } -- cgit From abd0a4f2b41812e9ba334945e256909e3d28da57 Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Fri, 21 Apr 2017 16:48:05 -0700 Subject: openvswitch: Typo fix. Fix typo in a comment. Signed-off-by: Jarno Rajahalme Acked-by: Greg Rose Signed-off-by: David S. Miller --- net/openvswitch/conntrack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index 7b2c2fce408a..58de4c2da673 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -373,7 +373,7 @@ static int ovs_ct_init_labels(struct nf_conn *ct, struct sw_flow_key *key, } /* Labels are included in the IPCTNL_MSG_CT_NEW event only if the - * IPCT_LABEL bit it set in the event cache. + * IPCT_LABEL bit is set in the event cache. */ nf_conntrack_event_cache(IPCT_LABEL, ct); -- cgit From 120645513f55a4ac5543120d9e79925d30a0156f Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Fri, 21 Apr 2017 16:48:06 -0700 Subject: openvswitch: Add eventmask support to CT action. Add a new optional conntrack action attribute OVS_CT_ATTR_EVENTMASK, which can be used in conjunction with the commit flag (OVS_CT_ATTR_COMMIT) to set the mask of bits specifying which conntrack events (IPCT_*) should be delivered via the Netfilter netlink multicast groups. Default behavior depends on the system configuration, but typically a lot of events are delivered. This can be very chatty for the NFNLGRP_CONNTRACK_UPDATE group, even if only some types of events are of interest. Netfilter core init_conntrack() adds the event cache extension, so we only need to set the ctmask value. However, if the system is configured without support for events, the setting will be skipped due to extension not being found. Signed-off-by: Jarno Rajahalme Reviewed-by: Greg Rose Acked-by: Joe Stringer Signed-off-by: David S. Miller --- include/uapi/linux/openvswitch.h | 12 ++++++++++++ net/openvswitch/conntrack.c | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 66d1c3ccfd8e..61b7d36dfe34 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -693,6 +693,17 @@ struct ovs_action_hash { * nothing if the connection is already committed will check that the current * packet is in conntrack entry's original direction. If directionality does * not match, will delete the existing conntrack entry and commit a new one. + * @OVS_CT_ATTR_EVENTMASK: Mask of bits indicating which conntrack event types + * (enum ip_conntrack_events IPCT_*) should be reported. For any bit set to + * zero, the corresponding event type is not generated. Default behavior + * depends on system configuration, but typically all event types are + * generated, hence listening on NFNLGRP_CONNTRACK_UPDATE events may get a lot + * of events. Explicitly passing this attribute allows limiting the updates + * received to the events of interest. The bit 1 << IPCT_NEW, 1 << + * IPCT_RELATED, and 1 << IPCT_DESTROY must be set to ones for those events to + * be received on NFNLGRP_CONNTRACK_NEW and NFNLGRP_CONNTRACK_DESTROY groups, + * respectively. Remaining bits control the changes for which an event is + * delivered on the NFNLGRP_CONNTRACK_UPDATE group. */ enum ovs_ct_attr { OVS_CT_ATTR_UNSPEC, @@ -704,6 +715,7 @@ enum ovs_ct_attr { related connections. */ OVS_CT_ATTR_NAT, /* Nested OVS_NAT_ATTR_* */ OVS_CT_ATTR_FORCE_COMMIT, /* No argument */ + OVS_CT_ATTR_EVENTMASK, /* u32 mask of IPCT_* events. */ __OVS_CT_ATTR_MAX }; diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index 58de4c2da673..4f7c3b5c080b 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -66,7 +66,9 @@ struct ovs_conntrack_info { u8 commit : 1; u8 nat : 3; /* enum ovs_ct_nat */ u8 force : 1; + u8 have_eventmask : 1; u16 family; + u32 eventmask; /* Mask of 1 << IPCT_*. */ struct md_mark mark; struct md_labels labels; #ifdef CONFIG_NF_NAT_NEEDED @@ -1007,6 +1009,20 @@ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key, if (!ct) return 0; + /* Set the conntrack event mask if given. NEW and DELETE events have + * their own groups, but the NFNLGRP_CONNTRACK_UPDATE group listener + * typically would receive many kinds of updates. Setting the event + * mask allows those events to be filtered. The set event mask will + * remain in effect for the lifetime of the connection unless changed + * by a further CT action with both the commit flag and the eventmask + * option. */ + if (info->have_eventmask) { + struct nf_conntrack_ecache *cache = nf_ct_ecache_find(ct); + + if (cache) + cache->ctmask = info->eventmask; + } + /* Apply changes before confirming the connection so that the initial * conntrack NEW netlink event carries the values given in the CT * action. @@ -1238,6 +1254,8 @@ static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = { /* NAT length is checked when parsing the nested attributes. */ [OVS_CT_ATTR_NAT] = { .minlen = 0, .maxlen = INT_MAX }, #endif + [OVS_CT_ATTR_EVENTMASK] = { .minlen = sizeof(u32), + .maxlen = sizeof(u32) }, }; static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info, @@ -1316,6 +1334,11 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info, break; } #endif + case OVS_CT_ATTR_EVENTMASK: + info->have_eventmask = true; + info->eventmask = nla_get_u32(a); + break; + default: OVS_NLERR(log, "Unknown conntrack attr (%d)", type); @@ -1515,6 +1538,10 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info, ct_info->helper->name)) return -EMSGSIZE; } + if (ct_info->have_eventmask && + nla_put_u32(skb, OVS_CT_ATTR_EVENTMASK, ct_info->eventmask)) + return -EMSGSIZE; + #ifdef CONFIG_NF_NAT_NEEDED if (ct_info->nat && !ovs_ct_nat_to_attr(ct_info, skb)) return -EMSGSIZE; -- cgit From a82fba8dbfb522bd19b1644bf599135680fd0122 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 21 Apr 2017 20:11:22 -0400 Subject: bnxt_en: Pass DCB RoCE app priority to firmware. When the driver gets the RoCE app priority set/delete call through DCBNL, the driver will send the information to the firmware to set up the priority VLAN tag for RDMA traffic. [ New version using the common ETH_P_IBOE constant in if_ether.h ] Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c | 108 +++++++++++++++++++++++++- drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h | 1 + 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c index 03532061d211..46de2f8ff024 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c @@ -1,6 +1,7 @@ /* Broadcom NetXtreme-C/E network driver. * * Copyright (c) 2014-2016 Broadcom Corporation + * Copyright (c) 2016-2017 Broadcom Limited * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,6 +15,7 @@ #include #include #include +#include #include "bnxt_hsi.h" #include "bnxt.h" #include "bnxt_dcb.h" @@ -241,6 +243,92 @@ static int bnxt_hwrm_queue_pfc_qcfg(struct bnxt *bp, struct ieee_pfc *pfc) return 0; } +static int bnxt_hwrm_set_dcbx_app(struct bnxt *bp, struct dcb_app *app, + bool add) +{ + struct hwrm_fw_set_structured_data_input set = {0}; + struct hwrm_fw_get_structured_data_input get = {0}; + struct hwrm_struct_data_dcbx_app *fw_app; + struct hwrm_struct_hdr *data; + dma_addr_t mapping; + size_t data_len; + int rc, n, i; + + if (bp->hwrm_spec_code < 0x10601) + return 0; + + n = IEEE_8021QAZ_MAX_TCS; + data_len = sizeof(*data) + sizeof(*fw_app) * n; + data = dma_alloc_coherent(&bp->pdev->dev, data_len, &mapping, + GFP_KERNEL); + if (!data) + return -ENOMEM; + + memset(data, 0, data_len); + bnxt_hwrm_cmd_hdr_init(bp, &get, HWRM_FW_GET_STRUCTURED_DATA, -1, -1); + get.dest_data_addr = cpu_to_le64(mapping); + get.structure_id = cpu_to_le16(STRUCT_HDR_STRUCT_ID_DCBX_APP); + get.subtype = cpu_to_le16(HWRM_STRUCT_DATA_SUBTYPE_HOST_OPERATIONAL); + get.count = 0; + rc = hwrm_send_message(bp, &get, sizeof(get), HWRM_CMD_TIMEOUT); + if (rc) + goto set_app_exit; + + fw_app = (struct hwrm_struct_data_dcbx_app *)(data + 1); + + if (data->struct_id != cpu_to_le16(STRUCT_HDR_STRUCT_ID_DCBX_APP)) { + rc = -ENODEV; + goto set_app_exit; + } + + n = data->count; + for (i = 0; i < n; i++, fw_app++) { + if (fw_app->protocol_id == cpu_to_be16(app->protocol) && + fw_app->protocol_selector == app->selector && + fw_app->priority == app->priority) { + if (add) + goto set_app_exit; + else + break; + } + } + if (add) { + /* append */ + n++; + fw_app->protocol_id = cpu_to_be16(app->protocol); + fw_app->protocol_selector = app->selector; + fw_app->priority = app->priority; + fw_app->valid = 1; + } else { + size_t len = 0; + + /* not found, nothing to delete */ + if (n == i) + goto set_app_exit; + + len = (n - 1 - i) * sizeof(*fw_app); + if (len) + memmove(fw_app, fw_app + 1, len); + n--; + memset(fw_app + n, 0, sizeof(*fw_app)); + } + data->count = n; + data->len = cpu_to_le16(sizeof(*fw_app) * n); + data->subtype = cpu_to_le16(HWRM_STRUCT_DATA_SUBTYPE_HOST_OPERATIONAL); + + bnxt_hwrm_cmd_hdr_init(bp, &set, HWRM_FW_SET_STRUCTURED_DATA, -1, -1); + set.src_data_addr = cpu_to_le64(mapping); + set.data_len = cpu_to_le16(sizeof(*data) + sizeof(*fw_app) * n); + set.hdr_cnt = 1; + rc = hwrm_send_message(bp, &set, sizeof(set), HWRM_CMD_TIMEOUT); + if (rc) + rc = -EIO; + +set_app_exit: + dma_free_coherent(&bp->pdev->dev, data_len, data, mapping); + return rc; +} + static int bnxt_ets_validate(struct bnxt *bp, struct ieee_ets *ets, u8 *tc) { int total_ets_bw = 0; @@ -417,6 +505,15 @@ static int bnxt_dcbnl_ieee_setapp(struct net_device *dev, struct dcb_app *app) return -EINVAL; rc = dcb_ieee_setapp(dev, app); + if (rc) + return rc; + + if ((app->selector == IEEE_8021QAZ_APP_SEL_ETHERTYPE && + app->protocol == ETH_P_IBOE) || + (app->selector == IEEE_8021QAZ_APP_SEL_DGRAM && + app->protocol == ROCE_V2_UDP_DPORT)) + rc = bnxt_hwrm_set_dcbx_app(bp, app, true); + return rc; } @@ -425,10 +522,19 @@ static int bnxt_dcbnl_ieee_delapp(struct net_device *dev, struct dcb_app *app) struct bnxt *bp = netdev_priv(dev); int rc; - if (!(bp->dcbx_cap & DCB_CAP_DCBX_VER_IEEE)) + if (!(bp->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) || + !(bp->dcbx_cap & DCB_CAP_DCBX_HOST)) return -EINVAL; rc = dcb_ieee_delapp(dev, app); + if (rc) + return rc; + if ((app->selector == IEEE_8021QAZ_APP_SEL_ETHERTYPE && + app->protocol == ETH_P_IBOE) || + (app->selector == IEEE_8021QAZ_APP_SEL_DGRAM && + app->protocol == ROCE_V2_UDP_DPORT)) + rc = bnxt_hwrm_set_dcbx_app(bp, app, false); + return rc; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h index 35a0d28cf2fd..ecd0a5e46a49 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h @@ -1,6 +1,7 @@ /* Broadcom NetXtreme-C/E network driver. * * Copyright (c) 2014-2016 Broadcom Corporation + * Copyright (c) 2016-2017 Broadcom Limited * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by -- cgit From f0249056eaf2b9a17b2b76a6e099e9b7877e187d Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 21 Apr 2017 20:11:23 -0400 Subject: bnxt_en: Fix VF attributes reporting. The .ndo_get_vf_config() is returning the wrong qos attribute. Fix the code that checks and reports the qos and spoofchk attributes. The BNXT_VF_QOS and BNXT_VF_LINK_UP flags should not be set by default during init. time. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index f89353175e6b..b8e7248294d9 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -138,8 +138,11 @@ int bnxt_get_vf_config(struct net_device *dev, int vf_id, ivi->max_tx_rate = vf->max_tx_rate; ivi->min_tx_rate = vf->min_tx_rate; ivi->vlan = vf->vlan; - ivi->qos = vf->flags & BNXT_VF_QOS; - ivi->spoofchk = vf->flags & BNXT_VF_SPOOFCHK; + if (vf->flags & BNXT_VF_QOS) + ivi->qos = vf->vlan >> VLAN_PRIO_SHIFT; + else + ivi->qos = 0; + ivi->spoofchk = !!(vf->flags & BNXT_VF_SPOOFCHK); if (!(vf->flags & BNXT_VF_LINK_FORCED)) ivi->linkstate = IFLA_VF_LINK_STATE_AUTO; else if (vf->flags & BNXT_VF_LINK_UP) @@ -304,7 +307,6 @@ static int bnxt_set_vf_attr(struct bnxt *bp, int num_vfs) for (i = 0; i < num_vfs; i++) { vf = &bp->pf.vf[i]; memset(vf, 0, sizeof(*vf)); - vf->flags = BNXT_VF_QOS | BNXT_VF_LINK_UP; } return 0; } -- cgit From 38a21b34aacd4db7b7b74c61afae42ea6718448d Mon Sep 17 00:00:00 2001 From: Deepak Khungar Date: Fri, 21 Apr 2017 20:11:24 -0400 Subject: bnxt_en: Add 100G link speed reporting for BCM57454 ASIC in ethtool Added support for 100G link speed reporting for Broadcom BCM57454 ASIC in ethtool command. Signed-off-by: Deepak Khungar Signed-off-by: Ray Jui Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 5 +++-- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 2 ++ drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 14 +++++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 129b8101b932..447ee3d27d21 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -5471,7 +5471,8 @@ static void bnxt_report_link(struct bnxt *bp) if (bp->link_info.link_up) { const char *duplex; const char *flow_ctrl; - u16 speed, fec; + u32 speed; + u16 fec; netif_carrier_on(bp->dev); if (bp->link_info.duplex == BNXT_LINK_DUPLEX_FULL) @@ -5487,7 +5488,7 @@ static void bnxt_report_link(struct bnxt *bp) else flow_ctrl = "none"; speed = bnxt_fw_to_ethtool_speed(bp->link_info.link_speed); - netdev_info(bp->dev, "NIC Link is Up, %d Mbps %s duplex, Flow control: %s\n", + netdev_info(bp->dev, "NIC Link is Up, %u Mbps %s duplex, Flow control: %s\n", speed, duplex, flow_ctrl); if (bp->flags & BNXT_FLAG_EEE_CAP) netdev_info(bp->dev, "EEE is %s\n", diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index c9a1688a65de..25fef612de70 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -851,6 +851,7 @@ struct bnxt_link_info { #define BNXT_LINK_SPEED_25GB PORT_PHY_QCFG_RESP_LINK_SPEED_25GB #define BNXT_LINK_SPEED_40GB PORT_PHY_QCFG_RESP_LINK_SPEED_40GB #define BNXT_LINK_SPEED_50GB PORT_PHY_QCFG_RESP_LINK_SPEED_50GB +#define BNXT_LINK_SPEED_100GB PORT_PHY_QCFG_RESP_LINK_SPEED_100GB u16 support_speeds; u16 auto_link_speeds; /* fw adv setting */ #define BNXT_LINK_SPEED_MSK_100MB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_100MB @@ -862,6 +863,7 @@ struct bnxt_link_info { #define BNXT_LINK_SPEED_MSK_25GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_25GB #define BNXT_LINK_SPEED_MSK_40GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_40GB #define BNXT_LINK_SPEED_MSK_50GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_50GB +#define BNXT_LINK_SPEED_MSK_100GB PORT_PHY_QCFG_RESP_SUPPORT_SPEEDS_100GB u16 support_auto_speeds; u16 lp_auto_link_speeds; u16 force_link_speed; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 848ecf212b8f..11ddf0adc6e1 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -929,6 +929,9 @@ u32 _bnxt_fw_to_ethtool_adv_spds(u16 fw_speeds, u8 fw_pause) if ((fw_speeds) & BNXT_LINK_SPEED_MSK_50GB) \ ethtool_link_ksettings_add_link_mode(lk_ksettings, name,\ 50000baseCR2_Full);\ + if ((fw_speeds) & BNXT_LINK_SPEED_MSK_100GB) \ + ethtool_link_ksettings_add_link_mode(lk_ksettings, name,\ + 100000baseCR4_Full);\ if ((fw_pause) & BNXT_LINK_PAUSE_RX) { \ ethtool_link_ksettings_add_link_mode(lk_ksettings, name,\ Pause); \ @@ -965,6 +968,9 @@ u32 _bnxt_fw_to_ethtool_adv_spds(u16 fw_speeds, u8 fw_pause) if (ethtool_link_ksettings_test_link_mode(lk_ksettings, name, \ 50000baseCR2_Full)) \ (fw_speeds) |= BNXT_LINK_SPEED_MSK_50GB; \ + if (ethtool_link_ksettings_test_link_mode(lk_ksettings, name, \ + 100000baseCR4_Full)) \ + (fw_speeds) |= BNXT_LINK_SPEED_MSK_100GB; \ } static void bnxt_fw_to_ethtool_advertised_spds(struct bnxt_link_info *link_info, @@ -1027,6 +1033,8 @@ u32 bnxt_fw_to_ethtool_speed(u16 fw_link_speed) return SPEED_40000; case BNXT_LINK_SPEED_50GB: return SPEED_50000; + case BNXT_LINK_SPEED_100GB: + return SPEED_100000; default: return SPEED_UNKNOWN; } @@ -1092,7 +1100,7 @@ static int bnxt_get_link_ksettings(struct net_device *dev, return 0; } -static u32 bnxt_get_fw_speed(struct net_device *dev, u16 ethtool_speed) +static u32 bnxt_get_fw_speed(struct net_device *dev, u32 ethtool_speed) { struct bnxt *bp = netdev_priv(dev); struct bnxt_link_info *link_info = &bp->link_info; @@ -1132,6 +1140,10 @@ static u32 bnxt_get_fw_speed(struct net_device *dev, u16 ethtool_speed) if (support_spds & BNXT_LINK_SPEED_MSK_50GB) fw_speed = PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_50GB; break; + case SPEED_100000: + if (support_spds & BNXT_LINK_SPEED_MSK_100GB) + fw_speed = PORT_PHY_CFG_REQ_AUTO_LINK_SPEED_100GB; + break; default: netdev_err(dev, "unsupported speed!\n"); break; -- cgit From 7d63818a35851cf00867248d5ab50a8fe8df5943 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 21 Apr 2017 20:11:25 -0400 Subject: bnxt_en: Check the FW_LLDP_AGENT flag before allowing DCBX host agent. Check the additional flag in bnxt_hwrm_func_qcfg() before allowing DCBX to be done in host mode. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 447ee3d27d21..9130628227e0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4483,7 +4483,8 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp) } #endif if (BNXT_PF(bp) && (le16_to_cpu(resp->flags) & - FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED)) + (FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED | + FUNC_QCFG_RESP_FLAGS_FW_LLDP_AGENT_ENABLED))) bp->flags |= BNXT_FLAG_FW_LLDP_AGENT; switch (resp->port_partition_type) { -- cgit From 9e54e322ded40f424dcb5a13508e2556919ce12a Mon Sep 17 00:00:00 2001 From: Deepak Khungar Date: Fri, 21 Apr 2017 20:11:26 -0400 Subject: bnxt_en: Restrict a PF in Multi-Host mode from changing port PHY configuration This change restricts the PF in multi-host mode from setting any port level PHY configuration. The settings are controlled by firmware in Multi-Host mode. Signed-off-by: Deepak Khungar Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 13 +++++++++---- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 4 +++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 9130628227e0..b3ba66032980 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4482,10 +4482,15 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp) vf->vlan = le16_to_cpu(resp->vlan) & VLAN_VID_MASK; } #endif - if (BNXT_PF(bp) && (le16_to_cpu(resp->flags) & - (FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED | - FUNC_QCFG_RESP_FLAGS_FW_LLDP_AGENT_ENABLED))) - bp->flags |= BNXT_FLAG_FW_LLDP_AGENT; + if (BNXT_PF(bp)) { + u16 flags = le16_to_cpu(resp->flags); + + if (flags & (FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED | + FUNC_QCFG_RESP_FLAGS_FW_LLDP_AGENT_ENABLED)) + bp->flags |= BNXT_FLAG_FW_LLDP_AGENT; + if (flags & FUNC_QCFG_RESP_FLAGS_MULTI_HOST) + bp->flags |= BNXT_FLAG_MULTI_HOST; + } switch (resp->port_partition_type) { case FUNC_QCFG_RESP_PORT_PARTITION_TYPE_NPAR1_0: diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 25fef612de70..3ef42dbc6327 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1005,6 +1005,7 @@ struct bnxt { #define BNXT_FLAG_NO_AGG_RINGS 0x20000 #define BNXT_FLAG_RX_PAGE_MODE 0x40000 #define BNXT_FLAG_FW_LLDP_AGENT 0x80000 + #define BNXT_FLAG_MULTI_HOST 0x100000 #define BNXT_FLAG_CHIP_NITRO_A0 0x1000000 #define BNXT_FLAG_ALL_CONFIG_FEATS (BNXT_FLAG_TPA | \ @@ -1014,7 +1015,8 @@ struct bnxt { #define BNXT_PF(bp) (!((bp)->flags & BNXT_FLAG_VF)) #define BNXT_VF(bp) ((bp)->flags & BNXT_FLAG_VF) #define BNXT_NPAR(bp) ((bp)->port_partition_type) -#define BNXT_SINGLE_PF(bp) (BNXT_PF(bp) && !BNXT_NPAR(bp)) +#define BNXT_MH(bp) ((bp)->flags & BNXT_FLAG_MULTI_HOST) +#define BNXT_SINGLE_PF(bp) (BNXT_PF(bp) && !BNXT_NPAR(bp) && !BNXT_MH(bp)) #define BNXT_CHIP_TYPE_NITRO_A0(bp) ((bp)->flags & BNXT_FLAG_CHIP_NITRO_A0) #define BNXT_RX_PAGE_MODE(bp) ((bp)->flags & BNXT_FLAG_RX_PAGE_MODE) -- cgit From 4cc17bcf7f39088b5b68e3689f1653184c7e7ad4 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sat, 22 Apr 2017 09:21:10 +0800 Subject: net: atheros: atl1: use offset_in_page() macro Use offset_in_page() macro instead of open-coding. Signed-off-by: Geliang Tang Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/atlx/atl1.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c index 022772e1e249..83d2db2abb45 100644 --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -1886,7 +1886,7 @@ static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter) buffer_info->skb = skb; buffer_info->length = (u16) adapter->rx_buffer_len; page = virt_to_page(skb->data); - offset = (unsigned long)skb->data & ~PAGE_MASK; + offset = offset_in_page(skb->data); buffer_info->dma = pci_map_page(pdev, page, offset, adapter->rx_buffer_len, PCI_DMA_FROMDEVICE); @@ -2230,7 +2230,7 @@ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); buffer_info->length = hdr_len; page = virt_to_page(skb->data); - offset = (unsigned long)skb->data & ~PAGE_MASK; + offset = offset_in_page(skb->data); buffer_info->dma = pci_map_page(adapter->pdev, page, offset, hdr_len, PCI_DMA_TODEVICE); @@ -2254,9 +2254,8 @@ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, data_len -= buffer_info->length; page = virt_to_page(skb->data + (hdr_len + i * ATL1_MAX_TX_BUF_LEN)); - offset = (unsigned long)(skb->data + - (hdr_len + i * ATL1_MAX_TX_BUF_LEN)) & - ~PAGE_MASK; + offset = offset_in_page(skb->data + + (hdr_len + i * ATL1_MAX_TX_BUF_LEN)); buffer_info->dma = pci_map_page(adapter->pdev, page, offset, buffer_info->length, PCI_DMA_TODEVICE); @@ -2268,7 +2267,7 @@ static void atl1_tx_map(struct atl1_adapter *adapter, struct sk_buff *skb, /* not TSO */ buffer_info->length = buf_len; page = virt_to_page(skb->data); - offset = (unsigned long)skb->data & ~PAGE_MASK; + offset = offset_in_page(skb->data); buffer_info->dma = pci_map_page(adapter->pdev, page, offset, buf_len, PCI_DMA_TODEVICE); if (++next_to_use == tpd_ring->count) -- cgit From 11a9ec43302fc8d7fca0dc26b75234c20ff4c464 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 22 Apr 2017 13:22:01 +0100 Subject: net: netcp: fix spelling mistake: "memomry" -> "memory" Trivial fix to spelling mistake in dev_err message and rejoin line. Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/netcp_ethss.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c index eece3e2eec14..897176fc5043 100644 --- a/drivers/net/ethernet/ti/netcp_ethss.c +++ b/drivers/net/ethernet/ti/netcp_ethss.c @@ -3048,8 +3048,7 @@ static void init_secondary_ports(struct gbe_priv *gbe_dev, for_each_child_of_node(node, port) { slave = devm_kzalloc(dev, sizeof(*slave), GFP_KERNEL); if (!slave) { - dev_err(dev, - "memomry alloc failed for secondary port(%s), skipping...\n", + dev_err(dev, "memory alloc failed for secondary port(%s), skipping...\n", port->name); continue; } -- cgit From 58c4c6a3f7cb3a142674ba34e7e67e2d585b9787 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Sat, 22 Apr 2017 09:33:16 -0700 Subject: net: add rcu locking when changing early demux systemd-sysctl is triggering a suspicious RCU usage message when net.ipv4.tcp_early_demux or net.ipv4.udp_early_demux is changed via a sysctl config file: [ 33.896184] =============================== [ 33.899558] [ ERR: suspicious RCU usage. ] [ 33.900624] 4.11.0-rc7+ #104 Not tainted [ 33.901698] ------------------------------- [ 33.903059] /home/dsa/kernel-2.git/net/ipv4/sysctl_net_ipv4.c:305 suspicious rcu_dereference_check() usage! [ 33.905724] other info that might help us debug this: [ 33.907656] rcu_scheduler_active = 2, debug_locks = 0 [ 33.909288] 1 lock held by systemd-sysctl/143: [ 33.910373] #0: (sb_writers#5){.+.+.+}, at: [] file_start_write+0x45/0x48 [ 33.912407] stack backtrace: [ 33.914018] CPU: 0 PID: 143 Comm: systemd-sysctl Not tainted 4.11.0-rc7+ #104 [ 33.915631] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140531_083030-gandalf 04/01/2014 [ 33.917870] Call Trace: [ 33.918431] dump_stack+0x81/0xb6 [ 33.919241] lockdep_rcu_suspicious+0x10f/0x118 [ 33.920263] proc_configure_early_demux+0x65/0x10a [ 33.921391] proc_udp_early_demux+0x3a/0x41 add rcu locking to proc_configure_early_demux. Fixes: dddb64bcb3461 ("net: Add sysctl to toggle early demux for tcp and udp") Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/sysctl_net_ipv4.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 6fb25693c00b..ddac9e64b702 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -302,6 +302,8 @@ static void proc_configure_early_demux(int enabled, int protocol) struct inet6_protocol *ip6prot; #endif + rcu_read_lock(); + ipprot = rcu_dereference(inet_protos[protocol]); if (ipprot) ipprot->early_demux = enabled ? ipprot->early_demux_handler : @@ -313,6 +315,7 @@ static void proc_configure_early_demux(int enabled, int protocol) ip6prot->early_demux = enabled ? ip6prot->early_demux_handler : NULL; #endif + rcu_read_unlock(); } static int proc_tcp_early_demux(struct ctl_table *table, int write, -- cgit From cf1ef3f0719b4dcb74810ed507e2a2540f9811b4 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 20 Apr 2017 14:45:46 -0700 Subject: net/tcp_fastopen: Disable active side TFO in certain scenarios Middlebox firewall issues can potentially cause server's data being blackholed after a successful 3WHS using TFO. Following are the related reports from Apple: https://www.nanog.org/sites/default/files/Paasch_Network_Support.pdf Slide 31 identifies an issue where the client ACK to the server's data sent during a TFO'd handshake is dropped. C ---> syn-data ---> S C <--- syn/ack ----- S C (accept & write) C <---- data ------- S C ----- ACK -> X S [retry and timeout] https://www.ietf.org/proceedings/94/slides/slides-94-tcpm-13.pdf Slide 5 shows a similar situation that the server's data gets dropped after 3WHS. C ---- syn-data ---> S C <--- syn/ack ----- S C ---- ack --------> S S (accept & write) C? X <- data ------ S [retry and timeout] This is the worst failure b/c the client can not detect such behavior to mitigate the situation (such as disabling TFO). Failing to proceed, the application (e.g., SSL library) may simply timeout and retry with TFO again, and the process repeats indefinitely. The proposed solution is to disable active TFO globally under the following circumstances: 1. client side TFO socket detects out of order FIN 2. client side TFO socket receives out of order RST We disable active side TFO globally for 1hr at first. Then if it happens again, we disable it for 2h, then 4h, 8h, ... And we reset the timeout to 1hr if a client side TFO sockets not opened on loopback has successfully received data segs from server. And we examine this condition during close(). The rational behind it is that when such firewall issue happens, application running on the client should eventually close the socket as it is not able to get the data it is expecting. Or application running on the server should close the socket as it is not able to receive any response from client. In both cases, out of order FIN or RST will get received on the client given that the firewall will not block them as no data are in those frames. And we want to disable active TFO globally as it helps if the middle box is very close to the client and most of the connections are likely to fail. Also, add a debug sysctl: tcp_fastopen_blackhole_detect_timeout_sec: the initial timeout to use when firewall blackhole issue happens. This can be set and read. When setting it to 0, it means to disable the active disable logic. Signed-off-by: Wei Wang Acked-by: Yuchung Cheng Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 8 +++ include/linux/tcp.h | 1 + include/net/tcp.h | 6 ++ net/ipv4/sysctl_net_ipv4.c | 21 +++++++ net/ipv4/tcp.c | 1 + net/ipv4/tcp_fastopen.c | 101 +++++++++++++++++++++++++++++++++ net/ipv4/tcp_input.c | 23 ++++++-- net/ipv4/tcp_ipv4.c | 3 + 8 files changed, 160 insertions(+), 4 deletions(-) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index b1c6500e7a8d..974ab47ae53a 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -602,6 +602,14 @@ tcp_fastopen - INTEGER Note that that additional client or server features are only effective if the basic support (0x1 and 0x2) are enabled respectively. +tcp_fastopen_blackhole_timeout_sec - INTEGER + Initial time period in second to disable Fastopen on active TCP sockets + when a TFO firewall blackhole issue happens. + This time period will grow exponentially when more blackhole issues + get detected right after Fastopen is re-enabled and will reset to + initial value when the blackhole issue goes away. + By default, it is set to 1hr. + tcp_syn_retries - INTEGER Number of times initial SYNs for an active TCP connection attempt will be retransmitted. Should not be higher than 127. Default value diff --git a/include/linux/tcp.h b/include/linux/tcp.h index cfc2d9506ce8..cbe5b602a2d3 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -233,6 +233,7 @@ struct tcp_sock { u8 syn_data:1, /* SYN includes data */ syn_fastopen:1, /* SYN includes Fast Open option */ syn_fastopen_exp:1,/* SYN includes Fast Open exp. option */ + syn_fastopen_ch:1, /* Active TFO re-enabling probe */ syn_data_acked:1,/* data in SYN is acked by SYN-ACK */ save_syn:1, /* Save headers of SYN packet */ is_cwnd_limited:1;/* forward progress limited by snd_cwnd? */ diff --git a/include/net/tcp.h b/include/net/tcp.h index cc6ae0a95201..c1abc2abbdcb 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1506,6 +1506,12 @@ struct tcp_fastopen_context { struct rcu_head rcu; }; +extern unsigned int sysctl_tcp_fastopen_blackhole_timeout; +void tcp_fastopen_active_disable(void); +bool tcp_fastopen_active_should_disable(struct sock *sk); +void tcp_fastopen_active_disable_ofo_check(struct sock *sk); +void tcp_fastopen_active_timeout_reset(void); + /* Latencies incurred by various limits for a sender. They are * chronograph-like stats that are mutually exclusive. */ diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index ddac9e64b702..86957e9cd6c6 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -350,6 +350,19 @@ static int proc_udp_early_demux(struct ctl_table *table, int write, return ret; } +static int proc_tfo_blackhole_detect_timeout(struct ctl_table *table, + int write, + void __user *buffer, + size_t *lenp, loff_t *ppos) +{ + int ret; + + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + if (write && ret == 0) + tcp_fastopen_active_timeout_reset(); + return ret; +} + static struct ctl_table ipv4_table[] = { { .procname = "tcp_timestamps", @@ -399,6 +412,14 @@ static struct ctl_table ipv4_table[] = { .maxlen = ((TCP_FASTOPEN_KEY_LENGTH * 2) + 10), .proc_handler = proc_tcp_fastopen_key, }, + { + .procname = "tcp_fastopen_blackhole_timeout_sec", + .data = &sysctl_tcp_fastopen_blackhole_timeout, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_tfo_blackhole_detect_timeout, + .extra1 = &zero, + }, { .procname = "tcp_abort_on_overflow", .data = &sysctl_tcp_abort_on_overflow, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 04843ae77b9e..efc976ae66ae 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2296,6 +2296,7 @@ int tcp_disconnect(struct sock *sk, int flags) tcp_clear_xmit_timers(sk); __skb_queue_purge(&sk->sk_receive_queue); tcp_write_queue_purge(sk); + tcp_fastopen_active_disable_ofo_check(sk); skb_rbtree_purge(&tp->out_of_order_queue); inet->inet_dport = 0; diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 8ea4e9787f82..ff2d30ffc6f3 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -341,6 +341,13 @@ bool tcp_fastopen_cookie_check(struct sock *sk, u16 *mss, cookie->len = -1; return false; } + + /* Firewall blackhole issue check */ + if (tcp_fastopen_active_should_disable(sk)) { + cookie->len = -1; + return false; + } + if (sysctl_tcp_fastopen & TFO_CLIENT_NO_COOKIE) { cookie->len = -1; return true; @@ -380,3 +387,97 @@ bool tcp_fastopen_defer_connect(struct sock *sk, int *err) return false; } EXPORT_SYMBOL(tcp_fastopen_defer_connect); + +/* + * The following code block is to deal with middle box issues with TFO: + * Middlebox firewall issues can potentially cause server's data being + * blackholed after a successful 3WHS using TFO. + * The proposed solution is to disable active TFO globally under the + * following circumstances: + * 1. client side TFO socket receives out of order FIN + * 2. client side TFO socket receives out of order RST + * We disable active side TFO globally for 1hr at first. Then if it + * happens again, we disable it for 2h, then 4h, 8h, ... + * And we reset the timeout back to 1hr when we see a successful active + * TFO connection with data exchanges. + */ + +/* Default to 1hr */ +unsigned int sysctl_tcp_fastopen_blackhole_timeout __read_mostly = 60 * 60; +static atomic_t tfo_active_disable_times __read_mostly = ATOMIC_INIT(0); +static unsigned long tfo_active_disable_stamp __read_mostly; + +/* Disable active TFO and record current jiffies and + * tfo_active_disable_times + */ +void tcp_fastopen_active_disable(void) +{ + atomic_inc(&tfo_active_disable_times); + tfo_active_disable_stamp = jiffies; +} + +/* Reset tfo_active_disable_times to 0 */ +void tcp_fastopen_active_timeout_reset(void) +{ + atomic_set(&tfo_active_disable_times, 0); +} + +/* Calculate timeout for tfo active disable + * Return true if we are still in the active TFO disable period + * Return false if timeout already expired and we should use active TFO + */ +bool tcp_fastopen_active_should_disable(struct sock *sk) +{ + int tfo_da_times = atomic_read(&tfo_active_disable_times); + int multiplier; + unsigned long timeout; + + if (!tfo_da_times) + return false; + + /* Limit timout to max: 2^6 * initial timeout */ + multiplier = 1 << min(tfo_da_times - 1, 6); + timeout = multiplier * sysctl_tcp_fastopen_blackhole_timeout * HZ; + if (time_before(jiffies, tfo_active_disable_stamp + timeout)) + return true; + + /* Mark check bit so we can check for successful active TFO + * condition and reset tfo_active_disable_times + */ + tcp_sk(sk)->syn_fastopen_ch = 1; + return false; +} + +/* Disable active TFO if FIN is the only packet in the ofo queue + * and no data is received. + * Also check if we can reset tfo_active_disable_times if data is + * received successfully on a marked active TFO sockets opened on + * a non-loopback interface + */ +void tcp_fastopen_active_disable_ofo_check(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct rb_node *p; + struct sk_buff *skb; + struct dst_entry *dst; + + if (!tp->syn_fastopen) + return; + + if (!tp->data_segs_in) { + p = rb_first(&tp->out_of_order_queue); + if (p && !rb_next(p)) { + skb = rb_entry(p, struct sk_buff, rbnode); + if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) { + tcp_fastopen_active_disable(); + return; + } + } + } else if (tp->syn_fastopen_ch && + atomic_read(&tfo_active_disable_times)) { + dst = sk_dst_get(sk); + if (!(dst && dst->dev && (dst->dev->flags & IFF_LOOPBACK))) + tcp_fastopen_active_timeout_reset(); + dst_release(dst); + } +} diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 341f021f02a2..9f342a67dc74 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5300,8 +5300,16 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb, if (rst_seq_match) tcp_reset(sk); - else + else { + /* Disable TFO if RST is out-of-order + * and no data has been received + * for current active TFO socket + */ + if (tp->syn_fastopen && !tp->data_segs_in && + sk->sk_state == TCP_ESTABLISHED) + tcp_fastopen_active_disable(); tcp_send_challenge_ack(sk, skb); + } goto discard; } @@ -6044,9 +6052,16 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) break; } - if (tp->linger2 < 0 || - (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && - after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt))) { + if (tp->linger2 < 0) { + tcp_done(sk); + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONDATA); + return 1; + } + if (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && + after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) { + /* Receive out of order FIN after close() */ + if (tp->syn_fastopen && th->fin) + tcp_fastopen_active_disable(); tcp_done(sk); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONDATA); return 1; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 20cbd2f07f28..cbbafe546c0f 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1855,6 +1855,9 @@ void tcp_v4_destroy_sock(struct sock *sk) /* Cleanup up the write buffer. */ tcp_write_queue_purge(sk); + /* Check if we want to disable active TFO */ + tcp_fastopen_active_disable_ofo_check(sk); + /* Cleans up our, hopefully empty, out_of_order_queue. */ skb_rbtree_purge(&tp->out_of_order_queue); -- cgit From 46c2fa39877ed70415ee2b1acfb9129e956f6de4 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 20 Apr 2017 14:45:47 -0700 Subject: net/tcp_fastopen: Add snmp counter for blackhole detection This counter records the number of times the firewall blackhole issue is detected and active TFO is disabled. Signed-off-by: Wei Wang Acked-by: Yuchung Cheng Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/tcp.h | 2 +- include/uapi/linux/snmp.h | 1 + net/ipv4/proc.c | 1 + net/ipv4/tcp_fastopen.c | 5 +++-- net/ipv4/tcp_input.c | 4 ++-- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index c1abc2abbdcb..da28bef1d82b 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1507,7 +1507,7 @@ struct tcp_fastopen_context { }; extern unsigned int sysctl_tcp_fastopen_blackhole_timeout; -void tcp_fastopen_active_disable(void); +void tcp_fastopen_active_disable(struct sock *sk); bool tcp_fastopen_active_should_disable(struct sock *sk); void tcp_fastopen_active_disable_ofo_check(struct sock *sk); void tcp_fastopen_active_timeout_reset(void); diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index cec0e171d20c..95cffcb21dfd 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -259,6 +259,7 @@ enum LINUX_MIB_TCPFASTOPENPASSIVEFAIL, /* TCPFastOpenPassiveFail */ LINUX_MIB_TCPFASTOPENLISTENOVERFLOW, /* TCPFastOpenListenOverflow */ LINUX_MIB_TCPFASTOPENCOOKIEREQD, /* TCPFastOpenCookieReqd */ + LINUX_MIB_TCPFASTOPENBLACKHOLE, /* TCPFastOpenBlackholeDetect */ LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES, /* TCPSpuriousRtxHostQueues */ LINUX_MIB_BUSYPOLLRXPACKETS, /* BusyPollRxPackets */ LINUX_MIB_TCPAUTOCORKING, /* TCPAutoCorking */ diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 4ccbf464d1ac..fa44e752a9a3 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -281,6 +281,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPFastOpenPassiveFail", LINUX_MIB_TCPFASTOPENPASSIVEFAIL), SNMP_MIB_ITEM("TCPFastOpenListenOverflow", LINUX_MIB_TCPFASTOPENLISTENOVERFLOW), SNMP_MIB_ITEM("TCPFastOpenCookieReqd", LINUX_MIB_TCPFASTOPENCOOKIEREQD), + SNMP_MIB_ITEM("TCPFastOpenBlackhole", LINUX_MIB_TCPFASTOPENBLACKHOLE), SNMP_MIB_ITEM("TCPSpuriousRtxHostQueues", LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES), SNMP_MIB_ITEM("BusyPollRxPackets", LINUX_MIB_BUSYPOLLRXPACKETS), SNMP_MIB_ITEM("TCPAutoCorking", LINUX_MIB_TCPAUTOCORKING), diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index ff2d30ffc6f3..4af82b914dd4 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -410,10 +410,11 @@ static unsigned long tfo_active_disable_stamp __read_mostly; /* Disable active TFO and record current jiffies and * tfo_active_disable_times */ -void tcp_fastopen_active_disable(void) +void tcp_fastopen_active_disable(struct sock *sk) { atomic_inc(&tfo_active_disable_times); tfo_active_disable_stamp = jiffies; + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPFASTOPENBLACKHOLE); } /* Reset tfo_active_disable_times to 0 */ @@ -469,7 +470,7 @@ void tcp_fastopen_active_disable_ofo_check(struct sock *sk) if (p && !rb_next(p)) { skb = rb_entry(p, struct sk_buff, rbnode); if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) { - tcp_fastopen_active_disable(); + tcp_fastopen_active_disable(sk); return; } } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 9f342a67dc74..5af2f04f8859 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5307,7 +5307,7 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb, */ if (tp->syn_fastopen && !tp->data_segs_in && sk->sk_state == TCP_ESTABLISHED) - tcp_fastopen_active_disable(); + tcp_fastopen_active_disable(sk); tcp_send_challenge_ack(sk, skb); } goto discard; @@ -6061,7 +6061,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt)) { /* Receive out of order FIN after close() */ if (tp->syn_fastopen && th->fin) - tcp_fastopen_active_disable(); + tcp_fastopen_active_disable(sk); tcp_done(sk); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONDATA); return 1; -- cgit From 59450f8d83cb6743178c7996a6c6fc78ff3e6db9 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 20 Apr 2017 14:45:48 -0700 Subject: net/tcp_fastopen: Remove mss check in tcp_write_timeout() Christoph Paasch from Apple found another firewall issue for TFO: After successful 3WHS using TFO, server and client starts to exchange data. Afterwards, a 10s idle time occurs on this connection. After that, firewall starts to drop every packet on this connection. The fix for this issue is to extend existing firewall blackhole detection logic in tcp_write_timeout() by removing the mss check. Signed-off-by: Wei Wang Acked-by: Yuchung Cheng Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_timer.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index b2ab411c6d37..14672543cf0b 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -201,11 +201,10 @@ static int tcp_write_timeout(struct sock *sk) if (retransmits_timed_out(sk, net->ipv4.sysctl_tcp_retries1, 0, 0)) { /* Some middle-boxes may black-hole Fast Open _after_ * the handshake. Therefore we conservatively disable - * Fast Open on this path on recurring timeouts with - * few or zero bytes acked after Fast Open. + * Fast Open on this path on recurring timeouts after + * successful Fast Open. */ - if (tp->syn_data_acked && - tp->bytes_acked <= tp->rx_opt.mss_clamp) { + if (tp->syn_data_acked) { tcp_fastopen_cache_set(sk, 0, NULL, true, 0); if (icsk->icsk_retransmits == net->ipv4.sysctl_tcp_retries1) NET_INC_STATS(sock_net(sk), -- cgit From 029c1ecbb2429cf08c7bd2de81e929f81feea914 Mon Sep 17 00:00:00 2001 From: Benjamin LaHaise Date: Sat, 22 Apr 2017 16:52:46 -0400 Subject: flow_dissector: add mpls support (v2) Add support for parsing MPLS flows to the flow dissector in preparation for adding MPLS match support to cls_flower. Signed-off-by: Benjamin LaHaise Signed-off-by: Benjamin LaHaise Reviewed-by: Jakub Kicinski Cc: "David S. Miller" Cc: Simon Horman Cc: Jamal Hadi Salim Cc: Cong Wang Cc: Jiri Pirko Cc: Eric Dumazet Cc: Hadar Hen Zion Cc: Gao Feng Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/mpls.h | 5 +++++ include/net/flow_dissector.h | 8 ++++++++ net/core/flow_dissector.c | 25 ++++++++++++++++++++++--- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/include/linux/mpls.h b/include/linux/mpls.h index 9999145bc190..384fb22b6c43 100644 --- a/include/linux/mpls.h +++ b/include/linux/mpls.h @@ -3,4 +3,9 @@ #include +#define MPLS_TTL_MASK (MPLS_LS_TTL_MASK >> MPLS_LS_TTL_SHIFT) +#define MPLS_BOS_MASK (MPLS_LS_S_MASK >> MPLS_LS_S_SHIFT) +#define MPLS_TC_MASK (MPLS_LS_TC_MASK >> MPLS_LS_TC_SHIFT) +#define MPLS_LABEL_MASK (MPLS_LS_LABEL_MASK >> MPLS_LS_LABEL_SHIFT) + #endif /* _LINUX_MPLS_H */ diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h index ac9703018a3a..8d21d448daa9 100644 --- a/include/net/flow_dissector.h +++ b/include/net/flow_dissector.h @@ -41,6 +41,13 @@ struct flow_dissector_key_vlan { u16 padding; }; +struct flow_dissector_key_mpls { + u32 mpls_ttl:8, + mpls_bos:1, + mpls_tc:3, + mpls_label:20; +}; + struct flow_dissector_key_keyid { __be32 keyid; }; @@ -169,6 +176,7 @@ enum flow_dissector_key_id { FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, /* struct flow_dissector_key_ipv6_addrs */ FLOW_DISSECTOR_KEY_ENC_CONTROL, /* struct flow_dissector_key_control */ FLOW_DISSECTOR_KEY_ENC_PORTS, /* struct flow_dissector_key_ports */ + FLOW_DISSECTOR_KEY_MPLS, /* struct flow_dissector_key_mpls */ FLOW_DISSECTOR_KEY_MAX, }; diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index c9cf425303f8..28d94bce4df8 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -126,9 +126,11 @@ __skb_flow_dissect_mpls(const struct sk_buff *skb, { struct flow_dissector_key_keyid *key_keyid; struct mpls_label *hdr, _hdr[2]; + u32 entry, label; if (!dissector_uses_key(flow_dissector, - FLOW_DISSECTOR_KEY_MPLS_ENTROPY)) + FLOW_DISSECTOR_KEY_MPLS_ENTROPY) && + !dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_MPLS)) return FLOW_DISSECT_RET_OUT_GOOD; hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, @@ -136,8 +138,25 @@ __skb_flow_dissect_mpls(const struct sk_buff *skb, if (!hdr) return FLOW_DISSECT_RET_OUT_BAD; - if ((ntohl(hdr[0].entry) & MPLS_LS_LABEL_MASK) >> - MPLS_LS_LABEL_SHIFT == MPLS_LABEL_ENTROPY) { + entry = ntohl(hdr[0].entry); + label = (entry & MPLS_LS_LABEL_MASK) >> MPLS_LS_LABEL_SHIFT; + + if (dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_MPLS)) { + struct flow_dissector_key_mpls *key_mpls; + + key_mpls = skb_flow_dissector_target(flow_dissector, + FLOW_DISSECTOR_KEY_MPLS, + target_container); + key_mpls->mpls_label = label; + key_mpls->mpls_ttl = (entry & MPLS_LS_TTL_MASK) + >> MPLS_LS_TTL_SHIFT; + key_mpls->mpls_tc = (entry & MPLS_LS_TC_MASK) + >> MPLS_LS_TC_SHIFT; + key_mpls->mpls_bos = (entry & MPLS_LS_S_MASK) + >> MPLS_LS_S_SHIFT; + } + + if (label == MPLS_LABEL_ENTROPY) { key_keyid = skb_flow_dissector_target(flow_dissector, FLOW_DISSECTOR_KEY_MPLS_ENTROPY, target_container); -- cgit From a577d8f793ff2fd514915686079e3c09bcf0df11 Mon Sep 17 00:00:00 2001 From: Benjamin LaHaise Date: Sat, 22 Apr 2017 16:52:47 -0400 Subject: cls_flower: add support for matching MPLS fields (v2) Add support to the tc flower classifier to match based on fields in MPLS labels (TTL, Bottom of Stack, TC field, Label). Signed-off-by: Benjamin LaHaise Signed-off-by: Benjamin LaHaise Reviewed-by: Jakub Kicinski Cc: "David S. Miller" Cc: Simon Horman Cc: Jamal Hadi Salim Cc: Cong Wang Cc: Jiri Pirko Cc: Eric Dumazet Cc: Hadar Hen Zion Cc: Gao Feng Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/uapi/linux/pkt_cls.h | 5 +++ net/sched/cls_flower.c | 74 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index 7a69f2a4ca0c..f1129e383b2a 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -432,6 +432,11 @@ enum { TCA_FLOWER_KEY_ARP_THA, /* ETH_ALEN */ TCA_FLOWER_KEY_ARP_THA_MASK, /* ETH_ALEN */ + TCA_FLOWER_KEY_MPLS_TTL, /* u8 - 8 bits */ + TCA_FLOWER_KEY_MPLS_BOS, /* u8 - 1 bit */ + TCA_FLOWER_KEY_MPLS_TC, /* u8 - 3 bits */ + TCA_FLOWER_KEY_MPLS_LABEL, /* be32 - 20 bits */ + __TCA_FLOWER_MAX, }; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 31ee3404aeb4..3ecf07666df3 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,7 @@ struct fl_flow_key { struct flow_dissector_key_ipv6_addrs enc_ipv6; }; struct flow_dissector_key_ports enc_tp; + struct flow_dissector_key_mpls mpls; } __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */ struct fl_flow_mask_range { @@ -418,6 +420,10 @@ static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { [TCA_FLOWER_KEY_ARP_SHA_MASK] = { .len = ETH_ALEN }, [TCA_FLOWER_KEY_ARP_THA] = { .len = ETH_ALEN }, [TCA_FLOWER_KEY_ARP_THA_MASK] = { .len = ETH_ALEN }, + [TCA_FLOWER_KEY_MPLS_TTL] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_MPLS_BOS] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_MPLS_TC] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_MPLS_LABEL] = { .type = NLA_U32 }, }; static void fl_set_key_val(struct nlattr **tb, @@ -433,6 +439,31 @@ static void fl_set_key_val(struct nlattr **tb, memcpy(mask, nla_data(tb[mask_type]), len); } +static void fl_set_key_mpls(struct nlattr **tb, + struct flow_dissector_key_mpls *key_val, + struct flow_dissector_key_mpls *key_mask) +{ + if (tb[TCA_FLOWER_KEY_MPLS_TTL]) { + key_val->mpls_ttl = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TTL]); + key_mask->mpls_ttl = MPLS_TTL_MASK; + } + if (tb[TCA_FLOWER_KEY_MPLS_BOS]) { + key_val->mpls_bos = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_BOS]); + key_mask->mpls_bos = MPLS_BOS_MASK; + } + if (tb[TCA_FLOWER_KEY_MPLS_TC]) { + key_val->mpls_tc = + nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TC]) & MPLS_TC_MASK; + key_mask->mpls_tc = MPLS_TC_MASK; + } + if (tb[TCA_FLOWER_KEY_MPLS_LABEL]) { + key_val->mpls_label = + nla_get_u32(tb[TCA_FLOWER_KEY_MPLS_LABEL]) & + MPLS_LABEL_MASK; + key_mask->mpls_label = MPLS_LABEL_MASK; + } +} + static void fl_set_key_vlan(struct nlattr **tb, struct flow_dissector_key_vlan *key_val, struct flow_dissector_key_vlan *key_mask) @@ -589,6 +620,9 @@ static int fl_set_key(struct net *net, struct nlattr **tb, &mask->icmp.code, TCA_FLOWER_KEY_ICMPV6_CODE_MASK, sizeof(key->icmp.code)); + } else if (key->basic.n_proto == htons(ETH_P_MPLS_UC) || + key->basic.n_proto == htons(ETH_P_MPLS_MC)) { + fl_set_key_mpls(tb, &key->mpls, &mask->mpls); } else if (key->basic.n_proto == htons(ETH_P_ARP) || key->basic.n_proto == htons(ETH_P_RARP)) { fl_set_key_val(tb, &key->arp.sip, TCA_FLOWER_KEY_ARP_SIP, @@ -724,6 +758,8 @@ static void fl_init_dissector(struct cls_fl_head *head, FLOW_DISSECTOR_KEY_ICMP, icmp); FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, FLOW_DISSECTOR_KEY_ARP, arp); + FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, + FLOW_DISSECTOR_KEY_MPLS, mpls); FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, FLOW_DISSECTOR_KEY_VLAN, vlan); FL_KEY_SET_IF_MASKED(&mask->key, keys, cnt, @@ -991,6 +1027,41 @@ static int fl_dump_key_val(struct sk_buff *skb, return 0; } +static int fl_dump_key_mpls(struct sk_buff *skb, + struct flow_dissector_key_mpls *mpls_key, + struct flow_dissector_key_mpls *mpls_mask) +{ + int err; + + if (!memchr_inv(mpls_mask, 0, sizeof(*mpls_mask))) + return 0; + if (mpls_mask->mpls_ttl) { + err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_TTL, + mpls_key->mpls_ttl); + if (err) + return err; + } + if (mpls_mask->mpls_tc) { + err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_TC, + mpls_key->mpls_tc); + if (err) + return err; + } + if (mpls_mask->mpls_label) { + err = nla_put_u32(skb, TCA_FLOWER_KEY_MPLS_LABEL, + mpls_key->mpls_label); + if (err) + return err; + } + if (mpls_mask->mpls_bos) { + err = nla_put_u8(skb, TCA_FLOWER_KEY_MPLS_BOS, + mpls_key->mpls_bos); + if (err) + return err; + } + return 0; +} + static int fl_dump_key_vlan(struct sk_buff *skb, struct flow_dissector_key_vlan *vlan_key, struct flow_dissector_key_vlan *vlan_mask) @@ -1096,6 +1167,9 @@ static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, sizeof(key->basic.n_proto))) goto nla_put_failure; + if (fl_dump_key_mpls(skb, &key->mpls, &mask->mpls)) + goto nla_put_failure; + if (fl_dump_key_vlan(skb, &key->vlan, &mask->vlan)) goto nla_put_failure; -- cgit From 5cd4fbeab21b89e08df4590b2020f2e5f60c0d19 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 22 Apr 2017 20:17:52 -0700 Subject: nfp: make use of the DMA_ATTR_SKIP_CPU_SYNC attr DMA unmap may destroy changes CPU made to the buffer. To make XDP run correctly on non-x86 platforms we should use the DMA_ATTR_SKIP_CPU_SYNC attribute. Thanks to using the attribute we can now push the sync operation to the common code path from XDP handler. A little bit of variable name reshuffling is required to bring the code back to readable state. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_common.c | 53 ++++++++++++++-------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index e2197160e4dc..f1128d12cd24 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -87,16 +87,31 @@ void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver, static dma_addr_t nfp_net_dma_map_rx(struct nfp_net_dp *dp, void *frag) { - return dma_map_single(dp->dev, frag + NFP_NET_RX_BUF_HEADROOM, - dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, - dp->rx_dma_dir); + return dma_map_single_attrs(dp->dev, frag + NFP_NET_RX_BUF_HEADROOM, + dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, + dp->rx_dma_dir, DMA_ATTR_SKIP_CPU_SYNC); +} + +static void +nfp_net_dma_sync_dev_rx(const struct nfp_net_dp *dp, dma_addr_t dma_addr) +{ + dma_sync_single_for_device(dp->dev, dma_addr, + dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, + dp->rx_dma_dir); } static void nfp_net_dma_unmap_rx(struct nfp_net_dp *dp, dma_addr_t dma_addr) { - dma_unmap_single(dp->dev, dma_addr, - dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, - dp->rx_dma_dir); + dma_unmap_single_attrs(dp->dev, dma_addr, + dp->fl_bufsz - NFP_NET_RX_BUF_NON_DATA, + dp->rx_dma_dir, DMA_ATTR_SKIP_CPU_SYNC); +} + +static void nfp_net_dma_sync_cpu_rx(struct nfp_net_dp *dp, dma_addr_t dma_addr, + unsigned int len) +{ + dma_sync_single_for_cpu(dp->dev, dma_addr - NFP_NET_RX_BUF_HEADROOM, + len, dp->rx_dma_dir); } /* Firmware reconfig @@ -1208,6 +1223,8 @@ static void nfp_net_rx_give_one(const struct nfp_net_dp *dp, wr_idx = rx_ring->wr_p & (rx_ring->cnt - 1); + nfp_net_dma_sync_dev_rx(dp, dma_addr); + /* Stash SKB and DMA address away */ rx_ring->rxbufs[wr_idx].frag = frag; rx_ring->rxbufs[wr_idx].dma_addr = dma_addr; @@ -1569,7 +1586,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) tx_ring = r_vec->xdp_ring; while (pkts_polled < budget) { - unsigned int meta_len, data_len, data_off, pkt_len; + unsigned int meta_len, data_len, meta_off, pkt_len, pkt_off; u8 meta_prepend[NFP_NET_MAX_PREPEND]; struct nfp_net_rx_buf *rxbuf; struct nfp_net_rx_desc *rxd; @@ -1608,11 +1625,12 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) data_len = le16_to_cpu(rxd->rxd.data_len); pkt_len = data_len - meta_len; + pkt_off = NFP_NET_RX_BUF_HEADROOM + dp->rx_dma_off; if (dp->rx_offset == NFP_NET_CFG_RX_OFFSET_DYNAMIC) - data_off = NFP_NET_RX_BUF_HEADROOM + meta_len; + pkt_off += meta_len; else - data_off = NFP_NET_RX_BUF_HEADROOM + dp->rx_offset; - data_off += dp->rx_dma_off; + pkt_off += dp->rx_offset; + meta_off = pkt_off - meta_len; /* Stats update */ u64_stats_update_begin(&r_vec->rx_sync); @@ -1621,7 +1639,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) u64_stats_update_end(&r_vec->rx_sync); /* Pointer to start of metadata */ - meta = rxbuf->frag + data_off - meta_len; + meta = rxbuf->frag + meta_off; if (unlikely(meta_len > NFP_NET_MAX_PREPEND || (dp->rx_offset && meta_len > dp->rx_offset))) { @@ -1631,6 +1649,9 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) continue; } + nfp_net_dma_sync_cpu_rx(dp, rxbuf->dma_addr + meta_off, + data_len); + if (xdp_prog && !(rxd->rxd.flags & PCIE_DESC_RX_BPF && dp->bpf_offload_xdp)) { unsigned int dma_off; @@ -1638,10 +1659,6 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) int act; hard_start = rxbuf->frag + NFP_NET_RX_BUF_HEADROOM; - dma_off = data_off - NFP_NET_RX_BUF_HEADROOM; - dma_sync_single_for_cpu(dp->dev, rxbuf->dma_addr, - dma_off + pkt_len, - DMA_BIDIRECTIONAL); /* Move prepend out of the way */ if (xdp_prog->xdp_adjust_head) { @@ -1650,12 +1667,12 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) } act = nfp_net_run_xdp(xdp_prog, rxbuf->frag, hard_start, - &data_off, &pkt_len); + &pkt_off, &pkt_len); switch (act) { case XDP_PASS: break; case XDP_TX: - dma_off = data_off - NFP_NET_RX_BUF_HEADROOM; + dma_off = pkt_off - NFP_NET_RX_BUF_HEADROOM; if (unlikely(!nfp_net_tx_xdp_buf(dp, rx_ring, tx_ring, rxbuf, dma_off, @@ -1689,7 +1706,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) nfp_net_rx_give_one(dp, rx_ring, new_frag, new_dma_addr); - skb_reserve(skb, data_off); + skb_reserve(skb, pkt_off); skb_put(skb, pkt_len); if (!dp->chained_metadata_format) { -- cgit From e524a6a9cdc979a45da7532645786469a48de2e3 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 22 Apr 2017 20:17:53 -0700 Subject: nfp: parse metadata prepend before XDP runs Calling memcpy to shift metadata out of the way for XDP to run seems like an overkill. The most common metadata contents are 8 bytes containing type and flow hash. Simply parse the metadata before we run XDP. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 6 ++ .../net/ethernet/netronome/nfp/nfp_net_common.c | 67 +++++++++++----------- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 052db9208fbb..8302a2d688da 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -284,6 +284,12 @@ struct nfp_net_rx_desc { #define NFP_NET_META_FIELD_MASK GENMASK(NFP_NET_META_FIELD_SIZE - 1, 0) +struct nfp_meta_parsed { + u32 hash_type; + u32 hash; + u32 mark; +}; + struct nfp_net_rx_hash { __be32 hash_type; __be32 hash; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index f1128d12cd24..3285053bece0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1402,8 +1402,9 @@ static void nfp_net_rx_csum(struct nfp_net_dp *dp, } } -static void nfp_net_set_hash(struct net_device *netdev, struct sk_buff *skb, - unsigned int type, __be32 *hash) +static void +nfp_net_set_hash(struct net_device *netdev, struct nfp_meta_parsed *meta, + unsigned int type, __be32 *hash) { if (!(netdev->features & NETIF_F_RXHASH)) return; @@ -1412,16 +1413,18 @@ static void nfp_net_set_hash(struct net_device *netdev, struct sk_buff *skb, case NFP_NET_RSS_IPV4: case NFP_NET_RSS_IPV6: case NFP_NET_RSS_IPV6_EX: - skb_set_hash(skb, get_unaligned_be32(hash), PKT_HASH_TYPE_L3); + meta->hash_type = PKT_HASH_TYPE_L3; break; default: - skb_set_hash(skb, get_unaligned_be32(hash), PKT_HASH_TYPE_L4); + meta->hash_type = PKT_HASH_TYPE_L4; break; } + + meta->hash = get_unaligned_be32(hash); } static void -nfp_net_set_hash_desc(struct net_device *netdev, struct sk_buff *skb, +nfp_net_set_hash_desc(struct net_device *netdev, struct nfp_meta_parsed *meta, void *data, struct nfp_net_rx_desc *rxd) { struct nfp_net_rx_hash *rx_hash = data; @@ -1429,12 +1432,12 @@ nfp_net_set_hash_desc(struct net_device *netdev, struct sk_buff *skb, if (!(rxd->rxd.flags & PCIE_DESC_RX_RSS)) return; - nfp_net_set_hash(netdev, skb, get_unaligned_be32(&rx_hash->hash_type), + nfp_net_set_hash(netdev, meta, get_unaligned_be32(&rx_hash->hash_type), &rx_hash->hash); } static void * -nfp_net_parse_meta(struct net_device *netdev, struct sk_buff *skb, +nfp_net_parse_meta(struct net_device *netdev, struct nfp_meta_parsed *meta, void *data, int meta_len) { u32 meta_info; @@ -1446,13 +1449,13 @@ nfp_net_parse_meta(struct net_device *netdev, struct sk_buff *skb, switch (meta_info & NFP_NET_META_FIELD_MASK) { case NFP_NET_META_HASH: meta_info >>= NFP_NET_META_FIELD_SIZE; - nfp_net_set_hash(netdev, skb, + nfp_net_set_hash(netdev, meta, meta_info & NFP_NET_META_FIELD_MASK, (__be32 *)data); data += 4; break; case NFP_NET_META_MARK: - skb->mark = get_unaligned_be32(data); + meta->mark = get_unaligned_be32(data); data += 4; break; default: @@ -1587,12 +1590,11 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) while (pkts_polled < budget) { unsigned int meta_len, data_len, meta_off, pkt_len, pkt_off; - u8 meta_prepend[NFP_NET_MAX_PREPEND]; struct nfp_net_rx_buf *rxbuf; struct nfp_net_rx_desc *rxd; + struct nfp_meta_parsed meta; dma_addr_t new_dma_addr; void *new_frag; - u8 *meta; idx = rx_ring->rd_p & (rx_ring->cnt - 1); @@ -1605,6 +1607,8 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) */ dma_rmb(); + memset(&meta, 0, sizeof(meta)); + rx_ring->rd_p++; pkts_polled++; @@ -1638,9 +1642,6 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) r_vec->rx_bytes += pkt_len; u64_stats_update_end(&r_vec->rx_sync); - /* Pointer to start of metadata */ - meta = rxbuf->frag + meta_off; - if (unlikely(meta_len > NFP_NET_MAX_PREPEND || (dp->rx_offset && meta_len > dp->rx_offset))) { nn_dp_warn(dp, "oversized RX packet metadata %u\n", @@ -1652,6 +1653,23 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) nfp_net_dma_sync_cpu_rx(dp, rxbuf->dma_addr + meta_off, data_len); + if (!dp->chained_metadata_format) { + nfp_net_set_hash_desc(dp->netdev, &meta, + rxbuf->frag + meta_off, rxd); + } else if (meta_len) { + void *end; + + end = nfp_net_parse_meta(dp->netdev, &meta, + rxbuf->frag + meta_off, + meta_len); + if (unlikely(end != rxbuf->frag + pkt_off)) { + nn_dp_warn(dp, "invalid RX packet metadata\n"); + nfp_net_rx_drop(dp, r_vec, rx_ring, rxbuf, + NULL); + continue; + } + } + if (xdp_prog && !(rxd->rxd.flags & PCIE_DESC_RX_BPF && dp->bpf_offload_xdp)) { unsigned int dma_off; @@ -1660,12 +1678,6 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) hard_start = rxbuf->frag + NFP_NET_RX_BUF_HEADROOM; - /* Move prepend out of the way */ - if (xdp_prog->xdp_adjust_head) { - memcpy(meta_prepend, meta, meta_len); - meta = meta_prepend; - } - act = nfp_net_run_xdp(xdp_prog, rxbuf->frag, hard_start, &pkt_off, &pkt_len); switch (act) { @@ -1709,19 +1721,8 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget) skb_reserve(skb, pkt_off); skb_put(skb, pkt_len); - if (!dp->chained_metadata_format) { - nfp_net_set_hash_desc(dp->netdev, skb, meta, rxd); - } else if (meta_len) { - void *end; - - end = nfp_net_parse_meta(dp->netdev, skb, meta, - meta_len); - if (unlikely(end != meta + meta_len)) { - nn_dp_warn(dp, "invalid RX packet metadata\n"); - nfp_net_rx_drop(dp, r_vec, rx_ring, NULL, skb); - continue; - } - } + skb->mark = meta.mark; + skb_set_hash(skb, meta.hash, meta.hash_type); skb_record_rx_queue(skb, rx_ring->idx); skb->protocol = eth_type_trans(skb, dp->netdev); -- cgit From 010e2f9cc5b0a9cb730e3238b06dbad54a577816 Mon Sep 17 00:00:00 2001 From: David Brunecz Date: Sat, 22 Apr 2017 20:17:54 -0700 Subject: nfp: add NSP routine to get static information Retrieve identifying information from the NSP. For now it only contains versions of firmware subcomponents. Signed-off-by: David Brunecz Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/Makefile | 1 + drivers/net/ethernet/netronome/nfp/nfp_main.c | 7 ++ drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h | 1 + .../net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c | 7 ++ .../net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h | 24 ++++++ .../ethernet/netronome/nfp/nfpcore/nfp_nsp_cmds.c | 89 ++++++++++++++++++++++ 6 files changed, 129 insertions(+) create mode 100644 drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_cmds.c diff --git a/drivers/net/ethernet/netronome/nfp/Makefile b/drivers/net/ethernet/netronome/nfp/Makefile index 4a5d13ef92a4..4b15f0f496aa 100644 --- a/drivers/net/ethernet/netronome/nfp/Makefile +++ b/drivers/net/ethernet/netronome/nfp/Makefile @@ -9,6 +9,7 @@ nfp-objs := \ nfpcore/nfp_mutex.o \ nfpcore/nfp_nffw.o \ nfpcore/nfp_nsp.o \ + nfpcore/nfp_nsp_cmds.o \ nfpcore/nfp_nsp_eth.o \ nfpcore/nfp_resource.o \ nfpcore/nfp_rtsym.o \ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index bea2a1a6c211..dde35dae35c5 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -253,6 +253,7 @@ exit_release_fw: static int nfp_nsp_init(struct pci_dev *pdev, struct nfp_pf *pf) { + struct nfp_nsp_identify *nspi; struct nfp_nsp *nsp; int err; @@ -269,6 +270,12 @@ static int nfp_nsp_init(struct pci_dev *pdev, struct nfp_pf *pf) pf->eth_tbl = __nfp_eth_read_ports(pf->cpp, nsp); + nspi = __nfp_nsp_identify(nsp); + if (nspi) { + dev_info(&pdev->dev, "BSP: %s\n", nspi->version); + kfree(nspi); + } + err = nfp_fw_load(pdev, pf, nsp); if (err < 0) { kfree(pf->eth_tbl); diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h index 8afef7593f13..4df2ce261b3f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h @@ -63,6 +63,7 @@ void nfp_nsp_config_clear_state(struct nfp_nsp *state); int nfp_nsp_read_eth_table(struct nfp_nsp *state, void *buf, unsigned int size); int nfp_nsp_write_eth_table(struct nfp_nsp *state, const void *buf, unsigned int size); +int nfp_nsp_read_identify(struct nfp_nsp *state, void *buf, unsigned int size); /* Implemented in nfp_resource.c */ diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 4635f42e15b0..61797c98f5fe 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -93,6 +93,7 @@ enum nfp_nsp_cmd { SPCODE_FW_LOAD = 6, /* Load fw from buffer, len in option */ SPCODE_ETH_RESCAN = 7, /* Rescan ETHs, write ETH_TABLE to buf */ SPCODE_ETH_CONTROL = 8, /* Update media config from buffer */ + SPCODE_NSP_IDENTIFY = 13, /* Read NSP version */ __MAX_SPCODE, }; @@ -493,3 +494,9 @@ int nfp_nsp_write_eth_table(struct nfp_nsp *state, return nfp_nsp_command_buf(state, SPCODE_ETH_CONTROL, size, buf, size, NULL, 0); } + +int nfp_nsp_read_identify(struct nfp_nsp *state, void *buf, unsigned int size) +{ + return nfp_nsp_command_buf(state, SPCODE_NSP_IDENTIFY, size, NULL, 0, + buf, size); +} diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h index 7d34ff145fd7..36b21e4dc56d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h @@ -147,4 +147,28 @@ int __nfp_eth_set_aneg(struct nfp_nsp *nsp, enum nfp_eth_aneg mode); int __nfp_eth_set_speed(struct nfp_nsp *nsp, unsigned int speed); int __nfp_eth_set_split(struct nfp_nsp *nsp, unsigned int lanes); +/** + * struct nfp_nsp_identify - NSP static information + * @version: opaque version string + * @flags: version flags + * @br_primary: branch id of primary bootloader + * @br_secondary: branch id of secondary bootloader + * @br_nsp: branch id of NSP + * @primary: version of primarary bootloader + * @secondary: version id of secondary bootloader + * @nsp: version id of NSP + */ +struct nfp_nsp_identify { + char version[40]; + u8 flags; + u8 br_primary; + u8 br_secondary; + u8 br_nsp; + u16 primary; + u16 secondary; + u16 nsp; +}; + +struct nfp_nsp_identify *__nfp_nsp_identify(struct nfp_nsp *nsp); + #endif diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_cmds.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_cmds.c new file mode 100644 index 000000000000..e7a263de3731 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_cmds.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "nfp.h" +#include "nfp_nsp.h" + +struct nsp_identify { + u8 version[40]; + u8 flags; + u8 br_primary; + u8 br_secondary; + u8 br_nsp; + __le16 primary; + __le16 secondary; + __le16 nsp; + __le16 reserved; +}; + +struct nfp_nsp_identify *__nfp_nsp_identify(struct nfp_nsp *nsp) +{ + struct nfp_nsp_identify *nspi = NULL; + struct nsp_identify *ni; + int ret; + + if (nfp_nsp_get_abi_ver_minor(nsp) < 15) + return NULL; + + ni = kzalloc(sizeof(*ni), GFP_KERNEL); + if (!ni) + return NULL; + + ret = nfp_nsp_read_identify(nsp, ni, sizeof(*ni)); + if (ret < 0) { + nfp_err(nfp_nsp_cpp(nsp), "reading bsp version failed %d\n", + ret); + goto exit_free; + } + + nspi = kzalloc(sizeof(*nspi), GFP_KERNEL); + if (!nspi) + goto exit_free; + + memcpy(nspi->version, ni->version, sizeof(nspi->version)); + nspi->version[sizeof(nspi->version) - 1] = '\0'; + nspi->flags = ni->flags; + nspi->br_primary = ni->br_primary; + nspi->br_secondary = ni->br_secondary; + nspi->br_nsp = ni->br_nsp; + nspi->primary = le16_to_cpu(ni->primary); + nspi->secondary = le16_to_cpu(ni->secondary); + nspi->nsp = le16_to_cpu(ni->nsp); + +exit_free: + kfree(ni); + return nspi; +} -- cgit From ee200a7377fb6ce5ea6d631af0905883f0dca6d2 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 22 Apr 2017 20:17:55 -0700 Subject: nfp: fix free list buffer size reporting XDP headroom should not be included in free list buffer size. Fixes: 6fe0c3b43804 ("nfp: add support for xdp_adjust_head()") Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 3285053bece0..8a9b74305493 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2165,7 +2165,7 @@ nfp_net_tx_ring_hw_cfg_write(struct nfp_net *nn, */ static int nfp_net_set_config_and_enable(struct nfp_net *nn) { - u32 new_ctrl, update = 0; + u32 bufsz, new_ctrl, update = 0; unsigned int r; int err; @@ -2199,8 +2199,9 @@ static int nfp_net_set_config_and_enable(struct nfp_net *nn) nfp_net_write_mac_addr(nn); nn_writel(nn, NFP_NET_CFG_MTU, nn->dp.netdev->mtu); - nn_writel(nn, NFP_NET_CFG_FLBUFSZ, - nn->dp.fl_bufsz - NFP_NET_RX_BUF_NON_DATA); + + bufsz = nn->dp.fl_bufsz - nn->dp.rx_dma_off - NFP_NET_RX_BUF_NON_DATA; + nn_writel(nn, NFP_NET_CFG_FLBUFSZ, bufsz); /* Enable device */ new_ctrl |= NFP_NET_CFG_CTRL_ENABLE; -- cgit From 90fdc561b08ce292f1d39a62f70012f150583b98 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sat, 22 Apr 2017 20:17:56 -0700 Subject: nfp: remove the refresh of all ports optimization The code refreshing the eth port state was trying to update state of all ports of the card. Unfortunately to safely walk the port list we would have to hold the port lock, which we can't due to lock ordering constraints against rtnl. Make the per-port sync refresh and async refresh of all ports completely separate routines. Fixes: 172f638c93dd ("nfp: add port state refresh") Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 3 +- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 13 +++-- drivers/net/ethernet/netronome/nfp/nfp_net_main.c | 67 +++++++++++++++------- 3 files changed, 58 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 8302a2d688da..8f20fdef0754 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -819,7 +819,8 @@ struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn); int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new); bool nfp_net_link_changed_read_clear(struct nfp_net *nn); -void nfp_net_refresh_port_config(struct nfp_net *nn); +int nfp_net_refresh_eth_port(struct nfp_net *nn); +void nfp_net_refresh_port_table(struct nfp_net *nn); #ifdef CONFIG_NFP_DEBUG void nfp_net_debugfs_create(void); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 3328041ec290..6e27d1281425 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -211,10 +211,15 @@ nfp_net_get_link_ksettings(struct net_device *netdev, return 0; /* Use link speed from ETH table if available, otherwise try the BAR */ - if (nn->eth_port && nfp_net_link_changed_read_clear(nn)) - nfp_net_refresh_port_config(nn); - /* Separate if - on FW error the port could've disappeared from table */ if (nn->eth_port) { + int err; + + if (nfp_net_link_changed_read_clear(nn)) { + err = nfp_net_refresh_eth_port(nn); + if (err) + return err; + } + cmd->base.port = nn->eth_port->port_type; cmd->base.speed = nn->eth_port->speed; cmd->base.duplex = DUPLEX_FULL; @@ -273,7 +278,7 @@ nfp_net_set_link_ksettings(struct net_device *netdev, if (err > 0) return 0; /* no change */ - nfp_net_refresh_port_config(nn); + nfp_net_refresh_port_table(nn); return err; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 4c6863a072d3..8cb87cbe1120 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -176,13 +176,13 @@ nfp_net_get_mac_addr(struct nfp_net *nn, struct nfp_cpp *cpp, unsigned int id) } static struct nfp_eth_table_port * -nfp_net_find_port(struct nfp_pf *pf, unsigned int id) +nfp_net_find_port(struct nfp_eth_table *eth_tbl, unsigned int id) { int i; - for (i = 0; pf->eth_tbl && i < pf->eth_tbl->count; i++) - if (pf->eth_tbl->ports[i].eth_index == id) - return &pf->eth_tbl->ports[i]; + for (i = 0; eth_tbl && i < eth_tbl->count; i++) + if (eth_tbl->ports[i].eth_index == id) + return ð_tbl->ports[i]; return NULL; } @@ -367,7 +367,7 @@ nfp_net_pf_alloc_netdevs(struct nfp_pf *pf, void __iomem *ctrl_bar, prev_tx_base = tgt_tx_base; prev_rx_base = tgt_rx_base; - eth_port = nfp_net_find_port(pf, i); + eth_port = nfp_net_find_port(pf->eth_tbl, i); if (eth_port && eth_port->override_changed) { nfp_warn(pf->cpp, "Config changed for port #%d, reboot required before port will be operational\n", i); } else { @@ -485,6 +485,7 @@ static void nfp_net_refresh_netdevs(struct work_struct *work) { struct nfp_pf *pf = container_of(work, struct nfp_pf, port_refresh_work); + struct nfp_eth_table *eth_table; struct nfp_net *nn, *next; mutex_lock(&pf->port_lock); @@ -493,6 +494,27 @@ static void nfp_net_refresh_netdevs(struct work_struct *work) if (list_empty(&pf->ports)) goto out; + list_for_each_entry(nn, &pf->ports, port_list) + nfp_net_link_changed_read_clear(nn); + + eth_table = nfp_eth_read_ports(pf->cpp); + if (!eth_table) { + nfp_err(pf->cpp, "Error refreshing port config!\n"); + goto out; + } + + rtnl_lock(); + list_for_each_entry(nn, &pf->ports, port_list) { + if (!nn->eth_port) + continue; + nn->eth_port = nfp_net_find_port(eth_table, + nn->eth_port->eth_index); + } + rtnl_unlock(); + + kfree(pf->eth_tbl); + pf->eth_tbl = eth_table; + list_for_each_entry_safe(nn, next, &pf->ports, port_list) { if (!nn->eth_port) { nfp_warn(pf->cpp, "Warning: port not present after reconfig\n"); @@ -517,31 +539,36 @@ out: mutex_unlock(&pf->port_lock); } -void nfp_net_refresh_port_config(struct nfp_net *nn) +void nfp_net_refresh_port_table(struct nfp_net *nn) { struct nfp_pf *pf = pci_get_drvdata(nn->pdev); - struct nfp_eth_table *old_table; - ASSERT_RTNL(); + schedule_work(&pf->port_refresh_work); +} - old_table = pf->eth_tbl; +int nfp_net_refresh_eth_port(struct nfp_net *nn) +{ + struct nfp_eth_table_port *eth_port; + struct nfp_eth_table *eth_table; - list_for_each_entry(nn, &pf->ports, port_list) - nfp_net_link_changed_read_clear(nn); + eth_table = nfp_eth_read_ports(nn->cpp); + if (!eth_table) { + nn_err(nn, "Error refreshing port state table!\n"); + return -EIO; + } - pf->eth_tbl = nfp_eth_read_ports(pf->cpp); - if (!pf->eth_tbl) { - pf->eth_tbl = old_table; - nfp_err(pf->cpp, "Error refreshing port config!\n"); - return; + eth_port = nfp_net_find_port(eth_table, nn->eth_port->eth_index); + if (!eth_port) { + nn_err(nn, "Error finding state of the port!\n"); + kfree(eth_table); + return -EIO; } - list_for_each_entry(nn, &pf->ports, port_list) - nn->eth_port = nfp_net_find_port(pf, nn->eth_port->eth_index); + memcpy(nn->eth_port, eth_port, sizeof(*eth_port)); - kfree(old_table); + kfree(eth_table); - schedule_work(&pf->port_refresh_work); + return 0; } /* -- cgit From a50fe0ffd76fbd17af36209a16caf0b8ad901fef Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sun, 23 Apr 2017 14:28:37 +0800 Subject: lwtunnel: check return value of nla_nest_start Function nla_nest_start() may return a NULL pointer on error. However, in function lwtunnel_fill_encap(), the return value of nla_nest_start() is not validated before it is used. This patch checks the return value of nla_nest_start() against NULL. Signed-off-by: Pan Bian Signed-off-by: David S. Miller --- net/core/lwtunnel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index b5888190223c..5cbed3816229 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -214,6 +214,8 @@ int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate) ret = -EOPNOTSUPP; nest = nla_nest_start(skb, RTA_ENCAP); + if (!nest) + goto nla_put_failure; rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[lwtstate->type]); if (likely(ops && ops->fill_encap)) -- cgit From 78302fd405769c9a9379e9adda119d533dce2eed Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sun, 23 Apr 2017 15:09:19 +0800 Subject: tipc: check return value of nlmsg_new Function nlmsg_new() will return a NULL pointer if there is no enough memory, and its return value should be checked before it is used. However, in function tipc_nl_node_get_monitor(), the validation of the return value of function nlmsg_new() is missed. This patch fixes the bug. Signed-off-by: Pan Bian Signed-off-by: David S. Miller --- net/tipc/node.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/tipc/node.c b/net/tipc/node.c index 01b1f077603e..aeef8011ac7d 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -2098,6 +2098,8 @@ int tipc_nl_node_get_monitor(struct sk_buff *skb, struct genl_info *info) int err; msg.skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg.skb) + return -ENOMEM; msg.portid = info->snd_portid; msg.seq = info->snd_seq; -- cgit From 2a39e7aa8a98f777f0732ca7125b6c9668791760 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sun, 23 Apr 2017 17:38:35 +0800 Subject: wan: pc300too: abort path on failure In function pc300_pci_init_one(), on the ioremap error path, function pc300_pci_remove_one() is called to free the allocated memory. However, the path is not terminated, and the freed memory will be used later, resulting in use-after-free bugs. This path fixes the bug. Signed-off-by: Pan Bian Signed-off-by: David S. Miller --- drivers/net/wan/pc300too.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wan/pc300too.c b/drivers/net/wan/pc300too.c index e1dd1ec18d64..b9b934b7713c 100644 --- a/drivers/net/wan/pc300too.c +++ b/drivers/net/wan/pc300too.c @@ -346,6 +346,7 @@ static int pc300_pci_init_one(struct pci_dev *pdev, card->rambase == NULL) { pr_err("ioremap() failed\n"); pc300_pci_remove_one(pdev); + return -ENOMEM; } /* PLX PCI 9050 workaround for local configuration register read bug */ -- cgit From 91ec701a553cb3de470fd471c6fefe3ad1125455 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sun, 23 Apr 2017 20:04:04 +0800 Subject: qlcnic: fix unchecked return value Function pci_find_ext_capability() may return 0, which is an invalid address. In function qlcnic_sriov_virtid_fn(), its return value is used without validation. This may result in invalid memory access bugs. This patch fixes the bug. Signed-off-by: Pan Bian Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index d7107055ec60..2f656f395f39 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -128,6 +128,8 @@ static int qlcnic_sriov_virtid_fn(struct qlcnic_adapter *adapter, int vf_id) return 0; pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); + if (!pos) + return 0; pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset); pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride); -- cgit From e390b55d5aefe2b51569068b2a505d19d72afbf1 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 24 Apr 2017 22:14:35 +0200 Subject: bpf: make bpf_xdp_adjust_head support mandatory Now that also the last in-tree user of the xdp_adjust_head bit has been removed, we can remove the flag from struct bpf_prog altogether. This, at the same time, also makes sure that any future driver for XDP comes with bpf_xdp_adjust_head() support right away. A rejection based on this flag would also mean that tail calls couldn't be used with such driver as per c2002f983767 ("bpf: fix checking xdp_adjust_head on tail calls") fix, thus lets not allow for it in the first place. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/filter.h | 3 +-- kernel/bpf/verifier.c | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/include/linux/filter.h b/include/linux/filter.h index 511fe910bf1d..9a7786db14fa 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -413,8 +413,7 @@ struct bpf_prog { locked:1, /* Program image locked? */ gpl_compatible:1, /* Is filter GPL compatible? */ cb_access:1, /* Is control block accessed? */ - dst_needed:1, /* Do we need dst entry? */ - xdp_adjust_head:1; /* Adjusting pkt head? */ + dst_needed:1; /* Do we need dst entry? */ kmemcheck_bitfield_end(meta); enum bpf_prog_type type; /* Type of BPF program */ u32 len; /* Number of filter blocks */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index ca15cf2b85bb..6f8b6ed690be 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3346,8 +3346,6 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env) prog->dst_needed = 1; if (insn->imm == BPF_FUNC_get_prandom_u32) bpf_user_rnd_init_once(); - if (insn->imm == BPF_FUNC_xdp_adjust_head) - prog->xdp_adjust_head = 1; if (insn->imm == BPF_FUNC_tail_call) { /* If we tail call into other programs, we * cannot make any assumptions since they can @@ -3355,7 +3353,6 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env) * the program array. */ prog->cb_access = 1; - prog->xdp_adjust_head = 1; /* mark bpf_tail_call as different opcode to avoid * conditional branch in the interpeter for every normal -- cgit From 69b6a7f743d3dac773f9a4c7b1786164ae329490 Mon Sep 17 00:00:00 2001 From: Alexander Alemayhu Date: Mon, 24 Apr 2017 15:31:06 +0200 Subject: samples/bpf: add -Wno-unknown-warning-option to clang I was initially going to remove '-Wno-address-of-packed-member' because I thought it was not supposed to be there but Daniel suggested using '-Wno-unknown-warning-option'. This silences several warnings similiar to the one below warning: unknown warning option '-Wno-address-of-packed-member' [-Wunknown-warning-option] 1 warning generated. clang -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/6.3.1/include -I./arch/x86/include -I./arch/x86/include/generated/uapi -I./arch/x86/include/generated -I./include -I./arch/x86/include/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h \ -D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \ -Wno-compare-distinct-pointer-types \ -Wno-gnu-variable-sized-type-not-at-end \ -Wno-address-of-packed-member -Wno-tautological-compare \ -O2 -emit-llvm -c samples/bpf/xdp_tx_iptunnel_kern.c -o -| llc -march=bpf -filetype=obj -o samples/bpf/xdp_tx_iptunnel_kern.o $ clang --version clang version 3.9.1 (tags/RELEASE_391/final) Target: x86_64-unknown-linux-gnu Thread model: posix InstalledDir: /usr/bin Signed-off-by: Alexander Alemayhu Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- samples/bpf/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index d42b495b0992..6c7468eb3684 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -189,4 +189,5 @@ $(obj)/%.o: $(src)/%.c -Wno-compare-distinct-pointer-types \ -Wno-gnu-variable-sized-type-not-at-end \ -Wno-address-of-packed-member -Wno-tautological-compare \ + -Wno-unknown-warning-option \ -O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@ -- cgit From 4784726f69ffa3e076d502a5885d753ccb24a82d Mon Sep 17 00:00:00 2001 From: Alexander Alemayhu Date: Mon, 24 Apr 2017 15:31:07 +0200 Subject: samples/bpf: add static to function with no prototype MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following warning samples/bpf/cookie_uid_helper_example.c: At top level: samples/bpf/cookie_uid_helper_example.c:276:6: warning: no previous prototype for ‘finish’ [-Wmissing-prototypes] void finish(int ret) ^~~~~~ HOSTLD samples/bpf/per_socket_stats_example Signed-off-by: Alexander Alemayhu Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- samples/bpf/cookie_uid_helper_example.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/bpf/cookie_uid_helper_example.c b/samples/bpf/cookie_uid_helper_example.c index ad5afedf2e70..9ce55840d61d 100644 --- a/samples/bpf/cookie_uid_helper_example.c +++ b/samples/bpf/cookie_uid_helper_example.c @@ -273,7 +273,7 @@ static int usage(void) return 1; } -void finish(int ret) +static void finish(int ret) { test_finish = true; } -- cgit From dfc5be0dc0f83e70919a8ec59c122709a30f791e Mon Sep 17 00:00:00 2001 From: Alexander Alemayhu Date: Mon, 24 Apr 2017 15:31:08 +0200 Subject: samples/bpf: check before defining offsetof Fixes the following warning samples/bpf/test_lru_dist.c:28:0: warning: "offsetof" redefined #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) In file included from ./tools/lib/bpf/bpf.h:25:0, from samples/bpf/libbpf.h:5, from samples/bpf/test_lru_dist.c:24: /usr/lib/gcc/x86_64-redhat-linux/6.3.1/include/stddef.h:417:0: note: this is the location of the previous definition #define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) Signed-off-by: Alexander Alemayhu Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- samples/bpf/test_lru_dist.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/samples/bpf/test_lru_dist.c b/samples/bpf/test_lru_dist.c index d96dc88d3b04..73c357142268 100644 --- a/samples/bpf/test_lru_dist.c +++ b/samples/bpf/test_lru_dist.c @@ -25,7 +25,9 @@ #include "bpf_util.h" #define min(a, b) ((a) < (b) ? (a) : (b)) -#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) +#ifndef offsetof +# define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) +#endif #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) -- cgit From e3a724edeec3836ed44675a6587a6db7b6b68dbe Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 24 Apr 2017 15:56:21 -0700 Subject: sparc64: Support cbcond instructions in eBPF JIT. cbcond combines a compare with a branch into a single instruction. The limitations are: 1) Only newer chips support it 2) For immediate compares we are limited to 5-bit signed immediate values 3) The branch displacement is limited to 10-bit signed 4) We cannot use it for JSET Also, cbcond (unlike all other sparc control transfers) lacks a delay slot. Currently we don't have a useful instruction we can push into the delay slot of normal branches. So using cbcond pretty much always increases code density, and is therefore a win. Signed-off-by: David S. Miller --- arch/sparc/net/bpf_jit_comp_64.c | 238 ++++++++++++++++++++++++++++++--------- 1 file changed, 184 insertions(+), 54 deletions(-) diff --git a/arch/sparc/net/bpf_jit_comp_64.c b/arch/sparc/net/bpf_jit_comp_64.c index 43bef1ceebbf..2b2f3c3335ce 100644 --- a/arch/sparc/net/bpf_jit_comp_64.c +++ b/arch/sparc/net/bpf_jit_comp_64.c @@ -18,6 +18,16 @@ static inline bool is_simm13(unsigned int value) return value + 0x1000 < 0x2000; } +static inline bool is_simm10(unsigned int value) +{ + return value + 0x200 < 0x400; +} + +static inline bool is_simm5(unsigned int value) +{ + return value + 0x10 < 0x20; +} + static void bpf_flush_icache(void *start_, void *end_) { /* Cheetah's I-cache is fully coherent. */ @@ -39,6 +49,7 @@ static void bpf_flush_icache(void *start_, void *end_) #define SEEN_MEM 4 /* use mem[] for temporary storage */ #define S13(X) ((X) & 0x1fff) +#define S5(X) ((X) & 0x1f) #define IMMED 0x00002000 #define RD(X) ((X) << 25) #define RS1(X) ((X) << 14) @@ -46,7 +57,8 @@ static void bpf_flush_icache(void *start_, void *end_) #define OP(X) ((X) << 30) #define OP2(X) ((X) << 22) #define OP3(X) ((X) << 19) -#define COND(X) ((X) << 25) +#define COND(X) (((X) & 0xf) << 25) +#define CBCOND(X) (((X) & 0x1f) << 25) #define F1(X) OP(X) #define F2(X, Y) (OP(X) | OP2(Y)) #define F3(X, Y) (OP(X) | OP3(Y)) @@ -75,10 +87,39 @@ static void bpf_flush_icache(void *start_, void *end_) #define WDISP22(X) (((X) >> 2) & 0x3fffff) #define WDISP19(X) (((X) >> 2) & 0x7ffff) +/* The 10-bit branch displacement for CBCOND is split into two fields */ +static u32 WDISP10(u32 off) +{ + u32 ret = ((off >> 2) & 0xff) << 5; + + ret |= ((off >> (2 + 8)) & 0x03) << 19; + + return ret; +} + +#define CBCONDE CBCOND(0x09) +#define CBCONDLE CBCOND(0x0a) +#define CBCONDL CBCOND(0x0b) +#define CBCONDLEU CBCOND(0x0c) +#define CBCONDCS CBCOND(0x0d) +#define CBCONDN CBCOND(0x0e) +#define CBCONDVS CBCOND(0x0f) +#define CBCONDNE CBCOND(0x19) +#define CBCONDG CBCOND(0x1a) +#define CBCONDGE CBCOND(0x1b) +#define CBCONDGU CBCOND(0x1c) +#define CBCONDCC CBCOND(0x1d) +#define CBCONDPOS CBCOND(0x1e) +#define CBCONDVC CBCOND(0x1f) + +#define CBCONDGEU CBCONDCC +#define CBCONDLU CBCONDCS + #define ANNUL (1 << 29) #define XCC (1 << 21) #define BRANCH (F2(0, 1) | XCC) +#define CBCOND_OP (F2(0, 3) | XCC) #define BA (BRANCH | CONDA) #define BG (BRANCH | CONDG) @@ -351,6 +392,22 @@ static void emit_branch(unsigned int br_opc, unsigned int from_idx, unsigned int emit(br_opc | WDISP22(off << 2), ctx); } +static void emit_cbcond(unsigned int cb_opc, unsigned int from_idx, unsigned int to_idx, + const u8 dst, const u8 src, struct jit_ctx *ctx) +{ + unsigned int off = to_idx - from_idx; + + emit(cb_opc | WDISP10(off << 2) | RS1(dst) | RS2(src), ctx); +} + +static void emit_cbcondi(unsigned int cb_opc, unsigned int from_idx, unsigned int to_idx, + const u8 dst, s32 imm, struct jit_ctx *ctx) +{ + unsigned int off = to_idx - from_idx; + + emit(cb_opc | IMMED | WDISP10(off << 2) | RS1(dst) | S5(imm), ctx); +} + #define emit_read_y(REG, CTX) emit(RD_Y | RD(REG), CTX) #define emit_write_y(REG, CTX) emit(WR_Y | IMMED | RS1(REG) | S13(0), CTX) @@ -358,7 +415,7 @@ static void emit_branch(unsigned int br_opc, unsigned int from_idx, unsigned int emit(SUBCC | RS1(R1) | RS2(R2) | RD(G0), CTX) #define emit_cmpi(R1, IMM, CTX) \ - emit(SUBCC | IMMED | RS1(R1) | S13(IMM) | RD(G0), CTX); + emit(SUBCC | IMMED | RS1(R1) | S13(IMM) | RD(G0), CTX) #define emit_btst(R1, R2, CTX) \ emit(ANDCC | RS1(R1) | RS2(R2) | RD(G0), CTX) @@ -366,6 +423,117 @@ static void emit_branch(unsigned int br_opc, unsigned int from_idx, unsigned int #define emit_btsti(R1, IMM, CTX) \ emit(ANDCC | IMMED | RS1(R1) | S13(IMM) | RD(G0), CTX) +static int emit_compare_and_branch(const u8 code, const u8 dst, u8 src, + const s32 imm, bool is_imm, int branch_dst, + struct jit_ctx *ctx) +{ + bool use_cbcond = (sparc64_elf_hwcap & AV_SPARC_CBCOND) != 0; + const u8 tmp = bpf2sparc[TMP_REG_1]; + + branch_dst = ctx->offset[branch_dst]; + + if (!is_simm10(branch_dst - ctx->idx) || + BPF_OP(code) == BPF_JSET) + use_cbcond = false; + + if (is_imm) { + bool fits = true; + + if (use_cbcond) { + if (!is_simm5(imm)) + fits = false; + } else if (!is_simm13(imm)) { + fits = false; + } + if (!fits) { + ctx->tmp_1_used = true; + emit_loadimm_sext(imm, tmp, ctx); + src = tmp; + is_imm = false; + } + } + + if (!use_cbcond) { + u32 br_opcode; + + if (BPF_OP(code) == BPF_JSET) { + if (is_imm) + emit_btsti(dst, imm, ctx); + else + emit_btst(dst, src, ctx); + } else { + if (is_imm) + emit_cmpi(dst, imm, ctx); + else + emit_cmp(dst, src, ctx); + } + switch (BPF_OP(code)) { + case BPF_JEQ: + br_opcode = BE; + break; + case BPF_JGT: + br_opcode = BGU; + break; + case BPF_JGE: + br_opcode = BGEU; + break; + case BPF_JSET: + case BPF_JNE: + br_opcode = BNE; + break; + case BPF_JSGT: + br_opcode = BG; + break; + case BPF_JSGE: + br_opcode = BGE; + break; + default: + /* Make sure we dont leak kernel information to the + * user. + */ + return -EFAULT; + } + emit_branch(br_opcode, ctx->idx, branch_dst, ctx); + emit_nop(ctx); + } else { + u32 cbcond_opcode; + + switch (BPF_OP(code)) { + case BPF_JEQ: + cbcond_opcode = CBCONDE; + break; + case BPF_JGT: + cbcond_opcode = CBCONDGU; + break; + case BPF_JGE: + cbcond_opcode = CBCONDGEU; + break; + case BPF_JNE: + cbcond_opcode = CBCONDNE; + break; + case BPF_JSGT: + cbcond_opcode = CBCONDG; + break; + case BPF_JSGE: + cbcond_opcode = CBCONDGE; + break; + default: + /* Make sure we dont leak kernel information to the + * user. + */ + return -EFAULT; + } + cbcond_opcode |= CBCOND_OP; + if (is_imm) + emit_cbcondi(cbcond_opcode, ctx->idx, branch_dst, + dst, imm, ctx); + else + emit_cbcond(cbcond_opcode, ctx->idx, branch_dst, + dst, src, ctx); + } + return 0; +} + static void load_skb_regs(struct jit_ctx *ctx, u8 r_skb) { const u8 r_headlen = bpf2sparc[SKB_HLEN_REG]; @@ -765,44 +933,15 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx) case BPF_JMP | BPF_JGE | BPF_X: case BPF_JMP | BPF_JNE | BPF_X: case BPF_JMP | BPF_JSGT | BPF_X: - case BPF_JMP | BPF_JSGE | BPF_X: { - u32 br_opcode; + case BPF_JMP | BPF_JSGE | BPF_X: + case BPF_JMP | BPF_JSET | BPF_X: { + int err; - emit_cmp(dst, src, ctx); -emit_cond_jmp: - switch (BPF_OP(code)) { - case BPF_JEQ: - br_opcode = BE; - break; - case BPF_JGT: - br_opcode = BGU; - break; - case BPF_JGE: - br_opcode = BGEU; - break; - case BPF_JSET: - case BPF_JNE: - br_opcode = BNE; - break; - case BPF_JSGT: - br_opcode = BG; - break; - case BPF_JSGE: - br_opcode = BGE; - break; - default: - /* Make sure we dont leak kernel information to the - * user. - */ - return -EFAULT; - } - emit_branch(br_opcode, ctx->idx, ctx->offset[i + off], ctx); - emit_nop(ctx); + err = emit_compare_and_branch(code, dst, src, 0, false, i + off, ctx); + if (err) + return err; break; } - case BPF_JMP | BPF_JSET | BPF_X: - emit_btst(dst, src, ctx); - goto emit_cond_jmp; /* IF (dst COND imm) JUMP off */ case BPF_JMP | BPF_JEQ | BPF_K: case BPF_JMP | BPF_JGT | BPF_K: @@ -810,23 +949,14 @@ emit_cond_jmp: case BPF_JMP | BPF_JNE | BPF_K: case BPF_JMP | BPF_JSGT | BPF_K: case BPF_JMP | BPF_JSGE | BPF_K: - if (is_simm13(imm)) { - emit_cmpi(dst, imm, ctx); - } else { - ctx->tmp_1_used = true; - emit_loadimm_sext(imm, bpf2sparc[TMP_REG_1], ctx); - emit_cmp(dst, bpf2sparc[TMP_REG_1], ctx); - } - goto emit_cond_jmp; - case BPF_JMP | BPF_JSET | BPF_K: - if (is_simm13(imm)) { - emit_btsti(dst, imm, ctx); - } else { - ctx->tmp_1_used = true; - emit_loadimm_sext(imm, bpf2sparc[TMP_REG_1], ctx); - emit_btst(dst, bpf2sparc[TMP_REG_1], ctx); - } - goto emit_cond_jmp; + case BPF_JMP | BPF_JSET | BPF_K: { + int err; + + err = emit_compare_and_branch(code, dst, 0, imm, true, i + off, ctx); + if (err) + return err; + break; + } /* function call */ case BPF_JMP | BPF_CALL: -- cgit From 58771c1cb0023fdd744e76d6cad7716dc4f579ee Mon Sep 17 00:00:00 2001 From: Salvatore Benedetto Date: Mon, 24 Apr 2017 13:13:20 +0100 Subject: Bluetooth: convert smp and selftest to crypto kpp API * Convert both smp and selftest to crypto kpp API * Remove module ecc as no more required * Add ecdh_helper functions for wrapping kpp async calls This patch has been tested *only* with selftest, which is called on module loading. Signed-off-by: Salvatore Benedetto Signed-off-by: Marcel Holtmann --- net/bluetooth/Kconfig | 1 + net/bluetooth/Makefile | 2 +- net/bluetooth/ecc.c | 816 -------------------------------------------- net/bluetooth/ecc.h | 54 --- net/bluetooth/ecdh_helper.c | 223 ++++++++++++ net/bluetooth/ecdh_helper.h | 27 ++ net/bluetooth/selftest.c | 6 +- net/bluetooth/smp.c | 8 +- 8 files changed, 259 insertions(+), 878 deletions(-) delete mode 100644 net/bluetooth/ecc.c delete mode 100644 net/bluetooth/ecc.h create mode 100644 net/bluetooth/ecdh_helper.c create mode 100644 net/bluetooth/ecdh_helper.h diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 06c31b9a68b0..68f951b3e85a 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -13,6 +13,7 @@ menuconfig BT select CRYPTO_CMAC select CRYPTO_ECB select CRYPTO_SHA256 + select CRYPTO_ECDH help Bluetooth is low-cost, low-power, short-range wireless technology. It was designed as a replacement for cables and other short-range diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index 4bfaa19a5573..5d0a113e2e40 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -13,7 +13,7 @@ bluetooth_6lowpan-y := 6lowpan.o bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \ - ecc.o hci_request.o mgmt_util.o + ecdh_helper.o hci_request.o mgmt_util.o bluetooth-$(CONFIG_BT_BREDR) += sco.o bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o diff --git a/net/bluetooth/ecc.c b/net/bluetooth/ecc.c deleted file mode 100644 index e1709f8467ac..000000000000 --- a/net/bluetooth/ecc.c +++ /dev/null @@ -1,816 +0,0 @@ -/* - * Copyright (c) 2013, Kenneth MacKay - * 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. - * - * 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 - * HOLDER 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 "ecc.h" - -/* 256-bit curve */ -#define ECC_BYTES 32 - -#define MAX_TRIES 16 - -/* Number of u64's needed */ -#define NUM_ECC_DIGITS (ECC_BYTES / 8) - -struct ecc_point { - u64 x[NUM_ECC_DIGITS]; - u64 y[NUM_ECC_DIGITS]; -}; - -typedef struct { - u64 m_low; - u64 m_high; -} uint128_t; - -#define CURVE_P_32 { 0xFFFFFFFFFFFFFFFFull, 0x00000000FFFFFFFFull, \ - 0x0000000000000000ull, 0xFFFFFFFF00000001ull } - -#define CURVE_G_32 { \ - { 0xF4A13945D898C296ull, 0x77037D812DEB33A0ull, \ - 0xF8BCE6E563A440F2ull, 0x6B17D1F2E12C4247ull }, \ - { 0xCBB6406837BF51F5ull, 0x2BCE33576B315ECEull, \ - 0x8EE7EB4A7C0F9E16ull, 0x4FE342E2FE1A7F9Bull } \ -} - -#define CURVE_N_32 { 0xF3B9CAC2FC632551ull, 0xBCE6FAADA7179E84ull, \ - 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFF00000000ull } - -static u64 curve_p[NUM_ECC_DIGITS] = CURVE_P_32; -static struct ecc_point curve_g = CURVE_G_32; -static u64 curve_n[NUM_ECC_DIGITS] = CURVE_N_32; - -static void vli_clear(u64 *vli) -{ - int i; - - for (i = 0; i < NUM_ECC_DIGITS; i++) - vli[i] = 0; -} - -/* Returns true if vli == 0, false otherwise. */ -static bool vli_is_zero(const u64 *vli) -{ - int i; - - for (i = 0; i < NUM_ECC_DIGITS; i++) { - if (vli[i]) - return false; - } - - return true; -} - -/* Returns nonzero if bit bit of vli is set. */ -static u64 vli_test_bit(const u64 *vli, unsigned int bit) -{ - return (vli[bit / 64] & ((u64) 1 << (bit % 64))); -} - -/* Counts the number of 64-bit "digits" in vli. */ -static unsigned int vli_num_digits(const u64 *vli) -{ - int i; - - /* Search from the end until we find a non-zero digit. - * We do it in reverse because we expect that most digits will - * be nonzero. - */ - for (i = NUM_ECC_DIGITS - 1; i >= 0 && vli[i] == 0; i--); - - return (i + 1); -} - -/* Counts the number of bits required for vli. */ -static unsigned int vli_num_bits(const u64 *vli) -{ - unsigned int i, num_digits; - u64 digit; - - num_digits = vli_num_digits(vli); - if (num_digits == 0) - return 0; - - digit = vli[num_digits - 1]; - for (i = 0; digit; i++) - digit >>= 1; - - return ((num_digits - 1) * 64 + i); -} - -/* Sets dest = src. */ -static void vli_set(u64 *dest, const u64 *src) -{ - int i; - - for (i = 0; i < NUM_ECC_DIGITS; i++) - dest[i] = src[i]; -} - -/* Returns sign of left - right. */ -static int vli_cmp(const u64 *left, const u64 *right) -{ - int i; - - for (i = NUM_ECC_DIGITS - 1; i >= 0; i--) { - if (left[i] > right[i]) - return 1; - else if (left[i] < right[i]) - return -1; - } - - return 0; -} - -/* Computes result = in << c, returning carry. Can modify in place - * (if result == in). 0 < shift < 64. - */ -static u64 vli_lshift(u64 *result, const u64 *in, - unsigned int shift) -{ - u64 carry = 0; - int i; - - for (i = 0; i < NUM_ECC_DIGITS; i++) { - u64 temp = in[i]; - - result[i] = (temp << shift) | carry; - carry = temp >> (64 - shift); - } - - return carry; -} - -/* Computes vli = vli >> 1. */ -static void vli_rshift1(u64 *vli) -{ - u64 *end = vli; - u64 carry = 0; - - vli += NUM_ECC_DIGITS; - - while (vli-- > end) { - u64 temp = *vli; - *vli = (temp >> 1) | carry; - carry = temp << 63; - } -} - -/* Computes result = left + right, returning carry. Can modify in place. */ -static u64 vli_add(u64 *result, const u64 *left, - const u64 *right) -{ - u64 carry = 0; - int i; - - for (i = 0; i < NUM_ECC_DIGITS; i++) { - u64 sum; - - sum = left[i] + right[i] + carry; - if (sum != left[i]) - carry = (sum < left[i]); - - result[i] = sum; - } - - return carry; -} - -/* Computes result = left - right, returning borrow. Can modify in place. */ -static u64 vli_sub(u64 *result, const u64 *left, const u64 *right) -{ - u64 borrow = 0; - int i; - - for (i = 0; i < NUM_ECC_DIGITS; i++) { - u64 diff; - - diff = left[i] - right[i] - borrow; - if (diff != left[i]) - borrow = (diff > left[i]); - - result[i] = diff; - } - - return borrow; -} - -static uint128_t mul_64_64(u64 left, u64 right) -{ - u64 a0 = left & 0xffffffffull; - u64 a1 = left >> 32; - u64 b0 = right & 0xffffffffull; - u64 b1 = right >> 32; - u64 m0 = a0 * b0; - u64 m1 = a0 * b1; - u64 m2 = a1 * b0; - u64 m3 = a1 * b1; - uint128_t result; - - m2 += (m0 >> 32); - m2 += m1; - - /* Overflow */ - if (m2 < m1) - m3 += 0x100000000ull; - - result.m_low = (m0 & 0xffffffffull) | (m2 << 32); - result.m_high = m3 + (m2 >> 32); - - return result; -} - -static uint128_t add_128_128(uint128_t a, uint128_t b) -{ - uint128_t result; - - result.m_low = a.m_low + b.m_low; - result.m_high = a.m_high + b.m_high + (result.m_low < a.m_low); - - return result; -} - -static void vli_mult(u64 *result, const u64 *left, const u64 *right) -{ - uint128_t r01 = { 0, 0 }; - u64 r2 = 0; - unsigned int i, k; - - /* Compute each digit of result in sequence, maintaining the - * carries. - */ - for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) { - unsigned int min; - - if (k < NUM_ECC_DIGITS) - min = 0; - else - min = (k + 1) - NUM_ECC_DIGITS; - - for (i = min; i <= k && i < NUM_ECC_DIGITS; i++) { - uint128_t product; - - product = mul_64_64(left[i], right[k - i]); - - r01 = add_128_128(r01, product); - r2 += (r01.m_high < product.m_high); - } - - result[k] = r01.m_low; - r01.m_low = r01.m_high; - r01.m_high = r2; - r2 = 0; - } - - result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low; -} - -static void vli_square(u64 *result, const u64 *left) -{ - uint128_t r01 = { 0, 0 }; - u64 r2 = 0; - int i, k; - - for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) { - unsigned int min; - - if (k < NUM_ECC_DIGITS) - min = 0; - else - min = (k + 1) - NUM_ECC_DIGITS; - - for (i = min; i <= k && i <= k - i; i++) { - uint128_t product; - - product = mul_64_64(left[i], left[k - i]); - - if (i < k - i) { - r2 += product.m_high >> 63; - product.m_high = (product.m_high << 1) | - (product.m_low >> 63); - product.m_low <<= 1; - } - - r01 = add_128_128(r01, product); - r2 += (r01.m_high < product.m_high); - } - - result[k] = r01.m_low; - r01.m_low = r01.m_high; - r01.m_high = r2; - r2 = 0; - } - - result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low; -} - -/* Computes result = (left + right) % mod. - * Assumes that left < mod and right < mod, result != mod. - */ -static void vli_mod_add(u64 *result, const u64 *left, const u64 *right, - const u64 *mod) -{ - u64 carry; - - carry = vli_add(result, left, right); - - /* result > mod (result = mod + remainder), so subtract mod to - * get remainder. - */ - if (carry || vli_cmp(result, mod) >= 0) - vli_sub(result, result, mod); -} - -/* Computes result = (left - right) % mod. - * Assumes that left < mod and right < mod, result != mod. - */ -static void vli_mod_sub(u64 *result, const u64 *left, const u64 *right, - const u64 *mod) -{ - u64 borrow = vli_sub(result, left, right); - - /* In this case, p_result == -diff == (max int) - diff. - * Since -x % d == d - x, we can get the correct result from - * result + mod (with overflow). - */ - if (borrow) - vli_add(result, result, mod); -} - -/* Computes result = product % curve_p - from http://www.nsa.gov/ia/_files/nist-routines.pdf */ -static void vli_mmod_fast(u64 *result, const u64 *product) -{ - u64 tmp[NUM_ECC_DIGITS]; - int carry; - - /* t */ - vli_set(result, product); - - /* s1 */ - tmp[0] = 0; - tmp[1] = product[5] & 0xffffffff00000000ull; - tmp[2] = product[6]; - tmp[3] = product[7]; - carry = vli_lshift(tmp, tmp, 1); - carry += vli_add(result, result, tmp); - - /* s2 */ - tmp[1] = product[6] << 32; - tmp[2] = (product[6] >> 32) | (product[7] << 32); - tmp[3] = product[7] >> 32; - carry += vli_lshift(tmp, tmp, 1); - carry += vli_add(result, result, tmp); - - /* s3 */ - tmp[0] = product[4]; - tmp[1] = product[5] & 0xffffffff; - tmp[2] = 0; - tmp[3] = product[7]; - carry += vli_add(result, result, tmp); - - /* s4 */ - tmp[0] = (product[4] >> 32) | (product[5] << 32); - tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000ull); - tmp[2] = product[7]; - tmp[3] = (product[6] >> 32) | (product[4] << 32); - carry += vli_add(result, result, tmp); - - /* d1 */ - tmp[0] = (product[5] >> 32) | (product[6] << 32); - tmp[1] = (product[6] >> 32); - tmp[2] = 0; - tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32); - carry -= vli_sub(result, result, tmp); - - /* d2 */ - tmp[0] = product[6]; - tmp[1] = product[7]; - tmp[2] = 0; - tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000ull); - carry -= vli_sub(result, result, tmp); - - /* d3 */ - tmp[0] = (product[6] >> 32) | (product[7] << 32); - tmp[1] = (product[7] >> 32) | (product[4] << 32); - tmp[2] = (product[4] >> 32) | (product[5] << 32); - tmp[3] = (product[6] << 32); - carry -= vli_sub(result, result, tmp); - - /* d4 */ - tmp[0] = product[7]; - tmp[1] = product[4] & 0xffffffff00000000ull; - tmp[2] = product[5]; - tmp[3] = product[6] & 0xffffffff00000000ull; - carry -= vli_sub(result, result, tmp); - - if (carry < 0) { - do { - carry += vli_add(result, result, curve_p); - } while (carry < 0); - } else { - while (carry || vli_cmp(curve_p, result) != 1) - carry -= vli_sub(result, result, curve_p); - } -} - -/* Computes result = (left * right) % curve_p. */ -static void vli_mod_mult_fast(u64 *result, const u64 *left, const u64 *right) -{ - u64 product[2 * NUM_ECC_DIGITS]; - - vli_mult(product, left, right); - vli_mmod_fast(result, product); -} - -/* Computes result = left^2 % curve_p. */ -static void vli_mod_square_fast(u64 *result, const u64 *left) -{ - u64 product[2 * NUM_ECC_DIGITS]; - - vli_square(product, left); - vli_mmod_fast(result, product); -} - -#define EVEN(vli) (!(vli[0] & 1)) -/* Computes result = (1 / p_input) % mod. All VLIs are the same size. - * See "From Euclid's GCD to Montgomery Multiplication to the Great Divide" - * https://labs.oracle.com/techrep/2001/smli_tr-2001-95.pdf - */ -static void vli_mod_inv(u64 *result, const u64 *input, const u64 *mod) -{ - u64 a[NUM_ECC_DIGITS], b[NUM_ECC_DIGITS]; - u64 u[NUM_ECC_DIGITS], v[NUM_ECC_DIGITS]; - u64 carry; - int cmp_result; - - if (vli_is_zero(input)) { - vli_clear(result); - return; - } - - vli_set(a, input); - vli_set(b, mod); - vli_clear(u); - u[0] = 1; - vli_clear(v); - - while ((cmp_result = vli_cmp(a, b)) != 0) { - carry = 0; - - if (EVEN(a)) { - vli_rshift1(a); - - if (!EVEN(u)) - carry = vli_add(u, u, mod); - - vli_rshift1(u); - if (carry) - u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull; - } else if (EVEN(b)) { - vli_rshift1(b); - - if (!EVEN(v)) - carry = vli_add(v, v, mod); - - vli_rshift1(v); - if (carry) - v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull; - } else if (cmp_result > 0) { - vli_sub(a, a, b); - vli_rshift1(a); - - if (vli_cmp(u, v) < 0) - vli_add(u, u, mod); - - vli_sub(u, u, v); - if (!EVEN(u)) - carry = vli_add(u, u, mod); - - vli_rshift1(u); - if (carry) - u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull; - } else { - vli_sub(b, b, a); - vli_rshift1(b); - - if (vli_cmp(v, u) < 0) - vli_add(v, v, mod); - - vli_sub(v, v, u); - if (!EVEN(v)) - carry = vli_add(v, v, mod); - - vli_rshift1(v); - if (carry) - v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull; - } - } - - vli_set(result, u); -} - -/* ------ Point operations ------ */ - -/* Returns true if p_point is the point at infinity, false otherwise. */ -static bool ecc_point_is_zero(const struct ecc_point *point) -{ - return (vli_is_zero(point->x) && vli_is_zero(point->y)); -} - -/* Point multiplication algorithm using Montgomery's ladder with co-Z - * coordinates. From http://eprint.iacr.org/2011/338.pdf - */ - -/* Double in place */ -static void ecc_point_double_jacobian(u64 *x1, u64 *y1, u64 *z1) -{ - /* t1 = x, t2 = y, t3 = z */ - u64 t4[NUM_ECC_DIGITS]; - u64 t5[NUM_ECC_DIGITS]; - - if (vli_is_zero(z1)) - return; - - vli_mod_square_fast(t4, y1); /* t4 = y1^2 */ - vli_mod_mult_fast(t5, x1, t4); /* t5 = x1*y1^2 = A */ - vli_mod_square_fast(t4, t4); /* t4 = y1^4 */ - vli_mod_mult_fast(y1, y1, z1); /* t2 = y1*z1 = z3 */ - vli_mod_square_fast(z1, z1); /* t3 = z1^2 */ - - vli_mod_add(x1, x1, z1, curve_p); /* t1 = x1 + z1^2 */ - vli_mod_add(z1, z1, z1, curve_p); /* t3 = 2*z1^2 */ - vli_mod_sub(z1, x1, z1, curve_p); /* t3 = x1 - z1^2 */ - vli_mod_mult_fast(x1, x1, z1); /* t1 = x1^2 - z1^4 */ - - vli_mod_add(z1, x1, x1, curve_p); /* t3 = 2*(x1^2 - z1^4) */ - vli_mod_add(x1, x1, z1, curve_p); /* t1 = 3*(x1^2 - z1^4) */ - if (vli_test_bit(x1, 0)) { - u64 carry = vli_add(x1, x1, curve_p); - vli_rshift1(x1); - x1[NUM_ECC_DIGITS - 1] |= carry << 63; - } else { - vli_rshift1(x1); - } - /* t1 = 3/2*(x1^2 - z1^4) = B */ - - vli_mod_square_fast(z1, x1); /* t3 = B^2 */ - vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - A */ - vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - 2A = x3 */ - vli_mod_sub(t5, t5, z1, curve_p); /* t5 = A - x3 */ - vli_mod_mult_fast(x1, x1, t5); /* t1 = B * (A - x3) */ - vli_mod_sub(t4, x1, t4, curve_p); /* t4 = B * (A - x3) - y1^4 = y3 */ - - vli_set(x1, z1); - vli_set(z1, y1); - vli_set(y1, t4); -} - -/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */ -static void apply_z(u64 *x1, u64 *y1, u64 *z) -{ - u64 t1[NUM_ECC_DIGITS]; - - vli_mod_square_fast(t1, z); /* z^2 */ - vli_mod_mult_fast(x1, x1, t1); /* x1 * z^2 */ - vli_mod_mult_fast(t1, t1, z); /* z^3 */ - vli_mod_mult_fast(y1, y1, t1); /* y1 * z^3 */ -} - -/* P = (x1, y1) => 2P, (x2, y2) => P' */ -static void xycz_initial_double(u64 *x1, u64 *y1, u64 *x2, u64 *y2, - u64 *p_initial_z) -{ - u64 z[NUM_ECC_DIGITS]; - - vli_set(x2, x1); - vli_set(y2, y1); - - vli_clear(z); - z[0] = 1; - - if (p_initial_z) - vli_set(z, p_initial_z); - - apply_z(x1, y1, z); - - ecc_point_double_jacobian(x1, y1, z); - - apply_z(x2, y2, z); -} - -/* Input P = (x1, y1, Z), Q = (x2, y2, Z) - * Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) - * or P => P', Q => P + Q - */ -static void xycz_add(u64 *x1, u64 *y1, u64 *x2, u64 *y2) -{ - /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ - u64 t5[NUM_ECC_DIGITS]; - - vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */ - vli_mod_square_fast(t5, t5); /* t5 = (x2 - x1)^2 = A */ - vli_mod_mult_fast(x1, x1, t5); /* t1 = x1*A = B */ - vli_mod_mult_fast(x2, x2, t5); /* t3 = x2*A = C */ - vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */ - vli_mod_square_fast(t5, y2); /* t5 = (y2 - y1)^2 = D */ - - vli_mod_sub(t5, t5, x1, curve_p); /* t5 = D - B */ - vli_mod_sub(t5, t5, x2, curve_p); /* t5 = D - B - C = x3 */ - vli_mod_sub(x2, x2, x1, curve_p); /* t3 = C - B */ - vli_mod_mult_fast(y1, y1, x2); /* t2 = y1*(C - B) */ - vli_mod_sub(x2, x1, t5, curve_p); /* t3 = B - x3 */ - vli_mod_mult_fast(y2, y2, x2); /* t4 = (y2 - y1)*(B - x3) */ - vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */ - - vli_set(x2, t5); -} - -/* Input P = (x1, y1, Z), Q = (x2, y2, Z) - * Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3) - * or P => P - Q, Q => P + Q - */ -static void xycz_add_c(u64 *x1, u64 *y1, u64 *x2, u64 *y2) -{ - /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ - u64 t5[NUM_ECC_DIGITS]; - u64 t6[NUM_ECC_DIGITS]; - u64 t7[NUM_ECC_DIGITS]; - - vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */ - vli_mod_square_fast(t5, t5); /* t5 = (x2 - x1)^2 = A */ - vli_mod_mult_fast(x1, x1, t5); /* t1 = x1*A = B */ - vli_mod_mult_fast(x2, x2, t5); /* t3 = x2*A = C */ - vli_mod_add(t5, y2, y1, curve_p); /* t4 = y2 + y1 */ - vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */ - - vli_mod_sub(t6, x2, x1, curve_p); /* t6 = C - B */ - vli_mod_mult_fast(y1, y1, t6); /* t2 = y1 * (C - B) */ - vli_mod_add(t6, x1, x2, curve_p); /* t6 = B + C */ - vli_mod_square_fast(x2, y2); /* t3 = (y2 - y1)^2 */ - vli_mod_sub(x2, x2, t6, curve_p); /* t3 = x3 */ - - vli_mod_sub(t7, x1, x2, curve_p); /* t7 = B - x3 */ - vli_mod_mult_fast(y2, y2, t7); /* t4 = (y2 - y1)*(B - x3) */ - vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */ - - vli_mod_square_fast(t7, t5); /* t7 = (y2 + y1)^2 = F */ - vli_mod_sub(t7, t7, t6, curve_p); /* t7 = x3' */ - vli_mod_sub(t6, t7, x1, curve_p); /* t6 = x3' - B */ - vli_mod_mult_fast(t6, t6, t5); /* t6 = (y2 + y1)*(x3' - B) */ - vli_mod_sub(y1, t6, y1, curve_p); /* t2 = y3' */ - - vli_set(x1, t7); -} - -static void ecc_point_mult(struct ecc_point *result, - const struct ecc_point *point, u64 *scalar, - u64 *initial_z, int num_bits) -{ - /* R0 and R1 */ - u64 rx[2][NUM_ECC_DIGITS]; - u64 ry[2][NUM_ECC_DIGITS]; - u64 z[NUM_ECC_DIGITS]; - int i, nb; - - vli_set(rx[1], point->x); - vli_set(ry[1], point->y); - - xycz_initial_double(rx[1], ry[1], rx[0], ry[0], initial_z); - - for (i = num_bits - 2; i > 0; i--) { - nb = !vli_test_bit(scalar, i); - xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]); - xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]); - } - - nb = !vli_test_bit(scalar, 0); - xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]); - - /* Find final 1/Z value. */ - vli_mod_sub(z, rx[1], rx[0], curve_p); /* X1 - X0 */ - vli_mod_mult_fast(z, z, ry[1 - nb]); /* Yb * (X1 - X0) */ - vli_mod_mult_fast(z, z, point->x); /* xP * Yb * (X1 - X0) */ - vli_mod_inv(z, z, curve_p); /* 1 / (xP * Yb * (X1 - X0)) */ - vli_mod_mult_fast(z, z, point->y); /* yP / (xP * Yb * (X1 - X0)) */ - vli_mod_mult_fast(z, z, rx[1 - nb]); /* Xb * yP / (xP * Yb * (X1 - X0)) */ - /* End 1/Z calculation */ - - xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]); - - apply_z(rx[0], ry[0], z); - - vli_set(result->x, rx[0]); - vli_set(result->y, ry[0]); -} - -static void ecc_bytes2native(const u8 bytes[ECC_BYTES], - u64 native[NUM_ECC_DIGITS]) -{ - int i; - - for (i = 0; i < NUM_ECC_DIGITS; i++) { - const u8 *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i); - - native[NUM_ECC_DIGITS - 1 - i] = - ((u64) digit[0] << 0) | - ((u64) digit[1] << 8) | - ((u64) digit[2] << 16) | - ((u64) digit[3] << 24) | - ((u64) digit[4] << 32) | - ((u64) digit[5] << 40) | - ((u64) digit[6] << 48) | - ((u64) digit[7] << 56); - } -} - -static void ecc_native2bytes(const u64 native[NUM_ECC_DIGITS], - u8 bytes[ECC_BYTES]) -{ - int i; - - for (i = 0; i < NUM_ECC_DIGITS; i++) { - u8 *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i); - - digit[0] = native[NUM_ECC_DIGITS - 1 - i] >> 0; - digit[1] = native[NUM_ECC_DIGITS - 1 - i] >> 8; - digit[2] = native[NUM_ECC_DIGITS - 1 - i] >> 16; - digit[3] = native[NUM_ECC_DIGITS - 1 - i] >> 24; - digit[4] = native[NUM_ECC_DIGITS - 1 - i] >> 32; - digit[5] = native[NUM_ECC_DIGITS - 1 - i] >> 40; - digit[6] = native[NUM_ECC_DIGITS - 1 - i] >> 48; - digit[7] = native[NUM_ECC_DIGITS - 1 - i] >> 56; - } -} - -bool ecc_make_key(u8 public_key[64], u8 private_key[32]) -{ - struct ecc_point pk; - u64 priv[NUM_ECC_DIGITS]; - unsigned int tries = 0; - - do { - if (tries++ >= MAX_TRIES) - return false; - - get_random_bytes(priv, ECC_BYTES); - - if (vli_is_zero(priv)) - continue; - - /* Make sure the private key is in the range [1, n-1]. */ - if (vli_cmp(curve_n, priv) != 1) - continue; - - ecc_point_mult(&pk, &curve_g, priv, NULL, vli_num_bits(priv)); - } while (ecc_point_is_zero(&pk)); - - ecc_native2bytes(priv, private_key); - ecc_native2bytes(pk.x, public_key); - ecc_native2bytes(pk.y, &public_key[32]); - - return true; -} - -bool ecdh_shared_secret(const u8 public_key[64], const u8 private_key[32], - u8 secret[32]) -{ - u64 priv[NUM_ECC_DIGITS]; - u64 rand[NUM_ECC_DIGITS]; - struct ecc_point product, pk; - - get_random_bytes(rand, ECC_BYTES); - - ecc_bytes2native(public_key, pk.x); - ecc_bytes2native(&public_key[32], pk.y); - ecc_bytes2native(private_key, priv); - - ecc_point_mult(&product, &pk, priv, rand, vli_num_bits(priv)); - - ecc_native2bytes(product.x, secret); - - return !ecc_point_is_zero(&product); -} diff --git a/net/bluetooth/ecc.h b/net/bluetooth/ecc.h deleted file mode 100644 index 8d6a2f4d1905..000000000000 --- a/net/bluetooth/ecc.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2013, Kenneth MacKay - * 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. - * - * 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 - * HOLDER 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. - */ - -/* Create a public/private key pair. - * Outputs: - * public_key - Will be filled in with the public key. - * private_key - Will be filled in with the private key. - * - * Returns true if the key pair was generated successfully, false - * if an error occurred. The keys are with the LSB first. - */ -bool ecc_make_key(u8 public_key[64], u8 private_key[32]); - -/* Compute a shared secret given your secret key and someone else's - * public key. - * Note: It is recommended that you hash the result of ecdh_shared_secret - * before using it for symmetric encryption or HMAC. - * - * Inputs: - * public_key - The public key of the remote party - * private_key - Your private key. - * - * Outputs: - * secret - Will be filled in with the shared secret value. - * - * Returns true if the shared secret was generated successfully, false - * if an error occurred. Both input and output parameters are with the - * LSB first. - */ -bool ecdh_shared_secret(const u8 public_key[64], const u8 private_key[32], - u8 secret[32]); diff --git a/net/bluetooth/ecdh_helper.c b/net/bluetooth/ecdh_helper.c new file mode 100644 index 000000000000..b6d9aa155485 --- /dev/null +++ b/net/bluetooth/ecdh_helper.c @@ -0,0 +1,223 @@ +/* + * ECDH helper functions - KPP wrappings + * + * Copyright (C) 2017 Intel Corporation + * + * 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; + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + * CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + * COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + * SOFTWARE IS DISCLAIMED. + */ +#include "ecdh_helper.h" + +#include +#include +#include +#include + +struct ecdh_completion { + struct completion completion; + int err; +}; + +static void ecdh_complete(struct crypto_async_request *req, int err) +{ + struct ecdh_completion *res = req->data; + + if (err == -EINPROGRESS) + return; + + res->err = err; + complete(&res->completion); +} + +static inline void swap_digits(u64 *in, u64 *out, unsigned int ndigits) +{ + int i; + + for (i = 0; i < ndigits; i++) + out[i] = __swab64(in[ndigits - 1 - i]); +} + +bool compute_ecdh_secret(const u8 public_key[64], const u8 private_key[32], + u8 secret[32]) +{ + struct crypto_kpp *tfm; + struct kpp_request *req; + struct ecdh p; + struct ecdh_completion result; + struct scatterlist src, dst; + u8 tmp[64]; + u8 *buf; + unsigned int buf_len; + int err = -ENOMEM; + + tfm = crypto_alloc_kpp("ecdh", CRYPTO_ALG_INTERNAL, 0); + if (IS_ERR(tfm)) { + pr_err("alg: kpp: Failed to load tfm for kpp: %ld\n", + PTR_ERR(tfm)); + return false; + } + + req = kpp_request_alloc(tfm, GFP_KERNEL); + if (!req) + goto free_kpp; + + init_completion(&result.completion); + + /* Security Manager Protocol holds digits in litte-endian order + * while ECC API expect big-endian data + */ + swap_digits((u64 *)private_key, (u64 *)tmp, 4); + p.key = (char *)tmp; + p.key_size = 32; + /* Set curve_id */ + p.curve_id = ECC_CURVE_NIST_P256; + buf_len = crypto_ecdh_key_len(&p); + buf = kmalloc(buf_len, GFP_KERNEL); + if (!buf) { + pr_err("alg: kpp: Failed to allocate %d bytes for buf\n", + buf_len); + goto free_req; + } + crypto_ecdh_encode_key(buf, buf_len, &p); + + /* Set A private Key */ + err = crypto_kpp_set_secret(tfm, (void *)buf, buf_len); + if (err) + goto free_all; + + swap_digits((u64 *)public_key, (u64 *)tmp, 4); /* x */ + swap_digits((u64 *)&public_key[32], (u64 *)&tmp[32], 4); /* y */ + + sg_init_one(&src, tmp, 64); + sg_init_one(&dst, secret, 32); + kpp_request_set_input(req, &src, 64); + kpp_request_set_output(req, &dst, 32); + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + ecdh_complete, &result); + err = crypto_kpp_compute_shared_secret(req); + if (err == -EINPROGRESS) { + wait_for_completion(&result.completion); + err = result.err; + } + if (err < 0) { + pr_err("alg: ecdh: compute shared secret failed. err %d\n", + err); + goto free_all; + } + + swap_digits((u64 *)secret, (u64 *)tmp, 4); + memcpy(secret, tmp, 32); + +free_all: + kzfree(buf); +free_req: + kpp_request_free(req); +free_kpp: + crypto_free_kpp(tfm); + return (err == 0); +} + +bool generate_ecdh_keys(u8 public_key[64], u8 private_key[32]) +{ + struct crypto_kpp *tfm; + struct kpp_request *req; + struct ecdh p; + struct ecdh_completion result; + struct scatterlist dst; + u8 tmp[64]; + u8 *buf; + unsigned int buf_len; + int err = -ENOMEM; + const unsigned short max_tries = 16; + unsigned short tries = 0; + + tfm = crypto_alloc_kpp("ecdh", CRYPTO_ALG_INTERNAL, 0); + if (IS_ERR(tfm)) { + pr_err("alg: kpp: Failed to load tfm for kpp: %ld\n", + PTR_ERR(tfm)); + return false; + } + + req = kpp_request_alloc(tfm, GFP_KERNEL); + if (!req) + goto free_kpp; + + init_completion(&result.completion); + + /* Set curve_id */ + p.curve_id = ECC_CURVE_NIST_P256; + p.key_size = 32; + buf_len = crypto_ecdh_key_len(&p); + buf = kmalloc(buf_len, GFP_KERNEL); + if (!buf) { + pr_err("alg: kpp: Failed to allocate %d bytes for buf\n", + buf_len); + goto free_req; + } + + do { + if (tries++ >= max_tries) + goto free_all; + + get_random_bytes(private_key, 32); + + /* Set private Key */ + p.key = (char *)private_key; + crypto_ecdh_encode_key(buf, buf_len, &p); + err = crypto_kpp_set_secret(tfm, buf, buf_len); + if (err) + goto free_all; + + sg_init_one(&dst, tmp, 64); + kpp_request_set_output(req, &dst, 64); + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + ecdh_complete, &result); + + err = crypto_kpp_generate_public_key(req); + + if (err == -EINPROGRESS) { + wait_for_completion(&result.completion); + err = result.err; + } + + /* Private key is not valid. Regenerate */ + if (err == -EINVAL) + continue; + + if (err < 0) + goto free_all; + else + break; + + } while (true); + + /* Keys are handed back in little endian as expected by Security + * Manager Protocol + */ + swap_digits((u64 *)tmp, (u64 *)public_key, 4); /* x */ + swap_digits((u64 *)&tmp[32], (u64 *)&public_key[32], 4); /* y */ + swap_digits((u64 *)private_key, (u64 *)tmp, 4); + memcpy(private_key, tmp, 32); + +free_all: + kzfree(buf); +free_req: + kpp_request_free(req); +free_kpp: + crypto_free_kpp(tfm); + return (err == 0); +} diff --git a/net/bluetooth/ecdh_helper.h b/net/bluetooth/ecdh_helper.h new file mode 100644 index 000000000000..7a423faf76e5 --- /dev/null +++ b/net/bluetooth/ecdh_helper.h @@ -0,0 +1,27 @@ +/* + * ECDH helper functions - KPP wrappings + * + * Copyright (C) 2017 Intel Corporation + * + * 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; + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + * CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + * COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + * SOFTWARE IS DISCLAIMED. + */ +#include + +bool compute_ecdh_secret(const u8 pub_a[64], const u8 priv_b[32], + u8 secret[32]); +bool generate_ecdh_keys(u8 public_key[64], u8 private_key[32]); diff --git a/net/bluetooth/selftest.c b/net/bluetooth/selftest.c index dc688f13e496..efef2815646e 100644 --- a/net/bluetooth/selftest.c +++ b/net/bluetooth/selftest.c @@ -26,7 +26,7 @@ #include #include -#include "ecc.h" +#include "ecdh_helper.h" #include "smp.h" #include "selftest.h" @@ -144,8 +144,8 @@ static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32], { u8 dhkey_a[32], dhkey_b[32]; - ecdh_shared_secret(pub_b, priv_a, dhkey_a); - ecdh_shared_secret(pub_a, priv_b, dhkey_b); + compute_ecdh_secret(pub_b, priv_a, dhkey_a); + compute_ecdh_secret(pub_a, priv_b, dhkey_b); if (memcmp(dhkey_a, dhkey, 32)) return -EINVAL; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index fae391f1871f..40e921a9cc17 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -31,7 +31,7 @@ #include #include -#include "ecc.h" +#include "ecdh_helper.h" #include "smp.h" #define SMP_DEV(hdev) \ @@ -570,7 +570,7 @@ int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16]) } else { while (true) { /* Generate local key pair for Secure Connections */ - if (!ecc_make_key(smp->local_pk, smp->local_sk)) + if (!generate_ecdh_keys(smp->local_pk, smp->local_sk)) return -EIO; /* This is unlikely, but we need to check that @@ -1896,7 +1896,7 @@ static u8 sc_send_public_key(struct smp_chan *smp) } else { while (true) { /* Generate local key pair for Secure Connections */ - if (!ecc_make_key(smp->local_pk, smp->local_sk)) + if (!generate_ecdh_keys(smp->local_pk, smp->local_sk)) return SMP_UNSPECIFIED; /* This is unlikely, but we need to check that @@ -2670,7 +2670,7 @@ static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb) SMP_DBG("Remote Public Key X: %32phN", smp->remote_pk); SMP_DBG("Remote Public Key Y: %32phN", smp->remote_pk + 32); - if (!ecdh_shared_secret(smp->remote_pk, smp->local_sk, smp->dhkey)) + if (!compute_ecdh_secret(smp->remote_pk, smp->local_sk, smp->dhkey)) return SMP_UNSPECIFIED; SMP_DBG("DHKey %32phN", smp->dhkey); -- cgit From 14933dc8d9964e46f1d5bd2a4dfe3d3be8e420e0 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 24 Apr 2017 19:42:34 -0700 Subject: sparc64: Improve 64-bit constant loading in eBPF JIT. Doing a full 64-bit decomposition is really stupid especially for simple values like 0 and -1. But if we are going to optimize this, go all the way and try for all 2 and 3 instruction sequences not requiring a temporary register as well. First we do the easy cases where it's a zero or sign extended 32-bit number (sethi+or, sethi+xor, respectively). Then we try to find a range of set bits we can load simply then shift up into place, in various ways. Then we try negating the constant and see if we can do a simple sequence using that with a xor at the end. (f.e. the range of set bits can't be loaded simply, but for the negated value it can) The final optimized strategy involves 4 instructions sequences not needing a temporary register. Otherwise we sadly fully decompose using a temp.. Example, from ALU64_XOR_K: 0x0000ffffffff0000 ^ 0x0 = 0x0000ffffffff0000: 0000000000000000 : 0: 9d e3 bf 50 save %sp, -176, %sp 4: 01 00 00 00 nop 8: 90 10 00 18 mov %i0, %o0 c: 13 3f ff ff sethi %hi(0xfffffc00), %o1 10: 92 12 63 ff or %o1, 0x3ff, %o1 ! ffffffff 14: 93 2a 70 10 sllx %o1, 0x10, %o1 18: 15 3f ff ff sethi %hi(0xfffffc00), %o2 1c: 94 12 a3 ff or %o2, 0x3ff, %o2 ! ffffffff 20: 95 2a b0 10 sllx %o2, 0x10, %o2 24: 92 1a 60 00 xor %o1, 0, %o1 28: 12 e2 40 8a cxbe %o1, %o2, 38 2c: 9a 10 20 02 mov 2, %o5 30: 10 60 00 03 b,pn %xcc, 3c 34: 01 00 00 00 nop 38: 9a 10 20 01 mov 1, %o5 ! 1 3c: 81 c7 e0 08 ret 40: 91 eb 40 00 restore %o5, %g0, %o0 Signed-off-by: David S. Miller --- arch/sparc/net/bpf_jit_comp_64.c | 249 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 245 insertions(+), 4 deletions(-) diff --git a/arch/sparc/net/bpf_jit_comp_64.c b/arch/sparc/net/bpf_jit_comp_64.c index 2b2f3c3335ce..ec7d10da94f0 100644 --- a/arch/sparc/net/bpf_jit_comp_64.c +++ b/arch/sparc/net/bpf_jit_comp_64.c @@ -28,6 +28,11 @@ static inline bool is_simm5(unsigned int value) return value + 0x10 < 0x20; } +static inline bool is_sethi(unsigned int value) +{ + return (value & ~0x3fffff) == 0; +} + static void bpf_flush_icache(void *start_, void *end_) { /* Cheetah's I-cache is fully coherent. */ @@ -367,16 +372,252 @@ static void emit_loadimm_sext(s32 K, unsigned int dest, struct jit_ctx *ctx) } } +static void analyze_64bit_constant(u32 high_bits, u32 low_bits, + int *hbsp, int *lbsp, int *abbasp) +{ + int lowest_bit_set, highest_bit_set, all_bits_between_are_set; + int i; + + lowest_bit_set = highest_bit_set = -1; + i = 0; + do { + if ((lowest_bit_set == -1) && ((low_bits >> i) & 1)) + lowest_bit_set = i; + if ((highest_bit_set == -1) && ((high_bits >> (32 - i - 1)) & 1)) + highest_bit_set = (64 - i - 1); + } while (++i < 32 && (highest_bit_set == -1 || + lowest_bit_set == -1)); + if (i == 32) { + i = 0; + do { + if (lowest_bit_set == -1 && ((high_bits >> i) & 1)) + lowest_bit_set = i + 32; + if (highest_bit_set == -1 && + ((low_bits >> (32 - i - 1)) & 1)) + highest_bit_set = 32 - i - 1; + } while (++i < 32 && (highest_bit_set == -1 || + lowest_bit_set == -1)); + } + + all_bits_between_are_set = 1; + for (i = lowest_bit_set; i <= highest_bit_set; i++) { + if (i < 32) { + if ((low_bits & (1 << i)) != 0) + continue; + } else { + if ((high_bits & (1 << (i - 32))) != 0) + continue; + } + all_bits_between_are_set = 0; + break; + } + *hbsp = highest_bit_set; + *lbsp = lowest_bit_set; + *abbasp = all_bits_between_are_set; +} + +static unsigned long create_simple_focus_bits(unsigned long high_bits, + unsigned long low_bits, + int lowest_bit_set, int shift) +{ + long hi, lo; + + if (lowest_bit_set < 32) { + lo = (low_bits >> lowest_bit_set) << shift; + hi = ((high_bits << (32 - lowest_bit_set)) << shift); + } else { + lo = 0; + hi = ((high_bits >> (lowest_bit_set - 32)) << shift); + } + return hi | lo; +} + +static bool const64_is_2insns(unsigned long high_bits, + unsigned long low_bits) +{ + int highest_bit_set, lowest_bit_set, all_bits_between_are_set; + + if (high_bits == 0 || high_bits == 0xffffffff) + return true; + + analyze_64bit_constant(high_bits, low_bits, + &highest_bit_set, &lowest_bit_set, + &all_bits_between_are_set); + + if ((highest_bit_set == 63 || lowest_bit_set == 0) && + all_bits_between_are_set != 0) + return true; + + if (highest_bit_set - lowest_bit_set < 21) + return true; + + return false; +} + +static void sparc_emit_set_const64_quick2(unsigned long high_bits, + unsigned long low_imm, + unsigned int dest, + int shift_count, struct jit_ctx *ctx) +{ + emit_loadimm32(high_bits, dest, ctx); + + /* Now shift it up into place. */ + emit_alu_K(SLLX, dest, shift_count, ctx); + + /* If there is a low immediate part piece, finish up by + * putting that in as well. + */ + if (low_imm != 0) + emit(OR | IMMED | RS1(dest) | S13(low_imm) | RD(dest), ctx); +} + static void emit_loadimm64(u64 K, unsigned int dest, struct jit_ctx *ctx) { + int all_bits_between_are_set, lowest_bit_set, highest_bit_set; unsigned int tmp = bpf2sparc[TMP_REG_1]; - u32 high_part = (K >> 32); - u32 low_part = (K & 0xffffffff); + u32 low_bits = (K & 0xffffffff); + u32 high_bits = (K >> 32); + + /* These two tests also take care of all of the one + * instruction cases. + */ + if (high_bits == 0xffffffff && (low_bits & 0x80000000)) + return emit_loadimm_sext(K, dest, ctx); + if (high_bits == 0x00000000) + return emit_loadimm32(K, dest, ctx); + + analyze_64bit_constant(high_bits, low_bits, &highest_bit_set, + &lowest_bit_set, &all_bits_between_are_set); + + /* 1) mov -1, %reg + * sllx %reg, shift, %reg + * 2) mov -1, %reg + * srlx %reg, shift, %reg + * 3) mov some_small_const, %reg + * sllx %reg, shift, %reg + */ + if (((highest_bit_set == 63 || lowest_bit_set == 0) && + all_bits_between_are_set != 0) || + ((highest_bit_set - lowest_bit_set) < 12)) { + int shift = lowest_bit_set; + long the_const = -1; + + if ((highest_bit_set != 63 && lowest_bit_set != 0) || + all_bits_between_are_set == 0) { + the_const = + create_simple_focus_bits(high_bits, low_bits, + lowest_bit_set, 0); + } else if (lowest_bit_set == 0) + shift = -(63 - highest_bit_set); + + emit(OR | IMMED | RS1(G0) | S13(the_const) | RD(dest), ctx); + if (shift > 0) + emit_alu_K(SLLX, dest, shift, ctx); + else if (shift < 0) + emit_alu_K(SRLX, dest, -shift, ctx); + + return; + } + + /* Now a range of 22 or less bits set somewhere. + * 1) sethi %hi(focus_bits), %reg + * sllx %reg, shift, %reg + * 2) sethi %hi(focus_bits), %reg + * srlx %reg, shift, %reg + */ + if ((highest_bit_set - lowest_bit_set) < 21) { + unsigned long focus_bits = + create_simple_focus_bits(high_bits, low_bits, + lowest_bit_set, 10); + + emit(SETHI(focus_bits, dest), ctx); + + /* If lowest_bit_set == 10 then a sethi alone could + * have done it. + */ + if (lowest_bit_set < 10) + emit_alu_K(SRLX, dest, 10 - lowest_bit_set, ctx); + else if (lowest_bit_set > 10) + emit_alu_K(SLLX, dest, lowest_bit_set - 10, ctx); + return; + } + + /* Ok, now 3 instruction sequences. */ + if (low_bits == 0) { + emit_loadimm32(high_bits, dest, ctx); + emit_alu_K(SLLX, dest, 32, ctx); + return; + } + + /* We may be able to do something quick + * when the constant is negated, so try that. + */ + if (const64_is_2insns((~high_bits) & 0xffffffff, + (~low_bits) & 0xfffffc00)) { + /* NOTE: The trailing bits get XOR'd so we need the + * non-negated bits, not the negated ones. + */ + unsigned long trailing_bits = low_bits & 0x3ff; + + if ((((~high_bits) & 0xffffffff) == 0 && + ((~low_bits) & 0x80000000) == 0) || + (((~high_bits) & 0xffffffff) == 0xffffffff && + ((~low_bits) & 0x80000000) != 0)) { + unsigned long fast_int = (~low_bits & 0xffffffff); + + if ((is_sethi(fast_int) && + (~high_bits & 0xffffffff) == 0)) { + emit(SETHI(fast_int, dest), ctx); + } else if (is_simm13(fast_int)) { + emit(OR | IMMED | RS1(G0) | S13(fast_int) | RD(dest), ctx); + } else { + emit_loadimm64(fast_int, dest, ctx); + } + } else { + u64 n = ((~low_bits) & 0xfffffc00) | + (((unsigned long)((~high_bits) & 0xffffffff))<<32); + emit_loadimm64(n, dest, ctx); + } + + low_bits = -0x400 | trailing_bits; + + emit(XOR | IMMED | RS1(dest) | S13(low_bits) | RD(dest), ctx); + return; + } + + /* 1) sethi %hi(xxx), %reg + * or %reg, %lo(xxx), %reg + * sllx %reg, yyy, %reg + */ + if ((highest_bit_set - lowest_bit_set) < 32) { + unsigned long focus_bits = + create_simple_focus_bits(high_bits, low_bits, + lowest_bit_set, 0); + + /* So what we know is that the set bits straddle the + * middle of the 64-bit word. + */ + sparc_emit_set_const64_quick2(focus_bits, 0, dest, + lowest_bit_set, ctx); + return; + } + + /* 1) sethi %hi(high_bits), %reg + * or %reg, %lo(high_bits), %reg + * sllx %reg, 32, %reg + * or %reg, low_bits, %reg + */ + if (is_simm13(low_bits) && ((int)low_bits > 0)) { + sparc_emit_set_const64_quick2(high_bits, low_bits, + dest, 32, ctx); + return; + } + /* Oh well, we tried... Do a full 64-bit decomposition. */ ctx->tmp_1_used = true; - emit_set_const(high_part, tmp, ctx); - emit_set_const(low_part, dest, ctx); + emit_loadimm32(high_bits, tmp, ctx); + emit_loadimm32(low_bits, dest, ctx); emit_alu_K(SLLX, tmp, 32, ctx); emit(OR | RS1(dest) | RS2(tmp) | RD(dest), ctx); } -- cgit From e4e8452a4ab304913c4b4242ba06bc4624f14a84 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Mon, 24 Apr 2017 13:49:26 -0400 Subject: virtio-net: napi helper functions Prepare virtio-net for tx napi by converting existing napi code to use helper functions. This also deduplicates some logic. Signed-off-by: Willem de Bruijn Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 65 ++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 666ada6130ab..b9c1df29892c 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -239,6 +239,26 @@ static struct page *get_a_page(struct receive_queue *rq, gfp_t gfp_mask) return p; } +static void virtqueue_napi_schedule(struct napi_struct *napi, + struct virtqueue *vq) +{ + if (napi_schedule_prep(napi)) { + virtqueue_disable_cb(vq); + __napi_schedule(napi); + } +} + +static void virtqueue_napi_complete(struct napi_struct *napi, + struct virtqueue *vq, int processed) +{ + int opaque; + + opaque = virtqueue_enable_cb_prepare(vq); + if (napi_complete_done(napi, processed) && + unlikely(virtqueue_poll(vq, opaque))) + virtqueue_napi_schedule(napi, vq); +} + static void skb_xmit_done(struct virtqueue *vq) { struct virtnet_info *vi = vq->vdev->priv; @@ -936,27 +956,20 @@ static void skb_recv_done(struct virtqueue *rvq) struct virtnet_info *vi = rvq->vdev->priv; struct receive_queue *rq = &vi->rq[vq2rxq(rvq)]; - /* Schedule NAPI, Suppress further interrupts if successful. */ - if (napi_schedule_prep(&rq->napi)) { - virtqueue_disable_cb(rvq); - __napi_schedule(&rq->napi); - } + virtqueue_napi_schedule(&rq->napi, rvq); } -static void virtnet_napi_enable(struct receive_queue *rq) +static void virtnet_napi_enable(struct virtqueue *vq, struct napi_struct *napi) { - napi_enable(&rq->napi); + napi_enable(napi); /* If all buffers were filled by other side before we napi_enabled, we - * won't get another interrupt, so process any outstanding packets - * now. virtnet_poll wants re-enable the queue, so we disable here. - * We synchronize against interrupts via NAPI_STATE_SCHED */ - if (napi_schedule_prep(&rq->napi)) { - virtqueue_disable_cb(rq->vq); - local_bh_disable(); - __napi_schedule(&rq->napi); - local_bh_enable(); - } + * won't get another interrupt, so process any outstanding packets now. + * Call local_bh_enable after to trigger softIRQ processing. + */ + local_bh_disable(); + virtqueue_napi_schedule(napi, vq); + local_bh_enable(); } static void refill_work(struct work_struct *work) @@ -971,7 +984,7 @@ static void refill_work(struct work_struct *work) napi_disable(&rq->napi); still_empty = !try_fill_recv(vi, rq, GFP_KERNEL); - virtnet_napi_enable(rq); + virtnet_napi_enable(rq->vq, &rq->napi); /* In theory, this can happen: if we don't get any buffers in * we will *never* try to fill again. @@ -1011,21 +1024,13 @@ static int virtnet_poll(struct napi_struct *napi, int budget) { struct receive_queue *rq = container_of(napi, struct receive_queue, napi); - unsigned int r, received; + unsigned int received; received = virtnet_receive(rq, budget); /* Out of packets? */ - if (received < budget) { - r = virtqueue_enable_cb_prepare(rq->vq); - if (napi_complete_done(napi, received)) { - if (unlikely(virtqueue_poll(rq->vq, r)) && - napi_schedule_prep(napi)) { - virtqueue_disable_cb(rq->vq); - __napi_schedule(napi); - } - } - } + if (received < budget) + virtqueue_napi_complete(napi, rq->vq, received); return received; } @@ -1040,7 +1045,7 @@ static int virtnet_open(struct net_device *dev) /* Make sure we have some buffers: if oom use wq. */ if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) schedule_delayed_work(&vi->refill, 0); - virtnet_napi_enable(&vi->rq[i]); + virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); } return 0; @@ -1747,7 +1752,7 @@ static int virtnet_restore_up(struct virtio_device *vdev) schedule_delayed_work(&vi->refill, 0); for (i = 0; i < vi->max_queue_pairs; i++) - virtnet_napi_enable(&vi->rq[i]); + virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); } netif_device_attach(vi->dev); -- cgit From b92f1e6751a6aaaf0b1e05128661ce0f23ed3695 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Mon, 24 Apr 2017 13:49:27 -0400 Subject: virtio-net: transmit napi Convert virtio-net to a standard napi tx completion path. This enables better TCP pacing using TCP small queues and increases single stream throughput. The virtio-net driver currently cleans tx descriptors on transmission of new packets in ndo_start_xmit. Latency depends on new traffic, so is unbounded. To avoid deadlock when a socket reaches its snd limit, packets are orphaned on tranmission. This breaks socket backpressure, including TSQ. Napi increases the number of interrupts generated compared to the current model, which keeps interrupts disabled as long as the ring has enough free descriptors. Keep tx napi optional and disabled for now. Follow-on patches will reduce the interrupt cost. Signed-off-by: Willem de Bruijn Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 76 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index b9c1df29892c..356d18481ee4 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -33,9 +33,10 @@ static int napi_weight = NAPI_POLL_WEIGHT; module_param(napi_weight, int, 0444); -static bool csum = true, gso = true; +static bool csum = true, gso = true, napi_tx; module_param(csum, bool, 0444); module_param(gso, bool, 0444); +module_param(napi_tx, bool, 0644); /* FIXME: MTU in config. */ #define GOOD_PACKET_LEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN) @@ -86,6 +87,8 @@ struct send_queue { /* Name of the send queue: output.$index */ char name[40]; + + struct napi_struct napi; }; /* Internal representation of a receive virtqueue */ @@ -262,12 +265,16 @@ static void virtqueue_napi_complete(struct napi_struct *napi, static void skb_xmit_done(struct virtqueue *vq) { struct virtnet_info *vi = vq->vdev->priv; + struct napi_struct *napi = &vi->sq[vq2txq(vq)].napi; /* Suppress further interrupts. */ virtqueue_disable_cb(vq); - /* We were probably waiting for more output buffers. */ - netif_wake_subqueue(vi->dev, vq2txq(vq)); + if (napi->weight) + virtqueue_napi_schedule(napi, vq); + else + /* We were probably waiting for more output buffers. */ + netif_wake_subqueue(vi->dev, vq2txq(vq)); } static unsigned int mergeable_ctx_to_buf_truesize(unsigned long mrg_ctx) @@ -972,6 +979,24 @@ static void virtnet_napi_enable(struct virtqueue *vq, struct napi_struct *napi) local_bh_enable(); } +static void virtnet_napi_tx_enable(struct virtnet_info *vi, + struct virtqueue *vq, + struct napi_struct *napi) +{ + if (!napi->weight) + return; + + /* Tx napi touches cachelines on the cpu handling tx interrupts. Only + * enable the feature if this is likely affine with the transmit path. + */ + if (!vi->affinity_hint_set) { + napi->weight = 0; + return; + } + + return virtnet_napi_enable(vq, napi); +} + static void refill_work(struct work_struct *work) { struct virtnet_info *vi = @@ -1046,6 +1071,7 @@ static int virtnet_open(struct net_device *dev) if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) schedule_delayed_work(&vi->refill, 0); virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); + virtnet_napi_tx_enable(vi, vi->sq[i].vq, &vi->sq[i].napi); } return 0; @@ -1081,6 +1107,24 @@ static void free_old_xmit_skbs(struct send_queue *sq) u64_stats_update_end(&stats->tx_syncp); } +static int virtnet_poll_tx(struct napi_struct *napi, int budget) +{ + struct send_queue *sq = container_of(napi, struct send_queue, napi); + struct virtnet_info *vi = sq->vq->vdev->priv; + struct netdev_queue *txq = netdev_get_tx_queue(vi->dev, vq2txq(sq->vq)); + + __netif_tx_lock(txq, raw_smp_processor_id()); + free_old_xmit_skbs(sq); + __netif_tx_unlock(txq); + + virtqueue_napi_complete(napi, sq->vq, 0); + + if (sq->vq->num_free >= 2 + MAX_SKB_FRAGS) + netif_tx_wake_queue(txq); + + return 0; +} + static int xmit_skb(struct send_queue *sq, struct sk_buff *skb) { struct virtio_net_hdr_mrg_rxbuf *hdr; @@ -1130,6 +1174,7 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) int err; struct netdev_queue *txq = netdev_get_tx_queue(dev, qnum); bool kick = !skb->xmit_more; + bool use_napi = sq->napi.weight; /* Free up any pending old buffers before queueing new ones. */ free_old_xmit_skbs(sq); @@ -1152,8 +1197,10 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) } /* Don't wait up for transmitted skbs to be freed. */ - skb_orphan(skb); - nf_reset(skb); + if (!use_napi) { + skb_orphan(skb); + nf_reset(skb); + } /* If running out of space, stop queue to avoid getting packets that we * are then unable to transmit. @@ -1167,7 +1214,8 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) */ if (sq->vq->num_free < 2+MAX_SKB_FRAGS) { netif_stop_subqueue(dev, qnum); - if (unlikely(!virtqueue_enable_cb_delayed(sq->vq))) { + if (!use_napi && + unlikely(!virtqueue_enable_cb_delayed(sq->vq))) { /* More just got used, free them then recheck. */ free_old_xmit_skbs(sq); if (sq->vq->num_free >= 2+MAX_SKB_FRAGS) { @@ -1371,8 +1419,10 @@ static int virtnet_close(struct net_device *dev) /* Make sure refill_work doesn't re-enable napi! */ cancel_delayed_work_sync(&vi->refill); - for (i = 0; i < vi->max_queue_pairs; i++) + for (i = 0; i < vi->max_queue_pairs; i++) { napi_disable(&vi->rq[i].napi); + napi_disable(&vi->sq[i].napi); + } return 0; } @@ -1727,8 +1777,10 @@ static void virtnet_freeze_down(struct virtio_device *vdev) cancel_delayed_work_sync(&vi->refill); if (netif_running(vi->dev)) { - for (i = 0; i < vi->max_queue_pairs; i++) + for (i = 0; i < vi->max_queue_pairs; i++) { napi_disable(&vi->rq[i].napi); + napi_disable(&vi->sq[i].napi); + } } } @@ -1751,8 +1803,11 @@ static int virtnet_restore_up(struct virtio_device *vdev) if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) schedule_delayed_work(&vi->refill, 0); - for (i = 0; i < vi->max_queue_pairs; i++) + for (i = 0; i < vi->max_queue_pairs; i++) { virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); + virtnet_napi_tx_enable(vi, vi->sq[i].vq, + &vi->sq[i].napi); + } } netif_device_attach(vi->dev); @@ -1957,6 +2012,7 @@ static void virtnet_free_queues(struct virtnet_info *vi) for (i = 0; i < vi->max_queue_pairs; i++) { napi_hash_del(&vi->rq[i].napi); netif_napi_del(&vi->rq[i].napi); + netif_napi_del(&vi->sq[i].napi); } /* We called napi_hash_del() before netif_napi_del(), @@ -2142,6 +2198,8 @@ static int virtnet_alloc_queues(struct virtnet_info *vi) vi->rq[i].pages = NULL; netif_napi_add(vi->dev, &vi->rq[i].napi, virtnet_poll, napi_weight); + netif_napi_add(vi->dev, &vi->sq[i].napi, virtnet_poll_tx, + napi_tx ? napi_weight : 0); sg_init_table(vi->rq[i].sg, ARRAY_SIZE(vi->rq[i].sg)); ewma_pkt_len_init(&vi->rq[i].mrg_avg_pkt_len); -- cgit From ea7735d97ba9064c448664429e249991ccd8aa77 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Mon, 24 Apr 2017 13:49:28 -0400 Subject: virtio-net: move free_old_xmit_skbs An upcoming patch will call free_old_xmit_skbs indirectly from virtnet_poll. Move the function above this to avoid having to introduce a forward declaration. This is a pure move: no code changes. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 60 ++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 356d18481ee4..4ec79e5d7a86 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1045,6 +1045,36 @@ static int virtnet_receive(struct receive_queue *rq, int budget) return received; } +static void free_old_xmit_skbs(struct send_queue *sq) +{ + struct sk_buff *skb; + unsigned int len; + struct virtnet_info *vi = sq->vq->vdev->priv; + struct virtnet_stats *stats = this_cpu_ptr(vi->stats); + unsigned int packets = 0; + unsigned int bytes = 0; + + while ((skb = virtqueue_get_buf(sq->vq, &len)) != NULL) { + pr_debug("Sent skb %p\n", skb); + + bytes += skb->len; + packets++; + + dev_kfree_skb_any(skb); + } + + /* Avoid overhead when no packets have been processed + * happens when called speculatively from start_xmit. + */ + if (!packets) + return; + + u64_stats_update_begin(&stats->tx_syncp); + stats->tx_bytes += bytes; + stats->tx_packets += packets; + u64_stats_update_end(&stats->tx_syncp); +} + static int virtnet_poll(struct napi_struct *napi, int budget) { struct receive_queue *rq = @@ -1077,36 +1107,6 @@ static int virtnet_open(struct net_device *dev) return 0; } -static void free_old_xmit_skbs(struct send_queue *sq) -{ - struct sk_buff *skb; - unsigned int len; - struct virtnet_info *vi = sq->vq->vdev->priv; - struct virtnet_stats *stats = this_cpu_ptr(vi->stats); - unsigned int packets = 0; - unsigned int bytes = 0; - - while ((skb = virtqueue_get_buf(sq->vq, &len)) != NULL) { - pr_debug("Sent skb %p\n", skb); - - bytes += skb->len; - packets++; - - dev_kfree_skb_any(skb); - } - - /* Avoid overhead when no packets have been processed - * happens when called speculatively from start_xmit. - */ - if (!packets) - return; - - u64_stats_update_begin(&stats->tx_syncp); - stats->tx_bytes += bytes; - stats->tx_packets += packets; - u64_stats_update_end(&stats->tx_syncp); -} - static int virtnet_poll_tx(struct napi_struct *napi, int budget) { struct send_queue *sq = container_of(napi, struct send_queue, napi); -- cgit From 7b0411ef4aa69c9256d6a2c289d0a2b320414633 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Mon, 24 Apr 2017 13:49:29 -0400 Subject: virtio-net: clean tx descriptors from rx napi Amortize the cost of virtual interrupts by doing both rx and tx work on reception of a receive interrupt if tx napi is enabled. With VIRTIO_F_EVENT_IDX, this suppresses most explicit tx completion interrupts for bidirectional workloads. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 4ec79e5d7a86..9dd978f34c1f 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1075,12 +1075,33 @@ static void free_old_xmit_skbs(struct send_queue *sq) u64_stats_update_end(&stats->tx_syncp); } +static void virtnet_poll_cleantx(struct receive_queue *rq) +{ + struct virtnet_info *vi = rq->vq->vdev->priv; + unsigned int index = vq2rxq(rq->vq); + struct send_queue *sq = &vi->sq[index]; + struct netdev_queue *txq = netdev_get_tx_queue(vi->dev, index); + + if (!sq->napi.weight) + return; + + if (__netif_tx_trylock(txq)) { + free_old_xmit_skbs(sq); + __netif_tx_unlock(txq); + } + + if (sq->vq->num_free >= 2 + MAX_SKB_FRAGS) + netif_tx_wake_queue(txq); +} + static int virtnet_poll(struct napi_struct *napi, int budget) { struct receive_queue *rq = container_of(napi, struct receive_queue, napi); unsigned int received; + virtnet_poll_cleantx(rq); + received = virtnet_receive(rq, budget); /* Out of packets? */ -- cgit From bdb12e0d2ffc84a0248788cdf6cfbff86ee34602 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Mon, 24 Apr 2017 13:49:30 -0400 Subject: virtio-net: keep tx interrupts disabled unless kick Tx napi mode increases the rate of transmit interrupts. Suppress some by masking interrupts while more packets are expected. The interrupts will be reenabled before the last packet is sent. This optimization reduces the througput drop with tx napi for unidirectional flows such as UDP_STREAM that do not benefit from cleaning tx completions in the the receive napi handler. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 9dd978f34c1f..003143835766 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1200,6 +1200,9 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) /* Free up any pending old buffers before queueing new ones. */ free_old_xmit_skbs(sq); + if (use_napi && kick) + virtqueue_enable_cb_delayed(sq->vq); + /* timestamp packet in software */ skb_tx_timestamp(skb); -- cgit From 81c5e13d90db1eb1bf326e5b1f6293a0d85e966d Mon Sep 17 00:00:00 2001 From: Stephane Grosjean Date: Thu, 19 Jan 2017 16:31:04 +0100 Subject: can: peak: fix usage of usb specific data type This patch fixes the wrong usage of a specific USB data type into a common header file. This common header file is intended to define the common data types and values that define access to the PEAK-System CAN-FD IP, whatever the PC interface is. Signed-off-by: Stephane Grosjean Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/peak_usb/pcan_ucan.h | 5 ++--- drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 23 ++++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/net/can/usb/peak_usb/pcan_ucan.h b/drivers/net/can/usb/peak_usb/pcan_ucan.h index 2147678f0225..104a4671452d 100644 --- a/drivers/net/can/usb/peak_usb/pcan_ucan.h +++ b/drivers/net/can/usb/peak_usb/pcan_ucan.h @@ -213,10 +213,9 @@ struct __packed pucan_tx_msg { }; /* build the cmd opcode_channel field with respect to the correct endianness */ -static inline __le16 pucan_cmd_opcode_channel(struct peak_usb_device *dev, - int opcode) +static inline __le16 pucan_cmd_opcode_channel(int index, int opcode) { - return cpu_to_le16(((dev->ctrl_idx) << 12) | ((opcode) & 0x3ff)); + return cpu_to_le16(((index) << 12) | ((opcode) & 0x3ff)); } /* return the channel number part from any received message channel_dlc field */ diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c index 304732550f0a..64cba85230f8 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -238,7 +238,7 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf) /* 1st, reset error counters: */ prc = (struct pucan_wr_err_cnt *)pc; - prc->opcode_channel = pucan_cmd_opcode_channel(dev, + prc->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PUCAN_CMD_WR_ERR_CNT); /* select both counters */ @@ -257,9 +257,10 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf) puo->opcode_channel = (dev->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) ? - pucan_cmd_opcode_channel(dev, + pucan_cmd_opcode_channel(dev->ctrl_idx, PUCAN_CMD_CLR_DIS_OPTION) : - pucan_cmd_opcode_channel(dev, PUCAN_CMD_SET_EN_OPTION); + pucan_cmd_opcode_channel(dev->ctrl_idx, + PUCAN_CMD_SET_EN_OPTION); puo->options = cpu_to_le16(PUCAN_OPTION_CANDFDISO); @@ -274,7 +275,7 @@ static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf) /* next, go back to operational mode */ cmd = (struct pucan_command *)pc; - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ? PUCAN_CMD_LISTEN_ONLY_MODE : PUCAN_CMD_NORMAL_MODE); @@ -296,7 +297,7 @@ static int pcan_usb_fd_set_bus(struct peak_usb_device *dev, u8 onoff) struct pucan_command *cmd = (struct pucan_command *)pc; /* build cmd to go back to reset mode */ - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PUCAN_CMD_RESET_MODE); l = sizeof(struct pucan_command); } @@ -332,7 +333,7 @@ static int pcan_usb_fd_set_filter_std(struct peak_usb_device *dev, int idx, } for (i = idx; i < n; i++, cmd++) { - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PUCAN_CMD_FILTER_STD); cmd->idx = cpu_to_le16(i); cmd->mask = cpu_to_le32(mask); @@ -352,7 +353,7 @@ static int pcan_usb_fd_set_options(struct peak_usb_device *dev, { struct pcan_ufd_options *cmd = pcan_usb_fd_cmd_buffer(dev); - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, (onoff) ? PUCAN_CMD_SET_EN_OPTION : PUCAN_CMD_CLR_DIS_OPTION); @@ -368,7 +369,7 @@ static int pcan_usb_fd_set_can_led(struct peak_usb_device *dev, u8 led_mode) { struct pcan_ufd_led *cmd = pcan_usb_fd_cmd_buffer(dev); - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PCAN_UFD_CMD_LED_SET); cmd->mode = led_mode; @@ -382,7 +383,7 @@ static int pcan_usb_fd_set_clock_domain(struct peak_usb_device *dev, { struct pcan_ufd_clock *cmd = pcan_usb_fd_cmd_buffer(dev); - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PCAN_UFD_CMD_CLK_SET); cmd->mode = clk_mode; @@ -396,7 +397,7 @@ static int pcan_usb_fd_set_bittiming_slow(struct peak_usb_device *dev, { struct pucan_timing_slow *cmd = pcan_usb_fd_cmd_buffer(dev); - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PUCAN_CMD_TIMING_SLOW); cmd->sjw_t = PUCAN_TSLOW_SJW_T(bt->sjw - 1, dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES); @@ -417,7 +418,7 @@ static int pcan_usb_fd_set_bittiming_fast(struct peak_usb_device *dev, { struct pucan_timing_fast *cmd = pcan_usb_fd_cmd_buffer(dev); - cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + cmd->opcode_channel = pucan_cmd_opcode_channel(dev->ctrl_idx, PUCAN_CMD_TIMING_FAST); cmd->sjw = PUCAN_TFAST_SJW(bt->sjw - 1); cmd->tseg2 = PUCAN_TFAST_TSEG2(bt->phase_seg2 - 1); -- cgit From 113ab88b2b689b431904e1d753e20390529ed204 Mon Sep 17 00:00:00 2001 From: Stephane Grosjean Date: Thu, 19 Jan 2017 16:31:05 +0100 Subject: can: peak: fix usage of const qualifier in pointers args Fixes the usage of the const qualifier in the memory pointer arguments of the declared inline functions. By changing the line containing "const", this patch also changes the name of the arg into a more usual one. Signed-off-by: Stephane Grosjean Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/peak_usb/pcan_ucan.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/can/usb/peak_usb/pcan_ucan.h b/drivers/net/can/usb/peak_usb/pcan_ucan.h index 104a4671452d..25e20ef2fef8 100644 --- a/drivers/net/can/usb/peak_usb/pcan_ucan.h +++ b/drivers/net/can/usb/peak_usb/pcan_ucan.h @@ -219,25 +219,25 @@ static inline __le16 pucan_cmd_opcode_channel(int index, int opcode) } /* return the channel number part from any received message channel_dlc field */ -static inline int pucan_msg_get_channel(struct pucan_rx_msg *rm) +static inline int pucan_msg_get_channel(const struct pucan_rx_msg *msg) { - return rm->channel_dlc & 0xf; + return msg->channel_dlc & 0xf; } /* return the dlc value from any received message channel_dlc field */ -static inline int pucan_msg_get_dlc(struct pucan_rx_msg *rm) +static inline int pucan_msg_get_dlc(const struct pucan_rx_msg *msg) { - return rm->channel_dlc >> 4; + return msg->channel_dlc >> 4; } -static inline int pucan_ermsg_get_channel(struct pucan_error_msg *em) +static inline int pucan_ermsg_get_channel(const struct pucan_error_msg *msg) { - return em->channel_type_d & 0x0f; + return msg->channel_type_d & 0x0f; } -static inline int pucan_stmsg_get_channel(struct pucan_status_msg *sm) +static inline int pucan_stmsg_get_channel(const struct pucan_status_msg *msg) { - return sm->channel_p_w_b & 0x0f; + return msg->channel_p_w_b & 0x0f; } #endif -- cgit From c3df7c5755ee1a53cd56a4efcf3426334ab9eea4 Mon Sep 17 00:00:00 2001 From: Stephane Grosjean Date: Thu, 19 Jan 2017 16:31:06 +0100 Subject: can: peak: move header file to new can common subdir The CAN-FD IP from PEAK-System runs into several kinds of PC CAN-FD interfaces. Up to now, only the USB CAN-FD adapters were supported by the Kernel. In order to prepare the adding of some new non-USB CAN-FD interfaces, this patch moves - and rename - the IP definitions file from its private (usb) sub-directory into a - newly created - CAN specific one. Signed-off-by: Stephane Grosjean Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/peak_usb/pcan_ucan.h | 243 ----------------------------- drivers/net/can/usb/peak_usb/pcan_usb_fd.c | 2 +- include/linux/can/dev/peak_canfd.h | 243 +++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+), 244 deletions(-) delete mode 100644 drivers/net/can/usb/peak_usb/pcan_ucan.h create mode 100644 include/linux/can/dev/peak_canfd.h diff --git a/drivers/net/can/usb/peak_usb/pcan_ucan.h b/drivers/net/can/usb/peak_usb/pcan_ucan.h deleted file mode 100644 index 25e20ef2fef8..000000000000 --- a/drivers/net/can/usb/peak_usb/pcan_ucan.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - * CAN driver for PEAK System micro-CAN based adapters - * - * Copyright (C) 2003-2011 PEAK System-Technik GmbH - * Copyright (C) 2011-2013 Stephane Grosjean - * - * 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 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ -#ifndef PUCAN_H -#define PUCAN_H - -/* uCAN commands opcodes list (low-order 10 bits) */ -#define PUCAN_CMD_NOP 0x000 -#define PUCAN_CMD_RESET_MODE 0x001 -#define PUCAN_CMD_NORMAL_MODE 0x002 -#define PUCAN_CMD_LISTEN_ONLY_MODE 0x003 -#define PUCAN_CMD_TIMING_SLOW 0x004 -#define PUCAN_CMD_TIMING_FAST 0x005 -#define PUCAN_CMD_FILTER_STD 0x008 -#define PUCAN_CMD_TX_ABORT 0x009 -#define PUCAN_CMD_WR_ERR_CNT 0x00a -#define PUCAN_CMD_SET_EN_OPTION 0x00b -#define PUCAN_CMD_CLR_DIS_OPTION 0x00c -#define PUCAN_CMD_END_OF_COLLECTION 0x3ff - -/* uCAN received messages list */ -#define PUCAN_MSG_CAN_RX 0x0001 -#define PUCAN_MSG_ERROR 0x0002 -#define PUCAN_MSG_STATUS 0x0003 -#define PUCAN_MSG_BUSLOAD 0x0004 -#define PUCAN_MSG_CAN_TX 0x1000 - -/* uCAN command common header */ -struct __packed pucan_command { - __le16 opcode_channel; - u16 args[3]; -}; - -#define PUCAN_TSLOW_BRP_BITS 10 -#define PUCAN_TSLOW_TSGEG1_BITS 8 -#define PUCAN_TSLOW_TSGEG2_BITS 7 -#define PUCAN_TSLOW_SJW_BITS 7 - -#define PUCAN_TSLOW_BRP_MASK ((1 << PUCAN_TSLOW_BRP_BITS) - 1) -#define PUCAN_TSLOW_TSEG1_MASK ((1 << PUCAN_TSLOW_TSGEG1_BITS) - 1) -#define PUCAN_TSLOW_TSEG2_MASK ((1 << PUCAN_TSLOW_TSGEG2_BITS) - 1) -#define PUCAN_TSLOW_SJW_MASK ((1 << PUCAN_TSLOW_SJW_BITS) - 1) - -/* uCAN TIMING_SLOW command fields */ -#define PUCAN_TSLOW_SJW_T(s, t) (((s) & PUCAN_TSLOW_SJW_MASK) | \ - ((!!(t)) << 7)) -#define PUCAN_TSLOW_TSEG2(t) ((t) & PUCAN_TSLOW_TSEG2_MASK) -#define PUCAN_TSLOW_TSEG1(t) ((t) & PUCAN_TSLOW_TSEG1_MASK) -#define PUCAN_TSLOW_BRP(b) ((b) & PUCAN_TSLOW_BRP_MASK) - -struct __packed pucan_timing_slow { - __le16 opcode_channel; - - u8 ewl; /* Error Warning limit */ - u8 sjw_t; /* Sync Jump Width + Triple sampling */ - u8 tseg2; /* Timing SEGment 2 */ - u8 tseg1; /* Timing SEGment 1 */ - - __le16 brp; /* BaudRate Prescaler */ -}; - -#define PUCAN_TFAST_BRP_BITS 10 -#define PUCAN_TFAST_TSGEG1_BITS 5 -#define PUCAN_TFAST_TSGEG2_BITS 4 -#define PUCAN_TFAST_SJW_BITS 4 - -#define PUCAN_TFAST_BRP_MASK ((1 << PUCAN_TFAST_BRP_BITS) - 1) -#define PUCAN_TFAST_TSEG1_MASK ((1 << PUCAN_TFAST_TSGEG1_BITS) - 1) -#define PUCAN_TFAST_TSEG2_MASK ((1 << PUCAN_TFAST_TSGEG2_BITS) - 1) -#define PUCAN_TFAST_SJW_MASK ((1 << PUCAN_TFAST_SJW_BITS) - 1) - -/* uCAN TIMING_FAST command fields */ -#define PUCAN_TFAST_SJW(s) ((s) & PUCAN_TFAST_SJW_MASK) -#define PUCAN_TFAST_TSEG2(t) ((t) & PUCAN_TFAST_TSEG2_MASK) -#define PUCAN_TFAST_TSEG1(t) ((t) & PUCAN_TFAST_TSEG1_MASK) -#define PUCAN_TFAST_BRP(b) ((b) & PUCAN_TFAST_BRP_MASK) - -struct __packed pucan_timing_fast { - __le16 opcode_channel; - - u8 unused; - u8 sjw; /* Sync Jump Width */ - u8 tseg2; /* Timing SEGment 2 */ - u8 tseg1; /* Timing SEGment 1 */ - - __le16 brp; /* BaudRate Prescaler */ -}; - -/* uCAN FILTER_STD command fields */ -#define PUCAN_FLTSTD_ROW_IDX_BITS 6 - -struct __packed pucan_filter_std { - __le16 opcode_channel; - - __le16 idx; - __le32 mask; /* CAN-ID bitmask in idx range */ -}; - -/* uCAN WR_ERR_CNT command fields */ -#define PUCAN_WRERRCNT_TE 0x4000 /* Tx error cntr write Enable */ -#define PUCAN_WRERRCNT_RE 0x8000 /* Rx error cntr write Enable */ - -struct __packed pucan_wr_err_cnt { - __le16 opcode_channel; - - __le16 sel_mask; - u8 tx_counter; /* Tx error counter new value */ - u8 rx_counter; /* Rx error counter new value */ - - u16 unused; -}; - -/* uCAN SET_EN/CLR_DIS _OPTION command fields */ -#define PUCAN_OPTION_ERROR 0x0001 -#define PUCAN_OPTION_BUSLOAD 0x0002 -#define PUCAN_OPTION_CANDFDISO 0x0004 - -struct __packed pucan_options { - __le16 opcode_channel; - - __le16 options; - u32 unused; -}; - -/* uCAN received messages global format */ -struct __packed pucan_msg { - __le16 size; - __le16 type; - __le32 ts_low; - __le32 ts_high; -}; - -/* uCAN flags for CAN/CANFD messages */ -#define PUCAN_MSG_SELF_RECEIVE 0x80 -#define PUCAN_MSG_ERROR_STATE_IND 0x40 /* error state indicator */ -#define PUCAN_MSG_BITRATE_SWITCH 0x20 /* bitrate switch */ -#define PUCAN_MSG_EXT_DATA_LEN 0x10 /* extended data length */ -#define PUCAN_MSG_SINGLE_SHOT 0x08 -#define PUCAN_MSG_LOOPED_BACK 0x04 -#define PUCAN_MSG_EXT_ID 0x02 -#define PUCAN_MSG_RTR 0x01 - -struct __packed pucan_rx_msg { - __le16 size; - __le16 type; - __le32 ts_low; - __le32 ts_high; - __le32 tag_low; - __le32 tag_high; - u8 channel_dlc; - u8 client; - __le16 flags; - __le32 can_id; - u8 d[0]; -}; - -/* uCAN error types */ -#define PUCAN_ERMSG_BIT_ERROR 0 -#define PUCAN_ERMSG_FORM_ERROR 1 -#define PUCAN_ERMSG_STUFF_ERROR 2 -#define PUCAN_ERMSG_OTHER_ERROR 3 -#define PUCAN_ERMSG_ERR_CNT_DEC 4 - -struct __packed pucan_error_msg { - __le16 size; - __le16 type; - __le32 ts_low; - __le32 ts_high; - u8 channel_type_d; - u8 code_g; - u8 tx_err_cnt; - u8 rx_err_cnt; -}; - -#define PUCAN_BUS_PASSIVE 0x20 -#define PUCAN_BUS_WARNING 0x40 -#define PUCAN_BUS_BUSOFF 0x80 - -struct __packed pucan_status_msg { - __le16 size; - __le16 type; - __le32 ts_low; - __le32 ts_high; - u8 channel_p_w_b; - u8 unused[3]; -}; - -/* uCAN transmitted message format */ -#define PUCAN_MSG_CHANNEL_DLC(c, d) (((c) & 0xf) | ((d) << 4)) - -struct __packed pucan_tx_msg { - __le16 size; - __le16 type; - __le32 tag_low; - __le32 tag_high; - u8 channel_dlc; - u8 client; - __le16 flags; - __le32 can_id; - u8 d[0]; -}; - -/* build the cmd opcode_channel field with respect to the correct endianness */ -static inline __le16 pucan_cmd_opcode_channel(int index, int opcode) -{ - return cpu_to_le16(((index) << 12) | ((opcode) & 0x3ff)); -} - -/* return the channel number part from any received message channel_dlc field */ -static inline int pucan_msg_get_channel(const struct pucan_rx_msg *msg) -{ - return msg->channel_dlc & 0xf; -} - -/* return the dlc value from any received message channel_dlc field */ -static inline int pucan_msg_get_dlc(const struct pucan_rx_msg *msg) -{ - return msg->channel_dlc >> 4; -} - -static inline int pucan_ermsg_get_channel(const struct pucan_error_msg *msg) -{ - return msg->channel_type_d & 0x0f; -} - -static inline int pucan_stmsg_get_channel(const struct pucan_status_msg *msg) -{ - return msg->channel_p_w_b & 0x0f; -} - -#endif diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c index 64cba85230f8..175c205d3f13 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -19,10 +19,10 @@ #include #include #include +#include #include "pcan_usb_core.h" #include "pcan_usb_pro.h" -#include "pcan_ucan.h" MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB FD adapter"); MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro FD adapter"); diff --git a/include/linux/can/dev/peak_canfd.h b/include/linux/can/dev/peak_canfd.h new file mode 100644 index 000000000000..25e20ef2fef8 --- /dev/null +++ b/include/linux/can/dev/peak_canfd.h @@ -0,0 +1,243 @@ +/* + * CAN driver for PEAK System micro-CAN based adapters + * + * Copyright (C) 2003-2011 PEAK System-Technik GmbH + * Copyright (C) 2011-2013 Stephane Grosjean + * + * 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 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef PUCAN_H +#define PUCAN_H + +/* uCAN commands opcodes list (low-order 10 bits) */ +#define PUCAN_CMD_NOP 0x000 +#define PUCAN_CMD_RESET_MODE 0x001 +#define PUCAN_CMD_NORMAL_MODE 0x002 +#define PUCAN_CMD_LISTEN_ONLY_MODE 0x003 +#define PUCAN_CMD_TIMING_SLOW 0x004 +#define PUCAN_CMD_TIMING_FAST 0x005 +#define PUCAN_CMD_FILTER_STD 0x008 +#define PUCAN_CMD_TX_ABORT 0x009 +#define PUCAN_CMD_WR_ERR_CNT 0x00a +#define PUCAN_CMD_SET_EN_OPTION 0x00b +#define PUCAN_CMD_CLR_DIS_OPTION 0x00c +#define PUCAN_CMD_END_OF_COLLECTION 0x3ff + +/* uCAN received messages list */ +#define PUCAN_MSG_CAN_RX 0x0001 +#define PUCAN_MSG_ERROR 0x0002 +#define PUCAN_MSG_STATUS 0x0003 +#define PUCAN_MSG_BUSLOAD 0x0004 +#define PUCAN_MSG_CAN_TX 0x1000 + +/* uCAN command common header */ +struct __packed pucan_command { + __le16 opcode_channel; + u16 args[3]; +}; + +#define PUCAN_TSLOW_BRP_BITS 10 +#define PUCAN_TSLOW_TSGEG1_BITS 8 +#define PUCAN_TSLOW_TSGEG2_BITS 7 +#define PUCAN_TSLOW_SJW_BITS 7 + +#define PUCAN_TSLOW_BRP_MASK ((1 << PUCAN_TSLOW_BRP_BITS) - 1) +#define PUCAN_TSLOW_TSEG1_MASK ((1 << PUCAN_TSLOW_TSGEG1_BITS) - 1) +#define PUCAN_TSLOW_TSEG2_MASK ((1 << PUCAN_TSLOW_TSGEG2_BITS) - 1) +#define PUCAN_TSLOW_SJW_MASK ((1 << PUCAN_TSLOW_SJW_BITS) - 1) + +/* uCAN TIMING_SLOW command fields */ +#define PUCAN_TSLOW_SJW_T(s, t) (((s) & PUCAN_TSLOW_SJW_MASK) | \ + ((!!(t)) << 7)) +#define PUCAN_TSLOW_TSEG2(t) ((t) & PUCAN_TSLOW_TSEG2_MASK) +#define PUCAN_TSLOW_TSEG1(t) ((t) & PUCAN_TSLOW_TSEG1_MASK) +#define PUCAN_TSLOW_BRP(b) ((b) & PUCAN_TSLOW_BRP_MASK) + +struct __packed pucan_timing_slow { + __le16 opcode_channel; + + u8 ewl; /* Error Warning limit */ + u8 sjw_t; /* Sync Jump Width + Triple sampling */ + u8 tseg2; /* Timing SEGment 2 */ + u8 tseg1; /* Timing SEGment 1 */ + + __le16 brp; /* BaudRate Prescaler */ +}; + +#define PUCAN_TFAST_BRP_BITS 10 +#define PUCAN_TFAST_TSGEG1_BITS 5 +#define PUCAN_TFAST_TSGEG2_BITS 4 +#define PUCAN_TFAST_SJW_BITS 4 + +#define PUCAN_TFAST_BRP_MASK ((1 << PUCAN_TFAST_BRP_BITS) - 1) +#define PUCAN_TFAST_TSEG1_MASK ((1 << PUCAN_TFAST_TSGEG1_BITS) - 1) +#define PUCAN_TFAST_TSEG2_MASK ((1 << PUCAN_TFAST_TSGEG2_BITS) - 1) +#define PUCAN_TFAST_SJW_MASK ((1 << PUCAN_TFAST_SJW_BITS) - 1) + +/* uCAN TIMING_FAST command fields */ +#define PUCAN_TFAST_SJW(s) ((s) & PUCAN_TFAST_SJW_MASK) +#define PUCAN_TFAST_TSEG2(t) ((t) & PUCAN_TFAST_TSEG2_MASK) +#define PUCAN_TFAST_TSEG1(t) ((t) & PUCAN_TFAST_TSEG1_MASK) +#define PUCAN_TFAST_BRP(b) ((b) & PUCAN_TFAST_BRP_MASK) + +struct __packed pucan_timing_fast { + __le16 opcode_channel; + + u8 unused; + u8 sjw; /* Sync Jump Width */ + u8 tseg2; /* Timing SEGment 2 */ + u8 tseg1; /* Timing SEGment 1 */ + + __le16 brp; /* BaudRate Prescaler */ +}; + +/* uCAN FILTER_STD command fields */ +#define PUCAN_FLTSTD_ROW_IDX_BITS 6 + +struct __packed pucan_filter_std { + __le16 opcode_channel; + + __le16 idx; + __le32 mask; /* CAN-ID bitmask in idx range */ +}; + +/* uCAN WR_ERR_CNT command fields */ +#define PUCAN_WRERRCNT_TE 0x4000 /* Tx error cntr write Enable */ +#define PUCAN_WRERRCNT_RE 0x8000 /* Rx error cntr write Enable */ + +struct __packed pucan_wr_err_cnt { + __le16 opcode_channel; + + __le16 sel_mask; + u8 tx_counter; /* Tx error counter new value */ + u8 rx_counter; /* Rx error counter new value */ + + u16 unused; +}; + +/* uCAN SET_EN/CLR_DIS _OPTION command fields */ +#define PUCAN_OPTION_ERROR 0x0001 +#define PUCAN_OPTION_BUSLOAD 0x0002 +#define PUCAN_OPTION_CANDFDISO 0x0004 + +struct __packed pucan_options { + __le16 opcode_channel; + + __le16 options; + u32 unused; +}; + +/* uCAN received messages global format */ +struct __packed pucan_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; +}; + +/* uCAN flags for CAN/CANFD messages */ +#define PUCAN_MSG_SELF_RECEIVE 0x80 +#define PUCAN_MSG_ERROR_STATE_IND 0x40 /* error state indicator */ +#define PUCAN_MSG_BITRATE_SWITCH 0x20 /* bitrate switch */ +#define PUCAN_MSG_EXT_DATA_LEN 0x10 /* extended data length */ +#define PUCAN_MSG_SINGLE_SHOT 0x08 +#define PUCAN_MSG_LOOPED_BACK 0x04 +#define PUCAN_MSG_EXT_ID 0x02 +#define PUCAN_MSG_RTR 0x01 + +struct __packed pucan_rx_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; + __le32 tag_low; + __le32 tag_high; + u8 channel_dlc; + u8 client; + __le16 flags; + __le32 can_id; + u8 d[0]; +}; + +/* uCAN error types */ +#define PUCAN_ERMSG_BIT_ERROR 0 +#define PUCAN_ERMSG_FORM_ERROR 1 +#define PUCAN_ERMSG_STUFF_ERROR 2 +#define PUCAN_ERMSG_OTHER_ERROR 3 +#define PUCAN_ERMSG_ERR_CNT_DEC 4 + +struct __packed pucan_error_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; + u8 channel_type_d; + u8 code_g; + u8 tx_err_cnt; + u8 rx_err_cnt; +}; + +#define PUCAN_BUS_PASSIVE 0x20 +#define PUCAN_BUS_WARNING 0x40 +#define PUCAN_BUS_BUSOFF 0x80 + +struct __packed pucan_status_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; + u8 channel_p_w_b; + u8 unused[3]; +}; + +/* uCAN transmitted message format */ +#define PUCAN_MSG_CHANNEL_DLC(c, d) (((c) & 0xf) | ((d) << 4)) + +struct __packed pucan_tx_msg { + __le16 size; + __le16 type; + __le32 tag_low; + __le32 tag_high; + u8 channel_dlc; + u8 client; + __le16 flags; + __le32 can_id; + u8 d[0]; +}; + +/* build the cmd opcode_channel field with respect to the correct endianness */ +static inline __le16 pucan_cmd_opcode_channel(int index, int opcode) +{ + return cpu_to_le16(((index) << 12) | ((opcode) & 0x3ff)); +} + +/* return the channel number part from any received message channel_dlc field */ +static inline int pucan_msg_get_channel(const struct pucan_rx_msg *msg) +{ + return msg->channel_dlc & 0xf; +} + +/* return the dlc value from any received message channel_dlc field */ +static inline int pucan_msg_get_dlc(const struct pucan_rx_msg *msg) +{ + return msg->channel_dlc >> 4; +} + +static inline int pucan_ermsg_get_channel(const struct pucan_error_msg *msg) +{ + return msg->channel_type_d & 0x0f; +} + +static inline int pucan_stmsg_get_channel(const struct pucan_status_msg *msg) +{ + return msg->channel_p_w_b & 0x0f; +} + +#endif -- cgit From 8ac8321e4a7981312348413b9ec314fd93d71a0c Mon Sep 17 00:00:00 2001 From: Stephane Grosjean Date: Thu, 19 Jan 2017 16:31:07 +0100 Subject: can: peak: add support for PEAK PCAN-PCIe FD CAN-FD boards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the support of the PCAN-PCI Express FD boards made by PEAK-System, for computers using the PCI Express slot. The PCAN-PCI Express FD has one or two CAN FD channels, depending on the model. A galvanic isolation of the CAN ports protects the electronics of the card and the respective computer against disturbances of up to 500 Volts. The PCAN-PCI Express FD can be operated with ambient temperatures in a range of -40 to +85 °C. Such boards run an extented version of the CAN-FD IP running into USB CAN-FD interfaces from PEAK-System, so this patch adds several new commands and their corresponding data types to the PEAK CAN-FD common definitions header file too. Signed-off-by: Stephane Grosjean Signed-off-by: Marc Kleine-Budde --- drivers/net/can/Kconfig | 1 + drivers/net/can/Makefile | 1 + drivers/net/can/peak_canfd/Kconfig | 13 + drivers/net/can/peak_canfd/Makefile | 5 + drivers/net/can/peak_canfd/peak_canfd.c | 801 ++++++++++++++++++++++++ drivers/net/can/peak_canfd/peak_canfd_user.h | 55 ++ drivers/net/can/peak_canfd/peak_pciefd_main.c | 842 ++++++++++++++++++++++++++ include/linux/can/dev/peak_canfd.h | 65 ++ 8 files changed, 1783 insertions(+) create mode 100644 drivers/net/can/peak_canfd/Kconfig create mode 100644 drivers/net/can/peak_canfd/Makefile create mode 100644 drivers/net/can/peak_canfd/peak_canfd.c create mode 100644 drivers/net/can/peak_canfd/peak_canfd_user.h create mode 100644 drivers/net/can/peak_canfd/peak_pciefd_main.c diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 22570ea3a8d2..aa20b69d2a1a 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -142,6 +142,7 @@ source "drivers/net/can/cc770/Kconfig" source "drivers/net/can/ifi_canfd/Kconfig" source "drivers/net/can/m_can/Kconfig" source "drivers/net/can/mscan/Kconfig" +source "drivers/net/can/peak_canfd/Kconfig" source "drivers/net/can/rcar/Kconfig" source "drivers/net/can/sja1000/Kconfig" source "drivers/net/can/softing/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 0da4f2f5c7e3..8581e2b3e87f 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/ obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_M_CAN) += m_can/ +obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_canfd/ obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o diff --git a/drivers/net/can/peak_canfd/Kconfig b/drivers/net/can/peak_canfd/Kconfig new file mode 100644 index 000000000000..84b30978a19f --- /dev/null +++ b/drivers/net/can/peak_canfd/Kconfig @@ -0,0 +1,13 @@ +config CAN_PEAK_PCIEFD + depends on PCI + tristate "PEAK-System PCAN-PCIe FD cards" + ---help--- + This driver adds support for the PEAK-System PCI Express FD + CAN-FD cards family. + These 1x or 2x CAN-FD channels cards offer CAN 2.0 a/b as well as + CAN-FD access to the CAN bus. Besides the nominal bitrate of up to + 1 Mbit/s, the data bytes of CAN-FD frames can be transmitted with + up to 12 Mbit/s. A galvanic isolation of the CAN ports protects the + electronics of the card and the respective computer against + disturbances of up to 500 Volts. The PCAN-PCI Express FD can be + operated with ambient temperatures in a range of -40 to +85 °C. diff --git a/drivers/net/can/peak_canfd/Makefile b/drivers/net/can/peak_canfd/Makefile new file mode 100644 index 000000000000..3dc7a6a0ba59 --- /dev/null +++ b/drivers/net/can/peak_canfd/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the PEAK-System CAN-FD IP module drivers +# +obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_pciefd.o +peak_pciefd-y := peak_pciefd_main.o peak_canfd.o diff --git a/drivers/net/can/peak_canfd/peak_canfd.c b/drivers/net/can/peak_canfd/peak_canfd.c new file mode 100644 index 000000000000..0d57be5ea97b --- /dev/null +++ b/drivers/net/can/peak_canfd/peak_canfd.c @@ -0,0 +1,801 @@ +/* + * Copyright (C) 2007, 2011 Wolfgang Grandegger + * Copyright (C) 2012 Stephane Grosjean + * + * Copyright (C) 2016 PEAK System-Technik GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include "peak_canfd_user.h" + +/* internal IP core cache size (used as default echo skbs max number) */ +#define PCANFD_ECHO_SKB_MAX 24 + +/* bittiming ranges of the PEAK-System PC CAN-FD interfaces */ +static const struct can_bittiming_const peak_canfd_nominal_const = { + .name = "peak_canfd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TSLOW_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TSLOW_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TSLOW_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TSLOW_BRP_BITS), + .brp_inc = 1, +}; + +static const struct can_bittiming_const peak_canfd_data_const = { + .name = "peak_canfd", + .tseg1_min = 1, + .tseg1_max = (1 << PUCAN_TFAST_TSGEG1_BITS), + .tseg2_min = 1, + .tseg2_max = (1 << PUCAN_TFAST_TSGEG2_BITS), + .sjw_max = (1 << PUCAN_TFAST_SJW_BITS), + .brp_min = 1, + .brp_max = (1 << PUCAN_TFAST_BRP_BITS), + .brp_inc = 1, +}; + +static struct peak_canfd_priv *pucan_init_cmd(struct peak_canfd_priv *priv) +{ + priv->cmd_len = 0; + return priv; +} + +static void *pucan_add_cmd(struct peak_canfd_priv *priv, int cmd_op) +{ + struct pucan_command *cmd; + + if (priv->cmd_len + sizeof(*cmd) > priv->cmd_maxlen) + return NULL; + + cmd = priv->cmd_buffer + priv->cmd_len; + + /* reset all unused bit to default */ + memset(cmd, 0, sizeof(*cmd)); + + cmd->opcode_channel = pucan_cmd_opcode_channel(priv->index, cmd_op); + priv->cmd_len += sizeof(*cmd); + + return cmd; +} + +static int pucan_write_cmd(struct peak_canfd_priv *priv) +{ + int err; + + if (priv->pre_cmd) { + err = priv->pre_cmd(priv); + if (err) + return err; + } + + err = priv->write_cmd(priv); + if (err) + return err; + + if (priv->post_cmd) + err = priv->post_cmd(priv); + + return err; +} + +/* uCAN commands interface functions */ +static int pucan_set_reset_mode(struct peak_canfd_priv *priv) +{ + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_RESET_MODE); + return pucan_write_cmd(priv); +} + +static int pucan_set_normal_mode(struct peak_canfd_priv *priv) +{ + int err; + + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_NORMAL_MODE); + err = pucan_write_cmd(priv); + if (!err) + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return err; +} + +static int pucan_set_listen_only_mode(struct peak_canfd_priv *priv) +{ + int err; + + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_LISTEN_ONLY_MODE); + err = pucan_write_cmd(priv); + if (!err) + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return err; +} + +static int pucan_set_timing_slow(struct peak_canfd_priv *priv, + const struct can_bittiming *pbt) +{ + struct pucan_timing_slow *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TIMING_SLOW); + + cmd->sjw_t = PUCAN_TSLOW_SJW_T(pbt->sjw - 1, + priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES); + cmd->tseg1 = PUCAN_TSLOW_TSEG1(pbt->prop_seg + pbt->phase_seg1 - 1); + cmd->tseg2 = PUCAN_TSLOW_TSEG2(pbt->phase_seg2 - 1); + cmd->brp = cpu_to_le16(PUCAN_TSLOW_BRP(pbt->brp - 1)); + + cmd->ewl = 96; /* default */ + + netdev_dbg(priv->ndev, + "nominal: brp=%u tseg1=%u tseg2=%u sjw=%u\n", + le16_to_cpu(cmd->brp), cmd->tseg1, cmd->tseg2, cmd->sjw_t); + + return pucan_write_cmd(priv); +} + +static int pucan_set_timing_fast(struct peak_canfd_priv *priv, + const struct can_bittiming *pbt) +{ + struct pucan_timing_fast *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TIMING_FAST); + + cmd->sjw = PUCAN_TFAST_SJW(pbt->sjw - 1); + cmd->tseg1 = PUCAN_TFAST_TSEG1(pbt->prop_seg + pbt->phase_seg1 - 1); + cmd->tseg2 = PUCAN_TFAST_TSEG2(pbt->phase_seg2 - 1); + cmd->brp = cpu_to_le16(PUCAN_TFAST_BRP(pbt->brp - 1)); + + netdev_dbg(priv->ndev, + "data: brp=%u tseg1=%u tseg2=%u sjw=%u\n", + le16_to_cpu(cmd->brp), cmd->tseg1, cmd->tseg2, cmd->sjw); + + return pucan_write_cmd(priv); +} + +static int pucan_set_std_filter(struct peak_canfd_priv *priv, u8 row, u32 mask) +{ + struct pucan_std_filter *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_SET_STD_FILTER); + + /* all the 11-bits CAN ID values are represented by one bit in a + * 64 rows array of 32 bits: the upper 6 bits of the CAN ID select the + * row while the lowest 5 bits select the bit in that row. + * + * bit filter + * 1 passed + * 0 discarded + */ + + /* select the row */ + cmd->idx = row; + + /* set/unset bits in the row */ + cmd->mask = cpu_to_le32(mask); + + return pucan_write_cmd(priv); +} + +static int pucan_tx_abort(struct peak_canfd_priv *priv, u16 flags) +{ + struct pucan_tx_abort *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_TX_ABORT); + + cmd->flags = cpu_to_le16(flags); + + return pucan_write_cmd(priv); +} + +static int pucan_clr_err_counters(struct peak_canfd_priv *priv) +{ + struct pucan_wr_err_cnt *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_WR_ERR_CNT); + + cmd->sel_mask = cpu_to_le16(PUCAN_WRERRCNT_TE | PUCAN_WRERRCNT_RE); + cmd->tx_counter = 0; + cmd->rx_counter = 0; + + return pucan_write_cmd(priv); +} + +static int pucan_set_options(struct peak_canfd_priv *priv, u16 opt_mask) +{ + struct pucan_options *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_SET_EN_OPTION); + + cmd->options = cpu_to_le16(opt_mask); + + return pucan_write_cmd(priv); +} + +static int pucan_clr_options(struct peak_canfd_priv *priv, u16 opt_mask) +{ + struct pucan_options *cmd; + + cmd = pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_CLR_DIS_OPTION); + + cmd->options = cpu_to_le16(opt_mask); + + return pucan_write_cmd(priv); +} + +static int pucan_setup_rx_barrier(struct peak_canfd_priv *priv) +{ + pucan_add_cmd(pucan_init_cmd(priv), PUCAN_CMD_RX_BARRIER); + + return pucan_write_cmd(priv); +} + +/* handle the reception of one CAN frame */ +static int pucan_handle_can_rx(struct peak_canfd_priv *priv, + struct pucan_rx_msg *msg) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct canfd_frame *cf; + struct sk_buff *skb; + const u16 rx_msg_flags = le16_to_cpu(msg->flags); + u8 cf_len; + + if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) + cf_len = can_dlc2len(get_canfd_dlc(pucan_msg_get_dlc(msg))); + else + cf_len = get_can_dlc(pucan_msg_get_dlc(msg)); + + /* if this frame is an echo, */ + if ((rx_msg_flags & PUCAN_MSG_LOOPED_BACK) && + !(rx_msg_flags & PUCAN_MSG_SELF_RECEIVE)) { + int n; + unsigned long flags; + + spin_lock_irqsave(&priv->echo_lock, flags); + n = can_get_echo_skb(priv->ndev, msg->client); + spin_unlock_irqrestore(&priv->echo_lock, flags); + + /* count bytes of the echo instead of skb */ + stats->tx_bytes += cf_len; + stats->tx_packets++; + + if (n) { + /* restart tx queue only if a slot is free */ + netif_wake_queue(priv->ndev); + } + + return 0; + } + + /* otherwise, it should be pushed into rx fifo */ + if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) { + /* CANFD frame case */ + skb = alloc_canfd_skb(priv->ndev, &cf); + if (!skb) + return -ENOMEM; + + if (rx_msg_flags & PUCAN_MSG_BITRATE_SWITCH) + cf->flags |= CANFD_BRS; + + if (rx_msg_flags & PUCAN_MSG_ERROR_STATE_IND) + cf->flags |= CANFD_ESI; + } else { + /* CAN 2.0 frame case */ + skb = alloc_can_skb(priv->ndev, (struct can_frame **)&cf); + if (!skb) + return -ENOMEM; + } + + cf->can_id = le32_to_cpu(msg->can_id); + cf->len = cf_len; + + if (rx_msg_flags & PUCAN_MSG_EXT_ID) + cf->can_id |= CAN_EFF_FLAG; + + if (rx_msg_flags & PUCAN_MSG_RTR) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, msg->d, cf->len); + + stats->rx_bytes += cf->len; + stats->rx_packets++; + + netif_rx(skb); + + return 0; +} + +/* handle rx/tx error counters notification */ +static int pucan_handle_error(struct peak_canfd_priv *priv, + struct pucan_error_msg *msg) +{ + priv->bec.txerr = msg->tx_err_cnt; + priv->bec.rxerr = msg->rx_err_cnt; + + return 0; +} + +/* handle status notification */ +static int pucan_handle_status(struct peak_canfd_priv *priv, + struct pucan_status_msg *msg) +{ + struct net_device *ndev = priv->ndev; + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + /* this STATUS is the CNF of the RX_BARRIER: Tx path can be setup */ + if (pucan_status_is_rx_barrier(msg)) { + unsigned long flags; + + if (priv->enable_tx_path) { + int err = priv->enable_tx_path(priv); + + if (err) + return err; + } + + /* restart network queue only if echo skb array is free */ + spin_lock_irqsave(&priv->echo_lock, flags); + + if (!priv->can.echo_skb[priv->echo_idx]) { + spin_unlock_irqrestore(&priv->echo_lock, flags); + + netif_wake_queue(ndev); + } else { + spin_unlock_irqrestore(&priv->echo_lock, flags); + } + + return 0; + } + + skb = alloc_can_err_skb(ndev, &cf); + + /* test state error bits according to their priority */ + if (pucan_status_is_busoff(msg)) { + netdev_dbg(ndev, "Bus-off entry status\n"); + priv->can.state = CAN_STATE_BUS_OFF; + priv->can.can_stats.bus_off++; + can_bus_off(ndev); + if (skb) + cf->can_id |= CAN_ERR_BUSOFF; + + } else if (pucan_status_is_passive(msg)) { + netdev_dbg(ndev, "Error passive status\n"); + priv->can.state = CAN_STATE_ERROR_PASSIVE; + priv->can.can_stats.error_passive++; + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (priv->bec.txerr > priv->bec.rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : + CAN_ERR_CRTL_RX_PASSIVE; + cf->data[6] = priv->bec.txerr; + cf->data[7] = priv->bec.rxerr; + } + + } else if (pucan_status_is_warning(msg)) { + netdev_dbg(ndev, "Error warning status\n"); + priv->can.state = CAN_STATE_ERROR_WARNING; + priv->can.can_stats.error_warning++; + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (priv->bec.txerr > priv->bec.rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = priv->bec.txerr; + cf->data[7] = priv->bec.rxerr; + } + + } else if (priv->can.state != CAN_STATE_ERROR_ACTIVE) { + /* back to ERROR_ACTIVE */ + netdev_dbg(ndev, "Error active status\n"); + can_change_state(ndev, cf, CAN_STATE_ERROR_ACTIVE, + CAN_STATE_ERROR_ACTIVE); + } else { + dev_kfree_skb(skb); + return 0; + } + + if (!skb) { + stats->rx_dropped++; + return -ENOMEM; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + return 0; +} + +/* handle uCAN Rx overflow notification */ +static int pucan_handle_cache_critical(struct peak_canfd_priv *priv) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + + stats->rx_over_errors++; + stats->rx_errors++; + + skb = alloc_can_err_skb(priv->ndev, &cf); + if (!skb) { + stats->rx_dropped++; + return -ENOMEM; + } + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + cf->data[6] = priv->bec.txerr; + cf->data[7] = priv->bec.rxerr; + + stats->rx_bytes += cf->can_dlc; + stats->rx_packets++; + netif_rx(skb); + + return 0; +} + +/* handle a single uCAN message */ +int peak_canfd_handle_msg(struct peak_canfd_priv *priv, + struct pucan_rx_msg *msg) +{ + u16 msg_type = le16_to_cpu(msg->type); + int msg_size = le16_to_cpu(msg->size); + int err; + + if (!msg_size || !msg_type) { + /* null packet found: end of list */ + goto exit; + } + + switch (msg_type) { + case PUCAN_MSG_CAN_RX: + err = pucan_handle_can_rx(priv, (struct pucan_rx_msg *)msg); + break; + case PUCAN_MSG_ERROR: + err = pucan_handle_error(priv, (struct pucan_error_msg *)msg); + break; + case PUCAN_MSG_STATUS: + err = pucan_handle_status(priv, (struct pucan_status_msg *)msg); + break; + case PUCAN_MSG_CACHE_CRITICAL: + err = pucan_handle_cache_critical(priv); + break; + default: + err = 0; + } + + if (err < 0) + return err; + +exit: + return msg_size; +} + +/* handle a list of rx_count messages from rx_msg memory address */ +int peak_canfd_handle_msgs_list(struct peak_canfd_priv *priv, + struct pucan_rx_msg *msg_list, int msg_count) +{ + void *msg_ptr = msg_list; + int i, msg_size; + + for (i = 0; i < msg_count; i++) { + msg_size = peak_canfd_handle_msg(priv, msg_ptr); + + /* a null packet can be found at the end of a list */ + if (msg_size <= 0) + break; + + msg_ptr += msg_size; + } + + if (msg_size < 0) + return msg_size; + + return i; +} + +static int peak_canfd_start(struct peak_canfd_priv *priv) +{ + int err; + + err = pucan_clr_err_counters(priv); + if (err) + goto err_exit; + + priv->echo_idx = 0; + + priv->bec.txerr = 0; + priv->bec.rxerr = 0; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + err = pucan_set_listen_only_mode(priv); + else + err = pucan_set_normal_mode(priv); + +err_exit: + return err; +} + +static void peak_canfd_stop(struct peak_canfd_priv *priv) +{ + int err; + + /* go back to RESET mode */ + err = pucan_set_reset_mode(priv); + if (err) { + netdev_err(priv->ndev, "channel %u reset failed\n", + priv->index); + } else { + /* abort last Tx (MUST be done in RESET mode only!) */ + pucan_tx_abort(priv, PUCAN_TX_ABORT_FLUSH); + } +} + +static int peak_canfd_set_mode(struct net_device *ndev, enum can_mode mode) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + switch (mode) { + case CAN_MODE_START: + peak_canfd_start(priv); + netif_wake_queue(ndev); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int peak_canfd_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + *bec = priv->bec; + return 0; +} + +static int peak_canfd_open(struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + int i, err = 0; + + err = open_candev(ndev); + if (err) { + netdev_err(ndev, "open_candev() failed, error %d\n", err); + goto err_exit; + } + + err = pucan_set_reset_mode(priv); + if (err) + goto err_close; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) + err = pucan_clr_options(priv, PUCAN_OPTION_CANDFDISO); + else + err = pucan_set_options(priv, PUCAN_OPTION_CANDFDISO); + + if (err) + goto err_close; + } + + /* set option: get rx/tx error counters */ + err = pucan_set_options(priv, PUCAN_OPTION_ERROR); + if (err) + goto err_close; + + /* accept all standard CAN ID */ + for (i = 0; i <= PUCAN_FLTSTD_ROW_IDX_MAX; i++) + pucan_set_std_filter(priv, i, 0xffffffff); + + err = peak_canfd_start(priv); + if (err) + goto err_close; + + /* receiving the RB status says when Tx path is ready */ + err = pucan_setup_rx_barrier(priv); + if (!err) + goto err_exit; + +err_close: + close_candev(ndev); +err_exit: + return err; +} + +static int peak_canfd_set_bittiming(struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + return pucan_set_timing_slow(priv, &priv->can.bittiming); +} + +static int peak_canfd_set_data_bittiming(struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + return pucan_set_timing_fast(priv, &priv->can.data_bittiming); +} + +static int peak_canfd_close(struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + peak_canfd_stop(priv); + close_candev(ndev); + + return 0; +} + +static netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct peak_canfd_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + struct pucan_tx_msg *msg; + u16 msg_size, msg_flags; + unsigned long flags; + bool should_stop_tx_queue; + int room_left; + u8 can_dlc; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + msg_size = ALIGN(sizeof(*msg) + cf->len, 4); + msg = priv->alloc_tx_msg(priv, msg_size, &room_left); + + /* should never happen except under bus-off condition and (auto-)restart + * mechanism + */ + if (!msg) { + stats->tx_dropped++; + netif_stop_queue(ndev); + return NETDEV_TX_BUSY; + } + + msg->size = cpu_to_le16(msg_size); + msg->type = cpu_to_le16(PUCAN_MSG_CAN_TX); + msg_flags = 0; + + if (cf->can_id & CAN_EFF_FLAG) { + msg_flags |= PUCAN_MSG_EXT_ID; + msg->can_id = cpu_to_le32(cf->can_id & CAN_EFF_MASK); + } else { + msg->can_id = cpu_to_le32(cf->can_id & CAN_SFF_MASK); + } + + if (can_is_canfd_skb(skb)) { + /* CAN FD frame format */ + can_dlc = can_len2dlc(cf->len); + + msg_flags |= PUCAN_MSG_EXT_DATA_LEN; + + if (cf->flags & CANFD_BRS) + msg_flags |= PUCAN_MSG_BITRATE_SWITCH; + + if (cf->flags & CANFD_ESI) + msg_flags |= PUCAN_MSG_ERROR_STATE_IND; + } else { + /* CAN 2.0 frame format */ + can_dlc = cf->len; + + if (cf->can_id & CAN_RTR_FLAG) + msg_flags |= PUCAN_MSG_RTR; + } + + /* always ask loopback for echo management */ + msg_flags |= PUCAN_MSG_LOOPED_BACK; + + /* set driver specific bit to differentiate with application loopback */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + msg_flags |= PUCAN_MSG_SELF_RECEIVE; + + msg->flags = cpu_to_le16(msg_flags); + msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(priv->index, can_dlc); + memcpy(msg->d, cf->data, cf->len); + + /* struct msg client field is used as an index in the echo skbs ring */ + msg->client = priv->echo_idx; + + spin_lock_irqsave(&priv->echo_lock, flags); + + /* prepare and save echo skb in internal slot */ + can_put_echo_skb(skb, ndev, priv->echo_idx); + + /* move echo index to the next slot */ + priv->echo_idx = (priv->echo_idx + 1) % priv->can.echo_skb_max; + + /* if next slot is not free, stop network queue (no slot free in echo + * skb ring means that the controller did not write these frames on + * the bus: no need to continue). + */ + should_stop_tx_queue = !!(priv->can.echo_skb[priv->echo_idx]); + + spin_unlock_irqrestore(&priv->echo_lock, flags); + + /* write the skb on the interface */ + priv->write_tx_msg(priv, msg); + + /* stop network tx queue if not enough room to save one more msg too */ + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + should_stop_tx_queue |= (room_left < + (sizeof(*msg) + CANFD_MAX_DLEN)); + else + should_stop_tx_queue |= (room_left < + (sizeof(*msg) + CAN_MAX_DLEN)); + + if (should_stop_tx_queue) + netif_stop_queue(ndev); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops peak_canfd_netdev_ops = { + .ndo_open = peak_canfd_open, + .ndo_stop = peak_canfd_close, + .ndo_start_xmit = peak_canfd_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +struct net_device *alloc_peak_canfd_dev(int sizeof_priv, int index, + int echo_skb_max) +{ + struct net_device *ndev; + struct peak_canfd_priv *priv; + + /* we DO support local echo */ + if (echo_skb_max < 0) + echo_skb_max = PCANFD_ECHO_SKB_MAX; + + /* allocate the candev object */ + ndev = alloc_candev(sizeof_priv, echo_skb_max); + if (!ndev) + return NULL; + + priv = netdev_priv(ndev); + + /* complete now socket-can initialization side */ + priv->can.state = CAN_STATE_STOPPED; + priv->can.bittiming_const = &peak_canfd_nominal_const; + priv->can.data_bittiming_const = &peak_canfd_data_const; + + priv->can.do_set_mode = peak_canfd_set_mode; + priv->can.do_get_berr_counter = peak_canfd_get_berr_counter; + priv->can.do_set_bittiming = peak_canfd_set_bittiming; + priv->can.do_set_data_bittiming = peak_canfd_set_data_bittiming; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_FD | + CAN_CTRLMODE_FD_NON_ISO | + CAN_CTRLMODE_BERR_REPORTING; + + priv->ndev = ndev; + priv->index = index; + priv->cmd_len = 0; + spin_lock_init(&priv->echo_lock); + + ndev->flags |= IFF_ECHO; + ndev->netdev_ops = &peak_canfd_netdev_ops; + ndev->dev_id = index; + + return ndev; +} diff --git a/drivers/net/can/peak_canfd/peak_canfd_user.h b/drivers/net/can/peak_canfd/peak_canfd_user.h new file mode 100644 index 000000000000..bf6de47f69c2 --- /dev/null +++ b/drivers/net/can/peak_canfd/peak_canfd_user.h @@ -0,0 +1,55 @@ +/* + * CAN driver for PEAK System micro-CAN based adapters + * + * Copyright (C) 2003-2011 PEAK System-Technik GmbH + * Copyright (C) 2011-2013 Stephane Grosjean + * + * 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 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#ifndef PEAK_CANFD_USER_H +#define PEAK_CANFD_USER_H + +#include + +#define PCANFD_ECHO_SKB_DEF -1 + +/* data structure private to each uCAN interface */ +struct peak_canfd_priv { + struct can_priv can; /* socket-can private data */ + struct net_device *ndev; /* network device */ + int index; /* channel index */ + + struct can_berr_counter bec; /* rx/tx err counters */ + + int echo_idx; /* echo skb free slot index */ + spinlock_t echo_lock; + + int cmd_len; + void *cmd_buffer; + int cmd_maxlen; + + int (*pre_cmd)(struct peak_canfd_priv *priv); + int (*write_cmd)(struct peak_canfd_priv *priv); + int (*post_cmd)(struct peak_canfd_priv *priv); + + int (*enable_tx_path)(struct peak_canfd_priv *priv); + void *(*alloc_tx_msg)(struct peak_canfd_priv *priv, u16 msg_size, + int *room_left); + int (*write_tx_msg)(struct peak_canfd_priv *priv, + struct pucan_tx_msg *msg); +}; + +struct net_device *alloc_peak_canfd_dev(int sizeof_priv, int index, + int echo_skb_max); +int peak_canfd_handle_msg(struct peak_canfd_priv *priv, + struct pucan_rx_msg *msg); +int peak_canfd_handle_msgs_list(struct peak_canfd_priv *priv, + struct pucan_rx_msg *rx_msg, int rx_count); +#endif diff --git a/drivers/net/can/peak_canfd/peak_pciefd_main.c b/drivers/net/can/peak_canfd/peak_pciefd_main.c new file mode 100644 index 000000000000..51c2d182a33a --- /dev/null +++ b/drivers/net/can/peak_canfd/peak_pciefd_main.c @@ -0,0 +1,842 @@ +/* + * Copyright (C) 2007, 2011 Wolfgang Grandegger + * Copyright (C) 2012 Stephane Grosjean + * + * Derived from the PCAN project file driver/src/pcan_pci.c: + * + * Copyright (C) 2001-2006 PEAK System-Technik GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "peak_canfd_user.h" + +MODULE_AUTHOR("Stephane Grosjean "); +MODULE_DESCRIPTION("Socket-CAN driver for PEAK PCAN PCIe FD family cards"); +MODULE_SUPPORTED_DEVICE("PEAK PCAN PCIe FD CAN cards"); +MODULE_LICENSE("GPL v2"); + +#define PCIEFD_DRV_NAME "peak_pciefd" + +#define PEAK_PCI_VENDOR_ID 0x001c /* The PCI device and vendor IDs */ +#define PEAK_PCIEFD_ID 0x0013 /* for PCIe slot cards */ + +/* PEAK PCIe board access description */ +#define PCIEFD_BAR0_SIZE (64 * 1024) +#define PCIEFD_RX_DMA_SIZE (4 * 1024) +#define PCIEFD_TX_DMA_SIZE (4 * 1024) + +#define PCIEFD_TX_PAGE_SIZE (2 * 1024) + +/* System Control Registers */ +#define PCIEFD_REG_SYS_CTL_SET 0x0000 /* set bits */ +#define PCIEFD_REG_SYS_CTL_CLR 0x0004 /* clear bits */ + +/* Version info registers */ +#define PCIEFD_REG_SYS_VER1 0x0040 /* version reg #1 */ +#define PCIEFD_REG_SYS_VER2 0x0044 /* version reg #2 */ + +/* System Control Registers Bits */ +#define PCIEFD_SYS_CTL_TS_RST 0x00000001 /* timestamp clock */ +#define PCIEFD_SYS_CTL_CLK_EN 0x00000002 /* system clock */ + +/* CAN-FD channel addresses */ +#define PCIEFD_CANX_OFF(c) (((c) + 1) * 0x1000) + +#define PCIEFD_ECHO_SKB_MAX PCANFD_ECHO_SKB_DEF + +/* CAN-FD channel registers */ +#define PCIEFD_REG_CAN_MISC 0x0000 /* Misc. control */ +#define PCIEFD_REG_CAN_CLK_SEL 0x0008 /* Clock selector */ +#define PCIEFD_REG_CAN_CMD_PORT_L 0x0010 /* 64-bits command port */ +#define PCIEFD_REG_CAN_CMD_PORT_H 0x0014 +#define PCIEFD_REG_CAN_TX_REQ_ACC 0x0020 /* Tx request accumulator */ +#define PCIEFD_REG_CAN_TX_CTL_SET 0x0030 /* Tx control set register */ +#define PCIEFD_REG_CAN_TX_CTL_CLR 0x0038 /* Tx control clear register */ +#define PCIEFD_REG_CAN_TX_DMA_ADDR_L 0x0040 /* 64-bits addr for Tx DMA */ +#define PCIEFD_REG_CAN_TX_DMA_ADDR_H 0x0044 +#define PCIEFD_REG_CAN_RX_CTL_SET 0x0050 /* Rx control set register */ +#define PCIEFD_REG_CAN_RX_CTL_CLR 0x0058 /* Rx control clear register */ +#define PCIEFD_REG_CAN_RX_CTL_WRT 0x0060 /* Rx control write register */ +#define PCIEFD_REG_CAN_RX_CTL_ACK 0x0068 /* Rx control ACK register */ +#define PCIEFD_REG_CAN_RX_DMA_ADDR_L 0x0070 /* 64-bits addr for Rx DMA */ +#define PCIEFD_REG_CAN_RX_DMA_ADDR_H 0x0074 + +/* CAN-FD channel misc register bits */ +#define CANFD_MISC_TS_RST 0x00000001 /* timestamp cnt rst */ + +/* CAN-FD channel Clock SELector Source & DIVider */ +#define CANFD_CLK_SEL_DIV_MASK 0x00000007 +#define CANFD_CLK_SEL_DIV_60MHZ 0x00000000 /* SRC=240MHz only */ +#define CANFD_CLK_SEL_DIV_40MHZ 0x00000001 /* SRC=240MHz only */ +#define CANFD_CLK_SEL_DIV_30MHZ 0x00000002 /* SRC=240MHz only */ +#define CANFD_CLK_SEL_DIV_24MHZ 0x00000003 /* SRC=240MHz only */ +#define CANFD_CLK_SEL_DIV_20MHZ 0x00000004 /* SRC=240MHz only */ + +#define CANFD_CLK_SEL_SRC_MASK 0x00000008 /* 0=80MHz, 1=240MHz */ +#define CANFD_CLK_SEL_SRC_240MHZ 0x00000008 +#define CANFD_CLK_SEL_SRC_80MHZ (~CANFD_CLK_SEL_SRC_240MHZ & \ + CANFD_CLK_SEL_SRC_MASK) + +#define CANFD_CLK_SEL_20MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_20MHZ) +#define CANFD_CLK_SEL_24MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_24MHZ) +#define CANFD_CLK_SEL_30MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_30MHZ) +#define CANFD_CLK_SEL_40MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_40MHZ) +#define CANFD_CLK_SEL_60MHZ (CANFD_CLK_SEL_SRC_240MHZ |\ + CANFD_CLK_SEL_DIV_60MHZ) +#define CANFD_CLK_SEL_80MHZ (CANFD_CLK_SEL_SRC_80MHZ) + +/* CAN-FD channel Rx/Tx control register bits */ +#define CANFD_CTL_UNC_BIT 0x00010000 /* Uncached DMA mem */ +#define CANFD_CTL_RST_BIT 0x00020000 /* reset DMA action */ +#define CANFD_CTL_IEN_BIT 0x00040000 /* IRQ enable */ + +/* Rx IRQ Count and Time Limits */ +#define CANFD_CTL_IRQ_CL_DEF 16 /* Rx msg max nb per IRQ in Rx DMA */ +#define CANFD_CTL_IRQ_TL_DEF 10 /* Time before IRQ if < CL (x100 µs) */ + +#define CANFD_OPTIONS_SET (CANFD_OPTION_ERROR | CANFD_OPTION_BUSLOAD) + +/* Tx anticipation window (link logical address should be aligned on 2K + * boundary) + */ +#define PCIEFD_TX_PAGE_COUNT (PCIEFD_TX_DMA_SIZE / PCIEFD_TX_PAGE_SIZE) + +#define CANFD_MSG_LNK_TX 0x1001 /* Tx msgs link */ + +/* 32-bits IRQ status fields, heading Rx DMA area */ +static inline int pciefd_irq_tag(u32 irq_status) +{ + return irq_status & 0x0000000f; +} + +static inline int pciefd_irq_rx_cnt(u32 irq_status) +{ + return (irq_status & 0x000007f0) >> 4; +} + +static inline int pciefd_irq_is_lnk(u32 irq_status) +{ + return irq_status & 0x00010000; +} + +/* Rx record */ +struct pciefd_rx_dma { + __le32 irq_status; + __le32 sys_time_low; + __le32 sys_time_high; + struct pucan_rx_msg msg[0]; +} __packed __aligned(4); + +/* Tx Link record */ +struct pciefd_tx_link { + __le16 size; + __le16 type; + __le32 laddr_lo; + __le32 laddr_hi; +} __packed __aligned(4); + +/* Tx page descriptor */ +struct pciefd_page { + void *vbase; /* page virtual address */ + dma_addr_t lbase; /* page logical address */ + u32 offset; + u32 size; +}; + +#define CANFD_IRQ_SET 0x00000001 +#define CANFD_TX_PATH_SET 0x00000002 + +/* CAN-FD channel object */ +struct pciefd_board; +struct pciefd_can { + struct peak_canfd_priv ucan; /* must be the first member */ + void __iomem *reg_base; /* channel config base addr */ + struct pciefd_board *board; /* reverse link */ + + struct pucan_command pucan_cmd; /* command buffer */ + + dma_addr_t rx_dma_laddr; /* DMA virtual and logical addr */ + void *rx_dma_vaddr; /* for Rx and Tx areas */ + dma_addr_t tx_dma_laddr; + void *tx_dma_vaddr; + + struct pciefd_page tx_pages[PCIEFD_TX_PAGE_COUNT]; + u16 tx_pages_free; /* free Tx pages counter */ + u16 tx_page_index; /* current page used for Tx */ + spinlock_t tx_lock; + + u32 irq_status; + u32 irq_tag; /* next irq tag */ +}; + +/* PEAK-PCIe FD board object */ +struct pciefd_board { + void __iomem *reg_base; + struct pci_dev *pci_dev; + int can_count; + spinlock_t cmd_lock; /* 64-bits cmds must be atomic */ + struct pciefd_can *can[0]; /* array of network devices */ +}; + +/* supported device ids. */ +static const struct pci_device_id peak_pciefd_tbl[] = { + {PEAK_PCI_VENDOR_ID, PEAK_PCIEFD_ID, PCI_ANY_ID, PCI_ANY_ID,}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, peak_pciefd_tbl); + +/* read a 32 bits value from a SYS block register */ +static inline u32 pciefd_sys_readreg(const struct pciefd_board *priv, u16 reg) +{ + return readl(priv->reg_base + reg); +} + +/* write a 32 bits value into a SYS block register */ +static inline void pciefd_sys_writereg(const struct pciefd_board *priv, + u32 val, u16 reg) +{ + writel(val, priv->reg_base + reg); +} + +/* read a 32 bits value from CAN-FD block register */ +static inline u32 pciefd_can_readreg(const struct pciefd_can *priv, u16 reg) +{ + return readl(priv->reg_base + reg); +} + +/* write a 32 bits value into a CAN-FD block register */ +static inline void pciefd_can_writereg(const struct pciefd_can *priv, + u32 val, u16 reg) +{ + writel(val, priv->reg_base + reg); +} + +/* give a channel logical Rx DMA address to the board */ +static void pciefd_can_setup_rx_dma(struct pciefd_can *priv) +{ +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + const u32 dma_addr_h = (u32)(priv->rx_dma_laddr >> 32); +#else + const u32 dma_addr_h = 0; +#endif + + /* (DMA must be reset for Rx) */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_RX_CTL_SET); + + /* write the logical address of the Rx DMA area for this channel */ + pciefd_can_writereg(priv, (u32)priv->rx_dma_laddr, + PCIEFD_REG_CAN_RX_DMA_ADDR_L); + pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_RX_DMA_ADDR_H); + + /* also indicates that Rx DMA is cacheable */ + pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, PCIEFD_REG_CAN_RX_CTL_CLR); +} + +/* clear channel logical Rx DMA address from the board */ +static void pciefd_can_clear_rx_dma(struct pciefd_can *priv) +{ + /* DMA must be reset for Rx */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_RX_CTL_SET); + + /* clear the logical address of the Rx DMA area for this channel */ + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_L); + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_RX_DMA_ADDR_H); +} + +/* give a channel logical Tx DMA address to the board */ +static void pciefd_can_setup_tx_dma(struct pciefd_can *priv) +{ +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + const u32 dma_addr_h = (u32)(priv->tx_dma_laddr >> 32); +#else + const u32 dma_addr_h = 0; +#endif + + /* (DMA must be reset for Tx) */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_SET); + + /* write the logical address of the Tx DMA area for this channel */ + pciefd_can_writereg(priv, (u32)priv->tx_dma_laddr, + PCIEFD_REG_CAN_TX_DMA_ADDR_L); + pciefd_can_writereg(priv, dma_addr_h, PCIEFD_REG_CAN_TX_DMA_ADDR_H); + + /* also indicates that Tx DMA is cacheable */ + pciefd_can_writereg(priv, CANFD_CTL_UNC_BIT, PCIEFD_REG_CAN_TX_CTL_CLR); +} + +/* clear channel logical Tx DMA address from the board */ +static void pciefd_can_clear_tx_dma(struct pciefd_can *priv) +{ + /* DMA must be reset for Tx */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_SET); + + /* clear the logical address of the Tx DMA area for this channel */ + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_L); + pciefd_can_writereg(priv, 0, PCIEFD_REG_CAN_TX_DMA_ADDR_H); +} + +static void pciefd_can_ack_rx_dma(struct pciefd_can *priv) +{ + /* read value of current IRQ tag and inc it for next one */ + priv->irq_tag = le32_to_cpu(*(__le32 *)priv->rx_dma_vaddr); + priv->irq_tag++; + priv->irq_tag &= 0xf; + + /* write the next IRQ tag for this CAN */ + pciefd_can_writereg(priv, priv->irq_tag, PCIEFD_REG_CAN_RX_CTL_ACK); +} + +/* IRQ handler */ +static irqreturn_t pciefd_irq_handler(int irq, void *arg) +{ + struct pciefd_can *priv = arg; + struct pciefd_rx_dma *rx_dma = priv->rx_dma_vaddr; + + /* INTA mode only to sync with PCIe transaction */ + if (!pci_dev_msi_enabled(priv->board->pci_dev)) + (void)pciefd_sys_readreg(priv->board, PCIEFD_REG_SYS_VER1); + + /* read IRQ status from the first 32-bits of the Rx DMA area */ + priv->irq_status = le32_to_cpu(rx_dma->irq_status); + + /* check if this (shared) IRQ is for this CAN */ + if (pciefd_irq_tag(priv->irq_status) != priv->irq_tag) + return IRQ_NONE; + + /* handle rx messages (if any) */ + peak_canfd_handle_msgs_list(&priv->ucan, + rx_dma->msg, + pciefd_irq_rx_cnt(priv->irq_status)); + + /* handle tx link interrupt (if any) */ + if (pciefd_irq_is_lnk(priv->irq_status)) { + unsigned long flags; + + spin_lock_irqsave(&priv->tx_lock, flags); + priv->tx_pages_free++; + spin_unlock_irqrestore(&priv->tx_lock, flags); + + /* wake producer up */ + netif_wake_queue(priv->ucan.ndev); + } + + /* re-enable Rx DMA transfer for this CAN */ + pciefd_can_ack_rx_dma(priv); + + return IRQ_HANDLED; +} + +static int pciefd_enable_tx_path(struct peak_canfd_priv *ucan) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + int i; + + /* initialize the Tx pages descriptors */ + priv->tx_pages_free = PCIEFD_TX_PAGE_COUNT - 1; + priv->tx_page_index = 0; + + priv->tx_pages[0].vbase = priv->tx_dma_vaddr; + priv->tx_pages[0].lbase = priv->tx_dma_laddr; + + for (i = 0; i < PCIEFD_TX_PAGE_COUNT; i++) { + priv->tx_pages[i].offset = 0; + priv->tx_pages[i].size = PCIEFD_TX_PAGE_SIZE - + sizeof(struct pciefd_tx_link); + if (i) { + priv->tx_pages[i].vbase = + priv->tx_pages[i - 1].vbase + + PCIEFD_TX_PAGE_SIZE; + priv->tx_pages[i].lbase = + priv->tx_pages[i - 1].lbase + + PCIEFD_TX_PAGE_SIZE; + } + } + + /* setup Tx DMA addresses into IP core */ + pciefd_can_setup_tx_dma(priv); + + /* start (TX_RST=0) Tx Path */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, PCIEFD_REG_CAN_TX_CTL_CLR); + + return 0; +} + +/* board specific CANFD command pre-processing */ +static int pciefd_pre_cmd(struct peak_canfd_priv *ucan) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + u16 cmd = pucan_cmd_get_opcode(&priv->pucan_cmd); + int err; + + /* pre-process command */ + switch (cmd) { + case PUCAN_CMD_NORMAL_MODE: + case PUCAN_CMD_LISTEN_ONLY_MODE: + + if (ucan->can.state == CAN_STATE_BUS_OFF) + break; + + /* going into operational mode: setup IRQ handler */ + err = request_irq(priv->board->pci_dev->irq, + pciefd_irq_handler, + IRQF_SHARED, + PCIEFD_DRV_NAME, + priv); + if (err) + return err; + + /* setup Rx DMA address */ + pciefd_can_setup_rx_dma(priv); + + /* setup max count of msgs per IRQ */ + pciefd_can_writereg(priv, (CANFD_CTL_IRQ_TL_DEF) << 8 | + CANFD_CTL_IRQ_CL_DEF, + PCIEFD_REG_CAN_RX_CTL_WRT); + + /* clear DMA RST for Rx (Rx start) */ + pciefd_can_writereg(priv, CANFD_CTL_RST_BIT, + PCIEFD_REG_CAN_RX_CTL_CLR); + + /* reset timestamps */ + pciefd_can_writereg(priv, !CANFD_MISC_TS_RST, + PCIEFD_REG_CAN_MISC); + + /* do an initial ACK */ + pciefd_can_ack_rx_dma(priv); + + /* enable IRQ for this CAN after having set next irq_tag */ + pciefd_can_writereg(priv, CANFD_CTL_IEN_BIT, + PCIEFD_REG_CAN_RX_CTL_SET); + + /* Tx path will be setup as soon as RX_BARRIER is received */ + break; + default: + break; + } + + return 0; +} + +/* write a command */ +static int pciefd_write_cmd(struct peak_canfd_priv *ucan) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + unsigned long flags; + + /* 64-bits command is atomic */ + spin_lock_irqsave(&priv->board->cmd_lock, flags); + + pciefd_can_writereg(priv, *(u32 *)ucan->cmd_buffer, + PCIEFD_REG_CAN_CMD_PORT_L); + pciefd_can_writereg(priv, *(u32 *)(ucan->cmd_buffer + 4), + PCIEFD_REG_CAN_CMD_PORT_H); + + spin_unlock_irqrestore(&priv->board->cmd_lock, flags); + + return 0; +} + +/* board specific CANFD command post-processing */ +static int pciefd_post_cmd(struct peak_canfd_priv *ucan) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + u16 cmd = pucan_cmd_get_opcode(&priv->pucan_cmd); + + switch (cmd) { + case PUCAN_CMD_RESET_MODE: + + if (ucan->can.state == CAN_STATE_STOPPED) + break; + + /* controller now in reset mode: */ + + /* stop and reset DMA addresses in Tx/Rx engines */ + pciefd_can_clear_tx_dma(priv); + pciefd_can_clear_rx_dma(priv); + + /* disable IRQ for this CAN */ + pciefd_can_writereg(priv, CANFD_CTL_IEN_BIT, + PCIEFD_REG_CAN_RX_CTL_CLR); + + free_irq(priv->board->pci_dev->irq, priv); + + ucan->can.state = CAN_STATE_STOPPED; + + break; + } + + return 0; +} + +static void *pciefd_alloc_tx_msg(struct peak_canfd_priv *ucan, u16 msg_size, + int *room_left) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + struct pciefd_page *page = priv->tx_pages + priv->tx_page_index; + unsigned long flags; + void *msg; + + spin_lock_irqsave(&priv->tx_lock, flags); + + if (page->offset + msg_size > page->size) { + struct pciefd_tx_link *lk; + + /* not enough space in this page: try another one */ + if (!priv->tx_pages_free) { + spin_unlock_irqrestore(&priv->tx_lock, flags); + + /* Tx overflow */ + return NULL; + } + + priv->tx_pages_free--; + + /* keep address of the very last free slot of current page */ + lk = page->vbase + page->offset; + + /* next, move on a new free page */ + priv->tx_page_index = (priv->tx_page_index + 1) % + PCIEFD_TX_PAGE_COUNT; + page = priv->tx_pages + priv->tx_page_index; + + /* put link record to this new page at the end of prev one */ + lk->size = cpu_to_le16(sizeof(*lk)); + lk->type = cpu_to_le16(CANFD_MSG_LNK_TX); + lk->laddr_lo = cpu_to_le32(page->lbase); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + lk->laddr_hi = cpu_to_le32(page->lbase >> 32); +#else + lk->laddr_hi = 0; +#endif + /* next msgs will be put from the begininng of this new page */ + page->offset = 0; + } + + *room_left = priv->tx_pages_free * page->size; + + spin_unlock_irqrestore(&priv->tx_lock, flags); + + msg = page->vbase + page->offset; + + /* give back room left in the tx ring */ + *room_left += page->size - (page->offset + msg_size); + + return msg; +} + +static int pciefd_write_tx_msg(struct peak_canfd_priv *ucan, + struct pucan_tx_msg *msg) +{ + struct pciefd_can *priv = (struct pciefd_can *)ucan; + struct pciefd_page *page = priv->tx_pages + priv->tx_page_index; + + /* this slot is now reserved for writing the frame */ + page->offset += le16_to_cpu(msg->size); + + /* tell the board a frame has been written in Tx DMA area */ + pciefd_can_writereg(priv, 1, PCIEFD_REG_CAN_TX_REQ_ACC); + + return 0; +} + +/* probe for CAN-FD channel #pciefd_board->can_count */ +static int pciefd_can_probe(struct pciefd_board *pciefd) +{ + struct net_device *ndev; + struct pciefd_can *priv; + u32 clk; + int err; + + /* allocate the candev object with default isize of echo skbs ring */ + ndev = alloc_peak_canfd_dev(sizeof(*priv), pciefd->can_count, + PCIEFD_ECHO_SKB_MAX); + if (!ndev) { + dev_err(&pciefd->pci_dev->dev, + "failed to alloc candev object\n"); + goto failure; + } + + priv = netdev_priv(ndev); + + /* fill-in candev private object: */ + + /* setup PCIe-FD own callbacks */ + priv->ucan.pre_cmd = pciefd_pre_cmd; + priv->ucan.write_cmd = pciefd_write_cmd; + priv->ucan.post_cmd = pciefd_post_cmd; + priv->ucan.enable_tx_path = pciefd_enable_tx_path; + priv->ucan.alloc_tx_msg = pciefd_alloc_tx_msg; + priv->ucan.write_tx_msg = pciefd_write_tx_msg; + + /* setup PCIe-FD own command buffer */ + priv->ucan.cmd_buffer = &priv->pucan_cmd; + priv->ucan.cmd_maxlen = sizeof(priv->pucan_cmd); + + priv->board = pciefd; + + /* CAN config regs block address */ + priv->reg_base = pciefd->reg_base + PCIEFD_CANX_OFF(priv->ucan.index); + + /* allocate non-cacheable DMA'able 4KB memory area for Rx */ + priv->rx_dma_vaddr = dmam_alloc_coherent(&pciefd->pci_dev->dev, + PCIEFD_RX_DMA_SIZE, + &priv->rx_dma_laddr, + GFP_KERNEL); + if (!priv->rx_dma_vaddr) { + dev_err(&pciefd->pci_dev->dev, + "Rx dmam_alloc_coherent(%u) failure\n", + PCIEFD_RX_DMA_SIZE); + goto err_free_candev; + } + + /* allocate non-cacheable DMA'able 4KB memory area for Tx */ + priv->tx_dma_vaddr = dmam_alloc_coherent(&pciefd->pci_dev->dev, + PCIEFD_TX_DMA_SIZE, + &priv->tx_dma_laddr, + GFP_KERNEL); + if (!priv->tx_dma_vaddr) { + dev_err(&pciefd->pci_dev->dev, + "Tx dmaim_alloc_coherent(%u) failure\n", + PCIEFD_TX_DMA_SIZE); + goto err_free_candev; + } + + /* CAN clock in RST mode */ + pciefd_can_writereg(priv, CANFD_MISC_TS_RST, PCIEFD_REG_CAN_MISC); + + /* read current clock value */ + clk = pciefd_can_readreg(priv, PCIEFD_REG_CAN_CLK_SEL); + switch (clk) { + case CANFD_CLK_SEL_20MHZ: + priv->ucan.can.clock.freq = 20 * 1000 * 1000; + break; + case CANFD_CLK_SEL_24MHZ: + priv->ucan.can.clock.freq = 24 * 1000 * 1000; + break; + case CANFD_CLK_SEL_30MHZ: + priv->ucan.can.clock.freq = 30 * 1000 * 1000; + break; + case CANFD_CLK_SEL_40MHZ: + priv->ucan.can.clock.freq = 40 * 1000 * 1000; + break; + case CANFD_CLK_SEL_60MHZ: + priv->ucan.can.clock.freq = 60 * 1000 * 1000; + break; + default: + pciefd_can_writereg(priv, CANFD_CLK_SEL_80MHZ, + PCIEFD_REG_CAN_CLK_SEL); + + /* fallthough */ + case CANFD_CLK_SEL_80MHZ: + priv->ucan.can.clock.freq = 80 * 1000 * 1000; + break; + } + + ndev->irq = pciefd->pci_dev->irq; + + SET_NETDEV_DEV(ndev, &pciefd->pci_dev->dev); + + err = register_candev(ndev); + if (err) { + dev_err(&pciefd->pci_dev->dev, + "couldn't register CAN device: %d\n", err); + goto err_free_candev; + } + + spin_lock_init(&priv->tx_lock); + + /* save the object address in the board structure */ + pciefd->can[pciefd->can_count] = priv; + + dev_info(&pciefd->pci_dev->dev, "%s at reg_base=0x%p irq=%d\n", + ndev->name, priv->reg_base, pciefd->pci_dev->irq); + + return 0; + +err_free_candev: + free_candev(ndev); + +failure: + return -ENOMEM; +} + +/* remove a CAN-FD channel by releasing all of its resources */ +static void pciefd_can_remove(struct pciefd_can *priv) +{ + /* unregister (close) the can device to go back to RST mode first */ + unregister_candev(priv->ucan.ndev); + + /* finally, free the candev object */ + free_candev(priv->ucan.ndev); +} + +/* remove all CAN-FD channels by releasing their own resources */ +static void pciefd_can_remove_all(struct pciefd_board *pciefd) +{ + while (pciefd->can_count > 0) + pciefd_can_remove(pciefd->can[--pciefd->can_count]); +} + +/* probe for the entire device */ +static int peak_pciefd_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct pciefd_board *pciefd; + int err, can_count; + u16 sub_sys_id; + u8 hw_ver_major; + u8 hw_ver_minor; + u8 hw_ver_sub; + u32 v2; + + err = pci_enable_device(pdev); + if (err) + return err; + err = pci_request_regions(pdev, PCIEFD_DRV_NAME); + if (err) + goto err_disable_pci; + + /* the number of channels depends on sub-system id */ + err = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &sub_sys_id); + if (err) + goto err_release_regions; + + dev_dbg(&pdev->dev, "probing device %04x:%04x:%04x\n", + pdev->vendor, pdev->device, sub_sys_id); + + if (sub_sys_id >= 0x0012) + can_count = 4; + else if (sub_sys_id >= 0x0010) + can_count = 3; + else if (sub_sys_id >= 0x0004) + can_count = 2; + else + can_count = 1; + + /* allocate board structure object */ + pciefd = devm_kzalloc(&pdev->dev, sizeof(*pciefd) + + can_count * sizeof(*pciefd->can), + GFP_KERNEL); + if (!pciefd) { + err = -ENOMEM; + goto err_release_regions; + } + + /* initialize the board structure */ + pciefd->pci_dev = pdev; + spin_lock_init(&pciefd->cmd_lock); + + /* save the PCI BAR0 virtual address for further system regs access */ + pciefd->reg_base = pci_iomap(pdev, 0, PCIEFD_BAR0_SIZE); + if (!pciefd->reg_base) { + dev_err(&pdev->dev, "failed to map PCI resource #0\n"); + err = -ENOMEM; + goto err_release_regions; + } + + /* read the firmware version number */ + v2 = pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER2); + + hw_ver_major = (v2 & 0x0000f000) >> 12; + hw_ver_minor = (v2 & 0x00000f00) >> 8; + hw_ver_sub = (v2 & 0x000000f0) >> 4; + + dev_info(&pdev->dev, + "%ux CAN-FD PCAN-PCIe FPGA v%u.%u.%u:\n", can_count, + hw_ver_major, hw_ver_minor, hw_ver_sub); + + /* stop system clock */ + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN, + PCIEFD_REG_SYS_CTL_CLR); + + pci_set_master(pdev); + + /* create now the corresponding channels objects */ + while (pciefd->can_count < can_count) { + err = pciefd_can_probe(pciefd); + if (err) + goto err_free_canfd; + + pciefd->can_count++; + } + + /* set system timestamps counter in RST mode */ + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST, + PCIEFD_REG_SYS_CTL_SET); + + /* wait a bit (read cycle) */ + (void)pciefd_sys_readreg(pciefd, PCIEFD_REG_SYS_VER1); + + /* free all clocks */ + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_TS_RST, + PCIEFD_REG_SYS_CTL_CLR); + + /* start system clock */ + pciefd_sys_writereg(pciefd, PCIEFD_SYS_CTL_CLK_EN, + PCIEFD_REG_SYS_CTL_SET); + + /* remember the board structure address in the device user data */ + pci_set_drvdata(pdev, pciefd); + + return 0; + +err_free_canfd: + pciefd_can_remove_all(pciefd); + + pci_iounmap(pdev, pciefd->reg_base); + +err_release_regions: + pci_release_regions(pdev); + +err_disable_pci: + pci_disable_device(pdev); + + return err; +} + +/* free the board structure object, as well as its resources: */ +static void peak_pciefd_remove(struct pci_dev *pdev) +{ + struct pciefd_board *pciefd = pci_get_drvdata(pdev); + + /* release CAN-FD channels resources */ + pciefd_can_remove_all(pciefd); + + pci_iounmap(pdev, pciefd->reg_base); + + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static struct pci_driver peak_pciefd_driver = { + .name = PCIEFD_DRV_NAME, + .id_table = peak_pciefd_tbl, + .probe = peak_pciefd_probe, + .remove = peak_pciefd_remove, +}; + +module_pci_driver(peak_pciefd_driver); diff --git a/include/linux/can/dev/peak_canfd.h b/include/linux/can/dev/peak_canfd.h index 25e20ef2fef8..46dceef2cfa6 100644 --- a/include/linux/can/dev/peak_canfd.h +++ b/include/linux/can/dev/peak_canfd.h @@ -23,11 +23,14 @@ #define PUCAN_CMD_LISTEN_ONLY_MODE 0x003 #define PUCAN_CMD_TIMING_SLOW 0x004 #define PUCAN_CMD_TIMING_FAST 0x005 +#define PUCAN_CMD_SET_STD_FILTER 0x006 +#define PUCAN_CMD_RESERVED2 0x007 #define PUCAN_CMD_FILTER_STD 0x008 #define PUCAN_CMD_TX_ABORT 0x009 #define PUCAN_CMD_WR_ERR_CNT 0x00a #define PUCAN_CMD_SET_EN_OPTION 0x00b #define PUCAN_CMD_CLR_DIS_OPTION 0x00c +#define PUCAN_CMD_RX_BARRIER 0x010 #define PUCAN_CMD_END_OF_COLLECTION 0x3ff /* uCAN received messages list */ @@ -35,6 +38,10 @@ #define PUCAN_MSG_ERROR 0x0002 #define PUCAN_MSG_STATUS 0x0003 #define PUCAN_MSG_BUSLOAD 0x0004 + +#define PUCAN_MSG_CACHE_CRITICAL 0x0102 + +/* uCAN transmitted messages */ #define PUCAN_MSG_CAN_TX 0x1000 /* uCAN command common header */ @@ -43,6 +50,12 @@ struct __packed pucan_command { u16 args[3]; }; +/* return the opcode from the opcode_channel field of a command */ +static inline u16 pucan_cmd_get_opcode(struct pucan_command *c) +{ + return le16_to_cpu(c->opcode_channel) & 0x3ff; +} + #define PUCAN_TSLOW_BRP_BITS 10 #define PUCAN_TSLOW_TSGEG1_BITS 8 #define PUCAN_TSLOW_TSGEG2_BITS 7 @@ -108,6 +121,27 @@ struct __packed pucan_filter_std { __le32 mask; /* CAN-ID bitmask in idx range */ }; +#define PUCAN_FLTSTD_ROW_IDX_MAX ((1 << PUCAN_FLTSTD_ROW_IDX_BITS) - 1) + +/* uCAN SET_STD_FILTER command fields */ +struct __packed pucan_std_filter { + __le16 opcode_channel; + + u8 unused; + u8 idx; + __le32 mask; /* CAN-ID bitmask in idx range */ +}; + +/* uCAN TX_ABORT commands fields */ +#define PUCAN_TX_ABORT_FLUSH 0x0001 + +struct __packed pucan_tx_abort { + __le16 opcode_channel; + + __le16 flags; + u32 unused; +}; + /* uCAN WR_ERR_CNT command fields */ #define PUCAN_WRERRCNT_TE 0x4000 /* Tx error cntr write Enable */ #define PUCAN_WRERRCNT_RE 0x8000 /* Rx error cntr write Enable */ @@ -184,6 +218,12 @@ struct __packed pucan_error_msg { u8 rx_err_cnt; }; +static inline int pucan_error_get_channel(const struct pucan_error_msg *msg) +{ + return msg->channel_type_d & 0x0f; +} + +#define PUCAN_RX_BARRIER 0x10 #define PUCAN_BUS_PASSIVE 0x20 #define PUCAN_BUS_WARNING 0x40 #define PUCAN_BUS_BUSOFF 0x80 @@ -197,6 +237,31 @@ struct __packed pucan_status_msg { u8 unused[3]; }; +static inline int pucan_status_get_channel(const struct pucan_status_msg *msg) +{ + return msg->channel_p_w_b & 0x0f; +} + +static inline int pucan_status_is_rx_barrier(const struct pucan_status_msg *msg) +{ + return msg->channel_p_w_b & PUCAN_RX_BARRIER; +} + +static inline int pucan_status_is_passive(const struct pucan_status_msg *msg) +{ + return msg->channel_p_w_b & PUCAN_BUS_PASSIVE; +} + +static inline int pucan_status_is_warning(const struct pucan_status_msg *msg) +{ + return msg->channel_p_w_b & PUCAN_BUS_WARNING; +} + +static inline int pucan_status_is_busoff(const struct pucan_status_msg *msg) +{ + return msg->channel_p_w_b & PUCAN_BUS_BUSOFF; +} + /* uCAN transmitted message format */ #define PUCAN_MSG_CHANNEL_DLC(c, d) (((c) & 0xf) | ((d) << 4)) -- cgit From 52973810b54bb569fa3baf14d0ff9bfa3e0f68f9 Mon Sep 17 00:00:00 2001 From: Mario Huettel Date: Sat, 8 Apr 2017 14:10:09 +0200 Subject: can: m_can: Disabled Interrupt Line 1 * Disabled interrupt line 1. The driver didn't use it. Signed-off-by: Mario Huettel Tested-by: Quentin Schulz Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 7a6554efd42b..6797977ef705 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -349,7 +349,8 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv, static inline void m_can_enable_all_interrupts(const struct m_can_priv *priv) { - m_can_write(priv, M_CAN_ILE, ILE_EINT0 | ILE_EINT1); + /* Only interrupt line 0 is used in this driver */ + m_can_write(priv, M_CAN_ILE, ILE_EINT0); } static inline void m_can_disable_all_interrupts(const struct m_can_priv *priv) -- cgit From 8f265895df44148135ee545f406de76d669dbfd9 Mon Sep 17 00:00:00 2001 From: Mario Huettel Date: Sat, 8 Apr 2017 14:10:10 +0200 Subject: can: m_can: Removed initialization of FIFO water marks FIFO water marks disabled because the driver doesn't handle water mark events. Signed-off-by: Mario Huettel Reviewed-by: Oliver Hartkopp Tested-by: Quentin Schulz Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 6797977ef705..ddcbe8bda55a 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -864,11 +864,11 @@ static void m_can_chip_config(struct net_device *dev) /* rx fifo configuration, blocking mode, fifo size 1 */ m_can_write(priv, M_CAN_RXF0C, (priv->mcfg[MRAM_RXF0].num << RXFC_FS_OFF) | - RXFC_FWM_1 | priv->mcfg[MRAM_RXF0].off); + priv->mcfg[MRAM_RXF0].off); m_can_write(priv, M_CAN_RXF1C, (priv->mcfg[MRAM_RXF1].num << RXFC_FS_OFF) | - RXFC_FWM_1 | priv->mcfg[MRAM_RXF1].off); + priv->mcfg[MRAM_RXF1].off); cccr = m_can_read(priv, M_CAN_CCCR); cccr &= ~(CCCR_TEST | CCCR_MON | (CCCR_CMR_MASK << CCCR_CMR_SHIFT) | -- cgit From ee8c3f6f75671c9172133636aebabf14dbc80066 Mon Sep 17 00:00:00 2001 From: Mario Huettel Date: Sat, 8 Apr 2017 14:10:11 +0200 Subject: can: m_can: Removed virtual address from print The virtual address of the device was printed. I removed it because it leaks internal information. Signed-off-by: Mario Huettel Tested-by: Quentin Schulz Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index ddcbe8bda55a..a966ed5b1946 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -1231,8 +1231,8 @@ static int m_can_plat_probe(struct platform_device *pdev) devm_can_led_init(dev); - dev_info(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n", - KBUILD_MODNAME, priv->base, dev->irq); + dev_info(&pdev->dev, "%s device registered (irq=%d)\n", + KBUILD_MODNAME, dev->irq); return 0; -- cgit From 5e1bd15a3767b2962595698e849ab59936ce6c33 Mon Sep 17 00:00:00 2001 From: Mario Huettel Date: Sat, 8 Apr 2017 14:10:12 +0200 Subject: can: m_can: Updated register defines to newest version * Updated register defines to newest M_CAN version (v3.2.1). * Changed defines in the whole code. Signed-off-by: Mario Huettel Reviewed-by: Oliver Hartkopp Tested-by: Quentin Schulz Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 190 +++++++++++++++++++++++++++--------------- 1 file changed, 125 insertions(+), 65 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index a966ed5b1946..15b5e1a1fc2b 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -37,17 +37,19 @@ enum m_can_reg { M_CAN_CREL = 0x0, M_CAN_ENDN = 0x4, M_CAN_CUST = 0x8, - M_CAN_FBTP = 0xc, + M_CAN_DBTP = 0xc, M_CAN_TEST = 0x10, M_CAN_RWD = 0x14, M_CAN_CCCR = 0x18, - M_CAN_BTP = 0x1c, + M_CAN_NBTP = 0x1c, M_CAN_TSCC = 0x20, M_CAN_TSCV = 0x24, M_CAN_TOCC = 0x28, M_CAN_TOCV = 0x2c, M_CAN_ECR = 0x40, M_CAN_PSR = 0x44, +/* TDCR Register only available for version >=3.1.x */ + M_CAN_TDCR = 0x48, M_CAN_IR = 0x50, M_CAN_IE = 0x54, M_CAN_ILS = 0x58, @@ -105,21 +107,21 @@ enum m_can_mram_cfg { MRAM_CFG_NUM, }; -/* Fast Bit Timing & Prescaler Register (FBTP) */ -#define FBTR_FBRP_MASK 0x1f -#define FBTR_FBRP_SHIFT 16 -#define FBTR_FTSEG1_SHIFT 8 -#define FBTR_FTSEG1_MASK (0xf << FBTR_FTSEG1_SHIFT) -#define FBTR_FTSEG2_SHIFT 4 -#define FBTR_FTSEG2_MASK (0x7 << FBTR_FTSEG2_SHIFT) -#define FBTR_FSJW_SHIFT 0 -#define FBTR_FSJW_MASK 0x3 +/* Data Bit Timing & Prescaler Register (DBTP) */ +#define DBTP_TDC BIT(23) +#define DBTP_DBRP_SHIFT 16 +#define DBTP_DBRP_MASK (0x1f << DBTP_DBRP_SHIFT) +#define DBTP_DTSEG1_SHIFT 8 +#define DBTP_DTSEG1_MASK (0x1f << DBTP_DTSEG1_SHIFT) +#define DBTP_DTSEG2_SHIFT 4 +#define DBTP_DTSEG2_MASK (0xf << DBTP_DTSEG2_SHIFT) +#define DBTP_DSJW_SHIFT 0 +#define DBTP_DSJW_MASK (0xf << DBTP_DSJW_SHIFT) /* Test Register (TEST) */ -#define TEST_LBCK BIT(4) +#define TEST_LBCK BIT(4) /* CC Control Register(CCCR) */ -#define CCCR_TEST BIT(7) #define CCCR_CMR_MASK 0x3 #define CCCR_CMR_SHIFT 10 #define CCCR_CMR_CANFD 0x1 @@ -130,21 +132,32 @@ enum m_can_mram_cfg { #define CCCR_CME_CAN 0 #define CCCR_CME_CANFD 0x1 #define CCCR_CME_CANFD_BRS 0x2 +#define CCCR_TXP BIT(14) #define CCCR_TEST BIT(7) #define CCCR_MON BIT(5) +#define CCCR_CSR BIT(4) +#define CCCR_CSA BIT(3) +#define CCCR_ASM BIT(2) #define CCCR_CCE BIT(1) #define CCCR_INIT BIT(0) #define CCCR_CANFD 0x10 - -/* Bit Timing & Prescaler Register (BTP) */ -#define BTR_BRP_MASK 0x3ff -#define BTR_BRP_SHIFT 16 -#define BTR_TSEG1_SHIFT 8 -#define BTR_TSEG1_MASK (0x3f << BTR_TSEG1_SHIFT) -#define BTR_TSEG2_SHIFT 4 -#define BTR_TSEG2_MASK (0xf << BTR_TSEG2_SHIFT) -#define BTR_SJW_SHIFT 0 -#define BTR_SJW_MASK 0xf +/* for version >=3.1.x */ +#define CCCR_EFBI BIT(13) +#define CCCR_PXHD BIT(12) +#define CCCR_BRSE BIT(9) +#define CCCR_FDOE BIT(8) +/* only for version >=3.2.x */ +#define CCCR_NISO BIT(15) + +/* Nominal Bit Timing & Prescaler Register (NBTP) */ +#define NBTP_NSJW_SHIFT 25 +#define NBTP_NSJW_MASK (0x7f << NBTP_NSJW_SHIFT) +#define NBTP_NBRP_SHIFT 16 +#define NBTP_NBRP_MASK (0x1ff << NBTP_NBRP_SHIFT) +#define NBTP_NTSEG1_SHIFT 8 +#define NBTP_NTSEG1_MASK (0xff << NBTP_NTSEG1_SHIFT) +#define NBTP_NTSEG2_SHIFT 0 +#define NBTP_NTSEG2_MASK (0x7f << NBTP_NTSEG2_SHIFT) /* Error Counter Register(ECR) */ #define ECR_RP BIT(15) @@ -161,6 +174,13 @@ enum m_can_mram_cfg { /* Interrupt Register(IR) */ #define IR_ALL_INT 0xffffffff + +/* Renamed bits for versions > 3.1.x */ +#define IR_ARA BIT(29) +#define IR_PED BIT(28) +#define IR_PEA BIT(27) + +/* Bits for version 3.0.x */ #define IR_STE BIT(31) #define IR_FOE BIT(30) #define IR_ACKE BIT(29) @@ -194,33 +214,40 @@ enum m_can_mram_cfg { #define IR_RF0W BIT(1) #define IR_RF0N BIT(0) #define IR_ERR_STATE (IR_BO | IR_EW | IR_EP) -#define IR_ERR_LEC (IR_STE | IR_FOE | IR_ACKE | IR_BE | IR_CRCE) -#define IR_ERR_BUS (IR_ERR_LEC | IR_WDI | IR_ELO | IR_BEU | \ + +/* Interrupts for version 3.0.x */ +#define IR_ERR_LEC_30X (IR_STE | IR_FOE | IR_ACKE | IR_BE | IR_CRCE) +#define IR_ERR_BUS_30X (IR_ERR_LEC_30X | IR_WDI | IR_ELO | IR_BEU | \ + IR_BEC | IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | \ + IR_RF1L | IR_RF0L) +#define IR_ERR_ALL_30X (IR_ERR_STATE | IR_ERR_BUS_30X) +/* Interrupts for version >= 3.1.x */ +#define IR_ERR_LEC_31X (IR_PED | IR_PEA) +#define IR_ERR_BUS_31X (IR_ERR_LEC_31X | IR_WDI | IR_ELO | IR_BEU | \ IR_BEC | IR_TOO | IR_MRAF | IR_TSW | IR_TEFL | \ IR_RF1L | IR_RF0L) -#define IR_ERR_ALL (IR_ERR_STATE | IR_ERR_BUS) +#define IR_ERR_ALL_31X (IR_ERR_STATE | IR_ERR_BUS_31X) /* Interrupt Line Select (ILS) */ #define ILS_ALL_INT0 0x0 #define ILS_ALL_INT1 0xFFFFFFFF /* Interrupt Line Enable (ILE) */ -#define ILE_EINT0 BIT(0) #define ILE_EINT1 BIT(1) +#define ILE_EINT0 BIT(0) /* Rx FIFO 0/1 Configuration (RXF0C/RXF1C) */ -#define RXFC_FWM_OFF 24 -#define RXFC_FWM_MASK 0x7f -#define RXFC_FWM_1 (1 << RXFC_FWM_OFF) -#define RXFC_FS_OFF 16 -#define RXFC_FS_MASK 0x7f +#define RXFC_FWM_SHIFT 24 +#define RXFC_FWM_MASK (0x7f < RXFC_FWM_SHIFT) +#define RXFC_FS_SHIFT 16 +#define RXFC_FS_MASK (0x7f << RXFC_FS_SHIFT) /* Rx FIFO 0/1 Status (RXF0S/RXF1S) */ #define RXFS_RFL BIT(25) #define RXFS_FF BIT(24) -#define RXFS_FPI_OFF 16 +#define RXFS_FPI_SHIFT 16 #define RXFS_FPI_MASK 0x3f0000 -#define RXFS_FGI_OFF 8 +#define RXFS_FGI_SHIFT 8 #define RXFS_FGI_MASK 0x3f00 #define RXFS_FFL_MASK 0x7f @@ -229,23 +256,46 @@ enum m_can_mram_cfg { #define M_CAN_RXESC_64BYTES 0x777 /* Tx Buffer Configuration(TXBC) */ -#define TXBC_NDTB_OFF 16 -#define TXBC_NDTB_MASK 0x3f +#define TXBC_NDTB_SHIFT 16 +#define TXBC_NDTB_MASK (0x3f << TXBC_NDTB_SHIFT) +#define TXBC_TFQS_SHIFT 24 +#define TXBC_TFQS_MASK (0x3f << TXBC_TFQS_SHIFT) + +/* Tx FIFO/Queue Status (TXFQS) */ +#define TXFQS_TFQF BIT(21) +#define TXFQS_TFQPI_SHIFT 16 +#define TXFQS_TFQPI_MASK (0x1f << TXFQS_TFQPI_SHIFT) +#define TXFQS_TFGI_SHIFT 8 +#define TXFQS_TFGI_MASK (0x1f << TXFQS_TFGI_SHIFT) +#define TXFQS_TFFL_SHIFT 0 +#define TXFQS_TFFL_MASK (0x3f << TXFQS_TFFL_SHIFT) /* Tx Buffer Element Size Configuration(TXESC) */ #define TXESC_TBDS_8BYTES 0x0 #define TXESC_TBDS_64BYTES 0x7 -/* Tx Event FIFO Con.guration (TXEFC) */ -#define TXEFC_EFS_OFF 16 -#define TXEFC_EFS_MASK 0x3f +/* Tx Event FIFO Configuration (TXEFC) */ +#define TXEFC_EFS_SHIFT 16 +#define TXEFC_EFS_MASK (0x3f << TXEFC_EFS_SHIFT) + +/* Tx Event FIFO Status (TXEFS) */ +#define TXEFS_TEFL BIT(25) +#define TXEFS_EFF BIT(24) +#define TXEFS_EFGI_SHIFT 8 +#define TXEFS_EFGI_MASK (0x1f << TXEFS_EFGI_SHIFT) +#define TXEFS_EFFL_SHIFT 0 +#define TXEFS_EFFL_MASK (0x3f << TXEFS_EFFL_SHIFT) + +/* Tx Event FIFO Acknowledge (TXEFA) */ +#define TXEFA_EFAI_SHIFT 0 +#define TXEFA_EFAI_MASK (0x1f << TXEFA_EFAI_SHIFT) /* Message RAM Configuration (in bytes) */ #define SIDF_ELEMENT_SIZE 4 #define XIDF_ELEMENT_SIZE 8 #define RXF0_ELEMENT_SIZE 72 #define RXF1_ELEMENT_SIZE 72 -#define RXB_ELEMENT_SIZE 16 +#define RXB_ELEMENT_SIZE 72 #define TXE_ELEMENT_SIZE 8 #define TXB_ELEMENT_SIZE 72 @@ -261,13 +311,20 @@ enum m_can_mram_cfg { #define RX_BUF_RTR BIT(29) /* R1 */ #define RX_BUF_ANMF BIT(31) -#define RX_BUF_EDL BIT(21) +#define RX_BUF_FDF BIT(21) #define RX_BUF_BRS BIT(20) /* Tx Buffer Element */ -/* R0 */ +/* T0 */ +#define TX_BUF_ESI BIT(31) #define TX_BUF_XTD BIT(30) #define TX_BUF_RTR BIT(29) +/* T1 */ +#define TX_BUF_EFC BIT(23) +#define TX_BUF_FDF BIT(21) +#define TX_BUF_BRS BIT(20) +#define TX_BUF_MM_SHIFT 24 +#define TX_BUF_MM_MASK (0xff << TX_BUF_MM_SHIFT) /* address offset and element number for each FIFO/Buffer in the Message RAM */ struct mram_cfg { @@ -368,9 +425,9 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) int i; /* calculate the fifo get index for where to read data */ - fgi = (rxfs & RXFS_FGI_MASK) >> RXFS_FGI_OFF; + fgi = (rxfs & RXFS_FGI_MASK) >> RXFS_FGI_SHIFT; dlc = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC); - if (dlc & RX_BUF_EDL) + if (dlc & RX_BUF_FDF) skb = alloc_canfd_skb(dev, &cf); else skb = alloc_can_skb(dev, (struct can_frame **)&cf); @@ -379,7 +436,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) return; } - if (dlc & RX_BUF_EDL) + if (dlc & RX_BUF_FDF) cf->len = can_dlc2len((dlc >> 16) & 0x0F); else cf->len = get_can_dlc((dlc >> 16) & 0x0F); @@ -395,7 +452,7 @@ static void m_can_read_fifo(struct net_device *dev, u32 rxfs) netdev_dbg(dev, "ESI Error\n"); } - if (!(dlc & RX_BUF_EDL) && (id & RX_BUF_RTR)) { + if (!(dlc & RX_BUF_FDF) && (id & RX_BUF_RTR)) { cf->can_id |= CAN_RTR_FLAG; } else { if (dlc & RX_BUF_BRS) @@ -533,7 +590,7 @@ static int __m_can_get_berr_counter(const struct net_device *dev, ecr = m_can_read(priv, M_CAN_ECR); bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT; - bec->txerr = ecr & ECR_TEC_MASK; + bec->txerr = (ecr & ECR_TEC_MASK) >> ECR_TEC_SHIFT; return 0; } @@ -724,7 +781,7 @@ static int m_can_poll(struct napi_struct *napi, int quota) if (irqstatus & IR_ERR_STATE) work_done += m_can_handle_state_errors(dev, psr); - if (irqstatus & IR_ERR_BUS) + if (irqstatus & IR_ERR_BUS_30X) work_done += m_can_handle_bus_errors(dev, irqstatus, psr); if (irqstatus & IR_RF0N) @@ -759,7 +816,7 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) * - state change IRQ * - bus error IRQ and bus error reporting */ - if ((ir & IR_RF0N) || (ir & IR_ERR_ALL)) { + if ((ir & IR_RF0N) || (ir & IR_ERR_ALL_30X)) { priv->irqstatus = ir; m_can_disable_all_interrupts(priv); napi_schedule(&priv->napi); @@ -812,19 +869,19 @@ static int m_can_set_bittiming(struct net_device *dev) sjw = bt->sjw - 1; tseg1 = bt->prop_seg + bt->phase_seg1 - 1; tseg2 = bt->phase_seg2 - 1; - reg_btp = (brp << BTR_BRP_SHIFT) | (sjw << BTR_SJW_SHIFT) | - (tseg1 << BTR_TSEG1_SHIFT) | (tseg2 << BTR_TSEG2_SHIFT); - m_can_write(priv, M_CAN_BTP, reg_btp); + reg_btp = (brp << NBTP_NBRP_SHIFT) | (sjw << NBTP_NSJW_SHIFT) | + (tseg1 << NBTP_NTSEG1_SHIFT) | (tseg2 << NBTP_NTSEG2_SHIFT); + m_can_write(priv, M_CAN_NBTP, reg_btp); if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { brp = dbt->brp - 1; sjw = dbt->sjw - 1; tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; tseg2 = dbt->phase_seg2 - 1; - reg_btp = (brp << FBTR_FBRP_SHIFT) | (sjw << FBTR_FSJW_SHIFT) | - (tseg1 << FBTR_FTSEG1_SHIFT) | - (tseg2 << FBTR_FTSEG2_SHIFT); - m_can_write(priv, M_CAN_FBTP, reg_btp); + reg_btp = (brp << DBTP_DBRP_SHIFT) | (sjw << DBTP_DSJW_SHIFT) | + (tseg1 << DBTP_DTSEG1_SHIFT) | + (tseg2 << DBTP_DTSEG2_SHIFT); + m_can_write(priv, M_CAN_DBTP, reg_btp); } return 0; @@ -852,22 +909,22 @@ static void m_can_chip_config(struct net_device *dev) m_can_write(priv, M_CAN_GFC, 0x0); /* only support one Tx Buffer currently */ - m_can_write(priv, M_CAN_TXBC, (1 << TXBC_NDTB_OFF) | + m_can_write(priv, M_CAN_TXBC, (1 << TXBC_NDTB_SHIFT) | priv->mcfg[MRAM_TXB].off); /* support 64 bytes payload */ m_can_write(priv, M_CAN_TXESC, TXESC_TBDS_64BYTES); - m_can_write(priv, M_CAN_TXEFC, (1 << TXEFC_EFS_OFF) | + m_can_write(priv, M_CAN_TXEFC, (1 << TXEFC_EFS_SHIFT) | priv->mcfg[MRAM_TXE].off); /* rx fifo configuration, blocking mode, fifo size 1 */ m_can_write(priv, M_CAN_RXF0C, - (priv->mcfg[MRAM_RXF0].num << RXFC_FS_OFF) | + (priv->mcfg[MRAM_RXF0].num << RXFC_FS_SHIFT) | priv->mcfg[MRAM_RXF0].off); m_can_write(priv, M_CAN_RXF1C, - (priv->mcfg[MRAM_RXF1].num << RXFC_FS_OFF) | + (priv->mcfg[MRAM_RXF1].num << RXFC_FS_SHIFT) | priv->mcfg[MRAM_RXF1].off); cccr = m_can_read(priv, M_CAN_CCCR); @@ -893,7 +950,7 @@ static void m_can_chip_config(struct net_device *dev) /* enable interrupts */ m_can_write(priv, M_CAN_IR, IR_ALL_INT); if (!(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) - m_can_write(priv, M_CAN_IE, IR_ALL_INT & ~IR_ERR_LEC); + m_can_write(priv, M_CAN_IE, IR_ALL_INT & ~IR_ERR_LEC_30X); else m_can_write(priv, M_CAN_IE, IR_ALL_INT); @@ -1144,10 +1201,12 @@ static int m_can_of_parse_mram(struct platform_device *pdev, priv->mcfg[MRAM_XIDF].num = out_val[2]; priv->mcfg[MRAM_RXF0].off = priv->mcfg[MRAM_XIDF].off + priv->mcfg[MRAM_XIDF].num * XIDF_ELEMENT_SIZE; - priv->mcfg[MRAM_RXF0].num = out_val[3] & RXFC_FS_MASK; + priv->mcfg[MRAM_RXF0].num = out_val[3] & + (RXFC_FS_MASK >> RXFC_FS_SHIFT); priv->mcfg[MRAM_RXF1].off = priv->mcfg[MRAM_RXF0].off + priv->mcfg[MRAM_RXF0].num * RXF0_ELEMENT_SIZE; - priv->mcfg[MRAM_RXF1].num = out_val[4] & RXFC_FS_MASK; + priv->mcfg[MRAM_RXF1].num = out_val[4] & + (RXFC_FS_MASK >> RXFC_FS_SHIFT); priv->mcfg[MRAM_RXB].off = priv->mcfg[MRAM_RXF1].off + priv->mcfg[MRAM_RXF1].num * RXF1_ELEMENT_SIZE; priv->mcfg[MRAM_RXB].num = out_val[5]; @@ -1156,7 +1215,8 @@ static int m_can_of_parse_mram(struct platform_device *pdev, priv->mcfg[MRAM_TXE].num = out_val[6]; priv->mcfg[MRAM_TXB].off = priv->mcfg[MRAM_TXE].off + priv->mcfg[MRAM_TXE].num * TXE_ELEMENT_SIZE; - priv->mcfg[MRAM_TXB].num = out_val[7] & TXBC_NDTB_MASK; + priv->mcfg[MRAM_TXB].num = out_val[7] & + (TXBC_NDTB_MASK >> TXBC_NDTB_SHIFT); dev_dbg(&pdev->dev, "mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n", priv->mram_base, @@ -1192,7 +1252,7 @@ static int m_can_plat_probe(struct platform_device *pdev) hclk = devm_clk_get(&pdev->dev, "hclk"); cclk = devm_clk_get(&pdev->dev, "cclk"); if (IS_ERR(hclk) || IS_ERR(cclk)) { - dev_err(&pdev->dev, "no clock find\n"); + dev_err(&pdev->dev, "no clock found\n"); return -ENODEV; } -- cgit From b03cfc5bb0e11f88988e5da2805867e468d03ca1 Mon Sep 17 00:00:00 2001 From: Mario Huettel Date: Sat, 8 Apr 2017 14:10:13 +0200 Subject: can: m_can: Enable M_CAN version dependent initialization This patch adapts the initialization of the M_CAN. So it can be used with all versions >= 3.0.x. Changes: * Added version element to m_can_priv structure to hold M_CAN version. * Renamed bittiming structs for version 3.0.x * Added new bittiming structs for version >= 3.1.x * Function alloc_m_can_dev takes 2 new arguments. The TX FIFO size and the base address of the module. * Chip configuration for CAN_CTRLMODE_LOOPBACK is changed: Enabled CCCR_MON bit. In combination with TEST_LBCK it activates the internal loopback mode. Leaving CCCR_MON '0' results in external loopback mode. * Clocks are temporarily enabled by platform_propbe function in order to allow read access to the Core Release register and the Control Register. Registers are used to detect M_CAN version and optional Non-ISO Feature. Initialization of M_CAN for version >= 3.1.x: * TX FIFO of M_CAN is used to transmit frames. The driver does not need to stop the tx queue after each frame sent. * Initialization of TX Event FIFO is added. * NON-ISO is fixed for all M_CAN versions < 3.2.x. Version 3.2.x _can_ have the NISO (Non-ISO) bit which can switch the mode of the M_CAN to Non-ISO mode. This bit does not have to be writeable. Therefore it is checked. If it is writable Non-ISO support is added to the controllers supported CAN modes. New Functions: * Function to check the Core Release version. The read value determines the behaviour of the driver. * Function to check if the NISO bit for version >= 3.2.x is implemented. Signed-off-by: Mario Huettel Reviewed-by: Oliver Hartkopp Tested-by: Quentin Schulz Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 340 +++++++++++++++++++++++++++++++++--------- 1 file changed, 269 insertions(+), 71 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 15b5e1a1fc2b..bfc7b8424901 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -23,7 +23,7 @@ #include #include #include - +#include #include /* napi related */ @@ -107,6 +107,14 @@ enum m_can_mram_cfg { MRAM_CFG_NUM, }; +/* Core Release Register (CREL) */ +#define CREL_REL_SHIFT 28 +#define CREL_REL_MASK (0xF << CREL_REL_SHIFT) +#define CREL_STEP_SHIFT 24 +#define CREL_STEP_MASK (0xF << CREL_STEP_SHIFT) +#define CREL_SUBSTEP_SHIFT 20 +#define CREL_SUBSTEP_MASK (0xF << CREL_SUBSTEP_SHIFT) + /* Data Bit Timing & Prescaler Register (DBTP) */ #define DBTP_TDC BIT(23) #define DBTP_DBRP_SHIFT 16 @@ -342,6 +350,7 @@ struct m_can_priv { struct clk *cclk; void __iomem *base; u32 irqstatus; + int version; /* message ram configuration */ void __iomem *mram_base; @@ -833,7 +842,7 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static const struct can_bittiming_const m_can_bittiming_const = { +static const struct can_bittiming_const m_can_bittiming_const_30X = { .name = KBUILD_MODNAME, .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ .tseg1_max = 64, @@ -845,7 +854,7 @@ static const struct can_bittiming_const m_can_bittiming_const = { .brp_inc = 1, }; -static const struct can_bittiming_const m_can_data_bittiming_const = { +static const struct can_bittiming_const m_can_data_bittiming_const_30X = { .name = KBUILD_MODNAME, .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ .tseg1_max = 16, @@ -857,6 +866,30 @@ static const struct can_bittiming_const m_can_data_bittiming_const = { .brp_inc = 1, }; +static const struct can_bittiming_const m_can_bittiming_const_31X = { + .name = KBUILD_MODNAME, + .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 256, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 128, + .sjw_max = 128, + .brp_min = 1, + .brp_max = 512, + .brp_inc = 1, +}; + +static const struct can_bittiming_const m_can_data_bittiming_const_31X = { + .name = KBUILD_MODNAME, + .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 32, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 32, + .brp_inc = 1, +}; + static int m_can_set_bittiming(struct net_device *dev) { struct m_can_priv *priv = netdev_priv(dev); @@ -928,29 +961,53 @@ static void m_can_chip_config(struct net_device *dev) priv->mcfg[MRAM_RXF1].off); cccr = m_can_read(priv, M_CAN_CCCR); - cccr &= ~(CCCR_TEST | CCCR_MON | (CCCR_CMR_MASK << CCCR_CMR_SHIFT) | - (CCCR_CME_MASK << CCCR_CME_SHIFT)); test = m_can_read(priv, M_CAN_TEST); test &= ~TEST_LBCK; + if (priv->version == 30) { + /* Version 3.0.x */ - if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) - cccr |= CCCR_MON; + cccr &= ~(CCCR_TEST | CCCR_MON | + (CCCR_CMR_MASK << CCCR_CMR_SHIFT) | + (CCCR_CME_MASK << CCCR_CME_SHIFT)); + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT; + + } else { + /* Version 3.1.x or 3.2.x */ + cccr &= ~(CCCR_TEST | CCCR_MON | CCCR_BRSE | CCCR_FDOE); + + /* Only 3.2.x has NISO Bit implemented */ + if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) + cccr |= CCCR_NISO; + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + cccr |= (CCCR_BRSE | CCCR_FDOE); + } + /* Loopback Mode */ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { - cccr |= CCCR_TEST; + cccr |= CCCR_TEST | CCCR_MON; test |= TEST_LBCK; } - if (priv->can.ctrlmode & CAN_CTRLMODE_FD) - cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT; + /* Enable Monitoring (all versions) */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + cccr |= CCCR_MON; + /* Write config */ m_can_write(priv, M_CAN_CCCR, cccr); m_can_write(priv, M_CAN_TEST, test); - /* enable interrupts */ + /* Enable interrupts */ m_can_write(priv, M_CAN_IR, IR_ALL_INT); if (!(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) - m_can_write(priv, M_CAN_IE, IR_ALL_INT & ~IR_ERR_LEC_30X); + if (priv->version == 30) + m_can_write(priv, M_CAN_IE, IR_ALL_INT & + ~(IR_ERR_LEC_30X)); + else + m_can_write(priv, M_CAN_IE, IR_ALL_INT & + ~(IR_ERR_LEC_31X)); else m_can_write(priv, M_CAN_IE, IR_ALL_INT); @@ -994,33 +1051,140 @@ static void free_m_can_dev(struct net_device *dev) free_candev(dev); } -static struct net_device *alloc_m_can_dev(void) +/* Checks core release number of M_CAN + * returns 0 if an unsupported device is detected + * else it returns the release and step coded as: + * return value = 10 * + 1 * + */ +static int m_can_check_core_release(void __iomem *m_can_base) +{ + u32 crel_reg; + u8 rel; + u8 step; + int res; + struct m_can_priv temp_priv = { + .base = m_can_base + }; + + /* Read Core Release Version and split into version number + * Example: Version 3.2.1 => rel = 3; step = 2; substep = 1; + */ + crel_reg = m_can_read(&temp_priv, M_CAN_CREL); + rel = (u8)((crel_reg & CREL_REL_MASK) >> CREL_REL_SHIFT); + step = (u8)((crel_reg & CREL_STEP_MASK) >> CREL_STEP_SHIFT); + + if (rel == 3) { + /* M_CAN v3.x.y: create return value */ + res = 30 + step; + } else { + /* Unsupported M_CAN version */ + res = 0; + } + + return res; +} + +/* Selectable Non ISO support only in version 3.2.x + * This function checks if the bit is writable. + */ +static bool m_can_niso_supported(const struct m_can_priv *priv) +{ + u32 cccr_reg, cccr_poll; + int niso_timeout; + + m_can_config_endisable(priv, true); + cccr_reg = m_can_read(priv, M_CAN_CCCR); + cccr_reg |= CCCR_NISO; + m_can_write(priv, M_CAN_CCCR, cccr_reg); + + niso_timeout = readl_poll_timeout((priv->base + M_CAN_CCCR), cccr_poll, + (cccr_poll == cccr_reg), 0, 10); + + /* Clear NISO */ + cccr_reg &= ~(CCCR_NISO); + m_can_write(priv, M_CAN_CCCR, cccr_reg); + + m_can_config_endisable(priv, false); + + /* return false if time out (-ETIMEDOUT), else return true */ + return !niso_timeout; +} + +static struct net_device *alloc_m_can_dev(struct platform_device *pdev, + void __iomem *addr, u32 tx_fifo_size) { struct net_device *dev; struct m_can_priv *priv; + int m_can_version; + unsigned int echo_buffer_count; + + m_can_version = m_can_check_core_release(addr); + /* return if unsupported version */ + if (!m_can_version) { + dev = NULL; + goto return_dev; + } - dev = alloc_candev(sizeof(*priv), 1); - if (!dev) - return NULL; + /* If version < 3.1.x, then only one echo buffer is used */ + echo_buffer_count = ((m_can_version == 30) + ? 1U + : (unsigned int)tx_fifo_size); + dev = alloc_candev(sizeof(*priv), echo_buffer_count); + if (!dev) { + dev = NULL; + goto return_dev; + } priv = netdev_priv(dev); netif_napi_add(dev, &priv->napi, m_can_poll, M_CAN_NAPI_WEIGHT); + /* Shared properties of all M_CAN versions */ + priv->version = m_can_version; priv->dev = dev; - priv->can.bittiming_const = &m_can_bittiming_const; - priv->can.data_bittiming_const = &m_can_data_bittiming_const; + priv->base = addr; priv->can.do_set_mode = m_can_set_mode; priv->can.do_get_berr_counter = m_can_get_berr_counter; - /* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.1 */ - can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO); - - /* CAN_CTRLMODE_FD_NON_ISO can not be changed with M_CAN IP v3.0.1 */ + /* Set M_CAN supported operations */ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_BERR_REPORTING | CAN_CTRLMODE_FD; + /* Set properties depending on M_CAN version */ + switch (priv->version) { + case 30: + /* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.x */ + can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO); + priv->can.bittiming_const = &m_can_bittiming_const_30X; + priv->can.data_bittiming_const = + &m_can_data_bittiming_const_30X; + break; + case 31: + /* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.1.x */ + can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO); + priv->can.bittiming_const = &m_can_bittiming_const_31X; + priv->can.data_bittiming_const = + &m_can_data_bittiming_const_31X; + break; + case 32: + priv->can.bittiming_const = &m_can_bittiming_const_31X; + priv->can.data_bittiming_const = + &m_can_data_bittiming_const_31X; + priv->can.ctrlmode_supported |= (m_can_niso_supported(priv) + ? CAN_CTRLMODE_FD_NON_ISO + : 0); + break; + default: + /* Unsupported device: free candev */ + free_m_can_dev(dev); + dev_err(&pdev->dev, "Unsupported version number: %2d", + priv->version); + dev = NULL; + break; + } + +return_dev: return dev; } @@ -1167,58 +1331,37 @@ static int register_m_can_dev(struct net_device *dev) return register_candev(dev); } -static int m_can_of_parse_mram(struct platform_device *pdev, - struct m_can_priv *priv) +static void m_can_of_parse_mram(struct m_can_priv *priv, + const u32 *mram_config_vals) { - struct device_node *np = pdev->dev.of_node; - struct resource *res; - void __iomem *addr; - u32 out_val[MRAM_CFG_LEN]; - int i, start, end, ret; + int i, start, end; - /* message ram could be shared */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram"); - if (!res) - return -ENODEV; - - addr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (!addr) - return -ENOMEM; - - /* get message ram configuration */ - ret = of_property_read_u32_array(np, "bosch,mram-cfg", - out_val, sizeof(out_val) / 4); - if (ret) { - dev_err(&pdev->dev, "can not get message ram configuration\n"); - return -ENODEV; - } - - priv->mram_base = addr; - priv->mcfg[MRAM_SIDF].off = out_val[0]; - priv->mcfg[MRAM_SIDF].num = out_val[1]; + priv->mcfg[MRAM_SIDF].off = mram_config_vals[0]; + priv->mcfg[MRAM_SIDF].num = mram_config_vals[1]; priv->mcfg[MRAM_XIDF].off = priv->mcfg[MRAM_SIDF].off + priv->mcfg[MRAM_SIDF].num * SIDF_ELEMENT_SIZE; - priv->mcfg[MRAM_XIDF].num = out_val[2]; + priv->mcfg[MRAM_XIDF].num = mram_config_vals[2]; priv->mcfg[MRAM_RXF0].off = priv->mcfg[MRAM_XIDF].off + priv->mcfg[MRAM_XIDF].num * XIDF_ELEMENT_SIZE; - priv->mcfg[MRAM_RXF0].num = out_val[3] & + priv->mcfg[MRAM_RXF0].num = mram_config_vals[3] & (RXFC_FS_MASK >> RXFC_FS_SHIFT); priv->mcfg[MRAM_RXF1].off = priv->mcfg[MRAM_RXF0].off + priv->mcfg[MRAM_RXF0].num * RXF0_ELEMENT_SIZE; - priv->mcfg[MRAM_RXF1].num = out_val[4] & + priv->mcfg[MRAM_RXF1].num = mram_config_vals[4] & (RXFC_FS_MASK >> RXFC_FS_SHIFT); priv->mcfg[MRAM_RXB].off = priv->mcfg[MRAM_RXF1].off + priv->mcfg[MRAM_RXF1].num * RXF1_ELEMENT_SIZE; - priv->mcfg[MRAM_RXB].num = out_val[5]; + priv->mcfg[MRAM_RXB].num = mram_config_vals[5]; priv->mcfg[MRAM_TXE].off = priv->mcfg[MRAM_RXB].off + priv->mcfg[MRAM_RXB].num * RXB_ELEMENT_SIZE; - priv->mcfg[MRAM_TXE].num = out_val[6]; + priv->mcfg[MRAM_TXE].num = mram_config_vals[6]; priv->mcfg[MRAM_TXB].off = priv->mcfg[MRAM_TXE].off + priv->mcfg[MRAM_TXE].num * TXE_ELEMENT_SIZE; - priv->mcfg[MRAM_TXB].num = out_val[7] & + priv->mcfg[MRAM_TXB].num = mram_config_vals[7] & (TXBC_NDTB_MASK >> TXBC_NDTB_SHIFT); - dev_dbg(&pdev->dev, "mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n", + dev_dbg(priv->device, + "mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n", priv->mram_base, priv->mcfg[MRAM_SIDF].off, priv->mcfg[MRAM_SIDF].num, priv->mcfg[MRAM_XIDF].off, priv->mcfg[MRAM_XIDF].num, @@ -1237,7 +1380,6 @@ static int m_can_of_parse_mram(struct platform_device *pdev, for (i = start; i < end; i += 4) writel(0x0, priv->mram_base + i); - return 0; } static int m_can_plat_probe(struct platform_device *pdev) @@ -1246,38 +1388,86 @@ static int m_can_plat_probe(struct platform_device *pdev) struct m_can_priv *priv; struct resource *res; void __iomem *addr; + void __iomem *mram_addr; struct clk *hclk, *cclk; int irq, ret; + struct device_node *np; + u32 mram_config_vals[MRAM_CFG_LEN]; + u32 tx_fifo_size; + + np = pdev->dev.of_node; hclk = devm_clk_get(&pdev->dev, "hclk"); cclk = devm_clk_get(&pdev->dev, "cclk"); + if (IS_ERR(hclk) || IS_ERR(cclk)) { dev_err(&pdev->dev, "no clock found\n"); - return -ENODEV; + ret = -ENODEV; + goto failed_ret; } + /* Enable clocks. Necessary to read Core Release in order to determine + * M_CAN version + */ + ret = clk_prepare_enable(hclk); + if (ret) + goto disable_hclk_ret; + + ret = clk_prepare_enable(cclk); + if (ret) + goto disable_cclk_ret; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can"); addr = devm_ioremap_resource(&pdev->dev, res); irq = platform_get_irq_byname(pdev, "int0"); - if (IS_ERR(addr) || irq < 0) - return -EINVAL; - /* allocate the m_can device */ - dev = alloc_m_can_dev(); - if (!dev) - return -ENOMEM; + if (IS_ERR(addr) || irq < 0) { + ret = -EINVAL; + goto disable_cclk_ret; + } + /* message ram could be shared */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram"); + if (!res) { + ret = -ENODEV; + goto disable_cclk_ret; + } + + mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!mram_addr) { + ret = -ENOMEM; + goto disable_cclk_ret; + } + + /* get message ram configuration */ + ret = of_property_read_u32_array(np, "bosch,mram-cfg", + mram_config_vals, + sizeof(mram_config_vals) / 4); + if (ret) { + dev_err(&pdev->dev, "Could not get Message RAM configuration."); + goto disable_cclk_ret; + } + + /* Get TX FIFO size + * Defines the total amount of echo buffers for loopback + */ + tx_fifo_size = mram_config_vals[7]; + + /* allocate the m_can device */ + dev = alloc_m_can_dev(pdev, addr, tx_fifo_size); + if (!dev) { + ret = -ENOMEM; + goto disable_cclk_ret; + } priv = netdev_priv(dev); dev->irq = irq; - priv->base = addr; priv->device = &pdev->dev; priv->hclk = hclk; priv->cclk = cclk; priv->can.clock.freq = clk_get_rate(cclk); + priv->mram_base = mram_addr; - ret = m_can_of_parse_mram(pdev, priv); - if (ret) - goto failed_free_dev; + m_can_of_parse_mram(priv, mram_config_vals); platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); @@ -1291,13 +1481,21 @@ static int m_can_plat_probe(struct platform_device *pdev) devm_can_led_init(dev); - dev_info(&pdev->dev, "%s device registered (irq=%d)\n", - KBUILD_MODNAME, dev->irq); + dev_info(&pdev->dev, "%s device registered (irq=%d, version=%d)\n", + KBUILD_MODNAME, dev->irq, priv->version); - return 0; + /* Probe finished + * Stop clocks. They will be reactivated once the M_CAN device is opened + */ + goto disable_cclk_ret; failed_free_dev: free_m_can_dev(dev); +disable_cclk_ret: + clk_disable_unprepare(cclk); +disable_hclk_ret: + clk_disable_unprepare(hclk); +failed_ret: return ret; } -- cgit From 428479e4714ce786629a009c66b4340a3ba651ce Mon Sep 17 00:00:00 2001 From: Mario Huettel Date: Sat, 8 Apr 2017 14:10:14 +0200 Subject: can: m_can: Configuration for TX and TX event FIFOs * TX/TX Event FIFO sizes are configured for version >= v3.1.x Signed-off-by: Mario Huettel Tested-by: Quentin Schulz Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index bfc7b8424901..620c421f6f6a 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -382,6 +382,18 @@ static inline void m_can_fifo_write(const struct m_can_priv *priv, fpi * TXB_ELEMENT_SIZE + offset); } +static inline u32 m_can_txe_fifo_read(const struct m_can_priv *priv, + u32 fgi, + u32 offset) { + return readl(priv->mram_base + priv->mcfg[MRAM_TXE].off + + fgi * TXE_ELEMENT_SIZE + offset); +} + +static inline bool m_can_tx_fifo_full(const struct m_can_priv *priv) +{ + return !!(m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQF); +} + static inline void m_can_config_endisable(const struct m_can_priv *priv, bool enable) { @@ -925,6 +937,7 @@ static int m_can_set_bittiming(struct net_device *dev) * - configure rx fifo * - accept non-matching frame into fifo 0 * - configure tx buffer + * - >= v3.1.x: TX FIFO is used * - configure mode * - setup bittiming */ @@ -941,15 +954,31 @@ static void m_can_chip_config(struct net_device *dev) /* Accept Non-matching Frames Into FIFO 0 */ m_can_write(priv, M_CAN_GFC, 0x0); - /* only support one Tx Buffer currently */ - m_can_write(priv, M_CAN_TXBC, (1 << TXBC_NDTB_SHIFT) | - priv->mcfg[MRAM_TXB].off); + if (priv->version == 30) { + /* only support one Tx Buffer currently */ + m_can_write(priv, M_CAN_TXBC, (1 << TXBC_NDTB_SHIFT) | + priv->mcfg[MRAM_TXB].off); + } else { + /* TX FIFO is used for newer IP Core versions */ + m_can_write(priv, M_CAN_TXBC, + (priv->mcfg[MRAM_TXB].num << TXBC_TFQS_SHIFT) | + (priv->mcfg[MRAM_TXB].off)); + } /* support 64 bytes payload */ m_can_write(priv, M_CAN_TXESC, TXESC_TBDS_64BYTES); - m_can_write(priv, M_CAN_TXEFC, (1 << TXEFC_EFS_SHIFT) | - priv->mcfg[MRAM_TXE].off); + /* TX Event FIFO */ + if (priv->version == 30) { + m_can_write(priv, M_CAN_TXEFC, (1 << TXEFC_EFS_SHIFT) | + priv->mcfg[MRAM_TXE].off); + } else { + /* Full TX Event FIFO is used */ + m_can_write(priv, M_CAN_TXEFC, + ((priv->mcfg[MRAM_TXE].num << TXEFC_EFS_SHIFT) + & TXEFC_EFS_MASK) | + priv->mcfg[MRAM_TXE].off); + } /* rx fifo configuration, blocking mode, fifo size 1 */ m_can_write(priv, M_CAN_RXF0C, -- cgit From 10c1c3975a6663375b38e67949722ba029201395 Mon Sep 17 00:00:00 2001 From: Mario Huettel Date: Sat, 8 Apr 2017 14:10:15 +0200 Subject: can: m_can: Enable TX FIFO Handling for M_CAN IP version >= v3.1.x * Added defines for TX Event FIFO Element * Adapted ndo_start_xmit function. For versions >= v3.1.x it uses the TX FIFO to optimize the data throughput. It stores the echo skb at the same index as in the M_CAN's TX FIFO. The frame's message marker is set to this index. This message marker is received in the TX Event FIFO after the message was successfully transmitted. It is used to echo the correct echo skb back to the network stack. * Added m_can_echo_tx_event function. It reads all received message markers in the TX Event FIFO and loops back the corresponding echo skbs. * ISR checks for new TX Event Entry interrupt for version >= 3.1.x. Signed-off-by: Mario Huettel Reviewed-by: Oliver Hartkopp Tested-by: Quentin Schulz Signed-off-by: Marc Kleine-Budde --- drivers/net/can/m_can/m_can.c | 188 +++++++++++++++++++++++++++++++++++------- 1 file changed, 159 insertions(+), 29 deletions(-) diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 620c421f6f6a..bf8fdaeb955e 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -334,6 +334,11 @@ enum m_can_mram_cfg { #define TX_BUF_MM_SHIFT 24 #define TX_BUF_MM_MASK (0xff << TX_BUF_MM_SHIFT) +/* Tx event FIFO Element */ +/* E1 */ +#define TX_EVENT_MM_SHIFT TX_BUF_MM_SHIFT +#define TX_EVENT_MM_MASK (0xff << TX_EVENT_MM_SHIFT) + /* address offset and element number for each FIFO/Buffer in the Message RAM */ struct mram_cfg { u16 off; @@ -817,6 +822,44 @@ end: return work_done; } +static void m_can_echo_tx_event(struct net_device *dev) +{ + u32 txe_count = 0; + u32 m_can_txefs; + u32 fgi = 0; + int i = 0; + unsigned int msg_mark; + + struct m_can_priv *priv = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + + /* read tx event fifo status */ + m_can_txefs = m_can_read(priv, M_CAN_TXEFS); + + /* Get Tx Event fifo element count */ + txe_count = (m_can_txefs & TXEFS_EFFL_MASK) + >> TXEFS_EFFL_SHIFT; + + /* Get and process all sent elements */ + for (i = 0; i < txe_count; i++) { + /* retrieve get index */ + fgi = (m_can_read(priv, M_CAN_TXEFS) & TXEFS_EFGI_MASK) + >> TXEFS_EFGI_SHIFT; + + /* get message marker */ + msg_mark = (m_can_txe_fifo_read(priv, fgi, 4) & + TX_EVENT_MM_MASK) >> TX_EVENT_MM_SHIFT; + + /* ack txe element */ + m_can_write(priv, M_CAN_TXEFA, (TXEFA_EFAI_MASK & + (fgi << TXEFA_EFAI_SHIFT))); + + /* update stats */ + stats->tx_bytes += can_get_echo_skb(dev, msg_mark); + stats->tx_packets++; + } +} + static irqreturn_t m_can_isr(int irq, void *dev_id) { struct net_device *dev = (struct net_device *)dev_id; @@ -843,12 +886,23 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) napi_schedule(&priv->napi); } - /* transmission complete interrupt */ - if (ir & IR_TC) { - stats->tx_bytes += can_get_echo_skb(dev, 0); - stats->tx_packets++; - can_led_event(dev, CAN_LED_EVENT_TX); - netif_wake_queue(dev); + if (priv->version == 30) { + if (ir & IR_TC) { + /* Transmission Complete Interrupt*/ + stats->tx_bytes += can_get_echo_skb(dev, 0); + stats->tx_packets++; + can_led_event(dev, CAN_LED_EVENT_TX); + netif_wake_queue(dev); + } + } else { + if (ir & IR_TEFN) { + /* New TX FIFO Element arrived */ + m_can_echo_tx_event(dev); + can_led_event(dev, CAN_LED_EVENT_TX); + if (netif_queue_stopped(dev) && + !m_can_tx_fifo_full(priv)) + netif_wake_queue(dev); + } } return IRQ_HANDLED; @@ -1291,19 +1345,34 @@ static int m_can_close(struct net_device *dev) return 0; } +static int m_can_next_echo_skb_occupied(struct net_device *dev, int putidx) +{ + struct m_can_priv *priv = netdev_priv(dev); + /*get wrap around for loopback skb index */ + unsigned int wrap = priv->can.echo_skb_max; + int next_idx; + + /* calculate next index */ + next_idx = (++putidx >= wrap ? 0 : putidx); + + /* check if occupied */ + return !!priv->can.echo_skb[next_idx]; +} + static netdev_tx_t m_can_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct m_can_priv *priv = netdev_priv(dev); struct canfd_frame *cf = (struct canfd_frame *)skb->data; - u32 id, cccr; + u32 id, cccr, fdflags; int i; + int putidx; if (can_dropped_invalid_skb(dev, skb)) return NETDEV_TX_OK; - netif_stop_queue(dev); - + /* Generate ID field for TX buffer Element */ + /* Common to all supported M_CAN versions */ if (cf->can_id & CAN_EFF_FLAG) { id = cf->can_id & CAN_EFF_MASK; id |= TX_BUF_XTD; @@ -1314,33 +1383,93 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb, if (cf->can_id & CAN_RTR_FLAG) id |= TX_BUF_RTR; - /* message ram configuration */ - m_can_fifo_write(priv, 0, M_CAN_FIFO_ID, id); - m_can_fifo_write(priv, 0, M_CAN_FIFO_DLC, can_len2dlc(cf->len) << 16); + if (priv->version == 30) { + netif_stop_queue(dev); + + /* message ram configuration */ + m_can_fifo_write(priv, 0, M_CAN_FIFO_ID, id); + m_can_fifo_write(priv, 0, M_CAN_FIFO_DLC, + can_len2dlc(cf->len) << 16); - for (i = 0; i < cf->len; i += 4) - m_can_fifo_write(priv, 0, M_CAN_FIFO_DATA(i / 4), - *(u32 *)(cf->data + i)); + for (i = 0; i < cf->len; i += 4) + m_can_fifo_write(priv, 0, + M_CAN_FIFO_DATA(i / 4), + *(u32 *)(cf->data + i)); + + can_put_echo_skb(skb, dev, 0); + + if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { + cccr = m_can_read(priv, M_CAN_CCCR); + cccr &= ~(CCCR_CMR_MASK << CCCR_CMR_SHIFT); + if (can_is_canfd_skb(skb)) { + if (cf->flags & CANFD_BRS) + cccr |= CCCR_CMR_CANFD_BRS << + CCCR_CMR_SHIFT; + else + cccr |= CCCR_CMR_CANFD << + CCCR_CMR_SHIFT; + } else { + cccr |= CCCR_CMR_CAN << CCCR_CMR_SHIFT; + } + m_can_write(priv, M_CAN_CCCR, cccr); + } + m_can_write(priv, M_CAN_TXBTIE, 0x1); + m_can_write(priv, M_CAN_TXBAR, 0x1); + /* End of xmit function for version 3.0.x */ + } else { + /* Transmit routine for version >= v3.1.x */ + + /* Check if FIFO full */ + if (m_can_tx_fifo_full(priv)) { + /* This shouldn't happen */ + netif_stop_queue(dev); + netdev_warn(dev, + "TX queue active although FIFO is full."); + return NETDEV_TX_BUSY; + } - can_put_echo_skb(skb, dev, 0); + /* get put index for frame */ + putidx = ((m_can_read(priv, M_CAN_TXFQS) & TXFQS_TFQPI_MASK) + >> TXFQS_TFQPI_SHIFT); + /* Write ID Field to FIFO Element */ + m_can_fifo_write(priv, putidx, M_CAN_FIFO_ID, id); - if (priv->can.ctrlmode & CAN_CTRLMODE_FD) { - cccr = m_can_read(priv, M_CAN_CCCR); - cccr &= ~(CCCR_CMR_MASK << CCCR_CMR_SHIFT); + /* get CAN FD configuration of frame */ + fdflags = 0; if (can_is_canfd_skb(skb)) { + fdflags |= TX_BUF_FDF; if (cf->flags & CANFD_BRS) - cccr |= CCCR_CMR_CANFD_BRS << CCCR_CMR_SHIFT; - else - cccr |= CCCR_CMR_CANFD << CCCR_CMR_SHIFT; - } else { - cccr |= CCCR_CMR_CAN << CCCR_CMR_SHIFT; + fdflags |= TX_BUF_BRS; } - m_can_write(priv, M_CAN_CCCR, cccr); - } - /* enable first TX buffer to start transfer */ - m_can_write(priv, M_CAN_TXBTIE, 0x1); - m_can_write(priv, M_CAN_TXBAR, 0x1); + /* Construct DLC Field. Also contains CAN-FD configuration + * use put index of fifo as message marker + * it is used in TX interrupt for + * sending the correct echo frame + */ + m_can_fifo_write(priv, putidx, M_CAN_FIFO_DLC, + ((putidx << TX_BUF_MM_SHIFT) & + TX_BUF_MM_MASK) | + (can_len2dlc(cf->len) << 16) | + fdflags | TX_BUF_EFC); + + for (i = 0; i < cf->len; i += 4) + m_can_fifo_write(priv, putidx, M_CAN_FIFO_DATA(i / 4), + *(u32 *)(cf->data + i)); + + /* Push loopback echo. + * Will be looped back on TX interrupt based on message marker + */ + can_put_echo_skb(skb, dev, putidx); + + /* Enable TX FIFO element to start transfer */ + m_can_write(priv, M_CAN_TXBAR, (1 << putidx)); + + /* stop network queue if fifo full */ + if (m_can_tx_fifo_full(priv) || + m_can_next_echo_skb_occupied(dev, putidx)) + netif_stop_queue(dev); + } return NETDEV_TX_OK; } @@ -1516,6 +1645,7 @@ static int m_can_plat_probe(struct platform_device *pdev) /* Probe finished * Stop clocks. They will be reactivated once the M_CAN device is opened */ + goto disable_cclk_ret; failed_free_dev: -- cgit From 51f3baad7de943780ce0c17bd7975df567dd6e14 Mon Sep 17 00:00:00 2001 From: Remigiusz Kołłątaj Date: Fri, 14 Apr 2017 20:32:28 +0200 Subject: can: mcba_usb: Add support for Microchip CAN BUS Analyzer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SocketCAN driver for Microchip CAN BUS Analyzer (http://www.microchip.com/development-tools/) Changes in v4: - possible memory leak fixed in mcba_usb_write_bulk_callback - LED support added - failure handling in mcba_usb_probe improved - C99 initializers for structs on stack Changes in v3: - improved/simplified CAN ID conversion - functions for transmission of skb and cmd separated - fixed/improved netif_stop_queue handling - style/cosmetic corrections Changes in v2: - Termination handling reimplemented to fit new netlink API (IFLA_CAN_TERMINATION) - Bitrate handling reimplemented to fit new netlink API (IFLA_CAN_BITRATE) - CAN ID conversion refactored (changed from macro to inline functions) - CAN DLC handling using get_can_dlc() - Endianness handling for can_speed introduced - Debugging removed - Redundant error prints removed - Style/cosmetic corrections (i.e. macro names, redefs, inits etc.) Signed-off-by: Remigiusz Kołłątaj Signed-off-by: Marc Kleine-Budde --- drivers/net/can/usb/Kconfig | 6 + drivers/net/can/usb/Makefile | 1 + drivers/net/can/usb/mcba_usb.c | 904 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 911 insertions(+) create mode 100644 drivers/net/can/usb/mcba_usb.c diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index 8483a40e7e9e..2d0313eb31c0 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -81,4 +81,10 @@ config CAN_8DEV_USB This driver supports the USB2CAN interface from 8 devices (http://www.8devices.com). +config CAN_MCBA_USB + tristate "Microchip CAN BUS Analyzer interface" + ---help--- + This driver supports the CAN BUS Analyzer interface + from Microchip (http://www.microchip.com/development-tools/). + endmenu diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile index a64cf983fb87..164453fd55d0 100644 --- a/drivers/net/can/usb/Makefile +++ b/drivers/net/can/usb/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_CAN_GS_USB) += gs_usb.o obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o +obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o diff --git a/drivers/net/can/usb/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c new file mode 100644 index 000000000000..7f0272558bef --- /dev/null +++ b/drivers/net/can/usb/mcba_usb.c @@ -0,0 +1,904 @@ +/* SocketCAN driver for Microchip CAN BUS Analyzer Tool + * + * Copyright (C) 2017 Mobica Limited + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. + * + * This driver is inspired by the 4.6.2 version of net/can/usb/usb_8dev.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* vendor and product id */ +#define MCBA_MODULE_NAME "mcba_usb" +#define MCBA_VENDOR_ID 0x04d8 +#define MCBA_PRODUCT_ID 0x0a30 + +/* driver constants */ +#define MCBA_MAX_RX_URBS 20 +#define MCBA_MAX_TX_URBS 20 +#define MCBA_CTX_FREE MCBA_MAX_TX_URBS + +/* RX buffer must be bigger than msg size since at the + * beggining USB messages are stacked. + */ +#define MCBA_USB_RX_BUFF_SIZE 64 +#define MCBA_USB_TX_BUFF_SIZE (sizeof(struct mcba_usb_msg)) + +/* MCBA endpoint numbers */ +#define MCBA_USB_EP_IN 1 +#define MCBA_USB_EP_OUT 1 + +/* Microchip command id */ +#define MBCA_CMD_RECEIVE_MESSAGE 0xE3 +#define MBCA_CMD_I_AM_ALIVE_FROM_CAN 0xF5 +#define MBCA_CMD_I_AM_ALIVE_FROM_USB 0xF7 +#define MBCA_CMD_CHANGE_BIT_RATE 0xA1 +#define MBCA_CMD_TRANSMIT_MESSAGE_EV 0xA3 +#define MBCA_CMD_SETUP_TERMINATION_RESISTANCE 0xA8 +#define MBCA_CMD_READ_FW_VERSION 0xA9 +#define MBCA_CMD_NOTHING_TO_SEND 0xFF +#define MBCA_CMD_TRANSMIT_MESSAGE_RSP 0xE2 + +#define MCBA_VER_REQ_USB 1 +#define MCBA_VER_REQ_CAN 2 + +#define MCBA_SIDL_EXID_MASK 0x8 +#define MCBA_DLC_MASK 0xf +#define MCBA_DLC_RTR_MASK 0x40 + +#define MCBA_CAN_STATE_WRN_TH 95 +#define MCBA_CAN_STATE_ERR_PSV_TH 127 + +#define MCBA_TERMINATION_DISABLED CAN_TERMINATION_DISABLED +#define MCBA_TERMINATION_ENABLED 120 + +struct mcba_usb_ctx { + struct mcba_priv *priv; + u32 ndx; + u8 dlc; + bool can; +}; + +/* Structure to hold all of our device specific stuff */ +struct mcba_priv { + struct can_priv can; /* must be the first member */ + struct sk_buff *echo_skb[MCBA_MAX_TX_URBS]; + struct mcba_usb_ctx tx_context[MCBA_MAX_TX_URBS]; + struct usb_device *udev; + struct net_device *netdev; + struct usb_anchor tx_submitted; + struct usb_anchor rx_submitted; + struct can_berr_counter bec; + bool usb_ka_first_pass; + bool can_ka_first_pass; + bool can_speed_check; + atomic_t free_ctx_cnt; +}; + +/* CAN frame */ +struct __packed mcba_usb_msg_can { + u8 cmd_id; + __be16 eid; + __be16 sid; + u8 dlc; + u8 data[8]; + u8 timestamp[4]; + u8 checksum; +}; + +/* command frame */ +struct __packed mcba_usb_msg { + u8 cmd_id; + u8 unused[18]; +}; + +struct __packed mcba_usb_msg_ka_usb { + u8 cmd_id; + u8 termination_state; + u8 soft_ver_major; + u8 soft_ver_minor; + u8 unused[15]; +}; + +struct __packed mcba_usb_msg_ka_can { + u8 cmd_id; + u8 tx_err_cnt; + u8 rx_err_cnt; + u8 rx_buff_ovfl; + u8 tx_bus_off; + __be16 can_bitrate; + __le16 rx_lost; + u8 can_stat; + u8 soft_ver_major; + u8 soft_ver_minor; + u8 debug_mode; + u8 test_complete; + u8 test_result; + u8 unused[4]; +}; + +struct __packed mcba_usb_msg_change_bitrate { + u8 cmd_id; + __be16 bitrate; + u8 unused[16]; +}; + +struct __packed mcba_usb_msg_termination { + u8 cmd_id; + u8 termination; + u8 unused[17]; +}; + +struct __packed mcba_usb_msg_fw_ver { + u8 cmd_id; + u8 pic; + u8 unused[17]; +}; + +static const struct usb_device_id mcba_usb_table[] = { + { USB_DEVICE(MCBA_VENDOR_ID, MCBA_PRODUCT_ID) }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, mcba_usb_table); + +static const u16 mcba_termination[] = { MCBA_TERMINATION_DISABLED, + MCBA_TERMINATION_ENABLED }; + +static const u32 mcba_bitrate[] = { 20000, 33333, 50000, 80000, 83333, + 100000, 125000, 150000, 175000, 200000, + 225000, 250000, 275000, 300000, 500000, + 625000, 800000, 1000000 }; + +static inline void mcba_init_ctx(struct mcba_priv *priv) +{ + int i = 0; + + for (i = 0; i < MCBA_MAX_TX_URBS; i++) { + priv->tx_context[i].ndx = MCBA_CTX_FREE; + priv->tx_context[i].priv = priv; + } + + atomic_set(&priv->free_ctx_cnt, ARRAY_SIZE(priv->tx_context)); +} + +static inline struct mcba_usb_ctx *mcba_usb_get_free_ctx(struct mcba_priv *priv, + struct can_frame *cf) +{ + int i = 0; + struct mcba_usb_ctx *ctx = NULL; + + for (i = 0; i < MCBA_MAX_TX_URBS; i++) { + if (priv->tx_context[i].ndx == MCBA_CTX_FREE) { + ctx = &priv->tx_context[i]; + ctx->ndx = i; + + if (cf) { + ctx->can = true; + ctx->dlc = cf->can_dlc; + } else { + ctx->can = false; + ctx->dlc = 0; + } + + atomic_dec(&priv->free_ctx_cnt); + break; + } + } + + if (!atomic_read(&priv->free_ctx_cnt)) + /* That was the last free ctx. Slow down tx path */ + netif_stop_queue(priv->netdev); + + return ctx; +} + +/* mcba_usb_free_ctx and mcba_usb_get_free_ctx are executed by different + * threads. The order of execution in below function is important. + */ +static inline void mcba_usb_free_ctx(struct mcba_usb_ctx *ctx) +{ + /* Increase number of free ctxs before freeing ctx */ + atomic_inc(&ctx->priv->free_ctx_cnt); + + ctx->ndx = MCBA_CTX_FREE; + + /* Wake up the queue once ctx is marked free */ + netif_wake_queue(ctx->priv->netdev); +} + +static void mcba_usb_write_bulk_callback(struct urb *urb) +{ + struct mcba_usb_ctx *ctx = urb->context; + struct net_device *netdev; + + WARN_ON(!ctx); + + netdev = ctx->priv->netdev; + + /* free up our allocated buffer */ + usb_free_coherent(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + + if (ctx->can) { + if (!netif_device_present(netdev)) + return; + + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += ctx->dlc; + + can_led_event(netdev, CAN_LED_EVENT_TX); + can_get_echo_skb(netdev, ctx->ndx); + } + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); + + /* Release the context */ + mcba_usb_free_ctx(ctx); +} + +/* Send data to device */ +static netdev_tx_t mcba_usb_xmit(struct mcba_priv *priv, + struct mcba_usb_msg *usb_msg, + struct mcba_usb_ctx *ctx) +{ + struct urb *urb; + u8 *buf; + int err; + + /* create a URB, and a buffer for it, and copy the data to the URB */ + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + buf = usb_alloc_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, GFP_ATOMIC, + &urb->transfer_dma); + if (!buf) { + err = -ENOMEM; + goto nomembuf; + } + + memcpy(buf, usb_msg, MCBA_USB_TX_BUFF_SIZE); + + usb_fill_bulk_urb(urb, priv->udev, + usb_sndbulkpipe(priv->udev, MCBA_USB_EP_OUT), buf, + MCBA_USB_TX_BUFF_SIZE, mcba_usb_write_bulk_callback, + ctx); + + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &priv->tx_submitted); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) + goto failed; + + /* Release our reference to this URB, the USB core will eventually free + * it entirely. + */ + usb_free_urb(urb); + + return 0; + +failed: + usb_unanchor_urb(urb); + usb_free_coherent(priv->udev, MCBA_USB_TX_BUFF_SIZE, buf, + urb->transfer_dma); + + if (err == -ENODEV) + netif_device_detach(priv->netdev); + else + netdev_warn(priv->netdev, "failed tx_urb %d\n", err); + +nomembuf: + usb_free_urb(urb); + + return err; +} + +/* Send data to device */ +static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + struct can_frame *cf = (struct can_frame *)skb->data; + struct mcba_usb_ctx *ctx = NULL; + struct net_device_stats *stats = &priv->netdev->stats; + u16 sid; + int err; + struct mcba_usb_msg_can usb_msg = { + .cmd_id = MBCA_CMD_TRANSMIT_MESSAGE_EV + }; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + ctx = mcba_usb_get_free_ctx(priv, cf); + if (!ctx) + return NETDEV_TX_BUSY; + + can_put_echo_skb(skb, priv->netdev, ctx->ndx); + + if (cf->can_id & CAN_EFF_FLAG) { + /* SIDH | SIDL | EIDH | EIDL + * 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0 + */ + sid = MCBA_SIDL_EXID_MASK; + /* store 28-18 bits */ + sid |= (cf->can_id & 0x1ffc0000) >> 13; + /* store 17-16 bits */ + sid |= (cf->can_id & 0x30000) >> 16; + put_unaligned_be16(sid, &usb_msg.sid); + + /* store 15-0 bits */ + put_unaligned_be16(cf->can_id & 0xffff, &usb_msg.eid); + } else { + /* SIDH | SIDL + * 10 - 3 | 2 1 0 x x x x x + */ + put_unaligned_be16((cf->can_id & CAN_SFF_MASK) << 5, + &usb_msg.sid); + usb_msg.eid = 0; + } + + usb_msg.dlc = cf->can_dlc; + + memcpy(usb_msg.data, cf->data, usb_msg.dlc); + + if (cf->can_id & CAN_RTR_FLAG) + usb_msg.dlc |= MCBA_DLC_RTR_MASK; + + err = mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, ctx); + if (err) + goto xmit_failed; + + return NETDEV_TX_OK; + +xmit_failed: + can_free_echo_skb(priv->netdev, ctx->ndx); + mcba_usb_free_ctx(ctx); + dev_kfree_skb(skb); + stats->tx_dropped++; + + return NETDEV_TX_OK; +} + +/* Send cmd to device */ +static void mcba_usb_xmit_cmd(struct mcba_priv *priv, + struct mcba_usb_msg *usb_msg) +{ + struct mcba_usb_ctx *ctx = NULL; + int err; + + ctx = mcba_usb_get_free_ctx(priv, NULL); + if (!ctx) { + netdev_err(priv->netdev, + "Lack of free ctx. Sending (%d) cmd aborted", + usb_msg->cmd_id); + + return; + } + + err = mcba_usb_xmit(priv, usb_msg, ctx); + if (err) + netdev_err(priv->netdev, "Failed to send cmd (%d)", + usb_msg->cmd_id); +} + +static void mcba_usb_xmit_change_bitrate(struct mcba_priv *priv, u16 bitrate) +{ + struct mcba_usb_msg_change_bitrate usb_msg = { + .cmd_id = MBCA_CMD_CHANGE_BIT_RATE + }; + + put_unaligned_be16(bitrate, &usb_msg.bitrate); + + mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg); +} + +static void mcba_usb_xmit_read_fw_ver(struct mcba_priv *priv, u8 pic) +{ + struct mcba_usb_msg_fw_ver usb_msg = { + .cmd_id = MBCA_CMD_READ_FW_VERSION, + .pic = pic + }; + + mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg); +} + +static void mcba_usb_process_can(struct mcba_priv *priv, + struct mcba_usb_msg_can *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &priv->netdev->stats; + u16 sid; + + skb = alloc_can_skb(priv->netdev, &cf); + if (!skb) + return; + + sid = get_unaligned_be16(&msg->sid); + + if (sid & MCBA_SIDL_EXID_MASK) { + /* SIDH | SIDL | EIDH | EIDL + * 28 - 21 | 20 19 18 x x x 17 16 | 15 - 8 | 7 - 0 + */ + cf->can_id = CAN_EFF_FLAG; + + /* store 28-18 bits */ + cf->can_id |= (sid & 0xffe0) << 13; + /* store 17-16 bits */ + cf->can_id |= (sid & 3) << 16; + /* store 15-0 bits */ + cf->can_id |= get_unaligned_be16(&msg->eid); + } else { + /* SIDH | SIDL + * 10 - 3 | 2 1 0 x x x x x + */ + cf->can_id = (sid & 0xffe0) >> 5; + } + + if (msg->dlc & MCBA_DLC_RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + + cf->can_dlc = get_can_dlc(msg->dlc & MCBA_DLC_MASK); + + memcpy(cf->data, msg->data, cf->can_dlc); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + can_led_event(priv->netdev, CAN_LED_EVENT_RX); + netif_rx(skb); +} + +static void mcba_usb_process_ka_usb(struct mcba_priv *priv, + struct mcba_usb_msg_ka_usb *msg) +{ + if (unlikely(priv->usb_ka_first_pass)) { + netdev_info(priv->netdev, "PIC USB version %hhu.%hhu\n", + msg->soft_ver_major, msg->soft_ver_minor); + + priv->usb_ka_first_pass = false; + } + + if (msg->termination_state) + priv->can.termination = MCBA_TERMINATION_ENABLED; + else + priv->can.termination = MCBA_TERMINATION_DISABLED; +} + +static u32 convert_can2host_bitrate(struct mcba_usb_msg_ka_can *msg) +{ + const u32 bitrate = get_unaligned_be16(&msg->can_bitrate); + + if ((bitrate == 33) || (bitrate == 83)) + return bitrate * 1000 + 333; + else + return bitrate * 1000; +} + +static void mcba_usb_process_ka_can(struct mcba_priv *priv, + struct mcba_usb_msg_ka_can *msg) +{ + if (unlikely(priv->can_ka_first_pass)) { + netdev_info(priv->netdev, "PIC CAN version %hhu.%hhu\n", + msg->soft_ver_major, msg->soft_ver_minor); + + priv->can_ka_first_pass = false; + } + + if (unlikely(priv->can_speed_check)) { + const u32 bitrate = convert_can2host_bitrate(msg); + + priv->can_speed_check = false; + + if (bitrate != priv->can.bittiming.bitrate) + netdev_err( + priv->netdev, + "Wrong bitrate reported by the device (%u). Expected %u", + bitrate, priv->can.bittiming.bitrate); + } + + priv->bec.txerr = msg->tx_err_cnt; + priv->bec.rxerr = msg->rx_err_cnt; + + if (msg->tx_bus_off) + priv->can.state = CAN_STATE_BUS_OFF; + + else if ((priv->bec.txerr > MCBA_CAN_STATE_ERR_PSV_TH) || + (priv->bec.rxerr > MCBA_CAN_STATE_ERR_PSV_TH)) + priv->can.state = CAN_STATE_ERROR_PASSIVE; + + else if ((priv->bec.txerr > MCBA_CAN_STATE_WRN_TH) || + (priv->bec.rxerr > MCBA_CAN_STATE_WRN_TH)) + priv->can.state = CAN_STATE_ERROR_WARNING; +} + +static void mcba_usb_process_rx(struct mcba_priv *priv, + struct mcba_usb_msg *msg) +{ + switch (msg->cmd_id) { + case MBCA_CMD_I_AM_ALIVE_FROM_CAN: + mcba_usb_process_ka_can(priv, + (struct mcba_usb_msg_ka_can *)msg); + break; + + case MBCA_CMD_I_AM_ALIVE_FROM_USB: + mcba_usb_process_ka_usb(priv, + (struct mcba_usb_msg_ka_usb *)msg); + break; + + case MBCA_CMD_RECEIVE_MESSAGE: + mcba_usb_process_can(priv, (struct mcba_usb_msg_can *)msg); + break; + + case MBCA_CMD_NOTHING_TO_SEND: + /* Side effect of communication between PIC_USB and PIC_CAN. + * PIC_CAN is telling us that it has nothing to send + */ + break; + + case MBCA_CMD_TRANSMIT_MESSAGE_RSP: + /* Transmission response from the device containing timestamp */ + break; + + default: + netdev_warn(priv->netdev, "Unsupported msg (0x%hhX)", + msg->cmd_id); + break; + } +} + +/* Callback for reading data from device + * + * Check urb status, call read function and resubmit urb read operation. + */ +static void mcba_usb_read_bulk_callback(struct urb *urb) +{ + struct mcba_priv *priv = urb->context; + struct net_device *netdev; + int retval; + int pos = 0; + + netdev = priv->netdev; + + if (!netif_device_present(netdev)) + return; + + switch (urb->status) { + case 0: /* success */ + break; + + case -ENOENT: + case -ESHUTDOWN: + return; + + default: + netdev_info(netdev, "Rx URB aborted (%d)\n", urb->status); + + goto resubmit_urb; + } + + while (pos < urb->actual_length) { + struct mcba_usb_msg *msg; + + if (pos + sizeof(struct mcba_usb_msg) > urb->actual_length) { + netdev_err(priv->netdev, "format error\n"); + break; + } + + msg = (struct mcba_usb_msg *)(urb->transfer_buffer + pos); + mcba_usb_process_rx(priv, msg); + + pos += sizeof(struct mcba_usb_msg); + } + +resubmit_urb: + + usb_fill_bulk_urb(urb, priv->udev, + usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_OUT), + urb->transfer_buffer, MCBA_USB_RX_BUFF_SIZE, + mcba_usb_read_bulk_callback, priv); + + retval = usb_submit_urb(urb, GFP_ATOMIC); + + if (retval == -ENODEV) + netif_device_detach(netdev); + else if (retval) + netdev_err(netdev, "failed resubmitting read bulk urb: %d\n", + retval); +} + +/* Start USB device */ +static int mcba_usb_start(struct mcba_priv *priv) +{ + struct net_device *netdev = priv->netdev; + int err, i; + + mcba_init_ctx(priv); + + for (i = 0; i < MCBA_MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf; + + /* create a URB, and a buffer for it */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE, + GFP_KERNEL, &urb->transfer_dma); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + usb_fill_bulk_urb(urb, priv->udev, + usb_rcvbulkpipe(priv->udev, MCBA_USB_EP_IN), + buf, MCBA_USB_RX_BUFF_SIZE, + mcba_usb_read_bulk_callback, priv); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &priv->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(priv->udev, MCBA_USB_RX_BUFF_SIZE, + buf, urb->transfer_dma); + usb_free_urb(urb); + break; + } + + /* Drop reference, USB core will take care of freeing it */ + usb_free_urb(urb); + } + + /* Did we submit any URBs */ + if (i == 0) { + netdev_warn(netdev, "couldn't setup read URBs\n"); + return err; + } + + /* Warn if we've couldn't transmit all the URBs */ + if (i < MCBA_MAX_RX_URBS) + netdev_warn(netdev, "rx performance may be slow\n"); + + mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_USB); + mcba_usb_xmit_read_fw_ver(priv, MCBA_VER_REQ_CAN); + + return err; +} + +/* Open USB device */ +static int mcba_usb_open(struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + int err; + + /* common open */ + err = open_candev(netdev); + if (err) + return err; + + priv->can_speed_check = true; + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + can_led_event(netdev, CAN_LED_EVENT_OPEN); + netif_start_queue(netdev); + + return 0; +} + +static void mcba_urb_unlink(struct mcba_priv *priv) +{ + usb_kill_anchored_urbs(&priv->rx_submitted); + usb_kill_anchored_urbs(&priv->tx_submitted); +} + +/* Close USB device */ +static int mcba_usb_close(struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + + priv->can.state = CAN_STATE_STOPPED; + + netif_stop_queue(netdev); + + /* Stop polling */ + mcba_urb_unlink(priv); + + close_candev(netdev); + can_led_event(netdev, CAN_LED_EVENT_STOP); + + return 0; +} + +/* Set network device mode + * + * Maybe we should leave this function empty, because the device + * set mode variable with open command. + */ +static int mcba_net_set_mode(struct net_device *netdev, enum can_mode mode) +{ + return 0; +} + +static int mcba_net_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct mcba_priv *priv = netdev_priv(netdev); + + bec->txerr = priv->bec.txerr; + bec->rxerr = priv->bec.rxerr; + + return 0; +} + +static const struct net_device_ops mcba_netdev_ops = { + .ndo_open = mcba_usb_open, + .ndo_stop = mcba_usb_close, + .ndo_start_xmit = mcba_usb_start_xmit, +}; + +/* Microchip CANBUS has hardcoded bittiming values by default. + * This function sends request via USB to change the speed and align bittiming + * values for presentation purposes only + */ +static int mcba_net_set_bittiming(struct net_device *netdev) +{ + struct mcba_priv *priv = netdev_priv(netdev); + const u16 bitrate_kbps = priv->can.bittiming.bitrate / 1000; + + mcba_usb_xmit_change_bitrate(priv, bitrate_kbps); + + return 0; +} + +static int mcba_set_termination(struct net_device *netdev, u16 term) +{ + struct mcba_priv *priv = netdev_priv(netdev); + struct mcba_usb_msg_termination usb_msg = { + .cmd_id = MBCA_CMD_SETUP_TERMINATION_RESISTANCE + }; + + if (term == MCBA_TERMINATION_ENABLED) + usb_msg.termination = 1; + else + usb_msg.termination = 0; + + mcba_usb_xmit_cmd(priv, (struct mcba_usb_msg *)&usb_msg); + + return 0; +} + +static int mcba_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *netdev; + struct mcba_priv *priv; + int err = -ENOMEM; + struct usb_device *usbdev = interface_to_usbdev(intf); + + netdev = alloc_candev(sizeof(struct mcba_priv), MCBA_MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Couldn't alloc candev\n"); + return -ENOMEM; + } + + priv = netdev_priv(netdev); + + priv->udev = usbdev; + priv->netdev = netdev; + priv->usb_ka_first_pass = true; + priv->can_ka_first_pass = true; + priv->can_speed_check = false; + + init_usb_anchor(&priv->rx_submitted); + init_usb_anchor(&priv->tx_submitted); + + usb_set_intfdata(intf, priv); + + /* Init CAN device */ + priv->can.state = CAN_STATE_STOPPED; + priv->can.termination_const = mcba_termination; + priv->can.termination_const_cnt = ARRAY_SIZE(mcba_termination); + priv->can.bitrate_const = mcba_bitrate; + priv->can.bitrate_const_cnt = ARRAY_SIZE(mcba_bitrate); + + priv->can.do_set_termination = mcba_set_termination; + priv->can.do_set_mode = mcba_net_set_mode; + priv->can.do_get_berr_counter = mcba_net_get_berr_counter; + priv->can.do_set_bittiming = mcba_net_set_bittiming; + + netdev->netdev_ops = &mcba_netdev_ops; + + netdev->flags |= IFF_ECHO; /* we support local echo */ + + SET_NETDEV_DEV(netdev, &intf->dev); + + err = register_candev(netdev); + if (err) { + netdev_err(netdev, "couldn't register CAN device: %d\n", err); + + goto cleanup_free_candev; + } + + devm_can_led_init(netdev); + + /* Start USB dev only if we have successfully registered CAN device */ + err = mcba_usb_start(priv); + if (err) { + if (err == -ENODEV) + netif_device_detach(priv->netdev); + + netdev_warn(netdev, "couldn't start device: %d\n", err); + + goto cleanup_unregister_candev; + } + + dev_info(&intf->dev, "Microchip CAN BUS analizer connected\n"); + + return 0; + +cleanup_unregister_candev: + unregister_candev(priv->netdev); + +cleanup_free_candev: + free_candev(netdev); + + return err; +} + +/* Called by the usb core when driver is unloaded or device is removed */ +static void mcba_usb_disconnect(struct usb_interface *intf) +{ + struct mcba_priv *priv = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + netdev_info(priv->netdev, "device disconnected\n"); + + unregister_candev(priv->netdev); + free_candev(priv->netdev); + + mcba_urb_unlink(priv); +} + +static struct usb_driver mcba_usb_driver = { + .name = MCBA_MODULE_NAME, + .probe = mcba_usb_probe, + .disconnect = mcba_usb_disconnect, + .id_table = mcba_usb_table, +}; + +module_usb_driver(mcba_usb_driver); + +MODULE_AUTHOR("Remigiusz Kołłątaj "); +MODULE_DESCRIPTION("SocketCAN driver for Microchip CAN BUS Analyzer Tool"); +MODULE_LICENSE("GPL v2"); -- cgit From a7bbd28f04ce4df26f07e96c4a743d529da99348 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 25 Apr 2017 08:19:38 +0200 Subject: can: fix memory leak in initial namespace support The can_rx_alldev_list is a per-net data structure now and allocated in can_pernet_init(). Make sure the memory is free'd in can_pernet_exit() too. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- net/can/af_can.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/can/af_can.c b/net/can/af_can.c index abf7d854a94d..2c935babe466 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -903,6 +903,8 @@ static void can_pernet_exit(struct net *net) } } rcu_read_unlock(); + + kfree(net->can.can_rx_alldev_list); } /* -- cgit From 48452c169d5ddfdbff4af556016dd9e1f47d762b Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 25 Apr 2017 08:19:39 +0200 Subject: can: remove obsolete pernet_operations definitions The namespace support for the CAN subsystem does not need any additional memory. So when ".size = 0" there's no extra memory allocated by the system. And therefore ".id" is obsolete too. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- net/can/af_can.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/can/af_can.c b/net/can/af_can.c index 2c935babe466..421b60fc42c3 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -75,8 +75,6 @@ static int stats_timer __read_mostly = 1; module_param(stats_timer, int, S_IRUGO); MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)"); -static int can_net_id; - static struct kmem_cache *rcv_cache __read_mostly; /* table of registered CAN protocols */ @@ -935,8 +933,6 @@ static struct notifier_block can_netdev_notifier __read_mostly = { static struct pernet_operations can_pernet_ops __read_mostly = { .init = can_pernet_init, .exit = can_pernet_exit, - .id = &can_net_id, - .size = 0, }; static __init int can_init(void) -- cgit From f2e72f43e7686720ddc5112fab2f5f71f47dc5e6 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 25 Apr 2017 08:19:40 +0200 Subject: can: remove obsolete definitions can_rx_alldev_list is a per-net data structure now. Remove it's definition here and can_rx_dev_list too. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- net/can/af_can.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/can/af_can.h b/net/can/af_can.h index f273c9d9b129..84a35e97c5e0 100644 --- a/net/can/af_can.h +++ b/net/can/af_can.h @@ -110,9 +110,6 @@ struct s_pstats { unsigned long rcv_entries_max; }; -/* receive filters subscribed for 'all' CAN devices */ -extern struct dev_rcv_lists can_rx_alldev_list; - /* function prototypes for the CAN networklayer procfs (proc.c) */ void can_init_proc(struct net *net); void can_remove_proc(struct net *net); @@ -122,6 +119,5 @@ void can_stat_update(unsigned long data); extern struct timer_list can_stattimer; /* timer for statistics update */ extern struct s_stats can_stats; /* packet statistics */ extern struct s_pstats can_pstats; /* receive list statistics */ -extern struct hlist_head can_rx_dev_list; /* rx dispatcher structures */ #endif /* AF_CAN_H */ -- cgit From cb5635a3677679666e4e81ecbb209d32f13dedcd Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 25 Apr 2017 08:19:41 +0200 Subject: can: complete initial namespace support The statistics and its proc output was not implemented as per-net in the initial network namespace support by Mario Kicherer (8e8cda6d737d). This patch adds the missing per-net statistics for the CAN subsystem. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/linux/can/core.h | 4 +- include/net/netns/can.h | 5 ++ net/can/af_can.c | 71 +++++++++++++----------- net/can/af_can.h | 5 -- net/can/proc.c | 141 +++++++++++++++++++++++++---------------------- 5 files changed, 121 insertions(+), 105 deletions(-) diff --git a/include/linux/can/core.h b/include/linux/can/core.h index 319a0da827b8..c9a17bb1221c 100644 --- a/include/linux/can/core.h +++ b/include/linux/can/core.h @@ -5,7 +5,7 @@ * * Authors: Oliver Hartkopp * Urs Thuermann - * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * Copyright (c) 2002-2017 Volkswagen Group Electronic Research * All rights reserved. * */ @@ -17,7 +17,7 @@ #include #include -#define CAN_VERSION "20120528" +#define CAN_VERSION "20170425" /* increment this number each time you change some user-space interface */ #define CAN_ABI_VERSION "9" diff --git a/include/net/netns/can.h b/include/net/netns/can.h index e8beba772f1a..574157dbc43a 100644 --- a/include/net/netns/can.h +++ b/include/net/netns/can.h @@ -8,6 +8,8 @@ #include struct dev_rcv_lists; +struct s_stats; +struct s_pstats; struct netns_can { #if IS_ENABLED(CONFIG_PROC_FS) @@ -26,6 +28,9 @@ struct netns_can { /* receive filters subscribed for 'all' CAN devices */ struct dev_rcv_lists *can_rx_alldev_list; spinlock_t can_rcvlists_lock; + struct timer_list can_stattimer;/* timer for statistics update */ + struct s_stats *can_stats; /* packet statistics */ + struct s_pstats *can_pstats; /* receive list statistics */ }; #endif /* __NETNS_CAN_H__ */ diff --git a/net/can/af_can.c b/net/can/af_can.c index 421b60fc42c3..b6406fe33c76 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -2,7 +2,7 @@ * af_can.c - Protocol family CAN core module * (used by different CAN protocol modules) * - * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * Copyright (c) 2002-2017 Volkswagen Group Electronic Research * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -81,10 +81,6 @@ static struct kmem_cache *rcv_cache __read_mostly; static const struct can_proto *proto_tab[CAN_NPROTO] __read_mostly; static DEFINE_MUTEX(proto_tab_lock); -struct timer_list can_stattimer; /* timer for statistics update */ -struct s_stats can_stats; /* packet statistics */ -struct s_pstats can_pstats; /* receive list statistics */ - static atomic_t skbcounter = ATOMIC_INIT(0); /* @@ -221,6 +217,7 @@ int can_send(struct sk_buff *skb, int loop) { struct sk_buff *newskb = NULL; struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + struct s_stats *can_stats = dev_net(skb->dev)->can.can_stats; int err = -EINVAL; if (skb->len == CAN_MTU) { @@ -309,8 +306,8 @@ int can_send(struct sk_buff *skb, int loop) netif_rx_ni(newskb); /* update statistics */ - can_stats.tx_frames++; - can_stats.tx_frames_delta++; + can_stats->tx_frames++; + can_stats->tx_frames_delta++; return 0; @@ -468,6 +465,7 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id, struct receiver *r; struct hlist_head *rl; struct dev_rcv_lists *d; + struct s_pstats *can_pstats = net->can.can_pstats; int err = 0; /* insert new receiver (dev,canid,mask) -> (func,data) */ @@ -499,9 +497,9 @@ int can_rx_register(struct net *net, struct net_device *dev, canid_t can_id, hlist_add_head_rcu(&r->list, rl); d->entries++; - can_pstats.rcv_entries++; - if (can_pstats.rcv_entries_max < can_pstats.rcv_entries) - can_pstats.rcv_entries_max = can_pstats.rcv_entries; + can_pstats->rcv_entries++; + if (can_pstats->rcv_entries_max < can_pstats->rcv_entries) + can_pstats->rcv_entries_max = can_pstats->rcv_entries; } else { kmem_cache_free(rcv_cache, r); err = -ENODEV; @@ -543,6 +541,7 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id, { struct receiver *r = NULL; struct hlist_head *rl; + struct s_pstats *can_pstats = net->can.can_pstats; struct dev_rcv_lists *d; if (dev && dev->type != ARPHRD_CAN) @@ -589,8 +588,8 @@ void can_rx_unregister(struct net *net, struct net_device *dev, canid_t can_id, hlist_del_rcu(&r->list); d->entries--; - if (can_pstats.rcv_entries > 0) - can_pstats.rcv_entries--; + if (can_pstats->rcv_entries > 0) + can_pstats->rcv_entries--; /* remove device structure requested by NETDEV_UNREGISTER */ if (d->remove_on_zero_entries && !d->entries) { @@ -684,11 +683,13 @@ static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb) static void can_receive(struct sk_buff *skb, struct net_device *dev) { struct dev_rcv_lists *d; + struct net *net = dev_net(dev); + struct s_stats *can_stats = net->can.can_stats; int matches; /* update statistics */ - can_stats.rx_frames++; - can_stats.rx_frames_delta++; + can_stats->rx_frames++; + can_stats->rx_frames_delta++; /* create non-zero unique skb identifier together with *skb */ while (!(can_skb_prv(skb)->skbcnt)) @@ -697,10 +698,10 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev) rcu_read_lock(); /* deliver the packet to sockets listening on all devices */ - matches = can_rcv_filter(dev_net(dev)->can.can_rx_alldev_list, skb); + matches = can_rcv_filter(net->can.can_rx_alldev_list, skb); /* find receive list for this device */ - d = find_dev_rcv_lists(dev_net(dev), dev); + d = find_dev_rcv_lists(net, dev); if (d) matches += can_rcv_filter(d, skb); @@ -710,8 +711,8 @@ static void can_receive(struct sk_buff *skb, struct net_device *dev) consume_skb(skb); if (matches > 0) { - can_stats.matches++; - can_stats.matches_delta++; + can_stats->matches++; + can_stats->matches_delta++; } } @@ -876,8 +877,20 @@ static int can_pernet_init(struct net *net) net->can.can_rx_alldev_list = kzalloc(sizeof(struct dev_rcv_lists), GFP_KERNEL); - if (IS_ENABLED(CONFIG_PROC_FS)) + net->can.can_stats = kzalloc(sizeof(struct s_stats), GFP_KERNEL); + net->can.can_pstats = kzalloc(sizeof(struct s_pstats), GFP_KERNEL); + + if (IS_ENABLED(CONFIG_PROC_FS)) { + /* the statistics are updated every second (timer triggered) */ + if (stats_timer) { + setup_timer(&net->can.can_stattimer, can_stat_update, + (unsigned long)net); + mod_timer(&net->can.can_stattimer, + round_jiffies(jiffies + HZ)); + } + net->can.can_stats->jiffies_init = jiffies; can_init_proc(net); + } return 0; } @@ -886,8 +899,11 @@ static void can_pernet_exit(struct net *net) { struct net_device *dev; - if (IS_ENABLED(CONFIG_PROC_FS)) + if (IS_ENABLED(CONFIG_PROC_FS)) { can_remove_proc(net); + if (stats_timer) + del_timer_sync(&net->can.can_stattimer); + } /* remove created dev_rcv_lists from still registered CAN devices */ rcu_read_lock(); @@ -903,6 +919,8 @@ static void can_pernet_exit(struct net *net) rcu_read_unlock(); kfree(net->can.can_rx_alldev_list); + kfree(net->can.can_stats); + kfree(net->can.can_pstats); } /* @@ -950,14 +968,6 @@ static __init int can_init(void) if (!rcv_cache) return -ENOMEM; - if (IS_ENABLED(CONFIG_PROC_FS)) { - if (stats_timer) { - /* the statistics are updated every second (timer triggered) */ - setup_timer(&can_stattimer, can_stat_update, 0); - mod_timer(&can_stattimer, round_jiffies(jiffies + HZ)); - } - } - register_pernet_subsys(&can_pernet_ops); /* protocol register */ @@ -971,11 +981,6 @@ static __init int can_init(void) static __exit void can_exit(void) { - if (IS_ENABLED(CONFIG_PROC_FS)) { - if (stats_timer) - del_timer_sync(&can_stattimer); - } - /* protocol unregister */ dev_remove_pack(&canfd_packet); dev_remove_pack(&can_packet); diff --git a/net/can/af_can.h b/net/can/af_can.h index 84a35e97c5e0..d0ef45bb2a72 100644 --- a/net/can/af_can.h +++ b/net/can/af_can.h @@ -115,9 +115,4 @@ void can_init_proc(struct net *net); void can_remove_proc(struct net *net); void can_stat_update(unsigned long data); -/* structures and variables from af_can.c needed in proc.c for reading */ -extern struct timer_list can_stattimer; /* timer for statistics update */ -extern struct s_stats can_stats; /* packet statistics */ -extern struct s_pstats can_pstats; /* receive list statistics */ - #endif /* AF_CAN_H */ diff --git a/net/can/proc.c b/net/can/proc.c index 9a8d54d57b22..83045f00c63c 100644 --- a/net/can/proc.c +++ b/net/can/proc.c @@ -75,21 +75,23 @@ static const char rx_list_name[][8] = { * af_can statistics stuff */ -static void can_init_stats(void) +static void can_init_stats(struct net *net) { + struct s_stats *can_stats = net->can.can_stats; + struct s_pstats *can_pstats = net->can.can_pstats; /* * This memset function is called from a timer context (when * can_stattimer is active which is the default) OR in a process * context (reading the proc_fs when can_stattimer is disabled). */ - memset(&can_stats, 0, sizeof(can_stats)); - can_stats.jiffies_init = jiffies; + memset(can_stats, 0, sizeof(struct s_stats)); + can_stats->jiffies_init = jiffies; - can_pstats.stats_reset++; + can_pstats->stats_reset++; if (user_reset) { user_reset = 0; - can_pstats.user_reset++; + can_pstats->user_reset++; } } @@ -115,64 +117,66 @@ static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif, void can_stat_update(unsigned long data) { + struct net *net = (struct net *)data; + struct s_stats *can_stats = net->can.can_stats; unsigned long j = jiffies; /* snapshot */ /* restart counting in timer context on user request */ if (user_reset) - can_init_stats(); + can_init_stats(net); /* restart counting on jiffies overflow */ - if (j < can_stats.jiffies_init) - can_init_stats(); + if (j < can_stats->jiffies_init) + can_init_stats(net); /* prevent overflow in calc_rate() */ - if (can_stats.rx_frames > (ULONG_MAX / HZ)) - can_init_stats(); + if (can_stats->rx_frames > (ULONG_MAX / HZ)) + can_init_stats(net); /* prevent overflow in calc_rate() */ - if (can_stats.tx_frames > (ULONG_MAX / HZ)) - can_init_stats(); + if (can_stats->tx_frames > (ULONG_MAX / HZ)) + can_init_stats(net); /* matches overflow - very improbable */ - if (can_stats.matches > (ULONG_MAX / 100)) - can_init_stats(); + if (can_stats->matches > (ULONG_MAX / 100)) + can_init_stats(net); /* calc total values */ - if (can_stats.rx_frames) - can_stats.total_rx_match_ratio = (can_stats.matches * 100) / - can_stats.rx_frames; + if (can_stats->rx_frames) + can_stats->total_rx_match_ratio = (can_stats->matches * 100) / + can_stats->rx_frames; - can_stats.total_tx_rate = calc_rate(can_stats.jiffies_init, j, - can_stats.tx_frames); - can_stats.total_rx_rate = calc_rate(can_stats.jiffies_init, j, - can_stats.rx_frames); + can_stats->total_tx_rate = calc_rate(can_stats->jiffies_init, j, + can_stats->tx_frames); + can_stats->total_rx_rate = calc_rate(can_stats->jiffies_init, j, + can_stats->rx_frames); /* calc current values */ - if (can_stats.rx_frames_delta) - can_stats.current_rx_match_ratio = - (can_stats.matches_delta * 100) / - can_stats.rx_frames_delta; + if (can_stats->rx_frames_delta) + can_stats->current_rx_match_ratio = + (can_stats->matches_delta * 100) / + can_stats->rx_frames_delta; - can_stats.current_tx_rate = calc_rate(0, HZ, can_stats.tx_frames_delta); - can_stats.current_rx_rate = calc_rate(0, HZ, can_stats.rx_frames_delta); + can_stats->current_tx_rate = calc_rate(0, HZ, can_stats->tx_frames_delta); + can_stats->current_rx_rate = calc_rate(0, HZ, can_stats->rx_frames_delta); /* check / update maximum values */ - if (can_stats.max_tx_rate < can_stats.current_tx_rate) - can_stats.max_tx_rate = can_stats.current_tx_rate; + if (can_stats->max_tx_rate < can_stats->current_tx_rate) + can_stats->max_tx_rate = can_stats->current_tx_rate; - if (can_stats.max_rx_rate < can_stats.current_rx_rate) - can_stats.max_rx_rate = can_stats.current_rx_rate; + if (can_stats->max_rx_rate < can_stats->current_rx_rate) + can_stats->max_rx_rate = can_stats->current_rx_rate; - if (can_stats.max_rx_match_ratio < can_stats.current_rx_match_ratio) - can_stats.max_rx_match_ratio = can_stats.current_rx_match_ratio; + if (can_stats->max_rx_match_ratio < can_stats->current_rx_match_ratio) + can_stats->max_rx_match_ratio = can_stats->current_rx_match_ratio; /* clear values for 'current rate' calculation */ - can_stats.tx_frames_delta = 0; - can_stats.rx_frames_delta = 0; - can_stats.matches_delta = 0; + can_stats->tx_frames_delta = 0; + can_stats->rx_frames_delta = 0; + can_stats->matches_delta = 0; /* restart timer (one second) */ - mod_timer(&can_stattimer, round_jiffies(jiffies + HZ)); + mod_timer(&net->can.can_stattimer, round_jiffies(jiffies + HZ)); } /* @@ -206,57 +210,61 @@ static void can_print_recv_banner(struct seq_file *m) static int can_stats_proc_show(struct seq_file *m, void *v) { + struct net *net = m->private; + struct s_stats *can_stats = net->can.can_stats; + struct s_pstats *can_pstats = net->can.can_pstats; + seq_putc(m, '\n'); - seq_printf(m, " %8ld transmitted frames (TXF)\n", can_stats.tx_frames); - seq_printf(m, " %8ld received frames (RXF)\n", can_stats.rx_frames); - seq_printf(m, " %8ld matched frames (RXMF)\n", can_stats.matches); + seq_printf(m, " %8ld transmitted frames (TXF)\n", can_stats->tx_frames); + seq_printf(m, " %8ld received frames (RXF)\n", can_stats->rx_frames); + seq_printf(m, " %8ld matched frames (RXMF)\n", can_stats->matches); seq_putc(m, '\n'); - if (can_stattimer.function == can_stat_update) { + if (net->can.can_stattimer.function == can_stat_update) { seq_printf(m, " %8ld %% total match ratio (RXMR)\n", - can_stats.total_rx_match_ratio); + can_stats->total_rx_match_ratio); seq_printf(m, " %8ld frames/s total tx rate (TXR)\n", - can_stats.total_tx_rate); + can_stats->total_tx_rate); seq_printf(m, " %8ld frames/s total rx rate (RXR)\n", - can_stats.total_rx_rate); + can_stats->total_rx_rate); seq_putc(m, '\n'); seq_printf(m, " %8ld %% current match ratio (CRXMR)\n", - can_stats.current_rx_match_ratio); + can_stats->current_rx_match_ratio); seq_printf(m, " %8ld frames/s current tx rate (CTXR)\n", - can_stats.current_tx_rate); + can_stats->current_tx_rate); seq_printf(m, " %8ld frames/s current rx rate (CRXR)\n", - can_stats.current_rx_rate); + can_stats->current_rx_rate); seq_putc(m, '\n'); seq_printf(m, " %8ld %% max match ratio (MRXMR)\n", - can_stats.max_rx_match_ratio); + can_stats->max_rx_match_ratio); seq_printf(m, " %8ld frames/s max tx rate (MTXR)\n", - can_stats.max_tx_rate); + can_stats->max_tx_rate); seq_printf(m, " %8ld frames/s max rx rate (MRXR)\n", - can_stats.max_rx_rate); + can_stats->max_rx_rate); seq_putc(m, '\n'); } seq_printf(m, " %8ld current receive list entries (CRCV)\n", - can_pstats.rcv_entries); + can_pstats->rcv_entries); seq_printf(m, " %8ld maximum receive list entries (MRCV)\n", - can_pstats.rcv_entries_max); + can_pstats->rcv_entries_max); - if (can_pstats.stats_reset) + if (can_pstats->stats_reset) seq_printf(m, "\n %8ld statistic resets (STR)\n", - can_pstats.stats_reset); + can_pstats->stats_reset); - if (can_pstats.user_reset) + if (can_pstats->user_reset) seq_printf(m, " %8ld user statistic resets (USTR)\n", - can_pstats.user_reset); + can_pstats->user_reset); seq_putc(m, '\n'); return 0; @@ -264,7 +272,7 @@ static int can_stats_proc_show(struct seq_file *m, void *v) static int can_stats_proc_open(struct inode *inode, struct file *file) { - return single_open(file, can_stats_proc_show, NULL); + return single_open_net(inode, file, can_stats_proc_show); } static const struct file_operations can_stats_proc_fops = { @@ -277,25 +285,28 @@ static const struct file_operations can_stats_proc_fops = { static int can_reset_stats_proc_show(struct seq_file *m, void *v) { + struct net *net = m->private; + struct s_pstats *can_pstats = net->can.can_pstats; + struct s_stats *can_stats = net->can.can_stats; + user_reset = 1; - if (can_stattimer.function == can_stat_update) { + if (net->can.can_stattimer.function == can_stat_update) { seq_printf(m, "Scheduled statistic reset #%ld.\n", - can_pstats.stats_reset + 1); - + can_pstats->stats_reset + 1); } else { - if (can_stats.jiffies_init != jiffies) - can_init_stats(); + if (can_stats->jiffies_init != jiffies) + can_init_stats(net); seq_printf(m, "Performed statistic reset #%ld.\n", - can_pstats.stats_reset); + can_pstats->stats_reset); } return 0; } static int can_reset_stats_proc_open(struct inode *inode, struct file *file) { - return single_open(file, can_reset_stats_proc_show, NULL); + return single_open_net(inode, file, can_reset_stats_proc_show); } static const struct file_operations can_reset_stats_proc_fops = { @@ -314,7 +325,7 @@ static int can_version_proc_show(struct seq_file *m, void *v) static int can_version_proc_open(struct inode *inode, struct file *file) { - return single_open(file, can_version_proc_show, NULL); + return single_open_net(inode, file, can_version_proc_show); } static const struct file_operations can_version_proc_fops = { -- cgit From 384317ef4187f59a1cb7a6163444d757340b3bb4 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 25 Apr 2017 08:19:42 +0200 Subject: can: network namespace support for CAN_BCM protocol The CAN_BCM protocol and its procfs entries were not implemented as per-net in the initial network namespace support by Mario Kicherer (8e8cda6d737d). This patch adds the missing per-net functionality for the CAN BCM. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/net/netns/can.h | 1 + net/can/bcm.c | 90 +++++++++++++++++++++++++++++++------------------ 2 files changed, 58 insertions(+), 33 deletions(-) diff --git a/include/net/netns/can.h b/include/net/netns/can.h index 574157dbc43a..0f3c31aab8a8 100644 --- a/include/net/netns/can.h +++ b/include/net/netns/can.h @@ -23,6 +23,7 @@ struct netns_can { struct proc_dir_entry *pde_rcvlist_sff; struct proc_dir_entry *pde_rcvlist_eff; struct proc_dir_entry *pde_rcvlist_err; + struct proc_dir_entry *bcmproc_dir; #endif /* receive filters subscribed for 'all' CAN devices */ diff --git a/net/can/bcm.c b/net/can/bcm.c index 1976629a8463..0e855917b7e1 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1,7 +1,7 @@ /* * bcm.c - Broadcast Manager to filter/send (cyclic) CAN content * - * Copyright (c) 2002-2016 Volkswagen Group Electronic Research + * Copyright (c) 2002-2017 Volkswagen Group Electronic Research * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -77,7 +77,7 @@ (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG) : \ (CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG)) -#define CAN_BCM_VERSION "20161123" +#define CAN_BCM_VERSION "20170425" MODULE_DESCRIPTION("PF_CAN broadcast manager protocol"); MODULE_LICENSE("Dual BSD/GPL"); @@ -118,8 +118,6 @@ struct bcm_op { struct net_device *rx_reg_dev; }; -static struct proc_dir_entry *proc_dir; - struct bcm_sock { struct sock sk; int bound; @@ -149,7 +147,7 @@ static inline ktime_t bcm_timeval_to_ktime(struct bcm_timeval tv) /* * procfs functions */ -static char *bcm_proc_getifname(char *result, int ifindex) +static char *bcm_proc_getifname(struct net *net, char *result, int ifindex) { struct net_device *dev; @@ -157,7 +155,7 @@ static char *bcm_proc_getifname(char *result, int ifindex) return "any"; rcu_read_lock(); - dev = dev_get_by_index_rcu(&init_net, ifindex); + dev = dev_get_by_index_rcu(net, ifindex); if (dev) strcpy(result, dev->name); else @@ -170,7 +168,8 @@ static char *bcm_proc_getifname(char *result, int ifindex) static int bcm_proc_show(struct seq_file *m, void *v) { char ifname[IFNAMSIZ]; - struct sock *sk = (struct sock *)m->private; + struct net *net = m->private; + struct sock *sk = (struct sock *)PDE_DATA(m->file->f_inode); struct bcm_sock *bo = bcm_sk(sk); struct bcm_op *op; @@ -178,7 +177,7 @@ static int bcm_proc_show(struct seq_file *m, void *v) seq_printf(m, " / sk %pK", sk); seq_printf(m, " / bo %pK", bo); seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs); - seq_printf(m, " / bound %s", bcm_proc_getifname(ifname, bo->ifindex)); + seq_printf(m, " / bound %s", bcm_proc_getifname(net, ifname, bo->ifindex)); seq_printf(m, " <<<\n"); list_for_each_entry(op, &bo->rx_ops, list) { @@ -190,7 +189,7 @@ static int bcm_proc_show(struct seq_file *m, void *v) continue; seq_printf(m, "rx_op: %03X %-5s ", op->can_id, - bcm_proc_getifname(ifname, op->ifindex)); + bcm_proc_getifname(net, ifname, op->ifindex)); if (op->flags & CAN_FD_FRAME) seq_printf(m, "(%u)", op->nframes); @@ -219,7 +218,7 @@ static int bcm_proc_show(struct seq_file *m, void *v) list_for_each_entry(op, &bo->tx_ops, list) { seq_printf(m, "tx_op: %03X %s ", op->can_id, - bcm_proc_getifname(ifname, op->ifindex)); + bcm_proc_getifname(net, ifname, op->ifindex)); if (op->flags & CAN_FD_FRAME) seq_printf(m, "(%u) ", op->nframes); @@ -242,7 +241,7 @@ static int bcm_proc_show(struct seq_file *m, void *v) static int bcm_proc_open(struct inode *inode, struct file *file) { - return single_open(file, bcm_proc_show, PDE_DATA(inode)); + return single_open_net(inode, file, bcm_proc_show); } static const struct file_operations bcm_proc_fops = { @@ -267,7 +266,7 @@ static void bcm_can_tx(struct bcm_op *op) if (!op->ifindex) return; - dev = dev_get_by_index(&init_net, op->ifindex); + dev = dev_get_by_index(sock_net(op->sk), op->ifindex); if (!dev) { /* RFC: should this bcm_op remove itself here? */ return; @@ -764,7 +763,7 @@ static void bcm_remove_op(struct bcm_op *op) static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op) { if (op->rx_reg_dev == dev) { - can_rx_unregister(&init_net, dev, op->can_id, + can_rx_unregister(dev_net(dev), dev, op->can_id, REGMASK(op->can_id), bcm_rx_handler, op); /* mark as removed subscription */ @@ -800,7 +799,7 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, if (op->rx_reg_dev) { struct net_device *dev; - dev = dev_get_by_index(&init_net, + dev = dev_get_by_index(sock_net(op->sk), op->ifindex); if (dev) { bcm_rx_unreg(dev, op); @@ -808,7 +807,8 @@ static int bcm_delete_rx_op(struct list_head *ops, struct bcm_msg_head *mh, } } } else - can_rx_unregister(&init_net, NULL, op->can_id, + can_rx_unregister(sock_net(op->sk), NULL, + op->can_id, REGMASK(op->can_id), bcm_rx_handler, op); @@ -1220,9 +1220,9 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, if (ifindex) { struct net_device *dev; - dev = dev_get_by_index(&init_net, ifindex); + dev = dev_get_by_index(sock_net(sk), ifindex); if (dev) { - err = can_rx_register(&init_net, dev, + err = can_rx_register(sock_net(sk), dev, op->can_id, REGMASK(op->can_id), bcm_rx_handler, op, @@ -1233,7 +1233,7 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, } } else - err = can_rx_register(&init_net, NULL, op->can_id, + err = can_rx_register(sock_net(sk), NULL, op->can_id, REGMASK(op->can_id), bcm_rx_handler, op, "bcm", sk); if (err) { @@ -1273,7 +1273,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk, return err; } - dev = dev_get_by_index(&init_net, ifindex); + dev = dev_get_by_index(sock_net(sk), ifindex); if (!dev) { kfree_skb(skb); return -ENODEV; @@ -1338,7 +1338,7 @@ static int bcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) if (ifindex) { struct net_device *dev; - dev = dev_get_by_index(&init_net, ifindex); + dev = dev_get_by_index(sock_net(sk), ifindex); if (!dev) return -ENODEV; @@ -1419,7 +1419,7 @@ static int bcm_notifier(struct notifier_block *nb, unsigned long msg, struct bcm_op *op; int notify_enodev = 0; - if (!net_eq(dev_net(dev), &init_net)) + if (!net_eq(dev_net(dev), sock_net(sk))) return NOTIFY_DONE; if (dev->type != ARPHRD_CAN) @@ -1491,6 +1491,7 @@ static int bcm_init(struct sock *sk) static int bcm_release(struct socket *sock) { struct sock *sk = sock->sk; + struct net *net = sock_net(sk); struct bcm_sock *bo; struct bcm_op *op, *next; @@ -1522,14 +1523,14 @@ static int bcm_release(struct socket *sock) if (op->rx_reg_dev) { struct net_device *dev; - dev = dev_get_by_index(&init_net, op->ifindex); + dev = dev_get_by_index(net, op->ifindex); if (dev) { bcm_rx_unreg(dev, op); dev_put(dev); } } } else - can_rx_unregister(&init_net, NULL, op->can_id, + can_rx_unregister(net, NULL, op->can_id, REGMASK(op->can_id), bcm_rx_handler, op); @@ -1537,8 +1538,8 @@ static int bcm_release(struct socket *sock) } /* remove procfs entry */ - if (proc_dir && bo->bcm_proc_read) - remove_proc_entry(bo->procname, proc_dir); + if (net->can.bcmproc_dir && bo->bcm_proc_read) + remove_proc_entry(bo->procname, net->can.bcmproc_dir); /* remove device reference */ if (bo->bound) { @@ -1561,6 +1562,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, struct sockaddr_can *addr = (struct sockaddr_can *)uaddr; struct sock *sk = sock->sk; struct bcm_sock *bo = bcm_sk(sk); + struct net *net = sock_net(sk); int ret = 0; if (len < sizeof(*addr)) @@ -1577,7 +1579,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, if (addr->can_ifindex) { struct net_device *dev; - dev = dev_get_by_index(&init_net, addr->can_ifindex); + dev = dev_get_by_index(net, addr->can_ifindex); if (!dev) { ret = -ENODEV; goto fail; @@ -1596,11 +1598,11 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, bo->ifindex = 0; } - if (proc_dir) { + if (net->can.bcmproc_dir) { /* unique socket address as filename */ sprintf(bo->procname, "%lu", sock_i_ino(sk)); bo->bcm_proc_read = proc_create_data(bo->procname, 0644, - proc_dir, + net->can.bcmproc_dir, &bcm_proc_fops, sk); if (!bo->bcm_proc_read) { ret = -ENOMEM; @@ -1687,6 +1689,31 @@ static const struct can_proto bcm_can_proto = { .prot = &bcm_proto, }; +static int canbcm_pernet_init(struct net *net) +{ + /* create /proc/net/can-bcm directory */ + if (IS_ENABLED(CONFIG_PROC_FS)) { + net->can.bcmproc_dir = + proc_net_mkdir(net, "can-bcm", net->proc_net); + } + + return 0; +} + +static void canbcm_pernet_exit(struct net *net) +{ + /* remove /proc/net/can-bcm directory */ + if (IS_ENABLED(CONFIG_PROC_FS)) { + if (net->can.bcmproc_dir) + remove_proc_entry("can-bcm", net->proc_net); + } +} + +static struct pernet_operations canbcm_pernet_ops __read_mostly = { + .init = canbcm_pernet_init, + .exit = canbcm_pernet_exit, +}; + static int __init bcm_module_init(void) { int err; @@ -1699,17 +1726,14 @@ static int __init bcm_module_init(void) return err; } - /* create /proc/net/can-bcm directory */ - proc_dir = proc_mkdir("can-bcm", init_net.proc_net); + register_pernet_subsys(&canbcm_pernet_ops); return 0; } static void __exit bcm_module_exit(void) { can_proto_unregister(&bcm_can_proto); - - if (proc_dir) - remove_proc_entry("can-bcm", init_net.proc_net); + unregister_pernet_subsys(&canbcm_pernet_ops); } module_init(bcm_module_init); -- cgit From 1ef83310b81551079af992c4cbb5e089dd1397be Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 25 Apr 2017 08:19:43 +0200 Subject: can: network namespace support for CAN gateway The CAN gateway was not implemented as per-net in the initial network namespace support by Mario Kicherer (8e8cda6d737d). This patch enables the CAN gateway to be used in different namespaces. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- include/net/netns/can.h | 3 +++ net/can/gw.c | 72 ++++++++++++++++++++++++++++++------------------- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/include/net/netns/can.h b/include/net/netns/can.h index 0f3c31aab8a8..b106e6ae2e5b 100644 --- a/include/net/netns/can.h +++ b/include/net/netns/can.h @@ -32,6 +32,9 @@ struct netns_can { struct timer_list can_stattimer;/* timer for statistics update */ struct s_stats *can_stats; /* packet statistics */ struct s_pstats *can_pstats; /* receive list statistics */ + + /* CAN GW per-net gateway jobs */ + struct hlist_head cgw_list; }; #endif /* __NETNS_CAN_H__ */ diff --git a/net/can/gw.c b/net/can/gw.c index ad5bf5d508d3..29748d844c3f 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -1,7 +1,7 @@ /* * gw.c - CAN frame Gateway/Router/Bridge with netlink interface * - * Copyright (c) 2011 Volkswagen Group Electronic Research + * Copyright (c) 2017 Volkswagen Group Electronic Research * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -59,7 +59,7 @@ #include #include -#define CAN_GW_VERSION "20130117" +#define CAN_GW_VERSION "20170425" #define CAN_GW_NAME "can-gw" MODULE_DESCRIPTION("PF_CAN netlink gateway"); @@ -79,9 +79,7 @@ MODULE_PARM_DESC(max_hops, __stringify(CGW_MAX_HOPS) " hops, " "default: " __stringify(CGW_DEFAULT_HOPS) ")"); -static HLIST_HEAD(cgw_list); static struct notifier_block notifier; - static struct kmem_cache *cgw_cache __read_mostly; /* structure that contains the (on-the-fly) CAN frame modifications */ @@ -438,16 +436,16 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data) gwj->handled_frames++; } -static inline int cgw_register_filter(struct cgw_job *gwj) +static inline int cgw_register_filter(struct net *net, struct cgw_job *gwj) { - return can_rx_register(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id, + return can_rx_register(net, gwj->src.dev, gwj->ccgw.filter.can_id, gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj, "gw", NULL); } -static inline void cgw_unregister_filter(struct cgw_job *gwj) +static inline void cgw_unregister_filter(struct net *net, struct cgw_job *gwj) { - can_rx_unregister(&init_net, gwj->src.dev, gwj->ccgw.filter.can_id, + can_rx_unregister(net, gwj->src.dev, gwj->ccgw.filter.can_id, gwj->ccgw.filter.can_mask, can_can_gw_rcv, gwj); } @@ -455,9 +453,8 @@ static int cgw_notifier(struct notifier_block *nb, unsigned long msg, void *ptr) { struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct net *net = dev_net(dev); - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; if (dev->type != ARPHRD_CAN) return NOTIFY_DONE; @@ -468,11 +465,11 @@ static int cgw_notifier(struct notifier_block *nb, ASSERT_RTNL(); - hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) { + hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) { if (gwj->src.dev == dev || gwj->dst.dev == dev) { hlist_del(&gwj->list); - cgw_unregister_filter(gwj); + cgw_unregister_filter(net, gwj); kmem_cache_free(cgw_cache, gwj); } } @@ -592,12 +589,13 @@ cancel: /* Dump information about all CAN gateway jobs, in response to RTM_GETROUTE */ static int cgw_dump_jobs(struct sk_buff *skb, struct netlink_callback *cb) { + struct net *net = sock_net(skb->sk); struct cgw_job *gwj = NULL; int idx = 0; int s_idx = cb->args[0]; rcu_read_lock(); - hlist_for_each_entry_rcu(gwj, &cgw_list, list) { + hlist_for_each_entry_rcu(gwj, &net->can.cgw_list, list) { if (idx < s_idx) goto cont; @@ -812,6 +810,7 @@ static int cgw_parse_attr(struct nlmsghdr *nlh, struct cf_mod *mod, static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { + struct net *net = sock_net(skb->sk); struct rtcanmsg *r; struct cgw_job *gwj; struct cf_mod mod; @@ -842,7 +841,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, ASSERT_RTNL(); /* check for updating an existing job with identical uid */ - hlist_for_each_entry(gwj, &cgw_list, list) { + hlist_for_each_entry(gwj, &net->can.cgw_list, list) { if (gwj->mod.uid != mod.uid) continue; @@ -880,7 +879,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, err = -ENODEV; - gwj->src.dev = __dev_get_by_index(&init_net, gwj->ccgw.src_idx); + gwj->src.dev = __dev_get_by_index(net, gwj->ccgw.src_idx); if (!gwj->src.dev) goto out; @@ -888,7 +887,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, if (gwj->src.dev->type != ARPHRD_CAN) goto out; - gwj->dst.dev = __dev_get_by_index(&init_net, gwj->ccgw.dst_idx); + gwj->dst.dev = __dev_get_by_index(net, gwj->ccgw.dst_idx); if (!gwj->dst.dev) goto out; @@ -898,9 +897,9 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh, ASSERT_RTNL(); - err = cgw_register_filter(gwj); + err = cgw_register_filter(net, gwj); if (!err) - hlist_add_head_rcu(&gwj->list, &cgw_list); + hlist_add_head_rcu(&gwj->list, &net->can.cgw_list); out: if (err) kmem_cache_free(cgw_cache, gwj); @@ -908,16 +907,16 @@ out: return err; } -static void cgw_remove_all_jobs(void) +static void cgw_remove_all_jobs(struct net *net) { struct cgw_job *gwj = NULL; struct hlist_node *nx; ASSERT_RTNL(); - hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) { + hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) { hlist_del(&gwj->list); - cgw_unregister_filter(gwj); + cgw_unregister_filter(net, gwj); kmem_cache_free(cgw_cache, gwj); } } @@ -925,6 +924,7 @@ static void cgw_remove_all_jobs(void) static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { + struct net *net = sock_net(skb->sk); struct cgw_job *gwj = NULL; struct hlist_node *nx; struct rtcanmsg *r; @@ -953,7 +953,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, /* two interface indices both set to 0 => remove all entries */ if (!ccgw.src_idx && !ccgw.dst_idx) { - cgw_remove_all_jobs(); + cgw_remove_all_jobs(net); return 0; } @@ -962,7 +962,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, ASSERT_RTNL(); /* remove only the first matching entry */ - hlist_for_each_entry_safe(gwj, nx, &cgw_list, list) { + hlist_for_each_entry_safe(gwj, nx, &net->can.cgw_list, list) { if (gwj->flags != r->flags) continue; @@ -985,7 +985,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, continue; hlist_del(&gwj->list); - cgw_unregister_filter(gwj); + cgw_unregister_filter(net, gwj); kmem_cache_free(cgw_cache, gwj); err = 0; break; @@ -994,6 +994,24 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, return err; } +static int __net_init cangw_pernet_init(struct net *net) +{ + INIT_HLIST_HEAD(&net->can.cgw_list); + return 0; +} + +static void __net_exit cangw_pernet_exit(struct net *net) +{ + rtnl_lock(); + cgw_remove_all_jobs(net); + rtnl_unlock(); +} + +static struct pernet_operations cangw_pernet_ops = { + .init = cangw_pernet_init, + .exit = cangw_pernet_exit, +}; + static __init int cgw_module_init(void) { /* sanitize given module parameter */ @@ -1002,6 +1020,7 @@ static __init int cgw_module_init(void) pr_info("can: netlink gateway (rev " CAN_GW_VERSION ") max_hops=%d\n", max_hops); + register_pernet_subsys(&cangw_pernet_ops); cgw_cache = kmem_cache_create("can_gw", sizeof(struct cgw_job), 0, 0, NULL); @@ -1031,10 +1050,7 @@ static __exit void cgw_module_exit(void) unregister_netdevice_notifier(¬ifier); - rtnl_lock(); - cgw_remove_all_jobs(); - rtnl_unlock(); - + unregister_pernet_subsys(&cangw_pernet_ops); rcu_barrier(); /* Wait for completion of call_rcu()'s */ kmem_cache_destroy(cgw_cache); -- cgit From a8f820a380a2a06fc4fe1a54159067958f800929 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 25 Apr 2017 08:19:44 +0200 Subject: can: add Virtual CAN Tunnel driver (vxcan) Similar to the virtual ethernet driver veth, vxcan implements a local CAN traffic tunnel between two virtual CAN network devices. See Kconfig entry for details. Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- drivers/net/can/Kconfig | 18 +++ drivers/net/can/Makefile | 1 + drivers/net/can/vxcan.c | 316 +++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/can/vxcan.h | 12 ++ 4 files changed, 347 insertions(+) create mode 100644 drivers/net/can/vxcan.c create mode 100644 include/uapi/linux/can/vxcan.h diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index aa20b69d2a1a..ac4ff394bc56 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -9,6 +9,24 @@ config CAN_VCAN This driver can also be built as a module. If so, the module will be called vcan. +config CAN_VXCAN + tristate "Virtual CAN Tunnel (vxcan)" + ---help--- + Similar to the virtual ethernet driver veth, vxcan implements a + local CAN traffic tunnel between two virtual CAN network devices. + When creating a vxcan, two vxcan devices are created as pair. + When one end receives the packet it appears on its pair and vice + versa. The vxcan can be used for cross namespace communication. + + In opposite to vcan loopback devices the vxcan only forwards CAN + frames to its pair and does *not* provide a local echo of sent + CAN frames. To disable a potential echo in af_can.c the vxcan driver + announces IFF_ECHO in the interface flags. To have a clean start + in each namespace the CAN GW hop counter is set to zero. + + This driver can also be built as a module. If so, the module + will be called vxcan. + config CAN_SLCAN tristate "Serial / USB serial CAN Adaptors (slcan)" depends on TTY diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 8581e2b3e87f..4aabbee133b8 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_CAN_VCAN) += vcan.o +obj-$(CONFIG_CAN_VXCAN) += vxcan.o obj-$(CONFIG_CAN_SLCAN) += slcan.o obj-$(CONFIG_CAN_DEV) += can-dev.o diff --git a/drivers/net/can/vxcan.c b/drivers/net/can/vxcan.c new file mode 100644 index 000000000000..7fbb24795681 --- /dev/null +++ b/drivers/net/can/vxcan.c @@ -0,0 +1,316 @@ +/* + * vxcan.c - Virtual CAN Tunnel for cross namespace communication + * + * This code is derived from drivers/net/can/vcan.c for the virtual CAN + * specific parts and from drivers/net/veth.c to implement the netlink API + * for network interface pairs in a common and established way. + * + * Copyright (c) 2017 Oliver Hartkopp + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "vxcan" + +MODULE_DESCRIPTION("Virtual CAN Tunnel"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Oliver Hartkopp "); +MODULE_ALIAS_RTNL_LINK(DRV_NAME); + +struct vxcan_priv { + struct net_device __rcu *peer; +}; + +static netdev_tx_t vxcan_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer; + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + struct net_device_stats *peerstats, *srcstats = &dev->stats; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + rcu_read_lock(); + peer = rcu_dereference(priv->peer); + if (unlikely(!peer)) { + kfree_skb(skb); + dev->stats.tx_dropped++; + goto out_unlock; + } + + skb = can_create_echo_skb(skb); + if (!skb) + goto out_unlock; + + /* reset CAN GW hop counter */ + skb->csum_start = 0; + skb->pkt_type = PACKET_BROADCAST; + skb->dev = peer; + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (netif_rx_ni(skb) == NET_RX_SUCCESS) { + srcstats->tx_packets++; + srcstats->tx_bytes += cfd->len; + peerstats = &peer->stats; + peerstats->rx_packets++; + peerstats->rx_bytes += cfd->len; + } + +out_unlock: + rcu_read_unlock(); + return NETDEV_TX_OK; +} + + +static int vxcan_open(struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer = rtnl_dereference(priv->peer); + + if (!peer) + return -ENOTCONN; + + if (peer->flags & IFF_UP) { + netif_carrier_on(dev); + netif_carrier_on(peer); + } + return 0; +} + +static int vxcan_close(struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer = rtnl_dereference(priv->peer); + + netif_carrier_off(dev); + if (peer) + netif_carrier_off(peer); + + return 0; +} + +static int vxcan_get_iflink(const struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer; + int iflink; + + rcu_read_lock(); + peer = rcu_dereference(priv->peer); + iflink = peer ? peer->ifindex : 0; + rcu_read_unlock(); + + return iflink; +} + +static int vxcan_change_mtu(struct net_device *dev, int new_mtu) +{ + /* Do not allow changing the MTU while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + + if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + +static const struct net_device_ops vxcan_netdev_ops = { + .ndo_open = vxcan_open, + .ndo_stop = vxcan_close, + .ndo_start_xmit = vxcan_xmit, + .ndo_get_iflink = vxcan_get_iflink, + .ndo_change_mtu = vxcan_change_mtu, +}; + +static void vxcan_setup(struct net_device *dev) +{ + dev->type = ARPHRD_CAN; + dev->mtu = CAN_MTU; + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->tx_queue_len = 0; + dev->flags = (IFF_NOARP|IFF_ECHO); + dev->netdev_ops = &vxcan_netdev_ops; + dev->destructor = free_netdev; +} + +/* forward declaration for rtnl_create_link() */ +static struct rtnl_link_ops vxcan_link_ops; + +static int vxcan_newlink(struct net *net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct vxcan_priv *priv; + struct net_device *peer; + struct net *peer_net; + + struct nlattr *peer_tb[IFLA_MAX + 1], **tbp = tb; + char ifname[IFNAMSIZ]; + unsigned char name_assign_type; + struct ifinfomsg *ifmp = NULL; + int err; + + /* register peer device */ + if (data && data[VXCAN_INFO_PEER]) { + struct nlattr *nla_peer; + + nla_peer = data[VXCAN_INFO_PEER]; + ifmp = nla_data(nla_peer); + err = rtnl_nla_parse_ifla(peer_tb, + nla_data(nla_peer) + + sizeof(struct ifinfomsg), + nla_len(nla_peer) - + sizeof(struct ifinfomsg), + NULL); + if (err < 0) + return err; + + tbp = peer_tb; + } + + if (tbp[IFLA_IFNAME]) { + nla_strlcpy(ifname, tbp[IFLA_IFNAME], IFNAMSIZ); + name_assign_type = NET_NAME_USER; + } else { + snprintf(ifname, IFNAMSIZ, DRV_NAME "%%d"); + name_assign_type = NET_NAME_ENUM; + } + + peer_net = rtnl_link_get_net(net, tbp); + if (IS_ERR(peer_net)) + return PTR_ERR(peer_net); + + peer = rtnl_create_link(peer_net, ifname, name_assign_type, + &vxcan_link_ops, tbp); + if (IS_ERR(peer)) { + put_net(peer_net); + return PTR_ERR(peer); + } + + if (ifmp && dev->ifindex) + peer->ifindex = ifmp->ifi_index; + + err = register_netdevice(peer); + put_net(peer_net); + peer_net = NULL; + if (err < 0) { + free_netdev(peer); + return err; + } + + netif_carrier_off(peer); + + err = rtnl_configure_link(peer, ifmp); + if (err < 0) { + unregister_netdevice(peer); + return err; + } + + /* register first device */ + if (tb[IFLA_IFNAME]) + nla_strlcpy(dev->name, tb[IFLA_IFNAME], IFNAMSIZ); + else + snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d"); + + err = register_netdevice(dev); + if (err < 0) { + unregister_netdevice(peer); + return err; + } + + netif_carrier_off(dev); + + /* cross link the device pair */ + priv = netdev_priv(dev); + rcu_assign_pointer(priv->peer, peer); + + priv = netdev_priv(peer); + rcu_assign_pointer(priv->peer, dev); + + return 0; +} + +static void vxcan_dellink(struct net_device *dev, struct list_head *head) +{ + struct vxcan_priv *priv; + struct net_device *peer; + + priv = netdev_priv(dev); + peer = rtnl_dereference(priv->peer); + + /* Note : dellink() is called from default_device_exit_batch(), + * before a rcu_synchronize() point. The devices are guaranteed + * not being freed before one RCU grace period. + */ + RCU_INIT_POINTER(priv->peer, NULL); + unregister_netdevice_queue(dev, head); + + if (peer) { + priv = netdev_priv(peer); + RCU_INIT_POINTER(priv->peer, NULL); + unregister_netdevice_queue(peer, head); + } +} + +static const struct nla_policy vxcan_policy[VXCAN_INFO_MAX + 1] = { + [VXCAN_INFO_PEER] = { .len = sizeof(struct ifinfomsg) }, +}; + +static struct net *vxcan_get_link_net(const struct net_device *dev) +{ + struct vxcan_priv *priv = netdev_priv(dev); + struct net_device *peer = rtnl_dereference(priv->peer); + + return peer ? dev_net(peer) : dev_net(dev); +} + +static struct rtnl_link_ops vxcan_link_ops = { + .kind = DRV_NAME, + .priv_size = sizeof(struct vxcan_priv), + .setup = vxcan_setup, + .newlink = vxcan_newlink, + .dellink = vxcan_dellink, + .policy = vxcan_policy, + .maxtype = VXCAN_INFO_MAX, + .get_link_net = vxcan_get_link_net, +}; + +static __init int vxcan_init(void) +{ + pr_info("vxcan: Virtual CAN Tunnel driver\n"); + + return rtnl_link_register(&vxcan_link_ops); +} + +static __exit void vxcan_exit(void) +{ + rtnl_link_unregister(&vxcan_link_ops); +} + +module_init(vxcan_init); +module_exit(vxcan_exit); diff --git a/include/uapi/linux/can/vxcan.h b/include/uapi/linux/can/vxcan.h new file mode 100644 index 000000000000..ffb0b7156f7e --- /dev/null +++ b/include/uapi/linux/can/vxcan.h @@ -0,0 +1,12 @@ +#ifndef _UAPI_CAN_VXCAN_H +#define _UAPI_CAN_VXCAN_H + +enum { + VXCAN_INFO_UNSPEC, + VXCAN_INFO_PEER, + + __VXCAN_INFO_MAX +#define VXCAN_INFO_MAX (__VXCAN_INFO_MAX - 1) +}; + +#endif -- cgit From 5e64ebc1c242523b491b5b5e5eba59243bb970cb Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 25 Apr 2017 08:19:45 +0200 Subject: can: enable module auto loading for virtual CAN interfaces Autoload the vcan module when a vcan instance is to be created by 'ip link add type vcan' Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- drivers/net/can/vcan.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/vcan.c b/drivers/net/can/vcan.c index 674f367087c5..facca33d53e9 100644 --- a/drivers/net/can/vcan.c +++ b/drivers/net/can/vcan.c @@ -1,7 +1,7 @@ /* * vcan.c - Virtual CAN interface * - * Copyright (c) 2002-2007 Volkswagen Group Electronic Research + * Copyright (c) 2002-2017 Volkswagen Group Electronic Research * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -50,9 +50,12 @@ #include #include +#define DRV_NAME "vcan" + MODULE_DESCRIPTION("virtual CAN interface"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Urs Thuermann "); +MODULE_ALIAS_RTNL_LINK(DRV_NAME); /* @@ -164,7 +167,7 @@ static void vcan_setup(struct net_device *dev) } static struct rtnl_link_ops vcan_link_ops __read_mostly = { - .kind = "vcan", + .kind = DRV_NAME, .setup = vcan_setup, }; -- cgit From b655f0e96d4061eac42dea2dccd37a3348d1f3f3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 25 Apr 2017 06:44:05 +0000 Subject: can: ti_hecc: fix return value check in ti_hecc_probe() In case of error, the function devm_ioremap_resource() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Fixes: dabf54dd1c63 ("can: ti_hecc: Convert TI HECC driver to DT only driver") Signed-off-by: Wei Yongjun Signed-off-by: Marc Kleine-Budde --- drivers/net/can/ti_hecc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index b8aac538275c..4d4941469cfc 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -898,9 +898,9 @@ static int ti_hecc_probe(struct platform_device *pdev) } priv->base = devm_ioremap_resource(&pdev->dev, res); - if (!priv->base) { + if (IS_ERR(priv->base)) { dev_err(&pdev->dev, "hecc ioremap failed\n"); - return -ENOMEM; + return PTR_ERR(priv->base); } /* handle hecc-ram memory */ @@ -911,9 +911,9 @@ static int ti_hecc_probe(struct platform_device *pdev) } priv->hecc_ram = devm_ioremap_resource(&pdev->dev, res); - if (!priv->hecc_ram) { + if (IS_ERR(priv->hecc_ram)) { dev_err(&pdev->dev, "hecc-ram ioremap failed\n"); - return -ENOMEM; + return PTR_ERR(priv->hecc_ram); } /* handle mbx memory */ @@ -924,9 +924,9 @@ static int ti_hecc_probe(struct platform_device *pdev) } priv->mbx = devm_ioremap_resource(&pdev->dev, res); - if (!priv->mbx) { + if (IS_ERR(priv->mbx)) { dev_err(&pdev->dev, "mbx ioremap failed\n"); - return -ENOMEM; + return PTR_ERR(priv->mbx); } irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); -- cgit From 77e409455f4171e24e95774cc80419f68b07af2f Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 11 Jan 2017 11:58:38 +0200 Subject: iwlwifi: mvm: support new rate flags Rates were changed to adapt to HE. Change is backward compatible. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h | 28 +++++++++++++++++----- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 11 ++++----- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 4 ++-- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h index ad9cc03e16c4..1b7d265ffb0a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rs.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2017 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 @@ -31,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -227,6 +229,9 @@ enum { */ #define RATE_LEGACY_RATE_MSK 0xff +/* Bit 10 - OFDM HE */ +#define RATE_MCS_OFDM_HE_POS 10 +#define RATE_MCS_OFDM_HE_MSK BIT(RATE_MCS_OFDM_HE_POS) /* * Bit 11-12: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz @@ -255,18 +260,29 @@ enum { #define RATE_MCS_ANT_MSK RATE_MCS_ANT_ABC_MSK #define RATE_MCS_ANT_NUM 3 -/* Bit 17-18: (0) SS, (1) SS*2 */ +/* Bit 17: (0) SS, (1) SS*2 */ #define RATE_MCS_STBC_POS 17 -#define RATE_MCS_HT_STBC_MSK (3 << RATE_MCS_STBC_POS) -#define RATE_MCS_VHT_STBC_MSK (1 << RATE_MCS_STBC_POS) +#define RATE_MCS_STBC_MSK BIT(RATE_MCS_STBC_POS) + +/* Bit 18: OFDM-HE dual carrier mode */ +#define RATE_HE_DUAL_CARRIER_MODE 18 +#define RATE_HE_DUAL_CARRIER_MODE_MSK BIT(RATE_HE_DUAL_CARRIER_MODE) /* Bit 19: (0) Beamforming is off, (1) Beamforming is on */ #define RATE_MCS_BF_POS 19 #define RATE_MCS_BF_MSK (1 << RATE_MCS_BF_POS) -/* Bit 20: (0) ZLF is off, (1) ZLF is on */ -#define RATE_MCS_ZLF_POS 20 -#define RATE_MCS_ZLF_MSK (1 << RATE_MCS_ZLF_POS) +/* + * Bit 20-21: HE guard interval and LTF type. + * (0) 1xLTF+1.6us, (1) 2xLTF+0.8us, + * (2) 2xLTF+1.6us, (3) 4xLTF+3.2us + */ +#define RATE_MCS_HE_GI_LTF_POS 20 +#define RATE_MCS_HE_GI_LTF_MSK (3 << RATE_MCS_HE_GI_LTF_POS) + +/* Bit 22-23: HE type. (0) SU, (1) SU_EXT, (2) MU, (3) trigger based */ +#define RATE_MCS_HE_TYPE_POS 22 +#define RATE_MCS_HE_TYPE_MSK (3 << RATE_MCS_HE_TYPE_POS) /* Bit 24-25: (0) 20MHz (no dup), (1) 2x20MHz, (2) 4x20MHz, 3 8x20MHz */ #define RATE_MCS_DUP_POS 24 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index ce907c58ebf6..7788eefcd2bd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -826,7 +826,7 @@ static u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm, if (is_siso(rate) && rate->stbc) { /* To enable STBC we need to set both a flag and ANT_AB */ ucode_rate |= RATE_MCS_ANT_AB_MSK; - ucode_rate |= RATE_MCS_VHT_STBC_MSK; + ucode_rate |= RATE_MCS_STBC_MSK; } ucode_rate |= rate->bw; @@ -873,7 +873,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate, rate->sgi = true; if (ucode_rate & RATE_MCS_LDPC_MSK) rate->ldpc = true; - if (ucode_rate & RATE_MCS_VHT_STBC_MSK) + if (ucode_rate & RATE_MCS_STBC_MSK) rate->stbc = true; if (ucode_rate & RATE_MCS_BF_MSK) rate->bfer = true; @@ -3641,13 +3641,12 @@ int rs_pretty_print_rate(char *buf, const u32 rate) bw = "BAD BW"; } - return sprintf(buf, "%s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s\n", + return sprintf(buf, "%s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s\n", type, rs_pretty_ant(ant), bw, mcs, nss, (rate & RATE_MCS_SGI_MSK) ? "SGI " : "NGI ", - (rate & RATE_MCS_HT_STBC_MSK) ? "STBC " : "", + (rate & RATE_MCS_STBC_MSK) ? "STBC " : "", (rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "", - (rate & RATE_MCS_BF_MSK) ? "BF " : "", - (rate & RATE_MCS_ZLF_MSK) ? "ZLF " : ""); + (rate & RATE_MCS_BF_MSK) ? "BF " : ""); } /** diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index d4c0ca7ccb34..d7c8e7de960b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -443,13 +443,13 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, if (rate_n_flags & RATE_MCS_LDPC_MSK) rx_status->flag |= RX_FLAG_LDPC; if (rate_n_flags & RATE_MCS_HT_MSK) { - u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> + u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >> RATE_MCS_STBC_POS; rx_status->flag |= RX_FLAG_HT; rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; } else if (rate_n_flags & RATE_MCS_VHT_MSK) { - u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >> + u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >> RATE_MCS_STBC_POS; rx_status->vht_nss = ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 24c4fbe139a3..362b240da65c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -974,13 +974,13 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, if (rate_n_flags & RATE_MCS_LDPC_MSK) rx_status->flag |= RX_FLAG_LDPC; if (rate_n_flags & RATE_MCS_HT_MSK) { - u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> + u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >> RATE_MCS_STBC_POS; rx_status->flag |= RX_FLAG_HT; rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; } else if (rate_n_flags & RATE_MCS_VHT_MSK) { - u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >> + u8 stbc = (rate_n_flags & RATE_MCS_STBC_MSK) >> RATE_MCS_STBC_POS; rx_status->vht_nss = ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> -- cgit From 8c5f47b15c5c1537c000e3a4c92c82c5bbf521a4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 20 Feb 2017 17:47:04 +0100 Subject: iwlwifi: mvm: make iwl_run_unified_mvm_ucode() static There's no need to have iwl_run_unified_mvm_ucode() be exposed to other parts of the code since the logic to pick it over the normal code in iwl_run_init_mvm_ucode() can just be done in that function itself. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 155 ++++++++++++++------------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 - drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 5 +- 3 files changed, 80 insertions(+), 81 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 900f1e25b9da..b5c7dffafdc1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -698,6 +698,82 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, return 0; } +static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) +{ + struct iwl_notification_wait init_wait; + struct iwl_nvm_access_complete_cmd nvm_complete = {}; + struct iwl_init_extended_cfg_cmd init_cfg = { + .init_flags = cpu_to_le32(BIT(IWL_INIT_NVM)), + }; + static const u16 init_complete[] = { + INIT_COMPLETE_NOTIF, + }; + int ret; + + lockdep_assert_held(&mvm->mutex); + + iwl_init_notification_wait(&mvm->notif_wait, + &init_wait, + init_complete, + ARRAY_SIZE(init_complete), + iwl_wait_init_complete, + NULL); + + /* Will also start the device */ + ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); + if (ret) { + IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); + goto error; + } + + /* Send init config command to mark that we are sending NVM access + * commands + */ + ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(SYSTEM_GROUP, + INIT_EXTENDED_CFG_CMD), 0, + sizeof(init_cfg), &init_cfg); + if (ret) { + IWL_ERR(mvm, "Failed to run init config command: %d\n", + ret); + goto error; + } + + /* Read the NVM only at driver load time, no need to do this twice */ + if (read_nvm) { + /* Read nvm */ + ret = iwl_nvm_init(mvm, true); + if (ret) { + IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); + goto error; + } + } + + /* In case we read the NVM from external file, load it to the NIC */ + if (mvm->nvm_file_name) + iwl_mvm_load_nvm_to_nic(mvm); + + ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans); + if (WARN_ON(ret)) + goto error; + + ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(REGULATORY_AND_NVM_GROUP, + NVM_ACCESS_COMPLETE), 0, + sizeof(nvm_complete), &nvm_complete); + if (ret) { + IWL_ERR(mvm, "Failed to run complete NVM access: %d\n", + ret); + goto error; + } + + /* We wait for the INIT complete notification */ + return iwl_wait_notification(&mvm->notif_wait, &init_wait, + MVM_UCODE_ALIVE_TIMEOUT); + +error: + iwl_remove_notification(&mvm->notif_wait, &init_wait); + return ret; +} + static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) { struct iwl_phy_cfg_cmd phy_cfg_cmd; @@ -726,6 +802,9 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) }; int ret; + if (iwl_mvm_has_new_tx_api(mvm)) + return iwl_run_unified_mvm_ucode(mvm, true); + lockdep_assert_held(&mvm->mutex); if (WARN_ON_ONCE(mvm->calibrating)) @@ -832,82 +911,6 @@ out: return ret; } -int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) -{ - struct iwl_notification_wait init_wait; - struct iwl_nvm_access_complete_cmd nvm_complete = {}; - struct iwl_init_extended_cfg_cmd init_cfg = { - .init_flags = cpu_to_le32(BIT(IWL_INIT_NVM)), - }; - static const u16 init_complete[] = { - INIT_COMPLETE_NOTIF, - }; - int ret; - - lockdep_assert_held(&mvm->mutex); - - iwl_init_notification_wait(&mvm->notif_wait, - &init_wait, - init_complete, - ARRAY_SIZE(init_complete), - iwl_wait_init_complete, - NULL); - - /* Will also start the device */ - ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); - if (ret) { - IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); - goto error; - } - - /* Send init config command to mark that we are sending NVM access - * commands - */ - ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(SYSTEM_GROUP, - INIT_EXTENDED_CFG_CMD), 0, - sizeof(init_cfg), &init_cfg); - if (ret) { - IWL_ERR(mvm, "Failed to run init config command: %d\n", - ret); - goto error; - } - - /* Read the NVM only at driver load time, no need to do this twice */ - if (read_nvm) { - /* Read nvm */ - ret = iwl_nvm_init(mvm, true); - if (ret) { - IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); - goto error; - } - } - - /* In case we read the NVM from external file, load it to the NIC */ - if (mvm->nvm_file_name) - iwl_mvm_load_nvm_to_nic(mvm); - - ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans); - if (WARN_ON(ret)) - goto error; - - ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(REGULATORY_AND_NVM_GROUP, - NVM_ACCESS_COMPLETE), 0, - sizeof(nvm_complete), &nvm_complete); - if (ret) { - IWL_ERR(mvm, "Failed to run complete NVM access: %d\n", - ret); - goto error; - } - - /* We wait for the INIT complete notification */ - return iwl_wait_notification(&mvm->notif_wait, &init_wait, - MVM_UCODE_ALIVE_TIMEOUT); - -error: - iwl_remove_notification(&mvm->notif_wait, &init_wait); - return ret; -} - static void iwl_mvm_parse_shared_mem_a000(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 1938dfb44152..ab87c0203b6f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1312,7 +1312,6 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm); ******************/ /* uCode */ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm); -int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm); /* Utils */ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 888053323c92..363930510a6e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -746,10 +746,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mutex_lock(&mvm->mutex); iwl_mvm_ref(mvm, IWL_MVM_REF_INIT_UCODE); - if (iwl_mvm_has_new_tx_api(mvm)) - err = iwl_run_unified_mvm_ucode(mvm, true); - else - err = iwl_run_init_mvm_ucode(mvm, true); + err = iwl_run_init_mvm_ucode(mvm, true); if (!err || !iwlmvm_mod_params.init_dbg) iwl_mvm_stop_device(mvm); iwl_mvm_unref(mvm, IWL_MVM_REF_INIT_UCODE); -- cgit From e3df1e4b51e5e54637355db7d732fedf271530e7 Mon Sep 17 00:00:00 2001 From: Sharon Dvir Date: Tue, 21 Feb 2017 10:41:31 +0200 Subject: iwlwifi: mvm: check if returned value is NULL While freeing inactive queue, check mvmsta to be valid before dereferencing it. Found by Klocwork. Signed-off-by: Sharon Dvir Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index a2a1fa06b781..fc6d854b04b6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -495,6 +495,8 @@ static int iwl_mvm_free_inactive_queue(struct iwl_mvm *mvm, int queue, spin_unlock_bh(&mvm->queue_info_lock); mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); + if (WARN_ON(!mvmsta)) + return -EINVAL; disable_agg_tids = iwl_mvm_remove_sta_queue_marking(mvm, queue); /* Disable the queue */ -- cgit From 3287bb1088d5470b2057ad3fefb9f3dae586515f Mon Sep 17 00:00:00 2001 From: Mordechai Goodstein Date: Sun, 12 Feb 2017 12:20:52 +0200 Subject: iwlwifi: mvm: scan: avoid "big" prints Delete the scanned channel results. No need in it we get it any way when logging. The print only clogs up the ftrace print buffer. Signed-off-by: Mordechai Goodstein Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 31 ++++----------------------- 1 file changed, 4 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 9668f945b4e6..ca6f3a82b277 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -294,34 +294,15 @@ int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm) return max_ie_len; } -static u8 *iwl_mvm_dump_channel_list(struct iwl_scan_results_notif *res, - int num_res, u8 *buf, size_t buf_size) -{ - int i; - u8 *pos = buf, *end = buf + buf_size; - - for (i = 0; pos < end && i < num_res; i++) - pos += snprintf(pos, end - pos, " %u", res[i].channel); - - /* terminate the string in case the buffer was too short */ - *(buf + buf_size - 1) = '\0'; - - return buf; -} - void iwl_mvm_rx_lmac_scan_iter_complete_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_lmac_scan_complete_notif *notif = (void *)pkt->data; - u8 buf[256]; IWL_DEBUG_SCAN(mvm, - "Scan offload iteration complete: status=0x%x scanned channels=%d channels list: %s\n", - notif->status, notif->scanned_channels, - iwl_mvm_dump_channel_list(notif->results, - notif->scanned_channels, buf, - sizeof(buf))); + "Scan offload iteration complete: status=0x%x scanned channels=%d\n", + notif->status, notif->scanned_channels); if (mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_FOUND) { IWL_DEBUG_SCAN(mvm, "Pass all scheduled scan results found\n"); @@ -1612,16 +1593,12 @@ void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm, { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_umac_scan_iter_complete_notif *notif = (void *)pkt->data; - u8 buf[256]; mvm->scan_start = le64_to_cpu(notif->start_tsf); IWL_DEBUG_SCAN(mvm, - "UMAC Scan iteration complete: status=0x%x scanned_channels=%d channels list: %s\n", - notif->status, notif->scanned_channels, - iwl_mvm_dump_channel_list(notif->results, - notif->scanned_channels, buf, - sizeof(buf))); + "UMAC Scan iteration complete: status=0x%x scanned_channels=%d\n", + notif->status, notif->scanned_channels); if (mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_FOUND) { IWL_DEBUG_SCAN(mvm, "Pass all scheduled scan results found\n"); -- cgit From e2ef147686751f1a08143bb7b3bda629cceb9369 Mon Sep 17 00:00:00 2001 From: Sharon Dvir Date: Tue, 21 Feb 2017 11:12:12 +0200 Subject: iwlwifi: mvm: handle possible BIOS bug In iwl_mvm_sar_get_ewrd_table() In case of a BIOS bug, n_profiles might be 0 thus we need to return an error value. Found by Klocwork. Signed-off-by: Sharon Dvir Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index b5c7dffafdc1..e6c9528eeeda 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1201,6 +1201,12 @@ static int iwl_mvm_sar_get_ewrd_table(struct iwl_mvm *mvm) enabled = !!(wifi_pkg->package.elements[1].integer.value); n_profiles = wifi_pkg->package.elements[2].integer.value; + /* in case of BIOS bug */ + if (n_profiles <= 0) { + ret = -EINVAL; + goto out_free; + } + for (i = 0; i < n_profiles; i++) { /* the tables start at element 3 */ static int pos = 3; -- cgit From 3133822f5ac13b04c2ca46f27cfe74606bbd4a6d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 20 Apr 2017 18:08:15 +0200 Subject: ipvlan: use pernet operations and restrict l3s hooks to master netns commit 4fbae7d83c98c30efc ("ipvlan: Introduce l3s mode") added registration of netfilter hooks via nf_register_hooks(). This API provides the illusion of 'global' netfilter hooks by placing the hooks in all current and future network namespaces. In case of ipvlan the hook appears to be only needed in the namespace that contains the ipvlan master device (i.e., usually init_net), so placing them in all namespaces is not needed. This switches ipvlan driver to pernet operations, and then only registers hooks in namespaces where a ipvlan master device is set to l3s mode. Extra care has to be taken when the master device is moved to another namespace, as we might have to 'move' the netfilter hooks too. This is done by storing the namespace the ipvlan port was created in. On REGISTER event, do (un)register operations in the old/new namespaces. This will also allow removal of the nf_register_hooks() in a future patch. Cc: Mahesh Bandewar Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- drivers/net/ipvlan/ipvlan.h | 2 + drivers/net/ipvlan/ipvlan_main.c | 83 ++++++++++++++++++++++++++++++++-------- 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h index 800a46c8d26c..7919369c0a72 100644 --- a/drivers/net/ipvlan/ipvlan.h +++ b/drivers/net/ipvlan/ipvlan.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,7 @@ struct ipvl_addr { struct ipvl_port { struct net_device *dev; + possible_net_t pnet; struct hlist_head hlhead[IPVLAN_HASH_SIZE]; struct list_head ipvlans; u16 mode; diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index aa8575ccbce3..618ed88fad0f 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -9,7 +9,11 @@ #include "ipvlan.h" -static u32 ipvl_nf_hook_refcnt = 0; +static unsigned int ipvlan_netid __read_mostly; + +struct ipvlan_netns { + unsigned int ipvl_nf_hook_refcnt; +}; static struct nf_hook_ops ipvl_nfops[] __read_mostly = { { @@ -35,28 +39,34 @@ static void ipvlan_adjust_mtu(struct ipvl_dev *ipvlan, struct net_device *dev) ipvlan->dev->mtu = dev->mtu; } -static int ipvlan_register_nf_hook(void) +static int ipvlan_register_nf_hook(struct net *net) { + struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid); int err = 0; - if (!ipvl_nf_hook_refcnt) { - err = _nf_register_hooks(ipvl_nfops, ARRAY_SIZE(ipvl_nfops)); + if (!vnet->ipvl_nf_hook_refcnt) { + err = nf_register_net_hooks(net, ipvl_nfops, + ARRAY_SIZE(ipvl_nfops)); if (!err) - ipvl_nf_hook_refcnt = 1; + vnet->ipvl_nf_hook_refcnt = 1; } else { - ipvl_nf_hook_refcnt++; + vnet->ipvl_nf_hook_refcnt++; } return err; } -static void ipvlan_unregister_nf_hook(void) +static void ipvlan_unregister_nf_hook(struct net *net) { - WARN_ON(!ipvl_nf_hook_refcnt); + struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid); + + if (WARN_ON(!vnet->ipvl_nf_hook_refcnt)) + return; - ipvl_nf_hook_refcnt--; - if (!ipvl_nf_hook_refcnt) - _nf_unregister_hooks(ipvl_nfops, ARRAY_SIZE(ipvl_nfops)); + vnet->ipvl_nf_hook_refcnt--; + if (!vnet->ipvl_nf_hook_refcnt) + nf_unregister_net_hooks(net, ipvl_nfops, + ARRAY_SIZE(ipvl_nfops)); } static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval) @@ -69,7 +79,7 @@ static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval) if (port->mode != nval) { if (nval == IPVLAN_MODE_L3S) { /* New mode is L3S */ - err = ipvlan_register_nf_hook(); + err = ipvlan_register_nf_hook(read_pnet(&port->pnet)); if (!err) { mdev->l3mdev_ops = &ipvl_l3mdev_ops; mdev->priv_flags |= IFF_L3MDEV_MASTER; @@ -78,7 +88,7 @@ static int ipvlan_set_port_mode(struct ipvl_port *port, u16 nval) } else if (port->mode == IPVLAN_MODE_L3S) { /* Old mode was L3S */ mdev->priv_flags &= ~IFF_L3MDEV_MASTER; - ipvlan_unregister_nf_hook(); + ipvlan_unregister_nf_hook(read_pnet(&port->pnet)); mdev->l3mdev_ops = NULL; } list_for_each_entry(ipvlan, &port->ipvlans, pnode) { @@ -111,6 +121,7 @@ static int ipvlan_port_create(struct net_device *dev) if (!port) return -ENOMEM; + write_pnet(&port->pnet, dev_net(dev)); port->dev = dev; port->mode = IPVLAN_MODE_L3; INIT_LIST_HEAD(&port->ipvlans); @@ -142,7 +153,7 @@ static void ipvlan_port_destroy(struct net_device *dev) dev->priv_flags &= ~IFF_IPVLAN_MASTER; if (port->mode == IPVLAN_MODE_L3S) { dev->priv_flags &= ~IFF_L3MDEV_MASTER; - ipvlan_unregister_nf_hook(); + ipvlan_unregister_nf_hook(dev_net(dev)); dev->l3mdev_ops = NULL; } netdev_rx_handler_unregister(dev); @@ -673,6 +684,24 @@ static int ipvlan_device_event(struct notifier_block *unused, ipvlan->dev); break; + case NETDEV_REGISTER: { + struct net *oldnet, *newnet = dev_net(dev); + struct ipvlan_netns *old_vnet; + + oldnet = read_pnet(&port->pnet); + if (net_eq(newnet, oldnet)) + break; + + write_pnet(&port->pnet, newnet); + + old_vnet = net_generic(oldnet, ipvlan_netid); + if (!old_vnet->ipvl_nf_hook_refcnt) + break; + + ipvlan_register_nf_hook(newnet); + ipvlan_unregister_nf_hook(oldnet); + break; + } case NETDEV_UNREGISTER: if (dev->reg_state != NETREG_UNREGISTERING) break; @@ -854,6 +883,23 @@ static struct notifier_block ipvlan_addr6_notifier_block __read_mostly = { .notifier_call = ipvlan_addr6_event, }; +static void ipvlan_ns_exit(struct net *net) +{ + struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid); + + if (WARN_ON_ONCE(vnet->ipvl_nf_hook_refcnt)) { + vnet->ipvl_nf_hook_refcnt = 0; + nf_unregister_net_hooks(net, ipvl_nfops, + ARRAY_SIZE(ipvl_nfops)); + } +} + +static struct pernet_operations ipvlan_net_ops = { + .id = &ipvlan_netid, + .size = sizeof(struct ipvlan_netns), + .exit = ipvlan_ns_exit, +}; + static int __init ipvlan_init_module(void) { int err; @@ -863,10 +909,16 @@ static int __init ipvlan_init_module(void) register_inet6addr_notifier(&ipvlan_addr6_notifier_block); register_inetaddr_notifier(&ipvlan_addr4_notifier_block); - err = ipvlan_link_register(&ipvlan_link_ops); + err = register_pernet_subsys(&ipvlan_net_ops); if (err < 0) goto error; + err = ipvlan_link_register(&ipvlan_link_ops); + if (err < 0) { + unregister_pernet_subsys(&ipvlan_net_ops); + goto error; + } + return 0; error: unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block); @@ -878,6 +930,7 @@ error: static void __exit ipvlan_cleanup_module(void) { rtnl_link_unregister(&ipvlan_link_ops); + unregister_pernet_subsys(&ipvlan_net_ops); unregister_netdevice_notifier(&ipvlan_notifier_block); unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block); unregister_inet6addr_notifier(&ipvlan_addr6_notifier_block); -- cgit From e0ee84ded79623ac063a4e817367d61ab7650b6c Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sun, 23 Apr 2017 13:17:28 -0400 Subject: net sched actions: Complete the JUMPX opcode per discussion at netconf/netdev: When we have an action that is capable of branching (example a policer), we can achieve a continuation of the action graph by programming a "continue" where we find an exact replica of the same filter rule with a lower priority and the remainder of the action graph. When you have 100s of thousands of filters which require such a feature it gets very inefficient to do two lookups. This patch completes a leftover feature of action codes. Its time has come. Example below where a user labels packets with a different skbmark on ingress of a port depending on whether they have/not exceeded the configured rate. This mark is then used to make further decisions on some egress port. #rate control, very low so we can easily see the effect sudo $TC actions add action police rate 1kbit burst 90k \ conform-exceed pipe/jump 2 index 10 # skbedit index 11 will be used if the user conforms sudo $TC actions add action skbedit mark 11 ok index 11 # skbedit index 12 will be used if the user does not conform sudo $TC actions add action skbedit mark 12 ok index 12 #lets bind the user .. sudo $TC filter add dev $ETH parent ffff: protocol ip prio 8 u32 \ match ip dst 127.0.0.8/32 flowid 1:10 \ action police index 10 \ action skbedit index 11 \ action skbedit index 12 #run a ping -f and see what happens.. # jhs@foobar:~$ sudo $TC -s filter ls dev $ETH parent ffff: protocol ip filter pref 8 u32 filter pref 8 u32 fh 800: ht divisor 1 filter pref 8 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:10 (rule hit 2800 success 1005) match 7f000008/ffffffff at 16 (success 1005 ) action order 1: police 0xa rate 1Kbit burst 23440b mtu 2Kb action pipe/jump 2 overhead 0b ref 2 bind 1 installed 207 sec used 122 sec Action statistics: Sent 84420 bytes 1005 pkt (dropped 0, overlimits 721 requeues 0) backlog 0b 0p requeues 0 action order 2: skbedit mark 11 pass index 11 ref 2 bind 1 installed 204 sec used 122 sec Action statistics: Sent 60564 bytes 721 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 action order 3: skbedit mark 12 pass index 12 ref 2 bind 1 installed 201 sec used 122 sec Action statistics: Sent 23856 bytes 284 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 Not bad, about 28% non-conforming packets.. Signed-off-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- net/sched/act_api.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 257360f773b4..7f2cd702bb27 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -428,24 +428,49 @@ static struct tc_action_ops *tc_lookup_action(struct nlattr *kind) return res; } +/*TCA_ACT_MAX_PRIO is 32, there count upto 32 */ +#define TCA_ACT_MAX_PRIO_MASK 0x1FF int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions, int nr_actions, struct tcf_result *res) { int ret = -1, i; + u32 jmp_prgcnt = 0; + u32 jmp_ttl = TCA_ACT_MAX_PRIO; /*matches actions per filter */ if (skb_skip_tc_classify(skb)) return TC_ACT_OK; +restart_act_graph: for (i = 0; i < nr_actions; i++) { const struct tc_action *a = actions[i]; + if (jmp_prgcnt > 0) { + jmp_prgcnt -= 1; + continue; + } repeat: ret = a->ops->act(skb, a, res); if (ret == TC_ACT_REPEAT) goto repeat; /* we need a ttl - JHS */ + + if (ret & TC_ACT_JUMP) { + jmp_prgcnt = ret & TCA_ACT_MAX_PRIO_MASK; + if (!jmp_prgcnt || (jmp_prgcnt > nr_actions)) { + /* faulty opcode, stop pipeline */ + return TC_ACT_OK; + } else { + jmp_ttl -= 1; + if (jmp_ttl > 0) + goto restart_act_graph; + else /* faulty graph, stop pipeline */ + return TC_ACT_OK; + } + } + if (ret != TC_ACT_PIPE) break; } + return ret; } EXPORT_SYMBOL(tcf_action_exec); -- cgit From c39855febc0e7332db389fd4017ab163589b1b20 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Mon, 24 Apr 2017 14:16:06 +0200 Subject: l2tp: set name_assign_type for devices created by l2tp_eth.c Export naming scheme used when creating l2tpeth interfaces (/sys/class/net//name_assign_type). This let userspace know if the device's name has been generated automatically or defined manually. Signed-off-by: Guillaume Nault Acked-by: James Chapman Signed-off-by: David S. Miller --- net/l2tp/l2tp_eth.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index b722d559c544..5e44b3cc1212 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -258,6 +258,7 @@ static void l2tp_eth_adjust_mtu(struct l2tp_tunnel *tunnel, static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) { + unsigned char name_assign_type; struct net_device *dev; char name[IFNAMSIZ]; struct l2tp_tunnel *tunnel; @@ -281,8 +282,11 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p goto out; } strlcpy(name, cfg->ifname, IFNAMSIZ); - } else + name_assign_type = NET_NAME_USER; + } else { strcpy(name, L2TP_ETH_DEV_NAME); + name_assign_type = NET_NAME_ENUM; + } session = l2tp_session_create(sizeof(*spriv), tunnel, session_id, peer_session_id, cfg); @@ -291,7 +295,7 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p goto out; } - dev = alloc_netdev(sizeof(*priv), name, NET_NAME_UNKNOWN, + dev = alloc_netdev(sizeof(*priv), name, name_assign_type, l2tp_eth_dev_setup); if (!dev) { rc = -ENOMEM; -- cgit From a485c2b877619935f7346146791f0d22f5da723a Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Mon, 24 Apr 2017 14:16:07 +0200 Subject: l2tp: define "l2tpeth" device type Export type of l2tpeth interfaces to userspace (/sys/class/net//uevent). Signed-off-by: Guillaume Nault Acked-by: James Chapman Signed-off-by: David S. Miller --- net/l2tp/l2tp_eth.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index 5e44b3cc1212..59aba8aeac03 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -130,8 +130,13 @@ static const struct net_device_ops l2tp_eth_netdev_ops = { .ndo_set_mac_address = eth_mac_addr, }; +static struct device_type l2tpeth_type = { + .name = "l2tpeth", +}; + static void l2tp_eth_dev_setup(struct net_device *dev) { + SET_NETDEV_DEVTYPE(dev, &l2tpeth_type); ether_setup(dev); dev->priv_flags &= ~IFF_TX_SKB_SHARING; dev->features |= NETIF_F_LLTX; -- cgit From 1996843012629825e4a2c339fedef1f7eade87bc Mon Sep 17 00:00:00 2001 From: "Chopra, Manish" Date: Mon, 24 Apr 2017 10:00:44 -0700 Subject: qed: refactor tunnelling - API/Structs This patch changes the tunnel APIs to use per tunnel info instead of using bitmasks for all tunnels and also uses single struct to hold the data to prepare multiple variant of tunnel configuration ramrods to be sent to the hardware. Signed-off-by: Manish Chopra Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 30 +- drivers/net/ethernet/qlogic/qed/qed_dev.c | 2 +- drivers/net/ethernet/qlogic/qed/qed_dev_api.h | 2 +- drivers/net/ethernet/qlogic/qed/qed_l2.c | 15 +- drivers/net/ethernet/qlogic/qed/qed_main.c | 24 +- drivers/net/ethernet/qlogic/qed/qed_sp.h | 4 +- drivers/net/ethernet/qlogic/qed/qed_sp_commands.c | 327 +++++++++++----------- 7 files changed, 207 insertions(+), 197 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index c539ba138db9..8b7b1dd25f1d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -149,9 +149,35 @@ enum qed_tunn_clss { QED_TUNN_CLSS_MAC_VNI, QED_TUNN_CLSS_INNER_MAC_VLAN, QED_TUNN_CLSS_INNER_MAC_VNI, + QED_TUNN_CLSS_MAC_VLAN_DUAL_STAGE, MAX_QED_TUNN_CLSS, }; +struct qed_tunn_update_type { + bool b_update_mode; + bool b_mode_enabled; + enum qed_tunn_clss tun_cls; +}; + +struct qed_tunn_update_udp_port { + bool b_update_port; + u16 port; +}; + +struct qed_tunnel_info { + struct qed_tunn_update_type vxlan; + struct qed_tunn_update_type l2_geneve; + struct qed_tunn_update_type ip_geneve; + struct qed_tunn_update_type l2_gre; + struct qed_tunn_update_type ip_gre; + + struct qed_tunn_update_udp_port vxlan_port; + struct qed_tunn_update_udp_port geneve_port; + + bool b_update_rx_cls; + bool b_update_tx_cls; +}; + struct qed_tunn_start_params { unsigned long tunn_mode; u16 vxlan_udp_port; @@ -648,9 +674,7 @@ struct qed_dev { /* SRIOV */ struct qed_hw_sriov_info *p_iov_info; #define IS_QED_SRIOV(cdev) (!!(cdev)->p_iov_info) - - unsigned long tunn_mode; - + struct qed_tunnel_info tunnel; bool b_is_vf; u32 drv_type; struct qed_eth_stats *reset_stats; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 6d2430896c5a..13817ccf2e58 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1453,7 +1453,7 @@ static int qed_hw_init_port(struct qed_hwfn *p_hwfn, static int qed_hw_init_pf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - struct qed_tunn_start_params *p_tunn, + struct qed_tunnel_info *p_tunn, int hw_mode, bool b_hw_start, enum qed_int_mode int_mode, diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h index 341636da9964..cefe3ee9064a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h +++ b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h @@ -113,7 +113,7 @@ struct qed_drv_load_params { struct qed_hw_init_params { /* Tunneling parameters */ - struct qed_tunn_start_params *p_tunn; + struct qed_tunnel_info *p_tunn; bool b_hw_start; diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index eb5e280eb104..03216babb06f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -2285,21 +2285,21 @@ static int qed_stop_txq(struct qed_dev *cdev, u8 rss_id, void *handle) static int qed_tunn_configure(struct qed_dev *cdev, struct qed_tunn_params *tunn_params) { - struct qed_tunn_update_params tunn_info; + struct qed_tunnel_info tunn_info; int i, rc; if (IS_VF(cdev)) return 0; memset(&tunn_info, 0, sizeof(tunn_info)); - if (tunn_params->update_vxlan_port == 1) { - tunn_info.update_vxlan_udp_port = 1; - tunn_info.vxlan_udp_port = tunn_params->vxlan_port; + if (tunn_params->update_vxlan_port) { + tunn_info.vxlan_port.b_update_port = true; + tunn_info.vxlan_port.port = tunn_params->vxlan_port; } - if (tunn_params->update_geneve_port == 1) { - tunn_info.update_geneve_udp_port = 1; - tunn_info.geneve_udp_port = tunn_params->geneve_port; + if (tunn_params->update_geneve_port) { + tunn_info.geneve_port.b_update_port = true; + tunn_info.geneve_port.port = tunn_params->geneve_port; } for_each_hwfn(cdev, i) { @@ -2307,7 +2307,6 @@ static int qed_tunn_configure(struct qed_dev *cdev, rc = qed_sp_pf_update_tunn_cfg(hwfn, &tunn_info, QED_SPQ_MODE_EBLOCK, NULL); - if (rc) return rc; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index da562cf8a965..a622d75e2547 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -909,8 +909,8 @@ static int qed_slowpath_start(struct qed_dev *cdev, { struct qed_drv_load_params drv_load_params; struct qed_hw_init_params hw_init_params; - struct qed_tunn_start_params tunn_info; struct qed_mcp_drv_version drv_version; + struct qed_tunnel_info tunn_info; const u8 *data = NULL; struct qed_hwfn *hwfn; struct qed_ptt *p_ptt; @@ -974,19 +974,19 @@ static int qed_slowpath_start(struct qed_dev *cdev, qed_dbg_pf_init(cdev); } - memset(&tunn_info, 0, sizeof(tunn_info)); - tunn_info.tunn_mode |= 1 << QED_MODE_VXLAN_TUNN | - 1 << QED_MODE_L2GRE_TUNN | - 1 << QED_MODE_IPGRE_TUNN | - 1 << QED_MODE_L2GENEVE_TUNN | - 1 << QED_MODE_IPGENEVE_TUNN; - - tunn_info.tunn_clss_vxlan = QED_TUNN_CLSS_MAC_VLAN; - tunn_info.tunn_clss_l2gre = QED_TUNN_CLSS_MAC_VLAN; - tunn_info.tunn_clss_ipgre = QED_TUNN_CLSS_MAC_VLAN; - /* Start the slowpath */ memset(&hw_init_params, 0, sizeof(hw_init_params)); + memset(&tunn_info, 0, sizeof(tunn_info)); + tunn_info.vxlan.b_mode_enabled = true; + tunn_info.l2_gre.b_mode_enabled = true; + tunn_info.ip_gre.b_mode_enabled = true; + tunn_info.l2_geneve.b_mode_enabled = true; + tunn_info.ip_geneve.b_mode_enabled = true; + tunn_info.vxlan.tun_cls = QED_TUNN_CLSS_MAC_VLAN; + tunn_info.l2_gre.tun_cls = QED_TUNN_CLSS_MAC_VLAN; + tunn_info.ip_gre.tun_cls = QED_TUNN_CLSS_MAC_VLAN; + tunn_info.l2_geneve.tun_cls = QED_TUNN_CLSS_MAC_VLAN; + tunn_info.ip_geneve.tun_cls = QED_TUNN_CLSS_MAC_VLAN; hw_init_params.p_tunn = &tunn_info; hw_init_params.b_hw_start = true; hw_init_params.int_mode = cdev->int_params.out.int_mode; diff --git a/drivers/net/ethernet/qlogic/qed/qed_sp.h b/drivers/net/ethernet/qlogic/qed/qed_sp.h index 583c8d38c8d7..3357bbefa445 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_sp.h @@ -409,7 +409,7 @@ int qed_sp_init_request(struct qed_hwfn *p_hwfn, */ int qed_sp_pf_start(struct qed_hwfn *p_hwfn, - struct qed_tunn_start_params *p_tunn, + struct qed_tunnel_info *p_tunn, enum qed_mf_mode mode, bool allow_npar_tx_switch); /** @@ -442,7 +442,7 @@ int qed_sp_pf_update(struct qed_hwfn *p_hwfn); int qed_sp_pf_stop(struct qed_hwfn *p_hwfn); int qed_sp_pf_update_tunn_cfg(struct qed_hwfn *p_hwfn, - struct qed_tunn_update_params *p_tunn, + struct qed_tunnel_info *p_tunn, enum spq_mode comp_mode, struct qed_spq_comp_cb *p_comp_data); /** diff --git a/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c b/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c index 6fb80f9ef446..96c6fda430dc 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c @@ -111,7 +111,7 @@ int qed_sp_init_request(struct qed_hwfn *p_hwfn, return 0; } -static enum tunnel_clss qed_tunn_get_clss_type(u8 type) +static enum tunnel_clss qed_tunn_clss_to_fw_clss(u8 type) { switch (type) { case QED_TUNN_CLSS_MAC_VLAN: @@ -122,206 +122,201 @@ static enum tunnel_clss qed_tunn_get_clss_type(u8 type) return TUNNEL_CLSS_INNER_MAC_VLAN; case QED_TUNN_CLSS_INNER_MAC_VNI: return TUNNEL_CLSS_INNER_MAC_VNI; + case QED_TUNN_CLSS_MAC_VLAN_DUAL_STAGE: + return TUNNEL_CLSS_MAC_VLAN_DUAL_STAGE; default: return TUNNEL_CLSS_MAC_VLAN; } } static void -qed_tunn_set_pf_fix_tunn_mode(struct qed_hwfn *p_hwfn, - struct qed_tunn_update_params *p_src, - struct pf_update_tunnel_config *p_tunn_cfg) +qed_set_pf_update_tunn_mode(struct qed_tunnel_info *p_tun, + struct qed_tunnel_info *p_src, bool b_pf_start) { - unsigned long cached_tunn_mode = p_hwfn->cdev->tunn_mode; - unsigned long update_mask = p_src->tunn_mode_update_mask; - unsigned long tunn_mode = p_src->tunn_mode; - unsigned long new_tunn_mode = 0; - - if (test_bit(QED_MODE_L2GRE_TUNN, &update_mask)) { - if (test_bit(QED_MODE_L2GRE_TUNN, &tunn_mode)) - __set_bit(QED_MODE_L2GRE_TUNN, &new_tunn_mode); - } else { - if (test_bit(QED_MODE_L2GRE_TUNN, &cached_tunn_mode)) - __set_bit(QED_MODE_L2GRE_TUNN, &new_tunn_mode); - } - - if (test_bit(QED_MODE_IPGRE_TUNN, &update_mask)) { - if (test_bit(QED_MODE_IPGRE_TUNN, &tunn_mode)) - __set_bit(QED_MODE_IPGRE_TUNN, &new_tunn_mode); - } else { - if (test_bit(QED_MODE_IPGRE_TUNN, &cached_tunn_mode)) - __set_bit(QED_MODE_IPGRE_TUNN, &new_tunn_mode); - } + if (p_src->vxlan.b_update_mode || b_pf_start) + p_tun->vxlan.b_mode_enabled = p_src->vxlan.b_mode_enabled; - if (test_bit(QED_MODE_VXLAN_TUNN, &update_mask)) { - if (test_bit(QED_MODE_VXLAN_TUNN, &tunn_mode)) - __set_bit(QED_MODE_VXLAN_TUNN, &new_tunn_mode); - } else { - if (test_bit(QED_MODE_VXLAN_TUNN, &cached_tunn_mode)) - __set_bit(QED_MODE_VXLAN_TUNN, &new_tunn_mode); - } - - if (p_src->update_geneve_udp_port) { - p_tunn_cfg->set_geneve_udp_port_flg = 1; - p_tunn_cfg->geneve_udp_port = - cpu_to_le16(p_src->geneve_udp_port); - } + if (p_src->l2_gre.b_update_mode || b_pf_start) + p_tun->l2_gre.b_mode_enabled = p_src->l2_gre.b_mode_enabled; - if (test_bit(QED_MODE_L2GENEVE_TUNN, &update_mask)) { - if (test_bit(QED_MODE_L2GENEVE_TUNN, &tunn_mode)) - __set_bit(QED_MODE_L2GENEVE_TUNN, &new_tunn_mode); - } else { - if (test_bit(QED_MODE_L2GENEVE_TUNN, &cached_tunn_mode)) - __set_bit(QED_MODE_L2GENEVE_TUNN, &new_tunn_mode); - } + if (p_src->ip_gre.b_update_mode || b_pf_start) + p_tun->ip_gre.b_mode_enabled = p_src->ip_gre.b_mode_enabled; - if (test_bit(QED_MODE_IPGENEVE_TUNN, &update_mask)) { - if (test_bit(QED_MODE_IPGENEVE_TUNN, &tunn_mode)) - __set_bit(QED_MODE_IPGENEVE_TUNN, &new_tunn_mode); - } else { - if (test_bit(QED_MODE_IPGENEVE_TUNN, &cached_tunn_mode)) - __set_bit(QED_MODE_IPGENEVE_TUNN, &new_tunn_mode); - } + if (p_src->l2_geneve.b_update_mode || b_pf_start) + p_tun->l2_geneve.b_mode_enabled = + p_src->l2_geneve.b_mode_enabled; - p_src->tunn_mode = new_tunn_mode; + if (p_src->ip_geneve.b_update_mode || b_pf_start) + p_tun->ip_geneve.b_mode_enabled = + p_src->ip_geneve.b_mode_enabled; } -static void -qed_tunn_set_pf_update_params(struct qed_hwfn *p_hwfn, - struct qed_tunn_update_params *p_src, - struct pf_update_tunnel_config *p_tunn_cfg) +static void qed_set_tunn_cls_info(struct qed_tunnel_info *p_tun, + struct qed_tunnel_info *p_src) { - unsigned long tunn_mode = p_src->tunn_mode; enum tunnel_clss type; - qed_tunn_set_pf_fix_tunn_mode(p_hwfn, p_src, p_tunn_cfg); - p_tunn_cfg->update_rx_pf_clss = p_src->update_rx_pf_clss; - p_tunn_cfg->update_tx_pf_clss = p_src->update_tx_pf_clss; - - type = qed_tunn_get_clss_type(p_src->tunn_clss_vxlan); - p_tunn_cfg->tunnel_clss_vxlan = type; - - type = qed_tunn_get_clss_type(p_src->tunn_clss_l2gre); - p_tunn_cfg->tunnel_clss_l2gre = type; + p_tun->b_update_rx_cls = p_src->b_update_rx_cls; + p_tun->b_update_tx_cls = p_src->b_update_tx_cls; + + type = qed_tunn_clss_to_fw_clss(p_src->vxlan.tun_cls); + p_tun->vxlan.tun_cls = type; + type = qed_tunn_clss_to_fw_clss(p_src->l2_gre.tun_cls); + p_tun->l2_gre.tun_cls = type; + type = qed_tunn_clss_to_fw_clss(p_src->ip_gre.tun_cls); + p_tun->ip_gre.tun_cls = type; + type = qed_tunn_clss_to_fw_clss(p_src->l2_geneve.tun_cls); + p_tun->l2_geneve.tun_cls = type; + type = qed_tunn_clss_to_fw_clss(p_src->ip_geneve.tun_cls); + p_tun->ip_geneve.tun_cls = type; +} - type = qed_tunn_get_clss_type(p_src->tunn_clss_ipgre); - p_tunn_cfg->tunnel_clss_ipgre = type; +static void qed_set_tunn_ports(struct qed_tunnel_info *p_tun, + struct qed_tunnel_info *p_src) +{ + p_tun->geneve_port.b_update_port = p_src->geneve_port.b_update_port; + p_tun->vxlan_port.b_update_port = p_src->vxlan_port.b_update_port; - if (p_src->update_vxlan_udp_port) { - p_tunn_cfg->set_vxlan_udp_port_flg = 1; - p_tunn_cfg->vxlan_udp_port = cpu_to_le16(p_src->vxlan_udp_port); - } + if (p_src->geneve_port.b_update_port) + p_tun->geneve_port.port = p_src->geneve_port.port; - if (test_bit(QED_MODE_L2GRE_TUNN, &tunn_mode)) - p_tunn_cfg->tx_enable_l2gre = 1; + if (p_src->vxlan_port.b_update_port) + p_tun->vxlan_port.port = p_src->vxlan_port.port; +} - if (test_bit(QED_MODE_IPGRE_TUNN, &tunn_mode)) - p_tunn_cfg->tx_enable_ipgre = 1; +static void +__qed_set_ramrod_tunnel_param(u8 *p_tunn_cls, u8 *p_enable_tx_clas, + struct qed_tunn_update_type *tun_type) +{ + *p_tunn_cls = tun_type->tun_cls; - if (test_bit(QED_MODE_VXLAN_TUNN, &tunn_mode)) - p_tunn_cfg->tx_enable_vxlan = 1; + if (tun_type->b_mode_enabled) + *p_enable_tx_clas = 1; +} - if (p_src->update_geneve_udp_port) { - p_tunn_cfg->set_geneve_udp_port_flg = 1; - p_tunn_cfg->geneve_udp_port = - cpu_to_le16(p_src->geneve_udp_port); +static void +qed_set_ramrod_tunnel_param(u8 *p_tunn_cls, u8 *p_enable_tx_clas, + struct qed_tunn_update_type *tun_type, + u8 *p_update_port, __le16 *p_port, + struct qed_tunn_update_udp_port *p_udp_port) +{ + __qed_set_ramrod_tunnel_param(p_tunn_cls, p_enable_tx_clas, tun_type); + if (p_udp_port->b_update_port) { + *p_update_port = 1; + *p_port = cpu_to_le16(p_udp_port->port); } +} - if (test_bit(QED_MODE_L2GENEVE_TUNN, &tunn_mode)) - p_tunn_cfg->tx_enable_l2geneve = 1; - - if (test_bit(QED_MODE_IPGENEVE_TUNN, &tunn_mode)) - p_tunn_cfg->tx_enable_ipgeneve = 1; - - type = qed_tunn_get_clss_type(p_src->tunn_clss_l2geneve); - p_tunn_cfg->tunnel_clss_l2geneve = type; - - type = qed_tunn_get_clss_type(p_src->tunn_clss_ipgeneve); - p_tunn_cfg->tunnel_clss_ipgeneve = type; +static void +qed_tunn_set_pf_update_params(struct qed_hwfn *p_hwfn, + struct qed_tunnel_info *p_src, + struct pf_update_tunnel_config *p_tunn_cfg) +{ + struct qed_tunnel_info *p_tun = &p_hwfn->cdev->tunnel; + + qed_set_pf_update_tunn_mode(p_tun, p_src, false); + qed_set_tunn_cls_info(p_tun, p_src); + qed_set_tunn_ports(p_tun, p_src); + + qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_vxlan, + &p_tunn_cfg->tx_enable_vxlan, + &p_tun->vxlan, + &p_tunn_cfg->set_vxlan_udp_port_flg, + &p_tunn_cfg->vxlan_udp_port, + &p_tun->vxlan_port); + + qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_l2geneve, + &p_tunn_cfg->tx_enable_l2geneve, + &p_tun->l2_geneve, + &p_tunn_cfg->set_geneve_udp_port_flg, + &p_tunn_cfg->geneve_udp_port, + &p_tun->geneve_port); + + __qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_ipgeneve, + &p_tunn_cfg->tx_enable_ipgeneve, + &p_tun->ip_geneve); + + __qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_l2gre, + &p_tunn_cfg->tx_enable_l2gre, + &p_tun->l2_gre); + + __qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_ipgre, + &p_tunn_cfg->tx_enable_ipgre, + &p_tun->ip_gre); + + p_tunn_cfg->update_rx_pf_clss = p_tun->b_update_rx_cls; + p_tunn_cfg->update_tx_pf_clss = p_tun->b_update_tx_cls; } static void qed_set_hw_tunn_mode(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - unsigned long tunn_mode) + struct qed_tunnel_info *p_tun) { - u8 l2gre_enable = 0, ipgre_enable = 0, vxlan_enable = 0; - u8 l2geneve_enable = 0, ipgeneve_enable = 0; - - if (test_bit(QED_MODE_L2GRE_TUNN, &tunn_mode)) - l2gre_enable = 1; - - if (test_bit(QED_MODE_IPGRE_TUNN, &tunn_mode)) - ipgre_enable = 1; - - if (test_bit(QED_MODE_VXLAN_TUNN, &tunn_mode)) - vxlan_enable = 1; + qed_set_gre_enable(p_hwfn, p_ptt, p_tun->l2_gre.b_mode_enabled, + p_tun->ip_gre.b_mode_enabled); + qed_set_vxlan_enable(p_hwfn, p_ptt, p_tun->vxlan.b_mode_enabled); - qed_set_gre_enable(p_hwfn, p_ptt, l2gre_enable, ipgre_enable); - qed_set_vxlan_enable(p_hwfn, p_ptt, vxlan_enable); + qed_set_geneve_enable(p_hwfn, p_ptt, p_tun->l2_geneve.b_mode_enabled, + p_tun->ip_geneve.b_mode_enabled); +} - if (test_bit(QED_MODE_L2GENEVE_TUNN, &tunn_mode)) - l2geneve_enable = 1; +static void qed_set_hw_tunn_mode_port(struct qed_hwfn *p_hwfn, + struct qed_tunnel_info *p_tunn) +{ + if (p_tunn->vxlan_port.b_update_port) + qed_set_vxlan_dest_port(p_hwfn, p_hwfn->p_main_ptt, + p_tunn->vxlan_port.port); - if (test_bit(QED_MODE_IPGENEVE_TUNN, &tunn_mode)) - ipgeneve_enable = 1; + if (p_tunn->geneve_port.b_update_port) + qed_set_geneve_dest_port(p_hwfn, p_hwfn->p_main_ptt, + p_tunn->geneve_port.port); - qed_set_geneve_enable(p_hwfn, p_ptt, l2geneve_enable, - ipgeneve_enable); + qed_set_hw_tunn_mode(p_hwfn, p_hwfn->p_main_ptt, p_tunn); } static void qed_tunn_set_pf_start_params(struct qed_hwfn *p_hwfn, - struct qed_tunn_start_params *p_src, + struct qed_tunnel_info *p_src, struct pf_start_tunnel_config *p_tunn_cfg) { - unsigned long tunn_mode; - enum tunnel_clss type; + struct qed_tunnel_info *p_tun = &p_hwfn->cdev->tunnel; if (!p_src) return; - tunn_mode = p_src->tunn_mode; - type = qed_tunn_get_clss_type(p_src->tunn_clss_vxlan); - p_tunn_cfg->tunnel_clss_vxlan = type; - type = qed_tunn_get_clss_type(p_src->tunn_clss_l2gre); - p_tunn_cfg->tunnel_clss_l2gre = type; - type = qed_tunn_get_clss_type(p_src->tunn_clss_ipgre); - p_tunn_cfg->tunnel_clss_ipgre = type; - - if (p_src->update_vxlan_udp_port) { - p_tunn_cfg->set_vxlan_udp_port_flg = 1; - p_tunn_cfg->vxlan_udp_port = cpu_to_le16(p_src->vxlan_udp_port); - } - - if (test_bit(QED_MODE_L2GRE_TUNN, &tunn_mode)) - p_tunn_cfg->tx_enable_l2gre = 1; - - if (test_bit(QED_MODE_IPGRE_TUNN, &tunn_mode)) - p_tunn_cfg->tx_enable_ipgre = 1; - - if (test_bit(QED_MODE_VXLAN_TUNN, &tunn_mode)) - p_tunn_cfg->tx_enable_vxlan = 1; - - if (p_src->update_geneve_udp_port) { - p_tunn_cfg->set_geneve_udp_port_flg = 1; - p_tunn_cfg->geneve_udp_port = - cpu_to_le16(p_src->geneve_udp_port); - } - - if (test_bit(QED_MODE_L2GENEVE_TUNN, &tunn_mode)) - p_tunn_cfg->tx_enable_l2geneve = 1; - - if (test_bit(QED_MODE_IPGENEVE_TUNN, &tunn_mode)) - p_tunn_cfg->tx_enable_ipgeneve = 1; - - type = qed_tunn_get_clss_type(p_src->tunn_clss_l2geneve); - p_tunn_cfg->tunnel_clss_l2geneve = type; - type = qed_tunn_get_clss_type(p_src->tunn_clss_ipgeneve); - p_tunn_cfg->tunnel_clss_ipgeneve = type; + qed_set_pf_update_tunn_mode(p_tun, p_src, true); + qed_set_tunn_cls_info(p_tun, p_src); + qed_set_tunn_ports(p_tun, p_src); + + qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_vxlan, + &p_tunn_cfg->tx_enable_vxlan, + &p_tun->vxlan, + &p_tunn_cfg->set_vxlan_udp_port_flg, + &p_tunn_cfg->vxlan_udp_port, + &p_tun->vxlan_port); + + qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_l2geneve, + &p_tunn_cfg->tx_enable_l2geneve, + &p_tun->l2_geneve, + &p_tunn_cfg->set_geneve_udp_port_flg, + &p_tunn_cfg->geneve_udp_port, + &p_tun->geneve_port); + + __qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_ipgeneve, + &p_tunn_cfg->tx_enable_ipgeneve, + &p_tun->ip_geneve); + + __qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_l2gre, + &p_tunn_cfg->tx_enable_l2gre, + &p_tun->l2_gre); + + __qed_set_ramrod_tunnel_param(&p_tunn_cfg->tunnel_clss_ipgre, + &p_tunn_cfg->tx_enable_ipgre, + &p_tun->ip_gre); } int qed_sp_pf_start(struct qed_hwfn *p_hwfn, - struct qed_tunn_start_params *p_tunn, + struct qed_tunnel_info *p_tunn, enum qed_mf_mode mode, bool allow_npar_tx_switch) { struct pf_start_ramrod_data *p_ramrod = NULL; @@ -416,11 +411,8 @@ int qed_sp_pf_start(struct qed_hwfn *p_hwfn, rc = qed_spq_post(p_hwfn, p_ent, NULL); - if (p_tunn) { - qed_set_hw_tunn_mode(p_hwfn, p_hwfn->p_main_ptt, - p_tunn->tunn_mode); - p_hwfn->cdev->tunn_mode = p_tunn->tunn_mode; - } + if (p_tunn) + qed_set_hw_tunn_mode_port(p_hwfn, &p_hwfn->cdev->tunnel); return rc; } @@ -451,7 +443,7 @@ int qed_sp_pf_update(struct qed_hwfn *p_hwfn) /* Set pf update ramrod command params */ int qed_sp_pf_update_tunn_cfg(struct qed_hwfn *p_hwfn, - struct qed_tunn_update_params *p_tunn, + struct qed_tunnel_info *p_tunn, enum spq_mode comp_mode, struct qed_spq_comp_cb *p_comp_data) { @@ -459,6 +451,9 @@ int qed_sp_pf_update_tunn_cfg(struct qed_hwfn *p_hwfn, struct qed_sp_init_data init_data; int rc = -EINVAL; + if (!p_tunn) + return -EINVAL; + /* Get SPQ entry */ memset(&init_data, 0, sizeof(init_data)); init_data.cid = qed_spq_get_cid(p_hwfn); @@ -479,15 +474,7 @@ int qed_sp_pf_update_tunn_cfg(struct qed_hwfn *p_hwfn, if (rc) return rc; - if (p_tunn->update_vxlan_udp_port) - qed_set_vxlan_dest_port(p_hwfn, p_hwfn->p_main_ptt, - p_tunn->vxlan_udp_port); - if (p_tunn->update_geneve_udp_port) - qed_set_geneve_dest_port(p_hwfn, p_hwfn->p_main_ptt, - p_tunn->geneve_udp_port); - - qed_set_hw_tunn_mode(p_hwfn, p_hwfn->p_main_ptt, p_tunn->tunn_mode); - p_hwfn->cdev->tunn_mode = p_tunn->tunn_mode; + qed_set_hw_tunn_mode_port(p_hwfn, &p_hwfn->cdev->tunnel); return rc; } -- cgit From 19489c7f0d9040ed2ffc23747e14af95dba479d2 Mon Sep 17 00:00:00 2001 From: "Chopra, Manish" Date: Mon, 24 Apr 2017 10:00:45 -0700 Subject: qed/qede: Enable tunnel offloads based on hw configuration This patch enables tunnel feature offloads based on hw configuration at initialization time instead of enabling them always. Signed-off-by: Manish Chopra Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_main.c | 15 +++++++++++ drivers/net/ethernet/qlogic/qede/qede_filter.c | 6 +++++ drivers/net/ethernet/qlogic/qede/qede_main.c | 36 ++++++++++++++++++-------- include/linux/qed/qed_if.h | 5 ++++ 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index a622d75e2547..e6502810ccd7 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -230,10 +230,25 @@ err0: int qed_fill_dev_info(struct qed_dev *cdev, struct qed_dev_info *dev_info) { + struct qed_tunnel_info *tun = &cdev->tunnel; struct qed_ptt *ptt; memset(dev_info, 0, sizeof(struct qed_dev_info)); + if (tun->vxlan.tun_cls == QED_TUNN_CLSS_MAC_VLAN && + tun->vxlan.b_mode_enabled) + dev_info->vxlan_enable = true; + + if (tun->l2_gre.b_mode_enabled && tun->ip_gre.b_mode_enabled && + tun->l2_gre.tun_cls == QED_TUNN_CLSS_MAC_VLAN && + tun->ip_gre.tun_cls == QED_TUNN_CLSS_MAC_VLAN) + dev_info->gre_enable = true; + + if (tun->l2_geneve.b_mode_enabled && tun->ip_geneve.b_mode_enabled && + tun->l2_geneve.tun_cls == QED_TUNN_CLSS_MAC_VLAN && + tun->ip_geneve.tun_cls == QED_TUNN_CLSS_MAC_VLAN) + dev_info->geneve_enable = true; + dev_info->num_hwfns = cdev->num_hwfns; dev_info->pci_mem_start = cdev->pci_params.mem_start; dev_info->pci_mem_end = cdev->pci_params.mem_end; diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index 34473fbac798..23e0c1696c86 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -887,6 +887,9 @@ void qede_udp_tunnel_add(struct net_device *dev, struct udp_tunnel_info *ti) switch (ti->type) { case UDP_TUNNEL_TYPE_VXLAN: + if (!edev->dev_info.common.vxlan_enable) + return; + if (edev->vxlan_dst_port) return; @@ -898,6 +901,9 @@ void qede_udp_tunnel_add(struct net_device *dev, struct udp_tunnel_info *ti) set_bit(QEDE_SP_VXLAN_PORT_CONFIG, &edev->sp_flags); break; case UDP_TUNNEL_TYPE_GENEVE: + if (!edev->dev_info.common.geneve_enable) + return; + if (edev->geneve_dst_port) return; diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 02b305c19f38..42f043b1524f 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -609,6 +609,7 @@ static void qede_init_ndev(struct qede_dev *edev) { struct net_device *ndev = edev->ndev; struct pci_dev *pdev = edev->pdev; + bool udp_tunnel_enable = false; netdev_features_t hw_features; pci_set_drvdata(pdev, ndev); @@ -631,20 +632,33 @@ static void qede_init_ndev(struct qede_dev *edev) NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6; - /* Encap features*/ - hw_features |= NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL | - NETIF_F_TSO_ECN | NETIF_F_GSO_UDP_TUNNEL_CSUM | - NETIF_F_GSO_GRE_CSUM; - if (!IS_VF(edev) && edev->dev_info.common.num_hwfns == 1) hw_features |= NETIF_F_NTUPLE; - ndev->hw_enc_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO_ECN | - NETIF_F_TSO6 | NETIF_F_GSO_GRE | - NETIF_F_GSO_UDP_TUNNEL | NETIF_F_RXCSUM | - NETIF_F_GSO_UDP_TUNNEL_CSUM | - NETIF_F_GSO_GRE_CSUM; + if (edev->dev_info.common.vxlan_enable || + edev->dev_info.common.geneve_enable) + udp_tunnel_enable = true; + + if (udp_tunnel_enable || edev->dev_info.common.gre_enable) { + hw_features |= NETIF_F_TSO_ECN; + ndev->hw_enc_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_SG | NETIF_F_TSO | + NETIF_F_TSO_ECN | NETIF_F_TSO6 | + NETIF_F_RXCSUM; + } + + if (udp_tunnel_enable) { + hw_features |= (NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM); + ndev->hw_enc_features |= (NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM); + } + + if (edev->dev_info.common.gre_enable) { + hw_features |= (NETIF_F_GSO_GRE | NETIF_F_GSO_GRE_CSUM); + ndev->hw_enc_features |= (NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM); + } ndev->vlan_features = hw_features | NETIF_F_RXHASH | NETIF_F_RXCSUM | NETIF_F_HIGHDMA; diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index 9f966be89510..5544d7b2f2bb 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -338,6 +338,11 @@ struct qed_dev_info { bool wol_support; enum qed_dev_type dev_type; + + /* Output parameters for qede */ + bool vxlan_enable; + bool gre_enable; + bool geneve_enable; }; enum qed_sb_type { -- cgit From 369bfd4ec77f1668e48d395e95849d29fccaa4c3 Mon Sep 17 00:00:00 2001 From: "Chopra, Manish" Date: Mon, 24 Apr 2017 10:00:46 -0700 Subject: qede: Disable tunnel offloads for non offloaded UDP ports This patch disables tunnel offloads via ndo_features_check() if given UDP port is not offloaded to hardware. This in turn allows to run multiple tunnel interfaces using different UDP ports. Signed-off-by: Manish Chopra Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_fp.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index 961b1d36b9eb..7b6f41d06245 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -1697,13 +1697,24 @@ netdev_features_t qede_features_check(struct sk_buff *skb, } /* Disable offloads for geneve tunnels, as HW can't parse - * the geneve header which has option length greater than 32B. + * the geneve header which has option length greater than 32b + * and disable offloads for the ports which are not offloaded. */ - if ((l4_proto == IPPROTO_UDP) && - ((skb_inner_mac_header(skb) - - skb_transport_header(skb)) > QEDE_MAX_TUN_HDR_LEN)) - return features & ~(NETIF_F_CSUM_MASK | - NETIF_F_GSO_MASK); + if (l4_proto == IPPROTO_UDP) { + struct qede_dev *edev = netdev_priv(dev); + u16 hdrlen, vxln_port, gnv_port; + + hdrlen = QEDE_MAX_TUN_HDR_LEN; + vxln_port = edev->vxlan_dst_port; + gnv_port = edev->geneve_dst_port; + + if ((skb_inner_mac_header(skb) - + skb_transport_header(skb)) > hdrlen || + (ntohs(udp_hdr(skb)->dest) != vxln_port && + ntohs(udp_hdr(skb)->dest) != gnv_port)) + return features & ~(NETIF_F_CSUM_MASK | + NETIF_F_GSO_MASK); + } } return features; -- cgit From 327a2b750c486c8e8f390dcff888881ad54d2f23 Mon Sep 17 00:00:00 2001 From: "Chopra, Manish" Date: Mon, 24 Apr 2017 10:00:47 -0700 Subject: qede: Configure UDP ports in local context. This patch configures UDP ports locally instead of configuring them in deferred context which would be helpful in synchronizing UDP ports configuration for VFs which will be enabled in further patches. Signed-off-by: Manish Chopra Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 2 - drivers/net/ethernet/qlogic/qede/qede_filter.c | 67 ++++++++++++++++++++------ drivers/net/ethernet/qlogic/qede/qede_main.c | 19 -------- 3 files changed, 52 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index 7e18ae6dec51..26699a752768 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -442,8 +442,6 @@ struct qede_fastpath { #define QEDE_TUNN_CSUM_UNNECESSARY BIT(2) #define QEDE_SP_RX_MODE 1 -#define QEDE_SP_VXLAN_PORT_CONFIG 2 -#define QEDE_SP_GENEVE_PORT_CONFIG 3 #ifdef CONFIG_RFS_ACCEL int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index 23e0c1696c86..4fa2c88e9693 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -883,7 +883,11 @@ int qede_set_features(struct net_device *dev, netdev_features_t features) void qede_udp_tunnel_add(struct net_device *dev, struct udp_tunnel_info *ti) { struct qede_dev *edev = netdev_priv(dev); + struct qed_tunn_params tunn_params; u16 t_port = ntohs(ti->port); + int rc; + + memset(&tunn_params, 0, sizeof(tunn_params)); switch (ti->type) { case UDP_TUNNEL_TYPE_VXLAN: @@ -893,12 +897,22 @@ void qede_udp_tunnel_add(struct net_device *dev, struct udp_tunnel_info *ti) if (edev->vxlan_dst_port) return; - edev->vxlan_dst_port = t_port; + tunn_params.update_vxlan_port = 1; + tunn_params.vxlan_port = t_port; - DP_VERBOSE(edev, QED_MSG_DEBUG, "Added vxlan port=%d\n", - t_port); + __qede_lock(edev); + rc = edev->ops->tunn_config(edev->cdev, &tunn_params); + __qede_unlock(edev); + + if (!rc) { + edev->vxlan_dst_port = t_port; + DP_VERBOSE(edev, QED_MSG_DEBUG, "Added vxlan port=%d\n", + t_port); + } else { + DP_NOTICE(edev, "Failed to add vxlan UDP port=%d\n", + t_port); + } - set_bit(QEDE_SP_VXLAN_PORT_CONFIG, &edev->sp_flags); break; case UDP_TUNNEL_TYPE_GENEVE: if (!edev->dev_info.common.geneve_enable) @@ -907,51 +921,74 @@ void qede_udp_tunnel_add(struct net_device *dev, struct udp_tunnel_info *ti) if (edev->geneve_dst_port) return; - edev->geneve_dst_port = t_port; + tunn_params.update_geneve_port = 1; + tunn_params.geneve_port = t_port; + + __qede_lock(edev); + rc = edev->ops->tunn_config(edev->cdev, &tunn_params); + __qede_unlock(edev); + + if (!rc) { + edev->geneve_dst_port = t_port; + DP_VERBOSE(edev, QED_MSG_DEBUG, + "Added geneve port=%d\n", t_port); + } else { + DP_NOTICE(edev, "Failed to add geneve UDP port=%d\n", + t_port); + } - DP_VERBOSE(edev, QED_MSG_DEBUG, "Added geneve port=%d\n", - t_port); - set_bit(QEDE_SP_GENEVE_PORT_CONFIG, &edev->sp_flags); break; default: return; } - - schedule_delayed_work(&edev->sp_task, 0); } -void qede_udp_tunnel_del(struct net_device *dev, struct udp_tunnel_info *ti) +void qede_udp_tunnel_del(struct net_device *dev, + struct udp_tunnel_info *ti) { struct qede_dev *edev = netdev_priv(dev); + struct qed_tunn_params tunn_params; u16 t_port = ntohs(ti->port); + memset(&tunn_params, 0, sizeof(tunn_params)); + switch (ti->type) { case UDP_TUNNEL_TYPE_VXLAN: if (t_port != edev->vxlan_dst_port) return; + tunn_params.update_vxlan_port = 1; + tunn_params.vxlan_port = 0; + + __qede_lock(edev); + edev->ops->tunn_config(edev->cdev, &tunn_params); + __qede_unlock(edev); + edev->vxlan_dst_port = 0; DP_VERBOSE(edev, QED_MSG_DEBUG, "Deleted vxlan port=%d\n", t_port); - set_bit(QEDE_SP_VXLAN_PORT_CONFIG, &edev->sp_flags); break; case UDP_TUNNEL_TYPE_GENEVE: if (t_port != edev->geneve_dst_port) return; + tunn_params.update_geneve_port = 1; + tunn_params.geneve_port = 0; + + __qede_lock(edev); + edev->ops->tunn_config(edev->cdev, &tunn_params); + __qede_unlock(edev); + edev->geneve_dst_port = 0; DP_VERBOSE(edev, QED_MSG_DEBUG, "Deleted geneve port=%d\n", t_port); - set_bit(QEDE_SP_GENEVE_PORT_CONFIG, &edev->sp_flags); break; default: return; } - - schedule_delayed_work(&edev->sp_task, 0); } static void qede_xdp_reload_func(struct qede_dev *edev, diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 42f043b1524f..f57c823730c0 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -796,7 +796,6 @@ static void qede_sp_task(struct work_struct *work) { struct qede_dev *edev = container_of(work, struct qede_dev, sp_task.work); - struct qed_dev *cdev = edev->cdev; __qede_lock(edev); @@ -804,24 +803,6 @@ static void qede_sp_task(struct work_struct *work) if (edev->state == QEDE_STATE_OPEN) qede_config_rx_mode(edev->ndev); - if (test_and_clear_bit(QEDE_SP_VXLAN_PORT_CONFIG, &edev->sp_flags)) { - struct qed_tunn_params tunn_params; - - memset(&tunn_params, 0, sizeof(tunn_params)); - tunn_params.update_vxlan_port = 1; - tunn_params.vxlan_port = edev->vxlan_dst_port; - qed_ops->tunn_config(cdev, &tunn_params); - } - - if (test_and_clear_bit(QEDE_SP_GENEVE_PORT_CONFIG, &edev->sp_flags)) { - struct qed_tunn_params tunn_params; - - memset(&tunn_params, 0, sizeof(tunn_params)); - tunn_params.update_geneve_port = 1; - tunn_params.geneve_port = edev->geneve_dst_port; - qed_ops->tunn_config(cdev, &tunn_params); - } - #ifdef CONFIG_RFS_ACCEL if (test_and_clear_bit(QEDE_SP_ARFS_CONFIG, &edev->sp_flags)) { if (edev->state == QEDE_STATE_OPEN) -- cgit From 97379f15c21e7ae27eb1ecf84adcace42c960c87 Mon Sep 17 00:00:00 2001 From: "Chopra, Manish" Date: Mon, 24 Apr 2017 10:00:48 -0700 Subject: qed/qede: Add UDP ports in bulletin board This patch adds support for UDP ports in bulletin board to notify UDP ports change to the VFs Signed-off-by: Manish Chopra Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_l2.c | 19 +++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_sriov.c | 23 +++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_sriov.h | 9 +++++++++ drivers/net/ethernet/qlogic/qed/qed_vf.c | 16 ++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_vf.h | 4 +++- drivers/net/ethernet/qlogic/qede/qede.h | 1 + drivers/net/ethernet/qlogic/qede/qede_filter.c | 11 +++++++++++ drivers/net/ethernet/qlogic/qede/qede_main.c | 1 + include/linux/qed/qed_eth_if.h | 1 + 9 files changed, 84 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 03216babb06f..b8bd1193fb9c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -2304,11 +2304,30 @@ static int qed_tunn_configure(struct qed_dev *cdev, for_each_hwfn(cdev, i) { struct qed_hwfn *hwfn = &cdev->hwfns[i]; + struct qed_tunnel_info *tun; + + tun = &hwfn->cdev->tunnel; rc = qed_sp_pf_update_tunn_cfg(hwfn, &tunn_info, QED_SPQ_MODE_EBLOCK, NULL); if (rc) return rc; + + if (IS_PF_SRIOV(hwfn)) { + u16 vxlan_port, geneve_port; + int j; + + vxlan_port = tun->vxlan_port.port; + geneve_port = tun->geneve_port.port; + + qed_for_each_vf(hwfn, j) { + qed_iov_bulletin_set_udp_ports(hwfn, j, + vxlan_port, + geneve_port); + } + + qed_schedule_iov(hwfn, QED_IOV_WQ_BULLETIN_UPDATE_FLAG); + } } return 0; diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 92a3ee1715d9..85672f6cf629 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -3511,6 +3511,29 @@ static void qed_iov_bulletin_set_forced_vlan(struct qed_hwfn *p_hwfn, qed_iov_configure_vport_forced(p_hwfn, vf_info, feature); } +void qed_iov_bulletin_set_udp_ports(struct qed_hwfn *p_hwfn, + int vfid, u16 vxlan_port, u16 geneve_port) +{ + struct qed_vf_info *vf_info; + + vf_info = qed_iov_get_vf_info(p_hwfn, (u16)vfid, true); + if (!vf_info) { + DP_NOTICE(p_hwfn->cdev, + "Can not set udp ports, invalid vfid [%d]\n", vfid); + return; + } + + if (vf_info->b_malicious) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Can not set udp ports to malicious VF [%d]\n", + vfid); + return; + } + + vf_info->bulletin.p_virt->vxlan_udp_port = vxlan_port; + vf_info->bulletin.p_virt->geneve_udp_port = geneve_port; +} + static bool qed_iov_vf_has_vport_instance(struct qed_hwfn *p_hwfn, int vfid) { struct qed_vf_info *p_vf_info; diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.h b/drivers/net/ethernet/qlogic/qed/qed_sriov.h index 8e96b1d19308..81a497ce6585 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.h +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.h @@ -270,6 +270,9 @@ enum qed_iov_wq_flag { */ u16 qed_iov_get_next_active_vf(struct qed_hwfn *p_hwfn, u16 rel_vf_id); +void qed_iov_bulletin_set_udp_ports(struct qed_hwfn *p_hwfn, + int vfid, u16 vxlan_port, u16 geneve_port); + /** * @brief Read sriov related information and allocated resources * reads from configuraiton space, shmem, etc. @@ -378,6 +381,12 @@ static inline u16 qed_iov_get_next_active_vf(struct qed_hwfn *p_hwfn, return MAX_NUM_VFS; } +static inline void +qed_iov_bulletin_set_udp_ports(struct qed_hwfn *p_hwfn, int vfid, + u16 vxlan_port, u16 geneve_port) +{ +} + static inline int qed_iov_hw_info(struct qed_hwfn *p_hwfn) { return 0; diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c index 798786562b1b..01cbbe655af4 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.c +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c @@ -1251,6 +1251,18 @@ static bool qed_vf_bulletin_get_forced_mac(struct qed_hwfn *hwfn, return true; } +static void +qed_vf_bulletin_get_udp_ports(struct qed_hwfn *p_hwfn, + u16 *p_vxlan_port, u16 *p_geneve_port) +{ + struct qed_bulletin_content *p_bulletin; + + p_bulletin = &p_hwfn->vf_iov_info->bulletin_shadow; + + *p_vxlan_port = p_bulletin->vxlan_udp_port; + *p_geneve_port = p_bulletin->geneve_udp_port; +} + void qed_vf_get_fw_version(struct qed_hwfn *p_hwfn, u16 *fw_major, u16 *fw_minor, u16 *fw_rev, u16 *fw_eng) @@ -1270,12 +1282,16 @@ static void qed_handle_bulletin_change(struct qed_hwfn *hwfn) struct qed_eth_cb_ops *ops = hwfn->cdev->protocol_ops.eth; u8 mac[ETH_ALEN], is_mac_exist, is_mac_forced; void *cookie = hwfn->cdev->ops_cookie; + u16 vxlan_port, geneve_port; + qed_vf_bulletin_get_udp_ports(hwfn, &vxlan_port, &geneve_port); is_mac_exist = qed_vf_bulletin_get_forced_mac(hwfn, mac, &is_mac_forced); if (is_mac_exist && cookie) ops->force_mac(cookie, mac, !!is_mac_forced); + ops->ports_update(cookie, vxlan_port, geneve_port); + /* Always update link configuration according to bulletin */ qed_link_update(hwfn); } diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.h b/drivers/net/ethernet/qlogic/qed/qed_vf.h index 105c0edd2a01..6cca145331cd 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.h +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.h @@ -513,7 +513,9 @@ struct qed_bulletin_content { u8 partner_rx_flow_ctrl_en; u8 partner_adv_pause; u8 sfp_tx_fault; - u8 padding4[6]; + u16 vxlan_udp_port; + u16 geneve_udp_port; + u8 padding4[2]; u32 speed; u32 partner_adv_speed; diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index 26699a752768..766a79d2ed75 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -480,6 +480,7 @@ irqreturn_t qede_msix_fp_int(int irq, void *fp_cookie); /* Filtering function definitions */ void qede_force_mac(void *dev, u8 *mac, bool forced); +void qede_udp_ports_update(void *dev, u16 vxlan_port, u16 geneve_port); int qede_set_mac_addr(struct net_device *ndev, void *p); int qede_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid); diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index 4fa2c88e9693..eb5652073ca8 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -480,6 +480,17 @@ ret_unlock: } #endif +void qede_udp_ports_update(void *dev, u16 vxlan_port, u16 geneve_port) +{ + struct qede_dev *edev = dev; + + if (edev->vxlan_dst_port != vxlan_port) + edev->vxlan_dst_port = 0; + + if (edev->geneve_dst_port != geneve_port) + edev->geneve_dst_port = 0; +} + void qede_force_mac(void *dev, u8 *mac, bool forced) { struct qede_dev *edev = dev; diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index f57c823730c0..292e2dc3f8ae 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -231,6 +231,7 @@ static struct qed_eth_cb_ops qede_ll_ops = { .link_update = qede_link_update, }, .force_mac = qede_force_mac, + .ports_update = qede_udp_ports_update, }; static int qede_netdev_event(struct notifier_block *this, unsigned long event, diff --git a/include/linux/qed/qed_eth_if.h b/include/linux/qed/qed_eth_if.h index 1eba803cb7f1..15fa7c6e4c6f 100644 --- a/include/linux/qed/qed_eth_if.h +++ b/include/linux/qed/qed_eth_if.h @@ -158,6 +158,7 @@ struct qed_tunn_params { struct qed_eth_cb_ops { struct qed_common_cb_ops common; void (*force_mac) (void *dev, u8 *mac, bool forced); + void (*ports_update)(void *dev, u16 vxlan_port, u16 geneve_port); }; #define QED_MAX_PHC_DRIFT_PPB 291666666 -- cgit From eaf3c0c6b4e307e5c7e6cbeb8c5a17be7feee249 Mon Sep 17 00:00:00 2001 From: "Chopra, Manish" Date: Mon, 24 Apr 2017 10:00:49 -0700 Subject: qed - VF tunnelling support [VXLAN/GENEVE/GRE] This patch adds hardware channel APIs support between VF and PF for tunnelling configuration for the VFs. According to that configuration VFs can run VXLAN/GENEVE/GRE tunnels over it with tunnel features offloaded. Using these APIs VF can also request for UDP ports configuration to the PF, although PF and it's child VFs share the same port. Signed-off-by: Manish Chopra Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 1 + drivers/net/ethernet/qlogic/qed/qed_dev.c | 15 +- drivers/net/ethernet/qlogic/qed/qed_l2.c | 3 - drivers/net/ethernet/qlogic/qed/qed_main.c | 8 + drivers/net/ethernet/qlogic/qed/qed_sp_commands.c | 3 + drivers/net/ethernet/qlogic/qed/qed_sriov.c | 217 ++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_vf.c | 149 +++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_vf.h | 54 ++++++ 8 files changed, 446 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 8b7b1dd25f1d..8a8f1396de66 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -718,6 +718,7 @@ struct qed_dev { u32 rdma_max_sge; u32 rdma_max_inline; u32 rdma_max_srq_sge; + u16 tunn_feature_mask; }; #define NUM_OF_VFS(dev) (QED_IS_BB(dev) ? MAX_NUM_VFS_BB \ diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 13817ccf2e58..f168b718594b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1594,6 +1594,19 @@ qed_fill_load_req_params(struct qed_load_req_params *p_load_req, p_load_req->override_force_load = p_drv_load->override_force_load; } +static int qed_vf_start(struct qed_hwfn *p_hwfn, + struct qed_hw_init_params *p_params) +{ + if (p_params->p_tunn) { + qed_vf_set_vf_start_tunn_update_param(p_params->p_tunn); + qed_vf_pf_tunnel_param_update(p_hwfn, p_params->p_tunn); + } + + p_hwfn->b_int_enabled = 1; + + return 0; +} + int qed_hw_init(struct qed_dev *cdev, struct qed_hw_init_params *p_params) { struct qed_load_req_params load_req_params; @@ -1623,7 +1636,7 @@ int qed_hw_init(struct qed_dev *cdev, struct qed_hw_init_params *p_params) } if (IS_VF(cdev)) { - p_hwfn->b_int_enabled = 1; + qed_vf_start(p_hwfn, p_params); continue; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index b8bd1193fb9c..746fed4099c8 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -2288,9 +2288,6 @@ static int qed_tunn_configure(struct qed_dev *cdev, struct qed_tunnel_info tunn_info; int i, rc; - if (IS_VF(cdev)) - return 0; - memset(&tunn_info, 0, sizeof(tunn_info)); if (tunn_params->update_vxlan_port) { tunn_info.vxlan_port.b_update_port = true; diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index e6502810ccd7..a919260b68f2 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -1022,6 +1022,14 @@ static int qed_slowpath_start(struct qed_dev *cdev, DP_INFO(cdev, "HW initialization and function start completed successfully\n"); + if (IS_PF(cdev)) { + cdev->tunn_feature_mask = (BIT(QED_MODE_VXLAN_TUNN) | + BIT(QED_MODE_L2GENEVE_TUNN) | + BIT(QED_MODE_IPGENEVE_TUNN) | + BIT(QED_MODE_L2GRE_TUNN) | + BIT(QED_MODE_IPGRE_TUNN)); + } + /* Allocate LL2 interface if needed */ if (QED_LEADING_HWFN(cdev)->using_ll2) { rc = qed_ll2_alloc_if(cdev); diff --git a/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c b/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c index 96c6fda430dc..bc3694e91b85 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sp_commands.c @@ -451,6 +451,9 @@ int qed_sp_pf_update_tunn_cfg(struct qed_hwfn *p_hwfn, struct qed_sp_init_data init_data; int rc = -EINVAL; + if (IS_VF(p_hwfn->cdev)) + return qed_vf_pf_tunnel_param_update(p_hwfn, p_tunn); + if (!p_tunn) return -EINVAL; diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 85672f6cf629..d5df29f787c5 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -2019,6 +2019,220 @@ out: qed_iov_vf_mbx_start_rxq_resp(p_hwfn, p_ptt, vf, status, b_legacy_vf); } +static void +qed_iov_pf_update_tun_response(struct pfvf_update_tunn_param_tlv *p_resp, + struct qed_tunnel_info *p_tun, + u16 tunn_feature_mask) +{ + p_resp->tunn_feature_mask = tunn_feature_mask; + p_resp->vxlan_mode = p_tun->vxlan.b_mode_enabled; + p_resp->l2geneve_mode = p_tun->l2_geneve.b_mode_enabled; + p_resp->ipgeneve_mode = p_tun->ip_geneve.b_mode_enabled; + p_resp->l2gre_mode = p_tun->l2_gre.b_mode_enabled; + p_resp->ipgre_mode = p_tun->l2_gre.b_mode_enabled; + p_resp->vxlan_clss = p_tun->vxlan.tun_cls; + p_resp->l2gre_clss = p_tun->l2_gre.tun_cls; + p_resp->ipgre_clss = p_tun->ip_gre.tun_cls; + p_resp->l2geneve_clss = p_tun->l2_geneve.tun_cls; + p_resp->ipgeneve_clss = p_tun->ip_geneve.tun_cls; + p_resp->geneve_udp_port = p_tun->geneve_port.port; + p_resp->vxlan_udp_port = p_tun->vxlan_port.port; +} + +static void +__qed_iov_pf_update_tun_param(struct vfpf_update_tunn_param_tlv *p_req, + struct qed_tunn_update_type *p_tun, + enum qed_tunn_mode mask, u8 tun_cls) +{ + if (p_req->tun_mode_update_mask & BIT(mask)) { + p_tun->b_update_mode = true; + + if (p_req->tunn_mode & BIT(mask)) + p_tun->b_mode_enabled = true; + } + + p_tun->tun_cls = tun_cls; +} + +static void +qed_iov_pf_update_tun_param(struct vfpf_update_tunn_param_tlv *p_req, + struct qed_tunn_update_type *p_tun, + struct qed_tunn_update_udp_port *p_port, + enum qed_tunn_mode mask, + u8 tun_cls, u8 update_port, u16 port) +{ + if (update_port) { + p_port->b_update_port = true; + p_port->port = port; + } + + __qed_iov_pf_update_tun_param(p_req, p_tun, mask, tun_cls); +} + +static bool +qed_iov_pf_validate_tunn_param(struct vfpf_update_tunn_param_tlv *p_req) +{ + bool b_update_requested = false; + + if (p_req->tun_mode_update_mask || p_req->update_tun_cls || + p_req->update_geneve_port || p_req->update_vxlan_port) + b_update_requested = true; + + return b_update_requested; +} + +static void qed_pf_validate_tunn_mode(struct qed_tunn_update_type *tun, int *rc) +{ + if (tun->b_update_mode && !tun->b_mode_enabled) { + tun->b_update_mode = false; + *rc = -EINVAL; + } +} + +static int +qed_pf_validate_modify_tunn_config(struct qed_hwfn *p_hwfn, + u16 *tun_features, bool *update, + struct qed_tunnel_info *tun_src) +{ + struct qed_eth_cb_ops *ops = p_hwfn->cdev->protocol_ops.eth; + struct qed_tunnel_info *tun = &p_hwfn->cdev->tunnel; + u16 bultn_vxlan_port, bultn_geneve_port; + void *cookie = p_hwfn->cdev->ops_cookie; + int i, rc = 0; + + *tun_features = p_hwfn->cdev->tunn_feature_mask; + bultn_vxlan_port = tun->vxlan_port.port; + bultn_geneve_port = tun->geneve_port.port; + qed_pf_validate_tunn_mode(&tun_src->vxlan, &rc); + qed_pf_validate_tunn_mode(&tun_src->l2_geneve, &rc); + qed_pf_validate_tunn_mode(&tun_src->ip_geneve, &rc); + qed_pf_validate_tunn_mode(&tun_src->l2_gre, &rc); + qed_pf_validate_tunn_mode(&tun_src->ip_gre, &rc); + + if ((tun_src->b_update_rx_cls || tun_src->b_update_tx_cls) && + (tun_src->vxlan.tun_cls != QED_TUNN_CLSS_MAC_VLAN || + tun_src->l2_geneve.tun_cls != QED_TUNN_CLSS_MAC_VLAN || + tun_src->ip_geneve.tun_cls != QED_TUNN_CLSS_MAC_VLAN || + tun_src->l2_gre.tun_cls != QED_TUNN_CLSS_MAC_VLAN || + tun_src->ip_gre.tun_cls != QED_TUNN_CLSS_MAC_VLAN)) { + tun_src->b_update_rx_cls = false; + tun_src->b_update_tx_cls = false; + rc = -EINVAL; + } + + if (tun_src->vxlan_port.b_update_port) { + if (tun_src->vxlan_port.port == tun->vxlan_port.port) { + tun_src->vxlan_port.b_update_port = false; + } else { + *update = true; + bultn_vxlan_port = tun_src->vxlan_port.port; + } + } + + if (tun_src->geneve_port.b_update_port) { + if (tun_src->geneve_port.port == tun->geneve_port.port) { + tun_src->geneve_port.b_update_port = false; + } else { + *update = true; + bultn_geneve_port = tun_src->geneve_port.port; + } + } + + qed_for_each_vf(p_hwfn, i) { + qed_iov_bulletin_set_udp_ports(p_hwfn, i, bultn_vxlan_port, + bultn_geneve_port); + } + + qed_schedule_iov(p_hwfn, QED_IOV_WQ_BULLETIN_UPDATE_FLAG); + ops->ports_update(cookie, bultn_vxlan_port, bultn_geneve_port); + + return rc; +} + +static void qed_iov_vf_mbx_update_tunn_param(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *p_vf) +{ + struct qed_tunnel_info *p_tun = &p_hwfn->cdev->tunnel; + struct qed_iov_vf_mbx *mbx = &p_vf->vf_mbx; + struct pfvf_update_tunn_param_tlv *p_resp; + struct vfpf_update_tunn_param_tlv *p_req; + u8 status = PFVF_STATUS_SUCCESS; + bool b_update_required = false; + struct qed_tunnel_info tunn; + u16 tunn_feature_mask = 0; + int i, rc = 0; + + mbx->offset = (u8 *)mbx->reply_virt; + + memset(&tunn, 0, sizeof(tunn)); + p_req = &mbx->req_virt->tunn_param_update; + + if (!qed_iov_pf_validate_tunn_param(p_req)) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "No tunnel update requested by VF\n"); + status = PFVF_STATUS_FAILURE; + goto send_resp; + } + + tunn.b_update_rx_cls = p_req->update_tun_cls; + tunn.b_update_tx_cls = p_req->update_tun_cls; + + qed_iov_pf_update_tun_param(p_req, &tunn.vxlan, &tunn.vxlan_port, + QED_MODE_VXLAN_TUNN, p_req->vxlan_clss, + p_req->update_vxlan_port, + p_req->vxlan_port); + qed_iov_pf_update_tun_param(p_req, &tunn.l2_geneve, &tunn.geneve_port, + QED_MODE_L2GENEVE_TUNN, + p_req->l2geneve_clss, + p_req->update_geneve_port, + p_req->geneve_port); + __qed_iov_pf_update_tun_param(p_req, &tunn.ip_geneve, + QED_MODE_IPGENEVE_TUNN, + p_req->ipgeneve_clss); + __qed_iov_pf_update_tun_param(p_req, &tunn.l2_gre, + QED_MODE_L2GRE_TUNN, p_req->l2gre_clss); + __qed_iov_pf_update_tun_param(p_req, &tunn.ip_gre, + QED_MODE_IPGRE_TUNN, p_req->ipgre_clss); + + /* If PF modifies VF's req then it should + * still return an error in case of partial configuration + * or modified configuration as opposed to requested one. + */ + rc = qed_pf_validate_modify_tunn_config(p_hwfn, &tunn_feature_mask, + &b_update_required, &tunn); + + if (rc) + status = PFVF_STATUS_FAILURE; + + /* If QED client is willing to update anything ? */ + if (b_update_required) { + u16 geneve_port; + + rc = qed_sp_pf_update_tunn_cfg(p_hwfn, &tunn, + QED_SPQ_MODE_EBLOCK, NULL); + if (rc) + status = PFVF_STATUS_FAILURE; + + geneve_port = p_tun->geneve_port.port; + qed_for_each_vf(p_hwfn, i) { + qed_iov_bulletin_set_udp_ports(p_hwfn, i, + p_tun->vxlan_port.port, + geneve_port); + } + } + +send_resp: + p_resp = qed_add_tlv(p_hwfn, &mbx->offset, + CHANNEL_TLV_UPDATE_TUNN_PARAM, sizeof(*p_resp)); + + qed_iov_pf_update_tun_response(p_resp, p_tun, tunn_feature_mask); + qed_add_tlv(p_hwfn, &mbx->offset, CHANNEL_TLV_LIST_END, + sizeof(struct channel_list_end_tlv)); + + qed_iov_send_response(p_hwfn, p_ptt, p_vf, sizeof(*p_resp), status); +} + static void qed_iov_vf_mbx_start_txq_resp(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, struct qed_vf_info *p_vf, u8 status) @@ -3275,6 +3489,9 @@ static void qed_iov_process_mbx_req(struct qed_hwfn *p_hwfn, case CHANNEL_TLV_RELEASE: qed_iov_vf_mbx_release(p_hwfn, p_ptt, p_vf); break; + case CHANNEL_TLV_UPDATE_TUNN_PARAM: + qed_iov_vf_mbx_update_tunn_param(p_hwfn, p_ptt, p_vf); + break; } } else if (qed_iov_tlv_supported(mbx->first_tlv.tl.type)) { DP_VERBOSE(p_hwfn, QED_MSG_IOV, diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c index 01cbbe655af4..c4c4a408b40b 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.c +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c @@ -418,6 +418,155 @@ free_p_iov: #define MSTORM_QZONE_START(dev) (TSTORM_QZONE_START + \ (TSTORM_QZONE_SIZE * NUM_OF_L2_QUEUES(dev))) +static void +__qed_vf_prep_tunn_req_tlv(struct vfpf_update_tunn_param_tlv *p_req, + struct qed_tunn_update_type *p_src, + enum qed_tunn_clss mask, u8 *p_cls) +{ + if (p_src->b_update_mode) { + p_req->tun_mode_update_mask |= BIT(mask); + + if (p_src->b_mode_enabled) + p_req->tunn_mode |= BIT(mask); + } + + *p_cls = p_src->tun_cls; +} + +static void +qed_vf_prep_tunn_req_tlv(struct vfpf_update_tunn_param_tlv *p_req, + struct qed_tunn_update_type *p_src, + enum qed_tunn_clss mask, + u8 *p_cls, struct qed_tunn_update_udp_port *p_port, + u8 *p_update_port, u16 *p_udp_port) +{ + if (p_port->b_update_port) { + *p_update_port = 1; + *p_udp_port = p_port->port; + } + + __qed_vf_prep_tunn_req_tlv(p_req, p_src, mask, p_cls); +} + +void qed_vf_set_vf_start_tunn_update_param(struct qed_tunnel_info *p_tun) +{ + if (p_tun->vxlan.b_mode_enabled) + p_tun->vxlan.b_update_mode = true; + if (p_tun->l2_geneve.b_mode_enabled) + p_tun->l2_geneve.b_update_mode = true; + if (p_tun->ip_geneve.b_mode_enabled) + p_tun->ip_geneve.b_update_mode = true; + if (p_tun->l2_gre.b_mode_enabled) + p_tun->l2_gre.b_update_mode = true; + if (p_tun->ip_gre.b_mode_enabled) + p_tun->ip_gre.b_update_mode = true; + + p_tun->b_update_rx_cls = true; + p_tun->b_update_tx_cls = true; +} + +static void +__qed_vf_update_tunn_param(struct qed_tunn_update_type *p_tun, + u16 feature_mask, u8 tunn_mode, + u8 tunn_cls, enum qed_tunn_mode val) +{ + if (feature_mask & BIT(val)) { + p_tun->b_mode_enabled = tunn_mode; + p_tun->tun_cls = tunn_cls; + } else { + p_tun->b_mode_enabled = false; + } +} + +static void qed_vf_update_tunn_param(struct qed_hwfn *p_hwfn, + struct qed_tunnel_info *p_tun, + struct pfvf_update_tunn_param_tlv *p_resp) +{ + /* Update mode and classes provided by PF */ + u16 feat_mask = p_resp->tunn_feature_mask; + + __qed_vf_update_tunn_param(&p_tun->vxlan, feat_mask, + p_resp->vxlan_mode, p_resp->vxlan_clss, + QED_MODE_VXLAN_TUNN); + __qed_vf_update_tunn_param(&p_tun->l2_geneve, feat_mask, + p_resp->l2geneve_mode, + p_resp->l2geneve_clss, + QED_MODE_L2GENEVE_TUNN); + __qed_vf_update_tunn_param(&p_tun->ip_geneve, feat_mask, + p_resp->ipgeneve_mode, + p_resp->ipgeneve_clss, + QED_MODE_IPGENEVE_TUNN); + __qed_vf_update_tunn_param(&p_tun->l2_gre, feat_mask, + p_resp->l2gre_mode, p_resp->l2gre_clss, + QED_MODE_L2GRE_TUNN); + __qed_vf_update_tunn_param(&p_tun->ip_gre, feat_mask, + p_resp->ipgre_mode, p_resp->ipgre_clss, + QED_MODE_IPGRE_TUNN); + p_tun->geneve_port.port = p_resp->geneve_udp_port; + p_tun->vxlan_port.port = p_resp->vxlan_udp_port; + + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "tunn mode: vxlan=0x%x, l2geneve=0x%x, ipgeneve=0x%x, l2gre=0x%x, ipgre=0x%x", + p_tun->vxlan.b_mode_enabled, p_tun->l2_geneve.b_mode_enabled, + p_tun->ip_geneve.b_mode_enabled, + p_tun->l2_gre.b_mode_enabled, p_tun->ip_gre.b_mode_enabled); +} + +int qed_vf_pf_tunnel_param_update(struct qed_hwfn *p_hwfn, + struct qed_tunnel_info *p_src) +{ + struct qed_tunnel_info *p_tun = &p_hwfn->cdev->tunnel; + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_update_tunn_param_tlv *p_resp; + struct vfpf_update_tunn_param_tlv *p_req; + int rc; + + p_req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_UPDATE_TUNN_PARAM, + sizeof(*p_req)); + + if (p_src->b_update_rx_cls && p_src->b_update_tx_cls) + p_req->update_tun_cls = 1; + + qed_vf_prep_tunn_req_tlv(p_req, &p_src->vxlan, QED_MODE_VXLAN_TUNN, + &p_req->vxlan_clss, &p_src->vxlan_port, + &p_req->update_vxlan_port, + &p_req->vxlan_port); + qed_vf_prep_tunn_req_tlv(p_req, &p_src->l2_geneve, + QED_MODE_L2GENEVE_TUNN, + &p_req->l2geneve_clss, &p_src->geneve_port, + &p_req->update_geneve_port, + &p_req->geneve_port); + __qed_vf_prep_tunn_req_tlv(p_req, &p_src->ip_geneve, + QED_MODE_IPGENEVE_TUNN, + &p_req->ipgeneve_clss); + __qed_vf_prep_tunn_req_tlv(p_req, &p_src->l2_gre, + QED_MODE_L2GRE_TUNN, &p_req->l2gre_clss); + __qed_vf_prep_tunn_req_tlv(p_req, &p_src->ip_gre, + QED_MODE_IPGRE_TUNN, &p_req->ipgre_clss); + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, + CHANNEL_TLV_LIST_END, + sizeof(struct channel_list_end_tlv)); + + p_resp = &p_iov->pf2vf_reply->tunn_param_resp; + rc = qed_send_msg2pf(p_hwfn, &p_resp->hdr.status, sizeof(*p_resp)); + + if (rc) + goto exit; + + if (p_resp->hdr.status != PFVF_STATUS_SUCCESS) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "Failed to update tunnel parameters\n"); + rc = -EINVAL; + } + + qed_vf_update_tunn_param(p_hwfn, p_tun, p_resp); +exit: + qed_vf_pf_req_end(p_hwfn, rc); + return rc; +} + int qed_vf_pf_rxq_start(struct qed_hwfn *p_hwfn, struct qed_queue_cid *p_cid, diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.h b/drivers/net/ethernet/qlogic/qed/qed_vf.h index 6cca145331cd..34ac70b0e5fe 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.h +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.h @@ -429,6 +429,43 @@ struct vfpf_ucast_filter_tlv { u16 padding[3]; }; +/* tunnel update param tlv */ +struct vfpf_update_tunn_param_tlv { + struct vfpf_first_tlv first_tlv; + + u8 tun_mode_update_mask; + u8 tunn_mode; + u8 update_tun_cls; + u8 vxlan_clss; + u8 l2gre_clss; + u8 ipgre_clss; + u8 l2geneve_clss; + u8 ipgeneve_clss; + u8 update_geneve_port; + u8 update_vxlan_port; + u16 geneve_port; + u16 vxlan_port; + u8 padding[2]; +}; + +struct pfvf_update_tunn_param_tlv { + struct pfvf_tlv hdr; + + u16 tunn_feature_mask; + u8 vxlan_mode; + u8 l2geneve_mode; + u8 ipgeneve_mode; + u8 l2gre_mode; + u8 ipgre_mode; + u8 vxlan_clss; + u8 l2gre_clss; + u8 ipgre_clss; + u8 l2geneve_clss; + u8 ipgeneve_clss; + u16 vxlan_udp_port; + u16 geneve_udp_port; +}; + struct tlv_buffer_size { u8 tlv_buffer[TLV_BUFFER_SIZE]; }; @@ -444,6 +481,7 @@ union vfpf_tlvs { struct vfpf_vport_start_tlv start_vport; struct vfpf_vport_update_tlv vport_update; struct vfpf_ucast_filter_tlv ucast_filter; + struct vfpf_update_tunn_param_tlv tunn_param_update; struct channel_list_end_tlv list_end; struct tlv_buffer_size tlv_buf_size; }; @@ -453,6 +491,7 @@ union pfvf_tlvs { struct pfvf_acquire_resp_tlv acquire_resp; struct tlv_buffer_size tlv_buf_size; struct pfvf_start_queue_resp_tlv queue_start; + struct pfvf_update_tunn_param_tlv tunn_param_resp; }; enum qed_bulletin_bit { @@ -557,6 +596,7 @@ enum { CHANNEL_TLV_VPORT_UPDATE_RSS, CHANNEL_TLV_VPORT_UPDATE_ACCEPT_ANY_VLAN, CHANNEL_TLV_VPORT_UPDATE_SGE_TPA, + CHANNEL_TLV_UPDATE_TUNN_PARAM, CHANNEL_TLV_MAX, /* Required for iterating over vport-update tlvs. @@ -874,6 +914,9 @@ void __qed_vf_get_link_caps(struct qed_hwfn *p_hwfn, struct qed_bulletin_content *p_bulletin); void qed_iov_vf_task(struct work_struct *work); +void qed_vf_set_vf_start_tunn_update_param(struct qed_tunnel_info *p_tun); +int qed_vf_pf_tunnel_param_update(struct qed_hwfn *p_hwfn, + struct qed_tunnel_info *p_tunn); #else static inline void qed_vf_get_link_params(struct qed_hwfn *p_hwfn, struct qed_mcp_link_params *params) @@ -1035,6 +1078,17 @@ __qed_vf_get_link_caps(struct qed_hwfn *p_hwfn, static inline void qed_iov_vf_task(struct work_struct *work) { } + +static inline void +qed_vf_set_vf_start_tunn_update_param(struct qed_tunnel_info *p_tun) +{ +} + +static inline int qed_vf_pf_tunnel_param_update(struct qed_hwfn *p_hwfn, + struct qed_tunnel_info *p_tunn) +{ + return -EINVAL; +} #endif #endif -- cgit From 799dbe3e1ce312801cf8755a45bbdd6557560ec4 Mon Sep 17 00:00:00 2001 From: Ivan Khoronzhuk Date: Mon, 24 Apr 2017 23:54:06 +0300 Subject: net: ethernet: ti: netcp_core: remove unused compl queue mapping This code is unused and probably was unintentionally left while moving completion queue mapping in submit function. Signed-off-by: Ivan Khoronzhuk Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/netcp_core.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 9027c9c509b5..729a7da90b5b 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1134,7 +1134,6 @@ netcp_tx_map_skb(struct sk_buff *skb, struct netcp_intf *netcp) u32 buf_len = skb_frag_size(frag); dma_addr_t desc_dma; u32 desc_dma_32; - u32 pkt_info; dma_addr = dma_map_page(dev, page, page_offset, buf_len, DMA_TO_DEVICE); @@ -1151,9 +1150,6 @@ netcp_tx_map_skb(struct sk_buff *skb, struct netcp_intf *netcp) } desc_dma = knav_pool_desc_virt_to_dma(netcp->tx_pool, ndesc); - pkt_info = - (netcp->tx_compl_qid & KNAV_DMA_DESC_RETQ_MASK) << - KNAV_DMA_DESC_RETQ_SHIFT; set_pkt_info(dma_addr, buf_len, 0, ndesc); desc_dma_32 = (u32)desc_dma; set_words(&desc_dma_32, 1, &pdesc->next_desc); -- cgit From 472ecf084a7687347f79720c83881c07407bfd8b Mon Sep 17 00:00:00 2001 From: Mike Maloney Date: Mon, 24 Apr 2017 21:29:11 -0400 Subject: selftests/net: Fix broken test case in psock_fanout The error return falue form sock_fanout_open is -1, not zero. One test case was checking for 0 instead of -1. Tested: Built and tested in clean client. Signed-off-by: Mike Maloney Signed-off-by: David S. Miller --- tools/testing/selftests/net/psock_fanout.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c index b4b1d91fcea5..989f917068d1 100644 --- a/tools/testing/selftests/net/psock_fanout.c +++ b/tools/testing/selftests/net/psock_fanout.c @@ -305,7 +305,7 @@ static void test_unique_fanout_group_ids(void) exit(1); } - if (sock_fanout_open(PACKET_FANOUT_CPU, first_group_id)) { + if (sock_fanout_open(PACKET_FANOUT_CPU, first_group_id) != -1) { fprintf(stderr, "ERROR: joined group with wrong type.\n"); exit(1); } -- cgit From 8fe45924387be6b5c1be59a7eb330790c61d5d10 Mon Sep 17 00:00:00 2001 From: Teng Qin Date: Mon, 24 Apr 2017 19:00:37 -0700 Subject: bpf: map_get_next_key to return first key on NULL When iterating through a map, we need to find a key that does not exist in the map so map_get_next_key will give us the first key of the map. This often requires a lot of guessing in production systems. This patch makes map_get_next_key return the first key when the key pointer in the parameter is NULL. Signed-off-by: Teng Qin Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/trace/events/bpf.h | 10 +++++++--- kernel/bpf/arraymap.c | 2 +- kernel/bpf/hashtab.c | 9 +++++---- kernel/bpf/syscall.c | 20 ++++++++++++-------- tools/testing/selftests/bpf/test_maps.c | 29 +++++++++++++++++++++++++---- 5 files changed, 50 insertions(+), 20 deletions(-) diff --git a/include/trace/events/bpf.h b/include/trace/events/bpf.h index c3a53fd47ff1..52c8425d144b 100644 --- a/include/trace/events/bpf.h +++ b/include/trace/events/bpf.h @@ -321,11 +321,14 @@ TRACE_EVENT(bpf_map_next_key, __dynamic_array(u8, key, map->key_size) __dynamic_array(u8, nxt, map->key_size) __field(bool, key_trunc) + __field(bool, key_null) __field(int, ufd) ), TP_fast_assign( - memcpy(__get_dynamic_array(key), key, map->key_size); + if (key) + memcpy(__get_dynamic_array(key), key, map->key_size); + __entry->key_null = !key; memcpy(__get_dynamic_array(nxt), key_next, map->key_size); __entry->type = map->map_type; __entry->key_len = min(map->key_size, 16U); @@ -336,8 +339,9 @@ TRACE_EVENT(bpf_map_next_key, TP_printk("map type=%s ufd=%d key=[%s%s] next=[%s%s]", __print_symbolic(__entry->type, __MAP_TYPE_SYM_TAB), __entry->ufd, - __print_hex(__get_dynamic_array(key), __entry->key_len), - __entry->key_trunc ? " ..." : "", + __entry->key_null ? "NULL" : __print_hex(__get_dynamic_array(key), + __entry->key_len), + __entry->key_trunc && !__entry->key_null ? " ..." : "", __print_hex(__get_dynamic_array(nxt), __entry->key_len), __entry->key_trunc ? " ..." : "") ); diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index ec621df5a97a..5e00b2333c26 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -182,7 +182,7 @@ int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value) static int array_map_get_next_key(struct bpf_map *map, void *key, void *next_key) { struct bpf_array *array = container_of(map, struct bpf_array, map); - u32 index = *(u32 *)key; + u32 index = key ? *(u32 *)key : U32_MAX; u32 *next = (u32 *)next_key; if (index >= array->map.max_entries) { diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index bc80c038e430..004334ea13ba 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -540,12 +540,15 @@ static int htab_map_get_next_key(struct bpf_map *map, void *key, void *next_key) struct hlist_nulls_head *head; struct htab_elem *l, *next_l; u32 hash, key_size; - int i; + int i = 0; WARN_ON_ONCE(!rcu_read_lock_held()); key_size = map->key_size; + if (!key) + goto find_first_elem; + hash = htab_map_hash(key, key_size); head = select_bucket(htab, hash); @@ -553,10 +556,8 @@ static int htab_map_get_next_key(struct bpf_map *map, void *key, void *next_key) /* lookup the key */ l = lookup_nulls_elem_raw(head, hash, key, key_size, htab->n_buckets); - if (!l) { - i = 0; + if (!l) goto find_first_elem; - } /* key was found, get next key in the same bucket */ next_l = hlist_nulls_entry_safe(rcu_dereference_raw(hlist_nulls_next_rcu(&l->hash_node)), diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b89288e2b589..13642c73dca0 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -536,14 +536,18 @@ static int map_get_next_key(union bpf_attr *attr) if (IS_ERR(map)) return PTR_ERR(map); - err = -ENOMEM; - key = kmalloc(map->key_size, GFP_USER); - if (!key) - goto err_put; - - err = -EFAULT; - if (copy_from_user(key, ukey, map->key_size) != 0) - goto free_key; + if (ukey) { + err = -ENOMEM; + key = kmalloc(map->key_size, GFP_USER); + if (!key) + goto err_put; + + err = -EFAULT; + if (copy_from_user(key, ukey, map->key_size) != 0) + goto free_key; + } else { + key = NULL; + } err = -ENOMEM; next_key = kmalloc(map->key_size, GFP_USER); diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index 20f1871874df..a977c4f7b0ce 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -28,7 +28,7 @@ static int map_flags; static void test_hashmap(int task, void *data) { - long long key, next_key, value; + long long key, next_key, first_key, value; int fd; fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), @@ -89,10 +89,13 @@ static void test_hashmap(int task, void *data) assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT); /* Iterate over two elements. */ + assert(bpf_map_get_next_key(fd, NULL, &first_key) == 0 && + (first_key == 1 || first_key == 2)); assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 && - (next_key == 1 || next_key == 2)); + (next_key == first_key)); assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 && - (next_key == 1 || next_key == 2)); + (next_key == 1 || next_key == 2) && + (next_key != first_key)); assert(bpf_map_get_next_key(fd, &next_key, &next_key) == -1 && errno == ENOENT); @@ -105,6 +108,8 @@ static void test_hashmap(int task, void *data) key = 0; /* Check that map is empty. */ + assert(bpf_map_get_next_key(fd, NULL, &next_key) == -1 && + errno == ENOENT); assert(bpf_map_get_next_key(fd, &key, &next_key) == -1 && errno == ENOENT); @@ -133,7 +138,7 @@ static void test_hashmap_percpu(int task, void *data) { unsigned int nr_cpus = bpf_num_possible_cpus(); long long value[nr_cpus]; - long long key, next_key; + long long key, next_key, first_key; int expected_key_mask = 0; int fd, i; @@ -193,7 +198,13 @@ static void test_hashmap_percpu(int task, void *data) assert(bpf_map_delete_elem(fd, &key) == -1 && errno == ENOENT); /* Iterate over two elements. */ + assert(bpf_map_get_next_key(fd, NULL, &first_key) == 0 && + ((expected_key_mask & first_key) == first_key)); while (!bpf_map_get_next_key(fd, &key, &next_key)) { + if (first_key) { + assert(next_key == first_key); + first_key = 0; + } assert((expected_key_mask & next_key) == next_key); expected_key_mask &= ~next_key; @@ -219,6 +230,8 @@ static void test_hashmap_percpu(int task, void *data) key = 0; /* Check that map is empty. */ + assert(bpf_map_get_next_key(fd, NULL, &next_key) == -1 && + errno == ENOENT); assert(bpf_map_get_next_key(fd, &key, &next_key) == -1 && errno == ENOENT); @@ -264,6 +277,8 @@ static void test_arraymap(int task, void *data) assert(bpf_map_lookup_elem(fd, &key, &value) == -1 && errno == ENOENT); /* Iterate over two elements. */ + assert(bpf_map_get_next_key(fd, NULL, &next_key) == 0 && + next_key == 0); assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 && next_key == 0); assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 && @@ -319,6 +334,8 @@ static void test_arraymap_percpu(int task, void *data) assert(bpf_map_lookup_elem(fd, &key, values) == -1 && errno == ENOENT); /* Iterate over two elements. */ + assert(bpf_map_get_next_key(fd, NULL, &next_key) == 0 && + next_key == 0); assert(bpf_map_get_next_key(fd, &key, &next_key) == 0 && next_key == 0); assert(bpf_map_get_next_key(fd, &next_key, &next_key) == 0 && @@ -400,6 +417,8 @@ static void test_map_large(void) errno == E2BIG); /* Iterate through all elements. */ + assert(bpf_map_get_next_key(fd, NULL, &key) == 0); + key.c = -1; for (i = 0; i < MAP_SIZE; i++) assert(bpf_map_get_next_key(fd, &key, &key) == 0); assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT); @@ -499,6 +518,7 @@ static void test_map_parallel(void) errno == EEXIST); /* Check that all elements were inserted. */ + assert(bpf_map_get_next_key(fd, NULL, &key) == 0); key = -1; for (i = 0; i < MAP_SIZE; i++) assert(bpf_map_get_next_key(fd, &key, &key) == 0); @@ -518,6 +538,7 @@ static void test_map_parallel(void) /* Nothing should be left. */ key = -1; + assert(bpf_map_get_next_key(fd, NULL, &key) == -1 && errno == ENOENT); assert(bpf_map_get_next_key(fd, &key, &key) == -1 && errno == ENOENT); } -- cgit From 2f7878c06e2d227aa5c405ddde356403b83e3509 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 25 Apr 2017 07:07:18 +0000 Subject: qed: fix invalid use of sizeof in qed_alloc_qm_data() sizeof() when applied to a pointer typed expression gives the size of the pointer, not that of the pointed data. Fixes: b5a9ee7cf3be ("qed: Revise QM configuration") Signed-off-by: Wei Yongjun Acked-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index f168b718594b..ea7931b85879 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -821,7 +821,7 @@ static int qed_alloc_qm_data(struct qed_hwfn *p_hwfn) if (!qm_info->qm_vport_params) goto alloc_err; - qm_info->qm_port_params = kzalloc(sizeof(qm_info->qm_port_params) * + qm_info->qm_port_params = kzalloc(sizeof(*qm_info->qm_port_params) * p_hwfn->cdev->num_ports_in_engines, GFP_KERNEL); if (!qm_info->qm_port_params) -- cgit From b5cdae3291f7be7a34e75affe4c0ec1f7f328b64 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 18 Apr 2017 15:36:58 -0400 Subject: net: Generic XDP This provides a generic SKB based non-optimized XDP path which is used if either the driver lacks a specific XDP implementation, or the user requests it via a new IFLA_XDP_FLAGS value named XDP_FLAGS_SKB_MODE. It is arguable that perhaps I should have required something like this as part of the initial XDP feature merge. I believe this is critical for two reasons: 1) Accessibility. More people can play with XDP with less dependencies. Yes I know we have XDP support in virtio_net, but that just creates another depedency for learning how to use this facility. I wrote this to make life easier for the XDP newbies. 2) As a model for what the expected semantics are. If there is a pure generic core implementation, it serves as a semantic example for driver folks adding XDP support. One thing I have not tried to address here is the issue of XDP_PACKET_HEADROOM, thanks to Daniel for spotting that. It seems incredibly expensive to do a skb_cow(skb, XDP_PACKET_HEADROOM) or whatever even if the XDP program doesn't try to push headers at all. I think we really need the verifier to somehow propagate whether certain XDP helpers are used or not. v5: - Handle both negative and positive offset after running prog - Fix mac length in XDP_TX case (Alexei) - Use rcu_dereference_protected() in free_netdev (kbuild test robot) v4: - Fix MAC header adjustmnet before calling prog (David Ahern) - Disable LRO when generic XDP is installed (Michael Chan) - Bypass qdisc et al. on XDP_TX and record the event (Alexei) - Do not perform generic XDP on reinjected packets (DaveM) v3: - Make sure XDP program sees packet at MAC header, push back MAC header if we do XDP_TX. (Alexei) - Elide GRO when generic XDP is in use. (Alexei) - Add XDP_FLAG_SKB_MODE flag which the user can use to request generic XDP even if the driver has an XDP implementation. (Alexei) - Report whether SKB mode is in use in rtnl_xdp_fill() via XDP_FLAGS attribute. (Daniel) v2: - Add some "fall through" comments in switch statements based upon feedback from Andrew Lunn - Use RCU for generic xdp_prog, thanks to Johannes Berg. Tested-by: Andy Gospodarek Tested-by: Jesper Dangaard Brouer Tested-by: David Ahern Signed-off-by: David S. Miller --- include/linux/netdevice.h | 8 +++ include/uapi/linux/if_link.h | 4 +- net/core/dev.c | 155 +++++++++++++++++++++++++++++++++++++++++-- net/core/gro_cells.c | 2 +- net/core/rtnetlink.c | 40 ++++++----- 5 files changed, 187 insertions(+), 22 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5d5267febd56..46d220c2bf92 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1905,9 +1905,17 @@ struct net_device { struct lock_class_key *qdisc_tx_busylock; struct lock_class_key *qdisc_running_key; bool proto_down; + struct bpf_prog __rcu *xdp_prog; }; #define to_net_dev(d) container_of(d, struct net_device, dev) +static inline bool netif_elide_gro(const struct net_device *dev) +{ + if (!(dev->features & NETIF_F_GRO) || dev->xdp_prog) + return true; + return false; +} + #define NETDEV_ALIGN 32 static inline diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 8b405afb2376..633aa0276d32 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -887,7 +887,9 @@ enum { /* XDP section */ #define XDP_FLAGS_UPDATE_IF_NOEXIST (1U << 0) -#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST) +#define XDP_FLAGS_SKB_MODE (2U << 0) +#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \ + XDP_FLAGS_SKB_MODE) enum { IFLA_XDP_UNSPEC, diff --git a/net/core/dev.c b/net/core/dev.c index db6e31564d06..1b3317c026c6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -95,6 +95,7 @@ #include #include #include +#include #include #include #include @@ -4251,6 +4252,125 @@ static int __netif_receive_skb(struct sk_buff *skb) return ret; } +static struct static_key generic_xdp_needed __read_mostly; + +static int generic_xdp_install(struct net_device *dev, struct netdev_xdp *xdp) +{ + struct bpf_prog *new = xdp->prog; + int ret = 0; + + switch (xdp->command) { + case XDP_SETUP_PROG: { + struct bpf_prog *old = rtnl_dereference(dev->xdp_prog); + + rcu_assign_pointer(dev->xdp_prog, new); + if (old) + bpf_prog_put(old); + + if (old && !new) { + static_key_slow_dec(&generic_xdp_needed); + } else if (new && !old) { + static_key_slow_inc(&generic_xdp_needed); + dev_disable_lro(dev); + } + break; + } + + case XDP_QUERY_PROG: + xdp->prog_attached = !!rcu_access_pointer(dev->xdp_prog); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static u32 netif_receive_generic_xdp(struct sk_buff *skb, + struct bpf_prog *xdp_prog) +{ + struct xdp_buff xdp; + u32 act = XDP_DROP; + void *orig_data; + int hlen, off; + u32 mac_len; + + /* Reinjected packets coming from act_mirred or similar should + * not get XDP generic processing. + */ + if (skb_cloned(skb)) + return XDP_PASS; + + if (skb_linearize(skb)) + goto do_drop; + + /* The XDP program wants to see the packet starting at the MAC + * header. + */ + mac_len = skb->data - skb_mac_header(skb); + hlen = skb_headlen(skb) + mac_len; + xdp.data = skb->data - mac_len; + xdp.data_end = xdp.data + hlen; + xdp.data_hard_start = skb->data - skb_headroom(skb); + orig_data = xdp.data; + + act = bpf_prog_run_xdp(xdp_prog, &xdp); + + off = xdp.data - orig_data; + if (off > 0) + __skb_pull(skb, off); + else if (off < 0) + __skb_push(skb, -off); + + switch (act) { + case XDP_TX: + __skb_push(skb, mac_len); + /* fall through */ + case XDP_PASS: + break; + + default: + bpf_warn_invalid_xdp_action(act); + /* fall through */ + case XDP_ABORTED: + trace_xdp_exception(skb->dev, xdp_prog, act); + /* fall through */ + case XDP_DROP: + do_drop: + kfree_skb(skb); + break; + } + + return act; +} + +/* When doing generic XDP we have to bypass the qdisc layer and the + * network taps in order to match in-driver-XDP behavior. + */ +static void generic_xdp_tx(struct sk_buff *skb, struct bpf_prog *xdp_prog) +{ + struct net_device *dev = skb->dev; + struct netdev_queue *txq; + bool free_skb = true; + int cpu, rc; + + txq = netdev_pick_tx(dev, skb, NULL); + cpu = smp_processor_id(); + HARD_TX_LOCK(dev, txq, cpu); + if (!netif_xmit_stopped(txq)) { + rc = netdev_start_xmit(skb, dev, txq, 0); + if (dev_xmit_complete(rc)) + free_skb = false; + } + HARD_TX_UNLOCK(dev, txq); + if (free_skb) { + trace_xdp_exception(dev, xdp_prog, XDP_TX); + kfree_skb(skb); + } +} + static int netif_receive_skb_internal(struct sk_buff *skb) { int ret; @@ -4262,6 +4382,21 @@ static int netif_receive_skb_internal(struct sk_buff *skb) rcu_read_lock(); + if (static_key_false(&generic_xdp_needed)) { + struct bpf_prog *xdp_prog = rcu_dereference(skb->dev->xdp_prog); + + if (xdp_prog) { + u32 act = netif_receive_generic_xdp(skb, xdp_prog); + + if (act != XDP_PASS) { + rcu_read_unlock(); + if (act == XDP_TX) + generic_xdp_tx(skb, xdp_prog); + return NET_RX_DROP; + } + } + } + #ifdef CONFIG_RPS if (static_key_false(&rps_needed)) { struct rps_dev_flow voidflow, *rflow = &voidflow; @@ -4494,7 +4629,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff enum gro_result ret; int grow; - if (!(skb->dev->features & NETIF_F_GRO)) + if (netif_elide_gro(skb->dev)) goto normal; if (skb->csum_bad) @@ -6723,6 +6858,7 @@ EXPORT_SYMBOL(dev_change_proto_down); */ int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags) { + int (*xdp_op)(struct net_device *dev, struct netdev_xdp *xdp); const struct net_device_ops *ops = dev->netdev_ops; struct bpf_prog *prog = NULL; struct netdev_xdp xdp; @@ -6730,14 +6866,16 @@ int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags) ASSERT_RTNL(); - if (!ops->ndo_xdp) - return -EOPNOTSUPP; + xdp_op = ops->ndo_xdp; + if (!xdp_op || (flags & XDP_FLAGS_SKB_MODE)) + xdp_op = generic_xdp_install; + if (fd >= 0) { if (flags & XDP_FLAGS_UPDATE_IF_NOEXIST) { memset(&xdp, 0, sizeof(xdp)); xdp.command = XDP_QUERY_PROG; - err = ops->ndo_xdp(dev, &xdp); + err = xdp_op(dev, &xdp); if (err < 0) return err; if (xdp.prog_attached) @@ -6753,7 +6891,7 @@ int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags) xdp.command = XDP_SETUP_PROG; xdp.prog = prog; - err = ops->ndo_xdp(dev, &xdp); + err = xdp_op(dev, &xdp); if (err < 0 && prog) bpf_prog_put(prog); @@ -7793,6 +7931,7 @@ EXPORT_SYMBOL(alloc_netdev_mqs); void free_netdev(struct net_device *dev) { struct napi_struct *p, *n; + struct bpf_prog *prog; might_sleep(); netif_free_tx_queues(dev); @@ -7811,6 +7950,12 @@ void free_netdev(struct net_device *dev) free_percpu(dev->pcpu_refcnt); dev->pcpu_refcnt = NULL; + prog = rcu_dereference_protected(dev->xdp_prog, 1); + if (prog) { + bpf_prog_put(prog); + static_key_slow_dec(&generic_xdp_needed); + } + /* Compatibility with error handling in drivers */ if (dev->reg_state == NETREG_UNINITIALIZED) { netdev_freemem(dev); diff --git a/net/core/gro_cells.c b/net/core/gro_cells.c index c98bbfbd26b8..814e58a3ce8b 100644 --- a/net/core/gro_cells.c +++ b/net/core/gro_cells.c @@ -13,7 +13,7 @@ int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb) struct net_device *dev = skb->dev; struct gro_cell *cell; - if (!gcells->cells || skb_cloned(skb) || !(dev->features & NETIF_F_GRO)) + if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(dev)) return netif_rx(skb); cell = this_cpu_ptr(gcells->cells); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 088f9c8b4196..9031a6c8bfa7 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -896,15 +896,13 @@ static size_t rtnl_port_size(const struct net_device *dev, return port_self_size; } -static size_t rtnl_xdp_size(const struct net_device *dev) +static size_t rtnl_xdp_size(void) { size_t xdp_size = nla_total_size(0) + /* nest IFLA_XDP */ - nla_total_size(1); /* XDP_ATTACHED */ + nla_total_size(1) + /* XDP_ATTACHED */ + nla_total_size(4); /* XDP_FLAGS */ - if (!dev->netdev_ops->ndo_xdp) - return 0; - else - return xdp_size; + return xdp_size; } static noinline size_t if_nlmsg_size(const struct net_device *dev, @@ -943,7 +941,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_PORT_ID */ + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_SWITCH_ID */ + nla_total_size(IFNAMSIZ) /* IFLA_PHYS_PORT_NAME */ - + rtnl_xdp_size(dev) /* IFLA_XDP */ + + rtnl_xdp_size() /* IFLA_XDP */ + nla_total_size(1); /* IFLA_PROTO_DOWN */ } @@ -1251,23 +1249,35 @@ static int rtnl_fill_link_ifmap(struct sk_buff *skb, struct net_device *dev) static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) { - struct netdev_xdp xdp_op = {}; struct nlattr *xdp; + u32 xdp_flags = 0; + u8 val = 0; int err; - if (!dev->netdev_ops->ndo_xdp) - return 0; xdp = nla_nest_start(skb, IFLA_XDP); if (!xdp) return -EMSGSIZE; - xdp_op.command = XDP_QUERY_PROG; - err = dev->netdev_ops->ndo_xdp(dev, &xdp_op); - if (err) - goto err_cancel; - err = nla_put_u8(skb, IFLA_XDP_ATTACHED, xdp_op.prog_attached); + if (rcu_access_pointer(dev->xdp_prog)) { + xdp_flags = XDP_FLAGS_SKB_MODE; + val = 1; + } else if (dev->netdev_ops->ndo_xdp) { + struct netdev_xdp xdp_op = {}; + + xdp_op.command = XDP_QUERY_PROG; + err = dev->netdev_ops->ndo_xdp(dev, &xdp_op); + if (err) + goto err_cancel; + val = xdp_op.prog_attached; + } + err = nla_put_u8(skb, IFLA_XDP_ATTACHED, val); if (err) goto err_cancel; + if (xdp_flags) { + err = nla_put_u32(skb, IFLA_XDP_FLAGS, xdp_flags); + if (err) + goto err_cancel; + } nla_nest_end(skb, xdp); return 0; -- cgit From 5e1fc7c5ba00599ccd7096eef3e9fd3362c1230f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 25 Apr 2017 11:36:50 +0000 Subject: drivers: net: xgene-v2: Fix error return code in xge_mdio_config() Fix to return error code -ENODEV from the no PHY found error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- drivers/net/ethernet/apm/xgene-v2/mdio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/apm/xgene-v2/mdio.c b/drivers/net/ethernet/apm/xgene-v2/mdio.c index a583c6a9a5ea..f5fe3bb2e59d 100644 --- a/drivers/net/ethernet/apm/xgene-v2/mdio.c +++ b/drivers/net/ethernet/apm/xgene-v2/mdio.c @@ -135,6 +135,7 @@ int xge_mdio_config(struct net_device *ndev) phydev = phy_find_first(mdio_bus); if (!phydev) { dev_err(dev, "no PHY found\n"); + ret = -ENODEV; goto err; } phydev = phy_connect(ndev, phydev_name(phydev), -- cgit From 5311a2476ef3e7ea4bc32d4f5f9ed118be624194 Mon Sep 17 00:00:00 2001 From: Dor Shaish Date: Mon, 20 Feb 2017 16:05:57 +0200 Subject: iwlwifi: mvm: freeze 7265D and 3168 on API version 29 iwl7265D and iwl3168 are frozen on API version 29. Set the MAX API allowed level to 29 from now on. Signed-off-by: Dor Shaish Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-7000.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c index aeefd42d23ad..3b3e076571d6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c @@ -73,8 +73,8 @@ /* Highest firmware API version supported */ #define IWL7260_UCODE_API_MAX 17 #define IWL7265_UCODE_API_MAX 17 -#define IWL7265D_UCODE_API_MAX 30 -#define IWL3168_UCODE_API_MAX 30 +#define IWL7265D_UCODE_API_MAX 29 +#define IWL3168_UCODE_API_MAX 29 /* Lowest firmware API version supported */ #define IWL7260_UCODE_API_MIN 17 -- cgit From 7daa7624e3adeba0955141d95eef9d66c78e945f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 24 Feb 2017 12:02:22 +0100 Subject: iwlwifi: mvm: avoid variable shadowing Remove an extra variable 'queue' that already exists. Also, since there are no code paths that use 'queue' without intializing it, remove the unnecessary zero initialization. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index fc6d854b04b6..e502a6e6bf90 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1895,7 +1895,7 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) struct iwl_mvm_int_sta *bsta = &mvmvif->bcast_sta; static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; const u8 *baddr = _baddr; - int queue = 0; + int queue; int ret; unsigned int wdg_timeout = iwl_mvm_get_wd_timeout(mvm, vif, false, false); @@ -1940,10 +1940,11 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) * to firmware so enable queue here - after the station was added */ if (iwl_mvm_has_new_tx_api(mvm)) { - int queue = iwl_mvm_tvqm_enable_txq(mvm, vif->hw_queue[0], - bsta->sta_id, - IWL_MAX_TID_COUNT, - wdg_timeout); + queue = iwl_mvm_tvqm_enable_txq(mvm, vif->hw_queue[0], + bsta->sta_id, + IWL_MAX_TID_COUNT, + wdg_timeout); + if (vif->type == NL80211_IFTYPE_AP) mvm->probe_queue = queue; else if (vif->type == NL80211_IFTYPE_P2P_DEVICE) -- cgit From c9be849d37f1ec3bea8591f63b33bd2cb1a4bafe Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Wed, 22 Feb 2017 14:39:10 +0200 Subject: iwlwifi: pcie: support debug applying on a000 hw Allow configuring debug destination on a000 HW. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c | 4 ++++ drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 1 + drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c index 1d95512361b2..b1f43397bb59 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c @@ -251,6 +251,10 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, iwl_enable_interrupts(trans); + /* Configure debug, if exists */ + if (trans->dbg_dest_tlv) + iwl_pcie_apply_destination(trans); + /* kick FW self load */ iwl_write64(trans, CSR_CTXT_INFO_BA, trans_pcie->ctxt_info_dma_addr); iwl_write_prph(trans, UREG_CPU_INIT_RUN, 1); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index b9e9e10c32fa..30835f06a6e2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -778,6 +778,7 @@ int iwl_pcie_txq_alloc(struct iwl_trans *trans, int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr, size_t size); void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr); +void iwl_pcie_apply_destination(struct iwl_trans *trans); /* transport gen 2 exported functions */ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 91f6030529b3..5120fbcfcaf2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -831,7 +831,7 @@ static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans, return 0; } -static void iwl_pcie_apply_destination(struct iwl_trans *trans) +void iwl_pcie_apply_destination(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); const struct iwl_fw_dbg_dest_tlv *dest = trans->dbg_dest_tlv; -- cgit From 396952ee9f1bf77720cef4a15f1e3be3425430e9 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 22 Feb 2017 19:40:55 +0200 Subject: iwlwifi: mvm: don't reserve queue in TVQM mode The reserved queue is never used, save the trouble. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 6 +++++- drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 7 ++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index e502a6e6bf90..b5b7214a811f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1188,6 +1188,10 @@ static int iwl_mvm_reserve_sta_stream(struct iwl_mvm *mvm, int queue; bool using_inactive_queue = false, same_sta = false; + /* queue reserving is disabled on new TX path */ + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return 0; + /* * Check for inactive queues, so we don't reach a situation where we * can't add a STA due to a shortage in queues that doesn't really exist @@ -1387,7 +1391,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, mvm_sta->dup_data = dup_data; } - if (iwl_mvm_is_dqa_supported(mvm)) { + if (iwl_mvm_is_dqa_supported(mvm) && !iwl_mvm_has_new_tx_api(mvm)) { ret = iwl_mvm_reserve_sta_stream(mvm, sta, ieee80211_vif_type_p2p(vif)); if (ret) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 1dde05697c29..987edc98a203 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -592,15 +592,16 @@ int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id, u8 minq, u8 maxq) lockdep_assert_held(&mvm->queue_info_lock); + /* This should not be hit with new TX path */ + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) + return -ENOSPC; + /* Start by looking for a free queue */ for (i = minq; i <= maxq; i++) if (mvm->queue_info[i].hw_queue_refcount == 0 && mvm->queue_info[i].status == IWL_MVM_QUEUE_FREE) return i; - if (iwl_mvm_has_new_tx_api(mvm)) - return -ENOSPC; - /* * If no free queue found - settle for an inactive one to reconfigure * Make sure that the inactive queue either already belongs to this STA, -- cgit From d4a7e708978af611a9c3a972bd4b7f877022f98b Mon Sep 17 00:00:00 2001 From: Haim Dreyfuss Date: Thu, 2 Feb 2017 14:49:50 +0200 Subject: iwlwifi: mvm: Ignore wifi mcc update in the driver while associated Wifi mcc (mobile country code) update is forbidden while associated. Currently, FW prevents these updates and the driver is unaware to this logic. From now on, the FW sends every wifi mcc update to the driver. The driver in his turn needs to decide whether to ignore it or not, depends on the association state. Signed-off-by: Haim Dreyfuss Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 9 ++++++-- drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 29 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index ab87c0203b6f..1bb4249fc6f4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1827,6 +1827,7 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, u32 size); void iwl_mvm_reorder_timer_expired(unsigned long data); struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); +bool iwl_mvm_is_vif_assoc(struct iwl_mvm *mvm); void iwl_mvm_inactivity_check(struct iwl_mvm *mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index eade099b6dbf..283c41df622c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -817,6 +817,11 @@ void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); + if (iwl_mvm_is_vif_assoc(mvm) && notif->source_id == MCC_SOURCE_WIFI) { + IWL_DEBUG_LAR(mvm, "Ignore mcc update while associated\n"); + return; + } + if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 987edc98a203..175e927493d6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -1063,6 +1063,35 @@ struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm) return bss_iter_data.vif; } +struct iwl_sta_iter_data { + bool assoc; +}; + +static void iwl_mvm_sta_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_sta_iter_data *data = _data; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (vif->bss_conf.assoc) + data->assoc = true; +} + +bool iwl_mvm_is_vif_assoc(struct iwl_mvm *mvm) +{ + struct iwl_sta_iter_data data = { + .assoc = false, + }; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_sta_iface_iterator, + &data); + return data.assoc; +} + unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, struct ieee80211_vif *vif, bool tdls, bool cmd_q) -- cgit From e2af3fabedbfc84fcecc7337cd4f0f031a51fa63 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 22 Feb 2017 19:35:10 +0200 Subject: iwlwifi: mvm: map cab_queue to different txq_id cab_queue can now get bigger than u8, since in TVQM we will support 512 queues.. Support it by maintaining internal mapping between the actual number and mac80211 queue (IWL_MVM_DQA_GCAST_QUEUE). For pre-a000 the internal queue will be the same as the mac80211 queue. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 5 +++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 2 ++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 9e69b9d2012c..2476af904f5e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -467,6 +467,11 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, queue = IWL_MVM_DQA_GCAST_QUEUE; } + /* + * For TVQM this will be overwritten later with the FW assigned + * queue value (when queue is enabled). + */ + mvmvif->cab_queue = queue; vif->cab_queue = queue; } else { vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 1bb4249fc6f4..1dbe37f8b08c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -380,6 +380,8 @@ struct iwl_mvm_vif { bool associated; u8 ap_assoc_sta_count; + u16 cab_queue; + bool uploaded; bool ap_ibss_active; bool pm_enabled; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index b5b7214a811f..2d5d1fc10013 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -2128,7 +2128,7 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) msta->sta_id, IWL_MAX_TID_COUNT, timeout); - vif->cab_queue = queue; + mvmvif->cab_queue = queue; } else { iwl_mvm_enable_txq(mvm, vif->cab_queue, vif->cab_queue, 0, &cfg, timeout); @@ -2151,7 +2151,7 @@ int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (!iwl_mvm_is_dqa_supported(mvm)) return 0; - iwl_mvm_disable_txq(mvm, vif->cab_queue, vif->cab_queue, + iwl_mvm_disable_txq(mvm, mvmvif->cab_queue, vif->cab_queue, IWL_MAX_TID_COUNT, 0); ret = iwl_mvm_rm_sta_common(mvm, mvmvif->mcast_sta.sta_id); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 8f737f6cdd80..06bd7cc562ff 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -630,6 +630,8 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) if (queue < 0) return -1; + if (queue == info.control.vif->cab_queue) + queue = mvmvif->cab_queue; } else if (info.control.vif->type == NL80211_IFTYPE_STATION && is_multicast_ether_addr(hdr->addr1)) { u8 ap_sta_id = ACCESS_ONCE(mvmvif->ap_sta_id); -- cgit From cd50ac0f31d111cf6c2a7b953a67703d5522ef55 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Tue, 18 Apr 2017 17:39:19 +0530 Subject: cfg80211: Fix dfs state propagation for non-DFS center channel When part of a bigger bandwidth (160 MHz) channel falls in DFS channel range it is possible that the center frequency may not necessarily be a radar channel. Remove the sanity check on channel flag for IEEE80211_CHAN_RADAR in regulatory_propagate_dfs_state(), this should fix the dfs state propagation for non-DFS center freq which has DFS channels in it's bandwidth, should also fix unnecessary WARN_ON() spam in regulatory_propagate_dfs_state(). Fixes: 8976672736d6 ("cfg80211: Share Channel DFS state across wiphys of same DFS domain") Reported-by: Johannes Berg Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Johannes Berg --- net/wireless/reg.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index a38f315819cd..5fae296a6a58 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -3244,9 +3244,6 @@ void regulatory_propagate_dfs_state(struct wiphy *wiphy, if (WARN_ON(!cfg80211_chandef_valid(chandef))) return; - if (WARN_ON(!(chandef->chan->flags & IEEE80211_CHAN_RADAR))) - return; - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { if (wiphy == &rdev->wiphy) continue; -- cgit From 127f60bfa98f30f7f47ae362b9807f0d223cbcc2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 13 Apr 2017 15:50:27 +0200 Subject: mac80211: rewrite monitor mode delivery logic The monitor mode delivery logic makes it hard to add any kind of filtering in an efficient way, because the monitor SKB is created first and then passed to all interfaces. Rewrite the logic to create the monitor SKB the first time it's actually needed, and then keep delivering it. Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 151 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 59 deletions(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 2142074d9fb0..0094f3c0af64 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -533,6 +533,59 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, } } +static struct sk_buff * +ieee80211_make_monitor_skb(struct ieee80211_local *local, + struct sk_buff **origskb, + struct ieee80211_rate *rate, + int rtap_vendor_space, bool use_origskb) +{ + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(*origskb); + int rt_hdrlen, needed_headroom; + struct sk_buff *skb; + + /* room for the radiotap header based on driver features */ + rt_hdrlen = ieee80211_rx_radiotap_hdrlen(local, status, *origskb); + needed_headroom = rt_hdrlen - rtap_vendor_space; + + if (use_origskb) { + /* only need to expand headroom if necessary */ + skb = *origskb; + *origskb = NULL; + + /* + * This shouldn't trigger often because most devices have an + * RX header they pull before we get here, and that should + * be big enough for our radiotap information. We should + * probably export the length to drivers so that we can have + * them allocate enough headroom to start with. + */ + if (skb_headroom(skb) < needed_headroom && + pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) { + dev_kfree_skb(skb); + return NULL; + } + } else { + /* + * Need to make a copy and possibly remove radiotap header + * and FCS from the original. + */ + skb = skb_copy_expand(*origskb, needed_headroom, 0, GFP_ATOMIC); + + if (!skb) + return NULL; + } + + /* prepend radiotap information */ + ieee80211_add_rx_radiotap_header(local, skb, rate, rt_hdrlen, true); + + skb_reset_mac_header(skb); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + + return skb; +} + /* * This function copies a received frame to all monitor interfaces and * returns a cleaned-up SKB that no longer includes the FCS nor the @@ -544,13 +597,12 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb); struct ieee80211_sub_if_data *sdata; - int rt_hdrlen, needed_headroom; - struct sk_buff *skb, *skb2; - struct net_device *prev_dev = NULL; + struct sk_buff *monskb = NULL; int present_fcs_len = 0; unsigned int rtap_vendor_space = 0; struct ieee80211_sub_if_data *monitor_sdata = rcu_dereference(local->monitor_sdata); + bool only_monitor = false; if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) { struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data; @@ -583,9 +635,11 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, return NULL; } + only_monitor = should_drop_frame(origskb, present_fcs_len, + rtap_vendor_space); + if (!local->monitors || (status->flag & RX_FLAG_SKIP_MONITOR)) { - if (should_drop_frame(origskb, present_fcs_len, - rtap_vendor_space)) { + if (only_monitor) { dev_kfree_skb(origskb); return NULL; } @@ -597,67 +651,46 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_vendor_space); - /* room for the radiotap header based on driver features */ - rt_hdrlen = ieee80211_rx_radiotap_hdrlen(local, status, origskb); - needed_headroom = rt_hdrlen - rtap_vendor_space; - - if (should_drop_frame(origskb, present_fcs_len, rtap_vendor_space)) { - /* only need to expand headroom if necessary */ - skb = origskb; - origskb = NULL; - - /* - * This shouldn't trigger often because most devices have an - * RX header they pull before we get here, and that should - * be big enough for our radiotap information. We should - * probably export the length to drivers so that we can have - * them allocate enough headroom to start with. - */ - if (skb_headroom(skb) < needed_headroom && - pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) { - dev_kfree_skb(skb); - return NULL; - } - } else { - /* - * Need to make a copy and possibly remove radiotap header - * and FCS from the original. - */ - skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC); - remove_monitor_info(origskb, present_fcs_len, - rtap_vendor_space); - - if (!skb) - return origskb; - } - - /* prepend radiotap information */ - ieee80211_add_rx_radiotap_header(local, skb, rate, rt_hdrlen, true); - - skb_reset_mac_header(skb); - skb->ip_summed = CHECKSUM_UNNECESSARY; - skb->pkt_type = PACKET_OTHERHOST; - skb->protocol = htons(ETH_P_802_2); - list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) { - if (prev_dev) { - skb2 = skb_clone(skb, GFP_ATOMIC); - if (skb2) { - skb2->dev = prev_dev; - netif_receive_skb(skb2); + bool last_monitor = list_is_last(&sdata->u.mntr.list, + &local->mon_list); + + if (!monskb) + monskb = ieee80211_make_monitor_skb(local, &origskb, + rate, + rtap_vendor_space, + only_monitor && + last_monitor); + + if (monskb) { + struct sk_buff *skb; + + if (last_monitor) { + skb = monskb; + monskb = NULL; + } else { + skb = skb_clone(monskb, GFP_ATOMIC); + } + + if (skb) { + skb->dev = sdata->dev; + ieee80211_rx_stats(skb->dev, skb->len); + netif_receive_skb(skb); } } - prev_dev = sdata->dev; - ieee80211_rx_stats(sdata->dev, skb->len); + if (last_monitor) + break; } - if (prev_dev) { - skb->dev = prev_dev; - netif_receive_skb(skb); - } else - dev_kfree_skb(skb); + /* this happens if last_monitor was erroneously false */ + dev_kfree_skb(monskb); + + /* ditto */ + if (!origskb) + return NULL; + remove_monitor_info(origskb, present_fcs_len, rtap_vendor_space); return origskb; } -- cgit From 6862fcee2feb3c5334239377160d463aee3da932 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 22 Feb 2017 19:34:17 +0200 Subject: iwlwifi: mvm: move internally to use bigger INVALID_TXQ We can't use IEEE80211_INVAL_HW_QUEUE to mark a queue as invalid since 255 will be a valid value for a TVQM queue index. Use IWL_MVM_INVALID_QUEUE instead for accessing txq_id. reserved_queue can stay a u8 since reserved_queue is not used when TVQM is enabled. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 12 ++++++------ drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 6 +++--- drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index f35f295d0c81..8ee5cf1419f4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2363,7 +2363,7 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA) continue; - if (tid_data->txq_id == IEEE80211_INVAL_HW_QUEUE) + if (tid_data->txq_id == IWL_MVM_INVALID_QUEUE) continue; __set_bit(tid_data->txq_id, &txqs); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 1dbe37f8b08c..ab4864f2f4cb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -717,6 +717,8 @@ enum iwl_mvm_queue_status { }; #define IWL_MVM_DQA_QUEUE_TIMEOUT (5 * HZ) +#define IWL_MVM_INVALID_QUEUE 0xFFFF + #define IWL_MVM_NUM_CIPHERS 10 #ifdef CONFIG_ACPI diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 2d5d1fc10013..99fa6b17a4a0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -464,7 +464,7 @@ static int iwl_mvm_remove_sta_queue_marking(struct iwl_mvm *mvm, int queue) for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { if (mvmsta->tid_data[tid].state == IWL_AGG_ON) disable_agg_tids |= BIT(tid); - mvmsta->tid_data[tid].txq_id = IEEE80211_INVAL_HW_QUEUE; + mvmsta->tid_data[tid].txq_id = IWL_MVM_INVALID_QUEUE; } mvmsta->tfd_queue_msk &= ~BIT(queue); /* Don't use this queue anymore */ @@ -1086,7 +1086,7 @@ static void iwl_mvm_tx_deferred_stream(struct iwl_mvm *mvm, ac = iwl_mvm_tid_to_ac_queue(tid); mac_queue = IEEE80211_SKB_CB(skb)->hw_queue; - if (tid_data->txq_id == IEEE80211_INVAL_HW_QUEUE && + if (tid_data->txq_id == IWL_MVM_INVALID_QUEUE && iwl_mvm_sta_alloc_queue(mvm, sta, ac, tid, hdr)) { IWL_ERR(mvm, "Can't alloc TXQ for sta %d tid %d - dropping frame\n", @@ -1267,7 +1267,7 @@ static void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm, int ac; u8 mac_queue; - if (txq_id == IEEE80211_INVAL_HW_QUEUE) + if (txq_id == IWL_MVM_INVALID_QUEUE) continue; skb_queue_head_init(&tid_data->deferred_tx_frames); @@ -1375,7 +1375,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, * Mark all queues for this STA as unallocated and defer TX * frames until the queue is allocated */ - mvm_sta->tid_data[i].txq_id = IEEE80211_INVAL_HW_QUEUE; + mvm_sta->tid_data[i].txq_id = IWL_MVM_INVALID_QUEUE; skb_queue_head_init(&mvm_sta->tid_data[i].deferred_tx_frames); } mvm_sta->deferred_traffic_tid_map = 0; @@ -1574,13 +1574,13 @@ static void iwl_mvm_disable_sta_queues(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); for (i = 0; i < ARRAY_SIZE(mvm_sta->tid_data); i++) { - if (mvm_sta->tid_data[i].txq_id == IEEE80211_INVAL_HW_QUEUE) + if (mvm_sta->tid_data[i].txq_id == IWL_MVM_INVALID_QUEUE) continue; ac = iwl_mvm_tid_to_ac_queue(i); iwl_mvm_disable_txq(mvm, mvm_sta->tid_data[i].txq_id, vif->hw_queue[ac], i, 0); - mvm_sta->tid_data[i].txq_id = IEEE80211_INVAL_HW_QUEUE; + mvm_sta->tid_data[i].txq_id = IWL_MVM_INVALID_QUEUE; } } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 06bd7cc562ff..5e305d391ffe 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -920,7 +920,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, __le16 fc; u16 seq_number = 0; u8 tid = IWL_MAX_TID_COUNT; - u8 txq_id = info->hw_queue; + u16 txq_id = info->hw_queue; bool is_ampdu = false; int hdrlen; @@ -990,11 +990,11 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM); /* Check if TXQ needs to be allocated or re-activated */ - if (unlikely(txq_id == IEEE80211_INVAL_HW_QUEUE || + if (unlikely(txq_id == IWL_MVM_INVALID_QUEUE || !mvmsta->tid_data[tid].is_tid_active) && iwl_mvm_is_dqa_supported(mvm)) { /* If TXQ needs to be allocated... */ - if (txq_id == IEEE80211_INVAL_HW_QUEUE) { + if (txq_id == IWL_MVM_INVALID_QUEUE) { iwl_mvm_tx_add_stream(mvm, mvmsta, tid, skb); /* diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 175e927493d6..0e594899c615 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -1203,7 +1203,7 @@ static void iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm, for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { int mac_queue = mvmsta->vif->hw_queue[tid_to_mac80211_ac[tid]]; - mvmsta->tid_data[tid].txq_id = IEEE80211_INVAL_HW_QUEUE; + mvmsta->tid_data[tid].txq_id = IWL_MVM_INVALID_QUEUE; mvm->queue_info[queue].hw_queue_to_mac80211 &= ~BIT(mac_queue); mvm->queue_info[queue].hw_queue_refcount--; mvm->queue_info[queue].tid_bitmap &= ~BIT(tid); -- cgit From e609474c890d72823c6d3e3e636871a3ed8f453c Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 23 Feb 2017 22:42:01 +0200 Subject: iwlwifi: mvm: remove color definition We use the full station field as sta_id, and never use the color. FW are about to clean the color out, so those defines are incorrect now (and were redundant before). Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h index e79df1c53d68..a20ac4d9e63e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h @@ -214,20 +214,6 @@ enum iwl_sta_sleep_flag { STA_SLEEP_STATE_MOREDATA = BIT(2), }; -/* STA ID and color bits definitions */ -#define STA_ID_SEED (0x0f) -#define STA_ID_POS (0) -#define STA_ID_MSK (STA_ID_SEED << STA_ID_POS) - -#define STA_COLOR_SEED (0x7) -#define STA_COLOR_POS (4) -#define STA_COLOR_MSK (STA_COLOR_SEED << STA_COLOR_POS) - -#define STA_ID_N_COLOR_GET_COLOR(id_n_color) \ - (((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS) -#define STA_ID_N_COLOR_GET_ID(id_n_color) \ - (((id_n_color) & STA_ID_MSK) >> STA_ID_POS) - #define STA_KEY_MAX_NUM (16) #define STA_KEY_IDX_INVALID (0xff) #define STA_KEY_MAX_DATA_KEY_NUM (4) -- cgit From 0fc9bf7add0f2d1a370c688dabc9846ad187c044 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 22 Feb 2017 12:08:19 +0200 Subject: iwlwifi: mvm: use defines instead of variables for shared dwell times Most of the dwells are constant across different scan types. Use defines instead of depending on scan type. This is needed as preparation to having different scan type per band. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 55 ++++++++++----------------- 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index ca6f3a82b277..8d1b994ae79f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -81,44 +81,30 @@ enum iwl_mvm_traffic_load { IWL_MVM_TRAFFIC_HIGH, }; +#define IWL_SCAN_DWELL_ACTIVE 10 +#define IWL_SCAN_DWELL_PASSIVE 110 +#define IWL_SCAN_DWELL_FRAGMENTED 44 +#define IWL_SCAN_DWELL_EXTENDED 90 + struct iwl_mvm_scan_timing_params { - u32 dwell_active; - u32 dwell_passive; - u32 dwell_fragmented; - u32 dwell_extended; u32 suspend_time; u32 max_out_time; }; static struct iwl_mvm_scan_timing_params scan_timing[] = { [IWL_SCAN_TYPE_UNASSOC] = { - .dwell_active = 10, - .dwell_passive = 110, - .dwell_fragmented = 44, - .dwell_extended = 90, .suspend_time = 0, .max_out_time = 0, }, [IWL_SCAN_TYPE_WILD] = { - .dwell_active = 10, - .dwell_passive = 110, - .dwell_fragmented = 44, - .dwell_extended = 90, .suspend_time = 30, .max_out_time = 120, }, [IWL_SCAN_TYPE_MILD] = { - .dwell_active = 10, - .dwell_passive = 110, - .dwell_fragmented = 44, - .dwell_extended = 90, .suspend_time = 120, .max_out_time = 120, }, [IWL_SCAN_TYPE_FRAGMENTED] = { - .dwell_active = 10, - .dwell_passive = 110, - .dwell_fragmented = 44, .suspend_time = 95, .max_out_time = 44, }, @@ -724,10 +710,10 @@ static void iwl_mvm_scan_lmac_dwell(struct iwl_mvm *mvm, struct iwl_scan_req_lmac *cmd, struct iwl_mvm_scan_params *params) { - cmd->active_dwell = scan_timing[params->type].dwell_active; - cmd->passive_dwell = scan_timing[params->type].dwell_passive; - cmd->fragmented_dwell = scan_timing[params->type].dwell_fragmented; - cmd->extended_dwell = scan_timing[params->type].dwell_extended; + cmd->active_dwell = IWL_SCAN_DWELL_ACTIVE; + cmd->passive_dwell = IWL_SCAN_DWELL_PASSIVE; + cmd->fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED; + cmd->extended_dwell = IWL_SCAN_DWELL_EXTENDED; cmd->max_out_time = cpu_to_le32(scan_timing[params->type].max_out_time); cmd->suspend_time = cpu_to_le32(scan_timing[params->type].suspend_time); cmd->scan_prio = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); @@ -925,13 +911,12 @@ static __le32 iwl_mvm_scan_config_rates(struct iwl_mvm *mvm) } static void iwl_mvm_fill_scan_dwell(struct iwl_mvm *mvm, - struct iwl_scan_dwell *dwell, - struct iwl_mvm_scan_timing_params *timing) + struct iwl_scan_dwell *dwell) { - dwell->active = timing->dwell_active; - dwell->passive = timing->dwell_passive; - dwell->fragmented = timing->dwell_fragmented; - dwell->extended = timing->dwell_extended; + dwell->active = IWL_SCAN_DWELL_ACTIVE; + dwell->passive = IWL_SCAN_DWELL_PASSIVE; + dwell->fragmented = IWL_SCAN_DWELL_FRAGMENTED; + dwell->extended = IWL_SCAN_DWELL_EXTENDED; } static void iwl_mvm_fill_channels(struct iwl_mvm *mvm, u8 *channels) @@ -960,7 +945,7 @@ static void iwl_mvm_fill_scan_config_v1(struct iwl_mvm *mvm, void *config, cfg->out_of_channel_time = cpu_to_le32(scan_timing[type].max_out_time); cfg->suspend_time = cpu_to_le32(scan_timing[type].suspend_time); - iwl_mvm_fill_scan_dwell(mvm, &cfg->dwell, &scan_timing[type]); + iwl_mvm_fill_scan_dwell(mvm, &cfg->dwell); memcpy(&cfg->mac_addr, &mvm->addresses[0].addr, ETH_ALEN); @@ -991,7 +976,7 @@ static void iwl_mvm_fill_scan_config(struct iwl_mvm *mvm, void *config, cpu_to_le32(scan_timing[type].max_out_time); } - iwl_mvm_fill_scan_dwell(mvm, &cfg->dwell, &scan_timing[type]); + iwl_mvm_fill_scan_dwell(mvm, &cfg->dwell); memcpy(&cfg->mac_addr, &mvm->addresses[0].addr, ETH_ALEN); @@ -1095,11 +1080,11 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, cmd->passive_dwell = params->measurement_dwell; cmd->extended_dwell = params->measurement_dwell; } else { - cmd->active_dwell = timing->dwell_active; - cmd->passive_dwell = timing->dwell_passive; - cmd->extended_dwell = timing->dwell_extended; + cmd->active_dwell = IWL_SCAN_DWELL_ACTIVE; + cmd->passive_dwell = IWL_SCAN_DWELL_PASSIVE; + cmd->extended_dwell = IWL_SCAN_DWELL_EXTENDED; } - cmd->fragmented_dwell = timing->dwell_fragmented; + cmd->fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED; if (iwl_mvm_has_new_tx_api(mvm)) { cmd->v6.scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); -- cgit From b0fa818e6c5f48bfbc3dfe1317754390b88446c1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 28 Feb 2017 16:42:10 +0100 Subject: iwlwifi: pcie: remove superfluous trans->dev assignment This struct member is already assigned in the previous call to iwl_trans_alloc(), so assigning the same value again is superfluous - remove it. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 5120fbcfcaf2..c551ba8809db 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2976,7 +2976,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, * PCI Tx retries from interfering with C3 CPU state */ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); - trans->dev = &pdev->dev; trans_pcie->pci_dev = pdev; iwl_disable_interrupts(trans); -- cgit From 53d515ec677a1903bb507b81dc12ae6922846c1b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 28 Feb 2017 16:44:16 +0100 Subject: iwlwifi: don't leak memory on allocation failure If we fail to allocate the small chunk of memory for the pieces of the firmware file, we leak the whole firmware image instead... Since the allocation failure is really unlikely, just bail out at that point instead. Remove the error message at the label since we now (and actually have been) use it for various reasons. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 212fb8d5c064..98a03a4615d6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1282,7 +1282,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) pieces = kzalloc(sizeof(*pieces), GFP_KERNEL); if (!pieces) - return; + goto out_free_fw; if (!ucode_raw) goto try_again; @@ -1512,17 +1512,18 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) goto free; out_free_fw: - IWL_ERR(drv, "failed to allocate pci memory\n"); iwl_dealloc_ucode(drv); release_firmware(ucode_raw); out_unbind: complete(&drv->request_firmware_complete); device_release_driver(drv->trans->dev); free: - for (i = 0; i < ARRAY_SIZE(pieces->img); i++) - kfree(pieces->img[i].sec); - kfree(pieces->dbg_mem_tlv); - kfree(pieces); + if (pieces) { + for (i = 0; i < ARRAY_SIZE(pieces->img); i++) + kfree(pieces->img[i].sec); + kfree(pieces->dbg_mem_tlv); + kfree(pieces); + } } struct iwl_drv *iwl_drv_start(struct iwl_trans *trans) -- cgit From 06a1e85e66bac2b65b4953124b4f1b2c4aebb39f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 28 Feb 2017 22:45:25 +0100 Subject: iwlwifi: remove module loading failure message When CONFIG_DEBUG_TEST_DRIVER_REMOVE is set, iwlwifi crashes when the opmode module cannot be loaded, due to completing the completion before using drv->dev, which can then already be freed. Fix this by removing the (fairly useless) message. Moving the completion later causes a deadlock instead, so that's not an option. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 98a03a4615d6..5cfacb0bca84 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1494,7 +1494,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) * or hangs loading. */ if (load_module) { - err = request_module("%s", op->name); + request_module("%s", op->name); #ifdef CONFIG_IWLWIFI_OPMODE_MODULAR if (err) IWL_ERR(drv, -- cgit From b9410b186f7129bde5dd5b20475105f17fedb452 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Tue, 28 Feb 2017 17:15:47 +0200 Subject: iwlwifi: gen2: support nmi triggering from host For gen2 there is a new register. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-io.c | 3 +++ drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index 0f893ae6e715..9c8b09cf1f7b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -246,6 +246,9 @@ void iwl_force_nmi(struct iwl_trans *trans) DEVICE_SET_NMI_VAL_DRV); iwl_write_prph(trans, DEVICE_SET_NMI_REG, DEVICE_SET_NMI_VAL_HW); + } else if (trans->cfg->gen2) { + iwl_write_prph(trans, UREG_NIC_SET_NMI_DRIVER, + DEVICE_SET_NMI_8000_VAL); } else { iwl_write_prph(trans, DEVICE_SET_NMI_8000_REG, DEVICE_SET_NMI_8000_VAL); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index f832e58e0ef9..306bc967742e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -114,6 +114,7 @@ #define DEVICE_SET_NMI_VAL_DRV BIT(7) #define DEVICE_SET_NMI_8000_REG 0x00a01c24 #define DEVICE_SET_NMI_8000_VAL 0x1000000 +#define UREG_NIC_SET_NMI_DRIVER 0x00a05c10 /* Shared registers (0x0..0x3ff, via target indirect or periphery */ #define SHR_BASE 0x00a10000 -- cgit From 34e10860ae8dc8ab675ba84529c280ff922855e3 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 23 Feb 2017 13:15:07 +0200 Subject: iwlwifi: mvm: remove references to queue_info in new TX path Most of the fields aren't needed in new TX path. Enlarging the struct to 512 queues will consume a lot of memory. Remove all references to the struct in the new TX path. Move mac80211 queue mapping outside, since it will be needed per queue for TVQM mode. Add warning in paths that shouldn't be hit. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 +- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 4 +- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 96 +++++++++++++++----------- drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 5 +- drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 58 +++++++++------- 5 files changed, 96 insertions(+), 71 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index ab4864f2f4cb..204664b578df 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -788,9 +788,9 @@ struct iwl_mvm { u64 on_time_scan; } radio_stats, accu_radio_stats; + u8 hw_queue_to_mac80211[IWL_MAX_HW_QUEUES]; + struct { - /* Map to HW queue */ - u32 hw_queue_to_mac80211; u8 hw_queue_refcount; u8 ra_sta_id; /* The RA this queue is mapped to, if exists */ bool reserved; /* Is this the TXQ reserved for a STA */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 363930510a6e..9ffff6ed8133 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1044,7 +1044,7 @@ static void iwl_mvm_stop_sw_queue(struct iwl_op_mode *op_mode, int hw_queue) unsigned long mq; spin_lock_bh(&mvm->queue_info_lock); - mq = mvm->queue_info[hw_queue].hw_queue_to_mac80211; + mq = mvm->hw_queue_to_mac80211[hw_queue]; spin_unlock_bh(&mvm->queue_info_lock); iwl_mvm_stop_mac_queues(mvm, mq); @@ -1074,7 +1074,7 @@ static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int hw_queue) unsigned long mq; spin_lock_bh(&mvm->queue_info_lock); - mq = mvm->queue_info[hw_queue].hw_queue_to_mac80211; + mq = mvm->hw_queue_to_mac80211[hw_queue]; spin_unlock_bh(&mvm->queue_info_lock); iwl_mvm_start_mac_queues(mvm, mq); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 99fa6b17a4a0..3be7fc01b19f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -644,7 +644,7 @@ int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid, cmd.sta_id = mvm->queue_info[queue].ra_sta_id; cmd.tx_fifo = iwl_mvm_ac_to_tx_fifo[mvm->queue_info[queue].mac80211_ac]; cmd.tid = mvm->queue_info[queue].txq_tid; - mq = mvm->queue_info[queue].hw_queue_to_mac80211; + mq = mvm->hw_queue_to_mac80211[queue]; shared_queue = (mvm->queue_info[queue].hw_queue_refcount > 1); spin_unlock_bh(&mvm->queue_info_lock); @@ -732,10 +732,6 @@ static int iwl_mvm_sta_alloc_queue_tvqm(struct iwl_mvm *mvm, mvmsta->tfd_queue_msk |= BIT(queue); spin_unlock_bh(&mvmsta->lock); - spin_lock_bh(&mvm->queue_info_lock); - mvm->queue_info[queue].status = IWL_MVM_QUEUE_READY; - spin_unlock_bh(&mvm->queue_info_lock); - return 0; } @@ -1131,8 +1127,12 @@ void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk) mutex_lock(&mvm->mutex); + /* No queue reconfiguration in TVQM mode */ + if (iwl_mvm_has_new_tx_api(mvm)) + goto alloc_queues; + /* Reconfigure queues requiring reconfiguation */ - for (queue = 0; queue < IWL_MAX_HW_QUEUES; queue++) { + for (queue = 0; queue < ARRAY_SIZE(mvm->queue_info); queue++) { bool reconfig; bool change_owner; @@ -1160,6 +1160,7 @@ void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk) iwl_mvm_change_queue_owner(mvm, queue); } +alloc_queues: /* Go over all stations with deferred traffic */ for_each_set_bit(sta_id, mvm->sta_deferred_frames, IWL_MVM_STATION_COUNT) { @@ -1298,9 +1299,8 @@ static void iwl_mvm_realloc_queues_after_restart(struct iwl_mvm *mvm, iwl_mvm_enable_txq(mvm, txq_id, mac_queue, seq, &cfg, wdg_timeout); + mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_READY; } - - mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_READY; } atomic_set(&mvm->pending_frames[mvm_sta->sta_id], 0); @@ -2492,10 +2492,18 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * one and mark it as reserved * 3. In DQA mode, but no traffic yet on this TID: same treatment as in * non-DQA mode, since the TXQ hasn't yet been allocated + * Don't support case 3 for new TX path as it is not expected to happen + * and aggregation will be offloaded soon anyway */ txq_id = mvmsta->tid_data[tid].txq_id; - if (iwl_mvm_is_dqa_supported(mvm) && - unlikely(mvm->queue_info[txq_id].status == IWL_MVM_QUEUE_SHARED)) { + if (iwl_mvm_has_new_tx_api(mvm)) { + if (txq_id == IWL_MVM_INVALID_QUEUE) { + ret = -ENXIO; + goto release_locks; + } + } else if (iwl_mvm_is_dqa_supported(mvm) && + unlikely(mvm->queue_info[txq_id].status == + IWL_MVM_QUEUE_SHARED)) { ret = -ENXIO; IWL_DEBUG_TX_QUEUES(mvm, "Can't start tid %d agg on shared queue!\n", @@ -2591,6 +2599,20 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, tid_data->amsdu_in_ampdu_allowed = amsdu; spin_unlock_bh(&mvmsta->lock); + if (iwl_mvm_has_new_tx_api(mvm)) { + /* + * If no queue iwl_mvm_sta_tx_agg_start() would have failed so + * no need to check queue's status + */ + if (buf_size < mvmsta->max_agg_bufsize) + return -ENOTSUPP; + + ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); + if (ret) + return -EIO; + goto out; + } + cfg.fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; spin_lock_bh(&mvm->queue_info_lock); @@ -2608,13 +2630,6 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * changed from current (become smaller) */ if (!alloc_queue && buf_size < mvmsta->max_agg_bufsize) { - /* - * On new TX API rs and BA manager are offloaded. - * For now though, just don't support being reconfigured - */ - if (iwl_mvm_has_new_tx_api(mvm)) - return -ENOTSUPP; - /* * If reconfiguring an existing queue, it first must be * drained @@ -2655,6 +2670,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, mvm->queue_info[queue].status = IWL_MVM_QUEUE_READY; spin_unlock_bh(&mvm->queue_info_lock); +out: /* * Even though in theory the peer could have different * aggregation reorder buffer sizes for different sessions, @@ -2672,6 +2688,27 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false); } +static void iwl_mvm_unreserve_agg_queue(struct iwl_mvm *mvm, + struct iwl_mvm_sta *mvmsta, + u16 txq_id) +{ + if (iwl_mvm_has_new_tx_api(mvm)) + return; + + spin_lock_bh(&mvm->queue_info_lock); + /* + * The TXQ is marked as reserved only if no traffic came through yet + * This means no traffic has been sent on this TID (agg'd or not), so + * we no longer have use for the queue. Since it hasn't even been + * allocated through iwl_mvm_enable_txq, so we can just mark it back as + * free. + */ + if (mvm->queue_info[txq_id].status == IWL_MVM_QUEUE_RESERVED) + mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_FREE; + + spin_unlock_bh(&mvm->queue_info_lock); +} + int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid) { @@ -2698,18 +2735,7 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, mvmsta->agg_tids &= ~BIT(tid); - spin_lock_bh(&mvm->queue_info_lock); - /* - * The TXQ is marked as reserved only if no traffic came through yet - * This means no traffic has been sent on this TID (agg'd or not), so - * we no longer have use for the queue. Since it hasn't even been - * allocated through iwl_mvm_enable_txq, so we can just mark it back as - * free. - */ - if (mvm->queue_info[txq_id].status == IWL_MVM_QUEUE_RESERVED) - mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_FREE; - - spin_unlock_bh(&mvm->queue_info_lock); + iwl_mvm_unreserve_agg_queue(mvm, mvmsta, txq_id); switch (tid_data->state) { case IWL_AGG_ON: @@ -2789,17 +2815,7 @@ 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); - spin_lock_bh(&mvm->queue_info_lock); - /* - * The TXQ is marked as reserved only if no traffic came through yet - * This means no traffic has been sent on this TID (agg'd or not), so - * we no longer have use for the queue. Since it hasn't even been - * allocated through iwl_mvm_enable_txq, so we can just mark it back as - * free. - */ - if (mvm->queue_info[txq_id].status == IWL_MVM_QUEUE_RESERVED) - mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_FREE; - spin_unlock_bh(&mvm->queue_info_lock); + iwl_mvm_unreserve_agg_queue(mvm, mvmsta, txq_id); if (old_state >= IWL_AGG_ON) { iwl_mvm_drain_sta(mvm, mvmsta, true); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 5e305d391ffe..bcaceb64a6e8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1006,6 +1006,9 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, return 0; } + /* queue should always be active in new TX path */ + WARN_ON(iwl_mvm_has_new_tx_api(mvm)); + /* If we are here - TXQ exists and needs to be re-activated */ spin_lock(&mvm->queue_info_lock); mvm->queue_info[txq_id].status = IWL_MVM_QUEUE_READY; @@ -1016,7 +1019,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, txq_id); } - if (iwl_mvm_is_dqa_supported(mvm)) { + if (iwl_mvm_is_dqa_supported(mvm) && !iwl_mvm_has_new_tx_api(mvm)) { /* Keep track of the time of the last frame for this RA/TID */ mvm->queue_info[txq_id].last_frame_time[tid] = jiffies; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 0e594899c615..8f4f176e204e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -671,7 +671,8 @@ static bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm, int queue, if (mvm->queue_info[queue].hw_queue_refcount > 0) enable_queue = false; - mvm->queue_info[queue].hw_queue_to_mac80211 |= BIT(mac80211_queue); + mvm->hw_queue_to_mac80211[queue] |= BIT(mac80211_queue); + mvm->queue_info[queue].hw_queue_refcount++; mvm->queue_info[queue].tid_bitmap |= BIT(tid); mvm->queue_info[queue].ra_sta_id = sta_id; @@ -689,7 +690,7 @@ static bool iwl_mvm_update_txq_mapping(struct iwl_mvm *mvm, int queue, 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); + mvm->hw_queue_to_mac80211[queue]); spin_unlock_bh(&mvm->queue_info_lock); @@ -721,7 +722,10 @@ int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, int mac80211_queue, IWL_DEBUG_TX_QUEUES(mvm, "Enabling TXQ #%d for sta %d tid %d\n", queue, sta_id, tid); - iwl_mvm_update_txq_mapping(mvm, queue, mac80211_queue, sta_id, tid); + mvm->hw_queue_to_mac80211[queue] |= BIT(mac80211_queue); + IWL_DEBUG_TX_QUEUES(mvm, + "Enabling TXQ #%d (mac80211 map:0x%x)\n", + queue, mvm->hw_queue_to_mac80211[queue]); return queue; } @@ -765,6 +769,17 @@ int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, .action = SCD_CFG_DISABLE_QUEUE, }; bool remove_mac_queue = true; + int ret; + + if (iwl_mvm_has_new_tx_api(mvm)) { + spin_lock_bh(&mvm->queue_info_lock); + mvm->hw_queue_to_mac80211[queue] &= ~BIT(mac80211_queue); + spin_unlock_bh(&mvm->queue_info_lock); + + iwl_trans_txq_free(mvm->trans, queue); + + return 0; + } spin_lock_bh(&mvm->queue_info_lock); @@ -792,7 +807,7 @@ int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, } if (remove_mac_queue) - mvm->queue_info[queue].hw_queue_to_mac80211 &= + mvm->hw_queue_to_mac80211[queue] &= ~BIT(mac80211_queue); mvm->queue_info[queue].hw_queue_refcount--; @@ -805,7 +820,7 @@ int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, "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); + mvm->hw_queue_to_mac80211[queue]); /* If the queue is still enabled - nothing left to do in this func */ if (cmd.action == SCD_CFG_ENABLE_QUEUE) { @@ -819,39 +834,30 @@ int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, /* 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, + mvm->hw_queue_to_mac80211[queue], "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->hw_queue_to_mac80211[queue], 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; + mvm->hw_queue_to_mac80211[queue] = 0; /* Regardless if this is a reserved TXQ for a STA - mark it as false */ mvm->queue_info[queue].reserved = false; spin_unlock_bh(&mvm->queue_info_lock); - if (iwl_mvm_has_new_tx_api(mvm)) { - iwl_trans_txq_free(mvm->trans, queue); - } else { - int ret; - - iwl_trans_txq_disable(mvm->trans, queue, false); - ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, - sizeof(struct iwl_scd_txq_cfg_cmd), - &cmd); + iwl_trans_txq_disable(mvm->trans, queue, false); + ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, + sizeof(struct iwl_scd_txq_cfg_cmd), &cmd); - if (ret) - IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n", - queue, ret); - return ret; - } - - return 0; + if (ret) + IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n", + queue, ret); + return ret; } /** @@ -1204,7 +1210,7 @@ static void iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm, int mac_queue = mvmsta->vif->hw_queue[tid_to_mac80211_ac[tid]]; mvmsta->tid_data[tid].txq_id = IWL_MVM_INVALID_QUEUE; - mvm->queue_info[queue].hw_queue_to_mac80211 &= ~BIT(mac_queue); + mvm->hw_queue_to_mac80211[queue] &= ~BIT(mac_queue); mvm->queue_info[queue].hw_queue_refcount--; mvm->queue_info[queue].tid_bitmap &= ~BIT(tid); mvmsta->tid_data[tid].is_tid_active = false; @@ -1224,7 +1230,7 @@ static void iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm, */ tid_bitmap = mvm->queue_info[queue].tid_bitmap; for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { - mvm->queue_info[queue].hw_queue_to_mac80211 |= + mvm->hw_queue_to_mac80211[queue] |= BIT(mvmsta->vif->hw_queue[tid_to_mac80211_ac[tid]]); } -- cgit From ced19f26968dc5cbcf870ae6856f8d7b789a7016 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Mon, 6 Feb 2017 19:09:32 +0200 Subject: iwlwifi: mvm: support station type API Support change to ADD_STA API to support station types. Each station is assigned its type. This simplifies FW handling of the broadcast and multicast stations: * broadcast station is identified by its type and not the mac address. * multicast queue is no longer treated differently. The opening and closing of it is done by referring to its station. There is no need to specify it in the MAC command. * When disabling TX to all station driver can disable the traffic on multicast station, so FW doesn't have to do it. Change is backward compatible. Change the order of adding and removing the stations according to FW requirements. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 2 + .../net/wireless/intel/iwlwifi/mvm/fw-api-mac.h | 5 +- .../net/wireless/intel/iwlwifi/mvm/fw-api-sta.h | 24 ++++++- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 6 +- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 31 +++++--- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 84 +++++++++++++++++++--- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 7 +- 7 files changed, 132 insertions(+), 27 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index 287e83eb30d9..44419e82da1b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -243,6 +243,7 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t; * scan request. * @IWL_UCODE_TLV_API_TKIP_MIC_KEYS: This ucode supports version 2 of * ADD_MODIFY_STA_KEY_API_S_VER_2. + * @IWL_UCODE_TLV_API_STA_TYPE: This ucode supports station type assignement. * * @NUM_IWL_UCODE_TLV_API: number of bits used */ @@ -253,6 +254,7 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_NEW_VERSION = (__force iwl_ucode_tlv_api_t)20, IWL_UCODE_TLV_API_SCAN_TSF_REPORT = (__force iwl_ucode_tlv_api_t)28, IWL_UCODE_TLV_API_TKIP_MIC_KEYS = (__force iwl_ucode_tlv_api_t)29, + IWL_UCODE_TLV_API_STA_TYPE = (__force iwl_ucode_tlv_api_t)30, NUM_IWL_UCODE_TLV_API #ifdef __CHECKER__ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h index d3cdd889c85c..970b030ed28d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h @@ -157,7 +157,8 @@ enum iwl_tsf_id { * @bi_reciprocal: 2^32 / bi * @dtim_interval: dtim transmit time in TU * @dtim_reciprocal: 2^32 / dtim_interval - * @mcast_qid: queue ID for multicast traffic + * @mcast_qid: queue ID for multicast traffic. + * NOTE: obsolete from VER2 and on * @beacon_template: beacon template ID */ struct iwl_mac_data_ap { @@ -169,7 +170,7 @@ struct iwl_mac_data_ap { __le32 dtim_reciprocal; __le32 mcast_qid; __le32 beacon_template; -} __packed; /* AP_MAC_DATA_API_S_VER_1 */ +} __packed; /* AP_MAC_DATA_API_S_VER_2 */ /** * struct iwl_mac_data_ibss - configuration data for IBSS MAC context diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h index a20ac4d9e63e..421b9dd1fb66 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-sta.h @@ -309,6 +309,24 @@ struct iwl_mvm_add_sta_cmd_v7 { __le32 tfd_queue_msk; } __packed; /* ADD_STA_CMD_API_S_VER_7 */ +/** + * enum iwl_sta_type - FW station types + * ( REPLY_ADD_STA = 0x18 ) + * @IWL_STA_LINK: Link station - normal RX and TX traffic. + * @IWL_STA_GENERAL_PURPOSE: General purpose. In AP mode used for beacons + * and probe responses. + * @IWL_STA_MULTICAST: multicast traffic, + * @IWL_STA_TDLS_LINK: TDLS link station + * @IWL_STA_AUX_ACTIVITY: auxilary station (scan, ROC and so on). + */ +enum iwl_sta_type { + IWL_STA_LINK, + IWL_STA_GENERAL_PURPOSE, + IWL_STA_MULTICAST, + IWL_STA_TDLS_LINK, + IWL_STA_AUX_ACTIVITY, +}; + /** * struct iwl_mvm_add_sta_cmd - Add/modify a station in the fw's sta table. * ( REPLY_ADD_STA = 0x18 ) @@ -333,6 +351,7 @@ struct iwl_mvm_add_sta_cmd_v7 { * @sleep_tx_count: number of packets to transmit to station even though it is * asleep. Used to synchronise PS-poll and u-APSD responses while ucode * keeps track of STA sleep state. + * @station_type: type of this station. See &enum iwl_sta_type. * @sleep_state_flags: Look at %iwl_sta_sleep_flag. * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP * mac-addr. @@ -367,14 +386,15 @@ struct iwl_mvm_add_sta_cmd { u8 remove_immediate_ba_tid; __le16 add_immediate_ba_ssn; __le16 sleep_tx_count; - __le16 sleep_state_flags; + u8 sleep_state_flags; + u8 station_type; __le16 assoc_id; __le16 beamform_flags; __le32 tfd_queue_msk; __le16 rx_ba_window; u8 sp_length; u8 uapsd_acs; -} __packed; /* ADD_STA_CMD_API_S_VER_9 */ +} __packed; /* ADD_STA_CMD_API_S_VER_10 */ /** * struct iwl_mvm_add_sta_key_common - add/modify sta key common part diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 2476af904f5e..0f1831b41915 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -907,7 +907,7 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, /* Allocate sniffer station */ ret = iwl_mvm_allocate_int_sta(mvm, &mvm->snif_sta, tfd_queue_msk, - vif->type); + vif->type, IWL_STA_GENERAL_PURPOSE); if (ret) return ret; @@ -1228,7 +1228,9 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int * vif->bss_conf.dtim_period)); - ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue); + if (!fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_STA_TYPE)) + ctxt_ap->mcast_qid = cpu_to_le32(vif->cab_queue); /* * Only set the beacon time when the MAC is being added, when we diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 8ee5cf1419f4..c121982d781e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1358,7 +1358,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, * which shouldn't be in TFD mask anyway */ ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->mcast_sta, - 0, vif->type); + 0, vif->type, + IWL_STA_MULTICAST); if (ret) goto out_release; } @@ -2111,15 +2112,15 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, if (ret) goto out_remove; - /* Send the bcast station. At this stage the TBTT and DTIM time events - * are added and applied to the scheduler */ - ret = iwl_mvm_send_add_bcast_sta(mvm, vif); + ret = iwl_mvm_add_mcast_sta(mvm, vif); if (ret) goto out_unbind; - ret = iwl_mvm_add_mcast_sta(mvm, vif); + /* Send the bcast station. At this stage the TBTT and DTIM time events + * are added and applied to the scheduler */ + ret = iwl_mvm_send_add_bcast_sta(mvm, vif); if (ret) - goto out_rm_bcast; + goto out_rm_mcast; /* must be set before quota calculations */ mvmvif->ap_ibss_active = true; @@ -2148,9 +2149,9 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, out_quota_failed: iwl_mvm_power_update_mac(mvm); mvmvif->ap_ibss_active = false; - iwl_mvm_rm_mcast_sta(mvm, vif); -out_rm_bcast: iwl_mvm_send_rm_bcast_sta(mvm, vif); +out_rm_mcast: + iwl_mvm_rm_mcast_sta(mvm, vif); out_unbind: iwl_mvm_binding_remove_vif(mvm, vif); out_remove: @@ -2196,8 +2197,20 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false, NULL); iwl_mvm_update_quotas(mvm, false, NULL); - iwl_mvm_rm_mcast_sta(mvm, vif); + + /* + * This is not very nice, but the simplest: + * For older FWs removing the mcast sta before the bcast station may + * cause assert 0x2b00. + * This is fixed in later FW (which will stop beaconing when removing + * bcast station). + * So make the order of removal depend on the TLV + */ + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) + iwl_mvm_rm_mcast_sta(mvm, vif); iwl_mvm_send_rm_bcast_sta(mvm, vif); + if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) + iwl_mvm_rm_mcast_sta(mvm, vif); iwl_mvm_binding_remove_vif(mvm, vif); iwl_mvm_power_update_mac(mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 3be7fc01b19f..d06ba5cb7297 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -77,9 +77,11 @@ */ static inline int iwl_mvm_add_sta_cmd_size(struct iwl_mvm *mvm) { - return iwl_mvm_has_new_rx_api(mvm) ? - sizeof(struct iwl_mvm_add_sta_cmd) : - sizeof(struct iwl_mvm_add_sta_cmd_v7); + if (iwl_mvm_has_new_rx_api(mvm) || + fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) + return sizeof(struct iwl_mvm_add_sta_cmd); + else + return sizeof(struct iwl_mvm_add_sta_cmd_v7); } static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, @@ -126,6 +128,9 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u32 status; u32 agg_size = 0, mpdu_dens = 0; + if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) + add_sta_cmd.station_type = mvm_sta->sta_type; + if (!update || (flags & STA_MODIFY_QUEUES)) { memcpy(&add_sta_cmd.addr, sta->addr, ETH_ALEN); @@ -1342,6 +1347,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF; mvm_sta->tx_protection = 0; mvm_sta->tt_tx_protection = false; + mvm_sta->sta_type = sta->tdls ? IWL_STA_TDLS_LINK : IWL_STA_LINK; /* HW restart, don't assume the memory has been zeroed */ atomic_set(&mvm->pending_frames[sta_id], 0); @@ -1725,7 +1731,8 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm, int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, - u32 qmask, enum nl80211_iftype iftype) + u32 qmask, enum nl80211_iftype iftype, + enum iwl_sta_type type) { if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype); @@ -1734,6 +1741,7 @@ int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, } sta->tfd_queue_msk = qmask; + sta->type = type; /* put a non-NULL value so iterating over the stations won't stop */ rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], ERR_PTR(-EINVAL)); @@ -1762,6 +1770,8 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, cmd.sta_id = sta->sta_id; cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id, color)); + if (fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) + cmd.station_type = sta->type; if (!iwl_mvm_has_new_tx_api(mvm)) cmd.tfd_queue_msk = cpu_to_le32(sta->tfd_queue_msk); @@ -1826,7 +1836,8 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) /* Allocate aux station and assign to it the aux queue */ ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue), - NL80211_IFTYPE_UNSPECIFIED); + NL80211_IFTYPE_UNSPECIFIED, + IWL_STA_AUX_ACTIVITY); if (ret) return ret; @@ -2025,7 +2036,8 @@ int iwl_mvm_alloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) } return iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, qmask, - ieee80211_vif_type_p2p(vif)); + ieee80211_vif_type_p2p(vif), + IWL_STA_GENERAL_PURPOSE); } /* Allocate a new station entry for the broadcast station to the given vif, @@ -2111,6 +2123,16 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (WARN_ON(vif->type != NL80211_IFTYPE_AP)) return -ENOTSUPP; + /* + * While in previous FWs we had to exclude cab queue from TFD queue + * mask, now it is needed as any other queue. + */ + if (!iwl_mvm_has_new_tx_api(mvm) && + fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) { + iwl_mvm_enable_txq(mvm, vif->cab_queue, vif->cab_queue, 0, + &cfg, timeout); + msta->tfd_queue_msk |= BIT(vif->cab_queue); + } ret = iwl_mvm_add_int_sta_common(mvm, msta, maddr, mvmvif->id, mvmvif->color); if (ret) { @@ -2121,7 +2143,9 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) /* * Enable cab queue after the ADD_STA command is sent. * This is needed for a000 firmware which won't accept SCD_QUEUE_CFG - * command with unknown station id. + * command with unknown station id, and for FW that doesn't support + * station API since the cab queue is not included in the + * tfd_queue_mask. */ if (iwl_mvm_has_new_tx_api(mvm)) { int queue = iwl_mvm_tvqm_enable_txq(mvm, vif->cab_queue, @@ -2129,7 +2153,8 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) IWL_MAX_TID_COUNT, timeout); mvmvif->cab_queue = queue; - } else { + } else if (!fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_STA_TYPE)) { iwl_mvm_enable_txq(mvm, vif->cab_queue, vif->cab_queue, 0, &cfg, timeout); } @@ -3452,13 +3477,13 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, /* Note: this is ignored by firmware not supporting GO uAPSD */ if (more_data) - cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_MOREDATA); + cmd.sleep_state_flags |= STA_SLEEP_STATE_MOREDATA; if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) { mvmsta->next_status_eosp = true; - cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_PS_POLL); + cmd.sleep_state_flags |= STA_SLEEP_STATE_PS_POLL; } else { - cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD); + cmd.sleep_state_flags |= STA_SLEEP_STATE_UAPSD; } /* block the Tx queues until the FW updated the sleep Tx count */ @@ -3535,6 +3560,27 @@ void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm, spin_unlock_bh(&mvm_sta->lock); } +static void iwl_mvm_int_sta_modify_disable_tx(struct iwl_mvm *mvm, + struct iwl_mvm_vif *mvmvif, + struct iwl_mvm_int_sta *sta, + bool disable) +{ + u32 id = FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color); + struct iwl_mvm_add_sta_cmd cmd = { + .add_modify = STA_MODE_MODIFY, + .sta_id = sta->sta_id, + .station_flags = disable ? cpu_to_le32(STA_FLG_DISABLE_TX) : 0, + .station_flags_msk = cpu_to_le32(STA_FLG_DISABLE_TX), + .mac_id_n_color = cpu_to_le32(id), + }; + int ret; + + ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, 0, + iwl_mvm_add_sta_cmd_size(mvm), &cmd); + if (ret) + IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); +} + void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif, bool disable) @@ -3559,6 +3605,22 @@ void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable); } + + if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_STA_TYPE)) + return; + + /* Need to block/unblock also multicast station */ + if (mvmvif->mcast_sta.sta_id != IWL_MVM_INVALID_STA) + iwl_mvm_int_sta_modify_disable_tx(mvm, mvmvif, + &mvmvif->mcast_sta, disable); + + /* + * Only unblock the broadcast station (FW blocks it for immediate + * quiet, not the driver) + */ + if (!disable && mvmvif->bcast_sta.sta_id != IWL_MVM_INVALID_STA) + iwl_mvm_int_sta_modify_disable_tx(mvm, mvmvif, + &mvmvif->bcast_sta, disable); } void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index a143a8757e27..2716cb5483bf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -380,6 +380,7 @@ struct iwl_mvm_rxq_dup_data { * @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for * tid. * @max_agg_bufsize: the maximal size of the AGG buffer for this station + * @sta_type: station type * @bt_reduced_txpower: is reduced tx power enabled for this station * @next_status_eosp: the next reclaimed packet is a PS-Poll response and * we need to signal the EOSP @@ -416,6 +417,7 @@ struct iwl_mvm_sta { u32 mac_id_n_color; u16 tid_disable_agg; u8 max_agg_bufsize; + enum iwl_sta_type sta_type; bool bt_reduced_txpower; bool next_status_eosp; spinlock_t lock; @@ -453,10 +455,12 @@ iwl_mvm_sta_from_mac80211(struct ieee80211_sta *sta) * struct iwl_mvm_int_sta - representation of an internal station (auxiliary or * broadcast) * @sta_id: the index of the station in the fw (will be replaced by id_n_color) + * @type: station type * @tfd_queue_msk: the tfd queues used by the station */ struct iwl_mvm_int_sta { u32 sta_id; + enum iwl_sta_type type; u32 tfd_queue_msk; }; @@ -536,7 +540,8 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, - u32 qmask, enum nl80211_iftype iftype); + u32 qmask, enum nl80211_iftype iftype, + enum iwl_sta_type type); void iwl_mvm_dealloc_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta); int iwl_mvm_add_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -- cgit From e982bc2ca87fc5e663c85356c65fb66d2ab50843 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 23 Feb 2017 14:19:45 +0200 Subject: iwlwifi: move to 512 queues Avoid using the old define since it will enlarge necessary structs for previous HW. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-a000.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-config.h | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 2 ++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 2 +- drivers/net/wireless/intel/iwlwifi/pcie/internal.h | 6 +++--- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c index 097cb45c8ad9..c648cfb981a3 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-a000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-a000.c @@ -85,7 +85,7 @@ static const struct iwl_base_params iwl_a000_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_A000, - .num_of_queues = 31, + .num_of_queues = 512, .shadow_ram_support = true, .led_compensation = 57, .wd_timeout = IWL_LONG_WD_TIMEOUT, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 4af1267181a9..a12197e3ce78 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -170,7 +170,7 @@ struct iwl_base_params { apmg_wake_up_wa:1, scd_chain_ext_wa:1; - u8 num_of_queues; /* def: HW dependent */ + u16 num_of_queues; /* def: HW dependent */ u8 max_ll_items; u8 led_compensation; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 626e2703a57f..181636861cb6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -396,6 +396,8 @@ static inline void iwl_free_rxb(struct iwl_rx_cmd_buffer *r) * currently supports */ #define IWL_MAX_HW_QUEUES 32 +#define IWL_MAX_TVQM_QUEUES 512 + #define IWL_MAX_TID_COUNT 8 #define IWL_MGMT_TID 15 #define IWL_FRAME_LIMIT 64 diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 204664b578df..4e74a6b90e70 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -788,7 +788,7 @@ struct iwl_mvm { u64 on_time_scan; } radio_stats, accu_radio_stats; - u8 hw_queue_to_mac80211[IWL_MAX_HW_QUEUES]; + u8 hw_queue_to_mac80211[IWL_MAX_TVQM_QUEUES]; struct { u8 hw_queue_refcount; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 30835f06a6e2..fd4faaaa1484 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -414,9 +414,9 @@ struct iwl_trans_pcie { struct iwl_dma_ptr kw; struct iwl_txq *txq_memory; - struct iwl_txq *txq[IWL_MAX_HW_QUEUES]; - unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; - unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; + struct iwl_txq *txq[IWL_MAX_TVQM_QUEUES]; + unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_TVQM_QUEUES)]; + unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_TVQM_QUEUES)]; /* PCI bus related data */ struct pci_dev *pci_dev; -- cgit From a1a5787730455609473fb5a9d86eee05af347a3b Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 5 Mar 2017 11:38:58 +0200 Subject: iwlwifi: rename wait_for_tx_queues_empty Rename current wait_tx_queue_empty to wait_tx_queues_empty since it waits for multiple queues (up to 32). Next patch will add a wait for single TX queue which is needed for gen2 to be scalable for 512. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/dvm/lib.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 8 ++++---- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 14 +++++++------- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c index 6c2d6da7eec6..74e52f7c5aa1 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c @@ -180,7 +180,7 @@ void iwlagn_dev_txfifo_flush(struct iwl_priv *priv) goto done; } IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n"); - iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff); + iwl_trans_wait_tx_queues_empty(priv->trans, 0xffffffff); done: ieee80211_wake_queues(priv->hw); mutex_unlock(&priv->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index 2a04d0cd71ae..5fd307da562e 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -1143,7 +1143,7 @@ static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } IWL_DEBUG_TX_QUEUES(priv, "wait transmit/flush all frames\n"); - iwl_trans_wait_tx_queue_empty(priv->trans, scd_queues); + iwl_trans_wait_tx_queues_empty(priv->trans, scd_queues); done: mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 181636861cb6..0ebfdbb22992 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -691,7 +691,7 @@ struct iwl_trans_ops { void (*txq_set_shared_mode)(struct iwl_trans *trans, u32 txq_id, bool shared); - int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm); + int (*wait_tx_queues_empty)(struct iwl_trans *trans, u32 txq_bm); void (*freeze_txq_timer)(struct iwl_trans *trans, unsigned long txqs, bool freeze); void (*block_txq_ptrs)(struct iwl_trans *trans, bool block); @@ -1195,15 +1195,15 @@ static inline void iwl_trans_block_txq_ptrs(struct iwl_trans *trans, trans->ops->block_txq_ptrs(trans, block); } -static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans, - u32 txqs) +static inline int iwl_trans_wait_tx_queues_empty(struct iwl_trans *trans, + u32 txqs) { if (WARN_ON_ONCE(trans->state != IWL_TRANS_FW_ALIVE)) { IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); return -EIO; } - return trans->ops->wait_tx_queue_empty(trans, txqs); + return trans->ops->wait_tx_queues_empty(trans, txqs); } static inline void iwl_trans_write8(struct iwl_trans *trans, u32 ofs, u8 val) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index c121982d781e..d91b28c3ea5e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1478,7 +1478,7 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, * already marked as draining, so to complete the draining, we * just need to wait until the transport is empty. */ - iwl_trans_wait_tx_queue_empty(mvm->trans, tfd_msk); + iwl_trans_wait_tx_queues_empty(mvm->trans, tfd_msk); } if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { @@ -4000,7 +4000,7 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, /* this can take a while, and we may need/want other operations * to succeed while doing this, so do it without the mutex held */ - iwl_trans_wait_tx_queue_empty(mvm->trans, msk); + iwl_trans_wait_tx_queues_empty(mvm->trans, msk); } } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index d06ba5cb7297..f5c786ddc526 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -658,7 +658,7 @@ int iwl_mvm_scd_queue_redirect(struct iwl_mvm *mvm, int queue, int tid, /* Stop MAC queues and wait for this queue to empty */ iwl_mvm_stop_mac_queues(mvm, mq); - ret = iwl_trans_wait_tx_queue_empty(mvm->trans, BIT(queue)); + ret = iwl_trans_wait_tx_queues_empty(mvm->trans, BIT(queue)); if (ret) { IWL_ERR(mvm, "Error draining queue %d before reconfig\n", queue); @@ -1614,8 +1614,8 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, 0); if (ret) return ret; - ret = iwl_trans_wait_tx_queue_empty(mvm->trans, - mvm_sta->tfd_queue_msk); + ret = iwl_trans_wait_tx_queues_empty(mvm->trans, + mvm_sta->tfd_queue_msk); if (ret) return ret; ret = iwl_mvm_drain_sta(mvm, mvm_sta, false); @@ -2659,8 +2659,8 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * If reconfiguring an existing queue, it first must be * drained */ - ret = iwl_trans_wait_tx_queue_empty(mvm->trans, - BIT(queue)); + ret = iwl_trans_wait_tx_queues_empty(mvm->trans, + BIT(queue)); if (ret) { IWL_ERR(mvm, "Error draining queue before reconfig\n"); @@ -2846,8 +2846,8 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, iwl_mvm_drain_sta(mvm, mvmsta, true); if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), 0)) IWL_ERR(mvm, "Couldn't flush the AGG queue\n"); - iwl_trans_wait_tx_queue_empty(mvm->trans, - mvmsta->tfd_queue_msk); + iwl_trans_wait_tx_queues_empty(mvm->trans, + mvmsta->tfd_queue_msk); iwl_mvm_drain_sta(mvm, mvmsta, false); iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index c551ba8809db..70acf850a9f1 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2833,7 +2833,7 @@ static void iwl_trans_pcie_resume(struct iwl_trans *trans) .ref = iwl_trans_pcie_ref, \ .unref = iwl_trans_pcie_unref, \ .dump_data = iwl_trans_pcie_dump_data, \ - .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty, \ + .wait_tx_queues_empty = iwl_trans_pcie_wait_txq_empty, \ .d3_suspend = iwl_trans_pcie_d3_suspend, \ .d3_resume = iwl_trans_pcie_d3_resume -- cgit From f99a6abe59e096cc2c753e667c19f22022e3bef4 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 5 Mar 2017 18:35:02 +0200 Subject: iwlwifi: mvm: memset binding before setting values The changes in commit 9415af7f306b ("iwlwifi: mvm: support new binding API") assigned values that were later memset to 0. Move the memset earlier. Fixes: 9415af7f306b ("iwlwifi: mvm: support new binding API") Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/binding.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c index 2e0ed080457f..75d35f6b041e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/binding.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/binding.c @@ -86,6 +86,8 @@ static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action, u32 status; int size; + memset(&cmd, 0, sizeof(cmd)); + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT)) { size = sizeof(cmd); @@ -98,8 +100,6 @@ static int iwl_mvm_binding_cmd(struct iwl_mvm *mvm, u32 action, size = IWL_BINDING_CMD_SIZE_V1; } - memset(&cmd, 0, sizeof(cmd)); - cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(phyctxt->id, phyctxt->color)); cmd.action = cpu_to_le32(action); -- cgit From d8a130b02d0caa9a8f0c14db11d84320ffc62c57 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 Mar 2017 15:49:00 +0100 Subject: iwlwifi: pcie: apply no-reclaim logic only to group 0 When applying no-reclaim logic to commands other than the group zero for legacy commands, commands such as 0x1c (TX_CMD in group 0) can't be used in any other group. Fix that by applying this logic only for group 0 - it's not and should never be needed for any other groups. Reported-by: Sharon Dvir Reported-by: Shaul Triebitz Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index f98f2d2b8a1b..1da2de205cdf 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1147,7 +1147,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, * Ucode should set SEQ_RX_FRAME bit if ucode-originated, * but apparently a few don't get set; catch them here. */ reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME); - if (reclaim) { + if (reclaim && !pkt->hdr.group_id) { int i; for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) { -- cgit From 291084951825e1efca081926b10bf3ff7e872476 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 8 Jan 2017 16:45:46 +0200 Subject: iwlwifi: adjust NVM parsing APIs for new a000 method In a000 devices we will get all nvm data from the firmware, and can save most of the parsing. Export two APIs that op mode will still use. Adjust API of init_sbands to be independent of NVM file structure so it can be used by op mode as well. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 32 +++++++++------------- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h | 16 ++++++++++- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 3bd6fc1b76d4..721ae6bef5da 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -7,7 +7,7 @@ * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,6 +34,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -438,25 +439,16 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; } -static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, - const __le16 *ch_section, - u8 tx_chains, u8 rx_chains, bool lar_supported) +void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, const __le16 *nvm_ch_flags, + u8 tx_chains, u8 rx_chains, bool lar_supported) { int n_channels; int n_used = 0; struct ieee80211_supported_band *sband; - if (cfg->device_family != IWL_DEVICE_FAMILY_8000) - n_channels = iwl_init_channel_map( - dev, cfg, data, - &ch_section[NVM_CHANNELS], lar_supported); - else - n_channels = iwl_init_channel_map( - dev, cfg, data, - &ch_section[NVM_CHANNELS_FAMILY_8000], - lar_supported); - + n_channels = iwl_init_channel_map(dev, cfg, data, nvm_ch_flags, + lar_supported); sband = &data->bands[NL80211_BAND_2GHZ]; sband->band = NL80211_BAND_2GHZ; sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; @@ -482,6 +474,7 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n", n_used, n_channels); } +IWL_EXPORT_SYMBOL(iwl_init_sbands); static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw, const __le16 *phy_sku) @@ -559,8 +552,8 @@ static void iwl_flip_hw_address(__le32 mac_addr0, __le32 mac_addr1, u8 *dest) dest[5] = hw_addr[0]; } -static void iwl_set_hw_address_from_csr(struct iwl_trans *trans, - struct iwl_nvm_data *data) +void iwl_set_hw_address_from_csr(struct iwl_trans *trans, + struct iwl_nvm_data *data) { __le32 mac_addr0 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR0_STRAP)); __le32 mac_addr1 = cpu_to_le32(iwl_read32(trans, CSR_MAC_ADDR1_STRAP)); @@ -578,6 +571,7 @@ static void iwl_set_hw_address_from_csr(struct iwl_trans *trans, iwl_flip_hw_address(mac_addr0, mac_addr1, data->hw_addr); } +IWL_EXPORT_SYMBOL(iwl_set_hw_address_from_csr); static void iwl_set_hw_address_family_8000(struct iwl_trans *trans, const struct iwl_cfg *cfg, @@ -718,7 +712,7 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB); data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1); lar_enabled = true; - ch_section = nvm_sw; + ch_section = &nvm_sw[NVM_CHANNELS]; } else { u16 lar_offset = data->nvm_version < 0xE39 ? NVM_LAR_OFFSET_FAMILY_8000_OLD : @@ -728,7 +722,7 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, data->lar_enabled = !!(lar_config & NVM_LAR_ENABLED_FAMILY_8000); lar_enabled = data->lar_enabled; - ch_section = regulatory; + ch_section = ®ulatory[NVM_CHANNELS_FAMILY_8000]; } /* If no valid mac address was found - bail out */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 7249e5b403f4..3fd6506a02ab 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -6,7 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2015 Intel Corporation. All rights reserved. - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -32,6 +32,7 @@ * BSD LICENSE * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -81,6 +82,19 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, const __le16 *mac_override, const __le16 *phy_sku, u8 tx_chains, u8 rx_chains, bool lar_fw_supported); +/** + * iwl_set_hw_address_from_csr - sets HW address for 9000 devices and on + */ +void iwl_set_hw_address_from_csr(struct iwl_trans *trans, + struct iwl_nvm_data *data); + +/** + * iwl_init_sbands - parse and set all channel profiles + */ +void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, const __le16 *nvm_ch_flags, + u8 tx_chains, u8 rx_chains, bool lar_supported); + /** * iwl_parse_mcc_info - parse MCC (mobile country code) info coming from FW * -- cgit From 7acedaf5c4355f812cfef883ac28bf15f7d9205e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 25 Apr 2017 11:36:52 -0700 Subject: net: move xdp_prog field in RX cache lines (struct net_device, xdp_prog) field should be moved in RX cache lines, reducing latencies when a single packet is received on idle host, since netif_elide_gro() needs it. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 46d220c2bf92..8c5c8cdc7b97 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1790,6 +1790,7 @@ struct net_device { unsigned int real_num_rx_queues; #endif + struct bpf_prog __rcu *xdp_prog; unsigned long gro_flush_timeout; rx_handler_func_t __rcu *rx_handler; void __rcu *rx_handler_data; @@ -1905,7 +1906,6 @@ struct net_device { struct lock_class_key *qdisc_tx_busylock; struct lock_class_key *qdisc_running_key; bool proto_down; - struct bpf_prog __rcu *xdp_prog; }; #define to_net_dev(d) container_of(d, struct net_device, dev) -- cgit From 1fefe14725c7cc7c720f9f8af2bc3bef13fd7d39 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 19 Apr 2017 18:14:04 +0200 Subject: netfilter: synproxy: only register hooks when needed Defer registration of the synproxy hooks until the first SYNPROXY rule is added. Also means we only register hooks in namespaces that need it. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_synproxy.h | 2 + net/ipv4/netfilter/ipt_SYNPROXY.c | 73 ++++++++++++++------------- net/ipv6/netfilter/ip6t_SYNPROXY.c | 73 ++++++++++++++------------- 3 files changed, 80 insertions(+), 68 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_synproxy.h b/include/net/netfilter/nf_conntrack_synproxy.h index b0ca402c1f72..a2fcb5271726 100644 --- a/include/net/netfilter/nf_conntrack_synproxy.h +++ b/include/net/netfilter/nf_conntrack_synproxy.h @@ -52,6 +52,8 @@ struct synproxy_stats { struct synproxy_net { struct nf_conn *tmpl; struct synproxy_stats __percpu *stats; + unsigned int hook_ref4; + unsigned int hook_ref6; }; extern unsigned int synproxy_net_id; diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index 3240a2614e82..c308ee0ee0bc 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c @@ -409,19 +409,56 @@ static unsigned int ipv4_synproxy_hook(void *priv, return NF_ACCEPT; } +static struct nf_hook_ops ipv4_synproxy_ops[] __read_mostly = { + { + .hook = ipv4_synproxy_hook, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, + }, + { + .hook = ipv4_synproxy_hook, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, + }, +}; + static int synproxy_tg4_check(const struct xt_tgchk_param *par) { + struct synproxy_net *snet = synproxy_pernet(par->net); const struct ipt_entry *e = par->entryinfo; + int err; if (e->ip.proto != IPPROTO_TCP || e->ip.invflags & XT_INV_PROTO) return -EINVAL; - return nf_ct_netns_get(par->net, par->family); + err = nf_ct_netns_get(par->net, par->family); + if (err) + return err; + + if (snet->hook_ref4 == 0) { + err = nf_register_net_hooks(par->net, ipv4_synproxy_ops, + ARRAY_SIZE(ipv4_synproxy_ops)); + if (err) { + nf_ct_netns_put(par->net, par->family); + return err; + } + } + + snet->hook_ref4++; + return err; } static void synproxy_tg4_destroy(const struct xt_tgdtor_param *par) { + struct synproxy_net *snet = synproxy_pernet(par->net); + + snet->hook_ref4--; + if (snet->hook_ref4 == 0) + nf_unregister_net_hooks(par->net, ipv4_synproxy_ops, + ARRAY_SIZE(ipv4_synproxy_ops)); nf_ct_netns_put(par->net, par->family); } @@ -436,46 +473,14 @@ static struct xt_target synproxy_tg4_reg __read_mostly = { .me = THIS_MODULE, }; -static struct nf_hook_ops ipv4_synproxy_ops[] __read_mostly = { - { - .hook = ipv4_synproxy_hook, - .pf = NFPROTO_IPV4, - .hooknum = NF_INET_LOCAL_IN, - .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, - }, - { - .hook = ipv4_synproxy_hook, - .pf = NFPROTO_IPV4, - .hooknum = NF_INET_POST_ROUTING, - .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, - }, -}; - static int __init synproxy_tg4_init(void) { - int err; - - err = nf_register_hooks(ipv4_synproxy_ops, - ARRAY_SIZE(ipv4_synproxy_ops)); - if (err < 0) - goto err1; - - err = xt_register_target(&synproxy_tg4_reg); - if (err < 0) - goto err2; - - return 0; - -err2: - nf_unregister_hooks(ipv4_synproxy_ops, ARRAY_SIZE(ipv4_synproxy_ops)); -err1: - return err; + return xt_register_target(&synproxy_tg4_reg); } static void __exit synproxy_tg4_exit(void) { xt_unregister_target(&synproxy_tg4_reg); - nf_unregister_hooks(ipv4_synproxy_ops, ARRAY_SIZE(ipv4_synproxy_ops)); } module_init(synproxy_tg4_init); diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index 4ef1ddd4bbbd..1252537f215f 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c @@ -430,20 +430,57 @@ static unsigned int ipv6_synproxy_hook(void *priv, return NF_ACCEPT; } +static struct nf_hook_ops ipv6_synproxy_ops[] __read_mostly = { + { + .hook = ipv6_synproxy_hook, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, + }, + { + .hook = ipv6_synproxy_hook, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, + }, +}; + static int synproxy_tg6_check(const struct xt_tgchk_param *par) { + struct synproxy_net *snet = synproxy_pernet(par->net); const struct ip6t_entry *e = par->entryinfo; + int err; if (!(e->ipv6.flags & IP6T_F_PROTO) || e->ipv6.proto != IPPROTO_TCP || e->ipv6.invflags & XT_INV_PROTO) return -EINVAL; - return nf_ct_netns_get(par->net, par->family); + err = nf_ct_netns_get(par->net, par->family); + if (err) + return err; + + if (snet->hook_ref6 == 0) { + err = nf_register_net_hooks(par->net, ipv6_synproxy_ops, + ARRAY_SIZE(ipv6_synproxy_ops)); + if (err) { + nf_ct_netns_put(par->net, par->family); + return err; + } + } + + snet->hook_ref6++; + return err; } static void synproxy_tg6_destroy(const struct xt_tgdtor_param *par) { + struct synproxy_net *snet = synproxy_pernet(par->net); + + snet->hook_ref6--; + if (snet->hook_ref6 == 0) + nf_unregister_net_hooks(par->net, ipv6_synproxy_ops, + ARRAY_SIZE(ipv6_synproxy_ops)); nf_ct_netns_put(par->net, par->family); } @@ -458,46 +495,14 @@ static struct xt_target synproxy_tg6_reg __read_mostly = { .me = THIS_MODULE, }; -static struct nf_hook_ops ipv6_synproxy_ops[] __read_mostly = { - { - .hook = ipv6_synproxy_hook, - .pf = NFPROTO_IPV6, - .hooknum = NF_INET_LOCAL_IN, - .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, - }, - { - .hook = ipv6_synproxy_hook, - .pf = NFPROTO_IPV6, - .hooknum = NF_INET_POST_ROUTING, - .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, - }, -}; - static int __init synproxy_tg6_init(void) { - int err; - - err = nf_register_hooks(ipv6_synproxy_ops, - ARRAY_SIZE(ipv6_synproxy_ops)); - if (err < 0) - goto err1; - - err = xt_register_target(&synproxy_tg6_reg); - if (err < 0) - goto err2; - - return 0; - -err2: - nf_unregister_hooks(ipv6_synproxy_ops, ARRAY_SIZE(ipv6_synproxy_ops)); -err1: - return err; + return xt_register_target(&synproxy_tg6_reg); } static void __exit synproxy_tg6_exit(void) { xt_unregister_target(&synproxy_tg6_reg); - nf_unregister_hooks(ipv6_synproxy_ops, ARRAY_SIZE(ipv6_synproxy_ops)); } module_init(synproxy_tg6_init); -- cgit From efe41606184ef33efde1d708eaf7d0fad9e06694 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 19 Apr 2017 18:25:22 +0200 Subject: ipvs: convert to use pernet nf_hook api nf_(un)register_hooks has to maintain an internal hook list to add/remove those hooks from net namespaces as they are added/deleted. ipvs already uses pernet_ops, so we can switch to the (more recent) pernet hook api instead. Compile tested only. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_core.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index b4a746d0e39b..d2d7bdf1d510 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -2200,6 +2200,7 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = { static int __net_init __ip_vs_init(struct net *net) { struct netns_ipvs *ipvs; + int ret; ipvs = net_generic(net, ip_vs_net_id); if (ipvs == NULL) @@ -2231,11 +2232,17 @@ static int __net_init __ip_vs_init(struct net *net) if (ip_vs_sync_net_init(ipvs) < 0) goto sync_fail; + ret = nf_register_net_hooks(net, ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); + if (ret < 0) + goto hook_fail; + return 0; /* * Error handling */ +hook_fail: + ip_vs_sync_net_cleanup(ipvs); sync_fail: ip_vs_conn_net_cleanup(ipvs); conn_fail: @@ -2255,6 +2262,7 @@ static void __net_exit __ip_vs_cleanup(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); + nf_unregister_net_hooks(net, ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); ip_vs_service_net_cleanup(ipvs); /* ip_vs_flush() with locks */ ip_vs_conn_net_cleanup(ipvs); ip_vs_app_net_cleanup(ipvs); @@ -2315,24 +2323,16 @@ static int __init ip_vs_init(void) if (ret < 0) goto cleanup_sub; - ret = nf_register_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); - if (ret < 0) { - pr_err("can't register hooks.\n"); - goto cleanup_dev; - } - ret = ip_vs_register_nl_ioctl(); if (ret < 0) { pr_err("can't register netlink/ioctl.\n"); - goto cleanup_hooks; + goto cleanup_dev; } pr_info("ipvs loaded.\n"); return ret; -cleanup_hooks: - nf_unregister_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); cleanup_dev: unregister_pernet_device(&ipvs_core_dev_ops); cleanup_sub: @@ -2349,7 +2349,6 @@ exit: static void __exit ip_vs_cleanup(void) { ip_vs_unregister_nl_ioctl(); - nf_unregister_hooks(ip_vs_ops, ARRAY_SIZE(ip_vs_ops)); unregister_pernet_device(&ipvs_core_dev_ops); unregister_pernet_subsys(&ipvs_core_ops); /* free ip_vs struct */ ip_vs_conn_cleanup(); -- cgit From 1a0ed0ad4812a59a19f3bdb237601389214d1ed1 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 20 Apr 2017 00:42:07 +0200 Subject: netfilter: decnet: only register hooks in init namespace looks like decnet isn't namespacified in first place, so restrict hook registration to the initial namespace. Prepares for eventual removal of legacy nf_register_hook() api. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/decnet/netfilter/dn_rtmsg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c index 85f2fdc360c2..f44303a40105 100644 --- a/net/decnet/netfilter/dn_rtmsg.c +++ b/net/decnet/netfilter/dn_rtmsg.c @@ -134,7 +134,7 @@ static int __init dn_rtmsg_init(void) return -ENOMEM; } - rv = nf_register_hook(&dnrmg_ops); + rv = nf_register_net_hook(&init_net, &dnrmg_ops); if (rv) { netlink_kernel_release(dnrmg); } @@ -144,7 +144,7 @@ static int __init dn_rtmsg_init(void) static void __exit dn_rtmsg_fini(void) { - nf_unregister_hook(&dnrmg_ops); + nf_unregister_net_hook(&init_net, &dnrmg_ops); netlink_kernel_release(dnrmg); } -- cgit From aee12a0a3727e16fb837367c4755cb6daaf45109 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 20 Apr 2017 00:45:48 +0200 Subject: ebtables: remove nf_hook_register usage Similar to ip_register_table, pass nf_hook_ops to ebt_register_table(). This allows to handle hook registration also via pernet_ops and allows us to avoid use of legacy register_hook api. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter_bridge/ebtables.h | 6 ++- net/bridge/netfilter/ebtable_broute.c | 4 +- net/bridge/netfilter/ebtable_filter.c | 15 ++------ net/bridge/netfilter/ebtable_nat.c | 15 ++------ net/bridge/netfilter/ebtables.c | 61 +++++++++++++++++++------------ 5 files changed, 50 insertions(+), 51 deletions(-) diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h index 984b2112c77b..a30efb437e6d 100644 --- a/include/linux/netfilter_bridge/ebtables.h +++ b/include/linux/netfilter_bridge/ebtables.h @@ -109,8 +109,10 @@ struct ebt_table { #define EBT_ALIGN(s) (((s) + (__alignof__(struct _xt_align)-1)) & \ ~(__alignof__(struct _xt_align)-1)) extern struct ebt_table *ebt_register_table(struct net *net, - const struct ebt_table *table); -extern void ebt_unregister_table(struct net *net, struct ebt_table *table); + const struct ebt_table *table, + const struct nf_hook_ops *); +extern void ebt_unregister_table(struct net *net, struct ebt_table *table, + const struct nf_hook_ops *); extern unsigned int ebt_do_table(struct sk_buff *skb, const struct nf_hook_state *state, struct ebt_table *table); diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c index 8fe36dc3aab2..2585b100ebbb 100644 --- a/net/bridge/netfilter/ebtable_broute.c +++ b/net/bridge/netfilter/ebtable_broute.c @@ -65,13 +65,13 @@ static int ebt_broute(struct sk_buff *skb) static int __net_init broute_net_init(struct net *net) { - net->xt.broute_table = ebt_register_table(net, &broute_table); + net->xt.broute_table = ebt_register_table(net, &broute_table, NULL); return PTR_ERR_OR_ZERO(net->xt.broute_table); } static void __net_exit broute_net_exit(struct net *net) { - ebt_unregister_table(net, net->xt.broute_table); + ebt_unregister_table(net, net->xt.broute_table, NULL); } static struct pernet_operations broute_net_ops = { diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index 593a1bdc079e..f22ef7c21913 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c @@ -93,13 +93,13 @@ static struct nf_hook_ops ebt_ops_filter[] __read_mostly = { static int __net_init frame_filter_net_init(struct net *net) { - net->xt.frame_filter = ebt_register_table(net, &frame_filter); + net->xt.frame_filter = ebt_register_table(net, &frame_filter, ebt_ops_filter); return PTR_ERR_OR_ZERO(net->xt.frame_filter); } static void __net_exit frame_filter_net_exit(struct net *net) { - ebt_unregister_table(net, net->xt.frame_filter); + ebt_unregister_table(net, net->xt.frame_filter, ebt_ops_filter); } static struct pernet_operations frame_filter_net_ops = { @@ -109,20 +109,11 @@ static struct pernet_operations frame_filter_net_ops = { static int __init ebtable_filter_init(void) { - int ret; - - ret = register_pernet_subsys(&frame_filter_net_ops); - if (ret < 0) - return ret; - ret = nf_register_hooks(ebt_ops_filter, ARRAY_SIZE(ebt_ops_filter)); - if (ret < 0) - unregister_pernet_subsys(&frame_filter_net_ops); - return ret; + return register_pernet_subsys(&frame_filter_net_ops); } static void __exit ebtable_filter_fini(void) { - nf_unregister_hooks(ebt_ops_filter, ARRAY_SIZE(ebt_ops_filter)); unregister_pernet_subsys(&frame_filter_net_ops); } diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index eb33919821ee..2f7a4f314406 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c @@ -93,13 +93,13 @@ static struct nf_hook_ops ebt_ops_nat[] __read_mostly = { static int __net_init frame_nat_net_init(struct net *net) { - net->xt.frame_nat = ebt_register_table(net, &frame_nat); + net->xt.frame_nat = ebt_register_table(net, &frame_nat, ebt_ops_nat); return PTR_ERR_OR_ZERO(net->xt.frame_nat); } static void __net_exit frame_nat_net_exit(struct net *net) { - ebt_unregister_table(net, net->xt.frame_nat); + ebt_unregister_table(net, net->xt.frame_nat, ebt_ops_nat); } static struct pernet_operations frame_nat_net_ops = { @@ -109,20 +109,11 @@ static struct pernet_operations frame_nat_net_ops = { static int __init ebtable_nat_init(void) { - int ret; - - ret = register_pernet_subsys(&frame_nat_net_ops); - if (ret < 0) - return ret; - ret = nf_register_hooks(ebt_ops_nat, ARRAY_SIZE(ebt_ops_nat)); - if (ret < 0) - unregister_pernet_subsys(&frame_nat_net_ops); - return ret; + return register_pernet_subsys(&frame_nat_net_ops); } static void __exit ebtable_nat_fini(void) { - nf_unregister_hooks(ebt_ops_nat, ARRAY_SIZE(ebt_ops_nat)); unregister_pernet_subsys(&frame_nat_net_ops); } diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index bdc629eb0207..9ec0c9f908fa 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1157,8 +1157,30 @@ free_newinfo: return ret; } +static void __ebt_unregister_table(struct net *net, struct ebt_table *table) +{ + int i; + + mutex_lock(&ebt_mutex); + list_del(&table->list); + mutex_unlock(&ebt_mutex); + EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, + ebt_cleanup_entry, net, NULL); + if (table->private->nentries) + module_put(table->me); + vfree(table->private->entries); + if (table->private->chainstack) { + for_each_possible_cpu(i) + vfree(table->private->chainstack[i]); + vfree(table->private->chainstack); + } + vfree(table->private); + kfree(table); +} + struct ebt_table * -ebt_register_table(struct net *net, const struct ebt_table *input_table) +ebt_register_table(struct net *net, const struct ebt_table *input_table, + const struct nf_hook_ops *ops) { struct ebt_table_info *newinfo; struct ebt_table *t, *table; @@ -1238,6 +1260,16 @@ ebt_register_table(struct net *net, const struct ebt_table *input_table) } list_add(&table->list, &net->xt.tables[NFPROTO_BRIDGE]); mutex_unlock(&ebt_mutex); + + if (!ops) + return table; + + ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks)); + if (ret) { + __ebt_unregister_table(net, table); + return ERR_PTR(ret); + } + return table; free_unlock: mutex_unlock(&ebt_mutex); @@ -1256,29 +1288,12 @@ out: return ERR_PTR(ret); } -void ebt_unregister_table(struct net *net, struct ebt_table *table) +void ebt_unregister_table(struct net *net, struct ebt_table *table, + const struct nf_hook_ops *ops) { - int i; - - if (!table) { - BUGPRINT("Request to unregister NULL table!!!\n"); - return; - } - mutex_lock(&ebt_mutex); - list_del(&table->list); - mutex_unlock(&ebt_mutex); - EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, - ebt_cleanup_entry, net, NULL); - if (table->private->nentries) - module_put(table->me); - vfree(table->private->entries); - if (table->private->chainstack) { - for_each_possible_cpu(i) - vfree(table->private->chainstack[i]); - vfree(table->private->chainstack); - } - vfree(table->private); - kfree(table); + if (ops) + nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks)); + __ebt_unregister_table(net, table); } /* userspace just supplied us with counters */ -- cgit From 495dcb56d09ddb63afe30e799af41876c3f061cc Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Thu, 20 Apr 2017 14:01:45 +0800 Subject: netfilter: SYNPROXY: Return NF_STOLEN instead of NF_DROP during handshaking Current SYNPROXY codes return NF_DROP during normal TCP handshaking, it is not friendly to caller. Because the nf_hook_slow would treat the NF_DROP as an error, and return -EPERM. As a result, it may cause the top caller think it meets one error. For example, the following codes are from cfv_rx_poll() err = netif_receive_skb(skb); if (unlikely(err)) { ++cfv->ndev->stats.rx_dropped; } else { ++cfv->ndev->stats.rx_packets; cfv->ndev->stats.rx_bytes += skb_len; } When SYNPROXY returns NF_DROP, then netif_receive_skb returns -EPERM. As a result, the cfv driver would treat it as an error, and increase the rx_dropped counter. So use NF_STOLEN instead of NF_DROP now because there is no error happened indeed, and free the skb directly. Signed-off-by: Gao Feng Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/ipt_SYNPROXY.c | 21 ++++++++++++++------- net/ipv6/netfilter/ip6t_SYNPROXY.c | 20 ++++++++++++++------ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index c308ee0ee0bc..af2b69b6895f 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c @@ -293,12 +293,16 @@ synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par) XT_SYNPROXY_OPT_ECN); synproxy_send_client_synack(net, skb, th, &opts); - return NF_DROP; - + consume_skb(skb); + return NF_STOLEN; } else if (th->ack && !(th->fin || th->rst || th->syn)) { /* ACK from client */ - synproxy_recv_client_ack(net, skb, th, &opts, ntohl(th->seq)); - return NF_DROP; + if (synproxy_recv_client_ack(net, skb, th, &opts, ntohl(th->seq))) { + consume_skb(skb); + return NF_STOLEN; + } else { + return NF_DROP; + } } return XT_CONTINUE; @@ -367,10 +371,13 @@ static unsigned int ipv4_synproxy_hook(void *priv, * number match the one of first SYN. */ if (synproxy_recv_client_ack(net, skb, th, &opts, - ntohl(th->seq) + 1)) + ntohl(th->seq) + 1)) { this_cpu_inc(snet->stats->cookie_retrans); - - return NF_DROP; + consume_skb(skb); + return NF_STOLEN; + } else { + return NF_DROP; + } } synproxy->isn = ntohl(th->ack_seq); diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index 1252537f215f..d3c4daa708b9 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c @@ -307,12 +307,17 @@ synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par) XT_SYNPROXY_OPT_ECN); synproxy_send_client_synack(net, skb, th, &opts); - return NF_DROP; + consume_skb(skb); + return NF_STOLEN; } else if (th->ack && !(th->fin || th->rst || th->syn)) { /* ACK from client */ - synproxy_recv_client_ack(net, skb, th, &opts, ntohl(th->seq)); - return NF_DROP; + if (synproxy_recv_client_ack(net, skb, th, &opts, ntohl(th->seq))) { + consume_skb(skb); + return NF_STOLEN; + } else { + return NF_DROP; + } } return XT_CONTINUE; @@ -388,10 +393,13 @@ static unsigned int ipv6_synproxy_hook(void *priv, * number match the one of first SYN. */ if (synproxy_recv_client_ack(net, skb, th, &opts, - ntohl(th->seq) + 1)) + ntohl(th->seq) + 1)) { this_cpu_inc(snet->stats->cookie_retrans); - - return NF_DROP; + consume_skb(skb); + return NF_STOLEN; + } else { + return NF_DROP; + } } synproxy->isn = ntohl(th->ack_seq); -- cgit From 54044b1f0204da158a6a395bd02b63bb02ffff98 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 20 Apr 2017 09:54:22 +0200 Subject: netfilter: conntrack: remove prealloc support It was used by the nat extension, but since commit 7c9664351980 ("netfilter: move nat hlist_head to nf_conn") its only needed for connections that use MASQUERADE target or a nat helper. Also it seems a lot easier to preallocate a fixed size instead. With default settings, conntrack first adds ecache extension (sysctl defaults to 1), so we get 40(ct extension header) + 24 (ecache) == 64 byte on x86_64 for initial allocation. Followup patches can constify the extension structs and avoid the initial zeroing of the entire extension area. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_extend.h | 6 ---- net/netfilter/nf_conntrack_extend.c | 49 +++-------------------------- net/netfilter/nf_nat_core.c | 1 - 3 files changed, 4 insertions(+), 52 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 5fc908dc9f32..dd776bf9e2fa 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -88,21 +88,15 @@ static inline void nf_ct_ext_free(struct nf_conn *ct) /* Add this type, returns pointer to data or NULL. */ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp); -#define NF_CT_EXT_F_PREALLOC 0x0001 - struct nf_ct_ext_type { /* Destroys relationships (can be NULL). */ void (*destroy)(struct nf_conn *ct); enum nf_ct_ext_id id; - unsigned int flags; - /* Length and min alignment. */ u8 len; u8 align; - /* initial size of nf_ct_ext. */ - u8 alloc_size; }; int nf_ct_extend_register(struct nf_ct_ext_type *type); diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index b5879a9c748d..2e4b41bc67a0 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -18,6 +18,7 @@ static struct nf_ct_ext_type __rcu *nf_ct_ext_types[NF_CT_EXT_NUM]; static DEFINE_MUTEX(nf_ct_ext_type_mutex); +#define NF_CT_EXT_PREALLOC 128u /* conntrack events are on by default */ void __nf_ct_ext_destroy(struct nf_conn *ct) { @@ -46,9 +47,8 @@ EXPORT_SYMBOL(__nf_ct_ext_destroy); static void * nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp) { - unsigned int off, len; + unsigned int off, len, alloc; struct nf_ct_ext_type *t; - size_t alloc_size; rcu_read_lock(); t = rcu_dereference(nf_ct_ext_types[id]); @@ -59,10 +59,10 @@ nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp) off = ALIGN(sizeof(struct nf_ct_ext), t->align); len = off + t->len; - alloc_size = t->alloc_size; rcu_read_unlock(); - *ext = kzalloc(alloc_size, gfp); + alloc = max(len, NF_CT_EXT_PREALLOC); + *ext = kzalloc(alloc, gfp); if (!*ext) return NULL; @@ -115,41 +115,6 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) } EXPORT_SYMBOL(nf_ct_ext_add); -static void update_alloc_size(struct nf_ct_ext_type *type) -{ - int i, j; - struct nf_ct_ext_type *t1, *t2; - enum nf_ct_ext_id min = 0, max = NF_CT_EXT_NUM - 1; - - /* unnecessary to update all types */ - if ((type->flags & NF_CT_EXT_F_PREALLOC) == 0) { - min = type->id; - max = type->id; - } - - /* This assumes that extended areas in conntrack for the types - whose NF_CT_EXT_F_PREALLOC bit set are allocated in order */ - for (i = min; i <= max; i++) { - t1 = rcu_dereference_protected(nf_ct_ext_types[i], - lockdep_is_held(&nf_ct_ext_type_mutex)); - if (!t1) - continue; - - t1->alloc_size = ALIGN(sizeof(struct nf_ct_ext), t1->align) + - t1->len; - for (j = 0; j < NF_CT_EXT_NUM; j++) { - t2 = rcu_dereference_protected(nf_ct_ext_types[j], - lockdep_is_held(&nf_ct_ext_type_mutex)); - if (t2 == NULL || t2 == t1 || - (t2->flags & NF_CT_EXT_F_PREALLOC) == 0) - continue; - - t1->alloc_size = ALIGN(t1->alloc_size, t2->align) - + t2->len; - } - } -} - /* This MUST be called in process context. */ int nf_ct_extend_register(struct nf_ct_ext_type *type) { @@ -161,12 +126,7 @@ int nf_ct_extend_register(struct nf_ct_ext_type *type) goto out; } - /* This ensures that nf_ct_ext_create() can allocate enough area - before updating alloc_size */ - type->alloc_size = ALIGN(sizeof(struct nf_ct_ext), type->align) - + type->len; rcu_assign_pointer(nf_ct_ext_types[type->id], type); - update_alloc_size(type); out: mutex_unlock(&nf_ct_ext_type_mutex); return ret; @@ -178,7 +138,6 @@ void nf_ct_extend_unregister(struct nf_ct_ext_type *type) { mutex_lock(&nf_ct_ext_type_mutex); RCU_INIT_POINTER(nf_ct_ext_types[type->id], NULL); - update_alloc_size(type); mutex_unlock(&nf_ct_ext_type_mutex); synchronize_rcu(); } diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 9cbf49f9c1b7..86eeacbb4793 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -717,7 +717,6 @@ static struct nf_ct_ext_type nat_extend __read_mostly = { .align = __alignof__(struct nf_conn_nat), .destroy = nf_nat_cleanup_conntrack, .id = NF_CT_EXT_NAT, - .flags = NF_CT_EXT_F_PREALLOC, }; #if IS_ENABLED(CONFIG_NF_CT_NETLINK) -- cgit From 23f671a1b56a4493075ab7263c78c526ac12a592 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 20 Apr 2017 09:54:23 +0200 Subject: netfilter: conntrack: mark extension structs as const Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_extend.h | 4 ++-- net/netfilter/nf_conntrack_acct.c | 2 +- net/netfilter/nf_conntrack_ecache.c | 2 +- net/netfilter/nf_conntrack_extend.c | 4 ++-- net/netfilter/nf_conntrack_helper.c | 2 +- net/netfilter/nf_conntrack_labels.c | 2 +- net/netfilter/nf_conntrack_seqadj.c | 2 +- net/netfilter/nf_conntrack_timeout.c | 2 +- net/netfilter/nf_conntrack_timestamp.c | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index dd776bf9e2fa..b01f73fb4dcb 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -99,6 +99,6 @@ struct nf_ct_ext_type { u8 align; }; -int nf_ct_extend_register(struct nf_ct_ext_type *type); -void nf_ct_extend_unregister(struct nf_ct_ext_type *type); +int nf_ct_extend_register(const struct nf_ct_ext_type *type); +void nf_ct_extend_unregister(const struct nf_ct_ext_type *type); #endif /* _NF_CONNTRACK_EXTEND_H */ diff --git a/net/netfilter/nf_conntrack_acct.c b/net/netfilter/nf_conntrack_acct.c index 45da11afa785..866916712905 100644 --- a/net/netfilter/nf_conntrack_acct.c +++ b/net/netfilter/nf_conntrack_acct.c @@ -55,7 +55,7 @@ seq_print_acct(struct seq_file *s, const struct nf_conn *ct, int dir) }; EXPORT_SYMBOL_GPL(seq_print_acct); -static struct nf_ct_ext_type acct_extend __read_mostly = { +static const struct nf_ct_ext_type acct_extend = { .len = sizeof(struct nf_conn_acct), .align = __alignof__(struct nf_conn_acct), .id = NF_CT_EXT_ACCT, diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c index 515212948125..caac41ad9483 100644 --- a/net/netfilter/nf_conntrack_ecache.c +++ b/net/netfilter/nf_conntrack_ecache.c @@ -347,7 +347,7 @@ static struct ctl_table event_sysctl_table[] = { }; #endif /* CONFIG_SYSCTL */ -static struct nf_ct_ext_type event_extend __read_mostly = { +static const struct nf_ct_ext_type event_extend = { .len = sizeof(struct nf_conntrack_ecache), .align = __alignof__(struct nf_conntrack_ecache), .id = NF_CT_EXT_ECACHE, diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 2e4b41bc67a0..5c66816eb965 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -116,7 +116,7 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) EXPORT_SYMBOL(nf_ct_ext_add); /* This MUST be called in process context. */ -int nf_ct_extend_register(struct nf_ct_ext_type *type) +int nf_ct_extend_register(const struct nf_ct_ext_type *type) { int ret = 0; @@ -134,7 +134,7 @@ out: EXPORT_SYMBOL_GPL(nf_ct_extend_register); /* This MUST be called in process context. */ -void nf_ct_extend_unregister(struct nf_ct_ext_type *type) +void nf_ct_extend_unregister(const struct nf_ct_ext_type *type) { mutex_lock(&nf_ct_ext_type_mutex); RCU_INIT_POINTER(nf_ct_ext_types[type->id], NULL); diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 8239b4406f56..a57a52f173f7 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -535,7 +535,7 @@ void nf_conntrack_helpers_unregister(struct nf_conntrack_helper *helper, } EXPORT_SYMBOL_GPL(nf_conntrack_helpers_unregister); -static struct nf_ct_ext_type helper_extend __read_mostly = { +static const struct nf_ct_ext_type helper_extend = { .len = sizeof(struct nf_conn_help), .align = __alignof__(struct nf_conn_help), .id = NF_CT_EXT_HELPER, diff --git a/net/netfilter/nf_conntrack_labels.c b/net/netfilter/nf_conntrack_labels.c index bcab8bde7312..adf219859901 100644 --- a/net/netfilter/nf_conntrack_labels.c +++ b/net/netfilter/nf_conntrack_labels.c @@ -82,7 +82,7 @@ void nf_connlabels_put(struct net *net) } EXPORT_SYMBOL_GPL(nf_connlabels_put); -static struct nf_ct_ext_type labels_extend __read_mostly = { +static const struct nf_ct_ext_type labels_extend = { .len = sizeof(struct nf_conn_labels), .align = __alignof__(struct nf_conn_labels), .id = NF_CT_EXT_LABELS, diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c index ef7063eced7c..a975efd6b8c3 100644 --- a/net/netfilter/nf_conntrack_seqadj.c +++ b/net/netfilter/nf_conntrack_seqadj.c @@ -231,7 +231,7 @@ s32 nf_ct_seq_offset(const struct nf_conn *ct, } EXPORT_SYMBOL_GPL(nf_ct_seq_offset); -static struct nf_ct_ext_type nf_ct_seqadj_extend __read_mostly = { +static const struct nf_ct_ext_type nf_ct_seqadj_extend = { .len = sizeof(struct nf_conn_seqadj), .align = __alignof__(struct nf_conn_seqadj), .id = NF_CT_EXT_SEQADJ, diff --git a/net/netfilter/nf_conntrack_timeout.c b/net/netfilter/nf_conntrack_timeout.c index 26e742006c48..46aee65f339b 100644 --- a/net/netfilter/nf_conntrack_timeout.c +++ b/net/netfilter/nf_conntrack_timeout.c @@ -31,7 +31,7 @@ EXPORT_SYMBOL_GPL(nf_ct_timeout_find_get_hook); void (*nf_ct_timeout_put_hook)(struct ctnl_timeout *timeout) __read_mostly; EXPORT_SYMBOL_GPL(nf_ct_timeout_put_hook); -static struct nf_ct_ext_type timeout_extend __read_mostly = { +static const struct nf_ct_ext_type timeout_extend = { .len = sizeof(struct nf_conn_timeout), .align = __alignof__(struct nf_conn_timeout), .id = NF_CT_EXT_TIMEOUT, diff --git a/net/netfilter/nf_conntrack_timestamp.c b/net/netfilter/nf_conntrack_timestamp.c index 7a394df0deb7..4c4734b78318 100644 --- a/net/netfilter/nf_conntrack_timestamp.c +++ b/net/netfilter/nf_conntrack_timestamp.c @@ -33,7 +33,7 @@ static struct ctl_table tstamp_sysctl_table[] = { }; #endif /* CONFIG_SYSCTL */ -static struct nf_ct_ext_type tstamp_extend __read_mostly = { +static const struct nf_ct_ext_type tstamp_extend = { .len = sizeof(struct nf_conn_tstamp), .align = __alignof__(struct nf_conn_tstamp), .id = NF_CT_EXT_TSTAMP, -- cgit From 22d4536d2c836f74421d01d534b3117223a822a0 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 20 Apr 2017 09:54:24 +0200 Subject: netfilter: conntrack: handle initial extension alloc via krealloc krealloc(NULL, ..) is same as kmalloc(), so we can avoid special-casing the initial allocation after the prealloc removal (we had to use ->alloc_len as the initial allocation size). This also means we do not zero the preallocated memory anymore; only offsets[]. Existing code makes sure the new (used) extension space gets zeroed out. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_extend.c | 51 +++++++++++-------------------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 5c66816eb965..68ae1be08ed8 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -44,49 +44,24 @@ void __nf_ct_ext_destroy(struct nf_conn *ct) } EXPORT_SYMBOL(__nf_ct_ext_destroy); -static void * -nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp) -{ - unsigned int off, len, alloc; - struct nf_ct_ext_type *t; - - rcu_read_lock(); - t = rcu_dereference(nf_ct_ext_types[id]); - if (!t) { - rcu_read_unlock(); - return NULL; - } - - off = ALIGN(sizeof(struct nf_ct_ext), t->align); - len = off + t->len; - rcu_read_unlock(); - - alloc = max(len, NF_CT_EXT_PREALLOC); - *ext = kzalloc(alloc, gfp); - if (!*ext) - return NULL; - - (*ext)->offset[id] = off; - (*ext)->len = len; - - return (void *)(*ext) + off; -} - void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) { + unsigned int newlen, newoff, oldlen, alloc; struct nf_ct_ext *old, *new; - int newlen, newoff; struct nf_ct_ext_type *t; /* Conntrack must not be confirmed to avoid races on reallocation. */ NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); old = ct->ext; - if (!old) - return nf_ct_ext_create(&ct->ext, id, gfp); - if (__nf_ct_ext_exist(old, id)) - return NULL; + if (old) { + if (__nf_ct_ext_exist(old, id)) + return NULL; + oldlen = old->len; + } else { + oldlen = sizeof(*new); + } rcu_read_lock(); t = rcu_dereference(nf_ct_ext_types[id]); @@ -95,15 +70,19 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) return NULL; } - newoff = ALIGN(old->len, t->align); + newoff = ALIGN(oldlen, t->align); newlen = newoff + t->len; rcu_read_unlock(); - new = __krealloc(old, newlen, gfp); + alloc = max(newlen, NF_CT_EXT_PREALLOC); + new = __krealloc(old, alloc, gfp); if (!new) return NULL; - if (new != old) { + if (!old) { + memset(new->offset, 0, sizeof(new->offset)); + ct->ext = new; + } else if (new != old) { kfree_rcu(old, rcu); rcu_assign_pointer(ct->ext, new); } -- cgit From ff459018d7cb13e43ee2f857949c26b235ccd9a5 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 20 Apr 2017 10:11:33 +0200 Subject: netfilter: masquerade: attach nat extension if not present Currently the nat extension is always attached as soon as nat module is loaded. However, most NAT uses do not need the nat extension anymore. Prepare to remove the add-nat-by-default by making those places that need it attach it if its not present yet. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/nf_nat_masquerade_ipv4.c | 5 +++-- net/ipv6/netfilter/nf_nat_masquerade_ipv6.c | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c index ea91058b5f6f..dc1dea15c1b4 100644 --- a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c +++ b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c @@ -37,7 +37,6 @@ nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum, NF_CT_ASSERT(hooknum == NF_INET_POST_ROUTING); ct = nf_ct_get(skb, &ctinfo); - nat = nfct_nat(ct); NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY)); @@ -56,7 +55,9 @@ nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum, return NF_DROP; } - nat->masq_index = out->ifindex; + nat = nf_ct_nat_ext_add(ct); + if (nat) + nat->masq_index = out->ifindex; /* Transfer from original range. */ memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); diff --git a/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c b/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c index 051b6a6bfff6..2297c9f073ba 100644 --- a/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c +++ b/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c @@ -30,6 +30,7 @@ nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range *range, const struct net_device *out) { enum ip_conntrack_info ctinfo; + struct nf_conn_nat *nat; struct in6_addr src; struct nf_conn *ct; struct nf_nat_range newrange; @@ -42,7 +43,9 @@ nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range *range, &ipv6_hdr(skb)->daddr, 0, &src) < 0) return NF_DROP; - nfct_nat(ct)->masq_index = out->ifindex; + nat = nf_ct_nat_ext_add(ct); + if (nat) + nat->masq_index = out->ifindex; newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS; newrange.min_addr.in6 = src; -- cgit From 2fe7c321ab54d391dffb98911f5fb5cd315d6526 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 20 Apr 2017 10:11:34 +0200 Subject: netfilter: pptp: attach nat extension when needed make sure nat extension gets added if the master conntrack is subject to NAT. This will be required once the nat core stops adding it by default. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/nf_nat_pptp.c | 25 +++++++++++++++++++++---- net/netfilter/nf_conntrack_pptp.c | 12 ++++++++++-- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/net/ipv4/netfilter/nf_nat_pptp.c b/net/ipv4/netfilter/nf_nat_pptp.c index 211fee5fe59d..8a69363b4884 100644 --- a/net/ipv4/netfilter/nf_nat_pptp.c +++ b/net/ipv4/netfilter/nf_nat_pptp.c @@ -49,9 +49,14 @@ static void pptp_nat_expected(struct nf_conn *ct, const struct nf_ct_pptp_master *ct_pptp_info; const struct nf_nat_pptp *nat_pptp_info; struct nf_nat_range range; + struct nf_conn_nat *nat; + nat = nf_ct_nat_ext_add(ct); + if (WARN_ON_ONCE(!nat)) + return; + + nat_pptp_info = &nat->help.nat_pptp_info; ct_pptp_info = nfct_help_data(master); - nat_pptp_info = &nfct_nat(master)->help.nat_pptp_info; /* And here goes the grand finale of corrosion... */ if (exp->dir == IP_CT_DIR_ORIGINAL) { @@ -120,13 +125,17 @@ pptp_outbound_pkt(struct sk_buff *skb, { struct nf_ct_pptp_master *ct_pptp_info; + struct nf_conn_nat *nat = nfct_nat(ct); struct nf_nat_pptp *nat_pptp_info; u_int16_t msg; __be16 new_callid; unsigned int cid_off; + if (WARN_ON_ONCE(!nat)) + return NF_DROP; + + nat_pptp_info = &nat->help.nat_pptp_info; ct_pptp_info = nfct_help_data(ct); - nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; new_callid = ct_pptp_info->pns_call_id; @@ -191,11 +200,15 @@ pptp_exp_gre(struct nf_conntrack_expect *expect_orig, struct nf_conntrack_expect *expect_reply) { const struct nf_conn *ct = expect_orig->master; + struct nf_conn_nat *nat = nfct_nat(ct); struct nf_ct_pptp_master *ct_pptp_info; struct nf_nat_pptp *nat_pptp_info; + if (WARN_ON_ONCE(!nat)) + return; + + nat_pptp_info = &nat->help.nat_pptp_info; ct_pptp_info = nfct_help_data(ct); - nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; /* save original PAC call ID in nat_info */ nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id; @@ -223,11 +236,15 @@ pptp_inbound_pkt(struct sk_buff *skb, union pptp_ctrl_union *pptpReq) { const struct nf_nat_pptp *nat_pptp_info; + struct nf_conn_nat *nat = nfct_nat(ct); u_int16_t msg; __be16 new_pcid; unsigned int pcid_off; - nat_pptp_info = &nfct_nat(ct)->help.nat_pptp_info; + if (WARN_ON_ONCE(!nat)) + return NF_DROP; + + nat_pptp_info = &nat->help.nat_pptp_info; new_pcid = nat_pptp_info->pns_call_id; switch (msg = ntohs(ctlh->messageType)) { diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index 126031909fc7..6959e93063d4 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -263,7 +263,7 @@ out_unexpect_orig: goto out_put_both; } -static inline int +static int pptp_inbound_pkt(struct sk_buff *skb, unsigned int protoff, struct PptpControlHeader *ctlh, union pptp_ctrl_union *pptpReq, @@ -391,7 +391,7 @@ invalid: return NF_ACCEPT; } -static inline int +static int pptp_outbound_pkt(struct sk_buff *skb, unsigned int protoff, struct PptpControlHeader *ctlh, union pptp_ctrl_union *pptpReq, @@ -523,6 +523,14 @@ conntrack_pptp_help(struct sk_buff *skb, unsigned int protoff, int ret; u_int16_t msg; +#if IS_ENABLED(CONFIG_NF_NAT) + if (!nf_ct_is_confirmed(ct) && (ct->status & IPS_NAT_MASK)) { + struct nf_conn_nat *nat = nf_ct_ext_find(ct, NF_CT_EXT_NAT); + + if (!nat && !nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC)) + return NF_DROP; + } +#endif /* don't do any tracking before tcp handshake complete */ if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; -- cgit From 9a08ecfe74d7796ddc92ec312d3b7eaeba5a7c22 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 20 Apr 2017 10:11:35 +0200 Subject: netfilter: don't attach a nat extension by default nowadays the NAT extension only stores the interface index (used to purge connections that got masqueraded when interface goes down) and pptp nat information. Previous patches moved nf_ct_nat_ext_add to those places that need it. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_nat.h | 2 +- net/ipv4/netfilter/nf_nat_l3proto_ipv4.c | 4 +--- net/ipv6/netfilter/nf_nat_l3proto_ipv6.c | 4 +--- net/netfilter/nf_nat_core.c | 6 ------ 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index c327a431a6f3..05c82a1a4267 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -67,7 +67,7 @@ static inline bool nf_nat_oif_changed(unsigned int hooknum, { #if IS_ENABLED(CONFIG_NF_NAT_MASQUERADE_IPV4) || \ IS_ENABLED(CONFIG_NF_NAT_MASQUERADE_IPV6) - return nat->masq_index && hooknum == NF_INET_POST_ROUTING && + return nat && nat->masq_index && hooknum == NF_INET_POST_ROUTING && CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL && nat->masq_index != out->ifindex; #else diff --git a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c index e3bfa6a169f0..feedd759ca80 100644 --- a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c @@ -264,9 +264,7 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb, if (!ct) return NF_ACCEPT; - nat = nf_ct_nat_ext_add(ct); - if (nat == NULL) - return NF_ACCEPT; + nat = nfct_nat(ct); switch (ctinfo) { case IP_CT_RELATED: diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c index 922b5aef273c..bf3ad3e7b647 100644 --- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c @@ -273,9 +273,7 @@ nf_nat_ipv6_fn(void *priv, struct sk_buff *skb, if (!ct) return NF_ACCEPT; - nat = nf_ct_nat_ext_add(ct); - if (nat == NULL) - return NF_ACCEPT; + nat = nfct_nat(ct); switch (ctinfo) { case IP_CT_RELATED: diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 86eeacbb4793..ec9e6d8101b9 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -408,12 +408,6 @@ nf_nat_setup_info(struct nf_conn *ct, enum nf_nat_manip_type maniptype) { struct nf_conntrack_tuple curr_tuple, new_tuple; - struct nf_conn_nat *nat; - - /* nat helper or nfctnetlink also setup binding */ - nat = nf_ct_nat_ext_add(ct); - if (nat == NULL) - return NF_ACCEPT; NF_CT_ASSERT(maniptype == NF_NAT_MANIP_SRC || maniptype == NF_NAT_MANIP_DST); -- cgit From 46cfa2148e7371c537efff1a1c693e58f523089d Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 16 Apr 2017 19:32:07 -0500 Subject: rtlwifi: rtl8821ae: setup 8812ae RFE according to device type Current channel switch implementation sets 8812ae RFE reg value assuming that device always has type 2. Extend possible RFE types set and write corresponding reg values. Source for new code is http://dlcdnet.asus.com/pub/ASUS/wireless/PCE-AC51/DR_PCE_AC51_20232801152016.zip Signed-off-by: Maxim Samoylov Signed-off-by: Larry Finger Cc: Stable Cc: Yan-Hsuan Chuang Cc: Pkshih Cc: Birming Chiu Cc: Shaofu Cc: Steven Ting Signed-off-by: Kalle Valo --- .../net/wireless/realtek/rtlwifi/rtl8821ae/phy.c | 122 ++++++++++++++++++--- .../net/wireless/realtek/rtlwifi/rtl8821ae/reg.h | 1 + 2 files changed, 107 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c index 94a5e587a1cd..aa3ccc740521 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c @@ -358,6 +358,107 @@ bool rtl8821ae_phy_rf_config(struct ieee80211_hw *hw) return rtl8821ae_phy_rf6052_config(hw); } +static void _rtl8812ae_phy_set_rfe_reg_24g(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 tmp; + + switch (rtlhal->rfe_type) { + case 3: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x54337770); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x54337770); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, 0x900, 0x00000303, 0x1); + break; + case 4: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77777777); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x001); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x001); + break; + case 5: + rtl_write_byte(rtlpriv, RA_RFE_PINMUX + 2, 0x77); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777); + tmp = rtl_read_byte(rtlpriv, RA_RFE_INV + 3); + rtl_write_byte(rtlpriv, RA_RFE_INV + 3, tmp & ~0x1); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + break; + case 1: + if (rtlpriv->btcoexist.bt_coexistence) { + rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xffffff, 0x777777); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, + 0x77777777); + rtl_set_bbreg(hw, RA_RFE_INV, 0x33f00000, 0x000); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + break; + } + case 0: + case 2: + default: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77777777); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x000); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + break; + } +} + +static void _rtl8812ae_phy_set_rfe_reg_5g(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 tmp; + + switch (rtlhal->rfe_type) { + case 0: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77337717); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337717); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + break; + case 1: + if (rtlpriv->btcoexist.bt_coexistence) { + rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xffffff, 0x337717); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, + 0x77337717); + rtl_set_bbreg(hw, RA_RFE_INV, 0x33f00000, 0x000); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + } else { + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, + 0x77337717); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, + 0x77337717); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x000); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + } + break; + case 3: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x54337717); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x54337717); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, 0x900, 0x00000303, 0x1); + break; + case 5: + rtl_write_byte(rtlpriv, RA_RFE_PINMUX + 2, 0x33); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337777); + tmp = rtl_read_byte(rtlpriv, RA_RFE_INV + 3); + rtl_write_byte(rtlpriv, RA_RFE_INV + 3, tmp | 0x1); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + break; + case 2: + case 4: + default: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77337777); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337777); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + break; + } +} + u32 phy_get_tx_swing_8812A(struct ieee80211_hw *hw, u8 band, u8 rf_path) { @@ -552,14 +653,9 @@ void rtl8821ae_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band) /* 0x82C[1:0] = 2b'00 */ rtl_set_bbreg(hw, 0x82c, 0x3, 0); } - if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { - rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, - 0x77777777); - rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, - 0x77777777); - rtl_set_bbreg(hw, RA_RFE_INV, 0x3ff00000, 0x000); - rtl_set_bbreg(hw, RB_RFE_INV, 0x3ff00000, 0x000); - } + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) + _rtl8812ae_phy_set_rfe_reg_24g(hw); rtl_set_bbreg(hw, RTXPATH, 0xf0, 0x1); rtl_set_bbreg(hw, RCCK_RX, 0x0f000000, 0x1); @@ -614,14 +710,8 @@ void rtl8821ae_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band) /* 0x82C[1:0] = 2'b00 */ rtl_set_bbreg(hw, 0x82c, 0x3, 1); - if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { - rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, - 0x77337777); - rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, - 0x77337777); - rtl_set_bbreg(hw, RA_RFE_INV, 0x3ff00000, 0x010); - rtl_set_bbreg(hw, RB_RFE_INV, 0x3ff00000, 0x010); - } + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) + _rtl8812ae_phy_set_rfe_reg_5g(hw); rtl_set_bbreg(hw, RTXPATH, 0xf0, 0); rtl_set_bbreg(hw, RCCK_RX, 0x0f000000, 0xf); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h index 1d6110f9c1fb..ed69dbe178ff 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h @@ -2424,6 +2424,7 @@ #define BMASKH4BITS 0xf0000000 #define BMASKOFDM_D 0xffc00000 #define BMASKCCK 0x3f3f3f3f +#define BMASKRFEINV 0x3ff00000 #define BRFREGOFFSETMASK 0xfffff -- cgit From bea35f90dbb13c08389993847019b44047862952 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 22 Apr 2017 14:21:49 +0100 Subject: orinoco: fix spelling mistake: "Registerred" -> "Registered" trivial fix to spelling mistake in dbg_dbg message Signed-off-by: Colin Ian King Signed-off-by: Kalle Valo --- drivers/net/wireless/intersil/orinoco/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intersil/orinoco/main.c b/drivers/net/wireless/intersil/orinoco/main.c index 28cf97489001..d9128bb25e85 100644 --- a/drivers/net/wireless/intersil/orinoco/main.c +++ b/drivers/net/wireless/intersil/orinoco/main.c @@ -2283,7 +2283,7 @@ int orinoco_if_add(struct orinoco_private *priv, priv->ndev = dev; /* Report what we've done */ - dev_dbg(priv->dev, "Registerred interface %s.\n", dev->name); + dev_dbg(priv->dev, "Registered interface %s.\n", dev->name); return 0; -- cgit From 2f6ae79cb04bb7f9b4be3f1c32b6fda35bf976bc Mon Sep 17 00:00:00 2001 From: Maksim Salau Date: Sat, 22 Apr 2017 20:03:06 +0300 Subject: orinoco_usb: Fix buffer on stack Allocate buffer on HEAP instead of STACK for a local variable that is to be sent using usb_control_msg(). Signed-off-by: Maksim Salau Signed-off-by: Kalle Valo --- drivers/net/wireless/intersil/orinoco/orinoco_usb.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c index 98e1380b9917..132f5fbda58b 100644 --- a/drivers/net/wireless/intersil/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/intersil/orinoco/orinoco_usb.c @@ -769,18 +769,31 @@ static int ezusb_submit_in_urb(struct ezusb_priv *upriv) static inline int ezusb_8051_cpucs(struct ezusb_priv *upriv, int reset) { - u8 res_val = reset; /* avoid argument promotion */ + int ret; + u8 *res_val = NULL; if (!upriv->udev) { err("%s: !upriv->udev", __func__); return -EFAULT; } - return usb_control_msg(upriv->udev, + + res_val = kmalloc(sizeof(*res_val), GFP_KERNEL); + + if (!res_val) + return -ENOMEM; + + *res_val = reset; /* avoid argument promotion */ + + ret = usb_control_msg(upriv->udev, usb_sndctrlpipe(upriv->udev, 0), EZUSB_REQUEST_FW_TRANS, USB_TYPE_VENDOR | USB_RECIP_DEVICE | - USB_DIR_OUT, EZUSB_CPUCS_REG, 0, &res_val, - sizeof(res_val), DEF_TIMEOUT); + USB_DIR_OUT, EZUSB_CPUCS_REG, 0, res_val, + sizeof(*res_val), DEF_TIMEOUT); + + kfree(res_val); + + return ret; } static int ezusb_firmware_download(struct ezusb_priv *upriv, -- cgit From 5fb01e91daf84ad1e50edfcf63116ecbe31e7ba7 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sun, 23 Apr 2017 15:00:23 +0800 Subject: mt7601u: check return value of alloc_skb Function alloc_skb() will return a NULL pointer if there is no enough memory. However, in function mt7601u_mcu_msg_alloc(), its return value is not validated before it is used. This patch fixes it. Signed-off-by: Pan Bian Acked-by: Jakub Kicinski Signed-off-by: Kalle Valo --- drivers/net/wireless/mediatek/mt7601u/mcu.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt7601u/mcu.c b/drivers/net/wireless/mediatek/mt7601u/mcu.c index dbdfb3f5c507..a9f5f398b2f8 100644 --- a/drivers/net/wireless/mediatek/mt7601u/mcu.c +++ b/drivers/net/wireless/mediatek/mt7601u/mcu.c @@ -66,8 +66,10 @@ mt7601u_mcu_msg_alloc(struct mt7601u_dev *dev, const void *data, int len) WARN_ON(len % 4); /* if length is not divisible by 4 we need to pad */ skb = alloc_skb(len + MT_DMA_HDR_LEN + 4, GFP_KERNEL); - skb_reserve(skb, MT_DMA_HDR_LEN); - memcpy(skb_put(skb, len), data, len); + if (skb) { + skb_reserve(skb, MT_DMA_HDR_LEN); + memcpy(skb_put(skb, len), data, len); + } return skb; } @@ -170,6 +172,8 @@ static int mt7601u_mcu_function_select(struct mt7601u_dev *dev, }; skb = mt7601u_mcu_msg_alloc(dev, &msg, sizeof(msg)); + if (!skb) + return -ENOMEM; return mt7601u_mcu_msg_send(dev, skb, CMD_FUN_SET_OP, func == 5); } @@ -205,6 +209,8 @@ mt7601u_mcu_calibrate(struct mt7601u_dev *dev, enum mcu_calibrate cal, u32 val) }; skb = mt7601u_mcu_msg_alloc(dev, &msg, sizeof(msg)); + if (!skb) + return -ENOMEM; return mt7601u_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP, true); } -- cgit From dc3f89c38a8406554ffeffa370aad086a9c5e9de Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Sun, 23 Apr 2017 21:19:38 +0800 Subject: libertas: check return value of alloc_workqueue Function alloc_workqueue() will return a NULL pointer if there is no enough memory, and its return value should be validated before using. However, in function if_spi_probe(), its return value is not checked. This may result in a NULL dereference bug. This patch fixes the bug. Signed-off-by: Pan Bian Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/libertas/if_spi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/marvell/libertas/if_spi.c b/drivers/net/wireless/marvell/libertas/if_spi.c index c3a53cd6988e..7b4955cc38db 100644 --- a/drivers/net/wireless/marvell/libertas/if_spi.c +++ b/drivers/net/wireless/marvell/libertas/if_spi.c @@ -1181,6 +1181,10 @@ static int if_spi_probe(struct spi_device *spi) /* Initialize interrupt handling stuff. */ card->workqueue = alloc_workqueue("libertas_spi", WQ_MEM_RECLAIM, 0); + if (!card->workqueue) { + err = -ENOMEM; + goto remove_card; + } INIT_WORK(&card->packet_work, if_spi_host_to_card_worker); INIT_WORK(&card->resume_work, if_spi_resume_worker); @@ -1209,6 +1213,7 @@ release_irq: free_irq(spi->irq, card); terminate_workqueue: destroy_workqueue(card->workqueue); +remove_card: lbs_remove_card(priv); /* will call free_netdev */ free_card: free_if_spi_card(card); -- cgit From 9dc7efd3978aa67ae598129d2a3f240b390ce508 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Mon, 24 Apr 2017 08:40:28 +0800 Subject: rndis_wlan: add return value validation Function create_singlethread_workqueue() will return a NULL pointer if there is no enough memory, and its return value should be validated before using. However, in function rndis_wlan_bind(), its return value is not checked. This may cause NULL dereference bugs. This patch fixes it. Signed-off-by: Pan Bian Signed-off-by: Kalle Valo --- drivers/net/wireless/rndis_wlan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index eb513628d801..490068c02054 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -3428,6 +3428,10 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) /* because rndis_command() sleeps we need to use workqueue */ priv->workqueue = create_singlethread_workqueue("rndis_wlan"); + if (!priv->workqueue) { + wiphy_free(wiphy); + return -ENOMEM; + } INIT_WORK(&priv->work, rndis_wlan_worker); INIT_DELAYED_WORK(&priv->dev_poller_work, rndis_device_poller); INIT_DELAYED_WORK(&priv->scan_work, rndis_get_scan_results); -- cgit From 455a1eb4654c24560eb9dfc634f29cba3d87601e Mon Sep 17 00:00:00 2001 From: James Hughes Date: Mon, 24 Apr 2017 12:40:50 +0100 Subject: brcmfmac: Ensure pointer correctly set if skb data location changes The incoming skb header may be resized if header space is insufficient, which might change the data adddress in the skb. Ensure that a cached pointer to that data is correctly set by moving assignment to after any possible changes. Signed-off-by: James Hughes Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 24118ce72b4f..753db686649f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -197,7 +197,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, int ret; struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; - struct ethhdr *eh = (struct ethhdr *)(skb->data); + struct ethhdr *eh; brcmf_dbg(DATA, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx); @@ -235,6 +235,8 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, goto done; } + eh = (struct ethhdr *)(skb->data); + if (eh->h_proto == htons(ETH_P_PAE)) atomic_inc(&ifp->pend_8021x_cnt); -- cgit From 9cc4b7cb86cbcc6330a3faa8cd65268cd2d3c227 Mon Sep 17 00:00:00 2001 From: James Hughes Date: Tue, 25 Apr 2017 10:15:06 +0100 Subject: brcmfmac: Make skb header writable before use The driver was making changes to the skb_header without ensuring it was writable (i.e. uncloned). This patch also removes some boiler plate header size checking/adjustment code as that is also handled by the skb_cow_header function used to make header writable. Signed-off-by: James Hughes Acked-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/broadcom/brcm80211/brcmfmac/core.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 753db686649f..a3d82368f1a9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -210,22 +210,13 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, goto done; } - /* Make sure there's enough room for any header */ - if (skb_headroom(skb) < drvr->hdrlen) { - struct sk_buff *skb2; - - brcmf_dbg(INFO, "%s: insufficient headroom\n", + /* Make sure there's enough writable headroom*/ + ret = skb_cow_head(skb, drvr->hdrlen); + if (ret < 0) { + brcmf_err("%s: skb_cow_head failed\n", brcmf_ifname(ifp)); - drvr->bus_if->tx_realloc++; - skb2 = skb_realloc_headroom(skb, drvr->hdrlen); dev_kfree_skb(skb); - skb = skb2; - if (skb == NULL) { - brcmf_err("%s: skb_realloc_headroom failed\n", - brcmf_ifname(ifp)); - ret = -ENOMEM; - goto done; - } + goto done; } /* validate length for ether packet */ -- cgit From 038a3e858de4e3ddf42c330a22b7efcddbc0a81a Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 25 Apr 2017 11:41:34 +0200 Subject: rhashtable: remove insecure_max_entries param no users in the tree, insecure_max_entries is always set to ht->p.max_size * 2 in rhtashtable_init(). Replace only spot that uses it with a ht->p.max_size check. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 6 ++---- lib/rhashtable.c | 6 ------ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index ae87dcdf52d2..ae93b65d13d7 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -125,7 +125,6 @@ struct rhashtable; * @key_len: Length of key * @key_offset: Offset of key in struct to be hashed * @head_offset: Offset of rhash_head in struct to be hashed - * @insecure_max_entries: Maximum number of entries (may be exceeded) * @max_size: Maximum size while expanding * @min_size: Minimum size while shrinking * @nulls_base: Base value to generate nulls marker @@ -140,7 +139,6 @@ struct rhashtable_params { size_t key_len; size_t key_offset; size_t head_offset; - unsigned int insecure_max_entries; unsigned int max_size; unsigned int min_size; u32 nulls_base; @@ -329,8 +327,8 @@ static inline bool rht_grow_above_100(const struct rhashtable *ht, static inline bool rht_grow_above_max(const struct rhashtable *ht, const struct bucket_table *tbl) { - return ht->p.insecure_max_entries && - atomic_read(&ht->nelems) >= ht->p.insecure_max_entries; + return ht->p.max_size && + (atomic_read(&ht->nelems) / 2u) >= ht->p.max_size; } /* The bucket lock is selected based on the hash and protects mutations diff --git a/lib/rhashtable.c b/lib/rhashtable.c index d22a5ef109fb..f3b82e0d417b 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -961,12 +961,6 @@ int rhashtable_init(struct rhashtable *ht, if (params->max_size) ht->p.max_size = rounddown_pow_of_two(params->max_size); - if (params->insecure_max_entries) - ht->p.insecure_max_entries = - rounddown_pow_of_two(params->insecure_max_entries); - else - ht->p.insecure_max_entries = ht->p.max_size * 2; - ht->p.min_size = max(ht->p.min_size, HASH_MIN_SIZE); if (params->nelem_hint) -- cgit From 69e996c58a35db9ca79b3f021a15bcd22202e1c0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 25 Apr 2017 10:15:32 -0700 Subject: tcp: add tp->tcp_mstamp field We want to use precise timestamps in TCP stack, but we do not want to call possibly expensive kernel time services too often. tp->tcp_mstamp is guaranteed to be updated once per incoming packet. We will use it in the following patches, removing specific skb_mstamp_get() calls, and removing ack_time from struct tcp_sacktag_state. Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 + net/ipv4/tcp_input.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index cbe5b602a2d3..99a22f44c32e 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -240,6 +240,7 @@ struct tcp_sock { u32 tlp_high_seq; /* snd_nxt at the time of TLP retransmit. */ /* RTT measurement */ + struct skb_mstamp tcp_mstamp; /* most recent packet received/sent */ u32 srtt_us; /* smoothed round trip time << 3 in usecs */ u32 mdev_us; /* medium deviation */ u32 mdev_max_us; /* maximal mdev for the last rtt period */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 5af2f04f8859..bd18c65df4a9 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5362,6 +5362,7 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb, { struct tcp_sock *tp = tcp_sk(sk); + skb_mstamp_get(&tp->tcp_mstamp); if (unlikely(!sk->sk_rx_dst)) inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb); /* @@ -5922,6 +5923,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) case TCP_SYN_SENT: tp->rx_opt.saw_tstamp = 0; + skb_mstamp_get(&tp->tcp_mstamp); queued = tcp_rcv_synsent_state_process(sk, skb, th); if (queued >= 0) return queued; @@ -5933,6 +5935,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) return 0; } + skb_mstamp_get(&tp->tcp_mstamp); tp->rx_opt.saw_tstamp = 0; req = tp->fastopen_rsk; if (req) { -- cgit From 7c1c7308592f0f261836a96e37b7835ffd10d85b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 25 Apr 2017 10:15:33 -0700 Subject: tcp: do not pass timestamp to tcp_rack_detect_loss() We can use tp->tcp_mstamp as it contains a recent timestamp. This removes a call to skb_mstamp_get() from tcp_rack_reo_timeout() Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_recovery.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c index d8acbd9f477a..fdac262e277b 100644 --- a/net/ipv4/tcp_recovery.c +++ b/net/ipv4/tcp_recovery.c @@ -45,8 +45,7 @@ static bool tcp_rack_sent_after(const struct skb_mstamp *t1, * or tcp_time_to_recover()'s "Trick#1: the loss is proven" code path will * make us enter the CA_Recovery state. */ -static void tcp_rack_detect_loss(struct sock *sk, const struct skb_mstamp *now, - u32 *reo_timeout) +static void tcp_rack_detect_loss(struct sock *sk, u32 *reo_timeout) { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; @@ -79,7 +78,7 @@ static void tcp_rack_detect_loss(struct sock *sk, const struct skb_mstamp *now, * A packet is lost if its elapsed time is beyond * the recent RTT plus the reordering window. */ - u32 elapsed = skb_mstamp_us_delta(now, + u32 elapsed = skb_mstamp_us_delta(&tp->tcp_mstamp, &skb->skb_mstamp); s32 remaining = tp->rack.rtt_us + reo_wnd - elapsed; @@ -115,7 +114,7 @@ void tcp_rack_mark_lost(struct sock *sk, const struct skb_mstamp *now) /* Reset the advanced flag to avoid unnecessary queue scanning */ tp->rack.advanced = 0; - tcp_rack_detect_loss(sk, now, &timeout); + tcp_rack_detect_loss(sk, &timeout); if (timeout) { timeout = usecs_to_jiffies(timeout + TCP_REO_TIMEOUT_MIN); inet_csk_reset_xmit_timer(sk, ICSK_TIME_REO_TIMEOUT, @@ -165,12 +164,10 @@ void tcp_rack_advance(struct tcp_sock *tp, u8 sacked, u32 end_seq, void tcp_rack_reo_timeout(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); - struct skb_mstamp now; u32 timeout, prior_inflight; - skb_mstamp_get(&now); prior_inflight = tcp_packets_in_flight(tp); - tcp_rack_detect_loss(sk, &now, &timeout); + tcp_rack_detect_loss(sk, &timeout); if (prior_inflight != tcp_packets_in_flight(tp)) { if (inet_csk(sk)->icsk_ca_state != TCP_CA_Recovery) { tcp_enter_recovery(sk, false); -- cgit From 128eda86bebeacefb0fcc64cab0155aa76857c92 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 25 Apr 2017 10:15:34 -0700 Subject: tcp: do not pass timestamp to tcp_rack_mark_lost() This is no longer used, since tcp_rack_detect_loss() takes the timestamp from tp->tcp_mstamp Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/tcp.h | 2 +- net/ipv4/tcp_input.c | 2 +- net/ipv4/tcp_recovery.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index da28bef1d82b..8b4433c4aaa2 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1853,7 +1853,7 @@ void tcp_v4_init(void); void tcp_init(void); /* tcp_recovery.c */ -extern void tcp_rack_mark_lost(struct sock *sk, const struct skb_mstamp *now); +extern void tcp_rack_mark_lost(struct sock *sk); extern void tcp_rack_advance(struct tcp_sock *tp, u8 sacked, u32 end_seq, const struct skb_mstamp *xmit_time, const struct skb_mstamp *ack_time); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index bd18c65df4a9..d4885f7a6a93 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2769,7 +2769,7 @@ static void tcp_rack_identify_loss(struct sock *sk, int *ack_flag, if (sysctl_tcp_recovery & TCP_RACK_LOSS_DETECTION) { u32 prior_retrans = tp->retrans_out; - tcp_rack_mark_lost(sk, ack_time); + tcp_rack_mark_lost(sk); if (prior_retrans > tp->retrans_out) *ack_flag |= FLAG_LOST_RETRANS; } diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c index fdac262e277b..6ca8b5d9d803 100644 --- a/net/ipv4/tcp_recovery.c +++ b/net/ipv4/tcp_recovery.c @@ -104,7 +104,7 @@ static void tcp_rack_detect_loss(struct sock *sk, u32 *reo_timeout) } } -void tcp_rack_mark_lost(struct sock *sk, const struct skb_mstamp *now) +void tcp_rack_mark_lost(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); u32 timeout; -- cgit From efab8f85826afbbfe4b0ca9208e006aabbd2df3a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 25 Apr 2017 10:15:35 -0700 Subject: tcp: do not pass timestamp to tcp_rack_identify_loss() Not used anymore now tp->tcp_mstamp holds the information. Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index d4885f7a6a93..99b0d65de169 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2760,8 +2760,7 @@ static bool tcp_try_undo_partial(struct sock *sk, const int acked) return false; } -static void tcp_rack_identify_loss(struct sock *sk, int *ack_flag, - const struct skb_mstamp *ack_time) +static void tcp_rack_identify_loss(struct sock *sk, int *ack_flag) { struct tcp_sock *tp = tcp_sk(sk); @@ -2857,11 +2856,11 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked, tcp_try_keep_open(sk); return; } - tcp_rack_identify_loss(sk, ack_flag, ack_time); + tcp_rack_identify_loss(sk, ack_flag); break; case TCP_CA_Loss: tcp_process_loss(sk, flag, is_dupack, rexmit); - tcp_rack_identify_loss(sk, ack_flag, ack_time); + tcp_rack_identify_loss(sk, ack_flag); if (!(icsk->icsk_ca_state == TCP_CA_Open || (*ack_flag & FLAG_LOST_RETRANS))) return; @@ -2877,7 +2876,7 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked, if (icsk->icsk_ca_state <= TCP_CA_Disorder) tcp_try_undo_dsack(sk); - tcp_rack_identify_loss(sk, ack_flag, ack_time); + tcp_rack_identify_loss(sk, ack_flag); if (!tcp_time_to_recover(sk, flag)) { tcp_try_to_open(sk, flag); return; -- cgit From 1317a9d69f5fa0f5417237772f55a4aac49f7921 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 25 Apr 2017 10:15:36 -0700 Subject: tcp: do not pass timestamp to tcp_fastretrans_alert() Not used anymore now tp->tcp_mstamp holds the information. This is needed to remove sack_state.ack_time in a following patch. Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 99b0d65de169..68094aa8cfb2 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2787,8 +2787,7 @@ static void tcp_rack_identify_loss(struct sock *sk, int *ack_flag) * tcp_xmit_retransmit_queue(). */ static void tcp_fastretrans_alert(struct sock *sk, const int acked, - bool is_dupack, int *ack_flag, int *rexmit, - const struct skb_mstamp *ack_time) + bool is_dupack, int *ack_flag, int *rexmit) { struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); @@ -3646,8 +3645,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (tcp_ack_is_dubious(sk, flag)) { is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP)); - tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit, - &sack_state.ack_time); + tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit); } if (tp->tlp_high_seq) tcp_process_tlp_ack(sk, ack, flag); @@ -3668,8 +3666,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) no_queue: /* If data was DSACKed, see if we can undo a cwnd reduction. */ if (flag & FLAG_DSACKING_ACK) - tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit, - &sack_state.ack_time); + tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit); /* If this ack opens up a zero window, clear backoff. It was * being used to time the probes, and is probably far higher than * it needs to be for normal retransmission. @@ -3693,8 +3690,7 @@ old_ack: skb_mstamp_get(&sack_state.ack_time); flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una, &sack_state); - tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit, - &sack_state.ack_time); + tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit); tcp_xmit_recovery(sk, rexmit); } -- cgit From 88d5c65098e5d15f2cea81f90bb6ecc167e1aa3b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 25 Apr 2017 10:15:37 -0700 Subject: tcp: do not pass timestamp to tcp_rate_gen() No longer needed, since tp->tcp_mstamp holds the information. This is needed to remove sack_state.ack_time in a following patch. Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/tcp.h | 2 +- net/ipv4/tcp_input.c | 3 +-- net/ipv4/tcp_rate.c | 7 ++++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 8b4433c4aaa2..d7aae25efc7f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1004,7 +1004,7 @@ void tcp_rate_skb_sent(struct sock *sk, struct sk_buff *skb); void tcp_rate_skb_delivered(struct sock *sk, struct sk_buff *skb, struct rate_sample *rs); void tcp_rate_gen(struct sock *sk, u32 delivered, u32 lost, - struct skb_mstamp *now, struct rate_sample *rs); + struct rate_sample *rs); void tcp_rate_check_app_limited(struct sock *sk); /* These functions determine how the current flow behaves in respect of SACK diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 68094aa8cfb2..2d84483de2e1 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3657,8 +3657,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) tcp_schedule_loss_probe(sk); delivered = tp->delivered - delivered; /* freshly ACKed or SACKed */ lost = tp->lost - lost; /* freshly marked lost */ - tcp_rate_gen(sk, delivered, lost, &sack_state.ack_time, - sack_state.rate); + tcp_rate_gen(sk, delivered, lost, sack_state.rate); tcp_cong_control(sk, ack, delivered, flag, sack_state.rate); tcp_xmit_recovery(sk, rexmit); return 1; diff --git a/net/ipv4/tcp_rate.c b/net/ipv4/tcp_rate.c index 9be1581a5a08..c6a9fa894646 100644 --- a/net/ipv4/tcp_rate.c +++ b/net/ipv4/tcp_rate.c @@ -106,7 +106,7 @@ void tcp_rate_skb_delivered(struct sock *sk, struct sk_buff *skb, /* Update the connection delivery information and generate a rate sample. */ void tcp_rate_gen(struct sock *sk, u32 delivered, u32 lost, - struct skb_mstamp *now, struct rate_sample *rs) + struct rate_sample *rs) { struct tcp_sock *tp = tcp_sk(sk); u32 snd_us, ack_us; @@ -120,7 +120,7 @@ void tcp_rate_gen(struct sock *sk, u32 delivered, u32 lost, * to carry current time, flags, stats like "tcp_sacktag_state". */ if (delivered) - tp->delivered_mstamp = *now; + tp->delivered_mstamp = tp->tcp_mstamp; rs->acked_sacked = delivered; /* freshly ACKed or SACKed */ rs->losses = lost; /* freshly marked lost */ @@ -138,7 +138,8 @@ void tcp_rate_gen(struct sock *sk, u32 delivered, u32 lost, * longer phase. */ snd_us = rs->interval_us; /* send phase */ - ack_us = skb_mstamp_us_delta(now, &rs->prior_mstamp); /* ack phase */ + ack_us = skb_mstamp_us_delta(&tp->tcp_mstamp, + &rs->prior_mstamp); /* ack phase */ rs->interval_us = max(snd_us, ack_us); /* Normally we expect interval_us >= min-rtt. -- cgit From d2329f102d846214e449941289c7009e16be01a0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 25 Apr 2017 10:15:38 -0700 Subject: tcp: do not pass timestamp to tcp_rack_advance() No longer needed, since tp->tcp_mstamp holds the information. This is needed to remove sack_state.ack_time in a following patch. Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/tcp.h | 3 +-- net/ipv4/tcp_input.c | 6 ++---- net/ipv4/tcp_recovery.c | 5 ++--- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index d7aae25efc7f..270e5cc43c99 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1855,8 +1855,7 @@ void tcp_init(void); /* tcp_recovery.c */ extern void tcp_rack_mark_lost(struct sock *sk); extern void tcp_rack_advance(struct tcp_sock *tp, u8 sacked, u32 end_seq, - const struct skb_mstamp *xmit_time, - const struct skb_mstamp *ack_time); + const struct skb_mstamp *xmit_time); extern void tcp_rack_reo_timeout(struct sock *sk); /* diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2d84483de2e1..5485204853d3 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1214,8 +1214,7 @@ static u8 tcp_sacktag_one(struct sock *sk, return sacked; if (!(sacked & TCPCB_SACKED_ACKED)) { - tcp_rack_advance(tp, sacked, end_seq, - xmit_time, &state->ack_time); + tcp_rack_advance(tp, sacked, end_seq, xmit_time); if (sacked & TCPCB_SACKED_RETRANS) { /* If the segment is not tagged as lost, @@ -3118,8 +3117,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, tp->delivered += acked_pcount; if (!tcp_skb_spurious_retrans(tp, skb)) tcp_rack_advance(tp, sacked, scb->end_seq, - &skb->skb_mstamp, - &sack->ack_time); + &skb->skb_mstamp); } if (sacked & TCPCB_LOST) tp->lost_out -= acked_pcount; diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c index 6ca8b5d9d803..cd72b3d3879e 100644 --- a/net/ipv4/tcp_recovery.c +++ b/net/ipv4/tcp_recovery.c @@ -127,8 +127,7 @@ void tcp_rack_mark_lost(struct sock *sk) * draft-cheng-tcpm-rack-00.txt */ void tcp_rack_advance(struct tcp_sock *tp, u8 sacked, u32 end_seq, - const struct skb_mstamp *xmit_time, - const struct skb_mstamp *ack_time) + const struct skb_mstamp *xmit_time) { u32 rtt_us; @@ -137,7 +136,7 @@ void tcp_rack_advance(struct tcp_sock *tp, u8 sacked, u32 end_seq, end_seq, tp->rack.end_seq)) return; - rtt_us = skb_mstamp_us_delta(ack_time, xmit_time); + rtt_us = skb_mstamp_us_delta(&tp->tcp_mstamp, xmit_time); if (sacked & TCPCB_RETRANS) { /* If the sacked packet was retransmitted, it's ambiguous * whether the retransmission or the original (or the prior -- cgit From 7e0ca8a4c19c5fbaa802ffd609f5f9ab9249af5d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 25 Apr 2017 10:15:39 -0700 Subject: tcp: use tp->tcp_mstamp in tcp_clean_rtx_queue() Following patch will remove ack_time from struct tcp_sacktag_state Same info is now found in tp->tcp_mstamp Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 5485204853d3..f4e1836c696c 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3056,8 +3056,8 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, { const struct inet_connection_sock *icsk = inet_csk(sk); struct skb_mstamp first_ackt, last_ackt; - struct skb_mstamp *now = &sack->ack_time; struct tcp_sock *tp = tcp_sk(sk); + struct skb_mstamp *now = &tp->tcp_mstamp; u32 prior_sacked = tp->sacked_out; u32 reord = tp->packets_out; bool fully_acked = true; -- cgit From a6db50b81e3f20b2b692bbddd35d9484057eae9d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 25 Apr 2017 10:15:40 -0700 Subject: tcp: remove ack_time from struct tcp_sacktag_state It is no longer needed, everything uses tp->tcp_mstamp instead. Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index f4e1836c696c..f475f0b53bfe 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1131,7 +1131,6 @@ struct tcp_sacktag_state { */ struct skb_mstamp first_sackt; struct skb_mstamp last_sackt; - struct skb_mstamp ack_time; /* Timestamp when the S/ACK was received */ struct rate_sample *rate; int flag; }; @@ -3572,8 +3571,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (after(ack, tp->snd_nxt)) goto invalid_ack; - skb_mstamp_get(&sack_state.ack_time); - if (icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) tcp_rearm_rto(sk); @@ -3684,7 +3681,6 @@ old_ack: * If data was DSACKed, see if we can undo a cwnd reduction. */ if (TCP_SKB_CB(skb)->sacked) { - skb_mstamp_get(&sack_state.ack_time); flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una, &sack_state); tcp_fastretrans_alert(sk, acked, is_dupack, &flag, &rexmit); -- cgit From 645f4c6f2ebd040688cc2a5f626ffc909e66ccf2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 25 Apr 2017 10:15:41 -0700 Subject: tcp: switch rcv_rtt_est and rcvq_space to high resolution timestamps Some devices or distributions use HZ=100 or HZ=250 TCP receive buffer autotuning has poor behavior caused by this choice. Since autotuning happens after 4 ms or 10 ms, short distance flows get their receive buffer tuned to a very high value, but after an initial period where it was frozen to (too small) initial value. With tp->tcp_mstamp introduction, we can switch to high resolution timestamps almost for free (at the expense of 8 additional bytes per TCP structure) Note that some TCP stacks use usec TCP timestamps where this patch makes even more sense : Many TCP flows have < 500 usec RTT. Hopefully this finer TS option can be standardized soon. Tested: HZ=100 kernel ./netperf -H lpaa24 -t TCP_RR -l 1000 -- -r 10000,10000 & Peer without patch : lpaa24:~# ss -tmi dst lpaa23 ... skmem:(r0,rb8388608,...) rcv_rtt:10 rcv_space:3210000 minrtt:0.017 Peer with the patch : lpaa23:~# ss -tmi dst lpaa24 ... skmem:(r0,rb428800,...) rcv_rtt:0.069 rcv_space:30000 minrtt:0.017 We can see saner RCVBUF, and more precise rcv_rtt information. Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/linux/tcp.h | 12 ++++++------ net/ipv4/tcp.c | 2 +- net/ipv4/tcp_input.c | 28 +++++++++++++++++----------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 99a22f44c32e..b6d5adcee8fc 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -333,16 +333,16 @@ struct tcp_sock { /* Receiver side RTT estimation */ struct { - u32 rtt; - u32 seq; - u32 time; + u32 rtt_us; + u32 seq; + struct skb_mstamp time; } rcv_rtt_est; /* Receiver queue space */ struct { - int space; - u32 seq; - u32 time; + int space; + u32 seq; + struct skb_mstamp time; } rcvq_space; /* TCP-specific MTU probe information. */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index efc976ae66ae..059dad7deefe 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2853,7 +2853,7 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) info->tcpi_snd_ssthresh = tp->snd_ssthresh; info->tcpi_advmss = tp->advmss; - info->tcpi_rcv_rtt = jiffies_to_usecs(tp->rcv_rtt_est.rtt)>>3; + info->tcpi_rcv_rtt = tp->rcv_rtt_est.rtt_us >> 3; info->tcpi_rcv_space = tp->rcvq_space.space; info->tcpi_total_retrans = tp->total_retrans; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index f475f0b53bfe..9739962bfb3f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -442,7 +442,8 @@ void tcp_init_buffer_space(struct sock *sk) tcp_sndbuf_expand(sk); tp->rcvq_space.space = tp->rcv_wnd; - tp->rcvq_space.time = tcp_time_stamp; + skb_mstamp_get(&tp->tcp_mstamp); + tp->rcvq_space.time = tp->tcp_mstamp; tp->rcvq_space.seq = tp->copied_seq; maxwin = tcp_full_space(sk); @@ -518,7 +519,7 @@ EXPORT_SYMBOL(tcp_initialize_rcv_mss); */ static void tcp_rcv_rtt_update(struct tcp_sock *tp, u32 sample, int win_dep) { - u32 new_sample = tp->rcv_rtt_est.rtt; + u32 new_sample = tp->rcv_rtt_est.rtt_us; long m = sample; if (m == 0) @@ -548,21 +549,23 @@ static void tcp_rcv_rtt_update(struct tcp_sock *tp, u32 sample, int win_dep) new_sample = m << 3; } - if (tp->rcv_rtt_est.rtt != new_sample) - tp->rcv_rtt_est.rtt = new_sample; + tp->rcv_rtt_est.rtt_us = new_sample; } static inline void tcp_rcv_rtt_measure(struct tcp_sock *tp) { - if (tp->rcv_rtt_est.time == 0) + u32 delta_us; + + if (tp->rcv_rtt_est.time.v64 == 0) goto new_measure; if (before(tp->rcv_nxt, tp->rcv_rtt_est.seq)) return; - tcp_rcv_rtt_update(tp, tcp_time_stamp - tp->rcv_rtt_est.time, 1); + delta_us = skb_mstamp_us_delta(&tp->tcp_mstamp, &tp->rcv_rtt_est.time); + tcp_rcv_rtt_update(tp, delta_us, 1); new_measure: tp->rcv_rtt_est.seq = tp->rcv_nxt + tp->rcv_wnd; - tp->rcv_rtt_est.time = tcp_time_stamp; + tp->rcv_rtt_est.time = tp->tcp_mstamp; } static inline void tcp_rcv_rtt_measure_ts(struct sock *sk, @@ -572,7 +575,10 @@ static inline void tcp_rcv_rtt_measure_ts(struct sock *sk, if (tp->rx_opt.rcv_tsecr && (TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq >= inet_csk(sk)->icsk_ack.rcv_mss)) - tcp_rcv_rtt_update(tp, tcp_time_stamp - tp->rx_opt.rcv_tsecr, 0); + tcp_rcv_rtt_update(tp, + jiffies_to_usecs(tcp_time_stamp - + tp->rx_opt.rcv_tsecr), + 0); } /* @@ -585,8 +591,8 @@ void tcp_rcv_space_adjust(struct sock *sk) int time; int copied; - time = tcp_time_stamp - tp->rcvq_space.time; - if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0) + time = skb_mstamp_us_delta(&tp->tcp_mstamp, &tp->rcvq_space.time); + if (time < (tp->rcv_rtt_est.rtt_us >> 3) || tp->rcv_rtt_est.rtt_us == 0) return; /* Number of bytes copied to user in last RTT */ @@ -642,7 +648,7 @@ void tcp_rcv_space_adjust(struct sock *sk) new_measure: tp->rcvq_space.seq = tp->copied_seq; - tp->rcvq_space.time = tcp_time_stamp; + tp->rcvq_space.time = tp->tcp_mstamp; } /* There is something which you must keep in mind when you analyze the -- cgit From 4c5e7a2c0501bd531aad1d0378c589a92cb3cc31 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 25 Apr 2017 11:33:03 -0700 Subject: dt-bindings: mdio: Clarify binding document The described GPIO reset property is applicable to *all* child PHYs. If we have one reset line per PHY present on the MDIO bus, these automatically become properties of the child PHY nodes. Finally, indicate how the RESET pulse width must be defined, which is the maximum value of all individual PHYs RESET pulse widths determined by reading their datasheets. Fixes: 69226896ad63 ("mdio_bus: Issue GPIO RESET to PHYs.") Signed-off-by: Florian Fainelli Reviewed-by: Roger Quadros Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/mdio.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/net/mdio.txt b/Documentation/devicetree/bindings/net/mdio.txt index 4ffbbacebda1..96a53f89aa6e 100644 --- a/Documentation/devicetree/bindings/net/mdio.txt +++ b/Documentation/devicetree/bindings/net/mdio.txt @@ -3,13 +3,17 @@ Common MDIO bus properties. These are generic properties that can apply to any MDIO bus. Optional properties: -- reset-gpios: List of one or more GPIOs that control the RESET lines - of the PHYs on that MDIO bus. -- reset-delay-us: RESET pulse width in microseconds as per PHY datasheet. +- reset-gpios: One GPIO that control the RESET lines of all PHYs on that MDIO + bus. +- reset-delay-us: RESET pulse width in microseconds. A list of child nodes, one per device on the bus is expected. These should follow the generic phy.txt, or a device specific binding document. +The 'reset-delay-us' indicates the RESET signal pulse width in microseconds and +applies to all PHY devices. It must therefore be appropriately determined based +on all PHY requirements (maximum value of all per-PHY RESET pulse widths). + Example : This example shows these optional properties, plus other properties required for the TI Davinci MDIO driver. @@ -21,7 +25,7 @@ required for the TI Davinci MDIO driver. #size-cells = <0>; reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; - reset-delay-us = <2>; /* PHY datasheet states 1us min */ + reset-delay-us = <2>; ethphy0: ethernet-phy@1 { reg = <1>; -- cgit From d346b9bc4f652ddaf29b2d80ec7fc75d3b07124e Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Tue, 25 Apr 2017 15:01:04 -0400 Subject: ibmvnic: Split initialization of scrqs to its own routine Split the sending of capability request crqs and the initialization of sub crqs into their own routines. This is a first step to moving the allocation of sub-crqs out of interrupt context. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 101 ++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 47 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 51bf337bea7a..c2e260ce45bb 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1678,48 +1678,20 @@ req_tx_irq_failed: return rc; } -static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry) +static int init_sub_crqs(struct ibmvnic_adapter *adapter) { struct device *dev = &adapter->vdev->dev; struct ibmvnic_sub_crq_queue **allqueues; int registered_queues = 0; - union ibmvnic_crq crq; int total_queues; int more = 0; int i; - if (!retry) { - /* Sub-CRQ entries are 32 byte long */ - int entries_page = 4 * PAGE_SIZE / (sizeof(u64) * 4); - - if (adapter->min_tx_entries_per_subcrq > entries_page || - adapter->min_rx_add_entries_per_subcrq > entries_page) { - dev_err(dev, "Fatal, invalid entries per sub-crq\n"); - goto allqueues_failed; - } - - /* Get the minimum between the queried max and the entries - * that fit in our PAGE_SIZE - */ - adapter->req_tx_entries_per_subcrq = - adapter->max_tx_entries_per_subcrq > entries_page ? - entries_page : adapter->max_tx_entries_per_subcrq; - adapter->req_rx_add_entries_per_subcrq = - adapter->max_rx_add_entries_per_subcrq > entries_page ? - entries_page : adapter->max_rx_add_entries_per_subcrq; - - adapter->req_tx_queues = adapter->opt_tx_comp_sub_queues; - adapter->req_rx_queues = adapter->opt_rx_comp_queues; - adapter->req_rx_add_queues = adapter->max_rx_add_queues; - - adapter->req_mtu = adapter->netdev->mtu + ETH_HLEN; - } - total_queues = adapter->req_tx_queues + adapter->req_rx_queues; allqueues = kcalloc(total_queues, sizeof(*allqueues), GFP_ATOMIC); if (!allqueues) - goto allqueues_failed; + return -1; for (i = 0; i < total_queues; i++) { allqueues[i] = init_sub_crq_queue(adapter); @@ -1776,6 +1748,56 @@ static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry) adapter->rx_scrq[i]->scrq_num = i; } + kfree(allqueues); + return 0; + +rx_failed: + kfree(adapter->tx_scrq); + adapter->tx_scrq = NULL; +tx_failed: + for (i = 0; i < registered_queues; i++) + release_sub_crq_queue(adapter, allqueues[i]); + kfree(allqueues); + return -1; +} + +static void ibmvnic_send_req_caps(struct ibmvnic_adapter *adapter, int retry) +{ + struct device *dev = &adapter->vdev->dev; + union ibmvnic_crq crq; + int rc; + + if (!retry) { + /* Sub-CRQ entries are 32 byte long */ + int entries_page = 4 * PAGE_SIZE / (sizeof(u64) * 4); + + if (adapter->min_tx_entries_per_subcrq > entries_page || + adapter->min_rx_add_entries_per_subcrq > entries_page) { + dev_err(dev, "Fatal, invalid entries per sub-crq\n"); + return; + } + + /* Get the minimum between the queried max and the entries + * that fit in our PAGE_SIZE + */ + adapter->req_tx_entries_per_subcrq = + adapter->max_tx_entries_per_subcrq > entries_page ? + entries_page : adapter->max_tx_entries_per_subcrq; + adapter->req_rx_add_entries_per_subcrq = + adapter->max_rx_add_entries_per_subcrq > entries_page ? + entries_page : adapter->max_rx_add_entries_per_subcrq; + + adapter->req_tx_queues = adapter->opt_tx_comp_sub_queues; + adapter->req_rx_queues = adapter->opt_rx_comp_queues; + adapter->req_rx_add_queues = adapter->max_rx_add_queues; + + adapter->req_mtu = adapter->netdev->mtu + ETH_HLEN; + } + + rc = init_sub_crqs(adapter); + if (rc) + return; + memset(&crq, 0, sizeof(crq)); crq.request_capability.first = IBMVNIC_CRQ_CMD; crq.request_capability.cmd = REQUEST_CAPABILITY; @@ -1829,20 +1851,6 @@ static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry) atomic_inc(&adapter->running_cap_crqs); ibmvnic_send_crq(adapter, &crq); } - - kfree(allqueues); - - return; - -rx_failed: - kfree(adapter->tx_scrq); - adapter->tx_scrq = NULL; -tx_failed: - for (i = 0; i < registered_queues; i++) - release_sub_crq_queue(adapter, allqueues[i]); - kfree(allqueues); -allqueues_failed: - ibmvnic_remove(adapter->vdev); } static int pending_scrq(struct ibmvnic_adapter *adapter, @@ -2568,7 +2576,7 @@ static void handle_request_cap_rsp(union ibmvnic_crq *crq, number), name); release_sub_crqs(adapter); *req_value = be64_to_cpu(crq->request_capability_rsp.number); - init_sub_crqs(adapter, 1); + ibmvnic_send_req_caps(adapter, 1); return; default: dev_err(dev, "Error %d in request cap rsp\n", @@ -2881,8 +2889,7 @@ static void handle_query_cap_rsp(union ibmvnic_crq *crq, out: if (atomic_read(&adapter->running_cap_crqs) == 0) { adapter->wait_capability = false; - init_sub_crqs(adapter, 0); - /* We're done querying the capabilities, initialize sub-crqs */ + ibmvnic_send_req_caps(adapter, 0); } } -- cgit From 1bb3c739ad2c0030792cd527f30865accc50c460 Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Tue, 25 Apr 2017 15:01:10 -0400 Subject: ibmvnic: Move initialization of sub crqs to ibmvnic_init The sub crq structures are initialized in interrupt context while handling the response to crqs when negotiating capabilities for the driver. The sub crqs do not need to be initialized at this point and can be moved to being done from ibmvnic_init. Moving the init of the sub crqs to ibmvnic_init also allows use to allocate the memory with GFP_KERNEL instead of GFP_ATOMIC. Signed-off-by: Nathan Fontenot Signed-off-by: David S. Miller --- drivers/net/ethernet/ibm/ibmvnic.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index c2e260ce45bb..4fcd2f0378ba 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1390,12 +1390,12 @@ static struct ibmvnic_sub_crq_queue *init_sub_crq_queue(struct ibmvnic_adapter struct ibmvnic_sub_crq_queue *scrq; int rc; - scrq = kzalloc(sizeof(*scrq), GFP_ATOMIC); + scrq = kzalloc(sizeof(*scrq), GFP_KERNEL); if (!scrq) return NULL; scrq->msgs = - (union sub_crq *)__get_free_pages(GFP_ATOMIC | __GFP_ZERO, 2); + (union sub_crq *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 2); if (!scrq->msgs) { dev_warn(dev, "Couldn't allocate crq queue messages page\n"); goto zero_page_failed; @@ -1689,7 +1689,7 @@ static int init_sub_crqs(struct ibmvnic_adapter *adapter) total_queues = adapter->req_tx_queues + adapter->req_rx_queues; - allqueues = kcalloc(total_queues, sizeof(*allqueues), GFP_ATOMIC); + allqueues = kcalloc(total_queues, sizeof(*allqueues), GFP_KERNEL); if (!allqueues) return -1; @@ -1729,7 +1729,7 @@ static int init_sub_crqs(struct ibmvnic_adapter *adapter) } adapter->tx_scrq = kcalloc(adapter->req_tx_queues, - sizeof(*adapter->tx_scrq), GFP_ATOMIC); + sizeof(*adapter->tx_scrq), GFP_KERNEL); if (!adapter->tx_scrq) goto tx_failed; @@ -1739,7 +1739,7 @@ static int init_sub_crqs(struct ibmvnic_adapter *adapter) } adapter->rx_scrq = kcalloc(adapter->req_rx_queues, - sizeof(*adapter->rx_scrq), GFP_ATOMIC); + sizeof(*adapter->rx_scrq), GFP_KERNEL); if (!adapter->rx_scrq) goto rx_failed; @@ -1765,7 +1765,6 @@ static void ibmvnic_send_req_caps(struct ibmvnic_adapter *adapter, int retry) { struct device *dev = &adapter->vdev->dev; union ibmvnic_crq crq; - int rc; if (!retry) { /* Sub-CRQ entries are 32 byte long */ @@ -1794,10 +1793,6 @@ static void ibmvnic_send_req_caps(struct ibmvnic_adapter *adapter, int retry) adapter->req_mtu = adapter->netdev->mtu + ETH_HLEN; } - rc = init_sub_crqs(adapter); - if (rc) - return; - memset(&crq, 0, sizeof(crq)); crq.request_capability.first = IBMVNIC_CRQ_CMD; crq.request_capability.cmd = REQUEST_CAPABILITY; @@ -3317,7 +3312,13 @@ static int ibmvnic_init(struct ibmvnic_adapter *adapter) return -1; } - return 0; + rc = init_sub_crqs(adapter); + if (rc) { + dev_err(dev, "Initialization of sub crqs failed\n"); + release_crq_queue(adapter); + } + + return rc; } static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) -- cgit From 78a57b482aa53b040fbc2b2ef0208b54f004161b Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 25 Apr 2017 15:59:17 -0400 Subject: virtio-net: on tx, only call napi_disable if tx napi is on As of tx napi, device down (`ip link set dev $dev down`) hangs unless tx napi is enabled. Else napi_enable is not called, so napi_disable will spin on test_and_set_bit NAPI_STATE_SCHED. Only call napi_disable if tx napi is enabled. Fixes: 5a719c2552ca ("virtio-net: transmit napi") Reported-by: Jason Wang Signed-off-by: Willem de Bruijn Acked-by: Michael S. Tsirkin Acked-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 003143835766..82f1c3a73345 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -997,6 +997,12 @@ static void virtnet_napi_tx_enable(struct virtnet_info *vi, return virtnet_napi_enable(vq, napi); } +static void virtnet_napi_tx_disable(struct napi_struct *napi) +{ + if (napi->weight) + napi_disable(napi); +} + static void refill_work(struct work_struct *work) { struct virtnet_info *vi = @@ -1445,7 +1451,7 @@ static int virtnet_close(struct net_device *dev) for (i = 0; i < vi->max_queue_pairs; i++) { napi_disable(&vi->rq[i].napi); - napi_disable(&vi->sq[i].napi); + virtnet_napi_tx_disable(&vi->sq[i].napi); } return 0; @@ -1803,7 +1809,7 @@ static void virtnet_freeze_down(struct virtio_device *vdev) if (netif_running(vi->dev)) { for (i = 0; i < vi->max_queue_pairs; i++) { napi_disable(&vi->rq[i].napi); - napi_disable(&vi->sq[i].napi); + virtnet_napi_tx_disable(&vi->sq[i].napi); } } } -- cgit From ab81007a7b519d72f3c26d753a9fe1ffd27edc20 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 26 Apr 2017 07:43:41 +0200 Subject: cfg80211: simplify netlink socket owner interface deletion There's no need to allocate a portid structure and then, for each of those, walk the interfaces - we can just add a flag to each interface and walk those directly. Due to padding in the struct, we can even do it without any memory cost, and it even simplifies the code. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 6 ++++-- net/wireless/core.c | 26 ++++---------------------- net/wireless/core.h | 8 -------- net/wireless/nl80211.c | 22 +++++----------------- 4 files changed, 13 insertions(+), 49 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 2a200b964b7a..af958938b3b1 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3988,6 +3988,7 @@ struct cfg80211_cqm_config; * @event_list: (private) list for internal event processing * @event_lock: (private) lock for event list * @owner_nlportid: (private) owner socket port ID + * @nl_owner_dead: (private) owner socket went away * @cqm_config: (private) nl80211 RSSI monitor state */ struct wireless_dev { @@ -4037,12 +4038,13 @@ struct wireless_dev { u32 ap_unexpected_nlportid; + u32 owner_nlportid; + bool nl_owner_dead; + bool cac_started; unsigned long cac_start_time; unsigned int cac_time_ms; - u32 owner_nlportid; - #ifdef CONFIG_CFG80211_WEXT /* wext data */ struct { diff --git a/net/wireless/core.c b/net/wireless/core.c index b0d6761f0cdd..4ea28de3a636 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -305,30 +305,14 @@ static void cfg80211_event_work(struct work_struct *work) void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev) { - struct cfg80211_iface_destroy *item; + struct wireless_dev *wdev, *tmp; ASSERT_RTNL(); - spin_lock_irq(&rdev->destroy_list_lock); - while ((item = list_first_entry_or_null(&rdev->destroy_list, - struct cfg80211_iface_destroy, - list))) { - struct wireless_dev *wdev, *tmp; - u32 nlportid = item->nlportid; - - list_del(&item->list); - kfree(item); - spin_unlock_irq(&rdev->destroy_list_lock); - - list_for_each_entry_safe(wdev, tmp, - &rdev->wiphy.wdev_list, list) { - if (nlportid == wdev->owner_nlportid) - rdev_del_virtual_intf(rdev, wdev); - } - - spin_lock_irq(&rdev->destroy_list_lock); + list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) { + if (wdev->nl_owner_dead) + rdev_del_virtual_intf(rdev, wdev); } - spin_unlock_irq(&rdev->destroy_list_lock); } static void cfg80211_destroy_iface_wk(struct work_struct *work) @@ -484,8 +468,6 @@ use_default_name: 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); INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); INIT_WORK(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk); INIT_WORK(&rdev->propagate_radar_detect_wk, diff --git a/net/wireless/core.h b/net/wireless/core.h index 5d27eca57d3b..f9b748e3425a 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -91,10 +91,7 @@ struct cfg80211_registered_device { struct cfg80211_coalesce *coalesce; - spinlock_t destroy_list_lock; - struct list_head destroy_list; struct work_struct destroy_work; - struct work_struct sched_scan_stop_wk; struct cfg80211_chan_def radar_chandef; @@ -264,11 +261,6 @@ struct cfg80211_beacon_registration { u32 nlportid; }; -struct cfg80211_iface_destroy { - struct list_head list; - u32 nlportid; -}; - struct cfg80211_cqm_config { u32 rssi_hyst; s32 last_rssi_event_value; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 50c35affccad..45f5f418e562 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14883,7 +14883,6 @@ static int nl80211_netlink_notify(struct notifier_block * nb, rcu_read_lock(); list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { - bool schedule_destroy_work = false; struct cfg80211_sched_scan_request *sched_scan_req = rcu_dereference(rdev->sched_scan_req); @@ -14899,10 +14898,12 @@ static int nl80211_netlink_notify(struct notifier_block * nb, list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list) { cfg80211_mlme_unregister_socket(wdev, notify->portid); - if (wdev->owner_nlportid == notify->portid) - schedule_destroy_work = true; - else if (wdev->conn_owner_nlportid == notify->portid) + if (wdev->owner_nlportid == notify->portid) { + wdev->nl_owner_dead = true; + schedule_work(&rdev->destroy_work); + } else if (wdev->conn_owner_nlportid == notify->portid) { schedule_work(&wdev->disconnect_wk); + } } spin_lock_bh(&rdev->beacon_registrations_lock); @@ -14915,19 +14916,6 @@ static int nl80211_netlink_notify(struct notifier_block * nb, } } spin_unlock_bh(&rdev->beacon_registrations_lock); - - if (schedule_destroy_work) { - struct cfg80211_iface_destroy *destroy; - - destroy = kzalloc(sizeof(*destroy), GFP_ATOMIC); - if (destroy) { - destroy->nlportid = notify->portid; - spin_lock(&rdev->destroy_list_lock); - list_add(&destroy->list, &rdev->destroy_list); - spin_unlock(&rdev->destroy_list_lock); - schedule_work(&rdev->destroy_work); - } - } } rcu_read_unlock(); -- cgit From ca986ad9bcd3893c8b0b4cc2cafcc8cf1554409c Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Fri, 21 Apr 2017 13:05:00 +0100 Subject: nl80211: allow multiple active scheduled scan requests This patch implements the idea to have multiple scheduled scan requests running concurrently. It mainly illustrates how to deal with the incoming request from user-space in terms of backward compatibility. In order to use multiple scheduled scans user-space needs to provide a flag attribute NL80211_ATTR_SCHED_SCAN_MULTI to indicate support. If not the request is treated as a legacy scan. Drivers currently supporting scheduled scan are now indicating they support a single scheduled scan request. This obsoletes WIPHY_FLAG_SUPPORTS_SCHED_SCAN. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel [clean up netlink destroy path to avoid allocations, code cleanups] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 2 +- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 2 +- drivers/net/wireless/ti/wlcore/main.c | 2 +- include/net/cfg80211.h | 9 +- include/uapi/linux/nl80211.h | 12 ++- net/wireless/core.c | 29 +++--- net/wireless/core.h | 11 +- net/wireless/nl80211.c | 63 ++++++++--- net/wireless/rdev-ops.h | 2 +- net/wireless/scan.c | 115 +++++++++++++++++---- net/wireless/trace.h | 18 ++-- 13 files changed, 205 insertions(+), 64 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 0c118b7c362c..1906412afa70 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3973,7 +3973,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, ar->fw_capabilities)) - ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + ar->wiphy->max_sched_scan_reqs = 1; if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, ar->fw_capabilities)) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 8c7f1ef288c6..c71173dc9965 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -6374,11 +6374,11 @@ err: static void brcmf_wiphy_pno_params(struct wiphy *wiphy) { /* scheduled scan settings */ + wiphy->max_sched_scan_reqs = 1; wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; wiphy->max_sched_scan_plan_interval = BRCMF_PNO_SCHED_SCAN_MAX_PERIOD; - wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; } #ifdef CONFIG_PM diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 5cdd95775ba6..8c58d47100a0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -620,7 +620,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) else hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + hw->wiphy->max_sched_scan_reqs = 1; hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES; /* we create the 802.11 header and zero length SSID IE. */ diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 49b4c805b7d5..9927bd5aac56 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -4297,7 +4297,6 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | WIPHY_FLAG_AP_UAPSD | - WIPHY_FLAG_SUPPORTS_SCHED_SCAN | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_HAS_CHANNEL_SWITCH | WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -4316,6 +4315,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; + wiphy->max_sched_scan_reqs = 1; wiphy->max_sched_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH; wiphy->max_sched_scan_ie_len = MWIFIEX_MAX_VSIE_LEN; wiphy->max_match_sets = MWIFIEX_MAX_SSID_LIST_LENGTH; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index a21fda910529..382ec15ec1af 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -6128,6 +6128,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - sizeof(struct ieee80211_header); + wl->hw->wiphy->max_sched_scan_reqs = 1; wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - sizeof(struct ieee80211_header); @@ -6135,7 +6136,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_SUPPORTS_SCHED_SCAN | WIPHY_FLAG_HAS_CHANNEL_SWITCH; wl->hw->wiphy->features |= NL80211_FEATURE_AP_SCAN; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index af958938b3b1..43c0f389c273 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1678,6 +1678,8 @@ struct cfg80211_bss_select_adjust { * @rcu_head: RCU callback used to free the struct * @owner_nlportid: netlink portid of owner (if this should is a request * owned by a particular socket) + * @nl_owner_dead: netlink owner socket was closed - this request be freed + * @list: for keeping list of requests. * @delay: delay in seconds to use before starting the first scan * cycle. The driver may ignore this parameter and start * immediately (or at any other time), if this feature is not @@ -1722,6 +1724,8 @@ struct cfg80211_sched_scan_request { unsigned long scan_start; struct rcu_head rcu_head; u32 owner_nlportid; + bool nl_owner_dead; + struct list_head list; /* keep last */ struct ieee80211_channel *channels[0]; @@ -3213,7 +3217,7 @@ enum wiphy_flags { WIPHY_FLAG_CONTROL_PORT_PROTOCOL = BIT(7), WIPHY_FLAG_IBSS_RSN = BIT(8), WIPHY_FLAG_MESH_AUTH = BIT(10), - WIPHY_FLAG_SUPPORTS_SCHED_SCAN = BIT(11), + /* use hole at 11 */ /* use hole at 12 */ WIPHY_FLAG_SUPPORTS_FW_ROAM = BIT(13), WIPHY_FLAG_AP_UAPSD = BIT(14), @@ -3551,6 +3555,8 @@ struct wiphy_iftype_ext_capab { * this variable determines its size * @max_scan_ssids: maximum number of SSIDs the device can scan for in * any given scan + * @max_sched_scan_reqs: maximum number of scheduled scan requests that + * the device can run concurrently. * @max_sched_scan_ssids: maximum number of SSIDs the device can scan * for in any given scheduled scan * @max_match_sets: maximum number of match sets the device can handle @@ -3687,6 +3693,7 @@ struct wiphy { int bss_priv_size; u8 max_scan_ssids; + u8 max_sched_scan_reqs; u8 max_sched_scan_ssids; u8 max_match_sets; u16 max_scan_ie_len; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 6095a6c4c412..f34127d241e5 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -387,7 +387,9 @@ * are used. Extra IEs can also be passed from the userspace by * using the %NL80211_ATTR_IE attribute. The first cycle of the * scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY - * is supplied. + * is supplied. If the device supports multiple concurrent scheduled + * scans, it will allow such when the caller provides the flag attribute + * %NL80211_ATTR_SCHED_SCAN_MULTI to indicate user-space support for it. * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if * scheduled scan is not running. The caller may assume that as soon * as the call returns, it is safe to start a new scheduled scan again. @@ -2081,6 +2083,11 @@ enum nl80211_commands { * @NL80211_ATTR_PMK: PMK for the PMKSA identified by %NL80211_ATTR_PMKID. * This is used with @NL80211_CMD_SET_PMKSA. * + * @NL80211_ATTR_SCHED_SCAN_MULTI: flag attribute which user-space shall use to + * indicate that it supports multiple active scheduled scan requests. + * @NL80211_ATTR_SCHED_SCAN_MAX_REQS: indicates maximum number of scheduled + * scan request that may be active for the device (u32). + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2500,6 +2507,9 @@ enum nl80211_attrs { NL80211_ATTR_PMK, + NL80211_ATTR_SCHED_SCAN_MULTI, + NL80211_ATTR_SCHED_SCAN_MAX_REQS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/core.c b/net/wireless/core.c index 4ea28de3a636..a3c0c48afb85 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -330,14 +330,16 @@ static void cfg80211_destroy_iface_wk(struct work_struct *work) static void cfg80211_sched_scan_stop_wk(struct work_struct *work) { struct cfg80211_registered_device *rdev; + struct cfg80211_sched_scan_request *req, *tmp; rdev = container_of(work, struct cfg80211_registered_device, sched_scan_stop_wk); rtnl_lock(); - - __cfg80211_stop_sched_scan(rdev, false); - + list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) { + if (req->nl_owner_dead) + cfg80211_stop_sched_scan_req(rdev, req, false); + } rtnl_unlock(); } @@ -452,6 +454,7 @@ use_default_name: spin_lock_init(&rdev->beacon_registrations_lock); spin_lock_init(&rdev->bss_lock); INIT_LIST_HEAD(&rdev->bss_list); + INIT_LIST_HEAD(&rdev->sched_scan_req_list); INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); INIT_LIST_HEAD(&rdev->mlme_unreg); @@ -1028,7 +1031,7 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct net_device *dev = wdev->netdev; - struct cfg80211_sched_scan_request *sched_scan_req; + struct cfg80211_sched_scan_request *pos, *tmp; ASSERT_RTNL(); ASSERT_WDEV_LOCK(wdev); @@ -1039,9 +1042,11 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: - sched_scan_req = rtnl_dereference(rdev->sched_scan_req); - if (sched_scan_req && dev == sched_scan_req->dev) - __cfg80211_stop_sched_scan(rdev, false); + list_for_each_entry_safe(pos, tmp, &rdev->sched_scan_req_list, + list) { + if (dev == pos->dev) + cfg80211_stop_sched_scan_req(rdev, pos, false); + } #ifdef CONFIG_CFG80211_WEXT kfree(wdev->wext.ie); @@ -1116,7 +1121,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev; - struct cfg80211_sched_scan_request *sched_scan_req; + struct cfg80211_sched_scan_request *pos, *tmp; if (!wdev) return NOTIFY_DONE; @@ -1193,10 +1198,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, ___cfg80211_scan_done(rdev, false); } - sched_scan_req = rtnl_dereference(rdev->sched_scan_req); - if (WARN_ON(sched_scan_req && - sched_scan_req->dev == wdev->netdev)) { - __cfg80211_stop_sched_scan(rdev, false); + list_for_each_entry_safe(pos, tmp, + &rdev->sched_scan_req_list, list) { + if (WARN_ON(pos && pos->dev == wdev->netdev)) + cfg80211_stop_sched_scan_req(rdev, pos, false); } rdev->opencount--; diff --git a/net/wireless/core.h b/net/wireless/core.h index f9b748e3425a..06eaf96053a8 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -74,7 +74,7 @@ struct cfg80211_registered_device { u32 bss_entries; struct cfg80211_scan_request *scan_req; /* protected by RTNL */ struct sk_buff *scan_msg; - struct cfg80211_sched_scan_request __rcu *sched_scan_req; + struct list_head sched_scan_req_list; unsigned long suspend_at; struct work_struct scan_done_wk; struct work_struct sched_scan_results_wk; @@ -416,9 +416,16 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, void __cfg80211_scan_done(struct work_struct *wk); void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool send_message); +void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev, + struct cfg80211_sched_scan_request *req); +int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev, + bool want_multi); void __cfg80211_sched_scan_results(struct work_struct *wk); +int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev, + struct cfg80211_sched_scan_request *req, + bool driver_initiated); int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, - bool driver_initiated); + u64 reqid, bool driver_initiated); void cfg80211_upload_connect_keys(struct wireless_dev *wdev); int cfg80211_change_iface(struct cfg80211_registered_device *rdev, struct net_device *dev, enum nl80211_iftype ntype, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 45f5f418e562..ac7e2314f9ec 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -419,6 +419,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { .len = FILS_ERP_MAX_RRK_LEN }, [NL80211_ATTR_FILS_CACHE_ID] = { .len = 2 }, [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN }, + [NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -1376,7 +1377,7 @@ static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev, CMD(tdls_mgmt, TDLS_MGMT); CMD(tdls_oper, TDLS_OPER); } - if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) + if (rdev->wiphy.max_sched_scan_reqs) CMD(sched_scan_start, START_SCHED_SCAN); CMD(probe_client, PROBE_CLIENT); CMD(set_noack_map, SET_NOACK_MAP); @@ -1815,6 +1816,11 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, nla_put_flag(msg, NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) goto nla_put_failure; + if (rdev->wiphy.max_sched_scan_reqs && + nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_MAX_REQS, + rdev->wiphy.max_sched_scan_reqs)) + goto nla_put_failure; + if (nla_put(msg, NL80211_ATTR_EXT_FEATURES, sizeof(rdev->wiphy.ext_features), rdev->wiphy.ext_features)) @@ -7336,14 +7342,16 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_sched_scan_request *sched_scan_req; + bool want_multi; int err; - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || - !rdev->ops->sched_scan_start) + if (!rdev->wiphy.max_sched_scan_reqs || !rdev->ops->sched_scan_start) return -EOPNOTSUPP; - if (rdev->sched_scan_req) - return -EINPROGRESS; + want_multi = info->attrs[NL80211_ATTR_SCHED_SCAN_MULTI]; + err = cfg80211_sched_scan_req_possible(rdev, want_multi); + if (err) + return err; sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev, info->attrs, @@ -7353,6 +7361,14 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, if (err) goto out_err; + /* leave request id zero for legacy request + * or if driver does not support multi-scheduled scan + */ + if (want_multi && rdev->wiphy.max_sched_scan_reqs > 1) { + while (!sched_scan_req->reqid) + sched_scan_req->reqid = rdev->wiphy.cookie_counter++; + } + err = rdev_sched_scan_start(rdev, dev, sched_scan_req); if (err) goto out_free; @@ -7363,7 +7379,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) sched_scan_req->owner_nlportid = info->snd_portid; - rcu_assign_pointer(rdev->sched_scan_req, sched_scan_req); + cfg80211_add_sched_scan_req(rdev, sched_scan_req); nl80211_send_sched_scan(sched_scan_req, NL80211_CMD_START_SCHED_SCAN); return 0; @@ -7377,13 +7393,27 @@ out_err: static int nl80211_stop_sched_scan(struct sk_buff *skb, struct genl_info *info) { + struct cfg80211_sched_scan_request *req; struct cfg80211_registered_device *rdev = info->user_ptr[0]; + u64 cookie; - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || - !rdev->ops->sched_scan_stop) + if (!rdev->wiphy.max_sched_scan_reqs || !rdev->ops->sched_scan_stop) return -EOPNOTSUPP; - return __cfg80211_stop_sched_scan(rdev, false); + if (info->attrs[NL80211_ATTR_COOKIE]) { + cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); + return __cfg80211_stop_sched_scan(rdev, cookie, false); + } + + req = list_first_or_null_rcu(&rdev->sched_scan_req_list, + struct cfg80211_sched_scan_request, + list); + if (!req || req->reqid || + (req->owner_nlportid && + req->owner_nlportid != info->snd_portid)) + return -ENOENT; + + return cfg80211_stop_sched_scan_req(rdev, req, false); } static int nl80211_start_radar_detection(struct sk_buff *skb, @@ -14883,16 +14913,15 @@ static int nl80211_netlink_notify(struct notifier_block * nb, rcu_read_lock(); list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { - struct cfg80211_sched_scan_request *sched_scan_req = - rcu_dereference(rdev->sched_scan_req); - - if (sched_scan_req && notify->portid && - sched_scan_req->owner_nlportid == notify->portid) { - sched_scan_req->owner_nlportid = 0; + struct cfg80211_sched_scan_request *sched_scan_req; - if (rdev->ops->sched_scan_stop && - rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) + list_for_each_entry_rcu(sched_scan_req, + &rdev->sched_scan_req_list, + list) { + if (sched_scan_req->owner_nlportid == notify->portid) { + sched_scan_req->nl_owner_dead = true; schedule_work(&rdev->sched_scan_stop_wk); + } } list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list) { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index e4a99989dd06..783f89c3e504 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -813,7 +813,7 @@ rdev_sched_scan_start(struct cfg80211_registered_device *rdev, struct cfg80211_sched_scan_request *request) { int ret; - trace_rdev_sched_scan_start(&rdev->wiphy, dev, request); + trace_rdev_sched_scan_start(&rdev->wiphy, dev, request->reqid); ret = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); trace_rdev_return_int(&rdev->wiphy, ret); return ret; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 6f4996c0f4df..bd9feed95c1e 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -300,6 +300,70 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, } EXPORT_SYMBOL(cfg80211_scan_done); +void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev, + struct cfg80211_sched_scan_request *req) +{ + ASSERT_RTNL(); + + list_add_rcu(&req->list, &rdev->sched_scan_req_list); +} + +static void cfg80211_del_sched_scan_req(struct cfg80211_registered_device *rdev, + struct cfg80211_sched_scan_request *req) +{ + ASSERT_RTNL(); + + list_del_rcu(&req->list); + kfree_rcu(req, rcu_head); +} + +static struct cfg80211_sched_scan_request * +cfg80211_find_sched_scan_req(struct cfg80211_registered_device *rdev, u64 reqid) +{ + struct cfg80211_sched_scan_request *pos; + + ASSERT_RTNL(); + + list_for_each_entry(pos, &rdev->sched_scan_req_list, list) { + if (pos->reqid == reqid) + return pos; + } + return ERR_PTR(-ENOENT); +} + +/* + * Determines if a scheduled scan request can be handled. When a legacy + * scheduled scan is running no other scheduled scan is allowed regardless + * whether the request is for legacy or multi-support scan. When a multi-support + * scheduled scan is running a request for legacy scan is not allowed. In this + * case a request for multi-support scan can be handled if resources are + * available, ie. struct wiphy::max_sched_scan_reqs limit is not yet reached. + */ +int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev, + bool want_multi) +{ + struct cfg80211_sched_scan_request *pos; + int i = 0; + + list_for_each_entry(pos, &rdev->sched_scan_req_list, list) { + /* request id zero means legacy in progress */ + if (!i && !pos->reqid) + return -EINPROGRESS; + i++; + } + + if (i) { + /* no legacy allowed when multi request(s) are active */ + if (!want_multi) + return -EINPROGRESS; + + /* resource limit reached */ + if (i == rdev->wiphy.max_sched_scan_reqs) + return -ENOSPC; + } + return 0; +} + void __cfg80211_sched_scan_results(struct work_struct *wk) { struct cfg80211_registered_device *rdev; @@ -310,10 +374,10 @@ void __cfg80211_sched_scan_results(struct work_struct *wk) rtnl_lock(); - request = rtnl_dereference(rdev->sched_scan_req); + request = cfg80211_find_sched_scan_req(rdev, 0); /* we don't have sched_scan_req anymore if the scan is stopping */ - if (request) { + if (!IS_ERR(request)) { if (request->flags & NL80211_SCAN_FLAG_FLUSH) { /* flush entries from previous scans */ spin_lock_bh(&rdev->bss_lock); @@ -329,10 +393,17 @@ void __cfg80211_sched_scan_results(struct work_struct *wk) void cfg80211_sched_scan_results(struct wiphy *wiphy) { + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + struct cfg80211_sched_scan_request *request; + trace_cfg80211_sched_scan_results(wiphy); /* ignore if we're not scanning */ - if (rcu_access_pointer(wiphy_to_rdev(wiphy)->sched_scan_req)) + rtnl_lock(); + request = cfg80211_find_sched_scan_req(rdev, 0); + rtnl_unlock(); + + if (!IS_ERR(request)) queue_work(cfg80211_wq, &wiphy_to_rdev(wiphy)->sched_scan_results_wk); } @@ -346,7 +417,7 @@ void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy) trace_cfg80211_sched_scan_stopped(wiphy); - __cfg80211_stop_sched_scan(rdev, true); + __cfg80211_stop_sched_scan(rdev, 0, true); } EXPORT_SYMBOL(cfg80211_sched_scan_stopped_rtnl); @@ -358,34 +429,40 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy) } EXPORT_SYMBOL(cfg80211_sched_scan_stopped); -int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, - bool driver_initiated) +int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev, + struct cfg80211_sched_scan_request *req, + bool driver_initiated) { - struct cfg80211_sched_scan_request *sched_scan_req; - struct net_device *dev; - ASSERT_RTNL(); - if (!rdev->sched_scan_req) - return -ENOENT; - - sched_scan_req = rtnl_dereference(rdev->sched_scan_req); - dev = sched_scan_req->dev; - if (!driver_initiated) { - int err = rdev_sched_scan_stop(rdev, dev); + int err = rdev_sched_scan_stop(rdev, req->dev); if (err) return err; } - nl80211_send_sched_scan(sched_scan_req, NL80211_CMD_SCHED_SCAN_STOPPED); + nl80211_send_sched_scan(req, NL80211_CMD_SCHED_SCAN_STOPPED); - RCU_INIT_POINTER(rdev->sched_scan_req, NULL); - kfree_rcu(sched_scan_req, rcu_head); + cfg80211_del_sched_scan_req(rdev, req); return 0; } +int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, + u64 reqid, bool driver_initiated) +{ + struct cfg80211_sched_scan_request *sched_scan_req; + + ASSERT_RTNL(); + + sched_scan_req = cfg80211_find_sched_scan_req(rdev, reqid); + if (IS_ERR(sched_scan_req)) + return PTR_ERR(sched_scan_req); + + return cfg80211_stop_sched_scan_req(rdev, sched_scan_req, + driver_initiated); +} + void cfg80211_bss_age(struct cfg80211_registered_device *rdev, unsigned long age_secs) { diff --git a/net/wireless/trace.h b/net/wireless/trace.h index fd55786f0462..52935c48b342 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1610,20 +1610,26 @@ DEFINE_EVENT(tx_rx_evt, rdev_set_antenna, TP_ARGS(wiphy, rx, tx) ); -TRACE_EVENT(rdev_sched_scan_start, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, - struct cfg80211_sched_scan_request *request), - TP_ARGS(wiphy, netdev, request), +DECLARE_EVENT_CLASS(wiphy_netdev_id_evt, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u64 id), + TP_ARGS(wiphy, netdev, id), TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY + __field(u64, id) ), TP_fast_assign( WIPHY_ASSIGN; NETDEV_ASSIGN; + __entry->id = id; ), - TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, - WIPHY_PR_ARG, NETDEV_PR_ARG) + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", id: %llu", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->id) +); + +DEFINE_EVENT(wiphy_netdev_id_evt, rdev_sched_scan_start, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u64 id), + TP_ARGS(wiphy, netdev, id) ); TRACE_EVENT(rdev_tdls_mgmt, -- cgit From 3007e3529ce1efd9c370a7b81633e45f730ae35b Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Fri, 21 Apr 2017 13:05:01 +0100 Subject: nl80211: add support for BSSIDs in scheduled scan matchsets This patch allows for the scheduled scan request to specify matchsets for specific BSSIDs. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel [docs, netlink policy fix] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 6 +++++- include/uapi/linux/nl80211.h | 4 ++++ net/wireless/nl80211.c | 40 ++++++++++++++++++++++++++++++---------- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 43c0f389c273..4058518e267a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1613,11 +1613,15 @@ static inline void get_random_mask_addr(u8 *buf, const u8 *addr, const u8 *mask) /** * struct cfg80211_match_set - sets of attributes to match * - * @ssid: SSID to be matched; may be zero-length for no match (RSSI only) + * @ssid: SSID to be matched; may be zero-length in case of BSSID match + * or no match (RSSI only) + * @bssid: BSSID to be matched; may be all-zero BSSID in case of SSID match + * or no match (RSSI only) * @rssi_thold: don't report scan results below this threshold (in s32 dBm) */ struct cfg80211_match_set { struct cfg80211_ssid ssid; + u8 bssid[ETH_ALEN]; s32 rssi_thold; }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index f34127d241e5..b8c44b98f12d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3194,6 +3194,7 @@ enum nl80211_reg_rule_attr { * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, * only report BSS with matching SSID. + * (This cannot be used together with BSSID.) * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a * BSS in scan results. Filtering is turned off if not specified. Note that * if this attribute is in a match set of its own, then it is treated as @@ -3209,6 +3210,8 @@ enum nl80211_reg_rule_attr { * BSS-es in the specified band is to be adjusted before doing * RSSI-based BSS selection. The attribute value is a packed structure * value as specified by &struct nl80211_bss_select_rssi_adjust. + * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching + * (this cannot be used together with SSID). * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * attribute number currently defined * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use @@ -3220,6 +3223,7 @@ enum nl80211_sched_scan_match_attr { NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST, + NL80211_SCHED_SCAN_MATCH_ATTR_BSSID, /* keep last */ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ac7e2314f9ec..dce69a87d4d0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -497,6 +497,7 @@ static const struct nla_policy nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = { [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY, .len = IEEE80211_MAX_SSID_LEN }, + [NL80211_SCHED_SCAN_MATCH_ATTR_BSSID] = { .len = ETH_ALEN }, [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 }, }; @@ -7036,8 +7037,15 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, NULL); if (err) return ERR_PTR(err); + + /* SSID and BSSID are mutually exclusive */ + if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID] && + tb[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID]) + return ERR_PTR(-EINVAL); + /* add other standalone attributes here */ - if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) { + if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID] || + tb[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID]) { n_match_sets++; continue; } @@ -7208,7 +7216,7 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, nla_for_each_nested(attr, attrs[NL80211_ATTR_SCHED_SCAN_MATCH], tmp) { - struct nlattr *ssid, *rssi; + struct nlattr *ssid, *bssid, *rssi; err = nla_parse_nested(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, @@ -7217,7 +7225,8 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, if (err) goto out_free; ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]; - if (ssid) { + bssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID]; + if (ssid || bssid) { if (WARN_ON(i >= n_match_sets)) { /* this indicates a programming error, * the loop above should have verified @@ -7227,14 +7236,25 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, goto out_free; } - if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) { - err = -EINVAL; - goto out_free; + if (ssid) { + if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) { + err = -EINVAL; + goto out_free; + } + memcpy(request->match_sets[i].ssid.ssid, + nla_data(ssid), nla_len(ssid)); + request->match_sets[i].ssid.ssid_len = + nla_len(ssid); + } + if (bssid) { + if (nla_len(bssid) != ETH_ALEN) { + err = -EINVAL; + goto out_free; + } + memcpy(request->match_sets[i].bssid, + nla_data(bssid), ETH_ALEN); } - memcpy(request->match_sets[i].ssid.ssid, - nla_data(ssid), nla_len(ssid)); - request->match_sets[i].ssid.ssid_len = - nla_len(ssid); + /* special attribute - old implementation w/a */ request->match_sets[i].rssi_thold = default_match_rssi; -- cgit From 3a3ecf1d5971b1f272124b445ef2d6b6ad3074fd Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Fri, 21 Apr 2017 13:05:02 +0100 Subject: cfg80211: add request id parameter to .sched_scan_stop() signature For multiple scheduled scan support the driver needs to know which scheduled scan request is being stopped. Pass the request id in the .sched_scan_stop() callback. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- .../net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 6 +++--- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 2 +- include/net/cfg80211.h | 15 ++++++++------- net/mac80211/cfg.c | 3 ++- net/wireless/rdev-ops.h | 6 +++--- net/wireless/scan.c | 2 +- net/wireless/trace.h | 10 +++++----- 8 files changed, 24 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 1906412afa70..fd53ffb1f014 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3352,7 +3352,7 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, } static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy, - struct net_device *dev) + struct net_device *dev, u64 reqid) { struct ath6kl_vif *vif = netdev_priv(dev); bool stopped; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index c71173dc9965..130f0d4eda06 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3405,7 +3405,7 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, } static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, - struct net_device *ndev) + struct net_device *ndev, u64 reqid) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); @@ -3607,7 +3607,7 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) cfg->wowl.pre_pmmode); cfg->wowl.active = false; if (cfg->wowl.nd_enabled) { - brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev); + brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev, 0); brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND); brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, brcmf_notify_sched_scan_results); @@ -3691,7 +3691,7 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, /* Stop scheduled scan */ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) - brcmf_cfg80211_sched_scan_stop(wiphy, ndev); + brcmf_cfg80211_sched_scan_stop(wiphy, ndev, 0); /* end any scanning */ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 9927bd5aac56..0406a98f86f0 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -2720,7 +2720,7 @@ mwifiex_cfg80211_sched_scan_start(struct wiphy *wiphy, * previous bgscan configuration in the firmware */ static int mwifiex_cfg80211_sched_scan_stop(struct wiphy *wiphy, - struct net_device *dev) + struct net_device *dev, u64 reqid) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4058518e267a..aa663f70969c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2773,12 +2773,12 @@ struct cfg80211_nan_func { * @set_cqm_txe_config: Configure connection quality monitor TX error * thresholds. * @sched_scan_start: Tell the driver to start a scheduled scan. - * @sched_scan_stop: Tell the driver to stop an ongoing scheduled scan. This - * call must stop the scheduled scan and be ready for starting a new one - * before it returns, i.e. @sched_scan_start may be called immediately - * after that again and should not fail in that case. The driver should - * not call cfg80211_sched_scan_stopped() for a requested stop (when this - * method returns 0.) + * @sched_scan_stop: Tell the driver to stop an ongoing scheduled scan with + * given request id. This call must stop the scheduled scan and be ready + * for starting a new one before it returns, i.e. @sched_scan_start may be + * called immediately after that again and should not fail in that case. + * The driver should not call cfg80211_sched_scan_stopped() for a requested + * stop (when this method returns 0). * * @mgmt_frame_register: Notify driver that a management frame type was * registered. The callback is allowed to sleep. @@ -3076,7 +3076,8 @@ struct cfg80211_ops { int (*sched_scan_start)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_sched_scan_request *request); - int (*sched_scan_stop)(struct wiphy *wiphy, struct net_device *dev); + int (*sched_scan_stop)(struct wiphy *wiphy, struct net_device *dev, + u64 reqid); int (*set_rekey_data)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_gtk_rekey_data *data); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d041f78ecee6..038c31336167 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2242,7 +2242,8 @@ ieee80211_sched_scan_start(struct wiphy *wiphy, } static int -ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) +ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev, + u64 reqid) { struct ieee80211_local *local = wiphy_priv(wiphy); diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 783f89c3e504..0598c1e5d0ad 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -820,11 +820,11 @@ rdev_sched_scan_start(struct cfg80211_registered_device *rdev, } static inline int rdev_sched_scan_stop(struct cfg80211_registered_device *rdev, - struct net_device *dev) + struct net_device *dev, u64 reqid) { int ret; - trace_rdev_sched_scan_stop(&rdev->wiphy, dev); - ret = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); + trace_rdev_sched_scan_stop(&rdev->wiphy, dev, reqid); + ret = rdev->ops->sched_scan_stop(&rdev->wiphy, dev, reqid); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } diff --git a/net/wireless/scan.c b/net/wireless/scan.c index bd9feed95c1e..8d86f704bcf3 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -436,7 +436,7 @@ int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev, ASSERT_RTNL(); if (!driver_initiated) { - int err = rdev_sched_scan_stop(rdev, req->dev); + int err = rdev_sched_scan_stop(rdev, req->dev, req->reqid); if (err) return err; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 52935c48b342..8d8f6b462b18 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -576,11 +576,6 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_stop_ap, TP_ARGS(wiphy, netdev) ); -DEFINE_EVENT(wiphy_netdev_evt, rdev_sched_scan_stop, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), - TP_ARGS(wiphy, netdev) -); - DEFINE_EVENT(wiphy_netdev_evt, rdev_set_rekey_data, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), TP_ARGS(wiphy, netdev) @@ -1632,6 +1627,11 @@ DEFINE_EVENT(wiphy_netdev_id_evt, rdev_sched_scan_start, TP_ARGS(wiphy, netdev, id) ); +DEFINE_EVENT(wiphy_netdev_id_evt, rdev_sched_scan_stop, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u64 id), + TP_ARGS(wiphy, netdev, id) +); + TRACE_EVENT(rdev_tdls_mgmt, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *peer, u8 action_code, u8 dialog_token, -- cgit From 1cbf41dbacb6c8decdf8d838bbf5ca5b448a269f Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 26 Apr 2017 10:58:46 +0300 Subject: ieee80211: add SUITE_B AKM selectors Add the definitions for SUITE_B and SUITE_B_192 AKM selectors as defined in IEEE802.11REVmc_D5.0, table 9-132. Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 294fa6273a62..23e095fa6701 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2356,18 +2356,20 @@ enum ieee80211_sa_query_action { #define WLAN_CIPHER_SUITE_SMS4 SUITE(0x001472, 1) /* AKM suite selectors */ -#define WLAN_AKM_SUITE_8021X SUITE(0x000FAC, 1) -#define WLAN_AKM_SUITE_PSK SUITE(0x000FAC, 2) -#define WLAN_AKM_SUITE_FT_PSK SUITE(0x000FAC, 4) -#define WLAN_AKM_SUITE_8021X_SHA256 SUITE(0x000FAC, 5) -#define WLAN_AKM_SUITE_PSK_SHA256 SUITE(0x000FAC, 6) -#define WLAN_AKM_SUITE_TDLS SUITE(0x000FAC, 7) -#define WLAN_AKM_SUITE_SAE SUITE(0x000FAC, 8) -#define WLAN_AKM_SUITE_FT_OVER_SAE SUITE(0x000FAC, 9) -#define WLAN_AKM_SUITE_FILS_SHA256 SUITE(0x000FAC, 14) -#define WLAN_AKM_SUITE_FILS_SHA384 SUITE(0x000FAC, 15) -#define WLAN_AKM_SUITE_FT_FILS_SHA256 SUITE(0x000FAC, 16) -#define WLAN_AKM_SUITE_FT_FILS_SHA384 SUITE(0x000FAC, 17) +#define WLAN_AKM_SUITE_8021X SUITE(0x000FAC, 1) +#define WLAN_AKM_SUITE_PSK SUITE(0x000FAC, 2) +#define WLAN_AKM_SUITE_FT_PSK SUITE(0x000FAC, 4) +#define WLAN_AKM_SUITE_8021X_SHA256 SUITE(0x000FAC, 5) +#define WLAN_AKM_SUITE_PSK_SHA256 SUITE(0x000FAC, 6) +#define WLAN_AKM_SUITE_TDLS SUITE(0x000FAC, 7) +#define WLAN_AKM_SUITE_SAE SUITE(0x000FAC, 8) +#define WLAN_AKM_SUITE_FT_OVER_SAE SUITE(0x000FAC, 9) +#define WLAN_AKM_SUITE_8021X_SUITE_B SUITE(0x000FAC, 11) +#define WLAN_AKM_SUITE_8021X_SUITE_B_192 SUITE(0x000FAC, 12) +#define WLAN_AKM_SUITE_FILS_SHA256 SUITE(0x000FAC, 14) +#define WLAN_AKM_SUITE_FILS_SHA384 SUITE(0x000FAC, 15) +#define WLAN_AKM_SUITE_FT_FILS_SHA256 SUITE(0x000FAC, 16) +#define WLAN_AKM_SUITE_FT_FILS_SHA384 SUITE(0x000FAC, 17) #define WLAN_MAX_KEY_LEN 32 -- cgit From 2ead3235fd7128347a60a3942b3e2048834d62aa Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 26 Apr 2017 10:58:48 +0300 Subject: ieee80211: add FT-802.1X AKM suite selector Add the definition for FT-8021.1X AKM selector as defined in IEEE Std 802.11-2016, table 9-133. Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 23e095fa6701..52abfbcd5975 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2358,6 +2358,7 @@ enum ieee80211_sa_query_action { /* AKM suite selectors */ #define WLAN_AKM_SUITE_8021X SUITE(0x000FAC, 1) #define WLAN_AKM_SUITE_PSK SUITE(0x000FAC, 2) +#define WLAN_AKM_SUITE_FT_8021X SUITE(0x000FAC, 3) #define WLAN_AKM_SUITE_FT_PSK SUITE(0x000FAC, 4) #define WLAN_AKM_SUITE_8021X_SHA256 SUITE(0x000FAC, 5) #define WLAN_AKM_SUITE_PSK_SHA256 SUITE(0x000FAC, 6) -- cgit From f6601e176c8b01bc545959c091778343a8c66951 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 26 Apr 2017 10:58:52 +0300 Subject: ieee80211: fix kernel-doc parsing errors Some of the enum definitions are unnamed but there's still an attempt at documenting them - that doesn't work. Name them to make that work. Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 52abfbcd5975..639e77abf064 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2177,37 +2177,37 @@ enum ieee80211_tdls_actioncode { #define WLAN_BSS_COEX_INFORMATION_REQUEST BIT(0) /** - * enum - mesh synchronization method identifier + * enum ieee80211_mesh_sync_method - mesh synchronization method identifier * * @IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET: the default synchronization method * @IEEE80211_SYNC_METHOD_VENDOR: a vendor specific synchronization method * that will be specified in a vendor specific information element */ -enum { +enum ieee80211_mesh_sync_method { IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET = 1, IEEE80211_SYNC_METHOD_VENDOR = 255, }; /** - * enum - mesh path selection protocol identifier + * enum ieee80211_mesh_path_protocol - mesh path selection protocol identifier * * @IEEE80211_PATH_PROTOCOL_HWMP: the default path selection protocol * @IEEE80211_PATH_PROTOCOL_VENDOR: a vendor specific protocol that will * be specified in a vendor specific information element */ -enum { +enum ieee80211_mesh_path_protocol { IEEE80211_PATH_PROTOCOL_HWMP = 1, IEEE80211_PATH_PROTOCOL_VENDOR = 255, }; /** - * enum - mesh path selection metric identifier + * enum ieee80211_mesh_path_metric - mesh path selection metric identifier * * @IEEE80211_PATH_METRIC_AIRTIME: the default path selection metric * @IEEE80211_PATH_METRIC_VENDOR: a vendor specific metric that will be * specified in a vendor specific information element */ -enum { +enum ieee80211_mesh_path_metric { IEEE80211_PATH_METRIC_AIRTIME = 1, IEEE80211_PATH_METRIC_VENDOR = 255, }; -- cgit From cf147085fdda044622973a12e4e06f1c753ab677 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 26 Apr 2017 10:58:51 +0300 Subject: mac80211: don't parse encrypted management frames in ieee80211_frame_acked ieee80211_frame_acked is called when a frame is acked by the peer. In case this is a management frame, we check if this an SMPS frame, in which case we can update our antenna configuration. When we parse the management frame we look at the category in case it is an action frame. That byte sits after the IV in case the frame was encrypted. This means that if the frame was encrypted, we basically look at the IV instead of looking at the category. It is then theorically possible that we think that an SMPS action frame was acked where really we had another frame that was encrypted. Since the only management frame whose ack needs to be tracked is the SMPS action frame, and that frame is not a robust management frame, it will never be encrypted. The easiest way to fix this problem is then to not look at frames that were encrypted. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- net/mac80211/status.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 83b8b11f24ea..fac191d6dcb7 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -200,6 +200,7 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) } if (ieee80211_is_action(mgmt->frame_control) && + !ieee80211_has_protected(mgmt->frame_control) && mgmt->u.action.category == WLAN_CATEGORY_HT && mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS && ieee80211_sdata_running(sdata)) { -- cgit From 4a199068230bd8074f2e39025a2216390ea5b829 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 26 Apr 2017 10:58:53 +0300 Subject: mac80211: disentangle iflist_mtx and chanctx_mtx At least on iwlwifi, sometimes lockdep complains that we can lock chanctx_mtx -> mvm.mutex -> iflist_mtx (due to iterate_interfaces) and iflist_mtx -> chanctx_mtx Remove the latter dependency in mac80211 by using the RTNL that we already hold in one case, and can relatively easily achieve in the other case. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 6 +----- net/mac80211/main.c | 2 ++ net/mac80211/util.c | 9 ++++++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 038c31336167..e4a370b42a93 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -739,11 +739,8 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy, return 0; mutex_lock(&local->mtx); - mutex_lock(&local->iflist_mtx); if (local->use_chanctx) { - sdata = rcu_dereference_protected( - local->monitor_sdata, - lockdep_is_held(&local->iflist_mtx)); + sdata = rtnl_dereference(local->monitor_sdata); if (sdata) { ieee80211_vif_release_channel(sdata); ret = ieee80211_vif_use_channel(sdata, chandef, @@ -756,7 +753,6 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy, if (ret == 0) local->monitor_chandef = *chandef; - mutex_unlock(&local->iflist_mtx); mutex_unlock(&local->mtx); return ret; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index ae408a96c407..8aa1f5b6a051 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -253,6 +253,7 @@ static void ieee80211_restart_work(struct work_struct *work) WARN(test_bit(SCAN_HW_SCANNING, &local->scanning), "%s called with hardware scan in progress\n", __func__); + flush_work(&local->radar_detected_work); rtnl_lock(); list_for_each_entry(sdata, &local->interfaces, list) flush_delayed_work(&sdata->dec_tailroom_needed_wk); @@ -1187,6 +1188,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) cancel_work_sync(&local->reconfig_filter); cancel_work_sync(&local->tdls_chsw_work); flush_work(&local->sched_scan_stopped_work); + flush_work(&local->radar_detected_work); ieee80211_clear_tx_pending(local); rate_control_deinitialize(local); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7a37ce78bb38..37dad3dd6bac 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2791,8 +2791,10 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) struct ieee80211_sub_if_data *sdata; struct cfg80211_chan_def chandef; + /* for interface list, to avoid linking iflist_mtx and chanctx_mtx */ + ASSERT_RTNL(); + mutex_lock(&local->mtx); - mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { /* it might be waiting for the local->mtx, but then * by the time it gets it, sdata->wdev.cac_started @@ -2809,7 +2811,6 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local) GFP_KERNEL); } } - mutex_unlock(&local->iflist_mtx); mutex_unlock(&local->mtx); } @@ -2831,7 +2832,9 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work) } mutex_unlock(&local->chanctx_mtx); + rtnl_lock(); ieee80211_dfs_cac_cancel(local); + rtnl_unlock(); if (num_chanctx > 1) /* XXX: multi-channel is not supported yet */ @@ -2846,7 +2849,7 @@ void ieee80211_radar_detected(struct ieee80211_hw *hw) trace_api_radar_detected(local); - ieee80211_queue_work(hw, &local->radar_detected_work); + schedule_work(&local->radar_detected_work); } EXPORT_SYMBOL(ieee80211_radar_detected); -- cgit From e5f2e0671e7ffb0723c5d96684d8fa1ea2403074 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Wed, 26 Apr 2017 10:58:54 +0300 Subject: mac80211: make multicast variable a bool in ieee80211_accept_frame() The multicast variable in the ieee80211_accept_frame() function is treated as a boolean, but defined as int. Fix that. Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 0094f3c0af64..fe6a760aa1ee 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3574,7 +3574,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) struct ieee80211_hdr *hdr = (void *)skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); u8 *bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type); - int multicast = is_multicast_ether_addr(hdr->addr1); + bool multicast = is_multicast_ether_addr(hdr->addr1); switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: -- cgit From c2701b370e6b4d0022043691b5ac7adad015e4fc Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 26 Apr 2017 20:14:34 +0200 Subject: can: fix CAN BCM build with CONFIG_PROC_FS disabled The introduced namespace support moved the BCM variables for procfs into a per-net data structure. This leads to a build failure with disabled procfs: on x86_64: when CONFIG_PROC_FS is not enabled: ../net/can/bcm.c:1541:14: error: 'struct netns_can' has no member named 'bcmproc_dir' ../net/can/bcm.c:1601:14: error: 'struct netns_can' has no member named 'bcmproc_dir' ../net/can/bcm.c:1696:11: error: 'struct netns_can' has no member named 'bcmproc_dir' ../net/can/bcm.c:1707:15: error: 'struct netns_can' has no member named 'bcmproc_dir' http://marc.info/?l=linux-can&m=149321842526524&w=2 Reported-by: Randy Dunlap Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- net/can/bcm.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 0e855917b7e1..65432633a250 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -147,6 +147,7 @@ static inline ktime_t bcm_timeval_to_ktime(struct bcm_timeval tv) /* * procfs functions */ +#if IS_ENABLED(CONFIG_PROC_FS) static char *bcm_proc_getifname(struct net *net, char *result, int ifindex) { struct net_device *dev; @@ -251,6 +252,7 @@ static const struct file_operations bcm_proc_fops = { .llseek = seq_lseek, .release = single_release, }; +#endif /* CONFIG_PROC_FS */ /* * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface @@ -1537,9 +1539,11 @@ static int bcm_release(struct socket *sock) bcm_remove_op(op); } +#if IS_ENABLED(CONFIG_PROC_FS) /* remove procfs entry */ if (net->can.bcmproc_dir && bo->bcm_proc_read) remove_proc_entry(bo->procname, net->can.bcmproc_dir); +#endif /* CONFIG_PROC_FS */ /* remove device reference */ if (bo->bound) { @@ -1598,6 +1602,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, bo->ifindex = 0; } +#if IS_ENABLED(CONFIG_PROC_FS) if (net->can.bcmproc_dir) { /* unique socket address as filename */ sprintf(bo->procname, "%lu", sock_i_ino(sk)); @@ -1609,6 +1614,7 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len, goto fail; } } +#endif /* CONFIG_PROC_FS */ bo->bound = 1; @@ -1691,22 +1697,21 @@ static const struct can_proto bcm_can_proto = { static int canbcm_pernet_init(struct net *net) { +#if IS_ENABLED(CONFIG_PROC_FS) /* create /proc/net/can-bcm directory */ - if (IS_ENABLED(CONFIG_PROC_FS)) { - net->can.bcmproc_dir = - proc_net_mkdir(net, "can-bcm", net->proc_net); - } + net->can.bcmproc_dir = proc_net_mkdir(net, "can-bcm", net->proc_net); +#endif /* CONFIG_PROC_FS */ return 0; } static void canbcm_pernet_exit(struct net *net) { +#if IS_ENABLED(CONFIG_PROC_FS) /* remove /proc/net/can-bcm directory */ - if (IS_ENABLED(CONFIG_PROC_FS)) { - if (net->can.bcmproc_dir) - remove_proc_entry("can-bcm", net->proc_net); - } + if (net->can.bcmproc_dir) + remove_proc_entry("can-bcm", net->proc_net); +#endif /* CONFIG_PROC_FS */ } static struct pernet_operations canbcm_pernet_ops __read_mostly = { -- cgit From 4b726e81dab2cefefd2c1cb00e3074ac0eec4b15 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 26 Apr 2017 21:10:55 -0700 Subject: tcp: tcp_rack_reo_timeout() must update tp->tcp_mstamp I wrongly assumed tp->tcp_mstamp was up to date at the time tcp_rack_reo_timeout() was called. It is not true, since we only update tcp->tcp_mstamp when receiving a packet (as initially done in commit 69e996c58a35 ("tcp: add tp->tcp_mstamp field") tcp_rack_reo_timeout() being called by a timer and not an incoming packet, we need to refresh tp->tcp_mstamp Fixes: 7c1c7308592f ("tcp: do not pass timestamp to tcp_rack_detect_loss()") Signed-off-by: Eric Dumazet Cc: Soheil Hassas Yeganeh Cc: Neal Cardwell Cc: Yuchung Cheng Acked-by: Soheil Hassas Yeganeh Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_recovery.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c index cd72b3d3879e..362b8c75bfab 100644 --- a/net/ipv4/tcp_recovery.c +++ b/net/ipv4/tcp_recovery.c @@ -166,6 +166,7 @@ void tcp_rack_reo_timeout(struct sock *sk) u32 timeout, prior_inflight; prior_inflight = tcp_packets_in_flight(tp); + skb_mstamp_get(&tp->tcp_mstamp); tcp_rack_detect_loss(sk, &timeout); if (prior_inflight != tcp_packets_in_flight(tp)) { if (inet_csk(sk)->icsk_ca_state != TCP_CA_Recovery) { -- cgit From 6d684e54690caef45cf14051ddeb7c71beeb681b Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Thu, 27 Apr 2017 13:44:51 +0800 Subject: rhashtable: Cap total number of entries to 2^31 When max_size is not set or if it set to a sufficiently large value, the nelems counter can overflow. This would cause havoc with the automatic shrinking as it would then attempt to fit a huge number of entries into a tiny hash table. This patch fixes this by adding max_elems to struct rhashtable to cap the number of elements. This is set to 2^31 as nelems is not a precise count. This is sufficiently smaller than UINT_MAX that it should be safe. When max_size is set max_elems will be lowered to at most twice max_size as is the status quo. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 5 +++-- lib/rhashtable.c | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index ae93b65d13d7..45f89369c4c8 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -155,6 +155,7 @@ struct rhashtable_params { * @nelems: Number of elements in table * @key_len: Key length for hashfn * @p: Configuration parameters + * @max_elems: Maximum number of elements in table * @rhlist: True if this is an rhltable * @run_work: Deferred worker to expand/shrink asynchronously * @mutex: Mutex to protect current/future table swapping @@ -165,6 +166,7 @@ struct rhashtable { atomic_t nelems; unsigned int key_len; struct rhashtable_params p; + unsigned int max_elems; bool rhlist; struct work_struct run_work; struct mutex mutex; @@ -327,8 +329,7 @@ static inline bool rht_grow_above_100(const struct rhashtable *ht, static inline bool rht_grow_above_max(const struct rhashtable *ht, const struct bucket_table *tbl) { - return ht->p.max_size && - (atomic_read(&ht->nelems) / 2u) >= ht->p.max_size; + return atomic_read(&ht->nelems) >= ht->max_elems; } /* The bucket lock is selected based on the hash and protects mutations diff --git a/lib/rhashtable.c b/lib/rhashtable.c index f3b82e0d417b..751630bbe409 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -961,6 +961,11 @@ int rhashtable_init(struct rhashtable *ht, if (params->max_size) ht->p.max_size = rounddown_pow_of_two(params->max_size); + /* Cap total entries at 2^31 to avoid nelems overflow. */ + ht->max_elems = 1u << 31; + if (ht->p.max_size < ht->max_elems / 2) + ht->max_elems = ht->p.max_size * 2; + ht->p.min_size = max(ht->p.min_size, HASH_MIN_SIZE); if (params->nelem_hint) -- cgit From 3993f2cb983b2100409851f7b2abb21d685ea19c Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 27 Apr 2017 09:11:13 -0700 Subject: samples/bpf: Add support for SKB_MODE to xdp1 and xdp_tx_iptunnel Add option to xdp1 and xdp_tx_iptunnel to insert xdp program in SKB_MODE: - update set_link_xdp_fd to take a flags argument that is added to the RTM_SETLINK message - Add -S option to xdp1 and xdp_tx_iptunnel user code. When passed in XDP_FLAGS_SKB_MODE is set in the flags arg passed to set_link_xdp_fd Signed-off-by: David Ahern Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/bpf_load.c | 19 +++++++++++++++--- samples/bpf/bpf_load.h | 2 +- samples/bpf/xdp1_user.c | 40 ++++++++++++++++++++++++++++++-------- samples/bpf/xdp_tx_iptunnel_user.c | 13 +++++++++---- 4 files changed, 58 insertions(+), 16 deletions(-) diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c index 0d449d8032d1..d4433a47e6c3 100644 --- a/samples/bpf/bpf_load.c +++ b/samples/bpf/bpf_load.c @@ -563,7 +563,7 @@ struct ksym *ksym_search(long key) return &syms[0]; } -int set_link_xdp_fd(int ifindex, int fd) +int set_link_xdp_fd(int ifindex, int fd, int flags) { struct sockaddr_nl sa; int sock, seq = 0, len, ret = -1; @@ -599,15 +599,28 @@ int set_link_xdp_fd(int ifindex, int fd) req.nh.nlmsg_seq = ++seq; req.ifinfo.ifi_family = AF_UNSPEC; req.ifinfo.ifi_index = ifindex; + + /* started nested attribute for XDP */ nla = (struct nlattr *)(((char *)&req) + NLMSG_ALIGN(req.nh.nlmsg_len)); nla->nla_type = NLA_F_NESTED | 43/*IFLA_XDP*/; + nla->nla_len = NLA_HDRLEN; - nla_xdp = (struct nlattr *)((char *)nla + NLA_HDRLEN); + /* add XDP fd */ + nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); nla_xdp->nla_type = 1/*IFLA_XDP_FD*/; nla_xdp->nla_len = NLA_HDRLEN + sizeof(int); memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd)); - nla->nla_len = NLA_HDRLEN + nla_xdp->nla_len; + nla->nla_len += nla_xdp->nla_len; + + /* if user passed in any flags, add those too */ + if (flags) { + nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); + nla_xdp->nla_type = 3/*IFLA_XDP_FLAGS*/; + nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags); + memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags)); + nla->nla_len += nla_xdp->nla_len; + } req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); diff --git a/samples/bpf/bpf_load.h b/samples/bpf/bpf_load.h index 68f6b2d22507..6bfd75ec6a16 100644 --- a/samples/bpf/bpf_load.h +++ b/samples/bpf/bpf_load.h @@ -47,5 +47,5 @@ struct ksym { int load_kallsyms(void); struct ksym *ksym_search(long key); -int set_link_xdp_fd(int ifindex, int fd); +int set_link_xdp_fd(int ifindex, int fd, int flags); #endif diff --git a/samples/bpf/xdp1_user.c b/samples/bpf/xdp1_user.c index d2be65d1fd86..deb05e630d84 100644 --- a/samples/bpf/xdp1_user.c +++ b/samples/bpf/xdp1_user.c @@ -5,6 +5,7 @@ * License as published by the Free Software Foundation. */ #include +#include #include #include #include @@ -12,16 +13,18 @@ #include #include #include +#include #include "bpf_load.h" #include "bpf_util.h" #include "libbpf.h" static int ifindex; +static int flags; static void int_exit(int sig) { - set_link_xdp_fd(ifindex, -1); + set_link_xdp_fd(ifindex, -1, flags); exit(0); } @@ -54,18 +57,39 @@ static void poll_stats(int interval) } } -int main(int ac, char **argv) +static void usage(const char *prog) { - char filename[256]; + fprintf(stderr, + "usage: %s [OPTS] IFINDEX\n\n" + "OPTS:\n" + " -S use skb-mode\n", + prog); +} - snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); +int main(int argc, char **argv) +{ + const char *optstr = "S"; + char filename[256]; + int opt; + + while ((opt = getopt(argc, argv, optstr)) != -1) { + switch (opt) { + case 'S': + flags |= XDP_FLAGS_SKB_MODE; + break; + default: + usage(basename(argv[0])); + return 1; + } + } - if (ac != 2) { - printf("usage: %s IFINDEX\n", argv[0]); + if (optind == argc) { + usage(basename(argv[0])); return 1; } + ifindex = strtoul(argv[optind], NULL, 0); - ifindex = strtoul(argv[1], NULL, 0); + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); if (load_bpf_file(filename)) { printf("%s", bpf_log_buf); @@ -79,7 +103,7 @@ int main(int ac, char **argv) signal(SIGINT, int_exit); - if (set_link_xdp_fd(ifindex, prog_fd[0]) < 0) { + if (set_link_xdp_fd(ifindex, prog_fd[0], flags) < 0) { printf("link set xdp fd failed\n"); return 1; } diff --git a/samples/bpf/xdp_tx_iptunnel_user.c b/samples/bpf/xdp_tx_iptunnel_user.c index 70e192fc61aa..cb2bda7b5346 100644 --- a/samples/bpf/xdp_tx_iptunnel_user.c +++ b/samples/bpf/xdp_tx_iptunnel_user.c @@ -5,6 +5,7 @@ * License as published by the Free Software Foundation. */ #include +#include #include #include #include @@ -28,7 +29,7 @@ static int ifindex = -1; static void int_exit(int sig) { if (ifindex > -1) - set_link_xdp_fd(ifindex, -1); + set_link_xdp_fd(ifindex, -1, 0); exit(0); } @@ -136,12 +137,13 @@ int main(int argc, char **argv) { unsigned char opt_flags[256] = {}; unsigned int kill_after_s = 0; - const char *optstr = "i:a:p:s:d:m:T:P:h"; + const char *optstr = "i:a:p:s:d:m:T:P:Sh"; int min_port = 0, max_port = 0; struct iptnl_info tnl = {}; struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; struct vip vip = {}; char filename[256]; + int flags = 0; int opt; int i; @@ -201,6 +203,9 @@ int main(int argc, char **argv) case 'T': kill_after_s = atoi(optarg); break; + case 'S': + flags |= XDP_FLAGS_SKB_MODE; + break; default: usage(argv[0]); return 1; @@ -243,14 +248,14 @@ int main(int argc, char **argv) } } - if (set_link_xdp_fd(ifindex, prog_fd[0]) < 0) { + if (set_link_xdp_fd(ifindex, prog_fd[0], flags) < 0) { printf("link set xdp fd failed\n"); return 1; } poll_stats(kill_after_s); - set_link_xdp_fd(ifindex, -1); + set_link_xdp_fd(ifindex, -1, flags); return 0; } -- cgit From 8ecbc40ada116f2f7d6b61cd646802c87b7c5c7d Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Wed, 26 Apr 2017 11:05:12 +0800 Subject: net: update comment for netif_dormant() function This patch updates the comment for netif_dormant() function to reflect the intended usage. Signed-off-by: Zhang Shengju Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 8c5c8cdc7b97..6847714a5ae3 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3408,10 +3408,10 @@ static inline void netif_dormant_off(struct net_device *dev) } /** - * netif_dormant - test if carrier present + * netif_dormant - test if device is dormant * @dev: network device * - * Check if carrier is present on device + * Check if device is dormant. */ static inline bool netif_dormant(const struct net_device *dev) { -- cgit From 0575c86b5dd596253bdfc0365b570d67b1a12523 Mon Sep 17 00:00:00 2001 From: Zhang Shengju Date: Wed, 26 Apr 2017 17:49:38 +0800 Subject: net: remove unnecessary carrier status check Since netif_carrier_on() will do nothing if device's carrier is already on, so it's unnecessary to do carrier status check. It's the same for netif_carrier_off(). Signed-off-by: Zhang Shengju Signed-off-by: David S. Miller --- net/core/dev.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 3361ee87fcc2..8371a01eee87 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -7245,13 +7245,10 @@ void netif_stacked_transfer_operstate(const struct net_device *rootdev, else netif_dormant_off(dev); - if (netif_carrier_ok(rootdev)) { - if (!netif_carrier_ok(dev)) - netif_carrier_on(dev); - } else { - if (netif_carrier_ok(dev)) - netif_carrier_off(dev); - } + if (netif_carrier_ok(rootdev)) + netif_carrier_on(dev); + else + netif_carrier_off(dev); } EXPORT_SYMBOL(netif_stacked_transfer_operstate); -- cgit From 1514dc857f8ebbeb44da09236efa133ed6e15615 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Wed, 26 Apr 2017 11:54:47 +0200 Subject: l2tp: remove useless device duplication test in l2tp_eth_create() There's no need to verify that cfg->ifname is unique at this point. register_netdev() will return -EEXIST if asked to create a device with a name that's alrealy in use. Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller --- net/l2tp/l2tp_eth.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index 59aba8aeac03..8b21af7321b9 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -280,12 +280,6 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p } if (cfg->ifname) { - dev = dev_get_by_name(net, cfg->ifname); - if (dev) { - dev_put(dev); - rc = -EEXIST; - goto out; - } strlcpy(name, cfg->ifname, IFNAMSIZ); name_assign_type = NET_NAME_USER; } else { -- cgit From 06b4fc520d21b7a6327983d64373505859672e91 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Wed, 26 Apr 2017 19:04:04 +0800 Subject: net: fib: Decrease one unnecessary rt cache flush in fib_disable_ip The func fib_flush already flushes the rt cache if necessary, so it is not necessary to invoke rt_cache_flush again in fib_disable_ip. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- net/ipv4/fib_frontend.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 5a0e456b5d58..39bd1edee676 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1130,7 +1130,8 @@ static void fib_disable_ip(struct net_device *dev, unsigned long event, { if (fib_sync_down_dev(dev, event, force)) fib_flush(dev_net(dev)); - rt_cache_flush(dev_net(dev)); + else + rt_cache_flush(dev_net(dev)); arp_ifdown(dev); } -- cgit From 99f906e9ad7b6e79ffeda30f45906a8448b9d6a2 Mon Sep 17 00:00:00 2001 From: Mike Manning Date: Wed, 26 Apr 2017 14:48:09 +0100 Subject: bridge: add per-port broadcast flood flag Support for l2 multicast flood control was added in commit b6cb5ac8331b ("net: bridge: add per-port multicast flood flag"). It allows broadcast as it was introduced specifically for unknown multicast flood control. But as broadcast is a special case of multicast, this may also need to be disabled. For this purpose, introduce a flag to disable the flooding of received l2 broadcasts. This approach is backwards compatible and provides flexibility in filtering for the desired packet types. Cc: Nikolay Aleksandrov Signed-off-by: Mike Manning Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/linux/if_bridge.h | 1 + include/uapi/linux/if_link.h | 1 + net/bridge/br_forward.c | 24 +++++++++++++++++------- net/bridge/br_if.c | 2 +- net/bridge/br_netlink.c | 3 +++ net/bridge/br_sysfs_if.c | 2 ++ 6 files changed, 25 insertions(+), 8 deletions(-) diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index c5847dc75a93..0c16866a7aac 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -48,6 +48,7 @@ struct br_ip_list { #define BR_MCAST_FLOOD BIT(11) #define BR_MULTICAST_TO_UNICAST BIT(12) #define BR_VLAN_TUNNEL BIT(13) +#define BR_BCAST_FLOOD BIT(14) #define BR_DEFAULT_AGEING_TIME (300 * HZ) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 633aa0276d32..8e56ac70e0d1 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -323,6 +323,7 @@ enum { IFLA_BRPORT_MCAST_FLOOD, IFLA_BRPORT_MCAST_TO_UCAST, IFLA_BRPORT_VLAN_TUNNEL, + IFLA_BRPORT_BCAST_FLOOD, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 902af6ba481c..48fb17417fac 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -183,13 +183,23 @@ void br_flood(struct net_bridge *br, struct sk_buff *skb, struct net_bridge_port *p; list_for_each_entry_rcu(p, &br->port_list, list) { - /* Do not flood unicast traffic to ports that turn it off */ - if (pkt_type == BR_PKT_UNICAST && !(p->flags & BR_FLOOD)) - continue; - /* Do not flood if mc off, except for traffic we originate */ - if (pkt_type == BR_PKT_MULTICAST && - !(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev) - continue; + /* Do not flood unicast traffic to ports that turn it off, nor + * other traffic if flood off, except for traffic we originate + */ + switch (pkt_type) { + case BR_PKT_UNICAST: + if (!(p->flags & BR_FLOOD)) + continue; + break; + case BR_PKT_MULTICAST: + if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev) + continue; + break; + case BR_PKT_BROADCAST: + if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev) + continue; + break; + } /* Do not flood to ports that enable proxy ARP */ if (p->flags & BR_PROXYARP) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index f3544d96155c..7f8d05cf9065 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -361,7 +361,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, p->path_cost = port_cost(dev); p->priority = 0x8000 >> BR_PORT_BITS; p->port_no = index; - p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD; + p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; br_init_port(p); br_set_state(p, BR_STATE_DISABLED); br_stp_port_timer_init(p); diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 650986473577..a572db710d4e 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -189,6 +189,8 @@ static int br_port_fill_attrs(struct sk_buff *skb, !!(p->flags & BR_FLOOD)) || nla_put_u8(skb, IFLA_BRPORT_MCAST_FLOOD, !!(p->flags & BR_MCAST_FLOOD)) || + nla_put_u8(skb, IFLA_BRPORT_BCAST_FLOOD, + !!(p->flags & BR_BCAST_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)) || @@ -683,6 +685,7 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD); br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD); br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST, BR_MULTICAST_TO_UNICAST); + br_set_port_flag(p, tb, IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD); br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP); br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI); diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 79aee759aba5..5d5d413a6cf8 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -173,6 +173,7 @@ BRPORT_ATTR_FLAG(unicast_flood, BR_FLOOD); BRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP); BRPORT_ATTR_FLAG(proxyarp_wifi, BR_PROXYARP_WIFI); BRPORT_ATTR_FLAG(multicast_flood, BR_MCAST_FLOOD); +BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD); #ifdef CONFIG_BRIDGE_IGMP_SNOOPING static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) @@ -221,6 +222,7 @@ static const struct brport_attribute *brport_attrs[] = { &brport_attr_proxyarp, &brport_attr_proxyarp_wifi, &brport_attr_multicast_flood, + &brport_attr_broadcast_flood, NULL }; -- cgit From adeb45cbb5057731ce9c47aad93756135d7947bf Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 26 Apr 2017 14:03:50 +0000 Subject: fib_rules: fix error return code Fix to return error code -EINVAL from the error handling case instead of 0, as done elsewhere in this function. Fixes: 622ec2c9d524 ("net: core: add UID to flows, rules, and routes") Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- net/core/fib_rules.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index c58c1df6f92b..f21c4d3aeae0 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -440,6 +440,7 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, if (tb[FRA_TUN_ID]) rule->tun_id = nla_get_be64(tb[FRA_TUN_ID]); + err = -EINVAL; if (tb[FRA_L3MDEV]) { #ifdef CONFIG_NET_L3_MASTER_DEV rule->l3mdev = nla_get_u8(tb[FRA_L3MDEV]); @@ -461,7 +462,6 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, else rule->suppress_ifgroup = -1; - err = -EINVAL; if (tb[FRA_GOTO]) { if (rule->action != FR_ACT_GOTO) goto errout_free; @@ -592,8 +592,10 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, if (tb[FRA_UID_RANGE]) { range = nla_get_kuid_range(tb); - if (!uid_range_set(&range)) + if (!uid_range_set(&range)) { + err = -EINVAL; goto errout; + } } else { range = fib_kuid_range_unset; } -- cgit From 26d31ac11fa47a0ee8dc4c64136ea34feda57e3e Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 26 Apr 2017 07:58:22 -0700 Subject: net: vrf: Do not allow looback to be moved to a VRF Moving the loopback into a VRF breaks networking for the default VRF. Since the VRF device is the loopback for VRF domains, there is no reason to move the loopback. Given the repercussions, block attempts to set lo into a VRF. Signed-off-by: David Ahern Reviewed-by: Greg Rose Signed-off-by: David S. Miller --- drivers/net/vrf.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index aa5d30428bba..ceda5861da78 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -877,6 +877,12 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) { int ret; + /* do not allow loopback device to be enslaved to a VRF. + * The vrf device acts as the loopback for the vrf. + */ + if (port_dev == dev_net(dev)->loopback_dev) + return -EOPNOTSUPP; + port_dev->priv_flags |= IFF_L3MDEV_SLAVE; ret = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL); if (ret < 0) -- cgit From f470f22cfa3d42f357ed404a06ec77aa09ddb05b Mon Sep 17 00:00:00 2001 From: "sudarsana.kalluru@cavium.com" Date: Wed, 26 Apr 2017 09:00:49 -0700 Subject: qed: Add support for MFW resource locking. The patch adds API for default initialization of the MFW resource locking. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_dev.c | 12 ++---------- drivers/net/ethernet/qlogic/qed/qed_mcp.c | 30 ++++++++++++++++++++++++++++++ drivers/net/ethernet/qlogic/qed/qed_mcp.h | 18 +++++++++++++++++- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index ea7931b85879..2a3ae00bbd42 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -2347,9 +2347,6 @@ static int qed_hw_set_resc_info(struct qed_hwfn *p_hwfn) return 0; } -#define QED_RESC_ALLOC_LOCK_RETRY_CNT 10 -#define QED_RESC_ALLOC_LOCK_RETRY_INTVL_US 10000 /* 10 msec */ - static int qed_hw_get_resc(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { struct qed_resc_unlock_params resc_unlock_params; @@ -2366,13 +2363,8 @@ static int qed_hw_get_resc(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) * needed, and proceed to the queries. Other failures, including a * failure to acquire the lock, will cause this function to fail. */ - memset(&resc_lock_params, 0, sizeof(resc_lock_params)); - resc_lock_params.resource = QED_RESC_LOCK_RESC_ALLOC; - resc_lock_params.retry_num = QED_RESC_ALLOC_LOCK_RETRY_CNT; - resc_lock_params.retry_interval = QED_RESC_ALLOC_LOCK_RETRY_INTVL_US; - resc_lock_params.sleep_b4_retry = true; - memset(&resc_unlock_params, 0, sizeof(resc_unlock_params)); - resc_unlock_params.resource = QED_RESC_LOCK_RESC_ALLOC; + qed_mcp_resc_lock_default_init(&resc_lock_params, &resc_unlock_params, + QED_RESC_LOCK_RESC_ALLOC, false); rc = qed_mcp_resc_lock(p_hwfn, p_ptt, &resc_lock_params); if (rc && rc != -EINVAL) { diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index ff6080df2246..7266b36a2655 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -2615,3 +2615,33 @@ qed_mcp_resc_unlock(struct qed_hwfn *p_hwfn, return 0; } + +void qed_mcp_resc_lock_default_init(struct qed_resc_lock_params *p_lock, + struct qed_resc_unlock_params *p_unlock, + enum qed_resc_lock + resource, bool b_is_permanent) +{ + if (p_lock) { + memset(p_lock, 0, sizeof(*p_lock)); + + /* Permanent resources don't require aging, and there's no + * point in trying to acquire them more than once since it's + * unexpected another entity would release them. + */ + if (b_is_permanent) { + p_lock->timeout = QED_MCP_RESC_LOCK_TO_NONE; + } else { + p_lock->retry_num = QED_MCP_RESC_LOCK_RETRY_CNT_DFLT; + p_lock->retry_interval = + QED_MCP_RESC_LOCK_RETRY_VAL_DFLT; + p_lock->sleep_b4_retry = true; + } + + p_lock->resource = resource; + } + + if (p_unlock) { + memset(p_unlock, 0, sizeof(*p_unlock)); + p_unlock->resource = resource; + } +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index ac7d406be1ed..e8cf59774f43 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -795,7 +795,8 @@ int qed_mcp_ov_update_eswitch(struct qed_hwfn *p_hwfn, enum qed_resc_lock { QED_RESC_LOCK_DBG_DUMP = QED_MCP_RESC_LOCK_MIN_VAL, - QED_RESC_LOCK_RESC_ALLOC = QED_MCP_RESC_LOCK_MAX_VAL + QED_RESC_LOCK_RESC_ALLOC = QED_MCP_RESC_LOCK_MAX_VAL, + QED_RESC_LOCK_RESC_INVALID }; /** @@ -818,9 +819,11 @@ struct qed_resc_lock_params { /* Number of times to retry locking */ u8 retry_num; +#define QED_MCP_RESC_LOCK_RETRY_CNT_DFLT 10 /* The interval in usec between retries */ u16 retry_interval; +#define QED_MCP_RESC_LOCK_RETRY_VAL_DFLT 10000 /* Use sleep or delay between retries */ bool sleep_b4_retry; @@ -872,4 +875,17 @@ qed_mcp_resc_unlock(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, struct qed_resc_unlock_params *p_params); +/** + * @brief - default initialization for lock/unlock resource structs + * + * @param p_lock - lock params struct to be initialized; Can be NULL + * @param p_unlock - unlock params struct to be initialized; Can be NULL + * @param resource - the requested resource + * @paral b_is_permanent - disable retries & aging when set + */ +void qed_mcp_resc_lock_default_init(struct qed_resc_lock_params *p_lock, + struct qed_resc_unlock_params *p_unlock, + enum qed_resc_lock + resource, bool b_is_permanent); + #endif -- cgit From db82f70e4c3e0901ba1e5c0eecbd913133261985 Mon Sep 17 00:00:00 2001 From: "sudarsana.kalluru@cavium.com" Date: Wed, 26 Apr 2017 09:00:50 -0700 Subject: qed: Add support for PTP resource locking. The patch adds support for per-port resource lock in favour of PTP. PTP module acquires/releases the MFW resource lock while enabling/ disabling the PTP on the interface. The PF instance which has the ownership of this resource lock will get the exclusive access to the PTP clock functionality on the port. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 1 + drivers/net/ethernet/qlogic/qed/qed_dev.c | 14 +++++ drivers/net/ethernet/qlogic/qed/qed_mcp.h | 4 ++ drivers/net/ethernet/qlogic/qed/qed_ptp.c | 87 +++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 8a8f1396de66..3f8d07bddf4d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -767,6 +767,7 @@ void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, void qed_clean_wfq_db(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); int qed_device_num_engines(struct qed_dev *cdev); +int qed_device_get_port_id(struct qed_dev *cdev); #define QED_LEADING_HWFN(dev) (&dev->hwfns[0]) diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 2a3ae00bbd42..aa1a4d5c864c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -4064,3 +4064,17 @@ int qed_device_num_engines(struct qed_dev *cdev) { return QED_IS_BB(cdev) ? 2 : 1; } + +static int qed_device_num_ports(struct qed_dev *cdev) +{ + /* in CMT always only one port */ + if (cdev->num_hwfns > 1) + return 1; + + return cdev->num_ports_in_engines * qed_device_num_engines(cdev); +} + +int qed_device_get_port_id(struct qed_dev *cdev) +{ + return (QED_LEADING_HWFN(cdev)->abs_pf_id) % qed_device_num_ports(cdev); +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index e8cf59774f43..5ae35d6cc7d1 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -795,6 +795,10 @@ int qed_mcp_ov_update_eswitch(struct qed_hwfn *p_hwfn, enum qed_resc_lock { QED_RESC_LOCK_DBG_DUMP = QED_MCP_RESC_LOCK_MIN_VAL, + QED_RESC_LOCK_PTP_PORT0, + QED_RESC_LOCK_PTP_PORT1, + QED_RESC_LOCK_PTP_PORT2, + QED_RESC_LOCK_PTP_PORT3, QED_RESC_LOCK_RESC_ALLOC = QED_MCP_RESC_LOCK_MAX_VAL, QED_RESC_LOCK_RESC_INVALID }; diff --git a/drivers/net/ethernet/qlogic/qed/qed_ptp.c b/drivers/net/ethernet/qlogic/qed/qed_ptp.c index 80c9c0b172dd..26a9baf3497f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ptp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ptp.c @@ -34,6 +34,7 @@ #include "qed_dev_api.h" #include "qed_hw.h" #include "qed_l2.h" +#include "qed_mcp.h" #include "qed_ptp.h" #include "qed_reg_addr.h" @@ -45,6 +46,82 @@ #define QED_DRIFT_CNTR_DIRECTION_SHIFT 31 #define QED_TIMESTAMP_MASK BIT(16) +static enum qed_resc_lock qed_ptcdev_to_resc(struct qed_hwfn *p_hwfn) +{ + switch (qed_device_get_port_id(p_hwfn->cdev)) { + case 0: + return QED_RESC_LOCK_PTP_PORT0; + case 1: + return QED_RESC_LOCK_PTP_PORT1; + case 2: + return QED_RESC_LOCK_PTP_PORT2; + case 3: + return QED_RESC_LOCK_PTP_PORT3; + default: + return QED_RESC_LOCK_RESC_INVALID; + } +} + +static int qed_ptp_res_lock(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + struct qed_resc_lock_params params; + enum qed_resc_lock resource; + int rc; + + resource = qed_ptcdev_to_resc(p_hwfn); + if (resource == QED_RESC_LOCK_RESC_INVALID) + return -EINVAL; + + qed_mcp_resc_lock_default_init(¶ms, NULL, resource, true); + + rc = qed_mcp_resc_lock(p_hwfn, p_ptt, ¶ms); + if (rc && rc != -EINVAL) { + return rc; + } else if (rc == -EINVAL) { + /* MFW doesn't support resource locking, first PF on the port + * has lock ownership. + */ + if (p_hwfn->abs_pf_id < p_hwfn->cdev->num_ports_in_engines) + return 0; + + DP_INFO(p_hwfn, "PF doesn't have lock ownership\n"); + return -EBUSY; + } else if (!rc && !params.b_granted) { + DP_INFO(p_hwfn, "Failed to acquire ptp resource lock\n"); + return -EBUSY; + } + + return rc; +} + +static int qed_ptp_res_unlock(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + struct qed_resc_unlock_params params; + enum qed_resc_lock resource; + int rc; + + resource = qed_ptcdev_to_resc(p_hwfn); + if (resource == QED_RESC_LOCK_RESC_INVALID) + return -EINVAL; + + qed_mcp_resc_lock_default_init(NULL, ¶ms, resource, true); + + rc = qed_mcp_resc_unlock(p_hwfn, p_ptt, ¶ms); + if (rc == -EINVAL) { + /* MFW doesn't support locking, first PF has lock ownership */ + if (p_hwfn->abs_pf_id < p_hwfn->cdev->num_ports_in_engines) { + rc = 0; + } else { + DP_INFO(p_hwfn, "PF doesn't have lock ownership\n"); + return -EINVAL; + } + } else if (rc) { + DP_INFO(p_hwfn, "Failed to release the ptp resource lock\n"); + } + + return rc; +} + /* Read Rx timestamp */ static int qed_ptp_hw_read_rx_ts(struct qed_dev *cdev, u64 *timestamp) { @@ -249,6 +326,14 @@ static int qed_ptp_hw_enable(struct qed_dev *cdev) { struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt; + int rc; + + rc = qed_ptp_res_lock(p_hwfn, p_ptt); + if (rc) { + DP_INFO(p_hwfn, + "Couldn't acquire the resource lock, skip ptp enable for this PF\n"); + return rc; + } /* Reset PTP event detection rules - will be configured in the IOCTL */ qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_PARAM_MASK, 0x7FF); @@ -305,6 +390,8 @@ static int qed_ptp_hw_disable(struct qed_dev *cdev) struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt; + qed_ptp_res_unlock(p_hwfn, p_ptt); + /* Reset PTP event detection rules */ qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_PARAM_MASK, 0x7FF); qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_RULE_MASK, 0x3FFF); -- cgit From 035744975aecf9b8e02920d93027a432c51062d1 Mon Sep 17 00:00:00 2001 From: "sudarsana.kalluru@cavium.com" Date: Wed, 26 Apr 2017 09:00:51 -0700 Subject: qede: Add support for PTP resource locking. The patch adds necessary changes to the driver to use qed resource locking functionality. Currently the ptp initialization is spread between driver probe/open implementations, associated APIs are qede_ptp_register_phc()/qede_ptp_start(). Clubbed this functionality into single API qed_ptp_enable() to simplify the usage of qed resource locking implementation. The new API will be invoked in the probe path. Similarly the ptp clean-up code is moved to qede_ptp_disable() which gets invoked in the driver unload path. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede_main.c | 18 +--- drivers/net/ethernet/qlogic/qede/qede_ptp.c | 150 +++++++++++++-------------- drivers/net/ethernet/qlogic/qede/qede_ptp.h | 6 +- 3 files changed, 75 insertions(+), 99 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 292e2dc3f8ae..b9ba23d71c61 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -907,13 +907,8 @@ static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level, edev->ops->common->set_id(cdev, edev->ndev->name, DRV_MODULE_VERSION); /* PTP not supported on VFs */ - if (!is_vf) { - rc = qede_ptp_register_phc(edev); - if (rc) { - DP_NOTICE(edev, "Cannot register PHC\n"); - goto err5; - } - } + if (!is_vf) + qede_ptp_enable(edev, true); edev->ops->register_ops(cdev, &qede_ll_ops, edev); @@ -928,8 +923,6 @@ static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level, return 0; -err5: - unregister_netdev(edev->ndev); err4: qede_roce_dev_remove(edev); err3: @@ -980,7 +973,7 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode) unregister_netdev(ndev); cancel_delayed_work_sync(&edev->sp_task); - qede_ptp_remove(edev); + qede_ptp_disable(edev); qede_roce_dev_remove(edev); @@ -1877,8 +1870,6 @@ static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode, qede_roce_dev_event_close(edev); edev->state = QEDE_STATE_CLOSED; - qede_ptp_stop(edev); - /* Close OS Tx */ netif_tx_disable(edev->ndev); netif_carrier_off(edev->ndev); @@ -1987,13 +1978,10 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode, qede_roce_dev_event_open(edev); - qede_ptp_start(edev, (mode == QEDE_LOAD_NORMAL)); - edev->state = QEDE_STATE_OPEN; DP_INFO(edev, "Ending successfully qede load\n"); - goto out; err4: qede_sync_free_irqs(edev); diff --git a/drivers/net/ethernet/qlogic/qede/qede_ptp.c b/drivers/net/ethernet/qlogic/qede/qede_ptp.c index 2e62dec09bd7..6396363a804e 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ptp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ptp.c @@ -206,21 +206,6 @@ static u64 qede_ptp_read_cc(const struct cyclecounter *cc) return phc_cycles; } -static void qede_ptp_init_cc(struct qede_dev *edev) -{ - struct qede_ptp *ptp; - - ptp = edev->ptp; - if (!ptp) - return; - - memset(&ptp->cc, 0, sizeof(ptp->cc)); - ptp->cc.read = qede_ptp_read_cc; - ptp->cc.mask = CYCLECOUNTER_MASK(64); - ptp->cc.shift = 0; - ptp->cc.mult = 1; -} - static int qede_ptp_cfg_filters(struct qede_dev *edev) { struct qede_ptp *ptp = edev->ptp; @@ -324,61 +309,6 @@ int qede_ptp_hw_ts(struct qede_dev *edev, struct ifreq *ifr) sizeof(config)) ? -EFAULT : 0; } -/* Called during load, to initialize PTP-related stuff */ -static void qede_ptp_init(struct qede_dev *edev, bool init_tc) -{ - struct qede_ptp *ptp; - int rc; - - ptp = edev->ptp; - if (!ptp) - return; - - spin_lock_init(&ptp->lock); - - /* Configure PTP in HW */ - rc = ptp->ops->enable(edev->cdev); - if (rc) { - DP_ERR(edev, "Stopping PTP initialization\n"); - return; - } - - /* Init work queue for Tx timestamping */ - INIT_WORK(&ptp->work, qede_ptp_task); - - /* Init cyclecounter and timecounter. This is done only in the first - * load. If done in every load, PTP application will fail when doing - * unload / load (e.g. MTU change) while it is running. - */ - if (init_tc) { - qede_ptp_init_cc(edev); - timecounter_init(&ptp->tc, &ptp->cc, - ktime_to_ns(ktime_get_real())); - } - - DP_VERBOSE(edev, QED_MSG_DEBUG, "PTP initialization is successful\n"); -} - -void qede_ptp_start(struct qede_dev *edev, bool init_tc) -{ - qede_ptp_init(edev, init_tc); - qede_ptp_cfg_filters(edev); -} - -void qede_ptp_remove(struct qede_dev *edev) -{ - struct qede_ptp *ptp; - - ptp = edev->ptp; - if (ptp && ptp->clock) { - ptp_clock_unregister(ptp->clock); - ptp->clock = NULL; - } - - kfree(ptp); - edev->ptp = NULL; -} - int qede_ptp_get_ts_info(struct qede_dev *edev, struct ethtool_ts_info *info) { struct qede_ptp *ptp = edev->ptp; @@ -417,8 +347,7 @@ int qede_ptp_get_ts_info(struct qede_dev *edev, struct ethtool_ts_info *info) return 0; } -/* Called during unload, to stop PTP-related stuff */ -void qede_ptp_stop(struct qede_dev *edev) +void qede_ptp_disable(struct qede_dev *edev) { struct qede_ptp *ptp; @@ -426,6 +355,11 @@ void qede_ptp_stop(struct qede_dev *edev) if (!ptp) return; + if (ptp->clock) { + ptp_clock_unregister(ptp->clock); + ptp->clock = NULL; + } + /* Cancel PTP work queue. Should be done after the Tx queues are * drained to prevent additional scheduling. */ @@ -439,11 +373,54 @@ void qede_ptp_stop(struct qede_dev *edev) spin_lock_bh(&ptp->lock); ptp->ops->disable(edev->cdev); spin_unlock_bh(&ptp->lock); + + kfree(ptp); + edev->ptp = NULL; } -int qede_ptp_register_phc(struct qede_dev *edev) +static int qede_ptp_init(struct qede_dev *edev, bool init_tc) { struct qede_ptp *ptp; + int rc; + + ptp = edev->ptp; + if (!ptp) + return -EINVAL; + + spin_lock_init(&ptp->lock); + + /* Configure PTP in HW */ + rc = ptp->ops->enable(edev->cdev); + if (rc) { + DP_INFO(edev, "PTP HW enable failed\n"); + return rc; + } + + /* Init work queue for Tx timestamping */ + INIT_WORK(&ptp->work, qede_ptp_task); + + /* Init cyclecounter and timecounter. This is done only in the first + * load. If done in every load, PTP application will fail when doing + * unload / load (e.g. MTU change) while it is running. + */ + if (init_tc) { + memset(&ptp->cc, 0, sizeof(ptp->cc)); + ptp->cc.read = qede_ptp_read_cc; + ptp->cc.mask = CYCLECOUNTER_MASK(64); + ptp->cc.shift = 0; + ptp->cc.mult = 1; + + timecounter_init(&ptp->tc, &ptp->cc, + ktime_to_ns(ktime_get_real())); + } + + return rc; +} + +int qede_ptp_enable(struct qede_dev *edev, bool init_tc) +{ + struct qede_ptp *ptp; + int rc; ptp = kzalloc(sizeof(*ptp), GFP_KERNEL); if (!ptp) { @@ -454,14 +431,19 @@ int qede_ptp_register_phc(struct qede_dev *edev) ptp->edev = edev; ptp->ops = edev->ops->ptp; if (!ptp->ops) { - kfree(ptp); - edev->ptp = NULL; - DP_ERR(edev, "PTP clock registeration failed\n"); - return -EIO; + DP_INFO(edev, "PTP enable failed\n"); + rc = -EIO; + goto err1; } edev->ptp = ptp; + rc = qede_ptp_init(edev, init_tc); + if (rc) + goto err1; + + qede_ptp_cfg_filters(edev); + /* Fill the ptp_clock_info struct and register PTP clock */ ptp->clock_info.owner = THIS_MODULE; snprintf(ptp->clock_info.name, 16, "%s", edev->ndev->name); @@ -478,13 +460,21 @@ int qede_ptp_register_phc(struct qede_dev *edev) ptp->clock = ptp_clock_register(&ptp->clock_info, &edev->pdev->dev); if (IS_ERR(ptp->clock)) { - ptp->clock = NULL; - kfree(ptp); - edev->ptp = NULL; + rc = -EINVAL; DP_ERR(edev, "PTP clock registeration failed\n"); + goto err2; } return 0; + +err2: + qede_ptp_disable(edev); + ptp->clock = NULL; +err1: + kfree(ptp); + edev->ptp = NULL; + + return rc; } void qede_ptp_tx_ts(struct qede_dev *edev, struct sk_buff *skb) diff --git a/drivers/net/ethernet/qlogic/qede/qede_ptp.h b/drivers/net/ethernet/qlogic/qede/qede_ptp.h index f328f9bba53a..691a14c4b2c5 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ptp.h +++ b/drivers/net/ethernet/qlogic/qede/qede_ptp.h @@ -40,10 +40,8 @@ void qede_ptp_rx_ts(struct qede_dev *edev, struct sk_buff *skb); void qede_ptp_tx_ts(struct qede_dev *edev, struct sk_buff *skb); int qede_ptp_hw_ts(struct qede_dev *edev, struct ifreq *req); -void qede_ptp_start(struct qede_dev *edev, bool init_tc); -void qede_ptp_stop(struct qede_dev *edev); -void qede_ptp_remove(struct qede_dev *edev); -int qede_ptp_register_phc(struct qede_dev *edev); +void qede_ptp_disable(struct qede_dev *edev); +int qede_ptp_enable(struct qede_dev *edev, bool init_tc); int qede_ptp_get_ts_info(struct qede_dev *edev, struct ethtool_ts_info *ts); static inline void qede_ptp_record_rx_ts(struct qede_dev *edev, -- cgit From 6a3ff0db13110fad9ecaf9554e0a1f93c5d7b774 Mon Sep 17 00:00:00 2001 From: "sudarsana.kalluru@cavium.com" Date: Wed, 26 Apr 2017 09:00:52 -0700 Subject: qed: Remove the un-needed ptp header file. The patch deletes the qed_ptp.h file which is not required. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_ptp.c | 1 - drivers/net/ethernet/qlogic/qed/qed_ptp.h | 47 ------------------------------- 2 files changed, 48 deletions(-) delete mode 100644 drivers/net/ethernet/qlogic/qed/qed_ptp.h diff --git a/drivers/net/ethernet/qlogic/qed/qed_ptp.c b/drivers/net/ethernet/qlogic/qed/qed_ptp.c index 26a9baf3497f..c0a3cbdfadfa 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ptp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ptp.c @@ -35,7 +35,6 @@ #include "qed_hw.h" #include "qed_l2.h" #include "qed_mcp.h" -#include "qed_ptp.h" #include "qed_reg_addr.h" /* 16 nano second time quantas to wait before making a Drift adjustment */ diff --git a/drivers/net/ethernet/qlogic/qed/qed_ptp.h b/drivers/net/ethernet/qlogic/qed/qed_ptp.h deleted file mode 100644 index 63c666d0b739..000000000000 --- a/drivers/net/ethernet/qlogic/qed/qed_ptp.h +++ /dev/null @@ -1,47 +0,0 @@ -/* QLogic qed NIC Driver - * Copyright (c) 2015-2017 QLogic Corporation - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * COPYING in the main directory of this source tree, or the - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and /or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef _QED_PTP_H -#define _QED_PTP_H -#include - -int qed_ptp_hwtstamp_tx_on(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); -int qed_ptp_cfg_rx_filters(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - enum qed_ptp_filter_type type); -int qed_ptp_read_rx_ts(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u64 *ts); -int qed_ptp_read_tx_ts(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u64 *ts); -int qed_ptp_read_cc(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, u64 *cycles); -int qed_ptp_adjfreq(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, s32 ppb); -int qed_ptp_disable(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); -int qed_ptp_enable(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); - -#endif -- cgit From d179bd1699fccd9d5b9bc38470bab7343a441ea0 Mon Sep 17 00:00:00 2001 From: "sudarsana.kalluru@cavium.com" Date: Wed, 26 Apr 2017 09:00:53 -0700 Subject: qed: Acquire/release ptt_ptp lock when enabling/disabling PTP. Move the code for acquiring/releasing ptt_ptp lock to ptp specific implementations i.e., ptp_enable()/disable() respectively. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 7 +++++-- drivers/net/ethernet/qlogic/qed/qed_main.c | 12 ------------ drivers/net/ethernet/qlogic/qed/qed_ptp.c | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 3f8d07bddf4d..c07191cb7631 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -474,6 +474,11 @@ struct qed_hwfn { struct qed_ptt *p_main_ptt; struct qed_ptt *p_dpc_ptt; + /* PTP will be used only by the leading function. + * Usage of all PTP-apis should be synchronized as result. + */ + struct qed_ptt *p_ptp_ptt; + struct qed_sb_sp_info *p_sp_sb; struct qed_sb_attn_info *p_sb_attn; @@ -532,8 +537,6 @@ struct qed_hwfn { struct qed_ptt *p_arfs_ptt; - /* p_ptp_ptt is valid for leading HWFN only */ - struct qed_ptt *p_ptp_ptt; struct qed_simd_fp_handler simd_proto_handler[64]; #ifdef CONFIG_QED_SRIOV diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index a919260b68f2..8a5a0649fc4a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -956,13 +956,6 @@ static int qed_slowpath_start(struct qed_dev *cdev, } } #endif - p_ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev)); - if (p_ptt) { - QED_LEADING_HWFN(cdev)->p_ptp_ptt = p_ptt; - } else { - DP_NOTICE(cdev, "Failed to acquire PTT for PTP\n"); - goto err; - } } cdev->rx_coalesce_usecs = QED_DEFAULT_RX_USECS; @@ -1076,9 +1069,6 @@ err: qed_ptt_release(QED_LEADING_HWFN(cdev), QED_LEADING_HWFN(cdev)->p_arfs_ptt); #endif - if (IS_PF(cdev) && QED_LEADING_HWFN(cdev)->p_ptp_ptt) - qed_ptt_release(QED_LEADING_HWFN(cdev), - QED_LEADING_HWFN(cdev)->p_ptp_ptt); qed_iov_wq_stop(cdev, false); @@ -1098,8 +1088,6 @@ static int qed_slowpath_stop(struct qed_dev *cdev) qed_ptt_release(QED_LEADING_HWFN(cdev), QED_LEADING_HWFN(cdev)->p_arfs_ptt); #endif - qed_ptt_release(QED_LEADING_HWFN(cdev), - QED_LEADING_HWFN(cdev)->p_ptp_ptt); qed_free_stream_mem(cdev); if (IS_QED_ETH_IF(cdev)) qed_sriov_disable(cdev, true); diff --git a/drivers/net/ethernet/qlogic/qed/qed_ptp.c b/drivers/net/ethernet/qlogic/qed/qed_ptp.c index c0a3cbdfadfa..1871ebfdb793 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ptp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ptp.c @@ -324,13 +324,23 @@ static int qed_ptp_hw_adjfreq(struct qed_dev *cdev, s32 ppb) static int qed_ptp_hw_enable(struct qed_dev *cdev) { struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); - struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt; + struct qed_ptt *p_ptt; int rc; + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) { + DP_NOTICE(p_hwfn, "Failed to acquire PTT for PTP\n"); + return -EBUSY; + } + + p_hwfn->p_ptp_ptt = p_ptt; + rc = qed_ptp_res_lock(p_hwfn, p_ptt); if (rc) { DP_INFO(p_hwfn, "Couldn't acquire the resource lock, skip ptp enable for this PF\n"); + qed_ptt_release(p_hwfn, p_ptt); + p_hwfn->p_ptp_ptt = NULL; return rc; } @@ -402,6 +412,9 @@ static int qed_ptp_hw_disable(struct qed_dev *cdev) qed_wr(p_hwfn, p_ptt, NIG_REG_RX_PTP_EN, 0x0); qed_wr(p_hwfn, p_ptt, NIG_REG_TX_PTP_EN, 0x0); + qed_ptt_release(p_hwfn, p_ptt); + p_hwfn->p_ptp_ptt = NULL; + return 0; } -- cgit From 7fdd69c5af2160236e97668bc1fb7d70855c66ae Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 26 Apr 2017 11:13:00 +0200 Subject: mac80211: clean up rate encoding bits in RX status In preparation for adding support for HE rates, clean up the driver report encoding for rate/bandwidth reporting on RX frames. Much of this patch was done with the following spatch: @@ expression status; @@ -status->flag & (RX_FLAG_HT | RX_FLAG_VHT) +status->enc_flags & (RX_ENC_FLAG_HT | RX_ENC_FLAG_VHT) @@ assignment operator op; expression status; @@ -status->flag op RX_FLAG_SHORTPRE +status->enc_flags op RX_ENC_FLAG_SHORTPRE @@ expression status; @@ -status->flag & RX_FLAG_SHORTPRE +status->enc_flags & RX_ENC_FLAG_SHORTPRE @@ assignment operator op; expression status; @@ -status->flag op RX_FLAG_HT +status->enc_flags op RX_ENC_FLAG_HT @@ expression status; @@ -status->flag & RX_FLAG_HT +status->enc_flags & RX_ENC_FLAG_HT @@ assignment operator op; expression status; @@ -status->flag op RX_FLAG_40MHZ +status->enc_flags op RX_ENC_FLAG_40MHZ @@ expression status; @@ -status->flag & RX_FLAG_40MHZ +status->enc_flags & RX_ENC_FLAG_40MHZ @@ assignment operator op; expression status; @@ -status->flag op RX_FLAG_SHORT_GI +status->enc_flags op RX_ENC_FLAG_SHORT_GI @@ expression status; @@ -status->flag & RX_FLAG_SHORT_GI +status->enc_flags & RX_ENC_FLAG_SHORT_GI @@ assignment operator op; expression status; @@ -status->flag op RX_FLAG_HT_GF +status->enc_flags op RX_ENC_FLAG_HT_GF @@ expression status; @@ -status->flag & RX_FLAG_HT_GF +status->enc_flags & RX_ENC_FLAG_HT_GF @@ assignment operator op; expression status; @@ -status->flag op RX_FLAG_VHT +status->enc_flags op RX_ENC_FLAG_VHT @@ expression status; @@ -status->flag & RX_FLAG_VHT +status->enc_flags & RX_ENC_FLAG_VHT @@ assignment operator op; expression status; @@ -status->flag op RX_FLAG_STBC_MASK +status->enc_flags op RX_ENC_FLAG_STBC_MASK @@ expression status; @@ -status->flag & RX_FLAG_STBC_MASK +status->enc_flags & RX_ENC_FLAG_STBC_MASK @@ assignment operator op; expression status; @@ -status->flag op RX_FLAG_LDPC +status->enc_flags op RX_ENC_FLAG_LDPC @@ expression status; @@ -status->flag & RX_FLAG_LDPC +status->enc_flags & RX_ENC_FLAG_LDPC @@ assignment operator op; expression status; @@ -status->flag op RX_FLAG_10MHZ +status->enc_flags op RX_ENC_FLAG_10MHZ @@ expression status; @@ -status->flag & RX_FLAG_10MHZ +status->enc_flags & RX_ENC_FLAG_10MHZ @@ assignment operator op; expression status; @@ -status->flag op RX_FLAG_5MHZ +status->enc_flags op RX_ENC_FLAG_5MHZ @@ expression status; @@ -status->flag & RX_FLAG_5MHZ +status->enc_flags & RX_ENC_FLAG_5MHZ @@ assignment operator op; expression status; @@ -status->vht_flag op RX_VHT_FLAG_80MHZ +status->enc_flags op RX_ENC_FLAG_80MHZ @@ expression status; @@ -status->vht_flag & RX_VHT_FLAG_80MHZ +status->enc_flags & RX_ENC_FLAG_80MHZ @@ assignment operator op; expression status; @@ -status->vht_flag op RX_VHT_FLAG_160MHZ +status->enc_flags op RX_ENC_FLAG_160MHZ @@ expression status; @@ -status->vht_flag & RX_VHT_FLAG_160MHZ +status->enc_flags & RX_ENC_FLAG_160MHZ @@ assignment operator op; expression status; @@ -status->vht_flag op RX_VHT_FLAG_BF +status->enc_flags op RX_ENC_FLAG_BF @@ expression status; @@ -status->vht_flag & RX_VHT_FLAG_BF +status->enc_flags & RX_ENC_FLAG_BF @@ assignment operator op; expression status, STBC; @@ -status->flag op STBC << RX_FLAG_STBC_SHIFT +status->enc_flags op STBC << RX_ENC_FLAG_STBC_SHIFT @@ assignment operator op; expression status; @@ -status.flag op RX_FLAG_SHORTPRE +status.enc_flags op RX_ENC_FLAG_SHORTPRE @@ expression status; @@ -status.flag & RX_FLAG_SHORTPRE +status.enc_flags & RX_ENC_FLAG_SHORTPRE @@ assignment operator op; expression status; @@ -status.flag op RX_FLAG_HT +status.enc_flags op RX_ENC_FLAG_HT @@ expression status; @@ -status.flag & RX_FLAG_HT +status.enc_flags & RX_ENC_FLAG_HT @@ assignment operator op; expression status; @@ -status.flag op RX_FLAG_40MHZ +status.enc_flags op RX_ENC_FLAG_40MHZ @@ expression status; @@ -status.flag & RX_FLAG_40MHZ +status.enc_flags & RX_ENC_FLAG_40MHZ @@ assignment operator op; expression status; @@ -status.flag op RX_FLAG_SHORT_GI +status.enc_flags op RX_ENC_FLAG_SHORT_GI @@ expression status; @@ -status.flag & RX_FLAG_SHORT_GI +status.enc_flags & RX_ENC_FLAG_SHORT_GI @@ assignment operator op; expression status; @@ -status.flag op RX_FLAG_HT_GF +status.enc_flags op RX_ENC_FLAG_HT_GF @@ expression status; @@ -status.flag & RX_FLAG_HT_GF +status.enc_flags & RX_ENC_FLAG_HT_GF @@ assignment operator op; expression status; @@ -status.flag op RX_FLAG_VHT +status.enc_flags op RX_ENC_FLAG_VHT @@ expression status; @@ -status.flag & RX_FLAG_VHT +status.enc_flags & RX_ENC_FLAG_VHT @@ assignment operator op; expression status; @@ -status.flag op RX_FLAG_STBC_MASK +status.enc_flags op RX_ENC_FLAG_STBC_MASK @@ expression status; @@ -status.flag & RX_FLAG_STBC_MASK +status.enc_flags & RX_ENC_FLAG_STBC_MASK @@ assignment operator op; expression status; @@ -status.flag op RX_FLAG_LDPC +status.enc_flags op RX_ENC_FLAG_LDPC @@ expression status; @@ -status.flag & RX_FLAG_LDPC +status.enc_flags & RX_ENC_FLAG_LDPC @@ assignment operator op; expression status; @@ -status.flag op RX_FLAG_10MHZ +status.enc_flags op RX_ENC_FLAG_10MHZ @@ expression status; @@ -status.flag & RX_FLAG_10MHZ +status.enc_flags & RX_ENC_FLAG_10MHZ @@ assignment operator op; expression status; @@ -status.flag op RX_FLAG_5MHZ +status.enc_flags op RX_ENC_FLAG_5MHZ @@ expression status; @@ -status.flag & RX_FLAG_5MHZ +status.enc_flags & RX_ENC_FLAG_5MHZ @@ assignment operator op; expression status; @@ -status.vht_flag op RX_VHT_FLAG_80MHZ +status.enc_flags op RX_ENC_FLAG_80MHZ @@ expression status; @@ -status.vht_flag & RX_VHT_FLAG_80MHZ +status.enc_flags & RX_ENC_FLAG_80MHZ @@ assignment operator op; expression status; @@ -status.vht_flag op RX_VHT_FLAG_160MHZ +status.enc_flags op RX_ENC_FLAG_160MHZ @@ expression status; @@ -status.vht_flag & RX_VHT_FLAG_160MHZ +status.enc_flags & RX_ENC_FLAG_160MHZ @@ assignment operator op; expression status; @@ -status.vht_flag op RX_VHT_FLAG_BF +status.enc_flags op RX_ENC_FLAG_BF @@ expression status; @@ -status.vht_flag & RX_VHT_FLAG_BF +status.enc_flags & RX_ENC_FLAG_BF @@ assignment operator op; expression status, STBC; @@ -status.flag op STBC << RX_FLAG_STBC_SHIFT +status.enc_flags op STBC << RX_ENC_FLAG_STBC_SHIFT @@ @@ -RX_FLAG_STBC_SHIFT +RX_ENC_FLAG_STBC_SHIFT Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath10k/htt_rx.c | 46 ++++----- drivers/net/wireless/ath/ath5k/base.c | 6 +- drivers/net/wireless/ath/ath9k/ar9003_mac.c | 6 +- drivers/net/wireless/ath/ath9k/common.c | 9 +- drivers/net/wireless/ath/ath9k/debug_sta.c | 6 +- drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 6 +- drivers/net/wireless/ath/ath9k/mac.c | 14 +-- drivers/net/wireless/ath/ath9k/mac.h | 2 +- drivers/net/wireless/ath/ath9k/recv.c | 8 +- drivers/net/wireless/ath/carl9170/rx.c | 8 +- drivers/net/wireless/ath/wcn36xx/txrx.c | 2 +- drivers/net/wireless/broadcom/b43/xmit.c | 2 +- .../wireless/broadcom/brcm80211/brcmsmac/main.c | 10 +- drivers/net/wireless/intel/iwlegacy/3945.c | 2 +- drivers/net/wireless/intel/iwlegacy/4965-mac.c | 8 +- drivers/net/wireless/intel/iwlwifi/dvm/rx.c | 10 +- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 24 ++--- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 24 ++--- drivers/net/wireless/intersil/p54/txrx.c | 2 +- drivers/net/wireless/mac80211_hwsim.c | 8 +- drivers/net/wireless/marvell/mwl8k.c | 16 +-- drivers/net/wireless/mediatek/mt7601u/mac.c | 12 +-- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 4 +- drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 3 +- drivers/net/wireless/ralink/rt2x00/rt2x00queue.h | 1 + drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c | 2 +- drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c | 2 +- .../net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 10 +- .../net/wireless/realtek/rtlwifi/rtl8188ee/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8192ce/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8192cu/trx.c | 8 +- .../net/wireless/realtek/rtlwifi/rtl8192de/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8192ee/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8192se/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8723ae/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8723be/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8821ae/trx.c | 10 +- drivers/net/wireless/st/cw1200/txrx.c | 2 +- drivers/net/wireless/ti/wl1251/rx.c | 2 +- drivers/net/wireless/ti/wlcore/rx.c | 2 +- include/net/mac80211.h | 108 ++++++++++----------- net/mac80211/ibss.c | 4 +- net/mac80211/ieee80211_i.h | 2 +- net/mac80211/rx.c | 58 +++++------ net/mac80211/scan.c | 8 +- net/mac80211/sta_info.h | 16 +-- net/mac80211/util.c | 22 ++--- 47 files changed, 261 insertions(+), 262 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 3448a3ce5919..2c29f8d12bcf 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -632,11 +632,11 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, sgi = (info3 >> 7) & 1; status->rate_idx = mcs; - status->flag |= RX_FLAG_HT; + status->enc_flags |= RX_ENC_FLAG_HT; if (sgi) - status->flag |= RX_FLAG_SHORT_GI; + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (bw) - status->flag |= RX_FLAG_40MHZ; + status->enc_flags |= RX_ENC_FLAG_40MHZ; break; case HTT_RX_VHT: case HTT_RX_VHT_WITH_TXBF: @@ -692,7 +692,7 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, status->vht_nss = nss; if (sgi) - status->flag |= RX_FLAG_SHORT_GI; + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; switch (bw) { /* 20MHZ */ @@ -700,18 +700,18 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, break; /* 40MHZ */ case 1: - status->flag |= RX_FLAG_40MHZ; + status->enc_flags |= RX_ENC_FLAG_40MHZ; break; /* 80MHZ */ case 2: - status->vht_flag |= RX_VHT_FLAG_80MHZ; + status->enc_flags |= RX_ENC_FLAG_80MHZ; break; case 3: - status->vht_flag |= RX_VHT_FLAG_160MHZ; + status->enc_flags |= RX_ENC_FLAG_160MHZ; break; } - status->flag |= RX_FLAG_VHT; + status->enc_flags |= RX_ENC_FLAG_VHT; break; default: break; @@ -875,12 +875,12 @@ static void ath10k_htt_rx_h_ppdu(struct ath10k *ar, status->freq = 0; status->rate_idx = 0; status->vht_nss = 0; - status->vht_flag &= ~RX_VHT_FLAG_80MHZ; - status->flag &= ~(RX_FLAG_HT | - RX_FLAG_VHT | - RX_FLAG_SHORT_GI | - RX_FLAG_40MHZ | - RX_FLAG_MACTIME_END); + status->enc_flags &= ~(RX_ENC_FLAG_HT | + RX_ENC_FLAG_VHT | + RX_ENC_FLAG_SHORT_GI | + RX_ENC_FLAG_40MHZ | + RX_ENC_FLAG_80MHZ); + status->flag &= ~RX_FLAG_MACTIME_END; status->flag |= RX_FLAG_NO_SIGNAL_VAL; ath10k_htt_rx_h_signal(ar, status, rxd); @@ -933,7 +933,7 @@ static void ath10k_process_rx(struct ath10k *ar, *status = *rx_status; ath10k_dbg(ar, ATH10K_DBG_DATA, - "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%llx fcs-err %i mic-err %i amsdu-more %i\n", + "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", skb, skb->len, ieee80211_get_SA(hdr), @@ -941,14 +941,14 @@ static void ath10k_process_rx(struct ath10k *ar, is_multicast_ether_addr(ieee80211_get_DA(hdr)) ? "mcast" : "ucast", (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4, - (status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) == 0 ? - "legacy" : "", - status->flag & RX_FLAG_HT ? "ht" : "", - status->flag & RX_FLAG_VHT ? "vht" : "", - status->flag & RX_FLAG_40MHZ ? "40" : "", - status->vht_flag & RX_VHT_FLAG_80MHZ ? "80" : "", - status->vht_flag & RX_VHT_FLAG_160MHZ ? "160" : "", - status->flag & RX_FLAG_SHORT_GI ? "sgi " : "", + (status->enc_flags & (RX_ENC_FLAG_HT | RX_ENC_FLAG_VHT)) == 0 ? + "legacy" : "", + status->enc_flags & RX_ENC_FLAG_HT ? "ht" : "", + status->enc_flags & RX_ENC_FLAG_VHT ? "vht" : "", + status->enc_flags & RX_ENC_FLAG_40MHZ ? "40" : "", + status->enc_flags & RX_ENC_FLAG_80MHZ ? "80" : "", + status->enc_flags & RX_ENC_FLAG_160MHZ ? "160" : "", + status->enc_flags & RX_ENC_FLAG_SHORT_GI ? "sgi " : "", status->rate_idx, status->vht_nss, status->freq, diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 92ece64fd455..da43469f6b49 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1414,10 +1414,10 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, rxs->flag |= ath5k_rx_decrypted(ah, skb, rs); switch (ah->ah_bwmode) { case AR5K_BWMODE_5MHZ: - rxs->flag |= RX_FLAG_5MHZ; + rxs->enc_flags |= RX_ENC_FLAG_5MHZ; break; case AR5K_BWMODE_10MHZ: - rxs->flag |= RX_FLAG_10MHZ; + rxs->enc_flags |= RX_ENC_FLAG_10MHZ; break; default: break; @@ -1425,7 +1425,7 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, if (rs->rs_rate == ah->sbands[ah->curchan->band].bitrates[rxs->rate_idx].hw_value_short) - rxs->flag |= RX_FLAG_SHORTPRE; + rxs->enc_flags |= RX_ENC_FLAG_SHORTPRE; trace_ath5k_rx(ah, skb); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index cc5bb0a76baf..909854de42a1 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -494,7 +494,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_status = 0; rxs->rs_flags = 0; - rxs->flag = 0; + rxs->enc_flags = 0; rxs->rs_datalen = rxsp->status2 & AR_DataLen; rxs->rs_tstamp = rxsp->status3; @@ -520,8 +520,8 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_isaggr = (rxsp->status11 & AR_RxAggr) ? 1 : 0; rxs->rs_moreaggr = (rxsp->status11 & AR_RxMoreAggr) ? 1 : 0; rxs->rs_antenna = (MS(rxsp->status4, AR_RxAntenna) & 0x7); - rxs->flag |= (rxsp->status4 & AR_GI) ? RX_FLAG_SHORT_GI : 0; - rxs->flag |= (rxsp->status4 & AR_2040) ? RX_FLAG_40MHZ : 0; + rxs->enc_flags |= (rxsp->status4 & AR_GI) ? RX_ENC_FLAG_SHORT_GI : 0; + rxs->enc_flags |= (rxsp->status4 & AR_2040) ? RX_ENC_FLAG_40MHZ : 0; rxs->evm0 = rxsp->status6; rxs->evm1 = rxsp->status7; diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c index b80e08b13b74..9ed779b020d3 100644 --- a/drivers/net/wireless/ath/ath9k/common.c +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -181,14 +181,13 @@ int ath9k_cmn_process_rate(struct ath_common *common, sband = hw->wiphy->bands[band]; if (IS_CHAN_QUARTER_RATE(ah->curchan)) - rxs->flag |= RX_FLAG_5MHZ; + rxs->enc_flags |= RX_ENC_FLAG_5MHZ; else if (IS_CHAN_HALF_RATE(ah->curchan)) - rxs->flag |= RX_FLAG_10MHZ; + rxs->enc_flags |= RX_ENC_FLAG_10MHZ; if (rx_stats->rs_rate & 0x80) { /* HT rate */ - rxs->flag |= RX_FLAG_HT; - rxs->flag |= rx_stats->flag; + rxs->enc_flags |= RX_ENC_FLAG_HT | rx_stats->enc_flags; rxs->rate_idx = rx_stats->rs_rate & 0x7f; return 0; } @@ -199,7 +198,7 @@ int ath9k_cmn_process_rate(struct ath_common *common, return 0; } if (sband->bitrates[i].hw_value_short == rx_stats->rs_rate) { - rxs->flag |= RX_FLAG_SHORTPRE; + rxs->enc_flags |= RX_ENC_FLAG_SHORTPRE; rxs->rate_idx = i; return 0; } diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wireless/ath/ath9k/debug_sta.c index 524cbf13ca9c..f1174c9d94d4 100644 --- a/drivers/net/wireless/ath/ath9k/debug_sta.c +++ b/drivers/net/wireless/ath/ath9k/debug_sta.c @@ -116,12 +116,12 @@ void ath_debug_rate_stats(struct ath_softc *sc, if (rxs->rate_idx >= ARRAY_SIZE(rstats->ht_stats)) goto exit; - if (rxs->flag & RX_FLAG_40MHZ) + if (rxs->enc_flags & RX_ENC_FLAG_40MHZ) rstats->ht_stats[rxs->rate_idx].ht40_cnt++; else rstats->ht_stats[rxs->rate_idx].ht20_cnt++; - if (rxs->flag & RX_FLAG_SHORT_GI) + if (rxs->enc_flags & RX_ENC_FLAG_SHORT_GI) rstats->ht_stats[rxs->rate_idx].sgi_cnt++; else rstats->ht_stats[rxs->rate_idx].lgi_cnt++; @@ -130,7 +130,7 @@ void ath_debug_rate_stats(struct ath_softc *sc, } if (IS_CCK_RATE(rs->rs_rate)) { - if (rxs->flag & RX_FLAG_SHORTPRE) + if (rxs->enc_flags & RX_ENC_FLAG_SHORTPRE) rstats->cck_stats[rxs->rate_idx].cck_sp_cnt++; else rstats->cck_stats[rxs->rate_idx].cck_lp_cnt++; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index f333ef1e3e7b..9f94efd66049 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -929,11 +929,11 @@ void ath9k_host_rx_init(struct ath9k_htc_priv *priv) static inline void convert_htc_flag(struct ath_rx_status *rx_stats, struct ath_htc_rx_status *rxstatus) { - rx_stats->flag = 0; + rx_stats->enc_flags = 0; if (rxstatus->rs_flags & ATH9K_RX_2040) - rx_stats->flag |= RX_FLAG_40MHZ; + rx_stats->enc_flags |= RX_ENC_FLAG_40MHZ; if (rxstatus->rs_flags & ATH9K_RX_GI) - rx_stats->flag |= RX_FLAG_SHORT_GI; + rx_stats->enc_flags |= RX_ENC_FLAG_SHORT_GI; } static void rx_status_htc_to_ath(struct ath_rx_status *rx_stats, diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index d937c39b3a0b..c98a563fdd01 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -535,7 +535,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_status = 0; rs->rs_flags = 0; - rs->flag = 0; + rs->enc_flags = 0; rs->rs_datalen = ads.ds_rxstatus1 & AR_DataLen; rs->rs_tstamp = ads.AR_RcvTimestamp; @@ -577,15 +577,15 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna); /* directly mapped flags for ieee80211_rx_status */ - rs->flag |= - (ads.ds_rxstatus3 & AR_GI) ? RX_FLAG_SHORT_GI : 0; - rs->flag |= - (ads.ds_rxstatus3 & AR_2040) ? RX_FLAG_40MHZ : 0; + rs->enc_flags |= + (ads.ds_rxstatus3 & AR_GI) ? RX_ENC_FLAG_SHORT_GI : 0; + rs->enc_flags |= + (ads.ds_rxstatus3 & AR_2040) ? RX_ENC_FLAG_40MHZ : 0; if (AR_SREV_9280_20_OR_LATER(ah)) - rs->flag |= + rs->enc_flags |= (ads.ds_rxstatus3 & AR_STBC) ? /* we can only Nss=1 STBC */ - (1 << RX_FLAG_STBC_SHIFT) : 0; + (1 << RX_ENC_FLAG_STBC_SHIFT) : 0; if (ads.ds_rxstatus8 & AR_PreDelimCRCErr) rs->rs_flags |= ATH9K_RX_DELIM_CRC_PRE; diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 770fc11b41d1..6bdf83f4ca9a 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -143,7 +143,7 @@ struct ath_rx_status { u32 evm2; u32 evm3; u32 evm4; - u32 flag; /* see enum mac80211_rx_flags */ + u16 enc_flags; }; struct ath_htc_rx_status { diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index d79837fe333f..65fcc34196f9 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -1037,11 +1037,11 @@ static void ath_rx_count_airtime(struct ath_softc *sc, rxs = IEEE80211_SKB_RXCB(skb); - is_sgi = !!(rxs->flag & RX_FLAG_SHORT_GI); - is_40 = !!(rxs->flag & RX_FLAG_40MHZ); - is_sp = !!(rxs->flag & RX_FLAG_SHORTPRE); + is_sgi = !!(rxs->enc_flags & RX_ENC_FLAG_SHORT_GI); + is_40 = !!(rxs->enc_flags & RX_ENC_FLAG_40MHZ); + is_sp = !!(rxs->enc_flags & RX_ENC_FLAG_SHORTPRE); - if (!!(rxs->flag & RX_FLAG_HT)) { + if (!!(rxs->enc_flags & RX_ENC_FLAG_HT)) { /* MCS rates */ airtime += ath_pkt_duration(sc, rxs->rate_idx, len, diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 0c34c8729dc6..c2ffe78b3491 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -358,7 +358,7 @@ static int carl9170_rx_mac_status(struct ar9170 *ar, switch (mac->status & AR9170_RX_STATUS_MODULATION) { case AR9170_RX_STATUS_MODULATION_CCK: if (mac->status & AR9170_RX_STATUS_SHORT_PREAMBLE) - status->flag |= RX_FLAG_SHORTPRE; + status->enc_flags |= RX_ENC_FLAG_SHORTPRE; switch (head->plcp[0]) { case AR9170_RX_PHY_RATE_CCK_1M: status->rate_idx = 0; @@ -423,12 +423,12 @@ static int carl9170_rx_mac_status(struct ar9170 *ar, case AR9170_RX_STATUS_MODULATION_HT: if (head->plcp[3] & 0x80) - status->flag |= RX_FLAG_40MHZ; + status->enc_flags |= RX_ENC_FLAG_40MHZ; if (head->plcp[6] & 0x80) - status->flag |= RX_FLAG_SHORT_GI; + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; status->rate_idx = clamp(0, 75, head->plcp[3] & 0x7f); - status->flag |= RX_FLAG_HT; + status->enc_flags |= RX_ENC_FLAG_HT; break; default: diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c index 8c387a0a3c09..22304edc5948 100644 --- a/drivers/net/wireless/ath/wcn36xx/txrx.c +++ b/drivers/net/wireless/ath/wcn36xx/txrx.c @@ -68,7 +68,7 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) RX_FLAG_MMIC_STRIPPED | RX_FLAG_DECRYPTED; - wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%llx\n", status.flag); + wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x\n", status.flag); memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); diff --git a/drivers/net/wireless/broadcom/b43/xmit.c b/drivers/net/wireless/broadcom/b43/xmit.c index b068d5aeee24..1b9c191e2a22 100644 --- a/drivers/net/wireless/broadcom/b43/xmit.c +++ b/drivers/net/wireless/broadcom/b43/xmit.c @@ -694,7 +694,7 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) if (unlikely(phystat0 & (B43_RX_PHYST0_PLCPHCF | B43_RX_PHYST0_PLCPFV))) status.flag |= RX_FLAG_FAILED_PLCP_CRC; if (phystat0 & B43_RX_PHYST0_SHORTPRMBL) - status.flag |= RX_FLAG_SHORTPRE; + status.enc_flags |= RX_ENC_FLAG_SHORTPRE; if (macstat & B43_RX_MAC_DECERR) { /* Decryption with the given key failed. * Drop the packet. We also won't be able to decrypt it with diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c index c2a938b59044..95386a3757ea 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c @@ -7092,9 +7092,9 @@ prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, rspec = brcms_c_compute_rspec(rxh, plcp); if (is_mcs_rate(rspec)) { rx_status->rate_idx = rspec & RSPEC_RATE_MASK; - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; if (rspec_is40mhz(rspec)) - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; } else { switch (rspec2rate(rspec)) { case BRCM_RATE_1M: @@ -7149,9 +7149,9 @@ prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, /* Determine short preamble and rate_idx */ if (is_cck_rate(rspec)) { if (rxh->PhyRxStatus_0 & PRXS0_SHORTH) - rx_status->flag |= RX_FLAG_SHORTPRE; + rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; } else if (is_ofdm_rate(rspec)) { - rx_status->flag |= RX_FLAG_SHORTPRE; + rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; } else { brcms_err(wlc->hw->d11core, "%s: Unknown modulation\n", __func__); @@ -7159,7 +7159,7 @@ prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, } if (plcp3_issgi(plcp[3])) - rx_status->flag |= RX_FLAG_SHORT_GI; + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (rxh->RxStatus1 & RXS_DECERR) { rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC; diff --git a/drivers/net/wireless/intel/iwlegacy/3945.c b/drivers/net/wireless/intel/iwlegacy/3945.c index 4db327a95414..080ea8155b90 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945.c +++ b/drivers/net/wireless/intel/iwlegacy/3945.c @@ -570,7 +570,7 @@ il3945_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) /* set the preamble flag if appropriate */ if (rx_hdr->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) - rx_status.flag |= RX_FLAG_SHORTPRE; + rx_status.enc_flags |= RX_ENC_FLAG_SHORTPRE; if ((unlikely(rx_stats->phy_count > 20))) { D_DROP("dsp size out of range [0,20]: %d\n", diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index 7eda525e3f4f..34a554b6ac3e 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -728,15 +728,15 @@ il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) /* set the preamble flag if appropriate */ if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) - rx_status.flag |= RX_FLAG_SHORTPRE; + rx_status.enc_flags |= RX_ENC_FLAG_SHORTPRE; /* Set up the HT phy flags */ if (rate_n_flags & RATE_MCS_HT_MSK) - rx_status.flag |= RX_FLAG_HT; + rx_status.enc_flags |= RX_ENC_FLAG_HT; if (rate_n_flags & RATE_MCS_HT40_MSK) - rx_status.flag |= RX_FLAG_40MHZ; + rx_status.enc_flags |= RX_ENC_FLAG_40MHZ; if (rate_n_flags & RATE_MCS_SGI_MSK) - rx_status.flag |= RX_FLAG_SHORT_GI; + rx_status.enc_flags |= RX_ENC_FLAG_SHORT_GI; if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) { /* We know which subframes of an A-MPDU belong diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c index dfa2041cfdac..70e4fab5a0b8 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c @@ -873,7 +873,7 @@ static void iwlagn_rx_reply_rx(struct iwl_priv *priv, /* set the preamble flag if appropriate */ if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) - rx_status.flag |= RX_FLAG_SHORTPRE; + rx_status.enc_flags |= RX_ENC_FLAG_SHORTPRE; if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) { /* @@ -887,13 +887,13 @@ static void iwlagn_rx_reply_rx(struct iwl_priv *priv, /* Set up the HT phy flags */ if (rate_n_flags & RATE_MCS_HT_MSK) - rx_status.flag |= RX_FLAG_HT; + rx_status.enc_flags |= RX_ENC_FLAG_HT; if (rate_n_flags & RATE_MCS_HT40_MSK) - rx_status.flag |= RX_FLAG_40MHZ; + rx_status.enc_flags |= RX_ENC_FLAG_40MHZ; if (rate_n_flags & RATE_MCS_SGI_MSK) - rx_status.flag |= RX_FLAG_SHORT_GI; + rx_status.enc_flags |= RX_ENC_FLAG_SHORT_GI; if (rate_n_flags & RATE_MCS_GF_MSK) - rx_status.flag |= RX_FLAG_HT_GF; + rx_status.enc_flags |= RX_ENC_FLAG_HT_GF; iwlagn_pass_packet_to_mac80211(priv, header, len, ampdu_status, rxb, &rx_status); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index d4c0ca7ccb34..314a245e249e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -410,7 +410,7 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, /* set the preamble flag if appropriate */ if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE)) - rx_status->flag |= RX_FLAG_SHORTPRE; + rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { /* @@ -427,27 +427,27 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, case RATE_MCS_CHAN_WIDTH_20: break; case RATE_MCS_CHAN_WIDTH_40: - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; break; case RATE_MCS_CHAN_WIDTH_80: - rx_status->vht_flag |= RX_VHT_FLAG_80MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_80MHZ; break; case RATE_MCS_CHAN_WIDTH_160: - rx_status->vht_flag |= RX_VHT_FLAG_160MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_160MHZ; break; } if (rate_n_flags & RATE_MCS_SGI_MSK) - rx_status->flag |= RX_FLAG_SHORT_GI; + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (rate_n_flags & RATE_HT_MCS_GF_MSK) - rx_status->flag |= RX_FLAG_HT_GF; + rx_status->enc_flags |= RX_ENC_FLAG_HT_GF; if (rate_n_flags & RATE_MCS_LDPC_MSK) - rx_status->flag |= RX_FLAG_LDPC; + rx_status->enc_flags |= RX_ENC_FLAG_LDPC; if (rate_n_flags & RATE_MCS_HT_MSK) { u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> RATE_MCS_STBC_POS; - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; - rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; + rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; } else if (rate_n_flags & RATE_MCS_VHT_MSK) { u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >> RATE_MCS_STBC_POS; @@ -455,10 +455,10 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> RATE_VHT_MCS_NSS_POS) + 1; rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; - rx_status->flag |= RX_FLAG_VHT; - rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; + rx_status->enc_flags |= RX_ENC_FLAG_VHT; + rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; if (rate_n_flags & RATE_MCS_BF_MSK) - rx_status->vht_flag |= RX_VHT_FLAG_BF; + rx_status->enc_flags |= RX_ENC_FLAG_BF; } else { int rate = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, rx_status->band); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 24c4fbe139a3..09f21bba6f48 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -824,7 +824,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, } /* set the preamble flag if appropriate */ if (phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE) - rx_status->flag |= RX_FLAG_SHORTPRE; + rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; if (likely(!(phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) { rx_status->mactime = le64_to_cpu(desc->tsf_on_air_rise); @@ -958,27 +958,27 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, case RATE_MCS_CHAN_WIDTH_20: break; case RATE_MCS_CHAN_WIDTH_40: - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; break; case RATE_MCS_CHAN_WIDTH_80: - rx_status->vht_flag |= RX_VHT_FLAG_80MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_80MHZ; break; case RATE_MCS_CHAN_WIDTH_160: - rx_status->vht_flag |= RX_VHT_FLAG_160MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_160MHZ; break; } if (rate_n_flags & RATE_MCS_SGI_MSK) - rx_status->flag |= RX_FLAG_SHORT_GI; + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (rate_n_flags & RATE_HT_MCS_GF_MSK) - rx_status->flag |= RX_FLAG_HT_GF; + rx_status->enc_flags |= RX_ENC_FLAG_HT_GF; if (rate_n_flags & RATE_MCS_LDPC_MSK) - rx_status->flag |= RX_FLAG_LDPC; + rx_status->enc_flags |= RX_ENC_FLAG_LDPC; if (rate_n_flags & RATE_MCS_HT_MSK) { u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> RATE_MCS_STBC_POS; - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; - rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; + rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; } else if (rate_n_flags & RATE_MCS_VHT_MSK) { u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >> RATE_MCS_STBC_POS; @@ -986,10 +986,10 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> RATE_VHT_MCS_NSS_POS) + 1; rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; - rx_status->flag |= RX_FLAG_VHT; - rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT; + rx_status->enc_flags |= RX_ENC_FLAG_VHT; + rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; if (rate_n_flags & RATE_MCS_BF_MSK) - rx_status->vht_flag |= RX_VHT_FLAG_BF; + rx_status->enc_flags |= RX_ENC_FLAG_BF; } else { int rate = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, rx_status->band); diff --git a/drivers/net/wireless/intersil/p54/txrx.c b/drivers/net/wireless/intersil/p54/txrx.c index 1af7da0b386e..5e1c91a80c58 100644 --- a/drivers/net/wireless/intersil/p54/txrx.c +++ b/drivers/net/wireless/intersil/p54/txrx.c @@ -352,7 +352,7 @@ static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb) rx_status->signal = p54_rssi_to_dbm(priv, hdr->rssi); if (hdr->rate & 0x10) - rx_status->flag |= RX_FLAG_SHORTPRE; + rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; if (priv->hw->conf.chandef.chan->band == NL80211_BAND_5GHZ) rx_status->rate_idx = (rate < 4) ? 0 : rate - 4; else diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 0cab122669c8..f6e96a5ee2f0 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1194,16 +1194,16 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, ieee80211_rate_get_vht_mcs(&info->control.rates[0]); rx_status.vht_nss = ieee80211_rate_get_vht_nss(&info->control.rates[0]); - rx_status.flag |= RX_FLAG_VHT; + rx_status.enc_flags |= RX_ENC_FLAG_VHT; } else { rx_status.rate_idx = info->control.rates[0].idx; if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) - rx_status.flag |= RX_FLAG_HT; + rx_status.enc_flags |= RX_ENC_FLAG_HT; } if (info->control.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - rx_status.flag |= RX_FLAG_40MHZ; + rx_status.enc_flags |= RX_ENC_FLAG_40MHZ; if (info->control.rates[0].flags & IEEE80211_TX_RC_SHORT_GI) - rx_status.flag |= RX_FLAG_SHORT_GI; + rx_status.enc_flags |= RX_ENC_FLAG_SHORT_GI; /* TODO: simulate real signal strength (and optional packet loss) */ rx_status.signal = -50; if (info->control.vif) diff --git a/drivers/net/wireless/marvell/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c index c295a4c6e5cd..9f6ab746d9fc 100644 --- a/drivers/net/wireless/marvell/mwl8k.c +++ b/drivers/net/wireless/marvell/mwl8k.c @@ -994,9 +994,9 @@ mwl8k_rxd_ap_process(void *_rxd, struct ieee80211_rx_status *status, *noise = -rxd->noise_floor; if (rxd->rate & MWL8K_AP_RATE_INFO_MCS_FORMAT) { - status->flag |= RX_FLAG_HT; + status->enc_flags |= RX_ENC_FLAG_HT; if (rxd->rate & MWL8K_AP_RATE_INFO_40MHZ) - status->flag |= RX_FLAG_40MHZ; + status->enc_flags |= RX_ENC_FLAG_40MHZ; status->rate_idx = MWL8K_AP_RATE_INFO_RATEID(rxd->rate); } else { int i; @@ -1011,7 +1011,7 @@ mwl8k_rxd_ap_process(void *_rxd, struct ieee80211_rx_status *status, if (rxd->channel > 14) { status->band = NL80211_BAND_5GHZ; - if (!(status->flag & RX_FLAG_HT)) + if (!(status->enc_flags & RX_ENC_FLAG_HT)) status->rate_idx -= 5; } else { status->band = NL80211_BAND_2GHZ; @@ -1109,17 +1109,17 @@ mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status, status->rate_idx = MWL8K_STA_RATE_INFO_RATEID(rate_info); if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE) - status->flag |= RX_FLAG_SHORTPRE; + status->enc_flags |= RX_ENC_FLAG_SHORTPRE; if (rate_info & MWL8K_STA_RATE_INFO_40MHZ) - status->flag |= RX_FLAG_40MHZ; + status->enc_flags |= RX_ENC_FLAG_40MHZ; if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI) - status->flag |= RX_FLAG_SHORT_GI; + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT) - status->flag |= RX_FLAG_HT; + status->enc_flags |= RX_ENC_FLAG_HT; if (rxd->channel > 14) { status->band = NL80211_BAND_5GHZ; - if (!(status->flag & RX_FLAG_HT)) + if (!(status->enc_flags & RX_ENC_FLAG_HT)) status->rate_idx -= 5; } else { status->band = NL80211_BAND_2GHZ; diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.c b/drivers/net/wireless/mediatek/mt7601u/mac.c index 3c576392ed89..064cad0f0938 100644 --- a/drivers/net/wireless/mediatek/mt7601u/mac.c +++ b/drivers/net/wireless/mediatek/mt7601u/mac.c @@ -401,7 +401,7 @@ mt76_mac_process_rate(struct ieee80211_rx_status *status, u16 rate) case MT_PHY_TYPE_CCK: if (idx >= 8) { idx -= 8; - status->flag |= RX_FLAG_SHORTPRE; + status->enc_flags |= RX_ENC_FLAG_SHORTPRE; } if (WARN_ON(idx >= 4)) @@ -410,10 +410,10 @@ mt76_mac_process_rate(struct ieee80211_rx_status *status, u16 rate) status->rate_idx = idx; return; case MT_PHY_TYPE_HT_GF: - status->flag |= RX_FLAG_HT_GF; + status->enc_flags |= RX_ENC_FLAG_HT_GF; /* fall through */ case MT_PHY_TYPE_HT: - status->flag |= RX_FLAG_HT; + status->enc_flags |= RX_ENC_FLAG_HT; status->rate_idx = idx; break; default: @@ -422,13 +422,13 @@ mt76_mac_process_rate(struct ieee80211_rx_status *status, u16 rate) } if (rate & MT_RXWI_RATE_SGI) - status->flag |= RX_FLAG_SHORT_GI; + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (rate & MT_RXWI_RATE_STBC) - status->flag |= 1 << RX_FLAG_STBC_SHIFT; + status->enc_flags |= 1 << RX_ENC_FLAG_STBC_SHIFT; if (rate & MT_RXWI_RATE_BW) - status->flag |= RX_FLAG_40MHZ; + status->enc_flags |= RX_ENC_FLAG_40MHZ; } static void diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 8585cdc3de53..03d415c553d1 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -889,10 +889,10 @@ void rt2800_process_rxwi(struct queue_entry *entry, rt2x00_desc_read(rxwi, 1, &word); if (rt2x00_get_field32(word, RXWI_W1_SHORT_GI)) - rxdesc->flags |= RX_FLAG_SHORT_GI; + rxdesc->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (rt2x00_get_field32(word, RXWI_W1_BW)) - rxdesc->flags |= RX_FLAG_40MHZ; + rxdesc->enc_flags |= RX_ENC_FLAG_40MHZ; /* * Detect RX rate, always use MCS as signal type. diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index 5f9fa97b6088..8a4640d2ead0 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -825,7 +825,7 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp) rate_idx = rt2x00lib_rxdone_read_signal(rt2x00dev, &rxdesc); if (rxdesc.rate_mode == RATE_MODE_HT_MIX || rxdesc.rate_mode == RATE_MODE_HT_GREENFIELD) - rxdesc.flags |= RX_FLAG_HT; + rxdesc.enc_flags |= RX_ENC_FLAG_HT; /* * Check if this is a beacon, and more frames have been @@ -865,6 +865,7 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp) rx_status->rate_idx = rate_idx; rx_status->signal = rxdesc.rssi; rx_status->flag = rxdesc.flags; + rx_status->enc_flags = rxdesc.enc_flags; rx_status->antenna = rt2x00dev->link.ant.active.rx; ieee80211_rx_ni(rt2x00dev->hw, entry->skb); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h index c78fb8c8838a..a4242e8e202e 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h @@ -184,6 +184,7 @@ struct rxdone_entry_desc { int flags; int dev_flags; u16 rate_mode; + u16 enc_flags; u8 cipher; u8 cipher_status; diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c index e387dec82d3d..225c1c8851cc 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c @@ -315,7 +315,7 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) rx_status.mactime = tsft; rx_status.flag |= RX_FLAG_MACTIME_START; if (flags & RTL818X_RX_DESC_FLAG_SPLCP) - rx_status.flag |= RX_FLAG_SHORTPRE; + rx_status.enc_flags |= RX_ENC_FLAG_SHORTPRE; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c index 7dd18896d35a..35fe991dcc56 100644 --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c @@ -389,7 +389,7 @@ static void rtl8187_rx_cb(struct urb *urb) rx_status.band = dev->conf.chandef.chan->band; rx_status.flag |= RX_FLAG_MACTIME_START; if (flags & RTL818X_RX_DESC_FLAG_SPLCP) - rx_status.flag |= RX_FLAG_SHORTPRE; + rx_status.enc_flags |= RX_ENC_FLAG_SHORTPRE; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 9b4a9a00be64..4aa370aac9f6 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -5041,7 +5041,7 @@ static void rtl8xxxu_rx_parse_phystats(struct rtl8xxxu_priv *priv, u32 rxmcs) { if (phy_stats->sgi_en) - rx_status->flag |= RX_FLAG_SHORT_GI; + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (rxmcs < DESC_RATE_6M) { /* @@ -5267,10 +5267,10 @@ int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb) if (rx_desc->crc32) rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (rx_desc->bw) - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; if (rx_desc->rxht) { - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0; } else { rx_status->rate_idx = rx_desc->rxmcs; @@ -5337,10 +5337,10 @@ int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb) if (rx_desc->crc32) rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (rx_desc->bw) - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; if (rx_desc->rxmcs >= DESC_RATE_MCS0) { - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0; } else { rx_status->rate_idx = rx_desc->rxmcs; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c index 09c908d4cf91..c5145c20200a 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c @@ -444,10 +444,10 @@ bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (status->rx_is40Mhzpacket) - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; if (status->is_ht) - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; rx_status->flag |= RX_FLAG_MACTIME_START; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c index 3616ba21959d..16f6c2eb6314 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c @@ -369,10 +369,10 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (stats->rx_is40Mhzpacket) - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; if (stats->is_ht) - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; rx_status->flag |= RX_FLAG_MACTIME_START; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c index 1611e42479d9..d80d2838726a 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c @@ -329,9 +329,9 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, if (!GET_RX_DESC_SWDEC(pdesc)) rx_status->flag |= RX_FLAG_DECRYPTED; if (GET_RX_DESC_BW(pdesc)) - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; if (GET_RX_DESC_RX_HT(pdesc)) - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; @@ -398,9 +398,9 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb) if (!GET_RX_DESC_SWDEC(rxdesc)) rx_status->flag |= RX_FLAG_DECRYPTED; if (GET_RX_DESC_BW(rxdesc)) - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; if (GET_RX_DESC_RX_HT(rxdesc)) - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; /* Data rate */ rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats.is_ht, false, stats.rate); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c index 5c9c8741134f..01d7308cdc9f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c @@ -503,9 +503,9 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, if (!GET_RX_DESC_SWDEC(pdesc)) rx_status->flag |= RX_FLAG_DECRYPTED; if (GET_RX_DESC_BW(pdesc)) - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; if (GET_RX_DESC_RXHT(pdesc)) - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c index 07440e9a8ca2..38ffee1f8838 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c @@ -394,10 +394,10 @@ bool rtl92ee_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (status->rx_is40Mhzpacket) - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; if (status->is_ht) - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; rx_status->flag |= RX_FLAG_MACTIME_START; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c index 12cef01e593b..12a4a8faedec 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c @@ -289,10 +289,10 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (stats->rx_is40Mhzpacket) - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; if (stats->is_ht) - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; rx_status->flag |= RX_FLAG_MACTIME_START; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c index c9838f52a7ea..b8d0bcae4a71 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c @@ -317,10 +317,10 @@ bool rtl8723e_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (status->rx_is40Mhzpacket) - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; if (status->is_ht) - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; rx_status->flag |= RX_FLAG_MACTIME_START; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c index 6f65003a895a..8d35a58bae93 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c @@ -373,10 +373,10 @@ bool rtl8723be_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (status->rx_is40Mhzpacket) - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; if (status->is_ht) - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; rx_status->flag |= RX_FLAG_MACTIME_START; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c index 108098152cf3..c4a35bfd4f3e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c @@ -520,16 +520,16 @@ bool rtl8821ae_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (status->rx_packet_bw == HT_CHANNEL_WIDTH_20_40) - rx_status->flag |= RX_FLAG_40MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; else if (status->rx_packet_bw == HT_CHANNEL_WIDTH_80) - rx_status->vht_flag |= RX_VHT_FLAG_80MHZ; + rx_status->enc_flags |= RX_ENC_FLAG_80MHZ; if (status->is_ht) - rx_status->flag |= RX_FLAG_HT; + rx_status->enc_flags |= RX_ENC_FLAG_HT; if (status->is_vht) - rx_status->flag |= RX_FLAG_VHT; + rx_status->enc_flags |= RX_ENC_FLAG_VHT; if (status->is_short_gi) - rx_status->flag |= RX_FLAG_SHORT_GI; + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; rx_status->vht_nss = status->vht_nss; rx_status->flag |= RX_FLAG_MACTIME_START; diff --git a/drivers/net/wireless/st/cw1200/txrx.c b/drivers/net/wireless/st/cw1200/txrx.c index 3d170287cd0b..e8b69dbdfe39 100644 --- a/drivers/net/wireless/st/cw1200/txrx.c +++ b/drivers/net/wireless/st/cw1200/txrx.c @@ -1085,7 +1085,7 @@ void cw1200_rx_cb(struct cw1200_common *priv, hdr->band); if (arg->rx_rate >= 14) { - hdr->flag |= RX_FLAG_HT; + hdr->enc_flags |= RX_ENC_FLAG_HT; hdr->rate_idx = arg->rx_rate - 14; } else if (arg->rx_rate >= 4) { hdr->rate_idx = arg->rx_rate - 2; diff --git a/drivers/net/wireless/ti/wl1251/rx.c b/drivers/net/wireless/ti/wl1251/rx.c index a27d4c22b6e8..50fb2a4a5259 100644 --- a/drivers/net/wireless/ti/wl1251/rx.c +++ b/drivers/net/wireless/ti/wl1251/rx.c @@ -141,7 +141,7 @@ static void wl1251_rx_status(struct wl1251 *wl, } if (desc->mod_pre & SHORT_PREAMBLE_BIT) - status->flag |= RX_FLAG_SHORTPRE; + status->enc_flags |= RX_ENC_FLAG_SHORTPRE; } static void wl1251_rx_body(struct wl1251 *wl, diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index b9e14045195f..5552c763dad5 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -72,7 +72,7 @@ static void wl1271_rx_status(struct wl1271 *wl, /* 11n support */ if (desc->rate <= wl->hw_min_ht_rate) - status->flag |= RX_FLAG_HT; + status->enc_flags |= RX_ENC_FLAG_HT; /* * Read the signal level and antenna diversity indication. diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b1ac872dc88a..53b3853d6fd1 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1045,16 +1045,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * (including FCS) was received. * @RX_FLAG_MACTIME_PLCP_START: The timestamp passed in the RX status (@mactime * field) is valid and contains the time the SYNC preamble was received. - * @RX_FLAG_SHORTPRE: Short preamble was used for this frame - * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index - * @RX_FLAG_VHT: VHT MCS was used and rate_index is MCS index - * @RX_FLAG_40MHZ: HT40 (40 MHz) was used - * @RX_FLAG_SHORT_GI: Short guard interval was used * @RX_FLAG_NO_SIGNAL_VAL: The signal strength value is not present. * Valid only for data frames (mainly A-MPDU) - * @RX_FLAG_HT_GF: This frame was received in a HT-greenfield transmission, if - * the driver fills this value it should add %IEEE80211_RADIOTAP_MCS_HAVE_FMT - * to hw.radiotap_mcs_details to advertise that fact * @RX_FLAG_AMPDU_DETAILS: A-MPDU details are known, in particular the reference * number (@ampdu_reference) must be populated and be a distinct number for * each A-MPDU @@ -1067,7 +1059,6 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * is stored in the @ampdu_delimiter_crc field) * @RX_FLAG_MIC_STRIPPED: The mic was stripped of this packet. Decryption was * done by the hardware - * @RX_FLAG_LDPC: LDPC was used * @RX_FLAG_ONLY_MONITOR: Report frame only to monitor interfaces without * processing it in any regular way. * This is useful if drivers offload some frames but still want to report @@ -1076,9 +1067,6 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * monitor interfaces. * This is useful if drivers offload some frames but still want to report * them for sniffing purposes. - * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3 - * @RX_FLAG_10MHZ: 10 MHz (half channel) was used - * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used * @RX_FLAG_AMSDU_MORE: Some drivers may prefer to report separate A-MSDU * subframes instead of a one huge frame for performance reasons. * All, but the last MSDU from an A-MSDU should have this flag set. E.g. @@ -1106,52 +1094,62 @@ enum mac80211_rx_flags { RX_FLAG_FAILED_FCS_CRC = BIT(5), RX_FLAG_FAILED_PLCP_CRC = BIT(6), RX_FLAG_MACTIME_START = BIT(7), - RX_FLAG_SHORTPRE = BIT(8), - RX_FLAG_HT = BIT(9), - RX_FLAG_40MHZ = BIT(10), - RX_FLAG_SHORT_GI = BIT(11), - RX_FLAG_NO_SIGNAL_VAL = BIT(12), - RX_FLAG_HT_GF = BIT(13), - RX_FLAG_AMPDU_DETAILS = BIT(14), - RX_FLAG_PN_VALIDATED = BIT(15), - RX_FLAG_DUP_VALIDATED = BIT(16), - RX_FLAG_AMPDU_LAST_KNOWN = BIT(17), - RX_FLAG_AMPDU_IS_LAST = BIT(18), - RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(19), - RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(20), - RX_FLAG_MACTIME_END = BIT(21), - RX_FLAG_VHT = BIT(22), - RX_FLAG_LDPC = BIT(23), - RX_FLAG_ONLY_MONITOR = BIT(24), - RX_FLAG_SKIP_MONITOR = BIT(25), - RX_FLAG_STBC_MASK = BIT(26) | BIT(27), - RX_FLAG_10MHZ = BIT(28), - RX_FLAG_5MHZ = BIT(29), - RX_FLAG_AMSDU_MORE = BIT(30), - RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(31), - RX_FLAG_MIC_STRIPPED = BIT_ULL(32), - RX_FLAG_ALLOW_SAME_PN = BIT_ULL(33), - RX_FLAG_ICV_STRIPPED = BIT_ULL(34), + RX_FLAG_NO_SIGNAL_VAL = BIT(8), + RX_FLAG_AMPDU_DETAILS = BIT(9), + RX_FLAG_PN_VALIDATED = BIT(10), + RX_FLAG_DUP_VALIDATED = BIT(11), + RX_FLAG_AMPDU_LAST_KNOWN = BIT(12), + RX_FLAG_AMPDU_IS_LAST = BIT(13), + RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(14), + RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(15), + RX_FLAG_MACTIME_END = BIT(16), + RX_FLAG_ONLY_MONITOR = BIT(17), + RX_FLAG_SKIP_MONITOR = BIT(18), + RX_FLAG_AMSDU_MORE = BIT(19), + RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(20), + RX_FLAG_MIC_STRIPPED = BIT(21), + RX_FLAG_ALLOW_SAME_PN = BIT(22), + RX_FLAG_ICV_STRIPPED = BIT(23), }; -#define RX_FLAG_STBC_SHIFT 26 - /** - * enum mac80211_rx_vht_flags - receive VHT flags + * enum mac80211_rx_encoding_flags - MCS & bandwidth flags * - * These flags are used with the @vht_flag member of - * &struct ieee80211_rx_status. - * @RX_VHT_FLAG_80MHZ: 80 MHz was used - * @RX_VHT_FLAG_160MHZ: 160 MHz was used - * @RX_VHT_FLAG_BF: packet was beamformed - */ - -enum mac80211_rx_vht_flags { - RX_VHT_FLAG_80MHZ = BIT(0), - RX_VHT_FLAG_160MHZ = BIT(1), - RX_VHT_FLAG_BF = BIT(2), + * @RX_ENC_FLAG_SHORTPRE: Short preamble was used for this frame + * @RX_ENC_FLAG_HT: HT MCS was used and rate_idx is MCS index + * @RX_ENC_FLAG_VHT: VHT MCS was used and rate_index is MCS index + * @RX_ENC_FLAG_40MHZ: HT40 (40 MHz) was used + * @RX_ENC_FLAG_SHORT_GI: Short guard interval was used + * @RX_ENC_FLAG_HT_GF: This frame was received in a HT-greenfield transmission, + * if the driver fills this value it should add + * %IEEE80211_RADIOTAP_MCS_HAVE_FMT + * to hw.radiotap_mcs_details to advertise that fact + * @RX_ENC_FLAG_LDPC: LDPC was used + * @RX_ENC_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3 + * @RX_ENC_FLAG_10MHZ: 10 MHz (half channel) was used + * @RX_ENC_FLAG_5MHZ: 5 MHz (quarter channel) was used + * @RX_ENC_FLAG_80MHZ: 80 MHz was used + * @RX_ENC_FLAG_160MHZ: 160 MHz was used + * @RX_ENC_FLAG_BF: packet was beamformed + */ +enum mac80211_rx_encoding_flags { + RX_ENC_FLAG_SHORTPRE = BIT(0), + RX_ENC_FLAG_HT = BIT(1), + RX_ENC_FLAG_40MHZ = BIT(2), + RX_ENC_FLAG_SHORT_GI = BIT(3), + RX_ENC_FLAG_HT_GF = BIT(4), + RX_ENC_FLAG_VHT = BIT(5), + RX_ENC_FLAG_STBC_MASK = BIT(6) | BIT(7), + RX_ENC_FLAG_LDPC = BIT(8), + RX_ENC_FLAG_10MHZ = BIT(9), + RX_ENC_FLAG_5MHZ = BIT(10), + RX_ENC_FLAG_80MHZ = BIT(11), + RX_ENC_FLAG_160MHZ = BIT(12), + RX_ENC_FLAG_BF = BIT(13), }; +#define RX_ENC_FLAG_STBC_SHIFT 6 + /** * struct ieee80211_rx_status - receive status * @@ -1181,7 +1179,7 @@ enum mac80211_rx_vht_flags { * HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT) * @vht_nss: number of streams (VHT only) * @flag: %RX_FLAG_\* - * @vht_flag: %RX_VHT_FLAG_\* + * @enc_flags: uses bits from &enum mac80211_rx_encoding_flags * @rx_flags: internal RX flags for mac80211 * @ampdu_reference: A-MPDU reference number, must be a different value for * each A-MPDU but the same for each subframe within one A-MPDU @@ -1192,9 +1190,9 @@ struct ieee80211_rx_status { u64 boottime_ns; u32 device_timestamp; u32 ampdu_reference; - u64 flag; + u32 flag; + u16 enc_flags; u16 freq; - u8 vht_flag; u8 rate_idx; u8 vht_nss; u8 rx_flags; diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index e957351976a2..6d9a80982d02 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1014,9 +1014,9 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, prev_rates = sta->sta.supp_rates[band]; /* make sure mandatory rates are always added */ scan_width = NL80211_BSS_CHAN_WIDTH_20; - if (rx_status->flag & RX_FLAG_5MHZ) + if (rx_status->enc_flags & RX_ENC_FLAG_5MHZ) scan_width = NL80211_BSS_CHAN_WIDTH_5; - if (rx_status->flag & RX_FLAG_10MHZ) + if (rx_status->enc_flags & RX_ENC_FLAG_10MHZ) scan_width = NL80211_BSS_CHAN_WIDTH_10; sta->sta.supp_rates[band] = supp_rates | diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index cf6d5abb65a3..a4ba849ac074 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1532,7 +1532,7 @@ ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status) return true; /* can't handle HT/VHT preamble yet */ if (status->flag & RX_FLAG_MACTIME_PLCP_START && - !(status->flag & (RX_FLAG_HT | RX_FLAG_VHT))) + !(status->enc_flags & (RX_ENC_FLAG_HT | RX_ENC_FLAG_VHT))) return true; return false; } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index fe6a760aa1ee..3f7ecde323d7 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -156,7 +156,7 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, /* padding for RX_FLAGS if necessary */ len = ALIGN(len, 2); - if (status->flag & RX_FLAG_HT) /* HT info */ + if (status->enc_flags & RX_ENC_FLAG_HT) /* HT info */ len += 3; if (status->flag & RX_FLAG_AMPDU_DETAILS) { @@ -164,7 +164,7 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, len += 8; } - if (status->flag & RX_FLAG_VHT) { + if (status->enc_flags & RX_ENC_FLAG_VHT) { len = ALIGN(len, 2); len += 12; } @@ -329,12 +329,12 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos |= IEEE80211_RADIOTAP_F_FCS; if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) *pos |= IEEE80211_RADIOTAP_F_BADFCS; - if (status->flag & RX_FLAG_SHORTPRE) + if (status->enc_flags & RX_ENC_FLAG_SHORTPRE) *pos |= IEEE80211_RADIOTAP_F_SHORTPRE; pos++; /* IEEE80211_RADIOTAP_RATE */ - if (!rate || status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) { + if (!rate || status->enc_flags & (RX_ENC_FLAG_HT | RX_ENC_FLAG_VHT)) { /* * Without rate information don't add it. If we have, * MCS information is a separate field in radiotap, @@ -345,9 +345,9 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, } else { int shift = 0; rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); - if (status->flag & RX_FLAG_10MHZ) + if (status->enc_flags & RX_ENC_FLAG_10MHZ) shift = 1; - else if (status->flag & RX_FLAG_5MHZ) + else if (status->enc_flags & RX_ENC_FLAG_5MHZ) shift = 2; *pos = DIV_ROUND_UP(rate->bitrate, 5 * (1 << shift)); } @@ -356,14 +356,14 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* IEEE80211_RADIOTAP_CHANNEL */ put_unaligned_le16(status->freq, pos); pos += 2; - if (status->flag & RX_FLAG_10MHZ) + if (status->enc_flags & RX_ENC_FLAG_10MHZ) channel_flags |= IEEE80211_CHAN_HALF; - else if (status->flag & RX_FLAG_5MHZ) + else if (status->enc_flags & RX_ENC_FLAG_5MHZ) channel_flags |= IEEE80211_CHAN_QUARTER; if (status->band == NL80211_BAND_5GHZ) channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ; - else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) + else if (status->enc_flags & (RX_ENC_FLAG_HT | RX_ENC_FLAG_VHT)) channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; else if (rate && rate->flags & IEEE80211_RATE_ERP_G) channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ; @@ -402,21 +402,21 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, put_unaligned_le16(rx_flags, pos); pos += 2; - if (status->flag & RX_FLAG_HT) { + if (status->enc_flags & RX_ENC_FLAG_HT) { unsigned int stbc; rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); *pos++ = local->hw.radiotap_mcs_details; *pos = 0; - if (status->flag & RX_FLAG_SHORT_GI) + if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) *pos |= IEEE80211_RADIOTAP_MCS_SGI; - if (status->flag & RX_FLAG_40MHZ) + if (status->enc_flags & RX_ENC_FLAG_40MHZ) *pos |= IEEE80211_RADIOTAP_MCS_BW_40; - if (status->flag & RX_FLAG_HT_GF) + if (status->enc_flags & RX_ENC_FLAG_HT_GF) *pos |= IEEE80211_RADIOTAP_MCS_FMT_GF; - if (status->flag & RX_FLAG_LDPC) + if (status->enc_flags & RX_ENC_FLAG_LDPC) *pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC; - stbc = (status->flag & RX_FLAG_STBC_MASK) >> RX_FLAG_STBC_SHIFT; + stbc = (status->enc_flags & RX_ENC_FLAG_STBC_MASK) >> RX_ENC_FLAG_STBC_SHIFT; *pos |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT; pos++; *pos++ = status->rate_idx; @@ -449,27 +449,27 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos++ = 0; } - if (status->flag & RX_FLAG_VHT) { + if (status->enc_flags & RX_ENC_FLAG_VHT) { u16 known = local->hw.radiotap_vht_details; rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT); put_unaligned_le16(known, pos); pos += 2; /* flags */ - if (status->flag & RX_FLAG_SHORT_GI) + if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; /* in VHT, STBC is binary */ - if (status->flag & RX_FLAG_STBC_MASK) + if (status->enc_flags & RX_ENC_FLAG_STBC_MASK) *pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC; - if (status->vht_flag & RX_VHT_FLAG_BF) + if (status->enc_flags & RX_ENC_FLAG_BF) *pos |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED; pos++; /* bandwidth */ - if (status->vht_flag & RX_VHT_FLAG_80MHZ) + if (status->enc_flags & RX_ENC_FLAG_80MHZ) *pos++ = 4; - else if (status->vht_flag & RX_VHT_FLAG_160MHZ) + else if (status->enc_flags & RX_ENC_FLAG_160MHZ) *pos++ = 11; - else if (status->flag & RX_FLAG_40MHZ) + else if (status->enc_flags & RX_ENC_FLAG_40MHZ) *pos++ = 1; else /* 20 MHz */ *pos++ = 0; @@ -477,7 +477,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos = (status->rate_idx << 4) | status->vht_nss; pos += 4; /* coding field */ - if (status->flag & RX_FLAG_LDPC) + if (status->enc_flags & RX_ENC_FLAG_LDPC) *pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0; pos++; /* group ID */ @@ -3336,8 +3336,8 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx, status = IEEE80211_SKB_RXCB((rx->skb)); sband = rx->local->hw.wiphy->bands[status->band]; - if (!(status->flag & RX_FLAG_HT) && - !(status->flag & RX_FLAG_VHT)) + if (!(status->enc_flags & RX_ENC_FLAG_HT) && + !(status->enc_flags & RX_ENC_FLAG_VHT)) rate = &sband->bitrates[status->rate_idx]; ieee80211_rx_cooked_monitor(rx, rate); @@ -3598,7 +3598,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) return false; if (!rx->sta) { int rate_idx; - if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) + if (status->enc_flags & (RX_ENC_FLAG_HT | RX_ENC_FLAG_VHT)) rate_idx = 0; /* TODO: HT/VHT rates */ else rate_idx = status->rate_idx; @@ -3618,7 +3618,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) return false; if (!rx->sta) { int rate_idx; - if (status->flag & RX_FLAG_HT) + if (status->enc_flags & RX_ENC_FLAG_HT) rate_idx = 0; /* TODO: HT rates */ else rate_idx = status->rate_idx; @@ -4281,7 +4281,7 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, * we probably can't have a valid rate here anyway. */ - if (status->flag & RX_FLAG_HT) { + if (status->enc_flags & RX_ENC_FLAG_HT) { /* * rate_idx is MCS index, which can be [0-76] * as documented on: @@ -4299,7 +4299,7 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, status->rate_idx, status->rate_idx)) goto drop; - } else if (status->flag & RX_FLAG_VHT) { + } else if (status->enc_flags & RX_ENC_FLAG_VHT) { if (WARN_ONCE(status->rate_idx > 9 || !status->vht_nss || status->vht_nss > 8, diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index faab3c490d2b..f693ca0e0858 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -79,9 +79,9 @@ ieee80211_bss_info_update(struct ieee80211_local *local, bss_meta.signal = (rx_status->signal * 100) / local->hw.max_signal; bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_20; - if (rx_status->flag & RX_FLAG_5MHZ) + if (rx_status->enc_flags & RX_ENC_FLAG_5MHZ) bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_5; - if (rx_status->flag & RX_FLAG_10MHZ) + if (rx_status->enc_flags & RX_ENC_FLAG_10MHZ) bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_10; bss_meta.chan = channel; @@ -174,8 +174,8 @@ ieee80211_bss_info_update(struct ieee80211_local *local, if (beacon) { struct ieee80211_supported_band *sband = local->hw.wiphy->bands[rx_status->band]; - if (!(rx_status->flag & RX_FLAG_HT) && - !(rx_status->flag & RX_FLAG_VHT)) + if (!(rx_status->enc_flags & RX_ENC_FLAG_HT) && + !(rx_status->enc_flags & RX_ENC_FLAG_VHT)) bss->beacon_rate = &sband->bitrates[rx_status->rate_idx]; } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 8949266d7bc3..22b8e0250fc6 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -740,25 +740,25 @@ static inline u16 sta_stats_encode_rate(struct ieee80211_rx_status *s) { u16 r = s->rate_idx; - if (s->vht_flag & RX_VHT_FLAG_80MHZ) + if (s->enc_flags & RX_ENC_FLAG_80MHZ) r |= RATE_INFO_BW_80 << STA_STATS_RATE_BW_SHIFT; - else if (s->vht_flag & RX_VHT_FLAG_160MHZ) + else if (s->enc_flags & RX_ENC_FLAG_160MHZ) r |= RATE_INFO_BW_160 << STA_STATS_RATE_BW_SHIFT; - else if (s->flag & RX_FLAG_40MHZ) + else if (s->enc_flags & RX_ENC_FLAG_40MHZ) r |= RATE_INFO_BW_40 << STA_STATS_RATE_BW_SHIFT; - else if (s->flag & RX_FLAG_10MHZ) + else if (s->enc_flags & RX_ENC_FLAG_10MHZ) r |= RATE_INFO_BW_10 << STA_STATS_RATE_BW_SHIFT; - else if (s->flag & RX_FLAG_5MHZ) + else if (s->enc_flags & RX_ENC_FLAG_5MHZ) r |= RATE_INFO_BW_5 << STA_STATS_RATE_BW_SHIFT; else r |= RATE_INFO_BW_20 << STA_STATS_RATE_BW_SHIFT; - if (s->flag & RX_FLAG_SHORT_GI) + if (s->enc_flags & RX_ENC_FLAG_SHORT_GI) r |= STA_STATS_RATE_SGI; - if (s->flag & RX_FLAG_VHT) + if (s->enc_flags & RX_ENC_FLAG_VHT) r |= STA_STATS_RATE_TYPE_VHT | (s->vht_nss << 4); - else if (s->flag & RX_FLAG_HT) + else if (s->enc_flags & RX_ENC_FLAG_HT) r |= STA_STATS_RATE_TYPE_HT; else r |= STA_STATS_RATE_TYPE_LEGACY | (s->band << 4); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 37dad3dd6bac..ca198d153d72 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2715,38 +2715,38 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, memset(&ri, 0, sizeof(ri)); /* Fill cfg80211 rate info */ - if (status->flag & RX_FLAG_HT) { + if (status->enc_flags & RX_ENC_FLAG_HT) { ri.mcs = status->rate_idx; ri.flags |= RATE_INFO_FLAGS_MCS; - if (status->flag & RX_FLAG_40MHZ) + if (status->enc_flags & RX_ENC_FLAG_40MHZ) ri.bw = RATE_INFO_BW_40; else ri.bw = RATE_INFO_BW_20; - if (status->flag & RX_FLAG_SHORT_GI) + if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) ri.flags |= RATE_INFO_FLAGS_SHORT_GI; - } else if (status->flag & RX_FLAG_VHT) { + } else if (status->enc_flags & RX_ENC_FLAG_VHT) { ri.flags |= RATE_INFO_FLAGS_VHT_MCS; ri.mcs = status->rate_idx; ri.nss = status->vht_nss; - if (status->flag & RX_FLAG_40MHZ) + if (status->enc_flags & RX_ENC_FLAG_40MHZ) ri.bw = RATE_INFO_BW_40; - else if (status->vht_flag & RX_VHT_FLAG_80MHZ) + else if (status->enc_flags & RX_ENC_FLAG_80MHZ) ri.bw = RATE_INFO_BW_80; - else if (status->vht_flag & RX_VHT_FLAG_160MHZ) + else if (status->enc_flags & RX_ENC_FLAG_160MHZ) ri.bw = RATE_INFO_BW_160; else ri.bw = RATE_INFO_BW_20; - if (status->flag & RX_FLAG_SHORT_GI) + if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) ri.flags |= RATE_INFO_FLAGS_SHORT_GI; } else { struct ieee80211_supported_band *sband; int shift = 0; int bitrate; - if (status->flag & RX_FLAG_10MHZ) { + if (status->enc_flags & RX_ENC_FLAG_10MHZ) { shift = 1; ri.bw = RATE_INFO_BW_10; - } else if (status->flag & RX_FLAG_5MHZ) { + } else if (status->enc_flags & RX_ENC_FLAG_5MHZ) { shift = 2; ri.bw = RATE_INFO_BW_5; } else { @@ -2762,7 +2762,7 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, if (status->band == NL80211_BAND_5GHZ) { ts += 20 << shift; mpdu_offset += 2; - } else if (status->flag & RX_FLAG_SHORTPRE) { + } else if (status->enc_flags & RX_ENC_FLAG_SHORTPRE) { ts += 96; } else { ts += 192; -- cgit From da6a4352e7c867f81d7336f6517e819b3cce06bf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 26 Apr 2017 12:14:59 +0200 Subject: mac80211: separate encoding/bandwidth from flags We currently use a lot of flags that are mutually incompatible, separate this out into actual encoding and bandwidth enum values. Much of this again done with spatch, with manual post-editing, mostly to add the switch statements and get rid of the conversions. @@ expression status; @@ -status->enc_flags |= RX_ENC_FLAG_80MHZ +status->bw = RATE_INFO_BW_80 @@ expression status; @@ -status->enc_flags |= RX_ENC_FLAG_40MHZ +status->bw = RATE_INFO_BW_40 @@ expression status; @@ -status->enc_flags |= RX_ENC_FLAG_20MHZ +status->bw = RATE_INFO_BW_20 @@ expression status; @@ -status->enc_flags |= RX_ENC_FLAG_160MHZ +status->bw = RATE_INFO_BW_160 @@ expression status; @@ -status->enc_flags |= RX_ENC_FLAG_5MHZ +status->bw = RATE_INFO_BW_5 @@ expression status; @@ -status->enc_flags |= RX_ENC_FLAG_10MHZ +status->bw = RATE_INFO_BW_10 @@ expression status; @@ -status->enc_flags |= RX_ENC_FLAG_VHT +status->encoding = RX_ENC_VHT @@ expression status; @@ -status->enc_flags |= RX_ENC_FLAG_HT +status->encoding = RX_ENC_HT @@ expression status; @@ -status.enc_flags |= RX_ENC_FLAG_VHT +status.encoding = RX_ENC_VHT @@ expression status; @@ -status.enc_flags |= RX_ENC_FLAG_HT +status.encoding = RX_ENC_HT @@ expression status; @@ -(status->enc_flags & RX_ENC_FLAG_HT) +(status->encoding == RX_ENC_HT) @@ expression status; @@ -(status->enc_flags & RX_ENC_FLAG_VHT) +(status->encoding == RX_ENC_VHT) @@ expression status; @@ -(status->enc_flags & RX_ENC_FLAG_5MHZ) +(status->bw == RATE_INFO_BW_5) @@ expression status; @@ -(status->enc_flags & RX_ENC_FLAG_10MHZ) +(status->bw == RATE_INFO_BW_10) @@ expression status; @@ -(status->enc_flags & RX_ENC_FLAG_40MHZ) +(status->bw == RATE_INFO_BW_40) @@ expression status; @@ -(status->enc_flags & RX_ENC_FLAG_80MHZ) +(status->bw == RATE_INFO_BW_80) @@ expression status; @@ -(status->enc_flags & RX_ENC_FLAG_160MHZ) +(status->bw == RATE_INFO_BW_160) Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath10k/htt_rx.c | 32 ++++++------- drivers/net/wireless/ath/ath5k/base.c | 4 +- drivers/net/wireless/ath/ath9k/ar9003_mac.c | 1 + drivers/net/wireless/ath/ath9k/common.c | 8 ++-- drivers/net/wireless/ath/ath9k/debug_sta.c | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 3 +- drivers/net/wireless/ath/ath9k/mac.c | 1 + drivers/net/wireless/ath/ath9k/mac.h | 2 + drivers/net/wireless/ath/ath9k/recv.c | 4 +- drivers/net/wireless/ath/carl9170/rx.c | 4 +- .../wireless/broadcom/brcm80211/brcmsmac/main.c | 4 +- drivers/net/wireless/intel/iwlegacy/4965-mac.c | 2 +- drivers/net/wireless/intel/iwlwifi/dvm/rx.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 10 ++-- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 10 ++-- drivers/net/wireless/mac80211_hwsim.c | 4 +- drivers/net/wireless/marvell/mwl8k.c | 12 ++--- drivers/net/wireless/mediatek/mt7601u/mac.c | 4 +- drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 2 +- drivers/net/wireless/ralink/rt2x00/rt2x00dev.c | 4 +- drivers/net/wireless/ralink/rt2x00/rt2x00queue.h | 2 + .../net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 8 ++-- .../net/wireless/realtek/rtlwifi/rtl8188ee/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8192ce/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8192cu/trx.c | 8 ++-- .../net/wireless/realtek/rtlwifi/rtl8192de/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8192ee/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8192se/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8723ae/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8723be/trx.c | 4 +- .../net/wireless/realtek/rtlwifi/rtl8821ae/trx.c | 8 ++-- drivers/net/wireless/st/cw1200/txrx.c | 2 +- drivers/net/wireless/ti/wlcore/rx.c | 2 +- include/net/mac80211.h | 37 +++++++-------- net/mac80211/ibss.c | 4 +- net/mac80211/ieee80211_i.h | 4 +- net/mac80211/rx.c | 55 +++++++++++++--------- net/mac80211/scan.c | 8 ++-- net/mac80211/sta_info.h | 29 +++++------- net/mac80211/util.c | 43 +++++++++-------- 40 files changed, 182 insertions(+), 171 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 2c29f8d12bcf..f5ddb83e285f 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -632,11 +632,11 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, sgi = (info3 >> 7) & 1; status->rate_idx = mcs; - status->enc_flags |= RX_ENC_FLAG_HT; + status->encoding = RX_ENC_HT; if (sgi) status->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (bw) - status->enc_flags |= RX_ENC_FLAG_40MHZ; + status->bw = RATE_INFO_BW_40; break; case HTT_RX_VHT: case HTT_RX_VHT_WITH_TXBF: @@ -700,18 +700,18 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, break; /* 40MHZ */ case 1: - status->enc_flags |= RX_ENC_FLAG_40MHZ; + status->bw = RATE_INFO_BW_40; break; /* 80MHZ */ case 2: - status->enc_flags |= RX_ENC_FLAG_80MHZ; + status->bw = RATE_INFO_BW_80; break; case 3: - status->enc_flags |= RX_ENC_FLAG_160MHZ; + status->bw = RATE_INFO_BW_160; break; } - status->enc_flags |= RX_ENC_FLAG_VHT; + status->encoding = RX_ENC_VHT; break; default: break; @@ -875,11 +875,8 @@ static void ath10k_htt_rx_h_ppdu(struct ath10k *ar, status->freq = 0; status->rate_idx = 0; status->vht_nss = 0; - status->enc_flags &= ~(RX_ENC_FLAG_HT | - RX_ENC_FLAG_VHT | - RX_ENC_FLAG_SHORT_GI | - RX_ENC_FLAG_40MHZ | - RX_ENC_FLAG_80MHZ); + status->encoding = RX_ENC_LEGACY; + status->bw = RATE_INFO_BW_20; status->flag &= ~RX_FLAG_MACTIME_END; status->flag |= RX_FLAG_NO_SIGNAL_VAL; @@ -941,13 +938,12 @@ static void ath10k_process_rx(struct ath10k *ar, is_multicast_ether_addr(ieee80211_get_DA(hdr)) ? "mcast" : "ucast", (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4, - (status->enc_flags & (RX_ENC_FLAG_HT | RX_ENC_FLAG_VHT)) == 0 ? - "legacy" : "", - status->enc_flags & RX_ENC_FLAG_HT ? "ht" : "", - status->enc_flags & RX_ENC_FLAG_VHT ? "vht" : "", - status->enc_flags & RX_ENC_FLAG_40MHZ ? "40" : "", - status->enc_flags & RX_ENC_FLAG_80MHZ ? "80" : "", - status->enc_flags & RX_ENC_FLAG_160MHZ ? "160" : "", + (status->encoding == RX_ENC_LEGACY) ? "legacy" : "", + (status->encoding == RX_ENC_HT) ? "ht" : "", + (status->encoding == RX_ENC_VHT) ? "vht" : "", + (status->bw == RATE_INFO_BW_40) ? "40" : "", + (status->bw == RATE_INFO_BW_80) ? "80" : "", + (status->bw == RATE_INFO_BW_160) ? "160" : "", status->enc_flags & RX_ENC_FLAG_SHORT_GI ? "sgi " : "", status->rate_idx, status->vht_nss, diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index da43469f6b49..527afcf39246 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1414,10 +1414,10 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, rxs->flag |= ath5k_rx_decrypted(ah, skb, rs); switch (ah->ah_bwmode) { case AR5K_BWMODE_5MHZ: - rxs->enc_flags |= RX_ENC_FLAG_5MHZ; + rxs->bw = RATE_INFO_BW_5; break; case AR5K_BWMODE_10MHZ: - rxs->enc_flags |= RX_ENC_FLAG_10MHZ; + rxs->bw = RATE_INFO_BW_10; break; default: break; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index 909854de42a1..68fcbe03bce2 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -495,6 +495,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_status = 0; rxs->rs_flags = 0; rxs->enc_flags = 0; + rxs->bw = RATE_INFO_BW_20; rxs->rs_datalen = rxsp->status2 & AR_DataLen; rxs->rs_tstamp = rxsp->status3; diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c index 9ed779b020d3..c67d0e08bd4c 100644 --- a/drivers/net/wireless/ath/ath9k/common.c +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -181,13 +181,15 @@ int ath9k_cmn_process_rate(struct ath_common *common, sband = hw->wiphy->bands[band]; if (IS_CHAN_QUARTER_RATE(ah->curchan)) - rxs->enc_flags |= RX_ENC_FLAG_5MHZ; + rxs->bw = RATE_INFO_BW_5; else if (IS_CHAN_HALF_RATE(ah->curchan)) - rxs->enc_flags |= RX_ENC_FLAG_10MHZ; + rxs->bw = RATE_INFO_BW_10; if (rx_stats->rs_rate & 0x80) { /* HT rate */ - rxs->enc_flags |= RX_ENC_FLAG_HT | rx_stats->enc_flags; + rxs->encoding = RX_ENC_HT; + rxs->enc_flags |= rx_stats->enc_flags; + rxs->bw = rx_stats->bw; rxs->rate_idx = rx_stats->rs_rate & 0x7f; return 0; } diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wireless/ath/ath9k/debug_sta.c index f1174c9d94d4..efc692ee67d4 100644 --- a/drivers/net/wireless/ath/ath9k/debug_sta.c +++ b/drivers/net/wireless/ath/ath9k/debug_sta.c @@ -116,7 +116,7 @@ void ath_debug_rate_stats(struct ath_softc *sc, if (rxs->rate_idx >= ARRAY_SIZE(rstats->ht_stats)) goto exit; - if (rxs->enc_flags & RX_ENC_FLAG_40MHZ) + if ((rxs->bw == RATE_INFO_BW_40)) rstats->ht_stats[rxs->rate_idx].ht40_cnt++; else rstats->ht_stats[rxs->rate_idx].ht20_cnt++; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 9f94efd66049..b38a586ea59a 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -930,8 +930,9 @@ static inline void convert_htc_flag(struct ath_rx_status *rx_stats, struct ath_htc_rx_status *rxstatus) { rx_stats->enc_flags = 0; + rx_stats->bw = RATE_INFO_BW_20; if (rxstatus->rs_flags & ATH9K_RX_2040) - rx_stats->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_stats->bw = RATE_INFO_BW_40; if (rxstatus->rs_flags & ATH9K_RX_GI) rx_stats->enc_flags |= RX_ENC_FLAG_SHORT_GI; } diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index c98a563fdd01..6128c2bb23d8 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -536,6 +536,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_status = 0; rs->rs_flags = 0; rs->enc_flags = 0; + rs->bw = RATE_INFO_BW_20; rs->rs_datalen = ads.ds_rxstatus1 & AR_DataLen; rs->rs_tstamp = ads.AR_RcvTimestamp; diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 6bdf83f4ca9a..fd6aa49adadf 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -16,6 +16,7 @@ #ifndef MAC_H #define MAC_H +#include #define set11nTries(_series, _index) \ (SM((_series)[_index].Tries, AR_XmitDataTries##_index)) @@ -144,6 +145,7 @@ struct ath_rx_status { u32 evm3; u32 evm4; u16 enc_flags; + enum rate_info_bw bw; }; struct ath_htc_rx_status { diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 65fcc34196f9..2197aee2bb72 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -1038,10 +1038,10 @@ static void ath_rx_count_airtime(struct ath_softc *sc, rxs = IEEE80211_SKB_RXCB(skb); is_sgi = !!(rxs->enc_flags & RX_ENC_FLAG_SHORT_GI); - is_40 = !!(rxs->enc_flags & RX_ENC_FLAG_40MHZ); + is_40 = !!(rxs->bw == RATE_INFO_BW_40); is_sp = !!(rxs->enc_flags & RX_ENC_FLAG_SHORTPRE); - if (!!(rxs->enc_flags & RX_ENC_FLAG_HT)) { + if (!!(rxs->encoding == RX_ENC_HT)) { /* MCS rates */ airtime += ath_pkt_duration(sc, rxs->rate_idx, len, diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index c2ffe78b3491..b2166726b05d 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -423,12 +423,12 @@ static int carl9170_rx_mac_status(struct ar9170 *ar, case AR9170_RX_STATUS_MODULATION_HT: if (head->plcp[3] & 0x80) - status->enc_flags |= RX_ENC_FLAG_40MHZ; + status->bw = RATE_INFO_BW_40; if (head->plcp[6] & 0x80) status->enc_flags |= RX_ENC_FLAG_SHORT_GI; status->rate_idx = clamp(0, 75, head->plcp[3] & 0x7f); - status->enc_flags |= RX_ENC_FLAG_HT; + status->encoding = RX_ENC_HT; break; default: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c index 95386a3757ea..0a14942b8216 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c @@ -7092,9 +7092,9 @@ prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, rspec = brcms_c_compute_rspec(rxh, plcp); if (is_mcs_rate(rspec)) { rx_status->rate_idx = rspec & RSPEC_RATE_MASK; - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; if (rspec_is40mhz(rspec)) - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; } else { switch (rspec2rate(rspec)) { case BRCM_RATE_1M: diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index 34a554b6ac3e..5d5faa3cad24 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -732,7 +732,7 @@ il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) /* Set up the HT phy flags */ if (rate_n_flags & RATE_MCS_HT_MSK) - rx_status.enc_flags |= RX_ENC_FLAG_HT; + rx_status.encoding = RX_ENC_HT; if (rate_n_flags & RATE_MCS_HT40_MSK) rx_status.enc_flags |= RX_ENC_FLAG_40MHZ; if (rate_n_flags & RATE_MCS_SGI_MSK) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c index 70e4fab5a0b8..1ee1ba9931a7 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c @@ -887,7 +887,7 @@ static void iwlagn_rx_reply_rx(struct iwl_priv *priv, /* Set up the HT phy flags */ if (rate_n_flags & RATE_MCS_HT_MSK) - rx_status.enc_flags |= RX_ENC_FLAG_HT; + rx_status.encoding = RX_ENC_HT; if (rate_n_flags & RATE_MCS_HT40_MSK) rx_status.enc_flags |= RX_ENC_FLAG_40MHZ; if (rate_n_flags & RATE_MCS_SGI_MSK) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 314a245e249e..93226329e077 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -427,13 +427,13 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, case RATE_MCS_CHAN_WIDTH_20: break; case RATE_MCS_CHAN_WIDTH_40: - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; break; case RATE_MCS_CHAN_WIDTH_80: - rx_status->enc_flags |= RX_ENC_FLAG_80MHZ; + rx_status->bw = RATE_INFO_BW_80; break; case RATE_MCS_CHAN_WIDTH_160: - rx_status->enc_flags |= RX_ENC_FLAG_160MHZ; + rx_status->bw = RATE_INFO_BW_160; break; } if (rate_n_flags & RATE_MCS_SGI_MSK) @@ -445,7 +445,7 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, if (rate_n_flags & RATE_MCS_HT_MSK) { u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> RATE_MCS_STBC_POS; - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; } else if (rate_n_flags & RATE_MCS_VHT_MSK) { @@ -455,7 +455,7 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> RATE_VHT_MCS_NSS_POS) + 1; rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; - rx_status->enc_flags |= RX_ENC_FLAG_VHT; + rx_status->encoding = RX_ENC_VHT; rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; if (rate_n_flags & RATE_MCS_BF_MSK) rx_status->enc_flags |= RX_ENC_FLAG_BF; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 09f21bba6f48..447f030c5c0a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -958,13 +958,13 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, case RATE_MCS_CHAN_WIDTH_20: break; case RATE_MCS_CHAN_WIDTH_40: - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; break; case RATE_MCS_CHAN_WIDTH_80: - rx_status->enc_flags |= RX_ENC_FLAG_80MHZ; + rx_status->bw = RATE_INFO_BW_80; break; case RATE_MCS_CHAN_WIDTH_160: - rx_status->enc_flags |= RX_ENC_FLAG_160MHZ; + rx_status->bw = RATE_INFO_BW_160; break; } if (rate_n_flags & RATE_MCS_SGI_MSK) @@ -976,7 +976,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, if (rate_n_flags & RATE_MCS_HT_MSK) { u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> RATE_MCS_STBC_POS; - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; } else if (rate_n_flags & RATE_MCS_VHT_MSK) { @@ -986,7 +986,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> RATE_VHT_MCS_NSS_POS) + 1; rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; - rx_status->enc_flags |= RX_ENC_FLAG_VHT; + rx_status->encoding = RX_ENC_VHT; rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; if (rate_n_flags & RATE_MCS_BF_MSK) rx_status->enc_flags |= RX_ENC_FLAG_BF; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index f6e96a5ee2f0..358f5f8ff6b9 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1194,11 +1194,11 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, ieee80211_rate_get_vht_mcs(&info->control.rates[0]); rx_status.vht_nss = ieee80211_rate_get_vht_nss(&info->control.rates[0]); - rx_status.enc_flags |= RX_ENC_FLAG_VHT; + rx_status.encoding = RX_ENC_VHT; } else { rx_status.rate_idx = info->control.rates[0].idx; if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) - rx_status.enc_flags |= RX_ENC_FLAG_HT; + rx_status.encoding = RX_ENC_HT; } if (info->control.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) rx_status.enc_flags |= RX_ENC_FLAG_40MHZ; diff --git a/drivers/net/wireless/marvell/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c index 9f6ab746d9fc..e813b2ca740c 100644 --- a/drivers/net/wireless/marvell/mwl8k.c +++ b/drivers/net/wireless/marvell/mwl8k.c @@ -994,9 +994,9 @@ mwl8k_rxd_ap_process(void *_rxd, struct ieee80211_rx_status *status, *noise = -rxd->noise_floor; if (rxd->rate & MWL8K_AP_RATE_INFO_MCS_FORMAT) { - status->enc_flags |= RX_ENC_FLAG_HT; + status->encoding = RX_ENC_HT; if (rxd->rate & MWL8K_AP_RATE_INFO_40MHZ) - status->enc_flags |= RX_ENC_FLAG_40MHZ; + status->bw = RATE_INFO_BW_40; status->rate_idx = MWL8K_AP_RATE_INFO_RATEID(rxd->rate); } else { int i; @@ -1011,7 +1011,7 @@ mwl8k_rxd_ap_process(void *_rxd, struct ieee80211_rx_status *status, if (rxd->channel > 14) { status->band = NL80211_BAND_5GHZ; - if (!(status->enc_flags & RX_ENC_FLAG_HT)) + if (!(status->encoding == RX_ENC_HT)) status->rate_idx -= 5; } else { status->band = NL80211_BAND_2GHZ; @@ -1111,15 +1111,15 @@ mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status, if (rate_info & MWL8K_STA_RATE_INFO_SHORTPRE) status->enc_flags |= RX_ENC_FLAG_SHORTPRE; if (rate_info & MWL8K_STA_RATE_INFO_40MHZ) - status->enc_flags |= RX_ENC_FLAG_40MHZ; + status->bw = RATE_INFO_BW_40; if (rate_info & MWL8K_STA_RATE_INFO_SHORTGI) status->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT) - status->enc_flags |= RX_ENC_FLAG_HT; + status->encoding = RX_ENC_HT; if (rxd->channel > 14) { status->band = NL80211_BAND_5GHZ; - if (!(status->enc_flags & RX_ENC_FLAG_HT)) + if (!(status->encoding == RX_ENC_HT)) status->rate_idx -= 5; } else { status->band = NL80211_BAND_2GHZ; diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.c b/drivers/net/wireless/mediatek/mt7601u/mac.c index 064cad0f0938..d6dc59bb00df 100644 --- a/drivers/net/wireless/mediatek/mt7601u/mac.c +++ b/drivers/net/wireless/mediatek/mt7601u/mac.c @@ -413,7 +413,7 @@ mt76_mac_process_rate(struct ieee80211_rx_status *status, u16 rate) status->enc_flags |= RX_ENC_FLAG_HT_GF; /* fall through */ case MT_PHY_TYPE_HT: - status->enc_flags |= RX_ENC_FLAG_HT; + status->encoding = RX_ENC_HT; status->rate_idx = idx; break; default: @@ -428,7 +428,7 @@ mt76_mac_process_rate(struct ieee80211_rx_status *status, u16 rate) status->enc_flags |= 1 << RX_ENC_FLAG_STBC_SHIFT; if (rate & MT_RXWI_RATE_BW) - status->enc_flags |= RX_ENC_FLAG_40MHZ; + status->bw = RATE_INFO_BW_40; } static void diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 03d415c553d1..d11c7b210e81 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -892,7 +892,7 @@ void rt2800_process_rxwi(struct queue_entry *entry, rxdesc->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (rt2x00_get_field32(word, RXWI_W1_BW)) - rxdesc->enc_flags |= RX_ENC_FLAG_40MHZ; + rxdesc->bw = RATE_INFO_BW_40; /* * Detect RX rate, always use MCS as signal type. diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index 8a4640d2ead0..357c0941aaad 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -825,7 +825,7 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp) rate_idx = rt2x00lib_rxdone_read_signal(rt2x00dev, &rxdesc); if (rxdesc.rate_mode == RATE_MODE_HT_MIX || rxdesc.rate_mode == RATE_MODE_HT_GREENFIELD) - rxdesc.enc_flags |= RX_ENC_FLAG_HT; + rxdesc.encoding = RX_ENC_HT; /* * Check if this is a beacon, and more frames have been @@ -866,6 +866,8 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp) rx_status->signal = rxdesc.rssi; rx_status->flag = rxdesc.flags; rx_status->enc_flags = rxdesc.enc_flags; + rx_status->encoding = rxdesc.encoding; + rx_status->bw = rxdesc.bw; rx_status->antenna = rt2x00dev->link.ant.active.rx; ieee80211_rx_ni(rt2x00dev->hw, entry->skb); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h index a4242e8e202e..6055f36211b9 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00queue.h @@ -185,6 +185,8 @@ struct rxdone_entry_desc { int dev_flags; u16 rate_mode; u16 enc_flags; + enum mac80211_rx_encoding encoding; + enum rate_info_bw bw; u8 cipher; u8 cipher_status; diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c index 4aa370aac9f6..39d56313bc94 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c @@ -5267,10 +5267,10 @@ int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb) if (rx_desc->crc32) rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (rx_desc->bw) - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; if (rx_desc->rxht) { - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0; } else { rx_status->rate_idx = rx_desc->rxmcs; @@ -5337,10 +5337,10 @@ int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb) if (rx_desc->crc32) rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (rx_desc->bw) - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; if (rx_desc->rxmcs >= DESC_RATE_MCS0) { - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; rx_status->rate_idx = rx_desc->rxmcs - DESC_RATE_MCS0; } else { rx_status->rate_idx = rx_desc->rxmcs; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c index c5145c20200a..dd3e12b74447 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/trx.c @@ -444,10 +444,10 @@ bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (status->rx_is40Mhzpacket) - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; if (status->is_ht) - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; rx_status->flag |= RX_FLAG_MACTIME_START; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c index 16f6c2eb6314..94a4b39437cd 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ce/trx.c @@ -369,10 +369,10 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (stats->rx_is40Mhzpacket) - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; if (stats->is_ht) - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; rx_status->flag |= RX_FLAG_MACTIME_START; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c index d80d2838726a..41422e4da8b7 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192cu/trx.c @@ -329,9 +329,9 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, if (!GET_RX_DESC_SWDEC(pdesc)) rx_status->flag |= RX_FLAG_DECRYPTED; if (GET_RX_DESC_BW(pdesc)) - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; if (GET_RX_DESC_RX_HT(pdesc)) - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; @@ -398,9 +398,9 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb) if (!GET_RX_DESC_SWDEC(rxdesc)) rx_status->flag |= RX_FLAG_DECRYPTED; if (GET_RX_DESC_BW(rxdesc)) - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; if (GET_RX_DESC_RX_HT(rxdesc)) - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; /* Data rate */ rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats.is_ht, false, stats.rate); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c index 01d7308cdc9f..86019f654428 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/trx.c @@ -503,9 +503,9 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, if (!GET_RX_DESC_SWDEC(pdesc)) rx_status->flag |= RX_FLAG_DECRYPTED; if (GET_RX_DESC_BW(pdesc)) - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; if (GET_RX_DESC_RXHT(pdesc)) - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c index 38ffee1f8838..b1864bb07c2c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/trx.c @@ -394,10 +394,10 @@ bool rtl92ee_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (status->rx_is40Mhzpacket) - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; if (status->is_ht) - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; rx_status->flag |= RX_FLAG_MACTIME_START; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c index 12a4a8faedec..a01dbd31d1b4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192se/trx.c @@ -289,10 +289,10 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (stats->rx_is40Mhzpacket) - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; if (stats->is_ht) - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; rx_status->flag |= RX_FLAG_MACTIME_START; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c index b8d0bcae4a71..f713c7249fed 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723ae/trx.c @@ -317,10 +317,10 @@ bool rtl8723e_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (status->rx_is40Mhzpacket) - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; if (status->is_ht) - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; rx_status->flag |= RX_FLAG_MACTIME_START; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c index 8d35a58bae93..3c6ce994c6aa 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/trx.c @@ -373,10 +373,10 @@ bool rtl8723be_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (status->rx_is40Mhzpacket) - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; if (status->is_ht) - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; rx_status->flag |= RX_FLAG_MACTIME_START; diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c index c4a35bfd4f3e..2182a3ec5b9f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c @@ -520,13 +520,13 @@ bool rtl8821ae_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; if (status->rx_packet_bw == HT_CHANNEL_WIDTH_20_40) - rx_status->enc_flags |= RX_ENC_FLAG_40MHZ; + rx_status->bw = RATE_INFO_BW_40; else if (status->rx_packet_bw == HT_CHANNEL_WIDTH_80) - rx_status->enc_flags |= RX_ENC_FLAG_80MHZ; + rx_status->bw = RATE_INFO_BW_80; if (status->is_ht) - rx_status->enc_flags |= RX_ENC_FLAG_HT; + rx_status->encoding = RX_ENC_HT; if (status->is_vht) - rx_status->enc_flags |= RX_ENC_FLAG_VHT; + rx_status->encoding = RX_ENC_VHT; if (status->is_short_gi) rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; diff --git a/drivers/net/wireless/st/cw1200/txrx.c b/drivers/net/wireless/st/cw1200/txrx.c index e8b69dbdfe39..cd63ffef025a 100644 --- a/drivers/net/wireless/st/cw1200/txrx.c +++ b/drivers/net/wireless/st/cw1200/txrx.c @@ -1085,7 +1085,7 @@ void cw1200_rx_cb(struct cw1200_common *priv, hdr->band); if (arg->rx_rate >= 14) { - hdr->enc_flags |= RX_ENC_FLAG_HT; + hdr->encoding = RX_ENC_HT; hdr->rate_idx = arg->rx_rate - 14; } else if (arg->rx_rate >= 4) { hdr->rate_idx = arg->rx_rate - 2; diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index 5552c763dad5..52a55f9acd80 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -72,7 +72,7 @@ static void wl1271_rx_status(struct wl1271 *wl, /* 11n support */ if (desc->rate <= wl->hw_min_ht_rate) - status->enc_flags |= RX_ENC_FLAG_HT; + status->encoding = RX_ENC_HT; /* * Read the signal level and antenna diversity indication. diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 53b3853d6fd1..c7c1f75a4a48 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1116,8 +1116,6 @@ enum mac80211_rx_flags { * enum mac80211_rx_encoding_flags - MCS & bandwidth flags * * @RX_ENC_FLAG_SHORTPRE: Short preamble was used for this frame - * @RX_ENC_FLAG_HT: HT MCS was used and rate_idx is MCS index - * @RX_ENC_FLAG_VHT: VHT MCS was used and rate_index is MCS index * @RX_ENC_FLAG_40MHZ: HT40 (40 MHz) was used * @RX_ENC_FLAG_SHORT_GI: Short guard interval was used * @RX_ENC_FLAG_HT_GF: This frame was received in a HT-greenfield transmission, @@ -1126,29 +1124,25 @@ enum mac80211_rx_flags { * to hw.radiotap_mcs_details to advertise that fact * @RX_ENC_FLAG_LDPC: LDPC was used * @RX_ENC_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3 - * @RX_ENC_FLAG_10MHZ: 10 MHz (half channel) was used - * @RX_ENC_FLAG_5MHZ: 5 MHz (quarter channel) was used - * @RX_ENC_FLAG_80MHZ: 80 MHz was used - * @RX_ENC_FLAG_160MHZ: 160 MHz was used * @RX_ENC_FLAG_BF: packet was beamformed */ enum mac80211_rx_encoding_flags { RX_ENC_FLAG_SHORTPRE = BIT(0), - RX_ENC_FLAG_HT = BIT(1), - RX_ENC_FLAG_40MHZ = BIT(2), - RX_ENC_FLAG_SHORT_GI = BIT(3), - RX_ENC_FLAG_HT_GF = BIT(4), - RX_ENC_FLAG_VHT = BIT(5), - RX_ENC_FLAG_STBC_MASK = BIT(6) | BIT(7), - RX_ENC_FLAG_LDPC = BIT(8), - RX_ENC_FLAG_10MHZ = BIT(9), - RX_ENC_FLAG_5MHZ = BIT(10), - RX_ENC_FLAG_80MHZ = BIT(11), - RX_ENC_FLAG_160MHZ = BIT(12), - RX_ENC_FLAG_BF = BIT(13), + RX_ENC_FLAG_40MHZ = BIT(1), + RX_ENC_FLAG_SHORT_GI = BIT(2), + RX_ENC_FLAG_HT_GF = BIT(3), + RX_ENC_FLAG_STBC_MASK = BIT(4) | BIT(5), + RX_ENC_FLAG_LDPC = BIT(6), + RX_ENC_FLAG_BF = BIT(7), }; -#define RX_ENC_FLAG_STBC_SHIFT 6 +#define RX_ENC_FLAG_STBC_SHIFT 4 + +enum mac80211_rx_encoding { + RX_ENC_LEGACY = 0, + RX_ENC_HT, + RX_ENC_VHT, +}; /** * struct ieee80211_rx_status - receive status @@ -1179,6 +1173,8 @@ enum mac80211_rx_encoding_flags { * HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT) * @vht_nss: number of streams (VHT only) * @flag: %RX_FLAG_\* + * @encoding: &enum mac80211_rx_encoding + * @bw: &enum rate_info_bw * @enc_flags: uses bits from &enum mac80211_rx_encoding_flags * @rx_flags: internal RX flags for mac80211 * @ampdu_reference: A-MPDU reference number, must be a different value for @@ -1191,8 +1187,9 @@ struct ieee80211_rx_status { u32 device_timestamp; u32 ampdu_reference; u32 flag; - u16 enc_flags; u16 freq; + u8 enc_flags; + u8 encoding:2, bw:3; u8 rate_idx; u8 vht_nss; u8 rx_flags; diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 6d9a80982d02..9d0f6100942b 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1014,9 +1014,9 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, prev_rates = sta->sta.supp_rates[band]; /* make sure mandatory rates are always added */ scan_width = NL80211_BSS_CHAN_WIDTH_20; - if (rx_status->enc_flags & RX_ENC_FLAG_5MHZ) + if (rx_status->bw == RATE_INFO_BW_5) scan_width = NL80211_BSS_CHAN_WIDTH_5; - if (rx_status->enc_flags & RX_ENC_FLAG_10MHZ) + else if (rx_status->bw == RATE_INFO_BW_10) scan_width = NL80211_BSS_CHAN_WIDTH_10; sta->sta.supp_rates[band] = supp_rates | diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a4ba849ac074..47d709afea25 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1530,9 +1530,9 @@ ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status) status->flag & RX_FLAG_MACTIME_END); if (status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END)) return true; - /* can't handle HT/VHT preamble yet */ + /* can't handle non-legacy preamble yet */ if (status->flag & RX_FLAG_MACTIME_PLCP_START && - !(status->enc_flags & (RX_ENC_FLAG_HT | RX_ENC_FLAG_VHT))) + status->encoding != RX_ENC_LEGACY) return true; return false; } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 3f7ecde323d7..762e89cf737a 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -156,7 +156,7 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, /* padding for RX_FLAGS if necessary */ len = ALIGN(len, 2); - if (status->enc_flags & RX_ENC_FLAG_HT) /* HT info */ + if (status->encoding == RX_ENC_HT) /* HT info */ len += 3; if (status->flag & RX_FLAG_AMPDU_DETAILS) { @@ -164,7 +164,7 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, len += 8; } - if (status->enc_flags & RX_ENC_FLAG_VHT) { + if (status->encoding == RX_ENC_VHT) { len = ALIGN(len, 2); len += 12; } @@ -334,7 +334,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, pos++; /* IEEE80211_RADIOTAP_RATE */ - if (!rate || status->enc_flags & (RX_ENC_FLAG_HT | RX_ENC_FLAG_VHT)) { + if (!rate || status->encoding != RX_ENC_LEGACY) { /* * Without rate information don't add it. If we have, * MCS information is a separate field in radiotap, @@ -345,9 +345,9 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, } else { int shift = 0; rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); - if (status->enc_flags & RX_ENC_FLAG_10MHZ) + if (status->bw == RATE_INFO_BW_10) shift = 1; - else if (status->enc_flags & RX_ENC_FLAG_5MHZ) + else if (status->bw == RATE_INFO_BW_5) shift = 2; *pos = DIV_ROUND_UP(rate->bitrate, 5 * (1 << shift)); } @@ -356,14 +356,14 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* IEEE80211_RADIOTAP_CHANNEL */ put_unaligned_le16(status->freq, pos); pos += 2; - if (status->enc_flags & RX_ENC_FLAG_10MHZ) + if (status->bw == RATE_INFO_BW_10) channel_flags |= IEEE80211_CHAN_HALF; - else if (status->enc_flags & RX_ENC_FLAG_5MHZ) + else if (status->bw == RATE_INFO_BW_5) channel_flags |= IEEE80211_CHAN_QUARTER; if (status->band == NL80211_BAND_5GHZ) channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ; - else if (status->enc_flags & (RX_ENC_FLAG_HT | RX_ENC_FLAG_VHT)) + else if (status->encoding != RX_ENC_LEGACY) channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; else if (rate && rate->flags & IEEE80211_RATE_ERP_G) channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ; @@ -402,7 +402,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, put_unaligned_le16(rx_flags, pos); pos += 2; - if (status->enc_flags & RX_ENC_FLAG_HT) { + if (status->encoding == RX_ENC_HT) { unsigned int stbc; rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); @@ -410,7 +410,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos = 0; if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) *pos |= IEEE80211_RADIOTAP_MCS_SGI; - if (status->enc_flags & RX_ENC_FLAG_40MHZ) + if (status->bw == RATE_INFO_BW_40) *pos |= IEEE80211_RADIOTAP_MCS_BW_40; if (status->enc_flags & RX_ENC_FLAG_HT_GF) *pos |= IEEE80211_RADIOTAP_MCS_FMT_GF; @@ -449,7 +449,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos++ = 0; } - if (status->enc_flags & RX_ENC_FLAG_VHT) { + if (status->encoding == RX_ENC_VHT) { u16 known = local->hw.radiotap_vht_details; rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT); @@ -465,14 +465,19 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos |= IEEE80211_RADIOTAP_VHT_FLAG_BEAMFORMED; pos++; /* bandwidth */ - if (status->enc_flags & RX_ENC_FLAG_80MHZ) + switch (status->bw) { + case RATE_INFO_BW_80: *pos++ = 4; - else if (status->enc_flags & RX_ENC_FLAG_160MHZ) + break; + case RATE_INFO_BW_160: *pos++ = 11; - else if (status->enc_flags & RX_ENC_FLAG_40MHZ) + break; + case RATE_INFO_BW_40: *pos++ = 1; - else /* 20 MHz */ + break; + default: *pos++ = 0; + } /* MCS/NSS */ *pos = (status->rate_idx << 4) | status->vht_nss; pos += 4; @@ -3336,8 +3341,8 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx, status = IEEE80211_SKB_RXCB((rx->skb)); sband = rx->local->hw.wiphy->bands[status->band]; - if (!(status->enc_flags & RX_ENC_FLAG_HT) && - !(status->enc_flags & RX_ENC_FLAG_VHT)) + if (!(status->encoding == RX_ENC_HT) && + !(status->encoding == RX_ENC_VHT)) rate = &sband->bitrates[status->rate_idx]; ieee80211_rx_cooked_monitor(rx, rate); @@ -3598,7 +3603,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) return false; if (!rx->sta) { int rate_idx; - if (status->enc_flags & (RX_ENC_FLAG_HT | RX_ENC_FLAG_VHT)) + if (status->encoding != RX_ENC_LEGACY) rate_idx = 0; /* TODO: HT/VHT rates */ else rate_idx = status->rate_idx; @@ -3618,7 +3623,7 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx) return false; if (!rx->sta) { int rate_idx; - if (status->enc_flags & RX_ENC_FLAG_HT) + if (status->encoding != RX_ENC_LEGACY) rate_idx = 0; /* TODO: HT rates */ else rate_idx = status->rate_idx; @@ -4281,7 +4286,8 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, * we probably can't have a valid rate here anyway. */ - if (status->enc_flags & RX_ENC_FLAG_HT) { + switch (status->encoding) { + case RX_ENC_HT: /* * rate_idx is MCS index, which can be [0-76] * as documented on: @@ -4299,14 +4305,19 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, status->rate_idx, status->rate_idx)) goto drop; - } else if (status->enc_flags & RX_ENC_FLAG_VHT) { + break; + case RX_ENC_VHT: if (WARN_ONCE(status->rate_idx > 9 || !status->vht_nss || status->vht_nss > 8, "Rate marked as a VHT rate but data is invalid: MCS: %d, NSS: %d\n", status->rate_idx, status->vht_nss)) goto drop; - } else { + break; + default: + WARN_ON_ONCE(1); + /* fall through */ + case RX_ENC_LEGACY: if (WARN_ON(status->rate_idx >= sband->n_bitrates)) goto drop; rate = &sband->bitrates[status->rate_idx]; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index f693ca0e0858..0b16e2e8dc9e 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -79,9 +79,9 @@ ieee80211_bss_info_update(struct ieee80211_local *local, bss_meta.signal = (rx_status->signal * 100) / local->hw.max_signal; bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_20; - if (rx_status->enc_flags & RX_ENC_FLAG_5MHZ) + if (rx_status->bw == RATE_INFO_BW_5) bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_5; - if (rx_status->enc_flags & RX_ENC_FLAG_10MHZ) + else if (rx_status->bw == RATE_INFO_BW_10) bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_10; bss_meta.chan = channel; @@ -174,8 +174,8 @@ ieee80211_bss_info_update(struct ieee80211_local *local, if (beacon) { struct ieee80211_supported_band *sband = local->hw.wiphy->bands[rx_status->band]; - if (!(rx_status->enc_flags & RX_ENC_FLAG_HT) && - !(rx_status->enc_flags & RX_ENC_FLAG_VHT)) + if (!(rx_status->encoding == RX_ENC_HT) && + !(rx_status->encoding == RX_ENC_VHT)) bss->beacon_rate = &sband->bitrates[rx_status->rate_idx]; } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 22b8e0250fc6..c7f259409187 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -1,7 +1,7 @@ /* * Copyright 2002-2005, Devicescape Software, Inc. * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright(c) 2015-2016 Intel Deutschland GmbH + * Copyright(c) 2015-2017 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 @@ -740,28 +740,25 @@ static inline u16 sta_stats_encode_rate(struct ieee80211_rx_status *s) { u16 r = s->rate_idx; - if (s->enc_flags & RX_ENC_FLAG_80MHZ) - r |= RATE_INFO_BW_80 << STA_STATS_RATE_BW_SHIFT; - else if (s->enc_flags & RX_ENC_FLAG_160MHZ) - r |= RATE_INFO_BW_160 << STA_STATS_RATE_BW_SHIFT; - else if (s->enc_flags & RX_ENC_FLAG_40MHZ) - r |= RATE_INFO_BW_40 << STA_STATS_RATE_BW_SHIFT; - else if (s->enc_flags & RX_ENC_FLAG_10MHZ) - r |= RATE_INFO_BW_10 << STA_STATS_RATE_BW_SHIFT; - else if (s->enc_flags & RX_ENC_FLAG_5MHZ) - r |= RATE_INFO_BW_5 << STA_STATS_RATE_BW_SHIFT; - else - r |= RATE_INFO_BW_20 << STA_STATS_RATE_BW_SHIFT; + r |= s->bw << STA_STATS_RATE_BW_SHIFT; if (s->enc_flags & RX_ENC_FLAG_SHORT_GI) r |= STA_STATS_RATE_SGI; - if (s->enc_flags & RX_ENC_FLAG_VHT) + switch (s->encoding) { + case RX_ENC_VHT: r |= STA_STATS_RATE_TYPE_VHT | (s->vht_nss << 4); - else if (s->enc_flags & RX_ENC_FLAG_HT) + break; + case RX_ENC_HT: r |= STA_STATS_RATE_TYPE_HT; - else + break; + default: + WARN_ON(1); + /* fall through */ + case RX_ENC_LEGACY: r |= STA_STATS_RATE_TYPE_LEGACY | (s->band << 4); + break; + } return r; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ca198d153d72..e9c28904998b 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -4,7 +4,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright (C) 2015-2016 Intel Deutschland GmbH + * Copyright (C) 2015-2017 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 @@ -2715,42 +2715,39 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, memset(&ri, 0, sizeof(ri)); /* Fill cfg80211 rate info */ - if (status->enc_flags & RX_ENC_FLAG_HT) { + switch (status->encoding) { + case RX_ENC_HT: ri.mcs = status->rate_idx; ri.flags |= RATE_INFO_FLAGS_MCS; - if (status->enc_flags & RX_ENC_FLAG_40MHZ) - ri.bw = RATE_INFO_BW_40; - else - ri.bw = RATE_INFO_BW_20; + ri.bw = status->bw; if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) ri.flags |= RATE_INFO_FLAGS_SHORT_GI; - } else if (status->enc_flags & RX_ENC_FLAG_VHT) { + break; + case RX_ENC_VHT: ri.flags |= RATE_INFO_FLAGS_VHT_MCS; ri.mcs = status->rate_idx; ri.nss = status->vht_nss; - if (status->enc_flags & RX_ENC_FLAG_40MHZ) - ri.bw = RATE_INFO_BW_40; - else if (status->enc_flags & RX_ENC_FLAG_80MHZ) - ri.bw = RATE_INFO_BW_80; - else if (status->enc_flags & RX_ENC_FLAG_160MHZ) - ri.bw = RATE_INFO_BW_160; - else - ri.bw = RATE_INFO_BW_20; + ri.bw = status->bw; if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) ri.flags |= RATE_INFO_FLAGS_SHORT_GI; - } else { + break; + default: + WARN_ON(1); + /* fall through */ + case RX_ENC_LEGACY: { struct ieee80211_supported_band *sband; int shift = 0; int bitrate; - if (status->enc_flags & RX_ENC_FLAG_10MHZ) { + ri.bw = status->bw; + + switch (status->bw) { + case RATE_INFO_BW_10: shift = 1; - ri.bw = RATE_INFO_BW_10; - } else if (status->enc_flags & RX_ENC_FLAG_5MHZ) { + break; + case RATE_INFO_BW_5: shift = 2; - ri.bw = RATE_INFO_BW_5; - } else { - ri.bw = RATE_INFO_BW_20; + break; } sband = local->hw.wiphy->bands[status->band]; @@ -2768,6 +2765,8 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, ts += 192; } } + break; + } } rate = cfg80211_calculate_bitrate(&ri); -- cgit From 8613c94815fcdd358638a22fed50c3f172042aa2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 26 Apr 2017 13:51:41 +0200 Subject: mac80211: rename ieee80211_rx_status::vht_nss to just nss This field will need to be used again for HE, so rename it now. Again, mostly done with this spatch: @@ expression status; @@ -status->vht_nss +status->nss @@ expression status; @@ -status.vht_nss +status.nss Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath10k/htt_rx.c | 6 +++--- drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 2 +- drivers/net/wireless/mac80211_hwsim.c | 2 +- drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c | 2 +- include/net/mac80211.h | 4 ++-- net/mac80211/rx.c | 8 ++++---- net/mac80211/sta_info.h | 2 +- net/mac80211/util.c | 4 ++-- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index f5ddb83e285f..84b6067ff6e7 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -689,7 +689,7 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, } status->rate_idx = mcs; - status->vht_nss = nss; + status->nss = nss; if (sgi) status->enc_flags |= RX_ENC_FLAG_SHORT_GI; @@ -874,7 +874,7 @@ static void ath10k_htt_rx_h_ppdu(struct ath10k *ar, /* New PPDU starts so clear out the old per-PPDU status. */ status->freq = 0; status->rate_idx = 0; - status->vht_nss = 0; + status->nss = 0; status->encoding = RX_ENC_LEGACY; status->bw = RATE_INFO_BW_20; status->flag &= ~RX_FLAG_MACTIME_END; @@ -946,7 +946,7 @@ static void ath10k_process_rx(struct ath10k *ar, (status->bw == RATE_INFO_BW_160) ? "160" : "", status->enc_flags & RX_ENC_FLAG_SHORT_GI ? "sgi " : "", status->rate_idx, - status->vht_nss, + status->nss, status->freq, status->band, status->flag, !!(status->flag & RX_FLAG_FAILED_FCS_CRC), diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 93226329e077..fb368d19c0a3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -451,7 +451,7 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, } else if (rate_n_flags & RATE_MCS_VHT_MSK) { u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >> RATE_MCS_STBC_POS; - rx_status->vht_nss = + rx_status->nss = ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> RATE_VHT_MCS_NSS_POS) + 1; rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 447f030c5c0a..d66b2b527bd8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -982,7 +982,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, } else if (rate_n_flags & RATE_MCS_VHT_MSK) { u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >> RATE_MCS_STBC_POS; - rx_status->vht_nss = + rx_status->nss = ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> RATE_VHT_MCS_NSS_POS) + 1; rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 358f5f8ff6b9..87444af20fc5 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1192,7 +1192,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, if (info->control.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) { rx_status.rate_idx = ieee80211_rate_get_vht_mcs(&info->control.rates[0]); - rx_status.vht_nss = + rx_status.nss = ieee80211_rate_get_vht_nss(&info->control.rates[0]); rx_status.encoding = RX_ENC_VHT; } else { diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c index 2182a3ec5b9f..03665e82065f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/trx.c @@ -531,7 +531,7 @@ bool rtl8821ae_rx_query_desc(struct ieee80211_hw *hw, if (status->is_short_gi) rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; - rx_status->vht_nss = status->vht_nss; + rx_status->nss = status->vht_nss; rx_status->flag |= RX_FLAG_MACTIME_START; /* hw will set status->decrypted true, if it finds the diff --git a/include/net/mac80211.h b/include/net/mac80211.h index c7c1f75a4a48..f1b58b580080 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1171,7 +1171,7 @@ enum mac80211_rx_encoding { * @antenna: antenna used * @rate_idx: index of data rate into band's supported rates or MCS index if * HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT) - * @vht_nss: number of streams (VHT only) + * @nss: number of streams (VHT and HE only) * @flag: %RX_FLAG_\* * @encoding: &enum mac80211_rx_encoding * @bw: &enum rate_info_bw @@ -1191,7 +1191,7 @@ struct ieee80211_rx_status { u8 enc_flags; u8 encoding:2, bw:3; u8 rate_idx; - u8 vht_nss; + u8 nss; u8 rx_flags; u8 band; u8 antenna; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 762e89cf737a..35f4c7d7a500 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -479,7 +479,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos++ = 0; } /* MCS/NSS */ - *pos = (status->rate_idx << 4) | status->vht_nss; + *pos = (status->rate_idx << 4) | status->nss; pos += 4; /* coding field */ if (status->enc_flags & RX_ENC_FLAG_LDPC) @@ -4308,10 +4308,10 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, break; case RX_ENC_VHT: if (WARN_ONCE(status->rate_idx > 9 || - !status->vht_nss || - status->vht_nss > 8, + !status->nss || + status->nss > 8, "Rate marked as a VHT rate but data is invalid: MCS: %d, NSS: %d\n", - status->rate_idx, status->vht_nss)) + status->rate_idx, status->nss)) goto drop; break; default: diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index c7f259409187..150f478c7c63 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -747,7 +747,7 @@ static inline u16 sta_stats_encode_rate(struct ieee80211_rx_status *s) switch (s->encoding) { case RX_ENC_VHT: - r |= STA_STATS_RATE_TYPE_VHT | (s->vht_nss << 4); + r |= STA_STATS_RATE_TYPE_VHT | (s->nss << 4); break; case RX_ENC_HT: r |= STA_STATS_RATE_TYPE_HT; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e9c28904998b..83342b73e9aa 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2726,7 +2726,7 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, case RX_ENC_VHT: ri.flags |= RATE_INFO_FLAGS_VHT_MCS; ri.mcs = status->rate_idx; - ri.nss = status->vht_nss; + ri.nss = status->nss; ri.bw = status->bw; if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) ri.flags |= RATE_INFO_FLAGS_SHORT_GI; @@ -2773,7 +2773,7 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, if (WARN_ONCE(!rate, "Invalid bitrate: flags=0x%llx, idx=%d, vht_nss=%d\n", (unsigned long long)status->flag, status->rate_idx, - status->vht_nss)) + status->nss)) return 0; /* rewind from end of MPDU */ -- cgit From dcba665b1f4a5e986f22ac4230d536341d3ea5da Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 26 Apr 2017 14:51:20 +0200 Subject: mac80211: use bitfield macros for encoded rate Instead of hand-coding the bit manipulations, use the bitfield macros to generate the code for the encoded bitrate. Signed-off-by: Johannes Berg --- net/mac80211/sta_info.c | 26 +++++++++++++----------- net/mac80211/sta_info.h | 54 ++++++++++++++++++++++++++++++++----------------- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 81ec1f72518d..464566c662c5 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2,7 +2,7 @@ * Copyright 2002-2005, Instant802 Networks, Inc. * Copyright 2006-2007 Jiri Benc * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright (C) 2015 - 2016 Intel Deutschland GmbH + * Copyright (C) 2015 - 2017 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 @@ -1957,27 +1957,32 @@ sta_get_last_rx_stats(struct sta_info *sta) static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate, struct rate_info *rinfo) { - rinfo->bw = (rate & STA_STATS_RATE_BW_MASK) >> - STA_STATS_RATE_BW_SHIFT; + rinfo->bw = STA_STATS_GET(BW, rate); - switch (rate & STA_STATS_RATE_TYPE_MASK) { + switch (STA_STATS_GET(TYPE, rate)) { case STA_STATS_RATE_TYPE_VHT: rinfo->flags = RATE_INFO_FLAGS_VHT_MCS; - rinfo->mcs = rate & 0xf; - rinfo->nss = (rate & 0xf0) >> 4; + rinfo->mcs = STA_STATS_GET(VHT_MCS, rate); + rinfo->nss = STA_STATS_GET(VHT_NSS, rate); + if (STA_STATS_GET(SGI, rate)) + rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; break; case STA_STATS_RATE_TYPE_HT: rinfo->flags = RATE_INFO_FLAGS_MCS; - rinfo->mcs = rate & 0xff; + rinfo->mcs = STA_STATS_GET(HT_MCS, rate); + if (STA_STATS_GET(SGI, rate)) + rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; break; case STA_STATS_RATE_TYPE_LEGACY: { struct ieee80211_supported_band *sband; u16 brate; unsigned int shift; + int band = STA_STATS_GET(LEGACY_BAND, rate); + int rate_idx = STA_STATS_GET(LEGACY_IDX, rate); rinfo->flags = 0; - sband = local->hw.wiphy->bands[(rate >> 4) & 0xf]; - brate = sband->bitrates[rate & 0xf].bitrate; + sband = local->hw.wiphy->bands[band]; + brate = sband->bitrates[rate_idx].bitrate; if (rinfo->bw == RATE_INFO_BW_5) shift = 2; else if (rinfo->bw == RATE_INFO_BW_10) @@ -1988,9 +1993,6 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate, break; } } - - if (rate & STA_STATS_RATE_SGI) - rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; } static int sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 150f478c7c63..5609cacb20d5 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -727,37 +728,54 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); unsigned long ieee80211_sta_last_active(struct sta_info *sta); +enum sta_stats_type { + STA_STATS_RATE_TYPE_INVALID = 0, + STA_STATS_RATE_TYPE_LEGACY, + STA_STATS_RATE_TYPE_HT, + STA_STATS_RATE_TYPE_VHT, +}; + +#define STA_STATS_FIELD_HT_MCS GENMASK( 7, 0) +#define STA_STATS_FIELD_LEGACY_IDX GENMASK( 3, 0) +#define STA_STATS_FIELD_LEGACY_BAND GENMASK( 7, 4) +#define STA_STATS_FIELD_VHT_MCS GENMASK( 3, 0) +#define STA_STATS_FIELD_VHT_NSS GENMASK( 7, 4) +#define STA_STATS_FIELD_BW GENMASK(11, 8) +#define STA_STATS_FIELD_SGI GENMASK(12, 12) +#define STA_STATS_FIELD_TYPE GENMASK(15, 13) + +#define STA_STATS_FIELD(_n, _v) FIELD_PREP(STA_STATS_FIELD_ ## _n, _v) +#define STA_STATS_GET(_n, _v) FIELD_GET(STA_STATS_FIELD_ ## _n, _v) + #define STA_STATS_RATE_INVALID 0 -#define STA_STATS_RATE_TYPE_MASK 0xC000 -#define STA_STATS_RATE_TYPE_LEGACY 0x4000 -#define STA_STATS_RATE_TYPE_HT 0x8000 -#define STA_STATS_RATE_TYPE_VHT 0xC000 -#define STA_STATS_RATE_SGI 0x1000 -#define STA_STATS_RATE_BW_SHIFT 9 -#define STA_STATS_RATE_BW_MASK (0x7 << STA_STATS_RATE_BW_SHIFT) - -static inline u16 sta_stats_encode_rate(struct ieee80211_rx_status *s) + +static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s) { - u16 r = s->rate_idx; + u16 r; - r |= s->bw << STA_STATS_RATE_BW_SHIFT; + r = STA_STATS_FIELD(BW, s->bw); if (s->enc_flags & RX_ENC_FLAG_SHORT_GI) - r |= STA_STATS_RATE_SGI; + r |= STA_STATS_FIELD(SGI, 1); switch (s->encoding) { case RX_ENC_VHT: - r |= STA_STATS_RATE_TYPE_VHT | (s->nss << 4); + r |= STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_VHT); + r |= STA_STATS_FIELD(VHT_NSS, s->nss); + r |= STA_STATS_FIELD(VHT_MCS, s->rate_idx); break; case RX_ENC_HT: - r |= STA_STATS_RATE_TYPE_HT; + r |= STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_HT); + r |= STA_STATS_FIELD(HT_MCS, s->rate_idx); break; - default: - WARN_ON(1); - /* fall through */ case RX_ENC_LEGACY: - r |= STA_STATS_RATE_TYPE_LEGACY | (s->band << 4); + r |= STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_LEGACY); + r |= STA_STATS_FIELD(LEGACY_BAND, s->band); + r |= STA_STATS_FIELD(LEGACY_IDX, s->rate_idx); break; + default: + WARN_ON(1); + return STA_STATS_RATE_INVALID; } return r; -- cgit From 18fb84d986b398c59be6729f38f1c4bbbe8c4e9a Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 26 Apr 2017 17:11:35 +0200 Subject: mac80211: make rate control tx status API more extensible Rename .tx_status_noskb to .tx_status_ext and pass a new on-stack struct ieee80211_tx_status instead of struct ieee80211_tx_info. This struct can be used to pass extra information, e.g. for dynamic tx power control Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- include/net/mac80211.h | 20 +++++++++++++---- net/mac80211/rate.c | 22 +++++++++++++++++++ net/mac80211/rate.h | 44 +++----------------------------------- net/mac80211/rc80211_minstrel.c | 6 +++--- net/mac80211/rc80211_minstrel_ht.c | 10 ++++----- net/mac80211/status.c | 11 ++++++++-- 6 files changed, 58 insertions(+), 55 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f1b58b580080..c9ba79afb136 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -948,6 +948,19 @@ struct ieee80211_tx_info { }; }; +/** + * struct ieee80211_tx_status - extended tx staus info for rate control + * + * @sta: Station that the packet was transmitted for + * @info: Basic tx status information + * @skb: Packet skb (can be NULL if not provided by the driver) + */ +struct ieee80211_tx_status { + struct ieee80211_sta *sta; + struct ieee80211_tx_info *info; + struct sk_buff *skb; +}; + /** * struct ieee80211_scan_ies - descriptors for different blocks of IEs * @@ -5471,10 +5484,9 @@ struct rate_control_ops { void (*free_sta)(void *priv, struct ieee80211_sta *sta, void *priv_sta); - void (*tx_status_noskb)(void *priv, - struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta, - struct ieee80211_tx_info *info); + void (*tx_status_ext)(void *priv, + struct ieee80211_supported_band *sband, + void *priv_sta, struct ieee80211_tx_status *st); void (*tx_status)(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, struct sk_buff *skb); diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 9d7a1cd949fb..b387c07b8b47 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -62,6 +62,28 @@ void rate_control_rate_init(struct sta_info *sta) set_sta_flag(sta, WLAN_STA_RATE_CONTROL); } +void rate_control_tx_status(struct ieee80211_local *local, + struct ieee80211_supported_band *sband, + struct ieee80211_tx_status *st) +{ + struct rate_control_ref *ref = local->rate_ctrl; + struct sta_info *sta = container_of(st->sta, struct sta_info, sta); + void *priv_sta = sta->rate_ctrl_priv; + + if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) + return; + + spin_lock_bh(&sta->rate_ctrl_lock); + if (ref->ops->tx_status_ext) + ref->ops->tx_status_ext(ref->priv, sband, priv_sta, st); + else if (st->skb) + ref->ops->tx_status(ref->priv, sband, st->sta, priv_sta, st->skb); + else + WARN_ON_ONCE(1); + + spin_unlock_bh(&sta->rate_ctrl_lock); +} + void rate_control_rate_update(struct ieee80211_local *local, struct ieee80211_supported_band *sband, struct sta_info *sta, u32 changed) diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index f7825ef5f871..8212bfeb71d6 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -28,47 +28,9 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_tx_rate_control *txrc); -static inline void rate_control_tx_status(struct ieee80211_local *local, - struct ieee80211_supported_band *sband, - struct sta_info *sta, - struct sk_buff *skb) -{ - struct rate_control_ref *ref = local->rate_ctrl; - struct ieee80211_sta *ista = &sta->sta; - void *priv_sta = sta->rate_ctrl_priv; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) - return; - - spin_lock_bh(&sta->rate_ctrl_lock); - if (ref->ops->tx_status) - ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); - else - ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info); - spin_unlock_bh(&sta->rate_ctrl_lock); -} - -static inline void -rate_control_tx_status_noskb(struct ieee80211_local *local, - struct ieee80211_supported_band *sband, - struct sta_info *sta, - struct ieee80211_tx_info *info) -{ - struct rate_control_ref *ref = local->rate_ctrl; - struct ieee80211_sta *ista = &sta->sta; - void *priv_sta = sta->rate_ctrl_priv; - - if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) - return; - - if (WARN_ON_ONCE(!ref->ops->tx_status_noskb)) - return; - - spin_lock_bh(&sta->rate_ctrl_lock); - ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info); - spin_unlock_bh(&sta->rate_ctrl_lock); -} +void rate_control_tx_status(struct ieee80211_local *local, + struct ieee80211_supported_band *sband, + struct ieee80211_tx_status *st); void rate_control_rate_init(struct sta_info *sta); void rate_control_rate_update(struct ieee80211_local *local, diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 3ebe4405a2d4..9766c1cc4b0a 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -264,9 +264,9 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) static void minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta, - struct ieee80211_tx_info *info) + void *priv_sta, struct ieee80211_tx_status *st) { + struct ieee80211_tx_info *info = st->info; struct minstrel_priv *mp = priv; struct minstrel_sta_info *mi = priv_sta; struct ieee80211_tx_rate *ar = info->status.rates; @@ -726,7 +726,7 @@ static u32 minstrel_get_expected_throughput(void *priv_sta) const struct rate_control_ops mac80211_minstrel = { .name = "minstrel", - .tx_status_noskb = minstrel_tx_status, + .tx_status_ext = minstrel_tx_status, .get_rate = minstrel_get_rate, .rate_init = minstrel_rate_init, .alloc = minstrel_alloc, diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 8e783e197e93..4a5bdad9f303 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -678,9 +678,9 @@ minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb) static void minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta, - struct ieee80211_tx_info *info) + void *priv_sta, struct ieee80211_tx_status *st) { + struct ieee80211_tx_info *info = st->info; struct minstrel_ht_sta_priv *msp = priv_sta; struct minstrel_ht_sta *mi = &msp->ht; struct ieee80211_tx_rate *ar = info->status.rates; @@ -690,8 +690,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, int i; if (!msp->is_ht) - return mac80211_minstrel.tx_status_noskb(priv, sband, sta, - &msp->legacy, info); + return mac80211_minstrel.tx_status_ext(priv, sband, + &msp->legacy, st); /* This packet was aggregated but doesn't carry status info */ if ((info->flags & IEEE80211_TX_CTL_AMPDU) && @@ -1374,7 +1374,7 @@ static u32 minstrel_ht_get_expected_throughput(void *priv_sta) static const struct rate_control_ops mac80211_minstrel_ht = { .name = "minstrel_ht", - .tx_status_noskb = minstrel_ht_tx_status, + .tx_status_ext = minstrel_ht_tx_status, .get_rate = minstrel_ht_get_rate, .rate_init = minstrel_ht_rate_init, .rate_update = minstrel_ht_rate_update, diff --git a/net/mac80211/status.c b/net/mac80211/status.c index fac191d6dcb7..e655b3abb84a 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -637,6 +637,7 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw, { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_supported_band *sband; + struct ieee80211_tx_status status = {}; int retry_count; bool acked, noack_success; @@ -669,7 +670,9 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw, ieee80211_lost_packet(sta, info); } - rate_control_tx_status_noskb(local, sband, sta, info); + status.sta = pubsta; + status.info = info; + rate_control_tx_status(local, sband, &status); } if (acked || noack_success) { @@ -748,6 +751,7 @@ 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); + struct ieee80211_tx_status status = {}; __le16 fc; struct ieee80211_supported_band *sband; struct rhlist_head *tmp; @@ -857,7 +861,10 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) } } - rate_control_tx_status(local, sband, sta, skb); + status.sta = &sta->sta; + status.skb = skb; + status.info = info; + rate_control_tx_status(local, sband, &status); if (ieee80211_vif_is_mesh(&sta->sdata->vif)) ieee80211s_update_metric(local, sta, skb); -- cgit From eefebd3164604a19744dca3df08e050999031d74 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 26 Apr 2017 17:11:36 +0200 Subject: mac80211: move ieee80211_tx_status_noskb below ieee80211_tx_status Makes further cleanups more readable Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/status.c | 116 +++++++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/net/mac80211/status.c b/net/mac80211/status.c index e655b3abb84a..2b3f02f56db3 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -631,64 +631,6 @@ static int ieee80211_tx_get_rates(struct ieee80211_hw *hw, return rates_idx; } -void ieee80211_tx_status_noskb(struct ieee80211_hw *hw, - struct ieee80211_sta *pubsta, - struct ieee80211_tx_info *info) -{ - struct ieee80211_local *local = hw_to_local(hw); - struct ieee80211_supported_band *sband; - struct ieee80211_tx_status status = {}; - int retry_count; - bool acked, noack_success; - - ieee80211_tx_get_rates(hw, info, &retry_count); - - sband = hw->wiphy->bands[info->band]; - - acked = !!(info->flags & IEEE80211_TX_STAT_ACK); - noack_success = !!(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED); - - if (pubsta) { - struct sta_info *sta; - - sta = container_of(pubsta, struct sta_info, sta); - - if (!acked) - sta->status_stats.retry_failed++; - sta->status_stats.retry_count += retry_count; - - if (acked) { - sta->status_stats.last_ack = jiffies; - - if (sta->status_stats.lost_packets) - sta->status_stats.lost_packets = 0; - - /* Track when last TDLS packet was ACKed */ - if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) - sta->status_stats.last_tdls_pkt_time = jiffies; - } else { - ieee80211_lost_packet(sta, info); - } - - status.sta = pubsta; - status.info = info; - rate_control_tx_status(local, sband, &status); - } - - if (acked || noack_success) { - I802_DEBUG_INC(local->dot11TransmittedFrameCount); - if (!pubsta) - I802_DEBUG_INC(local->dot11MulticastTransmittedFrameCount); - if (retry_count > 0) - I802_DEBUG_INC(local->dot11RetryCount); - if (retry_count > 1) - I802_DEBUG_INC(local->dot11MultipleRetryCount); - } else { - I802_DEBUG_INC(local->dot11FailedCount); - } -} -EXPORT_SYMBOL(ieee80211_tx_status_noskb); - 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) @@ -959,6 +901,64 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) } EXPORT_SYMBOL(ieee80211_tx_status); +void ieee80211_tx_status_noskb(struct ieee80211_hw *hw, + struct ieee80211_sta *pubsta, + struct ieee80211_tx_info *info) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_supported_band *sband; + struct ieee80211_tx_status status = {}; + int retry_count; + bool acked, noack_success; + + ieee80211_tx_get_rates(hw, info, &retry_count); + + sband = hw->wiphy->bands[info->band]; + + acked = !!(info->flags & IEEE80211_TX_STAT_ACK); + noack_success = !!(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED); + + if (pubsta) { + struct sta_info *sta; + + sta = container_of(pubsta, struct sta_info, sta); + + if (!acked) + sta->status_stats.retry_failed++; + sta->status_stats.retry_count += retry_count; + + if (acked) { + sta->status_stats.last_ack = jiffies; + + if (sta->status_stats.lost_packets) + sta->status_stats.lost_packets = 0; + + /* Track when last TDLS packet was ACKed */ + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) + sta->status_stats.last_tdls_pkt_time = jiffies; + } else { + ieee80211_lost_packet(sta, info); + } + + status.sta = pubsta; + status.info = info; + rate_control_tx_status(local, sband, &status); + } + + if (acked || noack_success) { + I802_DEBUG_INC(local->dot11TransmittedFrameCount); + if (!pubsta) + I802_DEBUG_INC(local->dot11MulticastTransmittedFrameCount); + if (retry_count > 0) + I802_DEBUG_INC(local->dot11RetryCount); + if (retry_count > 1) + I802_DEBUG_INC(local->dot11MultipleRetryCount); + } else { + I802_DEBUG_INC(local->dot11FailedCount); + } +} +EXPORT_SYMBOL(ieee80211_tx_status_noskb); + void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); -- cgit From 5fe49a9d11644f4aa9034c7eedbcfc6e52373e10 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 26 Apr 2017 17:11:37 +0200 Subject: mac80211: add ieee80211_tx_status_ext This allows the driver to pass in struct ieee80211_tx_status directly. Make ieee80211_tx_status_noskb a wrapper around it. As with ieee80211_tx_status_noskb, there is no _ni variant of this call, because it probably won't be needed. Even if the driver won't provide any extra status info other than what's in struct ieee80211_tx_info already, it can optimize status reporting this way by passing in the station pointer. Signed-off-by: Felix Fietkau [use C99 initializers] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 31 +++++++++++++++++++-- net/mac80211/status.c | 74 +++++++++++++++++++++++++++++++------------------- 2 files changed, 74 insertions(+), 31 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index c9ba79afb136..807ee6cd903f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4213,6 +4213,23 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif, void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb); +/** + * ieee80211_tx_status_ext - extended transmit status callback + * + * This function can be used as a replacement for ieee80211_tx_status + * in drivers that may want to provide extra information that does not + * fit into &struct ieee80211_tx_info. + * + * Calls to this function for a single hardware must be synchronized + * against each other. Calls to this function, ieee80211_tx_status_ni() + * and ieee80211_tx_status_irqsafe() may not be mixed for a single hardware. + * + * @hw: the hardware the frame was transmitted by + * @status: tx status information + */ +void ieee80211_tx_status_ext(struct ieee80211_hw *hw, + struct ieee80211_tx_status *status); + /** * ieee80211_tx_status_noskb - transmit status callback without skb * @@ -4229,9 +4246,17 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, * (NULL for multicast packets) * @info: tx status information */ -void ieee80211_tx_status_noskb(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, - struct ieee80211_tx_info *info); +static inline void ieee80211_tx_status_noskb(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct ieee80211_tx_info *info) +{ + struct ieee80211_tx_status status = { + .sta = sta, + .info = info, + }; + + ieee80211_tx_status_ext(hw, &status); +} /** * ieee80211_tx_status_ni - transmit status callback (in process context) diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 2b3f02f56db3..be47ac5cd8c8 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -688,16 +688,16 @@ void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb, dev_kfree_skb(skb); } -void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) +static void __ieee80211_tx_status(struct ieee80211_hw *hw, + struct ieee80211_tx_status *status) { + struct sk_buff *skb = status->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); - struct ieee80211_tx_status status = {}; + struct ieee80211_tx_info *info = status->info; + struct sta_info *sta; __le16 fc; struct ieee80211_supported_band *sband; - struct rhlist_head *tmp; - struct sta_info *sta; int retry_count; int rates_idx; bool send_to_cooked; @@ -708,16 +708,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); - rcu_read_lock(); - sband = local->hw.wiphy->bands[info->band]; fc = hdr->frame_control; - for_each_sta_info(local, hdr->addr1, sta, tmp) { - /* skip wrong virtual interface */ - if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr)) - continue; - + if (status->sta) { + sta = container_of(status->sta, struct sta_info, sta); shift = ieee80211_vif_get_shift(&sta->sdata->vif); if (info->flags & IEEE80211_TX_STATUS_EOSP) @@ -737,7 +732,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) * that this TX packet failed because of that. */ ieee80211_handle_filtered_frame(local, sta, skb); - rcu_read_unlock(); return; } @@ -787,7 +781,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) { ieee80211_handle_filtered_frame(local, sta, skb); - rcu_read_unlock(); return; } else { if (!acked) @@ -803,10 +796,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) } } - status.sta = &sta->sta; - status.skb = skb; - status.info = info; - rate_control_tx_status(local, sband, &status); + rate_control_tx_status(local, sband, status); if (ieee80211_vif_is_mesh(&sta->sdata->vif)) ieee80211s_update_metric(local, sta, skb); @@ -833,8 +823,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) } } - rcu_read_unlock(); - ieee80211_led_tx(local); /* SNMP counters @@ -899,18 +887,50 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) /* send to monitor interfaces */ ieee80211_tx_monitor(local, skb, sband, retry_count, shift, send_to_cooked); } + +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_status status = { + .skb = skb, + .info = IEEE80211_SKB_CB(skb), + }; + struct rhlist_head *tmp; + struct sta_info *sta; + + rcu_read_lock(); + + for_each_sta_info(local, hdr->addr1, sta, tmp) { + /* skip wrong virtual interface */ + if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr)) + continue; + + status.sta = &sta->sta; + break; + } + + __ieee80211_tx_status(hw, &status); + rcu_read_unlock(); +} EXPORT_SYMBOL(ieee80211_tx_status); -void ieee80211_tx_status_noskb(struct ieee80211_hw *hw, - struct ieee80211_sta *pubsta, - struct ieee80211_tx_info *info) +void ieee80211_tx_status_ext(struct ieee80211_hw *hw, + struct ieee80211_tx_status *status) { struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_tx_info *info = status->info; + struct ieee80211_sta *pubsta = status->sta; struct ieee80211_supported_band *sband; - struct ieee80211_tx_status status = {}; int retry_count; bool acked, noack_success; + if (status->skb) + return __ieee80211_tx_status(hw, status); + + if (!status->sta) + return; + ieee80211_tx_get_rates(hw, info, &retry_count); sband = hw->wiphy->bands[info->band]; @@ -940,9 +960,7 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw, ieee80211_lost_packet(sta, info); } - status.sta = pubsta; - status.info = info; - rate_control_tx_status(local, sband, &status); + rate_control_tx_status(local, sband, status); } if (acked || noack_success) { @@ -957,7 +975,7 @@ void ieee80211_tx_status_noskb(struct ieee80211_hw *hw, I802_DEBUG_INC(local->dot11FailedCount); } } -EXPORT_SYMBOL(ieee80211_tx_status_noskb); +EXPORT_SYMBOL(ieee80211_tx_status_ext); void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets) { -- cgit From 65ba101ebc3b80500882b6bf3502823e24a99f90 Mon Sep 17 00:00:00 2001 From: Aaron Conole Date: Mon, 10 Apr 2017 15:50:44 -0400 Subject: ipvs: remove unused function ip_vs_set_state_timeout There are no in-tree callers of this function and it isn't exported. Signed-off-by: Aaron Conole Signed-off-by: Simon Horman --- include/net/ip_vs.h | 2 -- net/netfilter/ipvs/ip_vs_proto.c | 22 ---------------------- 2 files changed, 24 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 632082300e77..4f4f786255ef 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -1349,8 +1349,6 @@ int ip_vs_protocol_init(void); void ip_vs_protocol_cleanup(void); void ip_vs_protocol_timeout_change(struct netns_ipvs *ipvs, int flags); int *ip_vs_create_timeout_table(int *table, int size); -int ip_vs_set_state_timeout(int *table, int num, const char *const *names, - const char *name, int to); void ip_vs_tcpudp_debug_packet(int af, struct ip_vs_protocol *pp, const struct sk_buff *skb, int offset, const char *msg); diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c index 8ae480715cea..ca880a3ad033 100644 --- a/net/netfilter/ipvs/ip_vs_proto.c +++ b/net/netfilter/ipvs/ip_vs_proto.c @@ -193,28 +193,6 @@ ip_vs_create_timeout_table(int *table, int size) } -/* - * Set timeout value for state specified by name - */ -int -ip_vs_set_state_timeout(int *table, int num, const char *const *names, - const char *name, int to) -{ - int i; - - if (!table || !name || !to) - return -EINVAL; - - for (i = 0; i < num; i++) { - if (strcmp(names[i], name)) - continue; - table[i] = to * HZ; - return 0; - } - return -ENOENT; -} - - const char * ip_vs_state_name(__u16 proto, int state) { struct ip_vs_protocol *pp = ip_vs_proto_get(proto); -- cgit From fb90e8dedb465bd06512f718b139ed8680d26dbe Mon Sep 17 00:00:00 2001 From: Aaron Conole Date: Wed, 12 Apr 2017 16:38:12 -0400 Subject: ipvs: change comparison on sync_refresh_period The sync_refresh_period variable is unsigned, so it can never be < 0. Signed-off-by: Aaron Conole Signed-off-by: Simon Horman --- net/netfilter/ipvs/ip_vs_sync.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index 30d6b2cc00a0..0e5b64a75da0 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -520,7 +520,7 @@ static int ip_vs_sync_conn_needed(struct netns_ipvs *ipvs, if (!(cp->flags & IP_VS_CONN_F_TEMPLATE) && pkts % sync_period != sysctl_sync_threshold(ipvs)) return 0; - } else if (sync_refresh_period <= 0 && + } else if (!sync_refresh_period && pkts != sysctl_sync_threshold(ipvs)) return 0; -- cgit From 21a8e9dd52b64f0170bad208293ef8c30c3c1403 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Thu, 27 Apr 2017 12:45:38 +0530 Subject: mac80211: Fix possible sband related NULL pointer de-reference Existing API 'ieee80211_get_sdata_band' returns default 2 GHz band even if the channel context configuration is NULL. This crashes for chipsets which support 5 Ghz alone when it tries to access members of 'sband'. Channel context configuration can be NULL in multivif case and when channel switch is in progress (or) when it fails. Fix this by replacing the API 'ieee80211_get_sdata_band' with 'ieee80211_get_sband' which returns a NULL pointer for sband when the channel configuration is NULL. An example scenario is as below: In multivif mode (AP + STA) with drivers like ath10k, when we do a channel switch in the AP vif (which has a number of clients connected) and a STA vif which is connected to some other AP, when the channel switch in AP vif fails, while the STA vifs tries to connect to the other AP, there is a window where the channel context is NULL/invalid and this results in a crash while the clients connected to the AP vif tries to reconnect and this race is very similar to the one investigated by Michal in https://patchwork.kernel.org/patch/3788161/ and this does happens with hardware that supports 5Ghz alone after long hours of testing with continuous channel switch on the AP vif ieee80211 phy0: channel context reservation cannot be finalized because some interfaces aren't switching wlan0: failed to finalize CSA, disconnecting wlan0-1: deauthenticating from 8c:fd:f0:01:54:9c by local choice (Reason: 3=DEAUTH_LEAVING) WARNING: CPU: 1 PID: 19032 at net/mac80211/ieee80211_i.h:1013 sta_info_alloc+0x374/0x3fc [mac80211] [] (sta_info_alloc [mac80211]) [] (ieee80211_add_station [mac80211])) [] (nl80211_new_station [cfg80211]) Unable to handle kernel NULL pointer dereference at virtual address 00000014 pgd = d5f4c000 Internal error: Oops: 17 [#1] PREEMPT SMP ARM PC is at sta_info_alloc+0x380/0x3fc [mac80211] LR is at sta_info_alloc+0x37c/0x3fc [mac80211] [] (sta_info_alloc [mac80211]) [] (ieee80211_add_station [mac80211]) [] (nl80211_new_station [cfg80211])) Cc: Michal Kazior Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 30 +++++++++++++++++------------- net/mac80211/ibss.c | 6 +++++- net/mac80211/ieee80211_i.h | 36 +++++++++++++++++++++--------------- net/mac80211/mesh.c | 29 ++++++++++++++++++++--------- net/mac80211/mesh_plink.c | 37 ++++++++++++++++++++++++++----------- net/mac80211/mlme.c | 14 ++++++++++++-- net/mac80211/rate.c | 4 +++- net/mac80211/sta_info.c | 13 +++++++++---- net/mac80211/tdls.c | 29 +++++++++++++++++++---------- net/mac80211/tx.c | 5 ++++- net/mac80211/util.c | 6 +++--- 11 files changed, 139 insertions(+), 70 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e4a370b42a93..6c2e6060cd54 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -660,10 +660,11 @@ void sta_set_rate_info_tx(struct sta_info *sta, int shift = ieee80211_vif_get_shift(&sta->sdata->vif); u16 brate; - sband = sta->local->hw.wiphy->bands[ - ieee80211_get_sdata_band(sta->sdata)]; - brate = sband->bitrates[rate->idx].bitrate; - rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift); + sband = ieee80211_get_sband(sta->sdata); + if (sband) { + brate = sband->bitrates[rate->idx].bitrate; + rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift); + } } if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) rinfo->bw = RATE_INFO_BW_40; @@ -1253,10 +1254,11 @@ static int sta_apply_parameters(struct ieee80211_local *local, int ret = 0; struct ieee80211_supported_band *sband; struct ieee80211_sub_if_data *sdata = sta->sdata; - enum nl80211_band band = ieee80211_get_sdata_band(sdata); u32 mask, set; - sband = local->hw.wiphy->bands[band]; + sband = ieee80211_get_sband(sdata); + if (!sband) + return -EINVAL; mask = params->sta_flags_mask; set = params->sta_flags_set; @@ -1389,7 +1391,7 @@ static int sta_apply_parameters(struct ieee80211_local *local, ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef, sband, params->supported_rates, params->supported_rates_len, - &sta->sta.supp_rates[band]); + &sta->sta.supp_rates[sband->band]); } if (params->ht_capa) @@ -1405,8 +1407,8 @@ static int sta_apply_parameters(struct ieee80211_local *local, /* returned value is only needed for rc update, but the * rc isn't initialized here yet, so ignore it */ - __ieee80211_vht_handle_opmode(sdata, sta, - params->opmode_notif, band); + __ieee80211_vht_handle_opmode(sdata, sta, params->opmode_notif, + sband->band); } if (params->support_p2p_ps >= 0) @@ -2044,13 +2046,15 @@ static int ieee80211_change_bss(struct wiphy *wiphy, struct bss_parameters *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - enum nl80211_band band; + struct ieee80211_supported_band *sband; u32 changed = 0; if (!sdata_dereference(sdata->u.ap.beacon, sdata)) return -ENOENT; - band = ieee80211_get_sdata_band(sdata); + sband = ieee80211_get_sband(sdata); + if (!sband) + return -EINVAL; if (params->use_cts_prot >= 0) { sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot; @@ -2063,7 +2067,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy, } if (!sdata->vif.bss_conf.use_short_slot && - band == NL80211_BAND_5GHZ) { + sband->band == NL80211_BAND_5GHZ) { sdata->vif.bss_conf.use_short_slot = true; changed |= BSS_CHANGED_ERP_SLOT; } @@ -2076,7 +2080,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy, if (params->basic_rates) { ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef, - wiphy->bands[band], + wiphy->bands[sband->band], params->basic_rates, params->basic_rates_len, &sdata->vif.bss_conf.basic_rates); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 9d0f6100942b..6db09fa18269 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -992,7 +992,7 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, enum nl80211_band band = rx_status->band; enum nl80211_bss_scan_width scan_width; struct ieee80211_local *local = sdata->local; - struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; + struct ieee80211_supported_band *sband; bool rates_updated = false; u32 supp_rates = 0; @@ -1002,6 +1002,10 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, if (!ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid)) return; + sband = local->hw.wiphy->bands[band]; + if (WARN_ON(!sband)) + return; + rcu_read_lock(); sta = sta_info_get(sdata, mgmt->sa); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 47d709afea25..2a5730573aa3 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1001,21 +1001,6 @@ sdata_assert_lock(struct ieee80211_sub_if_data *sdata) lockdep_assert_held(&sdata->wdev.mtx); } -static inline enum nl80211_band -ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata) -{ - enum nl80211_band band = NL80211_BAND_2GHZ; - struct ieee80211_chanctx_conf *chanctx_conf; - - rcu_read_lock(); - chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (!WARN_ON(!chanctx_conf)) - band = chanctx_conf->def.chan->band; - rcu_read_unlock(); - - return band; -} - static inline int ieee80211_chandef_get_shift(struct cfg80211_chan_def *chandef) { @@ -1421,6 +1406,27 @@ IEEE80211_WDEV_TO_SUB_IF(struct wireless_dev *wdev) return container_of(wdev, struct ieee80211_sub_if_data, wdev); } +static inline struct ieee80211_supported_band * +ieee80211_get_sband(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *chanctx_conf; + enum nl80211_band band; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + + if (WARN_ON(!chanctx_conf)) { + rcu_read_unlock(); + return NULL; + } + + band = chanctx_conf->def.chan->band; + rcu_read_unlock(); + + return local->hw.wiphy->bands[band]; +} + /* this struct represents 802.11n's RA/TID combination */ struct ieee80211_ra_tid { u8 ra[ETH_ALEN]; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 281d834c7548..737e1f082b0d 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -63,6 +63,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u32 basic_rates = 0; struct cfg80211_chan_def sta_chan_def; + struct ieee80211_supported_band *sband; /* * As support for each feature is added, check for matching @@ -83,7 +84,11 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth))) return false; - ieee80211_sta_get_rates(sdata, ie, ieee80211_get_sdata_band(sdata), + sband = ieee80211_get_sband(sdata); + if (!sband) + return false; + + ieee80211_sta_get_rates(sdata, ie, sband->band, &basic_rates); if (sdata->vif.bss_conf.basic_rates != basic_rates) @@ -399,12 +404,13 @@ static int mesh_add_ds_params_ie(struct ieee80211_sub_if_data *sdata, int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { - struct ieee80211_local *local = sdata->local; - enum nl80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_supported_band *sband; u8 *pos; - sband = local->hw.wiphy->bands[band]; + sband = ieee80211_get_sband(sdata); + if (!sband) + return -EINVAL; + if (!sband->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 || @@ -462,12 +468,13 @@ int mesh_add_ht_oper_ie(struct ieee80211_sub_if_data *sdata, int mesh_add_vht_cap_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { - struct ieee80211_local *local = sdata->local; - enum nl80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_supported_band *sband; u8 *pos; - sband = local->hw.wiphy->bands[band]; + sband = ieee80211_get_sband(sdata); + if (!sband) + return -EINVAL; + 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 || @@ -916,12 +923,16 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, struct cfg80211_csa_settings params; struct ieee80211_csa_ie csa_ie; struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - enum nl80211_band band = ieee80211_get_sdata_band(sdata); + struct ieee80211_supported_band *sband; int err; u32 sta_flags; sdata_assert_lock(sdata); + sband = ieee80211_get_sband(sdata); + if (!sband) + return false; + sta_flags = IEEE80211_STA_DISABLE_VHT; switch (sdata->vif.bss_conf.chandef.width) { case NL80211_CHAN_WIDTH_20_NOHT: @@ -935,7 +946,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, memset(¶ms, 0, sizeof(params)); memset(&csa_ie, 0, sizeof(csa_ie)); - err = ieee80211_parse_ch_switch_ie(sdata, elems, band, + err = ieee80211_parse_ch_switch_ie(sdata, elems, sband->band, sta_flags, sdata->vif.addr, &csa_ie); if (err < 0) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 953d71e784a9..1131cd504a15 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -95,19 +95,23 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta) static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; - enum nl80211_band band = ieee80211_get_sdata_band(sdata); - struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; + struct ieee80211_supported_band *sband; struct sta_info *sta; u32 erp_rates = 0, changed = 0; int i; bool short_slot = false; - if (band == NL80211_BAND_5GHZ) { + sband = ieee80211_get_sband(sdata); + if (!sband) + return changed; + + if (sband->band == NL80211_BAND_5GHZ) { /* (IEEE 802.11-2012 19.4.5) */ short_slot = true; goto out; - } else if (band != NL80211_BAND_2GHZ) + } else if (sband->band != NL80211_BAND_2GHZ) { goto out; + } for (i = 0; i < sband->n_bitrates; i++) if (sband->bitrates[i].flags & IEEE80211_RATE_ERP_G) @@ -123,7 +127,7 @@ static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata) continue; short_slot = false; - if (erp_rates & sta->sta.supp_rates[band]) + if (erp_rates & sta->sta.supp_rates[sband->band]) short_slot = true; else break; @@ -249,7 +253,15 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, mgmt->u.action.u.self_prot.action_code = action; if (action != WLAN_SP_MESH_PEERING_CLOSE) { - enum nl80211_band band = ieee80211_get_sdata_band(sdata); + struct ieee80211_supported_band *sband; + enum nl80211_band band; + + sband = ieee80211_get_sband(sdata); + if (!sband) { + err = -EINVAL; + goto free; + } + band = sband->band; /* capability info */ pos = skb_put(skb, 2); @@ -395,13 +407,16 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *elems, bool insert) { struct ieee80211_local *local = sdata->local; - enum nl80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_supported_band *sband; u32 rates, basic_rates = 0, changed = 0; enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth; - sband = local->hw.wiphy->bands[band]; - rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates); + sband = ieee80211_get_sband(sdata); + if (!sband) + return; + + rates = ieee80211_sta_get_rates(sdata, elems, sband->band, + &basic_rates); spin_lock_bh(&sta->mesh->plink_lock); sta->rx_stats.last_rx = jiffies; @@ -412,9 +427,9 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, goto out; sta->mesh->processed_beacon = true; - if (sta->sta.supp_rates[band] != rates) + if (sta->sta.supp_rates[sband->band] != rates) changed |= IEEE80211_RC_SUPP_RATES_CHANGED; - sta->sta.supp_rates[band] = rates; + sta->sta.supp_rates[sband->band] = rates; if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, elems->ht_cap_elem, sta)) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 24d69bcf71ad..45d80fe61c5f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1855,11 +1855,16 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, u16 capab, bool erp_valid, u8 erp) { struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; + struct ieee80211_supported_band *sband; u32 changed = 0; bool use_protection; bool use_short_preamble; bool use_short_slot; + sband = ieee80211_get_sband(sdata); + if (!sband) + return changed; + if (erp_valid) { use_protection = (erp & WLAN_ERP_USE_PROTECTION) != 0; use_short_preamble = (erp & WLAN_ERP_BARKER_PREAMBLE) == 0; @@ -1869,7 +1874,7 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, } use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME); - if (ieee80211_get_sdata_band(sdata) == NL80211_BAND_5GHZ) + if (sband->band == NL80211_BAND_5GHZ) use_short_slot = true; if (use_protection != bss_conf->use_cts_prot) { @@ -3004,7 +3009,12 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, goto out; } - sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; + sband = ieee80211_get_sband(sdata); + if (!sband) { + mutex_unlock(&sdata->local->sta_mtx); + ret = false; + goto out; + } /* Set up internal HT/VHT capabilities */ if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index b387c07b8b47..ea1f4315c521 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -926,7 +926,9 @@ int rate_control_set_rates(struct ieee80211_hw *hw, struct ieee80211_sta_rates *old; struct ieee80211_supported_band *sband; - sband = hw->wiphy->bands[ieee80211_get_sdata_band(sta->sdata)]; + sband = ieee80211_get_sband(sta->sdata); + if (!sband) + return -EINVAL; rate_control_apply_mask_ratetbl(sta, sband, rates); /* * mac80211 guarantees that this function will not be called diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 464566c662c5..7cdf7a835bb0 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -395,10 +395,15 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta->sta.smps_mode = IEEE80211_SMPS_OFF; if (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { - struct ieee80211_supported_band *sband = - hw->wiphy->bands[ieee80211_get_sdata_band(sdata)]; - u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> - IEEE80211_HT_CAP_SM_PS_SHIFT; + struct ieee80211_supported_band *sband; + u8 smps; + + sband = ieee80211_get_sband(sdata); + if (!sband) + goto free_txq; + + smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> + IEEE80211_HT_CAP_SM_PS_SHIFT; /* * Assume that hostapd advertises our caps in the beacon and * this is the known_smps_mode for a station that just assciated diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index afca7d103684..f20dcf1b1830 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -47,8 +47,7 @@ static void ieee80211_tdls_add_ext_capab(struct ieee80211_sub_if_data *sdata, NL80211_FEATURE_TDLS_CHANNEL_SWITCH; bool wider_band = ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) && !ifmgd->tdls_wider_bw_prohibited; - enum nl80211_band band = ieee80211_get_sdata_band(sdata); - struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; + struct ieee80211_supported_band *sband = ieee80211_get_sband(sdata); bool vht = sband && sband->vht_cap.vht_supported; u8 *pos = (void *)skb_put(skb, 10); @@ -180,11 +179,14 @@ static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb) static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata, u16 status_code) { + struct ieee80211_supported_band *sband; + /* The capability will be 0 when sending a failure code */ if (status_code != 0) return 0; - if (ieee80211_get_sdata_band(sdata) == NL80211_BAND_2GHZ) { + sband = ieee80211_get_sband(sdata); + if (sband && sband->band == NL80211_BAND_2GHZ) { return WLAN_CAPABILITY_SHORT_SLOT_TIME | WLAN_CAPABILITY_SHORT_PREAMBLE; } @@ -358,17 +360,20 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, u8 action_code, bool initiator, const u8 *extra_ies, size_t extra_ies_len) { - enum nl80211_band band = ieee80211_get_sdata_band(sdata); - struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; + struct ieee80211_local *local = sdata->local; struct ieee80211_sta_ht_cap ht_cap; struct ieee80211_sta_vht_cap vht_cap; struct sta_info *sta = NULL; size_t offset = 0, noffset; u8 *pos; - ieee80211_add_srates_ie(sdata, skb, false, band); - ieee80211_add_ext_srates_ie(sdata, skb, false, band); + sband = ieee80211_get_sband(sdata); + if (!sband) + return; + + ieee80211_add_srates_ie(sdata, skb, false, sband->band); + ieee80211_add_ext_srates_ie(sdata, skb, false, sband->band); ieee80211_tdls_add_supp_channels(sdata, skb); /* add any custom IEs that go before Extended Capabilities */ @@ -439,7 +444,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, * the same on all bands. The specification limits the setup to a * single HT-cap, so use the current band for now. */ - sband = local->hw.wiphy->bands[band]; memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); if ((action_code == WLAN_TDLS_SETUP_REQUEST || @@ -545,9 +549,13 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; size_t offset = 0, noffset; struct sta_info *sta, *ap_sta; - enum nl80211_band band = ieee80211_get_sdata_band(sdata); + struct ieee80211_supported_band *sband; u8 *pos; + sband = ieee80211_get_sband(sdata); + if (!sband) + return; + mutex_lock(&local->sta_mtx); sta = sta_info_get(sdata, peer); @@ -612,7 +620,8 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); /* only include VHT-operation if not on the 2.4GHz band */ - if (band != NL80211_BAND_2GHZ && sta->sta.vht_cap.vht_supported) { + if (sband->band != NL80211_BAND_2GHZ && + sta->sta.vht_cap.vht_supported) { /* * if both peers support WIDER_BW, we can expand the chandef to * a wider compatible one, up to 80MHz diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index f27719eeeed7..04b22f8982fe 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -4297,7 +4297,10 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, return bcn; shift = ieee80211_vif_get_shift(vif); - sband = hw->wiphy->bands[ieee80211_get_sdata_band(vif_to_sdata(vif))]; + sband = ieee80211_get_sband(vif_to_sdata(vif)); + if (!sband) + return bcn; + ieee80211_tx_monitor(hw_to_local(hw), copy, sband, 1, shift, false); return bcn; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 83342b73e9aa..4a5414481b78 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1590,14 +1590,14 @@ u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, size_t num_rates; u32 supp_rates, rate_flags; int i, j, shift; + sband = sdata->local->hw.wiphy->bands[band]; + if (WARN_ON(!sband)) + return 1; rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); shift = ieee80211_vif_get_shift(&sdata->vif); - if (WARN_ON(!sband)) - return 1; - num_rates = sband->n_bitrates; supp_rates = 0; for (i = 0; i < elems->supp_rates_len + -- cgit From 29ce6ecbb83c9185d76e3a7c340c9702d2a54961 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 26 Apr 2017 10:58:49 +0300 Subject: cfg80211: unify cfg80211_roamed() and cfg80211_roamed_bss() cfg80211_roamed() and cfg80211_roamed_bss() take the same arguments except that cfg80211_roamed() requires the BSSID and cfg80211_roamed_bss() requires the bss entry. Unify the two functions by using a struct for driver initiated roaming information so that either the BSSID or the bss entry can be passed as an argument to the unified function. Signed-off-by: Avraham Stern [modified the ath6k, brcm80211, rndis and wlan-ng drivers accordingly] Signed-off-by: Luca Coelho [modify brcmfmac to remove the useless cast, spotted by Arend] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 10 ++- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 13 +++- drivers/net/wireless/rndis_wlan.c | 19 +++-- drivers/staging/wlan-ng/cfg80211.c | 7 +- include/net/cfg80211.h | 58 +++++++------- net/wireless/core.h | 12 +-- net/wireless/nl80211.c | 18 +++-- net/wireless/nl80211.h | 5 +- net/wireless/sme.c | 90 +++++++++------------- net/wireless/util.c | 4 +- 10 files changed, 115 insertions(+), 121 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index fd53ffb1f014..0bc42fcceb74 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -806,9 +806,15 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, WLAN_STATUS_SUCCESS, GFP_KERNEL); cfg80211_put_bss(ar->wiphy, bss); } else if (vif->sme_state == SME_CONNECTED) { + struct cfg80211_roam_info roam_info = { + .bss = bss, + .req_ie = assoc_req_ie, + .req_ie_len = assoc_req_len, + .resp_ie = assoc_resp_ie, + .resp_ie_len = assoc_resp_len, + }; /* inform roam event to cfg80211 */ - cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len, - assoc_resp_ie, assoc_resp_len, GFP_KERNEL); + cfg80211_roamed(vif->ndev, &roam_info, GFP_KERNEL); } } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 130f0d4eda06..b43cccfb66b6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -5359,6 +5359,7 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, struct ieee80211_supported_band *band; struct brcmf_bss_info_le *bi; struct brcmu_chan ch; + struct cfg80211_roam_info roam_info = {}; u32 freq; s32 err = 0; u8 *buf; @@ -5397,9 +5398,15 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, done: kfree(buf); - cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid, - conn_info->req_ie, conn_info->req_ie_len, - conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); + + roam_info.channel = notify_channel; + roam_info.bssid = profile->bssid; + roam_info.req_ie = conn_info->req_ie; + roam_info.req_ie_len = conn_info->req_ie_len; + roam_info.resp_ie = conn_info->resp_ie; + roam_info.resp_ie_len = conn_info->resp_ie_len; + + cfg80211_roamed(ndev, &roam_info, GFP_KERNEL); brcmf_dbg(CONN, "Report roaming result\n"); set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index eb513628d801..37ae24cbf00e 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2830,15 +2830,22 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev) } if (priv->infra_mode == NDIS_80211_INFRA_INFRA) { - if (!roamed) + if (!roamed) { cfg80211_connect_result(usbdev->net, bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, 0, GFP_KERNEL); - else - cfg80211_roamed(usbdev->net, - get_current_channel(usbdev, NULL), - bssid, req_ie, req_ie_len, - resp_ie, resp_ie_len, GFP_KERNEL); + } else { + struct cfg80211_roam_info roam_info = { + .channel = get_current_channel(usbdev, NULL), + .bssid = bssid, + .req_ie = req_ie, + .req_ie_len = req_ie_len, + .resp_ie = resp_ie, + .resp_ie_len = resp_ie_len, + }; + + cfg80211_roamed(usbdev->net, &roam_info, GFP_KERNEL); + } } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) cfg80211_ibss_joined(usbdev->net, bssid, get_current_channel(usbdev, NULL), diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index cbb3388a9756..178f6f5d4613 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -666,8 +666,11 @@ void prism2_disconnected(struct wlandevice *wlandev) void prism2_roamed(struct wlandevice *wlandev) { - cfg80211_roamed(wlandev->netdev, NULL, wlandev->bssid, - NULL, 0, NULL, 0, GFP_KERNEL); + struct cfg80211_roam_info roam_info = { + .bssid = wlandev->bssid, + }; + + cfg80211_roamed(wlandev->netdev, &roam_info, GFP_KERNEL); } /* Structures for declaring wiphy interface */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index aa663f70969c..52e810fe0833 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2686,8 +2686,7 @@ struct cfg80211_nan_func { * indication of requesting reassociation. * In both the driver-initiated and new connect() call initiated roaming * cases, the result of roaming is indicated with a call to - * cfg80211_roamed() or cfg80211_roamed_bss(). - * (invoked with the wireless_dev mutex held) + * cfg80211_roamed(). (invoked with the wireless_dev mutex held) * @update_connect_params: Update the connect parameters while connected to a * BSS. The updated parameters can be used by driver/firmware for * subsequent BSS selection (roaming) decisions and to form the @@ -5390,51 +5389,46 @@ cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid, } /** - * cfg80211_roamed - notify cfg80211 of roaming + * struct cfg80211_roam_info - driver initiated roaming information * - * @dev: network device * @channel: the channel of the new AP - * @bssid: the BSSID of the new AP + * @bss: entry of bss to which STA got roamed (may be %NULL if %bssid is set) + * @bssid: the BSSID of the new AP (may be %NULL if %bss is set) * @req_ie: association request IEs (maybe be %NULL) * @req_ie_len: association request IEs length * @resp_ie: association response IEs (may be %NULL) * @resp_ie_len: assoc response IEs length - * @gfp: allocation flags - * - * It should be called by the underlying driver whenever it roamed - * from one AP to another while connected. */ -void cfg80211_roamed(struct net_device *dev, - struct ieee80211_channel *channel, - const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); +struct cfg80211_roam_info { + struct ieee80211_channel *channel; + struct cfg80211_bss *bss; + const u8 *bssid; + const u8 *req_ie; + size_t req_ie_len; + const u8 *resp_ie; + size_t resp_ie_len; +}; /** - * cfg80211_roamed_bss - notify cfg80211 of roaming + * cfg80211_roamed - notify cfg80211 of roaming * * @dev: network device - * @bss: entry of bss to which STA got roamed - * @req_ie: association request IEs (maybe be %NULL) - * @req_ie_len: association request IEs length - * @resp_ie: association response IEs (may be %NULL) - * @resp_ie_len: assoc response IEs length + * @info: information about the new BSS. struct &cfg80211_roam_info. * @gfp: allocation flags * - * This is just a wrapper to notify cfg80211 of roaming event with driver - * passing bss to avoid a race in timeout of the bss entry. It should be - * called by the underlying driver whenever it roamed from one AP to another - * while connected. Drivers which have roaming implemented in firmware - * may use this function to avoid a race in bss entry timeout where the bss - * entry of the new AP is seen in the driver, but gets timed out by the time - * it is accessed in __cfg80211_roamed() due to delay in scheduling + * This function may be called with the driver passing either the BSSID of the + * new AP or passing the bss entry to avoid a race in timeout of the bss entry. + * It should be called by the underlying driver whenever it roamed from one AP + * to another while connected. Drivers which have roaming implemented in + * firmware should pass the bss entry to avoid a race in bss entry timeout where + * the bss entry of the new AP is seen in the driver, but gets timed out by the + * time it is accessed in __cfg80211_roamed() due to delay in scheduling * rdev->event_work. In case of any failures, the reference is released - * either in cfg80211_roamed_bss() or in __cfg80211_romed(), Otherwise, - * it will be released while diconneting from the current bss. + * either in cfg80211_roamed() or in __cfg80211_romed(), Otherwise, it will be + * released while diconneting from the current bss. */ -void cfg80211_roamed_bss(struct net_device *dev, struct cfg80211_bss *bss, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); +void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, + gfp_t gfp); /** * cfg80211_disconnected - notify cfg80211 that connection was dropped diff --git a/net/wireless/core.h b/net/wireless/core.h index 06eaf96053a8..96baffab5e5b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -224,13 +224,7 @@ struct cfg80211_event { union { struct cfg80211_connect_resp_params cr; - struct { - const u8 *req_ie; - const u8 *resp_ie; - size_t req_ie_len; - size_t resp_ie_len; - struct cfg80211_bss *bss; - } rm; + struct cfg80211_roam_info rm; struct { const u8 *ie; size_t ie_len; @@ -390,9 +384,7 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev); void __cfg80211_roamed(struct wireless_dev *wdev, - struct cfg80211_bss *bss, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len); + struct cfg80211_roam_info *info); int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); void cfg80211_autodisconnect_wk(struct work_struct *work); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index dce69a87d4d0..570fc95dc507 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -13646,14 +13646,14 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, } void nl80211_send_roamed(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) + struct net_device *netdev, + struct cfg80211_roam_info *info, gfp_t gfp) { struct sk_buff *msg; void *hdr; + const u8 *bssid = info->bss ? info->bss->bssid : info->bssid; - msg = nlmsg_new(100 + req_ie_len + resp_ie_len, gfp); + msg = nlmsg_new(100 + info->req_ie_len + info->resp_ie_len, gfp); if (!msg) return; @@ -13666,10 +13666,12 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid) || - (req_ie && - nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) || - (resp_ie && - nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie))) + (info->req_ie && + nla_put(msg, NL80211_ATTR_REQ_IE, info->req_ie_len, + info->req_ie)) || + (info->resp_ie && + nla_put(msg, NL80211_ATTR_RESP_IE, info->resp_ie_len, + info->resp_ie))) goto nla_put_failure; genlmsg_end(msg, hdr); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index d5f6860e62ab..b96933322077 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -56,9 +56,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, struct cfg80211_connect_resp_params *params, gfp_t gfp); void nl80211_send_roamed(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); + struct net_device *netdev, + struct cfg80211_roam_info *info, gfp_t gfp); void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct net_device *netdev, u16 reason, const u8 *ie, size_t ie_len, bool from_ap); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 6459bb7c21f7..532a0007ce82 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -5,6 +5,7 @@ * * Copyright 2009 Johannes Berg * Copyright (C) 2009 Intel Corporation. All rights reserved. + * Copyright 2017 Intel Deutschland GmbH */ #include @@ -870,9 +871,7 @@ EXPORT_SYMBOL(cfg80211_connect_done); /* Consumes bss object one way or another */ void __cfg80211_roamed(struct wireless_dev *wdev, - struct cfg80211_bss *bss, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len) + struct cfg80211_roam_info *info) { #ifdef CONFIG_CFG80211_WEXT union iwreq_data wrqu; @@ -890,97 +889,84 @@ void __cfg80211_roamed(struct wireless_dev *wdev, cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); wdev->current_bss = NULL; - cfg80211_hold_bss(bss_from_pub(bss)); - wdev->current_bss = bss_from_pub(bss); + if (WARN_ON(!info->bss)) + return; + + cfg80211_hold_bss(bss_from_pub(info->bss)); + wdev->current_bss = bss_from_pub(info->bss); nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy), - wdev->netdev, bss->bssid, - req_ie, req_ie_len, resp_ie, resp_ie_len, - GFP_KERNEL); + wdev->netdev, info, GFP_KERNEL); #ifdef CONFIG_CFG80211_WEXT - if (req_ie) { + if (info->req_ie) { memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = req_ie_len; + wrqu.data.length = info->req_ie_len; wireless_send_event(wdev->netdev, IWEVASSOCREQIE, - &wrqu, req_ie); + &wrqu, info->req_ie); } - if (resp_ie) { + if (info->resp_ie) { memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = resp_ie_len; + wrqu.data.length = info->resp_ie_len; wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, - &wrqu, resp_ie); + &wrqu, info->resp_ie); } memset(&wrqu, 0, sizeof(wrqu)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN); - memcpy(wdev->wext.prev_bssid, bss->bssid, ETH_ALEN); + memcpy(wrqu.ap_addr.sa_data, info->bss->bssid, ETH_ALEN); + memcpy(wdev->wext.prev_bssid, info->bss->bssid, ETH_ALEN); wdev->wext.prev_bssid_valid = true; wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); #endif return; out: - cfg80211_put_bss(wdev->wiphy, bss); -} - -void cfg80211_roamed(struct net_device *dev, - struct ieee80211_channel *channel, - const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_bss *bss; - - bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, - wdev->ssid_len, - wdev->conn_bss_type, IEEE80211_PRIVACY_ANY); - if (WARN_ON(!bss)) - return; - - cfg80211_roamed_bss(dev, bss, req_ie, req_ie_len, resp_ie, - resp_ie_len, gfp); + cfg80211_put_bss(wdev->wiphy, info->bss); } -EXPORT_SYMBOL(cfg80211_roamed); -/* Consumes bss object one way or another */ -void cfg80211_roamed_bss(struct net_device *dev, - struct cfg80211_bss *bss, const u8 *req_ie, - size_t req_ie_len, const u8 *resp_ie, - size_t resp_ie_len, gfp_t gfp) +/* Consumes info->bss object one way or another */ +void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, + gfp_t gfp) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_event *ev; unsigned long flags; - if (WARN_ON(!bss)) + if (!info->bss) { + info->bss = cfg80211_get_bss(wdev->wiphy, info->channel, + info->bssid, wdev->ssid, + wdev->ssid_len, + wdev->conn_bss_type, + IEEE80211_PRIVACY_ANY); + } + + if (WARN_ON(!info->bss)) return; - ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); + ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len, gfp); if (!ev) { - cfg80211_put_bss(wdev->wiphy, bss); + cfg80211_put_bss(wdev->wiphy, info->bss); return; } ev->type = EVENT_ROAMED; ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); - ev->rm.req_ie_len = req_ie_len; - memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); - ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; - ev->rm.resp_ie_len = resp_ie_len; - memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); - ev->rm.bss = bss; + ev->rm.req_ie_len = info->req_ie_len; + memcpy((void *)ev->rm.req_ie, info->req_ie, info->req_ie_len); + ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + info->req_ie_len; + ev->rm.resp_ie_len = info->resp_ie_len; + memcpy((void *)ev->rm.resp_ie, info->resp_ie, info->resp_ie_len); + ev->rm.bss = info->bss; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); spin_unlock_irqrestore(&wdev->event_lock, flags); queue_work(cfg80211_wq, &rdev->event_work); } -EXPORT_SYMBOL(cfg80211_roamed_bss); +EXPORT_SYMBOL(cfg80211_roamed); void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap) diff --git a/net/wireless/util.c b/net/wireless/util.c index a46bc42d0910..7198373e2920 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -946,9 +946,7 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) ev->cr.status == WLAN_STATUS_SUCCESS); break; case EVENT_ROAMED: - __cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie, - ev->rm.req_ie_len, ev->rm.resp_ie, - ev->rm.resp_ie_len); + __cfg80211_roamed(wdev, &ev->rm); break; case EVENT_DISCONNECTED: __cfg80211_disconnected(wdev->netdev, -- cgit From e38a017bf080d47376db340e94b9c2ffc47eb9b4 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 26 Apr 2017 10:58:47 +0300 Subject: mac80211: Add support for BSS max idle period element Parse the BSS max idle period element and set the BSS configuration accordingly so the driver can use this information to configure the max idle period and to use protected management frames for keep alive when required. The BSS max idle period element is defined in IEEE802.11-2016, section 9.4.2.79 Signed-off-by: Avraham Stern Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 28 +++++++++++++++++++++++++++- include/net/mac80211.h | 14 +++++++++++++- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 14 +++++++++++++- net/mac80211/util.c | 9 +++++++++ 5 files changed, 63 insertions(+), 3 deletions(-) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 639e77abf064..69033353d0d1 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -7,7 +7,7 @@ * Copyright (c) 2005, Devicescape Software, Inc. * Copyright (c) 2006, Michael Wu * Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH - * Copyright (c) 2016 Intel Deutschland GmbH + * Copyright (c) 2016 - 2017 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 @@ -2316,6 +2316,32 @@ struct ieee80211_timeout_interval_ie { __le32 value; } __packed; +/** + * enum ieee80211_idle_options - BSS idle options + * @WLAN_IDLE_OPTIONS_PROTECTED_KEEP_ALIVE: the station should send an RSN + * protected frame to the AP to reset the idle timer at the AP for + * the station. + */ +enum ieee80211_idle_options { + WLAN_IDLE_OPTIONS_PROTECTED_KEEP_ALIVE = BIT(0), +}; + +/** + * struct ieee80211_bss_max_idle_period_ie + * + * This structure refers to "BSS Max idle period element" + * + * @max_idle_period: indicates the time period during which a station can + * refrain from transmitting frames to its associated AP without being + * disassociated. In units of 1000 TUs. + * @idle_options: indicates the options associated with the BSS idle capability + * as specified in &enum ieee80211_idle_options. + */ +struct ieee80211_bss_max_idle_period_ie { + __le16 max_idle_period; + u8 idle_options; +} __packed; + /* BACK action code */ enum ieee80211_back_actioncode { WLAN_ACTION_ADDBA_REQ = 0, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 807ee6cd903f..4d05a9443344 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -5,7 +5,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007-2010 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright (C) 2015 - 2016 Intel Deutschland GmbH + * Copyright (C) 2015 - 2017 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 @@ -299,6 +299,8 @@ struct ieee80211_vif_chanctx_switch { * context had been assigned. * @BSS_CHANGED_OCB: OCB join status changed * @BSS_CHANGED_MU_GROUPS: VHT MU-MIMO group id or user position changed + * @BSS_CHANGED_KEEP_ALIVE: keep alive options (idle period or protected + * keep alive) changed. */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -325,6 +327,7 @@ enum ieee80211_bss_change { BSS_CHANGED_BANDWIDTH = 1<<21, BSS_CHANGED_OCB = 1<<22, BSS_CHANGED_MU_GROUPS = 1<<23, + BSS_CHANGED_KEEP_ALIVE = 1<<24, /* when adding here, make sure to change ieee80211_reconfig */ }; @@ -533,6 +536,13 @@ struct ieee80211_mu_group_data { * @allow_p2p_go_ps: indication for AP or P2P GO interface, whether it's allowed * to use P2P PS mechanism or not. AP/P2P GO is not allowed to use P2P PS * if it has associated clients without P2P PS support. + * @max_idle_period: the time period during which the station can refrain from + * transmitting frames to its associated AP without being disassociated. + * In units of 1000 TUs. Zero value indicates that the AP did not include + * a (valid) BSS Max Idle Period Element. + * @protected_keep_alive: if set, indicates that the station should send an RSN + * protected frame to the AP to reset the idle timer at the AP for the + * station. */ struct ieee80211_bss_conf { const u8 *bssid; @@ -573,6 +583,8 @@ struct ieee80211_bss_conf { enum nl80211_tx_power_setting txpower_type; struct ieee80211_p2p_noa_attr p2p_noa_attr; bool allow_p2p_go_ps; + u16 max_idle_period; + bool protected_keep_alive; }; /** diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2a5730573aa3..f8f6c148f554 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1483,6 +1483,7 @@ struct ieee802_11_elems { const u8 *opmode_notif; const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie; + const struct ieee80211_bss_max_idle_period_ie *max_idle_period_ie; /* length of them, respectively */ u8 ext_capab_len; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 45d80fe61c5f..89dff563b1ec 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -6,7 +6,7 @@ * Copyright 2006-2007 Jiri Benc * Copyright 2007, Michael Wu * Copyright 2013-2014 Intel Mobile Communications GmbH - * Copyright (C) 2015 - 2016 Intel Deutschland GmbH + * Copyright (C) 2015 - 2017 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 @@ -3098,6 +3098,18 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, } changed |= BSS_CHANGED_QOS; + if (elems.max_idle_period_ie) { + bss_conf->max_idle_period = + le16_to_cpu(elems.max_idle_period_ie->max_idle_period); + bss_conf->protected_keep_alive = + !!(elems.max_idle_period_ie->idle_options & + WLAN_IDLE_OPTIONS_PROTECTED_KEEP_ALIVE); + changed |= BSS_CHANGED_KEEP_ALIVE; + } else { + bss_conf->max_idle_period = 0; + bss_conf->protected_keep_alive = false; + } + /* set AID and assoc capability, * ieee80211_set_associated() will tell the driver */ bss_conf->aid = aid; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 4a5414481b78..bfc28053639b 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -828,6 +828,7 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, case WLAN_EID_EXT_CAPABILITY: case WLAN_EID_CHAN_SWITCH_TIMING: case WLAN_EID_LINK_ID: + case WLAN_EID_BSS_MAX_IDLE_PERIOD: /* * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible * that if the content gets bigger it might be needed more than once @@ -1089,6 +1090,10 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, else elem_parse_failed = true; break; + case WLAN_EID_BSS_MAX_IDLE_PERIOD: + if (elen >= sizeof(*elems->max_idle_period_ie)) + elems->max_idle_period_ie = (void *)pos; + break; default: break; } @@ -1983,6 +1988,10 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (sdata->u.mgd.have_beacon) changed |= BSS_CHANGED_BEACON_INFO; + if (sdata->vif.bss_conf.max_idle_period || + sdata->vif.bss_conf.protected_keep_alive) + changed |= BSS_CHANGED_KEEP_ALIVE; + sdata_lock(sdata); ieee80211_bss_info_change_notify(sdata, changed); sdata_unlock(sdata); -- cgit From b34939b9836950d261610132853311054b507247 Mon Sep 17 00:00:00 2001 From: Arend Van Spriel Date: Fri, 28 Apr 2017 13:40:28 +0100 Subject: cfg80211: add request id to cfg80211_sched_scan_*() api Have proper request id filled in the SCHED_SCAN_RESULTS and SCHED_SCAN_STOPPED notifications toward user-space by having the driver provide it through the api. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +- drivers/net/wireless/ath/ath6kl/wmi.c | 2 +- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 4 +- drivers/net/wireless/marvell/mwifiex/cfg80211.c | 6 +-- drivers/net/wireless/marvell/mwifiex/main.c | 2 +- drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c | 2 +- drivers/net/wireless/marvell/mwifiex/sta_event.c | 2 +- drivers/net/wireless/marvell/mwifiex/sta_ioctl.c | 2 +- include/net/cfg80211.h | 11 ++-- net/mac80211/pm.c | 2 +- net/mac80211/scan.c | 4 +- net/mac80211/util.c | 2 +- net/wireless/core.c | 2 +- net/wireless/core.h | 4 +- net/wireless/scan.c | 63 +++++++++++----------- net/wireless/trace.h | 26 ++++++--- 16 files changed, 77 insertions(+), 59 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 0bc42fcceb74..414b5b596efc 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -169,7 +169,7 @@ static void ath6kl_cfg80211_sscan_disable(struct ath6kl_vif *vif) if (!stopped) return; - cfg80211_sched_scan_stopped(ar->wiphy); + cfg80211_sched_scan_stopped(ar->wiphy, 0); } static int ath6kl_set_wpa_version(struct ath6kl_vif *vif, diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index a082de81ec4c..bfc20b45b806 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1082,7 +1082,7 @@ void ath6kl_wmi_sscan_timer(unsigned long ptr) { struct ath6kl_vif *vif = (struct ath6kl_vif *) ptr; - cfg80211_sched_scan_results(vif->ar->wiphy); + cfg80211_sched_scan_results(vif->ar->wiphy, 0); } static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index b43cccfb66b6..cd1d6730eab7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -762,7 +762,7 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, brcmf_dbg(SCAN, "scheduled scan completed\n"); cfg->internal_escan = false; if (!aborted) - cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); + cfg80211_sched_scan_results(cfg_to_wiphy(cfg), 0); } else if (scan_request) { struct cfg80211_scan_info info = { .aborted = aborted, @@ -3372,7 +3372,7 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, goto free_req; out_err: - cfg80211_sched_scan_stopped(wiphy); + cfg80211_sched_scan_stopped(wiphy, 0); free_req: kfree(request); return err; diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 0406a98f86f0..7ec06bf13413 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -2053,7 +2053,7 @@ mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); if (!mwifiex_stop_bg_scan(priv)) - cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy); + cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy, 0); if (mwifiex_deauthenticate(priv, NULL)) return -EFAULT; @@ -2321,7 +2321,7 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, (int)sme->ssid_len, (char *)sme->ssid, sme->bssid); if (!mwifiex_stop_bg_scan(priv)) - cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy); + cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy, 0); ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, priv->bss_mode, sme->channel, sme, 0); @@ -2530,7 +2530,7 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, priv->scan_block = false; if (!mwifiex_stop_bg_scan(priv)) - cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy); + cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy, 0); user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); if (!user_scan_cfg) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 976011d532d5..dd87b9ff64c3 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -748,7 +748,7 @@ mwifiex_close(struct net_device *dev) mwifiex_dbg(priv->adapter, INFO, "aborting bgscan on ndo_stop\n"); mwifiex_stop_bg_scan(priv); - cfg80211_sched_scan_stopped(priv->wdev.wiphy); + cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0); } return 0; diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c index ab75da3e0c2b..f1d1f56fc23f 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c @@ -1201,7 +1201,7 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, break; case HostCmd_CMD_802_11_BG_SCAN_QUERY: ret = mwifiex_ret_802_11_scan(priv, resp); - cfg80211_sched_scan_results(priv->wdev.wiphy); + cfg80211_sched_scan_results(priv->wdev.wiphy, 0); mwifiex_dbg(adapter, CMD, "info: CMD_RESP: BG_SCAN result is ready!\n"); break; diff --git a/drivers/net/wireless/marvell/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c index b5b7664507eb..839df8a9634e 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_event.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c @@ -793,7 +793,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) case EVENT_BG_SCAN_STOPPED: dev_dbg(adapter->dev, "event: BGS_STOPPED\n"); - cfg80211_sched_scan_stopped(priv->wdev.wiphy); + cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0); if (priv->sched_scanning) priv->sched_scanning = false; break; diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index 1532ac9cee0b..42997e05d90f 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -560,7 +560,7 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter) #endif mwifiex_dbg(adapter, CMD, "aborting bgscan!\n"); mwifiex_stop_bg_scan(priv); - cfg80211_sched_scan_stopped(priv->wdev.wiphy); + cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0); #ifdef CONFIG_PM } #endif diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 52e810fe0833..6e90f1a4950f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1666,6 +1666,7 @@ struct cfg80211_bss_select_adjust { * (others are filtered out). * If ommited, all results are passed. * @n_match_sets: number of match sets + * @results_wk: worker for processing results notification. * @wiphy: the wiphy this was for * @dev: the interface * @scan_start: start time of the scheduled scan @@ -1726,6 +1727,7 @@ struct cfg80211_sched_scan_request { struct wiphy *wiphy; struct net_device *dev; unsigned long scan_start; + bool report_results; struct rcu_head rcu_head; u32 owner_nlportid; bool nl_owner_dead; @@ -4564,31 +4566,34 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, * cfg80211_sched_scan_results - notify that new scan results are available * * @wiphy: the wiphy which got scheduled scan results + * @reqid: identifier for the related scheduled scan request */ -void cfg80211_sched_scan_results(struct wiphy *wiphy); +void cfg80211_sched_scan_results(struct wiphy *wiphy, u64 reqid); /** * cfg80211_sched_scan_stopped - notify that the scheduled scan has stopped * * @wiphy: the wiphy on which the scheduled scan stopped + * @reqid: identifier for the related scheduled scan request * * The driver can call this function to inform cfg80211 that the * scheduled scan had to be stopped, for whatever reason. The driver * is then called back via the sched_scan_stop operation when done. */ -void cfg80211_sched_scan_stopped(struct wiphy *wiphy); +void cfg80211_sched_scan_stopped(struct wiphy *wiphy, u64 reqid); /** * cfg80211_sched_scan_stopped_rtnl - notify that the scheduled scan has stopped * * @wiphy: the wiphy on which the scheduled scan stopped + * @reqid: identifier for the related scheduled scan request * * The driver can call this function to inform cfg80211 that the * scheduled scan had to be stopped, for whatever reason. The driver * is then called back via the sched_scan_stop operation when done. * This function should be called with rtnl locked. */ -void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy); +void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy, u64 reqid); /** * cfg80211_inform_bss_frame_data - inform cfg80211 of a received BSS frame diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 76a8bcd8ef11..a87d195c4a61 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -10,7 +10,7 @@ static void ieee80211_sched_scan_cancel(struct ieee80211_local *local) { if (ieee80211_request_sched_scan_stop(local)) return; - cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy); + cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy, 0); } int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 0b16e2e8dc9e..47d2ed570470 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -1219,7 +1219,7 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw) trace_api_sched_scan_results(local); - cfg80211_sched_scan_results(hw->wiphy); + cfg80211_sched_scan_results(hw->wiphy, 0); } EXPORT_SYMBOL(ieee80211_sched_scan_results); @@ -1239,7 +1239,7 @@ void ieee80211_sched_scan_end(struct ieee80211_local *local) mutex_unlock(&local->mtx); - cfg80211_sched_scan_stopped(local->hw.wiphy); + cfg80211_sched_scan_stopped(local->hw.wiphy, 0); } void ieee80211_sched_scan_stopped_work(struct work_struct *work) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index bfc28053639b..ac9ac6c35594 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2112,7 +2112,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) mutex_unlock(&local->mtx); if (sched_scan_stopped) - cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy); + cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy, 0); wake_up: if (local->in_reconfig) { diff --git a/net/wireless/core.c b/net/wireless/core.c index a3c0c48afb85..83ea164f16b3 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -456,7 +456,6 @@ use_default_name: INIT_LIST_HEAD(&rdev->bss_list); INIT_LIST_HEAD(&rdev->sched_scan_req_list); INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); - INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); INIT_LIST_HEAD(&rdev->mlme_unreg); spin_lock_init(&rdev->mlme_unreg_lock); INIT_WORK(&rdev->mlme_unreg_wk, cfg80211_mlme_unreg_wk); @@ -473,6 +472,7 @@ use_default_name: INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); INIT_WORK(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk); + INIT_WORK(&rdev->sched_scan_res_wk, cfg80211_sched_scan_results_wk); INIT_WORK(&rdev->propagate_radar_detect_wk, cfg80211_propagate_radar_detect_wk); INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk); diff --git a/net/wireless/core.h b/net/wireless/core.h index 96baffab5e5b..6e809325af3b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -77,7 +77,6 @@ struct cfg80211_registered_device { struct list_head sched_scan_req_list; unsigned long suspend_at; struct work_struct scan_done_wk; - struct work_struct sched_scan_results_wk; struct genl_info *cur_cmd_info; @@ -93,6 +92,7 @@ struct cfg80211_registered_device { struct work_struct destroy_work; struct work_struct sched_scan_stop_wk; + struct work_struct sched_scan_res_wk; struct cfg80211_chan_def radar_chandef; struct work_struct propagate_radar_detect_wk; @@ -412,7 +412,7 @@ void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev, struct cfg80211_sched_scan_request *req); int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev, bool want_multi); -void __cfg80211_sched_scan_results(struct work_struct *wk); +void cfg80211_sched_scan_results_wk(struct work_struct *work); int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev, struct cfg80211_sched_scan_request *req, bool driver_initiated); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 8d86f704bcf3..14d5f0c8c45f 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -328,7 +328,7 @@ cfg80211_find_sched_scan_req(struct cfg80211_registered_device *rdev, u64 reqid) if (pos->reqid == reqid) return pos; } - return ERR_PTR(-ENOENT); + return NULL; } /* @@ -364,67 +364,66 @@ int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev, return 0; } -void __cfg80211_sched_scan_results(struct work_struct *wk) +void cfg80211_sched_scan_results_wk(struct work_struct *work) { struct cfg80211_registered_device *rdev; - struct cfg80211_sched_scan_request *request; + struct cfg80211_sched_scan_request *req, *tmp; - rdev = container_of(wk, struct cfg80211_registered_device, - sched_scan_results_wk); + rdev = container_of(work, struct cfg80211_registered_device, + sched_scan_res_wk); rtnl_lock(); - - request = cfg80211_find_sched_scan_req(rdev, 0); - - /* we don't have sched_scan_req anymore if the scan is stopping */ - if (!IS_ERR(request)) { - if (request->flags & NL80211_SCAN_FLAG_FLUSH) { - /* flush entries from previous scans */ - spin_lock_bh(&rdev->bss_lock); - __cfg80211_bss_expire(rdev, request->scan_start); - spin_unlock_bh(&rdev->bss_lock); - request->scan_start = jiffies; + list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) { + if (req->report_results) { + req->report_results = false; + if (req->flags & NL80211_SCAN_FLAG_FLUSH) { + /* flush entries from previous scans */ + spin_lock_bh(&rdev->bss_lock); + __cfg80211_bss_expire(rdev, req->scan_start); + spin_unlock_bh(&rdev->bss_lock); + req->scan_start = jiffies; + } + nl80211_send_sched_scan(req, + NL80211_CMD_SCHED_SCAN_RESULTS); } - nl80211_send_sched_scan(request, NL80211_CMD_SCHED_SCAN_RESULTS); } - rtnl_unlock(); } -void cfg80211_sched_scan_results(struct wiphy *wiphy) +void cfg80211_sched_scan_results(struct wiphy *wiphy, u64 reqid) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct cfg80211_sched_scan_request *request; - trace_cfg80211_sched_scan_results(wiphy); + trace_cfg80211_sched_scan_results(wiphy, reqid); /* ignore if we're not scanning */ rtnl_lock(); - request = cfg80211_find_sched_scan_req(rdev, 0); + request = cfg80211_find_sched_scan_req(rdev, reqid); + if (request) { + request->report_results = true; + queue_work(cfg80211_wq, &rdev->sched_scan_res_wk); + } rtnl_unlock(); - - if (!IS_ERR(request)) - queue_work(cfg80211_wq, - &wiphy_to_rdev(wiphy)->sched_scan_results_wk); } EXPORT_SYMBOL(cfg80211_sched_scan_results); -void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy) +void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy, u64 reqid) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); ASSERT_RTNL(); - trace_cfg80211_sched_scan_stopped(wiphy); + trace_cfg80211_sched_scan_stopped(wiphy, reqid); - __cfg80211_stop_sched_scan(rdev, 0, true); + __cfg80211_stop_sched_scan(rdev, reqid, true); } EXPORT_SYMBOL(cfg80211_sched_scan_stopped_rtnl); -void cfg80211_sched_scan_stopped(struct wiphy *wiphy) +void cfg80211_sched_scan_stopped(struct wiphy *wiphy, u64 reqid) { rtnl_lock(); - cfg80211_sched_scan_stopped_rtnl(wiphy); + cfg80211_sched_scan_stopped_rtnl(wiphy, reqid); rtnl_unlock(); } EXPORT_SYMBOL(cfg80211_sched_scan_stopped); @@ -456,8 +455,8 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, ASSERT_RTNL(); sched_scan_req = cfg80211_find_sched_scan_req(rdev, reqid); - if (IS_ERR(sched_scan_req)) - return PTR_ERR(sched_scan_req); + if (!sched_scan_req) + return -ENOENT; return cfg80211_stop_sched_scan_req(rdev, sched_scan_req, driver_initiated); diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 8d8f6b462b18..ca8b2059f92c 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2820,14 +2820,28 @@ TRACE_EVENT(cfg80211_scan_done, MAC_PR_ARG(tsf_bssid)) ); -DEFINE_EVENT(wiphy_only_evt, cfg80211_sched_scan_results, - TP_PROTO(struct wiphy *wiphy), - TP_ARGS(wiphy) +DECLARE_EVENT_CLASS(wiphy_id_evt, + TP_PROTO(struct wiphy *wiphy, u64 id), + TP_ARGS(wiphy, id), + TP_STRUCT__entry( + WIPHY_ENTRY + __field(u64, id) + ), + TP_fast_assign( + WIPHY_ASSIGN; + __entry->id = id; + ), + TP_printk(WIPHY_PR_FMT ", id: %llu", WIPHY_PR_ARG, __entry->id) ); -DEFINE_EVENT(wiphy_only_evt, cfg80211_sched_scan_stopped, - TP_PROTO(struct wiphy *wiphy), - TP_ARGS(wiphy) +DEFINE_EVENT(wiphy_id_evt, cfg80211_sched_scan_stopped, + TP_PROTO(struct wiphy *wiphy, u64 id), + TP_ARGS(wiphy, id) +); + +DEFINE_EVENT(wiphy_id_evt, cfg80211_sched_scan_results, + TP_PROTO(struct wiphy *wiphy, u64 id), + TP_ARGS(wiphy, id) ); TRACE_EVENT(cfg80211_get_bss, -- cgit From 2d2ab658d2debcb4c0e29c9e6f18e5683f3077bf Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 28 Apr 2017 14:10:48 +0800 Subject: rhashtable: Do not lower max_elems when max_size is zero The commit 6d684e54690c ("rhashtable: Cap total number of entries to 2^31") breaks rhashtable users that do not set max_size. This is because when max_size is zero max_elems is also incorrectly set to zero instead of 2^31. This patch fixes it by only lowering max_elems when max_size is not zero. Fixes: 6d684e54690c ("rhashtable: Cap total number of entries to 2^31") Reported-by: Florian Fainelli Reported-by: kernel test robot Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- lib/rhashtable.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 751630bbe409..3895486ef551 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -958,13 +958,14 @@ int rhashtable_init(struct rhashtable *ht, if (params->min_size) ht->p.min_size = roundup_pow_of_two(params->min_size); - if (params->max_size) - ht->p.max_size = rounddown_pow_of_two(params->max_size); - /* Cap total entries at 2^31 to avoid nelems overflow. */ ht->max_elems = 1u << 31; - if (ht->p.max_size < ht->max_elems / 2) - ht->max_elems = ht->p.max_size * 2; + + if (params->max_size) { + ht->p.max_size = rounddown_pow_of_two(params->max_size); + if (ht->p.max_size < ht->max_elems / 2) + ht->max_elems = ht->p.max_size * 2; + } ht->p.min_size = max(ht->p.min_size, HASH_MIN_SIZE); -- cgit From 7e56fbd27b4bd6ab7d641f45bf23d2af654412f6 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 27 Apr 2017 01:39:31 +0200 Subject: bpf, x86_64/arm64: remove old ldimm64 artifacts from jits For both cases, the verifier is already rejecting such invalid formed instructions. Thus, remove these artifacts from old times and align it with ppc64, sparc64 and s390x JITs that don't have them in the first place. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- arch/arm64/net/bpf_jit_comp.c | 9 --------- arch/x86/net/bpf_jit_comp.c | 7 ------- 2 files changed, 16 deletions(-) diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index a785554916c0..304736870dca 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -604,15 +604,6 @@ emit_cond_jmp: const struct bpf_insn insn1 = insn[1]; u64 imm64; - if (insn1.code != 0 || insn1.src_reg != 0 || - insn1.dst_reg != 0 || insn1.off != 0) { - /* Note: verifier in BPF core must catch invalid - * instructions. - */ - pr_err_once("Invalid BPF_LD_IMM64 instruction\n"); - return -EINVAL; - } - imm64 = (u64)insn1.imm << 32 | (u32)imm; emit_a64_mov_i64(dst, imm64, ctx); diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 32322ce9b405..14f840df1d95 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -490,13 +490,6 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, break; case BPF_LD | BPF_IMM | BPF_DW: - if (insn[1].code != 0 || insn[1].src_reg != 0 || - insn[1].dst_reg != 0 || insn[1].off != 0) { - /* verifier must catch invalid insns */ - pr_err("invalid BPF_LD_IMM64 insn\n"); - return -EINVAL; - } - /* optimization: if imm64 is zero, use 'xor ,' * to save 7 bytes. */ -- cgit From 728a853a44a0f01111883f95216e980722474c07 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 27 Apr 2017 01:39:32 +0200 Subject: bpf: add various test cases to verifier selftests Add several test cases around ldimm64, fp arithmetic and direct packet access. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/test_verifier.c | 137 ++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 95a8d5f3ab80..d3395c192a24 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -190,6 +190,86 @@ static struct bpf_test tests[] = { .errstr = "invalid bpf_ld_imm64 insn", .result = REJECT, }, + { + "test6 ld_imm64", + .insns = { + BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0), + BPF_RAW_INSN(0, 0, 0, 0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, + { + "test7 ld_imm64", + .insns = { + BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 1), + BPF_RAW_INSN(0, 0, 0, 0, 1), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, + { + "test8 ld_imm64", + .insns = { + BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 1, 1), + BPF_RAW_INSN(0, 0, 0, 0, 1), + BPF_EXIT_INSN(), + }, + .errstr = "uses reserved fields", + .result = REJECT, + }, + { + "test9 ld_imm64", + .insns = { + BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 1), + BPF_RAW_INSN(0, 0, 0, 1, 1), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_ld_imm64 insn", + .result = REJECT, + }, + { + "test10 ld_imm64", + .insns = { + BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 1), + BPF_RAW_INSN(0, BPF_REG_1, 0, 0, 1), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_ld_imm64 insn", + .result = REJECT, + }, + { + "test11 ld_imm64", + .insns = { + BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 1), + BPF_RAW_INSN(0, 0, BPF_REG_1, 0, 1), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_ld_imm64 insn", + .result = REJECT, + }, + { + "test12 ld_imm64", + .insns = { + BPF_MOV64_IMM(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, BPF_REG_1, 0, 1), + BPF_RAW_INSN(0, 0, 0, 0, 1), + BPF_EXIT_INSN(), + }, + .errstr = "not pointing to valid bpf_map", + .result = REJECT, + }, + { + "test13 ld_imm64", + .insns = { + BPF_MOV64_IMM(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, BPF_REG_1, 0, 1), + BPF_RAW_INSN(0, 0, BPF_REG_1, 0, 1), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_ld_imm64 insn", + .result = REJECT, + }, { "no bpf_exit", .insns = { @@ -330,6 +410,30 @@ static struct bpf_test tests[] = { .errstr = "invalid read from stack", .result = REJECT, }, + { + "invalid fp arithmetic", + /* If this gets ever changed, make sure JITs can deal with it. */ + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_SUB, BPF_REG_1, 8), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "R1 pointer arithmetic", + .result_unpriv = REJECT, + .errstr = "R1 invalid mem access", + .result = REJECT, + }, + { + "non-invalid fp arithmetic", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, { "invalid argument register", .insns = { @@ -1800,6 +1904,20 @@ static struct bpf_test tests[] = { .result_unpriv = REJECT, .result = ACCEPT, }, + { + "unpriv: adding of fp", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_MOV64_IMM(BPF_REG_1, 0), + BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_10), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, -8), + BPF_EXIT_INSN(), + }, + .errstr_unpriv = "pointer arithmetic prohibited", + .result_unpriv = REJECT, + .errstr = "R1 invalid mem access", + .result = REJECT, + }, { "unpriv: cmp of stack pointer", .insns = { @@ -2471,6 +2589,25 @@ static struct bpf_test tests[] = { .result = REJECT, .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, + { + "direct packet access: test16 (arith on data_end)", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 16), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), + BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "invalid access to packet", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, { "helper access to packet: test1, valid packet_ptr range", .insns = { -- cgit From d24f7c7fb91d94556936f2511035d1f123b449f4 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Thu, 27 Apr 2017 01:39:33 +0200 Subject: bpf: bpf_lock on kallsysms doesn't need to be irqsave Hannes rightfully spotted that the bpf_lock doesn't need to be irqsave variant. We never perform any such updates where this would be necessary (neither right now nor in future), therefore relax this further. Signed-off-by: Hannes Frederic Sowa Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/core.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index b4f1cb0c5ac7..6f81e0f5a0fa 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -394,27 +394,23 @@ static bool bpf_prog_kallsyms_verify_off(const struct bpf_prog *fp) void bpf_prog_kallsyms_add(struct bpf_prog *fp) { - unsigned long flags; - if (!bpf_prog_kallsyms_candidate(fp) || !capable(CAP_SYS_ADMIN)) return; - spin_lock_irqsave(&bpf_lock, flags); + spin_lock_bh(&bpf_lock); bpf_prog_ksym_node_add(fp->aux); - spin_unlock_irqrestore(&bpf_lock, flags); + spin_unlock_bh(&bpf_lock); } void bpf_prog_kallsyms_del(struct bpf_prog *fp) { - unsigned long flags; - if (!bpf_prog_kallsyms_candidate(fp)) return; - spin_lock_irqsave(&bpf_lock, flags); + spin_lock_bh(&bpf_lock); bpf_prog_ksym_node_del(fp->aux); - spin_unlock_irqrestore(&bpf_lock, flags); + spin_unlock_bh(&bpf_lock); } static struct bpf_prog *bpf_prog_kallsyms_find(unsigned long addr) -- cgit From 43bcf707ccdc79ee63edb953fcf72e6dc244cfa1 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 27 Apr 2017 01:39:34 +0200 Subject: bpf: fix _htons occurences in test_progs Dave reported that on sparc test_progs generates buggy swapped eth->h_proto protocol comparisons: 10: (15) if r3 == 0xdd86 goto pc+9 R0=imm2,min_value=2,max_value=2 R1=pkt(id=0,off=0,r=14) R2=pkt_end R3=inv R4=pkt(id=0,off=14,r=14) R5=inv56 R10=fp This is due to the unconditional ... #define htons __builtin_bswap16 #define ntohs __builtin_bswap16 ... in test_progs that causes this. Make use of asm/byteorder.h and use __constant_htons() where possible and only perform the bswap16 when on little endian in non-constant case. Fixes: 6882804c916b ("selftests/bpf: add a test for overlapping packet range checks") Fixes: 37821613626e ("selftests/bpf: add l4 load balancer test based on sched_cls") Reported-by: David S. Miller Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/bpf_util.h | 19 +++++++++++++++++++ tools/testing/selftests/bpf/test_l4lb.c | 11 +++++------ tools/testing/selftests/bpf/test_pkt_access.c | 6 +++--- tools/testing/selftests/bpf/test_progs.c | 10 ++++------ 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/tools/testing/selftests/bpf/bpf_util.h b/tools/testing/selftests/bpf/bpf_util.h index 84a5d1823f02..7de27966103d 100644 --- a/tools/testing/selftests/bpf/bpf_util.h +++ b/tools/testing/selftests/bpf/bpf_util.h @@ -6,6 +6,25 @@ #include #include +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN +# define __bpf_ntohs(x) __builtin_bswap16(x) +# define __bpf_htons(x) __builtin_bswap16(x) +#elif __BYTE_ORDER == __BIG_ENDIAN +# define __bpf_ntohs(x) (x) +# define __bpf_htons(x) (x) +#else +# error "Fix your __BYTE_ORDER?!" +#endif + +#define bpf_htons(x) \ + (__builtin_constant_p(x) ? \ + __constant_htons(x) : __bpf_htons(x)) +#define bpf_ntohs(x) \ + (__builtin_constant_p(x) ? \ + __constant_ntohs(x) : __bpf_ntohs(x)) + static inline unsigned int bpf_num_possible_cpus(void) { static const char *fcpu = "/sys/devices/system/cpu/possible"; diff --git a/tools/testing/selftests/bpf/test_l4lb.c b/tools/testing/selftests/bpf/test_l4lb.c index 368bfe8b9842..b68b21274bac 100644 --- a/tools/testing/selftests/bpf/test_l4lb.c +++ b/tools/testing/selftests/bpf/test_l4lb.c @@ -19,9 +19,8 @@ #include #include "bpf_helpers.h" #include "test_iptunnel_common.h" +#include "bpf_util.h" -#define htons __builtin_bswap16 -#define ntohs __builtin_bswap16 int _version SEC("version") = 1; static inline __u32 rol32(__u32 word, unsigned int shift) @@ -355,7 +354,7 @@ static __always_inline int process_packet(void *data, __u64 off, void *data_end, iph_len = sizeof(struct ipv6hdr); protocol = ip6h->nexthdr; pckt.proto = protocol; - pkt_bytes = ntohs(ip6h->payload_len); + pkt_bytes = bpf_ntohs(ip6h->payload_len); off += iph_len; if (protocol == IPPROTO_FRAGMENT) { return TC_ACT_SHOT; @@ -377,7 +376,7 @@ static __always_inline int process_packet(void *data, __u64 off, void *data_end, protocol = iph->protocol; pckt.proto = protocol; - pkt_bytes = ntohs(iph->tot_len); + pkt_bytes = bpf_ntohs(iph->tot_len); off += IPV4_HDR_LEN_NO_OPT; if (iph->frag_off & PCKT_FRAGMENTED) @@ -464,9 +463,9 @@ int balancer_ingress(struct __sk_buff *ctx) if (data + nh_off > data_end) return TC_ACT_SHOT; eth_proto = eth->eth_proto; - if (eth_proto == htons(ETH_P_IP)) + if (eth_proto == bpf_htons(ETH_P_IP)) return process_packet(data, nh_off, data_end, false, ctx); - else if (eth_proto == htons(ETH_P_IPV6)) + else if (eth_proto == bpf_htons(ETH_P_IPV6)) return process_packet(data, nh_off, data_end, true, ctx); else return TC_ACT_SHOT; diff --git a/tools/testing/selftests/bpf/test_pkt_access.c b/tools/testing/selftests/bpf/test_pkt_access.c index fd1e0832d409..711300508ee0 100644 --- a/tools/testing/selftests/bpf/test_pkt_access.c +++ b/tools/testing/selftests/bpf/test_pkt_access.c @@ -14,8 +14,8 @@ #include #include #include "bpf_helpers.h" +#include "bpf_util.h" -#define _htons __builtin_bswap16 #define barrier() __asm__ __volatile__("": : :"memory") int _version SEC("version") = 1; @@ -32,7 +32,7 @@ int process(struct __sk_buff *skb) if (eth + 1 > data_end) return TC_ACT_SHOT; - if (eth->h_proto == _htons(ETH_P_IP)) { + if (eth->h_proto == bpf_htons(ETH_P_IP)) { struct iphdr *iph = (struct iphdr *)(eth + 1); if (iph + 1 > data_end) @@ -40,7 +40,7 @@ int process(struct __sk_buff *skb) ihl_len = iph->ihl * 4; proto = iph->protocol; tcp = (struct tcphdr *)((void *)(iph) + ihl_len); - } else if (eth->h_proto == _htons(ETH_P_IPV6)) { + } else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) { struct ipv6hdr *ip6h = (struct ipv6hdr *)(eth + 1); if (ip6h + 1 > data_end) diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index 5275d4a1df24..7c2d899c8f43 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -30,8 +30,6 @@ typedef __u16 __sum16; #include "test_iptunnel_common.h" #include "bpf_util.h" -#define _htons __builtin_bswap16 - static int error_cnt, pass_cnt; #define MAGIC_BYTES 123 @@ -42,10 +40,10 @@ static struct { struct iphdr iph; struct tcphdr tcp; } __packed pkt_v4 = { - .eth.h_proto = _htons(ETH_P_IP), + .eth.h_proto = bpf_htons(ETH_P_IP), .iph.ihl = 5, .iph.protocol = 6, - .iph.tot_len = _htons(MAGIC_BYTES), + .iph.tot_len = bpf_htons(MAGIC_BYTES), .tcp.urg_ptr = 123, }; @@ -55,9 +53,9 @@ static struct { struct ipv6hdr iph; struct tcphdr tcp; } __packed pkt_v6 = { - .eth.h_proto = _htons(ETH_P_IPV6), + .eth.h_proto = bpf_htons(ETH_P_IPV6), .iph.nexthdr = 6, - .iph.payload_len = _htons(MAGIC_BYTES), + .iph.payload_len = bpf_htons(MAGIC_BYTES), .tcp.urg_ptr = 123, }; -- cgit From f3515b5d0b718cceae9e67802cfa20d5e6f9b567 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 27 Apr 2017 01:39:35 +0200 Subject: bpf: provide a generic macro for percpu values for selftests To overcome bugs as described and fixed in 89087c456fb5 ("bpf: Fix values type used in test_maps"), provide a generic BPF_DECLARE_PERCPU() and bpf_percpu() accessor macro for all percpu map values used in tests. Declaring variables works as follows (also works for structs): BPF_DECLARE_PERCPU(uint32_t, my_value); They can then be accessed normally as uint32_t type through: bpf_percpu(my_value, ) For example: bpf_percpu(my_value, 0)++; Implicitly, we make sure that the passed type is allocated and aligned by gcc at least on a 8-byte boundary, so that it works together with the map lookup/update syscall for percpu maps. We use it as a usage example in test_maps, so that others are free to adapt this into their code when necessary. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/bpf_util.h | 7 +++++++ tools/testing/selftests/bpf/test_maps.c | 37 ++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/tools/testing/selftests/bpf/bpf_util.h b/tools/testing/selftests/bpf/bpf_util.h index 7de27966103d..369e7d7bba80 100644 --- a/tools/testing/selftests/bpf/bpf_util.h +++ b/tools/testing/selftests/bpf/bpf_util.h @@ -54,4 +54,11 @@ static inline unsigned int bpf_num_possible_cpus(void) return possible_cpus; } +#define __bpf_percpu_val_align __attribute__((__aligned__(8))) + +#define BPF_DECLARE_PERCPU(type, name) \ + struct { type v; /* padding */ } __bpf_percpu_val_align \ + name[bpf_num_possible_cpus()] +#define bpf_percpu(name, cpu) name[(cpu)].v + #endif /* __BPF_UTIL__ */ diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index a977c4f7b0ce..93314524de0d 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -137,20 +137,20 @@ static void test_hashmap_sizes(int task, void *data) static void test_hashmap_percpu(int task, void *data) { unsigned int nr_cpus = bpf_num_possible_cpus(); - long long value[nr_cpus]; + BPF_DECLARE_PERCPU(long, value); long long key, next_key, first_key; int expected_key_mask = 0; int fd, i; fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_HASH, sizeof(key), - sizeof(value[0]), 2, map_flags); + sizeof(bpf_percpu(value, 0)), 2, map_flags); if (fd < 0) { printf("Failed to create hashmap '%s'!\n", strerror(errno)); exit(1); } for (i = 0; i < nr_cpus; i++) - value[i] = i + 100; + bpf_percpu(value, i) = i + 100; key = 1; /* Insert key=1 element. */ @@ -170,8 +170,9 @@ static void test_hashmap_percpu(int task, void *data) /* Check that key=1 can be found. Value could be 0 if the lookup * was run from a different CPU. */ - value[0] = 1; - assert(bpf_map_lookup_elem(fd, &key, value) == 0 && value[0] == 100); + bpf_percpu(value, 0) = 1; + assert(bpf_map_lookup_elem(fd, &key, value) == 0 && + bpf_percpu(value, 0) == 100); key = 2; /* Check that key=2 is not found. */ @@ -211,7 +212,7 @@ static void test_hashmap_percpu(int task, void *data) assert(bpf_map_lookup_elem(fd, &next_key, value) == 0); for (i = 0; i < nr_cpus; i++) - assert(value[i] == i + 100); + assert(bpf_percpu(value, i) == i + 100); key = next_key; } @@ -296,34 +297,36 @@ static void test_arraymap(int task, void *data) static void test_arraymap_percpu(int task, void *data) { unsigned int nr_cpus = bpf_num_possible_cpus(); + BPF_DECLARE_PERCPU(long, values); int key, next_key, fd, i; - long long values[nr_cpus]; fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key), - sizeof(values[0]), 2, 0); + sizeof(bpf_percpu(values, 0)), 2, 0); if (fd < 0) { printf("Failed to create arraymap '%s'!\n", strerror(errno)); exit(1); } for (i = 0; i < nr_cpus; i++) - values[i] = i + 100; + bpf_percpu(values, i) = i + 100; key = 1; /* Insert key=1 element. */ assert(bpf_map_update_elem(fd, &key, values, BPF_ANY) == 0); - values[0] = 0; + bpf_percpu(values, 0) = 0; assert(bpf_map_update_elem(fd, &key, values, BPF_NOEXIST) == -1 && errno == EEXIST); /* Check that key=1 can be found. */ - assert(bpf_map_lookup_elem(fd, &key, values) == 0 && values[0] == 100); + assert(bpf_map_lookup_elem(fd, &key, values) == 0 && + bpf_percpu(values, 0) == 100); key = 0; /* Check that key=0 is also found and zero initialized. */ assert(bpf_map_lookup_elem(fd, &key, values) == 0 && - values[0] == 0 && values[nr_cpus - 1] == 0); + bpf_percpu(values, 0) == 0 && + bpf_percpu(values, nr_cpus - 1) == 0); /* Check that key=2 cannot be inserted due to max_entries limit. */ key = 2; @@ -353,15 +356,15 @@ static void test_arraymap_percpu(int task, void *data) static void test_arraymap_percpu_many_keys(void) { unsigned int nr_cpus = bpf_num_possible_cpus(); + BPF_DECLARE_PERCPU(long, values); /* nr_keys is not too large otherwise the test stresses percpu * allocator more than anything else */ unsigned int nr_keys = 2000; - long long values[nr_cpus]; int key, fd, i; fd = bpf_create_map(BPF_MAP_TYPE_PERCPU_ARRAY, sizeof(key), - sizeof(values[0]), nr_keys, 0); + sizeof(bpf_percpu(values, 0)), nr_keys, 0); if (fd < 0) { printf("Failed to create per-cpu arraymap '%s'!\n", strerror(errno)); @@ -369,19 +372,19 @@ static void test_arraymap_percpu_many_keys(void) } for (i = 0; i < nr_cpus; i++) - values[i] = i + 10; + bpf_percpu(values, i) = i + 10; for (key = 0; key < nr_keys; key++) assert(bpf_map_update_elem(fd, &key, values, BPF_ANY) == 0); for (key = 0; key < nr_keys; key++) { for (i = 0; i < nr_cpus; i++) - values[i] = 0; + bpf_percpu(values, i) = 0; assert(bpf_map_lookup_elem(fd, &key, values) == 0); for (i = 0; i < nr_cpus; i++) - assert(values[i] == i + 10); + assert(bpf_percpu(values, i) == i + 10); } close(fd); -- cgit From 6133406be1aabfb041f024109efc41756970800e Mon Sep 17 00:00:00 2001 From: Paul Greenwalt Date: Mon, 13 Mar 2017 05:47:56 -0400 Subject: ixgbe: Acquire PHY semaphore before device reset A recent firmware change fixed an issue to acquire the PHY semaphore before accessing PHY registers. This led to a case where SW can issue a device reset clearing the MDIO registers. This patch makes SW acquire the PHY semaphore before issuing a device reset. Signed-off-by: Paul Greenwalt Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c | 8 ++++++++ drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index 84a467a8ed3d..6ea0d6a5fb90 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -95,6 +95,7 @@ s32 ixgbe_reset_hw_X540(struct ixgbe_hw *hw) { s32 status; u32 ctrl, i; + u32 swfw_mask = hw->phy.phy_semaphore_mask; /* Call adapter stop to disable tx/rx and clear interrupts */ status = hw->mac.ops.stop_adapter(hw); @@ -105,10 +106,17 @@ s32 ixgbe_reset_hw_X540(struct ixgbe_hw *hw) ixgbe_clear_tx_pending(hw); mac_reset_top: + status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask); + if (status) { + hw_dbg(hw, "semaphore failed with %d", status); + return IXGBE_ERR_SWFW_SYNC; + } + ctrl = IXGBE_CTRL_RST; ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL); IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl); IXGBE_WRITE_FLUSH(hw); + hw->mac.ops.release_swfw_sync(hw, swfw_mask); usleep_range(1000, 1200); /* Poll for reset bit to self-clear indicating reset is complete */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 2658394599e4..58d3bcaca2b9 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -3318,6 +3318,7 @@ static s32 ixgbe_reset_hw_X550em(struct ixgbe_hw *hw) u32 ctrl = 0; u32 i; bool link_up = false; + u32 swfw_mask = hw->phy.phy_semaphore_mask; /* Call adapter stop to disable Tx/Rx and clear interrupts */ status = hw->mac.ops.stop_adapter(hw); @@ -3363,9 +3364,16 @@ mac_reset_top: ctrl = IXGBE_CTRL_RST; } + status = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask); + if (status) { + hw_dbg(hw, "semaphore failed with %d", status); + return IXGBE_ERR_SWFW_SYNC; + } + ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL); IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl); IXGBE_WRITE_FLUSH(hw); + hw->mac.ops.release_swfw_sync(hw, swfw_mask); usleep_range(1000, 1200); /* Poll for reset bit to self-clear meaning reset is complete */ -- cgit From 9247080816297de4e31abb684939c0e53e3a8a67 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 24 Apr 2017 03:30:17 -0700 Subject: ixgbe: add XDP support for pass and drop actions Basic XDP drop support for ixgbe. Uses READ_ONCE/xchg semantics on XDP programs instead of RCU primitives as suggested by Daniel Borkmann and Alex Duyck. v2: fix the build issues seen w/ XDP when page sizes are larger than 4K and made minor fixes based on feedback from Jakub Kicinski Signed-off-by: John Fastabend Acked-by: Alexander Duyck Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 4 +- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 4 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 169 +++++++++++++++++++---- 3 files changed, 148 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 656ca8f69768..cb14813b0080 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -318,6 +318,7 @@ struct ixgbe_ring { struct ixgbe_ring *next; /* pointer to next ring in q_vector */ struct ixgbe_q_vector *q_vector; /* backpointer to host q_vector */ struct net_device *netdev; /* netdev ring belongs to */ + struct bpf_prog *xdp_prog; struct device *dev; /* device for DMA mapping */ struct ixgbe_fwd_adapter *l2_accel_priv; void *desc; /* descriptor ring memory */ @@ -555,6 +556,7 @@ struct ixgbe_adapter { unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; /* OS defined structs */ struct net_device *netdev; + struct bpf_prog *xdp_prog; struct pci_dev *pdev; unsigned long state; @@ -835,7 +837,7 @@ void ixgbe_down(struct ixgbe_adapter *adapter); void ixgbe_reinit_locked(struct ixgbe_adapter *adapter); void ixgbe_reset(struct ixgbe_adapter *adapter); void ixgbe_set_ethtool_ops(struct net_device *netdev); -int ixgbe_setup_rx_resources(struct ixgbe_ring *); +int ixgbe_setup_rx_resources(struct ixgbe_adapter *, struct ixgbe_ring *); int ixgbe_setup_tx_resources(struct ixgbe_ring *); void ixgbe_free_rx_resources(struct ixgbe_ring *); void ixgbe_free_tx_resources(struct ixgbe_ring *); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 59730ede4746..79a126d9e091 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -1128,7 +1128,7 @@ static int ixgbe_set_ringparam(struct net_device *netdev, sizeof(struct ixgbe_ring)); temp_ring[i].count = new_rx_count; - err = ixgbe_setup_rx_resources(&temp_ring[i]); + err = ixgbe_setup_rx_resources(adapter, &temp_ring[i]); if (err) { while (i) { i--; @@ -1761,7 +1761,7 @@ static int ixgbe_setup_desc_rings(struct ixgbe_adapter *adapter) rx_ring->netdev = adapter->netdev; rx_ring->reg_idx = adapter->rx_ring[0]->reg_idx; - err = ixgbe_setup_rx_resources(rx_ring); + err = ixgbe_setup_rx_resources(adapter, rx_ring); if (err) { ret_val = 4; goto err_nomem; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index afff2ca7f8c0..99b5357c3e00 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -49,6 +49,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -1855,6 +1858,10 @@ static void ixgbe_dma_sync_frag(struct ixgbe_ring *rx_ring, * @rx_desc: pointer to the EOP Rx descriptor * @skb: pointer to current skb being fixed * + * Check if the skb is valid in the XDP case it will be an error pointer. + * Return true in this case to abort processing and advance to next + * descriptor. + * * Check for corrupted packet headers caused by senders on the local L2 * embedded NIC switch not setting up their Tx Descriptors right. These * should be very rare. @@ -1873,6 +1880,10 @@ static bool ixgbe_cleanup_headers(struct ixgbe_ring *rx_ring, { struct net_device *netdev = rx_ring->netdev; + /* XDP packets use error pointer so abort at this point */ + if (IS_ERR(skb)) + return true; + /* verify that the packet does not have any known errors */ if (unlikely(ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_ERR_FRAME_ERR_MASK) && @@ -2048,7 +2059,7 @@ static void ixgbe_put_rx_buffer(struct ixgbe_ring *rx_ring, /* hand second half of page back to the ring */ ixgbe_reuse_rx_page(rx_ring, rx_buffer); } else { - if (IXGBE_CB(skb)->dma == rx_buffer->dma) { + if (!IS_ERR(skb) && IXGBE_CB(skb)->dma == rx_buffer->dma) { /* the page has been released from the ring */ IXGBE_CB(skb)->page_released = true; } else { @@ -2069,21 +2080,22 @@ static void ixgbe_put_rx_buffer(struct ixgbe_ring *rx_ring, static struct sk_buff *ixgbe_construct_skb(struct ixgbe_ring *rx_ring, struct ixgbe_rx_buffer *rx_buffer, - union ixgbe_adv_rx_desc *rx_desc, - unsigned int size) + struct xdp_buff *xdp, + union ixgbe_adv_rx_desc *rx_desc) { - void *va = page_address(rx_buffer->page) + rx_buffer->page_offset; + unsigned int size = xdp->data_end - xdp->data; #if (PAGE_SIZE < 8192) unsigned int truesize = ixgbe_rx_pg_size(rx_ring) / 2; #else - unsigned int truesize = SKB_DATA_ALIGN(size); + unsigned int truesize = SKB_DATA_ALIGN(xdp->data_end - + xdp->data_hard_start); #endif struct sk_buff *skb; /* prefetch first cache line of first page */ - prefetch(va); + prefetch(xdp->data); #if L1_CACHE_BYTES < 128 - prefetch(va + L1_CACHE_BYTES); + prefetch(xdp->data + L1_CACHE_BYTES); #endif /* allocate a skb to store the frags */ @@ -2096,7 +2108,7 @@ static struct sk_buff *ixgbe_construct_skb(struct ixgbe_ring *rx_ring, IXGBE_CB(skb)->dma = rx_buffer->dma; skb_add_rx_frag(skb, 0, rx_buffer->page, - rx_buffer->page_offset, + xdp->data - page_address(rx_buffer->page), size, truesize); #if (PAGE_SIZE < 8192) rx_buffer->page_offset ^= truesize; @@ -2104,7 +2116,8 @@ static struct sk_buff *ixgbe_construct_skb(struct ixgbe_ring *rx_ring, rx_buffer->page_offset += truesize; #endif } else { - memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long))); + memcpy(__skb_put(skb, size), + xdp->data, ALIGN(size, sizeof(long))); rx_buffer->pagecnt_bias++; } @@ -2113,32 +2126,32 @@ static struct sk_buff *ixgbe_construct_skb(struct ixgbe_ring *rx_ring, static struct sk_buff *ixgbe_build_skb(struct ixgbe_ring *rx_ring, struct ixgbe_rx_buffer *rx_buffer, - union ixgbe_adv_rx_desc *rx_desc, - unsigned int size) + struct xdp_buff *xdp, + union ixgbe_adv_rx_desc *rx_desc) { - void *va = page_address(rx_buffer->page) + rx_buffer->page_offset; #if (PAGE_SIZE < 8192) unsigned int truesize = ixgbe_rx_pg_size(rx_ring) / 2; #else unsigned int truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + - SKB_DATA_ALIGN(IXGBE_SKB_PAD + size); + SKB_DATA_ALIGN(xdp->data_end - + xdp->data_hard_start); #endif struct sk_buff *skb; /* prefetch first cache line of first page */ - prefetch(va); + prefetch(xdp->data); #if L1_CACHE_BYTES < 128 - prefetch(va + L1_CACHE_BYTES); + prefetch(xdp->data + L1_CACHE_BYTES); #endif - /* build an skb around the page buffer */ - skb = build_skb(va - IXGBE_SKB_PAD, truesize); + /* build an skb to around the page buffer */ + skb = build_skb(xdp->data_hard_start, truesize); if (unlikely(!skb)) return NULL; /* update pointers within the skb to store the data */ - skb_reserve(skb, IXGBE_SKB_PAD); - __skb_put(skb, size); + skb_reserve(skb, xdp->data - xdp->data_hard_start); + __skb_put(skb, xdp->data_end - xdp->data); /* record DMA address if this is the start of a chain of buffers */ if (!ixgbe_test_staterr(rx_desc, IXGBE_RXD_STAT_EOP)) @@ -2154,6 +2167,41 @@ static struct sk_buff *ixgbe_build_skb(struct ixgbe_ring *rx_ring, return skb; } +#define IXGBE_XDP_PASS 0 +#define IXGBE_XDP_CONSUMED 1 + +static struct sk_buff *ixgbe_run_xdp(struct ixgbe_ring *rx_ring, + struct xdp_buff *xdp) +{ + int result = IXGBE_XDP_PASS; + struct bpf_prog *xdp_prog; + u32 act; + + rcu_read_lock(); + xdp_prog = READ_ONCE(rx_ring->xdp_prog); + + if (!xdp_prog) + goto xdp_out; + + act = bpf_prog_run_xdp(xdp_prog, xdp); + switch (act) { + case XDP_PASS: + break; + default: + bpf_warn_invalid_xdp_action(act); + case XDP_TX: + case XDP_ABORTED: + trace_xdp_exception(rx_ring->netdev, xdp_prog, act); + /* fallthrough -- handle aborts by dropping packet */ + case XDP_DROP: + result = IXGBE_XDP_CONSUMED; + break; + } +xdp_out: + rcu_read_unlock(); + return ERR_PTR(-result); +} + /** * ixgbe_clean_rx_irq - Clean completed descriptors from Rx ring - bounce buf * @q_vector: structure containing interrupt and ring information @@ -2183,6 +2231,7 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, union ixgbe_adv_rx_desc *rx_desc; struct ixgbe_rx_buffer *rx_buffer; struct sk_buff *skb; + struct xdp_buff xdp; unsigned int size; /* return some buffers to hardware, one at a time is too slow */ @@ -2205,14 +2254,29 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, rx_buffer = ixgbe_get_rx_buffer(rx_ring, rx_desc, &skb, size); /* retrieve a buffer from the ring */ - if (skb) + if (!skb) { + xdp.data = page_address(rx_buffer->page) + + rx_buffer->page_offset; + xdp.data_hard_start = xdp.data - + ixgbe_rx_offset(rx_ring); + xdp.data_end = xdp.data + size; + + skb = ixgbe_run_xdp(rx_ring, &xdp); + } + + if (IS_ERR(skb)) { + total_rx_packets++; + total_rx_bytes += size; + rx_buffer->pagecnt_bias++; + } else if (skb) { ixgbe_add_rx_frag(rx_ring, rx_buffer, skb, size); - else if (ring_uses_build_skb(rx_ring)) + } else if (ring_uses_build_skb(rx_ring)) { skb = ixgbe_build_skb(rx_ring, rx_buffer, - rx_desc, size); - else + &xdp, rx_desc); + } else { skb = ixgbe_construct_skb(rx_ring, rx_buffer, - rx_desc, size); + &xdp, rx_desc); + } /* exit if we failed to retrieve a buffer */ if (!skb) { @@ -6073,7 +6137,8 @@ err_setup_tx: * * Returns 0 on success, negative on failure **/ -int ixgbe_setup_rx_resources(struct ixgbe_ring *rx_ring) +int ixgbe_setup_rx_resources(struct ixgbe_adapter *adapter, + struct ixgbe_ring *rx_ring) { struct device *dev = rx_ring->dev; int orig_node = dev_to_node(dev); @@ -6112,6 +6177,8 @@ int ixgbe_setup_rx_resources(struct ixgbe_ring *rx_ring) rx_ring->next_to_clean = 0; rx_ring->next_to_use = 0; + rx_ring->xdp_prog = adapter->xdp_prog; + return 0; err: vfree(rx_ring->rx_buffer_info); @@ -6135,7 +6202,7 @@ static int ixgbe_setup_all_rx_resources(struct ixgbe_adapter *adapter) int i, err = 0; for (i = 0; i < adapter->num_rx_queues; i++) { - err = ixgbe_setup_rx_resources(adapter->rx_ring[i]); + err = ixgbe_setup_rx_resources(adapter, adapter->rx_ring[i]); if (!err) continue; @@ -6203,6 +6270,7 @@ void ixgbe_free_rx_resources(struct ixgbe_ring *rx_ring) { ixgbe_clean_rx_ring(rx_ring); + rx_ring->xdp_prog = NULL; vfree(rx_ring->rx_buffer_info); rx_ring->rx_buffer_info = NULL; @@ -9468,6 +9536,54 @@ ixgbe_features_check(struct sk_buff *skb, struct net_device *dev, return features; } +static int ixgbe_xdp_setup(struct net_device *dev, struct bpf_prog *prog) +{ + int i, frame_size = dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + struct ixgbe_adapter *adapter = netdev_priv(dev); + struct bpf_prog *old_prog; + + if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED) + return -EINVAL; + + if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) + return -EINVAL; + + /* verify ixgbe ring attributes are sufficient for XDP */ + for (i = 0; i < adapter->num_rx_queues; i++) { + struct ixgbe_ring *ring = adapter->rx_ring[i]; + + if (ring_is_rsc_enabled(ring)) + return -EINVAL; + + if (frame_size > ixgbe_rx_bufsz(ring)) + return -EINVAL; + } + + old_prog = xchg(&adapter->xdp_prog, prog); + for (i = 0; i < adapter->num_rx_queues; i++) + xchg(&adapter->rx_ring[i]->xdp_prog, adapter->xdp_prog); + + if (old_prog) + bpf_prog_put(old_prog); + + return 0; +} + +static int ixgbe_xdp(struct net_device *dev, struct netdev_xdp *xdp) +{ + struct ixgbe_adapter *adapter = netdev_priv(dev); + + switch (xdp->command) { + case XDP_SETUP_PROG: + return ixgbe_xdp_setup(dev, xdp->prog); + case XDP_QUERY_PROG: + xdp->prog_attached = !!(adapter->xdp_prog); + return 0; + default: + return -EINVAL; + } +} + static const struct net_device_ops ixgbe_netdev_ops = { .ndo_open = ixgbe_open, .ndo_stop = ixgbe_close, @@ -9513,6 +9629,7 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_udp_tunnel_add = ixgbe_add_udp_tunnel_port, .ndo_udp_tunnel_del = ixgbe_del_udp_tunnel_port, .ndo_features_check = ixgbe_features_check, + .ndo_xdp = ixgbe_xdp, }; /** -- cgit From 33fdc82f08835de4c39a00657742f5b11db00d32 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 24 Apr 2017 03:30:18 -0700 Subject: ixgbe: add support for XDP_TX action A couple design choices were made here. First I use a new ring pointer structure xdp_ring[] in the adapter struct instead of pushing the newly allocated XDP TX rings into the tx_ring[] structure. This means we have to duplicate loops around rings in places we want to initialize both TX rings and XDP rings. But by making it explicit it is obvious when we are using XDP rings and when we are using TX rings. Further we don't have to do ring arithmatic which is error prone. As a proof point for doing this my first patches used only a single ring structure and introduced bugs in FCoE code and macvlan code paths. Second I am aware this is not the most optimized version of this code possible. I want to get baseline support in using the most readable format possible and then once this series is included I will optimize the TX path in another series of patches. Signed-off-by: John Fastabend Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 19 +- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 25 ++ drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c | 75 +++++- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 282 +++++++++++++++++++---- 4 files changed, 348 insertions(+), 53 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index cb14813b0080..e8cd4491f1fd 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -235,7 +235,11 @@ struct vf_macvlans { struct ixgbe_tx_buffer { union ixgbe_adv_tx_desc *next_to_watch; unsigned long time_stamp; - struct sk_buff *skb; + union { + struct sk_buff *skb; + /* XDP uses address ptr on irq_clean */ + void *data; + }; unsigned int bytecount; unsigned short gso_segs; __be16 protocol; @@ -288,6 +292,7 @@ enum ixgbe_ring_state_t { __IXGBE_TX_XPS_INIT_DONE, __IXGBE_TX_DETECT_HANG, __IXGBE_HANG_CHECK_ARMED, + __IXGBE_TX_XDP_RING, }; #define ring_uses_build_skb(ring) \ @@ -314,6 +319,12 @@ struct ixgbe_fwd_adapter { set_bit(__IXGBE_RX_RSC_ENABLED, &(ring)->state) #define clear_ring_rsc_enabled(ring) \ clear_bit(__IXGBE_RX_RSC_ENABLED, &(ring)->state) +#define ring_is_xdp(ring) \ + test_bit(__IXGBE_TX_XDP_RING, &(ring)->state) +#define set_ring_xdp(ring) \ + set_bit(__IXGBE_TX_XDP_RING, &(ring)->state) +#define clear_ring_xdp(ring) \ + clear_bit(__IXGBE_TX_XDP_RING, &(ring)->state) struct ixgbe_ring { struct ixgbe_ring *next; /* pointer to next ring in q_vector */ struct ixgbe_q_vector *q_vector; /* backpointer to host q_vector */ @@ -380,6 +391,7 @@ enum ixgbe_ring_f_enum { #define IXGBE_MAX_FCOE_INDICES 8 #define MAX_RX_QUEUES (IXGBE_MAX_FDIR_INDICES + 1) #define MAX_TX_QUEUES (IXGBE_MAX_FDIR_INDICES + 1) +#define MAX_XDP_QUEUES (IXGBE_MAX_FDIR_INDICES + 1) #define IXGBE_MAX_L2A_QUEUES 4 #define IXGBE_BAD_L2A_QUEUE 3 #define IXGBE_MAX_MACVLANS 31 @@ -623,6 +635,10 @@ struct ixgbe_adapter { __be16 vxlan_port; __be16 geneve_port; + /* XDP */ + int num_xdp_queues; + struct ixgbe_ring *xdp_ring[MAX_XDP_QUEUES]; + /* TX */ struct ixgbe_ring *tx_ring[MAX_TX_QUEUES] ____cacheline_aligned_in_smp; @@ -669,6 +685,7 @@ struct ixgbe_adapter { u64 tx_busy; unsigned int tx_ring_count; + unsigned int xdp_ring_count; unsigned int rx_ring_count; u32 link_speed; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 79a126d9e091..b0fd2f58a69c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -1071,15 +1071,19 @@ static int ixgbe_set_ringparam(struct net_device *netdev, if (!netif_running(adapter->netdev)) { for (i = 0; i < adapter->num_tx_queues; i++) adapter->tx_ring[i]->count = new_tx_count; + for (i = 0; i < adapter->num_xdp_queues; i++) + adapter->xdp_ring[i]->count = new_tx_count; for (i = 0; i < adapter->num_rx_queues; i++) adapter->rx_ring[i]->count = new_rx_count; adapter->tx_ring_count = new_tx_count; + adapter->xdp_ring_count = new_tx_count; adapter->rx_ring_count = new_rx_count; goto clear_reset; } /* allocate temporary buffer to store rings in */ i = max_t(int, adapter->num_tx_queues, adapter->num_rx_queues); + i = max_t(int, i, adapter->num_xdp_queues); temp_ring = vmalloc(i * sizeof(struct ixgbe_ring)); if (!temp_ring) { @@ -1111,12 +1115,33 @@ static int ixgbe_set_ringparam(struct net_device *netdev, } } + for (i = 0; i < adapter->num_xdp_queues; i++) { + memcpy(&temp_ring[i], adapter->xdp_ring[i], + sizeof(struct ixgbe_ring)); + + temp_ring[i].count = new_tx_count; + err = ixgbe_setup_tx_resources(&temp_ring[i]); + if (err) { + while (i) { + i--; + ixgbe_free_tx_resources(&temp_ring[i]); + } + goto err_setup; + } + } + for (i = 0; i < adapter->num_tx_queues; i++) { ixgbe_free_tx_resources(adapter->tx_ring[i]); memcpy(adapter->tx_ring[i], &temp_ring[i], sizeof(struct ixgbe_ring)); } + for (i = 0; i < adapter->num_xdp_queues; i++) { + ixgbe_free_tx_resources(adapter->xdp_ring[i]); + + memcpy(adapter->xdp_ring[i], &temp_ring[i], + sizeof(struct ixgbe_ring)); + } adapter->tx_ring_count = new_tx_count; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c index 1b8be7d813bd..b45fdc98033d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c @@ -267,12 +267,14 @@ static bool ixgbe_cache_ring_sriov(struct ixgbe_adapter *adapter) **/ static bool ixgbe_cache_ring_rss(struct ixgbe_adapter *adapter) { - int i; + int i, reg_idx; for (i = 0; i < adapter->num_rx_queues; i++) adapter->rx_ring[i]->reg_idx = i; - for (i = 0; i < adapter->num_tx_queues; i++) - adapter->tx_ring[i]->reg_idx = i; + for (i = 0, reg_idx = 0; i < adapter->num_tx_queues; i++, reg_idx++) + adapter->tx_ring[i]->reg_idx = reg_idx; + for (i = 0; i < adapter->num_xdp_queues; i++, reg_idx++) + adapter->xdp_ring[i]->reg_idx = reg_idx; return true; } @@ -308,6 +310,11 @@ static void ixgbe_cache_ring_register(struct ixgbe_adapter *adapter) ixgbe_cache_ring_rss(adapter); } +static int ixgbe_xdp_queues(struct ixgbe_adapter *adapter) +{ + return adapter->xdp_prog ? nr_cpu_ids : 0; +} + #define IXGBE_RSS_64Q_MASK 0x3F #define IXGBE_RSS_16Q_MASK 0xF #define IXGBE_RSS_8Q_MASK 0x7 @@ -382,6 +389,7 @@ static bool ixgbe_set_dcb_sriov_queues(struct ixgbe_adapter *adapter) adapter->num_rx_queues_per_pool = tcs; adapter->num_tx_queues = vmdq_i * tcs; + adapter->num_xdp_queues = 0; adapter->num_rx_queues = vmdq_i * tcs; #ifdef IXGBE_FCOE @@ -479,6 +487,7 @@ static bool ixgbe_set_dcb_queues(struct ixgbe_adapter *adapter) netdev_set_tc_queue(dev, i, rss_i, rss_i * i); adapter->num_tx_queues = rss_i * tcs; + adapter->num_xdp_queues = 0; adapter->num_rx_queues = rss_i * tcs; return true; @@ -549,6 +558,7 @@ static bool ixgbe_set_sriov_queues(struct ixgbe_adapter *adapter) adapter->num_rx_queues = vmdq_i * rss_i; adapter->num_tx_queues = vmdq_i * rss_i; + adapter->num_xdp_queues = 0; /* disable ATR as it is not supported when VMDq is enabled */ adapter->flags &= ~IXGBE_FLAG_FDIR_HASH_CAPABLE; @@ -669,6 +679,7 @@ static bool ixgbe_set_rss_queues(struct ixgbe_adapter *adapter) #endif /* IXGBE_FCOE */ adapter->num_rx_queues = rss_i; adapter->num_tx_queues = rss_i; + adapter->num_xdp_queues = ixgbe_xdp_queues(adapter); return true; } @@ -689,6 +700,7 @@ static void ixgbe_set_num_queues(struct ixgbe_adapter *adapter) /* Start with base case */ adapter->num_rx_queues = 1; adapter->num_tx_queues = 1; + adapter->num_xdp_queues = 0; adapter->num_rx_pools = adapter->num_rx_queues; adapter->num_rx_queues_per_pool = 1; @@ -719,8 +731,11 @@ static int ixgbe_acquire_msix_vectors(struct ixgbe_adapter *adapter) struct ixgbe_hw *hw = &adapter->hw; int i, vectors, vector_threshold; - /* We start by asking for one vector per queue pair */ + /* We start by asking for one vector per queue pair with XDP queues + * being stacked with TX queues. + */ vectors = max(adapter->num_rx_queues, adapter->num_tx_queues); + vectors = max(vectors, adapter->num_xdp_queues); /* It is easy to be greedy for MSI-X vectors. However, it really * doesn't do much good if we have a lot more vectors than CPUs. We'll @@ -800,6 +815,8 @@ static void ixgbe_add_ring(struct ixgbe_ring *ring, * @v_idx: index of vector in adapter struct * @txr_count: total number of Tx rings to allocate * @txr_idx: index of first Tx ring to allocate + * @xdp_count: total number of XDP rings to allocate + * @xdp_idx: index of first XDP ring to allocate * @rxr_count: total number of Rx rings to allocate * @rxr_idx: index of first Rx ring to allocate * @@ -808,6 +825,7 @@ static void ixgbe_add_ring(struct ixgbe_ring *ring, static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter, int v_count, int v_idx, int txr_count, int txr_idx, + int xdp_count, int xdp_idx, int rxr_count, int rxr_idx) { struct ixgbe_q_vector *q_vector; @@ -817,7 +835,7 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter, int ring_count, size; u8 tcs = netdev_get_num_tc(adapter->netdev); - ring_count = txr_count + rxr_count; + ring_count = txr_count + rxr_count + xdp_count; size = sizeof(struct ixgbe_q_vector) + (sizeof(struct ixgbe_ring) * ring_count); @@ -909,6 +927,33 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter, ring++; } + while (xdp_count) { + /* assign generic ring traits */ + ring->dev = &adapter->pdev->dev; + ring->netdev = adapter->netdev; + + /* configure backlink on ring */ + ring->q_vector = q_vector; + + /* update q_vector Tx values */ + ixgbe_add_ring(ring, &q_vector->tx); + + /* apply Tx specific ring traits */ + ring->count = adapter->tx_ring_count; + ring->queue_index = xdp_idx; + set_ring_xdp(ring); + + /* assign ring to adapter */ + adapter->xdp_ring[xdp_idx] = ring; + + /* update count and index */ + xdp_count--; + xdp_idx++; + + /* push pointer to next ring */ + ring++; + } + while (rxr_count) { /* assign generic ring traits */ ring->dev = &adapter->pdev->dev; @@ -1002,17 +1047,18 @@ static int ixgbe_alloc_q_vectors(struct ixgbe_adapter *adapter) int q_vectors = adapter->num_q_vectors; int rxr_remaining = adapter->num_rx_queues; int txr_remaining = adapter->num_tx_queues; - int rxr_idx = 0, txr_idx = 0, v_idx = 0; + int xdp_remaining = adapter->num_xdp_queues; + int rxr_idx = 0, txr_idx = 0, xdp_idx = 0, v_idx = 0; int err; /* only one q_vector if MSI-X is disabled. */ if (!(adapter->flags & IXGBE_FLAG_MSIX_ENABLED)) q_vectors = 1; - if (q_vectors >= (rxr_remaining + txr_remaining)) { + if (q_vectors >= (rxr_remaining + txr_remaining + xdp_remaining)) { for (; rxr_remaining; v_idx++) { err = ixgbe_alloc_q_vector(adapter, q_vectors, v_idx, - 0, 0, 1, rxr_idx); + 0, 0, 0, 0, 1, rxr_idx); if (err) goto err_out; @@ -1026,8 +1072,11 @@ static int ixgbe_alloc_q_vectors(struct ixgbe_adapter *adapter) for (; v_idx < q_vectors; v_idx++) { int rqpv = DIV_ROUND_UP(rxr_remaining, q_vectors - v_idx); int tqpv = DIV_ROUND_UP(txr_remaining, q_vectors - v_idx); + int xqpv = DIV_ROUND_UP(xdp_remaining, q_vectors - v_idx); + err = ixgbe_alloc_q_vector(adapter, q_vectors, v_idx, tqpv, txr_idx, + xqpv, xdp_idx, rqpv, rxr_idx); if (err) @@ -1036,14 +1085,17 @@ static int ixgbe_alloc_q_vectors(struct ixgbe_adapter *adapter) /* update counts and index */ rxr_remaining -= rqpv; txr_remaining -= tqpv; + xdp_remaining -= xqpv; rxr_idx++; txr_idx++; + xdp_idx += xqpv; } return 0; err_out: adapter->num_tx_queues = 0; + adapter->num_xdp_queues = 0; adapter->num_rx_queues = 0; adapter->num_q_vectors = 0; @@ -1066,6 +1118,7 @@ static void ixgbe_free_q_vectors(struct ixgbe_adapter *adapter) int v_idx = adapter->num_q_vectors; adapter->num_tx_queues = 0; + adapter->num_xdp_queues = 0; adapter->num_rx_queues = 0; adapter->num_q_vectors = 0; @@ -1172,9 +1225,10 @@ int ixgbe_init_interrupt_scheme(struct ixgbe_adapter *adapter) ixgbe_cache_ring_register(adapter); - e_dev_info("Multiqueue %s: Rx Queue count = %u, Tx Queue count = %u\n", + e_dev_info("Multiqueue %s: Rx Queue count = %u, Tx Queue count = %u XDP Queue count = %u\n", (adapter->num_rx_queues > 1) ? "Enabled" : "Disabled", - adapter->num_rx_queues, adapter->num_tx_queues); + adapter->num_rx_queues, adapter->num_tx_queues, + adapter->num_xdp_queues); set_bit(__IXGBE_DOWN, &adapter->state); @@ -1195,6 +1249,7 @@ err_alloc_q_vectors: void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter) { adapter->num_tx_queues = 0; + adapter->num_xdp_queues = 0; adapter->num_rx_queues = 0; ixgbe_free_q_vectors(adapter); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 99b5357c3e00..cb5be7de2c91 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -593,6 +593,19 @@ static void ixgbe_regdump(struct ixgbe_hw *hw, struct ixgbe_reg_info *reginfo) } +static void ixgbe_print_buffer(struct ixgbe_ring *ring, int n) +{ + struct ixgbe_tx_buffer *tx_buffer; + + tx_buffer = &ring->tx_buffer_info[ring->next_to_clean]; + pr_info(" %5d %5X %5X %016llX %08X %p %016llX\n", + n, ring->next_to_use, ring->next_to_clean, + (u64)dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + tx_buffer->next_to_watch, + (u64)tx_buffer->time_stamp); +} + /* * ixgbe_dump - Print registers, tx-rings and rx-rings */ @@ -602,7 +615,7 @@ static void ixgbe_dump(struct ixgbe_adapter *adapter) struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_reg_info *reginfo; int n = 0; - struct ixgbe_ring *tx_ring; + struct ixgbe_ring *ring; struct ixgbe_tx_buffer *tx_buffer; union ixgbe_adv_tx_desc *tx_desc; struct my_u0 { u64 a; u64 b; } *u0; @@ -642,14 +655,13 @@ static void ixgbe_dump(struct ixgbe_adapter *adapter) "Queue [NTU] [NTC] [bi(ntc)->dma ]", "leng", "ntw", "timestamp"); for (n = 0; n < adapter->num_tx_queues; n++) { - tx_ring = adapter->tx_ring[n]; - tx_buffer = &tx_ring->tx_buffer_info[tx_ring->next_to_clean]; - pr_info(" %5d %5X %5X %016llX %08X %p %016llX\n", - n, tx_ring->next_to_use, tx_ring->next_to_clean, - (u64)dma_unmap_addr(tx_buffer, dma), - dma_unmap_len(tx_buffer, len), - tx_buffer->next_to_watch, - (u64)tx_buffer->time_stamp); + ring = adapter->tx_ring[n]; + ixgbe_print_buffer(ring, n); + } + + for (n = 0; n < adapter->num_xdp_queues; n++) { + ring = adapter->xdp_ring[n]; + ixgbe_print_buffer(ring, n); } /* Print TX Rings */ @@ -694,28 +706,28 @@ static void ixgbe_dump(struct ixgbe_adapter *adapter) */ for (n = 0; n < adapter->num_tx_queues; n++) { - tx_ring = adapter->tx_ring[n]; + ring = adapter->tx_ring[n]; pr_info("------------------------------------\n"); - pr_info("TX QUEUE INDEX = %d\n", tx_ring->queue_index); + pr_info("TX QUEUE INDEX = %d\n", ring->queue_index); pr_info("------------------------------------\n"); pr_info("%s%s %s %s %s %s\n", "T [desc] [address 63:0 ] ", "[PlPOIdStDDt Ln] [bi->dma ] ", "leng", "ntw", "timestamp", "bi->skb"); - for (i = 0; tx_ring->desc && (i < tx_ring->count); i++) { - tx_desc = IXGBE_TX_DESC(tx_ring, i); - tx_buffer = &tx_ring->tx_buffer_info[i]; + for (i = 0; ring->desc && (i < ring->count); i++) { + tx_desc = IXGBE_TX_DESC(ring, i); + tx_buffer = &ring->tx_buffer_info[i]; u0 = (struct my_u0 *)tx_desc; if (dma_unmap_len(tx_buffer, len) > 0) { const char *ring_desc; - if (i == tx_ring->next_to_use && - i == tx_ring->next_to_clean) + if (i == ring->next_to_use && + i == ring->next_to_clean) ring_desc = " NTC/U"; - else if (i == tx_ring->next_to_use) + else if (i == ring->next_to_use) ring_desc = " NTU"; - else if (i == tx_ring->next_to_clean) + else if (i == ring->next_to_clean) ring_desc = " NTC"; else ring_desc = ""; @@ -984,6 +996,10 @@ static void ixgbe_update_xoff_rx_lfc(struct ixgbe_adapter *adapter) for (i = 0; i < adapter->num_tx_queues; i++) clear_bit(__IXGBE_HANG_CHECK_ARMED, &adapter->tx_ring[i]->state); + + for (i = 0; i < adapter->num_xdp_queues; i++) + clear_bit(__IXGBE_HANG_CHECK_ARMED, + &adapter->xdp_ring[i]->state); } static void ixgbe_update_xoff_received(struct ixgbe_adapter *adapter) @@ -1028,6 +1044,14 @@ static void ixgbe_update_xoff_received(struct ixgbe_adapter *adapter) if (xoff[tc]) clear_bit(__IXGBE_HANG_CHECK_ARMED, &tx_ring->state); } + + for (i = 0; i < adapter->num_xdp_queues; i++) { + struct ixgbe_ring *xdp_ring = adapter->xdp_ring[i]; + + tc = xdp_ring->dcb_tc; + if (xoff[tc]) + clear_bit(__IXGBE_HANG_CHECK_ARMED, &xdp_ring->state); + } } static u64 ixgbe_get_tx_completed(struct ixgbe_ring *ring) @@ -1179,7 +1203,10 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, total_packets += tx_buffer->gso_segs; /* free the skb */ - napi_consume_skb(tx_buffer->skb, napi_budget); + if (ring_is_xdp(tx_ring)) + page_frag_free(tx_buffer->data); + else + napi_consume_skb(tx_buffer->skb, napi_budget); /* unmap skb header data */ dma_unmap_single(tx_ring->dev, @@ -1240,7 +1267,7 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, if (check_for_tx_hang(tx_ring) && ixgbe_check_tx_hang(tx_ring)) { /* schedule immediate reset if we believe we hung */ struct ixgbe_hw *hw = &adapter->hw; - e_err(drv, "Detected Tx Unit Hang\n" + e_err(drv, "Detected Tx Unit Hang %s\n" " Tx Queue <%d>\n" " TDH, TDT <%x>, <%x>\n" " next_to_use <%x>\n" @@ -1248,13 +1275,16 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, "tx_buffer_info[next_to_clean]\n" " time_stamp <%lx>\n" " jiffies <%lx>\n", + ring_is_xdp(tx_ring) ? "(XDP)" : "", tx_ring->queue_index, IXGBE_READ_REG(hw, IXGBE_TDH(tx_ring->reg_idx)), IXGBE_READ_REG(hw, IXGBE_TDT(tx_ring->reg_idx)), tx_ring->next_to_use, i, tx_ring->tx_buffer_info[i].time_stamp, jiffies); - netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index); + if (!ring_is_xdp(tx_ring)) + netif_stop_subqueue(tx_ring->netdev, + tx_ring->queue_index); e_info(probe, "tx hang %d detected on queue %d, resetting adapter\n", @@ -1267,6 +1297,9 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, return true; } + if (ring_is_xdp(tx_ring)) + return !!budget; + netdev_tx_completed_queue(txring_txq(tx_ring), total_packets, total_bytes); @@ -2169,8 +2202,13 @@ static struct sk_buff *ixgbe_build_skb(struct ixgbe_ring *rx_ring, #define IXGBE_XDP_PASS 0 #define IXGBE_XDP_CONSUMED 1 +#define IXGBE_XDP_TX 2 -static struct sk_buff *ixgbe_run_xdp(struct ixgbe_ring *rx_ring, +static int ixgbe_xmit_xdp_ring(struct ixgbe_adapter *adapter, + struct xdp_buff *xdp); + +static struct sk_buff *ixgbe_run_xdp(struct ixgbe_adapter *adapter, + struct ixgbe_ring *rx_ring, struct xdp_buff *xdp) { int result = IXGBE_XDP_PASS; @@ -2187,9 +2225,11 @@ static struct sk_buff *ixgbe_run_xdp(struct ixgbe_ring *rx_ring, switch (act) { case XDP_PASS: break; + case XDP_TX: + result = ixgbe_xmit_xdp_ring(adapter, xdp); + break; default: bpf_warn_invalid_xdp_action(act); - case XDP_TX: case XDP_ABORTED: trace_xdp_exception(rx_ring->netdev, xdp_prog, act); /* fallthrough -- handle aborts by dropping packet */ @@ -2202,6 +2242,23 @@ xdp_out: return ERR_PTR(-result); } +static void ixgbe_rx_buffer_flip(struct ixgbe_ring *rx_ring, + struct ixgbe_rx_buffer *rx_buffer, + unsigned int size) +{ +#if (PAGE_SIZE < 8192) + unsigned int truesize = ixgbe_rx_pg_size(rx_ring) / 2; + + rx_buffer->page_offset ^= truesize; +#else + unsigned int truesize = ring_uses_build_skb(rx_ring) ? + SKB_DATA_ALIGN(IXGBE_SKB_PAD + size) : + SKB_DATA_ALIGN(size); + + rx_buffer->page_offset += truesize; +#endif +} + /** * ixgbe_clean_rx_irq - Clean completed descriptors from Rx ring - bounce buf * @q_vector: structure containing interrupt and ring information @@ -2220,8 +2277,8 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, const int budget) { unsigned int total_rx_bytes = 0, total_rx_packets = 0; -#ifdef IXGBE_FCOE struct ixgbe_adapter *adapter = q_vector->adapter; +#ifdef IXGBE_FCOE int ddp_bytes; unsigned int mss = 0; #endif /* IXGBE_FCOE */ @@ -2261,13 +2318,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, ixgbe_rx_offset(rx_ring); xdp.data_end = xdp.data + size; - skb = ixgbe_run_xdp(rx_ring, &xdp); + skb = ixgbe_run_xdp(adapter, rx_ring, &xdp); } if (IS_ERR(skb)) { + if (PTR_ERR(skb) == -IXGBE_XDP_TX) + ixgbe_rx_buffer_flip(rx_ring, rx_buffer, size); + else + rx_buffer->pagecnt_bias++; total_rx_packets++; total_rx_bytes += size; - rx_buffer->pagecnt_bias++; } else if (skb) { ixgbe_add_rx_frag(rx_ring, rx_buffer, skb, size); } else if (ring_uses_build_skb(rx_ring)) { @@ -3437,6 +3497,8 @@ static void ixgbe_configure_tx(struct ixgbe_adapter *adapter) /* Setup the HW Tx Head and Tail descriptor pointers */ for (i = 0; i < adapter->num_tx_queues; i++) ixgbe_configure_tx_ring(adapter, adapter->tx_ring[i]); + for (i = 0; i < adapter->num_xdp_queues; i++) + ixgbe_configure_tx_ring(adapter, adapter->xdp_ring[i]); } static void ixgbe_enable_rx_drop(struct ixgbe_adapter *adapter, @@ -5578,7 +5640,10 @@ static void ixgbe_clean_tx_ring(struct ixgbe_ring *tx_ring) union ixgbe_adv_tx_desc *eop_desc, *tx_desc; /* Free all the Tx ring sk_buffs */ - dev_kfree_skb_any(tx_buffer->skb); + if (ring_is_xdp(tx_ring)) + page_frag_free(tx_buffer->data); + else + dev_kfree_skb_any(tx_buffer->skb); /* unmap skb header data */ dma_unmap_single(tx_ring->dev, @@ -5619,7 +5684,8 @@ static void ixgbe_clean_tx_ring(struct ixgbe_ring *tx_ring) } /* reset BQL for queue */ - netdev_tx_reset_queue(txring_txq(tx_ring)); + if (!ring_is_xdp(tx_ring)) + netdev_tx_reset_queue(txring_txq(tx_ring)); /* reset next_to_use and next_to_clean */ tx_ring->next_to_use = 0; @@ -5648,6 +5714,8 @@ static void ixgbe_clean_all_tx_rings(struct ixgbe_adapter *adapter) for (i = 0; i < adapter->num_tx_queues; i++) ixgbe_clean_tx_ring(adapter->tx_ring[i]); + for (i = 0; i < adapter->num_xdp_queues; i++) + ixgbe_clean_tx_ring(adapter->xdp_ring[i]); } static void ixgbe_fdir_filter_exit(struct ixgbe_adapter *adapter) @@ -5742,6 +5810,11 @@ void ixgbe_down(struct ixgbe_adapter *adapter) u8 reg_idx = adapter->tx_ring[i]->reg_idx; IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), IXGBE_TXDCTL_SWFLSH); } + for (i = 0; i < adapter->num_xdp_queues; i++) { + u8 reg_idx = adapter->xdp_ring[i]->reg_idx; + + IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), IXGBE_TXDCTL_SWFLSH); + } /* Disable the Tx DMA engine on 82599 and later MAC */ switch (hw->mac.type) { @@ -6112,7 +6185,7 @@ err: **/ static int ixgbe_setup_all_tx_resources(struct ixgbe_adapter *adapter) { - int i, err = 0; + int i, j = 0, err = 0; for (i = 0; i < adapter->num_tx_queues; i++) { err = ixgbe_setup_tx_resources(adapter->tx_ring[i]); @@ -6122,10 +6195,20 @@ static int ixgbe_setup_all_tx_resources(struct ixgbe_adapter *adapter) e_err(probe, "Allocation for Tx Queue %u failed\n", i); goto err_setup_tx; } + for (j = 0; j < adapter->num_xdp_queues; j++) { + err = ixgbe_setup_tx_resources(adapter->xdp_ring[j]); + if (!err) + continue; + + e_err(probe, "Allocation for Tx Queue %u failed\n", j); + goto err_setup_tx; + } return 0; err_setup_tx: /* rewind the index freeing the rings as we go */ + while (j--) + ixgbe_free_tx_resources(adapter->xdp_ring[j]); while (i--) ixgbe_free_tx_resources(adapter->tx_ring[i]); return err; @@ -6258,6 +6341,9 @@ static void ixgbe_free_all_tx_resources(struct ixgbe_adapter *adapter) for (i = 0; i < adapter->num_tx_queues; i++) if (adapter->tx_ring[i]->desc) ixgbe_free_tx_resources(adapter->tx_ring[i]); + for (i = 0; i < adapter->num_xdp_queues; i++) + if (adapter->xdp_ring[i]->desc) + ixgbe_free_tx_resources(adapter->xdp_ring[i]); } /** @@ -6677,6 +6763,14 @@ void ixgbe_update_stats(struct ixgbe_adapter *adapter) bytes += tx_ring->stats.bytes; packets += tx_ring->stats.packets; } + for (i = 0; i < adapter->num_xdp_queues; i++) { + struct ixgbe_ring *xdp_ring = adapter->xdp_ring[i]; + + restart_queue += xdp_ring->tx_stats.restart_queue; + tx_busy += xdp_ring->tx_stats.tx_busy; + bytes += xdp_ring->stats.bytes; + packets += xdp_ring->stats.packets; + } adapter->restart_queue = restart_queue; adapter->tx_busy = tx_busy; netdev->stats.tx_bytes = bytes; @@ -6870,6 +6964,9 @@ static void ixgbe_fdir_reinit_subtask(struct ixgbe_adapter *adapter) for (i = 0; i < adapter->num_tx_queues; i++) set_bit(__IXGBE_TX_FDIR_INIT_DONE, &(adapter->tx_ring[i]->state)); + for (i = 0; i < adapter->num_xdp_queues; i++) + set_bit(__IXGBE_TX_FDIR_INIT_DONE, + &adapter->xdp_ring[i]->state); /* re-enable flow director interrupts */ IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_FLOW_DIR); } else { @@ -6903,6 +7000,8 @@ static void ixgbe_check_hang_subtask(struct ixgbe_adapter *adapter) if (netif_carrier_ok(adapter->netdev)) { for (i = 0; i < adapter->num_tx_queues; i++) set_check_for_tx_hang(adapter->tx_ring[i]); + for (i = 0; i < adapter->num_xdp_queues; i++) + set_check_for_tx_hang(adapter->xdp_ring[i]); } if (!(adapter->flags & IXGBE_FLAG_MSIX_ENABLED)) { @@ -7133,6 +7232,13 @@ static bool ixgbe_ring_tx_pending(struct ixgbe_adapter *adapter) return true; } + for (i = 0; i < adapter->num_xdp_queues; i++) { + struct ixgbe_ring *ring = adapter->xdp_ring[i]; + + if (ring->next_to_use != ring->next_to_clean) + return true; + } + return false; } @@ -8090,6 +8196,69 @@ static u16 ixgbe_select_queue(struct net_device *dev, struct sk_buff *skb, #endif } +static int ixgbe_xmit_xdp_ring(struct ixgbe_adapter *adapter, + struct xdp_buff *xdp) +{ + struct ixgbe_ring *ring = adapter->xdp_ring[smp_processor_id()]; + struct ixgbe_tx_buffer *tx_buffer; + union ixgbe_adv_tx_desc *tx_desc; + u32 len, cmd_type; + dma_addr_t dma; + u16 i; + + len = xdp->data_end - xdp->data; + + if (unlikely(!ixgbe_desc_unused(ring))) + return IXGBE_XDP_CONSUMED; + + dma = dma_map_single(ring->dev, xdp->data, len, DMA_TO_DEVICE); + if (dma_mapping_error(ring->dev, dma)) + return IXGBE_XDP_CONSUMED; + + /* record the location of the first descriptor for this packet */ + tx_buffer = &ring->tx_buffer_info[ring->next_to_use]; + tx_buffer->bytecount = len; + tx_buffer->gso_segs = 1; + tx_buffer->protocol = 0; + + i = ring->next_to_use; + tx_desc = IXGBE_TX_DESC(ring, i); + + dma_unmap_len_set(tx_buffer, len, len); + dma_unmap_addr_set(tx_buffer, dma, dma); + tx_buffer->data = xdp->data; + tx_desc->read.buffer_addr = cpu_to_le64(dma); + + /* put descriptor type bits */ + cmd_type = IXGBE_ADVTXD_DTYP_DATA | + IXGBE_ADVTXD_DCMD_DEXT | + IXGBE_ADVTXD_DCMD_IFCS; + cmd_type |= len | IXGBE_TXD_CMD; + tx_desc->read.cmd_type_len = cpu_to_le32(cmd_type); + tx_desc->read.olinfo_status = + cpu_to_le32(len << IXGBE_ADVTXD_PAYLEN_SHIFT); + + /* Force memory writes to complete before letting h/w know there + * are new descriptors to fetch. (Only applicable for weak-ordered + * memory model archs, such as IA-64). + * + * We also need this memory barrier to make certain all of the + * status bits have been updated before next_to_watch is written. + */ + wmb(); + + /* set next_to_watch value indicating a packet is present */ + i++; + if (i == ring->count) + i = 0; + + tx_buffer->next_to_watch = tx_desc; + ring->next_to_use = i; + + writel(i, ring->tail); + return IXGBE_XDP_TX; +} + netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb, struct ixgbe_adapter *adapter, struct ixgbe_ring *tx_ring) @@ -8381,6 +8550,23 @@ static void ixgbe_netpoll(struct net_device *netdev) #endif +static void ixgbe_get_ring_stats64(struct rtnl_link_stats64 *stats, + struct ixgbe_ring *ring) +{ + u64 bytes, packets; + unsigned int start; + + if (ring) { + do { + start = u64_stats_fetch_begin_irq(&ring->syncp); + packets = ring->stats.packets; + bytes = ring->stats.bytes; + } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + stats->tx_packets += packets; + stats->tx_bytes += bytes; + } +} + static void ixgbe_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) { @@ -8406,18 +8592,13 @@ static void ixgbe_get_stats64(struct net_device *netdev, for (i = 0; i < adapter->num_tx_queues; i++) { struct ixgbe_ring *ring = ACCESS_ONCE(adapter->tx_ring[i]); - u64 bytes, packets; - unsigned int start; - if (ring) { - do { - start = u64_stats_fetch_begin_irq(&ring->syncp); - packets = ring->stats.packets; - bytes = ring->stats.bytes; - } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); - stats->tx_packets += packets; - stats->tx_bytes += bytes; - } + ixgbe_get_ring_stats64(stats, ring); + } + for (i = 0; i < adapter->num_xdp_queues; i++) { + struct ixgbe_ring *ring = ACCESS_ONCE(adapter->xdp_ring[i]); + + ixgbe_get_ring_stats64(stats, ring); } rcu_read_unlock(); @@ -9559,9 +9740,23 @@ static int ixgbe_xdp_setup(struct net_device *dev, struct bpf_prog *prog) return -EINVAL; } + if (nr_cpu_ids > MAX_XDP_QUEUES) + return -ENOMEM; + old_prog = xchg(&adapter->xdp_prog, prog); - for (i = 0; i < adapter->num_rx_queues; i++) - xchg(&adapter->rx_ring[i]->xdp_prog, adapter->xdp_prog); + + /* If transitioning XDP modes reconfigure rings */ + if (!!prog != !!old_prog) { + int err = ixgbe_setup_tc(dev, netdev_get_num_tc(dev)); + + if (err) { + rcu_assign_pointer(adapter->xdp_prog, old_prog); + return -EINVAL; + } + } else { + for (i = 0; i < adapter->num_rx_queues; i++) + xchg(&adapter->rx_ring[i]->xdp_prog, adapter->xdp_prog); + } if (old_prog) bpf_prog_put(old_prog); @@ -10060,6 +10255,9 @@ skip_sriov: if (err) goto err_sw_init; + for (i = 0; i < adapter->num_xdp_queues; i++) + u64_stats_init(&adapter->xdp_ring[i]->syncp); + /* WOL not supported for all devices */ adapter->wol = 0; hw->eeprom.ops.read(hw, 0x2c, &adapter->eeprom_cap); -- cgit From 7379f97a4fce3c1aa3b80a85cb8440453bf30411 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Tue, 28 Mar 2017 09:47:03 -0700 Subject: ixgbe: delay tail write to every 'n' packets Current XDP implementation hits the tail on every XDP_TX return code. This patch changes driver behavior to only hit the tail after packet processing is complete. With this patch I can run XDP drop programs @ 14+Mpps and XDP_TX programs are at ~13.5Mpps. Signed-off-by: John Fastabend Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 28 ++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index cb5be7de2c91..3d7b09100945 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2283,6 +2283,7 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, unsigned int mss = 0; #endif /* IXGBE_FCOE */ u16 cleaned_count = ixgbe_desc_unused(rx_ring); + bool xdp_xmit = false; while (likely(total_rx_packets < budget)) { union ixgbe_adv_rx_desc *rx_desc; @@ -2322,10 +2323,12 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, } if (IS_ERR(skb)) { - if (PTR_ERR(skb) == -IXGBE_XDP_TX) + if (PTR_ERR(skb) == -IXGBE_XDP_TX) { + xdp_xmit = true; ixgbe_rx_buffer_flip(rx_ring, rx_buffer, size); - else + } else { rx_buffer->pagecnt_bias++; + } total_rx_packets++; total_rx_bytes += size; } else if (skb) { @@ -2393,6 +2396,16 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, total_rx_packets++; } + if (xdp_xmit) { + struct ixgbe_ring *ring = adapter->xdp_ring[smp_processor_id()]; + + /* Force memory writes to complete before letting h/w + * know there are new descriptors to fetch. + */ + wmb(); + writel(ring->next_to_use, ring->tail); + } + u64_stats_update_begin(&rx_ring->syncp); rx_ring->stats.packets += total_rx_packets; rx_ring->stats.bytes += total_rx_bytes; @@ -8238,14 +8251,8 @@ static int ixgbe_xmit_xdp_ring(struct ixgbe_adapter *adapter, tx_desc->read.olinfo_status = cpu_to_le32(len << IXGBE_ADVTXD_PAYLEN_SHIFT); - /* Force memory writes to complete before letting h/w know there - * are new descriptors to fetch. (Only applicable for weak-ordered - * memory model archs, such as IA-64). - * - * We also need this memory barrier to make certain all of the - * status bits have been updated before next_to_watch is written. - */ - wmb(); + /* Avoid any potential race with xdp_xmit and cleanup */ + smp_wmb(); /* set next_to_watch value indicating a packet is present */ i++; @@ -8255,7 +8262,6 @@ static int ixgbe_xmit_xdp_ring(struct ixgbe_adapter *adapter, tx_buffer->next_to_watch = tx_desc; ring->next_to_use = i; - writel(i, ring->tail); return IXGBE_XDP_TX; } -- cgit From e251ecf75226d5ff772df1ef170b8b981308dc68 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Tue, 28 Mar 2017 11:27:54 -0700 Subject: ixgbe: clean macvlan MAC filter table on VF reset Flush the macvlan filters on VF reset to avoid conflict with other VFs that may end up using the same MAC address. The main change here is the call to ixgbe_set_vf_macvlan() with index 0. Moved ixgbe_set_vf_macvlan() in front of ixgbe_vf_reset_event() to avoid adding a prototype. Reported-by: Sritej Kanakadandi Sritej Rama Signed-off-by: Emil Tantilov Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 105 +++++++++++++------------ 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 102ca937ddb4..c45de53300aa 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -677,58 +677,6 @@ update_vlvfb: } } -static inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf) -{ - struct ixgbe_hw *hw = &adapter->hw; - struct vf_data_storage *vfinfo = &adapter->vfinfo[vf]; - u8 num_tcs = netdev_get_num_tc(adapter->netdev); - - /* remove VLAN filters beloning to this VF */ - ixgbe_clear_vf_vlans(adapter, vf); - - /* add back PF assigned VLAN or VLAN 0 */ - ixgbe_set_vf_vlan(adapter, true, vfinfo->pf_vlan, vf); - - /* reset offloads to defaults */ - ixgbe_set_vmolr(hw, vf, !vfinfo->pf_vlan); - - /* set outgoing tags for VFs */ - if (!vfinfo->pf_vlan && !vfinfo->pf_qos && !num_tcs) { - ixgbe_clear_vmvir(adapter, vf); - } else { - if (vfinfo->pf_qos || !num_tcs) - ixgbe_set_vmvir(adapter, vfinfo->pf_vlan, - vfinfo->pf_qos, vf); - else - ixgbe_set_vmvir(adapter, vfinfo->pf_vlan, - adapter->default_up, vf); - - if (vfinfo->spoofchk_enabled) - hw->mac.ops.set_vlan_anti_spoofing(hw, true, vf); - } - - /* reset multicast table array for vf */ - adapter->vfinfo[vf].num_vf_mc_hashes = 0; - - /* Flush and reset the mta with the new values */ - ixgbe_set_rx_mode(adapter->netdev); - - ixgbe_del_mac_filter(adapter, adapter->vfinfo[vf].vf_mac_addresses, vf); - - /* reset VF api back to unknown */ - adapter->vfinfo[vf].vf_api = ixgbe_mbox_api_10; -} - -static int ixgbe_set_vf_mac(struct ixgbe_adapter *adapter, - int vf, unsigned char *mac_addr) -{ - ixgbe_del_mac_filter(adapter, adapter->vfinfo[vf].vf_mac_addresses, vf); - memcpy(adapter->vfinfo[vf].vf_mac_addresses, mac_addr, ETH_ALEN); - ixgbe_add_mac_filter(adapter, adapter->vfinfo[vf].vf_mac_addresses, vf); - - return 0; -} - static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter, int vf, int index, unsigned char *mac_addr) { @@ -784,6 +732,59 @@ static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter, return 0; } +static inline void ixgbe_vf_reset_event(struct ixgbe_adapter *adapter, u32 vf) +{ + struct ixgbe_hw *hw = &adapter->hw; + struct vf_data_storage *vfinfo = &adapter->vfinfo[vf]; + u8 num_tcs = netdev_get_num_tc(adapter->netdev); + + /* remove VLAN filters beloning to this VF */ + ixgbe_clear_vf_vlans(adapter, vf); + + /* add back PF assigned VLAN or VLAN 0 */ + ixgbe_set_vf_vlan(adapter, true, vfinfo->pf_vlan, vf); + + /* reset offloads to defaults */ + ixgbe_set_vmolr(hw, vf, !vfinfo->pf_vlan); + + /* set outgoing tags for VFs */ + if (!vfinfo->pf_vlan && !vfinfo->pf_qos && !num_tcs) { + ixgbe_clear_vmvir(adapter, vf); + } else { + if (vfinfo->pf_qos || !num_tcs) + ixgbe_set_vmvir(adapter, vfinfo->pf_vlan, + vfinfo->pf_qos, vf); + else + ixgbe_set_vmvir(adapter, vfinfo->pf_vlan, + adapter->default_up, vf); + + if (vfinfo->spoofchk_enabled) + hw->mac.ops.set_vlan_anti_spoofing(hw, true, vf); + } + + /* reset multicast table array for vf */ + adapter->vfinfo[vf].num_vf_mc_hashes = 0; + + /* Flush and reset the mta with the new values */ + ixgbe_set_rx_mode(adapter->netdev); + + ixgbe_del_mac_filter(adapter, adapter->vfinfo[vf].vf_mac_addresses, vf); + ixgbe_set_vf_macvlan(adapter, vf, 0, NULL); + + /* reset VF api back to unknown */ + adapter->vfinfo[vf].vf_api = ixgbe_mbox_api_10; +} + +static int ixgbe_set_vf_mac(struct ixgbe_adapter *adapter, + int vf, unsigned char *mac_addr) +{ + ixgbe_del_mac_filter(adapter, adapter->vfinfo[vf].vf_mac_addresses, vf); + memcpy(adapter->vfinfo[vf].vf_mac_addresses, mac_addr, ETH_ALEN); + ixgbe_add_mac_filter(adapter, adapter->vfinfo[vf].vf_mac_addresses, vf); + + return 0; +} + int ixgbe_vf_configuration(struct pci_dev *pdev, unsigned int event_mask) { struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); -- cgit From f87fc44770f54ff1b54d44ae9cec11f10efeca02 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Thu, 30 Mar 2017 20:49:02 -0700 Subject: ixgbevf: fix size of queue stats length IXGBEVF_QUEUE_STATS_LEN is based on ixgebvf_stats, not ixgbe_stats. This change fixes a bug where ethtool -S displayed some empty fields. Signed-off-by: Emil Tantilov Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c index 6bf740945260..43b70cd55bc6 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -80,7 +80,7 @@ static struct ixgbe_stats ixgbevf_gstrings_stats[] = { #define IXGBEVF_QUEUE_STATS_LEN ( \ (((struct ixgbevf_adapter *)netdev_priv(netdev))->num_tx_queues + \ ((struct ixgbevf_adapter *)netdev_priv(netdev))->num_rx_queues) * \ - (sizeof(struct ixgbe_stats) / sizeof(u64))) + (sizeof(struct ixgbevf_stats) / sizeof(u64))) #define IXGBEVF_GLOBAL_STATS_LEN ARRAY_SIZE(ixgbevf_gstrings_stats) #define IXGBEVF_STATS_LEN (IXGBEVF_GLOBAL_STATS_LEN + IXGBEVF_QUEUE_STATS_LEN) -- cgit From 27bdc44cdb2a8d96322d5978895eaae881fb8c2d Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Wed, 12 Apr 2017 13:35:22 -0700 Subject: ixgbe: Allow setting zero MAC address for VF Currently, there is no logic that allows a VF's MAC address to be removed from the RAR table. Allow the user to specify a zero MAC address in order to clear the VF's MAC address from the RAR table. This functionality is also utilized by libvirt when removing VFs. Signed-off-by: Tony Nguyen Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 28 +++++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index c45de53300aa..58897d97412e 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -1347,18 +1347,26 @@ void ixgbe_ping_all_vfs(struct ixgbe_adapter *adapter) int ixgbe_ndo_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) { struct ixgbe_adapter *adapter = netdev_priv(netdev); - if (!is_valid_ether_addr(mac) || (vf >= adapter->num_vfs)) + + if (vf >= adapter->num_vfs) + return -EINVAL; + + if (is_zero_ether_addr(mac)) { + adapter->vfinfo[vf].pf_set_mac = false; + dev_info(&adapter->pdev->dev, "removing MAC on VF %d\n", vf); + } else if (is_valid_ether_addr(mac)) { + adapter->vfinfo[vf].pf_set_mac = true; + dev_info(&adapter->pdev->dev, "setting MAC %pM on VF %d\n", + mac, vf); + dev_info(&adapter->pdev->dev, "Reload the VF driver to make this change effective."); + if (test_bit(__IXGBE_DOWN, &adapter->state)) { + dev_warn(&adapter->pdev->dev, "The VF MAC address has been set, but the PF device is not up.\n"); + dev_warn(&adapter->pdev->dev, "Bring the PF device up before attempting to use the VF device.\n"); + } + } else { return -EINVAL; - adapter->vfinfo[vf].pf_set_mac = true; - dev_info(&adapter->pdev->dev, "setting MAC %pM on VF %d\n", mac, vf); - dev_info(&adapter->pdev->dev, "Reload the VF driver to make this" - " change effective."); - if (test_bit(__IXGBE_DOWN, &adapter->state)) { - dev_warn(&adapter->pdev->dev, "The VF MAC address has been set," - " but the PF device is not up.\n"); - dev_warn(&adapter->pdev->dev, "Bring the PF device up before" - " attempting to use the VF device.\n"); } + return ixgbe_set_vf_mac(adapter, vf, mac); } -- cgit From 8dc963e1cd245e67d6a9ffb8447fc88fb6eaa370 Mon Sep 17 00:00:00 2001 From: Paul Greenwalt Date: Thu, 13 Apr 2017 08:07:07 -0400 Subject: ixgbe: Add 1000Base-T device based on X550EM_X MAC Add support for new 1000Base-T device based on X550EM_X MAC type. All PHY operations are disabled as the PHY is controlled by FW. Signed-off-by: Paul Greenwalt Tested-by: Krishneil Singh Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 2 ++ drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 ++ drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c | 45 ++++++++++++++++++++++++++- 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index e8cd4491f1fd..85b1afb345e3 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -826,6 +826,7 @@ enum ixgbe_boards { board_X540, board_X550, board_X550EM_x, + board_x550em_x_fw, board_x550em_a, board_x550em_a_fw, }; @@ -835,6 +836,7 @@ extern const struct ixgbe_info ixgbe_82599_info; extern const struct ixgbe_info ixgbe_X540_info; extern const struct ixgbe_info ixgbe_X550_info; extern const struct ixgbe_info ixgbe_X550EM_x_info; +extern const struct ixgbe_info ixgbe_x550em_x_fw_info; extern const struct ixgbe_info ixgbe_x550em_a_info; extern const struct ixgbe_info ixgbe_x550em_a_fw_info; #ifdef CONFIG_IXGBE_DCB diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 3d7b09100945..f765a2a0ed4b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -88,6 +88,7 @@ static const struct ixgbe_info *ixgbe_info_tbl[] = { [board_X540] = &ixgbe_X540_info, [board_X550] = &ixgbe_X550_info, [board_X550EM_x] = &ixgbe_X550EM_x_info, + [board_x550em_x_fw] = &ixgbe_x550em_x_fw_info, [board_x550em_a] = &ixgbe_x550em_a_info, [board_x550em_a_fw] = &ixgbe_x550em_a_fw_info, }; @@ -138,6 +139,7 @@ static const struct pci_device_id ixgbe_pci_tbl[] = { {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_KR), board_X550EM_x}, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_10G_T), board_X550EM_x}, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_SFP), board_X550EM_x}, + {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_X_1G_T), board_x550em_x_fw}, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_KR), board_x550em_a }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_KR_L), board_x550em_a }, {PCI_VDEVICE(INTEL, IXGBE_DEV_ID_X550EM_A_SFP_N), board_x550em_a }, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 2f06e4d9208d..9c2460c5ef1b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -3128,6 +3128,7 @@ enum ixgbe_phy_type { ixgbe_phy_x550em_kx4, ixgbe_phy_x550em_xfi, ixgbe_phy_x550em_ext_t, + ixgbe_phy_ext_1g_t, ixgbe_phy_cu_unknown, ixgbe_phy_qt, ixgbe_phy_xaui, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 58d3bcaca2b9..2ba024b575ea 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -49,6 +49,18 @@ static s32 ixgbe_get_invariants_X550_x(struct ixgbe_hw *hw) return 0; } +static s32 ixgbe_get_invariants_X550_x_fw(struct ixgbe_hw *hw) +{ + struct ixgbe_phy_info *phy = &hw->phy; + + /* Start with X540 invariants, since so similar */ + ixgbe_get_invariants_X540(hw); + + phy->ops.set_phy_power = NULL; + + return 0; +} + static s32 ixgbe_get_invariants_X550_a(struct ixgbe_hw *hw) { struct ixgbe_mac_info *mac = &hw->mac; @@ -334,9 +346,11 @@ static s32 ixgbe_identify_phy_x550em(struct ixgbe_hw *hw) else hw->phy.phy_semaphore_mask = IXGBE_GSSR_PHY0_SM; /* Fallthrough */ - case IXGBE_DEV_ID_X550EM_X_1G_T: case IXGBE_DEV_ID_X550EM_X_10G_T: return ixgbe_identify_phy_generic(hw); + case IXGBE_DEV_ID_X550EM_X_1G_T: + hw->phy.type = ixgbe_phy_ext_1g_t; + break; case IXGBE_DEV_ID_X550EM_A_1G_T: case IXGBE_DEV_ID_X550EM_A_1G_T_L: hw->phy.type = ixgbe_phy_fw; @@ -2158,6 +2172,8 @@ static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw) ixgbe_set_soft_rate_select_speed; break; case ixgbe_media_type_copper: + if (hw->device_id == IXGBE_DEV_ID_X550EM_X_1G_T) + break; mac->ops.setup_link = ixgbe_setup_mac_link_t_X550em; mac->ops.setup_fc = ixgbe_setup_fc_generic; mac->ops.check_link = ixgbe_check_link_t_X550em; @@ -2238,6 +2254,7 @@ static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw, *speed = IXGBE_LINK_SPEED_1GB_FULL | IXGBE_LINK_SPEED_10GB_FULL; break; + case ixgbe_phy_ext_1g_t: case ixgbe_phy_sgmii: *speed = IXGBE_LINK_SPEED_1GB_FULL; break; @@ -3185,6 +3202,11 @@ static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) phy->ops.setup_link = ixgbe_setup_fw_link; phy->ops.reset = ixgbe_reset_phy_fw; break; + case ixgbe_phy_ext_1g_t: + phy->ops.setup_link = NULL; + phy->ops.read_reg = NULL; + phy->ops.write_reg = NULL; + break; default: break; } @@ -3888,6 +3910,17 @@ static const struct ixgbe_phy_operations phy_ops_X550EM_x = { .write_reg = &ixgbe_write_phy_reg_generic, }; +static const struct ixgbe_phy_operations phy_ops_x550em_x_fw = { + X550_COMMON_PHY + .check_overtemp = NULL, + .init = ixgbe_init_phy_ops_X550em, + .identify = ixgbe_identify_phy_x550em, + .read_reg = NULL, + .write_reg = NULL, + .read_reg_mdi = NULL, + .write_reg_mdi = NULL, +}; + static const struct ixgbe_phy_operations phy_ops_x550em_a = { X550_COMMON_PHY .check_overtemp = &ixgbe_tn_check_overtemp, @@ -3950,6 +3983,16 @@ const struct ixgbe_info ixgbe_X550EM_x_info = { .link_ops = &link_ops_x550em_x, }; +const struct ixgbe_info ixgbe_x550em_x_fw_info = { + .mac = ixgbe_mac_X550EM_x, + .get_invariants = ixgbe_get_invariants_X550_x_fw, + .mac_ops = &mac_ops_X550EM_x, + .eeprom_ops = &eeprom_ops_X550EM_x, + .phy_ops = &phy_ops_x550em_x_fw, + .mbx_ops = &mbx_ops_generic, + .mvals = ixgbe_mvals_X550EM_x, +}; + const struct ixgbe_info ixgbe_x550em_a_info = { .mac = ixgbe_mac_x550em_a, .get_invariants = &ixgbe_get_invariants_X550_a, -- cgit From 3dfbfc7ebb959d68b35d5ca3b7499cc73dc57261 Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Thu, 13 Apr 2017 07:26:05 -0700 Subject: ixgbe: Check for RSS key before setting value The RSS key is being repopulated every time the interface is brought up regardless of whether there is an existing value. If the user sets the RSS key and the interface is brought up (e.g. reset), the user specified RSS key will be overwritten. This patch changes the rss_key to a pointer so we can check to see if the key has been populated and preserve it accordingly. Signed-off-by: Tony Nguyen Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 4 +--- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 30 ++++++++++++++++++++++-- drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 2 +- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 85b1afb345e3..76263762bea1 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -769,7 +769,7 @@ struct ixgbe_adapter { u8 rss_indir_tbl[IXGBE_MAX_RETA_ENTRIES]; #define IXGBE_RSS_KEY_SIZE 40 /* size of RSS Hash Key in bytes */ - u32 rss_key[IXGBE_RSS_KEY_SIZE / sizeof(u32)]; + u32 *rss_key; }; static inline u8 ixgbe_max_rss_indices(struct ixgbe_adapter *adapter) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index b0fd2f58a69c..7e5e336d7dcc 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -2967,9 +2967,7 @@ static int ixgbe_rss_indir_tbl_max(struct ixgbe_adapter *adapter) static u32 ixgbe_get_rxfh_key_size(struct net_device *netdev) { - struct ixgbe_adapter *adapter = netdev_priv(netdev); - - return sizeof(adapter->rss_key); + return IXGBE_RSS_KEY_SIZE; } static u32 ixgbe_rss_indir_size(struct net_device *netdev) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index f765a2a0ed4b..22a29df1d29e 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -3638,6 +3638,28 @@ void ixgbe_store_key(struct ixgbe_adapter *adapter) IXGBE_WRITE_REG(hw, IXGBE_RSSRK(i), adapter->rss_key[i]); } +/** + * ixgbe_init_rss_key - Initialize adapter RSS key + * @adapter: device handle + * + * Allocates and initializes the RSS key if it is not allocated. + **/ +static inline int ixgbe_init_rss_key(struct ixgbe_adapter *adapter) +{ + u32 *rss_key; + + if (!adapter->rss_key) { + rss_key = kzalloc(IXGBE_RSS_KEY_SIZE, GFP_KERNEL); + if (unlikely(!rss_key)) + return -ENOMEM; + + netdev_rss_key_fill(rss_key, IXGBE_RSS_KEY_SIZE); + adapter->rss_key = rss_key; + } + + return 0; +} + /** * ixgbe_store_reta - Write the RETA table to HW * @adapter: device handle @@ -3740,7 +3762,7 @@ static void ixgbe_setup_vfreta(struct ixgbe_adapter *adapter) /* Fill out hash function seeds */ for (i = 0; i < 10; i++) IXGBE_WRITE_REG(hw, IXGBE_PFVFRSSRK(i, pf_pool), - adapter->rss_key[i]); + *(adapter->rss_key + i)); /* Fill out the redirection table */ for (i = 0, j = 0; i < 64; i++, j++) { @@ -3801,7 +3823,6 @@ static void ixgbe_setup_mrqc(struct ixgbe_adapter *adapter) if (adapter->flags2 & IXGBE_FLAG2_RSS_FIELD_IPV6_UDP) rss_field |= IXGBE_MRQC_RSS_FIELD_IPV6_UDP; - netdev_rss_key_fill(adapter->rss_key, sizeof(adapter->rss_key)); if ((hw->mac.type >= ixgbe_mac_X550) && (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)) { unsigned int pf_pool = adapter->num_vfs; @@ -6015,6 +6036,9 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter, if (!adapter->mac_table) return -ENOMEM; + if (ixgbe_init_rss_key(adapter)) + return -ENOMEM; + /* Set MAC specific capability flags and exceptions */ switch (hw->mac.type) { case ixgbe_mac_82598EB: @@ -10391,6 +10415,7 @@ err_sw_init: iounmap(adapter->io_addr); kfree(adapter->jump_tables[0]); kfree(adapter->mac_table); + kfree(adapter->rss_key); err_ioremap: disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state); free_netdev(netdev); @@ -10475,6 +10500,7 @@ static void ixgbe_remove(struct pci_dev *pdev) } kfree(adapter->mac_table); + kfree(adapter->rss_key); disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state); free_netdev(netdev); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 58897d97412e..8baf298a8516 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -1113,7 +1113,7 @@ static int ixgbe_get_vf_rss_key(struct ixgbe_adapter *adapter, return -EOPNOTSUPP; } - memcpy(rss_key, adapter->rss_key, sizeof(adapter->rss_key)); + memcpy(rss_key, adapter->rss_key, IXGBE_RSS_KEY_SIZE); return 0; } -- cgit From 82fb670c5fdd5662c406871a6c21ebd55ba68e45 Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Thu, 13 Apr 2017 07:26:06 -0700 Subject: ixgbevf: Fix errors in retrieving RETA and RSS from PF Mailbox support for getting RETA and RSS is available for only 82599 and x540; a previous patch reversed the logic and these adapters were returning not supported. Also, the NACK check in ixgbevf_get_rss_key_locked() was checking for the command IXGBE_VF_GET_RETA instead of IXGBE_VF_GET_RSS_KEY. This patch corrects both issues by correcting the logic and checking for the right command. Signed-off-by: Tony Nguyen Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/vf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c index 8a5db9d7219d..b6d0c01eab10 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -333,7 +333,7 @@ int ixgbevf_get_reta_locked(struct ixgbe_hw *hw, u32 *reta, int num_rx_queues) switch (hw->api_version) { case ixgbe_mbox_api_13: case ixgbe_mbox_api_12: - if (hw->mac.type >= ixgbe_mac_X550_vf) + if (hw->mac.type < ixgbe_mac_X550_vf) break; default: return -EOPNOTSUPP; @@ -399,7 +399,7 @@ int ixgbevf_get_rss_key_locked(struct ixgbe_hw *hw, u8 *rss_key) switch (hw->api_version) { case ixgbe_mbox_api_13: case ixgbe_mbox_api_12: - if (hw->mac.type >= ixgbe_mac_X550_vf) + if (hw->mac.type < ixgbe_mac_X550_vf) break; default: return -EOPNOTSUPP; @@ -419,7 +419,7 @@ int ixgbevf_get_rss_key_locked(struct ixgbe_hw *hw, u8 *rss_key) msgbuf[0] &= ~IXGBE_VT_MSGTYPE_CTS; /* If the operation has been refused by a PF return -EPERM */ - if (msgbuf[0] == (IXGBE_VF_GET_RETA | IXGBE_VT_MSGTYPE_NACK)) + if (msgbuf[0] == (IXGBE_VF_GET_RSS_KEY | IXGBE_VT_MSGTYPE_NACK)) return -EPERM; /* If we didn't get an ACK there must have been -- cgit From e60ae00361bf4e5ef08cde5a30f131cf287ffe30 Mon Sep 17 00:00:00 2001 From: Tony Nguyen Date: Thu, 13 Apr 2017 07:26:07 -0700 Subject: ixgbevf: Check for RSS key before setting value The RSS key is being repopulated every time the interface is brought up regardless of whether there is an existing value. If the user sets the RSS key and the interface is brought up (e.g. reset), the user specified RSS key will be overwritten. This patch changes the rss_key to a pointer so we can check to see if the key has been populated and preserve it accordingly. Signed-off-by: Tony Nguyen Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbevf/ethtool.c | 3 ++- drivers/net/ethernet/intel/ixgbevf/ixgbevf.h | 2 +- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 33 +++++++++++++++++++++-- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c index 43b70cd55bc6..ff9d05f308ee 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -855,7 +855,8 @@ static int ixgbevf_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, if (adapter->hw.mac.type >= ixgbe_mac_X550_vf) { if (key) - memcpy(key, adapter->rss_key, sizeof(adapter->rss_key)); + memcpy(key, adapter->rss_key, + ixgbevf_get_rxfh_key_size(netdev)); if (indir) { int i; diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h index a8cbc2dda0dd..581f44bbd7b3 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h @@ -319,7 +319,7 @@ struct ixgbevf_adapter { spinlock_t mbx_lock; unsigned long last_reset; - u32 rss_key[IXGBEVF_VFRSSRK_REGS]; + u32 *rss_key; u8 rss_indir_tbl[IXGBEVF_X550_VFRETA_SIZE]; }; diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 80bab261a0ec..eee29bddddc1 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1660,6 +1660,28 @@ static void ixgbevf_rx_desc_queue_enable(struct ixgbevf_adapter *adapter, reg_idx); } +/** + * ixgbevf_init_rss_key - Initialize adapter RSS key + * @adapter: device handle + * + * Allocates and initializes the RSS key if it is not allocated. + **/ +static inline int ixgbevf_init_rss_key(struct ixgbevf_adapter *adapter) +{ + u32 *rss_key; + + if (!adapter->rss_key) { + rss_key = kzalloc(IXGBEVF_RSS_HASH_KEY_SIZE, GFP_KERNEL); + if (unlikely(!rss_key)) + return -ENOMEM; + + netdev_rss_key_fill(rss_key, IXGBEVF_RSS_HASH_KEY_SIZE); + adapter->rss_key = rss_key; + } + + return 0; +} + static void ixgbevf_setup_vfmrqc(struct ixgbevf_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; @@ -1668,9 +1690,8 @@ static void ixgbevf_setup_vfmrqc(struct ixgbevf_adapter *adapter) u8 i, j; /* Fill out hash function seeds */ - netdev_rss_key_fill(adapter->rss_key, sizeof(adapter->rss_key)); for (i = 0; i < IXGBEVF_VFRSSRK_REGS; i++) - IXGBE_WRITE_REG(hw, IXGBE_VFRSSRK(i), adapter->rss_key[i]); + IXGBE_WRITE_REG(hw, IXGBE_VFRSSRK(i), *(adapter->rss_key + i)); for (i = 0, j = 0; i < IXGBEVF_X550_VFRETA_SIZE; i++, j++) { if (j == rss_i) @@ -2611,6 +2632,12 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter) hw->mbx.ops.init_params(hw); + if (hw->mac.type >= ixgbe_mac_X550_vf) { + err = ixgbevf_init_rss_key(adapter); + if (err) + goto out; + } + /* assume legacy case in which PF would only give VF 2 queues */ hw->mac.max_tx_queues = 2; hw->mac.max_rx_queues = 2; @@ -4127,6 +4154,7 @@ err_register: err_sw_init: ixgbevf_reset_interrupt_capability(adapter); iounmap(adapter->io_addr); + kfree(adapter->rss_key); err_ioremap: disable_dev = !test_and_set_bit(__IXGBEVF_DISABLED, &adapter->state); free_netdev(netdev); @@ -4173,6 +4201,7 @@ static void ixgbevf_remove(struct pci_dev *pdev) hw_dbg(&adapter->hw, "Remove complete\n"); + kfree(adapter->rss_key); disable_dev = !test_and_set_bit(__IXGBEVF_DISABLED, &adapter->state); free_netdev(netdev); -- cgit From 763d9a302ab18da0a0078c9788ed6566d0c974e3 Mon Sep 17 00:00:00 2001 From: Salvatore Benedetto Date: Tue, 25 Apr 2017 16:59:47 +0100 Subject: Bluetooth: allocate data for kpp on heap Bluetooth would crash when computing ECDH keys with kpp if VMAP_STACK is enabled. Fix by allocating data passed to kpp on heap. Fixes: 58771c1c ("Bluetooth: convert smp and selftest to crypto kpp API") Signed-off-by: Salvatore Benedetto Signed-off-by: Marcel Holtmann --- net/bluetooth/ecdh_helper.c | 22 ++++++++++++++++------ net/bluetooth/selftest.c | 22 +++++++++++++++++----- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/net/bluetooth/ecdh_helper.c b/net/bluetooth/ecdh_helper.c index b6d9aa155485..579684bfc322 100644 --- a/net/bluetooth/ecdh_helper.c +++ b/net/bluetooth/ecdh_helper.c @@ -59,16 +59,19 @@ bool compute_ecdh_secret(const u8 public_key[64], const u8 private_key[32], struct ecdh p; struct ecdh_completion result; struct scatterlist src, dst; - u8 tmp[64]; - u8 *buf; + u8 *tmp, *buf; unsigned int buf_len; int err = -ENOMEM; + tmp = kmalloc(64, GFP_KERNEL); + if (!tmp) + return false; + tfm = crypto_alloc_kpp("ecdh", CRYPTO_ALG_INTERNAL, 0); if (IS_ERR(tfm)) { pr_err("alg: kpp: Failed to load tfm for kpp: %ld\n", PTR_ERR(tfm)); - return false; + goto free_tmp; } req = kpp_request_alloc(tfm, GFP_KERNEL); @@ -128,6 +131,8 @@ free_req: kpp_request_free(req); free_kpp: crypto_free_kpp(tfm); +free_tmp: + kfree(tmp); return (err == 0); } @@ -138,18 +143,21 @@ bool generate_ecdh_keys(u8 public_key[64], u8 private_key[32]) struct ecdh p; struct ecdh_completion result; struct scatterlist dst; - u8 tmp[64]; - u8 *buf; + u8 *tmp, *buf; unsigned int buf_len; int err = -ENOMEM; const unsigned short max_tries = 16; unsigned short tries = 0; + tmp = kmalloc(64, GFP_KERNEL); + if (!tmp) + return false; + tfm = crypto_alloc_kpp("ecdh", CRYPTO_ALG_INTERNAL, 0); if (IS_ERR(tfm)) { pr_err("alg: kpp: Failed to load tfm for kpp: %ld\n", PTR_ERR(tfm)); - return false; + goto free_tmp; } req = kpp_request_alloc(tfm, GFP_KERNEL); @@ -219,5 +227,7 @@ free_req: kpp_request_free(req); free_kpp: crypto_free_kpp(tfm); +free_tmp: + kfree(tmp); return (err == 0); } diff --git a/net/bluetooth/selftest.c b/net/bluetooth/selftest.c index efef2815646e..ee92c925ecc5 100644 --- a/net/bluetooth/selftest.c +++ b/net/bluetooth/selftest.c @@ -142,18 +142,30 @@ static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32], const u8 pub_a[64], const u8 pub_b[64], const u8 dhkey[32]) { - u8 dhkey_a[32], dhkey_b[32]; + u8 *tmp, *dhkey_a, *dhkey_b; + int ret = 0; + + tmp = kmalloc(64, GFP_KERNEL); + if (!tmp) + return -EINVAL; + + dhkey_a = &tmp[0]; + dhkey_b = &tmp[32]; compute_ecdh_secret(pub_b, priv_a, dhkey_a); compute_ecdh_secret(pub_a, priv_b, dhkey_b); - if (memcmp(dhkey_a, dhkey, 32)) - return -EINVAL; + if (memcmp(dhkey_a, dhkey, 32)) { + ret = -EINVAL; + goto out; + } if (memcmp(dhkey_b, dhkey, 32)) - return -EINVAL; + ret = -EINVAL; - return 0; +out: + kfree(dhkey_a); + return ret; } static char test_ecdh_buffer[32]; -- cgit From ab89f0bdd63a3721f7cd3f064f39fc4ac7ca14d4 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Mon, 24 Apr 2017 18:25:04 -0700 Subject: Bluetooth: Fix user channel for 32bit userspace on 64bit kernel Running 32bit userspace on 64bit kernel results in MSG_CMSG_COMPAT being defined as 0x80000000. This results in sendmsg failure if used from 32bit userspace running on 64bit kernel. Fix this by accounting for MSG_CMSG_COMPAT in flags check in hci_sock_sendmsg. Signed-off-by: Szymon Janc Signed-off-by: Marko Kiiskila Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- net/bluetooth/hci_sock.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index f64d6566021f..638bf0e1a2e3 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -1680,7 +1680,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE)) + if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE| + MSG_CMSG_COMPAT)) return -EINVAL; if (len < 4 || len > HCI_MAX_FRAME_SIZE) -- cgit From 377a6eac58e3492fbf74fed0a5e40ee90ed73f8e Mon Sep 17 00:00:00 2001 From: Frédéric Danis Date: Fri, 28 Apr 2017 13:26:11 +0200 Subject: Bluetooth: Add module license for HCI UART Nokia H4+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following error preventing to load Nokia H4+ module: kernel: [ 826.461619] hci_nokia: module license 'unspecified' taints kernel. kernel: [ 826.461629] Disabling lock debugging due to kernel taint kernel: [ 826.461836] hci_nokia: Unknown symbol gpiod_get_value_cansleep (err 0) kernel: [ 826.461876] hci_nokia: Unknown symbol devm_kmalloc (err 0) kernel: [ 826.461908] hci_nokia: Unknown symbol gpiod_set_value (err 0) kernel: [ 826.461937] hci_nokia: Unknown symbol serdev_device_set_baudrate (err 0) kernel: [ 826.461994] hci_nokia: Unknown symbol gpiod_set_value_cansleep (err 0) kernel: [ 826.462021] hci_nokia: Unknown symbol hci_uart_tx_wakeup (err 0) kernel: [ 826.462043] hci_nokia: Unknown symbol serdev_device_set_flow_control (err 0) kernel: [ 826.462064] hci_nokia: Unknown symbol gpiod_to_irq (err 0) kernel: [ 826.462085] hci_nokia: Unknown symbol serdev_device_open (err 0) kernel: [ 826.462106] hci_nokia: Unknown symbol gpiod_get_value (err 0) kernel: [ 826.462150] hci_nokia: Unknown symbol clk_prepare (err 0) kernel: [ 826.462182] hci_nokia: Unknown symbol pm_runtime_enable (err 0) kernel: [ 826.462204] hci_nokia: Unknown symbol h4_recv_buf (err 0) kernel: [ 826.462246] hci_nokia: Unknown symbol serdev_device_write_flush (err 0) kernel: [ 826.462268] hci_nokia: Unknown symbol serdev_device_get_tiocm (err 0) kernel: [ 826.462298] hci_nokia: Unknown symbol driver_unregister (err 0) kernel: [ 826.462318] hci_nokia: Unknown symbol serdev_device_wait_until_sent (err 0) kernel: [ 826.462347] hci_nokia: Unknown symbol __serdev_device_driver_register (err 0) kernel: [ 826.462384] hci_nokia: Unknown symbol serdev_device_set_tiocm (err 0) kernel: [ 826.462417] hci_nokia: Unknown symbol clk_get_rate (err 0) kernel: [ 826.462454] hci_nokia: Unknown symbol __pm_runtime_resume (err 0) kernel: [ 826.462486] hci_nokia: Unknown symbol serdev_device_close (err 0) kernel: [ 826.462524] hci_nokia: Unknown symbol cancel_work_sync (err 0) kernel: [ 826.462546] hci_nokia: Unknown symbol btbcm_set_bdaddr (err 0) kernel: [ 826.462567] hci_nokia: Unknown symbol clk_disable (err 0) kernel: [ 826.462610] hci_nokia: Unknown symbol __pm_runtime_disable (err 0) kernel: [ 826.462632] hci_nokia: Unknown symbol hci_uart_register_device (err 0) kernel: [ 826.462653] hci_nokia: Unknown symbol clk_enable (err 0) kernel: [ 826.462675] hci_nokia: Unknown symbol __pm_runtime_idle (err 0) kernel: [ 826.462700] hci_nokia: Unknown symbol clk_unprepare (err 0) Signed-off-by: Frédéric Danis Acked-by: Sebastian Reichel Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_nokia.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/bluetooth/hci_nokia.c b/drivers/bluetooth/hci_nokia.c index 4038daf78d24..a7d687d8d456 100644 --- a/drivers/bluetooth/hci_nokia.c +++ b/drivers/bluetooth/hci_nokia.c @@ -36,6 +36,8 @@ #include "hci_uart.h" #include "btbcm.h" +#define VERSION "0.1" + #define NOKIA_ID_BCM2048 0x04 #define NOKIA_ID_TI1271 0x31 @@ -818,3 +820,8 @@ static struct serdev_device_driver nokia_bluetooth_serdev_driver = { }; module_serdev_device_driver(nokia_bluetooth_serdev_driver); + +MODULE_AUTHOR("Sebastian Reichel "); +MODULE_DESCRIPTION("Bluetooth HCI UART Nokia H4+ driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); -- cgit From ab00f89fdff61975fa52a61608080ea7b8d8e800 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Fri, 28 Apr 2017 13:57:24 +0100 Subject: Bluetooth: hci_ldisc: Add protocol check to hci_uart_send_frame() Before attempting to send a HCI message, check that the Data Link protocol is still bound to the HCI UART driver. This makes the code consistent with the usage of the other proto function pointers. Therefore, add a check for HCI_UART_PROTO_READY into hci_uart_send_frame() and return -EUNATCH if the Data Link protocol is not bound. This also allows hci_send_frame() to report the error of an unbound Data Link protocol layer. Therefore, it assists with diagnostics into why HCI messages are being sent when the Data Link protocol is not bound and avoids potential crashes. Signed-off-by: Dean Jenkins Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ldisc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index c53513cb7654..c515aa9b923c 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -256,6 +256,9 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb), skb->len); + if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) + return -EUNATCH; + hu->proto->enqueue(hu, skb); hci_uart_tx_wakeup(hu); -- cgit From 048e1bd3a27fbeb84ccdff52e165370c1339a193 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Fri, 28 Apr 2017 13:57:25 +0100 Subject: Bluetooth: hci_ldisc: Add protocol check to hci_uart_dequeue() Before attempting to dequeue a Data Link protocol encapsulated message, check that the Data Link protocol is still bound to the HCI UART driver. This makes the code consistent with the usage of the other proto function pointers. Therefore, add a check for HCI_UART_PROTO_READY into hci_uart_dequeue() and return NULL if the Data Link protocol is not bound. This is needed for robustness as there is a scheduling race condition. hci_uart_write_work() is scheduled to run via work queue hu->write_work from hci_uart_tx_wakeup(). Therefore, there is a delay between scheduling hci_uart_write_work() to run and hci_uart_dequeue() running whereby the Data Link protocol layer could become unbound during the scheduling delay. In this case, without the check, the call to the unbound Data Link protocol layer dequeue function can crash. It is noted that hci_uart_tty_close() has a "cancel_work_sync(&hu->write_work)" statement but this only reduces the window of the race condition because it is possible for a new work-item to be added to work queue hu->write_work after the call to cancel_work_sync(). For example, Data Link layer retransmissions can be added to the work queue after the cancel_work_sync() has finished. Signed-off-by: Dean Jenkins Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ldisc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index c515aa9b923c..6f9e406b28a6 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -113,10 +113,12 @@ static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu) { struct sk_buff *skb = hu->tx_skb; - if (!skb) - skb = hu->proto->dequeue(hu); - else + if (!skb) { + if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) + skb = hu->proto->dequeue(hu); + } else { hu->tx_skb = NULL; + } return skb; } -- cgit From 2d6f1da168e1d62c47f7d50135ac4cbd8411dcb1 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Fri, 28 Apr 2017 13:57:26 +0100 Subject: Bluetooth: hci_ldisc: Add protocol check to hci_uart_tx_wakeup() Before attempting to schedule a work-item onto hu->write_work in hci_uart_tx_wakeup(), check that the Data Link protocol layer is still bound to the HCI UART driver. Failure to perform this protocol check causes a race condition between the work queue hu->write_work running hci_uart_write_work() and the Data Link protocol layer being unbound (closed) in hci_uart_tty_close(). Note hci_uart_tty_close() does have a "cancel_work_sync(&hu->write_work)" but it is ineffective because it cannot prevent work-items being added to hu->write_work after cancel_work_sync() has run. Therefore, add a check for HCI_UART_PROTO_READY into hci_uart_tx_wakeup() which prevents scheduling of the work queue when HCI_UART_PROTO_READY is in the clear state. However, note a small race condition remains because the hci_uart_tx_wakeup() thread can run in parallel with the hci_uart_tty_close() thread so it is possible that a schedule of hu->write_work can occur when HCI_UART_PROTO_READY is cleared. A complete solution needs locking of the threads which is implemented in a future commit. Signed-off-by: Dean Jenkins Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ldisc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 6f9e406b28a6..2edd30556956 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -125,6 +125,9 @@ static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu) int hci_uart_tx_wakeup(struct hci_uart *hu) { + if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) + return 0; + if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) { set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); return 0; -- cgit From 6322e63c35d68eac9c4a5ed59ea1c6d1e2746892 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 13 Apr 2017 04:45:54 -0400 Subject: i40e: properly spell I40E_VF_STATE_* flags These flags represent the state of the VF at various times. Do not spell them as _STAT_ which can be confusing to readers who may think these refer to statistics. Change-ID: I6bc092cd472e8276896a1fd7498aced2084312df Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 98 +++++++++++----------- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h | 14 ++-- 4 files changed, 58 insertions(+), 58 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 08035c4389cd..523dd81d76b7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -1826,7 +1826,7 @@ static inline bool i40e_active_vfs(struct i40e_pf *pf) int i; for (i = 0; i < pf->num_alloc_vfs; i++) - if (test_bit(I40E_VF_STAT_ACTIVE, &vfs[i].vf_states)) + if (test_bit(I40E_VF_STATE_ACTIVE, &vfs[i].vf_states)) return true; return false; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c001562f19b2..8f47a31cb2c8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7318,7 +7318,7 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf) "Too many MDD events on VF %d, disabled\n", i); dev_info(&pf->pdev->dev, "Use PF Control I/F to re-enable the VF\n"); - set_bit(I40E_VF_STAT_DISABLED, &vf->vf_states); + set_bit(I40E_VF_STATE_DISABLED, &vf->vf_states); } } diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 350cba70490c..a46c07799384 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -50,8 +50,8 @@ static void i40e_vc_vf_broadcast(struct i40e_pf *pf, for (i = 0; i < pf->num_alloc_vfs; i++, vf++) { int abs_vf_id = vf->vf_id + (int)hw->func_caps.vf_base_id; /* Not all vfs are enabled so skip the ones that are not */ - if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states) && - !test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states)) + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states) && + !test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) continue; /* Ignore return value on purpose - a given VF may fail, but @@ -137,8 +137,8 @@ void i40e_vc_notify_vf_reset(struct i40e_vf *vf) return; /* verify if the VF is in either init or active before proceeding */ - if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states) && - !test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states)) + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states) && + !test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) return; abs_vf_id = vf->vf_id + (int)vf->pf->hw.func_caps.vf_base_id; @@ -812,7 +812,7 @@ static void i40e_free_vf_res(struct i40e_vf *vf) /* Start by disabling VF's configuration API to prevent the OS from * accessing the VF's VSI after it's freed / invalidated. */ - clear_bit(I40E_VF_STAT_INIT, &vf->vf_states); + clear_bit(I40E_VF_STATE_INIT, &vf->vf_states); /* free vsi & disconnect it from the parent uplink */ if (vf->lan_vsi_idx) { @@ -884,7 +884,7 @@ static int i40e_alloc_vf_res(struct i40e_vf *vf) vf->num_queue_pairs = total_queue_pairs; /* VF is now completely initialized */ - set_bit(I40E_VF_STAT_INIT, &vf->vf_states); + set_bit(I40E_VF_STATE_INIT, &vf->vf_states); error_alloc: if (ret) @@ -938,7 +938,7 @@ static void i40e_trigger_vf_reset(struct i40e_vf *vf, bool flr) u32 reg, reg_idx, bit_idx; /* warn the VF */ - clear_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states); + clear_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states); /* Disable VF's configuration API during reset. The flag is re-enabled * in i40e_alloc_vf_res(), when it's safe again to access VF's VSI. @@ -946,7 +946,7 @@ static void i40e_trigger_vf_reset(struct i40e_vf *vf, bool flr) * to do it earlier to give some time to finish to any VF config * functions that may still be running at this point. */ - clear_bit(I40E_VF_STAT_INIT, &vf->vf_states); + clear_bit(I40E_VF_STATE_INIT, &vf->vf_states); /* In the case of a VFLR, the HW has already reset the VF and we * just need to clean up, so don't hit the VFRTRIG register. @@ -1004,8 +1004,8 @@ static void i40e_cleanup_reset_vf(struct i40e_vf *vf) if (!i40e_alloc_vf_res(vf)) { int abs_vf_id = vf->vf_id + hw->func_caps.vf_base_id; i40e_enable_vf_mappings(vf); - set_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states); - clear_bit(I40E_VF_STAT_DISABLED, &vf->vf_states); + set_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states); + clear_bit(I40E_VF_STATE_DISABLED, &vf->vf_states); /* Do not notify the client during VF init */ if (vf->pf->num_alloc_vfs) i40e_notify_client_of_vf_reset(pf, abs_vf_id); @@ -1194,7 +1194,7 @@ void i40e_free_vfs(struct i40e_pf *pf) i40e_notify_client_of_vf_enable(pf, 0); for (i = 0; i < pf->num_alloc_vfs; i++) - if (test_bit(I40E_VF_STAT_INIT, &pf->vf[i].vf_states)) + if (test_bit(I40E_VF_STATE_INIT, &pf->vf[i].vf_states)) i40e_vsi_stop_rings(pf->vsi[pf->vf[i].lan_vsi_idx]); /* Disable IOV before freeing resources. This lets any VF drivers @@ -1212,7 +1212,7 @@ void i40e_free_vfs(struct i40e_pf *pf) tmp = pf->num_alloc_vfs; pf->num_alloc_vfs = 0; for (i = 0; i < tmp; i++) { - if (test_bit(I40E_VF_STAT_INIT, &pf->vf[i].vf_states)) + if (test_bit(I40E_VF_STATE_INIT, &pf->vf[i].vf_states)) i40e_free_vf_res(&pf->vf[i]); /* disable qp mappings */ i40e_disable_vf_mappings(&pf->vf[i]); @@ -1418,7 +1418,7 @@ static int i40e_vc_send_msg_to_vf(struct i40e_vf *vf, u32 v_opcode, "Number of invalid messages exceeded for VF %d\n", vf->vf_id); dev_err(&pf->pdev->dev, "Use PF Control I/F to enable the VF\n"); - set_bit(I40E_VF_STAT_DISABLED, &vf->vf_states); + set_bit(I40E_VF_STATE_DISABLED, &vf->vf_states); } } else { vf->num_valid_msgs++; @@ -1493,7 +1493,7 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) int len = 0; int ret; - if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { aq_ret = I40E_ERR_PARAM; goto err; } @@ -1522,7 +1522,7 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) if (i40e_vf_client_capable(pf, vf->vf_id) && (vf->driver_caps & I40E_VIRTCHNL_VF_OFFLOAD_IWARP)) { vfres->vf_offload_flags |= I40E_VIRTCHNL_VF_OFFLOAD_IWARP; - set_bit(I40E_VF_STAT_IWARPENA, &vf->vf_states); + set_bit(I40E_VF_STATE_IWARPENA, &vf->vf_states); } if (vf->driver_caps & I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF) { @@ -1583,7 +1583,7 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) ether_addr_copy(vfres->vsi_res[0].default_mac_addr, vf->default_lan_addr.addr); } - set_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states); + set_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states); err: /* send the response back to the VF */ @@ -1606,7 +1606,7 @@ err: **/ static void i40e_vc_reset_vf_msg(struct i40e_vf *vf) { - if (test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states)) + if (test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) i40e_reset_vf(vf, false); } @@ -1654,7 +1654,7 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, int bkt; vsi = i40e_find_vsi_from_id(pf, info->vsi_id); - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states) || + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || !i40e_vc_isvalid_vsi_id(vf, info->vsi_id) || !vsi) { aq_ret = I40E_ERR_PARAM; @@ -1715,9 +1715,9 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, "VF %d successfully set multicast promiscuous mode\n", vf->vf_id); if (allmulti) - set_bit(I40E_VF_STAT_MC_PROMISC, &vf->vf_states); + set_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states); else - clear_bit(I40E_VF_STAT_MC_PROMISC, &vf->vf_states); + clear_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states); } if (info->flags & I40E_FLAG_VF_UNICAST_PROMISC) @@ -1766,9 +1766,9 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, "VF %d successfully set unicast promiscuous mode\n", vf->vf_id); if (alluni) - set_bit(I40E_VF_STAT_UC_PROMISC, &vf->vf_states); + set_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states); else - clear_bit(I40E_VF_STAT_UC_PROMISC, &vf->vf_states); + clear_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states); } error_param: @@ -1797,7 +1797,7 @@ static int i40e_vc_config_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) i40e_status aq_ret = 0; int i; - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { aq_ret = I40E_ERR_PARAM; goto error_param; } @@ -1854,7 +1854,7 @@ static int i40e_vc_config_irq_map_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) unsigned long tempmap; int i; - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { aq_ret = I40E_ERR_PARAM; goto error_param; } @@ -1914,7 +1914,7 @@ static int i40e_vc_enable_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) u16 vsi_id = vqs->vsi_id; i40e_status aq_ret = 0; - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { aq_ret = I40E_ERR_PARAM; goto error_param; } @@ -1953,7 +1953,7 @@ static int i40e_vc_disable_queues_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) struct i40e_pf *pf = vf->pf; i40e_status aq_ret = 0; - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { aq_ret = I40E_ERR_PARAM; goto error_param; } @@ -1995,7 +1995,7 @@ static int i40e_vc_get_stats_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) memset(&stats, 0, sizeof(struct i40e_eth_stats)); - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { aq_ret = I40E_ERR_PARAM; goto error_param; } @@ -2082,7 +2082,7 @@ static int i40e_vc_add_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) i40e_status ret = 0; int i; - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states) || + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || !i40e_vc_isvalid_vsi_id(vf, vsi_id)) { ret = I40E_ERR_PARAM; goto error_param; @@ -2151,7 +2151,7 @@ static int i40e_vc_del_mac_addr_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) i40e_status ret = 0; int i; - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states) || + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || !i40e_vc_isvalid_vsi_id(vf, vsi_id)) { ret = I40E_ERR_PARAM; goto error_param; @@ -2217,7 +2217,7 @@ static int i40e_vc_add_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) "VF is not trusted, switch the VF to trusted to add more VLAN addresses\n"); goto error_param; } - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states) || + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || !i40e_vc_isvalid_vsi_id(vf, vsi_id)) { aq_ret = I40E_ERR_PARAM; goto error_param; @@ -2244,12 +2244,12 @@ static int i40e_vc_add_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) if (!ret) vf->num_vlan++; - if (test_bit(I40E_VF_STAT_UC_PROMISC, &vf->vf_states)) + if (test_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states)) i40e_aq_set_vsi_uc_promisc_on_vlan(&pf->hw, vsi->seid, true, vfl->vlan_id[i], NULL); - if (test_bit(I40E_VF_STAT_MC_PROMISC, &vf->vf_states)) + if (test_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states)) i40e_aq_set_vsi_mc_promisc_on_vlan(&pf->hw, vsi->seid, true, vfl->vlan_id[i], @@ -2284,7 +2284,7 @@ static int i40e_vc_remove_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) i40e_status aq_ret = 0; int i; - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states) || + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || !i40e_vc_isvalid_vsi_id(vf, vsi_id)) { aq_ret = I40E_ERR_PARAM; goto error_param; @@ -2307,12 +2307,12 @@ static int i40e_vc_remove_vlan_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) i40e_vsi_kill_vlan(vsi, vfl->vlan_id[i]); vf->num_vlan--; - if (test_bit(I40E_VF_STAT_UC_PROMISC, &vf->vf_states)) + if (test_bit(I40E_VF_STATE_UC_PROMISC, &vf->vf_states)) i40e_aq_set_vsi_uc_promisc_on_vlan(&pf->hw, vsi->seid, false, vfl->vlan_id[i], NULL); - if (test_bit(I40E_VF_STAT_MC_PROMISC, &vf->vf_states)) + if (test_bit(I40E_VF_STATE_MC_PROMISC, &vf->vf_states)) i40e_aq_set_vsi_mc_promisc_on_vlan(&pf->hw, vsi->seid, false, vfl->vlan_id[i], @@ -2338,8 +2338,8 @@ static int i40e_vc_iwarp_msg(struct i40e_vf *vf, u8 *msg, u16 msglen) int abs_vf_id = vf->vf_id + pf->hw.func_caps.vf_base_id; i40e_status aq_ret = 0; - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states) || - !test_bit(I40E_VF_STAT_IWARPENA, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || + !test_bit(I40E_VF_STATE_IWARPENA, &vf->vf_states)) { aq_ret = I40E_ERR_PARAM; goto error_param; } @@ -2369,8 +2369,8 @@ static int i40e_vc_iwarp_qvmap_msg(struct i40e_vf *vf, u8 *msg, u16 msglen, (struct i40e_virtchnl_iwarp_qvlist_info *)msg; i40e_status aq_ret = 0; - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states) || - !test_bit(I40E_VF_STAT_IWARPENA, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || + !test_bit(I40E_VF_STATE_IWARPENA, &vf->vf_states)) { aq_ret = I40E_ERR_PARAM; goto error_param; } @@ -2407,7 +2407,7 @@ static int i40e_vc_config_rss_key(struct i40e_vf *vf, u8 *msg, u16 msglen) u16 vsi_id = vrk->vsi_id; i40e_status aq_ret = 0; - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states) || + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || !i40e_vc_isvalid_vsi_id(vf, vsi_id) || (vrk->key_len != I40E_HKEY_ARRAY_SIZE)) { aq_ret = I40E_ERR_PARAM; @@ -2439,7 +2439,7 @@ static int i40e_vc_config_rss_lut(struct i40e_vf *vf, u8 *msg, u16 msglen) u16 vsi_id = vrl->vsi_id; i40e_status aq_ret = 0; - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states) || + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states) || !i40e_vc_isvalid_vsi_id(vf, vsi_id) || (vrl->lut_entries != I40E_VF_HLUT_ARRAY_SIZE)) { aq_ret = I40E_ERR_PARAM; @@ -2469,7 +2469,7 @@ static int i40e_vc_get_rss_hena(struct i40e_vf *vf, u8 *msg, u16 msglen) i40e_status aq_ret = 0; int len = 0; - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { aq_ret = I40E_ERR_PARAM; goto err; } @@ -2506,7 +2506,7 @@ static int i40e_vc_set_rss_hena(struct i40e_vf *vf, u8 *msg, u16 msglen) struct i40e_hw *hw = &pf->hw; i40e_status aq_ret = 0; - if (!test_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { aq_ret = I40E_ERR_PARAM; goto err; } @@ -2536,7 +2536,7 @@ static int i40e_vc_validate_vf_msg(struct i40e_vf *vf, u32 v_opcode, int valid_len = 0; /* Check if VF is disabled. */ - if (test_bit(I40E_VF_STAT_DISABLED, &vf->vf_states)) + if (test_bit(I40E_VF_STATE_DISABLED, &vf->vf_states)) return I40E_ERR_PARAM; /* Validate message length. */ @@ -2860,7 +2860,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) vf = &(pf->vf[vf_id]); vsi = pf->vsi[vf->lan_vsi_idx]; - if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", vf_id); ret = -EAGAIN; @@ -2949,7 +2949,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, vf = &(pf->vf[vf_id]); vsi = pf->vsi[vf->lan_vsi_idx]; - if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", vf_id); ret = -EAGAIN; @@ -3081,7 +3081,7 @@ int i40e_ndo_set_vf_bw(struct net_device *netdev, int vf_id, int min_tx_rate, vf = &(pf->vf[vf_id]); vsi = pf->vsi[vf->lan_vsi_idx]; - if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", vf_id); ret = -EAGAIN; @@ -3162,7 +3162,7 @@ int i40e_ndo_get_vf_config(struct net_device *netdev, vf = &(pf->vf[vf_id]); /* first vsi is always the LAN vsi */ vsi = pf->vsi[vf->lan_vsi_idx]; - if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", vf_id); ret = -EAGAIN; @@ -3281,7 +3281,7 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable) } vf = &(pf->vf[vf_id]); - if (!test_bit(I40E_VF_STAT_INIT, &vf->vf_states)) { + if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) { dev_err(&pf->pdev->dev, "VF %d still in reset. Try again.\n", vf_id); ret = -EAGAIN; diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h index 9495f1422122..8c7c08489612 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h @@ -56,13 +56,13 @@ enum i40e_queue_ctrl { /* VF states */ enum i40e_vf_states { - I40E_VF_STAT_INIT = 0, - I40E_VF_STAT_ACTIVE, - I40E_VF_STAT_IWARPENA, - I40E_VF_STAT_FCOEENA, - I40E_VF_STAT_DISABLED, - I40E_VF_STAT_MC_PROMISC, - I40E_VF_STAT_UC_PROMISC, + I40E_VF_STATE_INIT = 0, + I40E_VF_STATE_ACTIVE, + I40E_VF_STATE_IWARPENA, + I40E_VF_STATE_FCOEENA, + I40E_VF_STATE_DISABLED, + I40E_VF_STATE_MC_PROMISC, + I40E_VF_STATE_UC_PROMISC, }; /* VF capabilities */ -- cgit From 1b48437028603ec51d5a1eb276c941c866375a3e Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Thu, 13 Apr 2017 04:45:55 -0400 Subject: i40e: make use of i40e_reset_all_vfs when initializing new VFs When allocating a large number of VFs, the driver previously used i40e_reset_vf in a sequence. Just as when performing a normal reset, this accumulates a large amount of delay for handling all of the VFs in sequence. This delay is mainly due to a hardware requirement to wait after initiating a reset on the VF. We recently added a new function, i40e_reset_all_vfs() which can be used to amortize the delay time, by first triggering all VF resets, then waiting once, and finally cleaning up and allocating the VFs. This is almost as good as truly running the resets in parallel. In order to avoid sending a spurious reset message to a client interface, we have a check to see whether we've assigned pf->num_alloc_vfs yet. This was originally intended as a way to distinguish the "initialization" case from the regular reset case. Unfortunately, this means that we can't directly use i40e_reset_all_vfs yet. Lets avoid this check of pf->num_alloc_vfs by replacing it with a proper VSI state bit which we can use instead. This makes the intention much clearer and allows us to re-use the i40e_reset_all_vfs function directly. Change-ID: I694279b37eb6b5a91b6670182d0c15d10244fd6e Signed-off-by: Jacob Keller Reviewed-by: Mitch Williams Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 10 +++++++--- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index a46c07799384..74977a295987 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1007,7 +1007,8 @@ static void i40e_cleanup_reset_vf(struct i40e_vf *vf) set_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states); clear_bit(I40E_VF_STATE_DISABLED, &vf->vf_states); /* Do not notify the client during VF init */ - if (vf->pf->num_alloc_vfs) + if (test_and_clear_bit(I40E_VF_STATE_PRE_ENABLE, + &vf->vf_states)) i40e_notify_client_of_vf_reset(pf, abs_vf_id); vf->num_vlan = 0; } @@ -1280,12 +1281,15 @@ int i40e_alloc_vfs(struct i40e_pf *pf, u16 num_alloc_vfs) /* assign default capabilities */ set_bit(I40E_VIRTCHNL_VF_CAP_L2, &vfs[i].vf_caps); vfs[i].spoofchk = true; - /* VF resources get allocated during reset */ - i40e_reset_vf(&vfs[i], false); + + set_bit(I40E_VF_STATE_PRE_ENABLE, &vfs[i].vf_states); } pf->num_alloc_vfs = num_alloc_vfs; + /* VF resources get allocated during reset */ + i40e_reset_all_vfs(pf, false); + i40e_notify_client_of_vf_enable(pf, num_alloc_vfs); err_alloc: diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h index 8c7c08489612..20d7c8160e9e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h @@ -63,6 +63,7 @@ enum i40e_vf_states { I40E_VF_STATE_DISABLED, I40E_VF_STATE_MC_PROMISC, I40E_VF_STATE_UC_PROMISC, + I40E_VF_STATE_PRE_ENABLE, }; /* VF capabilities */ -- cgit From 27826fd5d357d38b5cf834f9adcc70e6c2254d69 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 19 Apr 2017 09:25:50 -0400 Subject: i40e: rename index to port to avoid confusion The .index field of i40e_udp_port_config represents the udp port number. Rename this variable to port so that it is more obvious. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 2 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 70f9458f7a01..0f22c03ec726 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -245,7 +245,7 @@ struct i40e_tc_configuration { struct i40e_udp_port_config { /* AdminQ command interface expects port number in Host byte order */ - u16 index; + u16 port; u8 type; }; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 8f47a31cb2c8..063044268170 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7349,7 +7349,7 @@ static void i40e_sync_udp_filters_subtask(struct i40e_pf *pf) for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) { if (pf->pending_udp_bitmap & BIT_ULL(i)) { pf->pending_udp_bitmap &= ~BIT_ULL(i); - port = pf->udp_ports[i].index; + port = pf->udp_ports[i].port; if (port) ret = i40e_aq_add_udp_tunnel(hw, port, pf->udp_ports[i].type, @@ -7366,7 +7366,7 @@ static void i40e_sync_udp_filters_subtask(struct i40e_pf *pf) i40e_stat_str(&pf->hw, ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - pf->udp_ports[i].index = 0; + pf->udp_ports[i].port = 0; } } } @@ -8953,7 +8953,7 @@ static u8 i40e_get_udp_port_idx(struct i40e_pf *pf, u16 port) u8 i; for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) { - if (pf->udp_ports[i].index == port) + if (pf->udp_ports[i].port == port) return i; } @@ -9006,7 +9006,7 @@ static void i40e_udp_tunnel_add(struct net_device *netdev, } /* New port: add it and mark its index in the bitmap */ - pf->udp_ports[next_idx].index = port; + pf->udp_ports[next_idx].port = port; pf->pending_udp_bitmap |= BIT_ULL(next_idx); pf->flags |= I40E_FLAG_UDP_FILTER_SYNC; } @@ -9047,7 +9047,7 @@ static void i40e_udp_tunnel_del(struct net_device *netdev, /* if port exists, set it to 0 (mark for deletion) * and make it pending */ - pf->udp_ports[idx].index = 0; + pf->udp_ports[idx].port = 0; pf->pending_udp_bitmap |= BIT_ULL(idx); pf->flags |= I40E_FLAG_UDP_FILTER_SYNC; -- cgit From 1f190d9369487c1edfaea4d892231a62ea8206cc Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Wed, 19 Apr 2017 09:25:51 -0400 Subject: i40e: Reprogram port offloads after reset This patch corrects a major oversight in that we were not reprogramming the ports after a reset. As a result we completely lost all of the Rx tunnel offloads on receive including Rx checksum, RSS on inner headers, and ATR. The fix for this is pretty standard as all we needed to do is reset the filter bits to pending for all active filters and schedule the sync event. Signed-off-by: Alexander Duyck Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 063044268170..f44affc7e08c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7330,6 +7330,23 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf) i40e_flush(hw); } +/** + * i40e_sync_udp_filters - Trigger a sync event for existing UDP filters + * @pf: board private structure + **/ +static void i40e_sync_udp_filters(struct i40e_pf *pf) +{ + int i; + + /* loop through and set pending bit for all active UDP filters */ + for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) { + if (pf->udp_ports[i].port) + pf->pending_udp_bitmap |= BIT_ULL(i); + } + + pf->flags |= I40E_FLAG_UDP_FILTER_SYNC; +} + /** * i40e_sync_udp_filters_subtask - Sync the VSI filter list with HW * @pf: board private structure @@ -10738,6 +10755,9 @@ static int i40e_setup_pf_switch(struct i40e_pf *pf, bool reinit) i40e_ptp_init(pf); + /* repopulate tunnel port filters */ + i40e_sync_udp_filters(pf); + return ret; } -- cgit From 707d088af33043642692d4522225cb9ca638e7ee Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 19 Apr 2017 09:25:52 -0400 Subject: i40e: amortize wait time when disabling lots of VFs Just as we do in i40e_reset_all_vfs, save some time when freeing VFs by amortizing the wait time for stopping queues. We can use i40e_vsi_stop_rings_no_wait() to begin the process of stopping all the VF rings at once. Then, once we've started the process on each VF we can begin waiting for the VFs to stop. This helps reduce the total wait time by a large factor. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 74977a295987..2a47a6474366 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1194,9 +1194,21 @@ void i40e_free_vfs(struct i40e_pf *pf) usleep_range(1000, 2000); i40e_notify_client_of_vf_enable(pf, 0); - for (i = 0; i < pf->num_alloc_vfs; i++) + + /* Amortize wait time by stopping all VFs at the same time */ + for (i = 0; i < pf->num_alloc_vfs; i++) { + if (test_bit(I40E_VF_STATE_INIT, &pf->vf[i].vf_states)) + continue; + + i40e_vsi_stop_rings_no_wait(pf->vsi[pf->vf[i].lan_vsi_idx]); + } + + for (i = 0; i < pf->num_alloc_vfs; i++) { if (test_bit(I40E_VF_STATE_INIT, &pf->vf[i].vf_states)) - i40e_vsi_stop_rings(pf->vsi[pf->vf[i].lan_vsi_idx]); + continue; + + i40e_vsi_wait_queues_disabled(pf->vsi[pf->vf[i].lan_vsi_idx]); + } /* Disable IOV before freeing resources. This lets any VF drivers * running in the host get themselves cleaned up before we yank -- cgit From 2318b4018a9c2773a13f4fdac64d5519679fc171 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 19 Apr 2017 09:25:53 -0400 Subject: i40e: remove unnecessary msleep() delay in i40e_free_vfs The delay was added because of a desire to ensure that the VF driver can finish up removing. However, pci_disable_sriov already has its own ssleep() call that will sleep for an entire second, so there is no reason to add extra delay on top of this by using msleep here. In practice, an msleep() won't have a huge impact on timing but there is no real value in keeping it, so lets just simplify the code and remove it. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 2 -- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index f44affc7e08c..20850a646e6c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -47,7 +47,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 2 #define DRV_VERSION_MINOR 1 -#define DRV_VERSION_BUILD 7 +#define DRV_VERSION_BUILD 14 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 2a47a6474366..29f53f032c3c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1219,8 +1219,6 @@ void i40e_free_vfs(struct i40e_pf *pf) else dev_warn(&pf->pdev->dev, "VFs are assigned - not disabling SR-IOV\n"); - msleep(20); /* let any messages in transit get finished up */ - /* free up VF resources */ tmp = pf->num_alloc_vfs; pf->num_alloc_vfs = 0; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 5915273c372f..3ea81bd0db32 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -46,7 +46,7 @@ static const char i40evf_driver_string[] = #define DRV_VERSION_MAJOR 2 #define DRV_VERSION_MINOR 1 -#define DRV_VERSION_BUILD 7 +#define DRV_VERSION_BUILD 14 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) \ -- cgit From d19cb64b9222a93498c9dc8447503bfa87863d99 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 21 Apr 2017 13:38:05 -0700 Subject: i40e: separate PF and VSI state flags Avoid using the same named flags for both vsi->state and pf->state. This makes code review easier, as it is more likely that future authors will use the correct state field when checking bits. Previous commits already found issues with at least one check, and possibly others may be incorrect. This reduces confusion as it is more clear what each flag represents, and which flags are valid for which state field. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 12 ++++- drivers/net/ethernet/intel/i40e/i40e_client.c | 4 +- drivers/net/ethernet/intel/i40e/i40e_debugfs.c | 4 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 66 ++++++++++++------------- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 8 +-- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 8 +-- drivers/net/ethernet/intel/i40evf/i40evf.h | 7 ++- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 10 ++-- 8 files changed, 64 insertions(+), 55 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 0f22c03ec726..ac2a4850a30b 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -125,7 +125,6 @@ enum i40e_state_t { __I40E_CONFIG_BUSY, __I40E_CONFIG_DONE, __I40E_DOWN, - __I40E_NEEDS_RESTART, __I40E_SERVICE_SCHED, __I40E_ADMINQ_EVENT_PENDING, __I40E_MDD_EVENT_PENDING, @@ -138,7 +137,6 @@ enum i40e_state_t { __I40E_GLOBAL_RESET_REQUESTED, __I40E_EMP_RESET_REQUESTED, __I40E_EMP_RESET_INTR_RECEIVED, - __I40E_FILTER_OVERFLOW_PROMISC, __I40E_SUSPENDED, __I40E_PTP_TX_IN_PROGRESS, __I40E_BAD_EEPROM, @@ -149,6 +147,16 @@ enum i40e_state_t { __I40E_VF_DISABLE, }; +/* VSI state flags */ +enum i40e_vsi_state_t { + __I40E_VSI_DOWN, + __I40E_VSI_NEEDS_RESTART, + __I40E_VSI_SYNCING_FILTERS, + __I40E_VSI_OVERFLOW_PROMISC, + __I40E_VSI_REINIT_REQUESTED, + __I40E_VSI_DOWN_REQUESTED, +}; + enum i40e_interrupt_policy { I40E_INTERRUPT_BEST_CASE, I40E_INTERRUPT_MEDIUM, diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.c b/drivers/net/ethernet/intel/i40e/i40e_client.c index eb2896fd52a6..75e528a6943f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.c +++ b/drivers/net/ethernet/intel/i40e/i40e_client.c @@ -382,7 +382,7 @@ void i40e_client_subtask(struct i40e_pf *pf) * the netdev is up, then open the client. */ if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { - if (!test_bit(__I40E_DOWN, &vsi->state) && + if (!test_bit(__I40E_VSI_DOWN, &vsi->state) && client->ops && client->ops->open) { set_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); ret = client->ops->open(&cdev->lan_info, client); @@ -397,7 +397,7 @@ void i40e_client_subtask(struct i40e_pf *pf) /* Likewise for client close. If the client is up, but the netdev * is down, then close the client. */ - if (test_bit(__I40E_DOWN, &vsi->state) && + if (test_bit(__I40E_VSI_DOWN, &vsi->state) && client->ops && client->ops->close) { clear_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); client->ops->close(&cdev->lan_info, client, false); diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index a3d7ec62b76c..5408dbf04a00 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -174,7 +174,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) } dev_info(&pf->pdev->dev, " active_filters %u, promisc_threshold %u, overflow promisc %s\n", vsi->active_filters, vsi->promisc_threshold, - (test_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state) ? + (test_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state) ? "ON" : "OFF")); nstat = i40e_get_vsi_stats_struct(vsi); dev_info(&pf->pdev->dev, @@ -1706,7 +1706,7 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, } else if (!vsi->netdev) { dev_info(&pf->pdev->dev, "tx_timeout: no netdev for VSI %d\n", vsi_seid); - } else if (test_bit(__I40E_DOWN, &vsi->state)) { + } else if (test_bit(__I40E_VSI_DOWN, &vsi->state)) { dev_info(&pf->pdev->dev, "tx_timeout: VSI %d not UP\n", vsi_seid); } else if (rtnl_trylock()) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 20850a646e6c..c30f2bc65451 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -422,7 +422,7 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev, struct rtnl_link_stats64 *vsi_stats = i40e_get_vsi_stats_struct(vsi); int i; - if (test_bit(__I40E_DOWN, &vsi->state)) + if (test_bit(__I40E_VSI_DOWN, &vsi->state)) return; if (!vsi->tx_rings) @@ -753,7 +753,7 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) u64 tx_p, tx_b; u16 q; - if (test_bit(__I40E_DOWN, &vsi->state) || + if (test_bit(__I40E_VSI_DOWN, &vsi->state) || test_bit(__I40E_CONFIG_BUSY, &pf->state)) return; @@ -1346,7 +1346,7 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, * to failed, so we don't bother to try sending the filter * to the hardware. */ - if (test_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state)) + if (test_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state)) f->state = I40E_FILTER_FAILED; else f->state = I40E_FILTER_NEW; @@ -1525,7 +1525,7 @@ static int i40e_set_mac(struct net_device *netdev, void *p) return 0; } - if (test_bit(__I40E_DOWN, &vsi->back->state) || + if (test_bit(__I40E_VSI_DOWN, &vsi->back->state) || test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state)) return -EADDRNOTAVAIL; @@ -1920,7 +1920,7 @@ void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name, if (fcnt != num_add) { *promisc_changed = true; - set_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state); + set_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state); dev_warn(&vsi->back->pdev->dev, "Error %s adding RX filters on %s, promiscuous mode forced on\n", i40e_aq_str(hw, aq_err), @@ -2003,7 +2003,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) struct i40e_aqc_add_macvlan_element_data *add_list; struct i40e_aqc_remove_macvlan_element_data *del_list; - while (test_and_set_bit(__I40E_CONFIG_BUSY, &vsi->state)) + while (test_and_set_bit(__I40E_VSI_SYNCING_FILTERS, &vsi->state)) usleep_range(1000, 2000); pf = vsi->back; @@ -2139,7 +2139,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) num_add = 0; hlist_for_each_entry_safe(new, h, &tmp_add_list, hlist) { - if (test_bit(__I40E_FILTER_OVERFLOW_PROMISC, + if (test_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state)) { new->state = I40E_FILTER_FAILED; continue; @@ -2227,20 +2227,20 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) * safely exit if we didn't just enter, we no longer have any failed * filters, and we have reduced filters below the threshold value. */ - if (test_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state) && + if (test_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state) && !promisc_changed && !failed_filters && (vsi->active_filters < vsi->promisc_threshold)) { dev_info(&pf->pdev->dev, "filter logjam cleared on %s, leaving overflow promiscuous mode\n", vsi_name); - clear_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state); + clear_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state); promisc_changed = true; vsi->promisc_threshold = 0; } /* if the VF is not trusted do not do promisc */ if ((vsi->type == I40E_VSI_SRIOV) && !pf->vf[vsi->vf_id].trusted) { - clear_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state); + clear_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state); goto out; } @@ -2265,11 +2265,11 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) } if ((changed_flags & IFF_PROMISC) || (promisc_changed && - test_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state))) { + test_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state))) { bool cur_promisc; cur_promisc = (!!(vsi->current_netdev_flags & IFF_PROMISC) || - test_bit(__I40E_FILTER_OVERFLOW_PROMISC, + test_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state)); if ((vsi->type == I40E_VSI_MAIN) && (pf->lan_veb != I40E_NO_VEB) && @@ -2353,7 +2353,7 @@ out: if (retval) vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; - clear_bit(__I40E_CONFIG_BUSY, &vsi->state); + clear_bit(__I40E_VSI_SYNCING_FILTERS, &vsi->state); return retval; err_no_memory: @@ -2365,7 +2365,7 @@ err_no_memory_locked: spin_unlock_bh(&vsi->mac_filter_hash_lock); vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; - clear_bit(__I40E_CONFIG_BUSY, &vsi->state); + clear_bit(__I40E_VSI_SYNCING_FILTERS, &vsi->state); return -ENOMEM; } @@ -3907,7 +3907,7 @@ static void i40e_netpoll(struct net_device *netdev) int i; /* if interface is down do nothing */ - if (test_bit(__I40E_DOWN, &vsi->state)) + if (test_bit(__I40E_VSI_DOWN, &vsi->state)) return; if (pf->flags & I40E_FLAG_MSIX_ENABLED) { @@ -4436,7 +4436,7 @@ static void i40e_napi_disable_all(struct i40e_vsi *vsi) static void i40e_vsi_close(struct i40e_vsi *vsi) { struct i40e_pf *pf = vsi->back; - if (!test_and_set_bit(__I40E_DOWN, &vsi->state)) + if (!test_and_set_bit(__I40E_VSI_DOWN, &vsi->state)) i40e_down(vsi); i40e_vsi_free_irq(vsi); i40e_vsi_free_tx_resources(vsi); @@ -4453,10 +4453,10 @@ static void i40e_vsi_close(struct i40e_vsi *vsi) **/ static void i40e_quiesce_vsi(struct i40e_vsi *vsi) { - if (test_bit(__I40E_DOWN, &vsi->state)) + if (test_bit(__I40E_VSI_DOWN, &vsi->state)) return; - set_bit(__I40E_NEEDS_RESTART, &vsi->state); + set_bit(__I40E_VSI_NEEDS_RESTART, &vsi->state); if (vsi->netdev && netif_running(vsi->netdev)) vsi->netdev->netdev_ops->ndo_stop(vsi->netdev); else @@ -4469,10 +4469,9 @@ static void i40e_quiesce_vsi(struct i40e_vsi *vsi) **/ static void i40e_unquiesce_vsi(struct i40e_vsi *vsi) { - if (!test_bit(__I40E_NEEDS_RESTART, &vsi->state)) + if (!test_and_clear_bit(__I40E_VSI_NEEDS_RESTART, &vsi->state)) return; - clear_bit(__I40E_NEEDS_RESTART, &vsi->state); if (vsi->netdev && netif_running(vsi->netdev)) vsi->netdev->netdev_ops->ndo_open(vsi->netdev); else @@ -4638,7 +4637,7 @@ static void i40e_detect_recover_hung(struct i40e_pf *pf) return; /* Make sure, VSI state is not DOWN/RECOVERY_PENDING */ - if (test_bit(__I40E_DOWN, &vsi->back->state) || + if (test_bit(__I40E_VSI_DOWN, &vsi->back->state) || test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state)) return; @@ -5354,7 +5353,7 @@ static int i40e_up_complete(struct i40e_vsi *vsi) if (err) return err; - clear_bit(__I40E_DOWN, &vsi->state); + clear_bit(__I40E_VSI_DOWN, &vsi->state); i40e_napi_enable_all(vsi); i40e_vsi_enable_irq(vsi); @@ -5435,7 +5434,7 @@ void i40e_down(struct i40e_vsi *vsi) int i; /* It is assumed that the caller of this function - * sets the vsi->state __I40E_DOWN bit. + * sets the vsi->state __I40E_VSI_DOWN bit. */ if (vsi->netdev) { netif_carrier_off(vsi->netdev); @@ -5787,10 +5786,9 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags, bool lock_acquired) struct i40e_vsi *vsi = pf->vsi[v]; if (vsi != NULL && - test_bit(__I40E_REINIT_REQUESTED, &vsi->state)) { + test_and_clear_bit(__I40E_VSI_REINIT_REQUESTED, + &vsi->state)) i40e_vsi_reinit_locked(pf->vsi[v]); - clear_bit(__I40E_REINIT_REQUESTED, &vsi->state); - } } } else if (reset_flags & BIT_ULL(__I40E_DOWN_REQUESTED)) { int v; @@ -5801,10 +5799,10 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags, bool lock_acquired) struct i40e_vsi *vsi = pf->vsi[v]; if (vsi != NULL && - test_bit(__I40E_DOWN_REQUESTED, &vsi->state)) { - set_bit(__I40E_DOWN, &vsi->state); + test_and_clear_bit(__I40E_VSI_DOWN_REQUESTED, + &vsi->state)) { + set_bit(__I40E_VSI_DOWN, &vsi->state); i40e_down(vsi); - clear_bit(__I40E_DOWN_REQUESTED, &vsi->state); } } } else { @@ -6223,7 +6221,7 @@ static void i40e_fdir_reinit_subtask(struct i40e_pf *pf) **/ static void i40e_vsi_link_event(struct i40e_vsi *vsi, bool link_up) { - if (!vsi || test_bit(__I40E_DOWN, &vsi->state)) + if (!vsi || test_bit(__I40E_VSI_DOWN, &vsi->state)) return; switch (vsi->type) { @@ -6316,11 +6314,11 @@ static void i40e_link_event(struct i40e_pf *pf) if (new_link == old_link && new_link_speed == old_link_speed && - (test_bit(__I40E_DOWN, &vsi->state) || + (test_bit(__I40E_VSI_DOWN, &vsi->state) || new_link == netif_carrier_ok(vsi->netdev))) return; - if (!test_bit(__I40E_DOWN, &vsi->state)) + if (!test_bit(__I40E_VSI_DOWN, &vsi->state)) i40e_print_link_message(vsi, new_link); /* Notify the base of the switch tree connected to @@ -7591,7 +7589,7 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type) } vsi->type = type; vsi->back = pf; - set_bit(__I40E_DOWN, &vsi->state); + set_bit(__I40E_VSI_DOWN, &vsi->state); vsi->flags = 0; vsi->idx = vsi_idx; vsi->int_rate_limit = 0; @@ -9718,7 +9716,7 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) } vsi->active_filters = 0; - clear_bit(__I40E_FILTER_OVERFLOW_PROMISC, &vsi->state); + clear_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state); spin_lock_bh(&vsi->mac_filter_hash_lock); /* If macvlan filters already exist, force them to get loaded */ hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 1531a0f9fcc6..bbd21cbf3e4d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -850,7 +850,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, if (budget && ((j / WB_STRIDE) == 0) && (j > 0) && - !test_bit(__I40E_DOWN, &vsi->state) && + !test_bit(__I40E_VSI_DOWN, &vsi->state) && (I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) tx_ring->arm_wb = true; } @@ -868,7 +868,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, smp_mb(); if (__netif_subqueue_stopped(tx_ring->netdev, tx_ring->queue_index) && - !test_bit(__I40E_DOWN, &vsi->state)) { + !test_bit(__I40E_VSI_DOWN, &vsi->state)) { netif_wake_subqueue(tx_ring->netdev, tx_ring->queue_index); ++tx_ring->tx_stats.restart_queue; @@ -2179,7 +2179,7 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi, } enable_int: - if (!test_bit(__I40E_DOWN, &vsi->state)) + if (!test_bit(__I40E_VSI_DOWN, &vsi->state)) wr32(hw, INTREG(vector - 1), txval); if (q_vector->itr_countdown) @@ -2208,7 +2208,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) int budget_per_ring; int work_done = 0; - if (test_bit(__I40E_DOWN, &vsi->state)) { + if (test_bit(__I40E_VSI_DOWN, &vsi->state)) { napi_complete(napi); return 0; } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 34e96d98251a..c95ee0a8950f 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -266,7 +266,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, if (budget && ((j / WB_STRIDE) == 0) && (j > 0) && - !test_bit(__I40E_DOWN, &vsi->state) && + !test_bit(__I40E_VSI_DOWN, &vsi->state) && (I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) tx_ring->arm_wb = true; } @@ -284,7 +284,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, smp_mb(); if (__netif_subqueue_stopped(tx_ring->netdev, tx_ring->queue_index) && - !test_bit(__I40E_DOWN, &vsi->state)) { + !test_bit(__I40E_VSI_DOWN, &vsi->state)) { netif_wake_subqueue(tx_ring->netdev, tx_ring->queue_index); ++tx_ring->tx_stats.restart_queue; @@ -1508,7 +1508,7 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi, } enable_int: - if (!test_bit(__I40E_DOWN, &vsi->state)) + if (!test_bit(__I40E_VSI_DOWN, &vsi->state)) wr32(hw, INTREG(vector - 1), txval); if (q_vector->itr_countdown) @@ -1537,7 +1537,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) int budget_per_ring; int work_done = 0; - if (test_bit(__I40E_DOWN, &vsi->state)) { + if (test_bit(__I40E_VSI_DOWN, &vsi->state)) { napi_complete(napi); return 0; } diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 40f56e2335df..a56a6e54d907 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -49,6 +49,11 @@ #define DEFAULT_DEBUG_LEVEL_SHIFT 3 #define PFX "i40evf: " +/* VSI state flags shared with common code */ +enum i40evf_vsi_state_t { + __I40E_VSI_DOWN, +}; + /* dummy struct to make common code less painful */ struct i40e_vsi { struct i40evf_adapter *back; @@ -168,8 +173,6 @@ enum i40evf_critical_section_t { __I40EVF_IN_CRITICAL_TASK, /* cannot be interrupted */ __I40EVF_IN_CLIENT_TASK, }; -/* make common code happy */ -#define __I40E_DOWN __I40EVF_DOWN /* board specific private data structure */ struct i40evf_adapter { diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 3ea81bd0db32..b453d05c2a30 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -497,7 +497,7 @@ static void i40evf_netpoll(struct net_device *netdev) int i; /* if interface is down do nothing */ - if (test_bit(__I40E_DOWN, &adapter->vsi.state)) + if (test_bit(__I40E_VSI_DOWN, &adapter->vsi.state)) return; for (i = 0; i < q_vectors; i++) @@ -1087,7 +1087,7 @@ static void i40evf_configure(struct i40evf_adapter *adapter) static void i40evf_up_complete(struct i40evf_adapter *adapter) { adapter->state = __I40EVF_RUNNING; - clear_bit(__I40E_DOWN, &adapter->vsi.state); + clear_bit(__I40E_VSI_DOWN, &adapter->vsi.state); i40evf_napi_enable_all(adapter); @@ -1753,7 +1753,7 @@ static void i40evf_disable_vf(struct i40evf_adapter *adapter) adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED; if (netif_running(adapter->netdev)) { - set_bit(__I40E_DOWN, &adapter->vsi.state); + set_bit(__I40E_VSI_DOWN, &adapter->vsi.state); netif_carrier_off(adapter->netdev); netif_tx_disable(adapter->netdev); adapter->link_up = false; @@ -2233,7 +2233,7 @@ static int i40evf_close(struct net_device *netdev) return 0; - set_bit(__I40E_DOWN, &adapter->vsi.state); + set_bit(__I40E_VSI_DOWN, &adapter->vsi.state); if (CLIENT_ENABLED(adapter)) adapter->flags |= I40EVF_FLAG_CLIENT_NEEDS_CLOSE; @@ -2674,7 +2674,7 @@ static void i40evf_init_task(struct work_struct *work) dev_info(&pdev->dev, "GRO is enabled\n"); adapter->state = __I40EVF_DOWN; - set_bit(__I40E_DOWN, &adapter->vsi.state); + set_bit(__I40E_VSI_DOWN, &adapter->vsi.state); i40evf_misc_irq_enable(adapter); adapter->rss_key = kzalloc(adapter->rss_key_size, GFP_KERNEL); -- cgit From 0da36b9774cc24bac4bff446edf49f31aa98a282 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 19 Apr 2017 09:25:55 -0400 Subject: i40e: use DECLARE_BITMAP for state fields Instead of assuming our flags fit within an unsigned long, use DECLARE_BITMAP which will ensure that we always allocate enough space. Additionally, use __I40E_STATE_SIZE__ markers as the last element of the enumeration so that the size of the BITMAP is compile-time assigned rather than programmer-time assigned. This ensures that potential future flag additions do not actually overrun the array. This is especially important as 32bit systems would only have 32bit longs instead of 64bit longs as we generally have assumed in the prior code. This change also removes a dereference of the state fields throughout the code, so it does have a bit of code churn. The conversions were automated using sed replacements with an alternation s/&(vsi->back|vsi|pf)->state/\1->state/ s/&adapter->vsi.state/adapter->vsi.state/ For debugfs, we modify the printing so that we can display chunks of the state value on new lines. This ensures that we can print the entire set of state values. Additionally, we now print them as 08lx to ensure that they display nicely. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 8 +- drivers/net/ethernet/intel/i40e/i40e_client.c | 16 +- drivers/net/ethernet/intel/i40e/i40e_debugfs.c | 13 +- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 42 ++-- drivers/net/ethernet/intel/i40e/i40e_main.c | 233 ++++++++++----------- drivers/net/ethernet/intel/i40e/i40e_ptp.c | 4 +- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 14 +- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 18 +- drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 8 +- drivers/net/ethernet/intel/i40evf/i40evf.h | 4 +- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 10 +- 11 files changed, 189 insertions(+), 181 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index ac2a4850a30b..6eb21abdc60e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -145,6 +145,8 @@ enum i40e_state_t { __I40E_RESET_FAILED, __I40E_PORT_SUSPENDED, __I40E_VF_DISABLE, + /* This must be last as it determines the size of the BITMAP */ + __I40E_STATE_SIZE__, }; /* VSI state flags */ @@ -155,6 +157,8 @@ enum i40e_vsi_state_t { __I40E_VSI_OVERFLOW_PROMISC, __I40E_VSI_REINIT_REQUESTED, __I40E_VSI_DOWN_REQUESTED, + /* This must be last as it determines the size of the BITMAP */ + __I40E_VSI_STATE_SIZE__, }; enum i40e_interrupt_policy { @@ -330,7 +334,7 @@ struct i40e_flex_pit { struct i40e_pf { struct pci_dev *pdev; struct i40e_hw hw; - unsigned long state; + DECLARE_BITMAP(state, __I40E_STATE_SIZE__); struct msix_entry *msix_entries; bool fc_autoneg_status; @@ -601,7 +605,7 @@ struct i40e_vsi { bool stat_offsets_loaded; u32 current_netdev_flags; - unsigned long state; + DECLARE_BITMAP(state, __I40E_VSI_STATE_SIZE__); #define I40E_VSI_FLAG_FILTER_CHANGED BIT(0) #define I40E_VSI_FLAG_VEB_OWNER BIT(1) unsigned long flags; diff --git a/drivers/net/ethernet/intel/i40e/i40e_client.c b/drivers/net/ethernet/intel/i40e/i40e_client.c index 75e528a6943f..c3b81a97558e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_client.c +++ b/drivers/net/ethernet/intel/i40e/i40e_client.c @@ -371,8 +371,8 @@ void i40e_client_subtask(struct i40e_pf *pf) cdev = pf->cinst; /* If we're down or resetting, just bail */ - if (test_bit(__I40E_DOWN, &pf->state) || - test_bit(__I40E_CONFIG_BUSY, &pf->state)) + if (test_bit(__I40E_DOWN, pf->state) || + test_bit(__I40E_CONFIG_BUSY, pf->state)) return; if (!client || !cdev) @@ -382,7 +382,7 @@ void i40e_client_subtask(struct i40e_pf *pf) * the netdev is up, then open the client. */ if (!test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { - if (!test_bit(__I40E_VSI_DOWN, &vsi->state) && + if (!test_bit(__I40E_VSI_DOWN, vsi->state) && client->ops && client->ops->open) { set_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); ret = client->ops->open(&cdev->lan_info, client); @@ -397,7 +397,7 @@ void i40e_client_subtask(struct i40e_pf *pf) /* Likewise for client close. If the client is up, but the netdev * is down, then close the client. */ - if (test_bit(__I40E_VSI_DOWN, &vsi->state) && + if (test_bit(__I40E_VSI_DOWN, vsi->state) && client->ops && client->ops->close) { clear_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state); client->ops->close(&cdev->lan_info, client, false); @@ -503,7 +503,7 @@ static void i40e_client_release(struct i40e_client *client) continue; while (test_and_set_bit(__I40E_SERVICE_SCHED, - &pf->state)) + pf->state)) usleep_range(500, 1000); if (test_bit(__I40E_CLIENT_INSTANCE_OPENED, &cdev->state)) { @@ -521,7 +521,7 @@ static void i40e_client_release(struct i40e_client *client) i40e_client_del_instance(pf); dev_info(&pf->pdev->dev, "Deleted client instance of Client %s\n", client->name); - clear_bit(__I40E_SERVICE_SCHED, &pf->state); + clear_bit(__I40E_SERVICE_SCHED, pf->state); } mutex_unlock(&i40e_device_mutex); } @@ -661,10 +661,10 @@ static void i40e_client_request_reset(struct i40e_info *ldev, switch (reset_level) { case I40E_CLIENT_RESET_LEVEL_PF: - set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); + set_bit(__I40E_PF_RESET_REQUESTED, pf->state); break; case I40E_CLIENT_RESET_LEVEL_CORE: - set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); + set_bit(__I40E_PF_RESET_REQUESTED, pf->state); break; default: dev_warn(&pf->pdev->dev, diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index 5408dbf04a00..8f326f87a815 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -158,9 +158,12 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) dev_info(&pf->pdev->dev, " vlgrp: & = %p\n", vsi->active_vlans); dev_info(&pf->pdev->dev, - " state = %li flags = 0x%08lx, netdev_registered = %i, current_netdev_flags = 0x%04x\n", - vsi->state, vsi->flags, - vsi->netdev_registered, vsi->current_netdev_flags); + " flags = 0x%08lx, netdev_registered = %i, current_netdev_flags = 0x%04x\n", + vsi->flags, vsi->netdev_registered, vsi->current_netdev_flags); + for (i = 0; i < BITS_TO_LONGS(__I40E_VSI_STATE_SIZE__); i++) + dev_info(&pf->pdev->dev, + " state[%d] = %08lx\n", + i, vsi->state[i]); if (vsi == pf->vsi[pf->lan_vsi]) dev_info(&pf->pdev->dev, " MAC address: %pM SAN MAC: %pM Port MAC: %pM\n", pf->hw.mac.addr, @@ -174,7 +177,7 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) } dev_info(&pf->pdev->dev, " active_filters %u, promisc_threshold %u, overflow promisc %s\n", vsi->active_filters, vsi->promisc_threshold, - (test_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state) ? + (test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state) ? "ON" : "OFF")); nstat = i40e_get_vsi_stats_struct(vsi); dev_info(&pf->pdev->dev, @@ -1706,7 +1709,7 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, } else if (!vsi->netdev) { dev_info(&pf->pdev->dev, "tx_timeout: no netdev for VSI %d\n", vsi_seid); - } else if (test_bit(__I40E_VSI_DOWN, &vsi->state)) { + } else if (test_bit(__I40E_VSI_DOWN, vsi->state)) { dev_info(&pf->pdev->dev, "tx_timeout: VSI %d not UP\n", vsi_seid); } else if (rtnl_trylock()) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 523dd81d76b7..b1064c6468c2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -757,7 +757,7 @@ static int i40e_set_link_ksettings(struct net_device *netdev, if (memcmp(©_cmd, &safe_cmd, sizeof(struct ethtool_link_ksettings))) return -EOPNOTSUPP; - while (test_and_set_bit(__I40E_CONFIG_BUSY, &pf->state)) { + while (test_and_set_bit(__I40E_CONFIG_BUSY, pf->state)) { timeout--; if (!timeout) return -EBUSY; @@ -891,7 +891,7 @@ static int i40e_set_link_ksettings(struct net_device *netdev, } done: - clear_bit(__I40E_CONFIG_BUSY, &pf->state); + clear_bit(__I40E_CONFIG_BUSY, pf->state); return err; } @@ -987,7 +987,7 @@ static int i40e_set_pauseparam(struct net_device *netdev, } /* If we have link and don't have autoneg */ - if (!test_bit(__I40E_DOWN, &pf->state) && + if (!test_bit(__I40E_DOWN, pf->state) && !(hw_link_info->an_info & I40E_AQ_AN_COMPLETED)) { /* Send message that it might not necessarily work*/ netdev_info(netdev, "Autoneg did not complete so changing settings may not result in an actual change.\n"); @@ -1039,10 +1039,10 @@ static int i40e_set_pauseparam(struct net_device *netdev, err = -EAGAIN; } - if (!test_bit(__I40E_DOWN, &pf->state)) { + if (!test_bit(__I40E_DOWN, pf->state)) { /* Give it a little more time to try to come back */ msleep(75); - if (!test_bit(__I40E_DOWN, &pf->state)) + if (!test_bit(__I40E_DOWN, pf->state)) return i40e_nway_reset(netdev); } @@ -1139,8 +1139,8 @@ static int i40e_get_eeprom(struct net_device *netdev, /* make sure it is the right magic for NVMUpdate */ if ((eeprom->magic >> 16) != hw->device_id) errno = -EINVAL; - else if (test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state) || - test_bit(__I40E_RESET_INTR_RECEIVED, &pf->state)) + else if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) || + test_bit(__I40E_RESET_INTR_RECEIVED, pf->state)) errno = -EBUSY; else ret_val = i40e_nvmupd_command(hw, cmd, bytes, &errno); @@ -1246,8 +1246,8 @@ static int i40e_set_eeprom(struct net_device *netdev, /* check for NVMUpdate access method */ else if (!eeprom->magic || (eeprom->magic >> 16) != hw->device_id) errno = -EINVAL; - else if (test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state) || - test_bit(__I40E_RESET_INTR_RECEIVED, &pf->state)) + else if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) || + test_bit(__I40E_RESET_INTR_RECEIVED, pf->state)) errno = -EBUSY; else ret_val = i40e_nvmupd_command(hw, cmd, bytes, &errno); @@ -1332,7 +1332,7 @@ static int i40e_set_ringparam(struct net_device *netdev, (new_rx_count == vsi->rx_rings[0]->count)) return 0; - while (test_and_set_bit(__I40E_CONFIG_BUSY, &pf->state)) { + while (test_and_set_bit(__I40E_CONFIG_BUSY, pf->state)) { timeout--; if (!timeout) return -EBUSY; @@ -1485,7 +1485,7 @@ free_tx: } done: - clear_bit(__I40E_CONFIG_BUSY, &pf->state); + clear_bit(__I40E_CONFIG_BUSY, pf->state); return err; } @@ -1847,7 +1847,7 @@ static void i40e_diag_test(struct net_device *netdev, /* Offline tests */ netif_info(pf, drv, netdev, "offline testing starting\n"); - set_bit(__I40E_TESTING, &pf->state); + set_bit(__I40E_TESTING, pf->state); if (i40e_active_vfs(pf) || i40e_active_vmdqs(pf)) { dev_warn(&pf->pdev->dev, @@ -1857,7 +1857,7 @@ static void i40e_diag_test(struct net_device *netdev, data[I40E_ETH_TEST_INTR] = 1; data[I40E_ETH_TEST_LINK] = 1; eth_test->flags |= ETH_TEST_FL_FAILED; - clear_bit(__I40E_TESTING, &pf->state); + clear_bit(__I40E_TESTING, pf->state); goto skip_ol_tests; } @@ -1886,7 +1886,7 @@ static void i40e_diag_test(struct net_device *netdev, if (i40e_reg_test(netdev, &data[I40E_ETH_TEST_REG])) eth_test->flags |= ETH_TEST_FL_FAILED; - clear_bit(__I40E_TESTING, &pf->state); + clear_bit(__I40E_TESTING, pf->state); i40e_do_reset(pf, BIT(__I40E_PF_RESET_REQUESTED), true); if (if_running) @@ -2924,11 +2924,11 @@ static int i40e_del_fdir_entry(struct i40e_vsi *vsi, struct i40e_pf *pf = vsi->back; int ret = 0; - if (test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state) || - test_bit(__I40E_RESET_INTR_RECEIVED, &pf->state)) + if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) || + test_bit(__I40E_RESET_INTR_RECEIVED, pf->state)) return -EBUSY; - if (test_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state)) + if (test_bit(__I40E_FD_FLUSH_REQUESTED, pf->state)) return -EBUSY; ret = i40e_update_ethtool_fdir_entry(vsi, NULL, fsp->location, cmd); @@ -3646,11 +3646,11 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, if (pf->hw_disabled_flags & I40E_FLAG_FD_SB_ENABLED) return -ENOSPC; - if (test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state) || - test_bit(__I40E_RESET_INTR_RECEIVED, &pf->state)) + if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) || + test_bit(__I40E_RESET_INTR_RECEIVED, pf->state)) return -EBUSY; - if (test_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state)) + if (test_bit(__I40E_FD_FLUSH_REQUESTED, pf->state)) return -EBUSY; fsp = (struct ethtool_rx_flow_spec *)&cmd->fs; @@ -4087,7 +4087,7 @@ flags_complete: if ((changed_flags & I40E_FLAG_FD_ATR_ENABLED) && !(pf->flags & I40E_FLAG_FD_ATR_ENABLED)) { pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; - set_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); + set_bit(__I40E_FD_FLUSH_REQUESTED, pf->state); } /* Only allow ATR evict on hardware that is capable of handling it */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index c30f2bc65451..38772e49bb84 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -295,8 +295,8 @@ struct i40e_vsi *i40e_find_vsi_from_id(struct i40e_pf *pf, u16 id) **/ void i40e_service_event_schedule(struct i40e_pf *pf) { - if (!test_bit(__I40E_DOWN, &pf->state) && - !test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state)) + if (!test_bit(__I40E_VSI_DOWN, pf->state) && + !test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) queue_work(i40e_wq, &pf->service_task); } @@ -377,13 +377,13 @@ static void i40e_tx_timeout(struct net_device *netdev) switch (pf->tx_timeout_recovery_level) { case 1: - set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); + set_bit(__I40E_PF_RESET_REQUESTED, pf->state); break; case 2: - set_bit(__I40E_CORE_RESET_REQUESTED, &pf->state); + set_bit(__I40E_CORE_RESET_REQUESTED, pf->state); break; case 3: - set_bit(__I40E_GLOBAL_RESET_REQUESTED, &pf->state); + set_bit(__I40E_GLOBAL_RESET_REQUESTED, pf->state); break; default: netdev_err(netdev, "tx_timeout recovery unsuccessful\n"); @@ -422,7 +422,7 @@ static void i40e_get_netdev_stats_struct(struct net_device *netdev, struct rtnl_link_stats64 *vsi_stats = i40e_get_vsi_stats_struct(vsi); int i; - if (test_bit(__I40E_VSI_DOWN, &vsi->state)) + if (test_bit(__I40E_VSI_DOWN, vsi->state)) return; if (!vsi->tx_rings) @@ -753,8 +753,8 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) u64 tx_p, tx_b; u16 q; - if (test_bit(__I40E_VSI_DOWN, &vsi->state) || - test_bit(__I40E_CONFIG_BUSY, &pf->state)) + if (test_bit(__I40E_VSI_DOWN, vsi->state) || + test_bit(__I40E_CONFIG_BUSY, pf->state)) return; ns = i40e_get_vsi_stats_struct(vsi); @@ -1346,7 +1346,7 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi, * to failed, so we don't bother to try sending the filter * to the hardware. */ - if (test_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state)) + if (test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state)) f->state = I40E_FILTER_FAILED; else f->state = I40E_FILTER_NEW; @@ -1525,8 +1525,8 @@ static int i40e_set_mac(struct net_device *netdev, void *p) return 0; } - if (test_bit(__I40E_VSI_DOWN, &vsi->back->state) || - test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state)) + if (test_bit(__I40E_VSI_DOWN, vsi->back->state) || + test_bit(__I40E_RESET_RECOVERY_PENDING, vsi->back->state)) return -EADDRNOTAVAIL; if (ether_addr_equal(hw->mac.addr, addr->sa_data)) @@ -1920,7 +1920,7 @@ void i40e_aqc_add_filters(struct i40e_vsi *vsi, const char *vsi_name, if (fcnt != num_add) { *promisc_changed = true; - set_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state); + set_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); dev_warn(&vsi->back->pdev->dev, "Error %s adding RX filters on %s, promiscuous mode forced on\n", i40e_aq_str(hw, aq_err), @@ -2003,7 +2003,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) struct i40e_aqc_add_macvlan_element_data *add_list; struct i40e_aqc_remove_macvlan_element_data *del_list; - while (test_and_set_bit(__I40E_VSI_SYNCING_FILTERS, &vsi->state)) + while (test_and_set_bit(__I40E_VSI_SYNCING_FILTERS, vsi->state)) usleep_range(1000, 2000); pf = vsi->back; @@ -2140,7 +2140,7 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) num_add = 0; hlist_for_each_entry_safe(new, h, &tmp_add_list, hlist) { if (test_bit(__I40E_VSI_OVERFLOW_PROMISC, - &vsi->state)) { + vsi->state)) { new->state = I40E_FILTER_FAILED; continue; } @@ -2227,20 +2227,20 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) * safely exit if we didn't just enter, we no longer have any failed * filters, and we have reduced filters below the threshold value. */ - if (test_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state) && + if (test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state) && !promisc_changed && !failed_filters && (vsi->active_filters < vsi->promisc_threshold)) { dev_info(&pf->pdev->dev, "filter logjam cleared on %s, leaving overflow promiscuous mode\n", vsi_name); - clear_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state); + clear_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); promisc_changed = true; vsi->promisc_threshold = 0; } /* if the VF is not trusted do not do promisc */ if ((vsi->type == I40E_VSI_SRIOV) && !pf->vf[vsi->vf_id].trusted) { - clear_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state); + clear_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); goto out; } @@ -2265,12 +2265,12 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi) } if ((changed_flags & IFF_PROMISC) || (promisc_changed && - test_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state))) { + test_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state))) { bool cur_promisc; cur_promisc = (!!(vsi->current_netdev_flags & IFF_PROMISC) || test_bit(__I40E_VSI_OVERFLOW_PROMISC, - &vsi->state)); + vsi->state)); if ((vsi->type == I40E_VSI_MAIN) && (pf->lan_veb != I40E_NO_VEB) && !(pf->flags & I40E_FLAG_MFP_ENABLED)) { @@ -2353,7 +2353,7 @@ out: if (retval) vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; - clear_bit(__I40E_VSI_SYNCING_FILTERS, &vsi->state); + clear_bit(__I40E_VSI_SYNCING_FILTERS, vsi->state); return retval; err_no_memory: @@ -2365,7 +2365,7 @@ err_no_memory_locked: spin_unlock_bh(&vsi->mac_filter_hash_lock); vsi->flags |= I40E_VSI_FLAG_FILTER_CHANGED; - clear_bit(__I40E_VSI_SYNCING_FILTERS, &vsi->state); + clear_bit(__I40E_VSI_SYNCING_FILTERS, vsi->state); return -ENOMEM; } @@ -3611,29 +3611,29 @@ static irqreturn_t i40e_intr(int irq, void *data) * this is not a performance path and napi_schedule() * can deal with rescheduling. */ - if (!test_bit(__I40E_DOWN, &pf->state)) + if (!test_bit(__I40E_VSI_DOWN, pf->state)) napi_schedule_irqoff(&q_vector->napi); } if (icr0 & I40E_PFINT_ICR0_ADMINQ_MASK) { ena_mask &= ~I40E_PFINT_ICR0_ENA_ADMINQ_MASK; - set_bit(__I40E_ADMINQ_EVENT_PENDING, &pf->state); + set_bit(__I40E_ADMINQ_EVENT_PENDING, pf->state); i40e_debug(&pf->hw, I40E_DEBUG_NVM, "AdminQ event\n"); } if (icr0 & I40E_PFINT_ICR0_MAL_DETECT_MASK) { ena_mask &= ~I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK; - set_bit(__I40E_MDD_EVENT_PENDING, &pf->state); + set_bit(__I40E_MDD_EVENT_PENDING, pf->state); } if (icr0 & I40E_PFINT_ICR0_VFLR_MASK) { ena_mask &= ~I40E_PFINT_ICR0_ENA_VFLR_MASK; - set_bit(__I40E_VFLR_EVENT_PENDING, &pf->state); + set_bit(__I40E_VFLR_EVENT_PENDING, pf->state); } if (icr0 & I40E_PFINT_ICR0_GRST_MASK) { - if (!test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state)) - set_bit(__I40E_RESET_INTR_RECEIVED, &pf->state); + if (!test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) + set_bit(__I40E_RESET_INTR_RECEIVED, pf->state); ena_mask &= ~I40E_PFINT_ICR0_ENA_GRST_MASK; val = rd32(hw, I40E_GLGEN_RSTAT); val = (val & I40E_GLGEN_RSTAT_RESET_TYPE_MASK) @@ -3644,7 +3644,7 @@ static irqreturn_t i40e_intr(int irq, void *data) pf->globr_count++; } else if (val == I40E_RESET_EMPR) { pf->empr_count++; - set_bit(__I40E_EMP_RESET_INTR_RECEIVED, &pf->state); + set_bit(__I40E_EMP_RESET_INTR_RECEIVED, pf->state); } } @@ -3677,7 +3677,7 @@ static irqreturn_t i40e_intr(int irq, void *data) (icr0_remaining & I40E_PFINT_ICR0_PCI_EXCEPTION_MASK) || (icr0_remaining & I40E_PFINT_ICR0_ECC_ERR_MASK)) { dev_info(&pf->pdev->dev, "device will be reset\n"); - set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); + set_bit(__I40E_PF_RESET_REQUESTED, pf->state); i40e_service_event_schedule(pf); } ena_mask &= ~icr0_remaining; @@ -3687,7 +3687,7 @@ static irqreturn_t i40e_intr(int irq, void *data) enable_intr: /* re-enable interrupt causes */ wr32(hw, I40E_PFINT_ICR0_ENA, ena_mask); - if (!test_bit(__I40E_DOWN, &pf->state)) { + if (!test_bit(__I40E_VSI_DOWN, pf->state)) { i40e_service_event_schedule(pf); i40e_irq_dynamic_enable_icr0(pf, false); } @@ -3907,7 +3907,7 @@ static void i40e_netpoll(struct net_device *netdev) int i; /* if interface is down do nothing */ - if (test_bit(__I40E_VSI_DOWN, &vsi->state)) + if (test_bit(__I40E_VSI_DOWN, vsi->state)) return; if (pf->flags & I40E_FLAG_MSIX_ENABLED) { @@ -4144,7 +4144,7 @@ int i40e_vsi_start_rings(struct i40e_vsi *vsi) void i40e_vsi_stop_rings(struct i40e_vsi *vsi) { /* When port TX is suspended, don't wait */ - if (test_bit(__I40E_PORT_SUSPENDED, &vsi->back->state)) + if (test_bit(__I40E_PORT_SUSPENDED, vsi->back->state)) return i40e_vsi_stop_rings_no_wait(vsi); /* do rx first for enable and last for disable @@ -4436,14 +4436,14 @@ static void i40e_napi_disable_all(struct i40e_vsi *vsi) static void i40e_vsi_close(struct i40e_vsi *vsi) { struct i40e_pf *pf = vsi->back; - if (!test_and_set_bit(__I40E_VSI_DOWN, &vsi->state)) + if (!test_and_set_bit(__I40E_VSI_DOWN, vsi->state)) i40e_down(vsi); i40e_vsi_free_irq(vsi); i40e_vsi_free_tx_resources(vsi); i40e_vsi_free_rx_resources(vsi); vsi->current_netdev_flags = 0; pf->flags |= I40E_FLAG_SERVICE_CLIENT_REQUESTED; - if (test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state)) + if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) pf->flags |= I40E_FLAG_CLIENT_RESET; } @@ -4453,10 +4453,10 @@ static void i40e_vsi_close(struct i40e_vsi *vsi) **/ static void i40e_quiesce_vsi(struct i40e_vsi *vsi) { - if (test_bit(__I40E_VSI_DOWN, &vsi->state)) + if (test_bit(__I40E_VSI_DOWN, vsi->state)) return; - set_bit(__I40E_VSI_NEEDS_RESTART, &vsi->state); + set_bit(__I40E_VSI_NEEDS_RESTART, vsi->state); if (vsi->netdev && netif_running(vsi->netdev)) vsi->netdev->netdev_ops->ndo_stop(vsi->netdev); else @@ -4469,7 +4469,7 @@ static void i40e_quiesce_vsi(struct i40e_vsi *vsi) **/ static void i40e_unquiesce_vsi(struct i40e_vsi *vsi) { - if (!test_and_clear_bit(__I40E_VSI_NEEDS_RESTART, &vsi->state)) + if (!test_and_clear_bit(__I40E_VSI_NEEDS_RESTART, vsi->state)) return; if (vsi->netdev && netif_running(vsi->netdev)) @@ -4637,8 +4637,8 @@ static void i40e_detect_recover_hung(struct i40e_pf *pf) return; /* Make sure, VSI state is not DOWN/RECOVERY_PENDING */ - if (test_bit(__I40E_VSI_DOWN, &vsi->back->state) || - test_bit(__I40E_RESET_RECOVERY_PENDING, &vsi->back->state)) + if (test_bit(__I40E_VSI_DOWN, vsi->back->state) || + test_bit(__I40E_RESET_RECOVERY_PENDING, vsi->back->state)) return; /* Make sure type is MAIN VSI */ @@ -5185,7 +5185,7 @@ static int i40e_resume_port_tx(struct i40e_pf *pf) i40e_stat_str(&pf->hw, ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); /* Schedule PF reset to recover */ - set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); + set_bit(__I40E_PF_RESET_REQUESTED, pf->state); i40e_service_event_schedule(pf); } @@ -5353,7 +5353,7 @@ static int i40e_up_complete(struct i40e_vsi *vsi) if (err) return err; - clear_bit(__I40E_VSI_DOWN, &vsi->state); + clear_bit(__I40E_VSI_DOWN, vsi->state); i40e_napi_enable_all(vsi); i40e_vsi_enable_irq(vsi); @@ -5402,12 +5402,12 @@ static void i40e_vsi_reinit_locked(struct i40e_vsi *vsi) struct i40e_pf *pf = vsi->back; WARN_ON(in_interrupt()); - while (test_and_set_bit(__I40E_CONFIG_BUSY, &pf->state)) + while (test_and_set_bit(__I40E_CONFIG_BUSY, pf->state)) usleep_range(1000, 2000); i40e_down(vsi); i40e_up(vsi); - clear_bit(__I40E_CONFIG_BUSY, &pf->state); + clear_bit(__I40E_CONFIG_BUSY, pf->state); } /** @@ -5540,8 +5540,8 @@ int i40e_open(struct net_device *netdev) int err; /* disallow open during test or if eeprom is broken */ - if (test_bit(__I40E_TESTING, &pf->state) || - test_bit(__I40E_BAD_EEPROM, &pf->state)) + if (test_bit(__I40E_TESTING, pf->state) || + test_bit(__I40E_BAD_EEPROM, pf->state)) return -EBUSY; netif_carrier_off(netdev); @@ -5787,7 +5787,7 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags, bool lock_acquired) if (vsi != NULL && test_and_clear_bit(__I40E_VSI_REINIT_REQUESTED, - &vsi->state)) + vsi->state)) i40e_vsi_reinit_locked(pf->vsi[v]); } } else if (reset_flags & BIT_ULL(__I40E_DOWN_REQUESTED)) { @@ -5800,8 +5800,8 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags, bool lock_acquired) if (vsi != NULL && test_and_clear_bit(__I40E_VSI_DOWN_REQUESTED, - &vsi->state)) { - set_bit(__I40E_VSI_DOWN, &vsi->state); + vsi->state)) { + set_bit(__I40E_VSI_DOWN, vsi->state); i40e_down(vsi); } } @@ -5942,7 +5942,7 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf, else pf->flags &= ~I40E_FLAG_DCB_ENABLED; - set_bit(__I40E_PORT_SUSPENDED, &pf->state); + set_bit(__I40E_PORT_SUSPENDED, pf->state); /* Reconfiguration needed quiesce all VSIs */ i40e_pf_quiesce_all_vsi(pf); @@ -5951,7 +5951,7 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf, ret = i40e_resume_port_tx(pf); - clear_bit(__I40E_PORT_SUSPENDED, &pf->state); + clear_bit(__I40E_PORT_SUSPENDED, pf->state); /* In case of error no point in resuming VSIs */ if (ret) goto exit; @@ -5960,7 +5960,7 @@ static int i40e_handle_lldp_event(struct i40e_pf *pf, ret = i40e_pf_wait_queues_disabled(pf); if (ret) { /* Schedule PF reset to recover */ - set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); + set_bit(__I40E_PF_RESET_REQUESTED, pf->state); i40e_service_event_schedule(pf); } else { i40e_pf_unquiesce_all_vsi(pf); @@ -6075,7 +6075,7 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf) u32 fcnt_prog, fcnt_avail; struct hlist_node *node; - if (test_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state)) + if (test_bit(__I40E_FD_FLUSH_REQUESTED, pf->state)) return; /* Check if, FD SB or ATR was auto disabled and if there is enough room @@ -6174,7 +6174,7 @@ static void i40e_fdir_flush_and_replay(struct i40e_pf *pf) i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]); if (!disable_atr && !pf->fd_tcp4_filter_cnt) pf->hw_disabled_flags &= ~I40E_FLAG_FD_ATR_ENABLED; - clear_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); + 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"); } @@ -6204,10 +6204,10 @@ static void i40e_fdir_reinit_subtask(struct i40e_pf *pf) { /* if interface is down do nothing */ - if (test_bit(__I40E_DOWN, &pf->state)) + if (test_bit(__I40E_VSI_DOWN, pf->state)) return; - if (test_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state)) + if (test_bit(__I40E_FD_FLUSH_REQUESTED, pf->state)) i40e_fdir_flush_and_replay(pf); i40e_fdir_check_and_reenable(pf); @@ -6221,7 +6221,7 @@ static void i40e_fdir_reinit_subtask(struct i40e_pf *pf) **/ static void i40e_vsi_link_event(struct i40e_vsi *vsi, bool link_up) { - if (!vsi || test_bit(__I40E_VSI_DOWN, &vsi->state)) + if (!vsi || test_bit(__I40E_VSI_DOWN, vsi->state)) return; switch (vsi->type) { @@ -6314,11 +6314,11 @@ static void i40e_link_event(struct i40e_pf *pf) if (new_link == old_link && new_link_speed == old_link_speed && - (test_bit(__I40E_VSI_DOWN, &vsi->state) || + (test_bit(__I40E_VSI_DOWN, vsi->state) || new_link == netif_carrier_ok(vsi->netdev))) return; - if (!test_bit(__I40E_VSI_DOWN, &vsi->state)) + if (!test_bit(__I40E_VSI_DOWN, vsi->state)) i40e_print_link_message(vsi, new_link); /* Notify the base of the switch tree connected to @@ -6345,8 +6345,8 @@ static void i40e_watchdog_subtask(struct i40e_pf *pf) int i; /* if interface is down do nothing */ - if (test_bit(__I40E_DOWN, &pf->state) || - test_bit(__I40E_CONFIG_BUSY, &pf->state)) + if (test_bit(__I40E_VSI_DOWN, pf->state) || + test_bit(__I40E_CONFIG_BUSY, pf->state)) return; /* make sure we don't do these things too often */ @@ -6384,31 +6384,31 @@ static void i40e_reset_subtask(struct i40e_pf *pf) { u32 reset_flags = 0; - if (test_bit(__I40E_REINIT_REQUESTED, &pf->state)) { + if (test_bit(__I40E_REINIT_REQUESTED, pf->state)) { reset_flags |= BIT(__I40E_REINIT_REQUESTED); - clear_bit(__I40E_REINIT_REQUESTED, &pf->state); + clear_bit(__I40E_REINIT_REQUESTED, pf->state); } - if (test_bit(__I40E_PF_RESET_REQUESTED, &pf->state)) { + if (test_bit(__I40E_PF_RESET_REQUESTED, pf->state)) { reset_flags |= BIT(__I40E_PF_RESET_REQUESTED); - clear_bit(__I40E_PF_RESET_REQUESTED, &pf->state); + clear_bit(__I40E_PF_RESET_REQUESTED, pf->state); } - if (test_bit(__I40E_CORE_RESET_REQUESTED, &pf->state)) { + if (test_bit(__I40E_CORE_RESET_REQUESTED, pf->state)) { reset_flags |= BIT(__I40E_CORE_RESET_REQUESTED); - clear_bit(__I40E_CORE_RESET_REQUESTED, &pf->state); + clear_bit(__I40E_CORE_RESET_REQUESTED, pf->state); } - if (test_bit(__I40E_GLOBAL_RESET_REQUESTED, &pf->state)) { + if (test_bit(__I40E_GLOBAL_RESET_REQUESTED, pf->state)) { reset_flags |= BIT(__I40E_GLOBAL_RESET_REQUESTED); - clear_bit(__I40E_GLOBAL_RESET_REQUESTED, &pf->state); + clear_bit(__I40E_GLOBAL_RESET_REQUESTED, pf->state); } - if (test_bit(__I40E_DOWN_REQUESTED, &pf->state)) { - reset_flags |= BIT(__I40E_DOWN_REQUESTED); - clear_bit(__I40E_DOWN_REQUESTED, &pf->state); + if (test_bit(__I40E_VSI_DOWN_REQUESTED, pf->state)) { + reset_flags |= BIT(__I40E_VSI_DOWN_REQUESTED); + clear_bit(__I40E_VSI_DOWN_REQUESTED, pf->state); } /* If there's a recovery already waiting, it takes * precedence before starting a new reset sequence. */ - if (test_bit(__I40E_RESET_INTR_RECEIVED, &pf->state)) { + if (test_bit(__I40E_RESET_INTR_RECEIVED, pf->state)) { i40e_prep_for_reset(pf, false); i40e_reset(pf); i40e_rebuild(pf, false, false); @@ -6416,8 +6416,8 @@ static void i40e_reset_subtask(struct i40e_pf *pf) /* If we're already down or resetting, just bail */ if (reset_flags && - !test_bit(__I40E_DOWN, &pf->state) && - !test_bit(__I40E_CONFIG_BUSY, &pf->state)) { + !test_bit(__I40E_VSI_DOWN, pf->state) && + !test_bit(__I40E_CONFIG_BUSY, pf->state)) { rtnl_lock(); i40e_do_reset(pf, reset_flags, true); rtnl_unlock(); @@ -6466,7 +6466,7 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf) u32 val; /* Do not run clean AQ when PF reset fails */ - if (test_bit(__I40E_RESET_FAILED, &pf->state)) + if (test_bit(__I40E_RESET_FAILED, pf->state)) return; /* check for error indications */ @@ -6570,7 +6570,7 @@ static void i40e_clean_adminq_subtask(struct i40e_pf *pf) } while (i++ < pf->adminq_work_limit); if (i < pf->adminq_work_limit) - clear_bit(__I40E_ADMINQ_EVENT_PENDING, &pf->state); + clear_bit(__I40E_ADMINQ_EVENT_PENDING, pf->state); /* re-enable Admin queue interrupt cause */ val = rd32(hw, I40E_PFINT_ICR0_ENA); @@ -6596,13 +6596,13 @@ static void i40e_verify_eeprom(struct i40e_pf *pf) if (err) { dev_info(&pf->pdev->dev, "eeprom check failed (%d), Tx/Rx traffic disabled\n", err); - set_bit(__I40E_BAD_EEPROM, &pf->state); + set_bit(__I40E_BAD_EEPROM, pf->state); } } - if (!err && test_bit(__I40E_BAD_EEPROM, &pf->state)) { + if (!err && test_bit(__I40E_BAD_EEPROM, pf->state)) { dev_info(&pf->pdev->dev, "eeprom check passed, Tx/Rx traffic enabled\n"); - clear_bit(__I40E_BAD_EEPROM, &pf->state); + clear_bit(__I40E_BAD_EEPROM, pf->state); } } @@ -6920,8 +6920,8 @@ static void i40e_prep_for_reset(struct i40e_pf *pf, bool lock_acquired) i40e_status ret = 0; u32 v; - clear_bit(__I40E_RESET_INTR_RECEIVED, &pf->state); - if (test_and_set_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state)) + clear_bit(__I40E_RESET_INTR_RECEIVED, pf->state); + if (test_and_set_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) return; if (i40e_check_asq_alive(&pf->hw)) i40e_vc_notify_reset(pf); @@ -6980,8 +6980,8 @@ static int i40e_reset(struct i40e_pf *pf) ret = i40e_pf_reset(hw); if (ret) { dev_info(&pf->pdev->dev, "PF reset failed, %d\n", ret); - set_bit(__I40E_RESET_FAILED, &pf->state); - clear_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state); + set_bit(__I40E_RESET_FAILED, pf->state); + clear_bit(__I40E_RESET_RECOVERY_PENDING, pf->state); } else { pf->pfr_count++; } @@ -7003,7 +7003,7 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired) u32 val; int v; - if (test_bit(__I40E_DOWN, &pf->state)) + if (test_bit(__I40E_VSI_DOWN, pf->state)) goto clear_recovery; dev_dbg(&pf->pdev->dev, "Rebuilding internal switch\n"); @@ -7017,7 +7017,7 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired) } /* re-verify the eeprom if we just had an EMP reset */ - if (test_and_clear_bit(__I40E_EMP_RESET_INTR_RECEIVED, &pf->state)) + if (test_and_clear_bit(__I40E_EMP_RESET_INTR_RECEIVED, pf->state)) i40e_verify_eeprom(pf); i40e_clear_pxe_mode(hw); @@ -7180,9 +7180,9 @@ end_unlock: if (!lock_acquired) rtnl_unlock(); end_core_reset: - clear_bit(__I40E_RESET_FAILED, &pf->state); + clear_bit(__I40E_RESET_FAILED, pf->state); clear_recovery: - clear_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state); + clear_bit(__I40E_RESET_RECOVERY_PENDING, pf->state); } /** @@ -7235,7 +7235,7 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf) u32 reg; int i; - if (!test_bit(__I40E_MDD_EVENT_PENDING, &pf->state)) + if (!test_bit(__I40E_MDD_EVENT_PENDING, pf->state)) return; /* find what triggered the MDD event */ @@ -7287,7 +7287,7 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf) } /* Queue belongs to the PF, initiate a reset */ if (pf_mdd_detected) { - set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); + set_bit(__I40E_PF_RESET_REQUESTED, pf->state); i40e_service_event_schedule(pf); } } @@ -7321,7 +7321,7 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf) } /* re-enable mdd interrupt cause */ - clear_bit(__I40E_MDD_EVENT_PENDING, &pf->state); + clear_bit(__I40E_MDD_EVENT_PENDING, pf->state); reg = rd32(hw, I40E_PFINT_ICR0_ENA); reg |= I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK; wr32(hw, I40E_PFINT_ICR0_ENA, reg); @@ -7399,11 +7399,10 @@ static void i40e_service_task(struct work_struct *work) unsigned long start_time = jiffies; /* don't bother with service tasks if a reset is in progress */ - if (test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state)) { + if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) return; - } - if (test_and_set_bit(__I40E_SERVICE_SCHED, &pf->state)) + if (test_and_set_bit(__I40E_SERVICE_SCHED, pf->state)) return; i40e_detect_recover_hung(pf); @@ -7431,16 +7430,16 @@ static void i40e_service_task(struct work_struct *work) /* flush memory to make sure state is correct before next watchdog */ smp_mb__before_atomic(); - clear_bit(__I40E_SERVICE_SCHED, &pf->state); + clear_bit(__I40E_SERVICE_SCHED, pf->state); /* If the tasks have taken longer than one timer cycle or there * is more work to be done, reschedule the service task now * rather than wait for the timer to tick again. */ if (time_after(jiffies, (start_time + pf->service_timer_period)) || - test_bit(__I40E_ADMINQ_EVENT_PENDING, &pf->state) || - test_bit(__I40E_MDD_EVENT_PENDING, &pf->state) || - test_bit(__I40E_VFLR_EVENT_PENDING, &pf->state)) + test_bit(__I40E_ADMINQ_EVENT_PENDING, pf->state) || + test_bit(__I40E_MDD_EVENT_PENDING, pf->state) || + test_bit(__I40E_VFLR_EVENT_PENDING, pf->state)) i40e_service_event_schedule(pf); } @@ -7589,7 +7588,7 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type) } vsi->type = type; vsi->back = pf; - set_bit(__I40E_VSI_DOWN, &vsi->state); + set_bit(__I40E_VSI_DOWN, vsi->state); vsi->flags = 0; vsi->idx = vsi_idx; vsi->int_rate_limit = 0; @@ -8171,7 +8170,7 @@ static int i40e_setup_misc_vector(struct i40e_pf *pf) /* Only request the irq if this is the first time through, and * not when we're rebuilding after a Reset */ - if (!test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state)) { + if (!test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state)) { err = request_irq(pf->msix_entries[0].vector, i40e_intr, 0, pf->int_name, pf); if (err) { @@ -9716,7 +9715,7 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) } vsi->active_filters = 0; - clear_bit(__I40E_VSI_OVERFLOW_PROMISC, &vsi->state); + clear_bit(__I40E_VSI_OVERFLOW_PROMISC, vsi->state); spin_lock_bh(&vsi->mac_filter_hash_lock); /* If macvlan filters already exist, force them to get loaded */ hash_for_each_safe(vsi->mac_filter_hash, bkt, h, f, hlist) { @@ -9769,7 +9768,7 @@ int i40e_vsi_release(struct i40e_vsi *vsi) return -ENODEV; } if (vsi == pf->vsi[pf->lan_vsi] && - !test_bit(__I40E_DOWN, &pf->state)) { + !test_bit(__I40E_VSI_DOWN, pf->state)) { dev_info(&pf->pdev->dev, "Can't remove PF VSI\n"); return -ENODEV; } @@ -11005,7 +11004,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } pf->next_vsi = 0; pf->pdev = pdev; - set_bit(__I40E_DOWN, &pf->state); + set_bit(__I40E_VSI_DOWN, pf->state); hw = &pf->hw; hw->back = pf; @@ -11184,7 +11183,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pf->service_timer_period = HZ; INIT_WORK(&pf->service_task, i40e_service_task); - clear_bit(__I40E_SERVICE_SCHED, &pf->state); + clear_bit(__I40E_SERVICE_SCHED, pf->state); /* NVM bit on means WoL disabled for the port */ i40e_read_nvm_word(hw, I40E_SR_NVM_WAKE_ON_LAN, &wol_nvm_bits); @@ -11222,7 +11221,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* prep for VF support */ if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) && (pf->flags & I40E_FLAG_MSIX_ENABLED) && - !test_bit(__I40E_BAD_EEPROM, &pf->state)) { + !test_bit(__I40E_BAD_EEPROM, pf->state)) { if (pci_num_vf(pdev)) pf->flags |= I40E_FLAG_VEB_MODE_ENABLED; } @@ -11295,7 +11294,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * before setting up the misc vector or we get a race and the vector * ends up disabled forever. */ - clear_bit(__I40E_DOWN, &pf->state); + clear_bit(__I40E_VSI_DOWN, pf->state); /* In case of MSIX we are going to setup the misc vector right here * to handle admin queue events etc. In case of legacy and MSI @@ -11315,7 +11314,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* prep for VF support */ if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) && (pf->flags & I40E_FLAG_MSIX_ENABLED) && - !test_bit(__I40E_BAD_EEPROM, &pf->state)) { + !test_bit(__I40E_BAD_EEPROM, pf->state)) { /* disable link interrupts for VFs */ val = rd32(hw, I40E_PFGEN_PORTMDIO_NUM); val &= ~I40E_PFGEN_PORTMDIO_NUM_VFLINK_STAT_ENA_MASK; @@ -11450,7 +11449,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Unwind what we've done if something failed in the setup */ err_vsis: - set_bit(__I40E_DOWN, &pf->state); + set_bit(__I40E_VSI_DOWN, pf->state); i40e_clear_interrupt_scheme(pf); kfree(pf->vsi); err_switch_setup: @@ -11501,8 +11500,8 @@ static void i40e_remove(struct pci_dev *pdev) i40e_write_rx_ctl(hw, I40E_PFQF_HENA(1), 0); /* no more scheduling of any task */ - set_bit(__I40E_SUSPENDED, &pf->state); - set_bit(__I40E_DOWN, &pf->state); + set_bit(__I40E_SUSPENDED, pf->state); + set_bit(__I40E_VSI_DOWN, pf->state); if (pf->service_timer.data) del_timer_sync(&pf->service_timer); if (pf->service_task.func) @@ -11610,7 +11609,7 @@ static pci_ers_result_t i40e_pci_error_detected(struct pci_dev *pdev, } /* shutdown all operations */ - if (!test_bit(__I40E_SUSPENDED, &pf->state)) { + if (!test_bit(__I40E_SUSPENDED, pf->state)) { rtnl_lock(); i40e_prep_for_reset(pf, true); rtnl_unlock(); @@ -11677,7 +11676,7 @@ static void i40e_pci_error_resume(struct pci_dev *pdev) struct i40e_pf *pf = pci_get_drvdata(pdev); dev_dbg(&pdev->dev, "%s\n", __func__); - if (test_bit(__I40E_SUSPENDED, &pf->state)) + if (test_bit(__I40E_SUSPENDED, pf->state)) return; rtnl_lock(); @@ -11741,8 +11740,8 @@ static void i40e_shutdown(struct pci_dev *pdev) struct i40e_pf *pf = pci_get_drvdata(pdev); struct i40e_hw *hw = &pf->hw; - set_bit(__I40E_SUSPENDED, &pf->state); - set_bit(__I40E_DOWN, &pf->state); + set_bit(__I40E_SUSPENDED, pf->state); + set_bit(__I40E_VSI_DOWN, pf->state); rtnl_lock(); i40e_prep_for_reset(pf, true); rtnl_unlock(); @@ -11790,8 +11789,8 @@ static int i40e_suspend(struct pci_dev *pdev, pm_message_t state) struct i40e_hw *hw = &pf->hw; int retval = 0; - set_bit(__I40E_SUSPENDED, &pf->state); - set_bit(__I40E_DOWN, &pf->state); + set_bit(__I40E_SUSPENDED, pf->state); + set_bit(__I40E_VSI_DOWN, pf->state); if (pf->wol_en && (pf->flags & I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE)) i40e_enable_mc_magic_wake(pf); @@ -11842,8 +11841,8 @@ static int i40e_resume(struct pci_dev *pdev) pci_wake_from_d3(pdev, false); /* handling the reset will rebuild the device state */ - if (test_and_clear_bit(__I40E_SUSPENDED, &pf->state)) { - clear_bit(__I40E_DOWN, &pf->state); + if (test_and_clear_bit(__I40E_SUSPENDED, pf->state)) { + clear_bit(__I40E_VSI_DOWN, pf->state); rtnl_lock(); i40e_reset_and_rebuild(pf, false, true); rtnl_unlock(); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 2caee35528fa..18c1cc08da97 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -358,7 +358,7 @@ void i40e_ptp_tx_hwtstamp(struct i40e_pf *pf) skb_tstamp_tx(pf->ptp_tx_skb, &shhwtstamps); dev_kfree_skb_any(pf->ptp_tx_skb); pf->ptp_tx_skb = NULL; - clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, &pf->state); + clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state); } /** @@ -768,7 +768,7 @@ void i40e_ptp_stop(struct i40e_pf *pf) if (pf->ptp_tx_skb) { dev_kfree_skb_any(pf->ptp_tx_skb); pf->ptp_tx_skb = NULL; - clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, &pf->state); + clear_bit_unlock(__I40E_PTP_TX_IN_PROGRESS, pf->state); } if (pf->ptp_clock) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index bbd21cbf3e4d..6e677a235fe4 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -589,7 +589,7 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, * progress do nothing, once flush is complete the state will * be cleared. */ - if (test_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state)) + if (test_bit(__I40E_FD_FLUSH_REQUESTED, pf->state)) return; pf->fd_add_err++; @@ -599,7 +599,7 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, if ((rx_desc->wb.qword0.hi_dword.fd_id == 0) && (pf->hw_disabled_flags & I40E_FLAG_FD_SB_ENABLED)) { pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; - set_bit(__I40E_FD_FLUSH_REQUESTED, &pf->state); + set_bit(__I40E_FD_FLUSH_REQUESTED, pf->state); } /* filter programming failed most likely due to table full */ @@ -850,7 +850,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, if (budget && ((j / WB_STRIDE) == 0) && (j > 0) && - !test_bit(__I40E_VSI_DOWN, &vsi->state) && + !test_bit(__I40E_VSI_DOWN, vsi->state) && (I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) tx_ring->arm_wb = true; } @@ -868,7 +868,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, smp_mb(); if (__netif_subqueue_stopped(tx_ring->netdev, tx_ring->queue_index) && - !test_bit(__I40E_VSI_DOWN, &vsi->state)) { + !test_bit(__I40E_VSI_DOWN, vsi->state)) { netif_wake_subqueue(tx_ring->netdev, tx_ring->queue_index); ++tx_ring->tx_stats.restart_queue; @@ -2179,7 +2179,7 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi, } enable_int: - if (!test_bit(__I40E_VSI_DOWN, &vsi->state)) + if (!test_bit(__I40E_VSI_DOWN, vsi->state)) wr32(hw, INTREG(vector - 1), txval); if (q_vector->itr_countdown) @@ -2208,7 +2208,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) int budget_per_ring; int work_done = 0; - if (test_bit(__I40E_VSI_DOWN, &vsi->state)) { + if (test_bit(__I40E_VSI_DOWN, vsi->state)) { napi_complete(napi); return 0; } @@ -2634,7 +2634,7 @@ static int i40e_tsyn(struct i40e_ring *tx_ring, struct sk_buff *skb, return 0; if (pf->ptp_tx && - !test_and_set_bit_lock(__I40E_PTP_TX_IN_PROGRESS, &pf->state)) { + !test_and_set_bit_lock(__I40E_PTP_TX_IN_PROGRESS, pf->state)) { skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; pf->ptp_tx_skb = skb_get(skb); } else { diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 29f53f032c3c..95c23fbaa211 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1036,7 +1036,7 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) int i; /* If VFs have been disabled, there is no need to reset */ - if (test_and_set_bit(__I40E_VF_DISABLE, &pf->state)) + if (test_and_set_bit(__I40E_VF_DISABLE, pf->state)) return; i40e_trigger_vf_reset(vf, flr); @@ -1073,7 +1073,7 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) i40e_cleanup_reset_vf(vf); i40e_flush(hw); - clear_bit(__I40E_VF_DISABLE, &pf->state); + clear_bit(__I40E_VF_DISABLE, pf->state); } /** @@ -1098,7 +1098,7 @@ void i40e_reset_all_vfs(struct i40e_pf *pf, bool flr) return; /* If VFs have been disabled, there is no need to reset */ - if (test_and_set_bit(__I40E_VF_DISABLE, &pf->state)) + if (test_and_set_bit(__I40E_VF_DISABLE, pf->state)) return; /* Begin reset on all VFs at once */ @@ -1173,7 +1173,7 @@ void i40e_reset_all_vfs(struct i40e_pf *pf, bool flr) i40e_cleanup_reset_vf(&pf->vf[v]); i40e_flush(hw); - clear_bit(__I40E_VF_DISABLE, &pf->state); + clear_bit(__I40E_VF_DISABLE, pf->state); } /** @@ -1190,7 +1190,7 @@ void i40e_free_vfs(struct i40e_pf *pf) if (!pf->vf) return; - while (test_and_set_bit(__I40E_VF_DISABLE, &pf->state)) + while (test_and_set_bit(__I40E_VF_DISABLE, pf->state)) usleep_range(1000, 2000); i40e_notify_client_of_vf_enable(pf, 0); @@ -1246,7 +1246,7 @@ void i40e_free_vfs(struct i40e_pf *pf) wr32(hw, I40E_GLGEN_VFLRSTAT(reg_idx), BIT(bit_idx)); } } - clear_bit(__I40E_VF_DISABLE, &pf->state); + clear_bit(__I40E_VF_DISABLE, pf->state); } #ifdef CONFIG_PCI_IOV @@ -1326,7 +1326,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 (test_bit(__I40E_TESTING, &pf->state)) { + 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; @@ -2818,7 +2818,7 @@ int i40e_vc_process_vflr_event(struct i40e_pf *pf) struct i40e_vf *vf; int vf_id; - if (!test_bit(__I40E_VFLR_EVENT_PENDING, &pf->state)) + if (!test_bit(__I40E_VFLR_EVENT_PENDING, pf->state)) return 0; /* Re-enable the VFLR interrupt cause here, before looking for which @@ -2831,7 +2831,7 @@ int i40e_vc_process_vflr_event(struct i40e_pf *pf) wr32(hw, I40E_PFINT_ICR0_ENA, reg); i40e_flush(hw); - clear_bit(__I40E_VFLR_EVENT_PENDING, &pf->state); + clear_bit(__I40E_VFLR_EVENT_PENDING, pf->state); for (vf_id = 0; vf_id < pf->num_alloc_vfs; vf_id++) { reg_idx = (hw->func_caps.vf_base_id + vf_id) / 32; bit_idx = (hw->func_caps.vf_base_id + vf_id) % 32; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index c95ee0a8950f..dfe241a12ad0 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -266,7 +266,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, if (budget && ((j / WB_STRIDE) == 0) && (j > 0) && - !test_bit(__I40E_VSI_DOWN, &vsi->state) && + !test_bit(__I40E_VSI_DOWN, vsi->state) && (I40E_DESC_UNUSED(tx_ring) != tx_ring->count)) tx_ring->arm_wb = true; } @@ -284,7 +284,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, smp_mb(); if (__netif_subqueue_stopped(tx_ring->netdev, tx_ring->queue_index) && - !test_bit(__I40E_VSI_DOWN, &vsi->state)) { + !test_bit(__I40E_VSI_DOWN, vsi->state)) { netif_wake_subqueue(tx_ring->netdev, tx_ring->queue_index); ++tx_ring->tx_stats.restart_queue; @@ -1508,7 +1508,7 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi, } enable_int: - if (!test_bit(__I40E_VSI_DOWN, &vsi->state)) + if (!test_bit(__I40E_VSI_DOWN, vsi->state)) wr32(hw, INTREG(vector - 1), txval); if (q_vector->itr_countdown) @@ -1537,7 +1537,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) int budget_per_ring; int work_done = 0; - if (test_bit(__I40E_VSI_DOWN, &vsi->state)) { + if (test_bit(__I40E_VSI_DOWN, vsi->state)) { napi_complete(napi); return 0; } diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index a56a6e54d907..4681c63ee7e3 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -52,6 +52,8 @@ /* VSI state flags shared with common code */ enum i40evf_vsi_state_t { __I40E_VSI_DOWN, + /* This must be last as it determines the size of the BITMAP */ + __I40E_VSI_STATE_SIZE__, }; /* dummy struct to make common code less painful */ @@ -61,7 +63,7 @@ struct i40e_vsi { unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; u16 seid; u16 id; - unsigned long state; + DECLARE_BITMAP(state, __I40E_VSI_STATE_SIZE__); int base_vector; u16 work_limit; u16 qs_handle; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index b453d05c2a30..8e6276d864e6 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -497,7 +497,7 @@ static void i40evf_netpoll(struct net_device *netdev) int i; /* if interface is down do nothing */ - if (test_bit(__I40E_VSI_DOWN, &adapter->vsi.state)) + if (test_bit(__I40E_VSI_DOWN, adapter->vsi.state)) return; for (i = 0; i < q_vectors; i++) @@ -1087,7 +1087,7 @@ static void i40evf_configure(struct i40evf_adapter *adapter) static void i40evf_up_complete(struct i40evf_adapter *adapter) { adapter->state = __I40EVF_RUNNING; - clear_bit(__I40E_VSI_DOWN, &adapter->vsi.state); + clear_bit(__I40E_VSI_DOWN, adapter->vsi.state); i40evf_napi_enable_all(adapter); @@ -1753,7 +1753,7 @@ static void i40evf_disable_vf(struct i40evf_adapter *adapter) adapter->flags |= I40EVF_FLAG_PF_COMMS_FAILED; if (netif_running(adapter->netdev)) { - set_bit(__I40E_VSI_DOWN, &adapter->vsi.state); + set_bit(__I40E_VSI_DOWN, adapter->vsi.state); netif_carrier_off(adapter->netdev); netif_tx_disable(adapter->netdev); adapter->link_up = false; @@ -2233,7 +2233,7 @@ static int i40evf_close(struct net_device *netdev) return 0; - set_bit(__I40E_VSI_DOWN, &adapter->vsi.state); + set_bit(__I40E_VSI_DOWN, adapter->vsi.state); if (CLIENT_ENABLED(adapter)) adapter->flags |= I40EVF_FLAG_CLIENT_NEEDS_CLOSE; @@ -2674,7 +2674,7 @@ static void i40evf_init_task(struct work_struct *work) dev_info(&pdev->dev, "GRO is enabled\n"); adapter->state = __I40EVF_DOWN; - set_bit(__I40E_VSI_DOWN, &adapter->vsi.state); + set_bit(__I40E_VSI_DOWN, adapter->vsi.state); i40evf_misc_irq_enable(adapter); adapter->rss_key = kzalloc(adapter->rss_key_size, GFP_KERNEL); -- cgit From 789f38ca70e0b2848472aaf5f278aa3deabd4a4e Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 19 Apr 2017 09:25:56 -0400 Subject: i40evf: remove needless min_t() on num_online_cpus()*2 We already set pairs to the value of adapter->num_active_queues. This value is limited by vsi_res->num_queue_pairs and num_online_cpus(). This means that pairs by definition is already smaller than num_online_cpus()*2, so we don't even need to bother with this check. Lets just remove it and update the comment. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 8e6276d864e6..89035ee01679 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1271,13 +1271,13 @@ static int i40evf_set_interrupt_capability(struct i40evf_adapter *adapter) } pairs = adapter->num_active_queues; - /* It's easy to be greedy for MSI-X vectors, but it really - * doesn't do us much good if we have a lot more vectors - * than CPU's. So let's be conservative and only ask for - * (roughly) twice the number of vectors as there are CPU's. + /* It's easy to be greedy for MSI-X vectors, but it really doesn't do + * us much good if we have more vectors than CPUs. However, we already + * limit the total number of queues by the number of CPUs so we do not + * need any further limiting here. */ - v_budget = min_t(int, pairs, (int)(num_online_cpus() * 2)) + NONQ_VECS; - v_budget = min_t(int, v_budget, (int)adapter->vf_res->max_vectors); + v_budget = min_t(int, pairs + NONQ_VECS, + (int)adapter->vf_res->max_vectors); adapter->msix_entries = kcalloc(v_budget, sizeof(struct msix_entry), GFP_KERNEL); -- cgit From 47994c119a36e28e1779efabc92d6ab5329a6f75 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 19 Apr 2017 09:25:57 -0400 Subject: i40e: remove hw_disabled_flags in favor of using separate flag bits The hw_disabled_flags field was added as a way of signifying that a feature was automatically or temporarily disabled. However, we actually only use this for FDir features. Replace its use with new _AUTO_DISABLED flags instead. This is more readable, because you aren't setting an *_ENABLED flag to *disable* the feature. Additionally, clean up a few areas where we used these bits. First, we don't really need to set the auto-disable flag for ATR if we're fully disabling the feature via ethtool. Second, we should always clear the auto-disable bits in case they somehow got set when the feature was disabled. However, avoid displaying a message that we've re-enabled the feature. Third, we shouldn't be re-enabling ATR in the SB ntuple add flow, because it might have been disabled due to space constraints. Instead, we should just wait for the fdir_check_and_reenable to be called by the watchdog. Overall, this change allows us to simplify some code by removing an extra field we didn't need, and the result should make it more clear as to what we're actually doing with these flags. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 9 +---- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 6 +-- drivers/net/ethernet/intel/i40e/i40e_main.c | 53 +++++++++++++------------- drivers/net/ethernet/intel/i40e/i40e_txrx.c | 22 ++++------- 4 files changed, 38 insertions(+), 52 deletions(-) diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index 6eb21abdc60e..cdde3cc28fb5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -408,6 +408,8 @@ struct i40e_pf { #define I40E_FLAG_DCB_ENABLED BIT_ULL(20) #define I40E_FLAG_FD_SB_ENABLED BIT_ULL(21) #define I40E_FLAG_FD_ATR_ENABLED BIT_ULL(22) +#define I40E_FLAG_FD_SB_AUTO_DISABLED BIT_ULL(23) +#define I40E_FLAG_FD_ATR_AUTO_DISABLED BIT_ULL(24) #define I40E_FLAG_PTP BIT_ULL(25) #define I40E_FLAG_MFP_ENABLED BIT_ULL(26) #define I40E_FLAG_UDP_FILTER_SYNC BIT_ULL(27) @@ -440,13 +442,6 @@ struct i40e_pf { #define I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE BIT_ULL(57) #define I40E_FLAG_LEGACY_RX BIT_ULL(58) - /* Tracks features that are disabled due to hw limitations. - * If a bit is set here, it means that the corresponding - * bit in the 'flags' field is cleared i.e that feature - * is disabled - */ - u64 hw_disabled_flags; - struct i40e_client_instance *cinst; bool stat_offsets_loaded; struct i40e_hw_port_stats stats; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index b1064c6468c2..7a8eb486b9ea 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -3643,7 +3643,7 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi, if (!(pf->flags & I40E_FLAG_FD_SB_ENABLED)) return -EOPNOTSUPP; - if (pf->hw_disabled_flags & I40E_FLAG_FD_SB_ENABLED) + if (pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED) return -ENOSPC; if (test_bit(__I40E_RESET_RECOVERY_PENDING, pf->state) || @@ -4086,12 +4086,12 @@ flags_complete: /* Flush current ATR settings if ATR was disabled */ if ((changed_flags & I40E_FLAG_FD_ATR_ENABLED) && !(pf->flags & I40E_FLAG_FD_ATR_ENABLED)) { - pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; + pf->flags |= I40E_FLAG_FD_ATR_AUTO_DISABLED; set_bit(__I40E_FD_FLUSH_REQUESTED, pf->state); } /* Only allow ATR evict on hardware that is capable of handling it */ - if (pf->hw_disabled_flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) + if (pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) pf->flags &= ~I40E_FLAG_HW_ATR_EVICT_CAPABLE; if (changed_flags & I40E_FLAG_TRUE_PROMISC_SUPPORT) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 38772e49bb84..d5c9c9e06ff5 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -1050,13 +1050,13 @@ static void i40e_update_pf_stats(struct i40e_pf *pf) &osd->rx_lpi_count, &nsd->rx_lpi_count); if (pf->flags & I40E_FLAG_FD_SB_ENABLED && - !(pf->hw_disabled_flags & I40E_FLAG_FD_SB_ENABLED)) + !(pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED)) nsd->fd_sb_status = true; else nsd->fd_sb_status = false; if (pf->flags & I40E_FLAG_FD_ATR_ENABLED && - !(pf->hw_disabled_flags & I40E_FLAG_FD_ATR_ENABLED)) + !(pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED)) nsd->fd_atr_status = true; else nsd->fd_atr_status = false; @@ -6078,31 +6078,30 @@ void i40e_fdir_check_and_reenable(struct i40e_pf *pf) if (test_bit(__I40E_FD_FLUSH_REQUESTED, pf->state)) return; - /* Check if, FD SB or ATR was auto disabled and if there is enough room - * to re-enable - */ + /* Check if we have enough room to re-enable FDir SB capability. */ fcnt_prog = i40e_get_global_fd_count(pf); fcnt_avail = pf->fdir_pf_filter_count; if ((fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM)) || (pf->fd_add_err == 0) || (i40e_get_current_atr_cnt(pf) < pf->fd_atr_cnt)) { - if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) && - (pf->hw_disabled_flags & I40E_FLAG_FD_SB_ENABLED)) { - pf->hw_disabled_flags &= ~I40E_FLAG_FD_SB_ENABLED; - if (I40E_DEBUG_FD & pf->hw.debug_mask) + if (pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED) { + pf->flags &= ~I40E_FLAG_FD_SB_AUTO_DISABLED; + if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) && + (I40E_DEBUG_FD & pf->hw.debug_mask)) dev_info(&pf->pdev->dev, "FD Sideband/ntuple is being enabled since we have space in the table now\n"); } } - /* Wait for some more space to be available to turn on ATR. We also - * must check that no existing ntuple rules for TCP are in effect + /* We should wait for even more space before re-enabling ATR. + * Additionally, we cannot enable ATR as long as we still have TCP SB + * rules active. */ - if (fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM * 2)) { - if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && - (pf->hw_disabled_flags & I40E_FLAG_FD_ATR_ENABLED) && - (pf->fd_tcp4_filter_cnt == 0)) { - pf->hw_disabled_flags &= ~I40E_FLAG_FD_ATR_ENABLED; - if (I40E_DEBUG_FD & pf->hw.debug_mask) + if ((fcnt_prog < (fcnt_avail - I40E_FDIR_BUFFER_HEAD_ROOM_FOR_ATR)) && + (pf->fd_tcp4_filter_cnt == 0)) { + if (pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED) { + pf->flags &= ~I40E_FLAG_FD_ATR_AUTO_DISABLED; + if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && + (I40E_DEBUG_FD & pf->hw.debug_mask)) dev_info(&pf->pdev->dev, "ATR is being enabled since we have space in the table and there are no conflicting ntuple rules\n"); } } @@ -6153,7 +6152,7 @@ static void i40e_fdir_flush_and_replay(struct i40e_pf *pf) } pf->fd_flush_timestamp = jiffies; - pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; + pf->flags |= I40E_FLAG_FD_ATR_AUTO_DISABLED; /* flush all filters */ wr32(&pf->hw, I40E_PFQF_CTL_1, I40E_PFQF_CTL_1_CLEARFDTABLE_MASK); @@ -6173,7 +6172,7 @@ static void i40e_fdir_flush_and_replay(struct i40e_pf *pf) /* replay sideband filters */ i40e_fdir_filter_restore(pf->vsi[pf->lan_vsi]); if (!disable_atr && !pf->fd_tcp4_filter_cnt) - pf->hw_disabled_flags &= ~I40E_FLAG_FD_ATR_ENABLED; + pf->flags &= ~I40E_FLAG_FD_ATR_AUTO_DISABLED; 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"); @@ -8822,9 +8821,9 @@ static int i40e_sw_init(struct i40e_pf *pf) (pf->hw.aq.api_min_ver > 4))) { /* Supported in FW API version higher than 1.4 */ pf->flags |= I40E_FLAG_GENEVE_OFFLOAD_CAPABLE; - pf->hw_disabled_flags = I40E_FLAG_HW_ATR_EVICT_CAPABLE; + pf->flags = I40E_FLAG_HW_ATR_EVICT_CAPABLE; } else { - pf->hw_disabled_flags = I40E_FLAG_HW_ATR_EVICT_CAPABLE; + pf->flags = I40E_FLAG_HW_ATR_EVICT_CAPABLE; } pf->eeprom_version = 0xDEAD; @@ -8884,16 +8883,16 @@ bool i40e_set_ntuple(struct i40e_pf *pf, netdev_features_t features) need_reset = true; i40e_fdir_filter_exit(pf); } - pf->flags &= ~I40E_FLAG_FD_SB_ENABLED; - pf->hw_disabled_flags &= ~I40E_FLAG_FD_SB_ENABLED; + pf->flags &= ~(I40E_FLAG_FD_SB_ENABLED | + I40E_FLAG_FD_SB_AUTO_DISABLED); /* reset fd counters */ pf->fd_add_err = 0; pf->fd_atr_cnt = 0; /* if ATR was auto disabled it can be re-enabled. */ - if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && - (pf->hw_disabled_flags & I40E_FLAG_FD_ATR_ENABLED)) { - pf->hw_disabled_flags &= ~I40E_FLAG_FD_ATR_ENABLED; - if (I40E_DEBUG_FD & pf->hw.debug_mask) + if (pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED) { + pf->flags &= ~I40E_FLAG_FD_ATR_AUTO_DISABLED; + if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && + (I40E_DEBUG_FD & pf->hw.debug_mask)) dev_info(&pf->pdev->dev, "ATR re-enabled.\n"); } } diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 6e677a235fe4..29321a6167a6 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -333,15 +333,9 @@ static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi, if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && I40E_DEBUG_FD & pf->hw.debug_mask) dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n"); - pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; + pf->flags |= I40E_FLAG_FD_ATR_AUTO_DISABLED; } else { pf->fd_tcp4_filter_cnt--; - if (pf->fd_tcp4_filter_cnt == 0) { - if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) && - I40E_DEBUG_FD & pf->hw.debug_mask) - dev_info(&pf->pdev->dev, "ATR re-enabled due to no sideband TCP/IPv4 rules\n"); - pf->hw_disabled_flags &= ~I40E_FLAG_FD_ATR_ENABLED; - } } return 0; @@ -597,8 +591,8 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, pf->fd_atr_cnt = i40e_get_current_atr_cnt(pf); if ((rx_desc->wb.qword0.hi_dword.fd_id == 0) && - (pf->hw_disabled_flags & I40E_FLAG_FD_SB_ENABLED)) { - pf->hw_disabled_flags |= I40E_FLAG_FD_ATR_ENABLED; + pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED) { + pf->flags |= I40E_FLAG_FD_ATR_AUTO_DISABLED; set_bit(__I40E_FD_FLUSH_REQUESTED, pf->state); } @@ -611,12 +605,10 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, */ if (fcnt_prog >= (fcnt_avail - I40E_FDIR_BUFFER_FULL_MARGIN)) { if ((pf->flags & I40E_FLAG_FD_SB_ENABLED) && - !(pf->hw_disabled_flags & - I40E_FLAG_FD_SB_ENABLED)) { + !(pf->flags & I40E_FLAG_FD_SB_AUTO_DISABLED)) { + pf->flags |= I40E_FLAG_FD_SB_AUTO_DISABLED; if (I40E_DEBUG_FD & pf->hw.debug_mask) dev_warn(&pdev->dev, "FD filter space full, new ntuple rules will not be added\n"); - pf->hw_disabled_flags |= - I40E_FLAG_FD_SB_ENABLED; } } } else if (error == BIT(I40E_RX_PROG_STATUS_DESC_NO_FD_ENTRY_SHIFT)) { @@ -2312,7 +2304,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, if (!(pf->flags & I40E_FLAG_FD_ATR_ENABLED)) return; - if ((pf->hw_disabled_flags & I40E_FLAG_FD_ATR_ENABLED)) + if (pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED) return; /* if sampling is disabled do nothing */ @@ -2346,7 +2338,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, th = (struct tcphdr *)(hdr.network + hlen); /* Due to lack of space, no more new filters can be programmed */ - if (th->syn && (pf->hw_disabled_flags & I40E_FLAG_FD_ATR_ENABLED)) + if (th->syn && (pf->flags & I40E_FLAG_FD_ATR_AUTO_DISABLED)) return; if (pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) { /* HW ATR eviction will take care of removing filters on FIN -- cgit From 707636c6481696c3b73209c9a7f8c482f0748373 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 19 Apr 2017 09:25:58 -0400 Subject: i40evf: remove I40E_FLAG_FDIR_ATR_ENABLED The flag used by the common code and PF code is I40E_FLAG_FD_ATR_ENABLED, not *FDIR*. It turns out none of the txrx code actually shared with the VF driver actually checks the ATR flag. This is made even more obvious by the typo in the VF header file. Let's just remove the flag from the VF driver since it's not needed. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 4681c63ee7e3..b8ada6d8d890 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -223,7 +223,6 @@ struct i40evf_adapter { #define I40EVF_FLAG_ALLMULTI_ON BIT(19) #define I40EVF_FLAG_LEGACY_RX BIT(20) /* duplicates for common code */ -#define I40E_FLAG_FDIR_ATR_ENABLED 0 #define I40E_FLAG_DCB_ENABLED 0 #define I40E_FLAG_RX_CSUM_ENABLED I40EVF_FLAG_RX_CSUM_ENABLED #define I40E_FLAG_WB_ON_ITR_CAPABLE I40EVF_FLAG_WB_ON_ITR_CAPABLE -- cgit From 283aeafe6bf06af48068478eaf332f7a227e9af4 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 19 Apr 2017 09:25:59 -0400 Subject: i40evf: allocate queues before we setup the interrupts and q_vectors This matches the ordering of how we free stuff during reset and remove. It also makes logical sense because we set the interrupts based on the number of queues. Currently this doesn't really matter in practice. However a future patch moves the assignment of num_active_queues into i40evf_alloc_queues, which is required by i40evf_set_interrupt_capability. Signed-off-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 89035ee01679..445a97a57853 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -1508,6 +1508,13 @@ int i40evf_init_interrupt_scheme(struct i40evf_adapter *adapter) { int err; + err = i40evf_alloc_queues(adapter); + if (err) { + dev_err(&adapter->pdev->dev, + "Unable to allocate memory for queues\n"); + goto err_alloc_queues; + } + rtnl_lock(); err = i40evf_set_interrupt_capability(adapter); rtnl_unlock(); @@ -1524,23 +1531,16 @@ int i40evf_init_interrupt_scheme(struct i40evf_adapter *adapter) goto err_alloc_q_vectors; } - err = i40evf_alloc_queues(adapter); - if (err) { - dev_err(&adapter->pdev->dev, - "Unable to allocate memory for queues\n"); - goto err_alloc_queues; - } - dev_info(&adapter->pdev->dev, "Multiqueue %s: Queue pair count = %u", (adapter->num_active_queues > 1) ? "Enabled" : "Disabled", adapter->num_active_queues); return 0; -err_alloc_queues: - i40evf_free_q_vectors(adapter); err_alloc_q_vectors: i40evf_reset_interrupt_capability(adapter); err_set_interrupt: + i40evf_free_queues(adapter); +err_alloc_queues: return err; } -- cgit From 3dfc3eb581645bc503c7940861f494a0d75615da Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 19 Apr 2017 19:29:48 +0200 Subject: i40evf: hide unused variable On architectures with larger pages, we get a warning about an unused variable: drivers/net/ethernet/intel/i40evf/i40evf_main.c: In function 'i40evf_configure_rx': drivers/net/ethernet/intel/i40evf/i40evf_main.c:690:21: error: unused variable 'netdev' [-Werror=unused-variable] This moves the declaration into the #ifdef to avoid the warning. Fixes: dab86afdbbd1 ("i40e/i40evf: Change the way we limit the maximum frame size for Rx") Signed-off-by: Arnd Bergmann Acked-by: Jacob Keller Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40evf/i40evf_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 445a97a57853..ea110a730e16 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -694,13 +694,14 @@ static void i40evf_configure_tx(struct i40evf_adapter *adapter) static void i40evf_configure_rx(struct i40evf_adapter *adapter) { unsigned int rx_buf_len = I40E_RXBUFFER_2048; - struct net_device *netdev = adapter->netdev; struct i40e_hw *hw = &adapter->hw; int i; /* Legacy Rx will always default to a 2048 buffer size. */ #if (PAGE_SIZE < 8192) if (!(adapter->flags & I40EVF_FLAG_LEGACY_RX)) { + struct net_device *netdev = adapter->netdev; + /* For jumbo frames on systems with 4K pages we have to use * an order 1 page, so we might as well increase the size * of our Rx buffer to make better use of the available space -- cgit From 10ed1e0bd144b5c3a92ca0c8fd06eb80b80649e5 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Tue, 26 Jul 2016 14:25:35 -0400 Subject: e1000e: fix PTP on e1000_pch_lpt variants I've got reports that the Intel I-218V NIC in Intel NUC5i5RYH systems used as a PTP slave experiences random ~10 hour clock jumps, which are resolved if the same workaround for the 82574 and 82583 is employed, so set the appropriate flag2 in e1000_pch_lpt_info too. Reported-by: Rupesh Patel Signed-off-by: Jarod Wilson Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/ich8lan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index f3aaca743ea3..72add037f07e 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -5865,7 +5865,8 @@ const struct e1000_info e1000_pch2_info = { | FLAG_HAS_JUMBO_FRAMES | FLAG_APME_IN_WUC, .flags2 = FLAG2_HAS_PHY_STATS - | FLAG2_HAS_EEE, + | FLAG2_HAS_EEE + | FLAG2_CHECK_SYSTIM_OVERFLOW, .pba = 26, .max_hw_frame_size = 9022, .get_variants = e1000_get_variants_ich8lan, -- cgit From 3a3173b9c37aa1f07f8a71021114ee29a5712acb Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Thu, 6 Apr 2017 10:26:32 +0300 Subject: e1000e: Initial Support for CannonLake i219 (6) and i219 (7) are the next LOM generations that will be available on the nextIntel Client platform (CannonLake) This patch provides the initial support for these devices Signed-off-by: Sasha Neftin Reviewed-by: Raanan Avargil Reviewed-by: Dima Ruinskiy Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/e1000.h | 4 +++- drivers/net/ethernet/intel/e1000e/hw.h | 5 +++++ drivers/net/ethernet/intel/e1000e/ich8lan.c | 20 ++++++++++++++++++++ drivers/net/ethernet/intel/e1000e/netdev.c | 5 +++++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index a29b12e80855..f16d9826c66d 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -135,7 +135,8 @@ enum e1000_boards { board_pchlan, board_pch2lan, board_pch_lpt, - board_pch_spt + board_pch_spt, + board_pch_cnp }; struct e1000_ps_page { @@ -515,6 +516,7 @@ extern const struct e1000_info e1000_pch_info; extern const struct e1000_info e1000_pch2_info; extern const struct e1000_info e1000_pch_lpt_info; extern const struct e1000_info e1000_pch_spt_info; +extern const struct e1000_info e1000_pch_cnp_info; extern const struct e1000_info e1000_es2_info; void e1000e_ptp_init(struct e1000_adapter *adapter); diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h index 4e733bf1a38e..66bd5060a65b 100644 --- a/drivers/net/ethernet/intel/e1000e/hw.h +++ b/drivers/net/ethernet/intel/e1000e/hw.h @@ -96,6 +96,10 @@ struct e1000_hw; #define E1000_DEV_ID_PCH_SPT_I219_V4 0x15D8 #define E1000_DEV_ID_PCH_SPT_I219_LM5 0x15E3 #define E1000_DEV_ID_PCH_SPT_I219_V5 0x15D6 +#define E1000_DEV_ID_PCH_CNP_I219_LM6 0x15BD +#define E1000_DEV_ID_PCH_CNP_I219_V6 0x15BE +#define E1000_DEV_ID_PCH_CNP_I219_LM7 0x15BB +#define E1000_DEV_ID_PCH_CNP_I219_V7 0x15BC #define E1000_REVISION_4 4 @@ -118,6 +122,7 @@ enum e1000_mac_type { e1000_pch2lan, e1000_pch_lpt, e1000_pch_spt, + e1000_pch_cnp, }; enum e1000_media_type { diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index 72add037f07e..c0cd2874cba6 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -5915,3 +5915,23 @@ const struct e1000_info e1000_pch_spt_info = { .phy_ops = &ich8_phy_ops, .nvm_ops = &spt_nvm_ops, }; + +const struct e1000_info e1000_pch_cnp_info = { + .mac = e1000_pch_cnp, + .flags = FLAG_IS_ICH + | FLAG_HAS_WOL + | FLAG_HAS_HW_TIMESTAMP + | FLAG_HAS_CTRLEXT_ON_LOAD + | FLAG_HAS_AMT + | FLAG_HAS_FLASH + | FLAG_HAS_JUMBO_FRAMES + | FLAG_APME_IN_WUC, + .flags2 = FLAG2_HAS_PHY_STATS + | FLAG2_HAS_EEE, + .pba = 26, + .max_hw_frame_size = 9022, + .get_variants = e1000_get_variants_ich8lan, + .mac_ops = &ich8_mac_ops, + .phy_ops = &ich8_phy_ops, + .nvm_ops = &spt_nvm_ops, +}; diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 667fc45ce906..974fda2dd663 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -71,6 +71,7 @@ static const struct e1000_info *e1000_info_tbl[] = { [board_pch2lan] = &e1000_pch2_info, [board_pch_lpt] = &e1000_pch_lpt_info, [board_pch_spt] = &e1000_pch_spt_info, + [board_pch_cnp] = &e1000_pch_cnp_info, }; struct e1000_reg_info { @@ -7514,6 +7515,10 @@ static const struct pci_device_id e1000_pci_tbl[] = { { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V4), board_pch_spt }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_LM5), board_pch_spt }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_SPT_I219_V5), board_pch_spt }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_LM6), board_pch_cnp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V6), board_pch_cnp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_LM7), board_pch_cnp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V7), board_pch_cnp }, { 0, 0, 0, 0, 0, 0, 0 } /* terminate list */ }; -- cgit From c8744f44aeaee1caf5d6595e9351702253260088 Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Thu, 6 Apr 2017 10:26:47 +0300 Subject: e1000e: Add Support for CannonLake The propagation of CannonLake mac type to driver functionality Signed-off-by: Sasha Neftin Reviewed-by: Raanan Avargil Reviewed-by: Dima Ruinskiy Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/ethtool.c | 10 ++-- drivers/net/ethernet/intel/e1000e/ich8lan.c | 82 +++++++++++++++-------------- drivers/net/ethernet/intel/e1000e/netdev.c | 29 +++++----- drivers/net/ethernet/intel/e1000e/ptp.c | 4 +- 4 files changed, 63 insertions(+), 62 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index e70b1ebff60d..e23dbd9190d6 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -911,19 +911,20 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data) case e1000_pch2lan: case e1000_pch_lpt: case e1000_pch_spt: + /* fall through */ + case e1000_pch_cnp: mask |= BIT(18); break; default: break; } - if ((mac->type == e1000_pch_lpt) || (mac->type == e1000_pch_spt)) + if (mac->type >= e1000_pch_lpt) wlock_mac = (er32(FWSM) & E1000_FWSM_WLOCK_MAC_MASK) >> E1000_FWSM_WLOCK_MAC_SHIFT; for (i = 0; i < mac->rar_entry_count; i++) { - if ((mac->type == e1000_pch_lpt) || - (mac->type == e1000_pch_spt)) { + if (mac->type >= e1000_pch_lpt) { /* Cannot test write-protected SHRAL[n] registers */ if ((wlock_mac == 1) || (wlock_mac && (i > wlock_mac))) continue; @@ -1532,7 +1533,7 @@ static int e1000_setup_loopback_test(struct e1000_adapter *adapter) struct e1000_hw *hw = &adapter->hw; u32 rctl, fext_nvm11, tarc0; - if (hw->mac.type == e1000_pch_spt) { + if (hw->mac.type >= e1000_pch_spt) { fext_nvm11 = er32(FEXTNVM11); fext_nvm11 |= E1000_FEXTNVM11_DISABLE_MULR_FIX; ew32(FEXTNVM11, fext_nvm11); @@ -1576,6 +1577,7 @@ static void e1000_loopback_cleanup(struct e1000_adapter *adapter) switch (hw->mac.type) { case e1000_pch_spt: + case e1000_pch_cnp: fext_nvm11 = er32(FEXTNVM11); fext_nvm11 &= ~E1000_FEXTNVM11_DISABLE_MULR_FIX; ew32(FEXTNVM11, fext_nvm11); diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index c0cd2874cba6..68ea8b4555ab 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -237,7 +237,7 @@ static bool e1000_phy_is_accessible_pchlan(struct e1000_hw *hw) if (ret_val) return false; out: - if ((hw->mac.type == e1000_pch_lpt) || (hw->mac.type == e1000_pch_spt)) { + if (hw->mac.type >= e1000_pch_lpt) { /* Only unforce SMBus if ME is not active */ if (!(er32(FWSM) & E1000_ICH_FWSM_FW_VALID)) { /* Unforce SMBus mode in PHY */ @@ -333,6 +333,7 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw) switch (hw->mac.type) { case e1000_pch_lpt: case e1000_pch_spt: + case e1000_pch_cnp: if (e1000_phy_is_accessible_pchlan(hw)) break; @@ -474,6 +475,7 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw) case e1000_pch2lan: case e1000_pch_lpt: case e1000_pch_spt: + case e1000_pch_cnp: /* In case the PHY needs to be in mdio slow mode, * set slow mode and try to get the PHY id again. */ @@ -607,7 +609,7 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw) nvm->type = e1000_nvm_flash_sw; - if (hw->mac.type == e1000_pch_spt) { + if (hw->mac.type >= e1000_pch_spt) { /* in SPT, gfpreg doesn't exist. NVM size is taken from the * STRAP register. This is because in SPT the GbE Flash region * is no longer accessed through the flash registers. Instead, @@ -715,6 +717,7 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw) /* fall-through */ case e1000_pch_lpt: case e1000_pch_spt: + case e1000_pch_cnp: case e1000_pchlan: /* check management mode */ mac->ops.check_mng_mode = e1000_check_mng_mode_pchlan; @@ -732,7 +735,7 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw) break; } - if ((mac->type == e1000_pch_lpt) || (mac->type == e1000_pch_spt)) { + if (mac->type >= e1000_pch_lpt) { mac->rar_entry_count = E1000_PCH_LPT_RAR_ENTRIES; mac->ops.rar_set = e1000_rar_set_pch_lpt; mac->ops.setup_physical_interface = @@ -1399,9 +1402,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) * aggressive resulting in many collisions. To avoid this, increase * the IPG and reduce Rx latency in the PHY. */ - if (((hw->mac.type == e1000_pch2lan) || - (hw->mac.type == e1000_pch_lpt) || - (hw->mac.type == e1000_pch_spt)) && link) { + if ((hw->mac.type >= e1000_pch2lan) && link) { u16 speed, duplex; e1000e_get_speed_and_duplex_copper(hw, &speed, &duplex); @@ -1412,7 +1413,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) tipg_reg |= 0xFF; /* Reduce Rx latency in analog PHY */ emi_val = 0; - } else if (hw->mac.type == e1000_pch_spt && + } else if (hw->mac.type >= e1000_pch_spt && duplex == FULL_DUPLEX && speed != SPEED_1000) { tipg_reg |= 0xC; emi_val = 1; @@ -1435,8 +1436,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) emi_addr = I217_RX_CONFIG; ret_val = e1000_write_emi_reg_locked(hw, emi_addr, emi_val); - if (hw->mac.type == e1000_pch_lpt || - hw->mac.type == e1000_pch_spt) { + if (hw->mac.type >= e1000_pch_lpt) { u16 phy_reg; e1e_rphy_locked(hw, I217_PLL_CLOCK_GATE_REG, &phy_reg); @@ -1452,7 +1452,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) if (ret_val) return ret_val; - if (hw->mac.type == e1000_pch_spt) { + if (hw->mac.type >= e1000_pch_spt) { u16 data; u16 ptr_gap; @@ -1502,7 +1502,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) * on power up. * Set the Beacon Duration for I217 to 8 usec */ - if ((hw->mac.type == e1000_pch_lpt) || (hw->mac.type == e1000_pch_spt)) { + if (hw->mac.type >= e1000_pch_lpt) { u32 mac_reg; mac_reg = er32(FEXTNVM4); @@ -1520,8 +1520,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) if (ret_val) return ret_val; } - if ((hw->mac.type == e1000_pch_lpt) || - (hw->mac.type == e1000_pch_spt)) { + if (hw->mac.type >= e1000_pch_lpt) { /* Set platform power management values for * Latency Tolerance Reporting (LTR) */ @@ -1533,15 +1532,18 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) /* Clear link partner's EEE ability */ hw->dev_spec.ich8lan.eee_lp_ability = 0; - /* FEXTNVM6 K1-off workaround */ - if (hw->mac.type == e1000_pch_spt) { - u32 pcieanacfg = er32(PCIEANACFG); + if (hw->mac.type >= e1000_pch_lpt) { u32 fextnvm6 = er32(FEXTNVM6); - if (pcieanacfg & E1000_FEXTNVM6_K1_OFF_ENABLE) - fextnvm6 |= E1000_FEXTNVM6_K1_OFF_ENABLE; - else - fextnvm6 &= ~E1000_FEXTNVM6_K1_OFF_ENABLE; + if (hw->mac.type == e1000_pch_spt) { + /* FEXTNVM6 K1-off workaround - for SPT only */ + u32 pcieanacfg = er32(PCIEANACFG); + + if (pcieanacfg & E1000_FEXTNVM6_K1_OFF_ENABLE) + fextnvm6 |= E1000_FEXTNVM6_K1_OFF_ENABLE; + else + fextnvm6 &= ~E1000_FEXTNVM6_K1_OFF_ENABLE; + } ew32(FEXTNVM6, fextnvm6); } @@ -1640,6 +1642,7 @@ static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter) case e1000_pch2lan: case e1000_pch_lpt: case e1000_pch_spt: + case e1000_pch_cnp: rc = e1000_init_phy_params_pchlan(hw); break; default: @@ -2091,6 +2094,7 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw) case e1000_pch2lan: case e1000_pch_lpt: case e1000_pch_spt: + case e1000_pch_cnp: sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG_ICH8M; break; default: @@ -3125,6 +3129,7 @@ static s32 e1000_valid_nvm_bank_detect_ich8lan(struct e1000_hw *hw, u32 *bank) switch (hw->mac.type) { case e1000_pch_spt: + case e1000_pch_cnp: bank1_offset = nvm->flash_bank_size; act_offset = E1000_ICH_NVM_SIG_WORD; @@ -3380,7 +3385,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) /* Clear FCERR and DAEL in hw status by writing 1 */ hsfsts.hsf_status.flcerr = 1; hsfsts.hsf_status.dael = 1; - if (hw->mac.type == e1000_pch_spt) + if (hw->mac.type >= e1000_pch_spt) ew32flash(ICH_FLASH_HSFSTS, hsfsts.regval & 0xFFFF); else ew16flash(ICH_FLASH_HSFSTS, hsfsts.regval); @@ -3399,7 +3404,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) * Begin by setting Flash Cycle Done. */ hsfsts.hsf_status.flcdone = 1; - if (hw->mac.type == e1000_pch_spt) + if (hw->mac.type >= e1000_pch_spt) ew32flash(ICH_FLASH_HSFSTS, hsfsts.regval & 0xFFFF); else ew16flash(ICH_FLASH_HSFSTS, hsfsts.regval); @@ -3423,7 +3428,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) * now set the Flash Cycle Done. */ hsfsts.hsf_status.flcdone = 1; - if (hw->mac.type == e1000_pch_spt) + if (hw->mac.type >= e1000_pch_spt) ew32flash(ICH_FLASH_HSFSTS, hsfsts.regval & 0xFFFF); else @@ -3450,13 +3455,13 @@ static s32 e1000_flash_cycle_ich8lan(struct e1000_hw *hw, u32 timeout) u32 i = 0; /* Start a cycle by writing 1 in Flash Cycle Go in Hw Flash Control */ - if (hw->mac.type == e1000_pch_spt) + if (hw->mac.type >= e1000_pch_spt) hsflctl.regval = er32flash(ICH_FLASH_HSFSTS) >> 16; else hsflctl.regval = er16flash(ICH_FLASH_HSFCTL); hsflctl.hsf_ctrl.flcgo = 1; - if (hw->mac.type == e1000_pch_spt) + if (hw->mac.type >= e1000_pch_spt) ew32flash(ICH_FLASH_HSFSTS, hsflctl.regval << 16); else ew16flash(ICH_FLASH_HSFCTL, hsflctl.regval); @@ -3527,7 +3532,7 @@ static s32 e1000_read_flash_byte_ich8lan(struct e1000_hw *hw, u32 offset, /* In SPT, only 32 bits access is supported, * so this function should not be called. */ - if (hw->mac.type == e1000_pch_spt) + if (hw->mac.type >= e1000_pch_spt) return -E1000_ERR_NVM; else ret_val = e1000_read_flash_data_ich8lan(hw, offset, 1, &word); @@ -3634,8 +3639,7 @@ static s32 e1000_read_flash_data32_ich8lan(struct e1000_hw *hw, u32 offset, s32 ret_val = -E1000_ERR_NVM; u8 count = 0; - if (offset > ICH_FLASH_LINEAR_ADDR_MASK || - hw->mac.type != e1000_pch_spt) + if (offset > ICH_FLASH_LINEAR_ADDR_MASK || hw->mac.type < e1000_pch_spt) return -E1000_ERR_NVM; flash_linear_addr = ((ICH_FLASH_LINEAR_ADDR_MASK & offset) + hw->nvm.flash_base_addr); @@ -4068,6 +4072,7 @@ static s32 e1000_validate_nvm_checksum_ich8lan(struct e1000_hw *hw) switch (hw->mac.type) { case e1000_pch_lpt: case e1000_pch_spt: + case e1000_pch_cnp: word = NVM_COMPAT; valid_csum_mask = NVM_COMPAT_VALID_CSUM; break; @@ -4153,7 +4158,7 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, s32 ret_val; u8 count = 0; - if (hw->mac.type == e1000_pch_spt) { + if (hw->mac.type >= e1000_pch_spt) { if (size != 4 || offset > ICH_FLASH_LINEAR_ADDR_MASK) return -E1000_ERR_NVM; } else { @@ -4173,7 +4178,7 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, /* In SPT, This register is in Lan memory space, not * flash. Therefore, only 32 bit access is supported */ - if (hw->mac.type == e1000_pch_spt) + if (hw->mac.type >= e1000_pch_spt) hsflctl.regval = er32flash(ICH_FLASH_HSFSTS) >> 16; else hsflctl.regval = er16flash(ICH_FLASH_HSFCTL); @@ -4185,7 +4190,7 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, * not flash. Therefore, only 32 bit access is * supported */ - if (hw->mac.type == e1000_pch_spt) + if (hw->mac.type >= e1000_pch_spt) ew32flash(ICH_FLASH_HSFSTS, hsflctl.regval << 16); else ew16flash(ICH_FLASH_HSFCTL, hsflctl.regval); @@ -4243,7 +4248,7 @@ static s32 e1000_write_flash_data32_ich8lan(struct e1000_hw *hw, u32 offset, s32 ret_val; u8 count = 0; - if (hw->mac.type == e1000_pch_spt) { + if (hw->mac.type >= e1000_pch_spt) { if (offset > ICH_FLASH_LINEAR_ADDR_MASK) return -E1000_ERR_NVM; } @@ -4259,7 +4264,7 @@ static s32 e1000_write_flash_data32_ich8lan(struct e1000_hw *hw, u32 offset, /* In SPT, This register is in Lan memory space, not * flash. Therefore, only 32 bit access is supported */ - if (hw->mac.type == e1000_pch_spt) + if (hw->mac.type >= e1000_pch_spt) hsflctl.regval = er32flash(ICH_FLASH_HSFSTS) >> 16; else @@ -4272,7 +4277,7 @@ static s32 e1000_write_flash_data32_ich8lan(struct e1000_hw *hw, u32 offset, * not flash. Therefore, only 32 bit access is * supported */ - if (hw->mac.type == e1000_pch_spt) + if (hw->mac.type >= e1000_pch_spt) ew32flash(ICH_FLASH_HSFSTS, hsflctl.regval << 16); else ew16flash(ICH_FLASH_HSFCTL, hsflctl.regval); @@ -4464,14 +4469,14 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank) /* Write a value 11 (block Erase) in Flash * Cycle field in hw flash control */ - if (hw->mac.type == e1000_pch_spt) + if (hw->mac.type >= e1000_pch_spt) hsflctl.regval = er32flash(ICH_FLASH_HSFSTS) >> 16; else hsflctl.regval = er16flash(ICH_FLASH_HSFCTL); hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_ERASE; - if (hw->mac.type == e1000_pch_spt) + if (hw->mac.type >= e1000_pch_spt) ew32flash(ICH_FLASH_HSFSTS, hsflctl.regval << 16); else @@ -4894,8 +4899,7 @@ static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw) ew32(RFCTL, reg); /* Enable ECC on Lynxpoint */ - if ((hw->mac.type == e1000_pch_lpt) || - (hw->mac.type == e1000_pch_spt)) { + if (hw->mac.type >= e1000_pch_lpt) { reg = er32(PBECCSTS); reg |= E1000_PBECCSTS_ECC_ENABLE; ew32(PBECCSTS, reg); @@ -5299,7 +5303,7 @@ void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw) (device_id == E1000_DEV_ID_PCH_LPTLP_I218_V) || (device_id == E1000_DEV_ID_PCH_I218_LM3) || (device_id == E1000_DEV_ID_PCH_I218_V3) || - (hw->mac.type == e1000_pch_spt)) { + (hw->mac.type >= e1000_pch_spt)) { u32 fextnvm6 = er32(FEXTNVM6); ew32(FEXTNVM6, fextnvm6 & ~E1000_FEXTNVM6_REQ_PLL_CLK); diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 974fda2dd663..256a8a014583 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -1792,8 +1792,7 @@ static irqreturn_t e1000_intr_msi(int __always_unused irq, void *data) } /* Reset on uncorrectable ECC error */ - if ((icr & E1000_ICR_ECCER) && ((hw->mac.type == e1000_pch_lpt) || - (hw->mac.type == e1000_pch_spt))) { + if ((icr & E1000_ICR_ECCER) && (hw->mac.type >= e1000_pch_lpt)) { u32 pbeccsts = er32(PBECCSTS); adapter->corr_errors += @@ -1873,8 +1872,7 @@ static irqreturn_t e1000_intr(int __always_unused irq, void *data) } /* Reset on uncorrectable ECC error */ - if ((icr & E1000_ICR_ECCER) && ((hw->mac.type == e1000_pch_lpt) || - (hw->mac.type == e1000_pch_spt))) { + if ((icr & E1000_ICR_ECCER) && (hw->mac.type >= e1000_pch_lpt)) { u32 pbeccsts = er32(PBECCSTS); adapter->corr_errors += @@ -2242,8 +2240,7 @@ static void e1000_irq_enable(struct e1000_adapter *adapter) if (adapter->msix_entries) { ew32(EIAC_82574, adapter->eiac_mask & E1000_EIAC_MASK_82574); ew32(IMS, adapter->eiac_mask | E1000_IMS_LSC); - } else if ((hw->mac.type == e1000_pch_lpt) || - (hw->mac.type == e1000_pch_spt)) { + } else if (hw->mac.type >= e1000_pch_lpt) { ew32(IMS, IMS_ENABLE_MASK | E1000_IMS_ECCER); } else { ew32(IMS, IMS_ENABLE_MASK); @@ -3001,8 +2998,8 @@ static void e1000_configure_tx(struct e1000_adapter *adapter) hw->mac.ops.config_collision_dist(hw); - /* SPT Si errata workaround to avoid data corruption */ - if (hw->mac.type == e1000_pch_spt) { + /* SPT and CNP Si errata workaround to avoid data corruption */ + if (hw->mac.type >= e1000_pch_spt) { u32 reg_val; reg_val = er32(IOSFPC); @@ -3498,8 +3495,7 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca) /* Make sure clock is enabled on I217/I218/I219 before checking * the frequency */ - if (((hw->mac.type == e1000_pch_lpt) || - (hw->mac.type == e1000_pch_spt)) && + if ((hw->mac.type >= e1000_pch_lpt) && !(er32(TSYNCTXCTL) & E1000_TSYNCTXCTL_ENABLED) && !(er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_ENABLED)) { u32 fextnvm7 = er32(FEXTNVM7); @@ -4039,6 +4035,7 @@ void e1000e_reset(struct e1000_adapter *adapter) case e1000_pch2lan: case e1000_pch_lpt: case e1000_pch_spt: + case e1000_pch_cnp: fc->refresh_time = 0x0400; if (adapter->netdev->mtu <= ETH_DATA_LEN) { @@ -4083,7 +4080,7 @@ void e1000e_reset(struct e1000_adapter *adapter) } } - if (hw->mac.type == e1000_pch_spt) + if (hw->mac.type >= e1000_pch_spt) e1000_flush_desc_rings(adapter); /* Allow time for pending master requests to run */ mac->ops.reset_hw(hw); @@ -4158,7 +4155,7 @@ void e1000e_reset(struct e1000_adapter *adapter) phy_data &= ~IGP02E1000_PM_SPD; e1e_wphy(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); } - if (hw->mac.type == e1000_pch_spt && adapter->int_mode == 0) { + if (hw->mac.type >= e1000_pch_spt && adapter->int_mode == 0) { u32 reg; /* Fextnvm7 @ 0xe4[2] = 1 */ @@ -4292,7 +4289,7 @@ void e1000e_down(struct e1000_adapter *adapter, bool reset) if (!pci_channel_offline(adapter->pdev)) { if (reset) e1000e_reset(adapter); - else if (hw->mac.type == e1000_pch_spt) + else if (hw->mac.type >= e1000_pch_spt) e1000_flush_desc_rings(adapter); } e1000_clean_tx_ring(adapter->tx_ring); @@ -4980,8 +4977,7 @@ static void e1000e_update_stats(struct e1000_adapter *adapter) adapter->stats.mgpdc += er32(MGTPDC); /* Correctable ECC Errors */ - if ((hw->mac.type == e1000_pch_lpt) || - (hw->mac.type == e1000_pch_spt)) { + if (hw->mac.type >= e1000_pch_lpt) { u32 pbeccsts = er32(PBECCSTS); adapter->corr_errors += @@ -6355,8 +6351,7 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool runtime) if (adapter->hw.phy.type == e1000_phy_igp_3) { e1000e_igp3_phy_powerdown_workaround_ich8lan(&adapter->hw); - } else if ((hw->mac.type == e1000_pch_lpt) || - (hw->mac.type == e1000_pch_spt)) { + } else if (hw->mac.type >= e1000_pch_lpt) { if (!(wufc & (E1000_WUFC_EX | E1000_WUFC_MC | E1000_WUFC_BC))) /* ULP does not support wake from unicast, multicast * or broadcast. diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c index 34cc3be0df8e..b366885487a8 100644 --- a/drivers/net/ethernet/intel/e1000e/ptp.c +++ b/drivers/net/ethernet/intel/e1000e/ptp.c @@ -301,8 +301,8 @@ void e1000e_ptp_init(struct e1000_adapter *adapter) case e1000_pch2lan: case e1000_pch_lpt: case e1000_pch_spt: - if (((hw->mac.type != e1000_pch_lpt) && - (hw->mac.type != e1000_pch_spt)) || + case e1000_pch_cnp: + if ((hw->mac.type < e1000_pch_lpt) || (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI)) { adapter->ptp_clock_info.max_adj = 24000000 - 1; break; -- cgit From 68fe1d5da548aab2b6b1c28a9137248d6ccfcc43 Mon Sep 17 00:00:00 2001 From: Sasha Neftin Date: Thu, 6 Apr 2017 10:27:03 +0300 Subject: e1000e: Add Support for 38.4MHZ frequency Add support for 38.4MHz frequency is required for PTP on CannonLake. SYSTIM frequency adjustment attributes for TIMINCA are get/set dependent on the hardware clock frequency for a different types of adapters. 38.4MHz frequency supported by CannonLake and active once time synchronisation mechanism was enabled Changed abbreviation from Hz to HZ to be compliant checkpatch code style Signed-off-by: Sasha Neftin Reviewed-by: Raanan Avargil Reviewed-by: Dima Ruinskiy Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/e1000.h | 28 +++++++++-------- drivers/net/ethernet/intel/e1000e/netdev.c | 49 +++++++++++++++++++----------- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index f16d9826c66d..c7c994eb410e 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -379,18 +379,22 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca); * INCVALUE_n into the TIMINCA register allowing 32+8+(24-INCVALUE_SHIFT_n) * bits to count nanoseconds leaving the rest for fractional nonseconds. */ -#define INCVALUE_96MHz 125 -#define INCVALUE_SHIFT_96MHz 17 -#define INCPERIOD_SHIFT_96MHz 2 -#define INCPERIOD_96MHz (12 >> INCPERIOD_SHIFT_96MHz) - -#define INCVALUE_25MHz 40 -#define INCVALUE_SHIFT_25MHz 18 -#define INCPERIOD_25MHz 1 - -#define INCVALUE_24MHz 125 -#define INCVALUE_SHIFT_24MHz 14 -#define INCPERIOD_24MHz 3 +#define INCVALUE_96MHZ 125 +#define INCVALUE_SHIFT_96MHZ 17 +#define INCPERIOD_SHIFT_96MHZ 2 +#define INCPERIOD_96MHZ (12 >> INCPERIOD_SHIFT_96MHZ) + +#define INCVALUE_25MHZ 40 +#define INCVALUE_SHIFT_25MHZ 18 +#define INCPERIOD_25MHZ 1 + +#define INCVALUE_24MHZ 125 +#define INCVALUE_SHIFT_24MHZ 14 +#define INCPERIOD_24MHZ 3 + +#define INCVALUE_38400KHZ 26 +#define INCVALUE_SHIFT_38400KHZ 19 +#define INCPERIOD_38400KHZ 1 /* Another drawback of scaling the incvalue by a large factor is the * 64-bit SYSTIM register overflows more quickly. This is dealt with diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 256a8a014583..b3679728caac 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -3509,42 +3509,57 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca) switch (hw->mac.type) { case e1000_pch2lan: /* Stable 96MHz frequency */ - incperiod = INCPERIOD_96MHz; - incvalue = INCVALUE_96MHz; - shift = INCVALUE_SHIFT_96MHz; - adapter->cc.shift = shift + INCPERIOD_SHIFT_96MHz; + incperiod = INCPERIOD_96MHZ; + incvalue = INCVALUE_96MHZ; + shift = INCVALUE_SHIFT_96MHZ; + adapter->cc.shift = shift + INCPERIOD_SHIFT_96MHZ; break; case e1000_pch_lpt: if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) { /* Stable 96MHz frequency */ - incperiod = INCPERIOD_96MHz; - incvalue = INCVALUE_96MHz; - shift = INCVALUE_SHIFT_96MHz; - adapter->cc.shift = shift + INCPERIOD_SHIFT_96MHz; + incperiod = INCPERIOD_96MHZ; + incvalue = INCVALUE_96MHZ; + shift = INCVALUE_SHIFT_96MHZ; + adapter->cc.shift = shift + INCPERIOD_SHIFT_96MHZ; } else { /* Stable 25MHz frequency */ - incperiod = INCPERIOD_25MHz; - incvalue = INCVALUE_25MHz; - shift = INCVALUE_SHIFT_25MHz; + incperiod = INCPERIOD_25MHZ; + incvalue = INCVALUE_25MHZ; + shift = INCVALUE_SHIFT_25MHZ; adapter->cc.shift = shift; } break; case e1000_pch_spt: if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) { /* Stable 24MHz frequency */ - incperiod = INCPERIOD_24MHz; - incvalue = INCVALUE_24MHz; - shift = INCVALUE_SHIFT_24MHz; + incperiod = INCPERIOD_24MHZ; + incvalue = INCVALUE_24MHZ; + shift = INCVALUE_SHIFT_24MHZ; adapter->cc.shift = shift; break; } return -EINVAL; + case e1000_pch_cnp: + if (er32(TSYNCRXCTL) & E1000_TSYNCRXCTL_SYSCFI) { + /* Stable 24MHz frequency */ + incperiod = INCPERIOD_24MHZ; + incvalue = INCVALUE_24MHZ; + shift = INCVALUE_SHIFT_24MHZ; + adapter->cc.shift = shift; + } else { + /* Stable 38400KHz frequency */ + incperiod = INCPERIOD_38400KHZ; + incvalue = INCVALUE_38400KHZ; + shift = INCVALUE_SHIFT_38400KHZ; + adapter->cc.shift = shift; + } + break; case e1000_82574: case e1000_82583: /* Stable 25MHz frequency */ - incperiod = INCPERIOD_25MHz; - incvalue = INCVALUE_25MHz; - shift = INCVALUE_SHIFT_25MHz; + incperiod = INCPERIOD_25MHZ; + incvalue = INCVALUE_25MHZ; + shift = INCVALUE_SHIFT_25MHZ; adapter->cc.shift = shift; break; default: -- cgit From 1d447a39142e0eaf8526fcb37699e6b8cce5eaa8 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Mon, 24 Apr 2017 12:36:42 +0300 Subject: net/mlx5e: Extendable vport representor netdev private data Make representor netdev private data extendable by adding new struct "mlx5e_rep_priv" and use it as the rep netdev private data struct instead of directly pointing to mlx5_eswitch_rep. Added new en_rep.h header file to contain all representor related definitions and prototypes, and moved all representor specific logic into en_rep.c. Needed for downstream patches to extend representor functionality to support neighbour update. Signed-off-by: Saeed Mahameed Reviewed-by: Or Gerlitz --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 20 --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 77 +++------- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 176 ++++++++++++++++------ drivers/net/ethernet/mellanox/mlx5/core/en_rep.h | 55 +++++++ drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 4 +- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 22 ++- 6 files changed, 224 insertions(+), 130 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/en_rep.h diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 632a04b0ecaf..0099a3e397bc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -991,20 +991,6 @@ int mlx5e_create_mdev_resources(struct mlx5_core_dev *mdev); void mlx5e_destroy_mdev_resources(struct mlx5_core_dev *mdev); int mlx5e_refresh_tirs(struct mlx5e_priv *priv, bool enable_uc_lb); -struct mlx5_eswitch_rep; -int mlx5e_vport_rep_load(struct mlx5_eswitch *esw, - struct mlx5_eswitch_rep *rep); -void mlx5e_vport_rep_unload(struct mlx5_eswitch *esw, - struct mlx5_eswitch_rep *rep); -int mlx5e_nic_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep); -void mlx5e_nic_rep_unload(struct mlx5_eswitch *esw, - struct mlx5_eswitch_rep *rep); -int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv); -void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv); -int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr); -void mlx5e_handle_rx_cqe_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe); -void mlx5e_update_hw_rep_counters(struct mlx5e_priv *priv); - /* common netdev helpers */ int mlx5e_create_indirect_rqt(struct mlx5e_priv *priv); @@ -1031,12 +1017,6 @@ int mlx5e_open(struct net_device *netdev); void mlx5e_update_stats_work(struct work_struct *work); u32 mlx5e_choose_lro_timeout(struct mlx5_core_dev *mdev, u32 wanted_timeout); -int mlx5e_get_offload_stats(int attr_id, const struct net_device *dev, - void *sp); -bool mlx5e_has_offload_stats(const struct net_device *dev, int attr_id); - -bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv); - /* mlx5e generic netdev management API */ struct net_device* mlx5e_create_netdev(struct mlx5_core_dev *mdev, const struct mlx5e_profile *profile, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index e43411d232ee..1afaca96a30d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -35,9 +35,10 @@ #include #include #include +#include "eswitch.h" #include "en.h" #include "en_tc.h" -#include "eswitch.h" +#include "en_rep.h" #include "vxlan.h" struct mlx5e_rq_param { @@ -4123,48 +4124,10 @@ static int mlx5e_init_nic_tx(struct mlx5e_priv *priv) return 0; } -static void mlx5e_register_vport_rep(struct mlx5_core_dev *mdev) -{ - struct mlx5_eswitch *esw = mdev->priv.eswitch; - int total_vfs = MLX5_TOTAL_VPORTS(mdev); - int vport; - u8 mac[ETH_ALEN]; - - if (!MLX5_CAP_GEN(mdev, vport_group_manager)) - return; - - mlx5_query_nic_vport_mac_address(mdev, 0, mac); - - for (vport = 1; vport < total_vfs; vport++) { - struct mlx5_eswitch_rep rep; - - rep.load = mlx5e_vport_rep_load; - rep.unload = mlx5e_vport_rep_unload; - rep.vport = vport; - ether_addr_copy(rep.hw_id, mac); - mlx5_eswitch_register_vport_rep(esw, vport, &rep); - } -} - -static void mlx5e_unregister_vport_rep(struct mlx5_core_dev *mdev) -{ - struct mlx5_eswitch *esw = mdev->priv.eswitch; - int total_vfs = MLX5_TOTAL_VPORTS(mdev); - int vport; - - if (!MLX5_CAP_GEN(mdev, vport_group_manager)) - return; - - for (vport = 1; vport < total_vfs; vport++) - mlx5_eswitch_unregister_vport_rep(esw, vport); -} - static void mlx5e_nic_enable(struct mlx5e_priv *priv) { struct net_device *netdev = priv->netdev; struct mlx5_core_dev *mdev = priv->mdev; - struct mlx5_eswitch *esw = mdev->priv.eswitch; - struct mlx5_eswitch_rep rep; u16 max_mtu; mlx5e_init_l2_addr(priv); @@ -4179,16 +4142,8 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) mlx5e_enable_async_events(priv); - if (MLX5_CAP_GEN(mdev, vport_group_manager)) { - mlx5_query_nic_vport_mac_address(mdev, 0, rep.hw_id); - rep.load = mlx5e_nic_rep_load; - rep.unload = mlx5e_nic_rep_unload; - rep.vport = FDB_UPLINK_VPORT; - rep.netdev = netdev; - mlx5_eswitch_register_vport_rep(esw, 0, &rep); - } - - mlx5e_register_vport_rep(mdev); + if (MLX5_CAP_GEN(mdev, vport_group_manager)) + mlx5e_register_vport_reps(priv); if (netdev->reg_state != NETREG_REGISTERED) return; @@ -4212,7 +4167,6 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) static void mlx5e_nic_disable(struct mlx5e_priv *priv) { struct mlx5_core_dev *mdev = priv->mdev; - struct mlx5_eswitch *esw = mdev->priv.eswitch; rtnl_lock(); if (netif_running(priv->netdev)) @@ -4221,9 +4175,10 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv) rtnl_unlock(); queue_work(priv->wq, &priv->set_rx_mode_work); - mlx5e_unregister_vport_rep(mdev); + if (MLX5_CAP_GEN(mdev, vport_group_manager)) - mlx5_eswitch_unregister_vport_rep(esw, 0); + mlx5e_unregister_vport_reps(priv); + mlx5e_disable_async_events(priv); mlx5_lag_remove(mdev); } @@ -4394,7 +4349,7 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) { struct mlx5_eswitch *esw = mdev->priv.eswitch; int total_vfs = MLX5_TOTAL_VPORTS(mdev); - void *ppriv = NULL; + struct mlx5e_rep_priv *rpriv = NULL; void *priv; int vport; int err; @@ -4404,10 +4359,17 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) if (err) return NULL; - if (MLX5_CAP_GEN(mdev, vport_group_manager)) - ppriv = &esw->offloads.vport_reps[0]; + if (MLX5_CAP_GEN(mdev, vport_group_manager)) { + rpriv = kzalloc(sizeof(*rpriv), GFP_KERNEL); + if (!rpriv) { + mlx5_core_warn(mdev, + "Not creating net device, Failed to alloc rep priv data\n"); + return NULL; + } + rpriv->rep = &esw->offloads.vport_reps[0]; + } - netdev = mlx5e_create_netdev(mdev, &mlx5e_nic_profile, ppriv); + netdev = mlx5e_create_netdev(mdev, &mlx5e_nic_profile, rpriv); if (!netdev) { mlx5_core_err(mdev, "mlx5e_create_netdev failed\n"); goto err_unregister_reps; @@ -4439,16 +4401,19 @@ err_unregister_reps: for (vport = 1; vport < total_vfs; vport++) mlx5_eswitch_unregister_vport_rep(esw, vport); + kfree(rpriv); return NULL; } static void mlx5e_remove(struct mlx5_core_dev *mdev, void *vpriv) { struct mlx5e_priv *priv = vpriv; + void *ppriv = priv->ppriv; unregister_netdev(priv->netdev); mlx5e_detach(mdev, vpriv); mlx5e_destroy_netdev(priv); + kfree(ppriv); } static void *mlx5e_get_netdev(void *vpriv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 16b683e8226d..8e82b11afd99 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -37,6 +37,7 @@ #include "eswitch.h" #include "en.h" +#include "en_rep.h" #include "en_tc.h" static const char mlx5e_rep_driver_name[] = "mlx5e_rep"; @@ -75,7 +76,8 @@ static void mlx5e_rep_get_strings(struct net_device *dev, static void mlx5e_rep_update_hw_counters(struct mlx5e_priv *priv) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; - struct mlx5_eswitch_rep *rep = priv->ppriv; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep = rpriv->rep; struct rtnl_link_stats64 *vport_stats; struct ifla_vf_stats vf_stats; int err; @@ -165,7 +167,8 @@ static const struct ethtool_ops mlx5e_rep_ethtool_ops = { int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr) { struct mlx5e_priv *priv = netdev_priv(dev); - struct mlx5_eswitch_rep *rep = priv->ppriv; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep = rpriv->rep; struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; if (esw->mode == SRIOV_NONE) @@ -184,10 +187,10 @@ int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr) } int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) - { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; - struct mlx5_eswitch_rep *rep = priv->ppriv; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep = rpriv->rep; struct mlx5e_channel *c; int n, tc, num_sqs = 0; int err = -ENOMEM; @@ -212,42 +215,20 @@ out: return err; } -int mlx5e_nic_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) -{ - struct net_device *netdev = rep->netdev; - struct mlx5e_priv *priv = netdev_priv(netdev); - - if (test_bit(MLX5E_STATE_OPENED, &priv->state)) - return mlx5e_add_sqs_fwd_rules(priv); - return 0; -} - void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; - struct mlx5_eswitch_rep *rep = priv->ppriv; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep = rpriv->rep; mlx5_eswitch_sqs2vport_stop(esw, rep); } -void mlx5e_nic_rep_unload(struct mlx5_eswitch *esw, - struct mlx5_eswitch_rep *rep) -{ - struct net_device *netdev = rep->netdev; - struct mlx5e_priv *priv = netdev_priv(netdev); - - if (test_bit(MLX5E_STATE_OPENED, &priv->state)) - mlx5e_remove_sqs_fwd_rules(priv); - - /* clean (and re-init) existing uplink offloaded TC rules */ - mlx5e_tc_cleanup(priv); - mlx5e_tc_init(priv); -} - static int mlx5e_rep_open(struct net_device *dev) { struct mlx5e_priv *priv = netdev_priv(dev); - struct mlx5_eswitch_rep *rep = priv->ppriv; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep = rpriv->rep; struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; int err; @@ -265,7 +246,8 @@ static int mlx5e_rep_open(struct net_device *dev) static int mlx5e_rep_close(struct net_device *dev) { struct mlx5e_priv *priv = netdev_priv(dev); - struct mlx5_eswitch_rep *rep = priv->ppriv; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep = rpriv->rep; struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; (void)mlx5_eswitch_set_vport_state(esw, rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_DOWN); @@ -277,7 +259,8 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev, char *buf, size_t len) { struct mlx5e_priv *priv = netdev_priv(dev); - struct mlx5_eswitch_rep *rep = priv->ppriv; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep = rpriv->rep; int ret; ret = snprintf(buf, len, "%d", rep->vport - 1); @@ -320,10 +303,16 @@ static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, u32 handle, bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv) { - struct mlx5_eswitch_rep *rep = (struct mlx5_eswitch_rep *)priv->ppriv; struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep; + + if (!MLX5_CAP_GEN(priv->mdev, vport_group_manager)) + return false; - if (rep && rep->vport == FDB_UPLINK_VPORT && esw->mode == SRIOV_OFFLOADS) + rep = rpriv->rep; + if (esw->mode == SRIOV_OFFLOADS && + rep && rep->vport == FDB_UPLINK_VPORT) return true; return false; @@ -331,7 +320,8 @@ bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv) static bool mlx5e_is_vf_vport_rep(struct mlx5e_priv *priv) { - struct mlx5_eswitch_rep *rep = (struct mlx5_eswitch_rep *)priv->ppriv; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep = rpriv->rep; if (rep && rep->vport != FDB_UPLINK_VPORT) return true; @@ -464,7 +454,8 @@ static void mlx5e_init_rep(struct mlx5_core_dev *mdev, static int mlx5e_init_rep_rx(struct mlx5e_priv *priv) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; - struct mlx5_eswitch_rep *rep = priv->ppriv; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep = rpriv->rep; struct mlx5_flow_handle *flow_rule; int err; @@ -504,7 +495,8 @@ err_destroy_direct_rqts: static void mlx5e_cleanup_rep_rx(struct mlx5e_priv *priv) { - struct mlx5_eswitch_rep *rep = priv->ppriv; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep = rpriv->rep; mlx5e_tc_cleanup(priv); mlx5_del_flow_rules(rep->vport_rx_rule); @@ -543,20 +535,54 @@ static struct mlx5e_profile mlx5e_rep_profile = { .max_tc = 1, }; -int mlx5e_vport_rep_load(struct mlx5_eswitch *esw, - struct mlx5_eswitch_rep *rep) +/* e-Switch vport representors */ + +static int +mlx5e_nic_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) +{ + struct net_device *netdev = rep->netdev; + struct mlx5e_priv *priv = netdev_priv(netdev); + + if (test_bit(MLX5E_STATE_OPENED, &priv->state)) + return mlx5e_add_sqs_fwd_rules(priv); + return 0; +} + +static void +mlx5e_nic_rep_unload(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) { + struct net_device *netdev = rep->netdev; + struct mlx5e_priv *priv = netdev_priv(netdev); + + if (test_bit(MLX5E_STATE_OPENED, &priv->state)) + mlx5e_remove_sqs_fwd_rules(priv); + + /* clean (and re-init) existing uplink offloaded TC rules */ + mlx5e_tc_cleanup(priv); + mlx5e_tc_init(priv); +} + +static int +mlx5e_vport_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) +{ + struct mlx5e_rep_priv *rpriv; struct net_device *netdev; int err; - netdev = mlx5e_create_netdev(esw->dev, &mlx5e_rep_profile, rep); + rpriv = kzalloc(sizeof(*rpriv), GFP_KERNEL); + if (!rpriv) + return -ENOMEM; + + netdev = mlx5e_create_netdev(esw->dev, &mlx5e_rep_profile, rpriv); if (!netdev) { pr_warn("Failed to create representor netdev for vport %d\n", rep->vport); + kfree(rpriv); return -EINVAL; } rep->netdev = netdev; + rpriv->rep = rep; err = mlx5e_attach_netdev(netdev_priv(netdev)); if (err) { @@ -579,17 +605,77 @@ err_detach_netdev: err_destroy_netdev: mlx5e_destroy_netdev(netdev_priv(netdev)); - + kfree(rpriv); return err; } -void mlx5e_vport_rep_unload(struct mlx5_eswitch *esw, - struct mlx5_eswitch_rep *rep) +static void +mlx5e_vport_rep_unload(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) { struct net_device *netdev = rep->netdev; + struct mlx5e_priv *priv = netdev_priv(netdev); + void *ppriv = priv->ppriv; unregister_netdev(netdev); - mlx5e_detach_netdev(netdev_priv(netdev)); - mlx5e_destroy_netdev(netdev_priv(netdev)); + mlx5e_detach_netdev(priv); + mlx5e_destroy_netdev(priv); + kfree(ppriv); /* mlx5e_rep_priv */ +} + +static void mlx5e_rep_register_vf_vports(struct mlx5e_priv *priv) +{ + struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_eswitch *esw = mdev->priv.eswitch; + int total_vfs = MLX5_TOTAL_VPORTS(mdev); + int vport; + u8 mac[ETH_ALEN]; + + mlx5_query_nic_vport_mac_address(mdev, 0, mac); + + for (vport = 1; vport < total_vfs; vport++) { + struct mlx5_eswitch_rep rep; + + rep.load = mlx5e_vport_rep_load; + rep.unload = mlx5e_vport_rep_unload; + rep.vport = vport; + ether_addr_copy(rep.hw_id, mac); + mlx5_eswitch_register_vport_rep(esw, vport, &rep); + } +} + +static void mlx5e_rep_unregister_vf_vports(struct mlx5e_priv *priv) +{ + struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_eswitch *esw = mdev->priv.eswitch; + int total_vfs = MLX5_TOTAL_VPORTS(mdev); + int vport; + + for (vport = 1; vport < total_vfs; vport++) + mlx5_eswitch_unregister_vport_rep(esw, vport); +} + +void mlx5e_register_vport_reps(struct mlx5e_priv *priv) +{ + struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_eswitch *esw = mdev->priv.eswitch; + struct mlx5_eswitch_rep rep; + + mlx5_query_nic_vport_mac_address(mdev, 0, rep.hw_id); + rep.load = mlx5e_nic_rep_load; + rep.unload = mlx5e_nic_rep_unload; + rep.vport = FDB_UPLINK_VPORT; + rep.netdev = priv->netdev; + mlx5_eswitch_register_vport_rep(esw, 0, &rep); /* UPLINK PF vport*/ + + mlx5e_rep_register_vf_vports(priv); /* VFs vports */ +} + +void mlx5e_unregister_vport_reps(struct mlx5e_priv *priv) +{ + struct mlx5_core_dev *mdev = priv->mdev; + struct mlx5_eswitch *esw = mdev->priv.eswitch; + + mlx5e_rep_unregister_vf_vports(priv); /* VFs vports */ + mlx5_eswitch_unregister_vport_rep(esw, 0); /* UPLINK PF*/ } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h new file mode 100644 index 000000000000..b6595a699dc1 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __MLX5E_REP_H__ +#define __MLX5E_REP_H__ + +#include "eswitch.h" +#include "en.h" + +struct mlx5e_rep_priv { + struct mlx5_eswitch_rep *rep; +}; + +void mlx5e_register_vport_reps(struct mlx5e_priv *priv); +void mlx5e_unregister_vport_reps(struct mlx5e_priv *priv); +bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv); +int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv); +void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv); + +int mlx5e_get_offload_stats(int attr_id, const struct net_device *dev, void *sp); +bool mlx5e_has_offload_stats(const struct net_device *dev, int attr_id); + +int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr); +void mlx5e_handle_rx_cqe_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe); + +#endif /* __MLX5E_REP_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index ae66fad98244..d717573b73da 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -39,6 +39,7 @@ #include "en.h" #include "en_tc.h" #include "eswitch.h" +#include "en_rep.h" #include "ipoib.h" static inline bool mlx5e_rx_hw_stamp(struct mlx5e_tstamp *tstamp) @@ -809,7 +810,8 @@ void mlx5e_handle_rx_cqe_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe) { struct net_device *netdev = rq->netdev; struct mlx5e_priv *priv = netdev_priv(netdev); - struct mlx5_eswitch_rep *rep = priv->ppriv; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep = rpriv->rep; struct mlx5e_rx_wqe *wqe; struct sk_buff *skb; __be16 wqe_counter_be; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 21b5bcaf4bc0..7d379a189b63 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -46,6 +46,7 @@ #include #include "en.h" #include "en_tc.h" +#include "en_rep.h" #include "eswitch.h" #include "vxlan.h" @@ -702,16 +703,18 @@ static int parse_cls_flower(struct mlx5e_priv *priv, { struct mlx5_core_dev *dev = priv->mdev; struct mlx5_eswitch *esw = dev->priv.eswitch; - struct mlx5_eswitch_rep *rep = priv->ppriv; + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5_eswitch_rep *rep; u8 min_inline; int err; err = __parse_cls_flower(priv, spec, f, &min_inline); - if (!err && (flow->flags & MLX5E_TC_FLOW_ESWITCH) && - rep->vport != FDB_UPLINK_VPORT) { - if (esw->offloads.inline_mode != MLX5_INLINE_MODE_NONE && - esw->offloads.inline_mode < min_inline) { + if (!err && (flow->flags & MLX5E_TC_FLOW_ESWITCH)) { + rep = rpriv->rep; + if (rep->vport != FDB_UPLINK_VPORT && + (esw->offloads.inline_mode != MLX5_INLINE_MODE_NONE && + esw->offloads.inline_mode < min_inline)) { netdev_warn(priv->netdev, "Flow is not offloaded due to min inline setting, required %d actual %d\n", min_inline, esw->offloads.inline_mode); @@ -1439,6 +1442,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, struct mlx5e_tc_flow *flow) { struct mlx5_esw_flow_attr *attr = flow->esw_attr; + struct mlx5e_rep_priv *rpriv = priv->ppriv; struct ip_tunnel_info *info = NULL; const struct tc_action *a; LIST_HEAD(actions); @@ -1449,7 +1453,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, return -EINVAL; memset(attr, 0, sizeof(*attr)); - attr->in_rep = priv->ppriv; + attr->in_rep = rpriv->rep; tcf_exts_to_list(exts, &actions); list_for_each_entry(a, &actions, list) { @@ -1481,7 +1485,8 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, attr->action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_COUNT; out_priv = netdev_priv(out_dev); - attr->out_rep = out_priv->ppriv; + rpriv = out_priv->ppriv; + attr->out_rep = rpriv->rep; } else if (encap) { err = mlx5e_attach_encap(priv, info, out_dev, attr); @@ -1492,7 +1497,8 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_COUNT; out_priv = netdev_priv(attr->encap->out_dev); - attr->out_rep = out_priv->ppriv; + rpriv = out_priv->ppriv; + attr->out_rep = rpriv->rep; } else { pr_err("devices %s %s not on same switch HW, can't offload forwarding\n", priv->netdev->name, out_dev->name); -- cgit From 45247bf2985edd983bb241dfa71f0c17713c8e7b Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 25 Apr 2017 15:30:08 +0300 Subject: net/mlx5: Remove encap entry pointer from the eswitch flow attributes Encap wise, the tc eswitch flow attribute struct needs to have only the encap ID which is programmed later to the HW and none of the higher level encap params, fix that. This patch doesn't change any functionality. Signed-off-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 29 ++++++++++++---------- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 2 +- .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 2 +- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 7d379a189b63..c7b034eeb149 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1362,16 +1362,18 @@ out: static int mlx5e_attach_encap(struct mlx5e_priv *priv, struct ip_tunnel_info *tun_info, struct net_device *mirred_dev, - struct mlx5_esw_flow_attr *attr) + struct net_device **encap_dev, + struct mlx5e_tc_flow *flow) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct net_device *up_dev = mlx5_eswitch_get_uplink_netdev(esw); - struct mlx5e_priv *up_priv = netdev_priv(up_dev); unsigned short family = ip_tunnel_info_af(tun_info); + struct mlx5e_priv *up_priv = netdev_priv(up_dev); + struct mlx5_esw_flow_attr *attr = flow->esw_attr; struct ip_tunnel_key *key = &tun_info->key; struct mlx5_encap_entry *e; struct net_device *out_dev; - int tunnel_type, err = -EOPNOTSUPP; + int tunnel_type, err = 0; uintptr_t hash_key; bool found = false; @@ -1406,10 +1408,8 @@ vxlan_encap_offload_err: } } - if (found) { - attr->encap = e; - return 0; - } + if (found) + goto attach_flow; e = kzalloc(sizeof(*e), GFP_KERNEL); if (!e) @@ -1427,10 +1427,14 @@ vxlan_encap_offload_err: if (err) goto out_err; - attr->encap = e; hash_add_rcu(esw->offloads.encap_tbl, &e->encap_hlist, hash_key); - return err; +attach_flow: + list_add(&flow->encap, &e->flows); + *encap_dev = e->out_dev; + attr->encap_id = e->encap_id; + + return 0; out_err: kfree(e); @@ -1475,7 +1479,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, if (is_tcf_mirred_egress_redirect(a)) { int ifindex = tcf_mirred_ifindex(a); - struct net_device *out_dev; + struct net_device *out_dev, *encap_dev = NULL; struct mlx5e_priv *out_priv; out_dev = __dev_get_by_index(dev_net(priv->netdev), ifindex); @@ -1489,14 +1493,13 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, attr->out_rep = rpriv->rep; } else if (encap) { err = mlx5e_attach_encap(priv, info, - out_dev, attr); + out_dev, &encap_dev, flow); if (err) return err; - list_add(&flow->encap, &attr->encap->flows); attr->action |= MLX5_FLOW_CONTEXT_ACTION_ENCAP | MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_COUNT; - out_priv = netdev_priv(attr->encap->out_dev); + out_priv = netdev_priv(encap_dev); rpriv = out_priv->ppriv; attr->out_rep = rpriv->rep; } else { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 1e7f21be1233..9056961689fa 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -308,7 +308,7 @@ struct mlx5_esw_flow_attr { int action; u16 vlan; bool vlan_handled; - struct mlx5_encap_entry *encap; + u32 encap_id; u32 mod_hdr_id; }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index d297354e8ea9..f991f669047e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -92,7 +92,7 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, flow_act.modify_id = attr->mod_hdr_id; if (attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) - flow_act.encap_id = attr->encap->encap_id; + flow_act.encap_id = attr->encap_id; rule = mlx5_add_flow_rules((struct mlx5_flow_table *)esw->fdb_table.fdb, spec, &flow_act, dest, i); -- cgit From c1ae11521b1e87523553ddd3c1aa88be3d710c96 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Tue, 25 Apr 2017 16:19:26 +0300 Subject: net/mlx5e: Move the encap entry structure from the eswitch header The encap entry structure isn't manipulated by the eswitch code, hence it can/needs to be removed from the eswitch header. Do that, and change it to have mlx5e_ prefix. This patch doesn't change any functionality. Signed-off-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rep.h | 13 +++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 11 +++++------ drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 13 ------------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h index b6595a699dc1..425cb1b0bf02 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h @@ -33,6 +33,7 @@ #ifndef __MLX5E_REP_H__ #define __MLX5E_REP_H__ +#include #include "eswitch.h" #include "en.h" @@ -40,6 +41,18 @@ struct mlx5e_rep_priv { struct mlx5_eswitch_rep *rep; }; +struct mlx5e_encap_entry { + struct hlist_node encap_hlist; + struct list_head flows; + u32 encap_id; + struct neighbour *n; + struct ip_tunnel_info tun_info; + unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ + + struct net_device *out_dev; + int tunnel_type; +}; + void mlx5e_register_vport_reps(struct mlx5e_priv *priv); void mlx5e_unregister_vport_reps(struct mlx5e_priv *priv); bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index c7b034eeb149..3582ebcd4173 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -264,9 +264,9 @@ static void mlx5e_detach_encap(struct mlx5e_priv *priv, list_del(&flow->encap); if (list_empty(next)) { - struct mlx5_encap_entry *e; + struct mlx5e_encap_entry *e; - e = list_entry(next, struct mlx5_encap_entry, flows); + e = list_entry(next, struct mlx5e_encap_entry, flows); if (e->n) { mlx5_encap_dealloc(priv->mdev, e->encap_id); neigh_release(e->n); @@ -1211,7 +1211,7 @@ static void gen_vxlan_header_ipv6(struct net_device *out_dev, static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv, struct net_device *mirred_dev, - struct mlx5_encap_entry *e, + struct mlx5e_encap_entry *e, struct net_device **out_dev) { int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size); @@ -1285,9 +1285,8 @@ out: static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv, struct net_device *mirred_dev, - struct mlx5_encap_entry *e, + struct mlx5e_encap_entry *e, struct net_device **out_dev) - { int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size); int ipv6_encap_size = ETH_HLEN + sizeof(struct ipv6hdr) + VXLAN_HLEN; @@ -1371,7 +1370,7 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv, struct mlx5e_priv *up_priv = netdev_priv(up_dev); struct mlx5_esw_flow_attr *attr = flow->esw_attr; struct ip_tunnel_key *key = &tun_info->key; - struct mlx5_encap_entry *e; + struct mlx5e_encap_entry *e; struct net_device *out_dev; int tunnel_type, err = 0; uintptr_t hash_key; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 9056961689fa..751a673de97a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -36,7 +36,6 @@ #include #include #include -#include #include #define MLX5_MAX_UC_PER_VPORT(dev) \ @@ -289,18 +288,6 @@ enum { #define MLX5_FLOW_CONTEXT_ACTION_VLAN_POP 0x4000 #define MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH 0x8000 -struct mlx5_encap_entry { - struct hlist_node encap_hlist; - struct list_head flows; - u32 encap_id; - struct neighbour *n; - struct ip_tunnel_info tun_info; - unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ - - struct net_device *out_dev; - int tunnel_type; -}; - struct mlx5_esw_flow_attr { struct mlx5_eswitch_rep *in_rep; struct mlx5_eswitch_rep *out_rep; -- cgit From 1a8552bd81af5f4f19b26ac58e8c85866d4f7de8 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 2 Feb 2017 15:01:02 +0200 Subject: net/mlx5e: Remove output device parameter from create encap header helpers definition Passing output device parameter to the helper functions that deal with creation of encapsulation headers is redundant. Output device parameter can be defined inside those helpers, no need to pass it. Refactor the code by removing the parameter from the function signature. This patch doesn't change any functionality. Signed-off-by: Hadar Hen Zion Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 29 ++++++++++++------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 3582ebcd4173..25ecffa1a3df 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1211,12 +1211,12 @@ static void gen_vxlan_header_ipv6(struct net_device *out_dev, static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv, struct net_device *mirred_dev, - struct mlx5e_encap_entry *e, - struct net_device **out_dev) + struct mlx5e_encap_entry *e) { int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size); int ipv4_encap_size = ETH_HLEN + sizeof(struct iphdr) + VXLAN_HLEN; struct ip_tunnel_key *tun_key = &e->tun_info.key; + struct net_device *out_dev; struct neighbour *n = NULL; struct flowi4 fl4 = {}; char *encap_header; @@ -1245,7 +1245,7 @@ static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv, fl4.daddr = tun_key->u.ipv4.dst; fl4.saddr = tun_key->u.ipv4.src; - err = mlx5e_route_lookup_ipv4(priv, mirred_dev, out_dev, + err = mlx5e_route_lookup_ipv4(priv, mirred_dev, &out_dev, &fl4, &n, &ttl); if (err) goto out; @@ -1257,13 +1257,13 @@ static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv, } e->n = n; - e->out_dev = *out_dev; + e->out_dev = out_dev; - neigh_ha_snapshot(e->h_dest, n, *out_dev); + neigh_ha_snapshot(e->h_dest, n, out_dev); switch (e->tunnel_type) { case MLX5_HEADER_TYPE_VXLAN: - gen_vxlan_header_ipv4(*out_dev, encap_header, + gen_vxlan_header_ipv4(out_dev, encap_header, ipv4_encap_size, e->h_dest, ttl, fl4.daddr, fl4.saddr, tun_key->tp_dst, @@ -1285,12 +1285,12 @@ out: static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv, struct net_device *mirred_dev, - struct mlx5e_encap_entry *e, - struct net_device **out_dev) + struct mlx5e_encap_entry *e) { int max_encap_size = MLX5_CAP_ESW(priv->mdev, max_encap_header_size); int ipv6_encap_size = ETH_HLEN + sizeof(struct ipv6hdr) + VXLAN_HLEN; struct ip_tunnel_key *tun_key = &e->tun_info.key; + struct net_device *out_dev; struct neighbour *n = NULL; struct flowi6 fl6 = {}; char *encap_header; @@ -1320,7 +1320,7 @@ static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv, fl6.daddr = tun_key->u.ipv6.dst; fl6.saddr = tun_key->u.ipv6.src; - err = mlx5e_route_lookup_ipv6(priv, mirred_dev, out_dev, + err = mlx5e_route_lookup_ipv6(priv, mirred_dev, &out_dev, &fl6, &n, &ttl); if (err) goto out; @@ -1332,13 +1332,13 @@ static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv, } e->n = n; - e->out_dev = *out_dev; + e->out_dev = out_dev; - neigh_ha_snapshot(e->h_dest, n, *out_dev); + neigh_ha_snapshot(e->h_dest, n, out_dev); switch (e->tunnel_type) { case MLX5_HEADER_TYPE_VXLAN: - gen_vxlan_header_ipv6(*out_dev, encap_header, + gen_vxlan_header_ipv6(out_dev, encap_header, ipv6_encap_size, e->h_dest, ttl, &fl6.daddr, &fl6.saddr, tun_key->tp_dst, @@ -1371,7 +1371,6 @@ static int mlx5e_attach_encap(struct mlx5e_priv *priv, struct mlx5_esw_flow_attr *attr = flow->esw_attr; struct ip_tunnel_key *key = &tun_info->key; struct mlx5e_encap_entry *e; - struct net_device *out_dev; int tunnel_type, err = 0; uintptr_t hash_key; bool found = false; @@ -1419,9 +1418,9 @@ vxlan_encap_offload_err: INIT_LIST_HEAD(&e->flows); if (family == AF_INET) - err = mlx5e_create_encap_header_ipv4(priv, mirred_dev, e, &out_dev); + err = mlx5e_create_encap_header_ipv4(priv, mirred_dev, e); else if (family == AF_INET6) - err = mlx5e_create_encap_header_ipv6(priv, mirred_dev, e, &out_dev); + err = mlx5e_create_encap_header_ipv6(priv, mirred_dev, e); if (err) goto out_err; -- cgit From 0b67a38fe699ee60c56d193d33c9c7392cabaea7 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Tue, 14 Feb 2017 14:03:45 +0200 Subject: net/mlx5e: Use flag to properly monitor a flow rule offloading state Instead of relaying on the 'flow->rule' pointer value which can be valid or invalid (in case the FW returns an error while trying to offload the rule), monitor the rule state using a flag. In downstream patch which adds support to IP tunneling neigh update flow, a TC rule could be cached in the driver and not offloaded into the HW. In this case, the flow handle pointer stays NULL. Check the offloaded flag to properly deal with rules which are currently not offloaded when querying rule statistics. This patch doesn't add any new functionality. Signed-off-by: Hadar Hen Zion Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 25ecffa1a3df..2a9289b8a33b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -59,6 +59,7 @@ struct mlx5_nic_flow_attr { enum { MLX5E_TC_FLOW_ESWITCH = BIT(0), MLX5E_TC_FLOW_NIC = BIT(1), + MLX5E_TC_FLOW_OFFLOADED = BIT(2), }; struct mlx5e_tc_flow { @@ -245,7 +246,8 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5_esw_flow_attr *attr = flow->esw_attr; - mlx5_eswitch_del_offloaded_rule(esw, flow->rule, flow->esw_attr); + if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) + mlx5_eswitch_del_offloaded_rule(esw, flow->rule, flow->esw_attr); mlx5_eswitch_del_vlan_action(esw, flow->esw_attr); @@ -1591,6 +1593,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, goto err_free; } + flow->flags |= MLX5E_TC_FLOW_OFFLOADED; err = rhashtable_insert_fast(&tc->ht, &flow->node, tc->ht_params); if (err) @@ -1646,6 +1649,9 @@ int mlx5e_stats_flower(struct mlx5e_priv *priv, if (!flow) return -EINVAL; + if (!(flow->flags & MLX5E_TC_FLOW_OFFLOADED)) + return 0; + counter = mlx5_flow_rule_counter(flow->rule); if (!counter) return 0; -- cgit From 033354d501862ca1f58caa1e5c5775712e1bff17 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 2 Feb 2017 16:14:21 +0200 Subject: net/mlx5e: Read neigh parameters with proper locking The nud_state and hardware address fields are protected by the neighbour lock, we should acquire it before accessing those parameters. Use this lock to avoid inconsistency between the neighbour validity state and it's hardware address. Signed-off-by: Hadar Hen Zion Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 2a9289b8a33b..ae07fe6473bb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1223,6 +1223,7 @@ static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv, struct flowi4 fl4 = {}; char *encap_header; int ttl, err; + u8 nud_state; if (max_encap_size < ipv4_encap_size) { mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n", @@ -1252,7 +1253,12 @@ static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv, if (err) goto out; - if (!(n->nud_state & NUD_VALID)) { + read_lock_bh(&n->lock); + nud_state = n->nud_state; + ether_addr_copy(e->h_dest, n->ha); + read_unlock_bh(&n->lock); + + if (!(nud_state & NUD_VALID)) { pr_warn("%s: can't offload, neighbour to %pI4 invalid\n", __func__, &fl4.daddr); err = -EOPNOTSUPP; goto out; @@ -1261,8 +1267,6 @@ static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv, e->n = n; e->out_dev = out_dev; - neigh_ha_snapshot(e->h_dest, n, out_dev); - switch (e->tunnel_type) { case MLX5_HEADER_TYPE_VXLAN: gen_vxlan_header_ipv4(out_dev, encap_header, @@ -1297,6 +1301,7 @@ static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv, struct flowi6 fl6 = {}; char *encap_header; int err, ttl = 0; + u8 nud_state; if (max_encap_size < ipv6_encap_size) { mlx5_core_warn(priv->mdev, "encap size %d too big, max supported is %d\n", @@ -1327,7 +1332,12 @@ static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv, if (err) goto out; - if (!(n->nud_state & NUD_VALID)) { + read_lock_bh(&n->lock); + nud_state = n->nud_state; + ether_addr_copy(e->h_dest, n->ha); + read_unlock_bh(&n->lock); + + if (!(nud_state & NUD_VALID)) { pr_warn("%s: can't offload, neighbour to %pI6 invalid\n", __func__, &fl6.daddr); err = -EOPNOTSUPP; goto out; @@ -1336,8 +1346,6 @@ static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv, e->n = n; e->out_dev = out_dev; - neigh_ha_snapshot(e->h_dest, n, out_dev); - switch (e->tunnel_type) { case MLX5_HEADER_TYPE_VXLAN: gen_vxlan_header_ipv6(out_dev, encap_header, -- cgit From 37b498ff238549b30c9e70d4e45f522fd53b8994 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 2 Feb 2017 16:43:35 +0200 Subject: net/mlx5e: Add neighbour hash table to the representors Add hash table to the representors which is to be used by the next patch to save neighbours information in the driver. In order to offload IP tunnel encapsulation rules, the driver must find the tunnel dst neighbour according to the output device and the destination address given by the user. The next patch will cache the neighbors information in the driver to allow support in neigh update flow for tunnel encap rules. The neighbour entries are also saved in a list so we easily iterate over them when querying statistics in order to provide 'used' feedback to the kernel neighbour NUD core. Signed-off-by: Hadar Hen Zion Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 107 +++++++++++++++++++++-- drivers/net/ethernet/mellanox/mlx5/core/en_rep.h | 30 +++++++ 2 files changed, 129 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 8e82b11afd99..52ea7f1c0973 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -224,6 +224,68 @@ void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) mlx5_eswitch_sqs2vport_stop(esw, rep); } +static const struct rhashtable_params mlx5e_neigh_ht_params = { + .head_offset = offsetof(struct mlx5e_neigh_hash_entry, rhash_node), + .key_offset = offsetof(struct mlx5e_neigh_hash_entry, m_neigh), + .key_len = sizeof(struct mlx5e_neigh), + .automatic_shrinking = true, +}; + +static int mlx5e_rep_neigh_init(struct mlx5e_rep_priv *rpriv) +{ + struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update; + + INIT_LIST_HEAD(&neigh_update->neigh_list); + return rhashtable_init(&neigh_update->neigh_ht, &mlx5e_neigh_ht_params); +} + +static void mlx5e_rep_neigh_cleanup(struct mlx5e_rep_priv *rpriv) +{ + struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update; + + rhashtable_destroy(&neigh_update->neigh_ht); +} + +static int mlx5e_rep_neigh_entry_insert(struct mlx5e_priv *priv, + struct mlx5e_neigh_hash_entry *nhe) +{ + struct mlx5e_rep_priv *rpriv = priv->ppriv; + int err; + + err = rhashtable_insert_fast(&rpriv->neigh_update.neigh_ht, + &nhe->rhash_node, + mlx5e_neigh_ht_params); + if (err) + return err; + + list_add(&nhe->neigh_list, &rpriv->neigh_update.neigh_list); + + return err; +} + +static void mlx5e_rep_neigh_entry_remove(struct mlx5e_priv *priv, + struct mlx5e_neigh_hash_entry *nhe) +{ + struct mlx5e_rep_priv *rpriv = priv->ppriv; + + list_del(&nhe->neigh_list); + + rhashtable_remove_fast(&rpriv->neigh_update.neigh_ht, + &nhe->rhash_node, + mlx5e_neigh_ht_params); +} + +static struct mlx5e_neigh_hash_entry * +mlx5e_rep_neigh_entry_lookup(struct mlx5e_priv *priv, + struct mlx5e_neigh *m_neigh) +{ + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update; + + return rhashtable_lookup_fast(&neigh_update->neigh_ht, m_neigh, + mlx5e_neigh_ht_params); +} + static int mlx5e_rep_open(struct net_device *dev) { struct mlx5e_priv *priv = netdev_priv(dev); @@ -540,19 +602,33 @@ static struct mlx5e_profile mlx5e_rep_profile = { static int mlx5e_nic_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) { - struct net_device *netdev = rep->netdev; - struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_priv *priv = netdev_priv(rep->netdev); + struct mlx5e_rep_priv *rpriv = priv->ppriv; + + int err; + + if (test_bit(MLX5E_STATE_OPENED, &priv->state)) { + err = mlx5e_add_sqs_fwd_rules(priv); + if (err) + return err; + } + + err = mlx5e_rep_neigh_init(rpriv); + if (err) + goto err_remove_sqs; - if (test_bit(MLX5E_STATE_OPENED, &priv->state)) - return mlx5e_add_sqs_fwd_rules(priv); return 0; + +err_remove_sqs: + mlx5e_remove_sqs_fwd_rules(priv); + return err; } static void mlx5e_nic_rep_unload(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) { - struct net_device *netdev = rep->netdev; - struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_priv *priv = netdev_priv(rep->netdev); + struct mlx5e_rep_priv *rpriv = priv->ppriv; if (test_bit(MLX5E_STATE_OPENED, &priv->state)) mlx5e_remove_sqs_fwd_rules(priv); @@ -560,6 +636,8 @@ mlx5e_nic_rep_unload(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) /* clean (and re-init) existing uplink offloaded TC rules */ mlx5e_tc_cleanup(priv); mlx5e_tc_init(priv); + + mlx5e_rep_neigh_cleanup(rpriv); } static int @@ -591,15 +669,25 @@ mlx5e_vport_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) goto err_destroy_netdev; } + err = mlx5e_rep_neigh_init(rpriv); + if (err) { + pr_warn("Failed to initialized neighbours handling for vport %d\n", + rep->vport); + goto err_detach_netdev; + } + err = register_netdev(netdev); if (err) { pr_warn("Failed to register representor netdev for vport %d\n", rep->vport); - goto err_detach_netdev; + goto err_neigh_cleanup; } return 0; +err_neigh_cleanup: + mlx5e_rep_neigh_cleanup(rpriv); + err_detach_netdev: mlx5e_detach_netdev(netdev_priv(netdev)); @@ -615,9 +703,12 @@ mlx5e_vport_rep_unload(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) { struct net_device *netdev = rep->netdev; struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_rep_priv *rpriv = priv->ppriv; void *ppriv = priv->ppriv; - unregister_netdev(netdev); + unregister_netdev(rep->netdev); + + mlx5e_rep_neigh_cleanup(rpriv); mlx5e_detach_netdev(priv); mlx5e_destroy_netdev(priv); kfree(ppriv); /* mlx5e_rep_priv */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h index 425cb1b0bf02..99f6b5f41070 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h @@ -34,11 +34,41 @@ #define __MLX5E_REP_H__ #include +#include #include "eswitch.h" #include "en.h" +struct mlx5e_neigh_update_table { + struct rhashtable neigh_ht; + /* Save the neigh hash entries in a list in addition to the hash table + * (neigh_ht). In order to iterate easily over the neigh entries. + * Used for stats query. + */ + struct list_head neigh_list; +}; + struct mlx5e_rep_priv { struct mlx5_eswitch_rep *rep; + struct mlx5e_neigh_update_table neigh_update; +}; + +struct mlx5e_neigh { + struct net_device *dev; + union { + __be32 v4; + struct in6_addr v6; + } dst_ip; +}; + +struct mlx5e_neigh_hash_entry { + struct rhash_head rhash_node; + struct mlx5e_neigh m_neigh; + + /* Save the neigh hash entry in a list on the representor in + * addition to the hash table. In order to iterate easily over the + * neighbour entries. Used for stats query. + */ + struct list_head neigh_list; }; struct mlx5e_encap_entry { -- cgit From 232c001398ae8406dc0daf07b14e6ec9a5562719 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Mon, 20 Mar 2017 12:56:47 +0200 Subject: net/mlx5e: Add support to neighbour update flow In order to offload TC encap rules, the driver does a lookup for the IP tunnel neighbour according to the output device and the destination IP given by the user. To keep tracking after the validity state of such neighbours, we keep the neighbours information (pair of device pointer and destination IP) in a hash table maintained at the relevant egress representor and register to get NETEVENT_NEIGH_UPDATE events. When getting neighbour update netevent, we search for a match among the cached neighbours entries used for encapsulation. In case the neighbour isn't valid, we can't offload the flow into the HW. We cache the flow (requested matching and actions) in the driver and offload the rule later, when the neighbour is resolved and becomes valid. When a flow is only cached in the driver and not offloaded into HW yet, we use EAGAIN return value to mark it internally, the TC ndo still returns success. Listen to kernel neighbour update netevents to trace relevant neighbours validity state: 1. If a neighbour becomes valid, offload the related rules to HW. 2. If the neighbour becomes invalid, remove the related rules from HW. 3. If the neighbour mac address was changed, update the encap header. Remove all the offloaded rules using the old encap header from the HW and insert new rules to HW with updated encap header. Access to the neighbors hash table is protected by RTNL lock of its caller or by the table's spinlock. Details of the locking/synchronization among the different actions applied on the neighbour table: Add/remove operations - protected by RTNL lock of its caller (all TC commands are protected by RTNL lock). Add and remove operations are initiated only when the user inserts/removes a TC rule into/from the driver. Lookup/remove operations - since the lookup operation is done from netevent notifier block, RTNL lock can't be used (atomic context). Use the table's spin lock to protect lookups from TC user removal operation. bh is used since netevent can be called from a softirq context. Lookup/add operations - The hash table access functions are taking care of the protection between lookup and add operations. When adding/removing encap headers and rules to/from the HW, RTNL lock is used. It can happen when: 1. The user inserts/removes a TC rule into/from the driver (TC commands are protected by RTNL lock of it's caller). 2. The driver gets neighbour notification event, which reports about neighbour validity status change. Before adding/removing encap headers and rules to/from the HW, RTNL lock is taken. A neighbour hash table entry should be freed when its encap list is empty. Since The neighbour update netevent notification schedules a neighbour update work that uses the neighbour hash entry, it can't be freed unconditionally when the encap list becomes empty during TC delete rule flow. Use reference count to protect from freeing neighbour hash table entry while it's still in use. When the user asks to unregister a netdvice used by one of the neigbours, neighbour removal notification is received. Then we take a reference on the neighbour and don't free it until the relevant encap entries (and flows) are marked as invalid (not offloaded) and removed from HW. As long as the encap entry is still valid (checked under RTNL lock) we can safely access the neighbour device saved on mlx5e_neigh struct. Signed-off-by: Hadar Hen Zion Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 230 +++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx5/core/en_rep.h | 38 +++- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 202 +++++++++++++++---- drivers/net/ethernet/mellanox/mlx5/core/en_tc.h | 6 + drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 1 + 5 files changed, 434 insertions(+), 43 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 52ea7f1c0973..730de6b7e46e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include "eswitch.h" #include "en.h" @@ -224,6 +226,140 @@ void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) mlx5_eswitch_sqs2vport_stop(esw, rep); } +static void mlx5e_rep_neigh_entry_hold(struct mlx5e_neigh_hash_entry *nhe) +{ + refcount_inc(&nhe->refcnt); +} + +static void mlx5e_rep_neigh_entry_release(struct mlx5e_neigh_hash_entry *nhe) +{ + if (refcount_dec_and_test(&nhe->refcnt)) + kfree(nhe); +} + +static void mlx5e_rep_update_flows(struct mlx5e_priv *priv, + struct mlx5e_encap_entry *e, + bool neigh_connected, + unsigned char ha[ETH_ALEN]) +{ + struct ethhdr *eth = (struct ethhdr *)e->encap_header; + + ASSERT_RTNL(); + + if ((!neigh_connected && (e->flags & MLX5_ENCAP_ENTRY_VALID)) || + !ether_addr_equal(e->h_dest, ha)) + mlx5e_tc_encap_flows_del(priv, e); + + if (neigh_connected && !(e->flags & MLX5_ENCAP_ENTRY_VALID)) { + ether_addr_copy(e->h_dest, ha); + ether_addr_copy(eth->h_dest, ha); + + mlx5e_tc_encap_flows_add(priv, e); + } +} + +static void mlx5e_rep_neigh_update(struct work_struct *work) +{ + struct mlx5e_neigh_hash_entry *nhe = + container_of(work, struct mlx5e_neigh_hash_entry, neigh_update_work); + struct neighbour *n = nhe->n; + struct mlx5e_encap_entry *e; + unsigned char ha[ETH_ALEN]; + struct mlx5e_priv *priv; + bool neigh_connected; + bool encap_connected; + u8 nud_state, dead; + + rtnl_lock(); + + /* If these parameters are changed after we release the lock, + * we'll receive another event letting us know about it. + * We use this lock to avoid inconsistency between the neigh validity + * and it's hw address. + */ + read_lock_bh(&n->lock); + memcpy(ha, n->ha, ETH_ALEN); + nud_state = n->nud_state; + dead = n->dead; + read_unlock_bh(&n->lock); + + neigh_connected = (nud_state & NUD_VALID) && !dead; + + list_for_each_entry(e, &nhe->encap_list, encap_list) { + encap_connected = !!(e->flags & MLX5_ENCAP_ENTRY_VALID); + priv = netdev_priv(e->out_dev); + + if (encap_connected != neigh_connected || + !ether_addr_equal(e->h_dest, ha)) + mlx5e_rep_update_flows(priv, e, neigh_connected, ha); + } + mlx5e_rep_neigh_entry_release(nhe); + rtnl_unlock(); + neigh_release(n); +} + +static struct mlx5e_neigh_hash_entry * +mlx5e_rep_neigh_entry_lookup(struct mlx5e_priv *priv, + struct mlx5e_neigh *m_neigh); + +static int mlx5e_rep_netevent_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct mlx5e_rep_priv *rpriv = container_of(nb, struct mlx5e_rep_priv, + neigh_update.netevent_nb); + struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update; + struct net_device *netdev = rpriv->rep->netdev; + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_neigh_hash_entry *nhe = NULL; + struct mlx5e_neigh m_neigh = {}; + struct neighbour *n; + + switch (event) { + case NETEVENT_NEIGH_UPDATE: + n = ptr; +#if IS_ENABLED(CONFIG_IPV6) + if (n->tbl != ipv6_stub->nd_tbl && n->tbl != &arp_tbl) +#else + if (n->tbl != &arp_tbl) +#endif + return NOTIFY_DONE; + + m_neigh.dev = n->dev; + memcpy(&m_neigh.dst_ip, n->primary_key, n->tbl->key_len); + + /* We are in atomic context and can't take RTNL mutex, so use + * spin_lock_bh to lookup the neigh table. bh is used since + * netevent can be called from a softirq context. + */ + spin_lock_bh(&neigh_update->encap_lock); + nhe = mlx5e_rep_neigh_entry_lookup(priv, &m_neigh); + if (!nhe) { + spin_unlock_bh(&neigh_update->encap_lock); + return NOTIFY_DONE; + } + + /* This assignment is valid as long as the the neigh reference + * is taken + */ + nhe->n = n; + + /* Take a reference to ensure the neighbour and mlx5 encap + * entry won't be destructed until we drop the reference in + * delayed work. + */ + neigh_hold(n); + mlx5e_rep_neigh_entry_hold(nhe); + + if (!queue_work(priv->wq, &nhe->neigh_update_work)) { + mlx5e_rep_neigh_entry_release(nhe); + neigh_release(n); + } + spin_unlock_bh(&neigh_update->encap_lock); + break; + } + return NOTIFY_DONE; +} + static const struct rhashtable_params mlx5e_neigh_ht_params = { .head_offset = offsetof(struct mlx5e_neigh_hash_entry, rhash_node), .key_offset = offsetof(struct mlx5e_neigh_hash_entry, m_neigh), @@ -234,14 +370,34 @@ static const struct rhashtable_params mlx5e_neigh_ht_params = { static int mlx5e_rep_neigh_init(struct mlx5e_rep_priv *rpriv) { struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update; + int err; + + err = rhashtable_init(&neigh_update->neigh_ht, &mlx5e_neigh_ht_params); + if (err) + return err; INIT_LIST_HEAD(&neigh_update->neigh_list); - return rhashtable_init(&neigh_update->neigh_ht, &mlx5e_neigh_ht_params); + spin_lock_init(&neigh_update->encap_lock); + + rpriv->neigh_update.netevent_nb.notifier_call = mlx5e_rep_netevent_event; + err = register_netevent_notifier(&rpriv->neigh_update.netevent_nb); + if (err) + goto out_err; + return 0; + +out_err: + rhashtable_destroy(&neigh_update->neigh_ht); + return err; } static void mlx5e_rep_neigh_cleanup(struct mlx5e_rep_priv *rpriv) { struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update; + struct mlx5e_priv *priv = netdev_priv(rpriv->rep->netdev); + + unregister_netevent_notifier(&neigh_update->netevent_nb); + + flush_workqueue(priv->wq); /* flush neigh update works */ rhashtable_destroy(&neigh_update->neigh_ht); } @@ -268,13 +424,19 @@ static void mlx5e_rep_neigh_entry_remove(struct mlx5e_priv *priv, { struct mlx5e_rep_priv *rpriv = priv->ppriv; + spin_lock_bh(&rpriv->neigh_update.encap_lock); + list_del(&nhe->neigh_list); rhashtable_remove_fast(&rpriv->neigh_update.neigh_ht, &nhe->rhash_node, mlx5e_neigh_ht_params); + spin_unlock_bh(&rpriv->neigh_update.encap_lock); } +/* This function must only be called under RTNL lock or under the + * representor's encap_lock in case RTNL mutex can't be held. + */ static struct mlx5e_neigh_hash_entry * mlx5e_rep_neigh_entry_lookup(struct mlx5e_priv *priv, struct mlx5e_neigh *m_neigh) @@ -286,6 +448,72 @@ mlx5e_rep_neigh_entry_lookup(struct mlx5e_priv *priv, mlx5e_neigh_ht_params); } +static int mlx5e_rep_neigh_entry_create(struct mlx5e_priv *priv, + struct mlx5e_encap_entry *e, + struct mlx5e_neigh_hash_entry **nhe) +{ + int err; + + *nhe = kzalloc(sizeof(**nhe), GFP_KERNEL); + if (!*nhe) + return -ENOMEM; + + memcpy(&(*nhe)->m_neigh, &e->m_neigh, sizeof(e->m_neigh)); + INIT_WORK(&(*nhe)->neigh_update_work, mlx5e_rep_neigh_update); + INIT_LIST_HEAD(&(*nhe)->encap_list); + refcount_set(&(*nhe)->refcnt, 1); + + err = mlx5e_rep_neigh_entry_insert(priv, *nhe); + if (err) + goto out_free; + return 0; + +out_free: + kfree(*nhe); + return err; +} + +static void mlx5e_rep_neigh_entry_destroy(struct mlx5e_priv *priv, + struct mlx5e_neigh_hash_entry *nhe) +{ + /* The neigh hash entry must be removed from the hash table regardless + * of the reference count value, so it won't be found by the next + * neigh notification call. The neigh hash entry reference count is + * incremented only during creation and neigh notification calls and + * protects from freeing the nhe struct. + */ + mlx5e_rep_neigh_entry_remove(priv, nhe); + mlx5e_rep_neigh_entry_release(nhe); +} + +int mlx5e_rep_encap_entry_attach(struct mlx5e_priv *priv, + struct mlx5e_encap_entry *e) +{ + struct mlx5e_neigh_hash_entry *nhe; + int err; + + nhe = mlx5e_rep_neigh_entry_lookup(priv, &e->m_neigh); + if (!nhe) { + err = mlx5e_rep_neigh_entry_create(priv, e, &nhe); + if (err) + return err; + } + list_add(&e->encap_list, &nhe->encap_list); + return 0; +} + +void mlx5e_rep_encap_entry_detach(struct mlx5e_priv *priv, + struct mlx5e_encap_entry *e) +{ + struct mlx5e_neigh_hash_entry *nhe; + + list_del(&e->encap_list); + nhe = mlx5e_rep_neigh_entry_lookup(priv, &e->m_neigh); + + if (list_empty(&nhe->encap_list)) + mlx5e_rep_neigh_entry_destroy(priv, nhe); +} + static int mlx5e_rep_open(struct net_device *dev) { struct mlx5e_priv *priv = netdev_priv(dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h index 99f6b5f41070..e4d0ea5246fd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h @@ -45,6 +45,9 @@ struct mlx5e_neigh_update_table { * Used for stats query. */ struct list_head neigh_list; + /* protect lookup/remove operations */ + spinlock_t encap_lock; + struct notifier_block netevent_nb; }; struct mlx5e_rep_priv { @@ -69,18 +72,46 @@ struct mlx5e_neigh_hash_entry { * neighbour entries. Used for stats query. */ struct list_head neigh_list; + + /* encap list sharing the same neigh */ + struct list_head encap_list; + + /* valid only when the neigh reference is taken during + * neigh_update_work workqueue callback. + */ + struct neighbour *n; + struct work_struct neigh_update_work; + + /* neigh hash entry can be deleted only when the refcount is zero. + * refcount is needed to avoid neigh hash entry removal by TC, while + * it's used by the neigh notification call. + */ + refcount_t refcnt; +}; + +enum { + /* set when the encap entry is successfully offloaded into HW */ + MLX5_ENCAP_ENTRY_VALID = BIT(0), }; struct mlx5e_encap_entry { + /* neigh hash entry list of encaps sharing the same neigh */ + struct list_head encap_list; + struct mlx5e_neigh m_neigh; + /* a node of the eswitch encap hash table which keeping all the encap + * entries + */ struct hlist_node encap_hlist; struct list_head flows; u32 encap_id; - struct neighbour *n; struct ip_tunnel_info tun_info; unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ struct net_device *out_dev; int tunnel_type; + u8 flags; + char *encap_header; + int encap_size; }; void mlx5e_register_vport_reps(struct mlx5e_priv *priv); @@ -95,4 +126,9 @@ bool mlx5e_has_offload_stats(const struct net_device *dev, int attr_id); int mlx5e_attr_get(struct net_device *dev, struct switchdev_attr *attr); void mlx5e_handle_rx_cqe_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe); +int mlx5e_rep_encap_entry_attach(struct mlx5e_priv *priv, + struct mlx5e_encap_entry *e); +void mlx5e_rep_encap_entry_detach(struct mlx5e_priv *priv, + struct mlx5e_encap_entry *e); + #endif /* __MLX5E_REP_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index ae07fe6473bb..624dbfe31a0e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -45,8 +45,8 @@ #include #include #include "en.h" -#include "en_tc.h" #include "en_rep.h" +#include "en_tc.h" #include "eswitch.h" #include "vxlan.h" @@ -246,19 +246,75 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct mlx5_esw_flow_attr *attr = flow->esw_attr; - if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) + if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) { + flow->flags &= ~MLX5E_TC_FLOW_OFFLOADED; mlx5_eswitch_del_offloaded_rule(esw, flow->rule, flow->esw_attr); + } mlx5_eswitch_del_vlan_action(esw, flow->esw_attr); - if (flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) + if (flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP) { mlx5e_detach_encap(priv, flow); + kvfree(flow->esw_attr->parse_attr); + } if (flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) mlx5_modify_header_dealloc(priv->mdev, attr->mod_hdr_id); } +void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv, + struct mlx5e_encap_entry *e) +{ + struct mlx5e_tc_flow *flow; + int err; + + err = mlx5_encap_alloc(priv->mdev, e->tunnel_type, + e->encap_size, e->encap_header, + &e->encap_id); + if (err) { + mlx5_core_warn(priv->mdev, "Failed to offload cached encapsulation header, %d\n", + err); + return; + } + e->flags |= MLX5_ENCAP_ENTRY_VALID; + + list_for_each_entry(flow, &e->flows, encap) { + flow->esw_attr->encap_id = e->encap_id; + flow->rule = mlx5e_tc_add_fdb_flow(priv, + flow->esw_attr->parse_attr, + flow); + if (IS_ERR(flow->rule)) { + err = PTR_ERR(flow->rule); + mlx5_core_warn(priv->mdev, "Failed to update cached encapsulation flow, %d\n", + err); + continue; + } + flow->flags |= MLX5E_TC_FLOW_OFFLOADED; + } +} + +void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv, + struct mlx5e_encap_entry *e) +{ + struct mlx5e_tc_flow *flow; + struct mlx5_fc *counter; + + list_for_each_entry(flow, &e->flows, encap) { + if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) { + flow->flags &= ~MLX5E_TC_FLOW_OFFLOADED; + counter = mlx5_flow_rule_counter(flow->rule); + mlx5_del_flow_rules(flow->rule); + mlx5_fc_destroy(priv->mdev, counter); + } + } + + if (e->flags & MLX5_ENCAP_ENTRY_VALID) { + e->flags &= ~MLX5_ENCAP_ENTRY_VALID; + mlx5_encap_dealloc(priv->mdev, e->encap_id); + } +} + static void mlx5e_detach_encap(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow) { @@ -269,11 +325,13 @@ static void mlx5e_detach_encap(struct mlx5e_priv *priv, struct mlx5e_encap_entry *e; e = list_entry(next, struct mlx5e_encap_entry, flows); - if (e->n) { + mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e); + + if (e->flags & MLX5_ENCAP_ENTRY_VALID) mlx5_encap_dealloc(priv->mdev, e->encap_id); - neigh_release(e->n); - } + hlist_del_rcu(&e->encap_hlist); + kfree(e->encap_header); kfree(e); } } @@ -1253,20 +1311,27 @@ static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv, if (err) goto out; + /* used by mlx5e_detach_encap to lookup a neigh hash table + * entry in the neigh hash table when a user deletes a rule + */ + e->m_neigh.dev = n->dev; + memcpy(&e->m_neigh.dst_ip, n->primary_key, n->tbl->key_len); + e->out_dev = out_dev; + + /* It's importent to add the neigh to the hash table before checking + * the neigh validity state. So if we'll get a notification, in case the + * neigh changes it's validity state, we would find the relevant neigh + * in the hash. + */ + err = mlx5e_rep_encap_entry_attach(netdev_priv(out_dev), e); + if (err) + goto out; + read_lock_bh(&n->lock); nud_state = n->nud_state; ether_addr_copy(e->h_dest, n->ha); read_unlock_bh(&n->lock); - if (!(nud_state & NUD_VALID)) { - pr_warn("%s: can't offload, neighbour to %pI4 invalid\n", __func__, &fl4.daddr); - err = -EOPNOTSUPP; - goto out; - } - - e->n = n; - e->out_dev = out_dev; - switch (e->tunnel_type) { case MLX5_HEADER_TYPE_VXLAN: gen_vxlan_header_ipv4(out_dev, encap_header, @@ -1277,15 +1342,32 @@ static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv, break; default: err = -EOPNOTSUPP; - goto out; + goto destroy_neigh_entry; + } + e->encap_size = ipv4_encap_size; + e->encap_header = encap_header; + + if (!(nud_state & NUD_VALID)) { + neigh_event_send(n, NULL); + neigh_release(n); + return -EAGAIN; } err = mlx5_encap_alloc(priv->mdev, e->tunnel_type, ipv4_encap_size, encap_header, &e->encap_id); + if (err) + goto destroy_neigh_entry; + + e->flags |= MLX5_ENCAP_ENTRY_VALID; + neigh_release(n); + return err; + +destroy_neigh_entry: + mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e); out: - if (err && n) - neigh_release(n); kfree(encap_header); + if (n) + neigh_release(n); return err; } @@ -1332,20 +1414,27 @@ static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv, if (err) goto out; + /* used by mlx5e_detach_encap to lookup a neigh hash table + * entry in the neigh hash table when a user deletes a rule + */ + e->m_neigh.dev = n->dev; + memcpy(&e->m_neigh.dst_ip, n->primary_key, n->tbl->key_len); + e->out_dev = out_dev; + + /* It's importent to add the neigh to the hash table before checking + * the neigh validity state. So if we'll get a notification, in case the + * neigh changes it's validity state, we would find the relevant neigh + * in the hash. + */ + err = mlx5e_rep_encap_entry_attach(netdev_priv(out_dev), e); + if (err) + goto out; + read_lock_bh(&n->lock); nud_state = n->nud_state; ether_addr_copy(e->h_dest, n->ha); read_unlock_bh(&n->lock); - if (!(nud_state & NUD_VALID)) { - pr_warn("%s: can't offload, neighbour to %pI6 invalid\n", __func__, &fl6.daddr); - err = -EOPNOTSUPP; - goto out; - } - - e->n = n; - e->out_dev = out_dev; - switch (e->tunnel_type) { case MLX5_HEADER_TYPE_VXLAN: gen_vxlan_header_ipv6(out_dev, encap_header, @@ -1356,15 +1445,33 @@ static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv, break; default: err = -EOPNOTSUPP; - goto out; + goto destroy_neigh_entry; + } + + e->encap_size = ipv6_encap_size; + e->encap_header = encap_header; + + if (!(nud_state & NUD_VALID)) { + neigh_event_send(n, NULL); + neigh_release(n); + return -EAGAIN; } err = mlx5_encap_alloc(priv->mdev, e->tunnel_type, ipv6_encap_size, encap_header, &e->encap_id); + if (err) + goto destroy_neigh_entry; + + e->flags |= MLX5_ENCAP_ENTRY_VALID; + neigh_release(n); + return err; + +destroy_neigh_entry: + mlx5e_rep_encap_entry_detach(netdev_priv(e->out_dev), e); out: - if (err && n) - neigh_release(n); kfree(encap_header); + if (n) + neigh_release(n); return err; } @@ -1432,7 +1539,7 @@ vxlan_encap_offload_err: else if (family == AF_INET6) err = mlx5e_create_encap_header_ipv6(priv, mirred_dev, e); - if (err) + if (err && err != -EAGAIN) goto out_err; hash_add_rcu(esw->offloads.encap_tbl, &e->encap_hlist, hash_key); @@ -1440,9 +1547,10 @@ vxlan_encap_offload_err: attach_flow: list_add(&flow->encap, &e->flows); *encap_dev = e->out_dev; - attr->encap_id = e->encap_id; + if (e->flags & MLX5_ENCAP_ENTRY_VALID) + attr->encap_id = e->encap_id; - return 0; + return err; out_err: kfree(e); @@ -1459,7 +1567,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, const struct tc_action *a; LIST_HEAD(actions); bool encap = false; - int err; + int err = 0; if (tc_no_actions(exts)) return -EINVAL; @@ -1502,7 +1610,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, } else if (encap) { err = mlx5e_attach_encap(priv, info, out_dev, &encap_dev, flow); - if (err) + if (err && err != -EAGAIN) return err; attr->action |= MLX5_FLOW_CONTEXT_ACTION_ENCAP | MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | @@ -1510,6 +1618,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, out_priv = netdev_priv(encap_dev); rpriv = out_priv->ppriv; attr->out_rep = rpriv->rep; + attr->parse_attr = parse_attr; } else { pr_err("devices %s %s not on same switch HW, can't offload forwarding\n", priv->netdev->name, out_dev->name); @@ -1549,7 +1658,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, return -EINVAL; } - return 0; + return err; } int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, @@ -1587,7 +1696,7 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, if (flow->flags & MLX5E_TC_FLOW_ESWITCH) { err = parse_tc_fdb_actions(priv, f->exts, parse_attr, flow); if (err < 0) - goto err_free; + goto err_handle_encap_flow; flow->rule = mlx5e_tc_add_fdb_flow(priv, parse_attr, flow); } else { err = parse_tc_nic_actions(priv, f->exts, parse_attr, flow); @@ -1607,15 +1716,27 @@ int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, if (err) goto err_del_rule; - goto out; + if (flow->flags & MLX5E_TC_FLOW_ESWITCH && + !(flow->esw_attr->action & MLX5_FLOW_CONTEXT_ACTION_ENCAP)) + kvfree(parse_attr); + return err; err_del_rule: mlx5e_tc_del_flow(priv, flow); +err_handle_encap_flow: + if (err == -EAGAIN) { + err = rhashtable_insert_fast(&tc->ht, &flow->node, + tc->ht_params); + if (err) + mlx5e_tc_del_flow(priv, flow); + else + return 0; + } + err_free: - kfree(flow); -out: kvfree(parse_attr); + kfree(flow); return err; } @@ -1634,7 +1755,6 @@ int mlx5e_delete_flower(struct mlx5e_priv *priv, mlx5e_tc_del_flow(priv, flow); - kfree(flow); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index 34bf903fc886..278c7a646a55 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -46,6 +46,12 @@ int mlx5e_delete_flower(struct mlx5e_priv *priv, int mlx5e_stats_flower(struct mlx5e_priv *priv, struct tc_cls_flower_offload *f); +struct mlx5e_encap_entry; +void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv, + struct mlx5e_encap_entry *e); +void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv, + struct mlx5e_encap_entry *e); + static inline int mlx5e_tc_num_filters(struct mlx5e_priv *priv) { return atomic_read(&priv->fs.tc.ht.nelems); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 751a673de97a..55beda6bf134 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -297,6 +297,7 @@ struct mlx5_esw_flow_attr { bool vlan_handled; u32 encap_id; u32 mod_hdr_id; + struct mlx5e_tc_flow_parse_attr *parse_attr; }; int mlx5_eswitch_sqs2vport_start(struct mlx5_eswitch *esw, -- cgit From f6dfb4c3f2161c23ab2939dd1b5f133dcdf147c6 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Fri, 24 Feb 2017 12:16:33 +0200 Subject: net/mlx5e: Update neighbour 'used' state using HW flow rules counters When IP tunnel encapsulation rules are offloaded, the kernel can't see the traffic of the offloaded flow. The neighbour for the IP tunnel destination of the offloaded flow can mistakenly become STALE and deleted by the kernel since its 'used' value wasn't changed. To make sure that a neighbour which is used by the HW won't become STALE, we proactively update the neighbour 'used' value every DELAY_PROBE_TIME period, when packets were matched and counted by the HW for one of the tunnel encap flows related to this neighbour. The periodic task that updates the used neighbours is scheduled when a tunnel encap rule is successfully offloaded into HW and keeps re-scheduling itself as long as the representor's neighbours list isn't empty. Add, remove, lookup and status change operations done over the representor's neighbours list or the neighbour hash entry encaps list are all serialized by RTNL lock. Signed-off-by: Hadar Hen Zion Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 52 +++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/en_rep.h | 11 ++++ drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 58 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx5/core/en_tc.h | 3 ++ drivers/net/ethernet/mellanox/mlx5/core/fs_core.h | 5 ++ .../net/ethernet/mellanox/mlx5/core/fs_counters.c | 24 ++++++++- include/linux/mlx5/driver.h | 1 + 7 files changed, 152 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 730de6b7e46e..af61b10b85bf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -41,6 +41,7 @@ #include "en.h" #include "en_rep.h" #include "en_tc.h" +#include "fs_core.h" static const char mlx5e_rep_driver_name[] = "mlx5e_rep"; @@ -226,6 +227,51 @@ void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) mlx5_eswitch_sqs2vport_stop(esw, rep); } +static void mlx5e_rep_neigh_update_init_interval(struct mlx5e_rep_priv *rpriv) +{ +#if IS_ENABLED(CONFIG_IPV6) + unsigned long ipv6_interval = NEIGH_VAR(&ipv6_stub->nd_tbl->parms, + DELAY_PROBE_TIME); +#else + unsigned long ipv6_interval = ~0UL; +#endif + unsigned long ipv4_interval = NEIGH_VAR(&arp_tbl.parms, + DELAY_PROBE_TIME); + struct net_device *netdev = rpriv->rep->netdev; + struct mlx5e_priv *priv = netdev_priv(netdev); + + rpriv->neigh_update.min_interval = min_t(unsigned long, ipv6_interval, ipv4_interval); + mlx5_fc_update_sampling_interval(priv->mdev, rpriv->neigh_update.min_interval); +} + +void mlx5e_rep_queue_neigh_stats_work(struct mlx5e_priv *priv) +{ + struct mlx5e_rep_priv *rpriv = priv->ppriv; + struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update; + + mlx5_fc_queue_stats_work(priv->mdev, + &neigh_update->neigh_stats_work, + neigh_update->min_interval); +} + +static void mlx5e_rep_neigh_stats_work(struct work_struct *work) +{ + struct mlx5e_rep_priv *rpriv = container_of(work, struct mlx5e_rep_priv, + neigh_update.neigh_stats_work.work); + struct net_device *netdev = rpriv->rep->netdev; + struct mlx5e_priv *priv = netdev_priv(netdev); + struct mlx5e_neigh_hash_entry *nhe; + + rtnl_lock(); + if (!list_empty(&rpriv->neigh_update.neigh_list)) + mlx5e_rep_queue_neigh_stats_work(priv); + + list_for_each_entry(nhe, &rpriv->neigh_update.neigh_list, neigh_list) + mlx5e_tc_update_neigh_used_value(nhe); + + rtnl_unlock(); +} + static void mlx5e_rep_neigh_entry_hold(struct mlx5e_neigh_hash_entry *nhe) { refcount_inc(&nhe->refcnt); @@ -325,6 +371,7 @@ static int mlx5e_rep_netevent_event(struct notifier_block *nb, return NOTIFY_DONE; m_neigh.dev = n->dev; + m_neigh.family = n->ops->family; memcpy(&m_neigh.dst_ip, n->primary_key, n->tbl->key_len); /* We are in atomic context and can't take RTNL mutex, so use @@ -378,6 +425,9 @@ static int mlx5e_rep_neigh_init(struct mlx5e_rep_priv *rpriv) INIT_LIST_HEAD(&neigh_update->neigh_list); spin_lock_init(&neigh_update->encap_lock); + INIT_DELAYED_WORK(&neigh_update->neigh_stats_work, + mlx5e_rep_neigh_stats_work); + mlx5e_rep_neigh_update_init_interval(rpriv); rpriv->neigh_update.netevent_nb.notifier_call = mlx5e_rep_netevent_event; err = register_netevent_notifier(&rpriv->neigh_update.netevent_nb); @@ -399,6 +449,8 @@ static void mlx5e_rep_neigh_cleanup(struct mlx5e_rep_priv *rpriv) flush_workqueue(priv->wq); /* flush neigh update works */ + cancel_delayed_work_sync(&rpriv->neigh_update.neigh_stats_work); + rhashtable_destroy(&neigh_update->neigh_ht); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h index e4d0ea5246fd..a0a1a7a1d6c0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h @@ -48,6 +48,8 @@ struct mlx5e_neigh_update_table { /* protect lookup/remove operations */ spinlock_t encap_lock; struct notifier_block netevent_nb; + struct delayed_work neigh_stats_work; + unsigned long min_interval; /* jiffies */ }; struct mlx5e_rep_priv { @@ -61,6 +63,7 @@ struct mlx5e_neigh { __be32 v4; struct in6_addr v6; } dst_ip; + int family; }; struct mlx5e_neigh_hash_entry { @@ -87,6 +90,12 @@ struct mlx5e_neigh_hash_entry { * it's used by the neigh notification call. */ refcount_t refcnt; + + /* Save the last reported time offloaded trafic pass over one of the + * neigh hash entry flows. Use it to periodically update the neigh + * 'used' value and avoid neigh deleting by the kernel. + */ + unsigned long reported_lastuse; }; enum { @@ -131,4 +140,6 @@ int mlx5e_rep_encap_entry_attach(struct mlx5e_priv *priv, void mlx5e_rep_encap_entry_detach(struct mlx5e_priv *priv, struct mlx5e_encap_entry *e); +void mlx5e_rep_queue_neigh_stats_work(struct mlx5e_priv *priv); + #endif /* __MLX5E_REP_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 624dbfe31a0e..11c27e4fadf6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "en.h" #include "en_rep.h" #include "en_tc.h" @@ -278,6 +279,7 @@ void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv, return; } e->flags |= MLX5_ENCAP_ENTRY_VALID; + mlx5e_rep_queue_neigh_stats_work(priv); list_for_each_entry(flow, &e->flows, encap) { flow->esw_attr->encap_id = e->encap_id; @@ -315,6 +317,58 @@ void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv, } } +void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe) +{ + struct mlx5e_neigh *m_neigh = &nhe->m_neigh; + u64 bytes, packets, lastuse = 0; + struct mlx5e_tc_flow *flow; + struct mlx5e_encap_entry *e; + struct mlx5_fc *counter; + struct neigh_table *tbl; + bool neigh_used = false; + struct neighbour *n; + + if (m_neigh->family == AF_INET) + tbl = &arp_tbl; +#if IS_ENABLED(CONFIG_IPV6) + else if (m_neigh->family == AF_INET6) + tbl = ipv6_stub->nd_tbl; +#endif + else + return; + + list_for_each_entry(e, &nhe->encap_list, encap_list) { + if (!(e->flags & MLX5_ENCAP_ENTRY_VALID)) + continue; + list_for_each_entry(flow, &e->flows, encap) { + if (flow->flags & MLX5E_TC_FLOW_OFFLOADED) { + counter = mlx5_flow_rule_counter(flow->rule); + mlx5_fc_query_cached(counter, &bytes, &packets, &lastuse); + if (time_after((unsigned long)lastuse, nhe->reported_lastuse)) { + neigh_used = true; + break; + } + } + } + } + + if (neigh_used) { + nhe->reported_lastuse = jiffies; + + /* find the relevant neigh according to the cached device and + * dst ip pair + */ + n = neigh_lookup(tbl, &m_neigh->dst_ip, m_neigh->dev); + if (!n) { + WARN(1, "The neighbour already freed\n"); + return; + } + + neigh_event_send(n, NULL); + neigh_release(n); + } +} + static void mlx5e_detach_encap(struct mlx5e_priv *priv, struct mlx5e_tc_flow *flow) { @@ -1315,6 +1369,7 @@ static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv, * entry in the neigh hash table when a user deletes a rule */ e->m_neigh.dev = n->dev; + e->m_neigh.family = n->ops->family; memcpy(&e->m_neigh.dst_ip, n->primary_key, n->tbl->key_len); e->out_dev = out_dev; @@ -1359,6 +1414,7 @@ static int mlx5e_create_encap_header_ipv4(struct mlx5e_priv *priv, goto destroy_neigh_entry; e->flags |= MLX5_ENCAP_ENTRY_VALID; + mlx5e_rep_queue_neigh_stats_work(netdev_priv(out_dev)); neigh_release(n); return err; @@ -1418,6 +1474,7 @@ static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv, * entry in the neigh hash table when a user deletes a rule */ e->m_neigh.dev = n->dev; + e->m_neigh.family = n->ops->family; memcpy(&e->m_neigh.dst_ip, n->primary_key, n->tbl->key_len); e->out_dev = out_dev; @@ -1463,6 +1520,7 @@ static int mlx5e_create_encap_header_ipv6(struct mlx5e_priv *priv, goto destroy_neigh_entry; e->flags |= MLX5_ENCAP_ENTRY_VALID; + mlx5e_rep_queue_neigh_stats_work(netdev_priv(out_dev)); neigh_release(n); return err; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index 278c7a646a55..ecbe30d808ae 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -52,6 +52,9 @@ void mlx5e_tc_encap_flows_add(struct mlx5e_priv *priv, void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv, struct mlx5e_encap_entry *e); +struct mlx5e_neigh_hash_entry; +void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe); + static inline int mlx5e_tc_num_filters(struct mlx5e_priv *priv) { return atomic_read(&priv->fs.tc.ht.nelems); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 577d056bf3df..81eafc7b9dd9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -199,6 +199,11 @@ struct mlx5_flow_root_namespace { int mlx5_init_fc_stats(struct mlx5_core_dev *dev); void mlx5_cleanup_fc_stats(struct mlx5_core_dev *dev); +void mlx5_fc_queue_stats_work(struct mlx5_core_dev *dev, + struct delayed_work *dwork, + unsigned long delay); +void mlx5_fc_update_sampling_interval(struct mlx5_core_dev *dev, + unsigned long interval); int mlx5_init_fs(struct mlx5_core_dev *dev); void mlx5_cleanup_fs(struct mlx5_core_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c index 7431f633de31..6507d8acc54d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c @@ -165,7 +165,8 @@ static void mlx5_fc_stats_work(struct work_struct *work) list_splice_tail_init(&fc_stats->addlist, &tmplist); if (!list_empty(&tmplist) || !RB_EMPTY_ROOT(&fc_stats->counters)) - queue_delayed_work(fc_stats->wq, &fc_stats->work, MLX5_FC_STATS_PERIOD); + queue_delayed_work(fc_stats->wq, &fc_stats->work, + fc_stats->sampling_interval); spin_unlock(&fc_stats->addlist_lock); @@ -200,7 +201,7 @@ static void mlx5_fc_stats_work(struct work_struct *work) node = mlx5_fc_stats_query(dev, counter, last->id); } - fc_stats->next_query = now + MLX5_FC_STATS_PERIOD; + fc_stats->next_query = now + fc_stats->sampling_interval; } struct mlx5_fc *mlx5_fc_create(struct mlx5_core_dev *dev, bool aging) @@ -265,6 +266,7 @@ int mlx5_init_fc_stats(struct mlx5_core_dev *dev) if (!fc_stats->wq) return -ENOMEM; + fc_stats->sampling_interval = MLX5_FC_STATS_PERIOD; INIT_DELAYED_WORK(&fc_stats->work, mlx5_fc_stats_work); return 0; @@ -317,3 +319,21 @@ void mlx5_fc_query_cached(struct mlx5_fc *counter, counter->lastbytes = c.bytes; counter->lastpackets = c.packets; } + +void mlx5_fc_queue_stats_work(struct mlx5_core_dev *dev, + struct delayed_work *dwork, + unsigned long delay) +{ + struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats; + + queue_delayed_work(fc_stats->wq, dwork, delay); +} + +void mlx5_fc_update_sampling_interval(struct mlx5_core_dev *dev, + unsigned long interval) +{ + struct mlx5_fc_stats *fc_stats = &dev->priv.fc_stats; + + fc_stats->sampling_interval = min_t(unsigned long, interval, + fc_stats->sampling_interval); +} diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index f50864626230..3fece51dcf13 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -540,6 +540,7 @@ struct mlx5_fc_stats { struct workqueue_struct *wq; struct delayed_work work; unsigned long next_query; + unsigned long sampling_interval; /* jiffies */ }; struct mlx5_eswitch; -- cgit From a2fa1fe5ad13e7f11b82291fc08bdc654fac741e Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Tue, 14 Feb 2017 11:20:24 +0200 Subject: net/mlx5e: Act on delay probe time updates The user can change delay_first_probe_time parameter through sysctl. Listen to NETEVENT_DELAY_PROBE_TIME_UPDATE notifications and update the intervals for updating the neighbours 'used' value periodic task and for flow HW counters query periodic task. Both of the intervals will be update only in case the new delay prob time value is lower the current interval. Since the driver saves only one min interval value and not per device, the users will be able to set lower interval value for updating neighbour 'used' value periodic task but they won't be able to schedule a higher interval for this periodic task. The used interval for scheduling neighbour 'used' value periodic task is the minimal delay prob time parameter ever seen by the driver. Signed-off-by: Hadar Hen Zion Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 39 ++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index af61b10b85bf..79462c0368a0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -358,7 +358,9 @@ static int mlx5e_rep_netevent_event(struct notifier_block *nb, struct mlx5e_priv *priv = netdev_priv(netdev); struct mlx5e_neigh_hash_entry *nhe = NULL; struct mlx5e_neigh m_neigh = {}; + struct neigh_parms *p; struct neighbour *n; + bool found = false; switch (event) { case NETEVENT_NEIGH_UPDATE: @@ -403,6 +405,43 @@ static int mlx5e_rep_netevent_event(struct notifier_block *nb, } spin_unlock_bh(&neigh_update->encap_lock); break; + + case NETEVENT_DELAY_PROBE_TIME_UPDATE: + p = ptr; + + /* We check the device is present since we don't care about + * changes in the default table, we only care about changes + * done per device delay prob time parameter. + */ +#if IS_ENABLED(CONFIG_IPV6) + if (!p->dev || (p->tbl != ipv6_stub->nd_tbl && p->tbl != &arp_tbl)) +#else + if (!p->dev || p->tbl != &arp_tbl) +#endif + return NOTIFY_DONE; + + /* We are in atomic context and can't take RTNL mutex, + * so use spin_lock_bh to walk the neigh list and look for + * the relevant device. bh is used since netevent can be + * called from a softirq context. + */ + spin_lock_bh(&neigh_update->encap_lock); + list_for_each_entry(nhe, &neigh_update->neigh_list, neigh_list) { + if (p->dev == nhe->m_neigh.dev) { + found = true; + break; + } + } + spin_unlock_bh(&neigh_update->encap_lock); + if (!found) + return NOTIFY_DONE; + + neigh_update->min_interval = min_t(unsigned long, + NEIGH_VAR(p, DELAY_PROBE_TIME), + neigh_update->min_interval); + mlx5_fc_update_sampling_interval(priv->mdev, + neigh_update->min_interval); + break; } return NOTIFY_DONE; } -- cgit From 1f5b1e47ee08f6c623db599b6c23ce7c20b79458 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Wed, 29 Mar 2017 11:46:10 +0300 Subject: net/mlx5e: Optimize poll ICOSQ completion queue UMR operations are more frequent and important. Check them first, and add a compiler branch predictor hint. According to current design, ICOSQ CQ can contain at most one pending CQE per napi. Poll function is optimized accordingly. Performance: Single-stream packet-rate tested with pktgen. Packets are dropped in tc level to zoom into driver data-path. Larger gain is expected for larger packet sizes, as BW is higher and UMR posts are more frequent. --------------------------------------------- packet size | before | after | gain | 64B | 4,092,370 | 4,113,306 | 0.5% | 1024B | 3,421,435 | 3,633,819 | 6.2% | Signed-off-by: Tariq Toukan Cc: kernel-team@fb.com Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 62 ++++++++++++----------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 43729ec35dfc..491e83d09b58 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -49,10 +49,40 @@ struct mlx5_cqe64 *mlx5e_get_cqe(struct mlx5e_cq *cq) return cqe; } +static inline void mlx5e_poll_ico_single_cqe(struct mlx5e_cq *cq, + struct mlx5e_icosq *sq, + struct mlx5_cqe64 *cqe, + u16 *sqcc) +{ + struct mlx5_wq_cyc *wq = &sq->wq; + u16 ci = be16_to_cpu(cqe->wqe_counter) & wq->sz_m1; + struct mlx5e_sq_wqe_info *icowi = &sq->db.ico_wqe[ci]; + struct mlx5e_rq *rq = &sq->channel->rq; + + prefetch(rq); + mlx5_cqwq_pop(&cq->wq); + *sqcc += icowi->num_wqebbs; + + if (unlikely((cqe->op_own >> 4) != MLX5_CQE_REQ)) { + WARN_ONCE(true, "mlx5e: Bad OP in ICOSQ CQE: 0x%x\n", + cqe->op_own); + return; + } + + if (likely(icowi->opcode == MLX5_OPCODE_UMR)) { + mlx5e_post_rx_mpwqe(rq); + return; + } + + if (unlikely(icowi->opcode != MLX5_OPCODE_NOP)) + WARN_ONCE(true, + "mlx5e: Bad OPCODE in ICOSQ WQE info: 0x%x\n", + icowi->opcode); +} + static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq) { struct mlx5e_icosq *sq = container_of(cq, struct mlx5e_icosq, cq); - struct mlx5_wq_cyc *wq; struct mlx5_cqe64 *cqe; u16 sqcc; @@ -63,39 +93,13 @@ static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq) if (likely(!cqe)) return; - wq = &sq->wq; - /* sq->cc must be updated only after mlx5_cqwq_update_db_record(), * otherwise a cq overrun may occur */ sqcc = sq->cc; - do { - u16 ci = be16_to_cpu(cqe->wqe_counter) & wq->sz_m1; - struct mlx5e_sq_wqe_info *icowi = &sq->db.ico_wqe[ci]; - - mlx5_cqwq_pop(&cq->wq); - sqcc += icowi->num_wqebbs; - - if (unlikely((cqe->op_own >> 4) != MLX5_CQE_REQ)) { - WARN_ONCE(true, "mlx5e: Bad OP in ICOSQ CQE: 0x%x\n", - cqe->op_own); - break; - } - - switch (icowi->opcode) { - case MLX5_OPCODE_NOP: - break; - case MLX5_OPCODE_UMR: - mlx5e_post_rx_mpwqe(&sq->channel->rq); - break; - default: - WARN_ONCE(true, - "mlx5e: Bad OPCODE in ICOSQ WQE info: 0x%x\n", - icowi->opcode); - } - - } while ((cqe = mlx5e_get_cqe(cq))); + /* by design, there's only a single cqe */ + mlx5e_poll_ico_single_cqe(cq, sq, cqe, &sqcc); mlx5_cqwq_update_db_record(&cq->wq); -- cgit From ad78af9b88f3d12134d6406468e7798e65c385c0 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Wed, 15 Feb 2017 17:05:39 +0200 Subject: net/mlx5e: Use prefetchw when a write is to follow "prefetchw()" prefetches the cacheline for write. Use it for skb->data, as soon we'll be copying the packet header there. Performance: Single-stream packet-rate tested with pktgen. Packets are dropped in tc level to zoom into driver data-path. Larger gain is expected for smaller packets, as less time is spent on handling SKB fragments, making the path shorter and the improvement more significant. --------------------------------------------- packet size | before | after | gain | 64B | 4,113,306 | 4,778,720 | 16% | 1024B | 3,633,819 | 3,950,593 | 8.7% | Signed-off-by: Tariq Toukan Cc: kernel-team@fb.com Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index d717573b73da..7b1566f0ae58 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -906,7 +906,7 @@ void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe) goto mpwrq_cqe_out; } - prefetch(skb->data); + prefetchw(skb->data); cqe_bcnt = mpwrq_get_cqe_byte_cnt(cqe); mlx5e_mpwqe_fill_rx_skb(rq, cqe, wi, cqe_bcnt, skb); -- cgit From b1b03bded1ee8b88ad85055a15d5f62ab6ddde44 Mon Sep 17 00:00:00 2001 From: Tariq Toukan Date: Mon, 3 Apr 2017 10:48:30 +0300 Subject: net/mlx5e: Use u8 as ownership type in mlx5e_get_cqe() CQE ownership indication is as small as a single bit. Use u8 to speedup the comparison. Signed-off-by: Tariq Toukan Cc: kernel-team@fb.com Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 491e83d09b58..5ca6714e3e02 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -37,8 +37,8 @@ struct mlx5_cqe64 *mlx5e_get_cqe(struct mlx5e_cq *cq) struct mlx5_cqwq *wq = &cq->wq; u32 ci = mlx5_cqwq_get_ci(wq); struct mlx5_cqe64 *cqe = mlx5_cqwq_get_wqe(wq, ci); - int cqe_ownership_bit = cqe->op_own & MLX5_CQE_OWNER_MASK; - int sw_ownership_val = mlx5_cqwq_get_wrap_cnt(wq) & 1; + u8 cqe_ownership_bit = cqe->op_own & MLX5_CQE_OWNER_MASK; + u8 sw_ownership_val = mlx5_cqwq_get_wrap_cnt(wq) & 1; if (cqe_ownership_bit != sw_ownership_val) return NULL; -- cgit From 0f6e4cf67411631be7f010fdb5c2d82c0c8705c0 Mon Sep 17 00:00:00 2001 From: Eran Ben Elisha Date: Wed, 26 Apr 2017 13:42:04 +0300 Subject: net/mlx5e: Disable HW LRO when PCI is slower than link on striding RQ We will activate the HW LRO only on servers with PCI BW > MAX LINK BW, or when PCI BW > 16Gbps. On other cases we do not want LRO by default as LRO sessions might get timeout and add redundant software overhead. Tested: ethtool -k | grep large-receive-offload On systems with and without the limitations. Signed-off-by: Eran Ben Elisha Cc: kernel-team@fb.com Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 1afaca96a30d..a61b71b6fff3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3785,6 +3785,12 @@ static bool cqe_compress_heuristic(u32 link_speed, u32 pci_bw) (pci_bw < 40000) && (pci_bw < link_speed)); } +static bool hw_lro_heuristic(u32 link_speed, u32 pci_bw) +{ + return !(link_speed && pci_bw && + (pci_bw <= 16000) && (pci_bw < link_speed)); +} + void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, u8 cq_period_mode) { params->rx_cq_period_mode = cq_period_mode; @@ -3829,6 +3835,11 @@ void mlx5e_build_nic_params(struct mlx5_core_dev *mdev, params->num_channels = max_channels; params->num_tc = 1; + mlx5e_get_max_linkspeed(mdev, &link_speed); + mlx5e_get_pci_bw(mdev, &pci_bw); + mlx5_core_dbg(mdev, "Max link speed = %d, PCI BW = %d\n", + link_speed, pci_bw); + /* SQ */ params->log_sq_size = is_kdump_kernel() ? MLX5E_PARAMS_MINIMUM_LOG_SQ_SIZE : @@ -3837,13 +3848,9 @@ void mlx5e_build_nic_params(struct mlx5_core_dev *mdev, /* set CQE compression */ params->rx_cqe_compress_def = false; if (MLX5_CAP_GEN(mdev, cqe_compression) && - MLX5_CAP_GEN(mdev, vport_group_manager)) { - mlx5e_get_max_linkspeed(mdev, &link_speed); - mlx5e_get_pci_bw(mdev, &pci_bw); - mlx5_core_dbg(mdev, "Max link speed = %d, PCI BW = %d\n", - link_speed, pci_bw); + MLX5_CAP_GEN(mdev, vport_group_manager)) params->rx_cqe_compress_def = cqe_compress_heuristic(link_speed, pci_bw); - } + MLX5E_SET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS, params->rx_cqe_compress_def); /* RQ */ @@ -3852,7 +3859,7 @@ void mlx5e_build_nic_params(struct mlx5_core_dev *mdev, /* HW LRO */ /* TODO: && MLX5_CAP_ETH(mdev, lro_cap) */ if (params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) - params->lro_en = true; + params->lro_en = hw_lro_heuristic(link_speed, pci_bw); params->lro_timeout = mlx5e_choose_lro_timeout(mdev, MLX5E_DEFAULT_LRO_TIMEOUT); /* CQ moderation params */ -- cgit From 0a0ab1d2cc5d5e68191488235074b5b30d793bb7 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Tue, 28 Feb 2017 16:52:21 -0600 Subject: net/mlx5: E-Switch, Avoid redundant memory allocation struct esw_mc_addr is a small struct that can be part of struct mlx5_eswitch. Define it as a field and not as a pointer and save the kzalloc call and then error flow handling. Signed-off-by: Eli Cohen Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.c | 20 ++------------------ drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 9 ++++++++- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 21bed3c3334d..2e34d95ea776 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -53,13 +53,6 @@ struct esw_uc_addr { u32 vport; }; -/* E-Switch MC FDB table hash node */ -struct esw_mc_addr { /* SRIOV only */ - struct l2addr_node node; - struct mlx5_flow_handle *uplink_rule; /* Forward to uplink rule */ - u32 refcnt; -}; - /* Vport UC/MC hash node */ struct vport_addr { struct l2addr_node node; @@ -817,7 +810,7 @@ static void esw_update_vport_mc_promisc(struct mlx5_eswitch *esw, u32 vport_num) static void esw_apply_vport_rx_mode(struct mlx5_eswitch *esw, u32 vport_num, bool promisc, bool mc_promisc) { - struct esw_mc_addr *allmulti_addr = esw->mc_promisc; + struct esw_mc_addr *allmulti_addr = &esw->mc_promisc; struct mlx5_vport *vport = &esw->vports[vport_num]; if (IS_ERR_OR_NULL(vport->allmulti_rule) != mc_promisc) @@ -1688,7 +1681,7 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) esw_info(esw->dev, "disable SRIOV: active vports(%d) mode(%d)\n", esw->enabled_vports, esw->mode); - mc_promisc = esw->mc_promisc; + mc_promisc = &esw->mc_promisc; nvports = esw->enabled_vports; for (i = 0; i < esw->total_vports; i++) @@ -1732,7 +1725,6 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) { int l2_table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table); int total_vports = MLX5_TOTAL_VPORTS(dev); - struct esw_mc_addr *mc_promisc; struct mlx5_eswitch *esw; int vport_num; int err; @@ -1761,13 +1753,6 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) } esw->l2_table.size = l2_table_size; - mc_promisc = kzalloc(sizeof(*mc_promisc), GFP_KERNEL); - if (!mc_promisc) { - err = -ENOMEM; - goto abort; - } - esw->mc_promisc = mc_promisc; - esw->work_queue = create_singlethread_workqueue("mlx5_esw_wq"); if (!esw->work_queue) { err = -ENOMEM; @@ -1835,7 +1820,6 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) esw->dev->priv.eswitch = NULL; destroy_workqueue(esw->work_queue); kfree(esw->l2_table.bitmap); - kfree(esw->mc_promisc); kfree(esw->offloads.vport_reps); kfree(esw->vports); kfree(esw); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 55beda6bf134..b746f62c8c79 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -212,6 +212,13 @@ struct mlx5_esw_offload { u8 encap; }; +/* E-Switch MC FDB table hash node */ +struct esw_mc_addr { /* SRIOV only */ + struct l2addr_node node; + struct mlx5_flow_handle *uplink_rule; /* Forward to uplink rule */ + u32 refcnt; +}; + struct mlx5_eswitch { struct mlx5_core_dev *dev; struct mlx5_l2_table l2_table; @@ -225,7 +232,7 @@ struct mlx5_eswitch { * and async SRIOV admin state changes */ struct mutex state_lock; - struct esw_mc_addr *mc_promisc; + struct esw_mc_addr mc_promisc; struct { bool enabled; -- cgit From f958315358bc37aede49dc3cd7e27e037994ae84 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 30 Apr 2017 06:51:40 -0700 Subject: Bluetooth: zero kpp input for key generation When generating new ECDH keys with kpp, the shared secret input needs to be set to NULL. Fix this by including kpp_request_set_input call. Fixes: 58771c1c ("Bluetooth: convert smp and selftest to crypto kpp API") Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/ecdh_helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/ecdh_helper.c b/net/bluetooth/ecdh_helper.c index 579684bfc322..2a65ca3fa8fa 100644 --- a/net/bluetooth/ecdh_helper.c +++ b/net/bluetooth/ecdh_helper.c @@ -191,6 +191,7 @@ bool generate_ecdh_keys(u8 public_key[64], u8 private_key[32]) goto free_all; sg_init_one(&dst, tmp, 64); + kpp_request_set_input(req, NULL, 0); kpp_request_set_output(req, &dst, 64); kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, ecdh_complete, &result); -- cgit From 71653eb64bcca6110c42aadfd50b8d54d3a88079 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 30 Apr 2017 06:51:41 -0700 Subject: Bluetooth: Add selftest for ECDH key generation Since the ECDH key generation takes a different path, it needs to be tested as well. For this generate the public debug key from the private debug key and compare both. This also moves the seeding of the private key into the SMP calling code to allow for easier re-use of the ECDH key generation helper. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/ecdh_helper.c | 3 --- net/bluetooth/smp.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/ecdh_helper.c b/net/bluetooth/ecdh_helper.c index 2a65ca3fa8fa..24d4e60f8c48 100644 --- a/net/bluetooth/ecdh_helper.c +++ b/net/bluetooth/ecdh_helper.c @@ -22,7 +22,6 @@ */ #include "ecdh_helper.h" -#include #include #include #include @@ -181,8 +180,6 @@ bool generate_ecdh_keys(u8 public_key[64], u8 private_key[32]) if (tries++ >= max_tries) goto free_all; - get_random_bytes(private_key, 32); - /* Set private Key */ p.key = (char *)private_key; crypto_ecdh_encode_key(buf, buf_len, &p); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 40e921a9cc17..14585edc9439 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -569,6 +569,9 @@ int smp_generate_oob(struct hci_dev *hdev, u8 hash[16], u8 rand[16]) smp->debug_key = true; } else { while (true) { + /* Seed private key with random number */ + get_random_bytes(smp->local_sk, 32); + /* Generate local key pair for Secure Connections */ if (!generate_ecdh_keys(smp->local_pk, smp->local_sk)) return -EIO; @@ -1895,6 +1898,9 @@ static u8 sc_send_public_key(struct smp_chan *smp) set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags); } else { while (true) { + /* Seed private key with random number */ + get_random_bytes(smp->local_sk, 32); + /* Generate local key pair for Secure Connections */ if (!generate_ecdh_keys(smp->local_pk, smp->local_sk)) return SMP_UNSPECIFIED; @@ -3483,6 +3489,32 @@ void smp_unregister(struct hci_dev *hdev) #if IS_ENABLED(CONFIG_BT_SELFTEST_SMP) +static inline void swap_digits(u64 *in, u64 *out, unsigned int ndigits) +{ + int i; + + for (i = 0; i < ndigits; i++) + out[i] = __swab64(in[ndigits - 1 - i]); +} + +static int __init test_debug_key(void) +{ + u8 pk[64], sk[32]; + + swap_digits((u64 *)debug_sk, (u64 *)sk, 4); + + if (!generate_ecdh_keys(pk, sk)) + return -EINVAL; + + if (memcmp(sk, debug_sk, 32)) + return -EINVAL; + + if (memcmp(pk, debug_pk, 64)) + return -EINVAL; + + return 0; +} + static int __init test_ah(struct crypto_cipher *tfm_aes) { const u8 irk[16] = { @@ -3738,6 +3770,12 @@ static int __init run_selftests(struct crypto_cipher *tfm_aes, calltime = ktime_get(); + err = test_debug_key(); + if (err) { + BT_ERR("debug_key test failed"); + goto done; + } + err = test_ah(tfm_aes); if (err) { BT_ERR("smp_ah test failed"); -- cgit From ae3696c167cc04d32634c4af82f43b446c5176b0 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Wed, 26 Apr 2017 12:06:28 +0200 Subject: net: macb: fix phy interrupt parsing Since 83a77e9ec415, the phydev irq is explicitly set to PHY_POLL when there is no pdata. It doesn't work on DT enabled platforms because the phydev irq is already set by libphy before. Fixes: 83a77e9ec415 ("net: macb: Added PCI wrapper for Platform Driver.") Signed-off-by: Alexandre Belloni Acked-by: Nicolas Ferre Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 5cbd1e7a926a..91f7492623d3 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -432,15 +432,17 @@ static int macb_mii_probe(struct net_device *dev) } pdata = dev_get_platdata(&bp->pdev->dev); - if (pdata && gpio_is_valid(pdata->phy_irq_pin)) { - ret = devm_gpio_request(&bp->pdev->dev, pdata->phy_irq_pin, - "phy int"); - if (!ret) { - phy_irq = gpio_to_irq(pdata->phy_irq_pin); - phydev->irq = (phy_irq < 0) ? PHY_POLL : phy_irq; + if (pdata) { + if (gpio_is_valid(pdata->phy_irq_pin)) { + ret = devm_gpio_request(&bp->pdev->dev, + pdata->phy_irq_pin, "phy int"); + if (!ret) { + phy_irq = gpio_to_irq(pdata->phy_irq_pin); + phydev->irq = (phy_irq < 0) ? PHY_POLL : phy_irq; + } + } else { + phydev->irq = PHY_POLL; } - } else { - phydev->irq = PHY_POLL; } /* attach the mac to the phy */ -- cgit From d1f496fd8f34a40458d0eda6be0655926559e546 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 26 Apr 2017 09:09:23 -0700 Subject: bpf: restore skb->sk before pskb_trim() call While testing a fix [1] in ___pskb_trim(), addressing the WARN_ON_ONCE() in skb_try_coalesce() reported by Andrey, I found that we had an skb with skb->sk set but no skb->destructor. This invalidated heuristic found in commit 158f323b9868 ("net: adjust skb->truesize in pskb_expand_head()") and in cited patch. Considering the BUG_ON(skb->sk) we have in skb_orphan(), we should restrain the temporary setting to a minimal section. [1] https://patchwork.ozlabs.org/patch/755570/ net: adjust skb->truesize in ___pskb_trim() Fixes: 8f917bba0042 ("bpf: pass sk to helper functions") Signed-off-by: Eric Dumazet Cc: Willem de Bruijn Cc: Andrey Konovalov Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- net/core/filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/filter.c b/net/core/filter.c index 9a37860a80fc..a253a6197e6b 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -98,8 +98,8 @@ int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap) skb->sk = sk; pkt_len = bpf_prog_run_save_cb(filter->prog, skb); - err = pkt_len ? pskb_trim(skb, max(cap, pkt_len)) : -EPERM; skb->sk = save_sk; + err = pkt_len ? pskb_trim(skb, max(cap, pkt_len)) : -EPERM; } rcu_read_unlock(); -- cgit From d68be71ea14d609a5f31534003319be5db422595 Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Wed, 26 Apr 2017 19:07:35 +0200 Subject: tcp: fix access to sk->sk_state in tcp_poll() avoid direct access to sk->sk_state when tcp_poll() is called on a socket using active TCP fastopen with deferred connect. Use local variable 'state', which stores the result of sk_state_load(), like it was done in commit 00fd38d938db ("tcp: ensure proper barriers in lockless contexts"). Fixes: 19f6d3f3c842 ("net/tcp-fastopen: Add new API support") Signed-off-by: Davide Caratti Acked-by: Wei Wang Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 059dad7deefe..1e4c76d2b827 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -533,7 +533,7 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait) if (tp->urg_data & TCP_URG_VALID) mask |= POLLPRI; - } else if (sk->sk_state == TCP_SYN_SENT && inet_sk(sk)->defer_connect) { + } else if (state == TCP_SYN_SENT && inet_sk(sk)->defer_connect) { /* Active TCP fastopen socket with defer_connect * Return POLLOUT so application can call write() * in order for kernel to generate SYN+data -- cgit From 5d4e3443287b28a6e9c3436adbadba2497880bd1 Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Wed, 26 Apr 2017 16:41:23 -0700 Subject: bpf: Fix inaccurate helper function description The description inside uapi/linux/bpf.h about bpf_get_socket_uid helper function is no longer valid. It returns overflowuid rather than 0 when failed. Signed-off-by: Chenbo Feng Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index e553529929f6..945a1f5f63c5 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -481,8 +481,7 @@ union bpf_attr { * u32 bpf_get_socket_uid(skb) * Get the owner uid of the socket stored inside sk_buff. * @skb: pointer to skb - * Return: uid of the socket owner on success or 0 if the socket pointer - * inside sk_buff is NULL + * Return: uid of the socket owner on success or overflowuid if failed. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ -- cgit From 55b218c12c1d03c6f415f6d3ce929741b15b2dd3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 27 Apr 2017 16:36:59 +0300 Subject: bnx2x: Replace custom scnprintf() Use scnprintf() when printing version instead of custom open coded variants. Signed-off-by: Andy Shevchenko Acked-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | 79 +++--------------------- 1 file changed, 9 insertions(+), 70 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index b209b7f6093e..2acc4f081818 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -6163,94 +6163,33 @@ static void bnx2x_link_int_ack(struct link_params *params, static int bnx2x_format_ver(u32 num, u8 *str, u16 *len) { - u8 *str_ptr = str; - u32 mask = 0xf0000000; - u8 shift = 8*4; - u8 digit; - u8 remove_leading_zeros = 1; + u16 ret; + if (*len < 10) { /* Need more than 10chars for this format */ - *str_ptr = '\0'; + *str = '\0'; (*len)--; return -EINVAL; } - while (shift > 0) { - shift -= 4; - digit = ((num & mask) >> shift); - if (digit == 0 && remove_leading_zeros) { - *str_ptr = '0'; - } else { - if (digit < 0xa) - *str_ptr = digit + '0'; - else - *str_ptr = digit - 0xa + 'a'; - - remove_leading_zeros = 0; - str_ptr++; - (*len)--; - } - mask = mask >> 4; - if (shift == 4*4) { - if (remove_leading_zeros) { - str_ptr++; - (*len)--; - } - *str_ptr = '.'; - str_ptr++; - (*len)--; - remove_leading_zeros = 1; - } - } - if (remove_leading_zeros) - (*len)--; + ret = scnprintf(str, *len, "%hx.%hx", num >> 16, num); + *len -= ret; return 0; } static int bnx2x_3_seq_format_ver(u32 num, u8 *str, u16 *len) { - u8 *str_ptr = str; - u32 mask = 0x00f00000; - u8 shift = 8*3; - u8 digit; - u8 remove_leading_zeros = 1; + u16 ret; if (*len < 10) { /* Need more than 10chars for this format */ - *str_ptr = '\0'; + *str = '\0'; (*len)--; return -EINVAL; } - while (shift > 0) { - shift -= 4; - digit = ((num & mask) >> shift); - if (digit == 0 && remove_leading_zeros) { - *str_ptr = '0'; - } else { - if (digit < 0xa) - *str_ptr = digit + '0'; - else - *str_ptr = digit - 0xa + 'a'; - - remove_leading_zeros = 0; - str_ptr++; - (*len)--; - } - mask = mask >> 4; - if ((shift == 4*4) || (shift == 4*2)) { - if (remove_leading_zeros) { - str_ptr++; - (*len)--; - } - *str_ptr = '.'; - str_ptr++; - (*len)--; - remove_leading_zeros = 1; - } - } - if (remove_leading_zeros) - (*len)--; + ret = scnprintf(str, *len, "%hhx.%hhx.%hhx", num >> 16, num >> 8, num); + *len -= ret; return 0; } -- cgit From b77f016726bded1fe3abb10272343d39df6b50f1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 27 Apr 2017 16:37:00 +0300 Subject: bnx2x: Reuse bnx2x_null_format_ver() Reuse bnx2x_null_format_ver() in functions where it's appropriated instead of open coded variant. Signed-off-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 2acc4f081818..6d11a958200f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -6161,14 +6161,20 @@ static void bnx2x_link_int_ack(struct link_params *params, } } +static int bnx2x_null_format_ver(u32 spirom_ver, u8 *str, u16 *len) +{ + str[0] = '\0'; + (*len)--; + return 0; +} + static int bnx2x_format_ver(u32 num, u8 *str, u16 *len) { u16 ret; if (*len < 10) { /* Need more than 10chars for this format */ - *str = '\0'; - (*len)--; + bnx2x_null_format_ver(num, str, len); return -EINVAL; } @@ -6183,8 +6189,7 @@ static int bnx2x_3_seq_format_ver(u32 num, u8 *str, u16 *len) if (*len < 10) { /* Need more than 10chars for this format */ - *str = '\0'; - (*len)--; + bnx2x_null_format_ver(num, str, len); return -EINVAL; } @@ -6193,13 +6198,6 @@ static int bnx2x_3_seq_format_ver(u32 num, u8 *str, u16 *len) return 0; } -static int bnx2x_null_format_ver(u32 spirom_ver, u8 *str, u16 *len) -{ - str[0] = '\0'; - (*len)--; - return 0; -} - int bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 *version, u16 len) { -- cgit From 90a1bb98167c75b65e2a0527ef9200d172be3442 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 27 Apr 2017 16:37:01 +0300 Subject: bnx2x: Get rid of useless temporary variable Replace pattern int status; ... status = func(...); return status; by return func(...); No functional change intented. Signed-off-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 6d11a958200f..7dd83d0ef0a0 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -10618,22 +10618,19 @@ static u8 bnx2x_848xx_read_status(struct bnx2x_phy *phy, static int bnx2x_8485x_format_ver(u32 raw_ver, u8 *str, u16 *len) { - int status = 0; u32 num; num = ((raw_ver & 0xF80) >> 7) << 16 | ((raw_ver & 0x7F) << 8) | ((raw_ver & 0xF000) >> 12); - status = bnx2x_3_seq_format_ver(num, str, len); - return status; + return bnx2x_3_seq_format_ver(num, str, len); } static int bnx2x_848xx_format_ver(u32 raw_ver, u8 *str, u16 *len) { - int status = 0; u32 spirom_ver; + spirom_ver = ((raw_ver & 0xF80) >> 7) << 16 | (raw_ver & 0x7F); - status = bnx2x_format_ver(spirom_ver, str, len); - return status; + return bnx2x_format_ver(spirom_ver, str, len); } static void bnx2x_8481_hw_reset(struct bnx2x_phy *phy, @@ -12484,13 +12481,12 @@ static int bnx2x_populate_ext_phy(struct bnx2x *bp, static int bnx2x_populate_phy(struct bnx2x *bp, u8 phy_index, u32 shmem_base, u32 shmem2_base, u8 port, struct bnx2x_phy *phy) { - int status = 0; phy->type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN; if (phy_index == INT_PHY) return bnx2x_populate_int_phy(bp, shmem_base, port, phy); - status = bnx2x_populate_ext_phy(bp, phy_index, shmem_base, shmem2_base, + + return bnx2x_populate_ext_phy(bp, phy_index, shmem_base, shmem2_base, port, phy); - return status; } static void bnx2x_phy_def_cfg(struct link_params *params, -- cgit From d074bf9600443403aa24fbc12c1f18eadc90f5aa Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 27 Apr 2017 21:24:35 +0200 Subject: vxlan: correctly handle ipv6.disable module parameter When IPv6 is compiled but disabled at runtime, __vxlan_sock_add returns -EAFNOSUPPORT. For metadata based tunnels, this causes failure of the whole operation of bringing up the tunnel. Ignore failure of IPv6 socket creation for metadata based tunnels caused by IPv6 not being available. Fixes: b1be00a6c39f ("vxlan: support both IPv4 and IPv6 sockets in a single vxlan device") Signed-off-by: Jiri Benc Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index ebc98bb17a51..84a86cbb31b9 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2822,17 +2822,21 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6) static int vxlan_sock_add(struct vxlan_dev *vxlan) { - bool ipv6 = vxlan->flags & VXLAN_F_IPV6; bool metadata = vxlan->flags & VXLAN_F_COLLECT_METADATA; + bool ipv6 = vxlan->flags & VXLAN_F_IPV6 || metadata; + bool ipv4 = !ipv6 || metadata; int ret = 0; RCU_INIT_POINTER(vxlan->vn4_sock, NULL); #if IS_ENABLED(CONFIG_IPV6) RCU_INIT_POINTER(vxlan->vn6_sock, NULL); - if (ipv6 || metadata) + if (ipv6) { ret = __vxlan_sock_add(vxlan, true); + if (ret < 0 && ret != -EAFNOSUPPORT) + ipv4 = false; + } #endif - if (!ret && (!ipv6 || metadata)) + if (ipv4) ret = __vxlan_sock_add(vxlan, false); if (ret < 0) vxlan_sock_release(vxlan); -- cgit From baf4d7860771287f30fbe9b6b2dc18b04361439d Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 27 Apr 2017 21:24:36 +0200 Subject: vxlan: do not output confusing error message The message "Cannot bind port X, err=Y" creates only confusion. In metadata based mode, failure of IPv6 socket creation is okay if IPv6 is disabled and no error message should be printed. But when IPv6 tunnel was requested, such failure is fatal. The vxlan_socket_create does not know when the error is harmless and when it's not. Instead of passing such information down to vxlan_socket_create, remove the message completely. It's not useful. We propagate the error code up to the user space and the port number comes from the user space. There's nothing in the message that the process creating vxlan interface does not know. Signed-off-by: Jiri Benc Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 84a86cbb31b9..328b4712683c 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2758,8 +2758,6 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6, sock = vxlan_create_sock(net, ipv6, port, flags); if (IS_ERR(sock)) { - pr_info("Cannot bind port %d, err=%ld\n", ntohs(port), - PTR_ERR(sock)); kfree(vs); return ERR_CAST(sock); } -- cgit From 5e0740c445e6ae4026f5e52456ff8d0be9725183 Mon Sep 17 00:00:00 2001 From: Girish Moodalbail Date: Thu, 27 Apr 2017 14:11:53 -0700 Subject: geneve: fix incorrect setting of UDP checksum flag Creating a geneve link with 'udpcsum' set results in a creation of link for which UDP checksum will NOT be computed on outbound packets, as can be seen below. 11: gen0: mtu 1500 qdisc noop state DOWN link/ether c2:85:27:b6:b4:15 brd ff:ff:ff:ff:ff:ff promiscuity 0 geneve id 200 remote 192.168.13.1 dstport 6081 noudpcsum Similarly, creating a link with 'noudpcsum' set results in a creation of link for which UDP checksum will be computed on outbound packets. Fixes: 9b4437a5b870 ("geneve: Unify LWT and netdev handling.") Signed-off-by: Girish Moodalbail Acked-by: Pravin B Shelar Acked-by: Lance Richardson Signed-off-by: David S. Miller --- drivers/net/geneve.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index 7074b40ebd7f..dec5d563ab19 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1244,7 +1244,7 @@ static int geneve_newlink(struct net *net, struct net_device *dev, metadata = true; if (data[IFLA_GENEVE_UDP_CSUM] && - !nla_get_u8(data[IFLA_GENEVE_UDP_CSUM])) + nla_get_u8(data[IFLA_GENEVE_UDP_CSUM])) info.key.tun_flags |= TUNNEL_CSUM; if (data[IFLA_GENEVE_UDP_ZERO_CSUM6_TX] && -- cgit From b5082df8019ac47ff1e6b6454480e39d0223fc46 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 27 Apr 2017 22:40:23 +0100 Subject: net: Initialise init_net.count to 1 Initialise init_net.count to 1 for its pointer from init_nsproxy lest someone tries to do a get_net() and a put_net() in a process in which current->ns_proxy->net_ns points to the initial network namespace. Signed-off-by: David Howells Signed-off-by: David S. Miller --- net/core/net_namespace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index c1d8aed8e5a8..1934efd4a9d4 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -35,7 +35,8 @@ LIST_HEAD(net_namespace_list); EXPORT_SYMBOL_GPL(net_namespace_list); struct net init_net = { - .dev_base_head = LIST_HEAD_INIT(init_net.dev_base_head), + .count = ATOMIC_INIT(1), + .dev_base_head = LIST_HEAD_INIT(init_net.dev_base_head), }; EXPORT_SYMBOL(init_net); -- cgit From 1d11e732e7d501c4a231f0b32cf8b81990592689 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 27 Apr 2017 20:37:58 -0400 Subject: virtio-net: use netif_tx_napi_add for tx napi Avoid hashing the tx napi struct into napi_hash[], which is used for busy polling receive queues. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 82f1c3a73345..7877551fe4e0 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2228,8 +2228,8 @@ static int virtnet_alloc_queues(struct virtnet_info *vi) vi->rq[i].pages = NULL; netif_napi_add(vi->dev, &vi->rq[i].napi, virtnet_poll, napi_weight); - netif_napi_add(vi->dev, &vi->sq[i].napi, virtnet_poll_tx, - napi_tx ? napi_weight : 0); + netif_tx_napi_add(vi->dev, &vi->sq[i].napi, virtnet_poll_tx, + napi_tx ? napi_weight : 0); sg_init_table(vi->rq[i].sg, ARRAY_SIZE(vi->rq[i].sg)); ewma_pkt_len_init(&vi->rq[i].mrg_avg_pkt_len); -- cgit From 46c505188b788c050d28174c6e726835e057bb28 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 27 Apr 2017 21:06:15 -0700 Subject: nfp: replace -ENOTSUPP with -EOPNOTSUPP As Or points out in commit 423b3aecf290 ("net/mlx4: Change ENOTSUPP to EOPNOTSUPP"), ENOTSUPP is NFS specific error. Replace it with EOPNOTSUPP. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c | 24 +++++++++++----------- .../net/ethernet/netronome/nfp/nfp_net_common.c | 4 ++-- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 2 +- .../net/ethernet/netronome/nfp/nfp_net_offload.c | 12 +++++------ 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c b/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c index 335beb8b8b45..97a8f00674d0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_bpf_jit.c @@ -798,7 +798,7 @@ wrp_test_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, const struct bpf_insn *insn = &meta->insn; if (insn->off < 0) /* TODO */ - return -ENOTSUPP; + return -EOPNOTSUPP; wrp_test_reg_one(nfp_prog, insn->dst_reg * 2, alu_op, insn->src_reg * 2, br_mask, insn->off); @@ -818,7 +818,7 @@ wrp_cmp_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, u32 tmp_reg; if (insn->off < 0) /* TODO */ - return -ENOTSUPP; + return -EOPNOTSUPP; tmp_reg = ur_load_imm_any(nfp_prog, imm & ~0U, imm_b(nfp_prog)); if (!swap) @@ -847,7 +847,7 @@ wrp_cmp_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, u8 areg = insn->src_reg * 2, breg = insn->dst_reg * 2; if (insn->off < 0) /* TODO */ - return -ENOTSUPP; + return -EOPNOTSUPP; if (swap) { areg ^= breg; @@ -1132,7 +1132,7 @@ static int mem_ldx4_skb(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) emit_alu(nfp_prog, reg_both(meta->insn.dst_reg * 2), reg_none(), ALU_OP_NONE, NFP_BPF_ABI_LEN); else - return -ENOTSUPP; + return -EOPNOTSUPP; return 0; } @@ -1143,7 +1143,7 @@ static int mem_ldx4_xdp(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) if (meta->insn.off != offsetof(struct xdp_md, data) && meta->insn.off != offsetof(struct xdp_md, data_end)) - return -ENOTSUPP; + return -EOPNOTSUPP; emit_alu(nfp_prog, dst, reg_none(), ALU_OP_NONE, NFP_BPF_ABI_PKT); @@ -1174,12 +1174,12 @@ static int mem_stx4_skb(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) if (meta->insn.off == offsetof(struct sk_buff, mark)) return wrp_set_mark(nfp_prog, meta->insn.src_reg * 2); - return -ENOTSUPP; + return -EOPNOTSUPP; } static int mem_stx4_xdp(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { - return -ENOTSUPP; + return -EOPNOTSUPP; } static int mem_stx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) @@ -1192,7 +1192,7 @@ static int mem_stx4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) static int jump(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { if (meta->insn.off < 0) /* TODO */ - return -ENOTSUPP; + return -EOPNOTSUPP; emit_br(nfp_prog, BR_UNC, meta->insn.off, 0); return 0; @@ -1206,7 +1206,7 @@ static int jeq_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) u32 tmp_reg; if (insn->off < 0) /* TODO */ - return -ENOTSUPP; + return -EOPNOTSUPP; if (imm & ~0U) { tmp_reg = ur_load_imm_any(nfp_prog, imm & ~0U, imm_b(nfp_prog)); @@ -1245,7 +1245,7 @@ static int jset_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) u32 tmp_reg; if (insn->off < 0) /* TODO */ - return -ENOTSUPP; + return -EOPNOTSUPP; if (!imm) { meta->skip = true; @@ -1276,7 +1276,7 @@ static int jne_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) u32 tmp_reg; if (insn->off < 0) /* TODO */ - return -ENOTSUPP; + return -EOPNOTSUPP; if (!imm) { emit_alu(nfp_prog, reg_none(), reg_a(insn->dst_reg * 2), @@ -1302,7 +1302,7 @@ static int jeq_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) const struct bpf_insn *insn = &meta->insn; if (insn->off < 0) /* TODO */ - return -ENOTSUPP; + return -EOPNOTSUPP; emit_alu(nfp_prog, imm_a(nfp_prog), reg_a(insn->dst_reg * 2), ALU_OP_XOR, reg_b(insn->src_reg * 2)); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 8a9b74305493..0e87b446e98f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2651,9 +2651,9 @@ nfp_net_setup_tc(struct net_device *netdev, u32 handle, __be16 proto, struct nfp_net *nn = netdev_priv(netdev); if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) - return -ENOTSUPP; + return -EOPNOTSUPP; if (proto != htons(ETH_P_ALL)) - return -ENOTSUPP; + return -EOPNOTSUPP; if (tc->type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn)) { if (!nn->dp.bpf_offload_xdp) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 6e27d1281425..a704efd4e314 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -791,7 +791,7 @@ static int nfp_net_set_coalesce(struct net_device *netdev, ec->tx_coalesce_usecs_high || ec->tx_max_coalesced_frames_high || ec->rate_sample_interval) - return -ENOTSUPP; + return -EOPNOTSUPP; /* Compute factor used to convert coalesce '_usecs' parameters to * ME timestamp ticks. There are 16 ME clock cycles for each timestamp diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c index b5b6f69d1e0f..cc823df12c8a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_offload.c @@ -119,12 +119,12 @@ nfp_net_bpf_get_act(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf) if (tc_no_actions(cls_bpf->exts)) return NN_ACT_DIRECT; - return -ENOTSUPP; + return -EOPNOTSUPP; } /* TC legacy mode */ if (!tc_single_action(cls_bpf->exts)) - return -ENOTSUPP; + return -EOPNOTSUPP; tcf_exts_to_list(cls_bpf->exts, &actions); list_for_each_entry(a, &actions, list) { @@ -136,7 +136,7 @@ nfp_net_bpf_get_act(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf) return NN_ACT_TC_REDIR; } - return -ENOTSUPP; + return -EOPNOTSUPP; } static int @@ -152,7 +152,7 @@ nfp_net_bpf_offload_prepare(struct nfp_net *nn, int ret; if (!IS_ENABLED(CONFIG_BPF_SYSCALL)) - return -ENOTSUPP; + return -EOPNOTSUPP; ret = nfp_net_bpf_get_act(nn, cls_bpf); if (ret < 0) @@ -162,7 +162,7 @@ nfp_net_bpf_offload_prepare(struct nfp_net *nn, max_mtu = nn_readb(nn, NFP_NET_CFG_BPF_INL_MTU) * 64 - 32; if (max_mtu < nn->dp.netdev->mtu) { nn_info(nn, "BPF offload not supported with MTU larger than HW packet split boundary\n"); - return -ENOTSUPP; + return -EOPNOTSUPP; } start_off = nn_readw(nn, NFP_NET_CFG_BPF_START); @@ -289,6 +289,6 @@ int nfp_net_bpf_offload(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf) return nfp_net_bpf_stats_update(nn, cls_bpf); default: - return -ENOTSUPP; + return -EOPNOTSUPP; } } -- cgit From d78005a50f306edb000f0994f0470f151915cf90 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 27 Apr 2017 21:06:16 -0700 Subject: nfp: drop rx_ring param from buffer allocation We will soon allocate RX buffers for caching on XDP TX rings. The rx_ring parameter passed to nfp_net_rx_alloc_one() is not actually used, remove it. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 0e87b446e98f..fc0652d175fc 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1153,16 +1153,13 @@ nfp_net_free_frag(void *frag, bool xdp) /** * nfp_net_rx_alloc_one() - Allocate and map page frag for RX * @dp: NFP Net data path struct - * @rx_ring: RX ring structure of the skb * @dma_addr: Pointer to storage for DMA address (output param) * * This function will allcate a new page frag, map it for DMA. * * Return: allocated page frag or NULL on failure. */ -static void * -nfp_net_rx_alloc_one(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, - dma_addr_t *dma_addr) +static void *nfp_net_rx_alloc_one(struct nfp_net_dp *dp, dma_addr_t *dma_addr) { void *frag; @@ -1317,8 +1314,7 @@ nfp_net_rx_ring_bufs_alloc(struct nfp_net_dp *dp, rxbufs = rx_ring->rxbufs; for (i = 0; i < rx_ring->cnt - 1; i++) { - rxbufs[i].frag = - nfp_net_rx_alloc_one(dp, rx_ring, &rxbufs[i].dma_addr); + rxbufs[i].frag = nfp_net_rx_alloc_one(dp, &rxbufs[i].dma_addr); if (!rxbufs[i].frag) { nfp_net_rx_ring_bufs_free(dp, rx_ring); return -ENOMEM; -- cgit From 92e68195ebe914ae8b34cfb92148385a50454806 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 27 Apr 2017 21:06:17 -0700 Subject: nfp: do simple XDP TX buffer recycling On the RX path we follow the "drop if allocation of replacement buffer fails" rule. With XDP we extended that to the TX action, so if XDP prog returned TX but allocation of replacement RX buffer failed, we will drop the packet. To improve our XDP TX performance extend the idea of rings being always full to XDP TX rings. Pre-fill the XDP TX rings with RX buffers, and when XDP prog returns TX action swap the RX buffer with the next buffer from the TX ring. XDP TX complete will no longer free the buffers but let them sit on the TX ring and wait for swap with RX buffer, instead. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 2 + .../net/ethernet/netronome/nfp/nfp_net_common.c | 140 ++++++++++++--------- 2 files changed, 85 insertions(+), 57 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 8f20fdef0754..e7e9a9848746 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -201,6 +201,7 @@ struct nfp_net_tx_buf { * @txds: Virtual address of TX ring in host memory * @dma: DMA address of the TX ring * @size: Size, in bytes, of the TX ring (needed to free) + * @is_xdp: Is this a XDP TX ring? */ struct nfp_net_tx_ring { struct nfp_net_r_vector *r_vec; @@ -221,6 +222,7 @@ struct nfp_net_tx_ring { dma_addr_t dma; unsigned int size; + bool is_xdp; } ____cacheline_aligned; /* RX and freelist descriptor format */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index fc0652d175fc..4fbda0eb4776 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -478,15 +478,18 @@ static irqreturn_t nfp_net_irq_exn(int irq, void *data) * @tx_ring: TX ring structure * @r_vec: IRQ vector servicing this ring * @idx: Ring index + * @is_xdp: Is this an XDP TX ring? */ static void nfp_net_tx_ring_init(struct nfp_net_tx_ring *tx_ring, - struct nfp_net_r_vector *r_vec, unsigned int idx) + struct nfp_net_r_vector *r_vec, unsigned int idx, + bool is_xdp) { struct nfp_net *nn = r_vec->nfp_net; tx_ring->idx = idx; tx_ring->r_vec = r_vec; + tx_ring->is_xdp = is_xdp; tx_ring->qcidx = tx_ring->idx * nn->stride_tx; tx_ring->qcp_q = nn->tx_bar + NFP_QCP_QUEUE_OFF(tx_ring->qcidx); @@ -994,7 +997,6 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) static void nfp_net_xdp_complete(struct nfp_net_tx_ring *tx_ring) { struct nfp_net_r_vector *r_vec = tx_ring->r_vec; - struct nfp_net_dp *dp = &r_vec->nfp_net->dp; u32 done_pkts = 0, done_bytes = 0; int idx, todo; u32 qcp_rd_p; @@ -1010,22 +1012,12 @@ static void nfp_net_xdp_complete(struct nfp_net_tx_ring *tx_ring) else todo = qcp_rd_p + tx_ring->cnt - tx_ring->qcp_rd_p; + done_pkts = todo; while (todo--) { idx = tx_ring->rd_p & (tx_ring->cnt - 1); tx_ring->rd_p++; - if (!tx_ring->txbufs[idx].frag) - continue; - - nfp_net_dma_unmap_rx(dp, tx_ring->txbufs[idx].dma_addr); - __free_page(virt_to_page(tx_ring->txbufs[idx].frag)); - - done_pkts++; done_bytes += tx_ring->txbufs[idx].real_len; - - tx_ring->txbufs[idx].dma_addr = 0; - tx_ring->txbufs[idx].frag = NULL; - tx_ring->txbufs[idx].fidx = -2; } tx_ring->qcp_rd_p = qcp_rd_p; @@ -1050,42 +1042,35 @@ static void nfp_net_xdp_complete(struct nfp_net_tx_ring *tx_ring) static void nfp_net_tx_ring_reset(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) { - struct nfp_net_r_vector *r_vec = tx_ring->r_vec; const struct skb_frag_struct *frag; struct netdev_queue *nd_q; - while (tx_ring->rd_p != tx_ring->wr_p) { + while (!tx_ring->is_xdp && tx_ring->rd_p != tx_ring->wr_p) { struct nfp_net_tx_buf *tx_buf; - int idx; + struct sk_buff *skb; + int idx, nr_frags; idx = tx_ring->rd_p & (tx_ring->cnt - 1); tx_buf = &tx_ring->txbufs[idx]; - if (tx_ring == r_vec->xdp_ring) { - nfp_net_dma_unmap_rx(dp, tx_buf->dma_addr); - __free_page(virt_to_page(tx_ring->txbufs[idx].frag)); - } else { - struct sk_buff *skb = tx_ring->txbufs[idx].skb; - int nr_frags = skb_shinfo(skb)->nr_frags; - - if (tx_buf->fidx == -1) { - /* unmap head */ - dma_unmap_single(dp->dev, tx_buf->dma_addr, - skb_headlen(skb), - DMA_TO_DEVICE); - } else { - /* unmap fragment */ - frag = &skb_shinfo(skb)->frags[tx_buf->fidx]; - dma_unmap_page(dp->dev, tx_buf->dma_addr, - skb_frag_size(frag), - DMA_TO_DEVICE); - } + skb = tx_ring->txbufs[idx].skb; + nr_frags = skb_shinfo(skb)->nr_frags; - /* check for last gather fragment */ - if (tx_buf->fidx == nr_frags - 1) - dev_kfree_skb_any(skb); + if (tx_buf->fidx == -1) { + /* unmap head */ + dma_unmap_single(dp->dev, tx_buf->dma_addr, + skb_headlen(skb), DMA_TO_DEVICE); + } else { + /* unmap fragment */ + frag = &skb_shinfo(skb)->frags[tx_buf->fidx]; + dma_unmap_page(dp->dev, tx_buf->dma_addr, + skb_frag_size(frag), DMA_TO_DEVICE); } + /* check for last gather fragment */ + if (tx_buf->fidx == nr_frags - 1) + dev_kfree_skb_any(skb); + tx_buf->dma_addr = 0; tx_buf->skb = NULL; tx_buf->fidx = -2; @@ -1100,7 +1085,7 @@ nfp_net_tx_ring_reset(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) tx_ring->qcp_rd_p = 0; tx_ring->wr_ptr_add = 0; - if (tx_ring == r_vec->xdp_ring) + if (tx_ring->is_xdp) return; nd_q = netdev_get_tx_queue(dp->netdev, tx_ring->idx); @@ -1492,8 +1477,6 @@ nfp_net_tx_xdp_buf(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, { struct nfp_net_tx_buf *txbuf; struct nfp_net_tx_desc *txd; - dma_addr_t new_dma_addr; - void *new_frag; int wr_idx; if (unlikely(nfp_net_tx_full(tx_ring, 1))) { @@ -1501,17 +1484,13 @@ nfp_net_tx_xdp_buf(struct nfp_net_dp *dp, struct nfp_net_rx_ring *rx_ring, return false; } - new_frag = nfp_net_napi_alloc_one(dp, &new_dma_addr); - if (unlikely(!new_frag)) { - nfp_net_rx_drop(dp, rx_ring->r_vec, rx_ring, rxbuf, NULL); - return false; - } - nfp_net_rx_give_one(dp, rx_ring, new_frag, new_dma_addr); - wr_idx = tx_ring->wr_p & (tx_ring->cnt - 1); /* Stash the soft descriptor of the head then initialize it */ txbuf = &tx_ring->txbufs[wr_idx]; + + nfp_net_rx_give_one(dp, rx_ring, txbuf->frag, txbuf->dma_addr); + txbuf->frag = rxbuf->frag; txbuf->dma_addr = rxbuf->dma_addr; txbuf->fidx = -1; @@ -1796,13 +1775,11 @@ static void nfp_net_tx_ring_free(struct nfp_net_tx_ring *tx_ring) * nfp_net_tx_ring_alloc() - Allocate resource for a TX ring * @dp: NFP Net data path struct * @tx_ring: TX Ring structure to allocate - * @is_xdp: True if ring will be used for XDP * * Return: 0 on success, negative errno otherwise. */ static int -nfp_net_tx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring, - bool is_xdp) +nfp_net_tx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring) { struct nfp_net_r_vector *r_vec = tx_ring->r_vec; int sz; @@ -1820,7 +1797,7 @@ nfp_net_tx_ring_alloc(struct nfp_net_dp *dp, struct nfp_net_tx_ring *tx_ring, if (!tx_ring->txbufs) goto err_alloc; - if (!is_xdp) + if (!tx_ring->is_xdp) netif_set_xps_queue(dp->netdev, &r_vec->affinity_mask, tx_ring->idx); @@ -1831,6 +1808,45 @@ err_alloc: return -ENOMEM; } +static void +nfp_net_tx_ring_bufs_free(struct nfp_net_dp *dp, + struct nfp_net_tx_ring *tx_ring) +{ + unsigned int i; + + if (!tx_ring->is_xdp) + return; + + for (i = 0; i < tx_ring->cnt; i++) { + if (!tx_ring->txbufs[i].frag) + return; + + nfp_net_dma_unmap_rx(dp, tx_ring->txbufs[i].dma_addr); + __free_page(virt_to_page(tx_ring->txbufs[i].frag)); + } +} + +static int +nfp_net_tx_ring_bufs_alloc(struct nfp_net_dp *dp, + struct nfp_net_tx_ring *tx_ring) +{ + struct nfp_net_tx_buf *txbufs = tx_ring->txbufs; + unsigned int i; + + if (!tx_ring->is_xdp) + return 0; + + for (i = 0; i < tx_ring->cnt; i++) { + txbufs[i].frag = nfp_net_rx_alloc_one(dp, &txbufs[i].dma_addr); + if (!txbufs[i].frag) { + nfp_net_tx_ring_bufs_free(dp, tx_ring); + return -ENOMEM; + } + } + + return 0; +} + static int nfp_net_tx_rings_prepare(struct nfp_net *nn, struct nfp_net_dp *dp) { unsigned int r; @@ -1847,17 +1863,23 @@ static int nfp_net_tx_rings_prepare(struct nfp_net *nn, struct nfp_net_dp *dp) bias = dp->num_stack_tx_rings; nfp_net_tx_ring_init(&dp->tx_rings[r], &nn->r_vecs[r - bias], - r); + r, bias); - if (nfp_net_tx_ring_alloc(dp, &dp->tx_rings[r], bias)) + if (nfp_net_tx_ring_alloc(dp, &dp->tx_rings[r])) goto err_free_prev; + + if (nfp_net_tx_ring_bufs_alloc(dp, &dp->tx_rings[r])) + goto err_free_ring; } return 0; err_free_prev: - while (r--) + while (r--) { + nfp_net_tx_ring_bufs_free(dp, &dp->tx_rings[r]); +err_free_ring: nfp_net_tx_ring_free(&dp->tx_rings[r]); + } kfree(dp->tx_rings); return -ENOMEM; } @@ -1866,8 +1888,10 @@ static void nfp_net_tx_rings_free(struct nfp_net_dp *dp) { unsigned int r; - for (r = 0; r < dp->num_tx_rings; r++) + for (r = 0; r < dp->num_tx_rings; r++) { + nfp_net_tx_ring_bufs_free(dp, &dp->tx_rings[r]); nfp_net_tx_ring_free(&dp->tx_rings[r]); + } kfree(dp->tx_rings); } @@ -2365,8 +2389,10 @@ static void nfp_net_close_free_all(struct nfp_net *nn) nfp_net_rx_ring_bufs_free(&nn->dp, &nn->dp.rx_rings[r]); nfp_net_rx_ring_free(&nn->dp.rx_rings[r]); } - for (r = 0; r < nn->dp.num_tx_rings; r++) + for (r = 0; r < nn->dp.num_tx_rings; r++) { + nfp_net_tx_ring_bufs_free(&nn->dp, &nn->dp.tx_rings[r]); nfp_net_tx_ring_free(&nn->dp.tx_rings[r]); + } for (r = 0; r < nn->dp.num_r_vecs; r++) nfp_net_cleanup_vector(nn, &nn->r_vecs[r]); -- cgit From d38df0d364c3a154065109eb855f8d15ecd0fae2 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 27 Apr 2017 21:06:18 -0700 Subject: nfp: avoid reading TX queue indexes from the device Reading TX queue indexes from the device memory on each interrupt is expensive. It's doubly expensive with XDP running since we have two TX rings to check there. If the software indexes indicate that the TX queue is completely empty, however, we don't need to look at the device completion index at all. The queuing CPU is doing a wmb() before kicking the device TX so we should be safe to assume on the CPU handling the completions will never see old value of the software copy of the index. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 4fbda0eb4776..c763a5b3bd6b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -927,6 +927,9 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) int fidx; int idx; + if (tx_ring->wr_p == tx_ring->rd_p) + return; + /* Work out how many descriptors have been transmitted */ qcp_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q); @@ -1001,6 +1004,9 @@ static void nfp_net_xdp_complete(struct nfp_net_tx_ring *tx_ring) int idx, todo; u32 qcp_rd_p; + if (tx_ring->wr_p == tx_ring->rd_p) + return; + /* Work out how many descriptors have been transmitted */ qcp_rd_p = nfp_qcp_rd_ptr_read(tx_ring->qcp_q); -- cgit From 85cb207ee34134313acbfb948ae8a7d65f1f19e9 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 27 Apr 2017 21:06:19 -0700 Subject: nfp: don't completely refuse to work with old flashes Right now the required Service Process ABI version is still tied to max ID of known commands. For new NSP commands we are adding we are checking if NSP version is recent enough on command-by-command basis. The driver doesn't have to force the device to have the very latest flash, anything newer than 0.8 should do. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 61797c98f5fe..2fa9247bb23d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -78,7 +78,7 @@ #define NSP_MAGIC 0xab10 #define NSP_MAJOR 0 -#define NSP_MINOR (__MAX_SPCODE - 1) +#define NSP_MINOR 8 #define NSP_CODE_MAJOR GENMASK(15, 12) #define NSP_CODE_MINOR GENMASK(11, 0) @@ -94,8 +94,6 @@ enum nfp_nsp_cmd { SPCODE_ETH_RESCAN = 7, /* Rescan ETHs, write ETH_TABLE to buf */ SPCODE_ETH_CONTROL = 8, /* Update media config from buffer */ SPCODE_NSP_IDENTIFY = 13, /* Read NSP version */ - - __MAX_SPCODE, }; static const struct { -- cgit From dbf637ff3995bc134187bf64b390feab6125173c Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Thu, 27 Apr 2017 21:06:20 -0700 Subject: nfp: provide 256 bytes of XDP headroom in all configurations For legacy reasons NFP FW may be compiled to DMA packets to a constant offset into the buffer and use the space before it for metadata. This ensures that packets data always start at a certain offset regardless of the amount of preceding metadata. If rx offset is set to 0 there may still be up to 64 bytes of metadata but metadata will start at the beginning of the buffer, instead of: data_start_offset = rx_offset - meta_len Even though we make the buffers larger to accommodate up to 64 bytes of metadata, if there is only N bytes of metadata, we will end up with N bytes of headroom and 64 - N bytes of tailroom. Therefore we can't rely on that space for XDP headroom. Make sure we always allocate full 256 bytes. This, unfortunately, means we can't fit the headroom on an u8 any more. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 4 ++-- drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 13 +------------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index e7e9a9848746..38b41fdeaa8f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -470,10 +470,10 @@ struct nfp_net_dp { u8 chained_metadata_format:1; u8 rx_dma_dir; - u8 rx_dma_off; - u8 rx_offset; + u32 rx_dma_off; + u32 ctrl; u32 fl_bufsz; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index c763a5b3bd6b..b9f3548bb65f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2966,11 +2966,7 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) dp->xdp_prog = prog; dp->num_tx_rings += prog ? nn->dp.num_rx_rings : -nn->dp.num_rx_rings; dp->rx_dma_dir = prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; - if (prog) - dp->rx_dma_off = XDP_PACKET_HEADROOM - - (nn->dp.rx_offset ?: NFP_NET_MAX_PREPEND); - else - dp->rx_dma_off = 0; + dp->rx_dma_off = prog ? XDP_PACKET_HEADROOM - nn->dp.rx_offset : 0; /* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */ err = nfp_net_ring_reconfig(nn, dp); @@ -3198,13 +3194,6 @@ int nfp_net_netdev_init(struct net_device *netdev) struct nfp_net *nn = netdev_priv(netdev); int err; - /* XDP calls for 256 byte packet headroom which wouldn't fit in a u8. - * We, however, reuse the metadata prepend space for XDP buffers which - * is at least 1 byte long and as long as XDP headroom doesn't increase - * above 256 the *extra* XDP headroom will fit on 8 bits. - */ - BUILD_BUG_ON(XDP_PACKET_HEADROOM > 256); - nn->dp.chained_metadata_format = nn->fw_ver.major > 3; nn->dp.rx_dma_dir = DMA_FROM_DEVICE; -- cgit From 2fdd6bafe37db6b417c9609271db4ea5a92550c0 Mon Sep 17 00:00:00 2001 From: lipeng Date: Fri, 28 Apr 2017 14:49:46 +0800 Subject: net: hns: support deferred probe when can not obtain irq In the hip06 and hip07 SoCs, the interrupt lines from the DSAF controllers are connected to mbigen hw module. The mbigen module is probed with module_init, and, as such, is not guaranteed to probe before the HNS driver. So we need to support deferred probe. Signed-off-by: lipeng Reviewed-by: Yisen Zhuang Reviewed-by: Matthias Brugger Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c | 4 +++- drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c | 8 +++++++- drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c index eba406bea52f..93e71e27401b 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c @@ -510,7 +510,9 @@ int hns_ppe_init(struct dsaf_device *dsaf_dev) hns_ppe_get_cfg(dsaf_dev->ppe_common[i]); - hns_rcb_get_cfg(dsaf_dev->rcb_common[i]); + ret = hns_rcb_get_cfg(dsaf_dev->rcb_common[i]); + if (ret) + goto get_cfg_fail; } for (i = 0; i < HNS_PPE_COM_NUM; i++) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c index c20a0f4f8f02..e2e28532e4dc 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c @@ -492,7 +492,7 @@ static int hns_rcb_get_base_irq_idx(struct rcb_common_cb *rcb_common) *hns_rcb_get_cfg - get rcb config *@rcb_common: rcb common device */ -void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common) +int hns_rcb_get_cfg(struct rcb_common_cb *rcb_common) { struct ring_pair_cb *ring_pair_cb; u32 i; @@ -517,10 +517,16 @@ void hns_rcb_get_cfg(struct rcb_common_cb *rcb_common) ring_pair_cb->virq[HNS_RCB_IRQ_IDX_RX] = is_ver1 ? platform_get_irq(pdev, base_irq_idx + i * 2 + 1) : platform_get_irq(pdev, base_irq_idx + i * 3); + if ((ring_pair_cb->virq[HNS_RCB_IRQ_IDX_TX] == -EPROBE_DEFER) || + (ring_pair_cb->virq[HNS_RCB_IRQ_IDX_RX] == -EPROBE_DEFER)) + return -EPROBE_DEFER; + ring_pair_cb->q.phy_base = RCB_COMM_BASE_TO_RING_BASE(rcb_common->phy_base, i); hns_rcb_ring_pair_get_cfg(ring_pair_cb); } + + return 0; } /** diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h index a664ee88ab45..602816498c8d 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.h @@ -121,7 +121,7 @@ int hns_rcb_common_get_cfg(struct dsaf_device *dsaf_dev, int comm_index); void hns_rcb_common_free_cfg(struct dsaf_device *dsaf_dev, u32 comm_index); 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); +int hns_rcb_get_cfg(struct rcb_common_cb *rcb_common); void hns_rcb_get_queue_mode(enum dsaf_mode dsaf_mode, u16 *max_vfn, u16 *max_q_per_vf); -- cgit From 804ffe5c6197fc2696681c597ce096d93eb24af5 Mon Sep 17 00:00:00 2001 From: lipeng Date: Fri, 28 Apr 2017 14:49:47 +0800 Subject: net: hns: support deferred probe when no mdio In the hip06 and hip07 SoCs, phy connect to mdio bus.The mdio module is probed with module_init, and, as such, is not guaranteed to probe before the HNS driver. So we need to support deferred probe. We check for probe deferral in the mac init, so we not init DSAF when there is no mdio, and free all resource, to later learn that we need to defer the probe. Signed-off-by: lipeng Reviewed-by: Yisen Zhuang Reviewed-by: Matthias Brugger Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c | 39 +++++++++++++++++++---- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c index 0c1f56e58074..8b5cdf490850 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c @@ -696,6 +696,8 @@ hns_mac_register_phydev(struct mii_bus *mdio, struct hns_mac_cb *mac_cb, rc = phy_device_register(phy); if (rc) { phy_device_free(phy); + dev_err(&mdio->dev, "registered phy fail at address %i\n", + addr); return -ENODEV; } @@ -706,7 +708,7 @@ hns_mac_register_phydev(struct mii_bus *mdio, struct hns_mac_cb *mac_cb, return 0; } -static void hns_mac_register_phy(struct hns_mac_cb *mac_cb) +static int hns_mac_register_phy(struct hns_mac_cb *mac_cb) { struct acpi_reference_args args; struct platform_device *pdev; @@ -716,24 +718,39 @@ static void hns_mac_register_phy(struct hns_mac_cb *mac_cb) /* Loop over the child nodes and register a phy_device for each one */ if (!to_acpi_device_node(mac_cb->fw_port)) - return; + return -ENODEV; rc = acpi_node_get_property_reference( mac_cb->fw_port, "mdio-node", 0, &args); if (rc) - return; + return rc; addr = hns_mac_phy_parse_addr(mac_cb->dev, mac_cb->fw_port); if (addr < 0) - return; + return addr; /* dev address in adev */ pdev = hns_dsaf_find_platform_device(acpi_fwnode_handle(args.adev)); + if (!pdev) { + dev_err(mac_cb->dev, "mac%d mdio pdev is NULL\n", + mac_cb->mac_id); + return -EINVAL; + } + mii_bus = platform_get_drvdata(pdev); + if (!mii_bus) { + dev_err(mac_cb->dev, + "mac%d mdio is NULL, dsaf will probe again later\n", + mac_cb->mac_id); + return -EPROBE_DEFER; + } + rc = hns_mac_register_phydev(mii_bus, mac_cb, addr); if (!rc) dev_dbg(mac_cb->dev, "mac%d register phy addr:%d\n", mac_cb->mac_id, addr); + + return rc; } #define MAC_MEDIA_TYPE_MAX_LEN 16 @@ -754,7 +771,7 @@ static const struct { *@np:device node * return: 0 --success, negative --fail */ -static int hns_mac_get_info(struct hns_mac_cb *mac_cb) +static int hns_mac_get_info(struct hns_mac_cb *mac_cb) { struct device_node *np; struct regmap *syscon; @@ -864,7 +881,15 @@ static int hns_mac_get_info(struct hns_mac_cb *mac_cb) } } } else if (is_acpi_node(mac_cb->fw_port)) { - hns_mac_register_phy(mac_cb); + ret = hns_mac_register_phy(mac_cb); + /* + * Mac can work well if there is phy or not.If the port don't + * connect with phy, the return value will be ignored. Only + * when there is phy but can't find mdio bus, the return value + * will be handled. + */ + if (ret == -EPROBE_DEFER) + return ret; } else { dev_err(mac_cb->dev, "mac%d cannot find phy node\n", mac_cb->mac_id); @@ -1026,6 +1051,7 @@ int hns_mac_init(struct dsaf_device *dsaf_dev) dsaf_dev->mac_cb[port_id] = mac_cb; } } + /* init mac_cb for all port */ for (port_id = 0; port_id < max_port_num; port_id++) { mac_cb = dsaf_dev->mac_cb[port_id]; @@ -1035,6 +1061,7 @@ int hns_mac_init(struct dsaf_device *dsaf_dev) ret = hns_mac_get_cfg(dsaf_dev, mac_cb); if (ret) return ret; + ret = hns_mac_init_ex(mac_cb); if (ret) return ret; -- cgit From 66117a9d9a8ca948680d6554769ef9e88f936954 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 28 Apr 2017 15:56:09 +0300 Subject: qed: Unlock on error in qed_vf_pf_acquire() My static checker complains that we're holding a mutex on this error path. Let's goto exit instead of returning directly. Fixes: b0bccb69eba3 ("qed: Change locking scheme for VF channel") Signed-off-by: Dan Carpenter Acked-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_vf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c index c4c4a408b40b..11d71e5eea14 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.c +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c @@ -234,7 +234,7 @@ static int qed_vf_pf_acquire(struct qed_hwfn *p_hwfn) /* send acquire request */ rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); if (rc) - return rc; + goto exit; /* copy acquire response from buffer to p_hwfn */ memcpy(&p_iov->acquire_resp, resp, sizeof(p_iov->acquire_resp)); -- cgit From 77041e89ce3d4efb9e2364b6d27e97d4bbca74fc Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 28 Apr 2017 15:57:15 +0300 Subject: liquidio: silence a locking static checker warning Presumably we never hit this return, but static checkers complain that we need to unlock so we may as well fix that. Signed-off-by: Dan Carpenter Acked-by: Felix Manlunas Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c index 201b9875f9bb..5cca73b8880b 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_mailbox.c @@ -313,6 +313,7 @@ int octeon_mbox_process_message(struct octeon_mbox *mbox) return 0; } + spin_unlock_irqrestore(&mbox->lock, flags); WARN_ON(1); return 0; -- cgit From 39f37095990a39a0ee24f7621d06e9a6da6cd815 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 28 Apr 2017 16:03:48 +0300 Subject: lwtunnel: fix error path in lwtunnel_fill_encap() We recently added a check to see if nla_nest_start() fails. There are two issues with that. First, if it fails then I don't think we should call nla_nest_cancel(). Second, it's slightly convoluted but the current code returns success but we should return -EMSGSIZE instead. Fixes: a50fe0ffd76f ("lwtunnel: check return value of nla_nest_start") Signed-off-by: Dan Carpenter Acked-by: David Ahern Signed-off-by: David S. Miller --- net/core/lwtunnel.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index 5cbed3816229..cfae3d5fe11f 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -203,7 +203,7 @@ int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate) { const struct lwtunnel_encap_ops *ops; struct nlattr *nest; - int ret = -EINVAL; + int ret; if (!lwtstate) return 0; @@ -212,10 +212,11 @@ int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate) lwtstate->type > LWTUNNEL_ENCAP_MAX) return 0; - ret = -EOPNOTSUPP; nest = nla_nest_start(skb, RTA_ENCAP); if (!nest) - goto nla_put_failure; + return -EMSGSIZE; + + ret = -EOPNOTSUPP; rcu_read_lock(); ops = rcu_dereference(lwtun_encaps[lwtstate->type]); if (likely(ops && ops->fill_encap)) -- cgit From 5010e948420ea1cff85f6c2f64ec7a011aea3d07 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Fri, 28 Apr 2017 16:25:04 +0200 Subject: samples/bpf: bpf_load.c detect and abort if ELF maps section size is wrong The struct bpf_map_def was extended in commit fb30d4b71214 ("bpf: Add tests for map-in-map") with member unsigned int inner_map_idx. This changed the size of the maps section in the generated ELF _kern.o files. Unfortunately the loader in bpf_load.c does not detect or handle this. Thus, older _kern.o files became incompatible, and caused hard-to-debug errors where the syscall validation rejected BPF_MAP_CREATE request. This patch only detect the situation and aborts load_bpf_file(). It also add code comments warning people that read this loader for inspiration for these pitfalls. Fixes: fb30d4b71214 ("bpf: Add tests for map-in-map") Signed-off-by: Jesper Dangaard Brouer Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- samples/bpf/bpf_load.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c index d4433a47e6c3..0ec0dea3c41e 100644 --- a/samples/bpf/bpf_load.c +++ b/samples/bpf/bpf_load.c @@ -185,12 +185,16 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) return 0; } -static int load_maps(struct bpf_map_def *maps, int len, +static int load_maps(struct bpf_map_def *maps, int nr_maps, const char **map_names, fixup_map_cb fixup_map) { int i; - - for (i = 0; i < len / sizeof(struct bpf_map_def); i++) { + /* + * Warning: Using "maps" pointing to ELF data_maps->d_buf as + * an array of struct bpf_map_def is a wrong assumption about + * the ELF maps section format. + */ + for (i = 0; i < nr_maps; i++) { if (fixup_map) fixup_map(&maps[i], map_names[i], i); @@ -269,6 +273,10 @@ static int parse_relo_and_apply(Elf_Data *data, Elf_Data *symbols, return 1; } insn[insn_idx].src_reg = BPF_PSEUDO_MAP_FD; + /* + * Warning: Using sizeof(struct bpf_map_def) here is a + * wrong assumption about ELF maps section format + */ insn[insn_idx].imm = map_fd[sym.st_value / sizeof(struct bpf_map_def)]; } @@ -311,18 +319,18 @@ static int get_sorted_map_names(Elf *elf, Elf_Data *symbols, int maps_shndx, map_name = elf_strptr(elf, strtabidx, map_symbols[i].st_name); if (!map_name) { printf("cannot get map symbol\n"); - return 1; + return -1; } map_names[i] = strdup(map_name); if (!map_names[i]) { printf("strdup(%s): %s(%d)\n", map_name, strerror(errno), errno); - return 1; + return -1; } } - return 0; + return nr_maps; } static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map) @@ -396,11 +404,25 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map) } if (data_maps) { - if (get_sorted_map_names(elf, symbols, maps_shndx, strtabidx, - map_names)) + int nr_maps; + int prog_elf_map_sz; + + nr_maps = get_sorted_map_names(elf, symbols, maps_shndx, + strtabidx, map_names); + if (nr_maps < 0) goto done; - if (load_maps(data_maps->d_buf, data_maps->d_size, + /* Deduce map struct size stored in ELF maps section */ + prog_elf_map_sz = data_maps->d_size / nr_maps; + if (prog_elf_map_sz != sizeof(struct bpf_map_def)) { + printf("Error: ELF maps sec wrong size (%d/%lu)," + " old kern.o file?\n", + prog_elf_map_sz, sizeof(struct bpf_map_def)); + ret = 1; + goto done; + } + + if (load_maps(data_maps->d_buf, nr_maps, (const char **)map_names, fixup_map)) goto done; -- cgit From ba3f571d5dde27fc4ae83aabe2c06ff08087af49 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 28 Apr 2017 10:04:29 -0700 Subject: ipv4: get rid of ip_ra_lock After commit 1215e51edad1 ("ipv4: fix a deadlock in ip_ra_control") we always take RTNL lock for ip_ra_control() which is the only place we update the list ip_ra_chain, so the ip_ra_lock is no longer needed. As Eric points out, BH does not need to disable either, RCU readers don't care. Signed-off-by: Cong Wang Acked-by: Hannes Frederic Sowa Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/ip_sockglue.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 1d46d05efb0f..ec4fe3d4b5c9 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -330,7 +330,6 @@ int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc, sent to multicast group to reach destination designated router. */ struct ip_ra_chain __rcu *ip_ra_chain; -static DEFINE_SPINLOCK(ip_ra_lock); static void ip_ra_destroy_rcu(struct rcu_head *head) @@ -352,21 +351,17 @@ int ip_ra_control(struct sock *sk, unsigned char on, new_ra = on ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL; - spin_lock_bh(&ip_ra_lock); for (rap = &ip_ra_chain; - (ra = rcu_dereference_protected(*rap, - lockdep_is_held(&ip_ra_lock))) != NULL; + (ra = rtnl_dereference(*rap)) != NULL; rap = &ra->next) { if (ra->sk == sk) { if (on) { - spin_unlock_bh(&ip_ra_lock); kfree(new_ra); return -EADDRINUSE; } /* dont let ip_call_ra_chain() use sk again */ ra->sk = NULL; RCU_INIT_POINTER(*rap, ra->next); - spin_unlock_bh(&ip_ra_lock); if (ra->destructor) ra->destructor(sk); @@ -380,17 +375,14 @@ int ip_ra_control(struct sock *sk, unsigned char on, return 0; } } - if (!new_ra) { - spin_unlock_bh(&ip_ra_lock); + if (!new_ra) return -ENOBUFS; - } new_ra->sk = sk; new_ra->destructor = destructor; RCU_INIT_POINTER(new_ra->next, ra); rcu_assign_pointer(*rap, new_ra); sock_hold(sk); - spin_unlock_bh(&ip_ra_lock); return 0; } -- cgit From 58073b32b0f716cbd894d503ab2408db9d48aed6 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Fri, 28 Apr 2017 22:39:07 +0300 Subject: net: bridge: Fix improper taking over HW learned FDB Commit 7e26bf45e4cb ("net: bridge: allow SW learn to take over HW fdb entries") added the ability to "take over an entry which was previously learned via HW when it shows up from a SW port". However, if an entry was learned via HW and then a control packet (e.g., ARP request) was trapped to the CPU, the bridge driver will update the entry and remove the externally learned flag, although the entry is still present in HW. Instead, only clear the externally learned flag in case of roaming. Fixes: 7e26bf45e4cb ("net: bridge: allow SW learn to take over HW fdb entries") Signed-off-by: Ido Schimmel Signed-off-by: Arkadi Sharashevsky Cc: Nikolay Aleksandrov Acked-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- net/bridge/br_fdb.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index de7988b0349e..ab0c7cc8448f 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -589,16 +589,14 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, if (unlikely(source != fdb->dst)) { fdb->dst = source; fdb_modified = true; + /* Take over HW learned entry */ + if (unlikely(fdb->added_by_external_learn)) + fdb->added_by_external_learn = 0; } if (now != fdb->updated) fdb->updated = now; if (unlikely(added_by_user)) fdb->added_by_user = 1; - /* Take over HW learned entry */ - if (unlikely(fdb->added_by_external_learn)) { - fdb->added_by_external_learn = 0; - fdb_modified = true; - } if (unlikely(fdb_modified)) fdb_notify(br, fdb, RTM_NEWNEIGH); } -- cgit From 9b70de6d0266888b3743f03802502e43131043c8 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Fri, 28 Apr 2017 19:17:41 -0500 Subject: bnx2x: Align RX buffers The bnx2x driver is not providing proper alignment on the receive buffers it passes to build_skb(), causing skb_shared_info to be misaligned. skb_shared_info contains an atomic, and while PPC normally supports unaligned accesses, it does not support unaligned atomics. Aligning the size of rx buffers will ensure that page_frag_alloc() returns aligned addresses. This can be reproduced on PPC by setting the network MTU to 1450 (or other non-multiple-of-4) and then generating sufficient inbound network traffic (one or two large "wget"s usually does it), producing the following oops: Unable to handle kernel paging request for unaligned access at address 0xc00000ffc43af656 Faulting instruction address: 0xc00000000080ef8c Oops: Kernel access of bad area, sig: 7 [#1] SMP NR_CPUS=2048 NUMA PowerNV Modules linked in: vmx_crypto powernv_rng rng_core powernv_op_panel leds_powernv led_class nfsd ip_tables x_tables autofs4 xfs lpfc bnx2x mdio libcrc32c crc_t10dif crct10dif_generic crct10dif_common CPU: 104 PID: 0 Comm: swapper/104 Not tainted 4.11.0-rc8-00088-g4c761da #2 task: c00000ffd4892400 task.stack: c00000ffd4920000 NIP: c00000000080ef8c LR: c00000000080eee8 CTR: c0000000001f8320 REGS: c00000ffffc33710 TRAP: 0600 Not tainted (4.11.0-rc8-00088-g4c761da) MSR: 9000000000009033 CR: 24082042 XER: 00000000 CFAR: c00000000080eea0 DAR: c00000ffc43af656 DSISR: 00000000 SOFTE: 1 GPR00: c000000000907f64 c00000ffffc33990 c000000000dd3b00 c00000ffcaf22100 GPR04: c00000ffcaf22e00 0000000000000000 0000000000000000 0000000000000000 GPR08: 0000000000b80008 c00000ffc43af636 c00000ffc43af656 0000000000000000 GPR12: c0000000001f6f00 c00000000fe1a000 000000000000049f 000000000000c51f GPR16: 00000000ffffef33 0000000000000000 0000000000008a43 0000000000000001 GPR20: c00000ffc58a90c0 0000000000000000 000000000000dd86 0000000000000000 GPR24: c000007fd0ed10c0 00000000ffffffff 0000000000000158 000000000000014a GPR28: c00000ffc43af010 c00000ffc9144000 c00000ffcaf22e00 c00000ffcaf22100 NIP [c00000000080ef8c] __skb_clone+0xdc/0x140 LR [c00000000080eee8] __skb_clone+0x38/0x140 Call Trace: [c00000ffffc33990] [c00000000080fb74] skb_clone+0x74/0x110 (unreliable) [c00000ffffc339c0] [c000000000907f64] packet_rcv+0x144/0x510 [c00000ffffc33a40] [c000000000827b64] __netif_receive_skb_core+0x5b4/0xd80 [c00000ffffc33b00] [c00000000082b2bc] netif_receive_skb_internal+0x2c/0xc0 [c00000ffffc33b40] [c00000000082c49c] napi_gro_receive+0x11c/0x260 [c00000ffffc33b80] [d000000066483d68] bnx2x_poll+0xcf8/0x17b0 [bnx2x] [c00000ffffc33d00] [c00000000082babc] net_rx_action+0x31c/0x480 [c00000ffffc33e10] [c0000000000d5a44] __do_softirq+0x164/0x3d0 [c00000ffffc33f00] [c0000000000d60a8] irq_exit+0x108/0x120 [c00000ffffc33f20] [c000000000015b98] __do_irq+0x98/0x200 [c00000ffffc33f90] [c000000000027f14] call_do_irq+0x14/0x24 [c00000ffd4923a90] [c000000000015d94] do_IRQ+0x94/0x110 [c00000ffd4923ae0] [c000000000008d90] hardware_interrupt_common+0x150/0x160 Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index ad3e0631877e..eccb3d1b6abb 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -2021,6 +2021,7 @@ static void bnx2x_set_rx_buf_size(struct bnx2x *bp) ETH_OVERHEAD + mtu + BNX2X_FW_RX_ALIGN_END; + fp->rx_buf_size = SKB_DATA_ALIGN(fp->rx_buf_size); /* Note : rx_buf_size doesn't take into account NET_SKB_PAD */ if (fp->rx_buf_size + NET_SKB_PAD <= PAGE_SIZE) fp->rx_frag_size = fp->rx_buf_size + NET_SKB_PAD; -- cgit From d832565032280bae2cbff4fdb4d2c793de60d5be Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 29 Apr 2017 22:38:57 +0100 Subject: net: sunhme: fix spelling mistakes: "ParityErro" -> "ParityError" trivial fix to spelling mistakes in printk message. Signed-off-by: Colin Ian King Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunhme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/sun/sunhme.c b/drivers/net/ethernet/sun/sunhme.c index a6cc9a2d41c1..9e983e1d8249 100644 --- a/drivers/net/ethernet/sun/sunhme.c +++ b/drivers/net/ethernet/sun/sunhme.c @@ -1857,7 +1857,7 @@ static int happy_meal_is_not_so_happy(struct happy_meal *hp, u32 status) if (status & GREG_STAT_TXLERR) printk("LateError "); if (status & GREG_STAT_TXPERR) - printk("ParityErro "); + printk("ParityError "); if (status & GREG_STAT_TXTERR) printk("TagBotch "); printk("]\n"); -- cgit From 733336262b28d383cf72eba2287f1132deea4f47 Mon Sep 17 00:00:00 2001 From: Abhishek Shah Date: Sun, 30 Apr 2017 11:04:21 +0530 Subject: net: phy: Allow BCM5481x PHYs to setup internal TX/RX clock delay This patch allows users to enable/disable internal TX and/or RX clock delay for BCM5481x series PHYs so as to satisfy RGMII timing specifications. On a particular platform, whether TX and/or RX clock delay is required depends on how PHY connected to the MAC IP. This requirement can be specified through "phy-mode" property in the platform device tree. Signed-off-by: Abhishek Shah Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/broadcom.c | 69 ++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 9cd8b27d1292..a32dc5d11e89 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -74,27 +74,40 @@ static int bcm54612e_config_init(struct phy_device *phydev) return 0; } -static int bcm54810_config(struct phy_device *phydev) +static int bcm5481x_config(struct phy_device *phydev) { int rc, val; - val = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL); - val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN; - rc = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL, - val); - if (rc < 0) - return rc; - + /* handling PHY's internal RX clock delay */ val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC); - val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN; val |= MII_BCM54XX_AUXCTL_MISC_WREN; + if (phydev->interface == PHY_INTERFACE_MODE_RGMII || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { + /* Disable RGMII RXC-RXD skew */ + val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN; + } + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { + /* Enable RGMII RXC-RXD skew */ + val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN; + } rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC, val); if (rc < 0) return rc; + /* handling PHY's internal TX clock delay */ val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL); - val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN; + if (phydev->interface == PHY_INTERFACE_MODE_RGMII || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { + /* Disable internal TX clock delay */ + val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN; + } + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { + /* Enable internal TX clock delay */ + val |= BCM54810_SHD_CLK_CTL_GTXCLK_EN; + } rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val); if (rc < 0) return rc; @@ -244,7 +257,7 @@ static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev) static int bcm54xx_config_init(struct phy_device *phydev) { - int reg, err; + int reg, err, val; reg = phy_read(phydev, MII_BCM54XX_ECR); if (reg < 0) @@ -283,8 +296,14 @@ static int bcm54xx_config_init(struct phy_device *phydev) if (err) return err; } else if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810) { - err = bcm54810_config(phydev); - if (err) + /* For BCM54810, we need to disable BroadR-Reach function */ + val = bcm_phy_read_exp(phydev, + BCM54810_EXP_BROADREACH_LRE_MISC_CTL); + val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN; + err = bcm_phy_write_exp(phydev, + BCM54810_EXP_BROADREACH_LRE_MISC_CTL, + val); + if (err < 0) return err; } @@ -392,29 +411,7 @@ static int bcm5481_config_aneg(struct phy_device *phydev) ret = genphy_config_aneg(phydev); /* Then we can set up the delay. */ - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { - u16 reg; - - /* - * There is no BCM5481 specification available, so down - * here is everything we know about "register 0x18". This - * at least helps BCM5481 to successfully receive packets - * on MPC8360E-RDK board. Peter Barada - * says: "This sets delay between the RXD and RXC signals - * instead of using trace lengths to achieve timing". - */ - - /* Set RDX clk delay. */ - reg = 0x7 | (0x7 << 12); - phy_write(phydev, 0x18, reg); - - reg = phy_read(phydev, 0x18); - /* Set RDX-RXC skew. */ - reg |= (1 << 8); - /* Write bits 14:0. */ - reg |= (1 << 15); - phy_write(phydev, 0x18, reg); - } + bcm5481x_config(phydev); if (of_property_read_bool(np, "enet-phy-lane-swap")) { /* Lane Swap - Undocumented register...magic! */ -- cgit From 933bd83ed60e80ebb1aeb64a2f7cd3190d2312e2 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 24 Apr 2017 15:37:39 +0200 Subject: netfilter: batch synchronize_net calls during hook unregister synchronize_net is expensive and slows down netns cleanup a lot. We have two APIs to unregister a hook: nf_unregister_net_hook (which calls synchronize_net()) and nf_unregister_net_hooks (calls nf_unregister_net_hook in a loop) Make nf_unregister_net_hook a wapper around new helper __nf_unregister_net_hook, which unlinks the hook but does not free it. Then, we can call that helper in nf_unregister_net_hooks and then call synchronize_net() only once. Andrey Konovalov reports this change improves syzkaller fuzzing speed at least twice. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/core.c | 46 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/net/netfilter/core.c b/net/netfilter/core.c index a87a6f8a74d8..b5d908851cc8 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -126,14 +126,15 @@ int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg) } EXPORT_SYMBOL(nf_register_net_hook); -void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) +static struct nf_hook_entry * +__nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) { struct nf_hook_entry __rcu **pp; struct nf_hook_entry *p; pp = nf_hook_entry_head(net, reg); if (WARN_ON_ONCE(!pp)) - return; + return NULL; mutex_lock(&nf_hook_mutex); for (; (p = nf_entry_dereference(*pp)) != NULL; pp = &p->next) { @@ -145,7 +146,7 @@ void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) mutex_unlock(&nf_hook_mutex); if (!p) { WARN(1, "nf_unregister_net_hook: hook not found!\n"); - return; + return NULL; } #ifdef CONFIG_NETFILTER_INGRESS if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) @@ -154,6 +155,17 @@ void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) #ifdef HAVE_JUMP_LABEL static_key_slow_dec(&nf_hooks_needed[reg->pf][reg->hooknum]); #endif + + return p; +} + +void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) +{ + struct nf_hook_entry *p = __nf_unregister_net_hook(net, reg); + + if (!p) + return; + synchronize_net(); nf_queue_nf_hook_drop(net, p); /* other cpu might still process nfqueue verdict that used reg */ @@ -183,10 +195,32 @@ err: EXPORT_SYMBOL(nf_register_net_hooks); void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg, - unsigned int n) + unsigned int hookcount) { - while (n-- > 0) - nf_unregister_net_hook(net, ®[n]); + struct nf_hook_entry *to_free[16]; + unsigned int i, n; + + do { + n = min_t(unsigned int, hookcount, ARRAY_SIZE(to_free)); + + for (i = 0; i < n; i++) + to_free[i] = __nf_unregister_net_hook(net, ®[i]); + + synchronize_net(); + + for (i = 0; i < n; i++) { + if (to_free[i]) + nf_queue_nf_hook_drop(net, to_free[i]); + } + + synchronize_net(); + + for (i = 0; i < n; i++) + kfree(to_free[i]); + + reg += n; + hookcount -= n; + } while (hookcount > 0); } EXPORT_SYMBOL(nf_unregister_net_hooks); -- cgit From c83fa19603bdaeef17b815713dbbe3230c8a34ee Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 25 Apr 2017 10:24:03 +0200 Subject: netfilter: nf_log: don't call synchronize_rcu in nf_log_unset nf_log_unregister() (which is what gets called in the logger backends module exit paths) does a (required, module is removed) synchronize_rcu(). But nf_log_unset() is only called from pernet exit handlers. It doesn't free any memory so there appears to be no need to call synchronize_rcu. v2: Liping Zhang points out that nf_log_unregister() needs to be called after pernet unregister, else rmmod would become unsafe. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_log.c | 1 - net/netfilter/nfnetlink_log.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index cc32727e3f32..8bb152a7cca4 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -71,7 +71,6 @@ void nf_log_unset(struct net *net, const struct nf_logger *logger) RCU_INIT_POINTER(net->nf.nf_loggers[i], NULL); } mutex_unlock(&nf_log_mutex); - synchronize_rcu(); } EXPORT_SYMBOL(nf_log_unset); diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 896741206a50..da9704971a83 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -1140,10 +1140,10 @@ out: static void __exit nfnetlink_log_fini(void) { - nf_log_unregister(&nfulnl_logger); nfnetlink_subsys_unregister(&nfulnl_subsys); netlink_unregister_notifier(&nfulnl_rtnl_notifier); unregister_pernet_subsys(&nfnl_log_net_ops); + nf_log_unregister(&nfulnl_logger); } MODULE_DESCRIPTION("netfilter userspace logging"); -- cgit From 039b40ee5854dc733cf786fee4a88e240a012115 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 24 Apr 2017 15:37:41 +0200 Subject: netfilter: nf_queue: only call synchronize_net twice if nf_queue is active nf_unregister_net_hook(s) can avoid a second call to synchronize_net, provided there is no nfqueue active in that net namespace (which is the common case). This also gets rid of the extra arg to nf_queue_nf_hook_drop(), normally this gets called during netns cleanup so no packets should be queued. For the rare case of base chain being unregistered or module removal while nfqueue is in use the extra hiccup due to the packet drops isn't a big deal. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_queue.h | 3 +-- net/netfilter/core.c | 21 ++++++++++++--------- net/netfilter/nf_internals.h | 2 +- net/netfilter/nf_queue.c | 7 +++++-- net/netfilter/nfnetlink_queue.c | 18 ++++++++---------- 5 files changed, 27 insertions(+), 24 deletions(-) diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index 09948d10e38e..4454719ff849 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -24,8 +24,7 @@ struct nf_queue_entry { struct nf_queue_handler { int (*outfn)(struct nf_queue_entry *entry, unsigned int queuenum); - void (*nf_hook_drop)(struct net *net, - const struct nf_hook_entry *hooks); + unsigned int (*nf_hook_drop)(struct net *net); }; void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh); diff --git a/net/netfilter/core.c b/net/netfilter/core.c index b5d908851cc8..552d606e57ca 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -162,14 +162,17 @@ __nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) { struct nf_hook_entry *p = __nf_unregister_net_hook(net, reg); + unsigned int nfq; if (!p) return; synchronize_net(); - nf_queue_nf_hook_drop(net, p); + /* other cpu might still process nfqueue verdict that used reg */ - synchronize_net(); + nfq = nf_queue_nf_hook_drop(net); + if (nfq) + synchronize_net(); kfree(p); } EXPORT_SYMBOL(nf_unregister_net_hook); @@ -198,7 +201,7 @@ void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg, unsigned int hookcount) { struct nf_hook_entry *to_free[16]; - unsigned int i, n; + unsigned int i, n, nfq; do { n = min_t(unsigned int, hookcount, ARRAY_SIZE(to_free)); @@ -208,12 +211,12 @@ void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg, synchronize_net(); - for (i = 0; i < n; i++) { - if (to_free[i]) - nf_queue_nf_hook_drop(net, to_free[i]); - } - - synchronize_net(); + /* need 2nd synchronize_net() if nfqueue is used, skb + * can get reinjected right before nf_queue_hook_drop() + */ + nfq = nf_queue_nf_hook_drop(net); + if (nfq) + synchronize_net(); for (i = 0; i < n; i++) kfree(to_free[i]); diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h index c46d214d5323..bfa742da83af 100644 --- a/net/netfilter/nf_internals.h +++ b/net/netfilter/nf_internals.h @@ -14,7 +14,7 @@ /* nf_queue.c */ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, struct nf_hook_entry **entryp, unsigned int verdict); -void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_entry *entry); +unsigned int nf_queue_nf_hook_drop(struct net *net); int __init netfilter_queue_init(void); /* nf_log.c */ diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 4a7662486f44..043850c9d154 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -96,15 +96,18 @@ void nf_queue_entry_get_refs(struct nf_queue_entry *entry) } EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs); -void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_entry *entry) +unsigned int nf_queue_nf_hook_drop(struct net *net) { const struct nf_queue_handler *qh; + unsigned int count = 0; rcu_read_lock(); qh = rcu_dereference(net->nf.queue_handler); if (qh) - qh->nf_hook_drop(net, entry); + count = qh->nf_hook_drop(net); rcu_read_unlock(); + + return count; } static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state, diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index d09ab49e102a..dd8ec5b0fcd9 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -922,16 +922,10 @@ static struct notifier_block nfqnl_dev_notifier = { .notifier_call = nfqnl_rcv_dev_event, }; -static int nf_hook_cmp(struct nf_queue_entry *entry, unsigned long entry_ptr) -{ - return rcu_access_pointer(entry->hook) == - (struct nf_hook_entry *)entry_ptr; -} - -static void nfqnl_nf_hook_drop(struct net *net, - const struct nf_hook_entry *hook) +static unsigned int nfqnl_nf_hook_drop(struct net *net) { struct nfnl_queue_net *q = nfnl_queue_pernet(net); + unsigned int instances = 0; int i; rcu_read_lock(); @@ -939,10 +933,14 @@ static void nfqnl_nf_hook_drop(struct net *net, struct nfqnl_instance *inst; struct hlist_head *head = &q->instance_table[i]; - hlist_for_each_entry_rcu(inst, head, hlist) - nfqnl_flush(inst, nf_hook_cmp, (unsigned long)hook); + hlist_for_each_entry_rcu(inst, head, hlist) { + nfqnl_flush(inst, NULL, 0); + instances++; + } } rcu_read_unlock(); + + return instances; } static int -- cgit From 0e72f55f3510e722e725c54678800e99853faa3b Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 27 Apr 2017 16:39:43 +0200 Subject: netfilter: snmp: avoid stack size warning net/ipv4/netfilter/nf_nat_snmp_basic.c:1158:1: warning: the frame size of 1160 bytes is larger than 1024 bytes Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/nf_nat_snmp_basic.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c index da04b9c33ef3..d5b1e0b3f687 100644 --- a/net/ipv4/netfilter/nf_nat_snmp_basic.c +++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c @@ -827,8 +827,8 @@ static unsigned char snmp_object_decode(struct asn1_ctx *ctx, return 1; } -static unsigned char snmp_request_decode(struct asn1_ctx *ctx, - struct snmp_request *request) +static unsigned char noinline_for_stack +snmp_request_decode(struct asn1_ctx *ctx, struct snmp_request *request) { unsigned int cls, con, tag; unsigned char *end; @@ -920,10 +920,10 @@ static inline void mangle_address(unsigned char *begin, } } -static unsigned char snmp_trap_decode(struct asn1_ctx *ctx, - struct snmp_v1_trap *trap, - const struct oct1_map *map, - __sum16 *check) +static unsigned char noinline_for_stack +snmp_trap_decode(struct asn1_ctx *ctx, struct snmp_v1_trap *trap, + const struct oct1_map *map, + __sum16 *check) { unsigned int cls, con, tag, len; unsigned char *end; -- cgit From 8eeef2350453aa012d846457eb6ecd012a35d99b Mon Sep 17 00:00:00 2001 From: Liping Zhang Date: Sat, 29 Apr 2017 21:59:49 +0800 Subject: netfilter: nf_ct_ext: invoke destroy even when ext is not attached For NF_NAT_MANIP_SRC, we will insert the ct to the nat_bysource_table, then remove it from the nat_bysource_table via nat_extend->destroy. But now, the nat extension is attached on demand, so if the nat extension is not attached, we will not be notified when the ct is destroyed, i.e. we may fail to remove ct from the nat_bysource_table. So just keep it simple, even if the extension is not attached, we will still invoke the related ext->destroy. And this will also preserve the flexibility for the future extension. Fixes: 9a08ecfe74d7 ("netfilter: don't attach a nat extension by default") Signed-off-by: Liping Zhang Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_extend.h | 7 +------ net/netfilter/nf_conntrack_extend.c | 8 ++------ 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index b01f73fb4dcb..4944bc9153cf 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -69,12 +69,7 @@ static inline void *__nf_ct_ext_find(const struct nf_conn *ct, u8 id) ((id##_TYPE *)__nf_ct_ext_find((ext), (id))) /* Destroy all relationships */ -void __nf_ct_ext_destroy(struct nf_conn *ct); -static inline void nf_ct_ext_destroy(struct nf_conn *ct) -{ - if (ct->ext) - __nf_ct_ext_destroy(ct); -} +void nf_ct_ext_destroy(struct nf_conn *ct); /* Free operation. If you want to free a object referred from private area, * please implement __nf_ct_ext_free() and call it. diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 68ae1be08ed8..6c605e88ebae 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -20,16 +20,12 @@ static struct nf_ct_ext_type __rcu *nf_ct_ext_types[NF_CT_EXT_NUM]; static DEFINE_MUTEX(nf_ct_ext_type_mutex); #define NF_CT_EXT_PREALLOC 128u /* conntrack events are on by default */ -void __nf_ct_ext_destroy(struct nf_conn *ct) +void nf_ct_ext_destroy(struct nf_conn *ct) { unsigned int i; struct nf_ct_ext_type *t; - struct nf_ct_ext *ext = ct->ext; for (i = 0; i < NF_CT_EXT_NUM; i++) { - if (!__nf_ct_ext_exist(ext, i)) - continue; - rcu_read_lock(); t = rcu_dereference(nf_ct_ext_types[i]); @@ -42,7 +38,7 @@ void __nf_ct_ext_destroy(struct nf_conn *ct) rcu_read_unlock(); } } -EXPORT_SYMBOL(__nf_ct_ext_destroy); +EXPORT_SYMBOL(nf_ct_ext_destroy); void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) { -- cgit From 45d9b378e85f1b00ac047626827c68589168936c Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 30 Apr 2017 21:46:45 -0700 Subject: netlink: add NULL-friendly helper for setting extended ACK message As we propagate extended ack reporting throughout various paths in the kernel it may be that the same function is called with the extended ack parameter passed as NULL. One place where that happens is in drivers which have a centralized reconfiguration function called both from ndos and from ethtool_ops. Add a new helper for setting the error message in such conditions. Existing helper is left as is to encourage propagating the ext act fully wherever possible. It also makes it clear in the code which messages may be lost due to ext ack being NULL. Signed-off-by: Jakub Kicinski Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/netlink.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 8d2a8924705c..c20395edf2de 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -92,6 +92,14 @@ struct netlink_ext_ack { (extack)->_msg = _msg; \ } while (0) +#define NL_MOD_TRY_SET_ERR_MSG(extack, msg) do { \ + static const char _msg[] = KBUILD_MODNAME ": " msg; \ + struct netlink_ext_ack *_extack = (extack); \ + \ + if (_extack) \ + _extack->_msg = _msg; \ +} while (0) + extern void netlink_kernel_release(struct sock *sk); extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups); extern int netlink_change_ngroups(struct sock *sk, unsigned int groups); -- cgit From ddf9f970764f4390aba767e77fddaaced4a6760d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 30 Apr 2017 21:46:46 -0700 Subject: xdp: propagate extended ack to XDP setup Drivers usually have a number of restrictions for running XDP - most common being buffer sizes, LRO and number of rings. Even though some drivers try to be helpful and print error messages experience shows that users don't often consult kernel logs on netlink errors. Try to use the new extended ack mechanism to carry the message back to user space. Signed-off-by: Jakub Kicinski Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/netdevice.h | 10 ++++++++-- net/core/dev.c | 5 ++++- net/core/rtnetlink.c | 13 ++++++++----- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 6847714a5ae3..9c23bd2efb56 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -813,11 +813,16 @@ enum xdp_netdev_command { XDP_QUERY_PROG, }; +struct netlink_ext_ack; + struct netdev_xdp { enum xdp_netdev_command command; union { /* XDP_SETUP_PROG */ - struct bpf_prog *prog; + struct { + struct bpf_prog *prog; + struct netlink_ext_ack *extack; + }; /* XDP_QUERY_PROG */ bool prog_attached; }; @@ -3291,7 +3296,8 @@ int dev_get_phys_port_id(struct net_device *dev, int dev_get_phys_port_name(struct net_device *dev, char *name, size_t len); int dev_change_proto_down(struct net_device *dev, bool proto_down); -int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags); +int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, + int fd, u32 flags); struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev); struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq, int *ret); diff --git a/net/core/dev.c b/net/core/dev.c index 8371a01eee87..35a06cebb282 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6854,12 +6854,14 @@ EXPORT_SYMBOL(dev_change_proto_down); /** * dev_change_xdp_fd - set or clear a bpf program for a device rx path * @dev: device + * @extact: netlink extended ack * @fd: new program fd or negative value to clear * @flags: xdp-related flags * * Set or clear a bpf program for a device */ -int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags) +int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, + int fd, u32 flags) { int (*xdp_op)(struct net_device *dev, struct netdev_xdp *xdp); const struct net_device_ops *ops = dev->netdev_ops; @@ -6892,6 +6894,7 @@ int dev_change_xdp_fd(struct net_device *dev, int fd, u32 flags) memset(&xdp, 0, sizeof(xdp)); xdp.command = XDP_SETUP_PROG; + xdp.extack = extack; xdp.prog = prog; err = xdp_op(dev, &xdp); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 9031a6c8bfa7..6e67315ec368 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1919,6 +1919,7 @@ static int do_set_master(struct net_device *dev, int ifindex) #define DO_SETLINK_NOTIFY 0x03 static int do_setlink(const struct sk_buff *skb, struct net_device *dev, struct ifinfomsg *ifm, + struct netlink_ext_ack *extack, struct nlattr **tb, char *ifname, int status) { const struct net_device_ops *ops = dev->netdev_ops; @@ -2201,7 +2202,7 @@ static int do_setlink(const struct sk_buff *skb, } if (xdp[IFLA_XDP_FD]) { - err = dev_change_xdp_fd(dev, + err = dev_change_xdp_fd(dev, extack, nla_get_s32(xdp[IFLA_XDP_FD]), xdp_flags); if (err) @@ -2261,7 +2262,7 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, if (err < 0) goto errout; - err = do_setlink(skb, dev, ifm, tb, ifname, 0); + err = do_setlink(skb, dev, ifm, extack, tb, ifname, 0); errout: return err; } @@ -2423,6 +2424,7 @@ EXPORT_SYMBOL(rtnl_create_link); static int rtnl_group_changelink(const struct sk_buff *skb, struct net *net, int group, struct ifinfomsg *ifm, + struct netlink_ext_ack *extack, struct nlattr **tb) { struct net_device *dev, *aux; @@ -2430,7 +2432,7 @@ static int rtnl_group_changelink(const struct sk_buff *skb, for_each_netdev_safe(net, dev, aux) { if (dev->group == group) { - err = do_setlink(skb, dev, ifm, tb, NULL, 0); + err = do_setlink(skb, dev, ifm, extack, tb, NULL, 0); if (err < 0) return err; } @@ -2576,14 +2578,15 @@ replay: status |= DO_SETLINK_NOTIFY; } - return do_setlink(skb, dev, ifm, tb, ifname, status); + return do_setlink(skb, dev, ifm, extack, tb, ifname, + status); } if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { if (ifm->ifi_index == 0 && tb[IFLA_GROUP]) return rtnl_group_changelink(skb, net, nla_get_u32(tb[IFLA_GROUP]), - ifm, tb); + ifm, extack, tb); return -ENODEV; } -- cgit From d957c0f711aaeaac6bbffd82098737ac10b7985d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 30 Apr 2017 21:46:47 -0700 Subject: nfp: make use of extended ack message reporting Try to carry error messages to the user via the netlink extended ack message attribute. Signed-off-by: Jakub Kicinski Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 3 ++- .../net/ethernet/netronome/nfp/nfp_net_common.c | 22 +++++++++++++--------- .../net/ethernet/netronome/nfp/nfp_net_ethtool.c | 4 ++-- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 38b41fdeaa8f..fcf81b3be830 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -818,7 +818,8 @@ nfp_net_irqs_assign(struct nfp_net *nn, struct msix_entry *irq_entries, unsigned int n); struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn); -int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new); +int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new, + struct netlink_ext_ack *extack); bool nfp_net_link_changed_read_clear(struct nfp_net *nn); int nfp_net_refresh_eth_port(struct nfp_net *nn); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index b9f3548bb65f..db20376260f5 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -2524,24 +2524,27 @@ struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn) return new; } -static int nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp) +static int +nfp_net_check_config(struct nfp_net *nn, struct nfp_net_dp *dp, + struct netlink_ext_ack *extack) { /* XDP-enabled tests */ if (!dp->xdp_prog) return 0; if (dp->fl_bufsz > PAGE_SIZE) { - nn_warn(nn, "MTU too large w/ XDP enabled\n"); + NL_MOD_TRY_SET_ERR_MSG(extack, "MTU too large w/ XDP enabled"); return -EINVAL; } if (dp->num_tx_rings > nn->max_tx_rings) { - nn_warn(nn, "Insufficient number of TX rings w/ XDP enabled\n"); + NL_MOD_TRY_SET_ERR_MSG(extack, "Insufficient number of TX rings w/ XDP enabled"); return -EINVAL; } return 0; } -int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp) +int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp, + struct netlink_ext_ack *extack) { int r, err; @@ -2553,7 +2556,7 @@ int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *dp) dp->num_r_vecs = max(dp->num_rx_rings, dp->num_stack_tx_rings); - err = nfp_net_check_config(nn, dp); + err = nfp_net_check_config(nn, dp, extack); if (err) goto exit_free_dp; @@ -2628,7 +2631,7 @@ static int nfp_net_change_mtu(struct net_device *netdev, int new_mtu) dp->mtu = new_mtu; - return nfp_net_ring_reconfig(nn, dp); + return nfp_net_ring_reconfig(nn, dp, NULL); } static void nfp_net_stat64(struct net_device *netdev, @@ -2944,9 +2947,10 @@ static int nfp_net_xdp_offload(struct nfp_net *nn, struct bpf_prog *prog) return ret; } -static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) +static int nfp_net_xdp_setup(struct nfp_net *nn, struct netdev_xdp *xdp) { struct bpf_prog *old_prog = nn->dp.xdp_prog; + struct bpf_prog *prog = xdp->prog; struct nfp_net_dp *dp; int err; @@ -2969,7 +2973,7 @@ static int nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog) dp->rx_dma_off = prog ? XDP_PACKET_HEADROOM - nn->dp.rx_offset : 0; /* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */ - err = nfp_net_ring_reconfig(nn, dp); + err = nfp_net_ring_reconfig(nn, dp, xdp->extack); if (err) return err; @@ -2987,7 +2991,7 @@ static int nfp_net_xdp(struct net_device *netdev, struct netdev_xdp *xdp) switch (xdp->command) { case XDP_SETUP_PROG: - return nfp_net_xdp_setup(nn, xdp->prog); + return nfp_net_xdp_setup(nn, xdp); case XDP_QUERY_PROG: xdp->prog_attached = !!nn->dp.xdp_prog; return 0; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index a704efd4e314..abbb47e60cc3 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -309,7 +309,7 @@ static int nfp_net_set_ring_size(struct nfp_net *nn, u32 rxd_cnt, u32 txd_cnt) dp->rxd_cnt = rxd_cnt; dp->txd_cnt = txd_cnt; - return nfp_net_ring_reconfig(nn, dp); + return nfp_net_ring_reconfig(nn, dp, NULL); } static int nfp_net_set_ringparam(struct net_device *netdev, @@ -880,7 +880,7 @@ static int nfp_net_set_num_rings(struct nfp_net *nn, unsigned int total_rx, if (dp->xdp_prog) dp->num_tx_rings += total_rx; - return nfp_net_ring_reconfig(nn, dp); + return nfp_net_ring_reconfig(nn, dp, NULL); } static int nfp_net_set_channels(struct net_device *netdev, -- cgit From 9861ce039c2a4ff3e50eb7c679dadc15a8469e3f Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 30 Apr 2017 21:46:48 -0700 Subject: virtio_net: make use of extended ack message reporting Try to carry error messages to the user via the netlink extended ack message attribute. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 7877551fe4e0..3d0bc484b3d7 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1878,7 +1878,8 @@ err: return ret; } -static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog) +static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog, + struct netlink_ext_ack *extack) { unsigned long int max_sz = PAGE_SIZE - sizeof(struct padded_vnet_hdr); struct virtnet_info *vi = netdev_priv(dev); @@ -1890,16 +1891,17 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog) virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) || virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ECN) || virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO)) { - netdev_warn(dev, "can't set XDP while host is implementing LRO, disable LRO first\n"); + NL_SET_ERR_MSG(extack, "can't set XDP while host is implementing LRO, disable LRO first"); return -EOPNOTSUPP; } if (vi->mergeable_rx_bufs && !vi->any_header_sg) { - netdev_warn(dev, "XDP expects header/data in single page, any_header_sg required\n"); + NL_SET_ERR_MSG(extack, "XDP expects header/data in single page, any_header_sg required"); return -EINVAL; } if (dev->mtu > max_sz) { + NL_SET_ERR_MSG(extack, "MTU too large to enable XDP"); netdev_warn(dev, "XDP requires MTU less than %lu\n", max_sz); return -EINVAL; } @@ -1910,6 +1912,7 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog) /* XDP requires extra queues for XDP_TX */ if (curr_qp + xdp_qp > vi->max_queue_pairs) { + NL_SET_ERR_MSG(extack, "Too few free TX rings available"); netdev_warn(dev, "request %i queues but max is %i\n", curr_qp + xdp_qp, vi->max_queue_pairs); return -ENOMEM; @@ -1971,7 +1974,7 @@ static int virtnet_xdp(struct net_device *dev, struct netdev_xdp *xdp) { switch (xdp->command) { case XDP_SETUP_PROG: - return virtnet_xdp_set(dev, xdp->prog); + return virtnet_xdp_set(dev, xdp->prog, xdp->extack); case XDP_QUERY_PROG: xdp->prog_attached = virtnet_xdp_query(dev); return 0; -- cgit From 6387d0111ca4740b69a082a92fc373185af11133 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 1 May 2017 11:26:15 +0200 Subject: samples/bpf: fix SKB_MODE flag to be a 32-bit unsigned int The kernel side of XDP_FLAGS_SKB_MODE is unsigned, and the rtnetlink IFLA_XDP_FLAGS is defined as NLA_U32. Thus, userspace programs under samples/bpf/ should use the correct type. Fixes: 3993f2cb983b ("samples/bpf: Add support for SKB_MODE to xdp1 and xdp_tx_iptunnel") Signed-off-by: Jesper Dangaard Brouer Acked-by: Daniel Borkmann Reviewed-by: Andy Gospodarek Signed-off-by: David S. Miller --- samples/bpf/bpf_load.c | 3 ++- samples/bpf/bpf_load.h | 2 +- samples/bpf/xdp1_user.c | 8 ++++---- samples/bpf/xdp_tx_iptunnel_user.c | 8 ++++---- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c index 0ec0dea3c41e..4221dc359453 100644 --- a/samples/bpf/bpf_load.c +++ b/samples/bpf/bpf_load.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -585,7 +586,7 @@ struct ksym *ksym_search(long key) return &syms[0]; } -int set_link_xdp_fd(int ifindex, int fd, int flags) +int set_link_xdp_fd(int ifindex, int fd, __u32 flags) { struct sockaddr_nl sa; int sock, seq = 0, len, ret = -1; diff --git a/samples/bpf/bpf_load.h b/samples/bpf/bpf_load.h index 6bfd75ec6a16..05822f83173a 100644 --- a/samples/bpf/bpf_load.h +++ b/samples/bpf/bpf_load.h @@ -47,5 +47,5 @@ struct ksym { int load_kallsyms(void); struct ksym *ksym_search(long key); -int set_link_xdp_fd(int ifindex, int fd, int flags); +int set_link_xdp_fd(int ifindex, int fd, __u32 flags); #endif diff --git a/samples/bpf/xdp1_user.c b/samples/bpf/xdp1_user.c index deb05e630d84..378850c70eb8 100644 --- a/samples/bpf/xdp1_user.c +++ b/samples/bpf/xdp1_user.c @@ -20,11 +20,11 @@ #include "libbpf.h" static int ifindex; -static int flags; +static __u32 xdp_flags; static void int_exit(int sig) { - set_link_xdp_fd(ifindex, -1, flags); + set_link_xdp_fd(ifindex, -1, xdp_flags); exit(0); } @@ -75,7 +75,7 @@ int main(int argc, char **argv) while ((opt = getopt(argc, argv, optstr)) != -1) { switch (opt) { case 'S': - flags |= XDP_FLAGS_SKB_MODE; + xdp_flags |= XDP_FLAGS_SKB_MODE; break; default: usage(basename(argv[0])); @@ -103,7 +103,7 @@ int main(int argc, char **argv) signal(SIGINT, int_exit); - if (set_link_xdp_fd(ifindex, prog_fd[0], flags) < 0) { + if (set_link_xdp_fd(ifindex, prog_fd[0], xdp_flags) < 0) { printf("link set xdp fd failed\n"); return 1; } diff --git a/samples/bpf/xdp_tx_iptunnel_user.c b/samples/bpf/xdp_tx_iptunnel_user.c index cb2bda7b5346..880dd4aebfa4 100644 --- a/samples/bpf/xdp_tx_iptunnel_user.c +++ b/samples/bpf/xdp_tx_iptunnel_user.c @@ -142,8 +142,8 @@ int main(int argc, char **argv) struct iptnl_info tnl = {}; struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; struct vip vip = {}; + __u32 xdp_flags = 0; char filename[256]; - int flags = 0; int opt; int i; @@ -204,7 +204,7 @@ int main(int argc, char **argv) kill_after_s = atoi(optarg); break; case 'S': - flags |= XDP_FLAGS_SKB_MODE; + xdp_flags |= XDP_FLAGS_SKB_MODE; break; default: usage(argv[0]); @@ -248,14 +248,14 @@ int main(int argc, char **argv) } } - if (set_link_xdp_fd(ifindex, prog_fd[0], flags) < 0) { + if (set_link_xdp_fd(ifindex, prog_fd[0], xdp_flags) < 0) { printf("link set xdp fd failed\n"); return 1; } poll_stats(kill_after_s); - set_link_xdp_fd(ifindex, -1, flags); + set_link_xdp_fd(ifindex, -1, xdp_flags); return 0; } -- cgit From f76254a845a661ccdb9fa246ee72197f90a8d3dd Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 1 May 2017 11:26:20 +0200 Subject: samples/bpf: fix XDP_FLAGS_SKB_MODE detach for xdp_tx_iptunnel The xdp_tx_iptunnel program can be terminated in two ways, after N-seconds or via Ctrl-C SIGINT. The SIGINT code path does not handle detatching the correct XDP program, in-case the program was attached with XDP_FLAGS_SKB_MODE. Fix this by storing the XDP flags as a global variable, which is available for the SIGINT handler function. Fixes: 3993f2cb983b ("samples/bpf: Add support for SKB_MODE to xdp1 and xdp_tx_iptunnel") Signed-off-by: Jesper Dangaard Brouer Acked-by: Daniel Borkmann Reviewed-by: Andy Gospodarek Signed-off-by: David S. Miller --- samples/bpf/xdp_tx_iptunnel_user.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/bpf/xdp_tx_iptunnel_user.c b/samples/bpf/xdp_tx_iptunnel_user.c index 880dd4aebfa4..92b8bde9337c 100644 --- a/samples/bpf/xdp_tx_iptunnel_user.c +++ b/samples/bpf/xdp_tx_iptunnel_user.c @@ -25,11 +25,12 @@ #define STATS_INTERVAL_S 2U static int ifindex = -1; +static __u32 xdp_flags = 0; static void int_exit(int sig) { if (ifindex > -1) - set_link_xdp_fd(ifindex, -1, 0); + set_link_xdp_fd(ifindex, -1, xdp_flags); exit(0); } @@ -142,7 +143,6 @@ int main(int argc, char **argv) struct iptnl_info tnl = {}; struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; struct vip vip = {}; - __u32 xdp_flags = 0; char filename[256]; int opt; int i; -- cgit From 1a7fca63cd405fc77a9c9ce9291792f2a7d222a4 Mon Sep 17 00:00:00 2001 From: Benjamin LaHaise Date: Mon, 1 May 2017 09:58:40 -0400 Subject: flower: check unused bits in MPLS fields Since several of the the netlink attributes used to configure the flower classifier's MPLS TC, BOS and Label fields have additional bits which are unused, check those bits to ensure that they are actually 0 as suggested by Jamal. Signed-off-by: Benjamin LaHaise Cc: David Miller Cc: Jamal Hadi Salim Cc: Simon Horman Cc: Jakub Kicinski Cc: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_flower.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 3ecf07666df3..ca526c0881bd 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -439,29 +439,39 @@ static void fl_set_key_val(struct nlattr **tb, memcpy(mask, nla_data(tb[mask_type]), len); } -static void fl_set_key_mpls(struct nlattr **tb, - struct flow_dissector_key_mpls *key_val, - struct flow_dissector_key_mpls *key_mask) +static int fl_set_key_mpls(struct nlattr **tb, + struct flow_dissector_key_mpls *key_val, + struct flow_dissector_key_mpls *key_mask) { if (tb[TCA_FLOWER_KEY_MPLS_TTL]) { key_val->mpls_ttl = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TTL]); key_mask->mpls_ttl = MPLS_TTL_MASK; } if (tb[TCA_FLOWER_KEY_MPLS_BOS]) { - key_val->mpls_bos = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_BOS]); + u8 bos = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_BOS]); + + if (bos & ~MPLS_BOS_MASK) + return -EINVAL; + key_val->mpls_bos = bos; key_mask->mpls_bos = MPLS_BOS_MASK; } if (tb[TCA_FLOWER_KEY_MPLS_TC]) { - key_val->mpls_tc = - nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TC]) & MPLS_TC_MASK; + u8 tc = nla_get_u8(tb[TCA_FLOWER_KEY_MPLS_TC]); + + if (tc & ~MPLS_TC_MASK) + return -EINVAL; + key_val->mpls_tc = tc; key_mask->mpls_tc = MPLS_TC_MASK; } if (tb[TCA_FLOWER_KEY_MPLS_LABEL]) { - key_val->mpls_label = - nla_get_u32(tb[TCA_FLOWER_KEY_MPLS_LABEL]) & - MPLS_LABEL_MASK; + u32 label = nla_get_u32(tb[TCA_FLOWER_KEY_MPLS_LABEL]); + + if (label & ~MPLS_LABEL_MASK) + return -EINVAL; + key_val->mpls_label = label; key_mask->mpls_label = MPLS_LABEL_MASK; } + return 0; } static void fl_set_key_vlan(struct nlattr **tb, @@ -622,7 +632,9 @@ static int fl_set_key(struct net *net, struct nlattr **tb, sizeof(key->icmp.code)); } else if (key->basic.n_proto == htons(ETH_P_MPLS_UC) || key->basic.n_proto == htons(ETH_P_MPLS_MC)) { - fl_set_key_mpls(tb, &key->mpls, &mask->mpls); + ret = fl_set_key_mpls(tb, &key->mpls, &mask->mpls); + if (ret) + return ret; } else if (key->basic.n_proto == htons(ETH_P_ARP) || key->basic.n_proto == htons(ETH_P_RARP)) { fl_set_key_val(tb, &key->arp.sip, TCA_FLOWER_KEY_ARP_SIP, -- cgit From 2faf26575350e49c1e242b7a464e9302f78b9b15 Mon Sep 17 00:00:00 2001 From: Karim Eshapa Date: Mon, 1 May 2017 15:58:08 +0200 Subject: benet: Use time_before_eq for time comparison Use time_before_eq for time comparison more safe and dealing with timer wrapping to be future-proof. Signed-off-by: Karim Eshapa Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 8702661b99c0..f3a09ab55900 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -5268,15 +5268,15 @@ static bool be_err_is_recoverable(struct be_adapter *adapter) dev_err(&adapter->pdev->dev, "Recoverable HW error code: 0x%x\n", ue_err_code); - if (jiffies - err_rec->probe_time <= initial_idle_time) { + if (time_before_eq(jiffies - err_rec->probe_time, initial_idle_time)) { dev_err(&adapter->pdev->dev, "Cannot recover within %lu sec from driver load\n", jiffies_to_msecs(initial_idle_time) / MSEC_PER_SEC); return false; } - if (err_rec->last_recovery_time && - (jiffies - err_rec->last_recovery_time <= recovery_interval)) { + if (err_rec->last_recovery_time && time_before_eq( + jiffies - err_rec->last_recovery_time, recovery_interval)) { dev_err(&adapter->pdev->dev, "Cannot recover within %lu sec from last recovery\n", jiffies_to_msecs(recovery_interval) / MSEC_PER_SEC); -- cgit From 332270fdc8b6fba07d059a9ad44df9e1a2ad4529 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Sat, 29 Apr 2017 22:52:42 -0700 Subject: bpf: enhance verifier to understand stack pointer arithmetic llvm 4.0 and above generates the code like below: .... 440: (b7) r1 = 15 441: (05) goto pc+73 515: (79) r6 = *(u64 *)(r10 -152) 516: (bf) r7 = r10 517: (07) r7 += -112 518: (bf) r2 = r7 519: (0f) r2 += r1 520: (71) r1 = *(u8 *)(r8 +0) 521: (73) *(u8 *)(r2 +45) = r1 .... and the verifier complains "R2 invalid mem access 'inv'" for insn #521. This is because verifier marks register r2 as unknown value after #519 where r2 is a stack pointer and r1 holds a constant value. Teach verifier to recognize "stack_ptr + imm" and "stack_ptr + reg with const val" as valid stack_ptr with new offset. Signed-off-by: Yonghong Song Acked-by: Martin KaFai Lau Acked-by: Daniel Borkmann Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- kernel/bpf/verifier.c | 11 +++++++++++ tools/testing/selftests/bpf/test_verifier.c | 18 ++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 6f8b6ed690be..c2ff608c1984 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1922,6 +1922,17 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) dst_reg->type = PTR_TO_STACK; dst_reg->imm = insn->imm; return 0; + } else if (opcode == BPF_ADD && + BPF_CLASS(insn->code) == BPF_ALU64 && + dst_reg->type == PTR_TO_STACK && + ((BPF_SRC(insn->code) == BPF_X && + regs[insn->src_reg].type == CONST_IMM) || + BPF_SRC(insn->code) == BPF_K)) { + if (BPF_SRC(insn->code) == BPF_X) + dst_reg->imm += regs[insn->src_reg].imm; + else + dst_reg->imm += insn->imm; + return 0; } else if (opcode == BPF_ADD && BPF_CLASS(insn->code) == BPF_ALU64 && (dst_reg->type == PTR_TO_PACKET || diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index d3395c192a24..3773562056da 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -1932,16 +1932,22 @@ static struct bpf_test tests[] = { .result = ACCEPT, }, { - "unpriv: obfuscate stack pointer", + "stack pointer arithmetic", .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_1, 4), + BPF_JMP_IMM(BPF_JA, 0, 0, 0), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -10), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_7), + BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_1), + BPF_ST_MEM(0, BPF_REG_2, 4, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_7), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 8), + BPF_ST_MEM(0, BPF_REG_2, 4, 0), BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr_unpriv = "R2 pointer arithmetic", - .result_unpriv = REJECT, .result = ACCEPT, }, { -- cgit From 793ea8a9c7b4b4383348a717cb5c86c1bbdba30a Mon Sep 17 00:00:00 2001 From: Ram Amrani Date: Sun, 30 Apr 2017 11:49:05 +0300 Subject: qed: configure the RoCE max message size Signed-off-by: Ram Amrani Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_roce.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c index b8c811f95205..5d4061577767 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_roce.c +++ b/drivers/net/ethernet/qlogic/qed/qed_roce.c @@ -750,6 +750,8 @@ static struct qed_rdma_port *qed_rdma_query_port(void *rdma_cxt) p_port->link_speed = p_hwfn->mcp_info->link_output.speed; + p_port->max_msg_size = RDMA_MAX_DATA_SIZE_IN_WQE; + return p_port; } -- cgit From ba0154e96449a5be3360d3a07bc4b6d476e2667e Mon Sep 17 00:00:00 2001 From: Ram Amrani Date: Sun, 30 Apr 2017 11:49:06 +0300 Subject: qed: remove unused SQ error state The internal RoCE SQE QP state isn't being used. Instead we mark the QP as in regular error state. Signed-off-by: Ram Amrani Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_roce.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c index 5d4061577767..01244d7beeb9 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_roce.c +++ b/drivers/net/ethernet/qlogic/qed/qed_roce.c @@ -2191,8 +2191,7 @@ static int qed_roce_modify_qp(struct qed_hwfn *p_hwfn, params->modify_flags); return rc; - } else if (qp->cur_state == QED_ROCE_QP_STATE_ERR || - qp->cur_state == QED_ROCE_QP_STATE_SQE) { + } else if (qp->cur_state == QED_ROCE_QP_STATE_ERR) { /* ->ERR */ rc = qed_roce_sp_modify_responder(p_hwfn, qp, true, params->modify_flags); -- cgit From 105361943d3036f00f70a6621983b98673839591 Mon Sep 17 00:00:00 2001 From: Ram Amrani Date: Sun, 30 Apr 2017 11:49:07 +0300 Subject: qed: add error handling flow to TID deregistratin posting failure If the posting of the ramrod for the purpose of TID deregistration fails, abort the deregistration operation without using the FW's return code. Signed-off-by: Ram Amrani Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_roce.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c index 01244d7beeb9..0c449ddc04de 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_roce.c +++ b/drivers/net/ethernet/qlogic/qed/qed_roce.c @@ -2457,6 +2457,8 @@ qed_rdma_register_tid(void *rdma_cxt, } rc = qed_spq_post(p_hwfn, p_ent, &fw_return_code); + if (rc) + return rc; if (fw_return_code != RDMA_RETURN_OK) { DP_NOTICE(p_hwfn, "fw_return_code = %d\n", fw_return_code); -- cgit From e015d58b44a93a3fd89ed910d68659dfdc57237c Mon Sep 17 00:00:00 2001 From: Ram Amrani Date: Sun, 30 Apr 2017 11:49:08 +0300 Subject: qed: verify RoCE resource bitmaps are released Add mechanism to verify RoCE resources are released prior to freeing the bitmaps. If this is not the case, print what resources were not released. Signed-off-by: Ram Amrani Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_roce.c | 105 +++++++++++++++++++++-------- drivers/net/ethernet/qlogic/qed/qed_roce.h | 2 + 2 files changed, 80 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c index 0c449ddc04de..53f285e40e3d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_roce.c +++ b/drivers/net/ethernet/qlogic/qed/qed_roce.c @@ -90,7 +90,7 @@ void qed_roce_async_event(struct qed_hwfn *p_hwfn, } static int qed_rdma_bmap_alloc(struct qed_hwfn *p_hwfn, - struct qed_bmap *bmap, u32 max_count) + struct qed_bmap *bmap, u32 max_count, char *name) { DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "max_count = %08x\n", max_count); @@ -104,26 +104,24 @@ static int qed_rdma_bmap_alloc(struct qed_hwfn *p_hwfn, return -ENOMEM; } - DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocated bitmap %p\n", - bmap->bitmap); + snprintf(bmap->name, QED_RDMA_MAX_BMAP_NAME, "%s", name); + + DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "0\n"); return 0; } static int qed_rdma_bmap_alloc_id(struct qed_hwfn *p_hwfn, struct qed_bmap *bmap, u32 *id_num) { - DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "bmap = %p\n", bmap); - *id_num = find_first_zero_bit(bmap->bitmap, bmap->max_count); - - if (*id_num >= bmap->max_count) { - DP_NOTICE(p_hwfn, "no id available max_count=%d\n", - bmap->max_count); + if (*id_num >= bmap->max_count) return -EINVAL; - } __set_bit(*id_num, bmap->bitmap); + DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "%s bitmap: allocated id %d\n", + bmap->name, *id_num); + return 0; } @@ -141,15 +139,18 @@ static void qed_bmap_release_id(struct qed_hwfn *p_hwfn, { bool b_acquired; - DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "id_num = %08x", id_num); if (id_num >= bmap->max_count) return; b_acquired = test_and_clear_bit(id_num, bmap->bitmap); if (!b_acquired) { - DP_NOTICE(p_hwfn, "ID %d already released\n", id_num); + DP_NOTICE(p_hwfn, "%s bitmap: id %d already released\n", + bmap->name, id_num); return; } + + DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "%s bitmap: released id %d\n", + bmap->name, id_num); } static int qed_bmap_test_id(struct qed_hwfn *p_hwfn, @@ -224,7 +225,8 @@ static int qed_rdma_alloc(struct qed_hwfn *p_hwfn, } /* Allocate bit map for pd's */ - rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->pd_map, RDMA_MAX_PDS); + rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->pd_map, RDMA_MAX_PDS, + "PD"); if (rc) { DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Failed to allocate pd_map, rc = %d\n", @@ -234,7 +236,7 @@ static int qed_rdma_alloc(struct qed_hwfn *p_hwfn, /* Allocate DPI bitmap */ rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->dpi_map, - p_hwfn->dpi_count); + p_hwfn->dpi_count, "DPI"); if (rc) { DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Failed to allocate DPI bitmap, rc = %d\n", rc); @@ -245,7 +247,7 @@ static int qed_rdma_alloc(struct qed_hwfn *p_hwfn, * twice the number of QPs. */ rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->cq_map, - p_rdma_info->num_qps * 2); + p_rdma_info->num_qps * 2, "CQ"); if (rc) { DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Failed to allocate cq bitmap, rc = %d\n", rc); @@ -257,7 +259,7 @@ static int qed_rdma_alloc(struct qed_hwfn *p_hwfn, * The maximum number of CQs is bounded to twice the number of QPs. */ rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->toggle_bits, - p_rdma_info->num_qps * 2); + p_rdma_info->num_qps * 2, "Toggle"); if (rc) { DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Failed to allocate toogle bits, rc = %d\n", rc); @@ -266,7 +268,7 @@ static int qed_rdma_alloc(struct qed_hwfn *p_hwfn, /* Allocate bitmap for itids */ rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->tid_map, - p_rdma_info->num_mrs); + p_rdma_info->num_mrs, "MR"); if (rc) { DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Failed to allocate itids bitmaps, rc = %d\n", rc); @@ -274,7 +276,8 @@ static int qed_rdma_alloc(struct qed_hwfn *p_hwfn, } /* Allocate bitmap for cids used for qps. */ - rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->cid_map, num_cons); + rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->cid_map, num_cons, + "CID"); if (rc) { DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Failed to allocate cid bitmap, rc = %d\n", rc); @@ -282,7 +285,8 @@ static int qed_rdma_alloc(struct qed_hwfn *p_hwfn, } /* Allocate bitmap for cids used for responders/requesters. */ - rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->real_cid_map, num_cons); + rc = qed_rdma_bmap_alloc(p_hwfn, &p_rdma_info->real_cid_map, num_cons, + "REAL_CID"); if (rc) { DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Failed to allocate real cid bitmap, rc = %d\n", rc); @@ -313,6 +317,54 @@ free_rdma_info: return rc; } +static void qed_rdma_bmap_free(struct qed_hwfn *p_hwfn, + struct qed_bmap *bmap, bool check) +{ + int weight = bitmap_weight(bmap->bitmap, bmap->max_count); + int last_line = bmap->max_count / (64 * 8); + int last_item = last_line * 8 + + DIV_ROUND_UP(bmap->max_count % (64 * 8), 64); + u64 *pmap = (u64 *)bmap->bitmap; + int line, item, offset; + u8 str_last_line[200] = { 0 }; + + if (!weight || !check) + goto end; + + DP_NOTICE(p_hwfn, + "%s bitmap not free - size=%d, weight=%d, 512 bits per line\n", + bmap->name, bmap->max_count, weight); + + /* print aligned non-zero lines, if any */ + for (item = 0, line = 0; line < last_line; line++, item += 8) + if (bitmap_weight((unsigned long *)&pmap[item], 64 * 8)) + DP_NOTICE(p_hwfn, + "line 0x%04x: 0x%016llx 0x%016llx 0x%016llx 0x%016llx 0x%016llx 0x%016llx 0x%016llx 0x%016llx\n", + line, + pmap[item], + pmap[item + 1], + pmap[item + 2], + pmap[item + 3], + pmap[item + 4], + pmap[item + 5], + pmap[item + 6], pmap[item + 7]); + + /* print last unaligned non-zero line, if any */ + if ((bmap->max_count % (64 * 8)) && + (bitmap_weight((unsigned long *)&pmap[item], + bmap->max_count - item * 64))) { + offset = sprintf(str_last_line, "line 0x%04x: ", line); + for (; item < last_item; item++) + offset += sprintf(str_last_line + offset, + "0x%016llx ", pmap[item]); + DP_NOTICE(p_hwfn, "%s\n", str_last_line); + } + +end: + kfree(bmap->bitmap); + bmap->bitmap = NULL; +} + static void qed_rdma_resc_free(struct qed_hwfn *p_hwfn) { struct qed_bmap *rcid_map = &p_hwfn->p_rdma_info->real_cid_map; @@ -332,12 +384,12 @@ static void qed_rdma_resc_free(struct qed_hwfn *p_hwfn) } } - kfree(p_rdma_info->cid_map.bitmap); - kfree(p_rdma_info->tid_map.bitmap); - kfree(p_rdma_info->toggle_bits.bitmap); - kfree(p_rdma_info->cq_map.bitmap); - kfree(p_rdma_info->dpi_map.bitmap); - kfree(p_rdma_info->pd_map.bitmap); + qed_rdma_bmap_free(p_hwfn, &p_hwfn->p_rdma_info->cid_map, 1); + qed_rdma_bmap_free(p_hwfn, &p_hwfn->p_rdma_info->pd_map, 1); + qed_rdma_bmap_free(p_hwfn, &p_hwfn->p_rdma_info->dpi_map, 1); + qed_rdma_bmap_free(p_hwfn, &p_hwfn->p_rdma_info->cq_map, 1); + qed_rdma_bmap_free(p_hwfn, &p_hwfn->p_rdma_info->toggle_bits, 0); + qed_rdma_bmap_free(p_hwfn, &p_hwfn->p_rdma_info->tid_map, 1); kfree(p_rdma_info->port); kfree(p_rdma_info->dev); @@ -954,8 +1006,7 @@ static int qed_rdma_create_cq(void *rdma_cxt, /* Allocate icid */ spin_lock_bh(&p_info->lock); - rc = qed_rdma_bmap_alloc_id(p_hwfn, - &p_info->cq_map, &returned_id); + rc = qed_rdma_bmap_alloc_id(p_hwfn, &p_info->cq_map, &returned_id); spin_unlock_bh(&p_info->lock); if (rc) { diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.h b/drivers/net/ethernet/qlogic/qed/qed_roce.h index 3ccc08a7c995..9742af516183 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_roce.h +++ b/drivers/net/ethernet/qlogic/qed/qed_roce.h @@ -67,9 +67,11 @@ enum qed_rdma_toggle_bit { QED_RDMA_TOGGLE_BIT_SET = 1 }; +#define QED_RDMA_MAX_BMAP_NAME (10) struct qed_bmap { unsigned long *bitmap; u32 max_count; + char name[QED_RDMA_MAX_BMAP_NAME]; }; struct qed_rdma_info { -- cgit From 107392b75ffc96a2418d5382e52b08c598575e1b Mon Sep 17 00:00:00 2001 From: Ram Amrani Date: Sun, 30 Apr 2017 11:49:09 +0300 Subject: qed: align DPI configuration to HW requirements When calculating doorbell BAR partitioning round up the number of CPUs to the nearest power of 2 so the size of the DPI (per user section) configured in the hardware will be stored properly and not truncated. Signed-off-by: Ram Amrani Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 1 + drivers/net/ethernet/qlogic/qed/qed_dev.c | 12 +++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index c07191cb7631..edf3b68bf935 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -72,6 +72,7 @@ extern const struct qed_common_ops qed_common_ops_pass; #define QED_WFQ_UNIT 100 #define QED_WID_SIZE (1024) +#define QED_MIN_WIDS (4) #define QED_PF_DEMS_SIZE (4) /* cau states */ diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index aa1a4d5c864c..c478e079b039 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1318,17 +1318,15 @@ static int qed_hw_init_dpi_size(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u32 pwm_region_size, u32 n_cpus) { - u32 dpi_page_size_1, dpi_page_size_2, dpi_page_size; - u32 dpi_bit_shift, dpi_count; + u32 dpi_bit_shift, dpi_count, dpi_page_size; u32 min_dpis; + u32 n_wids; /* Calculate DPI size */ - dpi_page_size_1 = QED_WID_SIZE * n_cpus; - dpi_page_size_2 = max_t(u32, QED_WID_SIZE, PAGE_SIZE); - dpi_page_size = max_t(u32, dpi_page_size_1, dpi_page_size_2); - dpi_page_size = roundup_pow_of_two(dpi_page_size); + n_wids = max_t(u32, QED_MIN_WIDS, n_cpus); + dpi_page_size = QED_WID_SIZE * roundup_pow_of_two(n_wids); + dpi_page_size = (dpi_page_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); dpi_bit_shift = ilog2(dpi_page_size / 4096); - dpi_count = pwm_region_size / dpi_page_size; min_dpis = p_hwfn->pf_params.rdma_pf_params.min_dpis; -- cgit From 20b1bd96e9f4feeffc9206284df3c6a4438e9ca8 Mon Sep 17 00:00:00 2001 From: Ram Amrani Date: Sun, 30 Apr 2017 11:49:10 +0300 Subject: qed: output the DPM status and WID count Output to the RDMA driver whether DPM mode is enabled or disabled in the HW and if so what is the number of WIDs it supports Signed-off-by: Ram Amrani Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed.h | 1 + drivers/net/ethernet/qlogic/qed/qed_dev.c | 4 +++- drivers/net/ethernet/qlogic/qed/qed_roce.c | 4 ++++ include/linux/qed/qed_roce_if.h | 2 ++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index edf3b68bf935..2ab1aab7c3fe 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -526,6 +526,7 @@ struct qed_hwfn { struct dbg_tools_data dbg_info; /* PWM region specific data */ + u16 wid_count; u32 dpi_size; u32 dpi_count; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index c478e079b039..5f31140d0b77 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1354,7 +1354,7 @@ qed_hw_init_pf_doorbell_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { u32 pwm_regsize, norm_regsize; u32 non_pwm_conn, min_addr_reg1; - u32 db_bar_size, n_cpus; + u32 db_bar_size, n_cpus = 1; u32 roce_edpm_mode; u32 pf_dems_shift; int rc = 0; @@ -1415,6 +1415,8 @@ qed_hw_init_pf_doorbell_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) qed_rdma_dpm_bar(p_hwfn, p_ptt); } + p_hwfn->wid_count = (u16) n_cpus; + DP_INFO(p_hwfn, "doorbell bar: normal_region_size=%d, pwm_region_size=%d, dpi_size=%d, dpi_count=%d, roce_edpm=%s\n", norm_regsize, diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c index 53f285e40e3d..56289d7cd306 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_roce.c +++ b/drivers/net/ethernet/qlogic/qed/qed_roce.c @@ -784,6 +784,7 @@ static int qed_rdma_add_user(void *rdma_cxt, ((out_params->dpi) * p_hwfn->dpi_size); out_params->dpi_size = p_hwfn->dpi_size; + out_params->wid_count = p_hwfn->wid_count; DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Adding user - done, rc = %d\n", rc); return rc; @@ -856,9 +857,12 @@ static void qed_rdma_cnq_prod_update(void *rdma_cxt, u8 qz_offset, u16 prod) static int qed_fill_rdma_dev_info(struct qed_dev *cdev, struct qed_dev_rdma_info *info) { + struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); + memset(info, 0, sizeof(*info)); info->rdma_type = QED_RDMA_TYPE_ROCE; + info->user_dpm_enabled = (p_hwfn->db_bar_no_edpm == 0); qed_fill_dev_info(cdev, &info->common); diff --git a/include/linux/qed/qed_roce_if.h b/include/linux/qed/qed_roce_if.h index f742d4312c9d..cbb2ff0ce4bc 100644 --- a/include/linux/qed/qed_roce_if.h +++ b/include/linux/qed/qed_roce_if.h @@ -240,6 +240,7 @@ struct qed_rdma_add_user_out_params { u64 dpi_addr; u64 dpi_phys_addr; u32 dpi_size; + u16 wid_count; }; enum roce_mode { @@ -533,6 +534,7 @@ enum qed_rdma_type { struct qed_dev_rdma_info { struct qed_dev_info common; enum qed_rdma_type rdma_type; + u8 user_dpm_enabled; }; struct qed_rdma_ops { -- cgit From 07ff2ed03bb874a5bb97361a5a07ee28f1afa574 Mon Sep 17 00:00:00 2001 From: "Mintz, Yuval" Date: Sun, 30 Apr 2017 12:14:44 +0300 Subject: qed: Prevent warning without CONFIG_RFS_ACCEL After removing the PTP related initialization from slowpath start, the remaining PTT entry is required only in case CONFIG_RFS_ACCEL is set. Otherwise, it leads to a warning due to it being unused. Fixes: d179bd1699fc ("qed: Acquire/release ptt_ptp lock when enabling/disabling PTP") Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 8a5a0649fc4a..59992cf20d42 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -928,7 +928,9 @@ static int qed_slowpath_start(struct qed_dev *cdev, struct qed_tunnel_info tunn_info; const u8 *data = NULL; struct qed_hwfn *hwfn; +#ifdef CONFIG_RFS_ACCEL struct qed_ptt *p_ptt; +#endif int rc = -EINVAL; if (qed_iov_wq_start(cdev)) -- cgit From b1e455260c9187b16dd4ebc428b817ebac322043 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sun, 30 Apr 2017 19:47:14 +0300 Subject: mlxsw: spectrum_router: Simplify VRF enslavement When a netdev is enslaved to a VRF master, its router interface (RIF) needs to be destroyed (if exists) and a new one created using the corresponding virtual router (VR). >From the driver's perspective, the above is equivalent to an inetaddr event sent for this netdev. Therefore, when a port netdev (or its uppers) are enslaved to a VRF master, call the same function that would've been called had a NETDEV_UP was sent for this netdev in the inetaddr notification chain. This patch also fixes a bug when a LAG netdev with an existing RIF is enslaved to a VRF. Before this patch, each LAG port would drop the reference on the RIF, but would re-join the same one (in the wrong VR) soon after. With this patch, the corresponding RIF is first destroyed and a new one is created using the correct VR. Fixes: 7179eb5acd59 ("mlxsw: spectrum_router: Add support for VRFs") Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 77 +++------------ drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 10 +- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 107 +++++++++------------ 3 files changed, 63 insertions(+), 131 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 20c1b6c2dba0..88357cee7679 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -4106,7 +4106,6 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, if (!is_vlan_dev(upper_dev) && !netif_is_lag_master(upper_dev) && !netif_is_bridge_master(upper_dev) && - !netif_is_l3_master(upper_dev) && !netif_is_ovs_master(upper_dev)) return -EINVAL; if (!info->linking) @@ -4151,11 +4150,6 @@ static int mlxsw_sp_netdevice_port_upper_event(struct net_device *dev, else mlxsw_sp_port_lag_leave(mlxsw_sp_port, upper_dev); - } else if (netif_is_l3_master(upper_dev)) { - if (info->linking) - err = mlxsw_sp_port_vrf_join(mlxsw_sp_port); - else - mlxsw_sp_port_vrf_leave(mlxsw_sp_port); } else if (netif_is_ovs_master(upper_dev)) { if (info->linking) err = mlxsw_sp_port_ovs_join(mlxsw_sp_port); @@ -4275,7 +4269,7 @@ static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev, switch (event) { case NETDEV_PRECHANGEUPPER: upper_dev = info->upper_dev; - if (!is_vlan_dev(upper_dev) && !netif_is_l3_master(upper_dev)) + if (!is_vlan_dev(upper_dev)) return -EINVAL; if (is_vlan_dev(upper_dev) && br_dev != mlxsw_sp->master_bridge.dev) @@ -4290,12 +4284,6 @@ static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev, else mlxsw_sp_master_bridge_vlan_unlink(mlxsw_sp, upper_dev); - } else if (netif_is_l3_master(upper_dev)) { - if (info->linking) - err = mlxsw_sp_bridge_vrf_join(mlxsw_sp, - br_dev); - else - mlxsw_sp_bridge_vrf_leave(mlxsw_sp, br_dev); } else { err = -EINVAL; WARN_ON(1); @@ -4529,8 +4517,7 @@ static int mlxsw_sp_netdevice_vport_event(struct net_device *dev, switch (event) { case NETDEV_PRECHANGEUPPER: upper_dev = info->upper_dev; - if (!netif_is_bridge_master(upper_dev) && - !netif_is_l3_master(upper_dev)) + if (!netif_is_bridge_master(upper_dev)) return -EINVAL; if (!info->linking) break; @@ -4550,11 +4537,6 @@ static int mlxsw_sp_netdevice_vport_event(struct net_device *dev, upper_dev); else mlxsw_sp_vport_bridge_leave(mlxsw_sp_vport); - } else if (netif_is_l3_master(upper_dev)) { - if (info->linking) - err = mlxsw_sp_vport_vrf_join(mlxsw_sp_vport); - else - mlxsw_sp_vport_vrf_leave(mlxsw_sp_vport); } else { err = -EINVAL; WARN_ON(1); @@ -4585,47 +4567,6 @@ static int mlxsw_sp_netdevice_lag_vport_event(struct net_device *lag_dev, return 0; } -static int mlxsw_sp_netdevice_bridge_vlan_event(struct net_device *vlan_dev, - unsigned long event, void *ptr) -{ - struct netdev_notifier_changeupper_info *info; - struct mlxsw_sp *mlxsw_sp; - int err = 0; - - mlxsw_sp = mlxsw_sp_lower_get(vlan_dev); - if (!mlxsw_sp) - return 0; - - info = ptr; - - switch (event) { - case NETDEV_PRECHANGEUPPER: - /* VLAN devices are only allowed on top of the - * VLAN-aware bridge. - */ - if (WARN_ON(vlan_dev_real_dev(vlan_dev) != - mlxsw_sp->master_bridge.dev)) - return -EINVAL; - if (!netif_is_l3_master(info->upper_dev)) - return -EINVAL; - break; - case NETDEV_CHANGEUPPER: - if (netif_is_l3_master(info->upper_dev)) { - if (info->linking) - err = mlxsw_sp_bridge_vrf_join(mlxsw_sp, - vlan_dev); - else - mlxsw_sp_bridge_vrf_leave(mlxsw_sp, vlan_dev); - } else { - err = -EINVAL; - WARN_ON(1); - } - break; - } - - return err; -} - static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev, unsigned long event, void *ptr) { @@ -4638,13 +4579,19 @@ static int mlxsw_sp_netdevice_vlan_event(struct net_device *vlan_dev, else if (netif_is_lag_master(real_dev)) return mlxsw_sp_netdevice_lag_vport_event(real_dev, event, ptr, vid); - else if (netif_is_bridge_master(real_dev)) - return mlxsw_sp_netdevice_bridge_vlan_event(vlan_dev, event, - ptr); return 0; } +static bool mlxsw_sp_is_vrf_event(unsigned long event, void *ptr) +{ + struct netdev_notifier_changeupper_info *info = ptr; + + if (event != NETDEV_PRECHANGEUPPER && event != NETDEV_CHANGEUPPER) + return false; + return netif_is_l3_master(info->upper_dev); +} + static int mlxsw_sp_netdevice_event(struct notifier_block *unused, unsigned long event, void *ptr) { @@ -4653,6 +4600,8 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused, if (event == NETDEV_CHANGEADDR || event == NETDEV_CHANGEMTU) err = mlxsw_sp_netdevice_router_port_event(dev); + else if (mlxsw_sp_is_vrf_event(event, ptr)) + err = mlxsw_sp_netdevice_vrf_event(dev, event, ptr); else if (mlxsw_sp_port_dev_check(dev)) err = mlxsw_sp_netdevice_port_event(dev, event, ptr); else if (netif_is_lag_master(dev)) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 0af6e1abe0a7..0c23bc1e946d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -576,14 +576,8 @@ int mlxsw_sp_inetaddr_event(struct notifier_block *unused, unsigned long event, void *ptr); void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_rif *rif); -int mlxsw_sp_vport_vrf_join(struct mlxsw_sp_port *mlxsw_sp_vport); -void mlxsw_sp_vport_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_vport); -int mlxsw_sp_port_vrf_join(struct mlxsw_sp_port *mlxsw_sp_port); -void mlxsw_sp_port_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_port); -int mlxsw_sp_bridge_vrf_join(struct mlxsw_sp *mlxsw_sp, - struct net_device *l3_dev); -void mlxsw_sp_bridge_vrf_leave(struct mlxsw_sp *mlxsw_sp, - struct net_device *l3_dev); +int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event, + struct netdev_notifier_changeupper_info *info); int mlxsw_sp_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, unsigned int entry_count, u32 *p_entry_index); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 146f8c7d1120..33cec1cc1642 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -3345,6 +3345,21 @@ static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev, return 0; } +static int __mlxsw_sp_inetaddr_event(struct net_device *dev, + unsigned long event) +{ + if (mlxsw_sp_port_dev_check(dev)) + return mlxsw_sp_inetaddr_port_event(dev, event); + else if (netif_is_lag_master(dev)) + return mlxsw_sp_inetaddr_lag_event(dev, event); + else if (netif_is_bridge_master(dev)) + return mlxsw_sp_inetaddr_bridge_event(dev, dev, event); + else if (is_vlan_dev(dev)) + return mlxsw_sp_inetaddr_vlan_event(dev, event); + else + return 0; +} + int mlxsw_sp_inetaddr_event(struct notifier_block *unused, unsigned long event, void *ptr) { @@ -3362,15 +3377,7 @@ int mlxsw_sp_inetaddr_event(struct notifier_block *unused, if (!mlxsw_sp_rif_should_config(rif, ifa->ifa_dev, event)) goto out; - if (mlxsw_sp_port_dev_check(dev)) - err = mlxsw_sp_inetaddr_port_event(dev, event); - else if (netif_is_lag_master(dev)) - err = mlxsw_sp_inetaddr_lag_event(dev, event); - else if (netif_is_bridge_master(dev)) - err = mlxsw_sp_inetaddr_bridge_event(dev, dev, event); - else if (is_vlan_dev(dev)) - err = mlxsw_sp_inetaddr_vlan_event(dev, event); - + err = __mlxsw_sp_inetaddr_event(dev, event); out: return notifier_from_errno(err); } @@ -3433,71 +3440,53 @@ err_rif_edit: return err; } -int mlxsw_sp_vport_vrf_join(struct mlxsw_sp_port *mlxsw_sp_vport) +static int mlxsw_sp_port_vrf_join(struct mlxsw_sp *mlxsw_sp, + struct net_device *l3_dev) { - struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_vport); - struct net_device *dev = mlxsw_sp_vport->dev; + struct mlxsw_sp_rif *rif; - /* In case vPort already has a RIF, then we need to drop it. - * A new one will be created using the VRF's VR. + /* If netdev is already associated with a RIF, then we need to + * destroy it and create a new one with the new virtual router ID. */ - if (f && f->rif) - mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport); - - return mlxsw_sp_vport_rif_sp_join(mlxsw_sp_vport, dev); -} - -void mlxsw_sp_vport_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_vport) -{ - mlxsw_sp_vport_rif_sp_leave(mlxsw_sp_vport); -} - -int mlxsw_sp_port_vrf_join(struct mlxsw_sp_port *mlxsw_sp_port) -{ - struct mlxsw_sp_port *mlxsw_sp_vport; - - mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, 1); - if (WARN_ON(!mlxsw_sp_vport)) - return -EINVAL; + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev); + if (rif) + __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN); - return mlxsw_sp_vport_vrf_join(mlxsw_sp_vport); + return __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_UP); } -void mlxsw_sp_port_vrf_leave(struct mlxsw_sp_port *mlxsw_sp_port) +static void mlxsw_sp_port_vrf_leave(struct mlxsw_sp *mlxsw_sp, + struct net_device *l3_dev) { - struct mlxsw_sp_port *mlxsw_sp_vport; + struct mlxsw_sp_rif *rif; - mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, 1); - if (WARN_ON(!mlxsw_sp_vport)) + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, l3_dev); + if (!rif) return; - - mlxsw_sp_vport_vrf_leave(mlxsw_sp_vport); + __mlxsw_sp_inetaddr_event(l3_dev, NETDEV_DOWN); } -int mlxsw_sp_bridge_vrf_join(struct mlxsw_sp *mlxsw_sp, - struct net_device *l3_dev) +int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event, + struct netdev_notifier_changeupper_info *info) { - struct mlxsw_sp_fid *f; - - f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev); - if (WARN_ON(!f)) - return -EINVAL; - - if (f->rif) - mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->rif); + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev); + int err = 0; - return mlxsw_sp_rif_bridge_create(mlxsw_sp, l3_dev, f); -} + if (!mlxsw_sp) + return 0; -void mlxsw_sp_bridge_vrf_leave(struct mlxsw_sp *mlxsw_sp, - struct net_device *l3_dev) -{ - struct mlxsw_sp_fid *f; + switch (event) { + case NETDEV_PRECHANGEUPPER: + return 0; + case NETDEV_CHANGEUPPER: + if (info->linking) + err = mlxsw_sp_port_vrf_join(mlxsw_sp, l3_dev); + else + mlxsw_sp_port_vrf_leave(mlxsw_sp, l3_dev); + break; + } - f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev); - if (WARN_ON(!f)) - return; - mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->rif); + return err; } static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb) -- cgit From d5066c467ee3b8eb8716e776584b3953a0bb218a Mon Sep 17 00:00:00 2001 From: Liam Beguin Date: Mon, 1 May 2017 11:02:01 -0400 Subject: switchdev: documentation: fix whitespace issues Figure 1 is full of whitespaces; fix it Signed-off-by: Liam Beguin Signed-off-by: Sylvain Lemieux Acked-by: Ivan Vecera Signed-off-by: David S. Miller --- Documentation/networking/switchdev.txt | 70 +++++++++++++++++----------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/Documentation/networking/switchdev.txt b/Documentation/networking/switchdev.txt index 2bbac05ab9e2..3e7b946dea27 100644 --- a/Documentation/networking/switchdev.txt +++ b/Documentation/networking/switchdev.txt @@ -13,43 +13,43 @@ an example setup using a data-center-class switch ASIC chip. Other setups with SR-IOV or soft switches, such as OVS, are possible. -                             User-space tools                                  -                                                                               -       user space                   |                                          -      +-------------------------------------------------------------------+    -       kernel                       | Netlink                                  -                                    |                                          -                     +--------------+-------------------------------+          -                     |         Network stack                        |          -                     |           (Linux)                            |          -                     |                                              |          -                     +----------------------------------------------+          -                                                                               +                             User-space tools + +       user space                   | +      +-------------------------------------------------------------------+ +       kernel                       | Netlink +                                    | +                     +--------------+-------------------------------+ +                     |         Network stack                        | +                     |           (Linux)                            | +                     |                                              | +                     +----------------------------------------------+ + sw1p2 sw1p4 sw1p6 -                      sw1p1  + sw1p3 +  sw1p5 +         eth1              -                        +    |    +    |    +    |            +                -                        |    |    |    |    |    |            |                -                     +--+----+----+----+-+--+----+---+  +-----+-----+          -                     |         Switch driver         |  |    mgmt   |          -                     |        (this document)        |  |   driver  |          -                     |                               |  |           |          -                     +--------------+----------------+  +-----------+          -                                    |                                          -       kernel                       | HW bus (eg PCI)                          -      +-------------------------------------------------------------------+    -       hardware                     |                                          -                     +--------------+---+------------+                         -                     |         Switch device (sw1)   |                         -                     |  +----+                       +--------+                -                     |  |    v offloaded data path   | mgmt port               -                     |  |    |                       |                         -                     +--|----|----+----+----+----+---+                         -                        |    |    |    |    |    |                             -                        +    +    +    +    +    +                             +                      sw1p1  + sw1p3 +  sw1p5 +         eth1 +                        +    |    +    |    +    |            + +                        |    |    |    |    |    |            | +                     +--+----+----+----+-+--+----+---+  +-----+-----+ +                     |         Switch driver         |  |    mgmt   | +                     |        (this document)        |  |   driver  | +                     |                               |  |           | +                     +--------------+----------------+  +-----------+ +                                    | +       kernel                       | HW bus (eg PCI) +      +-------------------------------------------------------------------+ +       hardware                     | +                     +--------------+---+------------+ +                     |         Switch device (sw1)   | +                     |  +----+                       +--------+ +                     |  |    v offloaded data path   | mgmt port +                     |  |    |                       | +                     +--|----|----+----+----+----+---+ +                        |    |    |    |    |    | +                        +    +    +    +    +    +                        p1   p2   p3   p4   p5   p6 -                                        -                             front-panel ports                                 -                                                                               + +                             front-panel ports + Fig 1. -- cgit From 89a23c8b528bd2c89f3981573d6cd7d23840c8a6 Mon Sep 17 00:00:00 2001 From: Craig Gallek Date: Wed, 26 Apr 2017 14:37:45 -0400 Subject: ip6_tunnel: Fix missing tunnel encapsulation limit option The IPv6 tunneling code tries to insert IPV6_TLV_TNL_ENCAP_LIMIT and IPV6_TLV_PADN options when an encapsulation limit is defined (the default is a limit of 4). An MTU adjustment is done to account for these options as well. However, the options are never present in the generated packets. The issue appears to be a subtlety between IPV6_DSTOPTS and IPV6_RTHDRDSTOPTS defined in RFC 3542. When the IPIP tunnel driver was written, the encap limit options were included as IPV6_RTHDRDSTOPTS in dst0opt of struct ipv6_txoptions. Later, ipv6_push_nfrags_opts was (correctly) updated to require IPV6_RTHDR options when IPV6_RTHDRDSTOPTS are to be used. This caused the options to no longer be included in v6 encapsulated packets. The fix is to use IPV6_DSTOPTS (in dst1opt of struct ipv6_txoptions) instead. IPV6_DSTOPTS do not have the additional IPV6_RTHDR requirement. Fixes: 1df64a8569c7: ("[IPV6]: Add ip6ip6 tunnel driver.") Fixes: 333fad5364d6: ("[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542)") Signed-off-by: Craig Gallek Signed-off-by: David S. Miller --- net/ipv6/ip6_tunnel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 8a1bd52423ca..6eb2ae507500 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -954,7 +954,7 @@ static void init_tel_txopt(struct ipv6_tel_txoption *opt, __u8 encap_limit) opt->dst_opt[5] = IPV6_TLV_PADN; opt->dst_opt[6] = 1; - opt->ops.dst0opt = (struct ipv6_opt_hdr *) opt->dst_opt; + opt->ops.dst1opt = (struct ipv6_opt_hdr *) opt->dst_opt; opt->ops.opt_nflen = 8; } @@ -1178,7 +1178,7 @@ route_lookup: if (encap_limit >= 0) { init_tel_txopt(&opt, encap_limit); - ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL, NULL); + ipv6_push_frag_opts(skb, &opt.ops, &proto); } /* Calculate max headroom for all the headers and adjust -- cgit From 67d349ed603d5ce4a6f1722b1736e2bcef0e8690 Mon Sep 17 00:00:00 2001 From: Ilan Tayari Date: Sun, 30 Apr 2017 16:34:38 +0300 Subject: net/esp4: Fix invalid esph pointer crash Both esp_output and esp_xmit take a pointer to the ESP header and place it in esp_info struct prior to calling esp_output_head. Inside esp_output_head, the call to esp_output_udp_encap makes sure to update the pointer if it gets invalid. However, if esp_output_head itself calls skb_cow_data, the pointer is not updated and stays invalid, causing a crash after esp_output_head returns. Update the pointer if it becomes invalid in esp_output_head Fixes: fca11ebde3f0 ("esp4: Reorganize esp_output") Signed-off-by: Ilan Tayari Signed-off-by: David S. Miller --- net/ipv4/esp4.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 7f2caf71212b..65cc02bd82bc 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -317,6 +317,7 @@ cow: if (nfrags < 0) goto out; tail = skb_tail_pointer(trailer); + esp->esph = ip_esp_hdr(skb); skip_cow: esp_output_fill_trailer(tail, esp->tfclen, esp->plen, esp->proto); -- cgit From 152afb9b45a8af4a93699a15925c392a28182a26 Mon Sep 17 00:00:00 2001 From: Ilan Tayari Date: Sun, 30 Apr 2017 16:51:19 +0300 Subject: xfrm: Indicate xfrm_state offload errors Current code silently ignores driver errors when configuring IPSec offload xfrm_state, and falls back to host-based crypto. Fail the xfrm_state creation if the driver has an error, because the NIC offloading was explicitly requested by the user program. This will communicate back to the user that there was an error. Fixes: d77e38e612a0 ("xfrm: Add an IPsec hardware offloading API") Signed-off-by: Ilan Tayari Signed-off-by: David S. Miller --- net/xfrm/xfrm_user.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index ba74e5eeeeef..c4cceddac9db 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -595,9 +595,12 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, goto error; } - if (attrs[XFRMA_OFFLOAD_DEV] && - xfrm_dev_state_add(net, x, nla_data(attrs[XFRMA_OFFLOAD_DEV]))) - goto error; + if (attrs[XFRMA_OFFLOAD_DEV]) { + err = xfrm_dev_state_add(net, x, + nla_data(attrs[XFRMA_OFFLOAD_DEV])); + if (err) + goto error; + } if ((err = xfrm_alloc_replay_state_esn(&x->replay_esn, &x->preplay_esn, attrs[XFRMA_REPLAY_ESN_VAL]))) -- cgit From 3cf3c8469f70d18f8bbcdf8361e62812ebc571cd Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:10 -0400 Subject: net: dsa: mv88e6xxx: add max VID to info Some chips don't have a VLAN Table Unit, most of them do have a 4K table, some others as the 88E6390 family has a 13th bit for the VID. Add a new max_vid member to the info structure, used to check the presence of a VTU as well as the value used to iterate from in VTU GetNext operations. This makes the MV88E6XXX_FLAG_VTU obsolete, thus remove it. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 38 ++++++++++++++++++++++++++--------- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 13 ++---------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 44ba8cff5631..e45ddf3e90e8 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1440,7 +1440,7 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, u16 pvid; int err; - if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) + if (!chip->info->max_vid) return -EOPNOTSUPP; mutex_lock(&chip->reg_lock); @@ -1478,7 +1478,7 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, err = cb(&vlan->obj); if (err) break; - } while (next.vid < GLOBAL_VTU_VID_MASK); + } while (next.vid < chip->info->max_vid); unlock: mutex_unlock(&chip->reg_lock); @@ -1640,7 +1640,7 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) break; set_bit(vlan.fid, fid_bitmap); - } while (vlan.vid < GLOBAL_VTU_VID_MASK); + } while (vlan.vid < chip->info->max_vid); /* The reset value 0x000 is used to indicate that multiple address * databases are not needed. Return the next positive available. @@ -1799,7 +1799,7 @@ static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, PORT_CONTROL_2_8021Q_DISABLED; int err; - if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) + if (!chip->info->max_vid) return -EOPNOTSUPP; mutex_lock(&chip->reg_lock); @@ -1817,7 +1817,7 @@ mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, struct mv88e6xxx_chip *chip = ds->priv; int err; - if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) + if (!chip->info->max_vid) return -EOPNOTSUPP; /* If the requested port doesn't belong to the same bridge as the VLAN @@ -1860,7 +1860,7 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; u16 vid; - if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) + if (!chip->info->max_vid) return; mutex_lock(&chip->reg_lock); @@ -1921,7 +1921,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 pvid, vid; int err = 0; - if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU)) + if (!chip->info->max_vid) return -EOPNOTSUPP; mutex_lock(&chip->reg_lock); @@ -2090,7 +2090,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, int (*cb)(struct switchdev_obj *obj)) { struct mv88e6xxx_vtu_entry vlan = { - .vid = GLOBAL_VTU_VID_MASK, /* all ones */ + .vid = chip->info->max_vid, }; u16 fid; int err; @@ -2121,7 +2121,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, obj, cb); if (err) return err; - } while (vlan.vid < GLOBAL_VTU_VID_MASK); + } while (vlan.vid < chip->info->max_vid); return err; } @@ -3685,6 +3685,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6085", .num_databases = 4096, .num_ports = 10, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -3702,6 +3703,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6095/88E6095F", .num_databases = 256, .num_ports = 11, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -3718,6 +3720,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6097/88E6097F", .num_databases = 4096, .num_ports = 11, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -3735,6 +3738,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6123", .num_databases = 4096, .num_ports = 3, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -3752,6 +3756,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6131", .num_databases = 256, .num_ports = 8, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -3768,6 +3773,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6341", .num_databases = 4096, .num_ports = 6, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 3750, @@ -3784,6 +3790,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6161", .num_databases = 4096, .num_ports = 6, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -3801,6 +3808,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6165", .num_databases = 4096, .num_ports = 6, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -3818,6 +3826,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6171", .num_databases = 4096, .num_ports = 7, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -3835,6 +3844,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6172", .num_databases = 4096, .num_ports = 7, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -3852,6 +3862,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6175", .num_databases = 4096, .num_ports = 7, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -3869,6 +3880,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6176", .num_databases = 4096, .num_ports = 7, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -3886,6 +3898,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6185", .num_databases = 256, .num_ports = 10, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -3953,6 +3966,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6240", .num_databases = 4096, .num_ports = 7, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -3987,6 +4001,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6320", .num_databases = 4096, .num_ports = 7, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -4004,6 +4019,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6321", .num_databases = 4096, .num_ports = 7, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -4020,6 +4036,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6341", .num_databases = 4096, .num_ports = 6, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 3750, @@ -4036,6 +4053,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6350", .num_databases = 4096, .num_ports = 7, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -4053,6 +4071,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6351", .num_databases = 4096, .num_ports = 7, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, @@ -4070,6 +4089,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6352", .num_databases = 4096, .num_ports = 7, + .max_vid = 4095, .port_base_addr = 0x10, .global1_addr = 0x1b, .age_time_coeff = 15000, diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index c8f54986996b..5695ca206620 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -567,7 +567,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAG_G2_POT BIT_ULL(MV88E6XXX_CAP_G2_POT) #define MV88E6XXX_FLAG_STU BIT_ULL(MV88E6XXX_CAP_STU) -#define MV88E6XXX_FLAG_VTU BIT_ULL(MV88E6XXX_CAP_VTU) /* Ingress Rate Limit unit */ #define MV88E6XXX_FLAGS_IRL \ @@ -587,7 +586,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAGS_FAMILY_6095 \ (MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ - MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6097 \ @@ -598,7 +596,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_STU | \ - MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP) @@ -610,7 +607,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_STU | \ - MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP) @@ -618,8 +614,7 @@ enum mv88e6xxx_cap { (MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ - MV88E6XXX_FLAGS_MULTI_CHIP | \ - MV88E6XXX_FLAG_VTU) + MV88E6XXX_FLAGS_MULTI_CHIP) #define MV88E6XXX_FLAGS_FAMILY_6320 \ (MV88E6XXX_FLAG_EEE | \ @@ -627,7 +622,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ - MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP) @@ -638,7 +632,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_STU | \ - MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP | \ MV88E6XXX_FLAGS_SERDES) @@ -651,7 +644,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_STU | \ - MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP) @@ -664,7 +656,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ MV88E6XXX_FLAG_STU | \ - MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP | \ MV88E6XXX_FLAGS_SERDES) @@ -674,7 +665,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_STU | \ - MV88E6XXX_FLAG_VTU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP) @@ -686,6 +676,7 @@ struct mv88e6xxx_info { const char *name; unsigned int num_databases; unsigned int num_ports; + unsigned int max_vid; unsigned int port_base_addr; unsigned int global1_addr; unsigned int age_time_coeff; -- cgit From bd00e053ae29d3215a9085b5c0add7298bbc417c Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:11 -0400 Subject: net: dsa: mv88e6xxx: split VTU entry data member VLAN aware Marvell chips can program 802.1Q VLAN membership as well as 802.1s per VLAN Spanning Tree state using the same 3 VTU Data registers. Some chips such as 88E6185 use different Data registers offsets for ports state and membership, and program them in a single operation. Other chips such as 88E6352 use the same register layout but program them in distinct operations (an indirect table is used for 802.1s.) Newer chips such as 88E6390 use the same offsets for both state and membership in distinct operations, thus require multiple data accesses. To correctly abstract this, split the "data" structure member of mv88e6xxx_vtu_entry in two "state" and "member" members, before adding VTU support for newer chips. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 21 +++++++++++---------- drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 3 ++- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index e45ddf3e90e8..f025d3c22dba 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1312,7 +1312,7 @@ static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip, unsigned int shift = (i % 4) * 4 + nibble_offset; u16 reg = regs[i / 4]; - entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK; + entry->state[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK; } return 0; @@ -1339,7 +1339,7 @@ static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_chip *chip, for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { unsigned int shift = (i % 4) * 4 + nibble_offset; - u8 data = entry->data[i]; + u8 data = entry->state[i]; regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift; } @@ -1461,7 +1461,7 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, if (!next.valid) break; - if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) + if (next.member[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) continue; /* reinit and dump this VLAN obj */ @@ -1469,7 +1469,7 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, vlan->vid_end = next.vid; vlan->flags = 0; - if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED) + if (next.member[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED) vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED; if (next.vid == pvid) @@ -1669,7 +1669,8 @@ static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid, /* exclude all ports except the CPU and DSA ports */ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) - vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i) + vlan.member[i] = dsa_is_cpu_port(ds, i) || + dsa_is_dsa_port(ds, i) ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER; @@ -1765,7 +1766,7 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, if (!ds->ports[port].netdev) continue; - if (vlan.data[i] == + if (vlan.member[i] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) continue; @@ -1844,7 +1845,7 @@ static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port, if (err) return err; - vlan.data[port] = untagged ? + vlan.member[port] = untagged ? GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED : GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED; @@ -1890,10 +1891,10 @@ static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip, return err; /* Tell switchdev if this VLAN is handled in software */ - if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) + if (vlan.member[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) return -EOPNOTSUPP; - vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER; + vlan.member[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER; /* keep the VLAN unless all ports are excluded */ vlan.valid = false; @@ -1901,7 +1902,7 @@ static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip, if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)) continue; - if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) { + if (vlan.member[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) { vlan.valid = true; break; } diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 5695ca206620..8638892a7e18 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -704,7 +704,8 @@ struct mv88e6xxx_vtu_entry { u16 fid; u8 sid; bool valid; - u8 data[DSA_MAX_PORTS]; + u8 member[DSA_MAX_PORTS]; + u8 state[DSA_MAX_PORTS]; }; struct mv88e6xxx_bus_ops; -- cgit From 332aa5ccc82005de9441c1b65ca546488c7f8730 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:12 -0400 Subject: net: dsa: mv88e6xxx: move VTU Operation accessors Move the helper functions to access the Global 1 VTU Operation register to a new global1_vtu.c file, and get rid of the old underscore prefix naming convention. This file will be extended will all VTU/STU related code. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/Makefile | 1 + drivers/net/dsa/mv88e6xxx/chip.c | 39 +++++++++------------------------ drivers/net/dsa/mv88e6xxx/global1.h | 3 +++ drivers/net/dsa/mv88e6xxx/global1_vtu.c | 33 ++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 29 deletions(-) create mode 100644 drivers/net/dsa/mv88e6xxx/global1_vtu.c diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile index 31d37a90cec7..6edd869c8d6f 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile @@ -2,5 +2,6 @@ obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o mv88e6xxx-objs := chip.o mv88e6xxx-objs += global1.o mv88e6xxx-objs += global1_atu.o +mv88e6xxx-objs += global1_vtu.o mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o mv88e6xxx-objs += port.o diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index f025d3c22dba..bf0350432337 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3,9 +3,6 @@ * * Copyright (c) 2008 Marvell Semiconductor * - * Copyright (c) 2015 CMC Electronics, Inc. - * Added support for VLAN Table Unit operations - * * Copyright (c) 2016 Andrew Lunn * * Copyright (c) 2016-2017 Savoir-faire Linux Inc. @@ -1266,31 +1263,15 @@ static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) netdev_err(ds->ports[port].netdev, "failed to flush ATU\n"); } -static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip) -{ - return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY); -} - -static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_chip *chip, u16 op) -{ - int err; - - err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_OP, op); - if (err) - return err; - - return _mv88e6xxx_vtu_wait(chip); -} - static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_chip *chip) { int ret; - ret = _mv88e6xxx_vtu_wait(chip); + ret = mv88e6xxx_g1_vtu_op_wait(chip); if (ret < 0) return ret; - return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_FLUSH_ALL); + return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_FLUSH_ALL); } static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip, @@ -1380,11 +1361,11 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, u16 val; int err; - err = _mv88e6xxx_vtu_wait(chip); + err = mv88e6xxx_g1_vtu_op_wait(chip); if (err) return err; - err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_VTU_GET_NEXT); + err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_GET_NEXT); if (err) return err; @@ -1493,7 +1474,7 @@ static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, u16 reg = 0; int err; - err = _mv88e6xxx_vtu_wait(chip); + err = mv88e6xxx_g1_vtu_op_wait(chip); if (err) return err; @@ -1532,7 +1513,7 @@ loadpurge: if (err) return err; - return _mv88e6xxx_vtu_cmd(chip, op); + return mv88e6xxx_g1_vtu_op(chip, op); } static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, @@ -1542,7 +1523,7 @@ static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, u16 val; int err; - err = _mv88e6xxx_vtu_wait(chip); + err = mv88e6xxx_g1_vtu_op_wait(chip); if (err) return err; @@ -1551,7 +1532,7 @@ static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, if (err) return err; - err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_GET_NEXT); + err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_GET_NEXT); if (err) return err; @@ -1583,7 +1564,7 @@ static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip, u16 reg = 0; int err; - err = _mv88e6xxx_vtu_wait(chip); + err = mv88e6xxx_g1_vtu_op_wait(chip); if (err) return err; @@ -1606,7 +1587,7 @@ loadpurge: if (err) return err; - return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); + return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); } static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index e30cbe480d5b..c153d07d2065 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -50,4 +50,7 @@ int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all); int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port, bool all); +int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip); +int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op); + #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c new file mode 100644 index 000000000000..20dfdcbda238 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -0,0 +1,33 @@ +/* + * Marvell 88E6xxx VLAN [Spanning Tree] Translation Unit (VTU [STU]) support + * + * Copyright (c) 2008 Marvell Semiconductor + * Copyright (c) 2015 CMC Electronics, Inc. + * Copyright (c) 2017 Savoir-faire Linux, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "mv88e6xxx.h" +#include "global1.h" + +/* Offset 0x05: VTU Operation Register */ + +int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY); +} + +int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op) +{ + int err; + + err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_OP, op); + if (err) + return err; + + return mv88e6xxx_g1_vtu_op_wait(chip); +} -- cgit From b486d7c95cc8336aa91813a156f9385439bde2fc Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:13 -0400 Subject: net: dsa: mv88e6xxx: move VTU flush Move the VTU flush operation to global1_vtu.c and call it from a mv88e6xxx_vtu_setup helper, similarly to the ATU and PVT setup. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 28 ++++++++++++---------------- drivers/net/dsa/mv88e6xxx/global1.h | 1 + drivers/net/dsa/mv88e6xxx/global1_vtu.c | 13 +++++++++++++ 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index bf0350432337..d244c2283138 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1263,17 +1263,6 @@ static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) netdev_err(ds->ports[port].netdev, "failed to flush ATU\n"); } -static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_chip *chip) -{ - int ret; - - ret = mv88e6xxx_g1_vtu_op_wait(chip); - if (ret < 0) - return ret; - - return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_FLUSH_ALL); -} - static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry, unsigned int nibble_offset) @@ -1412,6 +1401,14 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, return 0; } +static int mv88e6xxx_vtu_setup(struct mv88e6xxx_chip *chip) +{ + if (!chip->info->max_vid) + return 0; + + return mv88e6xxx_g1_vtu_flush(chip); +} + static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, struct switchdev_obj_port_vlan *vlan, int (*cb)(struct switchdev_obj *obj)) @@ -2599,11 +2596,6 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip) if (err) return err; - /* Clear all the VTU and STU entries */ - err = _mv88e6xxx_vtu_stu_flush(chip); - if (err < 0) - return err; - /* Configure the IP ToS mapping registers. */ err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_0, 0x0000); if (err) @@ -2684,6 +2676,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) goto unlock; } + err = mv88e6xxx_vtu_setup(chip); + if (err) + goto unlock; + err = mv88e6xxx_pvt_setup(chip); if (err) goto unlock; diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index c153d07d2065..ed40e5557bbc 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -52,5 +52,6 @@ int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port, int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op); +int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 20dfdcbda238..c41ca28da084 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -31,3 +31,16 @@ int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op) return mv88e6xxx_g1_vtu_op_wait(chip); } + +/* VLAN Translation Unit Operations */ + +int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip) +{ + int err; + + err = mv88e6xxx_g1_vtu_op_wait(chip); + if (err) + return err; + + return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_FLUSH_ALL); +} -- cgit From 8ee51f6b4f0819fd3d6a4143222be796779cf501 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:14 -0400 Subject: net: dsa: mv88e6xxx: move VTU FID accessors Add helpers to access the VTU FID register in the global1_vtu.c file. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 7 ++----- drivers/net/dsa/mv88e6xxx/global1.h | 4 ++++ drivers/net/dsa/mv88e6xxx/global1_vtu.c | 25 +++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index d244c2283138..0452543a2463 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1371,11 +1371,9 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, return err; if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) { - err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_FID, &val); + err = mv88e6xxx_g1_vtu_fid_read(chip, &next); if (err) return err; - - next.fid = val & GLOBAL_VTU_FID_MASK; } else if (mv88e6xxx_num_databases(chip) == 256) { /* VTU DBNum[7:4] are located in VTU Operation 11:8, and * VTU DBNum[3:0] are located in VTU Operation 3:0 @@ -1491,8 +1489,7 @@ static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, } if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) { - reg = entry->fid & GLOBAL_VTU_FID_MASK; - err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, reg); + err = mv88e6xxx_g1_vtu_fid_write(chip, entry); if (err) return err; } else if (mv88e6xxx_num_databases(chip) == 256) { diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index ed40e5557bbc..353de283362f 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -50,6 +50,10 @@ int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all); int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port, bool all); +int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); +int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op); int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip); diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index c41ca28da084..81d6f9f3c84a 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -14,6 +14,31 @@ #include "mv88e6xxx.h" #include "global1.h" +/* Offset 0x02: VTU FID Register */ + +int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + u16 val; + int err; + + err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_FID, &val); + if (err) + return err; + + entry->fid = val & GLOBAL_VTU_FID_MASK; + + return 0; +} + +int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + u16 val = entry->fid & GLOBAL_VTU_FID_MASK; + + return mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, val); +} + /* Offset 0x05: VTU Operation Register */ int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip) -- cgit From d2ca1ea18db6a90475b983e65e8435632fe3d57e Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:15 -0400 Subject: net: dsa: mv88e6xxx: move VTU SID accessors Add helpers to access the VTU SID register in the global1_vtu.c file. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 21 ++++++++------------- drivers/net/dsa/mv88e6xxx/global1.h | 4 ++++ drivers/net/dsa/mv88e6xxx/global1_vtu.c | 25 +++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 0452543a2463..ec621879439d 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1387,11 +1387,9 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, } if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) { - err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val); + err = mv88e6xxx_g1_vtu_sid_read(chip, &next); if (err) return err; - - next.sid = val & GLOBAL_VTU_SID_MASK; } } @@ -1482,8 +1480,7 @@ static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, return err; if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) { - reg = entry->sid & GLOBAL_VTU_SID_MASK; - err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg); + err = mv88e6xxx_g1_vtu_sid_write(chip, entry); if (err) return err; } @@ -1513,7 +1510,9 @@ loadpurge: static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, struct mv88e6xxx_vtu_entry *entry) { - struct mv88e6xxx_vtu_entry next = { 0 }; + struct mv88e6xxx_vtu_entry next = { + .sid = sid, + }; u16 val; int err; @@ -1521,8 +1520,7 @@ static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, if (err) return err; - err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, - sid & GLOBAL_VTU_SID_MASK); + err = mv88e6xxx_g1_vtu_sid_write(chip, &next); if (err) return err; @@ -1530,12 +1528,10 @@ static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, if (err) return err; - err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val); + err = mv88e6xxx_g1_vtu_sid_read(chip, &next); if (err) return err; - next.sid = val & GLOBAL_VTU_SID_MASK; - err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val); if (err) return err; @@ -1576,8 +1572,7 @@ loadpurge: if (err) return err; - reg = entry->sid & GLOBAL_VTU_SID_MASK; - err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg); + err = mv88e6xxx_g1_vtu_sid_write(chip, entry); if (err) return err; diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 353de283362f..1bf235085215 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -54,6 +54,10 @@ int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); +int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); +int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op); int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip); diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 81d6f9f3c84a..201c063d15f2 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -39,6 +39,31 @@ int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip, return mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, val); } +/* Offset 0x03: VTU SID Register */ + +int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + u16 val; + int err; + + err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val); + if (err) + return err; + + entry->sid = val & GLOBAL_VTU_SID_MASK; + + return 0; +} + +int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + u16 val = entry->sid & GLOBAL_VTU_SID_MASK; + + return mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, val); +} + /* Offset 0x05: VTU Operation Register */ int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip) -- cgit From 3afb4bde6fe8f2d43b2153cc2672d07477729cca Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:16 -0400 Subject: net: dsa: mv88e6xxx: move VTU VID accessors Add helpers to access the VTU VID register in the global1_vtu.c file. At the same time, move mv88e6xxx_g1_vtu_vid_write at the beginning of _mv88e6xxx_vtu_loadpurge, which adds no functional changes but makes future patches simpler. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 57 +++++++++++++-------------------- drivers/net/dsa/mv88e6xxx/global1.h | 4 +++ drivers/net/dsa/mv88e6xxx/global1_vtu.c | 29 +++++++++++++++++ 3 files changed, 56 insertions(+), 34 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index ec621879439d..dce490e78347 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1337,12 +1337,6 @@ static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip, return _mv88e6xxx_vtu_stu_data_write(chip, entry, 2); } -static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_chip *chip, u16 vid) -{ - return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, - vid & GLOBAL_VTU_VID_MASK); -} - static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry) { @@ -1358,13 +1352,10 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, if (err) return err; - err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val); + err = mv88e6xxx_g1_vtu_vid_read(chip, &next); if (err) return err; - next.vid = val & GLOBAL_VTU_VID_MASK; - next.valid = !!(val & GLOBAL_VTU_VID_VALID); - if (next.valid) { err = mv88e6xxx_vtu_data_read(chip, &next); if (err) @@ -1410,7 +1401,9 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, int (*cb)(struct switchdev_obj *obj)) { struct mv88e6xxx_chip *chip = ds->priv; - struct mv88e6xxx_vtu_entry next; + struct mv88e6xxx_vtu_entry next = { + .vid = chip->info->max_vid, + }; u16 pvid; int err; @@ -1423,7 +1416,7 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, if (err) goto unlock; - err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK); + err = mv88e6xxx_g1_vtu_vid_write(chip, &next); if (err) goto unlock; @@ -1464,13 +1457,16 @@ static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry) { u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE; - u16 reg = 0; int err; err = mv88e6xxx_g1_vtu_op_wait(chip); if (err) return err; + err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + if (err) + return err; + if (!entry->valid) goto loadpurge; @@ -1496,14 +1492,7 @@ static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, op |= (entry->fid & 0xf0) << 8; op |= entry->fid & 0xf; } - - reg = GLOBAL_VTU_VID_VALID; loadpurge: - reg |= entry->vid & GLOBAL_VTU_VID_MASK; - err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg); - if (err) - return err; - return mv88e6xxx_g1_vtu_op(chip, op); } @@ -1513,7 +1502,6 @@ static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, struct mv88e6xxx_vtu_entry next = { .sid = sid, }; - u16 val; int err; err = mv88e6xxx_g1_vtu_op_wait(chip); @@ -1532,12 +1520,10 @@ static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, if (err) return err; - err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val); + err = mv88e6xxx_g1_vtu_vid_read(chip, &next); if (err) return err; - next.valid = !!(val & GLOBAL_VTU_VID_VALID); - if (next.valid) { err = mv88e6xxx_stu_data_read(chip, &next); if (err) @@ -1551,7 +1537,6 @@ static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry) { - u16 reg = 0; int err; err = mv88e6xxx_g1_vtu_op_wait(chip); @@ -1565,10 +1550,8 @@ static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip, err = mv88e6xxx_stu_data_write(chip, entry); if (err) return err; - - reg = GLOBAL_VTU_VID_VALID; loadpurge: - err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg); + err = mv88e6xxx_g1_vtu_vid_write(chip, entry); if (err) return err; @@ -1582,7 +1565,9 @@ loadpurge: static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) { DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); - struct mv88e6xxx_vtu_entry vlan; + struct mv88e6xxx_vtu_entry vlan = { + .vid = chip->info->max_vid, + }; int i, err; bitmap_zero(fid_bitmap, MV88E6XXX_N_FID); @@ -1597,7 +1582,7 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) } /* Set every FID bit used by the VLAN entries */ - err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK); + err = mv88e6xxx_g1_vtu_vid_write(chip, &vlan); if (err) return err; @@ -1681,7 +1666,9 @@ static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid, if (!vid) return -EINVAL; - err = _mv88e6xxx_vtu_vid_write(chip, vid - 1); + entry->vid = vid - 1; + entry->valid = false; + err = mv88e6xxx_g1_vtu_vid_write(chip, entry); if (err) return err; @@ -1706,7 +1693,9 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, u16 vid_begin, u16 vid_end) { struct mv88e6xxx_chip *chip = ds->priv; - struct mv88e6xxx_vtu_entry vlan; + struct mv88e6xxx_vtu_entry vlan = { + .vid = vid_begin - 1, + }; int i, err; if (!vid_begin) @@ -1714,7 +1703,7 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, mutex_lock(&chip->reg_lock); - err = _mv88e6xxx_vtu_vid_write(chip, vid_begin - 1); + err = mv88e6xxx_g1_vtu_vid_write(chip, &vlan); if (err) goto unlock; @@ -2076,7 +2065,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, return err; /* Dump VLANs' Filtering Information Databases */ - err = _mv88e6xxx_vtu_vid_write(chip, vlan.vid); + err = mv88e6xxx_g1_vtu_vid_write(chip, &vlan); if (err) return err; diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 1bf235085215..22fe22bd82cd 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -58,6 +58,10 @@ int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); +int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); +int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op); int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip); diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 201c063d15f2..6ac3d0eeae6b 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -82,6 +82,35 @@ int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op) return mv88e6xxx_g1_vtu_op_wait(chip); } +/* Offset 0x06: VTU VID Register */ + +int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + u16 val; + int err; + + err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val); + if (err) + return err; + + entry->vid = val & 0xfff; + entry->valid = !!(val & GLOBAL_VTU_VID_VALID); + + return 0; +} + +int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + u16 val = entry->vid & 0xfff; + + if (entry->valid) + val |= GLOBAL_VTU_VID_VALID; + + return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, val); +} + /* VLAN Translation Unit Operations */ int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip) -- cgit From f169e5ee5f29f81c1c8d647fa6fb5387ee793131 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:17 -0400 Subject: net: dsa: mv88e6xxx: move generic VTU GetNext Even though every switch model has a different way to access the VTU Data bits, the base implementation of the VTU GetNext operation remains the same: wait, write the first VID to iterate from, start the operation, and read the next VID. Move this generic implementation into global1_vtu.c and abstract the handling of the start VID (similarly to the ATU GetNext implementation), before introducing a new chip operation for specific chips. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 31 ++----------------------------- drivers/net/dsa/mv88e6xxx/global1.h | 2 ++ drivers/net/dsa/mv88e6xxx/global1_vtu.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 29 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index dce490e78347..70b420e134ce 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1340,19 +1340,11 @@ static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip, static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry) { - struct mv88e6xxx_vtu_entry next = { 0 }; + struct mv88e6xxx_vtu_entry next = *entry; u16 val; int err; - err = mv88e6xxx_g1_vtu_op_wait(chip); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_GET_NEXT); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_vid_read(chip, &next); + err = mv88e6xxx_g1_vtu_getnext(chip, &next); if (err) return err; @@ -1416,10 +1408,6 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, if (err) goto unlock; - err = mv88e6xxx_g1_vtu_vid_write(chip, &next); - if (err) - goto unlock; - do { err = _mv88e6xxx_vtu_getnext(chip, &next); if (err) @@ -1582,10 +1570,6 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) } /* Set every FID bit used by the VLAN entries */ - err = mv88e6xxx_g1_vtu_vid_write(chip, &vlan); - if (err) - return err; - do { err = _mv88e6xxx_vtu_getnext(chip, &vlan); if (err) @@ -1668,9 +1652,6 @@ static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid, entry->vid = vid - 1; entry->valid = false; - err = mv88e6xxx_g1_vtu_vid_write(chip, entry); - if (err) - return err; err = _mv88e6xxx_vtu_getnext(chip, entry); if (err) @@ -1703,10 +1684,6 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, mutex_lock(&chip->reg_lock); - err = mv88e6xxx_g1_vtu_vid_write(chip, &vlan); - if (err) - goto unlock; - do { err = _mv88e6xxx_vtu_getnext(chip, &vlan); if (err) @@ -2065,10 +2042,6 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, return err; /* Dump VLANs' Filtering Information Databases */ - err = mv88e6xxx_g1_vtu_vid_write(chip, &vlan); - if (err) - return err; - do { err = _mv88e6xxx_vtu_getnext(chip, &vlan); if (err) diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 22fe22bd82cd..66216848fc0d 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -64,6 +64,8 @@ int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op); +int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 6ac3d0eeae6b..c3f55be873b3 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -113,6 +113,35 @@ int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, /* VLAN Translation Unit Operations */ +int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_vtu_op_wait(chip); + if (err) + return err; + + /* To get the next higher active VID, the VTU GetNext operation can be + * started again without setting the VID registers since it already + * contains the last VID. + * + * To save a few hardware accesses and abstract this to the caller, + * write the VID only once, when the entry is given as invalid. + */ + if (!entry->valid) { + err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + if (err) + return err; + } + + err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_GET_NEXT); + if (err) + return err; + + return mv88e6xxx_g1_vtu_vid_read(chip, entry); +} + int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip) { int err; -- cgit From c499a64f349d063d8cdb40c0b96e84c35bbc414c Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:18 -0400 Subject: net: dsa: mv88e6xxx: move VTU Data accessors The code to access the VTU Data registers currently only supports the 88E6185 family and alike: 2-bit membership adjacent to 2-bit port state. Even though the 88E6352 family introduced an indirect table to program the VLAN Spanning Tree states, the usage of the VTU Data registers remains the same regardless the VTU or STU operation. Now that the mv88e6xxx_vtu_entry structure contains both port membership and states data, factorize the code to access them in global1_vtu.c. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 88 +++------------------------------ drivers/net/dsa/mv88e6xxx/global1.h | 4 ++ drivers/net/dsa/mv88e6xxx/global1_vtu.c | 61 +++++++++++++++++++++++ 3 files changed, 72 insertions(+), 81 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 70b420e134ce..b163e40bf42e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1263,80 +1263,6 @@ static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) netdev_err(ds->ports[port].netdev, "failed to flush ATU\n"); } -static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry, - unsigned int nibble_offset) -{ - u16 regs[3]; - int i, err; - - for (i = 0; i < 3; ++i) { - u16 *reg = ®s[i]; - - err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg); - if (err) - return err; - } - - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { - unsigned int shift = (i % 4) * 4 + nibble_offset; - u16 reg = regs[i / 4]; - - entry->state[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK; - } - - return 0; -} - -static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) -{ - return _mv88e6xxx_vtu_stu_data_read(chip, entry, 0); -} - -static int mv88e6xxx_stu_data_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) -{ - return _mv88e6xxx_vtu_stu_data_read(chip, entry, 2); -} - -static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry, - unsigned int nibble_offset) -{ - u16 regs[3] = { 0 }; - int i, err; - - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { - unsigned int shift = (i % 4) * 4 + nibble_offset; - u8 data = entry->state[i]; - - regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift; - } - - for (i = 0; i < 3; ++i) { - u16 reg = regs[i]; - - err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg); - if (err) - return err; - } - - return 0; -} - -static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) -{ - return _mv88e6xxx_vtu_stu_data_write(chip, entry, 0); -} - -static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) -{ - return _mv88e6xxx_vtu_stu_data_write(chip, entry, 2); -} - static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry) { @@ -1349,10 +1275,6 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, return err; if (next.valid) { - err = mv88e6xxx_vtu_data_read(chip, &next); - if (err) - return err; - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) { err = mv88e6xxx_g1_vtu_fid_read(chip, &next); if (err) @@ -1374,6 +1296,10 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, if (err) return err; } + + err = mv88e6185_g1_vtu_data_read(chip, &next); + if (err) + return err; } *entry = next; @@ -1459,7 +1385,7 @@ static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, goto loadpurge; /* Write port member tags */ - err = mv88e6xxx_vtu_data_write(chip, entry); + err = mv88e6185_g1_vtu_data_write(chip, entry); if (err) return err; @@ -1513,7 +1439,7 @@ static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, return err; if (next.valid) { - err = mv88e6xxx_stu_data_read(chip, &next); + err = mv88e6185_g1_vtu_data_read(chip, &next); if (err) return err; } @@ -1535,7 +1461,7 @@ static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip, goto loadpurge; /* Write port states */ - err = mv88e6xxx_stu_data_write(chip, entry); + err = mv88e6185_g1_vtu_data_write(chip, entry); if (err) return err; loadpurge: diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 66216848fc0d..9644ca649838 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -62,6 +62,10 @@ int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); +int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); +int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op); int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index c3f55be873b3..060c4a5a802d 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -111,6 +111,67 @@ int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, val); } +/* Offset 0x07: VTU/STU Data Register 1 + * Offset 0x08: VTU/STU Data Register 2 + * Offset 0x09: VTU/STU Data Register 3 + */ + +int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + u16 regs[3]; + int i; + + /* Read all 3 VTU/STU Data registers */ + for (i = 0; i < 3; ++i) { + u16 *reg = ®s[i]; + int err; + + err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg); + if (err) + return err; + } + + /* Extract MemberTag and PortState data */ + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { + unsigned int member_offset = (i % 4) * 4; + unsigned int state_offset = member_offset + 2; + + entry->member[i] = (regs[i / 4] >> member_offset) & 0x3; + entry->state[i] = (regs[i / 4] >> state_offset) & 0x3; + } + + return 0; +} + +int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + u16 regs[3] = { 0 }; + int i; + + /* Insert MemberTag and PortState data */ + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { + unsigned int member_offset = (i % 4) * 4; + unsigned int state_offset = member_offset + 2; + + regs[i / 4] |= (entry->member[i] & 0x3) << member_offset; + regs[i / 4] |= (entry->state[i] & 0x3) << state_offset; + } + + /* Write all 3 VTU/STU Data registers */ + for (i = 0; i < 3; ++i) { + u16 reg = regs[i]; + int err; + + err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg); + if (err) + return err; + } + + return 0; +} + /* VLAN Translation Unit Operations */ int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, -- cgit From 66a8e1f93319b8e3f5b6e81c06a2534c1491157c Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:19 -0400 Subject: net: dsa: mv88e6xxx: move STU GetNext operation Extract the generic portion of code to issue an STU GetNext operation, which will be used in other implementations. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 14 +------------- drivers/net/dsa/mv88e6xxx/global1.h | 2 ++ drivers/net/dsa/mv88e6xxx/global1_vtu.c | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index b163e40bf42e..5441b05f519b 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1422,19 +1422,7 @@ static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, if (err) return err; - err = mv88e6xxx_g1_vtu_sid_write(chip, &next); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_GET_NEXT); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_sid_read(chip, &next); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_vid_read(chip, &next); + err = mv88e6xxx_g1_vtu_stu_getnext(chip, &next); if (err) return err; diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 9644ca649838..5d276cc4262b 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -70,6 +70,8 @@ int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip); int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op); int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); +int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *vtu); int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 060c4a5a802d..cf572ba76195 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -174,6 +174,26 @@ int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, /* VLAN Translation Unit Operations */ +int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_vtu_sid_write(chip, entry); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_GET_NEXT); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_sid_read(chip, entry); + if (err) + return err; + + return mv88e6xxx_g1_vtu_vid_read(chip, entry); +} + int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry) { -- cgit From ef6fcea37f014ec54a0a9f7eaecc78cdb6ffc71e Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:20 -0400 Subject: net: dsa: mv88e6xxx: get STU entry on VTU GetNext Now that the code reads both VTU and STU data on VTU GetNext operation, fetch the STU entry data of a VTU entry at the same time. The STU data bits are masked with the VTU data bits and they are now all read at the same time a VTU GetNext operation is issued. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- drivers/net/dsa/mv88e6xxx/global1.h | 2 ++ drivers/net/dsa/mv88e6xxx/global1_vtu.c | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 5441b05f519b..38c3e047fbf9 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1292,7 +1292,7 @@ static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, } if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) { - err = mv88e6xxx_g1_vtu_sid_read(chip, &next); + err = mv88e6xxx_g1_vtu_stu_get(chip, &next); if (err) return err; } diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 5d276cc4262b..76b49a3a4701 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -72,6 +72,8 @@ int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *vtu); +int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *vtu); int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index cf572ba76195..710f86fa3b4e 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -194,6 +194,28 @@ int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip, return mv88e6xxx_g1_vtu_vid_read(chip, entry); } +int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *vtu) +{ + struct mv88e6xxx_vtu_entry stu; + int err; + + err = mv88e6xxx_g1_vtu_sid_read(chip, vtu); + if (err) + return err; + + stu.sid = vtu->sid - 1; + + err = mv88e6xxx_g1_vtu_stu_getnext(chip, &stu); + if (err) + return err; + + if (stu.sid != vtu->sid || !stu.valid) + return -EINVAL; + + return 0; +} + int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry) { -- cgit From 021e64ff7676ad5183c1845d4b316df20175702a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:21 -0400 Subject: net: dsa: mv88e6xxx: load STU entry with VTU entry Now that the code writes both VTU and STU data when loading a VTU entry, load the corresponding STU entry at the same time. This allows us to get rid of the STU management in the _mv88e6xxx_vtu_new helper and thus remove the separate implementations of STU Load/Purge and STU GetNext, as well as the unused family checks. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 108 ++------------------------------------- 1 file changed, 4 insertions(+), 104 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 38c3e047fbf9..fc30a3e3df47 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -677,31 +677,6 @@ static int mv88e6xxx_phy_ppu_write(struct mv88e6xxx_chip *chip, return err; } -static bool mv88e6xxx_6097_family(struct mv88e6xxx_chip *chip) -{ - return chip->info->family == MV88E6XXX_FAMILY_6097; -} - -static bool mv88e6xxx_6165_family(struct mv88e6xxx_chip *chip) -{ - return chip->info->family == MV88E6XXX_FAMILY_6165; -} - -static bool mv88e6xxx_6341_family(struct mv88e6xxx_chip *chip) -{ - return chip->info->family == MV88E6XXX_FAMILY_6341; -} - -static bool mv88e6xxx_6351_family(struct mv88e6xxx_chip *chip) -{ - return chip->info->family == MV88E6XXX_FAMILY_6351; -} - -static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip) -{ - return chip->info->family == MV88E6XXX_FAMILY_6352; -} - static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link, int speed, int duplex, phy_interface_t mode) @@ -1393,6 +1368,10 @@ static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, err = mv88e6xxx_g1_vtu_sid_write(chip, entry); if (err) return err; + + err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); + if (err) + return err; } if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) { @@ -1410,60 +1389,6 @@ loadpurge: return mv88e6xxx_g1_vtu_op(chip, op); } -static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid, - struct mv88e6xxx_vtu_entry *entry) -{ - struct mv88e6xxx_vtu_entry next = { - .sid = sid, - }; - int err; - - err = mv88e6xxx_g1_vtu_op_wait(chip); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_stu_getnext(chip, &next); - if (err) - return err; - - if (next.valid) { - err = mv88e6185_g1_vtu_data_read(chip, &next); - if (err) - return err; - } - - *entry = next; - return 0; -} - -static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) -{ - int err; - - err = mv88e6xxx_g1_vtu_op_wait(chip); - if (err) - return err; - - if (!entry->valid) - goto loadpurge; - - /* Write port states */ - err = mv88e6185_g1_vtu_data_write(chip, entry); - if (err) - return err; -loadpurge: - err = mv88e6xxx_g1_vtu_vid_write(chip, entry); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_sid_write(chip, entry); - if (err) - return err; - - return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); -} - static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) { DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); @@ -1527,31 +1452,6 @@ static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid, ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER; - if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) || - mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip) || - mv88e6xxx_6341_family(chip)) { - struct mv88e6xxx_vtu_entry vstp; - - /* Adding a VTU entry requires a valid STU entry. As VSTP is not - * implemented, only one STU entry is needed to cover all VTU - * entries. Thus, validate the SID 0. - */ - vlan.sid = 0; - err = _mv88e6xxx_stu_getnext(chip, GLOBAL_VTU_SID_MASK, &vstp); - if (err) - return err; - - if (vstp.sid != vlan.sid || !vstp.valid) { - memset(&vstp, 0, sizeof(vstp)); - vstp.valid = true; - vstp.sid = vlan.sid; - - err = _mv88e6xxx_stu_loadpurge(chip, &vstp); - if (err) - return err; - } - } - *entry = vlan; return 0; } -- cgit From f1394b78a602bae124a9b8473465ba48f4a5d5b2 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:22 -0400 Subject: net: dsa: mv88e6xxx: add VTU GetNext operation Add a new vtu_getnext operation to the chip info structure to differ the various implementations of the VTU accesses. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 82 ++++++++++++++------------------- drivers/net/dsa/mv88e6xxx/global1.h | 4 ++ drivers/net/dsa/mv88e6xxx/global1_vtu.c | 57 +++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 4 ++ 4 files changed, 99 insertions(+), 48 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index fc30a3e3df47..1e14edebc0c7 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1238,49 +1238,6 @@ static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port) netdev_err(ds->ports[port].netdev, "failed to flush ATU\n"); } -static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) -{ - struct mv88e6xxx_vtu_entry next = *entry; - u16 val; - int err; - - err = mv88e6xxx_g1_vtu_getnext(chip, &next); - if (err) - return err; - - if (next.valid) { - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) { - err = mv88e6xxx_g1_vtu_fid_read(chip, &next); - if (err) - return err; - } else if (mv88e6xxx_num_databases(chip) == 256) { - /* VTU DBNum[7:4] are located in VTU Operation 11:8, and - * VTU DBNum[3:0] are located in VTU Operation 3:0 - */ - err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_OP, &val); - if (err) - return err; - - next.fid = (val & 0xf00) >> 4; - next.fid |= val & 0xf; - } - - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) { - err = mv88e6xxx_g1_vtu_stu_get(chip, &next); - if (err) - return err; - } - - err = mv88e6185_g1_vtu_data_read(chip, &next); - if (err) - return err; - } - - *entry = next; - return 0; -} - static int mv88e6xxx_vtu_setup(struct mv88e6xxx_chip *chip) { if (!chip->info->max_vid) @@ -1289,6 +1246,15 @@ static int mv88e6xxx_vtu_setup(struct mv88e6xxx_chip *chip) return mv88e6xxx_g1_vtu_flush(chip); } +static int mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + if (!chip->info->ops->vtu_getnext) + return -EOPNOTSUPP; + + return chip->info->ops->vtu_getnext(chip, entry); +} + static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, struct switchdev_obj_port_vlan *vlan, int (*cb)(struct switchdev_obj *obj)) @@ -1310,7 +1276,7 @@ static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, goto unlock; do { - err = _mv88e6xxx_vtu_getnext(chip, &next); + err = mv88e6xxx_vtu_getnext(chip, &next); if (err) break; @@ -1410,7 +1376,7 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) /* Set every FID bit used by the VLAN entries */ do { - err = _mv88e6xxx_vtu_getnext(chip, &vlan); + err = mv88e6xxx_vtu_getnext(chip, &vlan); if (err) return err; @@ -1467,7 +1433,7 @@ static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid, entry->vid = vid - 1; entry->valid = false; - err = _mv88e6xxx_vtu_getnext(chip, entry); + err = mv88e6xxx_vtu_getnext(chip, entry); if (err) return err; @@ -1499,7 +1465,7 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, mutex_lock(&chip->reg_lock); do { - err = _mv88e6xxx_vtu_getnext(chip, &vlan); + err = mv88e6xxx_vtu_getnext(chip, &vlan); if (err) goto unlock; @@ -1857,7 +1823,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, /* Dump VLANs' Filtering Information Databases */ do { - err = _mv88e6xxx_vtu_getnext(chip, &vlan); + err = mv88e6xxx_vtu_getnext(chip, &vlan); if (err) return err; @@ -2702,6 +2668,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .ppu_enable = mv88e6185_g1_ppu_enable, .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -2723,6 +2690,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .ppu_enable = mv88e6185_g1_ppu_enable, .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, + .vtu_getnext = mv88e6185_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6097_ops = { @@ -2751,6 +2719,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -2774,6 +2743,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -2803,6 +2773,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .ppu_enable = mv88e6185_g1_ppu_enable, .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, + .vtu_getnext = mv88e6185_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6141_ops = { @@ -2834,6 +2805,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -2862,6 +2834,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6165_ops = { @@ -2883,6 +2856,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -2912,6 +2886,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -2943,6 +2918,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -2972,6 +2948,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -3003,6 +2980,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -3028,6 +3006,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .ppu_enable = mv88e6185_g1_ppu_enable, .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, + .vtu_getnext = mv88e6185_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6190_ops = { @@ -3149,6 +3128,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6290_ops = { @@ -3209,6 +3189,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .g1_set_egress_port = mv88e6095_g1_set_egress_port, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6185_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -3237,6 +3218,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .g1_set_cpu_port = mv88e6095_g1_set_cpu_port, .g1_set_egress_port = mv88e6095_g1_set_egress_port, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6185_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6341_ops = { @@ -3268,6 +3250,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -3297,6 +3280,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -3326,6 +3310,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -3357,6 +3342,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6352_g1_vtu_getnext, }; static const struct mv88e6xxx_ops mv88e6390_ops = { diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 76b49a3a4701..780ee8241dbf 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -74,6 +74,10 @@ int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *vtu); int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *vtu); +int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); +int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 710f86fa3b4e..8b07b6b0fc7d 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -245,6 +245,63 @@ int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, return mv88e6xxx_g1_vtu_vid_read(chip, entry); } +int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + u16 val; + int err; + + err = mv88e6xxx_g1_vtu_getnext(chip, entry); + if (err) + return err; + + if (entry->valid) { + err = mv88e6185_g1_vtu_data_read(chip, entry); + if (err) + return err; + + /* VTU DBNum[3:0] are located in VTU Operation 3:0 + * VTU DBNum[7:4] are located in VTU Operation 11:8 + */ + err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_OP, &val); + if (err) + return err; + + entry->fid = val & 0x000f; + entry->fid |= (val & 0x0f00) >> 4; + } + + return 0; +} + +int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + int err; + + /* Fetch VLAN MemberTag data from the VTU */ + err = mv88e6xxx_g1_vtu_getnext(chip, entry); + if (err) + return err; + + if (entry->valid) { + /* Fetch (and mask) VLAN PortState data from the STU */ + err = mv88e6xxx_g1_vtu_stu_get(chip, entry); + if (err) + return err; + + err = mv88e6185_g1_vtu_data_read(chip, entry); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_fid_read(chip, entry); + if (err) + return err; + } + + return 0; +} + int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip) { int err; diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index 8638892a7e18..b356f2875643 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -890,6 +890,10 @@ struct mv88e6xxx_ops { /* Can be either in g1 or g2, so don't use a prefix */ int (*mgmt_rsvd2cpu)(struct mv88e6xxx_chip *chip); + + /* VLAN Translation Unit operations */ + int (*vtu_getnext)(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); }; struct mv88e6xxx_irq_ops { -- cgit From 0ad5daf6ba80af4a8d72b4284079357c4e3b9e4a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:23 -0400 Subject: net: dsa: mv88e6xxx: add VTU Load/Purge operation Add a new vtu_loadpurge operation to the chip info structure to differ the various implementations of the VTU accesses. Now that the STU handling is abstracted behind VTU operations, kill the obsolete MV88E6XXX_FLAG_STU flag. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 80 +++++++++++++-------------------- drivers/net/dsa/mv88e6xxx/global1.h | 4 ++ drivers/net/dsa/mv88e6xxx/global1_vtu.c | 66 +++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 10 +---- 4 files changed, 103 insertions(+), 57 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 1e14edebc0c7..51c8b2ff9760 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1255,6 +1255,15 @@ static int mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip, return chip->info->ops->vtu_getnext(chip, entry); } +static int mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + if (!chip->info->ops->vtu_loadpurge) + return -EOPNOTSUPP; + + return chip->info->ops->vtu_loadpurge(chip, entry); +} + static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port, struct switchdev_obj_port_vlan *vlan, int (*cb)(struct switchdev_obj *obj)) @@ -1308,53 +1317,6 @@ unlock: return err; } -static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) -{ - u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE; - int err; - - err = mv88e6xxx_g1_vtu_op_wait(chip); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_vid_write(chip, entry); - if (err) - return err; - - if (!entry->valid) - goto loadpurge; - - /* Write port member tags */ - err = mv88e6185_g1_vtu_data_write(chip, entry); - if (err) - return err; - - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) { - err = mv88e6xxx_g1_vtu_sid_write(chip, entry); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); - if (err) - return err; - } - - if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) { - err = mv88e6xxx_g1_vtu_fid_write(chip, entry); - if (err) - return err; - } else if (mv88e6xxx_num_databases(chip) == 256) { - /* VTU DBNum[7:4] are located in VTU Operation 11:8, and - * VTU DBNum[3:0] are located in VTU Operation 3:0 - */ - op |= (entry->fid & 0xf0) << 8; - op |= entry->fid & 0xf; - } -loadpurge: - return mv88e6xxx_g1_vtu_op(chip, op); -} - static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) { DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); @@ -1565,7 +1527,7 @@ static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port, GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED : GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED; - return _mv88e6xxx_vtu_loadpurge(chip, &vlan); + return mv88e6xxx_vtu_loadpurge(chip, &vlan); } static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, @@ -1624,7 +1586,7 @@ static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip, } } - err = _mv88e6xxx_vtu_loadpurge(chip, &vlan); + err = mv88e6xxx_vtu_loadpurge(chip, &vlan); if (err) return err; @@ -2669,6 +2631,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -2691,6 +2654,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6097_ops = { @@ -2720,6 +2684,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -2744,6 +2709,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -2774,6 +2740,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6141_ops = { @@ -2806,6 +2773,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -2835,6 +2803,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6165_ops = { @@ -2857,6 +2826,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -2887,6 +2857,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -2919,6 +2890,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -2949,6 +2921,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -2981,6 +2954,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -3007,6 +2981,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6190_ops = { @@ -3129,6 +3104,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6290_ops = { @@ -3190,6 +3166,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -3219,6 +3196,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .g1_set_egress_port = mv88e6095_g1_set_egress_port, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, + .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6341_ops = { @@ -3251,6 +3229,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -3281,6 +3260,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -3311,6 +3291,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -3343,6 +3324,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, + .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6390_ops = { diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 780ee8241dbf..60dd7079c756 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -76,8 +76,12 @@ int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *vtu); int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); +int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); +int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 8b07b6b0fc7d..73e08c5c3948 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -302,6 +302,72 @@ int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, return 0; } +int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE; + int err; + + err = mv88e6xxx_g1_vtu_op_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + if (err) + return err; + + if (entry->valid) { + err = mv88e6185_g1_vtu_data_write(chip, entry); + if (err) + return err; + + /* VTU DBNum[3:0] are located in VTU Operation 3:0 + * VTU DBNum[7:4] are located in VTU Operation 11:8 + */ + op |= entry->fid & 0x000f; + op |= (entry->fid & 0x00f0) << 8; + } + + return mv88e6xxx_g1_vtu_op(chip, op); +} + +int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_vtu_op_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + if (err) + return err; + + if (entry->valid) { + /* Write MemberTag and PortState data */ + err = mv88e6185_g1_vtu_data_write(chip, entry); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_sid_write(chip, entry); + if (err) + return err; + + /* Load STU entry */ + err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_fid_write(chip, entry); + if (err) + return err; + } + + /* Load/Purge VTU entry */ + return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_LOAD_PURGE); +} + int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip) { int err; diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index b356f2875643..a0d57b10acfe 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -566,8 +566,6 @@ enum mv88e6xxx_cap { #define MV88E6XXX_FLAG_G2_IRL_DATA BIT_ULL(MV88E6XXX_CAP_G2_IRL_DATA) #define MV88E6XXX_FLAG_G2_POT BIT_ULL(MV88E6XXX_CAP_G2_POT) -#define MV88E6XXX_FLAG_STU BIT_ULL(MV88E6XXX_CAP_STU) - /* Ingress Rate Limit unit */ #define MV88E6XXX_FLAGS_IRL \ (MV88E6XXX_FLAG_G2_IRL_CMD | \ @@ -595,7 +593,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ - MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP) @@ -606,7 +603,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ - MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP) @@ -631,7 +627,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ MV88E6XXX_FLAG_G2_POT | \ - MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP | \ MV88E6XXX_FLAGS_SERDES) @@ -643,7 +638,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ - MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP) @@ -655,7 +649,6 @@ enum mv88e6xxx_cap { MV88E6XXX_FLAG_G2_MGMT_EN_2X | \ MV88E6XXX_FLAG_G2_MGMT_EN_0X | \ MV88E6XXX_FLAG_G2_POT | \ - MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP | \ MV88E6XXX_FLAGS_SERDES) @@ -664,7 +657,6 @@ enum mv88e6xxx_cap { (MV88E6XXX_FLAG_EEE | \ MV88E6XXX_FLAG_GLOBAL2 | \ MV88E6XXX_FLAG_G2_INT | \ - MV88E6XXX_FLAG_STU | \ MV88E6XXX_FLAGS_IRL | \ MV88E6XXX_FLAGS_MULTI_CHIP) @@ -894,6 +886,8 @@ struct mv88e6xxx_ops { /* VLAN Translation Unit operations */ int (*vtu_getnext)(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); + int (*vtu_loadpurge)(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); }; struct mv88e6xxx_irq_ops { -- cgit From bf7d71c0451776143cdbd9a42a5bcbd6478da3c8 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:24 -0400 Subject: net: dsa: mv88e6xxx: make VTU helpers static Now that we have chip operations for VTU accesses, mark all helpers from global1_vtu.c as static. Only the various implementations of the GetNext, LoadPurge and Flush operations need to be exposed. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/global1.h | 24 ----------------- drivers/net/dsa/mv88e6xxx/global1_vtu.c | 48 ++++++++++++++++----------------- 2 files changed, 24 insertions(+), 48 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 60dd7079c756..82e9812b8ba7 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -50,30 +50,6 @@ int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all); int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port, bool all); -int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry); -int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry); -int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry); -int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry); -int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry); -int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry); -int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry); -int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry); -int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip); -int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op); -int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry); -int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *vtu); -int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *vtu); int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 73e08c5c3948..469056d0b421 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -16,8 +16,8 @@ /* Offset 0x02: VTU FID Register */ -int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) { u16 val; int err; @@ -31,8 +31,8 @@ int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip, return 0; } -int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) { u16 val = entry->fid & GLOBAL_VTU_FID_MASK; @@ -41,8 +41,8 @@ int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip, /* Offset 0x03: VTU SID Register */ -int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) { u16 val; int err; @@ -56,8 +56,8 @@ int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, return 0; } -int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) { u16 val = entry->sid & GLOBAL_VTU_SID_MASK; @@ -66,12 +66,12 @@ int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, /* Offset 0x05: VTU Operation Register */ -int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip) +static int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip) { return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY); } -int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op) +static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op) { int err; @@ -84,8 +84,8 @@ int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op) /* Offset 0x06: VTU VID Register */ -int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) { u16 val; int err; @@ -100,8 +100,8 @@ int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, return 0; } -int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) { u16 val = entry->vid & 0xfff; @@ -116,8 +116,8 @@ int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, * Offset 0x09: VTU/STU Data Register 3 */ -int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) { u16 regs[3]; int i; @@ -144,8 +144,8 @@ int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip, return 0; } -int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) { u16 regs[3] = { 0 }; int i; @@ -174,8 +174,8 @@ int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, /* VLAN Translation Unit Operations */ -int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) { int err; @@ -194,8 +194,8 @@ int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip, return mv88e6xxx_g1_vtu_vid_read(chip, entry); } -int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *vtu) +static int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *vtu) { struct mv88e6xxx_vtu_entry stu; int err; @@ -216,8 +216,8 @@ int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip, return 0; } -int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) { int err; -- cgit From 567aa59a8b055bb5853bc7e5d5f8ac191216c9c7 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:25 -0400 Subject: net: dsa: mv88e6xxx: simplify VTU entry getter Make the code which fetches or initializes a new VTU entry more concise. This allows us the get rid of the old underscore prefix naming. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 62 ++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 38 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 51c8b2ff9760..aede0194e341 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1359,33 +1359,8 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) return mv88e6xxx_g1_atu_flush(chip, *fid, true); } -static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid, - struct mv88e6xxx_vtu_entry *entry) -{ - struct dsa_switch *ds = chip->ds; - struct mv88e6xxx_vtu_entry vlan = { - .valid = true, - .vid = vid, - }; - int i, err; - - err = mv88e6xxx_atu_new(chip, &vlan.fid); - if (err) - return err; - - /* exclude all ports except the CPU and DSA ports */ - for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) - vlan.member[i] = dsa_is_cpu_port(ds, i) || - dsa_is_dsa_port(ds, i) - ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED - : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER; - - *entry = vlan; - return 0; -} - -static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid, - struct mv88e6xxx_vtu_entry *entry, bool creat) +static int mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid, + struct mv88e6xxx_vtu_entry *entry, bool new) { int err; @@ -1399,17 +1374,28 @@ static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid, if (err) return err; - if (entry->vid != vid || !entry->valid) { - if (!creat) - return -EOPNOTSUPP; - /* -ENOENT would've been more appropriate, but switchdev expects - * -EOPNOTSUPP to inform bridge about an eventual software VLAN. - */ + if (entry->vid == vid && entry->valid) + return 0; + + if (new) { + int i; + + /* Initialize a fresh VLAN entry */ + memset(entry, 0, sizeof(*entry)); + entry->valid = true; + entry->vid = vid; - err = _mv88e6xxx_vtu_new(chip, vid, entry); + /* Include only CPU and DSA ports */ + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) + entry->member[i] = dsa_is_normal_port(chip->ds, i) ? + GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER : + GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED; + + return mv88e6xxx_atu_new(chip, &entry->fid); } - return err; + /* switchdev expects -EOPNOTSUPP to honor software VLANs */ + return -EOPNOTSUPP; } static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, @@ -1519,7 +1505,7 @@ static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port, struct mv88e6xxx_vtu_entry vlan; int err; - err = _mv88e6xxx_vtu_get(chip, vid, &vlan, true); + err = mv88e6xxx_vtu_get(chip, vid, &vlan, true); if (err) return err; @@ -1564,7 +1550,7 @@ static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry vlan; int i, err; - err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false); + err = mv88e6xxx_vtu_get(chip, vid, &vlan, false); if (err) return err; @@ -1639,7 +1625,7 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, if (vid == 0) err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid); else - err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false); + err = mv88e6xxx_vtu_get(chip, vid, &vlan, false); if (err) return err; -- cgit From 1ac758648b574d3d01a648fc7018fc8b0bb7454a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:26 -0400 Subject: net: dsa: mv88e6xxx: support the VTU Page bit Newer chips such as the 88E6390 have a VTU Page bit in the VTU VID register to specify a 13th bit for the VID. This can be used to support 8K VLANs. When dumping the whole VTU, all VID bits must be set to one, including this VTU Page bit. Add support for VID greater than 4095. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/global1_vtu.c | 7 +++++++ drivers/net/dsa/mv88e6xxx/mv88e6xxx.h | 1 + 2 files changed, 8 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 469056d0b421..8e77974fb2f8 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -95,6 +95,10 @@ static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, return err; entry->vid = val & 0xfff; + + if (val & GLOBAL_VTU_VID_PAGE) + entry->vid |= 0x1000; + entry->valid = !!(val & GLOBAL_VTU_VID_VALID); return 0; @@ -105,6 +109,9 @@ static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, { u16 val = entry->vid & 0xfff; + if (entry->vid & 0x1000) + val |= GLOBAL_VTU_VID_PAGE; + if (entry->valid) val |= GLOBAL_VTU_VID_VALID; diff --git a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h index a0d57b10acfe..77236cd72df2 100644 --- a/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx/mv88e6xxx.h @@ -249,6 +249,7 @@ #define GLOBAL_VTU_OP_STU_GET_NEXT ((0x06 << 12) | GLOBAL_VTU_OP_BUSY) #define GLOBAL_VTU_VID 0x06 #define GLOBAL_VTU_VID_MASK 0xfff +#define GLOBAL_VTU_VID_PAGE BIT(13) #define GLOBAL_VTU_VID_VALID BIT(12) #define GLOBAL_VTU_DATA_0_3 0x07 #define GLOBAL_VTU_DATA_4_7 0x08 -- cgit From 931d18223998c5360b960d1ce247579f6152ad8f Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 1 May 2017 14:05:27 -0400 Subject: net: dsa: mv88e6xxx: add VTU support for 88E6390 The 6390 family of chips use only 2 of the 3 VTU Data registers to pack the MemberTag and PortState VLAN data. This means that they must be written or read before or after each VTU/STU operations. Implement this variant to add support for VTU with such chips. These chips have a 13th bit for the VID thus set their max_vid to 8191. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 18 +++++ drivers/net/dsa/mv88e6xxx/global1.h | 4 ++ drivers/net/dsa/mv88e6xxx/global1_vtu.c | 124 ++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index aede0194e341..19581d783d8e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2998,6 +2998,8 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6390_g1_vtu_getnext, + .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -3028,6 +3030,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6390_g1_vtu_getnext, + .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -3058,6 +3062,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6390_g1_vtu_getnext, + .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3122,6 +3128,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6390_g1_vtu_getnext, + .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3344,6 +3352,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6390_g1_vtu_getnext, + .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -3376,6 +3386,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .watchdog_ops = &mv88e6390_watchdog_ops, .mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu, .reset = mv88e6352_g1_reset, + .vtu_getnext = mv88e6390_g1_vtu_getnext, + .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { @@ -3615,6 +3627,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6190", .num_databases = 4096, .num_ports = 11, /* 10 + Z80 */ + .max_vid = 8191, .port_base_addr = 0x0, .global1_addr = 0x1b, .tag_protocol = DSA_TAG_PROTO_DSA, @@ -3632,6 +3645,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6190X", .num_databases = 4096, .num_ports = 11, /* 10 + Z80 */ + .max_vid = 8191, .port_base_addr = 0x0, .global1_addr = 0x1b, .age_time_coeff = 3750, @@ -3649,6 +3663,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6191", .num_databases = 4096, .num_ports = 11, /* 10 + Z80 */ + .max_vid = 8191, .port_base_addr = 0x0, .global1_addr = 0x1b, .age_time_coeff = 3750, @@ -3684,6 +3699,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6290", .num_databases = 4096, .num_ports = 11, /* 10 + Z80 */ + .max_vid = 8191, .port_base_addr = 0x0, .global1_addr = 0x1b, .age_time_coeff = 3750, @@ -3806,6 +3822,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6390", .num_databases = 4096, .num_ports = 11, /* 10 + Z80 */ + .max_vid = 8191, .port_base_addr = 0x0, .global1_addr = 0x1b, .age_time_coeff = 3750, @@ -3822,6 +3839,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6390X", .num_databases = 4096, .num_ports = 11, /* 10 + Z80 */ + .max_vid = 8191, .port_base_addr = 0x0, .global1_addr = 0x1b, .age_time_coeff = 3750, diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 82e9812b8ba7..46a4ea0f8c47 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -58,6 +58,10 @@ int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); +int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); +int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 8e77974fb2f8..9aea22d4c9e2 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -179,6 +179,56 @@ static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, return 0; } +static int mv88e6390_g1_vtu_data_read(struct mv88e6xxx_chip *chip, u8 *data) +{ + u16 regs[2]; + int i; + + /* Read the 2 VTU/STU Data registers */ + for (i = 0; i < 2; ++i) { + u16 *reg = ®s[i]; + int err; + + err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg); + if (err) + return err; + } + + /* Extract data */ + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { + unsigned int offset = (i % 8) * 2; + + data[i] = (regs[i / 8] >> offset) & 0x3; + } + + return 0; +} + +static int mv88e6390_g1_vtu_data_write(struct mv88e6xxx_chip *chip, u8 *data) +{ + u16 regs[2] = { 0 }; + int i; + + /* Insert data */ + for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { + unsigned int offset = (i % 8) * 2; + + regs[i / 8] |= (data[i] & 0x3) << offset; + } + + /* Write the 2 VTU/STU Data registers */ + for (i = 0; i < 2; ++i) { + u16 reg = regs[i]; + int err; + + err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg); + if (err) + return err; + } + + return 0; +} + /* VLAN Translation Unit Operations */ static int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip, @@ -309,6 +359,38 @@ int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, return 0; } +int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + int err; + + /* Fetch VLAN MemberTag data from the VTU */ + err = mv88e6xxx_g1_vtu_getnext(chip, entry); + if (err) + return err; + + if (entry->valid) { + err = mv88e6390_g1_vtu_data_read(chip, entry->member); + if (err) + return err; + + /* Fetch VLAN PortState data from the STU */ + err = mv88e6xxx_g1_vtu_stu_get(chip, entry); + if (err) + return err; + + err = mv88e6390_g1_vtu_data_read(chip, entry->state); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_fid_read(chip, entry); + if (err) + return err; + } + + return 0; +} + int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry) { @@ -375,6 +457,48 @@ int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_LOAD_PURGE); } +int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_vtu_op_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + if (err) + return err; + + if (entry->valid) { + /* Write PortState data */ + err = mv88e6390_g1_vtu_data_write(chip, entry->state); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_sid_write(chip, entry); + if (err) + return err; + + /* Load STU entry */ + err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); + if (err) + return err; + + /* Write MemberTag data */ + err = mv88e6390_g1_vtu_data_write(chip, entry->member); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_fid_write(chip, entry); + if (err) + return err; + } + + /* Load/Purge VTU entry */ + return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_LOAD_PURGE); +} + int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip) { int err; -- cgit From 5b8481fa42ac58484d633b558579e302aead64c1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 1 May 2017 15:10:20 -0400 Subject: ipv6: Need to export ipv6_push_frag_opts for tunneling now. Since that change also made the nfrag function not necessary for exports, remove it. Fixes: 89a23c8b528b ("ip6_tunnel: Fix missing tunnel encapsulation limit option") Signed-off-by: David S. Miller --- net/ipv6/exthdrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index d32e2110aff2..b636f1da9aec 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -946,13 +946,13 @@ void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, if (opt->hopopt) ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); } -EXPORT_SYMBOL(ipv6_push_nfrag_opts); void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto) { if (opt->dst1opt) ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt); } +EXPORT_SYMBOL(ipv6_push_frag_opts); struct ipv6_txoptions * ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) -- cgit From bc1bafbbe9b3558d7789ff151ef4f185b6ad21f3 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 1 May 2017 12:43:49 -0700 Subject: bpf: Move endianness BPF helpers out of bpf_util.h We do not want to include things like stdio.h and friends into eBPF program builds. bpf_util.h is for host compiled programs, so eBPF C-code helpers don't really belong there. Add a new bpf_endian.h as a quick fix for this for now. Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/bpf_endian.h | 23 +++++++++++++++++++++++ tools/testing/selftests/bpf/bpf_util.h | 19 ------------------- tools/testing/selftests/bpf/test_l4lb.c | 2 +- tools/testing/selftests/bpf/test_pkt_access.c | 2 +- 4 files changed, 25 insertions(+), 21 deletions(-) create mode 100644 tools/testing/selftests/bpf/bpf_endian.h diff --git a/tools/testing/selftests/bpf/bpf_endian.h b/tools/testing/selftests/bpf/bpf_endian.h new file mode 100644 index 000000000000..19d0604f8694 --- /dev/null +++ b/tools/testing/selftests/bpf/bpf_endian.h @@ -0,0 +1,23 @@ +#ifndef __BPF_ENDIAN__ +#define __BPF_ENDIAN__ + +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN +# define __bpf_ntohs(x) __builtin_bswap16(x) +# define __bpf_htons(x) __builtin_bswap16(x) +#elif __BYTE_ORDER == __BIG_ENDIAN +# define __bpf_ntohs(x) (x) +# define __bpf_htons(x) (x) +#else +# error "Fix your __BYTE_ORDER?!" +#endif + +#define bpf_htons(x) \ + (__builtin_constant_p(x) ? \ + __constant_htons(x) : __bpf_htons(x)) +#define bpf_ntohs(x) \ + (__builtin_constant_p(x) ? \ + __constant_ntohs(x) : __bpf_ntohs(x)) + +#endif diff --git a/tools/testing/selftests/bpf/bpf_util.h b/tools/testing/selftests/bpf/bpf_util.h index 369e7d7bba80..20ecbaa0d85d 100644 --- a/tools/testing/selftests/bpf/bpf_util.h +++ b/tools/testing/selftests/bpf/bpf_util.h @@ -6,25 +6,6 @@ #include #include -#include - -#if __BYTE_ORDER == __LITTLE_ENDIAN -# define __bpf_ntohs(x) __builtin_bswap16(x) -# define __bpf_htons(x) __builtin_bswap16(x) -#elif __BYTE_ORDER == __BIG_ENDIAN -# define __bpf_ntohs(x) (x) -# define __bpf_htons(x) (x) -#else -# error "Fix your __BYTE_ORDER?!" -#endif - -#define bpf_htons(x) \ - (__builtin_constant_p(x) ? \ - __constant_htons(x) : __bpf_htons(x)) -#define bpf_ntohs(x) \ - (__builtin_constant_p(x) ? \ - __constant_ntohs(x) : __bpf_ntohs(x)) - static inline unsigned int bpf_num_possible_cpus(void) { static const char *fcpu = "/sys/devices/system/cpu/possible"; diff --git a/tools/testing/selftests/bpf/test_l4lb.c b/tools/testing/selftests/bpf/test_l4lb.c index b68b21274bac..1e10c9590991 100644 --- a/tools/testing/selftests/bpf/test_l4lb.c +++ b/tools/testing/selftests/bpf/test_l4lb.c @@ -19,7 +19,7 @@ #include #include "bpf_helpers.h" #include "test_iptunnel_common.h" -#include "bpf_util.h" +#include "bpf_endian.h" int _version SEC("version") = 1; diff --git a/tools/testing/selftests/bpf/test_pkt_access.c b/tools/testing/selftests/bpf/test_pkt_access.c index 711300508ee0..39387bb7e08c 100644 --- a/tools/testing/selftests/bpf/test_pkt_access.c +++ b/tools/testing/selftests/bpf/test_pkt_access.c @@ -14,7 +14,7 @@ #include #include #include "bpf_helpers.h" -#include "bpf_util.h" +#include "bpf_endian.h" #define barrier() __asm__ __volatile__("": : :"memory") int _version SEC("version") = 1; -- cgit From e06422c43968916dc945018fd9220f60866470b1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 1 May 2017 12:58:21 -0700 Subject: bpf: Include bpf_endian.h in test_progs.c too. Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/test_progs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index 7c2d899c8f43..4ed049a0b14b 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -29,6 +29,7 @@ typedef __u16 __sum16; #include #include "test_iptunnel_common.h" #include "bpf_util.h" +#include "bpf_endian.h" static int error_cnt, pass_cnt; -- cgit From 48e75b430670ebdbb00ba008e1d3690f61ab9824 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 1 May 2017 22:18:01 +0200 Subject: rhashtable: compact struct rhashtable_params By using smaller datatypes this (rather large) struct shrinks considerably (80 -> 48 bytes on x86_64). As this is embedded in other structs, this also rerduces size of several others, e.g. cls_fl_head or nft_hash. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 18 +++++++++--------- lib/rhashtable.c | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index 45f89369c4c8..7d56a7ea2b2e 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -127,23 +127,23 @@ struct rhashtable; * @head_offset: Offset of rhash_head in struct to be hashed * @max_size: Maximum size while expanding * @min_size: Minimum size while shrinking - * @nulls_base: Base value to generate nulls marker - * @automatic_shrinking: Enable automatic shrinking of tables * @locks_mul: Number of bucket locks to allocate per cpu (default: 128) + * @automatic_shrinking: Enable automatic shrinking of tables + * @nulls_base: Base value to generate nulls marker * @hashfn: Hash function (default: jhash2 if !(key_len % 4), or jhash) * @obj_hashfn: Function to hash object * @obj_cmpfn: Function to compare key with object */ struct rhashtable_params { - size_t nelem_hint; - size_t key_len; - size_t key_offset; - size_t head_offset; + u16 nelem_hint; + u16 key_len; + u16 key_offset; + u16 head_offset; unsigned int max_size; - unsigned int min_size; - u32 nulls_base; + u16 min_size; bool automatic_shrinking; - size_t locks_mul; + u8 locks_mul; + u32 nulls_base; rht_hashfn_t hashfn; rht_obj_hashfn_t obj_hashfn; rht_obj_cmpfn_t obj_cmpfn; diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 3895486ef551..a930e436db5d 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -967,7 +967,7 @@ int rhashtable_init(struct rhashtable *ht, ht->max_elems = ht->p.max_size * 2; } - ht->p.min_size = max(ht->p.min_size, HASH_MIN_SIZE); + ht->p.min_size = max_t(u16, ht->p.min_size, HASH_MIN_SIZE); if (params->nelem_hint) size = rounded_hashtable_size(&ht->p); -- cgit From e3bf4c61da801c7967d0efff0c3f6b22d2c0e544 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 1 May 2017 20:26:02 -0700 Subject: sparc64: Fix BPF JIT wrt. branches and ldimm64 instructions. Like other JITs, sparc64 maintains an array of instruction offsets but stores the entries off by one. This is done because jumps to the exit block are indexed to one past the last BPF instruction. So if we size the array by the program length, we need to record the previous instruction in order to stay within the array bounds. This is explained in ARM JIT commit 8eee539ddea0 ("arm64: bpf: fix out-of-bounds read in bpf2a64_offset()"). But this scheme requires a little bit of careful handling when the instruction before the branch destination is a 64-bit load immediate. It takes up 2 BPF instruction slots. Therefore, we have to fill in the array entry for the second half of the 64-bit load immediate instruction rather than for the one for the beginning of that instruction. Fixes: 7a12b5031c6b ("sparc64: Add eBPF JIT.") Signed-off-by: David S. Miller --- arch/sparc/net/bpf_jit_comp_64.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/sparc/net/bpf_jit_comp_64.c b/arch/sparc/net/bpf_jit_comp_64.c index ec7d10da94f0..21de77419f48 100644 --- a/arch/sparc/net/bpf_jit_comp_64.c +++ b/arch/sparc/net/bpf_jit_comp_64.c @@ -1446,12 +1446,13 @@ static int build_body(struct jit_ctx *ctx) int ret; ret = build_insn(insn, ctx); - ctx->offset[i] = ctx->idx; if (ret > 0) { i++; + ctx->offset[i] = ctx->idx; continue; } + ctx->offset[i] = ctx->idx; if (ret) return ret; } -- cgit From eb6211d3606971a957fea28f7532687f9d0f93f2 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 2 May 2017 00:47:09 +0200 Subject: bpf, samples: fix build warning in cookie_uid_helper_example Fix the following warnings triggered by 51570a5ab2b7 ("A Sample of using socket cookie and uid for traffic monitoring"): In file included from /home/foo/net-next/samples/bpf/cookie_uid_helper_example.c:54:0: /home/foo/net-next/samples/bpf/cookie_uid_helper_example.c: In function 'prog_load': /home/foo/net-next/samples/bpf/cookie_uid_helper_example.c:119:27: warning: overflow in implicit constant conversion [-Woverflow] -32 + offsetof(struct stats, uid)), ^ /home/foo/net-next/samples/bpf/libbpf.h:135:12: note: in definition of macro 'BPF_STX_MEM' .off = OFF, \ ^ /home/foo/net-next/samples/bpf/cookie_uid_helper_example.c:121:27: warning: overflow in implicit constant conversion [-Woverflow] -32 + offsetof(struct stats, packets), 1), ^ /home/foo/net-next/samples/bpf/libbpf.h:155:12: note: in definition of macro 'BPF_ST_MEM' .off = OFF, \ ^ /home/foo/net-next/samples/bpf/cookie_uid_helper_example.c:129:27: warning: overflow in implicit constant conversion [-Woverflow] -32 + offsetof(struct stats, bytes)), ^ /home/foo/net-next/samples/bpf/libbpf.h:135:12: note: in definition of macro 'BPF_STX_MEM' .off = OFF, \ ^ HOSTLD /home/foo/net-next/samples/bpf/per_socket_stats_example Fixes: 51570a5ab2b7 ("A Sample of using socket cookie and uid for traffic monitoring") Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- samples/bpf/cookie_uid_helper_example.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/bpf/cookie_uid_helper_example.c b/samples/bpf/cookie_uid_helper_example.c index 9ce55840d61d..b08ab4e88929 100644 --- a/samples/bpf/cookie_uid_helper_example.c +++ b/samples/bpf/cookie_uid_helper_example.c @@ -116,9 +116,9 @@ static void prog_load(void) * is set by directly place a IMM value 1 into the stack. */ BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, - -32 + offsetof(struct stats, uid)), + -32 + (__s16)offsetof(struct stats, uid)), BPF_ST_MEM(BPF_DW, BPF_REG_10, - -32 + offsetof(struct stats, packets), 1), + -32 + (__s16)offsetof(struct stats, packets), 1), /* * __sk_buff is a special struct used for eBPF program to * directly access some sk_buff field. @@ -126,7 +126,7 @@ static void prog_load(void) BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, offsetof(struct __sk_buff, len)), BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, - -32 + offsetof(struct stats, bytes)), + -32 + (__s16)offsetof(struct stats, bytes)), /* * add new map entry using BPF_FUNC_map_update_elem, it takes * 4 parameters (R1: map_fd, R2: &socket_cookie, R3: &stats, -- cgit From b5d60989c6f7501af72cb65893c02621dd16fd84 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 1 May 2017 15:53:43 -0700 Subject: xdp: fix parameter kdoc for extack Fix kdoc parameter spelling from extact to extack. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/dev.c b/net/core/dev.c index 35a06cebb282..0b2876e00834 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6854,7 +6854,7 @@ EXPORT_SYMBOL(dev_change_proto_down); /** * dev_change_xdp_fd - set or clear a bpf program for a device rx path * @dev: device - * @extact: netlink extended ack + * @extack: netlink extended ack * @fd: new program fd or negative value to clear * @flags: xdp-related flags * -- cgit From 4e9c3a667135799d50f2778a8a8dae2ca13aafd0 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 2 May 2017 07:52:01 -0700 Subject: selftests: bpf: Use bpf_endian.h in test_xdp.c This fixes the testcase on big-endian. Signed-off-by: David S. Miller --- tools/testing/selftests/bpf/test_xdp.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/bpf/test_xdp.c b/tools/testing/selftests/bpf/test_xdp.c index 9a33b038cb31..5e7df8bb5b5d 100644 --- a/tools/testing/selftests/bpf/test_xdp.c +++ b/tools/testing/selftests/bpf/test_xdp.c @@ -17,10 +17,9 @@ #include #include #include "bpf_helpers.h" +#include "bpf_endian.h" #include "test_iptunnel_common.h" -#define htons __builtin_bswap16 -#define ntohs __builtin_bswap16 int _version SEC("version") = 1; struct bpf_map_def SEC("maps") rxcnt = { @@ -104,7 +103,7 @@ static __always_inline int handle_ipv4(struct xdp_md *xdp) vip.family = AF_INET; vip.daddr.v4 = iph->daddr; vip.dport = dport; - payload_len = ntohs(iph->tot_len); + payload_len = bpf_ntohs(iph->tot_len); tnl = bpf_map_lookup_elem(&vip2tnl, &vip); /* It only does v4-in-v4 */ @@ -126,7 +125,7 @@ static __always_inline int handle_ipv4(struct xdp_md *xdp) iph + 1 > data_end) return XDP_DROP; - set_ethhdr(new_eth, old_eth, tnl, htons(ETH_P_IP)); + set_ethhdr(new_eth, old_eth, tnl, bpf_htons(ETH_P_IP)); iph->version = 4; iph->ihl = sizeof(*iph) >> 2; @@ -134,7 +133,7 @@ static __always_inline int handle_ipv4(struct xdp_md *xdp) iph->protocol = IPPROTO_IPIP; iph->check = 0; iph->tos = 0; - iph->tot_len = htons(payload_len + sizeof(*iph)); + iph->tot_len = bpf_htons(payload_len + sizeof(*iph)); iph->daddr = tnl->daddr.v4; iph->saddr = tnl->saddr.v4; iph->ttl = 8; @@ -195,12 +194,12 @@ static __always_inline int handle_ipv6(struct xdp_md *xdp) ip6h + 1 > data_end) return XDP_DROP; - set_ethhdr(new_eth, old_eth, tnl, htons(ETH_P_IPV6)); + set_ethhdr(new_eth, old_eth, tnl, bpf_htons(ETH_P_IPV6)); ip6h->version = 6; ip6h->priority = 0; memset(ip6h->flow_lbl, 0, sizeof(ip6h->flow_lbl)); - ip6h->payload_len = htons(ntohs(payload_len) + sizeof(*ip6h)); + ip6h->payload_len = bpf_htons(bpf_ntohs(payload_len) + sizeof(*ip6h)); ip6h->nexthdr = IPPROTO_IPV6; ip6h->hop_limit = 8; memcpy(ip6h->saddr.s6_addr32, tnl->saddr.v6, sizeof(tnl->saddr.v6)); @@ -224,9 +223,9 @@ int _xdp_tx_iptunnel(struct xdp_md *xdp) h_proto = eth->h_proto; - if (h_proto == htons(ETH_P_IP)) + if (h_proto == bpf_htons(ETH_P_IP)) return handle_ipv4(xdp); - else if (h_proto == htons(ETH_P_IPV6)) + else if (h_proto == bpf_htons(ETH_P_IPV6)) return handle_ipv6(xdp); else -- cgit From 78e5227237cae9172dd50c3ebb08d4fb31530676 Mon Sep 17 00:00:00 2001 From: David Miller Date: Tue, 2 May 2017 11:36:33 -0400 Subject: bpf: Do not dereference user pointer in bpf_test_finish(). Instead, pass the kattr in which has a kernel side copy of this data structure from userspace already. Fix based upon a suggestion from Alexei Starovoitov. Signed-off-by: David S. Miller Acked-by: Daniel Borkmann --- net/bpf/test_run.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 8a6d0a37c30c..f946912c8a81 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -49,10 +49,11 @@ static u32 bpf_test_run(struct bpf_prog *prog, void *ctx, u32 repeat, u32 *time) return ret; } -static int bpf_test_finish(union bpf_attr __user *uattr, const void *data, +static int bpf_test_finish(const union bpf_attr *kattr, + union bpf_attr __user *uattr, const void *data, u32 size, u32 retval, u32 duration) { - void __user *data_out = u64_to_user_ptr(uattr->test.data_out); + void __user *data_out = u64_to_user_ptr(kattr->test.data_out); int err = -EFAULT; if (data_out && copy_to_user(data_out, data, size)) @@ -140,7 +141,7 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, /* bpf program can never convert linear skb to non-linear */ if (WARN_ON_ONCE(skb_is_nonlinear(skb))) size = skb_headlen(skb); - ret = bpf_test_finish(uattr, skb->data, size, retval, duration); + ret = bpf_test_finish(kattr, uattr, skb->data, size, retval, duration); kfree_skb(skb); return ret; } @@ -166,7 +167,7 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, retval = bpf_test_run(prog, &xdp, repeat, &duration); if (xdp.data != data + XDP_PACKET_HEADROOM) size = xdp.data_end - xdp.data; - ret = bpf_test_finish(uattr, xdp.data, size, retval, duration); + ret = bpf_test_finish(kattr, uattr, xdp.data, size, retval, duration); kfree(data); return ret; } -- cgit From 586f8525979ad9574bf61637fd58c98d5077f29d Mon Sep 17 00:00:00 2001 From: David Miller Date: Tue, 2 May 2017 11:36:45 -0400 Subject: bpf: Align packet data properly in program testing framework. Make sure we apply NET_IP_ALIGN when reserving headroom for SKB and XDP test runs, just like a real driver would. Signed-off-by: David S. Miller Acked-by: Daniel Borkmann --- net/bpf/test_run.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index f946912c8a81..6be41a44d688 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -100,7 +100,7 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, void *data; int ret; - data = bpf_test_init(kattr, size, NET_SKB_PAD, + data = bpf_test_init(kattr, size, NET_SKB_PAD + NET_IP_ALIGN, SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); if (IS_ERR(data)) return PTR_ERR(data); @@ -125,7 +125,7 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, return -ENOMEM; } - skb_reserve(skb, NET_SKB_PAD); + skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN); __skb_put(skb, size); skb->protocol = eth_type_trans(skb, current->nsproxy->net_ns->loopback_dev); skb_reset_network_header(skb); @@ -156,16 +156,16 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, void *data; int ret; - data = bpf_test_init(kattr, size, XDP_PACKET_HEADROOM, 0); + data = bpf_test_init(kattr, size, XDP_PACKET_HEADROOM + NET_IP_ALIGN, 0); if (IS_ERR(data)) return PTR_ERR(data); xdp.data_hard_start = data; - xdp.data = data + XDP_PACKET_HEADROOM; + xdp.data = data + XDP_PACKET_HEADROOM + NET_IP_ALIGN; xdp.data_end = xdp.data + size; retval = bpf_test_run(prog, &xdp, repeat, &duration); - if (xdp.data != data + XDP_PACKET_HEADROOM) + if (xdp.data != data + XDP_PACKET_HEADROOM + NET_IP_ALIGN) size = xdp.data_end - xdp.data; ret = bpf_test_finish(kattr, uattr, xdp.data, size, retval, duration); kfree(data); -- cgit From 85f68fe89832057584a9e66e1e7e53d53e50faff Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 1 May 2017 02:57:20 +0200 Subject: bpf, arm64: implement jiting of BPF_XADD This work adds BPF_XADD for BPF_W/BPF_DW to the arm64 JIT and therefore completes JITing of all BPF instructions, meaning we can thus also remove the 'notyet' label and do not need to fall back to the interpreter when BPF_XADD is used in a program! This now also brings arm64 JIT in line with x86_64, s390x, ppc64, sparc64, where all current eBPF features are supported. BPF_W example from test_bpf: .u.insns_int = { BPF_ALU32_IMM(BPF_MOV, R0, 0x12), BPF_ST_MEM(BPF_W, R10, -40, 0x10), BPF_STX_XADD(BPF_W, R10, R0, -40), BPF_LDX_MEM(BPF_W, R0, R10, -40), BPF_EXIT_INSN(), }, [...] 00000020: 52800247 mov w7, #0x12 // #18 00000024: 928004eb mov x11, #0xffffffffffffffd8 // #-40 00000028: d280020a mov x10, #0x10 // #16 0000002c: b82b6b2a str w10, [x25,x11] // start of xadd mapping: 00000030: 928004ea mov x10, #0xffffffffffffffd8 // #-40 00000034: 8b19014a add x10, x10, x25 00000038: f9800151 prfm pstl1strm, [x10] 0000003c: 885f7d4b ldxr w11, [x10] 00000040: 0b07016b add w11, w11, w7 00000044: 880b7d4b stxr w11, w11, [x10] 00000048: 35ffffab cbnz w11, 0x0000003c // end of xadd mapping: [...] BPF_DW example from test_bpf: .u.insns_int = { BPF_ALU32_IMM(BPF_MOV, R0, 0x12), BPF_ST_MEM(BPF_DW, R10, -40, 0x10), BPF_STX_XADD(BPF_DW, R10, R0, -40), BPF_LDX_MEM(BPF_DW, R0, R10, -40), BPF_EXIT_INSN(), }, [...] 00000020: 52800247 mov w7, #0x12 // #18 00000024: 928004eb mov x11, #0xffffffffffffffd8 // #-40 00000028: d280020a mov x10, #0x10 // #16 0000002c: f82b6b2a str x10, [x25,x11] // start of xadd mapping: 00000030: 928004ea mov x10, #0xffffffffffffffd8 // #-40 00000034: 8b19014a add x10, x10, x25 00000038: f9800151 prfm pstl1strm, [x10] 0000003c: c85f7d4b ldxr x11, [x10] 00000040: 8b07016b add x11, x11, x7 00000044: c80b7d4b stxr w11, x11, [x10] 00000048: 35ffffab cbnz w11, 0x0000003c // end of xadd mapping: [...] Tested on Cavium ThunderX ARMv8, test suite results after the patch: No JIT: [ 3751.855362] test_bpf: Summary: 311 PASSED, 0 FAILED, [0/303 JIT'ed] With JIT: [ 3573.759527] test_bpf: Summary: 311 PASSED, 0 FAILED, [303/303 JIT'ed] Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- arch/arm64/include/asm/insn.h | 30 ++++++++++++ arch/arm64/kernel/insn.c | 106 ++++++++++++++++++++++++++++++++++++++++++ arch/arm64/net/bpf_jit.h | 19 ++++++++ arch/arm64/net/bpf_jit_comp.c | 16 +++++-- lib/test_bpf.c | 105 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 271 insertions(+), 5 deletions(-) diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index aecc07e09a18..29cb2ca756f6 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h @@ -80,6 +80,7 @@ enum aarch64_insn_register_type { AARCH64_INSN_REGTYPE_RM, AARCH64_INSN_REGTYPE_RD, AARCH64_INSN_REGTYPE_RA, + AARCH64_INSN_REGTYPE_RS, }; enum aarch64_insn_register { @@ -188,6 +189,8 @@ enum aarch64_insn_ldst_type { AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX, AARCH64_INSN_LDST_LOAD_PAIR_POST_INDEX, AARCH64_INSN_LDST_STORE_PAIR_POST_INDEX, + AARCH64_INSN_LDST_LOAD_EX, + AARCH64_INSN_LDST_STORE_EX, }; enum aarch64_insn_adsb_type { @@ -240,6 +243,23 @@ enum aarch64_insn_logic_type { AARCH64_INSN_LOGIC_BIC_SETFLAGS }; +enum aarch64_insn_prfm_type { + AARCH64_INSN_PRFM_TYPE_PLD, + AARCH64_INSN_PRFM_TYPE_PLI, + AARCH64_INSN_PRFM_TYPE_PST, +}; + +enum aarch64_insn_prfm_target { + AARCH64_INSN_PRFM_TARGET_L1, + AARCH64_INSN_PRFM_TARGET_L2, + AARCH64_INSN_PRFM_TARGET_L3, +}; + +enum aarch64_insn_prfm_policy { + AARCH64_INSN_PRFM_POLICY_KEEP, + AARCH64_INSN_PRFM_POLICY_STRM, +}; + #define __AARCH64_INSN_FUNCS(abbr, mask, val) \ static __always_inline bool aarch64_insn_is_##abbr(u32 code) \ { return (code & (mask)) == (val); } \ @@ -248,6 +268,7 @@ static __always_inline u32 aarch64_insn_get_##abbr##_value(void) \ __AARCH64_INSN_FUNCS(adr, 0x9F000000, 0x10000000) __AARCH64_INSN_FUNCS(adrp, 0x9F000000, 0x90000000) +__AARCH64_INSN_FUNCS(prfm, 0x3FC00000, 0x39800000) __AARCH64_INSN_FUNCS(prfm_lit, 0xFF000000, 0xD8000000) __AARCH64_INSN_FUNCS(str_reg, 0x3FE0EC00, 0x38206800) __AARCH64_INSN_FUNCS(ldr_reg, 0x3FE0EC00, 0x38606800) @@ -357,6 +378,11 @@ u32 aarch64_insn_gen_load_store_pair(enum aarch64_insn_register reg1, int offset, enum aarch64_insn_variant variant, enum aarch64_insn_ldst_type type); +u32 aarch64_insn_gen_load_store_ex(enum aarch64_insn_register reg, + enum aarch64_insn_register base, + enum aarch64_insn_register state, + enum aarch64_insn_size_type size, + enum aarch64_insn_ldst_type type); u32 aarch64_insn_gen_add_sub_imm(enum aarch64_insn_register dst, enum aarch64_insn_register src, int imm, enum aarch64_insn_variant variant, @@ -397,6 +423,10 @@ u32 aarch64_insn_gen_logical_shifted_reg(enum aarch64_insn_register dst, int shift, enum aarch64_insn_variant variant, enum aarch64_insn_logic_type type); +u32 aarch64_insn_gen_prefetch(enum aarch64_insn_register base, + enum aarch64_insn_prfm_type type, + enum aarch64_insn_prfm_target target, + enum aarch64_insn_prfm_policy policy); s32 aarch64_get_branch_offset(u32 insn); u32 aarch64_set_branch_offset(u32 insn, s32 offset); diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c index 3a63954a8b14..b884a926a632 100644 --- a/arch/arm64/kernel/insn.c +++ b/arch/arm64/kernel/insn.c @@ -474,6 +474,7 @@ static u32 aarch64_insn_encode_register(enum aarch64_insn_register_type type, shift = 10; break; case AARCH64_INSN_REGTYPE_RM: + case AARCH64_INSN_REGTYPE_RS: shift = 16; break; default: @@ -757,6 +758,111 @@ u32 aarch64_insn_gen_load_store_pair(enum aarch64_insn_register reg1, offset >> shift); } +u32 aarch64_insn_gen_load_store_ex(enum aarch64_insn_register reg, + enum aarch64_insn_register base, + enum aarch64_insn_register state, + enum aarch64_insn_size_type size, + enum aarch64_insn_ldst_type type) +{ + u32 insn; + + switch (type) { + case AARCH64_INSN_LDST_LOAD_EX: + insn = aarch64_insn_get_load_ex_value(); + break; + case AARCH64_INSN_LDST_STORE_EX: + insn = aarch64_insn_get_store_ex_value(); + break; + default: + pr_err("%s: unknown load/store exclusive encoding %d\n", __func__, type); + return AARCH64_BREAK_FAULT; + } + + insn = aarch64_insn_encode_ldst_size(size, insn); + + insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, + reg); + + insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, + base); + + insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT2, insn, + AARCH64_INSN_REG_ZR); + + return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RS, insn, + state); +} + +static u32 aarch64_insn_encode_prfm_imm(enum aarch64_insn_prfm_type type, + enum aarch64_insn_prfm_target target, + enum aarch64_insn_prfm_policy policy, + u32 insn) +{ + u32 imm_type = 0, imm_target = 0, imm_policy = 0; + + switch (type) { + case AARCH64_INSN_PRFM_TYPE_PLD: + break; + case AARCH64_INSN_PRFM_TYPE_PLI: + imm_type = BIT(0); + break; + case AARCH64_INSN_PRFM_TYPE_PST: + imm_type = BIT(1); + break; + default: + pr_err("%s: unknown prfm type encoding %d\n", __func__, type); + return AARCH64_BREAK_FAULT; + } + + switch (target) { + case AARCH64_INSN_PRFM_TARGET_L1: + break; + case AARCH64_INSN_PRFM_TARGET_L2: + imm_target = BIT(0); + break; + case AARCH64_INSN_PRFM_TARGET_L3: + imm_target = BIT(1); + break; + default: + pr_err("%s: unknown prfm target encoding %d\n", __func__, target); + return AARCH64_BREAK_FAULT; + } + + switch (policy) { + case AARCH64_INSN_PRFM_POLICY_KEEP: + break; + case AARCH64_INSN_PRFM_POLICY_STRM: + imm_policy = BIT(0); + break; + default: + pr_err("%s: unknown prfm policy encoding %d\n", __func__, policy); + return AARCH64_BREAK_FAULT; + } + + /* In this case, imm5 is encoded into Rt field. */ + insn &= ~GENMASK(4, 0); + insn |= imm_policy | (imm_target << 1) | (imm_type << 3); + + return insn; +} + +u32 aarch64_insn_gen_prefetch(enum aarch64_insn_register base, + enum aarch64_insn_prfm_type type, + enum aarch64_insn_prfm_target target, + enum aarch64_insn_prfm_policy policy) +{ + u32 insn = aarch64_insn_get_prfm_value(); + + insn = aarch64_insn_encode_ldst_size(AARCH64_INSN_SIZE_64, insn); + + insn = aarch64_insn_encode_prfm_imm(type, target, policy, insn); + + insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, + base); + + return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_12, insn, 0); +} + u32 aarch64_insn_gen_add_sub_imm(enum aarch64_insn_register dst, enum aarch64_insn_register src, int imm, enum aarch64_insn_variant variant, diff --git a/arch/arm64/net/bpf_jit.h b/arch/arm64/net/bpf_jit.h index 7c16e547ccb2..b02a9268dfbf 100644 --- a/arch/arm64/net/bpf_jit.h +++ b/arch/arm64/net/bpf_jit.h @@ -83,6 +83,25 @@ /* Rt = Rn[0]; Rt2 = Rn[8]; Rn += 16; */ #define A64_POP(Rt, Rt2, Rn) A64_LS_PAIR(Rt, Rt2, Rn, 16, LOAD, POST_INDEX) +/* Load/store exclusive */ +#define A64_SIZE(sf) \ + ((sf) ? AARCH64_INSN_SIZE_64 : AARCH64_INSN_SIZE_32) +#define A64_LSX(sf, Rt, Rn, Rs, type) \ + aarch64_insn_gen_load_store_ex(Rt, Rn, Rs, A64_SIZE(sf), \ + AARCH64_INSN_LDST_##type) +/* Rt = [Rn]; (atomic) */ +#define A64_LDXR(sf, Rt, Rn) \ + A64_LSX(sf, Rt, Rn, A64_ZR, LOAD_EX) +/* [Rn] = Rt; (atomic) Rs = [state] */ +#define A64_STXR(sf, Rt, Rn, Rs) \ + A64_LSX(sf, Rt, Rn, Rs, STORE_EX) + +/* Prefetch */ +#define A64_PRFM(Rn, type, target, policy) \ + aarch64_insn_gen_prefetch(Rn, AARCH64_INSN_PRFM_TYPE_##type, \ + AARCH64_INSN_PRFM_TARGET_##target, \ + AARCH64_INSN_PRFM_POLICY_##policy) + /* Add/subtract (immediate) */ #define A64_ADDSUB_IMM(sf, Rd, Rn, imm12, type) \ aarch64_insn_gen_add_sub_imm(Rd, Rn, imm12, \ diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index 304736870dca..4f2b35130f3c 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -321,6 +321,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx) const s32 imm = insn->imm; const int i = insn - ctx->prog->insnsi; const bool is64 = BPF_CLASS(code) == BPF_ALU64; + const bool isdw = BPF_SIZE(code) == BPF_DW; u8 jmp_cond; s32 jmp_offset; @@ -681,7 +682,16 @@ emit_cond_jmp: case BPF_STX | BPF_XADD | BPF_W: /* STX XADD: lock *(u64 *)(dst + off) += src */ case BPF_STX | BPF_XADD | BPF_DW: - goto notyet; + emit_a64_mov_i(1, tmp, off, ctx); + emit(A64_ADD(1, tmp, tmp, dst), ctx); + emit(A64_PRFM(tmp, PST, L1, STRM), ctx); + emit(A64_LDXR(isdw, tmp2, tmp), ctx); + emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); + emit(A64_STXR(isdw, tmp2, tmp, tmp2), ctx); + jmp_offset = -3; + check_imm19(jmp_offset); + emit(A64_CBNZ(0, tmp2, jmp_offset), ctx); + break; /* R0 = ntohx(*(size *)(((struct sk_buff *)R6)->data + imm)) */ case BPF_LD | BPF_ABS | BPF_W: @@ -748,10 +758,6 @@ emit_cond_jmp: } break; } -notyet: - pr_info_once("*** NOT YET: opcode %02x ***\n", code); - return -EFAULT; - default: pr_err_once("unknown opcode %02x\n", code); return -EINVAL; diff --git a/lib/test_bpf.c b/lib/test_bpf.c index 0362da0b66c3..3a7730ca81be 100644 --- a/lib/test_bpf.c +++ b/lib/test_bpf.c @@ -434,6 +434,41 @@ loop: return 0; } +static int __bpf_fill_stxdw(struct bpf_test *self, int size) +{ + unsigned int len = BPF_MAXINSNS; + struct bpf_insn *insn; + int i; + + insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL); + if (!insn) + return -ENOMEM; + + insn[0] = BPF_ALU32_IMM(BPF_MOV, R0, 1); + insn[1] = BPF_ST_MEM(size, R10, -40, 42); + + for (i = 2; i < len - 2; i++) + insn[i] = BPF_STX_XADD(size, R10, R0, -40); + + insn[len - 2] = BPF_LDX_MEM(size, R0, R10, -40); + insn[len - 1] = BPF_EXIT_INSN(); + + self->u.ptr.insns = insn; + self->u.ptr.len = len; + + return 0; +} + +static int bpf_fill_stxw(struct bpf_test *self) +{ + return __bpf_fill_stxdw(self, BPF_W); +} + +static int bpf_fill_stxdw(struct bpf_test *self) +{ + return __bpf_fill_stxdw(self, BPF_DW); +} + static struct bpf_test tests[] = { { "TAX", @@ -4302,6 +4337,41 @@ static struct bpf_test tests[] = { { }, { { 0, 0x22 } }, }, + { + "STX_XADD_W: Test side-effects, r10: 0x12 + 0x10 = 0x22", + .u.insns_int = { + BPF_ALU64_REG(BPF_MOV, R1, R10), + BPF_ALU32_IMM(BPF_MOV, R0, 0x12), + BPF_ST_MEM(BPF_W, R10, -40, 0x10), + BPF_STX_XADD(BPF_W, R10, R0, -40), + BPF_ALU64_REG(BPF_MOV, R0, R10), + BPF_ALU64_REG(BPF_SUB, R0, R1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + }, + { + "STX_XADD_W: Test side-effects, r0: 0x12 + 0x10 = 0x22", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0x12), + BPF_ST_MEM(BPF_W, R10, -40, 0x10), + BPF_STX_XADD(BPF_W, R10, R0, -40), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0x12 } }, + }, + { + "STX_XADD_W: X + 1 + 1 + 1 + ...", + { }, + INTERNAL, + { }, + { { 0, 4134 } }, + .fill_helper = bpf_fill_stxw, + }, { "STX_XADD_DW: Test: 0x12 + 0x10 = 0x22", .u.insns_int = { @@ -4315,6 +4385,41 @@ static struct bpf_test tests[] = { { }, { { 0, 0x22 } }, }, + { + "STX_XADD_DW: Test side-effects, r10: 0x12 + 0x10 = 0x22", + .u.insns_int = { + BPF_ALU64_REG(BPF_MOV, R1, R10), + BPF_ALU32_IMM(BPF_MOV, R0, 0x12), + BPF_ST_MEM(BPF_DW, R10, -40, 0x10), + BPF_STX_XADD(BPF_DW, R10, R0, -40), + BPF_ALU64_REG(BPF_MOV, R0, R10), + BPF_ALU64_REG(BPF_SUB, R0, R1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0 } }, + }, + { + "STX_XADD_DW: Test side-effects, r0: 0x12 + 0x10 = 0x22", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0x12), + BPF_ST_MEM(BPF_DW, R10, -40, 0x10), + BPF_STX_XADD(BPF_DW, R10, R0, -40), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0x12 } }, + }, + { + "STX_XADD_DW: X + 1 + 1 + 1 + ...", + { }, + INTERNAL, + { }, + { { 0, 4134 } }, + .fill_helper = bpf_fill_stxdw, + }, /* BPF_JMP | BPF_EXIT */ { "JMP_EXIT", -- cgit From ddc665a4bb4b728b4e6ecec8db1b64efa9184b9c Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 2 May 2017 20:34:54 +0200 Subject: bpf, arm64: fix jit branch offset related to ldimm64 When the instruction right before the branch destination is a 64 bit load immediate, we currently calculate the wrong jump offset in the ctx->offset[] array as we only account one instruction slot for the 64 bit load immediate although it uses two BPF instructions. Fix it up by setting the offset into the right slot after we incremented the index. Before (ldimm64 test 1): [...] 00000020: 52800007 mov w7, #0x0 // #0 00000024: d2800060 mov x0, #0x3 // #3 00000028: d2800041 mov x1, #0x2 // #2 0000002c: eb01001f cmp x0, x1 00000030: 54ffff82 b.cs 0x00000020 00000034: d29fffe7 mov x7, #0xffff // #65535 00000038: f2bfffe7 movk x7, #0xffff, lsl #16 0000003c: f2dfffe7 movk x7, #0xffff, lsl #32 00000040: f2ffffe7 movk x7, #0xffff, lsl #48 00000044: d29dddc7 mov x7, #0xeeee // #61166 00000048: f2bdddc7 movk x7, #0xeeee, lsl #16 0000004c: f2ddddc7 movk x7, #0xeeee, lsl #32 00000050: f2fdddc7 movk x7, #0xeeee, lsl #48 [...] After (ldimm64 test 1): [...] 00000020: 52800007 mov w7, #0x0 // #0 00000024: d2800060 mov x0, #0x3 // #3 00000028: d2800041 mov x1, #0x2 // #2 0000002c: eb01001f cmp x0, x1 00000030: 540000a2 b.cs 0x00000044 00000034: d29fffe7 mov x7, #0xffff // #65535 00000038: f2bfffe7 movk x7, #0xffff, lsl #16 0000003c: f2dfffe7 movk x7, #0xffff, lsl #32 00000040: f2ffffe7 movk x7, #0xffff, lsl #48 00000044: d29dddc7 mov x7, #0xeeee // #61166 00000048: f2bdddc7 movk x7, #0xeeee, lsl #16 0000004c: f2ddddc7 movk x7, #0xeeee, lsl #32 00000050: f2fdddc7 movk x7, #0xeeee, lsl #48 [...] Also, add a couple of test cases to make sure JITs pass this test. Tested on Cavium ThunderX ARMv8. The added test cases all pass after the fix. Fixes: 8eee539ddea0 ("arm64: bpf: fix out-of-bounds read in bpf2a64_offset()") Reported-by: David S. Miller Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Cc: Xi Wang Signed-off-by: David S. Miller --- arch/arm64/net/bpf_jit_comp.c | 8 ++++---- lib/test_bpf.c | 45 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c index 4f2b35130f3c..d68abde52740 100644 --- a/arch/arm64/net/bpf_jit_comp.c +++ b/arch/arm64/net/bpf_jit_comp.c @@ -776,14 +776,14 @@ static int build_body(struct jit_ctx *ctx) int ret; ret = build_insn(insn, ctx); - - if (ctx->image == NULL) - ctx->offset[i] = ctx->idx; - if (ret > 0) { i++; + if (ctx->image == NULL) + ctx->offset[i] = ctx->idx; continue; } + if (ctx->image == NULL) + ctx->offset[i] = ctx->idx; if (ret) return ret; } diff --git a/lib/test_bpf.c b/lib/test_bpf.c index 3a7730ca81be..a0f66280ea50 100644 --- a/lib/test_bpf.c +++ b/lib/test_bpf.c @@ -4761,6 +4761,51 @@ static struct bpf_test tests[] = { { }, { { 0, 1 } }, }, + { + /* Mainly testing JIT + imm64 here. */ + "JMP_JGE_X: ldimm64 test 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 3), + BPF_LD_IMM64(R2, 2), + BPF_JMP_REG(BPF_JGE, R1, R2, 2), + BPF_LD_IMM64(R0, 0xffffffffffffffffUL), + BPF_LD_IMM64(R0, 0xeeeeeeeeeeeeeeeeUL), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0xeeeeeeeeU } }, + }, + { + "JMP_JGE_X: ldimm64 test 2", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 3), + BPF_LD_IMM64(R2, 2), + BPF_JMP_REG(BPF_JGE, R1, R2, 0), + BPF_LD_IMM64(R0, 0xffffffffffffffffUL), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0xffffffffU } }, + }, + { + "JMP_JGE_X: ldimm64 test 3", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_LD_IMM64(R1, 3), + BPF_LD_IMM64(R2, 2), + BPF_JMP_REG(BPF_JGE, R1, R2, 4), + BPF_LD_IMM64(R0, 0xffffffffffffffffUL), + BPF_LD_IMM64(R0, 0xeeeeeeeeeeeeeeeeUL), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, /* BPF_JMP | BPF_JNE | BPF_X */ { "JMP_JNE_X: if (3 != 2) return 1", -- cgit From a9f11f963a546fea9144f6a6d1a307e814a387e7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 1 May 2017 15:29:48 -0700 Subject: tcp: fix wraparound issue in tcp_lp Be careful when comparing tcp_time_stamp to some u32 quantity, otherwise result can be surprising. Fixes: 7c106d7e782b ("[TCP]: TCP Low Priority congestion control") Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_lp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp_lp.c b/net/ipv4/tcp_lp.c index 046fd3910873..d6fb6c067af4 100644 --- a/net/ipv4/tcp_lp.c +++ b/net/ipv4/tcp_lp.c @@ -264,13 +264,15 @@ static void tcp_lp_pkts_acked(struct sock *sk, const struct ack_sample *sample) { struct tcp_sock *tp = tcp_sk(sk); struct lp *lp = inet_csk_ca(sk); + u32 delta; if (sample->rtt_us > 0) tcp_lp_rtt_sample(sk, sample->rtt_us); /* calc inference */ - if (tcp_time_stamp > tp->rx_opt.rcv_tsecr) - lp->inference = 3 * (tcp_time_stamp - tp->rx_opt.rcv_tsecr); + delta = tcp_time_stamp - tp->rx_opt.rcv_tsecr; + if ((s32)delta > 0) + lp->inference = 3 * delta; /* test if within inference */ if (lp->last_drop && (tcp_time_stamp - lp->last_drop < lp->inference)) -- cgit From 412b65d15a7f8a93794653968308fc100f2aa87c Mon Sep 17 00:00:00 2001 From: Timmy Li Date: Tue, 2 May 2017 10:46:52 +0800 Subject: net: hns: fix ethtool_get_strings overflow in hns driver hns_get_sset_count() returns HNS_NET_STATS_CNT and the data space allocated is not enough for ethtool_get_strings(), which will cause random memory corruption. When SLAB and DEBUG_SLAB are both enabled, memory corruptions like the the following can be observed without this patch: [ 43.115200] Slab corruption (Not tainted): Acpi-ParseExt start=ffff801fb0b69030, len=80 [ 43.115206] Redzone: 0x9f911029d006462/0x5f78745f31657070. [ 43.115208] Last user: [<5f7272655f746b70>](0x5f7272655f746b70) [ 43.115214] 010: 70 70 65 31 5f 74 78 5f 70 6b 74 00 6b 6b 6b 6b ppe1_tx_pkt.kkkk [ 43.115217] 030: 70 70 65 31 5f 74 78 5f 70 6b 74 5f 6f 6b 00 6b ppe1_tx_pkt_ok.k [ 43.115218] Next obj: start=ffff801fb0b69098, len=80 [ 43.115220] Redzone: 0x706d655f6f666966/0x9f911029d74e35b. [ 43.115229] Last user: [](acpi_os_release_object+0x28/0x38) [ 43.115231] 000: 74 79 00 6b 6b 6b 6b 6b 70 70 65 31 5f 74 78 5f ty.kkkkkppe1_tx_ [ 43.115232] 010: 70 6b 74 5f 65 72 72 5f 63 73 75 6d 5f 66 61 69 pkt_err_csum_fai Signed-off-by: Timmy Li Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c | 2 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c | 2 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c | 2 +- drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c index 74bd260ca02a..86944bc3b273 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_gmac.c @@ -666,7 +666,7 @@ static void hns_gmac_get_strings(u32 stringset, u8 *data) static int hns_gmac_get_sset_count(int stringset) { - if (stringset == ETH_SS_STATS) + if (stringset == ETH_SS_STATS || stringset == ETH_SS_PRIV_FLAGS) return ARRAY_SIZE(g_gmac_stats_string); return 0; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c index 93e71e27401b..b62816c1574e 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c @@ -422,7 +422,7 @@ void hns_ppe_update_stats(struct hns_ppe_cb *ppe_cb) int hns_ppe_get_sset_count(int stringset) { - if (stringset == ETH_SS_STATS) + if (stringset == ETH_SS_STATS || stringset == ETH_SS_PRIV_FLAGS) return ETH_PPE_STATIC_NUM; return 0; } diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c index e2e28532e4dc..6f3570cfb501 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c @@ -876,7 +876,7 @@ void hns_rcb_get_stats(struct hnae_queue *queue, u64 *data) */ int hns_rcb_get_ring_sset_count(int stringset) { - if (stringset == ETH_SS_STATS) + if (stringset == ETH_SS_STATS || stringset == ETH_SS_PRIV_FLAGS) return HNS_RING_STATIC_REG_NUM; return 0; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c index 37a2fc35148f..51e7e9f5af49 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_xgmac.c @@ -781,7 +781,7 @@ static void hns_xgmac_get_strings(u32 stringset, u8 *data) */ static int hns_xgmac_get_sset_count(int stringset) { - if (stringset == ETH_SS_STATS) + if (stringset == ETH_SS_STATS || stringset == ETH_SS_PRIV_FLAGS) return ARRAY_SIZE(g_xgmac_stats_string); return 0; -- cgit From 212c7fd614377fef4415d94856a59e9f484aa439 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 2 May 2017 09:58:00 +0200 Subject: stmmac: Add support for SIMATIC IOT2000 platform The IOT2000 is industrial controller platform, derived from the Intel Galileo Gen2 board. The variant IOT2020 comes with one LAN port, the IOT2040 has two of them. They can be told apart based on the board asset tag in the DMI table. Based on patch by Sascha Weisenberger. Signed-off-by: Jan Kiszka Signed-off-by: Sascha Weisenberger Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c | 26 +++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c index a224d7bf1c1b..39be96779145 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c @@ -32,6 +32,7 @@ */ struct stmmac_pci_dmi_data { const char *name; + const char *asset_tag; unsigned int func; int phy_addr; }; @@ -46,6 +47,7 @@ struct stmmac_pci_info { static int stmmac_pci_find_phy_addr(struct stmmac_pci_info *info) { const char *name = dmi_get_system_info(DMI_BOARD_NAME); + const char *asset_tag = dmi_get_system_info(DMI_BOARD_ASSET_TAG); unsigned int func = PCI_FUNC(info->pdev->devfn); struct stmmac_pci_dmi_data *dmi; @@ -57,8 +59,12 @@ static int stmmac_pci_find_phy_addr(struct stmmac_pci_info *info) return 1; for (dmi = info->dmi; dmi->name && *dmi->name; dmi++) { - if (!strcmp(dmi->name, name) && dmi->func == func) + if (!strcmp(dmi->name, name) && dmi->func == func) { + /* If asset tag is provided, match on it as well. */ + if (dmi->asset_tag && strcmp(dmi->asset_tag, asset_tag)) + continue; return dmi->phy_addr; + } } return -ENODEV; @@ -153,6 +159,24 @@ static struct stmmac_pci_dmi_data quark_pci_dmi_data[] = { .func = 6, .phy_addr = 1, }, + { + .name = "SIMATIC IOT2000", + .asset_tag = "6ES7647-0AA00-0YA2", + .func = 6, + .phy_addr = 1, + }, + { + .name = "SIMATIC IOT2000", + .asset_tag = "6ES7647-0AA00-1YA2", + .func = 6, + .phy_addr = 1, + }, + { + .name = "SIMATIC IOT2000", + .asset_tag = "6ES7647-0AA00-1YA2", + .func = 7, + .phy_addr = 1, + }, {} }; -- cgit From 461eec12012c29b66525c270208d30be8f6da8e7 Mon Sep 17 00:00:00 2001 From: "sudarsana.kalluru@cavium.com" Date: Tue, 2 May 2017 01:11:02 -0700 Subject: qede: Fix concurrency issue in PTP Tx path processing. PTP Tx timestamping data structures are not protected against the concurrent access in the Tx paths. Protecting the same using atomic bit locks. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qede/qede.h | 5 +++-- drivers/net/ethernet/qlogic/qede/qede_ptp.c | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index 766a79d2ed75..9b4f08b6f9b9 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -167,10 +167,11 @@ struct qede_dev { u32 dp_module; u8 dp_level; - u32 flags; -#define QEDE_FLAG_IS_VF BIT(0) + unsigned long flags; +#define QEDE_FLAG_IS_VF BIT(0) #define IS_VF(edev) (!!((edev)->flags & QEDE_FLAG_IS_VF)) #define QEDE_TX_TIMESTAMPING_EN BIT(1) +#define QEDE_FLAGS_PTP_TX_IN_PRORGESS BIT(2) const struct qed_eth_ops *ops; struct qede_ptp *ptp; diff --git a/drivers/net/ethernet/qlogic/qede/qede_ptp.c b/drivers/net/ethernet/qlogic/qede/qede_ptp.c index 6396363a804e..aa4b5e7bb8e1 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ptp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ptp.c @@ -181,6 +181,7 @@ static void qede_ptp_task(struct work_struct *work) skb_tstamp_tx(ptp->tx_skb, &shhwtstamps); dev_kfree_skb_any(ptp->tx_skb); ptp->tx_skb = NULL; + clear_bit_unlock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, &edev->flags); DP_VERBOSE(edev, QED_MSG_DEBUG, "Tx timestamp, timestamp cycles = %llu, ns = %llu\n", @@ -485,6 +486,9 @@ void qede_ptp_tx_ts(struct qede_dev *edev, struct sk_buff *skb) if (!ptp) return; + if (test_and_set_bit_lock(QEDE_FLAGS_PTP_TX_IN_PRORGESS, &edev->flags)) + return; + if (unlikely(!(edev->flags & QEDE_TX_TIMESTAMPING_EN))) { DP_NOTICE(edev, "Tx timestamping was not enabled, this packet will not be timestamped\n"); -- cgit From 8d3f87d8cd0a16c58ae7e4410938528866c1c0db Mon Sep 17 00:00:00 2001 From: "sudarsana.kalluru@cavium.com" Date: Tue, 2 May 2017 01:11:03 -0700 Subject: qed*: Fix issues in the ptp filter config implementation. PTP hardware filter configuration performed by the driver for a given user requested config is not correct for some of the PTP modes. Following changes are needed for PTP config-filter implementation. 1. NIG_REG_TX_PTP_EN register - Bits 0/1/2 respectively enables TimeSync/"V1 frame format support"/"V2 frame format support" on the TX side. Set the associated bits based on the user request. 2. ptp4l application fails to operate in Peer Delay mode. Following changes are needed to fix this, a. Driver should enable (set to 0) DA #1-related bits for IPv4, IPv6 and MAC destination addresses in these registers: NIG_REG_TX_LLH_PTP_RULE_MASK NIG_REG_LLH_PTP_RULE_MASK b. NIG_REG_LLH_PTP_PARAM_MASK/NIG_REG_TX_LLH_PTP_PARAM_MASK should be set to 0x0 in all modes. Signed-off-by: Sudarsana Reddy Kalluru Signed-off-by: Yuval Mintz Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_ptp.c | 84 ++++++++++++++++++----------- drivers/net/ethernet/qlogic/qede/qede_ptp.c | 34 +++++++++--- include/linux/qed/qed_eth_if.h | 23 +++++--- 3 files changed, 98 insertions(+), 43 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_ptp.c b/drivers/net/ethernet/qlogic/qed/qed_ptp.c index 1871ebfdb793..434a164a76ed 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ptp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ptp.c @@ -188,39 +188,73 @@ static int qed_ptp_hw_read_cc(struct qed_dev *cdev, u64 *phc_cycles) } /* Filter PTP protocol packets that need to be timestamped */ -static int qed_ptp_hw_cfg_rx_filters(struct qed_dev *cdev, - enum qed_ptp_filter_type type) +static int qed_ptp_hw_cfg_filters(struct qed_dev *cdev, + enum qed_ptp_filter_type rx_type, + enum qed_ptp_hwtstamp_tx_type tx_type) { struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt; - u32 rule_mask, parm_mask; + u32 rule_mask, enable_cfg = 0x0; - switch (type) { - case QED_PTP_FILTER_L2_IPV4_IPV6: - parm_mask = 0x6AA; - rule_mask = 0x3EEE; + switch (rx_type) { + case QED_PTP_FILTER_NONE: + enable_cfg = 0x0; + rule_mask = 0x3FFF; break; - case QED_PTP_FILTER_L2: - parm_mask = 0x6BF; - rule_mask = 0x3EFF; + case QED_PTP_FILTER_ALL: + enable_cfg = 0x7; + rule_mask = 0x3CAA; break; - case QED_PTP_FILTER_IPV4_IPV6: - parm_mask = 0x7EA; - rule_mask = 0x3FFE; + case QED_PTP_FILTER_V1_L4_EVENT: + enable_cfg = 0x3; + rule_mask = 0x3FFA; break; - case QED_PTP_FILTER_IPV4: - parm_mask = 0x7EE; + case QED_PTP_FILTER_V1_L4_GEN: + enable_cfg = 0x3; rule_mask = 0x3FFE; break; + case QED_PTP_FILTER_V2_L4_EVENT: + enable_cfg = 0x5; + rule_mask = 0x3FAA; + break; + case QED_PTP_FILTER_V2_L4_GEN: + enable_cfg = 0x5; + rule_mask = 0x3FEE; + break; + case QED_PTP_FILTER_V2_L2_EVENT: + enable_cfg = 0x5; + rule_mask = 0x3CFF; + break; + case QED_PTP_FILTER_V2_L2_GEN: + enable_cfg = 0x5; + rule_mask = 0x3EFF; + break; + case QED_PTP_FILTER_V2_EVENT: + enable_cfg = 0x5; + rule_mask = 0x3CAA; + break; + case QED_PTP_FILTER_V2_GEN: + enable_cfg = 0x5; + rule_mask = 0x3EEE; + break; default: - DP_INFO(p_hwfn, "Invalid PTP filter type %d\n", type); + DP_INFO(p_hwfn, "Invalid PTP filter type %d\n", rx_type); return -EINVAL; } - qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_PARAM_MASK, parm_mask); + qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_PARAM_MASK, 0); qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_RULE_MASK, rule_mask); + qed_wr(p_hwfn, p_ptt, NIG_REG_RX_PTP_EN, enable_cfg); - qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_TO_HOST, 0x1); + if (tx_type == QED_PTP_HWTSTAMP_TX_OFF) { + qed_wr(p_hwfn, p_ptt, NIG_REG_TX_PTP_EN, 0x0); + qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_PARAM_MASK, 0x7FF); + qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_RULE_MASK, 0x3FFF); + } else { + qed_wr(p_hwfn, p_ptt, NIG_REG_TX_PTP_EN, enable_cfg); + qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_PARAM_MASK, 0); + qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_RULE_MASK, rule_mask); + } /* Reset possibly old timestamps */ qed_wr(p_hwfn, p_ptt, NIG_REG_LLH_PTP_HOST_BUF_SEQID, @@ -383,17 +417,6 @@ static int qed_ptp_hw_enable(struct qed_dev *cdev) return 0; } -static int qed_ptp_hw_hwtstamp_tx_on(struct qed_dev *cdev) -{ - struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); - struct qed_ptt *p_ptt = p_hwfn->p_ptp_ptt; - - qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_PARAM_MASK, 0x6AA); - qed_wr(p_hwfn, p_ptt, NIG_REG_TX_LLH_PTP_RULE_MASK, 0x3EEE); - - return 0; -} - static int qed_ptp_hw_disable(struct qed_dev *cdev) { struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); @@ -419,8 +442,7 @@ static int qed_ptp_hw_disable(struct qed_dev *cdev) } const struct qed_eth_ptp_ops qed_ptp_ops_pass = { - .hwtstamp_tx_on = qed_ptp_hw_hwtstamp_tx_on, - .cfg_rx_filters = qed_ptp_hw_cfg_rx_filters, + .cfg_filters = qed_ptp_hw_cfg_filters, .read_rx_ts = qed_ptp_hw_read_rx_ts, .read_tx_ts = qed_ptp_hw_read_tx_ts, .read_cc = qed_ptp_hw_read_cc, diff --git a/drivers/net/ethernet/qlogic/qede/qede_ptp.c b/drivers/net/ethernet/qlogic/qede/qede_ptp.c index aa4b5e7bb8e1..24f06e2ef43e 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ptp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ptp.c @@ -209,6 +209,8 @@ static u64 qede_ptp_read_cc(const struct cyclecounter *cc) static int qede_ptp_cfg_filters(struct qede_dev *edev) { + enum qed_ptp_hwtstamp_tx_type tx_type = QED_PTP_HWTSTAMP_TX_ON; + enum qed_ptp_filter_type rx_filter = QED_PTP_FILTER_NONE; struct qede_ptp *ptp = edev->ptp; if (!ptp) @@ -222,7 +224,12 @@ static int qede_ptp_cfg_filters(struct qede_dev *edev) switch (ptp->tx_type) { case HWTSTAMP_TX_ON: edev->flags |= QEDE_TX_TIMESTAMPING_EN; - ptp->ops->hwtstamp_tx_on(edev->cdev); + tx_type = QED_PTP_HWTSTAMP_TX_ON; + break; + + case HWTSTAMP_TX_OFF: + edev->flags &= ~QEDE_TX_TIMESTAMPING_EN; + tx_type = QED_PTP_HWTSTAMP_TX_OFF; break; case HWTSTAMP_TX_ONESTEP_SYNC: @@ -233,42 +240,57 @@ static int qede_ptp_cfg_filters(struct qede_dev *edev) spin_lock_bh(&ptp->lock); switch (ptp->rx_filter) { case HWTSTAMP_FILTER_NONE: + rx_filter = QED_PTP_FILTER_NONE; break; case HWTSTAMP_FILTER_ALL: case HWTSTAMP_FILTER_SOME: ptp->rx_filter = HWTSTAMP_FILTER_NONE; + rx_filter = QED_PTP_FILTER_ALL; break; case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + ptp->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; + rx_filter = QED_PTP_FILTER_V1_L4_EVENT; + break; case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: ptp->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT; /* Initialize PTP detection for UDP/IPv4 events */ - ptp->ops->cfg_rx_filters(edev->cdev, QED_PTP_FILTER_IPV4); + rx_filter = QED_PTP_FILTER_V1_L4_GEN; break; case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; + rx_filter = QED_PTP_FILTER_V2_L4_EVENT; + break; case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT; /* Initialize PTP detection for UDP/IPv4 or UDP/IPv6 events */ - ptp->ops->cfg_rx_filters(edev->cdev, QED_PTP_FILTER_IPV4_IPV6); + rx_filter = QED_PTP_FILTER_V2_L4_GEN; break; case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; + rx_filter = QED_PTP_FILTER_V2_L2_EVENT; + break; case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; /* Initialize PTP detection L2 events */ - ptp->ops->cfg_rx_filters(edev->cdev, QED_PTP_FILTER_L2); + rx_filter = QED_PTP_FILTER_V2_L2_GEN; break; case HWTSTAMP_FILTER_PTP_V2_EVENT: + ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + rx_filter = QED_PTP_FILTER_V2_EVENT; + break; case HWTSTAMP_FILTER_PTP_V2_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: ptp->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; /* Initialize PTP detection L2, UDP/IPv4 or UDP/IPv6 events */ - ptp->ops->cfg_rx_filters(edev->cdev, - QED_PTP_FILTER_L2_IPV4_IPV6); + rx_filter = QED_PTP_FILTER_V2_GEN; break; } + ptp->ops->cfg_filters(edev->cdev, rx_filter, tx_type); + spin_unlock_bh(&ptp->lock); return 0; diff --git a/include/linux/qed/qed_eth_if.h b/include/linux/qed/qed_eth_if.h index 15fa7c6e4c6f..d66d16a559e1 100644 --- a/include/linux/qed/qed_eth_if.h +++ b/include/linux/qed/qed_eth_if.h @@ -164,10 +164,21 @@ struct qed_eth_cb_ops { #define QED_MAX_PHC_DRIFT_PPB 291666666 enum qed_ptp_filter_type { - QED_PTP_FILTER_L2, - QED_PTP_FILTER_IPV4, - QED_PTP_FILTER_IPV4_IPV6, - QED_PTP_FILTER_L2_IPV4_IPV6 + QED_PTP_FILTER_NONE, + QED_PTP_FILTER_ALL, + QED_PTP_FILTER_V1_L4_EVENT, + QED_PTP_FILTER_V1_L4_GEN, + QED_PTP_FILTER_V2_L4_EVENT, + QED_PTP_FILTER_V2_L4_GEN, + QED_PTP_FILTER_V2_L2_EVENT, + QED_PTP_FILTER_V2_L2_GEN, + QED_PTP_FILTER_V2_EVENT, + QED_PTP_FILTER_V2_GEN +}; + +enum qed_ptp_hwtstamp_tx_type { + QED_PTP_HWTSTAMP_TX_OFF, + QED_PTP_HWTSTAMP_TX_ON, }; #ifdef CONFIG_DCB @@ -230,8 +241,8 @@ struct qed_eth_dcbnl_ops { #endif struct qed_eth_ptp_ops { - int (*hwtstamp_tx_on)(struct qed_dev *); - int (*cfg_rx_filters)(struct qed_dev *, enum qed_ptp_filter_type); + int (*cfg_filters)(struct qed_dev *, enum qed_ptp_filter_type, + enum qed_ptp_hwtstamp_tx_type); int (*read_rx_ts)(struct qed_dev *, u64 *); int (*read_tx_ts)(struct qed_dev *, u64 *); int (*read_cc)(struct qed_dev *, u64 *); -- cgit From 9da3242e6a83b6f315aa9c394c939da8e4ad7774 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 2 May 2017 10:12:00 +0200 Subject: net: sched: add helpers to handle extended actions Jump is now the only one using value action opcode. This is going to change soon. So introduce helpers to work with this. Convert TC_ACT_JUMP. This also fixes the TC_ACT_JUMP check, which is incorrectly done as a bit check, not a value check. Fixes: e0ee84ded796 ("net sched actions: Complete the JUMPX opcode") Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/uapi/linux/pkt_cls.h | 15 ++++++++++++++- net/sched/act_api.c | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index f1129e383b2a..d613be3b3239 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -37,7 +37,20 @@ enum { #define TC_ACT_QUEUED 5 #define TC_ACT_REPEAT 6 #define TC_ACT_REDIRECT 7 -#define TC_ACT_JUMP 0x10000000 + +/* There is a special kind of actions called "extended actions", + * which need a value parameter. These have a local opcode located in + * the highest nibble, starting from 1. The rest of the bits + * are used to carry the value. These two parts together make + * a combined opcode. + */ +#define __TC_ACT_EXT_SHIFT 28 +#define __TC_ACT_EXT(local) ((local) << __TC_ACT_EXT_SHIFT) +#define TC_ACT_EXT_VAL_MASK ((1 << __TC_ACT_EXT_SHIFT) - 1) +#define TC_ACT_EXT_CMP(combined, opcode) \ + (((combined) & (~TC_ACT_EXT_VAL_MASK)) == opcode) + +#define TC_ACT_JUMP __TC_ACT_EXT(1) /* Action type identifiers*/ enum { diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 7f2cd702bb27..a90e8f355c00 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -453,7 +453,7 @@ repeat: if (ret == TC_ACT_REPEAT) goto repeat; /* we need a ttl - JHS */ - if (ret & TC_ACT_JUMP) { + if (TC_ACT_EXT_CMP(ret, TC_ACT_JUMP)) { jmp_prgcnt = ret & TCA_ACT_MAX_PRIO_MASK; if (!jmp_prgcnt || (jmp_prgcnt > nr_actions)) { /* faulty opcode, stop pipeline */ -- cgit From ee0d8d8482345ff97a75a7d747efc309f13b0d80 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 2 May 2017 13:58:53 +0300 Subject: ipx: call ipxitf_put() in ioctl error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We should call ipxitf_put() if the copy_to_user() fails. Reported-by: 李强 Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- net/ipx/af_ipx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index 8a9219ff2e77..fa31ef29e3fa 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -1168,11 +1168,10 @@ static int ipxitf_ioctl(unsigned int cmd, void __user *arg) sipx->sipx_network = ipxif->if_netnum; memcpy(sipx->sipx_node, ipxif->if_node, sizeof(sipx->sipx_node)); - rc = -EFAULT; + rc = 0; if (copy_to_user(arg, &ifr, sizeof(ifr))) - break; + rc = -EFAULT; ipxitf_put(ipxif); - rc = 0; break; } case SIOCAIPXITFCRT: -- cgit From 5836b4429777bf57ca8fc02b154263aa54d97508 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Tue, 2 May 2017 18:36:50 +0530 Subject: net: thunderx: Support for page recycling Adds support for page recycling for allocating receive buffers to reduce cost of refilling RBDR ring. Also got rid of using compound pages when pagesize is 4K, only order-0 pages now. Only page is recycled, DMA mappings still needs to be done for every receive buffer allocated due to following constraints - Cannot have just one receive buffer per 64KB page. - There is just one buffer ring shared across 8 Rx queues, so buffers of same page can go to any Rx queue. - HW gives buffer address where packet has been DMA'ed and not the index into buffer ring. This makes it not possible to resue DMA mapping info. So unfortunately have to go through costly mapping route for every buffer. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nic.h | 4 +- .../net/ethernet/cavium/thunder/nicvf_ethtool.c | 3 +- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 121 ++++++++++++++++++--- drivers/net/ethernet/cavium/thunder/nicvf_queues.h | 11 ++ 4 files changed, 119 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h index 6fb44218bf55..dca6aed49094 100644 --- a/drivers/net/ethernet/cavium/thunder/nic.h +++ b/drivers/net/ethernet/cavium/thunder/nic.h @@ -252,12 +252,14 @@ struct nicvf_drv_stats { u64 tx_csum_overflow; /* driver debug stats */ - u64 rcv_buffer_alloc_failures; u64 tx_tso; u64 tx_timeout; u64 txq_stop; u64 txq_wake; + u64 rcv_buffer_alloc_failures; + u64 page_alloc; + struct u64_stats_sync syncp; }; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c index 02a986cdbb39..a89db5f3e26e 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c @@ -100,11 +100,12 @@ static const struct nicvf_stat nicvf_drv_stats[] = { NICVF_DRV_STAT(tx_csum_overlap), NICVF_DRV_STAT(tx_csum_overflow), - NICVF_DRV_STAT(rcv_buffer_alloc_failures), NICVF_DRV_STAT(tx_tso), NICVF_DRV_STAT(tx_timeout), NICVF_DRV_STAT(txq_stop), NICVF_DRV_STAT(txq_wake), + NICVF_DRV_STAT(rcv_buffer_alloc_failures), + NICVF_DRV_STAT(page_alloc), }; static const struct nicvf_stat nicvf_queue_stats[] = { diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 7b0fd8d871cc..12f9709bb180 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -19,8 +19,6 @@ #include "q_struct.h" #include "nicvf_queues.h" -#define NICVF_PAGE_ORDER ((PAGE_SIZE <= 4096) ? PAGE_ALLOC_COSTLY_ORDER : 0) - static inline u64 nicvf_iova_to_phys(struct nicvf *nic, dma_addr_t dma_addr) { /* Translation is installed only when IOMMU is present */ @@ -90,33 +88,88 @@ static void nicvf_free_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem) dmem->base = NULL; } -/* Allocate buffer for packet reception - * HW returns memory address where packet is DMA'ed but not a pointer - * into RBDR ring, so save buffer address at the start of fragment and - * align the start address to a cache aligned address +/* Allocate a new page or recycle one if possible + * + * We cannot optimize dma mapping here, since + * 1. It's only one RBDR ring for 8 Rx queues. + * 2. CQE_RX gives address of the buffer where pkt has been DMA'ed + * and not idx into RBDR ring, so can't refer to saved info. + * 3. There are multiple receive buffers per page */ -static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, gfp_t gfp, - u32 buf_len, u64 **rbuf) +static struct pgcache *nicvf_alloc_page(struct nicvf *nic, + struct rbdr *rbdr, gfp_t gfp) { - int order = NICVF_PAGE_ORDER; + struct page *page = NULL; + struct pgcache *pgcache, *next; + + /* Check if page is already allocated */ + pgcache = &rbdr->pgcache[rbdr->pgidx]; + page = pgcache->page; + /* Check if page can be recycled */ + if (page && (page_ref_count(page) != 1)) + page = NULL; + + if (!page) { + page = alloc_pages(gfp | __GFP_COMP | __GFP_NOWARN, 0); + if (!page) + return NULL; + + this_cpu_inc(nic->pnicvf->drv_stats->page_alloc); + + /* Check for space */ + if (rbdr->pgalloc >= rbdr->pgcnt) { + /* Page can still be used */ + nic->rb_page = page; + return NULL; + } + + /* Save the page in page cache */ + pgcache->page = page; + rbdr->pgalloc++; + } + + /* Take extra page reference for recycling */ + page_ref_add(page, 1); + + rbdr->pgidx++; + rbdr->pgidx &= (rbdr->pgcnt - 1); + + /* Prefetch refcount of next page in page cache */ + next = &rbdr->pgcache[rbdr->pgidx]; + page = next->page; + if (page) + prefetch(&page->_refcount); + + return pgcache; +} + +/* Allocate buffer for packet reception */ +static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, struct rbdr *rbdr, + gfp_t gfp, u32 buf_len, u64 **rbuf) +{ + struct pgcache *pgcache = NULL; /* Check if request can be accomodated in previous allocated page */ if (nic->rb_page && - ((nic->rb_page_offset + buf_len) < (PAGE_SIZE << order))) { + ((nic->rb_page_offset + buf_len) <= PAGE_SIZE)) { nic->rb_pageref++; goto ret; } nicvf_get_page(nic); + nic->rb_page = NULL; - /* Allocate a new page */ - nic->rb_page = alloc_pages(gfp | __GFP_COMP | __GFP_NOWARN, - order); - if (!nic->rb_page) { + /* Get new page, either recycled or new one */ + pgcache = nicvf_alloc_page(nic, rbdr, gfp); + if (!pgcache && !nic->rb_page) { this_cpu_inc(nic->pnicvf->drv_stats->rcv_buffer_alloc_failures); return -ENOMEM; } + nic->rb_page_offset = 0; + /* Check if it's recycled */ + if (pgcache) + nic->rb_page = pgcache->page; ret: /* HW will ensure data coherency, CPU sync not required */ *rbuf = (u64 *)((u64)dma_map_page_attrs(&nic->pdev->dev, nic->rb_page, @@ -125,7 +178,7 @@ ret: DMA_ATTR_SKIP_CPU_SYNC)); if (dma_mapping_error(&nic->pdev->dev, (dma_addr_t)*rbuf)) { if (!nic->rb_page_offset) - __free_pages(nic->rb_page, order); + __free_pages(nic->rb_page, 0); nic->rb_page = NULL; return -ENOMEM; } @@ -177,10 +230,26 @@ static int nicvf_init_rbdr(struct nicvf *nic, struct rbdr *rbdr, rbdr->head = 0; rbdr->tail = 0; + /* Initialize page recycling stuff. + * + * Can't use single buffer per page especially with 64K pages. + * On embedded platforms i.e 81xx/83xx available memory itself + * is low and minimum ring size of RBDR is 8K, that takes away + * lots of memory. + */ + rbdr->pgcnt = ring_len / (PAGE_SIZE / buf_size); + rbdr->pgcnt = roundup_pow_of_two(rbdr->pgcnt); + rbdr->pgcache = kzalloc(sizeof(*rbdr->pgcache) * + rbdr->pgcnt, GFP_KERNEL); + if (!rbdr->pgcache) + return -ENOMEM; + rbdr->pgidx = 0; + rbdr->pgalloc = 0; + nic->rb_page = NULL; for (idx = 0; idx < ring_len; idx++) { - err = nicvf_alloc_rcv_buffer(nic, GFP_KERNEL, RCV_FRAG_LEN, - &rbuf); + err = nicvf_alloc_rcv_buffer(nic, rbdr, GFP_KERNEL, + RCV_FRAG_LEN, &rbuf); if (err) { /* To free already allocated and mapped ones */ rbdr->tail = idx - 1; @@ -201,6 +270,7 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr) { int head, tail; u64 buf_addr, phys_addr; + struct pgcache *pgcache; struct rbdr_entry_t *desc; if (!rbdr) @@ -234,6 +304,18 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr) if (phys_addr) put_page(virt_to_page(phys_to_virt(phys_addr))); + /* Sync page cache info */ + smp_rmb(); + + /* Release additional page references held for recycling */ + head = 0; + while (head < rbdr->pgcnt) { + pgcache = &rbdr->pgcache[head]; + if (pgcache->page && page_ref_count(pgcache->page) != 0) + put_page(pgcache->page); + head++; + } + /* Free RBDR ring */ nicvf_free_q_desc_mem(nic, &rbdr->dmem); } @@ -269,13 +351,16 @@ refill: else refill_rb_cnt = qs->rbdr_len - qcount - 1; + /* Sync page cache info */ + smp_rmb(); + /* Start filling descs from tail */ tail = nicvf_queue_reg_read(nic, NIC_QSET_RBDR_0_1_TAIL, rbdr_idx) >> 3; while (refill_rb_cnt) { tail++; tail &= (rbdr->dmem.q_len - 1); - if (nicvf_alloc_rcv_buffer(nic, gfp, RCV_FRAG_LEN, &rbuf)) + if (nicvf_alloc_rcv_buffer(nic, rbdr, gfp, RCV_FRAG_LEN, &rbuf)) break; desc = GET_RBDR_DESC(rbdr, tail); diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h index 10cb4b84625b..da4836601d8c 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h @@ -213,6 +213,11 @@ struct q_desc_mem { void *unalign_base; }; +struct pgcache { + struct page *page; + u64 dma_addr; +}; + struct rbdr { bool enable; u32 dma_size; @@ -222,6 +227,12 @@ struct rbdr { u32 head; u32 tail; struct q_desc_mem dmem; + + /* For page recycling */ + int pgidx; + int pgcnt; + int pgalloc; + struct pgcache *pgcache; } ____cacheline_aligned_in_smp; struct rcv_queue { -- cgit From 5e848e4c5d77438e126c97702ec3bea477f550a9 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Tue, 2 May 2017 18:36:51 +0530 Subject: net: thunderx: Optimize RBDR descriptor handling Receive buffer's physical address or iova will anyway not go beyond 49bits, since it is the max supported HW address. As per perf, updating bitfields i.e buf_addr:42 in RBDR descriptor entry consumes lots of cpu cycles, hence changed it to a 64bit field with alignment requirements taken care of. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 8 ++++---- drivers/net/ethernet/cavium/thunder/q_struct.h | 10 +--------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 12f9709bb180..dfc85a169127 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -257,7 +257,7 @@ static int nicvf_init_rbdr(struct nicvf *nic, struct rbdr *rbdr, } desc = GET_RBDR_DESC(rbdr, idx); - desc->buf_addr = (u64)rbuf >> NICVF_RCV_BUF_ALIGN; + desc->buf_addr = (u64)rbuf & ~(NICVF_RCV_BUF_ALIGN_BYTES - 1); } nicvf_get_page(nic); @@ -286,7 +286,7 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr) /* Release page references */ while (head != tail) { desc = GET_RBDR_DESC(rbdr, head); - buf_addr = ((u64)desc->buf_addr) << NICVF_RCV_BUF_ALIGN; + buf_addr = desc->buf_addr; phys_addr = nicvf_iova_to_phys(nic, buf_addr); dma_unmap_page_attrs(&nic->pdev->dev, buf_addr, RCV_FRAG_LEN, DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); @@ -297,7 +297,7 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr) } /* Release buffer of tail desc */ desc = GET_RBDR_DESC(rbdr, tail); - buf_addr = ((u64)desc->buf_addr) << NICVF_RCV_BUF_ALIGN; + buf_addr = desc->buf_addr; phys_addr = nicvf_iova_to_phys(nic, buf_addr); dma_unmap_page_attrs(&nic->pdev->dev, buf_addr, RCV_FRAG_LEN, DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); @@ -364,7 +364,7 @@ refill: break; desc = GET_RBDR_DESC(rbdr, tail); - desc->buf_addr = (u64)rbuf >> NICVF_RCV_BUF_ALIGN; + desc->buf_addr = (u64)rbuf & ~(NICVF_RCV_BUF_ALIGN_BYTES - 1); refill_rb_cnt--; new_rb++; } diff --git a/drivers/net/ethernet/cavium/thunder/q_struct.h b/drivers/net/ethernet/cavium/thunder/q_struct.h index f36347237a54..e47205aa87ea 100644 --- a/drivers/net/ethernet/cavium/thunder/q_struct.h +++ b/drivers/net/ethernet/cavium/thunder/q_struct.h @@ -359,15 +359,7 @@ union cq_desc_t { }; struct rbdr_entry_t { -#if defined(__BIG_ENDIAN_BITFIELD) - u64 rsvd0:15; - u64 buf_addr:42; - u64 cache_align:7; -#elif defined(__LITTLE_ENDIAN_BITFIELD) - u64 cache_align:7; - u64 buf_addr:42; - u64 rsvd0:15; -#endif + u64 buf_addr; }; /* TCP reassembly context */ -- cgit From 0dada88b8cd74569abc3dda50f1b268a5868f6f2 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Tue, 2 May 2017 18:36:52 +0530 Subject: net: thunderx: Optimize CQE_TX handling Optimized CQE handling with below changes - Feeing descriptors back to SQ in bulk i.e once per NAPI instance instead for every CQE_TX, this will reduce number of atomic updates to 'sq->free_cnt'. - Checking errors in CQE_TX and CQE_RX before calling appropriate fn()s to update error stats i.e reduce branching. Also removed debug messages in packet handling path which otherwise causes issues if DEBUG is enabled. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nicvf_main.c | 44 +++++++++++----------- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 5 --- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 81a2fcb3cb1b..0d79894400ab 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -498,7 +498,7 @@ static int nicvf_init_resources(struct nicvf *nic) static void nicvf_snd_pkt_handler(struct net_device *netdev, struct cqe_send_t *cqe_tx, - int cqe_type, int budget, + int budget, int *subdesc_cnt, unsigned int *tx_pkts, unsigned int *tx_bytes) { struct sk_buff *skb = NULL; @@ -513,12 +513,10 @@ static void nicvf_snd_pkt_handler(struct net_device *netdev, if (hdr->subdesc_type != SQ_DESC_TYPE_HEADER) return; - netdev_dbg(nic->netdev, - "%s Qset #%d SQ #%d SQ ptr #%d subdesc count %d\n", - __func__, cqe_tx->sq_qs, cqe_tx->sq_idx, - cqe_tx->sqe_ptr, hdr->subdesc_cnt); + /* Check for errors */ + if (cqe_tx->send_status) + nicvf_check_cqe_tx_errs(nic->pnicvf, cqe_tx); - nicvf_check_cqe_tx_errs(nic, cqe_tx); skb = (struct sk_buff *)sq->skbuff[cqe_tx->sqe_ptr]; if (skb) { /* Check for dummy descriptor used for HW TSO offload on 88xx */ @@ -528,12 +526,12 @@ static void nicvf_snd_pkt_handler(struct net_device *netdev, (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, hdr->rsvd2); nicvf_unmap_sndq_buffers(nic, sq, hdr->rsvd2, tso_sqe->subdesc_cnt); - nicvf_put_sq_desc(sq, tso_sqe->subdesc_cnt + 1); + *subdesc_cnt += tso_sqe->subdesc_cnt + 1; } else { nicvf_unmap_sndq_buffers(nic, sq, cqe_tx->sqe_ptr, hdr->subdesc_cnt); } - nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1); + *subdesc_cnt += hdr->subdesc_cnt + 1; prefetch(skb); (*tx_pkts)++; *tx_bytes += skb->len; @@ -544,7 +542,7 @@ static void nicvf_snd_pkt_handler(struct net_device *netdev, * a SKB attached, so just free SQEs here. */ if (!nic->hw_tso) - nicvf_put_sq_desc(sq, hdr->subdesc_cnt + 1); + *subdesc_cnt += hdr->subdesc_cnt + 1; } } @@ -595,9 +593,11 @@ static void nicvf_rcv_pkt_handler(struct net_device *netdev, } /* Check for errors */ - err = nicvf_check_cqe_rx_errs(nic, cqe_rx); - if (err && !cqe_rx->rb_cnt) - return; + if (cqe_rx->err_level || cqe_rx->err_opcode) { + err = nicvf_check_cqe_rx_errs(nic, cqe_rx); + if (err && !cqe_rx->rb_cnt) + return; + } skb = nicvf_get_rcv_skb(snic, cqe_rx); if (!skb) { @@ -646,6 +646,7 @@ static int nicvf_cq_intr_handler(struct net_device *netdev, u8 cq_idx, { int processed_cqe, work_done = 0, tx_done = 0; int cqe_count, cqe_head; + int subdesc_cnt = 0; struct nicvf *nic = netdev_priv(netdev); struct queue_set *qs = nic->qs; struct cmp_queue *cq = &qs->cq[cq_idx]; @@ -667,8 +668,6 @@ loop: cqe_head = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_HEAD, cq_idx) >> 9; cqe_head &= 0xFFFF; - netdev_dbg(nic->netdev, "%s CQ%d cqe_count %d cqe_head %d\n", - __func__, cq_idx, cqe_count, cqe_head); while (processed_cqe < cqe_count) { /* Get the CQ descriptor */ cq_desc = (struct cqe_rx_t *)GET_CQ_DESC(cq, cqe_head); @@ -682,17 +681,15 @@ loop: break; } - netdev_dbg(nic->netdev, "CQ%d cq_desc->cqe_type %d\n", - cq_idx, cq_desc->cqe_type); switch (cq_desc->cqe_type) { case CQE_TYPE_RX: nicvf_rcv_pkt_handler(netdev, napi, cq_desc); work_done++; break; case CQE_TYPE_SEND: - nicvf_snd_pkt_handler(netdev, - (void *)cq_desc, CQE_TYPE_SEND, - budget, &tx_pkts, &tx_bytes); + nicvf_snd_pkt_handler(netdev, (void *)cq_desc, + budget, &subdesc_cnt, + &tx_pkts, &tx_bytes); tx_done++; break; case CQE_TYPE_INVALID: @@ -704,9 +701,6 @@ loop: } processed_cqe++; } - netdev_dbg(nic->netdev, - "%s CQ%d processed_cqe %d work_done %d budget %d\n", - __func__, cq_idx, processed_cqe, work_done, budget); /* Ring doorbell to inform H/W to reuse processed CQEs */ nicvf_queue_reg_write(nic, NIC_QSET_CQ_0_7_DOOR, @@ -716,8 +710,12 @@ loop: goto loop; done: - /* Wakeup TXQ if its stopped earlier due to SQ full */ sq = &nic->qs->sq[cq_idx]; + /* Update SQ's descriptor free count */ + if (subdesc_cnt) + nicvf_put_sq_desc(sq, subdesc_cnt); + + /* Wakeup TXQ if its stopped earlier due to SQ full */ if (tx_done || (atomic_read(&sq->free_cnt) >= MIN_SQ_DESC_PER_PKT_XMIT)) { netdev = nic->pnicvf->netdev; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index dfc85a169127..90c5bc7d7344 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -1640,9 +1640,6 @@ void nicvf_update_sq_stats(struct nicvf *nic, int sq_idx) /* Check for errors in the receive cmp.queue entry */ int nicvf_check_cqe_rx_errs(struct nicvf *nic, struct cqe_rx_t *cqe_rx) { - if (!cqe_rx->err_level && !cqe_rx->err_opcode) - return 0; - if (netif_msg_rx_err(nic)) netdev_err(nic->netdev, "%s: RX error CQE err_level 0x%x err_opcode 0x%x\n", @@ -1731,8 +1728,6 @@ int nicvf_check_cqe_rx_errs(struct nicvf *nic, struct cqe_rx_t *cqe_rx) int nicvf_check_cqe_tx_errs(struct nicvf *nic, struct cqe_send_t *cqe_tx) { switch (cqe_tx->send_status) { - case CQ_TX_ERROP_GOOD: - return 0; case CQ_TX_ERROP_DESC_FAULT: this_cpu_inc(nic->drv_stats->tx_desc_fault); break; -- cgit From 927987f39f116db477fcd74ced2a2aea940e585c Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Tue, 2 May 2017 18:36:53 +0530 Subject: net: thunderx: Cleanup receive buffer allocation Get rid of unnecessary double pointer references and type casting in receive buffer allocation code. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 90c5bc7d7344..e4a02a96d4f0 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -145,7 +145,7 @@ static struct pgcache *nicvf_alloc_page(struct nicvf *nic, /* Allocate buffer for packet reception */ static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, struct rbdr *rbdr, - gfp_t gfp, u32 buf_len, u64 **rbuf) + gfp_t gfp, u32 buf_len, u64 *rbuf) { struct pgcache *pgcache = NULL; @@ -172,10 +172,10 @@ static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, struct rbdr *rbdr, nic->rb_page = pgcache->page; ret: /* HW will ensure data coherency, CPU sync not required */ - *rbuf = (u64 *)((u64)dma_map_page_attrs(&nic->pdev->dev, nic->rb_page, - nic->rb_page_offset, buf_len, - DMA_FROM_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC)); + *rbuf = (u64)dma_map_page_attrs(&nic->pdev->dev, nic->rb_page, + nic->rb_page_offset, buf_len, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); if (dma_mapping_error(&nic->pdev->dev, (dma_addr_t)*rbuf)) { if (!nic->rb_page_offset) __free_pages(nic->rb_page, 0); @@ -212,7 +212,7 @@ static int nicvf_init_rbdr(struct nicvf *nic, struct rbdr *rbdr, int ring_len, int buf_size) { int idx; - u64 *rbuf; + u64 rbuf; struct rbdr_entry_t *desc; int err; @@ -257,7 +257,7 @@ static int nicvf_init_rbdr(struct nicvf *nic, struct rbdr *rbdr, } desc = GET_RBDR_DESC(rbdr, idx); - desc->buf_addr = (u64)rbuf & ~(NICVF_RCV_BUF_ALIGN_BYTES - 1); + desc->buf_addr = rbuf & ~(NICVF_RCV_BUF_ALIGN_BYTES - 1); } nicvf_get_page(nic); @@ -330,7 +330,7 @@ static void nicvf_refill_rbdr(struct nicvf *nic, gfp_t gfp) int refill_rb_cnt; struct rbdr *rbdr; struct rbdr_entry_t *desc; - u64 *rbuf; + u64 rbuf; int new_rb = 0; refill: @@ -364,7 +364,7 @@ refill: break; desc = GET_RBDR_DESC(rbdr, tail); - desc->buf_addr = (u64)rbuf & ~(NICVF_RCV_BUF_ALIGN_BYTES - 1); + desc->buf_addr = rbuf & ~(NICVF_RCV_BUF_ALIGN_BYTES - 1); refill_rb_cnt--; new_rb++; } -- cgit From 05c773f52b96ef3fbc7d9bfa21caadc6247ef7a8 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Tue, 2 May 2017 18:36:54 +0530 Subject: net: thunderx: Add basic XDP support Adds basic XDP support i.e attaching a BPF program to an interface. Also takes care of allocating separate Tx queues for XDP path and for network stack packet transmission. This patch doesn't support handling of any of the XDP actions, all are treated as XDP_PASS i.e packets will be handed over to the network stack. Changes also involve allocating one receive buffer per page in XDP mode and multiple in normal mode i.e when no BPF program is attached. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nic.h | 6 +- .../net/ethernet/cavium/thunder/nicvf_ethtool.c | 26 +++- drivers/net/ethernet/cavium/thunder/nicvf_main.c | 162 ++++++++++++++++++++- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 15 +- drivers/net/ethernet/cavium/thunder/nicvf_queues.h | 9 ++ 5 files changed, 199 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nic.h b/drivers/net/ethernet/cavium/thunder/nic.h index dca6aed49094..4a02e618e318 100644 --- a/drivers/net/ethernet/cavium/thunder/nic.h +++ b/drivers/net/ethernet/cavium/thunder/nic.h @@ -268,9 +268,9 @@ struct nicvf { struct net_device *netdev; struct pci_dev *pdev; void __iomem *reg_base; + struct bpf_prog *xdp_prog; #define MAX_QUEUES_PER_QSET 8 struct queue_set *qs; - struct nicvf_cq_poll *napi[8]; void *iommu_domain; u8 vf_id; u8 sqs_id; @@ -296,6 +296,7 @@ struct nicvf { /* Queue count */ u8 rx_queues; u8 tx_queues; + u8 xdp_tx_queues; u8 max_queues; u8 node; @@ -320,6 +321,9 @@ struct nicvf { struct nicvf_drv_stats __percpu *drv_stats; struct bgx_stats bgx_stats; + /* Napi */ + struct nicvf_cq_poll *napi[8]; + /* MSI-X */ u8 num_vec; char irq_name[NIC_VF_MSIX_VECTORS][IFNAMSIZ + 15]; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c index a89db5f3e26e..b9ece9cbf98b 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c @@ -721,7 +721,7 @@ static int nicvf_set_channels(struct net_device *dev, struct nicvf *nic = netdev_priv(dev); int err = 0; bool if_up = netif_running(dev); - int cqcount; + u8 cqcount, txq_count; if (!channel->rx_count || !channel->tx_count) return -EINVAL; @@ -730,10 +730,26 @@ static int nicvf_set_channels(struct net_device *dev, if (channel->tx_count > nic->max_queues) return -EINVAL; + if (nic->xdp_prog && + ((channel->tx_count + channel->rx_count) > nic->max_queues)) { + netdev_err(nic->netdev, + "XDP mode, RXQs + TXQs > Max %d\n", + nic->max_queues); + return -EINVAL; + } + if (if_up) nicvf_stop(dev); - cqcount = max(channel->rx_count, channel->tx_count); + nic->rx_queues = channel->rx_count; + nic->tx_queues = channel->tx_count; + if (!nic->xdp_prog) + nic->xdp_tx_queues = 0; + else + nic->xdp_tx_queues = channel->rx_count; + + txq_count = nic->xdp_tx_queues + nic->tx_queues; + cqcount = max(nic->rx_queues, txq_count); if (cqcount > MAX_CMP_QUEUES_PER_QS) { nic->sqs_count = roundup(cqcount, MAX_CMP_QUEUES_PER_QS); @@ -742,12 +758,10 @@ static int nicvf_set_channels(struct net_device *dev, nic->sqs_count = 0; } - nic->qs->rq_cnt = min_t(u32, channel->rx_count, MAX_RCV_QUEUES_PER_QS); - nic->qs->sq_cnt = min_t(u32, channel->tx_count, MAX_SND_QUEUES_PER_QS); + nic->qs->rq_cnt = min_t(u8, nic->rx_queues, MAX_RCV_QUEUES_PER_QS); + nic->qs->sq_cnt = min_t(u8, txq_count, MAX_SND_QUEUES_PER_QS); nic->qs->cq_cnt = max(nic->qs->rq_cnt, nic->qs->sq_cnt); - nic->rx_queues = channel->rx_count; - nic->tx_queues = channel->tx_count; err = nicvf_set_real_num_queues(dev, nic->tx_queues, nic->rx_queues); if (err) return err; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 0d79894400ab..9c48873350f8 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include "nic_reg.h" #include "nic.h" @@ -397,8 +399,10 @@ static void nicvf_request_sqs(struct nicvf *nic) if (nic->rx_queues > MAX_RCV_QUEUES_PER_QS) rx_queues = nic->rx_queues - MAX_RCV_QUEUES_PER_QS; - if (nic->tx_queues > MAX_SND_QUEUES_PER_QS) - tx_queues = nic->tx_queues - MAX_SND_QUEUES_PER_QS; + + tx_queues = nic->tx_queues + nic->xdp_tx_queues; + if (tx_queues > MAX_SND_QUEUES_PER_QS) + tx_queues = tx_queues - MAX_SND_QUEUES_PER_QS; /* Set no of Rx/Tx queues in each of the SQsets */ for (sqs = 0; sqs < nic->sqs_count; sqs++) { @@ -496,6 +500,43 @@ static int nicvf_init_resources(struct nicvf *nic) return 0; } +static inline bool nicvf_xdp_rx(struct nicvf *nic, + struct bpf_prog *prog, + struct cqe_rx_t *cqe_rx) +{ + struct xdp_buff xdp; + u32 action; + u16 len; + u64 dma_addr, cpu_addr; + + /* Retrieve packet buffer's DMA address and length */ + len = *((u16 *)((void *)cqe_rx + (3 * sizeof(u64)))); + dma_addr = *((u64 *)((void *)cqe_rx + (7 * sizeof(u64)))); + + cpu_addr = nicvf_iova_to_phys(nic, dma_addr); + if (!cpu_addr) + return false; + + xdp.data = phys_to_virt(cpu_addr); + xdp.data_end = xdp.data + len; + + rcu_read_lock(); + action = bpf_prog_run_xdp(prog, &xdp); + rcu_read_unlock(); + + switch (action) { + case XDP_PASS: + case XDP_TX: + case XDP_ABORTED: + case XDP_DROP: + /* Pass on all packets to network stack */ + return false; + default: + bpf_warn_invalid_xdp_action(action); + } + return false; +} + static void nicvf_snd_pkt_handler(struct net_device *netdev, struct cqe_send_t *cqe_tx, int budget, int *subdesc_cnt, @@ -599,6 +640,11 @@ static void nicvf_rcv_pkt_handler(struct net_device *netdev, return; } + /* For XDP, ignore pkts spanning multiple pages */ + if (nic->xdp_prog && (cqe_rx->rb_cnt == 1)) + if (nicvf_xdp_rx(snic, nic->xdp_prog, cqe_rx)) + return; + skb = nicvf_get_rcv_skb(snic, cqe_rx); if (!skb) { netdev_dbg(nic->netdev, "Packet not received\n"); @@ -1529,6 +1575,117 @@ static int nicvf_set_features(struct net_device *netdev, return 0; } +static void nicvf_set_xdp_queues(struct nicvf *nic, bool bpf_attached) +{ + u8 cq_count, txq_count; + + /* Set XDP Tx queue count same as Rx queue count */ + if (!bpf_attached) + nic->xdp_tx_queues = 0; + else + nic->xdp_tx_queues = nic->rx_queues; + + /* If queue count > MAX_CMP_QUEUES_PER_QS, then additional qsets + * needs to be allocated, check how many. + */ + txq_count = nic->xdp_tx_queues + nic->tx_queues; + cq_count = max(nic->rx_queues, txq_count); + if (cq_count > MAX_CMP_QUEUES_PER_QS) { + nic->sqs_count = roundup(cq_count, MAX_CMP_QUEUES_PER_QS); + nic->sqs_count = (nic->sqs_count / MAX_CMP_QUEUES_PER_QS) - 1; + } else { + nic->sqs_count = 0; + } + + /* Set primary Qset's resources */ + nic->qs->rq_cnt = min_t(u8, nic->rx_queues, MAX_RCV_QUEUES_PER_QS); + nic->qs->sq_cnt = min_t(u8, txq_count, MAX_SND_QUEUES_PER_QS); + nic->qs->cq_cnt = max_t(u8, nic->qs->rq_cnt, nic->qs->sq_cnt); + + /* Update stack */ + nicvf_set_real_num_queues(nic->netdev, nic->tx_queues, nic->rx_queues); +} + +static int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog) +{ + struct net_device *dev = nic->netdev; + bool if_up = netif_running(nic->netdev); + struct bpf_prog *old_prog; + bool bpf_attached = false; + + /* For now just support only the usual MTU sized frames */ + if (prog && (dev->mtu > 1500)) { + netdev_warn(dev, "Jumbo frames not yet supported with XDP, current MTU %d.\n", + dev->mtu); + return -EOPNOTSUPP; + } + + if (prog && prog->xdp_adjust_head) + return -EOPNOTSUPP; + + /* ALL SQs attached to CQs i.e same as RQs, are treated as + * XDP Tx queues and more Tx queues are allocated for + * network stack to send pkts out. + * + * No of Tx queues are either same as Rx queues or whatever + * is left in max no of queues possible. + */ + if ((nic->rx_queues + nic->tx_queues) > nic->max_queues) { + netdev_warn(dev, + "Failed to attach BPF prog, RXQs + TXQs > Max %d\n", + nic->max_queues); + return -ENOMEM; + } + + if (if_up) + nicvf_stop(nic->netdev); + + old_prog = xchg(&nic->xdp_prog, prog); + /* Detach old prog, if any */ + if (old_prog) + bpf_prog_put(old_prog); + + if (nic->xdp_prog) { + /* Attach BPF program */ + nic->xdp_prog = bpf_prog_add(nic->xdp_prog, nic->rx_queues - 1); + if (!IS_ERR(nic->xdp_prog)) + bpf_attached = true; + } + + /* Calculate Tx queues needed for XDP and network stack */ + nicvf_set_xdp_queues(nic, bpf_attached); + + if (if_up) { + /* Reinitialize interface, clean slate */ + nicvf_open(nic->netdev); + netif_trans_update(nic->netdev); + } + + return 0; +} + +static int nicvf_xdp(struct net_device *netdev, struct netdev_xdp *xdp) +{ + struct nicvf *nic = netdev_priv(netdev); + + /* To avoid checks while retrieving buffer address from CQE_RX, + * do not support XDP for T88 pass1.x silicons which are anyway + * not in use widely. + */ + if (pass1_silicon(nic->pdev)) + return -EOPNOTSUPP; + + switch (xdp->command) { + case XDP_SETUP_PROG: + return nicvf_xdp_setup(nic, xdp->prog); + case XDP_QUERY_PROG: + xdp->prog_attached = !!nic->xdp_prog; + return 0; + default: + return -EINVAL; + } +} + static const struct net_device_ops nicvf_netdev_ops = { .ndo_open = nicvf_open, .ndo_stop = nicvf_stop, @@ -1539,6 +1696,7 @@ static const struct net_device_ops nicvf_netdev_ops = { .ndo_tx_timeout = nicvf_tx_timeout, .ndo_fix_features = nicvf_fix_features, .ndo_set_features = nicvf_set_features, + .ndo_xdp = nicvf_xdp, }; static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index e4a02a96d4f0..8c3c571568aa 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -19,14 +19,6 @@ #include "q_struct.h" #include "nicvf_queues.h" -static inline u64 nicvf_iova_to_phys(struct nicvf *nic, dma_addr_t dma_addr) -{ - /* Translation is installed only when IOMMU is present */ - if (nic->iommu_domain) - return iommu_iova_to_phys(nic->iommu_domain, dma_addr); - return dma_addr; -} - static void nicvf_get_page(struct nicvf *nic) { if (!nic->rb_pageref || !nic->rb_page) @@ -149,8 +141,10 @@ static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, struct rbdr *rbdr, { struct pgcache *pgcache = NULL; - /* Check if request can be accomodated in previous allocated page */ - if (nic->rb_page && + /* Check if request can be accomodated in previous allocated page. + * But in XDP mode only one buffer per page is permitted. + */ + if (!nic->pnicvf->xdp_prog && nic->rb_page && ((nic->rb_page_offset + buf_len) <= PAGE_SIZE)) { nic->rb_pageref++; goto ret; @@ -961,6 +955,7 @@ int nicvf_set_qset_resources(struct nicvf *nic) nic->rx_queues = qs->rq_cnt; nic->tx_queues = qs->sq_cnt; + nic->xdp_tx_queues = 0; return 0; } diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h index da4836601d8c..07136a2819ff 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h @@ -10,6 +10,7 @@ #define NICVF_QUEUES_H #include +#include #include "q_struct.h" #define MAX_QUEUE_SET 128 @@ -312,6 +313,14 @@ struct queue_set { #define CQ_ERR_MASK (CQ_WR_FULL | CQ_WR_DISABLE | CQ_WR_FAULT) +static inline u64 nicvf_iova_to_phys(struct nicvf *nic, dma_addr_t dma_addr) +{ + /* Translation is installed only when IOMMU is present */ + if (nic->iommu_domain) + return iommu_iova_to_phys(nic->iommu_domain, dma_addr); + return dma_addr; +} + void nicvf_unmap_sndq_buffers(struct nicvf *nic, struct snd_queue *sq, int hdr_sqe, u8 subdesc_cnt); void nicvf_config_vlan_stripping(struct nicvf *nic, -- cgit From c56d91ce38d54c0c0dd8d0e4c6a9e0cfa557152f Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Tue, 2 May 2017 18:36:55 +0530 Subject: net: thunderx: Add support for XDP_DROP Adds support for XDP_DROP. Also since in XDP mode there is just a single buffer per page, made changes to recycle DMA mapping info as well along with pages. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nicvf_main.c | 23 ++++++- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 77 ++++++++++++++++------ drivers/net/ethernet/cavium/thunder/nicvf_queues.h | 4 +- 3 files changed, 79 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 9c48873350f8..a58cc1e7b94a 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "nic_reg.h" @@ -505,6 +506,7 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct cqe_rx_t *cqe_rx) { struct xdp_buff xdp; + struct page *page; u32 action; u16 len; u64 dma_addr, cpu_addr; @@ -527,12 +529,27 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, switch (action) { case XDP_PASS: case XDP_TX: - case XDP_ABORTED: - case XDP_DROP: /* Pass on all packets to network stack */ return false; default: bpf_warn_invalid_xdp_action(action); + case XDP_ABORTED: + trace_xdp_exception(nic->netdev, prog, action); + case XDP_DROP: + page = virt_to_page(xdp.data); + /* Check if it's a recycled page, if not + * unmap the DMA mapping. + * + * Recycled page holds an extra reference. + */ + if (page_ref_count(page) == 1) { + dma_addr &= PAGE_MASK; + dma_unmap_page_attrs(&nic->pdev->dev, dma_addr, + RCV_FRAG_LEN, DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + } + put_page(page); + return true; } return false; } @@ -645,7 +662,7 @@ static void nicvf_rcv_pkt_handler(struct net_device *netdev, if (nicvf_xdp_rx(snic, nic->xdp_prog, cqe_rx)) return; - skb = nicvf_get_rcv_skb(snic, cqe_rx); + skb = nicvf_get_rcv_skb(snic, cqe_rx, nic->xdp_prog ? true : false); if (!skb) { netdev_dbg(nic->netdev, "Packet not received\n"); return; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 8c3c571568aa..5009f497e64f 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -117,6 +117,7 @@ static struct pgcache *nicvf_alloc_page(struct nicvf *nic, /* Save the page in page cache */ pgcache->page = page; + pgcache->dma_addr = 0; rbdr->pgalloc++; } @@ -144,7 +145,7 @@ static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, struct rbdr *rbdr, /* Check if request can be accomodated in previous allocated page. * But in XDP mode only one buffer per page is permitted. */ - if (!nic->pnicvf->xdp_prog && nic->rb_page && + if (!rbdr->is_xdp && nic->rb_page && ((nic->rb_page_offset + buf_len) <= PAGE_SIZE)) { nic->rb_pageref++; goto ret; @@ -165,18 +166,24 @@ static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, struct rbdr *rbdr, if (pgcache) nic->rb_page = pgcache->page; ret: - /* HW will ensure data coherency, CPU sync not required */ - *rbuf = (u64)dma_map_page_attrs(&nic->pdev->dev, nic->rb_page, - nic->rb_page_offset, buf_len, - DMA_FROM_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC); - if (dma_mapping_error(&nic->pdev->dev, (dma_addr_t)*rbuf)) { - if (!nic->rb_page_offset) - __free_pages(nic->rb_page, 0); - nic->rb_page = NULL; - return -ENOMEM; + if (rbdr->is_xdp && pgcache && pgcache->dma_addr) { + *rbuf = pgcache->dma_addr; + } else { + /* HW will ensure data coherency, CPU sync not required */ + *rbuf = (u64)dma_map_page_attrs(&nic->pdev->dev, nic->rb_page, + nic->rb_page_offset, buf_len, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + if (dma_mapping_error(&nic->pdev->dev, (dma_addr_t)*rbuf)) { + if (!nic->rb_page_offset) + __free_pages(nic->rb_page, 0); + nic->rb_page = NULL; + return -ENOMEM; + } + if (pgcache) + pgcache->dma_addr = *rbuf; + nic->rb_page_offset += buf_len; } - nic->rb_page_offset += buf_len; return 0; } @@ -230,8 +237,16 @@ static int nicvf_init_rbdr(struct nicvf *nic, struct rbdr *rbdr, * On embedded platforms i.e 81xx/83xx available memory itself * is low and minimum ring size of RBDR is 8K, that takes away * lots of memory. + * + * But for XDP it has to be a single buffer per page. */ - rbdr->pgcnt = ring_len / (PAGE_SIZE / buf_size); + if (!nic->pnicvf->xdp_prog) { + rbdr->pgcnt = ring_len / (PAGE_SIZE / buf_size); + rbdr->is_xdp = false; + } else { + rbdr->pgcnt = ring_len; + rbdr->is_xdp = true; + } rbdr->pgcnt = roundup_pow_of_two(rbdr->pgcnt); rbdr->pgcache = kzalloc(sizeof(*rbdr->pgcache) * rbdr->pgcnt, GFP_KERNEL); @@ -1454,8 +1469,31 @@ static inline unsigned frag_num(unsigned i) #endif } +static void nicvf_unmap_rcv_buffer(struct nicvf *nic, u64 dma_addr, + u64 buf_addr, bool xdp) +{ + struct page *page = NULL; + int len = RCV_FRAG_LEN; + + if (xdp) { + page = virt_to_page(phys_to_virt(buf_addr)); + /* Check if it's a recycled page, if not + * unmap the DMA mapping. + * + * Recycled page holds an extra reference. + */ + if (page_ref_count(page) != 1) + return; + /* Receive buffers in XDP mode are mapped from page start */ + dma_addr &= PAGE_MASK; + } + dma_unmap_page_attrs(&nic->pdev->dev, dma_addr, len, + DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); +} + /* Returns SKB for a received packet */ -struct sk_buff *nicvf_get_rcv_skb(struct nicvf *nic, struct cqe_rx_t *cqe_rx) +struct sk_buff *nicvf_get_rcv_skb(struct nicvf *nic, + struct cqe_rx_t *cqe_rx, bool xdp) { int frag; int payload_len = 0; @@ -1490,10 +1528,9 @@ struct sk_buff *nicvf_get_rcv_skb(struct nicvf *nic, struct cqe_rx_t *cqe_rx) if (!frag) { /* First fragment */ - dma_unmap_page_attrs(&nic->pdev->dev, - *rb_ptrs - cqe_rx->align_pad, - RCV_FRAG_LEN, DMA_FROM_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC); + nicvf_unmap_rcv_buffer(nic, + *rb_ptrs - cqe_rx->align_pad, + phys_addr, xdp); skb = nicvf_rb_ptr_to_skb(nic, phys_addr - cqe_rx->align_pad, payload_len); @@ -1503,9 +1540,7 @@ struct sk_buff *nicvf_get_rcv_skb(struct nicvf *nic, struct cqe_rx_t *cqe_rx) skb_put(skb, payload_len); } else { /* Add fragments */ - dma_unmap_page_attrs(&nic->pdev->dev, *rb_ptrs, - RCV_FRAG_LEN, DMA_FROM_DEVICE, - DMA_ATTR_SKIP_CPU_SYNC); + nicvf_unmap_rcv_buffer(nic, *rb_ptrs, phys_addr, xdp); page = virt_to_page(phys_to_virt(phys_addr)); offset = phys_to_virt(phys_addr) - page_address(page); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h index 07136a2819ff..db04c0e33c6c 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h @@ -228,6 +228,7 @@ struct rbdr { u32 head; u32 tail; struct q_desc_mem dmem; + bool is_xdp; /* For page recycling */ int pgidx; @@ -339,7 +340,8 @@ void nicvf_sq_free_used_descs(struct net_device *netdev, int nicvf_sq_append_skb(struct nicvf *nic, struct snd_queue *sq, struct sk_buff *skb, u8 sq_num); -struct sk_buff *nicvf_get_rcv_skb(struct nicvf *nic, struct cqe_rx_t *cqe_rx); +struct sk_buff *nicvf_get_rcv_skb(struct nicvf *nic, + struct cqe_rx_t *cqe_rx, bool xdp); void nicvf_rbdr_task(unsigned long data); void nicvf_rbdr_work(struct work_struct *work); -- cgit From 16f2bccda75da48888772c4829a468be620c5d79 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Tue, 2 May 2017 18:36:56 +0530 Subject: net: thunderx: Add support for XDP_TX Adds support for XDP_TX i.e transmits packet out of the XDP TX queue mapped to the corresponding Rx queue on which packet is received. Since SQ for XDP TX will be used only on a single cpu i.e SQ description creation and freeing, using atomic free count is not necessary and will become a bottleneck. Hence added a separate 'xdp_free_cnt' used for SQs designated for XDP to track descriptor free count. Changes also include - A new entry 'xdp_page' is added to save transmitted packet's page pointer for later cleanup. - XDP Tx SQ's doorbell is ringed once per NAPI instance. - Retrieving designated SQ for packets being sent out by stack via 'nicvf_xmit'. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nicvf_main.c | 63 ++++++++--- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 117 ++++++++++++++++++--- drivers/net/ethernet/cavium/thunder/nicvf_queues.h | 7 ++ 3 files changed, 160 insertions(+), 27 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index a58cc1e7b94a..bb13dee388c3 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -501,9 +501,8 @@ static int nicvf_init_resources(struct nicvf *nic) return 0; } -static inline bool nicvf_xdp_rx(struct nicvf *nic, - struct bpf_prog *prog, - struct cqe_rx_t *cqe_rx) +static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, + struct cqe_rx_t *cqe_rx, struct snd_queue *sq) { struct xdp_buff xdp; struct page *page; @@ -528,9 +527,11 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, switch (action) { case XDP_PASS: - case XDP_TX: - /* Pass on all packets to network stack */ + /* Pass on packet to network stack */ return false; + case XDP_TX: + nicvf_xdp_sq_append_pkt(nic, sq, (u64)xdp.data, dma_addr, len); + return true; default: bpf_warn_invalid_xdp_action(action); case XDP_ABORTED: @@ -560,6 +561,7 @@ static void nicvf_snd_pkt_handler(struct net_device *netdev, unsigned int *tx_pkts, unsigned int *tx_bytes) { struct sk_buff *skb = NULL; + struct page *page; struct nicvf *nic = netdev_priv(netdev); struct snd_queue *sq; struct sq_hdr_subdesc *hdr; @@ -575,6 +577,22 @@ static void nicvf_snd_pkt_handler(struct net_device *netdev, if (cqe_tx->send_status) nicvf_check_cqe_tx_errs(nic->pnicvf, cqe_tx); + /* Is this a XDP designated Tx queue */ + if (sq->is_xdp) { + page = (struct page *)sq->xdp_page[cqe_tx->sqe_ptr]; + /* Check if it's recycled page or else unmap DMA mapping */ + if (page && (page_ref_count(page) == 1)) + nicvf_unmap_sndq_buffers(nic, sq, cqe_tx->sqe_ptr, + hdr->subdesc_cnt); + + /* Release page reference for recycling */ + if (page) + put_page(page); + sq->xdp_page[cqe_tx->sqe_ptr] = (u64)NULL; + *subdesc_cnt += hdr->subdesc_cnt + 1; + return; + } + skb = (struct sk_buff *)sq->skbuff[cqe_tx->sqe_ptr]; if (skb) { /* Check for dummy descriptor used for HW TSO offload on 88xx */ @@ -634,7 +652,7 @@ static inline void nicvf_set_rxhash(struct net_device *netdev, static void nicvf_rcv_pkt_handler(struct net_device *netdev, struct napi_struct *napi, - struct cqe_rx_t *cqe_rx) + struct cqe_rx_t *cqe_rx, struct snd_queue *sq) { struct sk_buff *skb; struct nicvf *nic = netdev_priv(netdev); @@ -659,7 +677,7 @@ static void nicvf_rcv_pkt_handler(struct net_device *netdev, /* For XDP, ignore pkts spanning multiple pages */ if (nic->xdp_prog && (cqe_rx->rb_cnt == 1)) - if (nicvf_xdp_rx(snic, nic->xdp_prog, cqe_rx)) + if (nicvf_xdp_rx(snic, nic->xdp_prog, cqe_rx, sq)) return; skb = nicvf_get_rcv_skb(snic, cqe_rx, nic->xdp_prog ? true : false); @@ -715,8 +733,8 @@ static int nicvf_cq_intr_handler(struct net_device *netdev, u8 cq_idx, struct cmp_queue *cq = &qs->cq[cq_idx]; struct cqe_rx_t *cq_desc; struct netdev_queue *txq; - struct snd_queue *sq; - unsigned int tx_pkts = 0, tx_bytes = 0; + struct snd_queue *sq = &qs->sq[cq_idx]; + unsigned int tx_pkts = 0, tx_bytes = 0, txq_idx; spin_lock_bh(&cq->lock); loop: @@ -746,7 +764,7 @@ loop: switch (cq_desc->cqe_type) { case CQE_TYPE_RX: - nicvf_rcv_pkt_handler(netdev, napi, cq_desc); + nicvf_rcv_pkt_handler(netdev, napi, cq_desc, sq); work_done++; break; case CQE_TYPE_SEND: @@ -773,17 +791,26 @@ loop: goto loop; done: - sq = &nic->qs->sq[cq_idx]; /* Update SQ's descriptor free count */ if (subdesc_cnt) nicvf_put_sq_desc(sq, subdesc_cnt); + txq_idx = nicvf_netdev_qidx(nic, cq_idx); + /* Handle XDP TX queues */ + if (nic->pnicvf->xdp_prog) { + if (txq_idx < nic->pnicvf->xdp_tx_queues) { + nicvf_xdp_sq_doorbell(nic, sq, cq_idx); + goto out; + } + nic = nic->pnicvf; + txq_idx -= nic->pnicvf->xdp_tx_queues; + } + /* Wakeup TXQ if its stopped earlier due to SQ full */ if (tx_done || (atomic_read(&sq->free_cnt) >= MIN_SQ_DESC_PER_PKT_XMIT)) { netdev = nic->pnicvf->netdev; - txq = netdev_get_tx_queue(netdev, - nicvf_netdev_qidx(nic, cq_idx)); + txq = netdev_get_tx_queue(netdev, txq_idx); if (tx_pkts) netdev_tx_completed_queue(txq, tx_pkts, tx_bytes); @@ -796,10 +823,11 @@ done: if (netif_msg_tx_err(nic)) netdev_warn(netdev, "%s: Transmit queue wakeup SQ%d\n", - netdev->name, cq_idx); + netdev->name, txq_idx); } } +out: spin_unlock_bh(&cq->lock); return work_done; } @@ -1115,6 +1143,13 @@ static netdev_tx_t nicvf_xmit(struct sk_buff *skb, struct net_device *netdev) return NETDEV_TX_OK; } + /* In XDP case, initial HW tx queues are used for XDP, + * but stack's queue mapping starts at '0', so skip the + * Tx queues attached to Rx queues for XDP. + */ + if (nic->xdp_prog) + qid += nic->xdp_tx_queues; + snic = nic; /* Get secondary Qset's SQ structure */ if (qid >= MAX_SND_QUEUES_PER_QS) { diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 5009f497e64f..ec234b626fe3 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -19,6 +19,8 @@ #include "q_struct.h" #include "nicvf_queues.h" +static inline void nicvf_sq_add_gather_subdesc(struct snd_queue *sq, int qentry, + int size, u64 data); static void nicvf_get_page(struct nicvf *nic) { if (!nic->rb_pageref || !nic->rb_page) @@ -456,7 +458,7 @@ static void nicvf_free_cmp_queue(struct nicvf *nic, struct cmp_queue *cq) /* Initialize transmit queue */ static int nicvf_init_snd_queue(struct nicvf *nic, - struct snd_queue *sq, int q_len) + struct snd_queue *sq, int q_len, int qidx) { int err; @@ -469,17 +471,38 @@ static int nicvf_init_snd_queue(struct nicvf *nic, sq->skbuff = kcalloc(q_len, sizeof(u64), GFP_KERNEL); if (!sq->skbuff) return -ENOMEM; + sq->head = 0; sq->tail = 0; - atomic_set(&sq->free_cnt, q_len - 1); sq->thresh = SND_QUEUE_THRESH; - /* Preallocate memory for TSO segment's header */ - sq->tso_hdrs = dma_alloc_coherent(&nic->pdev->dev, - q_len * TSO_HEADER_SIZE, - &sq->tso_hdrs_phys, GFP_KERNEL); - if (!sq->tso_hdrs) - return -ENOMEM; + /* Check if this SQ is a XDP TX queue */ + if (nic->sqs_mode) + qidx += ((nic->sqs_id + 1) * MAX_SND_QUEUES_PER_QS); + if (qidx < nic->pnicvf->xdp_tx_queues) { + /* Alloc memory to save page pointers for XDP_TX */ + sq->xdp_page = kcalloc(q_len, sizeof(u64), GFP_KERNEL); + if (!sq->xdp_page) + return -ENOMEM; + sq->xdp_desc_cnt = 0; + sq->xdp_free_cnt = q_len - 1; + sq->is_xdp = true; + } else { + sq->xdp_page = NULL; + sq->xdp_desc_cnt = 0; + sq->xdp_free_cnt = 0; + sq->is_xdp = false; + + atomic_set(&sq->free_cnt, q_len - 1); + + /* Preallocate memory for TSO segment's header */ + sq->tso_hdrs = dma_alloc_coherent(&nic->pdev->dev, + q_len * TSO_HEADER_SIZE, + &sq->tso_hdrs_phys, + GFP_KERNEL); + if (!sq->tso_hdrs) + return -ENOMEM; + } return 0; } @@ -505,6 +528,7 @@ void nicvf_unmap_sndq_buffers(struct nicvf *nic, struct snd_queue *sq, static void nicvf_free_snd_queue(struct nicvf *nic, struct snd_queue *sq) { struct sk_buff *skb; + struct page *page; struct sq_hdr_subdesc *hdr; struct sq_hdr_subdesc *tso_sqe; @@ -522,8 +546,15 @@ static void nicvf_free_snd_queue(struct nicvf *nic, struct snd_queue *sq) smp_rmb(); while (sq->head != sq->tail) { skb = (struct sk_buff *)sq->skbuff[sq->head]; - if (!skb) + if (!skb || !sq->xdp_page) + goto next; + + page = (struct page *)sq->xdp_page[sq->head]; + if (!page) goto next; + else + put_page(page); + hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, sq->head); /* Check for dummy descriptor used for HW TSO offload on 88xx */ if (hdr->dont_send) { @@ -536,12 +567,14 @@ static void nicvf_free_snd_queue(struct nicvf *nic, struct snd_queue *sq) nicvf_unmap_sndq_buffers(nic, sq, sq->head, hdr->subdesc_cnt); } - dev_kfree_skb_any(skb); + if (skb) + dev_kfree_skb_any(skb); next: sq->head++; sq->head &= (sq->dmem.q_len - 1); } kfree(sq->skbuff); + kfree(sq->xdp_page); nicvf_free_q_desc_mem(nic, &sq->dmem); } @@ -932,7 +965,7 @@ static int nicvf_alloc_resources(struct nicvf *nic) /* Alloc send queue */ for (qidx = 0; qidx < qs->sq_cnt; qidx++) { - if (nicvf_init_snd_queue(nic, &qs->sq[qidx], qs->sq_len)) + if (nicvf_init_snd_queue(nic, &qs->sq[qidx], qs->sq_len, qidx)) goto alloc_fail; } @@ -1035,7 +1068,10 @@ static inline int nicvf_get_sq_desc(struct snd_queue *sq, int desc_cnt) int qentry; qentry = sq->tail; - atomic_sub(desc_cnt, &sq->free_cnt); + if (!sq->is_xdp) + atomic_sub(desc_cnt, &sq->free_cnt); + else + sq->xdp_free_cnt -= desc_cnt; sq->tail += desc_cnt; sq->tail &= (sq->dmem.q_len - 1); @@ -1053,7 +1089,10 @@ static inline void nicvf_rollback_sq_desc(struct snd_queue *sq, /* Free descriptor back to SQ for future use */ void nicvf_put_sq_desc(struct snd_queue *sq, int desc_cnt) { - atomic_add(desc_cnt, &sq->free_cnt); + if (!sq->is_xdp) + atomic_add(desc_cnt, &sq->free_cnt); + else + sq->xdp_free_cnt += desc_cnt; sq->head += desc_cnt; sq->head &= (sq->dmem.q_len - 1); } @@ -1111,6 +1150,58 @@ void nicvf_sq_free_used_descs(struct net_device *netdev, struct snd_queue *sq, } } +/* XDP Transmit APIs */ +void nicvf_xdp_sq_doorbell(struct nicvf *nic, + struct snd_queue *sq, int sq_num) +{ + if (!sq->xdp_desc_cnt) + return; + + /* make sure all memory stores are done before ringing doorbell */ + wmb(); + + /* Inform HW to xmit all TSO segments */ + nicvf_queue_reg_write(nic, NIC_QSET_SQ_0_7_DOOR, + sq_num, sq->xdp_desc_cnt); + sq->xdp_desc_cnt = 0; +} + +static inline void +nicvf_xdp_sq_add_hdr_subdesc(struct snd_queue *sq, int qentry, + int subdesc_cnt, u64 data, int len) +{ + struct sq_hdr_subdesc *hdr; + + hdr = (struct sq_hdr_subdesc *)GET_SQ_DESC(sq, qentry); + memset(hdr, 0, SND_QUEUE_DESC_SIZE); + hdr->subdesc_type = SQ_DESC_TYPE_HEADER; + hdr->subdesc_cnt = subdesc_cnt; + hdr->tot_len = len; + hdr->post_cqe = 1; + sq->xdp_page[qentry] = (u64)virt_to_page((void *)data); +} + +int nicvf_xdp_sq_append_pkt(struct nicvf *nic, struct snd_queue *sq, + u64 bufaddr, u64 dma_addr, u16 len) +{ + int subdesc_cnt = MIN_SQ_DESC_PER_PKT_XMIT; + int qentry; + + if (subdesc_cnt > sq->xdp_free_cnt) + return 0; + + qentry = nicvf_get_sq_desc(sq, subdesc_cnt); + + nicvf_xdp_sq_add_hdr_subdesc(sq, qentry, subdesc_cnt - 1, bufaddr, len); + + qentry = nicvf_get_nxt_sqentry(sq, qentry); + nicvf_sq_add_gather_subdesc(sq, qentry, len, dma_addr); + + sq->xdp_desc_cnt += subdesc_cnt; + + return 1; +} + /* Calculate no of SQ subdescriptors needed to transmit all * segments of this TSO packet. * Taken from 'Tilera network driver' with a minor modification. diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h index db04c0e33c6c..a07d5b4d5ce2 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h @@ -271,6 +271,10 @@ struct snd_queue { u32 tail; u64 *skbuff; void *desc; + u64 *xdp_page; + u16 xdp_desc_cnt; + u16 xdp_free_cnt; + bool is_xdp; #define TSO_HEADER_SIZE 128 /* For TSO segment's header */ @@ -339,6 +343,9 @@ void nicvf_sq_free_used_descs(struct net_device *netdev, struct snd_queue *sq, int qidx); int nicvf_sq_append_skb(struct nicvf *nic, struct snd_queue *sq, struct sk_buff *skb, u8 sq_num); +int nicvf_xdp_sq_append_pkt(struct nicvf *nic, struct snd_queue *sq, + u64 bufaddr, u64 dma_addr, u16 len); +void nicvf_xdp_sq_doorbell(struct nicvf *nic, struct snd_queue *sq, int sq_num); struct sk_buff *nicvf_get_rcv_skb(struct nicvf *nic, struct cqe_rx_t *cqe_rx, bool xdp); -- cgit From e3d06ff9ec9400b93bacf8fa92f3985c9412e282 Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Tue, 2 May 2017 18:36:57 +0530 Subject: net: thunderx: Support for XDP header adjustment When in XDP mode reserve XDP_PACKET_HEADROOM bytes at the start of receive buffer for XDP program to modify headers and adjust packet start. Additional code changes done to handle such packets. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nicvf_main.c | 63 ++++++++++++++++------ drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 9 +++- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index bb13dee388c3..d6477af88085 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -502,13 +502,15 @@ static int nicvf_init_resources(struct nicvf *nic) } static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, - struct cqe_rx_t *cqe_rx, struct snd_queue *sq) + struct cqe_rx_t *cqe_rx, struct snd_queue *sq, + struct sk_buff **skb) { struct xdp_buff xdp; struct page *page; u32 action; - u16 len; + u16 len, offset = 0; u64 dma_addr, cpu_addr; + void *orig_data; /* Retrieve packet buffer's DMA address and length */ len = *((u16 *)((void *)cqe_rx + (3 * sizeof(u64)))); @@ -517,17 +519,47 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, cpu_addr = nicvf_iova_to_phys(nic, dma_addr); if (!cpu_addr) return false; + cpu_addr = (u64)phys_to_virt(cpu_addr); + page = virt_to_page((void *)cpu_addr); - xdp.data = phys_to_virt(cpu_addr); + xdp.data_hard_start = page_address(page); + xdp.data = (void *)cpu_addr; xdp.data_end = xdp.data + len; + orig_data = xdp.data; rcu_read_lock(); action = bpf_prog_run_xdp(prog, &xdp); rcu_read_unlock(); + /* Check if XDP program has changed headers */ + if (orig_data != xdp.data) { + len = xdp.data_end - xdp.data; + offset = orig_data - xdp.data; + dma_addr -= offset; + } + switch (action) { case XDP_PASS: - /* Pass on packet to network stack */ + /* Check if it's a recycled page, if not + * unmap the DMA mapping. + * + * Recycled page holds an extra reference. + */ + if (page_ref_count(page) == 1) { + dma_addr &= PAGE_MASK; + dma_unmap_page_attrs(&nic->pdev->dev, dma_addr, + RCV_FRAG_LEN + XDP_PACKET_HEADROOM, + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + } + + /* Build SKB and pass on packet to network stack */ + *skb = build_skb(xdp.data, + RCV_FRAG_LEN - cqe_rx->align_pad + offset); + if (!*skb) + put_page(page); + else + skb_put(*skb, len); return false; case XDP_TX: nicvf_xdp_sq_append_pkt(nic, sq, (u64)xdp.data, dma_addr, len); @@ -537,7 +569,6 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, case XDP_ABORTED: trace_xdp_exception(nic->netdev, prog, action); case XDP_DROP: - page = virt_to_page(xdp.data); /* Check if it's a recycled page, if not * unmap the DMA mapping. * @@ -546,7 +577,8 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, if (page_ref_count(page) == 1) { dma_addr &= PAGE_MASK; dma_unmap_page_attrs(&nic->pdev->dev, dma_addr, - RCV_FRAG_LEN, DMA_FROM_DEVICE, + RCV_FRAG_LEN + XDP_PACKET_HEADROOM, + DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); } put_page(page); @@ -654,7 +686,7 @@ static void nicvf_rcv_pkt_handler(struct net_device *netdev, struct napi_struct *napi, struct cqe_rx_t *cqe_rx, struct snd_queue *sq) { - struct sk_buff *skb; + struct sk_buff *skb = NULL; struct nicvf *nic = netdev_priv(netdev); struct nicvf *snic = nic; int err = 0; @@ -676,15 +708,17 @@ static void nicvf_rcv_pkt_handler(struct net_device *netdev, } /* For XDP, ignore pkts spanning multiple pages */ - if (nic->xdp_prog && (cqe_rx->rb_cnt == 1)) - if (nicvf_xdp_rx(snic, nic->xdp_prog, cqe_rx, sq)) + if (nic->xdp_prog && (cqe_rx->rb_cnt == 1)) { + /* Packet consumed by XDP */ + if (nicvf_xdp_rx(snic, nic->xdp_prog, cqe_rx, sq, &skb)) return; + } else { + skb = nicvf_get_rcv_skb(snic, cqe_rx, + nic->xdp_prog ? true : false); + } - skb = nicvf_get_rcv_skb(snic, cqe_rx, nic->xdp_prog ? true : false); - if (!skb) { - netdev_dbg(nic->netdev, "Packet not received\n"); + if (!skb) return; - } if (netif_msg_pktdata(nic)) { netdev_info(nic->netdev, "%s: skb 0x%p, len=%d\n", netdev->name, @@ -1672,9 +1706,6 @@ static int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog) return -EOPNOTSUPP; } - if (prog && prog->xdp_adjust_head) - return -EOPNOTSUPP; - /* ALL SQs attached to CQs i.e same as RQs, are treated as * XDP Tx queues and more Tx queues are allocated for * network stack to send pkts out. diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index ec234b626fe3..43428ce760ca 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -164,6 +164,11 @@ static inline int nicvf_alloc_rcv_buffer(struct nicvf *nic, struct rbdr *rbdr, } nic->rb_page_offset = 0; + + /* Reserve space for header modifications by BPF program */ + if (rbdr->is_xdp) + buf_len += XDP_PACKET_HEADROOM; + /* Check if it's recycled */ if (pgcache) nic->rb_page = pgcache->page; @@ -183,7 +188,7 @@ ret: return -ENOMEM; } if (pgcache) - pgcache->dma_addr = *rbuf; + pgcache->dma_addr = *rbuf + XDP_PACKET_HEADROOM; nic->rb_page_offset += buf_len; } @@ -1575,6 +1580,8 @@ static void nicvf_unmap_rcv_buffer(struct nicvf *nic, u64 dma_addr, */ if (page_ref_count(page) != 1) return; + + len += XDP_PACKET_HEADROOM; /* Receive buffers in XDP mode are mapped from page start */ dma_addr &= PAGE_MASK; } -- cgit From 773225388dae15e72790d6f573e2e70e96292b6b Mon Sep 17 00:00:00 2001 From: Sunil Goutham Date: Tue, 2 May 2017 18:36:58 +0530 Subject: net: thunderx: Optimize page recycling for XDP Driver follows a method of taking one extra reference on the page for recycling which is fine in usual packet path where each 64KB page is segmented into multiple receive buffers. But in XDP mode since there is just one receive buffer per page taking extra page reference itself becomes big bottleneck consuming ~50% of CPU cycles due to atomic operations. This patch adds a internal ref count in pgcache for each page and additional page references are taken in a batch instead of just one at a time. Internal i.e 'pgcache->ref_count' and page's i.e 'page->_refcount' counters are compared to check page's recyclability. Signed-off-by: Sunil Goutham Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/thunder/nicvf_queues.c | 57 +++++++++++++++++++--- drivers/net/ethernet/cavium/thunder/nicvf_queues.h | 1 + 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 43428ce760ca..2b181762ad49 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -82,6 +82,8 @@ static void nicvf_free_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem) dmem->base = NULL; } +#define XDP_PAGE_REFCNT_REFILL 256 + /* Allocate a new page or recycle one if possible * * We cannot optimize dma mapping here, since @@ -90,9 +92,10 @@ static void nicvf_free_q_desc_mem(struct nicvf *nic, struct q_desc_mem *dmem) * and not idx into RBDR ring, so can't refer to saved info. * 3. There are multiple receive buffers per page */ -static struct pgcache *nicvf_alloc_page(struct nicvf *nic, - struct rbdr *rbdr, gfp_t gfp) +static inline struct pgcache *nicvf_alloc_page(struct nicvf *nic, + struct rbdr *rbdr, gfp_t gfp) { + int ref_count; struct page *page = NULL; struct pgcache *pgcache, *next; @@ -100,8 +103,23 @@ static struct pgcache *nicvf_alloc_page(struct nicvf *nic, pgcache = &rbdr->pgcache[rbdr->pgidx]; page = pgcache->page; /* Check if page can be recycled */ - if (page && (page_ref_count(page) != 1)) - page = NULL; + if (page) { + ref_count = page_ref_count(page); + /* Check if this page has been used once i.e 'put_page' + * called after packet transmission i.e internal ref_count + * and page's ref_count are equal i.e page can be recycled. + */ + if (rbdr->is_xdp && (ref_count == pgcache->ref_count)) + pgcache->ref_count--; + else + page = NULL; + + /* In non-XDP mode, page's ref_count needs to be '1' for it + * to be recycled. + */ + if (!rbdr->is_xdp && (ref_count != 1)) + page = NULL; + } if (!page) { page = alloc_pages(gfp | __GFP_COMP | __GFP_NOWARN, 0); @@ -120,11 +138,30 @@ static struct pgcache *nicvf_alloc_page(struct nicvf *nic, /* Save the page in page cache */ pgcache->page = page; pgcache->dma_addr = 0; + pgcache->ref_count = 0; rbdr->pgalloc++; } - /* Take extra page reference for recycling */ - page_ref_add(page, 1); + /* Take additional page references for recycling */ + if (rbdr->is_xdp) { + /* Since there is single RBDR (i.e single core doing + * page recycling) per 8 Rx queues, in XDP mode adjusting + * page references atomically is the biggest bottleneck, so + * take bunch of references at a time. + * + * So here, below reference counts defer by '1'. + */ + if (!pgcache->ref_count) { + pgcache->ref_count = XDP_PAGE_REFCNT_REFILL; + page_ref_add(page, XDP_PAGE_REFCNT_REFILL); + } + } else { + /* In non-XDP case, single 64K page is divided across multiple + * receive buffers, so cost of recycling is less anyway. + * So we can do with just one extra reference. + */ + page_ref_add(page, 1); + } rbdr->pgidx++; rbdr->pgidx &= (rbdr->pgcnt - 1); @@ -327,8 +364,14 @@ static void nicvf_free_rbdr(struct nicvf *nic, struct rbdr *rbdr) head = 0; while (head < rbdr->pgcnt) { pgcache = &rbdr->pgcache[head]; - if (pgcache->page && page_ref_count(pgcache->page) != 0) + if (pgcache->page && page_ref_count(pgcache->page) != 0) { + if (!rbdr->is_xdp) { + put_page(pgcache->page); + continue; + } + page_ref_sub(pgcache->page, pgcache->ref_count - 1); put_page(pgcache->page); + } head++; } diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h index a07d5b4d5ce2..57858522c33c 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h @@ -216,6 +216,7 @@ struct q_desc_mem { struct pgcache { struct page *page; + int ref_count; u64 dma_addr; }; -- cgit From e9f8b10101c6da3ab000a2fb17162374c9bd2c69 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Tue, 2 May 2017 18:16:53 +0200 Subject: tipc: refactor function tipc_sk_recvmsg() We try to make this function more readable by improving variable names and comments, plus some minor changes to the logics. Reviewed-by: Parthasarathy Bhuvaragan Signed-off-by: Jon Maloy Signed-off-by: David S. Miller --- net/tipc/socket.c | 109 +++++++++++++++++++++++++----------------------------- 1 file changed, 50 insertions(+), 59 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 8a4e9fe5f9eb..3855bfd1fb1b 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -51,6 +51,7 @@ #define TIPC_FWD_MSG 1 #define TIPC_MAX_PORT 0xffffffff #define TIPC_MIN_PORT 1 +#define TIPC_ACK_RATE 4 /* ACK at 1/4 of of rcv window size */ enum { TIPC_LISTEN = TCP_LISTEN, @@ -1290,7 +1291,7 @@ static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop) /** * tipc_recvmsg - receive packet-oriented message * @m: descriptor for message info - * @buf_len: total size of user buffer area + * @buflen: length of user buffer area * @flags: receive flags * * Used for SOCK_DGRAM, SOCK_RDM, and SOCK_SEQPACKET messages. @@ -1298,89 +1299,79 @@ static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop) * * Returns size of returned message data, errno otherwise */ -static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buf_len, - int flags) +static int tipc_recvmsg(struct socket *sock, struct msghdr *m, + size_t buflen, int flags) { struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); - struct sk_buff *buf; - struct tipc_msg *msg; - bool is_connectionless = tipc_sk_type_connectionless(sk); - long timeo; - unsigned int sz; - u32 err; - int res, hlen; + struct sk_buff *skb; + struct tipc_msg *hdr; + bool connected = !tipc_sk_type_connectionless(sk); + int rc, err, hlen, dlen, copy; + long timeout; /* Catch invalid receive requests */ - if (unlikely(!buf_len)) + if (unlikely(!buflen)) return -EINVAL; lock_sock(sk); - - if (!is_connectionless && unlikely(sk->sk_state == TIPC_OPEN)) { - res = -ENOTCONN; + if (unlikely(connected && sk->sk_state == TIPC_OPEN)) { + rc = -ENOTCONN; goto exit; } + timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); - timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); -restart: - - /* Look for a message in receive queue; wait if necessary */ - res = tipc_wait_for_rcvmsg(sock, &timeo); - if (res) - goto exit; - - /* Look at first message in receive queue */ - buf = skb_peek(&sk->sk_receive_queue); - msg = buf_msg(buf); - sz = msg_data_sz(msg); - hlen = msg_hdr_sz(msg); - err = msg_errcode(msg); - - /* Discard an empty non-errored message & try again */ - if ((!sz) && (!err)) { + do { + /* Look at first msg in receive queue; wait if necessary */ + rc = tipc_wait_for_rcvmsg(sock, &timeout); + if (unlikely(rc)) + goto exit; + skb = skb_peek(&sk->sk_receive_queue); + hdr = buf_msg(skb); + dlen = msg_data_sz(hdr); + hlen = msg_hdr_sz(hdr); + err = msg_errcode(hdr); + if (likely(dlen || err)) + break; tsk_advance_rx_queue(sk); - goto restart; - } + } while (1); - /* Capture sender's address (optional) */ - set_orig_addr(m, msg); - - /* Capture ancillary data (optional) */ - res = tipc_sk_anc_data_recv(m, msg, tsk); - if (res) + /* Collect msg meta data, including error code and rejected data */ + set_orig_addr(m, hdr); + rc = tipc_sk_anc_data_recv(m, hdr, tsk); + if (unlikely(rc)) goto exit; - /* Capture message data (if valid) & compute return value (always) */ - if (!err) { - if (unlikely(buf_len < sz)) { - sz = buf_len; + /* Capture data if non-error msg, otherwise just set return value */ + if (likely(!err)) { + copy = min_t(int, dlen, buflen); + if (unlikely(copy != dlen)) m->msg_flags |= MSG_TRUNC; - } - res = skb_copy_datagram_msg(buf, hlen, m, sz); - if (res) - goto exit; - res = sz; + rc = skb_copy_datagram_msg(skb, hlen, m, copy); } else { - if (is_connectionless || err == TIPC_CONN_SHUTDOWN || - m->msg_control) - res = 0; - else - res = -ECONNRESET; + copy = 0; + rc = 0; + if (err != TIPC_CONN_SHUTDOWN && connected && !m->msg_control) + rc = -ECONNRESET; } + if (unlikely(rc)) + goto exit; + /* Caption of data or error code/rejected data was successful */ if (unlikely(flags & MSG_PEEK)) goto exit; - if (likely(!is_connectionless)) { - tsk->rcv_unacked += tsk_inc(tsk, hlen + sz); - if (unlikely(tsk->rcv_unacked >= (tsk->rcv_win / 4))) - tipc_sk_send_ack(tsk); - } tsk_advance_rx_queue(sk); + if (likely(!connected)) + goto exit; + + /* Send connection flow control ack when applicable */ + tsk->rcv_unacked += tsk_inc(tsk, hlen + dlen); + if (tsk->rcv_unacked >= tsk->rcv_win / TIPC_ACK_RATE) + tipc_sk_send_ack(tsk); exit: release_sock(sk); - return res; + return rc ? rc : copy; } /** -- cgit From ec8a09fbbeff252c80daf62c7a78342003dddf9c Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Tue, 2 May 2017 18:16:54 +0200 Subject: tipc: refactor function tipc_sk_recv_stream() We try to make this function more readable by improving variable names and comments, using more stack variables, and doing some smaller changes to the logics. We also rename the function to make it consistent with naming conventions used elsewhere in the code. Reviewed-by: Parthasarathy Bhuvaragan Signed-off-by: Jon Maloy Signed-off-by: David S. Miller --- net/tipc/socket.c | 155 +++++++++++++++++++++++++----------------------------- 1 file changed, 71 insertions(+), 84 deletions(-) diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 3855bfd1fb1b..7e45ef938c18 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1375,9 +1375,9 @@ exit: } /** - * tipc_recv_stream - receive stream-oriented data + * tipc_recvstream - receive stream-oriented data * @m: descriptor for message info - * @buf_len: total size of user buffer area + * @buflen: total size of user buffer area * @flags: receive flags * * Used for SOCK_STREAM messages only. If not enough data is available @@ -1385,111 +1385,98 @@ exit: * * Returns size of returned message data, errno otherwise */ -static int tipc_recv_stream(struct socket *sock, struct msghdr *m, - size_t buf_len, int flags) +static int tipc_recvstream(struct socket *sock, struct msghdr *m, + size_t buflen, int flags) { struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); - struct sk_buff *buf; - struct tipc_msg *msg; - long timeo; - unsigned int sz; - int target; - int sz_copied = 0; - u32 err; - int res = 0, hlen; + struct sk_buff *skb; + struct tipc_msg *hdr; + struct tipc_skb_cb *skb_cb; + bool peek = flags & MSG_PEEK; + int offset, required, copy, copied = 0; + int hlen, dlen, err, rc; + long timeout; /* Catch invalid receive attempts */ - if (unlikely(!buf_len)) + if (unlikely(!buflen)) return -EINVAL; lock_sock(sk); if (unlikely(sk->sk_state == TIPC_OPEN)) { - res = -ENOTCONN; - goto exit; - } - - target = sock_rcvlowat(sk, flags & MSG_WAITALL, buf_len); - timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); - -restart: - /* Look for a message in receive queue; wait if necessary */ - res = tipc_wait_for_rcvmsg(sock, &timeo); - if (res) + rc = -ENOTCONN; goto exit; - - /* Look at first message in receive queue */ - buf = skb_peek(&sk->sk_receive_queue); - msg = buf_msg(buf); - sz = msg_data_sz(msg); - hlen = msg_hdr_sz(msg); - err = msg_errcode(msg); - - /* Discard an empty non-errored message & try again */ - if ((!sz) && (!err)) { - tsk_advance_rx_queue(sk); - goto restart; } + required = sock_rcvlowat(sk, flags & MSG_WAITALL, buflen); + timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); - /* Optionally capture sender's address & ancillary data of first msg */ - if (sz_copied == 0) { - set_orig_addr(m, msg); - res = tipc_sk_anc_data_recv(m, msg, tsk); - if (res) - goto exit; - } - - /* Capture message data (if valid) & compute return value (always) */ - if (!err) { - u32 offset = TIPC_SKB_CB(buf)->bytes_read; - u32 needed; - int sz_to_copy; - - sz -= offset; - needed = (buf_len - sz_copied); - sz_to_copy = min(sz, needed); + do { + /* Look at first msg in receive queue; wait if necessary */ + rc = tipc_wait_for_rcvmsg(sock, &timeout); + if (unlikely(rc)) + break; + skb = skb_peek(&sk->sk_receive_queue); + skb_cb = TIPC_SKB_CB(skb); + hdr = buf_msg(skb); + dlen = msg_data_sz(hdr); + hlen = msg_hdr_sz(hdr); + err = msg_errcode(hdr); - res = skb_copy_datagram_msg(buf, hlen + offset, m, sz_to_copy); - if (res) - goto exit; + /* Discard any empty non-errored (SYN-) message */ + if (unlikely(!dlen && !err)) { + tsk_advance_rx_queue(sk); + continue; + } - sz_copied += sz_to_copy; + /* Collect msg meta data, incl. error code and rejected data */ + if (!copied) { + set_orig_addr(m, hdr); + rc = tipc_sk_anc_data_recv(m, hdr, tsk); + if (rc) + break; + } - if (sz_to_copy < sz) { - if (!(flags & MSG_PEEK)) - TIPC_SKB_CB(buf)->bytes_read = - offset + sz_to_copy; - goto exit; + /* Copy data if msg ok, otherwise return error/partial data */ + if (likely(!err)) { + offset = skb_cb->bytes_read; + copy = min_t(int, dlen - offset, buflen - copied); + rc = skb_copy_datagram_msg(skb, hlen + offset, m, copy); + if (unlikely(rc)) + break; + copied += copy; + offset += copy; + if (unlikely(offset < dlen)) { + if (!peek) + skb_cb->bytes_read = offset; + break; + } + } else { + rc = 0; + if ((err != TIPC_CONN_SHUTDOWN) && !m->msg_control) + rc = -ECONNRESET; + if (copied || rc) + break; } - } else { - if (sz_copied != 0) - goto exit; /* can't add error msg to valid data */ - if ((err == TIPC_CONN_SHUTDOWN) || m->msg_control) - res = 0; - else - res = -ECONNRESET; - } + if (unlikely(peek)) + break; - if (unlikely(flags & MSG_PEEK)) - goto exit; + tsk_advance_rx_queue(sk); - tsk->rcv_unacked += tsk_inc(tsk, hlen + msg_data_sz(msg)); - if (unlikely(tsk->rcv_unacked >= (tsk->rcv_win / 4))) - tipc_sk_send_ack(tsk); - tsk_advance_rx_queue(sk); + /* Send connection flow control advertisement when applicable */ + tsk->rcv_unacked += tsk_inc(tsk, hlen + dlen); + if (unlikely(tsk->rcv_unacked >= tsk->rcv_win / TIPC_ACK_RATE)) + tipc_sk_send_ack(tsk); - /* Loop around if more data is required */ - if ((sz_copied < buf_len) && /* didn't get all requested data */ - (!skb_queue_empty(&sk->sk_receive_queue) || - (sz_copied < target)) && /* and more is ready or required */ - (!err)) /* and haven't reached a FIN */ - goto restart; + /* Exit if all requested data or FIN/error received */ + if (copied == buflen || err) + break; + } while (!skb_queue_empty(&sk->sk_receive_queue) || copied < required); exit: release_sock(sk); - return sz_copied ? sz_copied : res; + return copied ? copied : rc; } /** @@ -2584,7 +2571,7 @@ static const struct proto_ops stream_ops = { .setsockopt = tipc_setsockopt, .getsockopt = tipc_getsockopt, .sendmsg = tipc_sendstream, - .recvmsg = tipc_recv_stream, + .recvmsg = tipc_recvstream, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage }; -- cgit